summaryrefslogtreecommitdiff
path: root/lib/test_string.c
blob: c5cb92fb710e729a2de3e1a6f2c1352f8fd5127b (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
257
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>

static __init int memset16_selftest(void)
{
	unsigned i, j, k;
	u16 v, *p;

	p = kmalloc(256 * 2 * 2, GFP_KERNEL);
	if (!p)
		return -1;

	for (i = 0; i < 256; i++) {
		for (j = 0; j < 256; j++) {
			memset(p, 0xa1, 256 * 2 * sizeof(v));
			memset16(p + i, 0xb1b2, j);
			for (k = 0; k < 512; k++) {
				v = p[k];
				if (k < i) {
					if (v != 0xa1a1)
						goto fail;
				} else if (k < i + j) {
					if (v != 0xb1b2)
						goto fail;
				} else {
					if (v != 0xa1a1)
						goto fail;
				}
			}
		}
	}

fail:
	kfree(p);
	if (i < 256)
		return (i << 24) | (j << 16) | k | 0x8000;
	return 0;
}

static __init int memset32_selftest(void)
{
	unsigned i, j, k;
	u32 v, *p;

	p = kmalloc(256 * 2 * 4, GFP_KERNEL);
	if (!p)
		return -1;

	for (i = 0; i < 256; i++) {
		for (j = 0; j < 256; j++) {
			memset(p, 0xa1, 256 * 2 * sizeof(v));
			memset32(p + i, 0xb1b2b3b4, j);
			for (k = 0; k < 512; k++) {
				v = p[k];
				if (k < i) {
					if (v != 0xa1a1a1a1)
						goto fail;
				} else if (k < i + j) {
					if (v != 0xb1b2b3b4)
						goto fail;
				} else {
					if (v != 0xa1a1a1a1)
						goto fail;
				}
			}
		}
	}

fail:
	kfree(p);
	if (i < 256)
		return (i << 24) | (j << 16) | k | 0x8000;
	return 0;
}

static __init int memset64_selftest(void)
{
	unsigned i, j, k;
	u64 v, *p;

	p = kmalloc(256 * 2 * 8, GFP_KERNEL);
	if (!p)
		return -1;

	for (i = 0; i < 256; i++) {
		for (j = 0; j < 256; j++) {
			memset(p, 0xa1, 256 * 2 * sizeof(v));
			memset64(p + i, 0xb1b2b3b4b5b6b7b8ULL, j);
			for (k = 0; k < 512; k++) {
				v = p[k];
				if (k < i) {
					if (v != 0xa1a1a1a1a1a1a1a1ULL)
						goto fail;
				} else if (k < i + j) {
					if (v != 0xb1b2b3b4b5b6b7b8ULL)
						goto fail;
				} else {
					if (v != 0xa1a1a1a1a1a1a1a1ULL)
						goto fail;
				}
			}
		}
	}

fail:
	kfree(p);
	if (i < 256)
		return (i << 24) | (j << 16) | k | 0x8000;
	return 0;
}

static __init int strchr_selftest(void)
{
	const char *test_string = "abcdefghijkl";
	const char *empty_string = "";
	char *result;
	int i;

	for (i = 0; i < strlen(test_string) + 1; i++) {
		result = strchr(test_string, test_string[i]);
		if (result - test_string != i)
			return i + 'a';
	}

	result = strchr(empty_string, '\0');
	if (result != empty_string)
		return 0x101;

	result = strchr(empty_string, 'a');
	if (result)
		return 0x102;

	result = strchr(test_string, 'z');
	if (result)
		return 0x103;

	return 0;
}

static __init int strnchr_selftest(void)
{
	const char *test_string = "abcdefghijkl";
	const char *empty_string = "";
	char *result;
	int i, j;

	for (i = 0; i < strlen(test_string) + 1; i++) {
		for (j = 0; j < strlen(test_string) + 2; j++) {
			result = strnchr(test_string, j, test_string[i]);
			if (j <= i) {
				if (!result)
					continue;
				return ((i + 'a') << 8) | j;
			}
			if (result - test_string != i)
				return ((i + 'a') << 8) | j;
		}
	}

	result = strnchr(empty_string, 0, '\0');
	if (result)
		return 0x10001;

	result = strnchr(empty_string, 1, '\0');
	if (result != empty_string)
		return 0x10002;

	result = strnchr(empty_string, 1, 'a');
	if (result)
		return 0x10003;

	result = strnchr(NULL, 0, '\0');
	if (result)
		return 0x10004;

	return 0;
}

static __init int strspn_selftest(void)
{
	static const struct strspn_test {
		const char str[16];
		const char accept[16];
		const char reject[16];
		unsigned a;
		unsigned r;
	} tests[] __initconst = {
		{ "foobar", "", "", 0, 6 },
		{ "abba", "abc", "ABBA", 4, 4 },
		{ "abba", "a", "b", 1, 1 },
		{ "", "abc", "abc", 0, 0},
	};
	const struct strspn_test *s = tests;
	size_t i, res;

	for (i = 0; i < ARRAY_SIZE(tests); ++i, ++s) {
		res = strspn(s->str, s->accept);
		if (res != s->a)
			return 0x100 + 2*i;
		res = strcspn(s->str, s->reject);
		if (res != s->r)
			return 0x100 + 2*i + 1;
	}
	return 0;
}

static __exit void string_selftest_remove(void)
{
}

static __init int string_selftest_init(void)
{
	int test, subtest;

	test = 1;
	subtest = memset16_selftest();
	if (subtest)
		goto fail;

	test = 2;
	subtest = memset32_selftest();
	if (subtest)
		goto fail;

	test = 3;
	subtest = memset64_selftest();
	if (subtest)
		goto fail;

	test = 4;
	subtest = strchr_selftest();
	if (subtest)
		goto fail;

	test = 5;
	subtest = strnchr_selftest();
	if (subtest)
		goto fail;

	test = 6;
	subtest = strspn_selftest();
	if (subtest)
		goto fail;

	pr_info("String selftests succeeded\n");
	return 0;
fail:
	pr_crit("String selftest failure %d.%08x\n", test, subtest);
	return 0;
}

module_init(string_selftest_init);
module_exit(string_selftest_remove);
MODULE_LICENSE("GPL v2");
>
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.h90
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox.c575
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox.h124
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox_helper.c61
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox_helper.h44
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.c364
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.h149
-rw-r--r--drivers/accel/amdxdna/amdxdna_pm.c94
-rw-r--r--drivers/accel/amdxdna/amdxdna_pm.h18
-rw-r--r--drivers/accel/amdxdna/amdxdna_sysfs.c67
-rw-r--r--drivers/accel/amdxdna/amdxdna_ubuf.c232
-rw-r--r--drivers/accel/amdxdna/amdxdna_ubuf.h19
-rw-r--r--drivers/accel/amdxdna/npu1_regs.c122
-rw-r--r--drivers/accel/amdxdna/npu2_regs.c115
-rw-r--r--drivers/accel/amdxdna/npu4_regs.c146
-rw-r--r--drivers/accel/amdxdna/npu5_regs.c115
-rw-r--r--drivers/accel/amdxdna/npu6_regs.c116
-rw-r--r--drivers/accel/drm_accel.c208
-rw-r--r--drivers/accel/ethosu/Kconfig11
-rw-r--r--drivers/accel/ethosu/Makefile4
-rw-r--r--drivers/accel/ethosu/ethosu_device.h197
-rw-r--r--drivers/accel/ethosu/ethosu_drv.c403
-rw-r--r--drivers/accel/ethosu/ethosu_drv.h15
-rw-r--r--drivers/accel/ethosu/ethosu_gem.c704
-rw-r--r--drivers/accel/ethosu/ethosu_gem.h46
-rw-r--r--drivers/accel/ethosu/ethosu_job.c497
-rw-r--r--drivers/accel/ethosu/ethosu_job.h40
-rw-r--r--drivers/accel/habanalabs/Kconfig52
-rw-r--r--drivers/accel/habanalabs/Makefile20
-rw-r--r--drivers/accel/habanalabs/common/Makefile20
-rw-r--r--drivers/accel/habanalabs/common/asid.c (renamed from drivers/misc/habanalabs/common/asid.c)0
-rw-r--r--drivers/accel/habanalabs/common/command_buffer.c (renamed from drivers/misc/habanalabs/common/command_buffer.c)161
-rw-r--r--drivers/accel/habanalabs/common/command_submission.c (renamed from drivers/misc/habanalabs/common/command_submission.c)809
-rw-r--r--drivers/accel/habanalabs/common/context.c449
-rw-r--r--drivers/accel/habanalabs/common/debugfs.c2250
-rw-r--r--drivers/accel/habanalabs/common/decoder.c141
-rw-r--r--drivers/accel/habanalabs/common/device.c2968
-rw-r--r--drivers/accel/habanalabs/common/firmware_if.c (renamed from drivers/misc/habanalabs/common/firmware_if.c)980
-rw-r--r--drivers/accel/habanalabs/common/habanalabs.h (renamed from drivers/misc/habanalabs/common/habanalabs.h)907
-rw-r--r--drivers/accel/habanalabs/common/habanalabs_drv.c784
-rw-r--r--drivers/accel/habanalabs/common/habanalabs_ioctl.c1319
-rw-r--r--drivers/accel/habanalabs/common/hldio.c437
-rw-r--r--drivers/accel/habanalabs/common/hldio.h146
-rw-r--r--drivers/accel/habanalabs/common/hw_queue.c (renamed from drivers/misc/habanalabs/common/hw_queue.c)21
-rw-r--r--drivers/accel/habanalabs/common/hwmon.c931
-rw-r--r--drivers/accel/habanalabs/common/irq.c724
-rw-r--r--drivers/accel/habanalabs/common/memory.c (renamed from drivers/misc/habanalabs/common/memory.c)858
-rw-r--r--drivers/accel/habanalabs/common/memory_mgr.c (renamed from drivers/misc/habanalabs/common/memory_mgr.c)70
-rw-r--r--drivers/accel/habanalabs/common/mmu/Makefile3
-rw-r--r--drivers/accel/habanalabs/common/mmu/mmu.c (renamed from drivers/misc/habanalabs/common/mmu/mmu.c)355
-rw-r--r--drivers/accel/habanalabs/common/mmu/mmu_v1.c550
-rw-r--r--drivers/accel/habanalabs/common/mmu/mmu_v2.c338
-rw-r--r--drivers/accel/habanalabs/common/mmu/mmu_v2_hr.c (renamed from drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c)24
-rw-r--r--drivers/accel/habanalabs/common/pci/Makefile (renamed from drivers/misc/habanalabs/common/pci/Makefile)0
-rw-r--r--drivers/accel/habanalabs/common/pci/pci.c439
-rw-r--r--drivers/accel/habanalabs/common/security.c792
-rw-r--r--drivers/accel/habanalabs/common/security.h162
-rw-r--r--drivers/accel/habanalabs/common/state_dump.c (renamed from drivers/misc/habanalabs/common/state_dump.c)2
-rw-r--r--drivers/accel/habanalabs/common/sysfs.c567
-rw-r--r--drivers/accel/habanalabs/gaudi/Makefile (renamed from drivers/misc/habanalabs/gaudi/Makefile)0
-rw-r--r--drivers/accel/habanalabs/gaudi/gaudi.c (renamed from drivers/misc/habanalabs/gaudi/gaudi.c)525
-rw-r--r--drivers/accel/habanalabs/gaudi/gaudiP.h (renamed from drivers/misc/habanalabs/gaudi/gaudiP.h)19
-rw-r--r--drivers/accel/habanalabs/gaudi/gaudi_coresight.c (renamed from drivers/misc/habanalabs/gaudi/gaudi_coresight.c)15
-rw-r--r--drivers/accel/habanalabs/gaudi/gaudi_security.c (renamed from drivers/misc/habanalabs/gaudi/gaudi_security.c)0
-rw-r--r--drivers/accel/habanalabs/gaudi2/Makefile (renamed from drivers/misc/habanalabs/gaudi2/Makefile)0
-rw-r--r--drivers/accel/habanalabs/gaudi2/gaudi2.c12008
-rw-r--r--drivers/accel/habanalabs/gaudi2/gaudi2P.h (renamed from drivers/misc/habanalabs/gaudi2/gaudi2P.h)105
-rw-r--r--drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c (renamed from drivers/misc/habanalabs/gaudi2/gaudi2_coresight.c)60
-rw-r--r--drivers/accel/habanalabs/gaudi2/gaudi2_coresight_regs.h (renamed from drivers/misc/habanalabs/gaudi2/gaudi2_coresight_regs.h)0
-rw-r--r--drivers/accel/habanalabs/gaudi2/gaudi2_masks.h (renamed from drivers/misc/habanalabs/gaudi2/gaudi2_masks.h)24
-rw-r--r--drivers/accel/habanalabs/gaudi2/gaudi2_security.c (renamed from drivers/misc/habanalabs/gaudi2/gaudi2_security.c)102
-rw-r--r--drivers/accel/habanalabs/goya/Makefile (renamed from drivers/misc/habanalabs/goya/Makefile)0
-rw-r--r--drivers/accel/habanalabs/goya/goya.c (renamed from drivers/misc/habanalabs/goya/goya.c)142
-rw-r--r--drivers/accel/habanalabs/goya/goyaP.h (renamed from drivers/misc/habanalabs/goya/goyaP.h)4
-rw-r--r--drivers/accel/habanalabs/goya/goya_coresight.c (renamed from drivers/misc/habanalabs/goya/goya_coresight.c)24
-rw-r--r--drivers/accel/habanalabs/goya/goya_hwmgr.c (renamed from drivers/misc/habanalabs/goya/goya_hwmgr.c)0
-rw-r--r--drivers/accel/habanalabs/goya/goya_security.c (renamed from drivers/misc/habanalabs/goya/goya_security.c)0
-rw-r--r--drivers/accel/habanalabs/include/common/qman_if.h (renamed from drivers/misc/habanalabs/include/common/qman_if.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/cpu_if_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/cpu_if_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_core_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_core_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_qm_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma1_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma1_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma1_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma1_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma2_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma2_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma2_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma2_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma3_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma3_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma3_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma3_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma4_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma4_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma4_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma4_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma5_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma5_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma5_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma5_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma6_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma6_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma6_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma6_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma7_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma7_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma7_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma7_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/gaudi_blocks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_blocks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/gaudi_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h)2
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_qm_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme1_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme1_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme2_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme2_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme2_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme2_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mme3_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mme3_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/mmu_up_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/mmu_up_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm0_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm0_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic1_qm0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic1_qm0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic1_qm1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic1_qm1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic2_qm0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic2_qm0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic2_qm1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic2_qm1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic3_qm0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic3_qm0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic3_qm1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic3_qm1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic4_qm0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic4_qm0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nic4_qm1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nic4_qm1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_2_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_2_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_3_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_3_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_4_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_4_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_5_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_5_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_6_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_6_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_7_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_7_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_etr_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_etr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_global_conf_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_global_conf_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_global_conf_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_global_conf_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_timestamp_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_timestamp_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_2_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_2_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_3_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_3_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_4_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_4_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_5_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_5_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_6_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_6_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_7_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_7_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/stlb_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/stlb_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_cfg_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_cfg_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_qm_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc1_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc1_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc1_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc1_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc2_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc2_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc2_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc2_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc3_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc3_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc3_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc3_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc4_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc4_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc4_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc4_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc5_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc5_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc5_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc5_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc6_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc6_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc6_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc6_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc7_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc7_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/asic_reg/tpc7_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi/asic_reg/tpc7_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_async_events.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_coresight.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi_coresight.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_fw_if.h59
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_masks.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_packets.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi_packets.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi/gaudi_reg_map.h (renamed from drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/arc/gaudi2_arc_common_packets.h211
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_acp_eng_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_acp_eng_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_kdma_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_kdma_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/cpu_if_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/cpu_if_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_arc_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_arc_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_core_ctx_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_core_ctx_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_masks.h)15
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_masks.h)41
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_acc_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_acc_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_master_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_master_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_slave_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_slave_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_base_addr_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_base_addr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_end_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_end_regs.h)6
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_start_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_start_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_a_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_a_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_b_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_b_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_masks.h)9
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_mme_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_mme_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_acp_eng_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_acp_eng_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_secured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_secured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_mstr_if_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_mstr_if_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_wb0_mstr_if_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_wb0_mstr_if_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_hbw_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_hbw_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_lbw_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_lbw_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_hbw_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_hbw_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_lbw_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_lbw_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_tensor_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_tensor_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_sync_object_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_sync_object_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_tensor_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_tensor_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_special_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_special_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_busmon_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_busmon_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_etf_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_etf_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_funnel_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_funnel_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_spmu_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_spmu_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_stm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_stm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_arc_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_arc_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_dec_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_dec_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_masks.h)6
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_ctrl_special_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_ctrl_special_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_mme_ctrl_lo_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore1_mme_ctrl_lo_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_sync_mngr_glbl_regs.h1203
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore3_mme_ctrl_lo_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore3_mme_ctrl_lo_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/gaudi2_blocks_linux_driver.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_blocks_linux_driver.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h)24
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm0_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm0_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm_arc_aux0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm_arc_aux0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qpc0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qpc0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_completion_queue_ci_1_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_completion_queue_ci_1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_unsecure_doorbell0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_unsecure_doorbell0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dbi_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dbi_regs.h)3
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_dec_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_dec_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_masks.h)3
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_ctrl_special_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_ctrl_special_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h185
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_special_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_special_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_arc_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_arc_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_secured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_secured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma1_core_ctx_axuser_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma1_core_ctx_axuser_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma1_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma1_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_masks.h)3
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_pif_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_pif_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_etr_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_etr_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_etr_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_etr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_masks.h)27
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_timestamp_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_timestamp_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_desc_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_desc_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_masks.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_arc_aux_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_arc_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_axuser_nonsecured_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_axuser_nonsecured_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_cgm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_cgm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/xbar_edge_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/xbar_edge_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/asic_reg/xbar_mid_0_regs.h (renamed from drivers/misc/habanalabs/include/gaudi2/asic_reg/xbar_mid_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2.h (renamed from drivers/misc/habanalabs/include/gaudi2/gaudi2.h)4
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_async_events.h (renamed from drivers/misc/habanalabs/include/gaudi2/gaudi2_async_events.h)11
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_async_ids_map_extended.h2692
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_coresight.h (renamed from drivers/misc/habanalabs/include/gaudi2/gaudi2_coresight.h)0
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_fw_if.h (renamed from drivers/misc/habanalabs/include/gaudi2/gaudi2_fw_if.h)41
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_packets.h (renamed from drivers/misc/habanalabs/include/gaudi2/gaudi2_packets.h)4
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_reg_map.h69
-rw-r--r--drivers/accel/habanalabs/include/gaudi2/gaudi2_special_blocks.h157
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/cpu_if_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/cpu_if_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/cpu_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/cpu_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_0_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_1_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_2_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_2_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_3_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_3_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_4_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_4_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_macro_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_macro_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_macro_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_macro_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_nrtr_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_nrtr_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_nrtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_nrtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_0_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_0_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_0_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_0_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_1_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_1_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_2_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_2_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_3_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_3_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_4_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_4_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/goya_blocks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/goya_blocks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/goya_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/goya_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/ic_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/ic_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mc_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mc_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme1_rtr_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme1_rtr_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme1_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme1_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme2_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme2_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme3_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme3_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme4_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme4_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme5_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme5_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme6_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme6_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme_cmdq_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme_cmdq_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme_qm_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mme_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mme_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mmu_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mmu_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/mmu_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/mmu_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/pci_nrtr_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/pci_nrtr_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/pci_nrtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/pci_nrtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/pcie_aux_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/pcie_aux_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/pcie_wrap_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/pcie_wrap_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_emmc_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_emmc_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_etr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_global_conf_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_global_conf_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_global_conf_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_global_conf_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_mme_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_mme_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_pci_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_pci_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_spi_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_spi_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/psoc_timestamp_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/psoc_timestamp_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x0_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x0_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x1_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x1_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x2_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x2_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x3_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x3_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x4_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x4_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/stlb_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/stlb_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/stlb_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/stlb_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cfg_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cfg_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cmdq_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cmdq_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_nrtr_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_nrtr_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_nrtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_nrtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_qm_masks.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_qm_masks.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc0_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc0_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc1_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc1_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc1_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc1_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc1_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc1_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc1_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc1_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc2_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc2_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc2_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc2_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc2_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc2_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc2_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc2_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc3_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc3_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc3_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc3_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc3_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc3_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc3_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc3_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc4_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc4_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc4_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc4_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc4_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc4_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc4_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc4_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc5_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc5_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc5_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc5_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc5_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc5_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc5_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc5_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc6_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc6_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc6_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc6_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc6_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc6_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc6_rtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc6_rtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc7_cfg_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc7_cfg_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc7_cmdq_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc7_cmdq_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc7_nrtr_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc7_nrtr_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc7_qm_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc7_qm_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/asic_reg/tpc_pll_regs.h (renamed from drivers/misc/habanalabs/include/goya/asic_reg/tpc_pll_regs.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/goya.h (renamed from drivers/misc/habanalabs/include/goya/goya.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/goya_async_events.h (renamed from drivers/misc/habanalabs/include/goya/goya_async_events.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/goya_coresight.h (renamed from drivers/misc/habanalabs/include/goya/goya_coresight.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/goya_fw_if.h (renamed from drivers/misc/habanalabs/include/goya/goya_fw_if.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/goya_packets.h (renamed from drivers/misc/habanalabs/include/goya/goya_packets.h)0
-rw-r--r--drivers/accel/habanalabs/include/goya/goya_reg_map.h (renamed from drivers/misc/habanalabs/include/goya/goya_reg_map.h)0
-rw-r--r--drivers/accel/habanalabs/include/hw_ip/mmu/mmu_general.h (renamed from drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h)2
-rw-r--r--drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v1_0.h (renamed from drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h)0
-rw-r--r--drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v1_1.h (renamed from drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h)0
-rw-r--r--drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v2_0.h (renamed from drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v2_0.h)0
-rw-r--r--drivers/accel/habanalabs/include/hw_ip/pci/pci_general.h (renamed from drivers/misc/habanalabs/include/hw_ip/pci/pci_general.h)9
-rw-r--r--drivers/accel/ivpu/Kconfig27
-rw-r--r--drivers/accel/ivpu/Makefile30
-rw-r--r--drivers/accel/ivpu/ivpu_coredump.c39
-rw-r--r--drivers/accel/ivpu/ivpu_coredump.h25
-rw-r--r--drivers/accel/ivpu/ivpu_debugfs.c512
-rw-r--r--drivers/accel/ivpu/ivpu_debugfs.h17
-rw-r--r--drivers/accel/ivpu/ivpu_drv.c785
-rw-r--r--drivers/accel/ivpu/ivpu_drv.h316
-rw-r--r--drivers/accel/ivpu/ivpu_fw.c728
-rw-r--r--drivers/accel/ivpu/ivpu_fw.h67
-rw-r--r--drivers/accel/ivpu/ivpu_fw_log.c175
-rw-r--r--drivers/accel/ivpu/ivpu_fw_log.h31
-rw-r--r--drivers/accel/ivpu/ivpu_gem.c579
-rw-r--r--drivers/accel/ivpu/ivpu_gem.h117
-rw-r--r--drivers/accel/ivpu/ivpu_gem_userptr.c213
-rw-r--r--drivers/accel/ivpu/ivpu_hw.c420
-rw-r--r--drivers/accel/ivpu/ivpu_hw.h154
-rw-r--r--drivers/accel/ivpu/ivpu_hw_37xx_reg.h178
-rw-r--r--drivers/accel/ivpu/ivpu_hw_40xx_reg.h195
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs.c905
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs.h50
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h112
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h83
-rw-r--r--drivers/accel/ivpu/ivpu_hw_ip.c1199
-rw-r--r--drivers/accel/ivpu/ivpu_hw_ip.h36
-rw-r--r--drivers/accel/ivpu/ivpu_hw_reg_io.h133
-rw-r--r--drivers/accel/ivpu/ivpu_ipc.c593
-rw-r--r--drivers/accel/ivpu/ivpu_ipc.h113
-rw-r--r--drivers/accel/ivpu/ivpu_job.c1153
-rw-r--r--drivers/accel/ivpu/ivpu_job.h93
-rw-r--r--drivers/accel/ivpu/ivpu_jsm_msg.c562
-rw-r--r--drivers/accel/ivpu/ivpu_jsm_msg.h48
-rw-r--r--drivers/accel/ivpu/ivpu_mmu.c1029
-rw-r--r--drivers/accel/ivpu/ivpu_mmu.h53
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.c640
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.h51
-rw-r--r--drivers/accel/ivpu/ivpu_ms.c342
-rw-r--r--drivers/accel/ivpu/ivpu_ms.h36
-rw-r--r--drivers/accel/ivpu/ivpu_pm.c511
-rw-r--r--drivers/accel/ivpu/ivpu_pm.h50
-rw-r--r--drivers/accel/ivpu/ivpu_sysfs.c159
-rw-r--r--drivers/accel/ivpu/ivpu_sysfs.h13
-rw-r--r--drivers/accel/ivpu/ivpu_trace.h73
-rw-r--r--drivers/accel/ivpu/ivpu_trace_points.c9
-rw-r--r--drivers/accel/ivpu/vpu_boot_api.h473
-rw-r--r--drivers/accel/ivpu/vpu_jsm_api.h1726
-rw-r--r--drivers/accel/qaic/Kconfig23
-rw-r--r--drivers/accel/qaic/Makefile19
-rw-r--r--drivers/accel/qaic/mhi_controller.c976
-rw-r--r--drivers/accel/qaic/mhi_controller.h16
-rw-r--r--drivers/accel/qaic/qaic.h360
-rw-r--r--drivers/accel/qaic/qaic_control.c1560
-rw-r--r--drivers/accel/qaic/qaic_data.c2072
-rw-r--r--drivers/accel/qaic/qaic_debugfs.c304
-rw-r--r--drivers/accel/qaic/qaic_debugfs.h20
-rw-r--r--drivers/accel/qaic/qaic_drv.c860
-rw-r--r--drivers/accel/qaic/qaic_ras.c642
-rw-r--r--drivers/accel/qaic/qaic_ras.h10
-rw-r--r--drivers/accel/qaic/qaic_ssr.c815
-rw-r--r--drivers/accel/qaic/qaic_ssr.h17
-rw-r--r--drivers/accel/qaic/qaic_sysfs.c109
-rw-r--r--drivers/accel/qaic/qaic_timesync.c404
-rw-r--r--drivers/accel/qaic/qaic_timesync.h14
-rw-r--r--drivers/accel/qaic/sahara.c935
-rw-r--r--drivers/accel/qaic/sahara.h10
-rw-r--r--drivers/accel/rocket/Kconfig24
-rw-r--r--drivers/accel/rocket/Makefile10
-rw-r--r--drivers/accel/rocket/rocket_core.c110
-rw-r--r--drivers/accel/rocket/rocket_core.h64
-rw-r--r--drivers/accel/rocket/rocket_device.c60
-rw-r--r--drivers/accel/rocket/rocket_device.h30
-rw-r--r--drivers/accel/rocket/rocket_drv.c290
-rw-r--r--drivers/accel/rocket/rocket_drv.h32
-rw-r--r--drivers/accel/rocket/rocket_gem.c182
-rw-r--r--drivers/accel/rocket/rocket_gem.h34
-rw-r--r--drivers/accel/rocket/rocket_job.c637
-rw-r--r--drivers/accel/rocket/rocket_job.h52
-rw-r--r--drivers/accel/rocket/rocket_registers.h4404
-rw-r--r--drivers/accessibility/braille/braille_console.c1
-rw-r--r--drivers/accessibility/speakup/Kconfig1
-rw-r--r--drivers/accessibility/speakup/Makefile2
-rw-r--r--drivers/accessibility/speakup/devsynth.c116
-rw-r--r--drivers/accessibility/speakup/genmap.c1
-rw-r--r--drivers/accessibility/speakup/kobjects.c28
-rw-r--r--drivers/accessibility/speakup/main.c94
-rw-r--r--drivers/accessibility/speakup/makemapdata.c8
-rw-r--r--drivers/accessibility/speakup/speakup.h3
-rw-r--r--drivers/accessibility/speakup/speakup_acntpc.c38
-rw-r--r--drivers/accessibility/speakup/speakup_acntsa.c37
-rw-r--r--drivers/accessibility/speakup/speakup_apollo.c46
-rw-r--r--drivers/accessibility/speakup/speakup_audptr.c42
-rw-r--r--drivers/accessibility/speakup/speakup_bns.c36
-rw-r--r--drivers/accessibility/speakup/speakup_decext.c44
-rw-r--r--drivers/accessibility/speakup/speakup_decpc.c48
-rw-r--r--drivers/accessibility/speakup/speakup_dectlk.c45
-rw-r--r--drivers/accessibility/speakup/speakup_dtlk.c50
-rw-r--r--drivers/accessibility/speakup/speakup_dummy.c55
-rw-r--r--drivers/accessibility/speakup/speakup_keypc.c29
-rw-r--r--drivers/accessibility/speakup/speakup_ltlk.c53
-rw-r--r--drivers/accessibility/speakup/speakup_soft.c81
-rw-r--r--drivers/accessibility/speakup/speakup_spkout.c43
-rw-r--r--drivers/accessibility/speakup/speakup_txprt.c45
-rw-r--r--drivers/accessibility/speakup/spk_ttyio.c10
-rw-r--r--drivers/accessibility/speakup/spk_types.h4
-rw-r--r--drivers/accessibility/speakup/synth.c98
-rw-r--r--drivers/accessibility/speakup/utils.h2
-rw-r--r--drivers/accessibility/speakup/varhandlers.c13
-rw-r--r--drivers/acpi/Kconfig75
-rw-r--r--drivers/acpi/Makefile24
-rw-r--r--drivers/acpi/ac.c128
-rw-r--r--drivers/acpi/acpi_amba.c128
-rw-r--r--drivers/acpi/acpi_apd.c19
-rw-r--r--drivers/acpi/acpi_cmos_rtc.c87
-rw-r--r--drivers/acpi/acpi_dbg.c26
-rw-r--r--drivers/acpi/acpi_extlog.c62
-rw-r--r--drivers/acpi/acpi_ffh.c53
-rw-r--r--drivers/acpi/acpi_fpdt.c67
-rw-r--r--drivers/acpi/acpi_ipmi.c23
-rw-r--r--drivers/acpi/acpi_lpit.c21
-rw-r--r--drivers/acpi/acpi_lpss.c1388
-rw-r--r--drivers/acpi/acpi_memhotplug.c3
-rw-r--r--drivers/acpi/acpi_mrrm.c211
-rw-r--r--drivers/acpi/acpi_pad.c122
-rw-r--r--drivers/acpi/acpi_pcc.c48
-rw-r--r--drivers/acpi/acpi_platform.c53
-rw-r--r--drivers/acpi/acpi_pnp.c18
-rw-r--r--drivers/acpi/acpi_processor.c327
-rw-r--r--drivers/acpi/acpi_tad.c109
-rw-r--r--drivers/acpi/acpi_video.c315
-rw-r--r--drivers/acpi/acpi_watchdog.c4
-rw-r--r--drivers/acpi/acpica/Makefile4
-rw-r--r--drivers/acpi/acpica/acapps.h4
-rw-r--r--drivers/acpi/acpica/accommon.h2
-rw-r--r--drivers/acpi/acpica/acconvert.h2
-rw-r--r--drivers/acpi/acpica/acdebug.h4
-rw-r--r--drivers/acpi/acpica/acdispat.h2
-rw-r--r--drivers/acpi/acpica/acevents.h4
-rw-r--r--drivers/acpi/acpica/acglobal.h10
-rw-r--r--drivers/acpi/acpica/achware.h6
-rw-r--r--drivers/acpi/acpica/acinterp.h5
-rw-r--r--drivers/acpi/acpica/aclocal.h49
-rw-r--r--drivers/acpi/acpica/acmacros.h2
-rw-r--r--drivers/acpi/acpica/acnamesp.h2
-rw-r--r--drivers/acpi/acpica/acobject.h109
-rw-r--r--drivers/acpi/acpica/acopcode.h2
-rw-r--r--drivers/acpi/acpica/acparser.h2
-rw-r--r--drivers/acpi/acpica/acpredef.h8
-rw-r--r--drivers/acpi/acpica/acresrc.h4
-rw-r--r--drivers/acpi/acpica/acstruct.h2
-rw-r--r--drivers/acpi/acpica/actables.h7
-rw-r--r--drivers/acpi/acpica/acutils.h17
-rw-r--r--drivers/acpi/acpica/amlcode.h2
-rw-r--r--drivers/acpi/acpica/amlresrc.h32
-rw-r--r--drivers/acpi/acpica/dbcmds.c58
-rw-r--r--drivers/acpi/acpica/dbconvert.c2
-rw-r--r--drivers/acpi/acpica/dbhistry.c2
-rw-r--r--drivers/acpi/acpica/dbinput.c8
-rw-r--r--drivers/acpi/acpica/dbnames.c11
-rw-r--r--drivers/acpi/acpica/dsargs.c2
-rw-r--r--drivers/acpi/acpica/dscontrol.c2
-rw-r--r--drivers/acpi/acpica/dsdebug.c2
-rw-r--r--drivers/acpi/acpica/dsfield.c2
-rw-r--r--drivers/acpi/acpica/dsinit.c2
-rw-r--r--drivers/acpi/acpica/dsmethod.c36
-rw-r--r--drivers/acpi/acpica/dsmthdat.c1
-rw-r--r--drivers/acpi/acpica/dsobject.c2
-rw-r--r--drivers/acpi/acpica/dsopcode.c2
-rw-r--r--drivers/acpi/acpica/dspkginit.c2
-rw-r--r--drivers/acpi/acpica/dsutils.c9
-rw-r--r--drivers/acpi/acpica/dswexec.c8
-rw-r--r--drivers/acpi/acpica/dswload.c2
-rw-r--r--drivers/acpi/acpica/dswload2.c2
-rw-r--r--drivers/acpi/acpica/dswscope.c2
-rw-r--r--drivers/acpi/acpica/dswstate.c17
-rw-r--r--drivers/acpi/acpica/evevent.c2
-rw-r--r--drivers/acpi/acpica/evglock.c6
-rw-r--r--drivers/acpi/acpica/evgpe.c2
-rw-r--r--drivers/acpi/acpica/evgpeblk.c2
-rw-r--r--drivers/acpi/acpica/evgpeinit.c3
-rw-r--r--drivers/acpi/acpica/evgpeutil.c2
-rw-r--r--drivers/acpi/acpica/evhandler.c2
-rw-r--r--drivers/acpi/acpica/evmisc.c2
-rw-r--r--drivers/acpi/acpica/evregion.c17
-rw-r--r--drivers/acpi/acpica/evrgnini.c6
-rw-r--r--drivers/acpi/acpica/evxface.c2
-rw-r--r--drivers/acpi/acpica/evxfevnt.c2
-rw-r--r--drivers/acpi/acpica/evxfgpe.c2
-rw-r--r--drivers/acpi/acpica/evxfregn.c100
-rw-r--r--drivers/acpi/acpica/exconcat.c2
-rw-r--r--drivers/acpi/acpica/exconfig.c6
-rw-r--r--drivers/acpi/acpica/exconvrt.c64
-rw-r--r--drivers/acpi/acpica/excreate.c2
-rw-r--r--drivers/acpi/acpica/exdebug.c2
-rw-r--r--drivers/acpi/acpica/exdump.c2
-rw-r--r--drivers/acpi/acpica/exfield.c10
-rw-r--r--drivers/acpi/acpica/exfldio.c2
-rw-r--r--drivers/acpi/acpica/exmisc.c2
-rw-r--r--drivers/acpi/acpica/exmutex.c2
-rw-r--r--drivers/acpi/acpica/exnames.c2
-rw-r--r--drivers/acpi/acpica/exoparg1.c2
-rw-r--r--drivers/acpi/acpica/exoparg2.c2
-rw-r--r--drivers/acpi/acpica/exoparg3.c2
-rw-r--r--drivers/acpi/acpica/exoparg6.c2
-rw-r--r--drivers/acpi/acpica/exprep.c5
-rw-r--r--drivers/acpi/acpica/exregion.c29
-rw-r--r--drivers/acpi/acpica/exresnte.c2
-rw-r--r--drivers/acpi/acpica/exresolv.c2
-rw-r--r--drivers/acpi/acpica/exresop.c2
-rw-r--r--drivers/acpi/acpica/exserial.c17
-rw-r--r--drivers/acpi/acpica/exstore.c2
-rw-r--r--drivers/acpi/acpica/exstoren.c2
-rw-r--r--drivers/acpi/acpica/exstorob.c2
-rw-r--r--drivers/acpi/acpica/exsystem.c13
-rw-r--r--drivers/acpi/acpica/extrace.c57
-rw-r--r--drivers/acpi/acpica/exutils.c2
-rw-r--r--drivers/acpi/acpica/hwacpi.c2
-rw-r--r--drivers/acpi/acpica/hwesleep.c2
-rw-r--r--drivers/acpi/acpica/hwgpe.c2
-rw-r--r--drivers/acpi/acpica/hwsleep.c2
-rw-r--r--drivers/acpi/acpica/hwtimer.c2
-rw-r--r--drivers/acpi/acpica/hwvalid.c9
-rw-r--r--drivers/acpi/acpica/hwxface.c2
-rw-r--r--drivers/acpi/acpica/hwxfsleep.c17
-rw-r--r--drivers/acpi/acpica/nsarguments.c2
-rw-r--r--drivers/acpi/acpica/nsconvert.c2
-rw-r--r--drivers/acpi/acpica/nsdump.c2
-rw-r--r--drivers/acpi/acpica/nsdumpdv.c2
-rw-r--r--drivers/acpi/acpica/nsinit.c2
-rw-r--r--drivers/acpi/acpica/nsload.c2
-rw-r--r--drivers/acpi/acpica/nsnames.c2
-rw-r--r--drivers/acpi/acpica/nsparse.c2
-rw-r--r--drivers/acpi/acpica/nspredef.c2
-rw-r--r--drivers/acpi/acpica/nsprepkg.c2
-rw-r--r--drivers/acpi/acpica/nsrepair.c14
-rw-r--r--drivers/acpi/acpica/nsrepair2.c6
-rw-r--r--drivers/acpi/acpica/nsutils.c2
-rw-r--r--drivers/acpi/acpica/nswalk.c11
-rw-r--r--drivers/acpi/acpica/nsxfname.c4
-rw-r--r--drivers/acpi/acpica/psargs.c49
-rw-r--r--drivers/acpi/acpica/psloop.c2
-rw-r--r--drivers/acpi/acpica/psobject.c54
-rw-r--r--drivers/acpi/acpica/psopcode.c4
-rw-r--r--drivers/acpi/acpica/psopinfo.c6
-rw-r--r--drivers/acpi/acpica/psparse.c2
-rw-r--r--drivers/acpi/acpica/psscope.c2
-rw-r--r--drivers/acpi/acpica/pstree.c2
-rw-r--r--drivers/acpi/acpica/psutils.c2
-rw-r--r--drivers/acpi/acpica/pswalk.c2
-rw-r--r--drivers/acpi/acpica/psxface.c2
-rw-r--r--drivers/acpi/acpica/rsaddr.c3
-rw-r--r--drivers/acpi/acpica/rscalc.c35
-rw-r--r--drivers/acpi/acpica/rsdump.c2
-rw-r--r--drivers/acpi/acpica/rsdumpinfo.c17
-rw-r--r--drivers/acpi/acpica/rsinfo.c5
-rw-r--r--drivers/acpi/acpica/rsmisc.c10
-rw-r--r--drivers/acpi/acpica/rsserial.c49
-rw-r--r--drivers/acpi/acpica/tbdata.c4
-rw-r--r--drivers/acpi/acpica/tbfadt.c34
-rw-r--r--drivers/acpi/acpica/tbfind.c6
-rw-r--r--drivers/acpi/acpica/tbinstal.c2
-rw-r--r--drivers/acpi/acpica/tbprint.c93
-rw-r--r--drivers/acpi/acpica/tbutils.c18
-rw-r--r--drivers/acpi/acpica/tbxface.c2
-rw-r--r--drivers/acpi/acpica/tbxfload.c2
-rw-r--r--drivers/acpi/acpica/tbxfroot.c34
-rw-r--r--drivers/acpi/acpica/utaddress.c2
-rw-r--r--drivers/acpi/acpica/utalloc.c2
-rw-r--r--drivers/acpi/acpica/utascii.c2
-rw-r--r--drivers/acpi/acpica/utbuffer.c2
-rw-r--r--drivers/acpi/acpica/utcache.c4
-rw-r--r--drivers/acpi/acpica/utcksum.c170
-rw-r--r--drivers/acpi/acpica/utcopy.c9
-rw-r--r--drivers/acpi/acpica/utdebug.c12
-rw-r--r--drivers/acpi/acpica/utdecode.c2
-rw-r--r--drivers/acpi/acpica/utdelete.c6
-rw-r--r--drivers/acpi/acpica/uteval.c2
-rw-r--r--drivers/acpi/acpica/utglobal.c2
-rw-r--r--drivers/acpi/acpica/uthex.c2
-rw-r--r--drivers/acpi/acpica/utids.c2
-rw-r--r--drivers/acpi/acpica/utinit.c4
-rw-r--r--drivers/acpi/acpica/utlock.c2
-rw-r--r--drivers/acpi/acpica/utobject.c2
-rw-r--r--drivers/acpi/acpica/utosi.c3
-rw-r--r--drivers/acpi/acpica/utpredef.c2
-rw-r--r--drivers/acpi/acpica/utprint.c9
-rw-r--r--drivers/acpi/acpica/utresdecode.c11
-rw-r--r--drivers/acpi/acpica/utresrc.c3
-rw-r--r--drivers/acpi/acpica/utstring.c10
-rw-r--r--drivers/acpi/acpica/uttrack.c2
-rw-r--r--drivers/acpi/acpica/utuuid.c2
-rw-r--r--drivers/acpi/acpica/utxface.c2
-rw-r--r--drivers/acpi/acpica/utxfinit.c26
-rw-r--r--drivers/acpi/apei/Kconfig14
-rw-r--r--drivers/acpi/apei/Makefile2
-rw-r--r--drivers/acpi/apei/apei-base.c9
-rw-r--r--drivers/acpi/apei/apei-internal.h24
-rw-r--r--drivers/acpi/apei/bert.c6
-rw-r--r--drivers/acpi/apei/einj-core.c1201
-rw-r--r--drivers/acpi/apei/einj-cxl.c113
-rw-r--r--drivers/acpi/apei/einj.c799
-rw-r--r--drivers/acpi/apei/erst-dbg.c9
-rw-r--r--drivers/acpi/apei/erst.c47
-rw-r--r--drivers/acpi/apei/ghes.c561
-rw-r--r--drivers/acpi/apei/hest.c51
-rw-r--r--drivers/acpi/arm64/Kconfig6
-rw-r--r--drivers/acpi/arm64/Makefile10
-rw-r--r--drivers/acpi/arm64/agdi.c15
-rw-r--r--drivers/acpi/arm64/amba.c117
-rw-r--r--drivers/acpi/arm64/apmt.c180
-rw-r--r--drivers/acpi/arm64/cpuidle.c70
-rw-r--r--drivers/acpi/arm64/dma.c30
-rw-r--r--drivers/acpi/arm64/ffh.c107
-rw-r--r--drivers/acpi/arm64/gtdt.c106
-rw-r--r--drivers/acpi/arm64/init.c15
-rw-r--r--drivers/acpi/arm64/init.h7
-rw-r--r--drivers/acpi/arm64/iort.c95
-rw-r--r--drivers/acpi/arm64/mpam.c411
-rw-r--r--drivers/acpi/arm64/thermal_cpufreq.c22
-rw-r--r--drivers/acpi/battery.c293
-rw-r--r--drivers/acpi/bgrt.c11
-rw-r--r--drivers/acpi/bus.c290
-rw-r--r--drivers/acpi/button.c198
-rw-r--r--drivers/acpi/cppc_acpi.c687
-rw-r--r--drivers/acpi/custom_method.c103
-rw-r--r--drivers/acpi/device_pm.c97
-rw-r--r--drivers/acpi/device_sysfs.c228
-rw-r--r--drivers/acpi/dock.c50
-rw-r--r--drivers/acpi/dptf/Kconfig3
-rw-r--r--drivers/acpi/dptf/Makefile1
-rw-r--r--drivers/acpi/dptf/dptf_pch_fivr.c8
-rw-r--r--drivers/acpi/dptf/dptf_power.c12
-rw-r--r--drivers/acpi/dptf/int340x_thermal.c75
-rw-r--r--drivers/acpi/ec.c285
-rw-r--r--drivers/acpi/event.c4
-rw-r--r--drivers/acpi/evged.c3
-rw-r--r--drivers/acpi/fan.h57
-rw-r--r--drivers/acpi/fan_attr.c63
-rw-r--r--drivers/acpi/fan_core.c359
-rw-r--r--drivers/acpi/fan_hwmon.c180
-rw-r--r--drivers/acpi/glue.c14
-rw-r--r--drivers/acpi/hed.c25
-rw-r--r--drivers/acpi/internal.h96
-rw-r--r--drivers/acpi/ioapic.c1
-rw-r--r--drivers/acpi/irq.c59
-rw-r--r--drivers/acpi/mipi-disco-img.c805
-rw-r--r--drivers/acpi/nfit/core.c135
-rw-r--r--drivers/acpi/nfit/intel.c149
-rw-r--r--drivers/acpi/nfit/nfit.h2
-rw-r--r--drivers/acpi/nhlt.c289
-rw-r--r--drivers/acpi/numa/Kconfig5
-rw-r--r--drivers/acpi/numa/hmat.c495
-rw-r--r--drivers/acpi/numa/srat.c261
-rw-r--r--drivers/acpi/osi.c27
-rw-r--r--drivers/acpi/osl.c143
-rw-r--r--drivers/acpi/pci_irq.c11
-rw-r--r--drivers/acpi/pci_link.c18
-rw-r--r--drivers/acpi/pci_mcfg.c12
-rw-r--r--drivers/acpi/pci_root.c101
-rw-r--r--drivers/acpi/pci_slot.c2
-rw-r--r--drivers/acpi/pfr_telemetry.c15
-rw-r--r--drivers/acpi/pfr_update.c77
-rw-r--r--drivers/acpi/platform_profile.c705
-rw-r--r--drivers/acpi/pmic/intel_pmic.c2
-rw-r--r--drivers/acpi/pmic/intel_pmic.h4
-rw-r--r--drivers/acpi/pmic/intel_pmic_bxtwc.c4
-rw-r--r--drivers/acpi/pmic/intel_pmic_bytcrc.c5
-rw-r--r--drivers/acpi/pmic/intel_pmic_chtdc_ti.c43
-rw-r--r--drivers/acpi/pmic/intel_pmic_chtwc.c7
-rw-r--r--drivers/acpi/pmic/intel_pmic_xpower.c11
-rw-r--r--drivers/acpi/pmic/tps68470_pmic.c6
-rw-r--r--drivers/acpi/power.c129
-rw-r--r--drivers/acpi/pptt.c371
-rw-r--r--drivers/acpi/prmt.c112
-rw-r--r--drivers/acpi/proc.c17
-rw-r--r--drivers/acpi/processor_core.c78
-rw-r--r--drivers/acpi/processor_driver.c73
-rw-r--r--drivers/acpi/processor_idle.c237
-rw-r--r--drivers/acpi/processor_pdc.c87
-rw-r--r--drivers/acpi/processor_perflib.c163
-rw-r--r--drivers/acpi/processor_thermal.c109
-rw-r--r--drivers/acpi/processor_throttling.c11
-rw-r--r--drivers/acpi/property.c565
-rw-r--r--drivers/acpi/resource.c387
-rw-r--r--drivers/acpi/riscv/Kconfig7
-rw-r--r--drivers/acpi/riscv/Makefile5
-rw-r--r--drivers/acpi/riscv/cppc.c155
-rw-r--r--drivers/acpi/riscv/cpuidle.c81
-rw-r--r--drivers/acpi/riscv/init.c15
-rw-r--r--drivers/acpi/riscv/init.h5
-rw-r--r--drivers/acpi/riscv/irq.c404
-rw-r--r--drivers/acpi/riscv/rhct.c170
-rw-r--r--drivers/acpi/riscv/rimt.c520
-rw-r--r--drivers/acpi/sbs.c75
-rw-r--r--drivers/acpi/sbshc.c22
-rw-r--r--drivers/acpi/scan.c781
-rw-r--r--drivers/acpi/sleep.c102
-rw-r--r--drivers/acpi/sleep.h2
-rw-r--r--drivers/acpi/spcr.c26
-rw-r--r--drivers/acpi/sysfs.c28
-rw-r--r--drivers/acpi/tables.c230
-rw-r--r--drivers/acpi/thermal.c1233
-rw-r--r--drivers/acpi/thermal_lib.c166
-rw-r--r--drivers/acpi/tiny-power-button.c49
-rw-r--r--drivers/acpi/utils.c242
-rw-r--r--drivers/acpi/video_detect.c784
-rw-r--r--drivers/acpi/viot.c19
-rw-r--r--drivers/acpi/wakeup.c4
-rw-r--r--drivers/acpi/x86/Makefile8
-rw-r--r--drivers/acpi/x86/apple.c12
-rw-r--r--drivers/acpi/x86/blacklist.c (renamed from drivers/acpi/blacklist.c)2
-rw-r--r--drivers/acpi/x86/cmos_rtc.c98
-rw-r--r--drivers/acpi/x86/lpss.c1352
-rw-r--r--drivers/acpi/x86/s2idle.c298
-rw-r--r--drivers/acpi/x86/utils.c341
-rw-r--r--drivers/amba/Kconfig2
-rw-r--r--drivers/amba/bus.c53
-rw-r--r--drivers/amba/tegra-ahb.c2
-rw-r--r--drivers/android/Kconfig31
-rw-r--r--drivers/android/Makefile5
-rw-r--r--drivers/android/binder.c1156
-rw-r--r--drivers/android/binder/Makefile9
-rw-r--r--drivers/android/binder/allocation.rs602
-rw-r--r--drivers/android/binder/context.rs180
-rw-r--r--drivers/android/binder/deferred_close.rs204
-rw-r--r--drivers/android/binder/defs.rs182
-rw-r--r--drivers/android/binder/error.rs100
-rw-r--r--drivers/android/binder/freeze.rs398
-rw-r--r--drivers/android/binder/node.rs1131
-rw-r--r--drivers/android/binder/node/wrapper.rs78
-rw-r--r--drivers/android/binder/page_range.rs734
-rw-r--r--drivers/android/binder/page_range_helper.c24
-rw-r--r--drivers/android/binder/page_range_helper.h15
-rw-r--r--drivers/android/binder/process.rs1745
-rw-r--r--drivers/android/binder/range_alloc/array.rs251
-rw-r--r--drivers/android/binder/range_alloc/mod.rs329
-rw-r--r--drivers/android/binder/range_alloc/tree.rs488
-rw-r--r--drivers/android/binder/rust_binder.h23
-rw-r--r--drivers/android/binder/rust_binder_events.c59
-rw-r--r--drivers/android/binder/rust_binder_events.h36
-rw-r--r--drivers/android/binder/rust_binder_internal.h87
-rw-r--r--drivers/android/binder/rust_binder_main.rs611
-rw-r--r--drivers/android/binder/rust_binderfs.c795
-rw-r--r--drivers/android/binder/stats.rs89
-rw-r--r--drivers/android/binder/thread.rs1596
-rw-r--r--drivers/android/binder/trace.rs16
-rw-r--r--drivers/android/binder/transaction.rs456
-rw-r--r--drivers/android/binder_alloc.c1120
-rw-r--r--drivers/android/binder_alloc.h111
-rw-r--r--drivers/android/binder_alloc_selftest.c302
-rw-r--r--drivers/android/binder_internal.h58
-rw-r--r--drivers/android/binder_netlink.c32
-rw-r--r--drivers/android/binder_netlink.h21
-rw-r--r--drivers/android/binder_trace.h62
-rw-r--r--drivers/android/binderfs.c173
-rw-r--r--drivers/android/dbitmap.h169
-rw-r--r--drivers/android/tests/.kunitconfig7
-rw-r--r--drivers/android/tests/Makefile6
-rw-r--r--drivers/android/tests/binder_alloc_kunit.c572
-rw-r--r--drivers/ata/Kconfig135
-rw-r--r--drivers/ata/Makefile6
-rw-r--r--drivers/ata/acard-ahci.c16
-rw-r--r--drivers/ata/ahci.c766
-rw-r--r--drivers/ata/ahci.h287
-rw-r--r--drivers/ata/ahci_brcm.c15
-rw-r--r--drivers/ata/ahci_ceva.c135
-rw-r--r--drivers/ata/ahci_da850.c55
-rw-r--r--drivers/ata/ahci_dm816.c8
-rw-r--r--drivers/ata/ahci_dwc.c493
-rw-r--r--drivers/ata/ahci_imx.c448
-rw-r--r--drivers/ata/ahci_mtk.c7
-rw-r--r--drivers/ata/ahci_mvebu.c4
-rw-r--r--drivers/ata/ahci_octeon.c11
-rw-r--r--drivers/ata/ahci_platform.c15
-rw-r--r--drivers/ata/ahci_qoriq.c12
-rw-r--r--drivers/ata/ahci_seattle.c6
-rw-r--r--drivers/ata/ahci_st.c15
-rw-r--r--drivers/ata/ahci_sunxi.c4
-rw-r--r--drivers/ata/ahci_tegra.c7
-rw-r--r--drivers/ata/ahci_xgene.c46
-rw-r--r--drivers/ata/ata_generic.c4
-rw-r--r--drivers/ata/ata_piix.c14
-rw-r--r--drivers/ata/libahci.c388
-rw-r--r--drivers/ata/libahci_platform.c264
-rw-r--r--drivers/ata/libata-acpi.c99
-rw-r--r--drivers/ata/libata-core.c2118
-rw-r--r--drivers/ata/libata-eh.c1074
-rw-r--r--drivers/ata/libata-pmp.c29
-rw-r--r--drivers/ata/libata-sata.c752
-rw-r--r--drivers/ata/libata-scsi.c1690
-rw-r--r--drivers/ata/libata-sff.c176
-rw-r--r--drivers/ata/libata-trace.c2
-rw-r--r--drivers/ata/libata-transport.c334
-rw-r--r--drivers/ata/libata-transport.h3
-rw-r--r--drivers/ata/libata-zpodd.c5
-rw-r--r--drivers/ata/libata.h87
-rw-r--r--drivers/ata/pata_acpi.c4
-rw-r--r--drivers/ata/pata_ali.c12
-rw-r--r--drivers/ata/pata_amd.c6
-rw-r--r--drivers/ata/pata_arasan_cf.c9
-rw-r--r--drivers/ata/pata_artop.c6
-rw-r--r--drivers/ata/pata_atiixp.c4
-rw-r--r--drivers/ata/pata_atp867x.c4
-rw-r--r--drivers/ata/pata_bk3710.c380
-rw-r--r--drivers/ata/pata_buddha.c3
-rw-r--r--drivers/ata/pata_cmd640.c2
-rw-r--r--drivers/ata/pata_cmd64x.c2
-rw-r--r--drivers/ata/pata_cs5520.c16
-rw-r--r--drivers/ata/pata_cs5530.c2
-rw-r--r--drivers/ata/pata_cs5535.c2
-rw-r--r--drivers/ata/pata_cs5536.c4
-rw-r--r--drivers/ata/pata_cypress.c2
-rw-r--r--drivers/ata/pata_efar.c4
-rw-r--r--drivers/ata/pata_ep93xx.c139
-rw-r--r--drivers/ata/pata_falcon.c78
-rw-r--r--drivers/ata/pata_ftide010.c19
-rw-r--r--drivers/ata/pata_gayle.c18
-rw-r--r--drivers/ata/pata_hpt366.c14
-rw-r--r--drivers/ata/pata_hpt37x.c16
-rw-r--r--drivers/ata/pata_hpt3x2n.c4
-rw-r--r--drivers/ata/pata_hpt3x3.c2
-rw-r--r--drivers/ata/pata_icside.c6
-rw-r--r--drivers/ata/pata_imx.c37
-rw-r--r--drivers/ata/pata_isapnp.c5
-rw-r--r--drivers/ata/pata_it8213.c6
-rw-r--r--drivers/ata/pata_it821x.c11
-rw-r--r--drivers/ata/pata_ixp4xx_cf.c28
-rw-r--r--drivers/ata/pata_jmicron.c4
-rw-r--r--drivers/ata/pata_legacy.c15
-rw-r--r--drivers/ata/pata_macio.c61
-rw-r--r--drivers/ata/pata_marvell.c4
-rw-r--r--drivers/ata/pata_mpc52xx.c13
-rw-r--r--drivers/ata/pata_mpiix.c4
-rw-r--r--drivers/ata/pata_netcell.c2
-rw-r--r--drivers/ata/pata_ninja32.c2
-rw-r--r--drivers/ata/pata_ns87410.c4
-rw-r--r--drivers/ata/pata_ns87415.c4
-rw-r--r--drivers/ata/pata_octeon_cf.c61
-rw-r--r--drivers/ata/pata_of_platform.c2
-rw-r--r--drivers/ata/pata_oldpiix.c6
-rw-r--r--drivers/ata/pata_opti.c4
-rw-r--r--drivers/ata/pata_optidma.c8
-rw-r--r--drivers/ata/pata_palmld.c137
-rw-r--r--drivers/ata/pata_parport/Kconfig141
-rw-r--r--drivers/ata/pata_parport/Makefile19
-rw-r--r--drivers/ata/pata_parport/aten.c144
-rw-r--r--drivers/ata/pata_parport/bpck.c507
-rw-r--r--drivers/ata/pata_parport/bpck6.c464
-rw-r--r--drivers/ata/pata_parport/comm.c205
-rw-r--r--drivers/ata/pata_parport/dstr.c235
-rw-r--r--drivers/ata/pata_parport/epat.c365
-rw-r--r--drivers/ata/pata_parport/epia.c312
-rw-r--r--drivers/ata/pata_parport/fit2.c138
-rw-r--r--drivers/ata/pata_parport/fit3.c189
-rw-r--r--drivers/ata/pata_parport/friq.c264
-rw-r--r--drivers/ata/pata_parport/frpw.c298
-rw-r--r--drivers/ata/pata_parport/kbic.c308
-rw-r--r--drivers/ata/pata_parport/ktti.c111
-rw-r--r--drivers/ata/pata_parport/on20.c147
-rw-r--r--drivers/ata/pata_parport/on26.c315
-rw-r--r--drivers/ata/pata_parport/pata_parport.c835
-rw-r--r--drivers/ata/pata_parport/pata_parport.h96
-rw-r--r--drivers/ata/pata_pcmcia.c7
-rw-r--r--drivers/ata/pata_pdc2027x.c18
-rw-r--r--drivers/ata/pata_pdc202xx_old.c2
-rw-r--r--drivers/ata/pata_piccolo.c4
-rw-r--r--drivers/ata/pata_platform.c4
-rw-r--r--drivers/ata/pata_pxa.c19
-rw-r--r--drivers/ata/pata_radisys.c4
-rw-r--r--drivers/ata/pata_rb532_cf.c6
-rw-r--r--drivers/ata/pata_rdc.c10
-rw-r--r--drivers/ata/pata_rz1000.c2
-rw-r--r--drivers/ata/pata_samsung_cf.c662
-rw-r--r--drivers/ata/pata_sc1200.c2
-rw-r--r--drivers/ata/pata_sch.c2
-rw-r--r--drivers/ata/pata_serverworks.c22
-rw-r--r--drivers/ata/pata_sil680.c2
-rw-r--r--drivers/ata/pata_sis.c4
-rw-r--r--drivers/ata/pata_sl82c105.c7
-rw-r--r--drivers/ata/pata_triflex.c4
-rw-r--r--drivers/ata/pata_via.c13
-rw-r--r--drivers/ata/pdc_adma.c4
-rw-r--r--drivers/ata/sata_dwc_460ex.c26
-rw-r--r--drivers/ata/sata_fsl.c23
-rw-r--r--drivers/ata/sata_gemini.c48
-rw-r--r--drivers/ata/sata_gemini.h1
-rw-r--r--drivers/ata/sata_highbank.c19
-rw-r--r--drivers/ata/sata_inic162x.c20
-rw-r--r--drivers/ata/sata_mv.c99
-rw-r--r--drivers/ata/sata_nv.c42
-rw-r--r--drivers/ata/sata_promise.c10
-rw-r--r--drivers/ata/sata_qstor.c6
-rw-r--r--drivers/ata/sata_rcar.c21
-rw-r--r--drivers/ata/sata_sil.c18
-rw-r--r--drivers/ata/sata_sil24.c26
-rw-r--r--drivers/ata/sata_sis.c4
-rw-r--r--drivers/ata/sata_svw.c14
-rw-r--r--drivers/ata/sata_sx4.c58
-rw-r--r--drivers/ata/sata_uli.c6
-rw-r--r--drivers/ata/sata_via.c9
-rw-r--r--drivers/ata/sata_vsc.c4
-rw-r--r--drivers/atm/atmtcp.c16
-rw-r--r--drivers/atm/eni.c1
-rw-r--r--drivers/atm/fore200e.c17
-rw-r--r--drivers/atm/fore200e.h1
-rw-r--r--drivers/atm/idt77105.c5
-rw-r--r--drivers/atm/idt77252.c37
-rw-r--r--drivers/atm/iphase.c23
-rw-r--r--drivers/atm/lanai.c6
-rw-r--r--drivers/atm/nicstar.c3
-rw-r--r--drivers/atm/solos-pci.c8
-rw-r--r--drivers/atm/suni.c3
-rw-r--r--drivers/auxdisplay/Kconfig330
-rw-r--r--drivers/auxdisplay/Makefile12
-rw-r--r--drivers/auxdisplay/arm-charlcd.c2
-rw-r--r--drivers/auxdisplay/cfag12864b.c12
-rw-r--r--drivers/auxdisplay/cfag12864bfb.c17
-rw-r--r--drivers/auxdisplay/charlcd.c9
-rw-r--r--drivers/auxdisplay/charlcd.h7
-rw-r--r--drivers/auxdisplay/hd44780.c26
-rw-r--r--drivers/auxdisplay/hd44780_common.c35
-rw-r--r--drivers/auxdisplay/hd44780_common.h4
-rw-r--r--drivers/auxdisplay/ht16k33.c221
-rw-r--r--drivers/auxdisplay/img-ascii-lcd.c65
-rw-r--r--drivers/auxdisplay/ks0108.c1
-rw-r--r--drivers/auxdisplay/lcd2s.c19
-rw-r--r--drivers/auxdisplay/line-display.c415
-rw-r--r--drivers/auxdisplay/line-display.h57
-rw-r--r--drivers/auxdisplay/max6959.c194
-rw-r--r--drivers/auxdisplay/panel.c219
-rw-r--r--drivers/auxdisplay/seg-led-gpio.c110
-rw-r--r--drivers/base/Kconfig19
-rw-r--r--drivers/base/Makefile5
-rw-r--r--drivers/base/arch_numa.c230
-rw-r--r--drivers/base/arch_topology.c436
-rw-r--r--drivers/base/attribute_container.c48
-rw-r--r--drivers/base/auxiliary.c191
-rw-r--r--drivers/base/auxiliary_sysfs.c113
-rw-r--r--drivers/base/base.h192
-rw-r--r--drivers/base/bus.c681
-rw-r--r--drivers/base/cacheinfo.c443
-rw-r--r--drivers/base/class.c343
-rw-r--r--drivers/base/component.c38
-rw-r--r--drivers/base/container.c2
-rw-r--r--drivers/base/core.c1685
-rw-r--r--drivers/base/cpu.c245
-rw-r--r--drivers/base/dd.c172
-rw-r--r--drivers/base/devcoredump.c189
-rw-r--r--drivers/base/devres.c149
-rw-r--r--drivers/base/devtmpfs.c212
-rw-r--r--drivers/base/driver.c50
-rw-r--r--drivers/base/faux.c261
-rw-r--r--drivers/base/firmware_loader/Kconfig18
-rw-r--r--drivers/base/firmware_loader/builtin/main.c2
-rw-r--r--drivers/base/firmware_loader/fallback.c10
-rw-r--r--drivers/base/firmware_loader/fallback.h4
-rw-r--r--drivers/base/firmware_loader/fallback_table.c9
-rw-r--r--drivers/base/firmware_loader/firmware.h3
-rw-r--r--drivers/base/firmware_loader/main.c247
-rw-r--r--drivers/base/firmware_loader/sysfs.c28
-rw-r--r--drivers/base/firmware_loader/sysfs.h8
-rw-r--r--drivers/base/firmware_loader/sysfs_upload.c7
-rw-r--r--drivers/base/init.c3
-rw-r--r--drivers/base/isa.c11
-rw-r--r--drivers/base/memory.c290
-rw-r--r--drivers/base/module.c63
-rw-r--r--drivers/base/node.c448
-rw-r--r--drivers/base/physical_location.c14
-rw-r--r--drivers/base/physical_location.h2
-rw-r--r--drivers/base/platform-msi.c354
-rw-r--r--drivers/base/platform.c228
-rw-r--r--drivers/base/power/Makefile2
-rw-r--r--drivers/base/power/clock_ops.c75
-rw-r--r--drivers/base/power/common.c233
-rw-r--r--drivers/base/power/domain.c3401
-rw-r--r--drivers/base/power/domain_governor.c411
-rw-r--r--drivers/base/power/generic_ops.c109
-rw-r--r--drivers/base/power/main.c881
-rw-r--r--drivers/base/power/power.h1
-rw-r--r--drivers/base/power/qos.c3
-rw-r--r--drivers/base/power/runtime-test.c249
-rw-r--r--drivers/base/power/runtime.c533
-rw-r--r--drivers/base/power/sysfs.c33
-rw-r--r--drivers/base/power/trace.c6
-rw-r--r--drivers/base/power/wakeirq.c91
-rw-r--r--drivers/base/power/wakeup.c62
-rw-r--r--drivers/base/power/wakeup_stats.c4
-rw-r--r--drivers/base/property.c422
-rw-r--r--drivers/base/regmap/Kconfig42
-rw-r--r--drivers/base/regmap/Makefile6
-rw-r--r--drivers/base/regmap/internal.h58
-rw-r--r--drivers/base/regmap/regcache-flat.c109
-rw-r--r--drivers/base/regmap/regcache-lzo.c368
-rw-r--r--drivers/base/regmap/regcache-maple.c395
-rw-r--r--drivers/base/regmap/regcache-rbtree.c58
-rw-r--r--drivers/base/regmap/regcache.c192
-rw-r--r--drivers/base/regmap/regmap-ac97.c1
-rw-r--r--drivers/base/regmap/regmap-debugfs.c31
-rw-r--r--drivers/base/regmap/regmap-fsi.c231
-rw-r--r--drivers/base/regmap/regmap-i2c.c12
-rw-r--r--drivers/base/regmap/regmap-i3c.c11
-rw-r--r--drivers/base/regmap/regmap-irq.c458
-rw-r--r--drivers/base/regmap/regmap-kunit.c2131
-rw-r--r--drivers/base/regmap/regmap-mdio.c43
-rw-r--r--drivers/base/regmap/regmap-mmio.c268
-rw-r--r--drivers/base/regmap/regmap-ram.c87
-rw-r--r--drivers/base/regmap/regmap-raw-ram.c146
-rw-r--r--drivers/base/regmap/regmap-sccb.c1
-rw-r--r--drivers/base/regmap/regmap-sdw-mbq.c231
-rw-r--r--drivers/base/regmap/regmap-sdw.c47
-rw-r--r--drivers/base/regmap/regmap-slimbus.c7
-rw-r--r--drivers/base/regmap/regmap-spi-avmm.c15
-rw-r--r--drivers/base/regmap/regmap-spi.c4
-rw-r--r--drivers/base/regmap/regmap-spmi.c1
-rw-r--r--drivers/base/regmap/regmap-w1.c1
-rw-r--r--drivers/base/regmap/regmap.c539
-rw-r--r--drivers/base/regmap/trace.h77
-rw-r--r--drivers/base/soc.c25
-rw-r--r--drivers/base/swnode.c128
-rw-r--r--drivers/base/syscore.c82
-rw-r--r--drivers/base/test/.kunitconfig2
-rw-r--r--drivers/base/test/Kconfig10
-rw-r--r--drivers/base/test/Makefile3
-rw-r--r--drivers/base/test/platform-device-test.c263
-rw-r--r--drivers/base/test/property-entry-test.c34
-rw-r--r--drivers/base/test/root-device-test.c112
-rw-r--r--drivers/base/test/test_async_driver_probe.c4
-rw-r--r--drivers/base/topology.c114
-rw-r--r--drivers/base/trace.h8
-rw-r--r--drivers/base/transport_class.c17
-rw-r--r--drivers/bcma/driver_gpio.c16
-rw-r--r--drivers/bcma/driver_mips.c8
-rw-r--r--drivers/bcma/driver_pci_host.c12
-rw-r--r--drivers/bcma/host_soc.c4
-rw-r--r--drivers/bcma/main.c35
-rw-r--r--drivers/bcma/sprom.c2
-rw-r--r--drivers/block/Kconfig113
-rw-r--r--drivers/block/Makefile3
-rw-r--r--drivers/block/amiflop.c48
-rw-r--r--drivers/block/aoe/aoe.h1
-rw-r--r--drivers/block/aoe/aoeblk.c48
-rw-r--r--drivers/block/aoe/aoechr.c30
-rw-r--r--drivers/block/aoe/aoecmd.c39
-rw-r--r--drivers/block/aoe/aoedev.c22
-rw-r--r--drivers/block/aoe/aoemain.c4
-rw-r--r--drivers/block/aoe/aoenet.c6
-rw-r--r--drivers/block/ataflop.c69
-rw-r--r--drivers/block/brd.c449
-rw-r--r--drivers/block/drbd/Kconfig4
-rw-r--r--drivers/block/drbd/Makefile4
-rw-r--r--drivers/block/drbd/drbd_actlog.c37
-rw-r--r--drivers/block/drbd/drbd_bitmap.c89
-rw-r--r--drivers/block/drbd/drbd_buildtag.c22
-rw-r--r--drivers/block/drbd/drbd_debugfs.c4
-rw-r--r--drivers/block/drbd/drbd_debugfs.h2
-rw-r--r--drivers/block/drbd/drbd_int.h267
-rw-r--r--drivers/block/drbd/drbd_interval.c8
-rw-r--r--drivers/block/drbd/drbd_interval.h2
-rw-r--r--drivers/block/drbd/drbd_main.c258
-rw-r--r--drivers/block/drbd/drbd_nl.c309
-rw-r--r--drivers/block/drbd/drbd_nla.c2
-rw-r--r--drivers/block/drbd/drbd_nla.h2
-rw-r--r--drivers/block/drbd/drbd_polymorph_printk.h141
-rw-r--r--drivers/block/drbd/drbd_proc.c4
-rw-r--r--drivers/block/drbd/drbd_protocol.h2
-rw-r--r--drivers/block/drbd/drbd_receiver.c506
-rw-r--r--drivers/block/drbd/drbd_req.c57
-rw-r--r--drivers/block/drbd/drbd_req.h15
-rw-r--r--drivers/block/drbd/drbd_state.c59
-rw-r--r--drivers/block/drbd/drbd_state.h2
-rw-r--r--drivers/block/drbd/drbd_state_change.h10
-rw-r--r--drivers/block/drbd/drbd_strings.c2
-rw-r--r--drivers/block/drbd/drbd_strings.h2
-rw-r--r--drivers/block/drbd/drbd_vli.h4
-rw-r--r--drivers/block/drbd/drbd_worker.c194
-rw-r--r--drivers/block/floppy.c176
-rw-r--r--drivers/block/loop.c911
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c107
-rw-r--r--drivers/block/n64cart.c14
-rw-r--r--drivers/block/nbd.c546
-rw-r--r--drivers/block/null_blk/Kconfig2
-rw-r--r--drivers/block/null_blk/main.c1118
-rw-r--r--drivers/block/null_blk/null_blk.h52
-rw-r--r--drivers/block/null_blk/trace.h12
-rw-r--r--drivers/block/null_blk/zoned.c512
-rw-r--r--drivers/block/paride/Kconfig302
-rw-r--r--drivers/block/paride/Makefile29
-rw-r--r--drivers/block/paride/Transition-notes128
-rw-r--r--drivers/block/paride/aten.c162
-rw-r--r--drivers/block/paride/bpck.c477
-rw-r--r--drivers/block/paride/bpck6.c267
-rw-r--r--drivers/block/paride/comm.c218
-rw-r--r--drivers/block/paride/dstr.c233
-rw-r--r--drivers/block/paride/epat.c340
-rw-r--r--drivers/block/paride/epia.c316
-rw-r--r--drivers/block/paride/fit2.c151
-rw-r--r--drivers/block/paride/fit3.c211
-rw-r--r--drivers/block/paride/friq.c276
-rw-r--r--drivers/block/paride/frpw.c313
-rw-r--r--drivers/block/paride/kbic.c305
-rw-r--r--drivers/block/paride/ktti.c128
-rw-r--r--drivers/block/paride/mkd31
-rw-r--r--drivers/block/paride/on20.c153
-rw-r--r--drivers/block/paride/on26.c319
-rw-r--r--drivers/block/paride/paride.c479
-rw-r--r--drivers/block/paride/paride.h172
-rw-r--r--drivers/block/paride/pcd.c1042
-rw-r--r--drivers/block/paride/pd.c1032
-rw-r--r--drivers/block/paride/pf.c1057
-rw-r--r--drivers/block/paride/pg.c734
-rw-r--r--drivers/block/paride/ppc6lnx.c726
-rw-r--r--drivers/block/paride/pseudo.h102
-rw-r--r--drivers/block/paride/pt.c1024
-rw-r--r--drivers/block/pktcdvd.c2944
-rw-r--r--drivers/block/ps3disk.c30
-rw-r--r--drivers/block/ps3vram.c13
-rw-r--r--drivers/block/rbd.c811
-rw-r--r--drivers/block/rnbd/Makefile12
-rw-r--r--drivers/block/rnbd/rnbd-clt-sysfs.c26
-rw-r--r--drivers/block/rnbd/rnbd-clt.c106
-rw-r--r--drivers/block/rnbd/rnbd-common.c23
-rw-r--r--drivers/block/rnbd/rnbd-proto.h62
-rw-r--r--drivers/block/rnbd/rnbd-srv-dev.c43
-rw-r--r--drivers/block/rnbd/rnbd-srv-dev.h64
-rw-r--r--drivers/block/rnbd/rnbd-srv-sysfs.c32
-rw-r--r--drivers/block/rnbd/rnbd-srv-trace.c17
-rw-r--r--drivers/block/rnbd/rnbd-srv-trace.h207
-rw-r--r--drivers/block/rnbd/rnbd-srv.c244
-rw-r--r--drivers/block/rnbd/rnbd-srv.h6
-rw-r--r--drivers/block/rnull/Kconfig13
-rw-r--r--drivers/block/rnull/Makefile3
-rw-r--r--drivers/block/rnull/configfs.rs263
-rw-r--r--drivers/block/rnull/rnull.rs103
-rw-r--r--drivers/block/sunvdc.c53
-rw-r--r--drivers/block/swim.c41
-rw-r--r--drivers/block/swim3.c66
-rw-r--r--drivers/block/ublk_drv.c3393
-rw-r--r--drivers/block/virtio_blk.c976
-rw-r--r--drivers/block/xen-blkback/blkback.c133
-rw-r--r--drivers/block/xen-blkback/common.h109
-rw-r--r--drivers/block/xen-blkback/xenbus.c45
-rw-r--r--drivers/block/xen-blkfront.c140
-rw-r--r--drivers/block/z2ram.c12
-rw-r--r--drivers/block/zloop.c1507
-rw-r--r--drivers/block/zram/Kconfig99
-rw-r--r--drivers/block/zram/Makefile8
-rw-r--r--drivers/block/zram/backend_842.c61
-rw-r--r--drivers/block/zram/backend_842.h10
-rw-r--r--drivers/block/zram/backend_deflate.c148
-rw-r--r--drivers/block/zram/backend_deflate.h10
-rw-r--r--drivers/block/zram/backend_lz4.c127
-rw-r--r--drivers/block/zram/backend_lz4.h10
-rw-r--r--drivers/block/zram/backend_lz4hc.c128
-rw-r--r--drivers/block/zram/backend_lz4hc.h10
-rw-r--r--drivers/block/zram/backend_lzo.c59
-rw-r--r--drivers/block/zram/backend_lzo.h10
-rw-r--r--drivers/block/zram/backend_lzorle.c59
-rw-r--r--drivers/block/zram/backend_lzorle.h10
-rw-r--r--drivers/block/zram/backend_zstd.c217
-rw-r--r--drivers/block/zram/backend_zstd.h10
-rw-r--r--drivers/block/zram/zcomp.c254
-rw-r--r--drivers/block/zram/zcomp.h89
-rw-r--r--drivers/block/zram/zram_drv.c2517
-rw-r--r--drivers/block/zram/zram_drv.h71
-rw-r--r--drivers/bluetooth/Kconfig76
-rw-r--r--drivers/bluetooth/Makefile4
-rw-r--r--drivers/bluetooth/ath3k.c27
-rw-r--r--drivers/bluetooth/bfusb.c5
-rw-r--r--drivers/bluetooth/bluecard_cs.c6
-rw-r--r--drivers/bluetooth/bpa10x.c8
-rw-r--r--drivers/bluetooth/btbcm.c83
-rw-r--r--drivers/bluetooth/btintel.c1295
-rw-r--r--drivers/bluetooth/btintel.h114
-rw-r--r--drivers/bluetooth/btintel_pcie.c2767
-rw-r--r--drivers/bluetooth/btintel_pcie.h566
-rw-r--r--drivers/bluetooth/btmrvl_main.c12
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c10
-rw-r--r--drivers/bluetooth/btmtk.c1210
-rw-r--r--drivers/bluetooth/btmtk.h151
-rw-r--r--drivers/bluetooth/btmtksdio.c113
-rw-r--r--drivers/bluetooth/btmtkuart.c37
-rw-r--r--drivers/bluetooth/btnxpuart.c2031
-rw-r--r--drivers/bluetooth/btqca.c456
-rw-r--r--drivers/bluetooth/btqca.h96
-rw-r--r--drivers/bluetooth/btqcomsmd.c23
-rw-r--r--drivers/bluetooth/btrsi.c3
-rw-r--r--drivers/bluetooth/btrtl.c811
-rw-r--r--drivers/bluetooth/btrtl.h92
-rw-r--r--drivers/bluetooth/btsdio.c12
-rw-r--r--drivers/bluetooth/btusb.c2040
-rw-r--r--drivers/bluetooth/h4_recv.h146
-rw-r--r--drivers/bluetooth/hci_ag6xx.c2
-rw-r--r--drivers/bluetooth/hci_aml.c754
-rw-r--r--drivers/bluetooth/hci_ath.c2
-rw-r--r--drivers/bluetooth/hci_bcm.c115
-rw-r--r--drivers/bluetooth/hci_bcm4377.c2559
-rw-r--r--drivers/bluetooth/hci_bcsp.c13
-rw-r--r--drivers/bluetooth/hci_h4.c8
-rw-r--r--drivers/bluetooth/hci_h5.c76
-rw-r--r--drivers/bluetooth/hci_intel.c40
-rw-r--r--drivers/bluetooth/hci_ldisc.c79
-rw-r--r--drivers/bluetooth/hci_ll.c12
-rw-r--r--drivers/bluetooth/hci_mrvl.c96
-rw-r--r--drivers/bluetooth/hci_nokia.c21
-rw-r--r--drivers/bluetooth/hci_qca.c781
-rw-r--r--drivers/bluetooth/hci_serdev.c36
-rw-r--r--drivers/bluetooth/hci_uart.h32
-rw-r--r--drivers/bluetooth/hci_vhci.c187
-rw-r--r--drivers/bluetooth/virtio_bt.c64
-rw-r--r--drivers/bus/Kconfig30
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/arm-integrator-lm.c2
-rw-r--r--drivers/bus/brcmstb_gisb.c30
-rw-r--r--drivers/bus/bt1-apb.c25
-rw-r--r--drivers/bus/bt1-axi.c24
-rw-r--r--drivers/bus/fsl-mc/Kconfig2
-rw-r--r--drivers/bus/fsl-mc/dpmcp.c22
-rw-r--r--drivers/bus/fsl-mc/dprc-driver.c29
-rw-r--r--drivers/bus/fsl-mc/dprc.c12
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-allocator.c59
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c204
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-msi.c27
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-private.h8
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-uapi.c11
-rw-r--r--drivers/bus/fsl-mc/mc-io.c39
-rw-r--r--drivers/bus/fsl-mc/mc-sys.c4
-rw-r--r--drivers/bus/hisi_lpc.c102
-rw-r--r--drivers/bus/imx-aipstz.c108
-rw-r--r--drivers/bus/imx-weim.c56
-rw-r--r--drivers/bus/intel-ixp4xx-eb.c12
-rw-r--r--drivers/bus/mhi/Makefile4
-rw-r--r--drivers/bus/mhi/common.h38
-rw-r--r--drivers/bus/mhi/ep/internal.h6
-rw-r--r--drivers/bus/mhi/ep/main.c514
-rw-r--r--drivers/bus/mhi/ep/ring.c68
-rw-r--r--drivers/bus/mhi/ep/sm.c42
-rw-r--r--drivers/bus/mhi/host/boot.c273
-rw-r--r--drivers/bus/mhi/host/debugfs.c3
-rw-r--r--drivers/bus/mhi/host/init.c176
-rw-r--r--drivers/bus/mhi/host/internal.h87
-rw-r--r--drivers/bus/mhi/host/main.c125
-rw-r--r--drivers/bus/mhi/host/pci_generic.c630
-rw-r--r--drivers/bus/mhi/host/pm.c162
-rw-r--r--drivers/bus/mhi/host/trace.h283
-rw-r--r--drivers/bus/mips_cdmm.c12
-rw-r--r--drivers/bus/moxtet.c24
-rw-r--r--drivers/bus/mvebu-mbus.c100
-rw-r--r--drivers/bus/omap-ocp2scp.c4
-rw-r--r--drivers/bus/omap_l3_smx.c26
-rw-r--r--drivers/bus/qcom-ebi2.c1
-rw-r--r--drivers/bus/qcom-ssc-block-bus.c39
-rw-r--r--drivers/bus/simple-pm-bus.c73
-rw-r--r--drivers/bus/stm32_etzpc.c141
-rw-r--r--drivers/bus/stm32_firewall.c294
-rw-r--r--drivers/bus/stm32_firewall.h83
-rw-r--r--drivers/bus/stm32_rifsc.c841
-rw-r--r--drivers/bus/sun50i-de2.c3
-rw-r--r--drivers/bus/sunxi-rsb.c109
-rw-r--r--drivers/bus/tegra-aconnect.c4
-rw-r--r--drivers/bus/tegra-gmi.c12
-rw-r--r--drivers/bus/ti-pwmss.c5
-rw-r--r--drivers/bus/ti-sysc.c363
-rw-r--r--drivers/bus/ts-nbus.c85
-rw-r--r--drivers/bus/uniphier-system-bus.c54
-rw-r--r--drivers/bus/vexpress-config.c9
-rw-r--r--drivers/cache/Kconfig56
-rw-r--r--drivers/cache/Makefile7
-rw-r--r--drivers/cache/ax45mp_cache.c217
-rw-r--r--drivers/cache/hisi_soc_hha.c194
-rw-r--r--drivers/cache/sifive_ccache.c352
-rw-r--r--drivers/cache/starfive_starlink_cache.c130
-rw-r--r--drivers/cdrom/cdrom.c65
-rw-r--r--drivers/cdrom/gdrom.c35
-rw-r--r--drivers/cdx/Kconfig19
-rw-r--r--drivers/cdx/Makefile14
-rw-r--r--drivers/cdx/cdx.c978
-rw-r--r--drivers/cdx/cdx.h94
-rw-r--r--drivers/cdx/cdx_msi.c193
-rw-r--r--drivers/cdx/controller/Kconfig22
-rw-r--r--drivers/cdx/controller/Makefile9
-rw-r--r--drivers/cdx/controller/cdx_controller.c258
-rw-r--r--drivers/cdx/controller/cdx_controller.h30
-rw-r--r--drivers/cdx/controller/cdx_rpmsg.c202
-rw-r--r--drivers/cdx/controller/mc_cdx_pcol.h708
-rw-r--r--drivers/cdx/controller/mcdi.c870
-rw-r--r--drivers/cdx/controller/mcdi_functions.c256
-rw-r--r--drivers/cdx/controller/mcdi_functions.h126
-rw-r--r--drivers/cdx/controller/mcdid.h63
-rw-r--r--drivers/char/Kconfig50
-rw-r--r--drivers/char/Makefile3
-rw-r--r--drivers/char/adi.c17
-rw-r--r--drivers/char/agp/Kconfig16
-rw-r--r--drivers/char/agp/Makefile8
-rw-r--r--drivers/char/agp/agp.h16
-rw-r--r--drivers/char/agp/ali-agp.c1
-rw-r--r--drivers/char/agp/alpha-agp.c3
-rw-r--r--drivers/char/agp/amd-k7-agp.c25
-rw-r--r--drivers/char/agp/amd64-agp.c25
-rw-r--r--drivers/char/agp/ati-agp.c23
-rw-r--r--drivers/char/agp/backend.c11
-rw-r--r--drivers/char/agp/compat_ioctl.c291
-rw-r--r--drivers/char/agp/compat_ioctl.h106
-rw-r--r--drivers/char/agp/efficeon-agp.c17
-rw-r--r--drivers/char/agp/frontend.c1068
-rw-r--r--drivers/char/agp/hp-agp.c550
-rw-r--r--drivers/char/agp/i460-agp.c659
-rw-r--r--drivers/char/agp/intel-agp.c14
-rw-r--r--drivers/char/agp/intel-gtt.c58
-rw-r--r--drivers/char/agp/nvidia-agp.c26
-rw-r--r--drivers/char/agp/parisc-agp.c32
-rw-r--r--drivers/char/agp/sis-agp.c8
-rw-r--r--drivers/char/agp/sworks-agp.c1
-rw-r--r--drivers/char/agp/uninorth-agp.c2
-rw-r--r--drivers/char/agp/via-agp.c7
-rw-r--r--drivers/char/apm-emulation.c15
-rw-r--r--drivers/char/applicom.c11
-rw-r--r--drivers/char/bsr.c25
-rw-r--r--drivers/char/ds1620.c2
-rw-r--r--drivers/char/dsp56k.c19
-rw-r--r--drivers/char/dtlk.c8
-rw-r--r--drivers/char/hangcheck-timer.c26
-rw-r--r--drivers/char/hpet.c83
-rw-r--r--drivers/char/hw_random/Kconfig136
-rw-r--r--drivers/char/hw_random/Makefile5
-rw-r--r--drivers/char/hw_random/airoha-trng.c243
-rw-r--r--drivers/char/hw_random/amd-rng.c23
-rw-r--r--drivers/char/hw_random/arm_smccc_trng.c7
-rw-r--r--drivers/char/hw_random/atmel-rng.c16
-rw-r--r--drivers/char/hw_random/ba431-rng.c10
-rw-r--r--drivers/char/hw_random/bcm2835-rng.c22
-rw-r--r--drivers/char/hw_random/bcm74110-rng.c125
-rw-r--r--drivers/char/hw_random/cavium-rng-vf.c2
-rw-r--r--drivers/char/hw_random/cavium-rng.c1
-rw-r--r--drivers/char/hw_random/cctrng.c99
-rw-r--r--drivers/char/hw_random/cn10k-rng.c80
-rw-r--r--drivers/char/hw_random/core.c192
-rw-r--r--drivers/char/hw_random/exynos-trng.c237
-rw-r--r--drivers/char/hw_random/geode-rng.c42
-rw-r--r--drivers/char/hw_random/hisi-rng.c8
-rw-r--r--drivers/char/hw_random/histb-rng.c173
-rw-r--r--drivers/char/hw_random/imx-rngc.c165
-rw-r--r--drivers/char/hw_random/ingenic-rng.c8
-rw-r--r--drivers/char/hw_random/ingenic-trng.c59
-rw-r--r--drivers/char/hw_random/iproc-rng200.c28
-rw-r--r--drivers/char/hw_random/jh7110-trng.c397
-rw-r--r--drivers/char/hw_random/ks-sa-rng.c34
-rw-r--r--drivers/char/hw_random/meson-rng.c99
-rw-r--r--drivers/char/hw_random/mpfs-rng.c3
-rw-r--r--drivers/char/hw_random/mtk-rng.c17
-rw-r--r--drivers/char/hw_random/mxc-rnga.c27
-rw-r--r--drivers/char/hw_random/n2-drv.c16
-rw-r--r--drivers/char/hw_random/n2rng.h4
-rw-r--r--drivers/char/hw_random/nomadik-rng.c21
-rw-r--r--drivers/char/hw_random/npcm-rng.c28
-rw-r--r--drivers/char/hw_random/octeon-rng.c6
-rw-r--r--drivers/char/hw_random/omap-rng.c7
-rw-r--r--drivers/char/hw_random/omap3-rom-rng.c3
-rw-r--r--drivers/char/hw_random/pasemi-rng.c3
-rw-r--r--drivers/char/hw_random/pic32-rng.c76
-rw-r--r--drivers/char/hw_random/powernv-rng.c1
-rw-r--r--drivers/char/hw_random/rockchip-rng.c492
-rw-r--r--drivers/char/hw_random/s390-trng.c5
-rw-r--r--drivers/char/hw_random/st-rng.c22
-rw-r--r--drivers/char/hw_random/stm32-rng.c581
-rw-r--r--drivers/char/hw_random/timeriomem-rng.c36
-rw-r--r--drivers/char/hw_random/virtio-rng.c26
-rw-r--r--drivers/char/hw_random/xgene-rng.c61
-rw-r--r--drivers/char/hw_random/xiphera-trng.c3
-rw-r--r--drivers/char/ipmi/Kconfig26
-rw-r--r--drivers/char/ipmi/Makefile13
-rw-r--r--drivers/char/ipmi/bt-bmc.c7
-rw-r--r--drivers/char/ipmi/ipmb_dev_int.c15
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c29
-rw-r--r--drivers/char/ipmi/ipmi_ipmb.c30
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c1419
-rw-r--r--drivers/char/ipmi/ipmi_powernv.c23
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c25
-rw-r--r--drivers/char/ipmi/ipmi_si.h17
-rw-r--r--drivers/char/ipmi/ipmi_si_hardcode.c2
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c239
-rw-r--r--drivers/char/ipmi/ipmi_si_ls2k.c189
-rw-r--r--drivers/char/ipmi/ipmi_si_parisc.c2
-rw-r--r--drivers/char/ipmi/ipmi_si_pci.c57
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c43
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c208
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c163
-rw-r--r--drivers/char/ipmi/kcs_bmc.c5
-rw-r--r--drivers/char/ipmi/kcs_bmc_aspeed.c62
-rw-r--r--drivers/char/ipmi/kcs_bmc_cdev_ipmi.c4
-rw-r--r--drivers/char/ipmi/kcs_bmc_npcm7xx.c6
-rw-r--r--drivers/char/ipmi/kcs_bmc_serio.c4
-rw-r--r--drivers/char/ipmi/ssif_bmc.c877
-rw-r--r--drivers/char/lp.c20
-rw-r--r--drivers/char/mem.c188
-rw-r--r--drivers/char/misc.c118
-rw-r--r--drivers/char/misc_minor_kunit.c689
-rw-r--r--drivers/char/mspec.c295
-rw-r--r--drivers/char/mwave/3780i.c218
-rw-r--r--drivers/char/mwave/3780i.h12
-rw-r--r--drivers/char/mwave/Makefile6
-rw-r--r--drivers/char/mwave/README10
-rw-r--r--drivers/char/mwave/mwavedd.c337
-rw-r--r--drivers/char/mwave/mwavedd.h76
-rw-r--r--drivers/char/mwave/mwavepub.h22
-rw-r--r--drivers/char/mwave/smapi.c244
-rw-r--r--drivers/char/mwave/smapi.h6
-rw-r--r--drivers/char/mwave/tp3780i.c209
-rw-r--r--drivers/char/mwave/tp3780i.h30
-rw-r--r--drivers/char/nvram.c1
-rw-r--r--drivers/char/nwbutton.c1
-rw-r--r--drivers/char/nwflash.c1
-rw-r--r--drivers/char/pc8736x_gpio.c1
-rw-r--r--drivers/char/pcmcia/Kconfig68
-rw-r--r--drivers/char/pcmcia/Makefile11
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c1910
-rw-r--r--drivers/char/pcmcia/cm4040_cs.c684
-rw-r--r--drivers/char/pcmcia/cm4040_cs.h48
-rw-r--r--drivers/char/pcmcia/scr24x_cs.c359
-rw-r--r--drivers/char/pcmcia/synclink_cs.c4291
-rw-r--r--drivers/char/powernv-op-panel.c3
-rw-r--r--drivers/char/ppdev.c43
-rw-r--r--drivers/char/random.c473
-rw-r--r--drivers/char/scx200_gpio.c1
-rw-r--r--drivers/char/sonypi.c21
-rw-r--r--drivers/char/tlclk.c37
-rw-r--r--drivers/char/tpm/Kconfig48
-rw-r--r--drivers/char/tpm/Makefile5
-rw-r--r--drivers/char/tpm/eventlog/acpi.c35
-rw-r--r--drivers/char/tpm/eventlog/common.c54
-rw-r--r--drivers/char/tpm/eventlog/efi.c13
-rw-r--r--drivers/char/tpm/eventlog/of.c29
-rw-r--r--drivers/char/tpm/eventlog/tpm1.c10
-rw-r--r--drivers/char/tpm/st33zp24/i2c.c157
-rw-r--r--drivers/char/tpm/st33zp24/spi.c151
-rw-r--r--drivers/char/tpm/st33zp24/st33zp24.c43
-rw-r--r--drivers/char/tpm/st33zp24/st33zp24.h7
-rw-r--r--drivers/char/tpm/tpm-buf.c246
-rw-r--r--drivers/char/tpm/tpm-chip.c146
-rw-r--r--drivers/char/tpm/tpm-dev-common.c14
-rw-r--r--drivers/char/tpm/tpm-dev.c1
-rw-r--r--drivers/char/tpm/tpm-interface.c174
-rw-r--r--drivers/char/tpm/tpm-sysfs.c18
-rw-r--r--drivers/char/tpm/tpm.h98
-rw-r--r--drivers/char/tpm/tpm1-cmd.c5
-rw-r--r--drivers/char/tpm/tpm2-cmd.c255
-rw-r--r--drivers/char/tpm/tpm2-sessions.c1398
-rw-r--r--drivers/char/tpm/tpm2-space.c18
-rw-r--r--drivers/char/tpm/tpm_atmel.c68
-rw-r--r--drivers/char/tpm/tpm_atmel.h140
-rw-r--r--drivers/char/tpm/tpm_crb.c300
-rw-r--r--drivers/char/tpm/tpm_crb_ffa.c414
-rw-r--r--drivers/char/tpm/tpm_crb_ffa.h23
-rw-r--r--drivers/char/tpm/tpm_ftpm_tee.c107
-rw-r--r--drivers/char/tpm/tpm_ftpm_tee.h5
-rw-r--r--drivers/char/tpm/tpm_i2c_atmel.c11
-rw-r--r--drivers/char/tpm/tpm_i2c_infineon.c10
-rw-r--r--drivers/char/tpm/tpm_i2c_nuvoton.c25
-rw-r--r--drivers/char/tpm/tpm_ibmvtpm.c17
-rw-r--r--drivers/char/tpm/tpm_infineon.c17
-rw-r--r--drivers/char/tpm/tpm_loongson.c84
-rw-r--r--drivers/char/tpm/tpm_nsc.c5
-rw-r--r--drivers/char/tpm/tpm_ppi.c139
-rw-r--r--drivers/char/tpm/tpm_svsm.c121
-rw-r--r--drivers/char/tpm/tpm_tis.c91
-rw-r--r--drivers/char/tpm/tpm_tis_core.c553
-rw-r--r--drivers/char/tpm/tpm_tis_core.h20
-rw-r--r--drivers/char/tpm/tpm_tis_i2c.c72
-rw-r--r--drivers/char/tpm/tpm_tis_i2c_cr50.c166
-rw-r--r--drivers/char/tpm/tpm_tis_spi_main.c114
-rw-r--r--drivers/char/tpm/tpm_tis_synquacer.c23
-rw-r--r--drivers/char/tpm/tpm_vtpm_proxy.c37
-rw-r--r--drivers/char/tpm/tpmrm-dev.c1
-rw-r--r--drivers/char/tpm/xen-tpmfront.c6
-rw-r--r--drivers/char/ttyprintk.c10
-rw-r--r--drivers/char/virtio_console.c204
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c182
-rw-r--r--drivers/char/xillybus/Kconfig2
-rw-r--r--drivers/char/xillybus/xillybus_class.c28
-rw-r--r--drivers/char/xillybus/xillybus_core.c5
-rw-r--r--drivers/char/xillybus/xillybus_of.c4
-rw-r--r--drivers/char/xillybus/xillyusb.c66
-rw-r--r--drivers/clk/.kunitconfig4
-rw-r--r--drivers/clk/Kconfig152
-rw-r--r--drivers/clk/Makefile51
-rw-r--r--drivers/clk/actions/owl-common.c7
-rw-r--r--drivers/clk/actions/owl-common.h4
-rw-r--r--drivers/clk/actions/owl-composite.c43
-rw-r--r--drivers/clk/actions/owl-composite.h2
-rw-r--r--drivers/clk/actions/owl-divider.c13
-rw-r--r--drivers/clk/actions/owl-divider.h2
-rw-r--r--drivers/clk/actions/owl-factor.c12
-rw-r--r--drivers/clk/actions/owl-factor.h2
-rw-r--r--drivers/clk/actions/owl-gate.h2
-rw-r--r--drivers/clk/actions/owl-mux.h2
-rw-r--r--drivers/clk/actions/owl-pll.c25
-rw-r--r--drivers/clk/actions/owl-pll.h2
-rw-r--r--drivers/clk/analogbits/Kconfig2
-rw-r--r--drivers/clk/analogbits/wrpll-cln28hpc.c10
-rw-r--r--drivers/clk/at91/Makefile18
-rw-r--r--drivers/clk/at91/at91rm9200.c18
-rw-r--r--drivers/clk/at91/at91sam9260.c16
-rw-r--r--drivers/clk/at91/at91sam9g45.c26
-rw-r--r--drivers/clk/at91/at91sam9n12.c26
-rw-r--r--drivers/clk/at91/at91sam9rl.c16
-rw-r--r--drivers/clk/at91/at91sam9x5.c35
-rw-r--r--drivers/clk/at91/clk-audio-pll.c42
-rw-r--r--drivers/clk/at91/clk-generated.c16
-rw-r--r--drivers/clk/at91/clk-h32mx.c33
-rw-r--r--drivers/clk/at91/clk-main.c33
-rw-r--r--drivers/clk/at91/clk-master.c42
-rw-r--r--drivers/clk/at91/clk-peripheral.c83
-rw-r--r--drivers/clk/at91/clk-pll.c12
-rw-r--r--drivers/clk/at91/clk-plldiv.c34
-rw-r--r--drivers/clk/at91/clk-programmable.c11
-rw-r--r--drivers/clk/at91/clk-sam9x60-pll.c172
-rw-r--r--drivers/clk/at91/clk-smd.c29
-rw-r--r--drivers/clk/at91/clk-system.c14
-rw-r--r--drivers/clk/at91/clk-usb.c20
-rw-r--r--drivers/clk/at91/clk-utmi.c22
-rw-r--r--drivers/clk/at91/dt-compat.c157
-rw-r--r--drivers/clk/at91/pmc.c13
-rw-r--r--drivers/clk/at91/pmc.h60
-rw-r--r--drivers/clk/at91/sam9x60.c49
-rw-r--r--drivers/clk/at91/sam9x7.c952
-rw-r--r--drivers/clk/at91/sama5d2.c52
-rw-r--r--drivers/clk/at91/sama5d3.c36
-rw-r--r--drivers/clk/at91/sama5d4.c40
-rw-r--r--drivers/clk/at91/sama7d65.c1379
-rw-r--r--drivers/clk/at91/sama7g5.c845
-rw-r--r--drivers/clk/at91/sckc.c92
-rw-r--r--drivers/clk/axs10x/i2s_pll_clock.c17
-rw-r--r--drivers/clk/axs10x/pll_clock.c26
-rw-r--r--drivers/clk/baikal-t1/Kconfig12
-rw-r--r--drivers/clk/baikal-t1/Makefile1
-rw-r--r--drivers/clk/baikal-t1/ccu-div.c111
-rw-r--r--drivers/clk/baikal-t1/ccu-div.h17
-rw-r--r--drivers/clk/baikal-t1/ccu-pll.c14
-rw-r--r--drivers/clk/baikal-t1/ccu-pll.h8
-rw-r--r--drivers/clk/baikal-t1/ccu-rst.c217
-rw-r--r--drivers/clk/baikal-t1/ccu-rst.h67
-rw-r--r--drivers/clk/baikal-t1/clk-ccu-div.c261
-rw-r--r--drivers/clk/baikal-t1/clk-ccu-pll.c123
-rw-r--r--drivers/clk/bcm/Kconfig9
-rw-r--r--drivers/clk/bcm/Makefile1
-rw-r--r--drivers/clk/bcm/clk-bcm2711-dvp.c9
-rw-r--r--drivers/clk/bcm/clk-bcm2835-aux.c1
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c65
-rw-r--r--drivers/clk/bcm/clk-bcm53573-ilp.c4
-rw-r--r--drivers/clk/bcm/clk-bcm63268-timer.c216
-rw-r--r--drivers/clk/bcm/clk-bcm63xx-gate.c5
-rw-r--r--drivers/clk/bcm/clk-iproc-asiu.c25
-rw-r--r--drivers/clk/bcm/clk-iproc-pll.c12
-rw-r--r--drivers/clk/bcm/clk-kona.c21
-rw-r--r--drivers/clk/bcm/clk-kona.h2
-rw-r--r--drivers/clk/bcm/clk-raspberrypi.c144
-rw-r--r--drivers/clk/bcm/clk-sr.c2
-rw-r--r--drivers/clk/berlin/berlin2-avpll.c2
-rw-r--r--drivers/clk/berlin/berlin2-div.c1
-rw-r--r--drivers/clk/berlin/bg2.c5
-rw-r--r--drivers/clk/berlin/bg2q.c6
-rw-r--r--drivers/clk/clk-apple-nco.c17
-rw-r--r--drivers/clk/clk-asm9260.c35
-rw-r--r--drivers/clk/clk-aspeed.c5
-rw-r--r--drivers/clk/clk-ast2600.c78
-rw-r--r--drivers/clk/clk-axi-clkgen.c207
-rw-r--r--drivers/clk/clk-axm5516.c14
-rw-r--r--drivers/clk/clk-bm1880.c33
-rw-r--r--drivers/clk/clk-bulk.c6
-rw-r--r--drivers/clk/clk-cdce706.c62
-rw-r--r--drivers/clk/clk-cdce925.c157
-rw-r--r--drivers/clk/clk-clps711x.c2
-rw-r--r--drivers/clk/clk-composite.c11
-rw-r--r--drivers/clk/clk-conf.c57
-rw-r--r--drivers/clk/clk-cs2000-cp.c22
-rw-r--r--drivers/clk/clk-devres.c82
-rw-r--r--drivers/clk/clk-divider.c59
-rw-r--r--drivers/clk/clk-en7523.c565
-rw-r--r--drivers/clk/clk-ep93xx.c853
-rw-r--r--drivers/clk/clk-eyeq.c859
-rw-r--r--drivers/clk/clk-fixed-factor.c134
-rw-r--r--drivers/clk/clk-fixed-mmio.c5
-rw-r--r--drivers/clk/clk-fixed-rate.c32
-rw-r--r--drivers/clk/clk-fixed-rate_test.c380
-rw-r--r--drivers/clk/clk-fixed-rate_test.h8
-rw-r--r--drivers/clk/clk-fractional-divider.c133
-rw-r--r--drivers/clk/clk-fractional-divider_test.c148
-rw-r--r--drivers/clk/clk-fsl-sai.c5
-rw-r--r--drivers/clk/clk-gate.c2
-rw-r--r--drivers/clk/clk-gate_test.c33
-rw-r--r--drivers/clk/clk-gemini.c23
-rw-r--r--drivers/clk/clk-gpio.c207
-rw-r--r--drivers/clk/clk-highbank.c27
-rw-r--r--drivers/clk/clk-hsdk-pll.c30
-rw-r--r--drivers/clk/clk-k210.c6
-rw-r--r--drivers/clk/clk-lan966x.c99
-rw-r--r--drivers/clk/clk-lmk04832.c168
-rw-r--r--drivers/clk/clk-lochnagar.c12
-rw-r--r--drivers/clk/clk-loongson1.c305
-rw-r--r--drivers/clk/clk-loongson2.c460
-rw-r--r--drivers/clk/clk-max9485.c29
-rw-r--r--drivers/clk/clk-milbeaut.c29
-rw-r--r--drivers/clk/clk-multiplier.c12
-rw-r--r--drivers/clk/clk-nomadik.c12
-rw-r--r--drivers/clk/clk-npcm7xx.c31
-rw-r--r--drivers/clk/clk-npcm8xx.c430
-rw-r--r--drivers/clk/clk-oxnas.c249
-rw-r--r--drivers/clk/clk-palmas.c4
-rw-r--r--drivers/clk/clk-plldig.c2
-rw-r--r--drivers/clk/clk-pwm.c53
-rw-r--r--drivers/clk/clk-qoriq.c11
-rw-r--r--drivers/clk/clk-renesas-pcie.c166
-rw-r--r--drivers/clk/clk-rk808.c34
-rw-r--r--drivers/clk/clk-rp1.c2462
-rw-r--r--drivers/clk/clk-rpmi.c620
-rw-r--r--drivers/clk/clk-s2mps11.c17
-rw-r--r--drivers/clk/clk-scmi.c375
-rw-r--r--drivers/clk/clk-scpi.c22
-rw-r--r--drivers/clk/clk-si514.c41
-rw-r--r--drivers/clk/clk-si521xx.c398
-rw-r--r--drivers/clk/clk-si5341.c93
-rw-r--r--drivers/clk/clk-si5351.c171
-rw-r--r--drivers/clk/clk-si544.c67
-rw-r--r--drivers/clk/clk-si570.c112
-rw-r--r--drivers/clk/clk-sp7021.c708
-rw-r--r--drivers/clk/clk-sparx5.c10
-rw-r--r--drivers/clk/clk-stm32f4.c184
-rw-r--r--drivers/clk/clk-stm32h7.c1
-rw-r--r--drivers/clk/clk-tps68470.c25
-rw-r--r--drivers/clk/clk-twl.c214
-rw-r--r--drivers/clk/clk-versaclock3.c1171
-rw-r--r--drivers/clk/clk-versaclock5.c318
-rw-r--r--drivers/clk/clk-versaclock7.c1315
-rw-r--r--drivers/clk/clk-vt8500.c59
-rw-r--r--drivers/clk/clk-wm831x.c15
-rw-r--r--drivers/clk/clk-xgene.c49
-rw-r--r--drivers/clk/clk.c859
-rw-r--r--drivers/clk/clk_kunit_helpers.c237
-rw-r--r--drivers/clk/clk_parent_data_test.h10
-rw-r--r--drivers/clk/clk_test.c2729
-rw-r--r--drivers/clk/clkdev.c111
-rw-r--r--drivers/clk/davinci/Makefile10
-rw-r--r--drivers/clk/davinci/da8xx-cfgchip.c32
-rw-r--r--drivers/clk/davinci/pll-da830.c71
-rw-r--r--drivers/clk/davinci/pll-dm355.c77
-rw-r--r--drivers/clk/davinci/pll-dm365.c146
-rw-r--r--drivers/clk/davinci/pll-dm644x.c81
-rw-r--r--drivers/clk/davinci/pll-dm646x.c85
-rw-r--r--drivers/clk/davinci/pll.c95
-rw-r--r--drivers/clk/davinci/pll.h13
-rw-r--r--drivers/clk/davinci/psc-da830.c118
-rw-r--r--drivers/clk/davinci/psc-da850.c7
-rw-r--r--drivers/clk/davinci/psc-dm355.c89
-rw-r--r--drivers/clk/davinci/psc-dm365.c111
-rw-r--r--drivers/clk/davinci/psc-dm644x.c85
-rw-r--r--drivers/clk/davinci/psc-dm646x.c82
-rw-r--r--drivers/clk/davinci/psc.c35
-rw-r--r--drivers/clk/davinci/psc.h18
-rw-r--r--drivers/clk/hisilicon/clk-hi3519.c5
-rw-r--r--drivers/clk/hisilicon/clk-hi3559a.c17
-rw-r--r--drivers/clk/hisilicon/clk-hi3620.c5
-rw-r--r--drivers/clk/hisilicon/clk-hi3660-stub.c18
-rw-r--r--drivers/clk/hisilicon/clk-hi3660.c2
-rw-r--r--drivers/clk/hisilicon/clk-hi3670.c2
-rw-r--r--drivers/clk/hisilicon/clk-hi6220-stub.c14
-rw-r--r--drivers/clk/hisilicon/clk-hi6220.c3
-rw-r--r--drivers/clk/hisilicon/clk-hip04.c3
-rw-r--r--drivers/clk/hisilicon/clk.c2
-rw-r--r--drivers/clk/hisilicon/clkdivider-hi6220.c12
-rw-r--r--drivers/clk/hisilicon/clkgate-separated.c16
-rw-r--r--drivers/clk/hisilicon/crg-hi3516cv300.c5
-rw-r--r--drivers/clk/hisilicon/crg-hi3798cv200.c5
-rw-r--r--drivers/clk/imgtec/clk-boston.c6
-rw-r--r--drivers/clk/imx/Kconfig12
-rw-r--r--drivers/clk/imx/Makefile9
-rw-r--r--drivers/clk/imx/clk-busy.c9
-rw-r--r--drivers/clk/imx/clk-composite-7ulp.c24
-rw-r--r--drivers/clk/imx/clk-composite-8m.c126
-rw-r--r--drivers/clk/imx/clk-composite-93.c171
-rw-r--r--drivers/clk/imx/clk-cpu.c10
-rw-r--r--drivers/clk/imx/clk-fixup-div.c10
-rw-r--r--drivers/clk/imx/clk-fixup-mux.c3
-rw-r--r--drivers/clk/imx/clk-frac-pll.c20
-rw-r--r--drivers/clk/imx/clk-fracn-gppll.c125
-rw-r--r--drivers/clk/imx/clk-gate-93.c199
-rw-r--r--drivers/clk/imx/clk-gate-exclusive.c2
-rw-r--r--drivers/clk/imx/clk-gpr-mux.c114
-rw-r--r--drivers/clk/imx/clk-imx1.c1
-rw-r--r--drivers/clk/imx/clk-imx25.c7
-rw-r--r--drivers/clk/imx/clk-imx27.c3
-rw-r--r--drivers/clk/imx/clk-imx31.c1
-rw-r--r--drivers/clk/imx/clk-imx35.c3
-rw-r--r--drivers/clk/imx/clk-imx5.c8
-rw-r--r--drivers/clk/imx/clk-imx6q.c15
-rw-r--r--drivers/clk/imx/clk-imx6sl.c2
-rw-r--r--drivers/clk/imx/clk-imx6sll.c10
-rw-r--r--drivers/clk/imx/clk-imx6sx.c40
-rw-r--r--drivers/clk/imx/clk-imx6ul.c45
-rw-r--r--drivers/clk/imx/clk-imx7d.c12
-rw-r--r--drivers/clk/imx/clk-imx7ulp.c14
-rw-r--r--drivers/clk/imx/clk-imx8-acm.c497
-rw-r--r--drivers/clk/imx/clk-imx8dxl-rsrc.c3
-rw-r--r--drivers/clk/imx/clk-imx8mm.c18
-rw-r--r--drivers/clk/imx/clk-imx8mn.c141
-rw-r--r--drivers/clk/imx/clk-imx8mp-audiomix.c439
-rw-r--r--drivers/clk/imx/clk-imx8mp.c228
-rw-r--r--drivers/clk/imx/clk-imx8mq.c33
-rw-r--r--drivers/clk/imx/clk-imx8qm-rsrc.c5
-rw-r--r--drivers/clk/imx/clk-imx8qxp-lpcg.c7
-rw-r--r--drivers/clk/imx/clk-imx8qxp-lpcg.h1
-rw-r--r--drivers/clk/imx/clk-imx8qxp-rsrc.c4
-rw-r--r--drivers/clk/imx/clk-imx8qxp.c77
-rw-r--r--drivers/clk/imx/clk-imx8ulp-sim-lpav.c156
-rw-r--r--drivers/clk/imx/clk-imx8ulp.c40
-rw-r--r--drivers/clk/imx/clk-imx93.c181
-rw-r--r--drivers/clk/imx/clk-imx95-blk-ctl.c540
-rw-r--r--drivers/clk/imx/clk-imxrt1050.c32
-rw-r--r--drivers/clk/imx/clk-lpcg-scu.c41
-rw-r--r--drivers/clk/imx/clk-pfd.c20
-rw-r--r--drivers/clk/imx/clk-pll14xx.c71
-rw-r--r--drivers/clk/imx/clk-pllv2.c23
-rw-r--r--drivers/clk/imx/clk-pllv3.c74
-rw-r--r--drivers/clk/imx/clk-pllv4.c71
-rw-r--r--drivers/clk/imx/clk-scu.c87
-rw-r--r--drivers/clk/imx/clk-vf610.c12
-rw-r--r--drivers/clk/imx/clk.c45
-rw-r--r--drivers/clk/imx/clk.h53
-rw-r--r--drivers/clk/ingenic/Kconfig10
-rw-r--r--drivers/clk/ingenic/Makefile1
-rw-r--r--drivers/clk/ingenic/cgu.c69
-rw-r--r--drivers/clk/ingenic/cgu.h19
-rw-r--r--drivers/clk/ingenic/jz4725b-cgu.c2
-rw-r--r--drivers/clk/ingenic/jz4740-cgu.c2
-rw-r--r--drivers/clk/ingenic/jz4755-cgu.c346
-rw-r--r--drivers/clk/ingenic/jz4760-cgu.c20
-rw-r--r--drivers/clk/ingenic/jz4770-cgu.c2
-rw-r--r--drivers/clk/ingenic/jz4780-cgu.c26
-rw-r--r--drivers/clk/ingenic/pm.c14
-rw-r--r--drivers/clk/ingenic/pm.h2
-rw-r--r--drivers/clk/ingenic/tcu.c46
-rw-r--r--drivers/clk/ingenic/x1000-cgu.c140
-rw-r--r--drivers/clk/ingenic/x1830-cgu.c2
-rw-r--r--drivers/clk/keystone/pll.c17
-rw-r--r--drivers/clk/keystone/sci-clk.c30
-rw-r--r--drivers/clk/keystone/syscon-clk.c73
-rw-r--r--drivers/clk/kunit_clk_assigned_rates.h8
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_multiple.dtso16
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_multiple_consumer.dtso20
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_null.dtso14
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_null_consumer.dtso18
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_one.dtso14
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_one_consumer.dtso18
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso16
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso20
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_u64_one.dtso14
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso18
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_without.dtso13
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_without_consumer.dtso17
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_zero.dtso12
-rw-r--r--drivers/clk/kunit_clk_assigned_rates_zero_consumer.dtso16
-rw-r--r--drivers/clk/kunit_clk_fixed_rate_test.dtso19
-rw-r--r--drivers/clk/kunit_clk_hw_get_dev_of_node.dtso10
-rw-r--r--drivers/clk/kunit_clk_parent_data_test.dtso28
-rw-r--r--drivers/clk/loongson1/Makefile4
-rw-r--r--drivers/clk/loongson1/clk-loongson1b.c118
-rw-r--r--drivers/clk/loongson1/clk-loongson1c.c95
-rw-r--r--drivers/clk/loongson1/clk.c41
-rw-r--r--drivers/clk/loongson1/clk.h15
-rw-r--r--drivers/clk/mediatek/Kconfig657
-rw-r--r--drivers/clk/mediatek/Makefile113
-rw-r--r--drivers/clk/mediatek/clk-apmixed.c12
-rw-r--r--drivers/clk/mediatek/clk-cpumux.c11
-rw-r--r--drivers/clk/mediatek/clk-cpumux.h2
-rw-r--r--drivers/clk/mediatek/clk-fhctl.c264
-rw-r--r--drivers/clk/mediatek/clk-fhctl.h33
-rw-r--r--drivers/clk/mediatek/clk-gate.c131
-rw-r--r--drivers/clk/mediatek/clk-gate.h10
-rw-r--r--drivers/clk/mediatek/clk-mt2701-aud.c88
-rw-r--r--drivers/clk/mediatek/clk-mt2701-bdp.c62
-rw-r--r--drivers/clk/mediatek/clk-mt2701-eth.c51
-rw-r--r--drivers/clk/mediatek/clk-mt2701-g3d.c75
-rw-r--r--drivers/clk/mediatek/clk-mt2701-hif.c53
-rw-r--r--drivers/clk/mediatek/clk-mt2701-img.c52
-rw-r--r--drivers/clk/mediatek/clk-mt2701-mm.c57
-rw-r--r--drivers/clk/mediatek/clk-mt2701-vdec.c62
-rw-r--r--drivers/clk/mediatek/clk-mt2701.c88
-rw-r--r--drivers/clk/mediatek/clk-mt2712-apmixedsys.c168
-rw-r--r--drivers/clk/mediatek/clk-mt2712-bdp.c49
-rw-r--r--drivers/clk/mediatek/clk-mt2712-img.c49
-rw-r--r--drivers/clk/mediatek/clk-mt2712-jpgdec.c49
-rw-r--r--drivers/clk/mediatek/clk-mt2712-mfg.c49
-rw-r--r--drivers/clk/mediatek/clk-mt2712-mm.c66
-rw-r--r--drivers/clk/mediatek/clk-mt2712-vdec.c59
-rw-r--r--drivers/clk/mediatek/clk-mt2712-venc.c49
-rw-r--r--drivers/clk/mediatek/clk-mt2712.c1057
-rw-r--r--drivers/clk/mediatek/clk-mt6735-apmixedsys.c137
-rw-r--r--drivers/clk/mediatek/clk-mt6735-imgsys.c57
-rw-r--r--drivers/clk/mediatek/clk-mt6735-infracfg.c107
-rw-r--r--drivers/clk/mediatek/clk-mt6735-mfgcfg.c61
-rw-r--r--drivers/clk/mediatek/clk-mt6735-pericfg.c124
-rw-r--r--drivers/clk/mediatek/clk-mt6735-topckgen.c394
-rw-r--r--drivers/clk/mediatek/clk-mt6735-vdecsys.c79
-rw-r--r--drivers/clk/mediatek/clk-mt6735-vencsys.c53
-rw-r--r--drivers/clk/mediatek/clk-mt6765-audio.c59
-rw-r--r--drivers/clk/mediatek/clk-mt6765-cam.c48
-rw-r--r--drivers/clk/mediatek/clk-mt6765-img.c48
-rw-r--r--drivers/clk/mediatek/clk-mt6765-mipi0a.c49
-rw-r--r--drivers/clk/mediatek/clk-mt6765-mm.c48
-rw-r--r--drivers/clk/mediatek/clk-mt6765-vcodec.c49
-rw-r--r--drivers/clk/mediatek/clk-mt6765.c146
-rw-r--r--drivers/clk/mediatek/clk-mt6779-aud.c37
-rw-r--r--drivers/clk/mediatek/clk-mt6779-cam.c33
-rw-r--r--drivers/clk/mediatek/clk-mt6779-img.c32
-rw-r--r--drivers/clk/mediatek/clk-mt6779-ipe.c32
-rw-r--r--drivers/clk/mediatek/clk-mt6779-mfg.c30
-rw-r--r--drivers/clk/mediatek/clk-mt6779-mm.c27
-rw-r--r--drivers/clk/mediatek/clk-mt6779-vdec.c32
-rw-r--r--drivers/clk/mediatek/clk-mt6779-venc.c32
-rw-r--r--drivers/clk/mediatek/clk-mt6779.c92
-rw-r--r--drivers/clk/mediatek/clk-mt6795-apmixedsys.c213
-rw-r--r--drivers/clk/mediatek/clk-mt6795-infracfg.c152
-rw-r--r--drivers/clk/mediatek/clk-mt6795-mfg.c51
-rw-r--r--drivers/clk/mediatek/clk-mt6795-mm.c101
-rw-r--r--drivers/clk/mediatek/clk-mt6795-pericfg.c161
-rw-r--r--drivers/clk/mediatek/clk-mt6795-topckgen.c555
-rw-r--r--drivers/clk/mediatek/clk-mt6795-vdecsys.c56
-rw-r--r--drivers/clk/mediatek/clk-mt6795-vencsys.c51
-rw-r--r--drivers/clk/mediatek/clk-mt6797-img.c51
-rw-r--r--drivers/clk/mediatek/clk-mt6797-mm.c56
-rw-r--r--drivers/clk/mediatek/clk-mt6797-vdec.c61
-rw-r--r--drivers/clk/mediatek/clk-mt6797-venc.c51
-rw-r--r--drivers/clk/mediatek/clk-mt6797.c61
-rw-r--r--drivers/clk/mediatek/clk-mt7622-apmixedsys.c149
-rw-r--r--drivers/clk/mediatek/clk-mt7622-aud.c102
-rw-r--r--drivers/clk/mediatek/clk-mt7622-eth.c111
-rw-r--r--drivers/clk/mediatek/clk-mt7622-hif.c116
-rw-r--r--drivers/clk/mediatek/clk-mt7622-infracfg.c126
-rw-r--r--drivers/clk/mediatek/clk-mt7622.c373
-rw-r--r--drivers/clk/mediatek/clk-mt7629-eth.c37
-rw-r--r--drivers/clk/mediatek/clk-mt7629-hif.c116
-rw-r--r--drivers/clk/mediatek/clk-mt7629.c74
-rw-r--r--drivers/clk/mediatek/clk-mt7981-apmixed.c104
-rw-r--r--drivers/clk/mediatek/clk-mt7981-eth.c119
-rw-r--r--drivers/clk/mediatek/clk-mt7981-infracfg.c209
-rw-r--r--drivers/clk/mediatek/clk-mt7981-topckgen.c425
-rw-r--r--drivers/clk/mediatek/clk-mt7986-apmixed.c14
-rw-r--r--drivers/clk/mediatek/clk-mt7986-eth.c114
-rw-r--r--drivers/clk/mediatek/clk-mt7986-infracfg.c95
-rw-r--r--drivers/clk/mediatek/clk-mt7986-topckgen.c122
-rw-r--r--drivers/clk/mediatek/clk-mt7988-apmixed.c116
-rw-r--r--drivers/clk/mediatek/clk-mt7988-eth.c150
-rw-r--r--drivers/clk/mediatek/clk-mt7988-infracfg.c300
-rw-r--r--drivers/clk/mediatek/clk-mt7988-topckgen.c327
-rw-r--r--drivers/clk/mediatek/clk-mt7988-xfipll.c82
-rw-r--r--drivers/clk/mediatek/clk-mt8135-apmixedsys.c105
-rw-r--r--drivers/clk/mediatek/clk-mt8135.c266
-rw-r--r--drivers/clk/mediatek/clk-mt8167-apmixedsys.c147
-rw-r--r--drivers/clk/mediatek/clk-mt8167-aud.c50
-rw-r--r--drivers/clk/mediatek/clk-mt8167-img.c52
-rw-r--r--drivers/clk/mediatek/clk-mt8167-mfgcfg.c52
-rw-r--r--drivers/clk/mediatek/clk-mt8167-mm.c73
-rw-r--r--drivers/clk/mediatek/clk-mt8167-vdec.c60
-rw-r--r--drivers/clk/mediatek/clk-mt8167.c380
-rw-r--r--drivers/clk/mediatek/clk-mt8173-apmixedsys.c219
-rw-r--r--drivers/clk/mediatek/clk-mt8173-img.c56
-rw-r--r--drivers/clk/mediatek/clk-mt8173-infracfg.c164
-rw-r--r--drivers/clk/mediatek/clk-mt8173-mm.c70
-rw-r--r--drivers/clk/mediatek/clk-mt8173-pericfg.c123
-rw-r--r--drivers/clk/mediatek/clk-mt8173-topckgen.c654
-rw-r--r--drivers/clk/mediatek/clk-mt8173-vdecsys.c58
-rw-r--r--drivers/clk/mediatek/clk-mt8173-vencsys.c65
-rw-r--r--drivers/clk/mediatek/clk-mt8173.c1125
-rw-r--r--drivers/clk/mediatek/clk-mt8183-apmixedsys.c197
-rw-r--r--drivers/clk/mediatek/clk-mt8183-audio.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-cam.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-img.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-ipu0.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-ipu1.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-ipu_adl.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-ipu_conn.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-mfgcfg.c41
-rw-r--r--drivers/clk/mediatek/clk-mt8183-mm.c29
-rw-r--r--drivers/clk/mediatek/clk-mt8183-vdec.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183-venc.c32
-rw-r--r--drivers/clk/mediatek/clk-mt8183.c1068
-rw-r--r--drivers/clk/mediatek/clk-mt8186-apmixedsys.c78
-rw-r--r--drivers/clk/mediatek/clk-mt8186-cam.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-img.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-imp_iic_wrap.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-infra_ao.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-ipe.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-mcu.c68
-rw-r--r--drivers/clk/mediatek/clk-mt8186-mdp.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-mfg.c11
-rw-r--r--drivers/clk/mediatek/clk-mt8186-mm.c60
-rw-r--r--drivers/clk/mediatek/clk-mt8186-topckgen.c211
-rw-r--r--drivers/clk/mediatek/clk-mt8186-vdec.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-venc.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8186-wpe.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8188-adsp_audio26m.c52
-rw-r--r--drivers/clk/mediatek/clk-mt8188-apmixedsys.c157
-rw-r--r--drivers/clk/mediatek/clk-mt8188-cam.c138
-rw-r--r--drivers/clk/mediatek/clk-mt8188-ccu.c51
-rw-r--r--drivers/clk/mediatek/clk-mt8188-img.c131
-rw-r--r--drivers/clk/mediatek/clk-mt8188-imp_iic_wrap.c84
-rw-r--r--drivers/clk/mediatek/clk-mt8188-infra_ao.c225
-rw-r--r--drivers/clk/mediatek/clk-mt8188-ipe.c68
-rw-r--r--drivers/clk/mediatek/clk-mt8188-mfg.c51
-rw-r--r--drivers/clk/mediatek/clk-mt8188-peri_ao.c61
-rw-r--r--drivers/clk/mediatek/clk-mt8188-topckgen.c1362
-rw-r--r--drivers/clk/mediatek/clk-mt8188-vdec.c94
-rw-r--r--drivers/clk/mediatek/clk-mt8188-vdo0.c109
-rw-r--r--drivers/clk/mediatek/clk-mt8188-vdo1.c167
-rw-r--r--drivers/clk/mediatek/clk-mt8188-venc.c57
-rw-r--r--drivers/clk/mediatek/clk-mt8188-vpp0.c116
-rw-r--r--drivers/clk/mediatek/clk-mt8188-vpp1.c111
-rw-r--r--drivers/clk/mediatek/clk-mt8188-wpe.c106
-rw-r--r--drivers/clk/mediatek/clk-mt8192-apmixedsys.c213
-rw-r--r--drivers/clk/mediatek/clk-mt8192-aud.c35
-rw-r--r--drivers/clk/mediatek/clk-mt8192-cam.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-img.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-imp_iic_wrap.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-ipe.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-mdp.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-mfg.c14
-rw-r--r--drivers/clk/mediatek/clk-mt8192-mm.c33
-rw-r--r--drivers/clk/mediatek/clk-mt8192-msdc.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-scp_adsp.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-vdec.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192-venc.c8
-rw-r--r--drivers/clk/mediatek/clk-mt8192.c595
-rw-r--r--drivers/clk/mediatek/clk-mt8195-apmixedsys.c84
-rw-r--r--drivers/clk/mediatek/clk-mt8195-apusys_pll.c10
-rw-r--r--drivers/clk/mediatek/clk-mt8195-cam.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-ccu.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-img.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-infra_ao.c24
-rw-r--r--drivers/clk/mediatek/clk-mt8195-ipe.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-mfg.c12
-rw-r--r--drivers/clk/mediatek/clk-mt8195-peri_ao.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-scp_adsp.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-topckgen.c220
-rw-r--r--drivers/clk/mediatek/clk-mt8195-vdec.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-vdo0.c67
-rw-r--r--drivers/clk/mediatek/clk-mt8195-vdo1.c77
-rw-r--r--drivers/clk/mediatek/clk-mt8195-venc.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8195-vpp0.c22
-rw-r--r--drivers/clk/mediatek/clk-mt8195-vpp1.c22
-rw-r--r--drivers/clk/mediatek/clk-mt8195-wpe.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8196-apmixedsys.c204
-rw-r--r--drivers/clk/mediatek/clk-mt8196-disp0.c170
-rw-r--r--drivers/clk/mediatek/clk-mt8196-disp1.c170
-rw-r--r--drivers/clk/mediatek/clk-mt8196-imp_iic_wrap.c118
-rw-r--r--drivers/clk/mediatek/clk-mt8196-mcu.c167
-rw-r--r--drivers/clk/mediatek/clk-mt8196-mdpsys.c186
-rw-r--r--drivers/clk/mediatek/clk-mt8196-mfg.c150
-rw-r--r--drivers/clk/mediatek/clk-mt8196-ovl0.c154
-rw-r--r--drivers/clk/mediatek/clk-mt8196-ovl1.c154
-rw-r--r--drivers/clk/mediatek/clk-mt8196-peri_ao.c142
-rw-r--r--drivers/clk/mediatek/clk-mt8196-pextp.c131
-rw-r--r--drivers/clk/mediatek/clk-mt8196-topckgen.c985
-rw-r--r--drivers/clk/mediatek/clk-mt8196-topckgen2.c568
-rw-r--r--drivers/clk/mediatek/clk-mt8196-ufs_ao.c108
-rw-r--r--drivers/clk/mediatek/clk-mt8196-vdec.c253
-rw-r--r--drivers/clk/mediatek/clk-mt8196-vdisp_ao.c80
-rw-r--r--drivers/clk/mediatek/clk-mt8196-venc.c236
-rw-r--r--drivers/clk/mediatek/clk-mt8196-vlpckgen.c725
-rw-r--r--drivers/clk/mediatek/clk-mt8365-apmixedsys.c168
-rw-r--r--drivers/clk/mediatek/clk-mt8365-apu.c58
-rw-r--r--drivers/clk/mediatek/clk-mt8365-cam.c60
-rw-r--r--drivers/clk/mediatek/clk-mt8365-mfg.c66
-rw-r--r--drivers/clk/mediatek/clk-mt8365-mm.c97
-rw-r--r--drivers/clk/mediatek/clk-mt8365-vdec.c66
-rw-r--r--drivers/clk/mediatek/clk-mt8365-venc.c55
-rw-r--r--drivers/clk/mediatek/clk-mt8365.c817
-rw-r--r--drivers/clk/mediatek/clk-mt8516-apmixedsys.c122
-rw-r--r--drivers/clk/mediatek/clk-mt8516-aud.c50
-rw-r--r--drivers/clk/mediatek/clk-mt8516.c238
-rw-r--r--drivers/clk/mediatek/clk-mtk.c304
-rw-r--r--drivers/clk/mediatek/clk-mtk.h78
-rw-r--r--drivers/clk/mediatek/clk-mux.c194
-rw-r--r--drivers/clk/mediatek/clk-mux.h148
-rw-r--r--drivers/clk/mediatek/clk-pll.c143
-rw-r--r--drivers/clk/mediatek/clk-pll.h63
-rw-r--r--drivers/clk/mediatek/clk-pllfh.c298
-rw-r--r--drivers/clk/mediatek/clk-pllfh.h83
-rw-r--r--drivers/clk/mediatek/reset.c62
-rw-r--r--drivers/clk/mediatek/reset.h10
-rw-r--r--drivers/clk/meson/Kconfig106
-rw-r--r--drivers/clk/meson/Makefile9
-rw-r--r--drivers/clk/meson/a1-peripherals.c2100
-rw-r--r--drivers/clk/meson/a1-pll.c327
-rw-r--r--drivers/clk/meson/axg-aoclk.c185
-rw-r--r--drivers/clk/meson/axg-aoclk.h18
-rw-r--r--drivers/clk/meson/axg-audio.c1483
-rw-r--r--drivers/clk/meson/axg-audio.h143
-rw-r--r--drivers/clk/meson/axg.c756
-rw-r--r--drivers/clk/meson/axg.h168
-rw-r--r--drivers/clk/meson/c3-peripherals.c1128
-rw-r--r--drivers/clk/meson/c3-pll.c684
-rw-r--r--drivers/clk/meson/clk-cpu-dyndiv.c15
-rw-r--r--drivers/clk/meson/clk-dualdiv.c30
-rw-r--r--drivers/clk/meson/clk-mpll.c44
-rw-r--r--drivers/clk/meson/clk-mpll.h1
-rw-r--r--drivers/clk/meson/clk-phase.c21
-rw-r--r--drivers/clk/meson/clk-pll.c122
-rw-r--r--drivers/clk/meson/clk-pll.h4
-rw-r--r--drivers/clk/meson/clk-regmap.c64
-rw-r--r--drivers/clk/meson/clk-regmap.h24
-rw-r--r--drivers/clk/meson/g12a-aoclk.c282
-rw-r--r--drivers/clk/meson/g12a-aoclk.h32
-rw-r--r--drivers/clk/meson/g12a.c3989
-rw-r--r--drivers/clk/meson/g12a.h274
-rw-r--r--drivers/clk/meson/gxbb-aoclk.c153
-rw-r--r--drivers/clk/meson/gxbb-aoclk.h15
-rw-r--r--drivers/clk/meson/gxbb.c1981
-rw-r--r--drivers/clk/meson/gxbb.h196
-rw-r--r--drivers/clk/meson/meson-aoclk.c52
-rw-r--r--drivers/clk/meson/meson-aoclk.h5
-rw-r--r--drivers/clk/meson/meson-clkc-utils.c111
-rw-r--r--drivers/clk/meson/meson-clkc-utils.h108
-rw-r--r--drivers/clk/meson/meson-eeclk.c59
-rw-r--r--drivers/clk/meson/meson-eeclk.h25
-rw-r--r--drivers/clk/meson/meson8-ddr.c71
-rw-r--r--drivers/clk/meson/meson8b.c2148
-rw-r--r--drivers/clk/meson/meson8b.h197
-rw-r--r--drivers/clk/meson/s4-peripherals.c3061
-rw-r--r--drivers/clk/meson/s4-pll.c831
-rw-r--r--drivers/clk/meson/sclk-div.c21
-rw-r--r--drivers/clk/meson/vclk.c144
-rw-r--r--drivers/clk/meson/vclk.h51
-rw-r--r--drivers/clk/meson/vid-pll-div.c6
-rw-r--r--drivers/clk/microchip/Kconfig6
-rw-r--r--drivers/clk/microchip/Makefile1
-rw-r--r--drivers/clk/microchip/clk-core.c57
-rw-r--r--drivers/clk/microchip/clk-mpfs-ccc.c293
-rw-r--r--drivers/clk/microchip/clk-mpfs.c357
-rw-r--r--drivers/clk/microchip/clk-pic32mzda.c4
-rw-r--r--drivers/clk/mmp/Kconfig10
-rw-r--r--drivers/clk/mmp/Makefile7
-rw-r--r--drivers/clk/mmp/clk-audio.c28
-rw-r--r--drivers/clk/mmp/clk-frac.c82
-rw-r--r--drivers/clk/mmp/clk-gate.c2
-rw-r--r--drivers/clk/mmp/clk-mix.c10
-rw-r--r--drivers/clk/mmp/clk-mmp2.c454
-rw-r--r--drivers/clk/mmp/clk-of-mmp2.c30
-rw-r--r--drivers/clk/mmp/clk-of-pxa168.c124
-rw-r--r--drivers/clk/mmp/clk-of-pxa1928.c13
-rw-r--r--drivers/clk/mmp/clk-of-pxa910.c8
-rw-r--r--drivers/clk/mmp/clk-pxa168.c354
-rw-r--r--drivers/clk/mmp/clk-pxa1908-apbc.c130
-rw-r--r--drivers/clk/mmp/clk-pxa1908-apbcp.c82
-rw-r--r--drivers/clk/mmp/clk-pxa1908-apmu.c128
-rw-r--r--drivers/clk/mmp/clk-pxa1908-mpmu.c112
-rw-r--r--drivers/clk/mmp/clk-pxa910.c325
-rw-r--r--drivers/clk/mmp/clk.h10
-rw-r--r--drivers/clk/mmp/pwr-island.c2
-rw-r--r--drivers/clk/mstar/Kconfig7
-rw-r--r--drivers/clk/mstar/Makefile1
-rw-r--r--drivers/clk/mstar/clk-msc313-cpupll.c222
-rw-r--r--drivers/clk/mvebu/ap-cpu-clk.c29
-rw-r--r--drivers/clk/mvebu/armada-37xx-periph.c24
-rw-r--r--drivers/clk/mvebu/armada-37xx-tbg.c14
-rw-r--r--drivers/clk/mvebu/armada-37xx-xtal.c4
-rw-r--r--drivers/clk/mvebu/armada-xp.c5
-rw-r--r--drivers/clk/mvebu/armada_ap_cp_helper.c8
-rw-r--r--drivers/clk/mvebu/clk-corediv.c18
-rw-r--r--drivers/clk/mvebu/clk-cpu.c26
-rw-r--r--drivers/clk/mvebu/common.c12
-rw-r--r--drivers/clk/mvebu/cp110-system-controller.c22
-rw-r--r--drivers/clk/mvebu/dove-divider.c18
-rw-r--r--drivers/clk/mxs/clk-div.c10
-rw-r--r--drivers/clk/mxs/clk-frac.c16
-rw-r--r--drivers/clk/mxs/clk-ref.c30
-rw-r--r--drivers/clk/nuvoton/Kconfig19
-rw-r--r--drivers/clk/nuvoton/Makefile4
-rw-r--r--drivers/clk/nuvoton/clk-ma35d1-divider.c136
-rw-r--r--drivers/clk/nuvoton/clk-ma35d1-pll.c368
-rw-r--r--drivers/clk/nuvoton/clk-ma35d1.c1058
-rw-r--r--drivers/clk/nuvoton/clk-ma35d1.h18
-rw-r--r--drivers/clk/nxp/clk-lpc18xx-ccu.c2
-rw-r--r--drivers/clk/nxp/clk-lpc18xx-cgu.c23
-rw-r--r--drivers/clk/nxp/clk-lpc32xx.c62
-rw-r--r--drivers/clk/pistachio/clk-pll.c20
-rw-r--r--drivers/clk/pistachio/clk.h4
-rw-r--r--drivers/clk/pxa/clk-pxa.c3
-rw-r--r--drivers/clk/pxa/clk-pxa25x.c2
-rw-r--r--drivers/clk/pxa/clk-pxa27x.c3
-rw-r--r--drivers/clk/pxa/clk-pxa3xx.c2
-rw-r--r--drivers/clk/qcom/Kconfig867
-rw-r--r--drivers/clk/qcom/Makefile87
-rw-r--r--drivers/clk/qcom/a53-pll.c6
-rw-r--r--drivers/clk/qcom/a7-pll.c3
-rw-r--r--drivers/clk/qcom/apcs-msm8916.c4
-rw-r--r--drivers/clk/qcom/apcs-msm8996.c89
-rw-r--r--drivers/clk/qcom/apcs-sdx55.c10
-rw-r--r--drivers/clk/qcom/apss-ipq-pll.c174
-rw-r--r--drivers/clk/qcom/apss-ipq5424.c258
-rw-r--r--drivers/clk/qcom/apss-ipq6018.c76
-rw-r--r--drivers/clk/qcom/camcc-milos.c2161
-rw-r--r--drivers/clk/qcom/camcc-qcs615.c1597
-rw-r--r--drivers/clk/qcom/camcc-sa8775p.c1960
-rw-r--r--drivers/clk/qcom/camcc-sc7180.c39
-rw-r--r--drivers/clk/qcom/camcc-sc7280.c310
-rw-r--r--drivers/clk/qcom/camcc-sc8180x.c2889
-rw-r--r--drivers/clk/qcom/camcc-sc8280xp.c3064
-rw-r--r--drivers/clk/qcom/camcc-sdm845.c18
-rw-r--r--drivers/clk/qcom/camcc-sm4450.c1687
-rw-r--r--drivers/clk/qcom/camcc-sm6350.c1918
-rw-r--r--drivers/clk/qcom/camcc-sm7150.c2059
-rw-r--r--drivers/clk/qcom/camcc-sm8150.c2159
-rw-r--r--drivers/clk/qcom/camcc-sm8250.c67
-rw-r--r--drivers/clk/qcom/camcc-sm8450.c670
-rw-r--r--drivers/clk/qcom/camcc-sm8550.c3574
-rw-r--r--drivers/clk/qcom/camcc-sm8650.c3592
-rw-r--r--drivers/clk/qcom/camcc-x1e80100.c2491
-rw-r--r--drivers/clk/qcom/clk-alpha-pll.c1171
-rw-r--r--drivers/clk/qcom/clk-alpha-pll.h77
-rw-r--r--drivers/clk/qcom/clk-branch.c64
-rw-r--r--drivers/clk/qcom/clk-branch.h76
-rw-r--r--drivers/clk/qcom/clk-cbf-8996.c370
-rw-r--r--drivers/clk/qcom/clk-cpu-8996.c458
-rw-r--r--drivers/clk/qcom/clk-hfpll.c18
-rw-r--r--drivers/clk/qcom/clk-hfpll.h1
-rw-r--r--drivers/clk/qcom/clk-krait.c12
-rw-r--r--drivers/clk/qcom/clk-rcg.c3
-rw-r--r--drivers/clk/qcom/clk-rcg.h30
-rw-r--r--drivers/clk/qcom/clk-rcg2.c510
-rw-r--r--drivers/clk/qcom/clk-regmap-divider.c27
-rw-r--r--drivers/clk/qcom/clk-rpm.c254
-rw-r--r--drivers/clk/qcom/clk-rpmh.c718
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c1605
-rw-r--r--drivers/clk/qcom/clk-spmi-pmic-div.c37
-rw-r--r--drivers/clk/qcom/common.c160
-rw-r--r--drivers/clk/qcom/common.h25
-rw-r--r--drivers/clk/qcom/dispcc-glymur.c1982
-rw-r--r--drivers/clk/qcom/dispcc-milos.c974
-rw-r--r--drivers/clk/qcom/dispcc-qcm2290.c53
-rw-r--r--drivers/clk/qcom/dispcc-qcs615.c792
-rw-r--r--drivers/clk/qcom/dispcc-sc7180.c23
-rw-r--r--drivers/clk/qcom/dispcc-sc7280.c30
-rw-r--r--drivers/clk/qcom/dispcc-sc8280xp.c3207
-rw-r--r--drivers/clk/qcom/dispcc-sdm845.c18
-rw-r--r--drivers/clk/qcom/dispcc-sm4450.c769
-rw-r--r--drivers/clk/qcom/dispcc-sm6115.c607
-rw-r--r--drivers/clk/qcom/dispcc-sm6125.c19
-rw-r--r--drivers/clk/qcom/dispcc-sm6350.c49
-rw-r--r--drivers/clk/qcom/dispcc-sm6375.c601
-rw-r--r--drivers/clk/qcom/dispcc-sm7150.c1012
-rw-r--r--drivers/clk/qcom/dispcc-sm8250.c195
-rw-r--r--drivers/clk/qcom/dispcc-sm8450.c1865
-rw-r--r--drivers/clk/qcom/dispcc-sm8550.c1832
-rw-r--r--drivers/clk/qcom/dispcc-sm8750.c1961
-rw-r--r--drivers/clk/qcom/dispcc-x1e80100.c1721
-rw-r--r--drivers/clk/qcom/dispcc0-sa8775p.c1480
-rw-r--r--drivers/clk/qcom/dispcc1-sa8775p.c1480
-rw-r--r--drivers/clk/qcom/ecpricc-qdu1000.c2486
-rw-r--r--drivers/clk/qcom/gcc-apq8084.c1025
-rw-r--r--drivers/clk/qcom/gcc-glymur.c8615
-rw-r--r--drivers/clk/qcom/gcc-ipq4019.c1195
-rw-r--r--drivers/clk/qcom/gcc-ipq5018.c3725
-rw-r--r--drivers/clk/qcom/gcc-ipq5332.c3327
-rw-r--r--drivers/clk/qcom/gcc-ipq5424.c3342
-rw-r--r--drivers/clk/qcom/gcc-ipq6018.c162
-rw-r--r--drivers/clk/qcom/gcc-ipq806x.c10
-rw-r--r--drivers/clk/qcom/gcc-ipq8074.c1930
-rw-r--r--drivers/clk/qcom/gcc-ipq9574.c4154
-rw-r--r--drivers/clk/qcom/gcc-mdm9607.c5
-rw-r--r--drivers/clk/qcom/gcc-mdm9615.c268
-rw-r--r--drivers/clk/qcom/gcc-milos.c3225
-rw-r--r--drivers/clk/qcom/gcc-msm8660.c346
-rw-r--r--drivers/clk/qcom/gcc-msm8909.c2730
-rw-r--r--drivers/clk/qcom/gcc-msm8916.c1021
-rw-r--r--drivers/clk/qcom/gcc-msm8917.c3913
-rw-r--r--drivers/clk/qcom/gcc-msm8939.c736
-rw-r--r--drivers/clk/qcom/gcc-msm8953.c10
-rw-r--r--drivers/clk/qcom/gcc-msm8960.c458
-rw-r--r--drivers/clk/qcom/gcc-msm8974.c691
-rw-r--r--drivers/clk/qcom/gcc-msm8976.c35
-rw-r--r--drivers/clk/qcom/gcc-msm8994.c55
-rw-r--r--drivers/clk/qcom/gcc-msm8996.c245
-rw-r--r--drivers/clk/qcom/gcc-msm8998.c147
-rw-r--r--drivers/clk/qcom/gcc-qcm2290.c124
-rw-r--r--drivers/clk/qcom/gcc-qcs404.c847
-rw-r--r--drivers/clk/qcom/gcc-qcs615.c3034
-rw-r--r--drivers/clk/qcom/gcc-qcs8300.c3640
-rw-r--r--drivers/clk/qcom/gcc-qdu1000.c2705
-rw-r--r--drivers/clk/qcom/gcc-sa8775p.c4716
-rw-r--r--drivers/clk/qcom/gcc-sar2130p.c2366
-rw-r--r--drivers/clk/qcom/gcc-sc7180.c48
-rw-r--r--drivers/clk/qcom/gcc-sc7280.c54
-rw-r--r--drivers/clk/qcom/gcc-sc8180x.c509
-rw-r--r--drivers/clk/qcom/gcc-sc8280xp.c271
-rw-r--r--drivers/clk/qcom/gcc-sdm660.c81
-rw-r--r--drivers/clk/qcom/gcc-sdm845.c445
-rw-r--r--drivers/clk/qcom/gcc-sdx55.c78
-rw-r--r--drivers/clk/qcom/gcc-sdx65.c126
-rw-r--r--drivers/clk/qcom/gcc-sdx75.c2967
-rw-r--r--drivers/clk/qcom/gcc-sm4450.c2888
-rw-r--r--drivers/clk/qcom/gcc-sm6115.c121
-rw-r--r--drivers/clk/qcom/gcc-sm6125.c4
-rw-r--r--drivers/clk/qcom/gcc-sm6350.c43
-rw-r--r--drivers/clk/qcom/gcc-sm6375.c3919
-rw-r--r--drivers/clk/qcom/gcc-sm7150.c3044
-rw-r--r--drivers/clk/qcom/gcc-sm8150.c441
-rw-r--r--drivers/clk/qcom/gcc-sm8250.c48
-rw-r--r--drivers/clk/qcom/gcc-sm8350.c88
-rw-r--r--drivers/clk/qcom/gcc-sm8450.c496
-rw-r--r--drivers/clk/qcom/gcc-sm8550.c3391
-rw-r--r--drivers/clk/qcom/gcc-sm8650.c3851
-rw-r--r--drivers/clk/qcom/gcc-sm8750.c3276
-rw-r--r--drivers/clk/qcom/gcc-x1e80100.c7449
-rw-r--r--drivers/clk/qcom/gdsc.c236
-rw-r--r--drivers/clk/qcom/gdsc.h9
-rw-r--r--drivers/clk/qcom/gpucc-milos.c562
-rw-r--r--drivers/clk/qcom/gpucc-msm8998.c18
-rw-r--r--drivers/clk/qcom/gpucc-qcm2290.c423
-rw-r--r--drivers/clk/qcom/gpucc-qcs615.c531
-rw-r--r--drivers/clk/qcom/gpucc-sa8775p.c663
-rw-r--r--drivers/clk/qcom/gpucc-sar2130p.c503
-rw-r--r--drivers/clk/qcom/gpucc-sc7180.c28
-rw-r--r--drivers/clk/qcom/gpucc-sc7280.c32
-rw-r--r--drivers/clk/qcom/gpucc-sc8280xp.c475
-rw-r--r--drivers/clk/qcom/gpucc-sdm660.c10
-rw-r--r--drivers/clk/qcom/gpucc-sdm845.c22
-rw-r--r--drivers/clk/qcom/gpucc-sm4450.c804
-rw-r--r--drivers/clk/qcom/gpucc-sm6115.c504
-rw-r--r--drivers/clk/qcom/gpucc-sm6125.c425
-rw-r--r--drivers/clk/qcom/gpucc-sm6350.c31
-rw-r--r--drivers/clk/qcom/gpucc-sm6375.c474
-rw-r--r--drivers/clk/qcom/gpucc-sm8150.c21
-rw-r--r--drivers/clk/qcom/gpucc-sm8250.c19
-rw-r--r--drivers/clk/qcom/gpucc-sm8350.c25
-rw-r--r--drivers/clk/qcom/gpucc-sm8450.c807
-rw-r--r--drivers/clk/qcom/gpucc-sm8550.c596
-rw-r--r--drivers/clk/qcom/gpucc-sm8650.c663
-rw-r--r--drivers/clk/qcom/gpucc-x1e80100.c656
-rw-r--r--drivers/clk/qcom/gpucc-x1p42100.c587
-rw-r--r--drivers/clk/qcom/hfpll.c68
-rw-r--r--drivers/clk/qcom/ipq-cmn-pll.c468
-rw-r--r--drivers/clk/qcom/kpss-xcc.c39
-rw-r--r--drivers/clk/qcom/krait-cc.c249
-rw-r--r--drivers/clk/qcom/lcc-ipq806x.c95
-rw-r--r--drivers/clk/qcom/lcc-mdm9615.c572
-rw-r--r--drivers/clk/qcom/lcc-msm8960.c233
-rw-r--r--drivers/clk/qcom/lpass-gfm-sm8250.c3
-rw-r--r--drivers/clk/qcom/lpassaudiocc-sc7280.c145
-rw-r--r--drivers/clk/qcom/lpasscc-sc7280.c80
-rw-r--r--drivers/clk/qcom/lpasscc-sc8280xp.c89
-rw-r--r--drivers/clk/qcom/lpasscc-sdm845.c1
-rw-r--r--drivers/clk/qcom/lpasscc-sm6115.c85
-rw-r--r--drivers/clk/qcom/lpasscorecc-sc7180.c70
-rw-r--r--drivers/clk/qcom/lpasscorecc-sc7280.c37
-rw-r--r--drivers/clk/qcom/mmcc-apq8084.c1243
-rw-r--r--drivers/clk/qcom/mmcc-msm8960.c559
-rw-r--r--drivers/clk/qcom/mmcc-msm8974.c839
-rw-r--r--drivers/clk/qcom/mmcc-msm8994.c18
-rw-r--r--drivers/clk/qcom/mmcc-msm8996.c21
-rw-r--r--drivers/clk/qcom/mmcc-msm8998.c81
-rw-r--r--drivers/clk/qcom/mmcc-sdm660.c23
-rw-r--r--drivers/clk/qcom/mss-sc7180.c129
-rw-r--r--drivers/clk/qcom/nsscc-ipq5424.c1340
-rw-r--r--drivers/clk/qcom/nsscc-ipq9574.c3110
-rw-r--r--drivers/clk/qcom/nsscc-qca8k.c2221
-rw-r--r--drivers/clk/qcom/q6sstop-qcs404.c15
-rw-r--r--drivers/clk/qcom/reset.c34
-rw-r--r--drivers/clk/qcom/reset.h2
-rw-r--r--drivers/clk/qcom/tcsrcc-glymur.c313
-rw-r--r--drivers/clk/qcom/tcsrcc-sm8550.c207
-rw-r--r--drivers/clk/qcom/tcsrcc-sm8650.c190
-rw-r--r--drivers/clk/qcom/tcsrcc-sm8750.c141
-rw-r--r--drivers/clk/qcom/tcsrcc-x1e80100.c289
-rw-r--r--drivers/clk/qcom/turingcc-qcs404.c13
-rw-r--r--drivers/clk/qcom/videocc-milos.c403
-rw-r--r--drivers/clk/qcom/videocc-qcs615.c338
-rw-r--r--drivers/clk/qcom/videocc-sa8775p.c584
-rw-r--r--drivers/clk/qcom/videocc-sc7180.c16
-rw-r--r--drivers/clk/qcom/videocc-sc7280.c23
-rw-r--r--drivers/clk/qcom/videocc-sdm845.c19
-rw-r--r--drivers/clk/qcom/videocc-sm6350.c355
-rw-r--r--drivers/clk/qcom/videocc-sm7150.c357
-rw-r--r--drivers/clk/qcom/videocc-sm8150.c45
-rw-r--r--drivers/clk/qcom/videocc-sm8250.c39
-rw-r--r--drivers/clk/qcom/videocc-sm8350.c588
-rw-r--r--drivers/clk/qcom/videocc-sm8450.c483
-rw-r--r--drivers/clk/qcom/videocc-sm8550.c629
-rw-r--r--drivers/clk/qcom/videocc-sm8750.c463
-rw-r--r--drivers/clk/ralink/Kconfig7
-rw-r--r--drivers/clk/ralink/Makefile1
-rw-r--r--drivers/clk/ralink/clk-mt7621.c13
-rw-r--r--drivers/clk/ralink/clk-mtmips.c1132
-rw-r--r--drivers/clk/renesas/Kconfig50
-rw-r--r--drivers/clk/renesas/Makefile9
-rw-r--r--drivers/clk/renesas/clk-div6.c6
-rw-r--r--drivers/clk/renesas/clk-emev2.c3
-rw-r--r--drivers/clk/renesas/clk-mstp.c56
-rw-r--r--drivers/clk/renesas/clk-r8a73a4.c3
-rw-r--r--drivers/clk/renesas/clk-r8a7740.c27
-rw-r--r--drivers/clk/renesas/clk-r8a7778.c1
-rw-r--r--drivers/clk/renesas/clk-sh73a0.c2
-rw-r--r--drivers/clk/renesas/clk-vbattb.c205
-rw-r--r--drivers/clk/renesas/r7s9210-cpg-mssr.c17
-rw-r--r--drivers/clk/renesas/r8a774a1-cpg-mssr.c3
-rw-r--r--drivers/clk/renesas/r8a774b1-cpg-mssr.c3
-rw-r--r--drivers/clk/renesas/r8a774c0-cpg-mssr.c1
-rw-r--r--drivers/clk/renesas/r8a774e1-cpg-mssr.c4
-rw-r--r--drivers/clk/renesas/r8a7795-cpg-mssr.c133
-rw-r--r--drivers/clk/renesas/r8a7796-cpg-mssr.c3
-rw-r--r--drivers/clk/renesas/r8a77965-cpg-mssr.c3
-rw-r--r--drivers/clk/renesas/r8a77970-cpg-mssr.c14
-rw-r--r--drivers/clk/renesas/r8a77980-cpg-mssr.c18
-rw-r--r--drivers/clk/renesas/r8a77990-cpg-mssr.c1
-rw-r--r--drivers/clk/renesas/r8a77995-cpg-mssr.c3
-rw-r--r--drivers/clk/renesas/r8a779a0-cpg-mssr.c60
-rw-r--r--drivers/clk/renesas/r8a779f0-cpg-mssr.c69
-rw-r--r--drivers/clk/renesas/r8a779g0-cpg-mssr.c139
-rw-r--r--drivers/clk/renesas/r8a779h0-cpg-mssr.c307
-rw-r--r--drivers/clk/renesas/r9a06g032-clocks.c822
-rw-r--r--drivers/clk/renesas/r9a07g043-cpg.c206
-rw-r--r--drivers/clk/renesas/r9a07g044-cpg.c262
-rw-r--r--drivers/clk/renesas/r9a08g045-cpg.c393
-rw-r--r--drivers/clk/renesas/r9a09g011-cpg.c113
-rw-r--r--drivers/clk/renesas/r9a09g047-cpg.c532
-rw-r--r--drivers/clk/renesas/r9a09g056-cpg.c476
-rw-r--r--drivers/clk/renesas/r9a09g057-cpg.c567
-rw-r--r--drivers/clk/renesas/r9a09g077-cpg.c320
-rw-r--r--drivers/clk/renesas/rcar-cpg-lib.c20
-rw-r--r--drivers/clk/renesas/rcar-gen2-cpg.c9
-rw-r--r--drivers/clk/renesas/rcar-gen2-cpg.h3
-rw-r--r--drivers/clk/renesas/rcar-gen3-cpg.c76
-rw-r--r--drivers/clk/renesas/rcar-gen3-cpg.h4
-rw-r--r--drivers/clk/renesas/rcar-gen4-cpg.c306
-rw-r--r--drivers/clk/renesas/rcar-gen4-cpg.h38
-rw-r--r--drivers/clk/renesas/rcar-usb2-clock-sel.c5
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.c550
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.h47
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.c1081
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.h78
-rw-r--r--drivers/clk/renesas/rzv2h-cpg.c1693
-rw-r--r--drivers/clk/renesas/rzv2h-cpg.h368
-rw-r--r--drivers/clk/rockchip/Kconfig50
-rw-r--r--drivers/clk/rockchip/Makefile9
-rw-r--r--drivers/clk/rockchip/clk-cpu.c245
-rw-r--r--drivers/clk/rockchip/clk-ddr.c13
-rw-r--r--drivers/clk/rockchip/clk-gate-grf.c105
-rw-r--r--drivers/clk/rockchip/clk-half-divider.c12
-rw-r--r--drivers/clk/rockchip/clk-mmc-phase.c29
-rw-r--r--drivers/clk/rockchip/clk-pll.c253
-rw-r--r--drivers/clk/rockchip/clk-px30.c10
-rw-r--r--drivers/clk/rockchip/clk-rk3036.c16
-rw-r--r--drivers/clk/rockchip/clk-rk3128.c48
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c22
-rw-r--r--drivers/clk/rockchip/clk-rk3228.c7
-rw-r--r--drivers/clk/rockchip/clk-rk3288.c19
-rw-r--r--drivers/clk/rockchip/clk-rk3308.c5
-rw-r--r--drivers/clk/rockchip/clk-rk3328.c13
-rw-r--r--drivers/clk/rockchip/clk-rk3368.c7
-rw-r--r--drivers/clk/rockchip/clk-rk3399.c27
-rw-r--r--drivers/clk/rockchip/clk-rk3506.c869
-rw-r--r--drivers/clk/rockchip/clk-rk3528.c1187
-rw-r--r--drivers/clk/rockchip/clk-rk3562.c1101
-rw-r--r--drivers/clk/rockchip/clk-rk3568.c25
-rw-r--r--drivers/clk/rockchip/clk-rk3576.c1860
-rw-r--r--drivers/clk/rockchip/clk-rk3588.c2533
-rw-r--r--drivers/clk/rockchip/clk-rv1126.c1197
-rw-r--r--drivers/clk/rockchip/clk-rv1126b.c1117
-rw-r--r--drivers/clk/rockchip/clk.c216
-rw-r--r--drivers/clk/rockchip/clk.h463
-rw-r--r--drivers/clk/rockchip/gate-link.c85
-rw-r--r--drivers/clk/rockchip/rst-rk3506.c226
-rw-r--r--drivers/clk/rockchip/rst-rk3528.c306
-rw-r--r--drivers/clk/rockchip/rst-rk3562.c429
-rw-r--r--drivers/clk/rockchip/rst-rk3576.c651
-rw-r--r--drivers/clk/rockchip/rst-rk3588.c858
-rw-r--r--drivers/clk/rockchip/rst-rv1126b.c443
-rw-r--r--drivers/clk/rockchip/softrst.c34
-rw-r--r--drivers/clk/samsung/Kconfig37
-rw-r--r--drivers/clk/samsung/Makefile12
-rw-r--r--drivers/clk/samsung/clk-acpm.c185
-rw-r--r--drivers/clk/samsung/clk-artpec8.c1044
-rw-r--r--drivers/clk/samsung/clk-cpu.c562
-rw-r--r--drivers/clk/samsung/clk-cpu.h61
-rw-r--r--drivers/clk/samsung/clk-exynos-arm64.c284
-rw-r--r--drivers/clk/samsung/clk-exynos-arm64.h3
-rw-r--r--drivers/clk/samsung/clk-exynos-audss.c10
-rw-r--r--drivers/clk/samsung/clk-exynos-clkout.c22
-rw-r--r--drivers/clk/samsung/clk-exynos2200.c3928
-rw-r--r--drivers/clk/samsung/clk-exynos3250.c19
-rw-r--r--drivers/clk/samsung/clk-exynos4.c151
-rw-r--r--drivers/clk/samsung/clk-exynos4412-isp.c10
-rw-r--r--drivers/clk/samsung/clk-exynos5-subcmu.c7
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c16
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c48
-rw-r--r--drivers/clk/samsung/clk-exynos5410.c9
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c31
-rw-r--r--drivers/clk/samsung/clk-exynos5433.c236
-rw-r--r--drivers/clk/samsung/clk-exynos7.c3
-rw-r--r--drivers/clk/samsung/clk-exynos7870.c1829
-rw-r--r--drivers/clk/samsung/clk-exynos7885.c289
-rw-r--r--drivers/clk/samsung/clk-exynos850.c1337
-rw-r--r--drivers/clk/samsung/clk-exynos8895.c2803
-rw-r--r--drivers/clk/samsung/clk-exynos990.c2717
-rw-r--r--drivers/clk/samsung/clk-exynosautov9.c521
-rw-r--r--drivers/clk/samsung/clk-exynosautov920.c1961
-rw-r--r--drivers/clk/samsung/clk-fsd.c53
-rw-r--r--drivers/clk/samsung/clk-gs101.c4420
-rw-r--r--drivers/clk/samsung/clk-pll.c488
-rw-r--r--drivers/clk/samsung/clk-pll.h35
-rw-r--r--drivers/clk/samsung/clk-s3c2410-dclk.c440
-rw-r--r--drivers/clk/samsung/clk-s3c2410.c446
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c254
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c438
-rw-r--r--drivers/clk/samsung/clk-s3c64xx.c7
-rw-r--r--drivers/clk/samsung/clk-s5pv210-audss.c15
-rw-r--r--drivers/clk/samsung/clk-s5pv210.c7
-rw-r--r--drivers/clk/samsung/clk.c93
-rw-r--r--drivers/clk/samsung/clk.h186
-rw-r--r--drivers/clk/sifive/Kconfig6
-rw-r--r--drivers/clk/sifive/fu540-prci.h2
-rw-r--r--drivers/clk/sifive/fu740-prci.h2
-rw-r--r--drivers/clk/sifive/sifive-prci.c36
-rw-r--r--drivers/clk/sifive/sifive-prci.h4
-rw-r--r--drivers/clk/socfpga/Kconfig2
-rw-r--r--drivers/clk/socfpga/Makefile2
-rw-r--r--drivers/clk/socfpga/clk-agilex.c19
-rw-r--r--drivers/clk/socfpga/clk-agilex5.c561
-rw-r--r--drivers/clk/socfpga/clk-gate-a10.c94
-rw-r--r--drivers/clk/socfpga/clk-gate-s10.c53
-rw-r--r--drivers/clk/socfpga/clk-gate.c105
-rw-r--r--drivers/clk/socfpga/clk-periph-a10.c22
-rw-r--r--drivers/clk/socfpga/clk-periph-s10.c41
-rw-r--r--drivers/clk/socfpga/clk-periph.c26
-rw-r--r--drivers/clk/socfpga/clk-pll-a10.c32
-rw-r--r--drivers/clk/socfpga/clk-pll-s10.c42
-rw-r--r--drivers/clk/socfpga/clk-pll.c36
-rw-r--r--drivers/clk/socfpga/clk-s10.c9
-rw-r--r--drivers/clk/socfpga/clk.h1
-rw-r--r--drivers/clk/socfpga/stratix10-clk.h47
-rw-r--r--drivers/clk/sophgo/Kconfig58
-rw-r--r--drivers/clk/sophgo/Makefile13
-rw-r--r--drivers/clk/sophgo/clk-cv1800.c1540
-rw-r--r--drivers/clk/sophgo/clk-cv1800.h123
-rw-r--r--drivers/clk/sophgo/clk-cv18xx-common.c66
-rw-r--r--drivers/clk/sophgo/clk-cv18xx-common.h81
-rw-r--r--drivers/clk/sophgo/clk-cv18xx-ip.c889
-rw-r--r--drivers/clk/sophgo/clk-cv18xx-ip.h261
-rw-r--r--drivers/clk/sophgo/clk-cv18xx-pll.c419
-rw-r--r--drivers/clk/sophgo/clk-cv18xx-pll.h118
-rw-r--r--drivers/clk/sophgo/clk-sg2042-clkgen.c1153
-rw-r--r--drivers/clk/sophgo/clk-sg2042-pll.c559
-rw-r--r--drivers/clk/sophgo/clk-sg2042-rpgate.c291
-rw-r--r--drivers/clk/sophgo/clk-sg2042.h18
-rw-r--r--drivers/clk/sophgo/clk-sg2044-pll.c628
-rw-r--r--drivers/clk/sophgo/clk-sg2044.c1812
-rw-r--r--drivers/clk/spacemit/Kconfig19
-rw-r--r--drivers/clk/spacemit/Makefile5
-rw-r--r--drivers/clk/spacemit/ccu-k1.c1209
-rw-r--r--drivers/clk/spacemit/ccu_common.h48
-rw-r--r--drivers/clk/spacemit/ccu_ddn.c86
-rw-r--r--drivers/clk/spacemit/ccu_ddn.h50
-rw-r--r--drivers/clk/spacemit/ccu_mix.c270
-rw-r--r--drivers/clk/spacemit/ccu_mix.h223
-rw-r--r--drivers/clk/spacemit/ccu_pll.c159
-rw-r--r--drivers/clk/spacemit/ccu_pll.h86
-rw-r--r--drivers/clk/spear/clk-aux-synth.c12
-rw-r--r--drivers/clk/spear/clk-frac-synth.c12
-rw-r--r--drivers/clk/spear/clk-gpt-synth.c12
-rw-r--r--drivers/clk/spear/clk-vco-pll.c23
-rw-r--r--drivers/clk/spear/spear1310_clock.c1
-rw-r--r--drivers/clk/spear/spear1340_clock.c3
-rw-r--r--drivers/clk/spear/spear3xx_clock.c3
-rw-r--r--drivers/clk/spear/spear6xx_clock.c9
-rw-r--r--drivers/clk/sprd/Kconfig8
-rw-r--r--drivers/clk/sprd/Makefile1
-rw-r--r--drivers/clk/sprd/common.c23
-rw-r--r--drivers/clk/sprd/composite.c9
-rw-r--r--drivers/clk/sprd/composite.h36
-rw-r--r--drivers/clk/sprd/div.c29
-rw-r--r--drivers/clk/sprd/div.h22
-rw-r--r--drivers/clk/sprd/gate.h2
-rw-r--r--drivers/clk/sprd/pll.c8
-rw-r--r--drivers/clk/sprd/sc9860-clk.c10
-rw-r--r--drivers/clk/sprd/ums512-clk.c2202
-rw-r--r--drivers/clk/st/clk-flexgen.c95
-rw-r--r--drivers/clk/st/clkgen-fsyn.c43
-rw-r--r--drivers/clk/st/clkgen-mux.c5
-rw-r--r--drivers/clk/st/clkgen-pll.c38
-rw-r--r--drivers/clk/starfive/Kconfig63
-rw-r--r--drivers/clk/starfive/Makefile10
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7100-audio.c87
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7100.c716
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7100.h112
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110-aon.c144
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110-isp.c220
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110-pll.c507
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110-stg.c163
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110-sys.c538
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110-vout.c227
-rw-r--r--drivers/clk/starfive/clk-starfive-jh7110.h17
-rw-r--r--drivers/clk/starfive/clk-starfive-jh71x0.c339
-rw-r--r--drivers/clk/starfive/clk-starfive-jh71x0.h127
-rw-r--r--drivers/clk/stm32/Kconfig43
-rw-r--r--drivers/clk/stm32/Makefile3
-rw-r--r--drivers/clk/stm32/clk-stm32-core.c79
-rw-r--r--drivers/clk/stm32/clk-stm32-core.h7
-rw-r--r--drivers/clk/stm32/clk-stm32mp1.c (renamed from drivers/clk/clk-stm32mp1.c)148
-rw-r--r--drivers/clk/stm32/clk-stm32mp13.c85
-rw-r--r--drivers/clk/stm32/clk-stm32mp21.c1586
-rw-r--r--drivers/clk/stm32/clk-stm32mp25.c2001
-rw-r--r--drivers/clk/stm32/reset-stm32.c73
-rw-r--r--drivers/clk/stm32/reset-stm32.h15
-rw-r--r--drivers/clk/stm32/stm32mp21_rcc.h651
-rw-r--r--drivers/clk/stm32/stm32mp25_rcc.h712
-rw-r--r--drivers/clk/sunxi-ng/Kconfig84
-rw-r--r--drivers/clk/sunxi-ng/Makefile6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun20i-d1.c73
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun20i-d1.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun4i-a10.c8
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-a100.c12
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-a64.c66
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-a64.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c12
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h6.c33
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h616.c144
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h616.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun55i-a523-mcu.c469
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c249
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun55i-a523-r.h14
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun55i-a523.c1701
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun5i.c2
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun6i-a31.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun6i-rtc.c19
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a23.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a33.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a83t.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-de2.c67
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-h3.c25
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-r.c12
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-r40.c10
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-v3s.c46
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-v3s.h6
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c25
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c15
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80.c6
-rw-r--r--drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c17
-rw-r--r--drivers/clk/sunxi-ng/ccu-suniv-f1c100s.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu_common.c46
-rw-r--r--drivers/clk/sunxi-ng/ccu_common.h16
-rw-r--r--drivers/clk/sunxi-ng/ccu_div.c4
-rw-r--r--drivers/clk/sunxi-ng/ccu_div.h48
-rw-r--r--drivers/clk/sunxi-ng/ccu_frac.c12
-rw-r--r--drivers/clk/sunxi-ng/ccu_gate.c26
-rw-r--r--drivers/clk/sunxi-ng/ccu_mmc_timing.c10
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.c64
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.h79
-rw-r--r--drivers/clk/sunxi-ng/ccu_mult.c2
-rw-r--r--drivers/clk/sunxi-ng/ccu_mux.c29
-rw-r--r--drivers/clk/sunxi-ng/ccu_mux.h38
-rw-r--r--drivers/clk/sunxi-ng/ccu_nk.c23
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkm.c83
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkm.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkmp.c33
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.c59
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.h48
-rw-r--r--drivers/clk/sunxi-ng/ccu_phase.c2
-rw-r--r--drivers/clk/sunxi-ng/ccu_reset.c2
-rw-r--r--drivers/clk/sunxi-ng/ccu_reset.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu_sdm.c12
-rw-r--r--drivers/clk/sunxi/Kconfig10
-rw-r--r--drivers/clk/sunxi/clk-a20-gmac.c21
-rw-r--r--drivers/clk/sunxi/clk-simple-gates.c4
-rw-r--r--drivers/clk/sunxi/clk-sun6i-apb0-gates.c1
-rw-r--r--drivers/clk/sunxi/clk-sun8i-bus-gates.c4
-rw-r--r--drivers/clk/sunxi/clk-sun9i-cpus.c7
-rw-r--r--drivers/clk/sunxi/clk-sun9i-mmc.c11
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c11
-rw-r--r--drivers/clk/sunxi/clk-usb.c9
-rw-r--r--drivers/clk/tegra/Kconfig2
-rw-r--r--drivers/clk/tegra/clk-audio-sync.c10
-rw-r--r--drivers/clk/tegra/clk-bpmp.c46
-rw-r--r--drivers/clk/tegra/clk-device.c2
-rw-r--r--drivers/clk/tegra/clk-dfll.c7
-rw-r--r--drivers/clk/tegra/clk-divider.c28
-rw-r--r--drivers/clk/tegra/clk-periph.c13
-rw-r--r--drivers/clk/tegra/clk-pll.c52
-rw-r--r--drivers/clk/tegra/clk-super.c9
-rw-r--r--drivers/clk/tegra/clk-tegra-super-cclk.c15
-rw-r--r--drivers/clk/tegra/clk-tegra114.c32
-rw-r--r--drivers/clk/tegra/clk-tegra124-dfll-fcpu.c175
-rw-r--r--drivers/clk/tegra/clk-tegra124-emc.c2
-rw-r--r--drivers/clk/tegra/clk-tegra124.c2
-rw-r--r--drivers/clk/tegra/clk-tegra20.c31
-rw-r--r--drivers/clk/tegra/clk-tegra210-emc.c24
-rw-r--r--drivers/clk/tegra/clk-tegra210.c16
-rw-r--r--drivers/clk/tegra/clk-tegra30.c4
-rw-r--r--drivers/clk/tegra/clk.c8
-rw-r--r--drivers/clk/tegra/clk.h3
-rw-r--r--drivers/clk/thead/Kconfig13
-rw-r--r--drivers/clk/thead/Makefile2
-rw-r--r--drivers/clk/thead/clk-th1520-ap.c1308
-rw-r--r--drivers/clk/ti/adpll.c26
-rw-r--r--drivers/clk/ti/apll.c15
-rw-r--r--drivers/clk/ti/autoidle.c2
-rw-r--r--drivers/clk/ti/clk-33xx.c2
-rw-r--r--drivers/clk/ti/clk-43xx.c2
-rw-r--r--drivers/clk/ti/clk-44xx.c5
-rw-r--r--drivers/clk/ti/clk-54xx.c4
-rw-r--r--drivers/clk/ti/clk-dra7-atl.c38
-rw-r--r--drivers/clk/ti/clk.c140
-rw-r--r--drivers/clk/ti/clkctrl.c26
-rw-r--r--drivers/clk/ti/clkt_dpll.c36
-rw-r--r--drivers/clk/ti/clock.h17
-rw-r--r--drivers/clk/ti/composite.c8
-rw-r--r--drivers/clk/ti/divider.c25
-rw-r--r--drivers/clk/ti/dpll.c24
-rw-r--r--drivers/clk/ti/dpll3xxx.c11
-rw-r--r--drivers/clk/ti/dpll44xx.c89
-rw-r--r--drivers/clk/ti/fapll.c48
-rw-r--r--drivers/clk/ti/fixed-factor.c2
-rw-r--r--drivers/clk/ti/gate.c15
-rw-r--r--drivers/clk/ti/interface.c11
-rw-r--r--drivers/clk/ti/mux.c16
-rw-r--r--drivers/clk/uniphier/clk-uniphier-core.c13
-rw-r--r--drivers/clk/ux500/clk-prcmu.c15
-rw-r--r--drivers/clk/ux500/clk-sysctrl.c1
-rw-r--r--drivers/clk/versatile/clk-icst.c74
-rw-r--r--drivers/clk/versatile/clk-sp810.c3
-rw-r--r--drivers/clk/versatile/clk-vexpress-osc.c17
-rw-r--r--drivers/clk/visconti/clkc-tmpv770x.c79
-rw-r--r--drivers/clk/visconti/pll-tmpv770x.c5
-rw-r--r--drivers/clk/visconti/pll.c32
-rw-r--r--drivers/clk/visconti/pll.h5
-rw-r--r--drivers/clk/x86/Kconfig5
-rw-r--r--drivers/clk/x86/clk-cgu-pll.c23
-rw-r--r--drivers/clk/x86/clk-cgu.c141
-rw-r--r--drivers/clk/x86/clk-cgu.h46
-rw-r--r--drivers/clk/x86/clk-fch.c5
-rw-r--r--drivers/clk/x86/clk-lgm.c18
-rw-r--r--drivers/clk/x86/clk-pmc-atom.c16
-rw-r--r--drivers/clk/xilinx/Kconfig14
-rw-r--r--drivers/clk/xilinx/Makefile1
-rw-r--r--drivers/clk/xilinx/clk-xlnx-clock-wizard.c1253
-rw-r--r--drivers/clk/xilinx/xlnx_vcu.c58
-rw-r--r--drivers/clk/zynq/clkc.c8
-rw-r--r--drivers/clk/zynq/pll.c12
-rw-r--r--drivers/clk/zynqmp/clk-mux-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/clkc.c22
-rw-r--r--drivers/clk/zynqmp/divider.c80
-rw-r--r--drivers/clk/zynqmp/pll.c41
-rw-r--r--drivers/clocksource/Kconfig108
-rw-r--r--drivers/clocksource/Makefile12
-rw-r--r--drivers/clocksource/acpi_pm.c38
-rw-r--r--drivers/clocksource/arm_arch_timer.c772
-rw-r--r--drivers/clocksource/arm_arch_timer_mmio.c442
-rw-r--r--drivers/clocksource/arm_global_timer.c78
-rw-r--r--drivers/clocksource/asm9260_timer.c1
-rw-r--r--drivers/clocksource/bcm2835_timer.c2
-rw-r--r--drivers/clocksource/clps711x-timer.c23
-rw-r--r--drivers/clocksource/dw_apb_timer.c39
-rw-r--r--drivers/clocksource/em_sti.c8
-rw-r--r--drivers/clocksource/exynos_mct.c84
-rw-r--r--drivers/clocksource/hyperv_timer.c248
-rw-r--r--drivers/clocksource/i8253.c49
-rw-r--r--drivers/clocksource/ingenic-ost.c17
-rw-r--r--drivers/clocksource/ingenic-sysost.c27
-rw-r--r--drivers/clocksource/ingenic-timer.c13
-rw-r--r--drivers/clocksource/jcore-pit.c22
-rw-r--r--drivers/clocksource/mips-gic-timer.c65
-rw-r--r--drivers/clocksource/nomadik-mtu.c2
-rw-r--r--drivers/clocksource/renesas-ostm.c5
-rw-r--r--drivers/clocksource/samsung_pwm_timer.c4
-rw-r--r--drivers/clocksource/scx200_hrt.c1
-rw-r--r--drivers/clocksource/sh_cmt.c216
-rw-r--r--drivers/clocksource/sh_mtu2.c8
-rw-r--r--drivers/clocksource/sh_tmu.c8
-rw-r--r--drivers/clocksource/timer-armada-370-xp.c13
-rw-r--r--drivers/clocksource/timer-atmel-tcb.c1
-rw-r--r--drivers/clocksource/timer-cadence-ttc.c48
-rw-r--r--drivers/clocksource/timer-clint.c67
-rw-r--r--drivers/clocksource/timer-cs5535.c1
-rw-r--r--drivers/clocksource/timer-davinci.c30
-rw-r--r--drivers/clocksource/timer-econet-en751221.c216
-rw-r--r--drivers/clocksource/timer-ep93xx.c189
-rw-r--r--drivers/clocksource/timer-gxp.c10
-rw-r--r--drivers/clocksource/timer-imx-gpt.c65
-rw-r--r--drivers/clocksource/timer-imx-sysctr.c115
-rw-r--r--drivers/clocksource/timer-imx-tpm.c16
-rw-r--r--drivers/clocksource/timer-integrator-ap.c2
-rw-r--r--drivers/clocksource/timer-loongson1-pwm.c236
-rw-r--r--drivers/clocksource/timer-mediatek-cpux.c140
-rw-r--r--drivers/clocksource/timer-mediatek.c114
-rw-r--r--drivers/clocksource/timer-microchip-pit64b.c12
-rw-r--r--drivers/clocksource/timer-npcm7xx.c10
-rw-r--r--drivers/clocksource/timer-nxp-pit.c383
-rw-r--r--drivers/clocksource/timer-nxp-stm.c496
-rw-r--r--drivers/clocksource/timer-of.c17
-rw-r--r--drivers/clocksource/timer-of.h1
-rw-r--r--drivers/clocksource/timer-orion.c2
-rw-r--r--drivers/clocksource/timer-oxnas-rps.c288
-rw-r--r--drivers/clocksource/timer-qcom.c8
-rw-r--r--drivers/clocksource/timer-ralink.c157
-rw-r--r--drivers/clocksource/timer-rda.c9
-rw-r--r--drivers/clocksource/timer-realtek.c150
-rw-r--r--drivers/clocksource/timer-riscv.c146
-rw-r--r--drivers/clocksource/timer-rtl-otto.c303
-rw-r--r--drivers/clocksource/timer-sp804.c30
-rw-r--r--drivers/clocksource/timer-sprd.c24
-rw-r--r--drivers/clocksource/timer-stm32-lp.c111
-rw-r--r--drivers/clocksource/timer-stm32.c4
-rw-r--r--drivers/clocksource/timer-sun4i.c6
-rw-r--r--drivers/clocksource/timer-sun5i.c290
-rw-r--r--drivers/clocksource/timer-tegra.c1
-rw-r--r--drivers/clocksource/timer-tegra186.c124
-rw-r--r--drivers/clocksource/timer-ti-32k.c2
-rw-r--r--drivers/clocksource/timer-ti-dm-systimer.c75
-rw-r--r--drivers/clocksource/timer-ti-dm.c857
-rw-r--r--drivers/clocksource/timer-vf-pit.c194
-rw-r--r--drivers/comedi/Kconfig56
-rw-r--r--drivers/comedi/comedi_buf.c431
-rw-r--r--drivers/comedi/comedi_fops.c425
-rw-r--r--drivers/comedi/comedi_internal.h13
-rw-r--r--drivers/comedi/drivers.c180
-rw-r--r--drivers/comedi/drivers/8255.c20
-rw-r--r--drivers/comedi/drivers/8255_pci.c15
-rw-r--r--drivers/comedi/drivers/Makefile1
-rw-r--r--drivers/comedi/drivers/adl_pci7250.c220
-rw-r--r--drivers/comedi/drivers/adl_pci9111.c8
-rw-r--r--drivers/comedi/drivers/adl_pci9118.c12
-rw-r--r--drivers/comedi/drivers/adv_pci1710.c8
-rw-r--r--drivers/comedi/drivers/adv_pci1760.c2
-rw-r--r--drivers/comedi/drivers/adv_pci_dio.c14
-rw-r--r--drivers/comedi/drivers/aio_aio12_8.c10
-rw-r--r--drivers/comedi/drivers/aio_iiro_16.c3
-rw-r--r--drivers/comedi/drivers/amplc_dio200_common.c104
-rw-r--r--drivers/comedi/drivers/amplc_dio200_pci.c12
-rw-r--r--drivers/comedi/drivers/amplc_pc236_common.c2
-rw-r--r--drivers/comedi/drivers/amplc_pci224.c8
-rw-r--r--drivers/comedi/drivers/amplc_pci230.c10
-rw-r--r--drivers/comedi/drivers/c6xdigio.c46
-rw-r--r--drivers/comedi/drivers/cb_das16_cs.c8
-rw-r--r--drivers/comedi/drivers/cb_pcidas.c23
-rw-r--r--drivers/comedi/drivers/cb_pcidas64.c12
-rw-r--r--drivers/comedi/drivers/cb_pcidda.c2
-rw-r--r--drivers/comedi/drivers/cb_pcimdas.c12
-rw-r--r--drivers/comedi/drivers/cb_pcimdda.c2
-rw-r--r--drivers/comedi/drivers/comedi_8254.c234
-rw-r--r--drivers/comedi/drivers/comedi_8255.c124
-rw-r--r--drivers/comedi/drivers/comedi_bond.c4
-rw-r--r--drivers/comedi/drivers/comedi_test.c73
-rw-r--r--drivers/comedi/drivers/daqboard2000.c4
-rw-r--r--drivers/comedi/drivers/das08.c12
-rw-r--r--drivers/comedi/drivers/das16.c17
-rw-r--r--drivers/comedi/drivers/das16m1.c25
-rw-r--r--drivers/comedi/drivers/das1800.c8
-rw-r--r--drivers/comedi/drivers/das6402.c11
-rw-r--r--drivers/comedi/drivers/das800.c8
-rw-r--r--drivers/comedi/drivers/dmm32at.c3
-rw-r--r--drivers/comedi/drivers/jr3_pci.c5
-rw-r--r--drivers/comedi/drivers/me4000.c6
-rw-r--r--drivers/comedi/drivers/multiq3.c9
-rw-r--r--drivers/comedi/drivers/ni_670x.c2
-rw-r--r--drivers/comedi/drivers/ni_at_a2150.c8
-rw-r--r--drivers/comedi/drivers/ni_at_ao.c8
-rw-r--r--drivers/comedi/drivers/ni_atmio.c11
-rw-r--r--drivers/comedi/drivers/ni_atmio16d.c2
-rw-r--r--drivers/comedi/drivers/ni_daq_dio24.c2
-rw-r--r--drivers/comedi/drivers/ni_labpc_common.c51
-rw-r--r--drivers/comedi/drivers/ni_mio_common.c83
-rw-r--r--drivers/comedi/drivers/ni_pcidio.c2
-rw-r--r--drivers/comedi/drivers/ni_pcimio.c9
-rw-r--r--drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c5
-rw-r--r--drivers/comedi/drivers/ni_stc.h2
-rw-r--r--drivers/comedi/drivers/pcl711.c8
-rw-r--r--drivers/comedi/drivers/pcl724.c6
-rw-r--r--drivers/comedi/drivers/pcl726.c3
-rw-r--r--drivers/comedi/drivers/pcl812.c15
-rw-r--r--drivers/comedi/drivers/pcl816.c8
-rw-r--r--drivers/comedi/drivers/pcl818.c13
-rw-r--r--drivers/comedi/drivers/pcm3724.c2
-rw-r--r--drivers/comedi/drivers/rtd520.c6
-rw-r--r--drivers/comedi/drivers/usbduxsigma.c2
-rw-r--r--drivers/comedi/drivers/vmk80xx.c35
-rw-r--r--drivers/comedi/kcomedilib/kcomedilib_main.c120
-rw-r--r--drivers/connector/cn_proc.c111
-rw-r--r--drivers/connector/connector.c39
-rw-r--r--drivers/counter/104-quad-8.c914
-rw-r--r--drivers/counter/Kconfig126
-rw-r--r--drivers/counter/Makefile3
-rw-r--r--drivers/counter/counter-chrdev.c138
-rw-r--r--drivers/counter/counter-core.c25
-rw-r--r--drivers/counter/counter-sysfs.c312
-rw-r--r--drivers/counter/ftm-quaddec.c3
-rw-r--r--drivers/counter/i8254.c447
-rw-r--r--drivers/counter/intel-qep.c11
-rw-r--r--drivers/counter/interrupt-cnt.c29
-rw-r--r--drivers/counter/microchip-tcb-capture.c226
-rw-r--r--drivers/counter/rz-mtu3-cnt.c906
-rw-r--r--drivers/counter/stm32-lptimer-cnt.c27
-rw-r--r--drivers/counter/stm32-timer-cnt.c485
-rw-r--r--drivers/counter/ti-ecap-capture.c610
-rw-r--r--drivers/counter/ti-eqep.c174
-rw-r--r--drivers/cpufreq/Kconfig87
-rw-r--r--drivers/cpufreq/Kconfig.arm207
-rw-r--r--drivers/cpufreq/Kconfig.powerpc25
-rw-r--r--drivers/cpufreq/Kconfig.x8647
-rw-r--r--drivers/cpufreq/Makefile20
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c171
-rw-r--r--drivers/cpufreq/airoha-cpufreq.c153
-rw-r--r--drivers/cpufreq/amd-pstate-trace.h75
-rw-r--r--drivers/cpufreq/amd-pstate-ut.c298
-rw-r--r--drivers/cpufreq/amd-pstate.c1806
-rw-r--r--drivers/cpufreq/amd-pstate.h127
-rw-r--r--drivers/cpufreq/amd_freq_sensitivity.c4
-rw-r--r--drivers/cpufreq/apple-soc-cpufreq.c375
-rw-r--r--drivers/cpufreq/armada-37xx-cpufreq.c16
-rw-r--r--drivers/cpufreq/armada-8k-cpufreq.c27
-rw-r--r--drivers/cpufreq/bmips-cpufreq.c15
-rw-r--r--drivers/cpufreq/brcmstb-avs-cpufreq.c31
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c559
-rw-r--r--drivers/cpufreq/cpufreq-dt-platdev.c64
-rw-r--r--drivers/cpufreq/cpufreq-dt.c77
-rw-r--r--drivers/cpufreq/cpufreq-dt.h2
-rw-r--r--drivers/cpufreq/cpufreq-nforce2.c9
-rw-r--r--drivers/cpufreq/cpufreq.c832
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c25
-rw-r--r--drivers/cpufreq/cpufreq_governor.c49
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c29
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.h23
-rw-r--r--drivers/cpufreq/cpufreq_stats.c33
-rw-r--r--drivers/cpufreq/cpufreq_userspace.c77
-rw-r--r--drivers/cpufreq/davinci-cpufreq.c7
-rw-r--r--drivers/cpufreq/e_powersaver.c10
-rw-r--r--drivers/cpufreq/elanfreq.c2
-rw-r--r--drivers/cpufreq/freq_table.c65
-rw-r--r--drivers/cpufreq/highbank-cpufreq.c2
-rw-r--r--drivers/cpufreq/ia64-acpi-cpufreq.c353
-rw-r--r--drivers/cpufreq/imx-cpufreq-dt.c6
-rw-r--r--drivers/cpufreq/imx6q-cpufreq.c92
-rw-r--r--drivers/cpufreq/intel_pstate.c1472
-rw-r--r--drivers/cpufreq/kirkwood-cpufreq.c7
-rw-r--r--drivers/cpufreq/longhaul.c37
-rw-r--r--drivers/cpufreq/loongson1-cpufreq.c222
-rw-r--r--drivers/cpufreq/loongson2_cpufreq.c11
-rw-r--r--drivers/cpufreq/loongson3_cpufreq.c389
-rw-r--r--drivers/cpufreq/maple-cpufreq.c241
-rw-r--r--drivers/cpufreq/mediatek-cpufreq-hw.c184
-rw-r--r--drivers/cpufreq/mediatek-cpufreq.c223
-rw-r--r--drivers/cpufreq/mvebu-cpufreq.c2
-rw-r--r--drivers/cpufreq/omap-cpufreq.c11
-rw-r--r--drivers/cpufreq/p4-clockmod.c1
-rw-r--r--drivers/cpufreq/pasemi-cpufreq.c8
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c43
-rw-r--r--drivers/cpufreq/pmac32-cpufreq.c24
-rw-r--r--drivers/cpufreq/pmac64-cpufreq.c6
-rw-r--r--drivers/cpufreq/powernow-k6.c6
-rw-r--r--drivers/cpufreq/powernow-k7.c18
-rw-r--r--drivers/cpufreq/powernow-k8.c12
-rw-r--r--drivers/cpufreq/powernv-cpufreq.c32
-rw-r--r--drivers/cpufreq/powernv-trace.h44
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq.c173
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq.h33
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq_pervasive.c102
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq_pmi.c151
-rw-r--r--drivers/cpufreq/qcom-cpufreq-hw.c275
-rw-r--r--drivers/cpufreq/qcom-cpufreq-nvmem.c455
-rw-r--r--drivers/cpufreq/qoriq-cpufreq.c9
-rw-r--r--drivers/cpufreq/raspberrypi-cpufreq.c4
-rw-r--r--drivers/cpufreq/rcpufreq_dt.rs222
-rw-r--r--drivers/cpufreq/s3c2410-cpufreq.c155
-rw-r--r--drivers/cpufreq/s3c2412-cpufreq.c240
-rw-r--r--drivers/cpufreq/s3c2416-cpufreq.c492
-rw-r--r--drivers/cpufreq/s3c2440-cpufreq.c321
-rw-r--r--drivers/cpufreq/s3c24xx-cpufreq-debugfs.c163
-rw-r--r--drivers/cpufreq/s3c24xx-cpufreq.c648
-rw-r--r--drivers/cpufreq/s3c64xx-cpufreq.c11
-rw-r--r--drivers/cpufreq/s5pv210-cpufreq.c10
-rw-r--r--drivers/cpufreq/sa1100-cpufreq.c206
-rw-r--r--drivers/cpufreq/sa1110-cpufreq.c6
-rw-r--r--drivers/cpufreq/sc520_freq.c2
-rw-r--r--drivers/cpufreq/scmi-cpufreq.c203
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c32
-rw-r--r--drivers/cpufreq/sh-cpufreq.c11
-rw-r--r--drivers/cpufreq/sparc-us2e-cpufreq.c63
-rw-r--r--drivers/cpufreq/sparc-us3-cpufreq.c63
-rw-r--r--drivers/cpufreq/spear-cpufreq.c25
-rw-r--r--drivers/cpufreq/speedstep-centrino.c19
-rw-r--r--drivers/cpufreq/speedstep-ich.c1
-rw-r--r--drivers/cpufreq/speedstep-lib.c12
-rw-r--r--drivers/cpufreq/speedstep-lib.h10
-rw-r--r--drivers/cpufreq/speedstep-smi.c1
-rw-r--r--drivers/cpufreq/sti-cpufreq.c9
-rw-r--r--drivers/cpufreq/sun50i-cpufreq-nvmem.c292
-rw-r--r--drivers/cpufreq/tegra124-cpufreq.c70
-rw-r--r--drivers/cpufreq/tegra186-cpufreq.c210
-rw-r--r--drivers/cpufreq/tegra194-cpufreq.c375
-rw-r--r--drivers/cpufreq/tegra20-cpufreq.c4
-rw-r--r--drivers/cpufreq/ti-cpufreq.c179
-rw-r--r--drivers/cpufreq/vexpress-spc-cpufreq.c10
-rw-r--r--drivers/cpufreq/virtual-cpufreq.c332
-rw-r--r--drivers/cpuidle/Kconfig1
-rw-r--r--drivers/cpuidle/Kconfig.arm10
-rw-r--r--drivers/cpuidle/Makefile3
-rw-r--r--drivers/cpuidle/coupled.c15
-rw-r--r--drivers/cpuidle/cpuidle-arm.c12
-rw-r--r--drivers/cpuidle/cpuidle-big_little.c25
-rw-r--r--drivers/cpuidle/cpuidle-haltpoll.c12
-rw-r--r--drivers/cpuidle/cpuidle-kirkwood.c3
-rw-r--r--drivers/cpuidle/cpuidle-mvebu-v7.c15
-rw-r--r--drivers/cpuidle/cpuidle-powernv.c9
-rw-r--r--drivers/cpuidle/cpuidle-psci-domain.c81
-rw-r--r--drivers/cpuidle/cpuidle-psci.c167
-rw-r--r--drivers/cpuidle/cpuidle-psci.h24
-rw-r--r--drivers/cpuidle/cpuidle-pseries.c45
-rw-r--r--drivers/cpuidle/cpuidle-qcom-spm.c24
-rw-r--r--drivers/cpuidle/cpuidle-riscv-sbi.c143
-rw-r--r--drivers/cpuidle/cpuidle-tegra.c31
-rw-r--r--drivers/cpuidle/cpuidle.c121
-rw-r--r--drivers/cpuidle/cpuidle.h2
-rw-r--r--drivers/cpuidle/driver.c21
-rw-r--r--drivers/cpuidle/dt_idle_genpd.c28
-rw-r--r--drivers/cpuidle/dt_idle_genpd.h7
-rw-r--r--drivers/cpuidle/dt_idle_states.c32
-rw-r--r--drivers/cpuidle/governor.c15
-rw-r--r--drivers/cpuidle/governors/gov.h14
-rw-r--r--drivers/cpuidle/governors/haltpoll.c9
-rw-r--r--drivers/cpuidle/governors/ladder.c1
-rw-r--r--drivers/cpuidle/governors/menu.c384
-rw-r--r--drivers/cpuidle/governors/teo.c470
-rw-r--r--drivers/cpuidle/poll_state.c14
-rw-r--r--drivers/cpuidle/sysfs.c53
-rw-r--r--drivers/crypto/Kconfig264
-rw-r--r--drivers/crypto/Makefile22
-rw-r--r--drivers/crypto/allwinner/sun4i-ss/sun4i-ss-cipher.c2
-rw-r--r--drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c24
-rw-r--r--drivers/crypto/allwinner/sun4i-ss/sun4i-ss-hash.c4
-rw-r--r--drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h2
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c193
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c262
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c334
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c9
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-trng.c12
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h64
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-cipher.c44
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c179
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c192
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c13
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h19
-rw-r--r--drivers/crypto/amcc/crypto4xx_alg.c124
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.c153
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.h22
-rw-r--r--drivers/crypto/amlogic/amlogic-gxl-cipher.c23
-rw-r--r--drivers/crypto/amlogic/amlogic-gxl-core.c73
-rw-r--r--drivers/crypto/amlogic/amlogic-gxl.h9
-rw-r--r--drivers/crypto/aspeed/Kconfig57
-rw-r--r--drivers/crypto/aspeed/Makefile11
-rw-r--r--drivers/crypto/aspeed/aspeed-acry.c818
-rw-r--r--drivers/crypto/aspeed/aspeed-hace-crypto.c955
-rw-r--r--drivers/crypto/aspeed/aspeed-hace-hash.c826
-rw-r--r--drivers/crypto/aspeed/aspeed-hace.c280
-rw-r--r--drivers/crypto/aspeed/aspeed-hace.h274
-rw-r--r--drivers/crypto/atmel-aes.c282
-rw-r--r--drivers/crypto/atmel-ecc.c19
-rw-r--r--drivers/crypto/atmel-i2c.c40
-rw-r--r--drivers/crypto/atmel-i2c.h14
-rw-r--r--drivers/crypto/atmel-sha.c99
-rw-r--r--drivers/crypto/atmel-sha204a.c93
-rw-r--r--drivers/crypto/atmel-tdes.c254
-rw-r--r--drivers/crypto/axis/artpec6_crypto.c66
-rw-r--r--drivers/crypto/bcm/cipher.c204
-rw-r--r--drivers/crypto/bcm/cipher.h9
-rw-r--r--drivers/crypto/bcm/spu.c7
-rw-r--r--drivers/crypto/bcm/spu2.c5
-rw-r--r--drivers/crypto/caam/Kconfig11
-rw-r--r--drivers/crypto/caam/Makefile4
-rw-r--r--drivers/crypto/caam/blob_gen.c90
-rw-r--r--drivers/crypto/caam/caamalg.c638
-rw-r--r--drivers/crypto/caam/caamalg_desc.c87
-rw-r--r--drivers/crypto/caam/caamalg_desc.h13
-rw-r--r--drivers/crypto/caam/caamalg_qi.c100
-rw-r--r--drivers/crypto/caam/caamalg_qi2.c285
-rw-r--r--drivers/crypto/caam/caamalg_qi2.h12
-rw-r--r--drivers/crypto/caam/caamhash.c166
-rw-r--r--drivers/crypto/caam/caampkc.c123
-rw-r--r--drivers/crypto/caam/caampkc.h3
-rw-r--r--drivers/crypto/caam/caamprng.c12
-rw-r--r--drivers/crypto/caam/caamrng.c68
-rw-r--r--drivers/crypto/caam/ctrl.c520
-rw-r--r--drivers/crypto/caam/debugfs.c14
-rw-r--r--drivers/crypto/caam/debugfs.h9
-rw-r--r--drivers/crypto/caam/desc.h9
-rw-r--r--drivers/crypto/caam/desc_constr.h11
-rw-r--r--drivers/crypto/caam/dpseci-debugfs.c2
-rw-r--r--drivers/crypto/caam/intern.h32
-rw-r--r--drivers/crypto/caam/jr.c278
-rw-r--r--drivers/crypto/caam/key_gen.c2
-rw-r--r--drivers/crypto/caam/qi.c91
-rw-r--r--drivers/crypto/caam/qi.h12
-rw-r--r--drivers/crypto/caam/regs.h26
-rw-r--r--drivers/crypto/cavium/Makefile3
-rw-r--r--drivers/crypto/cavium/cpt/cpt_hw_types.h2
-rw-r--r--drivers/crypto/cavium/cpt/cptpf_main.c12
-rw-r--r--drivers/crypto/cavium/cpt/cptvf_algs.c44
-rw-r--r--drivers/crypto/cavium/cpt/cptvf_reqmanager.c4
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_aead.c16
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_hal.c2
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_lib.c2
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_main.c1
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_mbx.c3
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_skcipher.c27
-rw-r--r--drivers/crypto/cavium/zip/Makefile12
-rw-r--r--drivers/crypto/cavium/zip/common.h222
-rw-r--r--drivers/crypto/cavium/zip/zip_crypto.c319
-rw-r--r--drivers/crypto/cavium/zip/zip_crypto.h79
-rw-r--r--drivers/crypto/cavium/zip/zip_deflate.c200
-rw-r--r--drivers/crypto/cavium/zip/zip_deflate.h62
-rw-r--r--drivers/crypto/cavium/zip/zip_device.c202
-rw-r--r--drivers/crypto/cavium/zip/zip_device.h108
-rw-r--r--drivers/crypto/cavium/zip/zip_inflate.c223
-rw-r--r--drivers/crypto/cavium/zip/zip_inflate.h62
-rw-r--r--drivers/crypto/cavium/zip/zip_main.c651
-rw-r--r--drivers/crypto/cavium/zip/zip_main.h120
-rw-r--r--drivers/crypto/cavium/zip/zip_mem.c114
-rw-r--r--drivers/crypto/cavium/zip/zip_mem.h78
-rw-r--r--drivers/crypto/cavium/zip/zip_regs.h1347
-rw-r--r--drivers/crypto/ccp/Kconfig3
-rw-r--r--drivers/crypto/ccp/Makefile10
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-cmac.c21
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-galois.c12
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-xts.c20
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes.c62
-rw-r--r--drivers/crypto/ccp/ccp-crypto-des3.c35
-rw-r--r--drivers/crypto/ccp/ccp-crypto-main.c40
-rw-r--r--drivers/crypto/ccp/ccp-crypto-rsa.c18
-rw-r--r--drivers/crypto/ccp/ccp-crypto-sha.c26
-rw-r--r--drivers/crypto/ccp/ccp-debugfs.c3
-rw-r--r--drivers/crypto/ccp/ccp-dev.c2
-rw-r--r--drivers/crypto/ccp/ccp-dmaengine.c17
-rw-r--r--drivers/crypto/ccp/ccp-ops.c179
-rw-r--r--drivers/crypto/ccp/dbc.c259
-rw-r--r--drivers/crypto/ccp/dbc.h47
-rw-r--r--drivers/crypto/ccp/hsti.c138
-rw-r--r--drivers/crypto/ccp/hsti.h17
-rw-r--r--drivers/crypto/ccp/platform-access.c227
-rw-r--r--drivers/crypto/ccp/platform-access.h35
-rw-r--r--drivers/crypto/ccp/psp-dev.c173
-rw-r--r--drivers/crypto/ccp/psp-dev.h111
-rw-r--r--drivers/crypto/ccp/sev-dev-tio.c864
-rw-r--r--drivers/crypto/ccp/sev-dev-tio.h123
-rw-r--r--drivers/crypto/ccp/sev-dev-tsm.c405
-rw-r--r--drivers/crypto/ccp/sev-dev.c1927
-rw-r--r--drivers/crypto/ccp/sev-dev.h27
-rw-r--r--drivers/crypto/ccp/sfs.c311
-rw-r--r--drivers/crypto/ccp/sfs.h47
-rw-r--r--drivers/crypto/ccp/sp-dev.c14
-rw-r--r--drivers/crypto/ccp/sp-dev.h24
-rw-r--r--drivers/crypto/ccp/sp-pci.c241
-rw-r--r--drivers/crypto/ccp/sp-platform.c33
-rw-r--r--drivers/crypto/ccp/tee-dev.c63
-rw-r--r--drivers/crypto/ccp/tee-dev.h15
-rw-r--r--drivers/crypto/ccree/cc_aead.c76
-rw-r--r--drivers/crypto/ccree/cc_buffer_mgr.c80
-rw-r--r--drivers/crypto/ccree/cc_cipher.c59
-rw-r--r--drivers/crypto/ccree/cc_debugfs.c2
-rw-r--r--drivers/crypto/ccree/cc_driver.c19
-rw-r--r--drivers/crypto/ccree/cc_hash.c118
-rw-r--r--drivers/crypto/ccree/cc_pm.c1
-rw-r--r--drivers/crypto/chelsio/Kconfig8
-rw-r--r--drivers/crypto/chelsio/chcr_algo.c324
-rw-r--r--drivers/crypto/chelsio/chcr_core.h1
-rw-r--r--drivers/crypto/chelsio/chcr_crypto.h8
-rw-r--r--drivers/crypto/exynos-rng.c8
-rw-r--r--drivers/crypto/gemini/sl3516-ce-cipher.c26
-rw-r--r--drivers/crypto/gemini/sl3516-ce-core.c54
-rw-r--r--drivers/crypto/gemini/sl3516-ce.h10
-rw-r--r--drivers/crypto/geode-aes.c2
-rw-r--r--drivers/crypto/hifn_795x.c189
-rw-r--r--drivers/crypto/hisilicon/Kconfig11
-rw-r--r--drivers/crypto/hisilicon/Makefile2
-rw-r--r--drivers/crypto/hisilicon/debugfs.c1218
-rw-r--r--drivers/crypto/hisilicon/hpre/hpre.h31
-rw-r--r--drivers/crypto/hisilicon/hpre/hpre_crypto.c670
-rw-r--r--drivers/crypto/hisilicon/hpre/hpre_main.c619
-rw-r--r--drivers/crypto/hisilicon/qm.c3886
-rw-r--r--drivers/crypto/hisilicon/qm_common.h79
-rw-r--r--drivers/crypto/hisilicon/sec/sec_algs.c6
-rw-r--r--drivers/crypto/hisilicon/sec/sec_drv.c13
-rw-r--r--drivers/crypto/hisilicon/sec2/sec.h122
-rw-r--r--drivers/crypto/hisilicon/sec2/sec_crypto.c1330
-rw-r--r--drivers/crypto/hisilicon/sec2/sec_crypto.h13
-rw-r--r--drivers/crypto/hisilicon/sec2/sec_main.c515
-rw-r--r--drivers/crypto/hisilicon/sgl.c55
-rw-r--r--drivers/crypto/hisilicon/trng/trng.c8
-rw-r--r--drivers/crypto/hisilicon/zip/Makefile2
-rw-r--r--drivers/crypto/hisilicon/zip/dae_main.c277
-rw-r--r--drivers/crypto/hisilicon/zip/zip.h29
-rw-r--r--drivers/crypto/hisilicon/zip/zip_crypto.c361
-rw-r--r--drivers/crypto/hisilicon/zip/zip_main.c770
-rw-r--r--drivers/crypto/img-hash.c107
-rw-r--r--drivers/crypto/inside-secure/Makefile1
-rw-r--r--drivers/crypto/inside-secure/eip93/Kconfig20
-rw-r--r--drivers/crypto/inside-secure/eip93/Makefile5
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-aead.c711
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-aead.h38
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-aes.h16
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-cipher.c413
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-cipher.h60
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-common.c822
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-common.h24
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-des.h16
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-hash.c875
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-hash.h82
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-main.c501
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-main.h151
-rw-r--r--drivers/crypto/inside-secure/eip93/eip93-regs.h335
-rw-r--r--drivers/crypto/inside-secure/safexcel.c134
-rw-r--r--drivers/crypto/inside-secure/safexcel.h22
-rw-r--r--drivers/crypto/inside-secure/safexcel_cipher.c238
-rw-r--r--drivers/crypto/inside-secure/safexcel_hash.c320
-rw-r--r--drivers/crypto/intel/Kconfig6
-rw-r--r--drivers/crypto/intel/Makefile6
-rw-r--r--drivers/crypto/intel/iaa/Kconfig19
-rw-r--r--drivers/crypto/intel/iaa/Makefile12
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto.h148
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto_comp_fixed.c91
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto_main.c1949
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto_stats.c271
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto_stats.h45
-rw-r--r--drivers/crypto/intel/ixp4xx/Kconfig14
-rw-r--r--drivers/crypto/intel/ixp4xx/Makefile2
-rw-r--r--drivers/crypto/intel/ixp4xx/ixp4xx_crypto.c (renamed from drivers/crypto/ixp4xx_crypto.c)36
-rw-r--r--drivers/crypto/intel/keembay/Kconfig90
-rw-r--r--drivers/crypto/intel/keembay/Makefile (renamed from drivers/crypto/keembay/Makefile)0
-rw-r--r--drivers/crypto/intel/keembay/keembay-ocs-aes-core.c (renamed from drivers/crypto/keembay/keembay-ocs-aes-core.c)447
-rw-r--r--drivers/crypto/intel/keembay/keembay-ocs-ecc.c (renamed from drivers/crypto/keembay/keembay-ocs-ecc.c)77
-rw-r--r--drivers/crypto/intel/keembay/keembay-ocs-hcu-core.c (renamed from drivers/crypto/keembay/keembay-ocs-hcu-core.c)296
-rw-r--r--drivers/crypto/intel/keembay/ocs-aes.c (renamed from drivers/crypto/keembay/ocs-aes.c)4
-rw-r--r--drivers/crypto/intel/keembay/ocs-aes.h (renamed from drivers/crypto/keembay/ocs-aes.h)0
-rw-r--r--drivers/crypto/intel/keembay/ocs-hcu.c (renamed from drivers/crypto/keembay/ocs-hcu.c)1
-rw-r--r--drivers/crypto/intel/keembay/ocs-hcu.h (renamed from drivers/crypto/keembay/ocs-hcu.h)0
-rw-r--r--drivers/crypto/intel/qat/Kconfig133
-rw-r--r--drivers/crypto/intel/qat/Makefile12
-rw-r--r--drivers/crypto/intel/qat/qat_420xx/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_420xx/adf_420xx_hw_data.c486
-rw-r--r--drivers/crypto/intel/qat/qat_420xx/adf_420xx_hw_data.h55
-rw-r--r--drivers/crypto/intel/qat/qat_420xx/adf_drv.c215
-rw-r--r--drivers/crypto/intel/qat/qat_4xxx/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c480
-rw-r--r--drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.h62
-rw-r--r--drivers/crypto/intel/qat/qat_4xxx/adf_drv.c219
-rw-r--r--drivers/crypto/intel/qat/qat_6xxx/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.c950
-rw-r--r--drivers/crypto/intel/qat/qat_6xxx/adf_6xxx_hw_data.h168
-rw-r--r--drivers/crypto/intel/qat/qat_6xxx/adf_drv.c226
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxx/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxx/adf_c3xxx_hw_data.c (renamed from drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c)40
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxx/adf_c3xxx_hw_data.h (renamed from drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h)7
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c260
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxxvf/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c (renamed from drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c)5
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.h (renamed from drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c229
-rw-r--r--drivers/crypto/intel/qat/qat_c62x/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_c62x/adf_c62x_hw_data.c (renamed from drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c)40
-rw-r--r--drivers/crypto/intel/qat/qat_c62x/adf_c62x_hw_data.h (renamed from drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h)7
-rw-r--r--drivers/crypto/intel/qat/qat_c62x/adf_drv.c260
-rw-r--r--drivers/crypto/intel/qat/qat_c62xvf/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_c62xvf/adf_c62xvf_hw_data.c (renamed from drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c)5
-rw-r--r--drivers/crypto/intel/qat/qat_c62xvf/adf_c62xvf_hw_data.h (renamed from drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c229
-rw-r--r--drivers/crypto/intel/qat/qat_common/Makefile67
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_accel_devices.h494
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_accel_engine.c (renamed from drivers/crypto/qat/qat_common/adf_accel_engine.c)4
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_admin.c610
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_admin.h31
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_aer.c300
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_bank_state.c238
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_bank_state.h49
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg.c (renamed from drivers/crypto/qat/qat_common/adf_cfg.c)65
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg.h (renamed from drivers/crypto/qat/qat_common/adf_cfg.h)4
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg_common.h (renamed from drivers/crypto/qat/qat_common/adf_cfg_common.h)3
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg_services.c212
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg_services.h44
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg_strings.h (renamed from drivers/crypto/qat/qat_common/adf_cfg_strings.h)10
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cfg_user.h (renamed from drivers/crypto/qat/qat_common/adf_cfg_user.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_clock.c135
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_clock.h14
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cnv_dbgfs.c299
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_cnv_dbgfs.h11
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_common_drv.h (renamed from drivers/crypto/qat/qat_common/adf_common_drv.h)68
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c (renamed from drivers/crypto/qat/qat_common/adf_ctl_drv.c)109
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_dbgfs.c79
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_dbgfs.h29
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_dc.c64
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_dc.h17
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c (renamed from drivers/crypto/qat/qat_common/adf_dev_mgr.c)18
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_fw_config.h19
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_fw_counters.c265
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_fw_counters.h11
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_config.c213
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_config.h10
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_hw_csr_data.c101
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_hw_csr_data.h86
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_hw_data.c228
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_hw_data.h93
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_pfvf.c (renamed from drivers/crypto/qat/qat_common/adf_gen2_pfvf.c)4
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen2_pfvf.h (renamed from drivers/crypto/qat/qat_common/adf_gen2_pfvf.h)1
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_config.c276
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_config.h14
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_hw_csr_data.c231
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_hw_csr_data.h188
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.c600
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_hw_data.h182
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_pfvf.c (renamed from drivers/crypto/qat/qat_common/adf_gen4_pfvf.c)8
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_pfvf.h (renamed from drivers/crypto/qat/qat_common/adf_gen4_pfvf.h)1
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_pm.c (renamed from drivers/crypto/qat/qat_common/adf_gen4_pm.c)39
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_pm.h93
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_pm_debugfs.c199
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_ras.c1517
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_ras.h825
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_tl.c154
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_tl.h158
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c1011
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.h10
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_pm.h52
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_pm_dbgfs.c124
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_ras.c818
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_ras.h504
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_shared.c56
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_shared.h17
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_tl.c258
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_gen6_tl.h198
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_heartbeat.c345
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_heartbeat.h100
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_heartbeat_dbgfs.c248
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_heartbeat_dbgfs.h12
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c76
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c (renamed from drivers/crypto/qat/qat_common/adf_hw_arbiter.c)31
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_init.c516
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_isr.c (renamed from drivers/crypto/qat/qat_common/adf_isr.c)41
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c318
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.h89
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_msg.h (renamed from drivers/crypto/qat/qat_common/adf_pfvf_msg.h)7
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_pf_msg.c117
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_pf_msg.h39
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_pf_proto.c (renamed from drivers/crypto/qat/qat_common/adf_pfvf_pf_proto.c)16
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_pf_proto.h (renamed from drivers/crypto/qat/qat_common/adf_pfvf_pf_proto.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_utils.c (renamed from drivers/crypto/qat/qat_common/adf_pfvf_utils.c)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_utils.h (renamed from drivers/crypto/qat/qat_common/adf_pfvf_utils.h)11
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_vf_msg.c (renamed from drivers/crypto/qat/qat_common/adf_pfvf_vf_msg.c)14
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_vf_msg.h (renamed from drivers/crypto/qat/qat_common/adf_pfvf_vf_msg.h)1
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_vf_proto.c (renamed from drivers/crypto/qat/qat_common/adf_pfvf_vf_proto.c)6
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pfvf_vf_proto.h (renamed from drivers/crypto/qat/qat_common/adf_pfvf_vf_proto.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs.c48
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs.h12
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.c46
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_pm_dbgfs_utils.h36
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_rl.c1155
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_rl.h176
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_rl_admin.c98
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_rl_admin.h18
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sriov.c314
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sysfs.c353
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sysfs_ras_counters.c117
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sysfs_ras_counters.h27
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.c468
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.h11
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_telemetry.c328
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_telemetry.h105
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_timer.c71
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_timer.h21
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c737
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.h122
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_transport.c (renamed from drivers/crypto/qat/qat_common/adf_transport.c)4
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_transport.h (renamed from drivers/crypto/qat/qat_common/adf_transport.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_transport_access_macros.h (renamed from drivers/crypto/qat/qat_common/adf_transport_access_macros.h)2
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_transport_debug.c (renamed from drivers/crypto/qat/qat_common/adf_transport_debug.c)27
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_transport_internal.h (renamed from drivers/crypto/qat/qat_common/adf_transport_internal.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_vf_isr.c (renamed from drivers/crypto/qat/qat_common/adf_vf_isr.c)10
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_fw.h (renamed from drivers/crypto/qat/qat_common/icp_qat_fw.h)24
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_fw_comp.h421
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_fw_init_admin.h202
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_fw_la.h (renamed from drivers/crypto/qat/qat_common/icp_qat_fw_la.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_fw_loader_handle.h (renamed from drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h)1
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_fw_pke.h (renamed from drivers/crypto/qat/qat_common/icp_qat_fw_pke.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_hal.h (renamed from drivers/crypto/qat/qat_common/icp_qat_hal.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_hw.h (renamed from drivers/crypto/qat/qat_common/icp_qat_hw.h)90
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_hw_20_comp.h164
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_hw_20_comp_defs.h300
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_hw_51_comp.h99
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_hw_51_comp_defs.h318
-rw-r--r--drivers/crypto/intel/qat/qat_common/icp_qat_uclo.h (renamed from drivers/crypto/qat/qat_common/icp_qat_uclo.h)38
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_algs.c (renamed from drivers/crypto/qat/qat_common/qat_algs.c)412
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_algs_send.c91
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_algs_send.h25
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_asym_algs.c (renamed from drivers/crypto/qat/qat_common/qat_asym_algs.c)108
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_bl.c253
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_bl.h68
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_comp_algs.c269
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_comp_req.h113
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_compression.c295
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_compression.h36
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_crypto.c287
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_crypto.h68
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_hal.c (renamed from drivers/crypto/qat/qat_common/qat_hal.c)16
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_mig_dev.c130
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_uclo.c (renamed from drivers/crypto/qat/qat_common/qat_uclo.c)516
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xcc/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c (renamed from drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c)35
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h (renamed from drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h)5
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c260
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xccvf/Makefile3
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c (renamed from drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c)5
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h (renamed from drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h)0
-rw-r--r--drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c229
-rw-r--r--drivers/crypto/keembay/Kconfig90
-rw-r--r--drivers/crypto/loongson/Kconfig5
-rw-r--r--drivers/crypto/loongson/Makefile1
-rw-r--r--drivers/crypto/loongson/loongson-rng.c209
-rw-r--r--drivers/crypto/marvell/Kconfig8
-rw-r--r--drivers/crypto/marvell/cesa/cesa.c75
-rw-r--r--drivers/crypto/marvell/cesa/cesa.h9
-rw-r--r--drivers/crypto/marvell/cesa/cipher.c33
-rw-r--r--drivers/crypto/marvell/cesa/hash.c65
-rw-r--r--drivers/crypto/marvell/cesa/tdma.c55
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cpt_hw_types.h4
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c43
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cptpf_ucode.h2
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cptvf_algs.c365
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cptvf_algs.h1
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cptvf_main.c8
-rw-r--r--drivers/crypto/marvell/octeontx/otx_cptvf_mbox.c20
-rw-r--r--drivers/crypto/marvell/octeontx2/Makefile11
-rw-r--r--drivers/crypto/marvell/octeontx2/cn10k_cpt.c184
-rw-r--r--drivers/crypto/marvell/octeontx2/cn10k_cpt.h30
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_common.h71
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c59
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_hw_types.h9
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_mbox_common.c68
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_reqmgr.h366
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptlf.c177
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptlf.h147
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptpf.h11
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptpf_main.c161
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptpf_mbox.c312
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c223
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.h4
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptvf.h3
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptvf_algs.c382
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptvf_algs.h5
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptvf_main.c97
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptvf_mbox.c43
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptvf_reqmgr.c183
-rw-r--r--drivers/crypto/mxs-dcp.c140
-rw-r--r--drivers/crypto/n2_asm.S96
-rw-r--r--drivers/crypto/n2_core.c2190
-rw-r--r--drivers/crypto/n2_core.h232
-rw-r--r--drivers/crypto/nx/Makefile2
-rw-r--r--drivers/crypto/nx/nx-842.c39
-rw-r--r--drivers/crypto/nx/nx-842.h28
-rw-r--r--drivers/crypto/nx/nx-aes-cbc.c8
-rw-r--r--drivers/crypto/nx/nx-aes-ccm.c21
-rw-r--r--drivers/crypto/nx/nx-aes-ctr.c8
-rw-r--r--drivers/crypto/nx/nx-aes-ecb.c8
-rw-r--r--drivers/crypto/nx/nx-aes-gcm.c17
-rw-r--r--drivers/crypto/nx/nx-aes-xcbc.c128
-rw-r--r--drivers/crypto/nx/nx-common-powernv.c46
-rw-r--r--drivers/crypto/nx/nx-common-pseries.c115
-rw-r--r--drivers/crypto/nx/nx-sha256.c130
-rw-r--r--drivers/crypto/nx/nx-sha512.c143
-rw-r--r--drivers/crypto/nx/nx.c53
-rw-r--r--drivers/crypto/nx/nx.h18
-rw-r--r--drivers/crypto/omap-aes-gcm.c38
-rw-r--r--drivers/crypto/omap-aes.c334
-rw-r--r--drivers/crypto/omap-aes.h23
-rw-r--r--drivers/crypto/omap-des.c276
-rw-r--r--drivers/crypto/omap-sham.c335
-rw-r--r--drivers/crypto/padlock-sha.c478
-rw-r--r--drivers/crypto/qat/Kconfig97
-rw-r--r--drivers/crypto/qat/Makefile9
-rw-r--r--drivers/crypto/qat/qat_4xxx/Makefile4
-rw-r--r--drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.c369
-rw-r--r--drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.h75
-rw-r--r--drivers/crypto/qat/qat_4xxx/adf_drv.c352
-rw-r--r--drivers/crypto/qat/qat_c3xxx/Makefile4
-rw-r--r--drivers/crypto/qat/qat_c3xxx/adf_drv.c274
-rw-r--r--drivers/crypto/qat/qat_c3xxxvf/Makefile4
-rw-r--r--drivers/crypto/qat/qat_c3xxxvf/adf_drv.c241
-rw-r--r--drivers/crypto/qat/qat_c62x/Makefile4
-rw-r--r--drivers/crypto/qat/qat_c62x/adf_drv.c274
-rw-r--r--drivers/crypto/qat/qat_c62xvf/Makefile4
-rw-r--r--drivers/crypto/qat/qat_c62xvf/adf_drv.c241
-rw-r--r--drivers/crypto/qat/qat_common/Makefile28
-rw-r--r--drivers/crypto/qat/qat_common/adf_accel_devices.h302
-rw-r--r--drivers/crypto/qat/qat_common/adf_admin.c362
-rw-r--r--drivers/crypto/qat/qat_common/adf_aer.c222
-rw-r--r--drivers/crypto/qat/qat_common/adf_gen2_hw_data.c268
-rw-r--r--drivers/crypto/qat/qat_common/adf_gen2_hw_data.h165
-rw-r--r--drivers/crypto/qat/qat_common/adf_gen4_hw_data.c194
-rw-r--r--drivers/crypto/qat/qat_common/adf_gen4_hw_data.h142
-rw-r--r--drivers/crypto/qat/qat_common/adf_gen4_pm.h44
-rw-r--r--drivers/crypto/qat/qat_common/adf_init.c391
-rw-r--r--drivers/crypto/qat/qat_common/adf_pfvf_pf_msg.c52
-rw-r--r--drivers/crypto/qat/qat_common/adf_pfvf_pf_msg.h18
-rw-r--r--drivers/crypto/qat/qat_common/adf_sriov.c217
-rw-r--r--drivers/crypto/qat/qat_common/adf_sysfs.c191
-rw-r--r--drivers/crypto/qat/qat_common/icp_qat_fw_init_admin.h115
-rw-r--r--drivers/crypto/qat/qat_common/qat_algs_send.c86
-rw-r--r--drivers/crypto/qat/qat_common/qat_algs_send.h11
-rw-r--r--drivers/crypto/qat/qat_common/qat_crypto.c405
-rw-r--r--drivers/crypto/qat/qat_common/qat_crypto.h117
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/Makefile4
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/adf_drv.c274
-rw-r--r--drivers/crypto/qat/qat_dh895xccvf/Makefile4
-rw-r--r--drivers/crypto/qat/qat_dh895xccvf/adf_drv.c241
-rw-r--r--drivers/crypto/qce/aead.c28
-rw-r--r--drivers/crypto/qce/common.c5
-rw-r--r--drivers/crypto/qce/core.c138
-rw-r--r--drivers/crypto/qce/core.h10
-rw-r--r--drivers/crypto/qce/dma.c28
-rw-r--r--drivers/crypto/qce/dma.h3
-rw-r--r--drivers/crypto/qce/sha.c28
-rw-r--r--drivers/crypto/qce/skcipher.c10
-rw-r--r--drivers/crypto/qcom-rng.c88
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto.c532
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto.h116
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto_ahash.c360
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto_skcipher.c588
-rw-r--r--drivers/crypto/s5p-sss.c83
-rw-r--r--drivers/crypto/sa2ul.c82
-rw-r--r--drivers/crypto/sa2ul.h2
-rw-r--r--drivers/crypto/sahara.c864
-rw-r--r--drivers/crypto/starfive/Kconfig26
-rw-r--r--drivers/crypto/starfive/Makefile4
-rw-r--r--drivers/crypto/starfive/jh7110-aes.c1150
-rw-r--r--drivers/crypto/starfive/jh7110-cryp.c225
-rw-r--r--drivers/crypto/starfive/jh7110-cryp.h238
-rw-r--r--drivers/crypto/starfive/jh7110-hash.c854
-rw-r--r--drivers/crypto/starfive/jh7110-rsa.c592
-rw-r--r--drivers/crypto/stm32/Kconfig15
-rw-r--r--drivers/crypto/stm32/Makefile1
-rw-r--r--drivers/crypto/stm32/stm32-crc32.c487
-rw-r--r--drivers/crypto/stm32/stm32-cryp.c1526
-rw-r--r--drivers/crypto/stm32/stm32-hash.c1922
-rw-r--r--drivers/crypto/talitos.c37
-rw-r--r--drivers/crypto/talitos.h4
-rw-r--r--drivers/crypto/tegra/Makefile9
-rw-r--r--drivers/crypto/tegra/tegra-se-aes.c2062
-rw-r--r--drivers/crypto/tegra/tegra-se-hash.c1151
-rw-r--r--drivers/crypto/tegra/tegra-se-key.c179
-rw-r--r--drivers/crypto/tegra/tegra-se-main.c442
-rw-r--r--drivers/crypto/tegra/tegra-se.h589
-rw-r--r--drivers/crypto/ti/Kconfig15
-rw-r--r--drivers/crypto/ti/Makefile3
-rw-r--r--drivers/crypto/ti/dthev2-aes.c538
-rw-r--r--drivers/crypto/ti/dthev2-common.c217
-rw-r--r--drivers/crypto/ti/dthev2-common.h109
-rw-r--r--drivers/crypto/ux500/Kconfig32
-rw-r--r--drivers/crypto/ux500/Makefile8
-rw-r--r--drivers/crypto/ux500/cryp/Makefile10
-rw-r--r--drivers/crypto/ux500/cryp/cryp.c394
-rw-r--r--drivers/crypto/ux500/cryp/cryp.h315
-rw-r--r--drivers/crypto/ux500/cryp/cryp_core.c1600
-rw-r--r--drivers/crypto/ux500/cryp/cryp_irq.c45
-rw-r--r--drivers/crypto/ux500/cryp/cryp_irq.h31
-rw-r--r--drivers/crypto/ux500/cryp/cryp_irqp.h125
-rw-r--r--drivers/crypto/ux500/cryp/cryp_p.h122
-rw-r--r--drivers/crypto/ux500/hash/Makefile11
-rw-r--r--drivers/crypto/ux500/hash/hash_alg.h398
-rw-r--r--drivers/crypto/ux500/hash/hash_core.c1966
-rw-r--r--drivers/crypto/virtio/virtio_crypto_akcipher_algs.c163
-rw-r--r--drivers/crypto/virtio/virtio_crypto_common.h7
-rw-r--r--drivers/crypto/virtio/virtio_crypto_core.c83
-rw-r--r--drivers/crypto/virtio/virtio_crypto_mgr.c38
-rw-r--r--drivers/crypto/virtio/virtio_crypto_skcipher_algs.c45
-rw-r--r--drivers/crypto/vmx/.gitignore3
-rw-r--r--drivers/crypto/vmx/Kconfig14
-rw-r--r--drivers/crypto/vmx/Makefile11
-rw-r--r--drivers/crypto/vmx/aes.c134
-rw-r--r--drivers/crypto/vmx/aes_cbc.c133
-rw-r--r--drivers/crypto/vmx/aes_ctr.c149
-rw-r--r--drivers/crypto/vmx/aes_xts.c162
-rw-r--r--drivers/crypto/vmx/aesp8-ppc.h30
-rw-r--r--drivers/crypto/vmx/aesp8-ppc.pl3846
-rw-r--r--drivers/crypto/vmx/ghash.c185
-rw-r--r--drivers/crypto/vmx/ghashp8-ppc.pl243
-rw-r--r--drivers/crypto/vmx/ppc-xlate.pl229
-rw-r--r--drivers/crypto/vmx/vmx.c77
-rw-r--r--drivers/crypto/xilinx/Makefile1
-rw-r--r--drivers/crypto/xilinx/xilinx-trng.c430
-rw-r--r--drivers/crypto/xilinx/zynqmp-aes-gcm.c47
-rw-r--r--drivers/crypto/xilinx/zynqmp-sha.c104
-rw-r--r--drivers/cxl/Kconfig124
-rw-r--r--drivers/cxl/Makefile20
-rw-r--r--drivers/cxl/acpi.c768
-rw-r--r--drivers/cxl/core/Makefile9
-rw-r--r--drivers/cxl/core/cdat.c1074
-rw-r--r--drivers/cxl/core/core.h129
-rw-r--r--drivers/cxl/core/edac.c2109
-rw-r--r--drivers/cxl/core/features.c706
-rw-r--r--drivers/cxl/core/hdm.c966
-rw-r--r--drivers/cxl/core/mbox.c1034
-rw-r--r--drivers/cxl/core/mce.c65
-rw-r--r--drivers/cxl/core/mce.h20
-rw-r--r--drivers/cxl/core/memdev.c851
-rw-r--r--drivers/cxl/core/pci.c1196
-rw-r--r--drivers/cxl/core/pmem.c114
-rw-r--r--drivers/cxl/core/pmu.c68
-rw-r--r--drivers/cxl/core/port.c1414
-rw-r--r--drivers/cxl/core/ras.c126
-rw-r--r--drivers/cxl/core/region.c2961
-rw-r--r--drivers/cxl/core/regs.c532
-rw-r--r--drivers/cxl/core/suspend.c4
-rw-r--r--drivers/cxl/core/trace.c8
-rw-r--r--drivers/cxl/core/trace.h1105
-rw-r--r--drivers/cxl/cxl.h509
-rw-r--r--drivers/cxl/cxlmem.h710
-rw-r--r--drivers/cxl/cxlpci.h73
-rw-r--r--drivers/cxl/mem.c163
-rw-r--r--drivers/cxl/pci.c971
-rw-r--r--drivers/cxl/pmem.c514
-rw-r--r--drivers/cxl/pmu.h28
-rw-r--r--drivers/cxl/port.c137
-rw-r--r--drivers/cxl/security.c206
-rw-r--r--drivers/dax/Kconfig20
-rw-r--r--drivers/dax/Makefile2
-rw-r--r--drivers/dax/bus.c399
-rw-r--r--drivers/dax/bus.h21
-rw-r--r--drivers/dax/cxl.c49
-rw-r--r--drivers/dax/dax-private.h38
-rw-r--r--drivers/dax/device.c105
-rw-r--r--drivers/dax/hmem/Makefile3
-rw-r--r--drivers/dax/hmem/device.c109
-rw-r--r--drivers/dax/hmem/hmem.c159
-rw-r--r--drivers/dax/kmem.c83
-rw-r--r--drivers/dax/pmem.c10
-rw-r--r--drivers/dax/pmem/Makefile7
-rw-r--r--drivers/dax/pmem/pmem.c10
-rw-r--r--drivers/dax/super.c41
-rw-r--r--drivers/dca/dca-core.c5
-rw-r--r--drivers/dca/dca-sysfs.c20
-rw-r--r--drivers/devfreq/Kconfig12
-rw-r--r--drivers/devfreq/Makefile1
-rw-r--r--drivers/devfreq/devfreq-event.c12
-rw-r--r--drivers/devfreq/devfreq.c135
-rw-r--r--drivers/devfreq/event/exynos-nocp.c9
-rw-r--r--drivers/devfreq/event/exynos-ppmu.c22
-rw-r--r--drivers/devfreq/event/rockchip-dfi.c866
-rw-r--r--drivers/devfreq/exynos-bus.c41
-rw-r--r--drivers/devfreq/governor.h127
-rw-r--r--drivers/devfreq/governor_passive.c27
-rw-r--r--drivers/devfreq/governor_performance.c3
-rw-r--r--drivers/devfreq/governor_powersave.c3
-rw-r--r--drivers/devfreq/governor_simpleondemand.c7
-rw-r--r--drivers/devfreq/governor_userspace.c21
-rw-r--r--drivers/devfreq/hisi_uncore_freq.c658
-rw-r--r--drivers/devfreq/imx-bus.c4
-rw-r--r--drivers/devfreq/imx8m-ddrc.c2
-rw-r--r--drivers/devfreq/mtk-cci-devfreq.c32
-rw-r--r--drivers/devfreq/rk3399_dmc.c14
-rw-r--r--drivers/devfreq/sun8i-a33-mbus.c44
-rw-r--r--drivers/devfreq/tegra30-devfreq.c17
-rw-r--r--drivers/dibs/Kconfig23
-rw-r--r--drivers/dibs/Makefile8
-rw-r--r--drivers/dibs/dibs_loopback.c361
-rw-r--r--drivers/dibs/dibs_loopback.h57
-rw-r--r--drivers/dibs/dibs_main.c274
-rw-r--r--drivers/dio/dio-driver.c6
-rw-r--r--drivers/dio/dio.c8
-rw-r--r--drivers/dma-buf/Kconfig1
-rw-r--r--drivers/dma-buf/Makefile2
-rw-r--r--drivers/dma-buf/dma-buf-mapping.c248
-rw-r--r--drivers/dma-buf/dma-buf-sysfs-stats.c13
-rw-r--r--drivers/dma-buf/dma-buf-sysfs-stats.h4
-rw-r--r--drivers/dma-buf/dma-buf.c659
-rw-r--r--drivers/dma-buf/dma-fence-array.c123
-rw-r--r--drivers/dma-buf/dma-fence-chain.c19
-rw-r--r--drivers/dma-buf/dma-fence-unwrap.c179
-rw-r--r--drivers/dma-buf/dma-fence.c304
-rw-r--r--drivers/dma-buf/dma-heap.c67
-rw-r--r--drivers/dma-buf/dma-resv.c76
-rw-r--r--drivers/dma-buf/heaps/cma_heap.c73
-rw-r--r--drivers/dma-buf/heaps/system_heap.c91
-rw-r--r--drivers/dma-buf/st-dma-fence-chain.c24
-rw-r--r--drivers/dma-buf/st-dma-fence-unwrap.c290
-rw-r--r--drivers/dma-buf/st-dma-fence.c34
-rw-r--r--drivers/dma-buf/st-dma-resv.c10
-rw-r--r--drivers/dma-buf/sw_sync.c131
-rw-r--r--drivers/dma-buf/sync_debug.c76
-rw-r--r--drivers/dma-buf/sync_debug.h4
-rw-r--r--drivers/dma-buf/sync_file.c56
-rw-r--r--drivers/dma-buf/sync_trace.h2
-rw-r--r--drivers/dma-buf/udmabuf.c411
-rw-r--r--drivers/dma/Kconfig186
-rw-r--r--drivers/dma/Makefile22
-rw-r--r--drivers/dma/acpi-dma.c47
-rw-r--r--drivers/dma/altera-msgdma.c24
-rw-r--r--drivers/dma/amba-pl08x.c10
-rw-r--r--drivers/dma/amd/Kconfig42
-rw-r--r--drivers/dma/amd/Makefile5
-rw-r--r--drivers/dma/amd/ae4dma/Makefile10
-rw-r--r--drivers/dma/amd/ae4dma/ae4dma-dev.c157
-rw-r--r--drivers/dma/amd/ae4dma/ae4dma-pci.c156
-rw-r--r--drivers/dma/amd/ae4dma/ae4dma.h102
-rw-r--r--drivers/dma/amd/ptdma/Makefile (renamed from drivers/dma/ptdma/Makefile)0
-rw-r--r--drivers/dma/amd/ptdma/ptdma-debugfs.c143
-rw-r--r--drivers/dma/amd/ptdma/ptdma-dev.c (renamed from drivers/dma/ptdma/ptdma-dev.c)7
-rw-r--r--drivers/dma/amd/ptdma/ptdma-dmaengine.c659
-rw-r--r--drivers/dma/amd/ptdma/ptdma-pci.c (renamed from drivers/dma/ptdma/ptdma-pci.c)0
-rw-r--r--drivers/dma/amd/ptdma/ptdma.h (renamed from drivers/dma/ptdma/ptdma.h)9
-rw-r--r--drivers/dma/amd/qdma/Makefile5
-rw-r--r--drivers/dma/amd/qdma/qdma-comm-regs.c64
-rw-r--r--drivers/dma/amd/qdma/qdma.c1143
-rw-r--r--drivers/dma/amd/qdma/qdma.h266
-rw-r--r--drivers/dma/apple-admac.c180
-rw-r--r--drivers/dma/arm-dma350.c660
-rw-r--r--drivers/dma/at_hdmac.c1926
-rw-r--r--drivers/dma/at_hdmac_regs.h478
-rw-r--r--drivers/dma/at_xdmac.c311
-rw-r--r--drivers/dma/bcm-sba-raid.c12
-rw-r--r--drivers/dma/bcm2835-dma.c35
-rw-r--r--drivers/dma/bestcomm/bestcomm.c7
-rw-r--r--drivers/dma/bestcomm/sram.c24
-rw-r--r--drivers/dma/cv1800b-dmamux.c259
-rw-r--r--drivers/dma/dma-axi-dmac.c410
-rw-r--r--drivers/dma/dma-jz4780.c13
-rw-r--r--drivers/dma/dmaengine.c178
-rw-r--r--drivers/dma/dmatest.c22
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c250
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac.h10
-rw-r--r--drivers/dma/dw-edma/Kconfig5
-rw-r--r--drivers/dma/dw-edma/Makefile8
-rw-r--r--drivers/dma/dw-edma/dw-edma-core.c351
-rw-r--r--drivers/dma/dw-edma/dw-edma-core.h68
-rw-r--r--drivers/dma/dw-edma/dw-edma-pcie.c125
-rw-r--r--drivers/dma/dw-edma/dw-edma-v0-core.c248
-rw-r--r--drivers/dma/dw-edma/dw-edma-v0-core.h15
-rw-r--r--drivers/dma/dw-edma/dw-edma-v0-debugfs.c378
-rw-r--r--drivers/dma/dw-edma/dw-edma-v0-debugfs.h5
-rw-r--r--drivers/dma/dw-edma/dw-hdma-v0-core.c299
-rw-r--r--drivers/dma/dw-edma/dw-hdma-v0-core.h17
-rw-r--r--drivers/dma/dw-edma/dw-hdma-v0-debugfs.c170
-rw-r--r--drivers/dma/dw-edma/dw-hdma-v0-debugfs.h22
-rw-r--r--drivers/dma/dw-edma/dw-hdma-v0-regs.h129
-rw-r--r--drivers/dma/dw/acpi.c6
-rw-r--r--drivers/dma/dw/core.c142
-rw-r--r--drivers/dma/dw/dw.c40
-rw-r--r--drivers/dma/dw/idma32.c19
-rw-r--r--drivers/dma/dw/internal.h8
-rw-r--r--drivers/dma/dw/pci.c12
-rw-r--r--drivers/dma/dw/platform.c37
-rw-r--r--drivers/dma/dw/regs.h1
-rw-r--r--drivers/dma/dw/rzn1-dmamux.c19
-rw-r--r--drivers/dma/ep93xx_dma.c298
-rw-r--r--drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c29
-rw-r--r--drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.h11
-rw-r--r--drivers/dma/fsl-dpaa2-qdma/dpdmai.c119
-rw-r--r--drivers/dma/fsl-dpaa2-qdma/dpdmai.h61
-rw-r--r--drivers/dma/fsl-edma-common.c474
-rw-r--r--drivers/dma/fsl-edma-common.h319
-rw-r--r--drivers/dma/fsl-edma-main.c1003
-rw-r--r--drivers/dma/fsl-edma-trace.c4
-rw-r--r--drivers/dma/fsl-edma-trace.h132
-rw-r--r--drivers/dma/fsl-edma.c519
-rw-r--r--drivers/dma/fsl-qdma.c94
-rw-r--r--drivers/dma/fsl_raid.c7
-rw-r--r--drivers/dma/fsldma.c27
-rw-r--r--drivers/dma/fsldma.h1
-rw-r--r--drivers/dma/hisi_dma.c652
-rw-r--r--drivers/dma/hsu/hsu.c8
-rw-r--r--drivers/dma/hsu/hsu.h12
-rw-r--r--drivers/dma/hsu/pci.c47
-rw-r--r--drivers/dma/idma64.c24
-rw-r--r--drivers/dma/idxd/Makefile10
-rw-r--r--drivers/dma/idxd/bus.c14
-rw-r--r--drivers/dma/idxd/cdev.c463
-rw-r--r--drivers/dma/idxd/compat.c7
-rw-r--r--drivers/dma/idxd/debugfs.c138
-rw-r--r--drivers/dma/idxd/defaults.c51
-rw-r--r--drivers/dma/idxd/device.c297
-rw-r--r--drivers/dma/idxd/dma.c28
-rw-r--r--drivers/dma/idxd/idxd.h257
-rw-r--r--drivers/dma/idxd/init.c825
-rw-r--r--drivers/dma/idxd/irq.c323
-rw-r--r--drivers/dma/idxd/perfmon.c112
-rw-r--r--drivers/dma/idxd/registers.h230
-rw-r--r--drivers/dma/idxd/submit.c25
-rw-r--r--drivers/dma/idxd/sysfs.c492
-rw-r--r--drivers/dma/img-mdc-dma.c11
-rw-r--r--drivers/dma/imx-dma.c24
-rw-r--r--drivers/dma/imx-sdma.c162
-rw-r--r--drivers/dma/ioat/dca.c10
-rw-r--r--drivers/dma/ioat/dma.c25
-rw-r--r--drivers/dma/ioat/dma.h5
-rw-r--r--drivers/dma/ioat/hw.h3
-rw-r--r--drivers/dma/ioat/init.c94
-rw-r--r--drivers/dma/ioat/registers.h7
-rw-r--r--drivers/dma/iop-adma.c1554
-rw-r--r--drivers/dma/iop-adma.h914
-rw-r--r--drivers/dma/ipu/Makefile2
-rw-r--r--drivers/dma/ipu/ipu_idmac.c1802
-rw-r--r--drivers/dma/ipu/ipu_intern.h173
-rw-r--r--drivers/dma/ipu/ipu_irq.c367
-rw-r--r--drivers/dma/k3dma.c29
-rw-r--r--drivers/dma/lgm/lgm-dma.c19
-rw-r--r--drivers/dma/loongson1-apb-dma.c660
-rw-r--r--drivers/dma/loongson2-apb-dma.c705
-rw-r--r--drivers/dma/lpc18xx-dmamux.c4
-rw-r--r--drivers/dma/lpc32xx-dmamux.c195
-rw-r--r--drivers/dma/mcf-edma-main.c299
-rw-r--r--drivers/dma/mcf-edma.c330
-rw-r--r--drivers/dma/mediatek/mtk-cqdma.c19
-rw-r--r--drivers/dma/mediatek/mtk-hsdma.c11
-rw-r--r--drivers/dma/mediatek/mtk-uart-apdma.c11
-rw-r--r--drivers/dma/milbeaut-hdmac.c15
-rw-r--r--drivers/dma/milbeaut-xdmac.c15
-rw-r--r--drivers/dma/mmp_pdma.c301
-rw-r--r--drivers/dma/mmp_tdma.c48
-rw-r--r--drivers/dma/moxart-dma.c20
-rw-r--r--drivers/dma/mpc512x_dma.c8
-rw-r--r--drivers/dma/mv_xor.c43
-rw-r--r--drivers/dma/mv_xor.h2
-rw-r--r--drivers/dma/mv_xor_v2.c58
-rw-r--r--drivers/dma/mxs-dma.c16
-rw-r--r--drivers/dma/nbpfaxi.c41
-rw-r--r--drivers/dma/of-dma.c8
-rw-r--r--drivers/dma/owl-dma.c19
-rw-r--r--drivers/dma/pch_dma.c5
-rw-r--r--drivers/dma/pl330.c37
-rw-r--r--drivers/dma/plx_dma.c1
-rw-r--r--drivers/dma/ppc4xx/adma.c23
-rw-r--r--drivers/dma/ppc4xx/dma.h2
-rw-r--r--drivers/dma/ptdma/Kconfig13
-rw-r--r--drivers/dma/ptdma/ptdma-debugfs.c106
-rw-r--r--drivers/dma/ptdma/ptdma-dmaengine.c413
-rw-r--r--drivers/dma/pxa_dma.c36
-rw-r--r--drivers/dma/qcom/Kconfig1
-rw-r--r--drivers/dma/qcom/bam_dma.c46
-rw-r--r--drivers/dma/qcom/gpi.c85
-rw-r--r--drivers/dma/qcom/hidma.c47
-rw-r--r--drivers/dma/qcom/hidma_mgmt.c115
-rw-r--r--drivers/dma/qcom/qcom_adm.c28
-rw-r--r--drivers/dma/s3c24xx-dma.c1428
-rw-r--r--drivers/dma/sa11x0-dma.c10
-rw-r--r--drivers/dma/sf-pdma/sf-pdma.c65
-rw-r--r--drivers/dma/sf-pdma/sf-pdma.h11
-rw-r--r--drivers/dma/sh/Kconfig10
-rw-r--r--drivers/dma/sh/rcar-dmac.c32
-rw-r--r--drivers/dma/sh/rz-dmac.c148
-rw-r--r--drivers/dma/sh/shdma-arm.h48
-rw-r--r--drivers/dma/sh/shdma-base.c30
-rw-r--r--drivers/dma/sh/shdma.h2
-rw-r--r--drivers/dma/sh/shdmac.c31
-rw-r--r--drivers/dma/sh/usb-dmac.c33
-rw-r--r--drivers/dma/sprd-dma.c24
-rw-r--r--drivers/dma/st_fdma.c17
-rw-r--r--drivers/dma/st_fdma.h2
-rw-r--r--drivers/dma/ste_dma40.c353
-rw-r--r--drivers/dma/ste_dma40.h110
-rw-r--r--drivers/dma/ste_dma40_ll.c3
-rw-r--r--drivers/dma/ste_dma40_ll.h2
-rw-r--r--drivers/dma/stm32/Kconfig47
-rw-r--r--drivers/dma/stm32/Makefile5
-rw-r--r--drivers/dma/stm32/stm32-dma.c (renamed from drivers/dma/stm32-dma.c)203
-rw-r--r--drivers/dma/stm32/stm32-dma3.c1926
-rw-r--r--drivers/dma/stm32/stm32-dmamux.c (renamed from drivers/dma/stm32-dmamux.c)21
-rw-r--r--drivers/dma/stm32/stm32-mdma.c (renamed from drivers/dma/stm32-mdma.c)133
-rw-r--r--drivers/dma/sun4i-dma.c260
-rw-r--r--drivers/dma/sun6i-dma.c16
-rw-r--r--drivers/dma/tegra186-gpc-dma.c67
-rw-r--r--drivers/dma/tegra20-apb-dma.c12
-rw-r--r--drivers/dma/tegra210-adma.c332
-rw-r--r--drivers/dma/ti/Kconfig11
-rw-r--r--drivers/dma/ti/Makefile18
-rw-r--r--drivers/dma/ti/cppi41.c14
-rw-r--r--drivers/dma/ti/dma-crossbar.c5
-rw-r--r--drivers/dma/ti/edma.c91
-rw-r--r--drivers/dma/ti/k3-psil-am62.c12
-rw-r--r--drivers/dma/ti/k3-psil-am62a.c196
-rw-r--r--drivers/dma/ti/k3-psil-am62p.c325
-rw-r--r--drivers/dma/ti/k3-psil-j7200.c67
-rw-r--r--drivers/dma/ti/k3-psil-j721e.c79
-rw-r--r--drivers/dma/ti/k3-psil-j721s2.c84
-rw-r--r--drivers/dma/ti/k3-psil-j784s4.c354
-rw-r--r--drivers/dma/ti/k3-psil-priv.h3
-rw-r--r--drivers/dma/ti/k3-psil.c7
-rw-r--r--drivers/dma/ti/k3-udma-glue.c325
-rw-r--r--drivers/dma/ti/k3-udma-private.c2
-rw-r--r--drivers/dma/ti/k3-udma.c366
-rw-r--r--drivers/dma/ti/k3-udma.h1
-rw-r--r--drivers/dma/ti/omap-dma.c24
-rw-r--r--drivers/dma/timb_dma.c5
-rw-r--r--drivers/dma/txx9dmac.c6
-rw-r--r--drivers/dma/uniphier-mdmac.c15
-rw-r--r--drivers/dma/uniphier-xdmac.c23
-rw-r--r--drivers/dma/virt-dma.c1
-rw-r--r--drivers/dma/virt-dma.h10
-rw-r--r--drivers/dma/xgene-dma.c9
-rw-r--r--drivers/dma/xilinx/Makefile1
-rw-r--r--drivers/dma/xilinx/xdma-regs.h171
-rw-r--r--drivers/dma/xilinx/xdma.c1321
-rw-r--r--drivers/dma/xilinx/xilinx_dma.c215
-rw-r--r--drivers/dma/xilinx/xilinx_dpdma.c130
-rw-r--r--drivers/dma/xilinx/zynqmp_dma.c63
-rw-r--r--drivers/dpll/Kconfig13
-rw-r--r--drivers/dpll/Makefile11
-rw-r--r--drivers/dpll/dpll_core.c925
-rw-r--r--drivers/dpll/dpll_core.h92
-rw-r--r--drivers/dpll/dpll_netlink.c1932
-rw-r--r--drivers/dpll/dpll_netlink.h15
-rw-r--r--drivers/dpll/dpll_nl.c170
-rw-r--r--drivers/dpll/dpll_nl.h51
-rw-r--r--drivers/dpll/zl3073x/Kconfig39
-rw-r--r--drivers/dpll/zl3073x/Makefile11
-rw-r--r--drivers/dpll/zl3073x/core.c1080
-rw-r--r--drivers/dpll/zl3073x/core.h367
-rw-r--r--drivers/dpll/zl3073x/devlink.c390
-rw-r--r--drivers/dpll/zl3073x/devlink.h15
-rw-r--r--drivers/dpll/zl3073x/dpll.c1938
-rw-r--r--drivers/dpll/zl3073x/dpll.h48
-rw-r--r--drivers/dpll/zl3073x/flash.c666
-rw-r--r--drivers/dpll/zl3073x/flash.h29
-rw-r--r--drivers/dpll/zl3073x/fw.c419
-rw-r--r--drivers/dpll/zl3073x/fw.h52
-rw-r--r--drivers/dpll/zl3073x/i2c.c76
-rw-r--r--drivers/dpll/zl3073x/out.c157
-rw-r--r--drivers/dpll/zl3073x/out.h93
-rw-r--r--drivers/dpll/zl3073x/prop.c369
-rw-r--r--drivers/dpll/zl3073x/prop.h34
-rw-r--r--drivers/dpll/zl3073x/ref.c204
-rw-r--r--drivers/dpll/zl3073x/ref.h134
-rw-r--r--drivers/dpll/zl3073x/regs.h317
-rw-r--r--drivers/dpll/zl3073x/spi.c76
-rw-r--r--drivers/dpll/zl3073x/synth.c87
-rw-r--r--drivers/dpll/zl3073x/synth.h72
-rw-r--r--drivers/edac/Kconfig149
-rw-r--r--drivers/edac/Makefile25
-rw-r--r--drivers/edac/a72_edac.c225
-rw-r--r--drivers/edac/altera_edac.c77
-rw-r--r--drivers/edac/altera_edac.h2
-rw-r--r--drivers/edac/amd64_edac.c1946
-rw-r--r--drivers/edac/amd64_edac.h102
-rw-r--r--drivers/edac/amd8111_edac.c597
-rw-r--r--drivers/edac/amd8111_edac.h118
-rw-r--r--drivers/edac/amd8131_edac.c358
-rw-r--r--drivers/edac/amd8131_edac.h107
-rw-r--r--drivers/edac/armada_xp_edac.c17
-rw-r--r--drivers/edac/aspeed_edac.c4
-rw-r--r--drivers/edac/bluefield_edac.c184
-rw-r--r--drivers/edac/cell_edac.c282
-rw-r--r--drivers/edac/cpc925_edac.c6
-rw-r--r--drivers/edac/debugfs.c5
-rw-r--r--drivers/edac/dmc520_edac.c8
-rw-r--r--drivers/edac/e752x_edac.c2
-rw-r--r--drivers/edac/e7xxx_edac.c3
-rw-r--r--drivers/edac/ecs.c207
-rw-r--r--drivers/edac/edac_device.c268
-rw-r--r--drivers/edac/edac_device.h24
-rw-r--r--drivers/edac/edac_device_sysfs.c40
-rw-r--r--drivers/edac/edac_mc.c4
-rw-r--r--drivers/edac/edac_mc_sysfs.c391
-rw-r--r--drivers/edac/edac_module.c4
-rw-r--r--drivers/edac/edac_module.h7
-rw-r--r--drivers/edac/edac_pci.h5
-rw-r--r--drivers/edac/edac_pci_sysfs.c20
-rw-r--r--drivers/edac/fsl_ddr_edac.c147
-rw-r--r--drivers/edac/fsl_ddr_edac.h15
-rw-r--r--drivers/edac/ghes_edac.c97
-rw-r--r--drivers/edac/highbank_l2_edac.c8
-rw-r--r--drivers/edac/highbank_mc_edac.c13
-rw-r--r--drivers/edac/i10nm_base.c1026
-rw-r--r--drivers/edac/i5000_edac.c15
-rw-r--r--drivers/edac/i5100_edac.c5
-rw-r--r--drivers/edac/i5400_edac.c6
-rw-r--r--drivers/edac/i7300_edac.c9
-rw-r--r--drivers/edac/i7core_edac.c4
-rw-r--r--drivers/edac/i82860_edac.c3
-rw-r--r--drivers/edac/ie31200_edac.c706
-rw-r--r--drivers/edac/igen6_edac.c400
-rw-r--r--drivers/edac/imh_base.c602
-rw-r--r--drivers/edac/layerscape_edac.c8
-rw-r--r--drivers/edac/loongson_edac.c157
-rw-r--r--drivers/edac/mce_amd.c556
-rw-r--r--drivers/edac/mem_repair.c357
-rw-r--r--drivers/edac/mpc85xx_edac.c16
-rw-r--r--drivers/edac/npcm_edac.c542
-rw-r--r--drivers/edac/octeon_edac-l2c.c7
-rw-r--r--drivers/edac/octeon_edac-lmc.c4
-rw-r--r--drivers/edac/octeon_edac-pc.c6
-rw-r--r--drivers/edac/octeon_edac-pci.c5
-rw-r--r--drivers/edac/pnd2_edac.c66
-rw-r--r--drivers/edac/ppc4xx_edac.c1433
-rw-r--r--drivers/edac/ppc4xx_edac.h167
-rw-r--r--drivers/edac/qcom_edac.c197
-rw-r--r--drivers/edac/r82600_edac.c3
-rw-r--r--drivers/edac/sb_edac.c204
-rw-r--r--drivers/edac/scrub.c210
-rw-r--r--drivers/edac/sifive_edac.c15
-rw-r--r--drivers/edac/skx_base.c114
-rw-r--r--drivers/edac/skx_common.c359
-rw-r--r--drivers/edac/skx_common.h239
-rw-r--r--drivers/edac/synopsys_edac.c191
-rw-r--r--drivers/edac/thunderx_edac.c27
-rw-r--r--drivers/edac/ti_edac.c4
-rw-r--r--drivers/edac/versal_edac.c1196
-rw-r--r--drivers/edac/versalnet_edac.c962
-rw-r--r--drivers/edac/wq.c1
-rw-r--r--drivers/edac/xgene_edac.c34
-rw-r--r--drivers/edac/zynqmp_edac.c465
-rw-r--r--drivers/eisa/Kconfig9
-rw-r--r--drivers/eisa/Makefile10
-rw-r--r--drivers/eisa/eisa-bus.c14
-rw-r--r--drivers/eisa/pci_eisa.c4
-rw-r--r--drivers/eisa/virtual_root.c2
-rw-r--r--drivers/extcon/Kconfig42
-rw-r--r--drivers/extcon/Makefile3
-rw-r--r--drivers/extcon/extcon-adc-jack.c8
-rw-r--r--drivers/extcon/extcon-axp288.c6
-rw-r--r--drivers/extcon/extcon-fsa9480.c7
-rw-r--r--drivers/extcon/extcon-intel-cht-wc.c20
-rw-r--r--drivers/extcon/extcon-intel-mrfld.c24
-rw-r--r--drivers/extcon/extcon-lc824206xa.c495
-rw-r--r--drivers/extcon/extcon-max14526.c302
-rw-r--r--drivers/extcon/extcon-max3355.c4
-rw-r--r--drivers/extcon/extcon-max77693.c7
-rw-r--r--drivers/extcon/extcon-max77843.c14
-rw-r--r--drivers/extcon/extcon-palmas.c1
-rw-r--r--drivers/extcon/extcon-ptn5150.c4
-rw-r--r--drivers/extcon/extcon-qcom-spmi-misc.c8
-rw-r--r--drivers/extcon/extcon-rt8973a.c9
-rw-r--r--drivers/extcon/extcon-rtk-type-c.c1795
-rw-r--r--drivers/extcon/extcon-sm5502.c2
-rw-r--r--drivers/extcon/extcon-usb-gpio.c4
-rw-r--r--drivers/extcon/extcon-usbc-cros-ec.c4
-rw-r--r--drivers/extcon/extcon-usbc-tusb320.c358
-rw-r--r--drivers/extcon/extcon.c369
-rw-r--r--drivers/extcon/extcon.h8
-rw-r--r--drivers/firewire/.kunitconfig8
-rw-r--r--drivers/firewire/Kconfig83
-rw-r--r--drivers/firewire/Makefile7
-rw-r--r--drivers/firewire/core-card.c574
-rw-r--r--drivers/firewire/core-cdev.c770
-rw-r--r--drivers/firewire/core-device.c604
-rw-r--r--drivers/firewire/core-iso.c81
-rw-r--r--drivers/firewire/core-topology.c323
-rw-r--r--drivers/firewire/core-trace.c16
-rw-r--r--drivers/firewire/core-transaction.c756
-rw-r--r--drivers/firewire/core.h96
-rw-r--r--drivers/firewire/device-attribute-test.c253
-rw-r--r--drivers/firewire/init_ohci1394_dma.c14
-rw-r--r--drivers/firewire/net.c33
-rw-r--r--drivers/firewire/nosy.c6
-rw-r--r--drivers/firewire/ohci-serdes-test.c122
-rw-r--r--drivers/firewire/ohci.c1497
-rw-r--r--drivers/firewire/ohci.h243
-rw-r--r--drivers/firewire/packet-header-definitions.h236
-rw-r--r--drivers/firewire/packet-serdes-test.c917
-rw-r--r--drivers/firewire/phy-packet-definitions.h302
-rw-r--r--drivers/firewire/sbp2.c31
-rw-r--r--drivers/firewire/self-id-sequence-helper-test.c152
-rw-r--r--drivers/firewire/uapi-test.c90
-rw-r--r--drivers/firmware/Kconfig84
-rw-r--r--drivers/firmware/Makefile8
-rw-r--r--drivers/firmware/arm_ffa/Makefile6
-rw-r--r--drivers/firmware/arm_ffa/bus.c82
-rw-r--r--drivers/firmware/arm_ffa/common.h2
-rw-r--r--drivers/firmware/arm_ffa/driver.c1655
-rw-r--r--drivers/firmware/arm_scmi/Kconfig152
-rw-r--r--drivers/firmware/arm_scmi/Makefile28
-rw-r--r--drivers/firmware/arm_scmi/base.c10
-rw-r--r--drivers/firmware/arm_scmi/bus.c523
-rw-r--r--drivers/firmware/arm_scmi/clock.c595
-rw-r--r--drivers/firmware/arm_scmi/common.h382
-rw-r--r--drivers/firmware/arm_scmi/driver.c1898
-rw-r--r--drivers/firmware/arm_scmi/mailbox.c205
-rw-r--r--drivers/firmware/arm_scmi/msg.c32
-rw-r--r--drivers/firmware/arm_scmi/notify.c78
-rw-r--r--drivers/firmware/arm_scmi/notify.h4
-rw-r--r--drivers/firmware/arm_scmi/perf.c773
-rw-r--r--drivers/firmware/arm_scmi/pinctrl.c917
-rw-r--r--drivers/firmware/arm_scmi/power.c38
-rw-r--r--drivers/firmware/arm_scmi/powercap.c242
-rw-r--r--drivers/firmware/arm_scmi/protocols.h49
-rw-r--r--drivers/firmware/arm_scmi/quirks.c327
-rw-r--r--drivers/firmware/arm_scmi/quirks.h52
-rw-r--r--drivers/firmware/arm_scmi/raw_mode.c1514
-rw-r--r--drivers/firmware/arm_scmi/raw_mode.h31
-rw-r--r--drivers/firmware/arm_scmi/reset.c56
-rw-r--r--drivers/firmware/arm_scmi/scmi_pm_domain.c159
-rw-r--r--drivers/firmware/arm_scmi/scmi_power_control.c33
-rw-r--r--drivers/firmware/arm_scmi/sensors.c70
-rw-r--r--drivers/firmware/arm_scmi/shmem.c207
-rw-r--r--drivers/firmware/arm_scmi/smc.c244
-rw-r--r--drivers/firmware/arm_scmi/system.c22
-rw-r--r--drivers/firmware/arm_scmi/transports/Kconfig123
-rw-r--r--drivers/firmware/arm_scmi/transports/Makefile18
-rw-r--r--drivers/firmware/arm_scmi/transports/mailbox.c388
-rw-r--r--drivers/firmware/arm_scmi/transports/optee.c (renamed from drivers/firmware/arm_scmi/optee.c)163
-rw-r--r--drivers/firmware/arm_scmi/transports/smc.c313
-rw-r--r--drivers/firmware/arm_scmi/transports/virtio.c (renamed from drivers/firmware/arm_scmi/virtio.c)167
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/Kconfig50
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/Makefile5
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c384
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c276
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c263
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c430
-rw-r--r--drivers/firmware/arm_scmi/vendors/imx/imx95.rst1739
-rw-r--r--drivers/firmware/arm_scmi/voltage.c14
-rw-r--r--drivers/firmware/arm_scpi.c20
-rw-r--r--drivers/firmware/arm_sdei.c67
-rw-r--r--drivers/firmware/broadcom/bcm47xx_nvram.c19
-rw-r--r--drivers/firmware/broadcom/bcm47xx_sprom.c2
-rw-r--r--drivers/firmware/cirrus/Kconfig17
-rw-r--r--drivers/firmware/cirrus/Makefile4
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c1036
-rw-r--r--drivers/firmware/cirrus/test/Makefile23
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_bin.c203
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c725
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c367
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_utils.c13
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c478
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_bin.c2556
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c595
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c689
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c3281
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c1838
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c2669
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c2211
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c1347
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_tests.c14
-rw-r--r--drivers/firmware/dmi-id.c9
-rw-r--r--drivers/firmware/dmi-sysfs.c50
-rw-r--r--drivers/firmware/dmi_scan.c41
-rw-r--r--drivers/firmware/edd.c2
-rw-r--r--drivers/firmware/efi/Kconfig128
-rw-r--r--drivers/firmware/efi/Makefile10
-rw-r--r--drivers/firmware/efi/arm-runtime.c12
-rw-r--r--drivers/firmware/efi/capsule-loader.c3
-rw-r--r--drivers/firmware/efi/cper-arm.c55
-rw-r--r--drivers/firmware/efi/cper-x86.c2
-rw-r--r--drivers/firmware/efi/cper.c108
-rw-r--r--drivers/firmware/efi/cper_cxl.c156
-rw-r--r--drivers/firmware/efi/dev-path-parser.c11
-rw-r--r--drivers/firmware/efi/earlycon.c67
-rw-r--r--drivers/firmware/efi/efi-init.c127
-rw-r--r--drivers/firmware/efi/efi-pstore.c86
-rw-r--r--drivers/firmware/efi/efi.c273
-rw-r--r--drivers/firmware/efi/efibc.c2
-rw-r--r--drivers/firmware/efi/embedded-firmware.c4
-rw-r--r--drivers/firmware/efi/esrt.c23
-rw-r--r--drivers/firmware/efi/fake_mem.c124
-rw-r--r--drivers/firmware/efi/fake_mem.h10
-rw-r--r--drivers/firmware/efi/fdtparams.c6
-rw-r--r--drivers/firmware/efi/libstub/Makefile107
-rw-r--r--drivers/firmware/efi/libstub/Makefile.zboot61
-rw-r--r--drivers/firmware/efi/libstub/alignedmem.c8
-rw-r--r--drivers/firmware/efi/libstub/arm32-stub.c37
-rw-r--r--drivers/firmware/efi/libstub/arm64-stub.c164
-rw-r--r--drivers/firmware/efi/libstub/arm64.c139
-rw-r--r--drivers/firmware/efi/libstub/bitmap.c41
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-entry.c84
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-helper.c571
-rw-r--r--drivers/firmware/efi/libstub/efi-stub.c278
-rw-r--r--drivers/firmware/efi/libstub/efistub.h468
-rw-r--r--drivers/firmware/efi/libstub/fdt.c183
-rw-r--r--drivers/firmware/efi/libstub/file.c127
-rw-r--r--drivers/firmware/efi/libstub/find.c43
-rw-r--r--drivers/firmware/efi/libstub/gop.c450
-rw-r--r--drivers/firmware/efi/libstub/intrinsics.c74
-rw-r--r--drivers/firmware/efi/libstub/kaslr.c157
-rw-r--r--drivers/firmware/efi/libstub/loongarch-stub.c44
-rw-r--r--drivers/firmware/efi/libstub/loongarch-stub.h4
-rw-r--r--drivers/firmware/efi/libstub/loongarch.c84
-rw-r--r--drivers/firmware/efi/libstub/mem.c94
-rw-r--r--drivers/firmware/efi/libstub/pci.c34
-rw-r--r--drivers/firmware/efi/libstub/printk.c154
-rw-r--r--drivers/firmware/efi/libstub/random.c103
-rw-r--r--drivers/firmware/efi/libstub/randomalloc.c52
-rw-r--r--drivers/firmware/efi/libstub/relocate.c32
-rw-r--r--drivers/firmware/efi/libstub/riscv-stub.c125
-rw-r--r--drivers/firmware/efi/libstub/riscv.c98
-rw-r--r--drivers/firmware/efi/libstub/screen_info.c53
-rw-r--r--drivers/firmware/efi/libstub/smbios.c68
-rw-r--r--drivers/firmware/efi/libstub/string.c95
-rw-r--r--drivers/firmware/efi/libstub/systable.c8
-rw-r--r--drivers/firmware/efi/libstub/tpm.c93
-rw-r--r--drivers/firmware/efi/libstub/unaccepted_memory.c223
-rw-r--r--drivers/firmware/efi/libstub/x86-5lvl.c95
-rw-r--r--drivers/firmware/efi/libstub/x86-stub.c868
-rw-r--r--drivers/firmware/efi/libstub/x86-stub.h17
-rw-r--r--drivers/firmware/efi/libstub/zboot-decompress-gzip.c68
-rw-r--r--drivers/firmware/efi/libstub/zboot-decompress-zstd.c49
-rw-r--r--drivers/firmware/efi/libstub/zboot-header.S177
-rw-r--r--drivers/firmware/efi/libstub/zboot.c100
-rw-r--r--drivers/firmware/efi/libstub/zboot.lds63
-rw-r--r--drivers/firmware/efi/memattr.c36
-rw-r--r--drivers/firmware/efi/memmap.c249
-rw-r--r--drivers/firmware/efi/mokvar-table.c59
-rw-r--r--drivers/firmware/efi/ovmf-debug-log.c111
-rw-r--r--drivers/firmware/efi/rci2-table.c10
-rw-r--r--drivers/firmware/efi/riscv-runtime.c46
-rw-r--r--drivers/firmware/efi/runtime-map.c193
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c386
-rw-r--r--drivers/firmware/efi/stmm/mm_communication.h236
-rw-r--r--drivers/firmware/efi/stmm/tee_stmm_efi.c609
-rw-r--r--drivers/firmware/efi/sysfb_efi.c25
-rw-r--r--drivers/firmware/efi/test/efi_test.c5
-rw-r--r--drivers/firmware/efi/tpm.c28
-rw-r--r--drivers/firmware/efi/unaccepted_memory.c229
-rw-r--r--drivers/firmware/efi/vars.c146
-rw-r--r--drivers/firmware/efi/x86_fake_mem.c75
-rw-r--r--drivers/firmware/google/Kconfig25
-rw-r--r--drivers/firmware/google/Makefile3
-rw-r--r--drivers/firmware/google/cbmem.c135
-rw-r--r--drivers/firmware/google/coreboot_table.c95
-rw-r--r--drivers/firmware/google/coreboot_table.h28
-rw-r--r--drivers/firmware/google/framebuffer-coreboot.c30
-rw-r--r--drivers/firmware/google/gsmi.c29
-rw-r--r--drivers/firmware/google/memconsole-coreboot.c9
-rw-r--r--drivers/firmware/google/memconsole-x86-legacy.c1
-rw-r--r--drivers/firmware/google/memconsole.c3
-rw-r--r--drivers/firmware/google/vpd.c13
-rw-r--r--drivers/firmware/imx/Kconfig35
-rw-r--r--drivers/firmware/imx/Makefile4
-rw-r--r--drivers/firmware/imx/imx-dsp.c6
-rw-r--r--drivers/firmware/imx/imx-scu-irq.c143
-rw-r--r--drivers/firmware/imx/imx-scu-soc.c20
-rw-r--r--drivers/firmware/imx/imx-scu.c27
-rw-r--r--drivers/firmware/imx/scu-pd.c423
-rw-r--r--drivers/firmware/imx/sm-cpu.c85
-rw-r--r--drivers/firmware/imx/sm-lmm.c91
-rw-r--r--drivers/firmware/imx/sm-misc.c119
-rw-r--r--drivers/firmware/iscsi_ibft.c5
-rw-r--r--drivers/firmware/iscsi_ibft_find.c26
-rw-r--r--drivers/firmware/memmap.c2
-rw-r--r--drivers/firmware/meson/Kconfig2
-rw-r--r--drivers/firmware/meson/meson_sm.c57
-rw-r--r--drivers/firmware/microchip/Kconfig12
-rw-r--r--drivers/firmware/microchip/Makefile3
-rw-r--r--drivers/firmware/microchip/mpfs-auto-update.c467
-rw-r--r--drivers/firmware/mtk-adsp-ipc.c12
-rw-r--r--drivers/firmware/pcdp.c135
-rw-r--r--drivers/firmware/pcdp.h108
-rw-r--r--drivers/firmware/psci/psci.c227
-rw-r--r--drivers/firmware/psci/psci_checker.c4
-rw-r--r--drivers/firmware/qcom/Kconfig77
-rw-r--r--drivers/firmware/qcom/Makefile10
-rw-r--r--drivers/firmware/qcom/qcom_qseecom.c120
-rw-r--r--drivers/firmware/qcom/qcom_qseecom_uefisecapp.c866
-rw-r--r--drivers/firmware/qcom/qcom_scm-legacy.c (renamed from drivers/firmware/qcom_scm-legacy.c)2
-rw-r--r--drivers/firmware/qcom/qcom_scm-smc.c215
-rw-r--r--drivers/firmware/qcom/qcom_scm.c2476
-rw-r--r--drivers/firmware/qcom/qcom_scm.h194
-rw-r--r--drivers/firmware/qcom/qcom_tzmem.c524
-rw-r--r--drivers/firmware/qcom/qcom_tzmem.h13
-rw-r--r--drivers/firmware/qcom_scm-smc.c153
-rw-r--r--drivers/firmware/qcom_scm.c1479
-rw-r--r--drivers/firmware/qcom_scm.h161
-rw-r--r--drivers/firmware/qemu_fw_cfg.c25
-rw-r--r--drivers/firmware/raspberrypi.c57
-rw-r--r--drivers/firmware/samsung/Kconfig14
-rw-r--r--drivers/firmware/samsung/Makefile6
-rw-r--r--drivers/firmware/samsung/exynos-acpm-dvfs.c80
-rw-r--r--drivers/firmware/samsung/exynos-acpm-dvfs.h21
-rw-r--r--drivers/firmware/samsung/exynos-acpm-pmic.c239
-rw-r--r--drivers/firmware/samsung/exynos-acpm-pmic.h29
-rw-r--r--drivers/firmware/samsung/exynos-acpm.c792
-rw-r--r--drivers/firmware/samsung/exynos-acpm.h23
-rw-r--r--drivers/firmware/smccc/kvm_guest.c78
-rw-r--r--drivers/firmware/smccc/smccc.c45
-rw-r--r--drivers/firmware/smccc/soc_id.c113
-rw-r--r--drivers/firmware/stratix10-rsu.c277
-rw-r--r--drivers/firmware/stratix10-svc.c836
-rw-r--r--drivers/firmware/sysfb.c123
-rw-r--r--drivers/firmware/sysfb_simplefb.c23
-rw-r--r--drivers/firmware/tegra/Kconfig6
-rw-r--r--drivers/firmware/tegra/Makefile1
-rw-r--r--drivers/firmware/tegra/bpmp-debugfs.c93
-rw-r--r--drivers/firmware/tegra/bpmp-private.h6
-rw-r--r--drivers/firmware/tegra/bpmp-tegra186.c228
-rw-r--r--drivers/firmware/tegra/bpmp-tegra210.c15
-rw-r--r--drivers/firmware/tegra/bpmp.c88
-rw-r--r--drivers/firmware/tegra/ivc.c150
-rw-r--r--drivers/firmware/thead,th1520-aon.c250
-rw-r--r--drivers/firmware/ti_sci.c785
-rw-r--r--drivers/firmware/ti_sci.h155
-rw-r--r--drivers/firmware/turris-mox-rwtm.c574
-rw-r--r--drivers/firmware/xilinx/Makefile2
-rw-r--r--drivers/firmware/xilinx/zynqmp-debug.c177
-rw-r--r--drivers/firmware/xilinx/zynqmp-debug.h2
-rw-r--r--drivers/firmware/xilinx/zynqmp-ufs.c118
-rw-r--r--drivers/firmware/xilinx/zynqmp.c857
-rw-r--r--drivers/fpga/Kconfig29
-rw-r--r--drivers/fpga/Makefile7
-rw-r--r--drivers/fpga/altera-cvp.c23
-rw-r--r--drivers/fpga/altera-fpga2sdram.c12
-rw-r--r--drivers/fpga/altera-freeze-bridge.c15
-rw-r--r--drivers/fpga/altera-hps2fpga.c16
-rw-r--r--drivers/fpga/altera-pr-ip-core-plat.c9
-rw-r--r--drivers/fpga/altera-ps-spi.c47
-rw-r--r--drivers/fpga/dfl-afu-dma-region.c117
-rw-r--r--drivers/fpga/dfl-afu-error.c59
-rw-r--r--drivers/fpga/dfl-afu-main.c292
-rw-r--r--drivers/fpga/dfl-afu-region.c50
-rw-r--r--drivers/fpga/dfl-afu.h31
-rw-r--r--drivers/fpga/dfl-fme-br.c36
-rw-r--r--drivers/fpga/dfl-fme-error.c98
-rw-r--r--drivers/fpga/dfl-fme-main.c130
-rw-r--r--drivers/fpga/dfl-fme-mgr.c4
-rw-r--r--drivers/fpga/dfl-fme-perf.c2
-rw-r--r--drivers/fpga/dfl-fme-pr.c90
-rw-r--r--drivers/fpga/dfl-fme-pr.h2
-rw-r--r--drivers/fpga/dfl-fme-region.c12
-rw-r--r--drivers/fpga/dfl-fme.h2
-rw-r--r--drivers/fpga/dfl-pci.c72
-rw-r--r--drivers/fpga/dfl.c706
-rw-r--r--drivers/fpga/dfl.h182
-rw-r--r--drivers/fpga/fpga-bridge.c125
-rw-r--r--drivers/fpga/fpga-mgr.c103
-rw-r--r--drivers/fpga/fpga-region.c61
-rw-r--r--drivers/fpga/ice40-spi.c8
-rw-r--r--drivers/fpga/intel-m10-bmc-sec-update.c479
-rw-r--r--drivers/fpga/lattice-sysconfig-spi.c153
-rw-r--r--drivers/fpga/lattice-sysconfig.c397
-rw-r--r--drivers/fpga/lattice-sysconfig.h39
-rw-r--r--drivers/fpga/microchip-spi.c150
-rw-r--r--drivers/fpga/of-fpga-region.c6
-rw-r--r--drivers/fpga/socfpga-a10.c11
-rw-r--r--drivers/fpga/socfpga.c14
-rw-r--r--drivers/fpga/stratix10-soc.c9
-rw-r--r--drivers/fpga/tests/.kunitconfig5
-rw-r--r--drivers/fpga/tests/Kconfig11
-rw-r--r--drivers/fpga/tests/Makefile6
-rw-r--r--drivers/fpga/tests/fpga-bridge-test.c174
-rw-r--r--drivers/fpga/tests/fpga-mgr-test.c335
-rw-r--r--drivers/fpga/tests/fpga-region-test.c218
-rw-r--r--drivers/fpga/ts73xx-fpga.c4
-rw-r--r--drivers/fpga/versal-fpga.c4
-rw-r--r--drivers/fpga/xilinx-core.c229
-rw-r--r--drivers/fpga/xilinx-core.h27
-rw-r--r--drivers/fpga/xilinx-pr-decoupler.c27
-rw-r--r--drivers/fpga/xilinx-selectmap.c95
-rw-r--r--drivers/fpga/xilinx-spi.c231
-rw-r--r--drivers/fpga/zynq-fpga.c42
-rw-r--r--drivers/fpga/zynqmp-fpga.c21
-rw-r--r--drivers/fsi/Kconfig17
-rw-r--r--drivers/fsi/Makefile2
-rw-r--r--drivers/fsi/fsi-core.c182
-rw-r--r--drivers/fsi/fsi-master-aspeed.c9
-rw-r--r--drivers/fsi/fsi-master-ast-cf.c25
-rw-r--r--drivers/fsi/fsi-master-gpio.c7
-rw-r--r--drivers/fsi/fsi-master-hub.c3
-rw-r--r--drivers/fsi/fsi-master-i2cr.c316
-rw-r--r--drivers/fsi/fsi-master-i2cr.h33
-rw-r--r--drivers/fsi/fsi-master.h4
-rw-r--r--drivers/fsi/fsi-occ.c105
-rw-r--r--drivers/fsi/fsi-sbefifo.c74
-rw-r--r--drivers/fsi/fsi-scom.c9
-rw-r--r--drivers/fsi/fsi-slave.h28
-rw-r--r--drivers/fsi/i2cr-scom.c154
-rw-r--r--drivers/fwctl/Kconfig33
-rw-r--r--drivers/fwctl/Makefile6
-rw-r--r--drivers/fwctl/main.c421
-rw-r--r--drivers/fwctl/mlx5/Makefile4
-rw-r--r--drivers/fwctl/mlx5/main.c418
-rw-r--r--drivers/fwctl/pds/Makefile4
-rw-r--r--drivers/fwctl/pds/main.c535
-rw-r--r--drivers/gnss/core.c13
-rw-r--r--drivers/gnss/serial.c4
-rw-r--r--drivers/gnss/sirf.c4
-rw-r--r--drivers/gnss/ubx.c37
-rw-r--r--drivers/gpib/Kconfig255
-rw-r--r--drivers/gpib/Makefile20
-rw-r--r--drivers/gpib/TODO10
-rw-r--r--drivers/gpib/agilent_82350b/Makefile2
-rw-r--r--drivers/gpib/agilent_82350b/agilent_82350b.c896
-rw-r--r--drivers/gpib/agilent_82350b/agilent_82350b.h157
-rw-r--r--drivers/gpib/agilent_82357a/Makefile4
-rw-r--r--drivers/gpib/agilent_82357a/agilent_82357a.c1691
-rw-r--r--drivers/gpib/agilent_82357a/agilent_82357a.h182
-rw-r--r--drivers/gpib/cb7210/Makefile3
-rw-r--r--drivers/gpib/cb7210/cb7210.c1586
-rw-r--r--drivers/gpib/cb7210/cb7210.h203
-rw-r--r--drivers/gpib/cec/Makefile3
-rw-r--r--drivers/gpib/cec/cec.h20
-rw-r--r--drivers/gpib/cec/cec_gpib.c393
-rw-r--r--drivers/gpib/common/Makefile6
-rw-r--r--drivers/gpib/common/gpib_os.c2271
-rw-r--r--drivers/gpib/common/iblib.c717
-rw-r--r--drivers/gpib/common/ibsys.h34
-rw-r--r--drivers/gpib/eastwood/Makefile3
-rw-r--r--drivers/gpib/eastwood/fluke_gpib.c1180
-rw-r--r--drivers/gpib/eastwood/fluke_gpib.h146
-rw-r--r--drivers/gpib/fmh_gpib/Makefile2
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.c1754
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.h177
-rw-r--r--drivers/gpib/gpio/Makefile4
-rw-r--r--drivers/gpib/gpio/gpib_bitbang.c1469
-rw-r--r--drivers/gpib/hp_82335/Makefile4
-rw-r--r--drivers/gpib/hp_82335/hp82335.c371
-rw-r--r--drivers/gpib/hp_82335/hp82335.h52
-rw-r--r--drivers/gpib/hp_82341/Makefile2
-rw-r--r--drivers/gpib/hp_82341/hp_82341.c907
-rw-r--r--drivers/gpib/hp_82341/hp_82341.h165
-rw-r--r--drivers/gpib/include/amcc5920.h49
-rw-r--r--drivers/gpib/include/amccs5933.h59
-rw-r--r--drivers/gpib/include/gpibP.h41
-rw-r--r--drivers/gpib/include/gpib_cmd.h112
-rw-r--r--drivers/gpib/include/gpib_pci_ids.h23
-rw-r--r--drivers/gpib/include/gpib_proto.h49
-rw-r--r--drivers/gpib/include/gpib_state_machines.h23
-rw-r--r--drivers/gpib/include/gpib_types.h381
-rw-r--r--drivers/gpib/include/nec7210.h141
-rw-r--r--drivers/gpib/include/nec7210_registers.h218
-rw-r--r--drivers/gpib/include/plx9050.h72
-rw-r--r--drivers/gpib/include/quancom_pci.h22
-rw-r--r--drivers/gpib/include/tms9914.h280
-rw-r--r--drivers/gpib/include/tnt4882_registers.h192
-rw-r--r--drivers/gpib/ines/Makefile3
-rw-r--r--drivers/gpib/ines/ines.h165
-rw-r--r--drivers/gpib/ines/ines_gpib.c1500
-rw-r--r--drivers/gpib/lpvo_usb_gpib/Makefile3
-rw-r--r--drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c2025
-rw-r--r--drivers/gpib/nec7210/Makefile4
-rw-r--r--drivers/gpib/nec7210/board.h19
-rw-r--r--drivers/gpib/nec7210/nec7210.c1121
-rw-r--r--drivers/gpib/ni_usb/Makefile4
-rw-r--r--drivers/gpib/ni_usb/ni_usb_gpib.c2678
-rw-r--r--drivers/gpib/ni_usb/ni_usb_gpib.h226
-rw-r--r--drivers/gpib/pc2/Makefile5
-rw-r--r--drivers/gpib/pc2/pc2_gpib.c684
-rw-r--r--drivers/gpib/tms9914/Makefile6
-rw-r--r--drivers/gpib/tms9914/tms9914.c914
-rw-r--r--drivers/gpib/tnt4882/Makefile6
-rw-r--r--drivers/gpib/tnt4882/mite.c133
-rw-r--r--drivers/gpib/tnt4882/mite.h234
-rw-r--r--drivers/gpib/tnt4882/tnt4882_gpib.c1838
-rw-r--r--drivers/gpio/Kconfig585
-rw-r--r--drivers/gpio/Makefile43
-rw-r--r--drivers/gpio/TODO129
-rw-r--r--drivers/gpio/dev-sync-probe.c97
-rw-r--r--drivers/gpio/dev-sync-probe.h25
-rw-r--r--drivers/gpio/gpio-104-dio-48e.c453
-rw-r--r--drivers/gpio/gpio-104-idi-48.c342
-rw-r--r--drivers/gpio/gpio-104-idio-16.c350
-rw-r--r--drivers/gpio/gpio-74x164.c103
-rw-r--r--drivers/gpio/gpio-74xx-mmio.c34
-rw-r--r--drivers/gpio/gpio-adnp.c147
-rw-r--r--drivers/gpio/gpio-adp5520.c10
-rw-r--r--drivers/gpio/gpio-adp5585.c527
-rw-r--r--drivers/gpio/gpio-adp5588.c448
-rw-r--r--drivers/gpio/gpio-aggregator.c1623
-rw-r--r--drivers/gpio/gpio-altera-a10sr.c15
-rw-r--r--drivers/gpio/gpio-altera.c217
-rw-r--r--drivers/gpio/gpio-amd-fch.c5
-rw-r--r--drivers/gpio/gpio-amd8111.c12
-rw-r--r--drivers/gpio/gpio-amdpt.c54
-rw-r--r--drivers/gpio/gpio-arizona.c14
-rw-r--r--drivers/gpio/gpio-aspeed-sgpio.c125
-rw-r--r--drivers/gpio/gpio-aspeed.c805
-rw-r--r--drivers/gpio/gpio-ath79.c124
-rw-r--r--drivers/gpio/gpio-bcm-kona.c151
-rw-r--r--drivers/gpio/gpio-bd71815.c13
-rw-r--r--drivers/gpio/gpio-bd71828.c13
-rw-r--r--drivers/gpio/gpio-bd9571mwv.c6
-rw-r--r--drivers/gpio/gpio-blzp1600.c290
-rw-r--r--drivers/gpio/gpio-brcmstb.c170
-rw-r--r--drivers/gpio/gpio-bt8xx.c76
-rw-r--r--drivers/gpio/gpio-cadence.c98
-rw-r--r--drivers/gpio/gpio-cgbc.c200
-rw-r--r--drivers/gpio/gpio-clps711x.c32
-rw-r--r--drivers/gpio/gpio-creg-snps.c10
-rw-r--r--drivers/gpio/gpio-cros-ec.c214
-rw-r--r--drivers/gpio/gpio-crystalcove.c15
-rw-r--r--drivers/gpio/gpio-cs5535.c4
-rw-r--r--drivers/gpio/gpio-da9052.c41
-rw-r--r--drivers/gpio/gpio-da9055.c12
-rw-r--r--drivers/gpio/gpio-davinci.c190
-rw-r--r--drivers/gpio/gpio-dln2.c9
-rw-r--r--drivers/gpio/gpio-ds4520.c74
-rw-r--r--drivers/gpio/gpio-dwapb.c205
-rw-r--r--drivers/gpio/gpio-eic-sprd.c144
-rw-r--r--drivers/gpio/gpio-elkhartlake.c80
-rw-r--r--drivers/gpio/gpio-em.c17
-rw-r--r--drivers/gpio/gpio-en7523.c42
-rw-r--r--drivers/gpio/gpio-ep93xx.c400
-rw-r--r--drivers/gpio/gpio-exar.c65
-rw-r--r--drivers/gpio/gpio-f7188x.c288
-rw-r--r--drivers/gpio/gpio-ftgpio010.c99
-rw-r--r--drivers/gpio/gpio-fxl6408.c171
-rw-r--r--drivers/gpio/gpio-ge.c82
-rw-r--r--drivers/gpio/gpio-gpio-mm.c156
-rw-r--r--drivers/gpio/gpio-graniterapids.c415
-rw-r--r--drivers/gpio/gpio-grgpio.c177
-rw-r--r--drivers/gpio/gpio-gw-pld.c8
-rw-r--r--drivers/gpio/gpio-hisi.c82
-rw-r--r--drivers/gpio/gpio-hlwd.c136
-rw-r--r--drivers/gpio/gpio-htc-egpio.c35
-rw-r--r--drivers/gpio/gpio-i8255.c320
-rw-r--r--drivers/gpio/gpio-i8255.h54
-rw-r--r--drivers/gpio/gpio-ich.c20
-rw-r--r--drivers/gpio/gpio-idio-16.c178
-rw-r--r--drivers/gpio/gpio-idio-16.h32
-rw-r--r--drivers/gpio/gpio-idt3243x.c56
-rw-r--r--drivers/gpio/gpio-imx-scu.c134
-rw-r--r--drivers/gpio/gpio-iop.c59
-rw-r--r--drivers/gpio/gpio-it87.c9
-rw-r--r--drivers/gpio/gpio-ixp4xx.c137
-rw-r--r--drivers/gpio/gpio-janz-ttl.c4
-rw-r--r--drivers/gpio/gpio-kempld.c5
-rw-r--r--drivers/gpio/gpio-latch.c221
-rw-r--r--drivers/gpio/gpio-ljca.c492
-rw-r--r--drivers/gpio/gpio-logicvc.c12
-rw-r--r--drivers/gpio/gpio-loongson-64bit.c579
-rw-r--r--drivers/gpio/gpio-loongson.c6
-rw-r--r--drivers/gpio/gpio-loongson1.c91
-rw-r--r--drivers/gpio/gpio-lp3943.c13
-rw-r--r--drivers/gpio/gpio-lp873x.c10
-rw-r--r--drivers/gpio/gpio-lp87565.c13
-rw-r--r--drivers/gpio/gpio-lpc18xx.c55
-rw-r--r--drivers/gpio/gpio-lpc32xx.c23
-rw-r--r--drivers/gpio/gpio-macsmc.c292
-rw-r--r--drivers/gpio/gpio-madera.c16
-rw-r--r--drivers/gpio/gpio-max3191x.c38
-rw-r--r--drivers/gpio/gpio-max7300.c9
-rw-r--r--drivers/gpio/gpio-max730x.c26
-rw-r--r--drivers/gpio/gpio-max732x.c29
-rw-r--r--drivers/gpio/gpio-max7360.c257
-rw-r--r--drivers/gpio/gpio-max77620.c13
-rw-r--r--drivers/gpio/gpio-max77650.c12
-rw-r--r--drivers/gpio/gpio-max77759.c530
-rw-r--r--drivers/gpio/gpio-mb86s7x.c34
-rw-r--r--drivers/gpio/gpio-mc33880.c10
-rw-r--r--drivers/gpio/gpio-menz127.c111
-rw-r--r--drivers/gpio/gpio-merrifield.c463
-rw-r--r--drivers/gpio/gpio-ml-ioh.c16
-rw-r--r--drivers/gpio/gpio-mlxbf.c25
-rw-r--r--drivers/gpio/gpio-mlxbf2.c129
-rw-r--r--drivers/gpio/gpio-mlxbf3.c291
-rw-r--r--drivers/gpio/gpio-mm-lantiq.c69
-rw-r--r--drivers/gpio/gpio-mmio.c710
-rw-r--r--drivers/gpio/gpio-mockup.c101
-rw-r--r--drivers/gpio/gpio-moxtet.c14
-rw-r--r--drivers/gpio/gpio-mpc5200.c90
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c209
-rw-r--r--drivers/gpio/gpio-mpfs.c191
-rw-r--r--drivers/gpio/gpio-mpsse.c730
-rw-r--r--drivers/gpio/gpio-msc313.c50
-rw-r--r--drivers/gpio/gpio-mt7621.c87
-rw-r--r--drivers/gpio/gpio-mvebu.c129
-rw-r--r--drivers/gpio/gpio-mxc.c295
-rw-r--r--drivers/gpio/gpio-mxs.c39
-rw-r--r--drivers/gpio/gpio-nct6694.c499
-rw-r--r--drivers/gpio/gpio-nomadik.c734
-rw-r--r--drivers/gpio/gpio-npcm-sgpio.c617
-rw-r--r--drivers/gpio/gpio-octeon.c5
-rw-r--r--drivers/gpio/gpio-omap.c123
-rw-r--r--drivers/gpio/gpio-palmas.c26
-rw-r--r--drivers/gpio/gpio-pca953x.c737
-rw-r--r--drivers/gpio/gpio-pca9570.c54
-rw-r--r--drivers/gpio/gpio-pcf857x.c184
-rw-r--r--drivers/gpio/gpio-pch.c17
-rw-r--r--drivers/gpio/gpio-pci-idio-16.c403
-rw-r--r--drivers/gpio/gpio-pcie-idio-24.c691
-rw-r--r--drivers/gpio/gpio-pisosr.c32
-rw-r--r--drivers/gpio/gpio-pl061.c39
-rw-r--r--drivers/gpio/gpio-pmic-eic-sprd.c59
-rw-r--r--drivers/gpio/gpio-pxa.c32
-rw-r--r--drivers/gpio/gpio-qixis-fpga.c111
-rw-r--r--drivers/gpio/gpio-raspberrypi-exp.c10
-rw-r--r--drivers/gpio/gpio-rc5t583.c19
-rw-r--r--drivers/gpio/gpio-rcar.c85
-rw-r--r--drivers/gpio/gpio-rda.c58
-rw-r--r--drivers/gpio/gpio-rdc321x.c12
-rw-r--r--drivers/gpio/gpio-realtek-otto.c41
-rw-r--r--drivers/gpio/gpio-reg.c22
-rw-r--r--drivers/gpio/gpio-regmap.c172
-rw-r--r--drivers/gpio/gpio-rockchip.c76
-rw-r--r--drivers/gpio/gpio-rtd.c609
-rw-r--r--drivers/gpio/gpio-sa1100.c20
-rw-r--r--drivers/gpio/gpio-sama5d2-piobu.c27
-rw-r--r--drivers/gpio/gpio-sch.c44
-rw-r--r--drivers/gpio/gpio-sch311x.c32
-rw-r--r--drivers/gpio/gpio-shared-proxy.c334
-rw-r--r--drivers/gpio/gpio-sifive.c122
-rw-r--r--drivers/gpio/gpio-sim.c734
-rw-r--r--drivers/gpio/gpio-siox.c84
-rw-r--r--drivers/gpio/gpio-sl28cpld.c3
-rw-r--r--drivers/gpio/gpio-sloppy-logic-analyzer.c345
-rw-r--r--drivers/gpio/gpio-sodaville.c24
-rw-r--r--drivers/gpio/gpio-spacemit-k1.c307
-rw-r--r--drivers/gpio/gpio-spear-spics.c19
-rw-r--r--drivers/gpio/gpio-sprd.c17
-rw-r--r--drivers/gpio/gpio-sta2x11.c411
-rw-r--r--drivers/gpio/gpio-stmpe.c127
-rw-r--r--drivers/gpio/gpio-stp-xway.c23
-rw-r--r--drivers/gpio/gpio-syscon.c62
-rw-r--r--drivers/gpio/gpio-tangier.c518
-rw-r--r--drivers/gpio/gpio-tangier.h117
-rw-r--r--drivers/gpio/gpio-tb10x.c106
-rw-r--r--drivers/gpio/gpio-tc3589x.c17
-rw-r--r--drivers/gpio/gpio-tegra.c84
-rw-r--r--drivers/gpio/gpio-tegra186.c408
-rw-r--r--drivers/gpio/gpio-thunderx.c45
-rw-r--r--drivers/gpio/gpio-timberdale.c24
-rw-r--r--drivers/gpio/gpio-tpic2810.c53
-rw-r--r--drivers/gpio/gpio-tps65086.c41
-rw-r--r--drivers/gpio/gpio-tps65218.c31
-rw-r--r--drivers/gpio/gpio-tps65219.c269
-rw-r--r--drivers/gpio/gpio-tps6586x.c15
-rw-r--r--drivers/gpio/gpio-tps65910.c21
-rw-r--r--drivers/gpio/gpio-tps65912.c15
-rw-r--r--drivers/gpio/gpio-tps68470.c16
-rw-r--r--drivers/gpio/gpio-tqmx86.c245
-rw-r--r--drivers/gpio/gpio-ts4800.c40
-rw-r--r--drivers/gpio/gpio-ts4900.c21
-rw-r--r--drivers/gpio/gpio-ts5500.c8
-rw-r--r--drivers/gpio/gpio-twl4030.c100
-rw-r--r--drivers/gpio/gpio-twl6040.c27
-rw-r--r--drivers/gpio/gpio-ucb1400.c84
-rw-r--r--drivers/gpio/gpio-uniphier.c31
-rw-r--r--drivers/gpio/gpio-usbio.c248
-rw-r--r--drivers/gpio/gpio-vf610.c208
-rw-r--r--drivers/gpio/gpio-viperboard.c126
-rw-r--r--drivers/gpio/gpio-virtio.c54
-rw-r--r--drivers/gpio/gpio-virtuser.c1800
-rw-r--r--drivers/gpio/gpio-visconti.c82
-rw-r--r--drivers/gpio/gpio-vx855.c9
-rw-r--r--drivers/gpio/gpio-wcd934x.c20
-rw-r--r--drivers/gpio/gpio-wcove.c14
-rw-r--r--drivers/gpio/gpio-winbond.c14
-rw-r--r--drivers/gpio/gpio-wm831x.c31
-rw-r--r--drivers/gpio/gpio-wm8350.c20
-rw-r--r--drivers/gpio/gpio-wm8994.c23
-rw-r--r--drivers/gpio/gpio-ws16c48.c559
-rw-r--r--drivers/gpio/gpio-xgene-sb.c122
-rw-r--r--drivers/gpio/gpio-xgene.c12
-rw-r--r--drivers/gpio/gpio-xgs-iproc.c70
-rw-r--r--drivers/gpio/gpio-xilinx.c256
-rw-r--r--drivers/gpio/gpio-xlp.c22
-rw-r--r--drivers/gpio/gpio-xra1403.c22
-rw-r--r--drivers/gpio/gpio-xtensa.c11
-rw-r--r--drivers/gpio/gpio-zevio.c21
-rw-r--r--drivers/gpio/gpio-zynq.c55
-rw-r--r--drivers/gpio/gpio-zynqmp-modepin.c9
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c1427
-rw-r--r--drivers/gpio/gpiolib-acpi-quirks.c402
-rw-r--r--drivers/gpio/gpiolib-acpi.c1592
-rw-r--r--drivers/gpio/gpiolib-acpi.h88
-rw-r--r--drivers/gpio/gpiolib-cdev.c1038
-rw-r--r--drivers/gpio/gpiolib-devres.c268
-rw-r--r--drivers/gpio/gpiolib-legacy.c118
-rw-r--r--drivers/gpio/gpiolib-of.c1068
-rw-r--r--drivers/gpio/gpiolib-of.h32
-rw-r--r--drivers/gpio/gpiolib-shared.c656
-rw-r--r--drivers/gpio/gpiolib-shared.h71
-rw-r--r--drivers/gpio/gpiolib-swnode.c164
-rw-r--r--drivers/gpio/gpiolib-swnode.h14
-rw-r--r--drivers/gpio/gpiolib-sysfs.c1004
-rw-r--r--drivers/gpio/gpiolib-sysfs.h2
-rw-r--r--drivers/gpio/gpiolib.c3164
-rw-r--r--drivers/gpio/gpiolib.h217
-rw-r--r--drivers/gpu/Makefile1
-rw-r--r--drivers/gpu/drm/Kconfig412
-rw-r--r--drivers/gpu/drm/Kconfig.debug117
-rw-r--r--drivers/gpu/drm/Makefile198
-rw-r--r--drivers/gpu/drm/adp/Kconfig17
-rw-r--r--drivers/gpu/drm/adp/Makefile5
-rw-r--r--drivers/gpu/drm/adp/adp-mipi.c277
-rw-r--r--drivers/gpu/drm/adp/adp_drv.c614
-rw-r--r--drivers/gpu/drm/amd/acp/include/acp_gfx_if.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/Kconfig86
-rw-r--r--drivers/gpu/drm/amd/amdgpu/Makefile116
-rw-r--r--drivers/gpu/drm/amd/amdgpu/aldebaran.c139
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h646
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c984
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h232
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c85
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c568
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c410
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h229
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.c154
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_aldebaran.h27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c131
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c559
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c399
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h70
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10_3.c189
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c240
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c384
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c690
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h86
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c1513
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c126
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c463
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c216
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c129
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c201
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c302
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c591
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h105
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c1596
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c77
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_csa.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c100
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c674
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c371
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h46
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c4870
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_df.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c1424
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.c407
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.h14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c198
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_doorbell.h100
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_doorbell_mgr.c244
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c1097
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_eeprom.c61
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_eeprom.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c241
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.h69
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fdinfo.c75
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fdinfo.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c438
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c440
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.h13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fw_attestation.c42
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c208
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c617
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gem.h22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c1987
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h322
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfxhub.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c1053
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h199
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c41
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.c84
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c293
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h73
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c103
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c127
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c248
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c96
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c186
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h23
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_isp.c345
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_isp.h60
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c373
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.h62
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c419
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h114
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c675
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c558
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h116
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c1568
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h224
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.c46
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h25
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c244
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mn.h53
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h113
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_nbio.c41
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_nbio.h14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c618
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.h102
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_pmu.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c1988
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h166
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c232
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c3659
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h372
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c1038
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h71
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c215
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h61
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c582
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h261
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.c576
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.h127
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c304
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h116
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c325
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c493
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h81
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.c262
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_seq64.h49
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_smuio.h14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c146
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h100
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c1080
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h80
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c641
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h89
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c582
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h107
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umr.h38
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umsch_mm.c550
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umsch_mm.h246
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c1482
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h161
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c1011
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h77
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_utils.h91
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c192
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c282
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c1167
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h263
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c1094
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h160
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c110
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c1608
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h332
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c353
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c120
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_tlb_fence.c111
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c1018
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.h111
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c318
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c1107
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h217
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c991
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h85
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h263
-rw-r--r--drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c986
-rw-r--r--drivers/gpu/drm/amd/amdgpu/athub_v1_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/athub_v2_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/athub_v2_1.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/athub_v3_0.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/athub_v4_1_0.c122
-rw-r--r--drivers/gpu/drm/amd/amdgpu/athub_v4_1_0.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atom.c114
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atom.h27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_crtc.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_dp.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_dp.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_encoders.c68
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_i2c.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik.c140
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik_ih.c72
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik_sdma.c128
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cikd.h11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/clearstate_gfx12.h121
-rw-r--r--drivers/gpu/drm/amd/amdgpu/clearstate_gfx9.h27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/clearstate_si.h24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c56
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cz_ih.c66
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c158
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c3786
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v6_0.c496
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c205
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v1_7.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v3_6.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v4_15.c45
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v4_15.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v4_3.c61
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v4_3.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v4_6_2.c34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/df_v4_6_2.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c2579
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0_cleaner_shader.h91
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_1_10_cleaner_shader.asm125
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_3_0_cleaner_shader.asm124
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c3158
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0_3.c110
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0_3.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0_3_cleaner_shader.asm118
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0_cleaner_shader.h56
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c5793
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c215
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c353
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c489
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c1997
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0_cleaner_shader.h68
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4.c30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.c76
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_2_cleaner_shader.asm153
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c5062
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3_cleaner_shader.asm153
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3_cleaner_shader.h64
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.c516
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v11_5_0.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.c521
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v12_0.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v1_0.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c674
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v2_0.c22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v2_1.c172
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0.c52
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.c501
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfxhub_v3_0_3.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c568
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c560
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c1070
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v12_0.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c241
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c218
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c282
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c1232
-rw-r--r--drivers/gpu/drm/amd/amdgpu/hdp_v4_0.c51
-rw-r--r--drivers/gpu/drm/amd/amdgpu/hdp_v5_0.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/hdp_v5_2.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/hdp_v6_0.c30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/hdp_v7_0.c132
-rw-r--r--drivers/gpu/drm/amd/amdgpu/hdp_v7_0.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/iceland_ih.c66
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ih_v6_0.c167
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ih_v6_1.c796
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ih_v6_1.h28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ih_v7_0.c787
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ih_v7_0.h28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v11_0.c40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v11_0_3.c145
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v11_0_3.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v12_0.c406
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v12_0.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/isp_v4_1_0.c191
-rw-r--r--drivers/gpu/drm/amd/amdgpu/isp_v4_1_0.h50
-rw-r--r--drivers/gpu/drm/amd/amdgpu/isp_v4_1_1.c377
-rw-r--r--drivers/gpu/drm/amd/amdgpu/isp_v4_1_1.h49
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v1_0.c120
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v1_0.h17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c165
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c315
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c159
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c409
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c1486
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.h74
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c872
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.h35
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c733
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.h35
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c1101
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.h111
-rw-r--r--drivers/gpu/drm/amd/amdgpu/lsdma_v7_0.c121
-rw-r--r--drivers/gpu/drm/amd/amdgpu/lsdma_v7_0.h31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mca_v3_0.c44
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mca_v3_0.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.c501
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v10_1.c1259
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v10_1.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v11_0.c973
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_0.c1942
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_0.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v1_0.c69
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v1_7.c41
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c871
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v2_0.c58
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v2_3.c43
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v3_0.c55
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v3_0_1.c80
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v3_0_2.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v3_3.c746
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v3_3.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v4_1_0.c647
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v4_1_0.h28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v9_4.c53
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmsch_v3_0.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmsch_v4_0.h144
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmsch_v4_0_3.h37
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmsch_v5_0.h144
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c161
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_ai.h15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c270
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_nv.h18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_vi.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/navi10_ih.c77
-rw-r--r--drivers/gpu/drm/amd/amdgpu/navi10_sdma_pkt_open.h64
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c554
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.h33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v2_3.c40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c152
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v4_3.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v6_1.c23
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_0.c26
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_11.c400
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_11.h33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_2.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c47
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_7.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c696
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_9.h33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nv.c406
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nv.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nvd.h211
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h49
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v10_0.c112
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v11_0.c256
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v11_0_8.c52
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v12_0.c233
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v13_0.c423
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v13_0.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v13_0_4.c80
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v14_0.c705
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v14_0.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v3_1.c84
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c123
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c126
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c594
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_4.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c2613
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c871
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v5_0.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c996
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v5_2.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c1029
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v6_0_0_pkt_open.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c1859
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v7_0.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si.c656
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si_dma.c188
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si_enums.h246
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si_ih.c88
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sid.h1964
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sienna_cichlid.c298
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sienna_cichlid.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smu_v11_0_i2c.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smu_v13_0_10.c296
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smu_v13_0_10.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smuio_v13_0.c22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smuio_v13_0_3.c109
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smuio_v13_0_3.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smuio_v14_0_2.c62
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smuio_v14_0_2.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/smuio_v9_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15.c453
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15.h15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15_common.h143
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15d.h156
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc21.c593
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc24.c601
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc24.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ta_ras_if.h57
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ta_secureDisplay_if.h48
-rw-r--r--drivers/gpu/drm/amd/amdgpu/ta_xgmi_if.h62
-rw-r--r--drivers/gpu/drm/amd/amdgpu/tonga_ih.c88
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v12_0.c742
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v12_0.h105
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v6_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v6_1.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v6_7.c292
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v6_7.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v8_10.c355
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v8_10.h9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v8_14.c160
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v8_14.h51
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v8_7.c63
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umsch_mm_v4_0.c434
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umsch_mm_v4_0.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c102
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c71
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c78
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c102
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c352
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v1_0.c839
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v1_0.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v2_0.c75
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v3_0.c102
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v4_0.c330
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_sw_ring.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c455
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c427
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c1479
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c907
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c1433
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0.h6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c2131
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.h44
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c1723
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.h35
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c1442
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.h37
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c1729
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.h39
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vega10_ih.c64
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vega20_ih.c217
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vi.c188
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vid.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vpe_6_1_fw_if.h218
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vpe_v6_1.c398
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vpe_v6_1.h29
-rw-r--r--drivers/gpu/drm/amd/amdkfd/Kconfig3
-rw-r--r--drivers/gpu/drm/amd/amdkfd/Makefile11
-rw-r--r--drivers/gpu/drm/amd/amdkfd/cik_event_interrupt.c23
-rw-r--r--drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h4769
-rw-r--r--drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx10.asm497
-rw-r--r--drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm1136
-rw-r--r--drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx9.asm121
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c951
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_crat.c873
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_crat.h50
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_debug.c1152
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_debug.h142
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_debugfs.c78
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device.c1005
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c1940
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h84
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_cik.c150
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_v10.c45
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_v11.c43
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_v12.c90
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_v9.c76
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_vi.c204
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c251
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.c276
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.h1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c60
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_int_process_v10.c386
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_int_process_v11.c146
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c372
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c96
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_iommu.c348
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_iommu.h84
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c72
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.h4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_migrate.c494
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_migrate.h16
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_module.c3
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c128
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h48
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c47
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c67
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c265
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c459
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c597
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c52
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c204
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c175
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_vi.c7
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_pasid.c70
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_pm4_headers_ai.h79
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_pm4_headers_aldebaran.h2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_pm4_headers_diq.h291
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_priv.h507
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process.c819
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c367
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c385
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c146
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h29
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c1207
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.h58
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.c962
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.h51
-rw-r--r--drivers/gpu/drm/amd/amdkfd/soc15_int.h2
-rw-r--r--drivers/gpu/drm/amd/amdxcp/Makefile25
-rw-r--r--drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c152
-rw-r--r--drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.h30
-rw-r--r--drivers/gpu/drm/amd/display/Kconfig45
-rw-r--r--drivers/gpu/drm/amd/display/Makefile20
-rw-r--r--drivers/gpu/drm/amd/display/TODO110
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/Makefile22
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c7100
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h424
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c1776
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c209
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h36
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c687
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h70
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c458
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h14
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c1224
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c503
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h18
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c867
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c153
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c1116
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h41
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c1288
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h21
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c13
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c114
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c178
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c209
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.h49
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h93
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c215
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h36
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c66
-rw-r--r--drivers/gpu/drm/amd/display/dc/Makefile59
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/Makefile9
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/bw_fixed.c (renamed from drivers/gpu/drm/amd/display/dc/dml/calcs/bw_fixed.c)27
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/calcs_logger.h (renamed from drivers/gpu/drm/amd/display/dc/dml/calcs/calcs_logger.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/conversion.c39
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/conversion.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/custom_float.c173
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/dc_common.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/dce_calcs.c (renamed from drivers/gpu/drm/amd/display/dc/dml/calcs/dce_calcs.c)16
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/fixpt31_32.c38
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/vector.c17
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/bios_parser.c199
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c510
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table.c292
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table2.c107
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table2.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c104
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h8
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c104
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c106
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c104
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.c104
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c104
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/Makefile22
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c89
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c22
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c44
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dce112/dce112_clk_mgr.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dce120/dce120_clk_mgr.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c36
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_clk.c79
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.c25
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c68
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn201/dcn201_clk_mgr.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c93
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr_vbios_smu.c46
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr_vbios_smu.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dalsmc.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c53
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr_smu_msg.c9
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_smu11_driver_if.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30m_clk_mgr.c36
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30m_clk_mgr.h31
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30m_clk_mgr_smu_msg.c118
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30m_clk_mgr_smu_msg.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/dcn301_smu.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn301/vg_clk_mgr.c100
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c115
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_smu.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c371
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_smu.c33
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_smu.h42
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c333
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c59
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c144
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_smu.c22
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dalsmc.h11
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c591
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr_smu_msg.c173
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr_smu_msg.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_smu13_driver_if.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/smu13_driver_if.h108
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn351_clk_mgr.c141
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c1588
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.h67
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_smu.c508
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_smu.h220
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dalsmc.h55
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c1631
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h117
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.c472
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr_smu_msg.h41
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_smu14_driver_if.h66
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c4979
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_debug.c208
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c3699
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link.c4918
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c774
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c7593
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_dpcd.c246
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_dpia.c1009
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c133
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c530
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c4041
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_stat.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_state.c1077
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_stream.c844
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_surface.c126
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_vm_helper.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h2325
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_bios_types.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_ddc_types.h31
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c2133
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h308
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dp_types.h681
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dsc.h23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_fused_io.c148
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_fused_io.h31
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h133
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_helper.c120
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_hw_types.h423
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_link.h521
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_plane.h51
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_plane_priv.h35
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_spl_translate.c231
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_spl_translate.h23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_state.h77
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_state_priv.h128
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream.h230
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream_priv.h75
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_trace.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_types.h624
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/Makefile103
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.c)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.h526
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_dccg.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn201/dcn201_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_dccg.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_dccg.c)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_dccg.h)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dccg.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dccg.h)21
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.c (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_dccg.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn301/dcn301_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_dccg.h)6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn302/dcn302_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_dccg.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn303/dcn303_dccg.h64
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c878
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dccg.h)15
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c407
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.h211
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dccg.c)91
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dccg.h)42
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c2486
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.h256
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c915
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.h249
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/Makefile6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_abm.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_abm.h117
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_audio.c311
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_audio.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_aux.c64
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_aux.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c201
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c35
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c103
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c18
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c105
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c50
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_opp.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_opp.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_transform.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_transform.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c282
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_abm_lcd.c318
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_abm_lcd.h54
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c46
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c101
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_psr.h8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c473
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h42
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce100/Makefile46
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c142
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h50
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/Makefile6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c3056
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h95
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c224
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce112/Makefile5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c163
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce120/Makefile5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c271
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c126
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce60/Makefile5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.c432
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce80/Makefile5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c54
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/Makefile7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c73
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dwb.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dwb.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c3959
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h209
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.h722
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/Makefile10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.h316
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dsc.h781
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb.h363
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dwb_scl.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c2793
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn20/dcn20_vmid.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn201/Makefile10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn201/dcn201_link_encoder.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn201/dcn201_link_encoder.h14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn201/dcn201_mpc.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn201/dcn201_opp.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn201/dcn201_opp.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn21/Makefile6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hwseq.c246
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn21/dcn21_link_encoder.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/Makefile24
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_afmt.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c282
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hubp.c530
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c980
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mmhubbub.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mmhubbub.h15
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_vpg.h23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn301/Makefile14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn301/dcn301_panel_cntl.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn302/Makefile12
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/Makefile13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/dcn303_dccg.h46
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/dcn303_hwseq.c45
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/dcn303_hwseq.h18
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/dcn303_init.c22
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/dcn303_init.h15
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn303/dcn303_resource.h20
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/Makefile15
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_apg.c54
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_apg.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dccg.c728
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.c623
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_optc.c312
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_panel_cntl.c48
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_panel_cntl.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_vpg.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn31/dcn31_vpg.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn314/Makefile18
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dccg.c283
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dccg.h159
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.c404
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn315/Makefile30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn316/Makefile30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/Makefile20
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dpp.c164
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubp.c208
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c1276
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.h87
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c2482
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.h115
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c255
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn321/Makefile17
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn321/dcn321_resource.c1975
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/Makefile117
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_link_encoder.c)17
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_link_encoder.h)37
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_stream_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.c)24
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_stream_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.h)43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_link_encoder.c)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_link_encoder.h)6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_stream_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c)23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_stream_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dio_link_encoder.c)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dio_link_encoder.h)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_stream_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dio_stream_encoder.c)47
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_stream_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dio_stream_encoder.h)10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn301/dcn301_dio_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_dio_link_encoder.c)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn301/dcn301_dio_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_dio_link_encoder.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.c)23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_dio_link_encoder.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn314/dcn314_dio_stream_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dio_stream_encoder.c)86
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn314/dcn314_dio_stream_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dio_stream_encoder.h)44
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_link_encoder.c)90
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_link_encoder.h)17
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_stream_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_stream_encoder.c)81
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_stream_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_stream_encoder.h)85
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn321/dcn321_dio_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn321/dcn321_dio_link_encoder.c)11
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn321/dcn321_dio_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn321/dcn321_dio_link_encoder.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_link_encoder.c391
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_link_encoder.h188
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.c520
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_stream_encoder.h332
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.c322
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.h134
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_stream_encoder.c856
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_stream_encoder.h240
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_cp_psp.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_helpers.h55
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_pp_smu.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_services.h31
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_services_types.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/Makefile72
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/calcs/custom_float.c197
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calc_auto.c22
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calc_math.c16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c289
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dc_features.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.c36
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn10/dcn10_fpu.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c508
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20v2.c27
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20.c30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn20/display_rq_dlg_calc_20v2.c30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn21/display_mode_vba_21.c27
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn21/display_rq_dlg_calc_21.c30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c178
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c395
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn30/display_rq_dlg_calc_30.c33
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn301/dcn301_fpu.c103
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn301/dcn301_fpu.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c12
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.c383
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c444
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn31/display_rq_dlg_calc_31.c38
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c91
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c836
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn314/display_rq_dlg_calc_314.c73
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c2322
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c188
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c312
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.h36
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/display_rq_dlg_calc_32.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c724
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c620
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.h44
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn351/dcn351_fpu.c639
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn351/dcn351_fpu.h19
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h33
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.c26
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_mode_lib.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h48
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.c24
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_mode_vba.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/display_rq_dlg_helpers.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dml1_display_rq_dlg_calc.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h17
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dml_wrapper.c1884
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dml_wrapper_translation.c284
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dsc/qp_tables.h36
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/Makefile140
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/cmntypes.h94
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c10337
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.h204
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core_structs.h2032
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_lib_defines.h79
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c798
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.h74
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c929
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.h28
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c516
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h50
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c466
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h135
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn4_soc_bb.h372
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml2_external_lib_deps.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top.h46
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h191
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h526
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_policy_types.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h215
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h744
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c661
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c13342
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h39
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c39
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h2341
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c788
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c785
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c50
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c198
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c39
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.c706
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.h22
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c2390
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h33
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c83
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c147
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.h25
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.c49
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c1170
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.h14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_debug.h189
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h1010
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c1174
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.h52
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_types.h43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_internal_types.h157
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c911
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.h52
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.c311
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.h47
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c1528
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.h41
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c560
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.h149
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c704
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.h309
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_assert.h32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_depedencies.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c573
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.h63
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_logging.h32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/Makefile83
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c)73
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h)16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c)80
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_dscl.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_dscl.c)29
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dpp.c)89
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dpp.h)14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dpp_cm.c)67
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn201/dcn201_dpp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_dpp.c)23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn201/dcn201_dpp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_dpp.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp.c)136
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp.h)15
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp_cm.c)63
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.c239
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dpp.h)7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.c150
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.h67
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.c429
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.h740
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c242
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_dscl.c1186
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/Makefile35
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c481
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dsc.c)134
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.h619
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c142
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.h59
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c394
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.h346
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dsc.h (renamed from drivers/gpu/drm/amd/display/dc/inc/hw/dsc.h)10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dscc_types.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c17
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/Makefile46
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb.c)40
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb.h)30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb_cm.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb_cm.c)5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn35/dcn35_dwb.c57
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn35/dcn35_dwb.h61
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/Makefile10
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_factory_dcn20.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c15
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_factory_dcn32.c24
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_factory_dcn401.c264
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_factory_dcn401.h11
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c335
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/ddc_regs.h47
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c38
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hpd_regs.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_generic.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_hpd.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/hdcp/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/Makefile50
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/dcn31/dcn31_hpo_dp_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hpo_dp_link_encoder.c)24
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/dcn31/dcn31_hpo_dp_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hpo_dp_link_encoder.h)16
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/dcn31/dcn31_hpo_dp_stream_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hpo_dp_stream_encoder.c)71
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/dcn31/dcn31_hpo_dp_stream_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hpo_dp_stream_encoder.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/dcn32/dcn32_hpo_dp_link_encoder.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hpo_dp_link_encoder.c)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hpo/dcn32/dcn32_hpo_dp_link_encoder.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hpo_dp_link_encoder.h)5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/Makefile104
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubbub.c)38
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubbub.h)101
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubbub.c)51
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubbub.h)5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn201/dcn201_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_hubbub.c)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn201/dcn201_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_hubbub.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubbub.c)30
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubbub.h)8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hubbub.c)20
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hubbub.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn301/dcn301_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_hubbub.c)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn301/dcn301_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_hubbub.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.c)96
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubbub.h)14
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubbub.c)142
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubbub.h)106
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c664
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h172
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c1271
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h206
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/Makefile97
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c)70
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h)171
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c)175
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.h)61
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn201/dcn201_hubp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_hubp.c)9
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn201/dcn201_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_hubp.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubp.c)15
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubp.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c678
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hubp.h)19
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.c)24
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hubp.h)9
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c246
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubp.h)10
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c244
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.h76
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c1097
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h373
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/Makefile202
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce/dce_hwseq.c (renamed from drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce/dce_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h)151
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce100/dce100_hwseq.c172
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce100/dce100_hwseq.h54
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c3503
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h125
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce112/dce112_hwseq.c160
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce112/dce112_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce120/dce120_hwseq.c270
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce120/dce120_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c433
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce80/dce80_hwseq.c55
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce80/dce80_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c4147
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h221
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_init.c)11
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c3256
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.h)37
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.c)22
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_hwseq.c)108
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_hwseq.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_init.c)14
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_hwseq.c302
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hwseq.h)5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_init.c)23
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c1282
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h)19
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c)26
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_hwseq.c (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_hwseq.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_hwseq.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c)23
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn301/dcn301_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.c (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_hwseq.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_hwseq.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_init.c)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c64
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.h37
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_init.c40
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_init.h33
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c717
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_hwseq.h)5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_init.c)26
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c603
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.h)10
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c)32
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c1848
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h135
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c)54
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c1728
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h112
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c184
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn351/Makefile28
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_hwseq.c182
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_hwseq.h41
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c172
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.h33
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c4045
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h213
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c177
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h2025
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h242
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/clock_source.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_status.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_types.h332
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h133
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/dc_link_dp.h262
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/dc_link_dpia.h105
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/dcn_calcs.h25
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/dml_wrapper.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/abm.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/audio.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/aux_engine.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h77
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h146
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/cursor_reg_cache.h121
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h195
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h120
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h144
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dwb.h42
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h118
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h79
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h65
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h27
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h963
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/opp.h54
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/optc.h197
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/panel_cntl.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/pg_cntl.h55
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h38
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h211
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/transform.h20
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/vpg.h53
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h283
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw_sequencer_private.h176
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/link_enc_cfg.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/link_hwss.h11
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/link_service.h350
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/reg_helper.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/resource.h486
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h24
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/Makefile36
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dce110/irq_service_dce110.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dce120/irq_service_dce120.c41
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dce60/irq_service_dce60.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dce80/irq_service_dce80.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn10/irq_service_dcn10.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn20/irq_service_dcn20.c45
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn201/irq_service_dcn201.c61
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn201/irq_service_dcn201.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn21/irq_service_dcn21.c47
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn30/irq_service_dcn30.c46
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn302/irq_service_dcn302.c35
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.c52
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn303/irq_service_dcn303.h19
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn31/irq_service_dcn31.c45
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn314/irq_service_dcn314.c52
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn315/irq_service_dcn315.c45
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn32/irq_service_dcn32.c102
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.c400
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn35/irq_service_dcn35.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.c382
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn351/irq_service_dcn351.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.c381
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn36/irq_service_dcn36.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.c414
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/dcn401/irq_service_dcn401.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/irq_service.c83
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq/irq_service.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/irq_types.h31
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/Makefile38
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c1019
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.h44
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_trace.c (renamed from drivers/gpu/drm/amd/display/dc/link/link_dp_trace.c)24
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_trace.h (renamed from drivers/gpu/drm/amd/display/dc/link/link_dp_trace.h)15
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c353
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.h (renamed from drivers/gpu/drm/amd/display/dc/link/link_hwss_dio.h)11
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio_fixed_vs_pe_retimer.c199
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio_fixed_vs_pe_retimer.h37
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c176
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.h (renamed from drivers/gpu/drm/amd/display/dc/link/link_hwss_dpia.h)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.c228
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_dp.h62
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.c233
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.h33
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.c1644
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.h43
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.c2674
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.h53
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_factory.c916
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_factory.h31
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_hwss_dio.c196
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_hwss_dpia.c79
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_hwss_hpo_dp.c290
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_hwss_hpo_dp.h35
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_hwss_hpo_frl.h34
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_resource.c114
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_resource.h32
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_validation.c626
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_validation.h45
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c604
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h106
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c2599
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h117
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c171
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.h47
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c448
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h112
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c548
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.h41
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c210
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.h60
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c1816
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.h201
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c267
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.h43
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c491
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.h62
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_auxless.c80
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_auxless.h35
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c1046
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.h60
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_fixed_vs_pe_retimer.c553
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_fixed_vs_pe_retimer.h45
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c250
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.h (renamed from drivers/gpu/drm/amd/display/dc/inc/link_dpcd.h)5
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c1426
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h81
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c240
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.h54
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/Makefile54
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn20/dcn20_mmhubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mmhubbub.c)7
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn20/dcn20_mmhubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mmhubbub.h)7
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mmhubbub.c)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mmhubbub.h)14
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn35/dcn35_mmhubbub.c59
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn35/dcn35_mmhubbub.h75
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/Makefile72
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c)21
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn10/dcn10_mpc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c)36
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mpc.c)189
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mpc.h)17
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mpc.c)430
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mpc.h)84
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c635
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h257
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/Makefile51
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.c)51
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_opp.h)8
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_opp.c)52
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_opp.h)12
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.c66
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.h69
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/Makefile114
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.c)342
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h686
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.c)54
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.h)12
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn201/dcn201_optc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_optc.c)17
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn201/dcn201_optc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_optc.h)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_optc.c)99
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_optc.h)18
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn301/dcn301_optc.c190
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn301/dcn301_optc.h36
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c526
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_optc.h)16
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c)20
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.h)11
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c)108
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.h (renamed from drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.h)85
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c538
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.h83
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c552
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.h196
-rw-r--r--drivers/gpu/drm/amd/display/dc/os_types.h8
-rw-r--r--drivers/gpu/drm/amd/display/dc/pg/Makefile35
-rw-r--r--drivers/gpu/drm/amd/display/dc/pg/dcn35/dcn35_pg_cntl.c573
-rw-r--r--drivers/gpu/drm/amd/display/dc/pg/dcn35/dcn35_pg_cntl.h195
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/Makefile225
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c)74
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h)9
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c)38
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c)53
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c)46
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c)130
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c)104
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c)228
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.h)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c)344
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.h)16
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_resource.c)112
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn201/dcn201_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c)152
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c)184
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.h)9
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.c)114
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_resource.c)96
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn302/dcn302_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn303/dcn303_resource.c)118
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.h38
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c)214
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.h)15
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c)261
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.h)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn315/dcn315_resource.c)243
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn315/dcn315_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c (renamed from drivers/gpu/drm/amd/display/dc/dcn316/dcn316_resource.c)100
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn316/dcn316_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c2931
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h1281
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource_helpers.c780
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c2078
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.h (renamed from drivers/gpu/drm/amd/display/dc/dcn321/dcn321_resource.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c2223
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.h315
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c2196
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.h23
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c2196
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.h73
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c2282
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h656
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile19
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c304
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h22
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c27
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c37
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/Makefile33
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c1925
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl.h27
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_filters.c15
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_filters.h15
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_isharp_filters.c553
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_isharp_filters.h42
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_scl_easf_filters.c2586
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_scl_easf_filters.h37
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_scl_filters.c1233
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_scl_filters.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h560
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_custom_float.c151
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_custom_float.h29
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h30
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_fixpt31_32.c493
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_fixpt31_32.h522
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_os_types.h56
-rw-r--r--drivers/gpu/drm/amd/display/dc/virtual/virtual_link_hwss.c8
-rw-r--r--drivers/gpu/drm/amd/display/dmub/dmub_srv.h371
-rw-r--r--drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h4224
-rw-r--r--drivers/gpu/drm/amd/display/dmub/inc/dmub_subvp_state.h183
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/Makefile6
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c92
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.h7
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.c6
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.h4
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.c3
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn30.h3
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn303.c19
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn303.h19
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c135
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.h11
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.c67
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.h35
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c245
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.h33
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c611
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.h290
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn351.c34
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn351.h13
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn36.c34
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn36.h13
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c678
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.h290
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_reg.h1
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c686
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_srv_stat.c20
-rw-r--r--drivers/gpu/drm/amd/display/include/audio_types.h17
-rw-r--r--drivers/gpu/drm/amd/display/include/bios_parser_types.h11
-rw-r--r--drivers/gpu/drm/amd/display/include/dal_asic_id.h24
-rw-r--r--drivers/gpu/drm/amd/display/include/dal_types.h5
-rw-r--r--drivers/gpu/drm/amd/display/include/ddc_service_types.h11
-rw-r--r--drivers/gpu/drm/amd/display/include/dpcd_defs.h48
-rw-r--r--drivers/gpu/drm/amd/display/include/fixed31_32.h8
-rw-r--r--drivers/gpu/drm/amd/display/include/gpio_service_interface.h3
-rw-r--r--drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h5
-rw-r--r--drivers/gpu/drm/amd/display/include/grph_object_id.h11
-rw-r--r--drivers/gpu/drm/amd/display/include/hdcp_msg_types.h108
-rw-r--r--drivers/gpu/drm/amd/display/include/hdcp_types.h103
-rw-r--r--drivers/gpu/drm/amd/display/include/i2caux_interface.h82
-rw-r--r--drivers/gpu/drm/amd/display/include/link_service_types.h62
-rw-r--r--drivers/gpu/drm/amd/display/include/logger_interface.h9
-rw-r--r--drivers/gpu/drm/amd/display/include/logger_types.h148
-rw-r--r--drivers/gpu/drm/amd/display/include/signal_types.h53
-rw-r--r--drivers/gpu/drm/amd/display/modules/color/color_gamma.c569
-rw-r--r--drivers/gpu/drm/amd/display/modules/color/color_gamma.h14
-rw-r--r--drivers/gpu/drm/amd/display/modules/freesync/freesync.c318
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c11
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h4
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c41
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c95
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c53
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c99
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c122
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h18
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c10
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h10
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h54
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h176
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h39
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_stats.h6
-rw-r--r--drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c66
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_helpers.c204
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_helpers.h27
-rw-r--r--drivers/gpu/drm/amd/include/amd_acpi.h4
-rw-r--r--drivers/gpu/drm/amd/include/amd_cper.h269
-rw-r--r--drivers/gpu/drm/amd/include/amd_pcie.h18
-rw-r--r--drivers/gpu/drm/amd/include/amd_shared.h228
-rw-r--r--drivers/gpu/drm/amd/include/amdgpu_reg_state.h153
-rw-r--r--drivers/gpu/drm/amd/include/arct_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/athub/athub_1_8_0_offset.h411
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/athub/athub_1_8_0_sh_mask.h1807
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/athub/athub_4_1_0_offset.h287
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/athub/athub_4_1_0_sh_mask.h1348
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dce/dce_6_0_d.h7
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dce/dce_6_0_sh_mask.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_2_0_1_offset.h6193
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_2_0_1_sh_mask.h22091
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_2_0_3_offset.h6193
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_2_0_3_sh_mask.h22091
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_0_0_offset.h142
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_0_0_sh_mask.h76
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_0_2_offset.h114
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_0_2_sh_mask.h57
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_0_3_offset.h44
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_0_3_sh_mask.h34
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_sh_mask.h19
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_sh_mask.h10
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_sh_mask.h10
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h60
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_sh_mask.h55
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h37
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_sh_mask.h16
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h15279
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_sh_mask.h53485
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h15259
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_sh_mask.h53464
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h15485
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_sh_mask.h61940
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h16662
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_sh_mask.h145870
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/df/df_4_15_offset.h28
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/df/df_4_15_sh_mask.h28
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/df/df_4_3_offset.h30
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/df/df_4_3_sh_mask.h157
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_0_0_offset.h24
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_0_0_sh_mask.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_0_3_sh_mask.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h10
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_1_0_offset.h30
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_1_0_sh_mask.h123
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_3_0_offset.h122
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_3_0_sh_mask.h1527
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_11_0_0_offset.h12
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_11_0_0_sh_mask.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_11_0_3_offset.h12094
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_11_0_3_sh_mask.h44690
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_11_5_0_offset.h10002
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_11_5_0_sh_mask.h36579
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_12_0_0_offset.h11061
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_12_0_0_sh_mask.h40550
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_9_0_sh_mask.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_9_4_3_offset.h7450
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gc/gc_9_4_3_sh_mask.h31649
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/gca/gfx_6_0_d.h2
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/hdp/hdp_4_4_2_offset.h219
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/hdp/hdp_4_4_2_sh_mask.h663
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/hdp/hdp_7_0_0_offset.h219
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/hdp/hdp_7_0_0_sh_mask.h735
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/lsdma/lsdma_7_0_0_offset.h388
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/lsdma/lsdma_7_0_0_sh_mask.h1411
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_1_0_offset.h23
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_1_8_0_offset.h3366
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_1_8_0_sh_mask.h22628
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_3_3_0_offset.h1395
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_3_3_0_sh_mask.h6722
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_4_1_0_offset.h1341
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_4_1_0_sh_mask.h6943
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_9_4_1_offset.h36
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mmhub/mmhub_9_4_1_sh_mask.h56
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_13_0_2_sh_mask.h28
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_13_0_6_offset.h456
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_13_0_6_sh_mask.h702
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_14_0_0_offset.h359
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_14_0_0_sh_mask.h534
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_14_0_2_offset.h468
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/mp/mp_14_0_2_sh_mask.h692
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/nbif/nbif_6_3_1_offset.h11287
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/nbif/nbif_6_3_1_sh_mask.h32806
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h9406
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_sh_mask.h57899
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_9_0_offset.h10004
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_9_0_sh_mask.h38900
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/oss_1_0_d.h23
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/oss_1_0_sh_mask.h41
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_2_0_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_2_0_sh_mask.h11
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_4_2_offset.h263
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_4_2_sh_mask.h995
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_6_0_0_offset.h4
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_6_0_0_sh_mask.h10
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_6_1_0_offset.h279
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_6_1_0_sh_mask.h1019
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_7_0_0_offset.h279
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/oss/osssys_7_0_0_sh_mask.h1029
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/pcie/pcie_6_1_0_offset.h630
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/pcie/pcie_6_1_0_sh_mask.h4250
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h1113
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_sh_mask.h3300
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smu/smu_6_0_d.h44
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smu/smu_6_0_sh_mask.h188
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_10_0_2_offset.h102
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_10_0_2_sh_mask.h184
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_13_0_3_offset.h177
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_13_0_3_sh_mask.h428
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_14_0_2_offset.h511
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/smuio/smuio_14_0_2_sh_mask.h1106
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/thm/thm_14_0_2_offset.h228
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/thm/thm_14_0_2_sh_mask.h940
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/umc/umc_12_0_0_offset.h33
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/umc/umc_12_0_0_sh_mask.h95
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/umc/umc_8_10_0_offset.h2
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/umc/umc_8_10_0_sh_mask.h3
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/umc/umc_8_14_0_offset.h29
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/umc/umc_8_14_0_sh_mask.h37
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/uvd/uvd_4_0_sh_mask.h2
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_d.h5
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_sh_mask.h10
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_2_5_offset.h3
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_2_5_sh_mask.h27
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_4_0_0_offset.h422
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_4_0_0_sh_mask.h882
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_4_0_3_offset.h2367
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_4_0_3_sh_mask.h10919
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_4_0_5_offset.h1797
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_4_0_5_sh_mask.h8614
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_5_0_0_offset.h1694
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vcn/vcn_5_0_0_sh_mask.h7666
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vpe/vpe_6_1_0_offset.h1553
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vpe/vpe_6_1_0_sh_mask.h4393
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/xgmi/xgmi_6_1_0_sh_mask.h87
-rw-r--r--drivers/gpu/drm/amd/include/atom-bits.h2
-rw-r--r--drivers/gpu/drm/amd/include/atombios.h40
-rw-r--r--drivers/gpu/drm/amd/include/atomfirmware.h215
-rw-r--r--drivers/gpu/drm/amd/include/beige_goby_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/cgs_common.h23
-rw-r--r--drivers/gpu/drm/amd/include/cyan_skillfish_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/dimgrey_cavefish_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/discovery.h140
-rw-r--r--drivers/gpu/drm/amd/include/dm_pp_interface.h10
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/gfx/irqsrcs_gfx_11_0_0.h3
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/gfx/irqsrcs_gfx_12_0_0.h74
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/isp/irqsrcs_isp_4_1.h62
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_4_0.h3
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_5_0.h47
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/vpe/irqsrcs_vpe_6_1.h40
-rw-r--r--drivers/gpu/drm/amd/include/kgd_kfd_interface.h101
-rw-r--r--drivers/gpu/drm/amd/include/kgd_pp_interface.h1024
-rw-r--r--drivers/gpu/drm/amd/include/mes_api_def.h570
-rw-r--r--drivers/gpu/drm/amd/include/mes_v11_api_def.h118
-rw-r--r--drivers/gpu/drm/amd/include/mes_v12_api_def.h907
-rw-r--r--drivers/gpu/drm/amd/include/navi12_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/navi14_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/pptable.h95
-rw-r--r--drivers/gpu/drm/amd/include/renoir_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/sienna_cichlid_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/soc15_hw_ip.h1
-rw-r--r--drivers/gpu/drm/amd/include/soc15_ih_clientid.h2
-rw-r--r--drivers/gpu/drm/amd/include/soc21_enum.h2
-rw-r--r--drivers/gpu/drm/amd/include/soc24_enum.h21073
-rw-r--r--drivers/gpu/drm/amd/include/umsch_mm_4_0_api_def.h446
-rw-r--r--drivers/gpu/drm/amd/include/v10_structs.h3
-rw-r--r--drivers/gpu/drm/amd/include/v11_structs.h24
-rw-r--r--drivers/gpu/drm/amd/include/v12_structs.h1189
-rw-r--r--drivers/gpu/drm/amd/include/v9_structs.h30
-rw-r--r--drivers/gpu/drm/amd/include/vangogh_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/vega10_ip_offset.h6
-rw-r--r--drivers/gpu/drm/amd/include/vega20_ip_offset.h78
-rw-r--r--drivers/gpu/drm/amd/include/yellow_carp_offset.h7
-rw-r--r--drivers/gpu/drm/amd/pm/Makefile1
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c569
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm_internal.c86
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c3152
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h82
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm_internal.h6
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_pm.h43
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/kv_dpm.c222
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/legacy_dpm.c198
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/legacy_dpm.h7
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/r600_dpm.h3
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c642
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.h557
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_smc.c66
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/sislands_smc.h63
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c196
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ci_baco.c21
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/common_baco.c2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/fiji_baco.c24
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c14
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/polaris_baco.c30
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pp_psm.c23
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c475
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.h8
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c82
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppevvmath.h555
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/pptable_v1_0.h39
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c72
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.c5
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_baco.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.c2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_clockpowergating.h1
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c238
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_powertune.c27
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_thermal.c8
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c48
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.c5
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu9_baco.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c31
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/tonga_baco.c30
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_baco.c26
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c176
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_powertune.c130
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_pptable.h42
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c7
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c31
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_baco.c12
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c61
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.h3
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_pptable.h11
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.c12
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_baco.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c93
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_pptable.h19
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c575
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c10
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/hardwaremanager.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h39
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/power_state.h1
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/pp_thermal.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu11_driver_if.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu7.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu71.h22
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu73.h45
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu73_discrete.h73
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu75.h12
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu7_fusion.h42
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/smu9_driver_if.h22
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/vega12/smu9_driver_if.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c9
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c12
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/polaris10_smumgr.c11
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c19
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c38
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/tonga_smumgr.c9
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c10
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c28
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c8
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vegam_smumgr.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/Makefile2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c1595
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h477
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu11_driver_if_arcturus.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu11_driver_if_navi10.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu11_driver_if_sienna_cichlid.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu11_driver_if_vangogh.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_aldebaran.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h152
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_4.h19
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_5.h3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_6.h243
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h148
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_yellow_carp.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu14_driver_if_v14_0.h1889
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu14_driver_if_v14_0_0.h263
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_0_7_ppsmc.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h11
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h378
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h146
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_4_ppsmc.h15
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_pmfw.h471
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h123
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v14_0_0_pmfw.h194
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v14_0_0_ppsmc.h141
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v14_0_2_ppsmc.h150
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_11_0_cdr_table.h6
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h88
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h27
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0_pptable.h15
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v12_0.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h83
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0_0_pptable.h (renamed from drivers/gpu/drm/amd/pm/inc/smu_v13_0_0_pptable.h)37
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0_7_pptable.h37
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h249
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0_2_pptable.h204
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c985
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c19
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c460
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c1843
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c245
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c562
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c195
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c11
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/Makefile2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c510
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c769
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c1991
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c1073
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c183
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c160
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c4086
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h265
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c1556
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c231
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.h1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/Makefile30
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c1977
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c1723
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.h28
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c2932
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.h28
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c304
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h152
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_internal.h15
-rw-r--r--drivers/gpu/drm/amd/ras/Makefile34
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/Makefile33
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c285
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.h54
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c182
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.h27
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c648
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h83
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c94
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.h30
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.c125
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.h30
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.c190
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.h41
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c279
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h110
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/Makefile44
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras.h370
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca.c672
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca.h164
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c379
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.h71
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cmd.c522
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cmd.h426
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_core.c603
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_core_status.h37
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cper.c315
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cper.h304
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c1339
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h197
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx.c70
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx.h43
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.c426
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.h259
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_log_ring.c317
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_log_ring.h93
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1.c81
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1.h50
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c105
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.h30
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio.c96
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio.h46
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.c123
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.h31
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_process.c322
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_process.h53
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp.c750
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp.h145
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.c46
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.h31
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_ta_if.h231
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.c707
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.h166
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c511
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h314
-rw-r--r--drivers/gpu/drm/arm/Kconfig8
-rw-r--r--drivers/gpu/drm/arm/display/Kconfig3
-rw-r--r--drivers/gpu/drm/arm/display/include/malidp_utils.h2
-rw-r--r--drivers/gpu/drm/arm/display/komeda/Makefile4
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_component.c2
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c5
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c5
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_crtc.c86
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_dev.c10
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_drv.c74
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c16
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h1
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_kms.c64
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_kms.h8
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c5
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c37
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_plane.c1
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c6
-rw-r--r--drivers/gpu/drm/arm/hdlcd_crtc.c60
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.c163
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.h2
-rw-r--r--drivers/gpu/drm/arm/malidp_crtc.c9
-rw-r--r--drivers/gpu/drm/arm/malidp_drv.c113
-rw-r--r--drivers/gpu/drm/arm/malidp_drv.h2
-rw-r--r--drivers/gpu/drm/arm/malidp_hw.c10
-rw-r--r--drivers/gpu/drm/arm/malidp_mw.c22
-rw-r--r--drivers/gpu/drm/arm/malidp_planes.c71
-rw-r--r--drivers/gpu/drm/arm/malidp_regs.h2
-rw-r--r--drivers/gpu/drm/armada/Kconfig2
-rw-r--r--drivers/gpu/drm/armada/Makefile3
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c31
-rw-r--r--drivers/gpu/drm/armada/armada_debugfs.c2
-rw-r--r--drivers/gpu/drm/armada/armada_drm.h13
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c35
-rw-r--r--drivers/gpu/drm/armada/armada_fb.c14
-rw-r--r--drivers/gpu/drm/armada/armada_fb.h4
-rw-r--r--drivers/gpu/drm/armada/armada_fbdev.c112
-rw-r--r--drivers/gpu/drm/armada/armada_gem.c17
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c15
-rw-r--r--drivers/gpu/drm/armada/armada_plane.c10
-rw-r--r--drivers/gpu/drm/aspeed/Kconfig3
-rw-r--r--drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c11
-rw-r--r--drivers/gpu/drm/aspeed/aspeed_gfx_drv.c38
-rw-r--r--drivers/gpu/drm/aspeed/aspeed_gfx_out.c1
-rw-r--r--drivers/gpu/drm/ast/Kconfig9
-rw-r--r--drivers/gpu/drm/ast/Makefile20
-rw-r--r--drivers/gpu/drm/ast/ast_2000.c257
-rw-r--r--drivers/gpu/drm/ast/ast_2100.c480
-rw-r--r--drivers/gpu/drm/ast/ast_2200.c92
-rw-r--r--drivers/gpu/drm/ast/ast_2300.c1463
-rw-r--r--drivers/gpu/drm/ast/ast_2400.c100
-rw-r--r--drivers/gpu/drm/ast/ast_2500.c675
-rw-r--r--drivers/gpu/drm/ast/ast_2600.c116
-rw-r--r--drivers/gpu/drm/ast/ast_cursor.c314
-rw-r--r--drivers/gpu/drm/ast/ast_ddc.c187
-rw-r--r--drivers/gpu/drm/ast/ast_ddc.h11
-rw-r--r--drivers/gpu/drm/ast/ast_dp.c676
-rw-r--r--drivers/gpu/drm/ast/ast_dp501.c318
-rw-r--r--drivers/gpu/drm/ast/ast_dram_tables.h207
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c365
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h595
-rw-r--r--drivers/gpu/drm/ast/ast_i2c.c152
-rw-r--r--drivers/gpu/drm/ast/ast_main.c479
-rw-r--r--drivers/gpu/drm/ast/ast_mm.c44
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c1751
-rw-r--r--drivers/gpu/drm/ast/ast_post.c2080
-rw-r--r--drivers/gpu/drm/ast/ast_post.h50
-rw-r--r--drivers/gpu/drm/ast/ast_reg.h78
-rw-r--r--drivers/gpu/drm/ast/ast_sil164.c126
-rw-r--r--drivers/gpu/drm/ast/ast_tables.h240
-rw-r--r--drivers/gpu/drm/ast/ast_vbios.c241
-rw-r--r--drivers/gpu/drm/ast/ast_vbios.h108
-rw-r--r--drivers/gpu/drm/ast/ast_vga.c126
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/Kconfig5
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c198
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c153
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h133
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c3
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c421
-rw-r--r--drivers/gpu/drm/bridge/Kconfig130
-rw-r--r--drivers/gpu/drm/bridge/Makefile13
-rw-r--r--drivers/gpu/drm/bridge/adv7511/Kconfig5
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511.h84
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_audio.c84
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_cec.c89
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c632
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7533.c56
-rw-r--r--drivers/gpu/drm/bridge/analogix/Kconfig2
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-anx6345.c73
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c92
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c2
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c615
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.h35
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c209
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h9
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c554
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.h24
-rw-r--r--drivers/gpu/drm/bridge/aux-bridge.c150
-rw-r--r--drivers/gpu/drm/bridge/aux-hpd-bridge.c211
-rw-r--r--drivers/gpu/drm/bridge/cadence/Kconfig23
-rw-r--r--drivers/gpu/drm/bridge/cadence/Makefile3
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c1410
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-dsi-core.h82
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.c51
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-dsi-j721e.h16
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c253
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h2
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c33
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.h3
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c9
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h2
-rw-r--r--drivers/gpu/drm/bridge/cdns-dsi.c1330
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c85
-rw-r--r--drivers/gpu/drm/bridge/chrontel-ch7033.c38
-rw-r--r--drivers/gpu/drm/bridge/cros-ec-anx7688.c14
-rw-r--r--drivers/gpu/drm/bridge/display-connector.c116
-rw-r--r--drivers/gpu/drm/bridge/fsl-ldb.c211
-rw-r--r--drivers/gpu/drm/bridge/imx/Kconfig55
-rw-r--r--drivers/gpu/drm/bridge/imx/Makefile10
-rw-r--r--drivers/gpu/drm/bridge/imx/imx-ldb-helper.c33
-rw-r--r--drivers/gpu/drm/bridge/imx/imx-ldb-helper.h5
-rw-r--r--drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c91
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c158
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c207
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c213
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qm-ldb-drv.c588
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qm-ldb.c591
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-ldb-drv.c723
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c721
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c53
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c32
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c28
-rw-r--r--drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c915
-rw-r--r--drivers/gpu/drm/bridge/ite-it6263.c930
-rw-r--r--drivers/gpu/drm/bridge/ite-it6505.c1012
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c412
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8912b.c237
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9211.c34
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c615
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611uxc.c171
-rw-r--r--drivers/gpu/drm/bridge/lvds-codec.c31
-rw-r--r--drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c134
-rw-r--r--drivers/gpu/drm/bridge/microchip-lvds.c229
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.c35
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.h4
-rw-r--r--drivers/gpu/drm/bridge/nxp-ptn3460.c59
-rw-r--r--drivers/gpu/drm/bridge/panel.c186
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8622.c21
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c165
-rw-r--r--drivers/gpu/drm/bridge/samsung-dsim.c2321
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c271
-rw-r--r--drivers/gpu/drm/bridge/sii9234.c25
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c25
-rw-r--r--drivers/gpu/drm/bridge/simple-bridge.c83
-rw-r--r--drivers/gpu/drm/bridge/ssd2825.c775
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Kconfig29
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Makefile4
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c2097
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c42
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c45
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c11
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c10
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c1343
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h848
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c270
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c159
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c1032
-rw-r--r--drivers/gpu/drm/bridge/tc358762.c101
-rw-r--r--drivers/gpu/drm/bridge/tc358764.c31
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c773
-rw-r--r--drivers/gpu/drm/bridge/tc358768.c546
-rw-r--r--drivers/gpu/drm/bridge/tc358775.c169
-rw-r--r--drivers/gpu/drm/bridge/tda998x_drv.c (renamed from drivers/gpu/drm/i2c/tda998x_drv.c)94
-rw-r--r--drivers/gpu/drm/bridge/thc63lvd1024.c38
-rw-r--r--drivers/gpu/drm/bridge/ti-dlpc3433.c47
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c343
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c622
-rw-r--r--drivers/gpu/drm/bridge/ti-tdp158.c115
-rw-r--r--drivers/gpu/drm/bridge/ti-tfp410.c98
-rw-r--r--drivers/gpu/drm/bridge/ti-tpd12s015.c25
-rw-r--r--drivers/gpu/drm/bridge/waveshare-dsi.c203
-rw-r--r--drivers/gpu/drm/ci/arm.config70
-rw-r--r--drivers/gpu/drm/ci/arm64.config210
-rw-r--r--drivers/gpu/drm/ci/build-igt.sh74
-rw-r--r--drivers/gpu/drm/ci/build.sh161
-rw-r--r--drivers/gpu/drm/ci/build.yml226
-rw-r--r--drivers/gpu/drm/ci/check-devicetrees.yml50
-rwxr-xr-xdrivers/gpu/drm/ci/check-patch.py57
-rw-r--r--drivers/gpu/drm/ci/container.yml77
-rwxr-xr-xdrivers/gpu/drm/ci/dt-binding-check.sh19
-rwxr-xr-xdrivers/gpu/drm/ci/dtbs-check.sh22
-rw-r--r--drivers/gpu/drm/ci/gitlab-ci.yml349
-rwxr-xr-xdrivers/gpu/drm/ci/igt_runner.sh101
-rw-r--r--drivers/gpu/drm/ci/image-tags.yml18
-rwxr-xr-xdrivers/gpu/drm/ci/kunit.sh16
-rw-r--r--drivers/gpu/drm/ci/kunit.yml37
-rwxr-xr-xdrivers/gpu/drm/ci/lava-submit.sh105
-rwxr-xr-xdrivers/gpu/drm/ci/setup-llvm-links.sh13
-rw-r--r--drivers/gpu/drm/ci/static-checks.yml12
-rw-r--r--drivers/gpu/drm/ci/test.yml471
-rw-r--r--drivers/gpu/drm/ci/x86_64.config112
-rw-r--r--drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt40
-rw-r--r--drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt34
-rw-r--r--drivers/gpu/drm/ci/xfails/amdgpu-stoney-skips.txt40
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-amly-fails.txt48
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-amly-flakes.txt69
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-amly-skips.txt27
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-apl-fails.txt44
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-apl-flakes.txt13
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-apl-skips.txt31
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-cml-fails.txt74
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt34
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-cml-skips.txt27
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-glk-fails.txt47
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-glk-flakes.txt20
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-glk-skips.txt328
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt46
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt6
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt21
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt23
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-kbl-flakes.txt6
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-kbl-skips.txt152
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt30
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-tgl-skips.txt51
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-whl-fails.txt52
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-whl-flakes.txt13
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-whl-skips.txt24
-rw-r--r--drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt44
-rw-r--r--drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt55
-rw-r--r--drivers/gpu/drm/ci/xfails/mediatek-mt8173-skips.txt23
-rw-r--r--drivers/gpu/drm/ci/xfails/mediatek-mt8183-fails.txt38
-rw-r--r--drivers/gpu/drm/ci/xfails/mediatek-mt8183-flakes.txt41
-rw-r--r--drivers/gpu/drm/ci/xfails/mediatek-mt8183-skips.txt23
-rw-r--r--drivers/gpu/drm/ci/xfails/meson-g12b-fails.txt12
-rw-r--r--drivers/gpu/drm/ci/xfails/meson-g12b-skips.txt18
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt5
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-apq8016-skips.txt17
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt2
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-apq8096-flakes.txt6
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-apq8096-skips.txt28
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-fails.txt17
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-flakes.txt41
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-kingoftown-skips.txt39
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-fails.txt17
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-flakes.txt20
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sc7180-trogdor-lazor-limozeen-skips.txt30
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt10
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt6
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt212
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-g12b-fails.txt2
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-g12b-skips.txt24
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-mt8183-fails.txt2
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-mt8183-skips.txt24
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-rk3288-fails.txt2
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-rk3288-skips.txt27
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-rk3399-fails.txt2
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-rk3399-flakes.txt6
-rw-r--r--drivers/gpu/drm/ci/xfails/panfrost-rk3399-skips.txt27
-rw-r--r--drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt9
-rw-r--r--drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt34
-rw-r--r--drivers/gpu/drm/ci/xfails/rockchip-rk3288-skips.txt21
-rw-r--r--drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt74
-rw-r--r--drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt146
-rw-r--r--drivers/gpu/drm/ci/xfails/rockchip-rk3399-skips.txt26
-rw-r--r--drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt190
-rw-r--r--drivers/gpu/drm/ci/xfails/virtio_gpu-none-skips.txt31
-rw-r--r--drivers/gpu/drm/ci/xfails/vkms-none-fails.txt25
-rw-r--r--drivers/gpu/drm/ci/xfails/vkms-none-flakes.txt118
-rw-r--r--drivers/gpu/drm/ci/xfails/vkms-none-skips.txt811
-rw-r--r--drivers/gpu/drm/clients/Kconfig123
-rw-r--r--drivers/gpu/drm/clients/Makefile8
-rw-r--r--drivers/gpu/drm/clients/drm_client_internal.h25
-rw-r--r--drivers/gpu/drm/clients/drm_client_setup.c98
-rw-r--r--drivers/gpu/drm/clients/drm_fbdev_client.c176
-rw-r--r--drivers/gpu/drm/clients/drm_log.c441
-rw-r--r--drivers/gpu/drm/display/Kconfig103
-rw-r--r--drivers/gpu/drm/display/Makefile33
-rw-r--r--drivers/gpu/drm/display/drm_bridge_connector.c887
-rw-r--r--drivers/gpu/drm/display/drm_dp_aux_bus.c16
-rw-r--r--drivers/gpu/drm/display/drm_dp_aux_dev.c2
-rw-r--r--drivers/gpu/drm/display/drm_dp_cec.c77
-rw-r--r--drivers/gpu/drm/display/drm_dp_dual_mode_helper.c63
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c1561
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper_internal.h2
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology.c2075
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology_internal.h4
-rw-r--r--drivers/gpu/drm/display/drm_dp_tunnel.c1951
-rw-r--r--drivers/gpu/drm/display/drm_dsc_helper.c1157
-rw-r--r--drivers/gpu/drm/display/drm_hdcp_helper.c4
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_audio_helper.c195
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_cec_helper.c193
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c65
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_helper.c236
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_state_helper.c1180
-rw-r--r--drivers/gpu/drm/display/drm_scdc_helper.c60
-rw-r--r--drivers/gpu/drm/drm_agpsupport.c451
-rw-r--r--drivers/gpu/drm/drm_aperture.c197
-rw-r--r--drivers/gpu/drm/drm_atomic.c420
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c864
-rw-r--r--drivers/gpu/drm/drm_atomic_state_helper.c149
-rw-r--r--drivers/gpu/drm/drm_atomic_uapi.c388
-rw-r--r--drivers/gpu/drm/drm_auth.c80
-rw-r--r--drivers/gpu/drm/drm_blend.c19
-rw-r--r--drivers/gpu/drm/drm_bridge.c688
-rw-r--r--drivers/gpu/drm/drm_bridge_connector.c403
-rw-r--r--drivers/gpu/drm/drm_bridge_helper.c60
-rw-r--r--drivers/gpu/drm/drm_buddy.c844
-rw-r--r--drivers/gpu/drm/drm_bufs.c1631
-rw-r--r--drivers/gpu/drm/drm_cache.c9
-rw-r--r--drivers/gpu/drm/drm_client.c392
-rw-r--r--drivers/gpu/drm/drm_client_event.c214
-rw-r--r--drivers/gpu/drm/drm_client_modeset.c405
-rw-r--r--drivers/gpu/drm/drm_client_sysrq.c65
-rw-r--r--drivers/gpu/drm/drm_color_mgmt.c291
-rw-r--r--drivers/gpu/drm/drm_colorop.c599
-rw-r--r--drivers/gpu/drm/drm_connector.c1047
-rw-r--r--drivers/gpu/drm/drm_context.c525
-rw-r--r--drivers/gpu/drm/drm_crtc.c225
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c139
-rw-r--r--drivers/gpu/drm/drm_crtc_helper_internal.h17
-rw-r--r--drivers/gpu/drm/drm_crtc_internal.h54
-rw-r--r--drivers/gpu/drm/drm_damage_helper.c18
-rw-r--r--drivers/gpu/drm/drm_debugfs.c572
-rw-r--r--drivers/gpu/drm/drm_debugfs_crc.c1
-rw-r--r--drivers/gpu/drm/drm_displayid.c122
-rw-r--r--drivers/gpu/drm/drm_displayid_internal.h185
-rw-r--r--drivers/gpu/drm/drm_dma.c178
-rw-r--r--drivers/gpu/drm/drm_draw.c157
-rw-r--r--drivers/gpu/drm/drm_draw_internal.h56
-rw-r--r--drivers/gpu/drm/drm_drv.c444
-rw-r--r--drivers/gpu/drm/drm_dumb_buffers.c176
-rw-r--r--drivers/gpu/drm/drm_edid.c2129
-rw-r--r--drivers/gpu/drm/drm_edid_load.c272
-rw-r--r--drivers/gpu/drm/drm_eld.c57
-rw-r--r--drivers/gpu/drm/drm_encoder.c79
-rw-r--r--drivers/gpu/drm/drm_encoder_slave.c182
-rw-r--r--drivers/gpu/drm/drm_exec.c341
-rw-r--r--drivers/gpu/drm/drm_fb_cma_helper.c145
-rw-r--r--drivers/gpu/drm/drm_fb_dma_helper.c197
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c1537
-rw-r--r--drivers/gpu/drm/drm_fbdev_dma.c323
-rw-r--r--drivers/gpu/drm/drm_fbdev_shmem.c203
-rw-r--r--drivers/gpu/drm/drm_fbdev_ttm.c234
-rw-r--r--drivers/gpu/drm/drm_file.c525
-rw-r--r--drivers/gpu/drm/drm_flip_work.c28
-rw-r--r--drivers/gpu/drm/drm_format_helper.c1381
-rw-r--r--drivers/gpu/drm/drm_format_internal.h174
-rw-r--r--drivers/gpu/drm/drm_fourcc.c150
-rw-r--r--drivers/gpu/drm/drm_framebuffer.c289
-rw-r--r--drivers/gpu/drm/drm_gem.c603
-rw-r--r--drivers/gpu/drm/drm_gem_atomic_helper.c115
-rw-r--r--drivers/gpu/drm/drm_gem_cma_helper.c605
-rw-r--r--drivers/gpu/drm/drm_gem_dma_helper.c608
-rw-r--r--drivers/gpu/drm/drm_gem_framebuffer_helper.c71
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c645
-rw-r--r--drivers/gpu/drm/drm_gem_ttm_helper.c13
-rw-r--r--drivers/gpu/drm/drm_gem_vram_helper.c310
-rw-r--r--drivers/gpu/drm/drm_gpusvm.c1635
-rw-r--r--drivers/gpu/drm/drm_gpuvm.c3207
-rw-r--r--drivers/gpu/drm/drm_hashtab.c203
-rw-r--r--drivers/gpu/drm/drm_internal.h91
-rw-r--r--drivers/gpu/drm/drm_ioc32.c634
-rw-r--r--drivers/gpu/drm/drm_ioctl.c201
-rw-r--r--drivers/gpu/drm/drm_irq.c204
-rw-r--r--drivers/gpu/drm/drm_kms_helper_common.c32
-rw-r--r--drivers/gpu/drm/drm_lease.c70
-rw-r--r--drivers/gpu/drm/drm_legacy.h290
-rw-r--r--drivers/gpu/drm/drm_legacy_misc.c105
-rw-r--r--drivers/gpu/drm/drm_lock.c373
-rw-r--r--drivers/gpu/drm/drm_managed.c70
-rw-r--r--drivers/gpu/drm/drm_memory.c138
-rw-r--r--drivers/gpu/drm/drm_mipi_dbi.c308
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c849
-rw-r--r--drivers/gpu/drm/drm_mm.c40
-rw-r--r--drivers/gpu/drm/drm_mode_config.c47
-rw-r--r--drivers/gpu/drm/drm_mode_object.c26
-rw-r--r--drivers/gpu/drm/drm_modes.c720
-rw-r--r--drivers/gpu/drm/drm_modeset_helper.c105
-rw-r--r--drivers/gpu/drm/drm_modeset_lock.c4
-rw-r--r--drivers/gpu/drm/drm_nomodeset.c24
-rw-r--r--drivers/gpu/drm/drm_of.c144
-rw-r--r--drivers/gpu/drm/drm_pagemap.c882
-rw-r--r--drivers/gpu/drm/drm_panel.c462
-rw-r--r--drivers/gpu/drm/drm_panel_backlight_quirks.c156
-rw-r--r--drivers/gpu/drm/drm_panel_orientation_quirks.c222
-rw-r--r--drivers/gpu/drm/drm_panic.c1037
-rw-r--r--drivers/gpu/drm/drm_panic_qr.rs1016
-rw-r--r--drivers/gpu/drm/drm_pci.c205
-rw-r--r--drivers/gpu/drm/drm_plane.c426
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c98
-rw-r--r--drivers/gpu/drm/drm_prime.c227
-rw-r--r--drivers/gpu/drm/drm_print.c176
-rw-r--r--drivers/gpu/drm/drm_privacy_screen.c1
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c469
-rw-r--r--drivers/gpu/drm/drm_property.c59
-rw-r--r--drivers/gpu/drm/drm_rect.c3
-rw-r--r--drivers/gpu/drm/drm_scatter.c220
-rw-r--r--drivers/gpu/drm/drm_self_refresh_helper.c1
-rw-r--r--drivers/gpu/drm/drm_simple_kms_helper.c41
-rw-r--r--drivers/gpu/drm/drm_suballoc.c459
-rw-r--r--drivers/gpu/drm/drm_syncobj.c311
-rw-r--r--drivers/gpu/drm/drm_sysfs.c211
-rw-r--r--drivers/gpu/drm/drm_vblank.c482
-rw-r--r--drivers/gpu/drm/drm_vblank_helper.c176
-rw-r--r--drivers/gpu/drm/drm_vblank_work.c31
-rw-r--r--drivers/gpu/drm/drm_vm.c665
-rw-r--r--drivers/gpu/drm/drm_vma_manager.c77
-rw-r--r--drivers/gpu/drm/drm_writeback.c194
-rw-r--r--drivers/gpu/drm/etnaviv/cmdstream.xml.h52
-rw-r--r--drivers/gpu/drm/etnaviv/common.xml.h12
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_buffer.c17
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c1
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c10
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_drv.c150
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_drv.h8
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_dump.c26
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.c78
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.h9
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c20
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c22
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.c472
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.h55
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_hwdb.c171
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_mmu.c92
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_mmu.h2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_perfmon.c8
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_sched.c84
-rw-r--r--drivers/gpu/drm/etnaviv/state.xml.h103
-rw-r--r--drivers/gpu/drm/etnaviv/state_blt.xml.h22
-rw-r--r--drivers/gpu/drm/etnaviv/state_hi.xml.h95
-rw-r--r--drivers/gpu/drm/exynos/Kconfig5
-rw-r--r--drivers/gpu/drm/exynos/exynos5433_drm_decon.c24
-rw-r--r--drivers/gpu/drm/exynos/exynos7_drm_decon.c178
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp.c28
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dma.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c35
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h6
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c1808
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c161
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.h31
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimc.c20
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c38
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c20
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c18
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gsc.c24
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c33
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_mic.c23
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_rotator.c19
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_scaler.c21
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c94
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c64
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c9
-rw-r--r--drivers/gpu/drm/exynos/regs-decon7.h15
-rw-r--r--drivers/gpu/drm/fsl-dcu/Kconfig4
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c51
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c1
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c19
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c6
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.c2
-rw-r--r--drivers/gpu/drm/gma500/Kconfig8
-rw-r--r--drivers/gpu/drm/gma500/Makefile2
-rw-r--r--drivers/gpu/drm/gma500/backlight.c106
-rw-r--r--drivers/gpu/drm/gma500/cdv_device.c54
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_crt.c4
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_display.c2
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c12
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c5
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c10
-rw-r--r--drivers/gpu/drm/gma500/fbdev.c196
-rw-r--r--drivers/gpu/drm/gma500/framebuffer.c356
-rw-r--r--drivers/gpu/drm/gma500/gem.c1
-rw-r--r--drivers/gpu/drm/gma500/gma_display.c21
-rw-r--r--drivers/gpu/drm/gma500/gma_display.h3
-rw-r--r--drivers/gpu/drm/gma500/intel_bios.c23
-rw-r--r--drivers/gpu/drm/gma500/intel_bios.h4
-rw-r--r--drivers/gpu/drm/gma500/intel_gmbus.c5
-rw-r--r--drivers/gpu/drm/gma500/mid_bios.c6
-rw-r--r--drivers/gpu/drm/gma500/mmu.c42
-rw-r--r--drivers/gpu/drm/gma500/mmu.h2
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_crtc.c11
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_device.c65
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi.c7
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c4
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c5
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c2
-rw-r--r--drivers/gpu/drm/gma500/opregion.c9
-rw-r--r--drivers/gpu/drm/gma500/power.c158
-rw-r--r--drivers/gpu/drm/gma500/power.h18
-rw-r--r--drivers/gpu/drm/gma500/psb_device.c77
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c93
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.h62
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_display.c4
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_drv.h25
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c12
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_modes.c31
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_sdvo.c94
-rw-r--r--drivers/gpu/drm/gma500/psb_irq.c66
-rw-r--r--drivers/gpu/drm/gma500/psb_lid.c80
-rw-r--r--drivers/gpu/drm/gud/Kconfig1
-rw-r--r--drivers/gpu/drm/gud/gud_connector.c57
-rw-r--r--drivers/gpu/drm/gud/gud_drv.c132
-rw-r--r--drivers/gpu/drm/gud/gud_internal.h15
-rw-r--r--drivers/gpu/drm/gud/gud_pipe.c342
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/Kconfig7
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/Makefile4
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c168
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h69
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h21
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c307
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h64
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c392
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h132
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c71
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c104
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c182
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c130
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h38
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c47
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c48
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/Kconfig5
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c21
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h2
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h2
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c23
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c22
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm.h4
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_drv.c30
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_modeset.c228
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_proto.c2
-rw-r--r--drivers/gpu/drm/i2c/Kconfig36
-rw-r--r--drivers/gpu/drm/i2c/Makefile10
-rw-r--r--drivers/gpu/drm/i810/Makefile8
-rw-r--r--drivers/gpu/drm/i810/i810_dma.c1266
-rw-r--r--drivers/gpu/drm/i810/i810_drv.c101
-rw-r--r--drivers/gpu/drm/i810/i810_drv.h246
-rw-r--r--drivers/gpu/drm/i915/Kconfig67
-rw-r--r--drivers/gpu/drm/i915/Kconfig.debug41
-rw-r--r--drivers/gpu/drm/i915/Kconfig.profile26
-rw-r--r--drivers/gpu/drm/i915/Kconfig.unstable21
-rw-r--r--drivers/gpu/drm/i915/Makefile304
-rw-r--r--drivers/gpu/drm/i915/display/bxt_dpio_phy_regs.h273
-rw-r--r--drivers/gpu/drm/i915/display/dvo_ch7017.c20
-rw-r--r--drivers/gpu/drm/i915/display/dvo_ch7xxx.c46
-rw-r--r--drivers/gpu/drm/i915/display/dvo_ivch.c22
-rw-r--r--drivers/gpu/drm/i915/display/dvo_ns2501.c32
-rw-r--r--drivers/gpu/drm/i915/display/dvo_sil164.c37
-rw-r--r--drivers/gpu/drm/i915/display/dvo_tfp410.c24
-rw-r--r--drivers/gpu/drm/i915/display/g4x_dp.c544
-rw-r--r--drivers/gpu/drm/i915/display/g4x_dp.h31
-rw-r--r--drivers/gpu/drm/i915/display/g4x_hdmi.c431
-rw-r--r--drivers/gpu/drm/i915/display/g4x_hdmi.h21
-rw-r--r--drivers/gpu/drm/i915/display/hsw_ips.c225
-rw-r--r--drivers/gpu/drm/i915/display/hsw_ips.h38
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_display_sr.c97
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_display_sr.h14
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_plane.c670
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_plane.h44
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_plane_regs.h112
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_wm.c4190
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_wm.h37
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_wm_regs.h257
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi.c1025
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi.h6
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi_regs.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_acpi.c47
-rw-r--r--drivers/gpu/drm/i915/display/intel_acpi.h17
-rw-r--r--drivers/gpu/drm/i915/display/intel_alpm.c600
-rw-r--r--drivers/gpu/drm/i915/display/intel_alpm.h41
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic.c254
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic.h5
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic_plane.c1121
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic_plane.h69
-rw-r--r--drivers/gpu/drm/i915/display/intel_audio.c1198
-rw-r--r--drivers/gpu/drm/i915/display/intel_audio.h21
-rw-r--r--drivers/gpu/drm/i915/display/intel_audio_regs.h108
-rw-r--r--drivers/gpu/drm/i915/display/intel_backlight.c742
-rw-r--r--drivers/gpu/drm/i915/display/intel_backlight_regs.h121
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.c2264
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.h258
-rw-r--r--drivers/gpu/drm/i915/display/intel_bo.c81
-rw-r--r--drivers/gpu/drm/i915/display/intel_bo.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.c1240
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.h62
-rw-r--r--drivers/gpu/drm/i915/display/intel_casf.c290
-rw-r--r--drivers/gpu/drm/i915/display/intel_casf.h21
-rw-r--r--drivers/gpu/drm/i915/display/intel_casf_regs.h33
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c3033
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.h104
-rw-r--r--drivers/gpu/drm/i915/display/intel_cmtg.c188
-rw-r--r--drivers/gpu/drm/i915/display/intel_cmtg.h13
-rw-r--r--drivers/gpu/drm/i915/display/intel_cmtg_regs.h21
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.c3624
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.h42
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_pipeline.c99
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_pipeline.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_regs.h348
-rw-r--r--drivers/gpu/drm/i915/display/intel_colorop.c35
-rw-r--r--drivers/gpu/drm/i915/display/intel_colorop.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_combo_phy.c244
-rw-r--r--drivers/gpu/drm/i915/display/intel_combo_phy.h8
-rw-r--r--drivers/gpu/drm/i915/display/intel_combo_phy_regs.h125
-rw-r--r--drivers/gpu/drm/i915/display/intel_connector.c161
-rw-r--r--drivers/gpu/drm/i915/display/intel_connector.h12
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt.c595
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt.h20
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt_regs.h50
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc.c437
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc.h47
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc_state_dump.c402
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc_state_dump.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.c551
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.h9
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor_regs.h112
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.c3642
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.h70
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h436
-rw-r--r--drivers/gpu/drm/i915/display/intel_dbuf_bw.c295
-rw-r--r--drivers/gpu/drm/i915/display/intel_dbuf_bw.h37
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c3390
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.h43
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c246
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi_buf_trans.h10
-rw-r--r--drivers/gpu/drm/i915/display/intel_de.h204
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c7638
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.h429
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_conversion.c21
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_conversion.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_core.h627
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.c1913
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.h12
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs_params.c177
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs_params.h13
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.c1985
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.h326
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.c797
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.h42
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.c2486
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.h94
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_jiffies.h43
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_limits.h150
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_params.c242
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_params.h65
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.c1451
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.h153
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_map.c534
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_well.c1231
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_well.h66
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_reg_defs.h49
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_regs.h2934
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_reset.c127
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_reset.h20
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_rpm.c62
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_rpm.h37
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_rps.c108
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_rps.h46
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_snapshot.c79
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_snapshot.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_trace.h561
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_types.h948
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_utils.c32
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_utils.h31
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_wa.c79
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_wa.h36
-rw-r--r--drivers/gpu/drm/i915/display/intel_dkl_phy.c117
-rw-r--r--drivers/gpu/drm/i915/display/intel_dkl_phy.h25
-rw-r--r--drivers/gpu/drm/i915/display/intel_dkl_phy_regs.h207
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc.c1413
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc.h91
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc_regs.h510
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc_wl.c506
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc_wl.h41
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c5609
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.h163
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux.c441
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux.h11
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c336
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux_regs.h106
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_hdcp.c308
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.c1406
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.h20
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c2063
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.h19
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_test.c764
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_test.h23
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_tunnel.c814
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_tunnel.h133
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpio_phy.c756
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpio_phy.h154
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll.c1255
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll.h31
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll_mgr.c2778
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll_mgr.h229
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.c84
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.h13
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt_common.c35
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt_common.h13
-rw-r--r--drivers/gpu/drm/i915/display/intel_drrs.c186
-rw-r--r--drivers/gpu/drm/i915/display/intel_drrs.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb.c1098
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb.h88
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb_buffer.c82
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb_buffer.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb_regs.h100
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi.c40
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi.h13
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c22
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt.c702
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt_defs.h197
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo.c452
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo.h10
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo_dev.h46
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo_regs.h54
-rw-r--r--drivers/gpu/drm/i915/display/intel_encoder.c123
-rw-r--r--drivers/gpu/drm/i915/display/intel_encoder.h26
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb.c1094
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb.h55
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_bo.c101
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_bo.h25
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_pin.c249
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_pin.h21
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbc.c1203
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbc.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbc_regs.h129
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.c612
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.h50
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev_fb.c106
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev_fb.h23
-rw-r--r--drivers/gpu/drm/i915/display/intel_fdi.c767
-rw-r--r--drivers/gpu/drm/i915/display/intel_fdi.h30
-rw-r--r--drivers/gpu/drm/i915/display/intel_fdi_regs.h151
-rw-r--r--drivers/gpu/drm/i915/display/intel_fifo_underrun.c314
-rw-r--r--drivers/gpu/drm/i915/display/intel_fifo_underrun.h20
-rw-r--r--drivers/gpu/drm/i915/display/intel_fixed.h (renamed from drivers/gpu/drm/i915/i915_fixed.h)0
-rw-r--r--drivers/gpu/drm/i915/display/intel_flipq.c472
-rw-r--r--drivers/gpu/drm/i915/display/intel_flipq.h37
-rw-r--r--drivers/gpu/drm/i915/display/intel_frontbuffer.c214
-rw-r--r--drivers/gpu/drm/i915/display/intel_frontbuffer.h73
-rw-r--r--drivers/gpu/drm/i915/display/intel_global_state.c214
-rw-r--r--drivers/gpu/drm/i915/display/intel_global_state.h49
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus.c448
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus_regs.h81
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.c1514
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.h24
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc.c239
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc.h22
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc_message.c672
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc_message.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_regs.h272
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_shim.h137
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c1335
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.h32
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug.c917
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug.h30
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug_irq.c1495
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug_irq.h35
-rw-r--r--drivers/gpu/drm/i915/display/intel_hti.c43
-rw-r--r--drivers/gpu/drm/i915/display/intel_hti.h18
-rw-r--r--drivers/gpu/drm/i915/display/intel_hti_regs.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_link_bw.c497
-rw-r--r--drivers/gpu/drm/i915/display/intel_link_bw.h39
-rw-r--r--drivers/gpu/drm/i915/display/intel_load_detect.c225
-rw-r--r--drivers/gpu/drm/i915/display/intel_load_detect.h20
-rw-r--r--drivers/gpu/drm/i915/display/intel_lpe_audio.c143
-rw-r--r--drivers/gpu/drm/i915/display/intel_lpe_audio.h32
-rw-r--r--drivers/gpu/drm/i915/display/intel_lspcon.c188
-rw-r--r--drivers/gpu/drm/i915/display/intel_lspcon.h25
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy.c2327
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy.h47
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy_regs.h90
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds.c519
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds_regs.h65
-rw-r--r--drivers/gpu/drm/i915/display/intel_mg_phy_regs.h282
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_lock.c50
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_lock.h33
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_setup.c716
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_setup.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_verify.c208
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_verify.h11
-rw-r--r--drivers/gpu/drm/i915/display/intel_opregion.c522
-rw-r--r--drivers/gpu/drm/i915/display/intel_opregion.h104
-rw-r--r--drivers/gpu/drm/i915/display/intel_overlay.c257
-rw-r--r--drivers/gpu/drm/i915/display/intel_overlay.h66
-rw-r--r--drivers/gpu/drm/i915/display/intel_panel.c636
-rw-r--r--drivers/gpu/drm/i915/display/intel_panel.h22
-rw-r--r--drivers/gpu/drm/i915/display/intel_panic.c27
-rw-r--r--drivers/gpu/drm/i915/display/intel_panic.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch.c340
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch.h56
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_display.c386
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_display.h59
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_refclk.c335
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_refclk.h34
-rw-r--r--drivers/gpu/drm/i915/display/intel_pfit.c731
-rw-r--r--drivers/gpu/drm/i915/display/intel_pfit.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_pfit_regs.h79
-rw-r--r--drivers/gpu/drm/i915/display/intel_pipe_crc.c171
-rw-r--r--drivers/gpu/drm/i915/display/intel_pipe_crc.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_pipe_crc_regs.h152
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.c1766
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.h91
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane_initial.c283
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane_initial.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_pmdemand.c675
-rw-r--r--drivers/gpu/drm/i915/display/intel_pmdemand.h39
-rw-r--r--drivers/gpu/drm/i915/display/intel_pps.c1280
-rw-r--r--drivers/gpu/drm/i915/display/intel_pps.h25
-rw-r--r--drivers/gpu/drm/i915/display/intel_pps_regs.h77
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.c3632
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.h51
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr_regs.h342
-rw-r--r--drivers/gpu/drm/i915/display/intel_qp_tables.c196
-rw-r--r--drivers/gpu/drm/i915/display/intel_qp_tables.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_quirks.c141
-rw-r--r--drivers/gpu/drm/i915/display/intel_quirks.h23
-rw-r--r--drivers/gpu/drm/i915/display/intel_sbi.c92
-rw-r--r--drivers/gpu/drm/i915/display/intel_sbi.h27
-rw-r--r--drivers/gpu/drm/i915/display/intel_sbi_regs.h65
-rw-r--r--drivers/gpu/drm/i915/display/intel_sdvo.c1192
-rw-r--r--drivers/gpu/drm/i915/display/intel_sdvo.h19
-rw-r--r--drivers/gpu/drm/i915/display/intel_sdvo_regs.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c366
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.h17
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_phy.c1344
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_phy.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_phy_regs.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite.c614
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite.h36
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite_regs.h388
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite_uapi.c128
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite_uapi.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.c2067
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.h93
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc_phy_regs.h280
-rw-r--r--drivers/gpu/drm/i915/display/intel_tdf.h25
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv.c380
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv.h10
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv_regs.h490
-rw-r--r--drivers/gpu/drm/i915/display/intel_vblank.c781
-rw-r--r--drivers/gpu/drm/i915/display/intel_vblank.h53
-rw-r--r--drivers/gpu/drm/i915/display/intel_vbt_defs.h1143
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc.c1621
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc.h9
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc_regs.h354
-rw-r--r--drivers/gpu/drm/i915/display/intel_vga.c141
-rw-r--r--drivers/gpu/drm/i915/display/intel_vga.h12
-rw-r--r--drivers/gpu/drm/i915/display/intel_vga_regs.h36
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.c840
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr_regs.h126
-rw-r--r--drivers/gpu/drm/i915/display/intel_wm.c404
-rw-r--r--drivers/gpu/drm/i915/display/intel_wm.h36
-rw-r--r--drivers/gpu/drm/i915/display/intel_wm_types.h76
-rw-r--r--drivers/gpu/drm/i915/display/skl_prefill.c157
-rw-r--r--drivers/gpu/drm/i915/display/skl_prefill.h46
-rw-r--r--drivers/gpu/drm/i915/display/skl_scaler.c987
-rw-r--r--drivers/gpu/drm/i915/display/skl_scaler.h52
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c1677
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.h19
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane_regs.h585
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.c4108
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.h86
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark_regs.h87
-rw-r--r--drivers/gpu/drm/i915/display/vlv_clock.c88
-rw-r--r--drivers/gpu/drm/i915/display/vlv_clock.h38
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dpio_phy_regs.h309
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi.c1046
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi.h22
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi_pll.c362
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi_pll.h22
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi_regs.h329
-rw-r--r--drivers/gpu/drm/i915/display/vlv_sideband.c50
-rw-r--r--drivers/gpu/drm/i915/display/vlv_sideband.h156
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_busy.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_clflush.c9
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_clflush.h3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_context.c245
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_context.h3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_context_types.h17
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_create.c55
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c60
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_domain.c98
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c338
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_internal.c32
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ioctls.h3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_lmem.c29
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_lmem.h4
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_mman.c278
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_mman.h6
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.c213
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.h358
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.c103
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h84
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object_types.h107
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_pages.c189
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_phys.c28
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_pm.c50
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_pm.h3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_region.c4
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_region.h4
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_shmem.c292
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_shrinker.c90
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_stolen.c518
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_stolen.h29
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_throttle.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_tiling.c43
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm.c314
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm.h21
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c84
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c40
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_userptr.c88
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_userptr.h14
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_wait.c31
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gemfs.c21
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gemfs.h3
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/huge_gem_object.c8
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/huge_pages.c337
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c109
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c30
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c206
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c104
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c14
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c160
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c10
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c19
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.h14
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/mock_context.c44
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/mock_context.h3
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c4
-rw-r--r--drivers/gpu/drm/i915/gt/gen2_engine_cs.c35
-rw-r--r--drivers/gpu/drm/i915/gt/gen2_engine_cs.h6
-rw-r--r--drivers/gpu/drm/i915/gt/gen6_ppgtt.c26
-rw-r--r--drivers/gpu/drm/i915/gt/gen7_renderclear.c5
-rw-r--r--drivers/gpu/drm/i915/gt/gen8_engine_cs.c279
-rw-r--r--drivers/gpu/drm/i915/gt/gen8_engine_cs.h34
-rw-r--r--drivers/gpu/drm/i915/gt/gen8_ppgtt.c341
-rw-r--r--drivers/gpu/drm/i915/gt/gen8_ppgtt.h3
-rw-r--r--drivers/gpu/drm/i915/gt/intel_breadcrumbs.c30
-rw-r--r--drivers/gpu/drm/i915/gt/intel_breadcrumbs_types.h3
-rw-r--r--drivers/gpu/drm/i915/gt/intel_context.c30
-rw-r--r--drivers/gpu/drm/i915/gt/intel_context.h59
-rw-r--r--drivers/gpu/drm/i915/gt/intel_context_types.h18
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine.h48
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_cs.c610
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c50
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_pm.c41
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_pm.h1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_regs.h14
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_types.h74
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_user.c68
-rw-r--r--drivers/gpu/drm/i915/gt/intel_execlists_submission.c134
-rw-r--r--drivers/gpu/drm/i915/gt/intel_execlists_submission.h4
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt.c745
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c22
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt_gmch.c26
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gpu_commands.h17
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gsc.c131
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gsc.h10
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt.c557
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt.h122
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_buffer_pool.c11
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_ccs_mode.c39
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_ccs_mode.h13
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_clock_utils.c217
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_debugfs.c11
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_defines.h11
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_engines_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_irq.c176
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_mcr.c528
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_mcr.h29
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_pm.c69
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_pm.h53
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c313
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_pm_irq.c8
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_print.h57
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_regs.h523
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_requests.c22
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_sysfs.c31
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_sysfs.h11
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c769
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_types.h71
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gtt.c149
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gtt.h98
-rw-r--r--drivers/gpu/drm/i915/gt/intel_lrc.c323
-rw-r--r--drivers/gpu/drm/i915/gt/intel_lrc.h2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_migrate.c180
-rw-r--r--drivers/gpu/drm/i915/gt/intel_migrate.h13
-rw-r--r--drivers/gpu/drm/i915/gt/intel_mocs.c165
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ppgtt.c10
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rc6.c297
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rc6.h13
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rc6_types.h17
-rw-r--r--drivers/gpu/drm/i915/gt/intel_region_lmem.c82
-rw-r--r--drivers/gpu/drm/i915/gt/intel_renderstate.c8
-rw-r--r--drivers/gpu/drm/i915/gt/intel_reset.c362
-rw-r--r--drivers/gpu/drm/i915/gt/intel_reset.h8
-rw-r--r--drivers/gpu/drm/i915/gt/intel_reset_types.h7
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ring.c33
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ring.h1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ring_submission.c100
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rps.c637
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rps.h14
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rps_types.h6
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sa_media.c48
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sa_media.h15
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sseu.c90
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sseu.h2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_timeline.c1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_timeline.h1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_tlb.c173
-rw-r--r--drivers/gpu/drm/i915/gt/intel_tlb.h29
-rw-r--r--drivers/gpu/drm/i915/gt/intel_wopcm.c (renamed from drivers/gpu/drm/i915/intel_wopcm.c)45
-rw-r--r--drivers/gpu/drm/i915/gt/intel_wopcm.h (renamed from drivers/gpu/drm/i915/intel_wopcm.h)3
-rw-r--r--drivers/gpu/drm/i915/gt/intel_workarounds.c1680
-rw-r--r--drivers/gpu/drm/i915/gt/intel_workarounds_types.h12
-rw-r--r--drivers/gpu/drm/i915/gt/mock_engine.c6
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_context.c10
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_engine_cs.c54
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c122
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_engine_pm.c5
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_execlists.c129
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_gt_pm.c41
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_hangcheck.c123
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_llc.c1
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_lrc.c248
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_migrate.c236
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_mocs.c12
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_rc6.c69
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_reset.c22
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_ring_submission.c2
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_rps.c72
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_slpc.c319
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_timeline.c18
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_tlb.c405
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_workarounds.c42
-rw-r--r--drivers/gpu/drm/i915/gt/shaders/README6
-rw-r--r--drivers/gpu/drm/i915/gt/shaders/clear_kernel/hsw.asm2
-rw-r--r--drivers/gpu/drm/i915/gt/shaders/clear_kernel/ivb.asm2
-rw-r--r--drivers/gpu/drm/i915/gt/shmem_utils.c17
-rw-r--r--drivers/gpu/drm/i915/gt/sysfs_engines.c103
-rw-r--r--drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h34
-rw-r--r--drivers/gpu/drm/i915/gt/uc/abi/guc_actions_slpc_abi.h35
-rw-r--r--drivers/gpu/drm/i915/gt/uc/abi/guc_communication_ctb_abi.h29
-rw-r--r--drivers/gpu/drm/i915/gt/uc/abi/guc_errors_abi.h19
-rw-r--r--drivers/gpu/drm/i915/gt/uc/abi/guc_klvs_abi.h24
-rw-r--r--drivers/gpu/drm/i915/gt/uc/abi/guc_messages_abi.h30
-rw-r--r--drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h30
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_binary_headers.h135
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_fw.c524
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_fw.h21
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_proxy.c429
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_proxy.h18
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc.c358
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc.h85
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_debugfs.c39
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_debugfs.h14
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c231
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.h93
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc.c295
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc.h149
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c242
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c556
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h4
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c276
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_ct.h19
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_debugfs.c61
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_fw.c192
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h105
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_hwconfig.c13
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_log.c296
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_log.h50
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_print.h51
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_rc.c10
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_reg.h11
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c317
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.h5
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_slpc_types.h7
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c1322
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_submission.h4
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_huc.c566
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_huc.h56
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_huc_fw.c255
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_huc_fw.h7
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_huc_print.h21
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc.c207
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc.h6
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc_debugfs.c4
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c1045
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h93
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h7
-rw-r--r--drivers/gpu/drm/i915/gt/uc/selftest_guc.c165
-rw-r--r--drivers/gpu/drm/i915/gt/uc/selftest_guc_hangcheck.c162
-rw-r--r--drivers/gpu/drm/i915/gt/uc/selftest_guc_multi_lrc.c11
-rw-r--r--drivers/gpu/drm/i915/gvt/aperture_gm.c31
-rw-r--r--drivers/gpu/drm/i915/gvt/cfg_space.c9
-rw-r--r--drivers/gpu/drm/i915/gvt/cmd_parser.c49
-rw-r--r--drivers/gpu/drm/i915/gvt/debugfs.c55
-rw-r--r--drivers/gpu/drm/i915/gvt/display.c107
-rw-r--r--drivers/gpu/drm/i915/gvt/display.h55
-rw-r--r--drivers/gpu/drm/i915/gvt/dmabuf.c21
-rw-r--r--drivers/gpu/drm/i915/gvt/dmabuf.h2
-rw-r--r--drivers/gpu/drm/i915/gvt/edid.c58
-rw-r--r--drivers/gpu/drm/i915/gvt/edid.h12
-rw-r--r--drivers/gpu/drm/i915/gvt/fb_decoder.c156
-rw-r--r--drivers/gpu/drm/i915/gvt/fb_decoder.h15
-rw-r--r--drivers/gpu/drm/i915/gvt/firmware.c35
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c203
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.h4
-rw-r--r--drivers/gpu/drm/i915/gvt/gvt.h103
-rw-r--r--drivers/gpu/drm/i915/gvt/handlers.c318
-rw-r--r--drivers/gpu/drm/i915/gvt/interrupt.c34
-rw-r--r--drivers/gpu/drm/i915/gvt/interrupt.h33
-rw-r--r--drivers/gpu/drm/i915/gvt/kvmgt.c703
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio.c11
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio.h5
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio_context.c35
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio_context.h10
-rw-r--r--drivers/gpu/drm/i915/gvt/opregion.c11
-rw-r--r--drivers/gpu/drm/i915/gvt/page_track.c14
-rw-r--r--drivers/gpu/drm/i915/gvt/reg.h3
-rw-r--r--drivers/gpu/drm/i915/gvt/sched_policy.c8
-rw-r--r--drivers/gpu/drm/i915/gvt/scheduler.c26
-rw-r--r--drivers/gpu/drm/i915/gvt/scheduler.h2
-rw-r--r--drivers/gpu/drm/i915/gvt/trace.h2
-rw-r--r--drivers/gpu/drm/i915/gvt/vgpu.c237
-rw-r--r--drivers/gpu/drm/i915/i915_active.c152
-rw-r--r--drivers/gpu/drm/i915/i915_active.h15
-rw-r--r--drivers/gpu/drm/i915/i915_cmd_parser.c9
-rw-r--r--drivers/gpu/drm/i915/i915_config.c5
-rw-r--r--drivers/gpu/drm/i915/i915_config.h23
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c296
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs_params.c47
-rw-r--r--drivers/gpu/drm/i915/i915_deps.c2
-rw-r--r--drivers/gpu/drm/i915/i915_driver.c980
-rw-r--r--drivers/gpu/drm/i915/i915_driver.h4
-rw-r--r--drivers/gpu/drm/i915/i915_drm_client.c176
-rw-r--r--drivers/gpu/drm/i915/i915_drm_client.h62
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h1321
-rw-r--r--drivers/gpu/drm/i915/i915_file_private.h2
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c205
-rw-r--r--drivers/gpu/drm/i915/i915_gem.h52
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c88
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.h4
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c9
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.h5
-rw-r--r--drivers/gpu/drm/i915/i915_getparam.c35
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c785
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.h90
-rw-r--r--drivers/gpu/drm/i915/i915_gtt_view_types.h59
-rw-r--r--drivers/gpu/drm/i915/i915_hwmon.c994
-rw-r--r--drivers/gpu/drm/i915/i915_hwmon.h27
-rw-r--r--drivers/gpu/drm/i915/i915_iosf_mbi.h6
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c3987
-rw-r--r--drivers/gpu/drm/i915/i915_irq.h101
-rw-r--r--drivers/gpu/drm/i915/i915_jiffies.h16
-rw-r--r--drivers/gpu/drm/i915/i915_list_util.h23
-rw-r--r--drivers/gpu/drm/i915/i915_memcpy.c4
-rw-r--r--drivers/gpu/drm/i915/i915_mm.c16
-rw-r--r--drivers/gpu/drm/i915/i915_mm.h3
-rw-r--r--drivers/gpu/drm/i915/i915_mmio_range.c18
-rw-r--r--drivers/gpu/drm/i915/i915_mmio_range.h19
-rw-r--r--drivers/gpu/drm/i915/i915_module.c14
-rw-r--r--drivers/gpu/drm/i915/i915_params.c200
-rw-r--r--drivers/gpu/drm/i915/i915_params.h26
-rw-r--r--drivers/gpu/drm/i915/i915_pci.c824
-rw-r--r--drivers/gpu/drm/i915/i915_pci.h6
-rw-r--r--drivers/gpu/drm/i915/i915_perf.c1420
-rw-r--r--drivers/gpu/drm/i915/i915_perf.h6
-rw-r--r--drivers/gpu/drm/i915/i915_perf_oa_regs.h80
-rw-r--r--drivers/gpu/drm/i915/i915_perf_types.h126
-rw-r--r--drivers/gpu/drm/i915/i915_pmu.c610
-rw-r--r--drivers/gpu/drm/i915/i915_pmu.h45
-rw-r--r--drivers/gpu/drm/i915/i915_ptr_util.h66
-rw-r--r--drivers/gpu/drm/i915/i915_query.c51
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h7941
-rw-r--r--drivers/gpu/drm/i915/i915_reg_defs.h213
-rw-r--r--drivers/gpu/drm/i915/i915_request.c78
-rw-r--r--drivers/gpu/drm/i915/i915_request.h62
-rw-r--r--drivers/gpu/drm/i915/i915_scatterlist.c25
-rw-r--r--drivers/gpu/drm/i915/i915_scatterlist.h53
-rw-r--r--drivers/gpu/drm/i915/i915_scheduler.c2
-rw-r--r--drivers/gpu/drm/i915/i915_selftest.h2
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c124
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.h14
-rw-r--r--drivers/gpu/drm/i915/i915_sw_fence.c26
-rw-r--r--drivers/gpu/drm/i915/i915_sw_fence.h7
-rw-r--r--drivers/gpu/drm/i915/i915_switcheroo.c13
-rw-r--r--drivers/gpu/drm/i915/i915_syncmap.c19
-rw-r--r--drivers/gpu/drm/i915/i915_sysfs.c89
-rw-r--r--drivers/gpu/drm/i915/i915_timer_util.c36
-rw-r--r--drivers/gpu/drm/i915/i915_timer_util.h23
-rw-r--r--drivers/gpu/drm/i915/i915_trace.h56
-rw-r--r--drivers/gpu/drm/i915/i915_ttm_buddy_manager.c121
-rw-r--r--drivers/gpu/drm/i915/i915_user_extensions.c2
-rw-r--r--drivers/gpu/drm/i915/i915_utils.c99
-rw-r--r--drivers/gpu/drm/i915/i915_utils.h293
-rw-r--r--drivers/gpu/drm/i915/i915_vgpu.c2
-rw-r--r--drivers/gpu/drm/i915/i915_vma.c317
-rw-r--r--drivers/gpu/drm/i915/i915_vma.h114
-rw-r--r--drivers/gpu/drm/i915/i915_vma_resource.c10
-rw-r--r--drivers/gpu/drm/i915/i915_vma_resource.h63
-rw-r--r--drivers/gpu/drm/i915/i915_vma_types.h84
-rw-r--r--drivers/gpu/drm/i915/i915_wait_util.h119
-rw-r--r--drivers/gpu/drm/i915/intel_clock_gating.c801
-rw-r--r--drivers/gpu/drm/i915/intel_clock_gating.h14
-rw-r--r--drivers/gpu/drm/i915/intel_cpu_info.c44
-rw-r--r--drivers/gpu/drm/i915/intel_cpu_info.h13
-rw-r--r--drivers/gpu/drm/i915/intel_device_info.c409
-rw-r--r--drivers/gpu/drm/i915/intel_device_info.h160
-rw-r--r--drivers/gpu/drm/i915/intel_dram.c534
-rw-r--r--drivers/gpu/drm/i915/intel_dram.h14
-rw-r--r--drivers/gpu/drm/i915/intel_gvt.c81
-rw-r--r--drivers/gpu/drm/i915/intel_gvt_mmio_table.c407
-rw-r--r--drivers/gpu/drm/i915/intel_mchbar_regs.h30
-rw-r--r--drivers/gpu/drm/i915/intel_memory_region.c78
-rw-r--r--drivers/gpu/drm/i915/intel_memory_region.h14
-rw-r--r--drivers/gpu/drm/i915/intel_pch.c272
-rw-r--r--drivers/gpu/drm/i915/intel_pch.h87
-rw-r--r--drivers/gpu/drm/i915/intel_pci_config.h27
-rw-r--r--drivers/gpu/drm/i915/intel_pcode.c67
-rw-r--r--drivers/gpu/drm/i915/intel_pcode.h15
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c8474
-rw-r--r--drivers/gpu/drm/i915/intel_pm.h89
-rw-r--r--drivers/gpu/drm/i915/intel_pm_types.h76
-rw-r--r--drivers/gpu/drm/i915/intel_region_ttm.c28
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.c368
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.h65
-rw-r--r--drivers/gpu/drm/i915/intel_sbi.c74
-rw-r--r--drivers/gpu/drm/i915/intel_sbi.h23
-rw-r--r--drivers/gpu/drm/i915/intel_step.c188
-rw-r--r--drivers/gpu/drm/i915/intel_step.h30
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c942
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.h127
-rw-r--r--drivers/gpu/drm/i915/intel_uncore_trace.c7
-rw-r--r--drivers/gpu/drm/i915/intel_uncore_trace.h49
-rw-r--r--drivers/gpu/drm/i915/intel_wakeref.c118
-rw-r--r--drivers/gpu/drm/i915/intel_wakeref.h111
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp.c352
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp.h49
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_cmd.c8
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_42.h43
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_43.h61
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_cmn.h42
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_debugfs.c46
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_debugfs.h4
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c448
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.h45
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_huc.c73
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_huc.h13
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_irq.c37
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_irq.h8
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_pm.c29
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_pm.h9
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_regs.h27
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_session.c64
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_session.h14
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_tee.c286
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_tee.h5
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_tee_interface.h36
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_types.h49
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_active.c10
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem.c22
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_evict.c12
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_gtt.c427
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_live_selftests.h2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_perf.c22
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_random.h2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_request.c441
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_selftest.c86
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_sw_fence.c16
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_syncmap.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_vma.c85
-rw-r--r--drivers/gpu/drm/i915/selftests/igt_flush_test.c38
-rw-r--r--drivers/gpu/drm/i915/selftests/igt_live_test.c48
-rw-r--r--drivers/gpu/drm/i915/selftests/igt_live_test.h3
-rw-r--r--drivers/gpu/drm/i915/selftests/igt_spinner.c52
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_memory_region.c24
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_scheduler_helpers.c9
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_uncore.c23
-rw-r--r--drivers/gpu/drm/i915/selftests/lib_sw_fence.c6
-rw-r--r--drivers/gpu/drm/i915/selftests/librapl.c4
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_gem_device.c68
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_gtt.c10
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_region.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_request.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/scatterlist.c6
-rw-r--r--drivers/gpu/drm/i915/soc/intel_dram.c823
-rw-r--r--drivers/gpu/drm/i915/soc/intel_dram.h45
-rw-r--r--drivers/gpu/drm/i915/soc/intel_gmch.c188
-rw-r--r--drivers/gpu/drm/i915/soc/intel_gmch.h20
-rw-r--r--drivers/gpu/drm/i915/soc/intel_rom.c161
-rw-r--r--drivers/gpu/drm/i915/soc/intel_rom.h25
-rw-r--r--drivers/gpu/drm/i915/vlv_iosf_sb.c231
-rw-r--r--drivers/gpu/drm/i915/vlv_iosf_sb.h37
-rw-r--r--drivers/gpu/drm/i915/vlv_iosf_sb_reg.h180
-rw-r--r--drivers/gpu/drm/i915/vlv_sideband.c266
-rw-r--r--drivers/gpu/drm/i915/vlv_sideband.h125
-rw-r--r--drivers/gpu/drm/i915/vlv_sideband_reg.h180
-rw-r--r--drivers/gpu/drm/i915/vlv_suspend.c38
-rw-r--r--drivers/gpu/drm/imagination/Kconfig20
-rw-r--r--drivers/gpu/drm/imagination/Makefile35
-rw-r--r--drivers/gpu/drm/imagination/pvr_ccb.c646
-rw-r--r--drivers/gpu/drm/imagination/pvr_ccb.h71
-rw-r--r--drivers/gpu/drm/imagination/pvr_cccb.c267
-rw-r--r--drivers/gpu/drm/imagination/pvr_cccb.h110
-rw-r--r--drivers/gpu/drm/imagination/pvr_context.c485
-rw-r--r--drivers/gpu/drm/imagination/pvr_context.h226
-rw-r--r--drivers/gpu/drm/imagination/pvr_debugfs.c52
-rw-r--r--drivers/gpu/drm/imagination/pvr_debugfs.h29
-rw-r--r--drivers/gpu/drm/imagination/pvr_device.c773
-rw-r--r--drivers/gpu/drm/imagination/pvr_device.h794
-rw-r--r--drivers/gpu/drm/imagination/pvr_device_info.c255
-rw-r--r--drivers/gpu/drm/imagination/pvr_device_info.h186
-rw-r--r--drivers/gpu/drm/imagination/pvr_drv.c1535
-rw-r--r--drivers/gpu/drm/imagination/pvr_drv.h128
-rw-r--r--drivers/gpu/drm/imagination/pvr_free_list.c624
-rw-r--r--drivers/gpu/drm/imagination/pvr_free_list.h195
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw.c1515
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw.h496
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_info.h135
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_meta.c561
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_meta.h14
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_mips.c199
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_mips.h49
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_riscv.c165
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_startstop.c323
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_startstop.h13
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_trace.c466
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_trace.h76
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_util.c66
-rw-r--r--drivers/gpu/drm/imagination/pvr_gem.c416
-rw-r--r--drivers/gpu/drm/imagination/pvr_gem.h172
-rw-r--r--drivers/gpu/drm/imagination/pvr_hwrt.c546
-rw-r--r--drivers/gpu/drm/imagination/pvr_hwrt.h166
-rw-r--r--drivers/gpu/drm/imagination/pvr_job.c780
-rw-r--r--drivers/gpu/drm/imagination/pvr_job.h161
-rw-r--r--drivers/gpu/drm/imagination/pvr_mmu.c2646
-rw-r--r--drivers/gpu/drm/imagination/pvr_mmu.h108
-rw-r--r--drivers/gpu/drm/imagination/pvr_params.c147
-rw-r--r--drivers/gpu/drm/imagination/pvr_params.h72
-rw-r--r--drivers/gpu/drm/imagination/pvr_power.c703
-rw-r--r--drivers/gpu/drm/imagination/pvr_power.h59
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c1453
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.h173
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_cr_defs.h6308
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_cr_defs_client.h159
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_defs.h179
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif.h2188
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h493
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_client.h373
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h133
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_common.h60
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_dev_info.h113
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_resetframework.h28
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_sf.h1648
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h258
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h108
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_stream.h78
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_heap_config.h113
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_meta.h356
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_mips.h335
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_mips_check.h58
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_mmu_defs.h136
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_riscv.h41
-rw-r--r--drivers/gpu/drm/imagination/pvr_stream.c281
-rw-r--r--drivers/gpu/drm/imagination/pvr_stream.h75
-rw-r--r--drivers/gpu/drm/imagination/pvr_stream_defs.c351
-rw-r--r--drivers/gpu/drm/imagination/pvr_stream_defs.h16
-rw-r--r--drivers/gpu/drm/imagination/pvr_sync.c289
-rw-r--r--drivers/gpu/drm/imagination/pvr_sync.h84
-rw-r--r--drivers/gpu/drm/imagination/pvr_vm.c1194
-rw-r--r--drivers/gpu/drm/imagination/pvr_vm.h70
-rw-r--r--drivers/gpu/drm/imagination/pvr_vm_mips.c237
-rw-r--r--drivers/gpu/drm/imagination/pvr_vm_mips.h22
-rw-r--r--drivers/gpu/drm/imx/Kconfig44
-rw-r--r--drivers/gpu/drm/imx/Makefile12
-rw-r--r--drivers/gpu/drm/imx/dc/Kconfig13
-rw-r--r--drivers/gpu/drm/imx/dc/Makefile7
-rw-r--r--drivers/gpu/drm/imx/dc/dc-cf.c172
-rw-r--r--drivers/gpu/drm/imx/dc/dc-crtc.c555
-rw-r--r--drivers/gpu/drm/imx/dc/dc-de.c177
-rw-r--r--drivers/gpu/drm/imx/dc/dc-de.h59
-rw-r--r--drivers/gpu/drm/imx/dc/dc-drv.c293
-rw-r--r--drivers/gpu/drm/imx/dc/dc-drv.h102
-rw-r--r--drivers/gpu/drm/imx/dc/dc-ed.c288
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fg.c376
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fl.c185
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fu.c258
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fu.h129
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fw.c222
-rw-r--r--drivers/gpu/drm/imx/dc/dc-ic.c282
-rw-r--r--drivers/gpu/drm/imx/dc/dc-kms.c143
-rw-r--r--drivers/gpu/drm/imx/dc/dc-kms.h131
-rw-r--r--drivers/gpu/drm/imx/dc/dc-lb.c325
-rw-r--r--drivers/gpu/drm/imx/dc/dc-pe.c158
-rw-r--r--drivers/gpu/drm/imx/dc/dc-pe.h101
-rw-r--r--drivers/gpu/drm/imx/dc/dc-plane.c224
-rw-r--r--drivers/gpu/drm/imx/dc/dc-tc.c141
-rw-r--r--drivers/gpu/drm/imx/dcss/Kconfig7
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-blkctl.c13
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-crtc.c6
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-ctxld.c14
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dev.c45
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dev.h8
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dpr.c21
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-drv.c38
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dtg.c30
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-kms.c24
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-kms.h1
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-plane.c23
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-scaler.c25
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-ss.c12
-rw-r--r--drivers/gpu/drm/imx/imx-drm.h45
-rw-r--r--drivers/gpu/drm/imx/ipuv3/Kconfig49
-rw-r--r--drivers/gpu/drm/imx/ipuv3/Makefile11
-rw-r--r--drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c (renamed from drivers/gpu/drm/imx/dw_hdmi-imx.c)5
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-drm-core.c (renamed from drivers/gpu/drm/imx/imx-drm-core.c)64
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-drm.h31
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-ldb.c (renamed from drivers/gpu/drm/imx/imx-ldb.c)220
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-tve.c (renamed from drivers/gpu/drm/imx/imx-tve.c)49
-rw-r--r--drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c (renamed from drivers/gpu/drm/imx/ipuv3-crtc.c)14
-rw-r--r--drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c (renamed from drivers/gpu/drm/imx/ipuv3-plane.c)72
-rw-r--r--drivers/gpu/drm/imx/ipuv3/ipuv3-plane.h (renamed from drivers/gpu/drm/imx/ipuv3-plane.h)0
-rw-r--r--drivers/gpu/drm/imx/ipuv3/parallel-display.c288
-rw-r--r--drivers/gpu/drm/imx/lcdc/Kconfig10
-rw-r--r--drivers/gpu/drm/imx/lcdc/Makefile1
-rw-r--r--drivers/gpu/drm/imx/lcdc/imx-lcdc.c537
-rw-r--r--drivers/gpu/drm/imx/parallel-display.c383
-rw-r--r--drivers/gpu/drm/ingenic/Kconfig8
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c170
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-ipu.c26
-rw-r--r--drivers/gpu/drm/kmb/Kconfig5
-rw-r--r--drivers/gpu/drm/kmb/kmb_crtc.c1
-rw-r--r--drivers/gpu/drm/kmb/kmb_drv.c17
-rw-r--r--drivers/gpu/drm/kmb/kmb_drv.h1
-rw-r--r--drivers/gpu/drm/kmb/kmb_dsi.c4
-rw-r--r--drivers/gpu/drm/kmb/kmb_plane.c21
-rw-r--r--drivers/gpu/drm/lib/drm_random.c1
-rw-r--r--drivers/gpu/drm/lib/drm_random.h2
-rw-r--r--drivers/gpu/drm/lima/lima_bcast.c12
-rw-r--r--drivers/gpu/drm/lima/lima_bcast.h3
-rw-r--r--drivers/gpu/drm/lima/lima_ctx.c3
-rw-r--r--drivers/gpu/drm/lima/lima_ctx.h1
-rw-r--r--drivers/gpu/drm/lima/lima_devfreq.c15
-rw-r--r--drivers/gpu/drm/lima/lima_device.c2
-rw-r--r--drivers/gpu/drm/lima/lima_drv.c42
-rw-r--r--drivers/gpu/drm/lima/lima_drv.h5
-rw-r--r--drivers/gpu/drm/lima/lima_gem.c51
-rw-r--r--drivers/gpu/drm/lima/lima_gp.c49
-rw-r--r--drivers/gpu/drm/lima/lima_l2_cache.c6
-rw-r--r--drivers/gpu/drm/lima/lima_mmu.c23
-rw-r--r--drivers/gpu/drm/lima/lima_pmu.c3
-rw-r--r--drivers/gpu/drm/lima/lima_pp.c59
-rw-r--r--drivers/gpu/drm/lima/lima_sched.c79
-rw-r--r--drivers/gpu/drm/lima/lima_sched.h7
-rw-r--r--drivers/gpu/drm/lima/lima_trace.h8
-rw-r--r--drivers/gpu/drm/logicvc/Kconfig7
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_crtc.c2
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_drm.c31
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_interface.c3
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_layer.c15
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_mode.c6
-rw-r--r--drivers/gpu/drm/loongson/Kconfig20
-rw-r--r--drivers/gpu/drm/loongson/Makefile22
-rw-r--r--drivers/gpu/drm/loongson/loongson_device.c102
-rw-r--r--drivers/gpu/drm/loongson/loongson_module.c33
-rw-r--r--drivers/gpu/drm/loongson/loongson_module.h12
-rw-r--r--drivers/gpu/drm/loongson/lsdc_benchmark.c134
-rw-r--r--drivers/gpu/drm/loongson/lsdc_benchmark.h13
-rw-r--r--drivers/gpu/drm/loongson/lsdc_crtc.c1026
-rw-r--r--drivers/gpu/drm/loongson/lsdc_debugfs.c111
-rw-r--r--drivers/gpu/drm/loongson/lsdc_drv.c463
-rw-r--r--drivers/gpu/drm/loongson/lsdc_drv.h388
-rw-r--r--drivers/gpu/drm/loongson/lsdc_gem.c290
-rw-r--r--drivers/gpu/drm/loongson/lsdc_gem.h37
-rw-r--r--drivers/gpu/drm/loongson/lsdc_gfxpll.c199
-rw-r--r--drivers/gpu/drm/loongson/lsdc_gfxpll.h52
-rw-r--r--drivers/gpu/drm/loongson/lsdc_i2c.c179
-rw-r--r--drivers/gpu/drm/loongson/lsdc_i2c.h29
-rw-r--r--drivers/gpu/drm/loongson/lsdc_irq.c75
-rw-r--r--drivers/gpu/drm/loongson/lsdc_irq.h16
-rw-r--r--drivers/gpu/drm/loongson/lsdc_output.h21
-rw-r--r--drivers/gpu/drm/loongson/lsdc_output_7a1000.c178
-rw-r--r--drivers/gpu/drm/loongson/lsdc_output_7a2000.c552
-rw-r--r--drivers/gpu/drm/loongson/lsdc_pixpll.c484
-rw-r--r--drivers/gpu/drm/loongson/lsdc_pixpll.h86
-rw-r--r--drivers/gpu/drm/loongson/lsdc_plane.c794
-rw-r--r--drivers/gpu/drm/loongson/lsdc_probe.c56
-rw-r--r--drivers/gpu/drm/loongson/lsdc_probe.h12
-rw-r--r--drivers/gpu/drm/loongson/lsdc_regs.h406
-rw-r--r--drivers/gpu/drm/loongson/lsdc_ttm.c589
-rw-r--r--drivers/gpu/drm/loongson/lsdc_ttm.h99
-rw-r--r--drivers/gpu/drm/mcde/Kconfig4
-rw-r--r--drivers/gpu/drm/mcde/mcde_clk_div.c13
-rw-r--r--drivers/gpu/drm/mcde/mcde_display.c9
-rw-r--r--drivers/gpu/drm/mcde/mcde_drv.c34
-rw-r--r--drivers/gpu/drm/mcde/mcde_dsi.c16
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig45
-rw-r--r--drivers/gpu/drm/mediatek/Makefile26
-rw-r--r--drivers/gpu/drm/mediatek/mtk_cec.c42
-rw-r--r--drivers/gpu/drm/mediatek/mtk_crtc.c1186
-rw-r--r--drivers/gpu/drm/mediatek/mtk_crtc.h29
-rw-r--r--drivers/gpu/drm/mediatek/mtk_ddp_comp.c710
-rw-r--r--drivers/gpu/drm/mediatek/mtk_ddp_comp.h365
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_aal.c127
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ccorr.c65
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_color.c37
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_drv.h58
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_gamma.c238
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_merge.c106
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl.c366
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c679
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_rdma.c95
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp.c2951
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp_reg.h362
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dpi.c613
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dpi_regs.h9
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.c1008
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.h26
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c576
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h222
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c860
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.h53
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.c267
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.h49
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.c274
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.h45
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c541
-rw-r--r--drivers/gpu/drm/mediatek/mtk_ethdr.c396
-rw-r--r--drivers/gpu/drm/mediatek/mtk_ethdr.h26
-rw-r--r--drivers/gpu/drm/mediatek/mtk_gem.c289
-rw-r--r--drivers/gpu/drm/mediatek/mtk_gem.h48
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.c887
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.h14
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.c456
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.h198
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c34
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c396
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h263
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_v2.c1521
-rw-r--r--drivers/gpu/drm/mediatek/mtk_mdp_rdma.c72
-rw-r--r--drivers/gpu/drm/mediatek/mtk_padding.c154
-rw-r--r--drivers/gpu/drm/mediatek/mtk_plane.c367
-rw-r--r--drivers/gpu/drm/mediatek/mtk_plane.h54
-rw-r--r--drivers/gpu/drm/meson/Kconfig14
-rw-r--r--drivers/gpu/drm/meson/Makefile3
-rw-r--r--drivers/gpu/drm/meson/meson_drv.c127
-rw-r--r--drivers/gpu/drm/meson/meson_drv.h11
-rw-r--r--drivers/gpu/drm/meson/meson_dw_hdmi.c118
-rw-r--r--drivers/gpu/drm/meson/meson_dw_mipi_dsi.c358
-rw-r--r--drivers/gpu/drm/meson/meson_dw_mipi_dsi.h160
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.c61
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.h3
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_dsi.c171
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_dsi.h13
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.c98
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.h3
-rw-r--r--drivers/gpu/drm/meson/meson_overlay.c20
-rw-r--r--drivers/gpu/drm/meson/meson_plane.c31
-rw-r--r--drivers/gpu/drm/meson/meson_registers.h25
-rw-r--r--drivers/gpu/drm/meson/meson_vclk.c226
-rw-r--r--drivers/gpu/drm/meson/meson_vclk.h13
-rw-r--r--drivers/gpu/drm/meson/meson_venc.c248
-rw-r--r--drivers/gpu/drm/meson/meson_venc.h6
-rw-r--r--drivers/gpu/drm/meson/meson_viu.c5
-rw-r--r--drivers/gpu/drm/meson/meson_vpp.c2
-rw-r--r--drivers/gpu/drm/meson/meson_vpp.h2
-rw-r--r--drivers/gpu/drm/mga/Makefile11
-rw-r--r--drivers/gpu/drm/mga/mga_dma.c1168
-rw-r--r--drivers/gpu/drm/mga/mga_drv.c104
-rw-r--r--drivers/gpu/drm/mga/mga_drv.h685
-rw-r--r--drivers/gpu/drm/mga/mga_ioc32.c197
-rw-r--r--drivers/gpu/drm/mga/mga_irq.c169
-rw-r--r--drivers/gpu/drm/mga/mga_state.c1099
-rw-r--r--drivers/gpu/drm/mga/mga_warp.c167
-rw-r--r--drivers/gpu/drm/mgag200/Kconfig15
-rw-r--r--drivers/gpu/drm/mgag200/Makefile7
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_bmc.c99
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_ddc.c178
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_ddc.h11
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c66
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.h234
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200.c215
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh.c238
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh3.c142
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh5.c205
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200er.c275
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200ev.c276
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200ew3.c151
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200se.c392
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200wb.c285
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_i2c.c130
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c909
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_pll.c997
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_reg.h11
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_vga.c73
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_vga_bmc.c157
-rw-r--r--drivers/gpu/drm/msm/.gitignore1
-rw-r--r--drivers/gpu/drm/msm/Kconfig60
-rw-r--r--drivers/gpu/drm/msm/Makefile149
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx.xml.h3212
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_catalog.c48
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_gpu.c112
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_gpu.h7
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_gpummu.c122
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx.xml.h3247
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_catalog.c88
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c105
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx.xml.h4349
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx_catalog.c48
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx_gpu.c91
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx.xml.h5492
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_catalog.c153
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_debugfs.c6
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c273
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.h3
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_power.c6
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_preempt.c53
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx.xml.h7776
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_catalog.c1961
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.c1212
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.h81
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.xml.h483
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu.c2695
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu.h245
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c927
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h473
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_hfi.c403
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_hfi.h43
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_preempt.c477
-rw-r--r--drivers/gpu/drm/msm/adreno/a8xx_gpu.c1201
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_common.xml.h685
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_device.c605
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gen7_0_0_snapshot.h935
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gen7_2_0_snapshot.h755
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gen7_9_0_snapshot.h1446
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c506
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h509
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h2365
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h453
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h494
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h541
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h205
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h184
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h212
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h328
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h318
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h282
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h220
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h333
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h152
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h389
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h413
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h317
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h254
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h225
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h388
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h219
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h152
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h237
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h145
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h163
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h400
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h261
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h429
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h413
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h453
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h408
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h408
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h449
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_core_irq.h54
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c333
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h84
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c914
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h54
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c1637
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h172
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h165
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c450
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c265
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c313
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c865
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_formats.h55
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c1808
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h399
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c257
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.h134
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c597
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h135
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cwb.c75
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cwb.h70
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.c166
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h33
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c396
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.c72
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dspp.h18
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c503
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.h50
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c464
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h77
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.c284
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_lm.h41
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h250
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.c56
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.h21
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.c160
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h86
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c622
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.h210
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.c198
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_top.h32
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.c149
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_util.h55
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.c50
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_vbif.h14
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c149
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.h60
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hwio.h31
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c857
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h79
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c1890
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h94
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c876
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h101
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h222
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c108
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.h22
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c73
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.h3
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4.xml.h1181
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c64
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_dsi_encoder.c32
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_dtv_encoder.c37
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_irq.c9
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c212
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h22
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_lcdc_encoder.c137
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_connector.c120
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_pll.c88
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c147
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5.xml.h1979
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c199
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.h12
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_cmd_encoder.c42
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c51
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c24
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.h1
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_encoder.c71
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_irq.c11
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c271
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h14
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_mixer.c10
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_mixer.h4
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_pipe.c10
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_pipe.h4
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c155
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c39
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.h7
-rw-r--r--drivers/gpu/drm/msm/disp/mdp_common.xml.h111
-rw-r--r--drivers/gpu/drm/msm/disp/mdp_format.c630
-rw-r--r--drivers/gpu/drm/msm/disp/mdp_format.h77
-rw-r--r--drivers/gpu/drm/msm/disp/mdp_kms.h18
-rw-r--r--drivers/gpu/drm/msm/disp/msm_disp_snapshot.c2
-rw-r--r--drivers/gpu/drm/msm/disp/msm_disp_snapshot.h13
-rw-r--r--drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c53
-rw-r--r--drivers/gpu/drm/msm/dp/dp_audio.c662
-rw-r--r--drivers/gpu/drm/msm/dp/dp_audio.h55
-rw-r--r--drivers/gpu/drm/msm/dp/dp_aux.c454
-rw-r--r--drivers/gpu/drm/msm/dp/dp_aux.h30
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.c1096
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.h138
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.c1517
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.h58
-rw-r--r--drivers/gpu/drm/msm/dp/dp_debug.c160
-rw-r--r--drivers/gpu/drm/msm/dp/dp_debug.h54
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.c1600
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.h32
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.c317
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.h29
-rw-r--r--drivers/gpu/drm/msm/dp/dp_hpd.c67
-rw-r--r--drivers/gpu/drm/msm/dp/dp_hpd.h78
-rw-r--r--drivers/gpu/drm/msm/dp/dp_link.c678
-rw-r--r--drivers/gpu/drm/msm/dp/dp_link.h91
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.c674
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.h71
-rw-r--r--drivers/gpu/drm/msm/dp/dp_parser.c293
-rw-r--r--drivers/gpu/drm/msm/dp/dp_parser.h152
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.c257
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.h107
-rw-r--r--drivers/gpu/drm/msm/dp/dp_reg.h58
-rw-r--r--drivers/gpu/drm/msm/dp/dp_utils.c88
-rw-r--r--drivers/gpu/drm/msm/dp/dp_utils.h36
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.c103
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.h55
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.xml.h788
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_cfg.c306
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_cfg.h19
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_host.c945
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_manager.c489
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_phy_10nm.xml.h227
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_phy_14nm.xml.h309
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_phy_20nm.xml.h237
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_phy_28nm.xml.h384
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_phy_28nm_8960.xml.h286
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_phy_7nm.xml.h483
-rw-r--r--drivers/gpu/drm/msm/dsi/mmss_cc.xml.h131
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy.c243
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy.h30
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c426
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c379
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c123
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c523
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c288
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c970
-rw-r--r--drivers/gpu/drm/msm/dsi/sfpb.xml.h70
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c546
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h83
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.xml.h1377
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_audio.c199
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_bridge.c424
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c2
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_hpd.c92
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_i2c.c15
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy.c18
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c47
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c765
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c37
-rw-r--r--drivers/gpu/drm/msm/hdmi/qfprom.xml.h61
-rw-r--r--drivers/gpu/drm/msm/msm_atomic.c46
-rw-r--r--drivers/gpu/drm/msm/msm_debugfs.c171
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c913
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h325
-rw-r--r--drivers/gpu/drm/msm/msm_dsc_helper.h27
-rw-r--r--drivers/gpu/drm/msm/msm_fb.c67
-rw-r--r--drivers/gpu/drm/msm/msm_fbdev.c179
-rw-r--r--drivers/gpu/drm/msm/msm_fence.c93
-rw-r--r--drivers/gpu/drm/msm/msm_fence.h23
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c934
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h452
-rw-r--r--drivers/gpu/drm/msm/msm_gem_prime.c106
-rw-r--r--drivers/gpu/drm/msm/msm_gem_shrinker.c272
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c724
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c1639
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c362
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h279
-rw-r--r--drivers/gpu/drm/msm/msm_gpu_devfreq.c160
-rw-r--r--drivers/gpu/drm/msm/msm_gpu_trace.h90
-rw-r--r--drivers/gpu/drm/msm/msm_gpummu.c121
-rw-r--r--drivers/gpu/drm/msm/msm_io_utils.c39
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c560
-rw-r--r--drivers/gpu/drm/msm/msm_kms.c388
-rw-r--r--drivers/gpu/drm/msm/msm_kms.h77
-rw-r--r--drivers/gpu/drm/msm/msm_mdss.c279
-rw-r--r--drivers/gpu/drm/msm/msm_mmu.h53
-rw-r--r--drivers/gpu/drm/msm/msm_perf.c1
-rw-r--r--drivers/gpu/drm/msm/msm_rd.c94
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c46
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.h48
-rw-r--r--drivers/gpu/drm/msm/msm_submitqueue.c100
-rw-r--r--drivers/gpu/drm/msm/msm_syncobj.c172
-rw-r--r--drivers/gpu/drm/msm/msm_syncobj.h37
-rw-r--r--drivers/gpu/drm/msm/registers/.gitignore4
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a2xx.xml1865
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a3xx.xml1751
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a4xx.xml2409
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a5xx.xml3039
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx.xml5021
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx_descriptors.xml158
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml429
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml267
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx_perfcntrs.xml600
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a7xx_enums.xml216
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a7xx_perfcntrs.xml1030
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml121
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a8xx_enums.xml299
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/adreno_common.xml412
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml2445
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi.xml390
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi_phy_10nm.xml102
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi_phy_14nm.xml135
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi_phy_20nm.xml100
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi_phy_28nm.xml180
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi_phy_28nm_8960.xml134
-rw-r--r--drivers/gpu/drm/msm/registers/display/dsi_phy_7nm.xml261
-rw-r--r--drivers/gpu/drm/msm/registers/display/edp.xml239
-rw-r--r--drivers/gpu/drm/msm/registers/display/hdmi.xml1104
-rw-r--r--drivers/gpu/drm/msm/registers/display/mdp4.xml504
-rw-r--r--drivers/gpu/drm/msm/registers/display/mdp5.xml790
-rw-r--r--drivers/gpu/drm/msm/registers/display/mdp_common.xml90
-rw-r--r--drivers/gpu/drm/msm/registers/display/mdss.xml38
-rw-r--r--drivers/gpu/drm/msm/registers/display/msm.xml32
-rw-r--r--drivers/gpu/drm/msm/registers/display/sfpb.xml17
-rw-r--r--drivers/gpu/drm/msm/registers/freedreno_copyright.xml40
-rw-r--r--drivers/gpu/drm/msm/registers/gen_header.py1007
-rw-r--r--drivers/gpu/drm/msm/registers/rules-fd.xsd404
-rw-r--r--drivers/gpu/drm/mxsfb/Kconfig8
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.c121
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.h4
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_kms.c491
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_regs.h42
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_drv.c67
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_kms.c49
-rw-r--r--drivers/gpu/drm/nouveau/Kbuild20
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig41
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/crtc.c167
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dac.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dfp.c21
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.c24
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.h9
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/hw.c9
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/i2c/Kbuild5
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/i2c/ch7006_drv.c (renamed from drivers/gpu/drm/i2c/ch7006_drv.c)50
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/i2c/ch7006_mode.c (renamed from drivers/gpu/drm/i2c/ch7006_mode.c)8
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/i2c/ch7006_priv.h (renamed from drivers/gpu/drm/i2c/ch7006_priv.h)12
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/i2c/sil164_drv.c (renamed from drivers/gpu/drm/i2c/sil164_drv.c)46
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/nouveau_i2c_encoder.c145
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/overlay.c13
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv04.c30
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv17.c25
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base507c.c26
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base827c.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base907c.c10
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/core.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/core.h6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/core507d.c9
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/corec37d.c9
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/corec57d.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/coreca7d.c122
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/crc.c8
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/crc.h1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/crc907d.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/crcc37d.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/crcc57d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/crcca7d.c98
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/curs.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/curs507a.c30
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/dac507d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/dac907d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c1367
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.h19
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head.c36
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head.h6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head507d.c24
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head827d.c10
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head907d.c26
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head917d.c6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/headc37d.c18
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/headc57d.c26
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/headca7d.c297
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/ovly507e.c11
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/ovly827e.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/ovly907e.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/pior507d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/sor507d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/sor907d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/sorc37d.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/tile.h63
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wimm.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wimmc37b.c7
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.c182
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.h8
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c30
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c11
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c242
-rw-r--r--drivers/gpu/drm/nouveau/gv100_fence.c98
-rw-r--r--drivers/gpu/drm/nouveau/include/dispnv04/i2c/ch7006.h87
-rw-r--r--drivers/gpu/drm/nouveau/include/dispnv04/i2c/encoder_i2c.h220
-rw-r--r--drivers/gpu/drm/nouveau/include/dispnv04/i2c/sil164.h64
-rw-r--r--drivers/gpu/drm/nouveau/include/nvfw/acr.h85
-rw-r--r--drivers/gpu/drm/nouveau/include/nvfw/hs.h30
-rw-r--r--drivers/gpu/drm/nouveau/include/nvfw/ls.h51
-rw-r--r--drivers/gpu/drm/nouveau/include/nvfw/sec2.h45
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/class/clc36f.h137
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/class/clc97b.h22
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/class/clca7d.h868
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/class/clca7e.h137
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gb100/dev_hshub_base.h28
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gb10b/dev_fbhub.h18
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gb202/dev_ce.h12
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gb202/dev_therm.h17
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_falcon_v4.h20
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_fb.h15
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_fsp_pri.h28
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_mmu.h173
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_riscv_pri.h14
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_therm.h17
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/dev_xtl_ep_pri.h10
-rw-r--r--drivers/gpu/drm/nouveau/include/nvhw/ref/gh100/pri_nv_xal_ep.h13
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/chan.h76
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl0046.h23
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl006b.h12
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl0080.h16
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl506e.h13
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl506f.h14
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl5070.h92
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl826e.h15
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl826f.h16
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cl906f.h16
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/cla06f.h18
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/class.h104
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/clb069.h5
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/clc36f.h19
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/client.h11
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/conn.h28
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/device.h37
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/disp.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/driver.h5
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/event.h91
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/head.h23
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0000.h10
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0002.h39
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0003.h34
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0004.h5
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if000c.h26
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if000e.h26
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0010.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0011.h24
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0012.h275
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0013.h35
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0020.h45
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0021.h16
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/ioctl.h98
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/log.h51
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/notify.h35
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/object.h26
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/os.h21
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/outp.h102
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/push.h14
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/push906f.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/pushc97b.h18
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/vmm.h19
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/client.h17
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/device.h31
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/engine.h14
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/event.h66
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/falcon.h174
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h29
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/intr.h73
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/layout.h17
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/memory.h3
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/notify.h39
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/object.h16
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/oclass.h2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/os.h34
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/pci.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h42
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h4
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/ce.h2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h27
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h72
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h102
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/nvdec.h2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/nvenc.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/pm.h29
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/sec2.h5
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/acr.h29
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/bar.h4
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/bios.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/fault.h7
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h21
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/fsp.h24
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/gpio.h6
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h484
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h23
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h7
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/ltc.h16
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h6
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/mmu.h35
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h3
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/top.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/vfn.h23
-rw-r--r--drivers/gpu/drm/nouveau/include/nvrm/nvtypes.h26
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c433
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.h60
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c26
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_backlight.c115
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c33
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c404
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.h71
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo0039.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo5039.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo74c1.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo85b5.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo9039.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo90b5.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_boa0b5.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.c491
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.h38
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c391
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h17
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_crtc.h7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_debugfs.c70
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_debugfs.h15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c202
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c102
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.h21
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dmem.c398
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c398
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c594
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h168
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h51
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_exec.c408
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_exec.h60
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c614
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.h82
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c205
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.h20
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c149
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hwmon.c143
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ioc32.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_led.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_led.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c74
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.h17
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_nvif.c19
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_platform.c36
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_prime.c38
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sched.c524
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sched.h118
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_svm.c132
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_svm.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c64
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_usif.c200
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_usif.h10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.c2005
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.h99
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c26
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vmm.c7
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fbcon.c257
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fence.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv10_fence.c8
-rw-r--r--drivers/gpu/drm/nouveau/nv17_fence.c25
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.h4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fbcon.c299
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fence.c17
-rw-r--r--drivers/gpu/drm/nouveau/nv84_fence.c51
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fbcon.c297
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fence.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvif/Kbuild9
-rw-r--r--drivers/gpu/drm/nouveau/nvif/chan.c159
-rw-r--r--drivers/gpu/drm/nouveau/nvif/chan506f.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvif/chan906f.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvif/chanc36f.c77
-rw-r--r--drivers/gpu/drm/nouveau/nvif/client.c34
-rw-r--r--drivers/gpu/drm/nouveau/nvif/conn.c41
-rw-r--r--drivers/gpu/drm/nouveau/nvif/device.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvif/disp.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvif/driver.c32
-rw-r--r--drivers/gpu/drm/nouveau/nvif/event.c81
-rw-r--r--drivers/gpu/drm/nouveau/nvif/head.c58
-rw-r--r--drivers/gpu/drm/nouveau/nvif/mmu.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvif/notify.c210
-rw-r--r--drivers/gpu/drm/nouveau/nvif/object.c76
-rw-r--r--drivers/gpu/drm/nouveau/nvif/outp.c498
-rw-r--r--drivers/gpu/drm/nouveau/nvif/user.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvif/vmm.c101
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/client.c195
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/engine.c85
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/enum.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/event.c162
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/firmware.c204
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/intr.c442
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/ioctl.c200
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/memory.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/notify.c163
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/object.c78
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/oproxy.c58
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/subdev.c117
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/uevent.c157
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/ga100.c96
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/ga102.c50
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/gb202.c16
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/gt215.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/priv.h12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/ce/tu102.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/cipher/g84.c11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c865
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c71
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/user.c103
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c249
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.c26
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/chan.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c521
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c92
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c97
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c86
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c127
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c101
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c194
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c147
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h62
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c62
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c250
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c154
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/udisp.c25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uhead.c127
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c548
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/falcon.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/Kbuild31
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c472
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.c255
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/cgrp.h76
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c635
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h99
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c263
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/changf100.h29
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/changk104.h52
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv04.h29
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.c276
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/channv50.h53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/chid.c111
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/chid.h25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c226
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv10.c97
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv17.c98
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv40.c254
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/g84.c253
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/g98.c70
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c626
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga102.c294
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gb202.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c942
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.h38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c1506
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h168
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c105
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c59
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c109
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c99
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c46
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifog84.c95
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c308
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c361
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogv100.c241
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifonv50.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifotu102.c81
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c251
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c344
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.h23
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv10.c94
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv17.c103
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv40.c198
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.c381
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv50.h20
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h231
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c430
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.h137
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/runq.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/runq.h31
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/tu102.c473
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/ucgrp.c125
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/uchan.c418
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/user.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/usergv100.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/usertu102.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxga102.c77
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c224
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h78
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf104.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf108.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf110.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf119.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk20a.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm200.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm20b.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c80
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c60
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp104.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp107.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgv100.c62
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxtu102.c35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ga102.c358
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c505
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h81
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf104.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf110.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf117.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf119.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gk104.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gk110b.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gk208.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c119
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gm107.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gm200.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gm20b.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gp104.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gp107.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gp108.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gp10b.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gv100.c203
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv04.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv10.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv20.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv25.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv2a.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv30.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv34.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv35.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c39
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/mpeg/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvdec/ga102.c63
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gm107.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvdec/tu102.c34
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvenc/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvenc/base.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvenc/gm107.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvenc/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/nvenc/tu102.c34
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/Kbuild11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c868
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/g84.c165
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.c243
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gf100.h20
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gf108.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gf117.c80
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gk104.c184
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gt200.c157
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/gt215.c138
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c123
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.h15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/nv50.c175
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h105
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec/g98.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c98
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/ga102.c203
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c116
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/r535.c54
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c34
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/base.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.c26
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/chan.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/gf100.c22
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/nv04.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/nv10.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.c26
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/nv50.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/nvsw.c35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/sw/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/base.c253
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/cmdq.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/fw.c363
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/ga100.c68
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/ga102.c154
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/gm200.c350
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/gp102.c82
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/msgq.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/priv.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/qmgr.h9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/tu102.c28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/v1.c210
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c41
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/nvfw/hs.c39
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c72
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c154
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/ga100.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/ga102.c330
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c199
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c31
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/gv100.c67
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c177
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c144
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h104
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c41
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/base.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/priv.h3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bar/tu102.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c136
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowof.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bus/gf100.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c320
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c185
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/base.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g84.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/g98.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c16
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gt215.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/mcp89.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/r535.c51
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c27
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fault/gp100.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fault/gv100.c41
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fault/priv.h3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fault/tu102.c126
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fault/user.c32
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c81
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c41
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c37
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk104.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gk110.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm107.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm20b.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp100.c18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp102.c110
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gp10b.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c52
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/r535.c87
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.c54
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp102.c31
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c61
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fsp/Kbuild8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fsp/base.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gb100.c24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gb202.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c275
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fsp/priv.h29
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fuse/gm107.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c27
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gpio/gk104.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/Kbuild12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ad102.c66
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c118
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/fwsec.c360
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga102.c200
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gb100.c35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gb202.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gh100.c358
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/gv100.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/priv.h77
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/Kbuild19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/ad10x.c39
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/client.c49
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/engine.c189
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/engine.h20
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/ga100.c28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/ga1xx.c39
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/gb10x.c30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/gb20x.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/gh100.c30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/gpu.h70
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/gr.c87
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/gr.h55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/handles.h18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/nvdec.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/nvenc.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/Kbuild25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c112
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c202
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ce.c46
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/client.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/device.c148
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c1793
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fbsr.c327
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/fifo.c617
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gr.c356
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/gsp.c2205
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvdec.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvenc.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvjpg.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/alloc.h36
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/bar.h29
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/ce.h15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/client.h20
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/ctrl.h21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/device.h30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/disp.h741
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/engine.h260
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/event.h47
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/fbsr.h106
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/fifo.h350
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/gr.h73
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/gsp.h825
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/msgfn.h53
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/nvdec.h17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/nvenc.h17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/nvjpg.h17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/ofa.h16
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/rpcfn.h225
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/nvrm/vmm.h132
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ofa.c44
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rm.c52
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c698
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/vmm.c191
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/Kbuild9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/client.c28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c263
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fbsr.c149
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/fifo.c217
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gr.c191
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c216
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/client.h21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h355
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/engine.h318
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/fbsr.h19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/fifo.h213
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gr.h79
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/gsp.h634
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/msgfn.h57
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/ofa.h17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/rpcfn.h249
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/ofa.c28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/rm.c99
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h191
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rpc.h18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/tu1xx.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c445
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu116.c61
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c215
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxch.c215
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxch.h (renamed from drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h)0
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm200.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gh100.c28
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c23
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv04.c45
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv40.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c76
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/ga102.c62
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gf100.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gk104.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm107.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gm200.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp100.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp102.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/priv.h3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c130
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/g84.c35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/g98.c35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gf100.c82
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk104.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gk20a.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp100.c131
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gp10b.c11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/gt215.c63
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv04.c93
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv11.c21
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv17.c23
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv44.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/nv50.c29
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/priv.h50
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mc/tu102.c136
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/gh100.c25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memgf100.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv04.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/memnv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/priv.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/tu102.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/uvmm.c222
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c202
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.h32
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgf100.c16
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgh100.c306
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c74
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv50.c27
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c54
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/gh100.c30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c25
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c50
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm200.c40
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c56
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp102.c35
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gp10b.c17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gt215.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/privring/gm200.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fanpwm.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/therm/gp100.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/top/ga100.c11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/top/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/Kbuild8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/base.c60
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/ga100.c52
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/gv100.c36
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/priv.h30
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/r535.c57
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/tu102.c113
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/vfn/uvfn.c67
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk20a.c4
-rw-r--r--drivers/gpu/drm/nova/Kconfig16
-rw-r--r--drivers/gpu/drm/nova/Makefile3
-rw-r--r--drivers/gpu/drm/nova/driver.rs71
-rw-r--r--drivers/gpu/drm/nova/file.rs69
-rw-r--r--drivers/gpu/drm/nova/gem.rs47
-rw-r--r--drivers/gpu/drm/nova/nova.rs17
-rw-r--r--drivers/gpu/drm/omapdrm/Kconfig7
-rw-r--r--drivers/gpu/drm/omapdrm/dss/base.c28
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dispc.c164
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dpi.c13
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dsi.c42
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dss.c26
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dss.h13
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi4.c59
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi5.c49
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi5_core.c17
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi5_core.h1
-rw-r--r--drivers/gpu/drm/omapdrm/dss/omapdss.h3
-rw-r--r--drivers/gpu/drm/omapdrm/dss/sdi.c31
-rw-r--r--drivers/gpu/drm/omapdrm/dss/venc.c29
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_debugfs.c7
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c20
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c106
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.h6
-rw-r--r--drivers/gpu/drm/omapdrm/omap_encoder.c4
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c29
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.h5
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c238
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.h15
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c79
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.h3
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c13
-rw-r--r--drivers/gpu/drm/omapdrm/omap_irq.c5
-rw-r--r--drivers/gpu/drm/omapdrm/omap_overlay.c2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_plane.c4
-rw-r--r--drivers/gpu/drm/panel/Kconfig626
-rw-r--r--drivers/gpu/drm/panel/Makefile47
-rw-r--r--drivers/gpu/drm/panel/panel-abt-y030xx067a.c14
-rw-r--r--drivers/gpu/drm/panel/panel-arm-versatile.c13
-rw-r--r--drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c180
-rw-r--r--drivers/gpu/drm/panel/panel-auo-a030jtn01.c307
-rw-r--r--drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c146
-rw-r--r--drivers/gpu/drm/panel/panel-boe-himax8279d.c74
-rw-r--r--drivers/gpu/drm/panel/panel-boe-td4320.c247
-rw-r--r--drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c438
-rw-r--r--drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c241
-rw-r--r--drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c2485
-rw-r--r--drivers/gpu/drm/panel/panel-dsi-cm.c16
-rw-r--r--drivers/gpu/drm/panel/panel-ebbg-ft8719.c82
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c854
-rw-r--r--drivers/gpu/drm/panel/panel-elida-kd35t133.c165
-rw-r--r--drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c16
-rw-r--r--drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c16
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx8279.c1296
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83102.c1088
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83112a.c347
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83112b.c430
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx8394.c845
-rw-r--r--drivers/gpu/drm/panel/panel-hydis-hv101hd1.c188
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9322.c23
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9341.c253
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9805.c405
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9806e.c565
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9881c.c2180
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9882t.c768
-rw-r--r--drivers/gpu/drm/panel/panel-innolux-ej030na.c15
-rw-r--r--drivers/gpu/drm/panel/panel-innolux-p079zca.c348
-rw-r--r--drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c1216
-rw-r--r--drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c189
-rw-r--r--drivers/gpu/drm/panel/panel-jdi-lpm102a188a.c475
-rw-r--r--drivers/gpu/drm/panel/panel-jdi-lt070me05000.c90
-rw-r--r--drivers/gpu/drm/panel/panel-khadas-ts050.c1182
-rw-r--r--drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c66
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c400
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c308
-rw-r--r--drivers/gpu/drm/panel/panel-lg-lb035q02.c10
-rw-r--r--drivers/gpu/drm/panel/panel-lg-ld070wx3.c184
-rw-r--r--drivers/gpu/drm/panel/panel-lg-lg4573.c11
-rw-r--r--drivers/gpu/drm/panel/panel-lg-sw43408.c320
-rw-r--r--drivers/gpu/drm/panel/panel-lincolntech-lcd197.c261
-rw-r--r--drivers/gpu/drm/panel/panel-lvds.c20
-rw-r--r--drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c520
-rw-r--r--drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c104
-rw-r--r--drivers/gpu/drm/panel/panel-nec-nl8048hl11.c10
-rw-r--r--drivers/gpu/drm/panel/panel-newvision-nv3051d.c541
-rw-r--r--drivers/gpu/drm/panel/panel-newvision-nv3052c.c522
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt35510.c454
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt35560.c215
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt35950.c264
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36523.c1272
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36672a.c55
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36672e.c608
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt37801.c340
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt39016.c12
-rw-r--r--drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c62
-rw-r--r--drivers/gpu/drm/panel/panel-orisetech-ota5601a.c360
-rw-r--r--drivers/gpu/drm/panel/panel-orisetech-otm8009a.c39
-rw-r--r--drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c57
-rw-r--r--drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c63
-rw-r--r--drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c23
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm67191.c40
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm67200.c493
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm68200.c53
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm692e5.c373
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm69380.c309
-rw-r--r--drivers/gpu/drm/panel/panel-renesas-r61307.c325
-rw-r--r--drivers/gpu/drm/panel/panel-renesas-r69328.c281
-rw-r--r--drivers/gpu/drm/panel/panel-ronbo-rb070d30.c25
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-ams581vf01.c283
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-ams639rq08.c329
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-atna33xc20.c103
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-db7430.c22
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-ld9040.c50
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6d16d0.c17
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6d27a1.c11
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c492
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c256
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c385
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c31
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c342
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c29
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c5
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.c27
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c768
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c124
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c19
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e8aa5x01-ams561ra01.c981
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-sofef00.c229
-rw-r--r--drivers/gpu/drm/panel/panel-seiko-43wvf1g.c70
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c225
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c80
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c14
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c112
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c99
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c1671
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7701.c1372
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7703.c853
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7789v.c370
-rw-r--r--drivers/gpu/drm/panel/panel-sony-acx565akm.c21
-rw-r--r--drivers/gpu/drm/panel/panel-sony-td4353-jdi.c257
-rw-r--r--drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c487
-rw-r--r--drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c350
-rw-r--r--drivers/gpu/drm/panel/panel-summit.c134
-rw-r--r--drivers/gpu/drm/panel/panel-synaptics-r63353.c331
-rw-r--r--drivers/gpu/drm/panel/panel-synaptics-tddi.c277
-rw-r--r--drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c27
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-td028ttec1.c17
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-td043mtea1.c10
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-tpg110.c20
-rw-r--r--drivers/gpu/drm/panel/panel-truly-nt35597.c31
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-g2647fb105.c280
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-r66451.c349
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-rm69299.c360
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-rm692e5.c442
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-vtdr6130.c325
-rw-r--r--drivers/gpu/drm/panel/panel-widechips-ws2401.c18
-rw-r--r--drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c228
-rw-r--r--drivers/gpu/drm/panfrost/Kconfig4
-rw-r--r--drivers/gpu/drm/panfrost/Makefile3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_devfreq.c60
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_devfreq.h3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c225
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h102
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c565
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_dump.c241
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_dump.h12
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_features.h3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.c241
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.h78
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c37
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c233
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.h6
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c426
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.h42
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.c372
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.h4
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_perfcnt.c36
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_regs.h86
-rw-r--r--drivers/gpu/drm/panthor/Kconfig23
-rw-r--r--drivers/gpu/drm/panthor/Makefile16
-rw-r--r--drivers/gpu/drm/panthor/panthor_devfreq.c334
-rw-r--r--drivers/gpu/drm/panthor/panthor_devfreq.h23
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.c612
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.h548
-rw-r--r--drivers/gpu/drm/panthor/panthor_drv.c1765
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c1506
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.h531
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c455
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.h202
-rw-r--r--drivers/gpu/drm/panthor/panthor_gpu.c396
-rw-r--r--drivers/gpu/drm/panthor/panthor_gpu.h55
-rw-r--r--drivers/gpu/drm/panthor/panthor_heap.c632
-rw-r--r--drivers/gpu/drm/panthor/panthor_heap.h41
-rw-r--r--drivers/gpu/drm/panthor/panthor_hw.c224
-rw-r--r--drivers/gpu/drm/panthor/panthor_hw.h56
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c2833
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.h106
-rw-r--r--drivers/gpu/drm/panthor/panthor_pwr.c549
-rw-r--r--drivers/gpu/drm/panthor/panthor_pwr.h23
-rw-r--r--drivers/gpu/drm/panthor/panthor_regs.h291
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.c4149
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.h57
-rw-r--r--drivers/gpu/drm/pl111/Kconfig4
-rw-r--r--drivers/gpu/drm/pl111/pl111_display.c24
-rw-r--r--drivers/gpu/drm/pl111/pl111_drm.h4
-rw-r--r--drivers/gpu/drm/pl111/pl111_drv.c32
-rw-r--r--drivers/gpu/drm/pl111/pl111_versatile.c14
-rw-r--r--drivers/gpu/drm/qxl/Kconfig4
-rw-r--r--drivers/gpu/drm/qxl/qxl_cmd.c27
-rw-r--r--drivers/gpu/drm/qxl/qxl_debugfs.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c75
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c36
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h23
-rw-r--r--drivers/gpu/drm/qxl/qxl_dumb.c5
-rw-r--r--drivers/gpu/drm/qxl/qxl_gem.c28
-rw-r--r--drivers/gpu/drm/qxl/qxl_image.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_ioctl.c12
-rw-r--r--drivers/gpu/drm/qxl/qxl_irq.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_kms.c5
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.c38
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.h6
-rw-r--r--drivers/gpu/drm/qxl/qxl_prime.c8
-rw-r--r--drivers/gpu/drm/qxl/qxl_release.c70
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c20
-rw-r--r--drivers/gpu/drm/r128/Makefile10
-rw-r--r--drivers/gpu/drm/r128/ati_pcigart.c228
-rw-r--r--drivers/gpu/drm/r128/ati_pcigart.h31
-rw-r--r--drivers/gpu/drm/r128/r128_cce.c944
-rw-r--r--drivers/gpu/drm/r128/r128_drv.c116
-rw-r--r--drivers/gpu/drm/r128/r128_drv.h544
-rw-r--r--drivers/gpu/drm/r128/r128_ioc32.c199
-rw-r--r--drivers/gpu/drm/r128/r128_irq.c118
-rw-r--r--drivers/gpu/drm/r128/r128_state.c1641
-rw-r--r--drivers/gpu/drm/radeon/Kconfig36
-rw-r--r--drivers/gpu/drm/radeon/Makefile5
-rw-r--r--drivers/gpu/drm/radeon/atom-bits.h2
-rw-r--r--drivers/gpu/drm/radeon/atom.c67
-rw-r--r--drivers/gpu/drm/radeon/atom.h4
-rw-r--r--drivers/gpu/drm/radeon/atombios.h59
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c42
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c23
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c129
-rw-r--r--drivers/gpu/drm/radeon/atombios_i2c.c2
-rw-r--r--drivers/gpu/drm/radeon/btc_dpm.c90
-rw-r--r--drivers/gpu/drm/radeon/ci_dpm.c113
-rw-r--r--drivers/gpu/drm/radeon/ci_dpm.h6
-rw-r--r--drivers/gpu/drm/radeon/cik.c134
-rw-r--r--drivers/gpu/drm/radeon/clearstate_cayman.h9
-rw-r--r--drivers/gpu/drm/radeon/clearstate_ci.h3
-rw-r--r--drivers/gpu/drm/radeon/clearstate_evergreen.h8
-rw-r--r--drivers/gpu/drm/radeon/clearstate_si.h3
-rw-r--r--drivers/gpu/drm/radeon/cypress_dpm.c8
-rw-r--r--drivers/gpu/drm/radeon/dce3_1_afmt.c1
-rw-r--r--drivers/gpu/drm/radeon/dce6_afmt.c3
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c40
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c657
-rw-r--r--drivers/gpu/drm/radeon/evergreen_hdmi.c6
-rw-r--r--drivers/gpu/drm/radeon/evergreen_reg.h10
-rw-r--r--drivers/gpu/drm/radeon/evergreen_smc.h9
-rw-r--r--drivers/gpu/drm/radeon/kv_dpm.c11
-rw-r--r--drivers/gpu/drm/radeon/kv_smc.c2
-rw-r--r--drivers/gpu/drm/radeon/ni.c35
-rw-r--r--drivers/gpu/drm/radeon/ni_dpm.c13
-rw-r--r--drivers/gpu/drm/radeon/ni_dpm.h12
-rw-r--r--drivers/gpu/drm/radeon/nislands_smc.h51
-rw-r--r--drivers/gpu/drm/radeon/pptable.h12
-rw-r--r--drivers/gpu/drm/radeon/r100.c324
-rw-r--r--drivers/gpu/drm/radeon/r200.c34
-rw-r--r--drivers/gpu/drm/radeon/r300.c85
-rw-r--r--drivers/gpu/drm/radeon/r300_reg.h2
-rw-r--r--drivers/gpu/drm/radeon/r420.c9
-rw-r--r--drivers/gpu/drm/radeon/r520.c2
-rw-r--r--drivers/gpu/drm/radeon/r600.c22
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c459
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c10
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.h3
-rw-r--r--drivers/gpu/drm/radeon/r600_hdmi.c24
-rw-r--r--drivers/gpu/drm/radeon/radeon.h100
-rw-r--r--drivers/gpu/drm/radeon/radeon_acpi.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_acpi.h9
-rw-r--r--drivers/gpu/drm/radeon/radeon_agp.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c104
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c26
-rw-r--r--drivers/gpu/drm/radeon/radeon_audio.c151
-rw-r--r--drivers/gpu/drm/radeon/radeon_audio.h10
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c39
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c49
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c157
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c58
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c66
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c113
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_auxch.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_mst.c778
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c225
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h16
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c55
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c400
-rw-r--r--drivers/gpu/drm/radeon/radeon_fbdev.c293
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c163
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c45
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c95
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_ib.c18
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c17
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c41
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_encoders.c35
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_tv.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h89
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c47
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h27
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c36
-rw-r--r--drivers/gpu/drm/radeon/radeon_prime.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_sa.c316
-rw-r--r--drivers/gpu/drm/radeon/radeon_semaphore.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_test.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_trace.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c50
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_vm.c18
-rw-r--r--drivers/gpu/drm/radeon/rs400.c31
-rw-r--r--drivers/gpu/drm/radeon/rs600.c17
-rw-r--r--drivers/gpu/drm/radeon/rs690.c2
-rw-r--r--drivers/gpu/drm/radeon/rv515.c12
-rw-r--r--drivers/gpu/drm/radeon/rv6xx_dpm.h3
-rw-r--r--drivers/gpu/drm/radeon/rv740_dpm.c8
-rw-r--r--drivers/gpu/drm/radeon/rv770.c35
-rw-r--r--drivers/gpu/drm/radeon/rv770_dpm.c4
-rw-r--r--drivers/gpu/drm/radeon/rv770_smc.c36
-rw-r--r--drivers/gpu/drm/radeon/rv770_smc.h27
-rw-r--r--drivers/gpu/drm/radeon/si.c150
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c132
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.h21
-rw-r--r--drivers/gpu/drm/radeon/sid.h2
-rw-r--r--drivers/gpu/drm/radeon/sislands_smc.h51
-rw-r--r--drivers/gpu/drm/radeon/smu7.h6
-rw-r--r--drivers/gpu/drm/radeon/smu7_discrete.h51
-rw-r--r--drivers/gpu/drm/radeon/smu7_fusion.h42
-rw-r--r--drivers/gpu/drm/radeon/sumo_dpm.c24
-rw-r--r--drivers/gpu/drm/radeon/trinity_dpm.c26
-rw-r--r--drivers/gpu/drm/radeon/trinity_dpm.h3
-rw-r--r--drivers/gpu/drm/radeon/uvd_v1_0.c2
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig65
-rw-r--r--drivers/gpu/drm/rcar-du/Makefile23
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds.h37
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c808
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h172
-rw-r--r--drivers/gpu/drm/renesas/Kconfig5
-rw-r--r--drivers/gpu/drm/renesas/Makefile5
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/Kconfig77
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/Makefile16
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c (renamed from drivers/gpu/drm/rcar-du/rcar_cmm.c)9
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h (renamed from drivers/gpu/drm/rcar-du/rcar_cmm.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_crtc.c)91
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_crtc.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_drv.c)84
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_drv.h)3
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_encoder.c)13
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_encoder.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_group.c)64
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_group.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_kms.c)99
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_kms.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_plane.c)48
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_plane.h)2
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_regs.h)33
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_vsp.c)111
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_vsp.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.c (renamed from drivers/gpu/drm/rcar-du/rcar_du_writeback.c)4
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.h (renamed from drivers/gpu/drm/rcar-du/rcar_du_writeback.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c (renamed from drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c)4
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c (renamed from drivers/gpu/drm/rcar-du/rcar_lvds.c)260
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h41
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h (renamed from drivers/gpu/drm/rcar-du/rcar_lvds_regs.h)0
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c1350
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h31
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h319
-rw-r--r--drivers/gpu/drm/renesas/rz-du/Kconfig28
-rw-r--r--drivers/gpu/drm/renesas/rz-du/Makefile10
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c422
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.h89
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c199
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h78
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c126
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.h32
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c479
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.h42
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c355
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h82
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c1084
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h203
-rw-r--r--drivers/gpu/drm/renesas/shmobile/Kconfig16
-rw-r--r--drivers/gpu/drm/renesas/shmobile/Makefile7
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c622
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h46
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c297
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h52
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c189
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h32
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c328
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h20
-rw-r--r--drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h (renamed from drivers/gpu/drm/shmobile/shmob_drm_regs.h)0
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig41
-rw-r--r--drivers/gpu/drm/rockchip/Makefile3
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c282
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.c326
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.h10
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-reg.c4
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-reg.h4
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c259
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c508
-rw-r--r--drivers/gpu/drm/rockchip/dw_dp-rockchip.c150
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c353
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c669
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi.c1005
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi.h354
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.c359
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c71
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h28
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fb.c67
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fb.h8
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c46
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c271
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.h24
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.c2081
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.h443
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.c166
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.h23
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_rgb.c23
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_rgb.h8
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop2_reg.c2403
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.c243
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.h6
-rw-r--r--drivers/gpu/drm/savage/Makefile9
-rw-r--r--drivers/gpu/drm/savage/savage_bci.c1082
-rw-r--r--drivers/gpu/drm/savage/savage_drv.c91
-rw-r--r--drivers/gpu/drm/savage/savage_drv.h580
-rw-r--r--drivers/gpu/drm/savage/savage_state.c1169
-rw-r--r--drivers/gpu/drm/scheduler/.kunitconfig12
-rw-r--r--drivers/gpu/drm/scheduler/Makefile2
-rw-r--r--drivers/gpu/drm/scheduler/gpu_scheduler_trace.h111
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c453
-rw-r--r--drivers/gpu/drm/scheduler/sched_fence.c74
-rw-r--r--drivers/gpu/drm/scheduler/sched_internal.h91
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c1084
-rw-r--r--drivers/gpu/drm/scheduler/tests/Makefile7
-rw-r--r--drivers/gpu/drm/scheduler/tests/mock_scheduler.c370
-rw-r--r--drivers/gpu/drm/scheduler/tests/sched_tests.h224
-rw-r--r--drivers/gpu/drm/scheduler/tests/tests_basic.c563
-rw-r--r--drivers/gpu/drm/selftests/Makefile8
-rw-r--r--drivers/gpu/drm/selftests/drm_buddy_selftests.h15
-rw-r--r--drivers/gpu/drm/selftests/drm_cmdline_selftests.h68
-rw-r--r--drivers/gpu/drm/selftests/drm_mm_selftests.h28
-rw-r--r--drivers/gpu/drm/selftests/drm_modeset_selftests.h40
-rw-r--r--drivers/gpu/drm/selftests/drm_selftest.c109
-rw-r--r--drivers/gpu/drm/selftests/drm_selftest.h41
-rw-r--r--drivers/gpu/drm/selftests/test-drm_buddy.c994
-rw-r--r--drivers/gpu/drm/selftests/test-drm_cmdline_parser.c1141
-rw-r--r--drivers/gpu/drm/selftests/test-drm_damage_helper.c668
-rw-r--r--drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c275
-rw-r--r--drivers/gpu/drm/selftests/test-drm_format.c280
-rw-r--r--drivers/gpu/drm/selftests/test-drm_framebuffer.c350
-rw-r--r--drivers/gpu/drm/selftests/test-drm_mm.c2487
-rw-r--r--drivers/gpu/drm/selftests/test-drm_modeset_common.c32
-rw-r--r--drivers/gpu/drm/selftests/test-drm_modeset_common.h52
-rw-r--r--drivers/gpu/drm/selftests/test-drm_plane_helper.c224
-rw-r--r--drivers/gpu/drm/selftests/test-drm_rect.c223
-rw-r--r--drivers/gpu/drm/shmobile/Kconfig12
-rw-r--r--drivers/gpu/drm/shmobile/Makefile8
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_backlight.c82
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_backlight.h19
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_crtc.c684
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_crtc.h55
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.c303
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.h42
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_kms.c151
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_kms.h29
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_plane.c260
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_plane.h19
-rw-r--r--drivers/gpu/drm/sis/Makefile10
-rw-r--r--drivers/gpu/drm/sis/sis_drv.c143
-rw-r--r--drivers/gpu/drm/sis/sis_drv.h80
-rw-r--r--drivers/gpu/drm/sis/sis_mm.c363
-rw-r--r--drivers/gpu/drm/sitronix/Kconfig42
-rw-r--r--drivers/gpu/drm/sitronix/Makefile3
-rw-r--r--drivers/gpu/drm/sitronix/st7571-i2c.c1083
-rw-r--r--drivers/gpu/drm/sitronix/st7586.c (renamed from drivers/gpu/drm/tiny/st7586.c)68
-rw-r--r--drivers/gpu/drm/sitronix/st7735r.c (renamed from drivers/gpu/drm/tiny/st7735r.c)18
-rw-r--r--drivers/gpu/drm/solomon/Kconfig13
-rw-r--r--drivers/gpu/drm/solomon/ssd130x-i2c.c26
-rw-r--r--drivers/gpu/drm/solomon/ssd130x-spi.c67
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c1415
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.h39
-rw-r--r--drivers/gpu/drm/sprd/Kconfig2
-rw-r--r--drivers/gpu/drm/sprd/sprd_dpu.c41
-rw-r--r--drivers/gpu/drm/sprd/sprd_drm.c15
-rw-r--r--drivers/gpu/drm/sprd/sprd_dsi.c26
-rw-r--r--drivers/gpu/drm/sti/Kconfig5
-rw-r--r--drivers/gpu/drm/sti/Makefile2
-rw-r--r--drivers/gpu/drm/sti/sti_compositor.c17
-rw-r--r--drivers/gpu/drm/sti/sti_crtc.c1
-rw-r--r--drivers/gpu/drm/sti/sti_cursor.c18
-rw-r--r--drivers/gpu/drm/sti/sti_drv.c46
-rw-r--r--drivers/gpu/drm/sti/sti_dvo.c57
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.c22
-rw-r--r--drivers/gpu/drm/sti/sti_hda.c66
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c88
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.h4
-rw-r--r--drivers/gpu/drm/sti/sti_hqvdp.c42
-rw-r--r--drivers/gpu/drm/sti/sti_mixer.c2
-rw-r--r--drivers/gpu/drm/sti/sti_plane.c4
-rw-r--r--drivers/gpu/drm/sti/sti_plane.h1
-rw-r--r--drivers/gpu/drm/sti/sti_tvout.c18
-rw-r--r--drivers/gpu/drm/sti/sti_vtg.c22
-rw-r--r--drivers/gpu/drm/stm/Kconfig17
-rw-r--r--drivers/gpu/drm/stm/Makefile2
-rw-r--r--drivers/gpu/drm/stm/drv.c55
-rw-r--r--drivers/gpu/drm/stm/dw_mipi_dsi-stm.c289
-rw-r--r--drivers/gpu/drm/stm/ltdc.c331
-rw-r--r--drivers/gpu/drm/stm/ltdc.h6
-rw-r--r--drivers/gpu/drm/stm/lvds.c1222
-rw-r--r--drivers/gpu/drm/sun4i/Kconfig32
-rw-r--r--drivers/gpu/drm/sun4i/Makefile2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.c28
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_dotclock.c206
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c35
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_frontend.c33
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_frontend.h1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c12
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c205
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c3
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_layer.c1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c75
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c208
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon_dclk.h (renamed from drivers/gpu/drm/sun4i/sun4i_dotclock.h)0
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tv.c199
-rw-r--r--drivers/gpu/drm/sun4i/sun6i_drc.c4
-rw-r--r--drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c65
-rw-r--r--drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h7
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_csc.c113
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_csc.h16
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c8
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c3
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.c396
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.h96
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_tcon_top.c9
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_layer.c282
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_layer.h25
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_scaler.c44
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_scaler.h4
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_layer.c345
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_layer.h25
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_scaler.c49
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_scaler.h6
-rw-r--r--drivers/gpu/drm/sun4i/sunxi_engine.h13
-rw-r--r--drivers/gpu/drm/sysfb/Kconfig76
-rw-r--r--drivers/gpu/drm/sysfb/Makefile12
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb.c35
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_helper.h218
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_modeset.c608
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c104
-rw-r--r--drivers/gpu/drm/sysfb/efidrm.c390
-rw-r--r--drivers/gpu/drm/sysfb/ofdrm.c1145
-rw-r--r--drivers/gpu/drm/sysfb/simpledrm.c885
-rw-r--r--drivers/gpu/drm/sysfb/vesadrm.c652
-rw-r--r--drivers/gpu/drm/tdfx/Makefile8
-rw-r--r--drivers/gpu/drm/tdfx/tdfx_drv.c90
-rw-r--r--drivers/gpu/drm/tdfx/tdfx_drv.h47
-rw-r--r--drivers/gpu/drm/tegra/Kconfig7
-rw-r--r--drivers/gpu/drm/tegra/Makefile6
-rw-r--r--drivers/gpu/drm/tegra/dc.c61
-rw-r--r--drivers/gpu/drm/tegra/dc.h2
-rw-r--r--drivers/gpu/drm/tegra/dp.c67
-rw-r--r--drivers/gpu/drm/tegra/dp.h2
-rw-r--r--drivers/gpu/drm/tegra/dpaux.c43
-rw-r--r--drivers/gpu/drm/tegra/drm.c84
-rw-r--r--drivers/gpu/drm/tegra/drm.h43
-rw-r--r--drivers/gpu/drm/tegra/dsi.c122
-rw-r--r--drivers/gpu/drm/tegra/falcon.c20
-rw-r--r--drivers/gpu/drm/tegra/falcon.h1
-rw-r--r--drivers/gpu/drm/tegra/fb.c252
-rw-r--r--drivers/gpu/drm/tegra/fbdev.c143
-rw-r--r--drivers/gpu/drm/tegra/firewall.c3
-rw-r--r--drivers/gpu/drm/tegra/gem.c113
-rw-r--r--drivers/gpu/drm/tegra/gem.h21
-rw-r--r--drivers/gpu/drm/tegra/gr2d.c31
-rw-r--r--drivers/gpu/drm/tegra/gr3d.c69
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c106
-rw-r--r--drivers/gpu/drm/tegra/hub.c27
-rw-r--r--drivers/gpu/drm/tegra/hub.h3
-rw-r--r--drivers/gpu/drm/tegra/nvdec.c226
-rw-r--r--drivers/gpu/drm/tegra/nvjpg.c330
-rw-r--r--drivers/gpu/drm/tegra/output.c59
-rw-r--r--drivers/gpu/drm/tegra/plane.c17
-rw-r--r--drivers/gpu/drm/tegra/rgb.c55
-rw-r--r--drivers/gpu/drm/tegra/riscv.c106
-rw-r--r--drivers/gpu/drm/tegra/riscv.h30
-rw-r--r--drivers/gpu/drm/tegra/sor.c78
-rw-r--r--drivers/gpu/drm/tegra/submit.c32
-rw-r--r--drivers/gpu/drm/tegra/uapi.c7
-rw-r--r--drivers/gpu/drm/tegra/vic.c70
-rw-r--r--drivers/gpu/drm/tests/.kunitconfig2
-rw-r--r--drivers/gpu/drm/tests/Makefile29
-rw-r--r--drivers/gpu/drm/tests/drm_atomic_state_test.c379
-rw-r--r--drivers/gpu/drm/tests/drm_atomic_test.c153
-rw-r--r--drivers/gpu/drm/tests/drm_bridge_test.c521
-rw-r--r--drivers/gpu/drm/tests/drm_buddy_test.c893
-rw-r--r--drivers/gpu/drm/tests/drm_client_modeset_test.c200
-rw-r--r--drivers/gpu/drm/tests/drm_cmdline_parser_test.c1079
-rw-r--r--drivers/gpu/drm/tests/drm_connector_test.c1818
-rw-r--r--drivers/gpu/drm/tests/drm_damage_helper_test.c640
-rw-r--r--drivers/gpu/drm/tests/drm_dp_mst_helper_test.c577
-rw-r--r--drivers/gpu/drm/tests/drm_exec_test.c222
-rw-r--r--drivers/gpu/drm/tests/drm_fixp_test.c71
-rw-r--r--drivers/gpu/drm/tests/drm_format_helper_test.c1659
-rw-r--r--drivers/gpu/drm/tests/drm_format_test.c360
-rw-r--r--drivers/gpu/drm/tests/drm_framebuffer_test.c725
-rw-r--r--drivers/gpu/drm/tests/drm_gem_shmem_test.c385
-rw-r--r--drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c2334
-rw-r--r--drivers/gpu/drm/tests/drm_kunit_edid.h864
-rw-r--r--drivers/gpu/drm/tests/drm_kunit_helpers.c400
-rw-r--r--drivers/gpu/drm/tests/drm_managed_test.c117
-rw-r--r--drivers/gpu/drm/tests/drm_mm_test.c361
-rw-r--r--drivers/gpu/drm/tests/drm_modes_test.c208
-rw-r--r--drivers/gpu/drm/tests/drm_plane_helper_test.c319
-rw-r--r--drivers/gpu/drm/tests/drm_probe_helper_test.c217
-rw-r--r--drivers/gpu/drm/tests/drm_rect_test.c530
-rw-r--r--drivers/gpu/drm/tests/drm_sysfb_modeset_test.c168
-rw-r--r--drivers/gpu/drm/tidss/Kconfig5
-rw-r--r--drivers/gpu/drm/tidss/Makefile3
-rw-r--r--drivers/gpu/drm/tidss/tidss_crtc.c76
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.c930
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.h42
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc_regs.h107
-rw-r--r--drivers/gpu/drm/tidss/tidss_drv.c70
-rw-r--r--drivers/gpu/drm/tidss/tidss_drv.h14
-rw-r--r--drivers/gpu/drm/tidss/tidss_encoder.c147
-rw-r--r--drivers/gpu/drm/tidss/tidss_encoder.h5
-rw-r--r--drivers/gpu/drm/tidss/tidss_irq.c84
-rw-r--r--drivers/gpu/drm/tidss/tidss_irq.h4
-rw-r--r--drivers/gpu/drm/tidss/tidss_kms.c34
-rw-r--r--drivers/gpu/drm/tidss/tidss_oldi.c619
-rw-r--r--drivers/gpu/drm/tidss/tidss_oldi.h43
-rw-r--r--drivers/gpu/drm/tidss/tidss_plane.c57
-rw-r--r--drivers/gpu/drm/tidss/tidss_plane.h4
-rw-r--r--drivers/gpu/drm/tidss/tidss_scale_coefs.h2
-rw-r--r--drivers/gpu/drm/tilcdc/Kconfig3
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_crtc.c19
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c60
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_panel.c12
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_plane.c13
-rw-r--r--drivers/gpu/drm/tiny/Kconfig116
-rw-r--r--drivers/gpu/drm/tiny/Makefile8
-rw-r--r--drivers/gpu/drm/tiny/appletbdrm.c834
-rw-r--r--drivers/gpu/drm/tiny/arcpgu.c32
-rw-r--r--drivers/gpu/drm/tiny/bochs.c455
-rw-r--r--drivers/gpu/drm/tiny/cirrus-qemu.c675
-rw-r--r--drivers/gpu/drm/tiny/cirrus.c644
-rw-r--r--drivers/gpu/drm/tiny/gm12u320.c94
-rw-r--r--drivers/gpu/drm/tiny/hx8357d.c18
-rw-r--r--drivers/gpu/drm/tiny/ili9163.c19
-rw-r--r--drivers/gpu/drm/tiny/ili9225.c62
-rw-r--r--drivers/gpu/drm/tiny/ili9341.c18
-rw-r--r--drivers/gpu/drm/tiny/ili9486.c37
-rw-r--r--drivers/gpu/drm/tiny/mi0283qt.c19
-rw-r--r--drivers/gpu/drm/tiny/panel-mipi-dbi.c86
-rw-r--r--drivers/gpu/drm/tiny/pixpaper.c1166
-rw-r--r--drivers/gpu/drm/tiny/repaper.c64
-rw-r--r--drivers/gpu/drm/tiny/sharp-memory.c669
-rw-r--r--drivers/gpu/drm/tiny/simpledrm.c936
-rw-r--r--drivers/gpu/drm/ttm/Makefile3
-rw-r--r--drivers/gpu/drm/ttm/tests/.kunitconfig3
-rw-r--r--drivers/gpu/drm/ttm/tests/Makefile11
-rw-r--r--drivers/gpu/drm/ttm/tests/TODO27
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_bo_test.c637
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c1176
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_device_test.c206
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c304
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h52
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_mock_manager.c238
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_mock_manager.h30
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_pool_test.c437
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_resource_test.c337
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_tt_test.c402
-rw-r--r--drivers/gpu/drm/ttm/ttm_agp_backend.c1
-rw-r--r--drivers/gpu/drm/ttm/ttm_backup.c182
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c1251
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_internal.h60
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c612
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c98
-rw-r--r--drivers/gpu/drm/ttm/ttm_device.c123
-rw-r--r--drivers/gpu/drm/ttm/ttm_execbuf_util.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_module.c3
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool.c900
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool_internal.h25
-rw-r--r--drivers/gpu/drm/ttm/ttm_range_manager.c40
-rw-r--r--drivers/gpu/drm/ttm/ttm_resource.c471
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c129
-rw-r--r--drivers/gpu/drm/tve200/Kconfig4
-rw-r--r--drivers/gpu/drm/tve200/tve200_display.c13
-rw-r--r--drivers/gpu/drm/tve200/tve200_drv.c38
-rw-r--r--drivers/gpu/drm/tyr/Kconfig19
-rw-r--r--drivers/gpu/drm/tyr/Makefile3
-rw-r--r--drivers/gpu/drm/tyr/driver.rs205
-rw-r--r--drivers/gpu/drm/tyr/file.rs56
-rw-r--r--drivers/gpu/drm/tyr/gem.rs18
-rw-r--r--drivers/gpu/drm/tyr/gpu.rs219
-rw-r--r--drivers/gpu/drm/tyr/regs.rs108
-rw-r--r--drivers/gpu/drm/tyr/tyr.rs22
-rw-r--r--drivers/gpu/drm/udl/Kconfig1
-rw-r--r--drivers/gpu/drm/udl/Makefile8
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c139
-rw-r--r--drivers/gpu/drm/udl/udl_connector.h15
-rw-r--r--drivers/gpu/drm/udl/udl_drv.c39
-rw-r--r--drivers/gpu/drm/udl/udl_drv.h54
-rw-r--r--drivers/gpu/drm/udl/udl_edid.c81
-rw-r--r--drivers/gpu/drm/udl/udl_edid.h15
-rw-r--r--drivers/gpu/drm/udl/udl_main.c311
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c550
-rw-r--r--drivers/gpu/drm/udl/udl_proto.h68
-rw-r--r--drivers/gpu/drm/udl/udl_transfer.c61
-rw-r--r--drivers/gpu/drm/v3d/Makefile5
-rw-r--r--drivers/gpu/drm/v3d/v3d_bo.c97
-rw-r--r--drivers/gpu/drm/v3d/v3d_debugfs.c222
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.c209
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h278
-rw-r--r--drivers/gpu/drm/v3d/v3d_fence.c11
-rw-r--r--drivers/gpu/drm/v3d/v3d_gem.c894
-rw-r--r--drivers/gpu/drm/v3d/v3d_gemfs.c62
-rw-r--r--drivers/gpu/drm/v3d/v3d_irq.c202
-rw-r--r--drivers/gpu/drm/v3d/v3d_mmu.c95
-rw-r--r--drivers/gpu/drm/v3d/v3d_perfmon.c316
-rw-r--r--drivers/gpu/drm/v3d/v3d_performance_counters.h33
-rw-r--r--drivers/gpu/drm/v3d/v3d_regs.h141
-rw-r--r--drivers/gpu/drm/v3d/v3d_sched.c658
-rw-r--r--drivers/gpu/drm/v3d/v3d_submit.c1407
-rw-r--r--drivers/gpu/drm/v3d/v3d_sysfs.c66
-rw-r--r--drivers/gpu/drm/v3d/v3d_trace.h57
-rw-r--r--drivers/gpu/drm/vboxvideo/Kconfig1
-rw-r--r--drivers/gpu/drm/vboxvideo/hgsmi_base.c47
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_drv.c34
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_drv.h1
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_irq.c1
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_main.c27
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_mode.c25
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_ttm.c1
-rw-r--r--drivers/gpu/drm/vboxvideo/vboxvideo.h6
-rw-r--r--drivers/gpu/drm/vboxvideo/vboxvideo_guest.h2
-rw-r--r--drivers/gpu/drm/vc4/Kconfig23
-rw-r--r--drivers/gpu/drm/vc4/Makefile7
-rw-r--r--drivers/gpu/drm/vc4/tests/.kunitconfig13
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock.c207
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock.h61
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c41
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_output.c176
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_plane.c25
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c1140
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c124
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c514
-rw-r--r--drivers/gpu/drm/vc4/vc4_debugfs.c63
-rw-r--r--drivers/gpu/drm/vc4/vc4_dpi.c170
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c126
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h325
-rw-r--r--drivers/gpu/drm/vc4/vc4_dsi.c303
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c301
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c2194
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.h84
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi_phy.c646
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi_regs.h226
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c1303
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c19
-rw-r--r--drivers/gpu/drm/vc4/vc4_kms.c296
-rw-r--r--drivers/gpu/drm/vc4/vc4_perfmon.c36
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c1366
-rw-r--r--drivers/gpu/drm/vc4/vc4_regs.h320
-rw-r--r--drivers/gpu/drm/vc4/vc4_render_cl.c44
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c222
-rw-r--r--drivers/gpu/drm/vc4/vc4_v3d.c110
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate.c61
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate_shaders.c6
-rw-r--r--drivers/gpu/drm/vc4/vc4_vec.c647
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c32
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.h11
-rw-r--r--drivers/gpu/drm/vgem/vgem_fence.c22
-rw-r--r--drivers/gpu/drm/via/Makefile8
-rw-r--r--drivers/gpu/drm/via/via_3d_reg.h1650
-rw-r--r--drivers/gpu/drm/via/via_dma.c744
-rw-r--r--drivers/gpu/drm/via/via_dmablit.c807
-rw-r--r--drivers/gpu/drm/via/via_dmablit.h140
-rw-r--r--drivers/gpu/drm/via/via_drv.c124
-rw-r--r--drivers/gpu/drm/via/via_drv.h229
-rw-r--r--drivers/gpu/drm/via/via_irq.c388
-rw-r--r--drivers/gpu/drm/via/via_map.c132
-rw-r--r--drivers/gpu/drm/via/via_mm.c241
-rw-r--r--drivers/gpu/drm/via/via_verifier.c1110
-rw-r--r--drivers/gpu/drm/via/via_verifier.h62
-rw-r--r--drivers/gpu/drm/via/via_video.c94
-rw-r--r--drivers/gpu/drm/virtio/Kconfig12
-rw-r--r--drivers/gpu/drm/virtio/Makefile2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_debugfs.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c64
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.c131
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.h61
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_fence.c16
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_gem.c50
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_ioctl.c251
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c92
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c101
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_plane.c294
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_prime.c183
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_submit.c542
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_trace.h26
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c237
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vram.c11
-rw-r--r--drivers/gpu/drm/vkms/Kconfig32
-rw-r--r--drivers/gpu/drm/vkms/Makefile9
-rw-r--r--drivers/gpu/drm/vkms/tests/.kunitconfig4
-rw-r--r--drivers/gpu/drm/vkms/tests/Makefile8
-rw-r--r--drivers/gpu/drm/vkms/tests/vkms_color_test.c414
-rw-r--r--drivers/gpu/drm/vkms/tests/vkms_config_test.c1029
-rw-r--r--drivers/gpu/drm/vkms/tests/vkms_format_test.c279
-rw-r--r--drivers/gpu/drm/vkms/vkms_colorop.c120
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c705
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.h28
-rw-r--r--drivers/gpu/drm/vkms/vkms_config.c649
-rw-r--r--drivers/gpu/drm/vkms/vkms_config.h489
-rw-r--r--drivers/gpu/drm/vkms/vkms_configfs.c843
-rw-r--r--drivers/gpu/drm/vkms/vkms_configfs.h8
-rw-r--r--drivers/gpu/drm/vkms/vkms_connector.c96
-rw-r--r--drivers/gpu/drm/vkms/vkms_connector.h35
-rw-r--r--drivers/gpu/drm/vkms/vkms_crtc.c139
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.c166
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.h266
-rw-r--r--drivers/gpu/drm/vkms/vkms_formats.c971
-rw-r--r--drivers/gpu/drm/vkms/vkms_formats.h21
-rw-r--r--drivers/gpu/drm/vkms/vkms_luts.c811
-rw-r--r--drivers/gpu/drm/vkms/vkms_luts.h12
-rw-r--r--drivers/gpu/drm/vkms/vkms_output.c190
-rw-r--r--drivers/gpu/drm/vkms/vkms_plane.c186
-rw-r--r--drivers/gpu/drm/vkms/vkms_writeback.c98
-rw-r--r--drivers/gpu/drm/vmwgfx/Kconfig12
-rw-r--r--drivers/gpu/drm/vmwgfx/Makefile8
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h2
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.c168
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.h41
-rw-r--r--drivers/gpu/drm/vmwgfx/vmw_surface_cache.h10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_binding.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_blit.c151
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.c595
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.h238
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c17
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c55
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c70
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_context.c36
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c100
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c858
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h82
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c311
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h462
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c389
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c831
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c525
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.h21
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gem.c222
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c16
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_hashtab.c199
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_hashtab.h83
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_irq.c61
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c1788
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.h179
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c170
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_mob.c46
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_msg.c274
-rw-r--r--[-rwxr-xr-x]drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h196
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_msg_x86.h177
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c30
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c145
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_prime.c41
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c346
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c165
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_shader.c70
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_so.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c659
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c20
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_surface.c465
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c212
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c110
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_va.c6
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_validation.c229
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_validation.h43
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c633
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h75
-rw-r--r--drivers/gpu/drm/xe/.gitignore4
-rw-r--r--drivers/gpu/drm/xe/.kunitconfig18
-rw-r--r--drivers/gpu/drm/xe/Kconfig141
-rw-r--r--drivers/gpu/drm/xe/Kconfig.debug114
-rw-r--r--drivers/gpu/drm/xe/Kconfig.profile55
-rw-r--r--drivers/gpu/drm/xe/Makefile384
-rw-r--r--drivers/gpu/drm/xe/abi/gsc_command_header_abi.h46
-rw-r--r--drivers/gpu/drm/xe/abi/gsc_mkhi_commands_abi.h39
-rw-r--r--drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h44
-rw-r--r--drivers/gpu/drm/xe/abi/gsc_pxp_commands_abi.h100
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_abi.h281
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h279
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h659
-rw-r--r--drivers/gpu/drm/xe/abi/guc_capture_abi.h186
-rw-r--r--drivers/gpu/drm/xe/abi/guc_communication_ctb_abi.h129
-rw-r--r--drivers/gpu/drm/xe/abi/guc_communication_mmio_abi.h49
-rw-r--r--drivers/gpu/drm/xe/abi/guc_errors_abi.h101
-rw-r--r--drivers/gpu/drm/xe/abi/guc_klvs_abi.h428
-rw-r--r--drivers/gpu/drm/xe/abi/guc_log_abi.h75
-rw-r--r--drivers/gpu/drm/xe/abi/guc_messages_abi.h251
-rw-r--r--drivers/gpu/drm/xe/abi/guc_relay_actions_abi.h247
-rw-r--r--drivers/gpu/drm/xe/abi/guc_relay_communication_abi.h118
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h40
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h11
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_active.h22
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_config.h19
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h37
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_gtt_view_types.h7
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_reg.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_reg_defs.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h18
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h36
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_clock_gating.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_mchbar_regs.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_pci_config.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_pcode.h11
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_step.h14
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h162
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_uncore_trace.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h10
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h29
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h6
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/vlv_iosf_sb.h42
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/vlv_iosf_sb_reg.h6
-rw-r--r--drivers/gpu/drm/xe/display/ext/i915_irq.c85
-rw-r--r--drivers/gpu/drm/xe/display/intel_bo.c103
-rw-r--r--drivers/gpu/drm/xe/display/intel_fb_bo.c91
-rw-r--r--drivers/gpu/drm/xe/display/intel_fbdev_fb.c91
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c560
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h71
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_misc.c16
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_rpm.c74
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_rpm.h11
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_wa.c19
-rw-r--r--drivers/gpu/drm/xe/display/xe_dsb_buffer.c79
-rw-r--r--drivers/gpu/drm/xe/display/xe_fb_pin.c482
-rw-r--r--drivers/gpu/drm/xe/display/xe_hdcp_gsc.c213
-rw-r--r--drivers/gpu/drm/xe/display/xe_panic.c102
-rw-r--r--drivers/gpu/drm/xe/display/xe_plane_initial.c319
-rw-r--r--drivers/gpu/drm/xe/display/xe_stolen.c123
-rw-r--r--drivers/gpu/drm/xe/display/xe_tdf.c15
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_alu_commands.h79
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gfx_state_commands.h18
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gfxpipe_commands.h164
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gpu_commands.h77
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gsc_commands.h36
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_instr_defs.h35
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_mfx_commands.h28
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_mi_commands.h84
-rw-r--r--drivers/gpu/drm/xe/regs/xe_bars.h12
-rw-r--r--drivers/gpu/drm/xe/regs/xe_engine_regs.h220
-rw-r--r--drivers/gpu/drm/xe/regs/xe_eu_stall_regs.h29
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gsc_regs.h58
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h620
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gtt_defs.h37
-rw-r--r--drivers/gpu/drm/xe/regs/xe_guc_regs.h154
-rw-r--r--drivers/gpu/drm/xe/regs/xe_hw_error_regs.h20
-rw-r--r--drivers/gpu/drm/xe/regs/xe_i2c_regs.h23
-rw-r--r--drivers/gpu/drm/xe/regs/xe_irq_regs.h96
-rw-r--r--drivers/gpu/drm/xe/regs/xe_lrc_layout.h43
-rw-r--r--drivers/gpu/drm/xe/regs/xe_mchbar_regs.h48
-rw-r--r--drivers/gpu/drm/xe/regs/xe_oa_regs.h103
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pcode_regs.h27
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pmt.h35
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pxp_regs.h23
-rw-r--r--drivers/gpu/drm/xe/regs/xe_reg_defs.h137
-rw-r--r--drivers/gpu/drm/xe/regs/xe_regs.h65
-rw-r--r--drivers/gpu/drm/xe/tests/Makefile13
-rw-r--r--drivers/gpu/drm/xe/tests/xe_args_test.c221
-rw-r--r--drivers/gpu/drm/xe/tests/xe_bo.c637
-rw-r--r--drivers/gpu/drm/xe/tests/xe_dma_buf.c301
-rw-r--r--drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_config_kunit.c208
-rw-r--r--drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c333
-rw-r--r--drivers/gpu/drm/xe/tests/xe_guc_db_mgr_test.c201
-rw-r--r--drivers/gpu/drm/xe/tests/xe_guc_g2g_test.c776
-rw-r--r--drivers/gpu/drm/xe/tests/xe_guc_id_mgr_test.c136
-rw-r--r--drivers/gpu/drm/xe/tests/xe_guc_relay_test.c522
-rw-r--r--drivers/gpu/drm/xe/tests/xe_kunit_helpers.c129
-rw-r--r--drivers/gpu/drm/xe/tests/xe_kunit_helpers.h19
-rw-r--r--drivers/gpu/drm/xe/tests/xe_live_test_mod.c25
-rw-r--r--drivers/gpu/drm/xe/tests/xe_lmtt_test.c73
-rw-r--r--drivers/gpu/drm/xe/tests/xe_migrate.c791
-rw-r--r--drivers/gpu/drm/xe/tests/xe_mocs.c204
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci.c412
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci_test.c76
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci_test.h36
-rw-r--r--drivers/gpu/drm/xe/tests/xe_rtp_test.c548
-rw-r--r--drivers/gpu/drm/xe/tests/xe_sriov_pf_service_kunit.c227
-rw-r--r--drivers/gpu/drm/xe/tests/xe_test.h63
-rw-r--r--drivers/gpu/drm/xe/tests/xe_test_mod.c10
-rw-r--r--drivers/gpu/drm/xe/tests/xe_wa_test.c73
-rw-r--r--drivers/gpu/drm/xe/xe_args.h143
-rw-r--r--drivers/gpu/drm/xe/xe_assert.h176
-rw-r--r--drivers/gpu/drm/xe/xe_bb.c148
-rw-r--r--drivers/gpu/drm/xe/xe_bb.h28
-rw-r--r--drivers/gpu/drm/xe/xe_bb_types.h20
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c3666
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h454
-rw-r--r--drivers/gpu/drm/xe/xe_bo_doc.h179
-rw-r--r--drivers/gpu/drm/xe/xe_bo_evict.c352
-rw-r--r--drivers/gpu/drm/xe/xe_bo_evict.h21
-rw-r--r--drivers/gpu/drm/xe/xe_bo_types.h113
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.c1291
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.h47
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.c440
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.h17
-rw-r--r--drivers/gpu/drm/xe/xe_dep_job_types.h29
-rw-r--r--drivers/gpu/drm/xe/xe_dep_scheduler.c143
-rw-r--r--drivers/gpu/drm/xe/xe_dep_scheduler.h21
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c522
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.h35
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump_types.h93
-rw-r--r--drivers/gpu/drm/xe/xe_device.c1297
-rw-r--r--drivers/gpu/drm/xe/xe_device.h212
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.c296
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.h13
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h726
-rw-r--r--drivers/gpu/drm/xe/xe_device_wa_oob.rules5
-rw-r--r--drivers/gpu/drm/xe/xe_dma_buf.c369
-rw-r--r--drivers/gpu/drm/xe/xe_dma_buf.h15
-rw-r--r--drivers/gpu/drm/xe/xe_drm_client.c406
-rw-r--r--drivers/gpu/drm/xe/xe_drm_client.h70
-rw-r--r--drivers/gpu/drm/xe/xe_drv.h22
-rw-r--r--drivers/gpu/drm/xe/xe_eu_stall.c989
-rw-r--r--drivers/gpu/drm/xe/xe_eu_stall.h25
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c367
-rw-r--r--drivers/gpu/drm/xe/xe_exec.h14
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c1251
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h114
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h242
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c495
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.h21
-rw-r--r--drivers/gpu/drm/xe/xe_execlist_types.h51
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake.c261
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake.h64
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake_types.h110
-rw-r--r--drivers/gpu/drm/xe/xe_gen_wa_oob.c212
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c1124
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.h61
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt_types.h85
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c138
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.h120
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler_types.h57
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.c640
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.h27
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_debugfs.c71
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_debugfs.h14
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_proxy.c535
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_proxy.h21
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_submit.c220
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_submit.h32
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_types.h74
-rw-r--r--drivers/gpu/drm/xe/xe_gt.c1069
-rw-r--r--drivers/gpu/drm/xe/xe_gt.h146
-rw-r--r--drivers/gpu/drm/xe/xe_gt_ccs_mode.c202
-rw-r--r--drivers/gpu/drm/xe/xe_gt_ccs_mode.h24
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.c88
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.h16
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.c379
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.h16
-rw-r--r--drivers/gpu/drm/xe/xe_gt_freq.c304
-rw-r--r--drivers/gpu/drm/xe/xe_gt_freq.h13
-rw-r--r--drivers/gpu/drm/xe/xe_gt_idle.c416
-rw-r--r--drivers/gpu/drm/xe/xe_gt_idle.h22
-rw-r--r--drivers/gpu/drm/xe/xe_gt_idle_types.h43
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.c888
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.h74
-rw-r--r--drivers/gpu/drm/xe/xe_gt_printk.h128
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.c283
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.h43
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c2938
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h92
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h59
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c2157
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h43
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h137
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c622
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_helpers.h35
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c1069
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.h54
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration_types.h28
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c147
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h27
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor_types.h22
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c435
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h25
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h31
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c425
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service.h34
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service_types.h52
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h65
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_printk.h37
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.c1377
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.h43
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c72
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.h14
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h81
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats.c101
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats.h26
-rw-r--r--drivers/gpu/drm/xe/xe_gt_stats_types.h51
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sysfs.c55
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sysfs.h19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sysfs_types.h26
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle.c270
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle.h17
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.c372
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.h56
-rw-r--r--drivers/gpu/drm/xe/xe_gt_types.h370
-rw-r--r--drivers/gpu/drm/xe/xe_guard.h119
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c1719
-rw-r--r--drivers/gpu/drm/xe/xe_guc.h97
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c1013
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.h18
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads_types.h29
-rw-r--r--drivers/gpu/drm/xe/xe_guc_buf.c207
-rw-r--r--drivers/gpu/drm/xe/xe_guc_buf.h49
-rw-r--r--drivers/gpu/drm/xe/xe_guc_buf_types.h28
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture.c2044
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture.h61
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture_types.h70
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c2148
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h86
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct_types.h173
-rw-r--r--drivers/gpu/drm/xe/xe_guc_db_mgr.c267
-rw-r--r--drivers/gpu/drm/xe/xe_guc_db_mgr.h22
-rw-r--r--drivers/gpu/drm/xe/xe_guc_debugfs.c146
-rw-r--r--drivers/gpu/drm/xe/xe_guc_debugfs.h14
-rw-r--r--drivers/gpu/drm/xe/xe_guc_engine_activity.c521
-rw-r--r--drivers/gpu/drm/xe/xe_guc_engine_activity.h22
-rw-r--r--drivers/gpu/drm/xe/xe_guc_engine_activity_types.h102
-rw-r--r--drivers/gpu/drm/xe/xe_guc_exec_queue_types.h71
-rw-r--r--drivers/gpu/drm/xe/xe_guc_fwif.h366
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hwconfig.c202
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hwconfig.h20
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hxg_helpers.h108
-rw-r--r--drivers/gpu/drm/xe/xe_guc_id_mgr.c280
-rw-r--r--drivers/gpu/drm/xe/xe_guc_id_mgr.h22
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_helpers.c148
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_helpers.h64
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_thresholds_set.h71
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h68
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.c375
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.h61
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log_types.h57
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pagefault.c95
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pagefault.h15
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c1439
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.h46
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc_types.h40
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay.c967
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay.h37
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay_types.h40
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c2986
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.h52
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit_types.h144
-rw-r--r--drivers/gpu/drm/xe/xe_guc_tlb_inval.c242
-rw-r--r--drivers/gpu/drm/xe/xe_guc_tlb_inval.h19
-rw-r--r--drivers/gpu/drm/xe/xe_guc_types.h129
-rw-r--r--drivers/gpu/drm/xe/xe_heci_gsc.c249
-rw-r--r--drivers/gpu/drm/xe/xe_heci_gsc.h40
-rw-r--r--drivers/gpu/drm/xe/xe_huc.c318
-rw-r--r--drivers/gpu/drm/xe/xe_huc.h28
-rw-r--r--drivers/gpu/drm/xe/xe_huc_debugfs.c71
-rw-r--r--drivers/gpu/drm/xe/xe_huc_debugfs.h14
-rw-r--r--drivers/gpu/drm/xe/xe_huc_types.h24
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c1111
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.h82
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c685
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.h43
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group.c344
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group.h29
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_group_types.h51
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_types.h185
-rw-r--r--drivers/gpu/drm/xe/xe_hw_error.c182
-rw-r--r--drivers/gpu/drm/xe/xe_hw_error.h15
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence.c268
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence.h33
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence_types.h75
-rw-r--r--drivers/gpu/drm/xe/xe_hwmon.c1334
-rw-r--r--drivers/gpu/drm/xe/xe_hwmon.h19
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c372
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.h68
-rw-r--r--drivers/gpu/drm/xe/xe_irq.c1050
-rw-r--r--drivers/gpu/drm/xe/xe_irq.h26
-rw-r--r--drivers/gpu/drm/xe/xe_late_bind_fw.c464
-rw-r--r--drivers/gpu/drm/xe/xe_late_bind_fw.h17
-rw-r--r--drivers/gpu/drm/xe/xe_late_bind_fw_types.h75
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt.c573
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt.h28
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt_2l.c150
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt_ml.c161
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt_types.h63
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c2414
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h163
-rw-r--r--drivers/gpu/drm/xe/xe_lrc_types.h61
-rw-r--r--drivers/gpu/drm/xe/xe_macros.h22
-rw-r--r--drivers/gpu/drm/xe/xe_map.h93
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.c547
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.h30
-rw-r--r--drivers/gpu/drm/xe/xe_memirq_types.h37
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c2470
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.h174
-rw-r--r--drivers/gpu/drm/xe/xe_migrate_doc.h88
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.c410
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.h49
-rw-r--r--drivers/gpu/drm/xe/xe_mmio_gem.c226
-rw-r--r--drivers/gpu/drm/xe/xe_mmio_gem.h20
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.c838
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.h16
-rw-r--r--drivers/gpu/drm/xe/xe_module.c183
-rw-r--r--drivers/gpu/drm/xe/xe_module.h31
-rw-r--r--drivers/gpu/drm/xe/xe_nvm.c170
-rw-r--r--drivers/gpu/drm/xe/xe_nvm.h15
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c2816
-rw-r--r--drivers/gpu/drm/xe/xe_oa.h25
-rw-r--r--drivers/gpu/drm/xe/xe_oa_types.h271
-rw-r--r--drivers/gpu/drm/xe/xe_observation.c106
-rw-r--r--drivers/gpu/drm/xe/xe_observation.h20
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.c444
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.h19
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault_types.h136
-rw-r--r--drivers/gpu/drm/xe/xe_pat.c580
-rw-r--r--drivers/gpu/drm/xe/xe_pat.h61
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c1255
-rw-r--r--drivers/gpu/drm/xe/xe_pci.h15
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c266
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.h21
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h82
-rw-r--r--drivers/gpu/drm/xe/xe_pcode.c380
-rw-r--r--drivers/gpu/drm/xe/xe_pcode.h45
-rw-r--r--drivers/gpu/drm/xe/xe_pcode_api.h94
-rw-r--r--drivers/gpu/drm/xe/xe_platform_types.h42
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c980
-rw-r--r--drivers/gpu/drm/xe/xe_pm.h57
-rw-r--r--drivers/gpu/drm/xe/xe_pmu.c600
-rw-r--r--drivers/gpu/drm/xe/xe_pmu.h18
-rw-r--r--drivers/gpu/drm/xe/xe_pmu_types.h39
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence.c184
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence.h61
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence_types.h34
-rw-r--r--drivers/gpu/drm/xe/xe_printk.h129
-rw-r--r--drivers/gpu/drm/xe/xe_psmi.c294
-rw-r--r--drivers/gpu/drm/xe/xe_psmi.h14
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c2565
-rw-r--r--drivers/gpu/drm/xe/xe_pt.h52
-rw-r--r--drivers/gpu/drm/xe/xe_pt_types.h124
-rw-r--r--drivers/gpu/drm/xe/xe_pt_walk.c161
-rw-r--r--drivers/gpu/drm/xe/xe_pt_walk.h152
-rw-r--r--drivers/gpu/drm/xe/xe_pxp.c949
-rw-r--r--drivers/gpu/drm/xe/xe_pxp.h35
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_debugfs.c129
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_debugfs.h13
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_submit.c602
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_submit.h22
-rw-r--r--drivers/gpu/drm/xe/xe_pxp_types.h135
-rw-r--r--drivers/gpu/drm/xe/xe_query.c812
-rw-r--r--drivers/gpu/drm/xe/xe_query.h14
-rw-r--r--drivers/gpu/drm/xe/xe_range_fence.c161
-rw-r--r--drivers/gpu/drm/xe/xe_range_fence.h75
-rw-r--r--drivers/gpu/drm/xe/xe_reg_sr.c216
-rw-r--r--drivers/gpu/drm/xe/xe_reg_sr.h28
-rw-r--r--drivers/gpu/drm/xe/xe_reg_sr_types.h31
-rw-r--r--drivers/gpu/drm/xe/xe_reg_whitelist.c221
-rw-r--r--drivers/gpu/drm/xe/xe_reg_whitelist.h23
-rw-r--r--drivers/gpu/drm/xe/xe_res_cursor.h356
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c525
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.h17
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops_types.h22
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.c387
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.h506
-rw-r--r--drivers/gpu/drm/xe/xe_rtp_helpers.h83
-rw-r--r--drivers/gpu/drm/xe/xe_rtp_types.h131
-rw-r--r--drivers/gpu/drm/xe/xe_sa.c151
-rw-r--r--drivers/gpu/drm/xe/xe_sa.h72
-rw-r--r--drivers/gpu/drm/xe/xe_sa_types.h19
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c359
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.h95
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job_types.h79
-rw-r--r--drivers/gpu/drm/xe/xe_shrinker.c306
-rw-r--r--drivers/gpu/drm/xe/xe_shrinker.h16
-rw-r--r--drivers/gpu/drm/xe/xe_sriov.c176
-rw-r--r--drivers/gpu/drm/xe/xe_sriov.h48
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet.c520
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet.h30
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet_types.h75
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.c283
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.h31
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_control.c279
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_control.h22
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c395
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h18
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_helpers.h73
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_migration.c365
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_migration.h30
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_migration_types.h37
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.c438
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.h45
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h36
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_service.c216
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_service.h23
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_service_types.h36
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c647
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_sysfs.h16
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_types.h70
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_printk.h46
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_types.h40
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.c211
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.h20
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.c480
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.h35
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs_types.h51
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_types.h47
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vfio.c80
-rw-r--r--drivers/gpu/drm/xe/xe_step.c259
-rw-r--r--drivers/gpu/drm/xe/xe_step.h23
-rw-r--r--drivers/gpu/drm/xe/xe_step_types.h76
-rw-r--r--drivers/gpu/drm/xe/xe_survivability_mode.c377
-rw-r--r--drivers/gpu/drm/xe/xe_survivability_mode.h18
-rw-r--r--drivers/gpu/drm/xe/xe_survivability_mode_types.h43
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c1537
-rw-r--r--drivers/gpu/drm/xe/xe_svm.h389
-rw-r--r--drivers/gpu/drm/xe/xe_sync.c406
-rw-r--r--drivers/gpu/drm/xe/xe_sync.h47
-rw-r--r--drivers/gpu/drm/xe/xe_sync_types.h31
-rw-r--r--drivers/gpu/drm/xe/xe_tile.c217
-rw-r--r--drivers/gpu/drm/xe/xe_tile.h26
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.c142
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.h16
-rw-r--r--drivers/gpu/drm/xe/xe_tile_printk.h127
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c253
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h15
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_printk.h33
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf.c350
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf.h23
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h23
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sysfs.c61
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sysfs.h19
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sysfs_types.h27
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval.c433
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval.h46
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.c285
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.h34
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_types.h130
-rw-r--r--drivers/gpu/drm/xe/xe_trace.c9
-rw-r--r--drivers/gpu/drm/xe/xe_trace.h474
-rw-r--r--drivers/gpu/drm/xe/xe_trace_bo.c9
-rw-r--r--drivers/gpu/drm/xe/xe_trace_bo.h263
-rw-r--r--drivers/gpu/drm/xe/xe_trace_guc.c9
-rw-r--r--drivers/gpu/drm/xe/xe_trace_guc.h159
-rw-r--r--drivers/gpu/drm/xe/xe_trace_lrc.c9
-rw-r--r--drivers/gpu/drm/xe/xe_trace_lrc.h52
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c351
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.h21
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_sys_mgr.c120
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_sys_mgr.h13
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr.c480
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr.h46
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h48
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.c247
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.h19
-rw-r--r--drivers/gpu/drm/xe/xe_uc.c316
-rw-r--r--drivers/gpu/drm/xe/xe_uc.h25
-rw-r--r--drivers/gpu/drm/xe/xe_uc_debugfs.c30
-rw-r--r--drivers/gpu/drm/xe/xe_uc_debugfs.h14
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.c956
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.h193
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw_abi.h405
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw_types.h155
-rw-r--r--drivers/gpu/drm/xe/xe_uc_types.h28
-rw-r--r--drivers/gpu/drm/xe/xe_userptr.c322
-rw-r--r--drivers/gpu/drm/xe/xe_userptr.h107
-rw-r--r--drivers/gpu/drm/xe/xe_validation.c278
-rw-r--r--drivers/gpu/drm/xe/xe_validation.h192
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c4410
-rw-r--r--drivers/gpu/drm/xe/xe_vm.h414
-rw-r--r--drivers/gpu/drm/xe/xe_vm_doc.h555
-rw-r--r--drivers/gpu/drm/xe/xe_vm_madvise.c431
-rw-r--r--drivers/gpu/drm/xe/xe_vm_madvise.h15
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h477
-rw-r--r--drivers/gpu/drm/xe/xe_vram.c462
-rw-r--r--drivers/gpu/drm/xe/xe_vram.h25
-rw-r--r--drivers/gpu/drm/xe/xe_vram_freq.c124
-rw-r--r--drivers/gpu/drm/xe/xe_vram_freq.h13
-rw-r--r--drivers/gpu/drm/xe/xe_vram_types.h85
-rw-r--r--drivers/gpu/drm/xe/xe_vsec.c225
-rw-r--r--drivers/gpu/drm/xe/xe_vsec.h15
-rw-r--r--drivers/gpu/drm/xe/xe_wa.c1154
-rw-r--r--drivers/gpu/drm/xe/xe_wa.h53
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules78
-rw-r--r--drivers/gpu/drm/xe/xe_wait_user_fence.c183
-rw-r--r--drivers/gpu/drm/xe/xe_wait_user_fence.h15
-rw-r--r--drivers/gpu/drm/xe/xe_wopcm.c273
-rw-r--r--drivers/gpu/drm/xe/xe_wopcm.h16
-rw-r--r--drivers/gpu/drm/xe/xe_wopcm_types.h26
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front.c14
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front_gem.c6
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front_kms.c4
-rw-r--r--drivers/gpu/drm/xlnx/Kconfig14
-rw-r--r--drivers/gpu/drm/xlnx/Makefile3
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_disp.c903
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_disp.h41
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_disp_regs.h15
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dp.c1394
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dp.h11
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dp_audio.c448
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dpsub.c290
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dpsub.h59
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_kms.c548
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_kms.h46
-rw-r--r--drivers/gpu/host1x/Kconfig2
-rw-r--r--drivers/gpu/host1x/bus.c72
-rw-r--r--drivers/gpu/host1x/bus.h2
-rw-r--r--drivers/gpu/host1x/cdma.c26
-rw-r--r--drivers/gpu/host1x/cdma.h2
-rw-r--r--drivers/gpu/host1x/channel.c26
-rw-r--r--drivers/gpu/host1x/channel.h4
-rw-r--r--drivers/gpu/host1x/context.c51
-rw-r--r--drivers/gpu/host1x/context_bus.c2
-rw-r--r--drivers/gpu/host1x/debug.c44
-rw-r--r--drivers/gpu/host1x/debug.h1
-rw-r--r--drivers/gpu/host1x/dev.c224
-rw-r--r--drivers/gpu/host1x/dev.h28
-rw-r--r--drivers/gpu/host1x/fence.c120
-rw-r--r--drivers/gpu/host1x/fence.h19
-rw-r--r--drivers/gpu/host1x/hw/cdma_hw.c12
-rw-r--r--drivers/gpu/host1x/hw/channel_hw.c166
-rw-r--r--drivers/gpu/host1x/hw/debug_hw.c15
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x06_uclass.h2
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x07_uclass.h2
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x08_uclass.h2
-rw-r--r--drivers/gpu/host1x/hw/intr_hw.c137
-rw-r--r--drivers/gpu/host1x/hw/syncpt_hw.c3
-rw-r--r--drivers/gpu/host1x/intr.c347
-rw-r--r--drivers/gpu/host1x/intr.h84
-rw-r--r--drivers/gpu/host1x/job.c12
-rw-r--r--drivers/gpu/host1x/mipi.c21
-rw-r--r--drivers/gpu/host1x/syncpt.c108
-rw-r--r--drivers/gpu/host1x/syncpt.h3
-rw-r--r--drivers/gpu/ipu-v3/Kconfig2
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c53
-rw-r--r--drivers/gpu/ipu-v3/ipu-cpmem.c23
-rw-r--r--drivers/gpu/ipu-v3/ipu-csi.c108
-rw-r--r--drivers/gpu/ipu-v3/ipu-ic.c73
-rw-r--r--drivers/gpu/ipu-v3/ipu-image-convert.c63
-rw-r--r--drivers/gpu/ipu-v3/ipu-pre.c138
-rw-r--r--drivers/gpu/ipu-v3/ipu-prg.c11
-rw-r--r--drivers/gpu/ipu-v3/ipu-prv.h5
-rw-r--r--drivers/gpu/ipu-v3/ipu-vdi.c11
-rw-r--r--drivers/gpu/nova-core/Kconfig16
-rw-r--r--drivers/gpu/nova-core/Makefile3
-rw-r--r--drivers/gpu/nova-core/bitfield.rs330
-rw-r--r--drivers/gpu/nova-core/dma.rs54
-rw-r--r--drivers/gpu/nova-core/driver.rs104
-rw-r--r--drivers/gpu/nova-core/falcon.rs664
-rw-r--r--drivers/gpu/nova-core/falcon/gsp.rs57
-rw-r--r--drivers/gpu/nova-core/falcon/hal.rs60
-rw-r--r--drivers/gpu/nova-core/falcon/hal/ga102.rs120
-rw-r--r--drivers/gpu/nova-core/falcon/sec2.rs25
-rw-r--r--drivers/gpu/nova-core/fb.rs217
-rw-r--r--drivers/gpu/nova-core/fb/hal.rs41
-rw-r--r--drivers/gpu/nova-core/fb/hal/ga100.rs63
-rw-r--r--drivers/gpu/nova-core/fb/hal/ga102.rs38
-rw-r--r--drivers/gpu/nova-core/fb/hal/tu102.rs59
-rw-r--r--drivers/gpu/nova-core/firmware.rs244
-rw-r--r--drivers/gpu/nova-core/firmware/booter.rs401
-rw-r--r--drivers/gpu/nova-core/firmware/fwsec.rs438
-rw-r--r--drivers/gpu/nova-core/firmware/gsp.rs258
-rw-r--r--drivers/gpu/nova-core/firmware/riscv.rs95
-rw-r--r--drivers/gpu/nova-core/gfw.rs71
-rw-r--r--drivers/gpu/nova-core/gpu.rs302
-rw-r--r--drivers/gpu/nova-core/gsp.rs161
-rw-r--r--drivers/gpu/nova-core/gsp/boot.rs252
-rw-r--r--drivers/gpu/nova-core/gsp/cmdq.rs679
-rw-r--r--drivers/gpu/nova-core/gsp/commands.rs227
-rw-r--r--drivers/gpu/nova-core/gsp/fw.rs928
-rw-r--r--drivers/gpu/nova-core/gsp/fw/commands.rs128
-rw-r--r--drivers/gpu/nova-core/gsp/fw/r570_144.rs31
-rw-r--r--drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs951
-rw-r--r--drivers/gpu/nova-core/gsp/sequencer.rs407
-rw-r--r--drivers/gpu/nova-core/nova_core.rs33
-rw-r--r--drivers/gpu/nova-core/num.rs217
-rw-r--r--drivers/gpu/nova-core/regs.rs411
-rw-r--r--drivers/gpu/nova-core/regs/macros.rs721
-rw-r--r--drivers/gpu/nova-core/sbuffer.rs227
-rw-r--r--drivers/gpu/nova-core/util.rs16
-rw-r--r--drivers/gpu/nova-core/vbios.rs1097
-rw-r--r--drivers/gpu/trace/Kconfig11
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c5
-rw-r--r--drivers/greybus/Kconfig13
-rw-r--r--drivers/greybus/Makefile2
-rw-r--r--drivers/greybus/bundle.c2
-rw-r--r--drivers/greybus/connection.c4
-rw-r--r--drivers/greybus/control.c2
-rw-r--r--drivers/greybus/core.c51
-rw-r--r--drivers/greybus/es2.c11
-rw-r--r--drivers/greybus/gb-beagleplay.c1155
-rw-r--r--drivers/greybus/hd.c18
-rw-r--r--drivers/greybus/interface.c14
-rw-r--r--drivers/greybus/module.c2
-rw-r--r--drivers/greybus/operation.c7
-rw-r--r--drivers/greybus/svc.c10
-rw-r--r--drivers/hid/.kunitconfig1
-rw-r--r--drivers/hid/Kconfig316
-rw-r--r--drivers/hid/Makefile25
-rw-r--r--drivers/hid/amd-sfh-hid/Kconfig2
-rw-r--r--drivers/hid/amd-sfh-hid/Makefile2
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_client.c114
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_common.h27
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.c6
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.h5
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_pcie.c192
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_pcie.h8
-rw-r--r--drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c11
-rw-r--r--drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h3
-rw-r--r--drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h21
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c63
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c160
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.h3
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c117
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h49
-rw-r--r--drivers/hid/bpf/Kconfig16
-rw-r--r--drivers/hid/bpf/Makefile11
-rw-r--r--drivers/hid/bpf/hid_bpf_dispatch.c681
-rw-r--r--drivers/hid/bpf/hid_bpf_dispatch.h22
-rw-r--r--drivers/hid/bpf/hid_bpf_struct_ops.c326
-rw-r--r--drivers/hid/bpf/progs/FR-TEC__Raptor-Mach-2.bpf.c190
-rw-r--r--drivers/hid/bpf/progs/HP__Elite-Presenter.bpf.c62
-rw-r--r--drivers/hid/bpf/progs/Huion__Dial-2.bpf.c636
-rw-r--r--drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c563
-rw-r--r--drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c579
-rw-r--r--drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c366
-rw-r--r--drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c1395
-rw-r--r--drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c724
-rw-r--r--drivers/hid/bpf/progs/Huion__KeydialK20.bpf.c531
-rw-r--r--drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c63
-rw-r--r--drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c86
-rw-r--r--drivers/hid/bpf/progs/Makefile91
-rw-r--r--drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c136
-rw-r--r--drivers/hid/bpf/progs/Mistel__MD770.bpf.c154
-rw-r--r--drivers/hid/bpf/progs/README102
-rw-r--r--drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c148
-rw-r--r--drivers/hid/bpf/progs/TUXEDO__Sirius-16-Gen1-and-Gen2.bpf.c47
-rw-r--r--drivers/hid/bpf/progs/Thrustmaster__TCA-Yoke-Boeing.bpf.c144
-rw-r--r--drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c321
-rw-r--r--drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c177
-rw-r--r--drivers/hid/bpf/progs/XPPen__ACK05.bpf.c331
-rw-r--r--drivers/hid/bpf/progs/XPPen__Artist24.bpf.c231
-rw-r--r--drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c324
-rw-r--r--drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c305
-rw-r--r--drivers/hid/bpf/progs/XPPen__Deco02.bpf.c359
-rw-r--r--drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c231
-rw-r--r--drivers/hid/bpf/progs/hid_bpf.h21
-rw-r--r--drivers/hid/bpf/progs/hid_bpf_async.h219
-rw-r--r--drivers/hid/bpf/progs/hid_bpf_helpers.h188
-rw-r--r--drivers/hid/bpf/progs/hid_report_helpers.h2982
-rw-r--r--drivers/hid/hid-a4tech.c1
-rw-r--r--drivers/hid/hid-alps.c8
-rw-r--r--drivers/hid/hid-apple.c504
-rw-r--r--drivers/hid/hid-appleir.c6
-rw-r--r--drivers/hid/hid-appletb-bl.c204
-rw-r--r--drivers/hid/hid-appletb-kbd.c519
-rw-r--r--drivers/hid/hid-asus.c376
-rw-r--r--drivers/hid/hid-aureal.c3
-rw-r--r--drivers/hid/hid-belkin.c1
-rw-r--r--drivers/hid/hid-betopff.c18
-rw-r--r--drivers/hid/hid-bigbenff.c85
-rw-r--r--drivers/hid/hid-cherry.c3
-rw-r--r--drivers/hid/hid-chicony.c5
-rw-r--r--drivers/hid/hid-cmedia.c6
-rw-r--r--drivers/hid/hid-core.c666
-rw-r--r--drivers/hid/hid-corsair-void.c832
-rw-r--r--drivers/hid/hid-corsair.c8
-rw-r--r--drivers/hid/hid-cougar.c6
-rw-r--r--drivers/hid/hid-cp2112.c287
-rw-r--r--drivers/hid/hid-cypress.c3
-rw-r--r--drivers/hid/hid-debug.c3460
-rw-r--r--drivers/hid/hid-dr.c9
-rw-r--r--drivers/hid/hid-elan.c6
-rw-r--r--drivers/hid/hid-elecom.c33
-rw-r--r--drivers/hid/hid-elo.c1
-rw-r--r--drivers/hid/hid-emsff.c1
-rw-r--r--drivers/hid/hid-evision.c75
-rw-r--r--drivers/hid/hid-ezkey.c1
-rw-r--r--drivers/hid/hid-ft260.c325
-rw-r--r--drivers/hid/hid-gaff.c1
-rw-r--r--drivers/hid/hid-gembird.c2
-rw-r--r--drivers/hid/hid-generic.c14
-rw-r--r--drivers/hid/hid-glorious.c18
-rw-r--r--drivers/hid/hid-goodix-spi.c818
-rw-r--r--drivers/hid/hid-google-hammer.c42
-rw-r--r--drivers/hid/hid-google-stadiaff.c159
-rw-r--r--drivers/hid/hid-gyration.c1
-rw-r--r--drivers/hid/hid-haptic.c580
-rw-r--r--drivers/hid/hid-haptic.h127
-rw-r--r--drivers/hid/hid-holtek-kbd.c11
-rw-r--r--drivers/hid/hid-holtek-mouse.c5
-rw-r--r--drivers/hid/hid-hyperv.c105
-rw-r--r--drivers/hid/hid-ids.h205
-rw-r--r--drivers/hid/hid-input-test.c80
-rw-r--r--drivers/hid/hid-input.c198
-rw-r--r--drivers/hid/hid-ite.c8
-rw-r--r--drivers/hid/hid-kensington.c3
-rw-r--r--drivers/hid/hid-keytouch.c9
-rw-r--r--drivers/hid/hid-kye.c948
-rw-r--r--drivers/hid/hid-kysona.c290
-rw-r--r--drivers/hid/hid-lcpower.c1
-rw-r--r--drivers/hid/hid-lenovo.c300
-rw-r--r--drivers/hid/hid-letsketch.c8
-rw-r--r--drivers/hid/hid-lg-g15.c609
-rw-r--r--drivers/hid/hid-lg.c31
-rw-r--r--drivers/hid/hid-lg3ff.c4
-rw-r--r--drivers/hid/hid-lg4ff.c21
-rw-r--r--drivers/hid/hid-logitech-dj.c204
-rw-r--r--drivers/hid/hid-logitech-hidpp.c801
-rw-r--r--drivers/hid/hid-macally.c4
-rw-r--r--drivers/hid/hid-magicmouse.c186
-rw-r--r--drivers/hid/hid-maltron.c9
-rw-r--r--drivers/hid/hid-mcp2200.c397
-rw-r--r--drivers/hid/hid-mcp2221.c530
-rw-r--r--drivers/hid/hid-megaworld.c1
-rw-r--r--drivers/hid/hid-mf.c1
-rw-r--r--drivers/hid/hid-microsoft.c14
-rw-r--r--drivers/hid/hid-monterey.c3
-rw-r--r--drivers/hid/hid-multitouch.c365
-rw-r--r--drivers/hid/hid-nintendo.c1374
-rw-r--r--drivers/hid/hid-nti.c2
-rw-r--r--drivers/hid/hid-ntrig.c11
-rw-r--r--drivers/hid/hid-nvidia-shield.c1134
-rw-r--r--drivers/hid/hid-ortek.c3
-rw-r--r--drivers/hid/hid-petalynx.c3
-rw-r--r--drivers/hid/hid-picolcd_backlight.c12
-rw-r--r--drivers/hid/hid-picolcd_core.c20
-rw-r--r--drivers/hid/hid-picolcd_fb.c99
-rw-r--r--drivers/hid/hid-picolcd_lcd.c8
-rw-r--r--drivers/hid/hid-pl.c1
-rw-r--r--drivers/hid/hid-plantronics.c112
-rw-r--r--drivers/hid/hid-playstation.c1950
-rw-r--r--drivers/hid/hid-primax.c1
-rw-r--r--drivers/hid/hid-prodikeys.c130
-rw-r--r--drivers/hid/hid-pxrc.c112
-rw-r--r--drivers/hid/hid-quirks.c94
-rw-r--r--drivers/hid/hid-razer.c1
-rw-r--r--drivers/hid/hid-redragon.c3
-rw-r--r--drivers/hid/hid-retrode.c1
-rw-r--r--drivers/hid/hid-rmi.c18
-rw-r--r--drivers/hid/hid-roccat-arvo.c38
-rw-r--r--drivers/hid/hid-roccat-common.h14
-rw-r--r--drivers/hid/hid-roccat-isku.c35
-rw-r--r--drivers/hid/hid-roccat-kone.c52
-rw-r--r--drivers/hid/hid-roccat-koneplus.c58
-rw-r--r--drivers/hid/hid-roccat-konepure.c24
-rw-r--r--drivers/hid/hid-roccat-kovaplus.c58
-rw-r--r--drivers/hid/hid-roccat-lua.c6
-rw-r--r--drivers/hid/hid-roccat-pyra.c64
-rw-r--r--drivers/hid/hid-roccat-ryos.c22
-rw-r--r--drivers/hid/hid-roccat-savu.c22
-rw-r--r--drivers/hid/hid-roccat.c6
-rw-r--r--drivers/hid/hid-saitek.c5
-rw-r--r--drivers/hid/hid-samsung.c440
-rw-r--r--drivers/hid/hid-semitek.c5
-rw-r--r--drivers/hid/hid-sensor-custom.c263
-rw-r--r--drivers/hid/hid-sensor-hub.c31
-rw-r--r--drivers/hid/hid-sigmamicro.c4
-rw-r--r--drivers/hid/hid-sjoy.c1
-rw-r--r--drivers/hid/hid-sony.c1058
-rw-r--r--drivers/hid/hid-speedlink.c1
-rw-r--r--drivers/hid/hid-steam.c1041
-rw-r--r--drivers/hid/hid-steelseries.c494
-rw-r--r--drivers/hid/hid-sunplus.c3
-rw-r--r--drivers/hid/hid-thrustmaster.c9
-rw-r--r--drivers/hid/hid-tivo.c1
-rw-r--r--drivers/hid/hid-tmff.c1
-rw-r--r--drivers/hid/hid-topre.c58
-rw-r--r--drivers/hid/hid-topseed.c1
-rw-r--r--drivers/hid/hid-twinhan.c1
-rw-r--r--drivers/hid/hid-u2fzero.c1
-rw-r--r--drivers/hid/hid-uclogic-core-test.c112
-rw-r--r--drivers/hid/hid-uclogic-core.c182
-rw-r--r--drivers/hid/hid-uclogic-params-test.c222
-rw-r--r--drivers/hid/hid-uclogic-params.c603
-rw-r--r--drivers/hid/hid-uclogic-params.h68
-rw-r--r--drivers/hid/hid-uclogic-rdesc-test.c31
-rw-r--r--drivers/hid/hid-uclogic-rdesc.c320
-rw-r--r--drivers/hid/hid-uclogic-rdesc.h52
-rw-r--r--drivers/hid/hid-universal-pidff.c204
-rw-r--r--drivers/hid/hid-viewsonic.c9
-rw-r--r--drivers/hid/hid-vivaldi-common.c30
-rw-r--r--drivers/hid/hid-vivaldi-common.h4
-rw-r--r--drivers/hid/hid-vivaldi.c4
-rw-r--r--drivers/hid/hid-vrc2.c91
-rw-r--r--drivers/hid/hid-waltop.c31
-rw-r--r--drivers/hid/hid-wiimote-core.c13
-rw-r--r--drivers/hid/hid-wiimote-debug.c10
-rw-r--r--drivers/hid/hid-wiimote-modules.c225
-rw-r--r--drivers/hid/hid-wiimote.h1
-rw-r--r--drivers/hid/hid-winwing.c268
-rw-r--r--drivers/hid/hid-xiaomi.c8
-rw-r--r--drivers/hid/hid-xinmo.c1
-rw-r--r--drivers/hid/hid-zpff.c1
-rw-r--r--drivers/hid/hid-zydacron.c3
-rw-r--r--drivers/hid/hidraw.c291
-rw-r--r--drivers/hid/i2c-hid/Kconfig39
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-acpi.c46
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-core.c673
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c42
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of-elan.c131
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of-goodix.c111
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of.c48
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.h7
-rw-r--r--drivers/hid/intel-ish-hid/Kconfig1
-rw-r--r--drivers/hid/intel-ish-hid/Makefile3
-rw-r--r--drivers/hid/intel-ish-hid/ipc/hw-ish.h46
-rw-r--r--drivers/hid/intel-ish-hid/ipc/ipc.c163
-rw-r--r--drivers/hid/intel-ish-hid/ipc/pci-ish.c227
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-fw-loader.c64
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid-client.c104
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid.c6
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid.h11
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/bus.c35
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/bus.h1
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client-buffers.c21
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client.c219
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client.h3
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/dma-if.c10
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/hbm.c23
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/init.c38
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h51
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/loader.c432
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/loader.h265
-rw-r--r--drivers/hid/intel-thc-hid/Kconfig42
-rw-r--r--drivers/hid/intel-thc-hid/Makefile23
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c1044
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h227
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c165
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h14
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c248
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h20
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c1006
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h176
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c164
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h14
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c413
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h25
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c1719
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h133
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c1009
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h154
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h886
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c94
-rw-r--r--drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h26
-rw-r--r--drivers/hid/surface-hid/Kconfig2
-rw-r--r--drivers/hid/surface-hid/surface_hid.c10
-rw-r--r--drivers/hid/surface-hid/surface_hid_core.c4
-rw-r--r--drivers/hid/surface-hid/surface_kbd.c13
-rw-r--r--drivers/hid/uhid.c20
-rw-r--r--drivers/hid/usbhid/Kconfig3
-rw-r--r--drivers/hid/usbhid/hid-core.c60
-rw-r--r--drivers/hid/usbhid/hid-pidff.c1059
-rw-r--r--drivers/hid/usbhid/hid-pidff.h32
-rw-r--r--drivers/hid/usbhid/hiddev.c2
-rw-r--r--drivers/hid/usbhid/usbkbd.c4
-rw-r--r--drivers/hid/usbhid/usbmouse.c2
-rw-r--r--drivers/hid/wacom.h17
-rw-r--r--drivers/hid/wacom_sys.c252
-rw-r--r--drivers/hid/wacom_wac.c357
-rw-r--r--drivers/hid/wacom_wac.h27
-rw-r--r--drivers/hsi/clients/cmt_speech.c4
-rw-r--r--drivers/hsi/clients/nokia-modem.c4
-rw-r--r--drivers/hsi/clients/ssi_protocol.c39
-rw-r--r--drivers/hsi/controllers/omap_ssi_core.c40
-rw-r--r--drivers/hsi/controllers/omap_ssi_port.c39
-rw-r--r--drivers/hsi/hsi_core.c8
-rw-r--r--drivers/hte/Kconfig5
-rw-r--r--drivers/hte/hte-tegra194-test.c23
-rw-r--r--drivers/hte/hte-tegra194.c210
-rw-r--r--drivers/hte/hte.c21
-rw-r--r--drivers/hv/Kconfig91
-rw-r--r--drivers/hv/Makefile15
-rw-r--r--drivers/hv/channel.c330
-rw-r--r--drivers/hv/channel_mgmt.c126
-rw-r--r--drivers/hv/connection.c202
-rw-r--r--drivers/hv/hv.c578
-rw-r--r--drivers/hv/hv_balloon.c364
-rw-r--r--drivers/hv/hv_common.c629
-rw-r--r--drivers/hv/hv_fcopy.c427
-rw-r--r--drivers/hv/hv_kvp.c12
-rw-r--r--drivers/hv/hv_proc.c196
-rw-r--r--drivers/hv/hv_snapshot.c11
-rw-r--r--drivers/hv/hv_util.c66
-rw-r--r--drivers/hv/hv_utils_transport.c10
-rw-r--r--drivers/hv/hyperv_vmbus.h122
-rw-r--r--drivers/hv/mshv.h28
-rw-r--r--drivers/hv/mshv_common.c239
-rw-r--r--drivers/hv/mshv_eventfd.c839
-rw-r--r--drivers/hv/mshv_eventfd.h71
-rw-r--r--drivers/hv/mshv_irq.c128
-rw-r--r--drivers/hv/mshv_portid_table.c83
-rw-r--r--drivers/hv/mshv_regions.c555
-rw-r--r--drivers/hv/mshv_root.h336
-rw-r--r--drivers/hv/mshv_root_hv_call.c1024
-rw-r--r--drivers/hv/mshv_root_main.c2342
-rw-r--r--drivers/hv/mshv_synic.c665
-rw-r--r--drivers/hv/mshv_vtl.h25
-rw-r--r--drivers/hv/mshv_vtl_main.c1392
-rw-r--r--drivers/hv/ring_buffer.c79
-rw-r--r--drivers/hv/vmbus_drv.c915
-rw-r--r--drivers/hwmon/Kconfig598
-rw-r--r--drivers/hwmon/Makefile46
-rw-r--r--drivers/hwmon/abituguru.c13
-rw-r--r--drivers/hwmon/abituguru3.c14
-rw-r--r--drivers/hwmon/acpi_power_meter.c887
-rw-r--r--drivers/hwmon/ad7314.c10
-rw-r--r--drivers/hwmon/ad7414.c4
-rw-r--r--drivers/hwmon/ad7418.c11
-rw-r--r--drivers/hwmon/adc128d818.c69
-rw-r--r--drivers/hwmon/adm1021.c505
-rw-r--r--drivers/hwmon/adm1025.c4
-rw-r--r--drivers/hwmon/adm1026.c22
-rw-r--r--drivers/hwmon/adm1029.c9
-rw-r--r--drivers/hwmon/adm1031.c8
-rw-r--r--drivers/hwmon/adm1177.c34
-rw-r--r--drivers/hwmon/adm9240.c11
-rw-r--r--drivers/hwmon/ads7828.c12
-rw-r--r--drivers/hwmon/adt7310.c6
-rw-r--r--drivers/hwmon/adt7410.c23
-rw-r--r--drivers/hwmon/adt7411.c67
-rw-r--r--drivers/hwmon/adt7462.c6
-rw-r--r--drivers/hwmon/adt7470.c31
-rw-r--r--drivers/hwmon/adt7475.c280
-rw-r--r--drivers/hwmon/adt7x10.c54
-rw-r--r--drivers/hwmon/adt7x10.h5
-rw-r--r--drivers/hwmon/aht10.c192
-rw-r--r--drivers/hwmon/amc6821.c1508
-rw-r--r--drivers/hwmon/aquacomputer_d5next.c1349
-rw-r--r--drivers/hwmon/as370-hwmon.c5
-rw-r--r--drivers/hwmon/asb100.c12
-rw-r--r--drivers/hwmon/asc7621.c8
-rw-r--r--drivers/hwmon/aspeed-g6-pwm-tach.c551
-rw-r--r--drivers/hwmon/aspeed-pwm-tacho.c44
-rw-r--r--drivers/hwmon/asus-ec-sensors.c518
-rw-r--r--drivers/hwmon/asus_atk0110.c23
-rw-r--r--drivers/hwmon/asus_rog_ryujin.c579
-rw-r--r--drivers/hwmon/asus_wmi_ec_sensors.c622
-rw-r--r--drivers/hwmon/asus_wmi_sensors.c2
-rw-r--r--drivers/hwmon/atxp1.c5
-rw-r--r--drivers/hwmon/axi-fan-control.c109
-rw-r--r--drivers/hwmon/bt1-pvt.c13
-rw-r--r--drivers/hwmon/cgbc-hwmon.c307
-rw-r--r--drivers/hwmon/chipcap2.c777
-rw-r--r--drivers/hwmon/coretemp.c621
-rw-r--r--drivers/hwmon/corsair-cpro.c151
-rw-r--r--drivers/hwmon/corsair-psu.c152
-rw-r--r--drivers/hwmon/cros_ec_hwmon.c597
-rw-r--r--drivers/hwmon/da9052-hwmon.c42
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c745
-rw-r--r--drivers/hwmon/dme1737.c16
-rw-r--r--drivers/hwmon/drivetemp.c26
-rw-r--r--drivers/hwmon/ds1621.c9
-rw-r--r--drivers/hwmon/ds620.c5
-rw-r--r--drivers/hwmon/emc1403.c838
-rw-r--r--drivers/hwmon/emc2103.c10
-rw-r--r--drivers/hwmon/emc2305.c738
-rw-r--r--drivers/hwmon/emc6w201.c6
-rw-r--r--drivers/hwmon/f71805f.c4
-rw-r--r--drivers/hwmon/f71882fg.c2455
-rw-r--r--drivers/hwmon/f75375s.c51
-rw-r--r--drivers/hwmon/fam15h_power.c15
-rw-r--r--drivers/hwmon/fschmd.c17
-rw-r--r--drivers/hwmon/ftsteutates.c613
-rw-r--r--drivers/hwmon/g760a.c4
-rw-r--r--drivers/hwmon/g762.c57
-rw-r--r--drivers/hwmon/gigabyte_waterforce.c430
-rw-r--r--drivers/hwmon/gl518sm.c6
-rw-r--r--drivers/hwmon/gl520sm.c6
-rw-r--r--drivers/hwmon/gpd-fan.c683
-rw-r--r--drivers/hwmon/gpio-fan.c129
-rw-r--r--drivers/hwmon/gsc-hwmon.c33
-rw-r--r--drivers/hwmon/gxp-fan-ctrl.c253
-rw-r--r--drivers/hwmon/hih6130.c8
-rw-r--r--drivers/hwmon/hp-wmi-sensors.c2077
-rw-r--r--drivers/hwmon/hs3001.c234
-rw-r--r--drivers/hwmon/htu31.c350
-rw-r--r--drivers/hwmon/hwmon-vid.c4
-rw-r--r--drivers/hwmon/hwmon.c298
-rw-r--r--drivers/hwmon/i5500_temp.c15
-rw-r--r--drivers/hwmon/i5k_amb.c18
-rw-r--r--drivers/hwmon/ibmaem.c27
-rw-r--r--drivers/hwmon/ibmpex.c17
-rw-r--r--drivers/hwmon/ibmpowernv.c6
-rw-r--r--drivers/hwmon/iio_hwmon.c72
-rw-r--r--drivers/hwmon/ina209.c9
-rw-r--r--drivers/hwmon/ina238.c644
-rw-r--r--drivers/hwmon/ina2xx.c1001
-rw-r--r--drivers/hwmon/ina3221.c83
-rw-r--r--drivers/hwmon/intel-m10-bmc-hwmon.c245
-rw-r--r--drivers/hwmon/isl28022.c494
-rw-r--r--drivers/hwmon/it87.c774
-rw-r--r--drivers/hwmon/jc42.c346
-rw-r--r--drivers/hwmon/k10temp.c152
-rw-r--r--drivers/hwmon/k8temp.c2
-rw-r--r--drivers/hwmon/kbatt.c147
-rw-r--r--drivers/hwmon/kfan.c246
-rw-r--r--drivers/hwmon/lan966x-hwmon.c26
-rw-r--r--drivers/hwmon/lenovo-ec-sensors.c628
-rw-r--r--drivers/hwmon/lineage-pem.c4
-rw-r--r--drivers/hwmon/lm63.c15
-rw-r--r--drivers/hwmon/lm70.c10
-rw-r--r--drivers/hwmon/lm73.c12
-rw-r--r--drivers/hwmon/lm75.c419
-rw-r--r--drivers/hwmon/lm77.c6
-rw-r--r--drivers/hwmon/lm78.c13
-rw-r--r--drivers/hwmon/lm80.c4
-rw-r--r--drivers/hwmon/lm83.c24
-rw-r--r--drivers/hwmon/lm85.c13
-rw-r--r--drivers/hwmon/lm87.c24
-rw-r--r--drivers/hwmon/lm90.c212
-rw-r--r--drivers/hwmon/lm92.c458
-rw-r--r--drivers/hwmon/lm93.c8
-rw-r--r--drivers/hwmon/lm95234.c813
-rw-r--r--drivers/hwmon/lm95241.c26
-rw-r--r--drivers/hwmon/lm95245.c138
-rw-r--r--drivers/hwmon/lochnagar-hwmon.c22
-rw-r--r--drivers/hwmon/ltc2945.c136
-rw-r--r--drivers/hwmon/ltc2947-core.c140
-rw-r--r--drivers/hwmon/ltc2947-i2c.c6
-rw-r--r--drivers/hwmon/ltc2947-spi.c2
-rw-r--r--drivers/hwmon/ltc2990.c4
-rw-r--r--drivers/hwmon/ltc2991.c430
-rw-r--r--drivers/hwmon/ltc2992.c56
-rw-r--r--drivers/hwmon/ltc4151.c4
-rw-r--r--drivers/hwmon/ltc4215.c4
-rw-r--r--drivers/hwmon/ltc4222.c4
-rw-r--r--drivers/hwmon/ltc4245.c16
-rw-r--r--drivers/hwmon/ltc4260.c4
-rw-r--r--drivers/hwmon/ltc4261.c4
-rw-r--r--drivers/hwmon/ltc4282.c1706
-rw-r--r--drivers/hwmon/ltq-cputemp.c5
-rw-r--r--drivers/hwmon/macsmc-hwmon.c851
-rw-r--r--drivers/hwmon/max1111.c1
-rw-r--r--drivers/hwmon/max127.c31
-rw-r--r--drivers/hwmon/max16065.c36
-rw-r--r--drivers/hwmon/max1619.c505
-rw-r--r--drivers/hwmon/max1668.c489
-rw-r--r--drivers/hwmon/max197.c4
-rw-r--r--drivers/hwmon/max31722.c8
-rw-r--r--drivers/hwmon/max31730.c17
-rw-r--r--drivers/hwmon/max31760.c595
-rw-r--r--drivers/hwmon/max31790.c101
-rw-r--r--drivers/hwmon/max31827.c645
-rw-r--r--drivers/hwmon/max6620.c49
-rw-r--r--drivers/hwmon/max6621.c9
-rw-r--r--drivers/hwmon/max6639.c819
-rw-r--r--drivers/hwmon/max6642.c314
-rw-r--r--drivers/hwmon/max6650.c12
-rw-r--r--drivers/hwmon/max6697.c846
-rw-r--r--drivers/hwmon/max77705-hwmon.c221
-rw-r--r--drivers/hwmon/mc13783-adc.c4
-rw-r--r--drivers/hwmon/mc33xs2410_hwmon.c178
-rw-r--r--drivers/hwmon/mc34vr500.c263
-rw-r--r--drivers/hwmon/mcp3021.c11
-rw-r--r--drivers/hwmon/mlxreg-fan.c67
-rw-r--r--drivers/hwmon/mr75203.c406
-rw-r--r--drivers/hwmon/nct6683.c24
-rw-r--r--drivers/hwmon/nct6694-hwmon.c949
-rw-r--r--drivers/hwmon/nct6775-core.c496
-rw-r--r--drivers/hwmon/nct6775-i2c.c20
-rw-r--r--drivers/hwmon/nct6775-platform.c518
-rw-r--r--drivers/hwmon/nct6775.h33
-rw-r--r--drivers/hwmon/nct7363.c445
-rw-r--r--drivers/hwmon/nct7802.c79
-rw-r--r--drivers/hwmon/nct7904.c71
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c64
-rw-r--r--drivers/hwmon/nsa320-hwmon.c4
-rw-r--r--drivers/hwmon/ntc_thermistor.c127
-rw-r--r--drivers/hwmon/nzxt-kraken2.c17
-rw-r--r--drivers/hwmon/nzxt-kraken3.c1028
-rw-r--r--drivers/hwmon/nzxt-smart2.c14
-rw-r--r--drivers/hwmon/occ/Kconfig2
-rw-r--r--drivers/hwmon/occ/common.c253
-rw-r--r--drivers/hwmon/occ/p8_i2c.c9
-rw-r--r--drivers/hwmon/occ/p9_sbe.c36
-rw-r--r--drivers/hwmon/pc87360.c1469
-rw-r--r--drivers/hwmon/pc87427.c4
-rw-r--r--drivers/hwmon/pcf8591.c8
-rw-r--r--drivers/hwmon/peci/common.h3
-rw-r--r--drivers/hwmon/peci/cputemp.c128
-rw-r--r--drivers/hwmon/peci/dimmtemp.c124
-rw-r--r--drivers/hwmon/pmbus/Kconfig274
-rw-r--r--drivers/hwmon/pmbus/Makefile23
-rw-r--r--drivers/hwmon/pmbus/acbel-fsg032.c123
-rw-r--r--drivers/hwmon/pmbus/adm1266.c9
-rw-r--r--drivers/hwmon/pmbus/adm1275.c138
-rw-r--r--drivers/hwmon/pmbus/adp1050.c122
-rw-r--r--drivers/hwmon/pmbus/bel-pfe.c20
-rw-r--r--drivers/hwmon/pmbus/bpa-rs600.c4
-rw-r--r--drivers/hwmon/pmbus/crps.c74
-rw-r--r--drivers/hwmon/pmbus/delta-ahe50dc-fan.c4
-rw-r--r--drivers/hwmon/pmbus/dps920ab.c13
-rw-r--r--drivers/hwmon/pmbus/fsp-3y.c5
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c280
-rw-r--r--drivers/hwmon/pmbus/ina233.c191
-rw-r--r--drivers/hwmon/pmbus/inspur-ipsps.c6
-rw-r--r--drivers/hwmon/pmbus/ir35221.c6
-rw-r--r--drivers/hwmon/pmbus/ir36021.c7
-rw-r--r--drivers/hwmon/pmbus/ir38064.c18
-rw-r--r--drivers/hwmon/pmbus/irps5401.c6
-rw-r--r--drivers/hwmon/pmbus/isl68137.c236
-rw-r--r--drivers/hwmon/pmbus/lm25066.c18
-rw-r--r--drivers/hwmon/pmbus/lt3074.c122
-rw-r--r--drivers/hwmon/pmbus/lt7182s.c6
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c118
-rw-r--r--drivers/hwmon/pmbus/ltc3815.c6
-rw-r--r--drivers/hwmon/pmbus/ltc4286.c175
-rw-r--r--drivers/hwmon/pmbus/max15301.c101
-rw-r--r--drivers/hwmon/pmbus/max16064.c6
-rw-r--r--drivers/hwmon/pmbus/max16601.c20
-rw-r--r--drivers/hwmon/pmbus/max17616.c73
-rw-r--r--drivers/hwmon/pmbus/max20730.c72
-rw-r--r--drivers/hwmon/pmbus/max20751.c6
-rw-r--r--drivers/hwmon/pmbus/max31785.c198
-rw-r--r--drivers/hwmon/pmbus/max34440.c161
-rw-r--r--drivers/hwmon/pmbus/max8688.c6
-rw-r--r--drivers/hwmon/pmbus/mp2856.c466
-rw-r--r--drivers/hwmon/pmbus/mp2869.c659
-rw-r--r--drivers/hwmon/pmbus/mp2888.c19
-rw-r--r--drivers/hwmon/pmbus/mp2891.c600
-rw-r--r--drivers/hwmon/pmbus/mp2925.c316
-rw-r--r--drivers/hwmon/pmbus/mp29502.c670
-rw-r--r--drivers/hwmon/pmbus/mp2975.c511
-rw-r--r--drivers/hwmon/pmbus/mp2993.c261
-rw-r--r--drivers/hwmon/pmbus/mp5023.c6
-rw-r--r--drivers/hwmon/pmbus/mp5920.c90
-rw-r--r--drivers/hwmon/pmbus/mp5990.c230
-rw-r--r--drivers/hwmon/pmbus/mp9941.c319
-rw-r--r--drivers/hwmon/pmbus/mp9945.c243
-rw-r--r--drivers/hwmon/pmbus/mpq7932.c167
-rw-r--r--drivers/hwmon/pmbus/mpq8785.c165
-rw-r--r--drivers/hwmon/pmbus/pim4328.c4
-rw-r--r--drivers/hwmon/pmbus/pli1209bc.c8
-rw-r--r--drivers/hwmon/pmbus/pm6764tr.c6
-rw-r--r--drivers/hwmon/pmbus/pmbus.c6
-rw-r--r--drivers/hwmon/pmbus/pmbus.h67
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c1020
-rw-r--r--drivers/hwmon/pmbus/pxe1610.c10
-rw-r--r--drivers/hwmon/pmbus/q54sj108a2.c7
-rw-r--r--drivers/hwmon/pmbus/stpddc60.c8
-rw-r--r--drivers/hwmon/pmbus/tda38640.c224
-rw-r--r--drivers/hwmon/pmbus/tps25990.c436
-rw-r--r--drivers/hwmon/pmbus/tps40422.c6
-rw-r--r--drivers/hwmon/pmbus/tps53679.c45
-rw-r--r--drivers/hwmon/pmbus/tps546d24.c71
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c47
-rw-r--r--drivers/hwmon/pmbus/ucd9200.c8
-rw-r--r--drivers/hwmon/pmbus/xdp710.c131
-rw-r--r--drivers/hwmon/pmbus/xdpe12284.c10
-rw-r--r--drivers/hwmon/pmbus/xdpe152c4.c8
-rw-r--r--drivers/hwmon/pmbus/zl6100.c70
-rw-r--r--drivers/hwmon/powerz.c269
-rw-r--r--drivers/hwmon/powr1220.c28
-rw-r--r--drivers/hwmon/pt5161l.c639
-rw-r--r--drivers/hwmon/pwm-fan.c442
-rw-r--r--drivers/hwmon/qnap-mcu-hwmon.c363
-rw-r--r--drivers/hwmon/raspberrypi-hwmon.c32
-rw-r--r--drivers/hwmon/s3c-hwmon.c379
-rw-r--r--drivers/hwmon/sa67mcu-hwmon.c161
-rw-r--r--drivers/hwmon/sbrmi.c359
-rw-r--r--drivers/hwmon/sbtsi_temp.c72
-rw-r--r--drivers/hwmon/sch5627.c269
-rw-r--r--drivers/hwmon/sch5636.c7
-rw-r--r--drivers/hwmon/sch56xx-common.c175
-rw-r--r--drivers/hwmon/sch56xx-common.h7
-rw-r--r--drivers/hwmon/scmi-hwmon.c125
-rw-r--r--drivers/hwmon/scpi-hwmon.c16
-rw-r--r--drivers/hwmon/sfctemp.c311
-rw-r--r--drivers/hwmon/sg2042-mcu.c360
-rw-r--r--drivers/hwmon/sht15.c12
-rw-r--r--drivers/hwmon/sht21.c28
-rw-r--r--drivers/hwmon/sht3x.c514
-rw-r--r--drivers/hwmon/sht4x.c210
-rw-r--r--drivers/hwmon/shtc1.c8
-rw-r--r--drivers/hwmon/sis5595.c234
-rw-r--r--drivers/hwmon/sl28cpld-hwmon.c11
-rw-r--r--drivers/hwmon/smm665.c707
-rw-r--r--drivers/hwmon/smpro-hwmon.c466
-rw-r--r--drivers/hwmon/smsc47m1.c65
-rw-r--r--drivers/hwmon/smsc47m192.c6
-rw-r--r--drivers/hwmon/sparx5-temp.c21
-rw-r--r--drivers/hwmon/spd5118.c778
-rw-r--r--drivers/hwmon/stts751.c9
-rw-r--r--drivers/hwmon/surface_fan.c83
-rw-r--r--drivers/hwmon/surface_temp.c235
-rw-r--r--drivers/hwmon/sy7636a-hwmon.c10
-rw-r--r--drivers/hwmon/tc654.c6
-rw-r--r--drivers/hwmon/tc74.c4
-rw-r--r--drivers/hwmon/thmc50.c8
-rw-r--r--drivers/hwmon/tmp102.c43
-rw-r--r--drivers/hwmon/tmp103.c17
-rw-r--r--drivers/hwmon/tmp108.c98
-rw-r--r--drivers/hwmon/tmp401.c33
-rw-r--r--drivers/hwmon/tmp421.c47
-rw-r--r--drivers/hwmon/tmp464.c56
-rw-r--r--drivers/hwmon/tmp513.c138
-rw-r--r--drivers/hwmon/tps23861.c114
-rw-r--r--drivers/hwmon/tsc1641.c748
-rw-r--r--drivers/hwmon/ultra45_env.c9
-rw-r--r--drivers/hwmon/vexpress-hwmon.c3
-rw-r--r--drivers/hwmon/via-cputemp.c3
-rw-r--r--drivers/hwmon/via686a.c228
-rw-r--r--drivers/hwmon/vt1211.c63
-rw-r--r--drivers/hwmon/vt8231.c232
-rw-r--r--drivers/hwmon/w83627ehf.c23
-rw-r--r--drivers/hwmon/w83627hf.c1604
-rw-r--r--drivers/hwmon/w83773g.c7
-rw-r--r--drivers/hwmon/w83781d.c22
-rw-r--r--drivers/hwmon/w83791d.c29
-rw-r--r--drivers/hwmon/w83792d.c12
-rw-r--r--drivers/hwmon/w83793.c13
-rw-r--r--drivers/hwmon/w83795.c12
-rw-r--r--drivers/hwmon/w83l785ts.c12
-rw-r--r--drivers/hwmon/w83l786ng.c33
-rw-r--r--drivers/hwmon/xgene-hwmon.c62
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c172
-rw-r--r--drivers/hwspinlock/hwspinlock_internal.h3
-rw-r--r--drivers/hwspinlock/omap_hwspinlock.c61
-rw-r--r--drivers/hwspinlock/qcom_hwspinlock.c77
-rw-r--r--drivers/hwspinlock/u8500_hsem.c4
-rw-r--r--drivers/hwtracing/Kconfig2
-rw-r--r--drivers/hwtracing/coresight/Kconfig83
-rw-r--r--drivers/hwtracing/coresight/Makefile34
-rw-r--r--drivers/hwtracing/coresight/coresight-catu.c222
-rw-r--r--drivers/hwtracing/coresight/coresight-catu.h11
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-afdo.c1
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-preload.c2
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-preload.h2
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-pstop.c83
-rw-r--r--drivers/hwtracing/coresight/coresight-config.c8
-rw-r--r--drivers/hwtracing/coresight/coresight-config.h4
-rw-r--r--drivers/hwtracing/coresight/coresight-core.c1438
-rw-r--r--drivers/hwtracing/coresight/coresight-cpu-debug.c117
-rw-r--r--drivers/hwtracing/coresight/coresight-ctcu-core.c313
-rw-r--r--drivers/hwtracing/coresight/coresight-ctcu.h39
-rw-r--r--drivers/hwtracing/coresight/coresight-cti-core.c133
-rw-r--r--drivers/hwtracing/coresight/coresight-cti-platform.c10
-rw-r--r--drivers/hwtracing/coresight/coresight-cti-sysfs.c300
-rw-r--r--drivers/hwtracing/coresight/coresight-cti.h12
-rw-r--r--drivers/hwtracing/coresight/coresight-dummy.c231
-rw-r--r--drivers/hwtracing/coresight/coresight-etb10.c118
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c156
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.h22
-rw-r--r--drivers/hwtracing/coresight/coresight-etm.h10
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-core.c201
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-sysfs.c72
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-cfg.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c876
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c332
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.h75
-rw-r--r--drivers/hwtracing/coresight/coresight-funnel.c171
-rw-r--r--drivers/hwtracing/coresight/coresight-kunit-tests.c74
-rw-r--r--drivers/hwtracing/coresight/coresight-platform.c370
-rw-r--r--drivers/hwtracing/coresight/coresight-priv.h152
-rw-r--r--drivers/hwtracing/coresight/coresight-replicator.c174
-rw-r--r--drivers/hwtracing/coresight/coresight-self-hosted-trace.h9
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c264
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg-configfs.c14
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg.c77
-rw-r--r--drivers/hwtracing/coresight/coresight-sysfs.c481
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-core.c571
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etf.c253
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etr.c554
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.h131
-rw-r--r--drivers/hwtracing/coresight/coresight-tnoc.c246
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.c363
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.h43
-rw-r--r--drivers/hwtracing/coresight/coresight-tpdm.c1549
-rw-r--r--drivers/hwtracing/coresight/coresight-tpdm.h358
-rw-r--r--drivers/hwtracing/coresight/coresight-tpiu.c152
-rw-r--r--drivers/hwtracing/coresight/coresight-trace-id.c300
-rw-r--r--drivers/hwtracing/coresight/coresight-trace-id.h159
-rw-r--r--drivers/hwtracing/coresight/coresight-trbe.c159
-rw-r--r--drivers/hwtracing/coresight/coresight-trbe.h45
-rw-r--r--drivers/hwtracing/coresight/ultrasoc-smb.c611
-rw-r--r--drivers/hwtracing/coresight/ultrasoc-smb.h124
-rw-r--r--drivers/hwtracing/intel_th/Kconfig1
-rw-r--r--drivers/hwtracing/intel_th/acpi.c4
-rw-r--r--drivers/hwtracing/intel_th/core.c49
-rw-r--r--drivers/hwtracing/intel_th/gth.c8
-rw-r--r--drivers/hwtracing/intel_th/intel_th.h6
-rw-r--r--drivers/hwtracing/intel_th/msu-sink.c1
-rw-r--r--drivers/hwtracing/intel_th/msu.c56
-rw-r--r--drivers/hwtracing/intel_th/pci.c54
-rw-r--r--drivers/hwtracing/intel_th/sth.c2
-rw-r--r--drivers/hwtracing/ptt/Kconfig12
-rw-r--r--drivers/hwtracing/ptt/Makefile2
-rw-r--r--drivers/hwtracing/ptt/hisi_ptt.c1442
-rw-r--r--drivers/hwtracing/ptt/hisi_ptt.h257
-rw-r--r--drivers/hwtracing/stm/Kconfig1
-rw-r--r--drivers/hwtracing/stm/console.c1
-rw-r--r--drivers/hwtracing/stm/core.c22
-rw-r--r--drivers/hwtracing/stm/ftrace.c1
-rw-r--r--drivers/hwtracing/stm/heartbeat.c7
-rw-r--r--drivers/hwtracing/stm/p_basic.c3
-rw-r--r--drivers/hwtracing/stm/p_sys-t.c93
-rw-r--r--drivers/hwtracing/stm/stm.h2
-rw-r--r--drivers/i2c/Kconfig19
-rw-r--r--drivers/i2c/Makefile8
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c81
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c6
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c110
-rw-r--r--drivers/i2c/busses/Kconfig311
-rw-r--r--drivers/i2c/busses/Makefile35
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c25
-rw-r--r--drivers/i2c/busses/i2c-ali1563.c4
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c31
-rw-r--r--drivers/i2c/busses/i2c-altera.c8
-rw-r--r--drivers/i2c/busses/i2c-amd-asf-plat.c370
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2-pci.c32
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2-plat.c42
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2.h2
-rw-r--r--drivers/i2c/busses/i2c-amd756-s4882.c245
-rw-r--r--drivers/i2c/busses/i2c-amd756.c10
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c2
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c102
-rw-r--r--drivers/i2c/busses/i2c-at91-core.c29
-rw-r--r--drivers/i2c/busses/i2c-at91-master.c16
-rw-r--r--drivers/i2c/busses/i2c-at91-slave.c3
-rw-r--r--drivers/i2c/busses/i2c-au1550.c37
-rw-r--r--drivers/i2c/busses/i2c-axxia.c37
-rw-r--r--drivers/i2c/busses/i2c-bcm-iproc.c408
-rw-r--r--drivers/i2c/busses/i2c-bcm-kona.c23
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c47
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c77
-rw-r--r--drivers/i2c/busses/i2c-cadence.c589
-rw-r--r--drivers/i2c/busses/i2c-cbus-gpio.c6
-rw-r--r--drivers/i2c/busses/i2c-ccgx-ucsi.c1
-rw-r--r--drivers/i2c/busses/i2c-cgbc.c406
-rw-r--r--drivers/i2c/busses/i2c-cht-wc.c60
-rw-r--r--drivers/i2c/busses/i2c-cp2615.c14
-rw-r--r--drivers/i2c/busses/i2c-cpm.c14
-rw-r--r--drivers/i2c/busses/i2c-cros-ec-tunnel.c15
-rw-r--r--drivers/i2c/busses/i2c-davinci.c184
-rw-r--r--drivers/i2c/busses/i2c-designware-amdisp.c207
-rw-r--r--drivers/i2c/busses/i2c-designware-amdpsp.c294
-rw-r--r--drivers/i2c/busses/i2c-designware-common.c324
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h330
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c540
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c167
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c321
-rw-r--r--drivers/i2c/busses/i2c-designware-slave.c109
-rw-r--r--drivers/i2c/busses/i2c-digicolor.c20
-rw-r--r--drivers/i2c/busses/i2c-diolan-u2c.c2
-rw-r--r--drivers/i2c/busses/i2c-dln2.c14
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c30
-rw-r--r--drivers/i2c/busses/i2c-elektor.c2
-rw-r--r--drivers/i2c/busses/i2c-emev2.c37
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c141
-rw-r--r--drivers/i2c/busses/i2c-fsi.c56
-rw-r--r--drivers/i2c/busses/i2c-gpio.c104
-rw-r--r--drivers/i2c/busses/i2c-gxp.c608
-rw-r--r--drivers/i2c/busses/i2c-highlander.c6
-rw-r--r--drivers/i2c/busses/i2c-hisi.c70
-rw-r--r--drivers/i2c/busses/i2c-hix5hd2.c48
-rw-r--r--drivers/i2c/busses/i2c-i801.c1130
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c56
-rw-r--r--drivers/i2c/busses/i2c-img-scb.c32
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c1151
-rw-r--r--drivers/i2c/busses/i2c-imx.c661
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c21
-rw-r--r--drivers/i2c/busses/i2c-isch.c324
-rw-r--r--drivers/i2c/busses/i2c-ismt.c23
-rw-r--r--drivers/i2c/busses/i2c-jz4780.c53
-rw-r--r--drivers/i2c/busses/i2c-k1.c628
-rw-r--r--drivers/i2c/busses/i2c-keba.c594
-rw-r--r--drivers/i2c/busses/i2c-kempld.c40
-rw-r--r--drivers/i2c/busses/i2c-ljca.c343
-rw-r--r--drivers/i2c/busses/i2c-lpc2k.c54
-rw-r--r--drivers/i2c/busses/i2c-ls2x.c377
-rw-r--r--drivers/i2c/busses/i2c-mchp-pci1xxxx.c1212
-rw-r--r--drivers/i2c/busses/i2c-meson.c11
-rw-r--r--drivers/i2c/busses/i2c-microchip-corei2c.c237
-rw-r--r--drivers/i2c/busses/i2c-mlxbf.c1086
-rw-r--r--drivers/i2c/busses/i2c-mlxcpld.c32
-rw-r--r--drivers/i2c/busses/i2c-mpc.c92
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c99
-rw-r--r--drivers/i2c/busses/i2c-mt7621.c69
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c53
-rw-r--r--drivers/i2c/busses/i2c-mxs.c29
-rw-r--r--drivers/i2c/busses/i2c-nct6694.c196
-rw-r--r--drivers/i2c/busses/i2c-nforce2-s4985.c240
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c22
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c844
-rw-r--r--drivers/i2c/busses/i2c-npcm7xx.c635
-rw-r--r--drivers/i2c/busses/i2c-nvidia-gpu.c17
-rw-r--r--drivers/i2c/busses/i2c-ocores.c144
-rw-r--r--drivers/i2c/busses/i2c-octeon-core.c406
-rw-r--r--drivers/i2c/busses/i2c-octeon-core.h68
-rw-r--r--drivers/i2c/busses/i2c-octeon-platdrv.c5
-rw-r--r--drivers/i2c/busses/i2c-omap.c133
-rw-r--r--drivers/i2c/busses/i2c-opal.c16
-rw-r--r--drivers/i2c/busses/i2c-owl.c35
-rw-r--r--drivers/i2c/busses/i2c-parport.c1
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.c187
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.h5
-rw-r--r--drivers/i2c/busses/i2c-pasemi-pci.c12
-rw-r--r--drivers/i2c/busses/i2c-pasemi-platform.c31
-rw-r--r--drivers/i2c/busses/i2c-pca-isa.c2
-rw-r--r--drivers/i2c/busses/i2c-pca-platform.c7
-rw-r--r--drivers/i2c/busses/i2c-piix4.c85
-rw-r--r--drivers/i2c/busses/i2c-piix4.h44
-rw-r--r--drivers/i2c/busses/i2c-pnx.c75
-rw-r--r--drivers/i2c/busses/i2c-powermac.c24
-rw-r--r--drivers/i2c/busses/i2c-pxa-pci.c15
-rw-r--r--drivers/i2c/busses/i2c-pxa.c153
-rw-r--r--drivers/i2c/busses/i2c-qcom-cci.c110
-rw-r--r--drivers/i2c/busses/i2c-qcom-geni.c436
-rw-r--r--drivers/i2c/busses/i2c-qup.c102
-rw-r--r--drivers/i2c/busses/i2c-rcar.c297
-rw-r--r--drivers/i2c/busses/i2c-riic.c519
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c114
-rw-r--r--drivers/i2c/busses/i2c-robotfuzz-osif.c10
-rw-r--r--drivers/i2c/busses/i2c-rtl9300.c539
-rw-r--r--drivers/i2c/busses/i2c-rzv2m.c79
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c173
-rw-r--r--drivers/i2c/busses/i2c-scmi.c55
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c13
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c60
-rw-r--r--drivers/i2c/busses/i2c-sibyte.c4
-rw-r--r--drivers/i2c/busses/i2c-simtec.c4
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c24
-rw-r--r--drivers/i2c/busses/i2c-sis630.c16
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c2
-rw-r--r--drivers/i2c/busses/i2c-sprd.c33
-rw-r--r--drivers/i2c/busses/i2c-st.c56
-rw-r--r--drivers/i2c/busses/i2c-stm32.c15
-rw-r--r--drivers/i2c/busses/i2c-stm32f4.c55
-rw-r--r--drivers/i2c/busses/i2c-stm32f7.c490
-rw-r--r--drivers/i2c/busses/i2c-sun6i-p2wi.c51
-rw-r--r--drivers/i2c/busses/i2c-synquacer.c64
-rw-r--r--drivers/i2c/busses/i2c-taos-evm.c2
-rw-r--r--drivers/i2c/busses/i2c-tegra-bpmp.c10
-rw-r--r--drivers/i2c/busses/i2c-tegra.c295
-rw-r--r--drivers/i2c/busses/i2c-thunderx-pcidrv.c20
-rw-r--r--drivers/i2c/busses/i2c-tiny-usb.c23
-rw-r--r--drivers/i2c/busses/i2c-uniphier-f.c68
-rw-r--r--drivers/i2c/busses/i2c-uniphier.c60
-rw-r--r--drivers/i2c/busses/i2c-usbio.c321
-rw-r--r--drivers/i2c/busses/i2c-versatile.c7
-rw-r--r--drivers/i2c/busses/i2c-via.c17
-rw-r--r--drivers/i2c/busses/i2c-viai2c-common.c203
-rw-r--r--drivers/i2c/busses/i2c-viai2c-common.h85
-rw-r--r--drivers/i2c/busses/i2c-viai2c-wmt.c178
-rw-r--r--drivers/i2c/busses/i2c-viai2c-zhaoxin.c367
-rw-r--r--drivers/i2c/busses/i2c-viapro.c35
-rw-r--r--drivers/i2c/busses/i2c-viperboard.c34
-rw-r--r--drivers/i2c/busses/i2c-virtio.c36
-rw-r--r--drivers/i2c/busses/i2c-wmt.c470
-rw-r--r--drivers/i2c/busses/i2c-xgene-slimpro.c82
-rw-r--r--drivers/i2c/busses/i2c-xiic.c964
-rw-r--r--drivers/i2c/busses/i2c-xlp9xx.c12
-rw-r--r--drivers/i2c/busses/scx200_acb.c12
-rw-r--r--drivers/i2c/i2c-atr.c954
-rw-r--r--drivers/i2c/i2c-core-acpi.c97
-rw-r--r--drivers/i2c/i2c-core-base.c397
-rw-r--r--drivers/i2c/i2c-core-of-prober.c415
-rw-r--r--drivers/i2c/i2c-core-of.c77
-rw-r--r--drivers/i2c/i2c-core-slave.c16
-rw-r--r--drivers/i2c/i2c-core-smbus.c14
-rw-r--r--drivers/i2c/i2c-core.h17
-rw-r--r--drivers/i2c/i2c-dev.c92
-rw-r--r--drivers/i2c/i2c-mux.c44
-rw-r--r--drivers/i2c/i2c-slave-eeprom.c11
-rw-r--r--drivers/i2c/i2c-slave-testunit.c197
-rw-r--r--drivers/i2c/i2c-smbus.c143
-rw-r--r--drivers/i2c/i2c-stub.c2
-rw-r--r--drivers/i2c/muxes/Kconfig28
-rw-r--r--drivers/i2c/muxes/Makefile1
-rw-r--r--drivers/i2c/muxes/i2c-arb-gpio-challenge.c7
-rw-r--r--drivers/i2c/muxes/i2c-demux-pinctrl.c34
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpio.c44
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpmux.c12
-rw-r--r--drivers/i2c/muxes/i2c-mux-ltc4306.c21
-rw-r--r--drivers/i2c/muxes/i2c-mux-mlxcpld.c7
-rw-r--r--drivers/i2c/muxes/i2c-mux-mule.c147
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca9541.c22
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c200
-rw-r--r--drivers/i2c/muxes/i2c-mux-pinctrl.c12
-rw-r--r--drivers/i2c/muxes/i2c-mux-reg.c17
-rw-r--r--drivers/i3c/device.c68
-rw-r--r--drivers/i3c/internals.h55
-rw-r--r--drivers/i3c/master.c522
-rw-r--r--drivers/i3c/master/Kconfig50
-rw-r--r--drivers/i3c/master/Makefile3
-rw-r--r--drivers/i3c/master/adi-i3c-master.c1019
-rw-r--r--drivers/i3c/master/ast2600-i3c-master.c187
-rw-r--r--drivers/i3c/master/dw-i3c-master.c848
-rw-r--r--drivers/i3c/master/dw-i3c-master.h97
-rw-r--r--drivers/i3c/master/i3c-master-cdns.c122
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/Makefile4
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/cmd_v1.c31
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/cmd_v2.c7
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/core.c154
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dat_v1.c40
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dma.c180
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/ext_caps.c11
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/hci.h17
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/hci_quirks.c44
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c296
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/pio.c77
-rw-r--r--drivers/i3c/master/renesas-i3c.c1404
-rw-r--r--drivers/i3c/master/svc-i3c-master.c916
-rw-r--r--drivers/idle/Makefile5
-rw-r--r--drivers/idle/intel_idle.c932
-rw-r--r--drivers/iio/Kconfig13
-rw-r--r--drivers/iio/Makefile3
-rw-r--r--drivers/iio/TODO3
-rw-r--r--drivers/iio/accel/Kconfig105
-rw-r--r--drivers/iio/accel/Makefile13
-rw-r--r--drivers/iio/accel/adis16201.c8
-rw-r--r--drivers/iio/accel/adis16209.c4
-rw-r--r--drivers/iio/accel/adxl313.h68
-rw-r--r--drivers/iio/accel/adxl313_core.c1119
-rw-r--r--drivers/iio/accel/adxl313_i2c.c82
-rw-r--r--drivers/iio/accel/adxl313_spi.c65
-rw-r--r--drivers/iio/accel/adxl345.h116
-rw-r--r--drivers/iio/accel/adxl345_core.c2015
-rw-r--r--drivers/iio/accel/adxl345_i2c.c28
-rw-r--r--drivers/iio/accel/adxl345_spi.c37
-rw-r--r--drivers/iio/accel/adxl355.h20
-rw-r--r--drivers/iio/accel/adxl355_core.c153
-rw-r--r--drivers/iio/accel/adxl355_i2c.c17
-rw-r--r--drivers/iio/accel/adxl355_spi.c17
-rw-r--r--drivers/iio/accel/adxl367.c303
-rw-r--r--drivers/iio/accel/adxl367_i2c.c13
-rw-r--r--drivers/iio/accel/adxl367_spi.c8
-rw-r--r--drivers/iio/accel/adxl372.c34
-rw-r--r--drivers/iio/accel/adxl372_i2c.c10
-rw-r--r--drivers/iio/accel/adxl372_spi.c7
-rw-r--r--drivers/iio/accel/adxl380.c1955
-rw-r--r--drivers/iio/accel/adxl380.h30
-rw-r--r--drivers/iio/accel/adxl380_i2c.c68
-rw-r--r--drivers/iio/accel/adxl380_spi.c70
-rw-r--r--drivers/iio/accel/bma180.c65
-rw-r--r--drivers/iio/accel/bma220.h28
-rw-r--r--drivers/iio/accel/bma220_core.c585
-rw-r--r--drivers/iio/accel/bma220_i2c.c69
-rw-r--r--drivers/iio/accel/bma220_spi.c325
-rw-r--r--drivers/iio/accel/bma400.h149
-rw-r--r--drivers/iio/accel/bma400_core.c684
-rw-r--r--drivers/iio/accel/bma400_i2c.c10
-rw-r--r--drivers/iio/accel/bma400_spi.c4
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c102
-rw-r--r--drivers/iio/accel/bmc150-accel-i2c.c31
-rw-r--r--drivers/iio/accel/bmc150-accel-spi.c9
-rw-r--r--drivers/iio/accel/bmc150-accel.h4
-rw-r--r--drivers/iio/accel/bmi088-accel-core.c42
-rw-r--r--drivers/iio/accel/bmi088-accel-i2c.c70
-rw-r--r--drivers/iio/accel/bmi088-accel-spi.c12
-rw-r--r--drivers/iio/accel/cros_ec_accel_legacy.c2
-rw-r--r--drivers/iio/accel/da280.c67
-rw-r--r--drivers/iio/accel/da311.c7
-rw-r--r--drivers/iio/accel/dmard06.c13
-rw-r--r--drivers/iio/accel/dmard09.c13
-rw-r--r--drivers/iio/accel/dmard10.c11
-rw-r--r--drivers/iio/accel/fxls8962af-core.c157
-rw-r--r--drivers/iio/accel/fxls8962af-i2c.c12
-rw-r--r--drivers/iio/accel/fxls8962af-spi.c8
-rw-r--r--drivers/iio/accel/fxls8962af.h3
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c18
-rw-r--r--drivers/iio/accel/kionix-kx022a-i2c.c72
-rw-r--r--drivers/iio/accel/kionix-kx022a-spi.c72
-rw-r--r--drivers/iio/accel/kionix-kx022a.c1456
-rw-r--r--drivers/iio/accel/kionix-kx022a.h206
-rw-r--r--drivers/iio/accel/kxcjk-1013.c573
-rw-r--r--drivers/iio/accel/kxsd9-i2c.c15
-rw-r--r--drivers/iio/accel/kxsd9-spi.c7
-rw-r--r--drivers/iio/accel/kxsd9.c22
-rw-r--r--drivers/iio/accel/mc3230.c106
-rw-r--r--drivers/iio/accel/mma7455_core.c14
-rw-r--r--drivers/iio/accel/mma7455_i2c.c14
-rw-r--r--drivers/iio/accel/mma7455_spi.c2
-rw-r--r--drivers/iio/accel/mma7660.c67
-rw-r--r--drivers/iio/accel/mma8452.c145
-rw-r--r--drivers/iio/accel/mma9551.c53
-rw-r--r--drivers/iio/accel/mma9551_core.c51
-rw-r--r--drivers/iio/accel/mma9553.c79
-rw-r--r--drivers/iio/accel/msa311.c1296
-rw-r--r--drivers/iio/accel/mxc4005.c151
-rw-r--r--drivers/iio/accel/mxc6255.c14
-rw-r--r--drivers/iio/accel/sca3000.c37
-rw-r--r--drivers/iio/accel/sca3300.c42
-rw-r--r--drivers/iio/accel/ssp_accel_sensor.c2
-rw-r--r--drivers/iio/accel/st_accel.h3
-rw-r--r--drivers/iio/accel/st_accel_core.c104
-rw-r--r--drivers/iio/accel/st_accel_i2c.c30
-rw-r--r--drivers/iio/accel/st_accel_spi.c21
-rw-r--r--drivers/iio/accel/stk8312.c32
-rw-r--r--drivers/iio/accel/stk8ba50.c36
-rw-r--r--drivers/iio/adc/88pm886-gpadc.c393
-rw-r--r--drivers/iio/adc/Kconfig717
-rw-r--r--drivers/iio/adc/Makefile71
-rw-r--r--drivers/iio/adc/ab8500-gpadc.c40
-rw-r--r--drivers/iio/adc/ad4000.c1264
-rw-r--r--drivers/iio/adc/ad4030.c1228
-rw-r--r--drivers/iio/adc/ad4080.c701
-rw-r--r--drivers/iio/adc/ad4130.c2123
-rw-r--r--drivers/iio/adc/ad4170-4.c3027
-rw-r--r--drivers/iio/adc/ad4695.c2026
-rw-r--r--drivers/iio/adc/ad4851.c1317
-rw-r--r--drivers/iio/adc/ad7091r-base.c316
-rw-r--r--drivers/iio/adc/ad7091r-base.h83
-rw-r--r--drivers/iio/adc/ad7091r5.c126
-rw-r--r--drivers/iio/adc/ad7091r8.c272
-rw-r--r--drivers/iio/adc/ad7124.c1553
-rw-r--r--drivers/iio/adc/ad7173.c2099
-rw-r--r--drivers/iio/adc/ad7191.c554
-rw-r--r--drivers/iio/adc/ad7192.c939
-rw-r--r--drivers/iio/adc/ad7266.c61
-rw-r--r--drivers/iio/adc/ad7280a.c32
-rw-r--r--drivers/iio/adc/ad7291.c14
-rw-r--r--drivers/iio/adc/ad7292.c52
-rw-r--r--drivers/iio/adc/ad7298.c18
-rw-r--r--drivers/iio/adc/ad7380.c2129
-rw-r--r--drivers/iio/adc/ad7405.c253
-rw-r--r--drivers/iio/adc/ad7476.c490
-rw-r--r--drivers/iio/adc/ad7606.c1503
-rw-r--r--drivers/iio/adc/ad7606.h253
-rw-r--r--drivers/iio/adc/ad7606_bus_iface.h16
-rw-r--r--drivers/iio/adc/ad7606_par.c199
-rw-r--r--drivers/iio/adc/ad7606_spi.c529
-rw-r--r--drivers/iio/adc/ad7625.c687
-rw-r--r--drivers/iio/adc/ad7766.c14
-rw-r--r--drivers/iio/adc/ad7768-1.c1239
-rw-r--r--drivers/iio/adc/ad7779.c1050
-rw-r--r--drivers/iio/adc/ad7780.c14
-rw-r--r--drivers/iio/adc/ad7791.c42
-rw-r--r--drivers/iio/adc/ad7793.c131
-rw-r--r--drivers/iio/adc/ad7887.c15
-rw-r--r--drivers/iio/adc/ad7923.c44
-rw-r--r--drivers/iio/adc/ad7944.c889
-rw-r--r--drivers/iio/adc/ad7949.c11
-rw-r--r--drivers/iio/adc/ad799x.c86
-rw-r--r--drivers/iio/adc/ad9467.c1144
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c558
-rw-r--r--drivers/iio/adc/ade9000.c1799
-rw-r--r--drivers/iio/adc/adi-axi-adc.c885
-rw-r--r--drivers/iio/adc/aspeed_adc.c80
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c812
-rw-r--r--drivers/iio/adc/at91_adc.c148
-rw-r--r--drivers/iio/adc/axp20x_adc.c586
-rw-r--r--drivers/iio/adc/axp288_adc.c21
-rw-r--r--drivers/iio/adc/bcm_iproc_adc.c30
-rw-r--r--drivers/iio/adc/berlin2-adc.c30
-rw-r--r--drivers/iio/adc/cc10001_adc.c94
-rw-r--r--drivers/iio/adc/cpcap-adc.c54
-rw-r--r--drivers/iio/adc/da9150-gpadc.c33
-rw-r--r--drivers/iio/adc/dln2-adc.c54
-rw-r--r--drivers/iio/adc/envelope-detector.c4
-rw-r--r--drivers/iio/adc/ep93xx_adc.c12
-rw-r--r--drivers/iio/adc/exynos_adc.c296
-rw-r--r--drivers/iio/adc/fsl-imx25-gcq.c170
-rw-r--r--drivers/iio/adc/gehc-pmc-adc.c228
-rw-r--r--drivers/iio/adc/hi8435.c10
-rw-r--r--drivers/iio/adc/hx711.c101
-rw-r--r--drivers/iio/adc/imx7d_adc.c22
-rw-r--r--drivers/iio/adc/imx8qxp-adc.c35
-rw-r--r--drivers/iio/adc/imx93_adc.c495
-rw-r--r--drivers/iio/adc/ina2xx-adc.c43
-rw-r--r--drivers/iio/adc/industrialio-adc.c82
-rw-r--r--drivers/iio/adc/ingenic-adc.c26
-rw-r--r--drivers/iio/adc/intel_dc_ti_adc.c328
-rw-r--r--drivers/iio/adc/intel_mrfld_adc.c24
-rw-r--r--drivers/iio/adc/lp8788_adc.c18
-rw-r--r--drivers/iio/adc/lpc18xx_adc.c20
-rw-r--r--drivers/iio/adc/lpc32xx_adc.c17
-rw-r--r--drivers/iio/adc/ltc2309.c215
-rw-r--r--drivers/iio/adc/ltc2471.c8
-rw-r--r--drivers/iio/adc/ltc2485.c6
-rw-r--r--drivers/iio/adc/ltc2496.c11
-rw-r--r--drivers/iio/adc/ltc2497-core.c38
-rw-r--r--drivers/iio/adc/ltc2497.c82
-rw-r--r--drivers/iio/adc/ltc2497.h10
-rw-r--r--drivers/iio/adc/max1027.c53
-rw-r--r--drivers/iio/adc/max11100.c6
-rw-r--r--drivers/iio/adc/max1118.c13
-rw-r--r--drivers/iio/adc/max11205.c180
-rw-r--r--drivers/iio/adc/max11410.c1046
-rw-r--r--drivers/iio/adc/max1241.c32
-rw-r--r--drivers/iio/adc/max1363.c339
-rw-r--r--drivers/iio/adc/max14001.c391
-rw-r--r--drivers/iio/adc/max34408.c277
-rw-r--r--drivers/iio/adc/max77541-adc.c194
-rw-r--r--drivers/iio/adc/max9611.c13
-rw-r--r--drivers/iio/adc/mcp320x.c39
-rw-r--r--drivers/iio/adc/mcp3422.c6
-rw-r--r--drivers/iio/adc/mcp3564.c1483
-rw-r--r--drivers/iio/adc/mcp3911.c895
-rw-r--r--drivers/iio/adc/men_z188_adc.c3
-rw-r--r--drivers/iio/adc/meson_saradc.c462
-rw-r--r--drivers/iio/adc/mp2629_adc.c36
-rw-r--r--drivers/iio/adc/mt6359-auxadc.c908
-rw-r--r--drivers/iio/adc/mt6360-adc.c17
-rw-r--r--drivers/iio/adc/mt6370-adc.c354
-rw-r--r--drivers/iio/adc/mt6577_auxadc.c71
-rw-r--r--drivers/iio/adc/mxs-lradc-adc.c40
-rw-r--r--drivers/iio/adc/nau7802.c6
-rw-r--r--drivers/iio/adc/nct7201.c501
-rw-r--r--drivers/iio/adc/npcm_adc.c10
-rw-r--r--drivers/iio/adc/pac1921.c1345
-rw-r--r--drivers/iio/adc/pac1934.c1632
-rw-r--r--drivers/iio/adc/palmas_gpadc.c623
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c69
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c105
-rw-r--r--drivers/iio/adc/qcom-spmi-iadc.c5
-rw-r--r--drivers/iio/adc/qcom-spmi-rradc.c60
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c62
-rw-r--r--drivers/iio/adc/qcom-vadc-common.c2
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c46
-rw-r--r--drivers/iio/adc/rn5t618-adc.c13
-rw-r--r--drivers/iio/adc/rockchip_saradc.c333
-rw-r--r--drivers/iio/adc/rohm-bd79112.c551
-rw-r--r--drivers/iio/adc/rohm-bd79124.c1125
-rw-r--r--drivers/iio/adc/rtq6056.c893
-rw-r--r--drivers/iio/adc/rzg2l_adc.c454
-rw-r--r--drivers/iio/adc/rzn1-adc.c490
-rw-r--r--drivers/iio/adc/rzt2h_adc.c304
-rw-r--r--drivers/iio/adc/sc27xx_adc.c56
-rw-r--r--drivers/iio/adc/sd_adc_modulator.c97
-rw-r--r--drivers/iio/adc/sophgo-cv1800b-adc.c227
-rw-r--r--drivers/iio/adc/spear_adc.c95
-rw-r--r--drivers/iio/adc/stm32-adc-core.c126
-rw-r--r--drivers/iio/adc/stm32-adc-core.h79
-rw-r--r--drivers/iio/adc/stm32-adc.c727
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c421
-rw-r--r--drivers/iio/adc/stm32-dfsdm-core.c110
-rw-r--r--drivers/iio/adc/stm32-dfsdm.h60
-rw-r--r--drivers/iio/adc/stmpe-adc.c8
-rw-r--r--drivers/iio/adc/stx104.c402
-rw-r--r--drivers/iio/adc/sun20i-gpadc-iio.c262
-rw-r--r--drivers/iio/adc/sun4i-gpadc-iio.c37
-rw-r--r--drivers/iio/adc/ti-adc081c.c54
-rw-r--r--drivers/iio/adc/ti-adc0832.c11
-rw-r--r--drivers/iio/adc/ti-adc084s021.c26
-rw-r--r--drivers/iio/adc/ti-adc108s102.c39
-rw-r--r--drivers/iio/adc/ti-adc12138.c41
-rw-r--r--drivers/iio/adc/ti-adc128s052.c210
-rw-r--r--drivers/iio/adc/ti-adc161s626.c18
-rw-r--r--drivers/iio/adc/ti-ads1015.c215
-rw-r--r--drivers/iio/adc/ti-ads1100.c432
-rw-r--r--drivers/iio/adc/ti-ads1119.c825
-rw-r--r--drivers/iio/adc/ti-ads124s08.c17
-rw-r--r--drivers/iio/adc/ti-ads1298.c771
-rw-r--r--drivers/iio/adc/ti-ads131e08.c88
-rw-r--r--drivers/iio/adc/ti-ads7138.c749
-rw-r--r--drivers/iio/adc/ti-ads7924.c468
-rw-r--r--drivers/iio/adc/ti-ads7950.c18
-rw-r--r--drivers/iio/adc/ti-ads8344.c2
-rw-r--r--drivers/iio/adc/ti-ads8688.c77
-rw-r--r--drivers/iio/adc/ti-lmp92064.c383
-rw-r--r--drivers/iio/adc/ti-tlc4541.c19
-rw-r--r--drivers/iio/adc/ti-tsc2046.c78
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c30
-rw-r--r--drivers/iio/adc/twl4030-madc.c28
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c56
-rw-r--r--drivers/iio/adc/vf610_adc.c207
-rw-r--r--drivers/iio/adc/viperboard_adc.c4
-rw-r--r--drivers/iio/adc/xilinx-ams.c215
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c85
-rw-r--r--drivers/iio/adc/xilinx-xadc-events.c4
-rw-r--r--drivers/iio/adc/xilinx-xadc.h4
-rw-r--r--drivers/iio/addac/Kconfig35
-rw-r--r--drivers/iio/addac/Makefile2
-rw-r--r--drivers/iio/addac/ad74115.c1930
-rw-r--r--drivers/iio/addac/ad74413r.c221
-rw-r--r--drivers/iio/addac/stx104.c523
-rw-r--r--drivers/iio/afe/iio-rescale.c25
-rw-r--r--drivers/iio/amplifiers/Kconfig2
-rw-r--r--drivers/iio/amplifiers/ad8366.c25
-rw-r--r--drivers/iio/amplifiers/ada4250.c59
-rw-r--r--drivers/iio/amplifiers/hmc425a.c295
-rw-r--r--drivers/iio/buffer/industrialio-buffer-cb.c4
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c393
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c249
-rw-r--r--drivers/iio/buffer/industrialio-hw-consumer.c4
-rw-r--r--drivers/iio/buffer/industrialio-triggered-buffer.c16
-rw-r--r--drivers/iio/buffer/kfifo_buf.c3
-rw-r--r--drivers/iio/cdc/Kconfig10
-rw-r--r--drivers/iio/cdc/Makefile1
-rw-r--r--drivers/iio/cdc/ad7150.c38
-rw-r--r--drivers/iio/cdc/ad7746.c819
-rw-r--r--drivers/iio/chemical/Kconfig55
-rw-r--r--drivers/iio/chemical/Makefile6
-rw-r--r--drivers/iio/chemical/ags02ma.c165
-rw-r--r--drivers/iio/chemical/ams-iaq-core.c7
-rw-r--r--drivers/iio/chemical/atlas-ezo-sensor.c14
-rw-r--r--drivers/iio/chemical/atlas-sensor.c57
-rw-r--r--drivers/iio/chemical/bme680.h54
-rw-r--r--drivers/iio/chemical/bme680_core.c1114
-rw-r--r--drivers/iio/chemical/bme680_i2c.c13
-rw-r--r--drivers/iio/chemical/bme680_spi.c17
-rw-r--r--drivers/iio/chemical/ccs811.c95
-rw-r--r--drivers/iio/chemical/ens160.h10
-rw-r--r--drivers/iio/chemical/ens160_core.c376
-rw-r--r--drivers/iio/chemical/ens160_i2c.c62
-rw-r--r--drivers/iio/chemical/ens160_spi.c61
-rw-r--r--drivers/iio/chemical/mhz19b.c316
-rw-r--r--drivers/iio/chemical/pms7003.c15
-rw-r--r--drivers/iio/chemical/scd30_core.c124
-rw-r--r--drivers/iio/chemical/scd30_i2c.c6
-rw-r--r--drivers/iio/chemical/scd30_serial.c10
-rw-r--r--drivers/iio/chemical/scd4x.c98
-rw-r--r--drivers/iio/chemical/sen0322.c161
-rw-r--r--drivers/iio/chemical/sgp30.c32
-rw-r--r--drivers/iio/chemical/sgp40.c15
-rw-r--r--drivers/iio/chemical/sps30.c8
-rw-r--r--drivers/iio/chemical/sps30_i2c.c12
-rw-r--r--drivers/iio/chemical/sps30_serial.c20
-rw-r--r--drivers/iio/chemical/sunrise_co2.c12
-rw-r--r--drivers/iio/chemical/vz89x.c18
-rw-r--r--drivers/iio/common/Kconfig1
-rw-r--r--drivers/iio/common/Makefile1
-rw-r--r--drivers/iio/common/cros_ec_sensors/Kconfig9
-rw-r--r--drivers/iio/common/cros_ec_sensors/Makefile4
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_activity.c307
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c2
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c2
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c119
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.c32
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.h56
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c32
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c21
-rw-r--r--drivers/iio/common/inv_sensors/Kconfig7
-rw-r--r--drivers/iio/common/inv_sensors/Makefile6
-rw-r--r--drivers/iio/common/inv_sensors/inv_sensors_timestamp.c205
-rw-r--r--drivers/iio/common/ms_sensors/ms_sensors_i2c.c28
-rw-r--r--drivers/iio/common/scmi_sensors/scmi_iio.c94
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_dev.c22
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_iio.c20
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_spi.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_buffer.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c111
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_i2c.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_spi.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c30
-rw-r--r--drivers/iio/dac/Kconfig186
-rw-r--r--drivers/iio/dac/Makefile15
-rw-r--r--drivers/iio/dac/ad3530r.c516
-rw-r--r--drivers/iio/dac/ad3552r-common.c294
-rw-r--r--drivers/iio/dac/ad3552r-hs.c884
-rw-r--r--drivers/iio/dac/ad3552r-hs.h27
-rw-r--r--drivers/iio/dac/ad3552r.c702
-rw-r--r--drivers/iio/dac/ad3552r.h232
-rw-r--r--drivers/iio/dac/ad5064.c14
-rw-r--r--drivers/iio/dac/ad5360.c4
-rw-r--r--drivers/iio/dac/ad5380.c99
-rw-r--r--drivers/iio/dac/ad5421.c4
-rw-r--r--drivers/iio/dac/ad5446-i2c.c102
-rw-r--r--drivers/iio/dac/ad5446-spi.c252
-rw-r--r--drivers/iio/dac/ad5446.c571
-rw-r--r--drivers/iio/dac/ad5446.h77
-rw-r--r--drivers/iio/dac/ad5449.c19
-rw-r--r--drivers/iio/dac/ad5504.c65
-rw-r--r--drivers/iio/dac/ad5592r-base.c165
-rw-r--r--drivers/iio/dac/ad5592r.c8
-rw-r--r--drivers/iio/dac/ad5593r.c66
-rw-r--r--drivers/iio/dac/ad5624r.h5
-rw-r--r--drivers/iio/dac/ad5624r_spi.c77
-rw-r--r--drivers/iio/dac/ad5686-spi.c10
-rw-r--r--drivers/iio/dac/ad5686.c73
-rw-r--r--drivers/iio/dac/ad5686.h7
-rw-r--r--drivers/iio/dac/ad5696-i2c.c20
-rw-r--r--drivers/iio/dac/ad5755.c38
-rw-r--r--drivers/iio/dac/ad5758.c2
-rw-r--r--drivers/iio/dac/ad5761.c113
-rw-r--r--drivers/iio/dac/ad5764.c4
-rw-r--r--drivers/iio/dac/ad5766.c10
-rw-r--r--drivers/iio/dac/ad5770r.c69
-rw-r--r--drivers/iio/dac/ad5791.c395
-rw-r--r--drivers/iio/dac/ad7293.c87
-rw-r--r--drivers/iio/dac/ad7303.c6
-rw-r--r--drivers/iio/dac/ad8460.c957
-rw-r--r--drivers/iio/dac/ad8801.c83
-rw-r--r--drivers/iio/dac/ad9739a.c469
-rw-r--r--drivers/iio/dac/adi-axi-dac.c1045
-rw-r--r--drivers/iio/dac/cio-dac.c72
-rw-r--r--drivers/iio/dac/dpot-dac.c6
-rw-r--r--drivers/iio/dac/ds4424.c14
-rw-r--r--drivers/iio/dac/lpc18xx_dac.c12
-rw-r--r--drivers/iio/dac/ltc1660.c4
-rw-r--r--drivers/iio/dac/ltc2632.c77
-rw-r--r--drivers/iio/dac/ltc2664.c736
-rw-r--r--drivers/iio/dac/ltc2688.c150
-rw-r--r--drivers/iio/dac/m62332.c9
-rw-r--r--drivers/iio/dac/max517.c10
-rw-r--r--drivers/iio/dac/max5522.c202
-rw-r--r--drivers/iio/dac/max5821.c42
-rw-r--r--drivers/iio/dac/mcp4725.c76
-rw-r--r--drivers/iio/dac/mcp4728.c599
-rw-r--r--drivers/iio/dac/mcp4821.c236
-rw-r--r--drivers/iio/dac/mcp4922.c49
-rw-r--r--drivers/iio/dac/rohm-bd79703.c246
-rw-r--r--drivers/iio/dac/stm32-dac-core.c20
-rw-r--r--drivers/iio/dac/stm32-dac.c30
-rw-r--r--drivers/iio/dac/ti-dac082s085.c2
-rw-r--r--drivers/iio/dac/ti-dac5571.c67
-rw-r--r--drivers/iio/dac/ti-dac7311.c10
-rw-r--r--drivers/iio/dac/ti-dac7612.c4
-rw-r--r--drivers/iio/dac/vf610_dac.c31
-rw-r--r--drivers/iio/dummy/iio_dummy_evgen.c2
-rw-r--r--drivers/iio/dummy/iio_simple_dummy.c216
-rw-r--r--drivers/iio/dummy/iio_simple_dummy.h2
-rw-r--r--drivers/iio/dummy/iio_simple_dummy_buffer.c29
-rw-r--r--drivers/iio/dummy/iio_simple_dummy_events.c32
-rw-r--r--drivers/iio/filter/admv8818.c313
-rw-r--r--drivers/iio/frequency/Kconfig54
-rw-r--r--drivers/iio/frequency/Makefile2
-rw-r--r--drivers/iio/frequency/ad9523.c26
-rw-r--r--drivers/iio/frequency/adf4350.c222
-rw-r--r--drivers/iio/frequency/adf4371.c121
-rw-r--r--drivers/iio/frequency/adf4377.c1011
-rw-r--r--drivers/iio/frequency/admfm2000.c270
-rw-r--r--drivers/iio/frequency/admv1013.c86
-rw-r--r--drivers/iio/frequency/admv1014.c40
-rw-r--r--drivers/iio/frequency/admv4420.c2
-rw-r--r--drivers/iio/frequency/adrf6780.c23
-rw-r--r--drivers/iio/gyro/Kconfig2
-rw-r--r--drivers/iio/gyro/adis16080.c2
-rw-r--r--drivers/iio/gyro/adis16130.c2
-rw-r--r--drivers/iio/gyro/adis16136.c30
-rw-r--r--drivers/iio/gyro/adis16260.c25
-rw-r--r--drivers/iio/gyro/adxrs290.c16
-rw-r--r--drivers/iio/gyro/adxrs450.c7
-rw-r--r--drivers/iio/gyro/bmg160_core.c57
-rw-r--r--drivers/iio/gyro/bmg160_i2c.c29
-rw-r--r--drivers/iio/gyro/bmg160_spi.c12
-rw-r--r--drivers/iio/gyro/fxas21002c_core.c37
-rw-r--r--drivers/iio/gyro/fxas21002c_i2c.c11
-rw-r--r--drivers/iio/gyro/fxas21002c_spi.c3
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c15
-rw-r--r--drivers/iio/gyro/itg3200_buffer.c2
-rw-r--r--drivers/iio/gyro/itg3200_core.c19
-rw-r--r--drivers/iio/gyro/mpu3050-core.c46
-rw-r--r--drivers/iio/gyro/mpu3050-i2c.c15
-rw-r--r--drivers/iio/gyro/ssp_gyro_sensor.c2
-rw-r--r--drivers/iio/gyro/st_gyro_core.c6
-rw-r--r--drivers/iio/gyro/st_gyro_i2c.c9
-rw-r--r--drivers/iio/gyro/st_gyro_spi.c6
-rw-r--r--drivers/iio/health/afe4403.c128
-rw-r--r--drivers/iio/health/afe4404.c140
-rw-r--r--drivers/iio/health/max30100.c72
-rw-r--r--drivers/iio/health/max30102.c41
-rw-r--r--drivers/iio/humidity/Kconfig23
-rw-r--r--drivers/iio/humidity/Makefile2
-rw-r--r--drivers/iio/humidity/am2315.c18
-rw-r--r--drivers/iio/humidity/dht11.c11
-rw-r--r--drivers/iio/humidity/ens210.c339
-rw-r--r--drivers/iio/humidity/hdc100x.c85
-rw-r--r--drivers/iio/humidity/hdc2010.c21
-rw-r--r--drivers/iio/humidity/hdc3020.c905
-rw-r--r--drivers/iio/humidity/hid-sensor-humidity.c10
-rw-r--r--drivers/iio/humidity/hts221.h4
-rw-r--r--drivers/iio/humidity/hts221_buffer.c5
-rw-r--r--drivers/iio/humidity/hts221_core.c122
-rw-r--r--drivers/iio/humidity/hts221_i2c.c15
-rw-r--r--drivers/iio/humidity/hts221_spi.c6
-rw-r--r--drivers/iio/humidity/htu21.c10
-rw-r--r--drivers/iio/humidity/si7005.c14
-rw-r--r--drivers/iio/humidity/si7020.c144
-rw-r--r--drivers/iio/iio_core.h7
-rw-r--r--drivers/iio/imu/Kconfig37
-rw-r--r--drivers/iio/imu/Makefile8
-rw-r--r--drivers/iio/imu/adis.c121
-rw-r--r--drivers/iio/imu/adis16400.c412
-rw-r--r--drivers/iio/imu/adis16460.c24
-rw-r--r--drivers/iio/imu/adis16475.c1209
-rw-r--r--drivers/iio/imu/adis16480.c564
-rw-r--r--drivers/iio/imu/adis16550.c1147
-rw-r--r--drivers/iio/imu/adis_buffer.c78
-rw-r--r--drivers/iio/imu/adis_trigger.c39
-rw-r--r--drivers/iio/imu/bmi160/bmi160.h2
-rw-r--r--drivers/iio/imu/bmi160/bmi160_core.c67
-rw-r--r--drivers/iio/imu/bmi160/bmi160_i2c.c28
-rw-r--r--drivers/iio/imu/bmi160/bmi160_spi.c13
-rw-r--r--drivers/iio/imu/bmi270/Kconfig33
-rw-r--r--drivers/iio/imu/bmi270/Makefile7
-rw-r--r--drivers/iio/imu/bmi270/bmi270.h25
-rw-r--r--drivers/iio/imu/bmi270/bmi270_core.c1664
-rw-r--r--drivers/iio/imu/bmi270/bmi270_i2c.c70
-rw-r--r--drivers/iio/imu/bmi270/bmi270_spi.c94
-rw-r--r--drivers/iio/imu/bmi323/Kconfig33
-rw-r--r--drivers/iio/imu/bmi323/Makefile7
-rw-r--r--drivers/iio/imu/bmi323/bmi323.h209
-rw-r--r--drivers/iio/imu/bmi323/bmi323_core.c2304
-rw-r--r--drivers/iio/imu/bmi323/bmi323_i2c.c143
-rw-r--r--drivers/iio/imu/bmi323/bmi323_spi.c93
-rw-r--r--drivers/iio/imu/bno055/Kconfig28
-rw-r--r--drivers/iio/imu/bno055/Makefile10
-rw-r--r--drivers/iio/imu/bno055/bno055.c1693
-rw-r--r--drivers/iio/imu/bno055/bno055.h13
-rw-r--r--drivers/iio/imu/bno055/bno055_i2c.c57
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_core.c560
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_trace.c14
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_trace.h104
-rw-r--r--drivers/iio/imu/fxos8700_core.c112
-rw-r--r--drivers/iio/imu/fxos8700_i2c.c9
-rw-r--r--drivers/iio/imu/fxos8700_spi.c3
-rw-r--r--drivers/iio/imu/inv_icm42600/Kconfig1
-rw-r--r--drivers/iio/imu/inv_icm42600/Makefile1
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600.h109
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c635
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c163
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h12
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_core.c401
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c210
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c40
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c41
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c28
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c195
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h85
-rw-r--r--drivers/iio/imu/inv_icm45600/Kconfig70
-rw-r--r--drivers/iio/imu/inv_icm45600/Makefile16
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600.h385
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c782
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c558
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h101
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_core.c988
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c791
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c98
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c79
-rw-r--r--drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c108
-rw-r--r--drivers/iio/imu/inv_mpu6050/Kconfig5
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c21
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c69
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c688
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c40
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h68
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c119
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c27
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c104
-rw-r--r--drivers/iio/imu/kmx61.c59
-rw-r--r--drivers/iio/imu/smi240.c620
-rw-r--r--drivers/iio/imu/smi330/Kconfig33
-rw-r--r--drivers/iio/imu/smi330/Makefile7
-rw-r--r--drivers/iio/imu/smi330/smi330.h25
-rw-r--r--drivers/iio/imu/smi330/smi330_core.c918
-rw-r--r--drivers/iio/imu/smi330/smi330_i2c.c133
-rw-r--r--drivers/iio/imu/smi330/smi330_spi.c85
-rw-r--r--drivers/iio/imu/st_lsm6dsx/Kconfig49
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h94
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c174
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c638
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c59
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c10
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c91
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c41
-rw-r--r--drivers/iio/imu/st_lsm9ds0/Kconfig3
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h5
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c86
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c24
-rw-r--r--drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c15
-rw-r--r--drivers/iio/industrialio-acpi.c125
-rw-r--r--drivers/iio/industrialio-backend.c1129
-rw-r--r--drivers/iio/industrialio-buffer.c933
-rw-r--r--drivers/iio/industrialio-core.c589
-rw-r--r--drivers/iio/industrialio-event.c94
-rw-r--r--drivers/iio/industrialio-gts-helper.c1253
-rw-r--r--drivers/iio/industrialio-sw-trigger.c6
-rw-r--r--drivers/iio/industrialio-trigger.c140
-rw-r--r--drivers/iio/inkern.c710
-rw-r--r--drivers/iio/light/Kconfig185
-rw-r--r--drivers/iio/light/Makefile16
-rw-r--r--drivers/iio/light/acpi-als.c23
-rw-r--r--drivers/iio/light/adjd_s311.c20
-rw-r--r--drivers/iio/light/adux1020.c36
-rw-r--r--drivers/iio/light/al3000a.c210
-rw-r--r--drivers/iio/light/al3010.c111
-rw-r--r--drivers/iio/light/al3320a.c128
-rw-r--r--drivers/iio/light/apds9160.c1592
-rw-r--r--drivers/iio/light/apds9300.c32
-rw-r--r--drivers/iio/light/apds9306.c1359
-rw-r--r--drivers/iio/light/apds9960.c114
-rw-r--r--drivers/iio/light/as73211.c214
-rw-r--r--drivers/iio/light/bh1745.c901
-rw-r--r--drivers/iio/light/bh1750.c32
-rw-r--r--drivers/iio/light/bh1780.c14
-rw-r--r--drivers/iio/light/cm32181.c28
-rw-r--r--drivers/iio/light/cm3232.c50
-rw-r--r--drivers/iio/light/cm3323.c9
-rw-r--r--drivers/iio/light/cm3605.c8
-rw-r--r--drivers/iio/light/cm36651.c14
-rw-r--r--drivers/iio/light/cros_ec_light_prox.c2
-rw-r--r--drivers/iio/light/gp2ap002.c19
-rw-r--r--drivers/iio/light/gp2ap020a00f.c31
-rw-r--r--drivers/iio/light/hid-sensor-als.c195
-rw-r--r--drivers/iio/light/hid-sensor-prox.c241
-rw-r--r--drivers/iio/light/iqs621-als.c6
-rw-r--r--drivers/iio/light/isl29018.c52
-rw-r--r--drivers/iio/light/isl29028.c31
-rw-r--r--drivers/iio/light/isl29125.c33
-rw-r--r--drivers/iio/light/isl76682.c345
-rw-r--r--drivers/iio/light/jsa1212.c18
-rw-r--r--drivers/iio/light/lm3533-als.c10
-rw-r--r--drivers/iio/light/ltr390.c916
-rw-r--r--drivers/iio/light/ltr501.c265
-rw-r--r--drivers/iio/light/ltrf216a.c583
-rw-r--r--drivers/iio/light/lv0104cs.c5
-rw-r--r--drivers/iio/light/max44000.c29
-rw-r--r--drivers/iio/light/max44009.c20
-rw-r--r--drivers/iio/light/noa1305.c200
-rw-r--r--drivers/iio/light/opt3001.c209
-rw-r--r--drivers/iio/light/opt4001.c466
-rw-r--r--drivers/iio/light/opt4060.c1341
-rw-r--r--drivers/iio/light/pa12203001.c26
-rw-r--r--drivers/iio/light/rohm-bu27034.c1329
-rw-r--r--drivers/iio/light/rpr0521.c110
-rw-r--r--drivers/iio/light/si1133.c10
-rw-r--r--drivers/iio/light/si1145.c43
-rw-r--r--drivers/iio/light/st_uvis25.h5
-rw-r--r--drivers/iio/light/st_uvis25_core.c41
-rw-r--r--drivers/iio/light/st_uvis25_i2c.c11
-rw-r--r--drivers/iio/light/st_uvis25_spi.c8
-rw-r--r--drivers/iio/light/stk3310.c79
-rw-r--r--drivers/iio/light/tcs3414.c32
-rw-r--r--drivers/iio/light/tcs3472.c37
-rw-r--r--drivers/iio/light/tsl2563.c202
-rw-r--r--drivers/iio/light/tsl2583.c25
-rw-r--r--drivers/iio/light/tsl2591.c10
-rw-r--r--drivers/iio/light/tsl2772.c15
-rw-r--r--drivers/iio/light/tsl4531.c11
-rw-r--r--drivers/iio/light/us5182d.c33
-rw-r--r--drivers/iio/light/vcnl4000.c1331
-rw-r--r--drivers/iio/light/vcnl4035.c77
-rw-r--r--drivers/iio/light/veml3235.c547
-rw-r--r--drivers/iio/light/veml6030.c911
-rw-r--r--drivers/iio/light/veml6040.c280
-rw-r--r--drivers/iio/light/veml6046x00.c1030
-rw-r--r--drivers/iio/light/veml6070.c210
-rw-r--r--drivers/iio/light/veml6075.c478
-rw-r--r--drivers/iio/light/vl6180.c265
-rw-r--r--drivers/iio/light/zopt2201.c51
-rw-r--r--drivers/iio/magnetometer/Kconfig73
-rw-r--r--drivers/iio/magnetometer/Makefile8
-rw-r--r--drivers/iio/magnetometer/af8133j.c527
-rw-r--r--drivers/iio/magnetometer/ak8974.c47
-rw-r--r--drivers/iio/magnetometer/ak8975.c194
-rw-r--r--drivers/iio/magnetometer/als31300.c490
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c47
-rw-r--r--drivers/iio/magnetometer/bmc150_magn_i2c.c28
-rw-r--r--drivers/iio/magnetometer/bmc150_magn_spi.c14
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c12
-rw-r--r--drivers/iio/magnetometer/hmc5843.h17
-rw-r--r--drivers/iio/magnetometer/hmc5843_core.c16
-rw-r--r--drivers/iio/magnetometer/hmc5843_i2c.c14
-rw-r--r--drivers/iio/magnetometer/hmc5843_spi.c17
-rw-r--r--drivers/iio/magnetometer/mag3110.c177
-rw-r--r--drivers/iio/magnetometer/mmc35240.c24
-rw-r--r--drivers/iio/magnetometer/rm3100-core.c33
-rw-r--r--drivers/iio/magnetometer/rm3100-i2c.c4
-rw-r--r--drivers/iio/magnetometer/rm3100-spi.c3
-rw-r--r--drivers/iio/magnetometer/si7210.c446
-rw-r--r--drivers/iio/magnetometer/st_magn.h1
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c8
-rw-r--r--drivers/iio/magnetometer/st_magn_i2c.c14
-rw-r--r--drivers/iio/magnetometer/st_magn_spi.c11
-rw-r--r--drivers/iio/magnetometer/tlv493d.c526
-rw-r--r--drivers/iio/magnetometer/tmag5273.c735
-rw-r--r--drivers/iio/magnetometer/yamaha-yas530.c878
-rw-r--r--drivers/iio/multiplexer/iio-mux.c95
-rw-r--r--drivers/iio/orientation/hid-sensor-incl-3d.c14
-rw-r--r--drivers/iio/orientation/hid-sensor-rotation.c16
-rw-r--r--drivers/iio/position/hid-sensor-custom-intel-hinge.c16
-rw-r--r--drivers/iio/position/iqs624-pos.c2
-rw-r--r--drivers/iio/potentiometer/Kconfig10
-rw-r--r--drivers/iio/potentiometer/Makefile1
-rw-r--r--drivers/iio/potentiometer/ad5110.c23
-rw-r--r--drivers/iio/potentiometer/ad5272.c8
-rw-r--r--drivers/iio/potentiometer/ds1803.c19
-rw-r--r--drivers/iio/potentiometer/max5432.c5
-rw-r--r--drivers/iio/potentiometer/max5487.c6
-rw-r--r--drivers/iio/potentiometer/mcp4018.c39
-rw-r--r--drivers/iio/potentiometer/mcp41010.c4
-rw-r--r--drivers/iio/potentiometer/mcp4131.c5
-rw-r--r--drivers/iio/potentiometer/mcp4531.c143
-rw-r--r--drivers/iio/potentiometer/tpl0102.c6
-rw-r--r--drivers/iio/potentiometer/x9250.c220
-rw-r--r--drivers/iio/potentiostat/lmp91000.c21
-rw-r--r--drivers/iio/pressure/Kconfig91
-rw-r--r--drivers/iio/pressure/Makefile16
-rw-r--r--drivers/iio/pressure/abp060mg.c10
-rw-r--r--drivers/iio/pressure/adp810.c225
-rw-r--r--drivers/iio/pressure/bmp280-core.c3140
-rw-r--r--drivers/iio/pressure/bmp280-i2c.c50
-rw-r--r--drivers/iio/pressure/bmp280-regmap.c166
-rw-r--r--drivers/iio/pressure/bmp280-spi.c110
-rw-r--r--drivers/iio/pressure/bmp280.h538
-rw-r--r--drivers/iio/pressure/cros_ec_baro.c2
-rw-r--r--drivers/iio/pressure/dlhl60d.c79
-rw-r--r--drivers/iio/pressure/dps310.c408
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c19
-rw-r--r--drivers/iio/pressure/hp03.c10
-rw-r--r--drivers/iio/pressure/hp206c.c16
-rw-r--r--drivers/iio/pressure/hsc030pa.c541
-rw-r--r--drivers/iio/pressure/hsc030pa.h81
-rw-r--r--drivers/iio/pressure/hsc030pa_i2c.c74
-rw-r--r--drivers/iio/pressure/hsc030pa_spi.c61
-rw-r--r--drivers/iio/pressure/icp10100.c31
-rw-r--r--drivers/iio/pressure/mpl115.c62
-rw-r--r--drivers/iio/pressure/mpl115.h5
-rw-r--r--drivers/iio/pressure/mpl115_i2c.c9
-rw-r--r--drivers/iio/pressure/mpl115_spi.c5
-rw-r--r--drivers/iio/pressure/mpl3115.c655
-rw-r--r--drivers/iio/pressure/mprls0025pa.c455
-rw-r--r--drivers/iio/pressure/mprls0025pa.h97
-rw-r--r--drivers/iio/pressure/mprls0025pa_i2c.c97
-rw-r--r--drivers/iio/pressure/mprls0025pa_spi.c92
-rw-r--r--drivers/iio/pressure/ms5611.h16
-rw-r--r--drivers/iio/pressure/ms5611_core.c114
-rw-r--r--drivers/iio/pressure/ms5611_i2c.c16
-rw-r--r--drivers/iio/pressure/ms5611_spi.c13
-rw-r--r--drivers/iio/pressure/ms5637.c14
-rw-r--r--drivers/iio/pressure/rohm-bm1390.c913
-rw-r--r--drivers/iio/pressure/sdp500.c156
-rw-r--r--drivers/iio/pressure/st_pressure.h2
-rw-r--r--drivers/iio/pressure/st_pressure_core.c78
-rw-r--r--drivers/iio/pressure/st_pressure_i2c.c21
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c11
-rw-r--r--drivers/iio/pressure/t5403.c6
-rw-r--r--drivers/iio/pressure/zpa2326.c51
-rw-r--r--drivers/iio/pressure/zpa2326_i2c.c17
-rw-r--r--drivers/iio/pressure/zpa2326_spi.c6
-rw-r--r--drivers/iio/proximity/Kconfig48
-rw-r--r--drivers/iio/proximity/Makefile4
-rw-r--r--drivers/iio/proximity/as3935.c12
-rw-r--r--drivers/iio/proximity/aw96103.c846
-rw-r--r--drivers/iio/proximity/cros_ec_mkbp_proximity.c27
-rw-r--r--drivers/iio/proximity/d3323aa.c815
-rw-r--r--drivers/iio/proximity/hx9023s.c1222
-rw-r--r--drivers/iio/proximity/irsd200.c955
-rw-r--r--drivers/iio/proximity/isl29501.c30
-rw-r--r--drivers/iio/proximity/mb1232.c30
-rw-r--r--drivers/iio/proximity/ping.c6
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c34
-rw-r--r--drivers/iio/proximity/rfd77402.c7
-rw-r--r--drivers/iio/proximity/srf04.c24
-rw-r--r--drivers/iio/proximity/srf08.c26
-rw-r--r--drivers/iio/proximity/sx9310.c171
-rw-r--r--drivers/iio/proximity/sx9324.c328
-rw-r--r--drivers/iio/proximity/sx9360.c159
-rw-r--r--drivers/iio/proximity/sx9500.c77
-rw-r--r--drivers/iio/proximity/sx_common.c54
-rw-r--r--drivers/iio/proximity/sx_common.h8
-rw-r--r--drivers/iio/proximity/vcnl3020.c22
-rw-r--r--drivers/iio/proximity/vl53l0x-i2c.c191
-rw-r--r--drivers/iio/resolver/Kconfig16
-rw-r--r--drivers/iio/resolver/Makefile1
-rw-r--r--drivers/iio/resolver/ad2s1200.c5
-rw-r--r--drivers/iio/resolver/ad2s1210.c1617
-rw-r--r--drivers/iio/resolver/ad2s90.c4
-rw-r--r--drivers/iio/temperature/Kconfig36
-rw-r--r--drivers/iio/temperature/Makefile3
-rw-r--r--drivers/iio/temperature/hid-sensor-temperature.c15
-rw-r--r--drivers/iio/temperature/ltc2983.c576
-rw-r--r--drivers/iio/temperature/max30208.c251
-rw-r--r--drivers/iio/temperature/max31856.c2
-rw-r--r--drivers/iio/temperature/max31865.c2
-rw-r--r--drivers/iio/temperature/maxim_thermocouple.c61
-rw-r--r--drivers/iio/temperature/mcp9600.c580
-rw-r--r--drivers/iio/temperature/mlx90614.c262
-rw-r--r--drivers/iio/temperature/mlx90632.c550
-rw-r--r--drivers/iio/temperature/mlx90635.c1094
-rw-r--r--drivers/iio/temperature/tmp006.c140
-rw-r--r--drivers/iio/temperature/tmp007.c10
-rw-r--r--drivers/iio/temperature/tmp117.c107
-rw-r--r--drivers/iio/temperature/tsys01.c11
-rw-r--r--drivers/iio/temperature/tsys02d.c10
-rw-r--r--drivers/iio/test/Kconfig26
-rw-r--r--drivers/iio/test/Makefile2
-rw-r--r--drivers/iio/test/iio-test-format.c2
-rw-r--r--drivers/iio/test/iio-test-gts.c515
-rw-r--r--drivers/iio/test/iio-test-multiply.c212
-rw-r--r--drivers/iio/test/iio-test-rescale.c12
-rw-r--r--drivers/iio/trigger/iio-trig-hrtimer.c4
-rw-r--r--drivers/iio/trigger/iio-trig-interrupt.c4
-rw-r--r--drivers/iio/trigger/iio-trig-loop.c2
-rw-r--r--drivers/iio/trigger/iio-trig-sysfs.c20
-rw-r--r--drivers/iio/trigger/stm32-lptimer-trigger.c87
-rw-r--r--drivers/iio/trigger/stm32-timer-trigger.c112
-rw-r--r--drivers/infiniband/Kconfig6
-rw-r--r--drivers/infiniband/core/Makefile4
-rw-r--r--drivers/infiniband/core/addr.c95
-rw-r--r--drivers/infiniband/core/agent.c35
-rw-r--r--drivers/infiniband/core/cache.c84
-rw-r--r--drivers/infiniband/core/cm.c431
-rw-r--r--drivers/infiniband/core/cm_trace.h4
-rw-r--r--drivers/infiniband/core/cma.c684
-rw-r--r--drivers/infiniband/core/cma_configfs.c4
-rw-r--r--drivers/infiniband/core/cma_priv.h4
-rw-r--r--drivers/infiniband/core/cma_trace.h8
-rw-r--r--drivers/infiniband/core/core_priv.h4
-rw-r--r--drivers/infiniband/core/counters.c54
-rw-r--r--drivers/infiniband/core/cq.c12
-rw-r--r--drivers/infiniband/core/device.c414
-rw-r--r--drivers/infiniband/core/iwcm.c59
-rw-r--r--drivers/infiniband/core/iwpm_util.c2
-rw-r--r--drivers/infiniband/core/lag.c11
-rw-r--r--drivers/infiniband/core/mad.c523
-rw-r--r--drivers/infiniband/core/mad_priv.h76
-rw-r--r--drivers/infiniband/core/mad_rmpp.c43
-rw-r--r--drivers/infiniband/core/netlink.c3
-rw-r--r--drivers/infiniband/core/nldev.c505
-rw-r--r--drivers/infiniband/core/rdma_core.c41
-rw-r--r--drivers/infiniband/core/rdma_core.h1
-rw-r--r--drivers/infiniband/core/restrack.c71
-rw-r--r--drivers/infiniband/core/roce_gid_mgmt.c33
-rw-r--r--drivers/infiniband/core/rw.c2
-rw-r--r--drivers/infiniband/core/sa_query.c461
-rw-r--r--drivers/infiniband/core/sysfs.c46
-rw-r--r--drivers/infiniband/core/ucaps.c267
-rw-r--r--drivers/infiniband/core/ucma.c159
-rw-r--r--drivers/infiniband/core/ud_header.c83
-rw-r--r--drivers/infiniband/core/umem.c61
-rw-r--r--drivers/infiniband/core/umem_dmabuf.c71
-rw-r--r--drivers/infiniband/core/umem_odp.c282
-rw-r--r--drivers/infiniband/core/user_mad.c83
-rw-r--r--drivers/infiniband/core/uverbs.h29
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c235
-rw-r--r--drivers/infiniband/core/uverbs_ioctl.c86
-rw-r--r--drivers/infiniband/core/uverbs_main.c101
-rw-r--r--drivers/infiniband/core/uverbs_marshall.c42
-rw-r--r--drivers/infiniband/core/uverbs_std_types_counters.c2
-rw-r--r--drivers/infiniband/core/uverbs_std_types_cq.c88
-rw-r--r--drivers/infiniband/core/uverbs_std_types_device.c7
-rw-r--r--drivers/infiniband/core/uverbs_std_types_dmah.c145
-rw-r--r--drivers/infiniband/core/uverbs_std_types_mr.c174
-rw-r--r--drivers/infiniband/core/uverbs_std_types_qp.c4
-rw-r--r--drivers/infiniband/core/uverbs_uapi.c1
-rw-r--r--drivers/infiniband/core/verbs.c226
-rw-r--r--drivers/infiniband/hw/Makefile6
-rw-r--r--drivers/infiniband/hw/bng_re/Kconfig10
-rw-r--r--drivers/infiniband/hw/bng_re/Makefile8
-rw-r--r--drivers/infiniband/hw/bng_re/bng_debugfs.c39
-rw-r--r--drivers/infiniband/hw/bng_re/bng_debugfs.h12
-rw-r--r--drivers/infiniband/hw/bng_re/bng_dev.c534
-rw-r--r--drivers/infiniband/hw/bng_re/bng_fw.c767
-rw-r--r--drivers/infiniband/hw/bng_re/bng_fw.h211
-rw-r--r--drivers/infiniband/hw/bng_re/bng_re.h85
-rw-r--r--drivers/infiniband/hw/bng_re/bng_res.c279
-rw-r--r--drivers/infiniband/hw/bng_re/bng_res.h215
-rw-r--r--drivers/infiniband/hw/bng_re/bng_sp.c131
-rw-r--r--drivers/infiniband/hw/bng_re/bng_sp.h47
-rw-r--r--drivers/infiniband/hw/bng_re/bng_tlv.h128
-rw-r--r--drivers/infiniband/hw/bnxt_re/Makefile3
-rw-r--r--drivers/infiniband/hw/bnxt_re/bnxt_re.h166
-rw-r--r--drivers/infiniband/hw/bnxt_re/debugfs.c524
-rw-r--r--drivers/infiniband/hw/bnxt_re/debugfs.h55
-rw-r--r--drivers/infiniband/hw/bnxt_re/hw_counters.c194
-rw-r--r--drivers/infiniband/hw/bnxt_re/hw_counters.h49
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.c1603
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.h82
-rw-r--r--drivers/infiniband/hw/bnxt_re/main.c2333
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_fp.c895
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_fp.h110
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_rcfw.c854
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_rcfw.h133
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_res.c307
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_res.h256
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_sp.c736
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_sp.h103
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_tlv.h162
-rw-r--r--drivers/infiniband/hw/bnxt_re/roce_hsi.h7427
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c32
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c13
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c14
-rw-r--r--drivers/infiniband/hw/cxgb4/id_table.c4
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h45
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c8
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c68
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c42
-rw-r--r--drivers/infiniband/hw/cxgb4/restrack.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/t4fw_ri_api.h26
-rw-r--r--drivers/infiniband/hw/efa/efa.h26
-rw-r--r--drivers/infiniband/hw/efa/efa_admin_cmds_defs.h157
-rw-r--r--drivers/infiniband/hw/efa/efa_admin_defs.h4
-rw-r--r--drivers/infiniband/hw/efa/efa_com.c46
-rw-r--r--drivers/infiniband/hw/efa/efa_com.h6
-rw-r--r--drivers/infiniband/hw/efa/efa_com_cmd.c75
-rw-r--r--drivers/infiniband/hw/efa/efa_com_cmd.h38
-rw-r--r--drivers/infiniband/hw/efa/efa_io_defs.h391
-rw-r--r--drivers/infiniband/hw/efa/efa_main.c106
-rw-r--r--drivers/infiniband/hw/efa/efa_verbs.c287
-rw-r--r--drivers/infiniband/hw/erdma/Kconfig2
-rw-r--r--drivers/infiniband/hw/erdma/erdma.h56
-rw-r--r--drivers/infiniband/hw/erdma/erdma_cm.c89
-rw-r--r--drivers/infiniband/hw/erdma/erdma_cm.h10
-rw-r--r--drivers/infiniband/hw/erdma/erdma_cmdq.c173
-rw-r--r--drivers/infiniband/hw/erdma/erdma_cq.c75
-rw-r--r--drivers/infiniband/hw/erdma/erdma_eq.c115
-rw-r--r--drivers/infiniband/hw/erdma/erdma_hw.h325
-rw-r--r--drivers/infiniband/hw/erdma/erdma_main.c222
-rw-r--r--drivers/infiniband/hw/erdma/erdma_qp.c387
-rw-r--r--drivers/infiniband/hw/erdma/erdma_verbs.c1460
-rw-r--r--drivers/infiniband/hw/erdma/erdma_verbs.h271
-rw-r--r--drivers/infiniband/hw/hfi1/Makefile1
-rw-r--r--drivers/infiniband/hw/hfi1/affinity.c104
-rw-r--r--drivers/infiniband/hw/hfi1/affinity.h2
-rw-r--r--drivers/infiniband/hw/hfi1/aspm.c4
-rw-r--r--drivers/infiniband/hw/hfi1/aspm.h2
-rw-r--r--drivers/infiniband/hw/hfi1/chip.c157
-rw-r--r--drivers/infiniband/hw/hfi1/chip.h4
-rw-r--r--drivers/infiniband/hw/hfi1/chip_registers.h2
-rw-r--r--drivers/infiniband/hw/hfi1/common.h2
-rw-r--r--drivers/infiniband/hw/hfi1/debugfs.c30
-rw-r--r--drivers/infiniband/hw/hfi1/debugfs.h11
-rw-r--r--drivers/infiniband/hw/hfi1/device.c78
-rw-r--r--drivers/infiniband/hw/hfi1/device.h2
-rw-r--r--drivers/infiniband/hw/hfi1/driver.c13
-rw-r--r--drivers/infiniband/hw/hfi1/efivar.c4
-rw-r--r--drivers/infiniband/hw/hfi1/efivar.h2
-rw-r--r--drivers/infiniband/hw/hfi1/eprom.c2
-rw-r--r--drivers/infiniband/hw/hfi1/eprom.h2
-rw-r--r--drivers/infiniband/hw/hfi1/exp_rcv.c2
-rw-r--r--drivers/infiniband/hw/hfi1/exp_rcv.h7
-rw-r--r--drivers/infiniband/hw/hfi1/fault.c12
-rw-r--r--drivers/infiniband/hw/hfi1/fault.h2
-rw-r--r--drivers/infiniband/hw/hfi1/file_ops.c108
-rw-r--r--drivers/infiniband/hw/hfi1/firmware.c10
-rw-r--r--drivers/infiniband/hw/hfi1/hfi.h22
-rw-r--r--drivers/infiniband/hw/hfi1/init.c49
-rw-r--r--drivers/infiniband/hw/hfi1/intr.c33
-rw-r--r--drivers/infiniband/hw/hfi1/iowait.h4
-rw-r--r--drivers/infiniband/hw/hfi1/ipoib_main.c20
-rw-r--r--drivers/infiniband/hw/hfi1/ipoib_rx.c5
-rw-r--r--drivers/infiniband/hw/hfi1/ipoib_tx.c10
-rw-r--r--drivers/infiniband/hw/hfi1/mad.c30
-rw-r--r--drivers/infiniband/hw/hfi1/mad.h3
-rw-r--r--drivers/infiniband/hw/hfi1/mmu_rb.c182
-rw-r--r--drivers/infiniband/hw/hfi1/mmu_rb.h38
-rw-r--r--drivers/infiniband/hw/hfi1/netdev.h2
-rw-r--r--drivers/infiniband/hw/hfi1/netdev_rx.c11
-rw-r--r--drivers/infiniband/hw/hfi1/opa_compat.h2
-rw-r--r--drivers/infiniband/hw/hfi1/opfn.c4
-rw-r--r--drivers/infiniband/hw/hfi1/pcie.c43
-rw-r--r--drivers/infiniband/hw/hfi1/pin_system.c474
-rw-r--r--drivers/infiniband/hw/hfi1/pinning.h20
-rw-r--r--drivers/infiniband/hw/hfi1/pio.c32
-rw-r--r--drivers/infiniband/hw/hfi1/pio.h3
-rw-r--r--drivers/infiniband/hw/hfi1/pio_copy.c2
-rw-r--r--drivers/infiniband/hw/hfi1/platform.c2
-rw-r--r--drivers/infiniband/hw/hfi1/platform.h2
-rw-r--r--drivers/infiniband/hw/hfi1/qp.c2
-rw-r--r--drivers/infiniband/hw/hfi1/qp.h2
-rw-r--r--drivers/infiniband/hw/hfi1/qsfp.c22
-rw-r--r--drivers/infiniband/hw/hfi1/qsfp.h4
-rw-r--r--drivers/infiniband/hw/hfi1/rc.c2
-rw-r--r--drivers/infiniband/hw/hfi1/ruc.c2
-rw-r--r--drivers/infiniband/hw/hfi1/sdma.c53
-rw-r--r--drivers/infiniband/hw/hfi1/sdma.h61
-rw-r--r--drivers/infiniband/hw/hfi1/sdma_txreq.h5
-rw-r--r--drivers/infiniband/hw/hfi1/sysfs.c14
-rw-r--r--drivers/infiniband/hw/hfi1/tid_rdma.c40
-rw-r--r--drivers/infiniband/hw/hfi1/trace.c2
-rw-r--r--drivers/infiniband/hw/hfi1/trace.h2
-rw-r--r--drivers/infiniband/hw/hfi1/trace_ctxts.h2
-rw-r--r--drivers/infiniband/hw/hfi1/trace_dbg.h11
-rw-r--r--drivers/infiniband/hw/hfi1/trace_ibhdrs.h2
-rw-r--r--drivers/infiniband/hw/hfi1/trace_misc.h2
-rw-r--r--drivers/infiniband/hw/hfi1/trace_mmu.h54
-rw-r--r--drivers/infiniband/hw/hfi1/trace_rc.h2
-rw-r--r--drivers/infiniband/hw/hfi1/trace_rx.h4
-rw-r--r--drivers/infiniband/hw/hfi1/trace_tid.h4
-rw-r--r--drivers/infiniband/hw/hfi1/trace_tx.h6
-rw-r--r--drivers/infiniband/hw/hfi1/uc.c2
-rw-r--r--drivers/infiniband/hw/hfi1/ud.c2
-rw-r--r--drivers/infiniband/hw/hfi1/user_exp_rcv.c263
-rw-r--r--drivers/infiniband/hw/hfi1/user_exp_rcv.h7
-rw-r--r--drivers/infiniband/hw/hfi1/user_pages.c63
-rw-r--r--drivers/infiniband/hw/hfi1/user_sdma.c334
-rw-r--r--drivers/infiniband/hw/hfi1/user_sdma.h25
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.c97
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.h5
-rw-r--r--drivers/infiniband/hw/hfi1/verbs_txreq.c2
-rw-r--r--drivers/infiniband/hw/hfi1/verbs_txreq.h2
-rw-r--r--drivers/infiniband/hw/hfi1/vnic.h2
-rw-r--r--drivers/infiniband/hw/hfi1/vnic_main.c2
-rw-r--r--drivers/infiniband/hw/hfi1/vnic_sdma.c5
-rw-r--r--drivers/infiniband/hw/hns/Kconfig20
-rw-r--r--drivers/infiniband/hw/hns/Makefile13
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_ah.c57
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_alloc.c7
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_bond.c1012
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_bond.h95
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cmd.c19
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cmd.h3
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cq.c123
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_debugfs.c111
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_debugfs.h33
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_device.h247
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hem.c313
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hem.h73
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.c2117
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.h152
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2_dfx.c34
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_main.c475
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_mr.c586
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_pd.c13
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_qp.c433
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_restrack.c221
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_srq.c112
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_trace.h216
-rw-r--r--drivers/infiniband/hw/ionic/Kconfig15
-rw-r--r--drivers/infiniband/hw/ionic/Makefile9
-rw-r--r--drivers/infiniband/hw/ionic/ionic_admin.c1229
-rw-r--r--drivers/infiniband/hw/ionic/ionic_controlpath.c2679
-rw-r--r--drivers/infiniband/hw/ionic/ionic_datapath.c1399
-rw-r--r--drivers/infiniband/hw/ionic/ionic_fw.h1029
-rw-r--r--drivers/infiniband/hw/ionic/ionic_hw_stats.c484
-rw-r--r--drivers/infiniband/hw/ionic/ionic_ibdev.c440
-rw-r--r--drivers/infiniband/hw/ionic/ionic_ibdev.h517
-rw-r--r--drivers/infiniband/hw/ionic/ionic_lif_cfg.c111
-rw-r--r--drivers/infiniband/hw/ionic/ionic_lif_cfg.h66
-rw-r--r--drivers/infiniband/hw/ionic/ionic_pgtbl.c143
-rw-r--r--drivers/infiniband/hw/ionic/ionic_queue.c52
-rw-r--r--drivers/infiniband/hw/ionic/ionic_queue.h234
-rw-r--r--drivers/infiniband/hw/ionic/ionic_res.h154
-rw-r--r--drivers/infiniband/hw/irdma/Kconfig8
-rw-r--r--drivers/infiniband/hw/irdma/Makefile4
-rw-r--r--drivers/infiniband/hw/irdma/cm.c125
-rw-r--r--drivers/infiniband/hw/irdma/cm.h4
-rw-r--r--drivers/infiniband/hw/irdma/ctrl.c1905
-rw-r--r--drivers/infiniband/hw/irdma/defs.h317
-rw-r--r--drivers/infiniband/hw/irdma/hmc.c20
-rw-r--r--drivers/infiniband/hw/irdma/hmc.h21
-rw-r--r--drivers/infiniband/hw/irdma/hw.c461
-rw-r--r--drivers/infiniband/hw/irdma/i40iw_hw.c65
-rw-r--r--drivers/infiniband/hw/irdma/i40iw_hw.h6
-rw-r--r--drivers/infiniband/hw/irdma/i40iw_if.c7
-rw-r--r--drivers/infiniband/hw/irdma/icrdma_hw.c57
-rw-r--r--drivers/infiniband/hw/irdma/icrdma_hw.h8
-rw-r--r--drivers/infiniband/hw/irdma/icrdma_if.c347
-rw-r--r--drivers/infiniband/hw/irdma/ig3rdma_hw.c170
-rw-r--r--drivers/infiniband/hw/irdma/ig3rdma_hw.h32
-rw-r--r--drivers/infiniband/hw/irdma/ig3rdma_if.c236
-rw-r--r--drivers/infiniband/hw/irdma/irdma.h26
-rw-r--r--drivers/infiniband/hw/irdma/main.c326
-rw-r--r--drivers/infiniband/hw/irdma/main.h60
-rw-r--r--drivers/infiniband/hw/irdma/osdep.h14
-rw-r--r--drivers/infiniband/hw/irdma/pble.c48
-rw-r--r--drivers/infiniband/hw/irdma/pble.h4
-rw-r--r--drivers/infiniband/hw/irdma/protos.h15
-rw-r--r--drivers/infiniband/hw/irdma/puda.c47
-rw-r--r--drivers/infiniband/hw/irdma/puda.h11
-rw-r--r--drivers/infiniband/hw/irdma/trace.c2
-rw-r--r--drivers/infiniband/hw/irdma/trace.h2
-rw-r--r--drivers/infiniband/hw/irdma/trace_cm.h2
-rw-r--r--drivers/infiniband/hw/irdma/type.h404
-rw-r--r--drivers/infiniband/hw/irdma/uda.c2
-rw-r--r--drivers/infiniband/hw/irdma/uda.h2
-rw-r--r--drivers/infiniband/hw/irdma/uda_d.h7
-rw-r--r--drivers/infiniband/hw/irdma/uk.c767
-rw-r--r--drivers/infiniband/hw/irdma/user.h313
-rw-r--r--drivers/infiniband/hw/irdma/utils.c514
-rw-r--r--drivers/infiniband/hw/irdma/verbs.c2027
-rw-r--r--drivers/infiniband/hw/irdma/verbs.h116
-rw-r--r--drivers/infiniband/hw/irdma/virtchnl.c618
-rw-r--r--drivers/infiniband/hw/irdma/virtchnl.h176
-rw-r--r--drivers/infiniband/hw/irdma/ws.c2
-rw-r--r--drivers/infiniband/hw/irdma/ws.h2
-rw-r--r--drivers/infiniband/hw/mana/Kconfig10
-rw-r--r--drivers/infiniband/hw/mana/Makefile4
-rw-r--r--drivers/infiniband/hw/mana/ah.c58
-rw-r--r--drivers/infiniband/hw/mana/counters.c179
-rw-r--r--drivers/infiniband/hw/mana/counters.h62
-rw-r--r--drivers/infiniband/hw/mana/cq.c342
-rw-r--r--drivers/infiniband/hw/mana/device.c261
-rw-r--r--drivers/infiniband/hw/mana/main.c1134
-rw-r--r--drivers/infiniband/hw/mana/mana_ib.h738
-rw-r--r--drivers/infiniband/hw/mana/mr.c319
-rw-r--r--drivers/infiniband/hw/mana/qp.c921
-rw-r--r--drivers/infiniband/hw/mana/shadow_queue.h115
-rw-r--r--drivers/infiniband/hw/mana/wq.c92
-rw-r--r--drivers/infiniband/hw/mana/wr.c168
-rw-r--r--drivers/infiniband/hw/mlx4/alias_GUID.c4
-rw-r--r--drivers/infiniband/hw/mlx4/cm.c2
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c9
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c20
-rw-r--r--drivers/infiniband/hw/mlx4/main.c287
-rw-r--r--drivers/infiniband/hw/mlx4/mcg.c8
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h26
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c291
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c162
-rw-r--r--drivers/infiniband/hw/mlx4/sysfs.c2
-rw-r--r--drivers/infiniband/hw/mlx5/Makefile5
-rw-r--r--drivers/infiniband/hw/mlx5/ah.c13
-rw-r--r--drivers/infiniband/hw/mlx5/cmd.c78
-rw-r--r--drivers/infiniband/hw/mlx5/cmd.h7
-rw-r--r--drivers/infiniband/hw/mlx5/cong.c34
-rw-r--r--drivers/infiniband/hw/mlx5/counters.c483
-rw-r--r--drivers/infiniband/hw/mlx5/counters.h2
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c96
-rw-r--r--drivers/infiniband/hw/mlx5/data_direct.c227
-rw-r--r--drivers/infiniband/hw/mlx5/data_direct.h23
-rw-r--r--drivers/infiniband/hw/mlx5/devx.c259
-rw-r--r--drivers/infiniband/hw/mlx5/devx.h9
-rw-r--r--drivers/infiniband/hw/mlx5/dm.c7
-rw-r--r--drivers/infiniband/hw/mlx5/dmah.c54
-rw-r--r--drivers/infiniband/hw/mlx5/dmah.h23
-rw-r--r--drivers/infiniband/hw/mlx5/fs.c1063
-rw-r--r--drivers/infiniband/hw/mlx5/fs.h31
-rw-r--r--drivers/infiniband/hw/mlx5/gsi.c15
-rw-r--r--drivers/infiniband/hw/mlx5/ib_rep.c202
-rw-r--r--drivers/infiniband/hw/mlx5/macsec.c364
-rw-r--r--drivers/infiniband/hw/mlx5/macsec.h29
-rw-r--r--drivers/infiniband/hw/mlx5/mad.c131
-rw-r--r--drivers/infiniband/hw/mlx5/main.c1202
-rw-r--r--drivers/infiniband/hw/mlx5/mem.c200
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h345
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c1308
-rw-r--r--drivers/infiniband/hw/mlx5/odp.c708
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c360
-rw-r--r--drivers/infiniband/hw/mlx5/qp.h17
-rw-r--r--drivers/infiniband/hw/mlx5/qpc.c139
-rw-r--r--drivers/infiniband/hw/mlx5/restrack.c38
-rw-r--r--drivers/infiniband/hw/mlx5/srq.c19
-rw-r--r--drivers/infiniband/hw/mlx5/std_types.c99
-rw-r--r--drivers/infiniband/hw/mlx5/umr.c530
-rw-r--r--drivers/infiniband/hw/mlx5/umr.h18
-rw-r--r--drivers/infiniband/hw/mlx5/wr.c4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_catas.c4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.c6
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_memfree.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c29
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c2
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.c2
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_stats.c22
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c13
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.h5
-rw-r--r--drivers/infiniband/hw/qedr/main.c11
-rw-r--r--drivers/infiniband/hw/qedr/qedr_iw_cm.c3
-rw-r--r--drivers/infiniband/hw/qedr/qedr_roce_cm.c1
-rw-r--r--drivers/infiniband/hw/qedr/verbs.c22
-rw-r--r--drivers/infiniband/hw/qedr/verbs.h5
-rw-r--r--drivers/infiniband/hw/qib/Kconfig17
-rw-r--r--drivers/infiniband/hw/qib/Makefile17
-rw-r--r--drivers/infiniband/hw/qib/qib.h1493
-rw-r--r--drivers/infiniband/hw/qib/qib_6120_regs.h977
-rw-r--r--drivers/infiniband/hw/qib/qib_7220.h149
-rw-r--r--drivers/infiniband/hw/qib/qib_7220_regs.h1496
-rw-r--r--drivers/infiniband/hw/qib/qib_7322_regs.h3163
-rw-r--r--drivers/infiniband/hw/qib/qib_common.h798
-rw-r--r--drivers/infiniband/hw/qib/qib_debugfs.c274
-rw-r--r--drivers/infiniband/hw/qib/qib_debugfs.h45
-rw-r--r--drivers/infiniband/hw/qib/qib_diag.c906
-rw-r--r--drivers/infiniband/hw/qib/qib_driver.c804
-rw-r--r--drivers/infiniband/hw/qib/qib_eeprom.c271
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c2404
-rw-r--r--drivers/infiniband/hw/qib/qib_fs.c548
-rw-r--r--drivers/infiniband/hw/qib/qib_iba6120.c3536
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7220.c4596
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c8475
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c1798
-rw-r--r--drivers/infiniband/hw/qib/qib_intr.c240
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c2449
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.h300
-rw-r--r--drivers/infiniband/hw/qib/qib_pcie.c606
-rw-r--r--drivers/infiniband/hw/qib/qib_pio_copy.c64
-rw-r--r--drivers/infiniband/hw/qib/qib_qp.c454
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.c549
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.h188
-rw-r--r--drivers/infiniband/hw/qib/qib_rc.c2131
-rw-r--r--drivers/infiniband/hw/qib/qib_ruc.c314
-rw-r--r--drivers/infiniband/hw/qib/qib_sd7220.c1445
-rw-r--r--drivers/infiniband/hw/qib/qib_sdma.c999
-rw-r--r--drivers/infiniband/hw/qib/qib_sysfs.c737
-rw-r--r--drivers/infiniband/hw/qib/qib_twsi.c502
-rw-r--r--drivers/infiniband/hw/qib/qib_tx.c569
-rw-r--r--drivers/infiniband/hw/qib/qib_uc.c521
-rw-r--r--drivers/infiniband/hw/qib/qib_ud.c583
-rw-r--r--drivers/infiniband/hw/qib/qib_user_pages.c137
-rw-r--r--drivers/infiniband/hw/qib/qib_user_sdma.c1475
-rw-r--r--drivers/infiniband/hw/qib/qib_user_sdma.h52
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c1705
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.h402
-rw-r--r--drivers/infiniband/hw/qib/qib_wc_ppc64.c62
-rw-r--r--drivers/infiniband/hw/qib/qib_wc_x86_64.c150
-rw-r--r--drivers/infiniband/hw/usnic/usnic_abi.h2
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_main.c89
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_verbs.c8
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_verbs.h3
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.c24
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.h2
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h4
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c5
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c80
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c5
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c8
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c30
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h5
-rw-r--r--drivers/infiniband/sw/rdmavt/ah.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/ah.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/cq.c11
-rw-r--r--drivers/infiniband/sw/rdmavt/cq.h4
-rw-r--r--drivers/infiniband/sw/rdmavt/mad.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/mad.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/mcast.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/mcast.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/mmap.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/mmap.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/mr.c13
-rw-r--r--drivers/infiniband/sw/rdmavt/mr.h3
-rw-r--r--drivers/infiniband/sw/rdmavt/pd.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/pd.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/qp.c36
-rw-r--r--drivers/infiniband/sw/rdmavt/qp.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/rc.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/srq.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/srq.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/trace.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/trace.h4
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_cq.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_mr.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_qp.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_rc.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_rvt.h4
-rw-r--r--drivers/infiniband/sw/rdmavt/trace_tx.h2
-rw-r--r--drivers/infiniband/sw/rdmavt/vt.c8
-rw-r--r--drivers/infiniband/sw/rdmavt/vt.h2
-rw-r--r--drivers/infiniband/sw/rxe/Kconfig5
-rw-r--r--drivers/infiniband/sw/rxe/Makefile2
-rw-r--r--drivers/infiniband/sw/rxe/rxe.c86
-rw-r--r--drivers/infiniband/sw/rxe/rxe.h66
-rw-r--r--drivers/infiniband/sw/rxe/rxe_av.c43
-rw-r--r--drivers/infiniband/sw/rxe/rxe_comp.c224
-rw-r--r--drivers/infiniband/sw/rxe/rxe_cq.c53
-rw-r--r--drivers/infiniband/sw/rxe/rxe_hdr.h50
-rw-r--r--drivers/infiniband/sw/rxe/rxe_hw_counters.c2
-rw-r--r--drivers/infiniband/sw/rxe/rxe_hw_counters.h2
-rw-r--r--drivers/infiniband/sw/rxe/rxe_icrc.c52
-rw-r--r--drivers/infiniband/sw/rxe/rxe_loc.h101
-rw-r--r--drivers/infiniband/sw/rxe/rxe_mcast.c22
-rw-r--r--drivers/infiniband/sw/rxe/rxe_mmap.c6
-rw-r--r--drivers/infiniband/sw/rxe/rxe_mr.c662
-rw-r--r--drivers/infiniband/sw/rxe/rxe_mw.c52
-rw-r--r--drivers/infiniband/sw/rxe/rxe_net.c193
-rw-r--r--drivers/infiniband/sw/rxe/rxe_odp.c577
-rw-r--r--drivers/infiniband/sw/rxe/rxe_opcode.c35
-rw-r--r--drivers/infiniband/sw/rxe/rxe_opcode.h20
-rw-r--r--drivers/infiniband/sw/rxe/rxe_param.h20
-rw-r--r--drivers/infiniband/sw/rxe/rxe_pool.c83
-rw-r--r--drivers/infiniband/sw/rxe/rxe_pool.h3
-rw-r--r--drivers/infiniband/sw/rxe/rxe_qp.c579
-rw-r--r--drivers/infiniband/sw/rxe/rxe_queue.c17
-rw-r--r--drivers/infiniband/sw/rxe/rxe_queue.h108
-rw-r--r--drivers/infiniband/sw/rxe/rxe_recv.c116
-rw-r--r--drivers/infiniband/sw/rxe/rxe_req.c237
-rw-r--r--drivers/infiniband/sw/rxe/rxe_resp.c660
-rw-r--r--drivers/infiniband/sw/rxe/rxe_srq.c83
-rw-r--r--drivers/infiniband/sw/rxe/rxe_task.c313
-rw-r--r--drivers/infiniband/sw/rxe/rxe_task.h47
-rw-r--r--drivers/infiniband/sw/rxe/rxe_verbs.c1182
-rw-r--r--drivers/infiniband/sw/rxe/rxe_verbs.h131
-rw-r--r--drivers/infiniband/sw/siw/Kconfig4
-rw-r--r--drivers/infiniband/sw/siw/iwarp.h2
-rw-r--r--drivers/infiniband/sw/siw/siw.h75
-rw-r--r--drivers/infiniband/sw/siw/siw_cm.c251
-rw-r--r--drivers/infiniband/sw/siw/siw_cm.h2
-rw-r--r--drivers/infiniband/sw/siw/siw_cq.c28
-rw-r--r--drivers/infiniband/sw/siw/siw_main.c178
-rw-r--r--drivers/infiniband/sw/siw/siw_mem.c153
-rw-r--r--drivers/infiniband/sw/siw/siw_mem.h8
-rw-r--r--drivers/infiniband/sw/siw/siw_qp.c67
-rw-r--r--drivers/infiniband/sw/siw/siw_qp_rx.c136
-rw-r--r--drivers/infiniband/sw/siw/siw_qp_tx.c199
-rw-r--r--drivers/infiniband/sw/siw/siw_verbs.c195
-rw-r--r--drivers/infiniband/sw/siw/siw_verbs.h7
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h21
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c6
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ethtool.c15
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c95
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c239
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c10
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_netlink.c16
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_verbs.c8
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_vlan.c27
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c22
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h11
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c22
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c8
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c74
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c28
-rw-r--r--drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c8
-rw-r--r--drivers/infiniband/ulp/rtrs/Makefile10
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-clt-sysfs.c2
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-clt-trace.c15
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-clt-trace.h86
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-clt.c230
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-clt.h3
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-pri.h10
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c12
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv-trace.c16
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv-trace.h88
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv.c191
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv.h9
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs.c31
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs.h3
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c198
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h13
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c179
-rw-r--r--drivers/input/Kconfig18
-rw-r--r--drivers/input/Makefile4
-rw-r--r--drivers/input/evbug.c100
-rw-r--r--drivers/input/evdev.c34
-rw-r--r--drivers/input/ff-core.c99
-rw-r--r--drivers/input/ff-memless.c28
-rw-r--r--drivers/input/gameport/Kconfig4
-rw-r--r--drivers/input/gameport/emu10k1-gp.c5
-rw-r--r--drivers/input/gameport/fm801-gp.c2
-rw-r--r--drivers/input/gameport/gameport.c52
-rw-r--r--drivers/input/gameport/lightning.c3
-rw-r--r--drivers/input/gameport/ns558.c7
-rw-r--r--drivers/input/input-compat.c30
-rw-r--r--drivers/input/input-compat.h3
-rw-r--r--drivers/input/input-leds.c10
-rw-r--r--drivers/input/input-mt.c51
-rw-r--r--drivers/input/input-poller.c5
-rw-r--r--drivers/input/input.c823
-rw-r--r--drivers/input/joydev.c3
-rw-r--r--drivers/input/joystick/Kconfig25
-rw-r--r--drivers/input/joystick/Makefile1
-rw-r--r--drivers/input/joystick/a3d.c5
-rw-r--r--drivers/input/joystick/adafruit-seesaw.c330
-rw-r--r--drivers/input/joystick/adc-joystick.c190
-rw-r--r--drivers/input/joystick/adi.c5
-rw-r--r--drivers/input/joystick/amijoy.c3
-rw-r--r--drivers/input/joystick/analog.c6
-rw-r--r--drivers/input/joystick/as5011.c35
-rw-r--r--drivers/input/joystick/cobra.c5
-rw-r--r--drivers/input/joystick/db9.c40
-rw-r--r--drivers/input/joystick/fsia6b.c2
-rw-r--r--drivers/input/joystick/gamecon.c32
-rw-r--r--drivers/input/joystick/gf2k.c5
-rw-r--r--drivers/input/joystick/grip.c6
-rw-r--r--drivers/input/joystick/grip_mp.c3
-rw-r--r--drivers/input/joystick/guillemot.c5
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c48
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c11
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c60
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c36
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c13
-rw-r--r--drivers/input/joystick/interact.c5
-rw-r--r--drivers/input/joystick/joydump.c3
-rw-r--r--drivers/input/joystick/magellan.c7
-rw-r--r--drivers/input/joystick/maplecontrol.c2
-rw-r--r--drivers/input/joystick/n64joy.c39
-rw-r--r--drivers/input/joystick/psxpad-spi.c19
-rw-r--r--drivers/input/joystick/pxrc.c42
-rw-r--r--drivers/input/joystick/qwiic-joystick.c6
-rw-r--r--drivers/input/joystick/sidewinder.c8
-rw-r--r--drivers/input/joystick/spaceball.c7
-rw-r--r--drivers/input/joystick/spaceorb.c5
-rw-r--r--drivers/input/joystick/stinger.c5
-rw-r--r--drivers/input/joystick/tmdc.c6
-rw-r--r--drivers/input/joystick/turbografx.c32
-rw-r--r--drivers/input/joystick/twidjoy.c5
-rw-r--r--drivers/input/joystick/walkera0701.c17
-rw-r--r--drivers/input/joystick/warrior.c5
-rw-r--r--drivers/input/joystick/xpad.c815
-rw-r--r--drivers/input/joystick/zhenhua.c5
-rw-r--r--drivers/input/keyboard/Kconfig124
-rw-r--r--drivers/input/keyboard/Makefile10
-rw-r--r--drivers/input/keyboard/adc-keys.c5
-rw-r--r--drivers/input/keyboard/adp5520-keys.c4
-rw-r--r--drivers/input/keyboard/adp5585-keys.c371
-rw-r--r--drivers/input/keyboard/adp5588-keys.c779
-rw-r--r--drivers/input/keyboard/adp5589-keys.c1065
-rw-r--r--drivers/input/keyboard/amikbd.c34
-rw-r--r--drivers/input/keyboard/applespi.c89
-rw-r--r--drivers/input/keyboard/atakbd.c3
-rw-r--r--drivers/input/keyboard/atkbd.c233
-rw-r--r--drivers/input/keyboard/bcm-keypad.c26
-rw-r--r--drivers/input/keyboard/cap11xx.c361
-rw-r--r--drivers/input/keyboard/clps711x-keypad.c13
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c51
-rw-r--r--drivers/input/keyboard/cypress-sf.c16
-rw-r--r--drivers/input/keyboard/davinci_keyscan.c315
-rw-r--r--drivers/input/keyboard/dlink-dir685-touchkeys.c12
-rw-r--r--drivers/input/keyboard/ep93xx_keypad.c97
-rw-r--r--drivers/input/keyboard/gpio_keys.c185
-rw-r--r--drivers/input/keyboard/gpio_keys_polled.c34
-rw-r--r--drivers/input/keyboard/hilkbd.c4
-rw-r--r--drivers/input/keyboard/imx-sm-bbm-key.c225
-rw-r--r--drivers/input/keyboard/imx_keypad.c33
-rw-r--r--drivers/input/keyboard/imx_sc_key.c2
-rw-r--r--drivers/input/keyboard/ipaq-micro-keys.c30
-rw-r--r--drivers/input/keyboard/iqs62x-keys.c8
-rw-r--r--drivers/input/keyboard/lkkbd.c13
-rw-r--r--drivers/input/keyboard/lm8323.c158
-rw-r--r--drivers/input/keyboard/lm8333.c59
-rw-r--r--drivers/input/keyboard/locomokbd.c11
-rw-r--r--drivers/input/keyboard/lpc32xx-keys.c38
-rw-r--r--drivers/input/keyboard/maple_keyb.c11
-rw-r--r--drivers/input/keyboard/matrix_keypad.c494
-rw-r--r--drivers/input/keyboard/max7359_keypad.c11
-rw-r--r--drivers/input/keyboard/max7360-keypad.c308
-rw-r--r--drivers/input/keyboard/mcs_touchkey.c277
-rw-r--r--drivers/input/keyboard/mpr121_touchkey.c58
-rw-r--r--drivers/input/keyboard/mt6779-keypad.c59
-rw-r--r--drivers/input/keyboard/mtk-pmic-keys.c91
-rw-r--r--drivers/input/keyboard/newtonkbd.c5
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c437
-rw-r--r--drivers/input/keyboard/nspire-keypad.c3
-rw-r--r--drivers/input/keyboard/omap-keypad.c62
-rw-r--r--drivers/input/keyboard/omap4-keypad.c41
-rw-r--r--drivers/input/keyboard/opencores-kbd.c9
-rw-r--r--drivers/input/keyboard/pinephone-keyboard.c450
-rw-r--r--drivers/input/keyboard/pmic8xxx-keypad.c16
-rw-r--r--drivers/input/keyboard/pxa27x_keypad.c563
-rw-r--r--drivers/input/keyboard/pxa930_rotary.c195
-rw-r--r--drivers/input/keyboard/qt1050.c36
-rw-r--r--drivers/input/keyboard/qt1070.c61
-rw-r--r--drivers/input/keyboard/qt2160.c137
-rw-r--r--drivers/input/keyboard/samsung-keypad.c154
-rw-r--r--drivers/input/keyboard/sh_keysc.c12
-rw-r--r--drivers/input/keyboard/snvs_pwrkey.c56
-rw-r--r--drivers/input/keyboard/spear-keyboard.c111
-rw-r--r--drivers/input/keyboard/st-keyscan.c42
-rw-r--r--drivers/input/keyboard/stmpe-keypad.c6
-rw-r--r--drivers/input/keyboard/stowaway.c5
-rw-r--r--drivers/input/keyboard/sun4i-lradc-keys.c14
-rw-r--r--drivers/input/keyboard/sunkbd.c10
-rw-r--r--drivers/input/keyboard/tc3589x-keypad.c13
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c377
-rw-r--r--drivers/input/keyboard/tca8418_keypad.c16
-rw-r--r--drivers/input/keyboard/tegra-kbc.c144
-rw-r--r--drivers/input/keyboard/tm2-touchkey.c20
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c35
-rw-r--r--drivers/input/keyboard/xtkbd.c5
-rw-r--r--drivers/input/matrix-keymap.c26
-rw-r--r--drivers/input/misc/88pm80x_onkey.c19
-rw-r--r--drivers/input/misc/88pm860x_onkey.c9
-rw-r--r--drivers/input/misc/88pm886-onkey.c98
-rw-r--r--drivers/input/misc/Kconfig153
-rw-r--r--drivers/input/misc/Makefile13
-rw-r--r--drivers/input/misc/ad714x-i2c.c29
-rw-r--r--drivers/input/misc/ad714x-spi.c14
-rw-r--r--drivers/input/misc/ad714x.c25
-rw-r--r--drivers/input/misc/ad714x.h4
-rw-r--r--drivers/input/misc/adxl34x-i2c.c41
-rw-r--r--drivers/input/misc/adxl34x-spi.c34
-rw-r--r--drivers/input/misc/adxl34x.c171
-rw-r--r--drivers/input/misc/adxl34x.h6
-rw-r--r--drivers/input/misc/apanel.c5
-rw-r--r--drivers/input/misc/arizona-haptics.c14
-rw-r--r--drivers/input/misc/ati_remote2.c107
-rw-r--r--drivers/input/misc/atlas_btns.c5
-rw-r--r--drivers/input/misc/atmel_captouch.c9
-rw-r--r--drivers/input/misc/aw86927.c846
-rw-r--r--drivers/input/misc/axp20x-pek.c23
-rw-r--r--drivers/input/misc/bma150.c13
-rw-r--r--drivers/input/misc/cm109.c167
-rw-r--r--drivers/input/misc/cma3000_d0x.c21
-rw-r--r--drivers/input/misc/cma3000_d0x_i2c.c17
-rw-r--r--drivers/input/misc/cpcap-pwrbutton.c12
-rw-r--r--drivers/input/misc/cs40l50-vibra.c558
-rw-r--r--drivers/input/misc/da7280.c42
-rw-r--r--drivers/input/misc/da9052_onkey.c6
-rw-r--r--drivers/input/misc/da9055_onkey.c6
-rw-r--r--drivers/input/misc/da9063_onkey.c78
-rw-r--r--drivers/input/misc/dm355evm_keys.c238
-rw-r--r--drivers/input/misc/drv260x.c119
-rw-r--r--drivers/input/misc/drv2665.c59
-rw-r--r--drivers/input/misc/drv2667.c59
-rw-r--r--drivers/input/misc/e3x0-button.c10
-rw-r--r--drivers/input/misc/gpio-beeper.c2
-rw-r--r--drivers/input/misc/gpio-vibra.c32
-rw-r--r--drivers/input/misc/hisi_powerkey.c2
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c2
-rw-r--r--drivers/input/misc/ibm-panel.c196
-rw-r--r--drivers/input/misc/ideapad_slidebar.c32
-rw-r--r--drivers/input/misc/ims-pcu.c221
-rw-r--r--drivers/input/misc/iqs269a.c722
-rw-r--r--drivers/input/misc/iqs626a.c166
-rw-r--r--drivers/input/misc/iqs7222.c1170
-rw-r--r--drivers/input/misc/keyspan_remote.c2
-rw-r--r--drivers/input/misc/kxtj9.c58
-rw-r--r--drivers/input/misc/m68kspkr.c4
-rw-r--r--drivers/input/misc/max7360-rotary.c192
-rw-r--r--drivers/input/misc/max77693-haptic.c82
-rw-r--r--drivers/input/misc/max8925_onkey.c9
-rw-r--r--drivers/input/misc/max8997_haptic.c125
-rw-r--r--drivers/input/misc/mc13783-pwrbutton.c5
-rw-r--r--drivers/input/misc/mma8450.c25
-rw-r--r--drivers/input/misc/nxp-bbnsm-pwrkey.c239
-rw-r--r--drivers/input/misc/palmas-pwrbutton.c14
-rw-r--r--drivers/input/misc/pcap_keys.c6
-rw-r--r--drivers/input/misc/pcf50633-input.c115
-rw-r--r--drivers/input/misc/pcf8574_keypad.c24
-rw-r--r--drivers/input/misc/pcspkr.c4
-rw-r--r--drivers/input/misc/pf1550-onkey.c197
-rw-r--r--drivers/input/misc/pm8941-pwrkey.c50
-rw-r--r--drivers/input/misc/pm8xxx-vibrator.c99
-rw-r--r--drivers/input/misc/pmic8xxx-pwrkey.c9
-rw-r--r--drivers/input/misc/powermate.c14
-rw-r--r--drivers/input/misc/pwm-beeper.c45
-rw-r--r--drivers/input/misc/pwm-vibra.c68
-rw-r--r--drivers/input/misc/qnap-mcu-input.c153
-rw-r--r--drivers/input/misc/regulator-haptic.c35
-rw-r--r--drivers/input/misc/rotary_encoder.c42
-rw-r--r--drivers/input/misc/rt5120-pwrkey.c120
-rw-r--r--drivers/input/misc/sgi_btns.c1
-rw-r--r--drivers/input/misc/soc_button_array.c56
-rw-r--r--drivers/input/misc/sparcspkr.c88
-rw-r--r--drivers/input/misc/stpmic1_onkey.c12
-rw-r--r--drivers/input/misc/tps65219-pwrbutton.c150
-rw-r--r--drivers/input/misc/tps6594-pwrbutton.c126
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c5
-rw-r--r--drivers/input/misc/twl4030-vibra.c34
-rw-r--r--drivers/input/misc/twl6040-vibra.c15
-rw-r--r--drivers/input/misc/uinput.c52
-rw-r--r--drivers/input/misc/wistron_btns.c18
-rw-r--r--drivers/input/misc/wm831x-on.c4
-rw-r--r--drivers/input/misc/xen-kbdfront.c5
-rw-r--r--drivers/input/misc/yealink.c89
-rw-r--r--drivers/input/mouse/Kconfig18
-rw-r--r--drivers/input/mouse/Makefile2
-rw-r--r--drivers/input/mouse/alps.c86
-rw-r--r--drivers/input/mouse/amimouse.c11
-rw-r--r--drivers/input/mouse/appletouch.c2
-rw-r--r--drivers/input/mouse/bcm5974.c37
-rw-r--r--drivers/input/mouse/byd.c9
-rw-r--r--drivers/input/mouse/cyapa.c73
-rw-r--r--drivers/input/mouse/cyapa_gen3.c4
-rw-r--r--drivers/input/mouse/cyapa_gen5.c6
-rw-r--r--drivers/input/mouse/cyapa_gen6.c22
-rw-r--r--drivers/input/mouse/cypress_ps2.c218
-rw-r--r--drivers/input/mouse/cypress_ps2.h6
-rw-r--r--drivers/input/mouse/elan_i2c_core.c289
-rw-r--r--drivers/input/mouse/elan_i2c_i2c.c16
-rw-r--r--drivers/input/mouse/elantech.c43
-rw-r--r--drivers/input/mouse/focaltech.c11
-rw-r--r--drivers/input/mouse/hgpk.c6
-rw-r--r--drivers/input/mouse/inport.c3
-rw-r--r--drivers/input/mouse/lifebook.c6
-rw-r--r--drivers/input/mouse/logibm.c3
-rw-r--r--drivers/input/mouse/maplemouse.c2
-rw-r--r--drivers/input/mouse/navpoint.c362
-rw-r--r--drivers/input/mouse/pc110pad.c3
-rw-r--r--drivers/input/mouse/psmouse-base.c112
-rw-r--r--drivers/input/mouse/psmouse-smbus.c28
-rw-r--r--drivers/input/mouse/psmouse.h2
-rw-r--r--drivers/input/mouse/pxa930_trkball.c250
-rw-r--r--drivers/input/mouse/sentelic.c2
-rw-r--r--drivers/input/mouse/sermouse.c5
-rw-r--r--drivers/input/mouse/synaptics.c91
-rw-r--r--drivers/input/mouse/synaptics.h3
-rw-r--r--drivers/input/mouse/synaptics_i2c.c23
-rw-r--r--drivers/input/mouse/synaptics_usb.c2
-rw-r--r--drivers/input/mouse/trackpoint.c2
-rw-r--r--drivers/input/mouse/vmmouse.c76
-rw-r--r--drivers/input/mouse/vsxxxaa.c9
-rw-r--r--drivers/input/rmi4/Kconfig15
-rw-r--r--drivers/input/rmi4/Makefile2
-rw-r--r--drivers/input/rmi4/rmi_2d_sensor.c1
-rw-r--r--drivers/input/rmi4/rmi_2d_sensor.h3
-rw-r--r--drivers/input/rmi4/rmi_bus.c19
-rw-r--r--drivers/input/rmi4/rmi_bus.h4
-rw-r--r--drivers/input/rmi4/rmi_driver.c15
-rw-r--r--drivers/input/rmi4/rmi_driver.h4
-rw-r--r--drivers/input/rmi4/rmi_f01.c15
-rw-r--r--drivers/input/rmi4/rmi_f03.c6
-rw-r--r--drivers/input/rmi4/rmi_f12.c43
-rw-r--r--drivers/input/rmi4/rmi_f1a.c143
-rw-r--r--drivers/input/rmi4/rmi_f21.c179
-rw-r--r--drivers/input/rmi4/rmi_f34.c194
-rw-r--r--drivers/input/rmi4/rmi_f34.h17
-rw-r--r--drivers/input/rmi4/rmi_f34v7.c351
-rw-r--r--drivers/input/rmi4/rmi_f54.c10
-rw-r--r--drivers/input/rmi4/rmi_i2c.c16
-rw-r--r--drivers/input/rmi4/rmi_smbus.c74
-rw-r--r--drivers/input/rmi4/rmi_spi.c15
-rw-r--r--drivers/input/serio/Kconfig5
-rw-r--r--drivers/input/serio/altera_ps2.c14
-rw-r--r--drivers/input/serio/ambakmi.c15
-rw-r--r--drivers/input/serio/ams_delta_serio.c8
-rw-r--r--drivers/input/serio/apbps2.c14
-rw-r--r--drivers/input/serio/arc_ps2.c10
-rw-r--r--drivers/input/serio/ct82c710.c11
-rw-r--r--drivers/input/serio/gscps2.c128
-rw-r--r--drivers/input/serio/hil_mlc.c3
-rw-r--r--drivers/input/serio/hp_sdc.c3
-rw-r--r--drivers/input/serio/hyperv-keyboard.c50
-rw-r--r--drivers/input/serio/i8042-acpipnpio.h1823
-rw-r--r--drivers/input/serio/i8042-io.h5
-rw-r--r--drivers/input/serio/i8042-sparcio.h25
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h1668
-rw-r--r--drivers/input/serio/i8042.c381
-rw-r--r--drivers/input/serio/i8042.h4
-rw-r--r--drivers/input/serio/ioc3kbd.c11
-rw-r--r--drivers/input/serio/libps2.c327
-rw-r--r--drivers/input/serio/maceps2.c6
-rw-r--r--drivers/input/serio/olpc_apsp.c20
-rw-r--r--drivers/input/serio/parkbd.c5
-rw-r--r--drivers/input/serio/pcips2.c8
-rw-r--r--drivers/input/serio/ps2-gpio.c23
-rw-r--r--drivers/input/serio/ps2mult.c29
-rw-r--r--drivers/input/serio/q40kbd.c25
-rw-r--r--drivers/input/serio/rpckbd.c21
-rw-r--r--drivers/input/serio/sa1111ps2.c16
-rw-r--r--drivers/input/serio/serio.c181
-rw-r--r--drivers/input/serio/serio_raw.c127
-rw-r--r--drivers/input/serio/serport.c51
-rw-r--r--drivers/input/serio/sun4i-ps2.c20
-rw-r--r--drivers/input/serio/userio.c142
-rw-r--r--drivers/input/serio/xilinx_ps2.c30
-rw-r--r--drivers/input/sparse-keymap.c4
-rw-r--r--drivers/input/tablet/acecad.c7
-rw-r--r--drivers/input/tablet/aiptek.c24
-rw-r--r--drivers/input/tablet/hanwang.c7
-rw-r--r--drivers/input/tablet/kbtab.c4
-rw-r--r--drivers/input/tablet/pegasus_notetaker.c103
-rw-r--r--drivers/input/tablet/wacom_serial4.c2
-rw-r--r--drivers/input/tests/.kunitconfig3
-rw-r--r--drivers/input/tests/Makefile3
-rw-r--r--drivers/input/tests/input_test.c183
-rw-r--r--drivers/input/touch-overlay.c278
-rw-r--r--drivers/input/touchscreen.c1
-rw-r--r--drivers/input/touchscreen/88pm860x-ts.c20
-rw-r--r--drivers/input/touchscreen/Kconfig203
-rw-r--r--drivers/input/touchscreen/Makefile20
-rw-r--r--drivers/input/touchscreen/ad7877.c22
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c14
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c7
-rw-r--r--drivers/input/touchscreen/ad7879.c24
-rw-r--r--drivers/input/touchscreen/ad7879.h3
-rw-r--r--drivers/input/touchscreen/ads7846.c227
-rw-r--r--drivers/input/touchscreen/apple_z2.c477
-rw-r--r--drivers/input/touchscreen/ar1021_i2c.c16
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c173
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c170
-rw-r--r--drivers/input/touchscreen/bcm_iproc_tsc.c4
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c89
-rw-r--r--drivers/input/touchscreen/bu21029_ts.c69
-rw-r--r--drivers/input/touchscreen/chipone_icn8318.c17
-rw-r--r--drivers/input/touchscreen/chipone_icn8505.c43
-rw-r--r--drivers/input/touchscreen/colibri-vf50-ts.c10
-rw-r--r--drivers/input/touchscreen/cy8ctma140.c24
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c12
-rw-r--r--drivers/input/touchscreen/cyttsp4_core.c2180
-rw-r--r--drivers/input/touchscreen/cyttsp4_core.h448
-rw-r--r--drivers/input/touchscreen/cyttsp4_i2c.c75
-rw-r--r--drivers/input/touchscreen/cyttsp4_spi.c187
-rw-r--r--drivers/input/touchscreen/cyttsp5.c981
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c47
-rw-r--r--drivers/input/touchscreen/cyttsp_core.h5
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c.c62
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c_common.c85
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c2
-rw-r--r--drivers/input/touchscreen/da9052_tsi.c6
-rw-r--r--drivers/input/touchscreen/dynapro.c2
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c617
-rw-r--r--drivers/input/touchscreen/eeti_ts.c15
-rw-r--r--drivers/input/touchscreen/egalax_ts.c66
-rw-r--r--drivers/input/touchscreen/egalax_ts_serial.c2
-rw-r--r--drivers/input/touchscreen/ektf2127.c57
-rw-r--r--drivers/input/touchscreen/elants_i2c.c79
-rw-r--r--drivers/input/touchscreen/elo.c10
-rw-r--r--drivers/input/touchscreen/exc3000.c54
-rw-r--r--drivers/input/touchscreen/fsl-imx25-tcq.c3
-rw-r--r--drivers/input/touchscreen/fujitsu_ts.c2
-rw-r--r--drivers/input/touchscreen/goodix.c185
-rw-r--r--drivers/input/touchscreen/goodix.h1
-rw-r--r--drivers/input/touchscreen/goodix_berlin.h39
-rw-r--r--drivers/input/touchscreen/goodix_berlin_core.c802
-rw-r--r--drivers/input/touchscreen/goodix_berlin_i2c.c84
-rw-r--r--drivers/input/touchscreen/goodix_berlin_spi.c203
-rw-r--r--drivers/input/touchscreen/gunze.c5
-rw-r--r--drivers/input/touchscreen/hampshire.c2
-rw-r--r--drivers/input/touchscreen/hideep.c67
-rw-r--r--drivers/input/touchscreen/himax_hx83112b.c437
-rw-r--r--drivers/input/touchscreen/himax_hx852x.c503
-rw-r--r--drivers/input/touchscreen/hycon-hy46xx.c19
-rw-r--r--drivers/input/touchscreen/hynitron-cst816x.c253
-rw-r--r--drivers/input/touchscreen/hynitron_cstxxx.c498
-rw-r--r--drivers/input/touchscreen/ili210x.c192
-rw-r--r--drivers/input/touchscreen/ilitek_ts_i2c.c64
-rw-r--r--drivers/input/touchscreen/imagis.c156
-rw-r--r--drivers/input/touchscreen/imx6ul_tsc.c131
-rw-r--r--drivers/input/touchscreen/inexio.c2
-rw-r--r--drivers/input/touchscreen/ipaq-micro-ts.c11
-rw-r--r--drivers/input/touchscreen/iqs5xx.c37
-rw-r--r--drivers/input/touchscreen/iqs7211.c2557
-rw-r--r--drivers/input/touchscreen/lpc32xx_ts.c98
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c14
-rw-r--r--drivers/input/touchscreen/max11801_ts.c5
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c8
-rw-r--r--drivers/input/touchscreen/mcs5000_ts.c288
-rw-r--r--drivers/input/touchscreen/melfas_mip4.c75
-rw-r--r--drivers/input/touchscreen/migor_ts.c17
-rw-r--r--drivers/input/touchscreen/mms114.c102
-rw-r--r--drivers/input/touchscreen/msg2638.c207
-rw-r--r--drivers/input/touchscreen/mtouch.c2
-rw-r--r--drivers/input/touchscreen/novatek-nvt-ts.c354
-rw-r--r--drivers/input/touchscreen/pcap_ts.c4
-rw-r--r--drivers/input/touchscreen/penmount.c2
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c58
-rw-r--r--drivers/input/touchscreen/raspberrypi-ts.c7
-rw-r--r--drivers/input/touchscreen/raydium_i2c_ts.c78
-rw-r--r--drivers/input/touchscreen/resistive-adc-touch.c8
-rw-r--r--drivers/input/touchscreen/rohm_bu21023.c116
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c464
-rw-r--r--drivers/input/touchscreen/s6sy761.c38
-rw-r--r--drivers/input/touchscreen/silead.c53
-rw-r--r--drivers/input/touchscreen/sis_i2c.c29
-rw-r--r--drivers/input/touchscreen/st1232.c49
-rw-r--r--drivers/input/touchscreen/stmfts.c35
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c15
-rw-r--r--drivers/input/touchscreen/sun4i-ts.c20
-rw-r--r--drivers/input/touchscreen/sur40.c19
-rw-r--r--drivers/input/touchscreen/surface3_spi.c27
-rw-r--r--drivers/input/touchscreen/sx8654.c18
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c15
-rw-r--r--drivers/input/touchscreen/touchit213.c2
-rw-r--r--drivers/input/touchscreen/touchright.c2
-rw-r--r--drivers/input/touchscreen/touchwin.c2
-rw-r--r--drivers/input/touchscreen/tps6507x-ts.c2
-rw-r--r--drivers/input/touchscreen/ts4800-ts.c5
-rw-r--r--drivers/input/touchscreen/tsc2004.c20
-rw-r--r--drivers/input/touchscreen/tsc2005.c13
-rw-r--r--drivers/input/touchscreen/tsc2007.h2
-rw-r--r--drivers/input/touchscreen/tsc2007_core.c65
-rw-r--r--drivers/input/touchscreen/tsc200x-core.c275
-rw-r--r--drivers/input/touchscreen/tsc200x-core.h2
-rw-r--r--drivers/input/touchscreen/tsc40.c2
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c458
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c973
-rw-r--r--drivers/input/touchscreen/wacom_i2c.c17
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c37
-rw-r--r--drivers/input/touchscreen/wdt87xx_i2c.c37
-rw-r--r--drivers/input/touchscreen/wm831x-ts.c4
-rw-r--r--drivers/input/touchscreen/wm9705.c1
-rw-r--r--drivers/input/touchscreen/wm9712.c1
-rw-r--r--drivers/input/touchscreen/wm9713.c1
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c19
-rw-r--r--drivers/input/touchscreen/zet6223.c9
-rw-r--r--drivers/input/touchscreen/zforce_ts.c503
-rw-r--r--drivers/input/touchscreen/zinitix.c156
-rw-r--r--drivers/input/touchscreen/zylonite-wm97xx.c220
-rw-r--r--drivers/input/vivaldi-fmap.c7
-rw-r--r--drivers/interconnect/Kconfig7
-rw-r--r--drivers/interconnect/Makefile5
-rw-r--r--drivers/interconnect/core.c393
-rw-r--r--drivers/interconnect/debugfs-client.c173
-rw-r--r--drivers/interconnect/icc-clk.c203
-rw-r--r--drivers/interconnect/imx/imx.c23
-rw-r--r--drivers/interconnect/imx/imx.h11
-rw-r--r--drivers/interconnect/imx/imx8mm.c8
-rw-r--r--drivers/interconnect/imx/imx8mn.c8
-rw-r--r--drivers/interconnect/imx/imx8mp.c9
-rw-r--r--drivers/interconnect/imx/imx8mq.c8
-rw-r--r--drivers/interconnect/internal.h5
-rw-r--r--drivers/interconnect/mediatek/Kconfig29
-rw-r--r--drivers/interconnect/mediatek/Makefile5
-rw-r--r--drivers/interconnect/mediatek/icc-emi.c153
-rw-r--r--drivers/interconnect/mediatek/icc-emi.h40
-rw-r--r--drivers/interconnect/mediatek/mt8183.c143
-rw-r--r--drivers/interconnect/mediatek/mt8195.c339
-rw-r--r--drivers/interconnect/qcom/Kconfig186
-rw-r--r--drivers/interconnect/qcom/Makefile42
-rw-r--r--drivers/interconnect/qcom/bcm-voter.c38
-rw-r--r--drivers/interconnect/qcom/bcm-voter.h8
-rw-r--r--drivers/interconnect/qcom/glymur.c2522
-rw-r--r--drivers/interconnect/qcom/icc-common.c7
-rw-r--r--drivers/interconnect/qcom/icc-common.h3
-rw-r--r--drivers/interconnect/qcom/icc-rpm-clocks.c83
-rw-r--r--drivers/interconnect/qcom/icc-rpm.c422
-rw-r--r--drivers/interconnect/qcom/icc-rpm.h96
-rw-r--r--drivers/interconnect/qcom/icc-rpmh.c151
-rw-r--r--drivers/interconnect/qcom/icc-rpmh.h58
-rw-r--r--drivers/interconnect/qcom/kaanapali.c1855
-rw-r--r--drivers/interconnect/qcom/milos.c1919
-rw-r--r--drivers/interconnect/qcom/msm8909.c1329
-rw-r--r--drivers/interconnect/qcom/msm8916.c7
-rw-r--r--drivers/interconnect/qcom/msm8937.c1350
-rw-r--r--drivers/interconnect/qcom/msm8939.c8
-rw-r--r--drivers/interconnect/qcom/msm8953.c1323
-rw-r--r--drivers/interconnect/qcom/msm8974.c33
-rw-r--r--drivers/interconnect/qcom/msm8976.c1440
-rw-r--r--drivers/interconnect/qcom/msm8996.c65
-rw-r--r--drivers/interconnect/qcom/osm-l3.c183
-rw-r--r--drivers/interconnect/qcom/qcm2290.c35
-rw-r--r--drivers/interconnect/qcom/qcs404.c128
-rw-r--r--drivers/interconnect/qcom/qcs615.c1472
-rw-r--r--drivers/interconnect/qcom/qcs8300.c2001
-rw-r--r--drivers/interconnect/qcom/qdu1000.c1023
-rw-r--r--drivers/interconnect/qcom/sa8775p.c2710
-rw-r--r--drivers/interconnect/qcom/sar2130p.c1758
-rw-r--r--drivers/interconnect/qcom/sc7180.c1611
-rw-r--r--drivers/interconnect/qcom/sc7180.h151
-rw-r--r--drivers/interconnect/qcom/sc7280.c898
-rw-r--r--drivers/interconnect/qcom/sc7280.h156
-rw-r--r--drivers/interconnect/qcom/sc8180x.c699
-rw-r--r--drivers/interconnect/qcom/sc8180x.h181
-rw-r--r--drivers/interconnect/qcom/sc8280xp.c855
-rw-r--r--drivers/interconnect/qcom/sc8280xp.h209
-rw-r--r--drivers/interconnect/qcom/sdm660.c30
-rw-r--r--drivers/interconnect/qcom/sdm670.c1532
-rw-r--r--drivers/interconnect/qcom/sdm845.c1643
-rw-r--r--drivers/interconnect/qcom/sdm845.h142
-rw-r--r--drivers/interconnect/qcom/sdx55.c839
-rw-r--r--drivers/interconnect/qcom/sdx55.h70
-rw-r--r--drivers/interconnect/qcom/sdx65.c826
-rw-r--r--drivers/interconnect/qcom/sdx65.h65
-rw-r--r--drivers/interconnect/qcom/sdx75.c1040
-rw-r--r--drivers/interconnect/qcom/sm6115.c1426
-rw-r--r--drivers/interconnect/qcom/sm6350.c1817
-rw-r--r--drivers/interconnect/qcom/sm6350.h139
-rw-r--r--drivers/interconnect/qcom/sm7150.c1739
-rw-r--r--drivers/interconnect/qcom/sm8150.c1699
-rw-r--r--drivers/interconnect/qcom/sm8150.h154
-rw-r--r--drivers/interconnect/qcom/sm8250.c1835
-rw-r--r--drivers/interconnect/qcom/sm8250.h164
-rw-r--r--drivers/interconnect/qcom/sm8350.c1673
-rw-r--r--drivers/interconnect/qcom/sm8350.h168
-rw-r--r--drivers/interconnect/qcom/sm8450.c711
-rw-r--r--drivers/interconnect/qcom/sm8450.h169
-rw-r--r--drivers/interconnect/qcom/sm8550.c1614
-rw-r--r--drivers/interconnect/qcom/sm8650.c1957
-rw-r--r--drivers/interconnect/qcom/sm8750.c1535
-rw-r--r--drivers/interconnect/qcom/smd-rpm.c29
-rw-r--r--drivers/interconnect/qcom/smd-rpm.h15
-rw-r--r--drivers/interconnect/qcom/x1e80100.c1924
-rw-r--r--drivers/interconnect/samsung/exynos.c41
-rw-r--r--drivers/interconnect/trace.h10
-rw-r--r--drivers/iommu/Kconfig210
-rw-r--r--drivers/iommu/Makefile14
-rw-r--r--drivers/iommu/amd/Kconfig20
-rw-r--r--drivers/iommu/amd/Makefile3
-rw-r--r--drivers/iommu/amd/amd_iommu.h201
-rw-r--r--drivers/iommu/amd/amd_iommu_types.h462
-rw-r--r--drivers/iommu/amd/debugfs.c378
-rw-r--r--drivers/iommu/amd/init.c1487
-rw-r--r--drivers/iommu/amd/io_pgtable.c527
-rw-r--r--drivers/iommu/amd/iommu.c3024
-rw-r--r--drivers/iommu/amd/iommu_v2.c990
-rw-r--r--drivers/iommu/amd/pasid.c203
-rw-r--r--drivers/iommu/amd/ppr.c273
-rw-r--r--drivers/iommu/apple-dart.c881
-rw-r--r--drivers/iommu/arm/Kconfig144
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/Makefile9
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c485
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c663
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c612
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c3029
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h473
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c1349
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu-impl.c10
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu-nvidia.c8
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c521
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c434
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h28
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu.c442
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu.h77
-rw-r--r--drivers/iommu/arm/arm-smmu/qcom_iommu.c190
-rw-r--r--drivers/iommu/dma-iommu.c1276
-rw-r--r--drivers/iommu/dma-iommu.h62
-rw-r--r--drivers/iommu/exynos-iommu.c372
-rw-r--r--drivers/iommu/fsl_pamu.c17
-rw-r--r--drivers/iommu/fsl_pamu_domain.c172
-rw-r--r--drivers/iommu/generic_pt/.kunitconfig14
-rw-r--r--drivers/iommu/generic_pt/Kconfig79
-rw-r--r--drivers/iommu/generic_pt/fmt/Makefile28
-rw-r--r--drivers/iommu/generic_pt/fmt/amdv1.h411
-rw-r--r--drivers/iommu/generic_pt/fmt/defs_amdv1.h21
-rw-r--r--drivers/iommu/generic_pt/fmt/defs_vtdss.h21
-rw-r--r--drivers/iommu/generic_pt/fmt/defs_x86_64.h21
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_amdv1.c15
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_mock.c10
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_template.h48
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_vtdss.c10
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_x86_64.c11
-rw-r--r--drivers/iommu/generic_pt/fmt/vtdss.h285
-rw-r--r--drivers/iommu/generic_pt/fmt/x86_64.h279
-rw-r--r--drivers/iommu/generic_pt/iommu_pt.h1289
-rw-r--r--drivers/iommu/generic_pt/kunit_generic_pt.h823
-rw-r--r--drivers/iommu/generic_pt/kunit_iommu.h184
-rw-r--r--drivers/iommu/generic_pt/kunit_iommu_pt.h487
-rw-r--r--drivers/iommu/generic_pt/pt_common.h389
-rw-r--r--drivers/iommu/generic_pt/pt_defs.h332
-rw-r--r--drivers/iommu/generic_pt/pt_fmt_defaults.h295
-rw-r--r--drivers/iommu/generic_pt/pt_iter.h636
-rw-r--r--drivers/iommu/generic_pt/pt_log2.h122
-rw-r--r--drivers/iommu/hyperv-iommu.c56
-rw-r--r--drivers/iommu/intel/Kconfig40
-rw-r--r--drivers/iommu/intel/Makefile6
-rw-r--r--drivers/iommu/intel/cache.c528
-rw-r--r--drivers/iommu/intel/cap_audit.c219
-rw-r--r--drivers/iommu/intel/cap_audit.h131
-rw-r--r--drivers/iommu/intel/debugfs.c250
-rw-r--r--drivers/iommu/intel/dmar.c253
-rw-r--r--drivers/iommu/intel/iommu.c3611
-rw-r--r--drivers/iommu/intel/iommu.h766
-rw-r--r--drivers/iommu/intel/irq_remapping.c305
-rw-r--r--drivers/iommu/intel/nested.c242
-rw-r--r--drivers/iommu/intel/pasid.c1103
-rw-r--r--drivers/iommu/intel/pasid.h264
-rw-r--r--drivers/iommu/intel/perf.c12
-rw-r--r--drivers/iommu/intel/perf.h6
-rw-r--r--drivers/iommu/intel/perfmon.c790
-rw-r--r--drivers/iommu/intel/perfmon.h64
-rw-r--r--drivers/iommu/intel/prq.c396
-rw-r--r--drivers/iommu/intel/svm.c946
-rw-r--r--drivers/iommu/intel/trace.h98
-rw-r--r--drivers/iommu/io-pgfault.c546
-rw-r--r--drivers/iommu/io-pgtable-arm-selftests.c214
-rw-r--r--drivers/iommu/io-pgtable-arm-v7s.c187
-rw-r--r--drivers/iommu/io-pgtable-arm.c732
-rw-r--r--drivers/iommu/io-pgtable-dart.c483
-rw-r--r--drivers/iommu/io-pgtable.c29
-rw-r--r--drivers/iommu/ioasid.c422
-rw-r--r--drivers/iommu/iommu-pages.c253
-rw-r--r--drivers/iommu/iommu-pages.h148
-rw-r--r--drivers/iommu/iommu-priv.h68
-rw-r--r--drivers/iommu/iommu-sva-lib.c71
-rw-r--r--drivers/iommu/iommu-sva-lib.h67
-rw-r--r--drivers/iommu/iommu-sva.c341
-rw-r--r--drivers/iommu/iommu-sysfs.c10
-rw-r--r--drivers/iommu/iommu-traces.c1
-rw-r--r--drivers/iommu/iommu.c3155
-rw-r--r--drivers/iommu/iommufd/Kconfig50
-rw-r--r--drivers/iommu/iommufd/Makefile19
-rw-r--r--drivers/iommu/iommufd/device.c1663
-rw-r--r--drivers/iommu/iommufd/double_span.h53
-rw-r--r--drivers/iommu/iommufd/driver.c304
-rw-r--r--drivers/iommu/iommufd/eventq.c541
-rw-r--r--drivers/iommu/iommufd/hw_pagetable.c554
-rw-r--r--drivers/iommu/iommufd/io_pagetable.c1537
-rw-r--r--drivers/iommu/iommufd/io_pagetable.h312
-rw-r--r--drivers/iommu/iommufd/ioas.c663
-rw-r--r--drivers/iommu/iommufd/iommufd_private.h750
-rw-r--r--drivers/iommu/iommufd/iommufd_test.h294
-rw-r--r--drivers/iommu/iommufd/iova_bitmap.c446
-rw-r--r--drivers/iommu/iommufd/main.c808
-rw-r--r--drivers/iommu/iommufd/pages.c2520
-rw-r--r--drivers/iommu/iommufd/selftest.c2263
-rw-r--r--drivers/iommu/iommufd/vfio_compat.c536
-rw-r--r--drivers/iommu/iommufd/viommu.c430
-rw-r--r--drivers/iommu/iova.c270
-rw-r--r--drivers/iommu/ipmmu-vmsa.c201
-rw-r--r--drivers/iommu/irq_remapping.c13
-rw-r--r--drivers/iommu/irq_remapping.h2
-rw-r--r--drivers/iommu/msm_iommu.c123
-rw-r--r--drivers/iommu/mtk_iommu.c754
-rw-r--r--drivers/iommu/mtk_iommu_v1.c174
-rw-r--r--drivers/iommu/of_iommu.c200
-rw-r--r--drivers/iommu/omap-iommu-debug.c6
-rw-r--r--drivers/iommu/omap-iommu.c149
-rw-r--r--drivers/iommu/omap-iommu.h4
-rw-r--r--drivers/iommu/riscv/Kconfig20
-rw-r--r--drivers/iommu/riscv/Makefile3
-rw-r--r--drivers/iommu/riscv/iommu-bits.h784
-rw-r--r--drivers/iommu/riscv/iommu-pci.c128
-rw-r--r--drivers/iommu/riscv/iommu-platform.c179
-rw-r--r--drivers/iommu/riscv/iommu.c1682
-rw-r--r--drivers/iommu/riscv/iommu.h89
-rw-r--r--drivers/iommu/rockchip-iommu.c251
-rw-r--r--drivers/iommu/s390-iommu.c1188
-rw-r--r--drivers/iommu/sprd-iommu.c161
-rw-r--r--drivers/iommu/sun50i-iommu.c200
-rw-r--r--drivers/iommu/tegra-gart.c371
-rw-r--r--drivers/iommu/tegra-smmu.c224
-rw-r--r--drivers/iommu/virtio-iommu.c372
-rw-r--r--drivers/ipack/devices/ipoctal.c27
-rw-r--r--drivers/ipack/ipack.c21
-rw-r--r--drivers/irqchip/Kconfig233
-rw-r--r--drivers/irqchip/Makefile25
-rw-r--r--drivers/irqchip/exynos-combiner.c16
-rw-r--r--drivers/irqchip/irq-aclint-sswi.c209
-rw-r--r--drivers/irqchip/irq-al-fic.c21
-rw-r--r--drivers/irqchip/irq-alpine-msi.c161
-rw-r--r--drivers/irqchip/irq-apple-aic.c349
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c889
-rw-r--r--drivers/irqchip/irq-aspeed-i2c-ic.c2
-rw-r--r--drivers/irqchip/irq-aspeed-intc.c139
-rw-r--r--drivers/irqchip/irq-aspeed-scu-ic.c257
-rw-r--r--drivers/irqchip/irq-aspeed-vic.c4
-rw-r--r--drivers/irqchip/irq-ath79-misc.c22
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.c21
-rw-r--r--drivers/irqchip/irq-atmel-aic.c22
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c40
-rw-r--r--drivers/irqchip/irq-bcm2712-mip.c288
-rw-r--r--drivers/irqchip/irq-bcm2835.c6
-rw-r--r--drivers/irqchip/irq-bcm2836.c10
-rw-r--r--drivers/irqchip/irq-bcm6345-l1.c31
-rw-r--r--drivers/irqchip/irq-bcm7038-l1.c34
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c58
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c92
-rw-r--r--drivers/irqchip/irq-clps711x.c15
-rw-r--r--drivers/irqchip/irq-crossbar.c6
-rw-r--r--drivers/irqchip/irq-csky-apb-intc.c7
-rw-r--r--drivers/irqchip/irq-csky-mpintc.c2
-rw-r--r--drivers/irqchip/irq-davinci-aintc.c163
-rw-r--r--drivers/irqchip/irq-davinci-cp-intc.c62
-rw-r--r--drivers/irqchip/irq-digicolor.c2
-rw-r--r--drivers/irqchip/irq-dw-apb-ictl.c5
-rw-r--r--drivers/irqchip/irq-econet-en751221.c310
-rw-r--r--drivers/irqchip/irq-ftintc010.c9
-rw-r--r--drivers/irqchip/irq-gic-common.c32
-rw-r--r--drivers/irqchip/irq-gic-common.h13
-rw-r--r--drivers/irqchip/irq-gic-its-msi-parent.c331
-rw-r--r--drivers/irqchip/irq-gic-its-msi-parent.h12
-rw-r--r--drivers/irqchip/irq-gic-pm.c4
-rw-r--r--drivers/irqchip/irq-gic-v2m.c130
-rw-r--r--drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c6
-rw-r--r--drivers/irqchip/irq-gic-v3-its-pci-msi.c202
-rw-r--r--drivers/irqchip/irq-gic-v3-its-platform-msi.c163
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c852
-rw-r--r--drivers/irqchip/irq-gic-v3-mbi.c156
-rw-r--r--drivers/irqchip/irq-gic-v3.c929
-rw-r--r--drivers/irqchip/irq-gic-v4.c18
-rw-r--r--drivers/irqchip/irq-gic-v5-irs.c827
-rw-r--r--drivers/irqchip/irq-gic-v5-its.c1233
-rw-r--r--drivers/irqchip/irq-gic-v5-iwb.c277
-rw-r--r--drivers/irqchip/irq-gic-v5.c1130
-rw-r--r--drivers/irqchip/irq-gic.c115
-rw-r--r--drivers/irqchip/irq-goldfish-pic.c7
-rw-r--r--drivers/irqchip/irq-hip04.c12
-rw-r--r--drivers/irqchip/irq-i8259.c18
-rw-r--r--drivers/irqchip/irq-idt3243x.c2
-rw-r--r--drivers/irqchip/irq-imgpdc.c11
-rw-r--r--drivers/irqchip/irq-imx-gpcv2.c23
-rw-r--r--drivers/irqchip/irq-imx-intmux.c25
-rw-r--r--drivers/irqchip/irq-imx-irqsteer.c59
-rw-r--r--drivers/irqchip/irq-imx-mu-msi.c440
-rw-r--r--drivers/irqchip/irq-ingenic-tcu.c13
-rw-r--r--drivers/irqchip/irq-ingenic.c4
-rw-r--r--drivers/irqchip/irq-ixp4xx.c5
-rw-r--r--drivers/irqchip/irq-jcore-aic.c14
-rw-r--r--drivers/irqchip/irq-keystone.c20
-rw-r--r--drivers/irqchip/irq-lan966x-oic.c274
-rw-r--r--drivers/irqchip/irq-loongarch-avec.c433
-rw-r--r--drivers/irqchip/irq-loongarch-cpu.c61
-rw-r--r--drivers/irqchip/irq-loongson-eiointc.c434
-rw-r--r--drivers/irqchip/irq-loongson-htpic.c10
-rw-r--r--drivers/irqchip/irq-loongson-htvec.c183
-rw-r--r--drivers/irqchip/irq-loongson-liointc.c80
-rw-r--r--drivers/irqchip/irq-loongson-pch-lpc.c40
-rw-r--r--drivers/irqchip/irq-loongson-pch-msi.c108
-rw-r--r--drivers/irqchip/irq-loongson-pch-pic.c174
-rw-r--r--drivers/irqchip/irq-loongson.h27
-rw-r--r--drivers/irqchip/irq-lpc32xx.c4
-rw-r--r--drivers/irqchip/irq-ls-extirq.c91
-rw-r--r--drivers/irqchip/irq-ls-scfg-msi.c85
-rw-r--r--drivers/irqchip/irq-ls1x.c4
-rw-r--r--drivers/irqchip/irq-madera.c12
-rw-r--r--drivers/irqchip/irq-mbigen.c131
-rw-r--r--drivers/irqchip/irq-mchp-eic.c23
-rw-r--r--drivers/irqchip/irq-meson-gpio.c94
-rw-r--r--drivers/irqchip/irq-mips-cpu.c13
-rw-r--r--drivers/irqchip/irq-mips-gic.c338
-rw-r--r--drivers/irqchip/irq-mmp.c139
-rw-r--r--drivers/irqchip/irq-mscc-ocelot.c17
-rw-r--r--drivers/irqchip/irq-msi-lib.c166
-rw-r--r--drivers/irqchip/irq-mst-intc.c16
-rw-r--r--drivers/irqchip/irq-mtk-cirq.c112
-rw-r--r--drivers/irqchip/irq-mtk-sysirq.c4
-rw-r--r--drivers/irqchip/irq-mvebu-gicp.c68
-rw-r--r--drivers/irqchip/irq-mvebu-icu.c278
-rw-r--r--drivers/irqchip/irq-mvebu-odmi.c54
-rw-r--r--drivers/irqchip/irq-mvebu-pic.c21
-rw-r--r--drivers/irqchip/irq-mvebu-sei.c74
-rw-r--r--drivers/irqchip/irq-mxs.c7
-rw-r--r--drivers/irqchip/irq-nvic.c5
-rw-r--r--drivers/irqchip/irq-omap-intc.c7
-rw-r--r--drivers/irqchip/irq-or1k-pic.c4
-rw-r--r--drivers/irqchip/irq-orion.c9
-rw-r--r--drivers/irqchip/irq-owl-sirq.c4
-rw-r--r--drivers/irqchip/irq-partition-percpu.c241
-rw-r--r--drivers/irqchip/irq-pic32-evic.c18
-rw-r--r--drivers/irqchip/irq-pruss-intc.c27
-rw-r--r--drivers/irqchip/irq-qcom-mpm.c39
-rw-r--r--drivers/irqchip/irq-realtek-rtl.c134
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c19
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c16
-rw-r--r--drivers/irqchip/irq-renesas-rza1.c14
-rw-r--r--drivers/irqchip/irq-renesas-rzg2l.c395
-rw-r--r--drivers/irqchip/irq-renesas-rzv2h.c622
-rw-r--r--drivers/irqchip/irq-riscv-aplic-direct.c333
-rw-r--r--drivers/irqchip/irq-riscv-aplic-main.c234
-rw-r--r--drivers/irqchip/irq-riscv-aplic-main.h53
-rw-r--r--drivers/irqchip/irq-riscv-aplic-msi.c285
-rw-r--r--drivers/irqchip/irq-riscv-imsic-early.c282
-rw-r--r--drivers/irqchip/irq-riscv-imsic-platform.c390
-rw-r--r--drivers/irqchip/irq-riscv-imsic-state.c953
-rw-r--r--drivers/irqchip/irq-riscv-imsic-state.h109
-rw-r--r--drivers/irqchip/irq-riscv-intc.c343
-rw-r--r--drivers/irqchip/irq-riscv-rpmi-sysmsi.c328
-rw-r--r--drivers/irqchip/irq-sa11x0.c17
-rw-r--r--drivers/irqchip/irq-sg2042-msi.c340
-rw-r--r--drivers/irqchip/irq-sifive-plic.c575
-rw-r--r--drivers/irqchip/irq-sl28cpld.c4
-rw-r--r--drivers/irqchip/irq-sni-exiu.c6
-rw-r--r--drivers/irqchip/irq-sp7021-intc.c4
-rw-r--r--drivers/irqchip/irq-st.c24
-rw-r--r--drivers/irqchip/irq-starfive-jh8100-intc.c207
-rw-r--r--drivers/irqchip/irq-stm32-exti.c634
-rw-r--r--drivers/irqchip/irq-stm32mp-exti.c725
-rw-r--r--drivers/irqchip/irq-sun4i.c2
-rw-r--r--drivers/irqchip/irq-sun6i-r.c24
-rw-r--r--drivers/irqchip/irq-sunxi-nmi.c99
-rw-r--r--drivers/irqchip/irq-tb10x.c23
-rw-r--r--drivers/irqchip/irq-tegra.c17
-rw-r--r--drivers/irqchip/irq-ti-sci-inta.c17
-rw-r--r--drivers/irqchip/irq-ti-sci-intr.c13
-rw-r--r--drivers/irqchip/irq-ts4800.c18
-rw-r--r--drivers/irqchip/irq-uniphier-aidet.c3
-rw-r--r--drivers/irqchip/irq-versatile-fpga.c9
-rw-r--r--drivers/irqchip/irq-vf610-mscm-ir.c6
-rw-r--r--drivers/irqchip/irq-vic.c20
-rw-r--r--drivers/irqchip/irq-vt8500.c153
-rw-r--r--drivers/irqchip/irq-wpcm450-aic.c3
-rw-r--r--drivers/irqchip/irq-xilinx-intc.c8
-rw-r--r--drivers/irqchip/irq-xtensa-mx.c8
-rw-r--r--drivers/irqchip/irq-xtensa-pic.c40
-rw-r--r--drivers/irqchip/irq-zevio.c4
-rw-r--r--drivers/irqchip/irqchip.c10
-rw-r--r--drivers/irqchip/qcom-irq-combiner.c6
-rw-r--r--drivers/irqchip/qcom-pdc.c140
-rw-r--r--drivers/irqchip/spear-shirq.c2
-rw-r--r--drivers/isdn/Kconfig1
-rw-r--r--drivers/isdn/capi/Makefile3
-rw-r--r--drivers/isdn/capi/capi.c42
-rw-r--r--drivers/isdn/capi/kcapi.c13
-rw-r--r--drivers/isdn/hardware/mISDN/Kconfig12
-rw-r--r--drivers/isdn/hardware/mISDN/avmfritz.c3
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c80
-rw-r--r--drivers/isdn/hardware/mISDN/hfcpci.c53
-rw-r--r--drivers/isdn/hardware/mISDN/hfcsusb.c33
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNinfineon.c1
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNipac.c13
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNisar.c9
-rw-r--r--drivers/isdn/hardware/mISDN/netjet.c4
-rw-r--r--drivers/isdn/hardware/mISDN/speedfax.c1
-rw-r--r--drivers/isdn/hardware/mISDN/w6692.c11
-rw-r--r--drivers/isdn/mISDN/clock.c2
-rw-r--r--drivers/isdn/mISDN/core.c33
-rw-r--r--drivers/isdn/mISDN/core.h1
-rw-r--r--drivers/isdn/mISDN/dsp.h2
-rw-r--r--drivers/isdn/mISDN/dsp_blowfish.c5
-rw-r--r--drivers/isdn/mISDN/dsp_cmx.c19
-rw-r--r--drivers/isdn/mISDN/dsp_core.c9
-rw-r--r--drivers/isdn/mISDN/dsp_hwec.c6
-rw-r--r--drivers/isdn/mISDN/dsp_pipeline.c19
-rw-r--r--drivers/isdn/mISDN/dsp_tones.c6
-rw-r--r--drivers/isdn/mISDN/fsm.c6
-rw-r--r--drivers/isdn/mISDN/l1oip.h1
-rw-r--r--drivers/isdn/mISDN/l1oip_core.c24
-rw-r--r--drivers/isdn/mISDN/socket.c14
-rw-r--r--drivers/isdn/mISDN/timerdev.c7
-rw-r--r--drivers/leds/.kunitconfig4
-rw-r--r--drivers/leds/Kconfig232
-rw-r--r--drivers/leds/Makefile23
-rw-r--r--drivers/leds/blink/Kconfig4
-rw-r--r--drivers/leds/blink/leds-bcm63138.c30
-rw-r--r--drivers/leds/blink/leds-lgm-sso.c13
-rw-r--r--drivers/leds/flash/Kconfig60
-rw-r--r--drivers/leds/flash/Makefile4
-rw-r--r--drivers/leds/flash/leds-aat1290.c22
-rw-r--r--drivers/leds/flash/leds-as3645a.c22
-rw-r--r--drivers/leds/flash/leds-ktd2692.c136
-rw-r--r--drivers/leds/flash/leds-lm3601x.c39
-rw-r--r--drivers/leds/flash/leds-max77693.c24
-rw-r--r--drivers/leds/flash/leds-mt6360.c63
-rw-r--r--drivers/leds/flash/leds-mt6370-flash.c568
-rw-r--r--drivers/leds/flash/leds-qcom-flash.c989
-rw-r--r--drivers/leds/flash/leds-rt4505.c8
-rw-r--r--drivers/leds/flash/leds-rt8515.c12
-rw-r--r--drivers/leds/flash/leds-sgm3140.c17
-rw-r--r--drivers/leds/flash/leds-sy7802.c539
-rw-r--r--drivers/leds/flash/leds-tps6131x.c815
-rw-r--r--drivers/leds/led-class-flash.c16
-rw-r--r--drivers/leds/led-class-multicolor.c16
-rw-r--r--drivers/leds/led-class.c257
-rw-r--r--drivers/leds/led-core.c220
-rw-r--r--drivers/leds/led-test.c132
-rw-r--r--drivers/leds/led-triggers.c126
-rw-r--r--drivers/leds/leds-88pm860x.c9
-rw-r--r--drivers/leds/leds-adp5520.c4
-rw-r--r--drivers/leds/leds-an30259a.c45
-rw-r--r--drivers/leds/leds-apu.c3
-rw-r--r--drivers/leds/leds-ariel.c2
-rw-r--r--drivers/leds/leds-asic3.c177
-rw-r--r--drivers/leds/leds-aw200xx.c669
-rw-r--r--drivers/leds/leds-aw2013.c76
-rw-r--r--drivers/leds/leds-bcm6328.c58
-rw-r--r--drivers/leds/leds-bcm6358.c39
-rw-r--r--drivers/leds/leds-bd2606mvv.c157
-rw-r--r--drivers/leds/leds-bd2802.c9
-rw-r--r--drivers/leds/leds-blinkm.c236
-rw-r--r--drivers/leds/leds-cht-wcove.c472
-rw-r--r--drivers/leds/leds-clevo-mail.c3
-rw-r--r--drivers/leds/leds-cpcap.c2
-rw-r--r--drivers/leds/leds-cr0014114.c8
-rw-r--r--drivers/leds/leds-cros_ec.c263
-rw-r--r--drivers/leds/leds-da903x.c4
-rw-r--r--drivers/leds/leds-da9052.c4
-rw-r--r--drivers/leds/leds-el15203000.c16
-rw-r--r--drivers/leds/leds-expresswire.c72
-rw-r--r--drivers/leds/leds-gpio-register.c2
-rw-r--r--drivers/leds/leds-gpio.c88
-rw-r--r--drivers/leds/leds-ip30.c8
-rw-r--r--drivers/leds/leds-is31fl319x.c46
-rw-r--r--drivers/leds/leds-is31fl32xx.c69
-rw-r--r--drivers/leds/leds-lm3530.c8
-rw-r--r--drivers/leds/leds-lm3532.c54
-rw-r--r--drivers/leds/leds-lm3533.c16
-rw-r--r--drivers/leds/leds-lm355x.c8
-rw-r--r--drivers/leds/leds-lm3642.c8
-rw-r--r--drivers/leds/leds-lm3692x.c10
-rw-r--r--drivers/leds/leds-lm3697.c33
-rw-r--r--drivers/leds/leds-lp3944.c9
-rw-r--r--drivers/leds/leds-lp3952.c32
-rw-r--r--drivers/leds/leds-lp50xx.c112
-rw-r--r--drivers/leds/leds-lp5521.c432
-rw-r--r--drivers/leds/leds-lp5523.c806
-rw-r--r--drivers/leds/leds-lp5562.c306
-rw-r--r--drivers/leds/leds-lp5569.c544
-rw-r--r--drivers/leds/leds-lp55xx-common.c799
-rw-r--r--drivers/leds/leds-lp55xx-common.h164
-rw-r--r--drivers/leds/leds-lp8501.c327
-rw-r--r--drivers/leds/leds-lp8860.c231
-rw-r--r--drivers/leds/leds-lp8864.c296
-rw-r--r--drivers/leds/leds-max5970.c112
-rw-r--r--drivers/leds/leds-max77650.c18
-rw-r--r--drivers/leds/leds-max77705.c275
-rw-r--r--drivers/leds/leds-max8997.c7
-rw-r--r--drivers/leds/leds-mc13783.c28
-rw-r--r--drivers/leds/leds-mlxcpld.c3
-rw-r--r--drivers/leds/leds-mlxreg.c18
-rw-r--r--drivers/leds/leds-mt6323.c502
-rw-r--r--drivers/leds/leds-netxbig.c56
-rw-r--r--drivers/leds/leds-nic78bx.c41
-rw-r--r--drivers/leds/leds-ns2.c9
-rw-r--r--drivers/leds/leds-pca9532.c110
-rw-r--r--drivers/leds/leds-pca955x.c464
-rw-r--r--drivers/leds/leds-pca963x.c67
-rw-r--r--drivers/leds/leds-pca995x.c218
-rw-r--r--drivers/leds/leds-pm8058.c30
-rw-r--r--drivers/leds/leds-powernv.c33
-rw-r--r--drivers/leds/leds-pwm.c72
-rw-r--r--drivers/leds/leds-qnap-mcu.c392
-rw-r--r--drivers/leds/leds-rb532.c3
-rw-r--r--drivers/leds/leds-regulator.c3
-rw-r--r--drivers/leds/leds-s3c24xx.c83
-rw-r--r--drivers/leds/leds-sc27xx-bltc.c20
-rw-r--r--drivers/leds/leds-spi-byte.c68
-rw-r--r--drivers/leds/leds-ss4200.c9
-rw-r--r--drivers/leds/leds-st1202.c416
-rw-r--r--drivers/leds/leds-sun50i-a100.c573
-rw-r--r--drivers/leds/leds-sunfire.c6
-rw-r--r--drivers/leds/leds-syscon.c55
-rw-r--r--drivers/leds/leds-tca6507.c69
-rw-r--r--drivers/leds/leds-ti-lmu-common.c2
-rw-r--r--drivers/leds/leds-tlc591xx.c24
-rw-r--r--drivers/leds/leds-turris-omnia.c476
-rw-r--r--drivers/leds/leds-upboard.c126
-rw-r--r--drivers/leds/leds-wm831x-status.c4
-rw-r--r--drivers/leds/leds-wm8350.c3
-rw-r--r--drivers/leds/leds.h7
-rw-r--r--drivers/leds/rgb/Kconfig49
-rw-r--r--drivers/leds/rgb/Makefile4
-rw-r--r--drivers/leds/rgb/leds-group-multicolor.c173
-rw-r--r--drivers/leds/rgb/leds-ktd202x.c635
-rw-r--r--drivers/leds/rgb/leds-mt6370-rgb.c995
-rw-r--r--drivers/leds/rgb/leds-ncp5623.c270
-rw-r--r--drivers/leds/rgb/leds-pwm-multicolor.c28
-rw-r--r--drivers/leds/rgb/leds-qcom-lpg.c672
-rw-r--r--drivers/leds/simatic/Kconfig51
-rw-r--r--drivers/leds/simatic/Makefile5
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-apollolake.c67
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-core.c108
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-elkhartlake.c57
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio-f7188x.c107
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds-gpio.h22
-rw-r--r--drivers/leds/simatic/simatic-ipc-leds.c (renamed from drivers/leds/simple/simatic-ipc-leds.c)2
-rw-r--r--drivers/leds/simple/Kconfig11
-rw-r--r--drivers/leds/simple/Makefile3
-rw-r--r--drivers/leds/simple/simatic-ipc-leds-gpio.c105
-rw-r--r--drivers/leds/trigger/Kconfig27
-rw-r--r--drivers/leds/trigger/Makefile2
-rw-r--r--drivers/leds/trigger/ledtrig-activity.c8
-rw-r--r--drivers/leds/trigger/ledtrig-audio.c65
-rw-r--r--drivers/leds/trigger/ledtrig-backlight.c48
-rw-r--r--drivers/leds/trigger/ledtrig-cpu.c18
-rw-r--r--drivers/leds/trigger/ledtrig-default-on.c1
-rw-r--r--drivers/leds/trigger/ledtrig-disk.c13
-rw-r--r--drivers/leds/trigger/ledtrig-gpio.c155
-rw-r--r--drivers/leds/trigger/ledtrig-heartbeat.c4
-rw-r--r--drivers/leds/trigger/ledtrig-input-events.c165
-rw-r--r--drivers/leds/trigger/ledtrig-mtd.c8
-rw-r--r--drivers/leds/trigger/ledtrig-netdev.c551
-rw-r--r--drivers/leds/trigger/ledtrig-panic.c28
-rw-r--r--drivers/leds/trigger/ledtrig-pattern.c130
-rw-r--r--drivers/leds/trigger/ledtrig-timer.c5
-rw-r--r--drivers/leds/trigger/ledtrig-transient.c6
-rw-r--r--drivers/leds/trigger/ledtrig-tty.c253
-rw-r--r--drivers/leds/uleds.c13
-rw-r--r--drivers/macintosh/Kconfig4
-rw-r--r--drivers/macintosh/adb-iop.c2
-rw-r--r--drivers/macintosh/adb.c17
-rw-r--r--drivers/macintosh/adbhid.c2
-rw-r--r--drivers/macintosh/ams/ams-core.c2
-rw-r--r--drivers/macintosh/ams/ams-i2c.c14
-rw-r--r--drivers/macintosh/ams/ams.h7
-rw-r--r--drivers/macintosh/mac_hid.c9
-rw-r--r--drivers/macintosh/macio-adb.c30
-rw-r--r--drivers/macintosh/macio_asic.c18
-rw-r--r--drivers/macintosh/rack-meter.c6
-rw-r--r--drivers/macintosh/smu.c23
-rw-r--r--drivers/macintosh/therm_adt746x.c12
-rw-r--r--drivers/macintosh/therm_windtunnel.c21
-rw-r--r--drivers/macintosh/via-cuda.c16
-rw-r--r--drivers/macintosh/via-macii.c11
-rw-r--r--drivers/macintosh/via-pmu-backlight.c12
-rw-r--r--drivers/macintosh/via-pmu-led.c19
-rw-r--r--drivers/macintosh/via-pmu.c41
-rw-r--r--drivers/macintosh/windfarm_ad7417_sensor.c9
-rw-r--r--drivers/macintosh/windfarm_fcu_controls.c8
-rw-r--r--drivers/macintosh/windfarm_lm75_sensor.c16
-rw-r--r--drivers/macintosh/windfarm_lm87_sensor.c9
-rw-r--r--drivers/macintosh/windfarm_max6690_sensor.c9
-rw-r--r--drivers/macintosh/windfarm_pid.h5
-rw-r--r--drivers/macintosh/windfarm_pm112.c4
-rw-r--r--drivers/macintosh/windfarm_pm121.c7
-rw-r--r--drivers/macintosh/windfarm_pm72.c7
-rw-r--r--drivers/macintosh/windfarm_pm81.c12
-rw-r--r--drivers/macintosh/windfarm_pm91.c10
-rw-r--r--drivers/macintosh/windfarm_rm31.c7
-rw-r--r--drivers/macintosh/windfarm_smu_controls.c10
-rw-r--r--drivers/macintosh/windfarm_smu_sat.c10
-rw-r--r--drivers/macintosh/windfarm_smu_sensors.c4
-rw-r--r--drivers/mailbox/Kconfig148
-rw-r--r--drivers/mailbox/Makefile22
-rw-r--r--drivers/mailbox/apple-mailbox.c384
-rw-r--r--drivers/mailbox/arm_mhu.c3
-rw-r--r--drivers/mailbox/arm_mhu_db.c3
-rw-r--r--drivers/mailbox/arm_mhuv2.c17
-rw-r--r--drivers/mailbox/arm_mhuv3.c1103
-rw-r--r--drivers/mailbox/ast2700-mailbox.c235
-rw-r--r--drivers/mailbox/bcm-flexrm-mailbox.c30
-rw-r--r--drivers/mailbox/bcm-pdc-mailbox.c58
-rw-r--r--drivers/mailbox/bcm2835-mailbox.c3
-rw-r--r--drivers/mailbox/bcm74110-mailbox.c656
-rw-r--r--drivers/mailbox/cix-mailbox.c645
-rw-r--r--drivers/mailbox/cv1800-mailbox.c220
-rw-r--r--drivers/mailbox/exynos-mailbox.c157
-rw-r--r--drivers/mailbox/hi3660-mailbox.c1
-rw-r--r--drivers/mailbox/hi6220-mailbox.c6
-rw-r--r--drivers/mailbox/imx-mailbox.c176
-rw-r--r--drivers/mailbox/mailbox-altera.c4
-rw-r--r--drivers/mailbox/mailbox-mchp-ipc-sbi.c504
-rw-r--r--drivers/mailbox/mailbox-mpfs.c145
-rw-r--r--drivers/mailbox/mailbox-sti.c8
-rw-r--r--drivers/mailbox/mailbox-test.c30
-rw-r--r--drivers/mailbox/mailbox-th1520.c597
-rw-r--r--drivers/mailbox/mailbox.c334
-rw-r--r--drivers/mailbox/mailbox.h2
-rw-r--r--drivers/mailbox/mtk-adsp-mailbox.c3
-rw-r--r--drivers/mailbox/mtk-cmdq-mailbox.c340
-rw-r--r--drivers/mailbox/mtk-gpueb-mailbox.c319
-rw-r--r--drivers/mailbox/omap-mailbox.c577
-rw-r--r--drivers/mailbox/pcc.c328
-rw-r--r--drivers/mailbox/pl320-ipc.c14
-rw-r--r--drivers/mailbox/platform_mhu.c5
-rw-r--r--drivers/mailbox/qcom-apcs-ipc-mailbox.c43
-rw-r--r--drivers/mailbox/qcom-cpucp-mbox.c187
-rw-r--r--drivers/mailbox/qcom-ipcc.c30
-rw-r--r--drivers/mailbox/riscv-sbi-mpxy-mbox.c1019
-rw-r--r--drivers/mailbox/rockchip-mailbox.c17
-rw-r--r--drivers/mailbox/sprd-mailbox.c27
-rw-r--r--drivers/mailbox/stm32-ipcc.c5
-rw-r--r--drivers/mailbox/sun6i-msgbox.c6
-rw-r--r--drivers/mailbox/tegra-hsp.c101
-rw-r--r--drivers/mailbox/ti-msgmgr.c42
-rw-r--r--drivers/mailbox/zynqmp-ipi-mailbox.c452
-rw-r--r--drivers/mcb/mcb-core.c74
-rw-r--r--drivers/mcb/mcb-lpc.c41
-rw-r--r--drivers/mcb/mcb-parse.c26
-rw-r--r--drivers/mcb/mcb-pci.c45
-rw-r--r--drivers/md/Kconfig87
-rw-r--r--drivers/md/Makefile16
-rw-r--r--drivers/md/bcache/Kconfig10
-rw-r--r--drivers/md/bcache/Makefile4
-rw-r--r--drivers/md/bcache/alloc.c46
-rw-r--r--drivers/md/bcache/bcache.h28
-rw-r--r--drivers/md/bcache/bcache_ondisk.h11
-rw-r--r--drivers/md/bcache/bset.c46
-rw-r--r--drivers/md/bcache/bset.h32
-rw-r--r--drivers/md/bcache/btree.c195
-rw-r--r--drivers/md/bcache/btree.h5
-rw-r--r--drivers/md/bcache/closure.c207
-rw-r--r--drivers/md/bcache/closure.h378
-rw-r--r--drivers/md/bcache/debug.c3
-rw-r--r--drivers/md/bcache/io.c3
-rw-r--r--drivers/md/bcache/journal.c116
-rw-r--r--drivers/md/bcache/journal.h13
-rw-r--r--drivers/md/bcache/movinggc.c28
-rw-r--r--drivers/md/bcache/request.c98
-rw-r--r--drivers/md/bcache/request.h2
-rw-r--r--drivers/md/bcache/stats.c4
-rw-r--r--drivers/md/bcache/stats.h2
-rw-r--r--drivers/md/bcache/super.c429
-rw-r--r--drivers/md/bcache/sysfs.c61
-rw-r--r--drivers/md/bcache/sysfs.h2
-rw-r--r--drivers/md/bcache/util.c2
-rw-r--r--drivers/md/bcache/util.h3
-rw-r--r--drivers/md/bcache/writeback.c157
-rw-r--r--drivers/md/dm-audit.c2
-rw-r--r--drivers/md/dm-bio-prison-v1.c138
-rw-r--r--drivers/md/dm-bio-prison-v1.h34
-rw-r--r--drivers/md/dm-bio-prison-v2.c18
-rw-r--r--drivers/md/dm-bio-prison-v2.h11
-rw-r--r--drivers/md/dm-bio-record.h1
-rw-r--r--drivers/md/dm-bufio.c2178
-rw-r--r--drivers/md/dm-builtin.c3
-rw-r--r--drivers/md/dm-cache-background-tracker.c44
-rw-r--r--drivers/md/dm-cache-background-tracker.h54
-rw-r--r--drivers/md/dm-cache-block-types.h1
-rw-r--r--drivers/md/dm-cache-metadata.c180
-rw-r--r--drivers/md/dm-cache-metadata.h8
-rw-r--r--drivers/md/dm-cache-policy-internal.h14
-rw-r--r--drivers/md/dm-cache-policy-smq.c198
-rw-r--r--drivers/md/dm-cache-policy.c3
-rw-r--r--drivers/md/dm-cache-policy.h9
-rw-r--r--drivers/md/dm-cache-target.c373
-rw-r--r--drivers/md/dm-clone-metadata.c16
-rw-r--r--drivers/md/dm-clone-target.c44
-rw-r--r--drivers/md/dm-core.h33
-rw-r--r--drivers/md/dm-crypt.c686
-rw-r--r--drivers/md/dm-delay.c219
-rw-r--r--drivers/md/dm-dust.c27
-rw-r--r--drivers/md/dm-ebs-target.c40
-rw-r--r--drivers/md/dm-era-target.c163
-rw-r--r--drivers/md/dm-exception-store.c7
-rw-r--r--drivers/md/dm-exception-store.h57
-rw-r--r--drivers/md/dm-flakey.c388
-rw-r--r--drivers/md/dm-ima.c115
-rw-r--r--drivers/md/dm-ima.h7
-rw-r--r--drivers/md/dm-init.c31
-rw-r--r--drivers/md/dm-integrity.c1828
-rw-r--r--drivers/md/dm-io-rewind.c8
-rw-r--r--drivers/md/dm-io-tracker.h1
-rw-r--r--drivers/md/dm-io.c188
-rw-r--r--drivers/md/dm-ioctl.c388
-rw-r--r--drivers/md/dm-kcopyd.c71
-rw-r--r--drivers/md/dm-linear.c25
-rw-r--r--drivers/md/dm-log-userspace-base.c19
-rw-r--r--drivers/md/dm-log-userspace-transfer.c8
-rw-r--r--drivers/md/dm-log-userspace-transfer.h1
-rw-r--r--drivers/md/dm-log-writes.c55
-rw-r--r--drivers/md/dm-log.c93
-rw-r--r--drivers/md/dm-mpath.c393
-rw-r--r--drivers/md/dm-mpath.h3
-rw-r--r--drivers/md/dm-path-selector.c12
-rw-r--r--drivers/md/dm-path-selector.h30
-rw-r--r--drivers/md/dm-pcache/Kconfig17
-rw-r--r--drivers/md/dm-pcache/Makefile3
-rw-r--r--drivers/md/dm-pcache/backing_dev.c374
-rw-r--r--drivers/md/dm-pcache/backing_dev.h127
-rw-r--r--drivers/md/dm-pcache/cache.c445
-rw-r--r--drivers/md/dm-pcache/cache.h635
-rw-r--r--drivers/md/dm-pcache/cache_dev.c303
-rw-r--r--drivers/md/dm-pcache/cache_dev.h70
-rw-r--r--drivers/md/dm-pcache/cache_gc.c170
-rw-r--r--drivers/md/dm-pcache/cache_key.c888
-rw-r--r--drivers/md/dm-pcache/cache_req.c836
-rw-r--r--drivers/md/dm-pcache/cache_segment.c305
-rw-r--r--drivers/md/dm-pcache/cache_writeback.c261
-rw-r--r--drivers/md/dm-pcache/dm_pcache.c497
-rw-r--r--drivers/md/dm-pcache/dm_pcache.h67
-rw-r--r--drivers/md/dm-pcache/pcache_internal.h117
-rw-r--r--drivers/md/dm-pcache/segment.c61
-rw-r--r--drivers/md/dm-pcache/segment.h74
-rw-r--r--drivers/md/dm-ps-historical-service-time.c11
-rw-r--r--drivers/md/dm-ps-io-affinity.c13
-rw-r--r--drivers/md/dm-ps-queue-length.c24
-rw-r--r--drivers/md/dm-ps-round-robin.c33
-rw-r--r--drivers/md/dm-ps-service-time.c35
-rw-r--r--drivers/md/dm-raid.c360
-rw-r--r--drivers/md/dm-raid1.c126
-rw-r--r--drivers/md/dm-region-hash.c33
-rw-r--r--drivers/md/dm-rq.c45
-rw-r--r--drivers/md/dm-rq.h3
-rw-r--r--drivers/md/dm-snap-persistent.c54
-rw-r--r--drivers/md/dm-snap-transient.c18
-rw-r--r--drivers/md/dm-snap.c127
-rw-r--r--drivers/md/dm-stats.c123
-rw-r--r--drivers/md/dm-stats.h8
-rw-r--r--drivers/md/dm-stripe.c83
-rw-r--r--drivers/md/dm-switch.c75
-rw-r--r--drivers/md/dm-sysfs.c12
-rw-r--r--drivers/md/dm-table.c960
-rw-r--r--drivers/md/dm-target.c132
-rw-r--r--drivers/md/dm-thin-metadata.c152
-rw-r--r--drivers/md/dm-thin-metadata.h1
-rw-r--r--drivers/md/dm-thin.c343
-rw-r--r--drivers/md/dm-uevent.c6
-rw-r--r--drivers/md/dm-uevent.h6
-rw-r--r--drivers/md/dm-unstripe.c19
-rw-r--r--drivers/md/dm-vdo/Kconfig18
-rw-r--r--drivers/md/dm-vdo/Makefile57
-rw-r--r--drivers/md/dm-vdo/action-manager.c388
-rw-r--r--drivers/md/dm-vdo/action-manager.h110
-rw-r--r--drivers/md/dm-vdo/admin-state.c506
-rw-r--r--drivers/md/dm-vdo/admin-state.h178
-rw-r--r--drivers/md/dm-vdo/block-map.c3315
-rw-r--r--drivers/md/dm-vdo/block-map.h394
-rw-r--r--drivers/md/dm-vdo/completion.c140
-rw-r--r--drivers/md/dm-vdo/completion.h152
-rw-r--r--drivers/md/dm-vdo/constants.h93
-rw-r--r--drivers/md/dm-vdo/cpu.h59
-rw-r--r--drivers/md/dm-vdo/data-vio.c2026
-rw-r--r--drivers/md/dm-vdo/data-vio.h665
-rw-r--r--drivers/md/dm-vdo/dedupe.c3003
-rw-r--r--drivers/md/dm-vdo/dedupe.h120
-rw-r--r--drivers/md/dm-vdo/dm-vdo-target.c2935
-rw-r--r--drivers/md/dm-vdo/dump.c275
-rw-r--r--drivers/md/dm-vdo/dump.h17
-rw-r--r--drivers/md/dm-vdo/encodings.c1465
-rw-r--r--drivers/md/dm-vdo/encodings.h1298
-rw-r--r--drivers/md/dm-vdo/errors.c307
-rw-r--r--drivers/md/dm-vdo/errors.h73
-rw-r--r--drivers/md/dm-vdo/flush.c559
-rw-r--r--drivers/md/dm-vdo/flush.h44
-rw-r--r--drivers/md/dm-vdo/funnel-queue.c170
-rw-r--r--drivers/md/dm-vdo/funnel-queue.h110
-rw-r--r--drivers/md/dm-vdo/funnel-workqueue.c637
-rw-r--r--drivers/md/dm-vdo/funnel-workqueue.h51
-rw-r--r--drivers/md/dm-vdo/indexer/chapter-index.c293
-rw-r--r--drivers/md/dm-vdo/indexer/chapter-index.h61
-rw-r--r--drivers/md/dm-vdo/indexer/config.c376
-rw-r--r--drivers/md/dm-vdo/indexer/config.h124
-rw-r--r--drivers/md/dm-vdo/indexer/delta-index.c1970
-rw-r--r--drivers/md/dm-vdo/indexer/delta-index.h279
-rw-r--r--drivers/md/dm-vdo/indexer/funnel-requestqueue.c279
-rw-r--r--drivers/md/dm-vdo/indexer/funnel-requestqueue.h31
-rw-r--r--drivers/md/dm-vdo/indexer/geometry.c201
-rw-r--r--drivers/md/dm-vdo/indexer/geometry.h140
-rw-r--r--drivers/md/dm-vdo/indexer/hash-utils.h66
-rw-r--r--drivers/md/dm-vdo/indexer/index-layout.c1740
-rw-r--r--drivers/md/dm-vdo/indexer/index-layout.h43
-rw-r--r--drivers/md/dm-vdo/indexer/index-page-map.c173
-rw-r--r--drivers/md/dm-vdo/indexer/index-page-map.h50
-rw-r--r--drivers/md/dm-vdo/indexer/index-session.c735
-rw-r--r--drivers/md/dm-vdo/indexer/index-session.h85
-rw-r--r--drivers/md/dm-vdo/indexer/index.c1385
-rw-r--r--drivers/md/dm-vdo/indexer/index.h83
-rw-r--r--drivers/md/dm-vdo/indexer/indexer.h348
-rw-r--r--drivers/md/dm-vdo/indexer/io-factory.c415
-rw-r--r--drivers/md/dm-vdo/indexer/io-factory.h64
-rw-r--r--drivers/md/dm-vdo/indexer/open-chapter.c426
-rw-r--r--drivers/md/dm-vdo/indexer/open-chapter.h79
-rw-r--r--drivers/md/dm-vdo/indexer/radix-sort.c330
-rw-r--r--drivers/md/dm-vdo/indexer/radix-sort.h26
-rw-r--r--drivers/md/dm-vdo/indexer/sparse-cache.c624
-rw-r--r--drivers/md/dm-vdo/indexer/sparse-cache.h46
-rw-r--r--drivers/md/dm-vdo/indexer/volume-index.c1283
-rw-r--r--drivers/md/dm-vdo/indexer/volume-index.h193
-rw-r--r--drivers/md/dm-vdo/indexer/volume.c1695
-rw-r--r--drivers/md/dm-vdo/indexer/volume.h172
-rw-r--r--drivers/md/dm-vdo/int-map.c701
-rw-r--r--drivers/md/dm-vdo/int-map.h39
-rw-r--r--drivers/md/dm-vdo/io-submitter.c478
-rw-r--r--drivers/md/dm-vdo/io-submitter.h59
-rw-r--r--drivers/md/dm-vdo/logger.c239
-rw-r--r--drivers/md/dm-vdo/logger.h100
-rw-r--r--drivers/md/dm-vdo/logical-zone.c373
-rw-r--r--drivers/md/dm-vdo/logical-zone.h89
-rw-r--r--drivers/md/dm-vdo/memory-alloc.c438
-rw-r--r--drivers/md/dm-vdo/memory-alloc.h162
-rw-r--r--drivers/md/dm-vdo/message-stats.c480
-rw-r--r--drivers/md/dm-vdo/message-stats.h14
-rw-r--r--drivers/md/dm-vdo/murmurhash3.c155
-rw-r--r--drivers/md/dm-vdo/murmurhash3.h15
-rw-r--r--drivers/md/dm-vdo/numeric.h78
-rw-r--r--drivers/md/dm-vdo/packer.c779
-rw-r--r--drivers/md/dm-vdo/packer.h122
-rw-r--r--drivers/md/dm-vdo/permassert.c26
-rw-r--r--drivers/md/dm-vdo/permassert.h45
-rw-r--r--drivers/md/dm-vdo/physical-zone.c644
-rw-r--r--drivers/md/dm-vdo/physical-zone.h115
-rw-r--r--drivers/md/dm-vdo/priority-table.c224
-rw-r--r--drivers/md/dm-vdo/priority-table.h47
-rw-r--r--drivers/md/dm-vdo/recovery-journal.c1762
-rw-r--r--drivers/md/dm-vdo/recovery-journal.h316
-rw-r--r--drivers/md/dm-vdo/repair.c1762
-rw-r--r--drivers/md/dm-vdo/repair.h14
-rw-r--r--drivers/md/dm-vdo/slab-depot.c5181
-rw-r--r--drivers/md/dm-vdo/slab-depot.h612
-rw-r--r--drivers/md/dm-vdo/statistics.h278
-rw-r--r--drivers/md/dm-vdo/status-codes.c94
-rw-r--r--drivers/md/dm-vdo/status-codes.h86
-rw-r--r--drivers/md/dm-vdo/string-utils.c22
-rw-r--r--drivers/md/dm-vdo/string-utils.h23
-rw-r--r--drivers/md/dm-vdo/thread-device.c34
-rw-r--r--drivers/md/dm-vdo/thread-device.h20
-rw-r--r--drivers/md/dm-vdo/thread-registry.c93
-rw-r--r--drivers/md/dm-vdo/thread-registry.h32
-rw-r--r--drivers/md/dm-vdo/thread-utils.c108
-rw-r--r--drivers/md/dm-vdo/thread-utils.h20
-rw-r--r--drivers/md/dm-vdo/time-utils.h28
-rw-r--r--drivers/md/dm-vdo/types.h396
-rw-r--r--drivers/md/dm-vdo/vdo.c1721
-rw-r--r--drivers/md/dm-vdo/vdo.h362
-rw-r--r--drivers/md/dm-vdo/vio.c513
-rw-r--r--drivers/md/dm-vdo/vio.h204
-rw-r--r--drivers/md/dm-vdo/wait-queue.c205
-rw-r--r--drivers/md/dm-vdo/wait-queue.h138
-rw-r--r--drivers/md/dm-verity-fec.c145
-rw-r--r--drivers/md/dm-verity-fec.h24
-rw-r--r--drivers/md/dm-verity-loadpin.c11
-rw-r--r--drivers/md/dm-verity-target.c977
-rw-r--r--drivers/md/dm-verity-verify-sig.c26
-rw-r--r--drivers/md/dm-verity-verify-sig.h2
-rw-r--r--drivers/md/dm-verity.h73
-rw-r--r--drivers/md/dm-writecache.c230
-rw-r--r--drivers/md/dm-zero.c31
-rw-r--r--drivers/md/dm-zone.c729
-rw-r--r--drivers/md/dm-zoned-metadata.c122
-rw-r--r--drivers/md/dm-zoned-reclaim.c6
-rw-r--r--drivers/md/dm-zoned-target.c46
-rw-r--r--drivers/md/dm-zoned.h2
-rw-r--r--drivers/md/dm.c1162
-rw-r--r--drivers/md/dm.h70
-rw-r--r--drivers/md/md-autodetect.c7
-rw-r--r--drivers/md/md-bitmap.c1383
-rw-r--r--drivers/md/md-bitmap.h352
-rw-r--r--drivers/md/md-cluster.c218
-rw-r--r--drivers/md/md-cluster.h8
-rw-r--r--drivers/md/md-faulty.c363
-rw-r--r--drivers/md/md-linear.c149
-rw-r--r--drivers/md/md-linear.h17
-rw-r--r--drivers/md/md-llbitmap.c1626
-rw-r--r--drivers/md/md-multipath.c470
-rw-r--r--drivers/md/md-multipath.h32
-rw-r--r--drivers/md/md.c4379
-rw-r--r--drivers/md/md.h480
-rw-r--r--drivers/md/persistent-data/Kconfig2
-rw-r--r--drivers/md/persistent-data/dm-array.c107
-rw-r--r--drivers/md/persistent-data/dm-array.h3
-rw-r--r--drivers/md/persistent-data/dm-bitset.c14
-rw-r--r--drivers/md/persistent-data/dm-bitset.h1
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c52
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h22
-rw-r--r--drivers/md/persistent-data/dm-btree-internal.h8
-rw-r--r--drivers/md/persistent-data/dm-btree-remove.c52
-rw-r--r--drivers/md/persistent-data/dm-btree-spine.c27
-rw-r--r--drivers/md/persistent-data/dm-btree.c130
-rw-r--r--drivers/md/persistent-data/dm-btree.h15
-rw-r--r--drivers/md/persistent-data/dm-persistent-data-internal.h7
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.c64
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.h11
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.c13
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.h1
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c28
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.h1
-rw-r--r--drivers/md/persistent-data/dm-space-map.h4
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.c83
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.h9
-rw-r--r--drivers/md/raid0.c268
-rw-r--r--drivers/md/raid0.h1
-rw-r--r--drivers/md/raid1-10.c204
-rw-r--r--drivers/md/raid1.c1320
-rw-r--r--drivers/md/raid1.h31
-rw-r--r--drivers/md/raid10.c1216
-rw-r--r--drivers/md/raid10.h8
-rw-r--r--drivers/md/raid5-cache.c193
-rw-r--r--drivers/md/raid5-ppl.c52
-rw-r--r--drivers/md/raid5.c1357
-rw-r--r--drivers/md/raid5.h50
-rw-r--r--drivers/media/Kconfig4
-rw-r--r--drivers/media/cec/core/cec-adap.c179
-rw-r--r--drivers/media/cec/core/cec-api.c12
-rw-r--r--drivers/media/cec/core/cec-core.c43
-rw-r--r--drivers/media/cec/core/cec-notifier.c6
-rw-r--r--drivers/media/cec/core/cec-pin-error-inj.c62
-rw-r--r--drivers/media/cec/core/cec-pin-priv.h9
-rw-r--r--drivers/media/cec/core/cec-pin.c80
-rw-r--r--drivers/media/cec/core/cec-priv.h3
-rw-r--r--drivers/media/cec/i2c/Kconfig11
-rw-r--r--drivers/media/cec/i2c/Makefile1
-rw-r--r--drivers/media/cec/i2c/ch7322.c8
-rw-r--r--drivers/media/cec/i2c/tda9950.c (renamed from drivers/gpu/drm/i2c/tda9950.c)11
-rw-r--r--drivers/media/cec/platform/Kconfig2
-rw-r--r--drivers/media/cec/platform/Makefile2
-rw-r--r--drivers/media/cec/platform/cec-gpio/cec-gpio.c80
-rw-r--r--drivers/media/cec/platform/cros-ec/cros-ec-cec.c419
-rw-r--r--drivers/media/cec/platform/meson/ao-cec-g12a.c6
-rw-r--r--drivers/media/cec/platform/meson/ao-cec.c8
-rw-r--r--drivers/media/cec/platform/s5p/s5p_cec.c7
-rw-r--r--drivers/media/cec/platform/seco/seco-cec.c4
-rw-r--r--drivers/media/cec/platform/sti/stih-cec.c9
-rw-r--r--drivers/media/cec/platform/stm32/stm32-cec.c15
-rw-r--r--drivers/media/cec/platform/tegra/tegra_cec.c10
-rw-r--r--drivers/media/cec/usb/Kconfig1
-rw-r--r--drivers/media/cec/usb/Makefile1
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig14
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile2
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c657
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h51
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c1836
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h118
-rw-r--r--drivers/media/cec/usb/pulse8/pulse8-cec.c13
-rw-r--r--drivers/media/cec/usb/rainshadow/rainshadow-cec.c7
-rw-r--r--drivers/media/common/Kconfig3
-rw-r--r--drivers/media/common/Makefile1
-rw-r--r--drivers/media/common/b2c2/flexcop-common.h4
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c2
-rw-r--r--drivers/media/common/b2c2/flexcop-misc.c13
-rw-r--r--drivers/media/common/b2c2/flexcop-sram.c2
-rw-r--r--drivers/media/common/b2c2/flexcop.c22
-rw-r--r--drivers/media/common/btcx-risc.h29
-rw-r--r--drivers/media/common/cx2341x.c2
-rw-r--r--drivers/media/common/saa7146/Kconfig2
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c44
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c377
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c363
-rw-r--r--drivers/media/common/saa7146/saa7146_vbi.c291
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c957
-rw-r--r--drivers/media/common/siano/smscoreapi.c27
-rw-r--r--drivers/media/common/siano/smscoreapi.h28
-rw-r--r--drivers/media/common/siano/smsdvb-debugfs.c365
-rw-r--r--drivers/media/common/siano/smsdvb-main.c8
-rw-r--r--drivers/media/common/siano/smsendian.c10
-rw-r--r--drivers/media/common/siano/smsir.c2
-rw-r--r--drivers/media/common/uvc.c192
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-core.c73
-rw-r--r--drivers/media/common/videobuf2/frame_vector.c82
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c989
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-contig.c46
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-sg.c39
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dvb.c5
-rw-r--r--drivers/media/common/videobuf2/videobuf2-memops.c6
-rw-r--r--drivers/media/common/videobuf2/videobuf2-v4l2.c222
-rw-r--r--drivers/media/common/videobuf2/videobuf2-vmalloc.c35
-rw-r--r--drivers/media/dvb-core/Makefile2
-rw-r--r--drivers/media/dvb-core/dmxdev.c23
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c51
-rw-r--r--drivers/media/dvb-core/dvb_demux.c36
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c78
-rw-r--r--drivers/media/dvb-core/dvb_math.c141
-rw-r--r--drivers/media/dvb-core/dvb_net.c38
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c40
-rw-r--r--drivers/media/dvb-core/dvb_vb2.c30
-rw-r--r--drivers/media/dvb-core/dvbdev.c292
-rw-r--r--drivers/media/dvb-frontends/Kconfig4
-rw-r--r--drivers/media/dvb-frontends/a8293.c163
-rw-r--r--drivers/media/dvb-frontends/a8293.h3
-rw-r--r--drivers/media/dvb-frontends/af9013.c11
-rw-r--r--drivers/media/dvb-frontends/af9013_priv.h2
-rw-r--r--drivers/media/dvb-frontends/af9033.c9
-rw-r--r--drivers/media/dvb-frontends/af9033_priv.h2
-rw-r--r--drivers/media/dvb-frontends/as102_fe_types.h2
-rw-r--r--drivers/media/dvb-frontends/ascot2e.c2
-rw-r--r--drivers/media/dvb-frontends/atbm8830.c2
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c9
-rw-r--r--drivers/media/dvb-frontends/au8522_dig.c2
-rw-r--r--drivers/media/dvb-frontends/bcm3510.c8
-rw-r--r--drivers/media/dvb-frontends/bcm3510_priv.h6
-rw-r--r--drivers/media/dvb-frontends/cx22700.c2
-rw-r--r--drivers/media/dvb-frontends/cx22702.c2
-rw-r--r--drivers/media/dvb-frontends/cx24110.c6
-rw-r--r--drivers/media/dvb-frontends/cx24110.h8
-rw-r--r--drivers/media/dvb-frontends/cx24113.c2
-rw-r--r--drivers/media/dvb-frontends/cx24116.c9
-rw-r--r--drivers/media/dvb-frontends/cx24117.c2
-rw-r--r--drivers/media/dvb-frontends/cx24120.c6
-rw-r--r--drivers/media/dvb-frontends/cx24123.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2099.c9
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c17
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_priv.h4
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.c17
-rw-r--r--drivers/media/dvb-frontends/cxd2880/Kconfig2
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c14
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c14
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_top.c4
-rw-r--r--drivers/media/dvb-frontends/dib0070.c2
-rw-r--r--drivers/media/dvb-frontends/dib0090.c8
-rw-r--r--drivers/media/dvb-frontends/dib3000mb.c6
-rw-r--r--drivers/media/dvb-frontends/dib3000mc.c2
-rw-r--r--drivers/media/dvb-frontends/dib7000m.c2
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c23
-rw-r--r--drivers/media/dvb-frontends/dib8000.c16
-rw-r--r--drivers/media/dvb-frontends/dib9000.c4
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.c10
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h2
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver.h2
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c87
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c2
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c42
-rw-r--r--drivers/media/dvb-frontends/ds3000.c2
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.c14
-rw-r--r--drivers/media/dvb-frontends/ec100.c2
-rw-r--r--drivers/media/dvb-frontends/helene.c9
-rw-r--r--drivers/media/dvb-frontends/horus3a.c2
-rw-r--r--drivers/media/dvb-frontends/isl6405.c2
-rw-r--r--drivers/media/dvb-frontends/isl6421.c2
-rw-r--r--drivers/media/dvb-frontends/isl6423.c2
-rw-r--r--drivers/media/dvb-frontends/itd1000.c2
-rw-r--r--drivers/media/dvb-frontends/ix2505v.c2
-rw-r--r--drivers/media/dvb-frontends/l64781.c2
-rw-r--r--drivers/media/dvb-frontends/lg2160.c2
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.c4
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c20
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c17
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx.c2
-rw-r--r--drivers/media/dvb-frontends/lnbh25.c2
-rw-r--r--drivers/media/dvb-frontends/lnbp21.c4
-rw-r--r--drivers/media/dvb-frontends/lnbp22.c2
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c28
-rw-r--r--drivers/media/dvb-frontends/m88ds3103_priv.h2
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c2
-rw-r--r--drivers/media/dvb-frontends/mb86a16.c22
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c7
-rw-r--r--drivers/media/dvb-frontends/mn88443x.c21
-rw-r--r--drivers/media/dvb-frontends/mn88472.c9
-rw-r--r--drivers/media/dvb-frontends/mn88472_priv.h2
-rw-r--r--drivers/media/dvb-frontends/mn88473.c9
-rw-r--r--drivers/media/dvb-frontends/mn88473_priv.h2
-rw-r--r--drivers/media/dvb-frontends/mt312.c2
-rw-r--r--drivers/media/dvb-frontends/mt352.c2
-rw-r--r--drivers/media/dvb-frontends/mxl5xx.c26
-rw-r--r--drivers/media/dvb-frontends/mxl5xx_defs.h4
-rw-r--r--drivers/media/dvb-frontends/mxl692.c9
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c2
-rw-r--r--drivers/media/dvb-frontends/nxt6000.c2
-rw-r--r--drivers/media/dvb-frontends/or51132.c4
-rw-r--r--drivers/media/dvb-frontends/or51211.c4
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c13
-rw-r--r--drivers/media/dvb-frontends/rtl2830_priv.h2
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c13
-rw-r--r--drivers/media/dvb-frontends/rtl2832_priv.h2
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c15
-rw-r--r--drivers/media/dvb-frontends/s5h1409.c2
-rw-r--r--drivers/media/dvb-frontends/s5h1411.c2
-rw-r--r--drivers/media/dvb-frontends/s5h1420.c2
-rw-r--r--drivers/media/dvb-frontends/s5h1432.c2
-rw-r--r--drivers/media/dvb-frontends/s921.c2
-rw-r--r--drivers/media/dvb-frontends/si2165.c16
-rw-r--r--drivers/media/dvb-frontends/si2168.c11
-rw-r--r--drivers/media/dvb-frontends/si21xx.c2
-rw-r--r--drivers/media/dvb-frontends/sp2.c8
-rw-r--r--drivers/media/dvb-frontends/sp887x.c2
-rw-r--r--drivers/media/dvb-frontends/stb0899_algo.c2
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.c4
-rw-r--r--drivers/media/dvb-frontends/stb6000.c2
-rw-r--r--drivers/media/dvb-frontends/stb6100.c2
-rw-r--r--drivers/media/dvb-frontends/stv0288.c7
-rw-r--r--drivers/media/dvb-frontends/stv0297.c2
-rw-r--r--drivers/media/dvb-frontends/stv0299.c4
-rw-r--r--drivers/media/dvb-frontends/stv0367.c42
-rw-r--r--drivers/media/dvb-frontends/stv0367_priv.h3
-rw-r--r--drivers/media/dvb-frontends/stv0900_core.c2
-rw-r--r--drivers/media/dvb-frontends/stv090x.c47
-rw-r--r--drivers/media/dvb-frontends/stv0910.c5
-rw-r--r--drivers/media/dvb-frontends/stv6110.c2
-rw-r--r--drivers/media/dvb-frontends/stv6110x.c10
-rw-r--r--drivers/media/dvb-frontends/stv6110x_priv.h8
-rw-r--r--drivers/media/dvb-frontends/stv6111.c2
-rw-r--r--drivers/media/dvb-frontends/tc90522.c9
-rw-r--r--drivers/media/dvb-frontends/tda10021.c2
-rw-r--r--drivers/media/dvb-frontends/tda10023.c2
-rw-r--r--drivers/media/dvb-frontends/tda1002x.h2
-rw-r--r--drivers/media/dvb-frontends/tda10048.c23
-rw-r--r--drivers/media/dvb-frontends/tda1004x.c4
-rw-r--r--drivers/media/dvb-frontends/tda10071.c8
-rw-r--r--drivers/media/dvb-frontends/tda10086.c2
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.c6
-rw-r--r--drivers/media/dvb-frontends/tda665x.c2
-rw-r--r--drivers/media/dvb-frontends/tda8083.c2
-rw-r--r--drivers/media/dvb-frontends/tda8083.h8
-rw-r--r--drivers/media/dvb-frontends/tda8261.c2
-rw-r--r--drivers/media/dvb-frontends/tda826x.c2
-rw-r--r--drivers/media/dvb-frontends/ts2020.c20
-rw-r--r--drivers/media/dvb-frontends/tua6100.c2
-rw-r--r--drivers/media/dvb-frontends/ves1820.c2
-rw-r--r--drivers/media/dvb-frontends/ves1x93.c2
-rw-r--r--drivers/media/dvb-frontends/zd1301_demod.c4
-rw-r--r--drivers/media/dvb-frontends/zl10036.c6
-rw-r--r--drivers/media/dvb-frontends/zl10036.h2
-rw-r--r--drivers/media/dvb-frontends/zl10039.c2
-rw-r--r--drivers/media/dvb-frontends/zl10353.c2
-rw-r--r--drivers/media/firewire/firedtv-avc.c4
-rw-r--r--drivers/media/i2c/Kconfig701
-rw-r--r--drivers/media/i2c/Makefile44
-rw-r--r--drivers/media/i2c/ad5820.c40
-rw-r--r--drivers/media/i2c/ad9389b.c1216
-rw-r--r--drivers/media/i2c/adp1653.c37
-rw-r--r--drivers/media/i2c/adv7170.c10
-rw-r--r--drivers/media/i2c/adv7175.c10
-rw-r--r--drivers/media/i2c/adv7180.c490
-rw-r--r--drivers/media/i2c/adv7183.c12
-rw-r--r--drivers/media/i2c/adv7343.c12
-rw-r--r--drivers/media/i2c/adv7393.c11
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c31
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c6
-rw-r--r--drivers/media/i2c/adv748x/adv748x-csi2.c158
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c51
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h7
-rw-r--r--drivers/media/i2c/adv7511-v4l2.c137
-rw-r--r--drivers/media/i2c/adv7604.c175
-rw-r--r--drivers/media/i2c/adv7842.c191
-rw-r--r--drivers/media/i2c/ak7375.c162
-rw-r--r--drivers/media/i2c/ak881x.c11
-rw-r--r--drivers/media/i2c/alvium-csi2.c2546
-rw-r--r--drivers/media/i2c/alvium-csi2.h476
-rw-r--r--drivers/media/i2c/aptina-pll.c1
-rw-r--r--drivers/media/i2c/ar0521.c438
-rw-r--r--drivers/media/i2c/bt819.c12
-rw-r--r--drivers/media/i2c/bt856.c8
-rw-r--r--drivers/media/i2c/bt866.c8
-rw-r--r--drivers/media/i2c/ccs-pll.c71
-rw-r--r--drivers/media/i2c/ccs-pll.h29
-rw-r--r--drivers/media/i2c/ccs/Kconfig6
-rw-r--r--drivers/media/i2c/ccs/ccs-core.c782
-rw-r--r--drivers/media/i2c/ccs/ccs-data.c116
-rw-r--r--drivers/media/i2c/ccs/ccs-quirk.c3
-rw-r--r--drivers/media/i2c/ccs/ccs-quirk.h12
-rw-r--r--drivers/media/i2c/ccs/ccs-reg-access.c208
-rw-r--r--drivers/media/i2c/ccs/ccs-reg-access.h3
-rw-r--r--drivers/media/i2c/ccs/ccs-regs.h906
-rw-r--r--drivers/media/i2c/ccs/ccs.h23
-rw-r--r--drivers/media/i2c/ccs/smiapp-reg-defs.h951
-rw-r--r--drivers/media/i2c/cs3308.c8
-rw-r--r--drivers/media/i2c/cs5345.c8
-rw-r--r--drivers/media/i2c/cs53l32a.c9
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c3706
-rw-r--r--drivers/media/i2c/cx25840/cx25840-ir.c2
-rw-r--r--drivers/media/i2c/ds90ub913.c958
-rw-r--r--drivers/media/i2c/ds90ub953.c1437
-rw-r--r--drivers/media/i2c/ds90ub953.h104
-rw-r--r--drivers/media/i2c/ds90ub960.c5215
-rw-r--r--drivers/media/i2c/dw9714.c78
-rw-r--r--drivers/media/i2c/dw9719.c467
-rw-r--r--drivers/media/i2c/dw9768.c50
-rw-r--r--drivers/media/i2c/dw9807-vcm.c6
-rw-r--r--drivers/media/i2c/et8ek8/Kconfig4
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c67
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_mode.c9
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_reg.h1
-rw-r--r--drivers/media/i2c/gc0308.c1444
-rw-r--r--drivers/media/i2c/gc0310.c783
-rw-r--r--drivers/media/i2c/gc05a2.c1347
-rw-r--r--drivers/media/i2c/gc08a3.c1327
-rw-r--r--drivers/media/i2c/gc2145.c1484
-rw-r--r--drivers/media/i2c/hi556.c495
-rw-r--r--drivers/media/i2c/hi846.c102
-rw-r--r--drivers/media/i2c/hi847.c156
-rw-r--r--drivers/media/i2c/imx111.c1610
-rw-r--r--drivers/media/i2c/imx208.c171
-rw-r--r--drivers/media/i2c/imx214.c1638
-rw-r--r--drivers/media/i2c/imx219.c1738
-rw-r--r--drivers/media/i2c/imx258.c1551
-rw-r--r--drivers/media/i2c/imx274.c93
-rw-r--r--drivers/media/i2c/imx283.c1620
-rw-r--r--drivers/media/i2c/imx290.c1920
-rw-r--r--drivers/media/i2c/imx296.c1160
-rw-r--r--drivers/media/i2c/imx319.c215
-rw-r--r--drivers/media/i2c/imx334.c1053
-rw-r--r--drivers/media/i2c/imx335.c1323
-rw-r--r--drivers/media/i2c/imx355.c225
-rw-r--r--drivers/media/i2c/imx412.c100
-rw-r--r--drivers/media/i2c/imx415.c1482
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c62
-rw-r--r--drivers/media/i2c/isl7998x.c24
-rw-r--r--drivers/media/i2c/ks0127.c19
-rw-r--r--drivers/media/i2c/lm3560.c11
-rw-r--r--drivers/media/i2c/lm3646.c9
-rw-r--r--drivers/media/i2c/lt6911uxe.c707
-rw-r--r--drivers/media/i2c/m52790.c8
-rw-r--r--drivers/media/i2c/m5mols/Kconfig8
-rw-r--r--drivers/media/i2c/m5mols/Makefile4
-rw-r--r--drivers/media/i2c/m5mols/m5mols.h349
-rw-r--r--drivers/media/i2c/m5mols/m5mols_capture.c158
-rw-r--r--drivers/media/i2c/m5mols/m5mols_controls.c625
-rw-r--r--drivers/media/i2c/m5mols/m5mols_core.c1054
-rw-r--r--drivers/media/i2c/m5mols/m5mols_reg.h359
-rw-r--r--drivers/media/i2c/max2175.c12
-rw-r--r--drivers/media/i2c/max9271.h5
-rw-r--r--drivers/media/i2c/max9286.c639
-rw-r--r--drivers/media/i2c/max96714.c1017
-rw-r--r--drivers/media/i2c/max96717.c1104
-rw-r--r--drivers/media/i2c/ml86v7667.c11
-rw-r--r--drivers/media/i2c/msp3400-driver.c32
-rw-r--r--drivers/media/i2c/msp3400-driver.h2
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c2
-rw-r--r--drivers/media/i2c/mt9m001.c35
-rw-r--r--drivers/media/i2c/mt9m032.c893
-rw-r--r--drivers/media/i2c/mt9m111.c76
-rw-r--r--drivers/media/i2c/mt9m114.c2567
-rw-r--r--drivers/media/i2c/mt9p031.c150
-rw-r--r--drivers/media/i2c/mt9t001.c994
-rw-r--r--drivers/media/i2c/mt9t112.c21
-rw-r--r--drivers/media/i2c/mt9v011.c49
-rw-r--r--drivers/media/i2c/mt9v032.c145
-rw-r--r--drivers/media/i2c/mt9v111.c90
-rw-r--r--drivers/media/i2c/noon010pc30.c824
-rw-r--r--drivers/media/i2c/og01a1b.c317
-rw-r--r--drivers/media/i2c/og0ve1b.c816
-rw-r--r--drivers/media/i2c/ov01a10.c961
-rw-r--r--drivers/media/i2c/ov02a10.c71
-rw-r--r--drivers/media/i2c/ov02c10.c1022
-rw-r--r--drivers/media/i2c/ov02e10.c956
-rw-r--r--drivers/media/i2c/ov08d10.c154
-rw-r--r--drivers/media/i2c/ov08x40.c2405
-rw-r--r--drivers/media/i2c/ov13858.c136
-rw-r--r--drivers/media/i2c/ov13b10.c537
-rw-r--r--drivers/media/i2c/ov2640.c48
-rw-r--r--drivers/media/i2c/ov2659.c42
-rw-r--r--drivers/media/i2c/ov2680.c1338
-rw-r--r--drivers/media/i2c/ov2685.c128
-rw-r--r--drivers/media/i2c/ov2735.c1109
-rw-r--r--drivers/media/i2c/ov2740.c769
-rw-r--r--drivers/media/i2c/ov4689.c1045
-rw-r--r--drivers/media/i2c/ov5640.c348
-rw-r--r--drivers/media/i2c/ov5645.c426
-rw-r--r--drivers/media/i2c/ov5647.c102
-rw-r--r--drivers/media/i2c/ov5648.c91
-rw-r--r--drivers/media/i2c/ov5670.c469
-rw-r--r--drivers/media/i2c/ov5675.c265
-rw-r--r--drivers/media/i2c/ov5693.c647
-rw-r--r--drivers/media/i2c/ov5695.c60
-rw-r--r--drivers/media/i2c/ov6211.c793
-rw-r--r--drivers/media/i2c/ov64a40.c3677
-rw-r--r--drivers/media/i2c/ov6650.c1127
-rw-r--r--drivers/media/i2c/ov7251.c87
-rw-r--r--drivers/media/i2c/ov7640.c9
-rw-r--r--drivers/media/i2c/ov7670.c128
-rw-r--r--drivers/media/i2c/ov772x.c47
-rw-r--r--drivers/media/i2c/ov7740.c99
-rw-r--r--drivers/media/i2c/ov8856.c223
-rw-r--r--drivers/media/i2c/ov8858.c1997
-rw-r--r--drivers/media/i2c/ov8865.c125
-rw-r--r--drivers/media/i2c/ov9282.c673
-rw-r--r--drivers/media/i2c/ov9640.c16
-rw-r--r--drivers/media/i2c/ov9650.c101
-rw-r--r--drivers/media/i2c/ov9734.c160
-rw-r--r--drivers/media/i2c/rdacm20.c36
-rw-r--r--drivers/media/i2c/rdacm21.c31
-rw-r--r--drivers/media/i2c/rj54n1cb0c.c30
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c234
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c2
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h13
-rw-r--r--drivers/media/i2c/s5k4ecgx.c1034
-rw-r--r--drivers/media/i2c/s5k5baf.c166
-rw-r--r--drivers/media/i2c/s5k6a3.c63
-rw-r--r--drivers/media/i2c/s5k6aa.c1655
-rw-r--r--drivers/media/i2c/saa6588.c9
-rw-r--r--drivers/media/i2c/saa6752hs.c14
-rw-r--r--drivers/media/i2c/saa7110.c8
-rw-r--r--drivers/media/i2c/saa7115.c21
-rw-r--r--drivers/media/i2c/saa7127.c9
-rw-r--r--drivers/media/i2c/saa717x.c10
-rw-r--r--drivers/media/i2c/saa7185.c8
-rw-r--r--drivers/media/i2c/sony-btf-mpx.c9
-rw-r--r--drivers/media/i2c/sr030pc30.c764
-rw-r--r--drivers/media/i2c/st-mipid02.c591
-rw-r--r--drivers/media/i2c/tc358743.c348
-rw-r--r--drivers/media/i2c/tc358743_regs.h57
-rw-r--r--drivers/media/i2c/tc358746.c1717
-rw-r--r--drivers/media/i2c/tda1997x.c54
-rw-r--r--drivers/media/i2c/tda7432.c8
-rw-r--r--drivers/media/i2c/tda9840.c10
-rw-r--r--drivers/media/i2c/tea6415c.c10
-rw-r--r--drivers/media/i2c/tea6420.c10
-rw-r--r--drivers/media/i2c/thp7312.c2249
-rw-r--r--drivers/media/i2c/ths7303.c25
-rw-r--r--drivers/media/i2c/ths8200.c24
-rw-r--r--drivers/media/i2c/tlv320aic23b.c10
-rw-r--r--drivers/media/i2c/tvaudio.c14
-rw-r--r--drivers/media/i2c/tvp514x.c97
-rw-r--r--drivers/media/i2c/tvp5150.c26
-rw-r--r--drivers/media/i2c/tvp7002.c47
-rw-r--r--drivers/media/i2c/tw2804.c8
-rw-r--r--drivers/media/i2c/tw9900.c781
-rw-r--r--drivers/media/i2c/tw9903.c8
-rw-r--r--drivers/media/i2c/tw9906.c8
-rw-r--r--drivers/media/i2c/tw9910.c16
-rw-r--r--drivers/media/i2c/uda1342.c9
-rw-r--r--drivers/media/i2c/upd64031a.c10
-rw-r--r--drivers/media/i2c/upd64083.c10
-rw-r--r--drivers/media/i2c/vd55g1.c2061
-rw-r--r--drivers/media/i2c/vd56g3.c1582
-rw-r--r--drivers/media/i2c/vgxy61.c1911
-rw-r--r--drivers/media/i2c/video-i2c.c49
-rw-r--r--drivers/media/i2c/vp27smpx.c10
-rw-r--r--drivers/media/i2c/vpx3220.c13
-rw-r--r--drivers/media/i2c/vs6624.c856
-rw-r--r--drivers/media/i2c/vs6624_regs.h325
-rw-r--r--drivers/media/i2c/wm8739.c10
-rw-r--r--drivers/media/i2c/wm8775.c10
-rw-r--r--drivers/media/mc/Kconfig7
-rw-r--r--drivers/media/mc/mc-device.c20
-rw-r--r--drivers/media/mc/mc-devnode.c15
-rw-r--r--drivers/media/mc/mc-entity.c802
-rw-r--r--drivers/media/mc/mc-request.c58
-rw-r--r--drivers/media/mmc/siano/smssdio.c25
-rw-r--r--drivers/media/pci/Kconfig6
-rw-r--r--drivers/media/pci/Makefile5
-rw-r--r--drivers/media/pci/b2c2/flexcop-dma.c17
-rw-r--r--drivers/media/pci/b2c2/flexcop-pci.c2
-rw-r--r--drivers/media/pci/bt8xx/Kconfig4
-rw-r--r--drivers/media/pci/bt8xx/bt848.h8
-rw-r--r--drivers/media/pci/bt8xx/bt878.c9
-rw-r--r--drivers/media/pci/bt8xx/bt878.h3
-rw-r--r--drivers/media/pci/bt8xx/btcx-risc.c153
-rw-r--r--drivers/media/pci/bt8xx/btcx-risc.h9
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c10
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c18
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c1476
-rw-r--r--drivers/media/pci/bt8xx/bttv-gpio.c6
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c8
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c542
-rw-r--r--drivers/media/pci/bt8xx/bttv-vbi.c281
-rw-r--r--drivers/media/pci/bt8xx/bttv.h3
-rw-r--r--drivers/media/pci/bt8xx/bttvp.h109
-rw-r--r--drivers/media/pci/bt8xx/dst.c2
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c4
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.c22
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c13
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c105
-rw-r--r--drivers/media/pci/cx18/Kconfig2
-rw-r--r--drivers/media/pci/cx18/cx18-audio.c2
-rw-r--r--drivers/media/pci/cx18/cx18-audio.h2
-rw-r--r--drivers/media/pci/cx18/cx18-av-audio.c4
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.c6
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.h2
-rw-r--r--drivers/media/pci/cx18/cx18-av-firmware.c2
-rw-r--r--drivers/media/pci/cx18/cx18-av-vbi.c16
-rw-r--r--drivers/media/pci/cx18/cx18-cards.c2
-rw-r--r--drivers/media/pci/cx18/cx18-cards.h2
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c2
-rw-r--r--drivers/media/pci/cx18/cx18-controls.h2
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c15
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h40
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c4
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c100
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.h5
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.c4
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.h2
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.c17
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.h3
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.c2
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.h2
-rw-r--r--drivers/media/pci/cx18/cx18-io.c2
-rw-r--r--drivers/media/pci/cx18/cx18-io.h2
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c475
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.h10
-rw-r--r--drivers/media/pci/cx18/cx18-irq.c4
-rw-r--r--drivers/media/pci/cx18/cx18-irq.h2
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.c31
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.h2
-rw-r--r--drivers/media/pci/cx18/cx18-queue.c15
-rw-r--r--drivers/media/pci/cx18/cx18-queue.h2
-rw-r--r--drivers/media/pci/cx18/cx18-scb.c2
-rw-r--r--drivers/media/pci/cx18/cx18-scb.h4
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c279
-rw-r--r--drivers/media/pci/cx18/cx18-streams.h2
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.c2
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.h2
-rw-r--r--drivers/media/pci/cx18/cx18-version.h2
-rw-r--r--drivers/media/pci/cx18/cx18-video.c2
-rw-r--r--drivers/media/pci/cx18/cx18-video.h2
-rw-r--r--drivers/media/pci/cx18/cx23418.h2
-rw-r--r--drivers/media/pci/cx23885/cx23885-417.c4
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c30
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c46
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c16
-rw-r--r--drivers/media/pci/cx23885/cx23885-vbi.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c32
-rw-r--r--drivers/media/pci/cx23885/cx23885.h1
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.c2
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.c29
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.h1
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.c4
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.h3
-rw-r--r--drivers/media/pci/cx88/cx88-blackbird.c4
-rw-r--r--drivers/media/pci/cx88/cx88-dsp.c2
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c4
-rw-r--r--drivers/media/pci/cx88/cx88-input.c5
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c11
-rw-r--r--drivers/media/pci/cx88/cx88-video.c50
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c5
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-dummy-fe.c2
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-main.c2
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h2
-rw-r--r--drivers/media/pci/dm1105/Kconfig2
-rw-r--r--drivers/media/pci/dm1105/dm1105.c1
-rw-r--r--drivers/media/pci/dt3155/dt3155.c6
-rw-r--r--drivers/media/pci/dt3155/dt3155.h1
-rw-r--r--drivers/media/pci/intel/Kconfig21
-rw-r--r--drivers/media/pci/intel/Makefile6
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c880
-rw-r--r--drivers/media/pci/intel/ipu3/Kconfig19
-rw-r--r--drivers/media/pci/intel/ipu3/Makefile3
-rw-r--r--drivers/media/pci/intel/ipu3/cio2-bridge.c462
-rw-r--r--drivers/media/pci/intel/ipu3/cio2-bridge.h144
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2-main.c2058
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c1989
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.h12
-rw-r--r--drivers/media/pci/intel/ipu6/Kconfig18
-rw-r--r--drivers/media/pci/intel/ipu6/Makefile23
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.c159
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.h55
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.c910
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.h85
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.c362
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.h105
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.c459
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.h43
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-com.c413
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-com.h47
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.c487
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.h596
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c647
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h78
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c536
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c242
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c720
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.c850
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.h71
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c425
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h55
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.c1404
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.h135
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.c1380
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h202
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.c806
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.h73
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h224
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h172
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-regs.h179
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c846
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.h342
-rw-r--r--drivers/media/pci/intel/ivsc/Kconfig22
-rw-r--r--drivers/media/pci/intel/ivsc/Makefile9
-rw-r--r--drivers/media/pci/intel/ivsc/mei_ace.c582
-rw-r--r--drivers/media/pci/intel/ivsc/mei_csi.c791
-rw-r--r--drivers/media/pci/ivtv/Kconfig4
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c123
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.h25
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.c108
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c142
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.h8
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.c16
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.c25
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.c12
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-version.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.c21
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c18
-rw-r--r--drivers/media/pci/mantis/Kconfig2
-rw-r--r--drivers/media/pci/mantis/hopper_cards.c2
-rw-r--r--drivers/media/pci/mantis/mantis_cards.c2
-rw-r--r--drivers/media/pci/mantis/mantis_common.h2
-rw-r--r--drivers/media/pci/mantis/mantis_core.h43
-rw-r--r--drivers/media/pci/mantis/mantis_dma.c4
-rw-r--r--drivers/media/pci/mantis/mantis_dma.h2
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.c12
-rw-r--r--drivers/media/pci/meye/Kconfig16
-rw-r--r--drivers/media/pci/meye/Makefile2
-rw-r--r--drivers/media/pci/meye/meye.c1814
-rw-r--r--drivers/media/pci/meye/meye.h311
-rw-r--r--drivers/media/pci/mgb4/Kconfig18
-rw-r--r--drivers/media/pci/mgb4/Makefile6
-rw-r--r--drivers/media/pci/mgb4/mgb4_cmt.c248
-rw-r--r--drivers/media/pci/mgb4/mgb4_cmt.h17
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.c700
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.h75
-rw-r--r--drivers/media/pci/mgb4/mgb4_dma.c123
-rw-r--r--drivers/media/pci/mgb4/mgb4_dma.h18
-rw-r--r--drivers/media/pci/mgb4/mgb4_i2c.c140
-rw-r--r--drivers/media/pci/mgb4/mgb4_i2c.h35
-rw-r--r--drivers/media/pci/mgb4/mgb4_io.h54
-rw-r--r--drivers/media/pci/mgb4/mgb4_regs.c31
-rw-r--r--drivers/media/pci/mgb4/mgb4_regs.h35
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs.h18
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_in.c772
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_out.c741
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_pci.c71
-rw-r--r--drivers/media/pci/mgb4/mgb4_trigger.c209
-rw-r--r--drivers/media/pci/mgb4/mgb4_trigger.h8
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.c1037
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.h69
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c847
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.h65
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_core.c26
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c4
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_spi.c50
-rw-r--r--drivers/media/pci/ngene/ngene-core.c26
-rw-r--r--drivers/media/pci/ngene/ngene.h83
-rw-r--r--drivers/media/pci/pt1/pt1.c2
-rw-r--r--drivers/media/pci/pt3/pt3.c35
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c10
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c3
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c46
-rw-r--r--drivers/media/pci/saa7134/saa7134-dvb.c9
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c7
-rw-r--r--drivers/media/pci/saa7134/saa7134-go7007.c1
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c4
-rw-r--r--drivers/media/pci/saa7134/saa7134-ts.c3
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c3
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c418
-rw-r--r--drivers/media/pci/saa7134/saa7134.h17
-rw-r--r--drivers/media/pci/saa7146/hexium_gemini.c23
-rw-r--r--drivers/media/pci/saa7146/hexium_orion.c24
-rw-r--r--drivers/media/pci/saa7146/mxb.c53
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c20
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c28
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c12
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-encoder.c32
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c1
-rw-r--r--drivers/media/pci/saa7164/saa7164-vbi.c27
-rw-r--r--drivers/media/pci/saa7164/saa7164.h14
-rw-r--r--drivers/media/pci/smipcie/smipcie-main.c18
-rw-r--r--drivers/media/pci/smipcie/smipcie.h3
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-core.c19
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-g723.c39
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-gpio.c20
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-offsets.h10
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-p2m.c8
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c4
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-v4l2.c2
-rw-r--r--drivers/media/pci/sta2x11/Kconfig16
-rw-r--r--drivers/media/pci/sta2x11/Makefile2
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c1269
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.h29
-rw-r--r--drivers/media/pci/ttpci/budget-av.c621
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c552
-rw-r--r--drivers/media/pci/ttpci/budget-core.c59
-rw-r--r--drivers/media/pci/ttpci/budget.c193
-rw-r--r--drivers/media/pci/ttpci/budget.h26
-rw-r--r--drivers/media/pci/tw5864/tw5864-core.c15
-rw-r--r--drivers/media/pci/tw5864/tw5864-video.c17
-rw-r--r--drivers/media/pci/tw5864/tw5864.h7
-rw-r--r--drivers/media/pci/tw68/tw68-core.c4
-rw-r--r--drivers/media/pci/tw68/tw68-reg.h2
-rw-r--r--drivers/media/pci/tw68/tw68-risc.c2
-rw-r--r--drivers/media/pci/tw68/tw68-video.c27
-rw-r--r--drivers/media/pci/tw68/tw68.h2
-rw-r--r--drivers/media/pci/tw686x/tw686x-audio.c2
-rw-r--r--drivers/media/pci/tw686x/tw686x-core.c4
-rw-r--r--drivers/media/pci/tw686x/tw686x-video.c9
-rw-r--r--drivers/media/pci/zoran/Kconfig (renamed from drivers/staging/media/zoran/Kconfig)0
-rw-r--r--drivers/media/pci/zoran/Makefile (renamed from drivers/staging/media/zoran/Makefile)0
-rw-r--r--drivers/media/pci/zoran/videocodec.c (renamed from drivers/staging/media/zoran/videocodec.c)7
-rw-r--r--drivers/media/pci/zoran/videocodec.h325
-rw-r--r--drivers/media/pci/zoran/zoran.h (renamed from drivers/staging/media/zoran/zoran.h)38
-rw-r--r--drivers/media/pci/zoran/zoran_card.c (renamed from drivers/staging/media/zoran/zoran_card.c)62
-rw-r--r--drivers/media/pci/zoran/zoran_card.h27
-rw-r--r--drivers/media/pci/zoran/zoran_device.c (renamed from drivers/staging/media/zoran/zoran_device.c)37
-rw-r--r--drivers/media/pci/zoran/zoran_device.h56
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c (renamed from drivers/staging/media/zoran/zoran_driver.c)102
-rw-r--r--drivers/media/pci/zoran/zr36016.c406
-rw-r--r--drivers/media/pci/zoran/zr36016.h (renamed from drivers/staging/media/zoran/zr36016.h)0
-rw-r--r--drivers/media/pci/zoran/zr36050.c (renamed from drivers/staging/media/zoran/zr36050.c)184
-rw-r--r--drivers/media/pci/zoran/zr36050.h (renamed from drivers/staging/media/zoran/zr36050.h)0
-rw-r--r--drivers/media/pci/zoran/zr36057.h154
-rw-r--r--drivers/media/pci/zoran/zr36060.c (renamed from drivers/staging/media/zoran/zr36060.c)9
-rw-r--r--drivers/media/pci/zoran/zr36060.h203
-rw-r--r--drivers/media/platform/Kconfig8
-rw-r--r--drivers/media/platform/Makefile8
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c191
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.c2
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.h2
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.h7
-rw-r--r--drivers/media/platform/amlogic/Kconfig1
-rw-r--r--drivers/media/platform/amlogic/Makefile2
-rw-r--r--drivers/media/platform/amlogic/c3/Kconfig5
-rw-r--r--drivers/media/platform/amlogic/c3/Makefile5
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Kconfig19
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Makefile10
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c804
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-common.h340
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-core.c641
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c421
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c930
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h618
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c892
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c326
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c842
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c827
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d.c37
-rw-r--r--drivers/media/platform/amphion/vdec.c633
-rw-r--r--drivers/media/platform/amphion/venc.c93
-rw-r--r--drivers/media/platform/amphion/vpu.h23
-rw-r--r--drivers/media/platform/amphion/vpu_cmds.c83
-rw-r--r--drivers/media/platform/amphion/vpu_codec.h3
-rw-r--r--drivers/media/platform/amphion/vpu_color.c79
-rw-r--r--drivers/media/platform/amphion/vpu_core.c147
-rw-r--r--drivers/media/platform/amphion/vpu_core.h1
-rw-r--r--drivers/media/platform/amphion/vpu_dbg.c79
-rw-r--r--drivers/media/platform/amphion/vpu_defs.h14
-rw-r--r--drivers/media/platform/amphion/vpu_drv.c42
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.c230
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.h14
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c122
-rw-r--r--drivers/media/platform/amphion/vpu_malone.h1
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.c15
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.h1
-rw-r--r--drivers/media/platform/amphion/vpu_msgs.c47
-rw-r--r--drivers/media/platform/amphion/vpu_rpc.c2
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c280
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.h24
-rw-r--r--drivers/media/platform/amphion/vpu_windsor.c13
-rw-r--r--drivers/media/platform/arm/Kconfig5
-rw-r--r--drivers/media/platform/arm/Makefile2
-rw-r--r--drivers/media/platform/arm/mali-c55/Kconfig18
-rw-r--r--drivers/media/platform/arm/mali-c55/Makefile11
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-capture.c959
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-common.h310
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c917
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c665
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-params.c819
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-registers.h449
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-resizer.c1156
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-stats.c323
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-tpg.c437
-rw-r--r--drivers/media/platform/aspeed/Kconfig1
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c569
-rw-r--r--drivers/media/platform/atmel/Kconfig51
-rw-r--r--drivers/media/platform/atmel/Makefile7
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c61
-rw-r--r--drivers/media/platform/atmel/atmel-isi.h2
-rw-r--r--drivers/media/platform/broadcom/Kconfig23
-rw-r--r--drivers/media/platform/broadcom/Makefile3
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam-regs.h246
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c2757
-rw-r--r--drivers/media/platform/cadence/Kconfig2
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c510
-rw-r--r--drivers/media/platform/cadence/cdns-csi2tx.c15
-rw-r--r--drivers/media/platform/chips-media/Kconfig18
-rw-r--r--drivers/media/platform/chips-media/Makefile6
-rw-r--r--drivers/media/platform/chips-media/coda/Kconfig18
-rw-r--r--drivers/media/platform/chips-media/coda/Makefile6
-rw-r--r--drivers/media/platform/chips-media/coda/coda-bit.c (renamed from drivers/media/platform/chips-media/coda-bit.c)18
-rw-r--r--drivers/media/platform/chips-media/coda/coda-common.c (renamed from drivers/media/platform/chips-media/coda-common.c)72
-rw-r--r--drivers/media/platform/chips-media/coda/coda-gdi.c (renamed from drivers/media/platform/chips-media/coda-gdi.c)0
-rw-r--r--drivers/media/platform/chips-media/coda/coda-h264.c (renamed from drivers/media/platform/chips-media/coda-h264.c)0
-rw-r--r--drivers/media/platform/chips-media/coda/coda-jpeg.c (renamed from drivers/media/platform/chips-media/coda-jpeg.c)29
-rw-r--r--drivers/media/platform/chips-media/coda/coda-mpeg2.c (renamed from drivers/media/platform/chips-media/coda-mpeg2.c)0
-rw-r--r--drivers/media/platform/chips-media/coda/coda-mpeg4.c (renamed from drivers/media/platform/chips-media/coda-mpeg4.c)0
-rw-r--r--drivers/media/platform/chips-media/coda/coda.h (renamed from drivers/media/platform/chips-media/coda.h)0
-rw-r--r--drivers/media/platform/chips-media/coda/coda_regs.h (renamed from drivers/media/platform/chips-media/coda_regs.h)0
-rw-r--r--drivers/media/platform/chips-media/coda/imx-vdoa.c (renamed from drivers/media/platform/chips-media/imx-vdoa.c)6
-rw-r--r--drivers/media/platform/chips-media/coda/imx-vdoa.h (renamed from drivers/media/platform/chips-media/imx-vdoa.h)0
-rw-r--r--drivers/media/platform/chips-media/coda/trace.h175
-rw-r--r--drivers/media/platform/chips-media/trace.h175
-rw-r--r--drivers/media/platform/chips-media/wave5/Kconfig15
-rw-r--r--drivers/media/platform/chips-media/wave5/Makefile10
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.c243
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.h36
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-hw.c2762
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-regdefine.h737
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.c210
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.h35
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c1911
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c1840
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.c397
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.h85
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.c997
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.h878
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h102
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuerror.h292
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5.h123
-rw-r--r--drivers/media/platform/imagination/Kconfig14
-rw-r--r--drivers/media/platform/imagination/Makefile3
-rw-r--r--drivers/media/platform/imagination/e5010-core-regs.h585
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc-hw.c267
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc-hw.h42
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.c1630
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.h173
-rw-r--r--drivers/media/platform/imagination/e5010-mmu-regs.h311
-rw-r--r--drivers/media/platform/intel/pxa_camera.c117
-rw-r--r--drivers/media/platform/m2m-deinterlace.c43
-rw-r--r--drivers/media/platform/marvell/Kconfig6
-rw-r--r--drivers/media/platform/marvell/cafe-driver.c20
-rw-r--r--drivers/media/platform/marvell/mcam-core.c39
-rw-r--r--drivers/media/platform/marvell/mcam-core.h5
-rw-r--r--drivers/media/platform/marvell/mmp-driver.c49
-rw-r--r--drivers/media/platform/mediatek/Kconfig1
-rw-r--r--drivers/media/platform/mediatek/Makefile1
-rw-r--r--drivers/media/platform/mediatek/jpeg/Makefile14
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c796
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h168
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c362
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h7
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c17
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h9
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c267
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h7
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c7
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.c5
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c33
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c2
-rw-r--r--drivers/media/platform/mediatek/mdp3/Kconfig19
-rw-r--r--drivers/media/platform/mediatek/mdp3/Makefile6
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c1418
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_aal.h25
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h19
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_color.h31
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_fg.h23
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_hdr.h31
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_merge.h25
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_ovl.h25
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_pad.h21
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h89
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h41
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_tdshp.h34
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h47
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h63
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_sm_mt8183.h144
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_sm_mt8195.h283
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h154
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h23
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c724
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h43
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c2020
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h264
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c425
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h153
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c738
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h47
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c514
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h378
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-type.h53
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c288
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h63
-rw-r--r--drivers/media/platform/mediatek/vcodec/Kconfig1
-rw-r--r--drivers/media/platform/mediatek/vcodec/Makefile47
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/Makefile21
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_cmn_drv.h147
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c231
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.h74
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c)21
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h)8
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h)14
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c92
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c132
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.c68
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.h19
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c174
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.h75
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/Makefile25
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c)300
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.h (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h)11
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c605
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h347
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c)41
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h)9
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c)45
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.h17
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c621
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c918
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c2215
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c)91
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c)4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.h)21
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c)100
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c1323
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c1093
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c)86
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c)96
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c)152
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c)173
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_base.h (renamed from drivers/media/platform/mediatek/vcodec/vdec_drv_base.h)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.c (renamed from drivers/media/platform/mediatek/vcodec/vdec_drv_if.c)20
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.h (renamed from drivers/media/platform/mediatek/vcodec/vdec_drv_if.h)12
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h (renamed from drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.c372
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h191
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c320
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h (renamed from drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h)11
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/Makefile11
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c)350
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h)13
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c (renamed from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c)130
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h250
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c109
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h18
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c819
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_vp8_if.c (renamed from drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c)69
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_base.h (renamed from drivers/media/platform/mediatek/vcodec/venc_drv_base.h)4
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c (renamed from drivers/media/platform/mediatek/vcodec/venc_drv_if.c)21
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h (renamed from drivers/media/platform/mediatek/vcodec/venc_drv_if.h)13
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h (renamed from drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h)24
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c382
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.h (renamed from drivers/media/platform/mediatek/vcodec/venc_vpu_if.h)3
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c523
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h17
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c610
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c508
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h538
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c90
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h17
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c73
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c112
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c43
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h19
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c135
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h63
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c823
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c292
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h155
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c297
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c708
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_vpu_if.c293
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.c19
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.h2
-rw-r--r--drivers/media/platform/microchip/Kconfig61
-rw-r--r--drivers/media/platform/microchip/Makefile9
-rw-r--r--drivers/media/platform/microchip/microchip-csi2dc.c (renamed from drivers/media/platform/atmel/microchip-csi2dc.c)40
-rw-r--r--drivers/media/platform/microchip/microchip-isc-base.c1970
-rw-r--r--drivers/media/platform/microchip/microchip-isc-clk.c311
-rw-r--r--drivers/media/platform/microchip/microchip-isc-regs.h413
-rw-r--r--drivers/media/platform/microchip/microchip-isc-scaler.c271
-rw-r--r--drivers/media/platform/microchip/microchip-isc.h400
-rw-r--r--drivers/media/platform/microchip/microchip-sama5d2-isc.c673
-rw-r--r--drivers/media/platform/microchip/microchip-sama7g5-isc.c636
-rw-r--r--drivers/media/platform/nuvoton/Kconfig15
-rw-r--r--drivers/media/platform/nuvoton/Makefile2
-rw-r--r--drivers/media/platform/nuvoton/npcm-regs.h152
-rw-r--r--drivers/media/platform/nuvoton/npcm-video.c1826
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/Kconfig1
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c8
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/h264.c19
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/iommu.c7
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/trace.h2
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/v4l2.c51
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.c7
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.h1
-rw-r--r--drivers/media/platform/nxp/Kconfig37
-rw-r--r--drivers/media/platform/nxp/Makefile5
-rw-r--r--drivers/media/platform/nxp/dw100/Kconfig16
-rw-r--r--drivers/media/platform/nxp/dw100/Makefile3
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c1693
-rw-r--r--drivers/media/platform/nxp/dw100/dw100_regs.h117
-rw-r--r--drivers/media/platform/nxp/fsl-viu.c1599
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c23
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h18
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c1395
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h27
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c784
-rw-r--r--drivers/media/platform/nxp/imx-pxp.c381
-rw-r--r--drivers/media/platform/nxp/imx-pxp.h9
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c (renamed from drivers/staging/media/imx/imx7-media-csi.c)562
-rw-r--r--drivers/media/platform/nxp/imx8-isi/Kconfig22
-rw-r--r--drivers/media/platform/nxp/imx8-isi/Makefile8
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c581
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h416
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c507
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c130
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c97
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c649
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c856
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c870
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h418
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c1469
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c (renamed from drivers/staging/media/imx/imx8mq-mipi-csi2.c)358
-rw-r--r--drivers/media/platform/nxp/mx2_emmaprp.c37
-rw-r--r--drivers/media/platform/qcom/Kconfig1
-rw-r--r--drivers/media/platform/qcom/Makefile1
-rw-r--r--drivers/media/platform/qcom/camss/Makefile10
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-340.c190
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-1.c151
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-7.c202
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-680.c422
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.c501
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen3.c351
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen3.h25
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c739
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h110
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c6
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c1139
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c198
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h44
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.c91
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.h62
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c76
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.h4
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-170.c772
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-17x.c595
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-340.c320
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c61
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c71
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-8.c66
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-480.c395
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-680.c244
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.c21
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen3.c193
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-vbif.c31
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-vbif.h19
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c1075
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h127
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c402
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.h4
-rw-r--r--drivers/media/platform/qcom/camss/camss.c4065
-rw-r--r--drivers/media/platform/qcom/camss/camss.h76
-rw-r--r--drivers/media/platform/qcom/iris/Kconfig13
-rw-r--r--drivers/media/platform/qcom/iris/Makefile32
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c795
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h123
-rw-r--r--drivers/media/platform/qcom/iris/iris_common.c235
-rw-r--r--drivers/media/platform/qcom/iris/iris_common.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.c98
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.h125
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c917
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h37
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c109
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.c176
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h156
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.h16
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c1089
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h551
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c709
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.h41
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c1216
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h197
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c292
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h125
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c984
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.c317
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.h182
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h112
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h266
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen1.c417
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c1080
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h23
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sc7280.h26
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8650.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8750.h22
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.c140
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c397
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c131
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c277
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.h146
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c127
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.h55
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c343
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.h19
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c515
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.h25
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.c616
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.h27
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c718
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu2.c47
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu3x.c473
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c1555
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h153
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c389
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h37
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_register_defines.h17
-rw-r--r--drivers/media/platform/qcom/venus/Kconfig3
-rw-r--r--drivers/media/platform/qcom/venus/core.c336
-rw-r--r--drivers/media/platform/qcom/venus/core.h108
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c105
-rw-r--r--drivers/media/platform/qcom/venus/firmware.h2
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c69
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c28
-rw-r--r--drivers/media/platform/qcom/venus/hfi.h2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c50
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.h24
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h101
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c132
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.h14
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c117
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs.h3
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c57
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.c27
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.h34
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v4.c188
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v6.c33
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c129
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus_io.h4
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c193
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c200
-rw-r--r--drivers/media/platform/qcom/venus/vdec.h1
-rw-r--r--drivers/media/platform/qcom/venus/vdec_ctrls.c7
-rw-r--r--drivers/media/platform/qcom/venus/venc.c303
-rw-r--r--drivers/media/platform/qcom/venus/venc.h1
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c170
-rw-r--r--drivers/media/platform/raspberrypi/Kconfig6
-rw-r--r--drivers/media/platform/raspberrypi/Makefile4
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Kconfig14
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Makefile6
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be.c1796
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h519
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/Kconfig15
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/Makefile6
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h332
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h202
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c2506
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.h43
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.c586
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.h89
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/dphy.c181
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/dphy.h27
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c605
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h53
-rw-r--r--drivers/media/platform/renesas/Kconfig14
-rw-r--r--drivers/media/platform/renesas/Makefile5
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c2659
-rw-r--r--drivers/media/platform/renesas/rcar-fcp.c40
-rw-r--r--drivers/media/platform/renesas/rcar-isp.c535
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Makefile4
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c593
-rw-r--r--drivers/media/platform/renesas/rcar-vin/Kconfig16
-rw-r--r--drivers/media/platform/renesas/rcar-vin/Makefile1
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c803
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-csi2.c1606
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c442
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c608
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h82
-rw-r--r--drivers/media/platform/renesas/rcar_drif.c46
-rw-r--r--drivers/media/platform/renesas/rcar_fdp1.c58
-rw-r--r--drivers/media/platform/renesas/rcar_jpu.c113
-rw-r--r--drivers/media/platform/renesas/renesas-ceu.c48
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/Kconfig33
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/Makefile6
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c442
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h111
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h205
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c1049
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c379
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c1204
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/Makefile5
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c251
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c376
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c531
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h130
-rw-r--r--drivers/media/platform/renesas/sh_vou.c21
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h9
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c65
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c6
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c32
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c86
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.h10
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c230
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c215
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h65
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.c32
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.c20
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c102
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c25
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.c121
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.h29
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.c18
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c311
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.h56
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h43
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c170
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c108
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.h7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c65
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c80
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c30
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c305
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.c634
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.h16
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c129
-rw-r--r--drivers/media/platform/rockchip/Kconfig2
-rw-r--r--drivers/media/platform/rockchip/Makefile2
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c178
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c148
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.h2
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c244
-rw-r--r--drivers/media/platform/rockchip/rga/rga.h45
-rw-r--r--drivers/media/platform/rockchip/rkcif/Kconfig18
-rw-r--r--drivers/media/platform/rockchip/rkcif/Makefile8
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c865
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h25
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c777
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h23
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-common.h250
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-dev.c303
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.c442
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.h31
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-regs.h153
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.c636
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.h32
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Kconfig1
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c327
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.c14
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h168
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c150
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c6
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c269
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c595
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c1603
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h773
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c364
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c53
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Kconfig16
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Makefile3
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c (renamed from drivers/staging/media/rkvdec/rkvdec-h264.c)68
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c1848
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c820
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h (renamed from drivers/staging/media/rkvdec/rkvdec-regs.h)4
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c (renamed from drivers/staging/media/rkvdec/rkvdec-vp9.c)8
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c1411
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.h161
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.c14
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.h7
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c39
-rw-r--r--drivers/media/platform/samsung/exynos4-is/Kconfig2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/common.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-capture.c99
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.c47
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.h30
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c131
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c7
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.c9
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.h1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c44
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.h3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c23
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp.c24
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c13
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h12
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c57
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.h3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-m2m.c44
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-reg.c38
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-reg.h10
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c81
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.h8
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.c20
-rw-r--r--drivers/media/platform/samsung/s3c-camif/Kconfig8
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-capture.c65
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.c26
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.c49
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c69
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h12
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c5
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h1
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c19
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h4
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h52
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h3
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c239
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c8
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c6
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c41
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h55
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c44
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c116
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h3
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c214
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h3
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c7
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h14
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c40
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c355
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h9
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c51
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h8
-rw-r--r--drivers/media/platform/st/Makefile1
-rw-r--r--drivers/media/platform/st/sti/Kconfig1
-rw-r--r--drivers/media/platform/st/sti/Makefile1
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-debug.c8
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c62
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Kconfig29
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Makefile8
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c262
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h60
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c1186
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h285
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c244
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h18
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c235
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h17
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.c8
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c20
-rw-r--r--drivers/media/platform/st/sti/delta/delta-v4l2.c49
-rw-r--r--drivers/media/platform/st/sti/hva/hva-h264.c8
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.c3
-rw-r--r--drivers/media/platform/st/sti/hva/hva-v4l2.c57
-rw-r--r--drivers/media/platform/st/sti/hva/hva.h2
-rw-r--r--drivers/media/platform/st/stm32/Kconfig30
-rw-r--r--drivers/media/platform/st/stm32/Makefile2
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.c49
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.h2
-rw-r--r--drivers/media/platform/st/stm32/stm32-csi.c1141
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c172
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/Makefile4
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c964
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c594
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c111
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h217
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c672
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c540
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/Kconfig2
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c31
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h1
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c14
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c17
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/Kconfig12
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/Makefile2
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c1062
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h153
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c872
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h69
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c1100
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h89
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h362
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c685
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h38
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig4
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c83
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig3
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c86
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/Kconfig2
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/sun8i-di.c95
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/Kconfig2
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c26
-rw-r--r--drivers/media/platform/synopsys/Kconfig3
-rw-r--r--drivers/media/platform/synopsys/Makefile2
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Kconfig36
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Makefile4
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c2746
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h396
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c275
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h43
-rw-r--r--drivers/media/platform/ti/Kconfig13
-rw-r--r--drivers/media/platform/ti/Makefile1
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.c108
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.h4
-rw-r--r--drivers/media/platform/ti/cal/cal-camerarx.c438
-rw-r--r--drivers/media/platform/ti/cal/cal-video.c235
-rw-r--r--drivers/media/platform/ti/cal/cal.c285
-rw-r--r--drivers/media/platform/ti/cal/cal.h20
-rw-r--r--drivers/media/platform/ti/davinci/Kconfig65
-rw-r--r--drivers/media/platform/ti/davinci/Makefile7
-rw-r--r--drivers/media/platform/ti/davinci/ccdc_hw_device.h80
-rw-r--r--drivers/media/platform/ti/davinci/dm355_ccdc.c934
-rw-r--r--drivers/media/platform/ti/davinci/dm355_ccdc_regs.h297
-rw-r--r--drivers/media/platform/ti/davinci/dm644x_ccdc.c879
-rw-r--r--drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h140
-rw-r--r--drivers/media/platform/ti/davinci/isif.c1127
-rw-r--r--drivers/media/platform/ti/davinci/isif_regs.h256
-rw-r--r--drivers/media/platform/ti/davinci/vpbe.c840
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_display.c1510
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_osd.c1582
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_osd_regs.h352
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_venc.c676
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_venc_regs.h165
-rw-r--r--drivers/media/platform/ti/davinci/vpfe_capture.c1902
-rw-r--r--drivers/media/platform/ti/davinci/vpif.c13
-rw-r--r--drivers/media/platform/ti/davinci/vpif.h60
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c76
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.h2
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c27
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.h6
-rw-r--r--drivers/media/platform/ti/davinci/vpss.c529
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/Makefile2
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c1210
-rw-r--r--drivers/media/platform/ti/omap/omap_vout.c18
-rw-r--r--drivers/media/platform/ti/omap/omap_voutdef.h2
-rw-r--r--drivers/media/platform/ti/omap/omap_voutlib.c2
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c150
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.h17
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c53
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.c15
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.c11
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsiphy.c15
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a_aewb.c2
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a_af.c2
-rw-r--r--drivers/media/platform/ti/omap3isp/isphist.c2
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.c18
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.c21
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.c15
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.h3
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c88
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.h17
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c33
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.h3
-rw-r--r--drivers/media/platform/ti/vpe/vpe.c34
-rw-r--r--drivers/media/platform/verisilicon/Kconfig70
-rw-r--r--drivers/media/platform/verisilicon/Makefile44
-rw-r--r--drivers/media/platform/verisilicon/hantro.h (renamed from drivers/staging/media/hantro/hantro.h)77
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c (renamed from drivers/staging/media/hantro/hantro_drv.c)216
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1.c (renamed from drivers/staging/media/hantro/hantro_g1.c)0
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_h264_dec.c (renamed from drivers/staging/media/hantro/hantro_g1_h264_dec.c)2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c (renamed from drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c)2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_regs.h (renamed from drivers/staging/media/hantro/hantro_g1_regs.h)2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_vp8_dec.c (renamed from drivers/staging/media/hantro/hantro_g1_vp8_dec.c)0
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2.c137
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c (renamed from drivers/staging/media/hantro/hantro_g2_hevc_dec.c)58
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_regs.h (renamed from drivers/staging/media/hantro/hantro_g2_regs.h)17
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c (renamed from drivers/staging/media/hantro/hantro_g2_vp9_dec.c)38
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c (renamed from drivers/staging/media/hantro/hantro_h1_jpeg_enc.c)2
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_regs.h (renamed from drivers/staging/media/hantro/hantro_h1_regs.h)4
-rw-r--r--drivers/media/platform/verisilicon/hantro_h264.c (renamed from drivers/staging/media/hantro/hantro_h264.c)6
-rw-r--r--drivers/media/platform/verisilicon/hantro_hevc.c (renamed from drivers/staging/media/hantro/hantro_hevc.c)35
-rw-r--r--drivers/media/platform/verisilicon/hantro_hw.h589
-rw-r--r--drivers/media/platform/verisilicon/hantro_jpeg.c245
-rw-r--r--drivers/media/platform/verisilicon/hantro_jpeg.h (renamed from drivers/staging/media/hantro/hantro_jpeg.h)0
-rw-r--r--drivers/media/platform/verisilicon/hantro_mpeg2.c (renamed from drivers/staging/media/hantro/hantro_mpeg2.c)0
-rw-r--r--drivers/media/platform/verisilicon/hantro_postproc.c344
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c (renamed from drivers/staging/media/hantro/hantro_v4l2.c)243
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.h (renamed from drivers/staging/media/hantro/hantro_v4l2.h)7
-rw-r--r--drivers/media/platform/verisilicon/hantro_vp8.c (renamed from drivers/staging/media/hantro/hantro_vp8.c)0
-rw-r--r--drivers/media/platform/verisilicon/hantro_vp9.c (renamed from drivers/staging/media/hantro/hantro_vp9.c)0
-rw-r--r--drivers/media/platform/verisilicon/hantro_vp9.h (renamed from drivers/staging/media/hantro/hantro_vp9.h)0
-rw-r--r--drivers/media/platform/verisilicon/imx8m_vpu_hw.c (renamed from drivers/staging/media/hantro/imx8m_vpu_hw.c)55
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_entropymode.c4424
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_entropymode.h272
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_filmgrain.c401
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h36
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_h264_dec.c (renamed from drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c)2
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c (renamed from drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c)2
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c (renamed from drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c)2
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_vp8_dec.c (renamed from drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c)0
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_regs.h (renamed from drivers/staging/media/hantro/rockchip_vpu2_regs.h)0
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c2237
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_regs.h477
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu_hw.c (renamed from drivers/staging/media/hantro/rockchip_vpu_hw.c)138
-rw-r--r--drivers/media/platform/verisilicon/sama5d4_vdec_hw.c (renamed from drivers/staging/media/hantro/sama5d4_vdec_hw.c)0
-rw-r--r--drivers/media/platform/verisilicon/stm32mp25_vpu_hw.c186
-rw-r--r--drivers/media/platform/verisilicon/sunxi_vpu_hw.c (renamed from drivers/staging/media/hantro/sunxi_vpu_hw.c)0
-rw-r--r--drivers/media/platform/via/Kconfig2
-rw-r--r--drivers/media/platform/via/via-camera.c64
-rw-r--r--drivers/media/platform/video-mux.c126
-rw-r--r--drivers/media/platform/xilinx/Kconfig4
-rw-r--r--drivers/media/platform/xilinx/xilinx-csi2rxss.c87
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c65
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.h11
-rw-r--r--drivers/media/platform/xilinx/xilinx-tpg.c29
-rw-r--r--drivers/media/platform/xilinx/xilinx-vip.c6
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c97
-rw-r--r--drivers/media/platform/xilinx/xilinx-vtc.c6
-rw-r--r--drivers/media/radio/Kconfig35
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/radio-aimslab.c2
-rw-r--r--drivers/media/radio/radio-aztech.c2
-rw-r--r--drivers/media/radio/radio-cadet.c4
-rw-r--r--drivers/media/radio/radio-gemtek.c2
-rw-r--r--drivers/media/radio/radio-isa.c4
-rw-r--r--drivers/media/radio/radio-isa.h2
-rw-r--r--drivers/media/radio/radio-keene.c4
-rw-r--r--drivers/media/radio/radio-miropcm20.c6
-rw-r--r--drivers/media/radio/radio-raremono.c6
-rw-r--r--drivers/media/radio/radio-rtrack2.c2
-rw-r--r--drivers/media/radio/radio-shark.c10
-rw-r--r--drivers/media/radio/radio-shark2.c12
-rw-r--r--drivers/media/radio/radio-si476x.c13
-rw-r--r--drivers/media/radio/radio-tea5764.c8
-rw-r--r--drivers/media/radio/radio-terratec.c5
-rw-r--r--drivers/media/radio/radio-timb.c3
-rw-r--r--drivers/media/radio/radio-wl1273.c2161
-rw-r--r--drivers/media/radio/radio-zoltrix.c2
-rw-r--r--drivers/media/radio/saa7706h.c10
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c1
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c9
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c4
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h2
-rw-r--r--drivers/media/radio/si4713/radio-platform-si4713.c14
-rw-r--r--drivers/media/radio/si4713/si4713.c12
-rw-r--r--drivers/media/radio/tef6862.c10
-rw-r--r--drivers/media/radio/wl128x/Kconfig15
-rw-r--r--drivers/media/radio/wl128x/Makefile7
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h228
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c1676
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.h389
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.c820
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.h45
-rw-r--r--drivers/media/radio/wl128x/fmdrv_tx.c413
-rw-r--r--drivers/media/radio/wl128x/fmdrv_tx.h24
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c604
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.h20
-rw-r--r--drivers/media/rc/Kconfig23
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/ati_remote.c8
-rw-r--r--drivers/media/rc/bpf-lirc.c8
-rw-r--r--drivers/media/rc/ene_ir.c8
-rw-r--r--drivers/media/rc/gpio-ir-recv.c35
-rw-r--r--drivers/media/rc/gpio-ir-tx.c15
-rw-r--r--drivers/media/rc/igorplugusb.c6
-rw-r--r--drivers/media/rc/iguanair.c4
-rw-r--r--drivers/media/rc/img-ir/img-ir-core.c3
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c9
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.c4
-rw-r--r--drivers/media/rc/imon.c132
-rw-r--r--drivers/media/rc/imon_raw.c2
-rw-r--r--drivers/media/rc/ir-hix5hd2.c14
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c7
-rw-r--r--drivers/media/rc/ir-rx51.c296
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c8
-rw-r--r--drivers/media/rc/ir-spi.c86
-rw-r--r--drivers/media/rc/ir_toy.c4
-rw-r--r--drivers/media/rc/ite-cir.c1
-rw-r--r--drivers/media/rc/keymaps/Makefile4
-rw-r--r--drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c1
-rw-r--r--drivers/media/rc/keymaps/rc-alink-dtu-m.c1
-rw-r--r--drivers/media/rc/keymaps/rc-anysee.c1
-rw-r--r--drivers/media/rc/keymaps/rc-apac-viewcomp.c1
-rw-r--r--drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c1
-rw-r--r--drivers/media/rc/keymaps/rc-asus-pc39.c1
-rw-r--r--drivers/media/rc/keymaps/rc-asus-ps3-100.c1
-rw-r--r--drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c1
-rw-r--r--drivers/media/rc/keymaps/rc-ati-x10.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-a16d.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-cardbus.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-dvbt.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m135a.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-rm-ks.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia.c1
-rw-r--r--drivers/media/rc/keymaps/rc-avertv-303.c1
-rw-r--r--drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c1
-rw-r--r--drivers/media/rc/keymaps/rc-beelink-gs1.c1
-rw-r--r--drivers/media/rc/keymaps/rc-beelink-mxiii.c58
-rw-r--r--drivers/media/rc/keymaps/rc-behold-columbus.c1
-rw-r--r--drivers/media/rc/keymaps/rc-behold.c1
-rw-r--r--drivers/media/rc/keymaps/rc-budget-ci-old.c1
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy-1400.c1
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy.c1
-rw-r--r--drivers/media/rc/keymaps/rc-ct-90405.c1
-rw-r--r--drivers/media/rc/keymaps/rc-d680-dmb.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-rc5.c1
-rw-r--r--drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c1
-rw-r--r--drivers/media/rc/keymaps/rc-digittrade.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dm1105-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dreambox.c152
-rw-r--r--drivers/media/rc/keymaps/rc-dtt200u.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dvbsky.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dvico-mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-dvico-portable.c1
-rw-r--r--drivers/media/rc/keymaps/rc-em-terratec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv-fm53.c1
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv.c1
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-evga-indtube.c1
-rw-r--r--drivers/media/rc/keymaps/rc-eztv.c1
-rw-r--r--drivers/media/rc/keymaps/rc-flydvb.c1
-rw-r--r--drivers/media/rc/keymaps/rc-flyvideo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-fusionhdtv-mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-gadmei-rm008z.c1
-rw-r--r--drivers/media/rc/keymaps/rc-geekbox.c1
-rw-r--r--drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-gotview7135.c1
-rw-r--r--drivers/media/rc/keymaps/rc-hauppauge.c43
-rw-r--r--drivers/media/rc/keymaps/rc-hisi-poplar.c1
-rw-r--r--drivers/media/rc/keymaps/rc-hisi-tv-demo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-imon-mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-imon-pad.c1
-rw-r--r--drivers/media/rc/keymaps/rc-imon-rsc.c1
-rw-r--r--drivers/media/rc/keymaps/rc-iodata-bctv7e.c1
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v1.c1
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kaiomy.c1
-rw-r--r--drivers/media/rc/keymaps/rc-khadas.c1
-rw-r--r--drivers/media/rc/keymaps/rc-khamsin.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-315u.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-pc150u.c1
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c1
-rw-r--r--drivers/media/rc/keymaps/rc-leadtek-y04g0051.c1
-rw-r--r--drivers/media/rc/keymaps/rc-lme2510.c1
-rw-r--r--drivers/media/rc/keymaps/rc-manli.c1
-rw-r--r--drivers/media/rc/keymaps/rc-mecool-kii-pro.c1
-rw-r--r--drivers/media/rc/keymaps/rc-mecool-kiii-pro.c1
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10.c1
-rw-r--r--drivers/media/rc/keymaps/rc-minix-neo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-ii.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-iii.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c1
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere.c1
-rw-r--r--drivers/media/rc/keymaps/rc-mygica-utv3.c69
-rw-r--r--drivers/media/rc/keymaps/rc-nebula.c1
-rw-r--r--drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c1
-rw-r--r--drivers/media/rc/keymaps/rc-norwood.c1
-rw-r--r--drivers/media/rc/keymaps/rc-npgtech.c1
-rw-r--r--drivers/media/rc/keymaps/rc-odroid.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pctv-sedna.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pine64.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-color.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-grey.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-002t.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-mk12.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-new.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview.c1
-rw-r--r--drivers/media/rc/keymaps/rc-powercolor-real-angel.c1
-rw-r--r--drivers/media/rc/keymaps/rc-proteus-2309.c1
-rw-r--r--drivers/media/rc/keymaps/rc-purpletv.c1
-rw-r--r--drivers/media/rc/keymaps/rc-pv951.c1
-rw-r--r--drivers/media/rc/keymaps/rc-rc6-mce.c1
-rw-r--r--drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c1
-rw-r--r--drivers/media/rc/keymaps/rc-reddo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-siemens-gigaset-rc20.c71
-rw-r--r--drivers/media/rc/keymaps/rc-snapstream-firefly.c1
-rw-r--r--drivers/media/rc/keymaps/rc-streamzap.c1
-rw-r--r--drivers/media/rc/keymaps/rc-su3000.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tanix-tx3mini.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tanix-tx5max.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tbs-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-ts35.c1
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-usb2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim-2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tevii-nec.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c1
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand-02.c1
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand.c1
-rw-r--r--drivers/media/rc/keymaps/rc-trekstor.c1
-rw-r--r--drivers/media/rc/keymaps/rc-tt-1500.c1
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c1
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan1027.c1
-rw-r--r--drivers/media/rc/keymaps/rc-vega-s9x.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-m1f.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-s350.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-tv-pvr.c1
-rw-r--r--drivers/media/rc/keymaps/rc-videostrong-kii-pro.c1
-rw-r--r--drivers/media/rc/keymaps/rc-wetek-hub.c1
-rw-r--r--drivers/media/rc/keymaps/rc-wetek-play2.c1
-rw-r--r--drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c1
-rw-r--r--drivers/media/rc/keymaps/rc-winfast.c1
-rw-r--r--drivers/media/rc/keymaps/rc-x96max.c1
-rw-r--r--drivers/media/rc/keymaps/rc-xbox-360.c1
-rw-r--r--drivers/media/rc/keymaps/rc-xbox-dvd.c1
-rw-r--r--drivers/media/rc/keymaps/rc-zx-irdec.c1
-rw-r--r--drivers/media/rc/lirc_dev.c50
-rw-r--r--drivers/media/rc/mceusb.c20
-rw-r--r--drivers/media/rc/meson-ir-tx.c36
-rw-r--r--drivers/media/rc/meson-ir.c587
-rw-r--r--drivers/media/rc/mtk-cir.c8
-rw-r--r--drivers/media/rc/pwm-ir-tx.c86
-rw-r--r--drivers/media/rc/rc-core-priv.h6
-rw-r--r--drivers/media/rc/rc-ir-raw.c5
-rw-r--r--drivers/media/rc/rc-loopback.c1
-rw-r--r--drivers/media/rc/rc-main.c15
-rw-r--r--drivers/media/rc/redrat3.c4
-rw-r--r--drivers/media/rc/serial_ir.c3
-rw-r--r--drivers/media/rc/st_rc.c6
-rw-r--r--drivers/media/rc/streamzap.c70
-rw-r--r--drivers/media/rc/sunxi-cir.c8
-rw-r--r--drivers/media/spi/cxd2880-spi.c2
-rw-r--r--drivers/media/spi/gs1662.c35
-rw-r--r--drivers/media/test-drivers/Kconfig2
-rw-r--r--drivers/media/test-drivers/Makefile1
-rw-r--r--drivers/media/test-drivers/vicodec/Kconfig1
-rw-r--r--drivers/media/test-drivers/vicodec/codec-fwht.c2
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c90
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.c62
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_channel.c5
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_demod.c17
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_mux.c7
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_pes.c1
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_psi.c47
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_tuner.c9
-rw-r--r--drivers/media/test-drivers/vim2m.c368
-rw-r--r--drivers/media/test-drivers/vimc/vimc-capture.c22
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.c33
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.h14
-rw-r--r--drivers/media/test-drivers/vimc/vimc-core.c10
-rw-r--r--drivers/media/test-drivers/vimc/vimc-debayer.c210
-rw-r--r--drivers/media/test-drivers/vimc/vimc-lens.c5
-rw-r--r--drivers/media/test-drivers/vimc/vimc-scaler.c146
-rw-r--r--drivers/media/test-drivers/vimc/vimc-sensor.c132
-rw-r--r--drivers/media/test-drivers/vimc/vimc-streamer.c6
-rw-r--r--drivers/media/test-drivers/visl/Kconfig28
-rw-r--r--drivers/media/test-drivers/visl/Makefile8
-rw-r--r--drivers/media/test-drivers/visl/visl-core.c576
-rw-r--r--drivers/media/test-drivers/visl/visl-debugfs.c112
-rw-r--r--drivers/media/test-drivers/visl/visl-debugfs.h40
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.c648
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.h75
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-av1.h314
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-fwht.h66
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-h264.h349
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-hevc.h405
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-mpeg2.h99
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-points.c11
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-vp8.h156
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-vp9.h292
-rw-r--r--drivers/media/test-drivers/visl/visl-video.c803
-rw-r--r--drivers/media/test-drivers/visl/visl-video.h28
-rw-r--r--drivers/media/test-drivers/visl/visl.h173
-rw-r--r--drivers/media/test-drivers/vivid/Kconfig16
-rw-r--r--drivers/media/test-drivers/vivid/Makefile5
-rw-r--r--drivers/media/test-drivers/vivid/vivid-cec.c144
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c443
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.h153
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.c343
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-cap.c248
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-out.c13
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-touch.c11
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-cap.c5
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-out.c5
-rw-r--r--drivers/media/test-drivers/vivid/vivid-osd.c31
-rw-r--r--drivers/media/test-drivers/vivid/vivid-osd.h19
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-rx.c20
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-rx.h8
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-tx.c8
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-tx.h4
-rw-r--r--drivers/media/test-drivers/vivid/vivid-rds-gen.c2
-rw-r--r--drivers/media/test-drivers/vivid/vivid-sdr-cap.c40
-rw-r--r--drivers/media/test-drivers/vivid/vivid-sdr-cap.h18
-rw-r--r--drivers/media/test-drivers/vivid/vivid-touch-cap.c17
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-cap.c22
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-cap.h8
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-gen.c9
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-out.c15
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-out.h6
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c496
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.h29
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-common.c142
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-common.h13
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-out.c141
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-out.h16
-rw-r--r--drivers/media/tuners/e4000.c9
-rw-r--r--drivers/media/tuners/fc0011.c2
-rw-r--r--drivers/media/tuners/fc0012.c2
-rw-r--r--drivers/media/tuners/fc0013.c66
-rw-r--r--drivers/media/tuners/fc0013.h11
-rw-r--r--drivers/media/tuners/fc2580.c8
-rw-r--r--drivers/media/tuners/it913x.c4
-rw-r--r--drivers/media/tuners/m88rs6000t.c9
-rw-r--r--drivers/media/tuners/max2165.c2
-rw-r--r--drivers/media/tuners/mc44s803.c2
-rw-r--r--drivers/media/tuners/mt2060.c11
-rw-r--r--drivers/media/tuners/mt2063.c2
-rw-r--r--drivers/media/tuners/mt2131.c2
-rw-r--r--drivers/media/tuners/mt2266.c2
-rw-r--r--drivers/media/tuners/mxl301rf.c10
-rw-r--r--drivers/media/tuners/mxl5005s.c18
-rw-r--r--drivers/media/tuners/qm1d1b0004.c7
-rw-r--r--drivers/media/tuners/qm1d1c0042.c8
-rw-r--r--drivers/media/tuners/qt1010.c13
-rw-r--r--drivers/media/tuners/si2157.c8
-rw-r--r--drivers/media/tuners/tda18212.c9
-rw-r--r--drivers/media/tuners/tda18218.c2
-rw-r--r--drivers/media/tuners/tda18250.c9
-rw-r--r--drivers/media/tuners/tda18271-fe.c5
-rw-r--r--drivers/media/tuners/tda9887.c1
-rw-r--r--drivers/media/tuners/tea5761.c4
-rw-r--r--drivers/media/tuners/tea5767.c4
-rw-r--r--drivers/media/tuners/tua9001.c8
-rw-r--r--drivers/media/tuners/tuner-i2c.h4
-rw-r--r--drivers/media/tuners/tuner-simple.c20
-rw-r--r--drivers/media/tuners/tuner-types.c313
-rw-r--r--drivers/media/tuners/xc2028.c4
-rw-r--r--drivers/media/tuners/xc4000.c20
-rw-r--r--drivers/media/tuners/xc5000.c55
-rw-r--r--drivers/media/usb/Kconfig3
-rw-r--r--drivers/media/usb/Makefile3
-rw-r--r--drivers/media/usb/airspy/airspy.c15
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.c8
-rw-r--r--drivers/media/usb/au0828/au0828-core.c21
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c10
-rw-r--r--drivers/media/usb/au0828/au0828-vbi.c4
-rw-r--r--drivers/media/usb/au0828/au0828-video.c33
-rw-r--r--drivers/media/usb/b2c2/flexcop-usb.c44
-rw-r--r--drivers/media/usb/cpia2/Kconfig10
-rw-r--r--drivers/media/usb/cpia2/Makefile4
-rw-r--r--drivers/media/usb/cpia2/cpia2.h475
-rw-r--r--drivers/media/usb/cpia2/cpia2_core.c2434
-rw-r--r--drivers/media/usb/cpia2/cpia2_registers.h463
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c966
-rw-r--r--drivers/media/usb/cpia2/cpia2_v4l.c1226
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c41
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c60
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c6
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c9
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-i2c.c5
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h18
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c4
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c15
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h8
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c6
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c42
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.c23
-rw-r--r--drivers/media/usb/dvb-usb-v2/au6610.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/az6007.c29
-rw-r--r--drivers/media/usb/dvb-usb-v2/ce6230.c10
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvbsky.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/ec168.c14
-rw-r--r--drivers/media/usb/dvb-usb-v2/gl861.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c26
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c22
-rw-r--r--drivers/media/usb/dvb-usb/a800.c2
-rw-r--r--drivers/media/usb/dvb-usb/af9005-fe.c3
-rw-r--r--drivers/media/usb/dvb-usb/af9005.c9
-rw-r--r--drivers/media/usb/dvb-usb/az6027.c54
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-core.c2
-rw-r--r--drivers/media/usb/dvb-usb/cxusb-analog.c22
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c9
-rw-r--r--drivers/media/usb/dvb-usb/dib0700.h1
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c25
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-common.c1
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mb.c2
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc-common.c1
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc.c2
-rw-r--r--drivers/media/usb/dvb-usb/digitv.c8
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.c2
-rw-r--r--drivers/media/usb/dvb-usb/dtv5100.c9
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-init.c6
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb.h8
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c528
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.c4
-rw-r--r--drivers/media/usb/dvb-usb/m920x.c27
-rw-r--r--drivers/media/usb/dvb-usb/nova-t-usb2.c2
-rw-r--r--drivers/media/usb/dvb-usb/opera1.c20
-rw-r--r--drivers/media/usb/dvb-usb/pctv452e.c35
-rw-r--r--drivers/media/usb/dvb-usb/technisat-usb2.c6
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.c4
-rw-r--r--drivers/media/usb/dvb-usb/umt-010.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.c2
-rw-r--r--drivers/media/usb/em28xx/Kconfig5
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c56
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c3
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c10
-rw-r--r--drivers/media/usb/em28xx/em28xx-vbi.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c14
-rw-r--r--drivers/media/usb/em28xx/em28xx.h1
-rw-r--r--drivers/media/usb/go7007/Kconfig2
-rw-r--r--drivers/media/usb/go7007/go7007-driver.c9
-rw-r--r--drivers/media/usb/go7007/go7007-fw.c4
-rw-r--r--drivers/media/usb/go7007/go7007-i2c.c32
-rw-r--r--drivers/media/usb/go7007/go7007-usb.c5
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c8
-rw-r--r--drivers/media/usb/go7007/s2250-board.c8
-rw-r--r--drivers/media/usb/gspca/cpia1.c9
-rw-r--r--drivers/media/usb/gspca/finepix.c2
-rw-r--r--drivers/media/usb/gspca/gspca.c26
-rw-r--r--drivers/media/usb/gspca/ov534.c2
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c7
-rw-r--r--drivers/media/usb/gspca/vicam.c10
-rw-r--r--drivers/media/usb/hackrf/hackrf.c11
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c30
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c69
-rw-r--r--drivers/media/usb/msi2500/msi2500.c48
-rw-r--r--drivers/media/usb/pvrusb2/Kconfig3
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-context.c11
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debugifc.c5
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-dvb.c14
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-encoder.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c53
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-io.c4
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-main.c18
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.c167
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.h6
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-sysfs.c79
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-sysfs.h16
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.c120
-rw-r--r--drivers/media/usb/pwc/pwc-if.c5
-rw-r--r--drivers/media/usb/pwc/pwc-uncompress.c2
-rw-r--r--drivers/media/usb/s2255/s2255drv.c35
-rw-r--r--drivers/media/usb/siano/smsusb.c49
-rw-r--r--drivers/media/usb/stk1160/Kconfig12
-rw-r--r--drivers/media/usb/stk1160/stk1160-core.c3
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c6
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c83
-rw-r--r--drivers/media/usb/stk1160/stk1160.h7
-rw-r--r--drivers/media/usb/tm6000/Kconfig34
-rw-r--r--drivers/media/usb/tm6000/Makefile14
-rw-r--r--drivers/media/usb/tm6000/tm6000-alsa.c440
-rw-r--r--drivers/media/usb/tm6000/tm6000-cards.c1397
-rw-r--r--drivers/media/usb/tm6000/tm6000-core.c916
-rw-r--r--drivers/media/usb/tm6000/tm6000-dvb.c454
-rw-r--r--drivers/media/usb/tm6000/tm6000-i2c.c317
-rw-r--r--drivers/media/usb/tm6000/tm6000-input.c503
-rw-r--r--drivers/media/usb/tm6000/tm6000-regs.h588
-rw-r--r--drivers/media/usb/tm6000/tm6000-stds.c623
-rw-r--r--drivers/media/usb/tm6000/tm6000-usb-isoc.h38
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c1705
-rw-r--r--drivers/media/usb/tm6000/tm6000.h396
-rw-r--r--drivers/media/usb/ttusb-dec/ttusb_dec.c26
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.c12
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c2
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c18
-rw-r--r--drivers/media/usb/uvc/Kconfig1
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c1501
-rw-r--r--drivers/media/usb/uvc/uvc_debugfs.c1
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c1206
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c6
-rw-r--r--drivers/media/usb/uvc/uvc_metadata.c163
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c212
-rw-r--r--drivers/media/usb/uvc/uvc_status.c203
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c893
-rw-r--r--drivers/media/usb/uvc/uvc_video.c418
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h333
-rw-r--r--drivers/media/usb/zr364xx/Kconfig15
-rw-r--r--drivers/media/usb/zr364xx/Makefile3
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c1635
-rw-r--r--drivers/media/v4l2-core/Kconfig17
-rw-r--r--drivers/media/v4l2-core/Makefile7
-rw-r--r--drivers/media/v4l2-core/tuner-core.c7
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c724
-rw-r--r--drivers/media/v4l2-core/v4l2-cci.c203
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c568
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c108
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-api.c231
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c729
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-defs.c70
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-priv.h2
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-request.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c186
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c14
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c265
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-fh.c16
-rw-r--r--drivers/media/v4l2-core/v4l2-flash-led-class.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c205
-rw-r--r--drivers/media/v4l2-core/v4l2-h264.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-i2c.c5
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c849
-rw-r--r--drivers/media/v4l2-core/v4l2-isp.c132
-rw-r--r--drivers/media/v4l2-core/v4l2-jpeg.c202
-rw-r--r--drivers/media/v4l2-core/v4l2-mc.c81
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c109
-rw-r--r--drivers/media/v4l2-core/v4l2-spi.c6
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev-priv.h14
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c1743
-rw-r--r--drivers/media/v4l2-core/videobuf-core.c1198
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-contig.c408
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c685
-rw-r--r--drivers/media/v4l2-core/videobuf-vmalloc.c326
-rw-r--r--drivers/memory/Kconfig47
-rw-r--r--drivers/memory/Makefile5
-rw-r--r--drivers/memory/atmel-ebi.c53
-rw-r--r--drivers/memory/atmel-sdramc.c76
-rw-r--r--drivers/memory/brcmstb_dpfe.c13
-rw-r--r--drivers/memory/brcmstb_memc.c245
-rw-r--r--drivers/memory/bt1-l2-ctl.c3
-rw-r--r--drivers/memory/da8xx-ddrctl.c2
-rw-r--r--drivers/memory/dfl-emif.c62
-rw-r--r--drivers/memory/emif.c99
-rw-r--r--drivers/memory/fsl-corenet-cf.c15
-rw-r--r--drivers/memory/fsl_ifc.c9
-rw-r--r--drivers/memory/jz4780-nemc.c4
-rw-r--r--drivers/memory/mtk-smi.c249
-rw-r--r--drivers/memory/mvebu-devbus.c4
-rw-r--r--drivers/memory/of_memory.c2
-rw-r--r--drivers/memory/omap-gpmc.c227
-rw-r--r--drivers/memory/pl172.c58
-rw-r--r--drivers/memory/pl353-smc.c58
-rw-r--r--drivers/memory/renesas-rpc-if-regs.h147
-rw-r--r--drivers/memory/renesas-rpc-if.c851
-rw-r--r--drivers/memory/renesas-xspi-if-regs.h105
-rw-r--r--drivers/memory/samsung/exynos-srom.c10
-rw-r--r--drivers/memory/samsung/exynos5422-dmc.c96
-rw-r--r--drivers/memory/stm32-fmc2-ebi.c752
-rw-r--r--drivers/memory/stm32_omm.c470
-rw-r--r--drivers/memory/tegra/Kconfig8
-rw-r--r--drivers/memory/tegra/Makefile2
-rw-r--r--drivers/memory/tegra/mc.c155
-rw-r--r--drivers/memory/tegra/mc.h10
-rw-r--r--drivers/memory/tegra/tegra124-emc.c159
-rw-r--r--drivers/memory/tegra/tegra124.c4
-rw-r--r--drivers/memory/tegra/tegra186-emc.c248
-rw-r--r--drivers/memory/tegra/tegra186.c87
-rw-r--r--drivers/memory/tegra/tegra194.c1
-rw-r--r--drivers/memory/tegra/tegra20-emc.c194
-rw-r--r--drivers/memory/tegra/tegra20.c33
-rw-r--r--drivers/memory/tegra/tegra210-emc-cc-r21021.c429
-rw-r--r--drivers/memory/tegra/tegra210-emc-core.c32
-rw-r--r--drivers/memory/tegra/tegra210-emc-table.c2
-rw-r--r--drivers/memory/tegra/tegra210.c146
-rw-r--r--drivers/memory/tegra/tegra234.c967
-rw-r--r--drivers/memory/tegra/tegra264-bwmgr.h50
-rw-r--r--drivers/memory/tegra/tegra264.c313
-rw-r--r--drivers/memory/tegra/tegra30-emc.c153
-rw-r--r--drivers/memory/tegra/tegra30.c4
-rw-r--r--drivers/memory/ti-aemif.c267
-rw-r--r--drivers/memory/ti-emif-pm.c11
-rw-r--r--drivers/memory/ti-emif-sram-pm.S3
-rw-r--r--drivers/memstick/core/Kconfig2
-rw-r--r--drivers/memstick/core/memstick.c81
-rw-r--r--drivers/memstick/core/ms_block.c46
-rw-r--r--drivers/memstick/core/mspro_block.c208
-rw-r--r--drivers/memstick/host/Kconfig10
-rw-r--r--drivers/memstick/host/Makefile1
-rw-r--r--drivers/memstick/host/jmb38x_ms.c9
-rw-r--r--drivers/memstick/host/r592.c12
-rw-r--r--drivers/memstick/host/rtsx_pci_ms.c643
-rw-r--r--drivers/memstick/host/rtsx_usb_ms.c13
-rw-r--r--drivers/memstick/host/tifm_ms.c9
-rw-r--r--drivers/message/fusion/Kconfig2
-rw-r--r--drivers/message/fusion/lsi/mpi_cnfg.h60
-rw-r--r--drivers/message/fusion/mptbase.c19
-rw-r--r--drivers/message/fusion/mptbase.h6
-rw-r--r--drivers/message/fusion/mptctl.c30
-rw-r--r--drivers/message/fusion/mptfc.c124
-rw-r--r--drivers/message/fusion/mptlan.c2
-rw-r--r--drivers/message/fusion/mptlan.h3
-rw-r--r--drivers/message/fusion/mptsas.c53
-rw-r--r--drivers/message/fusion/mptscsih.c38
-rw-r--r--drivers/message/fusion/mptscsih.h7
-rw-r--r--drivers/message/fusion/mptspi.c24
-rw-r--r--drivers/mfd/88pm800.c16
-rw-r--r--drivers/mfd/88pm805.c15
-rw-r--r--drivers/mfd/88pm80x.c6
-rw-r--r--drivers/mfd/88pm860x-core.c33
-rw-r--r--drivers/mfd/88pm886.c154
-rw-r--r--drivers/mfd/Kconfig698
-rw-r--r--drivers/mfd/Makefile84
-rw-r--r--drivers/mfd/aat2870-core.c19
-rw-r--r--drivers/mfd/ab8500-core.c6
-rw-r--r--drivers/mfd/ab8500-sysctrl.c6
-rw-r--r--drivers/mfd/ac100.c2
-rw-r--r--drivers/mfd/acer-ec-a500.c8
-rw-r--r--drivers/mfd/act8945a.c9
-rw-r--r--drivers/mfd/adp5520.c10
-rw-r--r--drivers/mfd/adp5585.c843
-rw-r--r--drivers/mfd/altera-a10sr.c2
-rw-r--r--drivers/mfd/altera-sysmgr.c10
-rw-r--r--drivers/mfd/arizona-core.c23
-rw-r--r--drivers/mfd/arizona-i2c.c20
-rw-r--r--drivers/mfd/arizona-irq.c9
-rw-r--r--drivers/mfd/arizona-spi.c15
-rw-r--r--drivers/mfd/as3711.c5
-rw-r--r--drivers/mfd/as3722.c13
-rw-r--r--drivers/mfd/asic3.c1071
-rw-r--r--drivers/mfd/atc260x-core.c11
-rw-r--r--drivers/mfd/atc260x-i2c.c5
-rw-r--r--drivers/mfd/atmel-flexcom.c6
-rw-r--r--drivers/mfd/atmel-hlcdc.c5
-rw-r--r--drivers/mfd/atmel-smc.c15
-rw-r--r--drivers/mfd/axp20x-i2c.c32
-rw-r--r--drivers/mfd/axp20x-rsb.c1
-rw-r--r--drivers/mfd/axp20x.c577
-rw-r--r--drivers/mfd/bcm2835-pm.c4
-rw-r--r--drivers/mfd/bcm590xx.c74
-rw-r--r--drivers/mfd/bd9571mwv.c13
-rw-r--r--drivers/mfd/bq257xx.c99
-rw-r--r--drivers/mfd/cgbc-core.c428
-rw-r--r--drivers/mfd/cros_ec_dev.c97
-rw-r--r--drivers/mfd/cs40l50-core.c569
-rw-r--r--drivers/mfd/cs40l50-i2c.c68
-rw-r--r--drivers/mfd/cs40l50-spi.c68
-rw-r--r--drivers/mfd/cs42l43-i2c.c91
-rw-r--r--drivers/mfd/cs42l43-sdw.c212
-rw-r--r--drivers/mfd/cs42l43.c1249
-rw-r--r--drivers/mfd/cs42l43.h29
-rw-r--r--drivers/mfd/cs47l15-tables.c8
-rw-r--r--drivers/mfd/cs47l24-tables.c2
-rw-r--r--drivers/mfd/cs47l35-tables.c8
-rw-r--r--drivers/mfd/cs47l85-tables.c8
-rw-r--r--drivers/mfd/cs47l90-tables.c8
-rw-r--r--drivers/mfd/cs47l92-tables.c8
-rw-r--r--drivers/mfd/da903x.c8
-rw-r--r--drivers/mfd/da9052-core.c4
-rw-r--r--drivers/mfd/da9052-i2c.c13
-rw-r--r--drivers/mfd/da9052-spi.c3
-rw-r--r--drivers/mfd/da9055-core.c5
-rw-r--r--drivers/mfd/da9055-i2c.c11
-rw-r--r--drivers/mfd/da9062-core.c225
-rw-r--r--drivers/mfd/da9063-i2c.c38
-rw-r--r--drivers/mfd/da9150-core.c9
-rw-r--r--drivers/mfd/davinci_voicecodec.c136
-rw-r--r--drivers/mfd/db8500-prcmu.c10
-rw-r--r--drivers/mfd/dln2.c1
-rw-r--r--drivers/mfd/dm355evm_msp.c455
-rw-r--r--drivers/mfd/ene-kb3930.c8
-rw-r--r--drivers/mfd/exynos-lpass.c40
-rw-r--r--drivers/mfd/ezx-pcap.c34
-rw-r--r--drivers/mfd/fsl-imx25-tsadc.c50
-rw-r--r--drivers/mfd/gateworks-gsc.c13
-rw-r--r--drivers/mfd/hi6421-pmic-core.c13
-rw-r--r--drivers/mfd/hi655x-pmic.c18
-rw-r--r--drivers/mfd/htc-i2cpld.c631
-rw-r--r--drivers/mfd/htc-pasic3.c210
-rw-r--r--drivers/mfd/intel-lpss-acpi.c28
-rw-r--r--drivers/mfd/intel-lpss-pci.c326
-rw-r--r--drivers/mfd/intel-lpss.c61
-rw-r--r--drivers/mfd/intel-lpss.h42
-rw-r--r--drivers/mfd/intel-m10-bmc-core.c208
-rw-r--r--drivers/mfd/intel-m10-bmc-pmci.c457
-rw-r--r--drivers/mfd/intel-m10-bmc-spi.c184
-rw-r--r--drivers/mfd/intel-m10-bmc.c237
-rw-r--r--drivers/mfd/intel_pmc_bxt.c3
-rw-r--r--drivers/mfd/intel_soc_pmic_bxtwc.c195
-rw-r--r--drivers/mfd/intel_soc_pmic_chtdc_ti.c15
-rw-r--r--drivers/mfd/intel_soc_pmic_chtwc.c25
-rw-r--r--drivers/mfd/intel_soc_pmic_core.c160
-rw-r--r--drivers/mfd/intel_soc_pmic_core.h25
-rw-r--r--drivers/mfd/intel_soc_pmic_crc.c148
-rw-r--r--drivers/mfd/intel_soc_pmic_mrfld.c3
-rw-r--r--drivers/mfd/ioc3.c2
-rw-r--r--drivers/mfd/ipaq-micro.c15
-rw-r--r--drivers/mfd/iqs62x.c12
-rw-r--r--drivers/mfd/kempld-core.c271
-rw-r--r--drivers/mfd/khadas-mcu.c7
-rw-r--r--drivers/mfd/lm3533-core.c39
-rw-r--r--drivers/mfd/lochnagar-i2c.c17
-rw-r--r--drivers/mfd/loongson-se.c253
-rw-r--r--drivers/mfd/lp3943.c4
-rw-r--r--drivers/mfd/lp873x.c9
-rw-r--r--drivers/mfd/lp87565.c16
-rw-r--r--drivers/mfd/lp8788-irq.c5
-rw-r--r--drivers/mfd/lp8788.c20
-rw-r--r--drivers/mfd/lpc_ich.c135
-rw-r--r--drivers/mfd/ls2k-bmc-core.c532
-rw-r--r--drivers/mfd/macsmc.c499
-rw-r--r--drivers/mfd/madera-core.c7
-rw-r--r--drivers/mfd/madera-i2c.c16
-rw-r--r--drivers/mfd/madera-spi.c10
-rw-r--r--drivers/mfd/max14577.c36
-rw-r--r--drivers/mfd/max7360.c171
-rw-r--r--drivers/mfd/max77541.c220
-rw-r--r--drivers/mfd/max77620.c45
-rw-r--r--drivers/mfd/max77650.c3
-rw-r--r--drivers/mfd/max77686.c11
-rw-r--r--drivers/mfd/max77693.c14
-rw-r--r--drivers/mfd/max77705.c180
-rw-r--r--drivers/mfd/max77714.c2
-rw-r--r--drivers/mfd/max77759.c690
-rw-r--r--drivers/mfd/max77843.c7
-rw-r--r--drivers/mfd/max8907.c16
-rw-r--r--drivers/mfd/max8925-core.c13
-rw-r--r--drivers/mfd/max8925-i2c.c19
-rw-r--r--drivers/mfd/max8997-irq.c19
-rw-r--r--drivers/mfd/max8997.c19
-rw-r--r--drivers/mfd/max8998-irq.c2
-rw-r--r--drivers/mfd/max8998.c19
-rw-r--r--drivers/mfd/mc13xxx-core.c22
-rw-r--r--drivers/mfd/mc13xxx-i2c.c15
-rw-r--r--drivers/mfd/mc13xxx-spi.c19
-rw-r--r--drivers/mfd/mcp-core.c4
-rw-r--r--drivers/mfd/mcp-sa11x0.c10
-rw-r--r--drivers/mfd/menelaus.c9
-rw-r--r--drivers/mfd/menf21bmc.c2
-rw-r--r--drivers/mfd/mfd-core.c67
-rw-r--r--drivers/mfd/motorola-cpcap.c13
-rw-r--r--drivers/mfd/mp2629.c2
-rw-r--r--drivers/mfd/mt6358-irq.c11
-rw-r--r--drivers/mfd/mt6360-core.c39
-rw-r--r--drivers/mfd/mt6370.c312
-rw-r--r--drivers/mfd/mt6370.h99
-rw-r--r--drivers/mfd/mt6397-core.c72
-rw-r--r--drivers/mfd/mt6397-irq.c41
-rw-r--r--drivers/mfd/mxs-lradc.c13
-rw-r--r--drivers/mfd/nct6694.c388
-rw-r--r--drivers/mfd/ntxec.c9
-rw-r--r--drivers/mfd/ocelot-core.c87
-rw-r--r--drivers/mfd/ocelot-spi.c13
-rw-r--r--drivers/mfd/omap-usb-host.c11
-rw-r--r--drivers/mfd/omap-usb-tll.c20
-rw-r--r--drivers/mfd/palmas.c76
-rw-r--r--drivers/mfd/pcf50633-adc.c252
-rw-r--r--drivers/mfd/pcf50633-core.c327
-rw-r--r--drivers/mfd/pcf50633-gpio.c91
-rw-r--r--drivers/mfd/pcf50633-irq.c309
-rw-r--r--drivers/mfd/pf1550.c367
-rw-r--r--drivers/mfd/qcom-pm8008.c294
-rw-r--r--drivers/mfd/qcom-pm8xxx.c24
-rw-r--r--drivers/mfd/qcom-spmi-pmic.c108
-rw-r--r--drivers/mfd/qcom_rpm.c28
-rw-r--r--drivers/mfd/qnap-mcu.c417
-rw-r--r--drivers/mfd/rave-sp.c20
-rw-r--r--drivers/mfd/rc5t583-irq.c9
-rw-r--r--drivers/mfd/rc5t583.c5
-rw-r--r--drivers/mfd/retu-mfd.c22
-rw-r--r--drivers/mfd/rk808.c863
-rw-r--r--drivers/mfd/rk8xx-core.c880
-rw-r--r--drivers/mfd/rk8xx-i2c.c253
-rw-r--r--drivers/mfd/rk8xx-spi.c124
-rw-r--r--drivers/mfd/rn5t618.c24
-rw-r--r--drivers/mfd/rohm-bd71828.c143
-rw-r--r--drivers/mfd/rohm-bd718x7.c40
-rw-r--r--drivers/mfd/rohm-bd9576.c32
-rw-r--r--drivers/mfd/rohm-bd96801.c793
-rw-r--r--drivers/mfd/rsmu.h2
-rw-r--r--drivers/mfd/rsmu_core.c2
-rw-r--r--drivers/mfd/rsmu_i2c.c258
-rw-r--r--drivers/mfd/rsmu_spi.c54
-rw-r--r--drivers/mfd/rt4831.c7
-rw-r--r--drivers/mfd/rt5033.c26
-rw-r--r--drivers/mfd/rt5120.c123
-rw-r--r--drivers/mfd/rz-mtu3.c391
-rw-r--r--drivers/mfd/rz-mtu3.h147
-rw-r--r--drivers/mfd/sec-acpm.c421
-rw-r--r--drivers/mfd/sec-common.c301
-rw-r--r--drivers/mfd/sec-core.c508
-rw-r--r--drivers/mfd/sec-core.h23
-rw-r--r--drivers/mfd/sec-i2c.c239
-rw-r--r--drivers/mfd/sec-irq.c644
-rw-r--r--drivers/mfd/si476x-cmd.c16
-rw-r--r--drivers/mfd/si476x-i2c.c8
-rw-r--r--drivers/mfd/si476x-prop.c2
-rw-r--r--drivers/mfd/simple-mfd-i2c.c58
-rw-r--r--drivers/mfd/sky81452.c3
-rw-r--r--drivers/mfd/sm501.c75
-rw-r--r--drivers/mfd/smpro-core.c138
-rw-r--r--drivers/mfd/sprd-sc27xx-spi.c19
-rw-r--r--drivers/mfd/ssbi.c12
-rw-r--r--drivers/mfd/sta2x11-mfd.c645
-rw-r--r--drivers/mfd/stm32-lptimer.c39
-rw-r--r--drivers/mfd/stm32-timers.c86
-rw-r--r--drivers/mfd/stmfx.c27
-rw-r--r--drivers/mfd/stmpe-i2c.c28
-rw-r--r--drivers/mfd/stmpe-spi.c21
-rw-r--r--drivers/mfd/stmpe.c76
-rw-r--r--drivers/mfd/stpmic1.c39
-rw-r--r--drivers/mfd/stw481x.c7
-rw-r--r--drivers/mfd/sun4i-gpadc.c12
-rw-r--r--drivers/mfd/syscon.c225
-rw-r--r--drivers/mfd/t7l66xb.c432
-rw-r--r--drivers/mfd/tc3589x.c28
-rw-r--r--drivers/mfd/tc6387xb.c233
-rw-r--r--drivers/mfd/tc6393xb.c912
-rw-r--r--drivers/mfd/ti-lmu.c4
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c15
-rw-r--r--drivers/mfd/timberdale.c20
-rw-r--r--drivers/mfd/tmio_core.c70
-rw-r--r--drivers/mfd/tps6105x.c13
-rw-r--r--drivers/mfd/tps65010.c51
-rw-r--r--drivers/mfd/tps6507x.c6
-rw-r--r--drivers/mfd/tps65086.c31
-rw-r--r--drivers/mfd/tps65090.c15
-rw-r--r--drivers/mfd/tps65217.c11
-rw-r--r--drivers/mfd/tps65218.c8
-rw-r--r--drivers/mfd/tps65219.c564
-rw-r--r--drivers/mfd/tps6586x.c75
-rw-r--r--drivers/mfd/tps65910.c23
-rw-r--r--drivers/mfd/tps65911-comparator.c4
-rw-r--r--drivers/mfd/tps65912-core.c25
-rw-r--r--drivers/mfd/tps65912-i2c.c15
-rw-r--r--drivers/mfd/tps65912-spi.c8
-rw-r--r--drivers/mfd/tps6594-core.c801
-rw-r--r--drivers/mfd/tps6594-i2c.c254
-rw-r--r--drivers/mfd/tps6594-spi.c139
-rw-r--r--drivers/mfd/tqmx86.c175
-rw-r--r--drivers/mfd/twl-core.c138
-rw-r--r--drivers/mfd/twl4030-audio.c5
-rw-r--r--drivers/mfd/twl4030-irq.c6
-rw-r--r--drivers/mfd/twl4030-power.c18
-rw-r--r--drivers/mfd/twl6030-irq.c80
-rw-r--r--drivers/mfd/twl6040.c51
-rw-r--r--drivers/mfd/ucb1400_core.c158
-rw-r--r--drivers/mfd/ucb1x00-core.c12
-rw-r--r--drivers/mfd/upboard-fpga.c324
-rw-r--r--drivers/mfd/vexpress-sysreg.c89
-rw-r--r--drivers/mfd/wcd934x.c45
-rw-r--r--drivers/mfd/wl1273-core.c263
-rw-r--r--drivers/mfd/wm5102-tables.c4
-rw-r--r--drivers/mfd/wm5110-tables.c4
-rw-r--r--drivers/mfd/wm831x-auxadc.c43
-rw-r--r--drivers/mfd/wm831x-core.c5
-rw-r--r--drivers/mfd/wm831x-i2c.c18
-rw-r--r--drivers/mfd/wm831x-irq.c13
-rw-r--r--drivers/mfd/wm831x-spi.c16
-rw-r--r--drivers/mfd/wm8350-i2c.c9
-rw-r--r--drivers/mfd/wm8350-regmap.c2
-rw-r--r--drivers/mfd/wm8400-core.c11
-rw-r--r--drivers/mfd/wm8994-core.c44
-rw-r--r--drivers/mfd/wm8994-irq.c4
-rw-r--r--drivers/mfd/wm8994-regmap.c6
-rw-r--r--drivers/mfd/wm8997-tables.c2
-rw-r--r--drivers/mfd/wm8998-tables.c2
-rw-r--r--drivers/mfd/wm97xx-core.c10
-rw-r--r--drivers/misc/Kconfig178
-rw-r--r--drivers/misc/Makefile24
-rw-r--r--drivers/misc/ad525x_dpot-i2c.c7
-rw-r--r--drivers/misc/ad525x_dpot.c7
-rw-r--r--drivers/misc/altera-stapl/Makefile3
-rw-r--r--drivers/misc/altera-stapl/altera.c16
-rw-r--r--drivers/misc/amd-sbi/Kconfig23
-rw-r--r--drivers/misc/amd-sbi/Makefile4
-rw-r--r--drivers/misc/amd-sbi/rmi-core.c592
-rw-r--r--drivers/misc/amd-sbi/rmi-core.h74
-rw-r--r--drivers/misc/amd-sbi/rmi-hwmon.c120
-rw-r--r--drivers/misc/amd-sbi/rmi-i2c.c233
-rw-r--r--drivers/misc/apds9802als.c8
-rw-r--r--drivers/misc/apds990x.c35
-rw-r--r--drivers/misc/atmel-ssc.c11
-rw-r--r--drivers/misc/bcm-vk/bcm_vk.h3
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_dev.c8
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_msg.c16
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_msg.h2
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_sg.c2
-rw-r--r--drivers/misc/bcm-vk/bcm_vk_tty.c15
-rw-r--r--drivers/misc/bh1770glc.c29
-rw-r--r--drivers/misc/c2port/core.c31
-rw-r--r--drivers/misc/cardreader/Kconfig3
-rw-r--r--drivers/misc/cardreader/Makefile2
-rw-r--r--drivers/misc/cardreader/alcor_pci.c182
-rw-r--r--drivers/misc/cardreader/rts5227.c66
-rw-r--r--drivers/misc/cardreader/rts5228.c63
-rw-r--r--drivers/misc/cardreader/rts5249.c69
-rw-r--r--drivers/misc/cardreader/rts5260.c37
-rw-r--r--drivers/misc/cardreader/rts5261.c46
-rw-r--r--drivers/misc/cardreader/rts5264.c972
-rw-r--r--drivers/misc/cardreader/rts5264.h285
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.c136
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.h5
-rw-r--r--drivers/misc/cardreader/rtsx_usb.c33
-rw-r--r--drivers/misc/cb710/core.c8
-rw-r--r--drivers/misc/cs5535-mfgpt.c1
-rw-r--r--drivers/misc/cxl/Kconfig26
-rw-r--r--drivers/misc/cxl/Makefile14
-rw-r--r--drivers/misc/cxl/api.c532
-rw-r--r--drivers/misc/cxl/base.c125
-rw-r--r--drivers/misc/cxl/context.c362
-rw-r--r--drivers/misc/cxl/cxl.h1134
-rw-r--r--drivers/misc/cxl/cxllib.c271
-rw-r--r--drivers/misc/cxl/debugfs.c134
-rw-r--r--drivers/misc/cxl/fault.c356
-rw-r--r--drivers/misc/cxl/file.c700
-rw-r--r--drivers/misc/cxl/flash.c538
-rw-r--r--drivers/misc/cxl/guest.c1204
-rw-r--r--drivers/misc/cxl/hcalls.c643
-rw-r--r--drivers/misc/cxl/hcalls.h200
-rw-r--r--drivers/misc/cxl/irq.c450
-rw-r--r--drivers/misc/cxl/main.c383
-rw-r--r--drivers/misc/cxl/native.c1597
-rw-r--r--drivers/misc/cxl/of.c506
-rw-r--r--drivers/misc/cxl/pci.c2111
-rw-r--r--drivers/misc/cxl/sysfs.c771
-rw-r--r--drivers/misc/cxl/trace.c9
-rw-r--r--drivers/misc/cxl/trace.h691
-rw-r--r--drivers/misc/cxl/vphb.c316
-rw-r--r--drivers/misc/ds1682.c49
-rw-r--r--drivers/misc/dw-xdata-pcie.c11
-rw-r--r--drivers/misc/echo/Kconfig9
-rw-r--r--drivers/misc/echo/Makefile2
-rw-r--r--drivers/misc/echo/echo.c589
-rw-r--r--drivers/misc/echo/echo.h175
-rw-r--r--drivers/misc/echo/fir.h154
-rw-r--r--drivers/misc/echo/oslec.h81
-rw-r--r--drivers/misc/eeprom/Kconfig40
-rw-r--r--drivers/misc/eeprom/Makefile2
-rw-r--r--drivers/misc/eeprom/at24.c125
-rw-r--r--drivers/misc/eeprom/at25.c385
-rw-r--r--drivers/misc/eeprom/digsy_mtc_eeprom.c48
-rw-r--r--drivers/misc/eeprom/ee1004.c245
-rw-r--r--drivers/misc/eeprom/eeprom.c217
-rw-r--r--drivers/misc/eeprom/eeprom_93cx6.c15
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c183
-rw-r--r--drivers/misc/eeprom/idt_89hpesx.c225
-rw-r--r--drivers/misc/eeprom/m24lr.c606
-rw-r--r--drivers/misc/eeprom/max6875.c11
-rw-r--r--drivers/misc/enclosure.c6
-rw-r--r--drivers/misc/fastrpc.c667
-rw-r--r--drivers/misc/gehc-achc.c8
-rw-r--r--drivers/misc/genwqe/card_base.c53
-rw-r--r--drivers/misc/genwqe/card_base.h2
-rw-r--r--drivers/misc/genwqe/card_ddcb.c2
-rw-r--r--drivers/misc/genwqe/card_dev.c2
-rw-r--r--drivers/misc/genwqe/card_utils.c10
-rw-r--r--drivers/misc/habanalabs/Kconfig26
-rw-r--r--drivers/misc/habanalabs/Makefile20
-rw-r--r--drivers/misc/habanalabs/common/Makefile15
-rw-r--r--drivers/misc/habanalabs/common/context.c435
-rw-r--r--drivers/misc/habanalabs/common/debugfs.c1922
-rw-r--r--drivers/misc/habanalabs/common/decoder.c133
-rw-r--r--drivers/misc/habanalabs/common/device.c2176
-rw-r--r--drivers/misc/habanalabs/common/habanalabs_drv.c725
-rw-r--r--drivers/misc/habanalabs/common/habanalabs_ioctl.c1004
-rw-r--r--drivers/misc/habanalabs/common/hwmon.c912
-rw-r--r--drivers/misc/habanalabs/common/irq.c571
-rw-r--r--drivers/misc/habanalabs/common/mmu/Makefile3
-rw-r--r--drivers/misc/habanalabs/common/mmu/mmu_v1.c815
-rw-r--r--drivers/misc/habanalabs/common/pci/pci.c433
-rw-r--r--drivers/misc/habanalabs/common/security.c600
-rw-r--r--drivers/misc/habanalabs/common/sysfs.c504
-rw-r--r--drivers/misc/habanalabs/gaudi2/gaudi2.c9986
-rw-r--r--drivers/misc/habanalabs/include/common/cpucp_if.h1221
-rw-r--r--drivers/misc/habanalabs/include/common/hl_boot_if.h681
-rw-r--r--drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h91
-rw-r--r--drivers/misc/habanalabs/include/gaudi2/arc/gaudi2_arc_common_packets.h213
-rw-r--r--drivers/misc/habanalabs/include/gaudi2/gaudi2_async_ids_map_extended.h2668
-rw-r--r--drivers/misc/habanalabs/include/gaudi2/gaudi2_async_virt_events.h57
-rw-r--r--drivers/misc/habanalabs/include/gaudi2/gaudi2_reg_map.h59
-rw-r--r--drivers/misc/hi6421v600-irq.c10
-rw-r--r--drivers/misc/hisi_hikey_usb.c8
-rw-r--r--drivers/misc/hmc6352.c8
-rw-r--r--drivers/misc/hpilo.c32
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c40
-rw-r--r--drivers/misc/ibmvmc.c10
-rw-r--r--drivers/misc/ics932s401.c15
-rw-r--r--drivers/misc/isl29003.c20
-rw-r--r--drivers/misc/isl29020.c10
-rw-r--r--drivers/misc/keba/Kconfig26
-rw-r--r--drivers/misc/keba/Makefile4
-rw-r--r--drivers/misc/keba/cp500.c989
-rw-r--r--drivers/misc/keba/lan9252.c359
-rw-r--r--drivers/misc/kgdbts.c4
-rw-r--r--drivers/misc/lan966x_pci.c215
-rw-r--r--drivers/misc/lan966x_pci.dtso177
-rw-r--r--drivers/misc/lattice-ecp3-config.c2
-rw-r--r--drivers/misc/lis3lv02d/Kconfig8
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.c105
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.h4
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c30
-rw-r--r--drivers/misc/lkdtm/Makefile8
-rw-r--r--drivers/misc/lkdtm/bugs.c119
-rw-r--r--drivers/misc/lkdtm/cfi.c36
-rw-r--r--drivers/misc/lkdtm/core.c24
-rw-r--r--drivers/misc/lkdtm/fortify.c102
-rw-r--r--drivers/misc/lkdtm/heap.c80
-rw-r--r--drivers/misc/lkdtm/kstack_erase.c150
-rw-r--r--drivers/misc/lkdtm/perms.c21
-rw-r--r--drivers/misc/lkdtm/refcount.c16
-rw-r--r--drivers/misc/lkdtm/stackleak.c144
-rw-r--r--drivers/misc/lkdtm/usercopy.c2
-rw-r--r--drivers/misc/mchp_pci1xxxx/Kconfig14
-rw-r--r--drivers/misc/mchp_pci1xxxx/Makefile1
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c170
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h28
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c533
-rw-r--r--drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c441
-rw-r--r--drivers/misc/mei/Kconfig50
-rw-r--r--drivers/misc/mei/Makefile9
-rw-r--r--drivers/misc/mei/bus-fixup.c164
-rw-r--r--drivers/misc/mei/bus.c371
-rw-r--r--drivers/misc/mei/client.c223
-rw-r--r--drivers/misc/mei/client.h10
-rw-r--r--drivers/misc/mei/debugfs.c19
-rw-r--r--drivers/misc/mei/dma-ring.c14
-rw-r--r--drivers/misc/mei/gsc-me.c117
-rw-r--r--drivers/misc/mei/gsc_proxy/Kconfig14
-rw-r--r--drivers/misc/mei/gsc_proxy/Makefile7
-rw-r--r--drivers/misc/mei/gsc_proxy/mei_gsc_proxy.c210
-rw-r--r--drivers/misc/mei/hbm.c168
-rw-r--r--drivers/misc/mei/hdcp/Kconfig4
-rw-r--r--drivers/misc/mei/hdcp/mei_hdcp.c143
-rw-r--r--drivers/misc/mei/hdcp/mei_hdcp.h354
-rw-r--r--drivers/misc/mei/hw-me-regs.h23
-rw-r--r--drivers/misc/mei/hw-me.c271
-rw-r--r--drivers/misc/mei/hw-me.h21
-rw-r--r--drivers/misc/mei/hw-txe.c111
-rw-r--r--drivers/misc/mei/hw-txe.h2
-rw-r--r--drivers/misc/mei/hw.h112
-rw-r--r--drivers/misc/mei/init.c96
-rw-r--r--drivers/misc/mei/interrupt.c98
-rw-r--r--drivers/misc/mei/main.c232
-rw-r--r--drivers/misc/mei/mei-trace.h6
-rw-r--r--drivers/misc/mei/mei_dev.h111
-rw-r--r--drivers/misc/mei/mei_lb.c311
-rw-r--r--drivers/misc/mei/mkhi.h55
-rw-r--r--drivers/misc/mei/pci-me.c102
-rw-r--r--drivers/misc/mei/pci-txe.c61
-rw-r--r--drivers/misc/mei/platform-vsc.c459
-rw-r--r--drivers/misc/mei/pxp/Kconfig5
-rw-r--r--drivers/misc/mei/pxp/mei_pxp.c151
-rw-r--r--drivers/misc/mei/vsc-fw-loader.c776
-rw-r--r--drivers/misc/mei/vsc-tp.c577
-rw-r--r--drivers/misc/mei/vsc-tp.h50
-rw-r--r--drivers/misc/mrvl_cn10k_dpi.c676
-rw-r--r--drivers/misc/nsm.c505
-rw-r--r--drivers/misc/ntsync.c1209
-rw-r--r--drivers/misc/ocxl/afu_irq.c4
-rw-r--r--drivers/misc/ocxl/config.c20
-rw-r--r--drivers/misc/ocxl/context.c6
-rw-r--r--drivers/misc/ocxl/file.c38
-rw-r--r--drivers/misc/ocxl/link.c22
-rw-r--r--drivers/misc/ocxl/main.c2
-rw-r--r--drivers/misc/ocxl/ocxl_internal.h2
-rw-r--r--drivers/misc/ocxl/sysfs.c18
-rw-r--r--drivers/misc/open-dice.c24
-rw-r--r--drivers/misc/pch_phub.c4
-rw-r--r--drivers/misc/pci_endpoint_test.c661
-rw-r--r--drivers/misc/phantom.c25
-rw-r--r--drivers/misc/pvpanic/pvpanic-mmio.c65
-rw-r--r--drivers/misc/pvpanic/pvpanic-pci.c66
-rw-r--r--drivers/misc/pvpanic/pvpanic.c129
-rw-r--r--drivers/misc/pvpanic/pvpanic.h13
-rw-r--r--drivers/misc/qcom-coincell.c1
-rw-r--r--drivers/misc/rp1/Kconfig20
-rw-r--r--drivers/misc/rp1/Makefile3
-rw-r--r--drivers/misc/rp1/rp1-pci.dtso25
-rw-r--r--drivers/misc/rp1/rp1_pci.c336
-rw-r--r--drivers/misc/rpmb-core.c226
-rw-r--r--drivers/misc/sgi-gru/gru.h4
-rw-r--r--drivers/misc/sgi-gru/gru_instructions.h12
-rw-r--r--drivers/misc/sgi-gru/grufault.c19
-rw-r--r--drivers/misc/sgi-gru/grufile.c76
-rw-r--r--drivers/misc/sgi-gru/gruhandles.c6
-rw-r--r--drivers/misc/sgi-gru/grukservices.c10
-rw-r--r--drivers/misc/sgi-gru/grumain.c34
-rw-r--r--drivers/misc/sgi-gru/grutables.h16
-rw-r--r--drivers/misc/sgi-gru/grutlbpurge.c2
-rw-r--r--drivers/misc/sgi-xp/xp.h6
-rw-r--r--drivers/misc/sgi-xp/xp_uv.c24
-rw-r--r--drivers/misc/sgi-xp/xpc_main.c62
-rw-r--r--drivers/misc/sgi-xp/xpc_partition.c2
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c85
-rw-r--r--drivers/misc/smpro-errmon.c610
-rw-r--r--drivers/misc/smpro-misc.c145
-rw-r--r--drivers/misc/sram-exec.c7
-rw-r--r--drivers/misc/sram.c57
-rw-r--r--drivers/misc/sram.h1
-rw-r--r--drivers/misc/ti-st/Kconfig19
-rw-r--r--drivers/misc/ti-st/Makefile7
-rw-r--r--drivers/misc/ti-st/st_core.c923
-rw-r--r--drivers/misc/ti-st/st_kim.c846
-rw-r--r--drivers/misc/ti-st/st_ll.c156
-rw-r--r--drivers/misc/ti_fpc202.c435
-rw-r--r--drivers/misc/tifm_7xx1.c8
-rw-r--r--drivers/misc/tifm_core.c14
-rw-r--r--drivers/misc/tps6594-esm.c146
-rw-r--r--drivers/misc/tps6594-pfsm.c339
-rw-r--r--drivers/misc/tsl2550.c19
-rw-r--r--drivers/misc/uacce/uacce.c136
-rw-r--r--drivers/misc/vcpu_stall_detector.c39
-rw-r--r--drivers/misc/vmw_balloon.c51
-rw-r--r--drivers/misc/vmw_vmci/vmci_context.c58
-rw-r--r--drivers/misc/vmw_vmci/vmci_context.h4
-rw-r--r--drivers/misc/vmw_vmci/vmci_datagram.c10
-rw-r--r--drivers/misc/vmw_vmci/vmci_doorbell.c53
-rw-r--r--drivers/misc/vmw_vmci/vmci_event.c8
-rw-r--r--drivers/misc/vmw_vmci/vmci_guest.c62
-rw-r--r--drivers/misc/vmw_vmci/vmci_handle_array.c12
-rw-r--r--drivers/misc/vmw_vmci/vmci_handle_array.h8
-rw-r--r--drivers/misc/vmw_vmci/vmci_host.c17
-rw-r--r--drivers/misc/vmw_vmci/vmci_queue_pair.c151
-rw-r--r--drivers/misc/vmw_vmci/vmci_resource.c3
-rw-r--r--drivers/misc/xilinx_sdfec.c27
-rw-r--r--drivers/misc/xilinx_tmr_inject.c172
-rw-r--r--drivers/misc/xilinx_tmr_manager.c220
-rw-r--r--drivers/mmc/core/Kconfig4
-rw-r--r--drivers/mmc/core/Makefile2
-rw-r--r--drivers/mmc/core/block.c615
-rw-r--r--drivers/mmc/core/bus.c71
-rw-r--r--drivers/mmc/core/bus.h4
-rw-r--r--drivers/mmc/core/card.h65
-rw-r--r--drivers/mmc/core/core.c207
-rw-r--r--drivers/mmc/core/core.h48
-rw-r--r--drivers/mmc/core/debugfs.c66
-rw-r--r--drivers/mmc/core/host.c61
-rw-r--r--drivers/mmc/core/host.h8
-rw-r--r--drivers/mmc/core/mmc.c336
-rw-r--r--drivers/mmc/core/mmc_ops.c103
-rw-r--r--drivers/mmc/core/mmc_ops.h18
-rw-r--r--drivers/mmc/core/mmc_test.c112
-rw-r--r--drivers/mmc/core/pwrseq.c2
-rw-r--r--drivers/mmc/core/pwrseq_emmc.c5
-rw-r--r--drivers/mmc/core/pwrseq_sd8787.c39
-rw-r--r--drivers/mmc/core/pwrseq_simple.c58
-rw-r--r--drivers/mmc/core/queue.c127
-rw-r--r--drivers/mmc/core/queue.h3
-rw-r--r--drivers/mmc/core/quirks.h57
-rw-r--r--drivers/mmc/core/regulator.c163
-rw-r--r--drivers/mmc/core/sd.c289
-rw-r--r--drivers/mmc/core/sd.h6
-rw-r--r--drivers/mmc/core/sd_ops.c109
-rw-r--r--drivers/mmc/core/sd_ops.h5
-rw-r--r--drivers/mmc/core/sd_uhs2.c1304
-rw-r--r--drivers/mmc/core/sdio.c30
-rw-r--r--drivers/mmc/core/sdio_bus.c50
-rw-r--r--drivers/mmc/core/sdio_cis.c12
-rw-r--r--drivers/mmc/core/sdio_io.c2
-rw-r--r--drivers/mmc/core/sdio_irq.c4
-rw-r--r--drivers/mmc/core/sdio_uart.c56
-rw-r--r--drivers/mmc/core/slot-gpio.c66
-rw-r--r--drivers/mmc/host/Kconfig197
-rw-r--r--drivers/mmc/host/Makefile11
-rw-r--r--drivers/mmc/host/alcor.c32
-rw-r--r--drivers/mmc/host/atmel-mci.c495
-rw-r--r--drivers/mmc/host/au1xmmc.c85
-rw-r--r--drivers/mmc/host/bcm2835.c83
-rw-r--r--drivers/mmc/host/cavium-octeon.c7
-rw-r--r--drivers/mmc/host/cavium-thunderx.c5
-rw-r--r--drivers/mmc/host/cavium.c10
-rw-r--r--drivers/mmc/host/cb710-mmc.c44
-rw-r--r--drivers/mmc/host/cb710-mmc.h3
-rw-r--r--drivers/mmc/host/cqhci-core.c69
-rw-r--r--drivers/mmc/host/cqhci-crypto.c46
-rw-r--r--drivers/mmc/host/cqhci.h10
-rw-r--r--drivers/mmc/host/davinci_mmc.c147
-rw-r--r--drivers/mmc/host/dw_mmc-bluefield.c18
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c59
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798cv200.c6
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798mv200.c246
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c9
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c9
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c47
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.h2
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c237
-rw-r--r--drivers/mmc/host/dw_mmc-starfive.c129
-rw-r--r--drivers/mmc/host/dw_mmc.c203
-rw-r--r--drivers/mmc/host/dw_mmc.h41
-rw-r--r--drivers/mmc/host/jz4740_mmc.c121
-rw-r--r--drivers/mmc/host/litex_mmc.c17
-rw-r--r--drivers/mmc/host/loongson2-mmc.c1030
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c227
-rw-r--r--drivers/mmc/host/meson-mx-sdhc-clkc.c41
-rw-r--r--drivers/mmc/host/meson-mx-sdhc-mmc.c58
-rw-r--r--drivers/mmc/host/meson-mx-sdio.c352
-rw-r--r--drivers/mmc/host/mmc_hsq.c64
-rw-r--r--drivers/mmc/host/mmc_hsq.h16
-rw-r--r--drivers/mmc/host/mmc_spi.c248
-rw-r--r--drivers/mmc/host/mmci.c371
-rw-r--r--drivers/mmc/host/mmci.h31
-rw-r--r--drivers/mmc/host/mmci_stm32_sdmmc.c206
-rw-r--r--drivers/mmc/host/moxart-mmc.c99
-rw-r--r--drivers/mmc/host/mtk-sd.c901
-rw-r--r--drivers/mmc/host/mvsdio.c40
-rw-r--r--drivers/mmc/host/mxcmmc.c110
-rw-r--r--drivers/mmc/host/mxs-mmc.c42
-rw-r--r--drivers/mmc/host/of_mmc_spi.c1
-rw-r--r--drivers/mmc/host/omap.c168
-rw-r--r--drivers/mmc/host/omap_hsmmc.c60
-rw-r--r--drivers/mmc/host/owl-mmc.c49
-rw-r--r--drivers/mmc/host/pxamci.c103
-rw-r--r--drivers/mmc/host/renesas_sdhi.h28
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c333
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c151
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c29
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c40
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c107
-rw-r--r--drivers/mmc/host/s3cmci.c1777
-rw-r--r--drivers/mmc/host/s3cmci.h75
-rw-r--r--drivers/mmc/host/sdhci-acpi.c122
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c19
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c327
-rw-r--r--drivers/mmc/host/sdhci-cadence.c288
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c113
-rw-r--r--drivers/mmc/host/sdhci-cqhci.h24
-rw-r--r--drivers/mmc/host/sdhci-dove.c20
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c615
-rw-r--r--drivers/mmc/host/sdhci-esdhc-mcf.c43
-rw-r--r--drivers/mmc/host/sdhci-iproc.c46
-rw-r--r--drivers/mmc/host/sdhci-milbeaut.c27
-rw-r--r--drivers/mmc/host/sdhci-msm.c452
-rw-r--r--drivers/mmc/host/sdhci-npcm.c85
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c446
-rw-r--r--drivers/mmc/host/sdhci-of-aspeed.c27
-rw-r--r--drivers/mmc/host/sdhci-of-at91.c58
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c1801
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c56
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c4
-rw-r--r--drivers/mmc/host/sdhci-of-k1.c308
-rw-r--r--drivers/mmc/host/sdhci-of-ma35d1.c307
-rw-r--r--drivers/mmc/host/sdhci-of-sparx5.c35
-rw-r--r--drivers/mmc/host/sdhci-omap.c61
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c249
-rw-r--r--drivers/mmc/host/sdhci-pci-gli.c1129
-rw-r--r--drivers/mmc/host/sdhci-pci-o2micro.c291
-rw-r--r--drivers/mmc/host/sdhci-pci.h10
-rw-r--r--drivers/mmc/host/sdhci-pic32.c13
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c74
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h9
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c178
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c78
-rw-r--r--drivers/mmc/host/sdhci-s3c.c81
-rw-r--r--drivers/mmc/host/sdhci-spear.c29
-rw-r--r--drivers/mmc/host/sdhci-sprd.c263
-rw-r--r--drivers/mmc/host/sdhci-st.c25
-rw-r--r--drivers/mmc/host/sdhci-tegra.c108
-rw-r--r--drivers/mmc/host/sdhci-uhs2.c1251
-rw-r--r--drivers/mmc/host/sdhci-uhs2.h188
-rw-r--r--drivers/mmc/host/sdhci-xenon-phy.c48
-rw-r--r--drivers/mmc/host/sdhci-xenon.c74
-rw-r--r--drivers/mmc/host/sdhci-xenon.h3
-rw-r--r--drivers/mmc/host/sdhci.c623
-rw-r--r--drivers/mmc/host/sdhci.h129
-rw-r--r--drivers/mmc/host/sdhci_am654.c492
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.c113
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.h3
-rw-r--r--drivers/mmc/host/sdricoh_cs.c18
-rw-r--r--drivers/mmc/host/sh_mmcif.c156
-rw-r--r--drivers/mmc/host/sunplus-mmc.c997
-rw-r--r--drivers/mmc/host/sunxi-mmc.c55
-rw-r--r--drivers/mmc/host/tifm_sd.c58
-rw-r--r--drivers/mmc/host/tmio_mmc.c227
-rw-r--r--drivers/mmc/host/tmio_mmc.h50
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c113
-rw-r--r--drivers/mmc/host/toshsd.c18
-rw-r--r--drivers/mmc/host/uniphier-sd.c122
-rw-r--r--drivers/mmc/host/usdhi6rol0.c47
-rw-r--r--drivers/mmc/host/ushc.c4
-rw-r--r--drivers/mmc/host/via-sdmmc.c54
-rw-r--r--drivers/mmc/host/vub300.c61
-rw-r--r--drivers/mmc/host/wbsd.c111
-rw-r--r--drivers/mmc/host/wbsd.h10
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c50
-rw-r--r--drivers/most/Kconfig2
-rw-r--r--drivers/most/configfs.c8
-rw-r--r--drivers/most/core.c16
-rw-r--r--drivers/most/most_cdev.c13
-rw-r--r--drivers/most/most_snd.c10
-rw-r--r--drivers/most/most_usb.c39
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c23
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c14
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c4
-rw-r--r--drivers/mtd/chips/cfi_probe.c5
-rw-r--r--drivers/mtd/chips/cfi_util.c4
-rw-r--r--drivers/mtd/chips/gen_probe.c2
-rw-r--r--drivers/mtd/chips/jedec_probe.c7
-rw-r--r--drivers/mtd/chips/map_ram.c11
-rw-r--r--drivers/mtd/chips/map_rom.c3
-rw-r--r--drivers/mtd/devices/Kconfig21
-rw-r--r--drivers/mtd/devices/Makefile2
-rw-r--r--drivers/mtd/devices/bcm47xxsflash.c4
-rw-r--r--drivers/mtd/devices/block2mtd.c95
-rw-r--r--drivers/mtd/devices/docg3.c32
-rw-r--r--drivers/mtd/devices/docg3.h2
-rw-r--r--drivers/mtd/devices/lart.c682
-rw-r--r--drivers/mtd/devices/mchp23k256.c3
-rw-r--r--drivers/mtd/devices/mchp48l640.c39
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c12
-rw-r--r--drivers/mtd/devices/mtd_intel_dg.c880
-rw-r--r--drivers/mtd/devices/phram.c17
-rw-r--r--drivers/mtd/devices/powernv_flash.c7
-rw-r--r--drivers/mtd/devices/slram.c2
-rw-r--r--drivers/mtd/devices/spear_smi.c28
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c52
-rw-r--r--drivers/mtd/ftl.c8
-rw-r--r--drivers/mtd/hyperbus/hbmc-am654.c26
-rw-r--r--drivers/mtd/hyperbus/rpc-if.c31
-rw-r--r--drivers/mtd/inftlcore.c17
-rw-r--r--drivers/mtd/lpddr/lpddr2_nvm.c10
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c27
-rw-r--r--drivers/mtd/lpddr/qinfo_probe.c4
-rw-r--r--drivers/mtd/maps/Kconfig7
-rw-r--r--drivers/mtd/maps/Makefile12
-rw-r--r--drivers/mtd/maps/intel_vr_nor.c265
-rw-r--r--drivers/mtd/maps/lantiq-flash.c15
-rw-r--r--drivers/mtd/maps/map_funcs.c1
-rw-r--r--drivers/mtd/maps/pcmciamtd.c1
-rw-r--r--drivers/mtd/maps/physmap-bt1-rom.c1
-rw-r--r--drivers/mtd/maps/physmap-core.c26
-rw-r--r--drivers/mtd/maps/physmap-gemini.c2
-rw-r--r--drivers/mtd/maps/physmap-ixp4xx.c2
-rw-r--r--drivers/mtd/maps/physmap-ixp4xx.h1
-rw-r--r--drivers/mtd/maps/physmap-versatile.c4
-rw-r--r--drivers/mtd/maps/pismo.c7
-rw-r--r--drivers/mtd/maps/plat-ram.c9
-rw-r--r--drivers/mtd/maps/pxa2xx-flash.c5
-rw-r--r--drivers/mtd/maps/sa1100-flash.c10
-rw-r--r--drivers/mtd/maps/sun_uflash.c10
-rw-r--r--drivers/mtd/maps/vmu-flash.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c46
-rw-r--r--drivers/mtd/mtdblock.c16
-rw-r--r--drivers/mtd/mtdblock_ro.c6
-rw-r--r--drivers/mtd/mtdchar.c147
-rw-r--r--drivers/mtd/mtdconcat.c4
-rw-r--r--drivers/mtd/mtdcore.c333
-rw-r--r--drivers/mtd/mtdcore.h1
-rw-r--r--drivers/mtd/mtdoops.c120
-rw-r--r--drivers/mtd/mtdpart.c39
-rw-r--r--drivers/mtd/mtdpstore.c14
-rw-r--r--drivers/mtd/mtdsuper.c45
-rw-r--r--drivers/mtd/mtdswap.c10
-rw-r--r--drivers/mtd/nand/Kconfig8
-rw-r--r--drivers/mtd/nand/Makefile4
-rw-r--r--drivers/mtd/nand/bbt.c7
-rw-r--r--drivers/mtd/nand/core.c134
-rw-r--r--drivers/mtd/nand/ecc-mtk.c28
-rw-r--r--drivers/mtd/nand/ecc-mxic.c31
-rw-r--r--drivers/mtd/nand/ecc-realtek.c464
-rw-r--r--drivers/mtd/nand/ecc-sw-bch.c2
-rw-r--r--drivers/mtd/nand/ecc-sw-hamming.c2
-rw-r--r--drivers/mtd/nand/ecc.c4
-rw-r--r--drivers/mtd/nand/onenand/Kconfig3
-rw-r--r--drivers/mtd/nand/onenand/generic.c4
-rw-r--r--drivers/mtd/nand/onenand/onenand_base.c17
-rw-r--r--drivers/mtd/nand/onenand/onenand_bbt.c2
-rw-r--r--drivers/mtd/nand/onenand/onenand_omap2.c17
-rw-r--r--drivers/mtd/nand/onenand/onenand_samsung.c15
-rw-r--r--drivers/mtd/nand/qpic_common.c779
-rw-r--r--drivers/mtd/nand/raw/Kconfig104
-rw-r--r--drivers/mtd/nand/raw/Makefile7
-rw-r--r--drivers/mtd/nand/raw/ams-delta.c6
-rw-r--r--drivers/mtd/nand/raw/arasan-nand-controller.c84
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c52
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.c19
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.h2
-rw-r--r--drivers/mtd/nand/raw/au1550nd.c3
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/main.c4
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c7
-rw-r--r--drivers/mtd/nand/raw/brcmnand/Kconfig49
-rw-r--r--drivers/mtd/nand/raw/brcmnand/Makefile8
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c101
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcmbca_nand.c126
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c927
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.h4
-rw-r--r--drivers/mtd/nand/raw/brcmnand/iproc_nand.c7
-rw-r--r--drivers/mtd/nand/raw/cadence-nand-controller.c422
-rw-r--r--drivers/mtd/nand/raw/cafe_nand.c2
-rw-r--r--drivers/mtd/nand/raw/cs553x_nand.c8
-rw-r--r--drivers/mtd/nand/raw/davinci_nand.c273
-rw-r--r--drivers/mtd/nand/raw/denali.h2
-rw-r--r--drivers/mtd/nand/raw/denali_dt.c34
-rw-r--r--drivers/mtd/nand/raw/denali_pci.c2
-rw-r--r--drivers/mtd/nand/raw/diskonchip.c16
-rw-r--r--drivers/mtd/nand/raw/fsl_elbc_nand.c43
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c7
-rw-r--r--drivers/mtd/nand/raw/fsl_upm.c12
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c33
-rw-r--r--drivers/mtd/nand/raw/gpio.c4
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c124
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h6
-rw-r--r--drivers/mtd/nand/raw/hisi504_nand.c4
-rw-r--r--drivers/mtd/nand/raw/ingenic/ingenic_ecc.c1
-rw-r--r--drivers/mtd/nand/raw/ingenic/ingenic_ecc.h8
-rw-r--r--drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c19
-rw-r--r--drivers/mtd/nand/raw/intel-nand-controller.c80
-rw-r--r--drivers/mtd/nand/raw/internals.h2
-rw-r--r--drivers/mtd/nand/raw/loongson-nand-controller.c1024
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c84
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c88
-rw-r--r--drivers/mtd/nand/raw/marvell_nand.c95
-rw-r--r--drivers/mtd/nand/raw/meson_nand.c390
-rw-r--r--drivers/mtd/nand/raw/mpc5121_nfc.c21
-rw-r--r--drivers/mtd/nand/raw/mtk_nand.c107
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.c724
-rw-r--r--drivers/mtd/nand/raw/mxic_nand.c3
-rw-r--r--drivers/mtd/nand/raw/nand_base.c488
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c9
-rw-r--r--drivers/mtd/nand/raw/nand_hynix.c20
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c5
-rw-r--r--drivers/mtd/nand/raw/nand_jedec.c6
-rw-r--r--drivers/mtd/nand/raw/nand_macronix.c174
-rw-r--r--drivers/mtd/nand/raw/nand_onfi.c6
-rw-r--r--drivers/mtd/nand/raw/nand_sandisk.c26
-rw-r--r--drivers/mtd/nand/raw/nandsim.c34
-rw-r--r--drivers/mtd/nand/raw/ndfc.c7
-rw-r--r--drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c1029
-rw-r--r--drivers/mtd/nand/raw/omap2.c59
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c29
-rw-r--r--drivers/mtd/nand/raw/orion_nand.c43
-rw-r--r--drivers/mtd/nand/raw/oxnas_nand.c211
-rw-r--r--drivers/mtd/nand/raw/pasemi_nand.c67
-rw-r--r--drivers/mtd/nand/raw/pl35x-nand-controller.c31
-rw-r--r--drivers/mtd/nand/raw/plat_nand.c6
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c2524
-rw-r--r--drivers/mtd/nand/raw/r852.c7
-rw-r--r--drivers/mtd/nand/raw/renesas-nand-controller.c29
-rw-r--r--drivers/mtd/nand/raw/rockchip-nand-controller.c89
-rw-r--r--drivers/mtd/nand/raw/s3c2410.c1295
-rw-r--r--drivers/mtd/nand/raw/sh_flctl.c11
-rw-r--r--drivers/mtd/nand/raw/sharpsl.c4
-rw-r--r--drivers/mtd/nand/raw/sm_common.c6
-rw-r--r--drivers/mtd/nand/raw/socrates_nand.c7
-rw-r--r--drivers/mtd/nand/raw/stm32_fmc2_nand.c174
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c565
-rw-r--r--drivers/mtd/nand/raw/technologic-nand-controller.c222
-rw-r--r--drivers/mtd/nand/raw/tegra_nand.c12
-rw-r--r--drivers/mtd/nand/raw/tmio_nand.c533
-rw-r--r--drivers/mtd/nand/raw/txx9ndfmc.c13
-rw-r--r--drivers/mtd/nand/raw/vf610_nfc.c48
-rw-r--r--drivers/mtd/nand/raw/xway_nand.c10
-rw-r--r--drivers/mtd/nand/spi/Makefile4
-rw-r--r--drivers/mtd/nand/spi/alliancememory.c153
-rw-r--r--drivers/mtd/nand/spi/ato.c14
-rw-r--r--drivers/mtd/nand/spi/core.c465
-rw-r--r--drivers/mtd/nand/spi/esmt.c257
-rw-r--r--drivers/mtd/nand/spi/fmsh.c146
-rw-r--r--drivers/mtd/nand/spi/foresee.c105
-rw-r--r--drivers/mtd/nand/spi/gigadevice.c219
-rw-r--r--drivers/mtd/nand/spi/macronix.c286
-rw-r--r--drivers/mtd/nand/spi/micron.c171
-rw-r--r--drivers/mtd/nand/spi/otp.c362
-rw-r--r--drivers/mtd/nand/spi/paragon.c20
-rw-r--r--drivers/mtd/nand/spi/skyhigh.c147
-rw-r--r--drivers/mtd/nand/spi/toshiba.c57
-rw-r--r--drivers/mtd/nand/spi/winbond.c466
-rw-r--r--drivers/mtd/nand/spi/xtx.c154
-rw-r--r--drivers/mtd/nftlcore.c49
-rw-r--r--drivers/mtd/parsers/Kconfig40
-rw-r--r--drivers/mtd/parsers/Makefile3
-rw-r--r--drivers/mtd/parsers/ar7part.c129
-rw-r--r--drivers/mtd/parsers/bcm47xxpart.c6
-rw-r--r--drivers/mtd/parsers/bcm63xxpart.c1
-rw-r--r--drivers/mtd/parsers/brcm_u-boot.c85
-rw-r--r--drivers/mtd/parsers/cmdlinepart.c22
-rw-r--r--drivers/mtd/parsers/ofpart_core.c23
-rw-r--r--drivers/mtd/parsers/redboot.c2
-rw-r--r--drivers/mtd/parsers/scpart.c2
-rw-r--r--drivers/mtd/parsers/tplink_safeloader.c153
-rw-r--r--drivers/mtd/rfd_ftl.c4
-rw-r--r--drivers/mtd/sm_ftl.c17
-rw-r--r--drivers/mtd/spi-nor/Makefile3
-rw-r--r--drivers/mtd/spi-nor/atmel.c149
-rw-r--r--drivers/mtd/spi-nor/catalyst.c24
-rw-r--r--drivers/mtd/spi-nor/controllers/hisi-sfc.c5
-rw-r--r--drivers/mtd/spi-nor/controllers/nxp-spifi.c44
-rw-r--r--drivers/mtd/spi-nor/core.c1374
-rw-r--r--drivers/mtd/spi-nor/core.h245
-rw-r--r--drivers/mtd/spi-nor/debugfs.c46
-rw-r--r--drivers/mtd/spi-nor/eon.c74
-rw-r--r--drivers/mtd/spi-nor/esmt.c29
-rw-r--r--drivers/mtd/spi-nor/everspin.c39
-rw-r--r--drivers/mtd/spi-nor/fujitsu.c21
-rw-r--r--drivers/mtd/spi-nor/gigadevice.c106
-rw-r--r--drivers/mtd/spi-nor/intel.c23
-rw-r--r--drivers/mtd/spi-nor/issi.c123
-rw-r--r--drivers/mtd/spi-nor/macronix.c357
-rw-r--r--drivers/mtd/spi-nor/micron-st.c537
-rw-r--r--drivers/mtd/spi-nor/otp.c9
-rw-r--r--drivers/mtd/spi-nor/sfdp.c332
-rw-r--r--drivers/mtd/spi-nor/sfdp.h46
-rw-r--r--drivers/mtd/spi-nor/spansion.c1090
-rw-r--r--drivers/mtd/spi-nor/sst.c215
-rw-r--r--drivers/mtd/spi-nor/swp.c73
-rw-r--r--drivers/mtd/spi-nor/sysfs.c30
-rw-r--r--drivers/mtd/spi-nor/winbond.c449
-rw-r--r--drivers/mtd/spi-nor/xilinx.c172
-rw-r--r--drivers/mtd/spi-nor/xmc.c19
-rw-r--r--drivers/mtd/ssfdc.c10
-rw-r--r--drivers/mtd/tests/Makefile34
-rw-r--r--drivers/mtd/tests/mtd_nandecctest.c12
-rw-r--r--drivers/mtd/tests/mtd_test.c9
-rw-r--r--drivers/mtd/tests/nandbiterrs.c2
-rw-r--r--drivers/mtd/tests/oobtest.c10
-rw-r--r--drivers/mtd/tests/pagetest.c2
-rw-r--r--drivers/mtd/tests/readtest.c2
-rw-r--r--drivers/mtd/tests/speedtest.c2
-rw-r--r--drivers/mtd/tests/stresstest.c19
-rw-r--r--drivers/mtd/tests/subpagetest.c2
-rw-r--r--drivers/mtd/ubi/Kconfig22
-rw-r--r--drivers/mtd/ubi/Makefile1
-rw-r--r--drivers/mtd/ubi/attach.c16
-rw-r--r--drivers/mtd/ubi/block.c284
-rw-r--r--drivers/mtd/ubi/build.c241
-rw-r--r--drivers/mtd/ubi/cdev.c77
-rw-r--r--drivers/mtd/ubi/debug.c134
-rw-r--r--drivers/mtd/ubi/debug.h304
-rw-r--r--drivers/mtd/ubi/eba.c36
-rw-r--r--drivers/mtd/ubi/fastmap-wl.c151
-rw-r--r--drivers/mtd/ubi/fastmap.c83
-rw-r--r--drivers/mtd/ubi/io.c98
-rw-r--r--drivers/mtd/ubi/kapi.c84
-rw-r--r--drivers/mtd/ubi/misc.c2
-rw-r--r--drivers/mtd/ubi/nvmem.c191
-rw-r--r--drivers/mtd/ubi/ubi-media.h2
-rw-r--r--drivers/mtd/ubi/ubi.h82
-rw-r--r--drivers/mtd/ubi/vmt.c95
-rw-r--r--drivers/mtd/ubi/vtbl.c6
-rw-r--r--drivers/mtd/ubi/wl.c98
-rw-r--r--drivers/mtd/ubi/wl.h9
-rw-r--r--drivers/mux/Kconfig3
-rw-r--r--drivers/mux/adg792a.c4
-rw-r--r--drivers/mux/adgs1408.c4
-rw-r--r--drivers/mux/core.c15
-rw-r--r--drivers/mux/gpio.c9
-rw-r--r--drivers/mux/mmio.c118
-rw-r--r--drivers/net/Kconfig121
-rw-r--r--drivers/net/Makefile14
-rw-r--r--drivers/net/Space.c9
-rw-r--r--drivers/net/amt.c61
-rw-r--r--drivers/net/appletalk/Kconfig102
-rw-r--r--drivers/net/appletalk/Makefile7
-rw-r--r--drivers/net/appletalk/cops.c1005
-rw-r--r--drivers/net/appletalk/cops.h61
-rw-r--r--drivers/net/appletalk/cops_ffdrv.h532
-rw-r--r--drivers/net/appletalk/cops_ltdrv.h241
-rw-r--r--drivers/net/appletalk/ipddp.c345
-rw-r--r--drivers/net/appletalk/ipddp.h28
-rw-r--r--drivers/net/arcnet/Kconfig2
-rw-r--r--drivers/net/arcnet/arc-rawmode.c1
-rw-r--r--drivers/net/arcnet/arc-rimi.c1
-rw-r--r--drivers/net/arcnet/arcdevice.h5
-rw-r--r--drivers/net/arcnet/arcnet.c26
-rw-r--r--drivers/net/arcnet/capmode.c1
-rw-r--r--drivers/net/arcnet/com20020-isa.c1
-rw-r--r--drivers/net/arcnet/com20020-pci.c105
-rw-r--r--drivers/net/arcnet/com20020.c1
-rw-r--r--drivers/net/arcnet/com20020_cs.c12
-rw-r--r--drivers/net/arcnet/com90io.c1
-rw-r--r--drivers/net/arcnet/com90xx.c1
-rw-r--r--drivers/net/arcnet/rfc1051.c1
-rw-r--r--drivers/net/arcnet/rfc1201.c1
-rw-r--r--drivers/net/bareudp.c159
-rw-r--r--drivers/net/bonding/bond_3ad.c306
-rw-r--r--drivers/net/bonding/bond_alb.c19
-rw-r--r--drivers/net/bonding/bond_debugfs.c24
-rw-r--r--drivers/net/bonding/bond_main.c1213
-rw-r--r--drivers/net/bonding/bond_netlink.c94
-rw-r--r--drivers/net/bonding/bond_options.c280
-rw-r--r--drivers/net/bonding/bond_procfs.c2
-rw-r--r--drivers/net/bonding/bond_sysfs.c155
-rw-r--r--drivers/net/bonding/bond_sysfs_slave.c28
-rw-r--r--drivers/net/bonding/bonding_priv.h4
-rw-r--r--drivers/net/caif/caif_serial.c18
-rw-r--r--drivers/net/caif/caif_virtio.c11
-rw-r--r--drivers/net/can/Kconfig62
-rw-r--r--drivers/net/can/Makefile7
-rw-r--r--drivers/net/can/at91_can.c991
-rw-r--r--drivers/net/can/bxcan.c1101
-rw-r--r--drivers/net/can/c_can/Kconfig3
-rw-r--r--drivers/net/can/c_can/c_can.h17
-rw-r--r--drivers/net/can/c_can/c_can_main.c47
-rw-r--r--drivers/net/can/c_can/c_can_pci.c2
-rw-r--r--drivers/net/can/c_can/c_can_platform.c76
-rw-r--r--drivers/net/can/can327.c71
-rw-r--r--drivers/net/can/cc770/Kconfig1
-rw-r--r--drivers/net/can/cc770/cc770.c3
-rw-r--r--drivers/net/can/cc770/cc770_isa.c14
-rw-r--r--drivers/net/can/cc770/cc770_platform.c48
-rw-r--r--drivers/net/can/ctucanfd/Kconfig2
-rw-r--r--drivers/net/can/ctucanfd/ctucanfd_base.c32
-rw-r--r--drivers/net/can/ctucanfd/ctucanfd_pci.c8
-rw-r--r--drivers/net/can/ctucanfd/ctucanfd_platform.c10
-rw-r--r--drivers/net/can/dev/bittiming.c183
-rw-r--r--drivers/net/can/dev/calc_bittiming.c150
-rw-r--r--drivers/net/can/dev/dev.c252
-rw-r--r--drivers/net/can/dev/length.c15
-rw-r--r--drivers/net/can/dev/netlink.c908
-rw-r--r--drivers/net/can/dev/rx-offload.c40
-rw-r--r--drivers/net/can/dev/skb.c19
-rw-r--r--drivers/net/can/dummy_can.c285
-rw-r--r--drivers/net/can/esd/Kconfig12
-rw-r--r--drivers/net/can/esd/Makefile7
-rw-r--r--drivers/net/can/esd/esd_402_pci-core.c515
-rw-r--r--drivers/net/can/esd/esdacc.c769
-rw-r--r--drivers/net/can/esd/esdacc.h358
-rw-r--r--drivers/net/can/flexcan/flexcan-core.c192
-rw-r--r--drivers/net/can/flexcan/flexcan.h8
-rw-r--r--drivers/net/can/grcan.c29
-rw-r--r--drivers/net/can/ifi_canfd/ifi_canfd.c78
-rw-r--r--drivers/net/can/janz-ican3.c9
-rw-r--r--drivers/net/can/kvaser_pciefd.c1923
-rw-r--r--drivers/net/can/kvaser_pciefd/Makefile3
-rw-r--r--drivers/net/can/kvaser_pciefd/kvaser_pciefd.h96
-rw-r--r--drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c1908
-rw-r--r--drivers/net/can/kvaser_pciefd/kvaser_pciefd_devlink.c60
-rw-r--r--drivers/net/can/m_can/m_can.c1377
-rw-r--r--drivers/net/can/m_can/m_can.h67
-rw-r--r--drivers/net/can/m_can/m_can_pci.c17
-rw-r--r--drivers/net/can/m_can/m_can_platform.c40
-rw-r--r--drivers/net/can/m_can/tcan4x5x-core.c250
-rw-r--r--drivers/net/can/m_can/tcan4x5x-regmap.c46
-rw-r--r--drivers/net/can/m_can/tcan4x5x.h2
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c20
-rw-r--r--drivers/net/can/mscan/mscan.c9
-rw-r--r--drivers/net/can/pch_can.c1249
-rw-r--r--drivers/net/can/peak_canfd/peak_canfd.c53
-rw-r--r--drivers/net/can/peak_canfd/peak_canfd_user.h4
-rw-r--r--drivers/net/can/peak_canfd/peak_pciefd_main.c6
-rw-r--r--drivers/net/can/rcar/rcar_can.c315
-rw-r--r--drivers/net/can/rcar/rcar_canfd.c1236
-rw-r--r--drivers/net/can/rockchip/Kconfig10
-rw-r--r--drivers/net/can/rockchip/Makefile10
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-core.c962
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-ethtool.c73
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-rx.c299
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-timestamp.c105
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-tx.c167
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd.h553
-rw-r--r--drivers/net/can/sja1000/Kconfig3
-rw-r--r--drivers/net/can/sja1000/ems_pci.c153
-rw-r--r--drivers/net/can/sja1000/peak_pci.c8
-rw-r--r--drivers/net/can/sja1000/peak_pcmcia.c12
-rw-r--r--drivers/net/can/sja1000/plx_pci.c3
-rw-r--r--drivers/net/can/sja1000/sja1000.c122
-rw-r--r--drivers/net/can/sja1000/sja1000.h1
-rw-r--r--drivers/net/can/sja1000/sja1000_isa.c14
-rw-r--r--drivers/net/can/sja1000/sja1000_platform.c25
-rw-r--r--drivers/net/can/slcan/slcan-core.c52
-rw-r--r--drivers/net/can/softing/softing_fw.c2
-rw-r--r--drivers/net/can/softing/softing_main.c6
-rw-r--r--drivers/net/can/spi/hi311x.c98
-rw-r--r--drivers/net/can/spi/mcp251x.c88
-rw-r--r--drivers/net/can/spi/mcp251xfd/Kconfig1
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c438
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c2
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c1
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c11
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c118
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c71
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c165
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c166
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c29
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c59
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd.h96
-rw-r--r--drivers/net/can/sun4i_can.c57
-rw-r--r--drivers/net/can/ti_hecc.c16
-rw-r--r--drivers/net/can/usb/Kconfig42
-rw-r--r--drivers/net/can/usb/Makefile2
-rw-r--r--drivers/net/can/usb/ems_usb.c61
-rw-r--r--drivers/net/can/usb/esd_usb.c833
-rw-r--r--drivers/net/can/usb/etas_es58x/Makefile2
-rw-r--r--drivers/net/can/usb/etas_es58x/es581_4.c4
-rw-r--r--drivers/net/can/usb/etas_es58x/es58x_core.c109
-rw-r--r--drivers/net/can/usb/etas_es58x/es58x_core.h58
-rw-r--r--drivers/net/can/usb/etas_es58x/es58x_devlink.c260
-rw-r--r--drivers/net/can/usb/etas_es58x/es58x_fd.c12
-rw-r--r--drivers/net/can/usb/f81604.c1204
-rw-r--r--drivers/net/can/usb/gs_usb.c595
-rw-r--r--drivers/net/can/usb/kvaser_usb/Makefile7
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb.h93
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c386
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_devlink.c87
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c449
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c770
-rw-r--r--drivers/net/can/usb/mcba_usb.c14
-rw-r--r--drivers/net/can/usb/nct6694_canfd.c831
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb.c54
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.c198
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.h22
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_fd.c88
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_pro.c34
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_pro.h5
-rw-r--r--drivers/net/can/usb/ucan.c56
-rw-r--r--drivers/net/can/usb/usb_8dev.c3
-rw-r--r--drivers/net/can/vcan.c4
-rw-r--r--drivers/net/can/vxcan.c40
-rw-r--r--drivers/net/can/xilinx_can.c265
-rw-r--r--drivers/net/dsa/Kconfig61
-rw-r--r--drivers/net/dsa/Makefile9
-rw-r--r--drivers/net/dsa/b53/Kconfig2
-rw-r--r--drivers/net/dsa/b53/b53_common.c1149
-rw-r--r--drivers/net/dsa/b53/b53_mdio.c16
-rw-r--r--drivers/net/dsa/b53/b53_mmap.c176
-rw-r--r--drivers/net/dsa/b53/b53_priv.h194
-rw-r--r--drivers/net/dsa/b53/b53_regs.h93
-rw-r--r--drivers/net/dsa/b53/b53_serdes.c8
-rw-r--r--drivers/net/dsa/b53/b53_serdes.h2
-rw-r--r--drivers/net/dsa/b53/b53_spi.c2
-rw-r--r--drivers/net/dsa/b53/b53_srab.c6
-rw-r--r--drivers/net/dsa/bcm_sf2.c148
-rw-r--r--drivers/net/dsa/bcm_sf2.h8
-rw-r--r--drivers/net/dsa/bcm_sf2_cfp.c26
-rw-r--r--drivers/net/dsa/dsa_loop.c101
-rw-r--r--drivers/net/dsa/dsa_loop.h20
-rw-r--r--drivers/net/dsa/dsa_loop_bdinfo.c35
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek.c148
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek.h19
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c35
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h7
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek_ptp.c85
-rw-r--r--drivers/net/dsa/ks8995.c857
-rw-r--r--drivers/net/dsa/lan9303-core.c259
-rw-r--r--drivers/net/dsa/lan9303_i2c.c13
-rw-r--r--drivers/net/dsa/lan9303_mdio.c14
-rw-r--r--drivers/net/dsa/lantiq/Kconfig24
-rw-r--r--drivers/net/dsa/lantiq/Makefile3
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip.c518
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip.h301
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip_common.c1739
-rw-r--r--drivers/net/dsa/lantiq/lantiq_pce.h (renamed from drivers/net/dsa/lantiq_pce.h)9
-rw-r--r--drivers/net/dsa/lantiq/mxl-gsw1xx.c733
-rw-r--r--drivers/net/dsa/lantiq/mxl-gsw1xx.h126
-rw-r--r--drivers/net/dsa/lantiq/mxl-gsw1xx_pce.h154
-rw-r--r--drivers/net/dsa/lantiq_gswip.c2287
-rw-r--r--drivers/net/dsa/microchip/Kconfig23
-rw-r--r--drivers/net/dsa/microchip/Makefile11
-rw-r--r--drivers/net/dsa/microchip/ksz8.c2115
-rw-r--r--drivers/net/dsa/microchip/ksz8.h25
-rw-r--r--drivers/net/dsa/microchip/ksz8795.c1390
-rw-r--r--drivers/net/dsa/microchip/ksz8795_reg.h812
-rw-r--r--drivers/net/dsa/microchip/ksz8863_smi.c38
-rw-r--r--drivers/net/dsa/microchip/ksz8_reg.h850
-rw-r--r--drivers/net/dsa/microchip/ksz9477.c688
-rw-r--r--drivers/net/dsa/microchip/ksz9477.h49
-rw-r--r--drivers/net/dsa/microchip/ksz9477_acl.c1436
-rw-r--r--drivers/net/dsa/microchip/ksz9477_i2c.c58
-rw-r--r--drivers/net/dsa/microchip/ksz9477_reg.h83
-rw-r--r--drivers/net/dsa/microchip/ksz9477_tc_flower.c284
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c2929
-rw-r--r--drivers/net/dsa/microchip/ksz_common.h440
-rw-r--r--drivers/net/dsa/microchip/ksz_dcb.c612
-rw-r--r--drivers/net/dsa/microchip/ksz_dcb.h23
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp.c1188
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp.h89
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp_reg.h142
-rw-r--r--drivers/net/dsa/microchip/ksz_spi.c168
-rw-r--r--drivers/net/dsa/microchip/lan937x.h3
-rw-r--r--drivers/net/dsa/microchip/lan937x_main.c344
-rw-r--r--drivers/net/dsa/microchip/lan937x_reg.h21
-rw-r--r--drivers/net/dsa/mt7530-mdio.c265
-rw-r--r--drivers/net/dsa/mt7530-mmio.c100
-rw-r--r--drivers/net/dsa/mt7530.c2555
-rw-r--r--drivers/net/dsa/mt7530.h475
-rw-r--r--drivers/net/dsa/mv88e6060.c47
-rw-r--r--drivers/net/dsa/mv88e6xxx/Kconfig14
-rw-r--r--drivers/net/dsa/mv88e6xxx/Makefile9
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c2132
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h135
-rw-r--r--drivers/net/dsa/mv88e6xxx/devlink.c48
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1.c113
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1.h5
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1_atu.c110
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1_vtu.c10
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2.c117
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2.h24
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2_scratch.c37
-rw-r--r--drivers/net/dsa/mv88e6xxx/hwtstamp.c28
-rw-r--r--drivers/net/dsa/mv88e6xxx/hwtstamp.h21
-rw-r--r--drivers/net/dsa/mv88e6xxx/leds.c848
-rw-r--r--drivers/net/dsa/mv88e6xxx/pcs-6185.c191
-rw-r--r--drivers/net/dsa/mv88e6xxx/pcs-6352.c390
-rw-r--r--drivers/net/dsa/mv88e6xxx/pcs-639x.c970
-rw-r--r--drivers/net/dsa/mv88e6xxx/phy.c41
-rw-r--r--drivers/net/dsa/mv88e6xxx/phy.h4
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.c73
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.h175
-rw-r--r--drivers/net/dsa/mv88e6xxx/ptp.c195
-rw-r--r--drivers/net/dsa/mv88e6xxx/ptp.h135
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.c1097
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.h128
-rw-r--r--drivers/net/dsa/mv88e6xxx/switchdev.c83
-rw-r--r--drivers/net/dsa/mv88e6xxx/switchdev.h19
-rw-r--r--drivers/net/dsa/mv88e6xxx/trace.c6
-rw-r--r--drivers/net/dsa/mv88e6xxx/trace.h96
-rw-r--r--drivers/net/dsa/ocelot/Kconfig32
-rw-r--r--drivers/net/dsa/ocelot/Makefile13
-rw-r--r--drivers/net/dsa/ocelot/felix.c517
-rw-r--r--drivers/net/dsa/ocelot/felix.h32
-rw-r--r--drivers/net/dsa/ocelot/felix_vsc9959.c408
-rw-r--r--drivers/net/dsa/ocelot/ocelot_ext.c112
-rw-r--r--drivers/net/dsa/ocelot/seville_vsc9953.c128
-rw-r--r--drivers/net/dsa/qca/Kconfig9
-rw-r--r--drivers/net/dsa/qca/Makefile3
-rw-r--r--drivers/net/dsa/qca/ar9331.c59
-rw-r--r--drivers/net/dsa/qca/qca8k-8xxx.c751
-rw-r--r--drivers/net/dsa/qca/qca8k-common.c230
-rw-r--r--drivers/net/dsa/qca/qca8k-leds.c487
-rw-r--r--drivers/net/dsa/qca/qca8k.h99
-rw-r--r--drivers/net/dsa/qca/qca8k_leds.h16
-rw-r--r--drivers/net/dsa/realtek/Kconfig26
-rw-r--r--drivers/net/dsa/realtek/Makefile16
-rw-r--r--drivers/net/dsa/realtek/realtek-mdio.c206
-rw-r--r--drivers/net/dsa/realtek/realtek-mdio.h48
-rw-r--r--drivers/net/dsa/realtek/realtek-smi.c284
-rw-r--r--drivers/net/dsa/realtek/realtek-smi.h48
-rw-r--r--drivers/net/dsa/realtek/realtek.h21
-rw-r--r--drivers/net/dsa/realtek/rtl8365mb.c207
-rw-r--r--drivers/net/dsa/realtek/rtl8366-core.c30
-rw-r--r--drivers/net/dsa/realtek/rtl8366rb-leds.c177
-rw-r--r--drivers/net/dsa/realtek/rtl8366rb.c465
-rw-r--r--drivers/net/dsa/realtek/rtl8366rb.h107
-rw-r--r--drivers/net/dsa/realtek/rtl83xx.c331
-rw-r--r--drivers/net/dsa/realtek/rtl83xx.h24
-rw-r--r--drivers/net/dsa/rzn1_a5psw.c417
-rw-r--r--drivers/net/dsa/rzn1_a5psw.h11
-rw-r--r--drivers/net/dsa/sja1105/sja1105.h24
-rw-r--r--drivers/net/dsa/sja1105/sja1105_clocking.c21
-rw-r--r--drivers/net/dsa/sja1105/sja1105_devlink.c14
-rw-r--r--drivers/net/dsa/sja1105/sja1105_dynamic_config.c93
-rw-r--r--drivers/net/dsa/sja1105/sja1105_ethtool.c10
-rw-r--r--drivers/net/dsa/sja1105/sja1105_flower.c11
-rw-r--r--drivers/net/dsa/sja1105/sja1105_main.c306
-rw-r--r--drivers/net/dsa/sja1105/sja1105_mdio.c174
-rw-r--r--drivers/net/dsa/sja1105/sja1105_ptp.c110
-rw-r--r--drivers/net/dsa/sja1105/sja1105_ptp.h9
-rw-r--r--drivers/net/dsa/sja1105/sja1105_spi.c28
-rw-r--r--drivers/net/dsa/sja1105/sja1105_static_config.c14
-rw-r--r--drivers/net/dsa/sja1105/sja1105_tas.c15
-rw-r--r--drivers/net/dsa/vitesse-vsc73xx-core.c1557
-rw-r--r--drivers/net/dsa/vitesse-vsc73xx-platform.c6
-rw-r--r--drivers/net/dsa/vitesse-vsc73xx.h66
-rw-r--r--drivers/net/dsa/xrs700x/xrs700x.c75
-rw-r--r--drivers/net/dsa/xrs700x/xrs700x_i2c.c15
-rw-r--r--drivers/net/dsa/yt921x.c3006
-rw-r--r--drivers/net/dsa/yt921x.h567
-rw-r--r--drivers/net/dummy.c41
-rw-r--r--drivers/net/eql.c5
-rw-r--r--drivers/net/ethernet/3com/3c515.c15
-rw-r--r--drivers/net/ethernet/3com/3c574_cs.c4
-rw-r--r--drivers/net/ethernet/3com/3c589_cs.c17
-rw-r--r--drivers/net/ethernet/3com/3c59x.c6
-rw-r--r--drivers/net/ethernet/3com/Kconfig4
-rw-r--r--drivers/net/ethernet/8390/8390.c1
-rw-r--r--drivers/net/ethernet/8390/8390.h2
-rw-r--r--drivers/net/ethernet/8390/8390p.c1
-rw-r--r--drivers/net/ethernet/8390/Kconfig6
-rw-r--r--drivers/net/ethernet/8390/apne.c8
-rw-r--r--drivers/net/ethernet/8390/ax88796.c4
-rw-r--r--drivers/net/ethernet/8390/axnet_cs.c13
-rw-r--r--drivers/net/ethernet/8390/etherh.c2
-rw-r--r--drivers/net/ethernet/8390/hydra.c7
-rw-r--r--drivers/net/ethernet/8390/lib8390.c5
-rw-r--r--drivers/net/ethernet/8390/mac8390.c6
-rw-r--r--drivers/net/ethernet/8390/mcf8390.c10
-rw-r--r--drivers/net/ethernet/8390/ne.c8
-rw-r--r--drivers/net/ethernet/8390/ne2k-pci.c28
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c11
-rw-r--r--drivers/net/ethernet/8390/smc-ultra.c5
-rw-r--r--drivers/net/ethernet/8390/stnic.c6
-rw-r--r--drivers/net/ethernet/8390/wd.c5
-rw-r--r--drivers/net/ethernet/8390/zorro8390.c8
-rw-r--r--drivers/net/ethernet/Kconfig20
-rw-r--r--drivers/net/ethernet/Makefile5
-rw-r--r--drivers/net/ethernet/actions/owl-emac.c17
-rw-r--r--drivers/net/ethernet/adaptec/starfire.c8
-rw-r--r--drivers/net/ethernet/adi/Kconfig1
-rw-r--r--drivers/net/ethernet/adi/adin1110.c144
-rw-r--r--drivers/net/ethernet/aeroflex/greth.c12
-rw-r--r--drivers/net/ethernet/agere/et131x.c46
-rw-r--r--drivers/net/ethernet/airoha/Kconfig34
-rw-r--r--drivers/net/ethernet/airoha/Makefile9
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.c3180
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.h673
-rw-r--r--drivers/net/ethernet/airoha/airoha_npu.c783
-rw-r--r--drivers/net/ethernet/airoha/airoha_ppe.c1561
-rw-r--r--drivers/net/ethernet/airoha/airoha_ppe_debugfs.c187
-rw-r--r--drivers/net/ethernet/airoha/airoha_regs.h923
-rw-r--r--drivers/net/ethernet/alacritech/slic.h12
-rw-r--r--drivers/net/ethernet/alacritech/slicoss.c25
-rw-r--r--drivers/net/ethernet/allwinner/sun4i-emac.c3
-rw-r--r--drivers/net/ethernet/alteon/acenic.c31
-rw-r--r--drivers/net/ethernet/alteon/acenic.h8
-rw-r--r--drivers/net/ethernet/altera/Kconfig5
-rw-r--r--drivers/net/ethernet/altera/altera_tse.h5
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c123
-rw-r--r--drivers/net/ethernet/amazon/Kconfig2
-rw-r--r--drivers/net/ethernet/amazon/ena/Makefile2
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_admin_defs.h152
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_com.c830
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_com.h187
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_debugfs.c62
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_debugfs.h27
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_devlink.c210
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_devlink.h21
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_eth_com.c87
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_eth_com.h45
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_ethtool.c393
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_netdev.c1595
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_netdev.h133
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_phc.c233
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_phc.h37
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_regs_defs.h10
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_xdp.c469
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_xdp.h151
-rw-r--r--drivers/net/ethernet/amd/7990.c1
-rw-r--r--drivers/net/ethernet/amd/Kconfig19
-rw-r--r--drivers/net/ethernet/amd/Makefile1
-rw-r--r--drivers/net/ethernet/amd/a2065.c5
-rw-r--r--drivers/net/ethernet/amd/amd8111e.c14
-rw-r--r--drivers/net/ethernet/amd/amd8111e.h2
-rw-r--r--drivers/net/ethernet/amd/ariadne.c1
-rw-r--r--drivers/net/ethernet/amd/atarilance.c5
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.c6
-rw-r--r--drivers/net/ethernet/amd/declance.c4
-rw-r--r--drivers/net/ethernet/amd/hplance.c1
-rw-r--r--drivers/net/ethernet/amd/lance.c4
-rw-r--r--drivers/net/ethernet/amd/mvme147.c8
-rw-r--r--drivers/net/ethernet/amd/nmclan_cs.c4
-rw-r--r--drivers/net/ethernet/amd/pcnet32.c15
-rw-r--r--drivers/net/ethernet/amd/pds_core/Makefile13
-rw-r--r--drivers/net/ethernet/amd/pds_core/adminq.c310
-rw-r--r--drivers/net/ethernet/amd/pds_core/auxbus.c256
-rw-r--r--drivers/net/ethernet/amd/pds_core/core.c661
-rw-r--r--drivers/net/ethernet/amd/pds_core/core.h325
-rw-r--r--drivers/net/ethernet/amd/pds_core/debugfs.c175
-rw-r--r--drivers/net/ethernet/amd/pds_core/dev.c382
-rw-r--r--drivers/net/ethernet/amd/pds_core/devlink.c179
-rw-r--r--drivers/net/ethernet/amd/pds_core/fw.c197
-rw-r--r--drivers/net/ethernet/amd/pds_core/main.c609
-rw-r--r--drivers/net/ethernet/amd/sun3lance.c1
-rw-r--r--drivers/net/ethernet/amd/sunlance.c10
-rw-r--r--drivers/net/ethernet/amd/xgbe/Makefile4
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-common.h213
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dcb.c117
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c136
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-desc.c126
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c554
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c512
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c226
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-hwtstamp.c399
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-i2c.c137
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-main.c119
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c223
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-pci.c211
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c117
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c640
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-platform.c171
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-pps.c74
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ptp.c238
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-selftest.c346
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-smn.h30
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h270
-rw-r--r--drivers/net/ethernet/apm/xgene-v2/main.c8
-rw-r--r--drivers/net/ethernet/apm/xgene-v2/main.h1
-rw-r--r--drivers/net/ethernet/apm/xgene-v2/mdio.c18
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_hw.c16
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.c31
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.h3
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c10
-rw-r--r--drivers/net/ethernet/apple/bmac.c77
-rw-r--r--drivers/net/ethernet/apple/mace.c12
-rw-r--r--drivers/net/ethernet/apple/macmace.c6
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/Makefile2
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_cfg.h2
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_drvinfo.c16
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c161
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h8
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_hw.h7
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c22
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h1
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_macsec.c136
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_main.c74
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_main.h2
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_nic.c21
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_nic.h2
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c23
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ptp.c48
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ptp.h12
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ring.c148
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ring.h23
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_vec.c23
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c2
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c153
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c43
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h21
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h32
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h1
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c16
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c45
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/macsec/macsec_api.c18
-rw-r--r--drivers/net/ethernet/arc/Kconfig10
-rw-r--r--drivers/net/ethernet/arc/Makefile1
-rw-r--r--drivers/net/ethernet/arc/emac.h2
-rw-r--r--drivers/net/ethernet/arc/emac_arc.c90
-rw-r--r--drivers/net/ethernet/arc/emac_main.c33
-rw-r--r--drivers/net/ethernet/arc/emac_mdio.c9
-rw-r--r--drivers/net/ethernet/arc/emac_rockchip.c6
-rw-r--r--drivers/net/ethernet/asix/ax88796c_ioctl.c2
-rw-r--r--drivers/net/ethernet/asix/ax88796c_main.c10
-rw-r--r--drivers/net/ethernet/asix/ax88796c_main.h8
-rw-r--r--drivers/net/ethernet/atheros/Kconfig4
-rw-r--r--drivers/net/ethernet/atheros/ag71xx.c234
-rw-r--r--drivers/net/ethernet/atheros/alx/ethtool.c5
-rw-r--r--drivers/net/ethernet/atheros/alx/main.c23
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c.h3
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c107
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c20
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl1.c98
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.c19
-rw-r--r--drivers/net/ethernet/broadcom/Kconfig36
-rw-r--r--drivers/net/ethernet/broadcom/Makefile7
-rw-r--r--drivers/net/ethernet/broadcom/asp2/Makefile2
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp.c1469
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp.h600
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c464
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c1423
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_intf_defs.h256
-rw-r--r--drivers/net/ethernet/broadcom/b44.c101
-rw-r--r--drivers/net/ethernet/broadcom/bcm4908_enet.c74
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c47
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c106
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.h36
-rw-r--r--drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c1
-rw-r--r--drivers/net/ethernet/broadcom/bgmac-bcma.c7
-rw-r--r--drivers/net/ethernet/broadcom/bgmac-platform.c10
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c21
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.h5
-rw-r--r--drivers/net/ethernet/broadcom/bnge/Makefile13
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge.h255
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_auxr.c258
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_auxr.h84
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_core.c420
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_db.h34
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_devlink.c306
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_devlink.h18
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c33
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_ethtool.h9
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c548
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h112
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c1185
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h58
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_netdev.c2485
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_netdev.h454
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.c617
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.h97
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_rmem.c499
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_rmem.h202
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c81
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.h1
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h5
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c68
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h6
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c202
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_defs.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c14
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c155
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c8
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c16
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c17
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/Makefile1
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c7889
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h980
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.c305
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.h53
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c32
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_debugfs.c6
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_debugfs.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c270
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_dim.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c2651
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h7
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h10231
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hwmon.c241
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hwmon.h30
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.c9
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.h18
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c433
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.h100
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c237
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c19
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c629
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h79
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c50
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h6
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c155
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h11
-rw-r--r--drivers/net/ethernet/broadcom/cnic.c61
-rw-r--r--drivers/net/ethernet/broadcom/cnic.h3
-rw-r--r--drivers/net/ethernet/broadcom/cnic_if.h1
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c1553
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h91
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c131
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c132
-rw-r--r--drivers/net/ethernet/broadcom/sb1250-mac.c10
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c650
-rw-r--r--drivers/net/ethernet/broadcom/tg3.h13
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_cs.h60
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.c38
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.h8
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_msgq.c2
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_msgq.h8
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_enet.c6
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_tx_rx.c6
-rw-r--r--drivers/net/ethernet/brocade/bna/bna_types.h29
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.c73
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.h2
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad_debugfs.c40
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad_ethtool.c6
-rw-r--r--drivers/net/ethernet/cadence/macb.h356
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c1643
-rw-r--r--drivers/net/ethernet/cadence/macb_pci.c5
-rw-r--r--drivers/net/ethernet/cadence/macb_ptp.c131
-rw-r--r--drivers/net/ethernet/calxeda/xgmac.c6
-rw-r--r--drivers/net/ethernet/cavium/Kconfig7
-rw-r--r--drivers/net/ethernet/cavium/common/cavium_ptp.c9
-rw-r--r--drivers/net/ethernet/cavium/liquidio/Makefile8
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c294
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h5
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c9
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h2
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn66xx_device.c1
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn66xx_device.h1
-rw-r--r--drivers/net/ethernet/cavium/liquidio/cn68xx_device.c1
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_core.c21
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_ethtool.c37
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_main.c135
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_vf_main.c67
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c16
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_console.c7
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_device.c51
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_device.h14
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_droq.c9
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_droq.h2
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_iq.h3
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h5
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_main.h2
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c5
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_nic.c3
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_nic.h4
-rw-r--r--drivers/net/ethernet/cavium/liquidio/request_manager.c27
-rw-r--r--drivers/net/ethernet/cavium/liquidio/response_manager.c6
-rw-r--r--drivers/net/ethernet/cavium/octeon/octeon_mgmt.c71
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c105
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c65
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.c8
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.h2
-rw-r--r--drivers/net/ethernet/cavium/thunder/thunder_bgx.c77
-rw-r--r--drivers/net/ethernet/cavium/thunder/thunder_bgx.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/common.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cxgb2.c7
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/pm3393.c8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/sge.c10
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/tp.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/adapter.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_defs.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c31
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c39
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.h3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/l2t.c37
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/l2t.h3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/sge.c33
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c28
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h43
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c143
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_fcoe.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c237
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_mps.c98
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c13
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c121
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32_parse.h4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_thermal.c43
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.c21
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sched.c44
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sched.h12
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c55
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/smt.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/srq.c58
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/srq.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c9
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c12
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c2
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c75
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.h1
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c74
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.h36
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h6
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c30
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.h7
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c9
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c167
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c11
-rw-r--r--drivers/net/ethernet/chelsio/libcxgb/Makefile2
-rw-r--r--drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c8
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c13
-rw-r--r--drivers/net/ethernet/cirrus/ep93xx_eth.c72
-rw-r--r--drivers/net/ethernet/cirrus/mac89x0.c4
-rw-r--r--drivers/net/ethernet/cisco/enic/Kconfig1
-rw-r--r--drivers/net/ethernet/cisco/enic/Makefile2
-rw-r--r--drivers/net/ethernet/cisco/enic/cq_desc.h25
-rw-r--r--drivers/net/ethernet/cisco/enic/cq_enet_desc.h142
-rw-r--r--drivers/net/ethernet/cisco/enic/enic.h131
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_clsf.c2
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_clsf.h2
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_ethtool.c215
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c854
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_res.c129
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_res.h11
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_rq.c436
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_rq.h8
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_wq.c117
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_wq.h7
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_cq.h45
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.c20
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.h5
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_devcmd.h19
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_enet.h5
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rq.h4
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_vic.c3
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_wq.h2
-rw-r--r--drivers/net/ethernet/cortina/gemini.c197
-rw-r--r--drivers/net/ethernet/cortina/gemini.h4
-rw-r--r--drivers/net/ethernet/davicom/dm9000.c13
-rw-r--r--drivers/net/ethernet/davicom/dm9051.c16
-rw-r--r--drivers/net/ethernet/dec/tulip/21142.c4
-rw-r--r--drivers/net/ethernet/dec/tulip/de2104x.c12
-rw-r--r--drivers/net/ethernet/dec/tulip/dmfe.c4
-rw-r--r--drivers/net/ethernet/dec/tulip/eeprom.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/interrupt.c6
-rw-r--r--drivers/net/ethernet/dec/tulip/pnic.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/pnic2.c8
-rw-r--r--drivers/net/ethernet/dec/tulip/timer.c4
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip.h4
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip_core.c19
-rw-r--r--drivers/net/ethernet/dec/tulip/uli526x.c4
-rw-r--r--drivers/net/ethernet/dec/tulip/winbond-840.c8
-rw-r--r--drivers/net/ethernet/dec/tulip/xircom_cb.c4
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c128
-rw-r--r--drivers/net/ethernet/dlink/dl2k.h24
-rw-r--r--drivers/net/ethernet/dlink/sundance.c43
-rw-r--r--drivers/net/ethernet/dnet.c8
-rw-r--r--drivers/net/ethernet/ec_bhf.c6
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h4
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c232
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h5
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c96
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c103
-rw-r--r--drivers/net/ethernet/engleder/Kconfig1
-rw-r--r--drivers/net/ethernet/engleder/Makefile2
-rw-r--r--drivers/net/ethernet/engleder/tsnep.h94
-rw-r--r--drivers/net/ethernet/engleder/tsnep_ethtool.c201
-rw-r--r--drivers/net/ethernet/engleder/tsnep_hw.h22
-rw-r--r--drivers/net/ethernet/engleder/tsnep_main.c1960
-rw-r--r--drivers/net/ethernet/engleder/tsnep_ptp.c88
-rw-r--r--drivers/net/ethernet/engleder/tsnep_rxnfc.c307
-rw-r--r--drivers/net/ethernet/engleder/tsnep_selftests.c12
-rw-r--r--drivers/net/ethernet/engleder/tsnep_tc.c25
-rw-r--r--drivers/net/ethernet/engleder/tsnep_xdp.c85
-rw-r--r--drivers/net/ethernet/ethoc.c6
-rw-r--r--drivers/net/ethernet/ezchip/nps_enet.c12
-rw-r--r--drivers/net/ethernet/faraday/Kconfig1
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c131
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.h2
-rw-r--r--drivers/net/ethernet/faraday/ftmac100.c114
-rw-r--r--drivers/net/ethernet/fealnx.c12
-rw-r--r--drivers/net/ethernet/freescale/Kconfig6
-rw-r--r--drivers/net/ethernet/freescale/dpaa/Kconfig4
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_eth.c305
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_eth.h21
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c4
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_eth_trace.h4
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c237
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/Makefile2
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c57
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c22
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-trace.h144
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c747
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h115
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c190
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c72
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h12
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c5
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c52
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-flower.c39
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c237
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h9
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-xsk.c452
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h19
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpni.c6
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpni.h9
-rw-r--r--drivers/net/ethernet/freescale/enetc/Kconfig70
-rw-r--r--drivers/net/ethernet/freescale/enetc/Makefile20
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.c1916
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.h231
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c90
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_debugfs.h20
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_hw.h232
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_pf.c1102
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_cbdr.c58
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_ethtool.c760
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_hw.h248
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_ierb.c8
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_mdio.c119
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c37
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.c707
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.h35
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf_common.c439
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf_common.h22
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_ptp.c5
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_qos.c222
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_vf.c36
-rw-r--r--drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c845
-rw-r--r--drivers/net/ethernet/freescale/enetc/ntmp.c457
-rw-r--r--drivers/net/ethernet/freescale/enetc/ntmp_private.h104
-rw-r--r--drivers/net/ethernet/freescale/fec.h119
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c2056
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.c12
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx_phy.c9
-rw-r--r--drivers/net/ethernet/freescale/fec_ptp.c422
-rw-r--r--drivers/net/ethernet/freescale/fman/Kconfig3
-rw-r--r--drivers/net/ethernet/freescale/fman/fman.c37
-rw-r--r--drivers/net/ethernet/freescale/fman/fman.h3
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.c458
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_mac.h10
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.c824
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_muram.c1
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_port.c9
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.c131
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.c302
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.h51
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/Kconfig2
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c467
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet.h41
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fcc.c23
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fec.c30
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-scc.c30
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c15
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c22
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c24
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c109
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c61
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c637
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.h24
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth_ethtool.c95
-rw-r--r--drivers/net/ethernet/freescale/xgmac_mdio.c153
-rw-r--r--drivers/net/ethernet/fujitsu/Kconfig2
-rw-r--r--drivers/net/ethernet/fungible/funcore/fun_dev.c24
-rw-r--r--drivers/net/ethernet/fungible/funcore/fun_queue.c65
-rw-r--r--drivers/net/ethernet/fungible/funcore/fun_queue.h1
-rw-r--r--drivers/net/ethernet/fungible/funeth/Kconfig2
-rw-r--r--drivers/net/ethernet/fungible/funeth/Makefile2
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth.h4
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_devlink.c7
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_ethtool.c58
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_main.c61
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_rx.c5
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_trace.h6
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_tx.c3
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_txrx.h5
-rw-r--r--drivers/net/ethernet/google/Kconfig2
-rw-r--r--drivers/net/ethernet/google/gve/Makefile5
-rw-r--r--drivers/net/ethernet/google/gve/gve.h633
-rw-r--r--drivers/net/ethernet/google/gve/gve_adminq.c954
-rw-r--r--drivers/net/ethernet/google/gve/gve_adminq.h317
-rw-r--r--drivers/net/ethernet/google/gve/gve_buffer_mgmt_dqo.c344
-rw-r--r--drivers/net/ethernet/google/gve/gve_desc.h4
-rw-r--r--drivers/net/ethernet/google/gve/gve_desc_dqo.h11
-rw-r--r--drivers/net/ethernet/google/gve/gve_dqo.h31
-rw-r--r--drivers/net/ethernet/google/gve/gve_ethtool.c538
-rw-r--r--drivers/net/ethernet/google/gve/gve_flow_rule.c298
-rw-r--r--drivers/net/ethernet/google/gve/gve_main.c1957
-rw-r--r--drivers/net/ethernet/google/gve/gve_ptp.c166
-rw-r--r--drivers/net/ethernet/google/gve/gve_register.h9
-rw-r--r--drivers/net/ethernet/google/gve/gve_rx.c955
-rw-r--r--drivers/net/ethernet/google/gve/gve_rx_dqo.c887
-rw-r--r--drivers/net/ethernet/google/gve/gve_tx.c427
-rw-r--r--drivers/net/ethernet/google/gve/gve_tx_dqo.c903
-rw-r--r--drivers/net/ethernet/google/gve/gve_utils.c75
-rw-r--r--drivers/net/ethernet/google/gve/gve_utils.h11
-rw-r--r--drivers/net/ethernet/hisilicon/Kconfig20
-rw-r--r--drivers/net/ethernet/hisilicon/Makefile1
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/Makefile10
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h294
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c168
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.h12
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_diagnose.c349
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_diagnose.h11
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c194
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_err.h14
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c500
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.h16
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c399
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h63
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c145
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.h11
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c528
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c304
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h14
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h302
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_trace.h84
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c717
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.h44
-rw-r--r--drivers/net/ethernet/hisilicon/hip04_eth.c15
-rw-r--r--drivers/net/ethernet/hisilicon/hisi_femac.c10
-rw-r--r--drivers/net/ethernet/hisilicon/hix5hd2_gmac.c22
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hnae.c20
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hnae.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c20
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c5
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c45
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h5
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c183
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h30
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c87
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c34
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h4
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c75
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h5
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c5
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.c62
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.h3
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_ethtool.c104
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/Makefile15
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hnae3.c20
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hnae3.h82
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c81
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h39
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_rss.c43
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_rss.h9
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_tqp_stats.c18
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_tqp_stats.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c1073
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h15
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c447
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.h17
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c290
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_trace.h4
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h21
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c73
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c2013
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h644
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c48
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c549
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h38
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c1662
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h78
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c92
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c16
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c92
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.h13
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_regs.c669
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_regs.h17
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c56
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h11
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_trace.h106
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c329
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h5
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c9
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_regs.c166
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_regs.h13
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_trace.h62
-rw-r--r--drivers/net/ethernet/hisilicon/hns_mdio.c202
-rw-r--r--drivers/net/ethernet/huawei/Kconfig1
-rw-r--r--drivers/net/ethernet/huawei/Makefile1
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_debugfs.c18
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_dev.h4
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_devlink.c227
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_ethtool.c120
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c2
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c2
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h170
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c2
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c2
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_main.c26
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_port.c47
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_port.h12
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_rx.c4
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_sriov.c19
-rw-r--r--drivers/net/ethernet/huawei/hinic/hinic_tx.c12
-rw-r--r--drivers/net/ethernet/huawei/hinic3/Kconfig20
-rw-r--r--drivers/net/ethernet/huawei/hinic3/Makefile25
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c915
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h156
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_common.c76
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_common.h54
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_csr.h79
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_eqs.c776
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_eqs.h122
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c236
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h57
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c426
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h47
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h264
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c557
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h81
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c436
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h90
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_irq.c194
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_lld.c421
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_lld.h21
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_main.c409
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c860
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h141
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.c21
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mgmt.h15
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h224
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c496
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c385
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h61
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h93
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c885
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h145
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_pci_id_tbl.h9
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_queue_common.c68
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_queue_common.h54
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rss.c336
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rss.h14
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rx.c551
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rx.h104
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_tx.c779
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_tx.h147
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_wq.c138
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_wq.h87
-rw-r--r--drivers/net/ethernet/i825xx/82596.c5
-rw-r--r--drivers/net/ethernet/i825xx/lasi_82596.c5
-rw-r--r--drivers/net/ethernet/i825xx/lib82596.c5
-rw-r--r--drivers/net/ethernet/i825xx/sni_82596.c19
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.c4
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.h1
-rw-r--r--drivers/net/ethernet/ibm/Kconfig13
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c20
-rw-r--r--drivers/net/ethernet/ibm/emac/core.c275
-rw-r--r--drivers/net/ethernet/ibm/emac/core.h11
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.c114
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.h2
-rw-r--r--drivers/net/ethernet/ibm/emac/rgmii.c55
-rw-r--r--drivers/net/ethernet/ibm/emac/tah.c53
-rw-r--r--drivers/net/ethernet/ibm/emac/zmii.c53
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c1004
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.h105
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.c759
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.h26
-rw-r--r--drivers/net/ethernet/intel/Kconfig64
-rw-r--r--drivers/net/ethernet/intel/Makefile5
-rw-r--r--drivers/net/ethernet/intel/e100.c28
-rw-r--r--drivers/net/ethernet/intel/e1000/Makefile2
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000.h3
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_ethtool.c2
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.c50
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.h3
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c54
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.c23
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.c3
-rw-r--r--drivers/net/ethernet/intel/e1000e/Makefile10
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h11
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h29
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000e_trace.h42
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c238
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h18
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c215
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.h4
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.c35
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.h2
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c443
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.c10
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.c207
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.h2
-rw-r--r--drivers/net/ethernet/intel/e1000e/ptp.c52
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k.h3
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_common.c5
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_common.h2
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c87
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_main.c13
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_netdev.c8
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pci.c28
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.c129
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.h2
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.c12
-rw-r--r--drivers/net/ethernet/intel/i40e/Makefile5
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h536
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.c430
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.h27
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h167
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_alloc.h44
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_client.c56
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c2831
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.c390
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.h31
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c68
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ddp.c58
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debug.h48
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c318
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_devlink.c286
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_devlink.h18
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_diag.c29
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_diag.h12
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c745
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_hmc.c91
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_hmc.h50
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_io.h16
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c157
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h36
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c3181
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_nvm.c1224
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_osdep.h60
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h720
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ptp.c109
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_register.h19
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_status.h78
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_trace.h75
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c788
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.h80
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx_common.h2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_type.h252
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c930
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h27
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_xsk.c180
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_xsk.h16
-rw-r--r--drivers/net/ethernet/intel/iavf/Makefile7
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf.h202
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adminq.c148
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adminq.h19
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adminq_cmd.h83
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adv_rss.c127
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adv_rss.h34
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_alloc.h3
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_client.c578
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_client.h169
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_common.c486
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_ethtool.c535
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_fdir.c180
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_fdir.h30
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_main.c1894
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_osdep.h9
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_prototype.h20
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_ptp.c492
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_ptp.h47
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_register.h2
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_status.h2
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_trace.h12
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_txrx.c1013
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_txrx.h215
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_type.h375
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_types.h34
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_virtchnl.c838
-rw-r--r--drivers/net/ethernet/intel/ice/Makefile30
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/devlink.c2109
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/devlink.h26
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/health.c551
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/health.h71
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/port.c1001
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/port.h58
-rw-r--r--drivers/net/ethernet/intel/ice/ice.h414
-rw-r--r--drivers/net/ethernet/intel/ice/ice_adapter.c147
-rw-r--r--drivers/net/ethernet/intel/ice/ice_adapter.h53
-rw-r--r--drivers/net/ethernet/intel/ice/ice_adminq_cmd.h1052
-rw-r--r--drivers/net/ethernet/intel/ice/ice_arfs.c85
-rw-r--r--drivers/net/ethernet/intel/ice/ice_arfs.h2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_base.c854
-rw-r--r--drivers/net/ethernet/intel/ice/ice_base.h17
-rw-r--r--drivers/net/ethernet/intel/ice/ice_cgu_regs.h116
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.c2923
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.h190
-rw-r--r--drivers/net/ethernet/intel/ice/ice_controlq.c263
-rw-r--r--drivers/net/ethernet/intel/ice/ice_controlq.h30
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb.c160
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb.h2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb_lib.c160
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb_lib.h13
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb_nl.c56
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ddp.c2547
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ddp.h475
-rw-r--r--drivers/net/ethernet/intel/ice/ice_debugfs.c47
-rw-r--r--drivers/net/ethernet/intel/ice/ice_devids.h53
-rw-r--r--drivers/net/ethernet/intel/ice/ice_devlink.c1366
-rw-r--r--drivers/net/ethernet/intel/ice/ice_devlink.h21
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dpll.c3830
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dpll.h145
-rw-r--r--drivers/net/ethernet/intel/ice/ice_eswitch.c783
-rw-r--r--drivers/net/ethernet/intel/ice/ice_eswitch.h66
-rw-r--r--drivers/net/ethernet/intel/ice/ice_eswitch_br.c1355
-rw-r--r--drivers/net/ethernet/intel/ice/ice_eswitch_br.h122
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool.c1797
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool.h158
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool_fdir.c267
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fdir.c182
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fdir.h12
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flex_pipe.c2236
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flex_pipe.h84
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flex_type.h340
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flow.c929
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flow.h249
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fltr.c5
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fw_update.c94
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fw_update.h3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_gnss.c485
-rw-r--r--drivers/net/ethernet/intel/ice/ice_gnss.h35
-rw-r--r--drivers/net/ethernet/intel/ice/ice_hw_autogen.h107
-rw-r--r--drivers/net/ethernet/intel/ice/ice_hwmon.c126
-rw-r--r--drivers/net/ethernet/intel/ice/ice_hwmon.h15
-rw-r--r--drivers/net/ethernet/intel/ice/ice_idc.c272
-rw-r--r--drivers/net/ethernet/intel/ice/ice_idc_int.h5
-rw-r--r--drivers/net/ethernet/intel/ice/ice_irq.c275
-rw-r--r--drivers/net/ethernet/intel/ice/ice_irq.h36
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lag.c2565
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lag.h116
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h422
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lib.c2111
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lib.h61
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c3339
-rw-r--r--drivers/net/ethernet/intel/ice/ice_nvm.c177
-rw-r--r--drivers/net/ethernet/intel/ice/ice_nvm.h3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_osdep.h30
-rw-r--r--drivers/net/ethernet/intel/ice/ice_parser.c2430
-rw-r--r--drivers/net/ethernet/intel/ice/ice_parser.h538
-rw-r--r--drivers/net/ethernet/intel/ice/ice_parser_rt.c859
-rw-r--r--drivers/net/ethernet/intel/ice/ice_protocol_type.h273
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.c3142
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.h276
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp_consts.h384
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp_hw.c4553
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp_hw.h502
-rw-r--r--drivers/net/ethernet/intel/ice/ice_repr.c430
-rw-r--r--drivers/net/ethernet/intel/ice/ice_repr.h50
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sbq_cmd.h10
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sched.c468
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sched.h65
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sf_eth.c329
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sf_eth.h33
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sf_vsi_vlan_ops.c21
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sf_vsi_vlan_ops.h13
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sriov.c716
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sriov.h56
-rw-r--r--drivers/net/ethernet/intel/ice/ice_switch.c1667
-rw-r--r--drivers/net/ethernet/intel/ice/ice_switch.h76
-rw-r--r--drivers/net/ethernet/intel/ice/ice_tc_lib.c992
-rw-r--r--drivers/net/ethernet/intel/ice/ice_tc_lib.h75
-rw-r--r--drivers/net/ethernet/intel/ice/ice_trace.h130
-rw-r--r--drivers/net/ethernet/intel/ice/ice_tspll.c626
-rw-r--r--drivers/net/ethernet/intel/ice/ice_tspll.h31
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.c1146
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.h251
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx_lib.c551
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx_lib.h69
-rw-r--r--drivers/net/ethernet/intel/ice/ice_type.h207
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_lib.c782
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_lib.h141
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_lib_private.h7
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_mbx.c358
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_mbx.h26
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c177
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.h4
-rw-r--r--drivers/net/ethernet/intel/ice/ice_virtchnl.c3826
-rw-r--r--drivers/net/ethernet/intel/ice/ice_virtchnl.h82
-rw-r--r--drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.c181
-rw-r--r--drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c1939
-rw-r--r--drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.h56
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vlan_mode.c8
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.c211
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vsi_vlan_lib.h9
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.c5
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vsi_vlan_ops.h1
-rw-r--r--drivers/net/ethernet/intel/ice/ice_xsk.c729
-rw-r--r--drivers/net/ethernet/intel/ice/ice_xsk.h53
-rw-r--r--drivers/net/ethernet/intel/ice/virt/allowlist.c199
-rw-r--r--drivers/net/ethernet/intel/ice/virt/allowlist.h (renamed from drivers/net/ethernet/intel/ice/ice_virtchnl_allowlist.h)0
-rw-r--r--drivers/net/ethernet/intel/ice/virt/fdir.c2434
-rw-r--r--drivers/net/ethernet/intel/ice/virt/fdir.h57
-rw-r--r--drivers/net/ethernet/intel/ice/virt/queues.c975
-rw-r--r--drivers/net/ethernet/intel/ice/virt/queues.h20
-rw-r--r--drivers/net/ethernet/intel/ice/virt/rss.c1922
-rw-r--r--drivers/net/ethernet/intel/ice/virt/rss.h18
-rw-r--r--drivers/net/ethernet/intel/ice/virt/virtchnl.c2936
-rw-r--r--drivers/net/ethernet/intel/ice/virt/virtchnl.h140
-rw-r--r--drivers/net/ethernet/intel/idpf/Kconfig27
-rw-r--r--drivers/net/ethernet/intel/idpf/Makefile26
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf.h1036
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_controlq.c623
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_controlq.h144
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_controlq_api.h177
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_controlq_setup.c171
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_dev.c215
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_devids.h10
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_ethtool.c1799
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_idc.c503
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h128
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_lan_txrx.h310
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_lan_vf_regs.h128
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_lib.c2623
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_main.c392
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_mem.h20
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_ptp.c1021
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_ptp.h379
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c1183
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_txrx.c4706
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_txrx.h1118
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_vf_dev.c200
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_virtchnl.c4370
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_virtchnl.h189
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c673
-rw-r--r--drivers/net/ethernet/intel/idpf/virtchnl2.h1813
-rw-r--r--drivers/net/ethernet/intel/idpf/virtchnl2_lan_desc.h451
-rw-r--r--drivers/net/ethernet/intel/idpf/xdp.c486
-rw-r--r--drivers/net/ethernet/intel/idpf/xdp.h175
-rw-r--r--drivers/net/ethernet/intel/idpf/xsk.c633
-rw-r--r--drivers/net/ethernet/intel/idpf/xsk.h33
-rw-r--r--drivers/net/ethernet/intel/igb/Makefile6
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c33
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.c21
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.c12
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.c22
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c17
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h78
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c182
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c817
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c128
-rw-r--r--drivers/net/ethernet/intel/igb/igb_xsk.c562
-rw-r--r--drivers/net/ethernet/intel/igbvf/Makefile6
-rw-r--r--drivers/net/ethernet/intel/igbvf/ethtool.c6
-rw-r--r--drivers/net/ethernet/intel/igbvf/igbvf.h35
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.c1
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.h1
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c106
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.c13
-rw-r--r--drivers/net/ethernet/intel/igc/Makefile5
-rw-r--r--drivers/net/ethernet/intel/igc/igc.h267
-rw-r--r--drivers/net/ethernet/intel/igc/igc_base.c41
-rw-r--r--drivers/net/ethernet/intel/igc/igc_base.h29
-rw-r--r--drivers/net/ethernet/intel/igc/igc_defines.h122
-rw-r--r--drivers/net/ethernet/intel/igc/igc_diag.c3
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ethtool.c425
-rw-r--r--drivers/net/ethernet/intel/igc/igc_hw.h7
-rw-r--r--drivers/net/ethernet/intel/igc/igc_i225.c27
-rw-r--r--drivers/net/ethernet/intel/igc/igc_leds.c302
-rw-r--r--drivers/net/ethernet/intel/igc/igc_mac.c318
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c1566
-rw-r--r--drivers/net/ethernet/intel/igc/igc_nvm.c54
-rw-r--r--drivers/net/ethernet/intel/igc/igc_nvm.h2
-rw-r--r--drivers/net/ethernet/intel/igc/igc_phy.c39
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ptp.c555
-rw-r--r--drivers/net/ethernet/intel/igc/igc_regs.h48
-rw-r--r--drivers/net/ethernet/intel/igc/igc_tsn.c509
-rw-r--r--drivers/net/ethernet/intel/igc/igc_tsn.h59
-rw-r--r--drivers/net/ethernet/intel/igc/igc_xdp.c26
-rw-r--r--drivers/net/ethernet/intel/ixgb/Makefile9
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb.h179
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.c580
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.h79
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c642
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.c1229
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.h767
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ids.h23
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_main.c2285
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_osdep.h39
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_param.c442
-rw-r--r--drivers/net/ethernet/intel/ixgbe/Makefile12
-rw-r--r--drivers/net/ethernet/intel/ixgbe/devlink/devlink.c558
-rw-r--r--drivers/net/ethernet/intel/ixgbe/devlink/devlink.h12
-rw-r--r--drivers/net/ethernet/intel/ixgbe/devlink/region.c290
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h92
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c114
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c222
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c488
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h115
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c12
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h10
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c26
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h30
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c12
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h35
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c61
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c4043
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h102
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c507
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c22
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fw_update.c707
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fw_update.h12
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c114
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c17
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c1348
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c84
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h44
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c581
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h56
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c113
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c205
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_txrx_common.h7
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h358
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h1032
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c136
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h25
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c699
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x550.h25
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c30
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/Makefile6
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/defines.h6
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c59
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ipsec.c75
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h38
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c126
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.c12
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.h8
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.c196
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.h5
-rw-r--r--drivers/net/ethernet/intel/libeth/Kconfig15
-rw-r--r--drivers/net/ethernet/intel/libeth/Makefile12
-rw-r--r--drivers/net/ethernet/intel/libeth/priv.h37
-rw-r--r--drivers/net/ethernet/intel/libeth/rx.c273
-rw-r--r--drivers/net/ethernet/intel/libeth/tx.c41
-rw-r--r--drivers/net/ethernet/intel/libeth/xdp.c451
-rw-r--r--drivers/net/ethernet/intel/libeth/xsk.c271
-rw-r--r--drivers/net/ethernet/intel/libie/Kconfig25
-rw-r--r--drivers/net/ethernet/intel/libie/Makefile14
-rw-r--r--drivers/net/ethernet/intel/libie/adminq.c52
-rw-r--r--drivers/net/ethernet/intel/libie/fwlog.c1115
-rw-r--r--drivers/net/ethernet/intel/libie/rx.c126
-rw-r--r--drivers/net/ethernet/jme.c12
-rw-r--r--drivers/net/ethernet/korina.c14
-rw-r--r--drivers/net/ethernet/lantiq_etop.c42
-rw-r--r--drivers/net/ethernet/lantiq_xrx200.c8
-rw-r--r--drivers/net/ethernet/litex/litex_liteeth.c24
-rw-r--r--drivers/net/ethernet/marvell/Kconfig1
-rw-r--r--drivers/net/ethernet/marvell/Makefile1
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c127
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c146
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c458
-rw-r--r--drivers/net/ethernet/marvell/mvneta_bm.c4
-rw-r--r--drivers/net/ethernet/marvell/mvneta_bm.h2
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2.h13
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c56
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.h8
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c18
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c592
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c287
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/Makefile3
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c266
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c925
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_config.h64
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_cp_version.h11
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c282
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.h96
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.c494
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.h339
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_ethtool.c96
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_main.c758
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_main.h103
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_pfvf_mbox.c472
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_pfvf_mbox.h169
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h23
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h416
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_rx.c105
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_rx.h41
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_tx.c20
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep/octep_tx.h121
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/Kconfig19
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/Makefile10
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c489
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c500
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_config.h160
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_ethtool.c269
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c1220
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h338
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.c431
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_mbox.h166
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cn9k.h154
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_regs_cnxk.h162
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c511
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.h224
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.c331
-rw-r--r--drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_tx.h276
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/Kconfig10
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/Makefile4
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx.c368
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx.h24
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h33
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/api.h32
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c218
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.h28
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/mbox_init.c424
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/nix.c20
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/npa.c21
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/reg.h81
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/struct.h380
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/common.h13
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h26
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mbox.c172
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mbox.h873
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs.c1617
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs.h246
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs_cnf10kb.c277
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs_reg.h1129
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c932
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/npc.h45
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h617
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/ptp.c261
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/ptp.h3
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rpm.c387
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rpm.h48
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.c464
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.h366
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c299
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c66
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c441
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c1201
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c758
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c1793
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c87
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c464
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c432
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.h4
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c271
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.h22
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.c4
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h27
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c469
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c31
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h86
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_switch.c50
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_trace.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_trace.h100
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/Makefile11
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c62
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h5
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c1042
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h265
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c1815
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c450
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn20k.h17
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c678
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h434
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c51
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c125
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c9
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c426
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c123
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c1164
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c212
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h35
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h57
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c870
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c541
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h29
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c222
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_xsk.c245
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_xsk.h24
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/qos.c1774
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/qos.h78
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/qos_sq.c319
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/rep.c879
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/rep.h55
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera.h12
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_acl.c8
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_acl.h4
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_counter.c3
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_devlink.c22
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_devlink.h5
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_flower.c36
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_hw.c213
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_hw.h11
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_main.c55
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_matchall.c2
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_pci.c122
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_router.c1119
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_router_hw.c366
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_router_hw.h76
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_rxtx.c25
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_span.c5
-rw-r--r--drivers/net/ethernet/marvell/pxa168_eth.c33
-rw-r--r--drivers/net/ethernet/marvell/skge.c16
-rw-r--r--drivers/net/ethernet/marvell/sky2.c31
-rw-r--r--drivers/net/ethernet/marvell/sky2.h2
-rw-r--r--drivers/net/ethernet/mediatek/Kconfig5
-rw-r--r--drivers/net/ethernet/mediatek/Makefile9
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_path.c95
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c2555
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.h663
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe.c286
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe.h60
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c56
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe_offload.c128
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe_regs.h22
-rw-r--r--drivers/net/ethernet/mediatek/mtk_sgmii.c159
-rw-r--r--drivers/net/ethernet/mediatek/mtk_star_emac.c40
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed.c2267
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed.h87
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed_debugfs.c475
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed_mcu.c407
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed_regs.h507
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed_wo.c491
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed_wo.h283
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/Kconfig2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/alloc.c28
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/catas.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cq.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_clock.c50
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_cq.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c131
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c155
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c230
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c211
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c69
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw_qos.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/intf.c363
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c301
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mcg.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h24
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h53
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mr.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c22
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/qp.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Kconfig42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Makefile95
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c706
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cq.c35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/debugfs.c68
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/dev.c217
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.c611
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.h39
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/cmd_tracepoint.h45
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/crdump.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/en_rep_tracepoint.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/en_tc_tracepoint.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c141
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer_tracepoint.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.c160
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.h16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/dpll.c552
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ecpf.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h445
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/channels.c118
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/channels.h11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/dcbnl.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c100
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/devlink.h16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/dim.h45
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/fs.h38
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/fs_ethtool.h14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/health.c189
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/health.h15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/htb.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/mapping.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/mod_hdr.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c48
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/params.c837
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/params.h98
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c376
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/port.c346
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/port.h21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c288
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.h7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c351
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h72
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/qos.c70
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rep/bond.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c100
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c314
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c583
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c466
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rqt.c158
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rqt.h19
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rss.c253
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rss.h53
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c416
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h45
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/selq.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/accept.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.c119
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h25
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c63
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/drop.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c34
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred_nic.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c81
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ptype.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/sample.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/tun.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c89
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act_stats.c199
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act_stats.h27
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs.h12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_dmfs.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_hmfs.c294
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c102
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/int_port.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c61
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_act.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c403
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.h39
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c709
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c50
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c222
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.h12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c39
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_gre.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c105
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tir.c34
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tir.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/trap.c36
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/trap.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h167
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c534
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h78
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/pool.c42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c267
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.h50
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c68
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c41
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.h12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h66
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c1236
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h303
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c3004
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c451
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c68
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c62
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c59
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c91
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_stats.c26
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c155
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c454
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c1384
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.h47
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_stats.c42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c1155
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h77
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c201
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h121
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c95
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_common.c200
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c95
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_dim.c95
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c936
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_fs.c87
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c69
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c2650
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.c622
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.h13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c1546
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_stats.c921
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_stats.h102
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.c2044
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.h61
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tx.c131
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c65
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eq.c601
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_ofld.c25
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_ofld.c44
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ofld.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c202
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c456
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.h32
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_debugfs.c89
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_mcast.c1134
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_priv.h188
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/debugfs.c182
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c231
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/diag/bridge_tracepoint.h35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/diag/qos_tracepoint.h94
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c213
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec.c369
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c186
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.h57
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/legacy.c46
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c2220
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/qos.h33
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/vporttbl.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c891
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h394
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c2337
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/events.c45
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c219
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.c1180
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.h145
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c683
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_ft_pool.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_ft_pool.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_pool.c195
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_pool.h55
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw.c90
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c502
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/health.c346
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/hwmon.c423
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/hwmon.h25
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c76
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c151
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c43
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c131
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c25
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c681
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h48
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c164
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c220
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c81
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c39
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c958
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h38
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c755
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.h35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c501
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h79
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/dm.c42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/events.h40
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c113
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/fs_ttc.c652
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/fs_ttc.h22
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/ipsec_fs_roce.c875
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/ipsec_fs_roce.h31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.c2409
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/macsec_fs.h79
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h69
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c124
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c799
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.h14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c532
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h38
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/smfs.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/st.c185
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/tout.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/tout.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c696
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h278
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_irq.h47
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mr.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c99
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c423
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/port.c263
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/qos.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/qos.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/rdma.c35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/rdma.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/rl.c62
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c188
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/diag/dev_tracepoint.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c57
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c349
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h14
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/diag/vhca_tracepoint.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c110
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c120
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sriov.c57
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.c657
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/dr_dbg.h15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c520
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.h94
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v2.c231
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/Makefile2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/action.c2651
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/action.h316
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/action_ste_pool.c467
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/action_ste_pool.h69
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/buddy.c149
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/buddy.h21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c1226
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.h117
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc_complex.c1101
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc_complex.h88
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/cmd.c1219
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/cmd.h347
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/context.c257
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/context.h73
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/debug.c487
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/debug.h42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/definer.c2329
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/definer.h831
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c1641
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h120
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.c427
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.h73
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/internal.h59
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/matcher.c1293
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/matcher.h114
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/mlx5hws.h941
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.c582
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pat_arg.h102
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pool.c394
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/pool.h132
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/prm.h472
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/rule.c846
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/rule.h87
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.c1358
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.h265
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c494
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.h74
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/vport.c86
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/vport.h13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_action.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c)458
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_arg.c273
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_buddy.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_buddy.c)2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_cmd.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c)193
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_dbg.c1186
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_dbg.h35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_definer.c151
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_domain.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c)206
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_fw.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_fw.c)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c576
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_matcher.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c)0
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ptrn.c244
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_rule.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c)130
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_send.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c)474
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c)88
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste.h (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h)26
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste_v0.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c)18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste_v1.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c)458
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste_v1.h240
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste_v2.c81
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste_v2.h168
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_ste_v3.c263
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_table.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c)7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_types.h (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h)148
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/fs_dr.c (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c)189
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/fs_dr.h (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.h)11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/mlx5_ifc_dr.h (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr.h)40
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/mlx5_ifc_dr_ste_v1.h (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr_ste_v1.h)37
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/mlx5dr.h (renamed from drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h)26
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/uar.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/vport.c190
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/wc.c464
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/wq.h19
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h36
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_ethtool.c37
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c208
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c182
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio_bf2.h53
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio_bf3.h54
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_regs.h54
-rw-r--r--drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c78
-rw-r--r--drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_tlv_multi.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Kconfig1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Makefile2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/cmd.h54
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c413
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h40
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c56
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.c87
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_env.c98
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_env.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_linecards.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_thermal.c654
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/emad.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/i2c.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/item.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/minimal.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.c1065
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci_hw.h11
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/port.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h351
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/resources.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c1101
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h122
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum1_acl_tcam.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum2_mr_tcam.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c54
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_bloom_filter.c29
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.c101
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c352
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c28
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c32
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c106
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c904
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c165
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c242
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c27
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_port_range.c200
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c93
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h72
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c1959
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h34
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c333
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c39
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/trap.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/txheader.h63
-rw-r--r--drivers/net/ethernet/meta/Kconfig38
-rw-r--r--drivers/net/ethernet/meta/Makefile6
-rw-r--r--drivers/net/ethernet/meta/fbnic/Makefile28
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic.h227
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_csr.c149
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_csr.h1201
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c271
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_devlink.c668
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_drvinfo.h5
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c1924
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw.c1920
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw.h326
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c123
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw_log.h45
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c601
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h163
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_hwmon.c81
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_irq.c313
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mac.c934
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mac.h122
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mdio.c195
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.c857
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.h117
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_pci.c655
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_phylink.c311
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_rpc.c1246
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_rpc.h232
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_time.c303
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_tlv.c560
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_tlv.h158
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.c2930
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.h201
-rw-r--r--drivers/net/ethernet/micrel/ks8842.c5
-rw-r--r--drivers/net/ethernet/micrel/ks8851.h6
-rw-r--r--drivers/net/ethernet/micrel/ks8851_common.c84
-rw-r--r--drivers/net/ethernet/micrel/ks8851_par.c15
-rw-r--r--drivers/net/ethernet/micrel/ks8851_spi.c49
-rw-r--r--drivers/net/ethernet/micrel/ksz884x.c316
-rw-r--r--drivers/net/ethernet/microchip/Kconfig9
-rw-r--r--drivers/net/ethernet/microchip/Makefile3
-rw-r--r--drivers/net/ethernet/microchip/enc28j60.c28
-rw-r--r--drivers/net/ethernet/microchip/encx24j600-regmap.c19
-rw-r--r--drivers/net/ethernet/microchip/encx24j600.c7
-rw-r--r--drivers/net/ethernet/microchip/encx24j600_hw.h2
-rw-r--r--drivers/net/ethernet/microchip/fdma/Kconfig18
-rw-r--r--drivers/net/ethernet/microchip/fdma/Makefile7
-rw-r--r--drivers/net/ethernet/microchip/fdma/fdma_api.c146
-rw-r--r--drivers/net/ethernet/microchip/fdma/fdma_api.h243
-rw-r--r--drivers/net/ethernet/microchip/lan743x_ethtool.c361
-rw-r--r--drivers/net/ethernet/microchip/lan743x_ethtool.h71
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.c994
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.h51
-rw-r--r--drivers/net/ethernet/microchip/lan743x_ptp.c165
-rw-r--r--drivers/net/ethernet/microchip/lan743x_ptp.h12
-rw-r--r--drivers/net/ethernet/microchip/lan865x/Kconfig19
-rw-r--r--drivers/net/ethernet/microchip/lan865x/Makefile6
-rw-r--r--drivers/net/ethernet/microchip/lan865x/lan865x.c455
-rw-r--r--drivers/net/ethernet/microchip/lan966x/Kconfig15
-rw-r--r--drivers/net/ethernet/microchip/lan966x/Makefile12
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_dcb.c365
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c52
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c720
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_goto.c50
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h3
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_lag.c9
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.c221
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.h339
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c138
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_mqprio.c2
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c12
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_police.c226
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_port.c162
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c342
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_regs.h490
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c1
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_tc.c63
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c626
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c91
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c3270
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h11
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c244
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c784
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c29
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c136
-rw-r--r--drivers/net/ethernet/microchip/sparx5/Kconfig22
-rw-r--r--drivers/net/ethernet/microchip/sparx5/Makefile22
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c357
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.h82
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c191
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c406
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c222
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c224
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_vcap_ag_api.c3843
-rw-r--r--drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_vcap_impl.c85
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c126
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c408
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c71
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c460
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c14
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.c399
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.h449
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h5394
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c240
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c88
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_packet.c94
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_pgid.c15
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c25
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_police.c54
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_pool.c81
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_port.c388
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_port.h111
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c337
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c85
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_qos.c74
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_qos.h2
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_regs.c222
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_regs.h247
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_sdlb.c338
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c47
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_tc.c67
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_tc.h93
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c1581
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c204
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c3874
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h20
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c471
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h33
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c2101
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h228
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c49
-rw-r--r--drivers/net/ethernet/microchip/vcap/Kconfig53
-rw-r--r--drivers/net/ethernet/microchip/vcap/Makefile10
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_ag_api.h906
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.c3611
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.h280
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_client.h284
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c468
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h41
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c554
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c2352
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_private.h124
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c4062
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h18
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_tc.c412
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_tc.h32
-rw-r--r--drivers/net/ethernet/microsoft/Kconfig6
-rw-r--r--drivers/net/ethernet/microsoft/mana/gdma.h685
-rw-r--r--drivers/net/ethernet/microsoft/mana/gdma_main.c997
-rw-r--r--drivers/net/ethernet/microsoft/mana/hw_channel.c166
-rw-r--r--drivers/net/ethernet/microsoft/mana/hw_channel.h195
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana.h634
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_bpf.c70
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_en.c1996
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_ethtool.c390
-rw-r--r--drivers/net/ethernet/microsoft/mana/shm_channel.c15
-rw-r--r--drivers/net/ethernet/microsoft/mana/shm_channel.h21
-rw-r--r--drivers/net/ethernet/moxa/moxart_ether.c6
-rw-r--r--drivers/net/ethernet/mscc/Kconfig1
-rw-r--r--drivers/net/ethernet/mscc/Makefile1
-rw-r--r--drivers/net/ethernet/mscc/ocelot.c524
-rw-r--r--drivers/net/ethernet/mscc/ocelot.h19
-rw-r--r--drivers/net/ethernet/mscc/ocelot_devlink.c31
-rw-r--r--drivers/net/ethernet/mscc/ocelot_fdma.c7
-rw-r--r--drivers/net/ethernet/mscc/ocelot_flower.c119
-rw-r--r--drivers/net/ethernet/mscc/ocelot_io.c50
-rw-r--r--drivers/net/ethernet/mscc/ocelot_mm.c300
-rw-r--r--drivers/net/ethernet/mscc/ocelot_net.c115
-rw-r--r--drivers/net/ethernet/mscc/ocelot_ptp.c324
-rw-r--r--drivers/net/ethernet/mscc/ocelot_stats.c632
-rw-r--r--drivers/net/ethernet/mscc/ocelot_vcap.c1
-rw-r--r--drivers/net/ethernet/mscc/ocelot_vcap.h1
-rw-r--r--drivers/net/ethernet/mscc/ocelot_vsc7514.c247
-rw-r--r--drivers/net/ethernet/mscc/vsc7514_regs.c177
-rw-r--r--drivers/net/ethernet/mucse/Kconfig33
-rw-r--r--drivers/net/ethernet/mucse/Makefile7
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/Makefile11
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h71
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c143
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h17
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c320
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c406
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h20
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c191
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h88
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c21
-rw-r--r--drivers/net/ethernet/natsemi/jazzsonic.c6
-rw-r--r--drivers/net/ethernet/natsemi/macsonic.c4
-rw-r--r--drivers/net/ethernet/natsemi/natsemi.c10
-rw-r--r--drivers/net/ethernet/natsemi/ns83820.c19
-rw-r--r--drivers/net/ethernet/natsemi/sonic.c4
-rw-r--r--drivers/net/ethernet/natsemi/xtsonic.c4
-rw-r--r--drivers/net/ethernet/neterion/s2io.c65
-rw-r--r--drivers/net/ethernet/netronome/Kconfig13
-rw-r--r--drivers/net/ethernet/netronome/nfp/Makefile4
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/cmsg.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/jit.c4
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/offload.c3
-rw-r--r--drivers/net/ethernet/netronome/nfp/ccm_mbox.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/crypto/crypto.h23
-rw-r--r--drivers/net/ethernet/netronome/nfp/crypto/ipsec.c640
-rw-r--r--drivers/net/ethernet/netronome/nfp/crypto/tls.c9
-rw-r--r--drivers/net/ethernet/netronome/nfp/devlink_param.c14
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/action.c29
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/cmsg.c10
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/conntrack.c390
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/conntrack.h32
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/lag_conf.c70
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.c9
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.h23
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/metadata.c6
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/offload.c104
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/qos_conf.c20
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c188
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfd3/dp.c95
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c39
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfd3/nfd3.h8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfd3/xsk.c6
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfdk/dp.c94
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c21
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfdk/nfdk.h8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_app.h2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_devlink.c48
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_hwmon.c42
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_main.c66
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_main.h5
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net.h76
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c450
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h64
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c43
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c15
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c856
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_main.c68
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_repr.c10
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_repr.h2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_port.c1
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_port.h14
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c21
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h75
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c129
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nic/dcb.c571
-rw-r--r--drivers/net/ethernet/netronome/nfp/nic/main.c43
-rw-r--r--drivers/net/ethernet/netronome/nfp/nic/main.h46
-rw-r--r--drivers/net/ethernet/ni/nixge.c185
-rw-r--r--drivers/net/ethernet/nvidia/forcedeth.c59
-rw-r--r--drivers/net/ethernet/nxp/lpc_eth.c4
-rw-r--r--drivers/net/ethernet/oa_tc6.c1369
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/Kconfig2
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c52
-rw-r--r--drivers/net/ethernet/packetengines/hamachi.c6
-rw-r--r--drivers/net/ethernet/packetengines/yellowfin.c6
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.c14
-rw-r--r--drivers/net/ethernet/pensando/Kconfig2
-rw-r--r--drivers/net/ethernet/pensando/ionic/Makefile2
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic.h16
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_api.h131
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_aux.c102
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_aux.h10
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c230
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_debugfs.c9
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_dev.c599
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_dev.h162
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_devlink.c8
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_ethtool.c319
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_fw.c5
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_if.h436
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.c1130
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.h103
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_main.c221
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_phc.c70
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c8
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h1
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_stats.c22
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_txrx.c1315
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_txrx.h8
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic.h2
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c7
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c14
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c2
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c2
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c29
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed.h4
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_cxt.c1
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_dbg_hsi.h31
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_debug.c71
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_dev.c26
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_dev_api.h16
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_devlink.c31
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_fcoe.c19
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_fcoe.h17
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_hsi.h52
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_hw.c38
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_hw.h9
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c138
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_iscsi.c19
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_iscsi.h8
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_l2.c21
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_l2.h24
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_ll2.c12
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_ll2.h2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_main.c32
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.c74
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c10
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_ooo.c9
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_ptp.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_rdma.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_sriov.c15
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_vf.c45
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede.h9
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ethtool.c227
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_filter.c159
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_fp.c14
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_main.c88
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ptp.c102
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ptp.h8
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.c9
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic.h1
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c18
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h10
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c22
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c8
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h10
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c62
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c7
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c21
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c17
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c36
-rw-r--r--drivers/net/ethernet/qualcomm/Kconfig15
-rw-r--r--drivers/net/ethernet/qualcomm/Makefile1
-rw-r--r--drivers/net/ethernet/qualcomm/emac/emac-mac.c7
-rw-r--r--drivers/net/ethernet/qualcomm/emac/emac-sgmii.c26
-rw-r--r--drivers/net/ethernet/qualcomm/emac/emac.c16
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/Makefile7
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe.c239
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe.h39
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe_config.c2034
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe_config.h317
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c847
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.h16
-rw-r--r--drivers/net/ethernet/qualcomm/ppe/ppe_regs.h591
-rw-r--r--drivers/net/ethernet/qualcomm/qca_7k.c17
-rw-r--r--drivers/net/ethernet/qualcomm/qca_7k.h16
-rw-r--r--drivers/net/ethernet/qualcomm/qca_7k_common.c17
-rw-r--r--drivers/net/ethernet/qualcomm/qca_7k_common.h29
-rw-r--r--drivers/net/ethernet/qualcomm/qca_debug.c46
-rw-r--r--drivers/net/ethernet/qualcomm/qca_debug.h15
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c155
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.h26
-rw-r--r--drivers/net/ethernet/qualcomm/qca_uart.c25
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c17
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h20
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c18
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h6
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c191
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c64
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h1
-rw-r--r--drivers/net/ethernet/rdc/r6040.c5
-rw-r--r--drivers/net/ethernet/realtek/8139cp.c4
-rw-r--r--drivers/net/ethernet/realtek/8139too.c12
-rw-r--r--drivers/net/ethernet/realtek/Kconfig29
-rw-r--r--drivers/net/ethernet/realtek/Makefile4
-rw-r--r--drivers/net/ethernet/realtek/atp.c4
-rw-r--r--drivers/net/ethernet/realtek/r8169.h22
-rw-r--r--drivers/net/ethernet/realtek/r8169_firmware.c9
-rw-r--r--drivers/net/ethernet/realtek/r8169_leds.c275
-rw-r--r--drivers/net/ethernet/realtek/r8169_main.c1801
-rw-r--r--drivers/net/ethernet/realtek/r8169_phy_config.c272
-rw-r--r--drivers/net/ethernet/realtek/rtase/Makefile10
-rw-r--r--drivers/net/ethernet/realtek/rtase/rtase.h362
-rw-r--r--drivers/net/ethernet/realtek/rtase/rtase_main.c2400
-rw-r--r--drivers/net/ethernet/renesas/Kconfig39
-rw-r--r--drivers/net/ethernet/renesas/Makefile10
-rw-r--r--drivers/net/ethernet/renesas/ravb.h96
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c1991
-rw-r--r--drivers/net/ethernet/renesas/ravb_ptp.c29
-rw-r--r--drivers/net/ethernet/renesas/rcar_gen4_ptp.c187
-rw-r--r--drivers/net/ethernet/renesas/rcar_gen4_ptp.h25
-rw-r--r--drivers/net/ethernet/renesas/rswitch.h1074
-rw-r--r--drivers/net/ethernet/renesas/rswitch_l2.c316
-rw-r--r--drivers/net/ethernet/renesas/rswitch_l2.h15
-rw-r--r--drivers/net/ethernet/renesas/rswitch_main.c2295
-rw-r--r--drivers/net/ethernet/renesas/rtsn.c1371
-rw-r--r--drivers/net/ethernet/renesas/rtsn.h464
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c97
-rw-r--r--drivers/net/ethernet/rocker/rocker_main.c17
-rw-r--r--drivers/net/ethernet/rocker/rocker_ofdpa.c21
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h3
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c51
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c22
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c105
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c9
-rw-r--r--drivers/net/ethernet/seeq/ether3.c8
-rw-r--r--drivers/net/ethernet/seeq/sgiseeq.c6
-rw-r--r--drivers/net/ethernet/sfc/Kconfig7
-rw-r--r--drivers/net/ethernet/sfc/Makefile6
-rw-r--r--drivers/net/ethernet/sfc/bitfield.h2
-rw-r--r--drivers/net/ethernet/sfc/ef10.c284
-rw-r--r--drivers/net/ethernet/sfc/ef100.c3
-rw-r--r--drivers/net/ethernet/sfc/ef100_ethtool.c10
-rw-r--r--drivers/net/ethernet/sfc/ef100_netdev.c125
-rw-r--r--drivers/net/ethernet/sfc/ef100_nic.c172
-rw-r--r--drivers/net/ethernet/sfc/ef100_nic.h7
-rw-r--r--drivers/net/ethernet/sfc/ef100_rep.c61
-rw-r--r--drivers/net/ethernet/sfc/ef100_rep.h10
-rw-r--r--drivers/net/ethernet/sfc/ef100_rx.c28
-rw-r--r--drivers/net/ethernet/sfc/ef100_tx.c30
-rw-r--r--drivers/net/ethernet/sfc/ef100_tx.h2
-rw-r--r--drivers/net/ethernet/sfc/ef10_sriov.h2
-rw-r--r--drivers/net/ethernet/sfc/efx.c213
-rw-r--r--drivers/net/ethernet/sfc/efx.h5
-rw-r--r--drivers/net/ethernet/sfc/efx_channels.c56
-rw-r--r--drivers/net/ethernet/sfc/efx_channels.h7
-rw-r--r--drivers/net/ethernet/sfc/efx_common.c45
-rw-r--r--drivers/net/ethernet/sfc/efx_common.h1
-rw-r--r--drivers/net/ethernet/sfc/efx_devlink.c746
-rw-r--r--drivers/net/ethernet/sfc/efx_devlink.h47
-rw-r--r--drivers/net/ethernet/sfc/efx_reflash.c522
-rw-r--r--drivers/net/ethernet/sfc/efx_reflash.h20
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c20
-rw-r--r--drivers/net/ethernet/sfc/ethtool_common.c365
-rw-r--r--drivers/net/ethernet/sfc/ethtool_common.h29
-rw-r--r--drivers/net/ethernet/sfc/falcon/efx.c29
-rw-r--r--drivers/net/ethernet/sfc/falcon/efx.h1
-rw-r--r--drivers/net/ethernet/sfc/falcon/ethtool.c115
-rw-r--r--drivers/net/ethernet/sfc/falcon/falcon.c10
-rw-r--r--drivers/net/ethernet/sfc/falcon/farch.c22
-rw-r--r--drivers/net/ethernet/sfc/falcon/net_driver.h2
-rw-r--r--drivers/net/ethernet/sfc/falcon/nic.c20
-rw-r--r--drivers/net/ethernet/sfc/falcon/nic.h9
-rw-r--r--drivers/net/ethernet/sfc/falcon/rx.c5
-rw-r--r--drivers/net/ethernet/sfc/falcon/selftest.c58
-rw-r--r--drivers/net/ethernet/sfc/falcon/tx.c8
-rw-r--r--drivers/net/ethernet/sfc/falcon/tx.h3
-rw-r--r--drivers/net/ethernet/sfc/farch_regs.h2929
-rw-r--r--drivers/net/ethernet/sfc/filter.h11
-rw-r--r--drivers/net/ethernet/sfc/fw_formats.h114
-rw-r--r--drivers/net/ethernet/sfc/io.h110
-rw-r--r--drivers/net/ethernet/sfc/mae.c1877
-rw-r--r--drivers/net/ethernet/sfc/mae.h84
-rw-r--r--drivers/net/ethernet/sfc/mae_counter_format.h73
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c273
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h76
-rw-r--r--drivers/net/ethernet/sfc/mcdi_filters.c135
-rw-r--r--drivers/net/ethernet/sfc/mcdi_filters.h8
-rw-r--r--drivers/net/ethernet/sfc/mcdi_functions.c24
-rw-r--r--drivers/net/ethernet/sfc/mcdi_pcol.h13844
-rw-r--r--drivers/net/ethernet/sfc/mcdi_port.c59
-rw-r--r--drivers/net/ethernet/sfc/mcdi_port_common.c27
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h187
-rw-r--r--drivers/net/ethernet/sfc/nic.c167
-rw-r--r--drivers/net/ethernet/sfc/nic.h178
-rw-r--r--drivers/net/ethernet/sfc/nic_common.h16
-rw-r--r--drivers/net/ethernet/sfc/ptp.c560
-rw-r--r--drivers/net/ethernet/sfc/ptp.h15
-rw-r--r--drivers/net/ethernet/sfc/rx.c25
-rw-r--r--drivers/net/ethernet/sfc/rx_common.c101
-rw-r--r--drivers/net/ethernet/sfc/rx_common.h8
-rw-r--r--drivers/net/ethernet/sfc/selftest.c65
-rw-r--r--drivers/net/ethernet/sfc/siena/efx.c33
-rw-r--r--drivers/net/ethernet/sfc/siena/efx_channels.c11
-rw-r--r--drivers/net/ethernet/sfc/siena/efx_common.c16
-rw-r--r--drivers/net/ethernet/sfc/siena/ethtool.c13
-rw-r--r--drivers/net/ethernet/sfc/siena/ethtool_common.c258
-rw-r--r--drivers/net/ethernet/sfc/siena/ethtool_common.h15
-rw-r--r--drivers/net/ethernet/sfc/siena/farch.c2
-rw-r--r--drivers/net/ethernet/sfc/siena/io.h2
-rw-r--r--drivers/net/ethernet/sfc/siena/mcdi.c6
-rw-r--r--drivers/net/ethernet/sfc/siena/mcdi_pcol.h12
-rw-r--r--drivers/net/ethernet/sfc/siena/net_driver.h34
-rw-r--r--drivers/net/ethernet/sfc/siena/nic.c14
-rw-r--r--drivers/net/ethernet/sfc/siena/nic_common.h5
-rw-r--r--drivers/net/ethernet/sfc/siena/ptp.c43
-rw-r--r--drivers/net/ethernet/sfc/siena/ptp.h13
-rw-r--r--drivers/net/ethernet/sfc/siena/rx_common.c73
-rw-r--r--drivers/net/ethernet/sfc/siena/rx_common.h4
-rw-r--r--drivers/net/ethernet/sfc/siena/selftest.c58
-rw-r--r--drivers/net/ethernet/sfc/siena/siena.c4
-rw-r--r--drivers/net/ethernet/sfc/siena/tx_common.c6
-rw-r--r--drivers/net/ethernet/sfc/tc.c2560
-rw-r--r--drivers/net/ethernet/sfc/tc.h274
-rw-r--r--drivers/net/ethernet/sfc/tc_bindings.c13
-rw-r--r--drivers/net/ethernet/sfc/tc_bindings.h14
-rw-r--r--drivers/net/ethernet/sfc/tc_conntrack.c625
-rw-r--r--drivers/net/ethernet/sfc/tc_conntrack.h55
-rw-r--r--drivers/net/ethernet/sfc/tc_counters.c563
-rw-r--r--drivers/net/ethernet/sfc/tc_counters.h66
-rw-r--r--drivers/net/ethernet/sfc/tc_encap_actions.c753
-rw-r--r--drivers/net/ethernet/sfc/tc_encap_actions.h114
-rw-r--r--drivers/net/ethernet/sfc/tx.c63
-rw-r--r--drivers/net/ethernet/sfc/tx.h3
-rw-r--r--drivers/net/ethernet/sfc/tx_common.c43
-rw-r--r--drivers/net/ethernet/sfc/tx_common.h6
-rw-r--r--drivers/net/ethernet/sfc/tx_tso.c8
-rw-r--r--drivers/net/ethernet/sfc/vfdi.h252
-rw-r--r--drivers/net/ethernet/sfc/workarounds.h7
-rw-r--r--drivers/net/ethernet/sgi/ioc3-eth.c14
-rw-r--r--drivers/net/ethernet/sgi/meth.c6
-rw-r--r--drivers/net/ethernet/sis/Kconfig4
-rw-r--r--drivers/net/ethernet/sis/sis190.c6
-rw-r--r--drivers/net/ethernet/sis/sis900.c13
-rw-r--r--drivers/net/ethernet/smsc/Kconfig16
-rw-r--r--drivers/net/ethernet/smsc/Makefile1
-rw-r--r--drivers/net/ethernet/smsc/epic100.c4
-rw-r--r--drivers/net/ethernet/smsc/smc911x.c2198
-rw-r--r--drivers/net/ethernet/smsc/smc911x.h901
-rw-r--r--drivers/net/ethernet/smsc/smc9194.c1
-rw-r--r--drivers/net/ethernet/smsc/smc91c92_cs.c6
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c13
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h27
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c38
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.c10
-rw-r--r--drivers/net/ethernet/socionext/netsec.c35
-rw-r--r--drivers/net/ethernet/socionext/sni_ave.c27
-rw-r--r--drivers/net/ethernet/spacemit/Kconfig29
-rw-r--r--drivers/net/ethernet/spacemit/Makefile6
-rw-r--r--drivers/net/ethernet/spacemit/k1_emac.c2162
-rw-r--r--drivers/net/ethernet/spacemit/k1_emac.h416
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig139
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile18
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c257
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h29
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/chain_mode.c19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h198
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c62
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c266
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c235
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c28
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c279
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c228
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c134
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c487
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c68
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c649
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c240
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c59
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c144
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c34
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c101
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c245
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c770
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c235
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c1863
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c93
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c186
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c398
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c93
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c151
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c241
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c489
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.c159
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c166
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c87
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c403
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c286
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c208
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h32
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c249
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c91
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h175
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c679
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c43
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c252
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h107
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c129
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.c227
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.h65
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h63
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c66
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h121
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c438
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c28
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c177
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/enh_desc.c19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.c290
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h361
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc.h21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc_core.c139
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/norm_desc.c15
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h124
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_est.c179
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_est.h67
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c532
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c314
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.h28
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c112
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.c48
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.h12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c2848
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c696
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c123
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c67
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h76
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c486
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h15
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c183
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h23
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c371
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c375
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h64
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.h1
-rw-r--r--drivers/net/ethernet/sun/cassini.c67
-rw-r--r--drivers/net/ethernet/sun/ldmvsw.c12
-rw-r--r--drivers/net/ethernet/sun/niu.c105
-rw-r--r--drivers/net/ethernet/sun/niu.h8
-rw-r--r--drivers/net/ethernet/sun/sunbmac.c10
-rw-r--r--drivers/net/ethernet/sun/sungem.c27
-rw-r--r--drivers/net/ethernet/sun/sunhme.c1149
-rw-r--r--drivers/net/ethernet/sun/sunhme.h6
-rw-r--r--drivers/net/ethernet/sun/sunqe.c6
-rw-r--r--drivers/net/ethernet/sun/sunqe.h2
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c43
-rw-r--r--drivers/net/ethernet/sun/sunvnet_common.c19
-rw-r--r--drivers/net/ethernet/sunplus/spl2sw_driver.c9
-rw-r--r--drivers/net/ethernet/sunplus/spl2sw_mdio.c6
-rw-r--r--drivers/net/ethernet/sunplus/spl2sw_phy.c4
-rw-r--r--drivers/net/ethernet/synopsys/dwc-xlgmac-common.c7
-rw-r--r--drivers/net/ethernet/synopsys/dwc-xlgmac-net.c7
-rw-r--r--drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c5
-rw-r--r--drivers/net/ethernet/tehuti/Kconfig15
-rw-r--r--drivers/net/ethernet/tehuti/Makefile3
-rw-r--r--drivers/net/ethernet/tehuti/tehuti.c8
-rw-r--r--drivers/net/ethernet/tehuti/tehuti.h2
-rw-r--r--drivers/net/ethernet/tehuti/tn40.c1857
-rw-r--r--drivers/net/ethernet/tehuti/tn40.h266
-rw-r--r--drivers/net/ethernet/tehuti/tn40_mdio.c220
-rw-r--r--drivers/net/ethernet/tehuti/tn40_phy.c76
-rw-r--r--drivers/net/ethernet/tehuti/tn40_regs.h245
-rw-r--r--drivers/net/ethernet/ti/Kconfig82
-rw-r--r--drivers/net/ethernet/ti/Makefile25
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-ethtool.c291
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-nuss.c2046
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-nuss.h85
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-qos.c786
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-qos.h190
-rw-r--r--drivers/net/ethernet/ti/am65-cpts.c442
-rw-r--r--drivers/net/ethernet/ti/am65-cpts.h26
-rw-r--r--drivers/net/ethernet/ti/cpmac.c1251
-rw-r--r--drivers/net/ethernet/ti/cpsw-common.c2
-rw-r--r--drivers/net/ethernet/ti/cpsw-phy-sel.c4
-rw-r--r--drivers/net/ethernet/ti/cpsw.c62
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.c373
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.h64
-rw-r--r--drivers/net/ethernet/ti/cpsw_ethtool.c25
-rw-r--r--drivers/net/ethernet/ti/cpsw_new.c56
-rw-r--r--drivers/net/ethernet/ti/cpsw_priv.c84
-rw-r--r--drivers/net/ethernet/ti/cpsw_priv.h12
-rw-r--r--drivers/net/ethernet/ti/cpts.c39
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c38
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c88
-rw-r--r--drivers/net/ethernet/ti/icssg/icss_iep.c1107
-rw-r--r--drivers/net/ethernet/ti/icssg/icss_iep.h112
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_classifier.c469
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_common.c1853
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_config.c864
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_config.h335
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_ethtool.c321
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_mii_cfg.c124
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_mii_rt.h151
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.c2364
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.h526
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c1250
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_queues.c52
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_stats.c95
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_stats.h204
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_switch_map.h270
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_switchdev.c477
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_switchdev.h13
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth.c1746
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth.h262
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth_ptp.h85
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_switch.h257
-rw-r--r--drivers/net/ethernet/ti/k3-cppi-desc-pool.c56
-rw-r--r--drivers/net/ethernet/ti/k3-cppi-desc-pool.h6
-rw-r--r--drivers/net/ethernet/ti/netcp.h7
-rw-r--r--drivers/net/ethernet/ti/netcp_core.c85
-rw-r--r--drivers/net/ethernet/ti/netcp_ethss.c99
-rw-r--r--drivers/net/ethernet/ti/tlan.c8
-rw-r--r--drivers/net/ethernet/toshiba/Kconfig11
-rw-r--r--drivers/net/ethernet/toshiba/Makefile2
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.c262
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.h36
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_wireless.c3
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_wireless.h1
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.c2555
-rw-r--r--drivers/net/ethernet/toshiba/spider_net.h475
-rw-r--r--drivers/net/ethernet/toshiba/spider_net_ethtool.c174
-rw-r--r--drivers/net/ethernet/toshiba/tc35815.c13
-rw-r--r--drivers/net/ethernet/tundra/tsi108_eth.c13
-rw-r--r--drivers/net/ethernet/vertexcom/mse102x.c141
-rw-r--r--drivers/net/ethernet/via/Kconfig1
-rw-r--r--drivers/net/ethernet/via/via-rhine.c25
-rw-r--r--drivers/net/ethernet/via/via-velocity.c19
-rw-r--r--drivers/net/ethernet/via/via-velocity.h2
-rw-r--r--drivers/net/ethernet/wangxun/Kconfig62
-rw-r--r--drivers/net/ethernet/wangxun/Makefile3
-rw-r--r--drivers/net/ethernet/wangxun/libwx/Makefile8
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ethtool.c762
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ethtool.h60
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_hw.c2977
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_hw.h63
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_lib.c3358
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_lib.h45
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_mbx.c419
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_mbx.h99
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ptp.c905
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ptp.h20
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_sriov.c929
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_sriov.h18
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_type.h1507
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf.c599
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf.h129
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf_common.c414
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf_common.h22
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c292
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf_lib.h15
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/Makefile2
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe.h24
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c155
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.h9
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c99
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h13
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_main.c757
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c203
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.h11
-rw-r--r--drivers/net/ethernet/wangxun/ngbe/ngbe_type.h106
-rw-r--r--drivers/net/ethernet/wangxun/ngbevf/Makefile9
-rw-r--r--drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c266
-rw-r--r--drivers/net/ethernet/wangxun/ngbevf/ngbevf_type.h29
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/Makefile8
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe.h24
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c530
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h18
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c606
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.h11
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.c648
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.h20
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c227
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h12
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c268
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_irq.h7
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_main.c870
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c676
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h11
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_type.h453
-rw-r--r--drivers/net/ethernet/wangxun/txgbevf/Makefile9
-rw-r--r--drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c331
-rw-r--r--drivers/net/ethernet/wangxun/txgbevf/txgbevf_type.h26
-rw-r--r--drivers/net/ethernet/wiznet/w5100-spi.c12
-rw-r--r--drivers/net/ethernet/wiznet/w5100.c10
-rw-r--r--drivers/net/ethernet/wiznet/w5300.c6
-rw-r--r--drivers/net/ethernet/xilinx/Kconfig2
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac.h1
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c34
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_mdio.c2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet.h213
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_main.c1536
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c102
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c40
-rw-r--r--drivers/net/ethernet/xircom/Kconfig2
-rw-r--r--drivers/net/ethernet/xircom/xirc2ps_cs.c15
-rw-r--r--drivers/net/ethernet/xscale/ixp4xx_eth.c142
-rw-r--r--drivers/net/ethernet/xscale/ptp_ixp46x.c19
-rw-r--r--drivers/net/fddi/defxx.c24
-rw-r--r--drivers/net/fddi/defza.c12
-rw-r--r--drivers/net/fddi/skfp/rmt.c6
-rw-r--r--drivers/net/fddi/skfp/skfddi.c1
-rw-r--r--drivers/net/fjes/fjes_ethtool.c64
-rw-r--r--drivers/net/fjes/fjes_hw.c37
-rw-r--r--drivers/net/fjes/fjes_main.c22
-rw-r--r--drivers/net/fjes/fjes_trace.h12
-rw-r--r--drivers/net/geneve.c456
-rw-r--r--drivers/net/gtp.c998
-rw-r--r--drivers/net/hamradio/6pack.c131
-rw-r--r--drivers/net/hamradio/Kconfig23
-rw-r--r--drivers/net/hamradio/baycom_epp.c18
-rw-r--r--drivers/net/hamradio/baycom_par.c5
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c3
-rw-r--r--drivers/net/hamradio/baycom_ser_hdx.c4
-rw-r--r--drivers/net/hamradio/bpqether.c31
-rw-r--r--drivers/net/hamradio/hdlcdrv.c2
-rw-r--r--drivers/net/hamradio/mkiss.c4
-rw-r--r--drivers/net/hamradio/scc.c50
-rw-r--r--drivers/net/hamradio/yam.c4
-rw-r--r--drivers/net/hippi/rrunner.c5
-rw-r--r--drivers/net/hyperv/Kconfig3
-rw-r--r--drivers/net/hyperv/hyperv_net.h33
-rw-r--r--drivers/net/hyperv/netvsc.c177
-rw-r--r--drivers/net/hyperv/netvsc_bpf.c2
-rw-r--r--drivers/net/hyperv/netvsc_drv.c441
-rw-r--r--drivers/net/hyperv/netvsc_trace.h8
-rw-r--r--drivers/net/hyperv/rndis_filter.c91
-rw-r--r--drivers/net/ieee802154/Kconfig1
-rw-r--r--drivers/net/ieee802154/adf7242.c10
-rw-r--r--drivers/net/ieee802154/at86rf230.c101
-rw-r--r--drivers/net/ieee802154/atusb.c33
-rw-r--r--drivers/net/ieee802154/ca8210.c131
-rw-r--r--drivers/net/ieee802154/cc2520.c140
-rw-r--r--drivers/net/ieee802154/fakelb.c4
-rw-r--r--drivers/net/ieee802154/mac802154_hwsim.c189
-rw-r--r--drivers/net/ieee802154/mcr20a.c21
-rw-r--r--drivers/net/ieee802154/mrf24j40.c4
-rw-r--r--drivers/net/ifb.c31
-rw-r--r--drivers/net/ipa/Kconfig2
-rw-r--r--drivers/net/ipa/Makefile17
-rw-r--r--drivers/net/ipa/data/ipa_data-v3.1.c25
-rw-r--r--drivers/net/ipa/data/ipa_data-v3.5.1.c37
-rw-r--r--drivers/net/ipa/data/ipa_data-v4.11.c23
-rw-r--r--drivers/net/ipa/data/ipa_data-v4.2.c23
-rw-r--r--drivers/net/ipa/data/ipa_data-v4.5.c23
-rw-r--r--drivers/net/ipa/data/ipa_data-v4.7.c405
-rw-r--r--drivers/net/ipa/data/ipa_data-v4.9.c23
-rw-r--r--drivers/net/ipa/data/ipa_data-v5.0.c481
-rw-r--r--drivers/net/ipa/data/ipa_data-v5.5.c487
-rw-r--r--drivers/net/ipa/gsi.c532
-rw-r--r--drivers/net/ipa/gsi.h21
-rw-r--r--drivers/net/ipa/gsi_private.h15
-rw-r--r--drivers/net/ipa/gsi_reg.c162
-rw-r--r--drivers/net/ipa/gsi_reg.h509
-rw-r--r--drivers/net/ipa/gsi_trans.c77
-rw-r--r--drivers/net/ipa/gsi_trans.h11
-rw-r--r--drivers/net/ipa/ipa.h56
-rw-r--r--drivers/net/ipa/ipa_cmd.c131
-rw-r--r--drivers/net/ipa/ipa_cmd.h34
-rw-r--r--drivers/net/ipa/ipa_data.h13
-rw-r--r--drivers/net/ipa/ipa_endpoint.c900
-rw-r--r--drivers/net/ipa/ipa_endpoint.h14
-rw-r--r--drivers/net/ipa/ipa_gsi.c7
-rw-r--r--drivers/net/ipa/ipa_interrupt.c293
-rw-r--r--drivers/net/ipa/ipa_interrupt.h98
-rw-r--r--drivers/net/ipa/ipa_main.c389
-rw-r--r--drivers/net/ipa/ipa_mem.c116
-rw-r--r--drivers/net/ipa/ipa_mem.h15
-rw-r--r--drivers/net/ipa/ipa_modem.c128
-rw-r--r--drivers/net/ipa/ipa_modem.h5
-rw-r--r--drivers/net/ipa/ipa_power.c169
-rw-r--r--drivers/net/ipa/ipa_power.h36
-rw-r--r--drivers/net/ipa/ipa_qmi.c29
-rw-r--r--drivers/net/ipa/ipa_qmi.h4
-rw-r--r--drivers/net/ipa/ipa_qmi_msg.c23
-rw-r--r--drivers/net/ipa/ipa_qmi_msg.h23
-rw-r--r--drivers/net/ipa/ipa_reg.c114
-rw-r--r--drivers/net/ipa/ipa_reg.h367
-rw-r--r--drivers/net/ipa/ipa_resource.c19
-rw-r--r--drivers/net/ipa/ipa_smp2p.c43
-rw-r--r--drivers/net/ipa/ipa_smp2p.h9
-rw-r--r--drivers/net/ipa/ipa_sysfs.c21
-rw-r--r--drivers/net/ipa/ipa_sysfs.h4
-rw-r--r--drivers/net/ipa/ipa_table.c463
-rw-r--r--drivers/net/ipa/ipa_table.h37
-rw-r--r--drivers/net/ipa/ipa_uc.c48
-rw-r--r--drivers/net/ipa/ipa_uc.h9
-rw-r--r--drivers/net/ipa/ipa_version.h27
-rw-r--r--drivers/net/ipa/reg.h136
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v3.1.c293
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v3.5.1.c305
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v4.0.c310
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v4.11.c315
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v4.5.c313
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v4.9.c314
-rw-r--r--drivers/net/ipa/reg/gsi_reg-v5.0.c319
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v3.1.c390
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v3.5.1.c280
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v4.11.c282
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v4.2.c258
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v4.5.c298
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v4.7.c494
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v4.9.c282
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v5.0.c566
-rw-r--r--drivers/net/ipa/reg/ipa_reg-v5.5.c565
-rw-r--r--drivers/net/ipvlan/ipvlan.h4
-rw-r--r--drivers/net/ipvlan/ipvlan_core.c99
-rw-r--r--drivers/net/ipvlan/ipvlan_l3s.c12
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c53
-rw-r--r--drivers/net/ipvlan/ipvtap.c12
-rw-r--r--drivers/net/loopback.c36
-rw-r--r--drivers/net/macsec.c774
-rw-r--r--drivers/net/macvlan.c226
-rw-r--r--drivers/net/macvtap.c12
-rw-r--r--drivers/net/mctp/Kconfig24
-rw-r--r--drivers/net/mctp/Makefile2
-rw-r--r--drivers/net/mctp/mctp-i2c.c112
-rw-r--r--drivers/net/mctp/mctp-i3c.c767
-rw-r--r--drivers/net/mctp/mctp-serial.c152
-rw-r--r--drivers/net/mctp/mctp-usb.c390
-rw-r--r--drivers/net/mdio.c172
-rw-r--r--drivers/net/mdio/Kconfig71
-rw-r--r--drivers/net/mdio/Makefile4
-rw-r--r--drivers/net/mdio/acpi_mdio.c11
-rw-r--r--drivers/net/mdio/fwnode_mdio.c92
-rw-r--r--drivers/net/mdio/mdio-airoha.c278
-rw-r--r--drivers/net/mdio/mdio-aspeed.c53
-rw-r--r--drivers/net/mdio/mdio-bcm-iproc.c4
-rw-r--r--drivers/net/mdio/mdio-bcm-unimac.c133
-rw-r--r--drivers/net/mdio/mdio-bitbang.c78
-rw-r--r--drivers/net/mdio/mdio-cavium.c111
-rw-r--r--drivers/net/mdio/mdio-cavium.h9
-rw-r--r--drivers/net/mdio/mdio-gpio.c11
-rw-r--r--drivers/net/mdio/mdio-hisi-femac.c4
-rw-r--r--drivers/net/mdio/mdio-i2c.c417
-rw-r--r--drivers/net/mdio/mdio-ipq4019.c272
-rw-r--r--drivers/net/mdio/mdio-ipq8064.c13
-rw-r--r--drivers/net/mdio/mdio-moxart.c4
-rw-r--r--drivers/net/mdio/mdio-mscc-miim.c27
-rw-r--r--drivers/net/mdio/mdio-mux-bcm-iproc.c64
-rw-r--r--drivers/net/mdio/mdio-mux-bcm6368.c6
-rw-r--r--drivers/net/mdio/mdio-mux-gpio.c6
-rw-r--r--drivers/net/mdio/mdio-mux-meson-g12a.c65
-rw-r--r--drivers/net/mdio/mdio-mux-meson-gxl.c163
-rw-r--r--drivers/net/mdio/mdio-mux-mmioreg.c61
-rw-r--r--drivers/net/mdio/mdio-mux-multiplexer.c4
-rw-r--r--drivers/net/mdio/mdio-mux.c61
-rw-r--r--drivers/net/mdio/mdio-mvusb.c17
-rw-r--r--drivers/net/mdio/mdio-octeon.c34
-rw-r--r--drivers/net/mdio/mdio-realtek-rtl9300.c522
-rw-r--r--drivers/net/mdio/mdio-regmap.c93
-rw-r--r--drivers/net/mdio/mdio-sun4i.c4
-rw-r--r--drivers/net/mdio/mdio-thunder.c19
-rw-r--r--drivers/net/mdio/mdio-xgene.c32
-rw-r--r--drivers/net/mdio/of_mdio.c108
-rw-r--r--drivers/net/mhi_net.c11
-rw-r--r--drivers/net/mii.c3
-rw-r--r--drivers/net/net_failover.c14
-rw-r--r--drivers/net/netconsole.c1528
-rw-r--r--drivers/net/netdevsim/Makefile8
-rw-r--r--drivers/net/netdevsim/bpf.c11
-rw-r--r--drivers/net/netdevsim/bus.c203
-rw-r--r--drivers/net/netdevsim/dev.c241
-rw-r--r--drivers/net/netdevsim/ethtool.c76
-rw-r--r--drivers/net/netdevsim/fib.c5
-rw-r--r--drivers/net/netdevsim/health.c140
-rw-r--r--drivers/net/netdevsim/hwstats.c34
-rw-r--r--drivers/net/netdevsim/ipsec.c65
-rw-r--r--drivers/net/netdevsim/macsec.c351
-rw-r--r--drivers/net/netdevsim/netdev.c892
-rw-r--r--drivers/net/netdevsim/netdevsim.h108
-rw-r--r--drivers/net/netdevsim/psp.c252
-rw-r--r--drivers/net/netdevsim/udp_tunnels.c35
-rw-r--r--drivers/net/netkit.c1017
-rw-r--r--drivers/net/nlmon.c28
-rw-r--r--drivers/net/ntb_netdev.c25
-rw-r--r--drivers/net/ovpn/Makefile22
-rw-r--r--drivers/net/ovpn/bind.c55
-rw-r--r--drivers/net/ovpn/bind.h101
-rw-r--r--drivers/net/ovpn/crypto.c210
-rw-r--r--drivers/net/ovpn/crypto.h145
-rw-r--r--drivers/net/ovpn/crypto_aead.c389
-rw-r--r--drivers/net/ovpn/crypto_aead.h29
-rw-r--r--drivers/net/ovpn/io.c465
-rw-r--r--drivers/net/ovpn/io.h34
-rw-r--r--drivers/net/ovpn/main.c279
-rw-r--r--drivers/net/ovpn/main.h14
-rw-r--r--drivers/net/ovpn/netlink-gen.c263
-rw-r--r--drivers/net/ovpn/netlink-gen.h48
-rw-r--r--drivers/net/ovpn/netlink.c1293
-rw-r--r--drivers/net/ovpn/netlink.h18
-rw-r--r--drivers/net/ovpn/ovpnpriv.h55
-rw-r--r--drivers/net/ovpn/peer.c1364
-rw-r--r--drivers/net/ovpn/peer.h163
-rw-r--r--drivers/net/ovpn/pktid.c129
-rw-r--r--drivers/net/ovpn/pktid.h86
-rw-r--r--drivers/net/ovpn/proto.h118
-rw-r--r--drivers/net/ovpn/skb.h61
-rw-r--r--drivers/net/ovpn/socket.c241
-rw-r--r--drivers/net/ovpn/socket.h49
-rw-r--r--drivers/net/ovpn/stats.c21
-rw-r--r--drivers/net/ovpn/stats.h47
-rw-r--r--drivers/net/ovpn/tcp.c619
-rw-r--r--drivers/net/ovpn/tcp.h37
-rw-r--r--drivers/net/ovpn/udp.c448
-rw-r--r--drivers/net/ovpn/udp.h25
-rw-r--r--drivers/net/pcs/Kconfig28
-rw-r--r--drivers/net/pcs/Makefile5
-rw-r--r--drivers/net/pcs/pcs-altera-tse.c175
-rw-r--r--drivers/net/pcs/pcs-lynx.c249
-rw-r--r--drivers/net/pcs/pcs-mtk-lynxi.c329
-rw-r--r--drivers/net/pcs/pcs-rzn1-miic.c385
-rw-r--r--drivers/net/pcs/pcs-xpcs-nxp.c24
-rw-r--r--drivers/net/pcs/pcs-xpcs-plat.c457
-rw-r--r--drivers/net/pcs/pcs-xpcs-wx.c197
-rw-r--r--drivers/net/pcs/pcs-xpcs.c1352
-rw-r--r--drivers/net/pcs/pcs-xpcs.h81
-rw-r--r--drivers/net/pfcp.c298
-rw-r--r--drivers/net/phy/Kconfig164
-rw-r--r--drivers/net/phy/Makefile54
-rw-r--r--drivers/net/phy/adin.c61
-rw-r--r--drivers/net/phy/adin1100.c69
-rw-r--r--drivers/net/phy/air_en8811h.c1224
-rw-r--r--drivers/net/phy/amd.c35
-rw-r--r--drivers/net/phy/aquantia.h16
-rw-r--r--drivers/net/phy/aquantia/Kconfig6
-rw-r--r--drivers/net/phy/aquantia/Makefile6
-rw-r--r--drivers/net/phy/aquantia/aquantia.h259
-rw-r--r--drivers/net/phy/aquantia/aquantia_firmware.c385
-rw-r--r--drivers/net/phy/aquantia/aquantia_hwmon.c (renamed from drivers/net/phy/aquantia_hwmon.c)48
-rw-r--r--drivers/net/phy/aquantia/aquantia_leds.c160
-rw-r--r--drivers/net/phy/aquantia/aquantia_main.c1593
-rw-r--r--drivers/net/phy/aquantia_main.c842
-rw-r--r--drivers/net/phy/as21xxx.c1088
-rw-r--r--drivers/net/phy/at803x.c2218
-rw-r--r--drivers/net/phy/ax88796b.c9
-rw-r--r--drivers/net/phy/ax88796b_rust.rs134
-rw-r--r--drivers/net/phy/bcm-cygnus.c2
-rw-r--r--drivers/net/phy/bcm-phy-lib.c382
-rw-r--r--drivers/net/phy/bcm-phy-lib.h19
-rw-r--r--drivers/net/phy/bcm-phy-ptp.c45
-rw-r--r--drivers/net/phy/bcm54140.c21
-rw-r--r--drivers/net/phy/bcm63xx.c2
-rw-r--r--drivers/net/phy/bcm7xxx.c30
-rw-r--r--drivers/net/phy/bcm84881.c28
-rw-r--r--drivers/net/phy/bcm87xx.c15
-rw-r--r--drivers/net/phy/broadcom.c932
-rw-r--r--drivers/net/phy/cicada.c2
-rw-r--r--drivers/net/phy/cortina.c2
-rw-r--r--drivers/net/phy/davicom.c2
-rw-r--r--drivers/net/phy/dp83640.c139
-rw-r--r--drivers/net/phy/dp83822.c730
-rw-r--r--drivers/net/phy/dp83848.c4
-rw-r--r--drivers/net/phy/dp83867.c388
-rw-r--r--drivers/net/phy/dp83869.c54
-rw-r--r--drivers/net/phy/dp83tc811.c2
-rw-r--r--drivers/net/phy/dp83td510.c757
-rw-r--r--drivers/net/phy/dp83tg720.c678
-rw-r--r--drivers/net/phy/et1011c.c2
-rw-r--r--drivers/net/phy/fixed_phy.c282
-rw-r--r--drivers/net/phy/icplus.c11
-rw-r--r--drivers/net/phy/intel-xway.c262
-rw-r--r--drivers/net/phy/lxt.c2
-rw-r--r--drivers/net/phy/marvell-88q2xxx.c1154
-rw-r--r--drivers/net/phy/marvell-88x2222.c23
-rw-r--r--drivers/net/phy/marvell.c976
-rw-r--r--drivers/net/phy/marvell10g.c374
-rw-r--r--drivers/net/phy/mdio-boardinfo.c80
-rw-r--r--drivers/net/phy/mdio-boardinfo.h23
-rw-r--r--drivers/net/phy/mdio-open-alliance.h95
-rw-r--r--drivers/net/phy/mdio-private.h11
-rw-r--r--drivers/net/phy/mdio_bus.c747
-rw-r--r--drivers/net/phy/mdio_bus_provider.c442
-rw-r--r--drivers/net/phy/mdio_device.c74
-rw-r--r--drivers/net/phy/mdio_devres.c12
-rw-r--r--drivers/net/phy/mediatek-ge.c113
-rw-r--r--drivers/net/phy/mediatek/Kconfig40
-rw-r--r--drivers/net/phy/mediatek/Makefile5
-rw-r--r--drivers/net/phy/mediatek/mtk-2p5ge.c413
-rw-r--r--drivers/net/phy/mediatek/mtk-ge-soc.c1548
-rw-r--r--drivers/net/phy/mediatek/mtk-ge.c142
-rw-r--r--drivers/net/phy/mediatek/mtk-phy-lib.c347
-rw-r--r--drivers/net/phy/mediatek/mtk.h104
-rw-r--r--drivers/net/phy/meson-gxl.c87
-rw-r--r--drivers/net/phy/micrel.c3996
-rw-r--r--drivers/net/phy/microchip.c230
-rw-r--r--drivers/net/phy/microchip_rds_ptp.c1306
-rw-r--r--drivers/net/phy/microchip_rds_ptp.h247
-rw-r--r--drivers/net/phy/microchip_t1.c1342
-rw-r--r--drivers/net/phy/microchip_t1s.c610
-rw-r--r--drivers/net/phy/motorcomm.c2976
-rw-r--r--drivers/net/phy/mscc/mscc.h39
-rw-r--r--drivers/net/phy/mscc/mscc_macsec.c62
-rw-r--r--drivers/net/phy/mscc/mscc_macsec.h2
-rw-r--r--drivers/net/phy/mscc/mscc_main.c677
-rw-r--r--drivers/net/phy/mscc/mscc_ptp.c174
-rw-r--r--drivers/net/phy/mscc/mscc_ptp.h1
-rw-r--r--drivers/net/phy/mxl-86110.c978
-rw-r--r--drivers/net/phy/mxl-gpy.c660
-rw-r--r--drivers/net/phy/national.c2
-rw-r--r--drivers/net/phy/ncn26000.c171
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx-macsec.c1725
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.c1454
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.h63
-rw-r--r--drivers/net/phy/nxp-cbtx.c227
-rw-r--r--drivers/net/phy/nxp-tja11xx.c73
-rw-r--r--drivers/net/phy/open_alliance_helpers.c77
-rw-r--r--drivers/net/phy/open_alliance_helpers.h47
-rw-r--r--drivers/net/phy/phy-c45.c1079
-rw-r--r--drivers/net/phy/phy-caps.h64
-rw-r--r--drivers/net/phy/phy-core.c402
-rw-r--r--drivers/net/phy/phy.c1112
-rw-r--r--drivers/net/phy/phy_caps.c380
-rw-r--r--drivers/net/phy/phy_device.c1393
-rw-r--r--drivers/net/phy/phy_led_triggers.c25
-rw-r--r--drivers/net/phy/phy_link_topology.c105
-rw-r--r--drivers/net/phy/phy_package.c419
-rw-r--r--drivers/net/phy/phylib-internal.h31
-rw-r--r--drivers/net/phy/phylib.h34
-rw-r--r--drivers/net/phy/phylink.c2568
-rw-r--r--drivers/net/phy/qcom/Kconfig31
-rw-r--r--drivers/net/phy/qcom/Makefile6
-rw-r--r--drivers/net/phy/qcom/at803x.c1250
-rw-r--r--drivers/net/phy/qcom/qca807x.c868
-rw-r--r--drivers/net/phy/qcom/qca808x.c686
-rw-r--r--drivers/net/phy/qcom/qca83xx.c269
-rw-r--r--drivers/net/phy/qcom/qcom-phy-lib.c776
-rw-r--r--drivers/net/phy/qcom/qcom.h271
-rw-r--r--drivers/net/phy/qsemi.c2
-rw-r--r--drivers/net/phy/qt2025.rs111
-rw-r--r--drivers/net/phy/realtek.c1064
-rw-r--r--drivers/net/phy/realtek/Kconfig15
-rw-r--r--drivers/net/phy/realtek/Makefile4
-rw-r--r--drivers/net/phy/realtek/realtek.h10
-rw-r--r--drivers/net/phy/realtek/realtek_hwmon.c74
-rw-r--r--drivers/net/phy/realtek/realtek_main.c2277
-rw-r--r--drivers/net/phy/rockchip.c2
-rw-r--r--drivers/net/phy/sfp-bus.c252
-rw-r--r--drivers/net/phy/sfp.c999
-rw-r--r--drivers/net/phy/sfp.h5
-rw-r--r--drivers/net/phy/smsc.c510
-rw-r--r--drivers/net/phy/spi_ks8995.c506
-rw-r--r--drivers/net/phy/ste10Xp.c2
-rw-r--r--drivers/net/phy/stubs.c10
-rw-r--r--drivers/net/phy/teranetics.c5
-rw-r--r--drivers/net/phy/uPD60620.c2
-rw-r--r--drivers/net/phy/vitesse.c181
-rw-r--r--drivers/net/phy/xilinx_gmii2rgmii.c17
-rw-r--r--drivers/net/plip/plip.c8
-rw-r--r--drivers/net/ppp/Kconfig37
-rw-r--r--drivers/net/ppp/bsd_comp.c5
-rw-r--r--drivers/net/ppp/ppp_async.c37
-rw-r--r--drivers/net/ppp/ppp_deflate.c3
-rw-r--r--drivers/net/ppp/ppp_generic.c344
-rw-r--r--drivers/net/ppp/ppp_mppe.c110
-rw-r--r--drivers/net/ppp/ppp_synctty.c40
-rw-r--r--drivers/net/ppp/pppoe.c171
-rw-r--r--drivers/net/ppp/pptp.c64
-rw-r--r--drivers/net/pse-pd/Kconfig55
-rw-r--r--drivers/net/pse-pd/Makefile9
-rw-r--r--drivers/net/pse-pd/pd692x0.c1883
-rw-r--r--drivers/net/pse-pd/pse_core.c1978
-rw-r--r--drivers/net/pse-pd/pse_regulator.c155
-rw-r--r--drivers/net/pse-pd/si3474.c578
-rw-r--r--drivers/net/pse-pd/tps23881.c1563
-rw-r--r--drivers/net/rionet.c5
-rw-r--r--drivers/net/sb1000.c1179
-rw-r--r--drivers/net/slip/slhc.c60
-rw-r--r--drivers/net/slip/slip.c25
-rw-r--r--drivers/net/sungem_phy.c38
-rw-r--r--drivers/net/tap.c238
-rw-r--r--drivers/net/team/Makefile1
-rw-r--r--drivers/net/team/team.c3090
-rw-r--r--drivers/net/team/team_core.c3001
-rw-r--r--drivers/net/team/team_mode_activebackup.c11
-rw-r--r--drivers/net/team/team_mode_broadcast.c1
-rw-r--r--drivers/net/team/team_mode_loadbalance.c67
-rw-r--r--drivers/net/team/team_mode_random.c2
-rw-r--r--drivers/net/team/team_mode_roundrobin.c1
-rw-r--r--drivers/net/team/team_nl.c60
-rw-r--r--drivers/net/team/team_nl.h30
-rw-r--r--drivers/net/thunderbolt.c1415
-rw-r--r--drivers/net/thunderbolt/Kconfig12
-rw-r--r--drivers/net/thunderbolt/Makefile6
-rw-r--r--drivers/net/thunderbolt/main.c1470
-rw-r--r--drivers/net/thunderbolt/trace.c10
-rw-r--r--drivers/net/thunderbolt/trace.h141
-rw-r--r--drivers/net/tun.c547
-rw-r--r--drivers/net/tun_vnet.h269
-rw-r--r--drivers/net/usb/Kconfig19
-rw-r--r--drivers/net/usb/aqc111.c28
-rw-r--r--drivers/net/usb/asix.h1
-rw-r--r--drivers/net/usb/asix_common.c22
-rw-r--r--drivers/net/usb/asix_devices.c127
-rw-r--r--drivers/net/usb/ax88172a.c16
-rw-r--r--drivers/net/usb/ax88179_178a.c121
-rw-r--r--drivers/net/usb/catc.c4
-rw-r--r--drivers/net/usb/cdc_ether.c153
-rw-r--r--drivers/net/usb/cdc_mbim.c9
-rw-r--r--drivers/net/usb/cdc_ncm.c105
-rw-r--r--drivers/net/usb/ch9200.c7
-rw-r--r--drivers/net/usb/dm9601.c7
-rw-r--r--drivers/net/usb/gl620a.c4
-rw-r--r--drivers/net/usb/hso.c36
-rw-r--r--drivers/net/usb/ipheth.c227
-rw-r--r--drivers/net/usb/kalmia.c8
-rw-r--r--drivers/net/usb/lan78xx.c2159
-rw-r--r--drivers/net/usb/net1080.c2
-rw-r--r--drivers/net/usb/plusb.c10
-rw-r--r--drivers/net/usb/qmi_wwan.c57
-rw-r--r--drivers/net/usb/r8152.c973
-rw-r--r--drivers/net/usb/r8153_ecm.c6
-rw-r--r--drivers/net/usb/rndis_host.c3
-rw-r--r--drivers/net/usb/rtl8150.c38
-rw-r--r--drivers/net/usb/sierra_net.c12
-rw-r--r--drivers/net/usb/smsc75xx.c28
-rw-r--r--drivers/net/usb/smsc95xx.c161
-rw-r--r--drivers/net/usb/sr9700.c29
-rw-r--r--drivers/net/usb/sr9800.c8
-rw-r--r--drivers/net/usb/usbnet.c471
-rw-r--r--drivers/net/usb/zaurus.c21
-rw-r--r--drivers/net/veth.c505
-rw-r--r--drivers/net/virtio_net.c5353
-rw-r--r--drivers/net/vmxnet3/Makefile4
-rw-r--r--drivers/net/vmxnet3/vmxnet3_defs.h61
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c593
-rw-r--r--drivers/net/vmxnet3/vmxnet3_ethtool.c149
-rw-r--r--drivers/net/vmxnet3/vmxnet3_int.h74
-rw-r--r--drivers/net/vmxnet3/vmxnet3_xdp.c429
-rw-r--r--drivers/net/vmxnet3/vmxnet3_xdp.h47
-rw-r--r--drivers/net/vrf.c146
-rw-r--r--drivers/net/vsockmon.c25
-rw-r--r--drivers/net/vxlan/Makefile2
-rw-r--r--drivers/net/vxlan/vxlan_core.c1913
-rw-r--r--drivers/net/vxlan/vxlan_mdb.c1746
-rw-r--r--drivers/net/vxlan/vxlan_private.h109
-rw-r--r--drivers/net/vxlan/vxlan_vnifilter.c86
-rw-r--r--drivers/net/wan/Kconfig16
-rw-r--r--drivers/net/wan/Makefile3
-rw-r--r--drivers/net/wan/farsync.c2
-rw-r--r--drivers/net/wan/framer/Kconfig42
-rw-r--r--drivers/net/wan/framer/Makefile7
-rw-r--r--drivers/net/wan/framer/framer-core.c873
-rw-r--r--drivers/net/wan/framer/pef2256/Makefile8
-rw-r--r--drivers/net/wan/framer/pef2256/pef2256-regs.h250
-rw-r--r--drivers/net/wan/framer/pef2256/pef2256.c893
-rw-r--r--drivers/net/wan/fsl_qmc_hdlc.c808
-rw-r--r--drivers/net/wan/fsl_ucc_hdlc.c35
-rw-r--r--drivers/net/wan/hdlc_cisco.c4
-rw-r--r--drivers/net/wan/hdlc_fr.c4
-rw-r--r--drivers/net/wan/hdlc_ppp.c8
-rw-r--r--drivers/net/wan/ixp4xx_hss.c7
-rw-r--r--drivers/net/wan/lapbether.c10
-rw-r--r--drivers/net/wan/slic_ds26522.c3
-rw-r--r--drivers/net/wireguard/Makefile2
-rw-r--r--drivers/net/wireguard/allowedips.c114
-rw-r--r--drivers/net/wireguard/allowedips.h4
-rw-r--r--drivers/net/wireguard/cookie.c24
-rw-r--r--drivers/net/wireguard/device.c37
-rw-r--r--drivers/net/wireguard/generated/netlink.c73
-rw-r--r--drivers/net/wireguard/generated/netlink.h30
-rw-r--r--drivers/net/wireguard/main.c2
-rw-r--r--drivers/net/wireguard/netlink.c135
-rw-r--r--drivers/net/wireguard/noise.c38
-rw-r--r--drivers/net/wireguard/peer.h2
-rw-r--r--drivers/net/wireguard/queueing.c1
-rw-r--r--drivers/net/wireguard/queueing.h38
-rw-r--r--drivers/net/wireguard/receive.c22
-rw-r--r--drivers/net/wireguard/selftest/allowedips.c101
-rw-r--r--drivers/net/wireguard/send.c7
-rw-r--r--drivers/net/wireguard/socket.c4
-rw-r--r--drivers/net/wireguard/timers.c47
-rw-r--r--drivers/net/wireless/Kconfig76
-rw-r--r--drivers/net/wireless/Makefile11
-rw-r--r--drivers/net/wireless/admtek/adm8211.c9
-rw-r--r--drivers/net/wireless/ath/Kconfig1
-rw-r--r--drivers/net/wireless/ath/Makefile1
-rw-r--r--drivers/net/wireless/ath/ar5523/ar5523.c41
-rw-r--r--drivers/net/wireless/ath/ath.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/Kconfig8
-rw-r--r--drivers/net/wireless/ath/ath10k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath10k/ahb.c48
-rw-r--r--drivers/net/wireless/ath/ath10k/bmi.c13
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c104
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c210
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h39
-rw-r--r--drivers/net/wireless/ath/ath10k/coredump.c5
-rw-r--r--drivers/net/wireless/ath/ath10k/coredump.h9
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c69
-rw-r--r--drivers/net/wireless/ath/ath10k/debugfs_sta.c12
-rw-r--r--drivers/net/wireless/ath/ath10k/htc.c19
-rw-r--r--drivers/net/wireless/ath/ath10k/htc.h20
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.c3
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h30
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c55
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c28
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.c70
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h65
-rw-r--r--drivers/net/wireless/ath/ath10k/leds.c89
-rw-r--r--drivers/net/wireless/ath/ath10k/leds.h34
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c293
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c114
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.h9
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c62
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.h1
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c127
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h103
-rw-r--r--drivers/net/wireless/ath/ath10k/rx_desc.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/sdio.c39
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c65
-rw-r--r--drivers/net/wireless/ath/ath10k/spectral.c26
-rw-r--r--drivers/net/wireless/ath/ath10k/targaddrs.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode.c255
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode_i.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/thermal.c5
-rw-r--r--drivers/net/wireless/ath/ath10k/thermal.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/trace.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/trace.h64
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/usb.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/usb.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-ops.h32
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c27
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h7
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c156
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h137
-rw-r--r--drivers/net/wireless/ath/ath10k/wow.c1
-rw-r--r--drivers/net/wireless/ath/ath11k/Kconfig3
-rw-r--r--drivers/net/wireless/ath/ath11k/Makefile5
-rw-r--r--drivers/net/wireless/ath/ath11k/ahb.c384
-rw-r--r--drivers/net/wireless/ath/ath11k/ahb.h16
-rw-r--r--drivers/net/wireless/ath/ath11k/ce.c27
-rw-r--r--drivers/net/wireless/ath/ath11k/ce.h24
-rw-r--r--drivers/net/wireless/ath/ath11k/core.c1142
-rw-r--r--drivers/net/wireless/ath/ath11k/core.h189
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump.c54
-rw-r--r--drivers/net/wireless/ath/ath11k/coredump.h79
-rw-r--r--drivers/net/wireless/ath/ath11k/dbring.c16
-rw-r--r--drivers/net/wireless/ath/ath11k/dbring.h1
-rw-r--r--drivers/net/wireless/ath/ath11k/debug.c5
-rw-r--r--drivers/net/wireless/ath/ath11k/debug.h49
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs.c696
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs.h28
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c131
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h122
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs_sta.c143
-rw-r--r--drivers/net/wireless/ath/ath11k/debugfs_sta.h1
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.c72
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.h56
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.c448
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.h3
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_tx.c117
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_tx.h4
-rw-r--r--drivers/net/wireless/ath/ath11k/fw.c171
-rw-r--r--drivers/net/wireless/ath/ath11k/fw.h27
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.c130
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.h78
-rw-r--r--drivers/net/wireless/ath/ath11k/hal_desc.h9
-rw-r--r--drivers/net/wireless/ath/ath11k/hal_rx.c75
-rw-r--r--drivers/net/wireless/ath/ath11k/hal_rx.h39
-rw-r--r--drivers/net/wireless/ath/ath11k/hal_tx.c6
-rw-r--r--drivers/net/wireless/ath/ath11k/hal_tx.h4
-rw-r--r--drivers/net/wireless/ath/ath11k/hif.h79
-rw-r--r--drivers/net/wireless/ath/ath11k/htc.c45
-rw-r--r--drivers/net/wireless/ath/ath11k/htc.h18
-rw-r--r--drivers/net/wireless/ath/ath11k/hw.c525
-rw-r--r--drivers/net/wireless/ath/ath11k/hw.h47
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c3187
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.h8
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.c152
-rw-r--r--drivers/net/wireless/ath/ath11k/mhi.h4
-rw-r--r--drivers/net/wireless/ath/ath11k/p2p.c149
-rw-r--r--drivers/net/wireless/ath/ath11k/p2p.h22
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c408
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.h20
-rw-r--r--drivers/net/wireless/ath/ath11k/pcic.c198
-rw-r--r--drivers/net/wireless/ath/ath11k/pcic.h6
-rw-r--r--drivers/net/wireless/ath/ath11k/peer.c39
-rw-r--r--drivers/net/wireless/ath/ath11k/peer.h3
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.c375
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.h27
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.c439
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.h16
-rw-r--r--drivers/net/wireless/ath/ath11k/rx_desc.h3
-rw-r--r--drivers/net/wireless/ath/ath11k/spectral.c54
-rw-r--r--drivers/net/wireless/ath/ath11k/spectral.h2
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode.c433
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode.h6
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode_i.h50
-rw-r--r--drivers/net/wireless/ath/ath11k/thermal.c30
-rw-r--r--drivers/net/wireless/ath/ath11k/thermal.h11
-rw-r--r--drivers/net/wireless/ath/ath11k/trace.c2
-rw-r--r--drivers/net/wireless/ath/ath11k/trace.h69
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c2032
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.h823
-rw-r--r--drivers/net/wireless/ath/ath11k/wow.c65
-rw-r--r--drivers/net/wireless/ath/ath11k/wow.h1
-rw-r--r--drivers/net/wireless/ath/ath12k/Kconfig62
-rw-r--r--drivers/net/wireless/ath/ath12k/Makefile35
-rw-r--r--drivers/net/wireless/ath/ath12k/acpi.c510
-rw-r--r--drivers/net/wireless/ath/ath12k/acpi.h114
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.c1156
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.h80
-rw-r--r--drivers/net/wireless/ath/ath12k/ce.c1051
-rw-r--r--drivers/net/wireless/ath/ath12k/ce.h193
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c2321
-rw-r--r--drivers/net/wireless/ath/ath12k/core.h1519
-rw-r--r--drivers/net/wireless/ath/ath12k/coredump.c54
-rw-r--r--drivers/net/wireless/ath/ath12k/coredump.h81
-rw-r--r--drivers/net/wireless/ath/ath12k/dbring.c358
-rw-r--r--drivers/net/wireless/ath/ath12k/dbring.h80
-rw-r--r--drivers/net/wireless/ath/ath12k/debug.c104
-rw-r--r--drivers/net/wireless/ath/ath12k/debug.h76
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs.c1515
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs.h147
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c6178
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h2076
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_sta.c337
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs_sta.h24
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.c1841
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.h1976
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_mon.c4438
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_mon.h107
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.c4830
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.h191
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_tx.c1781
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_tx.h41
-rw-r--r--drivers/net/wireless/ath/ath12k/fw.c178
-rw-r--r--drivers/net/wireless/ath/ath12k/fw.h37
-rw-r--r--drivers/net/wireless/ath/ath12k/hal.c2663
-rw-r--r--drivers/net/wireless/ath/ath12k/hal.h1181
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_desc.h3045
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_rx.c947
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_rx.h1171
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_tx.c145
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_tx.h200
-rw-r--r--drivers/net/wireless/ath/ath12k/hif.h165
-rw-r--r--drivers/net/wireless/ath/ath12k/htc.c795
-rw-r--r--drivers/net/wireless/ath/ath12k/htc.h316
-rw-r--r--drivers/net/wireless/ath/ath12k/hw.c1680
-rw-r--r--drivers/net/wireless/ath/ath12k/hw.h382
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c14996
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.h209
-rw-r--r--drivers/net/wireless/ath/ath12k/mhi.c659
-rw-r--r--drivers/net/wireless/ath/ath12k/mhi.h47
-rw-r--r--drivers/net/wireless/ath/ath12k/p2p.c147
-rw-r--r--drivers/net/wireless/ath/ath12k/p2p.h24
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.c1895
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.h153
-rw-r--r--drivers/net/wireless/ath/ath12k/peer.c570
-rw-r--r--drivers/net/wireless/ath/ath12k/peer.h123
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.c3917
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.h635
-rw-r--r--drivers/net/wireless/ath/ath12k/reg.c994
-rw-r--r--drivers/net/wireless/ath/ath12k/reg.h123
-rw-r--r--drivers/net/wireless/ath/ath12k/rx_desc.h1544
-rw-r--r--drivers/net/wireless/ath/ath12k/testmode.c395
-rw-r--r--drivers/net/wireless/ath/ath12k/testmode.h40
-rw-r--r--drivers/net/wireless/ath/ath12k/trace.c10
-rw-r--r--drivers/net/wireless/ath/ath12k/trace.h179
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c11230
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.h6526
-rw-r--r--drivers/net/wireless/ath/ath12k/wow.c1029
-rw-r--r--drivers/net/wireless/ath/ath12k/wow.h62
-rw-r--r--drivers/net/wireless/ath/ath5k/ahb.c23
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c10
-rw-r--r--drivers/net/wireless/ath/ath5k/base.h2
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.h3
-rw-r--r--drivers/net/wireless/ath/ath5k/led.c3
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c20
-rw-r--r--drivers/net/wireless/ath/ath5k/pci.c6
-rw-r--r--drivers/net/wireless/ath/ath5k/pcu.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c43
-rw-r--r--drivers/net/wireless/ath/ath5k/reg.h2
-rw-r--r--drivers/net/wireless/ath/ath5k/reset.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/Makefile5
-rw-r--r--drivers/net/wireless/ath/ath6kl/bmi.c6
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c22
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/hif.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc.h6
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_mbox.c7
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_pipe.c9
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c8
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c10
-rw-r--r--drivers/net/wireless/ath/ath6kl/recovery.c6
-rw-r--r--drivers/net/wireless/ath/ath6kl/sdio.c22
-rw-r--r--drivers/net/wireless/ath/ath6kl/trace.h4
-rw-r--r--drivers/net/wireless/ath/ath6kl/txrx.c7
-rw-r--r--drivers/net/wireless/ath/ath6kl/usb.c6
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c4
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.h18
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig4
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile5
-rw-r--r--drivers/net/wireless/ath/ath9k/ahb.c89
-rw-r--r--drivers/net/wireless/ath/ath9k/antenna.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c10
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_calib.c30
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_hw.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_mac.c14
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_phy.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_aic.c10
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_calib.c74
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c66
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.h12
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_hw.c33
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_mac.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_mci.c13
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_paprd.c56
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c39
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.h93
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_wow.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h14
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/btcoex.c30
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.c61
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c39
-rw-r--r--drivers/net/wireless/ath/ath9k/common-beacon.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/common-debug.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/common-init.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/common-spectral.c62
-rw-r--r--drivers/net/wireless/ath/ath9k/common.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/common.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c119
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_debug.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/dynack.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.h12
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_4k.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_9287.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_def.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/gpio.c13
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c156
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h16
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_debug.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c44
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c32
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c14
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_hst.c17
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c168
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h6
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c70
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c39
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c42
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h8
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c71
-rw-r--r--drivers/net/wireless/ath/ath9k/mci.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/reg.h148
-rw-r--r--drivers/net/wireless/ath/ath9k/reg_aic.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/rng.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/tx99.c14
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.c35
-rw-r--r--drivers/net/wireless/ath/ath9k/wow.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c120
-rw-r--r--drivers/net/wireless/ath/carl9170/Makefile5
-rw-r--r--drivers/net/wireless/ath/carl9170/cmd.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/debug.c28
-rw-r--r--drivers/net/wireless/ath/carl9170/fw.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/fwcmd.h8
-rw-r--r--drivers/net/wireless/ath/carl9170/mac.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/main.c9
-rw-r--r--drivers/net/wireless/ath/carl9170/rx.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/tx.c8
-rw-r--r--drivers/net/wireless/ath/carl9170/usb.c61
-rw-r--r--drivers/net/wireless/ath/carl9170/wlan.h2
-rw-r--r--drivers/net/wireless/ath/dfs_pattern_detector.c21
-rw-r--r--drivers/net/wireless/ath/hw.c2
-rw-r--r--drivers/net/wireless/ath/key.c6
-rw-r--r--drivers/net/wireless/ath/main.c1
-rw-r--r--drivers/net/wireless/ath/testmode_i.h66
-rw-r--r--drivers/net/wireless/ath/trace.h4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.c35
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.h4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/hal.h76
-rw-r--r--drivers/net/wireless/ath/wcn36xx/main.c38
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c82
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.h3
-rw-r--r--drivers/net/wireless/ath/wcn36xx/testmode.c4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/txrx.c8
-rw-r--r--drivers/net/wireless/ath/wcn36xx/wcn36xx.h10
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c35
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c36
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.h5
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c26
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c6
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c34
-rw-r--r--drivers/net/wireless/ath/wil6210/p2p.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c1
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h10
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx_edma.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx_edma.h6
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h2
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c29
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h10
-rw-r--r--drivers/net/wireless/atmel/Kconfig35
-rw-r--r--drivers/net/wireless/atmel/Makefile4
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.c77
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.h2
-rw-r--r--drivers/net/wireless/atmel/atmel.c4528
-rw-r--r--drivers/net/wireless/atmel/atmel.h31
-rw-r--r--drivers/net/wireless/atmel/atmel_cs.c283
-rw-r--r--drivers/net/wireless/atmel/atmel_pci.c65
-rw-r--r--drivers/net/wireless/broadcom/b43/b43.h18
-rw-r--r--drivers/net/wireless/broadcom/b43/debugfs.c27
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.c8
-rw-r--r--drivers/net/wireless/broadcom/b43/main.c43
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ht.c6
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_n.c10
-rw-r--r--drivers/net/wireless/broadcom/b43/pio.c8
-rw-r--r--drivers/net/wireless/broadcom/b43/sysfs.c13
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_lpphy.c20
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_lpphy.h2
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/b43legacy.h2
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/debugfs.c26
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/dma.c8
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/main.c11
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/radio.c17
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/sysfs.c16
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/Kconfig1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile17
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/acpi.c51
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/Makefile12
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/core.c39
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c28
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/vops.h11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c136
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c19
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h56
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c1237
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h31
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c47
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c178
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h16
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c44
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h17
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/Makefile12
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c373
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h87
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c28
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/vops.h11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c41
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c65
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c141
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c167
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h68
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c116
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h149
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h179
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.c200
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h88
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c5
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c62
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h9
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c48
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c816
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c8
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c98
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c113
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/Makefile12
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c40
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c28
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/vops.h11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/xtlv.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c8
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c43
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c12
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c5
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c16
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c52
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c39
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c453
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h32
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h13
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c28
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c55
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c9
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h14
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h9
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h16
-rw-r--r--drivers/net/wireless/cisco/Kconfig59
-rw-r--r--drivers/net/wireless/cisco/Makefile3
-rw-r--r--drivers/net/wireless/cisco/airo.c8263
-rw-r--r--drivers/net/wireless/cisco/airo.h10
-rw-r--r--drivers/net/wireless/cisco/airo_cs.c218
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Kconfig11
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Makefile7
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c52
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.h2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.c136
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.h11
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw.h175
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_crypto.c246
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_crypto_ccmp.c438
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c728
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_crypto_wep.c247
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_module.c38
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_rx.c112
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_spy.c233
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_tx.c7
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_wx.c43
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c69
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-rs.c5
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.c4
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.h7
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-mac.c44
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-rs.c23
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965.c2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965.h2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/commands.h309
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.c73
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.h19
-rw-r--r--drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Kconfig26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Makefile35
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/1000.c59
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/2000.c106
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/22000.c985
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/5000.c91
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/6000.c243
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/7000.c193
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/8000.c99
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/9000.c175
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/ax210.c123
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/bz.c108
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/dr.c89
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c52
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c85
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c89
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c111
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c39
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/sc.c100
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/Makefile4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/agn.h27
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/calib.c22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/commands.h207
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/dev.h24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/devices.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c1144
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/led.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/lib.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c46
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/main.c187
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.c36
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.c70
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.h12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rx.c12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rxon.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/scan.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/sta.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.c29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.h12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tx.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/ucode.c18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.c811
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.h264
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/alive.h35
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/binding.h18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/coex.h112
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/commands.h102
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/config.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/context.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/d3.h322
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h319
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h106
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/debug.h140
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h226
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/location.h339
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h828
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac.h53
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h272
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/offload.h30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h66
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/phy.h35
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/power.h258
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rfi.h7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rs.h211
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rx.h557
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/scan.h219
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/sta.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/stats.h185
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h137
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/tx.h147
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/txq.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.c727
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.h34
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/debugfs.c94
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h75
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dump.c76
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/error-dump.h60
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h193
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.h31
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/init.c29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/notif-wait.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/paging.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/pnvm.c355
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/pnvm.h28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.c743
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.h267
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/rs.c138
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/runtime.h70
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/smem.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.c801
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.h370
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h585
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h269
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-csr.h59
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c185
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.c3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.h10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h38
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c497
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.h30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c879
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h88
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c394
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fh.h107
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.c125
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.h25
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-modparams.h24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c684
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h128
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c119
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h71
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h137
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-prph.h65
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c841
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h1085
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-utils.c195
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-utils.h58
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/Makefile2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h57
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/main.c377
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/net.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/sap.h91
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/trace-data.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mei/trace.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/Makefile12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/agg.c677
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/agg.h127
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ap.c363
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ap.h45
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/coex.c40
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/coex.h15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/constants.h81
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/d3.c2065
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/d3.h51
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/debugfs.c1113
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/debugfs.h244
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c451
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/fw.c548
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/hcmd.h56
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.c703
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.h250
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/key.c408
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/key.h46
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/led.c100
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/led.h29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.c909
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.h131
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/low_latency.c336
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/low_latency.h68
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c2702
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.h13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mcc.c285
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mcc.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.c776
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.h615
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.c1199
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.h171
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/notif.c719
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/notif.h35
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/phy.c198
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/phy.h60
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.c391
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.h33
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ptp.c321
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ptp.h45
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.c383
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.h23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/roc.c261
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/roc.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.c2264
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.h73
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.c2179
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.h173
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/session-protect.c222
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/session-protect.h102
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.c1319
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.h273
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/stats.c521
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/stats.h22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c663
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c62
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c339
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/link.c110
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/module.c11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c353
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c503
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h140
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/thermal.c467
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/thermal.h36
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/time_sync.c240
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/time_sync.h26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.c739
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.h23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tx.c1394
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tx.h77
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/Makefile8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/binding.c28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/coex.c159
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/constants.h18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c1800
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c267
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c875
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c401
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c127
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c951
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/led.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/link.c319
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c837
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c3513
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c408
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c335
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c1283
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c1226
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h1142
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/nvm.c142
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/offloading.c14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c732
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c180
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/power.c248
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ptp.c326
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/quota.c52
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rfi.c24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c412
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.c151
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.h73
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rx.c282
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c1771
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/scan.c802
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sf.c65
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.c999
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.h186
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tdls.c67
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c38
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tests/module.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.c489
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.h27
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c173
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tt.c296
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c883
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c462
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/vendor-cmd.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c345
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-v2.c618
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c35
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c2622
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h1172
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c2488
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c654
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c4367
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx-gen2.c1434
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c2689
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/internal.h842
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/iwl-context-info-v2.h344
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/iwl-context-info.h (renamed from drivers/net/wireless/intel/iwlwifi/iwl-context-info.h)47
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/rx.c2371
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c463
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c3734
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c259
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx.c1630
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/utils.c104
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/utils.h40
-rw-r--r--drivers/net/wireless/intel/iwlwifi/queue/tx.c1869
-rw-r--r--drivers/net/wireless/intel/iwlwifi/queue/tx.h183
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/Makefile7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/devinfo.c316
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/module.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c72
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/utils.c109
-rw-r--r--drivers/net/wireless/intersil/Kconfig2
-rw-r--r--drivers/net/wireless/intersil/Makefile2
-rw-r--r--drivers/net/wireless/intersil/hostap/Kconfig95
-rw-r--r--drivers/net/wireless/intersil/hostap/Makefile8
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap.h99
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211.h97
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_rx.c1116
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_tx.c554
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.c3277
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.h264
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_common.h420
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_config.h49
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_cs.c710
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_download.c811
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_hw.c3387
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_info.c509
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ioctl.c4053
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_main.c1126
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_pci.c445
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_plx.c617
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_proc.c411
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_wlan.h1051
-rw-r--r--drivers/net/wireless/intersil/orinoco/Kconfig143
-rw-r--r--drivers/net/wireless/intersil/orinoco/Makefile15
-rw-r--r--drivers/net/wireless/intersil/orinoco/airport.c268
-rw-r--r--drivers/net/wireless/intersil/orinoco/cfg.c291
-rw-r--r--drivers/net/wireless/intersil/orinoco/cfg.h15
-rw-r--r--drivers/net/wireless/intersil/orinoco/fw.c387
-rw-r--r--drivers/net/wireless/intersil/orinoco/fw.h21
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes.c777
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes.h534
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_dld.c477
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_dld.h52
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_rid.h165
-rw-r--r--drivers/net/wireless/intersil/orinoco/hw.c1360
-rw-r--r--drivers/net/wireless/intersil/orinoco/hw.h60
-rw-r--r--drivers/net/wireless/intersil/orinoco/main.c2414
-rw-r--r--drivers/net/wireless/intersil/orinoco/main.h50
-rw-r--r--drivers/net/wireless/intersil/orinoco/mic.c89
-rw-r--r--drivers/net/wireless/intersil/orinoco/mic.h23
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco.h251
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_cs.c341
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_nortel.c314
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_pci.c257
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_pci.h54
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_plx.c362
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_tmd.c237
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_usb.c1787
-rw-r--r--drivers/net/wireless/intersil/orinoco/scan.c259
-rw-r--r--drivers/net/wireless/intersil/orinoco/scan.h21
-rw-r--r--drivers/net/wireless/intersil/orinoco/spectrum_cs.c319
-rw-r--r--drivers/net/wireless/intersil/orinoco/wext.c1413
-rw-r--r--drivers/net/wireless/intersil/orinoco/wext.h13
-rw-r--r--drivers/net/wireless/intersil/p54/eeprom.h4
-rw-r--r--drivers/net/wireless/intersil/p54/fwio.c10
-rw-r--r--drivers/net/wireless/intersil/p54/main.c12
-rw-r--r--drivers/net/wireless/intersil/p54/p54.h3
-rw-r--r--drivers/net/wireless/intersil/p54/p54pci.c8
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi.c20
-rw-r--r--drivers/net/wireless/intersil/p54/txrx.c15
-rw-r--r--drivers/net/wireless/mac80211_hwsim.h280
-rw-r--r--drivers/net/wireless/marvell/libertas/Kconfig10
-rw-r--r--drivers/net/wireless/marvell/libertas/Makefile1
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.c94
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.c156
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.h15
-rw-r--r--drivers/net/wireless/marvell/libertas/cmdresp.c7
-rw-r--r--drivers/net/wireless/marvell/libertas/decl.h4
-rw-r--r--drivers/net/wireless/marvell/libertas/dev.h4
-rw-r--r--drivers/net/wireless/marvell/libertas/if_cs.c957
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.c76
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.c27
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.c6
-rw-r--r--drivers/net/wireless/marvell/libertas/main.c111
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.c51
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.h1
-rw-r--r--drivers/net/wireless/marvell/libertas/radiotap.h4
-rw-r--r--drivers/net/wireless/marvell/libertas/types.h21
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/cmd.c2
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/if_usb.c6
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/libertas_tf.h3
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/main.c17
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11h.c19
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.c20
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.h6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c53
-rw-r--r--drivers/net/wireless/marvell/mwifiex/Kconfig5
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c558
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfp.c6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cmdevt.c125
-rw-r--r--drivers/net/wireless/marvell/mwifiex/debugfs.c41
-rw-r--r--drivers/net/wireless/marvell/mwifiex/decl.h24
-rw-r--r--drivers/net/wireless/marvell/mwifiex/fw.h109
-rw-r--r--drivers/net/wireless/marvell/mwifiex/init.c110
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ioctl.h8
-rw-r--r--drivers/net/wireless/marvell/mwifiex/join.c84
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.c164
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.h103
-rw-r--r--drivers/net/wireless/marvell/mwifiex/pcie.c361
-rw-r--r--drivers/net/wireless/marvell/mwifiex/scan.c49
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.c106
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.h9
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmd.c186
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_event.c54
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_ioctl.c71
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_rx.c26
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_tx.c24
-rw-r--r--drivers/net/wireless/marvell/mwifiex/tdls.c20
-rw-r--r--drivers/net/wireless/marvell/mwifiex/txrx.c47
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_cmd.c210
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_event.c16
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_txrx.c45
-rw-r--r--drivers/net/wireless/marvell/mwifiex/usb.c33
-rw-r--r--drivers/net/wireless/marvell/mwifiex/util.c138
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.c21
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.h15
-rw-r--r--drivers/net/wireless/marvell/mwl8k.c203
-rw-r--r--drivers/net/wireless/mediatek/mt76/Kconfig17
-rw-r--r--drivers/net/wireless/mediatek/mt76/Makefile17
-rw-r--r--drivers/net/wireless/mediatek/mt76/agg-rx.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/channel.c416
-rw-r--r--drivers/net/wireless/mediatek/mt76/debugfs.c35
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c732
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.h164
-rw-r--r--drivers/net/wireless/mediatek/mt76/eeprom.c195
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c672
-rw-r--r--drivers/net/wireless/mediatek/mt76/mcu.c29
-rw-r--r--drivers/net/wireless/mediatek/mt76/mmio.c15
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h879
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/Kconfig4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/beacon.c83
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/core.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/dma.c63
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/init.c55
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mac.c129
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/main.c96
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mcu.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h15
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/regs.h14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/soc.c9
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/Kconfig4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c8
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/dma.c19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/init.c109
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mac.c169
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mac.h14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c141
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.c60
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.h13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mmio.c31
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h40
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci.c18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c66
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/regs.h14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/sdio.c27
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/soc.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/testmode.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac.h109
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h44
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c268
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h400
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c343
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c729
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h332
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c37
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/main.c23
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/pci.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/phy.c20
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/usb.c11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02.h27
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dma.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.c22
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c33
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.c24
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.h8
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_trace.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c24
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c33
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_util.c65
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c38
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/init.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c39
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/phy.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb.c11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c15
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c35
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/Kconfig11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/Makefile5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.c411
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.h136
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c545
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/dma.c364
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c185
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h58
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c483
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.c928
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.h42
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/main.c700
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c1384
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.h85
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mmio.c665
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h213
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/pci.c88
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/regs.h139
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/soc.c217
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/testmode.c81
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/testmode.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/Kconfig6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/Makefile11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.c279
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.h93
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c231
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/dma.c314
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/eeprom.h27
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/init.c317
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mac.c706
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mac.h53
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/main.c1320
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.c942
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.h33
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h452
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mt7921_trace.h51
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci.c430
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c201
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c83
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/regs.h457
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio.c93
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c73
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c27
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/testmode.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/trace.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/usb.c276
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/usb_mac.c255
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/Kconfig30
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/Makefile10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c319
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/init.c281
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.c1497
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.h23
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c2229
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.c3903
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.h714
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h380
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci.c639
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c151
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c53
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regd.c265
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regd.h19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regs.h92
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/testmode.c201
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/usb.c346
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x.h537
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c486
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h123
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_core.c986
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c168
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_dma.c372
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_mac.c384
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_regs.h487
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_trace.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_trace.h51
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_usb.c317
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Kconfig21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Makefile9
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/coredump.c268
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/coredump.h97
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c1056
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/dma.c1005
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c405
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h79
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/init.c1745
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c3321
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.h45
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/main.c2340
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c4889
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h974
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mmio.c892
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h902
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/npu.c352
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/pci.c269
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/regs.h791
-rw-r--r--drivers/net/wireless/mediatek/mt76/npu.c501
-rw-r--r--drivers/net/wireless/mediatek/mt76/pci.c25
-rw-r--r--drivers/net/wireless/mediatek/mt76/scan.c174
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio.c48
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio_txrx.c39
-rw-r--r--drivers/net/wireless/mediatek/mt76/testmode.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/testmode.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/trace.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c243
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb.c126
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb_trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb_trace.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/util.c25
-rw-r--r--drivers/net/wireless/mediatek/mt76/util.h9
-rw-r--r--drivers/net/wireless/mediatek/mt76/wed.c209
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/dma.c3
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/dma.h2
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/main.c12
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/trace.h2
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/tx.c2
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/usb.c1
-rw-r--r--drivers/net/wireless/microchip/wilc1000/cfg80211.c221
-rw-r--r--drivers/net/wireless/microchip/wilc1000/cfg80211.h6
-rw-r--r--drivers/net/wireless/microchip/wilc1000/fw.h13
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.c217
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.h46
-rw-r--r--drivers/net/wireless/microchip/wilc1000/mon.c4
-rw-r--r--drivers/net/wireless/microchip/wilc1000/netdev.c201
-rw-r--r--drivers/net/wireless/microchip/wilc1000/netdev.h18
-rw-r--r--drivers/net/wireless/microchip/wilc1000/sdio.c304
-rw-r--r--drivers/net/wireless/microchip/wilc1000/spi.c259
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.c514
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.h66
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_cfg.c37
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_cfg.h7
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_if.h2
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/Kconfig2
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/mac.c27
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/mac.h4
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/usb.c53
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/bus.h2
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/cfg80211.c16
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/commands.c14
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/core.c21
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/core.h3
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/event.c17
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c14
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c6
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c6
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/qlink.h21
-rw-r--r--drivers/net/wireless/ralink/rt2x00/Kconfig13
-rw-r--r--drivers/net/wireless/ralink/rt2x00/Makefile1
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.c13
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.h2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.c13
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.h2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.c13
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.h2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800.h25
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.c2175
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.h23
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800mmio.c5
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.c8
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800soc.c121
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.c7
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00.h38
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c5
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dev.c44
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00link.c4
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mac.c21
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00queue.c7
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00soc.c153
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00soc.h29
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00usb.c8
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.c9
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.h2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.c9
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.h2
-rw-r--r--drivers/net/wireless/ray_cs.c2819
-rw-r--r--drivers/net/wireless/ray_cs.h74
-rw-r--r--drivers/net/wireless/rayctl.h734
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8180/Makefile2
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c18
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8187/Makefile2
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c39
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8188e.c1885
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8188f.c1765
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8192c.c741
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8192e.c1767
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8192f.c2091
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8710b.c1875
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8723a.c648
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8723b.c1767
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/Kconfig14
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/Makefile5
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/core.c8239
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/regs.h1382
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h690
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192c.c580
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c1708
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723a.c396
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c1684
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c7023
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h1208
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/Kconfig16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/Makefile2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c56
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c79
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h10
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/cam.c5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/cam.h6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c67
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.h3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/debug.c12
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/efuse.c49
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/efuse.h6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c213
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.h25
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/ps.c19
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.c5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c29
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c40
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/phy.c14
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c12
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c10
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c63
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c41
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c8
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c15
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c226
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c56
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c18
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c147
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h10
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/Makefile11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/def.h (renamed from drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h)0
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/dm_common.c1061
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/dm_common.h79
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/fw_common.c370
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/fw_common.h49
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/hw_common.c1225
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/hw_common.h24
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/main.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/phy_common.c856
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/phy_common.h111
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/reg.h1393
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/rf_common.c359
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/rf_common.h13
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/trx_common.c516
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192d/trx_common.h405
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c1086
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h91
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c377
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h37
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c1190
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c44
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c940
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h60
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h1273
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c377
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c40
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c528
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h434
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/Makefile13
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/dm.c120
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/dm.h10
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/fw.c63
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/fw.h9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/hw.c1212
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/hw.h22
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/led.c10
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/led.h9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c3118
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h31
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/rf.c240
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/rf.h11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c394
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/table.c1675
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/table.h29
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c372
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.h60
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/dm.c23
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.c40
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c20
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/sw.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c24
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c25
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c42
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c18
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c15
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_btc.c16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hw.c34
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c42
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/phy.c6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/dm.c12
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c44
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.c42
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/led.h5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c49
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c8
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c28
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723com/phy_common.c12
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723com/phy_common.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c139
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.c60
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/led.h9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c79
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c286
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.h6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/wifi.h170
-rw-r--r--drivers/net/wireless/realtek/rtw88/Kconfig180
-rw-r--r--drivers/net/wireless/realtek/rtw88/Makefile65
-rw-r--r--drivers/net/wireless/realtek/rtw88/bf.c21
-rw-r--r--drivers/net/wireless/realtek/rtw88/bf.h7
-rw-r--r--drivers/net/wireless/realtek/rtw88/coex.c108
-rw-r--r--drivers/net/wireless/realtek/rtw88/coex.h11
-rw-r--r--drivers/net/wireless/realtek/rtw88/debug.c476
-rw-r--r--drivers/net/wireless/realtek/rtw88/debug.h21
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.c316
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.h49
-rw-r--r--drivers/net/wireless/realtek/rtw88/hci.h24
-rw-r--r--drivers/net/wireless/realtek/rtw88/led.c74
-rw-r--r--drivers/net/wireless/realtek/rtw88/led.h25
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac.c191
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac.h7
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac80211.c115
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c422
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.h221
-rw-r--r--drivers/net/wireless/realtek/rtw88/pci.c152
-rw-r--r--drivers/net/wireless/realtek/rtw88/pci.h3
-rw-r--r--drivers/net/wireless/realtek/rtw88/phy.c306
-rw-r--r--drivers/net/wireless/realtek/rtw88/phy.h20
-rw-r--r--drivers/net/wireless/realtek/rtw88/ps.c56
-rw-r--r--drivers/net/wireless/realtek/rtw88/ps.h2
-rw-r--r--drivers/net/wireless/realtek/rtw88/reg.h322
-rw-r--r--drivers/net/wireless/realtek/rtw88/regd.c24
-rw-r--r--drivers/net/wireless/realtek/rtw88/regd.h2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8703b.c2003
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8703b.h102
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8703b_tables.c902
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8703b_tables.h14
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723cs.c34
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723d.c714
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723d.h251
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723de.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723ds.c41
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723du.c36
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723x.c788
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8723x.h524
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812a.c1125
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812a.h10
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812a_table.c2812
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812a_table.h26
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8812au.c94
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a.c2270
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a.h62
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a_table.c23930
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814a_table.h40
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814ae.c31
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8814au.c54
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821a.c1226
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821a.h10
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821a_table.c2350
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821a_table.h21
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821au.c78
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821c.c248
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821c.h59
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821c_table.c1154
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821ce.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821cs.c36
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8821cu.c60
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822b.c145
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822b.h33
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822be.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822bs.c36
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822bu.c104
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c.c161
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c.h41
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c_table.c1239
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822ce.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822cs.c36
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822cu.c46
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw88xxa.c1989
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw88xxa.h175
-rw-r--r--drivers/net/wireless/realtek/rtw88/rx.c129
-rw-r--r--drivers/net/wireless/realtek/rtw88/rx.h75
-rw-r--r--drivers/net/wireless/realtek/rtw88/sar.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/sdio.c1437
-rw-r--r--drivers/net/wireless/realtek/rtw88/sdio.h178
-rw-r--r--drivers/net/wireless/realtek/rtw88/tx.c158
-rw-r--r--drivers/net/wireless/realtek/rtw88/tx.h143
-rw-r--r--drivers/net/wireless/realtek/rtw88/usb.c1381
-rw-r--r--drivers/net/wireless/realtek/rtw88/usb.h103
-rw-r--r--drivers/net/wireless/realtek/rtw88/util.c103
-rw-r--r--drivers/net/wireless/realtek/rtw88/util.h11
-rw-r--r--drivers/net/wireless/realtek/rtw88/wow.c2
-rw-r--r--drivers/net/wireless/realtek/rtw89/Kconfig115
-rw-r--r--drivers/net/wireless/realtek/rtw89/Makefile62
-rw-r--r--drivers/net/wireless/realtek/rtw89/acpi.c1281
-rw-r--r--drivers/net/wireless/realtek/rtw89/acpi.h298
-rw-r--r--drivers/net/wireless/realtek/rtw89/cam.c735
-rw-r--r--drivers/net/wireless/realtek/rtw89/cam.h665
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.c3269
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.h175
-rw-r--r--drivers/net/wireless/realtek/rtw89/coex.c8660
-rw-r--r--drivers/net/wireless/realtek/rtw89/coex.h226
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.c4795
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.h4265
-rw-r--r--drivers/net/wireless/realtek/rtw89/debug.c3796
-rw-r--r--drivers/net/wireless/realtek/rtw89/debug.h26
-rw-r--r--drivers/net/wireless/realtek/rtw89/efuse.c182
-rw-r--r--drivers/net/wireless/realtek/rtw89/efuse.h21
-rw-r--r--drivers/net/wireless/realtek/rtw89/efuse_be.c514
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.c9059
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.h4432
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.c4127
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.h877
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac80211.c1589
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac_be.c2657
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.c1702
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.h868
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci_be.c708
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.c5501
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.h629
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy_be.c1021
-rw-r--r--drivers/net/wireless/realtek/rtw89/ps.c450
-rw-r--r--drivers/net/wireless/realtek/rtw89/ps.h38
-rw-r--r--drivers/net/wireless/realtek/rtw89/reg.h5285
-rw-r--r--drivers/net/wireless/realtek/rtw89/regd.c1370
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b.c2721
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b.h76
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c3784
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.h34
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_rfk_table.c499
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_rfk_table.h38
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_table.c14916
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_table.h18
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851be.c102
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851bu.c66
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a.c693
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a.h5
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c346
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.h18
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a_table.c17
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a_table.h12
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852ae.c24
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852au.c79
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b.c1033
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b.h15
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_common.c2106
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_common.h400
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c4212
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.h34
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.c794
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_rfk_table.h62
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_table.c22927
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_table.h18
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852be.c106
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt.c890
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt.h15
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c4279
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.h34
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk_table.c490
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk_table.h38
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bte.c108
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bu.c81
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c.c1023
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c.h23
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c804
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.h20
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_rfk_table.c42
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_table.c29999
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_table.h19
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852ce.c46
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852cu.c69
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a.c3009
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a.h74
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c353
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.h18
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922ae.c119
-rw-r--r--drivers/net/wireless/realtek/rtw89/sar.c791
-rw-r--r--drivers/net/wireless/realtek/rtw89/sar.h28
-rw-r--r--drivers/net/wireless/realtek/rtw89/ser.c167
-rw-r--r--drivers/net/wireless/realtek/rtw89/txrx.h613
-rw-r--r--drivers/net/wireless/realtek/rtw89/usb.c1071
-rw-r--r--drivers/net/wireless/realtek/rtw89/usb.h77
-rw-r--r--drivers/net/wireless/realtek/rtw89/util.c162
-rw-r--r--drivers/net/wireless/realtek/rtw89/util.h37
-rw-r--r--drivers/net/wireless/realtek/rtw89/wow.c1820
-rw-r--r--drivers/net/wireless/realtek/rtw89/wow.h147
-rw-r--r--drivers/net/wireless/rndis_wlan.c3766
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_coex.c12
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_core.c4
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_debugfs.c3
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_hal.c18
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c34
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_main.c4
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c7
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio.c79
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio_ops.c15
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb.c32
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb_ops.c2
-rw-r--r--drivers/net/wireless/rsi/rsi_debugfs.h1
-rw-r--r--drivers/net/wireless/rsi/rsi_hal.h2
-rw-r--r--drivers/net/wireless/silabs/wfx/bus.h1
-rw-r--r--drivers/net/wireless/silabs/wfx/bus_sdio.c57
-rw-r--r--drivers/net/wireless/silabs/wfx/bus_spi.c47
-rw-r--r--drivers/net/wireless/silabs/wfx/data_tx.c125
-rw-r--r--drivers/net/wireless/silabs/wfx/data_tx.h21
-rw-r--r--drivers/net/wireless/silabs/wfx/hif_tx.c43
-rw-r--r--drivers/net/wireless/silabs/wfx/hif_tx.h1
-rw-r--r--drivers/net/wireless/silabs/wfx/main.c56
-rw-r--r--drivers/net/wireless/silabs/wfx/queue.c38
-rw-r--r--drivers/net/wireless/silabs/wfx/queue.h1
-rw-r--r--drivers/net/wireless/silabs/wfx/scan.c66
-rw-r--r--drivers/net/wireless/silabs/wfx/scan.h6
-rw-r--r--drivers/net/wireless/silabs/wfx/sta.c124
-rw-r--r--drivers/net/wireless/silabs/wfx/sta.h10
-rw-r--r--drivers/net/wireless/silabs/wfx/wfx.h8
-rw-r--r--drivers/net/wireless/st/cw1200/bh.c11
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200_sdio.c42
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200_spi.c79
-rw-r--r--drivers/net/wireless/st/cw1200/main.c7
-rw-r--r--drivers/net/wireless/st/cw1200/pm.c2
-rw-r--r--drivers/net/wireless/st/cw1200/queue.c49
-rw-r--r--drivers/net/wireless/st/cw1200/queue.h1
-rw-r--r--drivers/net/wireless/st/cw1200/sta.c22
-rw-r--r--drivers/net/wireless/st/cw1200/sta.h7
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.c12
-rw-r--r--drivers/net/wireless/st/cw1200/wsm.c2
-rw-r--r--drivers/net/wireless/ti/Kconfig8
-rw-r--r--drivers/net/wireless/ti/Makefile3
-rw-r--r--drivers/net/wireless/ti/wilink_platform_data.c35
-rw-r--r--drivers/net/wireless/ti/wl1251/acx.c35
-rw-r--r--drivers/net/wireless/ti/wl1251/acx.h3
-rw-r--r--drivers/net/wireless/ti/wl1251/cmd.c79
-rw-r--r--drivers/net/wireless/ti/wl1251/cmd.h5
-rw-r--r--drivers/net/wireless/ti/wl1251/init.c2
-rw-r--r--drivers/net/wireless/ti/wl1251/main.c16
-rw-r--r--drivers/net/wireless/ti/wl1251/reg.h6
-rw-r--r--drivers/net/wireless/ti/wl1251/sdio.c33
-rw-r--r--drivers/net/wireless/ti/wl1251/spi.c77
-rw-r--r--drivers/net/wireless/ti/wl1251/tx.c10
-rw-r--r--drivers/net/wireless/ti/wl1251/wl1251.h1
-rw-r--r--drivers/net/wireless/ti/wl1251/wl12xx_80211.h1
-rw-r--r--drivers/net/wireless/ti/wl12xx/main.c5
-rw-r--r--drivers/net/wireless/ti/wl12xx/reg.h6
-rw-r--r--drivers/net/wireless/ti/wl18xx/debugfs.c3
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.c10
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c79
-rw-r--r--drivers/net/wireless/ti/wl18xx/tx.c13
-rw-r--r--drivers/net/wireless/ti/wl18xx/wl18xx.h62
-rw-r--r--drivers/net/wireless/ti/wlcore/boot.c5
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.c47
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.h3
-rw-r--r--drivers/net/wireless/ti/wlcore/debugfs.c11
-rw-r--r--drivers/net/wireless/ti/wlcore/event.c6
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c212
-rw-r--r--drivers/net/wireless/ti/wlcore/scan.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/sdio.c36
-rw-r--r--drivers/net/wireless/ti/wlcore/spi.c7
-rw-r--r--drivers/net/wireless/ti/wlcore/sysfs.c14
-rw-r--r--drivers/net/wireless/ti/wlcore/testmode.c4
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.c8
-rw-r--r--drivers/net/wireless/ti/wlcore/vendor_cmd.c3
-rw-r--r--drivers/net/wireless/ti/wlcore/wl12xx_80211.h1
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore.h2
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore_i.h10
-rw-r--r--drivers/net/wireless/virtual/Kconfig20
-rw-r--r--drivers/net/wireless/virtual/Makefile3
-rw-r--r--drivers/net/wireless/virtual/mac80211_hwsim.c (renamed from drivers/net/wireless/mac80211_hwsim.c)1769
-rw-r--r--drivers/net/wireless/virtual/mac80211_hwsim.h346
-rw-r--r--drivers/net/wireless/virtual/virt_wifi.c (renamed from drivers/net/wireless/virt_wifi.c)36
-rw-r--r--drivers/net/wireless/wl3501.h615
-rw-r--r--drivers/net/wireless/wl3501_cs.c2030
-rw-r--r--drivers/net/wireless/zydas/Kconfig19
-rw-r--r--drivers/net/wireless/zydas/Makefile2
-rw-r--r--drivers/net/wireless/zydas/zd1201.c1905
-rw-r--r--drivers/net/wireless/zydas/zd1201.h144
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_def.h2
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.c17
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.h2
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.h3
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.c22
-rw-r--r--drivers/net/wwan/Kconfig3
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_coredump.c1
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_devlink.c9
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_imem.c58
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_imem.h13
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_imem_ops.c20
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_imem_ops.h10
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_mmio.c2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_mux.c1
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_mux.h7
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_mux_codec.c41
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_mux_codec.h2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_pcie.c77
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_pm.h2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_port.c3
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_port.h2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_protocol.h2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_trace.c3
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_trace.h2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_wwan.c44
-rw-r--r--drivers/net/wwan/mhi_wwan_ctrl.c2
-rw-r--r--drivers/net/wwan/mhi_wwan_mbim.c42
-rw-r--r--drivers/net/wwan/qcom_bam_dmux.c17
-rw-r--r--drivers/net/wwan/rpmsg_wwan_ctrl.c5
-rw-r--r--drivers/net/wwan/t7xx/Makefile5
-rw-r--r--drivers/net/wwan/t7xx/t7xx_cldma.c4
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_cldma.c87
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_cldma.h20
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c11
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h14
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c239
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h1
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c7
-rw-r--r--drivers/net/wwan/t7xx/t7xx_mhccif.h1
-rw-r--r--drivers/net/wwan/t7xx/t7xx_modem_ops.c132
-rw-r--r--drivers/net/wwan/t7xx/t7xx_modem_ops.h10
-rw-r--r--drivers/net/wwan/t7xx/t7xx_netdev.c126
-rw-r--r--drivers/net/wwan/t7xx/t7xx_netdev.h5
-rw-r--r--drivers/net/wwan/t7xx/t7xx_pci.c246
-rw-r--r--drivers/net/wwan/t7xx/t7xx_pci.h20
-rw-r--r--drivers/net/wwan/t7xx/t7xx_pcie_mac.c8
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port.h22
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c8
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_proxy.c180
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_proxy.h15
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_trace.c117
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_wwan.c149
-rw-r--r--drivers/net/wwan/t7xx/t7xx_reg.h26
-rw-r--r--drivers/net/wwan/t7xx/t7xx_state_monitor.c172
-rw-r--r--drivers/net/wwan/t7xx/t7xx_state_monitor.h10
-rw-r--r--drivers/net/wwan/wwan_core.c141
-rw-r--r--drivers/net/wwan/wwan_hwsim.c28
-rw-r--r--drivers/net/xen-netback/common.h9
-rw-r--r--drivers/net/xen-netback/hash.c5
-rw-r--r--drivers/net/xen-netback/interface.c24
-rw-r--r--drivers/net/xen-netback/netback.c388
-rw-r--r--drivers/net/xen-netback/rx.c8
-rw-r--r--drivers/net/xen-netback/xenbus.c5
-rw-r--r--drivers/net/xen-netfront.c62
-rw-r--r--drivers/nfc/fdp/fdp.c13
-rw-r--r--drivers/nfc/fdp/i2c.c10
-rw-r--r--drivers/nfc/mei_phy.h4
-rw-r--r--drivers/nfc/microread/i2c.c9
-rw-r--r--drivers/nfc/nfcmrvl/fw_dnld.c11
-rw-r--r--drivers/nfc/nfcmrvl/i2c.c22
-rw-r--r--drivers/nfc/nfcmrvl/main.c6
-rw-r--r--drivers/nfc/nfcmrvl/nfcmrvl.h30
-rw-r--r--drivers/nfc/nfcmrvl/spi.c1
-rw-r--r--drivers/nfc/nfcmrvl/uart.c20
-rw-r--r--drivers/nfc/nfcsim.c9
-rw-r--r--drivers/nfc/nxp-nci/core.c15
-rw-r--r--drivers/nfc/nxp-nci/firmware.c2
-rw-r--r--drivers/nfc/nxp-nci/i2c.c14
-rw-r--r--drivers/nfc/pn533/i2c.c9
-rw-r--r--drivers/nfc/pn533/pn533.c29
-rw-r--r--drivers/nfc/pn533/uart.c10
-rw-r--r--drivers/nfc/pn533/usb.c46
-rw-r--r--drivers/nfc/pn544/i2c.c13
-rw-r--r--drivers/nfc/s3fwrn5/Kconfig3
-rw-r--r--drivers/nfc/s3fwrn5/core.c11
-rw-r--r--drivers/nfc/s3fwrn5/firmware.c19
-rw-r--r--drivers/nfc/s3fwrn5/firmware.h2
-rw-r--r--drivers/nfc/s3fwrn5/i2c.c30
-rw-r--r--drivers/nfc/s3fwrn5/nci.c2
-rw-r--r--drivers/nfc/s3fwrn5/nci.h2
-rw-r--r--drivers/nfc/s3fwrn5/phy_common.c4
-rw-r--r--drivers/nfc/s3fwrn5/phy_common.h4
-rw-r--r--drivers/nfc/s3fwrn5/s3fwrn5.h2
-rw-r--r--drivers/nfc/s3fwrn5/uart.c5
-rw-r--r--drivers/nfc/st-nci/i2c.c9
-rw-r--r--drivers/nfc/st-nci/ndlc.c22
-rw-r--r--drivers/nfc/st-nci/se.c72
-rw-r--r--drivers/nfc/st21nfca/core.c4
-rw-r--r--drivers/nfc/st21nfca/dep.c18
-rw-r--r--drivers/nfc/st21nfca/i2c.c10
-rw-r--r--drivers/nfc/st21nfca/se.c19
-rw-r--r--drivers/nfc/st95hf/core.c28
-rw-r--r--drivers/nfc/trf7970a.c135
-rw-r--r--drivers/nfc/virtual_ncidev.c172
-rw-r--r--drivers/ntb/core.c12
-rw-r--r--drivers/ntb/hw/amd/ntb_hw_amd.c39
-rw-r--r--drivers/ntb/hw/amd/ntb_hw_amd.h1
-rw-r--r--drivers/ntb/hw/epf/ntb_hw_epf.c132
-rw-r--r--drivers/ntb/hw/idt/ntb_hw_idt.c59
-rw-r--r--drivers/ntb/hw/intel/ntb_hw_gen1.c11
-rw-r--r--drivers/ntb/hw/intel/ntb_hw_gen3.c3
-rw-r--r--drivers/ntb/hw/mscc/ntb_hw_switchtec.c11
-rw-r--r--drivers/ntb/msi.c22
-rw-r--r--drivers/ntb/ntb_transport.c67
-rw-r--r--drivers/ntb/test/ntb_perf.c8
-rw-r--r--drivers/ntb/test/ntb_pingpong.c3
-rw-r--r--drivers/ntb/test/ntb_tool.c17
-rw-r--r--drivers/nubus/bus.c9
-rw-r--r--drivers/nubus/nubus.c13
-rw-r--r--drivers/nubus/proc.c33
-rw-r--r--drivers/nvdimm/Kconfig52
-rw-r--r--drivers/nvdimm/Makefile1
-rw-r--r--drivers/nvdimm/badrange.c9
-rw-r--r--drivers/nvdimm/btt.c71
-rw-r--r--drivers/nvdimm/btt_devs.c30
-rw-r--r--drivers/nvdimm/bus.c128
-rw-r--r--drivers/nvdimm/claim.c27
-rw-r--r--drivers/nvdimm/core.c48
-rw-r--r--drivers/nvdimm/dax_devs.c20
-rw-r--r--drivers/nvdimm/dimm.c5
-rw-r--r--drivers/nvdimm/dimm_devs.c83
-rw-r--r--drivers/nvdimm/e820.c4
-rw-r--r--drivers/nvdimm/label.c3
-rw-r--r--drivers/nvdimm/namespace_devs.c204
-rw-r--r--drivers/nvdimm/nd-core.h15
-rw-r--r--drivers/nvdimm/nd.h19
-rw-r--r--drivers/nvdimm/nd_perf.c3
-rw-r--r--drivers/nvdimm/nd_virtio.c15
-rw-r--r--drivers/nvdimm/of_pmem.c19
-rw-r--r--drivers/nvdimm/pfn_devs.c140
-rw-r--r--drivers/nvdimm/pmem.c99
-rw-r--r--drivers/nvdimm/pmem.h4
-rw-r--r--drivers/nvdimm/ramdax.c282
-rw-r--r--drivers/nvdimm/region.c27
-rw-r--r--drivers/nvdimm/region_devs.c234
-rw-r--r--drivers/nvdimm/security.c59
-rw-r--r--drivers/nvdimm/virtio_pmem.c72
-rw-r--r--drivers/nvme/Makefile2
-rw-r--r--drivers/nvme/common/Kconfig13
-rw-r--r--drivers/nvme/common/Makefile6
-rw-r--r--drivers/nvme/common/auth.c496
-rw-r--r--drivers/nvme/common/keyring.c287
-rw-r--r--drivers/nvme/host/Kconfig49
-rw-r--r--drivers/nvme/host/Makefile4
-rw-r--r--drivers/nvme/host/apple.c378
-rw-r--r--drivers/nvme/host/auth.c511
-rw-r--r--drivers/nvme/host/constants.c34
-rw-r--r--drivers/nvme/host/core.c3237
-rw-r--r--drivers/nvme/host/fabrics.c494
-rw-r--r--drivers/nvme/host/fabrics.h66
-rw-r--r--drivers/nvme/host/fault_inject.c5
-rw-r--r--drivers/nvme/host/fc.c484
-rw-r--r--drivers/nvme/host/hwmon.c38
-rw-r--r--drivers/nvme/host/ioctl.c539
-rw-r--r--drivers/nvme/host/multipath.c654
-rw-r--r--drivers/nvme/host/nvme.h413
-rw-r--r--drivers/nvme/host/pci.c2149
-rw-r--r--drivers/nvme/host/pr.c344
-rw-r--r--drivers/nvme/host/rdma.c460
-rw-r--r--drivers/nvme/host/sysfs.c931
-rw-r--r--drivers/nvme/host/tcp.c1229
-rw-r--r--drivers/nvme/host/trace.c153
-rw-r--r--drivers/nvme/host/trace.h15
-rw-r--r--drivers/nvme/host/zns.c72
-rw-r--r--drivers/nvme/target/Kconfig51
-rw-r--r--drivers/nvme/target/Makefile5
-rw-r--r--drivers/nvme/target/admin-cmd.c850
-rw-r--r--drivers/nvme/target/auth.c191
-rw-r--r--drivers/nvme/target/configfs.c513
-rw-r--r--drivers/nvme/target/core.c649
-rw-r--r--drivers/nvme/target/debugfs.c229
-rw-r--r--drivers/nvme/target/debugfs.h42
-rw-r--r--drivers/nvme/target/discovery.c39
-rw-r--r--drivers/nvme/target/fabrics-cmd-auth.c180
-rw-r--r--drivers/nvme/target/fabrics-cmd.c192
-rw-r--r--drivers/nvme/target/fc.c444
-rw-r--r--drivers/nvme/target/fcloop.c579
-rw-r--r--drivers/nvme/target/io-cmd-bdev.c88
-rw-r--r--drivers/nvme/target/io-cmd-file.c30
-rw-r--r--drivers/nvme/target/loop.c168
-rw-r--r--drivers/nvme/target/nvmet.h303
-rw-r--r--drivers/nvme/target/passthru.c88
-rw-r--r--drivers/nvme/target/pci-epf.c2651
-rw-r--r--drivers/nvme/target/pr.c1155
-rw-r--r--drivers/nvme/target/rdma.c215
-rw-r--r--drivers/nvme/target/tcp.c751
-rw-r--r--drivers/nvme/target/trace.c214
-rw-r--r--drivers/nvme/target/trace.h33
-rw-r--r--drivers/nvme/target/zns.c91
-rw-r--r--drivers/nvmem/Kconfig427
-rw-r--r--drivers/nvmem/Makefile142
-rw-r--r--drivers/nvmem/an8855-efuse.c68
-rw-r--r--drivers/nvmem/apple-efuses.c2
-rw-r--r--drivers/nvmem/apple-spmi-nvmem.c62
-rw-r--r--drivers/nvmem/bcm-ocotp.c5
-rw-r--r--drivers/nvmem/brcm_nvram.c154
-rw-r--r--drivers/nvmem/core.c736
-rw-r--r--drivers/nvmem/imx-iim.c11
-rw-r--r--drivers/nvmem/imx-ocotp-ele.c248
-rw-r--r--drivers/nvmem/imx-ocotp-scu.c3
-rw-r--r--drivers/nvmem/imx-ocotp.c40
-rw-r--r--drivers/nvmem/internals.h59
-rw-r--r--drivers/nvmem/lan9662-otpc.c222
-rw-r--r--drivers/nvmem/layouts.c216
-rw-r--r--drivers/nvmem/layouts/Kconfig42
-rw-r--r--drivers/nvmem/layouts/Makefile8
-rw-r--r--drivers/nvmem/layouts/onie-tlv.c260
-rw-r--r--drivers/nvmem/layouts/sl28vpd.c169
-rw-r--r--drivers/nvmem/layouts/u-boot-env.c213
-rw-r--r--drivers/nvmem/layouts/u-boot-env.h15
-rw-r--r--drivers/nvmem/lpc18xx_eeprom.c4
-rw-r--r--drivers/nvmem/lpc18xx_otp.c7
-rw-r--r--drivers/nvmem/max77759-nvmem.c145
-rw-r--r--drivers/nvmem/meson-efuse.c45
-rw-r--r--drivers/nvmem/meson-mx-efuse.c12
-rw-r--r--drivers/nvmem/microchip-otpc.c3
-rw-r--r--drivers/nvmem/mtk-efuse.c69
-rw-r--r--drivers/nvmem/mxs-ocotp.c10
-rw-r--r--drivers/nvmem/nintendo-otp.c4
-rw-r--r--drivers/nvmem/qcom-spmi-sdam.c18
-rw-r--r--drivers/nvmem/qfprom.c50
-rw-r--r--drivers/nvmem/qnap-mcu-eeprom.c111
-rw-r--r--drivers/nvmem/qoriq-efuse.c78
-rw-r--r--drivers/nvmem/rave-sp-eeprom.c5
-rw-r--r--drivers/nvmem/rcar-efuse.c143
-rw-r--r--drivers/nvmem/rmem.c99
-rw-r--r--drivers/nvmem/rockchip-efuse.c5
-rw-r--r--drivers/nvmem/rockchip-otp.c206
-rw-r--r--drivers/nvmem/s32g-ocotp-nvmem.c100
-rw-r--r--drivers/nvmem/sc27xx-efuse.c3
-rw-r--r--drivers/nvmem/sec-qfprom.c97
-rw-r--r--drivers/nvmem/snvs_lpgpr.c3
-rw-r--r--drivers/nvmem/sprd-efuse.c4
-rw-r--r--drivers/nvmem/stm32-bsec-optee-ta.c298
-rw-r--r--drivers/nvmem/stm32-bsec-optee-ta.h80
-rw-r--r--drivers/nvmem/stm32-romem.c138
-rw-r--r--drivers/nvmem/sunplus-ocotp.c19
-rw-r--r--drivers/nvmem/sunxi_sid.c29
-rw-r--r--drivers/nvmem/u-boot-env.c100
-rw-r--r--drivers/nvmem/uniphier-efuse.c4
-rw-r--r--drivers/nvmem/vf610-ocotp.c3
-rw-r--r--drivers/nvmem/zynqmp_nvmem.c219
-rw-r--r--drivers/of/.kunitconfig5
-rw-r--r--drivers/of/Kconfig48
-rw-r--r--drivers/of/Makefile9
-rw-r--r--drivers/of/address.c684
-rw-r--r--drivers/of/base.c651
-rw-r--r--drivers/of/cpu.c210
-rw-r--r--drivers/of/device.c272
-rw-r--r--drivers/of/dynamic.c457
-rw-r--r--drivers/of/empty_root.dts13
-rw-r--r--drivers/of/fdt.c445
-rw-r--r--drivers/of/fdt_address.c25
-rw-r--r--drivers/of/irq.c395
-rw-r--r--drivers/of/kexec.c59
-rw-r--r--drivers/of/kobj.c12
-rw-r--r--drivers/of/kunit_overlay_test.dtso9
-rw-r--r--drivers/of/module.c81
-rw-r--r--drivers/of/of_kunit_helpers.c93
-rw-r--r--drivers/of/of_numa.c13
-rw-r--r--drivers/of/of_private.h70
-rw-r--r--drivers/of/of_reserved_mem.c518
-rw-r--r--drivers/of/of_test.c178
-rw-r--r--drivers/of/overlay.c103
-rw-r--r--drivers/of/overlay_test.c120
-rw-r--r--drivers/of/pdt.c2
-rw-r--r--drivers/of/platform.c197
-rw-r--r--drivers/of/property.c476
-rw-r--r--drivers/of/resolver.c100
-rw-r--r--drivers/of/unittest-data/Makefile68
-rw-r--r--drivers/of/unittest-data/overlay.dts64
-rw-r--r--drivers/of/unittest-data/overlay.dtso62
-rw-r--r--drivers/of/unittest-data/overlay_0.dts14
-rw-r--r--drivers/of/unittest-data/overlay_0.dtso9
-rw-r--r--drivers/of/unittest-data/overlay_1.dts14
-rw-r--r--drivers/of/unittest-data/overlay_1.dtso9
-rw-r--r--drivers/of/unittest-data/overlay_10.dtso (renamed from drivers/of/unittest-data/overlay_10.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_11.dts28
-rw-r--r--drivers/of/unittest-data/overlay_11.dtso27
-rw-r--r--drivers/of/unittest-data/overlay_12.dts14
-rw-r--r--drivers/of/unittest-data/overlay_12.dtso9
-rw-r--r--drivers/of/unittest-data/overlay_13.dts14
-rw-r--r--drivers/of/unittest-data/overlay_13.dtso9
-rw-r--r--drivers/of/unittest-data/overlay_15.dts30
-rw-r--r--drivers/of/unittest-data/overlay_15.dtso31
-rw-r--r--drivers/of/unittest-data/overlay_16.dtso (renamed from drivers/of/unittest-data/overlay_16.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_17.dtso (renamed from drivers/of/unittest-data/overlay_17.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_18.dtso (renamed from drivers/of/unittest-data/overlay_18.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_19.dtso (renamed from drivers/of/unittest-data/overlay_19.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_2.dtso (renamed from drivers/of/unittest-data/overlay_2.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_20.dtso (renamed from drivers/of/unittest-data/overlay_20.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_3.dtso (renamed from drivers/of/unittest-data/overlay_3.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_4.dts18
-rw-r--r--drivers/of/unittest-data/overlay_4.dtso17
-rw-r--r--drivers/of/unittest-data/overlay_5.dtso (renamed from drivers/of/unittest-data/overlay_5.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_6.dtso (renamed from drivers/of/unittest-data/overlay_6.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_7.dtso (renamed from drivers/of/unittest-data/overlay_7.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_8.dtso (renamed from drivers/of/unittest-data/overlay_8.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_9.dtso (renamed from drivers/of/unittest-data/overlay_9.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_bad_add_dup_node.dts28
-rw-r--r--drivers/of/unittest-data/overlay_bad_add_dup_node.dtso27
-rw-r--r--drivers/of/unittest-data/overlay_bad_add_dup_prop.dts39
-rw-r--r--drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso38
-rw-r--r--drivers/of/unittest-data/overlay_bad_phandle.dts14
-rw-r--r--drivers/of/unittest-data/overlay_bad_phandle.dtso13
-rw-r--r--drivers/of/unittest-data/overlay_bad_symbol.dts16
-rw-r--r--drivers/of/unittest-data/overlay_bad_symbol.dtso15
-rw-r--r--drivers/of/unittest-data/overlay_bad_unresolved.dtso7
-rw-r--r--drivers/of/unittest-data/overlay_base.dtso (renamed from drivers/of/unittest-data/overlay_base.dts)0
-rw-r--r--drivers/of/unittest-data/overlay_common.dtsi36
-rw-r--r--drivers/of/unittest-data/overlay_gpio_01.dts23
-rw-r--r--drivers/of/unittest-data/overlay_gpio_01.dtso24
-rw-r--r--drivers/of/unittest-data/overlay_gpio_02a.dts16
-rw-r--r--drivers/of/unittest-data/overlay_gpio_02a.dtso17
-rw-r--r--drivers/of/unittest-data/overlay_gpio_02b.dts16
-rw-r--r--drivers/of/unittest-data/overlay_gpio_02b.dtso17
-rw-r--r--drivers/of/unittest-data/overlay_gpio_03.dts23
-rw-r--r--drivers/of/unittest-data/overlay_gpio_03.dtso24
-rw-r--r--drivers/of/unittest-data/overlay_gpio_04a.dts16
-rw-r--r--drivers/of/unittest-data/overlay_gpio_04a.dtso17
-rw-r--r--drivers/of/unittest-data/overlay_gpio_04b.dts16
-rw-r--r--drivers/of/unittest-data/overlay_gpio_04b.dtso17
-rw-r--r--drivers/of/unittest-data/overlay_pci_node.dtso22
-rw-r--r--drivers/of/unittest-data/testcases.dtso (renamed from drivers/of/unittest-data/testcases.dts)0
-rw-r--r--drivers/of/unittest-data/testcases_common.dtsi2
-rw-r--r--drivers/of/unittest-data/tests-address.dtsi112
-rw-r--r--drivers/of/unittest-data/tests-interrupts.dtsi14
-rw-r--r--drivers/of/unittest-data/tests-lifecycle.dtsi8
-rw-r--r--drivers/of/unittest-data/tests-overlay.dtsi1
-rw-r--r--drivers/of/unittest-data/tests-phandle.dtsi12
-rw-r--r--drivers/of/unittest-data/tests-platform.dtsi28
-rw-r--r--drivers/of/unittest.c1372
-rw-r--r--drivers/opp/Kconfig1
-rw-r--r--drivers/opp/core.c1243
-rw-r--r--drivers/opp/cpu.c42
-rw-r--r--drivers/opp/debugfs.c25
-rw-r--r--drivers/opp/of.c658
-rw-r--r--drivers/opp/opp.h37
-rw-r--r--drivers/opp/ti-opp-supply.c21
-rw-r--r--drivers/parisc/Kconfig4
-rw-r--r--drivers/parisc/Makefile16
-rw-r--r--drivers/parisc/asp.c11
-rw-r--r--drivers/parisc/ccio-dma.c111
-rw-r--r--drivers/parisc/dino.c9
-rw-r--r--drivers/parisc/eisa.c5
-rw-r--r--drivers/parisc/eisa_eeprom.c2
-rw-r--r--drivers/parisc/eisa_enumerator.c8
-rw-r--r--drivers/parisc/gsc.c19
-rw-r--r--drivers/parisc/hppb.c7
-rw-r--r--drivers/parisc/iommu-helpers.h16
-rw-r--r--drivers/parisc/iosapic.c17
-rw-r--r--drivers/parisc/iosapic_private.h4
-rw-r--r--drivers/parisc/lasi.c37
-rw-r--r--drivers/parisc/lba_pci.c16
-rw-r--r--drivers/parisc/led.c898
-rw-r--r--drivers/parisc/pdc_stable.c81
-rw-r--r--drivers/parisc/power.c103
-rw-r--r--drivers/parisc/sba_iommu.c141
-rw-r--r--drivers/parisc/wax.c12
-rw-r--r--drivers/parport/Kconfig14
-rw-r--r--drivers/parport/Makefile1
-rw-r--r--drivers/parport/daisy.c1
-rw-r--r--drivers/parport/ieee1284.c4
-rw-r--r--drivers/parport/parport_amiga.c11
-rw-r--r--drivers/parport/parport_ax88796.c418
-rw-r--r--drivers/parport/parport_gsc.c30
-rw-r--r--drivers/parport/parport_gsc.h7
-rw-r--r--drivers/parport/parport_mfc3.c3
-rw-r--r--drivers/parport/parport_pc.c177
-rw-r--r--drivers/parport/parport_serial.c76
-rw-r--r--drivers/parport/parport_sunbpp.c6
-rw-r--r--drivers/parport/procfs.c212
-rw-r--r--drivers/parport/share.c46
-rw-r--r--drivers/pci/Kconfig78
-rw-r--r--drivers/pci/Makefile13
-rw-r--r--drivers/pci/access.c100
-rw-r--r--drivers/pci/ats.c82
-rw-r--r--drivers/pci/bus.c207
-rw-r--r--drivers/pci/controller/Kconfig463
-rw-r--r--drivers/pci/controller/Makefile4
-rw-r--r--drivers/pci/controller/cadence/Kconfig53
-rw-r--r--drivers/pci/controller/cadence/Makefile12
-rw-r--r--drivers/pci/controller/cadence/pci-j721e.c309
-rw-r--r--drivers/pci/controller/cadence/pci-sky1.c238
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-ep.c134
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-common.c288
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-common.h46
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-hpa.c368
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host.c361
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h193
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-hpa.c167
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-lga-regs.h230
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-plat.c17
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.c47
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.h512
-rw-r--r--drivers/pci/controller/cadence/pcie-sg2042.c131
-rw-r--r--drivers/pci/controller/dwc/Kconfig588
-rw-r--r--drivers/pci/controller/dwc/Makefile15
-rw-r--r--drivers/pci/controller/dwc/pci-dra7xx.c80
-rw-r--r--drivers/pci/controller/dwc/pci-exynos.c131
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c1966
-rw-r--r--drivers/pci/controller/dwc/pci-keystone.c452
-rw-r--r--drivers/pci/controller/dwc/pci-layerscape-ep.c154
-rw-r--r--drivers/pci/controller/dwc/pci-layerscape.c292
-rw-r--r--drivers/pci/controller/dwc/pci-meson.c40
-rw-r--r--drivers/pci/controller/dwc/pcie-al.c19
-rw-r--r--drivers/pci/controller/dwc/pcie-amd-mdb.c526
-rw-r--r--drivers/pci/controller/dwc/pcie-armada8k.c9
-rw-r--r--drivers/pci/controller/dwc/pcie-artpec6.c48
-rw-r--r--drivers/pci/controller/dwc/pcie-bt1.c645
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-debugfs.c927
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-ep.c918
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c765
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-plat.c27
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c894
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h508
-rw-r--r--drivers/pci/controller/dwc/pcie-dw-rockchip.c565
-rw-r--r--drivers/pci/controller/dwc/pcie-fu740.c3
-rw-r--r--drivers/pci/controller/dwc/pcie-hisi.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-histb.c69
-rw-r--r--drivers/pci/controller/dwc/pcie-intel-gw.c20
-rw-r--r--drivers/pci/controller/dwc/pcie-keembay.c55
-rw-r--r--drivers/pci/controller/dwc/pcie-kirin.c209
-rw-r--r--drivers/pci/controller/dwc/pcie-nxp-s32g.c406
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom-common.c88
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom-common.h14
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom-ep.c386
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c2168
-rw-r--r--drivers/pci/controller/dwc/pcie-rcar-gen4.c804
-rw-r--r--drivers/pci/controller/dwc/pcie-sophgo.c257
-rw-r--r--drivers/pci/controller/dwc/pcie-spacemit-k1.c357
-rw-r--r--drivers/pci/controller/dwc/pcie-spear13xx.c11
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32-ep.c343
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32.c370
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32.h19
-rw-r--r--drivers/pci/controller/dwc/pcie-tegra194.c301
-rw-r--r--drivers/pci/controller/dwc/pcie-uniphier-ep.c45
-rw-r--r--drivers/pci/controller/dwc/pcie-uniphier.c16
-rw-r--r--drivers/pci/controller/dwc/pcie-visconti.c6
-rw-r--r--drivers/pci/controller/mobiveil/Kconfig24
-rw-r--r--drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c14
-rw-r--r--drivers/pci/controller/mobiveil/pcie-mobiveil-host.c61
-rw-r--r--drivers/pci/controller/mobiveil/pcie-mobiveil-plat.c1
-rw-r--r--drivers/pci/controller/mobiveil/pcie-mobiveil.h5
-rw-r--r--drivers/pci/controller/pci-aardvark.c170
-rw-r--r--drivers/pci/controller/pci-ftpci100.c44
-rw-r--r--drivers/pci/controller/pci-host-common.c55
-rw-r--r--drivers/pci/controller/pci-host-common.h23
-rw-r--r--drivers/pci/controller/pci-host-generic.c3
-rw-r--r--drivers/pci/controller/pci-hyperv-intf.c1
-rw-r--r--drivers/pci/controller/pci-hyperv.c885
-rw-r--r--drivers/pci/controller/pci-ixp4xx.c19
-rw-r--r--drivers/pci/controller/pci-loongson.c94
-rw-r--r--drivers/pci/controller/pci-mvebu.c126
-rw-r--r--drivers/pci/controller/pci-rcar-gen2.c3
-rw-r--r--drivers/pci/controller/pci-tegra.c203
-rw-r--r--drivers/pci/controller/pci-thunder-ecam.c4
-rw-r--r--drivers/pci/controller/pci-thunder-pem.c5
-rw-r--r--drivers/pci/controller/pci-v3-semi.c9
-rw-r--r--drivers/pci/controller/pci-versatile.c1
-rw-r--r--drivers/pci/controller/pci-xgene-msi.c486
-rw-r--r--drivers/pci/controller/pci-xgene.c47
-rw-r--r--drivers/pci/controller/pcie-altera-msi.c58
-rw-r--r--drivers/pci/controller/pcie-altera.c279
-rw-r--r--drivers/pci/controller/pcie-apple.c400
-rw-r--r--drivers/pci/controller/pcie-brcmstb.c1116
-rw-r--r--drivers/pci/controller/pcie-hisi-error.c5
-rw-r--r--drivers/pci/controller/pcie-iproc-msi.c51
-rw-r--r--drivers/pci/controller/pcie-iproc-platform.c7
-rw-r--r--drivers/pci/controller/pcie-iproc.c47
-rw-r--r--drivers/pci/controller/pcie-iproc.h2
-rw-r--r--drivers/pci/controller/pcie-mediatek-gen3.c561
-rw-r--r--drivers/pci/controller/pcie-mediatek.c205
-rw-r--r--drivers/pci/controller/pcie-microchip-host.c1140
-rw-r--r--drivers/pci/controller/pcie-mt7621.c48
-rw-r--r--drivers/pci/controller/pcie-rcar-ep.c37
-rw-r--r--drivers/pci/controller/pcie-rcar-host.c185
-rw-r--r--drivers/pci/controller/pcie-rcar.h2
-rw-r--r--drivers/pci/controller/pcie-rockchip-ep.c622
-rw-r--r--drivers/pci/controller/pcie-rockchip-host.c88
-rw-r--r--drivers/pci/controller/pcie-rockchip.c250
-rw-r--r--drivers/pci/controller/pcie-rockchip.h163
-rw-r--r--drivers/pci/controller/pcie-rzg3s-host.c1761
-rw-r--r--drivers/pci/controller/pcie-xilinx-common.h31
-rw-r--r--drivers/pci/controller/pcie-xilinx-cpm.c156
-rw-r--r--drivers/pci/controller/pcie-xilinx-dma-pl.c847
-rw-r--r--drivers/pci/controller/pcie-xilinx-nwl.c274
-rw-r--r--drivers/pci/controller/pcie-xilinx.c68
-rw-r--r--drivers/pci/controller/plda/Kconfig31
-rw-r--r--drivers/pci/controller/plda/Makefile4
-rw-r--r--drivers/pci/controller/plda/pcie-microchip-host.c834
-rw-r--r--drivers/pci/controller/plda/pcie-plda-host.c651
-rw-r--r--drivers/pci/controller/plda/pcie-plda.h274
-rw-r--r--drivers/pci/controller/plda/pcie-starfive.c492
-rw-r--r--drivers/pci/controller/vmd.c502
-rw-r--r--drivers/pci/devres.c864
-rw-r--r--drivers/pci/doe.c603
-rw-r--r--drivers/pci/ecam.c2
-rw-r--r--drivers/pci/endpoint/Kconfig10
-rw-r--r--drivers/pci/endpoint/Makefile1
-rw-r--r--drivers/pci/endpoint/functions/Kconfig30
-rw-r--r--drivers/pci/endpoint/functions/Makefile1
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-mhi.c991
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-ntb.c16
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-test.c1088
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-vntb.c564
-rw-r--r--drivers/pci/endpoint/pci-ep-cfs.c83
-rw-r--r--drivers/pci/endpoint/pci-ep-msi.c100
-rw-r--r--drivers/pci/endpoint/pci-epc-core.c510
-rw-r--r--drivers/pci/endpoint/pci-epc-mem.c20
-rw-r--r--drivers/pci/endpoint/pci-epf-core.c242
-rw-r--r--drivers/pci/host-bridge.c1
-rw-r--r--drivers/pci/hotplug/Kconfig27
-rw-r--r--drivers/pci/hotplug/Makefile2
-rw-r--r--drivers/pci/hotplug/TODO22
-rw-r--r--drivers/pci/hotplug/acpiphp.h1
-rw-r--r--drivers/pci/hotplug/acpiphp_ampere_altra.c128
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c4
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c8
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c4
-rw-r--r--drivers/pci/hotplug/cpci_hotplug.h5
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c17
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c2
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c10
-rw-r--r--drivers/pci/hotplug/cpqphp_pci.c79
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c1
-rw-r--r--drivers/pci/hotplug/ibmphp.h7
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c6
-rw-r--r--drivers/pci/hotplug/ibmphp_pci.c12
-rw-r--r--drivers/pci/hotplug/octep_hp.c427
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c223
-rw-r--r--drivers/pci/hotplug/pciehp.h5
-rw-r--r--drivers/pci/hotplug/pciehp_core.c19
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c26
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c133
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c19
-rw-r--r--drivers/pci/hotplug/pnv_php.c252
-rw-r--r--drivers/pci/hotplug/rpaphp_core.c4
-rw-r--r--drivers/pci/hotplug/rpaphp_pci.c85
-rw-r--r--drivers/pci/hotplug/s390_pci_hpc.c71
-rw-r--r--drivers/pci/hotplug/shpchp.h57
-rw-r--r--drivers/pci/hotplug/shpchp_core.c29
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c79
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c87
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c8
-rw-r--r--drivers/pci/ide.c815
-rw-r--r--drivers/pci/iomap.c190
-rw-r--r--drivers/pci/iov.c219
-rw-r--r--drivers/pci/irq.c206
-rw-r--r--drivers/pci/mmap.c29
-rw-r--r--drivers/pci/msi/Makefile3
-rw-r--r--drivers/pci/msi/api.c406
-rw-r--r--drivers/pci/msi/irqdomain.c400
-rw-r--r--drivers/pci/msi/msi.c1237
-rw-r--r--drivers/pci/msi/msi.h114
-rw-r--r--drivers/pci/npem.c595
-rw-r--r--drivers/pci/of.c393
-rw-r--r--drivers/pci/of_property.c507
-rw-r--r--drivers/pci/p2pdma.c362
-rw-r--r--drivers/pci/pci-acpi.c328
-rw-r--r--drivers/pci/pci-bridge-emul.c52
-rw-r--r--drivers/pci/pci-bridge-emul.h2
-rw-r--r--drivers/pci/pci-driver.c154
-rw-r--r--drivers/pci/pci-mid.c4
-rw-r--r--drivers/pci/pci-pf-stub.c1
-rw-r--r--drivers/pci/pci-stub.c1
-rw-r--r--drivers/pci/pci-sysfs.c482
-rw-r--r--drivers/pci/pci.c2280
-rw-r--r--drivers/pci/pci.h746
-rw-r--r--drivers/pci/pcie/Kconfig19
-rw-r--r--drivers/pci/pcie/Makefile8
-rw-r--r--drivers/pci/pcie/aer.c892
-rw-r--r--drivers/pci/pcie/aer_inject.c6
-rw-r--r--drivers/pci/pcie/aspm.c1068
-rw-r--r--drivers/pci/pcie/bwctrl.c329
-rw-r--r--drivers/pci/pcie/dpc.c255
-rw-r--r--drivers/pci/pcie/edr.c40
-rw-r--r--drivers/pci/pcie/err.c71
-rw-r--r--drivers/pci/pcie/pme.c4
-rw-r--r--drivers/pci/pcie/portdrv.c848
-rw-r--r--drivers/pci/pcie/portdrv.h29
-rw-r--r--drivers/pci/pcie/portdrv_core.c586
-rw-r--r--drivers/pci/pcie/portdrv_pci.c252
-rw-r--r--drivers/pci/pcie/ptm.c624
-rw-r--r--drivers/pci/pcie/tlp.c137
-rw-r--r--drivers/pci/probe.c827
-rw-r--r--drivers/pci/proc.c4
-rw-r--r--drivers/pci/pwrctrl/Kconfig48
-rw-r--r--drivers/pci/pwrctrl/Makefile11
-rw-r--r--drivers/pci/pwrctrl/core.c150
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c139
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c648
-rw-r--r--drivers/pci/pwrctrl/slot.c95
-rw-r--r--drivers/pci/quirks.c551
-rw-r--r--drivers/pci/rebar.c328
-rw-r--r--drivers/pci/remove.c65
-rw-r--r--drivers/pci/search.c93
-rw-r--r--drivers/pci/setup-bus.c1753
-rw-r--r--drivers/pci/setup-irq.c64
-rw-r--r--drivers/pci/setup-res.c195
-rw-r--r--drivers/pci/slot.c70
-rw-r--r--drivers/pci/switch/switchtec.c267
-rw-r--r--drivers/pci/syscall.c12
-rw-r--r--drivers/pci/tph.c516
-rw-r--r--drivers/pci/tsm.c900
-rw-r--r--drivers/pci/vc.c9
-rw-r--r--drivers/pci/vgaarb.c410
-rw-r--r--drivers/pci/vpd.c50
-rw-r--r--drivers/pci/xen-pcifront.c175
-rw-r--r--drivers/pcmcia/Kconfig34
-rw-r--r--drivers/pcmcia/Makefile8
-rw-r--r--drivers/pcmcia/at91_cf.c407
-rw-r--r--drivers/pcmcia/bcm63xx_pcmcia.c10
-rw-r--r--drivers/pcmcia/cardbus.c1
-rw-r--r--drivers/pcmcia/cistpl.c6
-rw-r--r--drivers/pcmcia/cs.c26
-rw-r--r--drivers/pcmcia/cs_internal.h5
-rw-r--r--drivers/pcmcia/db1xxx_ss.c4
-rw-r--r--drivers/pcmcia/ds.c30
-rw-r--r--drivers/pcmcia/electra_cf.c8
-rw-r--r--drivers/pcmcia/i82092.c5
-rw-r--r--drivers/pcmcia/i82365.c3
-rw-r--r--drivers/pcmcia/max1600.c1
-rw-r--r--drivers/pcmcia/omap_cf.c21
-rw-r--r--drivers/pcmcia/pcmcia_resource.c2
-rw-r--r--drivers/pcmcia/pd6729.c7
-rw-r--r--drivers/pcmcia/pxa2xx_base.c12
-rw-r--r--drivers/pcmcia/pxa2xx_mainstone.c122
-rw-r--r--drivers/pcmcia/rsrc_iodyn.c168
-rw-r--r--drivers/pcmcia/rsrc_mgr.c1
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c12
-rw-r--r--drivers/pcmcia/sa1100_generic.c19
-rw-r--r--drivers/pcmcia/sa1100_h3600.c2
-rw-r--r--drivers/pcmcia/sa1100_simpad.c115
-rw-r--r--drivers/pcmcia/sa1111_badge4.c158
-rw-r--r--drivers/pcmcia/sa1111_generic.c8
-rw-r--r--drivers/pcmcia/sa1111_lubbock.c155
-rw-r--r--drivers/pcmcia/soc_common.c18
-rw-r--r--drivers/pcmcia/socket_sysfs.c5
-rw-r--r--drivers/pcmcia/tcic.c4
-rw-r--r--drivers/pcmcia/vrc4171_card.c745
-rw-r--r--drivers/pcmcia/xxs1500_ss.c4
-rw-r--r--drivers/pcmcia/yenta_socket.c14
-rw-r--r--drivers/peci/controller/Kconfig16
-rw-r--r--drivers/peci/controller/Makefile1
-rw-r--r--drivers/peci/controller/peci-aspeed.c19
-rw-r--r--drivers/peci/controller/peci-npcm.c297
-rw-r--r--drivers/peci/core.c19
-rw-r--r--drivers/peci/cpu.c38
-rw-r--r--drivers/peci/device.c9
-rw-r--r--drivers/peci/internal.h17
-rw-r--r--drivers/peci/request.c32
-rw-r--r--drivers/peci/sysfs.c2
-rw-r--r--drivers/perf/Kconfig125
-rw-r--r--drivers/perf/Makefile15
-rw-r--r--drivers/perf/alibaba_uncore_drw_pmu.c823
-rw-r--r--drivers/perf/amlogic/Kconfig10
-rw-r--r--drivers/perf/amlogic/Makefile5
-rw-r--r--drivers/perf/amlogic/meson_ddr_pmu_core.c564
-rw-r--r--drivers/perf/amlogic/meson_g12_ddr_pmu.c393
-rw-r--r--drivers/perf/apple_m1_cpu_pmu.c337
-rw-r--r--drivers/perf/arm-cci.c28
-rw-r--r--drivers/perf/arm-ccn.c24
-rw-r--r--drivers/perf/arm-cmn.c1020
-rw-r--r--drivers/perf/arm-ni.c862
-rw-r--r--drivers/perf/arm_brbe.c805
-rw-r--r--drivers/perf/arm_brbe.h47
-rw-r--r--drivers/perf/arm_cspmu/Kconfig29
-rw-r--r--drivers/perf/arm_cspmu/Makefile10
-rw-r--r--drivers/perf/arm_cspmu/ampere_cspmu.c261
-rw-r--r--drivers/perf/arm_cspmu/arm_cspmu.c1428
-rw-r--r--drivers/perf/arm_cspmu/arm_cspmu.h258
-rw-r--r--drivers/perf/arm_cspmu/nvidia_cspmu.c468
-rw-r--r--drivers/perf/arm_dmc620_pmu.c66
-rw-r--r--drivers/perf/arm_dsu_pmu.c53
-rw-r--r--drivers/perf/arm_pmu.c164
-rw-r--r--drivers/perf/arm_pmu_acpi.c253
-rw-r--r--drivers/perf/arm_pmu_platform.c24
-rw-r--r--drivers/perf/arm_pmuv3.c1666
-rw-r--r--drivers/perf/arm_smmuv3_pmu.c83
-rw-r--r--drivers/perf/arm_spe_pmu.c394
-rw-r--r--drivers/perf/arm_v6_pmu.c431
-rw-r--r--drivers/perf/arm_v7_pmu.c1924
-rw-r--r--drivers/perf/arm_xscale_pmu.c747
-rw-r--r--drivers/perf/cxl_pmu.c983
-rw-r--r--drivers/perf/dwc_pcie_pmu.c889
-rw-r--r--drivers/perf/fsl_imx8_ddr_perf.c194
-rw-r--r--drivers/perf/fsl_imx9_ddr_perf.c887
-rw-r--r--drivers/perf/fujitsu_uncore_pmu.c613
-rw-r--r--drivers/perf/hisilicon/Makefile3
-rw-r--r--drivers/perf/hisilicon/hisi_pcie_pmu.c211
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c61
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c429
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_hha_pmu.c62
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c599
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_mn_pmu.c411
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_noc_pmu.c443
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_pa_pmu.c155
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_pmu.c209
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_pmu.h76
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c260
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_uc_pmu.c595
-rw-r--r--drivers/perf/hisilicon/hns3_pmu.c37
-rw-r--r--drivers/perf/marvell_cn10k_ddr_pmu.c549
-rw-r--r--drivers/perf/marvell_cn10k_tad_pmu.c99
-rw-r--r--drivers/perf/marvell_pem_pmu.c425
-rw-r--r--drivers/perf/qcom_l2_pmu.c24
-rw-r--r--drivers/perf/qcom_l3_pmu.c18
-rw-r--r--drivers/perf/riscv_pmu.c142
-rw-r--r--drivers/perf/riscv_pmu_legacy.c47
-rw-r--r--drivers/perf/riscv_pmu_sbi.c934
-rw-r--r--drivers/perf/starfive_starlink_pmu.c642
-rw-r--r--drivers/perf/thunderx2_pmu.c38
-rw-r--r--drivers/perf/xgene_pmu.c57
-rw-r--r--drivers/phy/Kconfig37
-rw-r--r--drivers/phy/Makefile8
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c187
-rw-r--r--drivers/phy/allwinner/phy-sun50i-usb3.c1
-rw-r--r--drivers/phy/allwinner/phy-sun6i-mipi-dphy.c236
-rw-r--r--drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c16
-rw-r--r--drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c17
-rw-r--r--drivers/phy/amlogic/phy-meson-axg-pcie.c15
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c3
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-usb2.c29
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c36
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb2.c13
-rw-r--r--drivers/phy/amlogic/phy-meson8-hdmi-tx.c2
-rw-r--r--drivers/phy/amlogic/phy-meson8b-usb2.c37
-rw-r--r--drivers/phy/broadcom/Kconfig4
-rw-r--r--drivers/phy/broadcom/phy-bcm-cygnus-pcie.c20
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns-usb2.c55
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns-usb3.c10
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns2-pcie.c2
-rw-r--r--drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c1
-rw-r--r--drivers/phy/broadcom/phy-bcm-sr-pcie.c4
-rw-r--r--drivers/phy/broadcom/phy-bcm-sr-usb.c4
-rw-r--r--drivers/phy/broadcom/phy-bcm63xx-usbh.c9
-rw-r--r--drivers/phy/broadcom/phy-brcm-sata.c26
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c183
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.c517
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.h20
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb.c54
-rw-r--r--drivers/phy/cadence/cdns-dphy-rx.c36
-rw-r--r--drivers/phy/cadence/cdns-dphy.c161
-rw-r--r--drivers/phy/cadence/phy-cadence-salvo.c95
-rw-r--r--drivers/phy/cadence/phy-cadence-sierra.c632
-rw-r--r--drivers/phy/cadence/phy-cadence-torrent.c3827
-rw-r--r--drivers/phy/freescale/Kconfig14
-rw-r--r--drivers/phy/freescale/Makefile2
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c2
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c180
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c559
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8qm-hsio.c614
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c12
-rw-r--r--drivers/phy/freescale/phy-fsl-lynx-28g.c50
-rw-r--r--drivers/phy/freescale/phy-fsl-samsung-hdmi.c740
-rw-r--r--drivers/phy/hisilicon/Kconfig2
-rw-r--r--drivers/phy/hisilicon/phy-hi3660-usb3.c1
-rw-r--r--drivers/phy/hisilicon/phy-hi3670-pcie.c11
-rw-r--r--drivers/phy/hisilicon/phy-hi3670-usb3.c1
-rw-r--r--drivers/phy/hisilicon/phy-hi6220-usb.c2
-rw-r--r--drivers/phy/hisilicon/phy-hisi-inno-usb2.c77
-rw-r--r--drivers/phy/hisilicon/phy-histb-combphy.c7
-rw-r--r--drivers/phy/hisilicon/phy-hix5hd2-sata.c1
-rw-r--r--drivers/phy/ingenic/phy-ingenic-usb.c9
-rw-r--r--drivers/phy/intel/Kconfig10
-rw-r--r--drivers/phy/intel/Makefile1
-rw-r--r--drivers/phy/intel/phy-intel-lgm-combo.c51
-rw-r--r--drivers/phy/intel/phy-intel-thunderbay-emmc.c509
-rw-r--r--drivers/phy/lantiq/phy-lantiq-rcu-usb2.c1
-rw-r--r--drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c2
-rw-r--r--drivers/phy/marvell/Kconfig4
-rw-r--r--drivers/phy/marvell/phy-armada375-usb2.c2
-rw-r--r--drivers/phy/marvell/phy-armada38x-comphy.c10
-rw-r--r--drivers/phy/marvell/phy-berlin-sata.c3
-rw-r--r--drivers/phy/marvell/phy-berlin-usb.c7
-rw-r--r--drivers/phy/marvell/phy-mmp3-hsic.c5
-rw-r--r--drivers/phy/marvell/phy-mmp3-usb.c1
-rw-r--r--drivers/phy/marvell/phy-mvebu-a3700-comphy.c102
-rw-r--r--drivers/phy/marvell/phy-mvebu-a3700-utmi.c2
-rw-r--r--drivers/phy/marvell/phy-mvebu-cp110-comphy.c12
-rw-r--r--drivers/phy/marvell/phy-mvebu-cp110-utmi.c18
-rw-r--r--drivers/phy/marvell/phy-mvebu-sata.c1
-rw-r--r--drivers/phy/marvell/phy-pxa-28nm-hsic.c2
-rw-r--r--drivers/phy/marvell/phy-pxa-28nm-usb2.c3
-rw-r--r--drivers/phy/marvell/phy-pxa-usb.c3
-rw-r--r--drivers/phy/mediatek/Kconfig24
-rw-r--r--drivers/phy/mediatek/Makefile4
-rw-r--r--drivers/phy/mediatek/phy-mtk-dp.c4
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c238
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c246
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c535
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi-mt8195.h116
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi.c76
-rw-r--r--drivers/phy/mediatek/phy-mtk-hdmi.h15
-rw-r--r--drivers/phy/mediatek/phy-mtk-io.h8
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-csi-0-5-rx-reg.h62
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-csi-0-5.c294
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c164
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c76
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi.c55
-rw-r--r--drivers/phy/mediatek/phy-mtk-mipi-dsi.h7
-rw-r--r--drivers/phy/mediatek/phy-mtk-pcie.c19
-rw-r--r--drivers/phy/mediatek/phy-mtk-tphy.c685
-rw-r--r--drivers/phy/mediatek/phy-mtk-ufs.c79
-rw-r--r--drivers/phy/mediatek/phy-mtk-xfi-tphy.c451
-rw-r--r--drivers/phy/mediatek/phy-mtk-xsphy.c160
-rw-r--r--drivers/phy/microchip/Kconfig1
-rw-r--r--drivers/phy/microchip/lan966x_serdes.c103
-rw-r--r--drivers/phy/microchip/lan966x_serdes_regs.h42
-rw-r--r--drivers/phy/microchip/sparx5_serdes.c613
-rw-r--r--drivers/phy/microchip/sparx5_serdes.h45
-rw-r--r--drivers/phy/microchip/sparx5_serdes_regs.h830
-rw-r--r--drivers/phy/motorola/phy-cpcap-usb.c11
-rw-r--r--drivers/phy/motorola/phy-mapphone-mdm6600.c46
-rw-r--r--drivers/phy/mscc/phy-ocelot-serdes.c11
-rw-r--r--drivers/phy/nuvoton/Kconfig12
-rw-r--r--drivers/phy/nuvoton/Makefile3
-rw-r--r--drivers/phy/nuvoton/phy-ma35d1-usb2.c143
-rw-r--r--drivers/phy/phy-airoha-pcie-regs.h494
-rw-r--r--drivers/phy/phy-airoha-pcie.c1290
-rw-r--r--drivers/phy/phy-can-transceiver.c195
-rw-r--r--drivers/phy/phy-core-mipi-dphy.c31
-rw-r--r--drivers/phy/phy-core.c253
-rw-r--r--drivers/phy/phy-lgm-usb.c4
-rw-r--r--drivers/phy/phy-nxp-ptn3222.c123
-rw-r--r--drivers/phy/phy-snps-eusb2.c633
-rw-r--r--drivers/phy/phy-xgene.c3
-rw-r--r--drivers/phy/qualcomm/Kconfig116
-rw-r--r--drivers/phy/qualcomm/Makefile20
-rw-r--r--drivers/phy/qualcomm/phy-ath79-usb.c1
-rw-r--r--drivers/phy/qualcomm/phy-qcom-apq8064-sata.c16
-rw-r--r--drivers/phy/qualcomm/phy-qcom-edp.c615
-rw-r--r--drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c297
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c12
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-m31-eusb2.c326
-rw-r--r--drivers/phy/qualcomm/phy-qcom-m31.c348
-rw-r--r--drivers/phy/qualcomm/phy-qcom-pcie2.c6
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-combo.c4032
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-common.h62
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-dp-com-v3.h18
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v3.h21
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v4.h19
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v5.h14
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v6.h14
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-dp-phy.h62
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c405
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie.c4562
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v4.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h7
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h16
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h9
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6.h20
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_20.h25
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6_30.h25
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-sgmii.h20
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v2.h25
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h5
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h38
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v6.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v7.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v8.h38
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h20
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h17
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5_20.h21
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6-n4.h32
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6.h34
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_20.h19
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v6_30.h19
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h34
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8.h32
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcs-v8_50.h13
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h96
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v7.h87
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h64
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h2
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-ln-shrd-v5.h11
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-ln-shrd-v6.h32
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h52
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v7.h67
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h26
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h328
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h84
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_20.h51
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h64
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h80
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v8.h68
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-ufs.c2257
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c1354
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usb.c2617
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usbc.c1154
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.h156
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c65
-rw-r--r--drivers/phy/qualcomm/phy-qcom-sgmii-eth.c372
-rw-r--r--drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c348
-rw-r--r--drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c331
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c13
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hs.c2
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hsic.c6
-rw-r--r--drivers/phy/ralink/phy-mt7621-pci.c8
-rw-r--r--drivers/phy/ralink/phy-ralink-usb.c9
-rw-r--r--drivers/phy/realtek/Kconfig32
-rw-r--r--drivers/phy/realtek/Makefile3
-rw-r--r--drivers/phy/realtek/phy-rtk-usb2.c1314
-rw-r--r--drivers/phy/realtek/phy-rtk-usb3.c750
-rw-r--r--drivers/phy/renesas/Kconfig17
-rw-r--r--drivers/phy/renesas/Makefile2
-rw-r--r--drivers/phy/renesas/phy-rcar-gen2.c3
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-pcie.c13
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-usb2.c397
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-usb3.c12
-rw-r--r--drivers/phy/renesas/phy-rzg3e-usb3.c259
-rw-r--r--drivers/phy/renesas/r8a779f0-ether-serdes.c489
-rw-r--r--drivers/phy/rockchip/Kconfig46
-rw-r--r--drivers/phy/rockchip/Makefile4
-rw-r--r--drivers/phy/rockchip/phy-rockchip-dphy-rx0.c1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-emmc.c3
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-csidphy.c95
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c307
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-hdmi.c241
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c802
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c1103
-rw-r--r--drivers/phy/rockchip/phy-rockchip-pcie.c241
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c1714
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c2124
-rw-r--r--drivers/phy/rockchip/phy-rockchip-snps-pcie3.c355
-rw-r--r--drivers/phy/rockchip/phy-rockchip-typec.c16
-rw-r--r--drivers/phy/rockchip/phy-rockchip-usb.c61
-rw-r--r--drivers/phy/rockchip/phy-rockchip-usbdp.c1666
-rw-r--r--drivers/phy/samsung/Kconfig6
-rw-r--r--drivers/phy/samsung/Makefile2
-rw-r--r--drivers/phy/samsung/phy-exynos-dp-video.c9
-rw-r--r--drivers/phy/samsung/phy-exynos-mipi-video.c64
-rw-r--r--drivers/phy/samsung/phy-exynos5-usbdrd.c1946
-rw-r--r--drivers/phy/samsung/phy-exynos5250-usb2.c2
-rw-r--r--drivers/phy/samsung/phy-exynos7-ufs.c1
-rw-r--r--drivers/phy/samsung/phy-exynosautov9-ufs.c1
-rw-r--r--drivers/phy/samsung/phy-exynosautov920-ufs.c168
-rw-r--r--drivers/phy/samsung/phy-fsd-ufs.c1
-rw-r--r--drivers/phy/samsung/phy-gs101-ufs.c210
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.c75
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.h17
-rw-r--r--drivers/phy/samsung/phy-samsung-usb2.c5
-rw-r--r--drivers/phy/socionext/phy-uniphier-pcie.c2
-rw-r--r--drivers/phy/socionext/phy-uniphier-usb2.c2
-rw-r--r--drivers/phy/sophgo/Kconfig19
-rw-r--r--drivers/phy/sophgo/Makefile2
-rw-r--r--drivers/phy/sophgo/phy-cv1800-usb2.c169
-rw-r--r--drivers/phy/st/Kconfig11
-rw-r--r--drivers/phy/st/Makefile1
-rw-r--r--drivers/phy/st/phy-miphy28lp.c49
-rw-r--r--drivers/phy/st/phy-spear1310-miphy.c7
-rw-r--r--drivers/phy/st/phy-spear1340-miphy.c7
-rw-r--r--drivers/phy/st/phy-stih407-usb.c26
-rw-r--r--drivers/phy/st/phy-stm32-combophy.c605
-rw-r--r--drivers/phy/st/phy-stm32-usbphyc.c18
-rw-r--r--drivers/phy/starfive/Kconfig48
-rw-r--r--drivers/phy/starfive/Makefile5
-rw-r--r--drivers/phy/starfive/phy-jh7110-dphy-rx.c227
-rw-r--r--drivers/phy/starfive/phy-jh7110-dphy-tx.c461
-rw-r--r--drivers/phy/starfive/phy-jh7110-pcie.c204
-rw-r--r--drivers/phy/starfive/phy-jh7110-usb.c175
-rw-r--r--drivers/phy/sunplus/Kconfig12
-rw-r--r--drivers/phy/sunplus/Makefile2
-rw-r--r--drivers/phy/sunplus/phy-sunplus-usb2.c299
-rw-r--r--drivers/phy/tegra/Kconfig5
-rw-r--r--drivers/phy/tegra/Makefile1
-rw-r--r--drivers/phy/tegra/phy-tegra194-p2u.c16
-rw-r--r--drivers/phy/tegra/xusb-tegra124.c1
-rw-r--r--drivers/phy/tegra/xusb-tegra186.c229
-rw-r--r--drivers/phy/tegra/xusb-tegra210.c7
-rw-r--r--drivers/phy/tegra/xusb.c107
-rw-r--r--drivers/phy/tegra/xusb.h31
-rw-r--r--drivers/phy/ti/Kconfig6
-rw-r--r--drivers/phy/ti/phy-am654-serdes.c59
-rw-r--r--drivers/phy/ti/phy-da8xx-usb.c57
-rw-r--r--drivers/phy/ti/phy-dm816x-usb.c16
-rw-r--r--drivers/phy/ti/phy-gmii-sel.c212
-rw-r--r--drivers/phy/ti/phy-j721e-wiz.c477
-rw-r--r--drivers/phy/ti/phy-omap-control.c10
-rw-r--r--drivers/phy/ti/phy-omap-usb2.c41
-rw-r--r--drivers/phy/ti/phy-ti-pipe3.c32
-rw-r--r--drivers/phy/ti/phy-tusb1210.c92
-rw-r--r--drivers/phy/ti/phy-twl4030-usb.c6
-rw-r--r--drivers/phy/xilinx/phy-zynqmp.c406
-rw-r--r--drivers/pinctrl/Kconfig296
-rw-r--r--drivers/pinctrl/Makefile30
-rw-r--r--drivers/pinctrl/actions/pinctrl-owl.c55
-rw-r--r--drivers/pinctrl/actions/pinctrl-s500.c1
-rw-r--r--drivers/pinctrl/actions/pinctrl-s700.c1
-rw-r--r--drivers/pinctrl/actions/pinctrl-s900.c1
-rw-r--r--drivers/pinctrl/aspeed/Makefile2
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c21
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c21
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c69
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed.c5
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed.h2
-rw-r--r--drivers/pinctrl/bcm/Kconfig16
-rw-r--r--drivers/pinctrl/bcm/Kconfig.stb10
-rw-r--r--drivers/pinctrl/bcm/Makefile2
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm281xx.c862
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm2835.c145
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm4908.c3
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm6318.c121
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm63268.c139
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm6328.c91
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm6358.c24
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm6362.c121
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm6368.c91
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm63xx.c4
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm63xx.h2
-rw-r--r--drivers/pinctrl/bcm/pinctrl-brcmstb-bcm2712.c747
-rw-r--r--drivers/pinctrl/bcm/pinctrl-brcmstb.c442
-rw-r--r--drivers/pinctrl/bcm/pinctrl-brcmstb.h93
-rw-r--r--drivers/pinctrl/bcm/pinctrl-cygnus-mux.c17
-rw-r--r--drivers/pinctrl/bcm/pinctrl-iproc-gpio.c70
-rw-r--r--drivers/pinctrl/bcm/pinctrl-ns.c19
-rw-r--r--drivers/pinctrl/bcm/pinctrl-ns2-mux.c16
-rw-r--r--drivers/pinctrl/bcm/pinctrl-nsp-gpio.c41
-rw-r--r--drivers/pinctrl/bcm/pinctrl-nsp-mux.c16
-rw-r--r--drivers/pinctrl/berlin/berlin-bg2.c8
-rw-r--r--drivers/pinctrl/berlin/berlin-bg2cd.c8
-rw-r--r--drivers/pinctrl/berlin/berlin-bg2q.c8
-rw-r--r--drivers/pinctrl/berlin/berlin-bg4ct.c9
-rw-r--r--drivers/pinctrl/berlin/berlin.c41
-rw-r--r--drivers/pinctrl/berlin/berlin.h6
-rw-r--r--drivers/pinctrl/berlin/pinctrl-as370.c12
-rw-r--r--drivers/pinctrl/cirrus/Kconfig14
-rw-r--r--drivers/pinctrl/cirrus/Makefile2
-rw-r--r--drivers/pinctrl/cirrus/pinctrl-cs42l43.c624
-rw-r--r--drivers/pinctrl/cirrus/pinctrl-lochnagar.c34
-rw-r--r--drivers/pinctrl/cirrus/pinctrl-madera-core.c34
-rw-r--r--drivers/pinctrl/cix/Kconfig15
-rw-r--r--drivers/pinctrl/cix/Makefile4
-rw-r--r--drivers/pinctrl/cix/pinctrl-sky1-base.c587
-rw-r--r--drivers/pinctrl/cix/pinctrl-sky1.c559
-rw-r--r--drivers/pinctrl/cix/pinctrl-sky1.h48
-rw-r--r--drivers/pinctrl/core.c401
-rw-r--r--drivers/pinctrl/core.h58
-rw-r--r--drivers/pinctrl/devicetree.c26
-rw-r--r--drivers/pinctrl/devicetree.h6
-rw-r--r--drivers/pinctrl/freescale/Kconfig126
-rw-r--r--drivers/pinctrl/freescale/Makefile2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx-scmi.c361
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.c213
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.h24
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx1-core.c23
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx1.c228
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx23.c3
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx25.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx27.c354
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx28.c3
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx35.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx50.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx51.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx53.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6dl.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6q.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6sl.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6sll.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6sx.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6ul.c2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx7d.c2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx7ulp.c5
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8dxl.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8mm.c2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8mq.c6
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8qxp.c3
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx8ulp.c5
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx91.c271
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx93.c4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imxrt1050.c548
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imxrt1170.c2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-mxs.c44
-rw-r--r--drivers/pinctrl/freescale/pinctrl-mxs.h6
-rw-r--r--drivers/pinctrl/freescale/pinctrl-scu.c33
-rw-r--r--drivers/pinctrl/freescale/pinctrl-vf610.c4
-rw-r--r--drivers/pinctrl/intel/Kconfig56
-rw-r--r--drivers/pinctrl/intel/Kconfig.tng33
-rw-r--r--drivers/pinctrl/intel/Makefile4
-rw-r--r--drivers/pinctrl/intel/pinctrl-alderlake.c100
-rw-r--r--drivers/pinctrl/intel/pinctrl-baytrail.c585
-rw-r--r--drivers/pinctrl/intel/pinctrl-broxton.c40
-rw-r--r--drivers/pinctrl/intel/pinctrl-cannonlake.c106
-rw-r--r--drivers/pinctrl/intel/pinctrl-cedarfork.c56
-rw-r--r--drivers/pinctrl/intel/pinctrl-cherryview.c354
-rw-r--r--drivers/pinctrl/intel/pinctrl-denverton.c47
-rw-r--r--drivers/pinctrl/intel/pinctrl-elkhartlake.c110
-rw-r--r--drivers/pinctrl/intel/pinctrl-emmitsburg.c53
-rw-r--r--drivers/pinctrl/intel/pinctrl-geminilake.c27
-rw-r--r--drivers/pinctrl/intel/pinctrl-icelake.c100
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel-platform.c224
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.c844
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.h97
-rw-r--r--drivers/pinctrl/intel/pinctrl-jasperlake.c53
-rw-r--r--drivers/pinctrl/intel/pinctrl-lakefield.c45
-rw-r--r--drivers/pinctrl/intel/pinctrl-lewisburg.c19
-rw-r--r--drivers/pinctrl/intel/pinctrl-lynxpoint.c240
-rw-r--r--drivers/pinctrl/intel/pinctrl-merrifield.c668
-rw-r--r--drivers/pinctrl/intel/pinctrl-meteorlake.c278
-rw-r--r--drivers/pinctrl/intel/pinctrl-meteorpoint.c457
-rw-r--r--drivers/pinctrl/intel/pinctrl-moorefield.c344
-rw-r--r--drivers/pinctrl/intel/pinctrl-sunrisepoint.c67
-rw-r--r--drivers/pinctrl/intel/pinctrl-tangier.c585
-rw-r--r--drivers/pinctrl/intel/pinctrl-tangier.h92
-rw-r--r--drivers/pinctrl/intel/pinctrl-tigerlake.c107
-rw-r--r--drivers/pinctrl/mediatek/Kconfig179
-rw-r--r--drivers/pinctrl/mediatek/Makefile67
-rw-r--r--drivers/pinctrl/mediatek/mtk-eint.c356
-rw-r--r--drivers/pinctrl/mediatek/mtk-eint.h35
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-airoha.c3030
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-moore.c113
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-moore.h13
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt2701.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt2712.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt6397.c1
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt6765.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt6779.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt6795.c3
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt6878.c1478
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt6893.c879
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7620.c137
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7621.c117
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7622.c35
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7623.c45
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7629.c23
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt76x8.c249
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7981.c1060
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7986.c150
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt7988.c1546
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8127.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8135.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8167.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8173.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8183.c3
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8186.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8188.c1673
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8189.c1698
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8192.c5
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8195.c7
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8196.c1858
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8365.c23
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8516.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c143
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c70
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.h8
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt6878.h2248
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt6893.h2283
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt8188.h2259
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt8189.h2452
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt8196.h3085
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtmips.c351
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtmips.h53
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-paris.c103
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-paris.h17
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-rt2880.c61
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-rt305x.c140
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-rt3883.c108
-rw-r--r--drivers/pinctrl/meson/Kconfig41
-rw-r--r--drivers/pinctrl/meson/Makefile3
-rw-r--r--drivers/pinctrl/meson/pinctrl-amlogic-a4.c1109
-rw-r--r--drivers/pinctrl/meson/pinctrl-amlogic-c3.c1108
-rw-r--r--drivers/pinctrl/meson/pinctrl-amlogic-t7.c1611
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-a1.c19
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-axg-pmx.c16
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-axg-pmx.h4
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-axg.c29
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-g12a.c96
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-gxbb.c17
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-gxl.c27
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson-s4.c13
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.c46
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.h12
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8-pmx.c7
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8.c16
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8b.c16
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-ac5.c1
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-370.c1
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-375.c1
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-37xx.c126
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-38x.c9
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-39x.c9
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-ap806.c5
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-cp110.c6
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-xp.c9
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-dove.c51
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-kirkwood.c7
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-mvebu.c16
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-orion.c7
-rw-r--r--drivers/pinctrl/nomadik/Kconfig14
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-ab8500.c3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-ab8505.c3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-abx500.c83
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-abx500.h4
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik-db8500.c301
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c35
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik.c1049
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik.h189
-rw-r--r--drivers/pinctrl/nuvoton/Kconfig37
-rw-r--r--drivers/pinctrl/nuvoton/Makefile3
-rw-r--r--drivers/pinctrl/nuvoton/pinctrl-ma35.c1184
-rw-r--r--drivers/pinctrl/nuvoton/pinctrl-ma35.h52
-rw-r--r--drivers/pinctrl/nuvoton/pinctrl-ma35d1.c1798
-rw-r--r--drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c314
-rw-r--r--drivers/pinctrl/nuvoton/pinctrl-npcm8xx.c2474
-rw-r--r--drivers/pinctrl/nuvoton/pinctrl-wpcm450.c146
-rw-r--r--drivers/pinctrl/nxp/Kconfig15
-rw-r--r--drivers/pinctrl/nxp/Makefile4
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32.h61
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32cc.c992
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32g2.c818
-rw-r--r--drivers/pinctrl/pinconf-generic.c242
-rw-r--r--drivers/pinctrl/pinconf.c28
-rw-r--r--drivers/pinctrl/pinconf.h41
-rw-r--r--drivers/pinctrl/pinctrl-amd.c347
-rw-r--r--drivers/pinctrl/pinctrl-amd.h11
-rw-r--r--drivers/pinctrl/pinctrl-amdisp.c233
-rw-r--r--drivers/pinctrl/pinctrl-amdisp.h95
-rw-r--r--drivers/pinctrl/pinctrl-apple-gpio.c45
-rw-r--r--drivers/pinctrl/pinctrl-artpec6.c6
-rw-r--r--drivers/pinctrl/pinctrl-as3722.c46
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c142
-rw-r--r--drivers/pinctrl/pinctrl-at91.c375
-rw-r--r--drivers/pinctrl/pinctrl-aw9523.c1067
-rw-r--r--drivers/pinctrl/pinctrl-axp209.c73
-rw-r--r--drivers/pinctrl/pinctrl-bm1880.c8
-rw-r--r--drivers/pinctrl/pinctrl-cy8c95x0.c1519
-rw-r--r--drivers/pinctrl/pinctrl-da850-pupd.c6
-rw-r--r--drivers/pinctrl/pinctrl-da9062.c28
-rw-r--r--drivers/pinctrl/pinctrl-digicolor.c16
-rw-r--r--drivers/pinctrl/pinctrl-eic7700.c704
-rw-r--r--drivers/pinctrl/pinctrl-ep93xx.c1434
-rw-r--r--drivers/pinctrl/pinctrl-equilibrium.c144
-rw-r--r--drivers/pinctrl/pinctrl-equilibrium.h16
-rw-r--r--drivers/pinctrl/pinctrl-eyeq5.c575
-rw-r--r--drivers/pinctrl/pinctrl-falcon.c12
-rw-r--r--drivers/pinctrl/pinctrl-gemini.c23
-rw-r--r--drivers/pinctrl/pinctrl-ingenic.c1108
-rw-r--r--drivers/pinctrl/pinctrl-k210.c67
-rw-r--r--drivers/pinctrl/pinctrl-k230.c648
-rw-r--r--drivers/pinctrl/pinctrl-keembay.c71
-rw-r--r--drivers/pinctrl/pinctrl-lantiq.c5
-rw-r--r--drivers/pinctrl/pinctrl-lantiq.h8
-rw-r--r--drivers/pinctrl/pinctrl-loongson2.c312
-rw-r--r--drivers/pinctrl/pinctrl-lpc18xx.c12
-rw-r--r--drivers/pinctrl/pinctrl-max7360.c215
-rw-r--r--drivers/pinctrl/pinctrl-max77620.c11
-rw-r--r--drivers/pinctrl/pinctrl-mcp23s08.c169
-rw-r--r--drivers/pinctrl/pinctrl-mcp23s08.h9
-rw-r--r--drivers/pinctrl/pinctrl-mcp23s08_i2c.c95
-rw-r--r--drivers/pinctrl/pinctrl-mcp23s08_spi.c108
-rw-r--r--drivers/pinctrl/pinctrl-microchip-sgpio.c26
-rw-r--r--drivers/pinctrl/pinctrl-mlxbf3.c306
-rw-r--r--drivers/pinctrl/pinctrl-mpfs-iomux0.c278
-rw-r--r--drivers/pinctrl/pinctrl-ocelot.c314
-rw-r--r--drivers/pinctrl/pinctrl-oxnas.c1292
-rw-r--r--drivers/pinctrl/pinctrl-palmas.c5
-rw-r--r--drivers/pinctrl/pinctrl-pef2256.c358
-rw-r--r--drivers/pinctrl/pinctrl-pic32.c51
-rw-r--r--drivers/pinctrl/pinctrl-pic64gx-gpio2.c356
-rw-r--r--drivers/pinctrl/pinctrl-pistachio.c74
-rw-r--r--drivers/pinctrl/pinctrl-rk805.c296
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c1538
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.h11
-rw-r--r--drivers/pinctrl/pinctrl-rp1.c1913
-rw-r--r--drivers/pinctrl/pinctrl-scmi.c579
-rw-r--r--drivers/pinctrl/pinctrl-single.c137
-rw-r--r--drivers/pinctrl/pinctrl-st.c129
-rw-r--r--drivers/pinctrl/pinctrl-starfive.c1364
-rw-r--r--drivers/pinctrl/pinctrl-stmfx.c80
-rw-r--r--drivers/pinctrl/pinctrl-sx150x.c111
-rw-r--r--drivers/pinctrl/pinctrl-tb10x.c9
-rw-r--r--drivers/pinctrl/pinctrl-th1520.c918
-rw-r--r--drivers/pinctrl/pinctrl-thunderbay.c1297
-rw-r--r--drivers/pinctrl/pinctrl-tps6594.c582
-rw-r--r--drivers/pinctrl/pinctrl-upboard.c1070
-rw-r--r--drivers/pinctrl/pinctrl-utils.c36
-rw-r--r--drivers/pinctrl/pinctrl-utils.h23
-rw-r--r--drivers/pinctrl/pinctrl-xway.c283
-rw-r--r--drivers/pinctrl/pinctrl-zynq.c3
-rw-r--r--drivers/pinctrl/pinctrl-zynqmp.c189
-rw-r--r--drivers/pinctrl/pinmux.c392
-rw-r--r--drivers/pinctrl/pinmux.h49
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa25x.c1
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa27x.c1
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa2xx.c61
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa2xx.h15
-rw-r--r--drivers/pinctrl/qcom/Kconfig394
-rw-r--r--drivers/pinctrl/qcom/Kconfig.msm476
-rw-r--r--drivers/pinctrl/qcom/Makefile32
-rw-r--r--drivers/pinctrl/qcom/pinctrl-apq8064.c105
-rw-r--r--drivers/pinctrl/qcom/pinctrl-apq8084.c265
-rw-r--r--drivers/pinctrl/qcom/pinctrl-glymur.c1777
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq4019.c105
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq5018.c772
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq5332.c852
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq5424.c809
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq6018.c261
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq8064.c115
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq8074.c241
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq9574.c817
-rw-r--r--drivers/pinctrl/qcom/pinctrl-kaanapali.c1803
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpass-lpi.c178
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpass-lpi.h40
-rw-r--r--drivers/pinctrl/qcom/pinctrl-mdm9607.c277
-rw-r--r--drivers/pinctrl/qcom/pinctrl-mdm9615.c91
-rw-r--r--drivers/pinctrl/qcom/pinctrl-milos.c1339
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.c296
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.h61
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8226.c180
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8660.c253
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8909.c269
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8916.c557
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8917.c1625
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8953.c425
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8960.c465
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8976.c221
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8994.c565
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8996.c523
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8998.c395
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8x74.c487
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcm2290.c310
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcs404.c389
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcs615.c1106
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcs8300.c1245
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qdf2xxx.c9
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qdu1000.c1266
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sa8775p.c1558
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sar2130p.c1504
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc7180.c255
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c16
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc7280.c323
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc8180x.c295
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c187
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc8280xp.c363
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm660-lpass-lpi.c160
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm660.c402
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm670.c1355
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm845.c287
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdx55.c191
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdx65.c195
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdx75.c1142
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c236
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm4450.c1013
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c155
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm6115.c175
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm6125.c300
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm6350.c297
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm6375.c359
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm7150.c1273
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8150.c287
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c15
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8250.c340
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c151
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8350.c299
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c216
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8450.c301
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c224
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8550.c1781
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c231
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8650.c1761
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8750.c1730
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-gpio.c142
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-mpp.c67
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c63
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c79
-rw-r--r--drivers/pinctrl/qcom/pinctrl-x1e80100.c1880
-rw-r--r--drivers/pinctrl/qcom/tlmm-test.c709
-rw-r--r--drivers/pinctrl/ralink/Kconfig35
-rw-r--r--drivers/pinctrl/ralink/Makefile8
-rw-r--r--drivers/pinctrl/ralink/pinctrl-mt7620.c391
-rw-r--r--drivers/pinctrl/ralink/pinctrl-mt7621.c116
-rw-r--r--drivers/pinctrl/ralink/pinctrl-ralink.c351
-rw-r--r--drivers/pinctrl/ralink/pinctrl-ralink.h53
-rw-r--r--drivers/pinctrl/ralink/pinctrl-rt2880.c60
-rw-r--r--drivers/pinctrl/ralink/pinctrl-rt305x.c137
-rw-r--r--drivers/pinctrl/ralink/pinctrl-rt3883.c107
-rw-r--r--drivers/pinctrl/realtek/Kconfig24
-rw-r--r--drivers/pinctrl/realtek/Makefile6
-rw-r--r--drivers/pinctrl/realtek/pinctrl-rtd.c596
-rw-r--r--drivers/pinctrl/realtek/pinctrl-rtd.h124
-rw-r--r--drivers/pinctrl/realtek/pinctrl-rtd1315e.c1441
-rw-r--r--drivers/pinctrl/realtek/pinctrl-rtd1319d.c1611
-rw-r--r--drivers/pinctrl/realtek/pinctrl-rtd1619b.c1601
-rw-r--r--drivers/pinctrl/renesas/Kconfig274
-rw-r--r--drivers/pinctrl/renesas/Makefile3
-rw-r--r--drivers/pinctrl/renesas/core.c80
-rw-r--r--drivers/pinctrl/renesas/gpio.c14
-rw-r--r--drivers/pinctrl/renesas/pfc-emev2.c3
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a73a4.c6
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7740.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77470.c46
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7778.c43
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7779.c448
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7790.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7791.c6
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7792.c2
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7794.c50
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77950.c5703
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77951.c13
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a7796.c13
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77965.c13
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77970.c41
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77980.c56
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77990.c41
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a77995.c46
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a779a0.c16
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a779f0.c11
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a779g0.c1945
-rw-r--r--drivers/pinctrl/renesas/pfc-r8a779h0.c4116
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7203.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7264.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7269.c6
-rw-r--r--drivers/pinctrl/renesas/pfc-sh73a0.c8
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7720.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7722.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7723.c5
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7724.c5
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7734.c5
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7757.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7785.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-sh7786.c4
-rw-r--r--drivers/pinctrl/renesas/pfc-shx3.c4
-rw-r--r--drivers/pinctrl/renesas/pinctrl-rza1.c45
-rw-r--r--drivers/pinctrl/renesas/pinctrl-rza2.c46
-rw-r--r--drivers/pinctrl/renesas/pinctrl-rzg2l.c2693
-rw-r--r--drivers/pinctrl/renesas/pinctrl-rzn1.c45
-rw-r--r--drivers/pinctrl/renesas/pinctrl-rzt2h.c813
-rw-r--r--drivers/pinctrl/renesas/pinctrl-rzv2m.c124
-rw-r--r--drivers/pinctrl/renesas/pinctrl.c74
-rw-r--r--drivers/pinctrl/renesas/sh_pfc.h15
-rw-r--r--drivers/pinctrl/samsung/Kconfig5
-rw-r--r--drivers/pinctrl/samsung/Makefile1
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos-arm.c14
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos-arm64.c1276
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c530
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.h116
-rw-r--r--drivers/pinctrl/samsung/pinctrl-s3c24xx.c653
-rw-r--r--drivers/pinctrl/samsung/pinctrl-s3c64xx.c14
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.c350
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.h69
-rw-r--r--drivers/pinctrl/sophgo/Kconfig88
-rw-r--r--drivers/pinctrl/sophgo/Makefile13
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-cv1800b.c471
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-cv1812h.c780
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-cv18xx.c451
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-cv18xx.h139
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2000.c780
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2002.c551
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c296
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2042.c655
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2042.h49
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sg2044.c718
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sophgo-common.c451
-rw-r--r--drivers/pinctrl/sophgo/pinctrl-sophgo.h136
-rw-r--r--drivers/pinctrl/spacemit/Kconfig18
-rw-r--r--drivers/pinctrl/spacemit/Makefile3
-rw-r--r--drivers/pinctrl/spacemit/pinctrl-k1.c1065
-rw-r--r--drivers/pinctrl/spacemit/pinctrl-k1.h40
-rw-r--r--drivers/pinctrl/spear/pinctrl-plgpio.c25
-rw-r--r--drivers/pinctrl/spear/pinctrl-spear.c20
-rw-r--r--drivers/pinctrl/spear/pinctrl-spear1310.c2
-rw-r--r--drivers/pinctrl/spear/pinctrl-spear1340.c2
-rw-r--r--drivers/pinctrl/spear/pinctrl-spear300.c2
-rw-r--r--drivers/pinctrl/spear/pinctrl-spear310.c2
-rw-r--r--drivers/pinctrl/spear/pinctrl-spear320.c2
-rw-r--r--drivers/pinctrl/sprd/pinctrl-sprd.c34
-rw-r--r--drivers/pinctrl/sprd/pinctrl-sprd.h2
-rw-r--r--drivers/pinctrl/starfive/Kconfig51
-rw-r--r--drivers/pinctrl/starfive/Makefile7
-rw-r--r--drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c1354
-rw-r--r--drivers/pinctrl/starfive/pinctrl-starfive-jh7110-aon.c176
-rw-r--r--drivers/pinctrl/starfive/pinctrl-starfive-jh7110-sys.c449
-rw-r--r--drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c1008
-rw-r--r--drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h73
-rw-r--r--drivers/pinctrl/stm32/Kconfig22
-rw-r--r--drivers/pinctrl/stm32/Makefile2
-rw-r--r--drivers/pinctrl/stm32/pinctrl-stm32-hdp.c726
-rw-r--r--drivers/pinctrl/stm32/pinctrl-stm32.c690
-rw-r--r--drivers/pinctrl/stm32/pinctrl-stm32.h26
-rw-r--r--drivers/pinctrl/stm32/pinctrl-stm32mp257.c2588
-rw-r--r--drivers/pinctrl/sunplus/sppctl.c50
-rw-r--r--drivers/pinctrl/sunxi/Kconfig10
-rw-r--r--drivers/pinctrl/sunxi/Makefile3
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c11
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c9
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-a100-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-a100.c13
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-h5.c10
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-h6-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-h6.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c381
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c54
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c54
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun5i.c9
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c9
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a83t-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a83t.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-h3-r.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-h3.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c10
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c2
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-suniv-f1c100s.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi-dt.c373
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c134
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.h47
-rw-r--r--drivers/pinctrl/tegra/Kconfig8
-rw-r--r--drivers/pinctrl/tegra/Makefile2
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra-xusb.c16
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra.c165
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra.h39
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra114.c7
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra124.c7
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra186.c1979
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra194.c292
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra20.c18
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra210.c9
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra234.c1960
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra30.c7
-rw-r--r--drivers/pinctrl/ti/pinctrl-ti-iodelay.c136
-rw-r--r--drivers/pinctrl/uniphier/Kconfig2
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-core.c10
-rw-r--r--drivers/pinctrl/vt8500/pinctrl-wmt.c22
-rw-r--r--drivers/platform/Kconfig12
-rw-r--r--drivers/platform/Makefile5
-rw-r--r--drivers/platform/arm64/Kconfig93
-rw-r--r--drivers/platform/arm64/Makefile11
-rw-r--r--drivers/platform/arm64/acer-aspire1-ec.c562
-rw-r--r--drivers/platform/arm64/huawei-gaokun-ec.c827
-rw-r--r--drivers/platform/arm64/lenovo-thinkpad-t14s.c662
-rw-r--r--drivers/platform/arm64/lenovo-yoga-c630.c257
-rw-r--r--drivers/platform/chrome/Kconfig65
-rw-r--r--drivers/platform/chrome/Makefile19
-rw-r--r--drivers/platform/chrome/chromeos_acpi.c33
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c32
-rw-r--r--drivers/platform/chrome/chromeos_of_hw_prober.c187
-rw-r--r--drivers/platform/chrome/chromeos_privacy_screen.c3
-rw-r--r--drivers/platform/chrome/chromeos_pstore.c7
-rw-r--r--drivers/platform/chrome/cros_ec.c229
-rw-r--r--drivers/platform/chrome/cros_ec.h9
-rw-r--r--drivers/platform/chrome/cros_ec_chardev.c90
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c148
-rw-r--r--drivers/platform/chrome/cros_ec_i2c.c17
-rw-r--r--drivers/platform/chrome/cros_ec_ishtp.c81
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c47
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c502
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.c91
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.h19
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c239
-rw-r--r--drivers/platform/chrome/cros_ec_proto_test.c97
-rw-r--r--drivers/platform/chrome/cros_ec_proto_test_util.c128
-rw-r--r--drivers/platform/chrome/cros_ec_proto_test_util.h51
-rw-r--r--drivers/platform/chrome/cros_ec_rpmsg.c6
-rw-r--r--drivers/platform/chrome/cros_ec_sensorhub.c32
-rw-r--r--drivers/platform/chrome/cros_ec_sensorhub_ring.c85
-rw-r--r--drivers/platform/chrome/cros_ec_spi.c26
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c124
-rw-r--r--drivers/platform/chrome/cros_ec_trace.c10
-rw-r--r--drivers/platform/chrome/cros_ec_typec.c459
-rw-r--r--drivers/platform/chrome/cros_ec_typec.h87
-rw-r--r--drivers/platform/chrome/cros_ec_uart.c357
-rw-r--r--drivers/platform/chrome/cros_ec_vbc.c33
-rw-r--r--drivers/platform/chrome/cros_hps_i2c.c160
-rw-r--r--drivers/platform/chrome/cros_kbd_led_backlight.c106
-rw-r--r--drivers/platform/chrome/cros_kunit_util.c130
-rw-r--r--drivers/platform/chrome/cros_kunit_util.h48
-rw-r--r--drivers/platform/chrome/cros_typec_altmode.c373
-rw-r--r--drivers/platform/chrome/cros_typec_altmode.h51
-rw-r--r--drivers/platform/chrome/cros_typec_switch.c328
-rw-r--r--drivers/platform/chrome/cros_typec_vdm.c148
-rw-r--r--drivers/platform/chrome/cros_typec_vdm.h13
-rw-r--r--drivers/platform/chrome/cros_usbpd_logger.c18
-rw-r--r--drivers/platform/chrome/cros_usbpd_notify.c40
-rw-r--r--drivers/platform/chrome/wilco_ec/Kconfig1
-rw-r--r--drivers/platform/chrome/wilco_ec/core.c17
-rw-r--r--drivers/platform/chrome/wilco_ec/debugfs.c16
-rw-r--r--drivers/platform/chrome/wilco_ec/event.c14
-rw-r--r--drivers/platform/chrome/wilco_ec/mailbox.c22
-rw-r--r--drivers/platform/chrome/wilco_ec/properties.c2
-rw-r--r--drivers/platform/chrome/wilco_ec/sysfs.c5
-rw-r--r--drivers/platform/chrome/wilco_ec/telemetry.c23
-rw-r--r--drivers/platform/cznic/Kconfig98
-rw-r--r--drivers/platform/cznic/Makefile11
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-base.c419
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-gpio.c1123
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-keyctl.c162
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c260
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-trng.c94
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-watchdog.c130
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu.h163
-rw-r--r--drivers/platform/cznic/turris-signing-key.c193
-rw-r--r--drivers/platform/goldfish/Kconfig1
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.c7
-rw-r--r--drivers/platform/loongarch/Kconfig31
-rw-r--r--drivers/platform/loongarch/Makefile1
-rw-r--r--drivers/platform/loongarch/loongson-laptop.c635
-rw-r--r--drivers/platform/mellanox/Kconfig40
-rw-r--r--drivers/platform/mellanox/Makefile2
-rw-r--r--drivers/platform/mellanox/mlx-platform.c8177
-rw-r--r--drivers/platform/mellanox/mlxbf-bootctl.c803
-rw-r--r--drivers/platform/mellanox/mlxbf-bootctl.h38
-rw-r--r--drivers/platform/mellanox/mlxbf-pmc.c1220
-rw-r--r--drivers/platform/mellanox/mlxbf-tmfifo-regs.h10
-rw-r--r--drivers/platform/mellanox/mlxbf-tmfifo.c283
-rw-r--r--drivers/platform/mellanox/mlxreg-dpu.c613
-rw-r--r--drivers/platform/mellanox/mlxreg-hotplug.c59
-rw-r--r--drivers/platform/mellanox/mlxreg-io.c7
-rw-r--r--drivers/platform/mellanox/mlxreg-lc.c30
-rw-r--r--drivers/platform/mellanox/nvsw-sn2201.c135
-rw-r--r--drivers/platform/mips/Kconfig1
-rw-r--r--drivers/platform/mips/cpu_hwmon.c3
-rw-r--r--drivers/platform/mips/rs780e-acpi.c12
-rw-r--r--drivers/platform/olpc/olpc-ec.c3
-rw-r--r--drivers/platform/olpc/olpc-xo175-ec.c5
-rw-r--r--drivers/platform/raspberrypi/Kconfig52
-rw-r--r--drivers/platform/raspberrypi/Makefile15
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/TESTING125
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/TODO4
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/vchiq_arm.c1477
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/vchiq_bus.c112
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/vchiq_core.c4013
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/vchiq_debugfs.c157
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/vchiq_dev.c (renamed from drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c)237
-rw-r--r--drivers/platform/raspberrypi/vchiq-interface/vchiq_ioctl.h (renamed from drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h)10
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/Kconfig (renamed from drivers/staging/vc04_services/vchiq-mmal/Kconfig)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/Makefile4
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-common.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-common.h)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-encodings.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-msg-common.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-msg-common.h)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-msg-format.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-msg-format.h)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-msg-port.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-msg-port.h)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-msg.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h)2
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-parameters.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h)0
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-vchiq.c (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c)62
-rw-r--r--drivers/platform/raspberrypi/vchiq-mmal/mmal-vchiq.h (renamed from drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h)40
-rw-r--r--drivers/platform/surface/Kconfig2
-rw-r--r--drivers/platform/surface/aggregator/Kconfig4
-rw-r--r--drivers/platform/surface/aggregator/bus.c25
-rw-r--r--drivers/platform/surface/aggregator/controller.c120
-rw-r--r--drivers/platform/surface/aggregator/controller.h4
-rw-r--r--drivers/platform/surface/aggregator/core.c123
-rw-r--r--drivers/platform/surface/aggregator/ssh_msgb.h6
-rw-r--r--drivers/platform/surface/aggregator/ssh_packet_layer.c32
-rw-r--r--drivers/platform/surface/aggregator/ssh_packet_layer.h2
-rw-r--r--drivers/platform/surface/aggregator/ssh_parser.c2
-rw-r--r--drivers/platform/surface/aggregator/ssh_request_layer.c19
-rw-r--r--drivers/platform/surface/aggregator/trace.h75
-rw-r--r--drivers/platform/surface/surface3-wmi.c3
-rw-r--r--drivers/platform/surface/surface3_power.c10
-rw-r--r--drivers/platform/surface/surface_acpi_notify.c39
-rw-r--r--drivers/platform/surface/surface_aggregator_cdev.c10
-rw-r--r--drivers/platform/surface/surface_aggregator_hub.c8
-rw-r--r--drivers/platform/surface/surface_aggregator_registry.c196
-rw-r--r--drivers/platform/surface/surface_aggregator_tabletsw.c204
-rw-r--r--drivers/platform/surface/surface_dtx.c24
-rw-r--r--drivers/platform/surface/surface_gpe.c4
-rw-r--r--drivers/platform/surface/surface_hotplug.c17
-rw-r--r--drivers/platform/surface/surface_platform_profile.c131
-rw-r--r--drivers/platform/surface/surfacepro3_button.c3
-rw-r--r--drivers/platform/wmi/Kconfig34
-rw-r--r--drivers/platform/wmi/Makefile8
-rw-r--r--drivers/platform/wmi/core.c1429
-rw-r--r--drivers/platform/x86/Kconfig506
-rw-r--r--drivers/platform/x86/Makefile65
-rw-r--r--drivers/platform/x86/acer-wmi.c960
-rw-r--r--drivers/platform/x86/acerhdf.c137
-rw-r--r--drivers/platform/x86/adv_swbutton.c4
-rw-r--r--drivers/platform/x86/amd/Kconfig53
-rw-r--r--drivers/platform/x86/amd/Makefile12
-rw-r--r--drivers/platform/x86/amd/amd_isp4.c419
-rw-r--r--drivers/platform/x86/amd/hfi/Kconfig18
-rw-r--r--drivers/platform/x86/amd/hfi/Makefile7
-rw-r--r--drivers/platform/x86/amd/hfi/hfi.c546
-rw-r--r--drivers/platform/x86/amd/hsmp.c425
-rw-r--r--drivers/platform/x86/amd/hsmp/Kconfig49
-rw-r--r--drivers/platform/x86/amd/hsmp/Makefile13
-rw-r--r--drivers/platform/x86/amd/hsmp/acpi.c647
-rw-r--r--drivers/platform/x86/amd/hsmp/hsmp.c466
-rw-r--r--drivers/platform/x86/amd/hsmp/hsmp.h74
-rw-r--r--drivers/platform/x86/amd/hsmp/hwmon.c121
-rw-r--r--drivers/platform/x86/amd/hsmp/plat.c350
-rw-r--r--drivers/platform/x86/amd/pmc.c945
-rw-r--r--drivers/platform/x86/amd/pmc/Kconfig35
-rw-r--r--drivers/platform/x86/amd/pmc/Makefile9
-rw-r--r--drivers/platform/x86/amd/pmc/mp1_stb.c332
-rw-r--r--drivers/platform/x86/amd/pmc/mp2_stb.c280
-rw-r--r--drivers/platform/x86/amd/pmc/pmc-quirks.c368
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.c847
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.h174
-rw-r--r--drivers/platform/x86/amd/pmf/Kconfig32
-rw-r--r--drivers/platform/x86/amd/pmf/Makefile10
-rw-r--r--drivers/platform/x86/amd/pmf/acpi.c606
-rw-r--r--drivers/platform/x86/amd/pmf/auto-mode.c441
-rw-r--r--drivers/platform/x86/amd/pmf/cnqf.c467
-rw-r--r--drivers/platform/x86/amd/pmf/core.c523
-rw-r--r--drivers/platform/x86/amd/pmf/pmf.h898
-rw-r--r--drivers/platform/x86/amd/pmf/spc.c343
-rw-r--r--drivers/platform/x86/amd/pmf/sps.c444
-rw-r--r--drivers/platform/x86/amd/pmf/tee-if.c629
-rw-r--r--drivers/platform/x86/amd/wbrf.c317
-rw-r--r--drivers/platform/x86/amd/x3d_vcache.c176
-rw-r--r--drivers/platform/x86/amilo-rfkill.c13
-rw-r--r--drivers/platform/x86/apple-gmux.c490
-rw-r--r--drivers/platform/x86/asus-armoury.c1161
-rw-r--r--drivers/platform/x86/asus-armoury.h1541
-rw-r--r--drivers/platform/x86/asus-laptop.c69
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c226
-rw-r--r--drivers/platform/x86/asus-tf103c-dock.c24
-rw-r--r--drivers/platform/x86/asus-wireless.c15
-rw-r--r--drivers/platform/x86/asus-wmi.c2319
-rw-r--r--drivers/platform/x86/asus-wmi.h24
-rw-r--r--drivers/platform/x86/ayaneo-ec.c593
-rw-r--r--drivers/platform/x86/barco-p50-gpio.c116
-rw-r--r--drivers/platform/x86/classmate-laptop.c38
-rw-r--r--drivers/platform/x86/compal-laptop.c211
-rw-r--r--drivers/platform/x86/dasharo-acpi.c360
-rw-r--r--drivers/platform/x86/dell/Kconfig84
-rw-r--r--drivers/platform/x86/dell/Makefile41
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-base.c491
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-legacy.c95
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-wmax.c1652
-rw-r--r--drivers/platform/x86/dell/alienware-wmi.c853
-rw-r--r--drivers/platform/x86/dell/alienware-wmi.h117
-rw-r--r--drivers/platform/x86/dell/dcdbas.c27
-rw-r--r--drivers/platform/x86/dell/dcdbas.h8
-rw-r--r--drivers/platform/x86/dell/dell-laptop.c457
-rw-r--r--drivers/platform/x86/dell/dell-lis3lv02d.c259
-rw-r--r--drivers/platform/x86/dell/dell-pc.c302
-rw-r--r--drivers/platform/x86/dell/dell-rbtn.c20
-rw-r--r--drivers/platform/x86/dell/dell-smbios-base.c163
-rw-r--r--drivers/platform/x86/dell/dell-smbios-smm.c3
-rw-r--r--drivers/platform/x86/dell/dell-smbios-wmi.c177
-rw-r--r--drivers/platform/x86/dell/dell-smbios.h18
-rw-r--r--drivers/platform/x86/dell/dell-smo8800-ids.h27
-rw-r--r--drivers/platform/x86/dell/dell-smo8800.c24
-rw-r--r--drivers/platform/x86/dell/dell-uart-backlight.c407
-rw-r--r--drivers/platform/x86/dell/dell-wmi-aio.c13
-rw-r--r--drivers/platform/x86/dell/dell-wmi-base.c33
-rw-r--r--drivers/platform/x86/dell/dell-wmi-ddv.c1123
-rw-r--r--drivers/platform/x86/dell/dell-wmi-privacy.c58
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/Makefile2
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h5
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c5
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/int-attributes.c5
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c7
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/string-attributes.c5
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/sysman.c52
-rw-r--r--drivers/platform/x86/dell/dell_rbu.c44
-rw-r--r--drivers/platform/x86/eeepc-laptop.c24
-rw-r--r--drivers/platform/x86/eeepc-wmi.c29
-rw-r--r--drivers/platform/x86/firmware_attributes_class.c43
-rw-r--r--drivers/platform/x86/firmware_attributes_class.h5
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c195
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c3
-rw-r--r--drivers/platform/x86/gigabyte-wmi.c46
-rw-r--r--drivers/platform/x86/gpd-pocket-fan.c4
-rw-r--r--drivers/platform/x86/hdaps.c2
-rw-r--r--drivers/platform/x86/hp-wmi.c1544
-rw-r--r--drivers/platform/x86/hp/Kconfig81
-rw-r--r--drivers/platform/x86/hp/Makefile11
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/Makefile11
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c312
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.c1054
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.h487
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c449
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/int-attributes.c415
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c433
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c541
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c380
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/string-attributes.c391
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/surestart-attributes.c132
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c2303
-rw-r--r--drivers/platform/x86/hp/hp_accel.c (renamed from drivers/platform/x86/hp_accel.c)9
-rw-r--r--drivers/platform/x86/hp/tc1100-wmi.c (renamed from drivers/platform/x86/tc1100-wmi.c)6
-rw-r--r--drivers/platform/x86/huawei-wmi.c86
-rw-r--r--drivers/platform/x86/ibm_rtl.c21
-rw-r--r--drivers/platform/x86/ideapad-laptop.c1758
-rw-r--r--drivers/platform/x86/inspur_platform_profile.c220
-rw-r--r--drivers/platform/x86/intel/Kconfig58
-rw-r--r--drivers/platform/x86/intel/Makefile63
-rw-r--r--drivers/platform/x86/intel/bxtwc_tmu.c25
-rw-r--r--drivers/platform/x86/intel/bytcrc_pwrsrc.c255
-rw-r--r--drivers/platform/x86/intel/chtdc_ti_pwrbtn.c3
-rw-r--r--drivers/platform/x86/intel/chtwc_int33fe.c43
-rw-r--r--drivers/platform/x86/intel/ehl_pse_io.c86
-rw-r--r--drivers/platform/x86/intel/hid.c130
-rw-r--r--drivers/platform/x86/intel/ifs/Kconfig4
-rw-r--r--drivers/platform/x86/intel/ifs/Makefile2
-rw-r--r--drivers/platform/x86/intel/ifs/core.c131
-rw-r--r--drivers/platform/x86/intel/ifs/ifs.h245
-rw-r--r--drivers/platform/x86/intel/ifs/load.c396
-rw-r--r--drivers/platform/x86/intel/ifs/runtest.c479
-rw-r--r--drivers/platform/x86/intel/ifs/sysfs.c62
-rw-r--r--drivers/platform/x86/intel/int0002_vgpio.c31
-rw-r--r--drivers/platform/x86/intel/int1092/intel_sar.c18
-rw-r--r--drivers/platform/x86/intel/int3472/Kconfig1
-rw-r--r--drivers/platform/x86/intel/int3472/Makefile10
-rw-r--r--drivers/platform/x86/intel/int3472/clk_and_regulator.c185
-rw-r--r--drivers/platform/x86/intel/int3472/common.c14
-rw-r--r--drivers/platform/x86/intel/int3472/common.h122
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c440
-rw-r--r--drivers/platform/x86/intel/int3472/discrete_quirks.c21
-rw-r--r--drivers/platform/x86/intel/int3472/led.c60
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470.c88
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470.h3
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470_board_data.c183
-rw-r--r--drivers/platform/x86/intel/mrfld_pwrbtn.c3
-rw-r--r--drivers/platform/x86/intel/oaktrail.c13
-rw-r--r--drivers/platform/x86/intel/plr_tpmi.c355
-rw-r--r--drivers/platform/x86/intel/pmc/Kconfig5
-rw-r--r--drivers/platform/x86/intel/pmc/Makefile7
-rw-r--r--drivers/platform/x86/intel/pmc/adl.c318
-rw-r--r--drivers/platform/x86/intel/pmc/arl.c745
-rw-r--r--drivers/platform/x86/intel/pmc/cnp.c283
-rw-r--r--drivers/platform/x86/intel/pmc/core.c2472
-rw-r--r--drivers/platform/x86/intel/pmc/core.h276
-rw-r--r--drivers/platform/x86/intel/pmc/icl.c55
-rw-r--r--drivers/platform/x86/intel/pmc/lnl.c582
-rw-r--r--drivers/platform/x86/intel/pmc/mtl.c1006
-rw-r--r--drivers/platform/x86/intel/pmc/pltdrv.c26
-rw-r--r--drivers/platform/x86/intel/pmc/ptl.c580
-rw-r--r--drivers/platform/x86/intel/pmc/spt.c160
-rw-r--r--drivers/platform/x86/intel/pmc/ssram_telemetry.c208
-rw-r--r--drivers/platform/x86/intel/pmc/ssram_telemetry.h24
-rw-r--r--drivers/platform/x86/intel/pmc/tgl.c312
-rw-r--r--drivers/platform/x86/intel/pmc/wcl.c504
-rw-r--r--drivers/platform/x86/intel/pmt/Kconfig28
-rw-r--r--drivers/platform/x86/intel/pmt/Makefile4
-rw-r--r--drivers/platform/x86/intel/pmt/class.c153
-rw-r--r--drivers/platform/x86/intel/pmt/class.h48
-rw-r--r--drivers/platform/x86/intel/pmt/crashlog.c464
-rw-r--r--drivers/platform/x86/intel/pmt/discovery-kunit.c116
-rw-r--r--drivers/platform/x86/intel/pmt/discovery.c635
-rw-r--r--drivers/platform/x86/intel/pmt/features.c205
-rw-r--r--drivers/platform/x86/intel/pmt/telemetry.c294
-rw-r--r--drivers/platform/x86/intel/pmt/telemetry.h126
-rw-r--r--drivers/platform/x86/intel/punit_ipc.c41
-rw-r--r--drivers/platform/x86/intel/rst.c6
-rw-r--r--drivers/platform/x86/intel/sdsi.c267
-rw-r--r--drivers/platform/x86/intel/smartconnect.c2
-rw-r--r--drivers/platform/x86/intel/speed_select_if/Kconfig4
-rw-r--r--drivers/platform/x86/intel/speed_select_if/Makefile2
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_common.c201
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_common.h12
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_mbox_msr.c19
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c25
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_tpmi.c72
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c1833
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h18
-rw-r--r--drivers/platform/x86/intel/telemetry/core.c177
-rw-r--r--drivers/platform/x86/intel/telemetry/debugfs.c4
-rw-r--r--drivers/platform/x86/intel/telemetry/pltdrv.c238
-rw-r--r--drivers/platform/x86/intel/tpmi_power_domains.c265
-rw-r--r--drivers/platform/x86/intel/tpmi_power_domains.h19
-rw-r--r--drivers/platform/x86/intel/turbo_max_3.c9
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/Kconfig4
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/Makefile2
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c280
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h83
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c765
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c120
-rw-r--r--drivers/platform/x86/intel/vbtn.c46
-rw-r--r--drivers/platform/x86/intel/vsec.c595
-rw-r--r--drivers/platform/x86/intel/vsec.h52
-rw-r--r--drivers/platform/x86/intel/vsec_tpmi.c861
-rw-r--r--drivers/platform/x86/intel/wmi/sbl-fw-update.c18
-rw-r--r--drivers/platform/x86/intel/wmi/thunderbolt.c28
-rw-r--r--drivers/platform/x86/intel_ips.c110
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c205
-rw-r--r--drivers/platform/x86/intel_scu_ipcutil.c4
-rw-r--r--drivers/platform/x86/intel_scu_pcidrv.c4
-rw-r--r--drivers/platform/x86/intel_scu_pltdrv.c2
-rw-r--r--drivers/platform/x86/intel_scu_wdt.c6
-rw-r--r--drivers/platform/x86/lenovo-yogabook-wmi.c408
-rw-r--r--drivers/platform/x86/lenovo/Kconfig276
-rw-r--r--drivers/platform/x86/lenovo/Makefile28
-rw-r--r--drivers/platform/x86/lenovo/ideapad-laptop.c2580
-rw-r--r--drivers/platform/x86/lenovo/ideapad-laptop.h22
-rw-r--r--drivers/platform/x86/lenovo/think-lmi.c1855
-rw-r--r--drivers/platform/x86/lenovo/think-lmi.h140
-rw-r--r--drivers/platform/x86/lenovo/thinkpad_acpi.c (renamed from drivers/platform/x86/thinkpad_acpi.c)2133
-rw-r--r--drivers/platform/x86/lenovo/wmi-camera.c146
-rw-r--r--drivers/platform/x86/lenovo/wmi-capdata01.c302
-rw-r--r--drivers/platform/x86/lenovo/wmi-capdata01.h25
-rw-r--r--drivers/platform/x86/lenovo/wmi-events.c196
-rw-r--r--drivers/platform/x86/lenovo/wmi-events.h20
-rw-r--r--drivers/platform/x86/lenovo/wmi-gamezone.c414
-rw-r--r--drivers/platform/x86/lenovo/wmi-gamezone.h20
-rw-r--r--drivers/platform/x86/lenovo/wmi-helpers.c74
-rw-r--r--drivers/platform/x86/lenovo/wmi-helpers.h20
-rw-r--r--drivers/platform/x86/lenovo/wmi-hotkey-utilities.c224
-rw-r--r--drivers/platform/x86/lenovo/wmi-other.c665
-rw-r--r--drivers/platform/x86/lenovo/wmi-other.h16
-rw-r--r--drivers/platform/x86/lenovo/ymc.c165
-rw-r--r--drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c333
-rw-r--r--drivers/platform/x86/lenovo/yogabook.c573
-rw-r--r--drivers/platform/x86/lg-laptop.c288
-rw-r--r--drivers/platform/x86/meegopad_anx7428.c150
-rw-r--r--drivers/platform/x86/meraki-mx100.c404
-rw-r--r--drivers/platform/x86/mlx-platform.c5385
-rw-r--r--drivers/platform/x86/msi-ec.c1363
-rw-r--r--drivers/platform/x86/msi-ec.h122
-rw-r--r--drivers/platform/x86/msi-laptop.c140
-rw-r--r--drivers/platform/x86/msi-wmi-platform.c494
-rw-r--r--drivers/platform/x86/msi-wmi.c20
-rw-r--r--drivers/platform/x86/mxm-wmi.c8
-rw-r--r--drivers/platform/x86/nvidia-wmi-ec-backlight.c84
-rw-r--r--drivers/platform/x86/oxpec.c973
-rw-r--r--drivers/platform/x86/p2sb.c244
-rw-r--r--drivers/platform/x86/panasonic-laptop.c102
-rw-r--r--drivers/platform/x86/pcengines-apuv2.c193
-rw-r--r--drivers/platform/x86/peaq-wmi.c128
-rw-r--r--drivers/platform/x86/pmc_atom.c121
-rw-r--r--drivers/platform/x86/portwell-ec.c460
-rw-r--r--drivers/platform/x86/quickstart.c237
-rw-r--r--drivers/platform/x86/redmi-wmi.c130
-rw-r--r--drivers/platform/x86/samsung-galaxybook.c1426
-rw-r--r--drivers/platform/x86/samsung-laptop.c216
-rw-r--r--drivers/platform/x86/samsung-q10.c4
-rw-r--r--drivers/platform/x86/sel3350-platform.c249
-rw-r--r--drivers/platform/x86/serdev_helpers.h88
-rw-r--r--drivers/platform/x86/serial-multi-instantiate.c123
-rw-r--r--drivers/platform/x86/siemens/Kconfig64
-rw-r--r--drivers/platform/x86/siemens/Makefile11
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c52
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c52
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c88
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt.c253
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt.h20
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc.c237
-rw-r--r--drivers/platform/x86/silicom-platform.c996
-rw-r--r--drivers/platform/x86/simatic-ipc.c141
-rw-r--r--drivers/platform/x86/sony-laptop.c226
-rw-r--r--drivers/platform/x86/system76_acpi.c84
-rw-r--r--drivers/platform/x86/think-lmi.c1555
-rw-r--r--drivers/platform/x86/think-lmi.h103
-rw-r--r--drivers/platform/x86/topstar-laptop.c9
-rw-r--r--drivers/platform/x86/toshiba-wmi.c15
-rw-r--r--drivers/platform/x86/toshiba_acpi.c397
-rw-r--r--drivers/platform/x86/toshiba_bluetooth.c7
-rw-r--r--drivers/platform/x86/toshiba_haps.c5
-rw-r--r--drivers/platform/x86/touchscreen_dmi.c437
-rw-r--r--drivers/platform/x86/tuxedo/Kconfig8
-rw-r--r--drivers/platform/x86/tuxedo/Makefile8
-rw-r--r--drivers/platform/x86/tuxedo/nb04/Kconfig17
-rw-r--r--drivers/platform/x86/tuxedo/nb04/Makefile10
-rw-r--r--drivers/platform/x86/tuxedo/nb04/wmi_ab.c923
-rw-r--r--drivers/platform/x86/tuxedo/nb04/wmi_util.c91
-rw-r--r--drivers/platform/x86/tuxedo/nb04/wmi_util.h109
-rw-r--r--drivers/platform/x86/uniwill/Kconfig38
-rw-r--r--drivers/platform/x86/uniwill/Makefile8
-rw-r--r--drivers/platform/x86/uniwill/uniwill-acpi.c1912
-rw-r--r--drivers/platform/x86/uniwill/uniwill-wmi.c92
-rw-r--r--drivers/platform/x86/uniwill/uniwill-wmi.h129
-rw-r--r--drivers/platform/x86/uv_sysfs.c44
-rw-r--r--drivers/platform/x86/winmate-fm07-keys.c2
-rw-r--r--drivers/platform/x86/wireless-hotkey.c67
-rw-r--r--drivers/platform/x86/wmi-bmof.c86
-rw-r--r--drivers/platform/x86/wmi.c1492
-rw-r--r--drivers/platform/x86/x86-android-tablets.c1520
-rw-r--r--drivers/platform/x86/x86-android-tablets/Kconfig26
-rw-r--r--drivers/platform/x86/x86-android-tablets/Makefile9
-rw-r--r--drivers/platform/x86/x86-android-tablets/acer.c247
-rw-r--r--drivers/platform/x86/x86-android-tablets/asus.c319
-rw-r--r--drivers/platform/x86/x86-android-tablets/core.c565
-rw-r--r--drivers/platform/x86/x86-android-tablets/dmi.c234
-rw-r--r--drivers/platform/x86/x86-android-tablets/lenovo.c1079
-rw-r--r--drivers/platform/x86/x86-android-tablets/other.c883
-rw-r--r--drivers/platform/x86/x86-android-tablets/shared-psy-info.c186
-rw-r--r--drivers/platform/x86/x86-android-tablets/shared-psy-info.h35
-rw-r--r--drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c261
-rw-r--r--drivers/platform/x86/x86-android-tablets/x86-android-tablets.h137
-rw-r--r--drivers/platform/x86/xiaomi-wmi.c22
-rw-r--r--drivers/platform/x86/xo1-rfkill.c4
-rw-r--r--drivers/platform/x86/xo15-ebook.c22
-rw-r--r--drivers/pmdomain/Kconfig24
-rw-r--r--drivers/pmdomain/Makefile21
-rw-r--r--drivers/pmdomain/actions/Kconfig (renamed from drivers/soc/actions/Kconfig)0
-rw-r--r--drivers/pmdomain/actions/Makefile3
-rw-r--r--drivers/pmdomain/actions/owl-sps-helper.c (renamed from drivers/soc/actions/owl-sps-helper.c)0
-rw-r--r--drivers/pmdomain/actions/owl-sps.c (renamed from drivers/soc/actions/owl-sps.c)16
-rw-r--r--drivers/pmdomain/amlogic/Kconfig28
-rw-r--r--drivers/pmdomain/amlogic/Makefile3
-rw-r--r--drivers/pmdomain/amlogic/meson-ee-pwrc.c (renamed from drivers/soc/amlogic/meson-ee-pwrc.c)112
-rw-r--r--drivers/pmdomain/amlogic/meson-secure-pwrc.c508
-rw-r--r--drivers/pmdomain/apple/Kconfig17
-rw-r--r--drivers/pmdomain/apple/Makefile2
-rw-r--r--drivers/pmdomain/apple/pmgr-pwrstate.c327
-rw-r--r--drivers/pmdomain/arm/Kconfig37
-rw-r--r--drivers/pmdomain/arm/Makefile5
-rw-r--r--drivers/pmdomain/arm/scmi_perf_domain.c188
-rw-r--r--drivers/pmdomain/arm/scmi_pm_domain.c158
-rw-r--r--drivers/pmdomain/arm/scpi_pm_domain.c (renamed from drivers/firmware/scpi_pm_domain.c)3
-rw-r--r--drivers/pmdomain/bcm/Kconfig42
-rw-r--r--drivers/pmdomain/bcm/Makefile5
-rw-r--r--drivers/pmdomain/bcm/bcm-pmb.c (renamed from drivers/soc/bcm/bcm63xx/bcm-pmb.c)1
-rw-r--r--drivers/pmdomain/bcm/bcm2835-power.c (renamed from drivers/soc/bcm/bcm2835-power.c)43
-rw-r--r--drivers/pmdomain/bcm/bcm63xx-power.c (renamed from drivers/soc/bcm/bcm63xx/bcm63xx-power.c)1
-rw-r--r--drivers/pmdomain/bcm/raspberrypi-power.c (renamed from drivers/soc/bcm/raspberrypi-power.c)46
-rw-r--r--drivers/pmdomain/core.c3946
-rw-r--r--drivers/pmdomain/governor.c471
-rw-r--r--drivers/pmdomain/imx/Kconfig29
-rw-r--r--drivers/pmdomain/imx/Makefile8
-rw-r--r--drivers/pmdomain/imx/gpc.c (renamed from drivers/soc/imx/gpc.c)51
-rw-r--r--drivers/pmdomain/imx/gpcv2.c (renamed from drivers/soc/imx/gpcv2.c)42
-rw-r--r--drivers/pmdomain/imx/imx8m-blk-ctrl.c (renamed from drivers/soc/imx/imx8m-blk-ctrl.c)170
-rw-r--r--drivers/pmdomain/imx/imx8mp-blk-ctrl.c (renamed from drivers/soc/imx/imx8mp-blk-ctrl.c)237
-rw-r--r--drivers/pmdomain/imx/imx93-blk-ctrl.c470
-rw-r--r--drivers/pmdomain/imx/imx93-pd.c171
-rw-r--r--drivers/pmdomain/imx/scu-pd.c546
-rw-r--r--drivers/pmdomain/marvell/Kconfig18
-rw-r--r--drivers/pmdomain/marvell/Makefile3
-rw-r--r--drivers/pmdomain/marvell/pxa1908-power-controller.c274
-rw-r--r--drivers/pmdomain/mediatek/Kconfig58
-rw-r--r--drivers/pmdomain/mediatek/Makefile12
-rw-r--r--drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c144
-rw-r--r--drivers/pmdomain/mediatek/mt6735-pm-domains.h96
-rw-r--r--drivers/pmdomain/mediatek/mt6795-pm-domains.h (renamed from drivers/soc/mediatek/mt6795-pm-domains.h)21
-rw-r--r--drivers/pmdomain/mediatek/mt6893-pm-domains.h585
-rw-r--r--drivers/pmdomain/mediatek/mt8167-pm-domains.h (renamed from drivers/soc/mediatek/mt8167-pm-domains.h)25
-rw-r--r--drivers/pmdomain/mediatek/mt8173-pm-domains.h (renamed from drivers/soc/mediatek/mt8173-pm-domains.h)21
-rw-r--r--drivers/pmdomain/mediatek/mt8183-pm-domains.h300
-rw-r--r--drivers/pmdomain/mediatek/mt8186-pm-domains.h371
-rw-r--r--drivers/pmdomain/mediatek/mt8188-pm-domains.h696
-rw-r--r--drivers/pmdomain/mediatek/mt8192-pm-domains.h (renamed from drivers/soc/mediatek/mt8192-pm-domains.h)117
-rw-r--r--drivers/pmdomain/mediatek/mt8195-pm-domains.h (renamed from drivers/soc/mediatek/mt8195-pm-domains.h)205
-rw-r--r--drivers/pmdomain/mediatek/mt8196-pm-domains.h625
-rw-r--r--drivers/pmdomain/mediatek/mt8365-pm-domains.h201
-rw-r--r--drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c1044
-rw-r--r--drivers/pmdomain/mediatek/mtk-pm-domains.c1281
-rw-r--r--drivers/pmdomain/mediatek/mtk-pm-domains.h209
-rw-r--r--drivers/pmdomain/mediatek/mtk-scpsys.c (renamed from drivers/soc/mediatek/mtk-scpsys.c)9
-rw-r--r--drivers/pmdomain/qcom/Kconfig41
-rw-r--r--drivers/pmdomain/qcom/Makefile4
-rw-r--r--drivers/pmdomain/qcom/cpr.c (renamed from drivers/soc/qcom/cpr.c)110
-rw-r--r--drivers/pmdomain/qcom/rpmhpd.c1139
-rw-r--r--drivers/pmdomain/qcom/rpmpd.c1184
-rw-r--r--drivers/pmdomain/renesas/Kconfig117
-rw-r--r--drivers/pmdomain/renesas/Makefile31
-rw-r--r--drivers/pmdomain/renesas/r8a7742-sysc.c (renamed from drivers/soc/renesas/r8a7742-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7743-sysc.c (renamed from drivers/soc/renesas/r8a7743-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7745-sysc.c (renamed from drivers/soc/renesas/r8a7745-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a77470-sysc.c (renamed from drivers/soc/renesas/r8a77470-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a774a1-sysc.c (renamed from drivers/soc/renesas/r8a774a1-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a774b1-sysc.c (renamed from drivers/soc/renesas/r8a774b1-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a774c0-sysc.c (renamed from drivers/soc/renesas/r8a774c0-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a774e1-sysc.c (renamed from drivers/soc/renesas/r8a774e1-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7779-sysc.c (renamed from drivers/soc/renesas/r8a7779-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7790-sysc.c (renamed from drivers/soc/renesas/r8a7790-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7791-sysc.c (renamed from drivers/soc/renesas/r8a7791-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7792-sysc.c (renamed from drivers/soc/renesas/r8a7792-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7794-sysc.c (renamed from drivers/soc/renesas/r8a7794-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a7795-sysc.c (renamed from drivers/soc/renesas/r8a7795-sysc.c)10
-rw-r--r--drivers/pmdomain/renesas/r8a77960-sysc.c49
-rw-r--r--drivers/pmdomain/renesas/r8a77961-sysc.c47
-rw-r--r--drivers/pmdomain/renesas/r8a77965-sysc.c (renamed from drivers/soc/renesas/r8a77965-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a77970-sysc.c (renamed from drivers/soc/renesas/r8a77970-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a77980-sysc.c (renamed from drivers/soc/renesas/r8a77980-sysc.c)3
-rw-r--r--drivers/pmdomain/renesas/r8a77990-sysc.c (renamed from drivers/soc/renesas/r8a77990-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a77995-sysc.c (renamed from drivers/soc/renesas/r8a77995-sysc.c)0
-rw-r--r--drivers/pmdomain/renesas/r8a779a0-sysc.c (renamed from drivers/soc/renesas/r8a779a0-sysc.c)12
-rw-r--r--drivers/pmdomain/renesas/r8a779f0-sysc.c (renamed from drivers/soc/renesas/r8a779f0-sysc.c)12
-rw-r--r--drivers/pmdomain/renesas/r8a779g0-sysc.c (renamed from drivers/soc/renesas/r8a779g0-sysc.c)13
-rw-r--r--drivers/pmdomain/renesas/r8a779h0-sysc.c54
-rw-r--r--drivers/pmdomain/renesas/rcar-gen4-sysc.c (renamed from drivers/soc/renesas/rcar-gen4-sysc.c)25
-rw-r--r--drivers/pmdomain/renesas/rcar-gen4-sysc.h (renamed from drivers/soc/renesas/rcar-gen4-sysc.h)1
-rw-r--r--drivers/pmdomain/renesas/rcar-sysc.c (renamed from drivers/soc/renesas/rcar-sysc.c)93
-rw-r--r--drivers/pmdomain/renesas/rcar-sysc.h (renamed from drivers/soc/renesas/rcar-sysc.h)9
-rw-r--r--drivers/pmdomain/renesas/rmobile-sysc.c (renamed from drivers/soc/renesas/rmobile-sysc.c)47
-rw-r--r--drivers/pmdomain/rockchip/Kconfig19
-rw-r--r--drivers/pmdomain/rockchip/Makefile2
-rw-r--r--drivers/pmdomain/rockchip/pm-domains.c1650
-rw-r--r--drivers/pmdomain/samsung/Kconfig8
-rw-r--r--drivers/pmdomain/samsung/Makefile2
-rw-r--r--drivers/pmdomain/samsung/exynos-pm-domains.c175
-rw-r--r--drivers/pmdomain/st/Kconfig5
-rw-r--r--drivers/pmdomain/st/Makefile2
-rw-r--r--drivers/pmdomain/st/ste-ux500-pm-domain.c94
-rw-r--r--drivers/pmdomain/starfive/Kconfig12
-rw-r--r--drivers/pmdomain/starfive/Makefile2
-rw-r--r--drivers/pmdomain/starfive/jh71xx-pmu.c464
-rw-r--r--drivers/pmdomain/sunxi/Kconfig33
-rw-r--r--drivers/pmdomain/sunxi/Makefile4
-rw-r--r--drivers/pmdomain/sunxi/sun20i-ppu.c239
-rw-r--r--drivers/pmdomain/sunxi/sun50i-h6-prcm-ppu.c208
-rw-r--r--drivers/pmdomain/sunxi/sun55i-pck600.c234
-rw-r--r--drivers/pmdomain/tegra/Kconfig6
-rw-r--r--drivers/pmdomain/tegra/Makefile2
-rw-r--r--drivers/pmdomain/tegra/powergate-bpmp.c (renamed from drivers/soc/tegra/powergate-bpmp.c)5
-rw-r--r--drivers/pmdomain/thead/Kconfig13
-rw-r--r--drivers/pmdomain/thead/Makefile2
-rw-r--r--drivers/pmdomain/thead/th1520-pm-domains.c285
-rw-r--r--drivers/pmdomain/ti/Kconfig22
-rw-r--r--drivers/pmdomain/ti/Makefile3
-rw-r--r--drivers/pmdomain/ti/omap_prm.c (renamed from drivers/soc/ti/omap_prm.c)27
-rw-r--r--drivers/pmdomain/ti/ti_sci_pm_domains.c326
-rw-r--r--drivers/pmdomain/xilinx/Kconfig10
-rw-r--r--drivers/pmdomain/xilinx/Makefile2
-rw-r--r--drivers/pmdomain/xilinx/zynqmp-pm-domains.c304
-rw-r--r--drivers/pnp/base.h5
-rw-r--r--drivers/pnp/card.c32
-rw-r--r--drivers/pnp/core.c20
-rw-r--r--drivers/pnp/driver.c36
-rw-r--r--drivers/pnp/isapnp/Kconfig4
-rw-r--r--drivers/pnp/isapnp/core.c4
-rw-r--r--drivers/pnp/pnpacpi/core.c4
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c19
-rw-r--r--drivers/pnp/pnpbios/bioscalls.c2
-rw-r--r--drivers/pnp/pnpbios/pnpbios.h1
-rw-r--r--drivers/pnp/pnpbios/rsparser.c4
-rw-r--r--drivers/pnp/quirks.c31
-rw-r--r--drivers/power/Kconfig1
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/reset/Kconfig69
-rw-r--r--drivers/power/reset/Makefile7
-rw-r--r--drivers/power/reset/as3722-poweroff.c34
-rw-r--r--drivers/power/reset/at91-poweroff.c17
-rw-r--r--drivers/power/reset/at91-reset.c60
-rw-r--r--drivers/power/reset/at91-sama5d2_shdwc.c21
-rw-r--r--drivers/power/reset/atc260x-poweroff.c57
-rw-r--r--drivers/power/reset/axxia-reset.c23
-rw-r--r--drivers/power/reset/brcm-kona-reset.c19
-rw-r--r--drivers/power/reset/brcmstb-reboot.c59
-rw-r--r--drivers/power/reset/ep93xx-restart.c84
-rw-r--r--drivers/power/reset/gemini-poweroff.c20
-rw-r--r--drivers/power/reset/gpio-poweroff.c94
-rw-r--r--drivers/power/reset/gpio-restart.c40
-rw-r--r--drivers/power/reset/keystone-reset.c24
-rw-r--r--drivers/power/reset/ltc2952-poweroff.c12
-rw-r--r--drivers/power/reset/macsmc-reboot.c290
-rw-r--r--drivers/power/reset/msm-poweroff.c34
-rw-r--r--drivers/power/reset/mt6323-poweroff.c29
-rw-r--r--drivers/power/reset/nvmem-reboot-mode.c4
-rw-r--r--drivers/power/reset/ocelot-reset.c9
-rw-r--r--drivers/power/reset/odroid-go-ultra-poweroff.c178
-rw-r--r--drivers/power/reset/oxnas-restart.c233
-rw-r--r--drivers/power/reset/piix4-poweroff.c1
-rw-r--r--drivers/power/reset/pwr-mlxbf.c18
-rw-r--r--drivers/power/reset/qcom-pon.c51
-rw-r--r--drivers/power/reset/qnap-poweroff.c3
-rw-r--r--drivers/power/reset/reboot-mode.c25
-rw-r--r--drivers/power/reset/regulator-poweroff.c39
-rw-r--r--drivers/power/reset/restart-poweroff.c28
-rw-r--r--drivers/power/reset/rmobile-reset.c39
-rw-r--r--drivers/power/reset/sc27xx-poweroff.c10
-rw-r--r--drivers/power/reset/spacemit-p1-reboot.c88
-rw-r--r--drivers/power/reset/st-poweroff.c9
-rw-r--r--drivers/power/reset/syscon-poweroff.c84
-rw-r--r--drivers/power/reset/syscon-reboot.c106
-rw-r--r--drivers/power/reset/tdx-ec-poweroff.c150
-rw-r--r--drivers/power/reset/th1520-aon-reboot.c98
-rw-r--r--drivers/power/reset/tps65086-restart.c57
-rw-r--r--drivers/power/reset/vexpress-poweroff.c11
-rw-r--r--drivers/power/reset/xgene-reboot.c34
-rw-r--r--drivers/power/sequencing/Kconfig38
-rw-r--r--drivers/power/sequencing/Makefile7
-rw-r--r--drivers/power/sequencing/core.c1106
-rw-r--r--drivers/power/sequencing/pwrseq-qcom-wcn.c478
-rw-r--r--drivers/power/sequencing/pwrseq-thead-gpu.c249
-rw-r--r--drivers/power/supply/88pm860x_battery.c16
-rw-r--r--drivers/power/supply/88pm860x_charger.c46
-rw-r--r--drivers/power/supply/Kconfig301
-rw-r--r--drivers/power/supply/Makefile30
-rw-r--r--drivers/power/supply/ab8500_bmdata.c4
-rw-r--r--drivers/power/supply/ab8500_btemp.c35
-rw-r--r--drivers/power/supply/ab8500_chargalg.c31
-rw-r--r--drivers/power/supply/ab8500_charger.c77
-rw-r--r--drivers/power/supply/ab8500_fg.c69
-rw-r--r--drivers/power/supply/acer_a500_battery.c14
-rw-r--r--drivers/power/supply/act8945a_charger.c8
-rw-r--r--drivers/power/supply/adc-battery-helper.c327
-rw-r--r--drivers/power/supply/adc-battery-helper.h62
-rw-r--r--drivers/power/supply/adp5061.c16
-rw-r--r--drivers/power/supply/apm_power.c10
-rw-r--r--drivers/power/supply/axp20x_ac_power.c5
-rw-r--r--drivers/power/supply/axp20x_battery.c631
-rw-r--r--drivers/power/supply/axp20x_usb_power.c722
-rw-r--r--drivers/power/supply/axp288_charger.c37
-rw-r--r--drivers/power/supply/axp288_fuel_gauge.c24
-rw-r--r--drivers/power/supply/bd71828-power.c1049
-rw-r--r--drivers/power/supply/bd99954-charger.c99
-rw-r--r--drivers/power/supply/bq2415x_charger.c102
-rw-r--r--drivers/power/supply/bq24190_charger.c553
-rw-r--r--drivers/power/supply/bq24257_charger.c102
-rw-r--r--drivers/power/supply/bq24735-charger.c7
-rw-r--r--drivers/power/supply/bq2515x_charger.c77
-rw-r--r--drivers/power/supply/bq256xx_charger.c156
-rw-r--r--drivers/power/supply/bq257xx_charger.c755
-rw-r--r--drivers/power/supply/bq25890_charger.c507
-rw-r--r--drivers/power/supply/bq25980_charger.c20
-rw-r--r--drivers/power/supply/bq27xxx_battery.c511
-rw-r--r--drivers/power/supply/bq27xxx_battery_i2c.c75
-rw-r--r--drivers/power/supply/chagall-battery.c291
-rw-r--r--drivers/power/supply/charger-manager.c24
-rw-r--r--drivers/power/supply/collie_battery.c5
-rw-r--r--drivers/power/supply/cpcap-battery.c10
-rw-r--r--drivers/power/supply/cpcap-charger.c33
-rw-r--r--drivers/power/supply/cros_charge-control.c337
-rw-r--r--drivers/power/supply/cros_peripheral_charger.c38
-rw-r--r--drivers/power/supply/cros_usbpd-charger.c39
-rw-r--r--drivers/power/supply/cw2015_battery.c33
-rw-r--r--drivers/power/supply/da9030_battery.c20
-rw-r--r--drivers/power/supply/da9052-battery.c8
-rw-r--r--drivers/power/supply/da9150-charger.c88
-rw-r--r--drivers/power/supply/da9150-fg.c5
-rw-r--r--drivers/power/supply/ds2760_battery.c70
-rw-r--r--drivers/power/supply/ds2780_battery.c22
-rw-r--r--drivers/power/supply/ds2781_battery.c22
-rw-r--r--drivers/power/supply/ds2782_battery.c95
-rw-r--r--drivers/power/supply/generic-adc-battery.c249
-rw-r--r--drivers/power/supply/goldfish_battery.c25
-rw-r--r--drivers/power/supply/gpio-charger.c34
-rw-r--r--drivers/power/supply/huawei-gaokun-battery.c645
-rw-r--r--drivers/power/supply/ingenic-battery.c12
-rw-r--r--drivers/power/supply/intel_dc_ti_battery.c391
-rw-r--r--drivers/power/supply/ip5xxx_power.c575
-rw-r--r--drivers/power/supply/ipaq_micro_battery.c7
-rw-r--r--drivers/power/supply/isp1704_charger.c4
-rw-r--r--drivers/power/supply/lego_ev3_battery.c5
-rw-r--r--drivers/power/supply/lenovo_yoga_c630_battery.c498
-rw-r--r--drivers/power/supply/lp8727_charger.c44
-rw-r--r--drivers/power/supply/lp8788-charger.c34
-rw-r--r--drivers/power/supply/lt3651-charger.c2
-rw-r--r--drivers/power/supply/ltc2941-battery-gauge.c5
-rw-r--r--drivers/power/supply/ltc4162-l-charger.c463
-rw-r--r--drivers/power/supply/max14577_charger.c18
-rw-r--r--drivers/power/supply/max14656_charger_detector.c7
-rw-r--r--drivers/power/supply/max17040_battery.c38
-rw-r--r--drivers/power/supply/max17042_battery.c223
-rw-r--r--drivers/power/supply/max1720x_battery.c634
-rw-r--r--drivers/power/supply/max1721x_battery.c10
-rw-r--r--drivers/power/supply/max77650-charger.c10
-rw-r--r--drivers/power/supply/max77693_charger.c74
-rw-r--r--drivers/power/supply/max77705_charger.c713
-rw-r--r--drivers/power/supply/max77976_charger.c21
-rw-r--r--drivers/power/supply/max8903_charger.c3
-rw-r--r--drivers/power/supply/max8925_power.c42
-rw-r--r--drivers/power/supply/max8971_charger.c752
-rw-r--r--drivers/power/supply/max8998_charger.c1
-rw-r--r--drivers/power/supply/mm8013.c308
-rw-r--r--drivers/power/supply/mp2629_charger.c17
-rw-r--r--drivers/power/supply/mt6360_charger.c36
-rw-r--r--drivers/power/supply/mt6370-charger.c943
-rw-r--r--drivers/power/supply/olpc_battery.c13
-rw-r--r--drivers/power/supply/pcf50633-charger.c473
-rw-r--r--drivers/power/supply/pda_power.c520
-rw-r--r--drivers/power/supply/pf1550-charger.c641
-rw-r--r--drivers/power/supply/pm8916_bms_vm.c305
-rw-r--r--drivers/power/supply/pm8916_lbc.c381
-rw-r--r--drivers/power/supply/pmu_battery.c1
-rw-r--r--drivers/power/supply/power_supply.h59
-rw-r--r--drivers/power/supply/power_supply_core.c981
-rw-r--r--drivers/power/supply/power_supply_hwmon.c77
-rw-r--r--drivers/power/supply/power_supply_leds.c186
-rw-r--r--drivers/power/supply/power_supply_sysfs.c330
-rw-r--r--drivers/power/supply/qcom_battmgr.c1738
-rw-r--r--drivers/power/supply/qcom_smbb.c14
-rw-r--r--drivers/power/supply/qcom_smbx.c1052
-rw-r--r--drivers/power/supply/rk817_charger.c1249
-rw-r--r--drivers/power/supply/rn5t618_power.c14
-rw-r--r--drivers/power/supply/rt5033_battery.c56
-rw-r--r--drivers/power/supply/rt5033_charger.c753
-rw-r--r--drivers/power/supply/rt9455_charger.c22
-rw-r--r--drivers/power/supply/rt9467-charger.c1257
-rw-r--r--drivers/power/supply/rt9471.c939
-rw-r--r--drivers/power/supply/rt9756.c955
-rw-r--r--drivers/power/supply/rx51_battery.c61
-rw-r--r--drivers/power/supply/s3c_adc_battery.c453
-rw-r--r--drivers/power/supply/samsung-sdi-battery.c36
-rw-r--r--drivers/power/supply/sbs-battery.c19
-rw-r--r--drivers/power/supply/sbs-charger.c25
-rw-r--r--drivers/power/supply/sbs-manager.c14
-rw-r--r--drivers/power/supply/sc2731_charger.c6
-rw-r--r--drivers/power/supply/sc27xx_fuel_gauge.c29
-rw-r--r--drivers/power/supply/smb347-charger.c12
-rw-r--r--drivers/power/supply/stc3117_fuel_gauge.c612
-rw-r--r--drivers/power/supply/surface_battery.c12
-rw-r--r--drivers/power/supply/surface_charger.c6
-rw-r--r--drivers/power/supply/test_power.c175
-rw-r--r--drivers/power/supply/tosa_battery.c512
-rw-r--r--drivers/power/supply/tps65090-charger.c26
-rw-r--r--drivers/power/supply/tps65217_charger.c13
-rw-r--r--drivers/power/supply/twl4030_charger.c18
-rw-r--r--drivers/power/supply/twl4030_madc_battery.c69
-rw-r--r--drivers/power/supply/twl6030_charger.c581
-rw-r--r--drivers/power/supply/ucs1002_power.c36
-rw-r--r--drivers/power/supply/ug3105_battery.c403
-rw-r--r--drivers/power/supply/wm831x_backup.c15
-rw-r--r--drivers/power/supply/wm831x_power.c57
-rw-r--r--drivers/power/supply/wm8350_power.c35
-rw-r--r--drivers/power/supply/wm97xx_battery.c4
-rw-r--r--drivers/power/supply/z2_battery.c319
-rw-r--r--drivers/powercap/Kconfig33
-rw-r--r--drivers/powercap/Makefile2
-rw-r--r--drivers/powercap/arm_scmi_powercap.c550
-rw-r--r--drivers/powercap/dtpm.c18
-rw-r--r--drivers/powercap/dtpm_cpu.c74
-rw-r--r--drivers/powercap/dtpm_devfreq.c41
-rw-r--r--drivers/powercap/idle_inject.c78
-rw-r--r--drivers/powercap/intel_rapl_common.c1705
-rw-r--r--drivers/powercap/intel_rapl_msr.c128
-rw-r--r--drivers/powercap/intel_rapl_tpmi.c354
-rw-r--r--drivers/powercap/powercap_sys.c21
-rw-r--r--drivers/pps/Makefile3
-rw-r--r--drivers/pps/clients/pps-gpio.c22
-rw-r--r--drivers/pps/clients/pps-ktimer.c6
-rw-r--r--drivers/pps/clients/pps-ldisc.c12
-rw-r--r--drivers/pps/clients/pps_parport.c19
-rw-r--r--drivers/pps/generators/Kconfig38
-rw-r--r--drivers/pps/generators/Makefile9
-rw-r--r--drivers/pps/generators/pps_gen-dummy.c96
-rw-r--r--drivers/pps/generators/pps_gen.c344
-rw-r--r--drivers/pps/generators/pps_gen_parport.c7
-rw-r--r--drivers/pps/generators/pps_gen_tio.c272
-rw-r--r--drivers/pps/generators/sysfs.c75
-rw-r--r--drivers/pps/kapi.c16
-rw-r--r--drivers/pps/kc.c10
-rw-r--r--drivers/pps/pps.c144
-rw-r--r--drivers/ps3/ps3-lpm.c4
-rw-r--r--drivers/ps3/ps3-sys-manager.c2
-rw-r--r--drivers/ps3/ps3-vuart.c4
-rw-r--r--drivers/ps3/ps3av.c9
-rw-r--r--drivers/ps3/ps3stor_lib.c3
-rw-r--r--drivers/ps3/sys-manager-core.c2
-rw-r--r--drivers/ptp/Kconfig85
-rw-r--r--drivers/ptp/Makefile10
-rw-r--r--drivers/ptp/ptp_chardev.c795
-rw-r--r--drivers/ptp/ptp_clock.c297
-rw-r--r--drivers/ptp/ptp_clockmatrix.c60
-rw-r--r--drivers/ptp/ptp_clockmatrix.h2
-rw-r--r--drivers/ptp/ptp_dfl_tod.c332
-rw-r--r--drivers/ptp/ptp_dte.c9
-rw-r--r--drivers/ptp/ptp_fc3.c1010
-rw-r--r--drivers/ptp/ptp_fc3.h45
-rw-r--r--drivers/ptp/ptp_idt82p33.c722
-rw-r--r--drivers/ptp/ptp_idt82p33.h25
-rw-r--r--drivers/ptp/ptp_ines.c52
-rw-r--r--drivers/ptp/ptp_kvm_arm.c4
-rw-r--r--drivers/ptp/ptp_kvm_common.c15
-rw-r--r--drivers/ptp/ptp_kvm_x86.c65
-rw-r--r--drivers/ptp/ptp_mock.c175
-rw-r--r--drivers/ptp/ptp_netc.c1043
-rw-r--r--drivers/ptp/ptp_ocp.c1476
-rw-r--r--drivers/ptp/ptp_pch.c25
-rw-r--r--drivers/ptp/ptp_private.h61
-rw-r--r--drivers/ptp/ptp_qoriq.c81
-rw-r--r--drivers/ptp/ptp_qoriq_debugfs.c101
-rw-r--r--drivers/ptp/ptp_s390.c129
-rw-r--r--drivers/ptp/ptp_sysfs.c45
-rw-r--r--drivers/ptp/ptp_vclock.c55
-rw-r--r--drivers/ptp/ptp_vmclock.c610
-rw-r--r--drivers/ptp/ptp_vmw.c20
-rw-r--r--drivers/pwm/Kconfig247
-rw-r--r--drivers/pwm/Makefile22
-rw-r--r--drivers/pwm/core.c2556
-rw-r--r--drivers/pwm/pwm-ab8500.c145
-rw-r--r--drivers/pwm/pwm-adp5585.c223
-rw-r--r--drivers/pwm/pwm-airoha.c622
-rw-r--r--drivers/pwm/pwm-apple.c159
-rw-r--r--drivers/pwm/pwm-argon-fan-hat.c109
-rw-r--r--drivers/pwm/pwm-atmel-hlcdc.c94
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c223
-rw-r--r--drivers/pwm/pwm-atmel.c135
-rw-r--r--drivers/pwm/pwm-axi-pwmgen.c341
-rw-r--r--drivers/pwm/pwm-bcm-iproc.c63
-rw-r--r--drivers/pwm/pwm-bcm-kona.c28
-rw-r--r--drivers/pwm/pwm-bcm2835.c102
-rw-r--r--drivers/pwm/pwm-berlin.c103
-rw-r--r--drivers/pwm/pwm-brcmstb.c74
-rw-r--r--drivers/pwm/pwm-clk.c42
-rw-r--r--drivers/pwm/pwm-clps711x.c41
-rw-r--r--drivers/pwm/pwm-crc.c43
-rw-r--r--drivers/pwm/pwm-cros-ec.c184
-rw-r--r--drivers/pwm/pwm-dwc-core.c183
-rw-r--r--drivers/pwm/pwm-dwc.c296
-rw-r--r--drivers/pwm/pwm-dwc.h74
-rw-r--r--drivers/pwm/pwm-ep93xx.c44
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c138
-rw-r--r--drivers/pwm/pwm-gpio.c240
-rw-r--r--drivers/pwm/pwm-hibvt.c86
-rw-r--r--drivers/pwm/pwm-img.c92
-rw-r--r--drivers/pwm/pwm-imx-tpm.c120
-rw-r--r--drivers/pwm/pwm-imx1.c23
-rw-r--r--drivers/pwm/pwm-imx27.c202
-rw-r--r--drivers/pwm/pwm-intel-lgm.c25
-rw-r--r--drivers/pwm/pwm-iqs620a.c46
-rw-r--r--drivers/pwm/pwm-jz4740.c98
-rw-r--r--drivers/pwm/pwm-keembay.c24
-rw-r--r--drivers/pwm/pwm-loongson.c290
-rw-r--r--drivers/pwm/pwm-lp3943.c53
-rw-r--r--drivers/pwm/pwm-lpc18xx-sct.c95
-rw-r--r--drivers/pwm/pwm-lpc32xx.c40
-rw-r--r--drivers/pwm/pwm-lpss-pci.c69
-rw-r--r--drivers/pwm/pwm-lpss-platform.c59
-rw-r--r--drivers/pwm/pwm-lpss.c92
-rw-r--r--drivers/pwm/pwm-lpss.h26
-rw-r--r--drivers/pwm/pwm-max7360.c209
-rw-r--r--drivers/pwm/pwm-mc33xs2410.c407
-rw-r--r--drivers/pwm/pwm-mediatek.c498
-rw-r--r--drivers/pwm/pwm-meson.c570
-rw-r--r--drivers/pwm/pwm-microchip-core.c490
-rw-r--r--drivers/pwm/pwm-mtk-disp.c115
-rw-r--r--drivers/pwm/pwm-mxs.c33
-rw-r--r--drivers/pwm/pwm-ntxec.c19
-rw-r--r--drivers/pwm/pwm-omap-dmtimer.c78
-rw-r--r--drivers/pwm/pwm-pca9685.c593
-rw-r--r--drivers/pwm/pwm-pxa.c113
-rw-r--r--drivers/pwm/pwm-raspberrypi-poe.c29
-rw-r--r--drivers/pwm/pwm-rcar.c58
-rw-r--r--drivers/pwm/pwm-renesas-tpu.c46
-rw-r--r--drivers/pwm/pwm-rockchip.c103
-rw-r--r--drivers/pwm/pwm-rz-mtu3.c552
-rw-r--r--drivers/pwm/pwm-rzg2l-gpt.c456
-rw-r--r--drivers/pwm/pwm-samsung.c209
-rw-r--r--drivers/pwm/pwm-sifive.c116
-rw-r--r--drivers/pwm/pwm-sl28cpld.c30
-rw-r--r--drivers/pwm/pwm-sophgo-sg2042.c301
-rw-r--r--drivers/pwm/pwm-spear.c58
-rw-r--r--drivers/pwm/pwm-sprd.c93
-rw-r--r--drivers/pwm/pwm-sti.c228
-rw-r--r--drivers/pwm/pwm-stm32-lp.c280
-rw-r--r--drivers/pwm/pwm-stm32.c733
-rw-r--r--drivers/pwm/pwm-stmpe.c103
-rw-r--r--drivers/pwm/pwm-sun4i.c120
-rw-r--r--drivers/pwm/pwm-sunplus.c25
-rw-r--r--drivers/pwm/pwm-tegra.c75
-rw-r--r--drivers/pwm/pwm-tiecap.c72
-rw-r--r--drivers/pwm/pwm-tiehrpwm.c233
-rw-r--r--drivers/pwm/pwm-twl-led.c106
-rw-r--r--drivers/pwm/pwm-twl.c56
-rw-r--r--drivers/pwm/pwm-visconti.c27
-rw-r--r--drivers/pwm/pwm-vt8500.c93
-rw-r--r--drivers/pwm/pwm-xilinx.c59
-rw-r--r--drivers/pwm/pwm_th1520.rs387
-rw-r--r--drivers/pwm/sysfs.c529
-rw-r--r--drivers/rapidio/Kconfig17
-rw-r--r--drivers/rapidio/devices/rio_mport_cdev.c61
-rw-r--r--drivers/rapidio/devices/tsi721.c73
-rw-r--r--drivers/rapidio/devices/tsi721_dma.c4
-rw-r--r--drivers/rapidio/rio-driver.c11
-rw-r--r--drivers/rapidio/rio-scan.c13
-rw-r--r--drivers/rapidio/rio-sysfs.c10
-rw-r--r--drivers/rapidio/rio.c112
-rw-r--r--drivers/rapidio/rio.h2
-rw-r--r--drivers/rapidio/rio_cm.c35
-rw-r--r--drivers/ras/Kconfig13
-rw-r--r--drivers/ras/Makefile3
-rw-r--r--drivers/ras/amd/atl/Kconfig26
-rw-r--r--drivers/ras/amd/atl/Makefile20
-rw-r--r--drivers/ras/amd/atl/access.c137
-rw-r--r--drivers/ras/amd/atl/core.c232
-rw-r--r--drivers/ras/amd/atl/dehash.c457
-rw-r--r--drivers/ras/amd/atl/denormalize.c1279
-rw-r--r--drivers/ras/amd/atl/internal.h372
-rw-r--r--drivers/ras/amd/atl/map.c779
-rw-r--r--drivers/ras/amd/atl/prm.c53
-rw-r--r--drivers/ras/amd/atl/reg_fields.h606
-rw-r--r--drivers/ras/amd/atl/system.c321
-rw-r--r--drivers/ras/amd/atl/umc.c418
-rw-r--r--drivers/ras/amd/fmpm.c1041
-rw-r--r--drivers/ras/cec.c20
-rw-r--r--drivers/ras/debugfs.c12
-rw-r--r--drivers/ras/debugfs.h6
-rw-r--r--drivers/ras/ras.c72
-rw-r--r--drivers/regulator/88pg86x.c3
-rw-r--r--drivers/regulator/88pm800-regulator.c1
-rw-r--r--drivers/regulator/88pm8607.c1
-rw-r--r--drivers/regulator/88pm886-regulator.c392
-rw-r--r--drivers/regulator/Kconfig407
-rw-r--r--drivers/regulator/Makefile41
-rw-r--r--drivers/regulator/aat2870-regulator.c1
-rw-r--r--drivers/regulator/ab8500-ext.c1
-rw-r--r--drivers/regulator/ab8500.c1
-rw-r--r--drivers/regulator/act8865-regulator.c11
-rw-r--r--drivers/regulator/act8945a-regulator.c9
-rw-r--r--drivers/regulator/ad5398.c35
-rw-r--r--drivers/regulator/adp5055-regulator.c424
-rw-r--r--drivers/regulator/anatop-regulator.c1
-rw-r--r--drivers/regulator/arizona-ldo1.c14
-rw-r--r--drivers/regulator/arizona-micsupp.c30
-rw-r--r--drivers/regulator/as3711-regulator.c1
-rw-r--r--drivers/regulator/as3722-regulator.c1
-rw-r--r--drivers/regulator/atc260x-regulator.c8
-rw-r--r--drivers/regulator/aw37503-regulator.c240
-rw-r--r--drivers/regulator/axp20x-regulator.c410
-rw-r--r--drivers/regulator/bcm590xx-regulator.c1290
-rw-r--r--drivers/regulator/bd71815-regulator.c37
-rw-r--r--drivers/regulator/bd71828-regulator.c87
-rw-r--r--drivers/regulator/bd718x7-regulator.c130
-rw-r--r--drivers/regulator/bd9571mwv-regulator.c4
-rw-r--r--drivers/regulator/bd9576-regulator.c80
-rw-r--r--drivers/regulator/bd96801-regulator.c1352
-rw-r--r--drivers/regulator/bq257xx-regulator.c186
-rw-r--r--drivers/regulator/core.c1255
-rw-r--r--drivers/regulator/cpcap-regulator.c3
-rw-r--r--drivers/regulator/cros-ec-regulator.c5
-rw-r--r--drivers/regulator/da903x-regulator.c3
-rw-r--r--drivers/regulator/da9052-regulator.c23
-rw-r--r--drivers/regulator/da9055-regulator.c77
-rw-r--r--drivers/regulator/da9062-regulator.c14
-rw-r--r--drivers/regulator/da9063-regulator.c157
-rw-r--r--drivers/regulator/da9121-regulator.c72
-rw-r--r--drivers/regulator/da9210-regulator.c19
-rw-r--r--drivers/regulator/da9211-regulator.c18
-rw-r--r--drivers/regulator/db8500-prcmu.c5
-rw-r--r--drivers/regulator/devres.c358
-rw-r--r--drivers/regulator/dummy.c36
-rw-r--r--drivers/regulator/event.c91
-rw-r--r--drivers/regulator/fan53555.c231
-rw-r--r--drivers/regulator/fan53880.c19
-rw-r--r--drivers/regulator/fixed-helper.c8
-rw-r--r--drivers/regulator/fixed.c63
-rw-r--r--drivers/regulator/fp9931.c551
-rw-r--r--drivers/regulator/gpio-regulator.c30
-rw-r--r--drivers/regulator/helpers.c89
-rw-r--r--drivers/regulator/hi6421-regulator.c45
-rw-r--r--drivers/regulator/hi6421v530-regulator.c28
-rw-r--r--drivers/regulator/hi6421v600-regulator.c17
-rw-r--r--drivers/regulator/hi655x-regulator.c1
-rw-r--r--drivers/regulator/internal.h35
-rw-r--r--drivers/regulator/irq_helpers.c38
-rw-r--r--drivers/regulator/isl6271a-regulator.c9
-rw-r--r--drivers/regulator/isl9305.c5
-rw-r--r--drivers/regulator/lm363x-regulator.c1
-rw-r--r--drivers/regulator/lochnagar-regulator.c9
-rw-r--r--drivers/regulator/lp3971.c5
-rw-r--r--drivers/regulator/lp3972.c8
-rw-r--r--drivers/regulator/lp872x.c11
-rw-r--r--drivers/regulator/lp873x-regulator.c4
-rw-r--r--drivers/regulator/lp8755.c11
-rw-r--r--drivers/regulator/lp87565-regulator.c8
-rw-r--r--drivers/regulator/lp8788-buck.c65
-rw-r--r--drivers/regulator/lp8788-ldo.c2
-rw-r--r--drivers/regulator/ltc3589.c68
-rw-r--r--drivers/regulator/ltc3676.c5
-rw-r--r--drivers/regulator/max14577-regulator.c6
-rw-r--r--drivers/regulator/max1586.c15
-rw-r--r--drivers/regulator/max20086-regulator.c88
-rw-r--r--drivers/regulator/max20411-regulator.c165
-rw-r--r--drivers/regulator/max5970-regulator.c642
-rw-r--r--drivers/regulator/max597x-regulator.c502
-rw-r--r--drivers/regulator/max77503-regulator.c129
-rw-r--r--drivers/regulator/max77541-regulator.c153
-rw-r--r--drivers/regulator/max77620-regulator.c1
-rw-r--r--drivers/regulator/max77650-regulator.c34
-rw-r--r--drivers/regulator/max77686-regulator.c1
-rw-r--r--drivers/regulator/max77693-regulator.c1
-rw-r--r--drivers/regulator/max77802-regulator.c39
-rw-r--r--drivers/regulator/max77826-regulator.c8
-rw-r--r--drivers/regulator/max77838-regulator.c221
-rw-r--r--drivers/regulator/max77857-regulator.c461
-rw-r--r--drivers/regulator/max8649.c6
-rw-r--r--drivers/regulator/max8660.c5
-rw-r--r--drivers/regulator/max8893.c9
-rw-r--r--drivers/regulator/max8907-regulator.c1
-rw-r--r--drivers/regulator/max8925-regulator.c1
-rw-r--r--drivers/regulator/max8952.c8
-rw-r--r--drivers/regulator/max8973-regulator.c60
-rw-r--r--drivers/regulator/max8997-regulator.c113
-rw-r--r--drivers/regulator/max8998.c154
-rw-r--r--drivers/regulator/mc13783-regulator.c1
-rw-r--r--drivers/regulator/mc13892-regulator.c1
-rw-r--r--drivers/regulator/mc13xxx.h2
-rw-r--r--drivers/regulator/mcp16502.c30
-rw-r--r--drivers/regulator/mp5416.c19
-rw-r--r--drivers/regulator/mp8859.c257
-rw-r--r--drivers/regulator/mp886x.c22
-rw-r--r--drivers/regulator/mpq7920.c6
-rw-r--r--drivers/regulator/mt6311-regulator.c10
-rw-r--r--drivers/regulator/mt6315-regulator.c9
-rw-r--r--drivers/regulator/mt6316-regulator.c345
-rw-r--r--drivers/regulator/mt6323-regulator.c1
-rw-r--r--drivers/regulator/mt6331-regulator.c508
-rw-r--r--drivers/regulator/mt6332-regulator.c423
-rw-r--r--drivers/regulator/mt6357-regulator.c454
-rw-r--r--drivers/regulator/mt6358-regulator.c766
-rw-r--r--drivers/regulator/mt6359-regulator.c10
-rw-r--r--drivers/regulator/mt6360-regulator.c33
-rw-r--r--drivers/regulator/mt6363-regulator.c938
-rw-r--r--drivers/regulator/mt6370-regulator.c5
-rw-r--r--drivers/regulator/mt6380-regulator.c1
-rw-r--r--drivers/regulator/mt6397-regulator.c3
-rw-r--r--drivers/regulator/mtk-dvfsrc-regulator.c284
-rw-r--r--drivers/regulator/of_regulator.c312
-rw-r--r--drivers/regulator/palmas-regulator.c11
-rw-r--r--drivers/regulator/pbias-regulator.c2
-rw-r--r--drivers/regulator/pca9450-regulator.c762
-rw-r--r--drivers/regulator/pcap-regulator.c13
-rw-r--r--drivers/regulator/pcf50633-regulator.c123
-rw-r--r--drivers/regulator/pf0900-regulator.c975
-rw-r--r--drivers/regulator/pf1550-regulator.c429
-rw-r--r--drivers/regulator/pf530x-regulator.c375
-rw-r--r--drivers/regulator/pf8x00-regulator.c13
-rw-r--r--drivers/regulator/pf9453-regulator.c870
-rw-r--r--drivers/regulator/pfuze100-regulator.c20
-rw-r--r--drivers/regulator/pv88060-regulator.c7
-rw-r--r--drivers/regulator/pv88080-regulator.c61
-rw-r--r--drivers/regulator/pv88090-regulator.c7
-rw-r--r--drivers/regulator/pwm-regulator.c82
-rw-r--r--drivers/regulator/qcom-labibb-regulator.c13
-rw-r--r--drivers/regulator/qcom-pm8008-regulator.c198
-rw-r--r--drivers/regulator/qcom-refgen-regulator.c154
-rw-r--r--drivers/regulator/qcom-rpmh-regulator.c1465
-rw-r--r--drivers/regulator/qcom_rpm-regulator.c35
-rw-r--r--drivers/regulator/qcom_smd-regulator.c501
-rw-r--r--drivers/regulator/qcom_spmi-regulator.c581
-rw-r--r--drivers/regulator/qcom_usb_vbus-regulator.c2
-rw-r--r--drivers/regulator/raa215300.c192
-rw-r--r--drivers/regulator/rc5t583-regulator.c2
-rw-r--r--drivers/regulator/regnl.h13
-rw-r--r--drivers/regulator/renesas-usb-vbus-regulator.c69
-rw-r--r--drivers/regulator/rk808-regulator.c680
-rw-r--r--drivers/regulator/rn5t618-regulator.c1
-rw-r--r--drivers/regulator/rohm-regulator.c6
-rw-r--r--drivers/regulator/rpi-panel-attiny-regulator.c92
-rw-r--r--drivers/regulator/rpi-panel-v2-regulator.c125
-rw-r--r--drivers/regulator/rt4801-regulator.c3
-rw-r--r--drivers/regulator/rt4803.c216
-rw-r--r--drivers/regulator/rt4831-regulator.c2
-rw-r--r--drivers/regulator/rt5033-regulator.c1
-rw-r--r--drivers/regulator/rt5120-regulator.c5
-rw-r--r--drivers/regulator/rt5133-regulator.c642
-rw-r--r--drivers/regulator/rt5190a-regulator.c3
-rw-r--r--drivers/regulator/rt5739.c322
-rw-r--r--drivers/regulator/rt5759-regulator.c6
-rw-r--r--drivers/regulator/rt6160-regulator.c22
-rw-r--r--drivers/regulator/rt6190-regulator.c496
-rw-r--r--drivers/regulator/rt6245-regulator.c3
-rw-r--r--drivers/regulator/rtmv20-regulator.c5
-rw-r--r--drivers/regulator/rtq2134-regulator.c3
-rw-r--r--drivers/regulator/rtq2208-regulator.c668
-rw-r--r--drivers/regulator/rtq6752-regulator.c7
-rw-r--r--drivers/regulator/s2dos05-regulator.c165
-rw-r--r--drivers/regulator/s2mpa01.c2
-rw-r--r--drivers/regulator/s2mps11.c110
-rw-r--r--drivers/regulator/s5m8767.c187
-rw-r--r--drivers/regulator/sc2731-regulator.c1
-rw-r--r--drivers/regulator/scmi-regulator.c27
-rw-r--r--drivers/regulator/sky81452-regulator.c1
-rw-r--r--drivers/regulator/slg51000-regulator.c9
-rw-r--r--drivers/regulator/sm5703-regulator.c167
-rw-r--r--drivers/regulator/spacemit-p1.c157
-rw-r--r--drivers/regulator/stm32-booster.c6
-rw-r--r--drivers/regulator/stm32-pwr.c15
-rw-r--r--drivers/regulator/stm32-vrefbuf.c15
-rw-r--r--drivers/regulator/stpmic1_regulator.c5
-rw-r--r--drivers/regulator/stw481x-vmmc.c1
-rw-r--r--drivers/regulator/sun20i-regulator.c157
-rw-r--r--drivers/regulator/sy7636a-regulator.c35
-rw-r--r--drivers/regulator/sy8106a-regulator.c11
-rw-r--r--drivers/regulator/sy8824x.c36
-rw-r--r--drivers/regulator/sy8827n.c12
-rw-r--r--drivers/regulator/ti-abb-regulator.c47
-rw-r--r--drivers/regulator/tps51632-regulator.c17
-rw-r--r--drivers/regulator/tps6105x-regulator.c1
-rw-r--r--drivers/regulator/tps62360-regulator.c22
-rw-r--r--drivers/regulator/tps6286x-regulator.c32
-rw-r--r--drivers/regulator/tps6287x-regulator.c254
-rw-r--r--drivers/regulator/tps65023-regulator.c11
-rw-r--r--drivers/regulator/tps6507x-regulator.c1
-rw-r--r--drivers/regulator/tps65086-regulator.c189
-rw-r--r--drivers/regulator/tps65090-regulator.c1
-rw-r--r--drivers/regulator/tps65132-regulator.c10
-rw-r--r--drivers/regulator/tps65217-regulator.c1
-rw-r--r--drivers/regulator/tps65218-regulator.c3
-rw-r--r--drivers/regulator/tps65219-regulator.c509
-rw-r--r--drivers/regulator/tps6524x-regulator.c2
-rw-r--r--drivers/regulator/tps6586x-regulator.c1
-rw-r--r--drivers/regulator/tps65910-regulator.c2
-rw-r--r--drivers/regulator/tps65912-regulator.c1
-rw-r--r--drivers/regulator/tps6594-regulator.c845
-rw-r--r--drivers/regulator/tps68470-regulator.c1
-rw-r--r--drivers/regulator/twl-regulator.c2
-rw-r--r--drivers/regulator/twl6030-regulator.c21
-rw-r--r--drivers/regulator/uniphier-regulator.c7
-rw-r--r--drivers/regulator/userspace-consumer.c72
-rw-r--r--drivers/regulator/vctrl-regulator.c3
-rw-r--r--drivers/regulator/vexpress-regulator.c4
-rw-r--r--drivers/regulator/virtual.c5
-rw-r--r--drivers/regulator/vqmmc-ipq4019-regulator.c2
-rw-r--r--drivers/regulator/wm831x-dcdc.c4
-rw-r--r--drivers/regulator/wm831x-isink.c9
-rw-r--r--drivers/regulator/wm831x-ldo.c3
-rw-r--r--drivers/regulator/wm8350-regulator.c9
-rw-r--r--drivers/regulator/wm8400-regulator.c3
-rw-r--r--drivers/regulator/wm8994-regulator.c1
-rw-r--r--drivers/remoteproc/Kconfig52
-rw-r--r--drivers/remoteproc/Makefile6
-rw-r--r--drivers/remoteproc/da8xx_remoteproc.c108
-rw-r--r--drivers/remoteproc/imx_dsp_rproc.c858
-rw-r--r--drivers/remoteproc/imx_rproc.c812
-rw-r--r--drivers/remoteproc/imx_rproc.h23
-rw-r--r--drivers/remoteproc/ingenic_rproc.c3
-rw-r--r--drivers/remoteproc/keystone_remoteproc.c143
-rw-r--r--drivers/remoteproc/meson_mx_ao_arc.c4
-rw-r--r--drivers/remoteproc/mtk_common.h50
-rw-r--r--drivers/remoteproc/mtk_scp.c856
-rw-r--r--drivers/remoteproc/mtk_scp_ipi.c43
-rw-r--r--drivers/remoteproc/omap_remoteproc.c81
-rw-r--r--drivers/remoteproc/pru_rproc.c284
-rw-r--r--drivers/remoteproc/qcom_common.c125
-rw-r--r--drivers/remoteproc/qcom_common.h18
-rw-r--r--drivers/remoteproc/qcom_q6v5.c20
-rw-r--r--drivers/remoteproc/qcom_q6v5_adsp.c373
-rw-r--r--drivers/remoteproc/qcom_q6v5_mss.c637
-rw-r--r--drivers/remoteproc/qcom_q6v5_pas.c1218
-rw-r--r--drivers/remoteproc/qcom_q6v5_wcss.c188
-rw-r--r--drivers/remoteproc/qcom_sysmon.c18
-rw-r--r--drivers/remoteproc/qcom_wcnss.c116
-rw-r--r--drivers/remoteproc/qcom_wcnss.h2
-rw-r--r--drivers/remoteproc/qcom_wcnss_iris.c8
-rw-r--r--drivers/remoteproc/rcar_rproc.c36
-rw-r--r--drivers/remoteproc/remoteproc_core.c332
-rw-r--r--drivers/remoteproc/remoteproc_coredump.c6
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c4
-rw-r--r--drivers/remoteproc/remoteproc_internal.h44
-rw-r--r--drivers/remoteproc/remoteproc_sysfs.c2
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c193
-rw-r--r--drivers/remoteproc/st_remoteproc.c122
-rw-r--r--drivers/remoteproc/st_slim_rproc.c9
-rw-r--r--drivers/remoteproc/stm32_rproc.c176
-rw-r--r--drivers/remoteproc/ti_k3_common.c542
-rw-r--r--drivers/remoteproc/ti_k3_common.h118
-rw-r--r--drivers/remoteproc/ti_k3_dsp_remoteproc.c821
-rw-r--r--drivers/remoteproc/ti_k3_m4_remoteproc.c156
-rw-r--r--drivers/remoteproc/ti_k3_r5_remoteproc.c1260
-rw-r--r--drivers/remoteproc/ti_sci_proc.h26
-rw-r--r--drivers/remoteproc/wkup_m3_rproc.c73
-rw-r--r--drivers/remoteproc/xlnx_r5_remoteproc.c1563
-rw-r--r--drivers/resctrl/Kconfig24
-rw-r--r--drivers/resctrl/Makefile4
-rw-r--r--drivers/resctrl/mpam_devices.c2723
-rw-r--r--drivers/resctrl/mpam_internal.h658
-rw-r--r--drivers/resctrl/test_mpam_devices.c389
-rw-r--r--drivers/reset/Kconfig158
-rw-r--r--drivers/reset/Makefile25
-rw-r--r--drivers/reset/amlogic/Kconfig27
-rw-r--r--drivers/reset/amlogic/Makefile4
-rw-r--r--drivers/reset/amlogic/reset-meson-audio-arb.c (renamed from drivers/reset/reset-meson-audio-arb.c)27
-rw-r--r--drivers/reset/amlogic/reset-meson-aux.c81
-rw-r--r--drivers/reset/amlogic/reset-meson-common.c142
-rw-r--r--drivers/reset/amlogic/reset-meson.c105
-rw-r--r--drivers/reset/amlogic/reset-meson.h28
-rw-r--r--drivers/reset/core.c484
-rw-r--r--drivers/reset/hisilicon/hi6220_reset.c4
-rw-r--r--drivers/reset/hisilicon/reset-hi3660.c6
-rw-r--r--drivers/reset/reset-aspeed.c253
-rw-r--r--drivers/reset/reset-ath79.c6
-rw-r--r--drivers/reset/reset-axs10x.c4
-rw-r--r--drivers/reset/reset-bcm6345.c3
-rw-r--r--drivers/reset/reset-berlin.c3
-rw-r--r--drivers/reset/reset-brcmstb-rescal.c4
-rw-r--r--drivers/reset/reset-brcmstb.c3
-rw-r--r--drivers/reset/reset-eic7700.c429
-rw-r--r--drivers/reset/reset-eyeq.c581
-rw-r--r--drivers/reset/reset-gpio.c120
-rw-r--r--drivers/reset/reset-hsdk.c7
-rw-r--r--drivers/reset/reset-imx-scu.c101
-rw-r--r--drivers/reset/reset-imx7.c3
-rw-r--r--drivers/reset/reset-imx8mp-audiomix.c162
-rw-r--r--drivers/reset/reset-intel-gw.c3
-rw-r--r--drivers/reset/reset-k210.c4
-rw-r--r--drivers/reset/reset-k230.c371
-rw-r--r--drivers/reset/reset-lantiq.c2
-rw-r--r--drivers/reset/reset-lpc18xx.c49
-rw-r--r--drivers/reset/reset-ma35d1.c235
-rw-r--r--drivers/reset/reset-meson.c157
-rw-r--r--drivers/reset/reset-microchip-sparx5.c73
-rw-r--r--drivers/reset/reset-mpfs.c207
-rw-r--r--drivers/reset/reset-npcm.c91
-rw-r--r--drivers/reset/reset-oxnas.c114
-rw-r--r--drivers/reset/reset-qcom-aoss.c6
-rw-r--r--drivers/reset/reset-qcom-pdc.c7
-rw-r--r--drivers/reset/reset-rzg2l-usbphy-ctrl.c130
-rw-r--r--drivers/reset/reset-rzv2h-usb2phy.c236
-rw-r--r--drivers/reset/reset-simple.c8
-rw-r--r--drivers/reset/reset-spacemit.c304
-rw-r--r--drivers/reset/reset-starfive-jh7100.c173
-rw-r--r--drivers/reset/reset-sunplus.c3
-rw-r--r--drivers/reset/reset-th1520.c983
-rw-r--r--drivers/reset/reset-ti-sci.c4
-rw-r--r--drivers/reset/reset-ti-syscon.c2
-rw-r--r--drivers/reset/reset-uniphier-glue.c35
-rw-r--r--drivers/reset/reset-uniphier.c1
-rw-r--r--drivers/reset/reset-zynq.c1
-rw-r--r--drivers/reset/reset-zynqmp.c10
-rw-r--r--drivers/reset/starfive/Kconfig21
-rw-r--r--drivers/reset/starfive/Makefile5
-rw-r--r--drivers/reset/starfive/reset-starfive-jh7100.c74
-rw-r--r--drivers/reset/starfive/reset-starfive-jh7110.c103
-rw-r--r--drivers/reset/starfive/reset-starfive-jh71x0.c134
-rw-r--r--drivers/reset/starfive/reset-starfive-jh71x0.h14
-rw-r--r--drivers/reset/sti/Kconfig8
-rw-r--r--drivers/reset/sti/Makefile4
-rw-r--r--drivers/reset/sti/reset-syscfg.c29
-rw-r--r--drivers/reset/tegra/Kconfig3
-rw-r--r--drivers/rpmsg/Makefile1
-rw-r--r--drivers/rpmsg/qcom_glink_native.c546
-rw-r--r--drivers/rpmsg/qcom_glink_native.h8
-rw-r--r--drivers/rpmsg/qcom_glink_rpm.c98
-rw-r--r--drivers/rpmsg/qcom_glink_smem.c102
-rw-r--r--drivers/rpmsg/qcom_glink_ssr.c3
-rw-r--r--drivers/rpmsg/qcom_glink_trace.h406
-rw-r--r--drivers/rpmsg/qcom_smd.c46
-rw-r--r--drivers/rpmsg/rpmsg_char.c89
-rw-r--r--drivers/rpmsg/rpmsg_core.c101
-rw-r--r--drivers/rpmsg/rpmsg_ctrl.c16
-rw-r--r--drivers/rpmsg/rpmsg_internal.h12
-rw-r--r--drivers/rpmsg/rpmsg_ns.c2
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c42
-rw-r--r--drivers/rtc/Kconfig296
-rw-r--r--drivers/rtc/Makefile25
-rw-r--r--drivers/rtc/class.c32
-rw-r--r--drivers/rtc/dev.c5
-rw-r--r--drivers/rtc/interface.c53
-rw-r--r--drivers/rtc/lib.c36
-rw-r--r--drivers/rtc/lib_test.c81
-rw-r--r--drivers/rtc/nvmem.c1
-rw-r--r--drivers/rtc/rtc-88pm80x.c5
-rw-r--r--drivers/rtc/rtc-88pm860x.c6
-rw-r--r--drivers/rtc/rtc-88pm886.c97
-rw-r--r--drivers/rtc/rtc-ab-b5ze-s3.c4
-rw-r--r--drivers/rtc/rtc-ab-eoz9.c48
-rw-r--r--drivers/rtc/rtc-ab8500.c13
-rw-r--r--drivers/rtc/rtc-abx80x.c120
-rw-r--r--drivers/rtc/rtc-ac100.c8
-rw-r--r--drivers/rtc/rtc-amlogic-a4.c465
-rw-r--r--drivers/rtc/rtc-armada38x.c14
-rw-r--r--drivers/rtc/rtc-as3722.c2
-rw-r--r--drivers/rtc/rtc-asm9260.c3
-rw-r--r--drivers/rtc/rtc-aspeed.c18
-rw-r--r--drivers/rtc/rtc-at91rm9200.c20
-rw-r--r--drivers/rtc/rtc-at91sam9.c9
-rw-r--r--drivers/rtc/rtc-bd70528.c5
-rw-r--r--drivers/rtc/rtc-bq32k.c9
-rw-r--r--drivers/rtc/rtc-brcmstb-waketimer.c200
-rw-r--r--drivers/rtc/rtc-cadence.c6
-rw-r--r--drivers/rtc/rtc-cmos.c441
-rw-r--r--drivers/rtc/rtc-cpcap.c3
-rw-r--r--drivers/rtc/rtc-cros-ec.c90
-rw-r--r--drivers/rtc/rtc-cv1800.c218
-rw-r--r--drivers/rtc/rtc-da9055.c2
-rw-r--r--drivers/rtc/rtc-da9063.c114
-rw-r--r--drivers/rtc/rtc-davinci.c512
-rw-r--r--drivers/rtc/rtc-dm355evm.c151
-rw-r--r--drivers/rtc/rtc-ds1302.c6
-rw-r--r--drivers/rtc/rtc-ds1305.c5
-rw-r--r--drivers/rtc/rtc-ds1307.c51
-rw-r--r--drivers/rtc/rtc-ds1343.c8
-rw-r--r--drivers/rtc/rtc-ds1347.c2
-rw-r--r--drivers/rtc/rtc-ds1374.c8
-rw-r--r--drivers/rtc/rtc-ds1390.c2
-rw-r--r--drivers/rtc/rtc-ds1511.c340
-rw-r--r--drivers/rtc/rtc-ds1672.c4
-rw-r--r--drivers/rtc/rtc-ds1685.c12
-rw-r--r--drivers/rtc/rtc-ds1742.c4
-rw-r--r--drivers/rtc/rtc-ds2404.c181
-rw-r--r--drivers/rtc/rtc-ds3232.c36
-rw-r--r--drivers/rtc/rtc-efi.c76
-rw-r--r--drivers/rtc/rtc-em3027.c4
-rw-r--r--drivers/rtc/rtc-ep93xx.c24
-rw-r--r--drivers/rtc/rtc-fm3130.c4
-rw-r--r--drivers/rtc/rtc-fsl-ftm-alarm.c14
-rw-r--r--drivers/rtc/rtc-ftrtc010.c21
-rw-r--r--drivers/rtc/rtc-gamecube.c11
-rw-r--r--drivers/rtc/rtc-goldfish.c1
-rw-r--r--drivers/rtc/rtc-hid-sensor-time.c6
-rw-r--r--drivers/rtc/rtc-hym8563.c28
-rw-r--r--drivers/rtc/rtc-imx-sm-bbm.c162
-rw-r--r--drivers/rtc/rtc-imxdi.c12
-rw-r--r--drivers/rtc/rtc-isl12022.c600
-rw-r--r--drivers/rtc/rtc-isl12026.c8
-rw-r--r--drivers/rtc/rtc-isl1208.c179
-rw-r--r--drivers/rtc/rtc-jz4740.c127
-rw-r--r--drivers/rtc/rtc-loongson.c408
-rw-r--r--drivers/rtc/rtc-lp8788.c2
-rw-r--r--drivers/rtc/rtc-lpc24xx.c9
-rw-r--r--drivers/rtc/rtc-lpc32xx.c2
-rw-r--r--drivers/rtc/rtc-ls1x.c192
-rw-r--r--drivers/rtc/rtc-m41t80.c133
-rw-r--r--drivers/rtc/rtc-m48t59.c30
-rw-r--r--drivers/rtc/rtc-m48t86.c22
-rw-r--r--drivers/rtc/rtc-ma35d1.c304
-rw-r--r--drivers/rtc/rtc-max31335.c778
-rw-r--r--drivers/rtc/rtc-max6900.c4
-rw-r--r--drivers/rtc/rtc-max77686.c43
-rw-r--r--drivers/rtc/rtc-max8907.c1
-rw-r--r--drivers/rtc/rtc-max8925.c2
-rw-r--r--drivers/rtc/rtc-max8997.c2
-rw-r--r--drivers/rtc/rtc-mc13xxx.c17
-rw-r--r--drivers/rtc/rtc-mc146818-lib.c45
-rw-r--r--drivers/rtc/rtc-mcp795.c1
-rw-r--r--drivers/rtc/rtc-meson-vrtc.c18
-rw-r--r--drivers/rtc/rtc-meson.c17
-rw-r--r--drivers/rtc/rtc-moxart.c89
-rw-r--r--drivers/rtc/rtc-mpc5121.c11
-rw-r--r--drivers/rtc/rtc-mpfs.c38
-rw-r--r--drivers/rtc/rtc-msc313.c12
-rw-r--r--drivers/rtc/rtc-mt6397.c34
-rw-r--r--drivers/rtc/rtc-mt7622.c10
-rw-r--r--drivers/rtc/rtc-mv.c16
-rw-r--r--drivers/rtc/rtc-mxc.c30
-rw-r--r--drivers/rtc/rtc-mxc_v2.c9
-rw-r--r--drivers/rtc/rtc-nct3018y.c83
-rw-r--r--drivers/rtc/rtc-nct6694.c297
-rw-r--r--drivers/rtc/rtc-nxp-bbnsm.c231
-rw-r--r--drivers/rtc/rtc-omap.c24
-rw-r--r--drivers/rtc/rtc-optee.c465
-rw-r--r--drivers/rtc/rtc-palmas.c5
-rw-r--r--drivers/rtc/rtc-pcap.c6
-rw-r--r--drivers/rtc/rtc-pcf2123.c7
-rw-r--r--drivers/rtc/rtc-pcf2127.c990
-rw-r--r--drivers/rtc/rtc-pcf50633.c286
-rw-r--r--drivers/rtc/rtc-pcf85063.c347
-rw-r--r--drivers/rtc/rtc-pcf8523.c73
-rw-r--r--drivers/rtc/rtc-pcf85363.c73
-rw-r--r--drivers/rtc/rtc-pcf8563.c249
-rw-r--r--drivers/rtc/rtc-pcf8583.c4
-rw-r--r--drivers/rtc/rtc-pic32.c14
-rw-r--r--drivers/rtc/rtc-pl030.c16
-rw-r--r--drivers/rtc/rtc-pl031.c8
-rw-r--r--drivers/rtc/rtc-pm8xxx.c707
-rw-r--r--drivers/rtc/rtc-pxa.c14
-rw-r--r--drivers/rtc/rtc-r7301.c35
-rw-r--r--drivers/rtc/rtc-rc5t583.c6
-rw-r--r--drivers/rtc/rtc-rc5t619.c15
-rw-r--r--drivers/rtc/rtc-renesas-rtca3.c897
-rw-r--r--drivers/rtc/rtc-rk808.c49
-rw-r--r--drivers/rtc/rtc-rs5c313.c6
-rw-r--r--drivers/rtc/rtc-rs5c372.c19
-rw-r--r--drivers/rtc/rtc-rtd119x.c4
-rw-r--r--drivers/rtc/rtc-rv3028.c137
-rw-r--r--drivers/rtc/rtc-rv3029c2.c14
-rw-r--r--drivers/rtc/rtc-rv3032.c49
-rw-r--r--drivers/rtc/rtc-rv8803.c121
-rw-r--r--drivers/rtc/rtc-rx6110.c12
-rw-r--r--drivers/rtc/rtc-rx8010.c12
-rw-r--r--drivers/rtc/rtc-rx8025.c7
-rw-r--r--drivers/rtc/rtc-rx8111.c368
-rw-r--r--drivers/rtc/rtc-rx8581.c90
-rw-r--r--drivers/rtc/rtc-rzn1.c280
-rw-r--r--drivers/rtc/rtc-s32g.c385
-rw-r--r--drivers/rtc/rtc-s35390a.c33
-rw-r--r--drivers/rtc/rtc-s3c.c70
-rw-r--r--drivers/rtc/rtc-s3c.h19
-rw-r--r--drivers/rtc/rtc-s5m.c309
-rw-r--r--drivers/rtc/rtc-sa1100.c6
-rw-r--r--drivers/rtc/rtc-sc27xx.c4
-rw-r--r--drivers/rtc/rtc-sd2405al.c229
-rw-r--r--drivers/rtc/rtc-sd3078.c75
-rw-r--r--drivers/rtc/rtc-sh.c307
-rw-r--r--drivers/rtc/rtc-snvs.c16
-rw-r--r--drivers/rtc/rtc-spacemit-p1.c167
-rw-r--r--drivers/rtc/rtc-spear.c8
-rw-r--r--drivers/rtc/rtc-ssd202d.c249
-rw-r--r--drivers/rtc/rtc-st-lpc.c20
-rw-r--r--drivers/rtc/rtc-stm32.c565
-rw-r--r--drivers/rtc/rtc-stmp3xxx.c9
-rw-r--r--drivers/rtc/rtc-sun6i.c25
-rw-r--r--drivers/rtc/rtc-sunplus.c19
-rw-r--r--drivers/rtc/rtc-sunxi.c2
-rw-r--r--drivers/rtc/rtc-tegra.c6
-rw-r--r--drivers/rtc/rtc-test.c8
-rw-r--r--drivers/rtc/rtc-ti-k3.c61
-rw-r--r--drivers/rtc/rtc-tps6586x.c8
-rw-r--r--drivers/rtc/rtc-tps65910.c10
-rw-r--r--drivers/rtc/rtc-tps6594.c505
-rw-r--r--drivers/rtc/rtc-twl.c50
-rw-r--r--drivers/rtc/rtc-v3020.c369
-rw-r--r--drivers/rtc/rtc-vt8500.c4
-rw-r--r--drivers/rtc/rtc-wm831x.c2
-rw-r--r--drivers/rtc/rtc-wm8350.c15
-rw-r--r--drivers/rtc/rtc-x1205.c9
-rw-r--r--drivers/rtc/rtc-xgene.c7
-rw-r--r--drivers/rtc/rtc-zynqmp.c31
-rw-r--r--drivers/rtc/sysfs.c65
-rw-r--r--drivers/rtc/test_rtc_lib.c106
-rw-r--r--drivers/s390/block/Kconfig5
-rw-r--r--drivers/s390/block/dasd.c675
-rw-r--r--drivers/s390/block/dasd_3990_erp.c111
-rw-r--r--drivers/s390/block/dasd_alias.c23
-rw-r--r--drivers/s390/block/dasd_devmap.c790
-rw-r--r--drivers/s390/block/dasd_diag.c49
-rw-r--r--drivers/s390/block/dasd_eckd.c757
-rw-r--r--drivers/s390/block/dasd_eckd.h9
-rw-r--r--drivers/s390/block/dasd_eer.c15
-rw-r--r--drivers/s390/block/dasd_erp.c10
-rw-r--r--drivers/s390/block/dasd_fba.c124
-rw-r--r--drivers/s390/block/dasd_genhd.c166
-rw-r--r--drivers/s390/block/dasd_int.h153
-rw-r--r--drivers/s390/block/dasd_ioctl.c77
-rw-r--r--drivers/s390/block/dasd_proc.c10
-rw-r--r--drivers/s390/block/dcssblk.c163
-rw-r--r--drivers/s390/block/scm_blk.c35
-rw-r--r--drivers/s390/block/scm_drv.c3
-rw-r--r--drivers/s390/char/Kconfig11
-rw-r--r--drivers/s390/char/Makefile7
-rw-r--r--drivers/s390/char/con3215.c300
-rw-r--r--drivers/s390/char/con3270.c2321
-rw-r--r--drivers/s390/char/diag_ftp.c11
-rw-r--r--drivers/s390/char/fs3270.c145
-rw-r--r--drivers/s390/char/hmcdrv_cache.c5
-rw-r--r--drivers/s390/char/hmcdrv_dev.c29
-rw-r--r--drivers/s390/char/hmcdrv_ftp.c9
-rw-r--r--drivers/s390/char/hmcdrv_mod.c3
-rw-r--r--drivers/s390/char/keyboard.c1
-rw-r--r--drivers/s390/char/monreader.c18
-rw-r--r--drivers/s390/char/monwriter.c8
-rw-r--r--drivers/s390/char/raw3270.c429
-rw-r--r--drivers/s390/char/raw3270.h229
-rw-r--r--drivers/s390/char/sclp.c83
-rw-r--r--drivers/s390/char/sclp.h49
-rw-r--r--drivers/s390/char/sclp_ap.c3
-rw-r--r--drivers/s390/char/sclp_cmd.c459
-rw-r--r--drivers/s390/char/sclp_con.c19
-rw-r--r--drivers/s390/char/sclp_config.c7
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c11
-rw-r--r--drivers/s390/char/sclp_ctl.c13
-rw-r--r--drivers/s390/char/sclp_early.c24
-rw-r--r--drivers/s390/char/sclp_early_core.c68
-rw-r--r--drivers/s390/char/sclp_ftp.c9
-rw-r--r--drivers/s390/char/sclp_mem.c521
-rw-r--r--drivers/s390/char/sclp_ocf.c8
-rw-r--r--drivers/s390/char/sclp_pci.c22
-rw-r--r--drivers/s390/char/sclp_sd.c35
-rw-r--r--drivers/s390/char/sclp_sdias.c3
-rw-r--r--drivers/s390/char/sclp_tty.c26
-rw-r--r--drivers/s390/char/sclp_vt220.c14
-rw-r--r--drivers/s390/char/tape.h33
-rw-r--r--drivers/s390/char/tape_34xx.c32
-rw-r--r--drivers/s390/char/tape_3590.c95
-rw-r--r--drivers/s390/char/tape_char.c143
-rw-r--r--drivers/s390/char/tape_class.c25
-rw-r--r--drivers/s390/char/tape_core.c59
-rw-r--r--drivers/s390/char/tape_proc.c3
-rw-r--r--drivers/s390/char/tape_std.c89
-rw-r--r--drivers/s390/char/tape_std.h9
-rw-r--r--drivers/s390/char/tty3270.c1963
-rw-r--r--drivers/s390/char/tty3270.h15
-rw-r--r--drivers/s390/char/uvdevice.c334
-rw-r--r--drivers/s390/char/vmcp.c16
-rw-r--r--drivers/s390/char/vmlogrdr.c61
-rw-r--r--drivers/s390/char/vmur.c67
-rw-r--r--drivers/s390/char/vmur.h2
-rw-r--r--drivers/s390/char/zcore.c56
-rw-r--r--drivers/s390/cio/airq.c6
-rw-r--r--drivers/s390/cio/blacklist.c3
-rw-r--r--drivers/s390/cio/ccwgroup.c21
-rw-r--r--drivers/s390/cio/ccwreq.c3
-rw-r--r--drivers/s390/cio/chp.c182
-rw-r--r--drivers/s390/cio/chp.h3
-rw-r--r--drivers/s390/cio/chsc.c183
-rw-r--r--drivers/s390/cio/chsc.h29
-rw-r--r--drivers/s390/cio/chsc_sch.c37
-rw-r--r--drivers/s390/cio/cio.c33
-rw-r--r--drivers/s390/cio/cio.h4
-rw-r--r--drivers/s390/cio/cio_inject.c5
-rw-r--r--drivers/s390/cio/cmf.c26
-rw-r--r--drivers/s390/cio/crw.c9
-rw-r--r--drivers/s390/cio/css.c120
-rw-r--r--drivers/s390/cio/css.h17
-rw-r--r--drivers/s390/cio/device.c195
-rw-r--r--drivers/s390/cio/device_fsm.c31
-rw-r--r--drivers/s390/cio/device_id.c5
-rw-r--r--drivers/s390/cio/device_ops.c21
-rw-r--r--drivers/s390/cio/device_pgid.c23
-rw-r--r--drivers/s390/cio/device_status.c5
-rw-r--r--drivers/s390/cio/eadm_sch.c50
-rw-r--r--drivers/s390/cio/fcx.c24
-rw-r--r--drivers/s390/cio/idset.c12
-rw-r--r--drivers/s390/cio/ioasm.c122
-rw-r--r--drivers/s390/cio/isc.c5
-rw-r--r--drivers/s390/cio/itcw.c4
-rw-r--r--drivers/s390/cio/orb.h9
-rw-r--r--drivers/s390/cio/qdio.h11
-rw-r--r--drivers/s390/cio/qdio_debug.c3
-rw-r--r--drivers/s390/cio/qdio_main.c72
-rw-r--r--drivers/s390/cio/qdio_setup.c31
-rw-r--r--drivers/s390/cio/qdio_thinint.c8
-rw-r--r--drivers/s390/cio/scm.c9
-rw-r--r--drivers/s390/cio/trace.h4
-rw-r--r--drivers/s390/cio/vfio_ccw_chp.c7
-rw-r--r--drivers/s390/cio/vfio_ccw_cp.c416
-rw-r--r--drivers/s390/cio/vfio_ccw_cp.h3
-rw-r--r--drivers/s390/cio/vfio_ccw_drv.c187
-rw-r--r--drivers/s390/cio/vfio_ccw_fsm.c55
-rw-r--r--drivers/s390/cio/vfio_ccw_ops.c208
-rw-r--r--drivers/s390/cio/vfio_ccw_private.h32
-rw-r--r--drivers/s390/crypto/Makefile24
-rw-r--r--drivers/s390/crypto/ap_bus.c1180
-rw-r--r--drivers/s390/crypto/ap_bus.h198
-rw-r--r--drivers/s390/crypto/ap_card.c38
-rw-r--r--drivers/s390/crypto/ap_debug.h4
-rw-r--r--drivers/s390/crypto/ap_queue.c717
-rw-r--r--drivers/s390/crypto/pkey_api.c2486
-rw-r--r--drivers/s390/crypto/pkey_base.c380
-rw-r--r--drivers/s390/crypto/pkey_base.h240
-rw-r--r--drivers/s390/crypto/pkey_cca.c625
-rw-r--r--drivers/s390/crypto/pkey_ep11.c571
-rw-r--r--drivers/s390/crypto/pkey_pckmo.c473
-rw-r--r--drivers/s390/crypto/pkey_sysfs.c646
-rw-r--r--drivers/s390/crypto/pkey_uv.c317
-rw-r--r--drivers/s390/crypto/vfio_ap_drv.c26
-rw-r--r--drivers/s390/crypto/vfio_ap_ops.c1211
-rw-r--r--drivers/s390/crypto/vfio_ap_private.h26
-rw-r--r--drivers/s390/crypto/zcrypt_api.c871
-rw-r--r--drivers/s390/crypto/zcrypt_api.h28
-rw-r--r--drivers/s390/crypto/zcrypt_card.c12
-rw-r--r--drivers/s390/crypto/zcrypt_cca_key.h37
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.c790
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.h54
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c227
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.h134
-rw-r--r--drivers/s390/crypto/zcrypt_cex2c.c425
-rw-r--r--drivers/s390/crypto/zcrypt_cex2c.h18
-rw-r--r--drivers/s390/crypto/zcrypt_cex4.c215
-rw-r--r--drivers/s390/crypto/zcrypt_debug.h4
-rw-r--r--drivers/s390/crypto/zcrypt_ep11misc.c946
-rw-r--r--drivers/s390/crypto/zcrypt_ep11misc.h57
-rw-r--r--drivers/s390/crypto/zcrypt_error.h23
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c136
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.h3
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c359
-rw-r--r--drivers/s390/crypto/zcrypt_queue.c11
-rw-r--r--drivers/s390/net/Kconfig28
-rw-r--r--drivers/s390/net/Makefile2
-rw-r--r--drivers/s390/net/ctcm_dbug.c2
-rw-r--r--drivers/s390/net/ctcm_fsms.c53
-rw-r--r--drivers/s390/net/ctcm_main.c46
-rw-r--r--drivers/s390/net/ctcm_main.h1
-rw-r--r--drivers/s390/net/ctcm_mpc.c70
-rw-r--r--drivers/s390/net/ctcm_sysfs.c49
-rw-r--r--drivers/s390/net/fsm.c11
-rw-r--r--drivers/s390/net/ism.h55
-rw-r--r--drivers/s390/net/ism_drv.c340
-rw-r--r--drivers/s390/net/lcs.c2414
-rw-r--r--drivers/s390/net/lcs.h342
-rw-r--r--drivers/s390/net/netiucv.c2102
-rw-r--r--drivers/s390/net/qeth_core.h10
-rw-r--r--drivers/s390/net/qeth_core_main.c166
-rw-r--r--drivers/s390/net/qeth_core_mpc.c247
-rw-r--r--drivers/s390/net/qeth_core_mpc.h20
-rw-r--r--drivers/s390/net/qeth_core_sys.c103
-rw-r--r--drivers/s390/net/qeth_ethtool.c15
-rw-r--r--drivers/s390/net/qeth_l2_main.c71
-rw-r--r--drivers/s390/net/qeth_l2_sys.c28
-rw-r--r--drivers/s390/net/qeth_l3_main.c28
-rw-r--r--drivers/s390/net/qeth_l3_sys.c85
-rw-r--r--drivers/s390/net/smsgiucv.c4
-rw-r--r--drivers/s390/net/smsgiucv_app.c34
-rw-r--r--drivers/s390/scsi/zfcp_aux.c30
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c3
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c49
-rw-r--r--drivers/s390/scsi/zfcp_def.h6
-rw-r--r--drivers/s390/scsi/zfcp_erp.c7
-rw-r--r--drivers/s390/scsi/zfcp_ext.h5
-rw-r--r--drivers/s390/scsi/zfcp_fc.c35
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c37
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c12
-rw-r--r--drivers/s390/scsi/zfcp_qdio.h8
-rw-r--r--drivers/s390/scsi/zfcp_reqlist.h26
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c22
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c89
-rw-r--r--drivers/s390/scsi/zfcp_unit.c2
-rw-r--r--drivers/s390/virtio/virtio_ccw.c234
-rw-r--r--drivers/sbus/char/bbc_envctrl.c2
-rw-r--r--drivers/sbus/char/bbc_i2c.c11
-rw-r--r--drivers/sbus/char/bbc_i2c.h4
-rw-r--r--drivers/sbus/char/display7seg.c11
-rw-r--r--drivers/sbus/char/envctrl.c11
-rw-r--r--drivers/sbus/char/flash.c7
-rw-r--r--drivers/sbus/char/openprom.c3
-rw-r--r--drivers/sbus/char/oradax.c25
-rw-r--r--drivers/sbus/char/uctrl.c7
-rw-r--r--drivers/scsi/3w-9xxx.c62
-rw-r--r--drivers/scsi/3w-sas.c73
-rw-r--r--drivers/scsi/3w-sas.h4
-rw-r--r--drivers/scsi/3w-xxxx.c76
-rw-r--r--drivers/scsi/3w-xxxx.h2
-rw-r--r--drivers/scsi/53c700.c23
-rw-r--r--drivers/scsi/BusLogic.c28
-rw-r--r--drivers/scsi/BusLogic.h5
-rw-r--r--drivers/scsi/FlashPoint.c1
-rw-r--r--drivers/scsi/Kconfig79
-rw-r--r--drivers/scsi/Makefile3
-rw-r--r--drivers/scsi/NCR5380.c237
-rw-r--r--drivers/scsi/NCR5380.h20
-rw-r--r--drivers/scsi/a100u2w.c4
-rw-r--r--drivers/scsi/a2091.c2
-rw-r--r--drivers/scsi/a3000.c17
-rw-r--r--drivers/scsi/a4000t.c15
-rw-r--r--drivers/scsi/aacraid/aachba.c47
-rw-r--r--drivers/scsi/aacraid/aacraid.h25
-rw-r--r--drivers/scsi/aacraid/commctrl.c4
-rw-r--r--drivers/scsi/aacraid/comminit.c8
-rw-r--r--drivers/scsi/aacraid/commsup.c138
-rw-r--r--drivers/scsi/aacraid/linit.c23
-rw-r--r--drivers/scsi/aacraid/src.c2
-rw-r--r--drivers/scsi/advansys.c33
-rw-r--r--drivers/scsi/aha152x.c19
-rw-r--r--drivers/scsi/aha1542.c9
-rw-r--r--drivers/scsi/aha1740.c5
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic79xx77
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx99
-rw-r--r--drivers/scsi/aic7xxx/Makefile12
-rw-r--r--drivers/scsi/aic7xxx/aic7770.c15
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c6
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c6
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c52
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.h2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c141
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c3
-rw-r--r--drivers/scsi/aic7xxx/aicasm/Makefile18
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_gram.y1
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y1
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_scan.l3
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c1
-rw-r--r--drivers/scsi/aic94xx/Kconfig2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_hwi.c6
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c43
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_task.c16
-rw-r--r--drivers/scsi/aic94xx/aic94xx_tmf.c10
-rw-r--r--drivers/scsi/am53c974.c4
-rw-r--r--drivers/scsi/arcmsr/arcmsr.h30
-rw-r--r--drivers/scsi/arcmsr/arcmsr_attr.c6
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c170
-rw-r--r--drivers/scsi/arm/acornscsi.c13
-rw-r--r--drivers/scsi/arm/arxescsi.c2
-rw-r--r--drivers/scsi/arm/cumana_1.c2
-rw-r--r--drivers/scsi/arm/cumana_2.c4
-rw-r--r--drivers/scsi/arm/eesox.c4
-rw-r--r--drivers/scsi/arm/fas216.c8
-rw-r--r--drivers/scsi/arm/oak.c2
-rw-r--r--drivers/scsi/arm/powertec.c4
-rw-r--r--drivers/scsi/atari_scsi.c12
-rw-r--r--drivers/scsi/atp870u.c10
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c2
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c4
-rw-r--r--drivers/scsi/be2iscsi/be_main.c47
-rw-r--r--drivers/scsi/be2iscsi/be_main.h1
-rw-r--r--drivers/scsi/bfa/bfa.h40
-rw-r--r--drivers/scsi/bfa/bfa_core.c40
-rw-r--r--drivers/scsi/bfa/bfa_cs.h21
-rw-r--r--drivers/scsi/bfa/bfa_defs_fcs.h22
-rw-r--r--drivers/scsi/bfa/bfa_fc.h4
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.c486
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.h72
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.c60
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.h67
-rw-r--r--drivers/scsi/bfa/bfa_fcs.c6
-rw-r--r--drivers/scsi/bfa/bfa_fcs.h324
-rw-r--r--drivers/scsi/bfa/bfa_fcs_fcpim.c23
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c274
-rw-r--r--drivers/scsi/bfa/bfa_fcs_rport.c70
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c108
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h86
-rw-r--r--drivers/scsi/bfa/bfa_modules.h1
-rw-r--r--drivers/scsi/bfa/bfa_svc.c146
-rw-r--r--drivers/scsi/bfa/bfa_svc.h120
-rw-r--r--drivers/scsi/bfa/bfad.c55
-rw-r--r--drivers/scsi/bfa/bfad_attr.c30
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c44
-rw-r--r--drivers/scsi/bfa/bfad_debugfs.c4
-rw-r--r--drivers/scsi/bfa/bfad_drv.h33
-rw-r--r--drivers/scsi/bfa/bfad_im.c34
-rw-r--r--drivers/scsi/bfa/bfad_im.h27
-rw-r--r--drivers/scsi/bfa/bfi.h2
-rw-r--r--drivers/scsi/bnx2fc/Kconfig1
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h7
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c49
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c14
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c94
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_tgt.c14
-rw-r--r--drivers/scsi/bnx2i/Kconfig1
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h11
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c2
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c9
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c12
-rw-r--r--drivers/scsi/bvme6000_scsi.c4
-rw-r--r--drivers/scsi/ch.c92
-rw-r--r--drivers/scsi/csiostor/csio_defs.h18
-rw-r--r--drivers/scsi/csiostor/csio_hw.c8
-rw-r--r--drivers/scsi/csiostor/csio_init.c10
-rw-r--r--drivers/scsi/csiostor/csio_lnode.c10
-rw-r--r--drivers/scsi/csiostor/csio_lnode.h13
-rw-r--r--drivers/scsi/csiostor/csio_mb.c4
-rw-r--r--drivers/scsi/csiostor/csio_scsi.c32
-rw-r--r--drivers/scsi/csiostor/csio_wr.c7
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.c6
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c8
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c8
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.h6
-rw-r--r--drivers/scsi/cxlflash/Kconfig13
-rw-r--r--drivers/scsi/cxlflash/Makefile5
-rw-r--r--drivers/scsi/cxlflash/backend.h48
-rw-r--r--drivers/scsi/cxlflash/common.h340
-rw-r--r--drivers/scsi/cxlflash/cxl_hw.c177
-rw-r--r--drivers/scsi/cxlflash/lunmgt.c278
-rw-r--r--drivers/scsi/cxlflash/main.c3967
-rw-r--r--drivers/scsi/cxlflash/main.h129
-rw-r--r--drivers/scsi/cxlflash/ocxl_hw.c1399
-rw-r--r--drivers/scsi/cxlflash/ocxl_hw.h72
-rw-r--r--drivers/scsi/cxlflash/sislite.h560
-rw-r--r--drivers/scsi/cxlflash/superpipe.c2218
-rw-r--r--drivers/scsi/cxlflash/superpipe.h153
-rw-r--r--drivers/scsi/cxlflash/vlun.c1337
-rw-r--r--drivers/scsi/cxlflash/vlun.h82
-rw-r--r--drivers/scsi/dc395x.c727
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c133
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c13
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c138
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c108
-rw-r--r--drivers/scsi/dmx3191d.c4
-rw-r--r--drivers/scsi/elx/efct/efct_driver.c8
-rw-r--r--drivers/scsi/elx/efct/efct_hw.c5
-rw-r--r--drivers/scsi/elx/efct/efct_lio.c30
-rw-r--r--drivers/scsi/elx/efct/efct_xport.c6
-rw-r--r--drivers/scsi/elx/libefc/efc_els.c2
-rw-r--r--drivers/scsi/elx/libefc/efc_fabric.c4
-rw-r--r--drivers/scsi/elx/libefc/efc_node.c2
-rw-r--r--drivers/scsi/elx/libefc/efc_node.h12
-rw-r--r--drivers/scsi/elx/libefc/efc_nport.c2
-rw-r--r--drivers/scsi/elx/libefc/efclib.h6
-rw-r--r--drivers/scsi/elx/libefc_sli/sli4.c16
-rw-r--r--drivers/scsi/esas2r/atioctl.h1
-rw-r--r--drivers/scsi/esas2r/esas2r.h17
-rw-r--r--drivers/scsi/esas2r/esas2r_init.c7
-rw-r--r--drivers/scsi/esas2r/esas2r_ioctl.c21
-rw-r--r--drivers/scsi/esas2r/esas2r_main.c49
-rw-r--r--drivers/scsi/esas2r/esas2r_vda.c17
-rw-r--r--drivers/scsi/esp_scsi.c18
-rw-r--r--drivers/scsi/esp_scsi.h4
-rw-r--r--drivers/scsi/fcoe/fcoe.c35
-rw-r--r--drivers/scsi/fcoe/fcoe_ctlr.c10
-rw-r--r--drivers/scsi/fcoe/fcoe_sysfs.c79
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c10
-rw-r--r--drivers/scsi/fdomain.c6
-rw-r--r--drivers/scsi/fdomain_pci.c2
-rw-r--r--drivers/scsi/fnic/Makefile5
-rw-r--r--drivers/scsi/fnic/fdls_disc.c5093
-rw-r--r--drivers/scsi/fnic/fdls_fc.h253
-rw-r--r--drivers/scsi/fnic/fip.c1005
-rw-r--r--drivers/scsi/fnic/fip.h159
-rw-r--r--drivers/scsi/fnic/fnic.h360
-rw-r--r--drivers/scsi/fnic/fnic_attrs.c17
-rw-r--r--drivers/scsi/fnic/fnic_debugfs.c22
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c1741
-rw-r--r--drivers/scsi/fnic/fnic_fdls.h435
-rw-r--r--drivers/scsi/fnic/fnic_fip.h48
-rw-r--r--drivers/scsi/fnic/fnic_io.h16
-rw-r--r--drivers/scsi/fnic/fnic_isr.c170
-rw-r--r--drivers/scsi/fnic/fnic_main.c793
-rw-r--r--drivers/scsi/fnic/fnic_pci_subsys_devid.c131
-rw-r--r--drivers/scsi/fnic/fnic_res.c86
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c1863
-rw-r--r--drivers/scsi/fnic/fnic_stats.h52
-rw-r--r--drivers/scsi/fnic/fnic_trace.c180
-rw-r--r--drivers/scsi/fnic/vnic_dev.c4
-rw-r--r--drivers/scsi/fnic/vnic_scsi.h13
-rw-r--r--drivers/scsi/g_NCR5380.c5
-rw-r--r--drivers/scsi/gvp11.c7
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas.h78
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c476
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v1_hw.c76
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c137
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c1096
-rw-r--r--drivers/scsi/hosts.c81
-rw-r--r--drivers/scsi/hpsa.c128
-rw-r--r--drivers/scsi/hptiop.c21
-rw-r--r--drivers/scsi/hptiop.h4
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c601
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h52
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c25
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c80
-rw-r--r--drivers/scsi/ibmvscsi_tgt/libsrp.c6
-rw-r--r--drivers/scsi/imm.c87
-rw-r--r--drivers/scsi/imm.h4
-rw-r--r--drivers/scsi/initio.c13
-rw-r--r--drivers/scsi/ipr.c1014
-rw-r--r--drivers/scsi/ipr.h70
-rw-r--r--drivers/scsi/ips.c45
-rw-r--r--drivers/scsi/ips.h5
-rw-r--r--drivers/scsi/isci/host.c18
-rw-r--r--drivers/scsi/isci/host.h2
-rw-r--r--drivers/scsi/isci/init.c61
-rw-r--r--drivers/scsi/isci/isci.h15
-rw-r--r--drivers/scsi/isci/phy.c4
-rw-r--r--drivers/scsi/isci/port.c2
-rw-r--r--drivers/scsi/isci/port_config.c4
-rw-r--r--drivers/scsi/isci/remote_device.c61
-rw-r--r--drivers/scsi/isci/remote_device.h34
-rw-r--r--drivers/scsi/isci/request.c11
-rw-r--r--drivers/scsi/isci/request.h2
-rw-r--r--drivers/scsi/isci/scu_task_context.h2
-rw-r--r--drivers/scsi/isci/task.c4
-rw-r--r--drivers/scsi/isci/task.h10
-rw-r--r--drivers/scsi/iscsi_tcp.c198
-rw-r--r--drivers/scsi/iscsi_tcp.h9
-rw-r--r--drivers/scsi/jazz_esp.c8
-rw-r--r--drivers/scsi/libfc/fc_disc.c7
-rw-r--r--drivers/scsi/libfc/fc_elsct.c2
-rw-r--r--drivers/scsi/libfc/fc_encode.h18
-rw-r--r--drivers/scsi/libfc/fc_exch.c3
-rw-r--r--drivers/scsi/libfc/fc_fcp.c38
-rw-r--r--drivers/scsi/libfc/fc_lport.c8
-rw-r--r--drivers/scsi/libfc/fc_rport.c5
-rw-r--r--drivers/scsi/libiscsi.c82
-rw-r--r--drivers/scsi/libiscsi_tcp.c91
-rw-r--r--drivers/scsi/libsas/sas_ata.c293
-rw-r--r--drivers/scsi/libsas/sas_discover.c49
-rw-r--r--drivers/scsi/libsas/sas_expander.c395
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c4
-rw-r--r--drivers/scsi/libsas/sas_init.c27
-rw-r--r--drivers/scsi/libsas/sas_internal.h147
-rw-r--r--drivers/scsi/libsas/sas_phy.c14
-rw-r--r--drivers/scsi/libsas/sas_port.c21
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c78
-rw-r--r--drivers/scsi/lpfc/lpfc.h358
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c1015
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c297
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.h19
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h26
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c1474
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c770
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h18
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h79
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c2116
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c1084
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h153
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h178
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c1447
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h10
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c52
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c56
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c680
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.c206
-rw-r--r--drivers/scsi/lpfc/lpfc_nvmet.c63
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c410
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h32
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c1719
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h28
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h42
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h6
-rw-r--r--drivers/scsi/lpfc/lpfc_vmid.c47
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c231
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.h6
-rw-r--r--drivers/scsi/mac53c94.c7
-rw-r--r--drivers/scsi/mac_esp.c6
-rw-r--r--drivers/scsi/mac_scsi.c182
-rw-r--r--drivers/scsi/megaraid.c74
-rw-r--r--drivers/scsi/megaraid.h2
-rw-r--r--drivers/scsi/megaraid/Kconfig.megaraid119
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c28
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.c6
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h30
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c208
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fp.c8
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c36
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.h30
-rw-r--r--drivers/scsi/mesh.c10
-rw-r--r--drivers/scsi/mpi3mr/Kconfig1
-rw-r--r--drivers/scsi/mpi3mr/Makefile3
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h369
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_image.h47
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_init.h37
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_ioc.h83
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_pci.h8
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_sas.h4
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_tool.h45
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_transport.h28
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr.h523
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_app.c1914
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_debug.h29
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c1953
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_os.c1566
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_transport.c3354
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2.h11
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h239
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_image.h32
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_ioc.h81
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c508
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.h66
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_config.c153
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.c391
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.h59
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c202
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_transport.c58
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_trigger_pages.h44
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_warpdrive.c5
-rw-r--r--drivers/scsi/mvme147.c2
-rw-r--r--drivers/scsi/mvme16x_scsi.c4
-rw-r--r--drivers/scsi/mvsas/Kconfig2
-rw-r--r--drivers/scsi/mvsas/mv_64xx.h4
-rw-r--r--drivers/scsi/mvsas/mv_defs.h5
-rw-r--r--drivers/scsi/mvsas/mv_init.c60
-rw-r--r--drivers/scsi/mvsas/mv_sas.c88
-rw-r--r--drivers/scsi/mvsas/mv_sas.h11
-rw-r--r--drivers/scsi/mvumi.c21
-rw-r--r--drivers/scsi/mvumi.h6
-rw-r--r--drivers/scsi/myrb.c56
-rw-r--r--drivers/scsi/myrb.h1
-rw-r--r--drivers/scsi/myrs.c54
-rw-r--r--drivers/scsi/myrs.h1
-rw-r--r--drivers/scsi/ncr53c8xx.c13
-rw-r--r--drivers/scsi/nsp32.c4
-rw-r--r--drivers/scsi/pcmcia/Kconfig6
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c1
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c4
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c6
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.c83
-rw-r--r--drivers/scsi/pm8001/pm8001_defs.h9
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c316
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.h6
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c421
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c200
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h55
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.c552
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.h6
-rw-r--r--drivers/scsi/pmcraid.c139
-rw-r--r--drivers/scsi/ppa.c97
-rw-r--r--drivers/scsi/ppa.h4
-rw-r--r--drivers/scsi/ps3rom.c7
-rw-r--r--drivers/scsi/qedf/qedf.h7
-rw-r--r--drivers/scsi/qedf/qedf_attr.c6
-rw-r--r--drivers/scsi/qedf/qedf_dbg.h4
-rw-r--r--drivers/scsi/qedf/qedf_debugfs.c37
-rw-r--r--drivers/scsi/qedf/qedf_io.c93
-rw-r--r--drivers/scsi/qedf/qedf_main.c159
-rw-r--r--drivers/scsi/qedi/qedi_dbg.c22
-rw-r--r--drivers/scsi/qedi/qedi_dbg.h13
-rw-r--r--drivers/scsi/qedi/qedi_debugfs.c12
-rw-r--r--drivers/scsi/qedi/qedi_gbl.h5
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.c2
-rw-r--r--drivers/scsi/qedi/qedi_main.c57
-rw-r--r--drivers/scsi/qla1280.c96
-rw-r--r--drivers/scsi/qla1280.h12
-rw-r--r--drivers/scsi/qla2xxx/Kconfig44
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c128
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c207
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c103
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h47
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h192
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c252
-rw-r--r--drivers/scsi/qla2xxx/qla_dsd.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_edif.c115
-rw-r--r--drivers/scsi/qla2xxx/qla_edif.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_edif_bsg.h15
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h82
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c966
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c772
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h187
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c538
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c322
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c69
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c313
-rw-r--r--drivers/scsi/qla2xxx/qla_mr.c22
-rw-r--r--drivers/scsi/qla2xxx/qla_mr.h4
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.c460
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.h17
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c50
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.h4
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c512
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c112
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c2110
-rw-r--r--drivers/scsi/qla2xxx/qla_target.h122
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h6
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c56
-rw-r--r--drivers/scsi/qla4xxx/ql4_attr.c8
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h1
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c29
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c5
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c83
-rw-r--r--drivers/scsi/qlogicfas408.c2
-rw-r--r--drivers/scsi/qlogicfas408.h2
-rw-r--r--drivers/scsi/qlogicpti.c31
-rw-r--r--drivers/scsi/raid_class.c47
-rw-r--r--drivers/scsi/scsi.c300
-rw-r--r--drivers/scsi/scsi_bsg.c4
-rw-r--r--drivers/scsi/scsi_common.c49
-rw-r--r--drivers/scsi/scsi_debug.c3630
-rw-r--r--drivers/scsi/scsi_debugfs.c38
-rw-r--r--drivers/scsi/scsi_devinfo.c79
-rw-r--r--drivers/scsi/scsi_error.c209
-rw-r--r--drivers/scsi/scsi_ioctl.c108
-rw-r--r--drivers/scsi/scsi_lib.c695
-rw-r--r--drivers/scsi/scsi_lib_test.c337
-rw-r--r--drivers/scsi/scsi_logging.c21
-rw-r--r--drivers/scsi/scsi_pm.c1
-rw-r--r--drivers/scsi/scsi_priv.h33
-rw-r--r--drivers/scsi/scsi_proc.c167
-rw-r--r--drivers/scsi/scsi_proto_test.c56
-rw-r--r--drivers/scsi/scsi_scan.c397
-rw-r--r--drivers/scsi/scsi_sysctl.c23
-rw-r--r--drivers/scsi/scsi_sysfs.c200
-rw-r--r--drivers/scsi/scsi_trace.c24
-rw-r--r--drivers/scsi/scsi_transport_fc.c133
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c236
-rw-r--r--drivers/scsi/scsi_transport_sas.c112
-rw-r--r--drivers/scsi/scsi_transport_spi.c74
-rw-r--r--drivers/scsi/scsi_transport_srp.c16
-rw-r--r--drivers/scsi/scsicam.c18
-rw-r--r--drivers/scsi/sd.c1570
-rw-r--r--drivers/scsi/sd.h56
-rw-r--r--drivers/scsi/sd_dif.c46
-rw-r--r--drivers/scsi/sd_trace.h84
-rw-r--r--drivers/scsi/sd_zbc.c443
-rw-r--r--drivers/scsi/ses.c151
-rw-r--r--drivers/scsi/sg.c129
-rw-r--r--drivers/scsi/sgiwd93.c5
-rw-r--r--drivers/scsi/sim710.c2
-rw-r--r--drivers/scsi/smartpqi/Kconfig2
-rw-r--r--drivers/scsi/smartpqi/smartpqi.h71
-rw-r--r--drivers/scsi/smartpqi/smartpqi_init.c1493
-rw-r--r--drivers/scsi/smartpqi/smartpqi_sas_transport.c36
-rw-r--r--drivers/scsi/smartpqi/smartpqi_sis.c64
-rw-r--r--drivers/scsi/smartpqi/smartpqi_sis.h5
-rw-r--r--drivers/scsi/sni_53c710.c4
-rw-r--r--drivers/scsi/snic/snic_attrs.c11
-rw-r--r--drivers/scsi/snic/snic_debugfs.c14
-rw-r--r--drivers/scsi/snic/snic_disc.c7
-rw-r--r--drivers/scsi/snic/snic_main.c26
-rw-r--r--drivers/scsi/snic/snic_scsi.c21
-rw-r--r--drivers/scsi/snic/snic_trc.c5
-rw-r--r--drivers/scsi/sr.c119
-rw-r--r--drivers/scsi/sr.h2
-rw-r--r--drivers/scsi/sr_ioctl.c22
-rw-r--r--drivers/scsi/st.c230
-rw-r--r--drivers/scsi/st.h7
-rw-r--r--drivers/scsi/stex.c39
-rw-r--r--drivers/scsi/storvsc_drv.c301
-rw-r--r--drivers/scsi/sun3_scsi.c14
-rw-r--r--drivers/scsi/sun3x_esp.c8
-rw-r--r--drivers/scsi/sun_esp.c13
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c216
-rw-r--r--drivers/scsi/virtio_scsi.c139
-rw-r--r--drivers/scsi/vmw_pvscsi.c5
-rw-r--r--drivers/scsi/wd33c93.c66
-rw-r--r--drivers/scsi/wd33c93.h5
-rw-r--r--drivers/scsi/wd719x.c4
-rw-r--r--drivers/scsi/xen-scsifront.c31
-rw-r--r--drivers/scsi/zalon.c2
-rw-r--r--drivers/scsi/zorro_esp.c2
-rw-r--r--drivers/sh/Makefile1
-rw-r--r--drivers/sh/clk/core.c12
-rw-r--r--drivers/sh/clk/cpg.c25
-rw-r--r--drivers/sh/intc/core.c16
-rw-r--r--drivers/sh/intc/internals.h2
-rw-r--r--drivers/sh/intc/irqdomain.c5
-rw-r--r--drivers/sh/intc/userimask.c15
-rw-r--r--drivers/sh/intc/virq-debugfs.c1
-rw-r--r--drivers/sh/maple/maple.c15
-rw-r--r--drivers/sh/superhyway/Makefile8
-rw-r--r--drivers/sh/superhyway/superhyway-sysfs.c54
-rw-r--r--drivers/sh/superhyway/superhyway.c234
-rw-r--r--drivers/siox/siox-bus-gpio.c60
-rw-r--r--drivers/siox/siox-core.c56
-rw-r--r--drivers/siox/siox.h4
-rw-r--r--drivers/slimbus/Kconfig10
-rw-r--r--drivers/slimbus/Makefile3
-rw-r--r--drivers/slimbus/core.c31
-rw-r--r--drivers/slimbus/messaging.c20
-rw-r--r--drivers/slimbus/qcom-ctrl.c737
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c184
-rw-r--r--drivers/slimbus/stream.c33
-rw-r--r--drivers/soc/Kconfig8
-rw-r--r--drivers/soc/Makefile12
-rw-r--r--drivers/soc/actions/Makefile4
-rw-r--r--drivers/soc/amlogic/Kconfig35
-rw-r--r--drivers/soc/amlogic/Makefile3
-rw-r--r--drivers/soc/amlogic/meson-canvas.c13
-rw-r--r--drivers/soc/amlogic/meson-clk-measure.c462
-rw-r--r--drivers/soc/amlogic/meson-gx-pwrc-vpu.c379
-rw-r--r--drivers/soc/amlogic/meson-gx-socinfo.c23
-rw-r--r--drivers/soc/amlogic/meson-secure-pwrc.c231
-rw-r--r--drivers/soc/apple/Kconfig23
-rw-r--r--drivers/soc/apple/Makefile4
-rw-r--r--drivers/soc/apple/apple-pmgr-pwrstate.c324
-rw-r--r--drivers/soc/apple/mailbox.c463
-rw-r--r--drivers/soc/apple/mailbox.h48
-rw-r--r--drivers/soc/apple/rtkit-crashlog.c93
-rw-r--r--drivers/soc/apple/rtkit-internal.h9
-rw-r--r--drivers/soc/apple/rtkit.c295
-rw-r--r--drivers/soc/apple/sart.c68
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-ctrl.c18
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-snoop.c230
-rw-r--r--drivers/soc/aspeed/aspeed-p2a-ctrl.c20
-rw-r--r--drivers/soc/aspeed/aspeed-socinfo.c5
-rw-r--r--drivers/soc/aspeed/aspeed-uart-routing.c9
-rw-r--r--drivers/soc/atmel/sfr.c1
-rw-r--r--drivers/soc/atmel/soc.c39
-rw-r--r--drivers/soc/atmel/soc.h15
-rw-r--r--drivers/soc/bcm/Kconfig33
-rw-r--r--drivers/soc/bcm/Makefile3
-rw-r--r--drivers/soc/bcm/bcm63xx/Kconfig21
-rw-r--r--drivers/soc/bcm/bcm63xx/Makefile3
-rw-r--r--drivers/soc/bcm/brcmstb/Kconfig4
-rw-r--r--drivers/soc/bcm/brcmstb/biuctrl.c17
-rw-r--r--drivers/soc/bcm/brcmstb/pm/Makefile2
-rw-r--r--drivers/soc/bcm/brcmstb/pm/aon_defs.h105
-rw-r--r--drivers/soc/bcm/brcmstb/pm/pm-arm.c862
-rw-r--r--drivers/soc/bcm/brcmstb/pm/pm.h2
-rw-r--r--drivers/soc/bcm/brcmstb/pm/s2-arm.S68
-rw-r--r--drivers/soc/canaan/Kconfig9
-rw-r--r--drivers/soc/cirrus/Kconfig17
-rw-r--r--drivers/soc/cirrus/Makefile2
-rw-r--r--drivers/soc/cirrus/soc-ep93xx.c252
-rw-r--r--drivers/soc/dove/pmu.c12
-rw-r--r--drivers/soc/fsl/Kconfig4
-rw-r--r--drivers/soc/fsl/dpaa2-console.c7
-rw-r--r--drivers/soc/fsl/dpio/dpio-driver.c9
-rw-r--r--drivers/soc/fsl/dpio/dpio-service.c4
-rw-r--r--drivers/soc/fsl/qbman/Kconfig2
-rw-r--r--drivers/soc/fsl/qbman/bman_ccsr.c27
-rw-r--r--drivers/soc/fsl/qbman/dpaa_sys.c20
-rw-r--r--drivers/soc/fsl/qbman/dpaa_sys.h4
-rw-r--r--drivers/soc/fsl/qbman/qman.c34
-rw-r--r--drivers/soc/fsl/qbman/qman_ccsr.c73
-rw-r--r--drivers/soc/fsl/qbman/qman_portal.c5
-rw-r--r--drivers/soc/fsl/qbman/qman_test_stash.c10
-rw-r--r--drivers/soc/fsl/qe/Kconfig29
-rw-r--r--drivers/soc/fsl/qe/Makefile2
-rw-r--r--drivers/soc/fsl/qe/gpio.c226
-rw-r--r--drivers/soc/fsl/qe/qe.c9
-rw-r--r--drivers/soc/fsl/qe/qe_common.c85
-rw-r--r--drivers/soc/fsl/qe/qe_ic.c24
-rw-r--r--drivers/soc/fsl/qe/qe_tdm.c4
-rw-r--r--drivers/soc/fsl/qe/qmc.c2269
-rw-r--r--drivers/soc/fsl/qe/tsa.c1168
-rw-r--r--drivers/soc/fsl/qe/tsa.h45
-rw-r--r--drivers/soc/fsl/qe/ucc.c1
-rw-r--r--drivers/soc/fsl/rcpm.c1
-rw-r--r--drivers/soc/fujitsu/a64fx-diag.c5
-rw-r--r--drivers/soc/hisilicon/Kconfig24
-rw-r--r--drivers/soc/hisilicon/Makefile2
-rw-r--r--drivers/soc/hisilicon/kunpeng_hccs.c1834
-rw-r--r--drivers/soc/hisilicon/kunpeng_hccs.h233
-rw-r--r--drivers/soc/imx/Kconfig18
-rw-r--r--drivers/soc/imx/Makefile5
-rw-r--r--drivers/soc/imx/imx93-src.c32
-rw-r--r--drivers/soc/imx/soc-imx8m.c278
-rw-r--r--drivers/soc/imx/soc-imx9.c128
-rw-r--r--drivers/soc/ixp4xx/ixp4xx-npe.c11
-rw-r--r--drivers/soc/ixp4xx/ixp4xx-qmgr.c4
-rw-r--r--drivers/soc/litex/Kconfig2
-rw-r--r--drivers/soc/litex/litex_soc_ctrl.c28
-rw-r--r--drivers/soc/loongson/Kconfig29
-rw-r--r--drivers/soc/loongson/Makefile7
-rw-r--r--drivers/soc/loongson/loongson2_guts.c191
-rw-r--r--drivers/soc/loongson/loongson2_pm.c220
-rw-r--r--drivers/soc/mediatek/Kconfig50
-rw-r--r--drivers/soc/mediatek/Makefile5
-rw-r--r--drivers/soc/mediatek/mt8167-mmsys.h31
-rw-r--r--drivers/soc/mediatek/mt8173-mmsys.h80
-rw-r--r--drivers/soc/mediatek/mt8183-mmsys.h50
-rw-r--r--drivers/soc/mediatek/mt8183-pm-domains.h266
-rw-r--r--drivers/soc/mediatek/mt8186-mmsys.h96
-rw-r--r--drivers/soc/mediatek/mt8186-pm-domains.h344
-rw-r--r--drivers/soc/mediatek/mt8188-mmsys.h327
-rw-r--r--drivers/soc/mediatek/mt8192-mmsys.h71
-rw-r--r--drivers/soc/mediatek/mt8195-mmsys.h645
-rw-r--r--drivers/soc/mediatek/mt8365-mmsys.h84
-rw-r--r--drivers/soc/mediatek/mtk-cmdq-helper.c387
-rw-r--r--drivers/soc/mediatek/mtk-devapc.c36
-rw-r--r--drivers/soc/mediatek/mtk-dvfsrc.c578
-rw-r--r--drivers/soc/mediatek/mtk-mmsys.c449
-rw-r--r--drivers/soc/mediatek/mtk-mmsys.h56
-rw-r--r--drivers/soc/mediatek/mtk-mutex.c535
-rw-r--r--drivers/soc/mediatek/mtk-pm-domains.c675
-rw-r--r--drivers/soc/mediatek/mtk-pm-domains.h106
-rw-r--r--drivers/soc/mediatek/mtk-pmic-wrap.c463
-rw-r--r--drivers/soc/mediatek/mtk-regulator-coupler.c160
-rw-r--r--drivers/soc/mediatek/mtk-socinfo.c212
-rw-r--r--drivers/soc/mediatek/mtk-svs.c2123
-rw-r--r--drivers/soc/microchip/Kconfig15
-rw-r--r--drivers/soc/microchip/Makefile1
-rw-r--r--drivers/soc/microchip/mpfs-control-scb.c38
-rw-r--r--drivers/soc/microchip/mpfs-mss-top-sysreg.c44
-rw-r--r--drivers/soc/microchip/mpfs-sys-controller.c93
-rw-r--r--drivers/soc/nuvoton/Kconfig11
-rw-r--r--drivers/soc/nuvoton/Makefile2
-rw-r--r--drivers/soc/nuvoton/wpcm450-soc.c109
-rw-r--r--drivers/soc/pxa/ssp.c20
-rw-r--r--drivers/soc/qcom/Kconfig135
-rw-r--r--drivers/soc/qcom/Makefile19
-rw-r--r--drivers/soc/qcom/apr.c35
-rw-r--r--drivers/soc/qcom/cmd-db.c55
-rw-r--r--drivers/soc/qcom/icc-bwmon.c707
-rw-r--r--drivers/soc/qcom/ice.c754
-rw-r--r--drivers/soc/qcom/kryo-l2-accessors.c4
-rw-r--r--drivers/soc/qcom/llcc-qcom.c4723
-rw-r--r--drivers/soc/qcom/mdt_loader.c174
-rw-r--r--drivers/soc/qcom/ocmem.c107
-rw-r--r--drivers/soc/qcom/pdr_interface.c62
-rw-r--r--drivers/soc/qcom/pdr_internal.h317
-rw-r--r--drivers/soc/qcom/pmic_glink.c431
-rw-r--r--drivers/soc/qcom/pmic_glink_altmode.c558
-rw-r--r--drivers/soc/qcom/pmic_pdcharger_ulog.c170
-rw-r--r--drivers/soc/qcom/pmic_pdcharger_ulog.h36
-rw-r--r--drivers/soc/qcom/qcom-geni-se.c721
-rw-r--r--drivers/soc/qcom/qcom-pbs.c228
-rw-r--r--drivers/soc/qcom/qcom_aoss.c175
-rw-r--r--drivers/soc/qcom/qcom_gsbi.c23
-rw-r--r--drivers/soc/qcom/qcom_pd_mapper.c731
-rw-r--r--drivers/soc/qcom/qcom_pdr_msg.c352
-rw-r--r--drivers/soc/qcom/qcom_stats.c162
-rw-r--r--drivers/soc/qcom/qmi_encdec.c112
-rw-r--r--drivers/soc/qcom/qmi_interface.c45
-rw-r--r--drivers/soc/qcom/ramp_controller.c345
-rw-r--r--drivers/soc/qcom/rmtfs_mem.c54
-rw-r--r--drivers/soc/qcom/rpm-proc.c77
-rw-r--r--drivers/soc/qcom/rpm_master_stats.c167
-rw-r--r--drivers/soc/qcom/rpmh-internal.h20
-rw-r--r--drivers/soc/qcom/rpmh-rsc.c351
-rw-r--r--drivers/soc/qcom/rpmh.c23
-rw-r--r--drivers/soc/qcom/rpmhpd.c727
-rw-r--r--drivers/soc/qcom/rpmpd.c673
-rw-r--r--drivers/soc/qcom/smd-rpm.c31
-rw-r--r--drivers/soc/qcom/smem.c180
-rw-r--r--drivers/soc/qcom/smem_state.c18
-rw-r--r--drivers/soc/qcom/smp2p.c61
-rw-r--r--drivers/soc/qcom/smsm.c90
-rw-r--r--drivers/soc/qcom/socinfo.c669
-rw-r--r--drivers/soc/qcom/spm.c301
-rw-r--r--drivers/soc/qcom/trace-aoss.h48
-rw-r--r--drivers/soc/qcom/trace-rpmh.h31
-rw-r--r--drivers/soc/qcom/trace-smp2p.h98
-rw-r--r--drivers/soc/qcom/trace_icc-bwmon.h48
-rw-r--r--drivers/soc/qcom/ubwc_config.c317
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c15
-rw-r--r--drivers/soc/renesas/Kconfig511
-rw-r--r--drivers/soc/renesas/Makefile33
-rw-r--r--drivers/soc/renesas/pwc-rzv2m.c143
-rw-r--r--drivers/soc/renesas/r8a7796-sysc.c67
-rw-r--r--drivers/soc/renesas/r9a08g045-sysc.c93
-rw-r--r--drivers/soc/renesas/r9a09g047-sys.c147
-rw-r--r--drivers/soc/renesas/r9a09g056-sys.c144
-rw-r--r--drivers/soc/renesas/r9a09g057-sys.c169
-rw-r--r--drivers/soc/renesas/rcar-rst.c17
-rw-r--r--drivers/soc/renesas/renesas-soc.c85
-rw-r--r--drivers/soc/renesas/rz-sysc.c169
-rw-r--r--drivers/soc/renesas/rz-sysc.h53
-rw-r--r--drivers/soc/rockchip/Kconfig12
-rw-r--r--drivers/soc/rockchip/Makefile1
-rw-r--r--drivers/soc/rockchip/dtpm.c54
-rw-r--r--drivers/soc/rockchip/grf.c86
-rw-r--r--drivers/soc/rockchip/io-domain.c72
-rw-r--r--drivers/soc/rockchip/pm_domains.c1201
-rw-r--r--drivers/soc/samsung/Kconfig31
-rw-r--r--drivers/soc/samsung/Makefile5
-rw-r--r--drivers/soc/samsung/exynos-asv.c11
-rw-r--r--drivers/soc/samsung/exynos-chipid.c36
-rw-r--r--drivers/soc/samsung/exynos-pmu.c434
-rw-r--r--drivers/soc/samsung/exynos-pmu.h41
-rw-r--r--drivers/soc/samsung/exynos-usi.c108
-rw-r--r--drivers/soc/samsung/exynos3250-pmu.c1
-rw-r--r--drivers/soc/samsung/exynos4-pmu.c13
-rw-r--r--drivers/soc/samsung/exynos5250-pmu.c1
-rw-r--r--drivers/soc/samsung/exynos5420-pmu.c1
-rw-r--r--drivers/soc/samsung/gs101-pmu.c446
-rw-r--r--drivers/soc/samsung/pm_domains.c166
-rw-r--r--drivers/soc/samsung/s3c-pm-debug.c79
-rw-r--r--drivers/soc/sifive/Kconfig10
-rw-r--r--drivers/soc/sifive/Makefile3
-rw-r--r--drivers/soc/sifive/sifive_l2_cache.c237
-rw-r--r--drivers/soc/sophgo/Kconfig34
-rw-r--r--drivers/soc/sophgo/Makefile4
-rw-r--r--drivers/soc/sophgo/cv1800-rtcsys.c63
-rw-r--r--drivers/soc/sophgo/sg2044-topsys.c45
-rw-r--r--drivers/soc/sunxi/sunxi_mbus.c2
-rw-r--r--drivers/soc/sunxi/sunxi_sram.c114
-rw-r--r--drivers/soc/tegra/Kconfig43
-rw-r--r--drivers/soc/tegra/Makefile2
-rw-r--r--drivers/soc/tegra/cbb/Makefile9
-rw-r--r--drivers/soc/tegra/cbb/tegra-cbb.c158
-rw-r--r--drivers/soc/tegra/cbb/tegra194-cbb.c2354
-rw-r--r--drivers/soc/tegra/cbb/tegra234-cbb.c1622
-rw-r--r--drivers/soc/tegra/common.c12
-rw-r--r--drivers/soc/tegra/flowctrl.c4
-rw-r--r--drivers/soc/tegra/fuse/fuse-tegra.c264
-rw-r--r--drivers/soc/tegra/fuse/fuse-tegra20.c2
-rw-r--r--drivers/soc/tegra/fuse/fuse-tegra30.c430
-rw-r--r--drivers/soc/tegra/fuse/fuse.h12
-rw-r--r--drivers/soc/tegra/fuse/speedo-tegra210.c63
-rw-r--r--drivers/soc/tegra/fuse/tegra-apbmisc.c147
-rw-r--r--drivers/soc/tegra/pmc.c1312
-rw-r--r--drivers/soc/ti/Kconfig18
-rw-r--r--drivers/soc/ti/Makefile2
-rw-r--r--drivers/soc/ti/k3-ringacc.c79
-rw-r--r--drivers/soc/ti/k3-socinfo.c100
-rw-r--r--drivers/soc/ti/knav_dma.c46
-rw-r--r--drivers/soc/ti/knav_qmss.h2
-rw-r--r--drivers/soc/ti/knav_qmss_acc.c4
-rw-r--r--drivers/soc/ti/knav_qmss_queue.c134
-rw-r--r--drivers/soc/ti/pm33xx.c66
-rw-r--r--drivers/soc/ti/pruss.c449
-rw-r--r--drivers/soc/ti/pruss.h88
-rw-r--r--drivers/soc/ti/smartreflex.c46
-rw-r--r--drivers/soc/ti/ti_sci_inta_msi.c25
-rw-r--r--drivers/soc/ti/ti_sci_pm_domains.c208
-rw-r--r--drivers/soc/ti/wkup_m3_ipc.c27
-rw-r--r--drivers/soc/ux500/ux500-soc-id.c10
-rw-r--r--drivers/soc/versatile/Kconfig4
-rw-r--r--drivers/soc/versatile/soc-integrator.c1
-rw-r--r--drivers/soc/versatile/soc-realview.c20
-rw-r--r--drivers/soc/vt8500/Kconfig19
-rw-r--r--drivers/soc/vt8500/Makefile2
-rw-r--r--drivers/soc/vt8500/wmt-socinfo.c125
-rw-r--r--drivers/soc/xilinx/Kconfig9
-rw-r--r--drivers/soc/xilinx/Makefile1
-rw-r--r--drivers/soc/xilinx/xlnx_event_manager.c68
-rw-r--r--drivers/soc/xilinx/zynqmp_pm_domains.c322
-rw-r--r--drivers/soc/xilinx/zynqmp_power.c182
-rw-r--r--drivers/soundwire/Kconfig13
-rw-r--r--drivers/soundwire/Makefile12
-rw-r--r--drivers/soundwire/amd_init.c231
-rw-r--r--drivers/soundwire/amd_init.h21
-rw-r--r--drivers/soundwire/amd_manager.c1385
-rw-r--r--drivers/soundwire/amd_manager.h277
-rw-r--r--drivers/soundwire/bus.c520
-rw-r--r--drivers/soundwire/bus.h44
-rw-r--r--drivers/soundwire/bus_type.c60
-rw-r--r--drivers/soundwire/cadence_master.c1195
-rw-r--r--drivers/soundwire/cadence_master.h69
-rw-r--r--drivers/soundwire/debugfs.c233
-rw-r--r--drivers/soundwire/dmi-quirks.c75
-rw-r--r--drivers/soundwire/generic_bandwidth_allocation.c409
-rw-r--r--drivers/soundwire/intel.c1436
-rw-r--r--drivers/soundwire/intel.h223
-rw-r--r--drivers/soundwire/intel_ace2x.c1072
-rw-r--r--drivers/soundwire/intel_ace2x_debugfs.c153
-rw-r--r--drivers/soundwire/intel_auxdevice.c871
-rw-r--r--drivers/soundwire/intel_auxdevice.h19
-rw-r--r--drivers/soundwire/intel_bus_common.c286
-rw-r--r--drivers/soundwire/intel_init.c98
-rw-r--r--drivers/soundwire/irq.c63
-rw-r--r--drivers/soundwire/irq.h38
-rw-r--r--drivers/soundwire/master.c4
-rw-r--r--drivers/soundwire/mipi_disco.c218
-rw-r--r--drivers/soundwire/qcom.c682
-rw-r--r--drivers/soundwire/slave.c48
-rw-r--r--drivers/soundwire/stream.c451
-rw-r--r--drivers/soundwire/sysfs_local.h4
-rw-r--r--drivers/soundwire/sysfs_slave.c66
-rw-r--r--drivers/soundwire/sysfs_slave_dpn.c3
-rw-r--r--drivers/spi/Kconfig364
-rw-r--r--drivers/spi/Makefile40
-rw-r--r--drivers/spi/atmel-quadspi.c1192
-rw-r--r--drivers/spi/internals.h8
-rw-r--r--drivers/spi/spi-airoha-snfi.c1148
-rw-r--r--drivers/spi/spi-altera-core.c33
-rw-r--r--drivers/spi/spi-altera-dfl.c36
-rw-r--r--drivers/spi/spi-altera-platform.c38
-rw-r--r--drivers/spi/spi-amd-pci.c69
-rw-r--r--drivers/spi/spi-amd.c747
-rw-r--r--drivers/spi/spi-amd.h44
-rw-r--r--drivers/spi/spi-amlogic-spifc-a1.c467
-rw-r--r--drivers/spi/spi-amlogic-spifc-a4.c1222
-rw-r--r--drivers/spi/spi-amlogic-spisg.c888
-rw-r--r--drivers/spi/spi-apple.c531
-rw-r--r--drivers/spi/spi-ar934x.c41
-rw-r--r--drivers/spi/spi-armada-3700.c134
-rw-r--r--drivers/spi/spi-aspeed-smc.c820
-rw-r--r--drivers/spi/spi-at91-usart.c76
-rw-r--r--drivers/spi/spi-ath79.c59
-rw-r--r--drivers/spi/spi-atmel.c469
-rw-r--r--drivers/spi/spi-au1550.c110
-rw-r--r--drivers/spi/spi-axi-spi-engine.c1015
-rw-r--r--drivers/spi/spi-bcm-qspi.c97
-rw-r--r--drivers/spi/spi-bcm-qspi.h2
-rw-r--r--drivers/spi/spi-bcm2835.c258
-rw-r--r--drivers/spi/spi-bcm2835aux.c123
-rw-r--r--drivers/spi/spi-bcm63xx-hsspi.c573
-rw-r--r--drivers/spi/spi-bcm63xx.c125
-rw-r--r--drivers/spi/spi-bcmbca-hsspi.c643
-rw-r--r--drivers/spi/spi-bitbang-txrx.h16
-rw-r--r--drivers/spi/spi-bitbang.c188
-rw-r--r--drivers/spi/spi-brcmstb-qspi.c4
-rw-r--r--drivers/spi/spi-butterfly.c21
-rw-r--r--drivers/spi/spi-cadence-quadspi.c620
-rw-r--r--drivers/spi/spi-cadence-xspi.c751
-rw-r--r--drivers/spi/spi-cadence.c484
-rw-r--r--drivers/spi/spi-cavium-octeon.c36
-rw-r--r--drivers/spi/spi-cavium-thunderx.c44
-rw-r--r--drivers/spi/spi-cavium.c14
-rw-r--r--drivers/spi/spi-cavium.h2
-rw-r--r--drivers/spi/spi-ch341.c241
-rw-r--r--drivers/spi/spi-clps711x.c42
-rw-r--r--drivers/spi/spi-coldfire-qspi.c91
-rw-r--r--drivers/spi/spi-cs42l43.c451
-rw-r--r--drivers/spi/spi-davinci.c200
-rw-r--r--drivers/spi/spi-dln2.c106
-rw-r--r--drivers/spi/spi-dw-bt1.c49
-rw-r--r--drivers/spi/spi-dw-core.c212
-rw-r--r--drivers/spi/spi-dw-dma.c105
-rw-r--r--drivers/spi/spi-dw-mmio.c146
-rw-r--r--drivers/spi/spi-dw-pci.c19
-rw-r--r--drivers/spi/spi-dw.h15
-rw-r--r--drivers/spi/spi-ep93xx.c259
-rw-r--r--drivers/spi/spi-falcon.c41
-rw-r--r--drivers/spi/spi-fsi.c17
-rw-r--r--drivers/spi/spi-fsl-cpm.c112
-rw-r--r--drivers/spi/spi-fsl-cpm.h5
-rw-r--r--drivers/spi/spi-fsl-dspi.c744
-rw-r--r--drivers/spi/spi-fsl-espi.c88
-rw-r--r--drivers/spi/spi-fsl-lib.c18
-rw-r--r--drivers/spi/spi-fsl-lib.h3
-rw-r--r--drivers/spi/spi-fsl-lpspi.c252
-rw-r--r--drivers/spi/spi-fsl-qspi.c230
-rw-r--r--drivers/spi/spi-fsl-spi.c333
-rw-r--r--drivers/spi/spi-geni-qcom.c446
-rw-r--r--drivers/spi/spi-gpio.c222
-rw-r--r--drivers/spi/spi-gxp.c25
-rw-r--r--drivers/spi/spi-hisi-kunpeng.c96
-rw-r--r--drivers/spi/spi-hisi-sfc-v3xx.c29
-rw-r--r--drivers/spi/spi-img-spfi.c145
-rw-r--r--drivers/spi/spi-imx.c479
-rw-r--r--drivers/spi/spi-ingenic.c27
-rw-r--r--drivers/spi/spi-intel-pci.c30
-rw-r--r--drivers/spi/spi-intel-platform.c10
-rw-r--r--drivers/spi/spi-intel.c407
-rw-r--r--drivers/spi/spi-intel.h4
-rw-r--r--drivers/spi/spi-iproc-qspi.c13
-rw-r--r--drivers/spi/spi-jcore.c46
-rw-r--r--drivers/spi/spi-kspi2.c431
-rw-r--r--drivers/spi/spi-lantiq-ssc.c119
-rw-r--r--drivers/spi/spi-ljca.c297
-rw-r--r--drivers/spi/spi-lm70llp.c27
-rw-r--r--drivers/spi/spi-loongson-core.c280
-rw-r--r--drivers/spi/spi-loongson-pci.c54
-rw-r--r--drivers/spi/spi-loongson-plat.c47
-rw-r--r--drivers/spi/spi-loongson.h49
-rw-r--r--drivers/spi/spi-loopback-test.c72
-rw-r--r--drivers/spi/spi-lp8841-rtc.c65
-rw-r--r--drivers/spi/spi-mem.c165
-rw-r--r--drivers/spi/spi-meson-spicc.c462
-rw-r--r--drivers/spi/spi-meson-spifc.c85
-rw-r--r--drivers/spi/spi-microchip-core-qspi.c771
-rw-r--r--drivers/spi/spi-microchip-core-spi.c429
-rw-r--r--drivers/spi/spi-microchip-core.c617
-rw-r--r--drivers/spi/spi-mpc512x-psc.c192
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c177
-rw-r--r--drivers/spi/spi-mpc52xx.c101
-rw-r--r--drivers/spi/spi-mpfs.c626
-rw-r--r--drivers/spi/spi-mt65xx.c404
-rw-r--r--drivers/spi/spi-mt7621.c187
-rw-r--r--drivers/spi/spi-mtk-nor.c91
-rw-r--r--drivers/spi/spi-mtk-snfi.c125
-rw-r--r--drivers/spi/spi-mux.c14
-rw-r--r--drivers/spi/spi-mxic.c117
-rw-r--r--drivers/spi/spi-mxs.c96
-rw-r--r--drivers/spi/spi-npcm-fiu.c87
-rw-r--r--drivers/spi/spi-npcm-pspi.c75
-rw-r--r--drivers/spi/spi-nxp-fspi.c505
-rw-r--r--drivers/spi/spi-oc-tiny.c37
-rw-r--r--drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c62
-rw-r--r--drivers/spi/spi-offload-trigger-pwm.c172
-rw-r--r--drivers/spi/spi-offload.c465
-rw-r--r--drivers/spi/spi-omap-100k.c489
-rw-r--r--drivers/spi/spi-omap-uwire.c60
-rw-r--r--drivers/spi/spi-omap2-mcspi.c396
-rw-r--r--drivers/spi/spi-orion.c115
-rw-r--r--drivers/spi/spi-pci1xxxx.c1031
-rw-r--r--drivers/spi/spi-pic32-sqi.c123
-rw-r--r--drivers/spi/spi-pic32.c181
-rw-r--r--drivers/spi/spi-pl022.c497
-rw-r--r--drivers/spi/spi-ppc4xx.c84
-rw-r--r--drivers/spi/spi-pxa2xx-dma.c38
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c60
-rw-r--r--drivers/spi/spi-pxa2xx-platform.c230
-rw-r--r--drivers/spi/spi-pxa2xx.c600
-rw-r--r--drivers/spi/spi-pxa2xx.h49
-rw-r--r--drivers/spi/spi-qcom-qspi.c349
-rw-r--r--drivers/spi/spi-qpic-snand.c1653
-rw-r--r--drivers/spi/spi-qup.c296
-rw-r--r--drivers/spi/spi-rb4xx.c76
-rw-r--r--drivers/spi/spi-realtek-rtl-snand.c418
-rw-r--r--drivers/spi/spi-realtek-rtl.c2
-rw-r--r--drivers/spi/spi-rockchip-sfc.c299
-rw-r--r--drivers/spi/spi-rockchip.c219
-rw-r--r--drivers/spi/spi-rpc-if.c69
-rw-r--r--drivers/spi/spi-rspi.c50
-rw-r--r--drivers/spi/spi-rzv2h-rspi.c687
-rw-r--r--drivers/spi/spi-rzv2m-csi.c696
-rw-r--r--drivers/spi/spi-s3c24xx-regs.h41
-rw-r--r--drivers/spi/spi-s3c24xx.c604
-rw-r--r--drivers/spi/spi-s3c64xx.c645
-rw-r--r--drivers/spi/spi-sc18is602.c85
-rw-r--r--drivers/spi/spi-sg2044-nor.c511
-rw-r--r--drivers/spi/spi-sh-hspi.c8
-rw-r--r--drivers/spi/spi-sh-msiof.c476
-rw-r--r--drivers/spi/spi-sh-sci.c33
-rw-r--r--drivers/spi/spi-sh.c38
-rw-r--r--drivers/spi/spi-sifive.c90
-rw-r--r--drivers/spi/spi-slave-mt27xx.c28
-rw-r--r--drivers/spi/spi-slave-system-control.c2
-rw-r--r--drivers/spi/spi-slave-time.c2
-rw-r--r--drivers/spi/spi-sn-f-ospi.c697
-rw-r--r--drivers/spi/spi-sprd-adi.c39
-rw-r--r--drivers/spi/spi-sprd.c31
-rw-r--r--drivers/spi/spi-st-ssc4.c90
-rw-r--r--drivers/spi/spi-stm32-ospi.c1068
-rw-r--r--drivers/spi/spi-stm32-qspi.c162
-rw-r--r--drivers/spi/spi-stm32.c1266
-rw-r--r--drivers/spi/spi-sun4i.c95
-rw-r--r--drivers/spi/spi-sun6i.c350
-rw-r--r--drivers/spi/spi-sunplus-sp7021.c97
-rw-r--r--drivers/spi/spi-synquacer.c99
-rw-r--r--drivers/spi/spi-tegra114.c172
-rw-r--r--drivers/spi/spi-tegra20-sflash.c86
-rw-r--r--drivers/spi/spi-tegra20-slink.c124
-rw-r--r--drivers/spi/spi-tegra210-quad.c541
-rw-r--r--drivers/spi/spi-ti-qspi.c140
-rw-r--r--drivers/spi/spi-tle62x0.c2
-rw-r--r--drivers/spi/spi-topcliff-pch.c234
-rw-r--r--drivers/spi/spi-uniphier.c200
-rw-r--r--drivers/spi/spi-virtio.c431
-rw-r--r--drivers/spi/spi-wpcm-fiu.c504
-rw-r--r--drivers/spi/spi-xcomm.c108
-rw-r--r--drivers/spi/spi-xilinx.c103
-rw-r--r--drivers/spi/spi-xlp.c52
-rw-r--r--drivers/spi/spi-xtensa-xtfpga.c48
-rw-r--r--drivers/spi/spi-zynq-qspi.c70
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c500
-rw-r--r--drivers/spi/spi.c1532
-rw-r--r--drivers/spi/spidev.c150
-rw-r--r--drivers/spmi/Kconfig9
-rw-r--r--drivers/spmi/Makefile3
-rw-r--r--drivers/spmi/hisi-spmi-controller.c38
-rw-r--r--drivers/spmi/spmi-apple-controller.c168
-rw-r--r--drivers/spmi/spmi-devres.c64
-rw-r--r--drivers/spmi/spmi-mtk-pmif.c66
-rw-r--r--drivers/spmi/spmi-pmic-arb.c1110
-rw-r--r--drivers/spmi/spmi.c35
-rw-r--r--drivers/ssb/Kconfig3
-rw-r--r--drivers/ssb/driver_gpio.c26
-rw-r--r--drivers/ssb/main.c16
-rw-r--r--drivers/staging/Kconfig36
-rw-r--r--drivers/staging/Makefile18
-rw-r--r--drivers/staging/axis-fifo/axis-fifo.c612
-rw-r--r--drivers/staging/axis-fifo/axis-fifo.txt5
-rw-r--r--drivers/staging/board/Kconfig12
-rw-r--r--drivers/staging/board/Makefile4
-rw-r--r--drivers/staging/board/TODO2
-rw-r--r--drivers/staging/board/armadillo800eva.c88
-rw-r--r--drivers/staging/board/board.c204
-rw-r--r--drivers/staging/board/board.h46
-rw-r--r--drivers/staging/board/kzm9d.c26
-rw-r--r--drivers/staging/clocking-wizard/Kconfig10
-rw-r--r--drivers/staging/clocking-wizard/Makefile2
-rw-r--r--drivers/staging/clocking-wizard/TODO13
-rw-r--r--drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c634
-rw-r--r--drivers/staging/clocking-wizard/dt-binding.txt30
-rw-r--r--drivers/staging/emxx_udc/Kconfig11
-rw-r--r--drivers/staging/emxx_udc/Makefile2
-rw-r--r--drivers/staging/emxx_udc/TODO6
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.c3223
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.h554
-rw-r--r--drivers/staging/fbtft/Kconfig43
-rw-r--r--drivers/staging/fbtft/fb_ili9320.c2
-rw-r--r--drivers/staging/fbtft/fb_ili9341.c2
-rw-r--r--drivers/staging/fbtft/fb_ra8875.c7
-rw-r--r--drivers/staging/fbtft/fb_seps525.c7
-rw-r--r--drivers/staging/fbtft/fb_sh1106.c3
-rw-r--r--drivers/staging/fbtft/fb_ssd1289.c3
-rw-r--r--drivers/staging/fbtft/fb_ssd1306.c3
-rw-r--r--drivers/staging/fbtft/fb_ssd1325.c9
-rw-r--r--drivers/staging/fbtft/fb_ssd1331.c2
-rw-r--r--drivers/staging/fbtft/fb_ssd1351.c14
-rw-r--r--drivers/staging/fbtft/fb_uc1611.c3
-rw-r--r--drivers/staging/fbtft/fbtft-bus.c9
-rw-r--r--drivers/staging/fbtft/fbtft-core.c174
-rw-r--r--drivers/staging/fbtft/fbtft-sysfs.c4
-rw-r--r--drivers/staging/fbtft/fbtft.h7
-rw-r--r--drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev31
-rw-r--r--drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev62
-rw-r--r--drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt71
-rw-r--r--drivers/staging/fieldbus/Documentation/fieldbus_dev.txt66
-rw-r--r--drivers/staging/fieldbus/Kconfig19
-rw-r--r--drivers/staging/fieldbus/Makefile7
-rw-r--r--drivers/staging/fieldbus/TODO5
-rw-r--r--drivers/staging/fieldbus/anybuss/Kconfig41
-rw-r--r--drivers/staging/fieldbus/anybuss/Makefile10
-rw-r--r--drivers/staging/fieldbus/anybuss/anybuss-client.h99
-rw-r--r--drivers/staging/fieldbus/anybuss/anybuss-controller.h47
-rw-r--r--drivers/staging/fieldbus/anybuss/arcx-anybus.c377
-rw-r--r--drivers/staging/fieldbus/anybuss/hms-profinet.c224
-rw-r--r--drivers/staging/fieldbus/anybuss/host.c1452
-rw-r--r--drivers/staging/fieldbus/dev_core.c348
-rw-r--r--drivers/staging/fieldbus/fieldbus_dev.h114
-rw-r--r--drivers/staging/fwserial/Kconfig32
-rw-r--r--drivers/staging/fwserial/Makefile3
-rw-r--r--drivers/staging/fwserial/TODO14
-rw-r--r--drivers/staging/fwserial/dma_fifo.c294
-rw-r--r--drivers/staging/fwserial/dma_fifo.h117
-rw-r--r--drivers/staging/fwserial/fwserial.c2890
-rw-r--r--drivers/staging/fwserial/fwserial.h359
-rw-r--r--drivers/staging/gdm724x/Kconfig16
-rw-r--r--drivers/staging/gdm724x/Makefile8
-rw-r--r--drivers/staging/gdm724x/TODO16
-rw-r--r--drivers/staging/gdm724x/gdm_endian.c37
-rw-r--r--drivers/staging/gdm724x/gdm_endian.h30
-rw-r--r--drivers/staging/gdm724x/gdm_lte.c937
-rw-r--r--drivers/staging/gdm724x/gdm_lte.h71
-rw-r--r--drivers/staging/gdm724x/gdm_mux.c668
-rw-r--r--drivers/staging/gdm724x/gdm_mux.h85
-rw-r--r--drivers/staging/gdm724x/gdm_tty.c319
-rw-r--r--drivers/staging/gdm724x/gdm_tty.h60
-rw-r--r--drivers/staging/gdm724x/gdm_usb.c1012
-rw-r--r--drivers/staging/gdm724x/gdm_usb.h99
-rw-r--r--drivers/staging/gdm724x/hci.h45
-rw-r--r--drivers/staging/gdm724x/hci_packet.h82
-rw-r--r--drivers/staging/gdm724x/netlink_k.c128
-rw-r--r--drivers/staging/gdm724x/netlink_k.h16
-rw-r--r--drivers/staging/greybus/Documentation/firmware/firmware.c28
-rw-r--r--drivers/staging/greybus/Kconfig2
-rw-r--r--drivers/staging/greybus/arche-apb-ctrl.c5
-rw-r--r--drivers/staging/greybus/arche-platform.c14
-rw-r--r--drivers/staging/greybus/audio_apbridgea.h1
-rw-r--r--drivers/staging/greybus/audio_codec.c40
-rw-r--r--drivers/staging/greybus/audio_codec.h1
-rw-r--r--drivers/staging/greybus/audio_helper.c40
-rw-r--r--drivers/staging/greybus/audio_manager.c9
-rw-r--r--drivers/staging/greybus/audio_manager_module.c49
-rw-r--r--drivers/staging/greybus/audio_manager_private.h7
-rw-r--r--drivers/staging/greybus/audio_topology.c38
-rw-r--r--drivers/staging/greybus/authentication.c25
-rw-r--r--drivers/staging/greybus/bootrom.c11
-rw-r--r--drivers/staging/greybus/camera.c91
-rw-r--r--drivers/staging/greybus/fw-core.c4
-rw-r--r--drivers/staging/greybus/fw-download.c15
-rw-r--r--drivers/staging/greybus/fw-management.c93
-rw-r--r--drivers/staging/greybus/gb-camera.h4
-rw-r--r--drivers/staging/greybus/gbphy.c31
-rw-r--r--drivers/staging/greybus/gpio.c55
-rw-r--r--drivers/staging/greybus/greybus_authentication.h7
-rw-r--r--drivers/staging/greybus/greybus_firmware.h8
-rw-r--r--drivers/staging/greybus/hid.c3
-rw-r--r--drivers/staging/greybus/i2c.c3
-rw-r--r--drivers/staging/greybus/light.c38
-rw-r--r--drivers/staging/greybus/log.c1
-rw-r--r--drivers/staging/greybus/loopback.c9
-rw-r--r--drivers/staging/greybus/power_supply.c15
-rw-r--r--drivers/staging/greybus/pwm.c148
-rw-r--r--drivers/staging/greybus/raw.c30
-rw-r--r--drivers/staging/greybus/sdio.c1
-rw-r--r--drivers/staging/greybus/spi.c1
-rw-r--r--drivers/staging/greybus/spilib.c73
-rw-r--r--drivers/staging/greybus/tools/.gitignore2
-rw-r--r--drivers/staging/greybus/tools/Android.mk10
-rw-r--r--drivers/staging/greybus/tools/Makefile33
-rw-r--r--drivers/staging/greybus/tools/README.loopback198
-rwxr-xr-xdrivers/staging/greybus/tools/lbtest169
-rw-r--r--drivers/staging/greybus/tools/loopback_test.c992
-rw-r--r--drivers/staging/greybus/uart.c31
-rw-r--r--drivers/staging/greybus/usb.c3
-rw-r--r--drivers/staging/greybus/vibrator.c8
-rw-r--r--drivers/staging/iio/Kconfig3
-rw-r--r--drivers/staging/iio/Makefile3
-rw-r--r--drivers/staging/iio/TODO5
-rw-r--r--drivers/staging/iio/accel/Kconfig12
-rw-r--r--drivers/staging/iio/accel/Makefile1
-rw-r--r--drivers/staging/iio/accel/adis16203.c6
-rw-r--r--drivers/staging/iio/accel/adis16240.c443
-rw-r--r--drivers/staging/iio/adc/ad7816.c6
-rw-r--r--drivers/staging/iio/addac/adt7316-i2c.c18
-rw-r--r--drivers/staging/iio/addac/adt7316.c113
-rw-r--r--drivers/staging/iio/cdc/Kconfig17
-rw-r--r--drivers/staging/iio/cdc/Makefile6
-rw-r--r--drivers/staging/iio/cdc/ad7746.c767
-rw-r--r--drivers/staging/iio/frequency/ad9832.c169
-rw-r--r--drivers/staging/iio/frequency/ad9832.h1
-rw-r--r--drivers/staging/iio/frequency/ad9834.c62
-rw-r--r--drivers/staging/iio/frequency/ad9834.h10
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c113
-rw-r--r--drivers/staging/iio/meter/Kconfig37
-rw-r--r--drivers/staging/iio/meter/Makefile8
-rw-r--r--drivers/staging/iio/meter/ade7854-i2c.c151
-rw-r--r--drivers/staging/iio/meter/ade7854-spi.c160
-rw-r--r--drivers/staging/iio/meter/ade7854.c556
-rw-r--r--drivers/staging/iio/meter/ade7854.h173
-rw-r--r--drivers/staging/iio/meter/meter.h398
-rw-r--r--drivers/staging/iio/resolver/Kconfig18
-rw-r--r--drivers/staging/iio/resolver/Makefile6
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.c715
-rw-r--r--drivers/staging/ks7010/Kconfig14
-rw-r--r--drivers/staging/ks7010/Makefile4
-rw-r--r--drivers/staging/ks7010/TODO33
-rw-r--r--drivers/staging/ks7010/eap_packet.h70
-rw-r--r--drivers/staging/ks7010/ks7010_sdio.c1143
-rw-r--r--drivers/staging/ks7010/ks_hostif.c2313
-rw-r--r--drivers/staging/ks7010/ks_hostif.h617
-rw-r--r--drivers/staging/ks7010/ks_wlan.h567
-rw-r--r--drivers/staging/ks7010/ks_wlan_ioctl.h61
-rw-r--r--drivers/staging/ks7010/ks_wlan_net.c2671
-rw-r--r--drivers/staging/media/Kconfig26
-rw-r--r--drivers/staging/media/Makefile8
-rw-r--r--drivers/staging/media/atomisp/Kconfig19
-rw-r--r--drivers/staging/media/atomisp/Makefile20
-rw-r--r--drivers/staging/media/atomisp/TODO226
-rw-r--r--drivers/staging/media/atomisp/i2c/Kconfig69
-rw-r--r--drivers/staging/media/atomisp/i2c/Makefile11
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc0310.c1301
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c216
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c210
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-lm3554.c957
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c1816
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2680.c1245
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2722.c223
-rw-r--r--drivers/staging/media/atomisp/i2c/gc0310.h404
-rw-r--r--drivers/staging/media/atomisp/i2c/gc2235.h65
-rw-r--r--drivers/staging/media/atomisp/i2c/mt9m114.h1794
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2680.h917
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2722.h62
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/Makefile2
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/ad5823.h63
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c2000
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/ov5693.h1392
-rw-r--r--drivers/staging/media/atomisp/include/hmm/hmm.h14
-rw-r--r--drivers/staging/media/atomisp/include/hmm/hmm_bo.h25
-rw-r--r--drivers/staging/media/atomisp/include/hmm/hmm_common.h11
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp.h326
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h19
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_platform.h114
-rw-r--r--drivers/staging/media/atomisp/include/linux/libmsrlisthelper.h28
-rw-r--r--drivers/staging/media/atomisp/include/media/lm3554.h132
-rw-r--r--drivers/staging/media/atomisp/include/mmu/isp_mmu.h11
-rw-r--r--drivers/staging/media/atomisp/include/mmu/sh_mmu_mrfld.h11
-rw-r--r--drivers/staging/media/atomisp/notes.txt13
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp-regs.h12
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c3415
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.h103
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_common.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat.h87
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.c943
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.h11
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h310
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2.c93
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c554
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_dfs_tables.h11
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_drvfs.c202
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_drvfs.h25
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_file.c229
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_file.h44
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_fops.c1080
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_fops.h30
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c451
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_internal.h164
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.c2271
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.h38
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.c773
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.h183
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_tables.h11
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_tpg.c164
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_tpg.h39
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_trace_event.h11
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.c1288
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.h18
-rw-r--r--drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h11
-rw-r--r--drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h15
-rw-r--r--drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h9
-rw-r--r--drivers/staging/media/atomisp/pci/base/circbuf/src/circbuf.c15
-rw-r--r--drivers/staging/media/atomisp/pci/base/refcount/interface/ia_css_refcount.h9
-rw-r--r--drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c9
-rw-r--r--drivers/staging/media/atomisp/pci/bits.h13
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h22
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h14
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_util.h9
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c64
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c30
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c9
-rw-r--r--drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h20
-rw-r--r--drivers/staging/media/atomisp/pci/camera/util/src/util.c34
-rw-r--r--drivers/staging/media/atomisp/pci/cell_params.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/csi_rx_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx.c9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/ibuf_ctrl.c9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/ibuf_ctrl_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_dma.c9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_dma_private.h11
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq.c9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq_local.h12
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq_private.h11
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio.c9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h11
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/hrt/PixelGen_SysBlock_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/hrt/ibuf_cntrl_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/hrt/mipi_backend_common_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/hrt/mipi_backend_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/hrt/rx_csi_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/hrt/stream2mmio_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/ibuf_ctrl_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/isys_dma_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/isys_irq_global.h11
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/isys_stream2mmio_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/pixelgen_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_receiver_2400_common_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_receiver_2400_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/css_trace.h9
-rw-r--r--drivers/staging/media/atomisp/pci/defs.h37
-rw-r--r--drivers/staging/media/atomisp/pci/dma_v2_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/gdc_v2_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/gp_timer_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/gpio_block_defs.h26
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/debug_global.h16
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/dma_global.h10
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/event_fifo_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/fifo_monitor_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/gdc_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/gp_device_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/gp_timer_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/gpio_global.h32
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/hmem_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/debug.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/debug_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/debug_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma.c275
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h126
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/fifo_monitor.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/fifo_monitor_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/fifo_monitor_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gp_device.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gp_device_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gp_device_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gp_timer.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gp_timer_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gp_timer_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_local.h21
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_private.h18
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/hmem.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/hmem_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/hmem_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c11
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c495
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c28
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq_local.h17
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/isp.c69
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/isp_local.h40
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/isp_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/sp.c56
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/sp_local.h35
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/sp_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/timed_ctrl.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/timed_ctrl_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/timed_ctrl_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vamem_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c29
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem_local.h13
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/input_formatter_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/irq_global.h11
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/isp_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/mmu_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/sp_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/timed_ctrl_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/vamem_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/vmem_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h38
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/bitop_support.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/csi_rx.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/debug.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/device_access/device_access.h8
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/dma.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/event_fifo.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/fifo_monitor.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/gdc_device.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/gp_device.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/gp_timer.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/gpio.h46
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/hmem.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h15
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/debug_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/dma_public.h22
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/event_fifo_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/fifo_monitor_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gdc_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gp_device_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gp_timer_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gpio_public.h46
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/hmem_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/input_formatter_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/irq_public.h19
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isp_public.h22
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_dma_public.h11
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_irq_public.h11
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_public.h38
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_stream2mmio_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/pixelgen_public.h11
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/sp_public.h25
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/tag_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/timed_ctrl_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/vamem_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/vmem_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/input_formatter.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/input_system.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/irq.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/isp.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/isys_irq.h11
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/isys_stream2mmio.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h135
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/misc_support.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/mmu_device.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/pixelgen.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/platform_support.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/print_support.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/queue.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/resource.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/sp.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/tag.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/timed_ctrl.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h14
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/vamem.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/vmem.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/host/queue_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/host/queue_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/host/tag.c9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/host/tag_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/host/tag_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/queue_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/sw_event_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_shared/tag_global.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_streaming_to_mipi_types_hrt.h9
-rw-r--r--drivers/staging/media/atomisp/pci/hive_types.h30
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm.c119
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm_bo.c272
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css.h10
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_3a.h14
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_acc_types.h19
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_buffer.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_control.h38
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_device_access.c9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_device_access.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_dvs.h13
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_env.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_err.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_event_public.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_firmware.h15
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_frac.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_frame_format.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_frame_public.h125
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_host_data.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_input_port.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_irq.h15
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_isp_configs.c9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_isp_configs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_isp_params.c9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_isp_params.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_isp_states.c9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_isp_states.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_metadata.h13
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_mipi.h25
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_mmu.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_mmu_private.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_morph.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_pipe.h14
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_pipe_public.h78
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_prbs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_properties.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_shading.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_stream.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_stream_format.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_stream_public.h32
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_timer.h8
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_tpg.h79
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_types.h11
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_version.h9
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_version_data.h9
-rw-r--r--drivers/staging/media/atomisp/pci/if_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/input_formatter_subsystem_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/input_selector_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/input_switch_2400_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/input_system_ctrl_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/input_system_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/input_system_local.h7
-rw-r--r--drivers/staging/media/atomisp/pci/input_system_private.h7
-rw-r--r--drivers/staging/media/atomisp/pci/input_system_public.h4
-rw-r--r--drivers/staging/media/atomisp/pci/irq_controller_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/irq_types_hrt.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2_table.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr2_2/ia_css_bnr2_2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr2_2/ia_css_bnr2_2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr2_2/ia_css_bnr2_2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr2_2/ia_css_bnr2_2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.c12
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_1.0/ia_css_cnr.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_1.0/ia_css_cnr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_1.0/ia_css_cnr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_2/ia_css_cnr2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_2/ia_css_cnr2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_2/ia_css_cnr2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/cnr/cnr_2/ia_css_cnr2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/copy_output/copy_output_1.0/ia_css_copy_output.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/copy_output/copy_output_1.0/ia_css_copy_output.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/copy_output/copy_output_1.0/ia_css_copy_output_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/crop/crop_1.0/ia_css_crop.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/crop/crop_1.0/ia_css_crop.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/crop/crop_1.0/ia_css_crop_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/crop/crop_1.0/ia_css_crop_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/csc/csc_1.0/ia_css_csc.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/csc/csc_1.0/ia_css_csc.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/csc/csc_1.0/ia_css_csc_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/csc/csc_1.0/ia_css_csc_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc1_5/ia_css_ctc1_5.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc1_5/ia_css_ctc1_5.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc1_5/ia_css_ctc1_5_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc_1.0/ia_css_ctc.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc_1.0/ia_css_ctc.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc_1.0/ia_css_ctc_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc_1.0/ia_css_ctc_table.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc_1.0/ia_css_ctc_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc_1.0/ia_css_ctc_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.c12
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_2/ia_css_de2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_2/ia_css_de2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_2/ia_css_de2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/de/de_2/ia_css_de2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dp/dp_1.0/ia_css_dp.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dp/dp_1.0/ia_css_dp.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dp/dp_1.0/ia_css_dp_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dp/dp_1.0/ia_css_dp_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h15
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c33
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h35
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fixedbds/fixedbds_1.0/ia_css_fixedbds_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fixedbds/fixedbds_1.0/ia_css_fixedbds_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c18
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_1.0/ia_css_gc.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_1.0/ia_css_gc.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_1.0/ia_css_gc_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_1.0/ia_css_gc_table.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_1.0/ia_css_gc_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_1.0/ia_css_gc_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_2/ia_css_gc2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_2/ia_css_gc2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_2/ia_css_gc2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_2/ia_css_gc2_table.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_2/ia_css_gc2_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/gc/gc_2/ia_css_gc2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr_types.h13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c28
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_param.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_types.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c27
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_param.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_types.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/iterator/iterator_1.0/ia_css_iterator.host.c12
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/iterator/iterator_1.0/ia_css_iterator.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/iterator/iterator_1.0/ia_css_iterator_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5_table.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc1_5/ia_css_macc1_5_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc_1.0/ia_css_macc.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc_1.0/ia_css_macc.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc_1.0/ia_css_macc_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc_1.0/ia_css_macc_table.host.c13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc_1.0/ia_css_macc_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/macc/macc_1.0/ia_css_macc_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/norm/norm_1.0/ia_css_norm.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/norm/norm_1.0/ia_css_norm.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/norm/norm_1.0/ia_css_norm_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob2/ia_css_ob2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob2/ia_css_ob2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob2/ia_css_ob2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob2/ia_css_ob2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob_1.0/ia_css_ob.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob_1.0/ia_css_ob.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob_1.0/ia_css_ob_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ob/ob_1.0/ia_css_ob_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/output/output_1.0/ia_css_output.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/output/output_1.0/ia_css_output.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/output/output_1.0/ia_css_output_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/output/output_1.0/ia_css_output_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/qplane/qplane_2/ia_css_qplane.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/qplane/qplane_2/ia_css_qplane.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/qplane/qplane_2/ia_css_qplane_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/qplane/qplane_2/ia_css_qplane_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c30
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw_aa_binning/raw_aa_binning_1.0/ia_css_raa.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw_aa_binning/raw_aa_binning_1.0/ia_css_raa.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c11
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref_state.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h11
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/common/ia_css_sdis_common.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/common/ia_css_sdis_common_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c18
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2_types.h11
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tdf/tdf_1.0/ia_css_tdf_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tdf/tdf_1.0/ia_css_tdf_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr3/ia_css_tnr3_types.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr_state.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/uds/uds_1.0/ia_css_uds_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c14
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/wb/wb_1.0/ia_css_wb.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/wb/wb_1.0/ia_css_wb.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/wb/wb_1.0/ia_css_wb_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/wb/wb_1.0/ia_css_wb_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_1.0/ia_css_xnr.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_1.0/ia_css_xnr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_1.0/ia_css_xnr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_1.0/ia_css_xnr_table.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_1.0/ia_css_xnr_table.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_1.0/ia_css_xnr_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c27
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_1.0/ia_css_ynr.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_1.0/ia_css_ynr.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_1.0/ia_css_ynr_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_1.0/ia_css_ynr_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_2/ia_css_ynr2.host.c9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_2/ia_css_ynr2.host.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_2/ia_css_ynr2_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ynr/ynr_2/ia_css_ynr2_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h14
-rw-r--r--drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h165
-rw-r--r--drivers/staging/media/atomisp/pci/isp/modes/interface/isp_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_input_system_global.h12
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_input_system_local.h207
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_input_system_private.h13
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_input_system_public.h68
-rw-r--r--drivers/staging/media/atomisp/pci/isp2400_support.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp2401_input_system_global.h10
-rw-r--r--drivers/staging/media/atomisp/pci/isp2401_input_system_local.h97
-rw-r--r--drivers/staging/media/atomisp/pci/isp2401_input_system_private.h121
-rw-r--r--drivers/staging/media/atomisp/pci/isp_acquisition_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/isp_capture_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/mamoiada_params.h9
-rw-r--r--drivers/staging/media/atomisp/pci/mmu/isp_mmu.c11
-rw-r--r--drivers/staging/media/atomisp/pci/mmu/sh_mmu_mrfld.c11
-rw-r--r--drivers/staging/media/atomisp/pci/mmu_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/binary/interface/ia_css_binary.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c331
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/bufq/interface/ia_css_bufq.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/bufq/interface/ia_css_bufq_comm.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/bufq/src/bufq.c13
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h94
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug_internal.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug_pipe.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c1565
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/event/interface/ia_css_event.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/event/src/event.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/eventq/interface/ia_css_eventq.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/eventq/src/eventq.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/frame/interface/ia_css_frame.h11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/frame/interface/ia_css_frame_comm.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c305
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/ifmtr/interface/ia_css_ifmtr.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c33
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/inputfifo/interface/ia_css_inputfifo.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/inputfifo/src/inputfifo.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isp_param/interface/ia_css_isp_param.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isp_param/interface/ia_css_isp_param_types.h8
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/interface/ia_css_isys.h18
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/interface/ia_css_isys_comm.h11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/csi_rx_rmgr.c11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/csi_rx_rmgr.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/ibuf_ctrl_rmgr.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/ibuf_ctrl_rmgr.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/isys_dma_rmgr.c11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/isys_dma_rmgr.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/isys_init.c40
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/isys_stream2mmio_rmgr.c11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/isys_stream2mmio_rmgr.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/rx.c238
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c55
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h14
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline_common.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c23
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/queue/interface/ia_css_queue.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/queue/interface/ia_css_queue_comm.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c31
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/queue/src/queue_access.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/queue/src/queue_access.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr_vbuf.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/rmgr/src/rmgr.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/rmgr/src/rmgr_vbuf.c15
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/spctrl/interface/ia_css_spctrl.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/spctrl/interface/ia_css_spctrl_comm.h13
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/spctrl/src/spctrl.c9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/tagger/interface/ia_css_tagger_common.h9
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/timer/src/timer.c9
-rw-r--r--drivers/staging/media/atomisp/pci/scalar_processor_2400_params.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css.c1659
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_defs.h23
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_dvs_info.h37
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_firmware.c42
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_firmware.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_frac.h41
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_host_data.c9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_hrt.c9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_hrt.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_internal.h108
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_legacy.h10
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_metrics.c9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_metrics.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_mipi.c129
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_mipi.h11
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_mmu.c9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_dvs.c12
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_dvs.h32
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_shading.c28
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_shading.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_params.c123
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_params.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_params_internal.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_properties.c9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_sp.c298
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_sp.h20
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_stream_format.c9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_stream_format.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_struct.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_uds.h9
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_version.c9
-rw-r--r--drivers/staging/media/atomisp/pci/str2mem_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/streaming_to_mipi_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/system_global.h12
-rw-r--r--drivers/staging/media/atomisp/pci/system_local.c9
-rw-r--r--drivers/staging/media/atomisp/pci/system_local.h9
-rw-r--r--drivers/staging/media/atomisp/pci/timed_controller_defs.h9
-rw-r--r--drivers/staging/media/atomisp/pci/version.h9
-rw-r--r--drivers/staging/media/av7110/Kconfig22
-rw-r--r--drivers/staging/media/av7110/Makefile2
-rw-r--r--drivers/staging/media/av7110/TODO3
-rw-r--r--drivers/staging/media/av7110/audio-bilingual-channel-select.rst58
-rw-r--r--drivers/staging/media/av7110/audio-channel-select.rst57
-rw-r--r--drivers/staging/media/av7110/audio-clear-buffer.rst48
-rw-r--r--drivers/staging/media/av7110/audio-continue.rst48
-rw-r--r--drivers/staging/media/av7110/audio-fclose.rst51
-rw-r--r--drivers/staging/media/av7110/audio-fopen.rst103
-rw-r--r--drivers/staging/media/av7110/audio-fwrite.rst79
-rw-r--r--drivers/staging/media/av7110/audio-get-capabilities.rst54
-rw-r--r--drivers/staging/media/av7110/audio-get-status.rst54
-rw-r--r--drivers/staging/media/av7110/audio-pause.rst49
-rw-r--r--drivers/staging/media/av7110/audio-play.rst48
-rw-r--r--drivers/staging/media/av7110/audio-select-source.rst56
-rw-r--r--drivers/staging/media/av7110/audio-set-av-sync.rst58
-rw-r--r--drivers/staging/media/av7110/audio-set-bypass-mode.rst62
-rw-r--r--drivers/staging/media/av7110/audio-set-id.rst59
-rw-r--r--drivers/staging/media/av7110/audio-set-mixer.rst53
-rw-r--r--drivers/staging/media/av7110/audio-set-mute.rst62
-rw-r--r--drivers/staging/media/av7110/audio-set-streamtype.rst66
-rw-r--r--drivers/staging/media/av7110/audio-stop.rst48
-rw-r--r--drivers/staging/media/av7110/audio.rst27
-rw-r--r--drivers/staging/media/av7110/audio_data_types.rst116
-rw-r--r--drivers/staging/media/av7110/audio_function_calls.rst30
-rw-r--r--drivers/staging/media/av7110/av7110.c504
-rw-r--r--drivers/staging/media/av7110/av7110.h54
-rw-r--r--drivers/staging/media/av7110/av7110_av.c244
-rw-r--r--drivers/staging/media/av7110/av7110_av.h37
-rw-r--r--drivers/staging/media/av7110/av7110_ca.c68
-rw-r--r--drivers/staging/media/av7110/av7110_ca.h12
-rw-r--r--drivers/staging/media/av7110/av7110_hw.c233
-rw-r--r--drivers/staging/media/av7110/av7110_hw.h79
-rw-r--r--drivers/staging/media/av7110/av7110_ipack.c282
-rw-r--r--drivers/staging/media/av7110/av7110_ipack.h12
-rw-r--r--drivers/staging/media/av7110/av7110_ir.c3
-rw-r--r--drivers/staging/media/av7110/av7110_v4l.c254
-rw-r--r--drivers/staging/media/av7110/budget-patch.c665
-rw-r--r--drivers/staging/media/av7110/dvb_filter.c116
-rw-r--r--drivers/staging/media/av7110/dvb_filter.h22
-rw-r--r--drivers/staging/media/av7110/sp8870.c200
-rw-r--r--drivers/staging/media/av7110/sp8870.h24
-rw-r--r--drivers/staging/media/av7110/video-clear-buffer.rst54
-rw-r--r--drivers/staging/media/av7110/video-command.rst96
-rw-r--r--drivers/staging/media/av7110/video-continue.rst57
-rw-r--r--drivers/staging/media/av7110/video-fast-forward.rst72
-rw-r--r--drivers/staging/media/av7110/video-fclose.rst51
-rw-r--r--drivers/staging/media/av7110/video-fopen.rst111
-rw-r--r--drivers/staging/media/av7110/video-freeze.rst61
-rw-r--r--drivers/staging/media/av7110/video-fwrite.rst79
-rw-r--r--drivers/staging/media/av7110/video-get-capabilities.rst61
-rw-r--r--drivers/staging/media/av7110/video-get-event.rst105
-rw-r--r--drivers/staging/media/av7110/video-get-frame-count.rst65
-rw-r--r--drivers/staging/media/av7110/video-get-pts.rst69
-rw-r--r--drivers/staging/media/av7110/video-get-size.rst69
-rw-r--r--drivers/staging/media/av7110/video-get-status.rst72
-rw-r--r--drivers/staging/media/av7110/video-play.rst57
-rw-r--r--drivers/staging/media/av7110/video-select-source.rst76
-rw-r--r--drivers/staging/media/av7110/video-set-blank.rst64
-rw-r--r--drivers/staging/media/av7110/video-set-display-format.rst60
-rw-r--r--drivers/staging/media/av7110/video-set-format.rst82
-rw-r--r--drivers/staging/media/av7110/video-set-streamtype.rst61
-rw-r--r--drivers/staging/media/av7110/video-slowmotion.rst72
-rw-r--r--drivers/staging/media/av7110/video-stillpicture.rst61
-rw-r--r--drivers/staging/media/av7110/video-stop.rst74
-rw-r--r--drivers/staging/media/av7110/video-try-command.rst66
-rw-r--r--drivers/staging/media/av7110/video.rst36
-rw-r--r--drivers/staging/media/av7110/video_function_calls.rst35
-rw-r--r--drivers/staging/media/av7110/video_types.rst248
-rw-r--r--drivers/staging/media/deprecated/atmel/Kconfig47
-rw-r--r--drivers/staging/media/deprecated/atmel/Makefile8
-rw-r--r--drivers/staging/media/deprecated/atmel/TODO34
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-base.c (renamed from drivers/media/platform/atmel/atmel-isc-base.c)51
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-clk.c (renamed from drivers/media/platform/atmel/atmel-isc-clk.c)8
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-regs.h (renamed from drivers/media/platform/atmel/atmel-isc-regs.h)0
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc.h (renamed from drivers/media/platform/atmel/atmel-isc.h)16
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c (renamed from drivers/media/platform/atmel/atmel-sama5d2-isc.c)47
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c (renamed from drivers/media/platform/atmel/atmel-sama7g5-isc.c)47
-rw-r--r--drivers/staging/media/hantro/Kconfig50
-rw-r--r--drivers/staging/media/hantro/Makefile38
-rw-r--r--drivers/staging/media/hantro/TODO2
-rw-r--r--drivers/staging/media/hantro/hantro_g2.c44
-rw-r--r--drivers/staging/media/hantro/hantro_hw.h441
-rw-r--r--drivers/staging/media/hantro/hantro_jpeg.c348
-rw-r--r--drivers/staging/media/hantro/hantro_postproc.c279
-rw-r--r--drivers/staging/media/imx/Kconfig25
-rw-r--r--drivers/staging/media/imx/Makefile10
-rw-r--r--drivers/staging/media/imx/TODO41
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c34
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c38
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c58
-rw-r--r--drivers/staging/media/imx/imx-media-csc-scaler.c33
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c206
-rw-r--r--drivers/staging/media/imx/imx-media-dev-common.c22
-rw-r--r--drivers/staging/media/imx/imx-media-dev.c6
-rw-r--r--drivers/staging/media/imx/imx-media-fim.c72
-rw-r--r--drivers/staging/media/imx/imx-media-of.c17
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c129
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c86
-rw-r--r--drivers/staging/media/imx/imx-media.h24
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c65
-rw-r--r--drivers/staging/media/ipu3/include/uapi/intel-ipu3.h13
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-fw.c4
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-fw.h2
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-params.c50
-rw-r--r--drivers/staging/media/ipu3/ipu3-css.c18
-rw-r--r--drivers/staging/media/ipu3/ipu3-v4l2.c149
-rw-r--r--drivers/staging/media/ipu3/ipu3.c19
-rw-r--r--drivers/staging/media/ipu3/ipu3.h1
-rw-r--r--drivers/staging/media/ipu7/Kconfig19
-rw-r--r--drivers/staging/media/ipu7/Makefile23
-rw-r--r--drivers/staging/media/ipu7/TODO28
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h163
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h175
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h19
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h19
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h412
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h465
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h24
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h49
-rw-r--r--drivers/staging/media/ipu7/ipu7-boot.c430
-rw-r--r--drivers/staging/media/ipu7/ipu7-boot.h25
-rw-r--r--drivers/staging/media/ipu7/ipu7-bus.c158
-rw-r--r--drivers/staging/media/ipu7/ipu7-bus.h69
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress-regs.h461
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress.c1192
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress.h77
-rw-r--r--drivers/staging/media/ipu7/ipu7-cpd.c276
-rw-r--r--drivers/staging/media/ipu7/ipu7-cpd.h16
-rw-r--r--drivers/staging/media/ipu7/ipu7-dma.c477
-rw-r--r--drivers/staging/media/ipu7/ipu7-dma.h46
-rw-r--r--drivers/staging/media/ipu7/ipu7-fw-isys.c301
-rw-r--r--drivers/staging/media/ipu7/ipu7-fw-isys.h39
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi-phy.c1034
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi-phy.h16
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h1197
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2.c543
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2.h64
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-queue.c828
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-queue.h72
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-subdev.c333
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-subdev.h52
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-video.c1078
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-video.h117
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys.c1166
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys.h140
-rw-r--r--drivers/staging/media/ipu7/ipu7-mmu.c853
-rw-r--r--drivers/staging/media/ipu7/ipu7-mmu.h414
-rw-r--r--drivers/staging/media/ipu7/ipu7-platform-regs.h82
-rw-r--r--drivers/staging/media/ipu7/ipu7-syscom.c78
-rw-r--r--drivers/staging/media/ipu7/ipu7-syscom.h35
-rw-r--r--drivers/staging/media/ipu7/ipu7.c2778
-rw-r--r--drivers/staging/media/ipu7/ipu7.h242
-rw-r--r--drivers/staging/media/max96712/max96712.c143
-rw-r--r--drivers/staging/media/meson/vdec/codec_vp9.c10
-rw-r--r--drivers/staging/media/meson/vdec/esparser.c4
-rw-r--r--drivers/staging/media/meson/vdec/vdec.c60
-rw-r--r--drivers/staging/media/meson/vdec/vdec.h6
-rw-r--r--drivers/staging/media/meson/vdec/vdec_1.c16
-rw-r--r--drivers/staging/media/meson/vdec/vdec_hevc.c49
-rw-r--r--drivers/staging/media/meson/vdec/vdec_platform.c53
-rw-r--r--drivers/staging/media/meson/vdec/vdec_platform.h2
-rw-r--r--drivers/staging/media/omap4iss/Kconfig12
-rw-r--r--drivers/staging/media/omap4iss/Makefile9
-rw-r--r--drivers/staging/media/omap4iss/TODO3
-rw-r--r--drivers/staging/media/omap4iss/iss.c1358
-rw-r--r--drivers/staging/media/omap4iss/iss.h247
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c1380
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.h155
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.c277
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.h47
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.c580
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.h63
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.c845
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.h89
-rw-r--r--drivers/staging/media/omap4iss/iss_regs.h899
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.c885
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.h72
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c1287
-rw-r--r--drivers/staging/media/omap4iss/iss_video.h196
-rw-r--r--drivers/staging/media/rkvdec/Kconfig17
-rw-r--r--drivers/staging/media/rkvdec/Makefile3
-rw-r--r--drivers/staging/media/rkvdec/TODO11
-rw-r--r--drivers/staging/media/rkvdec/rkvdec.c1113
-rw-r--r--drivers/staging/media/rkvdec/rkvdec.h127
-rw-r--r--drivers/staging/media/starfive/Kconfig5
-rw-r--r--drivers/staging/media/starfive/Makefile2
-rw-r--r--drivers/staging/media/starfive/camss/Kconfig18
-rw-r--r--drivers/staging/media/starfive/camss/Makefile13
-rw-r--r--drivers/staging/media/starfive/camss/TODO.txt4
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.c438
-rw-r--r--drivers/staging/media/starfive/camss/stf-camss.h134
-rw-r--r--drivers/staging/media/starfive/camss/stf-capture.c605
-rw-r--r--drivers/staging/media/starfive/camss/stf-capture.h86
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp-hw-ops.c445
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.c379
-rw-r--r--drivers/staging/media/starfive/camss/stf-isp.h428
-rw-r--r--drivers/staging/media/starfive/camss/stf-video.c570
-rw-r--r--drivers/staging/media/starfive/camss/stf-video.h100
-rw-r--r--drivers/staging/media/stkwebcam/Kconfig18
-rw-r--r--drivers/staging/media/stkwebcam/Makefile5
-rw-r--r--drivers/staging/media/stkwebcam/TODO12
-rw-r--r--drivers/staging/media/stkwebcam/stk-sensor.c587
-rw-r--r--drivers/staging/media/stkwebcam/stk-webcam.c1434
-rw-r--r--drivers/staging/media/stkwebcam/stk-webcam.h123
-rw-r--r--drivers/staging/media/sunxi/Kconfig1
-rw-r--r--drivers/staging/media/sunxi/Makefile1
-rw-r--r--drivers/staging/media/sunxi/cedrus/Kconfig2
-rw-r--r--drivers/staging/media/sunxi/cedrus/TODO23
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus.c131
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus.h67
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_dec.c8
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h264.c141
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h265.c159
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_hw.c63
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_hw.h2
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c30
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_regs.h18
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_video.c205
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_video.h2
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_vp8.c45
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/Kconfig15
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/Makefile4
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/TODO.txt6
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c551
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h90
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c740
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h78
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c566
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h52
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c581
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h66
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h275
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h43
-rw-r--r--drivers/staging/media/tegra-video/Kconfig1
-rw-r--r--drivers/staging/media/tegra-video/Makefile2
-rw-r--r--drivers/staging/media/tegra-video/csi.c85
-rw-r--r--drivers/staging/media/tegra-video/csi.h6
-rw-r--r--drivers/staging/media/tegra-video/tegra20.c657
-rw-r--r--drivers/staging/media/tegra-video/tegra210.c96
-rw-r--r--drivers/staging/media/tegra-video/vi.c309
-rw-r--r--drivers/staging/media/tegra-video/vi.h71
-rw-r--r--drivers/staging/media/tegra-video/video.c5
-rw-r--r--drivers/staging/media/tegra-video/video.h2
-rw-r--r--drivers/staging/media/tegra-video/vip.c285
-rw-r--r--drivers/staging/media/tegra-video/vip.h68
-rw-r--r--drivers/staging/media/zoran/TODO19
-rw-r--r--drivers/staging/media/zoran/videocodec.h325
-rw-r--r--drivers/staging/media/zoran/zoran_card.h30
-rw-r--r--drivers/staging/media/zoran/zoran_device.h60
-rw-r--r--drivers/staging/media/zoran/zr36016.c430
-rw-r--r--drivers/staging/media/zoran/zr36057.h154
-rw-r--r--drivers/staging/media/zoran/zr36060.h203
-rw-r--r--drivers/staging/most/Kconfig2
-rw-r--r--drivers/staging/most/Makefile1
-rw-r--r--drivers/staging/most/TODO7
-rw-r--r--drivers/staging/most/dim2/dim2.c19
-rw-r--r--drivers/staging/most/dim2/hal.c9
-rw-r--r--drivers/staging/most/dim2/hal.h6
-rw-r--r--drivers/staging/most/i2c/Kconfig13
-rw-r--r--drivers/staging/most/i2c/Makefile4
-rw-r--r--drivers/staging/most/i2c/i2c.c373
-rw-r--r--drivers/staging/most/video/video.c28
-rw-r--r--drivers/staging/nvec/TODO6
-rw-r--r--drivers/staging/nvec/nvec.c72
-rw-r--r--drivers/staging/nvec/nvec_kbd.c13
-rw-r--r--drivers/staging/nvec/nvec_paz00.c5
-rw-r--r--drivers/staging/nvec/nvec_power.c6
-rw-r--r--drivers/staging/nvec/nvec_ps2.c47
-rw-r--r--drivers/staging/octeon/TODO1
-rw-r--r--drivers/staging/octeon/ethernet-mdio.c2
-rw-r--r--drivers/staging/octeon/ethernet-tx.c55
-rw-r--r--drivers/staging/octeon/ethernet-tx.h4
-rw-r--r--drivers/staging/octeon/ethernet.c5
-rw-r--r--drivers/staging/octeon/octeon-stubs.h142
-rw-r--r--drivers/staging/olpc_dcon/Kconfig17
-rw-r--r--drivers/staging/olpc_dcon/Makefile5
-rw-r--r--drivers/staging/olpc_dcon/TODO15
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.c809
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.h112
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1.c201
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c204
-rw-r--r--drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts48
-rw-r--r--drivers/staging/pi433/Documentation/devicetree/pi433.txt62
-rw-r--r--drivers/staging/pi433/Documentation/pi433.txt274
-rw-r--r--drivers/staging/pi433/Kconfig17
-rw-r--r--drivers/staging/pi433/Makefile4
-rw-r--r--drivers/staging/pi433/TODO5
-rw-r--r--drivers/staging/pi433/pi433_if.c1449
-rw-r--r--drivers/staging/pi433/pi433_if.h148
-rw-r--r--drivers/staging/pi433/rf69.c832
-rw-r--r--drivers/staging/pi433/rf69.h70
-rw-r--r--drivers/staging/pi433/rf69_enum.h126
-rw-r--r--drivers/staging/pi433/rf69_registers.h478
-rw-r--r--drivers/staging/qlge/Kconfig11
-rw-r--r--drivers/staging/qlge/Makefile8
-rw-r--r--drivers/staging/qlge/TODO33
-rw-r--r--drivers/staging/qlge/qlge.h2293
-rw-r--r--drivers/staging/qlge/qlge_dbg.c1314
-rw-r--r--drivers/staging/qlge/qlge_devlink.c167
-rw-r--r--drivers/staging/qlge/qlge_devlink.h9
-rw-r--r--drivers/staging/qlge/qlge_ethtool.c746
-rw-r--r--drivers/staging/qlge/qlge_main.c4845
-rw-r--r--drivers/staging/qlge/qlge_mpi.c1273
-rw-r--r--drivers/staging/r8188eu/Kconfig16
-rw-r--r--drivers/staging/r8188eu/Makefile54
-rw-r--r--drivers/staging/r8188eu/TODO16
-rw-r--r--drivers/staging/r8188eu/core/rtw_ap.c1142
-rw-r--r--drivers/staging/r8188eu/core/rtw_br_ext.c661
-rw-r--r--drivers/staging/r8188eu/core/rtw_cmd.c1533
-rw-r--r--drivers/staging/r8188eu/core/rtw_efuse.c74
-rw-r--r--drivers/staging/r8188eu/core/rtw_fw.c337
-rw-r--r--drivers/staging/r8188eu/core/rtw_ieee80211.c1150
-rw-r--r--drivers/staging/r8188eu/core/rtw_ioctl_set.c488
-rw-r--r--drivers/staging/r8188eu/core/rtw_iol.c160
-rw-r--r--drivers/staging/r8188eu/core/rtw_led.c411
-rw-r--r--drivers/staging/r8188eu/core/rtw_mlme.c1988
-rw-r--r--drivers/staging/r8188eu/core/rtw_mlme_ext.c7933
-rw-r--r--drivers/staging/r8188eu/core/rtw_p2p.c1928
-rw-r--r--drivers/staging/r8188eu/core/rtw_pwrctrl.c448
-rw-r--r--drivers/staging/r8188eu/core/rtw_recv.c1827
-rw-r--r--drivers/staging/r8188eu/core/rtw_rf.c29
-rw-r--r--drivers/staging/r8188eu/core/rtw_security.c1376
-rw-r--r--drivers/staging/r8188eu/core/rtw_sta_mgt.c480
-rw-r--r--drivers/staging/r8188eu/core/rtw_wlan_util.c1598
-rw-r--r--drivers/staging/r8188eu/core/rtw_xmit.c2138
-rw-r--r--drivers/staging/r8188eu/hal/Hal8188ERateAdaptive.c654
-rw-r--r--drivers/staging/r8188eu/hal/HalHWImg8188E_BB.c685
-rw-r--r--drivers/staging/r8188eu/hal/HalHWImg8188E_MAC.c208
-rw-r--r--drivers/staging/r8188eu/hal/HalHWImg8188E_RF.c240
-rw-r--r--drivers/staging/r8188eu/hal/HalPhyRf_8188e.c908
-rw-r--r--drivers/staging/r8188eu/hal/HalPwrSeqCmd.c149
-rw-r--r--drivers/staging/r8188eu/hal/hal_com.c312
-rw-r--r--drivers/staging/r8188eu/hal/hal_intf.c55
-rw-r--r--drivers/staging/r8188eu/hal/odm.c821
-rw-r--r--drivers/staging/r8188eu/hal/odm_HWConfig.c354
-rw-r--r--drivers/staging/r8188eu/hal/odm_RTL8188E.c264
-rw-r--r--drivers/staging/r8188eu/hal/odm_RegConfig8188E.c89
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_cmd.c696
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_dm.c146
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_hal_init.c930
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_phycfg.c727
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_rf6052.c417
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_rxdesc.c163
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188e_xmit.c22
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188eu_recv.c91
-rw-r--r--drivers/staging/r8188eu/hal/rtl8188eu_xmit.c642
-rw-r--r--drivers/staging/r8188eu/hal/usb_halinit.c1103
-rw-r--r--drivers/staging/r8188eu/hal/usb_ops_linux.c489
-rw-r--r--drivers/staging/r8188eu/include/Hal8188EPhyCfg.h98
-rw-r--r--drivers/staging/r8188eu/include/Hal8188EPhyReg.h1072
-rw-r--r--drivers/staging/r8188eu/include/Hal8188ERateAdaptive.h49
-rw-r--r--drivers/staging/r8188eu/include/HalHWImg8188E_BB.h27
-rw-r--r--drivers/staging/r8188eu/include/HalHWImg8188E_MAC.h13
-rw-r--r--drivers/staging/r8188eu/include/HalHWImg8188E_RF.h13
-rw-r--r--drivers/staging/r8188eu/include/HalPhyRf_8188e.h36
-rw-r--r--drivers/staging/r8188eu/include/HalPwrSeqCmd.h18
-rw-r--r--drivers/staging/r8188eu/include/HalVerDef.h42
-rw-r--r--drivers/staging/r8188eu/include/drv_types.h232
-rw-r--r--drivers/staging/r8188eu/include/hal_com.h150
-rw-r--r--drivers/staging/r8188eu/include/hal_intf.h45
-rw-r--r--drivers/staging/r8188eu/include/ieee80211.h817
-rw-r--r--drivers/staging/r8188eu/include/ioctl_cfg80211.h89
-rw-r--r--drivers/staging/r8188eu/include/mlme_osdep.h19
-rw-r--r--drivers/staging/r8188eu/include/odm.h422
-rw-r--r--drivers/staging/r8188eu/include/odm_HWConfig.h70
-rw-r--r--drivers/staging/r8188eu/include/odm_RTL8188E.h28
-rw-r--r--drivers/staging/r8188eu/include/odm_RegConfig8188E.h21
-rw-r--r--drivers/staging/r8188eu/include/odm_RegDefine11N.h47
-rw-r--r--drivers/staging/r8188eu/include/odm_types.h21
-rw-r--r--drivers/staging/r8188eu/include/osdep_intf.h59
-rw-r--r--drivers/staging/r8188eu/include/osdep_service.h153
-rw-r--r--drivers/staging/r8188eu/include/recv_osdep.h30
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_cmd.h90
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_dm.h28
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_hal.h191
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_recv.h42
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_rf.h18
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_spec.h1169
-rw-r--r--drivers/staging/r8188eu/include/rtl8188e_xmit.h152
-rw-r--r--drivers/staging/r8188eu/include/rtw_ap.h36
-rw-r--r--drivers/staging/r8188eu/include/rtw_br_ext.h43
-rw-r--r--drivers/staging/r8188eu/include/rtw_cmd.h939
-rw-r--r--drivers/staging/r8188eu/include/rtw_eeprom.h15
-rw-r--r--drivers/staging/r8188eu/include/rtw_efuse.h11
-rw-r--r--drivers/staging/r8188eu/include/rtw_event.h97
-rw-r--r--drivers/staging/r8188eu/include/rtw_fw.h17
-rw-r--r--drivers/staging/r8188eu/include/rtw_ht.h28
-rw-r--r--drivers/staging/r8188eu/include/rtw_io.h302
-rw-r--r--drivers/staging/r8188eu/include/rtw_ioctl.h13
-rw-r--r--drivers/staging/r8188eu/include/rtw_ioctl_set.h25
-rw-r--r--drivers/staging/r8188eu/include/rtw_iol.h55
-rw-r--r--drivers/staging/r8188eu/include/rtw_led.h68
-rw-r--r--drivers/staging/r8188eu/include/rtw_mlme.h599
-rw-r--r--drivers/staging/r8188eu/include/rtw_mlme_ext.h787
-rw-r--r--drivers/staging/r8188eu/include/rtw_p2p.h118
-rw-r--r--drivers/staging/r8188eu/include/rtw_pwrctrl.h114
-rw-r--r--drivers/staging/r8188eu/include/rtw_recv.h344
-rw-r--r--drivers/staging/r8188eu/include/rtw_rf.h80
-rw-r--r--drivers/staging/r8188eu/include/rtw_security.h231
-rw-r--r--drivers/staging/r8188eu/include/rtw_xmit.h361
-rw-r--r--drivers/staging/r8188eu/include/sta_info.h313
-rw-r--r--drivers/staging/r8188eu/include/usb_ops.h59
-rw-r--r--drivers/staging/r8188eu/include/usb_ops_linux.h29
-rw-r--r--drivers/staging/r8188eu/include/usb_osintf.h21
-rw-r--r--drivers/staging/r8188eu/include/wifi.h838
-rw-r--r--drivers/staging/r8188eu/include/wlan_bssdef.h327
-rw-r--r--drivers/staging/r8188eu/include/xmit_osdep.h49
-rw-r--r--drivers/staging/r8188eu/os_dep/ioctl_linux.c3787
-rw-r--r--drivers/staging/r8188eu/os_dep/mlme_linux.c205
-rw-r--r--drivers/staging/r8188eu/os_dep/os_intfs.c827
-rw-r--r--drivers/staging/r8188eu/os_dep/osdep_service.c230
-rw-r--r--drivers/staging/r8188eu/os_dep/recv_linux.c165
-rw-r--r--drivers/staging/r8188eu/os_dep/usb_intf.c469
-rw-r--r--drivers/staging/r8188eu/os_dep/usb_ops_linux.c198
-rw-r--r--drivers/staging/r8188eu/os_dep/xmit_linux.c237
-rw-r--r--drivers/staging/rtl8192e/Kconfig51
-rw-r--r--drivers/staging/rtl8192e/Makefile20
-rw-r--r--drivers/staging/rtl8192e/TODO2
-rw-r--r--drivers/staging/rtl8192e/dot11d.c168
-rw-r--r--drivers/staging/rtl8192e/dot11d.h84
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/Kconfig9
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/Makefile19
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8190P_def.h310
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c214
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h17
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c85
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h12
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c2355
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h38
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c196
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h52
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h439
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.c551
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.h33
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c1612
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h90
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h878
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_cam.c257
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_cam.h26
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_core.c2645
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_core.h603
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_dm.c2517
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_dm.h195
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c84
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h12
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c37
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.c81
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.h19
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pm.c92
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pm.h16
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_ps.c296
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_ps.h31
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_wx.c1218
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_wx.h13
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BA.h60
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BAProc.c551
-rw-r--r--drivers/staging/rtl8192e/rtl819x_HT.h277
-rw-r--r--drivers/staging/rtl8192e/rtl819x_HTProc.c895
-rw-r--r--drivers/staging/rtl8192e/rtl819x_Qos.h175
-rw-r--r--drivers/staging/rtl8192e/rtl819x_TS.h58
-rw-r--r--drivers/staging/rtl8192e/rtl819x_TSProc.c522
-rw-r--r--drivers/staging/rtl8192e/rtllib.h2157
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_ccmp.c421
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_tkip.c731
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_wep.c249
-rw-r--r--drivers/staging/rtl8192e/rtllib_debug.h55
-rw-r--r--drivers/staging/rtl8192e/rtllib_module.c185
-rw-r--r--drivers/staging/rtl8192e/rtllib_rx.c2744
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac.c3264
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac_wx.c648
-rw-r--r--drivers/staging/rtl8192e/rtllib_tx.c970
-rw-r--r--drivers/staging/rtl8192e/rtllib_wx.c787
-rw-r--r--drivers/staging/rtl8192u/Kconfig12
-rw-r--r--drivers/staging/rtl8192u/Makefile26
-rw-r--r--drivers/staging/rtl8192u/authors1
-rw-r--r--drivers/staging/rtl8192u/changes4
-rw-r--r--drivers/staging/rtl8192u/ieee80211/dot11d.c174
-rw-r--r--drivers/staging/rtl8192u/ieee80211/dot11d.h57
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211.h2449
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.c235
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.h86
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c421
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_tkip.c724
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_wep.c247
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_module.c290
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c2464
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c3056
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c598
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c839
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c810
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_BA.h54
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c700
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h302
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c1295
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_Qos.h82
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_TS.h102
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c534
-rw-r--r--drivers/staging/rtl8192u/r8180_93cx6.c170
-rw-r--r--drivers/staging/rtl8192u/r8180_93cx6.h25
-rw-r--r--drivers/staging/rtl8192u/r8190_rtl8256.c294
-rw-r--r--drivers/staging/rtl8192u/r8190_rtl8256.h24
-rw-r--r--drivers/staging/rtl8192u/r8192U.h1120
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c4940
-rw-r--r--drivers/staging/rtl8192u/r8192U_dm.c3019
-rw-r--r--drivers/staging/rtl8192u/r8192U_dm.h177
-rw-r--r--drivers/staging/rtl8192u/r8192U_hw.h246
-rw-r--r--drivers/staging/rtl8192u/r8192U_wx.c943
-rw-r--r--drivers/staging/rtl8192u/r8192U_wx.h24
-rw-r--r--drivers/staging/rtl8192u/r819xU_cmdpkt.c508
-rw-r--r--drivers/staging/rtl8192u/r819xU_cmdpkt.h190
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware.c340
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware.h19
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware_img.c549
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware_img.h26
-rw-r--r--drivers/staging/rtl8192u/r819xU_phy.c1733
-rw-r--r--drivers/staging/rtl8192u/r819xU_phy.h83
-rw-r--r--drivers/staging/rtl8192u/r819xU_phyreg.h143
-rw-r--r--drivers/staging/rtl8712/Kconfig21
-rw-r--r--drivers/staging/rtl8712/Makefile35
-rw-r--r--drivers/staging/rtl8712/TODO13
-rw-r--r--drivers/staging/rtl8712/basic_types.h28
-rw-r--r--drivers/staging/rtl8712/drv_types.h175
-rw-r--r--drivers/staging/rtl8712/ethernet.h21
-rw-r--r--drivers/staging/rtl8712/hal_init.c401
-rw-r--r--drivers/staging/rtl8712/ieee80211.c415
-rw-r--r--drivers/staging/rtl8712/ieee80211.h165
-rw-r--r--drivers/staging/rtl8712/mlme_linux.c160
-rw-r--r--drivers/staging/rtl8712/mlme_osdep.h31
-rw-r--r--drivers/staging/rtl8712/mp_custom_oid.h287
-rw-r--r--drivers/staging/rtl8712/os_intfs.c464
-rw-r--r--drivers/staging/rtl8712/osdep_intf.h32
-rw-r--r--drivers/staging/rtl8712/osdep_service.h60
-rw-r--r--drivers/staging/rtl8712/recv_linux.c139
-rw-r--r--drivers/staging/rtl8712/recv_osdep.h39
-rw-r--r--drivers/staging/rtl8712/rtl8712_bitdef.h26
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.c409
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.h231
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h95
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h19
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h41
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h32
-rw-r--r--drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h65
-rw-r--r--drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h24
-rw-r--r--drivers/staging/rtl8712/rtl8712_efuse.c564
-rw-r--r--drivers/staging/rtl8712/rtl8712_efuse.h43
-rw-r--r--drivers/staging/rtl8712/rtl8712_event.h86
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h131
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h61
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_bitdef.h68
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_regdef.h29
-rw-r--r--drivers/staging/rtl8712/rtl8712_hal.h142
-rw-r--r--drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h44
-rw-r--r--drivers/staging/rtl8712/rtl8712_io.c99
-rw-r--r--drivers/staging/rtl8712/rtl8712_led.c1830
-rw-r--r--drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h31
-rw-r--r--drivers/staging/rtl8712/rtl8712_macsetting_regdef.h20
-rw-r--r--drivers/staging/rtl8712/rtl8712_powersave_bitdef.h39
-rw-r--r--drivers/staging/rtl8712/rtl8712_powersave_regdef.h26
-rw-r--r--drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h36
-rw-r--r--drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h43
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.c1079
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.h145
-rw-r--r--drivers/staging/rtl8712/rtl8712_regdef.h32
-rw-r--r--drivers/staging/rtl8712/rtl8712_security_bitdef.h34
-rw-r--r--drivers/staging/rtl8712/rtl8712_spec.h121
-rw-r--r--drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h163
-rw-r--r--drivers/staging/rtl8712/rtl8712_syscfg_regdef.h42
-rw-r--r--drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h49
-rw-r--r--drivers/staging/rtl8712/rtl8712_timectrl_regdef.h26
-rw-r--r--drivers/staging/rtl8712/rtl8712_wmac_bitdef.h49
-rw-r--r--drivers/staging/rtl8712/rtl8712_wmac_regdef.h36
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.c745
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.h108
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.c796
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.h761
-rw-r--r--drivers/staging/rtl8712/rtl871x_debug.h130
-rw-r--r--drivers/staging/rtl8712/rtl871x_eeprom.c220
-rw-r--r--drivers/staging/rtl8712/rtl871x_eeprom.h88
-rw-r--r--drivers/staging/rtl8712/rtl871x_event.h109
-rw-r--r--drivers/staging/rtl8712/rtl871x_ht.h33
-rw-r--r--drivers/staging/rtl8712/rtl871x_io.c147
-rw-r--r--drivers/staging/rtl8712/rtl871x_io.h236
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl.h94
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_linux.c2330
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_rtl.c519
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_rtl.h109
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.c354
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.h45
-rw-r--r--drivers/staging/rtl8712/rtl871x_led.h118
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c1709
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.h205
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp.c724
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp.h275
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_ioctl.c883
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_ioctl.h328
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h1034
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.c234
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.h113
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.c669
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.h208
-rw-r--r--drivers/staging/rtl8712/rtl871x_rf.h55
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.c1386
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.h218
-rw-r--r--drivers/staging/rtl8712/rtl871x_sta_mgt.c263
-rw-r--r--drivers/staging/rtl8712/rtl871x_wlan_sme.h35
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.c1034
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.h288
-rw-r--r--drivers/staging/rtl8712/sta_info.h132
-rw-r--r--drivers/staging/rtl8712/usb_halinit.c307
-rw-r--r--drivers/staging/rtl8712/usb_intf.c639
-rw-r--r--drivers/staging/rtl8712/usb_ops.c195
-rw-r--r--drivers/staging/rtl8712/usb_ops.h38
-rw-r--r--drivers/staging/rtl8712/usb_ops_linux.c515
-rw-r--r--drivers/staging/rtl8712/usb_osintf.h35
-rw-r--r--drivers/staging/rtl8712/wifi.h196
-rw-r--r--drivers/staging/rtl8712/wlan_bssdef.h223
-rw-r--r--drivers/staging/rtl8712/xmit_linux.c175
-rw-r--r--drivers/staging/rtl8712/xmit_osdep.h52
-rw-r--r--drivers/staging/rtl8723bs/Kconfig2
-rw-r--r--drivers/staging/rtl8723bs/Makefile9
-rw-r--r--drivers/staging/rtl8723bs/TODO4
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_ap.c435
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_btcoex.c13
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_cmd.c126
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_debug.c68
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_efuse.c327
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_ieee80211.c83
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_io.c59
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_ioctl_set.c18
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_mlme.c637
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_mlme_ext.c294
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_odm.c195
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_pwrctrl.c45
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_recv.c300
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_rf.c34
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_security.c366
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_sta_mgt.c26
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_wlan_util.c177
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_xmit.c45
-rw-r--r--drivers/staging/rtl8723bs/hal/Hal8723BReg.h373
-rw-r--r--drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c68
-rw-r--r--drivers/staging/rtl8723bs/hal/HalHWImg8723B_BB.c17
-rw-r--r--drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.c12
-rw-r--r--drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c12
-rw-r--r--drivers/staging/rtl8723bs/hal/HalPhyRf.h2
-rw-r--r--drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c32
-rw-r--r--drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.h7
-rw-r--r--drivers/staging/rtl8723bs/hal/HalPwrSeqCmd.c1
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_btcoex.c36
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_com.c405
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_com_phycfg.c6
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_intf.c191
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_pwr_seq.c2
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_sdio.c1
-rw-r--r--drivers/staging/rtl8723bs/hal/odm.c193
-rw-r--r--drivers/staging/rtl8723bs/hal/odm.h24
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_CfoTracking.c4
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_DIG.c65
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_DIG.h2
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_NoiseMonitor.c130
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_NoiseMonitor.h39
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_precomp.h1
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c65
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_dm.c8
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c1142
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c22
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_rxdesc.c4
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c7
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c40
-rw-r--r--drivers/staging/rtl8723bs/hal/sdio_halinit.c91
-rw-r--r--drivers/staging/rtl8723bs/hal/sdio_ops.c8
-rw-r--r--drivers/staging/rtl8723bs/include/Hal8192CPhyReg.h882
-rw-r--r--drivers/staging/rtl8723bs/include/HalPwrSeqCmd.h11
-rw-r--r--drivers/staging/rtl8723bs/include/basic_types.h46
-rw-r--r--drivers/staging/rtl8723bs/include/drv_types.h37
-rw-r--r--drivers/staging/rtl8723bs/include/hal_btcoex.h2
-rw-r--r--drivers/staging/rtl8723bs/include/hal_com.h31
-rw-r--r--drivers/staging/rtl8723bs/include/hal_com_h2c.h76
-rw-r--r--drivers/staging/rtl8723bs/include/hal_com_reg.h800
-rw-r--r--drivers/staging/rtl8723bs/include/hal_intf.h115
-rw-r--r--drivers/staging/rtl8723bs/include/hal_phy_cfg.h5
-rw-r--r--drivers/staging/rtl8723bs/include/hal_phy_reg_8723b.h69
-rw-r--r--drivers/staging/rtl8723bs/include/hal_pwr_seq.h11
-rw-r--r--drivers/staging/rtl8723bs/include/ieee80211.h50
-rw-r--r--drivers/staging/rtl8723bs/include/ioctl_cfg80211.h1
-rw-r--r--drivers/staging/rtl8723bs/include/mlme_osdep.h19
-rw-r--r--drivers/staging/rtl8723bs/include/osdep_intf.h32
-rw-r--r--drivers/staging/rtl8723bs/include/osdep_service.h9
-rw-r--r--drivers/staging/rtl8723bs/include/osdep_service_linux.h72
-rw-r--r--drivers/staging/rtl8723bs/include/recv_osdep.h40
-rw-r--r--drivers/staging/rtl8723bs/include/rtl8723b_cmd.h3
-rw-r--r--drivers/staging/rtl8723bs/include/rtl8723b_hal.h10
-rw-r--r--drivers/staging/rtl8723bs/include/rtl8723b_recv.h5
-rw-r--r--drivers/staging/rtl8723bs/include/rtl8723b_xmit.h3
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_cmd.h7
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_debug.h14
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_efuse.h33
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_event.h2
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_ht.h22
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_io.h149
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_mlme.h211
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_mlme_ext.h55
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_mp.h374
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_odm.h28
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_pwrctrl.h38
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_recv.h57
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_rf.h2
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_security.h140
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_xmit.h12
-rw-r--r--drivers/staging/rtl8723bs/include/sdio_hal.h6
-rw-r--r--drivers/staging/rtl8723bs/include/sdio_osintf.h16
-rw-r--r--drivers/staging/rtl8723bs/include/sta_info.h72
-rw-r--r--drivers/staging/rtl8723bs/include/wifi.h314
-rw-r--r--drivers/staging/rtl8723bs/include/xmit_osdep.h4
-rw-r--r--drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c433
-rw-r--r--drivers/staging/rtl8723bs/os_dep/ioctl_linux.c1344
-rw-r--r--drivers/staging/rtl8723bs/os_dep/mlme_linux.c180
-rw-r--r--drivers/staging/rtl8723bs/os_dep/os_intfs.c128
-rw-r--r--drivers/staging/rtl8723bs/os_dep/osdep_service.c51
-rw-r--r--drivers/staging/rtl8723bs/os_dep/recv_linux.c229
-rw-r--r--drivers/staging/rtl8723bs/os_dep/sdio_intf.c24
-rw-r--r--drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c2
-rw-r--r--drivers/staging/rtl8723bs/os_dep/wifi_regd.c17
-rw-r--r--drivers/staging/rtl8723bs/os_dep/xmit_linux.c21
-rw-r--r--drivers/staging/rts5208/Kconfig9
-rw-r--r--drivers/staging/rts5208/Makefile5
-rw-r--r--drivers/staging/rts5208/TODO7
-rw-r--r--drivers/staging/rts5208/general.c25
-rw-r--r--drivers/staging/rts5208/general.h19
-rw-r--r--drivers/staging/rts5208/ms.c4309
-rw-r--r--drivers/staging/rts5208/ms.h214
-rw-r--r--drivers/staging/rts5208/rtsx.c993
-rw-r--r--drivers/staging/rts5208/rtsx.h150
-rw-r--r--drivers/staging/rts5208/rtsx_card.c1151
-rw-r--r--drivers/staging/rts5208/rtsx_card.h1087
-rw-r--r--drivers/staging/rts5208/rtsx_chip.c2161
-rw-r--r--drivers/staging/rts5208/rtsx_chip.h987
-rw-r--r--drivers/staging/rts5208/rtsx_scsi.c3279
-rw-r--r--drivers/staging/rts5208/rtsx_scsi.h131
-rw-r--r--drivers/staging/rts5208/rtsx_sys.h36
-rw-r--r--drivers/staging/rts5208/rtsx_transport.c768
-rw-r--r--drivers/staging/rts5208/rtsx_transport.h57
-rw-r--r--drivers/staging/rts5208/sd.c4717
-rw-r--r--drivers/staging/rts5208/sd.h289
-rw-r--r--drivers/staging/rts5208/spi.c906
-rw-r--r--drivers/staging/rts5208/spi.h52
-rw-r--r--drivers/staging/rts5208/xd.c2152
-rw-r--r--drivers/staging/rts5208/xd.h176
-rw-r--r--drivers/staging/sm750fb/Kconfig4
-rw-r--r--drivers/staging/sm750fb/Makefile3
-rw-r--r--drivers/staging/sm750fb/TODO8
-rw-r--r--drivers/staging/sm750fb/ddk750.h3
-rw-r--r--drivers/staging/sm750fb/ddk750_chip.c2
-rw-r--r--drivers/staging/sm750fb/ddk750_display.c1
-rw-r--r--drivers/staging/sm750fb/ddk750_dvi.c62
-rw-r--r--drivers/staging/sm750fb/ddk750_dvi.h57
-rw-r--r--drivers/staging/sm750fb/ddk750_hwi2c.c247
-rw-r--r--drivers/staging/sm750fb/ddk750_hwi2c.h12
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.c94
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.h2
-rw-r--r--drivers/staging/sm750fb/ddk750_power.h8
-rw-r--r--drivers/staging/sm750fb/ddk750_sii164.c408
-rw-r--r--drivers/staging/sm750fb/ddk750_sii164.h174
-rw-r--r--drivers/staging/sm750fb/sm750.c178
-rw-r--r--drivers/staging/sm750fb/sm750.h40
-rw-r--r--drivers/staging/sm750fb/sm750_accel.c19
-rw-r--r--drivers/staging/sm750fb/sm750_cursor.c15
-rw-r--r--drivers/staging/sm750fb/sm750_cursor.h12
-rw-r--r--drivers/staging/sm750fb/sm750_hw.c45
-rw-r--r--drivers/staging/vc04_services/Kconfig43
-rw-r--r--drivers/staging/vc04_services/Makefile16
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/Kconfig2
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/Makefile2
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c6
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c21
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835.c33
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835.h3
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/Kconfig13
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/Makefile11
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/TODO17
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c2006
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.h142
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/controls.c1401
-rw-r--r--drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h118
-rw-r--r--drivers/staging/vc04_services/interface/TESTING82
-rw-r--r--drivers/staging/vc04_services/interface/TODO73
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c1885
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h147
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_cfg.h41
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c74
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h10
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c3700
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h596
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c247
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h21
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h21
-rw-r--r--drivers/staging/vc04_services/vchiq-mmal/Makefile9
-rw-r--r--drivers/staging/vme_user/Kconfig25
-rw-r--r--drivers/staging/vme_user/vme.c358
-rw-r--r--drivers/staging/vme_user/vme.h53
-rw-r--r--drivers/staging/vme_user/vme_bridge.h80
-rw-r--r--drivers/staging/vme_user/vme_fake.c101
-rw-r--r--drivers/staging/vme_user/vme_tsi148.c257
-rw-r--r--drivers/staging/vme_user/vme_tsi148.h1307
-rw-r--r--drivers/staging/vme_user/vme_user.c27
-rw-r--r--drivers/staging/vt6655/Kconfig6
-rw-r--r--drivers/staging/vt6655/Makefile15
-rw-r--r--drivers/staging/vt6655/TODO21
-rw-r--r--drivers/staging/vt6655/baseband.c2301
-rw-r--r--drivers/staging/vt6655/baseband.h74
-rw-r--r--drivers/staging/vt6655/card.c830
-rw-r--r--drivers/staging/vt6655/card.h62
-rw-r--r--drivers/staging/vt6655/channel.c135
-rw-r--r--drivers/staging/vt6655/channel.h17
-rw-r--r--drivers/staging/vt6655/desc.h249
-rw-r--r--drivers/staging/vt6655/device.h292
-rw-r--r--drivers/staging/vt6655/device_cfg.h44
-rw-r--r--drivers/staging/vt6655/device_main.c1815
-rw-r--r--drivers/staging/vt6655/dpc.c145
-rw-r--r--drivers/staging/vt6655/dpc.h21
-rw-r--r--drivers/staging/vt6655/key.c143
-rw-r--r--drivers/staging/vt6655/key.h51
-rw-r--r--drivers/staging/vt6655/mac.c882
-rw-r--r--drivers/staging/vt6655/mac.h688
-rw-r--r--drivers/staging/vt6655/power.c144
-rw-r--r--drivers/staging/vt6655/power.h29
-rw-r--r--drivers/staging/vt6655/rf.c535
-rw-r--r--drivers/staging/vt6655/rf.h75
-rw-r--r--drivers/staging/vt6655/rxtx.c1462
-rw-r--r--drivers/staging/vt6655/rxtx.h185
-rw-r--r--drivers/staging/vt6655/srom.c140
-rw-r--r--drivers/staging/vt6655/srom.h85
-rw-r--r--drivers/staging/vt6655/test9
-rw-r--r--drivers/staging/vt6656/Kconfig7
-rw-r--r--drivers/staging/vt6656/Makefile15
-rw-r--r--drivers/staging/vt6656/TODO19
-rw-r--r--drivers/staging/vt6656/baseband.c455
-rw-r--r--drivers/staging/vt6656/baseband.h75
-rw-r--r--drivers/staging/vt6656/card.c477
-rw-r--r--drivers/staging/vt6656/card.h45
-rw-r--r--drivers/staging/vt6656/channel.c77
-rw-r--r--drivers/staging/vt6656/channel.h21
-rw-r--r--drivers/staging/vt6656/desc.h91
-rw-r--r--drivers/staging/vt6656/device.h386
-rw-r--r--drivers/staging/vt6656/key.c142
-rw-r--r--drivers/staging/vt6656/key.h40
-rw-r--r--drivers/staging/vt6656/mac.c183
-rw-r--r--drivers/staging/vt6656/mac.h373
-rw-r--r--drivers/staging/vt6656/main_usb.c1116
-rw-r--r--drivers/staging/vt6656/power.c112
-rw-r--r--drivers/staging/vt6656/power.h23
-rw-r--r--drivers/staging/vt6656/rf.c443
-rw-r--r--drivers/staging/vt6656/rf.h46
-rw-r--r--drivers/staging/vt6656/rxtx.c730
-rw-r--r--drivers/staging/vt6656/rxtx.h178
-rw-r--r--drivers/staging/vt6656/usbpipe.c506
-rw-r--r--drivers/staging/vt6656/usbpipe.h67
-rw-r--r--drivers/staging/vt6656/wcmd.c185
-rw-r--r--drivers/staging/vt6656/wcmd.h48
-rw-r--r--drivers/staging/wlan-ng/Kconfig13
-rw-r--r--drivers/staging/wlan-ng/Makefile8
-rw-r--r--drivers/staging/wlan-ng/README8
-rw-r--r--drivers/staging/wlan-ng/cfg80211.c718
-rw-r--r--drivers/staging/wlan-ng/hfa384x.h1428
-rw-r--r--drivers/staging/wlan-ng/hfa384x_usb.c3901
-rw-r--r--drivers/staging/wlan-ng/p80211conv.c664
-rw-r--r--drivers/staging/wlan-ng/p80211conv.h162
-rw-r--r--drivers/staging/wlan-ng/p80211hdr.h210
-rw-r--r--drivers/staging/wlan-ng/p80211ioctl.h90
-rw-r--r--drivers/staging/wlan-ng/p80211metadef.h248
-rw-r--r--drivers/staging/wlan-ng/p80211metastruct.h271
-rw-r--r--drivers/staging/wlan-ng/p80211mgmt.h521
-rw-r--r--drivers/staging/wlan-ng/p80211msg.h60
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.c1031
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.h238
-rw-r--r--drivers/staging/wlan-ng/p80211req.c244
-rw-r--r--drivers/staging/wlan-ng/p80211req.h54
-rw-r--r--drivers/staging/wlan-ng/p80211types.h319
-rw-r--r--drivers/staging/wlan-ng/p80211wep.c228
-rw-r--r--drivers/staging/wlan-ng/prism2fw.c1236
-rw-r--r--drivers/staging/wlan-ng/prism2mgmt.c1356
-rw-r--r--drivers/staging/wlan-ng/prism2mgmt.h120
-rw-r--r--drivers/staging/wlan-ng/prism2mib.c763
-rw-r--r--drivers/staging/wlan-ng/prism2sta.c2005
-rw-r--r--drivers/staging/wlan-ng/prism2usb.c299
-rw-r--r--drivers/target/Kconfig1
-rw-r--r--drivers/target/Makefile1
-rw-r--r--drivers/target/iscsi/Kconfig13
-rw-r--r--drivers/target/iscsi/cxgbit/cxgbit_cm.c2
-rw-r--r--drivers/target/iscsi/cxgbit/cxgbit_target.c2
-rw-r--r--drivers/target/iscsi/iscsi_target.c233
-rw-r--r--drivers/target/iscsi/iscsi_target.h3
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c85
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c5
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.c9
-rw-r--r--drivers/target/iscsi/iscsi_target_erl2.c48
-rw-r--r--drivers/target/iscsi/iscsi_target_erl2.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c122
-rw-r--r--drivers/target/iscsi/iscsi_target_login.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c115
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c64
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.h3
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.c7
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c31
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h7
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c141
-rw-r--r--drivers/target/iscsi/iscsi_target_util.h11
-rw-r--r--drivers/target/loopback/tcm_loop.c67
-rw-r--r--drivers/target/sbp/sbp_target.c44
-rw-r--r--drivers/target/target_core_alua.c10
-rw-r--r--drivers/target/target_core_configfs.c301
-rw-r--r--drivers/target/target_core_device.c211
-rw-r--r--drivers/target/target_core_fabric_configfs.c76
-rw-r--r--drivers/target/target_core_fabric_lib.c65
-rw-r--r--drivers/target/target_core_file.c37
-rw-r--r--drivers/target/target_core_iblock.c405
-rw-r--r--drivers/target/target_core_iblock.h2
-rw-r--r--drivers/target/target_core_internal.h12
-rw-r--r--drivers/target/target_core_pr.c151
-rw-r--r--drivers/target/target_core_pscsi.c66
-rw-r--r--drivers/target/target_core_pscsi.h2
-rw-r--r--drivers/target/target_core_rd.c4
-rw-r--r--drivers/target/target_core_sbc.c96
-rw-r--r--drivers/target/target_core_spc.c1065
-rw-r--r--drivers/target/target_core_stat.c233
-rw-r--r--drivers/target/target_core_tmr.c30
-rw-r--r--drivers/target/target_core_tpg.c96
-rw-r--r--drivers/target/target_core_transport.c472
-rw-r--r--drivers/target/target_core_user.c26
-rw-r--r--drivers/target/target_core_xcopy.c131
-rw-r--r--drivers/target/target_core_xcopy.h2
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h1
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c7
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c22
-rw-r--r--drivers/target/tcm_fc/tfc_io.c2
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c2
-rw-r--r--drivers/target/tcm_remote/Kconfig8
-rw-r--r--drivers/target/tcm_remote/Makefile2
-rw-r--r--drivers/target/tcm_remote/tcm_remote.c268
-rw-r--r--drivers/target/tcm_remote/tcm_remote.h20
-rw-r--r--drivers/tc/tc-driver.c8
-rw-r--r--drivers/tc/tc.c2
-rw-r--r--drivers/tee/Kconfig10
-rw-r--r--drivers/tee/Makefile3
-rw-r--r--drivers/tee/amdtee/amdtee_if.h10
-rw-r--r--drivers/tee/amdtee/amdtee_private.h2
-rw-r--r--drivers/tee/amdtee/call.c34
-rw-r--r--drivers/tee/amdtee/core.c56
-rw-r--r--drivers/tee/amdtee/shm_pool.c4
-rw-r--r--drivers/tee/optee/Kconfig23
-rw-r--r--drivers/tee/optee/Makefile1
-rw-r--r--drivers/tee/optee/call.c183
-rw-r--r--drivers/tee/optee/core.c163
-rw-r--r--drivers/tee/optee/device.c31
-rw-r--r--drivers/tee/optee/ffa_abi.c360
-rw-r--r--drivers/tee/optee/notif.c11
-rw-r--r--drivers/tee/optee/optee_ffa.h51
-rw-r--r--drivers/tee/optee/optee_msg.h96
-rw-r--r--drivers/tee/optee/optee_private.h129
-rw-r--r--drivers/tee/optee/optee_rpc_cmd.h36
-rw-r--r--drivers/tee/optee/optee_smc.h63
-rw-r--r--drivers/tee/optee/protmem.c335
-rw-r--r--drivers/tee/optee/rpc.c189
-rw-r--r--drivers/tee/optee/smc_abi.c514
-rw-r--r--drivers/tee/optee/supp.c35
-rw-r--r--drivers/tee/qcomtee/Kconfig13
-rw-r--r--drivers/tee/qcomtee/Makefile9
-rw-r--r--drivers/tee/qcomtee/async.c182
-rw-r--r--drivers/tee/qcomtee/call.c820
-rw-r--r--drivers/tee/qcomtee/core.c915
-rw-r--r--drivers/tee/qcomtee/mem_obj.c169
-rw-r--r--drivers/tee/qcomtee/primordial_obj.c113
-rw-r--r--drivers/tee/qcomtee/qcomtee.h185
-rw-r--r--drivers/tee/qcomtee/qcomtee_msg.h304
-rw-r--r--drivers/tee/qcomtee/qcomtee_object.h316
-rw-r--r--drivers/tee/qcomtee/shm.c150
-rw-r--r--drivers/tee/qcomtee/user_obj.c692
-rw-r--r--drivers/tee/tee_core.c401
-rw-r--r--drivers/tee/tee_heap.c500
-rw-r--r--drivers/tee/tee_private.h53
-rw-r--r--drivers/tee/tee_shm.c335
-rw-r--r--drivers/tee/tee_shm_pool.c2
-rw-r--r--drivers/tee/tstee/Kconfig11
-rw-r--r--drivers/tee/tstee/Makefile3
-rw-r--r--drivers/tee/tstee/core.c480
-rw-r--r--drivers/tee/tstee/tstee_private.h92
-rw-r--r--drivers/thermal/Kconfig121
-rw-r--r--drivers/thermal/Makefile26
-rw-r--r--drivers/thermal/airoha_thermal.c489
-rw-r--r--drivers/thermal/amlogic_thermal.c82
-rw-r--r--drivers/thermal/armada_thermal.c108
-rw-r--r--drivers/thermal/broadcom/bcm2711_thermal.c17
-rw-r--r--drivers/thermal/broadcom/bcm2835_thermal.c74
-rw-r--r--drivers/thermal/broadcom/brcmstb_thermal.c55
-rw-r--r--drivers/thermal/broadcom/ns-thermal.c54
-rw-r--r--drivers/thermal/broadcom/sr-thermal.c17
-rw-r--r--drivers/thermal/cpufreq_cooling.c66
-rw-r--r--drivers/thermal/cpuidle_cooling.c10
-rw-r--r--drivers/thermal/da9062-thermal.c79
-rw-r--r--drivers/thermal/db8500_thermal.c17
-rw-r--r--drivers/thermal/devfreq_cooling.c53
-rw-r--r--drivers/thermal/dove_thermal.c24
-rw-r--r--drivers/thermal/gov_bang_bang.c160
-rw-r--r--drivers/thermal/gov_fair_share.c122
-rw-r--r--drivers/thermal/gov_power_allocator.c577
-rw-r--r--drivers/thermal/gov_step_wise.c183
-rw-r--r--drivers/thermal/gov_user_space.c19
-rw-r--r--drivers/thermal/hisi_thermal.c73
-rw-r--r--drivers/thermal/imx8mm_thermal.c199
-rw-r--r--drivers/thermal/imx91_thermal.c384
-rw-r--r--drivers/thermal/imx_sc_thermal.c91
-rw-r--r--drivers/thermal/imx_thermal.c213
-rw-r--r--drivers/thermal/intel/Kconfig32
-rw-r--r--drivers/thermal/intel/Makefile2
-rw-r--r--drivers/thermal/intel/int340x_thermal/Kconfig7
-rw-r--r--drivers/thermal/intel/int340x_thermal/Makefile5
-rw-r--r--drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c216
-rw-r--r--drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h57
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c164
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3401_thermal.c17
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3402_thermal.c7
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3403_thermal.c21
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3406_thermal.c3
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c340
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h14
-rw-r--r--drivers/thermal/intel/int340x_thermal/platform_temperature_control.c313
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.c256
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.h49
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c311
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c18
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c182
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c127
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c92
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c229
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c284
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c256
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c137
-rw-r--r--drivers/thermal/intel/intel_hfi.c279
-rw-r--r--drivers/thermal/intel/intel_menlow.c523
-rw-r--r--drivers/thermal/intel/intel_pch_thermal.c418
-rw-r--r--drivers/thermal/intel/intel_powerclamp.c615
-rw-r--r--drivers/thermal/intel/intel_quark_dts_thermal.c115
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.c245
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.h15
-rw-r--r--drivers/thermal/intel/intel_soc_dts_thermal.c19
-rw-r--r--drivers/thermal/intel/intel_tcc.c306
-rw-r--r--drivers/thermal/intel/intel_tcc_cooling.c79
-rw-r--r--drivers/thermal/intel/therm_throt.c97
-rw-r--r--drivers/thermal/intel/thermal_interrupt.h6
-rw-r--r--drivers/thermal/intel/x86_pkg_temp_thermal.c189
-rw-r--r--drivers/thermal/k3_bandgap.c23
-rw-r--r--drivers/thermal/k3_j72xx_bandgap.c198
-rw-r--r--drivers/thermal/kirkwood_thermal.c21
-rw-r--r--drivers/thermal/loongson2_thermal.c218
-rw-r--r--drivers/thermal/max77620_thermal.c22
-rw-r--r--drivers/thermal/mediatek/Kconfig37
-rw-r--r--drivers/thermal/mediatek/Makefile2
-rw-r--r--drivers/thermal/mediatek/auxadc_thermal.c1316
-rw-r--r--drivers/thermal/mediatek/lvts_thermal.c1889
-rw-r--r--drivers/thermal/mtk_thermal.c1133
-rw-r--r--drivers/thermal/pcie_cooling.c80
-rw-r--r--drivers/thermal/qcom/Kconfig5
-rw-r--r--drivers/thermal/qcom/lmh.c20
-rw-r--r--drivers/thermal/qcom/qcom-spmi-adc-tm5.c58
-rw-r--r--drivers/thermal/qcom/qcom-spmi-temp-alarm.c673
-rw-r--r--drivers/thermal/qcom/tsens-8960.c3
-rw-r--r--drivers/thermal/qcom/tsens-v0_1.c686
-rw-r--r--drivers/thermal/qcom/tsens-v1.c396
-rw-r--r--drivers/thermal/qcom/tsens-v2.c199
-rw-r--r--drivers/thermal/qcom/tsens.c408
-rw-r--r--drivers/thermal/qcom/tsens.h74
-rw-r--r--drivers/thermal/qoriq_thermal.c134
-rw-r--r--drivers/thermal/rcar_gen3_thermal.c586
-rw-r--r--drivers/thermal/renesas/Kconfig43
-rw-r--r--drivers/thermal/renesas/Makefile7
-rw-r--r--drivers/thermal/renesas/rcar_gen3_thermal.c634
-rw-r--r--drivers/thermal/renesas/rcar_thermal.c (renamed from drivers/thermal/rcar_thermal.c)90
-rw-r--r--drivers/thermal/renesas/rzg2l_thermal.c (renamed from drivers/thermal/rzg2l_thermal.c)19
-rw-r--r--drivers/thermal/renesas/rzg3e_thermal.c547
-rw-r--r--drivers/thermal/renesas/rzg3s_thermal.c272
-rw-r--r--drivers/thermal/rockchip_thermal.c648
-rw-r--r--drivers/thermal/samsung/exynos_tmu.c614
-rw-r--r--drivers/thermal/spear_thermal.c24
-rw-r--r--drivers/thermal/sprd_thermal.c37
-rw-r--r--drivers/thermal/st/Kconfig4
-rw-r--r--drivers/thermal/st/Makefile1
-rw-r--r--drivers/thermal/st/st_thermal.c76
-rw-r--r--drivers/thermal/st/st_thermal.h20
-rw-r--r--drivers/thermal/st/st_thermal_memmap.c18
-rw-r--r--drivers/thermal/st/st_thermal_syscfg.c174
-rw-r--r--drivers/thermal/st/stm_thermal.c38
-rw-r--r--drivers/thermal/sun8i_thermal.c236
-rw-r--r--drivers/thermal/tegra/Makefile1
-rw-r--r--drivers/thermal/tegra/soctherm-fuse.c18
-rw-r--r--drivers/thermal/tegra/soctherm.c112
-rw-r--r--drivers/thermal/tegra/soctherm.h11
-rw-r--r--drivers/thermal/tegra/tegra-bpmp-thermal.c101
-rw-r--r--drivers/thermal/tegra/tegra114-soctherm.c209
-rw-r--r--drivers/thermal/tegra/tegra124-soctherm.c4
-rw-r--r--drivers/thermal/tegra/tegra132-soctherm.c4
-rw-r--r--drivers/thermal/tegra/tegra210-soctherm.c4
-rw-r--r--drivers/thermal/tegra/tegra30-tsensor.c101
-rw-r--r--drivers/thermal/testing/Makefile7
-rw-r--r--drivers/thermal/testing/command.c211
-rw-r--r--drivers/thermal/testing/thermal_testing.h11
-rw-r--r--drivers/thermal/testing/zone.c448
-rw-r--r--drivers/thermal/thermal-generic-adc.c98
-rw-r--r--drivers/thermal/thermal_core.c1573
-rw-r--r--drivers/thermal/thermal_core.h227
-rw-r--r--drivers/thermal/thermal_debugfs.c968
-rw-r--r--drivers/thermal/thermal_debugfs.h30
-rw-r--r--drivers/thermal/thermal_helpers.c180
-rw-r--r--drivers/thermal/thermal_hwmon.c22
-rw-r--r--drivers/thermal/thermal_hwmon.h4
-rw-r--r--drivers/thermal/thermal_mmio.c23
-rw-r--r--drivers/thermal/thermal_netlink.c451
-rw-r--r--drivers/thermal/thermal_netlink.h122
-rw-r--r--drivers/thermal/thermal_of.c1304
-rw-r--r--drivers/thermal/thermal_sysfs.c417
-rw-r--r--drivers/thermal/thermal_thresholds.c244
-rw-r--r--drivers/thermal/thermal_thresholds.h19
-rw-r--r--drivers/thermal/thermal_trace.h207
-rw-r--r--drivers/thermal/thermal_trace_ipa.h104
-rw-r--r--drivers/thermal/thermal_trip.c90
-rw-r--r--drivers/thermal/ti-soc-thermal/dra752-bandgap.h4
-rw-r--r--drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h8
-rw-r--r--drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h4
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c8
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.h4
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-thermal-common.c35
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-thermal.h15
-rw-r--r--drivers/thermal/uniphier_thermal.c63
-rw-r--r--drivers/thunderbolt/Kconfig21
-rw-r--r--drivers/thunderbolt/Makefile3
-rw-r--r--drivers/thunderbolt/acpi.c104
-rw-r--r--drivers/thunderbolt/cap.c49
-rw-r--r--drivers/thunderbolt/clx.c428
-rw-r--r--drivers/thunderbolt/ctl.c150
-rw-r--r--drivers/thunderbolt/ctl.h8
-rw-r--r--drivers/thunderbolt/debugfs.c1852
-rw-r--r--drivers/thunderbolt/dma_port.c21
-rw-r--r--drivers/thunderbolt/dma_test.c20
-rw-r--r--drivers/thunderbolt/domain.c118
-rw-r--r--drivers/thunderbolt/eeprom.c271
-rw-r--r--drivers/thunderbolt/icm.c121
-rw-r--r--drivers/thunderbolt/lc.c99
-rw-r--r--drivers/thunderbolt/nhi.c234
-rw-r--r--drivers/thunderbolt/nhi.h15
-rw-r--r--drivers/thunderbolt/nhi_regs.h31
-rw-r--r--drivers/thunderbolt/nvm.c415
-rw-r--r--drivers/thunderbolt/path.c36
-rw-r--r--drivers/thunderbolt/property.c38
-rw-r--r--drivers/thunderbolt/quirks.c71
-rw-r--r--drivers/thunderbolt/retimer.c295
-rw-r--r--drivers/thunderbolt/sb_regs.h77
-rw-r--r--drivers/thunderbolt/switch.c1534
-rw-r--r--drivers/thunderbolt/tb.c2189
-rw-r--r--drivers/thunderbolt/tb.h554
-rw-r--r--drivers/thunderbolt/tb_msgs.h25
-rw-r--r--drivers/thunderbolt/tb_regs.h99
-rw-r--r--drivers/thunderbolt/test.c163
-rw-r--r--drivers/thunderbolt/tmu.c729
-rw-r--r--drivers/thunderbolt/trace.h197
-rw-r--r--drivers/thunderbolt/tunnel.c1346
-rw-r--r--drivers/thunderbolt/tunnel.h137
-rw-r--r--drivers/thunderbolt/usb4.c1528
-rw-r--r--drivers/thunderbolt/usb4_port.c68
-rw-r--r--drivers/thunderbolt/xdomain.c363
-rw-r--r--drivers/tty/Kconfig62
-rw-r--r--drivers/tty/amiserial.c71
-rw-r--r--drivers/tty/ehv_bytechan.c26
-rw-r--r--drivers/tty/goldfish.c32
-rw-r--r--drivers/tty/hvc/Kconfig8
-rw-r--r--drivers/tty/hvc/hvc_console.c27
-rw-r--r--drivers/tty/hvc/hvc_console.h10
-rw-r--r--drivers/tty/hvc/hvc_dcc.c32
-rw-r--r--drivers/tty/hvc/hvc_iucv.c53
-rw-r--r--drivers/tty/hvc/hvc_opal.c22
-rw-r--r--drivers/tty/hvc/hvc_riscv_sbi.c46
-rw-r--r--drivers/tty/hvc/hvc_rtas.c13
-rw-r--r--drivers/tty/hvc/hvc_udbg.c9
-rw-r--r--drivers/tty/hvc/hvc_vio.c18
-rw-r--r--drivers/tty/hvc/hvc_xen.c131
-rw-r--r--drivers/tty/hvc/hvcs.c108
-rw-r--r--drivers/tty/hvc/hvsi.c15
-rw-r--r--drivers/tty/hvc/hvsi_lib.c22
-rw-r--r--drivers/tty/ipwireless/hardware.c8
-rw-r--r--drivers/tty/ipwireless/main.h3
-rw-r--r--drivers/tty/ipwireless/tty.c4
-rw-r--r--drivers/tty/mips_ejtag_fdc.c29
-rw-r--r--drivers/tty/moxa.c530
-rw-r--r--drivers/tty/mxser.c307
-rw-r--r--drivers/tty/n_gsm.c1377
-rw-r--r--drivers/tty/n_hdlc.c130
-rw-r--r--drivers/tty/n_null.c25
-rw-r--r--drivers/tty/n_tty.c1083
-rw-r--r--drivers/tty/nozomi.c33
-rw-r--r--drivers/tty/pty.c162
-rw-r--r--drivers/tty/rpmsg_tty.c5
-rw-r--r--drivers/tty/serdev/core.c108
-rw-r--r--drivers/tty/serdev/serdev-ttyport.c44
-rw-r--r--drivers/tty/serial/21285.c48
-rw-r--r--drivers/tty/serial/8250/8250.h81
-rw-r--r--drivers/tty/serial/8250/8250_alpha.c21
-rw-r--r--drivers/tty/serial/8250/8250_aspeed_vuart.c94
-rw-r--r--drivers/tty/serial/8250/8250_bcm2835aux.c151
-rw-r--r--drivers/tty/serial/8250/8250_bcm7271.c272
-rw-r--r--drivers/tty/serial/8250/8250_ce4100.c93
-rw-r--r--drivers/tty/serial/8250/8250_core.c917
-rw-r--r--drivers/tty/serial/8250/8250_dfl.c167
-rw-r--r--drivers/tty/serial/8250/8250_dma.c95
-rw-r--r--drivers/tty/serial/8250/8250_dw.c347
-rw-r--r--drivers/tty/serial/8250/8250_dwlib.c30
-rw-r--r--drivers/tty/serial/8250/8250_dwlib.h35
-rw-r--r--drivers/tty/serial/8250/8250_early.c42
-rw-r--r--drivers/tty/serial/8250/8250_em.c126
-rw-r--r--drivers/tty/serial/8250/8250_exar.c1079
-rw-r--r--drivers/tty/serial/8250/8250_fintek.c18
-rw-r--r--drivers/tty/serial/8250/8250_fsl.c37
-rw-r--r--drivers/tty/serial/8250/8250_gsc.c130
-rw-r--r--drivers/tty/serial/8250/8250_ingenic.c64
-rw-r--r--drivers/tty/serial/8250/8250_ioc3.c9
-rw-r--r--drivers/tty/serial/8250/8250_keba.c280
-rw-r--r--drivers/tty/serial/8250/8250_loongson.c238
-rw-r--r--drivers/tty/serial/8250/8250_lpc18xx.c26
-rw-r--r--drivers/tty/serial/8250/8250_lpss.c26
-rw-r--r--drivers/tty/serial/8250/8250_men_mcb.c215
-rw-r--r--drivers/tty/serial/8250/8250_mid.c23
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c93
-rw-r--r--drivers/tty/serial/8250/8250_ni.c450
-rw-r--r--drivers/tty/serial/8250/8250_of.c225
-rw-r--r--drivers/tty/serial/8250/8250_omap.c718
-rw-r--r--drivers/tty/serial/8250/8250_parisc.c131
-rw-r--r--drivers/tty/serial/8250/8250_pci.c922
-rw-r--r--drivers/tty/serial/8250/8250_pci1xxxx.c887
-rw-r--r--drivers/tty/serial/8250/8250_pcilib.c49
-rw-r--r--drivers/tty/serial/8250/8250_pcilib.h17
-rw-r--r--drivers/tty/serial/8250/8250_platform.c386
-rw-r--r--drivers/tty/serial/8250/8250_pnp.c55
-rw-r--r--drivers/tty/serial/8250/8250_port.c1478
-rw-r--r--drivers/tty/serial/8250/8250_pxa.c45
-rw-r--r--drivers/tty/serial/8250/8250_rsa.c211
-rw-r--r--drivers/tty/serial/8250/8250_rt288x.c136
-rw-r--r--drivers/tty/serial/8250/8250_tegra.c35
-rw-r--r--drivers/tty/serial/8250/8250_uniphier.c29
-rw-r--r--drivers/tty/serial/8250/Kconfig112
-rw-r--r--drivers/tty/serial/8250/Makefile56
-rw-r--r--drivers/tty/serial/8250/serial_cs.c7
-rw-r--r--drivers/tty/serial/Kconfig233
-rw-r--r--drivers/tty/serial/Makefile139
-rw-r--r--drivers/tty/serial/altera_jtaguart.c192
-rw-r--r--drivers/tty/serial/altera_uart.c89
-rw-r--r--drivers/tty/serial/amba-pl010.c59
-rw-r--r--drivers/tty/serial/amba-pl011.c896
-rw-r--r--drivers/tty/serial/apbuart.c50
-rw-r--r--drivers/tty/serial/ar933x_uart.c133
-rw-r--r--drivers/tty/serial/arc_uart.c70
-rw-r--r--drivers/tty/serial/atmel_serial.c421
-rw-r--r--drivers/tty/serial/atmel_serial.h75
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c146
-rw-r--r--drivers/tty/serial/clps711x.c27
-rw-r--r--drivers/tty/serial/cpm_uart.c1604
-rw-r--r--drivers/tty/serial/cpm_uart.h114
-rw-r--r--drivers/tty/serial/cpm_uart/Makefile12
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart.h145
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c1503
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c122
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h33
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c156
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h33
-rw-r--r--drivers/tty/serial/digicolor-usart.c42
-rw-r--r--drivers/tty/serial/dz.c59
-rw-r--r--drivers/tty/serial/earlycon-arm-semihost.c51
-rw-r--r--drivers/tty/serial/earlycon-riscv-sbi.c27
-rw-r--r--drivers/tty/serial/earlycon-semihost.c28
-rw-r--r--drivers/tty/serial/earlycon.c42
-rw-r--r--drivers/tty/serial/esp32_acm.c459
-rw-r--r--drivers/tty/serial/esp32_uart.c779
-rw-r--r--drivers/tty/serial/fsl_linflexuart.c65
-rw-r--r--drivers/tty/serial/fsl_lpuart.c1351
-rw-r--r--drivers/tty/serial/icom.c74
-rw-r--r--drivers/tty/serial/imx.c997
-rw-r--r--drivers/tty/serial/ip22zilog.c412
-rw-r--r--drivers/tty/serial/jsm/jsm.h5
-rw-r--r--drivers/tty/serial/jsm/jsm_cls.c66
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c4
-rw-r--r--drivers/tty/serial/jsm/jsm_neo.c82
-rw-r--r--drivers/tty/serial/jsm/jsm_tty.c21
-rw-r--r--drivers/tty/serial/kgdb_nmi.c380
-rw-r--r--drivers/tty/serial/kgdboc.c75
-rw-r--r--drivers/tty/serial/lantiq.c71
-rw-r--r--drivers/tty/serial/liteuart.c259
-rw-r--r--drivers/tty/serial/lpc32xx_hs.c82
-rw-r--r--drivers/tty/serial/ma35d1_serial.c831
-rw-r--r--drivers/tty/serial/max3100.c352
-rw-r--r--drivers/tty/serial/max310x.c479
-rw-r--r--drivers/tty/serial/mcf.c91
-rw-r--r--drivers/tty/serial/men_z135_uart.c45
-rw-r--r--drivers/tty/serial/meson_uart.c248
-rw-r--r--drivers/tty/serial/milbeaut_usio.c47
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c83
-rw-r--r--drivers/tty/serial/mps2-uart.c48
-rw-r--r--drivers/tty/serial/msm_serial.c210
-rw-r--r--drivers/tty/serial/mux.c51
-rw-r--r--drivers/tty/serial/mvebu-uart.c88
-rw-r--r--drivers/tty/serial/mxs-auart.c91
-rw-r--r--drivers/tty/serial/omap-serial.c171
-rw-r--r--drivers/tty/serial/owl-uart.c85
-rw-r--r--drivers/tty/serial/pch_uart.c117
-rw-r--r--drivers/tty/serial/pic32_uart.c100
-rw-r--r--drivers/tty/serial/pmac_zilog.c117
-rw-r--r--drivers/tty/serial/pxa.c76
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c1424
-rw-r--r--drivers/tty/serial/rda-uart.c76
-rw-r--r--drivers/tty/serial/rp2.c75
-rw-r--r--drivers/tty/serial/rsci.c480
-rw-r--r--drivers/tty/serial/rsci.h10
-rw-r--r--drivers/tty/serial/sa1100.c69
-rw-r--r--drivers/tty/serial/samsung_tty.c801
-rw-r--r--drivers/tty/serial/sb1250-duart.c33
-rw-r--r--drivers/tty/serial/sc16is7xx.c1499
-rw-r--r--drivers/tty/serial/sc16is7xx.h40
-rw-r--r--drivers/tty/serial/sc16is7xx_i2c.c67
-rw-r--r--drivers/tty/serial/sc16is7xx_spi.c90
-rw-r--r--drivers/tty/serial/sccnxp.c54
-rw-r--r--drivers/tty/serial/serial-tegra.c141
-rw-r--r--drivers/tty/serial/serial_base.h67
-rw-r--r--drivers/tty/serial/serial_base_bus.c288
-rw-r--r--drivers/tty/serial/serial_core.c1597
-rw-r--r--drivers/tty/serial/serial_ctrl.c68
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.c67
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.h27
-rw-r--r--drivers/tty/serial/serial_port.c321
-rw-r--r--drivers/tty/serial/serial_txx9.c69
-rw-r--r--drivers/tty/serial/sh-sci-common.h175
-rw-r--r--drivers/tty/serial/sh-sci.c1602
-rw-r--r--drivers/tty/serial/sh-sci.h177
-rw-r--r--drivers/tty/serial/sifive.c183
-rw-r--r--drivers/tty/serial/sprd_serial.c186
-rw-r--r--drivers/tty/serial/st-asc.c137
-rw-r--r--drivers/tty/serial/stm32-usart.c847
-rw-r--r--drivers/tty/serial/stm32-usart.h40
-rw-r--r--drivers/tty/serial/sunhv.c87
-rw-r--r--drivers/tty/serial/sunplus-uart.c60
-rw-r--r--drivers/tty/serial/sunsab.c119
-rw-r--r--drivers/tty/serial/sunsu.c293
-rw-r--r--drivers/tty/serial/sunzilog.c91
-rw-r--r--drivers/tty/serial/tegra-tcu.c19
-rw-r--r--drivers/tty/serial/tegra-utc.c625
-rw-r--r--drivers/tty/serial/timbuart.c34
-rw-r--r--drivers/tty/serial/uartlite.c93
-rw-r--r--drivers/tty/serial/ucc_uart.c86
-rw-r--r--drivers/tty/serial/vt8500_serial.c61
-rw-r--r--drivers/tty/serial/xilinx_uartps.c432
-rw-r--r--drivers/tty/serial/zs.c19
-rw-r--r--drivers/tty/synclink_gt.c189
-rw-r--r--drivers/tty/sysrq.c193
-rw-r--r--drivers/tty/tty.h32
-rw-r--r--drivers/tty/tty_audit.c53
-rw-r--r--drivers/tty/tty_baudrate.c26
-rw-r--r--drivers/tty/tty_buffer.c227
-rw-r--r--drivers/tty/tty_io.c486
-rw-r--r--drivers/tty/tty_ioctl.c433
-rw-r--r--drivers/tty/tty_jobctrl.c17
-rw-r--r--drivers/tty/tty_ldisc.c47
-rw-r--r--drivers/tty/tty_ldsem.c17
-rw-r--r--drivers/tty/tty_mutex.c6
-rw-r--r--drivers/tty/tty_port.c263
-rw-r--r--drivers/tty/ttynull.c5
-rw-r--r--drivers/tty/vcc.c45
-rw-r--r--drivers/tty/vt/.gitignore3
-rw-r--r--drivers/tty/vt/Makefile38
-rw-r--r--drivers/tty/vt/conmakehash.c9
-rw-r--r--drivers/tty/vt/consolemap.c120
-rw-r--r--drivers/tty/vt/defkeymap.c_shipped112
-rwxr-xr-xdrivers/tty/vt/gen_ucs_fallback_table.py360
-rwxr-xr-xdrivers/tty/vt/gen_ucs_recompose_table.py257
-rwxr-xr-xdrivers/tty/vt/gen_ucs_width_table.py307
-rw-r--r--drivers/tty/vt/keyboard.c372
-rw-r--r--drivers/tty/vt/selection.c114
-rw-r--r--drivers/tty/vt/ucs.c251
-rw-r--r--drivers/tty/vt/ucs_fallback_table.h_shipped3346
-rw-r--r--drivers/tty/vt/ucs_recompose_table.h_shipped102
-rw-r--r--drivers/tty/vt/ucs_width_table.h_shipped453
-rw-r--r--drivers/tty/vt/vc_screen.c131
-rw-r--r--drivers/tty/vt/vt.c2545
-rw-r--r--drivers/tty/vt/vt_ioctl.c216
-rw-r--r--drivers/ufs/core/Kconfig8
-rw-r--r--drivers/ufs/core/Makefile4
-rw-r--r--drivers/ufs/core/ufs-fault-injection.c22
-rw-r--r--drivers/ufs/core/ufs-fault-injection.h13
-rw-r--r--drivers/ufs/core/ufs-hwmon.c5
-rw-r--r--drivers/ufs/core/ufs-mcq.c709
-rw-r--r--drivers/ufs/core/ufs-rpmb.c254
-rw-r--r--drivers/ufs/core/ufs-sysfs.c935
-rw-r--r--drivers/ufs/core/ufs_bsg.c156
-rw-r--r--drivers/ufs/core/ufs_trace.h408
-rw-r--r--drivers/ufs/core/ufs_trace_types.h23
-rw-r--r--drivers/ufs/core/ufshcd-crypto.c65
-rw-r--r--drivers/ufs/core/ufshcd-crypto.h60
-rw-r--r--drivers/ufs/core/ufshcd-priv.h202
-rw-r--r--drivers/ufs/core/ufshcd.c5149
-rw-r--r--drivers/ufs/core/ufshpb.c2666
-rw-r--r--drivers/ufs/core/ufshpb.h318
-rw-r--r--drivers/ufs/host/Kconfig49
-rw-r--r--drivers/ufs/host/Makefile7
-rw-r--r--drivers/ufs/host/cdns-pltfrm.c39
-rw-r--r--drivers/ufs/host/tc-dwc-g210-pci.c20
-rw-r--r--drivers/ufs/host/tc-dwc-g210-pltfrm.c10
-rw-r--r--drivers/ufs/host/tc-dwc-g210.c32
-rw-r--r--drivers/ufs/host/ti-j721e-ufs.c41
-rw-r--r--drivers/ufs/host/ufs-amd-versal2.c564
-rw-r--r--drivers/ufs/host/ufs-exynos.c658
-rw-r--r--drivers/ufs/host/ufs-exynos.h34
-rw-r--r--drivers/ufs/host/ufs-hisi.c53
-rw-r--r--drivers/ufs/host/ufs-mediatek-sip.h94
-rw-r--r--drivers/ufs/host/ufs-mediatek-trace.h27
-rw-r--r--drivers/ufs/host/ufs-mediatek.c1362
-rw-r--r--drivers/ufs/host/ufs-mediatek.h165
-rw-r--r--drivers/ufs/host/ufs-qcom-ice.c245
-rw-r--r--drivers/ufs/host/ufs-qcom.c1916
-rw-r--r--drivers/ufs/host/ufs-qcom.h296
-rw-r--r--drivers/ufs/host/ufs-renesas.c744
-rw-r--r--drivers/ufs/host/ufs-rockchip.c362
-rw-r--r--drivers/ufs/host/ufs-rockchip.h90
-rw-r--r--drivers/ufs/host/ufs-sprd.c453
-rw-r--r--drivers/ufs/host/ufs-sprd.h85
-rw-r--r--drivers/ufs/host/ufshcd-dwc.c22
-rw-r--r--drivers/ufs/host/ufshcd-dwc.h46
-rw-r--r--drivers/ufs/host/ufshcd-pci.c177
-rw-r--r--drivers/ufs/host/ufshcd-pltfrm.c318
-rw-r--r--drivers/ufs/host/ufshcd-pltfrm.h15
-rw-r--r--drivers/uio/Kconfig32
-rw-r--r--drivers/uio/Makefile2
-rw-r--r--drivers/uio/uio.c87
-rw-r--r--drivers/uio/uio_aec.c3
-rw-r--r--drivers/uio/uio_cif.c3
-rw-r--r--drivers/uio/uio_dfl.c4
-rw-r--r--drivers/uio/uio_dmem_genirq.c74
-rw-r--r--drivers/uio/uio_fsl_elbc_gpcm.c17
-rw-r--r--drivers/uio/uio_hv_generic.c204
-rw-r--r--drivers/uio/uio_mf624.c3
-rw-r--r--drivers/uio/uio_netx.c3
-rw-r--r--drivers/uio/uio_pci_generic_sva.c192
-rw-r--r--drivers/uio/uio_pdrv_genirq.c39
-rw-r--r--drivers/uio/uio_pruss.c248
-rw-r--r--drivers/uio/uio_sercos3.c2
-rw-r--r--drivers/usb/Kconfig43
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/atm/cxacru.c129
-rw-r--r--drivers/usb/atm/speedtch.c18
-rw-r--r--drivers/usb/atm/ueagle-atm.c27
-rw-r--r--drivers/usb/atm/usbatm.c13
-rw-r--r--drivers/usb/c67x00/c67x00-drv.c6
-rw-r--r--drivers/usb/c67x00/c67x00-hcd.h1
-rw-r--r--drivers/usb/cdns3/Kconfig11
-rw-r--r--drivers/usb/cdns3/Makefile1
-rw-r--r--drivers/usb/cdns3/cdns3-debug.h8
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c207
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.h3
-rw-r--r--drivers/usb/cdns3/cdns3-imx.c28
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c15
-rw-r--r--drivers/usb/cdns3/cdns3-plat.c27
-rw-r--r--drivers/usb/cdns3/cdns3-starfive.c244
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c123
-rw-r--r--drivers/usb/cdns3/cdns3-trace.h111
-rw-r--r--drivers/usb/cdns3/cdnsp-debug.h358
-rw-r--r--drivers/usb/cdns3/cdnsp-ep0.c40
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c99
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.h28
-rw-r--r--drivers/usb/cdns3/cdnsp-pci.c64
-rw-r--r--drivers/usb/cdns3/cdnsp-ring.c240
-rw-r--r--drivers/usb/cdns3/cdnsp-trace.h47
-rw-r--r--drivers/usb/cdns3/core.c26
-rw-r--r--drivers/usb/cdns3/core.h12
-rw-r--r--drivers/usb/cdns3/drd.c27
-rw-r--r--drivers/usb/cdns3/drd.h9
-rw-r--r--drivers/usb/cdns3/host.c85
-rw-r--r--drivers/usb/chipidea/Kconfig14
-rw-r--r--drivers/usb/chipidea/Makefile3
-rw-r--r--drivers/usb/chipidea/ci.h29
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c250
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h6
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c5
-rw-r--r--drivers/usb/chipidea/ci_hdrc_npcm.c112
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c32
-rw-r--r--drivers/usb/chipidea/ci_hdrc_usb2.c20
-rw-r--r--drivers/usb/chipidea/core.c194
-rw-r--r--drivers/usb/chipidea/debug.c57
-rw-r--r--drivers/usb/chipidea/host.c85
-rw-r--r--drivers/usb/chipidea/otg.c12
-rw-r--r--drivers/usb/chipidea/otg.h1
-rw-r--r--drivers/usb/chipidea/otg_fsm.c13
-rw-r--r--drivers/usb/chipidea/trace.h4
-rw-r--r--drivers/usb/chipidea/udc.c243
-rw-r--r--drivers/usb/chipidea/udc.h2
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c458
-rw-r--r--drivers/usb/class/cdc-acm.c99
-rw-r--r--drivers/usb/class/cdc-wdm.c55
-rw-r--r--drivers/usb/class/usblp.c47
-rw-r--r--drivers/usb/class/usbtmc.c97
-rw-r--r--drivers/usb/common/common.c41
-rw-r--r--drivers/usb/common/debug.c96
-rw-r--r--drivers/usb/common/led.c4
-rw-r--r--drivers/usb/common/ulpi.c44
-rw-r--r--drivers/usb/common/usb-conn-gpio.c46
-rw-r--r--drivers/usb/common/usb-otg-fsm.c1
-rw-r--r--drivers/usb/core/Kconfig27
-rw-r--r--drivers/usb/core/Makefile10
-rw-r--r--drivers/usb/core/buffer.c49
-rw-r--r--drivers/usb/core/config.c196
-rw-r--r--drivers/usb/core/devices.c1
-rw-r--r--drivers/usb/core/devio.c58
-rw-r--r--drivers/usb/core/driver.c189
-rw-r--r--drivers/usb/core/endpoint.c13
-rw-r--r--drivers/usb/core/file.c70
-rw-r--r--drivers/usb/core/generic.c30
-rw-r--r--drivers/usb/core/hcd-pci.c65
-rw-r--r--drivers/usb/core/hcd.c185
-rw-r--r--drivers/usb/core/hub.c993
-rw-r--r--drivers/usb/core/hub.h18
-rw-r--r--drivers/usb/core/ledtrig-usbport.c16
-rw-r--r--drivers/usb/core/message.c88
-rw-r--r--drivers/usb/core/of.c75
-rw-r--r--drivers/usb/core/offload.c136
-rw-r--r--drivers/usb/core/phy.c120
-rw-r--r--drivers/usb/core/phy.h3
-rw-r--r--drivers/usb/core/port.c158
-rw-r--r--drivers/usb/core/quirks.c66
-rw-r--r--drivers/usb/core/sysfs.c187
-rw-r--r--drivers/usb/core/trace.c6
-rw-r--r--drivers/usb/core/trace.h61
-rw-r--r--drivers/usb/core/urb.c76
-rw-r--r--drivers/usb/core/usb-acpi.c163
-rw-r--r--drivers/usb/core/usb.c258
-rw-r--r--drivers/usb/core/usb.h27
-rw-r--r--drivers/usb/dwc2/Kconfig2
-rw-r--r--drivers/usb/dwc2/core.c73
-rw-r--r--drivers/usb/dwc2/core.h78
-rw-r--r--drivers/usb/dwc2/core_intr.c128
-rw-r--r--drivers/usb/dwc2/debugfs.c2
-rw-r--r--drivers/usb/dwc2/drd.c12
-rw-r--r--drivers/usb/dwc2/gadget.c186
-rw-r--r--drivers/usb/dwc2/hcd.c211
-rw-r--r--drivers/usb/dwc2/hcd.h31
-rw-r--r--drivers/usb/dwc2/hcd_ddma.c49
-rw-r--r--drivers/usb/dwc2/hcd_intr.c49
-rw-r--r--drivers/usb/dwc2/hcd_queue.c93
-rw-r--r--drivers/usb/dwc2/hw.h46
-rw-r--r--drivers/usb/dwc2/params.c205
-rw-r--r--drivers/usb/dwc2/pci.c44
-rw-r--r--drivers/usb/dwc2/platform.c185
-rw-r--r--drivers/usb/dwc3/Kconfig53
-rw-r--r--drivers/usb/dwc3/Makefile5
-rw-r--r--drivers/usb/dwc3/core.c1536
-rw-r--r--drivers/usb/dwc3/core.h156
-rw-r--r--drivers/usb/dwc3/debug.h27
-rw-r--r--drivers/usb/dwc3/debugfs.c145
-rw-r--r--drivers/usb/dwc3/drd.c23
-rw-r--r--drivers/usb/dwc3/dwc3-am62.c228
-rw-r--r--drivers/usb/dwc3/dwc3-apple.c489
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c73
-rw-r--r--drivers/usb/dwc3/dwc3-generic-plat.c233
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c140
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c7
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c34
-rw-r--r--drivers/usb/dwc3/dwc3-octeon.c534
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c9
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c23
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c207
-rw-r--r--drivers/usb/dwc3/dwc3-qcom-legacy.c935
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c881
-rw-r--r--drivers/usb/dwc3/dwc3-rtk.c459
-rw-r--r--drivers/usb/dwc3/dwc3-st.c60
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c174
-rw-r--r--drivers/usb/dwc3/ep0.c68
-rw-r--r--drivers/usb/dwc3/gadget.c1068
-rw-r--r--drivers/usb/dwc3/gadget.h1
-rw-r--r--drivers/usb/dwc3/glue.h193
-rw-r--r--drivers/usb/dwc3/host.c113
-rw-r--r--drivers/usb/dwc3/trace.h34
-rw-r--r--drivers/usb/early/xhci-dbc.c14
-rw-r--r--drivers/usb/fotg210/Kconfig38
-rw-r--r--drivers/usb/fotg210/Makefile6
-rw-r--r--drivers/usb/fotg210/fotg210-core.c220
-rw-r--r--drivers/usb/fotg210/fotg210-hcd.c (renamed from drivers/usb/host/fotg210-hcd.c)139
-rw-r--r--drivers/usb/fotg210/fotg210-hcd.h689
-rw-r--r--drivers/usb/fotg210/fotg210-udc.c (renamed from drivers/usb/gadget/udc/fotg210-udc.c)266
-rw-r--r--drivers/usb/fotg210/fotg210-udc.h254
-rw-r--r--drivers/usb/fotg210/fotg210.h61
-rw-r--r--drivers/usb/gadget/Kconfig20
-rw-r--r--drivers/usb/gadget/composite.c339
-rw-r--r--drivers/usb/gadget/config.c61
-rw-r--r--drivers/usb/gadget/configfs.c548
-rw-r--r--drivers/usb/gadget/epautoconf.c2
-rw-r--r--drivers/usb/gadget/function/Makefile6
-rw-r--r--drivers/usb/gadget/function/f_acm.c99
-rw-r--r--drivers/usb/gadget/function/f_ecm.c123
-rw-r--r--drivers/usb/gadget/function/f_eem.c12
-rw-r--r--drivers/usb/gadget/function/f_fs.c913
-rw-r--r--drivers/usb/gadget/function/f_hid.c512
-rw-r--r--drivers/usb/gadget/function/f_loopback.c7
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c62
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h2
-rw-r--r--drivers/usb/gadget/function/f_midi.c94
-rw-r--r--drivers/usb/gadget/function/f_midi2.c2893
-rw-r--r--drivers/usb/gadget/function/f_ncm.c293
-rw-r--r--drivers/usb/gadget/function/f_obex.c4
-rw-r--r--drivers/usb/gadget/function/f_phonet.c1
-rw-r--r--drivers/usb/gadget/function/f_printer.c99
-rw-r--r--drivers/usb/gadget/function/f_rndis.c105
-rw-r--r--drivers/usb/gadget/function/f_serial.c12
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c7
-rw-r--r--drivers/usb/gadget/function/f_subset.c5
-rw-r--r--drivers/usb/gadget/function/f_tcm.c766
-rw-r--r--drivers/usb/gadget/function/f_uac1.c147
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c1
-rw-r--r--drivers/usb/gadget/function/f_uac2.c112
-rw-r--r--drivers/usb/gadget/function/f_uvc.c272
-rw-r--r--drivers/usb/gadget/function/f_uvc.h2
-rw-r--r--drivers/usb/gadget/function/rndis.c8
-rw-r--r--drivers/usb/gadget/function/storage_common.c10
-rw-r--r--drivers/usb/gadget/function/storage_common.h4
-rw-r--r--drivers/usb/gadget/function/tcm.h28
-rw-r--r--drivers/usb/gadget/function/u_audio.c86
-rw-r--r--drivers/usb/gadget/function/u_ether.c122
-rw-r--r--drivers/usb/gadget/function/u_ether.h17
-rw-r--r--drivers/usb/gadget/function/u_fs.h2
-rw-r--r--drivers/usb/gadget/function/u_hid.h2
-rw-r--r--drivers/usb/gadget/function/u_midi2.h81
-rw-r--r--drivers/usb/gadget/function/u_ncm.h2
-rw-r--r--drivers/usb/gadget/function/u_phonet.h1
-rw-r--r--drivers/usb/gadget/function/u_serial.c138
-rw-r--r--drivers/usb/gadget/function/u_serial.h8
-rw-r--r--drivers/usb/gadget/function/u_uac1.h12
-rw-r--r--drivers/usb/gadget/function/u_uac1_legacy.c4
-rw-r--r--drivers/usb/gadget/function/u_uac2.h23
-rw-r--r--drivers/usb/gadget/function/u_uvc.h24
-rw-r--r--drivers/usb/gadget/function/uvc.h41
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c1613
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.h72
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c36
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h2
-rw-r--r--drivers/usb/gadget/function/uvc_trace.c11
-rw-r--r--drivers/usb/gadget/function/uvc_trace.h60
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c438
-rw-r--r--drivers/usb/gadget/function/uvc_video.c545
-rw-r--r--drivers/usb/gadget/function/uvc_video.h3
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c1
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c15
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c1
-rw-r--r--drivers/usb/gadget/legacy/hid.c11
-rw-r--r--drivers/usb/gadget/legacy/inode.c113
-rw-r--r--drivers/usb/gadget/legacy/raw_gadget.c109
-rw-r--r--drivers/usb/gadget/legacy/serial.c3
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c2
-rw-r--r--drivers/usb/gadget/legacy/webcam.c330
-rw-r--r--drivers/usb/gadget/legacy/zero.c32
-rw-r--r--drivers/usb/gadget/u_f.c2
-rw-r--r--drivers/usb/gadget/u_f.h86
-rw-r--r--drivers/usb/gadget/u_os_desc.h2
-rw-r--r--drivers/usb/gadget/udc/Kconfig109
-rw-r--r--drivers/usb/gadget/udc/Makefile11
-rw-r--r--drivers/usb/gadget/udc/amd5536udc_pci.c3
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c12
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/dev.c5
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/ep0.c1
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/epn.c17
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/hub.c4
-rw-r--r--drivers/usb/gadget/udc/aspeed_udc.c29
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c34
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c14
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c18
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc.h2
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c4
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c2
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_udc.c3
-rw-r--r--drivers/usb/gadget/udc/cdns2/Kconfig11
-rw-r--r--drivers/usb/gadget/udc/cdns2/Makefile7
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-debug.h203
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-ep0.c659
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.c2468
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.h716
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-pci.c136
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-trace.c11
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-trace.h536
-rw-r--r--drivers/usb/gadget/udc/core.c333
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c82
-rw-r--r--drivers/usb/gadget/udc/fotg210.h249
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c23
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c155
-rw-r--r--drivers/usb/gadget/udc/fsl_usb2_udc.h47
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c1514
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.h675
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c3
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c27
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c85
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c17
-rw-r--r--drivers/usb/gadget/udc/max3420_udc.c9
-rw-r--r--drivers/usb/gadget/udc/mv_u3d.h317
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c2065
-rw-r--r--drivers/usb/gadget/udc/mv_udc.h309
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2425
-rw-r--r--drivers/usb/gadget/udc/net2272.c2726
-rw-r--r--drivers/usb/gadget/udc/net2272.h584
-rw-r--r--drivers/usb/gadget/udc/net2280.c11
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c57
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c4
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c91
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c25
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c11
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c241
-rw-r--r--drivers/usb/gadget/udc/renesas_usbf.c3393
-rw-r--r--drivers/usb/gadget/udc/rzv2m_usb3drd.c137
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c1319
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c1994
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.h96
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc_regs.h146
-rw-r--r--drivers/usb/gadget/udc/snps_udc_core.c9
-rw-r--r--drivers/usb/gadget/udc/snps_udc_plat.c16
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c137
-rw-r--r--drivers/usb/gadget/udc/trace.h14
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c69
-rw-r--r--drivers/usb/gadget/usbstring.c2
-rw-r--r--drivers/usb/host/Kconfig121
-rw-r--r--drivers/usb/host/Makefile14
-rw-r--r--drivers/usb/host/bcma-hcd.c11
-rw-r--r--drivers/usb/host/ehci-atmel.c14
-rw-r--r--drivers/usb/host/ehci-brcm.c8
-rw-r--r--drivers/usb/host/ehci-dbg.c10
-rw-r--r--drivers/usb/host/ehci-exynos.c83
-rw-r--r--drivers/usb/host/ehci-fsl.c40
-rw-r--r--drivers/usb/host/ehci-grlib.c6
-rw-r--r--drivers/usb/host/ehci-hcd.c16
-rw-r--r--drivers/usb/host/ehci-hub.c10
-rw-r--r--drivers/usb/host/ehci-mv.c7
-rw-r--r--drivers/usb/host/ehci-npcm7xx.c62
-rw-r--r--drivers/usb/host/ehci-omap.c9
-rw-r--r--drivers/usb/host/ehci-orion.c35
-rw-r--r--drivers/usb/host/ehci-pci.c14
-rw-r--r--drivers/usb/host/ehci-platform.c56
-rw-r--r--drivers/usb/host/ehci-ppc-of.c12
-rw-r--r--drivers/usb/host/ehci-q.c24
-rw-r--r--drivers/usb/host/ehci-sched.c3
-rw-r--r--drivers/usb/host/ehci-sh.c20
-rw-r--r--drivers/usb/host/ehci-spear.c18
-rw-r--r--drivers/usb/host/ehci-st.c20
-rw-r--r--drivers/usb/host/ehci-sysfs.c18
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c4
-rw-r--r--drivers/usb/host/ehci.h20
-rw-r--r--drivers/usb/host/fhci-hcd.c77
-rw-r--r--drivers/usb/host/fhci-hub.c15
-rw-r--r--drivers/usb/host/fhci-sched.c4
-rw-r--r--drivers/usb/host/fhci.h4
-rw-r--r--drivers/usb/host/fotg210.h688
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c27
-rw-r--r--drivers/usb/host/isp116x-hcd.c7
-rw-r--r--drivers/usb/host/isp1362-hcd.c13
-rw-r--r--drivers/usb/host/max3421-hcd.c60
-rw-r--r--drivers/usb/host/octeon-hcd.c10
-rw-r--r--drivers/usb/host/ohci-at91.c25
-rw-r--r--drivers/usb/host/ohci-da8xx.c28
-rw-r--r--drivers/usb/host/ohci-exynos.c67
-rw-r--r--drivers/usb/host/ohci-hcd.c34
-rw-r--r--drivers/usb/host/ohci-hub.c2
-rw-r--r--drivers/usb/host/ohci-nxp.c27
-rw-r--r--drivers/usb/host/ohci-omap.c21
-rw-r--r--drivers/usb/host/ohci-pci.c35
-rw-r--r--drivers/usb/host/ohci-platform.c65
-rw-r--r--drivers/usb/host/ohci-ppc-of.c13
-rw-r--r--drivers/usb/host/ohci-pxa27x.c27
-rw-r--r--drivers/usb/host/ohci-s3c2410.c14
-rw-r--r--drivers/usb/host/ohci-sa1111.c5
-rw-r--r--drivers/usb/host/ohci-sm501.c7
-rw-r--r--drivers/usb/host/ohci-spear.c12
-rw-r--r--drivers/usb/host/ohci-st.c22
-rw-r--r--drivers/usb/host/ohci-tmio.c364
-rw-r--r--drivers/usb/host/ohci.h2
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c53
-rw-r--r--drivers/usb/host/pci-quirks.c157
-rw-r--r--drivers/usb/host/pci-quirks.h34
-rw-r--r--drivers/usb/host/r8a66597-hcd.c22
-rw-r--r--drivers/usb/host/sl811-hcd.c17
-rw-r--r--drivers/usb/host/u132-hcd.c3220
-rw-r--r--drivers/usb/host/uhci-grlib.c7
-rw-r--r--drivers/usb/host/uhci-hcd.c12
-rw-r--r--drivers/usb/host/uhci-hcd.h25
-rw-r--r--drivers/usb/host/uhci-pci.c19
-rw-r--r--drivers/usb/host/uhci-platform.c37
-rw-r--r--drivers/usb/host/uhci-q.c4
-rw-r--r--drivers/usb/host/xen-hcd.c14
-rw-r--r--drivers/usb/host/xhci-caps.h120
-rw-r--r--drivers/usb/host/xhci-dbgcap.c606
-rw-r--r--drivers/usb/host/xhci-dbgcap.h17
-rw-r--r--drivers/usb/host/xhci-dbgtty.c204
-rw-r--r--drivers/usb/host/xhci-debugfs.c197
-rw-r--r--drivers/usb/host/xhci-ext-caps.h32
-rw-r--r--drivers/usb/host/xhci-histb.c20
-rw-r--r--drivers/usb/host/xhci-hub.c499
-rw-r--r--drivers/usb/host/xhci-mem.c1243
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c425
-rw-r--r--drivers/usb/host/xhci-mtk.c130
-rw-r--r--drivers/usb/host/xhci-mtk.h33
-rw-r--r--drivers/usb/host/xhci-mvebu.c12
-rw-r--r--drivers/usb/host/xhci-mvebu.h6
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c56
-rw-r--r--drivers/usb/host/xhci-pci.c556
-rw-r--r--drivers/usb/host/xhci-pci.h19
-rw-r--r--drivers/usb/host/xhci-plat.c337
-rw-r--r--drivers/usb/host/xhci-plat.h14
-rw-r--r--drivers/usb/host/xhci-port.h181
-rw-r--r--drivers/usb/host/xhci-rcar-regs.h49
-rw-r--r--drivers/usb/host/xhci-rcar.c231
-rw-r--r--drivers/usb/host/xhci-rcar.h55
-rw-r--r--drivers/usb/host/xhci-ring.c1857
-rw-r--r--drivers/usb/host/xhci-rzg3e-regs.h12
-rw-r--r--drivers/usb/host/xhci-rzv2m.c39
-rw-r--r--drivers/usb/host/xhci-rzv2m.h16
-rw-r--r--drivers/usb/host/xhci-sideband.c507
-rw-r--r--drivers/usb/host/xhci-tegra.c566
-rw-r--r--drivers/usb/host/xhci-trace.c1
-rw-r--r--drivers/usb/host/xhci-trace.h189
-rw-r--r--drivers/usb/host/xhci.c1125
-rw-r--r--drivers/usb/host/xhci.h773
-rw-r--r--drivers/usb/image/mdc800.c1
-rw-r--r--drivers/usb/image/microtek.c14
-rw-r--r--drivers/usb/isp1760/Kconfig2
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c12
-rw-r--r--drivers/usb/isp1760/isp1760-if.c6
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c6
-rw-r--r--drivers/usb/misc/Kconfig114
-rw-r--r--drivers/usb/misc/Makefile5
-rw-r--r--drivers/usb/misc/apple-mfi-fastcharge.c25
-rw-r--r--drivers/usb/misc/appledisplay.c15
-rw-r--r--drivers/usb/misc/brcmstb-usb-pinmap.c1
-rw-r--r--drivers/usb/misc/chaoskey.c52
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c6
-rw-r--r--drivers/usb/misc/cytherm.c12
-rw-r--r--drivers/usb/misc/ehset.c8
-rw-r--r--drivers/usb/misc/ezusb.c1
-rw-r--r--drivers/usb/misc/ftdi-elan.c2784
-rw-r--r--drivers/usb/misc/idmouse.c8
-rw-r--r--drivers/usb/misc/iowarrior.c55
-rw-r--r--drivers/usb/misc/isight_firmware.c1
-rw-r--r--drivers/usb/misc/ldusb.c1
-rw-r--r--drivers/usb/misc/legousbtower.c2
-rw-r--r--drivers/usb/misc/onboard_usb_dev.c726
-rw-r--r--drivers/usb/misc/onboard_usb_dev.h158
-rw-r--r--drivers/usb/misc/onboard_usb_dev_pdevs.c144
-rw-r--r--drivers/usb/misc/onboard_usb_hub.c455
-rw-r--r--drivers/usb/misc/onboard_usb_hub.h36
-rw-r--r--drivers/usb/misc/onboard_usb_hub_pdevs.c143
-rw-r--r--drivers/usb/misc/qcom_eud.c51
-rw-r--r--drivers/usb/misc/sisusbvga/Kconfig34
-rw-r--r--drivers/usb/misc/sisusbvga/Makefile3
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c3230
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.h21
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_con.c1496
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_init.c955
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_init.h180
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_struct.h2
-rw-r--r--drivers/usb/misc/sisusbvga/sisusbvga.c2969
-rw-r--r--drivers/usb/misc/usb-ljca.c909
-rw-r--r--drivers/usb/misc/usb251xb.c213
-rw-r--r--drivers/usb/misc/usb3503.c102
-rw-r--r--drivers/usb/misc/usb4604.c5
-rw-r--r--drivers/usb/misc/usb_u132.h97
-rw-r--r--drivers/usb/misc/usbio.c749
-rw-r--r--drivers/usb/misc/usbsevseg.c2
-rw-r--r--drivers/usb/misc/usbtest.c11
-rw-r--r--drivers/usb/misc/uss720.c48
-rw-r--r--drivers/usb/misc/yurex.c13
-rw-r--r--drivers/usb/mon/mon_bin.c49
-rw-r--r--drivers/usb/mon/mon_main.c24
-rw-r--r--drivers/usb/mon/mon_stat.c7
-rw-r--r--drivers/usb/mon/mon_text.c30
-rw-r--r--drivers/usb/mtu3/Kconfig2
-rw-r--r--drivers/usb/mtu3/mtu3.h37
-rw-r--r--drivers/usb/mtu3/mtu3_core.c4
-rw-r--r--drivers/usb/mtu3/mtu3_debugfs.c43
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c4
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c8
-rw-r--r--drivers/usb/mtu3/mtu3_host.c33
-rw-r--r--drivers/usb/mtu3/mtu3_hw_regs.h1
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c23
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.c53
-rw-r--r--drivers/usb/mtu3/mtu3_trace.h8
-rw-r--r--drivers/usb/musb/Kconfig25
-rw-r--r--drivers/usb/musb/Makefile3
-rw-r--r--drivers/usb/musb/am35x.c610
-rw-r--r--drivers/usb/musb/cppi_dma.c1547
-rw-r--r--drivers/usb/musb/cppi_dma.h3
-rw-r--r--drivers/usb/musb/da8xx.c57
-rw-r--r--drivers/usb/musb/davinci.c606
-rw-r--r--drivers/usb/musb/davinci.h103
-rw-r--r--drivers/usb/musb/jz4740.c72
-rw-r--r--drivers/usb/musb/mediatek.c37
-rw-r--r--drivers/usb/musb/mpfs.c170
-rw-r--r--drivers/usb/musb/musb_core.c149
-rw-r--r--drivers/usb/musb/musb_core.h26
-rw-r--r--drivers/usb/musb/musb_cppi41.c10
-rw-r--r--drivers/usb/musb/musb_debugfs.c13
-rw-r--r--drivers/usb/musb/musb_dma.h17
-rw-r--r--drivers/usb/musb/musb_dsps.c24
-rw-r--r--drivers/usb/musb/musb_gadget.c101
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c2
-rw-r--r--drivers/usb/musb/musb_host.c35
-rw-r--r--drivers/usb/musb/musb_trace.h12
-rw-r--r--drivers/usb/musb/musb_virthub.c35
-rw-r--r--drivers/usb/musb/omap2430.c94
-rw-r--r--drivers/usb/musb/sunxi.c133
-rw-r--r--drivers/usb/musb/tusb6010.c62
-rw-r--r--drivers/usb/musb/ux500.c4
-rw-r--r--drivers/usb/phy/Kconfig43
-rw-r--r--drivers/usb/phy/Makefile3
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c4
-rw-r--r--drivers/usb/phy/phy-am335x-control.c1
-rw-r--r--drivers/usb/phy/phy-am335x.c4
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c10
-rw-r--r--drivers/usb/phy/phy-generic.c94
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c17
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c1641
-rw-r--r--drivers/usb/phy/phy-isp1301.c9
-rw-r--r--drivers/usb/phy/phy-jz4770.c362
-rw-r--r--drivers/usb/phy/phy-keystone.c4
-rw-r--r--drivers/usb/phy/phy-mv-usb.c882
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c165
-rw-r--r--drivers/usb/phy/phy-tahvo.c9
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c111
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c15
-rw-r--r--drivers/usb/phy/phy-ulpi.c23
-rw-r--r--drivers/usb/phy/phy.c40
-rw-r--r--drivers/usb/renesas_usbhs/common.c168
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c2
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c2
-rw-r--r--drivers/usb/renesas_usbhs/rza.c2
-rw-r--r--drivers/usb/renesas_usbhs/rza.h1
-rw-r--r--drivers/usb/renesas_usbhs/rza2.c14
-rw-r--r--drivers/usb/roles/class.c101
-rw-r--r--drivers/usb/roles/intel-xhci-usb-role-switch.c4
-rw-r--r--drivers/usb/serial/aircable.c3
-rw-r--r--drivers/usb/serial/ark3116.c10
-rw-r--r--drivers/usb/serial/belkin_sa.c61
-rw-r--r--drivers/usb/serial/bus.c12
-rw-r--r--drivers/usb/serial/ch341.c81
-rw-r--r--drivers/usb/serial/console.c4
-rw-r--r--drivers/usb/serial/cp210x.c65
-rw-r--r--drivers/usb/serial/cyberjack.c1
-rw-r--r--drivers/usb/serial/cypress_m8.c11
-rw-r--r--drivers/usb/serial/digi_acceleport.c15
-rw-r--r--drivers/usb/serial/empeg.c1
-rw-r--r--drivers/usb/serial/f81232.c21
-rw-r--r--drivers/usb/serial/f81534.c21
-rw-r--r--drivers/usb/serial/ftdi_sio.c739
-rw-r--r--drivers/usb/serial/ftdi_sio.h22
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h41
-rw-r--r--drivers/usb/serial/garmin_gps.c11
-rw-r--r--drivers/usb/serial/generic.c1
-rw-r--r--drivers/usb/serial/io_edgeport.c27
-rw-r--r--drivers/usb/serial/io_ti.c19
-rw-r--r--drivers/usb/serial/ipaq.c1
-rw-r--r--drivers/usb/serial/ipw.c1
-rw-r--r--drivers/usb/serial/ir-usb.c7
-rw-r--r--drivers/usb/serial/iuu_phoenix.c4
-rw-r--r--drivers/usb/serial/keyspan.c13
-rw-r--r--drivers/usb/serial/keyspan_pda.c13
-rw-r--r--drivers/usb/serial/kl5kusb105.c8
-rw-r--r--drivers/usb/serial/kobil_sct.c220
-rw-r--r--drivers/usb/serial/mct_u232.c14
-rw-r--r--drivers/usb/serial/metro-usb.c1
-rw-r--r--drivers/usb/serial/mos7720.c15
-rw-r--r--drivers/usb/serial/mos7840.c79
-rw-r--r--drivers/usb/serial/mxuport.c14
-rw-r--r--drivers/usb/serial/navman.c2
-rw-r--r--drivers/usb/serial/omninet.c1
-rw-r--r--drivers/usb/serial/opticon.c1
-rw-r--r--drivers/usb/serial/option.c351
-rw-r--r--drivers/usb/serial/oti6858.c10
-rw-r--r--drivers/usb/serial/pl2303.c61
-rw-r--r--drivers/usb/serial/pl2303.h4
-rw-r--r--drivers/usb/serial/qcaux.c2
-rw-r--r--drivers/usb/serial/qcserial.c6
-rw-r--r--drivers/usb/serial/quatech2.c25
-rw-r--r--drivers/usb/serial/safe_serial.c1
-rw-r--r--drivers/usb/serial/sierra.c3
-rw-r--r--drivers/usb/serial/spcp8x5.c14
-rw-r--r--drivers/usb/serial/ssu100.c5
-rw-r--r--drivers/usb/serial/symbolserial.c2
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c23
-rw-r--r--drivers/usb/serial/upd78f0730.c12
-rw-r--r--drivers/usb/serial/usb-serial-simple.c82
-rw-r--r--drivers/usb/serial/usb-serial.c43
-rw-r--r--drivers/usb/serial/usb_debug.c23
-rw-r--r--drivers/usb/serial/visor.c3
-rw-r--r--drivers/usb/serial/whiteheat.c15
-rw-r--r--drivers/usb/serial/wishbone-serial.c1
-rw-r--r--drivers/usb/serial/xr_serial.c116
-rw-r--r--drivers/usb/serial/xsens_mt.c1
-rw-r--r--drivers/usb/storage/Kconfig3
-rw-r--r--drivers/usb/storage/Makefile2
-rw-r--r--drivers/usb/storage/alauda.c37
-rw-r--r--drivers/usb/storage/cypress_atacb.c6
-rw-r--r--drivers/usb/storage/datafab.c20
-rw-r--r--drivers/usb/storage/debug.c4
-rw-r--r--drivers/usb/storage/ene_ub6250.c18
-rw-r--r--drivers/usb/storage/freecom.c7
-rw-r--r--drivers/usb/storage/initializers.c2
-rw-r--r--drivers/usb/storage/isd200.c29
-rw-r--r--drivers/usb/storage/jumpshot.c16
-rw-r--r--drivers/usb/storage/karma.c6
-rw-r--r--drivers/usb/storage/onetouch.c8
-rw-r--r--drivers/usb/storage/protocol.c3
-rw-r--r--drivers/usb/storage/realtek_cr.c25
-rw-r--r--drivers/usb/storage/scsiglue.c101
-rw-r--r--drivers/usb/storage/sddr09.c24
-rw-r--r--drivers/usb/storage/sddr55.c28
-rw-r--r--drivers/usb/storage/shuttle_usbat.c12
-rw-r--r--drivers/usb/storage/sierra_ms.c16
-rw-r--r--drivers/usb/storage/transport.c28
-rw-r--r--drivers/usb/storage/uas-detect.h17
-rw-r--r--drivers/usb/storage/uas.c120
-rw-r--r--drivers/usb/storage/unusual_cypress.h2
-rw-r--r--drivers/usb/storage/unusual_devs.h64
-rw-r--r--drivers/usb/storage/unusual_uas.h37
-rw-r--r--drivers/usb/storage/usb.c141
-rw-r--r--drivers/usb/storage/usb.h6
-rw-r--r--drivers/usb/storage/usual-tables.c17
-rw-r--r--drivers/usb/typec/Kconfig14
-rw-r--r--drivers/usb/typec/Makefile1
-rw-r--r--drivers/usb/typec/altmodes/Kconfig9
-rw-r--r--drivers/usb/typec/altmodes/Makefile2
-rw-r--r--drivers/usb/typec/altmodes/displayport.c326
-rw-r--r--drivers/usb/typec/altmodes/nvidia.c3
-rw-r--r--drivers/usb/typec/altmodes/thunderbolt.c388
-rw-r--r--drivers/usb/typec/anx7411.c98
-rw-r--r--drivers/usb/typec/bus.c165
-rw-r--r--drivers/usb/typec/bus.h4
-rw-r--r--drivers/usb/typec/class.c548
-rw-r--r--drivers/usb/typec/class.h27
-rw-r--r--drivers/usb/typec/hd3ss3220.c322
-rw-r--r--drivers/usb/typec/mux.c80
-rw-r--r--drivers/usb/typec/mux/Kconfig66
-rw-r--r--drivers/usb/typec/mux/Makefile7
-rw-r--r--drivers/usb/typec/mux/fsa4480.c222
-rw-r--r--drivers/usb/typec/mux/gpio-sbu-mux.c171
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c174
-rw-r--r--drivers/usb/typec/mux/it5205.c294
-rw-r--r--drivers/usb/typec/mux/nb7vpq904m.c527
-rw-r--r--drivers/usb/typec/mux/pi3usb30532.c5
-rw-r--r--drivers/usb/typec/mux/ps883x.c499
-rw-r--r--drivers/usb/typec/mux/ptn36502.c431
-rw-r--r--drivers/usb/typec/mux/tusb1046.c196
-rw-r--r--drivers/usb/typec/mux/wcd939x-usbss.c779
-rw-r--r--drivers/usb/typec/pd.c153
-rw-r--r--drivers/usb/typec/port-mapper.c32
-rw-r--r--drivers/usb/typec/qcom-pmic-typec.c262
-rw-r--r--drivers/usb/typec/retimer.c21
-rw-r--r--drivers/usb/typec/retimer.h4
-rw-r--r--drivers/usb/typec/rt1719.c17
-rw-r--r--drivers/usb/typec/stusb160x.c22
-rw-r--r--drivers/usb/typec/tcpm/Kconfig24
-rw-r--r--drivers/usb/typec/tcpm/Makefile3
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c58
-rw-r--r--drivers/usb/typec/tcpm/maxim_contaminant.c450
-rw-r--r--drivers/usb/typec/tcpm/qcom/Makefile7
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c172
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.h27
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c649
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h37
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c81
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c809
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h35
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c312
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim.c532
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim.h101
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c586
-rw-r--r--drivers/usb/typec/tcpm/tcpci_mt6360.c9
-rw-r--r--drivers/usb/typec/tcpm/tcpci_mt6370.c204
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c210
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c2323
-rw-r--r--drivers/usb/typec/tcpm/wcove.c10
-rw-r--r--drivers/usb/typec/tipd/core.c1446
-rw-r--r--drivers/usb/typec/tipd/tps6598x.h72
-rw-r--r--drivers/usb/typec/tipd/trace.h133
-rw-r--r--drivers/usb/typec/ucsi/Kconfig46
-rw-r--r--drivers/usb/typec/ucsi/Makefile6
-rw-r--r--drivers/usb/typec/ucsi/cros_ec_ucsi.c341
-rw-r--r--drivers/usb/typec/ucsi/debugfs.c171
-rw-r--r--drivers/usb/typec/ucsi/displayport.c40
-rw-r--r--drivers/usb/typec/ucsi/psy.c121
-rw-r--r--drivers/usb/typec/ucsi/trace.c19
-rw-r--r--drivers/usb/typec/ucsi/trace.h29
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c1215
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h444
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c151
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c282
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c486
-rw-r--r--drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c528
-rw-r--r--drivers/usb/typec/ucsi/ucsi_stm32g0.c89
-rw-r--r--drivers/usb/typec/ucsi/ucsi_yoga_c630.c332
-rw-r--r--drivers/usb/typec/wusb3801.c8
-rw-r--r--drivers/usb/usb-skeleton.c1
-rw-r--r--drivers/usb/usbip/stub_dev.c13
-rw-r--r--drivers/usb/usbip/stub_main.c14
-rw-r--r--drivers/usb/usbip/stub_rx.c83
-rw-r--r--drivers/usb/usbip/stub_tx.c11
-rw-r--r--drivers/usb/usbip/usbip_common.c94
-rw-r--r--drivers/usb/usbip/usbip_common.h6
-rw-r--r--drivers/usb/usbip/vhci_hcd.c226
-rw-r--r--drivers/usb/usbip/vhci_rx.c6
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c3
-rw-r--r--drivers/usb/usbip/vudc.h2
-rw-r--r--drivers/usb/usbip/vudc_dev.c8
-rw-r--r--drivers/usb/usbip/vudc_rx.c4
-rw-r--r--drivers/usb/usbip/vudc_sysfs.c8
-rw-r--r--drivers/usb/usbip/vudc_transfer.c2
-rw-r--r--drivers/usb/usbip/vudc_tx.c2
-rw-r--r--drivers/vdpa/Kconfig57
-rw-r--r--drivers/vdpa/Makefile3
-rw-r--r--drivers/vdpa/alibaba/eni_vdpa.c19
-rw-r--r--drivers/vdpa/ifcvf/ifcvf_base.c232
-rw-r--r--drivers/vdpa/ifcvf/ifcvf_base.h63
-rw-r--r--drivers/vdpa/ifcvf/ifcvf_main.c279
-rw-r--r--drivers/vdpa/mlx5/Makefile2
-rw-r--r--drivers/vdpa/mlx5/core/mlx5_vdpa.h78
-rw-r--r--drivers/vdpa/mlx5/core/mr.c513
-rw-r--r--drivers/vdpa/mlx5/core/resources.c79
-rw-r--r--drivers/vdpa/mlx5/net/debug.c153
-rw-r--r--drivers/vdpa/mlx5/net/mlx5_vnet.c1801
-rw-r--r--drivers/vdpa/mlx5/net/mlx5_vnet.h119
-rw-r--r--drivers/vdpa/octeon_ep/Makefile4
-rw-r--r--drivers/vdpa/octeon_ep/octep_vdpa.h114
-rw-r--r--drivers/vdpa/octeon_ep/octep_vdpa_hw.c549
-rw-r--r--drivers/vdpa/octeon_ep/octep_vdpa_main.c900
-rw-r--r--drivers/vdpa/pds/Makefile9
-rw-r--r--drivers/vdpa/pds/aux_drv.c140
-rw-r--r--drivers/vdpa/pds/aux_drv.h26
-rw-r--r--drivers/vdpa/pds/cmds.c185
-rw-r--r--drivers/vdpa/pds/cmds.h17
-rw-r--r--drivers/vdpa/pds/debugfs.c286
-rw-r--r--drivers/vdpa/pds/debugfs.h17
-rw-r--r--drivers/vdpa/pds/vdpa_dev.c859
-rw-r--r--drivers/vdpa/pds/vdpa_dev.h51
-rw-r--r--drivers/vdpa/solidrun/Makefile7
-rw-r--r--drivers/vdpa/solidrun/snet_ctrl.c336
-rw-r--r--drivers/vdpa/solidrun/snet_hwmon.c188
-rw-r--r--drivers/vdpa/solidrun/snet_main.c1137
-rw-r--r--drivers/vdpa/solidrun/snet_vdpa.h209
-rw-r--r--drivers/vdpa/vdpa.c496
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim.c472
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim.h24
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim_blk.c101
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim_net.c291
-rw-r--r--drivers/vdpa/vdpa_user/iova_domain.c170
-rw-r--r--drivers/vdpa/vdpa_user/iova_domain.h17
-rw-r--r--drivers/vdpa/vdpa_user/vduse_dev.c613
-rw-r--r--drivers/vdpa/virtio_pci/vp_vdpa.c85
-rw-r--r--drivers/vfio/Kconfig70
-rw-r--r--drivers/vfio/Makefile16
-rw-r--r--drivers/vfio/cdx/Kconfig17
-rw-r--r--drivers/vfio/cdx/Makefile12
-rw-r--r--drivers/vfio/cdx/intr.c217
-rw-r--r--drivers/vfio/cdx/main.c343
-rw-r--r--drivers/vfio/cdx/private.h62
-rw-r--r--drivers/vfio/container.c607
-rw-r--r--drivers/vfio/debugfs.c111
-rw-r--r--drivers/vfio/device_cdev.c311
-rw-r--r--drivers/vfio/fsl-mc/Kconfig11
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc.c156
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c18
-rw-r--r--drivers/vfio/group.c933
-rw-r--r--drivers/vfio/iommufd.c303
-rw-r--r--drivers/vfio/mdev/Kconfig8
-rw-r--r--drivers/vfio/mdev/mdev_core.c216
-rw-r--r--drivers/vfio/mdev/mdev_driver.c11
-rw-r--r--drivers/vfio/mdev/mdev_private.h35
-rw-r--r--drivers/vfio/mdev/mdev_sysfs.c189
-rw-r--r--drivers/vfio/pci/Kconfig23
-rw-r--r--drivers/vfio/pci/Makefile11
-rw-r--r--drivers/vfio/pci/hisilicon/Kconfig4
-rw-r--r--drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c874
-rw-r--r--drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h67
-rw-r--r--drivers/vfio/pci/mlx5/Kconfig3
-rw-r--r--drivers/vfio/pci/mlx5/cmd.c1573
-rw-r--r--drivers/vfio/pci/mlx5/cmd.h205
-rw-r--r--drivers/vfio/pci/mlx5/main.c1200
-rw-r--r--drivers/vfio/pci/nvgrace-gpu/Kconfig10
-rw-r--r--drivers/vfio/pci/nvgrace-gpu/Makefile3
-rw-r--r--drivers/vfio/pci/nvgrace-gpu/main.c1187
-rw-r--r--drivers/vfio/pci/pds/Kconfig20
-rw-r--r--drivers/vfio/pci/pds/Makefile11
-rw-r--r--drivers/vfio/pci/pds/cmds.c510
-rw-r--r--drivers/vfio/pci/pds/cmds.h25
-rw-r--r--drivers/vfio/pci/pds/dirty.c644
-rw-r--r--drivers/vfio/pci/pds/dirty.h41
-rw-r--r--drivers/vfio/pci/pds/lm.c444
-rw-r--r--drivers/vfio/pci/pds/lm.h42
-rw-r--r--drivers/vfio/pci/pds/pci_drv.c193
-rw-r--r--drivers/vfio/pci/pds/pci_drv.h9
-rw-r--r--drivers/vfio/pci/pds/vfio_dev.c215
-rw-r--r--drivers/vfio/pci/pds/vfio_dev.h35
-rw-r--r--drivers/vfio/pci/qat/Kconfig12
-rw-r--r--drivers/vfio/pci/qat/Makefile3
-rw-r--r--drivers/vfio/pci/qat/main.c704
-rw-r--r--drivers/vfio/pci/vfio_pci.c41
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c122
-rw-r--r--drivers/vfio/pci/vfio_pci_core.c1705
-rw-r--r--drivers/vfio/pci/vfio_pci_dmabuf.c350
-rw-r--r--drivers/vfio/pci/vfio_pci_igd.c15
-rw-r--r--drivers/vfio/pci/vfio_pci_intrs.c547
-rw-r--r--drivers/vfio/pci/vfio_pci_priv.h136
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c205
-rw-r--r--drivers/vfio/pci/vfio_pci_zdev.c2
-rw-r--r--drivers/vfio/pci/virtio/Kconfig31
-rw-r--r--drivers/vfio/pci/virtio/Makefile4
-rw-r--r--drivers/vfio/pci/virtio/common.h126
-rw-r--r--drivers/vfio/pci/virtio/legacy_io.c398
-rw-r--r--drivers/vfio/pci/virtio/main.c231
-rw-r--r--drivers/vfio/pci/virtio/migrate.c1336
-rw-r--r--drivers/vfio/pci/xe/Kconfig12
-rw-r--r--drivers/vfio/pci/xe/Makefile3
-rw-r--r--drivers/vfio/pci/xe/main.c573
-rw-r--r--drivers/vfio/platform/Kconfig23
-rw-r--r--drivers/vfio/platform/Makefile9
-rw-r--r--drivers/vfio/platform/reset/Kconfig8
-rw-r--r--drivers/vfio/platform/reset/vfio_platform_amdxgbe.c2
-rw-r--r--drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c2
-rw-r--r--drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c2
-rw-r--r--drivers/vfio/platform/vfio_amba.c85
-rw-r--r--drivers/vfio/platform/vfio_platform.c73
-rw-r--r--drivers/vfio/platform/vfio_platform_common.c126
-rw-r--r--drivers/vfio/platform/vfio_platform_irq.c109
-rw-r--r--drivers/vfio/platform/vfio_platform_private.h21
-rw-r--r--drivers/vfio/vfio.h404
-rw-r--r--drivers/vfio/vfio_iommu_spapr_tce.c174
-rw-r--r--drivers/vfio/vfio_iommu_type1.c767
-rw-r--r--drivers/vfio/vfio_main.c2146
-rw-r--r--drivers/vfio/vfio_spapr_eeh.c107
-rw-r--r--drivers/vfio/virqfd.c56
-rw-r--r--drivers/vhost/Kconfig24
-rw-r--r--drivers/vhost/net.c394
-rw-r--r--drivers/vhost/scsi.c1201
-rw-r--r--drivers/vhost/test.c13
-rw-r--r--drivers/vhost/vdpa.c363
-rw-r--r--drivers/vhost/vhost.c1069
-rw-r--r--drivers/vhost/vhost.h133
-rw-r--r--drivers/vhost/vringh.c345
-rw-r--r--drivers/vhost/vsock.c265
-rw-r--r--drivers/video/Kconfig38
-rw-r--r--drivers/video/Makefile6
-rw-r--r--drivers/video/aperture.c101
-rw-r--r--drivers/video/backlight/88pm860x_bl.c6
-rw-r--r--drivers/video/backlight/Kconfig93
-rw-r--r--drivers/video/backlight/Makefile11
-rw-r--r--drivers/video/backlight/aat2870_bl.c22
-rw-r--r--drivers/video/backlight/adp5520_bl.c5
-rw-r--r--drivers/video/backlight/adp8860_bl.c13
-rw-r--r--drivers/video/backlight/adp8870_bl.c15
-rw-r--r--drivers/video/backlight/ams369fg06.c25
-rw-r--r--drivers/video/backlight/apple_bl.c34
-rw-r--r--drivers/video/backlight/apple_dwi_bl.c124
-rw-r--r--drivers/video/backlight/arcxcnn_bl.c13
-rw-r--r--drivers/video/backlight/as3711_bl.c32
-rw-r--r--drivers/video/backlight/aw99706.c471
-rw-r--r--drivers/video/backlight/backlight.c163
-rw-r--r--drivers/video/backlight/bd6107.c31
-rw-r--r--drivers/video/backlight/corgi_lcd.c24
-rw-r--r--drivers/video/backlight/cr_bllcd.c266
-rw-r--r--drivers/video/backlight/da903x_bl.c1
-rw-r--r--drivers/video/backlight/da9052_bl.c7
-rw-r--r--drivers/video/backlight/ep93xx_bl.c1
-rw-r--r--drivers/video/backlight/gpio_backlight.c38
-rw-r--r--drivers/video/backlight/hp680_bl.c5
-rw-r--r--drivers/video/backlight/hx8357.c135
-rw-r--r--drivers/video/backlight/ili922x.c17
-rw-r--r--drivers/video/backlight/ili9320.c17
-rw-r--r--drivers/video/backlight/ipaq_micro_bl.c10
-rw-r--r--drivers/video/backlight/jornada720_bl.c4
-rw-r--r--drivers/video/backlight/jornada720_lcd.c12
-rw-r--r--drivers/video/backlight/kb3886_bl.c4
-rw-r--r--drivers/video/backlight/ktd253-backlight.c14
-rw-r--r--drivers/video/backlight/ktd2801-backlight.c129
-rw-r--r--drivers/video/backlight/ktz8866.c209
-rw-r--r--drivers/video/backlight/l4f00242t03.c73
-rw-r--r--drivers/video/backlight/lcd.c115
-rw-r--r--drivers/video/backlight/led_bl.c38
-rw-r--r--drivers/video/backlight/lm3509_bl.c343
-rw-r--r--drivers/video/backlight/lm3533_bl.c7
-rw-r--r--drivers/video/backlight/lm3630a_bl.c56
-rw-r--r--drivers/video/backlight/lm3639_bl.c9
-rw-r--r--drivers/video/backlight/lms283gf05.c4
-rw-r--r--drivers/video/backlight/lms501kf03.c26
-rw-r--r--drivers/video/backlight/locomolcd.c11
-rw-r--r--drivers/video/backlight/lp855x_bl.c68
-rw-r--r--drivers/video/backlight/lp8788_bl.c156
-rw-r--r--drivers/video/backlight/ltv350qv.c17
-rw-r--r--drivers/video/backlight/lv5207lp.c22
-rw-r--r--drivers/video/backlight/max8925_bl.c1
-rw-r--r--drivers/video/backlight/mp3309c.c422
-rw-r--r--drivers/video/backlight/mt6370-backlight.c349
-rw-r--r--drivers/video/backlight/omap1_bl.c47
-rw-r--r--drivers/video/backlight/otm3225a.c5
-rw-r--r--drivers/video/backlight/pandora_bl.c7
-rw-r--r--drivers/video/backlight/pcf50633-backlight.c155
-rw-r--r--drivers/video/backlight/platform_lcd.c23
-rw-r--r--drivers/video/backlight/pwm_bl.c153
-rw-r--r--drivers/video/backlight/qcom-wled.c13
-rw-r--r--drivers/video/backlight/rave-sp-backlight.c4
-rw-r--r--drivers/video/backlight/rt4831-backlight.c6
-rw-r--r--drivers/video/backlight/sky81452-backlight.c16
-rw-r--r--drivers/video/backlight/tdo24m.c21
-rw-r--r--drivers/video/backlight/tosa_bl.c174
-rw-r--r--drivers/video/backlight/tosa_bl.h8
-rw-r--r--drivers/video/backlight/tosa_lcd.c284
-rw-r--r--drivers/video/backlight/tps65217_bl.c1
-rw-r--r--drivers/video/backlight/vgg2432a4.c1
-rw-r--r--drivers/video/backlight/wm831x_bl.c1
-rw-r--r--drivers/video/cmdline.c135
-rw-r--r--drivers/video/console/Kconfig20
-rw-r--r--drivers/video/console/Makefile4
-rw-r--r--drivers/video/console/dummycon.c59
-rw-r--r--drivers/video/console/mdacon.c44
-rw-r--r--drivers/video/console/newport_con.c75
-rw-r--r--drivers/video/console/sticon.c105
-rw-r--r--drivers/video/console/vgacon.c275
-rw-r--r--drivers/video/fbdev/68328fb.c22
-rw-r--r--drivers/video/fbdev/Kconfig779
-rw-r--r--drivers/video/fbdev/Makefile24
-rw-r--r--drivers/video/fbdev/acornfb.c6
-rw-r--r--drivers/video/fbdev/amba-clcd.c986
-rw-r--r--drivers/video/fbdev/amifb.c23
-rw-r--r--drivers/video/fbdev/arcfb.c139
-rw-r--r--drivers/video/fbdev/arkfb.c40
-rw-r--r--drivers/video/fbdev/asiliantfb.c13
-rw-r--r--drivers/video/fbdev/atafb.c3
-rw-r--r--drivers/video/fbdev/atmel_lcdfb.c55
-rw-r--r--drivers/video/fbdev/aty/aty128fb.c91
-rw-r--r--drivers/video/fbdev/aty/atyfb.h2
-rw-r--r--drivers/video/fbdev/aty/atyfb_base.c66
-rw-r--r--drivers/video/fbdev/aty/mach64_accel.c2
-rw-r--r--drivers/video/fbdev/aty/mach64_cursor.c9
-rw-r--r--drivers/video/fbdev/aty/radeon_backlight.c12
-rw-r--r--drivers/video/fbdev/aty/radeon_base.c110
-rw-r--r--drivers/video/fbdev/aty/radeon_pm.c2
-rw-r--r--drivers/video/fbdev/au1100fb.c25
-rw-r--r--drivers/video/fbdev/au1200fb.c34
-rw-r--r--drivers/video/fbdev/broadsheetfb.c99
-rw-r--r--drivers/video/fbdev/bw2.c29
-rw-r--r--drivers/video/fbdev/c2p_iplan2.c3
-rw-r--r--drivers/video/fbdev/c2p_planar.c4
-rw-r--r--drivers/video/fbdev/carminefb.c22
-rw-r--r--drivers/video/fbdev/carminefb.h2
-rw-r--r--drivers/video/fbdev/cg14.c32
-rw-r--r--drivers/video/fbdev/cg3.c29
-rw-r--r--drivers/video/fbdev/cg6.c28
-rw-r--r--drivers/video/fbdev/chipsfb.c40
-rw-r--r--drivers/video/fbdev/cirrusfb.c15
-rw-r--r--drivers/video/fbdev/clps711x-fb.c53
-rw-r--r--drivers/video/fbdev/cobalt_lcdfb.c13
-rw-r--r--drivers/video/fbdev/controlfb.c50
-rw-r--r--drivers/video/fbdev/core/Kconfig207
-rw-r--r--drivers/video/fbdev/core/Makefile19
-rw-r--r--drivers/video/fbdev/core/bitblit.c174
-rw-r--r--drivers/video/fbdev/core/cfbcopyarea.c427
-rw-r--r--drivers/video/fbdev/core/cfbfillrect.c363
-rw-r--r--drivers/video/fbdev/core/cfbimgblt.c358
-rw-r--r--drivers/video/fbdev/core/cfbmem.h43
-rw-r--r--drivers/video/fbdev/core/fb_backlight.c51
-rw-r--r--drivers/video/fbdev/core/fb_chrdev.c444
-rw-r--r--drivers/video/fbdev/core/fb_cmdline.c94
-rw-r--r--drivers/video/fbdev/core/fb_copyarea.h405
-rw-r--r--drivers/video/fbdev/core/fb_ddc.c2
-rw-r--r--drivers/video/fbdev/core/fb_defio.c142
-rw-r--r--drivers/video/fbdev/core/fb_draw.h274
-rw-r--r--drivers/video/fbdev/core/fb_fillrect.h279
-rw-r--r--drivers/video/fbdev/core/fb_imageblit.h495
-rw-r--r--drivers/video/fbdev/core/fb_info.c80
-rw-r--r--drivers/video/fbdev/core/fb_internal.h84
-rw-r--r--drivers/video/fbdev/core/fb_io_fops.c173
-rw-r--r--drivers/video/fbdev/core/fb_logo.c508
-rw-r--r--drivers/video/fbdev/core/fb_procfs.c62
-rw-r--r--drivers/video/fbdev/core/fb_sys_fops.c44
-rw-r--r--drivers/video/fbdev/core/fbcmap.c1
-rw-r--r--drivers/video/fbdev/core/fbcon.c917
-rw-r--r--drivers/video/fbdev/core/fbcon.h59
-rw-r--r--drivers/video/fbdev/core/fbcon_ccw.c167
-rw-r--r--drivers/video/fbdev/core/fbcon_cw.c167
-rw-r--r--drivers/video/fbdev/core/fbcon_rotate.c47
-rw-r--r--drivers/video/fbdev/core/fbcon_rotate.h18
-rw-r--r--drivers/video/fbdev/core/fbcon_ud.c183
-rw-r--r--drivers/video/fbdev/core/fbcvt.c2
-rw-r--r--drivers/video/fbdev/core/fbmem.c1572
-rw-r--r--drivers/video/fbdev/core/fbmon.c21
-rw-r--r--drivers/video/fbdev/core/fbsysfs.c220
-rw-r--r--drivers/video/fbdev/core/modedb.c36
-rw-r--r--drivers/video/fbdev/core/softcursor.c18
-rw-r--r--drivers/video/fbdev/core/svgalib.c118
-rw-r--r--drivers/video/fbdev/core/syscopyarea.c370
-rw-r--r--drivers/video/fbdev/core/sysfillrect.c325
-rw-r--r--drivers/video/fbdev/core/sysimgblt.c334
-rw-r--r--drivers/video/fbdev/core/sysmem.h39
-rw-r--r--drivers/video/fbdev/core/tileblit.c81
-rw-r--r--drivers/video/fbdev/cyber2000fb.c63
-rw-r--r--drivers/video/fbdev/cyber2000fb.h2
-rw-r--r--drivers/video/fbdev/da8xx-fb.c1666
-rw-r--r--drivers/video/fbdev/dnfb.c2
-rw-r--r--drivers/video/fbdev/efifb.c286
-rw-r--r--drivers/video/fbdev/ep93xx-fb.c41
-rw-r--r--drivers/video/fbdev/ffb.c29
-rw-r--r--drivers/video/fbdev/fm2fb.c5
-rw-r--r--drivers/video/fbdev/fsl-diu-fb.c49
-rw-r--r--drivers/video/fbdev/g364fb.c12
-rw-r--r--drivers/video/fbdev/gbefb.c45
-rw-r--r--drivers/video/fbdev/geode/Kconfig13
-rw-r--r--drivers/video/fbdev/geode/display_gx.c1
-rw-r--r--drivers/video/fbdev/geode/gx1fb_core.c16
-rw-r--r--drivers/video/fbdev/geode/gxfb_core.c19
-rw-r--r--drivers/video/fbdev/geode/lxfb_core.c19
-rw-r--r--drivers/video/fbdev/geode/lxfb_ops.c23
-rw-r--r--drivers/video/fbdev/geode/suspend_gx.c10
-rw-r--r--drivers/video/fbdev/geode/video_gx.c16
-rw-r--r--drivers/video/fbdev/goldfishfb.c13
-rw-r--r--drivers/video/fbdev/grvga.c13
-rw-r--r--drivers/video/fbdev/gxt4500.c17
-rw-r--r--drivers/video/fbdev/hecubafb.c91
-rw-r--r--drivers/video/fbdev/hgafb.c59
-rw-r--r--drivers/video/fbdev/hitfb.c140
-rw-r--r--drivers/video/fbdev/hpfb.c12
-rw-r--r--drivers/video/fbdev/hyperv_fb.c149
-rw-r--r--drivers/video/fbdev/i740fb.c17
-rw-r--r--drivers/video/fbdev/i810/i810_dvt.c3
-rw-r--r--drivers/video/fbdev/i810/i810_main.c371
-rw-r--r--drivers/video/fbdev/imsttfb.c92
-rw-r--r--drivers/video/fbdev/imxfb.c280
-rw-r--r--drivers/video/fbdev/intelfb/Makefile8
-rw-r--r--drivers/video/fbdev/intelfb/intelfb.h382
-rw-r--r--drivers/video/fbdev/intelfb/intelfb_i2c.c209
-rw-r--r--drivers/video/fbdev/intelfb/intelfbdrv.c1668
-rw-r--r--drivers/video/fbdev/intelfb/intelfbhw.c2115
-rw-r--r--drivers/video/fbdev/intelfb/intelfbhw.h609
-rw-r--r--drivers/video/fbdev/kyro/STG4000InitDevice.c10
-rw-r--r--drivers/video/fbdev/kyro/fbdev.c42
-rw-r--r--drivers/video/fbdev/leo.c29
-rw-r--r--drivers/video/fbdev/macfb.c15
-rw-r--r--drivers/video/fbdev/macmodes.c4
-rw-r--r--drivers/video/fbdev/matrox/g450_pll.c26
-rw-r--r--drivers/video/fbdev/matrox/i2c-matroxfb.c15
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_DAC1064.c48
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_Ti3026.c2
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_accel.c9
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_base.c15
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_base.h6
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_crtc2.c9
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_g450.c62
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_maven.c8
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_misc.c21
-rw-r--r--drivers/video/fbdev/maxinefb.c7
-rw-r--r--drivers/video/fbdev/mb862xx/mb862xx-i2c.c1
-rw-r--r--drivers/video/fbdev/mb862xx/mb862xxfb_accel.c4
-rw-r--r--drivers/video/fbdev/mb862xx/mb862xxfbdrv.c54
-rw-r--r--drivers/video/fbdev/metronomefb.c97
-rw-r--r--drivers/video/fbdev/mmp/fb/Kconfig4
-rw-r--r--drivers/video/fbdev/mmp/fb/mmpfb.c13
-rw-r--r--drivers/video/fbdev/mmp/hw/mmp_ctrl.c8
-rw-r--r--drivers/video/fbdev/mmp/hw/mmp_ctrl.h2
-rw-r--r--drivers/video/fbdev/mmp/hw/mmp_spi.c32
-rw-r--r--drivers/video/fbdev/mx3fb.c1704
-rw-r--r--drivers/video/fbdev/neofb.c77
-rw-r--r--drivers/video/fbdev/nvidia/nv_backlight.c12
-rw-r--r--drivers/video/fbdev/nvidia/nv_hw.c8
-rw-r--r--drivers/video/fbdev/nvidia/nv_local.h2
-rw-r--r--drivers/video/fbdev/nvidia/nvidia.c110
-rw-r--r--drivers/video/fbdev/ocfb.c10
-rw-r--r--drivers/video/fbdev/offb.c57
-rw-r--r--drivers/video/fbdev/omap/Kconfig13
-rw-r--r--drivers/video/fbdev/omap/Makefile7
-rw-r--r--drivers/video/fbdev/omap/hwa742.c2
-rw-r--r--drivers/video/fbdev/omap/lcd_ams_delta.c10
-rw-r--r--drivers/video/fbdev/omap/lcd_dma.c5
-rw-r--r--drivers/video/fbdev/omap/lcd_h3.c82
-rw-r--r--drivers/video/fbdev/omap/lcd_htcherald.c59
-rw-r--r--drivers/video/fbdev/omap/lcd_inn1510.c69
-rw-r--r--drivers/video/fbdev/omap/lcd_inn1610.c99
-rw-r--r--drivers/video/fbdev/omap/lcd_mipid.c16
-rw-r--r--drivers/video/fbdev/omap/lcd_osk.c86
-rw-r--r--drivers/video/fbdev/omap/lcd_palmtt.c65
-rw-r--r--drivers/video/fbdev/omap/lcd_palmz71.c59
-rw-r--r--drivers/video/fbdev/omap/lcdc.c4
-rw-r--r--drivers/video/fbdev/omap/omapfb_main.c112
-rw-r--r--drivers/video/fbdev/omap2/omapfb/Kconfig6
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/connector-analog-tv.c67
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/connector-dvi.c7
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/connector-hdmi.c56
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/encoder-opa362.c11
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/encoder-tfp410.c74
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/encoder-tpd12s015.c7
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-dpi.c90
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c144
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-lgphilips-lb035q02.c21
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-nec-nl8048hl11.c72
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-sharp-ls037v7dw01.c10
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c132
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td043mtea1.c75
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/apply.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/core.c11
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dispc.c62
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c7
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/display.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dpi.c9
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dsi.c42
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dss-of.c110
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dss.c29
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dss.h3
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dss_features.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/hdmi.h2
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c16
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c12
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.c19
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c7
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/manager.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/omapdss-boot-init.c5
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/output.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c3
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/overlay.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/sdi.c13
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/venc.c21
-rw-r--r--drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c1
-rw-r--r--drivers/video/fbdev/omap2/omapfb/omapfb-main.c12
-rw-r--r--drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c3
-rw-r--r--drivers/video/fbdev/omap2/omapfb/vrfb.c10
-rw-r--r--drivers/video/fbdev/p9100.c32
-rw-r--r--drivers/video/fbdev/platinumfb.c43
-rw-r--r--drivers/video/fbdev/pm2fb.c24
-rw-r--r--drivers/video/fbdev/pm3fb.c15
-rw-r--r--drivers/video/fbdev/pmag-aa-fb.c5
-rw-r--r--drivers/video/fbdev/pmag-ba-fb.c5
-rw-r--r--drivers/video/fbdev/pmagb-b-fb.c5
-rw-r--r--drivers/video/fbdev/ps3fb.c20
-rw-r--r--drivers/video/fbdev/pvr2fb.c33
-rw-r--r--drivers/video/fbdev/pxa168fb.c14
-rw-r--r--drivers/video/fbdev/pxa3xx-gcu.c19
-rw-r--r--drivers/video/fbdev/pxafb.c73
-rw-r--r--drivers/video/fbdev/q40fb.c5
-rw-r--r--drivers/video/fbdev/riva/fbdev.c97
-rw-r--r--drivers/video/fbdev/s1d13xxxfb.c30
-rw-r--r--drivers/video/fbdev/s3c-fb.c9
-rw-r--r--drivers/video/fbdev/s3c2410fb-regs-lcd.h143
-rw-r--r--drivers/video/fbdev/s3c2410fb.c1142
-rw-r--r--drivers/video/fbdev/s3c2410fb.h48
-rw-r--r--drivers/video/fbdev/s3fb.c201
-rw-r--r--drivers/video/fbdev/sa1100fb.c43
-rw-r--r--drivers/video/fbdev/savage/savagefb_driver.c25
-rw-r--r--drivers/video/fbdev/sbuslib.c10
-rw-r--r--drivers/video/fbdev/sbuslib.h39
-rw-r--r--drivers/video/fbdev/sh7760fb.c63
-rw-r--r--drivers/video/fbdev/sh_mobile_lcdcfb.c79
-rw-r--r--drivers/video/fbdev/simplefb.c170
-rw-r--r--drivers/video/fbdev/sis/init301.c3
-rw-r--r--drivers/video/fbdev/sis/sis.h2
-rw-r--r--drivers/video/fbdev/sis/sis_accel.c2
-rw-r--r--drivers/video/fbdev/sis/sis_main.c85
-rw-r--r--drivers/video/fbdev/sis/vstruct.h2
-rw-r--r--drivers/video/fbdev/skeletonfb.c217
-rw-r--r--drivers/video/fbdev/sm501fb.c30
-rw-r--r--drivers/video/fbdev/sm712fb.c33
-rw-r--r--drivers/video/fbdev/smscufx.c224
-rw-r--r--drivers/video/fbdev/ssd1307fb.c137
-rw-r--r--drivers/video/fbdev/sstfb.c62
-rw-r--r--drivers/video/fbdev/sticore.h404
-rw-r--r--drivers/video/fbdev/stifb.c284
-rw-r--r--drivers/video/fbdev/sunxvr1000.c8
-rw-r--r--drivers/video/fbdev/sunxvr2500.c15
-rw-r--r--drivers/video/fbdev/sunxvr500.c13
-rw-r--r--drivers/video/fbdev/tcx.c35
-rw-r--r--drivers/video/fbdev/tdfxfb.c25
-rw-r--r--drivers/video/fbdev/tgafb.c31
-rw-r--r--drivers/video/fbdev/tmiofb.c1040
-rw-r--r--drivers/video/fbdev/tridentfb.c30
-rw-r--r--drivers/video/fbdev/udlfb.c140
-rw-r--r--drivers/video/fbdev/uvesafb.c58
-rw-r--r--drivers/video/fbdev/valkyriefb.c21
-rw-r--r--drivers/video/fbdev/vermilion/Makefile6
-rw-r--r--drivers/video/fbdev/vermilion/cr_pll.c195
-rw-r--r--drivers/video/fbdev/vermilion/vermilion.c1162
-rw-r--r--drivers/video/fbdev/vermilion/vermilion.h245
-rw-r--r--drivers/video/fbdev/vesafb.c150
-rw-r--r--drivers/video/fbdev/vfb.c28
-rw-r--r--drivers/video/fbdev/vga16fb.c244
-rw-r--r--drivers/video/fbdev/via/accel.c4
-rw-r--r--drivers/video/fbdev/via/chip.h8
-rw-r--r--drivers/video/fbdev/via/dvi.c24
-rw-r--r--drivers/video/fbdev/via/lcd.c6
-rw-r--r--drivers/video/fbdev/via/via-core.c20
-rw-r--r--drivers/video/fbdev/via/via-gpio.c40
-rw-r--r--drivers/video/fbdev/via/via-gpio.h13
-rw-r--r--drivers/video/fbdev/via/via_aux.h2
-rw-r--r--drivers/video/fbdev/via/via_i2c.c17
-rw-r--r--drivers/video/fbdev/via/viafbdev.c7
-rw-r--r--drivers/video/fbdev/via/vt1636.c6
-rw-r--r--drivers/video/fbdev/vt8500lcdfb.c12
-rw-r--r--drivers/video/fbdev/vt8623fb.c27
-rw-r--r--drivers/video/fbdev/w100fb.c1644
-rw-r--r--drivers/video/fbdev/w100fb.h924
-rw-r--r--drivers/video/fbdev/wm8505fb.c14
-rw-r--r--drivers/video/fbdev/wmt_ge_rops.c37
-rw-r--r--drivers/video/fbdev/xen-fbfront.c86
-rw-r--r--drivers/video/fbdev/xilinxfb.c24
-rw-r--r--drivers/video/hdmi.c120
-rw-r--r--drivers/video/logo/Kconfig4
-rw-r--r--drivers/video/logo/pnmtologo.c672
-rw-r--r--drivers/video/nomodeset.c26
-rw-r--r--drivers/video/screen_info_generic.c237
-rw-r--r--drivers/video/screen_info_pci.c157
-rw-r--r--drivers/video/sticore.c (renamed from drivers/video/console/sticore.c)291
-rw-r--r--drivers/virt/Kconfig7
-rw-r--r--drivers/virt/Makefile3
-rw-r--r--drivers/virt/acrn/acrn_drv.h2
-rw-r--r--drivers/virt/acrn/hsm.c8
-rw-r--r--drivers/virt/acrn/ioeventfd.c2
-rw-r--r--drivers/virt/acrn/ioreq.c14
-rw-r--r--drivers/virt/acrn/irqfd.c19
-rw-r--r--drivers/virt/acrn/mm.c73
-rw-r--r--drivers/virt/coco/Kconfig21
-rw-r--r--drivers/virt/coco/Makefile11
-rw-r--r--drivers/virt/coco/arm-cca-guest/Kconfig10
-rw-r--r--drivers/virt/coco/arm-cca-guest/Makefile2
-rw-r--r--drivers/virt/coco/arm-cca-guest/arm-cca-guest.c232
-rw-r--r--drivers/virt/coco/efi_secret/Kconfig2
-rw-r--r--drivers/virt/coco/efi_secret/efi_secret.c50
-rw-r--r--drivers/virt/coco/guest/Kconfig17
-rw-r--r--drivers/virt/coco/guest/Makefile4
-rw-r--r--drivers/virt/coco/guest/report.c539
-rw-r--r--drivers/virt/coco/guest/tsm-mr.c251
-rw-r--r--drivers/virt/coco/pkvm-guest/Kconfig10
-rw-r--r--drivers/virt/coco/pkvm-guest/Makefile2
-rw-r--r--drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c123
-rw-r--r--drivers/virt/coco/sev-guest/Kconfig3
-rw-r--r--drivers/virt/coco/sev-guest/sev-guest.c973
-rw-r--r--drivers/virt/coco/sev-guest/sev-guest.h63
-rw-r--r--drivers/virt/coco/tdx-guest/Kconfig12
-rw-r--r--drivers/virt/coco/tdx-guest/Makefile2
-rw-r--r--drivers/virt/coco/tdx-guest/tdx-guest.c441
-rw-r--r--drivers/virt/coco/tsm-core.c163
-rw-r--r--drivers/virt/fsl_hypervisor.c4
-rw-r--r--drivers/virt/vboxguest/Kconfig3
-rw-r--r--drivers/virt/vboxguest/vboxguest_core.c160
-rw-r--r--drivers/virt/vboxguest/vboxguest_linux.c38
-rw-r--r--drivers/virt/vboxguest/vboxguest_utils.c33
-rw-r--r--drivers/virt/vmgenid.c150
-rw-r--r--drivers/virtio/Kconfig91
-rw-r--r--drivers/virtio/Makefile7
-rw-r--r--drivers/virtio/virtio.c279
-rw-r--r--drivers/virtio/virtio_balloon.c234
-rw-r--r--drivers/virtio/virtio_debug.c117
-rw-r--r--drivers/virtio/virtio_dma_buf.c5
-rw-r--r--drivers/virtio/virtio_input.c14
-rw-r--r--drivers/virtio/virtio_mem.c388
-rw-r--r--drivers/virtio/virtio_mmio.c111
-rw-r--r--drivers/virtio/virtio_pci_admin_legacy_io.c244
-rw-r--r--drivers/virtio/virtio_pci_common.c338
-rw-r--r--drivers/virtio/virtio_pci_common.h76
-rw-r--r--drivers/virtio/virtio_pci_legacy.c1
-rw-r--r--drivers/virtio/virtio_pci_legacy_dev.c4
-rw-r--r--drivers/virtio/virtio_pci_modern.c779
-rw-r--r--drivers/virtio/virtio_pci_modern_dev.c139
-rw-r--r--drivers/virtio/virtio_ring.c1245
-rw-r--r--drivers/virtio/virtio_rtc_arm.c23
-rw-r--r--drivers/virtio/virtio_rtc_class.c262
-rw-r--r--drivers/virtio/virtio_rtc_driver.c1407
-rw-r--r--drivers/virtio/virtio_rtc_internal.h122
-rw-r--r--drivers/virtio/virtio_rtc_ptp.c347
-rw-r--r--drivers/virtio/virtio_vdpa.c214
-rw-r--r--drivers/vlynq/Kconfig21
-rw-r--r--drivers/vlynq/Makefile6
-rw-r--r--drivers/vlynq/vlynq.c799
-rw-r--r--drivers/w1/masters/Kconfig30
-rw-r--r--drivers/w1/masters/Makefile3
-rw-r--r--drivers/w1/masters/amd_axi_w1.c396
-rw-r--r--drivers/w1/masters/ds1wm.c675
-rw-r--r--drivers/w1/masters/ds2482.c43
-rw-r--r--drivers/w1/masters/ds2490.c38
-rw-r--r--drivers/w1/masters/matrox_w1.c26
-rw-r--r--drivers/w1/masters/mxc_w1.c4
-rw-r--r--drivers/w1/masters/omap_hdq.c23
-rw-r--r--drivers/w1/masters/sgi_w1.c6
-rw-r--r--drivers/w1/masters/w1-gpio.c168
-rw-r--r--drivers/w1/masters/w1-uart.c415
-rw-r--r--drivers/w1/slaves/Kconfig4
-rw-r--r--drivers/w1/slaves/w1_ds2406.c37
-rw-r--r--drivers/w1/slaves/w1_ds2408.c52
-rw-r--r--drivers/w1/slaves/w1_ds2413.c18
-rw-r--r--drivers/w1/slaves/w1_ds2430.c8
-rw-r--r--drivers/w1/slaves/w1_ds2431.c8
-rw-r--r--drivers/w1/slaves/w1_ds2433.c187
-rw-r--r--drivers/w1/slaves/w1_ds2438.c34
-rw-r--r--drivers/w1/slaves/w1_ds250x.c1
-rw-r--r--drivers/w1/slaves/w1_ds2780.c7
-rw-r--r--drivers/w1/slaves/w1_ds2781.c7
-rw-r--r--drivers/w1/slaves/w1_ds2805.c8
-rw-r--r--drivers/w1/slaves/w1_ds28e04.c37
-rw-r--r--drivers/w1/slaves/w1_ds28e17.c14
-rw-r--r--drivers/w1/slaves/w1_therm.c49
-rw-r--r--drivers/w1/w1.c101
-rw-r--r--drivers/w1/w1_int.c11
-rw-r--r--drivers/w1/w1_netlink.c51
-rw-r--r--drivers/watchdog/Kconfig317
-rw-r--r--drivers/watchdog/Makefile20
-rw-r--r--drivers/watchdog/acquirewdt.c5
-rw-r--r--drivers/watchdog/advantech_ec_wdt.c205
-rw-r--r--drivers/watchdog/advantechwdt.c5
-rw-r--r--drivers/watchdog/airoha_wdt.c216
-rw-r--r--drivers/watchdog/alim1535_wdt.c1
-rw-r--r--drivers/watchdog/alim7101_wdt.c5
-rw-r--r--drivers/watchdog/apple_wdt.c58
-rw-r--r--drivers/watchdog/ar7_wdt.c316
-rw-r--r--drivers/watchdog/arm_smc_wdt.c17
-rw-r--r--drivers/watchdog/armada_37xx_wdt.c28
-rw-r--r--drivers/watchdog/aspeed_wdt.c231
-rw-r--r--drivers/watchdog/at91rm9200_wdt.c19
-rw-r--r--drivers/watchdog/at91sam9_wdt.c45
-rw-r--r--drivers/watchdog/ath79_wdt.c23
-rw-r--r--drivers/watchdog/bcm2835_wdt.c7
-rw-r--r--drivers/watchdog/bcm47xx_wdt.c18
-rw-r--r--drivers/watchdog/bcm7038_wdt.c15
-rw-r--r--drivers/watchdog/bcm_kona_wdt.c4
-rw-r--r--drivers/watchdog/bd9576_wdt.c59
-rw-r--r--drivers/watchdog/bd96801_wdt.c417
-rw-r--r--drivers/watchdog/booke_wdt.c8
-rw-r--r--drivers/watchdog/cadence_wdt.c17
-rw-r--r--drivers/watchdog/cgbc_wdt.c211
-rw-r--r--drivers/watchdog/cpu5wdt.c285
-rw-r--r--drivers/watchdog/cpwd.c11
-rw-r--r--drivers/watchdog/cros_ec_wdt.c200
-rw-r--r--drivers/watchdog/da9052_wdt.c40
-rw-r--r--drivers/watchdog/da9055_wdt.c7
-rw-r--r--drivers/watchdog/da9062_wdt.c15
-rw-r--r--drivers/watchdog/da9063_wdt.c34
-rw-r--r--drivers/watchdog/davinci_wdt.c18
-rw-r--r--drivers/watchdog/db8500_wdt.c9
-rw-r--r--drivers/watchdog/diag288_wdt.c192
-rw-r--r--drivers/watchdog/dw_wdt.c56
-rw-r--r--drivers/watchdog/ep93xx_wdt.c5
-rw-r--r--drivers/watchdog/eurotechwdt.c3
-rw-r--r--drivers/watchdog/exar_wdt.c429
-rw-r--r--drivers/watchdog/ftwdt010_wdt.c31
-rw-r--r--drivers/watchdog/gef_wdt.c7
-rw-r--r--drivers/watchdog/geodewdt.c4
-rw-r--r--drivers/watchdog/gpio_wdt.c16
-rw-r--r--drivers/watchdog/gxp-wdt.c4
-rw-r--r--drivers/watchdog/hpwdt.c36
-rw-r--r--drivers/watchdog/iTCO_wdt.c85
-rw-r--r--drivers/watchdog/ib700wdt.c4
-rw-r--r--drivers/watchdog/ibmasr.c4
-rw-r--r--drivers/watchdog/ie6xx_wdt.c4
-rw-r--r--drivers/watchdog/imgpdc_wdt.c31
-rw-r--r--drivers/watchdog/imx2_wdt.c68
-rw-r--r--drivers/watchdog/imx7ulp_wdt.c230
-rw-r--r--drivers/watchdog/imx_sc_wdt.c51
-rw-r--r--drivers/watchdog/indydog.c1
-rw-r--r--drivers/watchdog/intel-mid_wdt.c15
-rw-r--r--drivers/watchdog/intel_oc_wdt.c233
-rw-r--r--drivers/watchdog/it8712f_wdt.c1
-rw-r--r--drivers/watchdog/it87_wdt.c82
-rw-r--r--drivers/watchdog/ixp4xx_wdt.c46
-rw-r--r--drivers/watchdog/kempld_wdt.c11
-rw-r--r--drivers/watchdog/lantiq_wdt.c3
-rw-r--r--drivers/watchdog/lenovo_se10_wdt.c308
-rw-r--r--drivers/watchdog/lenovo_se30_wdt.c396
-rw-r--r--drivers/watchdog/loongson1_wdt.c115
-rw-r--r--drivers/watchdog/lpc18xx_wdt.c41
-rw-r--r--drivers/watchdog/m54xx_wdt.c5
-rw-r--r--drivers/watchdog/machzwd.c5
-rw-r--r--drivers/watchdog/marvell_gti_wdt.c348
-rw-r--r--drivers/watchdog/max63xx_wdt.c5
-rw-r--r--drivers/watchdog/max77620_wdt.c1
-rw-r--r--drivers/watchdog/menz69_wdt.c22
-rw-r--r--drivers/watchdog/meson_gxbb_wdt.c57
-rw-r--r--drivers/watchdog/meson_wdt.c4
-rw-r--r--drivers/watchdog/mixcomwd.c5
-rw-r--r--drivers/watchdog/mlx_wdt.c5
-rw-r--r--drivers/watchdog/moxart_wdt.c4
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c6
-rw-r--r--drivers/watchdog/mt7621_wdt.c122
-rw-r--r--drivers/watchdog/mtk_wdt.c74
-rw-r--r--drivers/watchdog/mtx-1_wdt.c5
-rw-r--r--drivers/watchdog/nct6694_wdt.c307
-rw-r--r--drivers/watchdog/ni903x_wdt.c4
-rw-r--r--drivers/watchdog/nic7018_wdt.c13
-rw-r--r--drivers/watchdog/npcm_wdt.c13
-rw-r--r--drivers/watchdog/nv_tco.c5
-rw-r--r--drivers/watchdog/octeon-wdt-main.c10
-rw-r--r--drivers/watchdog/octeon-wdt-nmi.S5
-rw-r--r--drivers/watchdog/of_xilinx_wdt.c30
-rw-r--r--drivers/watchdog/omap_wdt.c16
-rw-r--r--drivers/watchdog/orion_wdt.c7
-rw-r--r--drivers/watchdog/pc87413_wdt.c1
-rw-r--r--drivers/watchdog/pcwd.c6
-rw-r--r--drivers/watchdog/pcwd_pci.c2
-rw-r--r--drivers/watchdog/pcwd_usb.c14
-rw-r--r--drivers/watchdog/pic32-dmt.c16
-rw-r--r--drivers/watchdog/pic32-wdt.c18
-rw-r--r--drivers/watchdog/pika_wdt.c5
-rw-r--r--drivers/watchdog/pm8916_wdt.c4
-rw-r--r--drivers/watchdog/pnx4008_wdt.c15
-rw-r--r--drivers/watchdog/pretimeout_noop.c2
-rw-r--r--drivers/watchdog/pretimeout_panic.c2
-rw-r--r--drivers/watchdog/qcom-wdt.c31
-rw-r--r--drivers/watchdog/rave-sp-wdt.c2
-rw-r--r--drivers/watchdog/rc32434_wdt.c4
-rw-r--r--drivers/watchdog/rdc321x_wdt.c5
-rw-r--r--drivers/watchdog/realtek_otto_wdt.c17
-rw-r--r--drivers/watchdog/renesas_wdt.c12
-rw-r--r--drivers/watchdog/renesas_wwdt.c163
-rw-r--r--drivers/watchdog/riowd.c7
-rw-r--r--drivers/watchdog/rn5t618_wdt.c24
-rw-r--r--drivers/watchdog/rt2880_wdt.c89
-rw-r--r--drivers/watchdog/rtd119x_wdt.c18
-rw-r--r--drivers/watchdog/rti_wdt.c105
-rw-r--r--drivers/watchdog/rza_wdt.c11
-rw-r--r--drivers/watchdog/rzg2l_wdt.c145
-rw-r--r--drivers/watchdog/rzn1_wdt.c26
-rw-r--r--drivers/watchdog/rzv2h_wdt.c385
-rw-r--r--drivers/watchdog/s32g_wdt.c315
-rw-r--r--drivers/watchdog/s3c2410_wdt.c448
-rw-r--r--drivers/watchdog/sa1100_wdt.c16
-rw-r--r--drivers/watchdog/sama5d4_wdt.c8
-rw-r--r--drivers/watchdog/sb_wdog.c1
-rw-r--r--drivers/watchdog/sbc60xxwdt.c5
-rw-r--r--drivers/watchdog/sbc7240_wdt.c1
-rw-r--r--drivers/watchdog/sbc8360.c1
-rw-r--r--drivers/watchdog/sbc_epx_c3.c1
-rw-r--r--drivers/watchdog/sbc_fitpc2_wdt.c5
-rw-r--r--drivers/watchdog/sbsa_gwdt.c62
-rw-r--r--drivers/watchdog/sc1200wdt.c1
-rw-r--r--drivers/watchdog/sc520_wdt.c3
-rw-r--r--drivers/watchdog/sch311x_wdt.c4
-rw-r--r--drivers/watchdog/scx200_wdt.c1
-rw-r--r--drivers/watchdog/shwdt.c8
-rw-r--r--drivers/watchdog/simatic-ipc-wdt.c10
-rw-r--r--drivers/watchdog/sl28cpld_wdt.c4
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c3
-rw-r--r--drivers/watchdog/softdog.c8
-rw-r--r--drivers/watchdog/sp5100_tco.c21
-rw-r--r--drivers/watchdog/sp805_wdt.c13
-rw-r--r--drivers/watchdog/st_lpc_wdt.c15
-rw-r--r--drivers/watchdog/starfive-wdt.c615
-rw-r--r--drivers/watchdog/stm32_iwdg.c101
-rw-r--r--drivers/watchdog/stmp3xxx_rtc_wdt.c3
-rw-r--r--drivers/watchdog/sunplus_wdt.c17
-rw-r--r--drivers/watchdog/sunxi_wdt.c12
-rw-r--r--drivers/watchdog/ts4800_wdt.c5
-rw-r--r--drivers/watchdog/ts72xx_wdt.c12
-rw-r--r--drivers/watchdog/twl4030_wdt.c11
-rw-r--r--drivers/watchdog/txx9wdt.c11
-rw-r--r--drivers/watchdog/via_wdt.c3
-rw-r--r--drivers/watchdog/visconti_wdt.c22
-rw-r--r--drivers/watchdog/w83627hf_wdt.c12
-rw-r--r--drivers/watchdog/w83877f_wdt.c5
-rw-r--r--drivers/watchdog/w83977f_wdt.c3
-rw-r--r--drivers/watchdog/wafer5823wdt.c1
-rw-r--r--drivers/watchdog/watchdog_core.c57
-rw-r--r--drivers/watchdog/watchdog_core.h8
-rw-r--r--drivers/watchdog/watchdog_dev.c56
-rw-r--r--drivers/watchdog/watchdog_hrtimer_pretimeout.c4
-rw-r--r--drivers/watchdog/watchdog_pretimeout.c5
-rw-r--r--drivers/watchdog/wdat_wdt.c77
-rw-r--r--drivers/watchdog/wdrtas.c2
-rw-r--r--drivers/watchdog/wdt.c2
-rw-r--r--drivers/watchdog/wdt285.c3
-rw-r--r--drivers/watchdog/wdt977.c1
-rw-r--r--drivers/watchdog/wdt_pci.c4
-rw-r--r--drivers/watchdog/wm8350_wdt.c9
-rw-r--r--drivers/watchdog/xilinx_wwdt.c253
-rw-r--r--drivers/watchdog/ziirave_wdt.c14
-rw-r--r--drivers/xen/Kconfig16
-rw-r--r--drivers/xen/acpi.c74
-rw-r--r--drivers/xen/balloon.c79
-rw-r--r--drivers/xen/efi.c61
-rw-r--r--drivers/xen/events/events_2l.c8
-rw-r--r--drivers/xen/events/events_base.c759
-rw-r--r--drivers/xen/events/events_fifo.c26
-rw-r--r--drivers/xen/events/events_internal.h1
-rw-r--r--drivers/xen/evtchn.c45
-rw-r--r--drivers/xen/gntalloc.c4
-rw-r--r--drivers/xen/gntdev-common.h7
-rw-r--r--drivers/xen/gntdev-dmabuf.c95
-rw-r--r--drivers/xen/gntdev-dmabuf.h2
-rw-r--r--drivers/xen/gntdev.c189
-rw-r--r--drivers/xen/grant-dma-iommu.c15
-rw-r--r--drivers/xen/grant-dma-ops.c194
-rw-r--r--drivers/xen/grant-table.c60
-rw-r--r--drivers/xen/manage.c22
-rw-r--r--drivers/xen/mcelog.c1
-rw-r--r--drivers/xen/pci.c59
-rw-r--r--drivers/xen/pcpu.c48
-rw-r--r--drivers/xen/platform-pci.c19
-rw-r--r--drivers/xen/privcmd-buf.c3
-rw-r--r--drivers/xen/privcmd.c728
-rw-r--r--drivers/xen/pvcalls-back.c44
-rw-r--r--drivers/xen/pvcalls-front.c67
-rw-r--r--drivers/xen/pvcalls-front.h2
-rw-r--r--drivers/xen/swiotlb-xen.c102
-rw-r--r--drivers/xen/sys-hypervisor.c71
-rw-r--r--drivers/xen/time.c8
-rw-r--r--drivers/xen/unpopulated-alloc.c4
-rw-r--r--drivers/xen/xen-acpi-pad.c3
-rw-r--r--drivers/xen/xen-acpi-processor.c19
-rw-r--r--drivers/xen/xen-balloon.c2
-rw-r--r--drivers/xen/xen-front-pgdir-shbuf.c36
-rw-r--r--drivers/xen/xen-pciback/conf_space.c19
-rw-r--r--drivers/xen/xen-pciback/conf_space_capability.c19
-rw-r--r--drivers/xen/xen-pciback/conf_space_header.c21
-rw-r--r--drivers/xen/xen-pciback/conf_space_quirks.h2
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c122
-rw-r--r--drivers/xen/xen-pciback/pciback.h5
-rw-r--r--drivers/xen/xen-pciback/xenbus.c6
-rw-r--r--drivers/xen/xen-scsiback.c76
-rw-r--r--drivers/xen/xenbus/Makefile14
-rw-r--r--drivers/xen/xenbus/xenbus.h4
-rw-r--r--drivers/xen/xenbus/xenbus_client.c79
-rw-r--r--drivers/xen/xenbus/xenbus_comms.c9
-rw-r--r--drivers/xen/xenbus/xenbus_dev_frontend.c7
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c67
-rw-r--r--drivers/xen/xenbus/xenbus_probe_backend.c25
-rw-r--r--drivers/xen/xenbus/xenbus_probe_frontend.c7
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c88
-rw-r--r--drivers/xen/xenfs/super.c2
-rw-r--r--drivers/xen/xenfs/xensyms.c14
-rw-r--r--drivers/zorro/names.c13
-rw-r--r--drivers/zorro/zorro-driver.c10
-rw-r--r--drivers/zorro/zorro-sysfs.c6
-rw-r--r--drivers/zorro/zorro.c17
-rw-r--r--drivers/zorro/zorro.h2
30305 files changed, 5712142 insertions, 2011130 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 19ee995bd0ae..c0f1fb893ec0 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -10,15 +10,25 @@ source "drivers/cxl/Kconfig"
source "drivers/pcmcia/Kconfig"
source "drivers/rapidio/Kconfig"
+config PC104
+ bool "PC/104 support" if EXPERT
+ help
+ Expose PC/104 form factor device drivers and options available for
+ selection and configuration. Enable this option if your target
+ machine has a PC/104 bus.
source "drivers/base/Kconfig"
source "drivers/bus/Kconfig"
+source "drivers/cache/Kconfig"
+
source "drivers/connector/Kconfig"
source "drivers/firmware/Kconfig"
+source "drivers/fwctl/Kconfig"
+
source "drivers/gnss/Kconfig"
source "drivers/mtd/Kconfig"
@@ -73,6 +83,8 @@ source "drivers/pps/Kconfig"
source "drivers/ptp/Kconfig"
+source "drivers/dpll/Kconfig"
+
source "drivers/pinctrl/Kconfig"
source "drivers/gpio/Kconfig"
@@ -99,6 +111,8 @@ source "drivers/media/Kconfig"
source "drivers/video/Kconfig"
+source "drivers/accel/Kconfig"
+
source "sound/Kconfig"
source "drivers/hid/Kconfig"
@@ -127,14 +141,10 @@ source "drivers/dma-buf/Kconfig"
source "drivers/dca/Kconfig"
-source "drivers/auxdisplay/Kconfig"
-
source "drivers/uio/Kconfig"
source "drivers/vfio/Kconfig"
-source "drivers/vlynq/Kconfig"
-
source "drivers/virt/Kconfig"
source "drivers/virtio/Kconfig"
@@ -151,6 +161,8 @@ source "drivers/greybus/Kconfig"
source "drivers/comedi/Kconfig"
+source "drivers/gpib/Kconfig"
+
source "drivers/staging/Kconfig"
source "drivers/platform/Kconfig"
@@ -173,6 +185,8 @@ source "drivers/soundwire/Kconfig"
source "drivers/soc/Kconfig"
+source "drivers/pmdomain/Kconfig"
+
source "drivers/devfreq/Kconfig"
source "drivers/extcon/Kconfig"
@@ -205,8 +219,6 @@ source "drivers/thunderbolt/Kconfig"
source "drivers/android/Kconfig"
-source "drivers/gpu/trace/Kconfig"
-
source "drivers/nvdimm/Kconfig"
source "drivers/dax/Kconfig"
@@ -239,4 +251,8 @@ source "drivers/peci/Kconfig"
source "drivers/hte/Kconfig"
+source "drivers/cdx/Kconfig"
+
+source "drivers/resctrl/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 057857258bfd..ccc05f1eae3e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -6,6 +6,7 @@
# Rewritten to use lists instead of if-statements.
#
+obj-y += cache/
obj-y += irqchip/
obj-y += bus/
@@ -16,6 +17,9 @@ obj-$(CONFIG_PINCTRL) += pinctrl/
obj-$(CONFIG_GPIOLIB) += gpio/
obj-y += pwm/
+# LEDs must come before PCI, it is needed by NPEM driver
+obj-y += leds/
+
obj-y += pci/
obj-$(CONFIG_PARISC) += parisc/
@@ -40,6 +44,7 @@ obj-$(CONFIG_DMADEVICES) += dma/
# SOC specific infrastructure drivers.
obj-y += soc/
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += pmdomain/
obj-y += virtio/
obj-$(CONFIG_VDPA) += vdpa/
@@ -64,14 +69,13 @@ obj-y += gpu/
obj-$(CONFIG_CONNECTOR) += connector/
-# i810fb and intelfb depend on char/agp/
+# i810fb depends on char/agp/
obj-$(CONFIG_FB_I810) += video/fbdev/i810/
-obj-$(CONFIG_FB_INTEL) += video/fbdev/intelfb/
obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ block/ misc/ mfd/ nfc/
obj-$(CONFIG_LIBNVDIMM) += nvdimm/
-obj-$(CONFIG_DAX) += dax/
+obj-y += dax/
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
obj-$(CONFIG_NUBUS) += nubus/
obj-y += cxl/
@@ -98,7 +102,6 @@ obj-$(CONFIG_DIO) += dio/
obj-$(CONFIG_SBUS) += sbus/
obj-$(CONFIG_ZORRO) += zorro/
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
-obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_USB_PHY) += usb/
obj-$(CONFIG_USB) += usb/
@@ -130,14 +133,14 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-y += mmc/
obj-y += ufs/
obj-$(CONFIG_MEMSTICK) += memstick/
-obj-$(CONFIG_NEW_LEDS) += leds/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-y += firmware/
+obj-$(CONFIG_FWCTL) += fwctl/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
obj-y += clocksource/
obj-$(CONFIG_DCA) += dca/
-obj-$(CONFIG_HID) += hid/
+obj-$(CONFIG_HID_SUPPORT) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_OF) += of/
obj-$(CONFIG_SSB) += ssb/
@@ -145,9 +148,9 @@ obj-$(CONFIG_BCMA) += bcma/
obj-$(CONFIG_VHOST_RING) += vhost/
obj-$(CONFIG_VHOST_IOTLB) += vhost/
obj-$(CONFIG_VHOST) += vhost/
-obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_COMEDI) += comedi/
+obj-$(CONFIG_GPIB) += gpib/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
@@ -158,8 +161,8 @@ obj-$(CONFIG_RPMSG) += rpmsg/
obj-$(CONFIG_SOUNDWIRE) += soundwire/
# Virtualization drivers
-obj-$(CONFIG_VIRT_DRIVERS) += virt/
-obj-$(subst m,y,$(CONFIG_HYPERV)) += hv/
+obj-y += virt/
+obj-$(CONFIG_HYPERV) += hv/
obj-$(CONFIG_PM_DEVFREQ) += devfreq/
obj-$(CONFIG_EXTCON) += extcon/
@@ -175,6 +178,7 @@ obj-$(CONFIG_USB4) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
obj-y += hwtracing/intel_th/
obj-$(CONFIG_STM) += hwtracing/stm/
+obj-$(CONFIG_HISI_PTT) += hwtracing/ptt/
obj-y += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
@@ -188,3 +192,10 @@ obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_PECI) += peci/
obj-$(CONFIG_HTE) += hte/
+obj-$(CONFIG_DRM_ACCEL) += accel/
+obj-$(CONFIG_CDX_BUS) += cdx/
+obj-$(CONFIG_DPLL) += dpll/
+obj-y += resctrl/
+
+obj-$(CONFIG_DIBS) += dibs/
+obj-$(CONFIG_S390) += s390/
diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig
new file mode 100644
index 000000000000..bdf48ccafcf2
--- /dev/null
+++ b/drivers/accel/Kconfig
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Compute Acceleration device configuration
+#
+# This framework provides support for compute acceleration devices, such
+# as, but not limited to, Machine-Learning and Deep-Learning acceleration
+# devices
+#
+if DRM
+
+menuconfig DRM_ACCEL
+ bool "Compute Acceleration Framework"
+ help
+ Framework for device drivers of compute acceleration devices, such
+ as, but not limited to, Machine-Learning and Deep-Learning
+ acceleration devices.
+ If you say Y here, you need to select the module that's right for
+ your acceleration device from the list below.
+ This framework is integrated with the DRM subsystem as compute
+ accelerators and GPUs share a lot in common and can use almost the
+ same infrastructure code.
+ Having said that, acceleration devices will have a different
+ major number than GPUs, and will be exposed to user-space using
+ different device files, called accel/accel* (in /dev, sysfs
+ and debugfs).
+
+source "drivers/accel/amdxdna/Kconfig"
+source "drivers/accel/ethosu/Kconfig"
+source "drivers/accel/habanalabs/Kconfig"
+source "drivers/accel/ivpu/Kconfig"
+source "drivers/accel/qaic/Kconfig"
+source "drivers/accel/rocket/Kconfig"
+
+endif
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile
new file mode 100644
index 000000000000..1d3a7251b950
--- /dev/null
+++ b/drivers/accel/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/
+obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/
+obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/
+obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/
+obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/
+obj-$(CONFIG_DRM_ACCEL_ROCKET) += rocket/ \ No newline at end of file
diff --git a/drivers/accel/amdxdna/Kconfig b/drivers/accel/amdxdna/Kconfig
new file mode 100644
index 000000000000..f39d7a87296c
--- /dev/null
+++ b/drivers/accel/amdxdna/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_AMDXDNA
+ tristate "AMD AI Engine"
+ depends on AMD_IOMMU
+ depends on DRM_ACCEL
+ depends on PCI && HAS_IOMEM
+ depends on X86_64
+ select DRM_SCHED
+ select DRM_GEM_SHMEM_HELPER
+ select FW_LOADER
+ select HMM_MIRROR
+ help
+ Choose this option to enable support for NPU integrated into AMD
+ client CPUs like AMD Ryzen AI 300 Series. AMD NPU can be used to
+ accelerate machine learning applications.
+
+ If "M" is selected, the driver module will be amdxdna.
diff --git a/drivers/accel/amdxdna/Makefile b/drivers/accel/amdxdna/Makefile
new file mode 100644
index 000000000000..6344aaf523fa
--- /dev/null
+++ b/drivers/accel/amdxdna/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+amdxdna-y := \
+ aie2_ctx.o \
+ aie2_error.o \
+ aie2_message.o \
+ aie2_pci.o \
+ aie2_pm.o \
+ aie2_psp.o \
+ aie2_smu.o \
+ aie2_solver.o \
+ amdxdna_ctx.o \
+ amdxdna_gem.o \
+ amdxdna_mailbox.o \
+ amdxdna_mailbox_helper.o \
+ amdxdna_pci_drv.o \
+ amdxdna_pm.o \
+ amdxdna_sysfs.o \
+ amdxdna_ubuf.o \
+ npu1_regs.o \
+ npu2_regs.o \
+ npu4_regs.o \
+ npu5_regs.o \
+ npu6_regs.o
+obj-$(CONFIG_DRM_ACCEL_AMDXDNA) = amdxdna.o
diff --git a/drivers/accel/amdxdna/TODO b/drivers/accel/amdxdna/TODO
new file mode 100644
index 000000000000..0e4bbebeaedf
--- /dev/null
+++ b/drivers/accel/amdxdna/TODO
@@ -0,0 +1 @@
+- Add debugfs support
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
new file mode 100644
index 000000000000..42d876a427c5
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -0,0 +1,1079 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_syncobj.h>
+#include <linux/hmm.h>
+#include <linux/types.h>
+#include <linux/xarray.h>
+#include <trace/events/amdxdna.h>
+
+#include "aie2_msg_priv.h"
+#include "aie2_pci.h"
+#include "aie2_solver.h"
+#include "amdxdna_ctx.h"
+#include "amdxdna_gem.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
+
+static bool force_cmdlist;
+module_param(force_cmdlist, bool, 0600);
+MODULE_PARM_DESC(force_cmdlist, "Force use command list (Default false)");
+
+#define HWCTX_MAX_TIMEOUT 60000 /* milliseconds */
+
+static void aie2_job_release(struct kref *ref)
+{
+ struct amdxdna_sched_job *job;
+
+ job = container_of(ref, struct amdxdna_sched_job, refcnt);
+ amdxdna_sched_job_cleanup(job);
+ atomic64_inc(&job->hwctx->job_free_cnt);
+ wake_up(&job->hwctx->priv->job_free_wq);
+ if (job->out_fence)
+ dma_fence_put(job->out_fence);
+ kfree(job);
+}
+
+static void aie2_job_put(struct amdxdna_sched_job *job)
+{
+ kref_put(&job->refcnt, aie2_job_release);
+}
+
+static void aie2_hwctx_status_shift_stop(struct amdxdna_hwctx *hwctx)
+{
+ hwctx->old_status = hwctx->status;
+ hwctx->status = HWCTX_STAT_STOP;
+}
+
+static void aie2_hwctx_status_restore(struct amdxdna_hwctx *hwctx)
+{
+ hwctx->status = hwctx->old_status;
+}
+
+/* The bad_job is used in aie2_sched_job_timedout, otherwise, set it to NULL */
+static void aie2_hwctx_stop(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx,
+ struct drm_sched_job *bad_job)
+{
+ drm_sched_stop(&hwctx->priv->sched, bad_job);
+ aie2_destroy_context(xdna->dev_handle, hwctx);
+}
+
+static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_gem_obj *heap = hwctx->priv->heap;
+ int ret;
+
+ ret = aie2_create_context(xdna->dev_handle, hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Create hwctx failed, ret %d", ret);
+ goto out;
+ }
+
+ ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
+ heap->mem.userptr, heap->mem.size);
+ if (ret) {
+ XDNA_ERR(xdna, "Map host buf failed, ret %d", ret);
+ goto out;
+ }
+
+ if (hwctx->status != HWCTX_STAT_READY) {
+ XDNA_DBG(xdna, "hwctx is not ready, status %d", hwctx->status);
+ goto out;
+ }
+
+ ret = aie2_config_cu(hwctx, NULL);
+ if (ret) {
+ XDNA_ERR(xdna, "Config cu failed, ret %d", ret);
+ goto out;
+ }
+
+out:
+ drm_sched_start(&hwctx->priv->sched, 0);
+ XDNA_DBG(xdna, "%s restarted, ret %d", hwctx->name, ret);
+ return ret;
+}
+
+static struct dma_fence *aie2_cmd_get_out_fence(struct amdxdna_hwctx *hwctx, u64 seq)
+{
+ struct dma_fence *fence, *out_fence = NULL;
+ int ret;
+
+ fence = drm_syncobj_fence_get(hwctx->priv->syncobj);
+ if (!fence)
+ return NULL;
+
+ ret = dma_fence_chain_find_seqno(&fence, seq);
+ if (ret)
+ goto out;
+
+ out_fence = dma_fence_get(dma_fence_chain_contained(fence));
+
+out:
+ dma_fence_put(fence);
+ return out_fence;
+}
+
+static void aie2_hwctx_wait_for_idle(struct amdxdna_hwctx *hwctx)
+{
+ struct dma_fence *fence;
+
+ fence = aie2_cmd_get_out_fence(hwctx, hwctx->priv->seq - 1);
+ if (!fence)
+ return;
+
+ /* Wait up to 2 seconds for fw to finish all pending requests */
+ dma_fence_wait_timeout(fence, false, msecs_to_jiffies(2000));
+ dma_fence_put(fence);
+}
+
+static int aie2_hwctx_suspend_cb(struct amdxdna_hwctx *hwctx, void *arg)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+
+ aie2_hwctx_wait_for_idle(hwctx);
+ aie2_hwctx_stop(xdna, hwctx, NULL);
+ aie2_hwctx_status_shift_stop(hwctx);
+
+ return 0;
+}
+
+void aie2_hwctx_suspend(struct amdxdna_client *client)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+
+ /*
+ * Command timeout is unlikely. But if it happens, it doesn't
+ * break the system. aie2_hwctx_stop() will destroy mailbox
+ * and abort all commands.
+ */
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+ amdxdna_hwctx_walk(client, NULL, aie2_hwctx_suspend_cb);
+}
+
+static int aie2_hwctx_resume_cb(struct amdxdna_hwctx *hwctx, void *arg)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+
+ aie2_hwctx_status_restore(hwctx);
+ return aie2_hwctx_restart(xdna, hwctx);
+}
+
+int aie2_hwctx_resume(struct amdxdna_client *client)
+{
+ /*
+ * The resume path cannot guarantee that mailbox channel can be
+ * regenerated. If this happen, when submit message to this
+ * mailbox channel, error will return.
+ */
+ return amdxdna_hwctx_walk(client, NULL, aie2_hwctx_resume_cb);
+}
+
+static void
+aie2_sched_notify(struct amdxdna_sched_job *job)
+{
+ struct dma_fence *fence = job->fence;
+
+ trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
+
+ amdxdna_pm_suspend_put(job->hwctx->client->xdna);
+ job->hwctx->priv->completed++;
+ dma_fence_signal(fence);
+
+ up(&job->hwctx->priv->job_sem);
+ job->job_done = true;
+ mmput_async(job->mm);
+ aie2_job_put(job);
+}
+
+static int
+aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size)
+{
+ struct amdxdna_sched_job *job = handle;
+ struct amdxdna_gem_obj *cmd_abo;
+ int ret = 0;
+ u32 status;
+
+ cmd_abo = job->cmd_bo;
+
+ if (unlikely(job->job_timeout)) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_TIMEOUT);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (unlikely(!data) || unlikely(size != sizeof(u32))) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ status = readl(data);
+ XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
+ if (status == AIE2_STATUS_SUCCESS)
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_COMPLETED);
+ else
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ERROR);
+
+out:
+ aie2_sched_notify(job);
+ return ret;
+}
+
+static int
+aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size)
+{
+ struct amdxdna_sched_job *job = handle;
+ int ret = 0;
+
+ if (unlikely(!data))
+ goto out;
+
+ if (unlikely(size != sizeof(u32))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ job->drv_cmd->result = readl(data);
+
+out:
+ aie2_sched_notify(job);
+ return ret;
+}
+
+static int
+aie2_sched_cmdlist_resp_handler(void *handle, void __iomem *data, size_t size)
+{
+ struct amdxdna_sched_job *job = handle;
+ struct amdxdna_gem_obj *cmd_abo;
+ struct amdxdna_dev *xdna;
+ u32 fail_cmd_status;
+ u32 fail_cmd_idx;
+ u32 cmd_status;
+ int ret = 0;
+
+ cmd_abo = job->cmd_bo;
+
+ if (unlikely(job->job_timeout)) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_TIMEOUT);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (unlikely(!data) || unlikely(size != sizeof(u32) * 3)) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd_status = readl(data + offsetof(struct cmd_chain_resp, status));
+ xdna = job->hwctx->client->xdna;
+ XDNA_DBG(xdna, "Status 0x%x", cmd_status);
+ if (cmd_status == AIE2_STATUS_SUCCESS) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_COMPLETED);
+ goto out;
+ }
+
+ /* Slow path to handle error, read from ringbuf on BAR */
+ fail_cmd_idx = readl(data + offsetof(struct cmd_chain_resp, fail_cmd_idx));
+ fail_cmd_status = readl(data + offsetof(struct cmd_chain_resp, fail_cmd_status));
+ XDNA_DBG(xdna, "Failed cmd idx %d, status 0x%x",
+ fail_cmd_idx, fail_cmd_status);
+
+ if (fail_cmd_status == AIE2_STATUS_SUCCESS) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
+ ret = -EINVAL;
+ goto out;
+ }
+ amdxdna_cmd_set_state(cmd_abo, fail_cmd_status);
+
+ if (amdxdna_cmd_get_op(cmd_abo) == ERT_CMD_CHAIN) {
+ struct amdxdna_cmd_chain *cc = amdxdna_cmd_get_payload(cmd_abo, NULL);
+
+ cc->error_index = fail_cmd_idx;
+ if (cc->error_index >= cc->command_count)
+ cc->error_index = 0;
+ }
+out:
+ aie2_sched_notify(job);
+ return ret;
+}
+
+static struct dma_fence *
+aie2_sched_job_run(struct drm_sched_job *sched_job)
+{
+ struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job);
+ struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
+ struct amdxdna_hwctx *hwctx = job->hwctx;
+ struct dma_fence *fence;
+ int ret;
+
+ if (!mmget_not_zero(job->mm))
+ return ERR_PTR(-ESRCH);
+
+ kref_get(&job->refcnt);
+ fence = dma_fence_get(job->fence);
+
+ if (job->drv_cmd) {
+ switch (job->drv_cmd->opcode) {
+ case SYNC_DEBUG_BO:
+ ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
+ break;
+ case ATTACH_DEBUG_BO:
+ ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ goto out;
+ }
+
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_NEW);
+
+ if (amdxdna_cmd_get_op(cmd_abo) == ERT_CMD_CHAIN)
+ ret = aie2_cmdlist_multi_execbuf(hwctx, job, aie2_sched_cmdlist_resp_handler);
+ else if (force_cmdlist)
+ ret = aie2_cmdlist_single_execbuf(hwctx, job, aie2_sched_cmdlist_resp_handler);
+ else
+ ret = aie2_execbuf(hwctx, job, aie2_sched_resp_handler);
+
+out:
+ if (ret) {
+ dma_fence_put(job->fence);
+ aie2_job_put(job);
+ mmput(job->mm);
+ fence = ERR_PTR(ret);
+ }
+ trace_xdna_job(sched_job, hwctx->name, "sent to device", job->seq);
+
+ return fence;
+}
+
+static void aie2_sched_job_free(struct drm_sched_job *sched_job)
+{
+ struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job);
+ struct amdxdna_hwctx *hwctx = job->hwctx;
+
+ trace_xdna_job(sched_job, hwctx->name, "job free", job->seq);
+ if (!job->job_done)
+ up(&hwctx->priv->job_sem);
+
+ drm_sched_job_cleanup(sched_job);
+ aie2_job_put(job);
+}
+
+static enum drm_gpu_sched_stat
+aie2_sched_job_timedout(struct drm_sched_job *sched_job)
+{
+ struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job);
+ struct amdxdna_hwctx *hwctx = job->hwctx;
+ struct amdxdna_dev *xdna;
+
+ xdna = hwctx->client->xdna;
+ trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq);
+ job->job_timeout = true;
+ mutex_lock(&xdna->dev_lock);
+ aie2_hwctx_stop(xdna, hwctx, sched_job);
+
+ aie2_hwctx_restart(xdna, hwctx);
+ mutex_unlock(&xdna->dev_lock);
+
+ return DRM_GPU_SCHED_STAT_RESET;
+}
+
+static const struct drm_sched_backend_ops sched_ops = {
+ .run_job = aie2_sched_job_run,
+ .free_job = aie2_sched_job_free,
+ .timedout_job = aie2_sched_job_timedout,
+};
+
+static int aie2_hwctx_col_list(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+ int start, end, first, last;
+ u32 width = 1, entries = 0;
+ int i;
+
+ if (!hwctx->num_tiles) {
+ XDNA_ERR(xdna, "Number of tiles is zero");
+ return -EINVAL;
+ }
+
+ ndev = xdna->dev_handle;
+ if (unlikely(!ndev->metadata.core.row_count)) {
+ XDNA_WARN(xdna, "Core tile row count is zero");
+ return -EINVAL;
+ }
+
+ hwctx->num_col = hwctx->num_tiles / ndev->metadata.core.row_count;
+ if (!hwctx->num_col || hwctx->num_col > ndev->total_col) {
+ XDNA_ERR(xdna, "Invalid num_col %d", hwctx->num_col);
+ return -EINVAL;
+ }
+
+ if (ndev->priv->col_align == COL_ALIGN_NATURE)
+ width = hwctx->num_col;
+
+ /*
+ * In range [start, end], find out columns that is multiple of width.
+ * 'first' is the first column,
+ * 'last' is the last column,
+ * 'entries' is the total number of columns.
+ */
+ start = xdna->dev_info->first_col;
+ end = ndev->total_col - hwctx->num_col;
+ if (start > 0 && end == 0) {
+ XDNA_DBG(xdna, "Force start from col 0");
+ start = 0;
+ }
+ first = start + (width - start % width) % width;
+ last = end - end % width;
+ if (last >= first)
+ entries = (last - first) / width + 1;
+ XDNA_DBG(xdna, "start %d end %d first %d last %d",
+ start, end, first, last);
+
+ if (unlikely(!entries)) {
+ XDNA_ERR(xdna, "Start %d end %d width %d",
+ start, end, width);
+ return -EINVAL;
+ }
+
+ hwctx->col_list = kmalloc_array(entries, sizeof(*hwctx->col_list), GFP_KERNEL);
+ if (!hwctx->col_list)
+ return -ENOMEM;
+
+ hwctx->col_list_len = entries;
+ hwctx->col_list[0] = first;
+ for (i = 1; i < entries; i++)
+ hwctx->col_list[i] = hwctx->col_list[i - 1] + width;
+
+ print_hex_dump_debug("col_list: ", DUMP_PREFIX_OFFSET, 16, 4, hwctx->col_list,
+ entries * sizeof(*hwctx->col_list), false);
+ return 0;
+}
+
+static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct alloc_requests *xrs_req;
+ int ret;
+
+ xrs_req = kzalloc(sizeof(*xrs_req), GFP_KERNEL);
+ if (!xrs_req)
+ return -ENOMEM;
+
+ xrs_req->cdo.start_cols = hwctx->col_list;
+ xrs_req->cdo.cols_len = hwctx->col_list_len;
+ xrs_req->cdo.ncols = hwctx->num_col;
+ xrs_req->cdo.qos_cap.opc = hwctx->max_opc;
+
+ xrs_req->rqos.gops = hwctx->qos.gops;
+ xrs_req->rqos.fps = hwctx->qos.fps;
+ xrs_req->rqos.dma_bw = hwctx->qos.dma_bandwidth;
+ xrs_req->rqos.latency = hwctx->qos.latency;
+ xrs_req->rqos.exec_time = hwctx->qos.frame_exec_time;
+ xrs_req->rqos.priority = hwctx->qos.priority;
+
+ xrs_req->rid = (uintptr_t)hwctx;
+
+ ret = xrs_allocate_resource(xdna->xrs_hdl, xrs_req, hwctx);
+ if (ret)
+ XDNA_ERR(xdna, "Allocate AIE resource failed, ret %d", ret);
+
+ kfree(xrs_req);
+ return ret;
+}
+
+static void aie2_release_resource(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ int ret;
+
+ ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
+ if (ret)
+ XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
+}
+
+static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct drm_file *filp = hwctx->client->filp;
+ struct drm_syncobj *syncobj;
+ u32 hdl;
+ int ret;
+
+ hwctx->syncobj_hdl = AMDXDNA_INVALID_FENCE_HANDLE;
+
+ ret = drm_syncobj_create(&syncobj, 0, NULL);
+ if (ret) {
+ XDNA_ERR(xdna, "Create ctx syncobj failed, ret %d", ret);
+ return ret;
+ }
+ ret = drm_syncobj_get_handle(filp, syncobj, &hdl);
+ if (ret) {
+ drm_syncobj_put(syncobj);
+ XDNA_ERR(xdna, "Create ctx syncobj handle failed, ret %d", ret);
+ return ret;
+ }
+ hwctx->priv->syncobj = syncobj;
+ hwctx->syncobj_hdl = hdl;
+
+ return 0;
+}
+
+static void aie2_ctx_syncobj_destroy(struct amdxdna_hwctx *hwctx)
+{
+ /*
+ * The syncobj_hdl is owned by user space and will be cleaned up
+ * separately.
+ */
+ drm_syncobj_put(hwctx->priv->syncobj);
+}
+
+int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ const struct drm_sched_init_args args = {
+ .ops = &sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
+ .credit_limit = HWCTX_MAX_CMDS,
+ .timeout = msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
+ .name = "amdxdna_js",
+ .dev = xdna->ddev.dev,
+ };
+ struct drm_gpu_scheduler *sched;
+ struct amdxdna_hwctx_priv *priv;
+ struct amdxdna_gem_obj *heap;
+ int i, ret;
+
+ priv = kzalloc(sizeof(*hwctx->priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ hwctx->priv = priv;
+
+ mutex_lock(&client->mm_lock);
+ heap = client->dev_heap;
+ if (!heap) {
+ XDNA_ERR(xdna, "The client dev heap object not exist");
+ mutex_unlock(&client->mm_lock);
+ ret = -ENOENT;
+ goto free_priv;
+ }
+ drm_gem_object_get(to_gobj(heap));
+ mutex_unlock(&client->mm_lock);
+ priv->heap = heap;
+ sema_init(&priv->job_sem, HWCTX_MAX_CMDS);
+
+ ret = amdxdna_gem_pin(heap);
+ if (ret) {
+ XDNA_ERR(xdna, "Dev heap pin failed, ret %d", ret);
+ goto put_heap;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->cmd_buf); i++) {
+ struct amdxdna_gem_obj *abo;
+ struct amdxdna_drm_create_bo args = {
+ .flags = 0,
+ .type = AMDXDNA_BO_DEV,
+ .vaddr = 0,
+ .size = MAX_CHAIN_CMDBUF_SIZE,
+ };
+
+ abo = amdxdna_drm_alloc_dev_bo(&xdna->ddev, &args, client->filp);
+ if (IS_ERR(abo)) {
+ ret = PTR_ERR(abo);
+ goto free_cmd_bufs;
+ }
+
+ XDNA_DBG(xdna, "Command buf %d addr 0x%llx size 0x%lx",
+ i, abo->mem.dev_addr, abo->mem.size);
+ priv->cmd_buf[i] = abo;
+ }
+
+ sched = &priv->sched;
+ mutex_init(&priv->io_lock);
+
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&priv->io_lock);
+ fs_reclaim_release(GFP_KERNEL);
+
+ ret = drm_sched_init(sched, &args);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to init DRM scheduler. ret %d", ret);
+ goto free_cmd_bufs;
+ }
+
+ ret = drm_sched_entity_init(&priv->entity, DRM_SCHED_PRIORITY_NORMAL,
+ &sched, 1, NULL);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to initial sched entiry. ret %d", ret);
+ goto free_sched;
+ }
+
+ ret = aie2_hwctx_col_list(hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Create col list failed, ret %d", ret);
+ goto free_entity;
+ }
+
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto free_col_list;
+
+ ret = aie2_alloc_resource(hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Alloc hw resource failed, ret %d", ret);
+ goto suspend_put;
+ }
+
+ ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
+ heap->mem.userptr, heap->mem.size);
+ if (ret) {
+ XDNA_ERR(xdna, "Map host buffer failed, ret %d", ret);
+ goto release_resource;
+ }
+
+ ret = aie2_ctx_syncobj_create(hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret);
+ goto release_resource;
+ }
+ amdxdna_pm_suspend_put(xdna);
+
+ hwctx->status = HWCTX_STAT_INIT;
+ init_waitqueue_head(&priv->job_free_wq);
+
+ XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name);
+
+ return 0;
+
+release_resource:
+ aie2_release_resource(hwctx);
+suspend_put:
+ amdxdna_pm_suspend_put(xdna);
+free_col_list:
+ kfree(hwctx->col_list);
+free_entity:
+ drm_sched_entity_destroy(&priv->entity);
+free_sched:
+ drm_sched_fini(&priv->sched);
+free_cmd_bufs:
+ for (i = 0; i < ARRAY_SIZE(priv->cmd_buf); i++) {
+ if (!priv->cmd_buf[i])
+ continue;
+ drm_gem_object_put(to_gobj(priv->cmd_buf[i]));
+ }
+ amdxdna_gem_unpin(heap);
+put_heap:
+ drm_gem_object_put(to_gobj(heap));
+free_priv:
+ kfree(priv);
+ return ret;
+}
+
+void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_dev *xdna;
+ int idx;
+
+ xdna = hwctx->client->xdna;
+
+ XDNA_DBG(xdna, "%s sequence number %lld", hwctx->name, hwctx->priv->seq);
+ aie2_hwctx_wait_for_idle(hwctx);
+
+ /* Request fw to destroy hwctx and cancel the rest pending requests */
+ aie2_release_resource(hwctx);
+
+ mutex_unlock(&xdna->dev_lock);
+ drm_sched_entity_destroy(&hwctx->priv->entity);
+
+ /* Wait for all submitted jobs to be completed or canceled */
+ wait_event(hwctx->priv->job_free_wq,
+ atomic64_read(&hwctx->job_submit_cnt) ==
+ atomic64_read(&hwctx->job_free_cnt));
+ mutex_lock(&xdna->dev_lock);
+
+ drm_sched_fini(&hwctx->priv->sched);
+ aie2_ctx_syncobj_destroy(hwctx);
+
+ for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++)
+ drm_gem_object_put(to_gobj(hwctx->priv->cmd_buf[idx]));
+ amdxdna_gem_unpin(hwctx->priv->heap);
+ drm_gem_object_put(to_gobj(hwctx->priv->heap));
+
+ mutex_destroy(&hwctx->priv->io_lock);
+ kfree(hwctx->col_list);
+ kfree(hwctx->priv);
+ kfree(hwctx->cus);
+}
+
+static int aie2_config_cu_resp_handler(void *handle, void __iomem *data, size_t size)
+{
+ struct amdxdna_hwctx *hwctx = handle;
+
+ amdxdna_pm_suspend_put(hwctx->client->xdna);
+ return 0;
+}
+
+static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size)
+{
+ struct amdxdna_hwctx_param_config_cu *config = buf;
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ u32 total_size;
+ int ret;
+
+ XDNA_DBG(xdna, "Config %d CU to %s", config->num_cus, hwctx->name);
+ if (XDNA_MBZ_DBG(xdna, config->pad, sizeof(config->pad)))
+ return -EINVAL;
+
+ if (hwctx->status != HWCTX_STAT_INIT) {
+ XDNA_ERR(xdna, "Not support re-config CU");
+ return -EINVAL;
+ }
+
+ if (!config->num_cus) {
+ XDNA_ERR(xdna, "Number of CU is zero");
+ return -EINVAL;
+ }
+
+ total_size = struct_size(config, cu_configs, config->num_cus);
+ if (total_size > size) {
+ XDNA_ERR(xdna, "CU config larger than size");
+ return -EINVAL;
+ }
+
+ hwctx->cus = kmemdup(config, total_size, GFP_KERNEL);
+ if (!hwctx->cus)
+ return -ENOMEM;
+
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto free_cus;
+
+ ret = aie2_config_cu(hwctx, aie2_config_cu_resp_handler);
+ if (ret) {
+ XDNA_ERR(xdna, "Config CU to firmware failed, ret %d", ret);
+ goto pm_suspend_put;
+ }
+
+ wmb(); /* To avoid locking in command submit when check status */
+ hwctx->status = HWCTX_STAT_READY;
+
+ return 0;
+
+pm_suspend_put:
+ amdxdna_pm_suspend_put(xdna);
+free_cus:
+ kfree(hwctx->cus);
+ hwctx->cus = NULL;
+ return ret;
+}
+
+static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq)
+{
+ struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq);
+
+ if (!out_fence) {
+ XDNA_ERR(hwctx->client->xdna, "Failed to get fence");
+ return;
+ }
+
+ dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT);
+ dma_fence_put(out_fence);
+}
+
+static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
+ bool attach)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_drv_cmd cmd = { 0 };
+ struct amdxdna_gem_obj *abo;
+ u64 seq;
+ int ret;
+
+ abo = amdxdna_gem_get_obj(client, bo_hdl, AMDXDNA_BO_DEV);
+ if (!abo) {
+ XDNA_ERR(xdna, "Get bo %d failed", bo_hdl);
+ return -EINVAL;
+ }
+
+ if (attach) {
+ if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) {
+ ret = -EBUSY;
+ goto put_obj;
+ }
+ cmd.opcode = ATTACH_DEBUG_BO;
+ } else {
+ if (abo->assigned_hwctx != hwctx->id) {
+ ret = -EINVAL;
+ goto put_obj;
+ }
+ cmd.opcode = DETACH_DEBUG_BO;
+ }
+
+ ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
+ &bo_hdl, 1, hwctx->id, &seq);
+ if (ret) {
+ XDNA_ERR(xdna, "Submit command failed");
+ goto put_obj;
+ }
+
+ aie2_cmd_wait(hwctx, seq);
+ if (cmd.result) {
+ XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
+ goto put_obj;
+ }
+
+ if (attach)
+ abo->assigned_hwctx = hwctx->id;
+ else
+ abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE;
+
+ XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name);
+
+put_obj:
+ amdxdna_gem_put_obj(abo);
+ return ret;
+}
+
+int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+ switch (type) {
+ case DRM_AMDXDNA_HWCTX_CONFIG_CU:
+ return aie2_hwctx_cu_config(hwctx, buf, size);
+ case DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF:
+ return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, true);
+ case DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF:
+ return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, false);
+ default:
+ XDNA_DBG(xdna, "Not supported type %d", type);
+ return -EOPNOTSUPP;
+ }
+}
+
+int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_drv_cmd cmd = { 0 };
+ u64 seq;
+ int ret;
+
+ cmd.opcode = SYNC_DEBUG_BO;
+ ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
+ &debug_bo_hdl, 1, hwctx->id, &seq);
+ if (ret) {
+ XDNA_ERR(xdna, "Submit command failed");
+ return ret;
+ }
+
+ aie2_cmd_wait(hwctx, seq);
+ if (cmd.result) {
+ XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aie2_populate_range(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ struct amdxdna_umap *mapp;
+ unsigned long timeout;
+ struct mm_struct *mm;
+ bool found;
+ int ret;
+
+ timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+again:
+ found = false;
+ down_write(&xdna->notifier_lock);
+ list_for_each_entry(mapp, &abo->mem.umap_list, node) {
+ if (mapp->invalid) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ abo->mem.map_invalid = false;
+ up_write(&xdna->notifier_lock);
+ return 0;
+ }
+ kref_get(&mapp->refcnt);
+ up_write(&xdna->notifier_lock);
+
+ XDNA_DBG(xdna, "populate memory range %lx %lx",
+ mapp->vma->vm_start, mapp->vma->vm_end);
+ mm = mapp->notifier.mm;
+ if (!mmget_not_zero(mm)) {
+ amdxdna_umap_put(mapp);
+ return -EFAULT;
+ }
+
+ mapp->range.notifier_seq = mmu_interval_read_begin(&mapp->notifier);
+ mmap_read_lock(mm);
+ ret = hmm_range_fault(&mapp->range);
+ mmap_read_unlock(mm);
+ if (ret) {
+ if (time_after(jiffies, timeout)) {
+ ret = -ETIME;
+ goto put_mm;
+ }
+
+ if (ret == -EBUSY) {
+ amdxdna_umap_put(mapp);
+ goto again;
+ }
+
+ goto put_mm;
+ }
+
+ down_write(&xdna->notifier_lock);
+ if (mmu_interval_read_retry(&mapp->notifier, mapp->range.notifier_seq)) {
+ up_write(&xdna->notifier_lock);
+ amdxdna_umap_put(mapp);
+ goto again;
+ }
+ mapp->invalid = false;
+ up_write(&xdna->notifier_lock);
+ amdxdna_umap_put(mapp);
+ goto again;
+
+put_mm:
+ amdxdna_umap_put(mapp);
+ mmput(mm);
+ return ret;
+}
+
+int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct ww_acquire_ctx acquire_ctx;
+ struct dma_fence_chain *chain;
+ struct amdxdna_gem_obj *abo;
+ unsigned long timeout = 0;
+ int ret, i;
+
+ ret = down_interruptible(&hwctx->priv->job_sem);
+ if (ret) {
+ XDNA_ERR(xdna, "Grab job sem failed, ret %d", ret);
+ return ret;
+ }
+
+ chain = dma_fence_chain_alloc();
+ if (!chain) {
+ XDNA_ERR(xdna, "Alloc fence chain failed");
+ ret = -ENOMEM;
+ goto up_sem;
+ }
+
+ ret = drm_sched_job_init(&job->base, &hwctx->priv->entity, 1, hwctx,
+ hwctx->client->filp->client_id);
+ if (ret) {
+ XDNA_ERR(xdna, "DRM job init failed, ret %d", ret);
+ goto free_chain;
+ }
+
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto cleanup_job;
+
+retry:
+ ret = drm_gem_lock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
+ if (ret) {
+ XDNA_WARN(xdna, "Failed to lock BOs, ret %d", ret);
+ goto suspend_put;
+ }
+
+ for (i = 0; i < job->bo_cnt; i++) {
+ ret = dma_resv_reserve_fences(job->bos[i]->resv, 1);
+ if (ret) {
+ XDNA_WARN(xdna, "Failed to reserve fences %d", ret);
+ drm_gem_unlock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
+ goto suspend_put;
+ }
+ }
+
+ down_read(&xdna->notifier_lock);
+ for (i = 0; i < job->bo_cnt; i++) {
+ abo = to_xdna_obj(job->bos[i]);
+ if (abo->mem.map_invalid) {
+ up_read(&xdna->notifier_lock);
+ drm_gem_unlock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
+ if (!timeout) {
+ timeout = jiffies +
+ msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ } else if (time_after(jiffies, timeout)) {
+ ret = -ETIME;
+ goto suspend_put;
+ }
+
+ ret = aie2_populate_range(abo);
+ if (ret)
+ goto suspend_put;
+ goto retry;
+ }
+ }
+
+ mutex_lock(&hwctx->priv->io_lock);
+ drm_sched_job_arm(&job->base);
+ job->out_fence = dma_fence_get(&job->base.s_fence->finished);
+ for (i = 0; i < job->bo_cnt; i++)
+ dma_resv_add_fence(job->bos[i]->resv, job->out_fence, DMA_RESV_USAGE_WRITE);
+ job->seq = hwctx->priv->seq++;
+ kref_get(&job->refcnt);
+ drm_sched_entity_push_job(&job->base);
+
+ *seq = job->seq;
+ drm_syncobj_add_point(hwctx->priv->syncobj, chain, job->out_fence, *seq);
+ mutex_unlock(&hwctx->priv->io_lock);
+
+ up_read(&xdna->notifier_lock);
+ drm_gem_unlock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
+
+ aie2_job_put(job);
+ atomic64_inc(&hwctx->job_submit_cnt);
+
+ return 0;
+
+suspend_put:
+ amdxdna_pm_suspend_put(xdna);
+cleanup_job:
+ drm_sched_job_cleanup(&job->base);
+free_chain:
+ dma_fence_chain_free(chain);
+up_sem:
+ up(&hwctx->priv->job_sem);
+ job->job_done = true;
+ return ret;
+}
+
+void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo,
+ unsigned long cur_seq)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ struct drm_gem_object *gobj = to_gobj(abo);
+ long ret;
+
+ ret = dma_resv_wait_timeout(gobj->resv, DMA_RESV_USAGE_BOOKKEEP,
+ true, MAX_SCHEDULE_TIMEOUT);
+ if (!ret || ret == -ERESTARTSYS)
+ XDNA_ERR(xdna, "Failed to wait for bo, ret %ld", ret);
+}
diff --git a/drivers/accel/amdxdna/aie2_error.c b/drivers/accel/amdxdna/aie2_error.c
new file mode 100644
index 000000000000..d452008ec4f4
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_error.c
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/drm_cache.h>
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/dma-mapping.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+
+#include "aie2_msg_priv.h"
+#include "aie2_pci.h"
+#include "amdxdna_error.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+struct async_event {
+ struct amdxdna_dev_hdl *ndev;
+ struct async_event_msg_resp resp;
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ u8 *buf;
+ dma_addr_t addr;
+ u32 size;
+};
+
+struct async_events {
+ struct workqueue_struct *wq;
+ u8 *buf;
+ dma_addr_t addr;
+ u32 size;
+ u32 event_cnt;
+ struct async_event event[] __counted_by(event_cnt);
+};
+
+/*
+ * Below enum, struct and lookup tables are porting from XAIE util header file.
+ *
+ * Below data is defined by AIE device and it is used for decode error message
+ * from the device.
+ */
+
+enum aie_module_type {
+ AIE_MEM_MOD = 0,
+ AIE_CORE_MOD,
+ AIE_PL_MOD,
+ AIE_UNKNOWN_MOD,
+};
+
+enum aie_error_category {
+ AIE_ERROR_SATURATION = 0,
+ AIE_ERROR_FP,
+ AIE_ERROR_STREAM,
+ AIE_ERROR_ACCESS,
+ AIE_ERROR_BUS,
+ AIE_ERROR_INSTRUCTION,
+ AIE_ERROR_ECC,
+ AIE_ERROR_LOCK,
+ AIE_ERROR_DMA,
+ AIE_ERROR_MEM_PARITY,
+ /* Unknown is not from XAIE, added for better category */
+ AIE_ERROR_UNKNOWN,
+};
+
+/* Don't pack, unless XAIE side changed */
+struct aie_error {
+ __u8 row;
+ __u8 col;
+ __u32 mod_type;
+ __u8 event_id;
+};
+
+struct aie_err_info {
+ u32 err_cnt;
+ u32 ret_code;
+ u32 rsvd;
+ struct aie_error payload[] __counted_by(err_cnt);
+};
+
+struct aie_event_category {
+ u8 event_id;
+ enum aie_error_category category;
+};
+
+#define EVENT_CATEGORY(id, cat) { id, cat }
+static const struct aie_event_category aie_ml_mem_event_cat[] = {
+ EVENT_CATEGORY(88U, AIE_ERROR_ECC),
+ EVENT_CATEGORY(90U, AIE_ERROR_ECC),
+ EVENT_CATEGORY(91U, AIE_ERROR_MEM_PARITY),
+ EVENT_CATEGORY(92U, AIE_ERROR_MEM_PARITY),
+ EVENT_CATEGORY(93U, AIE_ERROR_MEM_PARITY),
+ EVENT_CATEGORY(94U, AIE_ERROR_MEM_PARITY),
+ EVENT_CATEGORY(95U, AIE_ERROR_MEM_PARITY),
+ EVENT_CATEGORY(96U, AIE_ERROR_MEM_PARITY),
+ EVENT_CATEGORY(97U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(98U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(99U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(100U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(101U, AIE_ERROR_LOCK),
+};
+
+static const struct aie_event_category aie_ml_core_event_cat[] = {
+ EVENT_CATEGORY(55U, AIE_ERROR_ACCESS),
+ EVENT_CATEGORY(56U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(57U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(58U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(59U, AIE_ERROR_INSTRUCTION),
+ EVENT_CATEGORY(60U, AIE_ERROR_ACCESS),
+ EVENT_CATEGORY(62U, AIE_ERROR_ECC),
+ EVENT_CATEGORY(64U, AIE_ERROR_ECC),
+ EVENT_CATEGORY(65U, AIE_ERROR_ACCESS),
+ EVENT_CATEGORY(66U, AIE_ERROR_ACCESS),
+ EVENT_CATEGORY(67U, AIE_ERROR_LOCK),
+ EVENT_CATEGORY(70U, AIE_ERROR_INSTRUCTION),
+ EVENT_CATEGORY(71U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(72U, AIE_ERROR_BUS),
+};
+
+static const struct aie_event_category aie_ml_mem_tile_event_cat[] = {
+ EVENT_CATEGORY(130U, AIE_ERROR_ECC),
+ EVENT_CATEGORY(132U, AIE_ERROR_ECC),
+ EVENT_CATEGORY(133U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(134U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(135U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(136U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(137U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(138U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(139U, AIE_ERROR_LOCK),
+};
+
+static const struct aie_event_category aie_ml_shim_tile_event_cat[] = {
+ EVENT_CATEGORY(64U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(65U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(66U, AIE_ERROR_STREAM),
+ EVENT_CATEGORY(67U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(68U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(69U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(70U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(71U, AIE_ERROR_BUS),
+ EVENT_CATEGORY(72U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(73U, AIE_ERROR_DMA),
+ EVENT_CATEGORY(74U, AIE_ERROR_LOCK),
+};
+
+static const enum amdxdna_error_num aie_cat_err_num_map[] = {
+ [AIE_ERROR_SATURATION] = AMDXDNA_ERROR_NUM_AIE_SATURATION,
+ [AIE_ERROR_FP] = AMDXDNA_ERROR_NUM_AIE_FP,
+ [AIE_ERROR_STREAM] = AMDXDNA_ERROR_NUM_AIE_STREAM,
+ [AIE_ERROR_ACCESS] = AMDXDNA_ERROR_NUM_AIE_ACCESS,
+ [AIE_ERROR_BUS] = AMDXDNA_ERROR_NUM_AIE_BUS,
+ [AIE_ERROR_INSTRUCTION] = AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
+ [AIE_ERROR_ECC] = AMDXDNA_ERROR_NUM_AIE_ECC,
+ [AIE_ERROR_LOCK] = AMDXDNA_ERROR_NUM_AIE_LOCK,
+ [AIE_ERROR_DMA] = AMDXDNA_ERROR_NUM_AIE_DMA,
+ [AIE_ERROR_MEM_PARITY] = AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
+ [AIE_ERROR_UNKNOWN] = AMDXDNA_ERROR_NUM_UNKNOWN,
+};
+
+static_assert(ARRAY_SIZE(aie_cat_err_num_map) == AIE_ERROR_UNKNOWN + 1);
+
+static const enum amdxdna_error_module aie_err_mod_map[] = {
+ [AIE_MEM_MOD] = AMDXDNA_ERROR_MODULE_AIE_MEMORY,
+ [AIE_CORE_MOD] = AMDXDNA_ERROR_MODULE_AIE_CORE,
+ [AIE_PL_MOD] = AMDXDNA_ERROR_MODULE_AIE_PL,
+ [AIE_UNKNOWN_MOD] = AMDXDNA_ERROR_MODULE_UNKNOWN,
+};
+
+static_assert(ARRAY_SIZE(aie_err_mod_map) == AIE_UNKNOWN_MOD + 1);
+
+static enum aie_error_category
+aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
+{
+ const struct aie_event_category *lut;
+ int num_entry;
+ int i;
+
+ switch (mod_type) {
+ case AIE_PL_MOD:
+ lut = aie_ml_shim_tile_event_cat;
+ num_entry = ARRAY_SIZE(aie_ml_shim_tile_event_cat);
+ break;
+ case AIE_CORE_MOD:
+ lut = aie_ml_core_event_cat;
+ num_entry = ARRAY_SIZE(aie_ml_core_event_cat);
+ break;
+ case AIE_MEM_MOD:
+ if (row == 1) {
+ lut = aie_ml_mem_tile_event_cat;
+ num_entry = ARRAY_SIZE(aie_ml_mem_tile_event_cat);
+ } else {
+ lut = aie_ml_mem_event_cat;
+ num_entry = ARRAY_SIZE(aie_ml_mem_event_cat);
+ }
+ break;
+ default:
+ return AIE_ERROR_UNKNOWN;
+ }
+
+ for (i = 0; i < num_entry; i++) {
+ if (event_id != lut[i].event_id)
+ continue;
+
+ if (lut[i].category > AIE_ERROR_UNKNOWN)
+ return AIE_ERROR_UNKNOWN;
+
+ return lut[i].category;
+ }
+
+ return AIE_ERROR_UNKNOWN;
+}
+
+static void aie2_update_last_async_error(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
+{
+ struct aie_error *errs = err_info;
+ enum amdxdna_error_module err_mod;
+ enum aie_error_category aie_err;
+ enum amdxdna_error_num err_num;
+ struct aie_error *last_err;
+
+ last_err = &errs[num_err - 1];
+ if (last_err->mod_type >= AIE_UNKNOWN_MOD) {
+ err_num = aie_cat_err_num_map[AIE_ERROR_UNKNOWN];
+ err_mod = aie_err_mod_map[AIE_UNKNOWN_MOD];
+ } else {
+ aie_err = aie_get_error_category(last_err->row,
+ last_err->event_id,
+ last_err->mod_type);
+ err_num = aie_cat_err_num_map[aie_err];
+ err_mod = aie_err_mod_map[last_err->mod_type];
+ }
+
+ ndev->last_async_err.err_code = AMDXDNA_ERROR_ENCODE(err_num, err_mod);
+ ndev->last_async_err.ts_us = ktime_to_us(ktime_get_real());
+ ndev->last_async_err.ex_err_code = AMDXDNA_EXTRA_ERR_ENCODE(last_err->row, last_err->col);
+}
+
+static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
+{
+ struct aie_error *errs = err_info;
+ u32 err_col = 0; /* assume that AIE has less than 32 columns */
+ int i;
+
+ /* Get err column bitmap */
+ for (i = 0; i < num_err; i++) {
+ struct aie_error *err = &errs[i];
+ enum aie_error_category cat;
+
+ cat = aie_get_error_category(err->row, err->event_id, err->mod_type);
+ XDNA_ERR(ndev->xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d",
+ err->row, err->col, err->mod_type,
+ err->event_id, cat);
+
+ if (err->col >= 32) {
+ XDNA_WARN(ndev->xdna, "Invalid column number");
+ break;
+ }
+
+ err_col |= (1 << err->col);
+ }
+
+ return err_col;
+}
+
+static int aie2_error_async_cb(void *handle, void __iomem *data, size_t size)
+{
+ struct async_event *e = handle;
+
+ if (data) {
+ e->resp.type = readl(data + offsetof(struct async_event_msg_resp, type));
+ wmb(); /* Update status in the end, so that no lock for here */
+ e->resp.status = readl(data + offsetof(struct async_event_msg_resp, status));
+ }
+ queue_work(e->wq, &e->work);
+ return 0;
+}
+
+static int aie2_error_event_send(struct async_event *e)
+{
+ drm_clflush_virt_range(e->buf, e->size); /* device can access */
+ return aie2_register_asyn_event_msg(e->ndev, e->addr, e->size, e,
+ aie2_error_async_cb);
+}
+
+static void aie2_error_worker(struct work_struct *err_work)
+{
+ struct aie_err_info *info;
+ struct amdxdna_dev *xdna;
+ struct async_event *e;
+ u32 max_err;
+ u32 err_col;
+
+ e = container_of(err_work, struct async_event, work);
+
+ xdna = e->ndev->xdna;
+
+ if (e->resp.status == MAX_AIE2_STATUS_CODE)
+ return;
+
+ e->resp.status = MAX_AIE2_STATUS_CODE;
+
+ print_hex_dump_debug("AIE error: ", DUMP_PREFIX_OFFSET, 16, 4,
+ e->buf, 0x100, false);
+
+ info = (struct aie_err_info *)e->buf;
+ XDNA_DBG(xdna, "Error count %d return code %d", info->err_cnt, info->ret_code);
+
+ max_err = (e->size - sizeof(*info)) / sizeof(struct aie_error);
+ if (unlikely(info->err_cnt > max_err)) {
+ WARN_ONCE(1, "Error count too large %d\n", info->err_cnt);
+ return;
+ }
+ err_col = aie2_error_backtrack(e->ndev, info->payload, info->err_cnt);
+ if (!err_col) {
+ XDNA_WARN(xdna, "Did not get error column");
+ return;
+ }
+
+ mutex_lock(&xdna->dev_lock);
+ aie2_update_last_async_error(e->ndev, info->payload, info->err_cnt);
+
+ /* Re-sent this event to firmware */
+ if (aie2_error_event_send(e))
+ XDNA_WARN(xdna, "Unable to register async event");
+ mutex_unlock(&xdna->dev_lock);
+}
+
+void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+ struct async_events *events;
+
+ events = ndev->async_events;
+
+ mutex_unlock(&xdna->dev_lock);
+ destroy_workqueue(events->wq);
+ mutex_lock(&xdna->dev_lock);
+
+ dma_free_noncoherent(xdna->ddev.dev, events->size, events->buf,
+ events->addr, DMA_FROM_DEVICE);
+ kfree(events);
+}
+
+int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+ u32 total_col = ndev->total_col;
+ u32 total_size = ASYNC_BUF_SIZE * total_col;
+ struct async_events *events;
+ int i, ret;
+
+ events = kzalloc(struct_size(events, event, total_col), GFP_KERNEL);
+ if (!events)
+ return -ENOMEM;
+
+ events->buf = dma_alloc_noncoherent(xdna->ddev.dev, total_size, &events->addr,
+ DMA_FROM_DEVICE, GFP_KERNEL);
+ if (!events->buf) {
+ ret = -ENOMEM;
+ goto free_events;
+ }
+ events->size = total_size;
+ events->event_cnt = total_col;
+
+ events->wq = alloc_ordered_workqueue("async_wq", 0);
+ if (!events->wq) {
+ ret = -ENOMEM;
+ goto free_buf;
+ }
+
+ for (i = 0; i < events->event_cnt; i++) {
+ struct async_event *e = &events->event[i];
+ u32 offset = i * ASYNC_BUF_SIZE;
+
+ e->ndev = ndev;
+ e->wq = events->wq;
+ e->buf = &events->buf[offset];
+ e->addr = events->addr + offset;
+ e->size = ASYNC_BUF_SIZE;
+ e->resp.status = MAX_AIE2_STATUS_CODE;
+ INIT_WORK(&e->work, aie2_error_worker);
+
+ ret = aie2_error_event_send(e);
+ if (ret)
+ goto free_wq;
+ }
+
+ ndev->async_events = events;
+
+ XDNA_DBG(xdna, "Async event count %d, buf total size 0x%x",
+ events->event_cnt, events->size);
+ return 0;
+
+free_wq:
+ destroy_workqueue(events->wq);
+free_buf:
+ dma_free_noncoherent(xdna->ddev.dev, events->size, events->buf,
+ events->addr, DMA_FROM_DEVICE);
+free_events:
+ kfree(events);
+ return ret;
+}
+
+int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ args->num_element = 1;
+ args->element_size = sizeof(ndev->last_async_err);
+ if (copy_to_user(u64_to_user_ptr(args->buffer),
+ &ndev->last_async_err, args->element_size))
+ return -EFAULT;
+
+ return 0;
+}
diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c
new file mode 100644
index 000000000000..d493bb1c3360
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_message.c
@@ -0,0 +1,1074 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_cache.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/bitfield.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/xarray.h>
+
+#include "aie2_msg_priv.h"
+#include "aie2_pci.h"
+#include "amdxdna_ctx.h"
+#include "amdxdna_gem.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_mailbox_helper.h"
+#include "amdxdna_pci_drv.h"
+
+#define DECLARE_AIE2_MSG(name, op) \
+ DECLARE_XDNA_MSG_COMMON(name, op, MAX_AIE2_STATUS_CODE)
+
+#define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops)
+
+static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
+ struct xdna_mailbox_msg *msg)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+ struct xdna_notify *hdl = msg->handle;
+ int ret;
+
+ if (!ndev->mgmt_chann)
+ return -ENODEV;
+
+ drm_WARN_ON(&xdna->ddev, xdna->rpm_on && !mutex_is_locked(&xdna->dev_lock));
+ ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg);
+ if (ret == -ETIME) {
+ xdna_mailbox_stop_channel(ndev->mgmt_chann);
+ xdna_mailbox_destroy_channel(ndev->mgmt_chann);
+ ndev->mgmt_chann = NULL;
+ }
+
+ if (!ret && *hdl->status != AIE2_STATUS_SUCCESS) {
+ XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
+ msg->opcode, *hdl->data);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev)
+{
+ DECLARE_AIE2_MSG(suspend, MSG_OP_SUSPEND);
+
+ return aie2_send_mgmt_msg_wait(ndev, &msg);
+}
+
+int aie2_resume_fw(struct amdxdna_dev_hdl *ndev)
+{
+ DECLARE_AIE2_MSG(suspend, MSG_OP_RESUME);
+
+ return aie2_send_mgmt_msg_wait(ndev, &msg);
+}
+
+int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value)
+{
+ DECLARE_AIE2_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG);
+ int ret;
+
+ req.type = type;
+ req.value = value;
+
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Failed to set runtime config, ret %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value)
+{
+ DECLARE_AIE2_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG);
+ int ret;
+
+ req.type = type;
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Failed to get runtime config, ret %d", ret);
+ return ret;
+ }
+
+ *value = resp.value;
+ return 0;
+}
+
+int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid)
+{
+ DECLARE_AIE2_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID);
+
+ req.pasid = pasid;
+
+ return aie2_send_mgmt_msg_wait(ndev, &msg);
+}
+
+int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version)
+{
+ DECLARE_AIE2_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ int ret;
+
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret)
+ return ret;
+
+ XDNA_DBG(xdna, "Query AIE version - major: %u minor: %u completed",
+ resp.major, resp.minor);
+
+ version->major = resp.major;
+ version->minor = resp.minor;
+
+ return 0;
+}
+
+int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata)
+{
+ DECLARE_AIE2_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO);
+ int ret;
+
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret)
+ return ret;
+
+ metadata->size = resp.info.size;
+ metadata->cols = resp.info.cols;
+ metadata->rows = resp.info.rows;
+
+ metadata->version.major = resp.info.major;
+ metadata->version.minor = resp.info.minor;
+
+ metadata->core.row_count = resp.info.core_rows;
+ metadata->core.row_start = resp.info.core_row_start;
+ metadata->core.dma_channel_count = resp.info.core_dma_channels;
+ metadata->core.lock_count = resp.info.core_locks;
+ metadata->core.event_reg_count = resp.info.core_events;
+
+ metadata->mem.row_count = resp.info.mem_rows;
+ metadata->mem.row_start = resp.info.mem_row_start;
+ metadata->mem.dma_channel_count = resp.info.mem_dma_channels;
+ metadata->mem.lock_count = resp.info.mem_locks;
+ metadata->mem.event_reg_count = resp.info.mem_events;
+
+ metadata->shim.row_count = resp.info.shim_rows;
+ metadata->shim.row_start = resp.info.shim_row_start;
+ metadata->shim.dma_channel_count = resp.info.shim_dma_channels;
+ metadata->shim.lock_count = resp.info.shim_locks;
+ metadata->shim.event_reg_count = resp.info.shim_events;
+
+ return 0;
+}
+
+int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
+ struct amdxdna_fw_ver *fw_ver)
+{
+ DECLARE_AIE2_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION);
+ int ret;
+
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret)
+ return ret;
+
+ fw_ver->major = resp.major;
+ fw_ver->minor = resp.minor;
+ fw_ver->sub = resp.sub;
+ fw_ver->build = resp.build;
+
+ return 0;
+}
+
+int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
+{
+ DECLARE_AIE2_MSG(create_ctx, MSG_OP_CREATE_CONTEXT);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ struct xdna_mailbox_chann_res x2i;
+ struct xdna_mailbox_chann_res i2x;
+ struct cq_pair *cq_pair;
+ u32 intr_reg;
+ int ret;
+
+ req.aie_type = 1;
+ req.start_col = hwctx->start_col;
+ req.num_col = hwctx->num_col;
+ req.num_cq_pairs_requested = 1;
+ req.pasid = hwctx->client->pasid;
+ req.context_priority = 2;
+
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret)
+ return ret;
+
+ hwctx->fw_ctx_id = resp.context_id;
+ WARN_ONCE(hwctx->fw_ctx_id == -1, "Unexpected context id");
+
+ if (ndev->force_preempt_enabled) {
+ ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to enable force preempt %d", ret);
+ return ret;
+ }
+ }
+
+ cq_pair = &resp.cq_pair[0];
+ x2i.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.head_addr);
+ x2i.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.tail_addr);
+ x2i.rb_start_addr = AIE2_SRAM_OFF(ndev, cq_pair->x2i_q.buf_addr);
+ x2i.rb_size = cq_pair->x2i_q.buf_size;
+
+ i2x.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->i2x_q.head_addr);
+ i2x.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->i2x_q.tail_addr);
+ i2x.rb_start_addr = AIE2_SRAM_OFF(ndev, cq_pair->i2x_q.buf_addr);
+ i2x.rb_size = cq_pair->i2x_q.buf_size;
+
+ ret = pci_irq_vector(to_pci_dev(xdna->ddev.dev), resp.msix_id);
+ if (ret == -EINVAL) {
+ XDNA_ERR(xdna, "not able to create channel");
+ goto out_destroy_context;
+ }
+
+ intr_reg = i2x.mb_head_ptr_reg + 4;
+ hwctx->priv->mbox_chann = xdna_mailbox_create_channel(ndev->mbox, &x2i, &i2x,
+ intr_reg, ret);
+ if (!hwctx->priv->mbox_chann) {
+ XDNA_ERR(xdna, "not able to create channel");
+ ret = -EINVAL;
+ goto out_destroy_context;
+ }
+ ndev->hwctx_num++;
+
+ XDNA_DBG(xdna, "%s mailbox channel irq: %d, msix_id: %d",
+ hwctx->name, ret, resp.msix_id);
+ XDNA_DBG(xdna, "%s created fw ctx %d pasid %d", hwctx->name,
+ hwctx->fw_ctx_id, hwctx->client->pasid);
+
+ return 0;
+
+out_destroy_context:
+ aie2_destroy_context(ndev, hwctx);
+ return ret;
+}
+
+int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
+{
+ DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ int ret;
+
+ if (hwctx->fw_ctx_id == -1)
+ return 0;
+
+ xdna_mailbox_stop_channel(hwctx->priv->mbox_chann);
+
+ req.context_id = hwctx->fw_ctx_id;
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret)
+ XDNA_WARN(xdna, "%s destroy context failed, ret %d", hwctx->name, ret);
+
+ xdna_mailbox_destroy_channel(hwctx->priv->mbox_chann);
+ XDNA_DBG(xdna, "%s destroyed fw ctx %d", hwctx->name,
+ hwctx->fw_ctx_id);
+ hwctx->priv->mbox_chann = NULL;
+ hwctx->fw_ctx_id = -1;
+ ndev->hwctx_num--;
+
+ return ret;
+}
+
+int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
+{
+ DECLARE_AIE2_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ int ret;
+
+ req.context_id = context_id;
+ req.buf_addr = addr;
+ req.buf_size = size;
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret)
+ return ret;
+
+ XDNA_DBG(xdna, "fw ctx %d map host buf addr 0x%llx size 0x%llx",
+ context_id, addr, size);
+
+ return 0;
+}
+
+static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg)
+{
+ u32 *bitmap = arg;
+
+ *bitmap |= GENMASK(hwctx->start_col + hwctx->num_col - 1, hwctx->start_col);
+
+ return 0;
+}
+
+int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
+ u32 size, u32 *cols_filled)
+{
+ DECLARE_AIE2_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_client *client;
+ dma_addr_t dma_addr;
+ u32 aie_bitmap = 0;
+ u8 *buff_addr;
+ int ret;
+
+ buff_addr = dma_alloc_noncoherent(xdna->ddev.dev, size, &dma_addr,
+ DMA_FROM_DEVICE, GFP_KERNEL);
+ if (!buff_addr)
+ return -ENOMEM;
+
+ /* Go through each hardware context and mark the AIE columns that are active */
+ list_for_each_entry(client, &xdna->client_list, node)
+ amdxdna_hwctx_walk(client, &aie_bitmap, amdxdna_hwctx_col_map);
+
+ *cols_filled = 0;
+ req.dump_buff_addr = dma_addr;
+ req.dump_buff_size = size;
+ req.num_cols = hweight32(aie_bitmap);
+ req.aie_bitmap = aie_bitmap;
+
+ drm_clflush_virt_range(buff_addr, size); /* device can access */
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret) {
+ XDNA_ERR(xdna, "Error during NPU query, status %d", ret);
+ goto fail;
+ }
+
+ XDNA_DBG(xdna, "Query NPU status completed");
+
+ if (size < resp.size) {
+ ret = -EINVAL;
+ XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
+ goto fail;
+ }
+
+ if (copy_to_user(buf, buff_addr, resp.size)) {
+ ret = -EFAULT;
+ XDNA_ERR(xdna, "Failed to copy NPU status to user space");
+ goto fail;
+ }
+
+ *cols_filled = aie_bitmap;
+
+fail:
+ dma_free_noncoherent(xdna->ddev.dev, size, buff_addr, dma_addr, DMA_FROM_DEVICE);
+ return ret;
+}
+
+int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
+ char __user *buf, u32 size,
+ struct amdxdna_drm_query_telemetry_header *header)
+{
+ DECLARE_AIE2_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ dma_addr_t dma_addr;
+ u8 *addr;
+ int ret;
+
+ if (header->type >= MAX_TELEMETRY_TYPE)
+ return -EINVAL;
+
+ addr = dma_alloc_noncoherent(xdna->ddev.dev, size, &dma_addr,
+ DMA_FROM_DEVICE, GFP_KERNEL);
+ if (!addr)
+ return -ENOMEM;
+
+ req.buf_addr = dma_addr;
+ req.buf_size = size;
+ req.type = header->type;
+
+ drm_clflush_virt_range(addr, size); /* device can access */
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret) {
+ XDNA_ERR(xdna, "Query telemetry failed, status %d", ret);
+ goto free_buf;
+ }
+
+ if (size < resp.size) {
+ ret = -EINVAL;
+ XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
+ goto free_buf;
+ }
+
+ if (copy_to_user(buf, addr, resp.size)) {
+ ret = -EFAULT;
+ XDNA_ERR(xdna, "Failed to copy telemetry to user space");
+ goto free_buf;
+ }
+
+ header->major = resp.major;
+ header->minor = resp.minor;
+
+free_buf:
+ dma_free_noncoherent(xdna->ddev.dev, size, addr, dma_addr, DMA_FROM_DEVICE);
+ return ret;
+}
+
+int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
+ void *handle, int (*cb)(void*, void __iomem *, size_t))
+{
+ struct async_event_msg_req req = { 0 };
+ struct xdna_mailbox_msg msg = {
+ .send_data = (u8 *)&req,
+ .send_size = sizeof(req),
+ .handle = handle,
+ .opcode = MSG_OP_REGISTER_ASYNC_EVENT_MSG,
+ .notify_cb = cb,
+ };
+
+ req.buf_addr = addr;
+ req.buf_size = size;
+
+ XDNA_DBG(ndev->xdna, "Register addr 0x%llx size 0x%x", addr, size);
+ return xdna_mailbox_send_msg(ndev->mgmt_chann, &msg, TX_TIMEOUT);
+}
+
+int aie2_config_cu(struct amdxdna_hwctx *hwctx,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ u32 shift = xdna->dev_info->dev_mem_buf_shift;
+ struct config_cu_req req = { 0 };
+ struct xdna_mailbox_msg msg;
+ struct drm_gem_object *gobj;
+ struct amdxdna_gem_obj *abo;
+ int i;
+
+ if (!chann)
+ return -ENODEV;
+
+ if (hwctx->cus->num_cus > MAX_NUM_CUS) {
+ XDNA_DBG(xdna, "Exceed maximum CU %d", MAX_NUM_CUS);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < hwctx->cus->num_cus; i++) {
+ struct amdxdna_cu_config *cu = &hwctx->cus->cu_configs[i];
+
+ if (XDNA_MBZ_DBG(xdna, cu->pad, sizeof(cu->pad)))
+ return -EINVAL;
+
+ gobj = drm_gem_object_lookup(hwctx->client->filp, cu->cu_bo);
+ if (!gobj) {
+ XDNA_ERR(xdna, "Lookup GEM object failed");
+ return -EINVAL;
+ }
+ abo = to_xdna_obj(gobj);
+
+ if (abo->type != AMDXDNA_BO_DEV) {
+ drm_gem_object_put(gobj);
+ XDNA_ERR(xdna, "Invalid BO type");
+ return -EINVAL;
+ }
+
+ req.cfgs[i] = FIELD_PREP(AIE2_MSG_CFG_CU_PDI_ADDR,
+ abo->mem.dev_addr >> shift);
+ req.cfgs[i] |= FIELD_PREP(AIE2_MSG_CFG_CU_FUNC, cu->cu_func);
+ XDNA_DBG(xdna, "CU %d full addr 0x%llx, cfg 0x%x", i,
+ abo->mem.dev_addr, req.cfgs[i]);
+ drm_gem_object_put(gobj);
+ }
+ req.num_cus = hwctx->cus->num_cus;
+
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ msg.handle = hwctx;
+ msg.opcode = MSG_OP_CONFIG_CU;
+ msg.notify_cb = notify_cb;
+ return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+}
+
+static int aie2_init_exec_cu_req(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op)
+{
+ struct execute_buffer_req *cu_req = req;
+ u32 cmd_len;
+ void *cmd;
+
+ cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (cmd_len > sizeof(cu_req->payload))
+ return -EINVAL;
+
+ cu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (cu_req->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ memcpy(cu_req->payload, cmd, cmd_len);
+
+ *size = sizeof(*cu_req);
+ *msg_op = MSG_OP_EXECUTE_BUFFER_CF;
+ return 0;
+}
+
+static int aie2_init_exec_dpu_req(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op)
+{
+ struct exec_dpu_req *dpu_req = req;
+ struct amdxdna_cmd_start_npu *sn;
+ u32 cmd_len;
+
+ sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (cmd_len - sizeof(*sn) > sizeof(dpu_req->payload))
+ return -EINVAL;
+
+ dpu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (dpu_req->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ dpu_req->inst_buf_addr = sn->buffer;
+ dpu_req->inst_size = sn->buffer_size;
+ dpu_req->inst_prop_cnt = sn->prop_count;
+ memcpy(dpu_req->payload, sn->prop_args, cmd_len - sizeof(*sn));
+
+ *size = sizeof(*dpu_req);
+ *msg_op = MSG_OP_EXEC_DPU;
+ return 0;
+}
+
+static void aie2_init_exec_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt)
+{
+ struct cmd_chain_req *chain_req = req;
+
+ chain_req->buf_addr = slot_addr;
+ chain_req->buf_size = size;
+ chain_req->count = cmd_cnt;
+}
+
+static void aie2_init_npu_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt)
+{
+ struct cmd_chain_npu_req *npu_chain_req = req;
+
+ npu_chain_req->flags = 0;
+ npu_chain_req->reserved = 0;
+ npu_chain_req->buf_addr = slot_addr;
+ npu_chain_req->buf_size = size;
+ npu_chain_req->count = cmd_cnt;
+}
+
+static int
+aie2_cmdlist_fill_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_execbuf_cf *cf_slot = slot;
+ u32 cmd_len;
+ void *cmd;
+
+ cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (*size < sizeof(*cf_slot) + cmd_len)
+ return -EINVAL;
+
+ cf_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (cf_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ cf_slot->arg_cnt = cmd_len / sizeof(u32);
+ memcpy(cf_slot->args, cmd, cmd_len);
+ /* Accurate slot size to hint firmware to do necessary copy */
+ *size = sizeof(*cf_slot) + cmd_len;
+ return 0;
+}
+
+static int
+aie2_cmdlist_fill_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_dpu *dpu_slot = slot;
+ struct amdxdna_cmd_start_npu *sn;
+ u32 cmd_len;
+ u32 arg_sz;
+
+ sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*sn);
+ if (cmd_len < sizeof(*sn) || arg_sz > MAX_DPU_ARGS_SIZE)
+ return -EINVAL;
+
+ if (*size < sizeof(*dpu_slot) + arg_sz)
+ return -EINVAL;
+
+ dpu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (dpu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ dpu_slot->inst_buf_addr = sn->buffer;
+ dpu_slot->inst_size = sn->buffer_size;
+ dpu_slot->inst_prop_cnt = sn->prop_count;
+ dpu_slot->arg_cnt = arg_sz / sizeof(u32);
+ memcpy(dpu_slot->args, sn->prop_args, arg_sz);
+
+ /* Accurate slot size to hint firmware to do necessary copy */
+ *size = sizeof(*dpu_slot) + arg_sz;
+ return 0;
+}
+
+static int aie2_cmdlist_unsupp(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ return -EOPNOTSUPP;
+}
+
+static u32 aie2_get_chain_msg_op(u32 cmd_op)
+{
+ switch (cmd_op) {
+ case ERT_START_CU:
+ return MSG_OP_CHAIN_EXEC_BUFFER_CF;
+ case ERT_START_NPU:
+ return MSG_OP_CHAIN_EXEC_DPU;
+ default:
+ break;
+ }
+
+ return MSG_OP_MAX_OPCODE;
+}
+
+static struct aie2_exec_msg_ops legacy_exec_message_ops = {
+ .init_cu_req = aie2_init_exec_cu_req,
+ .init_dpu_req = aie2_init_exec_dpu_req,
+ .init_chain_req = aie2_init_exec_chain_req,
+ .fill_cf_slot = aie2_cmdlist_fill_cf,
+ .fill_dpu_slot = aie2_cmdlist_fill_dpu,
+ .fill_preempt_slot = aie2_cmdlist_unsupp,
+ .fill_elf_slot = aie2_cmdlist_unsupp,
+ .get_chain_msg_op = aie2_get_chain_msg_op,
+};
+
+static int
+aie2_cmdlist_fill_npu_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ u32 cmd_len;
+ void *cmd;
+
+ cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (*size < sizeof(*npu_slot) + cmd_len)
+ return -EINVAL;
+
+ npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (npu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_NON_ELF;
+ npu_slot->arg_cnt = cmd_len / sizeof(u32);
+ memcpy(npu_slot->args, cmd, cmd_len);
+
+ *size = sizeof(*npu_slot) + cmd_len;
+ return 0;
+}
+
+static int
+aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ struct amdxdna_cmd_start_npu *sn;
+ u32 cmd_len;
+ u32 arg_sz;
+
+ sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*sn);
+ if (cmd_len < sizeof(*sn) || arg_sz > MAX_NPU_ARGS_SIZE)
+ return -EINVAL;
+
+ if (*size < sizeof(*npu_slot) + arg_sz)
+ return -EINVAL;
+
+ npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (npu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_PARTIAL_ELF;
+ npu_slot->inst_buf_addr = sn->buffer;
+ npu_slot->inst_size = sn->buffer_size;
+ npu_slot->inst_prop_cnt = sn->prop_count;
+ npu_slot->arg_cnt = arg_sz / sizeof(u32);
+ memcpy(npu_slot->args, sn->prop_args, arg_sz);
+
+ *size = sizeof(*npu_slot) + arg_sz;
+ return 0;
+}
+
+static int
+aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ struct amdxdna_cmd_preempt_data *pd;
+ u32 cmd_len;
+ u32 arg_sz;
+
+ pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*pd);
+ if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
+ return -EINVAL;
+
+ if (*size < sizeof(*npu_slot) + arg_sz)
+ return -EINVAL;
+
+ npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (npu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_PREEMPT;
+ npu_slot->inst_buf_addr = pd->inst_buf;
+ npu_slot->save_buf_addr = pd->save_buf;
+ npu_slot->restore_buf_addr = pd->restore_buf;
+ npu_slot->inst_size = pd->inst_size;
+ npu_slot->save_size = pd->save_size;
+ npu_slot->restore_size = pd->restore_size;
+ npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
+ npu_slot->arg_cnt = arg_sz / sizeof(u32);
+ memcpy(npu_slot->args, pd->prop_args, arg_sz);
+
+ *size = sizeof(*npu_slot) + arg_sz;
+ return 0;
+}
+
+static int
+aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ struct amdxdna_cmd_preempt_data *pd;
+ u32 cmd_len;
+ u32 arg_sz;
+
+ pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*pd);
+ if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
+ return -EINVAL;
+
+ if (*size < sizeof(*npu_slot) + arg_sz)
+ return -EINVAL;
+
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_ELF;
+ npu_slot->inst_buf_addr = pd->inst_buf;
+ npu_slot->save_buf_addr = pd->save_buf;
+ npu_slot->restore_buf_addr = pd->restore_buf;
+ npu_slot->inst_size = pd->inst_size;
+ npu_slot->save_size = pd->save_size;
+ npu_slot->restore_size = pd->restore_size;
+ npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
+ npu_slot->arg_cnt = 1;
+ npu_slot->args[0] = AIE2_EXEC_BUFFER_KERNEL_OP_TXN;
+
+ *size = struct_size(npu_slot, args, npu_slot->arg_cnt);
+ return 0;
+}
+
+static u32 aie2_get_npu_chain_msg_op(u32 cmd_op)
+{
+ return MSG_OP_CHAIN_EXEC_NPU;
+}
+
+static struct aie2_exec_msg_ops npu_exec_message_ops = {
+ .init_cu_req = aie2_init_exec_cu_req,
+ .init_dpu_req = aie2_init_exec_dpu_req,
+ .init_chain_req = aie2_init_npu_chain_req,
+ .fill_cf_slot = aie2_cmdlist_fill_npu_cf,
+ .fill_dpu_slot = aie2_cmdlist_fill_npu_dpu,
+ .fill_preempt_slot = aie2_cmdlist_fill_npu_preempt,
+ .fill_elf_slot = aie2_cmdlist_fill_npu_elf,
+ .get_chain_msg_op = aie2_get_npu_chain_msg_op,
+};
+
+static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo,
+ size_t *size, u32 *msg_op)
+{
+ struct amdxdna_dev *xdna = cmd_abo->client->xdna;
+ int ret;
+ u32 op;
+
+
+ op = amdxdna_cmd_get_op(cmd_abo);
+ switch (op) {
+ case ERT_START_CU:
+ ret = EXEC_MSG_OPS(xdna)->init_cu_req(cmd_abo, req, size, msg_op);
+ if (ret) {
+ XDNA_DBG(xdna, "Init CU req failed ret %d", ret);
+ return ret;
+ }
+ break;
+ case ERT_START_NPU:
+ ret = EXEC_MSG_OPS(xdna)->init_dpu_req(cmd_abo, req, size, msg_op);
+ if (ret) {
+ XDNA_DBG(xdna, "Init DPU req failed ret %d", ret);
+ return ret;
+ }
+
+ break;
+ default:
+ XDNA_ERR(xdna, "Unsupported op %d", op);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
+ size_t *size, u32 *cmd_op)
+{
+ struct amdxdna_dev *xdna = cmd_abo->client->xdna;
+ int ret;
+ u32 op;
+
+ op = amdxdna_cmd_get_op(cmd_abo);
+ if (*cmd_op == ERT_INVALID_CMD)
+ *cmd_op = op;
+ else if (op != *cmd_op)
+ return -EINVAL;
+
+ switch (op) {
+ case ERT_START_CU:
+ ret = EXEC_MSG_OPS(xdna)->fill_cf_slot(cmd_abo, slot, size);
+ break;
+ case ERT_START_NPU:
+ ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size);
+ break;
+ case ERT_START_NPU_PREEMPT:
+ if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
+ return -EOPNOTSUPP;
+ ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size);
+ break;
+ case ERT_START_NPU_PREEMPT_ELF:
+ if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
+ return -EOPNOTSUPP;
+ ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size);
+ break;
+ default:
+ XDNA_INFO(xdna, "Unsupported op %d", op);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+void aie2_msg_init(struct amdxdna_dev_hdl *ndev)
+{
+ if (AIE2_FEATURE_ON(ndev, AIE2_NPU_COMMAND))
+ ndev->exec_msg_ops = &npu_exec_message_ops;
+ else
+ ndev->exec_msg_ops = &legacy_exec_message_ops;
+}
+
+static inline struct amdxdna_gem_obj *
+aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
+{
+ int idx = get_job_idx(job->seq);
+
+ return job->hwctx->priv->cmd_buf[idx];
+}
+
+int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
+ struct xdna_mailbox_msg msg;
+ union exec_req req;
+ int ret;
+
+ if (!chann)
+ return -ENODEV;
+
+ ret = aie2_init_exec_req(&req, cmd_abo, &msg.send_size, &msg.opcode);
+ if (ret)
+ return ret;
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ print_hex_dump_debug("cmd: ", DUMP_PREFIX_OFFSET, 16, 4, &req,
+ 0x40, false);
+
+ ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(xdna, "Send message failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
+ struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job);
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_cmd_chain *payload;
+ struct xdna_mailbox_msg msg;
+ union exec_chain_req req;
+ u32 payload_len;
+ u32 offset = 0;
+ size_t size;
+ int ret;
+ u32 op;
+ u32 i;
+
+ op = amdxdna_cmd_get_op(cmd_abo);
+ payload = amdxdna_cmd_get_payload(cmd_abo, &payload_len);
+ if (op != ERT_CMD_CHAIN || !payload ||
+ payload_len < struct_size(payload, data, payload->command_count))
+ return -EINVAL;
+
+ op = ERT_INVALID_CMD;
+ for (i = 0; i < payload->command_count; i++) {
+ u32 boh = (u32)(payload->data[i]);
+ struct amdxdna_gem_obj *abo;
+
+ abo = amdxdna_gem_get_obj(client, boh, AMDXDNA_BO_CMD);
+ if (!abo) {
+ XDNA_ERR(xdna, "Failed to find cmd BO %d", boh);
+ return -ENOENT;
+ }
+
+ size = cmdbuf_abo->mem.size - offset;
+ ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva + offset,
+ abo, &size, &op);
+ amdxdna_gem_put_obj(abo);
+ if (ret)
+ return ret;
+
+ offset += size;
+ }
+ msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op);
+ if (msg.opcode == MSG_OP_MAX_OPCODE)
+ return -EOPNOTSUPP;
+
+ /* The offset is the accumulated total size of the cmd buffer */
+ EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr,
+ offset, payload->command_count);
+ drm_clflush_virt_range(cmdbuf_abo->mem.kva, offset);
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(xdna, "Send message failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
+ struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job);
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
+ struct xdna_mailbox_msg msg;
+ union exec_chain_req req;
+ u32 op = ERT_INVALID_CMD;
+ size_t size;
+ int ret;
+
+ size = cmdbuf_abo->mem.size;
+ ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva, cmd_abo, &size, &op);
+ if (ret)
+ return ret;
+
+ msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op);
+ if (msg.opcode == MSG_OP_MAX_OPCODE)
+ return -EOPNOTSUPP;
+
+ EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr,
+ size, 1);
+ drm_clflush_virt_range(cmdbuf_abo->mem.kva, size);
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(hwctx->client->xdna, "Send message failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]);
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct xdna_mailbox_msg msg;
+ struct sync_bo_req req;
+ int ret = 0;
+
+ req.src_addr = 0;
+ req.dst_addr = amdxdna_dev_bo_offset(abo);
+ req.size = abo->mem.size;
+
+ /* Device to Host */
+ req.type = FIELD_PREP(AIE2_MSG_SYNC_BO_SRC_TYPE, SYNC_BO_DEV_MEM) |
+ FIELD_PREP(AIE2_MSG_SYNC_BO_DST_TYPE, SYNC_BO_HOST_MEM);
+
+ XDNA_DBG(xdna, "sync %d bytes src(0x%llx) to dst(0x%llx) completed",
+ req.size, req.src_addr, req.dst_addr);
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ msg.opcode = MSG_OP_SYNC_BO;
+
+ ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(xdna, "Send message failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]);
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct config_debug_bo_req req;
+ struct xdna_mailbox_msg msg;
+
+ if (job->drv_cmd->opcode == ATTACH_DEBUG_BO)
+ req.config = DEBUG_BO_REGISTER;
+ else
+ req.config = DEBUG_BO_UNREGISTER;
+
+ req.offset = amdxdna_dev_bo_offset(abo);
+ req.size = abo->mem.size;
+
+ XDNA_DBG(xdna, "offset 0x%llx size 0x%llx config %d",
+ req.offset, req.size, req.config);
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ msg.opcode = MSG_OP_CONFIG_DEBUG_BO;
+
+ return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+}
diff --git a/drivers/accel/amdxdna/aie2_msg_priv.h b/drivers/accel/amdxdna/aie2_msg_priv.h
new file mode 100644
index 000000000000..1c957a6298d3
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_msg_priv.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AIE2_MSG_PRIV_H_
+#define _AIE2_MSG_PRIV_H_
+
+enum aie2_msg_opcode {
+ MSG_OP_CREATE_CONTEXT = 0x2,
+ MSG_OP_DESTROY_CONTEXT = 0x3,
+ MSG_OP_GET_TELEMETRY = 0x4,
+ MSG_OP_SYNC_BO = 0x7,
+ MSG_OP_EXECUTE_BUFFER_CF = 0xC,
+ MSG_OP_QUERY_COL_STATUS = 0xD,
+ MSG_OP_QUERY_AIE_TILE_INFO = 0xE,
+ MSG_OP_QUERY_AIE_VERSION = 0xF,
+ MSG_OP_EXEC_DPU = 0x10,
+ MSG_OP_CONFIG_CU = 0x11,
+ MSG_OP_CHAIN_EXEC_BUFFER_CF = 0x12,
+ MSG_OP_CHAIN_EXEC_DPU = 0x13,
+ MSG_OP_CONFIG_DEBUG_BO = 0x14,
+ MSG_OP_CHAIN_EXEC_NPU = 0x18,
+ MSG_OP_MAX_XRT_OPCODE,
+ MSG_OP_SUSPEND = 0x101,
+ MSG_OP_RESUME = 0x102,
+ MSG_OP_ASSIGN_MGMT_PASID = 0x103,
+ MSG_OP_INVOKE_SELF_TEST = 0x104,
+ MSG_OP_MAP_HOST_BUFFER = 0x106,
+ MSG_OP_GET_FIRMWARE_VERSION = 0x108,
+ MSG_OP_SET_RUNTIME_CONFIG = 0x10A,
+ MSG_OP_GET_RUNTIME_CONFIG = 0x10B,
+ MSG_OP_REGISTER_ASYNC_EVENT_MSG = 0x10C,
+ MSG_OP_MAX_DRV_OPCODE,
+ MSG_OP_GET_PROTOCOL_VERSION = 0x301,
+ MSG_OP_MAX_OPCODE
+};
+
+enum aie2_msg_status {
+ AIE2_STATUS_SUCCESS = 0x0,
+ /* AIE Error codes */
+ AIE2_STATUS_AIE_SATURATION_ERROR = 0x1000001,
+ AIE2_STATUS_AIE_FP_ERROR = 0x1000002,
+ AIE2_STATUS_AIE_STREAM_ERROR = 0x1000003,
+ AIE2_STATUS_AIE_ACCESS_ERROR = 0x1000004,
+ AIE2_STATUS_AIE_BUS_ERROR = 0x1000005,
+ AIE2_STATUS_AIE_INSTRUCTION_ERROR = 0x1000006,
+ AIE2_STATUS_AIE_ECC_ERROR = 0x1000007,
+ AIE2_STATUS_AIE_LOCK_ERROR = 0x1000008,
+ AIE2_STATUS_AIE_DMA_ERROR = 0x1000009,
+ AIE2_STATUS_AIE_MEM_PARITY_ERROR = 0x100000a,
+ AIE2_STATUS_AIE_PWR_CFG_ERROR = 0x100000b,
+ AIE2_STATUS_AIE_BACKTRACK_ERROR = 0x100000c,
+ AIE2_STATUS_MAX_AIE_STATUS_CODE,
+ /* MGMT ERT Error codes */
+ AIE2_STATUS_MGMT_ERT_SELF_TEST_FAILURE = 0x2000001,
+ AIE2_STATUS_MGMT_ERT_HASH_MISMATCH,
+ AIE2_STATUS_MGMT_ERT_NOAVAIL,
+ AIE2_STATUS_MGMT_ERT_INVALID_PARAM,
+ AIE2_STATUS_MGMT_ERT_ENTER_SUSPEND_FAILURE,
+ AIE2_STATUS_MGMT_ERT_BUSY,
+ AIE2_STATUS_MGMT_ERT_APPLICATION_ACTIVE,
+ MAX_MGMT_ERT_STATUS_CODE,
+ /* APP ERT Error codes */
+ AIE2_STATUS_APP_ERT_FIRST_ERROR = 0x3000001,
+ AIE2_STATUS_APP_INVALID_INSTR,
+ AIE2_STATUS_APP_LOAD_PDI_FAIL,
+ MAX_APP_ERT_STATUS_CODE,
+ /* NPU RTOS Error Codes */
+ AIE2_STATUS_INVALID_INPUT_BUFFER = 0x4000001,
+ AIE2_STATUS_INVALID_COMMAND,
+ AIE2_STATUS_INVALID_PARAM,
+ AIE2_STATUS_INVALID_OPERATION = 0x4000006,
+ AIE2_STATUS_ASYNC_EVENT_MSGS_FULL,
+ AIE2_STATUS_MAX_RTOS_STATUS_CODE,
+ MAX_AIE2_STATUS_CODE
+};
+
+struct assign_mgmt_pasid_req {
+ __u16 pasid;
+ __u16 reserved;
+} __packed;
+
+struct assign_mgmt_pasid_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct map_host_buffer_req {
+ __u32 context_id;
+ __u64 buf_addr;
+ __u64 buf_size;
+} __packed;
+
+struct map_host_buffer_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+#define MAX_CQ_PAIRS 2
+struct cq_info {
+ __u32 head_addr;
+ __u32 tail_addr;
+ __u32 buf_addr;
+ __u32 buf_size;
+};
+
+struct cq_pair {
+ struct cq_info x2i_q;
+ struct cq_info i2x_q;
+};
+
+struct create_ctx_req {
+ __u32 aie_type;
+ __u8 start_col;
+ __u8 num_col;
+ __u16 reserved;
+ __u8 num_cq_pairs_requested;
+ __u8 reserved1;
+ __u16 pasid;
+ __u32 pad[2];
+ __u32 sec_comm_target_type;
+ __u32 context_priority;
+} __packed;
+
+struct create_ctx_resp {
+ enum aie2_msg_status status;
+ __u32 context_id;
+ __u16 msix_id;
+ __u8 num_cq_pairs_allocated;
+ __u8 reserved;
+ struct cq_pair cq_pair[MAX_CQ_PAIRS];
+} __packed;
+
+struct destroy_ctx_req {
+ __u32 context_id;
+} __packed;
+
+struct destroy_ctx_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+enum telemetry_type {
+ TELEMETRY_TYPE_DISABLED,
+ TELEMETRY_TYPE_HEALTH,
+ TELEMETRY_TYPE_ERROR_INFO,
+ TELEMETRY_TYPE_PROFILING,
+ TELEMETRY_TYPE_DEBUG,
+ MAX_TELEMETRY_TYPE
+};
+
+struct get_telemetry_req {
+ enum telemetry_type type;
+ __u64 buf_addr;
+ __u32 buf_size;
+} __packed;
+
+struct get_telemetry_resp {
+ __u32 major;
+ __u32 minor;
+ __u32 size;
+ enum aie2_msg_status status;
+} __packed;
+
+struct execute_buffer_req {
+ __u32 cu_idx;
+ __u32 payload[19];
+} __packed;
+
+struct exec_dpu_req {
+ __u64 inst_buf_addr;
+ __u32 inst_size;
+ __u32 inst_prop_cnt;
+ __u32 cu_idx;
+ __u32 payload[35];
+} __packed;
+
+enum exec_npu_type {
+ EXEC_NPU_TYPE_NON_ELF = 0x1,
+ EXEC_NPU_TYPE_PARTIAL_ELF = 0x2,
+ EXEC_NPU_TYPE_PREEMPT = 0x3,
+ EXEC_NPU_TYPE_ELF = 0x4,
+};
+
+union exec_req {
+ struct execute_buffer_req ebuf;
+ struct exec_dpu_req dpu_req;
+};
+
+struct execute_buffer_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct aie_tile_info {
+ __u32 size;
+ __u16 major;
+ __u16 minor;
+ __u16 cols;
+ __u16 rows;
+ __u16 core_rows;
+ __u16 mem_rows;
+ __u16 shim_rows;
+ __u16 core_row_start;
+ __u16 mem_row_start;
+ __u16 shim_row_start;
+ __u16 core_dma_channels;
+ __u16 mem_dma_channels;
+ __u16 shim_dma_channels;
+ __u16 core_locks;
+ __u16 mem_locks;
+ __u16 shim_locks;
+ __u16 core_events;
+ __u16 mem_events;
+ __u16 shim_events;
+ __u16 reserved;
+};
+
+struct aie_tile_info_req {
+ __u32 reserved;
+} __packed;
+
+struct aie_tile_info_resp {
+ enum aie2_msg_status status;
+ struct aie_tile_info info;
+} __packed;
+
+struct aie_version_info_req {
+ __u32 reserved;
+} __packed;
+
+struct aie_version_info_resp {
+ enum aie2_msg_status status;
+ __u16 major;
+ __u16 minor;
+} __packed;
+
+struct aie_column_info_req {
+ __u64 dump_buff_addr;
+ __u32 dump_buff_size;
+ __u32 num_cols;
+ __u32 aie_bitmap;
+} __packed;
+
+struct aie_column_info_resp {
+ enum aie2_msg_status status;
+ __u32 size;
+} __packed;
+
+struct suspend_req {
+ __u32 place_holder;
+} __packed;
+
+struct suspend_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct resume_req {
+ __u32 place_holder;
+} __packed;
+
+struct resume_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct check_header_hash_req {
+ __u64 hash_high;
+ __u64 hash_low;
+} __packed;
+
+struct check_header_hash_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct query_error_req {
+ __u64 buf_addr;
+ __u32 buf_size;
+ __u32 next_row;
+ __u32 next_column;
+ __u32 next_module;
+} __packed;
+
+struct query_error_resp {
+ enum aie2_msg_status status;
+ __u32 num_err;
+ __u32 has_next_err;
+ __u32 next_row;
+ __u32 next_column;
+ __u32 next_module;
+} __packed;
+
+struct protocol_version_req {
+ __u32 reserved;
+} __packed;
+
+struct protocol_version_resp {
+ enum aie2_msg_status status;
+ __u32 major;
+ __u32 minor;
+} __packed;
+
+struct firmware_version_req {
+ __u32 reserved;
+} __packed;
+
+struct firmware_version_resp {
+ enum aie2_msg_status status;
+ __u32 major;
+ __u32 minor;
+ __u32 sub;
+ __u32 build;
+} __packed;
+
+#define MAX_NUM_CUS 32
+#define AIE2_MSG_CFG_CU_PDI_ADDR GENMASK(16, 0)
+#define AIE2_MSG_CFG_CU_FUNC GENMASK(24, 17)
+struct config_cu_req {
+ __u32 num_cus;
+ __u32 cfgs[MAX_NUM_CUS];
+} __packed;
+
+struct config_cu_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct set_runtime_cfg_req {
+ __u32 type;
+ __u64 value;
+} __packed;
+
+struct set_runtime_cfg_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+struct get_runtime_cfg_req {
+ __u32 type;
+} __packed;
+
+struct get_runtime_cfg_resp {
+ enum aie2_msg_status status;
+ __u64 value;
+} __packed;
+
+enum async_event_type {
+ ASYNC_EVENT_TYPE_AIE_ERROR,
+ ASYNC_EVENT_TYPE_EXCEPTION,
+ MAX_ASYNC_EVENT_TYPE
+};
+
+#define ASYNC_BUF_SIZE SZ_8K
+struct async_event_msg_req {
+ __u64 buf_addr;
+ __u32 buf_size;
+} __packed;
+
+struct async_event_msg_resp {
+ enum aie2_msg_status status;
+ enum async_event_type type;
+} __packed;
+
+#define MAX_CHAIN_CMDBUF_SIZE SZ_4K
+
+struct cmd_chain_slot_execbuf_cf {
+ __u32 cu_idx;
+ __u32 arg_cnt;
+ __u32 args[] __counted_by(arg_cnt);
+};
+
+struct cmd_chain_slot_dpu {
+ __u64 inst_buf_addr;
+ __u32 inst_size;
+ __u32 inst_prop_cnt;
+ __u32 cu_idx;
+ __u32 arg_cnt;
+#define MAX_DPU_ARGS_SIZE (34 * sizeof(__u32))
+ __u32 args[] __counted_by(arg_cnt);
+};
+
+#define MAX_NPU_ARGS_SIZE (26 * sizeof(__u32))
+#define AIE2_EXEC_BUFFER_KERNEL_OP_TXN 3
+struct cmd_chain_slot_npu {
+ enum exec_npu_type type;
+ u64 inst_buf_addr;
+ u64 save_buf_addr;
+ u64 restore_buf_addr;
+ u32 inst_size;
+ u32 save_size;
+ u32 restore_size;
+ u32 inst_prop_cnt;
+ u32 cu_idx;
+ u32 arg_cnt;
+ u32 args[] __counted_by(arg_cnt);
+} __packed;
+
+struct cmd_chain_req {
+ __u64 buf_addr;
+ __u32 buf_size;
+ __u32 count;
+} __packed;
+
+struct cmd_chain_npu_req {
+ u32 flags;
+ u32 reserved;
+ u64 buf_addr;
+ u32 buf_size;
+ u32 count;
+} __packed;
+
+union exec_chain_req {
+ struct cmd_chain_npu_req npu_req;
+ struct cmd_chain_req req;
+};
+
+struct cmd_chain_resp {
+ enum aie2_msg_status status;
+ __u32 fail_cmd_idx;
+ enum aie2_msg_status fail_cmd_status;
+} __packed;
+
+#define AIE2_MSG_SYNC_BO_SRC_TYPE GENMASK(3, 0)
+#define AIE2_MSG_SYNC_BO_DST_TYPE GENMASK(7, 4)
+struct sync_bo_req {
+ __u64 src_addr;
+ __u64 dst_addr;
+ __u32 size;
+#define SYNC_BO_DEV_MEM 0
+#define SYNC_BO_HOST_MEM 2
+ __u32 type;
+} __packed;
+
+struct sync_bo_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+#define DEBUG_BO_UNREGISTER 0
+#define DEBUG_BO_REGISTER 1
+struct config_debug_bo_req {
+ __u64 offset;
+ __u64 size;
+ /*
+ * config operations.
+ * DEBUG_BO_REGISTER: Register debug buffer
+ * DEBUG_BO_UNREGISTER: Unregister debug buffer
+ */
+ __u32 config;
+} __packed;
+
+struct config_debug_bo_resp {
+ enum aie2_msg_status status;
+} __packed;
+#endif /* _AIE2_MSG_PRIV_H_ */
diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c
new file mode 100644
index 000000000000..ceef1c502e9e
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_pci.c
@@ -0,0 +1,1187 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/cleanup.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+#include <linux/xarray.h>
+
+#include "aie2_msg_priv.h"
+#include "aie2_pci.h"
+#include "aie2_solver.h"
+#include "amdxdna_ctx.h"
+#include "amdxdna_gem.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
+
+static int aie2_max_col = XRS_MAX_COL;
+module_param(aie2_max_col, uint, 0600);
+MODULE_PARM_DESC(aie2_max_col, "Maximum column could be used");
+
+/*
+ * The management mailbox channel is allocated by firmware.
+ * The related register and ring buffer information is on SRAM BAR.
+ * This struct is the register layout.
+ */
+#define MGMT_MBOX_MAGIC 0x55504e5f /* _NPU */
+struct mgmt_mbox_chann_info {
+ __u32 x2i_tail;
+ __u32 x2i_head;
+ __u32 x2i_buf;
+ __u32 x2i_buf_sz;
+ __u32 i2x_tail;
+ __u32 i2x_head;
+ __u32 i2x_buf;
+ __u32 i2x_buf_sz;
+ __u32 magic;
+ __u32 msi_id;
+ __u32 prot_major;
+ __u32 prot_minor;
+ __u32 rsvd[4];
+};
+
+static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor)
+{
+ const struct aie2_fw_feature_tbl *feature;
+ struct amdxdna_dev *xdna = ndev->xdna;
+
+ /*
+ * The driver supported mailbox behavior is defined by
+ * ndev->priv->protocol_major and protocol_minor.
+ *
+ * When protocol_major and fw_major are different, it means driver
+ * and firmware are incompatible.
+ */
+ if (ndev->priv->protocol_major != fw_major) {
+ XDNA_ERR(xdna, "Incompatible firmware protocol major %d minor %d",
+ fw_major, fw_minor);
+ return -EINVAL;
+ }
+
+ /*
+ * When protocol_minor is greater then fw_minor, that means driver
+ * relies on operation the installed firmware does not support.
+ */
+ if (ndev->priv->protocol_minor > fw_minor) {
+ XDNA_ERR(xdna, "Firmware minor version smaller than supported");
+ return -EINVAL;
+ }
+
+ for (feature = ndev->priv->fw_feature_tbl; feature && feature->min_minor;
+ feature++) {
+ if (fw_minor < feature->min_minor)
+ continue;
+ if (feature->max_minor > 0 && fw_minor > feature->max_minor)
+ continue;
+
+ set_bit(feature->feature, &ndev->feature_mask);
+ }
+
+ return 0;
+}
+
+static void aie2_dump_chann_info_debug(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+
+ XDNA_DBG(xdna, "i2x tail 0x%x", ndev->mgmt_i2x.mb_tail_ptr_reg);
+ XDNA_DBG(xdna, "i2x head 0x%x", ndev->mgmt_i2x.mb_head_ptr_reg);
+ XDNA_DBG(xdna, "i2x ringbuf 0x%x", ndev->mgmt_i2x.rb_start_addr);
+ XDNA_DBG(xdna, "i2x rsize 0x%x", ndev->mgmt_i2x.rb_size);
+ XDNA_DBG(xdna, "x2i tail 0x%x", ndev->mgmt_x2i.mb_tail_ptr_reg);
+ XDNA_DBG(xdna, "x2i head 0x%x", ndev->mgmt_x2i.mb_head_ptr_reg);
+ XDNA_DBG(xdna, "x2i ringbuf 0x%x", ndev->mgmt_x2i.rb_start_addr);
+ XDNA_DBG(xdna, "x2i rsize 0x%x", ndev->mgmt_x2i.rb_size);
+ XDNA_DBG(xdna, "x2i chann index 0x%x", ndev->mgmt_chan_idx);
+ XDNA_DBG(xdna, "mailbox protocol major 0x%x", ndev->mgmt_prot_major);
+ XDNA_DBG(xdna, "mailbox protocol minor 0x%x", ndev->mgmt_prot_minor);
+}
+
+static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
+{
+ struct mgmt_mbox_chann_info info_regs;
+ struct xdna_mailbox_chann_res *i2x;
+ struct xdna_mailbox_chann_res *x2i;
+ u32 addr, off;
+ u32 *reg;
+ int ret;
+ int i;
+
+ /*
+ * Once firmware is alive, it will write management channel
+ * information in SRAM BAR and write the address of that information
+ * at FW_ALIVE_OFF offset in SRMA BAR.
+ *
+ * Read a non-zero value from FW_ALIVE_OFF implies that firmware
+ * is alive.
+ */
+ ret = readx_poll_timeout(readl, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF),
+ addr, addr, AIE2_INTERVAL, AIE2_TIMEOUT);
+ if (ret || !addr)
+ return -ETIME;
+
+ off = AIE2_SRAM_OFF(ndev, addr);
+ reg = (u32 *)&info_regs;
+ for (i = 0; i < sizeof(info_regs) / sizeof(u32); i++)
+ reg[i] = readl(ndev->sram_base + off + i * sizeof(u32));
+
+ if (info_regs.magic != MGMT_MBOX_MAGIC) {
+ XDNA_ERR(ndev->xdna, "Invalid mbox magic 0x%x", info_regs.magic);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ i2x = &ndev->mgmt_i2x;
+ x2i = &ndev->mgmt_x2i;
+
+ i2x->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_head);
+ i2x->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_tail);
+ i2x->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.i2x_buf);
+ i2x->rb_size = info_regs.i2x_buf_sz;
+
+ x2i->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.x2i_head);
+ x2i->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.x2i_tail);
+ x2i->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.x2i_buf);
+ x2i->rb_size = info_regs.x2i_buf_sz;
+
+ ndev->mgmt_chan_idx = info_regs.msi_id;
+ ndev->mgmt_prot_major = info_regs.prot_major;
+ ndev->mgmt_prot_minor = info_regs.prot_minor;
+
+ ret = aie2_check_protocol(ndev, ndev->mgmt_prot_major, ndev->mgmt_prot_minor);
+
+done:
+ aie2_dump_chann_info_debug(ndev);
+
+ /* Must clear address at FW_ALIVE_OFF */
+ writel(0, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF));
+
+ return ret;
+}
+
+int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
+ enum rt_config_category category, u32 *val)
+{
+ const struct rt_config *cfg;
+ u32 value;
+ int ret;
+
+ for (cfg = ndev->priv->rt_config; cfg->type; cfg++) {
+ if (cfg->category != category)
+ continue;
+
+ if (cfg->feature_mask &&
+ bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX))
+ continue;
+
+ value = val ? *val : cfg->value;
+ ret = aie2_set_runtime_cfg(ndev, cfg->type, value);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Set type %d value %d failed",
+ cfg->type, value);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aie2_xdna_reset(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ ret = aie2_suspend_fw(ndev);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Suspend firmware failed");
+ return ret;
+ }
+
+ ret = aie2_resume_fw(ndev);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Resume firmware failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_INIT, NULL);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Runtime config failed");
+ return ret;
+ }
+
+ ret = aie2_assign_mgmt_pasid(ndev, 0);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Can not assign PASID");
+ return ret;
+ }
+
+ ret = aie2_xdna_reset(ndev);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Reset firmware failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ ret = aie2_query_firmware_version(ndev, &ndev->xdna->fw_ver);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "query firmware version failed");
+ return ret;
+ }
+
+ ret = aie2_query_aie_version(ndev, &ndev->version);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Query AIE version failed");
+ return ret;
+ }
+
+ ret = aie2_query_aie_metadata(ndev, &ndev->metadata);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Query AIE metadata failed");
+ return ret;
+ }
+
+ ndev->total_col = min(aie2_max_col, ndev->metadata.cols);
+
+ return 0;
+}
+
+static void aie2_mgmt_fw_fini(struct amdxdna_dev_hdl *ndev)
+{
+ if (aie2_suspend_fw(ndev))
+ XDNA_ERR(ndev->xdna, "Suspend_fw failed");
+ XDNA_DBG(ndev->xdna, "Firmware suspended");
+}
+
+static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action)
+{
+ struct amdxdna_hwctx *hwctx = cb_arg;
+ struct amdxdna_dev *xdna;
+ int ret;
+
+ xdna = hwctx->client->xdna;
+
+ hwctx->start_col = action->part.start_col;
+ hwctx->num_col = action->part.ncols;
+ ret = aie2_create_context(xdna->dev_handle, hwctx);
+ if (ret)
+ XDNA_ERR(xdna, "create context failed, ret %d", ret);
+
+ return ret;
+}
+
+static int aie2_xrs_unload(void *cb_arg)
+{
+ struct amdxdna_hwctx *hwctx = cb_arg;
+ struct amdxdna_dev *xdna;
+ int ret;
+
+ xdna = hwctx->client->xdna;
+
+ ret = aie2_destroy_context(xdna->dev_handle, hwctx);
+ if (ret)
+ XDNA_ERR(xdna, "destroy context failed, ret %d", ret);
+
+ return ret;
+}
+
+static int aie2_xrs_set_dft_dpm_level(struct drm_device *ddev, u32 dpm_level)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(ddev);
+ struct amdxdna_dev_hdl *ndev;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ ndev = xdna->dev_handle;
+ ndev->dft_dpm_level = dpm_level;
+ if (ndev->pw_mode != POWER_MODE_DEFAULT || ndev->dpm_level == dpm_level)
+ return 0;
+
+ return ndev->priv->hw_ops.set_dpm(ndev, dpm_level);
+}
+
+static struct xrs_action_ops aie2_xrs_actions = {
+ .load = aie2_xrs_load,
+ .unload = aie2_xrs_unload,
+ .set_dft_dpm_level = aie2_xrs_set_dft_dpm_level,
+};
+
+static void aie2_hw_stop(struct amdxdna_dev *xdna)
+{
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+
+ if (ndev->dev_status <= AIE2_DEV_INIT) {
+ XDNA_ERR(xdna, "device is already stopped");
+ return;
+ }
+
+ aie2_mgmt_fw_fini(ndev);
+ xdna_mailbox_stop_channel(ndev->mgmt_chann);
+ xdna_mailbox_destroy_channel(ndev->mgmt_chann);
+ ndev->mgmt_chann = NULL;
+ drmm_kfree(&xdna->ddev, ndev->mbox);
+ ndev->mbox = NULL;
+ aie2_psp_stop(ndev->psp_hdl);
+ aie2_smu_fini(ndev);
+ aie2_error_async_events_free(ndev);
+ pci_disable_device(pdev);
+
+ ndev->dev_status = AIE2_DEV_INIT;
+}
+
+static int aie2_hw_start(struct amdxdna_dev *xdna)
+{
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ struct xdna_mailbox_res mbox_res;
+ u32 xdna_mailbox_intr_reg;
+ int mgmt_mb_irq, ret;
+
+ if (ndev->dev_status >= AIE2_DEV_START) {
+ XDNA_INFO(xdna, "device is already started");
+ return 0;
+ }
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to enable device, ret %d", ret);
+ return ret;
+ }
+ pci_set_master(pdev);
+
+ ret = aie2_smu_init(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to init smu, ret %d", ret);
+ goto disable_dev;
+ }
+
+ ret = aie2_psp_start(ndev->psp_hdl);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to start psp, ret %d", ret);
+ goto fini_smu;
+ }
+
+ ret = aie2_get_mgmt_chann_info(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "firmware is not alive");
+ goto stop_psp;
+ }
+
+ mbox_res.ringbuf_base = ndev->sram_base;
+ mbox_res.ringbuf_size = pci_resource_len(pdev, xdna->dev_info->sram_bar);
+ mbox_res.mbox_base = ndev->mbox_base;
+ mbox_res.mbox_size = MBOX_SIZE(ndev);
+ mbox_res.name = "xdna_mailbox";
+ ndev->mbox = xdnam_mailbox_create(&xdna->ddev, &mbox_res);
+ if (!ndev->mbox) {
+ XDNA_ERR(xdna, "failed to create mailbox device");
+ ret = -ENODEV;
+ goto stop_psp;
+ }
+
+ mgmt_mb_irq = pci_irq_vector(pdev, ndev->mgmt_chan_idx);
+ if (mgmt_mb_irq < 0) {
+ ret = mgmt_mb_irq;
+ XDNA_ERR(xdna, "failed to alloc irq vector, ret %d", ret);
+ goto stop_psp;
+ }
+
+ xdna_mailbox_intr_reg = ndev->mgmt_i2x.mb_head_ptr_reg + 4;
+ ndev->mgmt_chann = xdna_mailbox_create_channel(ndev->mbox,
+ &ndev->mgmt_x2i,
+ &ndev->mgmt_i2x,
+ xdna_mailbox_intr_reg,
+ mgmt_mb_irq);
+ if (!ndev->mgmt_chann) {
+ XDNA_ERR(xdna, "failed to create management mailbox channel");
+ ret = -EINVAL;
+ goto stop_psp;
+ }
+
+ ret = aie2_pm_init(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to init pm, ret %d", ret);
+ goto destroy_mgmt_chann;
+ }
+
+ ret = aie2_mgmt_fw_init(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "initial mgmt firmware failed, ret %d", ret);
+ goto destroy_mgmt_chann;
+ }
+
+ ret = aie2_mgmt_fw_query(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to query fw, ret %d", ret);
+ goto destroy_mgmt_chann;
+ }
+
+ ret = aie2_error_async_events_alloc(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret);
+ goto destroy_mgmt_chann;
+ }
+
+ ndev->dev_status = AIE2_DEV_START;
+
+ return 0;
+
+destroy_mgmt_chann:
+ xdna_mailbox_stop_channel(ndev->mgmt_chann);
+ xdna_mailbox_destroy_channel(ndev->mgmt_chann);
+stop_psp:
+ aie2_psp_stop(ndev->psp_hdl);
+fini_smu:
+ aie2_smu_fini(ndev);
+disable_dev:
+ pci_disable_device(pdev);
+
+ return ret;
+}
+
+static int aie2_hw_suspend(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_client *client;
+
+ guard(mutex)(&xdna->dev_lock);
+ list_for_each_entry(client, &xdna->client_list, node)
+ aie2_hwctx_suspend(client);
+
+ aie2_hw_stop(xdna);
+
+ return 0;
+}
+
+static int aie2_hw_resume(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_client *client;
+ int ret;
+
+ ret = aie2_hw_start(xdna);
+ if (ret) {
+ XDNA_ERR(xdna, "Start hardware failed, %d", ret);
+ return ret;
+ }
+
+ list_for_each_entry(client, &xdna->client_list, node) {
+ ret = aie2_hwctx_resume(client);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int aie2_init(struct amdxdna_dev *xdna)
+{
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ void __iomem *tbl[PCI_NUM_RESOURCES] = {0};
+ struct init_config xrs_cfg = { 0 };
+ struct amdxdna_dev_hdl *ndev;
+ struct psp_config psp_conf;
+ const struct firmware *fw;
+ unsigned long bars = 0;
+ int i, nvec, ret;
+
+ ndev = drmm_kzalloc(&xdna->ddev, sizeof(*ndev), GFP_KERNEL);
+ if (!ndev)
+ return -ENOMEM;
+
+ ndev->priv = xdna->dev_info->dev_priv;
+ ndev->xdna = xdna;
+
+ ret = request_firmware(&fw, ndev->priv->fw_path, &pdev->dev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to request_firmware %s, ret %d",
+ ndev->priv->fw_path, ret);
+ return ret;
+ }
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ XDNA_ERR(xdna, "pcim enable device failed, ret %d", ret);
+ goto release_fw;
+ }
+
+ for (i = 0; i < PSP_MAX_REGS; i++)
+ set_bit(PSP_REG_BAR(ndev, i), &bars);
+
+ set_bit(xdna->dev_info->sram_bar, &bars);
+ set_bit(xdna->dev_info->smu_bar, &bars);
+ set_bit(xdna->dev_info->mbox_bar, &bars);
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ if (!test_bit(i, &bars))
+ continue;
+ tbl[i] = pcim_iomap(pdev, i, 0);
+ if (!tbl[i]) {
+ XDNA_ERR(xdna, "map bar %d failed", i);
+ ret = -ENOMEM;
+ goto release_fw;
+ }
+ }
+
+ ndev->sram_base = tbl[xdna->dev_info->sram_bar];
+ ndev->smu_base = tbl[xdna->dev_info->smu_bar];
+ ndev->mbox_base = tbl[xdna->dev_info->mbox_bar];
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to set DMA mask: %d", ret);
+ goto release_fw;
+ }
+
+ nvec = pci_msix_vec_count(pdev);
+ if (nvec <= 0) {
+ XDNA_ERR(xdna, "does not get number of interrupt vector");
+ ret = -EINVAL;
+ goto release_fw;
+ }
+
+ ret = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
+ if (ret < 0) {
+ XDNA_ERR(xdna, "failed to alloc irq vectors, ret %d", ret);
+ goto release_fw;
+ }
+
+ psp_conf.fw_size = fw->size;
+ psp_conf.fw_buf = fw->data;
+ for (i = 0; i < PSP_MAX_REGS; i++)
+ psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(ndev, i)] + PSP_REG_OFF(ndev, i);
+ ndev->psp_hdl = aie2m_psp_create(&xdna->ddev, &psp_conf);
+ if (!ndev->psp_hdl) {
+ XDNA_ERR(xdna, "failed to create psp");
+ ret = -ENOMEM;
+ goto release_fw;
+ }
+ xdna->dev_handle = ndev;
+
+ ret = aie2_hw_start(xdna);
+ if (ret) {
+ XDNA_ERR(xdna, "start npu failed, ret %d", ret);
+ goto release_fw;
+ }
+
+ xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1;
+ for (i = 0; i < xrs_cfg.clk_list.num_levels; i++)
+ xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk;
+ xrs_cfg.sys_eff_factor = 1;
+ xrs_cfg.ddev = &xdna->ddev;
+ xrs_cfg.actions = &aie2_xrs_actions;
+ xrs_cfg.total_col = ndev->total_col;
+
+ xdna->xrs_hdl = xrsm_init(&xrs_cfg);
+ if (!xdna->xrs_hdl) {
+ XDNA_ERR(xdna, "Initialize resolver failed");
+ ret = -EINVAL;
+ goto stop_hw;
+ }
+
+ release_firmware(fw);
+ aie2_msg_init(ndev);
+ amdxdna_pm_init(xdna);
+ return 0;
+
+stop_hw:
+ aie2_hw_stop(xdna);
+release_fw:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static void aie2_fini(struct amdxdna_dev *xdna)
+{
+ amdxdna_pm_fini(xdna);
+ aie2_hw_stop(xdna);
+}
+
+static int aie2_get_aie_status(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_aie_status status;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+ int ret;
+
+ ndev = xdna->dev_handle;
+ if (copy_from_user(&status, u64_to_user_ptr(args->buffer), sizeof(status))) {
+ XDNA_ERR(xdna, "Failed to copy AIE request into kernel");
+ return -EFAULT;
+ }
+
+ if (ndev->metadata.cols * ndev->metadata.size < status.buffer_size) {
+ XDNA_ERR(xdna, "Invalid buffer size. Given Size: %u. Need Size: %u.",
+ status.buffer_size, ndev->metadata.cols * ndev->metadata.size);
+ return -EINVAL;
+ }
+
+ ret = aie2_query_status(ndev, u64_to_user_ptr(status.buffer),
+ status.buffer_size, &status.cols_filled);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to get AIE status info. Ret: %d", ret);
+ return ret;
+ }
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &status, sizeof(status))) {
+ XDNA_ERR(xdna, "Failed to copy AIE request info to user space");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int aie2_get_aie_metadata(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_aie_metadata *meta;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+ int ret = 0;
+
+ ndev = xdna->dev_handle;
+ meta = kzalloc(sizeof(*meta), GFP_KERNEL);
+ if (!meta)
+ return -ENOMEM;
+
+ meta->col_size = ndev->metadata.size;
+ meta->cols = ndev->metadata.cols;
+ meta->rows = ndev->metadata.rows;
+
+ meta->version.major = ndev->metadata.version.major;
+ meta->version.minor = ndev->metadata.version.minor;
+
+ meta->core.row_count = ndev->metadata.core.row_count;
+ meta->core.row_start = ndev->metadata.core.row_start;
+ meta->core.dma_channel_count = ndev->metadata.core.dma_channel_count;
+ meta->core.lock_count = ndev->metadata.core.lock_count;
+ meta->core.event_reg_count = ndev->metadata.core.event_reg_count;
+
+ meta->mem.row_count = ndev->metadata.mem.row_count;
+ meta->mem.row_start = ndev->metadata.mem.row_start;
+ meta->mem.dma_channel_count = ndev->metadata.mem.dma_channel_count;
+ meta->mem.lock_count = ndev->metadata.mem.lock_count;
+ meta->mem.event_reg_count = ndev->metadata.mem.event_reg_count;
+
+ meta->shim.row_count = ndev->metadata.shim.row_count;
+ meta->shim.row_start = ndev->metadata.shim.row_start;
+ meta->shim.dma_channel_count = ndev->metadata.shim.dma_channel_count;
+ meta->shim.lock_count = ndev->metadata.shim.lock_count;
+ meta->shim.event_reg_count = ndev->metadata.shim.event_reg_count;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), meta, sizeof(*meta)))
+ ret = -EFAULT;
+
+ kfree(meta);
+ return ret;
+}
+
+static int aie2_get_aie_version(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_aie_version version;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+
+ ndev = xdna->dev_handle;
+ version.major = ndev->version.major;
+ version.minor = ndev->version.minor;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aie2_get_firmware_version(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_firmware_version version;
+ struct amdxdna_dev *xdna = client->xdna;
+
+ version.major = xdna->fw_ver.major;
+ version.minor = xdna->fw_ver.minor;
+ version.patch = xdna->fw_ver.sub;
+ version.build = xdna->fw_ver.build;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aie2_get_power_mode(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_get_power_mode mode = {};
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+
+ ndev = xdna->dev_handle;
+ mode.power_mode = ndev->pw_mode;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, sizeof(mode)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aie2_get_clock_metadata(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_clock_metadata *clock;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+ int ret = 0;
+
+ ndev = xdna->dev_handle;
+ clock = kzalloc(sizeof(*clock), GFP_KERNEL);
+ if (!clock)
+ return -ENOMEM;
+
+ snprintf(clock->mp_npu_clock.name, sizeof(clock->mp_npu_clock.name),
+ "MP-NPU Clock");
+ clock->mp_npu_clock.freq_mhz = ndev->npuclk_freq;
+ snprintf(clock->h_clock.name, sizeof(clock->h_clock.name), "H Clock");
+ clock->h_clock.freq_mhz = ndev->hclk_freq;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), clock, sizeof(*clock)))
+ ret = -EFAULT;
+
+ kfree(clock);
+ return ret;
+}
+
+static int aie2_hwctx_status_cb(struct amdxdna_hwctx *hwctx, void *arg)
+{
+ struct amdxdna_drm_hwctx_entry *tmp __free(kfree) = NULL;
+ struct amdxdna_drm_get_array *array_args = arg;
+ struct amdxdna_drm_hwctx_entry __user *buf;
+ u32 size;
+
+ if (!array_args->num_element)
+ return -EINVAL;
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp->pid = hwctx->client->pid;
+ tmp->context_id = hwctx->id;
+ tmp->start_col = hwctx->start_col;
+ tmp->num_col = hwctx->num_col;
+ tmp->command_submissions = hwctx->priv->seq;
+ tmp->command_completions = hwctx->priv->completed;
+ tmp->pasid = hwctx->client->pasid;
+ tmp->priority = hwctx->qos.priority;
+ tmp->gops = hwctx->qos.gops;
+ tmp->fps = hwctx->qos.fps;
+ tmp->dma_bandwidth = hwctx->qos.dma_bandwidth;
+ tmp->latency = hwctx->qos.latency;
+ tmp->frame_exec_time = hwctx->qos.frame_exec_time;
+ tmp->state = AMDXDNA_HWCTX_STATE_ACTIVE;
+
+ buf = u64_to_user_ptr(array_args->buffer);
+ size = min(sizeof(*tmp), array_args->element_size);
+
+ if (copy_to_user(buf, tmp, size))
+ return -EFAULT;
+
+ array_args->buffer += size;
+ array_args->num_element--;
+
+ return 0;
+}
+
+static int aie2_get_hwctx_status(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_get_array array_args;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_client *tmp_client;
+ int ret;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ array_args.element_size = sizeof(struct amdxdna_drm_query_hwctx);
+ array_args.buffer = args->buffer;
+ array_args.num_element = args->buffer_size / array_args.element_size;
+ list_for_each_entry(tmp_client, &xdna->client_list, node) {
+ ret = amdxdna_hwctx_walk(tmp_client, &array_args,
+ aie2_hwctx_status_cb);
+ if (ret)
+ break;
+ }
+
+ args->buffer_size -= (u32)(array_args.buffer - args->buffer);
+ return 0;
+}
+
+static int aie2_query_resource_info(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_get_resource_info res_info;
+ const struct amdxdna_dev_priv *priv;
+ struct amdxdna_dev_hdl *ndev;
+ struct amdxdna_dev *xdna;
+
+ xdna = client->xdna;
+ ndev = xdna->dev_handle;
+ priv = ndev->priv;
+
+ res_info.npu_clk_max = priv->dpm_clk_tbl[ndev->max_dpm_level].hclk;
+ res_info.npu_tops_max = ndev->max_tops;
+ res_info.npu_task_max = priv->hwctx_limit;
+ res_info.npu_tops_curr = ndev->curr_tops;
+ res_info.npu_task_curr = ndev->hwctx_num;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, sizeof(res_info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aie2_fill_hwctx_map(struct amdxdna_hwctx *hwctx, void *arg)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ u32 *map = arg;
+
+ if (hwctx->fw_ctx_id >= xdna->dev_handle->priv->hwctx_limit) {
+ XDNA_ERR(xdna, "Invalid fw ctx id %d/%d ", hwctx->fw_ctx_id,
+ xdna->dev_handle->priv->hwctx_limit);
+ return -EINVAL;
+ }
+
+ map[hwctx->fw_ctx_id] = hwctx->id;
+ return 0;
+}
+
+static int aie2_get_telemetry(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_telemetry_header *header __free(kfree) = NULL;
+ u32 telemetry_data_sz, header_sz, elem_num;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_client *tmp_client;
+ int ret;
+
+ elem_num = xdna->dev_handle->priv->hwctx_limit;
+ header_sz = struct_size(header, map, elem_num);
+ if (args->buffer_size <= header_sz) {
+ XDNA_ERR(xdna, "Invalid buffer size");
+ return -EINVAL;
+ }
+
+ telemetry_data_sz = args->buffer_size - header_sz;
+ if (telemetry_data_sz > SZ_4M) {
+ XDNA_ERR(xdna, "Buffer size is too big, %d", telemetry_data_sz);
+ return -EINVAL;
+ }
+
+ header = kzalloc(header_sz, GFP_KERNEL);
+ if (!header)
+ return -ENOMEM;
+
+ if (copy_from_user(header, u64_to_user_ptr(args->buffer), sizeof(*header))) {
+ XDNA_ERR(xdna, "Failed to copy telemetry header from user");
+ return -EFAULT;
+ }
+
+ header->map_num_elements = elem_num;
+ list_for_each_entry(tmp_client, &xdna->client_list, node) {
+ ret = amdxdna_hwctx_walk(tmp_client, &header->map,
+ aie2_fill_hwctx_map);
+ if (ret)
+ return ret;
+ }
+
+ ret = aie2_query_telemetry(xdna->dev_handle,
+ u64_to_user_ptr(args->buffer + header_sz),
+ telemetry_data_sz, header);
+ if (ret) {
+ XDNA_ERR(xdna, "Query telemetry failed ret %d", ret);
+ return ret;
+ }
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), header, header_sz)) {
+ XDNA_ERR(xdna, "Copy header failed");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int aie2_get_preempt_state(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_attribute_state state = {};
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+
+ ndev = xdna->dev_handle;
+ if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE)
+ state.state = ndev->force_preempt_enabled;
+ else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE)
+ state.state = ndev->frame_boundary_preempt;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ int ret, idx;
+
+ if (!drm_dev_enter(&xdna->ddev, &idx))
+ return -ENODEV;
+
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto dev_exit;
+
+ switch (args->param) {
+ case DRM_AMDXDNA_QUERY_AIE_STATUS:
+ ret = aie2_get_aie_status(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_AIE_METADATA:
+ ret = aie2_get_aie_metadata(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_AIE_VERSION:
+ ret = aie2_get_aie_version(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_CLOCK_METADATA:
+ ret = aie2_get_clock_metadata(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_HW_CONTEXTS:
+ ret = aie2_get_hwctx_status(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_FIRMWARE_VERSION:
+ ret = aie2_get_firmware_version(client, args);
+ break;
+ case DRM_AMDXDNA_GET_POWER_MODE:
+ ret = aie2_get_power_mode(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_TELEMETRY:
+ ret = aie2_get_telemetry(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_RESOURCE_INFO:
+ ret = aie2_query_resource_info(client, args);
+ break;
+ case DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE:
+ case DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE:
+ ret = aie2_get_preempt_state(client, args);
+ break;
+ default:
+ XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
+ ret = -EOPNOTSUPP;
+ }
+
+ amdxdna_pm_suspend_put(xdna);
+ XDNA_DBG(xdna, "Got param %d", args->param);
+
+dev_exit:
+ drm_dev_exit(idx);
+ return ret;
+}
+
+static int aie2_query_ctx_status_array(struct amdxdna_client *client,
+ struct amdxdna_drm_get_array *args)
+{
+ struct amdxdna_drm_get_array array_args;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_client *tmp_client;
+ int ret;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ if (args->element_size > SZ_4K || args->num_element > SZ_1K) {
+ XDNA_DBG(xdna, "Invalid element size %d or number of element %d",
+ args->element_size, args->num_element);
+ return -EINVAL;
+ }
+
+ array_args.element_size = min(args->element_size,
+ sizeof(struct amdxdna_drm_hwctx_entry));
+ array_args.buffer = args->buffer;
+ array_args.num_element = args->num_element * args->element_size /
+ array_args.element_size;
+ list_for_each_entry(tmp_client, &xdna->client_list, node) {
+ ret = amdxdna_hwctx_walk(tmp_client, &array_args,
+ aie2_hwctx_status_cb);
+ if (ret)
+ break;
+ }
+
+ args->element_size = array_args.element_size;
+ args->num_element = (u32)((array_args.buffer - args->buffer) /
+ args->element_size);
+
+ return 0;
+}
+
+static int aie2_get_array(struct amdxdna_client *client,
+ struct amdxdna_drm_get_array *args)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ int ret, idx;
+
+ if (!drm_dev_enter(&xdna->ddev, &idx))
+ return -ENODEV;
+
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto dev_exit;
+
+ switch (args->param) {
+ case DRM_AMDXDNA_HW_CONTEXT_ALL:
+ ret = aie2_query_ctx_status_array(client, args);
+ break;
+ case DRM_AMDXDNA_HW_LAST_ASYNC_ERR:
+ ret = aie2_get_array_async_error(xdna->dev_handle, args);
+ break;
+ default:
+ XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
+ ret = -EOPNOTSUPP;
+ }
+
+ amdxdna_pm_suspend_put(xdna);
+ XDNA_DBG(xdna, "Got param %d", args->param);
+
+dev_exit:
+ drm_dev_exit(idx);
+ return ret;
+}
+
+static int aie2_set_power_mode(struct amdxdna_client *client,
+ struct amdxdna_drm_set_state *args)
+{
+ struct amdxdna_drm_set_power_mode power_state;
+ enum amdxdna_power_mode_type power_mode;
+ struct amdxdna_dev *xdna = client->xdna;
+
+ if (copy_from_user(&power_state, u64_to_user_ptr(args->buffer),
+ sizeof(power_state))) {
+ XDNA_ERR(xdna, "Failed to copy power mode request into kernel");
+ return -EFAULT;
+ }
+
+ if (XDNA_MBZ_DBG(xdna, power_state.pad, sizeof(power_state.pad)))
+ return -EINVAL;
+
+ power_mode = power_state.power_mode;
+ if (power_mode > POWER_MODE_TURBO) {
+ XDNA_ERR(xdna, "Invalid power mode %d", power_mode);
+ return -EINVAL;
+ }
+
+ return aie2_pm_set_mode(xdna->dev_handle, power_mode);
+}
+
+static int aie2_set_preempt_state(struct amdxdna_client *client,
+ struct amdxdna_drm_set_state *args)
+{
+ struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle;
+ struct amdxdna_drm_attribute_state state;
+ u32 val;
+ int ret;
+
+ if (copy_from_user(&state, u64_to_user_ptr(args->buffer), sizeof(state)))
+ return -EFAULT;
+
+ if (state.state > 1)
+ return -EINVAL;
+
+ if (XDNA_MBZ_DBG(client->xdna, state.pad, sizeof(state.pad)))
+ return -EINVAL;
+
+ if (args->param == DRM_AMDXDNA_SET_FORCE_PREEMPT) {
+ ndev->force_preempt_enabled = state.state;
+ } else if (args->param == DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT) {
+ val = state.state;
+ ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
+ &val);
+ if (ret)
+ return ret;
+
+ ndev->frame_boundary_preempt = state.state;
+ }
+
+ return 0;
+}
+
+static int aie2_set_state(struct amdxdna_client *client,
+ struct amdxdna_drm_set_state *args)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ int ret, idx;
+
+ if (!drm_dev_enter(&xdna->ddev, &idx))
+ return -ENODEV;
+
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto dev_exit;
+
+ switch (args->param) {
+ case DRM_AMDXDNA_SET_POWER_MODE:
+ ret = aie2_set_power_mode(client, args);
+ break;
+ case DRM_AMDXDNA_SET_FORCE_PREEMPT:
+ case DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT:
+ ret = aie2_set_preempt_state(client, args);
+ break;
+ default:
+ XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ amdxdna_pm_suspend_put(xdna);
+dev_exit:
+ drm_dev_exit(idx);
+ return ret;
+}
+
+const struct amdxdna_dev_ops aie2_ops = {
+ .init = aie2_init,
+ .fini = aie2_fini,
+ .resume = aie2_hw_resume,
+ .suspend = aie2_hw_suspend,
+ .get_aie_info = aie2_get_info,
+ .set_aie_state = aie2_set_state,
+ .hwctx_init = aie2_hwctx_init,
+ .hwctx_fini = aie2_hwctx_fini,
+ .hwctx_config = aie2_hwctx_config,
+ .hwctx_sync_debug_bo = aie2_hwctx_sync_debug_bo,
+ .cmd_submit = aie2_cmd_submit,
+ .hmm_invalidate = aie2_hmm_invalidate,
+ .get_array = aie2_get_array,
+};
diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h
new file mode 100644
index 000000000000..a5f9c42155d1
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_pci.h
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AIE2_PCI_H_
+#define _AIE2_PCI_H_
+
+#include <drm/amdxdna_accel.h>
+#include <linux/semaphore.h>
+
+#include "amdxdna_mailbox.h"
+
+#define AIE2_INTERVAL 20000 /* us */
+#define AIE2_TIMEOUT 1000000 /* us */
+
+/* Firmware determines device memory base address and size */
+#define AIE2_DEVM_BASE 0x4000000
+#define AIE2_DEVM_SIZE SZ_64M
+
+#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->xdna->ddev.dev))
+
+#define AIE2_SRAM_OFF(ndev, addr) ((addr) - (ndev)->priv->sram_dev_addr)
+#define AIE2_MBOX_OFF(ndev, addr) ((addr) - (ndev)->priv->mbox_dev_addr)
+
+#define PSP_REG_BAR(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].bar_idx)
+#define PSP_REG_OFF(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].offset)
+#define SRAM_REG_OFF(ndev, idx) ((ndev)->priv->sram_offs[(idx)].offset)
+
+#define SMU_REG(ndev, idx) \
+({ \
+ typeof(ndev) _ndev = ndev; \
+ ((_ndev)->smu_base + (_ndev)->priv->smu_regs_off[(idx)].offset); \
+})
+#define SRAM_GET_ADDR(ndev, idx) \
+({ \
+ typeof(ndev) _ndev = ndev; \
+ ((_ndev)->sram_base + SRAM_REG_OFF((_ndev), (idx))); \
+})
+
+#define CHAN_SLOT_SZ SZ_8K
+#define MBOX_SIZE(ndev) \
+({ \
+ typeof(ndev) _ndev = (ndev); \
+ ((_ndev)->priv->mbox_size) ? (_ndev)->priv->mbox_size : \
+ pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->xdna->dev_info->mbox_bar); \
+})
+
+enum aie2_smu_reg_idx {
+ SMU_CMD_REG = 0,
+ SMU_ARG_REG,
+ SMU_INTR_REG,
+ SMU_RESP_REG,
+ SMU_OUT_REG,
+ SMU_MAX_REGS /* Keep this at the end */
+};
+
+enum aie2_sram_reg_idx {
+ MBOX_CHANN_OFF = 0,
+ FW_ALIVE_OFF,
+ SRAM_MAX_INDEX /* Keep this at the end */
+};
+
+enum psp_reg_idx {
+ PSP_CMD_REG = 0,
+ PSP_ARG0_REG,
+ PSP_ARG1_REG,
+ PSP_ARG2_REG,
+ PSP_NUM_IN_REGS, /* number of input registers */
+ PSP_INTR_REG = PSP_NUM_IN_REGS,
+ PSP_STATUS_REG,
+ PSP_RESP_REG,
+ PSP_MAX_REGS /* Keep this at the end */
+};
+
+struct amdxdna_client;
+struct amdxdna_fw_ver;
+struct amdxdna_hwctx;
+struct amdxdna_sched_job;
+
+struct psp_config {
+ const void *fw_buf;
+ u32 fw_size;
+ void __iomem *psp_regs[PSP_MAX_REGS];
+};
+
+struct aie_version {
+ u16 major;
+ u16 minor;
+};
+
+struct aie_tile_metadata {
+ u16 row_count;
+ u16 row_start;
+ u16 dma_channel_count;
+ u16 lock_count;
+ u16 event_reg_count;
+};
+
+struct aie_metadata {
+ u32 size;
+ u16 cols;
+ u16 rows;
+ struct aie_version version;
+ struct aie_tile_metadata core;
+ struct aie_tile_metadata mem;
+ struct aie_tile_metadata shim;
+};
+
+enum rt_config_category {
+ AIE2_RT_CFG_INIT,
+ AIE2_RT_CFG_CLK_GATING,
+ AIE2_RT_CFG_FORCE_PREEMPT,
+ AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
+};
+
+struct rt_config {
+ u32 type;
+ u32 value;
+ u32 category;
+ unsigned long feature_mask;
+};
+
+struct dpm_clk_freq {
+ u32 npuclk;
+ u32 hclk;
+};
+
+/*
+ * Define the maximum number of pending commands in a hardware context.
+ * Must be power of 2!
+ */
+#define HWCTX_MAX_CMDS 4
+#define get_job_idx(seq) ((seq) & (HWCTX_MAX_CMDS - 1))
+struct amdxdna_hwctx_priv {
+ struct amdxdna_gem_obj *heap;
+ void *mbox_chann;
+
+ struct drm_gpu_scheduler sched;
+ struct drm_sched_entity entity;
+
+ struct mutex io_lock; /* protect seq and cmd order */
+ struct wait_queue_head job_free_wq;
+ u32 num_pending;
+ u64 seq;
+ struct semaphore job_sem;
+ bool job_done;
+
+ /* Completed job counter */
+ u64 completed;
+
+ struct amdxdna_gem_obj *cmd_buf[HWCTX_MAX_CMDS];
+ struct drm_syncobj *syncobj;
+};
+
+enum aie2_dev_status {
+ AIE2_DEV_UNINIT,
+ AIE2_DEV_INIT,
+ AIE2_DEV_START,
+};
+
+struct aie2_exec_msg_ops {
+ int (*init_cu_req)(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op);
+ int (*init_dpu_req)(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op);
+ void (*init_chain_req)(void *req, u64 slot_addr, size_t size, u32 cmd_cnt);
+ int (*fill_cf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ int (*fill_dpu_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ int (*fill_preempt_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ int (*fill_elf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ u32 (*get_chain_msg_op)(u32 cmd_op);
+};
+
+struct amdxdna_dev_hdl {
+ struct amdxdna_dev *xdna;
+ const struct amdxdna_dev_priv *priv;
+ void __iomem *sram_base;
+ void __iomem *smu_base;
+ void __iomem *mbox_base;
+ struct psp_device *psp_hdl;
+
+ struct xdna_mailbox_chann_res mgmt_x2i;
+ struct xdna_mailbox_chann_res mgmt_i2x;
+ u32 mgmt_chan_idx;
+ u32 mgmt_prot_major;
+ u32 mgmt_prot_minor;
+
+ u32 total_col;
+ struct aie_version version;
+ struct aie_metadata metadata;
+ unsigned long feature_mask;
+ struct aie2_exec_msg_ops *exec_msg_ops;
+
+ /* power management and clock*/
+ enum amdxdna_power_mode_type pw_mode;
+ u32 dpm_level;
+ u32 dft_dpm_level;
+ u32 max_dpm_level;
+ u32 clk_gating;
+ u32 npuclk_freq;
+ u32 hclk_freq;
+ u32 max_tops;
+ u32 curr_tops;
+ u32 force_preempt_enabled;
+ u32 frame_boundary_preempt;
+
+ /* Mailbox and the management channel */
+ struct mailbox *mbox;
+ struct mailbox_channel *mgmt_chann;
+ struct async_events *async_events;
+
+ enum aie2_dev_status dev_status;
+ u32 hwctx_num;
+
+ struct amdxdna_async_error last_async_err;
+};
+
+#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
+ [reg_name] = {bar##_BAR_INDEX, (reg_addr) - bar##_BAR_BASE}
+
+struct aie2_bar_off_pair {
+ int bar_idx;
+ u32 offset;
+};
+
+struct aie2_hw_ops {
+ int (*set_dpm)(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
+};
+
+enum aie2_fw_feature {
+ AIE2_NPU_COMMAND,
+ AIE2_PREEMPT,
+ AIE2_FEATURE_MAX
+};
+
+struct aie2_fw_feature_tbl {
+ enum aie2_fw_feature feature;
+ u32 max_minor;
+ u32 min_minor;
+};
+
+#define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask)
+
+struct amdxdna_dev_priv {
+ const char *fw_path;
+ u64 protocol_major;
+ u64 protocol_minor;
+ const struct rt_config *rt_config;
+ const struct dpm_clk_freq *dpm_clk_tbl;
+ const struct aie2_fw_feature_tbl *fw_feature_tbl;
+
+#define COL_ALIGN_NONE 0
+#define COL_ALIGN_NATURE 1
+ u32 col_align;
+ u32 mbox_dev_addr;
+ /* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */
+ u32 mbox_size;
+ u32 hwctx_limit;
+ u32 sram_dev_addr;
+ struct aie2_bar_off_pair sram_offs[SRAM_MAX_INDEX];
+ struct aie2_bar_off_pair psp_regs_off[PSP_MAX_REGS];
+ struct aie2_bar_off_pair smu_regs_off[SMU_MAX_REGS];
+ struct aie2_hw_ops hw_ops;
+};
+
+extern const struct amdxdna_dev_ops aie2_ops;
+
+int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
+ enum rt_config_category category, u32 *val);
+
+/* aie2 npu hw config */
+extern const struct dpm_clk_freq npu1_dpm_clk_table[];
+extern const struct dpm_clk_freq npu4_dpm_clk_table[];
+extern const struct rt_config npu1_default_rt_cfg[];
+extern const struct rt_config npu4_default_rt_cfg[];
+extern const struct aie2_fw_feature_tbl npu4_fw_feature_table[];
+
+/* aie2_smu.c */
+int aie2_smu_init(struct amdxdna_dev_hdl *ndev);
+void aie2_smu_fini(struct amdxdna_dev_hdl *ndev);
+int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
+int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
+
+/* aie2_pm.c */
+int aie2_pm_init(struct amdxdna_dev_hdl *ndev);
+int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target);
+
+/* aie2_psp.c */
+struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf);
+int aie2_psp_start(struct psp_device *psp);
+void aie2_psp_stop(struct psp_device *psp);
+
+/* aie2_error.c */
+int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev);
+void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev);
+int aie2_error_async_msg_thread(void *data);
+int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev,
+ struct amdxdna_drm_get_array *args);
+
+/* aie2_message.c */
+void aie2_msg_init(struct amdxdna_dev_hdl *ndev);
+int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev);
+int aie2_resume_fw(struct amdxdna_dev_hdl *ndev);
+int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value);
+int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value);
+int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid);
+int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version);
+int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata);
+int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
+ struct amdxdna_fw_ver *fw_ver);
+int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
+int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
+int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
+int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled);
+int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
+ char __user *buf, u32 size,
+ struct amdxdna_drm_query_telemetry_header *header);
+int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
+ void *handle, int (*cb)(void*, void __iomem *, size_t));
+int aie2_config_cu(struct amdxdna_hwctx *hwctx,
+ int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
+ struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
+ struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t));
+
+/* aie2_hwctx.c */
+int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
+void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
+int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
+int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
+void aie2_hwctx_suspend(struct amdxdna_client *client);
+int aie2_hwctx_resume(struct amdxdna_client *client);
+int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
+void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
+
+#endif /* _AIE2_PCI_H_ */
diff --git a/drivers/accel/amdxdna/aie2_pm.c b/drivers/accel/amdxdna/aie2_pm.c
new file mode 100644
index 000000000000..426c38fce848
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_pm.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_pci_drv.h"
+
+#define AIE2_CLK_GATING_ENABLE 1
+#define AIE2_CLK_GATING_DISABLE 0
+
+static int aie2_pm_set_clk_gating(struct amdxdna_dev_hdl *ndev, u32 val)
+{
+ int ret;
+
+ ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_CLK_GATING, &val);
+ if (ret)
+ return ret;
+
+ ndev->clk_gating = val;
+ return 0;
+}
+
+int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ if (ndev->dev_status != AIE2_DEV_UNINIT) {
+ /* Resume device */
+ ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->dpm_level);
+ if (ret)
+ return ret;
+
+ ret = aie2_pm_set_clk_gating(ndev, ndev->clk_gating);
+ if (ret)
+ return ret;
+
+ return 0;
+ }
+
+ while (ndev->priv->dpm_clk_tbl[ndev->max_dpm_level].hclk)
+ ndev->max_dpm_level++;
+ ndev->max_dpm_level--;
+
+ ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->max_dpm_level);
+ if (ret)
+ return ret;
+
+ ret = aie2_pm_set_clk_gating(ndev, AIE2_CLK_GATING_ENABLE);
+ if (ret)
+ return ret;
+
+ ndev->pw_mode = POWER_MODE_DEFAULT;
+ ndev->dft_dpm_level = ndev->max_dpm_level;
+
+ return 0;
+}
+
+int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+ u32 clk_gating, dpm_level;
+ int ret;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ if (ndev->pw_mode == target)
+ return 0;
+
+ switch (target) {
+ case POWER_MODE_TURBO:
+ if (ndev->hwctx_num) {
+ XDNA_ERR(xdna, "Can not set turbo when there is active hwctx");
+ return -EINVAL;
+ }
+
+ clk_gating = AIE2_CLK_GATING_DISABLE;
+ dpm_level = ndev->max_dpm_level;
+ break;
+ case POWER_MODE_HIGH:
+ clk_gating = AIE2_CLK_GATING_ENABLE;
+ dpm_level = ndev->max_dpm_level;
+ break;
+ case POWER_MODE_DEFAULT:
+ clk_gating = AIE2_CLK_GATING_ENABLE;
+ dpm_level = ndev->dft_dpm_level;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level);
+ if (ret)
+ return ret;
+
+ ret = aie2_pm_set_clk_gating(ndev, clk_gating);
+ if (ret)
+ return ret;
+
+ ndev->pw_mode = target;
+
+ return 0;
+}
diff --git a/drivers/accel/amdxdna/aie2_psp.c b/drivers/accel/amdxdna/aie2_psp.c
new file mode 100644
index 000000000000..f28a060a8810
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_psp.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/drm_device.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+#define PSP_STATUS_READY BIT(31)
+
+/* PSP commands */
+#define PSP_VALIDATE 1
+#define PSP_START 2
+#define PSP_RELEASE_TMR 3
+
+/* PSP special arguments */
+#define PSP_START_COPY_FW 1
+
+/* PSP response error code */
+#define PSP_ERROR_CANCEL 0xFFFF0002
+#define PSP_ERROR_BAD_STATE 0xFFFF0007
+
+#define PSP_FW_ALIGN 0x10000
+#define PSP_POLL_INTERVAL 20000 /* us */
+#define PSP_POLL_TIMEOUT 1000000 /* us */
+
+#define PSP_REG(p, reg) ((p)->psp_regs[reg])
+
+struct psp_device {
+ struct drm_device *ddev;
+ struct psp_config conf;
+ u32 fw_buf_sz;
+ u64 fw_paddr;
+ void *fw_buffer;
+ void __iomem *psp_regs[PSP_MAX_REGS];
+};
+
+static int psp_exec(struct psp_device *psp, u32 *reg_vals)
+{
+ u32 resp_code;
+ int ret, i;
+ u32 ready;
+
+ /* Write command and argument registers */
+ for (i = 0; i < PSP_NUM_IN_REGS; i++)
+ writel(reg_vals[i], PSP_REG(psp, i));
+
+ /* clear and set PSP INTR register to kick off */
+ writel(0, PSP_REG(psp, PSP_INTR_REG));
+ writel(1, PSP_REG(psp, PSP_INTR_REG));
+
+ /* PSP should be busy. Wait for ready, so we know task is done. */
+ ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
+ FIELD_GET(PSP_STATUS_READY, ready),
+ PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
+ if (ret) {
+ drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
+ return ret;
+ }
+
+ resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
+ if (resp_code) {
+ drm_err(psp->ddev, "fw return error 0x%x", resp_code);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void aie2_psp_stop(struct psp_device *psp)
+{
+ u32 reg_vals[PSP_NUM_IN_REGS] = { PSP_RELEASE_TMR, };
+ int ret;
+
+ ret = psp_exec(psp, reg_vals);
+ if (ret)
+ drm_err(psp->ddev, "release tmr failed, ret %d", ret);
+}
+
+int aie2_psp_start(struct psp_device *psp)
+{
+ u32 reg_vals[PSP_NUM_IN_REGS];
+ int ret;
+
+ reg_vals[0] = PSP_VALIDATE;
+ reg_vals[1] = lower_32_bits(psp->fw_paddr);
+ reg_vals[2] = upper_32_bits(psp->fw_paddr);
+ reg_vals[3] = psp->fw_buf_sz;
+
+ ret = psp_exec(psp, reg_vals);
+ if (ret) {
+ drm_err(psp->ddev, "failed to validate fw, ret %d", ret);
+ return ret;
+ }
+
+ memset(reg_vals, 0, sizeof(reg_vals));
+ reg_vals[0] = PSP_START;
+ reg_vals[1] = PSP_START_COPY_FW;
+ ret = psp_exec(psp, reg_vals);
+ if (ret) {
+ drm_err(psp->ddev, "failed to start fw, ret %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf)
+{
+ struct psp_device *psp;
+ u64 offset;
+
+ psp = drmm_kzalloc(ddev, sizeof(*psp), GFP_KERNEL);
+ if (!psp)
+ return NULL;
+
+ psp->ddev = ddev;
+ memcpy(psp->psp_regs, conf->psp_regs, sizeof(psp->psp_regs));
+
+ psp->fw_buf_sz = ALIGN(conf->fw_size, PSP_FW_ALIGN);
+ psp->fw_buffer = drmm_kmalloc(ddev, psp->fw_buf_sz + PSP_FW_ALIGN, GFP_KERNEL);
+ if (!psp->fw_buffer) {
+ drm_err(ddev, "no memory for fw buffer");
+ return NULL;
+ }
+
+ /*
+ * AMD Platform Security Processor(PSP) requires host physical
+ * address to load NPU firmware.
+ */
+ psp->fw_paddr = virt_to_phys(psp->fw_buffer);
+ offset = ALIGN(psp->fw_paddr, PSP_FW_ALIGN) - psp->fw_paddr;
+ psp->fw_paddr += offset;
+ memcpy(psp->fw_buffer + offset, conf->fw_buf, conf->fw_size);
+
+ return psp;
+}
diff --git a/drivers/accel/amdxdna/aie2_smu.c b/drivers/accel/amdxdna/aie2_smu.c
new file mode 100644
index 000000000000..bd94ee96c2bc
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_smu.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/drm_device.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/iopoll.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
+
+#define SMU_RESULT_OK 1
+
+/* SMU commands */
+#define AIE2_SMU_POWER_ON 0x3
+#define AIE2_SMU_POWER_OFF 0x4
+#define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5
+#define AIE2_SMU_SET_HCLK_FREQ 0x6
+#define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7
+#define AIE2_SMU_SET_HARD_DPMLEVEL 0x8
+
+#define NPU4_DPM_TOPS(ndev, dpm_level) \
+({ \
+ typeof(ndev) _ndev = ndev; \
+ (4096 * (_ndev)->total_col * \
+ (_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
+})
+
+static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
+ u32 reg_arg, u32 *out)
+{
+ u32 resp;
+ int ret;
+
+ writel(0, SMU_REG(ndev, SMU_RESP_REG));
+ writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG));
+ writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG));
+
+ /* Clear and set SMU_INTR_REG to kick off */
+ writel(0, SMU_REG(ndev, SMU_INTR_REG));
+ writel(1, SMU_REG(ndev, SMU_INTR_REG));
+
+ ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp,
+ resp, AIE2_INTERVAL, AIE2_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd);
+ return ret;
+ }
+
+ if (out)
+ *out = readl(SMU_REG(ndev, SMU_OUT_REG));
+
+ if (resp != SMU_RESULT_OK) {
+ XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
+{
+ u32 freq;
+ int ret;
+
+ ret = amdxdna_pm_resume_get(ndev->xdna);
+ if (ret)
+ return ret;
+
+ ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
+ ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
+ ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
+ goto suspend_put;
+ }
+ ndev->npuclk_freq = freq;
+
+ ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
+ ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
+ ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
+ goto suspend_put;
+ }
+
+ amdxdna_pm_suspend_put(ndev->xdna);
+ ndev->hclk_freq = freq;
+ ndev->dpm_level = dpm_level;
+ ndev->max_tops = 2 * ndev->total_col;
+ ndev->curr_tops = ndev->max_tops * freq / 1028;
+
+ XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
+ ndev->npuclk_freq, ndev->hclk_freq);
+
+ return 0;
+
+suspend_put:
+ amdxdna_pm_suspend_put(ndev->xdna);
+ return ret;
+}
+
+int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
+{
+ int ret;
+
+ ret = amdxdna_pm_resume_get(ndev->xdna);
+ if (ret)
+ return ret;
+
+ ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
+ dpm_level, ret);
+ goto suspend_put;
+ }
+
+ ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
+ dpm_level, ret);
+ goto suspend_put;
+ }
+
+ amdxdna_pm_suspend_put(ndev->xdna);
+ ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
+ ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
+ ndev->dpm_level = dpm_level;
+ ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
+ ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
+
+ XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
+ ndev->npuclk_freq, ndev->hclk_freq);
+
+ return 0;
+
+suspend_put:
+ amdxdna_pm_suspend_put(ndev->xdna);
+ return ret;
+}
+
+int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ /*
+ * Failing to set power off indicates an unrecoverable hardware or
+ * firmware error.
+ */
+ ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
+ return ret;
+ }
+
+ ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ ndev->priv->hw_ops.set_dpm(ndev, 0);
+ ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
+ if (ret)
+ XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret);
+}
diff --git a/drivers/accel/amdxdna/aie2_solver.c b/drivers/accel/amdxdna/aie2_solver.c
new file mode 100644
index 000000000000..2013d1f13aae
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_solver.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/drm_device.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <linux/bitops.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+
+#include "aie2_solver.h"
+
+struct partition_node {
+ struct list_head list;
+ u32 nshared; /* # shared requests */
+ u32 start_col; /* start column */
+ u32 ncols; /* # columns */
+ bool exclusive; /* can not be shared if set */
+};
+
+struct solver_node {
+ struct list_head list;
+ u64 rid; /* Request ID from consumer */
+
+ struct partition_node *pt_node;
+ void *cb_arg;
+ u32 dpm_level;
+ u32 cols_len;
+ u32 start_cols[] __counted_by(cols_len);
+};
+
+struct solver_rgroup {
+ u32 rgid;
+ u32 nnode;
+ u32 npartition_node;
+
+ DECLARE_BITMAP(resbit, XRS_MAX_COL);
+ struct list_head node_list;
+ struct list_head pt_node_list;
+};
+
+struct solver_state {
+ struct solver_rgroup rgp;
+ struct init_config cfg;
+ struct xrs_action_ops *actions;
+};
+
+static u32 calculate_gops(struct aie_qos *rqos)
+{
+ u32 service_rate = 0;
+
+ if (rqos->latency)
+ service_rate = (1000 / rqos->latency);
+
+ if (rqos->fps > service_rate)
+ return rqos->fps * rqos->gops;
+
+ return service_rate * rqos->gops;
+}
+
+/*
+ * qos_meet() - Check the QOS request can be met.
+ */
+static int qos_meet(struct solver_state *xrs, struct aie_qos *rqos, u32 cgops)
+{
+ u32 request_gops = calculate_gops(rqos) * xrs->cfg.sys_eff_factor;
+
+ if (request_gops <= cgops)
+ return 0;
+
+ return -EINVAL;
+}
+
+/*
+ * sanity_check() - Do a basic sanity check on allocation request.
+ */
+static int sanity_check(struct solver_state *xrs, struct alloc_requests *req)
+{
+ struct cdo_parts *cdop = &req->cdo;
+ struct aie_qos *rqos = &req->rqos;
+ u32 cu_clk_freq;
+
+ if (cdop->ncols > xrs->cfg.total_col)
+ return -EINVAL;
+
+ /*
+ * We can find at least one CDOs groups that meet the
+ * GOPs requirement.
+ */
+ cu_clk_freq = xrs->cfg.clk_list.cu_clk_list[xrs->cfg.clk_list.num_levels - 1];
+
+ if (qos_meet(xrs, rqos, cdop->qos_cap.opc * cu_clk_freq / 1000))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool is_valid_qos_dpm_params(struct aie_qos *rqos)
+{
+ /*
+ * gops is retrieved from the xmodel, so it's always set
+ * fps and latency are the configurable params from the application
+ */
+ if (rqos->gops > 0 && (rqos->fps > 0 || rqos->latency > 0))
+ return true;
+
+ return false;
+}
+
+static int set_dpm_level(struct solver_state *xrs, struct alloc_requests *req, u32 *dpm_level)
+{
+ struct solver_rgroup *rgp = &xrs->rgp;
+ struct cdo_parts *cdop = &req->cdo;
+ struct aie_qos *rqos = &req->rqos;
+ u32 freq, max_dpm_level, level;
+ struct solver_node *node;
+
+ max_dpm_level = xrs->cfg.clk_list.num_levels - 1;
+ /* If no QoS parameters are passed, set it to the max DPM level */
+ if (!is_valid_qos_dpm_params(rqos)) {
+ level = max_dpm_level;
+ goto set_dpm;
+ }
+
+ /* Find one CDO group that meet the GOPs requirement. */
+ for (level = 0; level < max_dpm_level; level++) {
+ freq = xrs->cfg.clk_list.cu_clk_list[level];
+ if (!qos_meet(xrs, rqos, cdop->qos_cap.opc * freq / 1000))
+ break;
+ }
+
+ /* set the dpm level which fits all the sessions */
+ list_for_each_entry(node, &rgp->node_list, list) {
+ if (node->dpm_level > level)
+ level = node->dpm_level;
+ }
+
+set_dpm:
+ *dpm_level = level;
+ return xrs->cfg.actions->set_dft_dpm_level(xrs->cfg.ddev, level);
+}
+
+static struct solver_node *rg_search_node(struct solver_rgroup *rgp, u64 rid)
+{
+ struct solver_node *node;
+
+ list_for_each_entry(node, &rgp->node_list, list) {
+ if (node->rid == rid)
+ return node;
+ }
+
+ return NULL;
+}
+
+static void remove_partition_node(struct solver_rgroup *rgp,
+ struct partition_node *pt_node)
+{
+ pt_node->nshared--;
+ if (pt_node->nshared > 0)
+ return;
+
+ list_del(&pt_node->list);
+ rgp->npartition_node--;
+
+ bitmap_clear(rgp->resbit, pt_node->start_col, pt_node->ncols);
+ kfree(pt_node);
+}
+
+static void remove_solver_node(struct solver_rgroup *rgp,
+ struct solver_node *node)
+{
+ list_del(&node->list);
+ rgp->nnode--;
+
+ if (node->pt_node)
+ remove_partition_node(rgp, node->pt_node);
+
+ kfree(node);
+}
+
+static int get_free_partition(struct solver_state *xrs,
+ struct solver_node *snode,
+ struct alloc_requests *req)
+{
+ struct partition_node *pt_node;
+ u32 ncols = req->cdo.ncols;
+ u32 col, i;
+
+ for (i = 0; i < snode->cols_len; i++) {
+ col = snode->start_cols[i];
+ if (find_next_bit(xrs->rgp.resbit, XRS_MAX_COL, col) >= col + ncols)
+ break;
+ }
+
+ if (i == snode->cols_len)
+ return -ENODEV;
+
+ pt_node = kzalloc(sizeof(*pt_node), GFP_KERNEL);
+ if (!pt_node)
+ return -ENOMEM;
+
+ pt_node->nshared = 1;
+ pt_node->start_col = col;
+ pt_node->ncols = ncols;
+
+ /*
+ * Always set exclusive to false for now.
+ */
+ pt_node->exclusive = false;
+
+ list_add_tail(&pt_node->list, &xrs->rgp.pt_node_list);
+ xrs->rgp.npartition_node++;
+ bitmap_set(xrs->rgp.resbit, pt_node->start_col, pt_node->ncols);
+
+ snode->pt_node = pt_node;
+
+ return 0;
+}
+
+static int allocate_partition(struct solver_state *xrs,
+ struct solver_node *snode,
+ struct alloc_requests *req)
+{
+ struct partition_node *pt_node, *rpt_node = NULL;
+ int idx, ret;
+
+ ret = get_free_partition(xrs, snode, req);
+ if (!ret)
+ return ret;
+
+ /* try to get a share-able partition */
+ list_for_each_entry(pt_node, &xrs->rgp.pt_node_list, list) {
+ if (pt_node->exclusive)
+ continue;
+
+ if (rpt_node && pt_node->nshared >= rpt_node->nshared)
+ continue;
+
+ for (idx = 0; idx < snode->cols_len; idx++) {
+ if (snode->start_cols[idx] != pt_node->start_col)
+ continue;
+
+ if (req->cdo.ncols != pt_node->ncols)
+ continue;
+
+ rpt_node = pt_node;
+ break;
+ }
+ }
+
+ if (!rpt_node)
+ return -ENODEV;
+
+ rpt_node->nshared++;
+ snode->pt_node = rpt_node;
+
+ return 0;
+}
+
+static struct solver_node *create_solver_node(struct solver_state *xrs,
+ struct alloc_requests *req)
+{
+ struct cdo_parts *cdop = &req->cdo;
+ struct solver_node *node;
+ int ret;
+
+ node = kzalloc(struct_size(node, start_cols, cdop->cols_len), GFP_KERNEL);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+
+ node->rid = req->rid;
+ node->cols_len = cdop->cols_len;
+ memcpy(node->start_cols, cdop->start_cols, cdop->cols_len * sizeof(u32));
+
+ ret = allocate_partition(xrs, node, req);
+ if (ret)
+ goto free_node;
+
+ list_add_tail(&node->list, &xrs->rgp.node_list);
+ xrs->rgp.nnode++;
+ return node;
+
+free_node:
+ kfree(node);
+ return ERR_PTR(ret);
+}
+
+static void fill_load_action(struct solver_state *xrs,
+ struct solver_node *snode,
+ struct xrs_action_load *action)
+{
+ action->rid = snode->rid;
+ action->part.start_col = snode->pt_node->start_col;
+ action->part.ncols = snode->pt_node->ncols;
+}
+
+int xrs_allocate_resource(void *hdl, struct alloc_requests *req, void *cb_arg)
+{
+ struct xrs_action_load load_act;
+ struct solver_node *snode;
+ struct solver_state *xrs;
+ u32 dpm_level;
+ int ret;
+
+ xrs = (struct solver_state *)hdl;
+
+ ret = sanity_check(xrs, req);
+ if (ret) {
+ drm_err(xrs->cfg.ddev, "invalid request");
+ return ret;
+ }
+
+ if (rg_search_node(&xrs->rgp, req->rid)) {
+ drm_err(xrs->cfg.ddev, "rid %lld is in-use", req->rid);
+ return -EEXIST;
+ }
+
+ snode = create_solver_node(xrs, req);
+ if (IS_ERR(snode))
+ return PTR_ERR(snode);
+
+ fill_load_action(xrs, snode, &load_act);
+ ret = xrs->cfg.actions->load(cb_arg, &load_act);
+ if (ret)
+ goto free_node;
+
+ ret = set_dpm_level(xrs, req, &dpm_level);
+ if (ret)
+ goto free_node;
+
+ snode->dpm_level = dpm_level;
+ snode->cb_arg = cb_arg;
+
+ drm_dbg(xrs->cfg.ddev, "start col %d ncols %d\n",
+ snode->pt_node->start_col, snode->pt_node->ncols);
+
+ return 0;
+
+free_node:
+ remove_solver_node(&xrs->rgp, snode);
+
+ return ret;
+}
+
+int xrs_release_resource(void *hdl, u64 rid)
+{
+ struct solver_state *xrs = hdl;
+ struct solver_node *node;
+
+ node = rg_search_node(&xrs->rgp, rid);
+ if (!node) {
+ drm_err(xrs->cfg.ddev, "node not exist");
+ return -ENODEV;
+ }
+
+ xrs->cfg.actions->unload(node->cb_arg);
+ remove_solver_node(&xrs->rgp, node);
+
+ return 0;
+}
+
+void *xrsm_init(struct init_config *cfg)
+{
+ struct solver_rgroup *rgp;
+ struct solver_state *xrs;
+
+ xrs = drmm_kzalloc(cfg->ddev, sizeof(*xrs), GFP_KERNEL);
+ if (!xrs)
+ return NULL;
+
+ memcpy(&xrs->cfg, cfg, sizeof(*cfg));
+
+ rgp = &xrs->rgp;
+ INIT_LIST_HEAD(&rgp->node_list);
+ INIT_LIST_HEAD(&rgp->pt_node_list);
+
+ return xrs;
+}
diff --git a/drivers/accel/amdxdna/aie2_solver.h b/drivers/accel/amdxdna/aie2_solver.h
new file mode 100644
index 000000000000..a2e3c52229e9
--- /dev/null
+++ b/drivers/accel/amdxdna/aie2_solver.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AIE2_SOLVER_H
+#define _AIE2_SOLVER_H
+
+#define XRS_MAX_COL 128
+
+/*
+ * Structure used to describe a partition. A partition is column based
+ * allocation unit described by its start column and number of columns.
+ */
+struct aie_part {
+ u32 start_col;
+ u32 ncols;
+};
+
+/*
+ * The QoS capabilities of a given AIE partition.
+ */
+struct aie_qos_cap {
+ u32 opc; /* operations per cycle */
+ u32 dma_bw; /* DMA bandwidth */
+};
+
+/*
+ * QoS requirement of a resource allocation.
+ */
+struct aie_qos {
+ u32 gops; /* Giga operations */
+ u32 fps; /* Frames per second */
+ u32 dma_bw; /* DMA bandwidth */
+ u32 latency; /* Frame response latency */
+ u32 exec_time; /* Frame execution time */
+ u32 priority; /* Request priority */
+};
+
+/*
+ * Structure used to describe a relocatable CDO (Configuration Data Object).
+ */
+struct cdo_parts {
+ u32 *start_cols; /* Start column array */
+ u32 cols_len; /* Length of start column array */
+ u32 ncols; /* # of column */
+ struct aie_qos_cap qos_cap; /* CDO QoS capabilities */
+};
+
+/*
+ * Structure used to describe a request to allocate.
+ */
+struct alloc_requests {
+ u64 rid;
+ struct cdo_parts cdo;
+ struct aie_qos rqos; /* Requested QoS */
+};
+
+/*
+ * Load callback argument
+ */
+struct xrs_action_load {
+ u32 rid;
+ struct aie_part part;
+};
+
+/*
+ * Define the power level available
+ *
+ * POWER_LEVEL_MIN:
+ * Lowest power level. Usually set when all actions are unloaded.
+ *
+ * POWER_LEVEL_n
+ * Power levels 0 - n, is a step increase in system frequencies
+ */
+enum power_level {
+ POWER_LEVEL_MIN = 0x0,
+ POWER_LEVEL_0 = 0x1,
+ POWER_LEVEL_1 = 0x2,
+ POWER_LEVEL_2 = 0x3,
+ POWER_LEVEL_3 = 0x4,
+ POWER_LEVEL_4 = 0x5,
+ POWER_LEVEL_5 = 0x6,
+ POWER_LEVEL_6 = 0x7,
+ POWER_LEVEL_7 = 0x8,
+ POWER_LEVEL_NUM,
+};
+
+/*
+ * Structure used to describe the frequency table.
+ * Resource solver chooses the frequency from the table
+ * to meet the QOS requirements.
+ */
+struct clk_list_info {
+ u32 num_levels; /* available power levels */
+ u32 cu_clk_list[POWER_LEVEL_NUM]; /* available aie clock frequencies in Mhz*/
+};
+
+struct xrs_action_ops {
+ int (*load)(void *cb_arg, struct xrs_action_load *action);
+ int (*unload)(void *cb_arg);
+ int (*set_dft_dpm_level)(struct drm_device *ddev, u32 level);
+};
+
+/*
+ * Structure used to describe information for solver during initialization.
+ */
+struct init_config {
+ u32 total_col;
+ u32 sys_eff_factor; /* system efficiency factor */
+ u32 latency_adj; /* latency adjustment in ms */
+ struct clk_list_info clk_list; /* List of frequencies available in system */
+ struct drm_device *ddev;
+ struct xrs_action_ops *actions;
+};
+
+/*
+ * xrsm_init() - Register resource solver. Resource solver client needs
+ * to call this function to register itself.
+ *
+ * @cfg: The system metrics for resource solver to use
+ *
+ * Return: A resource solver handle
+ *
+ * Note: We should only create one handle per AIE array to be managed.
+ */
+void *xrsm_init(struct init_config *cfg);
+
+/*
+ * xrs_allocate_resource() - Request to allocate resources for a given context
+ * and a partition metadata. (See struct part_meta)
+ *
+ * @hdl: Resource solver handle obtained from xrs_init()
+ * @req: Input to the Resource solver including request id
+ * and partition metadata.
+ * @cb_arg: callback argument pointer
+ *
+ * Return: 0 when successful.
+ * Or standard error number when failing
+ *
+ * Note:
+ * There is no lock mechanism inside resource solver. So it is
+ * the caller's responsibility to lock down XCLBINs and grab
+ * necessary lock.
+ */
+int xrs_allocate_resource(void *hdl, struct alloc_requests *req, void *cb_arg);
+
+/*
+ * xrs_release_resource() - Request to free resources for a given context.
+ *
+ * @hdl: Resource solver handle obtained from xrs_init()
+ * @rid: The Request ID to identify the requesting context
+ */
+int xrs_release_resource(void *hdl, u64 rid);
+#endif /* _AIE2_SOLVER_H */
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c
new file mode 100644
index 000000000000..d17aef89a0ad
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/xarray.h>
+#include <trace/events/amdxdna.h>
+
+#include "amdxdna_ctx.h"
+#include "amdxdna_gem.h"
+#include "amdxdna_pci_drv.h"
+
+#define MAX_HWCTX_ID 255
+#define MAX_ARG_COUNT 4095
+
+struct amdxdna_fence {
+ struct dma_fence base;
+ spinlock_t lock; /* for base */
+ struct amdxdna_hwctx *hwctx;
+};
+
+static const char *amdxdna_fence_get_driver_name(struct dma_fence *fence)
+{
+ return KBUILD_MODNAME;
+}
+
+static const char *amdxdna_fence_get_timeline_name(struct dma_fence *fence)
+{
+ struct amdxdna_fence *xdna_fence;
+
+ xdna_fence = container_of(fence, struct amdxdna_fence, base);
+
+ return xdna_fence->hwctx->name;
+}
+
+static const struct dma_fence_ops fence_ops = {
+ .get_driver_name = amdxdna_fence_get_driver_name,
+ .get_timeline_name = amdxdna_fence_get_timeline_name,
+};
+
+static struct dma_fence *amdxdna_fence_create(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_fence *fence;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return NULL;
+
+ fence->hwctx = hwctx;
+ spin_lock_init(&fence->lock);
+ dma_fence_init(&fence->base, &fence_ops, &fence->lock, hwctx->id, 0);
+ return &fence->base;
+}
+
+static void amdxdna_hwctx_destroy_rcu(struct amdxdna_hwctx *hwctx,
+ struct srcu_struct *ss)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+
+ synchronize_srcu(ss);
+
+ /* At this point, user is not able to submit new commands */
+ xdna->dev_info->ops->hwctx_fini(hwctx);
+
+ kfree(hwctx->name);
+ kfree(hwctx);
+}
+
+int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
+ int (*walk)(struct amdxdna_hwctx *hwctx, void *arg))
+{
+ struct amdxdna_hwctx *hwctx;
+ unsigned long hwctx_id;
+ int ret = 0, idx;
+
+ idx = srcu_read_lock(&client->hwctx_srcu);
+ amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
+ ret = walk(hwctx, arg);
+ if (ret)
+ break;
+ }
+ srcu_read_unlock(&client->hwctx_srcu, idx);
+
+ return ret;
+}
+
+void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size)
+{
+ struct amdxdna_cmd *cmd = abo->mem.kva;
+ u32 num_masks, count;
+
+ if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN)
+ num_masks = 0;
+ else
+ num_masks = 1 + FIELD_GET(AMDXDNA_CMD_EXTRA_CU_MASK, cmd->header);
+
+ if (size) {
+ count = FIELD_GET(AMDXDNA_CMD_COUNT, cmd->header);
+ if (unlikely(count <= num_masks)) {
+ *size = 0;
+ return NULL;
+ }
+ *size = (count - num_masks) * sizeof(u32);
+ }
+ return &cmd->data[num_masks];
+}
+
+u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_cmd *cmd = abo->mem.kva;
+ u32 num_masks, i;
+ u32 *cu_mask;
+
+ if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN)
+ return INVALID_CU_IDX;
+
+ num_masks = 1 + FIELD_GET(AMDXDNA_CMD_EXTRA_CU_MASK, cmd->header);
+ cu_mask = cmd->data;
+ for (i = 0; i < num_masks; i++) {
+ if (cu_mask[i])
+ return ffs(cu_mask[i]) - 1;
+ }
+
+ return INVALID_CU_IDX;
+}
+
+/*
+ * This should be called in close() and remove(). DO NOT call in other syscalls.
+ * This guarantee that when hwctx and resources will be released, if user
+ * doesn't call amdxdna_drm_destroy_hwctx_ioctl.
+ */
+void amdxdna_hwctx_remove_all(struct amdxdna_client *client)
+{
+ struct amdxdna_hwctx *hwctx;
+ unsigned long hwctx_id;
+
+ amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
+ XDNA_DBG(client->xdna, "PID %d close HW context %d",
+ client->pid, hwctx->id);
+ xa_erase(&client->hwctx_xa, hwctx->id);
+ amdxdna_hwctx_destroy_rcu(hwctx, &client->hwctx_srcu);
+ }
+}
+
+int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_drm_create_hwctx *args = data;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_hwctx *hwctx;
+ int ret, idx;
+
+ if (args->ext || args->ext_flags)
+ return -EINVAL;
+
+ hwctx = kzalloc(sizeof(*hwctx), GFP_KERNEL);
+ if (!hwctx)
+ return -ENOMEM;
+
+ if (copy_from_user(&hwctx->qos, u64_to_user_ptr(args->qos_p), sizeof(hwctx->qos))) {
+ XDNA_ERR(xdna, "Access QoS info failed");
+ kfree(hwctx);
+ return -EFAULT;
+ }
+
+ hwctx->client = client;
+ hwctx->fw_ctx_id = -1;
+ hwctx->num_tiles = args->num_tiles;
+ hwctx->mem_size = args->mem_size;
+ hwctx->max_opc = args->max_opc;
+
+ guard(mutex)(&xdna->dev_lock);
+
+ if (!drm_dev_enter(dev, &idx)) {
+ ret = -ENODEV;
+ goto free_hwctx;
+ }
+
+ ret = xdna->dev_info->ops->hwctx_init(hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
+ goto dev_exit;
+ }
+
+ hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->fw_ctx_id);
+ if (!hwctx->name) {
+ ret = -ENOMEM;
+ goto fini_hwctx;
+ }
+
+ ret = xa_alloc_cyclic(&client->hwctx_xa, &hwctx->id, hwctx,
+ XA_LIMIT(AMDXDNA_INVALID_CTX_HANDLE + 1, MAX_HWCTX_ID),
+ &client->next_hwctxid, GFP_KERNEL);
+ if (ret < 0) {
+ XDNA_ERR(xdna, "Allocate hwctx ID failed, ret %d", ret);
+ goto free_name;
+ }
+
+ args->handle = hwctx->id;
+ args->syncobj_handle = hwctx->syncobj_hdl;
+
+ atomic64_set(&hwctx->job_submit_cnt, 0);
+ atomic64_set(&hwctx->job_free_cnt, 0);
+ XDNA_DBG(xdna, "PID %d create HW context %d, ret %d", client->pid, args->handle, ret);
+ drm_dev_exit(idx);
+ return 0;
+
+free_name:
+ kfree(hwctx->name);
+fini_hwctx:
+ xdna->dev_info->ops->hwctx_fini(hwctx);
+dev_exit:
+ drm_dev_exit(idx);
+free_hwctx:
+ kfree(hwctx);
+ return ret;
+}
+
+int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_drm_destroy_hwctx *args = data;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_hwctx *hwctx;
+ int ret = 0, idx;
+
+ if (XDNA_MBZ_DBG(xdna, &args->pad, sizeof(args->pad)))
+ return -EINVAL;
+
+ if (!drm_dev_enter(dev, &idx))
+ return -ENODEV;
+
+ mutex_lock(&xdna->dev_lock);
+ hwctx = xa_erase(&client->hwctx_xa, args->handle);
+ if (!hwctx) {
+ ret = -EINVAL;
+ XDNA_DBG(xdna, "PID %d HW context %d not exist",
+ client->pid, args->handle);
+ goto out;
+ }
+
+ /*
+ * The pushed jobs are handled by DRM scheduler during destroy.
+ * SRCU to synchronize with exec command ioctls.
+ */
+ amdxdna_hwctx_destroy_rcu(hwctx, &client->hwctx_srcu);
+
+ XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle);
+out:
+ mutex_unlock(&xdna->dev_lock);
+ drm_dev_exit(idx);
+ return ret;
+}
+
+int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_drm_config_hwctx *args = data;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_hwctx *hwctx;
+ int ret, idx;
+ u32 buf_size;
+ void *buf;
+ u64 val;
+
+ if (XDNA_MBZ_DBG(xdna, &args->pad, sizeof(args->pad)))
+ return -EINVAL;
+
+ if (!xdna->dev_info->ops->hwctx_config)
+ return -EOPNOTSUPP;
+
+ val = args->param_val;
+ buf_size = args->param_val_size;
+
+ switch (args->param_type) {
+ case DRM_AMDXDNA_HWCTX_CONFIG_CU:
+ /* For those types that param_val is pointer */
+ if (buf_size > PAGE_SIZE) {
+ XDNA_ERR(xdna, "Config CU param buffer too large");
+ return -E2BIG;
+ }
+
+ /* Hwctx needs to keep buf */
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, u64_to_user_ptr(val), buf_size)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ break;
+ case DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF:
+ case DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF:
+ /* For those types that param_val is a value */
+ buf = NULL;
+ buf_size = 0;
+ break;
+ default:
+ XDNA_DBG(xdna, "Unknown HW context config type %d", args->param_type);
+ return -EINVAL;
+ }
+
+ mutex_lock(&xdna->dev_lock);
+ idx = srcu_read_lock(&client->hwctx_srcu);
+ hwctx = xa_load(&client->hwctx_xa, args->handle);
+ if (!hwctx) {
+ XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle);
+ ret = -EINVAL;
+ goto unlock_srcu;
+ }
+
+ ret = xdna->dev_info->ops->hwctx_config(hwctx, args->param_type, val, buf, buf_size);
+
+unlock_srcu:
+ srcu_read_unlock(&client->hwctx_srcu, idx);
+ mutex_unlock(&xdna->dev_lock);
+ kfree(buf);
+ return ret;
+}
+
+int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_hwctx *hwctx;
+ struct amdxdna_gem_obj *abo;
+ struct drm_gem_object *gobj;
+ int ret, idx;
+
+ if (!xdna->dev_info->ops->hwctx_sync_debug_bo)
+ return -EOPNOTSUPP;
+
+ gobj = drm_gem_object_lookup(client->filp, debug_bo_hdl);
+ if (!gobj)
+ return -EINVAL;
+
+ abo = to_xdna_obj(gobj);
+ guard(mutex)(&xdna->dev_lock);
+ idx = srcu_read_lock(&client->hwctx_srcu);
+ hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx);
+ if (!hwctx) {
+ ret = -EINVAL;
+ goto unlock_srcu;
+ }
+
+ ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl);
+
+unlock_srcu:
+ srcu_read_unlock(&client->hwctx_srcu, idx);
+ drm_gem_object_put(gobj);
+ return ret;
+}
+
+static void
+amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
+{
+ int i;
+
+ for (i = 0; i < job->bo_cnt; i++) {
+ if (!job->bos[i])
+ break;
+ drm_gem_object_put(job->bos[i]);
+ }
+}
+
+static int
+amdxdna_arg_bos_lookup(struct amdxdna_client *client,
+ struct amdxdna_sched_job *job,
+ u32 *bo_hdls, u32 bo_cnt)
+{
+ struct drm_gem_object *gobj;
+ int i, ret;
+
+ job->bo_cnt = bo_cnt;
+ for (i = 0; i < job->bo_cnt; i++) {
+ struct amdxdna_gem_obj *abo;
+
+ gobj = drm_gem_object_lookup(client->filp, bo_hdls[i]);
+ if (!gobj) {
+ ret = -ENOENT;
+ goto put_shmem_bo;
+ }
+ abo = to_xdna_obj(gobj);
+
+ mutex_lock(&abo->lock);
+ if (abo->pinned) {
+ mutex_unlock(&abo->lock);
+ job->bos[i] = gobj;
+ continue;
+ }
+
+ ret = amdxdna_gem_pin_nolock(abo);
+ if (ret) {
+ mutex_unlock(&abo->lock);
+ drm_gem_object_put(gobj);
+ goto put_shmem_bo;
+ }
+ abo->pinned = true;
+ mutex_unlock(&abo->lock);
+
+ job->bos[i] = gobj;
+ }
+
+ return 0;
+
+put_shmem_bo:
+ amdxdna_arg_bos_put(job);
+ return ret;
+}
+
+void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
+{
+ trace_amdxdna_debug_point(job->hwctx->name, job->seq, "job release");
+ amdxdna_arg_bos_put(job);
+ amdxdna_gem_put_obj(job->cmd_bo);
+ dma_fence_put(job->fence);
+}
+
+int amdxdna_cmd_submit(struct amdxdna_client *client,
+ struct amdxdna_drv_cmd *drv_cmd,
+ u32 cmd_bo_hdl, u32 *arg_bo_hdls, u32 arg_bo_cnt,
+ u32 hwctx_hdl, u64 *seq)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_sched_job *job;
+ struct amdxdna_hwctx *hwctx;
+ int ret, idx;
+
+ XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt);
+ job = kzalloc(struct_size(job, bos, arg_bo_cnt), GFP_KERNEL);
+ if (!job)
+ return -ENOMEM;
+
+ job->drv_cmd = drv_cmd;
+
+ if (cmd_bo_hdl != AMDXDNA_INVALID_BO_HANDLE) {
+ job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_CMD);
+ if (!job->cmd_bo) {
+ XDNA_ERR(xdna, "Failed to get cmd bo from %d", cmd_bo_hdl);
+ ret = -EINVAL;
+ goto free_job;
+ }
+ }
+
+ ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
+ if (ret) {
+ XDNA_ERR(xdna, "Argument BOs lookup failed, ret %d", ret);
+ goto cmd_put;
+ }
+
+ idx = srcu_read_lock(&client->hwctx_srcu);
+ hwctx = xa_load(&client->hwctx_xa, hwctx_hdl);
+ if (!hwctx) {
+ XDNA_DBG(xdna, "PID %d failed to get hwctx %d",
+ client->pid, hwctx_hdl);
+ ret = -EINVAL;
+ goto unlock_srcu;
+ }
+
+
+ job->hwctx = hwctx;
+ job->mm = current->mm;
+
+ job->fence = amdxdna_fence_create(hwctx);
+ if (!job->fence) {
+ XDNA_ERR(xdna, "Failed to create fence");
+ ret = -ENOMEM;
+ goto unlock_srcu;
+ }
+ kref_init(&job->refcnt);
+
+ ret = xdna->dev_info->ops->cmd_submit(hwctx, job, seq);
+ if (ret)
+ goto put_fence;
+
+ /*
+ * The amdxdna_hwctx_destroy_rcu() will release hwctx and associated
+ * resource after synchronize_srcu(). The submitted jobs should be
+ * handled by the queue, for example DRM scheduler, in device layer.
+ * For here we can unlock SRCU.
+ */
+ srcu_read_unlock(&client->hwctx_srcu, idx);
+ trace_amdxdna_debug_point(hwctx->name, *seq, "job pushed");
+
+ return 0;
+
+put_fence:
+ dma_fence_put(job->fence);
+unlock_srcu:
+ srcu_read_unlock(&client->hwctx_srcu, idx);
+ amdxdna_arg_bos_put(job);
+cmd_put:
+ amdxdna_gem_put_obj(job->cmd_bo);
+free_job:
+ kfree(job);
+ return ret;
+}
+
+/*
+ * The submit command ioctl submits a command to firmware. One firmware command
+ * may contain multiple command BOs for processing as a whole.
+ * The command sequence number is returned which can be used for wait command ioctl.
+ */
+static int amdxdna_drm_submit_execbuf(struct amdxdna_client *client,
+ struct amdxdna_drm_exec_cmd *args)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ u32 *arg_bo_hdls = NULL;
+ u32 cmd_bo_hdl;
+ int ret;
+
+ if (args->arg_count > MAX_ARG_COUNT) {
+ XDNA_ERR(xdna, "Invalid arg bo count %d", args->arg_count);
+ return -EINVAL;
+ }
+
+ /* Only support single command for now. */
+ if (args->cmd_count != 1) {
+ XDNA_ERR(xdna, "Invalid cmd bo count %d", args->cmd_count);
+ return -EINVAL;
+ }
+
+ cmd_bo_hdl = (u32)args->cmd_handles;
+ if (args->arg_count) {
+ arg_bo_hdls = kcalloc(args->arg_count, sizeof(u32), GFP_KERNEL);
+ if (!arg_bo_hdls)
+ return -ENOMEM;
+ ret = copy_from_user(arg_bo_hdls, u64_to_user_ptr(args->args),
+ args->arg_count * sizeof(u32));
+ if (ret) {
+ ret = -EFAULT;
+ goto free_cmd_bo_hdls;
+ }
+ }
+
+ ret = amdxdna_cmd_submit(client, NULL, cmd_bo_hdl, arg_bo_hdls,
+ args->arg_count, args->hwctx, &args->seq);
+ if (ret)
+ XDNA_DBG(xdna, "Submit cmds failed, ret %d", ret);
+
+free_cmd_bo_hdls:
+ kfree(arg_bo_hdls);
+ if (!ret)
+ XDNA_DBG(xdna, "Pushed cmd %lld to scheduler", args->seq);
+ return ret;
+}
+
+int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_drm_exec_cmd *args = data;
+
+ if (args->ext || args->ext_flags)
+ return -EINVAL;
+
+ switch (args->type) {
+ case AMDXDNA_CMD_SUBMIT_EXEC_BUF:
+ return amdxdna_drm_submit_execbuf(client, args);
+ }
+
+ XDNA_ERR(client->xdna, "Invalid command type %d", args->type);
+ return -EINVAL;
+}
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h
new file mode 100644
index 000000000000..b6151244d64f
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_ctx.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_CTX_H_
+#define _AMDXDNA_CTX_H_
+
+#include <linux/bitfield.h>
+
+#include "amdxdna_gem.h"
+
+struct amdxdna_hwctx_priv;
+
+enum ert_cmd_opcode {
+ ERT_START_CU = 0,
+ ERT_CMD_CHAIN = 19,
+ ERT_START_NPU = 20,
+ ERT_START_NPU_PREEMPT = 21,
+ ERT_START_NPU_PREEMPT_ELF = 22,
+ ERT_INVALID_CMD = ~0U,
+};
+
+enum ert_cmd_state {
+ ERT_CMD_STATE_INVALID,
+ ERT_CMD_STATE_NEW,
+ ERT_CMD_STATE_QUEUED,
+ ERT_CMD_STATE_RUNNING,
+ ERT_CMD_STATE_COMPLETED,
+ ERT_CMD_STATE_ERROR,
+ ERT_CMD_STATE_ABORT,
+ ERT_CMD_STATE_SUBMITTED,
+ ERT_CMD_STATE_TIMEOUT,
+ ERT_CMD_STATE_NORESPONSE,
+};
+
+/*
+ * Interpretation of the beginning of data payload for ERT_START_NPU in
+ * amdxdna_cmd. The rest of the payload in amdxdna_cmd is regular kernel args.
+ */
+struct amdxdna_cmd_start_npu {
+ u64 buffer; /* instruction buffer address */
+ u32 buffer_size; /* size of buffer in bytes */
+ u32 prop_count; /* properties count */
+ u32 prop_args[]; /* properties and regular kernel arguments */
+};
+
+/*
+ * Interpretation of the beginning of data payload for ERT_CMD_CHAIN in
+ * amdxdna_cmd. The rest of the payload in amdxdna_cmd is cmd BO handles.
+ */
+struct amdxdna_cmd_chain {
+ u32 command_count;
+ u32 submit_index;
+ u32 error_index;
+ u32 reserved[3];
+ u64 data[] __counted_by(command_count);
+};
+
+/*
+ * Interpretation of the beginning of data payload for ERT_START_NPU_PREEMPT in
+ * amdxdna_cmd. The rest of the payload in amdxdna_cmd is regular kernel args.
+ */
+struct amdxdna_cmd_preempt_data {
+ u64 inst_buf; /* instruction buffer address */
+ u64 save_buf; /* save buffer address */
+ u64 restore_buf; /* restore buffer address */
+ u32 inst_size; /* size of instruction buffer in bytes */
+ u32 save_size; /* size of save buffer in bytes */
+ u32 restore_size; /* size of restore buffer in bytes */
+ u32 inst_prop_cnt; /* properties count */
+ u32 prop_args[]; /* properties and regular kernel arguments */
+};
+
+/* Exec buffer command header format */
+#define AMDXDNA_CMD_STATE GENMASK(3, 0)
+#define AMDXDNA_CMD_EXTRA_CU_MASK GENMASK(11, 10)
+#define AMDXDNA_CMD_COUNT GENMASK(22, 12)
+#define AMDXDNA_CMD_OPCODE GENMASK(27, 23)
+struct amdxdna_cmd {
+ u32 header;
+ u32 data[];
+};
+
+#define INVALID_CU_IDX (~0U)
+
+struct amdxdna_hwctx {
+ struct amdxdna_client *client;
+ struct amdxdna_hwctx_priv *priv;
+ char *name;
+
+ u32 id;
+ u32 max_opc;
+ u32 num_tiles;
+ u32 mem_size;
+ u32 fw_ctx_id;
+ u32 col_list_len;
+ u32 *col_list;
+ u32 start_col;
+ u32 num_col;
+#define HWCTX_STAT_INIT 0
+#define HWCTX_STAT_READY 1
+#define HWCTX_STAT_STOP 2
+ u32 status;
+ u32 old_status;
+
+ struct amdxdna_qos_info qos;
+ struct amdxdna_hwctx_param_config_cu *cus;
+ u32 syncobj_hdl;
+
+ atomic64_t job_submit_cnt;
+ atomic64_t job_free_cnt ____cacheline_aligned_in_smp;
+};
+
+#define drm_job_to_xdna_job(j) \
+ container_of(j, struct amdxdna_sched_job, base)
+
+enum amdxdna_job_opcode {
+ SYNC_DEBUG_BO,
+ ATTACH_DEBUG_BO,
+ DETACH_DEBUG_BO,
+};
+
+struct amdxdna_drv_cmd {
+ enum amdxdna_job_opcode opcode;
+ u32 result;
+};
+
+struct amdxdna_sched_job {
+ struct drm_sched_job base;
+ struct kref refcnt;
+ struct amdxdna_hwctx *hwctx;
+ struct mm_struct *mm;
+ /* The fence to notice DRM scheduler that job is done by hardware */
+ struct dma_fence *fence;
+ /* user can wait on this fence */
+ struct dma_fence *out_fence;
+ bool job_done;
+ bool job_timeout;
+ u64 seq;
+ struct amdxdna_drv_cmd *drv_cmd;
+ struct amdxdna_gem_obj *cmd_bo;
+ size_t bo_cnt;
+ struct drm_gem_object *bos[] __counted_by(bo_cnt);
+};
+
+static inline u32
+amdxdna_cmd_get_op(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_cmd *cmd = abo->mem.kva;
+
+ return FIELD_GET(AMDXDNA_CMD_OPCODE, cmd->header);
+}
+
+static inline void
+amdxdna_cmd_set_state(struct amdxdna_gem_obj *abo, enum ert_cmd_state s)
+{
+ struct amdxdna_cmd *cmd = abo->mem.kva;
+
+ cmd->header &= ~AMDXDNA_CMD_STATE;
+ cmd->header |= FIELD_PREP(AMDXDNA_CMD_STATE, s);
+}
+
+static inline enum ert_cmd_state
+amdxdna_cmd_get_state(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_cmd *cmd = abo->mem.kva;
+
+ return FIELD_GET(AMDXDNA_CMD_STATE, cmd->header);
+}
+
+void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size);
+u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo);
+
+void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job);
+void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
+int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
+ int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
+int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
+
+int amdxdna_cmd_submit(struct amdxdna_client *client,
+ struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
+ u32 *arg_bo_hdls, u32 arg_bo_cnt,
+ u32 hwctx_hdl, u64 *seq);
+
+int amdxdna_cmd_wait(struct amdxdna_client *client, u32 hwctx_hdl,
+ u64 seq, u32 timeout);
+
+int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int amdxdna_drm_destroy_hwctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+
+#endif /* _AMDXDNA_CTX_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_error.h b/drivers/accel/amdxdna/amdxdna_error.h
new file mode 100644
index 000000000000..c51de86ec12b
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_error.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_ERROR_H_
+#define _AMDXDNA_ERROR_H_
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#define AMDXDNA_ERR_DRV_AIE 4
+#define AMDXDNA_ERR_SEV_CRITICAL 3
+#define AMDXDNA_ERR_CLASS_AIE 2
+
+#define AMDXDNA_ERR_NUM_MASK GENMASK_U64(15, 0)
+#define AMDXDNA_ERR_DRV_MASK GENMASK_U64(23, 16)
+#define AMDXDNA_ERR_SEV_MASK GENMASK_U64(31, 24)
+#define AMDXDNA_ERR_MOD_MASK GENMASK_U64(39, 32)
+#define AMDXDNA_ERR_CLASS_MASK GENMASK_U64(47, 40)
+
+enum amdxdna_error_num {
+ AMDXDNA_ERROR_NUM_AIE_SATURATION = 3,
+ AMDXDNA_ERROR_NUM_AIE_FP,
+ AMDXDNA_ERROR_NUM_AIE_STREAM,
+ AMDXDNA_ERROR_NUM_AIE_ACCESS,
+ AMDXDNA_ERROR_NUM_AIE_BUS,
+ AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
+ AMDXDNA_ERROR_NUM_AIE_ECC,
+ AMDXDNA_ERROR_NUM_AIE_LOCK,
+ AMDXDNA_ERROR_NUM_AIE_DMA,
+ AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
+ AMDXDNA_ERROR_NUM_UNKNOWN = 15,
+};
+
+enum amdxdna_error_module {
+ AMDXDNA_ERROR_MODULE_AIE_CORE = 3,
+ AMDXDNA_ERROR_MODULE_AIE_MEMORY,
+ AMDXDNA_ERROR_MODULE_AIE_SHIM,
+ AMDXDNA_ERROR_MODULE_AIE_NOC,
+ AMDXDNA_ERROR_MODULE_AIE_PL,
+ AMDXDNA_ERROR_MODULE_UNKNOWN = 8,
+};
+
+#define AMDXDNA_ERROR_ENCODE(err_num, err_mod) \
+ (FIELD_PREP(AMDXDNA_ERR_NUM_MASK, err_num) | \
+ FIELD_PREP_CONST(AMDXDNA_ERR_DRV_MASK, AMDXDNA_ERR_DRV_AIE) | \
+ FIELD_PREP_CONST(AMDXDNA_ERR_SEV_MASK, AMDXDNA_ERR_SEV_CRITICAL) | \
+ FIELD_PREP(AMDXDNA_ERR_MOD_MASK, err_mod) | \
+ FIELD_PREP_CONST(AMDXDNA_ERR_CLASS_MASK, AMDXDNA_ERR_CLASS_AIE))
+
+#define AMDXDNA_EXTRA_ERR_COL_MASK GENMASK_U64(7, 0)
+#define AMDXDNA_EXTRA_ERR_ROW_MASK GENMASK_U64(15, 8)
+
+#define AMDXDNA_EXTRA_ERR_ENCODE(row, col) \
+ (FIELD_PREP(AMDXDNA_EXTRA_ERR_COL_MASK, col) | \
+ FIELD_PREP(AMDXDNA_EXTRA_ERR_ROW_MASK, row))
+
+#endif /* _AMDXDNA_ERROR_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
new file mode 100644
index 000000000000..dfa916eeb2d9
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -0,0 +1,972 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_cache.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-direct.h>
+#include <linux/iosys-map.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+
+#include "amdxdna_ctx.h"
+#include "amdxdna_gem.h"
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_ubuf.h"
+
+#define XDNA_MAX_CMD_BO_SIZE SZ_32K
+
+MODULE_IMPORT_NS("DMA_BUF");
+
+static int
+amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_client *client = abo->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_mem *mem = &abo->mem;
+ struct amdxdna_gem_obj *heap;
+ u64 offset;
+ u32 align;
+ int ret;
+
+ mutex_lock(&client->mm_lock);
+
+ heap = client->dev_heap;
+ if (!heap) {
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ if (heap->mem.userptr == AMDXDNA_INVALID_ADDR) {
+ XDNA_ERR(xdna, "Invalid dev heap userptr");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ if (mem->size == 0 || mem->size > heap->mem.size) {
+ XDNA_ERR(xdna, "Invalid dev bo size 0x%lx, limit 0x%lx",
+ mem->size, heap->mem.size);
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ align = 1 << max(PAGE_SHIFT, xdna->dev_info->dev_mem_buf_shift);
+ ret = drm_mm_insert_node_generic(&heap->mm, &abo->mm_node,
+ mem->size, align,
+ 0, DRM_MM_INSERT_BEST);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to alloc dev bo memory, ret %d", ret);
+ goto unlock_out;
+ }
+
+ mem->dev_addr = abo->mm_node.start;
+ offset = mem->dev_addr - heap->mem.dev_addr;
+ mem->userptr = heap->mem.userptr + offset;
+ mem->kva = heap->mem.kva + offset;
+
+ drm_gem_object_get(to_gobj(heap));
+
+unlock_out:
+ mutex_unlock(&client->mm_lock);
+
+ return ret;
+}
+
+static void
+amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo)
+{
+ mutex_destroy(&abo->lock);
+ kfree(abo);
+}
+
+static void
+amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_gem_obj *heap;
+
+ mutex_lock(&abo->client->mm_lock);
+
+ drm_mm_remove_node(&abo->mm_node);
+
+ heap = abo->client->dev_heap;
+ drm_gem_object_put(to_gobj(heap));
+
+ mutex_unlock(&abo->client->mm_lock);
+}
+
+static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni,
+ const struct mmu_notifier_range *range,
+ unsigned long cur_seq)
+{
+ struct amdxdna_umap *mapp = container_of(mni, struct amdxdna_umap, notifier);
+ struct amdxdna_gem_obj *abo = mapp->abo;
+ struct amdxdna_dev *xdna;
+
+ xdna = to_xdna_dev(to_gobj(abo)->dev);
+ XDNA_DBG(xdna, "Invalidating range 0x%lx, 0x%lx, type %d",
+ mapp->vma->vm_start, mapp->vma->vm_end, abo->type);
+
+ if (!mmu_notifier_range_blockable(range))
+ return false;
+
+ down_write(&xdna->notifier_lock);
+ abo->mem.map_invalid = true;
+ mapp->invalid = true;
+ mmu_interval_set_seq(&mapp->notifier, cur_seq);
+ up_write(&xdna->notifier_lock);
+
+ xdna->dev_info->ops->hmm_invalidate(abo, cur_seq);
+
+ if (range->event == MMU_NOTIFY_UNMAP) {
+ down_write(&xdna->notifier_lock);
+ if (!mapp->unmapped) {
+ queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work);
+ mapp->unmapped = true;
+ }
+ up_write(&xdna->notifier_lock);
+ }
+
+ return true;
+}
+
+static const struct mmu_interval_notifier_ops amdxdna_hmm_ops = {
+ .invalidate = amdxdna_hmm_invalidate,
+};
+
+static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo,
+ struct vm_area_struct *vma)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ struct amdxdna_umap *mapp;
+
+ down_read(&xdna->notifier_lock);
+ list_for_each_entry(mapp, &abo->mem.umap_list, node) {
+ if (!vma || mapp->vma == vma) {
+ if (!mapp->unmapped) {
+ queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work);
+ mapp->unmapped = true;
+ }
+ if (vma)
+ break;
+ }
+ }
+ up_read(&xdna->notifier_lock);
+}
+
+static void amdxdna_umap_release(struct kref *ref)
+{
+ struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt);
+ struct vm_area_struct *vma = mapp->vma;
+ struct amdxdna_dev *xdna;
+
+ mmu_interval_notifier_remove(&mapp->notifier);
+ if (is_import_bo(mapp->abo) && vma->vm_file && vma->vm_file->f_mapping)
+ mapping_clear_unevictable(vma->vm_file->f_mapping);
+
+ xdna = to_xdna_dev(to_gobj(mapp->abo)->dev);
+ down_write(&xdna->notifier_lock);
+ list_del(&mapp->node);
+ up_write(&xdna->notifier_lock);
+
+ kvfree(mapp->range.hmm_pfns);
+ kfree(mapp);
+}
+
+void amdxdna_umap_put(struct amdxdna_umap *mapp)
+{
+ kref_put(&mapp->refcnt, amdxdna_umap_release);
+}
+
+static void amdxdna_hmm_unreg_work(struct work_struct *work)
+{
+ struct amdxdna_umap *mapp = container_of(work, struct amdxdna_umap,
+ hmm_unreg_work);
+
+ amdxdna_umap_put(mapp);
+}
+
+static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo,
+ struct vm_area_struct *vma)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ unsigned long len = vma->vm_end - vma->vm_start;
+ unsigned long addr = vma->vm_start;
+ struct amdxdna_umap *mapp;
+ u32 nr_pages;
+ int ret;
+
+ if (!xdna->dev_info->ops->hmm_invalidate)
+ return 0;
+
+ mapp = kzalloc(sizeof(*mapp), GFP_KERNEL);
+ if (!mapp)
+ return -ENOMEM;
+
+ nr_pages = (PAGE_ALIGN(addr + len) - (addr & PAGE_MASK)) >> PAGE_SHIFT;
+ mapp->range.hmm_pfns = kvcalloc(nr_pages, sizeof(*mapp->range.hmm_pfns),
+ GFP_KERNEL);
+ if (!mapp->range.hmm_pfns) {
+ ret = -ENOMEM;
+ goto free_map;
+ }
+
+ ret = mmu_interval_notifier_insert_locked(&mapp->notifier,
+ current->mm,
+ addr,
+ len,
+ &amdxdna_hmm_ops);
+ if (ret) {
+ XDNA_ERR(xdna, "Insert mmu notifier failed, ret %d", ret);
+ goto free_pfns;
+ }
+
+ mapp->range.notifier = &mapp->notifier;
+ mapp->range.start = vma->vm_start;
+ mapp->range.end = vma->vm_end;
+ mapp->range.default_flags = HMM_PFN_REQ_FAULT;
+ mapp->vma = vma;
+ mapp->abo = abo;
+ kref_init(&mapp->refcnt);
+
+ if (abo->mem.userptr == AMDXDNA_INVALID_ADDR)
+ abo->mem.userptr = addr;
+ INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work);
+ if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping)
+ mapping_set_unevictable(vma->vm_file->f_mapping);
+
+ down_write(&xdna->notifier_lock);
+ list_add_tail(&mapp->node, &abo->mem.umap_list);
+ up_write(&xdna->notifier_lock);
+
+ return 0;
+
+free_pfns:
+ kvfree(mapp->range.hmm_pfns);
+free_map:
+ kfree(mapp);
+ return ret;
+}
+
+static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+
+ XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, abo->mem.dev_addr);
+ if (abo->pinned)
+ amdxdna_gem_unpin(abo);
+
+ amdxdna_gem_heap_free(abo);
+ drm_gem_object_release(gobj);
+ amdxdna_gem_destroy_obj(abo);
+}
+
+static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
+ struct vm_area_struct *vma)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ unsigned long num_pages = vma_pages(vma);
+ unsigned long offset = 0;
+ int ret;
+
+ if (!is_import_bo(abo)) {
+ ret = drm_gem_shmem_mmap(&abo->base, vma);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed shmem mmap %d", ret);
+ return ret;
+ }
+
+ /* The buffer is based on memory pages. Fix the flag. */
+ vm_flags_mod(vma, VM_MIXEDMAP, VM_PFNMAP);
+ ret = vm_insert_pages(vma, vma->vm_start, abo->base.pages,
+ &num_pages);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed insert pages %d", ret);
+ vma->vm_ops->close(vma);
+ return ret;
+ }
+
+ return 0;
+ }
+
+ vma->vm_private_data = NULL;
+ vma->vm_ops = NULL;
+ ret = dma_buf_mmap(abo->dma_buf, vma, 0);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret);
+ return ret;
+ }
+
+ do {
+ vm_fault_t fault_ret;
+
+ fault_ret = handle_mm_fault(vma, vma->vm_start + offset,
+ FAULT_FLAG_WRITE, NULL);
+ if (fault_ret & VM_FAULT_ERROR) {
+ vma->vm_ops->close(vma);
+ XDNA_ERR(xdna, "Fault in page failed");
+ return -EFAULT;
+ }
+
+ offset += PAGE_SIZE;
+ } while (--num_pages);
+
+ /* Drop the reference drm_gem_mmap_obj() acquired.*/
+ drm_gem_object_put(to_gobj(abo));
+
+ return 0;
+}
+
+static int amdxdna_gem_obj_mmap(struct drm_gem_object *gobj,
+ struct vm_area_struct *vma)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+ int ret;
+
+ ret = amdxdna_hmm_register(abo, vma);
+ if (ret)
+ return ret;
+
+ ret = amdxdna_insert_pages(abo, vma);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed insert pages, ret %d", ret);
+ goto hmm_unreg;
+ }
+
+ XDNA_DBG(xdna, "BO map_offset 0x%llx type %d userptr 0x%lx size 0x%lx",
+ drm_vma_node_offset_addr(&gobj->vma_node), abo->type,
+ vma->vm_start, gobj->size);
+ return 0;
+
+hmm_unreg:
+ amdxdna_hmm_unregister(abo, vma);
+ return ret;
+}
+
+static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
+{
+ struct drm_gem_object *gobj = dma_buf->priv;
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+ unsigned long num_pages = vma_pages(vma);
+ int ret;
+
+ vma->vm_ops = &drm_gem_shmem_vm_ops;
+ vma->vm_private_data = gobj;
+
+ drm_gem_object_get(gobj);
+ ret = drm_gem_shmem_mmap(&abo->base, vma);
+ if (ret)
+ goto put_obj;
+
+ /* The buffer is based on memory pages. Fix the flag. */
+ vm_flags_mod(vma, VM_MIXEDMAP, VM_PFNMAP);
+ ret = vm_insert_pages(vma, vma->vm_start, abo->base.pages,
+ &num_pages);
+ if (ret)
+ goto close_vma;
+
+ return 0;
+
+close_vma:
+ vma->vm_ops->close(vma);
+put_obj:
+ drm_gem_object_put(gobj);
+ return ret;
+}
+
+static const struct dma_buf_ops amdxdna_dmabuf_ops = {
+ .attach = drm_gem_map_attach,
+ .detach = drm_gem_map_detach,
+ .map_dma_buf = drm_gem_map_dma_buf,
+ .unmap_dma_buf = drm_gem_unmap_dma_buf,
+ .release = drm_gem_dmabuf_release,
+ .mmap = amdxdna_gem_dmabuf_mmap,
+ .vmap = drm_gem_dmabuf_vmap,
+ .vunmap = drm_gem_dmabuf_vunmap,
+};
+
+static int amdxdna_gem_obj_vmap(struct amdxdna_gem_obj *abo, void **vaddr)
+{
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
+ int ret;
+
+ if (is_import_bo(abo))
+ ret = dma_buf_vmap_unlocked(abo->dma_buf, &map);
+ else
+ ret = drm_gem_vmap(to_gobj(abo), &map);
+
+ *vaddr = map.vaddr;
+ return ret;
+}
+
+static void amdxdna_gem_obj_vunmap(struct amdxdna_gem_obj *abo)
+{
+ struct iosys_map map;
+
+ if (!abo->mem.kva)
+ return;
+
+ iosys_map_set_vaddr(&map, abo->mem.kva);
+
+ if (is_import_bo(abo))
+ dma_buf_vunmap_unlocked(abo->dma_buf, &map);
+ else
+ drm_gem_vunmap(to_gobj(abo), &map);
+}
+
+static struct dma_buf *amdxdna_gem_prime_export(struct drm_gem_object *gobj, int flags)
+{
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ if (abo->dma_buf) {
+ get_dma_buf(abo->dma_buf);
+ return abo->dma_buf;
+ }
+
+ exp_info.ops = &amdxdna_dmabuf_ops;
+ exp_info.size = gobj->size;
+ exp_info.flags = flags;
+ exp_info.priv = gobj;
+ exp_info.resv = gobj->resv;
+
+ return drm_gem_dmabuf_export(gobj->dev, &exp_info);
+}
+
+static void amdxdna_imported_obj_free(struct amdxdna_gem_obj *abo)
+{
+ dma_buf_unmap_attachment_unlocked(abo->attach, abo->base.sgt, DMA_BIDIRECTIONAL);
+ dma_buf_detach(abo->dma_buf, abo->attach);
+ dma_buf_put(abo->dma_buf);
+ drm_gem_object_release(to_gobj(abo));
+ kfree(abo);
+}
+
+static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
+ struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
+
+ XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, abo->mem.dev_addr);
+
+ amdxdna_hmm_unregister(abo, NULL);
+ flush_workqueue(xdna->notifier_wq);
+
+ if (abo->pinned)
+ amdxdna_gem_unpin(abo);
+
+ if (abo->type == AMDXDNA_BO_DEV_HEAP)
+ drm_mm_takedown(&abo->mm);
+
+ amdxdna_gem_obj_vunmap(abo);
+ mutex_destroy(&abo->lock);
+
+ if (is_import_bo(abo)) {
+ amdxdna_imported_obj_free(abo);
+ return;
+ }
+
+ drm_gem_shmem_free(&abo->base);
+}
+
+static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = {
+ .free = amdxdna_gem_dev_obj_free,
+};
+
+static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
+ .free = amdxdna_gem_obj_free,
+ .print_info = drm_gem_shmem_object_print_info,
+ .pin = drm_gem_shmem_object_pin,
+ .unpin = drm_gem_shmem_object_unpin,
+ .get_sg_table = drm_gem_shmem_object_get_sg_table,
+ .vmap = drm_gem_shmem_object_vmap,
+ .vunmap = drm_gem_shmem_object_vunmap,
+ .mmap = amdxdna_gem_obj_mmap,
+ .vm_ops = &drm_gem_shmem_vm_ops,
+ .export = amdxdna_gem_prime_export,
+};
+
+static struct amdxdna_gem_obj *
+amdxdna_gem_create_obj(struct drm_device *dev, size_t size)
+{
+ struct amdxdna_gem_obj *abo;
+
+ abo = kzalloc(sizeof(*abo), GFP_KERNEL);
+ if (!abo)
+ return ERR_PTR(-ENOMEM);
+
+ abo->pinned = false;
+ abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE;
+ mutex_init(&abo->lock);
+
+ abo->mem.userptr = AMDXDNA_INVALID_ADDR;
+ abo->mem.dev_addr = AMDXDNA_INVALID_ADDR;
+ abo->mem.size = size;
+ INIT_LIST_HEAD(&abo->mem.umap_list);
+
+ return abo;
+}
+
+/* For drm_driver->gem_create_object callback */
+struct drm_gem_object *
+amdxdna_gem_create_object_cb(struct drm_device *dev, size_t size)
+{
+ struct amdxdna_gem_obj *abo;
+
+ abo = amdxdna_gem_create_obj(dev, size);
+ if (IS_ERR(abo))
+ return ERR_CAST(abo);
+
+ to_gobj(abo)->funcs = &amdxdna_gem_shmem_funcs;
+
+ return to_gobj(abo);
+}
+
+static struct amdxdna_gem_obj *
+amdxdna_gem_create_shmem_object(struct drm_device *dev, size_t size)
+{
+ struct drm_gem_shmem_object *shmem = drm_gem_shmem_create(dev, size);
+
+ if (IS_ERR(shmem))
+ return ERR_CAST(shmem);
+
+ shmem->map_wc = false;
+ return to_xdna_obj(&shmem->base);
+}
+
+static struct amdxdna_gem_obj *
+amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ enum amdxdna_ubuf_flag flags = 0;
+ struct amdxdna_drm_va_tbl va_tbl;
+ struct drm_gem_object *gobj;
+ struct dma_buf *dma_buf;
+
+ if (copy_from_user(&va_tbl, u64_to_user_ptr(args->vaddr), sizeof(va_tbl))) {
+ XDNA_DBG(xdna, "Access va table failed");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (va_tbl.num_entries) {
+ if (args->type == AMDXDNA_BO_CMD)
+ flags |= AMDXDNA_UBUF_FLAG_MAP_DMA;
+
+ dma_buf = amdxdna_get_ubuf(dev, flags, va_tbl.num_entries,
+ u64_to_user_ptr(args->vaddr + sizeof(va_tbl)));
+ } else {
+ dma_buf = dma_buf_get(va_tbl.dmabuf_fd);
+ }
+
+ if (IS_ERR(dma_buf))
+ return ERR_CAST(dma_buf);
+
+ gobj = amdxdna_gem_prime_import(dev, dma_buf);
+ if (IS_ERR(gobj)) {
+ dma_buf_put(dma_buf);
+ return ERR_CAST(gobj);
+ }
+
+ dma_buf_put(dma_buf);
+
+ return to_xdna_obj(gobj);
+}
+
+static struct amdxdna_gem_obj *
+amdxdna_gem_create_object(struct drm_device *dev,
+ struct amdxdna_drm_create_bo *args)
+{
+ size_t aligned_sz = PAGE_ALIGN(args->size);
+
+ if (args->vaddr)
+ return amdxdna_gem_create_ubuf_object(dev, args);
+
+ return amdxdna_gem_create_shmem_object(dev, aligned_sz);
+}
+
+struct drm_gem_object *
+amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)
+{
+ struct dma_buf_attachment *attach;
+ struct amdxdna_gem_obj *abo;
+ struct drm_gem_object *gobj;
+ struct sg_table *sgt;
+ int ret;
+
+ get_dma_buf(dma_buf);
+
+ attach = dma_buf_attach(dma_buf, dev->dev);
+ if (IS_ERR(attach)) {
+ ret = PTR_ERR(attach);
+ goto put_buf;
+ }
+
+ sgt = dma_buf_map_attachment_unlocked(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto fail_detach;
+ }
+
+ gobj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
+ if (IS_ERR(gobj)) {
+ ret = PTR_ERR(gobj);
+ goto fail_unmap;
+ }
+
+ abo = to_xdna_obj(gobj);
+ abo->attach = attach;
+ abo->dma_buf = dma_buf;
+
+ return gobj;
+
+fail_unmap:
+ dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_BIDIRECTIONAL);
+fail_detach:
+ dma_buf_detach(dma_buf, attach);
+put_buf:
+ dma_buf_put(dma_buf);
+
+ return ERR_PTR(ret);
+}
+
+static struct amdxdna_gem_obj *
+amdxdna_drm_alloc_shmem(struct drm_device *dev,
+ struct amdxdna_drm_create_bo *args,
+ struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_gem_obj *abo;
+
+ abo = amdxdna_gem_create_object(dev, args);
+ if (IS_ERR(abo))
+ return ERR_CAST(abo);
+
+ abo->client = client;
+ abo->type = AMDXDNA_BO_SHMEM;
+
+ return abo;
+}
+
+static struct amdxdna_gem_obj *
+amdxdna_drm_create_dev_heap(struct drm_device *dev,
+ struct amdxdna_drm_create_bo *args,
+ struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_gem_obj *abo;
+ int ret;
+
+ if (args->size > xdna->dev_info->dev_mem_size) {
+ XDNA_DBG(xdna, "Invalid dev heap size 0x%llx, limit 0x%lx",
+ args->size, xdna->dev_info->dev_mem_size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mutex_lock(&client->mm_lock);
+ if (client->dev_heap) {
+ XDNA_DBG(client->xdna, "dev heap is already created");
+ ret = -EBUSY;
+ goto mm_unlock;
+ }
+
+ abo = amdxdna_gem_create_object(dev, args);
+ if (IS_ERR(abo)) {
+ ret = PTR_ERR(abo);
+ goto mm_unlock;
+ }
+
+ abo->type = AMDXDNA_BO_DEV_HEAP;
+ abo->client = client;
+ abo->mem.dev_addr = client->xdna->dev_info->dev_mem_base;
+ drm_mm_init(&abo->mm, abo->mem.dev_addr, abo->mem.size);
+
+ ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva);
+ if (ret) {
+ XDNA_ERR(xdna, "Vmap heap bo failed, ret %d", ret);
+ goto release_obj;
+ }
+
+ client->dev_heap = abo;
+ drm_gem_object_get(to_gobj(abo));
+ mutex_unlock(&client->mm_lock);
+
+ return abo;
+
+release_obj:
+ drm_gem_object_put(to_gobj(abo));
+mm_unlock:
+ mutex_unlock(&client->mm_lock);
+ return ERR_PTR(ret);
+}
+
+struct amdxdna_gem_obj *
+amdxdna_drm_alloc_dev_bo(struct drm_device *dev,
+ struct amdxdna_drm_create_bo *args,
+ struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ size_t aligned_sz = PAGE_ALIGN(args->size);
+ struct amdxdna_gem_obj *abo;
+ int ret;
+
+ abo = amdxdna_gem_create_obj(&xdna->ddev, aligned_sz);
+ if (IS_ERR(abo))
+ return abo;
+
+ to_gobj(abo)->funcs = &amdxdna_gem_dev_obj_funcs;
+ abo->type = AMDXDNA_BO_DEV;
+ abo->client = client;
+
+ ret = amdxdna_gem_heap_alloc(abo);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to alloc dev bo memory, ret %d", ret);
+ amdxdna_gem_destroy_obj(abo);
+ return ERR_PTR(ret);
+ }
+
+ drm_gem_private_object_init(&xdna->ddev, to_gobj(abo), aligned_sz);
+
+ return abo;
+}
+
+static struct amdxdna_gem_obj *
+amdxdna_drm_create_cmd_bo(struct drm_device *dev,
+ struct amdxdna_drm_create_bo *args,
+ struct drm_file *filp)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_gem_obj *abo;
+ int ret;
+
+ if (args->size > XDNA_MAX_CMD_BO_SIZE) {
+ XDNA_ERR(xdna, "Command bo size 0x%llx too large", args->size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (args->size < sizeof(struct amdxdna_cmd)) {
+ XDNA_DBG(xdna, "Command BO size 0x%llx too small", args->size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ abo = amdxdna_gem_create_object(dev, args);
+ if (IS_ERR(abo))
+ return ERR_CAST(abo);
+
+ abo->type = AMDXDNA_BO_CMD;
+ abo->client = filp->driver_priv;
+
+ ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva);
+ if (ret) {
+ XDNA_ERR(xdna, "Vmap cmd bo failed, ret %d", ret);
+ goto release_obj;
+ }
+
+ return abo;
+
+release_obj:
+ drm_gem_object_put(to_gobj(abo));
+ return ERR_PTR(ret);
+}
+
+int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_drm_create_bo *args = data;
+ struct amdxdna_gem_obj *abo;
+ int ret;
+
+ if (args->flags)
+ return -EINVAL;
+
+ XDNA_DBG(xdna, "BO arg type %d vaddr 0x%llx size 0x%llx flags 0x%llx",
+ args->type, args->vaddr, args->size, args->flags);
+ switch (args->type) {
+ case AMDXDNA_BO_SHMEM:
+ abo = amdxdna_drm_alloc_shmem(dev, args, filp);
+ break;
+ case AMDXDNA_BO_DEV_HEAP:
+ abo = amdxdna_drm_create_dev_heap(dev, args, filp);
+ break;
+ case AMDXDNA_BO_DEV:
+ abo = amdxdna_drm_alloc_dev_bo(dev, args, filp);
+ break;
+ case AMDXDNA_BO_CMD:
+ abo = amdxdna_drm_create_cmd_bo(dev, args, filp);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (IS_ERR(abo))
+ return PTR_ERR(abo);
+
+ /* ready to publish object to userspace */
+ ret = drm_gem_handle_create(filp, to_gobj(abo), &args->handle);
+ if (ret) {
+ XDNA_ERR(xdna, "Create handle failed");
+ goto put_obj;
+ }
+
+ XDNA_DBG(xdna, "BO hdl %d type %d userptr 0x%llx xdna_addr 0x%llx size 0x%lx",
+ args->handle, args->type, abo->mem.userptr,
+ abo->mem.dev_addr, abo->mem.size);
+put_obj:
+ /* Dereference object reference. Handle holds it now. */
+ drm_gem_object_put(to_gobj(abo));
+ return ret;
+}
+
+int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ int ret;
+
+ if (abo->type == AMDXDNA_BO_DEV)
+ abo = abo->client->dev_heap;
+
+ if (is_import_bo(abo))
+ return 0;
+
+ ret = drm_gem_shmem_pin(&abo->base);
+
+ XDNA_DBG(xdna, "BO type %d ret %d", abo->type, ret);
+ return ret;
+}
+
+int amdxdna_gem_pin(struct amdxdna_gem_obj *abo)
+{
+ int ret;
+
+ mutex_lock(&abo->lock);
+ ret = amdxdna_gem_pin_nolock(abo);
+ mutex_unlock(&abo->lock);
+
+ return ret;
+}
+
+void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo)
+{
+ if (abo->type == AMDXDNA_BO_DEV)
+ abo = abo->client->dev_heap;
+
+ if (is_import_bo(abo))
+ return;
+
+ mutex_lock(&abo->lock);
+ drm_gem_shmem_unpin(&abo->base);
+ mutex_unlock(&abo->lock);
+}
+
+struct amdxdna_gem_obj *amdxdna_gem_get_obj(struct amdxdna_client *client,
+ u32 bo_hdl, u8 bo_type)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_gem_obj *abo;
+ struct drm_gem_object *gobj;
+
+ gobj = drm_gem_object_lookup(client->filp, bo_hdl);
+ if (!gobj) {
+ XDNA_DBG(xdna, "Can not find bo %d", bo_hdl);
+ return NULL;
+ }
+
+ abo = to_xdna_obj(gobj);
+ if (bo_type == AMDXDNA_BO_INVALID || abo->type == bo_type)
+ return abo;
+
+ drm_gem_object_put(gobj);
+ return NULL;
+}
+
+int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_drm_get_bo_info *args = data;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_gem_obj *abo;
+ struct drm_gem_object *gobj;
+ int ret = 0;
+
+ if (args->ext || args->ext_flags || args->pad)
+ return -EINVAL;
+
+ gobj = drm_gem_object_lookup(filp, args->handle);
+ if (!gobj) {
+ XDNA_DBG(xdna, "Lookup GEM object %d failed", args->handle);
+ return -ENOENT;
+ }
+
+ abo = to_xdna_obj(gobj);
+ args->vaddr = abo->mem.userptr;
+ args->xdna_addr = abo->mem.dev_addr;
+
+ if (abo->type != AMDXDNA_BO_DEV)
+ args->map_offset = drm_vma_node_offset_addr(&gobj->vma_node);
+ else
+ args->map_offset = AMDXDNA_INVALID_ADDR;
+
+ XDNA_DBG(xdna, "BO hdl %d map_offset 0x%llx vaddr 0x%llx xdna_addr 0x%llx",
+ args->handle, args->map_offset, args->vaddr, args->xdna_addr);
+
+ drm_gem_object_put(gobj);
+ return ret;
+}
+
+/*
+ * The sync bo ioctl is to make sure the CPU cache is in sync with memory.
+ * This is required because NPU is not cache coherent device. CPU cache
+ * flushing/invalidation is expensive so it is best to handle this outside
+ * of the command submission path. This ioctl allows explicit cache
+ * flushing/invalidation outside of the critical path.
+ */
+int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *filp)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_drm_sync_bo *args = data;
+ struct amdxdna_gem_obj *abo;
+ struct drm_gem_object *gobj;
+ int ret;
+
+ gobj = drm_gem_object_lookup(filp, args->handle);
+ if (!gobj) {
+ XDNA_ERR(xdna, "Lookup GEM object failed");
+ return -ENOENT;
+ }
+ abo = to_xdna_obj(gobj);
+
+ ret = amdxdna_gem_pin(abo);
+ if (ret) {
+ XDNA_ERR(xdna, "Pin BO %d failed, ret %d", args->handle, ret);
+ goto put_obj;
+ }
+
+ if (is_import_bo(abo))
+ drm_clflush_sg(abo->base.sgt);
+ else if (abo->mem.kva)
+ drm_clflush_virt_range(abo->mem.kva + args->offset, args->size);
+ else if (abo->base.pages)
+ drm_clflush_pages(abo->base.pages, gobj->size >> PAGE_SHIFT);
+ else
+ drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
+
+ amdxdna_gem_unpin(abo);
+
+ XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
+ args->handle, args->offset, args->size);
+
+ if (args->direction == SYNC_DIRECT_FROM_DEVICE)
+ ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle);
+
+put_obj:
+ drm_gem_object_put(gobj);
+ return ret;
+}
diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h
new file mode 100644
index 000000000000..f79fc7f3c93b
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_gem.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_GEM_H_
+#define _AMDXDNA_GEM_H_
+
+#include <linux/hmm.h>
+#include "amdxdna_pci_drv.h"
+
+struct amdxdna_umap {
+ struct vm_area_struct *vma;
+ struct mmu_interval_notifier notifier;
+ struct hmm_range range;
+ struct work_struct hmm_unreg_work;
+ struct amdxdna_gem_obj *abo;
+ struct list_head node;
+ struct kref refcnt;
+ bool invalid;
+ bool unmapped;
+};
+
+struct amdxdna_mem {
+ u64 userptr;
+ void *kva;
+ u64 dev_addr;
+ size_t size;
+ struct page **pages;
+ u32 nr_pages;
+ struct list_head umap_list;
+ bool map_invalid;
+};
+
+struct amdxdna_gem_obj {
+ struct drm_gem_shmem_object base;
+ struct amdxdna_client *client;
+ u8 type;
+ bool pinned;
+ struct mutex lock; /* Protects: pinned */
+ struct amdxdna_mem mem;
+
+ /* Below members is uninitialized when needed */
+ struct drm_mm mm; /* For AMDXDNA_BO_DEV_HEAP */
+ struct drm_mm_node mm_node; /* For AMDXDNA_BO_DEV */
+ u32 assigned_hwctx;
+ struct dma_buf *dma_buf;
+ struct dma_buf_attachment *attach;
+};
+
+#define to_gobj(obj) (&(obj)->base.base)
+#define is_import_bo(obj) ((obj)->attach)
+
+static inline struct amdxdna_gem_obj *to_xdna_obj(struct drm_gem_object *gobj)
+{
+ return container_of(gobj, struct amdxdna_gem_obj, base.base);
+}
+
+struct amdxdna_gem_obj *amdxdna_gem_get_obj(struct amdxdna_client *client,
+ u32 bo_hdl, u8 bo_type);
+static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo)
+{
+ drm_gem_object_put(to_gobj(abo));
+}
+
+static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
+{
+ return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr;
+}
+
+void amdxdna_umap_put(struct amdxdna_umap *mapp);
+
+struct drm_gem_object *
+amdxdna_gem_create_object_cb(struct drm_device *dev, size_t size);
+struct drm_gem_object *
+amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf);
+struct amdxdna_gem_obj *
+amdxdna_drm_alloc_dev_bo(struct drm_device *dev,
+ struct amdxdna_drm_create_bo *args,
+ struct drm_file *filp);
+
+int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo);
+int amdxdna_gem_pin(struct amdxdna_gem_obj *abo);
+void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo);
+
+int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+
+#endif /* _AMDXDNA_GEM_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c
new file mode 100644
index 000000000000..858df97cd3fb
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_mailbox.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/drm_device.h>
+#include <drm/drm_managed.h>
+#include <linux/bitfield.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+#include <linux/xarray.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/amdxdna.h>
+
+#include "amdxdna_mailbox.h"
+
+#define MB_ERR(chann, fmt, args...) \
+({ \
+ typeof(chann) _chann = chann; \
+ dev_err((_chann)->mb->dev, "xdna_mailbox.%d: "fmt, \
+ (_chann)->msix_irq, ##args); \
+})
+#define MB_DBG(chann, fmt, args...) \
+({ \
+ typeof(chann) _chann = chann; \
+ dev_dbg((_chann)->mb->dev, "xdna_mailbox.%d: "fmt, \
+ (_chann)->msix_irq, ##args); \
+})
+#define MB_WARN_ONCE(chann, fmt, args...) \
+({ \
+ typeof(chann) _chann = chann; \
+ dev_warn_once((_chann)->mb->dev, "xdna_mailbox.%d: "fmt, \
+ (_chann)->msix_irq, ##args); \
+})
+
+#define MAGIC_VAL 0x1D000000U
+#define MAGIC_VAL_MASK 0xFF000000
+#define MAX_MSG_ID_ENTRIES 256
+#define MSG_RX_TIMER 200 /* milliseconds */
+#define MAILBOX_NAME "xdna_mailbox"
+
+enum channel_res_type {
+ CHAN_RES_X2I,
+ CHAN_RES_I2X,
+ CHAN_RES_NUM
+};
+
+struct mailbox {
+ struct device *dev;
+ struct xdna_mailbox_res res;
+};
+
+struct mailbox_channel {
+ struct mailbox *mb;
+ struct xdna_mailbox_chann_res res[CHAN_RES_NUM];
+ int msix_irq;
+ u32 iohub_int_addr;
+ struct xarray chan_xa;
+ u32 next_msgid;
+ u32 x2i_tail;
+
+ /* Received msg related fields */
+ struct workqueue_struct *work_q;
+ struct work_struct rx_work;
+ u32 i2x_head;
+ bool bad_state;
+};
+
+#define MSG_BODY_SZ GENMASK(10, 0)
+#define MSG_PROTO_VER GENMASK(23, 16)
+struct xdna_msg_header {
+ __u32 total_size;
+ __u32 sz_ver;
+ __u32 id;
+ __u32 opcode;
+} __packed;
+
+static_assert(sizeof(struct xdna_msg_header) == 16);
+
+struct mailbox_pkg {
+ struct xdna_msg_header header;
+ __u32 payload[];
+};
+
+/* The protocol version. */
+#define MSG_PROTOCOL_VERSION 0x1
+/* The tombstone value. */
+#define TOMBSTONE 0xDEADFACE
+
+struct mailbox_msg {
+ void *handle;
+ int (*notify_cb)(void *handle, void __iomem *data, size_t size);
+ size_t pkg_size; /* package size in bytes */
+ struct mailbox_pkg pkg;
+};
+
+static void mailbox_reg_write(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 data)
+{
+ struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
+ void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
+
+ writel(data, ringbuf_addr);
+}
+
+static u32 mailbox_reg_read(struct mailbox_channel *mb_chann, u32 mbox_reg)
+{
+ struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
+ void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
+
+ return readl(ringbuf_addr);
+}
+
+static int mailbox_reg_read_non_zero(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 *val)
+{
+ struct xdna_mailbox_res *mb_res = &mb_chann->mb->res;
+ void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg;
+ int ret, value;
+
+ /* Poll till value is not zero */
+ ret = readx_poll_timeout(readl, ringbuf_addr, value,
+ value, 1 /* us */, 100);
+ if (ret < 0)
+ return ret;
+
+ *val = value;
+ return 0;
+}
+
+static inline void
+mailbox_set_headptr(struct mailbox_channel *mb_chann, u32 headptr_val)
+{
+ mailbox_reg_write(mb_chann, mb_chann->res[CHAN_RES_I2X].mb_head_ptr_reg, headptr_val);
+ mb_chann->i2x_head = headptr_val;
+}
+
+static inline void
+mailbox_set_tailptr(struct mailbox_channel *mb_chann, u32 tailptr_val)
+{
+ mailbox_reg_write(mb_chann, mb_chann->res[CHAN_RES_X2I].mb_tail_ptr_reg, tailptr_val);
+ mb_chann->x2i_tail = tailptr_val;
+}
+
+static inline u32
+mailbox_get_headptr(struct mailbox_channel *mb_chann, enum channel_res_type type)
+{
+ return mailbox_reg_read(mb_chann, mb_chann->res[type].mb_head_ptr_reg);
+}
+
+static inline u32
+mailbox_get_tailptr(struct mailbox_channel *mb_chann, enum channel_res_type type)
+{
+ return mailbox_reg_read(mb_chann, mb_chann->res[type].mb_tail_ptr_reg);
+}
+
+static inline u32
+mailbox_get_ringbuf_size(struct mailbox_channel *mb_chann, enum channel_res_type type)
+{
+ return mb_chann->res[type].rb_size;
+}
+
+static inline int mailbox_validate_msgid(int msg_id)
+{
+ return (msg_id & MAGIC_VAL_MASK) == MAGIC_VAL;
+}
+
+static int mailbox_acquire_msgid(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
+{
+ u32 msg_id;
+ int ret;
+
+ ret = xa_alloc_cyclic_irq(&mb_chann->chan_xa, &msg_id, mb_msg,
+ XA_LIMIT(0, MAX_MSG_ID_ENTRIES - 1),
+ &mb_chann->next_msgid, GFP_NOWAIT);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Add MAGIC_VAL to the higher bits.
+ */
+ msg_id |= MAGIC_VAL;
+ return msg_id;
+}
+
+static void mailbox_release_msgid(struct mailbox_channel *mb_chann, int msg_id)
+{
+ msg_id &= ~MAGIC_VAL_MASK;
+ xa_erase_irq(&mb_chann->chan_xa, msg_id);
+}
+
+static void mailbox_release_msg(struct mailbox_channel *mb_chann,
+ struct mailbox_msg *mb_msg)
+{
+ MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x",
+ mb_msg->pkg.header.id, mb_msg->pkg.header.opcode);
+ if (mb_msg->notify_cb)
+ mb_msg->notify_cb(mb_msg->handle, NULL, 0);
+ kfree(mb_msg);
+}
+
+static int
+mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
+{
+ void __iomem *write_addr;
+ u32 ringbuf_size;
+ u32 head, tail;
+ u32 start_addr;
+ u32 tmp_tail;
+
+ head = mailbox_get_headptr(mb_chann, CHAN_RES_X2I);
+ tail = mb_chann->x2i_tail;
+ ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I);
+ start_addr = mb_chann->res[CHAN_RES_X2I].rb_start_addr;
+ tmp_tail = tail + mb_msg->pkg_size;
+
+ if (tail < head && tmp_tail >= head)
+ goto no_space;
+
+ if (tail >= head && (tmp_tail > ringbuf_size - sizeof(u32) &&
+ mb_msg->pkg_size >= head))
+ goto no_space;
+
+ if (tail >= head && tmp_tail > ringbuf_size - sizeof(u32)) {
+ write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
+ writel(TOMBSTONE, write_addr);
+
+ /* tombstone is set. Write from the start of the ringbuf */
+ tail = 0;
+ }
+
+ write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
+ memcpy_toio(write_addr, &mb_msg->pkg, mb_msg->pkg_size);
+ mailbox_set_tailptr(mb_chann, tail + mb_msg->pkg_size);
+
+ trace_mbox_set_tail(MAILBOX_NAME, mb_chann->msix_irq,
+ mb_msg->pkg.header.opcode,
+ mb_msg->pkg.header.id);
+
+ return 0;
+
+no_space:
+ return -ENOSPC;
+}
+
+static int
+mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *header,
+ void __iomem *data)
+{
+ struct mailbox_msg *mb_msg;
+ int msg_id;
+ int ret = 0;
+
+ msg_id = header->id;
+ if (!mailbox_validate_msgid(msg_id)) {
+ MB_ERR(mb_chann, "Bad message ID 0x%x", msg_id);
+ return -EINVAL;
+ }
+
+ msg_id &= ~MAGIC_VAL_MASK;
+ mb_msg = xa_erase_irq(&mb_chann->chan_xa, msg_id);
+ if (!mb_msg) {
+ MB_ERR(mb_chann, "Cannot find msg 0x%x", msg_id);
+ return -EINVAL;
+ }
+
+ MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x",
+ header->opcode, header->total_size, header->id);
+ if (mb_msg->notify_cb) {
+ ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size);
+ if (unlikely(ret))
+ MB_ERR(mb_chann, "Message callback ret %d", ret);
+ }
+
+ kfree(mb_msg);
+ return ret;
+}
+
+static int mailbox_get_msg(struct mailbox_channel *mb_chann)
+{
+ struct xdna_msg_header header;
+ void __iomem *read_addr;
+ u32 msg_size, rest;
+ u32 ringbuf_size;
+ u32 head, tail;
+ u32 start_addr;
+ int ret;
+
+ if (mailbox_reg_read_non_zero(mb_chann, mb_chann->res[CHAN_RES_I2X].mb_tail_ptr_reg, &tail))
+ return -EINVAL;
+ head = mb_chann->i2x_head;
+ ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_I2X);
+ start_addr = mb_chann->res[CHAN_RES_I2X].rb_start_addr;
+
+ if (unlikely(tail > ringbuf_size || !IS_ALIGNED(tail, 4))) {
+ MB_WARN_ONCE(mb_chann, "Invalid tail 0x%x", tail);
+ return -EINVAL;
+ }
+
+ /* ringbuf empty */
+ if (head == tail)
+ return -ENOENT;
+
+ if (head == ringbuf_size)
+ head = 0;
+
+ /* Peek size of the message or TOMBSTONE */
+ read_addr = mb_chann->mb->res.ringbuf_base + start_addr + head;
+ header.total_size = readl(read_addr);
+ /* size is TOMBSTONE, set next read from 0 */
+ if (header.total_size == TOMBSTONE) {
+ if (head < tail) {
+ MB_WARN_ONCE(mb_chann, "Tombstone, head 0x%x tail 0x%x",
+ head, tail);
+ return -EINVAL;
+ }
+ mailbox_set_headptr(mb_chann, 0);
+ return 0;
+ }
+
+ if (unlikely(!header.total_size || !IS_ALIGNED(header.total_size, 4))) {
+ MB_WARN_ONCE(mb_chann, "Invalid total size 0x%x", header.total_size);
+ return -EINVAL;
+ }
+ msg_size = sizeof(header) + header.total_size;
+
+ if (msg_size > ringbuf_size - head || msg_size > tail - head) {
+ MB_WARN_ONCE(mb_chann, "Invalid message size %d, tail %d, head %d",
+ msg_size, tail, head);
+ return -EINVAL;
+ }
+
+ rest = sizeof(header) - sizeof(u32);
+ read_addr += sizeof(u32);
+ memcpy_fromio((u32 *)&header + 1, read_addr, rest);
+ read_addr += rest;
+
+ ret = mailbox_get_resp(mb_chann, &header, read_addr);
+
+ mailbox_set_headptr(mb_chann, head + msg_size);
+ /* After update head, it can equal to ringbuf_size. This is expected. */
+ trace_mbox_set_head(MAILBOX_NAME, mb_chann->msix_irq,
+ header.opcode, header.id);
+
+ return ret;
+}
+
+static irqreturn_t mailbox_irq_handler(int irq, void *p)
+{
+ struct mailbox_channel *mb_chann = p;
+
+ trace_mbox_irq_handle(MAILBOX_NAME, irq);
+ /* Schedule a rx_work to call the callback functions */
+ queue_work(mb_chann->work_q, &mb_chann->rx_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mailbox_rx_worker(struct work_struct *rx_work)
+{
+ struct mailbox_channel *mb_chann;
+ int ret;
+
+ mb_chann = container_of(rx_work, struct mailbox_channel, rx_work);
+
+ if (READ_ONCE(mb_chann->bad_state)) {
+ MB_ERR(mb_chann, "Channel in bad state, work aborted");
+ return;
+ }
+
+again:
+ mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
+
+ while (1) {
+ /*
+ * If return is 0, keep consuming next message, until there is
+ * no messages or an error happened.
+ */
+ ret = mailbox_get_msg(mb_chann);
+ if (ret == -ENOENT)
+ break;
+
+ /* Other error means device doesn't look good, disable irq. */
+ if (unlikely(ret)) {
+ MB_ERR(mb_chann, "Unexpected ret %d, disable irq", ret);
+ WRITE_ONCE(mb_chann->bad_state, true);
+ return;
+ }
+ }
+
+ /*
+ * The hardware will not generate interrupt if firmware creates a new
+ * response right after driver clears interrupt register. Check
+ * the interrupt register to make sure there is not any new response
+ * before exiting.
+ */
+ if (mailbox_reg_read(mb_chann, mb_chann->iohub_int_addr))
+ goto again;
+}
+
+int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann,
+ const struct xdna_mailbox_msg *msg, u64 tx_timeout)
+{
+ struct xdna_msg_header *header;
+ struct mailbox_msg *mb_msg;
+ size_t pkg_size;
+ int ret;
+
+ pkg_size = sizeof(*header) + msg->send_size;
+ if (pkg_size > mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I)) {
+ MB_ERR(mb_chann, "Message size larger than ringbuf size");
+ return -EINVAL;
+ }
+
+ if (unlikely(!IS_ALIGNED(msg->send_size, 4))) {
+ MB_ERR(mb_chann, "Message must be 4 bytes align");
+ return -EINVAL;
+ }
+
+ /* The fist word in payload can NOT be TOMBSTONE */
+ if (unlikely(((u32 *)msg->send_data)[0] == TOMBSTONE)) {
+ MB_ERR(mb_chann, "Tomb stone in data");
+ return -EINVAL;
+ }
+
+ if (READ_ONCE(mb_chann->bad_state)) {
+ MB_ERR(mb_chann, "Channel in bad state");
+ return -EPIPE;
+ }
+
+ mb_msg = kzalloc(sizeof(*mb_msg) + pkg_size, GFP_KERNEL);
+ if (!mb_msg)
+ return -ENOMEM;
+
+ mb_msg->handle = msg->handle;
+ mb_msg->notify_cb = msg->notify_cb;
+ mb_msg->pkg_size = pkg_size;
+
+ header = &mb_msg->pkg.header;
+ /*
+ * Hardware use total_size and size to split huge message.
+ * We do not support it here. Thus the values are the same.
+ */
+ header->total_size = msg->send_size;
+ header->sz_ver = FIELD_PREP(MSG_BODY_SZ, msg->send_size) |
+ FIELD_PREP(MSG_PROTO_VER, MSG_PROTOCOL_VERSION);
+ header->opcode = msg->opcode;
+ memcpy(mb_msg->pkg.payload, msg->send_data, msg->send_size);
+
+ ret = mailbox_acquire_msgid(mb_chann, mb_msg);
+ if (unlikely(ret < 0)) {
+ MB_ERR(mb_chann, "mailbox_acquire_msgid failed");
+ goto msg_id_failed;
+ }
+ header->id = ret;
+
+ MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x",
+ header->opcode, header->total_size, header->id);
+
+ ret = mailbox_send_msg(mb_chann, mb_msg);
+ if (ret) {
+ MB_DBG(mb_chann, "Error in mailbox send msg, ret %d", ret);
+ goto release_id;
+ }
+
+ return 0;
+
+release_id:
+ mailbox_release_msgid(mb_chann, header->id);
+msg_id_failed:
+ kfree(mb_msg);
+ return ret;
+}
+
+struct mailbox_channel *
+xdna_mailbox_create_channel(struct mailbox *mb,
+ const struct xdna_mailbox_chann_res *x2i,
+ const struct xdna_mailbox_chann_res *i2x,
+ u32 iohub_int_addr,
+ int mb_irq)
+{
+ struct mailbox_channel *mb_chann;
+ int ret;
+
+ if (!is_power_of_2(x2i->rb_size) || !is_power_of_2(i2x->rb_size)) {
+ pr_err("Ring buf size must be power of 2");
+ return NULL;
+ }
+
+ mb_chann = kzalloc(sizeof(*mb_chann), GFP_KERNEL);
+ if (!mb_chann)
+ return NULL;
+
+ mb_chann->mb = mb;
+ mb_chann->msix_irq = mb_irq;
+ mb_chann->iohub_int_addr = iohub_int_addr;
+ memcpy(&mb_chann->res[CHAN_RES_X2I], x2i, sizeof(*x2i));
+ memcpy(&mb_chann->res[CHAN_RES_I2X], i2x, sizeof(*i2x));
+
+ xa_init_flags(&mb_chann->chan_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
+ mb_chann->x2i_tail = mailbox_get_tailptr(mb_chann, CHAN_RES_X2I);
+ mb_chann->i2x_head = mailbox_get_headptr(mb_chann, CHAN_RES_I2X);
+
+ INIT_WORK(&mb_chann->rx_work, mailbox_rx_worker);
+ mb_chann->work_q = create_singlethread_workqueue(MAILBOX_NAME);
+ if (!mb_chann->work_q) {
+ MB_ERR(mb_chann, "Create workqueue failed");
+ goto free_and_out;
+ }
+
+ /* Everything look good. Time to enable irq handler */
+ ret = request_irq(mb_irq, mailbox_irq_handler, 0, MAILBOX_NAME, mb_chann);
+ if (ret) {
+ MB_ERR(mb_chann, "Failed to request irq %d ret %d", mb_irq, ret);
+ goto destroy_wq;
+ }
+
+ mb_chann->bad_state = false;
+ mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
+
+ MB_DBG(mb_chann, "Mailbox channel created (irq: %d)", mb_chann->msix_irq);
+ return mb_chann;
+
+destroy_wq:
+ destroy_workqueue(mb_chann->work_q);
+free_and_out:
+ kfree(mb_chann);
+ return NULL;
+}
+
+int xdna_mailbox_destroy_channel(struct mailbox_channel *mb_chann)
+{
+ struct mailbox_msg *mb_msg;
+ unsigned long msg_id;
+
+ MB_DBG(mb_chann, "IRQ disabled and RX work cancelled");
+ free_irq(mb_chann->msix_irq, mb_chann);
+ destroy_workqueue(mb_chann->work_q);
+ /* We can clean up and release resources */
+
+ xa_for_each(&mb_chann->chan_xa, msg_id, mb_msg)
+ mailbox_release_msg(mb_chann, mb_msg);
+
+ xa_destroy(&mb_chann->chan_xa);
+
+ MB_DBG(mb_chann, "Mailbox channel destroyed, irq: %d", mb_chann->msix_irq);
+ kfree(mb_chann);
+ return 0;
+}
+
+void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann)
+{
+ /* Disable an irq and wait. This might sleep. */
+ disable_irq(mb_chann->msix_irq);
+
+ /* Cancel RX work and wait for it to finish */
+ cancel_work_sync(&mb_chann->rx_work);
+ MB_DBG(mb_chann, "IRQ disabled and RX work cancelled");
+}
+
+struct mailbox *xdnam_mailbox_create(struct drm_device *ddev,
+ const struct xdna_mailbox_res *res)
+{
+ struct mailbox *mb;
+
+ mb = drmm_kzalloc(ddev, sizeof(*mb), GFP_KERNEL);
+ if (!mb)
+ return NULL;
+ mb->dev = ddev->dev;
+
+ /* mailbox and ring buf base and size information */
+ memcpy(&mb->res, res, sizeof(*res));
+
+ return mb;
+}
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.h b/drivers/accel/amdxdna/amdxdna_mailbox.h
new file mode 100644
index 000000000000..ea367f2fb738
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_mailbox.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AIE2_MAILBOX_H_
+#define _AIE2_MAILBOX_H_
+
+struct mailbox;
+struct mailbox_channel;
+
+/*
+ * xdna_mailbox_msg - message struct
+ *
+ * @opcode: opcode for firmware
+ * @handle: handle used for the notify callback
+ * @notify_cb: callback function to notify the sender when there is response
+ * @send_data: pointing to sending data
+ * @send_size: size of the sending data
+ *
+ * The mailbox will split the sending data in to multiple firmware message if
+ * the size of the data is too big. This is transparent to the sender. The
+ * sender will receive one notification.
+ */
+struct xdna_mailbox_msg {
+ u32 opcode;
+ void *handle;
+ int (*notify_cb)(void *handle, void __iomem *data, size_t size);
+ u8 *send_data;
+ size_t send_size;
+};
+
+/*
+ * xdna_mailbox_res - mailbox hardware resource
+ *
+ * @ringbuf_base: ring buffer base address
+ * @ringbuf_size: ring buffer size
+ * @mbox_base: mailbox base address
+ * @mbox_size: mailbox size
+ */
+struct xdna_mailbox_res {
+ void __iomem *ringbuf_base;
+ size_t ringbuf_size;
+ void __iomem *mbox_base;
+ size_t mbox_size;
+ const char *name;
+};
+
+/*
+ * xdna_mailbox_chann_res - resources
+ *
+ * @rb_start_addr: ring buffer start address
+ * @rb_size: ring buffer size
+ * @mb_head_ptr_reg: mailbox head pointer register
+ * @mb_tail_ptr_reg: mailbox tail pointer register
+ */
+struct xdna_mailbox_chann_res {
+ u32 rb_start_addr;
+ u32 rb_size;
+ u32 mb_head_ptr_reg;
+ u32 mb_tail_ptr_reg;
+};
+
+/*
+ * xdna_mailbox_create() -- create mailbox subsystem and initialize
+ *
+ * @ddev: device pointer
+ * @res: SRAM and mailbox resources
+ *
+ * Return: If success, return a handle of mailbox subsystem.
+ * Otherwise, return NULL pointer.
+ */
+struct mailbox *xdnam_mailbox_create(struct drm_device *ddev,
+ const struct xdna_mailbox_res *res);
+
+/*
+ * xdna_mailbox_create_channel() -- Create a mailbox channel instance
+ *
+ * @mailbox: the handle return from xdna_mailbox_create()
+ * @x2i: host to firmware mailbox resources
+ * @i2x: firmware to host mailbox resources
+ * @xdna_mailbox_intr_reg: register addr of MSI-X interrupt
+ * @mb_irq: Linux IRQ number associated with mailbox MSI-X interrupt vector index
+ *
+ * Return: If success, return a handle of mailbox channel. Otherwise, return NULL.
+ */
+struct mailbox_channel *
+xdna_mailbox_create_channel(struct mailbox *mailbox,
+ const struct xdna_mailbox_chann_res *x2i,
+ const struct xdna_mailbox_chann_res *i2x,
+ u32 xdna_mailbox_intr_reg,
+ int mb_irq);
+
+/*
+ * xdna_mailbox_destroy_channel() -- destroy mailbox channel
+ *
+ * @mailbox_chann: the handle return from xdna_mailbox_create_channel()
+ *
+ * Return: if success, return 0. otherwise return error code
+ */
+int xdna_mailbox_destroy_channel(struct mailbox_channel *mailbox_chann);
+
+/*
+ * xdna_mailbox_stop_channel() -- stop mailbox channel
+ *
+ * @mailbox_chann: the handle return from xdna_mailbox_create_channel()
+ *
+ * Return: if success, return 0. otherwise return error code
+ */
+void xdna_mailbox_stop_channel(struct mailbox_channel *mailbox_chann);
+
+/*
+ * xdna_mailbox_send_msg() -- Send a message
+ *
+ * @mailbox_chann: Mailbox channel handle
+ * @msg: message struct for message information
+ * @tx_timeout: the timeout value for sending the message in ms.
+ *
+ * Return: If success return 0, otherwise, return error code
+ */
+int xdna_mailbox_send_msg(struct mailbox_channel *mailbox_chann,
+ const struct xdna_mailbox_msg *msg, u64 tx_timeout);
+
+#endif /* _AIE2_MAILBOX_ */
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox_helper.c b/drivers/accel/amdxdna/amdxdna_mailbox_helper.c
new file mode 100644
index 000000000000..6d0c24513476
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_mailbox_helper.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/completion.h>
+
+#include "amdxdna_gem.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_mailbox_helper.h"
+#include "amdxdna_pci_drv.h"
+
+int xdna_msg_cb(void *handle, void __iomem *data, size_t size)
+{
+ struct xdna_notify *cb_arg = handle;
+ int ret;
+
+ if (unlikely(!data))
+ goto out;
+
+ if (unlikely(cb_arg->size != size)) {
+ cb_arg->error = -EINVAL;
+ goto out;
+ }
+
+ memcpy_fromio(cb_arg->data, data, cb_arg->size);
+ print_hex_dump_debug("resp data: ", DUMP_PREFIX_OFFSET,
+ 16, 4, cb_arg->data, cb_arg->size, true);
+out:
+ ret = cb_arg->error;
+ complete(&cb_arg->comp);
+ return ret;
+}
+
+int xdna_send_msg_wait(struct amdxdna_dev *xdna, struct mailbox_channel *chann,
+ struct xdna_mailbox_msg *msg)
+{
+ struct xdna_notify *hdl = msg->handle;
+ int ret;
+
+ ret = xdna_mailbox_send_msg(chann, msg, TX_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(xdna, "Send message failed, ret %d", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&hdl->comp,
+ msecs_to_jiffies(RX_TIMEOUT));
+ if (!ret) {
+ XDNA_ERR(xdna, "Wait for completion timeout");
+ return -ETIME;
+ }
+
+ return hdl->error;
+}
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox_helper.h b/drivers/accel/amdxdna/amdxdna_mailbox_helper.h
new file mode 100644
index 000000000000..556c712cad0a
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_mailbox_helper.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_MAILBOX_HELPER_H
+#define _AMDXDNA_MAILBOX_HELPER_H
+
+#define TX_TIMEOUT 2000 /* milliseconds */
+#define RX_TIMEOUT 5000 /* milliseconds */
+
+struct amdxdna_dev;
+
+struct xdna_notify {
+ struct completion comp;
+ u32 *data;
+ size_t size;
+ int error;
+ u32 *status;
+};
+
+#define DECLARE_XDNA_MSG_COMMON(name, op, s) \
+ struct name##_req req = { 0 }; \
+ struct name##_resp resp = { .status = s }; \
+ struct xdna_notify hdl = { \
+ .error = 0, \
+ .data = (u32 *)&resp, \
+ .size = sizeof(resp), \
+ .comp = COMPLETION_INITIALIZER_ONSTACK(hdl.comp), \
+ .status = (u32 *)&resp.status, \
+ }; \
+ struct xdna_mailbox_msg msg = { \
+ .send_data = (u8 *)&req, \
+ .send_size = sizeof(req), \
+ .handle = &hdl, \
+ .opcode = op, \
+ .notify_cb = xdna_msg_cb, \
+ }
+
+int xdna_msg_cb(void *handle, void __iomem *data, size_t size);
+int xdna_send_msg_wait(struct amdxdna_dev *xdna, struct mailbox_channel *chann,
+ struct xdna_mailbox_msg *msg);
+
+#endif /* _AMDXDNA_MAILBOX_HELPER_H */
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c
new file mode 100644
index 000000000000..1973ab67721b
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_managed.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/iommu.h>
+#include <linux/pci.h>
+
+#include "amdxdna_ctx.h"
+#include "amdxdna_gem.h"
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
+
+MODULE_FIRMWARE("amdnpu/1502_00/npu.sbin");
+MODULE_FIRMWARE("amdnpu/17f0_10/npu.sbin");
+MODULE_FIRMWARE("amdnpu/17f0_11/npu.sbin");
+MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin");
+
+/*
+ * 0.0: Initial version
+ * 0.1: Support getting all hardware contexts by DRM_IOCTL_AMDXDNA_GET_ARRAY
+ * 0.2: Support getting last error hardware error
+ * 0.3: Support firmware debug buffer
+ * 0.4: Support getting resource information
+ * 0.5: Support getting telemetry data
+ * 0.6: Support preemption
+ */
+#define AMDXDNA_DRIVER_MAJOR 0
+#define AMDXDNA_DRIVER_MINOR 6
+
+/*
+ * Bind the driver base on (vendor_id, device_id) pair and later use the
+ * (device_id, rev_id) pair as a key to select the devices. The devices with
+ * same device_id have very similar interface to host driver.
+ */
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1502) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x17f0) },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static const struct amdxdna_device_id amdxdna_ids[] = {
+ { 0x1502, 0x0, &dev_npu1_info },
+ { 0x17f0, 0x0, &dev_npu2_info },
+ { 0x17f0, 0x10, &dev_npu4_info },
+ { 0x17f0, 0x11, &dev_npu5_info },
+ { 0x17f0, 0x20, &dev_npu6_info },
+ {0}
+};
+
+static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(ddev);
+ struct amdxdna_client *client;
+ int ret;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ client->pid = pid_nr(rcu_access_pointer(filp->pid));
+ client->xdna = xdna;
+
+ client->sva = iommu_sva_bind_device(xdna->ddev.dev, current->mm);
+ if (IS_ERR(client->sva)) {
+ ret = PTR_ERR(client->sva);
+ XDNA_ERR(xdna, "SVA bind device failed, ret %d", ret);
+ goto failed;
+ }
+ client->pasid = iommu_sva_get_pasid(client->sva);
+ if (client->pasid == IOMMU_PASID_INVALID) {
+ XDNA_ERR(xdna, "SVA get pasid failed");
+ ret = -ENODEV;
+ goto unbind_sva;
+ }
+ init_srcu_struct(&client->hwctx_srcu);
+ xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC);
+ mutex_init(&client->mm_lock);
+
+ mutex_lock(&xdna->dev_lock);
+ list_add_tail(&client->node, &xdna->client_list);
+ mutex_unlock(&xdna->dev_lock);
+
+ filp->driver_priv = client;
+ client->filp = filp;
+
+ XDNA_DBG(xdna, "pid %d opened", client->pid);
+ return 0;
+
+unbind_sva:
+ iommu_sva_unbind_device(client->sva);
+failed:
+ kfree(client);
+
+ return ret;
+}
+
+static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = to_xdna_dev(ddev);
+
+ XDNA_DBG(xdna, "closing pid %d", client->pid);
+
+ xa_destroy(&client->hwctx_xa);
+ cleanup_srcu_struct(&client->hwctx_srcu);
+ mutex_destroy(&client->mm_lock);
+ if (client->dev_heap)
+ drm_gem_object_put(to_gobj(client->dev_heap));
+
+ iommu_sva_unbind_device(client->sva);
+
+ XDNA_DBG(xdna, "pid %d closed", client->pid);
+ kfree(client);
+}
+
+static int amdxdna_flush(struct file *f, fl_owner_t id)
+{
+ struct drm_file *filp = f->private_data;
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = client->xdna;
+ int idx;
+
+ XDNA_DBG(xdna, "PID %d flushing...", client->pid);
+ if (!drm_dev_enter(&xdna->ddev, &idx))
+ return 0;
+
+ mutex_lock(&xdna->dev_lock);
+ list_del_init(&client->node);
+ amdxdna_hwctx_remove_all(client);
+ mutex_unlock(&xdna->dev_lock);
+
+ drm_dev_exit(idx);
+ return 0;
+}
+
+static int amdxdna_drm_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_drm_get_info *args = data;
+ int ret;
+
+ if (!xdna->dev_info->ops->get_aie_info)
+ return -EOPNOTSUPP;
+
+ XDNA_DBG(xdna, "Request parameter %u", args->param);
+ mutex_lock(&xdna->dev_lock);
+ ret = xdna->dev_info->ops->get_aie_info(client, args);
+ mutex_unlock(&xdna->dev_lock);
+ return ret;
+}
+
+static int amdxdna_drm_get_array_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_drm_get_array *args = data;
+
+ if (!xdna->dev_info->ops->get_array)
+ return -EOPNOTSUPP;
+
+ if (args->pad || !args->num_element || !args->element_size)
+ return -EINVAL;
+
+ guard(mutex)(&xdna->dev_lock);
+ return xdna->dev_info->ops->get_array(client, args);
+}
+
+static int amdxdna_drm_set_state_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ struct amdxdna_drm_set_state *args = data;
+ int ret;
+
+ if (!xdna->dev_info->ops->set_aie_state)
+ return -EOPNOTSUPP;
+
+ XDNA_DBG(xdna, "Request parameter %u", args->param);
+ mutex_lock(&xdna->dev_lock);
+ ret = xdna->dev_info->ops->set_aie_state(client, args);
+ mutex_unlock(&xdna->dev_lock);
+
+ return ret;
+}
+
+static const struct drm_ioctl_desc amdxdna_drm_ioctls[] = {
+ /* Context */
+ DRM_IOCTL_DEF_DRV(AMDXDNA_CREATE_HWCTX, amdxdna_drm_create_hwctx_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(AMDXDNA_DESTROY_HWCTX, amdxdna_drm_destroy_hwctx_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(AMDXDNA_CONFIG_HWCTX, amdxdna_drm_config_hwctx_ioctl, 0),
+ /* BO */
+ DRM_IOCTL_DEF_DRV(AMDXDNA_CREATE_BO, amdxdna_drm_create_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(AMDXDNA_GET_BO_INFO, amdxdna_drm_get_bo_info_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(AMDXDNA_SYNC_BO, amdxdna_drm_sync_bo_ioctl, 0),
+ /* Execution */
+ DRM_IOCTL_DEF_DRV(AMDXDNA_EXEC_CMD, amdxdna_drm_submit_cmd_ioctl, 0),
+ /* AIE hardware */
+ DRM_IOCTL_DEF_DRV(AMDXDNA_GET_INFO, amdxdna_drm_get_info_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(AMDXDNA_GET_ARRAY, amdxdna_drm_get_array_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(AMDXDNA_SET_STATE, amdxdna_drm_set_state_ioctl, DRM_ROOT_ONLY),
+};
+
+static const struct file_operations amdxdna_fops = {
+ .owner = THIS_MODULE,
+ .open = accel_open,
+ .release = drm_release,
+ .flush = amdxdna_flush,
+ .unlocked_ioctl = drm_ioctl,
+ .compat_ioctl = drm_compat_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = noop_llseek,
+ .mmap = drm_gem_mmap,
+ .fop_flags = FOP_UNSIGNED_OFFSET,
+};
+
+const struct drm_driver amdxdna_drm_drv = {
+ .driver_features = DRIVER_GEM | DRIVER_COMPUTE_ACCEL |
+ DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE,
+ .fops = &amdxdna_fops,
+ .name = "amdxdna_accel_driver",
+ .desc = "AMD XDNA DRM implementation",
+ .major = AMDXDNA_DRIVER_MAJOR,
+ .minor = AMDXDNA_DRIVER_MINOR,
+ .open = amdxdna_drm_open,
+ .postclose = amdxdna_drm_close,
+ .ioctls = amdxdna_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(amdxdna_drm_ioctls),
+
+ .gem_create_object = amdxdna_gem_create_object_cb,
+ .gem_prime_import = amdxdna_gem_prime_import,
+};
+
+static const struct amdxdna_dev_info *
+amdxdna_get_dev_info(struct pci_dev *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(amdxdna_ids); i++) {
+ if (pdev->device == amdxdna_ids[i].device &&
+ pdev->revision == amdxdna_ids[i].revision)
+ return amdxdna_ids[i].dev_info;
+ }
+ return NULL;
+}
+
+static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct amdxdna_dev *xdna;
+ int ret;
+
+ xdna = devm_drm_dev_alloc(dev, &amdxdna_drm_drv, typeof(*xdna), ddev);
+ if (IS_ERR(xdna))
+ return PTR_ERR(xdna);
+
+ xdna->dev_info = amdxdna_get_dev_info(pdev);
+ if (!xdna->dev_info)
+ return -ENODEV;
+
+ drmm_mutex_init(&xdna->ddev, &xdna->dev_lock);
+ init_rwsem(&xdna->notifier_lock);
+ INIT_LIST_HEAD(&xdna->client_list);
+ pci_set_drvdata(pdev, xdna);
+
+ if (IS_ENABLED(CONFIG_LOCKDEP)) {
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&xdna->notifier_lock);
+ fs_reclaim_release(GFP_KERNEL);
+ }
+
+ xdna->notifier_wq = alloc_ordered_workqueue("notifier_wq", 0);
+ if (!xdna->notifier_wq)
+ return -ENOMEM;
+
+ mutex_lock(&xdna->dev_lock);
+ ret = xdna->dev_info->ops->init(xdna);
+ mutex_unlock(&xdna->dev_lock);
+ if (ret) {
+ XDNA_ERR(xdna, "Hardware init failed, ret %d", ret);
+ goto destroy_notifier_wq;
+ }
+
+ ret = amdxdna_sysfs_init(xdna);
+ if (ret) {
+ XDNA_ERR(xdna, "Create amdxdna attrs failed: %d", ret);
+ goto failed_dev_fini;
+ }
+
+ ret = drm_dev_register(&xdna->ddev, 0);
+ if (ret) {
+ XDNA_ERR(xdna, "DRM register failed, ret %d", ret);
+ goto failed_sysfs_fini;
+ }
+
+ return 0;
+
+failed_sysfs_fini:
+ amdxdna_sysfs_fini(xdna);
+failed_dev_fini:
+ mutex_lock(&xdna->dev_lock);
+ xdna->dev_info->ops->fini(xdna);
+ mutex_unlock(&xdna->dev_lock);
+destroy_notifier_wq:
+ destroy_workqueue(xdna->notifier_wq);
+ return ret;
+}
+
+static void amdxdna_remove(struct pci_dev *pdev)
+{
+ struct amdxdna_dev *xdna = pci_get_drvdata(pdev);
+ struct amdxdna_client *client;
+
+ destroy_workqueue(xdna->notifier_wq);
+
+ drm_dev_unplug(&xdna->ddev);
+ amdxdna_sysfs_fini(xdna);
+
+ mutex_lock(&xdna->dev_lock);
+ client = list_first_entry_or_null(&xdna->client_list,
+ struct amdxdna_client, node);
+ while (client) {
+ list_del_init(&client->node);
+ amdxdna_hwctx_remove_all(client);
+
+ client = list_first_entry_or_null(&xdna->client_list,
+ struct amdxdna_client, node);
+ }
+
+ xdna->dev_info->ops->fini(xdna);
+ mutex_unlock(&xdna->dev_lock);
+}
+
+static const struct dev_pm_ops amdxdna_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume)
+ RUNTIME_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume, NULL)
+};
+
+static struct pci_driver amdxdna_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = pci_ids,
+ .probe = amdxdna_probe,
+ .remove = amdxdna_remove,
+ .driver.pm = &amdxdna_pm_ops,
+};
+
+module_pci_driver(amdxdna_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("XRT Team <runtimeca39d@amd.com>");
+MODULE_DESCRIPTION("amdxdna driver");
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h
new file mode 100644
index 000000000000..c99477f5e454
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_PCI_DRV_H_
+#define _AMDXDNA_PCI_DRV_H_
+
+#include <drm/drm_print.h>
+#include <linux/workqueue.h>
+#include <linux/xarray.h>
+
+#define XDNA_INFO(xdna, fmt, args...) drm_info(&(xdna)->ddev, fmt, ##args)
+#define XDNA_WARN(xdna, fmt, args...) drm_warn(&(xdna)->ddev, "%s: "fmt, __func__, ##args)
+#define XDNA_ERR(xdna, fmt, args...) drm_err(&(xdna)->ddev, "%s: "fmt, __func__, ##args)
+#define XDNA_DBG(xdna, fmt, args...) drm_dbg(&(xdna)->ddev, fmt, ##args)
+#define XDNA_INFO_ONCE(xdna, fmt, args...) drm_info_once(&(xdna)->ddev, fmt, ##args)
+
+#define XDNA_MBZ_DBG(xdna, ptr, sz) \
+ ({ \
+ int __i; \
+ int __ret = 0; \
+ u8 *__ptr = (u8 *)(ptr); \
+ for (__i = 0; __i < (sz); __i++) { \
+ if (__ptr[__i]) { \
+ XDNA_DBG(xdna, "MBZ check failed"); \
+ __ret = -EINVAL; \
+ break; \
+ } \
+ } \
+ __ret; \
+ })
+
+#define to_xdna_dev(drm_dev) \
+ ((struct amdxdna_dev *)container_of(drm_dev, struct amdxdna_dev, ddev))
+
+extern const struct drm_driver amdxdna_drm_drv;
+
+struct amdxdna_client;
+struct amdxdna_dev;
+struct amdxdna_drm_get_info;
+struct amdxdna_drm_set_state;
+struct amdxdna_gem_obj;
+struct amdxdna_hwctx;
+struct amdxdna_sched_job;
+
+/*
+ * struct amdxdna_dev_ops - Device hardware operation callbacks
+ */
+struct amdxdna_dev_ops {
+ int (*init)(struct amdxdna_dev *xdna);
+ void (*fini)(struct amdxdna_dev *xdna);
+ int (*resume)(struct amdxdna_dev *xdna);
+ int (*suspend)(struct amdxdna_dev *xdna);
+ int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
+ void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
+ int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
+ int (*hwctx_sync_debug_bo)(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
+ void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
+ int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
+ int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args);
+ int (*set_aie_state)(struct amdxdna_client *client, struct amdxdna_drm_set_state *args);
+ int (*get_array)(struct amdxdna_client *client, struct amdxdna_drm_get_array *args);
+};
+
+/*
+ * struct amdxdna_dev_info - Device hardware information
+ * Record device static information, like reg, mbox, PSP, SMU bar index
+ */
+struct amdxdna_dev_info {
+ int reg_bar;
+ int mbox_bar;
+ int sram_bar;
+ int psp_bar;
+ int smu_bar;
+ int device_type;
+ int first_col;
+ u32 dev_mem_buf_shift;
+ u64 dev_mem_base;
+ size_t dev_mem_size;
+ char *vbnv;
+ const struct amdxdna_dev_priv *dev_priv;
+ const struct amdxdna_dev_ops *ops;
+};
+
+struct amdxdna_fw_ver {
+ u32 major;
+ u32 minor;
+ u32 sub;
+ u32 build;
+};
+
+struct amdxdna_dev {
+ struct drm_device ddev;
+ struct amdxdna_dev_hdl *dev_handle;
+ const struct amdxdna_dev_info *dev_info;
+ void *xrs_hdl;
+
+ struct mutex dev_lock; /* per device lock */
+ struct list_head client_list;
+ struct amdxdna_fw_ver fw_ver;
+ struct rw_semaphore notifier_lock; /* for mmu notifier*/
+ struct workqueue_struct *notifier_wq;
+ bool rpm_on;
+};
+
+/*
+ * struct amdxdna_device_id - PCI device info
+ */
+struct amdxdna_device_id {
+ unsigned short device;
+ u8 revision;
+ const struct amdxdna_dev_info *dev_info;
+};
+
+/*
+ * struct amdxdna_client - amdxdna client
+ * A per fd data structure for managing context and other user process stuffs.
+ */
+struct amdxdna_client {
+ struct list_head node;
+ pid_t pid;
+ struct srcu_struct hwctx_srcu;
+ struct xarray hwctx_xa;
+ u32 next_hwctxid;
+ struct amdxdna_dev *xdna;
+ struct drm_file *filp;
+
+ struct mutex mm_lock; /* protect memory related */
+ struct amdxdna_gem_obj *dev_heap;
+
+ struct iommu_sva *sva;
+ int pasid;
+};
+
+#define amdxdna_for_each_hwctx(client, hwctx_id, entry) \
+ xa_for_each(&(client)->hwctx_xa, hwctx_id, entry)
+
+/* Add device info below */
+extern const struct amdxdna_dev_info dev_npu1_info;
+extern const struct amdxdna_dev_info dev_npu2_info;
+extern const struct amdxdna_dev_info dev_npu4_info;
+extern const struct amdxdna_dev_info dev_npu5_info;
+extern const struct amdxdna_dev_info dev_npu6_info;
+
+int amdxdna_sysfs_init(struct amdxdna_dev *xdna);
+void amdxdna_sysfs_fini(struct amdxdna_dev *xdna);
+
+#endif /* _AMDXDNA_PCI_DRV_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_pm.c b/drivers/accel/amdxdna/amdxdna_pm.c
new file mode 100644
index 000000000000..fa38e65d617c
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_pm.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_drv.h>
+#include <linux/pm_runtime.h>
+
+#include "amdxdna_pm.h"
+
+#define AMDXDNA_AUTOSUSPEND_DELAY 5000 /* milliseconds */
+
+int amdxdna_pm_suspend(struct device *dev)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev));
+ int ret = -EOPNOTSUPP;
+ bool rpm;
+
+ if (xdna->dev_info->ops->suspend) {
+ rpm = xdna->rpm_on;
+ xdna->rpm_on = false;
+ ret = xdna->dev_info->ops->suspend(xdna);
+ xdna->rpm_on = rpm;
+ }
+
+ XDNA_DBG(xdna, "Suspend done ret %d", ret);
+ return ret;
+}
+
+int amdxdna_pm_resume(struct device *dev)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev));
+ int ret = -EOPNOTSUPP;
+ bool rpm;
+
+ if (xdna->dev_info->ops->resume) {
+ rpm = xdna->rpm_on;
+ xdna->rpm_on = false;
+ ret = xdna->dev_info->ops->resume(xdna);
+ xdna->rpm_on = rpm;
+ }
+
+ XDNA_DBG(xdna, "Resume done ret %d", ret);
+ return ret;
+}
+
+int amdxdna_pm_resume_get(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+ int ret;
+
+ if (!xdna->rpm_on)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ XDNA_ERR(xdna, "Resume failed: %d", ret);
+ pm_runtime_set_suspended(dev);
+ }
+
+ return ret;
+}
+
+void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+
+ if (!xdna->rpm_on)
+ return;
+
+ pm_runtime_put_autosuspend(dev);
+}
+
+void amdxdna_pm_init(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, AMDXDNA_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_allow(dev);
+ pm_runtime_put_autosuspend(dev);
+ xdna->rpm_on = true;
+}
+
+void amdxdna_pm_fini(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+
+ xdna->rpm_on = false;
+ pm_runtime_get_noresume(dev);
+ pm_runtime_forbid(dev);
+}
diff --git a/drivers/accel/amdxdna/amdxdna_pm.h b/drivers/accel/amdxdna/amdxdna_pm.h
new file mode 100644
index 000000000000..77b2d6e45570
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_pm.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_PM_H_
+#define _AMDXDNA_PM_H_
+
+#include "amdxdna_pci_drv.h"
+
+int amdxdna_pm_suspend(struct device *dev);
+int amdxdna_pm_resume(struct device *dev);
+int amdxdna_pm_resume_get(struct amdxdna_dev *xdna);
+void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna);
+void amdxdna_pm_init(struct amdxdna_dev *xdna);
+void amdxdna_pm_fini(struct amdxdna_dev *xdna);
+
+#endif /* _AMDXDNA_PM_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_sysfs.c b/drivers/accel/amdxdna/amdxdna_sysfs.c
new file mode 100644
index 000000000000..f27e4ee960a0
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_sysfs.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/types.h>
+
+#include "amdxdna_gem.h"
+#include "amdxdna_pci_drv.h"
+
+static ssize_t vbnv_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct amdxdna_dev *xdna = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", xdna->dev_info->vbnv);
+}
+static DEVICE_ATTR_RO(vbnv);
+
+static ssize_t device_type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct amdxdna_dev *xdna = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", xdna->dev_info->device_type);
+}
+static DEVICE_ATTR_RO(device_type);
+
+static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct amdxdna_dev *xdna = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d.%d.%d.%d\n", xdna->fw_ver.major,
+ xdna->fw_ver.minor, xdna->fw_ver.sub,
+ xdna->fw_ver.build);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static struct attribute *amdxdna_attrs[] = {
+ &dev_attr_device_type.attr,
+ &dev_attr_vbnv.attr,
+ &dev_attr_fw_version.attr,
+ NULL,
+};
+
+static struct attribute_group amdxdna_attr_group = {
+ .attrs = amdxdna_attrs,
+};
+
+int amdxdna_sysfs_init(struct amdxdna_dev *xdna)
+{
+ int ret;
+
+ ret = sysfs_create_group(&xdna->ddev.dev->kobj, &amdxdna_attr_group);
+ if (ret)
+ XDNA_ERR(xdna, "Create attr group failed");
+
+ return ret;
+}
+
+void amdxdna_sysfs_fini(struct amdxdna_dev *xdna)
+{
+ sysfs_remove_group(&xdna->ddev.dev->kobj, &amdxdna_attr_group);
+}
diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
new file mode 100644
index 000000000000..077b2261cf2a
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+#include <linux/dma-buf.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_ubuf.h"
+
+struct amdxdna_ubuf_priv {
+ struct page **pages;
+ u64 nr_pages;
+ enum amdxdna_ubuf_flag flags;
+ struct mm_struct *mm;
+};
+
+static struct sg_table *amdxdna_ubuf_map(struct dma_buf_attachment *attach,
+ enum dma_data_direction direction)
+{
+ struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv;
+ struct sg_table *sg;
+ int ret;
+
+ sg = kzalloc(sizeof(*sg), GFP_KERNEL);
+ if (!sg)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->nr_pages, 0,
+ ubuf->nr_pages << PAGE_SHIFT, GFP_KERNEL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) {
+ ret = dma_map_sgtable(attach->dev, sg, direction, 0);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ return sg;
+}
+
+static void amdxdna_ubuf_unmap(struct dma_buf_attachment *attach,
+ struct sg_table *sg,
+ enum dma_data_direction direction)
+{
+ struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv;
+
+ if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA)
+ dma_unmap_sgtable(attach->dev, sg, direction, 0);
+
+ sg_free_table(sg);
+ kfree(sg);
+}
+
+static void amdxdna_ubuf_release(struct dma_buf *dbuf)
+{
+ struct amdxdna_ubuf_priv *ubuf = dbuf->priv;
+
+ unpin_user_pages(ubuf->pages, ubuf->nr_pages);
+ kvfree(ubuf->pages);
+ atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm);
+ mmdrop(ubuf->mm);
+ kfree(ubuf);
+}
+
+static vm_fault_t amdxdna_ubuf_vm_fault(struct vm_fault *vmf)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ struct amdxdna_ubuf_priv *ubuf;
+ unsigned long pfn;
+ pgoff_t pgoff;
+
+ ubuf = vma->vm_private_data;
+ pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
+
+ pfn = page_to_pfn(ubuf->pages[pgoff]);
+ return vmf_insert_pfn(vma, vmf->address, pfn);
+}
+
+static const struct vm_operations_struct amdxdna_ubuf_vm_ops = {
+ .fault = amdxdna_ubuf_vm_fault,
+};
+
+static int amdxdna_ubuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
+{
+ struct amdxdna_ubuf_priv *ubuf = dbuf->priv;
+
+ vma->vm_ops = &amdxdna_ubuf_vm_ops;
+ vma->vm_private_data = ubuf;
+ vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
+
+ return 0;
+}
+
+static int amdxdna_ubuf_vmap(struct dma_buf *dbuf, struct iosys_map *map)
+{
+ struct amdxdna_ubuf_priv *ubuf = dbuf->priv;
+ void *kva;
+
+ kva = vmap(ubuf->pages, ubuf->nr_pages, VM_MAP, PAGE_KERNEL);
+ if (!kva)
+ return -EINVAL;
+
+ iosys_map_set_vaddr(map, kva);
+ return 0;
+}
+
+static void amdxdna_ubuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map)
+{
+ vunmap(map->vaddr);
+}
+
+static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = {
+ .map_dma_buf = amdxdna_ubuf_map,
+ .unmap_dma_buf = amdxdna_ubuf_unmap,
+ .release = amdxdna_ubuf_release,
+ .mmap = amdxdna_ubuf_mmap,
+ .vmap = amdxdna_ubuf_vmap,
+ .vunmap = amdxdna_ubuf_vunmap,
+};
+
+struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
+ enum amdxdna_ubuf_flag flags,
+ u32 num_entries, void __user *va_entries)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev);
+ unsigned long lock_limit, new_pinned;
+ struct amdxdna_drm_va_entry *va_ent;
+ struct amdxdna_ubuf_priv *ubuf;
+ u32 npages, start = 0;
+ struct dma_buf *dbuf;
+ int i, ret;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ if (!can_do_mlock())
+ return ERR_PTR(-EPERM);
+
+ ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL);
+ if (!ubuf)
+ return ERR_PTR(-ENOMEM);
+
+ ubuf->flags = flags;
+ ubuf->mm = current->mm;
+ mmgrab(ubuf->mm);
+
+ va_ent = kvcalloc(num_entries, sizeof(*va_ent), GFP_KERNEL);
+ if (!va_ent) {
+ ret = -ENOMEM;
+ goto free_ubuf;
+ }
+
+ if (copy_from_user(va_ent, va_entries, sizeof(*va_ent) * num_entries)) {
+ XDNA_DBG(xdna, "Access va entries failed");
+ ret = -EINVAL;
+ goto free_ent;
+ }
+
+ for (i = 0, exp_info.size = 0; i < num_entries; i++) {
+ if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) ||
+ !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) {
+ XDNA_ERR(xdna, "Invalid address or len %llx, %llx",
+ va_ent[i].vaddr, va_ent[i].len);
+ ret = -EINVAL;
+ goto free_ent;
+ }
+
+ exp_info.size += va_ent[i].len;
+ }
+
+ ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
+ lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
+ if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {
+ XDNA_DBG(xdna, "New pin %ld, limit %ld, cap %d",
+ new_pinned, lock_limit, capable(CAP_IPC_LOCK));
+ ret = -ENOMEM;
+ goto sub_pin_cnt;
+ }
+
+ ubuf->pages = kvmalloc_array(ubuf->nr_pages, sizeof(*ubuf->pages), GFP_KERNEL);
+ if (!ubuf->pages) {
+ ret = -ENOMEM;
+ goto sub_pin_cnt;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ npages = va_ent[i].len >> PAGE_SHIFT;
+
+ ret = pin_user_pages_fast(va_ent[i].vaddr, npages,
+ FOLL_WRITE | FOLL_LONGTERM,
+ &ubuf->pages[start]);
+ if (ret < 0 || ret != npages) {
+ ret = -ENOMEM;
+ XDNA_ERR(xdna, "Failed to pin pages ret %d", ret);
+ goto destroy_pages;
+ }
+
+ start += ret;
+ }
+
+ exp_info.ops = &amdxdna_ubuf_dmabuf_ops;
+ exp_info.priv = ubuf;
+ exp_info.flags = O_RDWR | O_CLOEXEC;
+
+ dbuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dbuf)) {
+ ret = PTR_ERR(dbuf);
+ goto destroy_pages;
+ }
+ kvfree(va_ent);
+
+ return dbuf;
+
+destroy_pages:
+ if (start)
+ unpin_user_pages(ubuf->pages, start);
+ kvfree(ubuf->pages);
+sub_pin_cnt:
+ atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm);
+free_ent:
+ kvfree(va_ent);
+free_ubuf:
+ mmdrop(ubuf->mm);
+ kfree(ubuf);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.h b/drivers/accel/amdxdna/amdxdna_ubuf.h
new file mode 100644
index 000000000000..e5cb3bdb3ec9
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_ubuf.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+#ifndef _AMDXDNA_UBUF_H_
+#define _AMDXDNA_UBUF_H_
+
+#include <drm/drm_device.h>
+#include <linux/dma-buf.h>
+
+enum amdxdna_ubuf_flag {
+ AMDXDNA_UBUF_FLAG_MAP_DMA = 1,
+};
+
+struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
+ enum amdxdna_ubuf_flag flags,
+ u32 num_entries, void __user *va_entries);
+
+#endif /* _AMDXDNA_UBUF_H_ */
diff --git a/drivers/accel/amdxdna/npu1_regs.c b/drivers/accel/amdxdna/npu1_regs.c
new file mode 100644
index 000000000000..ec407f3b48fc
--- /dev/null
+++ b/drivers/accel/amdxdna/npu1_regs.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/sizes.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+/* Address definition from NPU1 docs */
+#define MPNPU_PUB_SEC_INTR 0x3010090
+#define MPNPU_PUB_PWRMGMT_INTR 0x3010094
+#define MPNPU_PUB_SCRATCH2 0x30100A0
+#define MPNPU_PUB_SCRATCH3 0x30100A4
+#define MPNPU_PUB_SCRATCH4 0x30100A8
+#define MPNPU_PUB_SCRATCH5 0x30100AC
+#define MPNPU_PUB_SCRATCH6 0x30100B0
+#define MPNPU_PUB_SCRATCH7 0x30100B4
+#define MPNPU_PUB_SCRATCH9 0x30100BC
+
+#define MPNPU_SRAM_X2I_MAILBOX_0 0x30A0000
+#define MPNPU_SRAM_X2I_MAILBOX_1 0x30A2000
+#define MPNPU_SRAM_I2X_MAILBOX_15 0x30BF000
+
+#define MPNPU_APERTURE0_BASE 0x3000000
+#define MPNPU_APERTURE1_BASE 0x3080000
+#define MPNPU_APERTURE2_BASE 0x30C0000
+
+/* PCIe BAR Index for NPU1 */
+#define NPU1_REG_BAR_INDEX 0
+#define NPU1_MBOX_BAR_INDEX 4
+#define NPU1_PSP_BAR_INDEX 0
+#define NPU1_SMU_BAR_INDEX 0
+#define NPU1_SRAM_BAR_INDEX 2
+/* Associated BARs and Apertures */
+#define NPU1_REG_BAR_BASE MPNPU_APERTURE0_BASE
+#define NPU1_MBOX_BAR_BASE MPNPU_APERTURE2_BASE
+#define NPU1_PSP_BAR_BASE MPNPU_APERTURE0_BASE
+#define NPU1_SMU_BAR_BASE MPNPU_APERTURE0_BASE
+#define NPU1_SRAM_BAR_BASE MPNPU_APERTURE1_BASE
+
+const struct rt_config npu1_default_rt_cfg[] = {
+ { 2, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
+ { 4, 1, AIE2_RT_CFG_INIT }, /* Debug BO */
+ { 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
+ { 0 },
+};
+
+const struct dpm_clk_freq npu1_dpm_clk_table[] = {
+ {400, 800},
+ {600, 1024},
+ {600, 1024},
+ {600, 1024},
+ {600, 1024},
+ {720, 1309},
+ {720, 1309},
+ {847, 1600},
+ { 0 }
+};
+
+static const struct aie2_fw_feature_tbl npu1_fw_feature_table[] = {
+ { .feature = AIE2_NPU_COMMAND, .min_minor = 8 },
+ { 0 }
+};
+
+static const struct amdxdna_dev_priv npu1_dev_priv = {
+ .fw_path = "amdnpu/1502_00/npu.sbin",
+ .protocol_major = 0x5,
+ .protocol_minor = 0x7,
+ .rt_config = npu1_default_rt_cfg,
+ .dpm_clk_tbl = npu1_dpm_clk_table,
+ .fw_feature_tbl = npu1_fw_feature_table,
+ .col_align = COL_ALIGN_NONE,
+ .mbox_dev_addr = NPU1_MBOX_BAR_BASE,
+ .mbox_size = 0, /* Use BAR size */
+ .sram_dev_addr = NPU1_SRAM_BAR_BASE,
+ .hwctx_limit = 6,
+ .sram_offs = {
+ DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU1_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
+ DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU1_SRAM, MPNPU_SRAM_I2X_MAILBOX_15),
+ },
+ .psp_regs_off = {
+ DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU1_PSP, MPNPU_PUB_SCRATCH2),
+ DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU1_PSP, MPNPU_PUB_SCRATCH3),
+ DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU1_PSP, MPNPU_PUB_SCRATCH4),
+ DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU1_PSP, MPNPU_PUB_SCRATCH9),
+ DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU1_PSP, MPNPU_PUB_SEC_INTR),
+ DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU1_PSP, MPNPU_PUB_SCRATCH2),
+ DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU1_PSP, MPNPU_PUB_SCRATCH3),
+ },
+ .smu_regs_off = {
+ DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU1_SMU, MPNPU_PUB_SCRATCH5),
+ DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU1_SMU, MPNPU_PUB_SCRATCH7),
+ DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU1_SMU, MPNPU_PUB_PWRMGMT_INTR),
+ DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU1_SMU, MPNPU_PUB_SCRATCH6),
+ DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU1_SMU, MPNPU_PUB_SCRATCH7),
+ },
+ .hw_ops = {
+ .set_dpm = npu1_set_dpm,
+ },
+};
+
+const struct amdxdna_dev_info dev_npu1_info = {
+ .reg_bar = NPU1_REG_BAR_INDEX,
+ .mbox_bar = NPU1_MBOX_BAR_INDEX,
+ .sram_bar = NPU1_SRAM_BAR_INDEX,
+ .psp_bar = NPU1_PSP_BAR_INDEX,
+ .smu_bar = NPU1_SMU_BAR_INDEX,
+ .first_col = 1,
+ .dev_mem_buf_shift = 15, /* 32 KiB aligned */
+ .dev_mem_base = AIE2_DEVM_BASE,
+ .dev_mem_size = AIE2_DEVM_SIZE,
+ .vbnv = "RyzenAI-npu1",
+ .device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .dev_priv = &npu1_dev_priv,
+ .ops = &aie2_ops,
+};
diff --git a/drivers/accel/amdxdna/npu2_regs.c b/drivers/accel/amdxdna/npu2_regs.c
new file mode 100644
index 000000000000..86f87d0d1354
--- /dev/null
+++ b/drivers/accel/amdxdna/npu2_regs.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/sizes.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+/* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */
+#define MPNPU_PUB_SEC_INTR 0x3010060
+#define MPNPU_PUB_PWRMGMT_INTR 0x3010064
+#define MPNPU_PUB_SCRATCH0 0x301006C
+#define MPNPU_PUB_SCRATCH1 0x3010070
+#define MPNPU_PUB_SCRATCH2 0x3010074
+#define MPNPU_PUB_SCRATCH3 0x3010078
+#define MPNPU_PUB_SCRATCH4 0x301007C
+#define MPNPU_PUB_SCRATCH5 0x3010080
+#define MPNPU_PUB_SCRATCH6 0x3010084
+#define MPNPU_PUB_SCRATCH7 0x3010088
+#define MPNPU_PUB_SCRATCH8 0x301008C
+#define MPNPU_PUB_SCRATCH9 0x3010090
+#define MPNPU_PUB_SCRATCH10 0x3010094
+#define MPNPU_PUB_SCRATCH11 0x3010098
+#define MPNPU_PUB_SCRATCH12 0x301009C
+#define MPNPU_PUB_SCRATCH13 0x30100A0
+#define MPNPU_PUB_SCRATCH14 0x30100A4
+#define MPNPU_PUB_SCRATCH15 0x30100A8
+#define MP0_C2PMSG_73 0x3810A24
+#define MP0_C2PMSG_123 0x3810AEC
+
+#define MP1_C2PMSG_0 0x3B10900
+#define MP1_C2PMSG_60 0x3B109F0
+#define MP1_C2PMSG_61 0x3B109F4
+
+#define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000
+#define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000
+#define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000
+#define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000
+
+#define MMNPU_APERTURE0_BASE 0x3000000
+#define MMNPU_APERTURE1_BASE 0x3600000
+#define MMNPU_APERTURE3_BASE 0x3810000
+#define MMNPU_APERTURE4_BASE 0x3B10000
+
+/* PCIe BAR Index for NPU2 */
+#define NPU2_REG_BAR_INDEX 0
+#define NPU2_MBOX_BAR_INDEX 0
+#define NPU2_PSP_BAR_INDEX 4
+#define NPU2_SMU_BAR_INDEX 5
+#define NPU2_SRAM_BAR_INDEX 2
+/* Associated BARs and Apertures */
+#define NPU2_REG_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU2_MBOX_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU2_PSP_BAR_BASE MMNPU_APERTURE3_BASE
+#define NPU2_SMU_BAR_BASE MMNPU_APERTURE4_BASE
+#define NPU2_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
+
+static const struct amdxdna_dev_priv npu2_dev_priv = {
+ .fw_path = "amdnpu/17f0_00/npu.sbin",
+ .protocol_major = 0x6,
+ .protocol_minor = 0x6,
+ .rt_config = npu4_default_rt_cfg,
+ .dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
+ .col_align = COL_ALIGN_NATURE,
+ .mbox_dev_addr = NPU2_MBOX_BAR_BASE,
+ .mbox_size = 0, /* Use BAR size */
+ .sram_dev_addr = NPU2_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
+ .sram_offs = {
+ DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
+ DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
+ },
+ .psp_regs_off = {
+ DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU2_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU2_REG, MPNPU_PUB_SCRATCH3),
+ DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU2_REG, MPNPU_PUB_SCRATCH4),
+ DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU2_REG, MPNPU_PUB_SCRATCH9),
+ DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU2_PSP, MP0_C2PMSG_73),
+ DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU2_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU2_REG, MPNPU_PUB_SCRATCH3),
+ },
+ .smu_regs_off = {
+ DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU2_SMU, MP1_C2PMSG_0),
+ DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU2_SMU, MP1_C2PMSG_60),
+ DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU2_SMU, MMNPU_APERTURE4_BASE),
+ DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU2_SMU, MP1_C2PMSG_61),
+ DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU2_SMU, MP1_C2PMSG_60),
+ },
+ .hw_ops = {
+ .set_dpm = npu4_set_dpm,
+ },
+};
+
+const struct amdxdna_dev_info dev_npu2_info = {
+ .reg_bar = NPU2_REG_BAR_INDEX,
+ .mbox_bar = NPU2_MBOX_BAR_INDEX,
+ .sram_bar = NPU2_SRAM_BAR_INDEX,
+ .psp_bar = NPU2_PSP_BAR_INDEX,
+ .smu_bar = NPU2_SMU_BAR_INDEX,
+ .first_col = 0,
+ .dev_mem_buf_shift = 15, /* 32 KiB aligned */
+ .dev_mem_base = AIE2_DEVM_BASE,
+ .dev_mem_size = AIE2_DEVM_SIZE,
+ .vbnv = "RyzenAI-npu2",
+ .device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .dev_priv = &npu2_dev_priv,
+ .ops = &aie2_ops, /* NPU2 can share NPU1's callback */
+};
diff --git a/drivers/accel/amdxdna/npu4_regs.c b/drivers/accel/amdxdna/npu4_regs.c
new file mode 100644
index 000000000000..986a5f28ba24
--- /dev/null
+++ b/drivers/accel/amdxdna/npu4_regs.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/sizes.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+/* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */
+#define MPNPU_PUB_SEC_INTR 0x3010060
+#define MPNPU_PUB_PWRMGMT_INTR 0x3010064
+#define MPNPU_PUB_SCRATCH0 0x301006C
+#define MPNPU_PUB_SCRATCH1 0x3010070
+#define MPNPU_PUB_SCRATCH2 0x3010074
+#define MPNPU_PUB_SCRATCH3 0x3010078
+#define MPNPU_PUB_SCRATCH4 0x301007C
+#define MPNPU_PUB_SCRATCH5 0x3010080
+#define MPNPU_PUB_SCRATCH6 0x3010084
+#define MPNPU_PUB_SCRATCH7 0x3010088
+#define MPNPU_PUB_SCRATCH8 0x301008C
+#define MPNPU_PUB_SCRATCH9 0x3010090
+#define MPNPU_PUB_SCRATCH10 0x3010094
+#define MPNPU_PUB_SCRATCH11 0x3010098
+#define MPNPU_PUB_SCRATCH12 0x301009C
+#define MPNPU_PUB_SCRATCH13 0x30100A0
+#define MPNPU_PUB_SCRATCH14 0x30100A4
+#define MPNPU_PUB_SCRATCH15 0x30100A8
+#define MP0_C2PMSG_73 0x3810A24
+#define MP0_C2PMSG_123 0x3810AEC
+
+#define MP1_C2PMSG_0 0x3B10900
+#define MP1_C2PMSG_60 0x3B109F0
+#define MP1_C2PMSG_61 0x3B109F4
+
+#define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000
+#define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000
+#define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000
+#define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000
+
+#define MMNPU_APERTURE0_BASE 0x3000000
+#define MMNPU_APERTURE1_BASE 0x3600000
+#define MMNPU_APERTURE3_BASE 0x3810000
+#define MMNPU_APERTURE4_BASE 0x3B10000
+
+/* PCIe BAR Index for NPU4 */
+#define NPU4_REG_BAR_INDEX 0
+#define NPU4_MBOX_BAR_INDEX 0
+#define NPU4_PSP_BAR_INDEX 4
+#define NPU4_SMU_BAR_INDEX 5
+#define NPU4_SRAM_BAR_INDEX 2
+/* Associated BARs and Apertures */
+#define NPU4_REG_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU4_MBOX_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU4_PSP_BAR_BASE MMNPU_APERTURE3_BASE
+#define NPU4_SMU_BAR_BASE MMNPU_APERTURE4_BASE
+#define NPU4_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
+
+const struct rt_config npu4_default_rt_cfg[] = {
+ { 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
+ { 10, 1, AIE2_RT_CFG_INIT }, /* DEBUG BUF */
+ { 14, 0, AIE2_RT_CFG_INIT, BIT_U64(AIE2_PREEMPT) }, /* Frame boundary preemption */
+ { 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
+ { 2, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
+ { 3, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
+ { 4, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
+ { 13, 0, AIE2_RT_CFG_FORCE_PREEMPT },
+ { 14, 0, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT },
+ { 0 },
+};
+
+const struct dpm_clk_freq npu4_dpm_clk_table[] = {
+ {396, 792},
+ {600, 1056},
+ {792, 1152},
+ {975, 1267},
+ {975, 1267},
+ {1056, 1408},
+ {1152, 1584},
+ {1267, 1800},
+ { 0 }
+};
+
+const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = {
+ { .feature = AIE2_NPU_COMMAND, .min_minor = 15 },
+ { .feature = AIE2_PREEMPT, .min_minor = 12 },
+ { 0 }
+};
+
+static const struct amdxdna_dev_priv npu4_dev_priv = {
+ .fw_path = "amdnpu/17f0_10/npu.sbin",
+ .protocol_major = 0x6,
+ .protocol_minor = 12,
+ .rt_config = npu4_default_rt_cfg,
+ .dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
+ .col_align = COL_ALIGN_NATURE,
+ .mbox_dev_addr = NPU4_MBOX_BAR_BASE,
+ .mbox_size = 0, /* Use BAR size */
+ .sram_dev_addr = NPU4_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
+ .sram_offs = {
+ DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU4_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
+ DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU4_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
+ },
+ .psp_regs_off = {
+ DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU4_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU4_REG, MPNPU_PUB_SCRATCH3),
+ DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU4_REG, MPNPU_PUB_SCRATCH4),
+ DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU4_REG, MPNPU_PUB_SCRATCH9),
+ DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU4_PSP, MP0_C2PMSG_73),
+ DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU4_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU4_REG, MPNPU_PUB_SCRATCH3),
+ },
+ .smu_regs_off = {
+ DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU4_SMU, MP1_C2PMSG_0),
+ DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU4_SMU, MP1_C2PMSG_60),
+ DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU4_SMU, MMNPU_APERTURE4_BASE),
+ DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU4_SMU, MP1_C2PMSG_61),
+ DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU4_SMU, MP1_C2PMSG_60),
+ },
+ .hw_ops = {
+ .set_dpm = npu4_set_dpm,
+ },
+};
+
+const struct amdxdna_dev_info dev_npu4_info = {
+ .reg_bar = NPU4_REG_BAR_INDEX,
+ .mbox_bar = NPU4_MBOX_BAR_INDEX,
+ .sram_bar = NPU4_SRAM_BAR_INDEX,
+ .psp_bar = NPU4_PSP_BAR_INDEX,
+ .smu_bar = NPU4_SMU_BAR_INDEX,
+ .first_col = 0,
+ .dev_mem_buf_shift = 15, /* 32 KiB aligned */
+ .dev_mem_base = AIE2_DEVM_BASE,
+ .dev_mem_size = AIE2_DEVM_SIZE,
+ .vbnv = "RyzenAI-npu4",
+ .device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .dev_priv = &npu4_dev_priv,
+ .ops = &aie2_ops, /* NPU4 can share NPU1's callback */
+};
diff --git a/drivers/accel/amdxdna/npu5_regs.c b/drivers/accel/amdxdna/npu5_regs.c
new file mode 100644
index 000000000000..75ad97f0b937
--- /dev/null
+++ b/drivers/accel/amdxdna/npu5_regs.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/sizes.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+/* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */
+#define MPNPU_PUB_SEC_INTR 0x3010060
+#define MPNPU_PUB_PWRMGMT_INTR 0x3010064
+#define MPNPU_PUB_SCRATCH0 0x301006C
+#define MPNPU_PUB_SCRATCH1 0x3010070
+#define MPNPU_PUB_SCRATCH2 0x3010074
+#define MPNPU_PUB_SCRATCH3 0x3010078
+#define MPNPU_PUB_SCRATCH4 0x301007C
+#define MPNPU_PUB_SCRATCH5 0x3010080
+#define MPNPU_PUB_SCRATCH6 0x3010084
+#define MPNPU_PUB_SCRATCH7 0x3010088
+#define MPNPU_PUB_SCRATCH8 0x301008C
+#define MPNPU_PUB_SCRATCH9 0x3010090
+#define MPNPU_PUB_SCRATCH10 0x3010094
+#define MPNPU_PUB_SCRATCH11 0x3010098
+#define MPNPU_PUB_SCRATCH12 0x301009C
+#define MPNPU_PUB_SCRATCH13 0x30100A0
+#define MPNPU_PUB_SCRATCH14 0x30100A4
+#define MPNPU_PUB_SCRATCH15 0x30100A8
+#define MP0_C2PMSG_73 0x3810A24
+#define MP0_C2PMSG_123 0x3810AEC
+
+#define MP1_C2PMSG_0 0x3B10900
+#define MP1_C2PMSG_60 0x3B109F0
+#define MP1_C2PMSG_61 0x3B109F4
+
+#define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000
+#define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000
+#define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000
+#define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000
+
+#define MMNPU_APERTURE0_BASE 0x3000000
+#define MMNPU_APERTURE1_BASE 0x3600000
+#define MMNPU_APERTURE3_BASE 0x3810000
+#define MMNPU_APERTURE4_BASE 0x3B10000
+
+/* PCIe BAR Index for NPU5 */
+#define NPU5_REG_BAR_INDEX 0
+#define NPU5_MBOX_BAR_INDEX 0
+#define NPU5_PSP_BAR_INDEX 4
+#define NPU5_SMU_BAR_INDEX 5
+#define NPU5_SRAM_BAR_INDEX 2
+/* Associated BARs and Apertures */
+#define NPU5_REG_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU5_MBOX_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU5_PSP_BAR_BASE MMNPU_APERTURE3_BASE
+#define NPU5_SMU_BAR_BASE MMNPU_APERTURE4_BASE
+#define NPU5_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
+
+static const struct amdxdna_dev_priv npu5_dev_priv = {
+ .fw_path = "amdnpu/17f0_11/npu.sbin",
+ .protocol_major = 0x6,
+ .protocol_minor = 12,
+ .rt_config = npu4_default_rt_cfg,
+ .dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
+ .col_align = COL_ALIGN_NATURE,
+ .mbox_dev_addr = NPU5_MBOX_BAR_BASE,
+ .mbox_size = 0, /* Use BAR size */
+ .sram_dev_addr = NPU5_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
+ .sram_offs = {
+ DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU5_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
+ DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU5_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
+ },
+ .psp_regs_off = {
+ DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU5_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU5_REG, MPNPU_PUB_SCRATCH3),
+ DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU5_REG, MPNPU_PUB_SCRATCH4),
+ DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU5_REG, MPNPU_PUB_SCRATCH9),
+ DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU5_PSP, MP0_C2PMSG_73),
+ DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU5_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU5_REG, MPNPU_PUB_SCRATCH3),
+ },
+ .smu_regs_off = {
+ DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU5_SMU, MP1_C2PMSG_0),
+ DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU5_SMU, MP1_C2PMSG_60),
+ DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU5_SMU, MMNPU_APERTURE4_BASE),
+ DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU5_SMU, MP1_C2PMSG_61),
+ DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU5_SMU, MP1_C2PMSG_60),
+ },
+ .hw_ops = {
+ .set_dpm = npu4_set_dpm,
+ },
+};
+
+const struct amdxdna_dev_info dev_npu5_info = {
+ .reg_bar = NPU5_REG_BAR_INDEX,
+ .mbox_bar = NPU5_MBOX_BAR_INDEX,
+ .sram_bar = NPU5_SRAM_BAR_INDEX,
+ .psp_bar = NPU5_PSP_BAR_INDEX,
+ .smu_bar = NPU5_SMU_BAR_INDEX,
+ .first_col = 0,
+ .dev_mem_buf_shift = 15, /* 32 KiB aligned */
+ .dev_mem_base = AIE2_DEVM_BASE,
+ .dev_mem_size = AIE2_DEVM_SIZE,
+ .vbnv = "RyzenAI-npu5",
+ .device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .dev_priv = &npu5_dev_priv,
+ .ops = &aie2_ops,
+};
diff --git a/drivers/accel/amdxdna/npu6_regs.c b/drivers/accel/amdxdna/npu6_regs.c
new file mode 100644
index 000000000000..758dc013fe13
--- /dev/null
+++ b/drivers/accel/amdxdna/npu6_regs.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/sizes.h>
+
+#include "aie2_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+/* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */
+#define MPNPU_PUB_SEC_INTR 0x3010060
+#define MPNPU_PUB_PWRMGMT_INTR 0x3010064
+#define MPNPU_PUB_SCRATCH0 0x301006C
+#define MPNPU_PUB_SCRATCH1 0x3010070
+#define MPNPU_PUB_SCRATCH2 0x3010074
+#define MPNPU_PUB_SCRATCH3 0x3010078
+#define MPNPU_PUB_SCRATCH4 0x301007C
+#define MPNPU_PUB_SCRATCH5 0x3010080
+#define MPNPU_PUB_SCRATCH6 0x3010084
+#define MPNPU_PUB_SCRATCH7 0x3010088
+#define MPNPU_PUB_SCRATCH8 0x301008C
+#define MPNPU_PUB_SCRATCH9 0x3010090
+#define MPNPU_PUB_SCRATCH10 0x3010094
+#define MPNPU_PUB_SCRATCH11 0x3010098
+#define MPNPU_PUB_SCRATCH12 0x301009C
+#define MPNPU_PUB_SCRATCH13 0x30100A0
+#define MPNPU_PUB_SCRATCH14 0x30100A4
+#define MPNPU_PUB_SCRATCH15 0x30100A8
+#define MP0_C2PMSG_73 0x3810A24
+#define MP0_C2PMSG_123 0x3810AEC
+
+#define MP1_C2PMSG_0 0x3B10900
+#define MP1_C2PMSG_60 0x3B109F0
+#define MP1_C2PMSG_61 0x3B109F4
+
+#define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000
+#define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000
+#define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000
+#define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000
+
+#define MMNPU_APERTURE0_BASE 0x3000000
+#define MMNPU_APERTURE1_BASE 0x3600000
+#define MMNPU_APERTURE3_BASE 0x3810000
+#define MMNPU_APERTURE4_BASE 0x3B10000
+
+/* PCIe BAR Index for NPU6 */
+#define NPU6_REG_BAR_INDEX 0
+#define NPU6_MBOX_BAR_INDEX 0
+#define NPU6_PSP_BAR_INDEX 4
+#define NPU6_SMU_BAR_INDEX 5
+#define NPU6_SRAM_BAR_INDEX 2
+/* Associated BARs and Apertures */
+#define NPU6_REG_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU6_MBOX_BAR_BASE MMNPU_APERTURE0_BASE
+#define NPU6_PSP_BAR_BASE MMNPU_APERTURE3_BASE
+#define NPU6_SMU_BAR_BASE MMNPU_APERTURE4_BASE
+#define NPU6_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
+
+static const struct amdxdna_dev_priv npu6_dev_priv = {
+ .fw_path = "amdnpu/17f0_10/npu.sbin",
+ .protocol_major = 0x6,
+ .protocol_minor = 12,
+ .rt_config = npu4_default_rt_cfg,
+ .dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
+ .col_align = COL_ALIGN_NATURE,
+ .mbox_dev_addr = NPU6_MBOX_BAR_BASE,
+ .mbox_size = 0, /* Use BAR size */
+ .sram_dev_addr = NPU6_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
+ .sram_offs = {
+ DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
+ DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
+ },
+ .psp_regs_off = {
+ DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU6_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU6_REG, MPNPU_PUB_SCRATCH3),
+ DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU6_REG, MPNPU_PUB_SCRATCH4),
+ DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU6_REG, MPNPU_PUB_SCRATCH9),
+ DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU6_PSP, MP0_C2PMSG_73),
+ DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU6_PSP, MP0_C2PMSG_123),
+ DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU6_REG, MPNPU_PUB_SCRATCH3),
+ },
+ .smu_regs_off = {
+ DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU6_SMU, MP1_C2PMSG_0),
+ DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU6_SMU, MP1_C2PMSG_60),
+ DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU6_SMU, MMNPU_APERTURE4_BASE),
+ DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU6_SMU, MP1_C2PMSG_61),
+ DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU6_SMU, MP1_C2PMSG_60),
+ },
+ .hw_ops = {
+ .set_dpm = npu4_set_dpm,
+ },
+
+};
+
+const struct amdxdna_dev_info dev_npu6_info = {
+ .reg_bar = NPU6_REG_BAR_INDEX,
+ .mbox_bar = NPU6_MBOX_BAR_INDEX,
+ .sram_bar = NPU6_SRAM_BAR_INDEX,
+ .psp_bar = NPU6_PSP_BAR_INDEX,
+ .smu_bar = NPU6_SMU_BAR_INDEX,
+ .first_col = 0,
+ .dev_mem_buf_shift = 15, /* 32 KiB aligned */
+ .dev_mem_base = AIE2_DEVM_BASE,
+ .dev_mem_size = AIE2_DEVM_SIZE,
+ .vbnv = "RyzenAI-npu6",
+ .device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .dev_priv = &npu6_dev_priv,
+ .ops = &aie2_ops,
+};
diff --git a/drivers/accel/drm_accel.c b/drivers/accel/drm_accel.c
new file mode 100644
index 000000000000..ca3357acd127
--- /dev/null
+++ b/drivers/accel/drm_accel.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/xarray.h>
+
+#include <drm/drm_accel.h>
+#include <drm/drm_auth.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_print.h>
+
+DEFINE_XARRAY_ALLOC(accel_minors_xa);
+
+static const struct device_type accel_sysfs_device_minor = {
+ .name = "accel_minor"
+};
+
+static char *accel_devnode(const struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
+}
+
+static const struct class accel_class = {
+ .name = "accel",
+ .devnode = accel_devnode,
+};
+
+static int accel_sysfs_init(void)
+{
+ return class_register(&accel_class);
+}
+
+static void accel_sysfs_destroy(void)
+{
+ class_unregister(&accel_class);
+}
+
+static int accel_name_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_minor *minor = node->minor;
+ struct drm_device *dev = minor->dev;
+ struct drm_master *master;
+
+ mutex_lock(&dev->master_mutex);
+ master = dev->master;
+ seq_printf(m, "%s", dev->driver->name);
+ if (dev->dev)
+ seq_printf(m, " dev=%s", dev_name(dev->dev));
+ if (master && master->unique)
+ seq_printf(m, " master=%s", master->unique);
+ if (dev->unique)
+ seq_printf(m, " unique=%s", dev->unique);
+ seq_puts(m, "\n");
+ mutex_unlock(&dev->master_mutex);
+
+ return 0;
+}
+
+static const struct drm_info_list accel_debugfs_list[] = {
+ {"name", accel_name_info, 0}
+};
+#define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)
+
+/**
+ * accel_debugfs_register() - Register debugfs for device
+ * @dev: Pointer to the device instance.
+ *
+ * Creates common files for accelerators.
+ */
+void accel_debugfs_register(struct drm_device *dev)
+{
+ struct drm_minor *minor = dev->accel;
+
+ minor->debugfs_root = dev->debugfs_root;
+
+ drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
+ dev->debugfs_root, minor);
+}
+
+/**
+ * accel_set_device_instance_params() - Set some device parameters for accel device
+ * @kdev: Pointer to the device instance.
+ * @index: The minor's index
+ *
+ * This function creates the dev_t of the device using the accel major and
+ * the device's minor number. In addition, it sets the class and type of the
+ * device instance to the accel sysfs class and device type, respectively.
+ */
+void accel_set_device_instance_params(struct device *kdev, int index)
+{
+ kdev->devt = MKDEV(ACCEL_MAJOR, index);
+ kdev->class = &accel_class;
+ kdev->type = &accel_sysfs_device_minor;
+}
+
+/**
+ * accel_open - open method for ACCEL file
+ * @inode: device inode
+ * @filp: file pointer.
+ *
+ * This function must be used by drivers as their &file_operations.open method.
+ * It looks up the correct ACCEL device and instantiates all the per-file
+ * resources for it. It also calls the &drm_driver.open driver callback.
+ *
+ * Return: 0 on success or negative errno value on failure.
+ */
+int accel_open(struct inode *inode, struct file *filp)
+{
+ struct drm_device *dev;
+ struct drm_minor *minor;
+ int retcode;
+
+ minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
+ if (IS_ERR(minor))
+ return PTR_ERR(minor);
+
+ dev = minor->dev;
+
+ atomic_fetch_inc(&dev->open_count);
+
+ /* share address_space across all char-devs of a single device */
+ filp->f_mapping = dev->anon_inode->i_mapping;
+
+ retcode = drm_open_helper(filp, minor);
+ if (retcode)
+ goto err_undo;
+
+ return 0;
+
+err_undo:
+ atomic_dec(&dev->open_count);
+ drm_minor_release(minor);
+ return retcode;
+}
+EXPORT_SYMBOL_GPL(accel_open);
+
+static int accel_stub_open(struct inode *inode, struct file *filp)
+{
+ const struct file_operations *new_fops;
+ struct drm_minor *minor;
+ int err;
+
+ minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));
+ if (IS_ERR(minor))
+ return PTR_ERR(minor);
+
+ new_fops = fops_get(minor->dev->driver->fops);
+ if (!new_fops) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ replace_fops(filp, new_fops);
+ if (filp->f_op->open)
+ err = filp->f_op->open(inode, filp);
+ else
+ err = 0;
+
+out:
+ drm_minor_release(minor);
+
+ return err;
+}
+
+static const struct file_operations accel_stub_fops = {
+ .owner = THIS_MODULE,
+ .open = accel_stub_open,
+ .llseek = noop_llseek,
+};
+
+void accel_core_exit(void)
+{
+ unregister_chrdev(ACCEL_MAJOR, "accel");
+ accel_sysfs_destroy();
+ WARN_ON(!xa_empty(&accel_minors_xa));
+}
+
+int __init accel_core_init(void)
+{
+ int ret;
+
+ ret = accel_sysfs_init();
+ if (ret < 0) {
+ DRM_ERROR("Cannot create ACCEL class: %d\n", ret);
+ goto error;
+ }
+
+ ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);
+ if (ret < 0)
+ DRM_ERROR("Cannot register ACCEL major: %d\n", ret);
+
+error:
+ /*
+ * Any cleanup due to errors will be done in drm_core_exit() that
+ * will call accel_core_exit()
+ */
+ return ret;
+}
diff --git a/drivers/accel/ethosu/Kconfig b/drivers/accel/ethosu/Kconfig
new file mode 100644
index 000000000000..d25f9b3eb317
--- /dev/null
+++ b/drivers/accel/ethosu/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_ARM_ETHOSU
+ tristate "Arm Ethos-U65/U85 NPU"
+ depends on HAS_IOMEM
+ depends on DRM_ACCEL
+ select DRM_GEM_DMA_HELPER
+ select DRM_SCHED
+ select GENERIC_ALLOCATOR
+ help
+ Enables driver for Arm Ethos-U65/U85 NPUs
diff --git a/drivers/accel/ethosu/Makefile b/drivers/accel/ethosu/Makefile
new file mode 100644
index 000000000000..17db5a600416
--- /dev/null
+++ b/drivers/accel/ethosu/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o
+ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o
diff --git a/drivers/accel/ethosu/ethosu_device.h b/drivers/accel/ethosu/ethosu_device.h
new file mode 100644
index 000000000000..b189fa783d6a
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_device.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0-only or MIT */
+/* Copyright 2025 Arm, Ltd. */
+
+#ifndef __ETHOSU_DEVICE_H__
+#define __ETHOSU_DEVICE_H__
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+
+#include <drm/ethosu_accel.h>
+
+struct clk;
+struct gen_pool;
+
+#define NPU_REG_ID 0x0000
+#define NPU_REG_STATUS 0x0004
+#define NPU_REG_CMD 0x0008
+#define NPU_REG_RESET 0x000c
+#define NPU_REG_QBASE 0x0010
+#define NPU_REG_QBASE_HI 0x0014
+#define NPU_REG_QREAD 0x0018
+#define NPU_REG_QCONFIG 0x001c
+#define NPU_REG_QSIZE 0x0020
+#define NPU_REG_PROT 0x0024
+#define NPU_REG_CONFIG 0x0028
+#define NPU_REG_REGIONCFG 0x003c
+#define NPU_REG_AXILIMIT0 0x0040 // U65
+#define NPU_REG_AXILIMIT1 0x0044 // U65
+#define NPU_REG_AXILIMIT2 0x0048 // U65
+#define NPU_REG_AXILIMIT3 0x004c // U65
+#define NPU_REG_MEM_ATTR0 0x0040 // U85
+#define NPU_REG_MEM_ATTR1 0x0044 // U85
+#define NPU_REG_MEM_ATTR2 0x0048 // U85
+#define NPU_REG_MEM_ATTR3 0x004c // U85
+#define NPU_REG_AXI_SRAM 0x0050 // U85
+#define NPU_REG_AXI_EXT 0x0054 // U85
+
+#define NPU_REG_BASEP(x) (0x0080 + (x) * 8)
+#define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8)
+#define NPU_BASEP_REGION_MAX 8
+
+#define ID_ARCH_MAJOR_MASK GENMASK(31, 28)
+#define ID_ARCH_MINOR_MASK GENMASK(27, 20)
+#define ID_ARCH_PATCH_MASK GENMASK(19, 16)
+#define ID_VER_MAJOR_MASK GENMASK(11, 8)
+#define ID_VER_MINOR_MASK GENMASK(7, 4)
+
+#define CONFIG_MACS_PER_CC_MASK GENMASK(3, 0)
+#define CONFIG_CMD_STREAM_VER_MASK GENMASK(7, 4)
+
+#define STATUS_STATE_RUNNING BIT(0)
+#define STATUS_IRQ_RAISED BIT(1)
+#define STATUS_BUS_STATUS BIT(2)
+#define STATUS_RESET_STATUS BIT(3)
+#define STATUS_CMD_PARSE_ERR BIT(4)
+#define STATUS_CMD_END_REACHED BIT(5)
+
+#define CMD_CLEAR_IRQ BIT(1)
+#define CMD_TRANSITION_TO_RUN BIT(0)
+
+#define RESET_PENDING_CSL BIT(1)
+#define RESET_PENDING_CPL BIT(0)
+
+#define PROT_ACTIVE_CSL BIT(1)
+
+enum ethosu_cmds {
+ NPU_OP_CONV = 0x2,
+ NPU_OP_DEPTHWISE = 0x3,
+ NPU_OP_POOL = 0x5,
+ NPU_OP_ELEMENTWISE = 0x6,
+ NPU_OP_RESIZE = 0x7, // U85 only
+ NPU_OP_DMA_START = 0x10,
+ NPU_SET_IFM_PAD_TOP = 0x100,
+ NPU_SET_IFM_PAD_LEFT = 0x101,
+ NPU_SET_IFM_PAD_RIGHT = 0x102,
+ NPU_SET_IFM_PAD_BOTTOM = 0x103,
+ NPU_SET_IFM_DEPTH_M1 = 0x104,
+ NPU_SET_IFM_PRECISION = 0x105,
+ NPU_SET_IFM_BROADCAST = 0x108,
+ NPU_SET_IFM_WIDTH0_M1 = 0x10a,
+ NPU_SET_IFM_HEIGHT0_M1 = 0x10b,
+ NPU_SET_IFM_HEIGHT1_M1 = 0x10c,
+ NPU_SET_IFM_REGION = 0x10f,
+ NPU_SET_OFM_WIDTH_M1 = 0x111,
+ NPU_SET_OFM_HEIGHT_M1 = 0x112,
+ NPU_SET_OFM_DEPTH_M1 = 0x113,
+ NPU_SET_OFM_PRECISION = 0x114,
+ NPU_SET_OFM_WIDTH0_M1 = 0x11a,
+ NPU_SET_OFM_HEIGHT0_M1 = 0x11b,
+ NPU_SET_OFM_HEIGHT1_M1 = 0x11c,
+ NPU_SET_OFM_REGION = 0x11f,
+ NPU_SET_KERNEL_WIDTH_M1 = 0x120,
+ NPU_SET_KERNEL_HEIGHT_M1 = 0x121,
+ NPU_SET_KERNEL_STRIDE = 0x122,
+ NPU_SET_WEIGHT_REGION = 0x128,
+ NPU_SET_SCALE_REGION = 0x129,
+ NPU_SET_DMA0_SRC_REGION = 0x130,
+ NPU_SET_DMA0_DST_REGION = 0x131,
+ NPU_SET_DMA0_SIZE0 = 0x132,
+ NPU_SET_DMA0_SIZE1 = 0x133,
+ NPU_SET_IFM2_BROADCAST = 0x180,
+ NPU_SET_IFM2_PRECISION = 0x185,
+ NPU_SET_IFM2_WIDTH0_M1 = 0x18a,
+ NPU_SET_IFM2_HEIGHT0_M1 = 0x18b,
+ NPU_SET_IFM2_HEIGHT1_M1 = 0x18c,
+ NPU_SET_IFM2_REGION = 0x18f,
+ NPU_SET_IFM_BASE0 = 0x4000,
+ NPU_SET_IFM_BASE1 = 0x4001,
+ NPU_SET_IFM_BASE2 = 0x4002,
+ NPU_SET_IFM_BASE3 = 0x4003,
+ NPU_SET_IFM_STRIDE_X = 0x4004,
+ NPU_SET_IFM_STRIDE_Y = 0x4005,
+ NPU_SET_IFM_STRIDE_C = 0x4006,
+ NPU_SET_OFM_BASE0 = 0x4010,
+ NPU_SET_OFM_BASE1 = 0x4011,
+ NPU_SET_OFM_BASE2 = 0x4012,
+ NPU_SET_OFM_BASE3 = 0x4013,
+ NPU_SET_OFM_STRIDE_X = 0x4014,
+ NPU_SET_OFM_STRIDE_Y = 0x4015,
+ NPU_SET_OFM_STRIDE_C = 0x4016,
+ NPU_SET_WEIGHT_BASE = 0x4020,
+ NPU_SET_WEIGHT_LENGTH = 0x4021,
+ NPU_SET_SCALE_BASE = 0x4022,
+ NPU_SET_SCALE_LENGTH = 0x4023,
+ NPU_SET_DMA0_SRC = 0x4030,
+ NPU_SET_DMA0_DST = 0x4031,
+ NPU_SET_DMA0_LEN = 0x4032,
+ NPU_SET_DMA0_SRC_STRIDE0 = 0x4033,
+ NPU_SET_DMA0_SRC_STRIDE1 = 0x4034,
+ NPU_SET_DMA0_DST_STRIDE0 = 0x4035,
+ NPU_SET_DMA0_DST_STRIDE1 = 0x4036,
+ NPU_SET_IFM2_BASE0 = 0x4080,
+ NPU_SET_IFM2_BASE1 = 0x4081,
+ NPU_SET_IFM2_BASE2 = 0x4082,
+ NPU_SET_IFM2_BASE3 = 0x4083,
+ NPU_SET_IFM2_STRIDE_X = 0x4084,
+ NPU_SET_IFM2_STRIDE_Y = 0x4085,
+ NPU_SET_IFM2_STRIDE_C = 0x4086,
+ NPU_SET_WEIGHT1_BASE = 0x4090,
+ NPU_SET_WEIGHT1_LENGTH = 0x4091,
+ NPU_SET_SCALE1_BASE = 0x4092,
+ NPU_SET_WEIGHT2_BASE = 0x4092,
+ NPU_SET_SCALE1_LENGTH = 0x4093,
+ NPU_SET_WEIGHT2_LENGTH = 0x4093,
+ NPU_SET_WEIGHT3_BASE = 0x4094,
+ NPU_SET_WEIGHT3_LENGTH = 0x4095,
+};
+
+#define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */
+
+/**
+ * struct ethosu_device - Ethosu device
+ */
+struct ethosu_device {
+ /** @base: Base drm_device. */
+ struct drm_device base;
+
+ /** @iomem: CPU mapping of the registers. */
+ void __iomem *regs;
+
+ void __iomem *sram;
+ struct gen_pool *srampool;
+ dma_addr_t sramphys;
+
+ struct clk_bulk_data *clks;
+ int num_clks;
+ int irq;
+
+ struct drm_ethosu_npu_info npu_info;
+
+ struct ethosu_job *in_flight_job;
+ /* For in_flight_job and ethosu_job_hw_submit() */
+ struct mutex job_lock;
+
+ /* For dma_fence */
+ spinlock_t fence_lock;
+
+ struct drm_gpu_scheduler sched;
+ /* For ethosu_job_do_push() */
+ struct mutex sched_lock;
+ u64 fence_context;
+ u64 emit_seqno;
+};
+
+#define to_ethosu_device(drm_dev) \
+ ((struct ethosu_device *)container_of(drm_dev, struct ethosu_device, base))
+
+static inline bool ethosu_is_u65(const struct ethosu_device *ethosudev)
+{
+ return FIELD_GET(ID_ARCH_MAJOR_MASK, ethosudev->npu_info.id) == 1;
+}
+
+#endif
diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c
new file mode 100644
index 000000000000..e05a69bf5574
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_drv.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only or MIT
+// Copyright (C) 2025 Arm, Ltd.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_utils.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_accel.h>
+#include <drm/ethosu_accel.h>
+
+#include "ethosu_drv.h"
+#include "ethosu_device.h"
+#include "ethosu_gem.h"
+#include "ethosu_job.h"
+
+static int ethosu_ioctl_dev_query(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct ethosu_device *ethosudev = to_ethosu_device(ddev);
+ struct drm_ethosu_dev_query *args = data;
+
+ if (!args->pointer) {
+ switch (args->type) {
+ case DRM_ETHOSU_DEV_QUERY_NPU_INFO:
+ args->size = sizeof(ethosudev->npu_info);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ switch (args->type) {
+ case DRM_ETHOSU_DEV_QUERY_NPU_INFO:
+ if (args->size < offsetofend(struct drm_ethosu_npu_info, sram_size))
+ return -EINVAL;
+ return copy_struct_to_user(u64_to_user_ptr(args->pointer),
+ args->size,
+ &ethosudev->npu_info,
+ sizeof(ethosudev->npu_info), NULL);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define ETHOSU_BO_FLAGS DRM_ETHOSU_BO_NO_MMAP
+
+static int ethosu_ioctl_bo_create(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_bo_create *args = data;
+ int cookie, ret;
+
+ if (!drm_dev_enter(ddev, &cookie))
+ return -ENODEV;
+
+ if (!args->size || (args->flags & ~ETHOSU_BO_FLAGS)) {
+ ret = -EINVAL;
+ goto out_dev_exit;
+ }
+
+ ret = ethosu_gem_create_with_handle(file, ddev, &args->size,
+ args->flags, &args->handle);
+
+out_dev_exit:
+ drm_dev_exit(cookie);
+ return ret;
+}
+
+static int ethosu_ioctl_bo_wait(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_bo_wait *args = data;
+ int cookie, ret;
+ unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
+
+ if (args->pad)
+ return -EINVAL;
+
+ if (!drm_dev_enter(ddev, &cookie))
+ return -ENODEV;
+
+ ret = drm_gem_dma_resv_wait(file, args->handle, true, timeout);
+
+ drm_dev_exit(cookie);
+ return ret;
+}
+
+static int ethosu_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_bo_mmap_offset *args = data;
+ struct drm_gem_object *obj;
+
+ if (args->pad)
+ return -EINVAL;
+
+ obj = drm_gem_object_lookup(file, args->handle);
+ if (!obj)
+ return -ENOENT;
+
+ args->offset = drm_vma_node_offset_addr(&obj->vma_node);
+ drm_gem_object_put(obj);
+ return 0;
+}
+
+static int ethosu_ioctl_cmdstream_bo_create(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_cmdstream_bo_create *args = data;
+ int cookie, ret;
+
+ if (!drm_dev_enter(ddev, &cookie))
+ return -ENODEV;
+
+ if (!args->size || !args->data || args->pad || args->flags) {
+ ret = -EINVAL;
+ goto out_dev_exit;
+ }
+
+ args->flags |= DRM_ETHOSU_BO_NO_MMAP;
+
+ ret = ethosu_gem_cmdstream_create(file, ddev, args->size, args->data,
+ args->flags, &args->handle);
+
+out_dev_exit:
+ drm_dev_exit(cookie);
+ return ret;
+}
+
+static int ethosu_open(struct drm_device *ddev, struct drm_file *file)
+{
+ int ret = 0;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EINVAL;
+
+ struct ethosu_file_priv __free(kfree) *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err_put_mod;
+ }
+ priv->edev = to_ethosu_device(ddev);
+
+ ret = ethosu_job_open(priv);
+ if (ret)
+ goto err_put_mod;
+
+ file->driver_priv = no_free_ptr(priv);
+ return 0;
+
+err_put_mod:
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file)
+{
+ ethosu_job_close(file->driver_priv);
+ kfree(file->driver_priv);
+ module_put(THIS_MODULE);
+}
+
+static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = {
+#define ETHOSU_IOCTL(n, func, flags) \
+ DRM_IOCTL_DEF_DRV(ETHOSU_##n, ethosu_ioctl_##func, flags)
+
+ ETHOSU_IOCTL(DEV_QUERY, dev_query, 0),
+ ETHOSU_IOCTL(BO_CREATE, bo_create, 0),
+ ETHOSU_IOCTL(BO_WAIT, bo_wait, 0),
+ ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0),
+ ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0),
+ ETHOSU_IOCTL(SUBMIT, submit, 0),
+};
+
+DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops);
+
+/*
+ * Ethosu driver version:
+ * - 1.0 - initial interface
+ */
+static const struct drm_driver ethosu_drm_driver = {
+ .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
+ .open = ethosu_open,
+ .postclose = ethosu_postclose,
+ .ioctls = ethosu_drm_driver_ioctls,
+ .num_ioctls = ARRAY_SIZE(ethosu_drm_driver_ioctls),
+ .fops = &ethosu_drm_driver_fops,
+ .name = "ethosu",
+ .desc = "Arm Ethos-U Accel driver",
+ .major = 1,
+ .minor = 0,
+
+ .gem_create_object = ethosu_gem_create_object,
+};
+
+#define U65_DRAM_AXI_LIMIT_CFG 0x1f3f0002
+#define U65_SRAM_AXI_LIMIT_CFG 0x1f3f00b0
+#define U85_AXI_EXT_CFG 0x00021f3f
+#define U85_AXI_SRAM_CFG 0x00021f3f
+#define U85_MEM_ATTR0_CFG 0x00000000
+#define U85_MEM_ATTR2_CFG 0x000000b7
+
+static int ethosu_reset(struct ethosu_device *ethosudev)
+{
+ int ret;
+ u32 reg;
+
+ writel_relaxed(RESET_PENDING_CSL, ethosudev->regs + NPU_REG_RESET);
+ ret = readl_poll_timeout(ethosudev->regs + NPU_REG_STATUS, reg,
+ !FIELD_GET(STATUS_RESET_STATUS, reg),
+ USEC_PER_MSEC, USEC_PER_SEC);
+ if (ret)
+ return ret;
+
+ if (!FIELD_GET(PROT_ACTIVE_CSL, readl_relaxed(ethosudev->regs + NPU_REG_PROT))) {
+ dev_warn(ethosudev->base.dev, "Could not reset to non-secure mode (PROT = %x)\n",
+ readl_relaxed(ethosudev->regs + NPU_REG_PROT));
+ }
+
+ /*
+ * Assign region 2 (SRAM) to AXI M0 (AXILIMIT0),
+ * everything else to AXI M1 (AXILIMIT2)
+ */
+ writel_relaxed(0x0000aa8a, ethosudev->regs + NPU_REG_REGIONCFG);
+ if (ethosu_is_u65(ethosudev)) {
+ writel_relaxed(U65_SRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT0);
+ writel_relaxed(U65_DRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT2);
+ } else {
+ writel_relaxed(U85_AXI_SRAM_CFG, ethosudev->regs + NPU_REG_AXI_SRAM);
+ writel_relaxed(U85_AXI_EXT_CFG, ethosudev->regs + NPU_REG_AXI_EXT);
+ writel_relaxed(U85_MEM_ATTR0_CFG, ethosudev->regs + NPU_REG_MEM_ATTR0); // SRAM
+ writel_relaxed(U85_MEM_ATTR2_CFG, ethosudev->regs + NPU_REG_MEM_ATTR2); // DRAM
+ }
+
+ if (ethosudev->sram)
+ memset_io(ethosudev->sram, 0, ethosudev->npu_info.sram_size);
+
+ return 0;
+}
+
+static int ethosu_device_resume(struct device *dev)
+{
+ struct ethosu_device *ethosudev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(ethosudev->num_clks, ethosudev->clks);
+ if (ret)
+ return ret;
+
+ ret = ethosu_reset(ethosudev);
+ if (!ret)
+ return 0;
+
+ clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks);
+ return ret;
+}
+
+static int ethosu_device_suspend(struct device *dev)
+{
+ struct ethosu_device *ethosudev = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks);
+ return 0;
+}
+
+static int ethosu_sram_init(struct ethosu_device *ethosudev)
+{
+ ethosudev->npu_info.sram_size = 0;
+
+ ethosudev->srampool = of_gen_pool_get(ethosudev->base.dev->of_node, "sram", 0);
+ if (!ethosudev->srampool)
+ return 0;
+
+ ethosudev->npu_info.sram_size = gen_pool_size(ethosudev->srampool);
+
+ ethosudev->sram = (void __iomem *)gen_pool_dma_alloc(ethosudev->srampool,
+ ethosudev->npu_info.sram_size,
+ &ethosudev->sramphys);
+ if (!ethosudev->sram) {
+ dev_err(ethosudev->base.dev, "failed to allocate from SRAM pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int ethosu_init(struct ethosu_device *ethosudev)
+{
+ int ret;
+ u32 id, config;
+
+ ret = ethosu_device_resume(ethosudev->base.dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);
+ pm_runtime_use_autosuspend(ethosudev->base.dev);
+ ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev);
+ if (ret)
+ return ret;
+ pm_runtime_get_noresume(ethosudev->base.dev);
+
+ ethosudev->npu_info.id = id = readl_relaxed(ethosudev->regs + NPU_REG_ID);
+ ethosudev->npu_info.config = config = readl_relaxed(ethosudev->regs + NPU_REG_CONFIG);
+
+ ethosu_sram_init(ethosudev);
+
+ dev_info(ethosudev->base.dev,
+ "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n",
+ FIELD_GET(ID_ARCH_MAJOR_MASK, id),
+ FIELD_GET(ID_ARCH_MINOR_MASK, id),
+ FIELD_GET(ID_ARCH_PATCH_MASK, id),
+ FIELD_GET(ID_VER_MAJOR_MASK, id),
+ FIELD_GET(ID_VER_MINOR_MASK, id),
+ FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config),
+ 1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config),
+ ethosudev->npu_info.sram_size / 1024);
+
+ return 0;
+}
+
+static int ethosu_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ethosu_device *ethosudev;
+
+ ethosudev = devm_drm_dev_alloc(&pdev->dev, &ethosu_drm_driver,
+ struct ethosu_device, base);
+ if (IS_ERR(ethosudev))
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ethosudev);
+
+ dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
+
+ ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
+
+ ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &ethosudev->clks);
+ if (ethosudev->num_clks < 0)
+ return ethosudev->num_clks;
+
+ ret = ethosu_job_init(ethosudev);
+ if (ret)
+ return ret;
+
+ ret = ethosu_init(ethosudev);
+ if (ret)
+ return ret;
+
+ ret = drm_dev_register(&ethosudev->base, 0);
+ if (ret)
+ pm_runtime_dont_use_autosuspend(ethosudev->base.dev);
+
+ pm_runtime_put_autosuspend(ethosudev->base.dev);
+ return ret;
+}
+
+static void ethosu_remove(struct platform_device *pdev)
+{
+ struct ethosu_device *ethosudev = dev_get_drvdata(&pdev->dev);
+
+ drm_dev_unregister(&ethosudev->base);
+ ethosu_job_fini(ethosudev);
+ if (ethosudev->sram)
+ gen_pool_free(ethosudev->srampool, (unsigned long)ethosudev->sram,
+ ethosudev->npu_info.sram_size);
+}
+
+static const struct of_device_id dt_match[] = {
+ { .compatible = "arm,ethos-u65" },
+ { .compatible = "arm,ethos-u85" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ethosu_pm_ops,
+ ethosu_device_suspend,
+ ethosu_device_resume,
+ NULL);
+
+static struct platform_driver ethosu_driver = {
+ .probe = ethosu_probe,
+ .remove = ethosu_remove,
+ .driver = {
+ .name = "ethosu",
+ .pm = pm_ptr(&ethosu_pm_ops),
+ .of_match_table = dt_match,
+ },
+};
+module_platform_driver(ethosu_driver);
+
+MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
+MODULE_DESCRIPTION("Arm Ethos-U Accel Driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/accel/ethosu/ethosu_drv.h b/drivers/accel/ethosu/ethosu_drv.h
new file mode 100644
index 000000000000..9e21dfe94184
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_drv.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/* Copyright 2025 Arm, Ltd. */
+#ifndef __ETHOSU_DRV_H__
+#define __ETHOSU_DRV_H__
+
+#include <drm/gpu_scheduler.h>
+
+struct ethosu_device;
+
+struct ethosu_file_priv {
+ struct ethosu_device *edev;
+ struct drm_sched_entity sched_entity;
+};
+
+#endif
diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c
new file mode 100644
index 000000000000..473b5f5d7514
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_gem.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-only or MIT
+/* Copyright 2025 Arm, Ltd. */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <drm/ethosu_accel.h>
+
+#include "ethosu_device.h"
+#include "ethosu_gem.h"
+
+static void ethosu_gem_free_object(struct drm_gem_object *obj)
+{
+ struct ethosu_gem_object *bo = to_ethosu_bo(obj);
+
+ kfree(bo->info);
+ drm_gem_free_mmap_offset(&bo->base.base);
+ drm_gem_dma_free(&bo->base);
+}
+
+static int ethosu_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct ethosu_gem_object *bo = to_ethosu_bo(obj);
+
+ /* Don't allow mmap on objects that have the NO_MMAP flag set. */
+ if (bo->flags & DRM_ETHOSU_BO_NO_MMAP)
+ return -EINVAL;
+
+ return drm_gem_dma_object_mmap(obj, vma);
+}
+
+static const struct drm_gem_object_funcs ethosu_gem_funcs = {
+ .free = ethosu_gem_free_object,
+ .print_info = drm_gem_dma_object_print_info,
+ .get_sg_table = drm_gem_dma_object_get_sg_table,
+ .vmap = drm_gem_dma_object_vmap,
+ .mmap = ethosu_gem_mmap,
+ .vm_ops = &drm_gem_dma_vm_ops,
+};
+
+/**
+ * ethosu_gem_create_object - Implementation of driver->gem_create_object.
+ * @ddev: DRM device
+ * @size: Size in bytes of the memory the object will reference
+ *
+ * This lets the GEM helpers allocate object structs for us, and keep
+ * our BO stats correct.
+ */
+struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev, size_t size)
+{
+ struct ethosu_gem_object *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj->base.base.funcs = &ethosu_gem_funcs;
+ return &obj->base.base;
+}
+
+/**
+ * ethosu_gem_create_with_handle() - Create a GEM object and attach it to a handle.
+ * @file: DRM file.
+ * @ddev: DRM device.
+ * @size: Size of the GEM object to allocate.
+ * @flags: Combination of drm_ethosu_bo_flags flags.
+ * @handle: Pointer holding the handle pointing to the new GEM object.
+ *
+ * Return: Zero on success
+ */
+int ethosu_gem_create_with_handle(struct drm_file *file,
+ struct drm_device *ddev,
+ u64 *size, u32 flags, u32 *handle)
+{
+ struct drm_gem_dma_object *mem;
+ struct ethosu_gem_object *bo;
+ int ret;
+
+ mem = drm_gem_dma_create(ddev, *size);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ bo = to_ethosu_bo(&mem->base);
+ bo->flags = flags;
+
+ /*
+ * Allocate an id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file, &mem->base, handle);
+ if (!ret)
+ *size = bo->base.base.size;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_put(&mem->base);
+
+ return ret;
+}
+
+struct dma {
+ s8 region;
+ u64 len;
+ u64 offset;
+ s64 stride[2];
+};
+
+struct dma_state {
+ u16 size0;
+ u16 size1;
+ s8 mode;
+ struct dma src;
+ struct dma dst;
+};
+
+struct buffer {
+ u64 base;
+ u32 length;
+ s8 region;
+};
+
+struct feat_matrix {
+ u64 base[4];
+ s64 stride_x;
+ s64 stride_y;
+ s64 stride_c;
+ s8 region;
+ u8 broadcast;
+ u16 stride_kernel;
+ u16 precision;
+ u16 depth;
+ u16 width;
+ u16 width0;
+ u16 height[3];
+ u8 pad_top;
+ u8 pad_left;
+ u8 pad_bottom;
+ u8 pad_right;
+};
+
+struct cmd_state {
+ struct dma_state dma;
+ struct buffer scale[2];
+ struct buffer weight[4];
+ struct feat_matrix ofm;
+ struct feat_matrix ifm;
+ struct feat_matrix ifm2;
+};
+
+static void cmd_state_init(struct cmd_state *st)
+{
+ /* Initialize to all 1s to detect missing setup */
+ memset(st, 0xff, sizeof(*st));
+}
+
+static u64 cmd_to_addr(u32 *cmd)
+{
+ return ((u64)((cmd[0] & 0xff0000) << 16)) | cmd[1];
+}
+
+static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
+ struct dma_state *dma_st, struct dma *dma)
+{
+ s8 mode = dma_st->mode;
+ u64 len = dma->len;
+
+ if (mode >= 1) {
+ len += dma->stride[0];
+ len *= dma_st->size0;
+ }
+ if (mode == 2) {
+ len += dma->stride[1];
+ len *= dma_st->size1;
+ }
+ if (dma->region >= 0)
+ info->region_size[dma->region] = max(info->region_size[dma->region],
+ len + dma->offset);
+
+ return len;
+}
+
+static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
+ struct feat_matrix *fm,
+ u32 x, u32 y, u32 c)
+{
+ u32 element_size, storage = fm->precision >> 14;
+ int tile = 0;
+ u64 addr;
+
+ if (fm->region < 0)
+ return U64_MAX;
+
+ switch (storage) {
+ case 0:
+ if (x >= fm->width0 + 1) {
+ x -= fm->width0 + 1;
+ tile += 1;
+ }
+ if (y >= fm->height[tile] + 1) {
+ y -= fm->height[tile] + 1;
+ tile += 2;
+ }
+ break;
+ case 1:
+ if (y >= fm->height[1] + 1) {
+ y -= fm->height[1] + 1;
+ tile = 2;
+ } else if (y >= fm->height[0] + 1) {
+ y -= fm->height[0] + 1;
+ tile = 1;
+ }
+ break;
+ }
+ if (fm->base[tile] == U64_MAX)
+ return U64_MAX;
+
+ addr = fm->base[tile] + y * fm->stride_y;
+
+ switch ((fm->precision >> 6) & 0x3) { // format
+ case 0: //nhwc:
+ addr += x * fm->stride_x + c;
+ break;
+ case 1: //nhcwb16:
+ element_size = BIT((fm->precision >> 1) & 0x3);
+
+ addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
+ break;
+ }
+
+ info->region_size[fm->region] = max(info->region_size[fm->region], addr + 1);
+
+ return addr;
+}
+
+static int calc_sizes(struct drm_device *ddev,
+ struct ethosu_validated_cmdstream_info *info,
+ u16 op, struct cmd_state *st,
+ bool ifm, bool ifm2, bool weight, bool scale)
+{
+ u64 len;
+
+ if (ifm) {
+ if (st->ifm.stride_kernel == U16_MAX)
+ return -EINVAL;
+ u32 stride_y = ((st->ifm.stride_kernel >> 8) & 0x2) +
+ ((st->ifm.stride_kernel >> 1) & 0x1) + 1;
+ u32 stride_x = ((st->ifm.stride_kernel >> 5) & 0x2) +
+ (st->ifm.stride_kernel & 0x1) + 1;
+ u32 ifm_height = st->ofm.height[2] * stride_y +
+ st->ifm.height[2] - (st->ifm.pad_top + st->ifm.pad_bottom);
+ u32 ifm_width = st->ofm.width * stride_x +
+ st->ifm.width - (st->ifm.pad_left + st->ifm.pad_right);
+
+ len = feat_matrix_length(info, &st->ifm, ifm_width,
+ ifm_height, st->ifm.depth);
+ dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
+ op, st->ifm.region, st->ifm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ if (ifm2) {
+ len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
+ 0, st->ofm.depth);
+ dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
+ op, st->ifm2.region, st->ifm2.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ if (weight) {
+ dev_dbg(ddev->dev, "op %d: W:%d:0x%llx-0x%llx\n",
+ op, st->weight[0].region, st->weight[0].base,
+ st->weight[0].base + st->weight[0].length - 1);
+ if (st->weight[0].region < 0 || st->weight[0].base == U64_MAX ||
+ st->weight[0].length == U32_MAX)
+ return -EINVAL;
+ info->region_size[st->weight[0].region] =
+ max(info->region_size[st->weight[0].region],
+ st->weight[0].base + st->weight[0].length);
+ }
+
+ if (scale) {
+ dev_dbg(ddev->dev, "op %d: S:%d:0x%llx-0x%llx\n",
+ op, st->scale[0].region, st->scale[0].base,
+ st->scale[0].base + st->scale[0].length - 1);
+ if (st->scale[0].region < 0 || st->scale[0].base == U64_MAX ||
+ st->scale[0].length == U32_MAX)
+ return -EINVAL;
+ info->region_size[st->scale[0].region] =
+ max(info->region_size[st->scale[0].region],
+ st->scale[0].base + st->scale[0].length);
+ }
+
+ len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+ st->ofm.height[2], st->ofm.depth);
+ dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
+ op, st->ofm.region, st->ofm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ info->output_region[st->ofm.region] = true;
+
+ return 0;
+}
+
+static int calc_sizes_elemwise(struct drm_device *ddev,
+ struct ethosu_validated_cmdstream_info *info,
+ u16 op, struct cmd_state *st,
+ bool ifm, bool ifm2)
+{
+ u32 height, width, depth;
+ u64 len;
+
+ if (ifm) {
+ height = st->ifm.broadcast & 0x1 ? 0 : st->ofm.height[2];
+ width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
+ depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
+
+ len = feat_matrix_length(info, &st->ifm, width,
+ height, depth);
+ dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
+ op, st->ifm.region, st->ifm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ if (ifm2) {
+ height = st->ifm2.broadcast & 0x1 ? 0 : st->ofm.height[2];
+ width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
+ depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
+
+ len = feat_matrix_length(info, &st->ifm2, width,
+ height, depth);
+ dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
+ op, st->ifm2.region, st->ifm2.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+ st->ofm.height[2], st->ofm.depth);
+ dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
+ op, st->ofm.region, st->ofm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ info->output_region[st->ofm.region] = true;
+
+ return 0;
+}
+
+static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
+ u32 __user *ucmds,
+ struct ethosu_gem_object *bo,
+ u32 size)
+{
+ struct ethosu_validated_cmdstream_info __free(kfree) *info = kzalloc(sizeof(*info), GFP_KERNEL);
+ struct ethosu_device *edev = to_ethosu_device(ddev);
+ u32 *bocmds = bo->base.vaddr;
+ struct cmd_state st;
+ int i, ret;
+
+ if (!info)
+ return -ENOMEM;
+ info->cmd_size = size;
+
+ cmd_state_init(&st);
+
+ for (i = 0; i < size / 4; i++) {
+ bool use_ifm, use_ifm2, use_scale;
+ u64 dstlen, srclen;
+ u16 cmd, param;
+ u32 cmds[2];
+ u64 addr;
+
+ if (get_user(cmds[0], ucmds++))
+ return -EFAULT;
+
+ bocmds[i] = cmds[0];
+
+ cmd = cmds[0];
+ param = cmds[0] >> 16;
+
+ if (cmd & 0x4000) {
+ if (get_user(cmds[1], ucmds++))
+ return -EFAULT;
+
+ i++;
+ bocmds[i] = cmds[1];
+ addr = cmd_to_addr(cmds);
+ }
+
+ switch (cmd) {
+ case NPU_OP_DMA_START:
+ srclen = dma_length(info, &st.dma, &st.dma.src);
+ dstlen = dma_length(info, &st.dma, &st.dma.dst);
+
+ if (st.dma.dst.region >= 0)
+ info->output_region[st.dma.dst.region] = true;
+ dev_dbg(ddev->dev, "cmd: DMA SRC:%d:0x%llx+0x%llx DST:%d:0x%llx+0x%llx\n",
+ st.dma.src.region, st.dma.src.offset, srclen,
+ st.dma.dst.region, st.dma.dst.offset, dstlen);
+ break;
+ case NPU_OP_CONV:
+ case NPU_OP_DEPTHWISE:
+ use_ifm2 = param & 0x1; // weights_ifm2
+ use_scale = !(st.ofm.precision & 0x100);
+ ret = calc_sizes(ddev, info, cmd, &st, true, use_ifm2,
+ !use_ifm2, use_scale);
+ if (ret)
+ return ret;
+ break;
+ case NPU_OP_POOL:
+ use_ifm = param != 0x4; // pooling mode
+ use_scale = !(st.ofm.precision & 0x100);
+ ret = calc_sizes(ddev, info, cmd, &st, use_ifm, false,
+ false, use_scale);
+ if (ret)
+ return ret;
+ break;
+ case NPU_OP_ELEMENTWISE:
+ use_ifm2 = !((st.ifm2.broadcast == 8) || (param == 5) ||
+ (param == 6) || (param == 7) || (param == 0x24));
+ use_ifm = st.ifm.broadcast != 8;
+ ret = calc_sizes_elemwise(ddev, info, cmd, &st, use_ifm, use_ifm2);
+ if (ret)
+ return ret;
+ break;
+ case NPU_OP_RESIZE: // U85 only
+ WARN_ON(1); // TODO
+ break;
+ case NPU_SET_KERNEL_WIDTH_M1:
+ st.ifm.width = param;
+ break;
+ case NPU_SET_KERNEL_HEIGHT_M1:
+ st.ifm.height[2] = param;
+ break;
+ case NPU_SET_KERNEL_STRIDE:
+ st.ifm.stride_kernel = param;
+ break;
+ case NPU_SET_IFM_PAD_TOP:
+ st.ifm.pad_top = param & 0x7f;
+ break;
+ case NPU_SET_IFM_PAD_LEFT:
+ st.ifm.pad_left = param & 0x7f;
+ break;
+ case NPU_SET_IFM_PAD_RIGHT:
+ st.ifm.pad_right = param & 0xff;
+ break;
+ case NPU_SET_IFM_PAD_BOTTOM:
+ st.ifm.pad_bottom = param & 0xff;
+ break;
+ case NPU_SET_IFM_DEPTH_M1:
+ st.ifm.depth = param;
+ break;
+ case NPU_SET_IFM_PRECISION:
+ st.ifm.precision = param;
+ break;
+ case NPU_SET_IFM_BROADCAST:
+ st.ifm.broadcast = param;
+ break;
+ case NPU_SET_IFM_REGION:
+ st.ifm.region = param & 0x7f;
+ break;
+ case NPU_SET_IFM_WIDTH0_M1:
+ st.ifm.width0 = param;
+ break;
+ case NPU_SET_IFM_HEIGHT0_M1:
+ st.ifm.height[0] = param;
+ break;
+ case NPU_SET_IFM_HEIGHT1_M1:
+ st.ifm.height[1] = param;
+ break;
+ case NPU_SET_IFM_BASE0:
+ case NPU_SET_IFM_BASE1:
+ case NPU_SET_IFM_BASE2:
+ case NPU_SET_IFM_BASE3:
+ st.ifm.base[cmd & 0x3] = addr;
+ break;
+ case NPU_SET_IFM_STRIDE_X:
+ st.ifm.stride_x = addr;
+ break;
+ case NPU_SET_IFM_STRIDE_Y:
+ st.ifm.stride_y = addr;
+ break;
+ case NPU_SET_IFM_STRIDE_C:
+ st.ifm.stride_c = addr;
+ break;
+
+ case NPU_SET_OFM_WIDTH_M1:
+ st.ofm.width = param;
+ break;
+ case NPU_SET_OFM_HEIGHT_M1:
+ st.ofm.height[2] = param;
+ break;
+ case NPU_SET_OFM_DEPTH_M1:
+ st.ofm.depth = param;
+ break;
+ case NPU_SET_OFM_PRECISION:
+ st.ofm.precision = param;
+ break;
+ case NPU_SET_OFM_REGION:
+ st.ofm.region = param & 0x7;
+ break;
+ case NPU_SET_OFM_WIDTH0_M1:
+ st.ofm.width0 = param;
+ break;
+ case NPU_SET_OFM_HEIGHT0_M1:
+ st.ofm.height[0] = param;
+ break;
+ case NPU_SET_OFM_HEIGHT1_M1:
+ st.ofm.height[1] = param;
+ break;
+ case NPU_SET_OFM_BASE0:
+ case NPU_SET_OFM_BASE1:
+ case NPU_SET_OFM_BASE2:
+ case NPU_SET_OFM_BASE3:
+ st.ofm.base[cmd & 0x3] = addr;
+ break;
+ case NPU_SET_OFM_STRIDE_X:
+ st.ofm.stride_x = addr;
+ break;
+ case NPU_SET_OFM_STRIDE_Y:
+ st.ofm.stride_y = addr;
+ break;
+ case NPU_SET_OFM_STRIDE_C:
+ st.ofm.stride_c = addr;
+ break;
+
+ case NPU_SET_IFM2_BROADCAST:
+ st.ifm2.broadcast = param;
+ break;
+ case NPU_SET_IFM2_PRECISION:
+ st.ifm2.precision = param;
+ break;
+ case NPU_SET_IFM2_REGION:
+ st.ifm2.region = param & 0x7;
+ break;
+ case NPU_SET_IFM2_WIDTH0_M1:
+ st.ifm2.width0 = param;
+ break;
+ case NPU_SET_IFM2_HEIGHT0_M1:
+ st.ifm2.height[0] = param;
+ break;
+ case NPU_SET_IFM2_HEIGHT1_M1:
+ st.ifm2.height[1] = param;
+ break;
+ case NPU_SET_IFM2_BASE0:
+ case NPU_SET_IFM2_BASE1:
+ case NPU_SET_IFM2_BASE2:
+ case NPU_SET_IFM2_BASE3:
+ st.ifm2.base[cmd & 0x3] = addr;
+ break;
+ case NPU_SET_IFM2_STRIDE_X:
+ st.ifm2.stride_x = addr;
+ break;
+ case NPU_SET_IFM2_STRIDE_Y:
+ st.ifm2.stride_y = addr;
+ break;
+ case NPU_SET_IFM2_STRIDE_C:
+ st.ifm2.stride_c = addr;
+ break;
+
+ case NPU_SET_WEIGHT_REGION:
+ st.weight[0].region = param & 0x7;
+ break;
+ case NPU_SET_SCALE_REGION:
+ st.scale[0].region = param & 0x7;
+ break;
+ case NPU_SET_WEIGHT_BASE:
+ st.weight[0].base = addr;
+ break;
+ case NPU_SET_WEIGHT_LENGTH:
+ st.weight[0].length = cmds[1];
+ break;
+ case NPU_SET_SCALE_BASE:
+ st.scale[0].base = addr;
+ break;
+ case NPU_SET_SCALE_LENGTH:
+ st.scale[0].length = cmds[1];
+ break;
+ case NPU_SET_WEIGHT1_BASE:
+ st.weight[1].base = addr;
+ break;
+ case NPU_SET_WEIGHT1_LENGTH:
+ st.weight[1].length = cmds[1];
+ break;
+ case NPU_SET_SCALE1_BASE: // NPU_SET_WEIGHT2_BASE (U85)
+ if (ethosu_is_u65(edev))
+ st.scale[1].base = addr;
+ else
+ st.weight[2].base = addr;
+ break;
+ case NPU_SET_SCALE1_LENGTH: // NPU_SET_WEIGHT2_LENGTH (U85)
+ if (ethosu_is_u65(edev))
+ st.scale[1].length = cmds[1];
+ else
+ st.weight[1].length = cmds[1];
+ break;
+ case NPU_SET_WEIGHT3_BASE:
+ st.weight[3].base = addr;
+ break;
+ case NPU_SET_WEIGHT3_LENGTH:
+ st.weight[3].length = cmds[1];
+ break;
+
+ case NPU_SET_DMA0_SRC_REGION:
+ if (param & 0x100)
+ st.dma.src.region = -1;
+ else
+ st.dma.src.region = param & 0x7;
+ st.dma.mode = (param >> 9) & 0x3;
+ break;
+ case NPU_SET_DMA0_DST_REGION:
+ if (param & 0x100)
+ st.dma.dst.region = -1;
+ else
+ st.dma.dst.region = param & 0x7;
+ break;
+ case NPU_SET_DMA0_SIZE0:
+ st.dma.size0 = param;
+ break;
+ case NPU_SET_DMA0_SIZE1:
+ st.dma.size1 = param;
+ break;
+ case NPU_SET_DMA0_SRC_STRIDE0:
+ st.dma.src.stride[0] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_SRC_STRIDE1:
+ st.dma.src.stride[1] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_DST_STRIDE0:
+ st.dma.dst.stride[0] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_DST_STRIDE1:
+ st.dma.dst.stride[1] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_SRC:
+ st.dma.src.offset = addr;
+ break;
+ case NPU_SET_DMA0_DST:
+ st.dma.dst.offset = addr;
+ break;
+ case NPU_SET_DMA0_LEN:
+ st.dma.src.len = st.dma.dst.len = addr;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < NPU_BASEP_REGION_MAX; i++) {
+ if (!info->region_size[i])
+ continue;
+ dev_dbg(ddev->dev, "region %d max size: 0x%llx\n",
+ i, info->region_size[i]);
+ }
+
+ bo->info = no_free_ptr(info);
+ return 0;
+}
+
+/**
+ * ethosu_gem_cmdstream_create() - Create a GEM object and attach it to a handle.
+ * @file: DRM file.
+ * @ddev: DRM device.
+ * @exclusive_vm: Exclusive VM. Not NULL if the GEM object can't be shared.
+ * @size: Size of the GEM object to allocate.
+ * @flags: Combination of drm_ethosu_bo_flags flags.
+ * @handle: Pointer holding the handle pointing to the new GEM object.
+ *
+ * Return: Zero on success
+ */
+int ethosu_gem_cmdstream_create(struct drm_file *file,
+ struct drm_device *ddev,
+ u32 size, u64 data, u32 flags, u32 *handle)
+{
+ int ret;
+ struct drm_gem_dma_object *mem;
+ struct ethosu_gem_object *bo;
+
+ mem = drm_gem_dma_create(ddev, size);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ bo = to_ethosu_bo(&mem->base);
+ bo->flags = flags;
+
+ ret = ethosu_gem_cmdstream_copy_and_validate(ddev,
+ (void __user *)(uintptr_t)data,
+ bo, size);
+ if (ret)
+ goto fail;
+
+ /*
+ * Allocate an id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file, &mem->base, handle);
+
+fail:
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_put(&mem->base);
+
+ return ret;
+}
diff --git a/drivers/accel/ethosu/ethosu_gem.h b/drivers/accel/ethosu/ethosu_gem.h
new file mode 100644
index 000000000000..3922895a60fb
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_gem.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2025 Arm, Ltd. */
+
+#ifndef __ETHOSU_GEM_H__
+#define __ETHOSU_GEM_H__
+
+#include "ethosu_device.h"
+#include <drm/drm_gem_dma_helper.h>
+
+struct ethosu_validated_cmdstream_info {
+ u32 cmd_size;
+ u64 region_size[NPU_BASEP_REGION_MAX];
+ bool output_region[NPU_BASEP_REGION_MAX];
+};
+
+/**
+ * struct ethosu_gem_object - Driver specific GEM object.
+ */
+struct ethosu_gem_object {
+ /** @base: Inherit from drm_gem_shmem_object. */
+ struct drm_gem_dma_object base;
+
+ struct ethosu_validated_cmdstream_info *info;
+
+ /** @flags: Combination of drm_ethosu_bo_flags flags. */
+ u32 flags;
+};
+
+static inline
+struct ethosu_gem_object *to_ethosu_bo(struct drm_gem_object *obj)
+{
+ return container_of(to_drm_gem_dma_obj(obj), struct ethosu_gem_object, base);
+}
+
+struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev,
+ size_t size);
+
+int ethosu_gem_create_with_handle(struct drm_file *file,
+ struct drm_device *ddev,
+ u64 *size, u32 flags, uint32_t *handle);
+
+int ethosu_gem_cmdstream_create(struct drm_file *file,
+ struct drm_device *ddev,
+ u32 size, u64 data, u32 flags, u32 *handle);
+
+#endif /* __ETHOSU_GEM_H__ */
diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c
new file mode 100644
index 000000000000..26e7a2f64d71
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_job.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+/* Copyright 2025 Arm, Ltd. */
+
+#include <linux/bitfield.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/ethosu_accel.h>
+
+#include "ethosu_device.h"
+#include "ethosu_drv.h"
+#include "ethosu_gem.h"
+#include "ethosu_job.h"
+
+#define JOB_TIMEOUT_MS 500
+
+static struct ethosu_job *to_ethosu_job(struct drm_sched_job *sched_job)
+{
+ return container_of(sched_job, struct ethosu_job, base);
+}
+
+static const char *ethosu_fence_get_driver_name(struct dma_fence *fence)
+{
+ return "ethosu";
+}
+
+static const char *ethosu_fence_get_timeline_name(struct dma_fence *fence)
+{
+ return "ethosu-npu";
+}
+
+static const struct dma_fence_ops ethosu_fence_ops = {
+ .get_driver_name = ethosu_fence_get_driver_name,
+ .get_timeline_name = ethosu_fence_get_timeline_name,
+};
+
+static void ethosu_job_hw_submit(struct ethosu_device *dev, struct ethosu_job *job)
+{
+ struct drm_gem_dma_object *cmd_bo = to_drm_gem_dma_obj(job->cmd_bo);
+ struct ethosu_validated_cmdstream_info *cmd_info = to_ethosu_bo(job->cmd_bo)->info;
+
+ for (int i = 0; i < job->region_cnt; i++) {
+ struct drm_gem_dma_object *bo;
+ int region = job->region_bo_num[i];
+
+ bo = to_drm_gem_dma_obj(job->region_bo[i]);
+ writel_relaxed(lower_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP(region));
+ writel_relaxed(upper_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP_HI(region));
+ dev_dbg(dev->base.dev, "Region %d base addr = %pad\n", region, &bo->dma_addr);
+ }
+
+ if (job->sram_size) {
+ writel_relaxed(lower_32_bits(dev->sramphys),
+ dev->regs + NPU_REG_BASEP(ETHOSU_SRAM_REGION));
+ writel_relaxed(upper_32_bits(dev->sramphys),
+ dev->regs + NPU_REG_BASEP_HI(ETHOSU_SRAM_REGION));
+ dev_dbg(dev->base.dev, "Region %d base addr = %pad (SRAM)\n",
+ ETHOSU_SRAM_REGION, &dev->sramphys);
+ }
+
+ writel_relaxed(lower_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE);
+ writel_relaxed(upper_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE_HI);
+ writel_relaxed(cmd_info->cmd_size, dev->regs + NPU_REG_QSIZE);
+
+ writel(CMD_TRANSITION_TO_RUN, dev->regs + NPU_REG_CMD);
+
+ dev_dbg(dev->base.dev,
+ "Submitted cmd at %pad to core\n", &cmd_bo->dma_addr);
+}
+
+static int ethosu_acquire_object_fences(struct ethosu_job *job)
+{
+ int i, ret;
+ struct drm_gem_object **bos = job->region_bo;
+ struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
+
+ for (i = 0; i < job->region_cnt; i++) {
+ bool is_write;
+
+ if (!bos[i])
+ break;
+
+ ret = dma_resv_reserve_fences(bos[i]->resv, 1);
+ if (ret)
+ return ret;
+
+ is_write = info->output_region[job->region_bo_num[i]];
+ ret = drm_sched_job_add_implicit_dependencies(&job->base, bos[i],
+ is_write);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ethosu_attach_object_fences(struct ethosu_job *job)
+{
+ int i;
+ struct dma_fence *fence = job->inference_done_fence;
+ struct drm_gem_object **bos = job->region_bo;
+ struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
+
+ for (i = 0; i < job->region_cnt; i++)
+ if (info->output_region[job->region_bo_num[i]])
+ dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
+}
+
+static int ethosu_job_push(struct ethosu_job *job)
+{
+ struct ww_acquire_ctx acquire_ctx;
+ int ret;
+
+ ret = drm_gem_lock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
+ if (ret)
+ return ret;
+
+ ret = ethosu_acquire_object_fences(job);
+ if (ret)
+ goto out;
+
+ ret = pm_runtime_resume_and_get(job->dev->base.dev);
+ if (!ret) {
+ guard(mutex)(&job->dev->sched_lock);
+
+ drm_sched_job_arm(&job->base);
+ job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished);
+ kref_get(&job->refcount); /* put by scheduler job completion */
+ drm_sched_entity_push_job(&job->base);
+ ethosu_attach_object_fences(job);
+ }
+
+out:
+ drm_gem_unlock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
+ return ret;
+}
+
+static void ethosu_job_cleanup(struct kref *ref)
+{
+ struct ethosu_job *job = container_of(ref, struct ethosu_job,
+ refcount);
+ unsigned int i;
+
+ pm_runtime_put_autosuspend(job->dev->base.dev);
+
+ dma_fence_put(job->done_fence);
+ dma_fence_put(job->inference_done_fence);
+
+ for (i = 0; i < job->region_cnt; i++)
+ drm_gem_object_put(job->region_bo[i]);
+
+ drm_gem_object_put(job->cmd_bo);
+
+ kfree(job);
+}
+
+static void ethosu_job_put(struct ethosu_job *job)
+{
+ kref_put(&job->refcount, ethosu_job_cleanup);
+}
+
+static void ethosu_job_free(struct drm_sched_job *sched_job)
+{
+ struct ethosu_job *job = to_ethosu_job(sched_job);
+
+ drm_sched_job_cleanup(sched_job);
+ ethosu_job_put(job);
+}
+
+static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
+{
+ struct ethosu_job *job = to_ethosu_job(sched_job);
+ struct ethosu_device *dev = job->dev;
+ struct dma_fence *fence = job->done_fence;
+
+ if (unlikely(job->base.s_fence->finished.error))
+ return NULL;
+
+ dma_fence_init(fence, &ethosu_fence_ops, &dev->fence_lock,
+ dev->fence_context, ++dev->emit_seqno);
+ dma_fence_get(fence);
+
+ scoped_guard(mutex, &dev->job_lock) {
+ dev->in_flight_job = job;
+ ethosu_job_hw_submit(dev, job);
+ }
+
+ return fence;
+}
+
+static void ethosu_job_handle_irq(struct ethosu_device *dev)
+{
+ u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
+
+ if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) {
+ dev_err(dev->base.dev, "Error IRQ - %x\n", status);
+ drm_sched_fault(&dev->sched);
+ return;
+ }
+
+ scoped_guard(mutex, &dev->job_lock) {
+ if (dev->in_flight_job) {
+ dma_fence_signal(dev->in_flight_job->done_fence);
+ dev->in_flight_job = NULL;
+ }
+ }
+}
+
+static irqreturn_t ethosu_job_irq_handler_thread(int irq, void *data)
+{
+ struct ethosu_device *dev = data;
+
+ ethosu_job_handle_irq(dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ethosu_job_irq_handler(int irq, void *data)
+{
+ struct ethosu_device *dev = data;
+ u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
+
+ if (!(status & STATUS_IRQ_RAISED))
+ return IRQ_NONE;
+
+ writel_relaxed(CMD_CLEAR_IRQ, dev->regs + NPU_REG_CMD);
+ return IRQ_WAKE_THREAD;
+}
+
+static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad)
+{
+ struct ethosu_job *job = to_ethosu_job(bad);
+ struct ethosu_device *dev = job->dev;
+ bool running;
+ u32 *bocmds = to_drm_gem_dma_obj(job->cmd_bo)->vaddr;
+ u32 cmdaddr;
+
+ cmdaddr = readl_relaxed(dev->regs + NPU_REG_QREAD);
+ running = FIELD_GET(STATUS_STATE_RUNNING, readl_relaxed(dev->regs + NPU_REG_STATUS));
+
+ if (running) {
+ int ret;
+ u32 reg;
+
+ ret = readl_relaxed_poll_timeout(dev->regs + NPU_REG_QREAD,
+ reg,
+ reg != cmdaddr,
+ USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+
+ /* If still running and progress is being made, just return */
+ if (!ret)
+ return DRM_GPU_SCHED_STAT_NO_HANG;
+ }
+
+ dev_err(dev->base.dev, "NPU sched timed out: NPU %s, cmdstream offset 0x%x: 0x%x\n",
+ running ? "running" : "stopped",
+ cmdaddr, bocmds[cmdaddr / 4]);
+
+ drm_sched_stop(&dev->sched, bad);
+
+ scoped_guard(mutex, &dev->job_lock)
+ dev->in_flight_job = NULL;
+
+ /* Proceed with reset now. */
+ pm_runtime_force_suspend(dev->base.dev);
+ pm_runtime_force_resume(dev->base.dev);
+
+ /* Restart the scheduler */
+ drm_sched_start(&dev->sched, 0);
+
+ return DRM_GPU_SCHED_STAT_RESET;
+}
+
+static const struct drm_sched_backend_ops ethosu_sched_ops = {
+ .run_job = ethosu_job_run,
+ .timedout_job = ethosu_job_timedout,
+ .free_job = ethosu_job_free
+};
+
+int ethosu_job_init(struct ethosu_device *edev)
+{
+ struct device *dev = edev->base.dev;
+ struct drm_sched_init_args args = {
+ .ops = &ethosu_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
+ .credit_limit = 1,
+ .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
+ .name = dev_name(dev),
+ .dev = dev,
+ };
+ int ret;
+
+ spin_lock_init(&edev->fence_lock);
+ ret = devm_mutex_init(dev, &edev->job_lock);
+ if (ret)
+ return ret;
+ ret = devm_mutex_init(dev, &edev->sched_lock);
+ if (ret)
+ return ret;
+
+ edev->irq = platform_get_irq(to_platform_device(dev), 0);
+ if (edev->irq < 0)
+ return edev->irq;
+
+ ret = devm_request_threaded_irq(dev, edev->irq,
+ ethosu_job_irq_handler,
+ ethosu_job_irq_handler_thread,
+ IRQF_SHARED, KBUILD_MODNAME,
+ edev);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ return ret;
+ }
+
+ edev->fence_context = dma_fence_context_alloc(1);
+
+ ret = drm_sched_init(&edev->sched, &args);
+ if (ret) {
+ dev_err(dev, "Failed to create scheduler: %d\n", ret);
+ goto err_sched;
+ }
+
+ return 0;
+
+err_sched:
+ drm_sched_fini(&edev->sched);
+ return ret;
+}
+
+void ethosu_job_fini(struct ethosu_device *dev)
+{
+ drm_sched_fini(&dev->sched);
+}
+
+int ethosu_job_open(struct ethosu_file_priv *ethosu_priv)
+{
+ struct ethosu_device *dev = ethosu_priv->edev;
+ struct drm_gpu_scheduler *sched = &dev->sched;
+ int ret;
+
+ ret = drm_sched_entity_init(&ethosu_priv->sched_entity,
+ DRM_SCHED_PRIORITY_NORMAL,
+ &sched, 1, NULL);
+ return WARN_ON(ret);
+}
+
+void ethosu_job_close(struct ethosu_file_priv *ethosu_priv)
+{
+ struct drm_sched_entity *entity = &ethosu_priv->sched_entity;
+
+ drm_sched_entity_destroy(entity);
+}
+
+static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
+ struct drm_ethosu_job *job)
+{
+ struct ethosu_device *edev = to_ethosu_device(dev);
+ struct ethosu_file_priv *file_priv = file->driver_priv;
+ struct ethosu_job *ejob = NULL;
+ struct ethosu_validated_cmdstream_info *cmd_info;
+ int ret = 0;
+
+ /* BO region 2 is reserved if SRAM is used */
+ if (job->region_bo_handles[ETHOSU_SRAM_REGION] && job->sram_size)
+ return -EINVAL;
+
+ if (edev->npu_info.sram_size < job->sram_size)
+ return -EINVAL;
+
+ ejob = kzalloc(sizeof(*ejob), GFP_KERNEL);
+ if (!ejob)
+ return -ENOMEM;
+
+ kref_init(&ejob->refcount);
+
+ ejob->dev = edev;
+ ejob->sram_size = job->sram_size;
+
+ ejob->done_fence = kzalloc(sizeof(*ejob->done_fence), GFP_KERNEL);
+ if (!ejob->done_fence) {
+ ret = -ENOMEM;
+ goto out_cleanup_job;
+ }
+
+ ret = drm_sched_job_init(&ejob->base,
+ &file_priv->sched_entity,
+ 1, NULL, file->client_id);
+ if (ret)
+ goto out_put_job;
+
+ ejob->cmd_bo = drm_gem_object_lookup(file, job->cmd_bo);
+ if (!ejob->cmd_bo) {
+ ret = -ENOENT;
+ goto out_cleanup_job;
+ }
+ cmd_info = to_ethosu_bo(ejob->cmd_bo)->info;
+ if (!cmd_info) {
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+
+ for (int i = 0; i < NPU_BASEP_REGION_MAX; i++) {
+ struct drm_gem_object *gem;
+
+ /* Can only omit a BO handle if the region is not used or used for SRAM */
+ if (!job->region_bo_handles[i] &&
+ (!cmd_info->region_size[i] || (i == ETHOSU_SRAM_REGION && job->sram_size)))
+ continue;
+
+ if (job->region_bo_handles[i] && !cmd_info->region_size[i]) {
+ dev_err(dev->dev,
+ "Cmdstream BO handle %d set for unused region %d\n",
+ job->region_bo_handles[i], i);
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+
+ gem = drm_gem_object_lookup(file, job->region_bo_handles[i]);
+ if (!gem) {
+ dev_err(dev->dev,
+ "Invalid BO handle %d for region %d\n",
+ job->region_bo_handles[i], i);
+ ret = -ENOENT;
+ goto out_cleanup_job;
+ }
+
+ ejob->region_bo[ejob->region_cnt] = gem;
+ ejob->region_bo_num[ejob->region_cnt] = i;
+ ejob->region_cnt++;
+
+ if (to_ethosu_bo(gem)->info) {
+ dev_err(dev->dev,
+ "Cmdstream BO handle %d used for region %d\n",
+ job->region_bo_handles[i], i);
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+
+ /* Verify the command stream doesn't have accesses outside the BO */
+ if (cmd_info->region_size[i] > gem->size) {
+ dev_err(dev->dev,
+ "cmd stream region %d size greater than BO size (%llu > %zu)\n",
+ i, cmd_info->region_size[i], gem->size);
+ ret = -EOVERFLOW;
+ goto out_cleanup_job;
+ }
+ }
+ ret = ethosu_job_push(ejob);
+
+out_cleanup_job:
+ if (ret)
+ drm_sched_job_cleanup(&ejob->base);
+out_put_job:
+ ethosu_job_put(ejob);
+
+ return ret;
+}
+
+int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ethosu_submit *args = data;
+ int ret = 0;
+ unsigned int i = 0;
+
+ if (args->pad) {
+ drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n");
+ return -EINVAL;
+ }
+
+ struct drm_ethosu_job __free(kvfree) *jobs =
+ kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL);
+ if (!jobs)
+ return -ENOMEM;
+
+ if (copy_from_user(jobs,
+ (void __user *)(uintptr_t)args->jobs,
+ args->job_count * sizeof(*jobs))) {
+ drm_dbg(dev, "Failed to copy incoming job array\n");
+ return -EFAULT;
+ }
+
+ for (i = 0; i < args->job_count; i++) {
+ ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/accel/ethosu/ethosu_job.h b/drivers/accel/ethosu/ethosu_job.h
new file mode 100644
index 000000000000..ff1cf448d094
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_job.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+/* Copyright 2025 Arm, Ltd. */
+
+#ifndef __ETHOSU_JOB_H__
+#define __ETHOSU_JOB_H__
+
+#include <linux/kref.h>
+#include <drm/gpu_scheduler.h>
+
+struct ethosu_device;
+struct ethosu_file_priv;
+
+struct ethosu_job {
+ struct drm_sched_job base;
+ struct ethosu_device *dev;
+
+ struct drm_gem_object *cmd_bo;
+ struct drm_gem_object *region_bo[NPU_BASEP_REGION_MAX];
+ u8 region_bo_num[NPU_BASEP_REGION_MAX];
+ u8 region_cnt;
+ u32 sram_size;
+
+ /* Fence to be signaled by drm-sched once its done with the job */
+ struct dma_fence *inference_done_fence;
+
+ /* Fence to be signaled by IRQ handler when the job is complete. */
+ struct dma_fence *done_fence;
+
+ struct kref refcount;
+};
+
+int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file);
+
+int ethosu_job_init(struct ethosu_device *dev);
+void ethosu_job_fini(struct ethosu_device *dev);
+int ethosu_job_open(struct ethosu_file_priv *ethosu_priv);
+void ethosu_job_close(struct ethosu_file_priv *ethosu_priv);
+
+#endif
diff --git a/drivers/accel/habanalabs/Kconfig b/drivers/accel/habanalabs/Kconfig
new file mode 100644
index 000000000000..6d1506acbd72
--- /dev/null
+++ b/drivers/accel/habanalabs/Kconfig
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# HabanaLabs AI accelerators driver
+#
+
+config DRM_ACCEL_HABANALABS
+ tristate "HabanaLabs AI accelerators"
+ depends on DRM_ACCEL
+ depends on X86 && X86_64
+ depends on PCI && HAS_IOMEM
+ select GENERIC_ALLOCATOR
+ select HWMON
+ select DMA_SHARED_BUFFER
+ select CRC32
+ select FW_LOADER
+ help
+ Enables PCIe card driver for Habana's AI Processors (AIP) that are
+ designed to accelerate Deep Learning inference and training workloads.
+
+ The driver manages the PCIe devices and provides IOCTL interface for
+ the user to submit workloads to the devices.
+
+ The user-space interface is described in
+ include/uapi/drm/habanalabs_accel.h
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called habanalabs.
+
+if DRM_ACCEL_HABANALABS
+
+config HL_HLDIO
+ bool "Habanalabs NVMe Direct I/O (HLDIO)"
+ depends on PCI_P2PDMA
+ depends on BLOCK
+ help
+ Enable NVMe peer-to-peer direct I/O support for Habanalabs AI
+ accelerators.
+
+ This allows direct data transfers between NVMe storage devices
+ and Habanalabs accelerators without involving system memory,
+ using PCI peer-to-peer DMA capabilities.
+
+ Requirements:
+ - CONFIG_PCI_P2PDMA=y
+ - NVMe device and Habanalabs accelerator under same PCI root complex
+ - IOMMU disabled or in passthrough mode
+ - Hardware supporting PCI P2P DMA
+
+ If unsure, say N
+endif # DRM_ACCEL_HABANALABS
diff --git a/drivers/accel/habanalabs/Makefile b/drivers/accel/habanalabs/Makefile
new file mode 100644
index 000000000000..98510cdd5066
--- /dev/null
+++ b/drivers/accel/habanalabs/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for HabanaLabs AI accelerators driver
+#
+
+obj-$(CONFIG_DRM_ACCEL_HABANALABS) := habanalabs.o
+
+include $(src)/common/Makefile
+habanalabs-y += $(HL_COMMON_FILES)
+
+include $(src)/gaudi2/Makefile
+habanalabs-y += $(HL_GAUDI2_FILES)
+
+include $(src)/gaudi/Makefile
+habanalabs-y += $(HL_GAUDI_FILES)
+
+include $(src)/goya/Makefile
+habanalabs-y += $(HL_GOYA_FILES)
+
+habanalabs-$(CONFIG_DEBUG_FS) += common/debugfs.o
diff --git a/drivers/accel/habanalabs/common/Makefile b/drivers/accel/habanalabs/common/Makefile
new file mode 100644
index 000000000000..b6d00de09db5
--- /dev/null
+++ b/drivers/accel/habanalabs/common/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+include $(src)/common/mmu/Makefile
+habanalabs-y += $(HL_COMMON_MMU_FILES)
+
+include $(src)/common/pci/Makefile
+habanalabs-y += $(HL_COMMON_PCI_FILES)
+
+HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \
+ common/asid.o common/habanalabs_ioctl.o \
+ common/command_buffer.o common/hw_queue.o common/irq.o \
+ common/sysfs.o common/hwmon.o common/memory.o \
+ common/command_submission.o common/firmware_if.o \
+ common/security.o common/state_dump.o \
+ common/memory_mgr.o common/decoder.o
+
+# Conditionally add HLDIO support
+ifdef CONFIG_HL_HLDIO
+HL_COMMON_FILES += common/hldio.o
+endif \ No newline at end of file
diff --git a/drivers/misc/habanalabs/common/asid.c b/drivers/accel/habanalabs/common/asid.c
index c9c2619cc43d..c9c2619cc43d 100644
--- a/drivers/misc/habanalabs/common/asid.c
+++ b/drivers/accel/habanalabs/common/asid.c
diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/accel/habanalabs/common/command_buffer.c
index b027f66f8bd4..0f0d295116e7 100644
--- a/drivers/misc/habanalabs/common/command_buffer.c
+++ b/drivers/accel/habanalabs/common/command_buffer.c
@@ -5,108 +5,62 @@
* All Rights Reserved.
*/
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include "habanalabs.h"
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#define CB_VA_POOL_SIZE (4UL * SZ_1G)
+
static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb)
{
struct hl_device *hdev = ctx->hdev;
struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_vm_va_block *va_block, *tmp;
- dma_addr_t bus_addr;
- u64 virt_addr;
u32 page_size = prop->pmmu.page_size;
- s32 offset;
int rc;
if (!hdev->supports_cb_mapping) {
dev_err_ratelimited(hdev->dev,
- "Cannot map CB because no VA range is allocated for CB mapping\n");
- return -EINVAL;
- }
-
- if (!hdev->mmu_enable) {
- dev_err_ratelimited(hdev->dev,
- "Cannot map CB because MMU is disabled\n");
+ "Mapping a CB to the device's MMU is not supported\n");
return -EINVAL;
}
- INIT_LIST_HEAD(&cb->va_block_list);
-
- for (bus_addr = cb->bus_address;
- bus_addr < cb->bus_address + cb->size;
- bus_addr += page_size) {
-
- virt_addr = (u64) gen_pool_alloc(ctx->cb_va_pool, page_size);
- if (!virt_addr) {
- dev_err(hdev->dev,
- "Failed to allocate device virtual address for CB\n");
- rc = -ENOMEM;
- goto err_va_pool_free;
- }
+ if (cb->is_mmu_mapped)
+ return 0;
- va_block = kzalloc(sizeof(*va_block), GFP_KERNEL);
- if (!va_block) {
- rc = -ENOMEM;
- gen_pool_free(ctx->cb_va_pool, virt_addr, page_size);
- goto err_va_pool_free;
- }
+ cb->roundup_size = roundup(cb->size, page_size);
- va_block->start = virt_addr;
- va_block->end = virt_addr + page_size - 1;
- va_block->size = page_size;
- list_add_tail(&va_block->node, &cb->va_block_list);
+ cb->virtual_addr = (u64) gen_pool_alloc(ctx->cb_va_pool, cb->roundup_size);
+ if (!cb->virtual_addr) {
+ dev_err(hdev->dev, "Failed to allocate device virtual address for CB\n");
+ return -ENOMEM;
}
- mutex_lock(&ctx->mmu_lock);
-
- bus_addr = cb->bus_address;
- offset = 0;
- list_for_each_entry(va_block, &cb->va_block_list, node) {
- rc = hl_mmu_map_page(ctx, va_block->start, bus_addr,
- va_block->size, list_is_last(&va_block->node,
- &cb->va_block_list));
- if (rc) {
- dev_err(hdev->dev, "Failed to map VA %#llx to CB\n",
- va_block->start);
- goto err_va_umap;
- }
+ mutex_lock(&hdev->mmu_lock);
- bus_addr += va_block->size;
- offset += va_block->size;
+ rc = hl_mmu_map_contiguous(ctx, cb->virtual_addr, cb->bus_address, cb->roundup_size);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to map VA %#llx to CB\n", cb->virtual_addr);
+ goto err_va_pool_free;
}
rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV);
+ if (rc)
+ goto err_mmu_unmap;
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
cb->is_mmu_mapped = true;
- return rc;
-
-err_va_umap:
- list_for_each_entry(va_block, &cb->va_block_list, node) {
- if (offset <= 0)
- break;
- hl_mmu_unmap_page(ctx, va_block->start, va_block->size,
- offset <= va_block->size);
- offset -= va_block->size;
- }
-
- rc = hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
-
- mutex_unlock(&ctx->mmu_lock);
+ return 0;
+err_mmu_unmap:
+ hl_mmu_unmap_contiguous(ctx, cb->virtual_addr, cb->roundup_size);
err_va_pool_free:
- list_for_each_entry_safe(va_block, tmp, &cb->va_block_list, node) {
- gen_pool_free(ctx->cb_va_pool, va_block->start, va_block->size);
- list_del(&va_block->node);
- kfree(va_block);
- }
+ mutex_unlock(&hdev->mmu_lock);
+ gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size);
return rc;
}
@@ -114,27 +68,13 @@ err_va_pool_free:
static void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb)
{
struct hl_device *hdev = ctx->hdev;
- struct hl_vm_va_block *va_block, *tmp;
-
- mutex_lock(&ctx->mmu_lock);
-
- list_for_each_entry(va_block, &cb->va_block_list, node)
- if (hl_mmu_unmap_page(ctx, va_block->start, va_block->size,
- list_is_last(&va_block->node,
- &cb->va_block_list)))
- dev_warn_ratelimited(hdev->dev,
- "Failed to unmap CB's va 0x%llx\n",
- va_block->start);
+ mutex_lock(&hdev->mmu_lock);
+ hl_mmu_unmap_contiguous(ctx, cb->virtual_addr, cb->roundup_size);
hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
+ mutex_unlock(&hdev->mmu_lock);
- mutex_unlock(&ctx->mmu_lock);
-
- list_for_each_entry_safe(va_block, tmp, &cb->va_block_list, node) {
- gen_pool_free(ctx->cb_va_pool, va_block->start, va_block->size);
- list_del(&va_block->node);
- kfree(va_block);
- }
+ gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size);
}
static void cb_fini(struct hl_device *hdev, struct hl_cb *cb)
@@ -151,6 +91,7 @@ static void cb_fini(struct hl_device *hdev, struct hl_cb *cb)
static void cb_do_release(struct hl_device *hdev, struct hl_cb *cb)
{
if (cb->is_pool) {
+ atomic_set(&cb->is_handle_destroyed, 0);
spin_lock(&hdev->cb_pool_lock);
list_add(&cb->pool_list, &hdev->cb_pool);
spin_unlock(&hdev->cb_pool_lock);
@@ -361,8 +302,25 @@ int hl_cb_create(struct hl_device *hdev, struct hl_mem_mgr *mmg,
int hl_cb_destroy(struct hl_mem_mgr *mmg, u64 cb_handle)
{
+ struct hl_cb *cb;
int rc;
+ cb = hl_cb_get(mmg, cb_handle);
+ if (!cb) {
+ dev_dbg(mmg->dev, "CB destroy failed, no CB was found for handle %#llx\n",
+ cb_handle);
+ return -EINVAL;
+ }
+
+ /* Make sure that CB handle isn't destroyed more than once */
+ rc = atomic_cmpxchg(&cb->is_handle_destroyed, 0, 1);
+ hl_cb_put(cb);
+ if (rc) {
+ dev_dbg(mmg->dev, "CB destroy failed, handle %#llx was already destroyed\n",
+ cb_handle);
+ return -EINVAL;
+ }
+
rc = hl_mmap_mem_buf_put_handle(mmg, cb_handle);
if (rc < 0)
return rc; /* Invalid handle */
@@ -376,7 +334,6 @@ int hl_cb_destroy(struct hl_mem_mgr *mmg, u64 cb_handle)
static int hl_cb_info(struct hl_mem_mgr *mmg,
u64 handle, u32 flags, u32 *usage_cnt, u64 *device_va)
{
- struct hl_vm_va_block *va_block;
struct hl_cb *cb;
int rc = 0;
@@ -388,9 +345,8 @@ static int hl_cb_info(struct hl_mem_mgr *mmg,
}
if (flags & HL_CB_FLAGS_GET_DEVICE_VA) {
- va_block = list_first_entry(&cb->va_block_list, struct hl_vm_va_block, node);
- if (va_block) {
- *device_va = va_block->start;
+ if (cb->is_mmu_mapped) {
+ *device_va = cb->virtual_addr;
} else {
dev_err(mmg->dev, "CB is not mapped to the device's MMU\n");
rc = -EINVAL;
@@ -405,17 +361,18 @@ out:
return rc;
}
-int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data)
+int hl_cb_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
{
- union hl_cb_args *args = data;
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
struct hl_device *hdev = hpriv->hdev;
+ union hl_cb_args *args = data;
u64 handle = 0, device_va = 0;
enum hl_device_status status;
u32 usage_cnt = 0;
int rc;
if (!hl_device_operational(hdev, &status)) {
- dev_warn_ratelimited(hdev->dev,
+ dev_dbg_ratelimited(hdev->dev,
"Device is %s. Can't execute CB IOCTL\n",
hdev->status[status]);
return -EBUSY;
@@ -566,16 +523,23 @@ int hl_cb_va_pool_init(struct hl_ctx *ctx)
return -ENOMEM;
}
- rc = gen_pool_add(ctx->cb_va_pool, prop->cb_va_start_addr,
- prop->cb_va_end_addr - prop->cb_va_start_addr, -1);
+ ctx->cb_va_pool_base = hl_reserve_va_block(hdev, ctx, HL_VA_RANGE_TYPE_HOST,
+ CB_VA_POOL_SIZE, HL_MMU_VA_ALIGNMENT_NOT_NEEDED);
+ if (!ctx->cb_va_pool_base) {
+ rc = -ENOMEM;
+ goto err_pool_destroy;
+ }
+ rc = gen_pool_add(ctx->cb_va_pool, ctx->cb_va_pool_base, CB_VA_POOL_SIZE, -1);
if (rc) {
dev_err(hdev->dev,
"Failed to add memory to VA gen pool for CB mapping\n");
- goto err_pool_destroy;
+ goto err_unreserve_va_block;
}
return 0;
+err_unreserve_va_block:
+ hl_unreserve_va_block(hdev, ctx, ctx->cb_va_pool_base, CB_VA_POOL_SIZE);
err_pool_destroy:
gen_pool_destroy(ctx->cb_va_pool);
@@ -590,4 +554,5 @@ void hl_cb_va_pool_fini(struct hl_ctx *ctx)
return;
gen_pool_destroy(ctx->cb_va_pool);
+ hl_unreserve_va_block(hdev, ctx, ctx->cb_va_pool_base, CB_VA_POOL_SIZE);
}
diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/accel/habanalabs/common/command_submission.c
index 90a4574cbe2d..dee487724918 100644
--- a/drivers/misc/habanalabs/common/command_submission.c
+++ b/drivers/accel/habanalabs/common/command_submission.c
@@ -5,16 +5,19 @@
* All Rights Reserved.
*/
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include "habanalabs.h"
#include <linux/uaccess.h>
#include <linux/slab.h>
#define HL_CS_FLAGS_TYPE_MASK (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT | \
- HL_CS_FLAGS_COLLECTIVE_WAIT)
+ HL_CS_FLAGS_COLLECTIVE_WAIT | HL_CS_FLAGS_RESERVE_SIGNALS_ONLY | \
+ HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY | HL_CS_FLAGS_ENGINE_CORE_COMMAND | \
+ HL_CS_FLAGS_ENGINES_COMMAND | HL_CS_FLAGS_FLUSH_PCI_HBW_WRITES)
-#define MAX_TS_ITER_NUM 10
+
+#define MAX_TS_ITER_NUM 100
/**
* enum hl_cs_wait_status - cs wait status
@@ -28,6 +31,24 @@ enum hl_cs_wait_status {
CS_WAIT_STATUS_GONE
};
+/*
+ * Data used while handling wait/timestamp nodes.
+ * The purpose of this struct is to store the needed data for both operations
+ * in one variable instead of passing large number of arguments to functions.
+ */
+struct wait_interrupt_data {
+ struct hl_user_interrupt *interrupt;
+ struct hl_mmap_mem_buf *buf;
+ struct hl_mem_mgr *mmg;
+ struct hl_cb *cq_cb;
+ u64 ts_handle;
+ u64 ts_offset;
+ u64 cq_handle;
+ u64 cq_offset;
+ u64 target_value;
+ u64 intr_timeout_us;
+};
+
static void job_wq_completion(struct work_struct *work);
static int _hl_cs_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u64 timeout_us, u64 seq,
enum hl_cs_wait_status *status, s64 *timestamp);
@@ -277,14 +298,8 @@ bool cs_needs_timeout(struct hl_cs *cs)
static bool is_cb_patched(struct hl_device *hdev, struct hl_cs_job *job)
{
- /*
- * Patched CB is created for external queues jobs, and for H/W queues
- * jobs if the user CB was allocated by driver and MMU is disabled.
- */
- return (job->queue_type == QUEUE_TYPE_EXT ||
- (job->queue_type == QUEUE_TYPE_HW &&
- job->is_kernel_allocated_cb &&
- !hdev->mmu_enable));
+ /* Patched CB is created for external queues jobs */
+ return (job->queue_type == QUEUE_TYPE_EXT);
}
/*
@@ -360,14 +375,13 @@ static void hl_complete_job(struct hl_device *hdev, struct hl_cs_job *job)
}
}
- /* For H/W queue jobs, if a user CB was allocated by driver and MMU is
- * enabled, the user CB isn't released in cs_parser() and thus should be
+ /* For H/W queue jobs, if a user CB was allocated by driver,
+ * the user CB isn't released in cs_parser() and thus should be
* released here. This is also true for INT queues jobs which were
* allocated by driver.
*/
- if ((job->is_kernel_allocated_cb &&
- ((job->queue_type == QUEUE_TYPE_HW && hdev->mmu_enable) ||
- job->queue_type == QUEUE_TYPE_INT))) {
+ if (job->is_kernel_allocated_cb &&
+ (job->queue_type == QUEUE_TYPE_HW || job->queue_type == QUEUE_TYPE_INT)) {
atomic_dec(&job->user_cb->cs_cnt);
hl_cb_put(job->user_cb);
}
@@ -395,8 +409,16 @@ static void hl_complete_job(struct hl_device *hdev, struct hl_cs_job *job)
* flow by calling 'hl_hw_queue_update_ci'.
*/
if (cs_needs_completion(cs) &&
- (job->queue_type == QUEUE_TYPE_EXT || job->queue_type == QUEUE_TYPE_HW))
+ (job->queue_type == QUEUE_TYPE_EXT || job->queue_type == QUEUE_TYPE_HW)) {
+
+ /* In CS based completions, the timestamp is already available,
+ * so no need to extract it from job
+ */
+ if (hdev->asic_prop.completion_mode == HL_COMPLETION_MODE_JOB)
+ cs->completion_timestamp = job->timestamp;
+
cs_put(cs);
+ }
hl_cs_job_put(job);
}
@@ -646,7 +668,7 @@ static inline void cs_release_sob_reset_handler(struct hl_device *hdev,
/*
* we get refcount upon reservation of signals or signal/wait cs for the
* hw_sob object, and need to put it when the first staged cs
- * (which cotains the encaps signals) or cs signal/wait is completed.
+ * (which contains the encaps signals) or cs signal/wait is completed.
*/
if ((hl_cs_cmpl->type == CS_TYPE_SIGNAL) ||
(hl_cs_cmpl->type == CS_TYPE_WAIT) ||
@@ -740,13 +762,11 @@ static void cs_do_release(struct kref *ref)
*/
if (hl_cs_cmpl->encaps_signals)
kref_put(&hl_cs_cmpl->encaps_sig_hdl->refcount,
- hl_encaps_handle_do_release);
+ hl_encaps_release_handle_and_put_ctx);
}
- if ((cs->type == CS_TYPE_WAIT || cs->type == CS_TYPE_COLLECTIVE_WAIT)
- && cs->encaps_signals)
- kref_put(&cs->encaps_sig_hdl->refcount,
- hl_encaps_handle_do_release);
+ if ((cs->type == CS_TYPE_WAIT || cs->type == CS_TYPE_COLLECTIVE_WAIT) && cs->encaps_signals)
+ kref_put(&cs->encaps_sig_hdl->refcount, hl_encaps_release_handle_and_put_ctx);
out:
/* Must be called before hl_ctx_put because inside we use ctx to get
@@ -775,7 +795,7 @@ out:
}
if (cs->timestamp) {
- cs->fence->timestamp = ktime_get();
+ cs->fence->timestamp = cs->completion_timestamp;
hl_push_cs_outcome(hdev, &cs->ctx->outcome_store, cs->sequence,
cs->fence->timestamp, cs->fence->error);
}
@@ -795,12 +815,14 @@ out:
static void cs_timedout(struct work_struct *work)
{
+ struct hl_cs *cs = container_of(work, struct hl_cs, work_tdr.work);
+ bool skip_reset_on_timeout, device_reset = false;
struct hl_device *hdev;
- u64 event_mask;
+ u64 event_mask = 0x0;
+ uint timeout_sec;
int rc;
- struct hl_cs *cs = container_of(work, struct hl_cs,
- work_tdr.work);
- bool skip_reset_on_timeout = cs->skip_reset_on_timeout, device_reset = false;
+
+ skip_reset_on_timeout = cs->skip_reset_on_timeout;
rc = cs_get_unless_zero(cs);
if (!rc)
@@ -824,40 +846,38 @@ static void cs_timedout(struct work_struct *work)
}
/* Save only the first CS timeout parameters */
- rc = atomic_cmpxchg(&hdev->last_error.cs_timeout.write_enable, 1, 0);
+ rc = atomic_cmpxchg(&hdev->captured_err_info.cs_timeout.write_enable, 1, 0);
if (rc) {
- hdev->last_error.cs_timeout.timestamp = ktime_get();
- hdev->last_error.cs_timeout.seq = cs->sequence;
-
- event_mask = device_reset ? (HL_NOTIFIER_EVENT_CS_TIMEOUT |
- HL_NOTIFIER_EVENT_DEVICE_RESET) : HL_NOTIFIER_EVENT_CS_TIMEOUT;
-
- hl_notifier_event_send_all(hdev, event_mask);
+ hdev->captured_err_info.cs_timeout.timestamp = ktime_get();
+ hdev->captured_err_info.cs_timeout.seq = cs->sequence;
+ event_mask |= HL_NOTIFIER_EVENT_CS_TIMEOUT;
}
+ timeout_sec = jiffies_to_msecs(hdev->timeout_jiffies) / 1000;
+
switch (cs->type) {
case CS_TYPE_SIGNAL:
dev_err(hdev->dev,
- "Signal command submission %llu has not finished in time!\n",
- cs->sequence);
+ "Signal command submission %llu has not finished in %u seconds!\n",
+ cs->sequence, timeout_sec);
break;
case CS_TYPE_WAIT:
dev_err(hdev->dev,
- "Wait command submission %llu has not finished in time!\n",
- cs->sequence);
+ "Wait command submission %llu has not finished in %u seconds!\n",
+ cs->sequence, timeout_sec);
break;
case CS_TYPE_COLLECTIVE_WAIT:
dev_err(hdev->dev,
- "Collective Wait command submission %llu has not finished in time!\n",
- cs->sequence);
+ "Collective Wait command submission %llu has not finished in %u seconds!\n",
+ cs->sequence, timeout_sec);
break;
default:
dev_err(hdev->dev,
- "Command submission %llu has not finished in time!\n",
- cs->sequence);
+ "Command submission %llu has not finished in %u seconds!\n",
+ cs->sequence, timeout_sec);
break;
}
@@ -867,8 +887,12 @@ static void cs_timedout(struct work_struct *work)
cs_put(cs);
- if (device_reset)
- hl_device_reset(hdev, HL_DRV_RESET_TDR);
+ if (device_reset) {
+ event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
+ hl_device_cond_reset(hdev, HL_DRV_RESET_TDR, event_mask);
+ } else if (event_mask) {
+ hl_notifier_event_send_all(hdev, event_mask);
+ }
}
static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx,
@@ -1009,6 +1033,34 @@ static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs)
hl_complete_job(hdev, job);
}
+/*
+ * release_reserved_encaps_signals() - release reserved encapsulated signals.
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Release reserved encapsulated signals which weren't un-reserved, or for which a CS with
+ * encapsulated signals wasn't submitted and thus weren't released as part of CS roll-back.
+ * For these signals need also to put the refcount of the H/W SOB which was taken at the
+ * reservation.
+ */
+static void release_reserved_encaps_signals(struct hl_device *hdev)
+{
+ struct hl_ctx *ctx = hl_get_compute_ctx(hdev);
+ struct hl_cs_encaps_sig_handle *handle;
+ struct hl_encaps_signals_mgr *mgr;
+ u32 id;
+
+ if (!ctx)
+ return;
+
+ mgr = &ctx->sig_mgr;
+
+ idr_for_each_entry(&mgr->handles, handle, id)
+ if (handle->cs_seq == ULLONG_MAX)
+ kref_put(&handle->refcount, hl_encaps_release_handle_and_put_sob_ctx);
+
+ hl_ctx_put(ctx);
+}
+
void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush)
{
int i;
@@ -1037,6 +1089,8 @@ void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush)
}
force_complete_multi_cs(hdev);
+
+ release_reserved_encaps_signals(hdev);
}
static void
@@ -1046,17 +1100,19 @@ wake_pending_user_interrupt_threads(struct hl_user_interrupt *interrupt)
unsigned long flags;
spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_for_each_entry_safe(pend, temp, &interrupt->wait_list_head, wait_list_node) {
- if (pend->ts_reg_info.buf) {
- list_del(&pend->wait_list_node);
- hl_mmap_mem_buf_put(pend->ts_reg_info.buf);
- hl_cb_put(pend->ts_reg_info.cq_cb);
- } else {
- pend->fence.error = -EIO;
- complete_all(&pend->fence.completion);
- }
+ list_for_each_entry_safe(pend, temp, &interrupt->wait_list_head, list_node) {
+ pend->fence.error = -EIO;
+ complete_all(&pend->fence.completion);
}
spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
+
+ spin_lock_irqsave(&interrupt->ts_list_lock, flags);
+ list_for_each_entry_safe(pend, temp, &interrupt->ts_list_head, list_node) {
+ list_del(&pend->list_node);
+ hl_mmap_mem_buf_put(pend->ts_reg_info.buf);
+ hl_cb_put(pend->ts_reg_info.cq_cb);
+ }
+ spin_unlock_irqrestore(&interrupt->ts_list_lock, flags);
}
void hl_release_pending_user_interrupts(struct hl_device *hdev)
@@ -1087,6 +1143,26 @@ void hl_release_pending_user_interrupts(struct hl_device *hdev)
wake_pending_user_interrupt_threads(interrupt);
}
+static void force_complete_cs(struct hl_device *hdev)
+{
+ struct hl_cs *cs;
+
+ spin_lock(&hdev->cs_mirror_lock);
+
+ list_for_each_entry(cs, &hdev->cs_mirror_list, mirror_node) {
+ cs->fence->error = -EIO;
+ complete_all(&cs->fence->completion);
+ }
+
+ spin_unlock(&hdev->cs_mirror_lock);
+}
+
+void hl_abort_waiting_for_cs_completions(struct hl_device *hdev)
+{
+ force_complete_cs(hdev);
+ force_complete_multi_cs(hdev);
+}
+
static void job_wq_completion(struct work_struct *work)
{
struct hl_cs_job *job = container_of(work, struct hl_cs_job,
@@ -1108,6 +1184,22 @@ static void cs_completion(struct work_struct *work)
hl_complete_job(hdev, job);
}
+u32 hl_get_active_cs_num(struct hl_device *hdev)
+{
+ u32 active_cs_num = 0;
+ struct hl_cs *cs;
+
+ spin_lock(&hdev->cs_mirror_lock);
+
+ list_for_each_entry(cs, &hdev->cs_mirror_list, mirror_node)
+ if (!cs->completed)
+ active_cs_num++;
+
+ spin_unlock(&hdev->cs_mirror_lock);
+
+ return active_cs_num;
+}
+
static int validate_queue_index(struct hl_device *hdev,
struct hl_cs_chunk *chunk,
enum hl_queue_type *queue_type,
@@ -1242,6 +1334,12 @@ static enum hl_cs_type hl_cs_get_cs_type(u32 cs_type_flags)
return CS_RESERVE_SIGNALS;
else if (cs_type_flags & HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY)
return CS_UNRESERVE_SIGNALS;
+ else if (cs_type_flags & HL_CS_FLAGS_ENGINE_CORE_COMMAND)
+ return CS_TYPE_ENGINE_CORE;
+ else if (cs_type_flags & HL_CS_FLAGS_ENGINES_COMMAND)
+ return CS_TYPE_ENGINES;
+ else if (cs_type_flags & HL_CS_FLAGS_FLUSH_PCI_HBW_WRITES)
+ return CS_TYPE_FLUSH_PCI_HBW_WRITES;
else
return CS_TYPE_DEFAULT;
}
@@ -1253,10 +1351,17 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args)
u32 cs_type_flags, num_chunks;
enum hl_device_status status;
enum hl_cs_type cs_type;
+ bool is_sync_stream;
+ int i;
- if (!hl_device_operational(hdev, &status)) {
+ for (i = 0 ; i < sizeof(args->in.pad) ; i++)
+ if (args->in.pad[i]) {
+ dev_dbg(hdev->dev, "Padding bytes must be 0\n");
+ return -EINVAL;
+ }
+
+ if (!hl_device_operational(hdev, &status))
return -EBUSY;
- }
if ((args->in.cs_flags & HL_CS_FLAGS_STAGED_SUBMISSION) &&
!hdev->supports_staged_submission) {
@@ -1276,9 +1381,10 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args)
cs_type = hl_cs_get_cs_type(cs_type_flags);
num_chunks = args->in.num_chunks_execute;
- if (unlikely((cs_type == CS_TYPE_SIGNAL || cs_type == CS_TYPE_WAIT ||
- cs_type == CS_TYPE_COLLECTIVE_WAIT) &&
- !hdev->supports_sync_stream)) {
+ is_sync_stream = (cs_type == CS_TYPE_SIGNAL || cs_type == CS_TYPE_WAIT ||
+ cs_type == CS_TYPE_COLLECTIVE_WAIT);
+
+ if (unlikely(is_sync_stream && !hdev->supports_sync_stream)) {
dev_err(hdev->dev, "Sync stream CS is not supported\n");
return -EINVAL;
}
@@ -1288,7 +1394,7 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args)
dev_err(hdev->dev, "Got execute CS with 0 chunks, context %d\n", ctx->asid);
return -EINVAL;
}
- } else if (num_chunks != 1) {
+ } else if (is_sync_stream && num_chunks != 1) {
dev_err(hdev->dev,
"Sync stream CS mandates one chunk only, context %d\n",
ctx->asid);
@@ -1584,13 +1690,14 @@ static int hl_cs_ctx_switch(struct hl_fpriv *hpriv, union hl_cs_args *args,
struct hl_device *hdev = hpriv->hdev;
struct hl_ctx *ctx = hpriv->ctx;
bool need_soft_reset = false;
- int rc = 0, do_ctx_switch;
+ int rc = 0, do_ctx_switch = 0;
void __user *chunks;
u32 num_chunks, tmp;
u16 sob_count;
int ret;
- do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0);
+ if (hdev->supports_ctx_switch)
+ do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0);
if (do_ctx_switch || (args->in.cs_flags & HL_CS_FLAGS_FORCE_RESTORE)) {
mutex_lock(&hpriv->restore_phase_mutex);
@@ -1643,16 +1750,11 @@ static int hl_cs_ctx_switch(struct hl_fpriv *hpriv, union hl_cs_args *args,
/* Need to wait for restore completion before execution phase */
if (num_chunks) {
enum hl_cs_wait_status status;
-wait_again:
+
ret = _hl_cs_wait_ioctl(hdev, ctx,
jiffies_to_usecs(hdev->timeout_jiffies),
*cs_seq, &status, NULL);
if (ret) {
- if (ret == -ERESTARTSYS) {
- usleep_range(100, 200);
- goto wait_again;
- }
-
dev_err(hdev->dev,
"Restore CS for context %d failed to complete %d\n",
ctx->asid, ret);
@@ -1661,9 +1763,10 @@ wait_again:
}
}
- ctx->thread_ctx_switch_wait_token = 1;
+ if (hdev->supports_ctx_switch)
+ ctx->thread_ctx_switch_wait_token = 1;
- } else if (!ctx->thread_ctx_switch_wait_token) {
+ } else if (hdev->supports_ctx_switch && !ctx->thread_ctx_switch_wait_token) {
rc = hl_poll_timeout_memory(hdev,
&ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1),
100, jiffies_to_usecs(hdev->timeout_jiffies), false);
@@ -1856,8 +1959,7 @@ static int cs_ioctl_signal_wait_create_jobs(struct hl_device *hdev,
else
cb_size = hdev->asic_funcs->get_signal_cb_size(hdev);
- cb = hl_cb_kernel_create(hdev, cb_size,
- q_type == QUEUE_TYPE_HW && hdev->mmu_enable);
+ cb = hl_cb_kernel_create(hdev, cb_size, q_type == QUEUE_TYPE_HW);
if (!cb) {
atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
atomic64_inc(&cntr->out_of_mem_drop_cnt);
@@ -1993,6 +2095,8 @@ static int cs_ioctl_reserve_signals(struct hl_fpriv *hpriv,
*/
handle->pre_sob_val = prop->next_sob_val - handle->count;
+ handle->cs_seq = ULLONG_MAX;
+
*signals_count = prop->next_sob_val;
hdev->asic_funcs->hw_queues_unlock(hdev);
@@ -2058,7 +2162,7 @@ static int cs_ioctl_unreserve_signals(struct hl_fpriv *hpriv, u32 handle_id)
hdev->asic_funcs->hw_queues_unlock(hdev);
rc = -EINVAL;
- goto out;
+ goto out_unlock;
}
/*
@@ -2073,15 +2177,21 @@ static int cs_ioctl_unreserve_signals(struct hl_fpriv *hpriv, u32 handle_id)
/* Release the id and free allocated memory of the handle */
idr_remove(&mgr->handles, handle_id);
+
+ /* unlock before calling ctx_put, where we might sleep */
+ spin_unlock(&mgr->lock);
hl_ctx_put(encaps_sig_hdl->ctx);
kfree(encaps_sig_hdl);
+ goto out;
} else {
rc = -EINVAL;
dev_err(hdev->dev, "failed to unreserve signals, cannot find handler\n");
}
-out:
+
+out_unlock:
spin_unlock(&mgr->lock);
+out:
return rc;
}
@@ -2342,17 +2452,111 @@ put_cs:
/* We finished with the CS in this function, so put the ref */
cs_put(cs);
free_cs_chunk_array:
- if (!wait_cs_submitted && cs_encaps_signals && handle_found &&
- is_wait_cs)
- kref_put(&encaps_sig_hdl->refcount,
- hl_encaps_handle_do_release);
+ if (!wait_cs_submitted && cs_encaps_signals && handle_found && is_wait_cs)
+ kref_put(&encaps_sig_hdl->refcount, hl_encaps_release_handle_and_put_ctx);
kfree(cs_chunk_array);
out:
return rc;
}
-int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data)
+static int cs_ioctl_engine_cores(struct hl_fpriv *hpriv, u64 engine_cores,
+ u32 num_engine_cores, u32 core_command)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ void __user *engine_cores_arr;
+ u32 *cores;
+ int rc;
+
+ if (!hdev->asic_prop.supports_engine_modes)
+ return -EPERM;
+
+ if (!num_engine_cores || num_engine_cores > hdev->asic_prop.num_engine_cores) {
+ dev_err(hdev->dev, "Number of engine cores %d is invalid\n", num_engine_cores);
+ return -EINVAL;
+ }
+
+ if (core_command != HL_ENGINE_CORE_RUN && core_command != HL_ENGINE_CORE_HALT) {
+ dev_err(hdev->dev, "Engine core command is invalid\n");
+ return -EINVAL;
+ }
+
+ engine_cores_arr = (void __user *) (uintptr_t) engine_cores;
+ cores = kmalloc_array(num_engine_cores, sizeof(u32), GFP_KERNEL);
+ if (!cores)
+ return -ENOMEM;
+
+ if (copy_from_user(cores, engine_cores_arr, num_engine_cores * sizeof(u32))) {
+ dev_err(hdev->dev, "Failed to copy core-ids array from user\n");
+ kfree(cores);
+ return -EFAULT;
+ }
+
+ rc = hdev->asic_funcs->set_engine_cores(hdev, cores, num_engine_cores, core_command);
+ kfree(cores);
+
+ return rc;
+}
+
+static int cs_ioctl_engines(struct hl_fpriv *hpriv, u64 engines_arr_user_addr,
+ u32 num_engines, enum hl_engine_command command)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ u32 *engines, max_num_of_engines;
+ void __user *engines_arr;
+ int rc;
+
+ if (!hdev->asic_prop.supports_engine_modes)
+ return -EPERM;
+
+ if (command >= HL_ENGINE_COMMAND_MAX) {
+ dev_err(hdev->dev, "Engine command is invalid\n");
+ return -EINVAL;
+ }
+
+ max_num_of_engines = hdev->asic_prop.max_num_of_engines;
+ if (command == HL_ENGINE_CORE_RUN || command == HL_ENGINE_CORE_HALT)
+ max_num_of_engines = hdev->asic_prop.num_engine_cores;
+
+ if (!num_engines || num_engines > max_num_of_engines) {
+ dev_err(hdev->dev, "Number of engines %d is invalid\n", num_engines);
+ return -EINVAL;
+ }
+
+ engines_arr = (void __user *) (uintptr_t) engines_arr_user_addr;
+ engines = kmalloc_array(num_engines, sizeof(u32), GFP_KERNEL);
+ if (!engines)
+ return -ENOMEM;
+
+ if (copy_from_user(engines, engines_arr, num_engines * sizeof(u32))) {
+ dev_err(hdev->dev, "Failed to copy engine-ids array from user\n");
+ kfree(engines);
+ return -EFAULT;
+ }
+
+ rc = hdev->asic_funcs->set_engines(hdev, engines, num_engines, command);
+ kfree(engines);
+
+ return rc;
+}
+
+static int cs_ioctl_flush_pci_hbw_writes(struct hl_fpriv *hpriv)
{
+ struct hl_device *hdev = hpriv->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ if (!prop->hbw_flush_reg) {
+ dev_dbg(hdev->dev, "HBW flush is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ RREG32(prop->hbw_flush_reg);
+
+ return 0;
+}
+
+int hl_cs_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
+{
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
union hl_cs_args *args = data;
enum hl_cs_type cs_type = 0;
u64 cs_seq = ULONG_MAX;
@@ -2382,7 +2586,7 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data)
cs_seq = args->in.seq;
timeout = flags & HL_CS_FLAGS_CUSTOM_TIMEOUT
- ? msecs_to_jiffies(args->in.timeout * 1000)
+ ? secs_to_jiffies(args->in.timeout)
: hpriv->hdev->timeout_jiffies;
switch (cs_type) {
@@ -2403,6 +2607,17 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data)
rc = cs_ioctl_unreserve_signals(hpriv,
args->in.encaps_sig_handle_id);
break;
+ case CS_TYPE_ENGINE_CORE:
+ rc = cs_ioctl_engine_cores(hpriv, args->in.engine_cores,
+ args->in.num_engine_cores, args->in.core_command);
+ break;
+ case CS_TYPE_ENGINES:
+ rc = cs_ioctl_engines(hpriv, args->in.engines,
+ args->in.num_engines, args->in.engine_command);
+ break;
+ case CS_TYPE_FLUSH_PCI_HBW_WRITES:
+ rc = cs_ioctl_flush_pci_hbw_writes(hpriv);
+ break;
default:
rc = cs_ioctl_default(hpriv, chunks, num_chunks, &cs_seq,
args->in.cs_flags,
@@ -2494,7 +2709,9 @@ report_results:
*status = CS_WAIT_STATUS_BUSY;
}
- if (error == -ETIMEDOUT || error == -EIO)
+ if (completion_rc == -ERESTARTSYS)
+ rc = completion_rc;
+ else if (error == -ETIMEDOUT || error == -EIO)
rc = error;
return rc;
@@ -2524,7 +2741,7 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data, struct multi_cs_com
ktime_t max_ktime, first_cs_time;
enum hl_cs_wait_status status;
- memset(fence_ptr, 0, arr_len * sizeof(*fence_ptr));
+ memset(fence_ptr, 0, arr_len * sizeof(struct hl_fence *));
/* get all fences under the same lock */
rc = hl_ctx_get_fences(mcs_data->ctx, seq_arr, fence_ptr, arr_len);
@@ -2624,7 +2841,8 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data, struct multi_cs_com
break;
default:
dev_err(hdev->dev, "Invalid fence status\n");
- return -EINVAL;
+ rc = -EINVAL;
+ break;
}
}
@@ -2753,6 +2971,9 @@ static int hl_wait_multi_cs_completion(struct multi_cs_data *mcs_data,
if (completion_rc > 0)
mcs_data->timestamp = mcs_compl->timestamp;
+ if (completion_rc == -ERESTARTSYS)
+ return completion_rc;
+
mcs_data->wait_status = completion_rc;
return 0;
@@ -2795,7 +3016,13 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
u32 size_to_copy;
u64 *cs_seq_arr;
u8 seq_arr_len;
- int rc;
+ int rc, i;
+
+ for (i = 0 ; i < sizeof(args->in.pad) ; i++)
+ if (args->in.pad[i]) {
+ dev_dbg(hdev->dev, "Padding bytes must be 0\n");
+ return -EINVAL;
+ }
if (!hdev->supports_wait_for_multi_cs) {
dev_err(hdev->dev, "Wait for multi CS is not supported\n");
@@ -2826,7 +3053,7 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
}
/* allocate array for the fences */
- fence_arr = kmalloc_array(seq_arr_len, sizeof(*fence_arr), GFP_KERNEL);
+ fence_arr = kmalloc_array(seq_arr_len, sizeof(struct hl_fence *), GFP_KERNEL);
if (!fence_arr) {
rc = -ENOMEM;
goto free_seq_arr;
@@ -2898,15 +3125,15 @@ put_ctx:
free_seq_arr:
kfree(cs_seq_arr);
- if (rc)
- return rc;
-
- if (mcs_data.wait_status == -ERESTARTSYS) {
+ if (rc == -ERESTARTSYS) {
dev_err_ratelimited(hdev->dev,
"user process got signal while waiting for Multi-CS\n");
- return -EINTR;
+ rc = -EINTR;
}
+ if (rc)
+ return rc;
+
/* update output args */
memset(args, 0, sizeof(*args));
@@ -2986,163 +3213,228 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
return 0;
}
-static int ts_buff_get_kernel_ts_record(struct hl_mmap_mem_buf *buf,
- struct hl_cb *cq_cb,
- u64 ts_offset, u64 cq_offset, u64 target_value,
- spinlock_t *wait_list_lock,
- struct hl_user_pending_interrupt **pend)
+static inline void set_record_cq_info(struct hl_user_pending_interrupt *record,
+ struct hl_cb *cq_cb, u32 cq_offset, u32 target_value)
{
- struct hl_ts_buff *ts_buff = buf->private;
- struct hl_user_pending_interrupt *requested_offset_record =
- (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
- ts_offset;
- struct hl_user_pending_interrupt *cb_last =
- (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
+ record->ts_reg_info.cq_cb = cq_cb;
+ record->cq_kernel_addr = (u64 *) cq_cb->kernel_address + cq_offset;
+ record->cq_target_value = target_value;
+}
+
+static int validate_and_get_ts_record(struct device *dev,
+ struct hl_ts_buff *ts_buff, u64 ts_offset,
+ struct hl_user_pending_interrupt **req_event_record)
+{
+ struct hl_user_pending_interrupt *ts_cb_last;
+
+ *req_event_record = (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
+ ts_offset;
+ ts_cb_last = (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
(ts_buff->kernel_buff_size / sizeof(struct hl_user_pending_interrupt));
- unsigned long flags, iter_counter = 0;
- u64 current_cq_counter;
/* Validate ts_offset not exceeding last max */
- if (requested_offset_record >= cb_last) {
- dev_err(buf->mmg->dev, "Ts offset exceeds max CB offset(0x%llx)\n",
- (u64)(uintptr_t)cb_last);
+ if (*req_event_record >= ts_cb_last) {
+ dev_err(dev, "Ts offset(%llu) exceeds max CB offset(0x%llx)\n",
+ ts_offset, (u64)(uintptr_t)ts_cb_last);
return -EINVAL;
}
-start_over:
- spin_lock_irqsave(wait_list_lock, flags);
+ return 0;
+}
- /* Unregister only if we didn't reach the target value
- * since in this case there will be no handling in irq context
- * and then it's safe to delete the node out of the interrupt list
- * then re-use it on other interrupt
- */
- if (requested_offset_record->ts_reg_info.in_use) {
- current_cq_counter = *requested_offset_record->cq_kernel_addr;
- if (current_cq_counter < requested_offset_record->cq_target_value) {
- list_del(&requested_offset_record->wait_list_node);
- spin_unlock_irqrestore(wait_list_lock, flags);
+static void unregister_timestamp_node(struct hl_device *hdev,
+ struct hl_user_pending_interrupt *record, bool need_lock)
+{
+ struct hl_user_interrupt *interrupt = record->ts_reg_info.interrupt;
+ bool ts_rec_found = false;
+ unsigned long flags;
- hl_mmap_mem_buf_put(requested_offset_record->ts_reg_info.buf);
- hl_cb_put(requested_offset_record->ts_reg_info.cq_cb);
+ if (need_lock)
+ spin_lock_irqsave(&interrupt->ts_list_lock, flags);
- dev_dbg(buf->mmg->dev,
- "ts node removed from interrupt list now can re-use\n");
- } else {
- dev_dbg(buf->mmg->dev,
- "ts node in middle of irq handling\n");
-
- /* irq handling in the middle give it time to finish */
- spin_unlock_irqrestore(wait_list_lock, flags);
- usleep_range(1, 10);
- if (++iter_counter == MAX_TS_ITER_NUM) {
- dev_err(buf->mmg->dev,
- "handling registration interrupt took too long!!\n");
- return -EINVAL;
- }
+ if (record->ts_reg_info.in_use) {
+ record->ts_reg_info.in_use = false;
+ list_del(&record->list_node);
+ ts_rec_found = true;
+ }
+
+ if (need_lock)
+ spin_unlock_irqrestore(&interrupt->ts_list_lock, flags);
+
+ /* Put refcounts that were taken when we registered the event */
+ if (ts_rec_found) {
+ hl_mmap_mem_buf_put(record->ts_reg_info.buf);
+ hl_cb_put(record->ts_reg_info.cq_cb);
+ }
+}
+
+static int ts_get_and_handle_kernel_record(struct hl_device *hdev, struct hl_ctx *ctx,
+ struct wait_interrupt_data *data, unsigned long *flags,
+ struct hl_user_pending_interrupt **pend)
+{
+ struct hl_user_pending_interrupt *req_offset_record;
+ struct hl_ts_buff *ts_buff = data->buf->private;
+ bool need_lock = false;
+ int rc;
+
+ rc = validate_and_get_ts_record(data->buf->mmg->dev, ts_buff, data->ts_offset,
+ &req_offset_record);
+ if (rc)
+ return rc;
- goto start_over;
+ /* In case the node already registered, need to unregister first then re-use */
+ if (req_offset_record->ts_reg_info.in_use) {
+ /*
+ * Since interrupt here can be different than the one the node currently registered
+ * on, and we don't want to lock two lists while we're doing unregister, so
+ * unlock the new interrupt wait list here and acquire the lock again after you done
+ */
+ if (data->interrupt->interrupt_id !=
+ req_offset_record->ts_reg_info.interrupt->interrupt_id) {
+
+ need_lock = true;
+ spin_unlock_irqrestore(&data->interrupt->ts_list_lock, *flags);
}
- } else {
- spin_unlock_irqrestore(wait_list_lock, flags);
+
+ unregister_timestamp_node(hdev, req_offset_record, need_lock);
+
+ if (need_lock)
+ spin_lock_irqsave(&data->interrupt->ts_list_lock, *flags);
}
- /* Fill up the new registration node info */
- requested_offset_record->ts_reg_info.in_use = 1;
- requested_offset_record->ts_reg_info.buf = buf;
- requested_offset_record->ts_reg_info.cq_cb = cq_cb;
- requested_offset_record->ts_reg_info.timestamp_kernel_addr =
- (u64 *) ts_buff->user_buff_address + ts_offset;
- requested_offset_record->cq_kernel_addr =
- (u64 *) cq_cb->kernel_address + cq_offset;
- requested_offset_record->cq_target_value = target_value;
+ /* Fill up the new registration node info and add it to the list */
+ req_offset_record->ts_reg_info.in_use = true;
+ req_offset_record->ts_reg_info.buf = data->buf;
+ req_offset_record->ts_reg_info.timestamp_kernel_addr =
+ (u64 *) ts_buff->user_buff_address + data->ts_offset;
+ req_offset_record->ts_reg_info.interrupt = data->interrupt;
+ set_record_cq_info(req_offset_record, data->cq_cb, data->cq_offset,
+ data->target_value);
- *pend = requested_offset_record;
+ *pend = req_offset_record;
- dev_dbg(buf->mmg->dev, "Found available node in TS kernel CB %p\n",
- requested_offset_record);
- return 0;
+ return rc;
+}
+
+static int _hl_interrupt_ts_reg_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
+ struct wait_interrupt_data *data,
+ u32 *status, u64 *timestamp)
+{
+ struct hl_user_pending_interrupt *pend;
+ unsigned long flags;
+ int rc = 0;
+
+ hl_ctx_get(ctx);
+
+ data->cq_cb = hl_cb_get(data->mmg, data->cq_handle);
+ if (!data->cq_cb) {
+ rc = -EINVAL;
+ goto put_ctx;
+ }
+
+ /* Validate the cq offset */
+ if (((u64 *) data->cq_cb->kernel_address + data->cq_offset) >=
+ ((u64 *) data->cq_cb->kernel_address + (data->cq_cb->size / sizeof(u64)))) {
+ rc = -EINVAL;
+ goto put_cq_cb;
+ }
+
+ data->buf = hl_mmap_mem_buf_get(data->mmg, data->ts_handle);
+ if (!data->buf) {
+ rc = -EINVAL;
+ goto put_cq_cb;
+ }
+
+ spin_lock_irqsave(&data->interrupt->ts_list_lock, flags);
+
+ /* get ts buffer record */
+ rc = ts_get_and_handle_kernel_record(hdev, ctx, data, &flags, &pend);
+ if (rc) {
+ spin_unlock_irqrestore(&data->interrupt->ts_list_lock, flags);
+ goto put_ts_buff;
+ }
+
+ /* We check for completion value as interrupt could have been received
+ * before we add the timestamp node to the ts list.
+ */
+ if (*pend->cq_kernel_addr >= data->target_value) {
+ spin_unlock_irqrestore(&data->interrupt->ts_list_lock, flags);
+
+ pend->ts_reg_info.in_use = 0;
+ *status = HL_WAIT_CS_STATUS_COMPLETED;
+ *pend->ts_reg_info.timestamp_kernel_addr = ktime_get_ns();
+
+ goto put_ts_buff;
+ }
+
+ list_add_tail(&pend->list_node, &data->interrupt->ts_list_head);
+ spin_unlock_irqrestore(&data->interrupt->ts_list_lock, flags);
+
+ rc = *status = HL_WAIT_CS_STATUS_COMPLETED;
+
+ hl_ctx_put(ctx);
+
+ return rc;
+
+put_ts_buff:
+ hl_mmap_mem_buf_put(data->buf);
+put_cq_cb:
+ hl_cb_put(data->cq_cb);
+put_ctx:
+ hl_ctx_put(ctx);
+
+ return rc;
}
static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
- struct hl_mem_mgr *cb_mmg, struct hl_mem_mgr *mmg,
- u64 timeout_us, u64 cq_counters_handle, u64 cq_counters_offset,
- u64 target_value, struct hl_user_interrupt *interrupt,
- bool register_ts_record, u64 ts_handle, u64 ts_offset,
+ struct wait_interrupt_data *data,
u32 *status, u64 *timestamp)
{
struct hl_user_pending_interrupt *pend;
- struct hl_mmap_mem_buf *buf;
- struct hl_cb *cq_cb;
unsigned long timeout, flags;
long completion_rc;
int rc = 0;
- timeout = hl_usecs64_to_jiffies(timeout_us);
+ timeout = hl_usecs64_to_jiffies(data->intr_timeout_us);
hl_ctx_get(ctx);
- cq_cb = hl_cb_get(cb_mmg, cq_counters_handle);
- if (!cq_cb) {
+ data->cq_cb = hl_cb_get(data->mmg, data->cq_handle);
+ if (!data->cq_cb) {
rc = -EINVAL;
goto put_ctx;
}
/* Validate the cq offset */
- if (((u64 *) cq_cb->kernel_address + cq_counters_offset) >=
- ((u64 *) cq_cb->kernel_address + (cq_cb->size / sizeof(u64)))) {
+ if (((u64 *) data->cq_cb->kernel_address + data->cq_offset) >=
+ ((u64 *) data->cq_cb->kernel_address + (data->cq_cb->size / sizeof(u64)))) {
rc = -EINVAL;
goto put_cq_cb;
}
- if (register_ts_record) {
- dev_dbg(hdev->dev, "Timestamp registration: interrupt id: %u, ts offset: %llu, cq_offset: %llu\n",
- interrupt->interrupt_id, ts_offset, cq_counters_offset);
- buf = hl_mmap_mem_buf_get(mmg, ts_handle);
- if (!buf) {
- rc = -EINVAL;
- goto put_cq_cb;
- }
-
- /* Find first available record */
- rc = ts_buff_get_kernel_ts_record(buf, cq_cb, ts_offset,
- cq_counters_offset, target_value,
- &interrupt->wait_list_lock, &pend);
- if (rc)
- goto put_ts_buff;
- } else {
- pend = kzalloc(sizeof(*pend), GFP_KERNEL);
- if (!pend) {
- rc = -ENOMEM;
- goto put_cq_cb;
- }
- hl_fence_init(&pend->fence, ULONG_MAX);
- pend->cq_kernel_addr = (u64 *) cq_cb->kernel_address + cq_counters_offset;
- pend->cq_target_value = target_value;
+ pend = kzalloc(sizeof(*pend), GFP_KERNEL);
+ if (!pend) {
+ rc = -ENOMEM;
+ goto put_cq_cb;
}
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
+ hl_fence_init(&pend->fence, ULONG_MAX);
+ pend->cq_kernel_addr = (u64 *) data->cq_cb->kernel_address + data->cq_offset;
+ pend->cq_target_value = data->target_value;
+ spin_lock_irqsave(&data->interrupt->wait_list_lock, flags);
+
/* We check for completion value as interrupt could have been received
- * before we added the node to the wait list
+ * before we add the wait node to the wait list.
*/
- if (*pend->cq_kernel_addr >= target_value) {
- if (register_ts_record)
- pend->ts_reg_info.in_use = 0;
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
+ if (*pend->cq_kernel_addr >= data->target_value || (!data->intr_timeout_us)) {
+ spin_unlock_irqrestore(&data->interrupt->wait_list_lock, flags);
- *status = HL_WAIT_CS_STATUS_COMPLETED;
+ if (*pend->cq_kernel_addr >= data->target_value)
+ *status = HL_WAIT_CS_STATUS_COMPLETED;
+ else
+ *status = HL_WAIT_CS_STATUS_BUSY;
- if (register_ts_record) {
- *pend->ts_reg_info.timestamp_kernel_addr = ktime_get_ns();
- goto put_ts_buff;
- } else {
- pend->fence.timestamp = ktime_get();
- goto set_timestamp;
- }
- } else if (!timeout_us) {
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
- *status = HL_WAIT_CS_STATUS_BUSY;
pend->fence.timestamp = ktime_get();
goto set_timestamp;
}
@@ -3153,42 +3445,37 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
* in order to shorten the list pass loop, since
* same list could have nodes for different cq counter handle.
*/
- list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
- if (register_ts_record) {
- rc = *status = HL_WAIT_CS_STATUS_COMPLETED;
- goto ts_registration_exit;
- }
+ list_add_tail(&pend->list_node, &data->interrupt->wait_list_head);
+ spin_unlock_irqrestore(&data->interrupt->wait_list_lock, flags);
/* Wait for interrupt handler to signal completion */
completion_rc = wait_for_completion_interruptible_timeout(&pend->fence.completion,
timeout);
if (completion_rc > 0) {
- *status = HL_WAIT_CS_STATUS_COMPLETED;
+ if (pend->fence.error == -EIO) {
+ dev_err_ratelimited(hdev->dev,
+ "interrupt based wait ioctl aborted(error:%d) due to a reset cycle initiated\n",
+ pend->fence.error);
+ rc = -EIO;
+ *status = HL_WAIT_CS_STATUS_ABORTED;
+ } else {
+ *status = HL_WAIT_CS_STATUS_COMPLETED;
+ }
} else {
if (completion_rc == -ERESTARTSYS) {
dev_err_ratelimited(hdev->dev,
"user process got signal while waiting for interrupt ID %d\n",
- interrupt->interrupt_id);
+ data->interrupt->interrupt_id);
rc = -EINTR;
*status = HL_WAIT_CS_STATUS_ABORTED;
} else {
- if (pend->fence.error == -EIO) {
- dev_err_ratelimited(hdev->dev,
- "interrupt based wait ioctl aborted(error:%d) due to a reset cycle initiated\n",
- pend->fence.error);
- rc = -EIO;
- *status = HL_WAIT_CS_STATUS_ABORTED;
- } else {
- /* The wait has timed-out. We don't know anything beyond that
- * because the workload wasn't submitted through the driver.
- * Therefore, from driver's perspective, the workload is still
- * executing.
- */
- rc = 0;
- *status = HL_WAIT_CS_STATUS_BUSY;
- }
+ /* The wait has timed-out. We don't know anything beyond that
+ * because the workload was not submitted through the driver.
+ * Therefore, from driver's perspective, the workload is still
+ * executing.
+ */
+ rc = 0;
+ *status = HL_WAIT_CS_STATUS_BUSY;
}
}
@@ -3198,23 +3485,20 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
* for ts record, the node will be deleted in the irq handler after
* we reach the target value.
*/
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_del(&pend->wait_list_node);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
+ spin_lock_irqsave(&data->interrupt->wait_list_lock, flags);
+ list_del(&pend->list_node);
+ spin_unlock_irqrestore(&data->interrupt->wait_list_lock, flags);
set_timestamp:
*timestamp = ktime_to_ns(pend->fence.timestamp);
kfree(pend);
- hl_cb_put(cq_cb);
-ts_registration_exit:
+ hl_cb_put(data->cq_cb);
hl_ctx_put(ctx);
return rc;
-put_ts_buff:
- hl_mmap_mem_buf_put(buf);
put_cq_cb:
- hl_cb_put(cq_cb);
+ hl_cb_put(data->cq_cb);
put_ctx:
hl_ctx_put(ctx);
@@ -3249,7 +3533,7 @@ static int _hl_interrupt_wait_ioctl_user_addr(struct hl_device *hdev, struct hl_
* handler to monitor
*/
spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head);
+ list_add_tail(&pend->list_node, &interrupt->wait_list_head);
spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
/* We check for completion value as interrupt could have been received
@@ -3326,7 +3610,7 @@ wait_again:
remove_pending_user_interrupt:
spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_del(&pend->wait_list_node);
+ list_del(&pend->list_node);
spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
*timestamp = ktime_to_ns(pend->fence.timestamp);
@@ -3385,19 +3669,42 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data)
return -EINVAL;
}
- if (args->in.flags & HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ)
- rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->mem_mgr, &hpriv->mem_mgr,
- args->in.interrupt_timeout_us, args->in.cq_counters_handle,
- args->in.cq_counters_offset,
- args->in.target, interrupt,
- !!(args->in.flags & HL_WAIT_CS_FLAGS_REGISTER_INTERRUPT),
- args->in.timestamp_handle, args->in.timestamp_offset,
- &status, &timestamp);
- else
+ if (args->in.flags & HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ) {
+ struct wait_interrupt_data wait_intr_data = {0};
+
+ wait_intr_data.interrupt = interrupt;
+ wait_intr_data.mmg = &hpriv->mem_mgr;
+ wait_intr_data.cq_handle = args->in.cq_counters_handle;
+ wait_intr_data.cq_offset = args->in.cq_counters_offset;
+ wait_intr_data.ts_handle = args->in.timestamp_handle;
+ wait_intr_data.ts_offset = args->in.timestamp_offset;
+ wait_intr_data.target_value = args->in.target;
+ wait_intr_data.intr_timeout_us = args->in.interrupt_timeout_us;
+
+ if (args->in.flags & HL_WAIT_CS_FLAGS_REGISTER_INTERRUPT) {
+ /*
+ * Allow only one registration at a time. this is needed in order to prevent
+ * issues while handling the flow of re-use of the same offset.
+ * Since the registration flow is protected only by the interrupt lock,
+ * re-use flow might request to move ts node to another interrupt list,
+ * and in such case we're not protected.
+ */
+ mutex_lock(&hpriv->ctx->ts_reg_lock);
+
+ rc = _hl_interrupt_ts_reg_ioctl(hdev, hpriv->ctx, &wait_intr_data,
+ &status, &timestamp);
+
+ mutex_unlock(&hpriv->ctx->ts_reg_lock);
+ } else
+ rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &wait_intr_data,
+ &status, &timestamp);
+ } else {
rc = _hl_interrupt_wait_ioctl_user_addr(hdev, hpriv->ctx,
args->in.interrupt_timeout_us, args->in.addr,
args->in.target, interrupt, &status,
&timestamp);
+ }
+
if (rc)
return rc;
@@ -3412,16 +3719,18 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data)
return 0;
}
-int hl_wait_ioctl(struct hl_fpriv *hpriv, void *data)
+int hl_wait_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
{
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
+ struct hl_device *hdev = hpriv->hdev;
union hl_wait_cs_args *args = data;
u32 flags = args->in.flags;
int rc;
- /* If the device is not operational, no point in waiting for any command submission or
- * user interrupt
+ /* If the device is not operational, or if an error has happened and user should release the
+ * device, there is no point in waiting for any command submission or user interrupt.
*/
- if (!hl_device_operational(hpriv->hdev, NULL))
+ if (!hl_device_operational(hpriv->hdev, NULL) || hdev->reset_info.watchdog_active)
return -EBUSY;
if (flags & HL_WAIT_CS_FLAGS_INTERRUPT)
diff --git a/drivers/accel/habanalabs/common/context.c b/drivers/accel/habanalabs/common/context.c
new file mode 100644
index 000000000000..9f212b17611a
--- /dev/null
+++ b/drivers/accel/habanalabs/common/context.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2021 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+
+#include <linux/slab.h>
+
+static void encaps_handle_do_release(struct hl_cs_encaps_sig_handle *handle, bool put_hw_sob,
+ bool put_ctx)
+{
+ struct hl_encaps_signals_mgr *mgr = &handle->ctx->sig_mgr;
+
+ if (put_hw_sob)
+ hw_sob_put(handle->hw_sob);
+
+ spin_lock(&mgr->lock);
+ idr_remove(&mgr->handles, handle->id);
+ spin_unlock(&mgr->lock);
+
+ if (put_ctx)
+ hl_ctx_put(handle->ctx);
+
+ kfree(handle);
+}
+
+void hl_encaps_release_handle_and_put_ctx(struct kref *ref)
+{
+ struct hl_cs_encaps_sig_handle *handle =
+ container_of(ref, struct hl_cs_encaps_sig_handle, refcount);
+
+ encaps_handle_do_release(handle, false, true);
+}
+
+static void hl_encaps_release_handle_and_put_sob(struct kref *ref)
+{
+ struct hl_cs_encaps_sig_handle *handle =
+ container_of(ref, struct hl_cs_encaps_sig_handle, refcount);
+
+ encaps_handle_do_release(handle, true, false);
+}
+
+void hl_encaps_release_handle_and_put_sob_ctx(struct kref *ref)
+{
+ struct hl_cs_encaps_sig_handle *handle =
+ container_of(ref, struct hl_cs_encaps_sig_handle, refcount);
+
+ encaps_handle_do_release(handle, true, true);
+}
+
+static void hl_encaps_sig_mgr_init(struct hl_encaps_signals_mgr *mgr)
+{
+ spin_lock_init(&mgr->lock);
+ idr_init(&mgr->handles);
+}
+
+static void hl_encaps_sig_mgr_fini(struct hl_device *hdev, struct hl_encaps_signals_mgr *mgr)
+{
+ struct hl_cs_encaps_sig_handle *handle;
+ struct idr *idp;
+ u32 id;
+
+ idp = &mgr->handles;
+
+ /* The IDR is expected to be empty at this stage, because any left signal should have been
+ * released as part of CS roll-back.
+ */
+ if (!idr_is_empty(idp)) {
+ dev_warn(hdev->dev,
+ "device released while some encaps signals handles are still allocated\n");
+ idr_for_each_entry(idp, handle, id)
+ kref_put(&handle->refcount, hl_encaps_release_handle_and_put_sob);
+ }
+
+ idr_destroy(&mgr->handles);
+}
+
+static void hl_ctx_fini(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ int i;
+
+ /* Release all allocated HW block mapped list entries and destroy
+ * the mutex.
+ */
+ hl_hw_block_mem_fini(ctx);
+
+ /*
+ * If we arrived here, there are no jobs waiting for this context
+ * on its queues so we can safely remove it.
+ * This is because for each CS, we increment the ref count and for
+ * every CS that was finished we decrement it and we won't arrive
+ * to this function unless the ref count is 0
+ */
+
+ for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++)
+ hl_fence_put(ctx->cs_pending[i]);
+
+ kfree(ctx->cs_pending);
+
+ if (ctx->asid != HL_KERNEL_ASID_ID) {
+ dev_dbg(hdev->dev, "closing user context, asid=%u\n", ctx->asid);
+
+ /* The engines are stopped as there is no executing CS, but the
+ * Coresight might be still working by accessing addresses
+ * related to the stopped engines. Hence stop it explicitly.
+ */
+ if (hdev->in_debug)
+ hl_device_set_debug_mode(hdev, ctx, false);
+
+ hdev->asic_funcs->ctx_fini(ctx);
+
+ hl_dec_ctx_fini(ctx);
+
+ hl_cb_va_pool_fini(ctx);
+ hl_vm_ctx_fini(ctx);
+ hl_asid_free(hdev, ctx->asid);
+ hl_encaps_sig_mgr_fini(hdev, &ctx->sig_mgr);
+ mutex_destroy(&ctx->ts_reg_lock);
+ } else {
+ dev_dbg(hdev->dev, "closing kernel context\n");
+ hdev->asic_funcs->ctx_fini(ctx);
+ hl_vm_ctx_fini(ctx);
+ hl_mmu_ctx_fini(ctx);
+ }
+}
+
+void hl_ctx_do_release(struct kref *ref)
+{
+ struct hl_ctx *ctx;
+
+ ctx = container_of(ref, struct hl_ctx, refcount);
+
+ hl_ctx_fini(ctx);
+
+ if (ctx->hpriv) {
+ struct hl_fpriv *hpriv = ctx->hpriv;
+
+ mutex_lock(&hpriv->ctx_lock);
+ hpriv->ctx = NULL;
+ mutex_unlock(&hpriv->ctx_lock);
+
+ hl_hpriv_put(hpriv);
+ }
+
+ kfree(ctx);
+}
+
+int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv)
+{
+ struct hl_ctx_mgr *ctx_mgr = &hpriv->ctx_mgr;
+ struct hl_ctx *ctx;
+ int rc;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+
+ mutex_lock(&ctx_mgr->lock);
+ rc = idr_alloc(&ctx_mgr->handles, ctx, 1, 0, GFP_KERNEL);
+ mutex_unlock(&ctx_mgr->lock);
+
+ if (rc < 0) {
+ dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n");
+ goto free_ctx;
+ }
+
+ ctx->handle = rc;
+
+ rc = hl_ctx_init(hdev, ctx, false);
+ if (rc)
+ goto remove_from_idr;
+
+ hl_hpriv_get(hpriv);
+ ctx->hpriv = hpriv;
+
+ /* TODO: remove for multiple contexts per process */
+ hpriv->ctx = ctx;
+
+ /* TODO: remove the following line for multiple process support */
+ hdev->is_compute_ctx_active = true;
+
+ return 0;
+
+remove_from_idr:
+ mutex_lock(&ctx_mgr->lock);
+ idr_remove(&ctx_mgr->handles, ctx->handle);
+ mutex_unlock(&ctx_mgr->lock);
+free_ctx:
+ kfree(ctx);
+out_err:
+ return rc;
+}
+
+int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx)
+{
+ int rc = 0, i;
+
+ ctx->hdev = hdev;
+
+ kref_init(&ctx->refcount);
+
+ ctx->cs_sequence = 1;
+ spin_lock_init(&ctx->cs_lock);
+ atomic_set(&ctx->thread_ctx_switch_token, 1);
+ ctx->thread_ctx_switch_wait_token = 0;
+ ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs,
+ sizeof(struct hl_fence *),
+ GFP_KERNEL);
+ if (!ctx->cs_pending)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&ctx->outcome_store.used_list);
+ INIT_LIST_HEAD(&ctx->outcome_store.free_list);
+ hash_init(ctx->outcome_store.outcome_map);
+ for (i = 0; i < ARRAY_SIZE(ctx->outcome_store.nodes_pool); ++i)
+ list_add(&ctx->outcome_store.nodes_pool[i].list_link,
+ &ctx->outcome_store.free_list);
+
+ hl_hw_block_mem_init(ctx);
+
+ if (is_kernel_ctx) {
+ ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */
+ rc = hl_vm_ctx_init(ctx);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to init mem ctx module\n");
+ rc = -ENOMEM;
+ goto err_hw_block_mem_fini;
+ }
+
+ rc = hdev->asic_funcs->ctx_init(ctx);
+ if (rc) {
+ dev_err(hdev->dev, "ctx_init failed\n");
+ goto err_vm_ctx_fini;
+ }
+ } else {
+ ctx->asid = hl_asid_alloc(hdev);
+ if (!ctx->asid) {
+ dev_err(hdev->dev, "No free ASID, failed to create context\n");
+ rc = -ENOMEM;
+ goto err_hw_block_mem_fini;
+ }
+
+ rc = hl_vm_ctx_init(ctx);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to init mem ctx module\n");
+ rc = -ENOMEM;
+ goto err_asid_free;
+ }
+
+ rc = hl_cb_va_pool_init(ctx);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to init VA pool for mapped CB\n");
+ goto err_vm_ctx_fini;
+ }
+
+ rc = hdev->asic_funcs->ctx_init(ctx);
+ if (rc) {
+ dev_err(hdev->dev, "ctx_init failed\n");
+ goto err_cb_va_pool_fini;
+ }
+
+ hl_encaps_sig_mgr_init(&ctx->sig_mgr);
+
+ mutex_init(&ctx->ts_reg_lock);
+
+ dev_dbg(hdev->dev, "create user context, comm=\"%s\", asid=%u\n",
+ current->comm, ctx->asid);
+ }
+
+ return 0;
+
+err_cb_va_pool_fini:
+ hl_cb_va_pool_fini(ctx);
+err_vm_ctx_fini:
+ hl_vm_ctx_fini(ctx);
+err_asid_free:
+ if (ctx->asid != HL_KERNEL_ASID_ID)
+ hl_asid_free(hdev, ctx->asid);
+err_hw_block_mem_fini:
+ hl_hw_block_mem_fini(ctx);
+ kfree(ctx->cs_pending);
+
+ return rc;
+}
+
+static int hl_ctx_get_unless_zero(struct hl_ctx *ctx)
+{
+ return kref_get_unless_zero(&ctx->refcount);
+}
+
+void hl_ctx_get(struct hl_ctx *ctx)
+{
+ kref_get(&ctx->refcount);
+}
+
+int hl_ctx_put(struct hl_ctx *ctx)
+{
+ return kref_put(&ctx->refcount, hl_ctx_do_release);
+}
+
+struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev)
+{
+ struct hl_ctx *ctx = NULL;
+ struct hl_fpriv *hpriv;
+
+ mutex_lock(&hdev->fpriv_list_lock);
+
+ list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) {
+ mutex_lock(&hpriv->ctx_lock);
+ ctx = hpriv->ctx;
+ if (ctx && !hl_ctx_get_unless_zero(ctx))
+ ctx = NULL;
+ mutex_unlock(&hpriv->ctx_lock);
+
+ /* There can only be a single user which has opened the compute device, so exit
+ * immediately once we find its context or if we see that it has been released
+ */
+ break;
+ }
+
+ mutex_unlock(&hdev->fpriv_list_lock);
+
+ return ctx;
+}
+
+/*
+ * hl_ctx_get_fence_locked - get CS fence under CS lock
+ *
+ * @ctx: pointer to the context structure.
+ * @seq: CS sequences number
+ *
+ * @return valid fence pointer on success, NULL if fence is gone, otherwise
+ * error pointer.
+ *
+ * NOTE: this function shall be called with cs_lock locked
+ */
+static struct hl_fence *hl_ctx_get_fence_locked(struct hl_ctx *ctx, u64 seq)
+{
+ struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop;
+ struct hl_fence *fence;
+
+ if (seq >= ctx->cs_sequence)
+ return ERR_PTR(-EINVAL);
+
+ if (seq + asic_prop->max_pending_cs < ctx->cs_sequence)
+ return NULL;
+
+ fence = ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)];
+ hl_fence_get(fence);
+ return fence;
+}
+
+struct hl_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq)
+{
+ struct hl_fence *fence;
+
+ spin_lock(&ctx->cs_lock);
+
+ fence = hl_ctx_get_fence_locked(ctx, seq);
+
+ spin_unlock(&ctx->cs_lock);
+
+ return fence;
+}
+
+/*
+ * hl_ctx_get_fences - get multiple CS fences under the same CS lock
+ *
+ * @ctx: pointer to the context structure.
+ * @seq_arr: array of CS sequences to wait for
+ * @fence: fence array to store the CS fences
+ * @arr_len: length of seq_arr and fence_arr
+ *
+ * @return 0 on success, otherwise non 0 error code
+ */
+int hl_ctx_get_fences(struct hl_ctx *ctx, u64 *seq_arr,
+ struct hl_fence **fence, u32 arr_len)
+{
+ struct hl_fence **fence_arr_base = fence;
+ int i, rc = 0;
+
+ spin_lock(&ctx->cs_lock);
+
+ for (i = 0; i < arr_len; i++, fence++) {
+ u64 seq = seq_arr[i];
+
+ *fence = hl_ctx_get_fence_locked(ctx, seq);
+
+ if (IS_ERR(*fence)) {
+ dev_err(ctx->hdev->dev,
+ "Failed to get fence for CS with seq 0x%llx\n",
+ seq);
+ rc = PTR_ERR(*fence);
+ break;
+ }
+ }
+
+ spin_unlock(&ctx->cs_lock);
+
+ if (rc)
+ hl_fences_put(fence_arr_base, i);
+
+ return rc;
+}
+
+/*
+ * hl_ctx_mgr_init - initialize the context manager
+ *
+ * @ctx_mgr: pointer to context manager structure
+ *
+ * This manager is an object inside the hpriv object of the user process.
+ * The function is called when a user process opens the FD.
+ */
+void hl_ctx_mgr_init(struct hl_ctx_mgr *ctx_mgr)
+{
+ mutex_init(&ctx_mgr->lock);
+ idr_init(&ctx_mgr->handles);
+}
+
+/*
+ * hl_ctx_mgr_fini - finalize the context manager
+ *
+ * @hdev: pointer to device structure
+ * @ctx_mgr: pointer to context manager structure
+ *
+ * This function goes over all the contexts in the manager and frees them.
+ * It is called when a process closes the FD.
+ */
+void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *ctx_mgr)
+{
+ struct hl_ctx *ctx;
+ struct idr *idp;
+ u32 id;
+
+ idp = &ctx_mgr->handles;
+
+ idr_for_each_entry(idp, ctx, id)
+ kref_put(&ctx->refcount, hl_ctx_do_release);
+
+ idr_destroy(&ctx_mgr->handles);
+ mutex_destroy(&ctx_mgr->lock);
+}
diff --git a/drivers/accel/habanalabs/common/debugfs.c b/drivers/accel/habanalabs/common/debugfs.c
new file mode 100644
index 000000000000..5f0820b19ccb
--- /dev/null
+++ b/drivers/accel/habanalabs/common/debugfs.c
@@ -0,0 +1,2250 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2021 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+#include "hldio.h"
+#include "../include/hw_ip/mmu/mmu_general.h"
+
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/iommu.h>
+
+#define MMU_ADDR_BUF_SIZE 40
+#define MMU_ASID_BUF_SIZE 10
+#define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE)
+#define I2C_MAX_TRANSACTION_LEN 8
+
+static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr,
+ u8 i2c_reg, u8 i2c_len, u64 *val)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -EBUSY;
+
+ if (i2c_len > I2C_MAX_TRANSACTION_LEN) {
+ dev_err(hdev->dev, "I2C transaction length %u, exceeds maximum of %u\n",
+ i2c_len, I2C_MAX_TRANSACTION_LEN);
+ return -EINVAL;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_I2C_RD <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.i2c_bus = i2c_bus;
+ pkt.i2c_addr = i2c_addr;
+ pkt.i2c_reg = i2c_reg;
+ pkt.i2c_len = i2c_len;
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, val);
+ if (rc && rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to read from I2C, error %d\n", rc);
+
+ return rc;
+}
+
+static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr,
+ u8 i2c_reg, u8 i2c_len, u64 val)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -EBUSY;
+
+ if (i2c_len > I2C_MAX_TRANSACTION_LEN) {
+ dev_err(hdev->dev, "I2C transaction length %u, exceeds maximum of %u\n",
+ i2c_len, I2C_MAX_TRANSACTION_LEN);
+ return -EINVAL;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_I2C_WR <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.i2c_bus = i2c_bus;
+ pkt.i2c_addr = i2c_addr;
+ pkt.i2c_reg = i2c_reg;
+ pkt.i2c_len = i2c_len;
+ pkt.value = cpu_to_le64(val);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to write to I2C, error %d\n", rc);
+
+ return rc;
+}
+
+static void hl_debugfs_led_set(struct hl_device *hdev, u8 led, u8 state)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL))
+ return;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_LED_SET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.led_index = cpu_to_le32(led);
+ pkt.value = cpu_to_le64(state);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to set LED %d, error %d\n", led, rc);
+}
+
+static int command_buffers_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_cb *cb;
+ bool first = true;
+
+ spin_lock(&dev_entry->cb_spinlock);
+
+ list_for_each_entry(cb, &dev_entry->cb_list, debugfs_list) {
+ if (first) {
+ first = false;
+ seq_puts(s, "\n");
+ seq_puts(s, " CB ID CTX ID CB size CB RefCnt mmap? CS counter\n");
+ seq_puts(s, "---------------------------------------------------------------\n");
+ }
+ seq_printf(s,
+ " %03llu %d 0x%08x %d %d %d\n",
+ cb->buf->handle, cb->ctx->asid, cb->size,
+ kref_read(&cb->buf->refcount),
+ atomic_read(&cb->buf->mmap), atomic_read(&cb->cs_cnt));
+ }
+
+ spin_unlock(&dev_entry->cb_spinlock);
+
+ if (!first)
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int command_submission_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_cs *cs;
+ bool first = true;
+
+ spin_lock(&dev_entry->cs_spinlock);
+
+ list_for_each_entry(cs, &dev_entry->cs_list, debugfs_list) {
+ if (first) {
+ first = false;
+ seq_puts(s, "\n");
+ seq_puts(s, " CS ID CS TYPE CTX ASID CS RefCnt Submitted Completed\n");
+ seq_puts(s, "----------------------------------------------------------------\n");
+ }
+ seq_printf(s,
+ " %llu %d %d %d %d %d\n",
+ cs->sequence, cs->type, cs->ctx->asid,
+ kref_read(&cs->refcount),
+ cs->submitted, cs->completed);
+ }
+
+ spin_unlock(&dev_entry->cs_spinlock);
+
+ if (!first)
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int command_submission_jobs_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_cs_job *job;
+ bool first = true;
+
+ spin_lock(&dev_entry->cs_job_spinlock);
+
+ list_for_each_entry(job, &dev_entry->cs_job_list, debugfs_list) {
+ if (first) {
+ first = false;
+ seq_puts(s, "\n");
+ seq_puts(s, " JOB ID CS ID CS TYPE CTX ASID JOB RefCnt H/W Queue\n");
+ seq_puts(s, "---------------------------------------------------------------\n");
+ }
+ if (job->cs)
+ seq_printf(s,
+ " %02d %llu %d %d %d %d\n",
+ job->id, job->cs->sequence, job->cs->type,
+ job->cs->ctx->asid, kref_read(&job->refcount),
+ job->hw_queue_id);
+ else
+ seq_printf(s,
+ " %02d 0 0 %d %d %d\n",
+ job->id, HL_KERNEL_ASID_ID,
+ kref_read(&job->refcount), job->hw_queue_id);
+ }
+
+ spin_unlock(&dev_entry->cs_job_spinlock);
+
+ if (!first)
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int userptr_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_userptr *userptr;
+ char dma_dir[4][30] = {"DMA_BIDIRECTIONAL", "DMA_TO_DEVICE",
+ "DMA_FROM_DEVICE", "DMA_NONE"};
+ bool first = true;
+
+ spin_lock(&dev_entry->userptr_spinlock);
+
+ list_for_each_entry(userptr, &dev_entry->userptr_list, debugfs_list) {
+ if (first) {
+ first = false;
+ seq_puts(s, "\n");
+ seq_puts(s, " pid user virtual address size dma dir\n");
+ seq_puts(s, "----------------------------------------------------------\n");
+ }
+ seq_printf(s, " %-7d 0x%-14llx %-10llu %-30s\n",
+ userptr->pid, userptr->addr, userptr->size,
+ dma_dir[userptr->dir]);
+ }
+
+ spin_unlock(&dev_entry->userptr_spinlock);
+
+ if (!first)
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int vm_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_vm_hw_block_list_node *lnode;
+ struct hl_ctx *ctx;
+ struct hl_vm *vm;
+ struct hl_vm_hash_node *hnode;
+ struct hl_userptr *userptr;
+ struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
+ struct hl_va_range *va_range;
+ struct hl_vm_va_block *va_block;
+ enum vm_type *vm_type;
+ bool once = true;
+ u64 j;
+ int i;
+
+ mutex_lock(&dev_entry->ctx_mem_hash_mutex);
+
+ list_for_each_entry(ctx, &dev_entry->ctx_mem_hash_list, debugfs_list) {
+ once = false;
+ seq_puts(s, "\n\n----------------------------------------------------");
+ seq_puts(s, "\n----------------------------------------------------\n\n");
+ seq_printf(s, "ctx asid: %u\n", ctx->asid);
+
+ seq_puts(s, "\nmappings:\n\n");
+ seq_puts(s, " virtual address size handle\n");
+ seq_puts(s, "----------------------------------------------------\n");
+ mutex_lock(&ctx->mem_hash_lock);
+ hash_for_each(ctx->mem_hash, i, hnode, node) {
+ vm_type = hnode->ptr;
+
+ if (*vm_type == VM_TYPE_USERPTR) {
+ userptr = hnode->ptr;
+ seq_printf(s,
+ " 0x%-14llx %-10llu\n",
+ hnode->vaddr, userptr->size);
+ } else {
+ phys_pg_pack = hnode->ptr;
+ seq_printf(s,
+ " 0x%-14llx %-10llu %-4u\n",
+ hnode->vaddr, phys_pg_pack->total_size,
+ phys_pg_pack->handle);
+ }
+ }
+ mutex_unlock(&ctx->mem_hash_lock);
+
+ if (ctx->asid != HL_KERNEL_ASID_ID &&
+ !list_empty(&ctx->hw_block_mem_list)) {
+ seq_puts(s, "\nhw_block mappings:\n\n");
+ seq_puts(s,
+ " virtual address block size mapped size HW block id\n");
+ seq_puts(s,
+ "---------------------------------------------------------------\n");
+ mutex_lock(&ctx->hw_block_list_lock);
+ list_for_each_entry(lnode, &ctx->hw_block_mem_list, node) {
+ seq_printf(s,
+ " 0x%-14lx %-6u %-6u %-9u\n",
+ lnode->vaddr, lnode->block_size, lnode->mapped_size,
+ lnode->id);
+ }
+ mutex_unlock(&ctx->hw_block_list_lock);
+ }
+
+ vm = &ctx->hdev->vm;
+ spin_lock(&vm->idr_lock);
+
+ if (!idr_is_empty(&vm->phys_pg_pack_handles))
+ seq_puts(s, "\n\nallocations:\n");
+
+ idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_pack, i) {
+ if (phys_pg_pack->asid != ctx->asid)
+ continue;
+
+ seq_printf(s, "\nhandle: %u\n", phys_pg_pack->handle);
+ seq_printf(s, "page size: %u\n\n",
+ phys_pg_pack->page_size);
+ seq_puts(s, " physical address\n");
+ seq_puts(s, "---------------------\n");
+ for (j = 0 ; j < phys_pg_pack->npages ; j++) {
+ seq_printf(s, " 0x%-14llx\n",
+ phys_pg_pack->pages[j]);
+ }
+ }
+ spin_unlock(&vm->idr_lock);
+
+ }
+
+ mutex_unlock(&dev_entry->ctx_mem_hash_mutex);
+
+ ctx = hl_get_compute_ctx(dev_entry->hdev);
+ if (ctx) {
+ seq_puts(s, "\nVA ranges:\n\n");
+ for (i = HL_VA_RANGE_TYPE_HOST ; i < HL_VA_RANGE_TYPE_MAX ; ++i) {
+ va_range = ctx->va_range[i];
+ seq_printf(s, " va_range %d\n", i);
+ seq_puts(s, "---------------------\n");
+ mutex_lock(&va_range->lock);
+ list_for_each_entry(va_block, &va_range->list, node) {
+ seq_printf(s, "%#16llx - %#16llx (%#llx)\n",
+ va_block->start, va_block->end,
+ va_block->size);
+ }
+ mutex_unlock(&va_range->lock);
+ seq_puts(s, "\n");
+ }
+ hl_ctx_put(ctx);
+ }
+
+ if (!once)
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int userptr_lookup_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct scatterlist *sg;
+ struct hl_userptr *userptr;
+ bool first = true;
+ u64 total_npages, npages, sg_start, sg_end;
+ dma_addr_t dma_addr;
+ int i;
+
+ spin_lock(&dev_entry->userptr_spinlock);
+
+ list_for_each_entry(userptr, &dev_entry->userptr_list, debugfs_list) {
+ if (dev_entry->userptr_lookup >= userptr->addr &&
+ dev_entry->userptr_lookup < userptr->addr + userptr->size) {
+ total_npages = 0;
+ for_each_sgtable_dma_sg(userptr->sgt, sg, i) {
+ npages = hl_get_sg_info(sg, &dma_addr);
+ sg_start = userptr->addr +
+ total_npages * PAGE_SIZE;
+ sg_end = userptr->addr +
+ (total_npages + npages) * PAGE_SIZE;
+
+ if (dev_entry->userptr_lookup >= sg_start &&
+ dev_entry->userptr_lookup < sg_end) {
+ dma_addr += (dev_entry->userptr_lookup -
+ sg_start);
+ if (first) {
+ first = false;
+ seq_puts(s, "\n");
+ seq_puts(s, " user virtual address dma address pid region start region size\n");
+ seq_puts(s, "---------------------------------------------------------------------------------------\n");
+ }
+ seq_printf(s, " 0x%-18llx 0x%-16llx %-8u 0x%-16llx %-12llu\n",
+ dev_entry->userptr_lookup,
+ (u64)dma_addr, userptr->pid,
+ userptr->addr, userptr->size);
+ }
+ total_npages += npages;
+ }
+ }
+ }
+
+ spin_unlock(&dev_entry->userptr_spinlock);
+
+ if (!first)
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static ssize_t userptr_lookup_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ ssize_t rc;
+ u64 value;
+
+ rc = kstrtoull_from_user(buf, count, 16, &value);
+ if (rc)
+ return rc;
+
+ dev_entry->userptr_lookup = value;
+
+ return count;
+}
+
+static int mmu_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ struct hl_ctx *ctx;
+ struct hl_mmu_hop_info hops_info = {0};
+ u64 virt_addr = dev_entry->mmu_addr, phys_addr;
+ int i;
+
+ if (dev_entry->mmu_asid == HL_KERNEL_ASID_ID)
+ ctx = hdev->kernel_ctx;
+ else
+ ctx = hl_get_compute_ctx(hdev);
+
+ if (!ctx) {
+ dev_err(hdev->dev, "no ctx available\n");
+ return 0;
+ }
+
+ if (hl_mmu_get_tlb_info(ctx, virt_addr, &hops_info)) {
+ dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n",
+ virt_addr);
+ goto put_ctx;
+ }
+
+ hl_mmu_va_to_pa(ctx, virt_addr, &phys_addr);
+
+ if (hops_info.scrambled_vaddr &&
+ (dev_entry->mmu_addr != hops_info.scrambled_vaddr))
+ seq_printf(s,
+ "asid: %u, virt_addr: 0x%llx, scrambled virt_addr: 0x%llx,\nphys_addr: 0x%llx, scrambled_phys_addr: 0x%llx\n",
+ dev_entry->mmu_asid, dev_entry->mmu_addr,
+ hops_info.scrambled_vaddr,
+ hops_info.unscrambled_paddr, phys_addr);
+ else
+ seq_printf(s,
+ "asid: %u, virt_addr: 0x%llx, phys_addr: 0x%llx\n",
+ dev_entry->mmu_asid, dev_entry->mmu_addr, phys_addr);
+
+ for (i = 0 ; i < hops_info.used_hops ; i++) {
+ seq_printf(s, "hop%d_addr: 0x%llx\n",
+ i, hops_info.hop_info[i].hop_addr);
+ seq_printf(s, "hop%d_pte_addr: 0x%llx\n",
+ i, hops_info.hop_info[i].hop_pte_addr);
+ seq_printf(s, "hop%d_pte: 0x%llx\n",
+ i, hops_info.hop_info[i].hop_pte_val);
+ }
+
+put_ctx:
+ if (dev_entry->mmu_asid != HL_KERNEL_ASID_ID)
+ hl_ctx_put(ctx);
+
+ return 0;
+}
+
+static ssize_t mmu_asid_va_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ char kbuf[MMU_KBUF_SIZE] = {0};
+ char *c;
+ ssize_t rc;
+
+ if (count > sizeof(kbuf) - 1)
+ goto err;
+ if (copy_from_user(kbuf, buf, count))
+ goto err;
+ kbuf[count] = 0;
+
+ c = strchr(kbuf, ' ');
+ if (!c)
+ goto err;
+ *c = '\0';
+
+ rc = kstrtouint(kbuf, 10, &dev_entry->mmu_asid);
+ if (rc)
+ goto err;
+
+ if (strncmp(c+1, "0x", 2))
+ goto err;
+ rc = kstrtoull(c+3, 16, &dev_entry->mmu_addr);
+ if (rc)
+ goto err;
+
+ return count;
+
+err:
+ dev_err(hdev->dev, "usage: echo <asid> <0xaddr> > mmu\n");
+
+ return -EINVAL;
+}
+
+static int mmu_ack_error(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ int rc;
+
+ if (!dev_entry->mmu_cap_mask) {
+ dev_err(hdev->dev, "mmu_cap_mask is not set\n");
+ goto err;
+ }
+
+ rc = hdev->asic_funcs->ack_mmu_errors(hdev, dev_entry->mmu_cap_mask);
+ if (rc)
+ goto err;
+
+ return 0;
+err:
+ return -EINVAL;
+}
+
+static ssize_t mmu_ack_error_value_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ char kbuf[MMU_KBUF_SIZE] = {0};
+ ssize_t rc;
+
+ if (count > sizeof(kbuf) - 1)
+ goto err;
+
+ if (copy_from_user(kbuf, buf, count))
+ goto err;
+
+ kbuf[count] = 0;
+
+ if (strncmp(kbuf, "0x", 2))
+ goto err;
+
+ rc = kstrtoull(kbuf, 16, &dev_entry->mmu_cap_mask);
+ if (rc)
+ goto err;
+
+ return count;
+err:
+ dev_err(hdev->dev, "usage: echo <0xmmu_cap_mask > > mmu_error\n");
+
+ return -EINVAL;
+}
+
+static int engines_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ struct engines_data eng_data;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev,
+ "Can't check device idle during reset\n");
+ return 0;
+ }
+
+ eng_data.actual_size = 0;
+ eng_data.allocated_buf_size = HL_ENGINES_DATA_MAX_SIZE;
+ eng_data.buf = vmalloc(eng_data.allocated_buf_size);
+ if (!eng_data.buf)
+ return -ENOMEM;
+
+ hdev->asic_funcs->is_device_idle(hdev, NULL, 0, &eng_data);
+
+ if (eng_data.actual_size > eng_data.allocated_buf_size) {
+ dev_err(hdev->dev,
+ "Engines data size (%d Bytes) is bigger than allocated size (%u Bytes)\n",
+ eng_data.actual_size, eng_data.allocated_buf_size);
+ vfree(eng_data.buf);
+ return -ENOMEM;
+ }
+
+ seq_write(s, eng_data.buf, eng_data.actual_size);
+
+ vfree(eng_data.buf);
+
+ return 0;
+}
+
+#ifdef CONFIG_HL_HLDIO
+/* DIO debugfs functions following the standard pattern */
+static int dio_ssd2hl_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+
+ if (!hdev->asic_prop.supports_nvme) {
+ seq_puts(s, "NVMe Direct I/O not supported\\n");
+ return 0;
+ }
+
+ seq_puts(s, "Usage: echo \"fd=N va=0xADDR off=N len=N\" > dio_ssd2hl\n");
+ seq_printf(s, "Last transfer: %zu bytes\\n", dev_entry->dio_stats.last_len_read);
+ seq_puts(s, "Note: All parameters must be page-aligned (4KB)\\n");
+
+ return 0;
+}
+
+static ssize_t dio_ssd2hl_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ struct hl_ctx *ctx = hdev->kernel_ctx;
+ char kbuf[128];
+ u64 device_va = 0, off_bytes = 0, len_bytes = 0;
+ u32 fd = 0;
+ size_t len_read = 0;
+ int rc, parsed;
+
+ if (!hdev->asic_prop.supports_nvme)
+ return -EOPNOTSUPP;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+
+ kbuf[count] = 0;
+
+ /* Parse: fd=N va=0xADDR off=N len=N */
+ parsed = sscanf(kbuf, "fd=%u va=0x%llx off=%llu len=%llu",
+ &fd, &device_va, &off_bytes, &len_bytes);
+ if (parsed != 4) {
+ dev_err(hdev->dev, "Invalid format. Expected: fd=N va=0xADDR off=N len=N\\n");
+ return -EINVAL;
+ }
+
+ /* Validate file descriptor */
+ if (fd == 0) {
+ dev_err(hdev->dev, "Invalid file descriptor: %u\\n", fd);
+ return -EINVAL;
+ }
+
+ /* Validate alignment requirements */
+ if (!IS_ALIGNED(device_va, PAGE_SIZE) ||
+ !IS_ALIGNED(off_bytes, PAGE_SIZE) ||
+ !IS_ALIGNED(len_bytes, PAGE_SIZE)) {
+ dev_err(hdev->dev,
+ "All parameters must be page-aligned (4KB)\\n");
+ return -EINVAL;
+ }
+
+ /* Validate transfer size */
+ if (len_bytes == 0 || len_bytes > SZ_1G) {
+ dev_err(hdev->dev, "Invalid length: %llu (max 1GB)\\n",
+ len_bytes);
+ return -EINVAL;
+ }
+
+ dev_dbg(hdev->dev, "DIO SSD2HL: fd=%u va=0x%llx off=%llu len=%llu\\n",
+ fd, device_va, off_bytes, len_bytes);
+
+ rc = hl_dio_ssd2hl(hdev, ctx, fd, device_va, off_bytes, len_bytes, &len_read);
+ if (rc < 0) {
+ dev_entry->dio_stats.failed_ops++;
+ dev_err(hdev->dev, "SSD2HL operation failed: %d\\n", rc);
+ return rc;
+ }
+
+ /* Update statistics */
+ dev_entry->dio_stats.total_ops++;
+ dev_entry->dio_stats.successful_ops++;
+ dev_entry->dio_stats.bytes_transferred += len_read;
+ dev_entry->dio_stats.last_len_read = len_read;
+
+ dev_dbg(hdev->dev, "DIO SSD2HL completed: %zu bytes transferred\\n", len_read);
+
+ return count;
+}
+
+static int dio_hl2ssd_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, "HL2SSD (device-to-SSD) transfers not implemented\\n");
+ return 0;
+}
+
+static ssize_t dio_hl2ssd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+
+ if (!hdev->asic_prop.supports_nvme)
+ return -EOPNOTSUPP;
+
+ dev_dbg(hdev->dev, "HL2SSD operation not implemented\\n");
+ return -EOPNOTSUPP;
+}
+
+static int dio_stats_show(struct seq_file *s, void *data)
+{
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ struct hl_dio_stats *stats = &dev_entry->dio_stats;
+ u64 avg_bytes_per_op = 0, success_rate = 0;
+
+ if (!hdev->asic_prop.supports_nvme) {
+ seq_puts(s, "NVMe Direct I/O not supported\\n");
+ return 0;
+ }
+
+ if (stats->successful_ops > 0)
+ avg_bytes_per_op = stats->bytes_transferred / stats->successful_ops;
+
+ if (stats->total_ops > 0)
+ success_rate = (stats->successful_ops * 100) / stats->total_ops;
+
+ seq_puts(s, "=== Habanalabs Direct I/O Statistics ===\\n");
+ seq_printf(s, "Total operations: %llu\\n", stats->total_ops);
+ seq_printf(s, "Successful ops: %llu\\n", stats->successful_ops);
+ seq_printf(s, "Failed ops: %llu\\n", stats->failed_ops);
+ seq_printf(s, "Success rate: %llu%%\\n", success_rate);
+ seq_printf(s, "Total bytes: %llu\\n", stats->bytes_transferred);
+ seq_printf(s, "Avg bytes per op: %llu\\n", avg_bytes_per_op);
+ seq_printf(s, "Last transfer: %zu bytes\\n", stats->last_len_read);
+
+ return 0;
+}
+
+static int dio_reset_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, "Write '1' to reset DIO statistics\\n");
+ return 0;
+}
+
+static ssize_t dio_reset_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct hl_debugfs_entry *entry = s->private;
+ struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
+ struct hl_device *hdev = dev_entry->hdev;
+ char kbuf[8];
+ unsigned long val;
+ int rc;
+
+ if (!hdev->asic_prop.supports_nvme)
+ return -EOPNOTSUPP;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+
+ kbuf[count] = 0;
+
+ rc = kstrtoul(kbuf, 0, &val);
+ if (rc)
+ return rc;
+
+ if (val == 1) {
+ memset(&dev_entry->dio_stats, 0, sizeof(dev_entry->dio_stats));
+ dev_dbg(hdev->dev, "DIO statistics reset\\n");
+ } else {
+ dev_err(hdev->dev, "Write '1' to reset statistics\\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+#endif
+
+static ssize_t hl_memory_scrub(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 val = hdev->memory_scrub_val;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL)) {
+ dev_warn_ratelimited(hdev->dev, "Can't scrub memory, device is not operational\n");
+ return -EIO;
+ }
+
+ mutex_lock(&hdev->fpriv_list_lock);
+ if (hdev->is_compute_ctx_active) {
+ mutex_unlock(&hdev->fpriv_list_lock);
+ dev_err(hdev->dev, "can't scrub dram, context exist\n");
+ return -EBUSY;
+ }
+ hdev->is_in_dram_scrub = true;
+ mutex_unlock(&hdev->fpriv_list_lock);
+
+ rc = hdev->asic_funcs->scrub_device_dram(hdev, val);
+
+ mutex_lock(&hdev->fpriv_list_lock);
+ hdev->is_in_dram_scrub = false;
+ mutex_unlock(&hdev->fpriv_list_lock);
+
+ if (rc)
+ return rc;
+ return count;
+}
+
+static bool hl_is_device_va(struct hl_device *hdev, u64 addr)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ if (prop->dram_supports_virtual_memory &&
+ (addr >= prop->dmmu.start_addr && addr < prop->dmmu.end_addr))
+ return true;
+
+ if (addr >= prop->pmmu.start_addr &&
+ addr < prop->pmmu.end_addr)
+ return true;
+
+ if (addr >= prop->pmmu_huge.start_addr &&
+ addr < prop->pmmu_huge.end_addr)
+ return true;
+
+ return false;
+}
+
+static bool hl_is_device_internal_memory_va(struct hl_device *hdev, u64 addr,
+ u32 size)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 dram_start_addr, dram_end_addr;
+
+ if (prop->dram_supports_virtual_memory) {
+ dram_start_addr = prop->dmmu.start_addr;
+ dram_end_addr = prop->dmmu.end_addr;
+ } else {
+ dram_start_addr = prop->dram_base_address;
+ dram_end_addr = prop->dram_end_address;
+ }
+
+ if (hl_mem_area_inside_range(addr, size, dram_start_addr,
+ dram_end_addr))
+ return true;
+
+ if (hl_mem_area_inside_range(addr, size, prop->sram_base_address,
+ prop->sram_end_address))
+ return true;
+
+ return false;
+}
+
+static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, u32 size,
+ u64 *phys_addr)
+{
+ struct hl_vm_phys_pg_pack *phys_pg_pack;
+ struct hl_ctx *ctx;
+ struct hl_vm_hash_node *hnode;
+ u64 end_address, range_size;
+ struct hl_userptr *userptr;
+ enum vm_type *vm_type;
+ bool valid = false;
+ int i, rc = 0;
+
+ ctx = hl_get_compute_ctx(hdev);
+
+ if (!ctx) {
+ dev_err(hdev->dev, "no ctx available\n");
+ return -EINVAL;
+ }
+
+ /* Verify address is mapped */
+ mutex_lock(&ctx->mem_hash_lock);
+ hash_for_each(ctx->mem_hash, i, hnode, node) {
+ vm_type = hnode->ptr;
+
+ if (*vm_type == VM_TYPE_USERPTR) {
+ userptr = hnode->ptr;
+ range_size = userptr->size;
+ } else {
+ phys_pg_pack = hnode->ptr;
+ range_size = phys_pg_pack->total_size;
+ }
+
+ end_address = virt_addr + size;
+ if ((virt_addr >= hnode->vaddr) &&
+ (end_address <= hnode->vaddr + range_size)) {
+ valid = true;
+ break;
+ }
+ }
+ mutex_unlock(&ctx->mem_hash_lock);
+
+ if (!valid) {
+ dev_err(hdev->dev,
+ "virt addr 0x%llx is not mapped\n",
+ virt_addr);
+ rc = -EINVAL;
+ goto put_ctx;
+ }
+
+ rc = hl_mmu_va_to_pa(ctx, virt_addr, phys_addr);
+ if (rc) {
+ dev_err(hdev->dev,
+ "virt addr 0x%llx is not mapped to phys addr\n",
+ virt_addr);
+ rc = -EINVAL;
+ }
+
+put_ctx:
+ hl_ctx_put(ctx);
+
+ return rc;
+}
+
+static int hl_access_dev_mem_by_region(struct hl_device *hdev, u64 addr,
+ u64 *val, enum debugfs_access_type acc_type, bool *found)
+{
+ size_t acc_size = (acc_type == DEBUGFS_READ64 || acc_type == DEBUGFS_WRITE64) ?
+ sizeof(u64) : sizeof(u32);
+ struct pci_mem_region *mem_reg;
+ int i;
+
+ for (i = 0; i < PCI_REGION_NUMBER; i++) {
+ mem_reg = &hdev->pci_mem_region[i];
+ if (!mem_reg->used)
+ continue;
+ if (addr >= mem_reg->region_base &&
+ addr <= mem_reg->region_base + mem_reg->region_size - acc_size) {
+ *found = true;
+ return hdev->asic_funcs->access_dev_mem(hdev, i, addr, val, acc_type);
+ }
+ }
+ return 0;
+}
+
+static void hl_access_host_mem(struct hl_device *hdev, u64 addr, u64 *val,
+ enum debugfs_access_type acc_type)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 offset = prop->device_dma_offset_for_host_access;
+
+ switch (acc_type) {
+ case DEBUGFS_READ32:
+ *val = *(u32 *) phys_to_virt(addr - offset);
+ break;
+ case DEBUGFS_WRITE32:
+ *(u32 *) phys_to_virt(addr - offset) = *val;
+ break;
+ case DEBUGFS_READ64:
+ *val = *(u64 *) phys_to_virt(addr - offset);
+ break;
+ case DEBUGFS_WRITE64:
+ *(u64 *) phys_to_virt(addr - offset) = *val;
+ break;
+ default:
+ dev_err(hdev->dev, "hostmem access-type %d id not supported\n", acc_type);
+ break;
+ }
+}
+
+static void dump_cfg_access_entry(struct hl_device *hdev,
+ struct hl_debugfs_cfg_access_entry *entry)
+{
+ char *access_type = "";
+ struct tm tm;
+
+ switch (entry->debugfs_type) {
+ case DEBUGFS_READ32:
+ access_type = "READ32 from";
+ break;
+ case DEBUGFS_WRITE32:
+ access_type = "WRITE32 to";
+ break;
+ case DEBUGFS_READ64:
+ access_type = "READ64 from";
+ break;
+ case DEBUGFS_WRITE64:
+ access_type = "WRITE64 to";
+ break;
+ default:
+ dev_err(hdev->dev, "Invalid DEBUGFS access type (%u)\n", entry->debugfs_type);
+ return;
+ }
+
+ time64_to_tm(entry->seconds_since_epoch, 0, &tm);
+ dev_info(hdev->dev,
+ "%ld-%02d-%02d %02d:%02d:%02d (UTC): %s %#llx\n", tm.tm_year + 1900, tm.tm_mon + 1,
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, access_type, entry->addr);
+}
+
+void hl_debugfs_cfg_access_history_dump(struct hl_device *hdev)
+{
+ struct hl_debugfs_cfg_access *dbgfs = &hdev->debugfs_cfg_accesses;
+ u32 i, head, count = 0;
+ time64_t entry_time, now;
+ unsigned long flags;
+
+ now = ktime_get_real_seconds();
+
+ spin_lock_irqsave(&dbgfs->lock, flags);
+ head = dbgfs->head;
+ if (head == 0)
+ i = HL_DBGFS_CFG_ACCESS_HIST_LEN - 1;
+ else
+ i = head - 1;
+
+ /* Walk back until timeout or invalid entry */
+ while (dbgfs->cfg_access_list[i].valid) {
+ entry_time = dbgfs->cfg_access_list[i].seconds_since_epoch;
+ /* Stop when entry is older than timeout */
+ if (now - entry_time > HL_DBGFS_CFG_ACCESS_HIST_TIMEOUT_SEC)
+ break;
+
+ /* print single entry under lock */
+ {
+ struct hl_debugfs_cfg_access_entry entry = dbgfs->cfg_access_list[i];
+ /*
+ * We copy the entry out under lock and then print after
+ * releasing the lock to minimize time under lock.
+ */
+ spin_unlock_irqrestore(&dbgfs->lock, flags);
+ dump_cfg_access_entry(hdev, &entry);
+ spin_lock_irqsave(&dbgfs->lock, flags);
+ }
+
+ /* mark consumed */
+ dbgfs->cfg_access_list[i].valid = false;
+
+ if (i == 0)
+ i = HL_DBGFS_CFG_ACCESS_HIST_LEN - 1;
+ else
+ i--;
+ count++;
+ if (count >= HL_DBGFS_CFG_ACCESS_HIST_LEN)
+ break;
+ }
+ spin_unlock_irqrestore(&dbgfs->lock, flags);
+}
+
+static void check_if_cfg_access_and_log(struct hl_device *hdev, u64 addr, size_t access_size,
+ enum debugfs_access_type access_type)
+{
+ struct hl_debugfs_cfg_access *dbgfs_cfg_accesses = &hdev->debugfs_cfg_accesses;
+ struct pci_mem_region *mem_reg = &hdev->pci_mem_region[PCI_REGION_CFG];
+ struct hl_debugfs_cfg_access_entry *new_entry;
+ unsigned long flags;
+
+ /* Check if address is in config memory */
+ if (addr >= mem_reg->region_base &&
+ mem_reg->region_size >= access_size &&
+ addr <= mem_reg->region_base + mem_reg->region_size - access_size) {
+
+ spin_lock_irqsave(&dbgfs_cfg_accesses->lock, flags);
+
+ new_entry = &dbgfs_cfg_accesses->cfg_access_list[dbgfs_cfg_accesses->head];
+ new_entry->seconds_since_epoch = ktime_get_real_seconds();
+ new_entry->addr = addr;
+ new_entry->debugfs_type = access_type;
+ new_entry->valid = true;
+ dbgfs_cfg_accesses->head = (dbgfs_cfg_accesses->head + 1)
+ % HL_DBGFS_CFG_ACCESS_HIST_LEN;
+
+ spin_unlock_irqrestore(&dbgfs_cfg_accesses->lock, flags);
+
+ }
+}
+
+static int hl_access_mem(struct hl_device *hdev, u64 addr, u64 *val,
+ enum debugfs_access_type acc_type)
+{
+ size_t acc_size = (acc_type == DEBUGFS_READ64 || acc_type == DEBUGFS_WRITE64) ?
+ sizeof(u64) : sizeof(u32);
+ u64 host_start = hdev->asic_prop.host_base_address;
+ u64 host_end = hdev->asic_prop.host_end_address;
+ bool user_address, found = false;
+ int rc;
+
+ user_address = hl_is_device_va(hdev, addr);
+ if (user_address) {
+ rc = device_va_to_pa(hdev, addr, acc_size, &addr);
+ if (rc)
+ return rc;
+ }
+
+ check_if_cfg_access_and_log(hdev, addr, acc_size, acc_type);
+ rc = hl_access_dev_mem_by_region(hdev, addr, val, acc_type, &found);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed reading addr %#llx from dev mem (%d)\n",
+ addr, rc);
+ return rc;
+ }
+
+ if (found)
+ return 0;
+
+ if (!user_address || device_iommu_mapped(&hdev->pdev->dev)) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (addr >= host_start && addr <= host_end - acc_size) {
+ hl_access_host_mem(hdev, addr, val, acc_type);
+ } else {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_err(hdev->dev, "invalid addr %#llx\n", addr);
+ return rc;
+}
+
+static ssize_t hl_data_read32(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 value64, addr = entry->addr;
+ char tmp_buf[32];
+ ssize_t rc;
+ u32 val;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev, "Can't read during reset\n");
+ return 0;
+ }
+
+ if (*ppos)
+ return 0;
+
+ rc = hl_access_mem(hdev, addr, &value64, DEBUGFS_READ32);
+ if (rc)
+ return rc;
+
+ val = value64; /* downcast back to 32 */
+
+ sprintf(tmp_buf, "0x%08x\n", val);
+ return simple_read_from_buffer(buf, count, ppos, tmp_buf,
+ strlen(tmp_buf));
+}
+
+static ssize_t hl_data_write32(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 value64, addr = entry->addr;
+ u32 value;
+ ssize_t rc;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev, "Can't write during reset\n");
+ return 0;
+ }
+
+ rc = kstrtouint_from_user(buf, count, 16, &value);
+ if (rc)
+ return rc;
+
+ value64 = value;
+ rc = hl_access_mem(hdev, addr, &value64, DEBUGFS_WRITE32);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static ssize_t hl_data_read64(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 addr = entry->addr;
+ char tmp_buf[32];
+ ssize_t rc;
+ u64 val;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev, "Can't read during reset\n");
+ return 0;
+ }
+
+ if (*ppos)
+ return 0;
+
+ rc = hl_access_mem(hdev, addr, &val, DEBUGFS_READ64);
+ if (rc)
+ return rc;
+
+ sprintf(tmp_buf, "0x%016llx\n", val);
+ return simple_read_from_buffer(buf, count, ppos, tmp_buf,
+ strlen(tmp_buf));
+}
+
+static ssize_t hl_data_write64(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 addr = entry->addr;
+ u64 value;
+ ssize_t rc;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev, "Can't write during reset\n");
+ return 0;
+ }
+
+ rc = kstrtoull_from_user(buf, count, 16, &value);
+ if (rc)
+ return rc;
+
+ rc = hl_access_mem(hdev, addr, &value, DEBUGFS_WRITE64);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static ssize_t hl_dma_size_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 addr = entry->addr;
+ ssize_t rc;
+ u32 size;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev, "Can't DMA during reset\n");
+ return 0;
+ }
+ rc = kstrtouint_from_user(buf, count, 16, &size);
+ if (rc)
+ return rc;
+
+ if (!size) {
+ dev_err(hdev->dev, "DMA read failed. size can't be 0\n");
+ return -EINVAL;
+ }
+
+ if (size > SZ_128M) {
+ dev_err(hdev->dev,
+ "DMA read failed. size can't be larger than 128MB\n");
+ return -EINVAL;
+ }
+
+ if (!hl_is_device_internal_memory_va(hdev, addr, size)) {
+ dev_err(hdev->dev,
+ "DMA read failed. Invalid 0x%010llx + 0x%08x\n",
+ addr, size);
+ return -EINVAL;
+ }
+
+ /* Free the previous allocation, if there was any */
+ entry->data_dma_blob_desc.size = 0;
+ vfree(entry->data_dma_blob_desc.data);
+
+ entry->data_dma_blob_desc.data = vmalloc(size);
+ if (!entry->data_dma_blob_desc.data)
+ return -ENOMEM;
+
+ rc = hdev->asic_funcs->debugfs_read_dma(hdev, addr, size,
+ entry->data_dma_blob_desc.data);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to DMA from 0x%010llx\n", addr);
+ vfree(entry->data_dma_blob_desc.data);
+ entry->data_dma_blob_desc.data = NULL;
+ return -EIO;
+ }
+
+ entry->data_dma_blob_desc.size = size;
+
+ return count;
+}
+
+static ssize_t hl_monitor_dump_trigger(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 size, trig;
+ ssize_t rc;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev, "Can't dump monitors during reset\n");
+ return 0;
+ }
+ rc = kstrtouint_from_user(buf, count, 10, &trig);
+ if (rc)
+ return rc;
+
+ if (trig != 1) {
+ dev_err(hdev->dev, "Must write 1 to trigger monitor dump\n");
+ return -EINVAL;
+ }
+
+ size = sizeof(struct cpucp_monitor_dump);
+
+ /* Free the previous allocation, if there was any */
+ entry->mon_dump_blob_desc.size = 0;
+ vfree(entry->mon_dump_blob_desc.data);
+
+ entry->mon_dump_blob_desc.data = vmalloc(size);
+ if (!entry->mon_dump_blob_desc.data)
+ return -ENOMEM;
+
+ rc = hdev->asic_funcs->get_monitor_dump(hdev, entry->mon_dump_blob_desc.data);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to dump monitors\n");
+ vfree(entry->mon_dump_blob_desc.data);
+ entry->mon_dump_blob_desc.data = NULL;
+ return -EIO;
+ }
+
+ entry->mon_dump_blob_desc.size = size;
+
+ return count;
+}
+
+static ssize_t hl_get_power_state(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ char tmp_buf[200];
+ int i;
+
+ if (*ppos)
+ return 0;
+
+ if (hdev->pdev->current_state == PCI_D0)
+ i = 1;
+ else if (hdev->pdev->current_state == PCI_D3hot)
+ i = 2;
+ else
+ i = 3;
+
+ sprintf(tmp_buf,
+ "current power state: %d\n1 - D0\n2 - D3hot\n3 - Unknown\n", i);
+ return simple_read_from_buffer(buf, count, ppos, tmp_buf,
+ strlen(tmp_buf));
+}
+
+static ssize_t hl_set_power_state(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 value;
+ ssize_t rc;
+
+ rc = kstrtouint_from_user(buf, count, 10, &value);
+ if (rc)
+ return rc;
+
+ if (value == 1) {
+ pci_set_power_state(hdev->pdev, PCI_D0);
+ pci_restore_state(hdev->pdev);
+ rc = pci_enable_device(hdev->pdev);
+ if (rc < 0)
+ return rc;
+ } else if (value == 2) {
+ pci_save_state(hdev->pdev);
+ pci_disable_device(hdev->pdev);
+ pci_set_power_state(hdev->pdev, PCI_D3hot);
+ } else {
+ dev_dbg(hdev->dev, "invalid power state value %u\n", value);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t hl_i2c_data_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ char tmp_buf[32];
+ u64 val;
+ ssize_t rc;
+
+ if (*ppos)
+ return 0;
+
+ rc = hl_debugfs_i2c_read(hdev, entry->i2c_bus, entry->i2c_addr,
+ entry->i2c_reg, entry->i2c_len, &val);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to read from I2C bus %d, addr %d, reg %d, len %d\n",
+ entry->i2c_bus, entry->i2c_addr, entry->i2c_reg, entry->i2c_len);
+ return rc;
+ }
+
+ sprintf(tmp_buf, "%#02llx\n", val);
+ rc = simple_read_from_buffer(buf, count, ppos, tmp_buf,
+ strlen(tmp_buf));
+
+ return rc;
+}
+
+static ssize_t hl_i2c_data_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u64 value;
+ ssize_t rc;
+
+ rc = kstrtou64_from_user(buf, count, 16, &value);
+ if (rc)
+ return rc;
+
+ rc = hl_debugfs_i2c_write(hdev, entry->i2c_bus, entry->i2c_addr,
+ entry->i2c_reg, entry->i2c_len, value);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to write %#02llx to I2C bus %d, addr %d, reg %d, len %d\n",
+ value, entry->i2c_bus, entry->i2c_addr, entry->i2c_reg, entry->i2c_len);
+ return rc;
+ }
+
+ return count;
+}
+
+static ssize_t hl_led0_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 value;
+ ssize_t rc;
+
+ rc = kstrtouint_from_user(buf, count, 10, &value);
+ if (rc)
+ return rc;
+
+ value = value ? 1 : 0;
+
+ hl_debugfs_led_set(hdev, 0, value);
+
+ return count;
+}
+
+static ssize_t hl_led1_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 value;
+ ssize_t rc;
+
+ rc = kstrtouint_from_user(buf, count, 10, &value);
+ if (rc)
+ return rc;
+
+ value = value ? 1 : 0;
+
+ hl_debugfs_led_set(hdev, 1, value);
+
+ return count;
+}
+
+static ssize_t hl_led2_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 value;
+ ssize_t rc;
+
+ rc = kstrtouint_from_user(buf, count, 10, &value);
+ if (rc)
+ return rc;
+
+ value = value ? 1 : 0;
+
+ hl_debugfs_led_set(hdev, 2, value);
+
+ return count;
+}
+
+static ssize_t hl_device_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ static const char *help =
+ "Valid values: disable, enable, suspend, resume, cpu_timeout\n";
+ return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
+}
+
+static ssize_t hl_device_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ char data[30] = {0};
+
+ /* don't allow partial writes */
+ if (*ppos != 0)
+ return 0;
+
+ simple_write_to_buffer(data, 29, ppos, buf, count);
+
+ if (strncmp("disable", data, strlen("disable")) == 0) {
+ hdev->disabled = true;
+ } else if (strncmp("enable", data, strlen("enable")) == 0) {
+ hdev->disabled = false;
+ } else if (strncmp("suspend", data, strlen("suspend")) == 0) {
+ hdev->asic_funcs->suspend(hdev);
+ } else if (strncmp("resume", data, strlen("resume")) == 0) {
+ hdev->asic_funcs->resume(hdev);
+ } else if (strncmp("cpu_timeout", data, strlen("cpu_timeout")) == 0) {
+ hdev->device_cpu_disabled = true;
+ } else {
+ dev_err(hdev->dev,
+ "Valid values: disable, enable, suspend, resume, cpu_timeout\n");
+ count = -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t hl_clk_gate_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return count;
+}
+
+static ssize_t hl_stop_on_err_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ char tmp_buf[200];
+ ssize_t rc;
+
+ if (!hdev->asic_prop.configurable_stop_on_err)
+ return -EOPNOTSUPP;
+
+ if (*ppos)
+ return 0;
+
+ sprintf(tmp_buf, "%d\n", hdev->stop_on_err);
+ rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf,
+ strlen(tmp_buf) + 1);
+
+ return rc;
+}
+
+static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 value;
+ ssize_t rc;
+
+ if (!hdev->asic_prop.configurable_stop_on_err)
+ return -EOPNOTSUPP;
+
+ if (hdev->reset_info.in_reset) {
+ dev_warn_ratelimited(hdev->dev,
+ "Can't change stop on error during reset\n");
+ return 0;
+ }
+
+ rc = kstrtouint_from_user(buf, count, 10, &value);
+ if (rc)
+ return rc;
+
+ hdev->stop_on_err = value ? 1 : 0;
+
+ hl_device_reset(hdev, 0);
+
+ return count;
+}
+
+static ssize_t hl_security_violations_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+
+ hdev->asic_funcs->ack_protection_bits_errors(hdev);
+
+ return 0;
+}
+
+static ssize_t hl_state_dump_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ ssize_t rc;
+
+ down_read(&entry->state_dump_sem);
+ if (!entry->state_dump[entry->state_dump_head])
+ rc = 0;
+ else
+ rc = simple_read_from_buffer(
+ buf, count, ppos,
+ entry->state_dump[entry->state_dump_head],
+ strlen(entry->state_dump[entry->state_dump_head]));
+ up_read(&entry->state_dump_sem);
+
+ return rc;
+}
+
+static ssize_t hl_state_dump_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ ssize_t rc;
+ u32 size;
+ int i;
+
+ rc = kstrtouint_from_user(buf, count, 10, &size);
+ if (rc)
+ return rc;
+
+ if (size <= 0 || size >= ARRAY_SIZE(entry->state_dump)) {
+ dev_err(hdev->dev, "Invalid number of dumps to skip\n");
+ return -EINVAL;
+ }
+
+ if (entry->state_dump[entry->state_dump_head]) {
+ down_write(&entry->state_dump_sem);
+ for (i = 0; i < size; ++i) {
+ vfree(entry->state_dump[entry->state_dump_head]);
+ entry->state_dump[entry->state_dump_head] = NULL;
+ if (entry->state_dump_head > 0)
+ entry->state_dump_head--;
+ else
+ entry->state_dump_head =
+ ARRAY_SIZE(entry->state_dump) - 1;
+ }
+ up_write(&entry->state_dump_sem);
+ }
+
+ return count;
+}
+
+static ssize_t hl_timeout_locked_read(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ char tmp_buf[200];
+ ssize_t rc;
+
+ if (*ppos)
+ return 0;
+
+ sprintf(tmp_buf, "%d\n",
+ jiffies_to_msecs(hdev->timeout_jiffies) / 1000);
+ rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf,
+ strlen(tmp_buf) + 1);
+
+ return rc;
+}
+
+static ssize_t hl_timeout_locked_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+ u32 value;
+ ssize_t rc;
+
+ rc = kstrtouint_from_user(buf, count, 10, &value);
+ if (rc)
+ return rc;
+
+ if (value)
+ hdev->timeout_jiffies = secs_to_jiffies(value);
+ else
+ hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
+
+ return count;
+}
+
+static ssize_t hl_check_razwi_happened(struct file *f, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
+ struct hl_device *hdev = entry->hdev;
+
+ hdev->asic_funcs->check_if_razwi_happened(hdev);
+
+ return 0;
+}
+
+static const struct file_operations hl_mem_scrub_fops = {
+ .owner = THIS_MODULE,
+ .write = hl_memory_scrub,
+};
+
+static const struct file_operations hl_data32b_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_data_read32,
+ .write = hl_data_write32
+};
+
+static const struct file_operations hl_data64b_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_data_read64,
+ .write = hl_data_write64
+};
+
+static const struct file_operations hl_dma_size_fops = {
+ .owner = THIS_MODULE,
+ .write = hl_dma_size_write
+};
+
+static const struct file_operations hl_monitor_dump_fops = {
+ .owner = THIS_MODULE,
+ .write = hl_monitor_dump_trigger
+};
+
+static const struct file_operations hl_i2c_data_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_i2c_data_read,
+ .write = hl_i2c_data_write
+};
+
+static const struct file_operations hl_power_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_get_power_state,
+ .write = hl_set_power_state
+};
+
+static const struct file_operations hl_led0_fops = {
+ .owner = THIS_MODULE,
+ .write = hl_led0_write
+};
+
+static const struct file_operations hl_led1_fops = {
+ .owner = THIS_MODULE,
+ .write = hl_led1_write
+};
+
+static const struct file_operations hl_led2_fops = {
+ .owner = THIS_MODULE,
+ .write = hl_led2_write
+};
+
+static const struct file_operations hl_device_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_device_read,
+ .write = hl_device_write
+};
+
+static const struct file_operations hl_clk_gate_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_clk_gate_read,
+ .write = hl_clk_gate_write
+};
+
+static const struct file_operations hl_stop_on_err_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_stop_on_err_read,
+ .write = hl_stop_on_err_write
+};
+
+static const struct file_operations hl_security_violations_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_security_violations_read
+};
+
+static const struct file_operations hl_state_dump_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_state_dump_read,
+ .write = hl_state_dump_write
+};
+
+static const struct file_operations hl_timeout_locked_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_timeout_locked_read,
+ .write = hl_timeout_locked_write
+};
+
+static const struct file_operations hl_razwi_check_fops = {
+ .owner = THIS_MODULE,
+ .read = hl_check_razwi_happened
+};
+
+static const struct hl_info_list hl_debugfs_list[] = {
+ {"command_buffers", command_buffers_show, NULL},
+ {"command_submission", command_submission_show, NULL},
+ {"command_submission_jobs", command_submission_jobs_show, NULL},
+ {"userptr", userptr_show, NULL},
+ {"vm", vm_show, NULL},
+ {"userptr_lookup", userptr_lookup_show, userptr_lookup_write},
+ {"mmu", mmu_show, mmu_asid_va_write},
+ {"mmu_error", mmu_ack_error, mmu_ack_error_value_write},
+ {"engines", engines_show, NULL},
+#ifdef CONFIG_HL_HLDIO
+ /* DIO entries - only created if NVMe is supported */
+ {"dio_ssd2hl", dio_ssd2hl_show, dio_ssd2hl_write},
+ {"dio_stats", dio_stats_show, NULL},
+ {"dio_reset", dio_reset_show, dio_reset_write},
+ {"dio_hl2ssd", dio_hl2ssd_show, dio_hl2ssd_write},
+#endif
+};
+
+static int hl_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct hl_debugfs_entry *node = inode->i_private;
+
+ return single_open(file, node->info_ent->show, node);
+}
+
+static ssize_t hl_debugfs_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct hl_debugfs_entry *node = file->f_inode->i_private;
+
+ if (node->info_ent->write)
+ return node->info_ent->write(file, buf, count, f_pos);
+ else
+ return -EINVAL;
+
+}
+
+static const struct file_operations hl_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = hl_debugfs_open,
+ .read = seq_read,
+ .write = hl_debugfs_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void add_secured_nodes(struct hl_dbg_device_entry *dev_entry, struct dentry *root)
+{
+ debugfs_create_u8("i2c_bus",
+ 0644,
+ root,
+ &dev_entry->i2c_bus);
+
+ debugfs_create_u8("i2c_addr",
+ 0644,
+ root,
+ &dev_entry->i2c_addr);
+
+ debugfs_create_u8("i2c_reg",
+ 0644,
+ root,
+ &dev_entry->i2c_reg);
+
+ debugfs_create_u8("i2c_len",
+ 0644,
+ root,
+ &dev_entry->i2c_len);
+
+ debugfs_create_file("i2c_data",
+ 0644,
+ root,
+ dev_entry,
+ &hl_i2c_data_fops);
+
+ debugfs_create_file("led0",
+ 0200,
+ root,
+ dev_entry,
+ &hl_led0_fops);
+
+ debugfs_create_file("led1",
+ 0200,
+ root,
+ dev_entry,
+ &hl_led1_fops);
+
+ debugfs_create_file("led2",
+ 0200,
+ root,
+ dev_entry,
+ &hl_led2_fops);
+}
+
+static void add_files_to_device(struct hl_device *hdev, struct hl_dbg_device_entry *dev_entry,
+ struct dentry *root)
+{
+ int count = ARRAY_SIZE(hl_debugfs_list);
+ struct hl_debugfs_entry *entry;
+ int i;
+
+ debugfs_create_x64("memory_scrub_val",
+ 0644,
+ root,
+ &hdev->memory_scrub_val);
+
+ debugfs_create_file("memory_scrub",
+ 0200,
+ root,
+ dev_entry,
+ &hl_mem_scrub_fops);
+
+ debugfs_create_x64("addr",
+ 0644,
+ root,
+ &dev_entry->addr);
+
+ debugfs_create_file("data32",
+ 0644,
+ root,
+ dev_entry,
+ &hl_data32b_fops);
+
+ debugfs_create_file("data64",
+ 0644,
+ root,
+ dev_entry,
+ &hl_data64b_fops);
+
+ debugfs_create_file("set_power_state",
+ 0644,
+ root,
+ dev_entry,
+ &hl_power_fops);
+
+ debugfs_create_file("device",
+ 0644,
+ root,
+ dev_entry,
+ &hl_device_fops);
+
+ debugfs_create_file("clk_gate",
+ 0644,
+ root,
+ dev_entry,
+ &hl_clk_gate_fops);
+
+ debugfs_create_file("stop_on_err",
+ 0644,
+ root,
+ dev_entry,
+ &hl_stop_on_err_fops);
+
+ debugfs_create_file("dump_security_violations",
+ 0400,
+ root,
+ dev_entry,
+ &hl_security_violations_fops);
+
+ debugfs_create_file("dump_razwi_events",
+ 0400,
+ root,
+ dev_entry,
+ &hl_razwi_check_fops);
+
+ debugfs_create_file("dma_size",
+ 0200,
+ root,
+ dev_entry,
+ &hl_dma_size_fops);
+
+ debugfs_create_blob("data_dma",
+ 0400,
+ root,
+ &dev_entry->data_dma_blob_desc);
+
+ debugfs_create_file("monitor_dump_trig",
+ 0200,
+ root,
+ dev_entry,
+ &hl_monitor_dump_fops);
+
+ debugfs_create_blob("monitor_dump",
+ 0400,
+ root,
+ &dev_entry->mon_dump_blob_desc);
+
+ debugfs_create_x8("skip_reset_on_timeout",
+ 0644,
+ root,
+ &hdev->reset_info.skip_reset_on_timeout);
+
+ debugfs_create_file("state_dump",
+ 0644,
+ root,
+ dev_entry,
+ &hl_state_dump_fops);
+
+ debugfs_create_file("timeout_locked",
+ 0644,
+ root,
+ dev_entry,
+ &hl_timeout_locked_fops);
+
+ debugfs_create_u32("device_release_watchdog_timeout",
+ 0644,
+ root,
+ &hdev->device_release_watchdog_timeout_sec);
+
+ debugfs_create_u16("server_type",
+ 0444,
+ root,
+ &hdev->asic_prop.server_type);
+
+ for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) {
+ /* Skip DIO entries if NVMe is not supported */
+ if (strncmp(hl_debugfs_list[i].name, "dio_", 4) == 0 &&
+ !hdev->asic_prop.supports_nvme)
+ continue;
+
+ debugfs_create_file(hl_debugfs_list[i].name,
+ 0644,
+ root,
+ entry,
+ &hl_debugfs_fops);
+ entry->info_ent = &hl_debugfs_list[i];
+ entry->dev_entry = dev_entry;
+ }
+}
+
+int hl_debugfs_device_init(struct hl_device *hdev)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+ int count = ARRAY_SIZE(hl_debugfs_list);
+
+ dev_entry->hdev = hdev;
+ dev_entry->entry_arr = kmalloc_array(count, sizeof(struct hl_debugfs_entry), GFP_KERNEL);
+ if (!dev_entry->entry_arr)
+ return -ENOMEM;
+
+ dev_entry->data_dma_blob_desc.size = 0;
+ dev_entry->data_dma_blob_desc.data = NULL;
+ dev_entry->mon_dump_blob_desc.size = 0;
+ dev_entry->mon_dump_blob_desc.data = NULL;
+
+ INIT_LIST_HEAD(&dev_entry->file_list);
+ INIT_LIST_HEAD(&dev_entry->cb_list);
+ INIT_LIST_HEAD(&dev_entry->cs_list);
+ INIT_LIST_HEAD(&dev_entry->cs_job_list);
+ INIT_LIST_HEAD(&dev_entry->userptr_list);
+ INIT_LIST_HEAD(&dev_entry->ctx_mem_hash_list);
+ mutex_init(&dev_entry->file_mutex);
+ init_rwsem(&dev_entry->state_dump_sem);
+ spin_lock_init(&dev_entry->cb_spinlock);
+ spin_lock_init(&dev_entry->cs_spinlock);
+ spin_lock_init(&dev_entry->cs_job_spinlock);
+ spin_lock_init(&dev_entry->userptr_spinlock);
+ mutex_init(&dev_entry->ctx_mem_hash_mutex);
+
+ spin_lock_init(&hdev->debugfs_cfg_accesses.lock);
+ hdev->debugfs_cfg_accesses.head = 0; /* already zero by alloc but explicit init is fine */
+
+#ifdef CONFIG_HL_HLDIO
+ /* Initialize DIO statistics */
+ memset(&dev_entry->dio_stats, 0, sizeof(dev_entry->dio_stats));
+#endif
+
+ return 0;
+}
+
+void hl_debugfs_device_fini(struct hl_device *hdev)
+{
+ struct hl_dbg_device_entry *entry = &hdev->hl_debugfs;
+ int i;
+
+ mutex_destroy(&entry->ctx_mem_hash_mutex);
+ mutex_destroy(&entry->file_mutex);
+
+ vfree(entry->data_dma_blob_desc.data);
+ vfree(entry->mon_dump_blob_desc.data);
+
+ for (i = 0; i < ARRAY_SIZE(entry->state_dump); ++i)
+ vfree(entry->state_dump[i]);
+
+ kfree(entry->entry_arr);
+
+}
+
+void hl_debugfs_add_device(struct hl_device *hdev)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ dev_entry->root = hdev->drm.accel->debugfs_root;
+
+ add_files_to_device(hdev, dev_entry, dev_entry->root);
+
+ if (!hdev->asic_prop.fw_security_enabled)
+ add_secured_nodes(dev_entry, dev_entry->root);
+
+}
+
+void hl_debugfs_add_file(struct hl_fpriv *hpriv)
+{
+ struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs;
+
+ mutex_lock(&dev_entry->file_mutex);
+ list_add(&hpriv->debugfs_list, &dev_entry->file_list);
+ mutex_unlock(&dev_entry->file_mutex);
+}
+
+void hl_debugfs_remove_file(struct hl_fpriv *hpriv)
+{
+ struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs;
+
+ mutex_lock(&dev_entry->file_mutex);
+ list_del(&hpriv->debugfs_list);
+ mutex_unlock(&dev_entry->file_mutex);
+}
+
+void hl_debugfs_add_cb(struct hl_cb *cb)
+{
+ struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->cb_spinlock);
+ list_add(&cb->debugfs_list, &dev_entry->cb_list);
+ spin_unlock(&dev_entry->cb_spinlock);
+}
+
+void hl_debugfs_remove_cb(struct hl_cb *cb)
+{
+ struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->cb_spinlock);
+ list_del(&cb->debugfs_list);
+ spin_unlock(&dev_entry->cb_spinlock);
+}
+
+void hl_debugfs_add_cs(struct hl_cs *cs)
+{
+ struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->cs_spinlock);
+ list_add(&cs->debugfs_list, &dev_entry->cs_list);
+ spin_unlock(&dev_entry->cs_spinlock);
+}
+
+void hl_debugfs_remove_cs(struct hl_cs *cs)
+{
+ struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->cs_spinlock);
+ list_del(&cs->debugfs_list);
+ spin_unlock(&dev_entry->cs_spinlock);
+}
+
+void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->cs_job_spinlock);
+ list_add(&job->debugfs_list, &dev_entry->cs_job_list);
+ spin_unlock(&dev_entry->cs_job_spinlock);
+}
+
+void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->cs_job_spinlock);
+ list_del(&job->debugfs_list);
+ spin_unlock(&dev_entry->cs_job_spinlock);
+}
+
+void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->userptr_spinlock);
+ list_add(&userptr->debugfs_list, &dev_entry->userptr_list);
+ spin_unlock(&dev_entry->userptr_spinlock);
+}
+
+void hl_debugfs_remove_userptr(struct hl_device *hdev,
+ struct hl_userptr *userptr)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ spin_lock(&dev_entry->userptr_spinlock);
+ list_del(&userptr->debugfs_list);
+ spin_unlock(&dev_entry->userptr_spinlock);
+}
+
+void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ mutex_lock(&dev_entry->ctx_mem_hash_mutex);
+ list_add(&ctx->debugfs_list, &dev_entry->ctx_mem_hash_list);
+ mutex_unlock(&dev_entry->ctx_mem_hash_mutex);
+}
+
+void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ mutex_lock(&dev_entry->ctx_mem_hash_mutex);
+ list_del(&ctx->debugfs_list);
+ mutex_unlock(&dev_entry->ctx_mem_hash_mutex);
+}
+
+/**
+ * hl_debugfs_set_state_dump - register state dump making it accessible via
+ * debugfs
+ * @hdev: pointer to the device structure
+ * @data: the actual dump data
+ * @length: the length of the data
+ */
+void hl_debugfs_set_state_dump(struct hl_device *hdev, char *data,
+ unsigned long length)
+{
+ struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
+
+ down_write(&dev_entry->state_dump_sem);
+
+ dev_entry->state_dump_head = (dev_entry->state_dump_head + 1) %
+ ARRAY_SIZE(dev_entry->state_dump);
+ vfree(dev_entry->state_dump[dev_entry->state_dump_head]);
+ dev_entry->state_dump[dev_entry->state_dump_head] = data;
+
+ up_write(&dev_entry->state_dump_sem);
+}
+
diff --git a/drivers/accel/habanalabs/common/decoder.c b/drivers/accel/habanalabs/common/decoder.c
new file mode 100644
index 000000000000..c03a6da45d00
--- /dev/null
+++ b/drivers/accel/habanalabs/common/decoder.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+
+#define VCMD_CONTROL_OFFSET 0x40 /* SWREG16 */
+#define VCMD_IRQ_STATUS_OFFSET 0x44 /* SWREG17 */
+
+#define VCMD_IRQ_STATUS_ENDCMD_MASK 0x1
+#define VCMD_IRQ_STATUS_BUSERR_MASK 0x2
+#define VCMD_IRQ_STATUS_TIMEOUT_MASK 0x4
+#define VCMD_IRQ_STATUS_CMDERR_MASK 0x8
+#define VCMD_IRQ_STATUS_ABORT_MASK 0x10
+#define VCMD_IRQ_STATUS_RESET_MASK 0x20
+
+static void dec_print_abnrm_intr_source(struct hl_device *hdev, u32 irq_status)
+{
+ const char *format = "abnormal interrupt source:%s%s%s%s%s%s\n";
+ char *intr_source[6] = {"Unknown", "", "", "", "", ""};
+ int i = 0;
+
+ if (!irq_status)
+ return;
+
+ if (irq_status & VCMD_IRQ_STATUS_ENDCMD_MASK)
+ intr_source[i++] = " ENDCMD";
+ if (irq_status & VCMD_IRQ_STATUS_BUSERR_MASK)
+ intr_source[i++] = " BUSERR";
+ if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK)
+ intr_source[i++] = " TIMEOUT";
+ if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
+ intr_source[i++] = " CMDERR";
+ if (irq_status & VCMD_IRQ_STATUS_ABORT_MASK)
+ intr_source[i++] = " ABORT";
+ if (irq_status & VCMD_IRQ_STATUS_RESET_MASK)
+ intr_source[i++] = " RESET";
+
+ dev_err(hdev->dev, format, intr_source[0], intr_source[1],
+ intr_source[2], intr_source[3], intr_source[4], intr_source[5]);
+}
+
+static void dec_abnrm_intr_work(struct work_struct *work)
+{
+ struct hl_dec *dec = container_of(work, struct hl_dec, abnrm_intr_work);
+ struct hl_device *hdev = dec->hdev;
+ u32 irq_status, event_mask = 0;
+ bool reset_required = false;
+
+ irq_status = RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);
+
+ dev_err(hdev->dev, "Decoder abnormal interrupt %#x, core %d\n", irq_status, dec->core_id);
+
+ dec_print_abnrm_intr_source(hdev, irq_status);
+
+ /* Clear the interrupt */
+ WREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET, irq_status);
+
+ /* Flush the interrupt clear */
+ RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);
+
+ if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK) {
+ reset_required = true;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ }
+
+ if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
+ event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;
+
+ if (irq_status & (VCMD_IRQ_STATUS_ENDCMD_MASK |
+ VCMD_IRQ_STATUS_BUSERR_MASK |
+ VCMD_IRQ_STATUS_ABORT_MASK))
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+
+ if (reset_required) {
+ event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
+ hl_device_cond_reset(hdev, 0, event_mask);
+ } else if (event_mask) {
+ hl_notifier_event_send_all(hdev, event_mask);
+ }
+}
+
+void hl_dec_fini(struct hl_device *hdev)
+{
+ kfree(hdev->dec);
+}
+
+int hl_dec_init(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_dec *dec;
+ int rc, j;
+
+ /* if max core is 0, nothing to do*/
+ if (!prop->max_dec)
+ return 0;
+
+ hdev->dec = kcalloc(prop->max_dec, sizeof(struct hl_dec), GFP_KERNEL);
+ if (!hdev->dec)
+ return -ENOMEM;
+
+ for (j = 0 ; j < prop->max_dec ; j++) {
+ dec = hdev->dec + j;
+
+ dec->hdev = hdev;
+ INIT_WORK(&dec->abnrm_intr_work, dec_abnrm_intr_work);
+ dec->core_id = j;
+ dec->base_addr = hdev->asic_funcs->get_dec_base_addr(hdev, j);
+ if (!dec->base_addr) {
+ dev_err(hdev->dev, "Invalid base address of decoder %d\n", j);
+ rc = -EINVAL;
+ goto err_dec_fini;
+ }
+ }
+
+ return 0;
+
+err_dec_fini:
+ hl_dec_fini(hdev);
+
+ return rc;
+}
+
+void hl_dec_ctx_fini(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_dec *dec;
+ int j;
+
+ for (j = 0 ; j < prop->max_dec ; j++) {
+ if (!!(prop->decoder_enabled_mask & BIT(j))) {
+ dec = hdev->dec + j;
+ /* Stop the decoder */
+ WREG32(dec->base_addr + VCMD_CONTROL_OFFSET, 0);
+ }
+ }
+}
diff --git a/drivers/accel/habanalabs/common/device.c b/drivers/accel/habanalabs/common/device.c
new file mode 100644
index 000000000000..999c92d7036e
--- /dev/null
+++ b/drivers/accel/habanalabs/common/device.c
@@ -0,0 +1,2968 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#define pr_fmt(fmt) "habanalabs: " fmt
+
+#include <uapi/drm/habanalabs_accel.h>
+#include "habanalabs.h"
+
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+#include <linux/vmalloc.h>
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+
+#include <trace/events/habanalabs.h>
+
+#define HL_RESET_DELAY_USEC 10000 /* 10ms */
+
+#define HL_DEVICE_RELEASE_WATCHDOG_TIMEOUT_SEC 30
+
+enum dma_alloc_type {
+ DMA_ALLOC_COHERENT,
+ DMA_ALLOC_POOL,
+};
+
+#define MEM_SCRUB_DEFAULT_VAL 0x1122334455667788
+
+static void hl_device_heartbeat(struct work_struct *work);
+
+/*
+ * hl_set_dram_bar- sets the bar to allow later access to address
+ *
+ * @hdev: pointer to habanalabs device structure.
+ * @addr: the address the caller wants to access.
+ * @region: the PCI region.
+ * @new_bar_region_base: the new BAR region base address.
+ *
+ * @return: the old BAR base address on success, U64_MAX for failure.
+ * The caller should set it back to the old address after use.
+ *
+ * In case the bar space does not cover the whole address space,
+ * the bar base address should be set to allow access to a given address.
+ * This function can be called also if the bar doesn't need to be set,
+ * in that case it just won't change the base.
+ */
+static u64 hl_set_dram_bar(struct hl_device *hdev, u64 addr, struct pci_mem_region *region,
+ u64 *new_bar_region_base)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 bar_base_addr, old_base;
+
+ if (is_power_of_2(prop->dram_pci_bar_size))
+ bar_base_addr = addr & ~(prop->dram_pci_bar_size - 0x1ull);
+ else
+ bar_base_addr = region->region_base +
+ div64_u64((addr - region->region_base), prop->dram_pci_bar_size) *
+ prop->dram_pci_bar_size;
+
+ old_base = hdev->asic_funcs->set_dram_bar_base(hdev, bar_base_addr);
+
+ /* in case of success we need to update the new BAR base */
+ if ((old_base != U64_MAX) && new_bar_region_base)
+ *new_bar_region_base = bar_base_addr;
+
+ return old_base;
+}
+
+int hl_access_sram_dram_region(struct hl_device *hdev, u64 addr, u64 *val,
+ enum debugfs_access_type acc_type, enum pci_region region_type, bool set_dram_bar)
+{
+ struct pci_mem_region *region = &hdev->pci_mem_region[region_type];
+ u64 old_base = 0, rc, bar_region_base = region->region_base;
+ void __iomem *acc_addr;
+
+ if (set_dram_bar) {
+ old_base = hl_set_dram_bar(hdev, addr, region, &bar_region_base);
+ if (old_base == U64_MAX)
+ return -EIO;
+ }
+
+ acc_addr = hdev->pcie_bar[region->bar_id] + region->offset_in_bar +
+ (addr - bar_region_base);
+
+ switch (acc_type) {
+ case DEBUGFS_READ8:
+ *val = readb(acc_addr);
+ break;
+ case DEBUGFS_WRITE8:
+ writeb(*val, acc_addr);
+ break;
+ case DEBUGFS_READ32:
+ *val = readl(acc_addr);
+ break;
+ case DEBUGFS_WRITE32:
+ writel(*val, acc_addr);
+ break;
+ case DEBUGFS_READ64:
+ *val = readq(acc_addr);
+ break;
+ case DEBUGFS_WRITE64:
+ writeq(*val, acc_addr);
+ break;
+ }
+
+ if (set_dram_bar) {
+ rc = hl_set_dram_bar(hdev, old_base, region, NULL);
+ if (rc == U64_MAX)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void *hl_dma_alloc_common(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
+ gfp_t flag, enum dma_alloc_type alloc_type,
+ const char *caller)
+{
+ void *ptr = NULL;
+
+ switch (alloc_type) {
+ case DMA_ALLOC_COHERENT:
+ ptr = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, size, dma_handle, flag);
+ break;
+ case DMA_ALLOC_POOL:
+ ptr = hdev->asic_funcs->asic_dma_pool_zalloc(hdev, size, flag, dma_handle);
+ break;
+ }
+
+ if (trace_habanalabs_dma_alloc_enabled() && !ZERO_OR_NULL_PTR(ptr))
+ trace_habanalabs_dma_alloc(&(hdev)->pdev->dev, (u64) (uintptr_t) ptr, *dma_handle,
+ size, caller);
+
+ return ptr;
+}
+
+static void hl_asic_dma_free_common(struct hl_device *hdev, size_t size, void *cpu_addr,
+ dma_addr_t dma_handle, enum dma_alloc_type alloc_type,
+ const char *caller)
+{
+ /* this is needed to avoid warning on using freed pointer */
+ u64 store_cpu_addr = (u64) (uintptr_t) cpu_addr;
+
+ switch (alloc_type) {
+ case DMA_ALLOC_COHERENT:
+ hdev->asic_funcs->asic_dma_free_coherent(hdev, size, cpu_addr, dma_handle);
+ break;
+ case DMA_ALLOC_POOL:
+ hdev->asic_funcs->asic_dma_pool_free(hdev, cpu_addr, dma_handle);
+ break;
+ }
+
+ trace_habanalabs_dma_free(&(hdev)->pdev->dev, store_cpu_addr, dma_handle, size, caller);
+}
+
+void *hl_asic_dma_alloc_coherent_caller(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
+ gfp_t flag, const char *caller)
+{
+ return hl_dma_alloc_common(hdev, size, dma_handle, flag, DMA_ALLOC_COHERENT, caller);
+}
+
+void hl_asic_dma_free_coherent_caller(struct hl_device *hdev, size_t size, void *cpu_addr,
+ dma_addr_t dma_handle, const char *caller)
+{
+ hl_asic_dma_free_common(hdev, size, cpu_addr, dma_handle, DMA_ALLOC_COHERENT, caller);
+}
+
+void *hl_asic_dma_pool_zalloc_caller(struct hl_device *hdev, size_t size, gfp_t mem_flags,
+ dma_addr_t *dma_handle, const char *caller)
+{
+ return hl_dma_alloc_common(hdev, size, dma_handle, mem_flags, DMA_ALLOC_POOL, caller);
+}
+
+void hl_asic_dma_pool_free_caller(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr,
+ const char *caller)
+{
+ hl_asic_dma_free_common(hdev, 0, vaddr, dma_addr, DMA_ALLOC_POOL, caller);
+}
+
+void *hl_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle)
+{
+ return hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, size, dma_handle);
+}
+
+void hl_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr)
+{
+ hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, size, vaddr);
+}
+
+int hl_dma_map_sgtable_caller(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir, const char *caller)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct scatterlist *sg;
+ int rc, i;
+
+ rc = hdev->asic_funcs->dma_map_sgtable(hdev, sgt, dir);
+ if (rc)
+ return rc;
+
+ if (!trace_habanalabs_dma_map_page_enabled())
+ return 0;
+
+ for_each_sgtable_dma_sg(sgt, sg, i)
+ trace_habanalabs_dma_map_page(&(hdev)->pdev->dev,
+ page_to_phys(sg_page(sg)),
+ sg->dma_address - prop->device_dma_offset_for_host_access,
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+ sg->dma_length,
+#else
+ sg->length,
+#endif
+ dir, caller);
+
+ return 0;
+}
+
+int hl_asic_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct scatterlist *sg;
+ int rc, i;
+
+ rc = dma_map_sgtable(&hdev->pdev->dev, sgt, dir, 0);
+ if (rc)
+ return rc;
+
+ /* Shift to the device's base physical address of host memory if necessary */
+ if (prop->device_dma_offset_for_host_access)
+ for_each_sgtable_dma_sg(sgt, sg, i)
+ sg->dma_address += prop->device_dma_offset_for_host_access;
+
+ return 0;
+}
+
+void hl_dma_unmap_sgtable_caller(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir, const char *caller)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct scatterlist *sg;
+ int i;
+
+ hdev->asic_funcs->dma_unmap_sgtable(hdev, sgt, dir);
+
+ if (trace_habanalabs_dma_unmap_page_enabled()) {
+ for_each_sgtable_dma_sg(sgt, sg, i)
+ trace_habanalabs_dma_unmap_page(&(hdev)->pdev->dev,
+ page_to_phys(sg_page(sg)),
+ sg->dma_address - prop->device_dma_offset_for_host_access,
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+ sg->dma_length,
+#else
+ sg->length,
+#endif
+ dir, caller);
+ }
+}
+
+void hl_asic_dma_unmap_sgtable(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct scatterlist *sg;
+ int i;
+
+ /* Cancel the device's base physical address of host memory if necessary */
+ if (prop->device_dma_offset_for_host_access)
+ for_each_sgtable_dma_sg(sgt, sg, i)
+ sg->dma_address -= prop->device_dma_offset_for_host_access;
+
+ dma_unmap_sgtable(&hdev->pdev->dev, sgt, dir, 0);
+}
+
+/*
+ * hl_access_cfg_region - access the config region
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @addr: the address to access
+ * @val: the value to write from or read to
+ * @acc_type: the type of access (read/write 64/32)
+ */
+int hl_access_cfg_region(struct hl_device *hdev, u64 addr, u64 *val,
+ enum debugfs_access_type acc_type)
+{
+ struct pci_mem_region *cfg_region = &hdev->pci_mem_region[PCI_REGION_CFG];
+ u32 val_h, val_l;
+
+ if (!IS_ALIGNED(addr, sizeof(u32))) {
+ dev_err(hdev->dev, "address %#llx not a multiple of %zu\n", addr, sizeof(u32));
+ return -EINVAL;
+ }
+
+ switch (acc_type) {
+ case DEBUGFS_READ32:
+ *val = RREG32(addr - cfg_region->region_base);
+ break;
+ case DEBUGFS_WRITE32:
+ WREG32(addr - cfg_region->region_base, *val);
+ break;
+ case DEBUGFS_READ64:
+ val_l = RREG32(addr - cfg_region->region_base);
+ val_h = RREG32(addr + sizeof(u32) - cfg_region->region_base);
+
+ *val = (((u64) val_h) << 32) | val_l;
+ break;
+ case DEBUGFS_WRITE64:
+ WREG32(addr - cfg_region->region_base, lower_32_bits(*val));
+ WREG32(addr + sizeof(u32) - cfg_region->region_base, upper_32_bits(*val));
+ break;
+ default:
+ dev_err(hdev->dev, "access type %d is not supported\n", acc_type);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+ * hl_access_dev_mem - access device memory
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @region_type: the type of the region the address belongs to
+ * @addr: the address to access
+ * @val: the value to write from or read to
+ * @acc_type: the type of access (r/w, 32/64)
+ */
+int hl_access_dev_mem(struct hl_device *hdev, enum pci_region region_type,
+ u64 addr, u64 *val, enum debugfs_access_type acc_type)
+{
+ switch (region_type) {
+ case PCI_REGION_CFG:
+ return hl_access_cfg_region(hdev, addr, val, acc_type);
+ case PCI_REGION_SRAM:
+ case PCI_REGION_DRAM:
+ return hl_access_sram_dram_region(hdev, addr, val, acc_type,
+ region_type, (region_type == PCI_REGION_DRAM));
+ default:
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...)
+{
+ va_list args;
+ int str_size;
+
+ va_start(args, fmt);
+ /* Calculate formatted string length. Assuming each string is null terminated, hence
+ * increment result by 1
+ */
+ str_size = vsnprintf(NULL, 0, fmt, args) + 1;
+ va_end(args);
+
+ if ((e->actual_size + str_size) < e->allocated_buf_size) {
+ va_start(args, fmt);
+ vsnprintf(e->buf + e->actual_size, str_size, fmt, args);
+ va_end(args);
+ }
+
+ /* Need to update the size even when not updating destination buffer to get the exact size
+ * of all input strings
+ */
+ e->actual_size += str_size;
+}
+
+enum hl_device_status hl_device_status(struct hl_device *hdev)
+{
+ enum hl_device_status status;
+
+ if (hdev->device_fini_pending) {
+ status = HL_DEVICE_STATUS_MALFUNCTION;
+ } else if (hdev->reset_info.in_reset) {
+ if (hdev->reset_info.in_compute_reset)
+ status = HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE;
+ else
+ status = HL_DEVICE_STATUS_IN_RESET;
+ } else if (hdev->reset_info.needs_reset) {
+ status = HL_DEVICE_STATUS_NEEDS_RESET;
+ } else if (hdev->disabled) {
+ status = HL_DEVICE_STATUS_MALFUNCTION;
+ } else if (!hdev->init_done) {
+ status = HL_DEVICE_STATUS_IN_DEVICE_CREATION;
+ } else {
+ status = HL_DEVICE_STATUS_OPERATIONAL;
+ }
+
+ return status;
+}
+
+bool hl_device_operational(struct hl_device *hdev,
+ enum hl_device_status *status)
+{
+ enum hl_device_status current_status;
+
+ current_status = hl_device_status(hdev);
+ if (status)
+ *status = current_status;
+
+ switch (current_status) {
+ case HL_DEVICE_STATUS_MALFUNCTION:
+ case HL_DEVICE_STATUS_IN_RESET:
+ case HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE:
+ case HL_DEVICE_STATUS_NEEDS_RESET:
+ return false;
+ case HL_DEVICE_STATUS_OPERATIONAL:
+ case HL_DEVICE_STATUS_IN_DEVICE_CREATION:
+ default:
+ return true;
+ }
+}
+
+bool hl_ctrl_device_operational(struct hl_device *hdev,
+ enum hl_device_status *status)
+{
+ enum hl_device_status current_status;
+
+ current_status = hl_device_status(hdev);
+ if (status)
+ *status = current_status;
+
+ switch (current_status) {
+ case HL_DEVICE_STATUS_MALFUNCTION:
+ return false;
+ case HL_DEVICE_STATUS_IN_RESET:
+ case HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE:
+ case HL_DEVICE_STATUS_NEEDS_RESET:
+ case HL_DEVICE_STATUS_OPERATIONAL:
+ case HL_DEVICE_STATUS_IN_DEVICE_CREATION:
+ default:
+ return true;
+ }
+}
+
+static void print_idle_status_mask(struct hl_device *hdev, const char *message,
+ u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE])
+{
+ if (idle_mask[3])
+ dev_err(hdev->dev, "%s %s (mask %#llx_%016llx_%016llx_%016llx)\n",
+ dev_name(&hdev->pdev->dev), message,
+ idle_mask[3], idle_mask[2], idle_mask[1], idle_mask[0]);
+ else if (idle_mask[2])
+ dev_err(hdev->dev, "%s %s (mask %#llx_%016llx_%016llx)\n",
+ dev_name(&hdev->pdev->dev), message,
+ idle_mask[2], idle_mask[1], idle_mask[0]);
+ else if (idle_mask[1])
+ dev_err(hdev->dev, "%s %s (mask %#llx_%016llx)\n",
+ dev_name(&hdev->pdev->dev), message, idle_mask[1], idle_mask[0]);
+ else
+ dev_err(hdev->dev, "%s %s (mask %#llx)\n", dev_name(&hdev->pdev->dev), message,
+ idle_mask[0]);
+}
+
+static void hpriv_release(struct kref *ref)
+{
+ u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0};
+ bool reset_device, device_is_idle = true;
+ struct hl_fpriv *hpriv;
+ struct hl_device *hdev;
+
+ hpriv = container_of(ref, struct hl_fpriv, refcount);
+
+ hdev = hpriv->hdev;
+
+ hdev->asic_funcs->send_device_activity(hdev, false);
+
+ hl_debugfs_remove_file(hpriv);
+
+ mutex_destroy(&hpriv->ctx_lock);
+ mutex_destroy(&hpriv->restore_phase_mutex);
+
+ /* There should be no memory buffers at this point and handles IDR can be destroyed */
+ hl_mem_mgr_idr_destroy(&hpriv->mem_mgr);
+
+ /* Device should be reset if reset-upon-device-release is enabled, or if there is a pending
+ * reset that waits for device release.
+ */
+ reset_device = hdev->reset_upon_device_release || hdev->reset_info.watchdog_active;
+
+ /* Check the device idle status and reset if not idle.
+ * Skip it if already in reset, or if device is going to be reset in any case.
+ */
+ if (!hdev->reset_info.in_reset && !reset_device && !hdev->pldm)
+ device_is_idle = hdev->asic_funcs->is_device_idle(hdev, idle_mask,
+ HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL);
+ if (!device_is_idle) {
+ print_idle_status_mask(hdev, "device is not idle after user context is closed",
+ idle_mask);
+ reset_device = true;
+ }
+
+ /* We need to remove the user from the list to make sure the reset process won't
+ * try to kill the user process. Because, if we got here, it means there are no
+ * more driver/device resources that the user process is occupying so there is
+ * no need to kill it
+ *
+ * However, we can't set the compute_ctx to NULL at this stage. This is to prevent
+ * a race between the release and opening the device again. We don't want to let
+ * a user open the device while there a reset is about to happen.
+ */
+ mutex_lock(&hdev->fpriv_list_lock);
+ list_del(&hpriv->dev_node);
+ mutex_unlock(&hdev->fpriv_list_lock);
+
+ put_pid(hpriv->taskpid);
+
+ if (reset_device) {
+ hl_device_reset(hdev, HL_DRV_RESET_DEV_RELEASE);
+ } else {
+ /* Scrubbing is handled within hl_device_reset(), so here need to do it directly */
+ int rc = hdev->asic_funcs->scrub_device_mem(hdev);
+
+ if (rc) {
+ dev_err(hdev->dev, "failed to scrub memory from hpriv release (%d)\n", rc);
+ hl_device_reset(hdev, HL_DRV_RESET_HARD);
+ }
+ }
+
+ /* Now we can mark the compute_ctx as not active. Even if a reset is running in a different
+ * thread, we don't care because the in_reset is marked so if a user will try to open
+ * the device it will fail on that, even if compute_ctx is false.
+ */
+ mutex_lock(&hdev->fpriv_list_lock);
+ hdev->is_compute_ctx_active = false;
+ mutex_unlock(&hdev->fpriv_list_lock);
+
+ hdev->compute_ctx_in_release = 0;
+
+ /* release the eventfd */
+ if (hpriv->notifier_event.eventfd)
+ eventfd_ctx_put(hpriv->notifier_event.eventfd);
+
+ mutex_destroy(&hpriv->notifier_event.lock);
+
+ kfree(hpriv);
+}
+
+void hl_hpriv_get(struct hl_fpriv *hpriv)
+{
+ kref_get(&hpriv->refcount);
+}
+
+int hl_hpriv_put(struct hl_fpriv *hpriv)
+{
+ return kref_put(&hpriv->refcount, hpriv_release);
+}
+
+static void print_device_in_use_info(struct hl_device *hdev,
+ struct hl_mem_mgr_fini_stats *mm_fini_stats, const char *message)
+{
+ u32 active_cs_num, dmabuf_export_cnt;
+ bool unknown_reason = true;
+ char buf[128];
+ size_t size;
+ int offset;
+
+ size = sizeof(buf);
+ offset = 0;
+
+ active_cs_num = hl_get_active_cs_num(hdev);
+ if (active_cs_num) {
+ unknown_reason = false;
+ offset += scnprintf(buf + offset, size - offset, " [%u active CS]", active_cs_num);
+ }
+
+ dmabuf_export_cnt = atomic_read(&hdev->dmabuf_export_cnt);
+ if (dmabuf_export_cnt) {
+ unknown_reason = false;
+ offset += scnprintf(buf + offset, size - offset, " [%u exported dma-buf]",
+ dmabuf_export_cnt);
+ }
+
+ if (mm_fini_stats->n_busy_cb) {
+ unknown_reason = false;
+ offset += scnprintf(buf + offset, size - offset, " [%u live CB handles]",
+ mm_fini_stats->n_busy_cb);
+ }
+
+ if (unknown_reason)
+ scnprintf(buf + offset, size - offset, " [unknown reason]");
+
+ dev_notice(hdev->dev, "%s%s\n", message, buf);
+}
+
+/*
+ * hl_device_release() - release function for habanalabs device.
+ * @ddev: pointer to DRM device structure.
+ * @file: pointer to DRM file private data structure.
+ *
+ * Called when process closes an habanalabs device
+ */
+void hl_device_release(struct drm_device *ddev, struct drm_file *file_priv)
+{
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
+ struct hl_device *hdev = to_hl_device(ddev);
+ struct hl_mem_mgr_fini_stats mm_fini_stats;
+
+ if (!hdev) {
+ pr_crit("Closing FD after device was removed. Memory leak will occur and it is advised to reboot.\n");
+ put_pid(hpriv->taskpid);
+ }
+
+ hl_ctx_mgr_fini(hdev, &hpriv->ctx_mgr);
+
+ /* Memory buffers might be still in use at this point and thus the handles IDR destruction
+ * is postponed to hpriv_release().
+ */
+ hl_mem_mgr_fini(&hpriv->mem_mgr, &mm_fini_stats);
+
+ hdev->compute_ctx_in_release = 1;
+
+ if (!hl_hpriv_put(hpriv)) {
+ print_device_in_use_info(hdev, &mm_fini_stats,
+ "User process closed FD but device still in use");
+ hl_device_reset(hdev, HL_DRV_RESET_HARD);
+ }
+
+ hdev->last_open_session_duration_jif = jiffies - hdev->last_successful_open_jif;
+}
+
+static int hl_device_release_ctrl(struct inode *inode, struct file *filp)
+{
+ struct hl_fpriv *hpriv = filp->private_data;
+ struct hl_device *hdev = hpriv->hdev;
+
+ filp->private_data = NULL;
+
+ if (!hdev) {
+ pr_err("Closing FD after device was removed\n");
+ goto out;
+ }
+
+ mutex_lock(&hdev->fpriv_ctrl_list_lock);
+ list_del(&hpriv->dev_node);
+ mutex_unlock(&hdev->fpriv_ctrl_list_lock);
+out:
+ put_pid(hpriv->taskpid);
+
+ kfree(hpriv);
+
+ return 0;
+}
+
+static int __hl_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ unsigned long vm_pgoff;
+
+ if (!hdev) {
+ pr_err_ratelimited("Trying to mmap after device was removed! Please close FD\n");
+ return -ENODEV;
+ }
+
+ vm_pgoff = vma->vm_pgoff;
+
+ switch (vm_pgoff & HL_MMAP_TYPE_MASK) {
+ case HL_MMAP_TYPE_BLOCK:
+ vma->vm_pgoff = HL_MMAP_OFFSET_VALUE_GET(vm_pgoff);
+ return hl_hw_block_mmap(hpriv, vma);
+
+ case HL_MMAP_TYPE_CB:
+ case HL_MMAP_TYPE_TS_BUFF:
+ return hl_mem_mgr_mmap(&hpriv->mem_mgr, vma, NULL);
+ }
+ return -EINVAL;
+}
+
+/*
+ * hl_mmap - mmap function for habanalabs device
+ *
+ * @*filp: pointer to file structure
+ * @*vma: pointer to vm_area_struct of the process
+ *
+ * Called when process does an mmap on habanalabs device. Call the relevant mmap
+ * function at the end of the common code.
+ */
+int hl_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *file_priv = filp->private_data;
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
+
+ return __hl_mmap(hpriv, vma);
+}
+
+static const struct file_operations hl_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .open = hl_device_open_ctrl,
+ .release = hl_device_release_ctrl,
+ .unlocked_ioctl = hl_ioctl_control,
+ .compat_ioctl = hl_ioctl_control
+};
+
+static void device_release_func(struct device *dev)
+{
+ kfree(dev);
+}
+
+/*
+ * device_init_cdev - Initialize cdev and device for habanalabs device
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @class: pointer to the class object of the device
+ * @minor: minor number of the specific device
+ * @fops: file operations to install for this device
+ * @name: name of the device as it will appear in the filesystem
+ * @cdev: pointer to the char device object that will be initialized
+ * @dev: pointer to the device object that will be initialized
+ *
+ * Initialize a cdev and a Linux device for habanalabs's device.
+ */
+static int device_init_cdev(struct hl_device *hdev, const struct class *class,
+ int minor, const struct file_operations *fops,
+ char *name, struct cdev *cdev,
+ struct device **dev)
+{
+ cdev_init(cdev, fops);
+ cdev->owner = THIS_MODULE;
+
+ *dev = kzalloc(sizeof(**dev), GFP_KERNEL);
+ if (!*dev)
+ return -ENOMEM;
+
+ device_initialize(*dev);
+ (*dev)->devt = MKDEV(hdev->major, minor);
+ (*dev)->class = class;
+ (*dev)->release = device_release_func;
+ dev_set_drvdata(*dev, hdev);
+ dev_set_name(*dev, "%s", name);
+
+ return 0;
+}
+
+static int cdev_sysfs_debugfs_add(struct hl_device *hdev)
+{
+ const struct class *accel_class = hdev->drm.accel->kdev->class;
+ char name[32];
+ int rc;
+
+ hdev->cdev_idx = hdev->drm.accel->index;
+
+ /* Initialize cdev and device structures for the control device */
+ snprintf(name, sizeof(name), "accel_controlD%d", hdev->cdev_idx);
+ rc = device_init_cdev(hdev, accel_class, hdev->cdev_idx, &hl_ctrl_ops, name,
+ &hdev->cdev_ctrl, &hdev->dev_ctrl);
+ if (rc)
+ return rc;
+
+ rc = cdev_device_add(&hdev->cdev_ctrl, hdev->dev_ctrl);
+ if (rc) {
+ dev_err(hdev->dev_ctrl,
+ "failed to add an accel control char device to the system\n");
+ goto free_ctrl_device;
+ }
+
+ rc = hl_sysfs_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize sysfs\n");
+ goto delete_ctrl_cdev_device;
+ }
+
+ hl_debugfs_add_device(hdev);
+
+ hdev->cdev_sysfs_debugfs_created = true;
+
+ return 0;
+
+delete_ctrl_cdev_device:
+ cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl);
+free_ctrl_device:
+ put_device(hdev->dev_ctrl);
+ return rc;
+}
+
+static void cdev_sysfs_debugfs_remove(struct hl_device *hdev)
+{
+ if (!hdev->cdev_sysfs_debugfs_created)
+ return;
+
+ hl_sysfs_fini(hdev);
+
+ cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl);
+ put_device(hdev->dev_ctrl);
+}
+
+static void device_hard_reset_pending(struct work_struct *work)
+{
+ struct hl_device_reset_work *device_reset_work =
+ container_of(work, struct hl_device_reset_work, reset_work.work);
+ struct hl_device *hdev = device_reset_work->hdev;
+ u32 flags;
+ int rc;
+
+ flags = device_reset_work->flags | HL_DRV_RESET_FROM_RESET_THR;
+
+ rc = hl_device_reset(hdev, flags);
+
+ if ((rc == -EBUSY) && !hdev->device_fini_pending) {
+ struct hl_ctx *ctx = hl_get_compute_ctx(hdev);
+
+ if (ctx) {
+ /* The read refcount value should subtracted by one, because the read is
+ * protected with hl_get_compute_ctx().
+ */
+ dev_info(hdev->dev,
+ "Could not reset device (compute_ctx refcount %u). will try again in %u seconds",
+ kref_read(&ctx->refcount) - 1, HL_PENDING_RESET_PER_SEC);
+ hl_ctx_put(ctx);
+ } else {
+ dev_info(hdev->dev, "Could not reset device. will try again in %u seconds",
+ HL_PENDING_RESET_PER_SEC);
+ }
+
+ queue_delayed_work(hdev->reset_wq, &device_reset_work->reset_work,
+ secs_to_jiffies(HL_PENDING_RESET_PER_SEC));
+ }
+}
+
+static void device_release_watchdog_func(struct work_struct *work)
+{
+ struct hl_device_reset_work *watchdog_work =
+ container_of(work, struct hl_device_reset_work, reset_work.work);
+ struct hl_device *hdev = watchdog_work->hdev;
+ u32 flags;
+
+ dev_dbg(hdev->dev, "Device wasn't released in time. Initiate hard-reset.\n");
+
+ flags = watchdog_work->flags | HL_DRV_RESET_HARD | HL_DRV_RESET_FROM_WD_THR;
+
+ hl_device_reset(hdev, flags);
+}
+
+/*
+ * device_early_init - do some early initialization for the habanalabs device
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Install the relevant function pointers and call the early_init function,
+ * if such a function exists
+ */
+static int device_early_init(struct hl_device *hdev)
+{
+ int i, rc;
+ char workq_name[32];
+
+ switch (hdev->asic_type) {
+ case ASIC_GOYA:
+ goya_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GOYA", sizeof(hdev->asic_name));
+ break;
+ case ASIC_GAUDI:
+ gaudi_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GAUDI", sizeof(hdev->asic_name));
+ break;
+ case ASIC_GAUDI_SEC:
+ gaudi_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GAUDI SEC", sizeof(hdev->asic_name));
+ break;
+ case ASIC_GAUDI2:
+ gaudi2_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GAUDI2", sizeof(hdev->asic_name));
+ break;
+ case ASIC_GAUDI2B:
+ gaudi2_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GAUDI2B", sizeof(hdev->asic_name));
+ break;
+ case ASIC_GAUDI2C:
+ gaudi2_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GAUDI2C", sizeof(hdev->asic_name));
+ break;
+ case ASIC_GAUDI2D:
+ gaudi2_set_asic_funcs(hdev);
+ strscpy(hdev->asic_name, "GAUDI2D", sizeof(hdev->asic_name));
+ break;
+ default:
+ dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
+ hdev->asic_type);
+ return -EINVAL;
+ }
+
+ rc = hdev->asic_funcs->early_init(hdev);
+ if (rc)
+ return rc;
+
+ rc = hl_asid_init(hdev);
+ if (rc)
+ goto early_fini;
+
+ if (hdev->asic_prop.completion_queues_count) {
+ hdev->cq_wq = kcalloc(hdev->asic_prop.completion_queues_count,
+ sizeof(struct workqueue_struct *),
+ GFP_KERNEL);
+ if (!hdev->cq_wq) {
+ rc = -ENOMEM;
+ goto asid_fini;
+ }
+ }
+
+ for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) {
+ snprintf(workq_name, 32, "hl%u-free-jobs-%u", hdev->cdev_idx, (u32) i);
+ hdev->cq_wq[i] = create_singlethread_workqueue(workq_name);
+ if (hdev->cq_wq[i] == NULL) {
+ dev_err(hdev->dev, "Failed to allocate CQ workqueue\n");
+ rc = -ENOMEM;
+ goto free_cq_wq;
+ }
+ }
+
+ snprintf(workq_name, 32, "hl%u-events", hdev->cdev_idx);
+ hdev->eq_wq = create_singlethread_workqueue(workq_name);
+ if (hdev->eq_wq == NULL) {
+ dev_err(hdev->dev, "Failed to allocate EQ workqueue\n");
+ rc = -ENOMEM;
+ goto free_cq_wq;
+ }
+
+ snprintf(workq_name, 32, "hl%u-cs-completions", hdev->cdev_idx);
+ hdev->cs_cmplt_wq = alloc_workqueue(workq_name, WQ_UNBOUND, 0);
+ if (!hdev->cs_cmplt_wq) {
+ dev_err(hdev->dev,
+ "Failed to allocate CS completions workqueue\n");
+ rc = -ENOMEM;
+ goto free_eq_wq;
+ }
+
+ snprintf(workq_name, 32, "hl%u-ts-free-obj", hdev->cdev_idx);
+ hdev->ts_free_obj_wq = alloc_workqueue(workq_name, WQ_UNBOUND, 0);
+ if (!hdev->ts_free_obj_wq) {
+ dev_err(hdev->dev,
+ "Failed to allocate Timestamp registration free workqueue\n");
+ rc = -ENOMEM;
+ goto free_cs_cmplt_wq;
+ }
+
+ snprintf(workq_name, 32, "hl%u-prefetch", hdev->cdev_idx);
+ hdev->prefetch_wq = alloc_workqueue(workq_name, WQ_UNBOUND, 0);
+ if (!hdev->prefetch_wq) {
+ dev_err(hdev->dev, "Failed to allocate MMU prefetch workqueue\n");
+ rc = -ENOMEM;
+ goto free_ts_free_wq;
+ }
+
+ hdev->hl_chip_info = kzalloc(sizeof(struct hwmon_chip_info), GFP_KERNEL);
+ if (!hdev->hl_chip_info) {
+ rc = -ENOMEM;
+ goto free_prefetch_wq;
+ }
+
+ rc = hl_mmu_if_set_funcs(hdev);
+ if (rc)
+ goto free_chip_info;
+
+ hl_mem_mgr_init(hdev->dev, &hdev->kernel_mem_mgr);
+
+ snprintf(workq_name, 32, "hl%u_device_reset", hdev->cdev_idx);
+ hdev->reset_wq = create_singlethread_workqueue(workq_name);
+ if (!hdev->reset_wq) {
+ rc = -ENOMEM;
+ dev_err(hdev->dev, "Failed to create device reset WQ\n");
+ goto free_cb_mgr;
+ }
+
+ INIT_DELAYED_WORK(&hdev->work_heartbeat, hl_device_heartbeat);
+
+ INIT_DELAYED_WORK(&hdev->device_reset_work.reset_work, device_hard_reset_pending);
+ hdev->device_reset_work.hdev = hdev;
+ hdev->device_fini_pending = 0;
+
+ INIT_DELAYED_WORK(&hdev->device_release_watchdog_work.reset_work,
+ device_release_watchdog_func);
+ hdev->device_release_watchdog_work.hdev = hdev;
+
+ mutex_init(&hdev->send_cpu_message_lock);
+ mutex_init(&hdev->debug_lock);
+ INIT_LIST_HEAD(&hdev->cs_mirror_list);
+ spin_lock_init(&hdev->cs_mirror_lock);
+ spin_lock_init(&hdev->reset_info.lock);
+ INIT_LIST_HEAD(&hdev->fpriv_list);
+ INIT_LIST_HEAD(&hdev->fpriv_ctrl_list);
+ mutex_init(&hdev->fpriv_list_lock);
+ mutex_init(&hdev->fpriv_ctrl_list_lock);
+ mutex_init(&hdev->clk_throttling.lock);
+
+ return 0;
+
+free_cb_mgr:
+ hl_mem_mgr_fini(&hdev->kernel_mem_mgr, NULL);
+ hl_mem_mgr_idr_destroy(&hdev->kernel_mem_mgr);
+free_chip_info:
+ kfree(hdev->hl_chip_info);
+free_prefetch_wq:
+ destroy_workqueue(hdev->prefetch_wq);
+free_ts_free_wq:
+ destroy_workqueue(hdev->ts_free_obj_wq);
+free_cs_cmplt_wq:
+ destroy_workqueue(hdev->cs_cmplt_wq);
+free_eq_wq:
+ destroy_workqueue(hdev->eq_wq);
+free_cq_wq:
+ for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
+ if (hdev->cq_wq[i])
+ destroy_workqueue(hdev->cq_wq[i]);
+ kfree(hdev->cq_wq);
+asid_fini:
+ hl_asid_fini(hdev);
+early_fini:
+ if (hdev->asic_funcs->early_fini)
+ hdev->asic_funcs->early_fini(hdev);
+
+ return rc;
+}
+
+/*
+ * device_early_fini - finalize all that was done in device_early_init
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ */
+static void device_early_fini(struct hl_device *hdev)
+{
+ int i;
+
+ mutex_destroy(&hdev->debug_lock);
+ mutex_destroy(&hdev->send_cpu_message_lock);
+
+ mutex_destroy(&hdev->fpriv_list_lock);
+ mutex_destroy(&hdev->fpriv_ctrl_list_lock);
+
+ mutex_destroy(&hdev->clk_throttling.lock);
+
+ hl_mem_mgr_fini(&hdev->kernel_mem_mgr, NULL);
+ hl_mem_mgr_idr_destroy(&hdev->kernel_mem_mgr);
+
+ kfree(hdev->hl_chip_info);
+
+ destroy_workqueue(hdev->prefetch_wq);
+ destroy_workqueue(hdev->ts_free_obj_wq);
+ destroy_workqueue(hdev->cs_cmplt_wq);
+ destroy_workqueue(hdev->eq_wq);
+ destroy_workqueue(hdev->reset_wq);
+
+ for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
+ destroy_workqueue(hdev->cq_wq[i]);
+ kfree(hdev->cq_wq);
+
+ hl_asid_fini(hdev);
+
+ if (hdev->asic_funcs->early_fini)
+ hdev->asic_funcs->early_fini(hdev);
+}
+
+static bool is_pci_link_healthy(struct hl_device *hdev)
+{
+ u16 device_id;
+
+ if (!hdev->pdev)
+ return false;
+
+ pci_read_config_word(hdev->pdev, PCI_DEVICE_ID, &device_id);
+
+ return (device_id == hdev->pdev->device);
+}
+
+static bool hl_device_eq_heartbeat_received(struct hl_device *hdev)
+{
+ struct eq_heartbeat_debug_info *heartbeat_debug_info = &hdev->heartbeat_debug_info;
+ u32 cpu_q_id = heartbeat_debug_info->cpu_queue_id, pq_pi_mask = (HL_QUEUE_LENGTH << 1) - 1;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ if (!prop->cpucp_info.eq_health_check_supported)
+ return true;
+
+ if (!hdev->eq_heartbeat_received) {
+ dev_err(hdev->dev, "EQ heartbeat event was not received!\n");
+
+ dev_err(hdev->dev,
+ "EQ: {CI %u, HB counter %u, last HB time: %ptTs}, PQ: {PI: %u, CI: %u (%u), last HB time: %ptTs}\n",
+ hdev->event_queue.ci,
+ heartbeat_debug_info->heartbeat_event_counter,
+ &hdev->heartbeat_debug_info.last_eq_heartbeat_ts,
+ hdev->kernel_queues[cpu_q_id].pi,
+ atomic_read(&hdev->kernel_queues[cpu_q_id].ci),
+ atomic_read(&hdev->kernel_queues[cpu_q_id].ci) & pq_pi_mask,
+ &hdev->heartbeat_debug_info.last_pq_heartbeat_ts);
+
+ hl_eq_dump(hdev, &hdev->event_queue);
+
+ return false;
+ }
+
+ hdev->eq_heartbeat_received = false;
+
+ return true;
+}
+
+static void hl_device_heartbeat(struct work_struct *work)
+{
+ struct hl_device *hdev = container_of(work, struct hl_device,
+ work_heartbeat.work);
+ struct hl_info_fw_err_info info = {0};
+ u64 event_mask = HL_NOTIFIER_EVENT_DEVICE_RESET | HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE;
+
+ /* Start heartbeat checks only after driver has enabled events from FW */
+ if (!hl_device_operational(hdev, NULL) || !hdev->init_done)
+ goto reschedule;
+
+ /*
+ * For EQ health check need to check if driver received the heartbeat eq event
+ * in order to validate the eq is working.
+ * Only if both the EQ is healthy and we managed to send the next heartbeat reschedule.
+ */
+ if (hl_device_eq_heartbeat_received(hdev) && (!hdev->asic_funcs->send_heartbeat(hdev)))
+ goto reschedule;
+
+ if (hl_device_operational(hdev, NULL))
+ dev_err(hdev->dev, "Device heartbeat failed! PCI link is %s\n",
+ is_pci_link_healthy(hdev) ? "healthy" : "broken");
+
+ info.err_type = HL_INFO_FW_HEARTBEAT_ERR;
+ info.event_mask = &event_mask;
+ hl_handle_fw_err(hdev, &info);
+ hl_device_cond_reset(hdev, HL_DRV_RESET_HARD | HL_DRV_RESET_HEARTBEAT, event_mask);
+
+ return;
+
+reschedule:
+ /*
+ * prev_reset_trigger tracks consecutive fatal h/w errors until first
+ * heartbeat immediately post reset.
+ * If control reached here, then at least one heartbeat work has been
+ * scheduled since last reset/init cycle.
+ * So if the device is not already in reset cycle, reset the flag
+ * prev_reset_trigger as no reset occurred with HL_DRV_RESET_FW_FATAL_ERR
+ * status for at least one heartbeat. From this point driver restarts
+ * tracking future consecutive fatal errors.
+ */
+ if (!hdev->reset_info.in_reset)
+ hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT;
+
+ schedule_delayed_work(&hdev->work_heartbeat,
+ usecs_to_jiffies(HL_HEARTBEAT_PER_USEC));
+}
+
+/*
+ * device_late_init - do late stuff initialization for the habanalabs device
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Do stuff that either needs the device H/W queues to be active or needs
+ * to happen after all the rest of the initialization is finished
+ */
+static int device_late_init(struct hl_device *hdev)
+{
+ int rc;
+
+ if (hdev->asic_funcs->late_init) {
+ rc = hdev->asic_funcs->late_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev,
+ "failed late initialization for the H/W\n");
+ return rc;
+ }
+ }
+
+ hdev->high_pll = hdev->asic_prop.high_pll;
+ hdev->late_init_done = true;
+
+ return 0;
+}
+
+/*
+ * device_late_fini - finalize all that was done in device_late_init
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ */
+static void device_late_fini(struct hl_device *hdev)
+{
+ if (!hdev->late_init_done)
+ return;
+
+ if (hdev->asic_funcs->late_fini)
+ hdev->asic_funcs->late_fini(hdev);
+
+ hdev->late_init_done = false;
+}
+
+int hl_device_utilization(struct hl_device *hdev, u32 *utilization)
+{
+ u64 max_power, curr_power, dc_power, dividend, divisor;
+ int rc;
+
+ max_power = hdev->max_power;
+ dc_power = hdev->asic_prop.dc_power_default;
+ divisor = max_power - dc_power;
+ if (!divisor) {
+ dev_warn(hdev->dev, "device utilization is not supported\n");
+ return -EOPNOTSUPP;
+ }
+ rc = hl_fw_cpucp_power_get(hdev, &curr_power);
+
+ if (rc)
+ return rc;
+
+ curr_power = clamp(curr_power, dc_power, max_power);
+
+ dividend = (curr_power - dc_power) * 100;
+ *utilization = (u32) div_u64(dividend, divisor);
+
+ return 0;
+}
+
+int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool enable)
+{
+ int rc = 0;
+
+ mutex_lock(&hdev->debug_lock);
+
+ if (!enable) {
+ if (!hdev->in_debug) {
+ dev_err(hdev->dev,
+ "Failed to disable debug mode because device was not in debug mode\n");
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (!hdev->reset_info.hard_reset_pending)
+ hdev->asic_funcs->halt_coresight(hdev, ctx);
+
+ hdev->in_debug = 0;
+
+ goto out;
+ }
+
+ if (hdev->in_debug) {
+ dev_err(hdev->dev,
+ "Failed to enable debug mode because device is already in debug mode\n");
+ rc = -EFAULT;
+ goto out;
+ }
+
+ hdev->in_debug = 1;
+
+out:
+ mutex_unlock(&hdev->debug_lock);
+
+ return rc;
+}
+
+static void take_release_locks(struct hl_device *hdev)
+{
+ /* Flush anyone that is inside the critical section of enqueue
+ * jobs to the H/W
+ */
+ hdev->asic_funcs->hw_queues_lock(hdev);
+ hdev->asic_funcs->hw_queues_unlock(hdev);
+
+ /* Flush processes that are sending message to CPU */
+ mutex_lock(&hdev->send_cpu_message_lock);
+ mutex_unlock(&hdev->send_cpu_message_lock);
+
+ /* Flush anyone that is inside device open */
+ mutex_lock(&hdev->fpriv_list_lock);
+ mutex_unlock(&hdev->fpriv_list_lock);
+ mutex_lock(&hdev->fpriv_ctrl_list_lock);
+ mutex_unlock(&hdev->fpriv_ctrl_list_lock);
+}
+
+static void hl_abort_waiting_for_completions(struct hl_device *hdev)
+{
+ hl_abort_waiting_for_cs_completions(hdev);
+
+ /* Release all pending user interrupts, each pending user interrupt
+ * holds a reference to a user context.
+ */
+ hl_release_pending_user_interrupts(hdev);
+}
+
+static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset,
+ bool skip_wq_flush)
+{
+ if (hard_reset) {
+ if (hdev->heartbeat)
+ cancel_delayed_work_sync(&hdev->work_heartbeat);
+
+ device_late_fini(hdev);
+ }
+
+ /*
+ * Halt the engines and disable interrupts so we won't get any more
+ * completions from H/W and we won't have any accesses from the
+ * H/W to the host machine
+ */
+ hdev->asic_funcs->halt_engines(hdev, hard_reset, fw_reset);
+
+ /* Go over all the queues, release all CS and their jobs */
+ hl_cs_rollback_all(hdev, skip_wq_flush);
+
+ /* flush the MMU prefetch workqueue */
+ flush_workqueue(hdev->prefetch_wq);
+
+ hl_abort_waiting_for_completions(hdev);
+}
+
+/*
+ * hl_device_suspend - initiate device suspend
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Puts the hw in the suspend state (all asics).
+ * Returns 0 for success or an error on failure.
+ * Called at driver suspend.
+ */
+int hl_device_suspend(struct hl_device *hdev)
+{
+ int rc;
+
+ pci_save_state(hdev->pdev);
+
+ /* Block future CS/VM/JOB completion operations */
+ spin_lock(&hdev->reset_info.lock);
+ if (hdev->reset_info.in_reset) {
+ spin_unlock(&hdev->reset_info.lock);
+ dev_err(hdev->dev, "Can't suspend while in reset\n");
+ return -EIO;
+ }
+ hdev->reset_info.in_reset = 1;
+ spin_unlock(&hdev->reset_info.lock);
+
+ /* This blocks all other stuff that is not blocked by in_reset */
+ hdev->disabled = true;
+
+ take_release_locks(hdev);
+
+ rc = hdev->asic_funcs->suspend(hdev);
+ if (rc)
+ dev_err(hdev->dev,
+ "Failed to disable PCI access of device CPU\n");
+
+ /* Shut down the device */
+ pci_disable_device(hdev->pdev);
+ pci_set_power_state(hdev->pdev, PCI_D3hot);
+
+ return 0;
+}
+
+/*
+ * hl_device_resume - initiate device resume
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Bring the hw back to operating state (all asics).
+ * Returns 0 for success or an error on failure.
+ * Called at driver resume.
+ */
+int hl_device_resume(struct hl_device *hdev)
+{
+ int rc;
+
+ pci_set_power_state(hdev->pdev, PCI_D0);
+ pci_restore_state(hdev->pdev);
+ rc = pci_enable_device_mem(hdev->pdev);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to enable PCI device in resume\n");
+ return rc;
+ }
+
+ pci_set_master(hdev->pdev);
+
+ rc = hdev->asic_funcs->resume(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to resume device after suspend\n");
+ goto disable_device;
+ }
+
+
+ /* 'in_reset' was set to true during suspend, now we must clear it in order
+ * for hard reset to be performed
+ */
+ spin_lock(&hdev->reset_info.lock);
+ hdev->reset_info.in_reset = 0;
+ spin_unlock(&hdev->reset_info.lock);
+
+ rc = hl_device_reset(hdev, HL_DRV_RESET_HARD);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to reset device during resume\n");
+ goto disable_device;
+ }
+
+ return 0;
+
+disable_device:
+ pci_disable_device(hdev->pdev);
+
+ return rc;
+}
+
+static int device_kill_open_processes(struct hl_device *hdev, u32 timeout, bool control_dev)
+{
+ struct task_struct *task = NULL;
+ struct list_head *hpriv_list;
+ struct hl_fpriv *hpriv;
+ struct mutex *hpriv_lock;
+ u32 pending_cnt;
+
+ hpriv_lock = control_dev ? &hdev->fpriv_ctrl_list_lock : &hdev->fpriv_list_lock;
+ hpriv_list = control_dev ? &hdev->fpriv_ctrl_list : &hdev->fpriv_list;
+
+ /* Giving time for user to close FD, and for processes that are inside
+ * hl_device_open to finish
+ */
+ if (!list_empty(hpriv_list))
+ ssleep(1);
+
+ if (timeout) {
+ pending_cnt = timeout;
+ } else {
+ if (hdev->process_kill_trial_cnt) {
+ /* Processes have been already killed */
+ pending_cnt = 1;
+ goto wait_for_processes;
+ } else {
+ /* Wait a small period after process kill */
+ pending_cnt = HL_PENDING_RESET_PER_SEC;
+ }
+ }
+
+ mutex_lock(hpriv_lock);
+
+ /* This section must be protected because we are dereferencing
+ * pointers that are freed if the process exits
+ */
+ list_for_each_entry(hpriv, hpriv_list, dev_node) {
+ task = get_pid_task(hpriv->taskpid, PIDTYPE_PID);
+ if (task) {
+ dev_info(hdev->dev, "Killing user process pid=%d\n",
+ task_pid_nr(task));
+ send_sig(SIGKILL, task, 1);
+ usleep_range(1000, 10000);
+
+ put_task_struct(task);
+ } else {
+ dev_dbg(hdev->dev,
+ "Can't get task struct for user process %d, process was killed from outside the driver\n",
+ pid_nr(hpriv->taskpid));
+ }
+ }
+
+ mutex_unlock(hpriv_lock);
+
+ /*
+ * We killed the open users, but that doesn't mean they are closed.
+ * It could be that they are running a long cleanup phase in the driver
+ * e.g. MMU unmappings, or running other long teardown flow even before
+ * our cleanup.
+ * Therefore we need to wait again to make sure they are closed before
+ * continuing with the reset.
+ */
+
+wait_for_processes:
+ while ((!list_empty(hpriv_list)) && (pending_cnt)) {
+ dev_dbg(hdev->dev,
+ "Waiting for all unmap operations to finish before hard reset\n");
+
+ pending_cnt--;
+
+ ssleep(1);
+ }
+
+ /* All processes exited successfully */
+ if (list_empty(hpriv_list))
+ return 0;
+
+ /* Give up waiting for processes to exit */
+ if (hdev->process_kill_trial_cnt == HL_PENDING_RESET_MAX_TRIALS)
+ return -ETIME;
+
+ hdev->process_kill_trial_cnt++;
+
+ return -EBUSY;
+}
+
+static void device_disable_open_processes(struct hl_device *hdev, bool control_dev)
+{
+ struct list_head *hpriv_list;
+ struct hl_fpriv *hpriv;
+ struct mutex *hpriv_lock;
+
+ hpriv_lock = control_dev ? &hdev->fpriv_ctrl_list_lock : &hdev->fpriv_list_lock;
+ hpriv_list = control_dev ? &hdev->fpriv_ctrl_list : &hdev->fpriv_list;
+
+ mutex_lock(hpriv_lock);
+ list_for_each_entry(hpriv, hpriv_list, dev_node)
+ hpriv->hdev = NULL;
+ mutex_unlock(hpriv_lock);
+}
+
+static void send_disable_pci_access(struct hl_device *hdev, u32 flags)
+{
+ /* If reset is due to heartbeat, device CPU is no responsive in
+ * which case no point sending PCI disable message to it.
+ */
+ if ((flags & HL_DRV_RESET_HARD) &&
+ !(flags & (HL_DRV_RESET_HEARTBEAT | HL_DRV_RESET_BYPASS_REQ_TO_FW))) {
+ /* Disable PCI access from device F/W so he won't send
+ * us additional interrupts. We disable MSI/MSI-X at
+ * the halt_engines function and we can't have the F/W
+ * sending us interrupts after that. We need to disable
+ * the access here because if the device is marked
+ * disable, the message won't be send. Also, in case
+ * of heartbeat, the device CPU is marked as disable
+ * so this message won't be sent
+ */
+ if (hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0))
+ return;
+
+ /* disable_irq also generates sync irq, this verifies that last EQs are handled
+ * before disabled is set. The IRQ will be enabled again in request_irq call.
+ */
+ if (hdev->cpu_queues_enable)
+ disable_irq(pci_irq_vector(hdev->pdev, hdev->asic_prop.eq_interrupt_id));
+ }
+}
+
+static void handle_reset_trigger(struct hl_device *hdev, u32 flags)
+{
+ u32 cur_reset_trigger = HL_RESET_TRIGGER_DEFAULT;
+
+ /* No consecutive mechanism when user context exists */
+ if (hdev->is_compute_ctx_active)
+ return;
+
+ /*
+ * 'reset cause' is being updated here, because getting here
+ * means that it's the 1st time and the last time we're here
+ * ('in_reset' makes sure of it). This makes sure that
+ * 'reset_cause' will continue holding its 1st recorded reason!
+ */
+ if (flags & HL_DRV_RESET_HEARTBEAT) {
+ hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT;
+ cur_reset_trigger = HL_DRV_RESET_HEARTBEAT;
+ } else if (flags & HL_DRV_RESET_TDR) {
+ hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_TDR;
+ cur_reset_trigger = HL_DRV_RESET_TDR;
+ } else if (flags & HL_DRV_RESET_FW_FATAL_ERR) {
+ hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
+ cur_reset_trigger = HL_DRV_RESET_FW_FATAL_ERR;
+ } else {
+ hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
+ }
+
+ /*
+ * If reset cause is same twice, then reset_trigger_repeated
+ * is set and if this reset is due to a fatal FW error
+ * device is set to an unstable state.
+ */
+ if (hdev->reset_info.prev_reset_trigger != cur_reset_trigger) {
+ hdev->reset_info.prev_reset_trigger = cur_reset_trigger;
+ hdev->reset_info.reset_trigger_repeated = 0;
+ } else {
+ hdev->reset_info.reset_trigger_repeated = 1;
+ }
+}
+
+static void reset_heartbeat_debug_info(struct hl_device *hdev)
+{
+ hdev->heartbeat_debug_info.last_pq_heartbeat_ts = 0;
+ hdev->heartbeat_debug_info.last_eq_heartbeat_ts = 0;
+ hdev->heartbeat_debug_info.heartbeat_event_counter = 0;
+}
+
+static inline void device_heartbeat_schedule(struct hl_device *hdev)
+{
+ if (!hdev->heartbeat)
+ return;
+
+ reset_heartbeat_debug_info(hdev);
+
+ /*
+ * Before scheduling the heartbeat driver will check if eq event has received.
+ * for the first schedule we need to set the indication as true then for the next
+ * one this indication will be true only if eq event was sent by FW.
+ */
+ hdev->eq_heartbeat_received = true;
+
+ schedule_delayed_work(&hdev->work_heartbeat,
+ usecs_to_jiffies(HL_HEARTBEAT_PER_USEC));
+}
+
+/*
+ * hl_device_reset - reset the device
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @flags: reset flags.
+ *
+ * Block future CS and wait for pending CS to be enqueued
+ * Call ASIC H/W fini
+ * Flush all completions
+ * Re-initialize all internal data structures
+ * Call ASIC H/W init, late_init
+ * Test queues
+ * Enable device
+ *
+ * Returns 0 for success or an error on failure.
+ */
+int hl_device_reset(struct hl_device *hdev, u32 flags)
+{
+ bool hard_reset, from_hard_reset_thread, fw_reset, reset_upon_device_release,
+ schedule_hard_reset = false, delay_reset, from_dev_release, from_watchdog_thread;
+ u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0};
+ struct hl_ctx *ctx;
+ int i, rc, hw_fini_rc;
+
+ if (!hdev->init_done) {
+ dev_err(hdev->dev, "Can't reset before initialization is done\n");
+ return 0;
+ }
+
+ hard_reset = !!(flags & HL_DRV_RESET_HARD);
+ from_hard_reset_thread = !!(flags & HL_DRV_RESET_FROM_RESET_THR);
+ fw_reset = !!(flags & HL_DRV_RESET_BYPASS_REQ_TO_FW);
+ from_dev_release = !!(flags & HL_DRV_RESET_DEV_RELEASE);
+ delay_reset = !!(flags & HL_DRV_RESET_DELAY);
+ from_watchdog_thread = !!(flags & HL_DRV_RESET_FROM_WD_THR);
+ reset_upon_device_release = hdev->reset_upon_device_release && from_dev_release;
+
+ if (hdev->cpld_shutdown) {
+ dev_err(hdev->dev, "Cannot reset device, cpld is shutdown! Device is NOT usable\n");
+ return -EIO;
+ }
+
+ if (!hard_reset && (hl_device_status(hdev) == HL_DEVICE_STATUS_MALFUNCTION)) {
+ dev_dbg(hdev->dev, "soft-reset isn't supported on a malfunctioning device\n");
+ return 0;
+ }
+
+ if (!hard_reset && !hdev->asic_prop.supports_compute_reset) {
+ dev_dbg(hdev->dev, "asic doesn't support compute reset - do hard-reset instead\n");
+ hard_reset = true;
+ }
+
+ if (reset_upon_device_release) {
+ if (hard_reset) {
+ dev_crit(hdev->dev,
+ "Aborting reset because hard-reset is mutually exclusive with reset-on-device-release\n");
+ return -EINVAL;
+ }
+
+ goto do_reset;
+ }
+
+ if (!hard_reset && !hdev->asic_prop.allow_inference_soft_reset) {
+ dev_dbg(hdev->dev,
+ "asic doesn't allow inference soft reset - do hard-reset instead\n");
+ hard_reset = true;
+ }
+
+do_reset:
+ /* Re-entry of reset thread */
+ if (from_hard_reset_thread && hdev->process_kill_trial_cnt)
+ goto kill_processes;
+
+ /*
+ * Prevent concurrency in this function - only one reset should be
+ * done at any given time. We need to perform this only if we didn't
+ * get here from a dedicated hard reset thread.
+ */
+ if (!from_hard_reset_thread) {
+ /* Block future CS/VM/JOB completion operations */
+ spin_lock(&hdev->reset_info.lock);
+ if (hdev->reset_info.in_reset) {
+ /* We allow scheduling of a hard reset only during a compute reset */
+ if (hard_reset && hdev->reset_info.in_compute_reset)
+ hdev->reset_info.hard_reset_schedule_flags = flags;
+ spin_unlock(&hdev->reset_info.lock);
+ return 0;
+ }
+
+ /* This still allows the completion of some KDMA ops
+ * Update this before in_reset because in_compute_reset implies we are in reset
+ */
+ hdev->reset_info.in_compute_reset = !hard_reset;
+
+ hdev->reset_info.in_reset = 1;
+
+ spin_unlock(&hdev->reset_info.lock);
+
+ /* Cancel the device release watchdog work if required.
+ * In case of reset-upon-device-release while the release watchdog work is
+ * scheduled due to a hard-reset, do hard-reset instead of compute-reset.
+ */
+ if ((hard_reset || from_dev_release) && hdev->reset_info.watchdog_active) {
+ struct hl_device_reset_work *watchdog_work =
+ &hdev->device_release_watchdog_work;
+
+ hdev->reset_info.watchdog_active = 0;
+ if (!from_watchdog_thread)
+ cancel_delayed_work_sync(&watchdog_work->reset_work);
+
+ if (from_dev_release && (watchdog_work->flags & HL_DRV_RESET_HARD)) {
+ hdev->reset_info.in_compute_reset = 0;
+ flags |= HL_DRV_RESET_HARD;
+ flags &= ~HL_DRV_RESET_DEV_RELEASE;
+ hard_reset = true;
+ }
+ }
+
+ if (delay_reset)
+ usleep_range(HL_RESET_DELAY_USEC, HL_RESET_DELAY_USEC << 1);
+
+escalate_reset_flow:
+ handle_reset_trigger(hdev, flags);
+ send_disable_pci_access(hdev, flags);
+
+ /* This also blocks future CS/VM/JOB completion operations */
+ hdev->disabled = true;
+
+ take_release_locks(hdev);
+
+ if (hard_reset)
+ dev_info(hdev->dev, "Going to reset device\n");
+ else if (reset_upon_device_release)
+ dev_dbg(hdev->dev, "Going to reset device after release by user\n");
+ else
+ dev_dbg(hdev->dev, "Going to reset engines of inference device\n");
+ }
+
+ if ((hard_reset) && (!from_hard_reset_thread)) {
+ hdev->reset_info.hard_reset_pending = true;
+
+ hdev->process_kill_trial_cnt = 0;
+
+ hdev->device_reset_work.flags = flags;
+
+ /*
+ * Because the reset function can't run from heartbeat work,
+ * we need to call the reset function from a dedicated work.
+ */
+ queue_delayed_work(hdev->reset_wq, &hdev->device_reset_work.reset_work, 0);
+
+ return 0;
+ }
+
+ cleanup_resources(hdev, hard_reset, fw_reset, from_dev_release);
+
+kill_processes:
+ if (hard_reset) {
+ /* Kill processes here after CS rollback. This is because the
+ * process can't really exit until all its CSs are done, which
+ * is what we do in cs rollback
+ */
+ rc = device_kill_open_processes(hdev, 0, false);
+
+ if (rc == -EBUSY) {
+ if (hdev->device_fini_pending) {
+ dev_crit(hdev->dev,
+ "%s Failed to kill all open processes, stopping hard reset\n",
+ dev_name(&(hdev)->pdev->dev));
+ goto out_err;
+ }
+
+ /* signal reset thread to reschedule */
+ return rc;
+ }
+
+ if (rc) {
+ dev_crit(hdev->dev,
+ "%s Failed to kill all open processes, stopping hard reset\n",
+ dev_name(&(hdev)->pdev->dev));
+ goto out_err;
+ }
+
+ /* Flush the Event queue workers to make sure no other thread is
+ * reading or writing to registers during the reset
+ */
+ flush_workqueue(hdev->eq_wq);
+ }
+
+ /* Reset the H/W. It will be in idle state after this returns */
+ hw_fini_rc = hdev->asic_funcs->hw_fini(hdev, hard_reset, fw_reset);
+
+ if (hard_reset) {
+ hdev->fw_loader.fw_comp_loaded = FW_TYPE_NONE;
+
+ /* Release kernel context */
+ if (hdev->kernel_ctx && hl_ctx_put(hdev->kernel_ctx) == 1)
+ hdev->kernel_ctx = NULL;
+
+ hl_vm_fini(hdev);
+ hl_mmu_fini(hdev);
+ hl_eq_reset(hdev, &hdev->event_queue);
+ }
+
+ /* Re-initialize PI,CI to 0 in all queues (hw queue, cq) */
+ hl_hw_queue_reset(hdev, hard_reset);
+ for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
+ hl_cq_reset(hdev, &hdev->completion_queue[i]);
+
+ /* Make sure the context switch phase will run again */
+ ctx = hl_get_compute_ctx(hdev);
+ if (ctx) {
+ atomic_set(&ctx->thread_ctx_switch_token, 1);
+ ctx->thread_ctx_switch_wait_token = 0;
+ hl_ctx_put(ctx);
+ }
+
+ if (hw_fini_rc) {
+ rc = hw_fini_rc;
+ goto out_err;
+ }
+ /* Finished tear-down, starting to re-initialize */
+
+ if (hard_reset) {
+ hdev->device_cpu_disabled = false;
+ hdev->reset_info.hard_reset_pending = false;
+
+ /*
+ * Put the device in an unusable state if there are 2 back to back resets due to
+ * fatal errors.
+ */
+ if (hdev->reset_info.reset_trigger_repeated &&
+ (hdev->reset_info.prev_reset_trigger == HL_DRV_RESET_FW_FATAL_ERR ||
+ hdev->reset_info.prev_reset_trigger ==
+ HL_DRV_RESET_HEARTBEAT)) {
+ dev_crit(hdev->dev,
+ "%s Consecutive fatal errors, stopping hard reset\n",
+ dev_name(&(hdev)->pdev->dev));
+ rc = -EIO;
+ goto out_err;
+ }
+
+ if (hdev->kernel_ctx) {
+ dev_crit(hdev->dev,
+ "%s kernel ctx was alive during hard reset, something is terribly wrong\n",
+ dev_name(&(hdev)->pdev->dev));
+ rc = -EBUSY;
+ goto out_err;
+ }
+
+ rc = hl_mmu_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to initialize MMU S/W after hard reset\n");
+ goto out_err;
+ }
+
+ /* Allocate the kernel context */
+ hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx),
+ GFP_KERNEL);
+ if (!hdev->kernel_ctx) {
+ rc = -ENOMEM;
+ hl_mmu_fini(hdev);
+ goto out_err;
+ }
+
+ hdev->is_compute_ctx_active = false;
+
+ rc = hl_ctx_init(hdev, hdev->kernel_ctx, true);
+ if (rc) {
+ dev_err(hdev->dev,
+ "failed to init kernel ctx in hard reset\n");
+ kfree(hdev->kernel_ctx);
+ hdev->kernel_ctx = NULL;
+ hl_mmu_fini(hdev);
+ goto out_err;
+ }
+ }
+
+ /* Device is now enabled as part of the initialization requires
+ * communication with the device firmware to get information that
+ * is required for the initialization itself
+ */
+ hdev->disabled = false;
+
+ /* F/W security enabled indication might be updated after hard-reset */
+ if (hard_reset) {
+ rc = hl_fw_read_preboot_status(hdev);
+ if (rc)
+ goto out_err;
+ }
+
+ rc = hdev->asic_funcs->hw_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize the H/W after reset\n");
+ goto out_err;
+ }
+
+ /* If device is not idle fail the reset process */
+ if (!hdev->asic_funcs->is_device_idle(hdev, idle_mask,
+ HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL)) {
+ print_idle_status_mask(hdev, "device is not idle after reset", idle_mask);
+ rc = -EIO;
+ goto out_err;
+ }
+
+ /* Check that the communication with the device is working */
+ rc = hdev->asic_funcs->test_queues(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to detect if device is alive after reset\n");
+ goto out_err;
+ }
+
+ if (hard_reset) {
+ rc = device_late_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed late init after hard reset\n");
+ goto out_err;
+ }
+
+ rc = hl_vm_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to init memory module after hard reset\n");
+ goto out_err;
+ }
+
+ if (!hdev->asic_prop.fw_security_enabled)
+ hl_fw_set_max_power(hdev);
+ } else {
+ rc = hdev->asic_funcs->compute_reset_late_init(hdev);
+ if (rc) {
+ if (reset_upon_device_release)
+ dev_err(hdev->dev,
+ "Failed late init in reset after device release\n");
+ else
+ dev_err(hdev->dev, "Failed late init after compute reset\n");
+ goto out_err;
+ }
+ }
+
+ rc = hdev->asic_funcs->scrub_device_mem(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "scrub mem failed from device reset (%d)\n", rc);
+ goto out_err;
+ }
+
+ spin_lock(&hdev->reset_info.lock);
+ hdev->reset_info.in_compute_reset = 0;
+
+ /* Schedule hard reset only if requested and if not already in hard reset.
+ * We keep 'in_reset' enabled, so no other reset can go in during the hard
+ * reset schedule
+ */
+ if (!hard_reset && hdev->reset_info.hard_reset_schedule_flags)
+ schedule_hard_reset = true;
+ else
+ hdev->reset_info.in_reset = 0;
+
+ spin_unlock(&hdev->reset_info.lock);
+
+ hdev->reset_info.needs_reset = false;
+
+ if (hard_reset)
+ dev_info(hdev->dev,
+ "Successfully finished resetting the %s device\n",
+ dev_name(&(hdev)->pdev->dev));
+ else
+ dev_dbg(hdev->dev,
+ "Successfully finished resetting the %s device\n",
+ dev_name(&(hdev)->pdev->dev));
+
+ if (hard_reset) {
+ hdev->reset_info.hard_reset_cnt++;
+
+ device_heartbeat_schedule(hdev);
+
+ /* After reset is done, we are ready to receive events from
+ * the F/W. We can't do it before because we will ignore events
+ * and if those events are fatal, we won't know about it and
+ * the device will be operational although it shouldn't be
+ */
+ hdev->asic_funcs->enable_events_from_fw(hdev);
+ } else {
+ if (!reset_upon_device_release)
+ hdev->reset_info.compute_reset_cnt++;
+
+ if (schedule_hard_reset) {
+ dev_info(hdev->dev, "Performing hard reset scheduled during compute reset\n");
+ flags = hdev->reset_info.hard_reset_schedule_flags;
+ hdev->reset_info.hard_reset_schedule_flags = 0;
+ hard_reset = true;
+ goto escalate_reset_flow;
+ }
+ }
+
+ return 0;
+
+out_err:
+ hdev->disabled = true;
+
+ spin_lock(&hdev->reset_info.lock);
+ hdev->reset_info.in_compute_reset = 0;
+
+ if (hard_reset) {
+ dev_err(hdev->dev,
+ "%s Failed to reset! Device is NOT usable\n",
+ dev_name(&(hdev)->pdev->dev));
+ hdev->reset_info.hard_reset_cnt++;
+ } else {
+ if (reset_upon_device_release) {
+ dev_err(hdev->dev, "Failed to reset device after user release\n");
+ flags &= ~HL_DRV_RESET_DEV_RELEASE;
+ } else {
+ dev_err(hdev->dev, "Failed to do compute reset\n");
+ hdev->reset_info.compute_reset_cnt++;
+ }
+
+ spin_unlock(&hdev->reset_info.lock);
+ flags |= HL_DRV_RESET_HARD;
+ hard_reset = true;
+ goto escalate_reset_flow;
+ }
+
+ hdev->reset_info.in_reset = 0;
+
+ spin_unlock(&hdev->reset_info.lock);
+
+ return rc;
+}
+
+/*
+ * hl_device_cond_reset() - conditionally reset the device.
+ * @hdev: pointer to habanalabs device structure.
+ * @reset_flags: reset flags.
+ * @event_mask: events to notify user about.
+ *
+ * Conditionally reset the device, or alternatively schedule a watchdog work to reset the device
+ * unless another reset precedes it.
+ */
+int hl_device_cond_reset(struct hl_device *hdev, u32 flags, u64 event_mask)
+{
+ struct hl_ctx *ctx = NULL;
+
+ /* F/W reset cannot be postponed */
+ if (flags & HL_DRV_RESET_BYPASS_REQ_TO_FW)
+ goto device_reset;
+
+ /* Device release watchdog is relevant only if user exists and gets a reset notification */
+ if (!(event_mask & HL_NOTIFIER_EVENT_DEVICE_RESET)) {
+ dev_err(hdev->dev, "Resetting device without a reset indication to user\n");
+ goto device_reset;
+ }
+
+ ctx = hl_get_compute_ctx(hdev);
+ if (!ctx)
+ goto device_reset;
+
+ /*
+ * There is no point in postponing the reset if user is not registered for events.
+ * However if no eventfd_ctx exists but the device release watchdog is already scheduled, it
+ * just implies that user has unregistered as part of handling a previous event. In this
+ * case an immediate reset is not required.
+ */
+ if (!ctx->hpriv->notifier_event.eventfd && !hdev->reset_info.watchdog_active)
+ goto device_reset;
+
+ /* Schedule the device release watchdog work unless reset is already in progress or if the
+ * work is already scheduled.
+ */
+ spin_lock(&hdev->reset_info.lock);
+ if (hdev->reset_info.in_reset) {
+ spin_unlock(&hdev->reset_info.lock);
+ goto device_reset;
+ }
+
+ if (hdev->reset_info.watchdog_active) {
+ hdev->device_release_watchdog_work.flags |= flags;
+ goto out;
+ }
+
+ hdev->device_release_watchdog_work.flags = flags;
+ dev_dbg(hdev->dev, "Device is going to be hard-reset in %u sec unless being released\n",
+ hdev->device_release_watchdog_timeout_sec);
+ schedule_delayed_work(&hdev->device_release_watchdog_work.reset_work,
+ secs_to_jiffies(hdev->device_release_watchdog_timeout_sec));
+ hdev->reset_info.watchdog_active = 1;
+out:
+ spin_unlock(&hdev->reset_info.lock);
+
+ hl_notifier_event_send_all(hdev, event_mask);
+
+ hl_ctx_put(ctx);
+
+ hl_abort_waiting_for_completions(hdev);
+
+ return 0;
+
+device_reset:
+ if (event_mask)
+ hl_notifier_event_send_all(hdev, event_mask);
+ if (ctx)
+ hl_ctx_put(ctx);
+
+ return hl_device_reset(hdev, flags | HL_DRV_RESET_HARD);
+}
+
+static void hl_notifier_event_send(struct hl_notifier_event *notifier_event, u64 event_mask)
+{
+ mutex_lock(&notifier_event->lock);
+ notifier_event->events_mask |= event_mask;
+
+ if (notifier_event->eventfd)
+ eventfd_signal(notifier_event->eventfd);
+
+ mutex_unlock(&notifier_event->lock);
+}
+
+/*
+ * hl_notifier_event_send_all - notify all user processes via eventfd
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @event_mask: the occurred event/s
+ * Returns 0 for success or an error on failure.
+ */
+void hl_notifier_event_send_all(struct hl_device *hdev, u64 event_mask)
+{
+ struct hl_fpriv *hpriv;
+
+ if (!event_mask) {
+ dev_warn(hdev->dev, "Skip sending zero event");
+ return;
+ }
+
+ mutex_lock(&hdev->fpriv_list_lock);
+
+ list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node)
+ hl_notifier_event_send(&hpriv->notifier_event, event_mask);
+
+ mutex_unlock(&hdev->fpriv_list_lock);
+}
+
+/*
+ * hl_device_init - main initialization function for habanalabs device
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Allocate an id for the device, do early initialization and then call the
+ * ASIC specific initialization functions. Finally, create the cdev and the
+ * Linux device to expose it to the user
+ */
+int hl_device_init(struct hl_device *hdev)
+{
+ int i, rc, cq_cnt, user_interrupt_cnt, cq_ready_cnt;
+ struct hl_ts_free_jobs *free_jobs_data;
+ bool expose_interfaces_on_err = false;
+ void *p;
+
+ /* Initialize ASIC function pointers and perform early init */
+ rc = device_early_init(hdev);
+ if (rc)
+ goto out_disabled;
+
+ user_interrupt_cnt = hdev->asic_prop.user_dec_intr_count +
+ hdev->asic_prop.user_interrupt_count;
+
+ if (user_interrupt_cnt) {
+ hdev->user_interrupt = kcalloc(user_interrupt_cnt, sizeof(*hdev->user_interrupt),
+ GFP_KERNEL);
+ if (!hdev->user_interrupt) {
+ rc = -ENOMEM;
+ goto early_fini;
+ }
+
+ /* Timestamp records supported only if CQ supported in device */
+ if (hdev->asic_prop.first_available_cq[0] != USHRT_MAX) {
+ for (i = 0 ; i < user_interrupt_cnt ; i++) {
+ p = vzalloc(TIMESTAMP_FREE_NODES_NUM *
+ sizeof(struct timestamp_reg_free_node));
+ if (!p) {
+ rc = -ENOMEM;
+ goto free_usr_intr_mem;
+ }
+ free_jobs_data = &hdev->user_interrupt[i].ts_free_jobs_data;
+ free_jobs_data->free_nodes_pool = p;
+ free_jobs_data->free_nodes_length = TIMESTAMP_FREE_NODES_NUM;
+ free_jobs_data->next_avail_free_node_idx = 0;
+ }
+ }
+ }
+
+ free_jobs_data = &hdev->common_user_cq_interrupt.ts_free_jobs_data;
+ p = vzalloc(TIMESTAMP_FREE_NODES_NUM *
+ sizeof(struct timestamp_reg_free_node));
+ if (!p) {
+ rc = -ENOMEM;
+ goto free_usr_intr_mem;
+ }
+
+ free_jobs_data->free_nodes_pool = p;
+ free_jobs_data->free_nodes_length = TIMESTAMP_FREE_NODES_NUM;
+ free_jobs_data->next_avail_free_node_idx = 0;
+
+ /*
+ * Start calling ASIC initialization. First S/W then H/W and finally
+ * late init
+ */
+ rc = hdev->asic_funcs->sw_init(hdev);
+ if (rc)
+ goto free_common_usr_intr_mem;
+
+
+ /* initialize completion structure for multi CS wait */
+ hl_multi_cs_completion_init(hdev);
+
+ /*
+ * Initialize the H/W queues. Must be done before hw_init, because
+ * there the addresses of the kernel queue are being written to the
+ * registers of the device
+ */
+ rc = hl_hw_queues_create(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize kernel queues\n");
+ goto sw_fini;
+ }
+
+ cq_cnt = hdev->asic_prop.completion_queues_count;
+
+ /*
+ * Initialize the completion queues. Must be done before hw_init,
+ * because there the addresses of the completion queues are being
+ * passed as arguments to request_irq
+ */
+ if (cq_cnt) {
+ hdev->completion_queue = kcalloc(cq_cnt,
+ sizeof(*hdev->completion_queue),
+ GFP_KERNEL);
+
+ if (!hdev->completion_queue) {
+ dev_err(hdev->dev,
+ "failed to allocate completion queues\n");
+ rc = -ENOMEM;
+ goto hw_queues_destroy;
+ }
+ }
+
+ for (i = 0, cq_ready_cnt = 0 ; i < cq_cnt ; i++, cq_ready_cnt++) {
+ rc = hl_cq_init(hdev, &hdev->completion_queue[i],
+ hdev->asic_funcs->get_queue_id_for_cq(hdev, i));
+ if (rc) {
+ dev_err(hdev->dev,
+ "failed to initialize completion queue\n");
+ goto cq_fini;
+ }
+ hdev->completion_queue[i].cq_idx = i;
+ }
+
+ hdev->shadow_cs_queue = kcalloc(hdev->asic_prop.max_pending_cs,
+ sizeof(struct hl_cs *), GFP_KERNEL);
+ if (!hdev->shadow_cs_queue) {
+ rc = -ENOMEM;
+ goto cq_fini;
+ }
+
+ /*
+ * Initialize the event queue. Must be done before hw_init,
+ * because there the address of the event queue is being
+ * passed as argument to request_irq
+ */
+ rc = hl_eq_init(hdev, &hdev->event_queue);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize event queue\n");
+ goto free_shadow_cs_queue;
+ }
+
+ /* MMU S/W must be initialized before kernel context is created */
+ rc = hl_mmu_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to initialize MMU S/W structures\n");
+ goto eq_fini;
+ }
+
+ /* Allocate the kernel context */
+ hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL);
+ if (!hdev->kernel_ctx) {
+ rc = -ENOMEM;
+ goto mmu_fini;
+ }
+
+ hdev->is_compute_ctx_active = false;
+
+ hdev->asic_funcs->state_dump_init(hdev);
+
+ hdev->device_release_watchdog_timeout_sec = HL_DEVICE_RELEASE_WATCHDOG_TIMEOUT_SEC;
+
+ hdev->memory_scrub_val = MEM_SCRUB_DEFAULT_VAL;
+
+ rc = hl_debugfs_device_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize debugfs entry structure\n");
+ kfree(hdev->kernel_ctx);
+ goto mmu_fini;
+ }
+
+ /* The debugfs entry structure is accessed in hl_ctx_init(), so it must be called after
+ * hl_debugfs_device_init().
+ */
+ rc = hl_ctx_init(hdev, hdev->kernel_ctx, true);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize kernel context\n");
+ kfree(hdev->kernel_ctx);
+ goto debugfs_device_fini;
+ }
+
+ rc = hl_cb_pool_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize CB pool\n");
+ goto release_ctx;
+ }
+
+ rc = hl_dec_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to initialize the decoder module\n");
+ goto cb_pool_fini;
+ }
+
+ /*
+ * From this point, override rc (=0) in case of an error to allow debugging
+ * (by adding char devices and creating sysfs/debugfs files as part of the error flow).
+ */
+ expose_interfaces_on_err = true;
+
+ /* Device is now enabled as part of the initialization requires
+ * communication with the device firmware to get information that
+ * is required for the initialization itself
+ */
+ hdev->disabled = false;
+
+ rc = hdev->asic_funcs->hw_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize the H/W\n");
+ rc = 0;
+ goto out_disabled;
+ }
+
+ /* Check that the communication with the device is working */
+ rc = hdev->asic_funcs->test_queues(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to detect if device is alive\n");
+ rc = 0;
+ goto out_disabled;
+ }
+
+ rc = device_late_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed late initialization\n");
+ rc = 0;
+ goto out_disabled;
+ }
+
+ dev_info(hdev->dev, "Found %s device with %lluGB DRAM\n",
+ hdev->asic_name,
+ hdev->asic_prop.dram_size / SZ_1G);
+
+ rc = hl_vm_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to initialize memory module\n");
+ rc = 0;
+ goto out_disabled;
+ }
+
+ /*
+ * Expose devices and sysfs/debugfs files to user.
+ * From here there is no need to expose them in case of an error.
+ */
+ expose_interfaces_on_err = false;
+
+ rc = drm_dev_register(&hdev->drm, 0);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to register DRM device, rc %d\n", rc);
+ rc = 0;
+ goto out_disabled;
+ }
+
+ rc = cdev_sysfs_debugfs_add(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to add char devices and sysfs/debugfs files\n");
+ rc = 0;
+ goto out_disabled;
+ }
+
+ /* Need to call this again because the max power might change,
+ * depending on card type for certain ASICs
+ */
+ if (hdev->asic_prop.set_max_power_on_device_init &&
+ !hdev->asic_prop.fw_security_enabled)
+ hl_fw_set_max_power(hdev);
+
+ /*
+ * hl_hwmon_init() must be called after device_late_init(), because only
+ * there we get the information from the device about which
+ * hwmon-related sensors the device supports.
+ * Furthermore, it must be done after adding the device to the system.
+ */
+ rc = hl_hwmon_init(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to initialize hwmon\n");
+ rc = 0;
+ goto out_disabled;
+ }
+
+ /* Scheduling the EQ heartbeat thread must come after driver is done with all
+ * initializations, as we want to make sure the FW gets enough time to be prepared
+ * to respond to heartbeat packets.
+ */
+ device_heartbeat_schedule(hdev);
+
+ dev_notice(hdev->dev,
+ "Successfully added device %s to habanalabs driver\n",
+ dev_name(&(hdev)->pdev->dev));
+
+ /* After initialization is done, we are ready to receive events from
+ * the F/W. We can't do it before because we will ignore events and if
+ * those events are fatal, we won't know about it and the device will
+ * be operational although it shouldn't be
+ */
+ hdev->asic_funcs->enable_events_from_fw(hdev);
+
+ hdev->init_done = true;
+
+ return 0;
+
+cb_pool_fini:
+ hl_cb_pool_fini(hdev);
+release_ctx:
+ if (hl_ctx_put(hdev->kernel_ctx) != 1)
+ dev_err(hdev->dev,
+ "kernel ctx is still alive on initialization failure\n");
+debugfs_device_fini:
+ hl_debugfs_device_fini(hdev);
+mmu_fini:
+ hl_mmu_fini(hdev);
+eq_fini:
+ hl_eq_fini(hdev, &hdev->event_queue);
+free_shadow_cs_queue:
+ kfree(hdev->shadow_cs_queue);
+cq_fini:
+ for (i = 0 ; i < cq_ready_cnt ; i++)
+ hl_cq_fini(hdev, &hdev->completion_queue[i]);
+ kfree(hdev->completion_queue);
+hw_queues_destroy:
+ hl_hw_queues_destroy(hdev);
+sw_fini:
+ hdev->asic_funcs->sw_fini(hdev);
+free_common_usr_intr_mem:
+ vfree(hdev->common_user_cq_interrupt.ts_free_jobs_data.free_nodes_pool);
+free_usr_intr_mem:
+ if (user_interrupt_cnt) {
+ for (i = 0 ; i < user_interrupt_cnt ; i++) {
+ if (!hdev->user_interrupt[i].ts_free_jobs_data.free_nodes_pool)
+ break;
+ vfree(hdev->user_interrupt[i].ts_free_jobs_data.free_nodes_pool);
+ }
+ kfree(hdev->user_interrupt);
+ }
+early_fini:
+ device_early_fini(hdev);
+out_disabled:
+ hdev->disabled = true;
+ if (expose_interfaces_on_err) {
+ drm_dev_register(&hdev->drm, 0);
+ cdev_sysfs_debugfs_add(hdev);
+ }
+
+ pr_err("Failed to initialize accel%d. Device %s is NOT usable!\n",
+ hdev->cdev_idx, dev_name(&hdev->pdev->dev));
+
+ return rc;
+}
+
+/*
+ * hl_device_fini - main tear-down function for habanalabs device
+ *
+ * @hdev: pointer to habanalabs device structure
+ *
+ * Destroy the device, call ASIC fini functions and release the id
+ */
+void hl_device_fini(struct hl_device *hdev)
+{
+ u32 user_interrupt_cnt;
+ bool device_in_reset;
+ ktime_t timeout;
+ u64 reset_sec;
+ int i, rc;
+
+ dev_info(hdev->dev, "Removing device %s\n", dev_name(&(hdev)->pdev->dev));
+
+ hdev->device_fini_pending = 1;
+ flush_delayed_work(&hdev->device_reset_work.reset_work);
+
+ if (hdev->pldm)
+ reset_sec = HL_PLDM_HARD_RESET_MAX_TIMEOUT;
+ else
+ reset_sec = HL_HARD_RESET_MAX_TIMEOUT;
+
+ /*
+ * This function is competing with the reset function, so try to
+ * take the reset atomic and if we are already in middle of reset,
+ * wait until reset function is finished. Reset function is designed
+ * to always finish. However, in Gaudi, because of all the network
+ * ports, the hard reset could take between 10-30 seconds
+ */
+
+ timeout = ktime_add_us(ktime_get(), reset_sec * 1000 * 1000);
+
+ spin_lock(&hdev->reset_info.lock);
+ device_in_reset = !!hdev->reset_info.in_reset;
+ if (!device_in_reset)
+ hdev->reset_info.in_reset = 1;
+ spin_unlock(&hdev->reset_info.lock);
+
+ while (device_in_reset) {
+ usleep_range(50, 200);
+
+ spin_lock(&hdev->reset_info.lock);
+ device_in_reset = !!hdev->reset_info.in_reset;
+ if (!device_in_reset)
+ hdev->reset_info.in_reset = 1;
+ spin_unlock(&hdev->reset_info.lock);
+
+ if (ktime_compare(ktime_get(), timeout) > 0) {
+ dev_crit(hdev->dev,
+ "%s Failed to remove device because reset function did not finish\n",
+ dev_name(&(hdev)->pdev->dev));
+ return;
+ }
+ }
+
+ cancel_delayed_work_sync(&hdev->device_release_watchdog_work.reset_work);
+
+ /* Disable PCI access from device F/W so it won't send us additional
+ * interrupts. We disable MSI/MSI-X at the halt_engines function and we
+ * can't have the F/W sending us interrupts after that. We need to
+ * disable the access here because if the device is marked disable, the
+ * message won't be send. Also, in case of heartbeat, the device CPU is
+ * marked as disable so this message won't be sent
+ */
+ hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
+
+ /* Mark device as disabled */
+ hdev->disabled = true;
+
+ take_release_locks(hdev);
+
+ hdev->reset_info.hard_reset_pending = true;
+
+ hl_hwmon_fini(hdev);
+
+ cleanup_resources(hdev, true, false, false);
+
+ /* Kill processes here after CS rollback. This is because the process
+ * can't really exit until all its CSs are done, which is what we
+ * do in cs rollback
+ */
+ dev_info(hdev->dev,
+ "Waiting for all processes to exit (timeout of %u seconds)",
+ HL_WAIT_PROCESS_KILL_ON_DEVICE_FINI);
+
+ hdev->process_kill_trial_cnt = 0;
+ rc = device_kill_open_processes(hdev, HL_WAIT_PROCESS_KILL_ON_DEVICE_FINI, false);
+ if (rc) {
+ dev_crit(hdev->dev, "Failed to kill all open processes (%d)\n", rc);
+ device_disable_open_processes(hdev, false);
+ }
+
+ hdev->process_kill_trial_cnt = 0;
+ rc = device_kill_open_processes(hdev, 0, true);
+ if (rc) {
+ dev_crit(hdev->dev, "Failed to kill all control device open processes (%d)\n", rc);
+ device_disable_open_processes(hdev, true);
+ }
+
+ hl_cb_pool_fini(hdev);
+
+ /* Reset the H/W. It will be in idle state after this returns */
+ rc = hdev->asic_funcs->hw_fini(hdev, true, false);
+ if (rc)
+ dev_err(hdev->dev, "hw_fini failed in device fini while removing device %d\n", rc);
+
+ /* Reset the H/W (if it accessible). It will be in idle state after this returns */
+ if (!hdev->cpld_shutdown) {
+ rc = hdev->asic_funcs->hw_fini(hdev, true, false);
+ if (rc)
+ dev_err(hdev->dev,
+ "hw_fini failed in device fini while removing device %d\n", rc);
+ }
+
+ hdev->fw_loader.fw_comp_loaded = FW_TYPE_NONE;
+
+ /* Release kernel context */
+ if ((hdev->kernel_ctx) && (hl_ctx_put(hdev->kernel_ctx) != 1))
+ dev_err(hdev->dev, "kernel ctx is still alive\n");
+
+ hl_dec_fini(hdev);
+
+ hl_vm_fini(hdev);
+
+ hl_mmu_fini(hdev);
+
+ vfree(hdev->captured_err_info.page_fault_info.user_mappings);
+
+ hl_eq_fini(hdev, &hdev->event_queue);
+
+ kfree(hdev->shadow_cs_queue);
+
+ for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
+ hl_cq_fini(hdev, &hdev->completion_queue[i]);
+ kfree(hdev->completion_queue);
+
+ user_interrupt_cnt = hdev->asic_prop.user_dec_intr_count +
+ hdev->asic_prop.user_interrupt_count;
+
+ if (user_interrupt_cnt) {
+ if (hdev->asic_prop.first_available_cq[0] != USHRT_MAX) {
+ for (i = 0 ; i < user_interrupt_cnt ; i++)
+ vfree(hdev->user_interrupt[i].ts_free_jobs_data.free_nodes_pool);
+ }
+
+ kfree(hdev->user_interrupt);
+ }
+
+ vfree(hdev->common_user_cq_interrupt.ts_free_jobs_data.free_nodes_pool);
+
+ hl_hw_queues_destroy(hdev);
+
+ /* Call ASIC S/W finalize function */
+ hdev->asic_funcs->sw_fini(hdev);
+
+ device_early_fini(hdev);
+
+ /* Hide devices and sysfs/debugfs files from user */
+ cdev_sysfs_debugfs_remove(hdev);
+ drm_dev_unregister(&hdev->drm);
+
+ hl_debugfs_device_fini(hdev);
+
+ pr_info("removed device successfully\n");
+}
+
+/*
+ * MMIO register access helper functions.
+ */
+
+/*
+ * hl_rreg - Read an MMIO register
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @reg: MMIO register offset (in bytes)
+ *
+ * Returns the value of the MMIO register we are asked to read
+ *
+ */
+inline u32 hl_rreg(struct hl_device *hdev, u32 reg)
+{
+ u32 val = readl(hdev->rmmio + reg);
+
+ if (unlikely(trace_habanalabs_rreg32_enabled()))
+ trace_habanalabs_rreg32(&(hdev)->pdev->dev, reg, val);
+
+ return val;
+}
+
+/*
+ * hl_wreg - Write to an MMIO register
+ *
+ * @hdev: pointer to habanalabs device structure
+ * @reg: MMIO register offset (in bytes)
+ * @val: 32-bit value
+ *
+ * Writes the 32-bit value into the MMIO register
+ *
+ */
+inline void hl_wreg(struct hl_device *hdev, u32 reg, u32 val)
+{
+ if (unlikely(trace_habanalabs_wreg32_enabled()))
+ trace_habanalabs_wreg32(&(hdev)->pdev->dev, reg, val);
+
+ writel(val, hdev->rmmio + reg);
+}
+
+void hl_capture_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
+ u8 flags)
+{
+ struct razwi_info *razwi_info = &hdev->captured_err_info.razwi_info;
+
+ if (num_of_engines > HL_RAZWI_MAX_NUM_OF_ENGINES_PER_RTR) {
+ dev_err(hdev->dev,
+ "Number of possible razwi initiators (%u) exceeded limit (%u)\n",
+ num_of_engines, HL_RAZWI_MAX_NUM_OF_ENGINES_PER_RTR);
+ return;
+ }
+
+ /* In case it's the first razwi since the device was opened, capture its parameters */
+ if (atomic_cmpxchg(&hdev->captured_err_info.razwi_info.razwi_detected, 0, 1))
+ return;
+
+ razwi_info->razwi.timestamp = ktime_to_ns(ktime_get());
+ razwi_info->razwi.addr = addr;
+ razwi_info->razwi.num_of_possible_engines = num_of_engines;
+ memcpy(&razwi_info->razwi.engine_id[0], &engine_id[0],
+ num_of_engines * sizeof(u16));
+ razwi_info->razwi.flags = flags;
+
+ razwi_info->razwi_info_available = true;
+}
+
+void hl_handle_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
+ u8 flags, u64 *event_mask)
+{
+ hl_capture_razwi(hdev, addr, engine_id, num_of_engines, flags);
+
+ if (event_mask)
+ *event_mask |= HL_NOTIFIER_EVENT_RAZWI;
+}
+
+static void hl_capture_user_mappings(struct hl_device *hdev, bool is_pmmu)
+{
+ struct page_fault_info *pgf_info = &hdev->captured_err_info.page_fault_info;
+ struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
+ struct hl_vm_hash_node *hnode;
+ struct hl_userptr *userptr;
+ enum vm_type *vm_type;
+ struct hl_ctx *ctx;
+ u32 map_idx = 0;
+ int i;
+
+ /* Reset previous session count*/
+ pgf_info->num_of_user_mappings = 0;
+
+ ctx = hl_get_compute_ctx(hdev);
+ if (!ctx) {
+ dev_err(hdev->dev, "Can't get user context for user mappings\n");
+ return;
+ }
+
+ mutex_lock(&ctx->mem_hash_lock);
+ hash_for_each(ctx->mem_hash, i, hnode, node) {
+ vm_type = hnode->ptr;
+ if (((*vm_type == VM_TYPE_USERPTR) && is_pmmu) ||
+ ((*vm_type == VM_TYPE_PHYS_PACK) && !is_pmmu))
+ pgf_info->num_of_user_mappings++;
+
+ }
+
+ if (!pgf_info->num_of_user_mappings)
+ goto finish;
+
+ /* In case we already allocated in previous session, need to release it before
+ * allocating new buffer.
+ */
+ vfree(pgf_info->user_mappings);
+ pgf_info->user_mappings =
+ vzalloc(pgf_info->num_of_user_mappings * sizeof(struct hl_user_mapping));
+ if (!pgf_info->user_mappings) {
+ pgf_info->num_of_user_mappings = 0;
+ goto finish;
+ }
+
+ hash_for_each(ctx->mem_hash, i, hnode, node) {
+ vm_type = hnode->ptr;
+ if ((*vm_type == VM_TYPE_USERPTR) && (is_pmmu)) {
+ userptr = hnode->ptr;
+ pgf_info->user_mappings[map_idx].dev_va = hnode->vaddr;
+ pgf_info->user_mappings[map_idx].size = userptr->size;
+ map_idx++;
+ } else if ((*vm_type == VM_TYPE_PHYS_PACK) && (!is_pmmu)) {
+ phys_pg_pack = hnode->ptr;
+ pgf_info->user_mappings[map_idx].dev_va = hnode->vaddr;
+ pgf_info->user_mappings[map_idx].size = phys_pg_pack->total_size;
+ map_idx++;
+ }
+ }
+finish:
+ mutex_unlock(&ctx->mem_hash_lock);
+ hl_ctx_put(ctx);
+}
+
+void hl_capture_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu)
+{
+ struct page_fault_info *pgf_info = &hdev->captured_err_info.page_fault_info;
+
+ /* Capture only the first page fault */
+ if (atomic_cmpxchg(&pgf_info->page_fault_detected, 0, 1))
+ return;
+
+ pgf_info->page_fault.timestamp = ktime_to_ns(ktime_get());
+ pgf_info->page_fault.addr = addr;
+ pgf_info->page_fault.engine_id = eng_id;
+ hl_capture_user_mappings(hdev, is_pmmu);
+
+ pgf_info->page_fault_info_available = true;
+}
+
+void hl_handle_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu,
+ u64 *event_mask)
+{
+ hl_capture_page_fault(hdev, addr, eng_id, is_pmmu);
+
+ if (event_mask)
+ *event_mask |= HL_NOTIFIER_EVENT_PAGE_FAULT;
+}
+
+static void hl_capture_hw_err(struct hl_device *hdev, u16 event_id)
+{
+ struct hw_err_info *info = &hdev->captured_err_info.hw_err;
+
+ /* Capture only the first HW err */
+ if (atomic_cmpxchg(&info->event_detected, 0, 1))
+ return;
+
+ info->event.timestamp = ktime_to_ns(ktime_get());
+ info->event.event_id = event_id;
+
+ info->event_info_available = true;
+}
+
+void hl_handle_critical_hw_err(struct hl_device *hdev, u16 event_id, u64 *event_mask)
+{
+ hl_capture_hw_err(hdev, event_id);
+
+ if (event_mask)
+ *event_mask |= HL_NOTIFIER_EVENT_CRITICL_HW_ERR;
+}
+
+static void hl_capture_fw_err(struct hl_device *hdev, struct hl_info_fw_err_info *fw_info)
+{
+ struct fw_err_info *info = &hdev->captured_err_info.fw_err;
+
+ /* Capture only the first FW error */
+ if (atomic_cmpxchg(&info->event_detected, 0, 1))
+ return;
+
+ info->event.timestamp = ktime_to_ns(ktime_get());
+ info->event.err_type = fw_info->err_type;
+ if (fw_info->err_type == HL_INFO_FW_REPORTED_ERR)
+ info->event.event_id = fw_info->event_id;
+
+ info->event_info_available = true;
+}
+
+void hl_handle_fw_err(struct hl_device *hdev, struct hl_info_fw_err_info *info)
+{
+ hl_capture_fw_err(hdev, info);
+
+ if (info->event_mask)
+ *info->event_mask |= HL_NOTIFIER_EVENT_CRITICL_FW_ERR;
+}
+
+void hl_capture_engine_err(struct hl_device *hdev, u16 engine_id, u16 error_count)
+{
+ struct engine_err_info *info = &hdev->captured_err_info.engine_err;
+
+ /* Capture only the first engine error */
+ if (atomic_cmpxchg(&info->event_detected, 0, 1))
+ return;
+
+ info->event.timestamp = ktime_to_ns(ktime_get());
+ info->event.engine_id = engine_id;
+ info->event.error_count = error_count;
+ info->event_info_available = true;
+}
+
+void hl_enable_err_info_capture(struct hl_error_info *captured_err_info)
+{
+ vfree(captured_err_info->page_fault_info.user_mappings);
+ memset(captured_err_info, 0, sizeof(struct hl_error_info));
+ atomic_set(&captured_err_info->cs_timeout.write_enable, 1);
+ captured_err_info->undef_opcode.write_enable = true;
+}
+
+void hl_init_cpu_for_irq(struct hl_device *hdev)
+{
+#ifdef CONFIG_NUMA
+ struct cpumask *available_mask = &hdev->irq_affinity_mask;
+ int numa_node = hdev->pdev->dev.numa_node, i;
+ static struct cpumask cpu_mask;
+
+ if (numa_node < 0)
+ return;
+
+ if (!cpumask_and(&cpu_mask, cpumask_of_node(numa_node), cpu_online_mask)) {
+ dev_err(hdev->dev, "No available affinities in current numa node\n");
+ return;
+ }
+
+ /* Remove HT siblings */
+ for_each_cpu(i, &cpu_mask)
+ cpumask_set_cpu(cpumask_first(topology_sibling_cpumask(i)), available_mask);
+#endif
+}
+
+void hl_set_irq_affinity(struct hl_device *hdev, int irq)
+{
+ if (cpumask_empty(&hdev->irq_affinity_mask)) {
+ dev_dbg(hdev->dev, "affinity mask is empty\n");
+ return;
+ }
+
+ if (irq_set_affinity_and_hint(irq, &hdev->irq_affinity_mask))
+ dev_err(hdev->dev, "Failed setting irq %d affinity\n", irq);
+}
+
+void hl_eq_heartbeat_event_handle(struct hl_device *hdev)
+{
+ hdev->heartbeat_debug_info.heartbeat_event_counter++;
+ hdev->heartbeat_debug_info.last_eq_heartbeat_ts = ktime_get_real_seconds();
+ hdev->eq_heartbeat_received = true;
+}
+
+void hl_handle_clk_change_event(struct hl_device *hdev, u16 event_type, u64 *event_mask)
+{
+ struct hl_clk_throttle *clk_throttle = &hdev->clk_throttling;
+ ktime_t zero_time = ktime_set(0, 0);
+
+ mutex_lock(&clk_throttle->lock);
+
+ switch (event_type) {
+ case EQ_EVENT_POWER_EVT_START:
+ clk_throttle->current_reason |= HL_CLK_THROTTLE_POWER;
+ clk_throttle->aggregated_reason |= HL_CLK_THROTTLE_POWER;
+ clk_throttle->timestamp[HL_CLK_THROTTLE_TYPE_POWER].start = ktime_get();
+ clk_throttle->timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = zero_time;
+ dev_dbg_ratelimited(hdev->dev, "Clock throttling due to power consumption\n");
+ break;
+
+ case EQ_EVENT_POWER_EVT_END:
+ clk_throttle->current_reason &= ~HL_CLK_THROTTLE_POWER;
+ clk_throttle->timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = ktime_get();
+ dev_dbg_ratelimited(hdev->dev, "Power envelop is safe, back to optimal clock\n");
+ break;
+
+ case EQ_EVENT_THERMAL_EVT_START:
+ clk_throttle->current_reason |= HL_CLK_THROTTLE_THERMAL;
+ clk_throttle->aggregated_reason |= HL_CLK_THROTTLE_THERMAL;
+ clk_throttle->timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].start = ktime_get();
+ clk_throttle->timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = zero_time;
+ *event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ dev_info_ratelimited(hdev->dev, "Clock throttling due to overheating\n");
+ break;
+
+ case EQ_EVENT_THERMAL_EVT_END:
+ clk_throttle->current_reason &= ~HL_CLK_THROTTLE_THERMAL;
+ clk_throttle->timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = ktime_get();
+ *event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ dev_info_ratelimited(hdev->dev, "Thermal envelop is safe, back to optimal clock\n");
+ break;
+
+ default:
+ dev_err(hdev->dev, "Received invalid clock change event %d\n", event_type);
+ break;
+ }
+
+ mutex_unlock(&clk_throttle->lock);
+}
+
+void hl_eq_cpld_shutdown_event_handle(struct hl_device *hdev, u16 event_id, u64 *event_mask)
+{
+ hl_handle_critical_hw_err(hdev, event_id, event_mask);
+ *event_mask |= HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE;
+
+ /* Avoid any new accesses to the H/W */
+ hdev->disabled = true;
+ hdev->cpld_shutdown = true;
+}
diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/accel/habanalabs/common/firmware_if.c
index 608ca67527a5..eeb6b2a80fc7 100644
--- a/drivers/misc/habanalabs/common/firmware_if.c
+++ b/drivers/accel/habanalabs/common/firmware_if.c
@@ -6,23 +6,66 @@
*/
#include "habanalabs.h"
-#include "../include/common/hl_boot_if.h"
+#include <linux/habanalabs/hl_boot_if.h>
+#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ctype.h>
+#include <linux/vmalloc.h>
+
+#include <trace/events/habanalabs.h>
#define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */
-struct fw_binning_conf {
- u64 tpc_binning;
- u32 dec_binning;
- u32 hbm_binning;
- u32 edma_binning;
- u32 mme_redundancy;
+static char *comms_cmd_str_arr[COMMS_INVLD_LAST] = {
+ [COMMS_NOOP] = __stringify(COMMS_NOOP),
+ [COMMS_CLR_STS] = __stringify(COMMS_CLR_STS),
+ [COMMS_RST_STATE] = __stringify(COMMS_RST_STATE),
+ [COMMS_PREP_DESC] = __stringify(COMMS_PREP_DESC),
+ [COMMS_DATA_RDY] = __stringify(COMMS_DATA_RDY),
+ [COMMS_EXEC] = __stringify(COMMS_EXEC),
+ [COMMS_RST_DEV] = __stringify(COMMS_RST_DEV),
+ [COMMS_GOTO_WFE] = __stringify(COMMS_GOTO_WFE),
+ [COMMS_SKIP_BMC] = __stringify(COMMS_SKIP_BMC),
+ [COMMS_PREP_DESC_ELBI] = __stringify(COMMS_PREP_DESC_ELBI),
+};
+
+static char *comms_sts_str_arr[COMMS_STS_INVLD_LAST] = {
+ [COMMS_STS_NOOP] = __stringify(COMMS_STS_NOOP),
+ [COMMS_STS_ACK] = __stringify(COMMS_STS_ACK),
+ [COMMS_STS_OK] = __stringify(COMMS_STS_OK),
+ [COMMS_STS_ERR] = __stringify(COMMS_STS_ERR),
+ [COMMS_STS_VALID_ERR] = __stringify(COMMS_STS_VALID_ERR),
+ [COMMS_STS_TIMEOUT_ERR] = __stringify(COMMS_STS_TIMEOUT_ERR),
};
+/**
+ * hl_fw_version_cmp() - compares the FW version to a specific version
+ *
+ * @hdev: pointer to hl_device structure
+ * @major: major number of a reference version
+ * @minor: minor number of a reference version
+ * @subminor: sub-minor number of a reference version
+ *
+ * Return 1 if FW version greater than the reference version, -1 if it's
+ * smaller and 0 if versions are identical.
+ */
+int hl_fw_version_cmp(struct hl_device *hdev, u32 major, u32 minor, u32 subminor)
+{
+ if (hdev->fw_sw_major_ver != major)
+ return (hdev->fw_sw_major_ver > major) ? 1 : -1;
+
+ if (hdev->fw_sw_minor_ver != minor)
+ return (hdev->fw_sw_minor_ver > minor) ? 1 : -1;
+
+ if (hdev->fw_sw_sub_minor_ver != subminor)
+ return (hdev->fw_sw_sub_minor_ver > subminor) ? 1 : -1;
+
+ return 0;
+}
+
static char *extract_fw_ver_from_str(const char *fw_str)
{
char *str, *fw_ver, *whitespace;
@@ -54,41 +97,124 @@ free_fw_ver:
return NULL;
}
-static int extract_fw_sub_versions(struct hl_device *hdev, char *preboot_ver)
+/**
+ * extract_u32_until_given_char() - given a string of the format "<u32><char>*", extract the u32.
+ * @str: the given string
+ * @ver_num: the pointer to the extracted u32 to be returned to the caller.
+ * @given_char: the given char at the end of the u32 in the string
+ *
+ * Return: Upon success, return a pointer to the given_char in the string. Upon failure, return NULL
+ */
+static char *extract_u32_until_given_char(char *str, u32 *ver_num, char given_char)
{
- char major[8], minor[8], *first_dot, *second_dot;
- int rc;
+ char num_str[8] = {}, *ch;
- first_dot = strnstr(preboot_ver, ".", 10);
- if (first_dot) {
- strscpy(major, preboot_ver, first_dot - preboot_ver + 1);
- rc = kstrtou32(major, 10, &hdev->fw_major_version);
- } else {
- rc = -EINVAL;
- }
+ ch = strchrnul(str, given_char);
+ if (*ch == '\0' || ch == str || ch - str >= sizeof(num_str))
+ return NULL;
- if (rc) {
- dev_err(hdev->dev, "Error %d parsing preboot major version\n", rc);
- goto out;
+ memcpy(num_str, str, ch - str);
+ if (kstrtou32(num_str, 10, ver_num))
+ return NULL;
+ return ch;
+}
+
+/**
+ * hl_get_sw_major_minor_subminor() - extract the FW's SW version major, minor, sub-minor
+ * from the version string
+ * @hdev: pointer to the hl_device
+ * @fw_str: the FW's version string
+ *
+ * The extracted version is set in the hdev fields: fw_sw_{major/minor/sub_minor}_ver.
+ *
+ * fw_str is expected to have one of two possible formats, examples:
+ * 1) 'Preboot version hl-gaudi2-1.9.0-fw-42.0.1-sec-3'
+ * 2) 'Preboot version hl-gaudi2-1.9.0-rc-fw-42.0.1-sec-3'
+ * In those examples, the SW major,minor,subminor are correspondingly: 1,9,0.
+ *
+ * Return: 0 for success or a negative error code for failure.
+ */
+static int hl_get_sw_major_minor_subminor(struct hl_device *hdev, const char *fw_str)
+{
+ char *end, *start;
+
+ end = strnstr(fw_str, "-rc-", VERSION_MAX_LEN);
+ if (end == fw_str)
+ return -EINVAL;
+
+ if (!end)
+ end = strnstr(fw_str, "-fw-", VERSION_MAX_LEN);
+
+ if (end == fw_str)
+ return -EINVAL;
+
+ if (!end)
+ return -EINVAL;
+
+ for (start = end - 1; start != fw_str; start--) {
+ if (*start == '-')
+ break;
}
- /* skip the first dot */
- first_dot++;
+ if (start == fw_str)
+ return -EINVAL;
- second_dot = strnstr(first_dot, ".", 10);
- if (second_dot) {
- strscpy(minor, first_dot, second_dot - first_dot + 1);
- rc = kstrtou32(minor, 10, &hdev->fw_minor_version);
- } else {
- rc = -EINVAL;
+ /* start/end point each to the starting and ending hyphen of the sw version e.g. -1.9.0- */
+ start++;
+ start = extract_u32_until_given_char(start, &hdev->fw_sw_major_ver, '.');
+ if (!start)
+ goto err_zero_ver;
+
+ start++;
+ start = extract_u32_until_given_char(start, &hdev->fw_sw_minor_ver, '.');
+ if (!start)
+ goto err_zero_ver;
+
+ start++;
+ start = extract_u32_until_given_char(start, &hdev->fw_sw_sub_minor_ver, '-');
+ if (!start)
+ goto err_zero_ver;
+
+ return 0;
+
+err_zero_ver:
+ hdev->fw_sw_major_ver = 0;
+ hdev->fw_sw_minor_ver = 0;
+ hdev->fw_sw_sub_minor_ver = 0;
+ return -EINVAL;
+}
+
+/**
+ * hl_get_preboot_major_minor() - extract the FW's version major, minor from the version string.
+ * @hdev: pointer to the hl_device
+ * @preboot_ver: the FW's version string
+ *
+ * preboot_ver is expected to be the format of <major>.<minor>.<sub minor>*, e.g: 42.0.1-sec-3
+ * The extracted version is set in the hdev fields: fw_inner_{major/minor}_ver.
+ *
+ * Return: 0 on success, negative error code for failure.
+ */
+static int hl_get_preboot_major_minor(struct hl_device *hdev, char *preboot_ver)
+{
+ preboot_ver = extract_u32_until_given_char(preboot_ver, &hdev->fw_inner_major_ver, '.');
+ if (!preboot_ver) {
+ dev_err(hdev->dev, "Error parsing preboot major version\n");
+ goto err_zero_ver;
}
- if (rc)
- dev_err(hdev->dev, "Error %d parsing preboot minor version\n", rc);
+ preboot_ver++;
-out:
- kfree(preboot_ver);
- return rc;
+ preboot_ver = extract_u32_until_given_char(preboot_ver, &hdev->fw_inner_minor_ver, '.');
+ if (!preboot_ver) {
+ dev_err(hdev->dev, "Error parsing preboot minor version\n");
+ goto err_zero_ver;
+ }
+ return 0;
+
+err_zero_ver:
+ hdev->fw_inner_major_ver = 0;
+ hdev->fw_inner_minor_ver = 0;
+ return -EINVAL;
}
static int hl_request_fw(struct hl_device *hdev,
@@ -245,43 +371,63 @@ int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name,
int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode, u64 value)
{
struct cpucp_packet pkt = {};
+ int rc;
pkt.ctl = cpu_to_le32(opcode << CPUCP_PKT_CTL_OPCODE_SHIFT);
pkt.value = cpu_to_le64(value);
- return hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ if (rc)
+ dev_err(hdev->dev, "Failed to disable FW's PCI access\n");
+
+ return rc;
}
+/**
+ * hl_fw_send_cpu_message() - send CPU message to the device.
+ *
+ * @hdev: pointer to hl_device structure.
+ * @hw_queue_id: HW queue ID
+ * @msg: raw data of the message/packet
+ * @size: size of @msg in bytes
+ * @timeout_us: timeout in usec to wait for CPU reply on the message
+ * @result: return code reported by FW
+ *
+ * send message to the device CPU.
+ *
+ * Return: 0 on success, non-zero for failure.
+ * -ENOMEM: memory allocation failure
+ * -EAGAIN: CPU is disabled (try again when enabled)
+ * -ETIMEDOUT: timeout waiting for FW response
+ * -EIO: protocol error
+ */
int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
- u16 len, u32 timeout, u64 *result)
+ u16 size, u32 timeout_us, u64 *result)
{
struct hl_hw_queue *queue = &hdev->kernel_queues[hw_queue_id];
struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 tmp, expected_ack_val, pi, opcode;
struct cpucp_packet *pkt;
dma_addr_t pkt_dma_addr;
struct hl_bd *sent_bd;
- u32 tmp, expected_ack_val, pi;
- int rc;
+ int rc = 0, fw_rc;
- pkt = hl_cpu_accessible_dma_pool_alloc(hdev, len, &pkt_dma_addr);
+ pkt = hl_cpu_accessible_dma_pool_alloc(hdev, size, &pkt_dma_addr);
if (!pkt) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for packet to CPU\n");
+ dev_err(hdev->dev, "Failed to allocate DMA memory for packet to CPU\n");
return -ENOMEM;
}
- memcpy(pkt, msg, len);
+ memcpy(pkt, msg, size);
mutex_lock(&hdev->send_cpu_message_lock);
/* CPU-CP messages can be sent during soft-reset */
- if (hdev->disabled && !hdev->reset_info.in_compute_reset) {
- rc = 0;
+ if (hdev->disabled && !hdev->reset_info.in_compute_reset)
goto out;
- }
if (hdev->device_cpu_disabled) {
- rc = -EIO;
+ rc = -EAGAIN;
goto out;
}
@@ -297,7 +443,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
* Which means that we don't need to lock the access to the entire H/W
* queues module when submitting a JOB to the CPU queue.
*/
- hl_hw_queue_submit_bd(hdev, queue, hl_queue_inc_ptr(queue->pi), len, pkt_dma_addr);
+ hl_hw_queue_submit_bd(hdev, queue, hl_queue_inc_ptr(queue->pi), size, pkt_dma_addr);
if (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN)
expected_ack_val = queue->pi;
@@ -306,7 +452,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp,
(tmp == expected_ack_val), 1000,
- timeout, true);
+ timeout_us, true);
hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id);
@@ -314,25 +460,61 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
/* If FW performed reset just before sending it a packet, we will get a timeout.
* This is expected behavior, hence no need for error message.
*/
- if (!hl_device_operational(hdev, NULL) && !hdev->reset_info.in_compute_reset)
+ if (!hl_device_operational(hdev, NULL) && !hdev->reset_info.in_compute_reset) {
dev_dbg(hdev->dev, "Device CPU packet timeout (0x%x) due to FW reset\n",
tmp);
- else
- dev_err(hdev->dev, "Device CPU packet timeout (0x%x)\n", tmp);
+ } else {
+ struct hl_bd *bd = queue->kernel_address;
+
+ bd += hl_pi_2_offset(pi);
+
+ dev_err(hdev->dev, "Device CPU packet timeout (status = 0x%x)\n"
+ "Pkt info[%u]: dma_addr: 0x%llx, kernel_addr: %p, len:0x%x, ctl: 0x%x, ptr:0x%llx, dram_bd:%u\n",
+ tmp, pi, pkt_dma_addr, (void *)pkt, bd->len, bd->ctl, bd->ptr,
+ queue->dram_bd);
+ }
hdev->device_cpu_disabled = true;
goto out;
}
tmp = le32_to_cpu(pkt->ctl);
- rc = (tmp & CPUCP_PKT_CTL_RC_MASK) >> CPUCP_PKT_CTL_RC_SHIFT;
- if (rc) {
- dev_dbg(hdev->dev, "F/W ERROR %d for CPU packet %d\n",
- rc, (tmp & CPUCP_PKT_CTL_OPCODE_MASK) >> CPUCP_PKT_CTL_OPCODE_SHIFT);
+ fw_rc = (tmp & CPUCP_PKT_CTL_RC_MASK) >> CPUCP_PKT_CTL_RC_SHIFT;
+ if (fw_rc) {
+ opcode = (tmp & CPUCP_PKT_CTL_OPCODE_MASK) >> CPUCP_PKT_CTL_OPCODE_SHIFT;
+
+ if (!prop->supports_advanced_cpucp_rc) {
+ dev_dbg(hdev->dev, "F/W ERROR %d for CPU packet %d\n", rc, opcode);
+ rc = -EIO;
+ goto scrub_descriptor;
+ }
+
+ switch (fw_rc) {
+ case cpucp_packet_invalid:
+ dev_err(hdev->dev,
+ "CPU packet %d is not supported by F/W\n", opcode);
+ break;
+ case cpucp_packet_fault:
+ dev_err(hdev->dev,
+ "F/W failed processing CPU packet %d\n", opcode);
+ break;
+ case cpucp_packet_invalid_pkt:
+ dev_dbg(hdev->dev,
+ "CPU packet %d is not supported by F/W\n", opcode);
+ break;
+ case cpucp_packet_invalid_params:
+ dev_err(hdev->dev,
+ "F/W reports invalid parameters for CPU packet %d\n", opcode);
+ break;
+
+ default:
+ dev_err(hdev->dev,
+ "Unknown F/W ERROR %d for CPU packet %d\n", rc, opcode);
+ }
/* propagate the return code from the f/w to the callers who want to check it */
if (result)
- *result = rc;
+ *result = fw_rc;
rc = -EIO;
@@ -340,6 +522,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
*result = le64_to_cpu(pkt->result);
}
+scrub_descriptor:
/* Scrub previous buffer descriptor 'ctl' field which contains the
* previous PI value written during packet submission.
* We must do this or else F/W can read an old value upon queue wraparound.
@@ -351,7 +534,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
out:
mutex_unlock(&hdev->send_cpu_message_lock);
- hl_cpu_accessible_dma_pool_free(hdev, len, pkt);
+ hl_cpu_accessible_dma_pool_free(hdev, size, pkt);
return rc;
}
@@ -372,7 +555,7 @@ int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type)
0, &result);
if (rc)
- dev_err(hdev->dev, "failed to unmask RAZWI IRQ %d", event_type);
+ dev_err(hdev->dev, "failed to unmask event %d", event_type);
return rc;
}
@@ -411,7 +594,7 @@ int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr,
total_pkt_size, 0, &result);
if (rc)
- dev_err(hdev->dev, "failed to unmask IRQ array\n");
+ dev_err(hdev->dev, "failed to unmask event array\n");
kfree(pkt);
@@ -421,7 +604,7 @@ int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr,
int hl_fw_test_cpu_queue(struct hl_device *hdev)
{
struct cpucp_packet test_pkt = {};
- u64 result;
+ u64 result = 0;
int rc;
test_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST <<
@@ -462,19 +645,46 @@ void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size,
size);
}
+int hl_fw_send_soft_reset(struct hl_device *hdev)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_SOFT_RESET << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ if (rc)
+ dev_err(hdev->dev, "failed to send soft-reset msg (err = %d)\n", rc);
+
+ return rc;
+}
+
+int hl_fw_send_device_activity(struct hl_device *hdev, bool open)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_ACTIVE_STATUS_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.value = cpu_to_le64(open);
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ if (rc)
+ dev_err(hdev->dev, "failed to send device activity msg(%u)\n", open);
+
+ return rc;
+}
+
int hl_fw_send_heartbeat(struct hl_device *hdev)
{
struct cpucp_packet hb_pkt;
- u64 result;
+ u64 result = 0;
int rc;
memset(&hb_pkt, 0, sizeof(hb_pkt));
- hb_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
+ hb_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST << CPUCP_PKT_CTL_OPCODE_SHIFT);
hb_pkt.value = cpu_to_le64(CPUCP_PACKET_FENCE_VAL);
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &hb_pkt,
- sizeof(hb_pkt), 0, &result);
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &hb_pkt, sizeof(hb_pkt), 0, &result);
if ((rc) || (result != CPUCP_PACKET_FENCE_VAL))
return -EIO;
@@ -485,42 +695,32 @@ int hl_fw_send_heartbeat(struct hl_device *hdev)
rc = -EIO;
}
+ hdev->heartbeat_debug_info.last_pq_heartbeat_ts = ktime_get_real_seconds();
+
return rc;
}
-static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val,
- u32 sts_val)
+static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, u32 sts_val)
{
bool err_exists = false;
if (!(err_val & CPU_BOOT_ERR0_ENABLED))
return false;
- if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL) {
- dev_err(hdev->dev,
- "Device boot error - DRAM initialization failed\n");
- err_exists = true;
- }
+ if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL)
+ dev_err(hdev->dev, "Device boot error - DRAM initialization failed\n");
- if (err_val & CPU_BOOT_ERR0_FIT_CORRUPTED) {
+ if (err_val & CPU_BOOT_ERR0_FIT_CORRUPTED)
dev_err(hdev->dev, "Device boot error - FIT image corrupted\n");
- err_exists = true;
- }
- if (err_val & CPU_BOOT_ERR0_TS_INIT_FAIL) {
- dev_err(hdev->dev,
- "Device boot error - Thermal Sensor initialization failed\n");
- err_exists = true;
- }
+ if (err_val & CPU_BOOT_ERR0_TS_INIT_FAIL)
+ dev_err(hdev->dev, "Device boot error - Thermal Sensor initialization failed\n");
if (err_val & CPU_BOOT_ERR0_BMC_WAIT_SKIPPED) {
if (hdev->bmc_enable) {
- dev_err(hdev->dev,
- "Device boot error - Skipped waiting for BMC\n");
- err_exists = true;
+ dev_err(hdev->dev, "Device boot error - Skipped waiting for BMC\n");
} else {
- dev_info(hdev->dev,
- "Device boot message - Skipped waiting for BMC\n");
+ dev_info(hdev->dev, "Device boot message - Skipped waiting for BMC\n");
/* This is an info so we don't want it to disable the
* device
*/
@@ -528,43 +728,29 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val,
}
}
- if (err_val & CPU_BOOT_ERR0_NIC_DATA_NOT_RDY) {
- dev_err(hdev->dev,
- "Device boot error - Serdes data from BMC not available\n");
- err_exists = true;
- }
+ if (err_val & CPU_BOOT_ERR0_NIC_DATA_NOT_RDY)
+ dev_err(hdev->dev, "Device boot error - Serdes data from BMC not available\n");
- if (err_val & CPU_BOOT_ERR0_NIC_FW_FAIL) {
- dev_err(hdev->dev,
- "Device boot error - NIC F/W initialization failed\n");
- err_exists = true;
- }
+ if (err_val & CPU_BOOT_ERR0_NIC_FW_FAIL)
+ dev_err(hdev->dev, "Device boot error - NIC F/W initialization failed\n");
- if (err_val & CPU_BOOT_ERR0_SECURITY_NOT_RDY) {
- dev_err(hdev->dev,
- "Device boot warning - security not ready\n");
- err_exists = true;
- }
+ if (err_val & CPU_BOOT_ERR0_SECURITY_NOT_RDY)
+ dev_err(hdev->dev, "Device boot warning - security not ready\n");
- if (err_val & CPU_BOOT_ERR0_SECURITY_FAIL) {
+ if (err_val & CPU_BOOT_ERR0_SECURITY_FAIL)
dev_err(hdev->dev, "Device boot error - security failure\n");
- err_exists = true;
- }
- if (err_val & CPU_BOOT_ERR0_EFUSE_FAIL) {
+ if (err_val & CPU_BOOT_ERR0_EFUSE_FAIL)
dev_err(hdev->dev, "Device boot error - eFuse failure\n");
- err_exists = true;
- }
- if (err_val & CPU_BOOT_ERR0_SEC_IMG_VER_FAIL) {
+ if (err_val & CPU_BOOT_ERR0_SEC_IMG_VER_FAIL)
dev_err(hdev->dev, "Device boot error - Failed to load preboot secondary image\n");
- err_exists = true;
- }
- if (err_val & CPU_BOOT_ERR0_PLL_FAIL) {
+ if (err_val & CPU_BOOT_ERR0_PLL_FAIL)
dev_err(hdev->dev, "Device boot error - PLL failure\n");
- err_exists = true;
- }
+
+ if (err_val & CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL)
+ dev_err(hdev->dev, "Device boot error - Failed to set threshold for temperature sensor\n");
if (err_val & CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL) {
/* Ignore this bit, don't prevent driver loading */
@@ -572,47 +758,32 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val,
err_val &= ~CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL;
}
- if (err_val & CPU_BOOT_ERR0_BINNING_FAIL) {
+ if (err_val & CPU_BOOT_ERR0_BINNING_FAIL)
dev_err(hdev->dev, "Device boot error - binning failure\n");
- err_exists = true;
- }
if (sts_val & CPU_BOOT_DEV_STS0_ENABLED)
dev_dbg(hdev->dev, "Device status0 %#x\n", sts_val);
+ if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED)
+ dev_err(hdev->dev, "Device boot warning - Skipped DRAM initialization\n");
+
+ if (err_val & CPU_BOOT_ERR_ENG_ARC_MEM_SCRUB_FAIL)
+ dev_err(hdev->dev, "Device boot error - ARC memory scrub failed\n");
+
/* All warnings should go here in order not to reach the unknown error validation */
- if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) {
- dev_warn(hdev->dev,
- "Device boot warning - Skipped DRAM initialization\n");
- /* This is a warning so we don't want it to disable the
- * device
- */
- err_val &= ~CPU_BOOT_ERR0_DRAM_SKIPPED;
+ if (err_val & CPU_BOOT_ERR0_EEPROM_FAIL) {
+ dev_err(hdev->dev, "Device boot error - EEPROM failure detected\n");
+ err_exists = true;
}
- if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL) {
- dev_warn(hdev->dev,
- "Device boot warning - Failed to load preboot primary image\n");
- /* This is a warning so we don't want it to disable the
- * device as we have a secondary preboot image
- */
- err_val &= ~CPU_BOOT_ERR0_PRI_IMG_VER_FAIL;
- }
+ if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL)
+ dev_warn(hdev->dev, "Device boot warning - Failed to load preboot primary image\n");
- if (err_val & CPU_BOOT_ERR0_TPM_FAIL) {
- dev_warn(hdev->dev,
- "Device boot warning - TPM failure\n");
- /* This is a warning so we don't want it to disable the
- * device
- */
- err_val &= ~CPU_BOOT_ERR0_TPM_FAIL;
- }
+ if (err_val & CPU_BOOT_ERR0_TPM_FAIL)
+ dev_warn(hdev->dev, "Device boot warning - TPM failure\n");
- if (!err_exists && (err_val & ~CPU_BOOT_ERR0_ENABLED)) {
- dev_err(hdev->dev,
- "Device boot error - unknown ERR0 error 0x%08x\n", err_val);
+ if (err_val & CPU_BOOT_ERR_FATAL_MASK)
err_exists = true;
- }
/* return error only if it's in the predefined mask */
if (err_exists && ((err_val & ~CPU_BOOT_ERR0_ENABLED) &
@@ -768,7 +939,7 @@ static int hl_fw_send_msi_info_msg(struct hl_device *hdev)
{
struct cpucp_array_data_packet *pkt;
size_t total_pkt_size, data_size;
- u64 result;
+ u64 result = 0;
int rc;
/* skip sending this info for unsupported ASICs */
@@ -859,11 +1030,10 @@ int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size)
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_EEPROM_TIMEOUT_USEC, &result);
-
if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP EEPROM packet, error %d\n",
- rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP EEPROM packet, error %d\n", rc);
goto out;
}
@@ -904,7 +1074,9 @@ int hl_fw_get_monitor_dump(struct hl_device *hdev, void *data)
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_MON_DUMP_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev, "Failed to handle CPU-CP monitor-dump packet, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP monitor-dump packet, error %d\n", rc);
goto out;
}
@@ -938,8 +1110,9 @@ int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
return rc;
}
counters->rx_throughput = result;
@@ -953,8 +1126,9 @@ int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
return rc;
}
counters->tx_throughput = result;
@@ -967,8 +1141,9 @@ int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
return rc;
}
counters->replay_cnt = (u32) result;
@@ -988,9 +1163,9 @@ int hl_fw_cpucp_total_energy_get(struct hl_device *hdev, u64 *total_energy)
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CpuCP total energy pkt, error %d\n",
- rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CpuCP total energy pkt, error %d\n", rc);
return rc;
}
@@ -1066,7 +1241,8 @@ int hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index,
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev, "Failed to read PLL info, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to read PLL info, error %d\n", rc);
return rc;
}
@@ -1093,7 +1269,8 @@ int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power)
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev, "Failed to read power, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to read power, error %d\n", rc);
return rc;
}
@@ -1130,8 +1307,9 @@ int hl_fw_dram_replaced_row_get(struct hl_device *hdev,
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
HL_CPUCP_INFO_TIMEOUT_USEC, &result);
if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP replaced rows info pkt, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP replaced rows info pkt, error %d\n", rc);
goto out;
}
@@ -1156,7 +1334,8 @@ int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num)
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
if (rc) {
- dev_err(hdev->dev,
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
"Failed to handle CPU-CP pending rows info pkt, error %d\n", rc);
goto out;
}
@@ -1197,7 +1376,7 @@ void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev)
COMMS_RST_DEV, 0, false,
hdev->fw_loader.cpu_timeout);
if (rc)
- dev_warn(hdev->dev, "Failed sending COMMS_RST_DEV\n");
+ dev_err(hdev->dev, "Failed sending COMMS_RST_DEV\n");
} else {
WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_RST_DEV);
}
@@ -1205,8 +1384,10 @@ void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev)
void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev)
{
- struct static_fw_load_mgr *static_loader =
- &hdev->fw_loader.static_loader;
+ struct fw_load_mgr *fw_loader = &hdev->fw_loader;
+ u32 status, cpu_boot_status_reg, cpu_timeout;
+ struct static_fw_load_mgr *static_loader;
+ struct pre_fw_load_props *pre_fw_load;
int rc;
if (hdev->device_cpu_is_halted)
@@ -1214,12 +1395,28 @@ void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev)
/* Stop device CPU to make sure nothing bad happens */
if (hdev->asic_prop.dynamic_fw_load) {
+ pre_fw_load = &fw_loader->pre_fw_load;
+ cpu_timeout = fw_loader->cpu_timeout;
+ cpu_boot_status_reg = pre_fw_load->cpu_boot_status_reg;
+
rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader,
- COMMS_GOTO_WFE, 0, true,
- hdev->fw_loader.cpu_timeout);
- if (rc)
- dev_warn(hdev->dev, "Failed sending COMMS_GOTO_WFE\n");
+ COMMS_GOTO_WFE, 0, false, cpu_timeout);
+ if (rc) {
+ dev_err(hdev->dev, "Failed sending COMMS_GOTO_WFE\n");
+ } else {
+ rc = hl_poll_timeout(
+ hdev,
+ cpu_boot_status_reg,
+ status,
+ status == CPU_BOOT_STATUS_IN_WFE,
+ hdev->fw_poll_interval_usec,
+ cpu_timeout);
+ if (rc)
+ dev_err(hdev->dev, "Current status=%u. Timed-out updating to WFE\n",
+ status);
+ }
} else {
+ static_loader = &hdev->fw_loader.static_loader;
WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_GOTO_WFE);
msleep(static_loader->cpu_reset_wait_msec);
@@ -1278,19 +1475,23 @@ static void detect_cpu_boot_status(struct hl_device *hdev, u32 status)
dev_err(hdev->dev,
"Device boot progress - Stuck in preboot after security initialization\n");
break;
+ case CPU_BOOT_STATUS_FW_SHUTDOWN_PREP:
+ dev_err(hdev->dev,
+ "Device boot progress - Stuck in preparation for shutdown\n");
+ break;
default:
dev_err(hdev->dev,
- "Device boot progress - Invalid status code %d\n",
- status);
+ "Device boot progress - Invalid or unexpected status code %d\n", status);
break;
}
}
-static int hl_fw_wait_preboot_ready(struct hl_device *hdev)
+int hl_fw_wait_preboot_ready(struct hl_device *hdev)
{
struct pre_fw_load_props *pre_fw_load = &hdev->fw_loader.pre_fw_load;
- u32 status;
- int rc;
+ u32 status = 0, timeout;
+ int rc, tries = 1, fw_err = 0;
+ bool preboot_still_runs;
/* Need to check two possible scenarios:
*
@@ -1300,6 +1501,8 @@ static int hl_fw_wait_preboot_ready(struct hl_device *hdev)
* All other status values - for older firmwares where the uboot was
* loaded from the FLASH
*/
+ timeout = pre_fw_load->wait_for_preboot_timeout;
+retry:
rc = hl_poll_timeout(
hdev,
pre_fw_load->cpu_boot_status_reg,
@@ -1308,20 +1511,37 @@ static int hl_fw_wait_preboot_ready(struct hl_device *hdev)
(status == CPU_BOOT_STATUS_READY_TO_BOOT) ||
(status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT),
hdev->fw_poll_interval_usec,
- pre_fw_load->wait_for_preboot_timeout);
+ timeout);
+ /*
+ * if F/W reports "security-ready" it means preboot might take longer.
+ * If the field 'wait_for_preboot_extended_timeout' is non 0 we wait again
+ * with that timeout
+ */
+ preboot_still_runs = (status == CPU_BOOT_STATUS_SECURITY_READY ||
+ status == CPU_BOOT_STATUS_IN_PREBOOT ||
+ status == CPU_BOOT_STATUS_FW_SHUTDOWN_PREP ||
+ status == CPU_BOOT_STATUS_DRAM_RDY);
+
+ if (rc && tries && preboot_still_runs) {
+ tries--;
+ if (pre_fw_load->wait_for_preboot_extended_timeout) {
+ timeout = pre_fw_load->wait_for_preboot_extended_timeout;
+ goto retry;
+ }
+ }
- if (rc) {
- dev_err(hdev->dev, "CPU boot ready status timeout\n");
+ /* If we read all FF, then something is totally wrong, no point
+ * of reading specific errors
+ */
+ if (status != -1)
+ fw_err = fw_read_errors(hdev, pre_fw_load->boot_err0_reg,
+ pre_fw_load->boot_err1_reg,
+ pre_fw_load->sts_boot_dev_sts0_reg,
+ pre_fw_load->sts_boot_dev_sts1_reg);
+ if (rc || fw_err) {
detect_cpu_boot_status(hdev, status);
-
- /* If we read all FF, then something is totally wrong, no point
- * of reading specific errors
- */
- if (status != -1)
- fw_read_errors(hdev, pre_fw_load->boot_err0_reg,
- pre_fw_load->boot_err1_reg,
- pre_fw_load->sts_boot_dev_sts0_reg,
- pre_fw_load->sts_boot_dev_sts1_reg);
+ dev_err(hdev->dev, "CPU boot %s (status = %d)\n",
+ fw_err ? "failed due to an error" : "ready timeout", status);
return -EIO;
}
@@ -1476,6 +1696,8 @@ static void hl_fw_preboot_update_state(struct hl_device *hdev)
*/
prop->hard_reset_done_by_fw = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN);
+ prop->fw_security_enabled = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_SECURITY_EN);
+
dev_dbg(hdev->dev, "Firmware preboot boot device status0 %#x\n",
cpu_boot_dev_sts0);
@@ -1514,7 +1736,7 @@ int hl_fw_read_preboot_status(struct hl_device *hdev)
hdev->asic_funcs->init_firmware_preload_params(hdev);
/*
- * In order to determine boot method (static VS dymanic) we need to
+ * In order to determine boot method (static VS dynamic) we need to
* read the boot caps register
*/
rc = hl_fw_read_preboot_caps(hdev);
@@ -1590,6 +1812,7 @@ static void hl_fw_dynamic_send_cmd(struct hl_device *hdev,
val = FIELD_PREP(COMMS_COMMAND_CMD_MASK, cmd);
val |= FIELD_PREP(COMMS_COMMAND_SIZE_MASK, size);
+ trace_habanalabs_comms_send_cmd(&hdev->pdev->dev, comms_cmd_str_arr[cmd]);
WREG32(le32_to_cpu(dyn_regs->kmd_msg_to_cpu), val);
}
@@ -1647,6 +1870,8 @@ static int hl_fw_dynamic_wait_for_status(struct hl_device *hdev,
dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
+ trace_habanalabs_comms_wait_status(&hdev->pdev->dev, comms_sts_str_arr[expected_status]);
+
/* Wait for expected status */
rc = hl_poll_timeout(
hdev,
@@ -1662,6 +1887,9 @@ static int hl_fw_dynamic_wait_for_status(struct hl_device *hdev,
return -EIO;
}
+ trace_habanalabs_comms_wait_status_done(&hdev->pdev->dev,
+ comms_sts_str_arr[expected_status]);
+
/*
* skip storing FW response for NOOP to preserve the actual desired
* FW status
@@ -1734,8 +1962,12 @@ int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev,
{
int rc;
+ trace_habanalabs_comms_protocol_cmd(&hdev->pdev->dev, comms_cmd_str_arr[cmd]);
+
/* first send clear command to clean former commands */
rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader);
+ if (rc)
+ return rc;
/* send the actual command */
hl_fw_dynamic_send_cmd(hdev, fw_loader, cmd, size);
@@ -1781,7 +2013,7 @@ int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev,
*
* @return the CRC32 result
*
- * NOTE: kernel's CRC32 differ's from standard CRC32 calculation.
+ * NOTE: kernel's CRC32 differs from standard CRC32 calculation.
* in order to be aligned we need to flip the bits of both the input
* initial CRC and kernel's CRC32 result.
* in addition both sides use initial CRC of 0,
@@ -1798,7 +2030,7 @@ static u32 hl_fw_compat_crc32(u8 *data, size_t size)
*
* @hdev: pointer to the habanalabs device structure
* @addr: device address of memory transfer
- * @size: memory transter size
+ * @size: memory transfer size
* @region: PCI memory region
*
* @return 0 on success, otherwise non-zero error code
@@ -1838,7 +2070,7 @@ static int hl_fw_dynamic_validate_memory_bound(struct hl_device *hdev,
*
* @hdev: pointer to the habanalabs device structure
* @fw_loader: managing structure for loading device's FW
- * @fw_desc: the descriptor form FW
+ * @fw_desc: the descriptor from FW
*
* @return 0 on success, otherwise non-zero error code
*/
@@ -1854,50 +2086,36 @@ static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev,
u64 addr;
int rc;
- if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC) {
- dev_err(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n",
+ if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC)
+ dev_dbg(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n",
fw_desc->header.magic);
- return -EIO;
- }
- if (fw_desc->header.version != HL_COMMS_DESC_VER) {
- dev_err(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n",
+ if (fw_desc->header.version != HL_COMMS_DESC_VER)
+ dev_dbg(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n",
fw_desc->header.version);
- return -EIO;
- }
/*
- * calc CRC32 of data without header.
+ * Calc CRC32 of data without header. use the size of the descriptor
+ * reported by firmware, without calculating it ourself, to allow adding
+ * more fields to the lkd_fw_comms_desc structure.
* note that no alignment/stride address issues here as all structures
- * are 64 bit padded
+ * are 64 bit padded.
*/
- data_size = sizeof(struct lkd_fw_comms_desc) -
- sizeof(struct comms_desc_header);
- data_ptr = (u8 *)fw_desc + sizeof(struct comms_desc_header);
-
- if (le16_to_cpu(fw_desc->header.size) != data_size) {
- dev_err(hdev->dev,
- "Invalid descriptor size 0x%x, expected size 0x%zx\n",
- le16_to_cpu(fw_desc->header.size), data_size);
- return -EIO;
- }
+ data_ptr = (u8 *)fw_desc + sizeof(struct comms_msg_header);
+ data_size = le16_to_cpu(fw_desc->header.size);
data_crc32 = hl_fw_compat_crc32(data_ptr, data_size);
-
if (data_crc32 != le32_to_cpu(fw_desc->header.crc32)) {
- dev_err(hdev->dev,
- "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n",
- data_crc32, fw_desc->header.crc32);
+ dev_err(hdev->dev, "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n",
+ data_crc32, fw_desc->header.crc32);
return -EIO;
}
/* find memory region to which to copy the image */
addr = le64_to_cpu(fw_desc->img_addr);
region_id = hl_get_pci_memory_region(hdev, addr);
- if ((region_id != PCI_REGION_SRAM) &&
- ((region_id != PCI_REGION_DRAM))) {
- dev_err(hdev->dev,
- "Invalid region to copy FW image address=%llx\n", addr);
+ if ((region_id != PCI_REGION_SRAM) && ((region_id != PCI_REGION_DRAM))) {
+ dev_err(hdev->dev, "Invalid region to copy FW image address=%llx\n", addr);
return -EIO;
}
@@ -1914,8 +2132,7 @@ static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev,
fw_loader->dynamic_loader.fw_image_size,
region);
if (rc) {
- dev_err(hdev->dev,
- "invalid mem transfer request for FW image\n");
+ dev_err(hdev->dev, "invalid mem transfer request for FW image\n");
return rc;
}
@@ -1945,6 +2162,43 @@ static int hl_fw_dynamic_validate_response(struct hl_device *hdev,
return rc;
}
+/*
+ * hl_fw_dynamic_read_descriptor_msg - read and show the ascii msg that sent by fw
+ *
+ * @hdev: pointer to the habanalabs device structure
+ * @fw_desc: the descriptor from FW
+ */
+static void hl_fw_dynamic_read_descriptor_msg(struct hl_device *hdev,
+ struct lkd_fw_comms_desc *fw_desc)
+{
+ int i;
+ char *msg;
+
+ for (i = 0 ; i < LKD_FW_ASCII_MSG_MAX ; i++) {
+ if (!fw_desc->ascii_msg[i].valid)
+ return;
+
+ /* force NULL termination */
+ msg = fw_desc->ascii_msg[i].msg;
+ msg[LKD_FW_ASCII_MSG_MAX_LEN - 1] = '\0';
+
+ switch (fw_desc->ascii_msg[i].msg_lvl) {
+ case LKD_FW_ASCII_MSG_ERR:
+ dev_err(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
+ break;
+ case LKD_FW_ASCII_MSG_WRN:
+ dev_warn(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
+ break;
+ case LKD_FW_ASCII_MSG_INF:
+ dev_info(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
+ break;
+ default:
+ dev_dbg(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
+ break;
+ }
+ }
+}
+
/**
* hl_fw_dynamic_read_and_validate_descriptor - read and validate FW descriptor
*
@@ -1959,8 +2213,10 @@ static int hl_fw_dynamic_read_and_validate_descriptor(struct hl_device *hdev,
struct lkd_fw_comms_desc *fw_desc;
struct pci_mem_region *region;
struct fw_response *response;
- enum pci_region region_id;
+ void *temp_fw_desc;
void __iomem *src;
+ u16 fw_data_size;
+ enum pci_region region_id;
int rc;
fw_desc = &fw_loader->dynamic_loader.comm_desc;
@@ -1987,9 +2243,33 @@ static int hl_fw_dynamic_read_and_validate_descriptor(struct hl_device *hdev,
fw_loader->dynamic_loader.fw_desc_valid = false;
src = hdev->pcie_bar[region->bar_id] + region->offset_in_bar +
response->ram_offset;
+
+ /*
+ * We do the copy of the fw descriptor in 2 phases:
+ * 1. copy the header + data info according to our lkd_fw_comms_desc definition.
+ * then we're able to read the actual data size provided by fw.
+ * this is needed for cases where data in descriptor was changed(add/remove)
+ * in embedded specs header file before updating lkd copy of the header file
+ * 2. copy descriptor to temporary buffer with aligned size and send it to validation
+ */
memcpy_fromio(fw_desc, src, sizeof(struct lkd_fw_comms_desc));
+ fw_data_size = le16_to_cpu(fw_desc->header.size);
- return hl_fw_dynamic_validate_descriptor(hdev, fw_loader, fw_desc);
+ temp_fw_desc = vzalloc(sizeof(struct comms_msg_header) + fw_data_size);
+ if (!temp_fw_desc)
+ return -ENOMEM;
+
+ memcpy_fromio(temp_fw_desc, src, sizeof(struct comms_msg_header) + fw_data_size);
+
+ rc = hl_fw_dynamic_validate_descriptor(hdev, fw_loader,
+ (struct lkd_fw_comms_desc *) temp_fw_desc);
+
+ if (!rc)
+ hl_fw_dynamic_read_descriptor_msg(hdev, temp_fw_desc);
+
+ vfree(temp_fw_desc);
+
+ return rc;
}
/**
@@ -2030,6 +2310,7 @@ static int hl_fw_dynamic_read_device_fw_version(struct hl_device *hdev,
struct asic_fixed_properties *prop = &hdev->asic_prop;
char *preboot_ver, *boot_ver;
char btl_ver[32];
+ int rc;
switch (fwc) {
case FW_COMP_BOOT_FIT:
@@ -2043,22 +2324,22 @@ static int hl_fw_dynamic_read_device_fw_version(struct hl_device *hdev,
break;
case FW_COMP_PREBOOT:
strscpy(prop->preboot_ver, fw_version, VERSION_MAX_LEN);
- preboot_ver = strnstr(prop->preboot_ver, "Preboot",
- VERSION_MAX_LEN);
+ preboot_ver = strnstr(prop->preboot_ver, "Preboot", VERSION_MAX_LEN);
+ dev_info(hdev->dev, "preboot full version: '%s'\n", preboot_ver);
+
if (preboot_ver && preboot_ver != prop->preboot_ver) {
strscpy(btl_ver, prop->preboot_ver,
min((int) (preboot_ver - prop->preboot_ver), 31));
dev_info(hdev->dev, "%s\n", btl_ver);
}
+ rc = hl_get_sw_major_minor_subminor(hdev, preboot_ver);
+ if (rc)
+ return rc;
preboot_ver = extract_fw_ver_from_str(prop->preboot_ver);
if (preboot_ver) {
- int rc;
-
- dev_info(hdev->dev, "preboot version %s\n", preboot_ver);
-
- /* This function takes care of freeing preboot_ver */
- rc = extract_fw_sub_versions(hdev, preboot_ver);
+ rc = hl_get_preboot_major_minor(hdev, preboot_ver);
+ kfree(preboot_ver);
if (rc)
return rc;
}
@@ -2246,16 +2527,6 @@ static int hl_fw_dynamic_load_image(struct hl_device *hdev,
if (rc)
goto release_fw;
- /* update state according to boot stage */
- if (cur_fwc == FW_COMP_BOOT_FIT) {
- struct cpu_dyn_regs *dyn_regs;
-
- dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
- hl_fw_boot_fit_update_state(hdev,
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
- }
-
/* copy boot fit to space allocated by FW */
rc = hl_fw_dynamic_copy_image(hdev, fw, fw_loader);
if (rc)
@@ -2302,7 +2573,7 @@ static int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev,
hdev->fw_poll_interval_usec,
dyn_loader->wait_for_bl_timeout);
if (rc) {
- dev_err(hdev->dev, "failed to wait for boot\n");
+ dev_err(hdev->dev, "failed to wait for boot (status = %d)\n", status);
return rc;
}
@@ -2329,7 +2600,7 @@ static int hl_fw_dynamic_wait_for_linux_active(struct hl_device *hdev,
hdev->fw_poll_interval_usec,
fw_loader->cpu_timeout);
if (rc) {
- dev_err(hdev->dev, "failed to wait for Linux\n");
+ dev_err(hdev->dev, "failed to wait for Linux (status = %d)\n", status);
return rc;
}
@@ -2407,63 +2678,54 @@ static void hl_fw_linux_update_state(struct hl_device *hdev,
static int hl_fw_dynamic_send_msg(struct hl_device *hdev,
struct fw_load_mgr *fw_loader, u8 msg_type, void *data)
{
- struct lkd_msg_comms msg;
+ struct lkd_msg_comms *msg;
int rc;
- memset(&msg, 0, sizeof(msg));
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
/* create message to be sent */
- msg.header.type = msg_type;
- msg.header.size = cpu_to_le16(sizeof(struct comms_msg_header));
- msg.header.magic = cpu_to_le32(HL_COMMS_MSG_MAGIC);
+ msg->header.type = msg_type;
+ msg->header.size = cpu_to_le16(sizeof(struct comms_msg_header));
+ msg->header.magic = cpu_to_le32(HL_COMMS_MSG_MAGIC);
switch (msg_type) {
case HL_COMMS_RESET_CAUSE_TYPE:
- msg.reset_cause = *(__u8 *) data;
+ msg->reset_cause = *(__u8 *) data;
break;
- case HL_COMMS_BINNING_CONF_TYPE:
- {
- struct fw_binning_conf *binning_conf = (struct fw_binning_conf *) data;
-
- msg.tpc_binning_conf = cpu_to_le64(binning_conf->tpc_binning);
- msg.dec_binning_conf = cpu_to_le32(binning_conf->dec_binning);
- msg.hbm_binning_conf = cpu_to_le32(binning_conf->hbm_binning);
- msg.edma_binning_conf = cpu_to_le32(binning_conf->edma_binning);
- msg.mme_redundancy_conf = cpu_to_le32(binning_conf->mme_redundancy);
- break;
- }
-
default:
dev_err(hdev->dev,
"Send COMMS message - invalid message type %u\n",
msg_type);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader,
sizeof(struct lkd_msg_comms));
if (rc)
- return rc;
+ goto out;
/* copy message to space allocated by FW */
- rc = hl_fw_dynamic_copy_msg(hdev, &msg, fw_loader);
+ rc = hl_fw_dynamic_copy_msg(hdev, msg, fw_loader);
if (rc)
- return rc;
+ goto out;
rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY,
0, true,
fw_loader->cpu_timeout);
if (rc)
- return rc;
+ goto out;
rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC,
0, true,
fw_loader->cpu_timeout);
- if (rc)
- return rc;
- return 0;
+out:
+ kfree(msg);
+ return rc;
}
/**
@@ -2488,7 +2750,7 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev,
struct fw_load_mgr *fw_loader)
{
struct cpu_dyn_regs *dyn_regs;
- int rc;
+ int rc, fw_error_rc;
dev_info(hdev->dev,
"Loading %sfirmware to device, may take some time...\n",
@@ -2503,13 +2765,6 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev,
*/
dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
- /* if no preboot loaded indication- wait for preboot */
- if (!(hdev->fw_loader.fw_comp_loaded & FW_TYPE_PREBOOT_CPU)) {
- rc = hl_fw_wait_preboot_ready(hdev);
- if (rc)
- return -EIO;
- }
-
rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_RST_STATE,
0, true,
fw_loader->cpu_timeout);
@@ -2526,14 +2781,47 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev,
hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
}
+ rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, sizeof(struct lkd_msg_comms));
+ if (rc)
+ goto protocol_err;
+
+ if (hdev->asic_prop.support_dynamic_resereved_fw_size)
+ hdev->asic_prop.reserved_fw_mem_size =
+ le32_to_cpu(fw_loader->dynamic_loader.comm_desc.rsvd_mem_size_mb) * SZ_1M;
+
if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) {
- rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, 0);
- if (rc)
- goto protocol_err;
+ struct lkd_fw_binning_info *binning_info;
/* read preboot version */
- return hl_fw_dynamic_read_device_fw_version(hdev, FW_COMP_PREBOOT,
+ rc = hl_fw_dynamic_read_device_fw_version(hdev, FW_COMP_PREBOOT,
fw_loader->dynamic_loader.comm_desc.cur_fw_ver);
+ if (rc)
+ return rc;
+
+ /* read binning info from preboot */
+ if (hdev->support_preboot_binning) {
+ binning_info = &fw_loader->dynamic_loader.comm_desc.binning_info;
+ hdev->tpc_binning = le64_to_cpu(binning_info->tpc_mask_l);
+ hdev->dram_binning = le32_to_cpu(binning_info->dram_mask);
+ hdev->edma_binning = le32_to_cpu(binning_info->edma_mask);
+ hdev->decoder_binning = le32_to_cpu(binning_info->dec_mask);
+ hdev->rotator_binning = le32_to_cpu(binning_info->rot_mask);
+
+ rc = hdev->asic_funcs->set_dram_properties(hdev);
+ if (rc)
+ return rc;
+
+ rc = hdev->asic_funcs->set_binning_masks(hdev);
+ if (rc)
+ return rc;
+
+ dev_dbg(hdev->dev,
+ "Read binning masks: tpc: 0x%llx, dram: 0x%llx, edma: 0x%x, dec: 0x%x, rot:0x%x\n",
+ hdev->tpc_binning, hdev->dram_binning, hdev->edma_binning,
+ hdev->decoder_binning, hdev->rotator_binning);
+ }
+
+ return 0;
}
/* load boot fit to FW */
@@ -2544,26 +2832,30 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev,
goto protocol_err;
}
+ rc = hl_fw_dynamic_wait_for_boot_fit_active(hdev, fw_loader);
+ if (rc)
+ goto protocol_err;
+
+ hl_fw_boot_fit_update_state(hdev,
+ le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
+ le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
+
/*
* when testing FW load (without Linux) on PLDM we don't want to
* wait until boot fit is active as it may take several hours.
- * instead, we load the bootfit and let it do all initializations in
+ * instead, we load the bootfit and let it do all initialization in
* the background.
*/
if (hdev->pldm && !(hdev->fw_components & FW_TYPE_LINUX))
return 0;
- rc = hl_fw_dynamic_wait_for_boot_fit_active(hdev, fw_loader);
- if (rc)
- goto protocol_err;
-
/* Enable DRAM scrambling before Linux boot and after successful
* UBoot
*/
hdev->asic_funcs->init_cpu_scrambler_dram(hdev);
if (!(hdev->fw_components & FW_TYPE_LINUX)) {
- dev_info(hdev->dev, "Skip loading Linux F/W\n");
+ dev_dbg(hdev->dev, "Skip loading Linux F/W\n");
return 0;
}
@@ -2590,19 +2882,23 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev,
if (rc)
goto protocol_err;
- hl_fw_linux_update_state(hdev, le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
+ hl_fw_linux_update_state(hdev,
+ le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
hl_fw_dynamic_update_linux_interrupt_if(hdev);
- return 0;
-
protocol_err:
- if (fw_loader->dynamic_loader.fw_desc_valid)
- fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0),
+ if (fw_loader->dynamic_loader.fw_desc_valid) {
+ fw_error_rc = fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0),
le32_to_cpu(dyn_regs->cpu_boot_err1),
le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
+
+ if (fw_error_rc)
+ return fw_error_rc;
+ }
+
return rc;
}
@@ -2651,7 +2947,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev,
if (rc) {
dev_dbg(hdev->dev,
- "No boot fit request received, resuming boot\n");
+ "No boot fit request received (status = %d), resuming boot\n", status);
} else {
rc = hdev->asic_funcs->load_boot_fit_to_device(hdev);
if (rc)
@@ -2674,7 +2970,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev,
if (rc) {
dev_err(hdev->dev,
- "Timeout waiting for boot fit load ack\n");
+ "Timeout waiting for boot fit load ack (status = %d)\n", status);
goto out;
}
@@ -2752,7 +3048,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev,
if (rc) {
dev_err(hdev->dev,
- "Failed to get ACK on skipping BMC, %d\n",
+ "Failed to get ACK on skipping BMC (status = %d)\n",
status);
WREG32(msg_to_cpu_reg, KMD_MSG_NA);
rc = -EIO;
@@ -2779,7 +3075,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev,
"Device reports FIT image is corrupted\n");
else
dev_err(hdev->dev,
- "Failed to load firmware to device, %d\n",
+ "Failed to load firmware to device (status = %d)\n",
status);
rc = -EIO;
@@ -2889,10 +3185,10 @@ long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr)
pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
-
if (rc) {
- dev_err(hdev->dev, "Failed to get frequency of PLL %d, error %d\n",
- used_pll_idx, rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to get frequency of PLL %d, error %d\n",
+ used_pll_idx, rc);
return rc;
}
@@ -2916,8 +3212,7 @@ void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq)
pkt.value = cpu_to_le64(freq);
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
-
- if (rc)
+ if (rc && rc != -EAGAIN)
dev_err(hdev->dev, "Failed to set frequency to PLL %d, error %d\n",
used_pll_idx, rc);
}
@@ -2933,9 +3228,9 @@ long hl_fw_get_max_power(struct hl_device *hdev)
pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT);
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
-
if (rc) {
- dev_err(hdev->dev, "Failed to get max power, error %d\n", rc);
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev, "Failed to get max power, error %d\n", rc);
return rc;
}
@@ -2957,7 +3252,86 @@ void hl_fw_set_max_power(struct hl_device *hdev)
pkt.value = cpu_to_le64(hdev->max_power);
rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
-
- if (rc)
+ if (rc && rc != -EAGAIN)
dev_err(hdev->dev, "Failed to set max power, error %d\n", rc);
}
+
+static int hl_fw_get_sec_attest_data(struct hl_device *hdev, u32 packet_id, void *data, u32 size,
+ u32 nonce, u32 timeout)
+{
+ struct cpucp_packet pkt = {};
+ dma_addr_t req_dma_addr;
+ void *req_cpu_addr;
+ int rc;
+
+ req_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, size, &req_dma_addr);
+ if (!req_cpu_addr) {
+ dev_err(hdev->dev,
+ "Failed to allocate DMA memory for CPU-CP packet %u\n", packet_id);
+ return -ENOMEM;
+ }
+
+ memset(data, 0, size);
+
+ pkt.ctl = cpu_to_le32(packet_id << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.addr = cpu_to_le64(req_dma_addr);
+ pkt.data_max_size = cpu_to_le32(size);
+ pkt.nonce = cpu_to_le32(nonce);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), timeout, NULL);
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP pkt %u, error %d\n", packet_id, rc);
+ goto out;
+ }
+
+ memcpy(data, req_cpu_addr, size);
+
+out:
+ hl_cpu_accessible_dma_pool_free(hdev, size, req_cpu_addr);
+
+ return rc;
+}
+
+int hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info,
+ u32 nonce)
+{
+ return hl_fw_get_sec_attest_data(hdev, CPUCP_PACKET_SEC_ATTEST_GET, sec_attest_info,
+ sizeof(struct cpucp_sec_attest_info), nonce,
+ HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC);
+}
+
+int hl_fw_get_dev_info_signed(struct hl_device *hdev,
+ struct cpucp_dev_info_signed *dev_info_signed, u32 nonce)
+{
+ return hl_fw_get_sec_attest_data(hdev, CPUCP_PACKET_INFO_SIGNED_GET, dev_info_signed,
+ sizeof(struct cpucp_dev_info_signed), nonce,
+ HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC);
+}
+
+int hl_fw_send_generic_request(struct hl_device *hdev, enum hl_passthrough_type sub_opcode,
+ dma_addr_t buff, u32 *size)
+{
+ struct cpucp_packet pkt = {};
+ u64 result;
+ int rc = 0;
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_GENERIC_PASSTHROUGH << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.addr = cpu_to_le64(buff);
+ pkt.data_max_size = cpu_to_le32(*size);
+ pkt.pkt_subidx = cpu_to_le32(sub_opcode);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *)&pkt, sizeof(pkt),
+ HL_CPUCP_INFO_TIMEOUT_USEC, &result);
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err(hdev->dev, "failed to send CPUCP data of generic fw pkt\n");
+ } else {
+ dev_dbg(hdev->dev, "generic pkt was successful, result: 0x%llx\n", result);
+ }
+
+ *size = (u32)result;
+
+ return rc;
+}
diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/accel/habanalabs/common/habanalabs.h
index d59bba9e55c9..d94c2ba22a6a 100644
--- a/drivers/misc/habanalabs/common/habanalabs.h
+++ b/drivers/accel/habanalabs/common/habanalabs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
*
- * Copyright 2016-2022 HabanaLabs, Ltd.
+ * Copyright 2016-2023 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
@@ -8,10 +8,10 @@
#ifndef HABANALABSP_H_
#define HABANALABSP_H_
-#include "../include/common/cpucp_if.h"
+#include <linux/habanalabs/cpucp_if.h>
#include "../include/common/qman_if.h"
#include "../include/hw_ip/mmu/mmu_general.h"
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include <linux/cdev.h>
#include <linux/iopoll.h>
@@ -29,11 +29,18 @@
#include <linux/coresight.h>
#include <linux/dma-buf.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+
+#include "security.h"
+
#define HL_NAME "habanalabs"
struct hl_device;
struct hl_fpriv;
+#define PCI_VENDOR_ID_HABANALABS 0x1da3
+
/* Use upper bits of mmap offset to store habana driver specific information.
* bits[63:59] - Encode mmap type
* bits[45:0] - mmap offset value
@@ -50,36 +57,42 @@ struct hl_fpriv;
#define HL_MMAP_OFFSET_VALUE_MASK (0x1FFFFFFFFFFFull >> PAGE_SHIFT)
#define HL_MMAP_OFFSET_VALUE_GET(off) (off & HL_MMAP_OFFSET_VALUE_MASK)
-#define HL_PENDING_RESET_PER_SEC 10
-#define HL_PENDING_RESET_MAX_TRIALS 60 /* 10 minutes */
-#define HL_PENDING_RESET_LONG_SEC 60
+#define HL_PENDING_RESET_PER_SEC 10
+#define HL_PENDING_RESET_MAX_TRIALS 60 /* 10 minutes */
+#define HL_PENDING_RESET_LONG_SEC 60
+/*
+ * In device fini, wait 10 minutes for user processes to be terminated after we kill them.
+ * This is needed to prevent situation of clearing resources while user processes are still alive.
+ */
+#define HL_WAIT_PROCESS_KILL_ON_DEVICE_FINI 600
#define HL_HARD_RESET_MAX_TIMEOUT 120
#define HL_PLDM_HARD_RESET_MAX_TIMEOUT (HL_HARD_RESET_MAX_TIMEOUT * 3)
#define HL_DEVICE_TIMEOUT_USEC 1000000 /* 1 s */
-#define HL_HEARTBEAT_PER_USEC 5000000 /* 5 s */
+#define HL_HEARTBEAT_PER_USEC 10000000 /* 10 s */
#define HL_PLL_LOW_JOB_FREQ_USEC 5000000 /* 5 s */
#define HL_CPUCP_INFO_TIMEOUT_USEC 10000000 /* 10s */
#define HL_CPUCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */
#define HL_CPUCP_MON_DUMP_TIMEOUT_USEC 10000000 /* 10s */
+#define HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC 10000000 /* 10s */
#define HL_FW_STATUS_POLL_INTERVAL_USEC 10000 /* 10ms */
#define HL_FW_COMMS_STATUS_PLDM_POLL_INTERVAL_USEC 1000000 /* 1s */
#define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */
-#define HL_SIM_MAX_TIMEOUT_US 100000000 /* 100s */
-
#define HL_INVALID_QUEUE UINT_MAX
#define HL_COMMON_USER_CQ_INTERRUPT_ID 0xFFF
#define HL_COMMON_DEC_INTERRUPT_ID 0xFFE
-#define HL_STATE_DUMP_HIST_LEN 5
+#define HL_STATE_DUMP_HIST_LEN 5
+#define HL_DBGFS_CFG_ACCESS_HIST_LEN 20
+#define HL_DBGFS_CFG_ACCESS_HIST_TIMEOUT_SEC 2 /* 2s */
/* Default value for device reset trigger , an invalid value */
#define HL_RESET_TRIGGER_DEFAULT 0xFF
@@ -93,8 +106,10 @@ struct hl_fpriv;
/* MMU */
#define MMU_HASH_TABLE_BITS 7 /* 1 << 7 buckets */
+#define TIMESTAMP_FREE_NODES_NUM 512
+
/**
- * enum hl_mmu_page_table_locaion - mmu page table location
+ * enum hl_mmu_page_table_location - mmu page table location
* @MMU_DR_PGT: page-table is located on device DRAM.
* @MMU_HR_PGT: page-table is located on host memory.
* @MMU_NUM_PGT_LOCATIONS: number of page-table locations currently supported.
@@ -105,18 +120,6 @@ enum hl_mmu_page_table_location {
MMU_NUM_PGT_LOCATIONS /* num of PGT locations */
};
-/**
- * enum hl_mmu_enablement - what mmu modules to enable
- * @MMU_EN_NONE: mmu disabled.
- * @MMU_EN_ALL: enable all.
- * @MMU_EN_PMMU_ONLY: Enable only the PMMU leaving the DMMU disabled.
- */
-enum hl_mmu_enablement {
- MMU_EN_NONE = 0,
- MMU_EN_ALL = 1,
- MMU_EN_PMMU_ONLY = 3, /* N/A for Goya/Gaudi */
-};
-
/*
* HL_RSVD_SOBS 'sync stream' reserved sync objects per QMAN stream
* HL_RSVD_MONS 'sync stream' reserved monitors per QMAN stream
@@ -143,6 +146,24 @@ enum hl_mmu_enablement {
#define HL_MAX_DCORES 8
+/* DMA alloc/free wrappers */
+#define hl_asic_dma_alloc_coherent(hdev, size, dma_handle, flags) \
+ hl_asic_dma_alloc_coherent_caller(hdev, size, dma_handle, flags, __func__)
+
+#define hl_asic_dma_pool_zalloc(hdev, size, mem_flags, dma_handle) \
+ hl_asic_dma_pool_zalloc_caller(hdev, size, mem_flags, dma_handle, __func__)
+
+#define hl_asic_dma_free_coherent(hdev, size, cpu_addr, dma_handle) \
+ hl_asic_dma_free_coherent_caller(hdev, size, cpu_addr, dma_handle, __func__)
+
+#define hl_asic_dma_pool_free(hdev, vaddr, dma_addr) \
+ hl_asic_dma_pool_free_caller(hdev, vaddr, dma_addr, __func__)
+
+#define hl_dma_map_sgtable(hdev, sgt, dir) \
+ hl_dma_map_sgtable_caller(hdev, sgt, dir, __func__)
+#define hl_dma_unmap_sgtable(hdev, sgt, dir) \
+ hl_dma_unmap_sgtable_caller(hdev, sgt, dir, __func__)
+
/*
* Reset Flags
*
@@ -171,6 +192,9 @@ enum hl_mmu_enablement {
*
* - HL_DRV_RESET_DELAY
* Set if a delay should be added before the reset
+ *
+ * - HL_DRV_RESET_FROM_WD_THR
+ * Set if the caller is the device release watchdog thread
*/
#define HL_DRV_RESET_HARD (1 << 0)
@@ -181,6 +205,7 @@ enum hl_mmu_enablement {
#define HL_DRV_RESET_BYPASS_REQ_TO_FW (1 << 5)
#define HL_DRV_RESET_FW_FATAL_ERR (1 << 6)
#define HL_DRV_RESET_DELAY (1 << 7)
+#define HL_DRV_RESET_FROM_WD_THR (1 << 8)
/*
* Security
@@ -208,6 +233,7 @@ enum hl_protection_levels {
* struct iterate_module_ctx - HW module iterator
* @fn: function to apply to each HW module instance
* @data: optional internal data to the function iterator
+ * @rc: return code for optional use of iterator/iterator-caller
*/
struct iterate_module_ctx {
/*
@@ -217,10 +243,12 @@ struct iterate_module_ctx {
* @inst: HW module instance within the block
* @offset: current HW module instance offset from the 1-st HW module instance
* in the 1-st block
- * @data: function specific data
+ * @ctx: the iterator context.
*/
- void (*fn)(struct hl_device *hdev, int block, int inst, u32 offset, void *data);
+ void (*fn)(struct hl_device *hdev, int block, int inst, u32 offset,
+ struct iterate_module_ctx *ctx);
void *data;
+ int rc;
};
struct hl_block_glbl_sec {
@@ -342,7 +370,10 @@ enum hl_cs_type {
CS_TYPE_WAIT,
CS_TYPE_COLLECTIVE_WAIT,
CS_RESERVE_SIGNALS,
- CS_UNRESERVE_SIGNALS
+ CS_UNRESERVE_SIGNALS,
+ CS_TYPE_ENGINE_CORE,
+ CS_TYPE_ENGINES,
+ CS_TYPE_FLUSH_PCI_HBW_WRITES,
};
/*
@@ -414,18 +445,22 @@ enum hl_collective_mode {
* a CB handle can be provided for jobs on this queue.
* Otherwise, a CB address must be provided.
* @collective_mode: collective mode of current queue
+ * @q_dram_bd_address: PQ dram address, used when PQ need to reside in DRAM.
* @driver_only: true if only the driver is allowed to send a job to this queue,
* false otherwise.
* @binned: True if the queue is binned out and should not be used
* @supports_sync_stream: True if queue supports sync stream
+ * @dram_bd: True if the bd should be copied to dram, needed for PQ which has been allocated on dram
*/
struct hw_queue_properties {
enum hl_queue_type type;
enum queue_cb_alloc_flags cb_alloc_flags;
enum hl_collective_mode collective_mode;
+ u64 q_dram_bd_address;
u8 driver_only;
u8 binned;
u8 supports_sync_stream;
+ u8 dram_bd;
};
/**
@@ -512,6 +547,8 @@ struct hl_hints_range {
/**
* struct asic_fixed_properties - ASIC specific immutable properties.
* @hw_queues_props: H/W queues properties.
+ * @special_blocks: points to an array containing special blocks info.
+ * @skip_special_blocks_cfg: special blocks skip configs.
* @cpucp_info: received various information from CPU-CP regarding the H/W, e.g.
* available sensors.
* @uboot_ver: F/W U-boot version.
@@ -522,8 +559,7 @@ struct hl_hints_range {
* allocated with huge pages.
* @hints_dram_reserved_va_range: dram hint addresses reserved range.
* @hints_host_reserved_va_range: host hint addresses reserved range.
- * @hints_host_hpage_reserved_va_range: host huge page hint addresses reserved
- * range.
+ * @hints_host_hpage_reserved_va_range: host huge page hint addresses reserved range.
* @sram_base_address: SRAM physical start address.
* @sram_end_address: SRAM physical end address.
* @sram_user_base_address - SRAM physical start address for user access.
@@ -544,10 +580,6 @@ struct hl_hints_range {
* @tpc_binning_mask: which TPCs are binned. 0 means usable and 1 means binned.
* @dram_enabled_mask: which DRAMs are enabled.
* @dram_binning_mask: which DRAMs are binned. 0 means usable, 1 means binned.
- * @cb_va_start_addr: virtual start address of command buffers which are mapped
- * to the device's MMU.
- * @cb_va_end_addr: virtual end address of command buffers which are mapped to
- * the device's MMU.
* @dram_hints_align_mask: dram va hint addresses alignment mask which is used
* for hints validity check.
* @cfg_base_address: config space base address.
@@ -558,13 +590,13 @@ struct hl_hints_range {
* @host_base_address: host physical start address for host DMA from device
* @host_end_address: host physical end address for host DMA from device
* @max_freq_value: current max clk frequency.
+ * @engine_core_interrupt_reg_addr: interrupt register address for engine core to use
+ * in order to raise events toward FW.
* @clk_pll_index: clock PLL index that specify which PLL determines the clock
* we display to the user
* @mmu_pgt_size: MMU page tables total size.
* @mmu_pte_size: PTE size in MMU page tables.
- * @mmu_hop_table_size: MMU hop table size.
- * @mmu_hop0_tables_total_size: total size of MMU hop0 tables.
- * @dram_page_size: page size for MMU DRAM allocation.
+ * @dram_page_size: The DRAM physical page size.
* @cfg_size: configuration space size on SRAM.
* @sram_size: total size of SRAM.
* @max_asid: maximum number of open contexts (ASIDs).
@@ -578,8 +610,8 @@ struct hl_hints_range {
* @cb_pool_cb_cnt: number of CBs in the CB pool.
* @cb_pool_cb_size: size of each CB in the CB pool.
* @decoder_enabled_mask: which decoders are enabled.
- * @decoder_binning_mask: which decoders are binned, 0 means usable and 1
- * means binned (at most one binned decoder per dcore).
+ * @decoder_binning_mask: which decoders are binned, 0 means usable and 1 means binned.
+ * @rotator_enabled_mask: which rotators are enabled.
* @edma_enabled_mask: which EDMAs are enabled.
* @edma_binning_mask: which EDMAs are binned, 0 means usable and 1 means
* binned (at most one binned DMA).
@@ -614,6 +646,15 @@ struct hl_hints_range {
* which the property supports_user_set_page_size is true
* (i.e. the DRAM supports multiple page sizes), otherwise
* it will shall be equal to dram_page_size.
+ * @num_engine_cores: number of engine cpu cores.
+ * @max_num_of_engines: maximum number of all engines in the ASIC.
+ * @num_of_special_blocks: special_blocks array size.
+ * @glbl_err_max_cause_num: global err max cause number.
+ * @hbw_flush_reg: register to read to generate HBW flush. value of 0 means HBW flush is
+ * not supported.
+ * @reserved_fw_mem_size: size of dram memory reserved for FW.
+ * @fw_event_queue_size: queue size for events from CPU-CP.
+ * A value of 0 means using the default HL_EQ_SIZE_IN_BYTES value.
* @collective_first_sob: first sync object available for collective use
* @collective_first_mon: first monitor available for collective use
* @sync_stream_first_sob: first sync object available for sync stream use
@@ -624,6 +665,8 @@ struct hl_hints_range {
* @first_available_cq: first available CQ for the user.
* @user_interrupt_count: number of user interrupts.
* @user_dec_intr_count: number of decoder interrupts exposed to user.
+ * @tpc_interrupt_id: interrupt id for TPC to use in order to raise events towards the host.
+ * @eq_interrupt_id: interrupt id for EQ, uses to synchronize EQ interrupts in hard-reset.
* @cache_line_size: device cache line size.
* @server_type: Server type that the ASIC is currently installed in.
* The value is according to enum hl_server_type in uapi file.
@@ -657,10 +700,16 @@ struct hl_hints_range {
* @configurable_stop_on_err: is stop-on-error option configurable via debugfs.
* @set_max_power_on_device_init: true if need to set max power in F/W on device init.
* @supports_user_set_page_size: true if user can set the allocation page size.
- * @dma_mask: the dma mask to be set for this device
+ * @dma_mask: the dma mask to be set for this device.
+ * @supports_advanced_cpucp_rc: true if new cpucp opcodes are supported.
+ * @supports_engine_modes: true if changing engines/engine_cores modes is supported.
+ * @support_dynamic_resereved_fw_size: true if we support dynamic reserved size for fw.
+ * @supports_nvme: indicates whether the asic supports NVMe P2P DMA.
*/
struct asic_fixed_properties {
struct hw_queue_properties *hw_queues_props;
+ struct hl_special_block_info *special_blocks;
+ struct hl_skip_blocks_cfg skip_special_blocks_cfg;
struct cpucp_info cpucp_info;
char uboot_ver[VERSION_MAX_LEN];
char preboot_ver[VERSION_MAX_LEN];
@@ -689,8 +738,6 @@ struct asic_fixed_properties {
u64 tpc_binning_mask;
u64 dram_enabled_mask;
u64 dram_binning_mask;
- u64 cb_va_start_addr;
- u64 cb_va_end_addr;
u64 dram_hints_align_mask;
u64 cfg_base_address;
u64 mmu_cache_mng_addr;
@@ -699,11 +746,10 @@ struct asic_fixed_properties {
u64 host_base_address;
u64 host_end_address;
u64 max_freq_value;
+ u64 engine_core_interrupt_reg_addr;
u32 clk_pll_index;
u32 mmu_pgt_size;
u32 mmu_pte_size;
- u32 mmu_hop_table_size;
- u32 mmu_hop0_tables_total_size;
u32 dram_page_size;
u32 cfg_size;
u32 sram_size;
@@ -719,6 +765,7 @@ struct asic_fixed_properties {
u32 cb_pool_cb_size;
u32 decoder_enabled_mask;
u32 decoder_binning_mask;
+ u32 rotator_enabled_mask;
u32 edma_enabled_mask;
u32 edma_binning_mask;
u32 max_pending_cs;
@@ -734,6 +781,13 @@ struct asic_fixed_properties {
u32 faulty_dram_cluster_map;
u32 xbar_edge_enabled_mask;
u32 device_mem_alloc_default_page_size;
+ u32 num_engine_cores;
+ u32 max_num_of_engines;
+ u32 num_of_special_blocks;
+ u32 glbl_err_max_cause_num;
+ u32 hbw_flush_reg;
+ u32 reserved_fw_mem_size;
+ u32 fw_event_queue_size;
u16 collective_first_sob;
u16 collective_first_mon;
u16 sync_stream_first_sob;
@@ -744,6 +798,8 @@ struct asic_fixed_properties {
u16 first_available_cq[HL_MAX_DCORES];
u16 user_interrupt_count;
u16 user_dec_intr_count;
+ u16 tpc_interrupt_id;
+ u16 eq_interrupt_id;
u16 cache_line_size;
u16 server_type;
u8 completion_queues_count;
@@ -766,6 +822,10 @@ struct asic_fixed_properties {
u8 set_max_power_on_device_init;
u8 supports_user_set_page_size;
u8 dma_mask;
+ u8 supports_advanced_cpucp_rc;
+ u8 supports_engine_modes;
+ u8 support_dynamic_resereved_fw_size;
+ u8 supports_nvme;
};
/**
@@ -797,7 +857,7 @@ struct hl_fence {
* @lock: spinlock to protect fence.
* @hdev: habanalabs device structure.
* @hw_sob: the H/W SOB used in this signal/wait CS.
- * @encaps_sig_hdl: encaps signals hanlder.
+ * @encaps_sig_hdl: encaps signals handler.
* @cs_seq: command submission sequence number.
* @type: type of the CS - signal/wait.
* @sob_val: the SOB value that is used in this signal/wait CS.
@@ -849,6 +909,18 @@ struct hl_mem_mgr {
};
/**
+ * struct hl_mem_mgr_fini_stats - describes statistics returned during memory manager teardown.
+ * @n_busy_cb: the amount of CB handles that could not be removed
+ * @n_busy_ts: the amount of TS handles that could not be removed
+ * @n_busy_other: the amount of any other type of handles that could not be removed
+ */
+struct hl_mem_mgr_fini_stats {
+ u32 n_busy_cb;
+ u32 n_busy_ts;
+ u32 n_busy_other;
+};
+
+/**
* struct hl_mmap_mem_buf_behavior - describes unified memory manager buffer behavior
* @topic: string identifier used for logging
* @mem_id: memory type identifier, embedded in the handle and used to identify
@@ -898,14 +970,15 @@ struct hl_mmap_mem_buf {
* @buf: back pointer to the parent mappable memory buffer
* @debugfs_list: node in debugfs list of command buffers.
* @pool_list: node in pool list of command buffers.
- * @va_block_list: list of virtual addresses blocks of the CB if it is mapped to
- * the device's MMU.
* @kernel_address: Holds the CB's kernel virtual address.
+ * @virtual_addr: Holds the CB's virtual address.
* @bus_address: Holds the CB's DMA address.
* @size: holds the CB's size.
+ * @roundup_size: holds the cb size after roundup to page size.
* @cs_cnt: holds number of CS that this CB participates in.
+ * @is_handle_destroyed: atomic boolean indicating whether or not the CB handle was destroyed.
* @is_pool: true if CB was acquired from the pool, false otherwise.
- * @is_internal: internaly allocated
+ * @is_internal: internally allocated
* @is_mmu_mapped: true if the CB is mapped to the device's MMU.
*/
struct hl_cb {
@@ -914,11 +987,13 @@ struct hl_cb {
struct hl_mmap_mem_buf *buf;
struct list_head debugfs_list;
struct list_head pool_list;
- struct list_head va_block_list;
void *kernel_address;
+ u64 virtual_addr;
dma_addr_t bus_address;
u32 size;
+ u32 roundup_size;
atomic_t cs_cnt;
+ atomic_t is_handle_destroyed;
u8 is_pool;
u8 is_internal;
u8 is_mmu_mapped;
@@ -996,6 +1071,8 @@ struct hl_encaps_signals_mgr {
* @collective_mode: collective mode of current queue
* @kernel_address: holds the queue's kernel virtual address.
* @bus_address: holds the queue's DMA address.
+ * @pq_dram_address: hold the dram address when the PQ is allocated, used when dram_bd is true in
+ * queue properites.
* @pi: holds the queue's pi value.
* @ci: holds the queue's ci value, AS CALCULATED BY THE DRIVER (not real ci).
* @hw_queue_id: the id of the H/W queue.
@@ -1005,6 +1082,7 @@ struct hl_encaps_signals_mgr {
* @valid: is the queue valid (we have array of 32 queues, not all of them
* exist).
* @supports_sync_stream: True if queue supports sync stream
+ * @dram_bd: True if the bd should be copied to dram, needed for PQ which has been allocated on dram
*/
struct hl_hw_queue {
struct hl_cs_job **shadow_queue;
@@ -1013,6 +1091,7 @@ struct hl_hw_queue {
enum hl_collective_mode collective_mode;
void *kernel_address;
dma_addr_t bus_address;
+ u64 pq_dram_address;
u32 pi;
atomic_t ci;
u32 hw_queue_id;
@@ -1021,6 +1100,7 @@ struct hl_hw_queue {
u16 int_queue_len;
u8 valid;
u8 supports_sync_stream;
+ u8 dram_bd;
};
/**
@@ -1045,20 +1125,51 @@ struct hl_cq {
atomic_t free_slots_cnt;
};
+enum hl_user_interrupt_type {
+ HL_USR_INTERRUPT_CQ = 0,
+ HL_USR_INTERRUPT_DECODER,
+ HL_USR_INTERRUPT_TPC,
+ HL_USR_INTERRUPT_UNEXPECTED
+};
+
+/**
+ * struct hl_ts_free_jobs - holds user interrupt ts free nodes related data
+ * @free_nodes_pool: pool of nodes to be used for free timestamp jobs
+ * @free_nodes_length: number of nodes in free_nodes_pool
+ * @next_avail_free_node_idx: index of the next free node in the pool
+ *
+ * the free nodes pool must be protected by the user interrupt lock
+ * to avoid race between different interrupts which are using the same
+ * ts buffer with different offsets.
+ */
+struct hl_ts_free_jobs {
+ struct timestamp_reg_free_node *free_nodes_pool;
+ u32 free_nodes_length;
+ u32 next_avail_free_node_idx;
+};
+
/**
* struct hl_user_interrupt - holds user interrupt information
* @hdev: pointer to the device structure
+ * @ts_free_jobs_data: timestamp free jobs related data
+ * @type: user interrupt type
* @wait_list_head: head to the list of user threads pending on this interrupt
+ * @ts_list_head: head to the list of timestamp records
* @wait_list_lock: protects wait_list_head
+ * @ts_list_lock: protects ts_list_head
+ * @timestamp: last timestamp taken upon interrupt
* @interrupt_id: msix interrupt id
- * @is_decoder: whether this entry represents a decoder interrupt
*/
struct hl_user_interrupt {
- struct hl_device *hdev;
- struct list_head wait_list_head;
- spinlock_t wait_list_lock;
- u32 interrupt_id;
- bool is_decoder;
+ struct hl_device *hdev;
+ struct hl_ts_free_jobs ts_free_jobs_data;
+ enum hl_user_interrupt_type type;
+ struct list_head wait_list_head;
+ struct list_head ts_list_head;
+ spinlock_t wait_list_lock;
+ spinlock_t ts_list_lock;
+ ktime_t timestamp;
+ u32 interrupt_id;
};
/**
@@ -1066,11 +1177,15 @@ struct hl_user_interrupt {
* @free_objects_node: node in the list free_obj_jobs
* @cq_cb: pointer to cq command buffer to be freed
* @buf: pointer to timestamp buffer to be freed
+ * @in_use: indicates whether the node still in use in workqueue thread.
+ * @dynamic_alloc: indicates whether the node was allocated dynamically in the interrupt handler
*/
struct timestamp_reg_free_node {
struct list_head free_objects_node;
struct hl_cb *cq_cb;
struct hl_mmap_mem_buf *buf;
+ atomic_t in_use;
+ u8 dynamic_alloc;
};
/* struct timestamp_reg_work_obj - holds the timestamp registration free objects job
@@ -1079,17 +1194,21 @@ struct timestamp_reg_free_node {
* @free_obj: workqueue object to free timestamp registration node objects
* @hdev: pointer to the device structure
* @free_obj_head: list of free jobs nodes (node type timestamp_reg_free_node)
+ * @dynamic_alloc_free_obj_head: list of free jobs nodes which were dynamically allocated in the
+ * interrupt handler.
*/
struct timestamp_reg_work_obj {
struct work_struct free_obj;
struct hl_device *hdev;
struct list_head *free_obj_head;
+ struct list_head *dynamic_alloc_free_obj_head;
};
/* struct timestamp_reg_info - holds the timestamp registration related data.
* @buf: pointer to the timestamp buffer which include both user/kernel buffers.
* relevant only when doing timestamps records registration.
* @cq_cb: pointer to CQ counter CB.
+ * @interrupt: interrupt that the node hanged on it's wait list.
* @timestamp_kernel_addr: timestamp handle address, where to set timestamp
* relevant only when doing timestamps records
* registration.
@@ -1099,25 +1218,26 @@ struct timestamp_reg_work_obj {
* allocating records dynamically.
*/
struct timestamp_reg_info {
- struct hl_mmap_mem_buf *buf;
- struct hl_cb *cq_cb;
- u64 *timestamp_kernel_addr;
- u8 in_use;
+ struct hl_mmap_mem_buf *buf;
+ struct hl_cb *cq_cb;
+ struct hl_user_interrupt *interrupt;
+ u64 *timestamp_kernel_addr;
+ bool in_use;
};
/**
* struct hl_user_pending_interrupt - holds a context to a user thread
* pending on an interrupt
* @ts_reg_info: holds the timestamps registration nodes info
- * @wait_list_node: node in the list of user threads pending on an interrupt
+ * @list_node: node in the list of user threads pending on an interrupt or timestamp
* @fence: hl fence object for interrupt completion
* @cq_target_value: CQ target value
* @cq_kernel_addr: CQ kernel address, to be used in the cq interrupt
- * handler for taget value comparison
+ * handler for target value comparison
*/
struct hl_user_pending_interrupt {
struct timestamp_reg_info ts_reg_info;
- struct list_head wait_list_node;
+ struct list_head list_node;
struct hl_fence fence;
u64 cq_target_value;
u64 *cq_kernel_addr;
@@ -1128,6 +1248,7 @@ struct hl_user_pending_interrupt {
* @hdev: pointer to the device structure
* @kernel_address: holds the queue's kernel virtual address
* @bus_address: holds the queue's DMA address
+ * @size: the event queue size
* @ci: ci inside the queue
* @prev_eqe_index: the index of the previous event queue entry. The index of
* the current entry's index must be +1 of the previous one.
@@ -1139,6 +1260,7 @@ struct hl_eq {
struct hl_device *hdev;
void *kernel_address;
dma_addr_t bus_address;
+ u32 size;
u32 ci;
u32 prev_eqe_index;
bool check_eqe_index;
@@ -1147,15 +1269,15 @@ struct hl_eq {
/**
* struct hl_dec - describes a decoder sw instance.
* @hdev: pointer to the device structure.
- * @completion_abnrm_work: workqueue object to run when decoder generates an error interrupt
+ * @abnrm_intr_work: workqueue work item to run when decoder generates an error interrupt.
* @core_id: ID of the decoder.
* @base_addr: base address of the decoder.
*/
struct hl_dec {
- struct hl_device *hdev;
- struct work_struct completion_abnrm_work;
- u32 core_id;
- u32 base_addr;
+ struct hl_device *hdev;
+ struct work_struct abnrm_intr_work;
+ u32 core_id;
+ u32 base_addr;
};
/**
@@ -1165,15 +1287,20 @@ struct hl_dec {
* @ASIC_GAUDI: Gaudi device (HL-2000).
* @ASIC_GAUDI_SEC: Gaudi secured device (HL-2000).
* @ASIC_GAUDI2: Gaudi2 device.
- * @ASIC_GAUDI2_SEC: Gaudi2 secured device.
+ * @ASIC_GAUDI2B: Gaudi2B device.
+ * @ASIC_GAUDI2C: Gaudi2C device.
+ * @ASIC_GAUDI2D: Gaudi2D device.
*/
enum hl_asic_type {
ASIC_INVALID,
+
ASIC_GOYA,
ASIC_GAUDI,
ASIC_GAUDI_SEC,
ASIC_GAUDI2,
- ASIC_GAUDI2_SEC,
+ ASIC_GAUDI2B,
+ ASIC_GAUDI2C,
+ ASIC_GAUDI2D,
};
struct hl_cs_parser;
@@ -1316,6 +1443,8 @@ struct dynamic_fw_load_mgr {
* @boot_err0_reg: boot_err0 register address
* @boot_err1_reg: boot_err1 register address
* @wait_for_preboot_timeout: timeout to poll for preboot ready
+ * @wait_for_preboot_extended_timeout: timeout to pull for preboot ready in case where we know
+ * preboot needs longer time.
*/
struct pre_fw_load_props {
u32 cpu_boot_status_reg;
@@ -1324,6 +1453,7 @@ struct pre_fw_load_props {
u32 boot_err0_reg;
u32 boot_err1_reg;
u32 wait_for_preboot_timeout;
+ u32 wait_for_preboot_extended_timeout;
};
/**
@@ -1372,6 +1502,18 @@ struct fw_load_mgr {
struct hl_cs;
/**
+ * struct engines_data - asic engines data
+ * @buf: buffer for engines data in ascii
+ * @actual_size: actual size of data that was written by the driver to the allocated buffer
+ * @allocated_buf_size: total size of allocated buffer
+ */
+struct engines_data {
+ char *buf;
+ int actual_size;
+ u32 allocated_buf_size;
+};
+
+/**
* struct hl_asic_funcs - ASIC specific functions that are can be called from
* common code.
* @early_init: sets up early driver state (pre sw_init), doesn't configure H/W.
@@ -1411,11 +1553,9 @@ struct hl_cs;
* @asic_dma_pool_free: free small DMA allocation from pool.
* @cpu_accessible_dma_pool_alloc: allocate CPU PQ packet from DMA pool.
* @cpu_accessible_dma_pool_free: free CPU PQ packet from DMA pool.
- * @asic_dma_unmap_single: unmap a single DMA buffer
- * @asic_dma_map_single: map a single buffer to a DMA
- * @hl_dma_unmap_sgtable: DMA unmap scatter-gather table.
+ * @dma_unmap_sgtable: DMA unmap scatter-gather table.
+ * @dma_map_sgtable: DMA map scatter-gather table.
* @cs_parser: parse Command Submission.
- * @asic_dma_map_sgtable: DMA map scatter-gather table.
* @add_end_of_cb_packets: Add packets to the end of CB, if device requires it.
* @update_eq_ci: update event queue CI.
* @context_switch: called upon ASID context switch.
@@ -1434,11 +1574,9 @@ struct hl_cs;
* @send_heartbeat: send is-alive packet to CPU-CP and verify response.
* @debug_coresight: perform certain actions on Coresight for debugging.
* @is_device_idle: return true if device is idle, false otherwise.
- * @non_hard_reset_late_init: perform certain actions needed after a reset which is not hard-reset
+ * @compute_reset_late_init: perform certain actions needed after a compute reset
* @hw_queues_lock: acquire H/W queues lock.
* @hw_queues_unlock: release H/W queues lock.
- * @kdma_lock: acquire H/W queues lock. Relevant from GRECO ASIC
- * @kdma_unlock: release H/W queues lock. Relevant from GRECO ASIC
* @get_pci_id: retrieve PCI ID.
* @get_eeprom_data: retrieve EEPROM data from F/W.
* @get_monitor_dump: retrieve monitor registers dump from F/W.
@@ -1498,6 +1636,11 @@ struct hl_cs;
* @check_if_razwi_happened: check if there was a razwi due to RR violation.
* @access_dev_mem: access device memory
* @set_dram_bar_base: set the base of the DRAM BAR
+ * @set_engine_cores: set a config command to engine cores
+ * @set_engines: set a config command to user engines
+ * @send_device_activity: indication to FW about device availability
+ * @set_dram_properties: set DRAM related properties.
+ * @set_binning_masks: set binning/enable masks for all relevant components.
*/
struct hl_asic_funcs {
int (*early_init)(struct hl_device *hdev);
@@ -1507,7 +1650,7 @@ struct hl_asic_funcs {
int (*sw_init)(struct hl_device *hdev);
int (*sw_fini)(struct hl_device *hdev);
int (*hw_init)(struct hl_device *hdev);
- void (*hw_fini)(struct hl_device *hdev, bool hard_reset, bool fw_reset);
+ int (*hw_fini)(struct hl_device *hdev, bool hard_reset, bool fw_reset);
void (*halt_engines)(struct hl_device *hdev, bool hard_reset, bool fw_reset);
int (*suspend)(struct hl_device *hdev);
int (*resume)(struct hl_device *hdev);
@@ -1533,18 +1676,11 @@ struct hl_asic_funcs {
size_t size, dma_addr_t *dma_handle);
void (*cpu_accessible_dma_pool_free)(struct hl_device *hdev,
size_t size, void *vaddr);
- void (*asic_dma_unmap_single)(struct hl_device *hdev,
- dma_addr_t dma_addr, int len,
+ void (*dma_unmap_sgtable)(struct hl_device *hdev, struct sg_table *sgt,
enum dma_data_direction dir);
- dma_addr_t (*asic_dma_map_single)(struct hl_device *hdev,
- void *addr, int len,
- enum dma_data_direction dir);
- void (*hl_dma_unmap_sgtable)(struct hl_device *hdev,
- struct sg_table *sgt,
+ int (*dma_map_sgtable)(struct hl_device *hdev, struct sg_table *sgt,
enum dma_data_direction dir);
int (*cs_parser)(struct hl_device *hdev, struct hl_cs_parser *parser);
- int (*asic_dma_map_sgtable)(struct hl_device *hdev, struct sg_table *sgt,
- enum dma_data_direction dir);
void (*add_end_of_cb_packets)(struct hl_device *hdev,
void *kernel_address, u32 len,
u32 original_len,
@@ -1570,13 +1706,11 @@ struct hl_asic_funcs {
int (*mmu_prefetch_cache_range)(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size);
int (*send_heartbeat)(struct hl_device *hdev);
int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data);
- bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr,
- u8 mask_len, struct seq_file *s);
- int (*non_hard_reset_late_init)(struct hl_device *hdev);
+ bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e);
+ int (*compute_reset_late_init)(struct hl_device *hdev);
void (*hw_queues_lock)(struct hl_device *hdev);
void (*hw_queues_unlock)(struct hl_device *hdev);
- void (*kdma_lock)(struct hl_device *hdev, int dcore_id);
- void (*kdma_unlock)(struct hl_device *hdev, int dcore_id);
u32 (*get_pci_id)(struct hl_device *hdev);
int (*get_eeprom_data)(struct hl_device *hdev, void *data, size_t max_size);
int (*get_monitor_dump)(struct hl_device *hdev, void *data);
@@ -1634,6 +1768,13 @@ struct hl_asic_funcs {
int (*access_dev_mem)(struct hl_device *hdev, enum pci_region region_type,
u64 addr, u64 *val, enum debugfs_access_type acc_type);
u64 (*set_dram_bar_base)(struct hl_device *hdev, u64 addr);
+ int (*set_engine_cores)(struct hl_device *hdev, u32 *core_ids,
+ u32 num_cores, u32 core_command);
+ int (*set_engines)(struct hl_device *hdev, u32 *engine_ids,
+ u32 num_engines, u32 engine_command);
+ int (*send_device_activity)(struct hl_device *hdev, bool open);
+ int (*set_dram_properties)(struct hl_device *hdev);
+ int (*set_binning_masks)(struct hl_device *hdev);
};
@@ -1694,17 +1835,22 @@ struct hl_cs_counters_atomic {
* struct hl_dmabuf_priv - a dma-buf private object.
* @dmabuf: pointer to dma-buf object.
* @ctx: pointer to the dma-buf owner's context.
- * @phys_pg_pack: pointer to physical page pack if the dma-buf was exported for
- * memory allocation handle.
- * @device_address: physical address of the device's memory. Relevant only
- * if phys_pg_pack is NULL (dma-buf was exported from address).
- * The total size can be taken from the dmabuf object.
+ * @phys_pg_pack: pointer to physical page pack if the dma-buf was exported
+ * where virtual memory is supported.
+ * @memhash_hnode: pointer to the memhash node. this object holds the export count.
+ * @offset: the offset into the buffer from which the memory is exported.
+ * Relevant only if virtual memory is supported and phys_pg_pack is being used.
+ * device_phys_addr: physical address of the device's memory. Relevant only
+ * if phys_pg_pack is NULL (dma-buf was exported from address).
+ * The total size can be taken from the dmabuf object.
*/
struct hl_dmabuf_priv {
struct dma_buf *dmabuf;
struct hl_ctx *ctx;
struct hl_vm_phys_pg_pack *phys_pg_pack;
- uint64_t device_address;
+ struct hl_vm_hash_node *memhash_hnode;
+ u64 offset;
+ u64 device_phys_addr;
};
#define HL_CS_OUTCOME_HISTORY_LEN 256
@@ -1727,10 +1873,10 @@ struct hl_cs_outcome {
/**
* struct hl_cs_outcome_store - represents a limited store of completed CS outcomes
- * @outcome_map: index of completed CS searcheable by sequence number
+ * @outcome_map: index of completed CS searchable by sequence number
* @used_list: list of outcome objects currently in use
* @free_list: list of outcome objects currently not in use
- * @nodes_pool: a static pool of preallocated outcome objects
+ * @nodes_pool: a static pool of pre-allocated outcome objects
* @db_lock: any operation on the store must take this lock
*/
struct hl_cs_outcome_store {
@@ -1752,21 +1898,21 @@ struct hl_cs_outcome_store {
* @hpriv: pointer to the private (Kernel Driver) data of the process (fd).
* @hdev: pointer to the device structure.
* @refcount: reference counter for the context. Context is released only when
- * this hits 0l. It is incremented on CS and CS_WAIT.
+ * this hits 0. It is incremented on CS and CS_WAIT.
* @cs_pending: array of hl fence objects representing pending CS.
- * @outcome_store: storage data structure used to remember ouitcomes of completed
+ * @outcome_store: storage data structure used to remember outcomes of completed
* command submissions for a long time after CS id wraparound.
* @va_range: holds available virtual addresses for host and dram mappings.
* @mem_hash_lock: protects the mem_hash.
- * @mmu_lock: protects the MMU page tables. Any change to the PGT, modifying the
- * MMU hash or walking the PGT requires talking this lock.
* @hw_block_list_lock: protects the HW block memory list.
+ * @ts_reg_lock: timestamp registration ioctls lock.
* @debugfs_list: node in debugfs list of contexts.
* @hw_block_mem_list: list of HW block virtual mapped addresses.
* @cs_counters: context command submission counters.
* @cb_va_pool: device VA pool for command buffers which are mapped to the
* device's MMU.
* @sig_mgr: encaps signals handle manager.
+ * @cb_va_pool_base: the base address for the device VA pool
* @cs_sequence: sequence number for CS. Value is assigned to a CS and passed
* to user so user could inquire about CS. It is used as
* index to cs_pending array.
@@ -1795,13 +1941,14 @@ struct hl_ctx {
struct hl_cs_outcome_store outcome_store;
struct hl_va_range *va_range[HL_VA_RANGE_TYPE_MAX];
struct mutex mem_hash_lock;
- struct mutex mmu_lock;
struct mutex hw_block_list_lock;
+ struct mutex ts_reg_lock;
struct list_head debugfs_list;
struct list_head hw_block_mem_list;
struct hl_cs_counters_atomic cs_counters;
struct gen_pool *cb_va_pool;
struct hl_encaps_signals_mgr sig_mgr;
+ u64 cb_va_pool_base;
u64 cs_sequence;
u64 *dram_default_hops;
spinlock_t cs_lock;
@@ -1823,7 +1970,6 @@ struct hl_ctx_mgr {
};
-
/*
* COMMAND SUBMISSIONS
*/
@@ -1843,17 +1989,17 @@ struct hl_ctx_mgr {
* @dma_mapped: true if the SG was mapped to DMA addresses, false otherwise.
*/
struct hl_userptr {
- enum vm_type vm_type; /* must be first */
- struct list_head job_node;
- struct page **pages;
- unsigned int npages;
- struct sg_table *sgt;
- enum dma_data_direction dir;
- struct list_head debugfs_list;
- pid_t pid;
- u64 addr;
- u64 size;
- u8 dma_mapped;
+ enum vm_type vm_type; /* must be first */
+ struct list_head job_node;
+ struct page **pages;
+ unsigned int npages;
+ struct sg_table *sgt;
+ enum dma_data_direction dir;
+ struct list_head debugfs_list;
+ pid_t pid;
+ u64 addr;
+ u64 size;
+ u8 dma_mapped;
};
/**
@@ -1880,6 +2026,7 @@ struct hl_userptr {
* @type: CS_TYPE_*.
* @jobs_cnt: counter of submitted jobs on all queues.
* @encaps_sig_hdl_id: encaps signals handle id, set for the first staged cs.
+ * @completion_timestamp: timestamp of the last completed cs job.
* @sob_addr_offset: sob offset from the configuration base address.
* @initial_sob_count: count of completed signals in SOB before current submission of signal or
* cs with encaps signals.
@@ -1889,7 +2036,7 @@ struct hl_userptr {
* @tdr_active: true if TDR was activated for this CS (to prevent
* double TDR activation).
* @aborted: true if CS was aborted due to some device error.
- * @timestamp: true if a timestmap must be captured upon completion.
+ * @timestamp: true if a timestamp must be captured upon completion.
* @staged_last: true if this is the last staged CS and needs completion.
* @staged_first: true if this is the first staged CS and we need to receive
* timeout for this CS.
@@ -1912,6 +2059,7 @@ struct hl_cs {
struct list_head staged_cs_node;
struct list_head debugfs_list;
struct hl_cs_encaps_sig_handle *encaps_sig_hdl;
+ ktime_t completion_timestamp;
u64 sequence;
u64 staged_sequence;
u64 timeout_jiffies;
@@ -1947,6 +2095,7 @@ struct hl_cs {
* @debugfs_list: node in debugfs list of command submission jobs.
* @refcount: reference counter for usage of the CS job.
* @queue_type: the type of the H/W queue this job is submitted to.
+ * @timestamp: timestamp upon job completion
* @id: the id of this job inside a CS.
* @hw_queue_id: the id of the H/W queue this job is submitted to.
* @user_cb_size: the actual size of the CB we got from the user.
@@ -1973,6 +2122,7 @@ struct hl_cs_job {
struct list_head debugfs_list;
struct kref refcount;
enum hl_queue_type queue_type;
+ ktime_t timestamp;
u32 id;
u32 hw_queue_id;
u32 user_cb_size;
@@ -2033,12 +2183,16 @@ struct hl_cs_parser {
* hl_userptr).
* @node: node to hang on the hash table in context object.
* @vaddr: key virtual address.
+ * @handle: memory handle for device memory allocation.
* @ptr: value pointer (hl_vm_phys_pg_list or hl_userptr).
+ * @export_cnt: number of exports from within the VA block.
*/
struct hl_vm_hash_node {
struct hlist_node node;
u64 vaddr;
+ u64 handle;
void *ptr;
+ int export_cnt;
};
/**
@@ -2047,14 +2201,16 @@ struct hl_vm_hash_node {
* @node: node to hang on the list in context object.
* @ctx: the context this node belongs to.
* @vaddr: virtual address of the HW block.
- * @size: size of the block.
+ * @block_size: size of the block.
+ * @mapped_size: size of the block which is mapped. May change if partial un-mappings are done.
* @id: HW block id (handle).
*/
struct hl_vm_hw_block_list_node {
struct list_head node;
struct hl_ctx *ctx;
unsigned long vaddr;
- u32 size;
+ u32 block_size;
+ u32 mapped_size;
u32 id;
};
@@ -2067,7 +2223,6 @@ struct hl_vm_hw_block_list_node {
* @node: used to attach to deletion list that is used when all the allocations are cleared
* at the teardown of the context.
* @mapping_cnt: number of shared mappings.
- * @exporting_cnt: number of dma-buf exporting.
* @asid: the context related to this list.
* @page_size: size of each page in the pack.
* @flags: HL_MEM_* flags related to this list.
@@ -2083,7 +2238,6 @@ struct hl_vm_phys_pg_pack {
u64 total_size;
struct list_head node;
atomic_t mapping_cnt;
- u32 exporting_cnt;
u32 asid;
u32 page_size;
u32 flags;
@@ -2124,6 +2278,9 @@ struct hl_vm {
u8 init_done;
};
+#ifdef CONFIG_HL_HLDIO
+#include "hldio.h"
+#endif
/*
* DEBUG, PROFILING STRUCTURE
@@ -2166,7 +2323,7 @@ struct hl_notifier_event {
/**
* struct hl_fpriv - process information stored in FD private data.
* @hdev: habanalabs device structure.
- * @filp: pointer to the given file structure.
+ * @file_priv: pointer to the DRM file private data structure.
* @taskpid: current process ID.
* @ctx: current executing context. TODO: remove for multiple ctx per process
* @ctx_mgr: context manager to handle multiple context for this FD.
@@ -2181,7 +2338,7 @@ struct hl_notifier_event {
*/
struct hl_fpriv {
struct hl_device *hdev;
- struct file *filp;
+ struct drm_file *file_priv;
struct pid *taskpid;
struct hl_ctx *ctx;
struct hl_ctx_mgr ctx_mgr;
@@ -2194,7 +2351,6 @@ struct hl_fpriv {
struct mutex ctx_lock;
};
-
/*
* DebugFS
*/
@@ -2214,7 +2370,7 @@ struct hl_info_list {
/**
* struct hl_debugfs_entry - debugfs dentry wrapper.
- * @info_ent: dentry realted ops.
+ * @info_ent: dentry related ops.
* @dev_entry: ASIC specific debugfs manager.
*/
struct hl_debugfs_entry {
@@ -2222,6 +2378,7 @@ struct hl_debugfs_entry {
struct hl_dbg_device_entry *dev_entry;
};
+
/**
* struct hl_dbg_device_entry - ASIC specific debugfs manager.
* @root: root dentry.
@@ -2238,7 +2395,7 @@ struct hl_debugfs_entry {
* @userptr_list: list of available userptrs (virtual memory chunk descriptor).
* @userptr_spinlock: protects userptr_list.
* @ctx_mem_hash_list: list of available contexts with MMU mappings.
- * @ctx_mem_hash_spinlock: protects cb_list.
+ * @ctx_mem_hash_mutex: protects list of available contexts with MMU mappings.
* @data_dma_blob_desc: data DMA descriptor of blob.
* @mon_dump_blob_desc: monitor dump descriptor of blob.
* @state_dump: data of the system states in case of a bad cs.
@@ -2253,6 +2410,7 @@ struct hl_debugfs_entry {
* @i2c_addr: generic u8 debugfs file for address value to use in i2c_data_read.
* @i2c_reg: generic u8 debugfs file for register value to use in i2c_data_read.
* @i2c_len: generic u8 debugfs file for length value to use in i2c_data_read.
+ * @dio_stats: Direct I/O statistics
*/
struct hl_dbg_device_entry {
struct dentry *root;
@@ -2269,7 +2427,7 @@ struct hl_dbg_device_entry {
struct list_head userptr_list;
spinlock_t userptr_spinlock;
struct list_head ctx_mem_hash_list;
- spinlock_t ctx_mem_hash_spinlock;
+ struct mutex ctx_mem_hash_mutex;
struct debugfs_blob_wrapper data_dma_blob_desc;
struct debugfs_blob_wrapper mon_dump_blob_desc;
char *state_dump[HL_STATE_DUMP_HIST_LEN];
@@ -2284,6 +2442,35 @@ struct hl_dbg_device_entry {
u8 i2c_addr;
u8 i2c_reg;
u8 i2c_len;
+#ifdef CONFIG_HL_HLDIO
+ struct hl_dio_stats dio_stats;
+#endif
+};
+
+/**
+ * struct hl_debugfs_cfg_access_entry - single debugfs config access object, member of
+ * hl_debugfs_cfg_access.
+ * @seconds_since_epoch: seconds since January 1, 1970, used for time comparisons.
+ * @debugfs_type: the debugfs operation requested, can be READ32, WRITE32, READ64 or WRITE64.
+ * @addr: the requested address to access.
+ * @valid: if set, this entry has valid data for dumping at interrupt time.
+ */
+struct hl_debugfs_cfg_access_entry {
+ ktime_t seconds_since_epoch;
+ enum debugfs_access_type debugfs_type;
+ u64 addr;
+ bool valid;
+};
+
+/**
+ * struct hl_debugfs_cfg_access - saves debugfs config region access requests history.
+ * @cfg_access_list: list of objects describing config region access requests.
+ * @head: next valid index to add new entry to in cfg_access_list.
+ */
+struct hl_debugfs_cfg_access {
+ struct hl_debugfs_cfg_access_entry cfg_access_list[HL_DBGFS_CFG_ACCESS_HIST_LEN];
+ u32 head;
+ spinlock_t lock; /* protects head and entries */
};
/**
@@ -2422,7 +2609,7 @@ struct hl_state_dump_specs {
* DEVICES
*/
-#define HL_STR_MAX 32
+#define HL_STR_MAX 64
#define HL_DEV_STS_MAX (HL_DEVICE_STATUS_LAST + 1)
@@ -2453,13 +2640,9 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
#define WREG32_AND(reg, and) WREG32_P(reg, 0, and)
#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or))
-#define RMWREG32(reg, val, mask) \
- do { \
- u32 tmp_ = RREG32(reg); \
- tmp_ &= ~(mask); \
- tmp_ |= ((val) << __ffs(mask)); \
- WREG32(reg, tmp_); \
- } while (0)
+#define RMWREG32_SHIFTED(reg, val, mask) WREG32_P(reg, val, ~(mask))
+
+#define RMWREG32(reg, val, mask) RMWREG32_SHIFTED(reg, (val) << __ffs(mask), mask)
#define RREG32_MASK(reg, mask) ((RREG32(reg) & mask) >> __ffs(mask))
@@ -2478,12 +2661,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
ktime_t __timeout; \
u32 __elbi_read; \
int __rc = 0; \
- if (hdev->pdev) \
- __timeout = ktime_add_us(ktime_get(), timeout_us); \
- else \
- __timeout = ktime_add_us(ktime_get(),\
- min((u64)(timeout_us * 10), \
- (u64) HL_SIM_MAX_TIMEOUT_US)); \
+ __timeout = ktime_add_us(ktime_get(), timeout_us); \
might_sleep_if(sleep_us); \
for (;;) { \
if (elbi) { \
@@ -2492,7 +2670,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
break; \
(val) = __elbi_read; \
} else {\
- (val) = RREG32((u32)addr); \
+ (val) = RREG32(lower_32_bits(addr)); \
} \
if (cond) \
break; \
@@ -2503,7 +2681,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
break; \
(val) = __elbi_read; \
} else {\
- (val) = RREG32((u32)addr); \
+ (val) = RREG32(lower_32_bits(addr)); \
} \
break; \
} \
@@ -2535,13 +2713,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
u8 __arr_idx; \
int __rc = 0; \
\
- if (hdev->pdev) \
- __timeout = ktime_add_us(ktime_get(), timeout_us); \
- else \
- __timeout = ktime_add_us(ktime_get(),\
- min(((u64)timeout_us * 10), \
- (u64) HL_SIM_MAX_TIMEOUT_US)); \
- \
+ __timeout = ktime_add_us(ktime_get(), timeout_us); \
might_sleep_if(sleep_us); \
if (arr_size >= 64) \
__rc = -EINVAL; \
@@ -2558,7 +2730,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
if (__rc) \
break; \
} else { \
- __read_val = RREG32((u32)(addr_arr)[__arr_idx]); \
+ __read_val = RREG32(lower_32_bits(addr_arr[__arr_idx])); \
} \
if (__read_val == (expected_val)) \
__elem_bitmask &= ~BIT_ULL(__arr_idx); \
@@ -2594,17 +2766,18 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
* updated directly by the device. If false, the host memory being polled will
* be updated by host CPU. Required so host knows whether or not the memory
* might need to be byte-swapped before returning value to caller.
+ *
+ * On the first 4 polling iterations the macro goes to sleep for short period of
+ * time that gradually increases and reaches sleep_us on the fifth iteration.
*/
#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us, \
mem_written_by_device) \
({ \
+ u64 __sleep_step_us; \
ktime_t __timeout; \
- if (hdev->pdev) \
- __timeout = ktime_add_us(ktime_get(), timeout_us); \
- else \
- __timeout = ktime_add_us(ktime_get(),\
- min((u64)(timeout_us * 100), \
- (u64) HL_SIM_MAX_TIMEOUT_US)); \
+ u8 __step = 8; \
+ \
+ __timeout = ktime_add_us(ktime_get(), timeout_us); \
might_sleep_if(sleep_us); \
for (;;) { \
/* Verify we read updates done by other cores or by device */ \
@@ -2620,8 +2793,10 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
(val) = le32_to_cpu(*(__le32 *) &(val)); \
break; \
} \
- if (sleep_us) \
- usleep_range((sleep_us >> 2) + 1, sleep_us); \
+ __sleep_step_us = sleep_us >> __step; \
+ if (__sleep_step_us) \
+ usleep_range((__sleep_step_us >> 2) + 1, __sleep_step_us); \
+ __step >>= 1; \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
@@ -2634,29 +2809,29 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
p->size = sz; \
})
-#define HL_USR_INTR_STRUCT_INIT(usr_intr, hdev, intr_id, decoder) \
+#define HL_USR_INTR_STRUCT_INIT(usr_intr, hdev, intr_id, intr_type) \
({ \
usr_intr.hdev = hdev; \
usr_intr.interrupt_id = intr_id; \
- usr_intr.is_decoder = decoder; \
+ usr_intr.type = intr_type; \
INIT_LIST_HEAD(&usr_intr.wait_list_head); \
spin_lock_init(&usr_intr.wait_list_lock); \
+ INIT_LIST_HEAD(&usr_intr.ts_list_head); \
+ spin_lock_init(&usr_intr.ts_list_lock); \
})
struct hwmon_chip_info;
/**
- * struct hl_device_reset_work - reset workqueue task wrapper.
- * @wq: work queue for device reset procedure.
+ * struct hl_device_reset_work - reset work wrapper.
* @reset_work: reset work to be done.
* @hdev: habanalabs device structure.
* @flags: reset flags.
*/
struct hl_device_reset_work {
- struct workqueue_struct *wq;
- struct delayed_work reset_work;
- struct hl_device *hdev;
- u32 flags;
+ struct delayed_work reset_work;
+ struct hl_device *hdev;
+ u32 flags;
};
/**
@@ -2775,7 +2950,7 @@ struct hl_mmu_funcs {
/**
* struct hl_prefetch_work - prefetch work structure handler
- * @pf_work: actual work struct.
+ * @prefetch_work: actual work struct.
* @ctx: compute context.
* @va: virtual address to pre-fetch.
* @size: pre-fetch size.
@@ -2783,7 +2958,7 @@ struct hl_mmu_funcs {
* @asid: ASID for maintenance operation.
*/
struct hl_prefetch_work {
- struct work_struct pf_work;
+ struct work_struct prefetch_work;
struct hl_ctx *ctx;
u64 va;
u64 size;
@@ -2889,43 +3064,19 @@ struct cs_timeout_info {
u64 seq;
};
-/**
- * struct razwi_info - info about last razwi error occurred.
- * @timestamp: razwi timestamp.
- * @write_enable: if set writing to razwi parameters in the structure is enabled.
- * otherwise - disabled, so the first (root cause) razwi will not be overwritten.
- * @addr: address that caused razwi.
- * @engine_id_1: engine id of the razwi initiator, if it was initiated by engine that does
- * not have engine id it will be set to U16_MAX.
- * @engine_id_2: second engine id of razwi initiator. Might happen that razwi have 2 possible
- * engines which one them caused the razwi. In that case, it will contain the
- * second possible engine id, otherwise it will be set to U16_MAX.
- * @non_engine_initiator: in case the initiator of the razwi does not have engine id.
- * @type: cause of razwi, page fault or access error, otherwise it will be set to U8_MAX.
- */
-struct razwi_info {
- ktime_t timestamp;
- atomic_t write_enable;
- u64 addr;
- u16 engine_id_1;
- u16 engine_id_2;
- u8 non_engine_initiator;
- u8 type;
-};
-
#define MAX_QMAN_STREAMS_INFO 4
#define OPCODE_INFO_MAX_ADDR_SIZE 8
/**
* struct undefined_opcode_info - info about last undefined opcode error
* @timestamp: timestamp of the undefined opcode error
* @cb_addr_streams: CB addresses (per stream) that are currently exists in the PQ
- * entiers. In case all streams array entries are
+ * entries. In case all streams array entries are
* filled with values, it means the execution was in Lower-CP.
* @cq_addr: the address of the current handled command buffer
* @cq_size: the size of the current handled command buffer
* @cb_addr_streams_len: num of streams - actual len of cb_addr_streams array.
- * should be equal to 1 incase of undefined opcode
- * in Upper-CP (specific stream) and equal to 4 incase
+ * should be equal to 1 in case of undefined opcode
+ * in Upper-CP (specific stream) and equal to 4 in case
* of undefined opcode in Lower-CP.
* @engine_id: engine-id that the error occurred on
* @stream_id: the stream id the error occurred on. In case the stream equals to
@@ -2946,21 +3097,113 @@ struct undefined_opcode_info {
};
/**
- * struct last_error_session_info - info about last session errors occurred.
- * @cs_timeout: CS timeout error last information.
- * @razwi: razwi last information.
- * @undef_opcode: undefined opcode information
+ * struct page_fault_info - page fault information.
+ * @page_fault: holds information collected during a page fault.
+ * @user_mappings: buffer containing user mappings.
+ * @num_of_user_mappings: number of user mappings.
+ * @page_fault_detected: if set as 1, then a page-fault was discovered for the
+ * first time after the driver has finished booting-up.
+ * Since we're looking for the page-fault's root cause,
+ * we don't care of the others that might follow it-
+ * so once changed to 1, it will remain that way.
+ * @page_fault_info_available: indicates that a page fault info is now available.
+ */
+struct page_fault_info {
+ struct hl_page_fault_info page_fault;
+ struct hl_user_mapping *user_mappings;
+ u64 num_of_user_mappings;
+ atomic_t page_fault_detected;
+ bool page_fault_info_available;
+};
+
+/**
+ * struct razwi_info - RAZWI information.
+ * @razwi: holds information collected during a RAZWI
+ * @razwi_detected: if set as 1, then a RAZWI was discovered for the
+ * first time after the driver has finished booting-up.
+ * Since we're looking for the RAZWI's root cause,
+ * we don't care of the others that might follow it-
+ * so once changed to 1, it will remain that way.
+ * @razwi_info_available: indicates that a RAZWI info is now available.
+ */
+struct razwi_info {
+ struct hl_info_razwi_event razwi;
+ atomic_t razwi_detected;
+ bool razwi_info_available;
+};
+
+/**
+ * struct hw_err_info - HW error information.
+ * @event: holds information on the event.
+ * @event_detected: if set as 1, then a HW event was discovered for the
+ * first time after the driver has finished booting-up.
+ * currently we assume that only fatal events (that require hard-reset) are
+ * reported so we don't care of the others that might follow it.
+ * so once changed to 1, it will remain that way.
+ * TODO: support multiple events.
+ * @event_info_available: indicates that a HW event info is now available.
+ */
+struct hw_err_info {
+ struct hl_info_hw_err_event event;
+ atomic_t event_detected;
+ bool event_info_available;
+};
+
+/**
+ * struct fw_err_info - FW error information.
+ * @event: holds information on the event.
+ * @event_detected: if set as 1, then a FW event was discovered for the
+ * first time after the driver has finished booting-up.
+ * currently we assume that only fatal events (that require hard-reset) are
+ * reported so we don't care of the others that might follow it.
+ * so once changed to 1, it will remain that way.
+ * TODO: support multiple events.
+ * @event_info_available: indicates that a HW event info is now available.
+ */
+struct fw_err_info {
+ struct hl_info_fw_err_event event;
+ atomic_t event_detected;
+ bool event_info_available;
+};
+
+/**
+ * struct engine_err_info - engine error information.
+ * @event: holds information on the event.
+ * @event_detected: if set as 1, then an engine event was discovered for the
+ * first time after the driver has finished booting-up.
+ * @event_info_available: indicates that an engine event info is now available.
*/
-struct last_error_session_info {
+struct engine_err_info {
+ struct hl_info_engine_err_event event;
+ atomic_t event_detected;
+ bool event_info_available;
+};
+
+
+/**
+ * struct hl_error_info - holds information collected during an error.
+ * @cs_timeout: CS timeout error information.
+ * @razwi_info: RAZWI information.
+ * @undef_opcode: undefined opcode information.
+ * @page_fault_info: page fault information.
+ * @hw_err: (fatal) hardware error information.
+ * @fw_err: firmware error information.
+ * @engine_err: engine error information.
+ */
+struct hl_error_info {
struct cs_timeout_info cs_timeout;
- struct razwi_info razwi;
+ struct razwi_info razwi_info;
struct undefined_opcode_info undef_opcode;
+ struct page_fault_info page_fault_info;
+ struct hw_err_info hw_err;
+ struct fw_err_info fw_err;
+ struct engine_err_info engine_err;
};
/**
* struct hl_reset_info - holds current device reset information.
* @lock: lock to protect critical reset flows.
- * @compute_reset_cnt: number of compte resets since the driver was loaded.
+ * @compute_reset_cnt: number of compute resets since the driver was loaded.
* @hard_reset_cnt: number of hard resets since the driver was loaded.
* @hard_reset_schedule_flags: hard reset is scheduled to after current compute reset,
* here we hold the hard reset flags.
@@ -2971,12 +3214,13 @@ struct last_error_session_info {
* @hard_reset_pending: is there a hard reset work pending.
* @curr_reset_cause: saves an enumerated reset cause when a hard reset is
* triggered, and cleared after it is shared with preboot.
- * @prev_reset_trigger: saves the previous trigger which caused a reset, overidden
+ * @prev_reset_trigger: saves the previous trigger which caused a reset, overridden
* with a new value on next reset
* @reset_trigger_repeated: set if device reset is triggered more than once with
* same cause.
* @skip_reset_on_timeout: Skip device reset if CS has timed out, wait for it to
* complete instead.
+ * @watchdog_active: true if a device release watchdog work is scheduled.
*/
struct hl_reset_info {
spinlock_t lock;
@@ -2987,12 +3231,26 @@ struct hl_reset_info {
u8 in_compute_reset;
u8 needs_reset;
u8 hard_reset_pending;
-
u8 curr_reset_cause;
u8 prev_reset_trigger;
u8 reset_trigger_repeated;
-
u8 skip_reset_on_timeout;
+ u8 watchdog_active;
+};
+
+/**
+ * struct eq_heartbeat_debug_info - stores debug info to be used upon heartbeat failure.
+ * @last_pq_heartbeat_ts: timestamp of the last test packet that was sent to FW.
+ * This packet is the trigger in FW to send the EQ heartbeat event.
+ * @last_eq_heartbeat_ts: timestamp of the last EQ heartbeat event that was received from FW.
+ * @heartbeat_event_counter: number of heartbeat events received.
+ * @cpu_queue_id: used to read the queue pi/ci
+ */
+struct eq_heartbeat_debug_info {
+ time64_t last_pq_heartbeat_ts;
+ time64_t last_eq_heartbeat_ts;
+ u32 heartbeat_event_counter;
+ u32 cpu_queue_id;
};
/**
@@ -3002,18 +3260,22 @@ struct hl_reset_info {
* (required only for PCI address match mode)
* @pcie_bar: array of available PCIe bars virtual addresses.
* @rmmio: configuration area address on SRAM.
- * @cdev: related char device.
+ * @drm: related DRM device.
* @cdev_ctrl: char device for control operations only (INFO IOCTL)
* @dev: related kernel basic device structure.
* @dev_ctrl: related kernel device structure for the control device
* @work_heartbeat: delayed work for CPU-CP is-alive check.
* @device_reset_work: delayed work which performs hard reset
+ * @device_release_watchdog_work: watchdog work that performs hard reset if user doesn't release
+ * device upon certain error cases.
* @asic_name: ASIC specific name.
* @asic_type: ASIC specific type.
* @completion_queue: array of hl_cq.
* @user_interrupt: array of hl_user_interrupt. upon the corresponding user
* interrupt, driver will monitor the list of fences
* registered to this interrupt.
+ * @tpc_interrupt: single TPC interrupt for all TPCs.
+ * @unexpected_error_interrupt: single interrupt for unexpected user error indication.
* @common_user_cq_interrupt: common user CQ interrupt for all user CQ interrupts.
* upon any user CQ interrupt, driver will monitor the
* list of fences registered to this common structure.
@@ -3026,7 +3288,8 @@ struct hl_reset_info {
* @cs_cmplt_wq: work queue of CS completions for executing work in process
* context.
* @ts_free_obj_wq: work queue for timestamp registration objects release.
- * @pf_wq: work queue for MMU pre-fetch operations.
+ * @prefetch_wq: work queue for MMU pre-fetch operations.
+ * @reset_wq: work queue for device reset procedure.
* @kernel_ctx: Kernel driver context structure.
* @kernel_queues: array of hl_hw_queue.
* @cs_mirror_list: CS mirror list for TDR.
@@ -3041,6 +3304,12 @@ struct hl_reset_info {
* @asid_mutex: protects asid_bitmap.
* @send_cpu_message_lock: enforces only one message in Host <-> CPU-CP queue.
* @debug_lock: protects critical section of setting debug mode for device
+ * @mmu_lock: protects the MMU page tables and invalidation h/w. Although the
+ * page tables are per context, the invalidation h/w is per MMU.
+ * Therefore, we can't allow multiple contexts (we only have two,
+ * user and kernel) to access the invalidation h/w at the same time.
+ * In addition, any change to the PGT, modifying the MMU hash or
+ * walking the PGT requires talking this lock.
* @asic_prop: ASIC specific immutable properties.
* @asic_funcs: ASIC specific functions.
* @asic_specific: ASIC specific information to use only from ASIC files.
@@ -3049,7 +3318,8 @@ struct hl_reset_info {
* @hl_chip_info: ASIC's sensors information.
* @device_status_description: device status description.
* @hl_debugfs: device's debugfs manager.
- * @cb_pool: list of preallocated CBs.
+ * @debugfs_cfg_accesses: list of last debugfs config region accesses.
+ * @cb_pool: list of pre allocated CBs.
* @cb_pool_lock: protects the CB pool.
* @internal_cb_pool_virt_addr: internal command buffer pool virtual address.
* @internal_cb_pool_dma_addr: internal command buffer pool dma address.
@@ -3070,11 +3340,17 @@ struct hl_reset_info {
* @state_dump_specs: constants and dictionaries needed to dump system state.
* @multi_cs_completion: array of multi-CS completion.
* @clk_throttling: holds information about current/previous clock throttling events
- * @last_error: holds information about last session in which CS timeout or razwi error occurred.
+ * @captured_err_info: holds information about errors.
* @reset_info: holds current device reset information.
+ * @heartbeat_debug_info: counters used to debug heartbeat failures.
+ * @hldio: describes habanalabs direct storage interaction interface.
+ * @irq_affinity_mask: mask of available CPU cores for user and decoder interrupt handling.
* @stream_master_qid_arr: pointer to array with QIDs of master streams.
- * @fw_major_version: major version of current loaded preboot.
- * @fw_minor_version: minor version of current loaded preboot.
+ * @fw_inner_major_ver: the major of current loaded preboot inner version.
+ * @fw_inner_minor_ver: the minor of current loaded preboot inner version.
+ * @fw_sw_major_ver: the major of current loaded preboot SW version.
+ * @fw_sw_minor_ver: the minor of current loaded preboot SW version.
+ * @fw_sw_sub_minor_ver: the sub-minor of current loaded preboot SW version.
* @dram_used_mem: current DRAM memory consumption.
* @memory_scrub_val: the value to which the dram will be scrubbed to using cb scrub_device_dram
* @timeout_jiffies: device CS timeout value.
@@ -3102,6 +3378,7 @@ struct hl_reset_info {
* drams are binned-out
* @tpc_binning: contains mask of tpc engines that is received from the f/w which indicates which
* tpc engines are binned-out
+ * @dmabuf_export_cnt: number of dma-buf exporting.
* @card_type: Various ASICs have several card types. This indicates the card
* type of the current device.
* @major: habanalabs kernel driver major.
@@ -3110,12 +3387,16 @@ struct hl_reset_info {
* indicates which decoder engines are binned-out
* @edma_binning: contains mask of edma engines that is received from the f/w which
* indicates which edma engines are binned-out
+ * @device_release_watchdog_timeout_sec: device release watchdog timeout value in seconds.
+ * @rotator_binning: contains mask of rotators engines that is received from the f/w
+ * which indicates which rotator engines are binned-out(Gaudi3 and above).
* @id: device minor.
- * @id_control: minor of the control device
+ * @cdev_idx: char device index.
* @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit
* addresses.
* @is_in_dram_scrub: true if dram scrub operation is on going.
* @disabled: is device disabled.
+ * @cpld_shutdown: is cpld shutdown.
* @late_init_done: is late init stage was done during initialization.
* @hwmon_initialized: is H/W monitor sensors was initialized.
* @reset_on_lockup: true if a reset should be done in case of stuck CS, false
@@ -3130,7 +3411,7 @@ struct hl_reset_info {
* @in_debug: whether the device is in a state where the profiling/tracing infrastructure
* can be used. This indication is needed because in some ASICs we need to do
* specific operations to enable that infrastructure.
- * @cdev_sysfs_created: were char devices and sysfs nodes created.
+ * @cdev_sysfs_debugfs_created: were char devices and sysfs/debugfs files created.
* @stop_on_err: true if engines should stop on error.
* @supports_sync_stream: is sync stream supported.
* @sync_stream_queue_idx: helper index for sync stream queues initialization.
@@ -3152,10 +3433,13 @@ struct hl_reset_info {
* @supports_mmu_prefetch: true if prefetch is supported, otherwise false.
* @reset_upon_device_release: reset the device when the user closes the file descriptor of the
* device.
+ * @supports_ctx_switch: true if a ctx switch is required upon first submission.
+ * @support_preboot_binning: true if we support read binning info from preboot.
+ * @eq_heartbeat_received: indication that eq heartbeat event has received from FW.
* @nic_ports_mask: Controls which NIC ports are enabled. Used only for testing.
* @fw_components: Controls which f/w components to load to the device. There are multiple f/w
* stages and sometimes we want to stop at a certain stage. Used only for testing.
- * @mmu_enable: Whether to enable or disable the device MMU(s). Used only for testing.
+ * @mmu_disable: Disable the device MMU(s). Used only for testing.
* @cpu_queues_enable: Whether to enable queues communication vs. the f/w. Used only for testing.
* @pldm: Whether we are running in Palladium environment. Used only for testing.
* @hard_reset_on_fw_events: Whether to do device hard-reset when a fatal event is received from
@@ -3171,17 +3455,20 @@ struct hl_device {
u64 pcie_bar_phys[HL_PCI_NUM_BARS];
void __iomem *pcie_bar[HL_PCI_NUM_BARS];
void __iomem *rmmio;
- struct cdev cdev;
+ struct drm_device drm;
struct cdev cdev_ctrl;
struct device *dev;
struct device *dev_ctrl;
struct delayed_work work_heartbeat;
struct hl_device_reset_work device_reset_work;
+ struct hl_device_reset_work device_release_watchdog_work;
char asic_name[HL_STR_MAX];
char status[HL_DEV_STS_MAX][HL_STR_MAX];
enum hl_asic_type asic_type;
struct hl_cq *completion_queue;
struct hl_user_interrupt *user_interrupt;
+ struct hl_user_interrupt tpc_interrupt;
+ struct hl_user_interrupt unexpected_error_interrupt;
struct hl_user_interrupt common_user_cq_interrupt;
struct hl_user_interrupt common_decoder_interrupt;
struct hl_cs **shadow_cs_queue;
@@ -3189,7 +3476,8 @@ struct hl_device {
struct workqueue_struct *eq_wq;
struct workqueue_struct *cs_cmplt_wq;
struct workqueue_struct *ts_free_obj_wq;
- struct workqueue_struct *pf_wq;
+ struct workqueue_struct *prefetch_wq;
+ struct workqueue_struct *reset_wq;
struct hl_ctx *kernel_ctx;
struct hl_hw_queue *kernel_queues;
struct list_head cs_mirror_list;
@@ -3204,6 +3492,7 @@ struct hl_device {
struct mutex asid_mutex;
struct mutex send_cpu_message_lock;
struct mutex debug_lock;
+ struct mutex mmu_lock;
struct asic_fixed_properties asic_prop;
const struct hl_asic_funcs *asic_funcs;
void *asic_specific;
@@ -3212,6 +3501,7 @@ struct hl_device {
struct hwmon_chip_info *hl_chip_info;
struct hl_dbg_device_entry hl_debugfs;
+ struct hl_debugfs_cfg_access debugfs_cfg_accesses;
struct list_head cb_pool;
spinlock_t cb_pool_lock;
@@ -3242,13 +3532,22 @@ struct hl_device {
struct multi_cs_completion multi_cs_completion[
MULTI_CS_MAX_USER_CTX];
struct hl_clk_throttle clk_throttling;
- struct last_error_session_info last_error;
+ struct hl_error_info captured_err_info;
struct hl_reset_info reset_info;
+ struct eq_heartbeat_debug_info heartbeat_debug_info;
+#ifdef CONFIG_HL_HLDIO
+ struct hl_dio hldio;
+#endif
+ cpumask_t irq_affinity_mask;
+
u32 *stream_master_qid_arr;
- u32 fw_major_version;
- u32 fw_minor_version;
+ u32 fw_inner_major_ver;
+ u32 fw_inner_minor_ver;
+ u32 fw_sw_major_ver;
+ u32 fw_sw_minor_ver;
+ u32 fw_sw_sub_minor_ver;
atomic64_t dram_used_mem;
u64 memory_scrub_val;
u64 timeout_jiffies;
@@ -3263,17 +3562,20 @@ struct hl_device {
u64 fw_comms_poll_interval_usec;
u64 dram_binning;
u64 tpc_binning;
-
+ atomic_t dmabuf_export_cnt;
enum cpucp_card_types card_type;
u32 major;
u32 high_pll;
u32 decoder_binning;
u32 edma_binning;
+ u32 device_release_watchdog_timeout_sec;
+ u32 rotator_binning;
u16 id;
- u16 id_control;
+ u16 cdev_idx;
u16 cpu_pci_msb_addr;
u8 is_in_dram_scrub;
u8 disabled;
+ u8 cpld_shutdown;
u8 late_init_done;
u8 hwmon_initialized;
u8 reset_on_lockup;
@@ -3283,7 +3585,7 @@ struct hl_device {
u8 init_done;
u8 device_cpu_disabled;
u8 in_debug;
- u8 cdev_sysfs_created;
+ u8 cdev_sysfs_debugfs_created;
u8 stop_on_err;
u8 supports_sync_stream;
u8 sync_stream_queue_idx;
@@ -3300,11 +3602,14 @@ struct hl_device {
u8 compute_ctx_in_release;
u8 supports_mmu_prefetch;
u8 reset_upon_device_release;
+ u8 supports_ctx_switch;
+ u8 support_preboot_binning;
+ u8 eq_heartbeat_received;
- /* Parameters for bring-up */
+ /* Parameters for bring-up to be upstreamed */
u64 nic_ports_mask;
u64 fw_components;
- u8 mmu_enable;
+ u8 mmu_disable;
u8 cpu_queues_enable;
u8 pldm;
u8 hard_reset_on_fw_events;
@@ -3313,6 +3618,9 @@ struct hl_device {
u8 heartbeat;
};
+/* Retrieve PCI device name in case of a PCI device or dev name in simulator */
+#define HL_DEV_NAME(hdev) \
+ ((hdev)->pdev ? dev_name(&(hdev)->pdev->dev) : "NA-DEVICE")
/**
* struct hl_cs_encaps_sig_handle - encapsulated signals handle structure
@@ -3339,6 +3647,20 @@ struct hl_cs_encaps_sig_handle {
u32 count;
};
+/**
+ * struct hl_info_fw_err_info - firmware error information structure
+ * @err_type: The type of error detected (or reported).
+ * @event_mask: Pointer to the event mask to be modified with the detected error flag
+ * (can be NULL)
+ * @event_id: The id of the event that reported the error
+ * (applicable when err_type is HL_INFO_FW_REPORTED_ERR).
+ */
+struct hl_info_fw_err_info {
+ enum hl_info_fw_err_type err_type;
+ u64 *event_mask;
+ u16 event_id;
+};
+
/*
* IOCTLs
*/
@@ -3363,7 +3685,6 @@ struct hl_ioctl_desc {
hl_ioctl_t *func;
};
-
/*
* Kernel module functions that can be accessed by entire module
*/
@@ -3407,6 +3728,11 @@ static inline bool hl_mem_area_inside_range(u64 address, u64 size,
return false;
}
+static inline struct hl_device *to_hl_device(struct drm_device *ddev)
+{
+ return container_of(ddev, struct hl_device, drm);
+}
+
/**
* hl_mem_area_crosses_range() - Checks whether address+size crossing a range.
* @address: The start address of the area we want to validate.
@@ -3426,26 +3752,41 @@ static inline bool hl_mem_area_crosses_range(u64 address, u32 size,
}
uint64_t hl_set_dram_bar_default(struct hl_device *hdev, u64 addr);
-void *hl_asic_dma_alloc_coherent(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
- gfp_t flag);
-void hl_asic_dma_free_coherent(struct hl_device *hdev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle);
void *hl_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle);
void hl_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr);
-void *hl_asic_dma_pool_zalloc(struct hl_device *hdev, size_t size, gfp_t mem_flags,
- dma_addr_t *dma_handle);
-void hl_asic_dma_pool_free(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr);
-int hl_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir);
-void hl_dma_unmap_sgtable(struct hl_device *hdev, struct sg_table *sgt,
+void *hl_asic_dma_alloc_coherent_caller(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
+ gfp_t flag, const char *caller);
+void hl_asic_dma_free_coherent_caller(struct hl_device *hdev, size_t size, void *cpu_addr,
+ dma_addr_t dma_handle, const char *caller);
+void *hl_asic_dma_pool_zalloc_caller(struct hl_device *hdev, size_t size, gfp_t mem_flags,
+ dma_addr_t *dma_handle, const char *caller);
+void hl_asic_dma_pool_free_caller(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr,
+ const char *caller);
+int hl_dma_map_sgtable_caller(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir, const char *caller);
+void hl_dma_unmap_sgtable_caller(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir, const char *caller);
+int hl_asic_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt,
enum dma_data_direction dir);
+void hl_asic_dma_unmap_sgtable(struct hl_device *hdev, struct sg_table *sgt,
+ enum dma_data_direction dir);
+int hl_access_sram_dram_region(struct hl_device *hdev, u64 addr, u64 *val,
+ enum debugfs_access_type acc_type, enum pci_region region_type, bool set_dram_bar);
int hl_access_cfg_region(struct hl_device *hdev, u64 addr, u64 *val,
enum debugfs_access_type acc_type);
int hl_access_dev_mem(struct hl_device *hdev, enum pci_region region_type,
u64 addr, u64 *val, enum debugfs_access_type acc_type);
-int hl_device_open(struct inode *inode, struct file *filp);
+
+int hl_mmap(struct file *filp, struct vm_area_struct *vma);
+
+int hl_device_open(struct drm_device *drm, struct drm_file *file_priv);
+void hl_device_release(struct drm_device *ddev, struct drm_file *file_priv);
+
int hl_device_open_ctrl(struct inode *inode, struct file *filp);
bool hl_device_operational(struct hl_device *hdev,
enum hl_device_status *status);
+bool hl_ctrl_device_operational(struct hl_device *hdev,
+ enum hl_device_status *status);
enum hl_device_status hl_device_status(struct hl_device *hdev);
int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool enable);
int hl_hw_queues_create(struct hl_device *hdev);
@@ -3469,11 +3810,13 @@ int hl_eq_init(struct hl_device *hdev, struct hl_eq *q);
void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q);
void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q);
void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q);
+void hl_eq_dump(struct hl_device *hdev, struct hl_eq *q);
irqreturn_t hl_irq_handler_cq(int irq, void *arg);
irqreturn_t hl_irq_handler_eq(int irq, void *arg);
irqreturn_t hl_irq_handler_dec_abnrm(int irq, void *arg);
-irqreturn_t hl_irq_handler_user_interrupt(int irq, void *arg);
-irqreturn_t hl_irq_handler_default(int irq, void *arg);
+irqreturn_t hl_irq_user_interrupt_handler(int irq, void *arg);
+irqreturn_t hl_irq_user_interrupt_thread_handler(int irq, void *arg);
+irqreturn_t hl_irq_eq_error_interrupt_thread_handler(int irq, void *arg);
u32 hl_cq_inc_ptr(u32 ptr);
int hl_asid_init(struct hl_device *hdev);
@@ -3494,11 +3837,12 @@ int hl_ctx_get_fences(struct hl_ctx *ctx, u64 *seq_arr,
void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr);
void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr);
-int hl_device_init(struct hl_device *hdev, struct class *hclass);
+int hl_device_init(struct hl_device *hdev);
void hl_device_fini(struct hl_device *hdev);
int hl_device_suspend(struct hl_device *hdev);
int hl_device_resume(struct hl_device *hdev);
int hl_device_reset(struct hl_device *hdev, u32 flags);
+int hl_device_cond_reset(struct hl_device *hdev, u32 flags, u64 event_mask);
void hl_hpriv_get(struct hl_fpriv *hpriv);
int hl_hpriv_put(struct hl_fpriv *hpriv);
int hl_device_utilization(struct hl_device *hdev, u32 *utilization);
@@ -3513,6 +3857,7 @@ void hl_sysfs_fini(struct hl_device *hdev);
int hl_hwmon_init(struct hl_device *hdev);
void hl_hwmon_fini(struct hl_device *hdev);
+void hl_hwmon_release_resources(struct hl_device *hdev);
int hl_cb_create(struct hl_device *hdev, struct hl_mem_mgr *mmg,
struct hl_ctx *ctx, u32 cb_size, bool internal_cb,
@@ -3542,6 +3887,7 @@ bool cs_needs_timeout(struct hl_cs *cs);
bool is_staged_cs_last_exists(struct hl_device *hdev, struct hl_cs *cs);
struct hl_cs *hl_staged_cs_find_first(struct hl_device *hdev, u64 cs_seq);
void hl_multi_cs_completion_init(struct hl_device *hdev);
+u32 hl_get_active_cs_num(struct hl_device *hdev);
void goya_set_asic_funcs(struct hl_device *hdev);
void gaudi_set_asic_funcs(struct hl_device *hdev);
@@ -3557,7 +3903,7 @@ void hl_hw_block_mem_init(struct hl_ctx *ctx);
void hl_hw_block_mem_fini(struct hl_ctx *ctx);
u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
- enum hl_va_range_type type, u32 size, u32 alignment);
+ enum hl_va_range_type type, u64 size, u32 alignment);
int hl_unreserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
u64 start_addr, u64 size);
int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
@@ -3617,10 +3963,9 @@ struct pgt_info *hl_mmu_hr_get_alloc_next_hop(struct hl_ctx *ctx,
u64 curr_pte, bool *is_new_hop);
int hl_mmu_hr_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops,
struct hl_hr_mmu_funcs *hr_func);
-void hl_mmu_swap_out(struct hl_ctx *ctx);
-void hl_mmu_swap_in(struct hl_ctx *ctx);
int hl_mmu_if_set_funcs(struct hl_device *hdev);
void hl_mmu_v1_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu);
+void hl_mmu_v2_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu);
void hl_mmu_v2_hr_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu);
int hl_mmu_va_to_pa(struct hl_ctx *ctx, u64 virt_addr, u64 *phys_addr);
int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
@@ -3628,7 +3973,24 @@ int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
u64 hl_mmu_scramble_addr(struct hl_device *hdev, u64 addr);
u64 hl_mmu_descramble_addr(struct hl_device *hdev, u64 addr);
bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr);
-
+struct pgt_info *hl_mmu_dr_get_pgt_info(struct hl_ctx *ctx, u64 hop_addr);
+void hl_mmu_dr_free_hop(struct hl_ctx *ctx, u64 hop_addr);
+void hl_mmu_dr_free_pgt_node(struct hl_ctx *ctx, struct pgt_info *pgt_info);
+u64 hl_mmu_dr_get_phys_hop0_addr(struct hl_ctx *ctx);
+u64 hl_mmu_dr_get_hop0_addr(struct hl_ctx *ctx);
+void hl_mmu_dr_write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val);
+void hl_mmu_dr_write_final_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val);
+void hl_mmu_dr_clear_pte(struct hl_ctx *ctx, u64 pte_addr);
+u64 hl_mmu_dr_get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr);
+void hl_mmu_dr_get_pte(struct hl_ctx *ctx, u64 hop_addr);
+int hl_mmu_dr_put_pte(struct hl_ctx *ctx, u64 hop_addr);
+u64 hl_mmu_dr_get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte, bool *is_new_hop);
+u64 hl_mmu_dr_alloc_hop(struct hl_ctx *ctx);
+void hl_mmu_dr_flush(struct hl_ctx *ctx);
+int hl_mmu_dr_init(struct hl_device *hdev);
+void hl_mmu_dr_fini(struct hl_device *hdev);
+
+int hl_fw_version_cmp(struct hl_device *hdev, u32 major, u32 minor, u32 subminor);
int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name,
void __iomem *dst, u32 src_offset, u32 size);
int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode, u64 value);
@@ -3665,6 +4027,7 @@ int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power);
void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev);
void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev);
int hl_fw_init_cpu(struct hl_device *hdev);
+int hl_fw_wait_preboot_ready(struct hl_device *hdev);
int hl_fw_read_preboot_status(struct hl_device *hdev);
int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev,
struct fw_load_mgr *fw_loader,
@@ -3674,6 +4037,8 @@ int hl_fw_dram_replaced_row_get(struct hl_device *hdev,
struct cpucp_hbm_row_info *info);
int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num);
int hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid);
+int hl_fw_send_device_activity(struct hl_device *hdev, bool open);
+int hl_fw_send_soft_reset(struct hl_device *hdev);
int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3],
bool is_wc[3]);
int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data);
@@ -3697,6 +4062,10 @@ int hl_get_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long *va
void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long value);
long hl_fw_get_max_power(struct hl_device *hdev);
void hl_fw_set_max_power(struct hl_device *hdev);
+int hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info,
+ u32 nonce);
+int hl_fw_get_dev_info_signed(struct hl_device *hdev,
+ struct cpucp_dev_info_signed *dev_info_signed, u32 nonce);
int hl_set_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long value);
int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value);
int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value);
@@ -3705,10 +4074,13 @@ int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk);
void hl_fw_set_pll_profile(struct hl_device *hdev);
void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp);
void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp);
+int hl_fw_send_generic_request(struct hl_device *hdev, enum hl_passthrough_type sub_opcode,
+ dma_addr_t buff, u32 *size);
void hw_sob_get(struct hl_hw_sob *hw_sob);
void hw_sob_put(struct hl_hw_sob *hw_sob);
-void hl_encaps_handle_do_release(struct kref *ref);
+void hl_encaps_release_handle_and_put_ctx(struct kref *ref);
+void hl_encaps_release_handle_and_put_sob_ctx(struct kref *ref);
void hl_hw_queue_encaps_sig_set_sob_info(struct hl_device *hdev,
struct hl_cs *cs, struct hl_cs_job *job,
struct hl_cs_compl *cs_cmpl);
@@ -3718,6 +4090,7 @@ void hl_dec_fini(struct hl_device *hdev);
void hl_dec_ctx_fini(struct hl_ctx *ctx);
void hl_release_pending_user_interrupts(struct hl_device *hdev);
+void hl_abort_waiting_for_cs_completions(struct hl_device *hdev);
int hl_cs_signal_sob_wraparound_handler(struct hl_device *hdev, u32 q_idx,
struct hl_hw_sob **hw_sob, u32 count, bool encaps_sig);
@@ -3732,7 +4105,8 @@ char *hl_format_as_binary(char *buf, size_t buf_len, u32 n);
const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type);
void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg);
-void hl_mem_mgr_fini(struct hl_mem_mgr *mmg);
+void hl_mem_mgr_fini(struct hl_mem_mgr *mmg, struct hl_mem_mgr_fini_stats *stats);
+void hl_mem_mgr_idr_destroy(struct hl_mem_mgr *mmg);
int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
void *args);
struct hl_mmap_mem_buf *hl_mmap_mem_buf_get(struct hl_mem_mgr *mmg,
@@ -3743,13 +4117,29 @@ struct hl_mmap_mem_buf *
hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp,
void *args);
+__printf(2, 3) void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...);
+void hl_capture_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
+ u8 flags);
+void hl_handle_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
+ u8 flags, u64 *event_mask);
+void hl_capture_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu);
+void hl_handle_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu,
+ u64 *event_mask);
+void hl_handle_critical_hw_err(struct hl_device *hdev, u16 event_id, u64 *event_mask);
+void hl_handle_fw_err(struct hl_device *hdev, struct hl_info_fw_err_info *info);
+void hl_capture_engine_err(struct hl_device *hdev, u16 engine_id, u16 error_count);
+void hl_enable_err_info_capture(struct hl_error_info *captured_err_info);
+void hl_init_cpu_for_irq(struct hl_device *hdev);
+void hl_set_irq_affinity(struct hl_device *hdev, int irq);
+void hl_eq_heartbeat_event_handle(struct hl_device *hdev);
+void hl_handle_clk_change_event(struct hl_device *hdev, u16 event_type, u64 *event_mask);
+void hl_eq_cpld_shutdown_event_handle(struct hl_device *hdev, u16 event_id, u64 *event_mask);
#ifdef CONFIG_DEBUG_FS
-void hl_debugfs_init(void);
-void hl_debugfs_fini(void);
+int hl_debugfs_device_init(struct hl_device *hdev);
+void hl_debugfs_device_fini(struct hl_device *hdev);
void hl_debugfs_add_device(struct hl_device *hdev);
-void hl_debugfs_remove_device(struct hl_device *hdev);
void hl_debugfs_add_file(struct hl_fpriv *hpriv);
void hl_debugfs_remove_file(struct hl_fpriv *hpriv);
void hl_debugfs_add_cb(struct hl_cb *cb);
@@ -3765,14 +4155,16 @@ void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx);
void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx);
void hl_debugfs_set_state_dump(struct hl_device *hdev, char *data,
unsigned long length);
+void hl_debugfs_cfg_access_history_dump(struct hl_device *hdev);
#else
-static inline void __init hl_debugfs_init(void)
+static inline int hl_debugfs_device_init(struct hl_device *hdev)
{
+ return 0;
}
-static inline void hl_debugfs_fini(void)
+static inline void hl_debugfs_device_fini(struct hl_device *hdev)
{
}
@@ -3780,10 +4172,6 @@ static inline void hl_debugfs_add_device(struct hl_device *hdev)
{
}
-static inline void hl_debugfs_remove_device(struct hl_device *hdev)
-{
-}
-
static inline void hl_debugfs_add_file(struct hl_fpriv *hpriv)
{
}
@@ -3843,6 +4231,10 @@ static inline void hl_debugfs_set_state_dump(struct hl_device *hdev,
{
}
+static inline void hl_debugfs_cfg_access_history_dump(struct hl_device *hdev)
+{
+}
+
#endif
/* Security */
@@ -3895,11 +4287,12 @@ void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
const u32 pb_blocks[], u32 blocks_array_size);
/* IOCTLs */
-long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg);
-int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data);
-int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data);
-int hl_wait_ioctl(struct hl_fpriv *hpriv, void *data);
-int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data);
+int hl_info_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv);
+int hl_cb_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv);
+int hl_cs_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv);
+int hl_wait_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv);
+int hl_mem_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv);
+int hl_debug_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv);
#endif /* HABANALABSP_H_ */
diff --git a/drivers/accel/habanalabs/common/habanalabs_drv.c b/drivers/accel/habanalabs/common/habanalabs_drv.c
new file mode 100644
index 000000000000..0035748f3228
--- /dev/null
+++ b/drivers/accel/habanalabs/common/habanalabs_drv.c
@@ -0,0 +1,784 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2021 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+#define pr_fmt(fmt) "habanalabs: " fmt
+
+#include "habanalabs.h"
+#include "../include/hw_ip/pci/pci_general.h"
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_ioctl.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/habanalabs.h>
+
+#define HL_DRIVER_AUTHOR "HabanaLabs Kernel Driver Team"
+
+#define HL_DRIVER_DESC "Driver for HabanaLabs's AI Accelerators"
+
+MODULE_AUTHOR(HL_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(HL_DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
+
+static int hl_major;
+static DEFINE_IDR(hl_devs_idr);
+static DEFINE_MUTEX(hl_devs_idr_lock);
+
+#define HL_DEFAULT_TIMEOUT_LOCKED 30 /* 30 seconds */
+#define GAUDI_DEFAULT_TIMEOUT_LOCKED 600 /* 10 minutes */
+
+static int timeout_locked = HL_DEFAULT_TIMEOUT_LOCKED;
+static int reset_on_lockup = 1;
+static int memory_scrub;
+static ulong boot_error_status_mask = ULONG_MAX;
+
+module_param(timeout_locked, int, 0444);
+MODULE_PARM_DESC(timeout_locked,
+ "Device lockup timeout in seconds (0 = disabled, default 30s)");
+
+module_param(reset_on_lockup, int, 0444);
+MODULE_PARM_DESC(reset_on_lockup,
+ "Do device reset on lockup (0 = no, 1 = yes, default yes)");
+
+module_param(memory_scrub, int, 0444);
+MODULE_PARM_DESC(memory_scrub,
+ "Scrub device memory in various states (0 = no, 1 = yes, default no)");
+
+module_param(boot_error_status_mask, ulong, 0444);
+MODULE_PARM_DESC(boot_error_status_mask,
+ "Mask of the error status during device CPU boot (If bitX is cleared then error X is masked. Default all 1's)");
+
+#define PCI_IDS_GOYA 0x0001
+#define PCI_IDS_GAUDI 0x1000
+#define PCI_IDS_GAUDI_SEC 0x1010
+
+#define PCI_IDS_GAUDI2 0x1020
+
+static const struct pci_device_id ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GOYA), },
+ { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI), },
+ { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI_SEC), },
+ { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI2), },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ids);
+
+static const struct drm_ioctl_desc hl_drm_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(HL_INFO, hl_info_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(HL_CB, hl_cb_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(HL_CS, hl_cs_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(HL_WAIT_CS, hl_wait_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(HL_MEMORY, hl_mem_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(HL_DEBUG, hl_debug_ioctl, 0),
+};
+
+static const struct file_operations hl_fops = {
+ .owner = THIS_MODULE,
+ .open = accel_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .compat_ioctl = drm_compat_ioctl,
+ .llseek = noop_llseek,
+ .mmap = hl_mmap
+};
+
+static const struct drm_driver hl_driver = {
+ .driver_features = DRIVER_COMPUTE_ACCEL,
+
+ .name = HL_NAME,
+ .desc = HL_DRIVER_DESC,
+ .major = LINUX_VERSION_MAJOR,
+ .minor = LINUX_VERSION_PATCHLEVEL,
+ .patchlevel = LINUX_VERSION_SUBLEVEL,
+
+ .fops = &hl_fops,
+ .open = hl_device_open,
+ .postclose = hl_device_release,
+ .ioctls = hl_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(hl_drm_ioctls)
+};
+
+/*
+ * get_asic_type - translate device id to asic type
+ *
+ * @hdev: pointer to habanalabs device structure.
+ *
+ * Translate device id and revision id to asic type.
+ * In case of unidentified device, return -1
+ */
+static enum hl_asic_type get_asic_type(struct hl_device *hdev)
+{
+ struct pci_dev *pdev = hdev->pdev;
+ enum hl_asic_type asic_type = ASIC_INVALID;
+
+ switch (pdev->device) {
+ case PCI_IDS_GOYA:
+ asic_type = ASIC_GOYA;
+ break;
+ case PCI_IDS_GAUDI:
+ asic_type = ASIC_GAUDI;
+ break;
+ case PCI_IDS_GAUDI_SEC:
+ asic_type = ASIC_GAUDI_SEC;
+ break;
+ case PCI_IDS_GAUDI2:
+ switch (pdev->revision) {
+ case REV_ID_A:
+ asic_type = ASIC_GAUDI2;
+ break;
+ case REV_ID_B:
+ asic_type = ASIC_GAUDI2B;
+ break;
+ case REV_ID_C:
+ asic_type = ASIC_GAUDI2C;
+ break;
+ case REV_ID_D:
+ asic_type = ASIC_GAUDI2D;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return asic_type;
+}
+
+static bool is_asic_secured(enum hl_asic_type asic_type)
+{
+ switch (asic_type) {
+ case ASIC_GAUDI_SEC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * hl_device_open() - open function for habanalabs device.
+ * @ddev: pointer to DRM device structure.
+ * @file: pointer to DRM file private data structure.
+ *
+ * Called when process opens an habanalabs device.
+ */
+int hl_device_open(struct drm_device *ddev, struct drm_file *file_priv)
+{
+ struct hl_device *hdev = to_hl_device(ddev);
+ enum hl_device_status status;
+ struct hl_fpriv *hpriv;
+ int rc;
+
+ hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL);
+ if (!hpriv)
+ return -ENOMEM;
+
+ hpriv->hdev = hdev;
+ mutex_init(&hpriv->notifier_event.lock);
+ mutex_init(&hpriv->restore_phase_mutex);
+ mutex_init(&hpriv->ctx_lock);
+ kref_init(&hpriv->refcount);
+
+ hl_ctx_mgr_init(&hpriv->ctx_mgr);
+ hl_mem_mgr_init(hpriv->hdev->dev, &hpriv->mem_mgr);
+
+ hpriv->taskpid = get_task_pid(current, PIDTYPE_PID);
+
+ mutex_lock(&hdev->fpriv_list_lock);
+
+ if (!hl_device_operational(hdev, &status)) {
+ dev_dbg_ratelimited(hdev->dev,
+ "Can't open %s because it is %s\n",
+ dev_name(hdev->dev), hdev->status[status]);
+
+ if (status == HL_DEVICE_STATUS_IN_RESET ||
+ status == HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE)
+ rc = -EAGAIN;
+ else
+ rc = -EPERM;
+
+ goto out_err;
+ }
+
+ if (hdev->is_in_dram_scrub) {
+ dev_dbg_ratelimited(hdev->dev,
+ "Can't open %s during dram scrub\n",
+ dev_name(hdev->dev));
+ rc = -EAGAIN;
+ goto out_err;
+ }
+
+ if (hdev->compute_ctx_in_release) {
+ dev_dbg_ratelimited(hdev->dev,
+ "Can't open %s because another user is still releasing it\n",
+ dev_name(hdev->dev));
+ rc = -EAGAIN;
+ goto out_err;
+ }
+
+ if (hdev->is_compute_ctx_active) {
+ dev_dbg_ratelimited(hdev->dev,
+ "Can't open %s because another user is working on it\n",
+ dev_name(hdev->dev));
+ rc = -EBUSY;
+ goto out_err;
+ }
+
+ rc = hl_ctx_create(hdev, hpriv);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to create context %d\n", rc);
+ goto out_err;
+ }
+
+ list_add(&hpriv->dev_node, &hdev->fpriv_list);
+ mutex_unlock(&hdev->fpriv_list_lock);
+
+ hdev->asic_funcs->send_device_activity(hdev, true);
+
+ hl_debugfs_add_file(hpriv);
+
+ hl_enable_err_info_capture(&hdev->captured_err_info);
+
+ hdev->open_counter++;
+ hdev->last_successful_open_jif = jiffies;
+ hdev->last_successful_open_ktime = ktime_get();
+
+ file_priv->driver_priv = hpriv;
+ hpriv->file_priv = file_priv;
+
+ return 0;
+
+out_err:
+ mutex_unlock(&hdev->fpriv_list_lock);
+ hl_mem_mgr_fini(&hpriv->mem_mgr, NULL);
+ hl_mem_mgr_idr_destroy(&hpriv->mem_mgr);
+ hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr);
+ mutex_destroy(&hpriv->ctx_lock);
+ mutex_destroy(&hpriv->restore_phase_mutex);
+ mutex_destroy(&hpriv->notifier_event.lock);
+ put_pid(hpriv->taskpid);
+
+ kfree(hpriv);
+
+ return rc;
+}
+
+int hl_device_open_ctrl(struct inode *inode, struct file *filp)
+{
+ struct hl_device *hdev;
+ struct hl_fpriv *hpriv;
+ int rc;
+
+ mutex_lock(&hl_devs_idr_lock);
+ hdev = idr_find(&hl_devs_idr, iminor(inode));
+ mutex_unlock(&hl_devs_idr_lock);
+
+ if (!hdev) {
+ pr_err("Couldn't find device %d:%d\n",
+ imajor(inode), iminor(inode));
+ return -ENXIO;
+ }
+
+ hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL);
+ if (!hpriv)
+ return -ENOMEM;
+
+ /* Prevent other routines from reading partial hpriv data by
+ * initializing hpriv fields before inserting it to the list
+ */
+ hpriv->hdev = hdev;
+ filp->private_data = hpriv;
+
+ nonseekable_open(inode, filp);
+
+ hpriv->taskpid = get_task_pid(current, PIDTYPE_PID);
+
+ mutex_lock(&hdev->fpriv_ctrl_list_lock);
+
+ if (!hl_ctrl_device_operational(hdev, NULL)) {
+ dev_dbg_ratelimited(hdev->dev_ctrl,
+ "Can't open %s because it is disabled\n",
+ dev_name(hdev->dev_ctrl));
+ rc = -EPERM;
+ goto out_err;
+ }
+
+ list_add(&hpriv->dev_node, &hdev->fpriv_ctrl_list);
+ mutex_unlock(&hdev->fpriv_ctrl_list_lock);
+
+ return 0;
+
+out_err:
+ mutex_unlock(&hdev->fpriv_ctrl_list_lock);
+ filp->private_data = NULL;
+ put_pid(hpriv->taskpid);
+
+ kfree(hpriv);
+
+ return rc;
+}
+
+static void set_driver_behavior_per_device(struct hl_device *hdev)
+{
+ hdev->nic_ports_mask = 0;
+ hdev->fw_components = FW_TYPE_ALL_TYPES;
+ hdev->cpu_queues_enable = 1;
+ hdev->pldm = 0;
+ hdev->hard_reset_on_fw_events = 1;
+ hdev->bmc_enable = 1;
+ hdev->reset_on_preboot_fail = 1;
+ hdev->heartbeat = 1;
+}
+
+static void copy_kernel_module_params_to_device(struct hl_device *hdev)
+{
+ hdev->asic_prop.fw_security_enabled = is_asic_secured(hdev->asic_type);
+
+ hdev->major = hl_major;
+ hdev->memory_scrub = memory_scrub;
+ hdev->reset_on_lockup = reset_on_lockup;
+ hdev->boot_error_status_mask = boot_error_status_mask;
+}
+
+static void fixup_device_params_per_asic(struct hl_device *hdev, int timeout)
+{
+ switch (hdev->asic_type) {
+ case ASIC_GAUDI:
+ case ASIC_GAUDI_SEC:
+ /* If user didn't request a different timeout than the default one, we have
+ * a different default timeout for Gaudi
+ */
+ if (timeout == HL_DEFAULT_TIMEOUT_LOCKED)
+ hdev->timeout_jiffies = secs_to_jiffies(GAUDI_DEFAULT_TIMEOUT_LOCKED);
+
+ hdev->reset_upon_device_release = 0;
+ break;
+
+ case ASIC_GOYA:
+ hdev->reset_upon_device_release = 0;
+ break;
+
+ default:
+ hdev->reset_upon_device_release = 1;
+ break;
+ }
+}
+
+static int fixup_device_params(struct hl_device *hdev)
+{
+ int tmp_timeout;
+
+ tmp_timeout = timeout_locked;
+
+ hdev->fw_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC;
+ hdev->fw_comms_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC;
+
+ if (tmp_timeout)
+ hdev->timeout_jiffies = secs_to_jiffies(tmp_timeout);
+ else
+ hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
+
+ hdev->stop_on_err = true;
+ hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
+ hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT;
+
+ /* Enable only after the initialization of the device */
+ hdev->disabled = true;
+
+ if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU) &&
+ (hdev->fw_components & ~FW_TYPE_PREBOOT_CPU)) {
+ pr_err("Preboot must be set along with other components");
+ return -EINVAL;
+ }
+
+ /* If CPU queues not enabled, no way to do heartbeat */
+ if (!hdev->cpu_queues_enable)
+ hdev->heartbeat = 0;
+ fixup_device_params_per_asic(hdev, tmp_timeout);
+
+ return 0;
+}
+
+static int allocate_device_id(struct hl_device *hdev)
+{
+ int id;
+
+ mutex_lock(&hl_devs_idr_lock);
+ id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, GFP_KERNEL);
+ mutex_unlock(&hl_devs_idr_lock);
+
+ if (id < 0) {
+ if (id == -ENOSPC)
+ pr_err("too many devices in the system\n");
+ return -EBUSY;
+ }
+
+ hdev->id = id;
+
+ /*
+ * Firstly initialized with the internal device ID.
+ * Will be updated later after the DRM device registration to hold the minor ID.
+ */
+ hdev->cdev_idx = hdev->id;
+
+ return 0;
+}
+
+/**
+ * create_hdev - create habanalabs device instance
+ *
+ * @dev: will hold the pointer to the new habanalabs device structure
+ * @pdev: pointer to the pci device
+ *
+ * Allocate memory for habanalabs device and initialize basic fields
+ * Identify the ASIC type
+ * Allocate ID (minor) for the device (only for real devices)
+ */
+static int create_hdev(struct hl_device **dev, struct pci_dev *pdev)
+{
+ struct hl_device *hdev;
+ int rc;
+
+ *dev = NULL;
+
+ hdev = devm_drm_dev_alloc(&pdev->dev, &hl_driver, struct hl_device, drm);
+ if (IS_ERR(hdev))
+ return PTR_ERR(hdev);
+
+ hdev->dev = hdev->drm.dev;
+
+ /* Will be NULL in case of simulator device */
+ hdev->pdev = pdev;
+
+ /* Assign status description string */
+ strscpy(hdev->status[HL_DEVICE_STATUS_OPERATIONAL], "operational", HL_STR_MAX);
+ strscpy(hdev->status[HL_DEVICE_STATUS_IN_RESET], "in reset", HL_STR_MAX);
+ strscpy(hdev->status[HL_DEVICE_STATUS_MALFUNCTION], "disabled", HL_STR_MAX);
+ strscpy(hdev->status[HL_DEVICE_STATUS_NEEDS_RESET], "needs reset", HL_STR_MAX);
+ strscpy(hdev->status[HL_DEVICE_STATUS_IN_DEVICE_CREATION],
+ "in device creation", HL_STR_MAX);
+ strscpy(hdev->status[HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE],
+ "in reset after device release", HL_STR_MAX);
+
+
+ /* First, we must find out which ASIC are we handling. This is needed
+ * to configure the behavior of the driver (kernel parameters)
+ */
+ hdev->asic_type = get_asic_type(hdev);
+ if (hdev->asic_type == ASIC_INVALID) {
+ dev_err(&pdev->dev, "Unsupported ASIC\n");
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ copy_kernel_module_params_to_device(hdev);
+
+ set_driver_behavior_per_device(hdev);
+
+ fixup_device_params(hdev);
+
+ rc = allocate_device_id(hdev);
+ if (rc)
+ goto out_err;
+
+ *dev = hdev;
+
+ return 0;
+
+out_err:
+ return rc;
+}
+
+/*
+ * destroy_hdev - destroy habanalabs device instance
+ *
+ * @dev: pointer to the habanalabs device structure
+ *
+ */
+static void destroy_hdev(struct hl_device *hdev)
+{
+ /* Remove device from the device list */
+ mutex_lock(&hl_devs_idr_lock);
+ idr_remove(&hl_devs_idr, hdev->id);
+ mutex_unlock(&hl_devs_idr_lock);
+
+}
+
+static int hl_pmops_suspend(struct device *dev)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ pr_debug("Going to suspend PCI device\n");
+
+ if (!hdev) {
+ pr_err("device pointer is NULL in suspend\n");
+ return 0;
+ }
+
+ return hl_device_suspend(hdev);
+}
+
+static int hl_pmops_resume(struct device *dev)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ pr_debug("Going to resume PCI device\n");
+
+ if (!hdev) {
+ pr_err("device pointer is NULL in resume\n");
+ return 0;
+ }
+
+ return hl_device_resume(hdev);
+}
+
+/**
+ * hl_pci_probe - probe PCI habanalabs devices
+ *
+ * @pdev: pointer to pci device
+ * @id: pointer to pci device id structure
+ *
+ * Standard PCI probe function for habanalabs device.
+ * Create a new habanalabs device and initialize it according to the
+ * device's type
+ */
+static int hl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct hl_device *hdev;
+ int rc;
+
+ dev_info(&pdev->dev, HL_NAME
+ " device found [%04x:%04x] (rev %x)\n",
+ (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
+
+ rc = create_hdev(&hdev, pdev);
+ if (rc)
+ return rc;
+
+ pci_set_drvdata(pdev, hdev);
+
+ rc = hl_device_init(hdev);
+ if (rc) {
+ dev_err(&pdev->dev, "Fatal error during habanalabs device init\n");
+ rc = -ENODEV;
+ goto disable_device;
+ }
+
+ return 0;
+
+disable_device:
+ pci_set_drvdata(pdev, NULL);
+ destroy_hdev(hdev);
+
+ return rc;
+}
+
+/*
+ * hl_pci_remove - remove PCI habanalabs devices
+ *
+ * @pdev: pointer to pci device
+ *
+ * Standard PCI remove function for habanalabs device
+ */
+static void hl_pci_remove(struct pci_dev *pdev)
+{
+ struct hl_device *hdev;
+
+ hdev = pci_get_drvdata(pdev);
+ if (!hdev)
+ return;
+
+ hl_device_fini(hdev);
+ pci_set_drvdata(pdev, NULL);
+ destroy_hdev(hdev);
+}
+
+/**
+ * hl_pci_err_detected - a PCI bus error detected on this device
+ *
+ * @pdev: pointer to pci device
+ * @state: PCI error type
+ *
+ * Called by the PCI subsystem whenever a non-correctable
+ * PCI bus error is detected
+ */
+static pci_ers_result_t
+hl_pci_err_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct hl_device *hdev = pci_get_drvdata(pdev);
+ enum pci_ers_result result;
+
+ switch (state) {
+ case pci_channel_io_normal:
+ dev_warn(hdev->dev, "PCI normal state error detected\n");
+ return PCI_ERS_RESULT_CAN_RECOVER;
+
+ case pci_channel_io_frozen:
+ dev_warn(hdev->dev, "PCI frozen state error detected\n");
+ result = PCI_ERS_RESULT_NEED_RESET;
+ break;
+
+ case pci_channel_io_perm_failure:
+ dev_warn(hdev->dev, "PCI failure state error detected\n");
+ result = PCI_ERS_RESULT_DISCONNECT;
+ break;
+
+ default:
+ result = PCI_ERS_RESULT_NONE;
+ }
+
+ hdev->asic_funcs->halt_engines(hdev, true, false);
+
+ return result;
+}
+
+/**
+ * hl_pci_err_resume - resume after a PCI slot reset
+ *
+ * @pdev: pointer to pci device
+ *
+ */
+static void hl_pci_err_resume(struct pci_dev *pdev)
+{
+ struct hl_device *hdev = pci_get_drvdata(pdev);
+
+ dev_warn(hdev->dev, "Resuming device after PCI slot reset\n");
+ hl_device_resume(hdev);
+}
+
+/**
+ * hl_pci_err_slot_reset - a PCI slot reset has just happened
+ *
+ * @pdev: pointer to pci device
+ *
+ * Determine if the driver can recover from the PCI slot reset
+ */
+static pci_ers_result_t hl_pci_err_slot_reset(struct pci_dev *pdev)
+{
+ struct hl_device *hdev = pci_get_drvdata(pdev);
+
+ dev_warn(hdev->dev, "PCI slot reset detected\n");
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void hl_pci_reset_prepare(struct pci_dev *pdev)
+{
+ struct hl_device *hdev;
+
+ hdev = pci_get_drvdata(pdev);
+ if (!hdev)
+ return;
+
+ hdev->disabled = true;
+}
+
+static void hl_pci_reset_done(struct pci_dev *pdev)
+{
+ struct hl_device *hdev;
+ u32 flags;
+
+ hdev = pci_get_drvdata(pdev);
+ if (!hdev)
+ return;
+
+ /*
+ * Schedule a thread to trigger hard reset.
+ * The reason for this handler, is for rare cases where the driver is up
+ * and FLR occurs. This is valid only when working with no VM, so FW handles FLR
+ * and resets the device. FW will go back preboot stage, so driver needs to perform
+ * hard reset in order to load FW fit again.
+ */
+ flags = HL_DRV_RESET_HARD | HL_DRV_RESET_BYPASS_REQ_TO_FW;
+
+ hl_device_reset(hdev, flags);
+}
+
+static const struct dev_pm_ops hl_pm_ops = {
+ .suspend = hl_pmops_suspend,
+ .resume = hl_pmops_resume,
+};
+
+static const struct pci_error_handlers hl_pci_err_handler = {
+ .error_detected = hl_pci_err_detected,
+ .slot_reset = hl_pci_err_slot_reset,
+ .resume = hl_pci_err_resume,
+ .reset_prepare = hl_pci_reset_prepare,
+ .reset_done = hl_pci_reset_done,
+};
+
+static struct pci_driver hl_pci_driver = {
+ .name = HL_NAME,
+ .id_table = ids,
+ .probe = hl_pci_probe,
+ .remove = hl_pci_remove,
+ .shutdown = hl_pci_remove,
+ .driver = {
+ .name = HL_NAME,
+ .pm = &hl_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .err_handler = &hl_pci_err_handler,
+};
+
+/*
+ * hl_init - Initialize the habanalabs kernel driver
+ */
+static int __init hl_init(void)
+{
+ int rc;
+ dev_t dev;
+
+ pr_info("loading driver\n");
+
+ rc = alloc_chrdev_region(&dev, 0, HL_MAX_MINORS, HL_NAME);
+ if (rc < 0) {
+ pr_err("unable to get major\n");
+ return rc;
+ }
+
+ hl_major = MAJOR(dev);
+
+ rc = pci_register_driver(&hl_pci_driver);
+ if (rc) {
+ pr_err("failed to register pci device\n");
+ goto remove_major;
+ }
+
+ pr_debug("driver loaded\n");
+
+ return 0;
+
+remove_major:
+ unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS);
+ return rc;
+}
+
+/*
+ * hl_exit - Release all resources of the habanalabs kernel driver
+ */
+static void __exit hl_exit(void)
+{
+ pci_unregister_driver(&hl_pci_driver);
+
+ unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS);
+
+ idr_destroy(&hl_devs_idr);
+
+ pr_debug("driver removed\n");
+}
+
+module_init(hl_init);
+module_exit(hl_exit);
diff --git a/drivers/accel/habanalabs/common/habanalabs_ioctl.c b/drivers/accel/habanalabs/common/habanalabs_ioctl.c
new file mode 100644
index 000000000000..fdfdabc85e54
--- /dev/null
+++ b/drivers/accel/habanalabs/common/habanalabs_ioctl.c
@@ -0,0 +1,1319 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#define pr_fmt(fmt) "habanalabs: " fmt
+
+#include <uapi/drm/habanalabs_accel.h>
+#include "habanalabs.h"
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+/* make sure there is space for all the signed info */
+static_assert(sizeof(struct cpucp_info) <= SEC_DEV_INFO_BUF_SZ);
+
+static u32 hl_debug_struct_size[HL_DEBUG_OP_TIMESTAMP + 1] = {
+ [HL_DEBUG_OP_ETR] = sizeof(struct hl_debug_params_etr),
+ [HL_DEBUG_OP_ETF] = sizeof(struct hl_debug_params_etf),
+ [HL_DEBUG_OP_STM] = sizeof(struct hl_debug_params_stm),
+ [HL_DEBUG_OP_FUNNEL] = 0,
+ [HL_DEBUG_OP_BMON] = sizeof(struct hl_debug_params_bmon),
+ [HL_DEBUG_OP_SPMU] = sizeof(struct hl_debug_params_spmu),
+ [HL_DEBUG_OP_TIMESTAMP] = 0
+
+};
+
+static int device_status_info(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_device_status dev_stat = {0};
+ u32 size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!size) || (!out))
+ return -EINVAL;
+
+ dev_stat.status = hl_device_status(hdev);
+
+ return copy_to_user(out, &dev_stat,
+ min((size_t)size, sizeof(dev_stat))) ? -EFAULT : 0;
+}
+
+static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_hw_ip_info hw_ip = {0};
+ u32 size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 sram_kmd_size, dram_kmd_size, dram_available_size;
+
+ if ((!size) || (!out))
+ return -EINVAL;
+
+ sram_kmd_size = (prop->sram_user_base_address -
+ prop->sram_base_address);
+ dram_kmd_size = (prop->dram_user_base_address -
+ prop->dram_base_address);
+
+ hw_ip.device_id = hdev->asic_funcs->get_pci_id(hdev);
+ hw_ip.sram_base_address = prop->sram_user_base_address;
+ hw_ip.dram_base_address =
+ prop->dram_supports_virtual_memory ?
+ prop->dmmu.start_addr : prop->dram_user_base_address;
+ hw_ip.tpc_enabled_mask = prop->tpc_enabled_mask & 0xFF;
+ hw_ip.tpc_enabled_mask_ext = prop->tpc_enabled_mask;
+
+ hw_ip.sram_size = prop->sram_size - sram_kmd_size;
+
+ dram_available_size = prop->dram_size - dram_kmd_size;
+
+ hw_ip.dram_size = DIV_ROUND_DOWN_ULL(dram_available_size, prop->dram_page_size) *
+ prop->dram_page_size;
+
+ if (hw_ip.dram_size > PAGE_SIZE)
+ hw_ip.dram_enabled = 1;
+
+ hw_ip.dram_page_size = prop->dram_page_size;
+ hw_ip.device_mem_alloc_default_page_size = prop->device_mem_alloc_default_page_size;
+ hw_ip.num_of_events = prop->num_of_events;
+
+ memcpy(hw_ip.cpucp_version, prop->cpucp_info.cpucp_version,
+ min(VERSION_MAX_LEN, HL_INFO_VERSION_MAX_LEN));
+
+ memcpy(hw_ip.card_name, prop->cpucp_info.card_name,
+ min(CARD_NAME_MAX_LEN, HL_INFO_CARD_NAME_MAX_LEN));
+
+ hw_ip.cpld_version = le32_to_cpu(prop->cpucp_info.cpld_version);
+ hw_ip.module_id = le32_to_cpu(prop->cpucp_info.card_location);
+
+ hw_ip.psoc_pci_pll_nr = prop->psoc_pci_pll_nr;
+ hw_ip.psoc_pci_pll_nf = prop->psoc_pci_pll_nf;
+ hw_ip.psoc_pci_pll_od = prop->psoc_pci_pll_od;
+ hw_ip.psoc_pci_pll_div_factor = prop->psoc_pci_pll_div_factor;
+
+ hw_ip.decoder_enabled_mask = prop->decoder_enabled_mask;
+ hw_ip.mme_master_slave_mode = prop->mme_master_slave_mode;
+ hw_ip.first_available_interrupt_id = prop->first_available_user_interrupt;
+ hw_ip.number_of_user_interrupts = prop->user_interrupt_count;
+ hw_ip.tpc_interrupt_id = prop->tpc_interrupt_id;
+
+ hw_ip.edma_enabled_mask = prop->edma_enabled_mask;
+ hw_ip.server_type = prop->server_type;
+ hw_ip.security_enabled = prop->fw_security_enabled;
+ hw_ip.revision_id = hdev->pdev->revision;
+ hw_ip.rotator_enabled_mask = prop->rotator_enabled_mask;
+ hw_ip.engine_core_interrupt_reg_addr = prop->engine_core_interrupt_reg_addr;
+ hw_ip.reserved_dram_size = dram_kmd_size;
+
+ return copy_to_user(out, &hw_ip,
+ min((size_t) size, sizeof(hw_ip))) ? -EFAULT : 0;
+}
+
+static int hw_events_info(struct hl_device *hdev, bool aggregate,
+ struct hl_info_args *args)
+{
+ u32 size, max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ void *arr;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ arr = hdev->asic_funcs->get_events_stat(hdev, aggregate, &size);
+ if (!arr) {
+ dev_err(hdev->dev, "Events info not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ return copy_to_user(out, arr, min(max_size, size)) ? -EFAULT : 0;
+}
+
+static int events_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ u32 max_size = args->return_size;
+ u64 events_mask;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((max_size < sizeof(u64)) || (!out))
+ return -EINVAL;
+
+ mutex_lock(&hpriv->notifier_event.lock);
+ events_mask = hpriv->notifier_event.events_mask;
+ hpriv->notifier_event.events_mask = 0;
+ mutex_unlock(&hpriv->notifier_event.lock);
+
+ return copy_to_user(out, &events_mask, sizeof(u64)) ? -EFAULT : 0;
+}
+
+static int dram_usage_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_info_dram_usage dram_usage = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 dram_kmd_size;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ dram_kmd_size = (prop->dram_user_base_address -
+ prop->dram_base_address);
+ dram_usage.dram_free_mem = (prop->dram_size - dram_kmd_size) -
+ atomic64_read(&hdev->dram_used_mem);
+ if (hpriv->ctx)
+ dram_usage.ctx_dram_mem =
+ atomic64_read(&hpriv->ctx->dram_phys_mem);
+
+ return copy_to_user(out, &dram_usage,
+ min((size_t) max_size, sizeof(dram_usage))) ? -EFAULT : 0;
+}
+
+static int hw_idle(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_hw_idle hw_idle = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev,
+ hw_idle.busy_engines_mask_ext,
+ HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL);
+ hw_idle.busy_engines_mask =
+ lower_32_bits(hw_idle.busy_engines_mask_ext[0]);
+
+ return copy_to_user(out, &hw_idle,
+ min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0;
+}
+
+static int debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, struct hl_debug_args *args)
+{
+ struct hl_debug_params *params;
+ void *input = NULL, *output = NULL;
+ int rc;
+
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ params->reg_idx = args->reg_idx;
+ params->enable = args->enable;
+ params->op = args->op;
+
+ if (args->input_ptr && args->input_size) {
+ input = kzalloc(hl_debug_struct_size[args->op], GFP_KERNEL);
+ if (!input) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(input, u64_to_user_ptr(args->input_ptr),
+ args->input_size)) {
+ rc = -EFAULT;
+ dev_err(hdev->dev, "failed to copy input debug data\n");
+ goto out;
+ }
+
+ params->input = input;
+ }
+
+ if (args->output_ptr && args->output_size) {
+ output = kzalloc(args->output_size, GFP_KERNEL);
+ if (!output) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ params->output = output;
+ params->output_size = args->output_size;
+ }
+
+ rc = hdev->asic_funcs->debug_coresight(hdev, ctx, params);
+ if (rc) {
+ dev_err(hdev->dev,
+ "debug coresight operation failed %d\n", rc);
+ goto out;
+ }
+
+ if (output && copy_to_user((void __user *) (uintptr_t) args->output_ptr,
+ output, args->output_size)) {
+ dev_err(hdev->dev, "copy to user failed in debug ioctl\n");
+ rc = -EFAULT;
+ goto out;
+ }
+
+
+out:
+ kfree(params);
+ kfree(output);
+ kfree(input);
+
+ return rc;
+}
+
+static int device_utilization(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_device_utilization device_util = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_device_utilization(hdev, &device_util.utilization);
+ if (rc)
+ return -EINVAL;
+
+ return copy_to_user(out, &device_util,
+ min((size_t) max_size, sizeof(device_util))) ? -EFAULT : 0;
+}
+
+static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_clk_rate clk_rate = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, &clk_rate.max_clk_rate_mhz);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &clk_rate, min_t(size_t, max_size, sizeof(clk_rate)))
+ ? -EFAULT : 0;
+}
+
+static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_reset_count reset_count = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ reset_count.hard_reset_cnt = hdev->reset_info.hard_reset_cnt;
+ reset_count.soft_reset_cnt = hdev->reset_info.compute_reset_cnt;
+
+ return copy_to_user(out, &reset_count,
+ min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0;
+}
+
+static int time_sync_info(struct hl_device *hdev, struct hl_info_args *args)
+{
+ struct hl_info_time_sync time_sync = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ time_sync.device_time = hdev->asic_funcs->get_device_time(hdev);
+ time_sync.host_time = ktime_get_raw_ns();
+ time_sync.tsc_time = rdtsc();
+
+ return copy_to_user(out, &time_sync,
+ min((size_t) max_size, sizeof(time_sync))) ? -EFAULT : 0;
+}
+
+static int pci_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_info_pci_counters pci_counters = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_cpucp_pci_counters_get(hdev, &pci_counters);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &pci_counters,
+ min((size_t) max_size, sizeof(pci_counters))) ? -EFAULT : 0;
+}
+
+static int clk_throttle_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_info_clk_throttle clk_throttle = {0};
+ ktime_t end_time, zero_time = ktime_set(0, 0);
+ u32 max_size = args->return_size;
+ int i;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ mutex_lock(&hdev->clk_throttling.lock);
+
+ clk_throttle.clk_throttling_reason = hdev->clk_throttling.current_reason;
+
+ for (i = 0 ; i < HL_CLK_THROTTLE_TYPE_MAX ; i++) {
+ if (!(hdev->clk_throttling.aggregated_reason & BIT(i)))
+ continue;
+
+ clk_throttle.clk_throttling_timestamp_us[i] =
+ ktime_to_us(hdev->clk_throttling.timestamp[i].start);
+
+ if (ktime_compare(hdev->clk_throttling.timestamp[i].end, zero_time))
+ end_time = hdev->clk_throttling.timestamp[i].end;
+ else
+ end_time = ktime_get();
+
+ clk_throttle.clk_throttling_duration_ns[i] =
+ ktime_to_ns(ktime_sub(end_time,
+ hdev->clk_throttling.timestamp[i].start));
+
+ }
+ mutex_unlock(&hdev->clk_throttling.lock);
+
+ return copy_to_user(out, &clk_throttle,
+ min((size_t) max_size, sizeof(clk_throttle))) ? -EFAULT : 0;
+}
+
+static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_info_cs_counters cs_counters = {0};
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_cs_counters_atomic *cntr;
+ u32 max_size = args->return_size;
+
+ cntr = &hdev->aggregated_cs_counters;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ cs_counters.total_out_of_mem_drop_cnt =
+ atomic64_read(&cntr->out_of_mem_drop_cnt);
+ cs_counters.total_parsing_drop_cnt =
+ atomic64_read(&cntr->parsing_drop_cnt);
+ cs_counters.total_queue_full_drop_cnt =
+ atomic64_read(&cntr->queue_full_drop_cnt);
+ cs_counters.total_device_in_reset_drop_cnt =
+ atomic64_read(&cntr->device_in_reset_drop_cnt);
+ cs_counters.total_max_cs_in_flight_drop_cnt =
+ atomic64_read(&cntr->max_cs_in_flight_drop_cnt);
+ cs_counters.total_validation_drop_cnt =
+ atomic64_read(&cntr->validation_drop_cnt);
+
+ if (hpriv->ctx) {
+ cs_counters.ctx_out_of_mem_drop_cnt =
+ atomic64_read(
+ &hpriv->ctx->cs_counters.out_of_mem_drop_cnt);
+ cs_counters.ctx_parsing_drop_cnt =
+ atomic64_read(
+ &hpriv->ctx->cs_counters.parsing_drop_cnt);
+ cs_counters.ctx_queue_full_drop_cnt =
+ atomic64_read(
+ &hpriv->ctx->cs_counters.queue_full_drop_cnt);
+ cs_counters.ctx_device_in_reset_drop_cnt =
+ atomic64_read(
+ &hpriv->ctx->cs_counters.device_in_reset_drop_cnt);
+ cs_counters.ctx_max_cs_in_flight_drop_cnt =
+ atomic64_read(
+ &hpriv->ctx->cs_counters.max_cs_in_flight_drop_cnt);
+ cs_counters.ctx_validation_drop_cnt =
+ atomic64_read(
+ &hpriv->ctx->cs_counters.validation_drop_cnt);
+ }
+
+ return copy_to_user(out, &cs_counters,
+ min((size_t) max_size, sizeof(cs_counters))) ? -EFAULT : 0;
+}
+
+static int sync_manager_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_info_sync_manager sm_info = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ if (args->dcore_id >= HL_MAX_DCORES)
+ return -EINVAL;
+
+ sm_info.first_available_sync_object =
+ prop->first_available_user_sob[args->dcore_id];
+ sm_info.first_available_monitor =
+ prop->first_available_user_mon[args->dcore_id];
+ sm_info.first_available_cq =
+ prop->first_available_cq[args->dcore_id];
+
+ return copy_to_user(out, &sm_info, min_t(size_t, (size_t) max_size,
+ sizeof(sm_info))) ? -EFAULT : 0;
+}
+
+static int total_energy_consumption_info(struct hl_fpriv *hpriv,
+ struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_info_energy total_energy = {0};
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_cpucp_total_energy_get(hdev,
+ &total_energy.total_energy_consumption);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &total_energy,
+ min((size_t) max_size, sizeof(total_energy))) ? -EFAULT : 0;
+}
+
+static int pll_frequency_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_pll_frequency_info freq_info = { {0} };
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_cpucp_pll_info_get(hdev, args->pll_index, freq_info.output);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &freq_info,
+ min((size_t) max_size, sizeof(freq_info))) ? -EFAULT : 0;
+}
+
+static int power_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ struct hl_power_info power_info = {0};
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_cpucp_power_get(hdev, &power_info.power);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &power_info,
+ min((size_t) max_size, sizeof(power_info))) ? -EFAULT : 0;
+}
+
+static int open_stats_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ struct hl_open_stats_info open_stats_info = {0};
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ open_stats_info.last_open_period_ms = jiffies64_to_msecs(
+ hdev->last_open_session_duration_jif);
+ open_stats_info.open_counter = hdev->open_counter;
+ open_stats_info.is_compute_ctx_active = hdev->is_compute_ctx_active;
+ open_stats_info.compute_ctx_in_release = hdev->compute_ctx_in_release;
+
+ return copy_to_user(out, &open_stats_info,
+ min((size_t) max_size, sizeof(open_stats_info))) ? -EFAULT : 0;
+}
+
+static int dram_pending_rows_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ u32 pend_rows_num = 0;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_dram_pending_row_get(hdev, &pend_rows_num);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &pend_rows_num,
+ min_t(size_t, max_size, sizeof(pend_rows_num))) ? -EFAULT : 0;
+}
+
+static int dram_replaced_rows_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ struct cpucp_hbm_row_info info = {0};
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ rc = hl_fw_dram_replaced_row_get(hdev, &info);
+ if (rc)
+ return rc;
+
+ return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
+}
+
+static int last_err_open_dev_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_info_last_err_open_dev_time info = {0};
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ info.timestamp = ktime_to_ns(hdev->last_successful_open_ktime);
+
+ return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
+}
+
+static int cs_timeout_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_info_cs_timeout_event info = {0};
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ info.seq = hdev->captured_err_info.cs_timeout.seq;
+ info.timestamp = ktime_to_ns(hdev->captured_err_info.cs_timeout.timestamp);
+
+ return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
+}
+
+static int razwi_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ struct razwi_info *razwi_info;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ razwi_info = &hdev->captured_err_info.razwi_info;
+ if (!razwi_info->razwi_info_available)
+ return 0;
+
+ return copy_to_user(out, &razwi_info->razwi,
+ min_t(size_t, max_size, sizeof(struct hl_info_razwi_event))) ? -EFAULT : 0;
+}
+
+static int undefined_opcode_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ struct hl_info_undefined_opcode_event info = {0};
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ info.timestamp = ktime_to_ns(hdev->captured_err_info.undef_opcode.timestamp);
+ info.engine_id = hdev->captured_err_info.undef_opcode.engine_id;
+ info.cq_addr = hdev->captured_err_info.undef_opcode.cq_addr;
+ info.cq_size = hdev->captured_err_info.undef_opcode.cq_size;
+ info.stream_id = hdev->captured_err_info.undef_opcode.stream_id;
+ info.cb_addr_streams_len = hdev->captured_err_info.undef_opcode.cb_addr_streams_len;
+ memcpy(info.cb_addr_streams, hdev->captured_err_info.undef_opcode.cb_addr_streams,
+ sizeof(info.cb_addr_streams));
+
+ return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
+}
+
+static int dev_mem_alloc_page_sizes_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_info_dev_memalloc_page_sizes info = {0};
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ /*
+ * Future ASICs that will support multiple DRAM page sizes will support only "powers of 2"
+ * pages (unlike some of the ASICs before supporting multiple page sizes).
+ * For this reason for all ASICs that not support multiple page size the function will
+ * return an empty bitmask indicating that multiple page sizes is not supported.
+ */
+ info.page_order_bitmask = hdev->asic_prop.dmmu.supported_pages_mask;
+
+ return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
+}
+
+static int sec_attest_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct cpucp_sec_attest_info *sec_attest_info;
+ struct hl_info_sec_attest *info;
+ u32 max_size = args->return_size;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ sec_attest_info = kmalloc(sizeof(*sec_attest_info), GFP_KERNEL);
+ if (!sec_attest_info)
+ return -ENOMEM;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ rc = -ENOMEM;
+ goto free_sec_attest_info;
+ }
+
+ rc = hl_fw_get_sec_attest_info(hpriv->hdev, sec_attest_info, args->sec_attest_nonce);
+ if (rc)
+ goto free_info;
+
+ info->nonce = le32_to_cpu(sec_attest_info->nonce);
+ info->pcr_quote_len = le16_to_cpu(sec_attest_info->pcr_quote_len);
+ info->pub_data_len = le16_to_cpu(sec_attest_info->pub_data_len);
+ info->certificate_len = le16_to_cpu(sec_attest_info->certificate_len);
+ info->pcr_num_reg = sec_attest_info->pcr_num_reg;
+ info->pcr_reg_len = sec_attest_info->pcr_reg_len;
+ info->quote_sig_len = sec_attest_info->quote_sig_len;
+ memcpy(&info->pcr_data, &sec_attest_info->pcr_data, sizeof(info->pcr_data));
+ memcpy(&info->pcr_quote, &sec_attest_info->pcr_quote, sizeof(info->pcr_quote));
+ memcpy(&info->public_data, &sec_attest_info->public_data, sizeof(info->public_data));
+ memcpy(&info->certificate, &sec_attest_info->certificate, sizeof(info->certificate));
+ memcpy(&info->quote_sig, &sec_attest_info->quote_sig, sizeof(info->quote_sig));
+
+ rc = copy_to_user(out, info,
+ min_t(size_t, max_size, sizeof(*info))) ? -EFAULT : 0;
+
+free_info:
+ kfree(info);
+free_sec_attest_info:
+ kfree(sec_attest_info);
+
+ return rc;
+}
+
+static int dev_info_signed(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct cpucp_dev_info_signed *dev_info_signed;
+ struct hl_info_signed *info;
+ u32 max_size = args->return_size;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ dev_info_signed = kzalloc(sizeof(*dev_info_signed), GFP_KERNEL);
+ if (!dev_info_signed)
+ return -ENOMEM;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ rc = -ENOMEM;
+ goto free_dev_info_signed;
+ }
+
+ rc = hl_fw_get_dev_info_signed(hpriv->hdev,
+ dev_info_signed, args->sec_attest_nonce);
+ if (rc)
+ goto free_info;
+
+ info->nonce = le32_to_cpu(dev_info_signed->nonce);
+ info->info_sig_len = dev_info_signed->info_sig_len;
+ info->pub_data_len = le16_to_cpu(dev_info_signed->pub_data_len);
+ info->certificate_len = le16_to_cpu(dev_info_signed->certificate_len);
+ info->dev_info_len = sizeof(struct cpucp_info);
+ memcpy(&info->info_sig, &dev_info_signed->info_sig, sizeof(info->info_sig));
+ memcpy(&info->public_data, &dev_info_signed->public_data, sizeof(info->public_data));
+ memcpy(&info->certificate, &dev_info_signed->certificate, sizeof(info->certificate));
+ memcpy(&info->dev_info, &dev_info_signed->info, info->dev_info_len);
+
+ rc = copy_to_user(out, info, min_t(size_t, max_size, sizeof(*info))) ? -EFAULT : 0;
+
+free_info:
+ kfree(info);
+free_dev_info_signed:
+ kfree(dev_info_signed);
+
+ return rc;
+}
+
+
+static int eventfd_register(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ int rc;
+
+ /* check if there is already a registered on that process */
+ mutex_lock(&hpriv->notifier_event.lock);
+ if (hpriv->notifier_event.eventfd) {
+ mutex_unlock(&hpriv->notifier_event.lock);
+ return -EINVAL;
+ }
+
+ hpriv->notifier_event.eventfd = eventfd_ctx_fdget(args->eventfd);
+ if (IS_ERR(hpriv->notifier_event.eventfd)) {
+ rc = PTR_ERR(hpriv->notifier_event.eventfd);
+ hpriv->notifier_event.eventfd = NULL;
+ mutex_unlock(&hpriv->notifier_event.lock);
+ return rc;
+ }
+
+ mutex_unlock(&hpriv->notifier_event.lock);
+ return 0;
+}
+
+static int eventfd_unregister(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ mutex_lock(&hpriv->notifier_event.lock);
+ if (!hpriv->notifier_event.eventfd) {
+ mutex_unlock(&hpriv->notifier_event.lock);
+ return -EINVAL;
+ }
+
+ eventfd_ctx_put(hpriv->notifier_event.eventfd);
+ hpriv->notifier_event.eventfd = NULL;
+ mutex_unlock(&hpriv->notifier_event.lock);
+ return 0;
+}
+
+static int engine_status_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ u32 status_buf_size = args->return_size;
+ struct hl_device *hdev = hpriv->hdev;
+ struct engines_data eng_data;
+ int rc;
+
+ if ((status_buf_size < SZ_1K) || (status_buf_size > HL_ENGINES_DATA_MAX_SIZE) || (!out))
+ return -EINVAL;
+
+ eng_data.actual_size = 0;
+ eng_data.allocated_buf_size = status_buf_size;
+ eng_data.buf = vmalloc(status_buf_size);
+ if (!eng_data.buf)
+ return -ENOMEM;
+
+ hdev->asic_funcs->is_device_idle(hdev, NULL, 0, &eng_data);
+
+ if (eng_data.actual_size > eng_data.allocated_buf_size) {
+ dev_err(hdev->dev,
+ "Engines data size (%d Bytes) is bigger than allocated size (%u Bytes)\n",
+ eng_data.actual_size, status_buf_size);
+ vfree(eng_data.buf);
+ return -ENOMEM;
+ }
+
+ args->user_buffer_actual_size = eng_data.actual_size;
+ rc = copy_to_user(out, eng_data.buf, min_t(size_t, status_buf_size, eng_data.actual_size)) ?
+ -EFAULT : 0;
+
+ vfree(eng_data.buf);
+
+ return rc;
+}
+
+static int page_fault_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_device *hdev = hpriv->hdev;
+ u32 max_size = args->return_size;
+ struct page_fault_info *pgf_info;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ pgf_info = &hdev->captured_err_info.page_fault_info;
+ if (!pgf_info->page_fault_info_available)
+ return 0;
+
+ return copy_to_user(out, &pgf_info->page_fault,
+ min_t(size_t, max_size, sizeof(struct hl_page_fault_info))) ? -EFAULT : 0;
+}
+
+static int user_mappings_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ u32 user_buf_size = args->return_size;
+ struct hl_device *hdev = hpriv->hdev;
+ struct page_fault_info *pgf_info;
+ u64 actual_size;
+
+ if (!out)
+ return -EINVAL;
+
+ pgf_info = &hdev->captured_err_info.page_fault_info;
+ if (!pgf_info->page_fault_info_available)
+ return 0;
+
+ args->array_size = pgf_info->num_of_user_mappings;
+
+ actual_size = pgf_info->num_of_user_mappings * sizeof(struct hl_user_mapping);
+ if (user_buf_size < actual_size)
+ return -ENOMEM;
+
+ return copy_to_user(out, pgf_info->user_mappings, actual_size) ? -EFAULT : 0;
+}
+
+static int hw_err_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *user_buf = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_device *hdev = hpriv->hdev;
+ u32 user_buf_size = args->return_size;
+ struct hw_err_info *info;
+ int rc;
+
+ if (!user_buf)
+ return -EINVAL;
+
+ info = &hdev->captured_err_info.hw_err;
+ if (!info->event_info_available)
+ return 0;
+
+ if (user_buf_size < sizeof(struct hl_info_hw_err_event))
+ return -ENOMEM;
+
+ rc = copy_to_user(user_buf, &info->event, sizeof(struct hl_info_hw_err_event));
+ return rc ? -EFAULT : 0;
+}
+
+static int fw_err_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *user_buf = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_device *hdev = hpriv->hdev;
+ u32 user_buf_size = args->return_size;
+ struct fw_err_info *info;
+ int rc;
+
+ if (!user_buf)
+ return -EINVAL;
+
+ info = &hdev->captured_err_info.fw_err;
+ if (!info->event_info_available)
+ return 0;
+
+ if (user_buf_size < sizeof(struct hl_info_fw_err_event))
+ return -ENOMEM;
+
+ rc = copy_to_user(user_buf, &info->event, sizeof(struct hl_info_fw_err_event));
+ return rc ? -EFAULT : 0;
+}
+
+static int engine_err_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *user_buf = (void __user *) (uintptr_t) args->return_pointer;
+ struct hl_device *hdev = hpriv->hdev;
+ u32 user_buf_size = args->return_size;
+ struct engine_err_info *info;
+ int rc;
+
+ if (!user_buf)
+ return -EINVAL;
+
+ info = &hdev->captured_err_info.engine_err;
+ if (!info->event_info_available)
+ return 0;
+
+ if (user_buf_size < sizeof(struct hl_info_engine_err_event))
+ return -ENOMEM;
+
+ rc = copy_to_user(user_buf, &info->event, sizeof(struct hl_info_engine_err_event));
+ return rc ? -EFAULT : 0;
+}
+
+static int send_fw_generic_request(struct hl_device *hdev, struct hl_info_args *info_args)
+{
+ void __user *buff = (void __user *) (uintptr_t) info_args->return_pointer;
+ u32 size = info_args->return_size;
+ dma_addr_t dma_handle;
+ bool need_input_buff;
+ void *fw_buff;
+ int rc = 0;
+
+ switch (info_args->fw_sub_opcode) {
+ case HL_PASSTHROUGH_VERSIONS:
+ need_input_buff = false;
+ break;
+ case HL_GET_ERR_COUNTERS_CMD:
+ need_input_buff = true;
+ break;
+ case HL_GET_P_STATE:
+ need_input_buff = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (size > SZ_1M) {
+ dev_err(hdev->dev, "buffer size cannot exceed 1MB\n");
+ return -EINVAL;
+ }
+
+ fw_buff = hl_cpu_accessible_dma_pool_alloc(hdev, size, &dma_handle);
+ if (!fw_buff)
+ return -ENOMEM;
+
+
+ if (need_input_buff && copy_from_user(fw_buff, buff, size)) {
+ dev_dbg(hdev->dev, "Failed to copy from user FW buff\n");
+ rc = -EFAULT;
+ goto free_buff;
+ }
+
+ rc = hl_fw_send_generic_request(hdev, info_args->fw_sub_opcode, dma_handle, &size);
+ if (rc)
+ goto free_buff;
+
+ if (copy_to_user(buff, fw_buff, min(size, info_args->return_size))) {
+ dev_dbg(hdev->dev, "Failed to copy to user FW generic req output\n");
+ rc = -EFAULT;
+ }
+
+free_buff:
+ hl_cpu_accessible_dma_pool_free(hdev, info_args->return_size, fw_buff);
+
+ return rc;
+}
+
+static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
+ struct device *dev)
+{
+ enum hl_device_status status;
+ struct hl_info_args *args = data;
+ struct hl_device *hdev = hpriv->hdev;
+ int rc;
+
+ if (args->pad) {
+ dev_dbg(hdev->dev, "Padding bytes must be 0\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Information is returned for the following opcodes even if the device
+ * is disabled or in reset.
+ */
+ switch (args->op) {
+ case HL_INFO_HW_IP_INFO:
+ return hw_ip_info(hdev, args);
+
+ case HL_INFO_DEVICE_STATUS:
+ return device_status_info(hdev, args);
+
+ case HL_INFO_RESET_COUNT:
+ return get_reset_count(hdev, args);
+
+ case HL_INFO_HW_EVENTS:
+ return hw_events_info(hdev, false, args);
+
+ case HL_INFO_HW_EVENTS_AGGREGATE:
+ return hw_events_info(hdev, true, args);
+
+ case HL_INFO_CS_COUNTERS:
+ return cs_counters_info(hpriv, args);
+
+ case HL_INFO_CLK_THROTTLE_REASON:
+ return clk_throttle_info(hpriv, args);
+
+ case HL_INFO_SYNC_MANAGER:
+ return sync_manager_info(hpriv, args);
+
+ case HL_INFO_OPEN_STATS:
+ return open_stats_info(hpriv, args);
+
+ case HL_INFO_LAST_ERR_OPEN_DEV_TIME:
+ return last_err_open_dev_info(hpriv, args);
+
+ case HL_INFO_CS_TIMEOUT_EVENT:
+ return cs_timeout_info(hpriv, args);
+
+ case HL_INFO_RAZWI_EVENT:
+ return razwi_info(hpriv, args);
+
+ case HL_INFO_UNDEFINED_OPCODE_EVENT:
+ return undefined_opcode_info(hpriv, args);
+
+ case HL_INFO_DEV_MEM_ALLOC_PAGE_SIZES:
+ return dev_mem_alloc_page_sizes_info(hpriv, args);
+
+ case HL_INFO_GET_EVENTS:
+ return events_info(hpriv, args);
+
+ case HL_INFO_PAGE_FAULT_EVENT:
+ return page_fault_info(hpriv, args);
+
+ case HL_INFO_USER_MAPPINGS:
+ return user_mappings_info(hpriv, args);
+
+ case HL_INFO_UNREGISTER_EVENTFD:
+ return eventfd_unregister(hpriv, args);
+
+ case HL_INFO_HW_ERR_EVENT:
+ return hw_err_info(hpriv, args);
+
+ case HL_INFO_FW_ERR_EVENT:
+ return fw_err_info(hpriv, args);
+
+ case HL_INFO_USER_ENGINE_ERR_EVENT:
+ return engine_err_info(hpriv, args);
+
+ case HL_INFO_DRAM_USAGE:
+ return dram_usage_info(hpriv, args);
+ default:
+ break;
+ }
+
+ if (!hl_device_operational(hdev, &status)) {
+ dev_dbg_ratelimited(dev,
+ "Device is %s. Can't execute INFO IOCTL\n",
+ hdev->status[status]);
+ return -EBUSY;
+ }
+
+ switch (args->op) {
+ case HL_INFO_HW_IDLE:
+ rc = hw_idle(hdev, args);
+ break;
+
+ case HL_INFO_DEVICE_UTILIZATION:
+ rc = device_utilization(hdev, args);
+ break;
+
+ case HL_INFO_CLK_RATE:
+ rc = get_clk_rate(hdev, args);
+ break;
+
+ case HL_INFO_TIME_SYNC:
+ return time_sync_info(hdev, args);
+
+ case HL_INFO_PCI_COUNTERS:
+ return pci_counters_info(hpriv, args);
+
+ case HL_INFO_TOTAL_ENERGY:
+ return total_energy_consumption_info(hpriv, args);
+
+ case HL_INFO_PLL_FREQUENCY:
+ return pll_frequency_info(hpriv, args);
+
+ case HL_INFO_POWER:
+ return power_info(hpriv, args);
+
+
+ case HL_INFO_DRAM_REPLACED_ROWS:
+ return dram_replaced_rows_info(hpriv, args);
+
+ case HL_INFO_DRAM_PENDING_ROWS:
+ return dram_pending_rows_info(hpriv, args);
+
+ case HL_INFO_SECURED_ATTESTATION:
+ return sec_attest_info(hpriv, args);
+
+ case HL_INFO_REGISTER_EVENTFD:
+ return eventfd_register(hpriv, args);
+
+ case HL_INFO_ENGINE_STATUS:
+ return engine_status_info(hpriv, args);
+
+ case HL_INFO_FW_GENERIC_REQ:
+ return send_fw_generic_request(hdev, args);
+
+ case HL_INFO_DEV_SIGNED:
+ return dev_info_signed(hpriv, args);
+
+ default:
+ dev_err(dev, "Invalid request %d\n", args->op);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int hl_info_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
+{
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
+
+ return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev);
+}
+
+static int hl_info_ioctl_control(struct hl_fpriv *hpriv, void *data)
+{
+ struct hl_info_args *args = data;
+
+ switch (args->op) {
+ case HL_INFO_GET_EVENTS:
+ case HL_INFO_UNREGISTER_EVENTFD:
+ case HL_INFO_REGISTER_EVENTFD:
+ return -EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev_ctrl);
+}
+
+int hl_debug_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
+{
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
+ struct hl_device *hdev = hpriv->hdev;
+ struct hl_debug_args *args = data;
+ enum hl_device_status status;
+
+ int rc = 0;
+
+ if (!hl_device_operational(hdev, &status)) {
+ dev_dbg_ratelimited(hdev->dev,
+ "Device is %s. Can't execute DEBUG IOCTL\n",
+ hdev->status[status]);
+ return -EBUSY;
+ }
+
+ switch (args->op) {
+ case HL_DEBUG_OP_ETR:
+ case HL_DEBUG_OP_ETF:
+ case HL_DEBUG_OP_STM:
+ case HL_DEBUG_OP_FUNNEL:
+ case HL_DEBUG_OP_BMON:
+ case HL_DEBUG_OP_SPMU:
+ case HL_DEBUG_OP_TIMESTAMP:
+ if (!hdev->in_debug) {
+ dev_err_ratelimited(hdev->dev,
+ "Rejecting debug configuration request because device not in debug mode\n");
+ return -EFAULT;
+ }
+ args->input_size = min(args->input_size, hl_debug_struct_size[args->op]);
+ rc = debug_coresight(hdev, hpriv->ctx, args);
+ break;
+
+ case HL_DEBUG_OP_SET_MODE:
+ rc = hl_device_set_debug_mode(hdev, hpriv->ctx, (bool) args->enable);
+ break;
+
+ default:
+ dev_err(hdev->dev, "Invalid request %d\n", args->op);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+#define HL_IOCTL_DEF(ioctl, _func) \
+ [_IOC_NR(ioctl) - HL_COMMAND_START] = {.cmd = ioctl, .func = _func}
+
+static const struct hl_ioctl_desc hl_ioctls_control[] = {
+ HL_IOCTL_DEF(DRM_IOCTL_HL_INFO, hl_info_ioctl_control)
+};
+
+static long _hl_ioctl(struct hl_fpriv *hpriv, unsigned int cmd, unsigned long arg,
+ const struct hl_ioctl_desc *ioctl, struct device *dev)
+{
+ unsigned int nr = _IOC_NR(cmd);
+ char stack_kdata[128] = {0};
+ char *kdata = NULL;
+ unsigned int usize, asize;
+ hl_ioctl_t *func;
+ u32 hl_size;
+ int retcode;
+
+ /* Do not trust userspace, use our own definition */
+ func = ioctl->func;
+
+ if (unlikely(!func)) {
+ dev_dbg(dev, "no function\n");
+ retcode = -ENOTTY;
+ goto out_err;
+ }
+
+ hl_size = _IOC_SIZE(ioctl->cmd);
+ usize = asize = _IOC_SIZE(cmd);
+ if (hl_size > asize)
+ asize = hl_size;
+
+ cmd = ioctl->cmd;
+
+ if (cmd & (IOC_IN | IOC_OUT)) {
+ if (asize <= sizeof(stack_kdata)) {
+ kdata = stack_kdata;
+ } else {
+ kdata = kzalloc(asize, GFP_KERNEL);
+ if (!kdata) {
+ retcode = -ENOMEM;
+ goto out_err;
+ }
+ }
+ }
+
+ if (cmd & IOC_IN) {
+ if (copy_from_user(kdata, (void __user *)arg, usize)) {
+ retcode = -EFAULT;
+ goto out_err;
+ }
+ }
+
+ retcode = func(hpriv, kdata);
+
+ if ((cmd & IOC_OUT) && copy_to_user((void __user *)arg, kdata, usize))
+ retcode = -EFAULT;
+
+out_err:
+ if (retcode)
+ dev_dbg_ratelimited(dev,
+ "error in ioctl: pid=%d, comm=\"%s\", cmd=%#010x, nr=%#04x\n",
+ task_pid_nr(current), current->comm, cmd, nr);
+
+ if (kdata != stack_kdata)
+ kfree(kdata);
+
+ return retcode;
+}
+
+long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ struct hl_fpriv *hpriv = filep->private_data;
+ struct hl_device *hdev = hpriv->hdev;
+ const struct hl_ioctl_desc *ioctl = NULL;
+ unsigned int nr = _IOC_NR(cmd);
+
+ if (!hdev) {
+ pr_err_ratelimited("Sending ioctl after device was removed! Please close FD\n");
+ return -ENODEV;
+ }
+
+ if (nr == _IOC_NR(DRM_IOCTL_HL_INFO)) {
+ ioctl = &hl_ioctls_control[nr - HL_COMMAND_START];
+ } else {
+ dev_dbg_ratelimited(hdev->dev_ctrl,
+ "invalid ioctl: pid=%d, comm=\"%s\", cmd=%#010x, nr=%#04x\n",
+ task_pid_nr(current), current->comm, cmd, nr);
+ return -ENOTTY;
+ }
+
+ return _hl_ioctl(hpriv, cmd, arg, ioctl, hdev->dev_ctrl);
+}
diff --git a/drivers/accel/habanalabs/common/hldio.c b/drivers/accel/habanalabs/common/hldio.c
new file mode 100644
index 000000000000..083ae5610875
--- /dev/null
+++ b/drivers/accel/habanalabs/common/hldio.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2024 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+#include "hldio.h"
+#include <generated/uapi/linux/version.h>
+#include <linux/pci-p2pdma.h>
+#include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+
+/*
+ * NVMe Direct I/O implementation for habanalabs driver
+ *
+ * ASSUMPTIONS
+ * ===========
+ * 1. No IOMMU (well, technically it can work with IOMMU, but it is *almost useless).
+ * 2. Only READ operations (can extend in the future).
+ * 3. No sparse files (can overcome this in the future).
+ * 4. Kernel version >= 6.9
+ * 5. Requiring page alignment is OK (I don't see a solution to this one right,
+ * now, how do we read partial pages?)
+ * 6. Kernel compiled with CONFIG_PCI_P2PDMA. This requires a CUSTOM kernel.
+ * Theoretically I have a slight idea on how this could be solvable, but it
+ * is probably inacceptable for the upstream. Also may not work in the end.
+ * 7. Either make sure our cards and disks are under the same PCI bridge, or
+ * compile a custom kernel to hack around this.
+ */
+
+#define IO_STABILIZE_TIMEOUT 10000000 /* 10 seconds in microseconds */
+
+/*
+ * This struct contains all the useful data I could milk out of the file handle
+ * provided by the user.
+ * @TODO: right now it is retrieved on each IO, but can be done once with some
+ * dedicated IOCTL, call it for example HL_REGISTER_HANDLE.
+ */
+struct hl_dio_fd {
+ /* Back pointer in case we need it in async completion */
+ struct hl_ctx *ctx;
+ /* Associated fd struct */
+ struct file *filp;
+};
+
+/*
+ * This is a single IO descriptor
+ */
+struct hl_direct_io {
+ struct hl_dio_fd f;
+ struct kiocb kio;
+ struct bio_vec *bv;
+ struct iov_iter iter;
+ u64 device_va;
+ u64 off_bytes;
+ u64 len_bytes;
+ u32 type;
+};
+
+bool hl_device_supports_nvme(struct hl_device *hdev)
+{
+ return hdev->asic_prop.supports_nvme;
+}
+
+static int hl_dio_fd_register(struct hl_ctx *ctx, int fd, struct hl_dio_fd *f)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct block_device *bd;
+ struct super_block *sb;
+ struct inode *inode;
+ struct gendisk *gd;
+ struct device *disk_dev;
+ int rc;
+
+ f->filp = fget(fd);
+ if (!f->filp) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ if (!(f->filp->f_flags & O_DIRECT)) {
+ dev_err(hdev->dev, "file is not in the direct mode\n");
+ rc = -EINVAL;
+ goto fput;
+ }
+
+ if (!f->filp->f_op->read_iter) {
+ dev_err(hdev->dev, "read iter is not supported, need to fall back to legacy\n");
+ rc = -EINVAL;
+ goto fput;
+ }
+
+ inode = file_inode(f->filp);
+ sb = inode->i_sb;
+ bd = sb->s_bdev;
+ gd = bd->bd_disk;
+
+ if (inode->i_blocks << sb->s_blocksize_bits < i_size_read(inode)) {
+ dev_err(hdev->dev, "sparse files are not currently supported\n");
+ rc = -EINVAL;
+ goto fput;
+ }
+
+ if (!bd || !gd) {
+ dev_err(hdev->dev, "invalid block device\n");
+ rc = -ENODEV;
+ goto fput;
+ }
+ /* Get the underlying device from the block device */
+ disk_dev = disk_to_dev(gd);
+ if (!dma_pci_p2pdma_supported(disk_dev)) {
+ dev_err(hdev->dev, "device does not support PCI P2P DMA\n");
+ rc = -EOPNOTSUPP;
+ goto fput;
+ }
+
+ /*
+ * @TODO: Maybe we need additional checks here
+ */
+
+ f->ctx = ctx;
+ rc = 0;
+
+ goto out;
+fput:
+ fput(f->filp);
+out:
+ return rc;
+}
+
+static void hl_dio_fd_unregister(struct hl_dio_fd *f)
+{
+ fput(f->filp);
+}
+
+static long hl_dio_count_io(struct hl_device *hdev)
+{
+ s64 sum = 0;
+ int i;
+
+ for_each_possible_cpu(i)
+ sum += per_cpu(*hdev->hldio.inflight_ios, i);
+
+ return sum;
+}
+
+static bool hl_dio_get_iopath(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+
+ if (hdev->hldio.io_enabled) {
+ this_cpu_inc(*hdev->hldio.inflight_ios);
+
+ /* Avoid race conditions */
+ if (!hdev->hldio.io_enabled) {
+ this_cpu_dec(*hdev->hldio.inflight_ios);
+ return false;
+ }
+
+ hl_ctx_get(ctx);
+
+ return true;
+ }
+
+ return false;
+}
+
+static void hl_dio_put_iopath(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+
+ hl_ctx_put(ctx);
+ this_cpu_dec(*hdev->hldio.inflight_ios);
+}
+
+static void hl_dio_set_io_enabled(struct hl_device *hdev, bool enabled)
+{
+ hdev->hldio.io_enabled = enabled;
+}
+
+static bool hl_dio_validate_io(struct hl_device *hdev, struct hl_direct_io *io)
+{
+ if ((u64)io->device_va & ~PAGE_MASK) {
+ dev_dbg(hdev->dev, "device address must be 4K aligned\n");
+ return false;
+ }
+
+ if (io->len_bytes & ~PAGE_MASK) {
+ dev_dbg(hdev->dev, "IO length must be 4K aligned\n");
+ return false;
+ }
+
+ if (io->off_bytes & ~PAGE_MASK) {
+ dev_dbg(hdev->dev, "IO offset must be 4K aligned\n");
+ return false;
+ }
+
+ return true;
+}
+
+static struct page *hl_dio_va2page(struct hl_device *hdev, struct hl_ctx *ctx, u64 device_va)
+{
+ struct hl_dio *hldio = &hdev->hldio;
+ u64 device_pa;
+ int rc, i;
+
+ rc = hl_mmu_va_to_pa(ctx, device_va, &device_pa);
+ if (rc) {
+ dev_err(hdev->dev, "device virtual address translation error: %#llx (%d)",
+ device_va, rc);
+ return NULL;
+ }
+
+ for (i = 0 ; i < hldio->np2prs ; ++i) {
+ if (device_pa >= hldio->p2prs[i].device_pa &&
+ device_pa < hldio->p2prs[i].device_pa + hldio->p2prs[i].size)
+ return hldio->p2prs[i].p2ppages[(device_pa - hldio->p2prs[i].device_pa) >>
+ PAGE_SHIFT];
+ }
+
+ return NULL;
+}
+
+static ssize_t hl_direct_io(struct hl_device *hdev, struct hl_direct_io *io)
+{
+ u64 npages, device_va;
+ ssize_t rc;
+ int i;
+
+ if (!hl_dio_validate_io(hdev, io))
+ return -EINVAL;
+
+ if (!hl_dio_get_iopath(io->f.ctx)) {
+ dev_info(hdev->dev, "can't schedule a new IO, IO is disabled\n");
+ return -ESHUTDOWN;
+ }
+
+ init_sync_kiocb(&io->kio, io->f.filp);
+ io->kio.ki_pos = io->off_bytes;
+
+ npages = (io->len_bytes >> PAGE_SHIFT);
+
+ /* @TODO: this can be implemented smarter, vmalloc in iopath is not
+ * ideal. Maybe some variation of genpool. Number of pages may differ
+ * greatly, so maybe even use pools of different sizes and chose the
+ * closest one.
+ */
+ io->bv = vzalloc(npages * sizeof(struct bio_vec));
+ if (!io->bv)
+ return -ENOMEM;
+
+ for (i = 0, device_va = io->device_va; i < npages ; ++i, device_va += PAGE_SIZE) {
+ io->bv[i].bv_page = hl_dio_va2page(hdev, io->f.ctx, device_va);
+ if (!io->bv[i].bv_page) {
+ dev_err(hdev->dev, "error getting page struct for device va %#llx",
+ device_va);
+ rc = -EFAULT;
+ goto cleanup;
+ }
+ io->bv[i].bv_offset = 0;
+ io->bv[i].bv_len = PAGE_SIZE;
+ }
+
+ iov_iter_bvec(&io->iter, io->type, io->bv, 1, io->len_bytes);
+ if (io->f.filp->f_op && io->f.filp->f_op->read_iter)
+ rc = io->f.filp->f_op->read_iter(&io->kio, &io->iter);
+ else
+ rc = -EINVAL;
+
+cleanup:
+ vfree(io->bv);
+ hl_dio_put_iopath(io->f.ctx);
+
+ dev_dbg(hdev->dev, "IO ended with %ld\n", rc);
+
+ return rc;
+}
+
+/*
+ * @TODO: This function can be used as a callback for io completion under
+ * kio->ki_complete in order to implement async IO.
+ * Note that on more recent kernels there is no ret2.
+ */
+__maybe_unused static void hl_direct_io_complete(struct kiocb *kio, long ret, long ret2)
+{
+ struct hl_direct_io *io = container_of(kio, struct hl_direct_io, kio);
+
+ dev_dbg(io->f.ctx->hdev->dev, "IO completed with %ld\n", ret);
+
+ /* Do something to copy result to user / notify completion */
+
+ hl_dio_put_iopath(io->f.ctx);
+
+ hl_dio_fd_unregister(&io->f);
+}
+
+/*
+ * DMA disk to ASIC, wait for results. Must be invoked from the user context
+ */
+int hl_dio_ssd2hl(struct hl_device *hdev, struct hl_ctx *ctx, int fd,
+ u64 device_va, off_t off_bytes, size_t len_bytes,
+ size_t *len_read)
+{
+ struct hl_direct_io *io;
+ ssize_t rc;
+
+ dev_dbg(hdev->dev, "SSD2HL fd=%d va=%#llx len=%#lx\n", fd, device_va, len_bytes);
+
+ io = kzalloc(sizeof(*io), GFP_KERNEL);
+ if (!io) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ *io = (struct hl_direct_io){
+ .device_va = device_va,
+ .len_bytes = len_bytes,
+ .off_bytes = off_bytes,
+ .type = READ,
+ };
+
+ rc = hl_dio_fd_register(ctx, fd, &io->f);
+ if (rc)
+ goto kfree_io;
+
+ rc = hl_direct_io(hdev, io);
+ if (rc >= 0) {
+ *len_read = rc;
+ rc = 0;
+ }
+
+ /* This shall be called only in the case of a sync IO */
+ hl_dio_fd_unregister(&io->f);
+kfree_io:
+ kfree(io);
+out:
+ return rc;
+}
+
+static void hl_p2p_region_fini(struct hl_device *hdev, struct hl_p2p_region *p2pr)
+{
+ if (p2pr->p2ppages) {
+ vfree(p2pr->p2ppages);
+ p2pr->p2ppages = NULL;
+ }
+
+ if (p2pr->p2pmem) {
+ dev_dbg(hdev->dev, "freeing P2P mem from %p, size=%#llx\n",
+ p2pr->p2pmem, p2pr->size);
+ pci_free_p2pmem(hdev->pdev, p2pr->p2pmem, p2pr->size);
+ p2pr->p2pmem = NULL;
+ }
+}
+
+void hl_p2p_region_fini_all(struct hl_device *hdev)
+{
+ int i;
+
+ for (i = 0 ; i < hdev->hldio.np2prs ; ++i)
+ hl_p2p_region_fini(hdev, &hdev->hldio.p2prs[i]);
+
+ kvfree(hdev->hldio.p2prs);
+ hdev->hldio.p2prs = NULL;
+ hdev->hldio.np2prs = 0;
+}
+
+int hl_p2p_region_init(struct hl_device *hdev, struct hl_p2p_region *p2pr)
+{
+ void *addr;
+ int rc, i;
+
+ /* Start by publishing our p2p memory */
+ rc = pci_p2pdma_add_resource(hdev->pdev, p2pr->bar, p2pr->size, p2pr->bar_offset);
+ if (rc) {
+ dev_err(hdev->dev, "error adding p2p resource: %d\n", rc);
+ goto err;
+ }
+
+ /* Alloc all p2p mem */
+ p2pr->p2pmem = pci_alloc_p2pmem(hdev->pdev, p2pr->size);
+ if (!p2pr->p2pmem) {
+ dev_err(hdev->dev, "error allocating p2p memory\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ p2pr->p2ppages = vmalloc((p2pr->size >> PAGE_SHIFT) * sizeof(struct page *));
+ if (!p2pr->p2ppages) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0, addr = p2pr->p2pmem ; i < (p2pr->size >> PAGE_SHIFT) ; ++i, addr += PAGE_SIZE) {
+ p2pr->p2ppages[i] = virt_to_page(addr);
+ if (!p2pr->p2ppages[i]) {
+ rc = -EFAULT;
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ hl_p2p_region_fini(hdev, p2pr);
+ return rc;
+}
+
+int hl_dio_start(struct hl_device *hdev)
+{
+ dev_dbg(hdev->dev, "initializing HLDIO\n");
+
+ /* Initialize the IO counter and enable IO */
+ hdev->hldio.inflight_ios = alloc_percpu(s64);
+ if (!hdev->hldio.inflight_ios)
+ return -ENOMEM;
+
+ hl_dio_set_io_enabled(hdev, true);
+
+ return 0;
+}
+
+void hl_dio_stop(struct hl_device *hdev)
+{
+ dev_dbg(hdev->dev, "deinitializing HLDIO\n");
+
+ if (hdev->hldio.io_enabled) {
+ /* Wait for all the IO to finish */
+ hl_dio_set_io_enabled(hdev, false);
+ hl_poll_timeout_condition(hdev, !hl_dio_count_io(hdev), 1000, IO_STABILIZE_TIMEOUT);
+ }
+
+ if (hdev->hldio.inflight_ios) {
+ free_percpu(hdev->hldio.inflight_ios);
+ hdev->hldio.inflight_ios = NULL;
+ }
+}
diff --git a/drivers/accel/habanalabs/common/hldio.h b/drivers/accel/habanalabs/common/hldio.h
new file mode 100644
index 000000000000..2874388f2851
--- /dev/null
+++ b/drivers/accel/habanalabs/common/hldio.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * hldio.h - NVMe Direct I/O (HLDIO) infrastructure for Habana Labs Driver
+ *
+ * This feature requires specific hardware setup and must not be built
+ * under COMPILE_TEST.
+ */
+
+#ifndef __HL_HLDIO_H__
+#define __HL_HLDIO_H__
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/ktime.h> /* ktime functions */
+#include <linux/delay.h> /* usleep_range */
+#include <linux/kernel.h> /* might_sleep_if */
+#include <linux/errno.h> /* error codes */
+
+/* Forward declarations */
+struct hl_device;
+struct file;
+
+/* Enable only if Kconfig selected */
+#ifdef CONFIG_HL_HLDIO
+/**
+ * struct hl_p2p_region - describes a single P2P memory region
+ * @p2ppages: array of page structs for the P2P memory
+ * @p2pmem: virtual address of the P2P memory region
+ * @device_pa: physical address on the device
+ * @bar_offset: offset within the BAR
+ * @size: size of the region in bytes
+ * @bar: BAR number containing this region
+ */
+struct hl_p2p_region {
+ struct page **p2ppages;
+ void *p2pmem;
+ u64 device_pa;
+ u64 bar_offset;
+ u64 size;
+ int bar;
+};
+
+/**
+ * struct hl_dio_stats - Direct I/O statistics
+ * @total_ops: total number of operations attempted
+ * @successful_ops: number of successful operations
+ * @failed_ops: number of failed operations
+ * @bytes_transferred: total bytes successfully transferred
+ * @last_len_read: length of the last read operation
+ */
+struct hl_dio_stats {
+ u64 total_ops;
+ u64 successful_ops;
+ u64 failed_ops;
+ u64 bytes_transferred;
+ size_t last_len_read;
+};
+
+/**
+ * struct hl_dio - describes habanalabs direct storage interaction interface
+ * @p2prs: array of p2p regions
+ * @inflight_ios: percpu counter for inflight ios
+ * @np2prs: number of elements in p2prs
+ * @io_enabled: 1 if io is enabled 0 otherwise
+ */
+struct hl_dio {
+ struct hl_p2p_region *p2prs;
+ s64 __percpu *inflight_ios;
+ u8 np2prs;
+ u8 io_enabled;
+};
+
+int hl_dio_ssd2hl(struct hl_device *hdev, struct hl_ctx *ctx, int fd,
+ u64 device_va, off_t off_bytes, size_t len_bytes,
+ size_t *len_read);
+void hl_p2p_region_fini_all(struct hl_device *hdev);
+int hl_p2p_region_init(struct hl_device *hdev, struct hl_p2p_region *p2pr);
+int hl_dio_start(struct hl_device *hdev);
+void hl_dio_stop(struct hl_device *hdev);
+
+/* Init/teardown */
+int hl_hldio_init(struct hl_device *hdev);
+void hl_hldio_fini(struct hl_device *hdev);
+
+/* File operations */
+long hl_hldio_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+
+/* DebugFS hooks */
+#ifdef CONFIG_DEBUG_FS
+void hl_hldio_debugfs_init(struct hl_device *hdev);
+void hl_hldio_debugfs_fini(struct hl_device *hdev);
+#else
+static inline void hl_hldio_debugfs_init(struct hl_device *hdev) { }
+static inline void hl_hldio_debugfs_fini(struct hl_device *hdev) { }
+#endif
+
+#else /* !CONFIG_HL_HLDIO */
+
+struct hl_p2p_region;
+/* Stubs when HLDIO is disabled */
+static inline int hl_dio_ssd2hl(struct hl_device *hdev, struct hl_ctx *ctx, int fd,
+ u64 device_va, off_t off_bytes, size_t len_bytes,
+ size_t *len_read)
+{ return -EOPNOTSUPP; }
+static inline void hl_p2p_region_fini_all(struct hl_device *hdev) {}
+static inline int hl_p2p_region_init(struct hl_device *hdev, struct hl_p2p_region *p2pr)
+{ return -EOPNOTSUPP; }
+static inline int hl_dio_start(struct hl_device *hdev) { return -EOPNOTSUPP; }
+static inline void hl_dio_stop(struct hl_device *hdev) {}
+
+static inline int hl_hldio_init(struct hl_device *hdev) { return 0; }
+static inline void hl_hldio_fini(struct hl_device *hdev) { }
+static inline long hl_hldio_ioctl(struct file *f, unsigned int c,
+ unsigned long a)
+{ return -ENOTTY; }
+static inline void hl_hldio_debugfs_init(struct hl_device *hdev) { }
+static inline void hl_hldio_debugfs_fini(struct hl_device *hdev) { }
+
+#endif /* CONFIG_HL_HLDIO */
+
+/* Simplified polling macro for HLDIO (no simulator support) */
+#define hl_poll_timeout_condition(hdev, cond, sleep_us, timeout_us) \
+({ \
+ ktime_t __timeout = ktime_add_us(ktime_get(), timeout_us); \
+ might_sleep_if(sleep_us); \
+ (void)(hdev); /* keep signature consistent, hdev unused */ \
+ for (;;) { \
+ mb(); /* ensure ordering of memory operations */ \
+ if (cond) \
+ break; \
+ if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) \
+ break; \
+ if (sleep_us) \
+ usleep_range((sleep_us >> 2) + 1, sleep_us); \
+ } \
+ (cond) ? 0 : -ETIMEDOUT; \
+})
+
+#ifdef CONFIG_HL_HLDIO
+bool hl_device_supports_nvme(struct hl_device *hdev);
+#else
+static inline bool hl_device_supports_nvme(struct hl_device *hdev) { return false; }
+#endif
+
+#endif /* __HL_HLDIO_H__ */
diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/accel/habanalabs/common/hw_queue.c
index 3f15ab9d827f..3d04a7507cce 100644
--- a/drivers/misc/habanalabs/common/hw_queue.c
+++ b/drivers/accel/habanalabs/common/hw_queue.c
@@ -84,6 +84,8 @@ void hl_hw_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q,
u32 ctl, u32 len, u64 ptr)
{
struct hl_bd *bd;
+ u64 addr;
+ int i;
bd = q->kernel_address;
bd += hl_pi_2_offset(q->pi);
@@ -91,7 +93,16 @@ void hl_hw_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q,
bd->len = cpu_to_le32(len);
bd->ptr = cpu_to_le64(ptr);
+ if (q->dram_bd)
+ for (i = 0 ; i < 2 ; i++) {
+ addr = q->pq_dram_address +
+ ((hl_pi_2_offset(q->pi) * sizeof(struct hl_bd)) + (i * sizeof(u64)));
+ hdev->asic_funcs->access_dev_mem(hdev, PCI_REGION_DRAM, addr,
+ (u64 *)(bd) + i, DEBUGFS_WRITE64);
+ }
+
q->pi = hl_queue_inc_ptr(q->pi);
+
hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi);
}
@@ -826,9 +837,7 @@ static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
q->kernel_address = p;
- q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH,
- sizeof(*q->shadow_queue),
- GFP_KERNEL);
+ q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH, sizeof(struct hl_cs_job *), GFP_KERNEL);
if (!q->shadow_queue) {
dev_err(hdev->dev,
"Failed to allocate shadow queue for H/W queue %d\n",
@@ -1089,12 +1098,18 @@ int hl_hw_queues_create(struct hl_device *hdev)
q->supports_sync_stream =
asic->hw_queues_props[i].supports_sync_stream;
q->collective_mode = asic->hw_queues_props[i].collective_mode;
+ q->dram_bd = asic->hw_queues_props[i].dram_bd;
+
rc = queue_init(hdev, q, i);
if (rc) {
dev_err(hdev->dev,
"failed to initialize queue %d\n", i);
goto release_queues;
}
+
+ /* Set DRAM PQ address for the queue if it should be at DRAM */
+ if (q->dram_bd)
+ q->pq_dram_address = asic->hw_queues_props[i].q_dram_bd_address;
}
return 0;
diff --git a/drivers/accel/habanalabs/common/hwmon.c b/drivers/accel/habanalabs/common/hwmon.c
new file mode 100644
index 000000000000..52d1e6bf10dc
--- /dev/null
+++ b/drivers/accel/habanalabs/common/hwmon.c
@@ -0,0 +1,931 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2019 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+
+#define HWMON_NR_SENSOR_TYPES (hwmon_max)
+
+#ifdef _HAS_HWMON_HWMON_T_ENABLE
+
+static u32 fixup_flags_legacy_fw(struct hl_device *hdev, enum hwmon_sensor_types type,
+ u32 cpucp_flags)
+{
+ u32 flags;
+
+ switch (type) {
+ case hwmon_temp:
+ flags = (cpucp_flags << 1) | HWMON_T_ENABLE;
+ break;
+
+ case hwmon_in:
+ flags = (cpucp_flags << 1) | HWMON_I_ENABLE;
+ break;
+
+ case hwmon_curr:
+ flags = (cpucp_flags << 1) | HWMON_C_ENABLE;
+ break;
+
+ case hwmon_fan:
+ flags = (cpucp_flags << 1) | HWMON_F_ENABLE;
+ break;
+
+ case hwmon_power:
+ flags = (cpucp_flags << 1) | HWMON_P_ENABLE;
+ break;
+
+ case hwmon_pwm:
+ /* enable bit was here from day 1, so no need to adjust */
+ flags = cpucp_flags;
+ break;
+
+ default:
+ dev_err_ratelimited(hdev->dev, "unsupported h/w sensor type %d\n", type);
+ flags = cpucp_flags;
+ break;
+ }
+
+ return flags;
+}
+
+static u32 fixup_attr_legacy_fw(u32 attr)
+{
+ return (attr - 1);
+}
+
+#else
+
+static u32 fixup_flags_legacy_fw(struct hl_device *hdev, enum hwmon_sensor_types type,
+ u32 cpucp_flags)
+{
+ return cpucp_flags;
+}
+
+static u32 fixup_attr_legacy_fw(u32 attr)
+{
+ return attr;
+}
+
+#endif /* !_HAS_HWMON_HWMON_T_ENABLE */
+
+static u32 adjust_hwmon_flags(struct hl_device *hdev, enum hwmon_sensor_types type, u32 cpucp_flags)
+{
+ u32 flags, cpucp_input_val;
+ bool use_cpucp_enum;
+
+ use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
+ CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false;
+
+ /* If f/w is using it's own enum, we need to check if the properties values are aligned.
+ * If not, it means we need to adjust the values to the new format that is used in the
+ * kernel since 5.6 (enum values were incremented by 1 by adding a new enable value).
+ */
+ if (use_cpucp_enum) {
+ switch (type) {
+ case hwmon_temp:
+ cpucp_input_val = cpucp_temp_input;
+ if (cpucp_input_val == hwmon_temp_input)
+ flags = cpucp_flags;
+ else
+ flags = (cpucp_flags << 1) | HWMON_T_ENABLE;
+ break;
+
+ case hwmon_in:
+ cpucp_input_val = cpucp_in_input;
+ if (cpucp_input_val == hwmon_in_input)
+ flags = cpucp_flags;
+ else
+ flags = (cpucp_flags << 1) | HWMON_I_ENABLE;
+ break;
+
+ case hwmon_curr:
+ cpucp_input_val = cpucp_curr_input;
+ if (cpucp_input_val == hwmon_curr_input)
+ flags = cpucp_flags;
+ else
+ flags = (cpucp_flags << 1) | HWMON_C_ENABLE;
+ break;
+
+ case hwmon_fan:
+ cpucp_input_val = cpucp_fan_input;
+ if (cpucp_input_val == hwmon_fan_input)
+ flags = cpucp_flags;
+ else
+ flags = (cpucp_flags << 1) | HWMON_F_ENABLE;
+ break;
+
+ case hwmon_pwm:
+ /* enable bit was here from day 1, so no need to adjust */
+ flags = cpucp_flags;
+ break;
+
+ case hwmon_power:
+ cpucp_input_val = CPUCP_POWER_INPUT;
+ if (cpucp_input_val == hwmon_power_input)
+ flags = cpucp_flags;
+ else
+ flags = (cpucp_flags << 1) | HWMON_P_ENABLE;
+ break;
+
+ default:
+ dev_err_ratelimited(hdev->dev, "unsupported h/w sensor type %d\n", type);
+ flags = cpucp_flags;
+ break;
+ }
+ } else {
+ flags = fixup_flags_legacy_fw(hdev, type, cpucp_flags);
+ }
+
+ return flags;
+}
+
+int hl_build_hwmon_channel_info(struct hl_device *hdev, struct cpucp_sensor *sensors_arr)
+{
+ u32 num_sensors_for_type, flags, num_active_sensor_types = 0, arr_size = 0, *curr_arr;
+ u32 sensors_by_type_next_index[HWMON_NR_SENSOR_TYPES] = {0};
+ u32 *sensors_by_type[HWMON_NR_SENSOR_TYPES] = {NULL};
+ struct hwmon_channel_info **channels_info;
+ u32 counts[HWMON_NR_SENSOR_TYPES] = {0};
+ enum hwmon_sensor_types type;
+ int rc, i, j;
+
+ for (i = 0 ; i < CPUCP_MAX_SENSORS ; i++) {
+ type = le32_to_cpu(sensors_arr[i].type);
+
+ if ((type == 0) && (sensors_arr[i].flags == 0))
+ break;
+
+ if (type >= HWMON_NR_SENSOR_TYPES) {
+ dev_err_ratelimited(hdev->dev,
+ "Got wrong sensor type %d from device\n", type);
+ return -EINVAL;
+ }
+
+ counts[type]++;
+ arr_size++;
+ }
+
+ for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) {
+ if (counts[i] == 0)
+ continue;
+
+ num_sensors_for_type = counts[i] + 1;
+ dev_dbg(hdev->dev, "num_sensors_for_type %d = %d\n", i, num_sensors_for_type);
+
+ curr_arr = kcalloc(num_sensors_for_type, sizeof(*curr_arr), GFP_KERNEL);
+ if (!curr_arr) {
+ rc = -ENOMEM;
+ goto sensors_type_err;
+ }
+
+ num_active_sensor_types++;
+ sensors_by_type[i] = curr_arr;
+ }
+
+ for (i = 0 ; i < arr_size ; i++) {
+ type = le32_to_cpu(sensors_arr[i].type);
+ curr_arr = sensors_by_type[type];
+ flags = adjust_hwmon_flags(hdev, type, le32_to_cpu(sensors_arr[i].flags));
+ curr_arr[sensors_by_type_next_index[type]++] = flags;
+ }
+
+ channels_info = kcalloc(num_active_sensor_types + 1, sizeof(struct hwmon_channel_info *),
+ GFP_KERNEL);
+ if (!channels_info) {
+ rc = -ENOMEM;
+ goto channels_info_array_err;
+ }
+
+ for (i = 0 ; i < num_active_sensor_types ; i++) {
+ channels_info[i] = kzalloc(sizeof(*channels_info[i]), GFP_KERNEL);
+ if (!channels_info[i]) {
+ rc = -ENOMEM;
+ goto channel_info_err;
+ }
+ }
+
+ for (i = 0, j = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) {
+ if (!sensors_by_type[i])
+ continue;
+
+ channels_info[j]->type = i;
+ channels_info[j]->config = sensors_by_type[i];
+ j++;
+ }
+
+ hdev->hl_chip_info->info = (const struct hwmon_channel_info **)channels_info;
+
+ return 0;
+
+channel_info_err:
+ for (i = 0 ; i < num_active_sensor_types ; i++) {
+ if (channels_info[i]) {
+ kfree(channels_info[i]->config);
+ kfree(channels_info[i]);
+ }
+ }
+ kfree(channels_info);
+
+channels_info_array_err:
+sensors_type_err:
+ for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++)
+ kfree(sensors_by_type[i]);
+
+ return rc;
+}
+
+static int hl_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ bool use_cpucp_enum;
+ u32 cpucp_attr;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
+ CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ cpucp_attr = cpucp_temp_input;
+ break;
+ case hwmon_temp_max:
+ cpucp_attr = cpucp_temp_max;
+ break;
+ case hwmon_temp_crit:
+ cpucp_attr = cpucp_temp_crit;
+ break;
+ case hwmon_temp_max_hyst:
+ cpucp_attr = cpucp_temp_max_hyst;
+ break;
+ case hwmon_temp_crit_hyst:
+ cpucp_attr = cpucp_temp_crit_hyst;
+ break;
+ case hwmon_temp_offset:
+ cpucp_attr = cpucp_temp_offset;
+ break;
+ case hwmon_temp_highest:
+ cpucp_attr = cpucp_temp_highest;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ rc = hl_get_temperature(hdev, channel, cpucp_attr, val);
+ else
+ rc = hl_get_temperature(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ cpucp_attr = cpucp_in_input;
+ break;
+ case hwmon_in_min:
+ cpucp_attr = cpucp_in_min;
+ break;
+ case hwmon_in_max:
+ cpucp_attr = cpucp_in_max;
+ break;
+ case hwmon_in_highest:
+ cpucp_attr = cpucp_in_highest;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ rc = hl_get_voltage(hdev, channel, cpucp_attr, val);
+ else
+ rc = hl_get_voltage(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ cpucp_attr = cpucp_curr_input;
+ break;
+ case hwmon_curr_min:
+ cpucp_attr = cpucp_curr_min;
+ break;
+ case hwmon_curr_max:
+ cpucp_attr = cpucp_curr_max;
+ break;
+ case hwmon_curr_highest:
+ cpucp_attr = cpucp_curr_highest;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ rc = hl_get_current(hdev, channel, cpucp_attr, val);
+ else
+ rc = hl_get_current(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ cpucp_attr = cpucp_fan_input;
+ break;
+ case hwmon_fan_min:
+ cpucp_attr = cpucp_fan_min;
+ break;
+ case hwmon_fan_max:
+ cpucp_attr = cpucp_fan_max;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ rc = hl_get_fan_speed(hdev, channel, cpucp_attr, val);
+ else
+ rc = hl_get_fan_speed(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ cpucp_attr = cpucp_pwm_input;
+ break;
+ case hwmon_pwm_enable:
+ cpucp_attr = cpucp_pwm_enable;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ rc = hl_get_pwm_info(hdev, channel, cpucp_attr, val);
+ else
+ /* no need for fixup as pwm was aligned from day 1 */
+ rc = hl_get_pwm_info(hdev, channel, attr, val);
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ cpucp_attr = CPUCP_POWER_INPUT;
+ break;
+ case hwmon_power_input_highest:
+ cpucp_attr = CPUCP_POWER_INPUT_HIGHEST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ rc = hl_get_power(hdev, channel, cpucp_attr, val);
+ else
+ rc = hl_get_power(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int hl_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ u32 cpucp_attr;
+ bool use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
+ CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_offset:
+ cpucp_attr = cpucp_temp_offset;
+ break;
+ case hwmon_temp_reset_history:
+ cpucp_attr = cpucp_temp_reset_history;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ hl_set_temperature(hdev, channel, cpucp_attr, val);
+ else
+ hl_set_temperature(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ cpucp_attr = cpucp_pwm_input;
+ break;
+ case hwmon_pwm_enable:
+ cpucp_attr = cpucp_pwm_enable;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ hl_set_pwm_info(hdev, channel, cpucp_attr, val);
+ else
+ /* no need for fixup as pwm was aligned from day 1 */
+ hl_set_pwm_info(hdev, channel, attr, val);
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_reset_history:
+ cpucp_attr = cpucp_in_reset_history;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ hl_set_voltage(hdev, channel, cpucp_attr, val);
+ else
+ hl_set_voltage(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_reset_history:
+ cpucp_attr = cpucp_curr_reset_history;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ hl_set_current(hdev, channel, cpucp_attr, val);
+ else
+ hl_set_current(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_reset_history:
+ cpucp_attr = CPUCP_POWER_RESET_INPUT_HISTORY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_cpucp_enum)
+ hl_set_power(hdev, channel, cpucp_attr, val);
+ else
+ hl_set_power(hdev, channel, fixup_attr_legacy_fw(attr), val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static umode_t hl_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_max:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_crit:
+ case hwmon_temp_crit_hyst:
+ case hwmon_temp_highest:
+ return 0444;
+ case hwmon_temp_offset:
+ return 0644;
+ case hwmon_temp_reset_history:
+ return 0200;
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_min:
+ case hwmon_in_max:
+ case hwmon_in_highest:
+ return 0444;
+ case hwmon_in_reset_history:
+ return 0200;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_min:
+ case hwmon_curr_max:
+ case hwmon_curr_highest:
+ return 0444;
+ case hwmon_curr_reset_history:
+ return 0200;
+ }
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_min:
+ case hwmon_fan_max:
+ return 0444;
+ }
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ return 0644;
+ }
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ case hwmon_power_input_highest:
+ return 0444;
+ case hwmon_power_reset_history:
+ return 0200;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_ops hl_hwmon_ops = {
+ .is_visible = hl_is_visible,
+ .read = hl_read,
+ .write = hl_write
+};
+
+int hl_get_temperature(struct hl_device *hdev,
+ int sensor_index, u32 attr, long *value)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEMPERATURE_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, &result);
+
+ *value = (long) result;
+
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to get temperature from sensor %d, error %d\n",
+ sensor_index, rc);
+ *value = 0;
+ }
+
+ return rc;
+}
+
+int hl_set_temperature(struct hl_device *hdev,
+ int sensor_index, u32 attr, long value)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEMPERATURE_SET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+ pkt.value = __cpu_to_le64(value);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to set temperature of sensor %d, error %d\n",
+ sensor_index, rc);
+
+ return rc;
+}
+
+int hl_get_voltage(struct hl_device *hdev,
+ int sensor_index, u32 attr, long *value)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_VOLTAGE_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, &result);
+
+ *value = (long) result;
+
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to get voltage from sensor %d, error %d\n",
+ sensor_index, rc);
+ *value = 0;
+ }
+
+ return rc;
+}
+
+int hl_get_current(struct hl_device *hdev,
+ int sensor_index, u32 attr, long *value)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_CURRENT_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, &result);
+
+ *value = (long) result;
+
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to get current from sensor %d, error %d\n",
+ sensor_index, rc);
+ *value = 0;
+ }
+
+ return rc;
+}
+
+int hl_get_fan_speed(struct hl_device *hdev,
+ int sensor_index, u32 attr, long *value)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_FAN_SPEED_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, &result);
+
+ *value = (long) result;
+
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to get fan speed from sensor %d, error %d\n",
+ sensor_index, rc);
+ *value = 0;
+ }
+
+ return rc;
+}
+
+int hl_get_pwm_info(struct hl_device *hdev,
+ int sensor_index, u32 attr, long *value)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_PWM_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, &result);
+
+ *value = (long) result;
+
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to get pwm info from sensor %d, error %d\n",
+ sensor_index, rc);
+ *value = 0;
+ }
+
+ return rc;
+}
+
+void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr,
+ long value)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_PWM_SET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+ pkt.value = cpu_to_le64(value);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to set pwm info to sensor %d, error %d\n",
+ sensor_index, rc);
+}
+
+int hl_set_voltage(struct hl_device *hdev,
+ int sensor_index, u32 attr, long value)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_VOLTAGE_SET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+ pkt.value = __cpu_to_le64(value);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to set voltage of sensor %d, error %d\n",
+ sensor_index, rc);
+
+ return rc;
+}
+
+int hl_set_current(struct hl_device *hdev,
+ int sensor_index, u32 attr, long value)
+{
+ struct cpucp_packet pkt;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_CURRENT_SET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+ pkt.value = __cpu_to_le64(value);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to set current of sensor %d, error %d\n",
+ sensor_index, rc);
+
+ return rc;
+}
+
+int hl_set_power(struct hl_device *hdev,
+ int sensor_index, u32 attr, long value)
+{
+ struct cpucp_packet pkt;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ if (prop->use_get_power_for_reset_history)
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ else
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_SET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+ pkt.value = __cpu_to_le64(value);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, NULL);
+ if (rc && rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to set power of sensor %d, error %d\n",
+ sensor_index, rc);
+
+ return rc;
+}
+
+int hl_get_power(struct hl_device *hdev,
+ int sensor_index, u32 attr, long *value)
+{
+ struct cpucp_packet pkt;
+ u64 result;
+ int rc;
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET <<
+ CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.sensor_index = __cpu_to_le16(sensor_index);
+ pkt.type = __cpu_to_le16(attr);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ 0, &result);
+
+ *value = (long) result;
+
+ if (rc) {
+ if (rc != -EAGAIN)
+ dev_err_ratelimited(hdev->dev,
+ "Failed to get power of sensor %d, error %d\n",
+ sensor_index, rc);
+ *value = 0;
+ }
+
+ return rc;
+}
+
+int hl_hwmon_init(struct hl_device *hdev)
+{
+ struct device *dev = hdev->pdev ? &hdev->pdev->dev : hdev->dev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc;
+
+ if ((hdev->hwmon_initialized) || !(hdev->cpu_queues_enable))
+ return 0;
+
+ if (hdev->hl_chip_info->info) {
+ hdev->hl_chip_info->ops = &hl_hwmon_ops;
+
+ hdev->hwmon_dev = hwmon_device_register_with_info(dev,
+ prop->cpucp_info.card_name, hdev,
+ hdev->hl_chip_info, NULL);
+ if (IS_ERR(hdev->hwmon_dev)) {
+ rc = PTR_ERR(hdev->hwmon_dev);
+ dev_err(hdev->dev,
+ "Unable to register hwmon device: %d\n", rc);
+ return rc;
+ }
+
+ dev_info(hdev->dev, "%s: add sensors information\n",
+ dev_name(hdev->hwmon_dev));
+
+ hdev->hwmon_initialized = true;
+ } else {
+ dev_info(hdev->dev, "no available sensors\n");
+ }
+
+ return 0;
+}
+
+void hl_hwmon_fini(struct hl_device *hdev)
+{
+ if (!hdev->hwmon_initialized)
+ return;
+
+ hwmon_device_unregister(hdev->hwmon_dev);
+}
+
+void hl_hwmon_release_resources(struct hl_device *hdev)
+{
+ const struct hwmon_channel_info * const *channel_info_arr;
+ int i = 0;
+
+ if (!hdev->hl_chip_info->info)
+ return;
+
+ channel_info_arr = hdev->hl_chip_info->info;
+
+ while (channel_info_arr[i]) {
+ kfree(channel_info_arr[i]->config);
+ kfree(channel_info_arr[i]);
+ i++;
+ }
+
+ kfree(channel_info_arr);
+
+ hdev->hl_chip_info->info = NULL;
+}
diff --git a/drivers/accel/habanalabs/common/irq.c b/drivers/accel/habanalabs/common/irq.c
new file mode 100644
index 000000000000..7c9f2f6a2870
--- /dev/null
+++ b/drivers/accel/habanalabs/common/irq.c
@@ -0,0 +1,724 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+
+#include <linux/slab.h>
+
+/**
+ * struct hl_eqe_work - This structure is used to schedule work of EQ
+ * entry and cpucp_reset event
+ *
+ * @eq_work: workqueue object to run when EQ entry is received
+ * @hdev: pointer to device structure
+ * @eq_entry: copy of the EQ entry
+ */
+struct hl_eqe_work {
+ struct work_struct eq_work;
+ struct hl_device *hdev;
+ struct hl_eq_entry eq_entry;
+};
+
+/**
+ * hl_cq_inc_ptr - increment ci or pi of cq
+ *
+ * @ptr: the current ci or pi value of the completion queue
+ *
+ * Increment ptr by 1. If it reaches the number of completion queue
+ * entries, set it to 0
+ */
+inline u32 hl_cq_inc_ptr(u32 ptr)
+{
+ ptr++;
+ if (unlikely(ptr == HL_CQ_LENGTH))
+ ptr = 0;
+ return ptr;
+}
+
+/**
+ * hl_eq_inc_ptr - increment ci of eq
+ *
+ * @ptr: the current ci value of the event queue
+ *
+ * Increment ptr by 1. If it reaches the number of event queue
+ * entries, set it to 0
+ */
+static inline u32 hl_eq_inc_ptr(u32 ptr)
+{
+ ptr++;
+ if (unlikely(ptr == HL_EQ_LENGTH))
+ ptr = 0;
+ return ptr;
+}
+
+static void irq_handle_eqe(struct work_struct *work)
+{
+ struct hl_eqe_work *eqe_work = container_of(work, struct hl_eqe_work,
+ eq_work);
+ struct hl_device *hdev = eqe_work->hdev;
+
+ hdev->asic_funcs->handle_eqe(hdev, &eqe_work->eq_entry);
+
+ kfree(eqe_work);
+}
+
+/**
+ * job_finish - queue job finish work
+ *
+ * @hdev: pointer to device structure
+ * @cs_seq: command submission sequence
+ * @cq: completion queue
+ * @timestamp: interrupt timestamp
+ *
+ */
+static void job_finish(struct hl_device *hdev, u32 cs_seq, struct hl_cq *cq, ktime_t timestamp)
+{
+ struct hl_hw_queue *queue;
+ struct hl_cs_job *job;
+
+ queue = &hdev->kernel_queues[cq->hw_queue_id];
+ job = queue->shadow_queue[hl_pi_2_offset(cs_seq)];
+ job->timestamp = timestamp;
+ queue_work(hdev->cq_wq[cq->cq_idx], &job->finish_work);
+
+ atomic_inc(&queue->ci);
+}
+
+/**
+ * cs_finish - queue all cs jobs finish work
+ *
+ * @hdev: pointer to device structure
+ * @cs_seq: command submission sequence
+ * @timestamp: interrupt timestamp
+ *
+ */
+static void cs_finish(struct hl_device *hdev, u16 cs_seq, ktime_t timestamp)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_hw_queue *queue;
+ struct hl_cs *cs;
+ struct hl_cs_job *job;
+
+ cs = hdev->shadow_cs_queue[cs_seq & (prop->max_pending_cs - 1)];
+ if (!cs) {
+ dev_warn(hdev->dev,
+ "No pointer to CS in shadow array at index %d\n",
+ cs_seq);
+ return;
+ }
+
+ list_for_each_entry(job, &cs->job_list, cs_node) {
+ queue = &hdev->kernel_queues[job->hw_queue_id];
+ atomic_inc(&queue->ci);
+ }
+
+ cs->completion_timestamp = timestamp;
+ queue_work(hdev->cs_cmplt_wq, &cs->finish_work);
+}
+
+/**
+ * hl_irq_handler_cq - irq handler for completion queue
+ *
+ * @irq: irq number
+ * @arg: pointer to completion queue structure
+ *
+ */
+irqreturn_t hl_irq_handler_cq(int irq, void *arg)
+{
+ struct hl_cq *cq = arg;
+ struct hl_device *hdev = cq->hdev;
+ bool shadow_index_valid, entry_ready;
+ u16 shadow_index;
+ struct hl_cq_entry *cq_entry, *cq_base;
+ ktime_t timestamp = ktime_get();
+
+ if (hdev->disabled) {
+ dev_dbg(hdev->dev,
+ "Device disabled but received IRQ %d for CQ %d\n",
+ irq, cq->hw_queue_id);
+ return IRQ_HANDLED;
+ }
+
+ cq_base = cq->kernel_address;
+
+ while (1) {
+ cq_entry = (struct hl_cq_entry *) &cq_base[cq->ci];
+
+ entry_ready = !!FIELD_GET(CQ_ENTRY_READY_MASK,
+ le32_to_cpu(cq_entry->data));
+ if (!entry_ready)
+ break;
+
+ /* Make sure we read CQ entry contents after we've
+ * checked the ownership bit.
+ */
+ dma_rmb();
+
+ shadow_index_valid =
+ !!FIELD_GET(CQ_ENTRY_SHADOW_INDEX_VALID_MASK,
+ le32_to_cpu(cq_entry->data));
+
+ shadow_index = FIELD_GET(CQ_ENTRY_SHADOW_INDEX_MASK,
+ le32_to_cpu(cq_entry->data));
+
+ /*
+ * CQ interrupt handler has 2 modes of operation:
+ * 1. Interrupt per CS completion: (Single CQ for all queues)
+ * CQ entry represents a completed CS
+ *
+ * 2. Interrupt per CS job completion in queue: (CQ per queue)
+ * CQ entry represents a completed job in a certain queue
+ */
+ if (shadow_index_valid && !hdev->disabled) {
+ if (hdev->asic_prop.completion_mode ==
+ HL_COMPLETION_MODE_CS)
+ cs_finish(hdev, shadow_index, timestamp);
+ else
+ job_finish(hdev, shadow_index, cq, timestamp);
+ }
+
+ /* Clear CQ entry ready bit */
+ cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) &
+ ~CQ_ENTRY_READY_MASK);
+
+ cq->ci = hl_cq_inc_ptr(cq->ci);
+
+ /* Increment free slots */
+ atomic_inc(&cq->free_slots_cnt);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * hl_ts_free_objects - handler of the free objects workqueue.
+ * This function should put refcount to objects that the registration node
+ * took refcount to them.
+ * @work: workqueue object pointer
+ */
+static void hl_ts_free_objects(struct work_struct *work)
+{
+ struct timestamp_reg_work_obj *job =
+ container_of(work, struct timestamp_reg_work_obj, free_obj);
+ struct list_head *dynamic_alloc_free_list_head = job->dynamic_alloc_free_obj_head;
+ struct timestamp_reg_free_node *free_obj, *temp_free_obj;
+ struct list_head *free_list_head = job->free_obj_head;
+
+ struct hl_device *hdev = job->hdev;
+
+ list_for_each_entry_safe(free_obj, temp_free_obj, free_list_head, free_objects_node) {
+ dev_dbg(hdev->dev, "About to put refcount to buf (%p) cq_cb(%p)\n",
+ free_obj->buf,
+ free_obj->cq_cb);
+
+ hl_mmap_mem_buf_put(free_obj->buf);
+ hl_cb_put(free_obj->cq_cb);
+ atomic_set(&free_obj->in_use, 0);
+ }
+
+ kfree(free_list_head);
+
+ if (dynamic_alloc_free_list_head) {
+ list_for_each_entry_safe(free_obj, temp_free_obj, dynamic_alloc_free_list_head,
+ free_objects_node) {
+ dev_dbg(hdev->dev,
+ "Dynamic_Alloc list: About to put refcount to buf (%p) cq_cb(%p)\n",
+ free_obj->buf,
+ free_obj->cq_cb);
+
+ hl_mmap_mem_buf_put(free_obj->buf);
+ hl_cb_put(free_obj->cq_cb);
+ list_del(&free_obj->free_objects_node);
+ kfree(free_obj);
+ }
+
+ kfree(dynamic_alloc_free_list_head);
+ }
+
+ kfree(job);
+}
+
+/*
+ * This function called with spin_lock of wait_list_lock taken
+ * This function will set timestamp and delete the registration node from the
+ * wait_list_lock.
+ * and since we're protected with spin_lock here, so we cannot just put the refcount
+ * for the objects here, since the release function may be called and it's also a long
+ * logic (which might sleep also) that cannot be handled in irq context.
+ * so here we'll be filling a list with nodes of "put" jobs and then will send this
+ * list to a dedicated workqueue to do the actual put.
+ */
+static int handle_registration_node(struct hl_device *hdev, struct hl_user_pending_interrupt *pend,
+ struct list_head **free_list,
+ struct list_head **dynamic_alloc_list,
+ struct hl_user_interrupt *intr)
+{
+ struct hl_ts_free_jobs *ts_free_jobs_data;
+ struct timestamp_reg_free_node *free_node;
+ u32 free_node_index;
+ u64 timestamp;
+
+ ts_free_jobs_data = &intr->ts_free_jobs_data;
+ free_node_index = ts_free_jobs_data->next_avail_free_node_idx;
+
+ if (!(*free_list)) {
+ /* Alloc/Init the timestamp registration free objects list */
+ *free_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+ if (!(*free_list))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(*free_list);
+ }
+
+ free_node = &ts_free_jobs_data->free_nodes_pool[free_node_index];
+ if (atomic_cmpxchg(&free_node->in_use, 0, 1)) {
+ dev_dbg(hdev->dev,
+ "Timestamp free node pool is full, buff: %p, record: %p, irq: %u\n",
+ pend->ts_reg_info.buf,
+ pend,
+ intr->interrupt_id);
+
+ if (!(*dynamic_alloc_list)) {
+ *dynamic_alloc_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+ if (!(*dynamic_alloc_list))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(*dynamic_alloc_list);
+ }
+
+ free_node = kmalloc(sizeof(struct timestamp_reg_free_node), GFP_ATOMIC);
+ if (!free_node)
+ return -ENOMEM;
+
+ free_node->dynamic_alloc = 1;
+ }
+
+ timestamp = ktime_to_ns(intr->timestamp);
+
+ *pend->ts_reg_info.timestamp_kernel_addr = timestamp;
+
+ dev_dbg(hdev->dev, "Irq handle: Timestamp record (%p) ts cb address (%p), interrupt_id: %u\n",
+ pend, pend->ts_reg_info.timestamp_kernel_addr, intr->interrupt_id);
+
+ list_del(&pend->list_node);
+
+ /* Putting the refcount for ts_buff and cq_cb objects will be handled
+ * in workqueue context, just add job to free_list.
+ */
+ free_node->buf = pend->ts_reg_info.buf;
+ free_node->cq_cb = pend->ts_reg_info.cq_cb;
+
+ if (free_node->dynamic_alloc) {
+ list_add(&free_node->free_objects_node, *dynamic_alloc_list);
+ } else {
+ ts_free_jobs_data->next_avail_free_node_idx =
+ (++free_node_index) % ts_free_jobs_data->free_nodes_length;
+ list_add(&free_node->free_objects_node, *free_list);
+ }
+
+ /* Mark TS record as free */
+ pend->ts_reg_info.in_use = false;
+
+ return 0;
+}
+
+static void handle_user_interrupt_ts_list(struct hl_device *hdev, struct hl_user_interrupt *intr)
+{
+ struct list_head *ts_reg_free_list_head = NULL, *dynamic_alloc_list_head = NULL;
+ struct hl_user_pending_interrupt *pend, *temp_pend;
+ struct timestamp_reg_work_obj *job;
+ bool reg_node_handle_fail = false;
+ unsigned long flags;
+ int rc;
+
+ /* For registration nodes:
+ * As part of handling the registration nodes, we should put refcount to
+ * some objects. the problem is that we cannot do that under spinlock
+ * or in irq handler context at all (since release functions are long and
+ * might sleep), so we will need to handle that part in workqueue context.
+ * To avoid handling kmalloc failure which compels us rolling back actions
+ * and move nodes hanged on the free list back to the interrupt ts list
+ * we always alloc the job of the WQ at the beginning.
+ */
+ job = kmalloc(sizeof(*job), GFP_ATOMIC);
+ if (!job)
+ return;
+
+ spin_lock_irqsave(&intr->ts_list_lock, flags);
+ list_for_each_entry_safe(pend, temp_pend, &intr->ts_list_head, list_node) {
+ if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) ||
+ !pend->cq_kernel_addr) {
+ if (!reg_node_handle_fail) {
+ rc = handle_registration_node(hdev, pend,
+ &ts_reg_free_list_head,
+ &dynamic_alloc_list_head, intr);
+ if (rc)
+ reg_node_handle_fail = true;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&intr->ts_list_lock, flags);
+
+ if (ts_reg_free_list_head) {
+ INIT_WORK(&job->free_obj, hl_ts_free_objects);
+ job->free_obj_head = ts_reg_free_list_head;
+ job->dynamic_alloc_free_obj_head = dynamic_alloc_list_head;
+ job->hdev = hdev;
+ queue_work(hdev->ts_free_obj_wq, &job->free_obj);
+ } else {
+ kfree(job);
+ }
+}
+
+static void handle_user_interrupt_wait_list(struct hl_device *hdev, struct hl_user_interrupt *intr)
+{
+ struct hl_user_pending_interrupt *pend, *temp_pend;
+ unsigned long flags;
+
+ spin_lock_irqsave(&intr->wait_list_lock, flags);
+ list_for_each_entry_safe(pend, temp_pend, &intr->wait_list_head, list_node) {
+ if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) ||
+ !pend->cq_kernel_addr) {
+ /* Handle wait target value node */
+ pend->fence.timestamp = intr->timestamp;
+ complete_all(&pend->fence.completion);
+ }
+ }
+ spin_unlock_irqrestore(&intr->wait_list_lock, flags);
+}
+
+static void handle_tpc_interrupt(struct hl_device *hdev)
+{
+ u64 event_mask;
+ u32 flags;
+
+ event_mask = HL_NOTIFIER_EVENT_TPC_ASSERT |
+ HL_NOTIFIER_EVENT_USER_ENGINE_ERR |
+ HL_NOTIFIER_EVENT_DEVICE_RESET;
+
+ flags = HL_DRV_RESET_DELAY;
+
+ dev_err_ratelimited(hdev->dev, "Received TPC assert\n");
+ hl_device_cond_reset(hdev, flags, event_mask);
+}
+
+static void handle_unexpected_user_interrupt(struct hl_device *hdev)
+{
+ dev_err_ratelimited(hdev->dev, "Received unexpected user error interrupt\n");
+}
+
+/**
+ * hl_irq_user_interrupt_handler - irq handler for user interrupts.
+ *
+ * @irq: irq number
+ * @arg: pointer to user interrupt structure
+ */
+irqreturn_t hl_irq_user_interrupt_handler(int irq, void *arg)
+{
+ struct hl_user_interrupt *user_int = arg;
+ struct hl_device *hdev = user_int->hdev;
+
+ user_int->timestamp = ktime_get();
+ switch (user_int->type) {
+ case HL_USR_INTERRUPT_CQ:
+ /* First handle user waiters threads */
+ handle_user_interrupt_wait_list(hdev, &hdev->common_user_cq_interrupt);
+ handle_user_interrupt_wait_list(hdev, user_int);
+
+ /* Second handle user timestamp registrations */
+ handle_user_interrupt_ts_list(hdev, &hdev->common_user_cq_interrupt);
+ handle_user_interrupt_ts_list(hdev, user_int);
+ break;
+ case HL_USR_INTERRUPT_DECODER:
+ handle_user_interrupt_wait_list(hdev, &hdev->common_decoder_interrupt);
+
+ /* Handle decoder interrupt registered on this specific irq */
+ handle_user_interrupt_wait_list(hdev, user_int);
+ break;
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * hl_irq_user_interrupt_thread_handler - irq thread handler for user interrupts.
+ * This function is invoked by threaded irq mechanism
+ *
+ * @irq: irq number
+ * @arg: pointer to user interrupt structure
+ *
+ */
+irqreturn_t hl_irq_user_interrupt_thread_handler(int irq, void *arg)
+{
+ struct hl_user_interrupt *user_int = arg;
+ struct hl_device *hdev = user_int->hdev;
+
+ user_int->timestamp = ktime_get();
+ switch (user_int->type) {
+ case HL_USR_INTERRUPT_TPC:
+ handle_tpc_interrupt(hdev);
+ break;
+ case HL_USR_INTERRUPT_UNEXPECTED:
+ handle_unexpected_user_interrupt(hdev);
+ break;
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t hl_irq_eq_error_interrupt_thread_handler(int irq, void *arg)
+{
+ u64 event_mask = HL_NOTIFIER_EVENT_DEVICE_RESET | HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE;
+ struct hl_device *hdev = arg;
+
+ dev_err(hdev->dev, "EQ error interrupt received\n");
+
+ hl_device_cond_reset(hdev, HL_DRV_RESET_HARD, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * hl_irq_handler_eq - irq handler for event queue
+ *
+ * @irq: irq number
+ * @arg: pointer to event queue structure
+ *
+ */
+irqreturn_t hl_irq_handler_eq(int irq, void *arg)
+{
+ struct hl_eq *eq = arg;
+ struct hl_device *hdev = eq->hdev;
+ struct hl_eq_entry *eq_entry;
+ struct hl_eq_entry *eq_base;
+ struct hl_eqe_work *handle_eqe_work;
+ bool entry_ready;
+ u32 cur_eqe, ctl;
+ u16 cur_eqe_index, event_type;
+
+ eq_base = eq->kernel_address;
+
+ while (1) {
+ cur_eqe = le32_to_cpu(eq_base[eq->ci].hdr.ctl);
+ entry_ready = !!FIELD_GET(EQ_CTL_READY_MASK, cur_eqe);
+
+ if (!entry_ready)
+ break;
+
+ cur_eqe_index = FIELD_GET(EQ_CTL_INDEX_MASK, cur_eqe);
+ if ((hdev->event_queue.check_eqe_index) &&
+ (((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK) != cur_eqe_index)) {
+ dev_err(hdev->dev,
+ "EQE %#x in queue is ready but index does not match %d!=%d",
+ cur_eqe,
+ ((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK),
+ cur_eqe_index);
+ break;
+ }
+
+ eq->prev_eqe_index++;
+
+ eq_entry = &eq_base[eq->ci];
+
+ /*
+ * Make sure we read EQ entry contents after we've
+ * checked the ownership bit.
+ */
+ dma_rmb();
+
+ if (hdev->disabled && !hdev->reset_info.in_compute_reset) {
+ ctl = le32_to_cpu(eq_entry->hdr.ctl);
+ event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK) >> EQ_CTL_EVENT_TYPE_SHIFT);
+ dev_warn(hdev->dev,
+ "Device disabled but received an EQ event (%u)\n", event_type);
+ goto skip_irq;
+ }
+
+ handle_eqe_work = kmalloc(sizeof(*handle_eqe_work), GFP_ATOMIC);
+ if (handle_eqe_work) {
+ INIT_WORK(&handle_eqe_work->eq_work, irq_handle_eqe);
+ handle_eqe_work->hdev = hdev;
+
+ memcpy(&handle_eqe_work->eq_entry, eq_entry,
+ sizeof(*eq_entry));
+
+ queue_work(hdev->eq_wq, &handle_eqe_work->eq_work);
+ }
+skip_irq:
+ /* Clear EQ entry ready bit */
+ eq_entry->hdr.ctl =
+ cpu_to_le32(le32_to_cpu(eq_entry->hdr.ctl) &
+ ~EQ_CTL_READY_MASK);
+
+ eq->ci = hl_eq_inc_ptr(eq->ci);
+
+ hdev->asic_funcs->update_eq_ci(hdev, eq->ci);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * hl_irq_handler_dec_abnrm - Decoder error interrupt handler
+ * @irq: IRQ number
+ * @arg: pointer to decoder structure.
+ */
+irqreturn_t hl_irq_handler_dec_abnrm(int irq, void *arg)
+{
+ struct hl_dec *dec = arg;
+
+ schedule_work(&dec->abnrm_intr_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * hl_cq_init - main initialization function for an cq object
+ *
+ * @hdev: pointer to device structure
+ * @q: pointer to cq structure
+ * @hw_queue_id: The H/W queue ID this completion queue belongs to
+ * HL_INVALID_QUEUE if cq is not attached to any specific queue
+ *
+ * Allocate dma-able memory for the completion queue and initialize fields
+ * Returns 0 on success
+ */
+int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id)
+{
+ void *p;
+
+ p = hl_asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, &q->bus_address,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!p)
+ return -ENOMEM;
+
+ q->hdev = hdev;
+ q->kernel_address = p;
+ q->hw_queue_id = hw_queue_id;
+ q->ci = 0;
+ q->pi = 0;
+
+ atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH);
+
+ return 0;
+}
+
+/**
+ * hl_cq_fini - destroy completion queue
+ *
+ * @hdev: pointer to device structure
+ * @q: pointer to cq structure
+ *
+ * Free the completion queue memory
+ */
+void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q)
+{
+ hl_asic_dma_free_coherent(hdev, HL_CQ_SIZE_IN_BYTES, q->kernel_address, q->bus_address);
+}
+
+void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q)
+{
+ q->ci = 0;
+ q->pi = 0;
+
+ atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH);
+
+ /*
+ * It's not enough to just reset the PI/CI because the H/W may have
+ * written valid completion entries before it was halted and therefore
+ * we need to clean the actual queues so we won't process old entries
+ * when the device is operational again
+ */
+
+ memset(q->kernel_address, 0, HL_CQ_SIZE_IN_BYTES);
+}
+
+/**
+ * hl_eq_init - main initialization function for an event queue object
+ *
+ * @hdev: pointer to device structure
+ * @q: pointer to eq structure
+ *
+ * Allocate dma-able memory for the event queue and initialize fields
+ * Returns 0 on success
+ */
+int hl_eq_init(struct hl_device *hdev, struct hl_eq *q)
+{
+ u32 size = hdev->asic_prop.fw_event_queue_size ? : HL_EQ_SIZE_IN_BYTES;
+ void *p;
+
+ p = hl_cpu_accessible_dma_pool_alloc(hdev, size, &q->bus_address);
+ if (!p)
+ return -ENOMEM;
+
+ q->hdev = hdev;
+ q->kernel_address = p;
+ q->size = size;
+ q->ci = 0;
+ q->prev_eqe_index = 0;
+
+ return 0;
+}
+
+/**
+ * hl_eq_fini - destroy event queue
+ *
+ * @hdev: pointer to device structure
+ * @q: pointer to eq structure
+ *
+ * Free the event queue memory
+ */
+void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q)
+{
+ flush_workqueue(hdev->eq_wq);
+
+ hl_cpu_accessible_dma_pool_free(hdev, q->size, q->kernel_address);
+}
+
+void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q)
+{
+ q->ci = 0;
+ q->prev_eqe_index = 0;
+
+ /*
+ * It's not enough to just reset the PI/CI because the H/W may have
+ * written valid completion entries before it was halted and therefore
+ * we need to clean the actual queues so we won't process old entries
+ * when the device is operational again
+ */
+
+ memset(q->kernel_address, 0, q->size);
+}
+
+void hl_eq_dump(struct hl_device *hdev, struct hl_eq *q)
+{
+ u32 eq_length, eqe_size, ctl, ready, mode, type, index;
+ struct hl_eq_header *hdr;
+ u8 *ptr;
+ int i;
+
+ eq_length = HL_EQ_LENGTH;
+ eqe_size = q->size / HL_EQ_LENGTH;
+
+ dev_info(hdev->dev, "Contents of EQ entries headers:\n");
+
+ for (i = 0, ptr = q->kernel_address ; i < eq_length ; ++i, ptr += eqe_size) {
+ hdr = (struct hl_eq_header *) ptr;
+ ctl = le32_to_cpu(hdr->ctl);
+ ready = FIELD_GET(EQ_CTL_READY_MASK, ctl);
+ mode = FIELD_GET(EQ_CTL_EVENT_MODE_MASK, ctl);
+ type = FIELD_GET(EQ_CTL_EVENT_TYPE_MASK, ctl);
+ index = FIELD_GET(EQ_CTL_INDEX_MASK, ctl);
+
+ dev_info(hdev->dev, "%02u: %#010x [ready: %u, mode %u, type %04u, index %05u]\n",
+ i, ctl, ready, mode, type, index);
+ }
+}
diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/accel/habanalabs/common/memory.c
index 61bc1bfe984a..633db4bff46f 100644
--- a/drivers/misc/habanalabs/common/memory.c
+++ b/drivers/accel/habanalabs/common/memory.c
@@ -5,7 +5,7 @@
* All Rights Reserved.
*/
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include "habanalabs.h"
#include "../include/hw_ip/mmu/mmu_general.h"
@@ -14,12 +14,14 @@
#include <linux/vmalloc.h>
#include <linux/pci-p2pdma.h>
-MODULE_IMPORT_NS(DMA_BUF);
+MODULE_IMPORT_NS("DMA_BUF");
#define HL_MMU_DEBUG 0
/* use small pages for supporting non-pow2 (32M/40M/48M) DRAM phys page sizes */
-#define DRAM_POOL_PAGE_SIZE SZ_8M
+#define DRAM_POOL_PAGE_SIZE SZ_8M
+
+#define MEM_HANDLE_INVALID ULONG_MAX
static int allocate_timestamps_buffers(struct hl_fpriv *hpriv,
struct hl_mem_in *args, u64 *handle);
@@ -233,10 +235,8 @@ static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size,
}
rc = hl_pin_host_memory(hdev, addr, size, userptr);
- if (rc) {
- dev_err(hdev->dev, "Failed to pin host memory\n");
+ if (rc)
goto pin_err;
- }
userptr->dma_mapped = true;
userptr->dir = DMA_BIDIRECTIONAL;
@@ -244,7 +244,7 @@ static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size,
*p_userptr = userptr;
- rc = hdev->asic_funcs->asic_dma_map_sgtable(hdev, userptr->sgt, DMA_BIDIRECTIONAL);
+ rc = hl_dma_map_sgtable(hdev, userptr->sgt, DMA_BIDIRECTIONAL);
if (rc) {
dev_err(hdev->dev, "failed to map sgt with DMA region\n");
goto dma_map_err;
@@ -371,12 +371,6 @@ static int free_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args)
return -EINVAL;
}
- if (phys_pg_pack->exporting_cnt) {
- spin_unlock(&vm->idr_lock);
- dev_dbg(hdev->dev, "handle %u is exported, cannot free\n", handle);
- return -EINVAL;
- }
-
/* must remove from idr before the freeing of the physical pages as the refcount of the pool
* is also the trigger of the idr destroy
*/
@@ -457,7 +451,7 @@ static void merge_va_blocks_locked(struct hl_device *hdev,
prev = list_prev_entry(va_block, node);
if (&prev->node != va_list && prev->end + 1 == va_block->start) {
prev->end = va_block->end;
- prev->size = prev->end - prev->start;
+ prev->size = prev->end - prev->start + 1;
list_del(&va_block->node);
kfree(va_block);
va_block = prev;
@@ -466,7 +460,7 @@ static void merge_va_blocks_locked(struct hl_device *hdev,
next = list_next_entry(va_block, node);
if (&next->node != va_list && va_block->end + 1 == next->start) {
next->start = va_block->start;
- next->size = next->end - next->start;
+ next->size = next->end - next->start + 1;
list_del(&va_block->node);
kfree(va_block);
}
@@ -611,6 +605,7 @@ static u64 get_va_block(struct hl_device *hdev,
bool is_align_pow_2 = is_power_of_2(va_range->page_size);
bool is_hint_dram_addr = hl_is_dram_va(hdev, hint_addr);
bool force_hint = flags & HL_MEM_FORCE_HINT;
+ int rc;
if (is_align_pow_2)
align_mask = ~((u64)va_block_align - 1);
@@ -728,9 +723,13 @@ static u64 get_va_block(struct hl_device *hdev,
kfree(new_va_block);
}
- if (add_prev)
- add_va_block_locked(hdev, &va_range->list, prev_start,
- prev_end);
+ if (add_prev) {
+ rc = add_va_block_locked(hdev, &va_range->list, prev_start, prev_end);
+ if (rc) {
+ reserved_valid_start = 0;
+ goto out;
+ }
+ }
print_va_list_locked(hdev, &va_range->list);
out:
@@ -755,7 +754,7 @@ out:
* - Return the start address of the virtual block.
*/
u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
- enum hl_va_range_type type, u32 size, u32 alignment)
+ enum hl_va_range_type type, u64 size, u32 alignment)
{
return get_va_block(hdev, ctx->va_range[type], size, 0,
max(alignment, ctx->va_range[type]->page_size),
@@ -833,7 +832,6 @@ int hl_unreserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
* physical pages
*
* This function does the following:
- * - Pin the physical pages related to the given virtual block.
* - Create a physical page pack from the physical pages related to the given
* virtual block.
*/
@@ -957,8 +955,8 @@ static int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
(i + 1) == phys_pg_pack->npages);
if (rc) {
dev_err(hdev->dev,
- "map failed for handle %u, npages: %llu, mapped: %llu",
- phys_pg_pack->handle, phys_pg_pack->npages,
+ "map failed (%d) for handle %u, npages: %llu, mapped: %llu\n",
+ rc, phys_pg_pack->handle, phys_pg_pack->npages,
mapped_pg_cnt);
goto err;
}
@@ -1035,30 +1033,6 @@ static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
}
}
-static int get_paddr_from_handle(struct hl_ctx *ctx, struct hl_mem_in *args,
- u64 *paddr)
-{
- struct hl_device *hdev = ctx->hdev;
- struct hl_vm *vm = &hdev->vm;
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- u32 handle;
-
- handle = lower_32_bits(args->map_device.handle);
- spin_lock(&vm->idr_lock);
- phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle);
- if (!phys_pg_pack) {
- spin_unlock(&vm->idr_lock);
- dev_err(hdev->dev, "no match for handle %u\n", handle);
- return -EINVAL;
- }
-
- *paddr = phys_pg_pack->pages[0];
-
- spin_unlock(&vm->idr_lock);
-
- return 0;
-}
-
/**
* map_device_va() - map the given memory.
* @ctx: pointer to the context structure.
@@ -1101,10 +1075,8 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *device
huge_page_size = hdev->asic_prop.pmmu_huge.page_size;
rc = dma_map_host_va(hdev, addr, size, &userptr);
- if (rc) {
- dev_err(hdev->dev, "failed to get userptr from va\n");
+ if (rc)
return rc;
- }
rc = init_phys_pg_pack_from_userptr(ctx, userptr,
&phys_pg_pack, false);
@@ -1210,18 +1182,19 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *device
goto va_block_err;
}
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack);
if (rc) {
- dev_err(hdev->dev, "mapping page pack failed for handle %u\n", handle);
- mutex_unlock(&ctx->mmu_lock);
+ dev_err(hdev->dev, "mapping page pack failed (%d) for handle %u\n",
+ rc, handle);
+ mutex_unlock(&hdev->mmu_lock);
goto map_err;
}
rc = hl_mmu_invalidate_cache_range(hdev, false, *vm_type | MMU_OP_SKIP_LOW_CACHE_INV,
ctx->asid, ret_vaddr, phys_pg_pack->total_size);
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
if (rc)
goto map_err;
@@ -1240,6 +1213,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *device
hnode->ptr = vm_type;
hnode->vaddr = ret_vaddr;
+ hnode->handle = is_userptr ? MEM_HANDLE_INVALID : handle;
mutex_lock(&ctx->mem_hash_lock);
hash_add(ctx->mem_hash, &hnode->node, ret_vaddr);
@@ -1273,6 +1247,18 @@ init_page_pack_err:
return rc;
}
+/* Should be called while the context's mem_hash_lock is taken */
+static struct hl_vm_hash_node *get_vm_hash_node_locked(struct hl_ctx *ctx, u64 vaddr)
+{
+ struct hl_vm_hash_node *hnode;
+
+ hash_for_each_possible(ctx->mem_hash, hnode, node, vaddr)
+ if (vaddr == hnode->vaddr)
+ return hnode;
+
+ return NULL;
+}
+
/**
* unmap_device_va() - unmap the given device virtual address.
* @ctx: pointer to the context structure.
@@ -1288,10 +1274,10 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
{
struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
u64 vaddr = args->unmap.device_virt_addr;
- struct hl_vm_hash_node *hnode = NULL;
struct asic_fixed_properties *prop;
struct hl_device *hdev = ctx->hdev;
struct hl_userptr *userptr = NULL;
+ struct hl_vm_hash_node *hnode;
struct hl_va_range *va_range;
enum vm_type *vm_type;
bool is_userptr;
@@ -1301,15 +1287,16 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
/* protect from double entrance */
mutex_lock(&ctx->mem_hash_lock);
- hash_for_each_possible(ctx->mem_hash, hnode, node, (unsigned long)vaddr)
- if (vaddr == hnode->vaddr)
- break;
-
+ hnode = get_vm_hash_node_locked(ctx, vaddr);
if (!hnode) {
mutex_unlock(&ctx->mem_hash_lock);
- dev_err(hdev->dev,
- "unmap failed, no mem hnode for vaddr 0x%llx\n",
- vaddr);
+ dev_err(hdev->dev, "unmap failed, no mem hnode for vaddr 0x%llx\n", vaddr);
+ return -EINVAL;
+ }
+
+ if (hnode->export_cnt) {
+ mutex_unlock(&ctx->mem_hash_lock);
+ dev_err(hdev->dev, "failed to unmap %#llx, memory is exported\n", vaddr);
return -EINVAL;
}
@@ -1362,7 +1349,7 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
else
vaddr &= ~(((u64) phys_pg_pack->page_size) - 1);
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack);
@@ -1375,7 +1362,7 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
rc = hl_mmu_invalidate_cache_range(hdev, true, *vm_type, ctx->asid, vaddr,
phys_pg_pack->total_size);
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
/*
* If the context is closing we don't need to check for the MMU cache
@@ -1418,18 +1405,23 @@ vm_type_err:
return rc;
}
-static int map_block(struct hl_device *hdev, u64 address, u64 *handle,
- u32 *size)
+static int map_block(struct hl_device *hdev, u64 address, u64 *handle, u32 *size)
{
- u32 block_id = 0;
+ u32 block_id;
int rc;
+ *handle = 0;
+ if (size)
+ *size = 0;
+
rc = hdev->asic_funcs->get_hw_block_id(hdev, address, size, &block_id);
+ if (rc)
+ return rc;
*handle = block_id | HL_MMAP_TYPE_BLOCK;
*handle <<= PAGE_SHIFT;
- return rc;
+ return 0;
}
static void hw_block_vm_close(struct vm_area_struct *vma)
@@ -1437,6 +1429,13 @@ static void hw_block_vm_close(struct vm_area_struct *vma)
struct hl_vm_hw_block_list_node *lnode =
(struct hl_vm_hw_block_list_node *) vma->vm_private_data;
struct hl_ctx *ctx = lnode->ctx;
+ long new_mmap_size;
+
+ new_mmap_size = lnode->mapped_size - (vma->vm_end - vma->vm_start);
+ if (new_mmap_size > 0) {
+ lnode->mapped_size = new_mmap_size;
+ return;
+ }
mutex_lock(&ctx->hw_block_list_lock);
list_del(&lnode->node);
@@ -1487,23 +1486,23 @@ int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
if (!lnode)
return -ENOMEM;
- vma->vm_ops = &hw_block_vm_ops;
- vma->vm_private_data = lnode;
-
- hl_ctx_get(ctx);
-
rc = hdev->asic_funcs->hw_block_mmap(hdev, vma, block_id, block_size);
if (rc) {
- hl_ctx_put(ctx);
kfree(lnode);
return rc;
}
+ hl_ctx_get(ctx);
+
lnode->ctx = ctx;
lnode->vaddr = vma->vm_start;
- lnode->size = block_size;
+ lnode->block_size = block_size;
+ lnode->mapped_size = lnode->block_size;
lnode->id = block_id;
+ vma->vm_private_data = lnode;
+ vma->vm_ops = &hw_block_vm_ops;
+
mutex_lock(&ctx->hw_block_list_lock);
list_add_tail(&lnode->node, &ctx->hw_block_mem_list);
mutex_unlock(&ctx->hw_block_list_lock);
@@ -1533,24 +1532,20 @@ static int set_dma_sg(struct scatterlist *sg, u64 bar_address, u64 chunk_size,
}
static struct sg_table *alloc_sgt_from_device_pages(struct hl_device *hdev, u64 *pages, u64 npages,
- u64 page_size, struct device *dev,
- enum dma_data_direction dir)
+ u64 page_size, u64 exported_size, u64 offset,
+ struct device *dev, enum dma_data_direction dir)
{
- u64 chunk_size, bar_address, dma_max_seg_size;
- struct asic_fixed_properties *prop;
- int rc, i, j, nents, cur_page;
+ u64 dma_max_seg_size, curr_page, size, chunk_size, left_size_to_export, left_size_in_page,
+ left_size_in_dma_seg, device_address, bar_address, start_page;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
struct scatterlist *sg;
+ unsigned int nents, i;
struct sg_table *sgt;
+ bool next_sg_entry;
+ int rc;
- prop = &hdev->asic_prop;
-
- dma_max_seg_size = dma_get_max_seg_size(dev);
-
- /* We would like to align the max segment size to PAGE_SIZE, so the
- * SGL will contain aligned addresses that can be easily mapped to
- * an MMU
- */
- dma_max_seg_size = ALIGN_DOWN(dma_max_seg_size, PAGE_SIZE);
+ /* Align max segment size to PAGE_SIZE to fit the minimal IOMMU mapping granularity */
+ dma_max_seg_size = ALIGN_DOWN(dma_get_max_seg_size(dev), PAGE_SIZE);
if (dma_max_seg_size < PAGE_SIZE) {
dev_err_ratelimited(hdev->dev,
"dma_max_seg_size %llu can't be smaller than PAGE_SIZE\n",
@@ -1562,107 +1557,149 @@ static struct sg_table *alloc_sgt_from_device_pages(struct hl_device *hdev, u64
if (!sgt)
return ERR_PTR(-ENOMEM);
- /* If the size of each page is larger than the dma max segment size,
- * then we can't combine pages and the number of entries in the SGL
- * will just be the
- * <number of pages> * <chunks of max segment size in each page>
- */
- if (page_size > dma_max_seg_size)
- nents = npages * DIV_ROUND_UP_ULL(page_size, dma_max_seg_size);
- else
- /* Get number of non-contiguous chunks */
- for (i = 1, nents = 1, chunk_size = page_size ; i < npages ; i++) {
- if (pages[i - 1] + page_size != pages[i] ||
- chunk_size + page_size > dma_max_seg_size) {
- nents++;
- chunk_size = page_size;
- continue;
- }
+ /* Use the offset to move to the actual first page that is exported */
+ for (start_page = 0 ; start_page < npages ; ++start_page) {
+ if (offset < page_size)
+ break;
- chunk_size += page_size;
- }
+ /* The offset value was validated so there can't be an underflow */
+ offset -= page_size;
+ }
- rc = sg_alloc_table(sgt, nents, GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto error_free;
+ /* Calculate the required number of entries for the SG table */
+ curr_page = start_page;
+ nents = 1;
+ left_size_to_export = exported_size;
+ left_size_in_page = page_size - offset;
+ left_size_in_dma_seg = dma_max_seg_size;
+ next_sg_entry = false;
- cur_page = 0;
+ while (true) {
+ size = min3(left_size_to_export, left_size_in_page, left_size_in_dma_seg);
+ left_size_to_export -= size;
+ left_size_in_page -= size;
+ left_size_in_dma_seg -= size;
- if (page_size > dma_max_seg_size) {
- u64 size_left, cur_device_address = 0;
+ if (!left_size_to_export)
+ break;
- size_left = page_size;
+ if (!left_size_in_page) {
+ /* left_size_to_export is not zero so there must be another page */
+ if (pages[curr_page] + page_size != pages[curr_page + 1])
+ next_sg_entry = true;
- /* Need to split each page into the number of chunks of
- * dma_max_seg_size
- */
- for_each_sgtable_dma_sg(sgt, sg, i) {
- if (size_left == page_size)
- cur_device_address =
- pages[cur_page] - prop->dram_base_address;
- else
- cur_device_address += dma_max_seg_size;
+ ++curr_page;
+ left_size_in_page = page_size;
+ }
- chunk_size = min(size_left, dma_max_seg_size);
+ if (!left_size_in_dma_seg) {
+ next_sg_entry = true;
+ left_size_in_dma_seg = dma_max_seg_size;
+ }
+
+ if (next_sg_entry) {
+ ++nents;
+ next_sg_entry = false;
+ }
+ }
- bar_address = hdev->dram_pci_bar_start + cur_device_address;
+ rc = sg_alloc_table(sgt, nents, GFP_KERNEL | __GFP_ZERO);
+ if (rc)
+ goto err_free_sgt;
- rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
- if (rc)
- goto error_unmap;
+ /* Prepare the SG table entries */
+ curr_page = start_page;
+ device_address = pages[curr_page] + offset;
+ left_size_to_export = exported_size;
+ left_size_in_page = page_size - offset;
+ left_size_in_dma_seg = dma_max_seg_size;
+ next_sg_entry = false;
- if (size_left > dma_max_seg_size) {
- size_left -= dma_max_seg_size;
- } else {
- cur_page++;
- size_left = page_size;
+ for_each_sgtable_dma_sg(sgt, sg, i) {
+ bar_address = hdev->dram_pci_bar_start + (device_address - prop->dram_base_address);
+ chunk_size = 0;
+
+ for ( ; curr_page < npages ; ++curr_page) {
+ size = min3(left_size_to_export, left_size_in_page, left_size_in_dma_seg);
+ chunk_size += size;
+ left_size_to_export -= size;
+ left_size_in_page -= size;
+ left_size_in_dma_seg -= size;
+
+ if (!left_size_to_export)
+ break;
+
+ if (!left_size_in_page) {
+ /* left_size_to_export is not zero so there must be another page */
+ if (pages[curr_page] + page_size != pages[curr_page + 1]) {
+ device_address = pages[curr_page + 1];
+ next_sg_entry = true;
+ }
+
+ left_size_in_page = page_size;
}
- }
- } else {
- /* Merge pages and put them into the scatterlist */
- for_each_sgtable_dma_sg(sgt, sg, i) {
- chunk_size = page_size;
- for (j = cur_page + 1 ; j < npages ; j++) {
- if (pages[j - 1] + page_size != pages[j] ||
- chunk_size + page_size > dma_max_seg_size)
- break;
-
- chunk_size += page_size;
+
+ if (!left_size_in_dma_seg) {
+ /*
+ * Skip setting a new device address if already moving to a page
+ * which is not contiguous with the current page.
+ */
+ if (!next_sg_entry) {
+ device_address += chunk_size;
+ next_sg_entry = true;
+ }
+
+ left_size_in_dma_seg = dma_max_seg_size;
}
- bar_address = hdev->dram_pci_bar_start +
- (pages[cur_page] - prop->dram_base_address);
+ if (next_sg_entry) {
+ next_sg_entry = false;
+ break;
+ }
+ }
- rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
- if (rc)
- goto error_unmap;
+ rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
+ if (rc)
+ goto err_unmap;
+ }
- cur_page = j;
- }
+ /* There should be nothing left to export exactly after looping over all SG elements */
+ if (left_size_to_export) {
+ dev_err(hdev->dev,
+ "left size to export %#llx after initializing %u SG elements\n",
+ left_size_to_export, sgt->nents);
+ rc = -ENOMEM;
+ goto err_unmap;
}
- /* Because we are not going to include a CPU list we want to have some
- * chance that other users will detect this by setting the orig_nents
- * to 0 and using only nents (length of DMA list) when going over the
- * sgl
+ /*
+ * Because we are not going to include a CPU list, we want to have some chance that other
+ * users will detect this when going over SG table, by setting the orig_nents to 0 and using
+ * only nents (length of DMA list).
*/
sgt->orig_nents = 0;
+ dev_dbg(hdev->dev, "prepared SG table with %u entries for importer %s\n",
+ nents, dev_name(dev));
+ for_each_sgtable_dma_sg(sgt, sg, i)
+ dev_dbg(hdev->dev,
+ "SG entry %d: address %#llx, length %#x\n",
+ i, sg_dma_address(sg), sg_dma_len(sg));
+
return sgt;
-error_unmap:
+err_unmap:
for_each_sgtable_dma_sg(sgt, sg, i) {
if (!sg_dma_len(sg))
continue;
- dma_unmap_resource(dev, sg_dma_address(sg),
- sg_dma_len(sg), dir,
+ dma_unmap_resource(dev, sg_dma_address(sg), sg_dma_len(sg), dir,
DMA_ATTR_SKIP_CPU_SYNC);
}
sg_free_table(sgt);
-error_free:
+err_free_sgt:
kfree(sgt);
return ERR_PTR(rc);
}
@@ -1677,7 +1714,7 @@ static int hl_dmabuf_attach(struct dma_buf *dmabuf,
hl_dmabuf = dmabuf->priv;
hdev = hl_dmabuf->ctx->hdev;
- rc = pci_p2pdma_distance_many(hdev->pdev, &attachment->dev, 1, true);
+ rc = pci_p2pdma_distance(hdev->pdev, attachment->dev, true);
if (rc < 0)
attachment->peer2peer = false;
@@ -1687,6 +1724,7 @@ static int hl_dmabuf_attach(struct dma_buf *dmabuf,
static struct sg_table *hl_map_dmabuf(struct dma_buf_attachment *attachment,
enum dma_data_direction dir)
{
+ u64 *pages, npages, page_size, exported_size, offset;
struct dma_buf *dma_buf = attachment->dmabuf;
struct hl_vm_phys_pg_pack *phys_pg_pack;
struct hl_dmabuf_priv *hl_dmabuf;
@@ -1695,28 +1733,28 @@ static struct sg_table *hl_map_dmabuf(struct dma_buf_attachment *attachment,
hl_dmabuf = dma_buf->priv;
hdev = hl_dmabuf->ctx->hdev;
- phys_pg_pack = hl_dmabuf->phys_pg_pack;
if (!attachment->peer2peer) {
dev_dbg(hdev->dev, "Failed to map dmabuf because p2p is disabled\n");
return ERR_PTR(-EPERM);
}
- if (phys_pg_pack)
- sgt = alloc_sgt_from_device_pages(hdev,
- phys_pg_pack->pages,
- phys_pg_pack->npages,
- phys_pg_pack->page_size,
- attachment->dev,
- dir);
- else
- sgt = alloc_sgt_from_device_pages(hdev,
- &hl_dmabuf->device_address,
- 1,
- hl_dmabuf->dmabuf->size,
- attachment->dev,
- dir);
+ exported_size = hl_dmabuf->dmabuf->size;
+ offset = hl_dmabuf->offset;
+ phys_pg_pack = hl_dmabuf->phys_pg_pack;
+
+ if (phys_pg_pack) {
+ pages = phys_pg_pack->pages;
+ npages = phys_pg_pack->npages;
+ page_size = phys_pg_pack->page_size;
+ } else {
+ pages = &hl_dmabuf->device_phys_addr;
+ npages = 1;
+ page_size = hl_dmabuf->dmabuf->size;
+ }
+ sgt = alloc_sgt_from_device_pages(hdev, pages, npages, page_size, exported_size, offset,
+ attachment->dev, dir);
if (IS_ERR(sgt))
dev_err(hdev->dev, "failed (%ld) to initialize sgt for dmabuf\n", PTR_ERR(sgt));
@@ -1748,20 +1786,64 @@ static void hl_unmap_dmabuf(struct dma_buf_attachment *attachment,
kfree(sgt);
}
-static void hl_release_dmabuf(struct dma_buf *dmabuf)
+static struct hl_vm_hash_node *memhash_node_export_get(struct hl_ctx *ctx, u64 addr)
{
- struct hl_dmabuf_priv *hl_dmabuf = dmabuf->priv;
- struct hl_ctx *ctx = hl_dmabuf->ctx;
struct hl_device *hdev = ctx->hdev;
- struct hl_vm *vm = &hdev->vm;
+ struct hl_vm_hash_node *hnode;
- if (hl_dmabuf->phys_pg_pack) {
- spin_lock(&vm->idr_lock);
- hl_dmabuf->phys_pg_pack->exporting_cnt--;
- spin_unlock(&vm->idr_lock);
+ /* get the memory handle */
+ mutex_lock(&ctx->mem_hash_lock);
+ hnode = get_vm_hash_node_locked(ctx, addr);
+ if (!hnode) {
+ mutex_unlock(&ctx->mem_hash_lock);
+ dev_dbg(hdev->dev, "map address %#llx not found\n", addr);
+ return ERR_PTR(-EINVAL);
}
- hl_ctx_put(hl_dmabuf->ctx);
+ if (upper_32_bits(hnode->handle)) {
+ mutex_unlock(&ctx->mem_hash_lock);
+ dev_dbg(hdev->dev, "invalid handle %#llx for map address %#llx\n",
+ hnode->handle, addr);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /*
+ * node found, increase export count so this memory cannot be unmapped
+ * and the hash node cannot be deleted.
+ */
+ hnode->export_cnt++;
+ mutex_unlock(&ctx->mem_hash_lock);
+
+ return hnode;
+}
+
+static void memhash_node_export_put(struct hl_ctx *ctx, struct hl_vm_hash_node *hnode)
+{
+ mutex_lock(&ctx->mem_hash_lock);
+ hnode->export_cnt--;
+ mutex_unlock(&ctx->mem_hash_lock);
+}
+
+static void hl_release_dmabuf(struct dma_buf *dmabuf)
+{
+ struct hl_dmabuf_priv *hl_dmabuf = dmabuf->priv;
+ struct hl_ctx *ctx;
+
+ ctx = hl_dmabuf->ctx;
+
+ if (hl_dmabuf->memhash_hnode)
+ memhash_node_export_put(ctx, hl_dmabuf->memhash_hnode);
+
+ atomic_dec(&ctx->hdev->dmabuf_export_cnt);
+ hl_ctx_put(ctx);
+
+ /*
+ * Paired with get_file() in export_dmabuf().
+ * 'ctx' can be still used here to get the file pointer, even after hl_ctx_put() was called,
+ * because releasing the compute device file involves another reference decrement, and it
+ * would be possible only after calling fput().
+ */
+ fput(ctx->hpriv->file_priv->filp);
kfree(hl_dmabuf);
}
@@ -1773,13 +1855,18 @@ static const struct dma_buf_ops habanalabs_dmabuf_ops = {
.release = hl_release_dmabuf,
};
-static int export_dmabuf_common(struct hl_ctx *ctx,
+static int export_dmabuf(struct hl_ctx *ctx,
struct hl_dmabuf_priv *hl_dmabuf,
u64 total_size, int flags, int *dmabuf_fd)
{
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct hl_device *hdev = ctx->hdev;
- int rc, fd;
+ CLASS(get_unused_fd, fd)(flags);
+
+ if (fd < 0) {
+ dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf, %d\n", fd);
+ return fd;
+ }
exp_info.ops = &habanalabs_dmabuf_ops;
exp_info.size = total_size;
@@ -1792,268 +1879,214 @@ static int export_dmabuf_common(struct hl_ctx *ctx,
return PTR_ERR(hl_dmabuf->dmabuf);
}
- fd = dma_buf_fd(hl_dmabuf->dmabuf, flags);
- if (fd < 0) {
- dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf\n");
- rc = fd;
- goto err_dma_buf_put;
- }
-
hl_dmabuf->ctx = ctx;
hl_ctx_get(hl_dmabuf->ctx);
+ atomic_inc(&ctx->hdev->dmabuf_export_cnt);
+
+ /* Get compute device file to enforce release order, such that all exported dma-buf will be
+ * released first and only then the compute device.
+ * Paired with fput() in hl_release_dmabuf().
+ */
+ get_file(ctx->hpriv->file_priv->filp);
*dmabuf_fd = fd;
+ fd_install(take_fd(fd), hl_dmabuf->dmabuf->file);
return 0;
-
-err_dma_buf_put:
- dma_buf_put(hl_dmabuf->dmabuf);
- return rc;
}
-/**
- * export_dmabuf_from_addr() - export a dma-buf object for the given memory
- * address and size.
- * @ctx: pointer to the context structure.
- * @device_addr: device memory physical address.
- * @size: size of device memory.
- * @flags: DMA-BUF file/FD flags.
- * @dmabuf_fd: pointer to result FD that represents the dma-buf object.
- *
- * Create and export a dma-buf object for an existing memory allocation inside
- * the device memory, and return a FD which is associated with the dma-buf
- * object.
- *
- * Return: 0 on success, non-zero for failure.
- */
-static int export_dmabuf_from_addr(struct hl_ctx *ctx, u64 device_addr,
- u64 size, int flags, int *dmabuf_fd)
+static int validate_export_params_common(struct hl_device *hdev, u64 addr, u64 size, u64 offset)
{
- struct hl_dmabuf_priv *hl_dmabuf;
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop;
- u64 bar_address;
- int rc;
-
- prop = &hdev->asic_prop;
-
- if (!IS_ALIGNED(device_addr, PAGE_SIZE)) {
+ if (!PAGE_ALIGNED(addr)) {
dev_dbg(hdev->dev,
- "exported device memory address 0x%llx should be aligned to 0x%lx\n",
- device_addr, PAGE_SIZE);
+ "exported device memory address 0x%llx should be aligned to PAGE_SIZE 0x%lx\n",
+ addr, PAGE_SIZE);
return -EINVAL;
}
- if (size < PAGE_SIZE) {
+ if (!size || !PAGE_ALIGNED(size)) {
dev_dbg(hdev->dev,
- "exported device memory size %llu should be equal to or greater than %lu\n",
+ "exported device memory size %llu should be a multiple of PAGE_SIZE %lu\n",
size, PAGE_SIZE);
return -EINVAL;
}
+ if (!PAGE_ALIGNED(offset)) {
+ dev_dbg(hdev->dev,
+ "exported device memory offset %llu should be a multiple of PAGE_SIZE %lu\n",
+ offset, PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_export_params_no_mmu(struct hl_device *hdev, u64 device_addr, u64 size)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 bar_address;
+ int rc;
+
+ rc = validate_export_params_common(hdev, device_addr, size, 0);
+ if (rc)
+ return rc;
+
if (device_addr < prop->dram_user_base_address ||
- device_addr + size > prop->dram_end_address ||
- device_addr + size < device_addr) {
+ (device_addr + size) > prop->dram_end_address ||
+ (device_addr + size) < device_addr) {
dev_dbg(hdev->dev,
"DRAM memory range 0x%llx (+0x%llx) is outside of DRAM boundaries\n",
device_addr, size);
return -EINVAL;
}
- bar_address = hdev->dram_pci_bar_start +
- (device_addr - prop->dram_base_address);
+ bar_address = hdev->dram_pci_bar_start + (device_addr - prop->dram_base_address);
- if (bar_address + size >
- hdev->dram_pci_bar_start + prop->dram_pci_bar_size ||
- bar_address + size < bar_address) {
+ if ((bar_address + size) > (hdev->dram_pci_bar_start + prop->dram_pci_bar_size) ||
+ (bar_address + size) < bar_address) {
dev_dbg(hdev->dev,
"DRAM memory range 0x%llx (+0x%llx) is outside of PCI BAR boundaries\n",
device_addr, size);
return -EINVAL;
}
- hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL);
- if (!hl_dmabuf)
- return -ENOMEM;
-
- hl_dmabuf->device_address = device_addr;
-
- rc = export_dmabuf_common(ctx, hl_dmabuf, size, flags, dmabuf_fd);
- if (rc)
- goto err_free_dmabuf_wrapper;
-
return 0;
-
-err_free_dmabuf_wrapper:
- kfree(hl_dmabuf);
- return rc;
}
-/**
- * export_dmabuf_from_handle() - export a dma-buf object for the given memory
- * handle.
- * @ctx: pointer to the context structure.
- * @handle: device memory allocation handle.
- * @flags: DMA-BUF file/FD flags.
- * @dmabuf_fd: pointer to result FD that represents the dma-buf object.
- *
- * Create and export a dma-buf object for an existing memory allocation inside
- * the device memory, and return a FD which is associated with the dma-buf
- * object.
- *
- * Return: 0 on success, non-zero for failure.
- */
-static int export_dmabuf_from_handle(struct hl_ctx *ctx, u64 handle, int flags,
- int *dmabuf_fd)
+static int validate_export_params(struct hl_device *hdev, u64 device_addr, u64 size, u64 offset,
+ struct hl_vm_phys_pg_pack *phys_pg_pack)
{
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- struct hl_dmabuf_priv *hl_dmabuf;
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop;
- struct hl_vm *vm = &hdev->vm;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
u64 bar_address;
- int rc, i;
-
- prop = &hdev->asic_prop;
-
- if (upper_32_bits(handle)) {
- dev_dbg(hdev->dev, "no match for handle 0x%llx\n", handle);
- return -EINVAL;
- }
+ int i, rc;
- spin_lock(&vm->idr_lock);
+ rc = validate_export_params_common(hdev, device_addr, size, offset);
+ if (rc)
+ return rc;
- phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, (u32) handle);
- if (!phys_pg_pack) {
- spin_unlock(&vm->idr_lock);
- dev_dbg(hdev->dev, "no match for handle 0x%x\n", (u32) handle);
+ if ((offset + size) > phys_pg_pack->total_size) {
+ dev_dbg(hdev->dev, "offset %#llx and size %#llx exceed total map size %#llx\n",
+ offset, size, phys_pg_pack->total_size);
return -EINVAL;
}
- /* increment now to avoid freeing device memory while exporting */
- phys_pg_pack->exporting_cnt++;
-
- spin_unlock(&vm->idr_lock);
-
- if (phys_pg_pack->vm_type != VM_TYPE_PHYS_PACK) {
- dev_dbg(hdev->dev, "handle 0x%llx does not represent DRAM memory\n", handle);
- rc = -EINVAL;
- goto err_dec_exporting_cnt;
- }
-
for (i = 0 ; i < phys_pg_pack->npages ; i++) {
-
bar_address = hdev->dram_pci_bar_start +
- (phys_pg_pack->pages[i] -
- prop->dram_base_address);
-
- if (bar_address + phys_pg_pack->page_size >
- hdev->dram_pci_bar_start + prop->dram_pci_bar_size ||
- bar_address + phys_pg_pack->page_size < bar_address) {
+ (phys_pg_pack->pages[i] - prop->dram_base_address);
+ if ((bar_address + phys_pg_pack->page_size) >
+ (hdev->dram_pci_bar_start + prop->dram_pci_bar_size) ||
+ (bar_address + phys_pg_pack->page_size) < bar_address) {
dev_dbg(hdev->dev,
"DRAM memory range 0x%llx (+0x%x) is outside of PCI BAR boundaries\n",
- phys_pg_pack->pages[i],
- phys_pg_pack->page_size);
-
- rc = -EINVAL;
- goto err_dec_exporting_cnt;
+ phys_pg_pack->pages[i], phys_pg_pack->page_size);
+ return -EINVAL;
}
}
- hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL);
- if (!hl_dmabuf) {
- rc = -ENOMEM;
- goto err_dec_exporting_cnt;
- }
-
- hl_dmabuf->phys_pg_pack = phys_pg_pack;
-
- rc = export_dmabuf_common(ctx, hl_dmabuf, phys_pg_pack->total_size,
- flags, dmabuf_fd);
- if (rc)
- goto err_free_dmabuf_wrapper;
-
return 0;
+}
-err_free_dmabuf_wrapper:
- kfree(hl_dmabuf);
+static struct hl_vm_phys_pg_pack *get_phys_pg_pack_from_hash_node(struct hl_device *hdev,
+ struct hl_vm_hash_node *hnode)
+{
+ struct hl_vm_phys_pg_pack *phys_pg_pack;
+ struct hl_vm *vm = &hdev->vm;
-err_dec_exporting_cnt:
spin_lock(&vm->idr_lock);
- phys_pg_pack->exporting_cnt--;
+ phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, (u32) hnode->handle);
+ if (!phys_pg_pack) {
+ spin_unlock(&vm->idr_lock);
+ dev_dbg(hdev->dev, "no match for handle 0x%x\n", (u32) hnode->handle);
+ return ERR_PTR(-EINVAL);
+ }
+
spin_unlock(&vm->idr_lock);
- return rc;
+ if (phys_pg_pack->vm_type != VM_TYPE_PHYS_PACK) {
+ dev_dbg(hdev->dev, "handle 0x%llx does not represent DRAM memory\n", hnode->handle);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return phys_pg_pack;
}
-static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args)
+/**
+ * export_dmabuf_from_addr() - export a dma-buf object for the given memory
+ * address and size.
+ * @ctx: pointer to the context structure.
+ * @addr: device address.
+ * @size: size of device memory to export.
+ * @offset: the offset into the buffer from which to start exporting
+ * @flags: DMA-BUF file/FD flags.
+ * @dmabuf_fd: pointer to result FD that represents the dma-buf object.
+ *
+ * Create and export a dma-buf object for an existing memory allocation inside
+ * the device memory, and return a FD which is associated with the dma-buf
+ * object.
+ *
+ * Return: 0 on success, non-zero for failure.
+ */
+static int export_dmabuf_from_addr(struct hl_ctx *ctx, u64 addr, u64 size, u64 offset,
+ int flags, int *dmabuf_fd)
{
- struct hl_device *hdev = hpriv->hdev;
- u64 block_handle, device_addr = 0;
- struct hl_ctx *ctx = hpriv->ctx;
- u32 handle = 0, block_size;
+ struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
+ struct hl_vm_hash_node *hnode = NULL;
+ struct asic_fixed_properties *prop;
+ struct hl_dmabuf_priv *hl_dmabuf;
+ struct hl_device *hdev;
int rc;
- switch (args->in.op) {
- case HL_MEM_OP_ALLOC:
- if (args->in.alloc.mem_size == 0) {
- dev_err(hdev->dev, "alloc size must be larger than 0\n");
- rc = -EINVAL;
- goto out;
- }
-
- /* Force contiguous as there are no real MMU
- * translations to overcome physical memory gaps
- */
- args->in.flags |= HL_MEM_CONTIGUOUS;
- rc = alloc_device_memory(ctx, &args->in, &handle);
+ hdev = ctx->hdev;
+ prop = &hdev->asic_prop;
- memset(args, 0, sizeof(*args));
- args->out.handle = (__u64) handle;
- break;
+ /* offset must be 0 in devices without virtual memory support */
+ if (!prop->dram_supports_virtual_memory && offset) {
+ dev_dbg(hdev->dev, "offset is not allowed in device without virtual memory\n");
+ return -EINVAL;
+ }
- case HL_MEM_OP_FREE:
- rc = free_device_memory(ctx, &args->in);
- break;
+ hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL);
+ if (!hl_dmabuf)
+ return -ENOMEM;
- case HL_MEM_OP_MAP:
- if (args->in.flags & HL_MEM_USERPTR) {
- dev_err(hdev->dev, "Failed to map host memory when MMU is disabled\n");
- rc = -EPERM;
- } else {
- rc = get_paddr_from_handle(ctx, &args->in, &device_addr);
- memset(args, 0, sizeof(*args));
- args->out.device_virt_addr = device_addr;
+ if (prop->dram_supports_virtual_memory) {
+ hnode = memhash_node_export_get(ctx, addr);
+ if (IS_ERR(hnode)) {
+ rc = PTR_ERR(hnode);
+ goto err_free_dmabuf_wrapper;
}
+ phys_pg_pack = get_phys_pg_pack_from_hash_node(hdev, hnode);
+ if (IS_ERR(phys_pg_pack)) {
+ rc = PTR_ERR(phys_pg_pack);
+ goto dec_memhash_export_cnt;
+ }
+ rc = validate_export_params(hdev, addr, size, offset, phys_pg_pack);
+ if (rc)
+ goto dec_memhash_export_cnt;
- break;
-
- case HL_MEM_OP_UNMAP:
- rc = 0;
- break;
+ hl_dmabuf->phys_pg_pack = phys_pg_pack;
+ hl_dmabuf->memhash_hnode = hnode;
+ hl_dmabuf->offset = offset;
+ } else {
+ rc = validate_export_params_no_mmu(hdev, addr, size);
+ if (rc)
+ goto err_free_dmabuf_wrapper;
- case HL_MEM_OP_MAP_BLOCK:
- rc = map_block(hdev, args->in.map_block.block_addr, &block_handle, &block_size);
- args->out.block_handle = block_handle;
- args->out.block_size = block_size;
- break;
+ hl_dmabuf->device_phys_addr = addr;
+ }
- case HL_MEM_OP_EXPORT_DMABUF_FD:
- dev_err(hdev->dev, "Failed to export dma-buf object when MMU is disabled\n");
- rc = -EPERM;
- break;
+ rc = export_dmabuf(ctx, hl_dmabuf, size, flags, dmabuf_fd);
+ if (rc)
+ goto dec_memhash_export_cnt;
- case HL_MEM_OP_TS_ALLOC:
- rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle);
- break;
- default:
- dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n");
- rc = -EINVAL;
- break;
- }
+ return 0;
-out:
+dec_memhash_export_cnt:
+ if (prop->dram_supports_virtual_memory)
+ memhash_node_export_put(ctx, hnode);
+err_free_dmabuf_wrapper:
+ kfree(hl_dmabuf);
return rc;
}
@@ -2070,19 +2103,20 @@ static int hl_ts_mmap(struct hl_mmap_mem_buf *buf, struct vm_area_struct *vma, v
{
struct hl_ts_buff *ts_buff = buf->private;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY | VM_NORESERVE;
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY | VM_NORESERVE);
return remap_vmalloc_range(vma, ts_buff->user_buff_address, 0);
}
static int hl_ts_alloc_buf(struct hl_mmap_mem_buf *buf, gfp_t gfp, void *args)
{
struct hl_ts_buff *ts_buff = NULL;
- u32 size, num_elements;
+ u32 num_elements;
+ size_t size;
void *p;
num_elements = *(u32 *)args;
- ts_buff = kzalloc(sizeof(*ts_buff), GFP_KERNEL);
+ ts_buff = kzalloc(sizeof(*ts_buff), gfp);
if (!ts_buff)
return -ENOMEM;
@@ -2097,7 +2131,7 @@ static int hl_ts_alloc_buf(struct hl_mmap_mem_buf *buf, gfp_t gfp, void *args)
/* Allocate the internal kernel buffer */
size = num_elements * sizeof(struct hl_user_pending_interrupt);
- p = vmalloc(size);
+ p = vzalloc(size);
if (!p)
goto free_user_buff;
@@ -2127,11 +2161,11 @@ static struct hl_mmap_mem_buf_behavior hl_ts_behavior = {
* allocate_timestamps_buffers() - allocate timestamps buffers
* This function will allocate ts buffer that will later on be mapped to the user
* in order to be able to read the timestamp.
- * in additon it'll allocate an extra buffer for registration management.
+ * in addition it'll allocate an extra buffer for registration management.
* since we cannot fail during registration for out-of-memory situation, so
* we'll prepare a pool which will be used as user interrupt nodes and instead
* of dynamically allocating nodes while registration we'll pick the node from
- * this pool. in addtion it'll add node to the mapping hash which will be used
+ * this pool. in addition it'll add node to the mapping hash which will be used
* to map user ts buffer to the internal kernel ts buffer.
* @hpriv: pointer to the private data of the fd
* @args: ioctl input
@@ -2157,8 +2191,9 @@ static int allocate_timestamps_buffers(struct hl_fpriv *hpriv, struct hl_mem_in
return 0;
}
-int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
+int hl_mem_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
{
+ struct hl_fpriv *hpriv = file_priv->driver_priv;
enum hl_device_status status;
union hl_mem_args *args = data;
struct hl_device *hdev = hpriv->hdev;
@@ -2168,15 +2203,12 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
int rc, dmabuf_fd = -EBADF;
if (!hl_device_operational(hdev, &status)) {
- dev_warn_ratelimited(hdev->dev,
+ dev_dbg_ratelimited(hdev->dev,
"Device is %s. Can't execute MEMORY IOCTL\n",
hdev->status[status]);
return -EBUSY;
}
- if (!hdev->mmu_enable)
- return mem_ioctl_no_mmu(hpriv, args);
-
switch (args->in.op) {
case HL_MEM_OP_ALLOC:
if (args->in.alloc.mem_size == 0) {
@@ -2257,17 +2289,12 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
break;
case HL_MEM_OP_EXPORT_DMABUF_FD:
- if (hdev->asic_prop.dram_supports_virtual_memory)
- rc = export_dmabuf_from_handle(ctx,
- args->in.export_dmabuf_fd.handle,
- args->in.flags,
- &dmabuf_fd);
- else
- rc = export_dmabuf_from_addr(ctx,
- args->in.export_dmabuf_fd.handle,
- args->in.export_dmabuf_fd.mem_size,
- args->in.flags,
- &dmabuf_fd);
+ rc = export_dmabuf_from_addr(ctx,
+ args->in.export_dmabuf_fd.addr,
+ args->in.export_dmabuf_fd.mem_size,
+ args->in.export_dmabuf_fd.offset,
+ args->in.flags,
+ &dmabuf_fd);
memset(args, 0, sizeof(*args));
args->out.fd = dmabuf_fd;
break;
@@ -2296,13 +2323,11 @@ static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size,
return -EFAULT;
}
- userptr->pages = kvmalloc_array(npages, sizeof(*userptr->pages),
- GFP_KERNEL);
+ userptr->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
if (!userptr->pages)
return -ENOMEM;
- rc = pin_user_pages_fast(start, npages,
- FOLL_FORCE | FOLL_WRITE | FOLL_LONGTERM,
+ rc = pin_user_pages_fast(start, npages, FOLL_WRITE | FOLL_LONGTERM,
userptr->pages);
if (rc != npages) {
@@ -2312,7 +2337,7 @@ static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size,
if (rc < 0)
goto destroy_pages;
npages = rc;
- rc = -EFAULT;
+ rc = -ENOMEM;
goto put_pages;
}
userptr->npages = npages;
@@ -2416,7 +2441,7 @@ void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr)
hl_debugfs_remove_userptr(hdev, userptr);
if (userptr->dma_mapped)
- hdev->asic_funcs->hl_dma_unmap_sgtable(hdev, userptr->sgt, userptr->dir);
+ hl_dma_unmap_sgtable(hdev, userptr->sgt, userptr->dir);
unpin_user_pages_dirty_lock(userptr->pages, userptr->npages, true);
kvfree(userptr->pages);
@@ -2497,24 +2522,20 @@ static int va_range_init(struct hl_device *hdev, struct hl_va_range **va_ranges,
/*
* PAGE_SIZE alignment
- * it is the callers responsibility to align the addresses if the
+ * it is the caller's responsibility to align the addresses if the
* page size is not a power of 2
*/
if (is_power_of_2(page_size)) {
- if (start & (PAGE_SIZE - 1)) {
- start &= PAGE_MASK;
- start += PAGE_SIZE;
- }
+ start = round_up(start, page_size);
/*
* The end of the range is inclusive, hence we need to align it
* to the end of the last full page in the range. For example if
* end = 0x3ff5 with page size 0x1000, we need to align it to
- * 0x2fff. The remainig 0xff5 bytes do not form a full page.
+ * 0x2fff. The remaining 0xff5 bytes do not form a full page.
*/
- if ((end + 1) & (PAGE_SIZE - 1))
- end = ((end + 1) & PAGE_MASK) - 1;
+ end = round_down(end + 1, page_size) - 1;
}
if (start >= end) {
@@ -2682,13 +2703,10 @@ int hl_vm_ctx_init(struct hl_ctx *ctx)
atomic64_set(&ctx->dram_phys_mem, 0);
/*
- * - If MMU is enabled, init the ranges as usual.
- * - If MMU is disabled, in case of host mapping, the returned address
- * is the given one.
* In case of DRAM mapping, the returned address is the physical
* address of the memory related to the given handle.
*/
- if (!ctx->hdev->mmu_enable)
+ if (ctx->hdev->mmu_disable)
return 0;
dram_range_start = prop->dmmu.start_addr;
@@ -2738,7 +2756,7 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx)
struct hl_mem_in args;
int i;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return;
hl_debugfs_remove_ctx_mem_hash(hdev, ctx);
@@ -2759,13 +2777,13 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx)
unmap_device_va(ctx, &args, true);
}
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
/* invalidate the cache once after the unmapping loop */
hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
hl_mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK);
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
INIT_LIST_HEAD(&free_list);
diff --git a/drivers/misc/habanalabs/common/memory_mgr.c b/drivers/accel/habanalabs/common/memory_mgr.c
index 56df962d2f3c..4401beb99e42 100644
--- a/drivers/misc/habanalabs/common/memory_mgr.c
+++ b/drivers/accel/habanalabs/common/memory_mgr.c
@@ -11,7 +11,7 @@
* hl_mmap_mem_buf_get - increase the buffer refcount and return a pointer to
* the buffer descriptor.
*
- * @mmg: parent unifed memory manager
+ * @mmg: parent unified memory manager
* @handle: requested buffer handle
*
* Find the buffer in the store and return a pointer to its descriptor.
@@ -25,8 +25,7 @@ struct hl_mmap_mem_buf *hl_mmap_mem_buf_get(struct hl_mem_mgr *mmg, u64 handle)
buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
if (!buf) {
spin_unlock(&mmg->lock);
- dev_warn(mmg->dev,
- "Buff get failed, no match to handle %#llx\n", handle);
+ dev_dbg(mmg->dev, "Buff get failed, no match to handle %#llx\n", handle);
return NULL;
}
kref_get(&buf->refcount);
@@ -104,7 +103,7 @@ int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf)
* hl_mmap_mem_buf_put_handle - decrease the reference to the buffer with the
* given handle.
*
- * @mmg: parent unifed memory manager
+ * @mmg: parent unified memory manager
* @handle: requested buffer handle
*
* Decrease the reference to the buffer, and release it if it was the last one.
@@ -137,7 +136,7 @@ int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle)
/**
* hl_mmap_mem_buf_alloc - allocate a new mappable buffer
*
- * @mmg: parent unifed memory manager
+ * @mmg: parent unified memory manager
* @behavior: behavior object describing this buffer polymorphic behavior
* @gfp: gfp flags to use for the memory allocations
* @args: additional args passed to behavior->alloc
@@ -222,7 +221,7 @@ static const struct vm_operations_struct hl_mmap_mem_buf_vm_ops = {
/**
* hl_mem_mgr_mmap - map the given buffer to the user
*
- * @mmg: unifed memory manager
+ * @mmg: unified memory manager
* @vma: the vma object for which mmap was closed.
* @args: additional args passed to behavior->mmap
*
@@ -260,13 +259,8 @@ int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
goto put_mem;
}
-#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK
- if (!access_ok(VERIFY_WRITE, (void __user *)(uintptr_t)vma->vm_start,
- user_mem_size)) {
-#else
if (!access_ok((void __user *)(uintptr_t)vma->vm_start,
user_mem_size)) {
-#endif
dev_err(mmg->dev, "%s: User pointer is invalid - 0x%lx\n",
buf->behavior->topic, vma->vm_start);
@@ -276,7 +270,7 @@ int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
if (atomic_cmpxchg(&buf->mmap, 0, 1)) {
dev_err(mmg->dev,
- "%s, Memory mmap failed, already mmaped to user\n",
+ "%s, Memory mmap failed, already mapped to user\n",
buf->behavior->topic);
rc = -EINVAL;
goto put_mem;
@@ -319,31 +313,75 @@ void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg)
idr_init(&mmg->handles);
}
+static void hl_mem_mgr_fini_stats_reset(struct hl_mem_mgr_fini_stats *stats)
+{
+ if (!stats)
+ return;
+
+ memset(stats, 0, sizeof(*stats));
+}
+
+static void hl_mem_mgr_fini_stats_inc(u64 mem_id, struct hl_mem_mgr_fini_stats *stats)
+{
+ if (!stats)
+ return;
+
+ switch (mem_id) {
+ case HL_MMAP_TYPE_CB:
+ ++stats->n_busy_cb;
+ break;
+ case HL_MMAP_TYPE_TS_BUFF:
+ ++stats->n_busy_ts;
+ break;
+ default:
+ /* we currently store only CB/TS so this shouldn't happen */
+ ++stats->n_busy_other;
+ }
+}
+
/**
* hl_mem_mgr_fini - release unified memory manager
*
- * @mmg: parent unifed memory manager
+ * @mmg: parent unified memory manager
+ * @stats: if non-NULL, will return some counters for handles that could not be removed.
*
* Release the unified memory manager. Shall be called from an interrupt context.
*/
-void hl_mem_mgr_fini(struct hl_mem_mgr *mmg)
+void hl_mem_mgr_fini(struct hl_mem_mgr *mmg, struct hl_mem_mgr_fini_stats *stats)
{
struct hl_mmap_mem_buf *buf;
struct idr *idp;
const char *topic;
+ u64 mem_id;
u32 id;
+ hl_mem_mgr_fini_stats_reset(stats);
+
idp = &mmg->handles;
idr_for_each_entry(idp, buf, id) {
topic = buf->behavior->topic;
- if (hl_mmap_mem_buf_put(buf) != 1)
+ mem_id = buf->behavior->mem_id;
+ if (hl_mmap_mem_buf_put(buf) != 1) {
dev_err(mmg->dev,
"%s: Buff handle %u for CTX is still alive\n",
topic, id);
+ hl_mem_mgr_fini_stats_inc(mem_id, stats);
+ }
}
+}
- /* TODO: can it happen that some buffer is still in use at this point? */
+/**
+ * hl_mem_mgr_idr_destroy() - destroy memory manager IDR.
+ * @mmg: parent unified memory manager
+ *
+ * Destroy the memory manager IDR.
+ * Shall be called when IDR is empty and no memory buffers are in use.
+ */
+void hl_mem_mgr_idr_destroy(struct hl_mem_mgr *mmg)
+{
+ if (!idr_is_empty(&mmg->handles))
+ dev_crit(mmg->dev, "memory manager IDR is destroyed while it is not empty!\n");
idr_destroy(&mmg->handles);
}
diff --git a/drivers/accel/habanalabs/common/mmu/Makefile b/drivers/accel/habanalabs/common/mmu/Makefile
new file mode 100644
index 000000000000..f4b815bf4f7d
--- /dev/null
+++ b/drivers/accel/habanalabs/common/mmu/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+HL_COMMON_MMU_FILES := common/mmu/mmu.o common/mmu/mmu_v1.o \
+ common/mmu/mmu_v2.o common/mmu/mmu_v2_hr.o
diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/accel/habanalabs/common/mmu/mmu.c
index 60740de47b34..79823facce7f 100644
--- a/drivers/misc/habanalabs/common/mmu/mmu.c
+++ b/drivers/accel/habanalabs/common/mmu/mmu.c
@@ -6,9 +6,12 @@
*/
#include <linux/slab.h>
+#include <linux/pci.h>
#include "../habanalabs.h"
+#include <trace/events/habanalabs.h>
+
/**
* hl_mmu_get_funcs() - get MMU functions structure
* @hdev: habanalabs device structure.
@@ -42,9 +45,11 @@ int hl_mmu_init(struct hl_device *hdev)
{
int rc = -EOPNOTSUPP;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return 0;
+ mutex_init(&hdev->mmu_lock);
+
if (hdev->mmu_func[MMU_DR_PGT].init != NULL) {
rc = hdev->mmu_func[MMU_DR_PGT].init(hdev);
if (rc)
@@ -78,7 +83,7 @@ fini_dr_mmu:
*/
void hl_mmu_fini(struct hl_device *hdev)
{
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return;
if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)
@@ -86,6 +91,8 @@ void hl_mmu_fini(struct hl_device *hdev)
if (hdev->mmu_func[MMU_HR_PGT].fini != NULL)
hdev->mmu_func[MMU_HR_PGT].fini(hdev);
+
+ mutex_destroy(&hdev->mmu_lock);
}
/**
@@ -101,11 +108,9 @@ int hl_mmu_ctx_init(struct hl_ctx *ctx)
struct hl_device *hdev = ctx->hdev;
int rc = -EOPNOTSUPP;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return 0;
- mutex_init(&ctx->mmu_lock);
-
if (hdev->mmu_func[MMU_DR_PGT].ctx_init != NULL) {
rc = hdev->mmu_func[MMU_DR_PGT].ctx_init(ctx);
if (rc)
@@ -141,7 +146,7 @@ void hl_mmu_ctx_fini(struct hl_ctx *ctx)
{
struct hl_device *hdev = ctx->hdev;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return;
if (hdev->mmu_func[MMU_DR_PGT].ctx_fini != NULL)
@@ -149,8 +154,6 @@ void hl_mmu_ctx_fini(struct hl_ctx *ctx)
if (hdev->mmu_func[MMU_HR_PGT].ctx_fini != NULL)
hdev->mmu_func[MMU_HR_PGT].ctx_fini(ctx);
-
- mutex_destroy(&ctx->mmu_lock);
}
/*
@@ -231,7 +234,7 @@ int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, bool flu
u64 real_virt_addr;
bool is_dram_addr;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return 0;
is_dram_addr = hl_is_dram_va(hdev, virt_addr);
@@ -259,6 +262,9 @@ int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, bool flu
if (flush_pte)
mmu_funcs->flush(ctx);
+ if (trace_habanalabs_mmu_unmap_enabled() && !rc)
+ trace_habanalabs_mmu_unmap(&hdev->pdev->dev, virt_addr, 0, page_size, flush_pte);
+
return rc;
}
@@ -296,7 +302,7 @@ int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_s
bool is_dram_addr;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return 0;
is_dram_addr = hl_is_dram_va(hdev, virt_addr);
@@ -344,6 +350,8 @@ int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_s
if (flush_pte)
mmu_funcs->flush(ctx);
+ trace_habanalabs_mmu_map(&hdev->pdev->dev, virt_addr, phys_addr, page_size, flush_pte);
+
return 0;
err:
@@ -403,6 +411,8 @@ int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr,
dev_err(hdev->dev,
"Map failed for va 0x%llx to pa 0x%llx\n",
curr_va, curr_pa);
+ /* last mapping failed so don't try to unmap it - reduce off by page_size */
+ off -= page_size;
goto unmap;
}
}
@@ -463,46 +473,6 @@ int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size)
return rc;
}
-/*
- * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out
- *
- * @ctx: pointer to the context structure
- *
- */
-void hl_mmu_swap_out(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
-
- if (!hdev->mmu_enable)
- return;
-
- if (hdev->mmu_func[MMU_DR_PGT].swap_out != NULL)
- hdev->mmu_func[MMU_DR_PGT].swap_out(ctx);
-
- if (hdev->mmu_func[MMU_HR_PGT].swap_out != NULL)
- hdev->mmu_func[MMU_HR_PGT].swap_out(ctx);
-}
-
-/*
- * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in
- *
- * @ctx: pointer to the context structure
- *
- */
-void hl_mmu_swap_in(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
-
- if (!hdev->mmu_enable)
- return;
-
- if (hdev->mmu_func[MMU_DR_PGT].swap_in != NULL)
- hdev->mmu_func[MMU_DR_PGT].swap_in(ctx);
-
- if (hdev->mmu_func[MMU_HR_PGT].swap_in != NULL)
- hdev->mmu_func[MMU_HR_PGT].swap_in(ctx);
-}
-
static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr,
struct hl_mmu_hop_info *hops,
u64 *phys_addr)
@@ -531,8 +501,8 @@ static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr,
u32 page_off;
/*
- * Bit arithmetics cannot be used for non power of two page
- * sizes. In addition, since bit arithmetics is not used,
+ * Bit arithmetic cannot be used for non power of two page
+ * sizes. In addition, since bit arithmetic is not used,
* we cannot ignore dram base. All that shall be considered.
*/
@@ -585,7 +555,7 @@ int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
int pgt_residency, rc;
bool is_dram_addr;
- if (!hdev->mmu_enable)
+ if (hdev->mmu_disable)
return -EOPNOTSUPP;
prop = &hdev->asic_prop;
@@ -600,9 +570,9 @@ int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;
mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
rc = mmu_funcs->get_tlb_info(ctx, virt_addr, hops);
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
if (rc)
return rc;
@@ -616,7 +586,9 @@ int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
int hl_mmu_if_set_funcs(struct hl_device *hdev)
{
- if (!hdev->mmu_enable)
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ if (hdev->mmu_disable)
return 0;
switch (hdev->asic_type) {
@@ -626,9 +598,12 @@ int hl_mmu_if_set_funcs(struct hl_device *hdev)
hl_mmu_v1_set_funcs(hdev, &hdev->mmu_func[MMU_DR_PGT]);
break;
case ASIC_GAUDI2:
- case ASIC_GAUDI2_SEC:
- /* MMUs in Gaudi2 are always host resident */
- hl_mmu_v2_hr_set_funcs(hdev, &hdev->mmu_func[MMU_HR_PGT]);
+ case ASIC_GAUDI2B:
+ case ASIC_GAUDI2C:
+ case ASIC_GAUDI2D:
+ hl_mmu_v2_set_funcs(hdev, &hdev->mmu_func[MMU_DR_PGT]);
+ if (prop->pmmu.host_resident)
+ hl_mmu_v2_hr_set_funcs(hdev, &hdev->mmu_func[MMU_HR_PGT]);
break;
default:
dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
@@ -670,7 +645,10 @@ int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags)
rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags);
if (rc)
- dev_err_ratelimited(hdev->dev, "MMU cache invalidation failed\n");
+ dev_err_ratelimited(hdev->dev,
+ "%s: %s cache invalidation failed, rc=%d\n",
+ dev_name(&hdev->pdev->dev),
+ flags == VM_TYPE_USERPTR ? "PMMU" : "HMMU", rc);
return rc;
}
@@ -683,25 +661,28 @@ int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,
rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, is_hard, flags,
asid, va, size);
if (rc)
- dev_err_ratelimited(hdev->dev, "MMU cache range invalidation failed\n");
+ dev_err_ratelimited(hdev->dev,
+ "%s: %s cache range invalidation failed: va=%#llx, size=%llu, rc=%d",
+ dev_name(&hdev->pdev->dev), flags == VM_TYPE_USERPTR ? "PMMU" : "HMMU",
+ va, size, rc);
return rc;
}
static void hl_mmu_prefetch_work_function(struct work_struct *work)
{
- struct hl_prefetch_work *pfw = container_of(work, struct hl_prefetch_work, pf_work);
+ struct hl_prefetch_work *pfw = container_of(work, struct hl_prefetch_work, prefetch_work);
struct hl_ctx *ctx = pfw->ctx;
+ struct hl_device *hdev = ctx->hdev;
- if (!hl_device_operational(ctx->hdev, NULL))
+ if (!hl_device_operational(hdev, NULL))
goto put_ctx;
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
- ctx->hdev->asic_funcs->mmu_prefetch_cache_range(ctx, pfw->flags, pfw->asid,
- pfw->va, pfw->size);
+ hdev->asic_funcs->mmu_prefetch_cache_range(ctx, pfw->flags, pfw->asid, pfw->va, pfw->size);
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
put_ctx:
/*
@@ -714,25 +695,25 @@ put_ctx:
int hl_mmu_prefetch_cache_range(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size)
{
- struct hl_prefetch_work *handle_pf_work;
+ struct hl_prefetch_work *handle_prefetch_work;
- handle_pf_work = kmalloc(sizeof(*handle_pf_work), GFP_KERNEL);
- if (!handle_pf_work)
+ handle_prefetch_work = kmalloc(sizeof(*handle_prefetch_work), GFP_KERNEL);
+ if (!handle_prefetch_work)
return -ENOMEM;
- INIT_WORK(&handle_pf_work->pf_work, hl_mmu_prefetch_work_function);
- handle_pf_work->ctx = ctx;
- handle_pf_work->va = va;
- handle_pf_work->size = size;
- handle_pf_work->flags = flags;
- handle_pf_work->asid = asid;
+ INIT_WORK(&handle_prefetch_work->prefetch_work, hl_mmu_prefetch_work_function);
+ handle_prefetch_work->ctx = ctx;
+ handle_prefetch_work->va = va;
+ handle_prefetch_work->size = size;
+ handle_prefetch_work->flags = flags;
+ handle_prefetch_work->asid = asid;
/*
* as actual prefetch is done in a WQ we must get the context (and put it
* at the end of the work function)
*/
hl_ctx_get(ctx);
- queue_work(ctx->hdev->pf_wq, &handle_pf_work->pf_work);
+ queue_work(ctx->hdev->prefetch_wq, &handle_prefetch_work->prefetch_work);
return 0;
}
@@ -748,7 +729,7 @@ u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte)
* @mmu_prop: MMU properties.
* @hop_idx: HOP index.
* @hop_addr: HOP address.
- * @virt_addr: virtual address fro the translation.
+ * @virt_addr: virtual address for the translation.
*
* @return the matching PTE value on success, otherwise U64_MAX.
*/
@@ -772,7 +753,7 @@ static void mmu_dma_mem_free_from_chunk(struct gen_pool *pool,
struct gen_pool_chunk *chunk,
void *data)
{
- struct hl_device *hdev = (struct hl_device *)data;
+ struct hl_device *hdev = data;
hl_asic_dma_free_coherent(hdev, (chunk->end_addr - chunk->start_addr) + 1,
(void *)chunk->start_addr, chunk->phys_addr);
@@ -1235,3 +1216,219 @@ int hl_mmu_hr_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_
return 0;
}
+struct pgt_info *hl_mmu_dr_get_pgt_info(struct hl_ctx *ctx, u64 hop_addr)
+{
+ struct pgt_info *pgt_info = NULL;
+
+ hash_for_each_possible(ctx->mmu_shadow_hash, pgt_info, node,
+ (unsigned long) hop_addr)
+ if (hop_addr == pgt_info->shadow_addr)
+ break;
+
+ return pgt_info;
+}
+
+void hl_mmu_dr_free_hop(struct hl_ctx *ctx, u64 hop_addr)
+{
+ struct pgt_info *pgt_info = hl_mmu_dr_get_pgt_info(ctx, hop_addr);
+
+ hl_mmu_dr_free_pgt_node(ctx, pgt_info);
+}
+
+void hl_mmu_dr_free_pgt_node(struct hl_ctx *ctx, struct pgt_info *pgt_info)
+{
+ struct hl_device *hdev = ctx->hdev;
+
+ gen_pool_free(hdev->mmu_priv.dr.mmu_pgt_pool, pgt_info->phys_addr,
+ hdev->asic_prop.dmmu.hop_table_size);
+ hash_del(&pgt_info->node);
+ kfree((u64 *) (uintptr_t) pgt_info->shadow_addr);
+ kfree(pgt_info);
+}
+
+u64 hl_mmu_dr_get_phys_hop0_addr(struct hl_ctx *ctx)
+{
+ return ctx->hdev->asic_prop.mmu_pgt_addr +
+ (ctx->asid * ctx->hdev->asic_prop.dmmu.hop_table_size);
+}
+
+u64 hl_mmu_dr_get_hop0_addr(struct hl_ctx *ctx)
+{
+ return (u64) (uintptr_t) ctx->hdev->mmu_priv.dr.mmu_shadow_hop0 +
+ (ctx->asid * ctx->hdev->asic_prop.dmmu.hop_table_size);
+}
+
+u64 hl_mmu_dr_get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr)
+{
+ u64 page_mask = ctx->hdev->asic_prop.dmmu.hop_table_size - 1;
+ u64 shadow_hop_addr = shadow_addr & (~page_mask);
+ u64 pte_offset = shadow_addr & page_mask;
+ u64 phys_hop_addr;
+
+ if (shadow_hop_addr != hl_mmu_dr_get_hop0_addr(ctx))
+ phys_hop_addr = hl_mmu_dr_get_pgt_info(ctx, shadow_hop_addr)->phys_addr;
+ else
+ phys_hop_addr = hl_mmu_dr_get_phys_hop0_addr(ctx);
+
+ return phys_hop_addr + pte_offset;
+}
+
+void hl_mmu_dr_write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val)
+{
+ u64 phys_val = hl_mmu_dr_get_phys_addr(ctx, val);
+
+ ctx->hdev->asic_funcs->write_pte(ctx->hdev, hl_mmu_dr_get_phys_addr(ctx, shadow_pte_addr),
+ phys_val);
+
+ *(u64 *) (uintptr_t) shadow_pte_addr = val;
+}
+
+void hl_mmu_dr_write_final_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val)
+{
+ ctx->hdev->asic_funcs->write_pte(ctx->hdev,
+ hl_mmu_dr_get_phys_addr(ctx, shadow_pte_addr), val);
+ *(u64 *) (uintptr_t) shadow_pte_addr = val;
+}
+
+void hl_mmu_dr_clear_pte(struct hl_ctx *ctx, u64 pte_addr)
+{
+ hl_mmu_dr_write_final_pte(ctx, pte_addr, 0);
+}
+
+void hl_mmu_dr_get_pte(struct hl_ctx *ctx, u64 hop_addr)
+{
+ hl_mmu_dr_get_pgt_info(ctx, hop_addr)->num_of_ptes++;
+}
+
+int hl_mmu_dr_put_pte(struct hl_ctx *ctx, u64 hop_addr)
+{
+ struct pgt_info *pgt_info = hl_mmu_dr_get_pgt_info(ctx, hop_addr);
+ int num_of_ptes_left;
+
+ pgt_info->num_of_ptes--;
+
+ /*
+ * Need to save the number of ptes left because hl_mmu_free_hop might free
+ * the pgt_info
+ */
+ num_of_ptes_left = pgt_info->num_of_ptes;
+ if (!num_of_ptes_left)
+ hl_mmu_dr_free_pgt_node(ctx, pgt_info);
+
+ return num_of_ptes_left;
+}
+
+u64 hl_mmu_dr_alloc_hop(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct pgt_info *pgt_info;
+ u64 phys_addr, shadow_addr;
+
+ pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL);
+ if (!pgt_info)
+ return ULLONG_MAX;
+
+ phys_addr = (u64) gen_pool_alloc(hdev->mmu_priv.dr.mmu_pgt_pool,
+ prop->dmmu.hop_table_size);
+ if (!phys_addr) {
+ dev_err(hdev->dev, "failed to allocate page\n");
+ goto pool_add_err;
+ }
+
+ shadow_addr = (u64) (uintptr_t) kzalloc(prop->dmmu.hop_table_size,
+ GFP_KERNEL);
+ if (!shadow_addr)
+ goto shadow_err;
+
+ pgt_info->phys_addr = phys_addr;
+ pgt_info->shadow_addr = shadow_addr;
+ pgt_info->ctx = ctx;
+ pgt_info->num_of_ptes = 0;
+ hash_add(ctx->mmu_shadow_hash, &pgt_info->node, shadow_addr);
+
+ return shadow_addr;
+
+shadow_err:
+ gen_pool_free(hdev->mmu_priv.dr.mmu_pgt_pool,
+ phys_addr, prop->dmmu.hop_table_size);
+pool_add_err:
+ kfree(pgt_info);
+
+ return ULLONG_MAX;
+}
+
+u64 hl_mmu_dr_get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte, bool *is_new_hop)
+{
+ u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
+
+ if (hop_addr == ULLONG_MAX) {
+ hop_addr = hl_mmu_dr_alloc_hop(ctx);
+ *is_new_hop = (hop_addr != ULLONG_MAX);
+ }
+
+ return hop_addr;
+}
+
+void hl_mmu_dr_flush(struct hl_ctx *ctx)
+{
+ /* flush all writes from all cores to reach PCI */
+ mb();
+ ctx->hdev->asic_funcs->read_pte(ctx->hdev, hl_mmu_dr_get_phys_hop0_addr(ctx));
+}
+
+int hl_mmu_dr_init(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc;
+
+ hdev->mmu_priv.dr.mmu_pgt_pool =
+ gen_pool_create(__ffs(prop->dmmu.hop_table_size), -1);
+
+ if (!hdev->mmu_priv.dr.mmu_pgt_pool) {
+ dev_err(hdev->dev, "Failed to create page gen pool\n");
+ return -ENOMEM;
+ }
+
+ rc = gen_pool_add(hdev->mmu_priv.dr.mmu_pgt_pool, prop->mmu_pgt_addr +
+ prop->dmmu.hop0_tables_total_size,
+ prop->dmmu.pgt_size - prop->dmmu.hop0_tables_total_size,
+ -1);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to add memory to page gen pool\n");
+ goto err_pool_add;
+ }
+
+ hdev->mmu_priv.dr.mmu_shadow_hop0 = kvcalloc(prop->max_asid,
+ prop->dmmu.hop_table_size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0)) {
+ rc = -ENOMEM;
+ goto err_pool_add;
+ }
+
+ /* MMU H/W init will be done in device hw_init() */
+
+ return 0;
+
+err_pool_add:
+ gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
+
+ return rc;
+}
+
+void hl_mmu_dr_fini(struct hl_device *hdev)
+{
+ /* MMU H/W fini was already done in device hw_fini() */
+
+ if (ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0))
+ return;
+
+ kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0);
+ gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
+
+ /* Make sure that if we arrive here again without init was
+ * called we won't cause kernel panic. This can happen for
+ * example if we fail during hard reset code at certain points
+ */
+ hdev->mmu_priv.dr.mmu_shadow_hop0 = NULL;
+}
diff --git a/drivers/accel/habanalabs/common/mmu/mmu_v1.c b/drivers/accel/habanalabs/common/mmu/mmu_v1.c
new file mode 100644
index 000000000000..845d16aaa637
--- /dev/null
+++ b/drivers/accel/habanalabs/common/mmu/mmu_v1.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2019 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "../habanalabs.h"
+#include "../../include/hw_ip/mmu/mmu_general.h"
+
+#include <linux/slab.h>
+
+#define MMU_V1_MAX_HOPS (MMU_HOP4 + 1)
+
+static inline u64 get_hop_pte_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,
+ u64 *hop_addr_arr, u64 virt_addr, enum mmu_hop_num hop_idx)
+{
+ u64 mask, shift;
+
+ mask = mmu_prop->hop_masks[hop_idx];
+ shift = mmu_prop->hop_shifts[hop_idx];
+ return hop_addr_arr[hop_idx] +
+ ctx->hdev->asic_prop.mmu_pte_size * ((virt_addr & mask) >> shift);
+}
+
+static int dram_default_mapping_init(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr,
+ hop2_pte_addr, hop3_pte_addr, pte_val;
+ int rc, i, j, hop3_allocated = 0;
+
+ if ((!prop->dram_supports_virtual_memory) ||
+ (!hdev->dram_default_page_mapping) ||
+ (ctx->asid == HL_KERNEL_ASID_ID))
+ return 0;
+
+ num_of_hop3 = prop->dram_size_for_default_page_mapping;
+ do_div(num_of_hop3, prop->dram_page_size);
+ do_div(num_of_hop3, HOP_PTE_ENTRIES_512);
+
+ /* add hop1 and hop2 */
+ total_hops = num_of_hop3 + 2;
+
+ ctx->dram_default_hops = kcalloc(total_hops, HL_PTE_SIZE, GFP_KERNEL);
+ if (!ctx->dram_default_hops)
+ return -ENOMEM;
+
+ hop0_addr = hl_mmu_dr_get_hop0_addr(ctx);
+
+ hop1_addr = hl_mmu_dr_alloc_hop(ctx);
+ if (hop1_addr == ULLONG_MAX) {
+ dev_err(hdev->dev, "failed to alloc hop 1\n");
+ rc = -ENOMEM;
+ goto hop1_err;
+ }
+
+ ctx->dram_default_hops[total_hops - 1] = hop1_addr;
+
+ hop2_addr = hl_mmu_dr_alloc_hop(ctx);
+ if (hop2_addr == ULLONG_MAX) {
+ dev_err(hdev->dev, "failed to alloc hop 2\n");
+ rc = -ENOMEM;
+ goto hop2_err;
+ }
+
+ ctx->dram_default_hops[total_hops - 2] = hop2_addr;
+
+ for (i = 0 ; i < num_of_hop3 ; i++) {
+ ctx->dram_default_hops[i] = hl_mmu_dr_alloc_hop(ctx);
+ if (ctx->dram_default_hops[i] == ULLONG_MAX) {
+ dev_err(hdev->dev, "failed to alloc hop 3, i: %d\n", i);
+ rc = -ENOMEM;
+ goto hop3_err;
+ }
+ hop3_allocated++;
+ }
+
+ /* need only pte 0 in hops 0 and 1 */
+ pte_val = (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
+ hl_mmu_dr_write_pte(ctx, hop0_addr, pte_val);
+
+ pte_val = (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
+ hl_mmu_dr_write_pte(ctx, hop1_addr, pte_val);
+ hl_mmu_dr_get_pte(ctx, hop1_addr);
+
+ hop2_pte_addr = hop2_addr;
+ for (i = 0 ; i < num_of_hop3 ; i++) {
+ pte_val = (ctx->dram_default_hops[i] & HOP_PHYS_ADDR_MASK) |
+ PAGE_PRESENT_MASK;
+ hl_mmu_dr_write_pte(ctx, hop2_pte_addr, pte_val);
+ hl_mmu_dr_get_pte(ctx, hop2_addr);
+ hop2_pte_addr += HL_PTE_SIZE;
+ }
+
+ pte_val = (prop->mmu_dram_default_page_addr & HOP_PHYS_ADDR_MASK) |
+ LAST_MASK | PAGE_PRESENT_MASK;
+
+ for (i = 0 ; i < num_of_hop3 ; i++) {
+ hop3_pte_addr = ctx->dram_default_hops[i];
+ for (j = 0 ; j < HOP_PTE_ENTRIES_512 ; j++) {
+ hl_mmu_dr_write_final_pte(ctx, hop3_pte_addr, pte_val);
+ hl_mmu_dr_get_pte(ctx, ctx->dram_default_hops[i]);
+ hop3_pte_addr += HL_PTE_SIZE;
+ }
+ }
+
+ hl_mmu_dr_flush(ctx);
+
+ return 0;
+
+hop3_err:
+ for (i = 0 ; i < hop3_allocated ; i++)
+ hl_mmu_dr_free_hop(ctx, ctx->dram_default_hops[i]);
+
+ hl_mmu_dr_free_hop(ctx, hop2_addr);
+hop2_err:
+ hl_mmu_dr_free_hop(ctx, hop1_addr);
+hop1_err:
+ kfree(ctx->dram_default_hops);
+
+ return rc;
+}
+
+static void dram_default_mapping_fini(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr,
+ hop2_pte_addr, hop3_pte_addr;
+ int i, j;
+
+ if ((!prop->dram_supports_virtual_memory) ||
+ (!hdev->dram_default_page_mapping) ||
+ (ctx->asid == HL_KERNEL_ASID_ID))
+ return;
+
+ num_of_hop3 = prop->dram_size_for_default_page_mapping;
+ do_div(num_of_hop3, prop->dram_page_size);
+ do_div(num_of_hop3, HOP_PTE_ENTRIES_512);
+
+ hop0_addr = hl_mmu_dr_get_hop0_addr(ctx);
+ /* add hop1 and hop2 */
+ total_hops = num_of_hop3 + 2;
+ hop1_addr = ctx->dram_default_hops[total_hops - 1];
+ hop2_addr = ctx->dram_default_hops[total_hops - 2];
+
+ for (i = 0 ; i < num_of_hop3 ; i++) {
+ hop3_pte_addr = ctx->dram_default_hops[i];
+ for (j = 0 ; j < HOP_PTE_ENTRIES_512 ; j++) {
+ hl_mmu_dr_clear_pte(ctx, hop3_pte_addr);
+ hl_mmu_dr_put_pte(ctx, ctx->dram_default_hops[i]);
+ hop3_pte_addr += HL_PTE_SIZE;
+ }
+ }
+
+ hop2_pte_addr = hop2_addr;
+ for (i = 0 ; i < num_of_hop3 ; i++) {
+ hl_mmu_dr_clear_pte(ctx, hop2_pte_addr);
+ hl_mmu_dr_put_pte(ctx, hop2_addr);
+ hop2_pte_addr += HL_PTE_SIZE;
+ }
+
+ hl_mmu_dr_clear_pte(ctx, hop1_addr);
+ hl_mmu_dr_put_pte(ctx, hop1_addr);
+ hl_mmu_dr_clear_pte(ctx, hop0_addr);
+
+ kfree(ctx->dram_default_hops);
+
+ hl_mmu_dr_flush(ctx);
+}
+
+/**
+ * hl_mmu_v1_ctx_init() - initialize a context for using the MMU module.
+ * @ctx: pointer to the context structure to initialize.
+ *
+ * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
+ * page tables hops related to this context.
+ * Return: 0 on success, non-zero otherwise.
+ */
+static int hl_mmu_v1_ctx_init(struct hl_ctx *ctx)
+{
+ hash_init(ctx->mmu_shadow_hash);
+ return dram_default_mapping_init(ctx);
+}
+
+/*
+ * hl_mmu_ctx_fini - disable a ctx from using the mmu module
+ *
+ * @ctx: pointer to the context structure
+ *
+ * This function does the following:
+ * - Free any pgts which were not freed yet
+ * - Free the mutex
+ * - Free DRAM default page mapping hops
+ */
+static void hl_mmu_v1_ctx_fini(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct pgt_info *pgt_info;
+ struct hlist_node *tmp;
+ int i;
+
+ dram_default_mapping_fini(ctx);
+
+ if (!hash_empty(ctx->mmu_shadow_hash))
+ dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n",
+ ctx->asid);
+
+ hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) {
+ dev_err_ratelimited(hdev->dev,
+ "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",
+ pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);
+ hl_mmu_dr_free_pgt_node(ctx, pgt_info);
+ }
+}
+
+static int hl_mmu_v1_unmap(struct hl_ctx *ctx,
+ u64 virt_addr, bool is_dram_addr)
+{
+ u64 hop_addr[MMU_V1_MAX_HOPS] = {0}, hop_pte_addr[MMU_V1_MAX_HOPS] = {0}, curr_pte = 0;
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_mmu_properties *mmu_prop;
+ bool is_huge, clear_hop3 = true;
+ int hop_idx;
+
+ /* shifts and masks are the same in PMMU and HPMMU, use one of them */
+ mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
+
+ for (hop_idx = MMU_HOP0; hop_idx < MMU_HOP4; hop_idx++) {
+ if (hop_idx == MMU_HOP0) {
+ hop_addr[hop_idx] = hl_mmu_dr_get_hop0_addr(ctx);
+ } else {
+ hop_addr[hop_idx] = hl_mmu_get_next_hop_addr(ctx, curr_pte);
+ if (hop_addr[hop_idx] == ULLONG_MAX)
+ goto not_mapped;
+ }
+
+ hop_pte_addr[hop_idx] =
+ get_hop_pte_addr(ctx, mmu_prop, hop_addr, virt_addr, hop_idx);
+
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[hop_idx];
+ }
+
+ is_huge = curr_pte & mmu_prop->last_mask;
+
+ if (is_dram_addr && !is_huge) {
+ dev_err(hdev->dev, "DRAM unmapping should use huge pages only\n");
+ return -EFAULT;
+ }
+
+ if (!is_huge) {
+ hop_idx = MMU_HOP4;
+ hop_addr[hop_idx] = hl_mmu_get_next_hop_addr(ctx, curr_pte);
+ if (hop_addr[hop_idx] == ULLONG_MAX)
+ goto not_mapped;
+
+ hop_pte_addr[hop_idx] =
+ get_hop_pte_addr(ctx, mmu_prop, hop_addr, virt_addr, hop_idx);
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[hop_idx];
+ clear_hop3 = false;
+ }
+
+ if (hdev->dram_default_page_mapping && is_dram_addr) {
+ u64 default_pte = (prop->mmu_dram_default_page_addr &
+ HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask |
+ PAGE_PRESENT_MASK;
+ if (curr_pte == default_pte) {
+ dev_err(hdev->dev,
+ "DRAM: hop3 PTE points to zero page, can't unmap, va: 0x%llx\n",
+ virt_addr);
+ goto not_mapped;
+ }
+
+ if (!(curr_pte & PAGE_PRESENT_MASK)) {
+ dev_err(hdev->dev,
+ "DRAM: hop3 PTE is cleared! can't unmap, va: 0x%llx\n",
+ virt_addr);
+ goto not_mapped;
+ }
+
+ hop_idx = MMU_HOP3;
+ hl_mmu_dr_write_final_pte(ctx, hop_pte_addr[hop_idx], default_pte);
+ hl_mmu_dr_put_pte(ctx, hop_addr[hop_idx]);
+ } else {
+ if (!(curr_pte & PAGE_PRESENT_MASK))
+ goto not_mapped;
+
+ if (hop_addr[MMU_HOP4])
+ hl_mmu_dr_clear_pte(ctx, hop_pte_addr[MMU_HOP4]);
+ else
+ hl_mmu_dr_clear_pte(ctx, hop_pte_addr[MMU_HOP3]);
+
+ if (hop_addr[MMU_HOP4] && !hl_mmu_dr_put_pte(ctx, hop_addr[MMU_HOP4]))
+ clear_hop3 = true;
+
+ if (!clear_hop3)
+ goto mapped;
+
+ for (hop_idx = MMU_HOP3; hop_idx >= 0; hop_idx--) {
+ hl_mmu_dr_clear_pte(ctx, hop_pte_addr[hop_idx]);
+
+ if (hop_idx == MMU_HOP0)
+ break;
+
+ if (hl_mmu_dr_put_pte(ctx, hop_addr[hop_idx]))
+ goto mapped;
+ }
+ }
+
+mapped:
+ return 0;
+
+not_mapped:
+ dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n",
+ virt_addr);
+
+ return -EINVAL;
+}
+
+static int hl_mmu_v1_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
+ u32 page_size, bool is_dram_addr)
+{
+ u64 hop_addr[MMU_V1_MAX_HOPS] = {0}, hop_pte_addr[MMU_V1_MAX_HOPS] = {0}, curr_pte = 0;
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_mmu_properties *mmu_prop;
+ bool is_huge, hop_new[MMU_V1_MAX_HOPS] = {false};
+ int num_hops, hop_idx, prev_hop, rc = -ENOMEM;
+
+ /*
+ * This mapping function can map a page or a huge page. For huge page
+ * there are only 3 hops rather than 4. Currently the DRAM allocation
+ * uses huge pages only but user memory could have been allocated with
+ * one of the two page sizes. Since this is a common code for all the
+ * three cases, we need this hugs page check.
+ */
+ if (is_dram_addr) {
+ mmu_prop = &prop->dmmu;
+ is_huge = true;
+ } else if (page_size == prop->pmmu_huge.page_size) {
+ mmu_prop = &prop->pmmu_huge;
+ is_huge = true;
+ } else {
+ mmu_prop = &prop->pmmu;
+ is_huge = false;
+ }
+
+ num_hops = is_huge ? (MMU_V1_MAX_HOPS - 1) : MMU_V1_MAX_HOPS;
+
+ for (hop_idx = MMU_HOP0; hop_idx < num_hops; hop_idx++) {
+ if (hop_idx == MMU_HOP0) {
+ hop_addr[hop_idx] = hl_mmu_dr_get_hop0_addr(ctx);
+ } else {
+ hop_addr[hop_idx] =
+ hl_mmu_dr_get_alloc_next_hop_addr(ctx, curr_pte, &hop_new[hop_idx]);
+ if (hop_addr[hop_idx] == ULLONG_MAX)
+ goto err;
+ }
+
+ hop_pte_addr[hop_idx] =
+ get_hop_pte_addr(ctx, mmu_prop, hop_addr, virt_addr, hop_idx);
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[hop_idx];
+ }
+
+ if (hdev->dram_default_page_mapping && is_dram_addr) {
+ u64 default_pte = (prop->mmu_dram_default_page_addr &
+ HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask |
+ PAGE_PRESENT_MASK;
+
+ if (curr_pte != default_pte) {
+ dev_err(hdev->dev,
+ "DRAM: mapping already exists for virt_addr 0x%llx\n",
+ virt_addr);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ for (hop_idx = MMU_HOP1; hop_idx < num_hops; hop_idx++) {
+ if (hop_new[hop_idx]) {
+ dev_err(hdev->dev, "DRAM mapping should not allocate more hops\n");
+ rc = -EFAULT;
+ goto err;
+ }
+ }
+ } else if (curr_pte & PAGE_PRESENT_MASK) {
+ dev_err(hdev->dev,
+ "mapping already exists for virt_addr 0x%llx\n",
+ virt_addr);
+
+ for (hop_idx = MMU_HOP0; hop_idx < num_hops; hop_idx++)
+ dev_dbg(hdev->dev, "hop%d pte: 0x%llx (0x%llx)\n", hop_idx,
+ *(u64 *) (uintptr_t) hop_pte_addr[hop_idx],
+ hop_pte_addr[hop_idx]);
+
+ rc = -EINVAL;
+ goto err;
+ }
+
+ curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask
+ | PAGE_PRESENT_MASK;
+
+ hl_mmu_dr_write_final_pte(ctx, hop_pte_addr[num_hops - 1], curr_pte);
+
+ for (hop_idx = MMU_HOP1; hop_idx < num_hops; hop_idx++) {
+ prev_hop = hop_idx - 1;
+
+ if (hop_new[hop_idx]) {
+ curr_pte = (hop_addr[hop_idx] & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
+ hl_mmu_dr_write_pte(ctx, hop_pte_addr[prev_hop], curr_pte);
+ if (hop_idx != MMU_HOP1)
+ hl_mmu_dr_get_pte(ctx, hop_addr[prev_hop]);
+ }
+ }
+
+ hl_mmu_dr_get_pte(ctx, hop_addr[num_hops - 1]);
+
+ return 0;
+
+err:
+ for (hop_idx = num_hops; hop_idx > MMU_HOP0; hop_idx--) {
+ if (hop_new[hop_idx])
+ hl_mmu_dr_free_hop(ctx, hop_addr[hop_idx]);
+ }
+
+ return rc;
+}
+
+/*
+ * hl_mmu_v1_swap_out - marks all mapping of the given ctx as swapped out
+ *
+ * @ctx: pointer to the context structure
+ *
+ */
+static void hl_mmu_v1_swap_out(struct hl_ctx *ctx)
+{
+
+}
+
+/*
+ * hl_mmu_v1_swap_in - marks all mapping of the given ctx as swapped in
+ *
+ * @ctx: pointer to the context structure
+ *
+ */
+static void hl_mmu_v1_swap_in(struct hl_ctx *ctx)
+{
+
+}
+
+static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
+ struct hl_mmu_hop_info *hops)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_mmu_properties *mmu_prop;
+ bool is_dram_addr, is_pmmu_addr, is_pmmu_h_addr, is_huge;
+ int i, used_hops;
+
+ is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
+ prop->dmmu.start_addr,
+ prop->dmmu.end_addr);
+ is_pmmu_addr = hl_mem_area_inside_range(virt_addr, prop->pmmu.page_size,
+ prop->pmmu.start_addr,
+ prop->pmmu.end_addr);
+ is_pmmu_h_addr = hl_mem_area_inside_range(virt_addr,
+ prop->pmmu_huge.page_size,
+ prop->pmmu_huge.start_addr,
+ prop->pmmu_huge.end_addr);
+ if (is_dram_addr) {
+ mmu_prop = &prop->dmmu;
+ is_huge = true;
+ } else if (is_pmmu_addr) {
+ mmu_prop = &prop->pmmu;
+ is_huge = false;
+ } else if (is_pmmu_h_addr) {
+ mmu_prop = &prop->pmmu_huge;
+ is_huge = true;
+ } else {
+ return -EINVAL;
+ }
+
+ used_hops = mmu_prop->num_hops;
+
+ /* huge pages use lesser hops */
+ if (is_huge)
+ used_hops--;
+
+ hops->hop_info[0].hop_addr = hl_mmu_dr_get_phys_hop0_addr(ctx);
+ hops->hop_info[0].hop_pte_addr =
+ hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,
+ hops->hop_info[0].hop_addr, virt_addr);
+ hops->hop_info[0].hop_pte_val =
+ hdev->asic_funcs->read_pte(hdev,
+ hops->hop_info[0].hop_pte_addr);
+
+ for (i = 1 ; i < used_hops ; i++) {
+ hops->hop_info[i].hop_addr =
+ hl_mmu_get_next_hop_addr(ctx,
+ hops->hop_info[i - 1].hop_pte_val);
+ if (hops->hop_info[i].hop_addr == ULLONG_MAX)
+ return -EFAULT;
+
+ hops->hop_info[i].hop_pte_addr =
+ hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
+ hops->hop_info[i].hop_addr,
+ virt_addr);
+ hops->hop_info[i].hop_pte_val =
+ hdev->asic_funcs->read_pte(hdev,
+ hops->hop_info[i].hop_pte_addr);
+
+ if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
+ return -EFAULT;
+
+ if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask)
+ break;
+ }
+
+ /* if passed over all hops then no last hop was found */
+ if (i == mmu_prop->num_hops)
+ return -EFAULT;
+
+ if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
+ return -EFAULT;
+
+ hops->used_hops = i + 1;
+
+ return 0;
+}
+
+/*
+ * hl_mmu_v1_prepare - prepare mmu for working with mmu v1
+ *
+ * @hdev: pointer to the device structure
+ */
+void hl_mmu_v1_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu)
+{
+ mmu->init = hl_mmu_dr_init;
+ mmu->fini = hl_mmu_dr_fini;
+ mmu->ctx_init = hl_mmu_v1_ctx_init;
+ mmu->ctx_fini = hl_mmu_v1_ctx_fini;
+ mmu->map = hl_mmu_v1_map;
+ mmu->unmap = hl_mmu_v1_unmap;
+ mmu->flush = hl_mmu_dr_flush;
+ mmu->swap_out = hl_mmu_v1_swap_out;
+ mmu->swap_in = hl_mmu_v1_swap_in;
+ mmu->get_tlb_info = hl_mmu_v1_get_tlb_info;
+}
diff --git a/drivers/accel/habanalabs/common/mmu/mmu_v2.c b/drivers/accel/habanalabs/common/mmu/mmu_v2.c
new file mode 100644
index 000000000000..4bc0268fff1c
--- /dev/null
+++ b/drivers/accel/habanalabs/common/mmu/mmu_v2.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2020 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "../habanalabs.h"
+#include "../../include/hw_ip/mmu/mmu_general.h"
+#include "../../include/hw_ip/mmu/mmu_v2_0.h"
+
+#include <linux/slab.h>
+
+/**
+ * hl_mmu_v2_ctx_init() - initialize a context for using the MMU module.
+ * @ctx: pointer to the context structure to initialize.
+ *
+ * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
+ * page tables hops related to this context.
+ * Return: 0 on success, non-zero otherwise.
+ */
+static int hl_mmu_v2_ctx_init(struct hl_ctx *ctx)
+{
+ hash_init(ctx->mmu_shadow_hash);
+
+ return 0;
+}
+
+/*
+ * hl_mmu_v2_ctx_fini - disable a ctx from using the mmu module
+ *
+ * @ctx: pointer to the context structure
+ *
+ * This function does the following:
+ * - Free any pgts which were not freed yet
+ * - Free the mutex
+ * - Free DRAM default page mapping hops
+ */
+static void hl_mmu_v2_ctx_fini(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct pgt_info *pgt_info;
+ struct hlist_node *tmp;
+ int i;
+
+ if (!hash_empty(ctx->mmu_shadow_hash))
+ dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n",
+ ctx->asid);
+
+ hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) {
+ dev_err_ratelimited(hdev->dev,
+ "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",
+ pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);
+ hl_mmu_dr_free_pgt_node(ctx, pgt_info);
+ }
+}
+
+static int hl_mmu_v2_unmap(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr)
+{
+ u64 hop_addr[MMU_ARCH_6_HOPS] = { 0 }, hop_pte_addr[MMU_ARCH_6_HOPS] = { 0 }, curr_pte,
+ scrambled_virt_addr;
+ struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;
+ struct hl_device *hdev = ctx->hdev;
+ struct hl_mmu_properties *mmu_prop;
+ bool is_huge = false;
+ int i, hop_last;
+
+ /* device resident in V2 are allowed only for HMMU */
+ if (!is_dram_addr)
+ return -EINVAL;
+
+ mmu_prop = &prop->dmmu;
+
+ hop_last = mmu_prop->num_hops - 1;
+
+ scrambled_virt_addr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);
+
+ hop_addr[0] = hl_mmu_dr_get_hop0_addr(ctx);
+ hop_pte_addr[0] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,
+ hop_addr[0], scrambled_virt_addr);
+ if (hop_pte_addr[0] == U64_MAX)
+ return -EFAULT;
+
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[0];
+
+ for (i = 1 ; i < mmu_prop->num_hops ; i++) {
+ hop_addr[i] = hl_mmu_get_next_hop_addr(ctx, curr_pte);
+ if (hop_addr[i] == ULLONG_MAX)
+ goto not_mapped;
+
+ hop_pte_addr[i] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
+ hop_addr[i], scrambled_virt_addr);
+ if (hop_pte_addr[i] == U64_MAX)
+ return -EFAULT;
+
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[i];
+
+ if ((i <= hop_last) && (curr_pte & mmu_prop->last_mask)) {
+ hop_last = i;
+ is_huge = true;
+ break;
+ }
+ }
+
+ if (is_dram_addr && !is_huge) {
+ dev_err(hdev->dev, "DRAM unmapping should use huge pages only\n");
+ return -EFAULT;
+ }
+
+ if (!(curr_pte & PAGE_PRESENT_MASK))
+ goto not_mapped;
+
+ for (i = hop_last ; i > 0 ; i--) {
+ hl_mmu_dr_clear_pte(ctx, hop_pte_addr[i]);
+ if (hl_mmu_dr_put_pte(ctx, hop_addr[i]))
+ goto mapped;
+ }
+ hl_mmu_dr_clear_pte(ctx, hop_pte_addr[0]);
+
+mapped:
+ return 0;
+
+not_mapped:
+ dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n",
+ virt_addr);
+
+ return -EINVAL;
+}
+
+static int hl_mmu_v2_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
+ u32 page_size, bool is_dram_addr)
+{
+ u64 hop_addr[MMU_ARCH_6_HOPS] = { 0 }, hop_pte_addr[MMU_ARCH_6_HOPS] = { 0 },
+ curr_pte = 0, scrambled_virt_addr, scrambled_phys_addr;
+ struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;
+ bool hop_new[MMU_ARCH_6_HOPS] = { false };
+ struct hl_device *hdev = ctx->hdev;
+ struct hl_mmu_properties *mmu_prop;
+ int rc, i, hop_last;
+
+ /* device resident in V2 are allowed only for HMMU */
+ if (!is_dram_addr)
+ return -EINVAL;
+
+ mmu_prop = &prop->dmmu;
+
+ hop_last = mmu_prop->num_hops - 1;
+
+ scrambled_virt_addr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);
+ scrambled_phys_addr = hdev->asic_funcs->scramble_addr(hdev, phys_addr);
+
+ /* First hop is preallocated therefore it is treated differently */
+ hop_addr[0] = hl_mmu_dr_get_hop0_addr(ctx);
+ hop_pte_addr[0] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,
+ hop_addr[0], scrambled_virt_addr);
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[0];
+
+ /* Handle hop1 to hop_last */
+ for (i = 1 ; i <= hop_last ; i++) {
+ hop_addr[i] = hl_mmu_dr_get_alloc_next_hop_addr(ctx, curr_pte, &hop_new[i]);
+ if (hop_addr[i] == ULLONG_MAX) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ hop_pte_addr[i] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
+ hop_addr[i], scrambled_virt_addr);
+ if (hop_pte_addr[i] == U64_MAX) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (!hop_pte_addr[i]) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[i];
+ }
+
+ if (curr_pte & PAGE_PRESENT_MASK) {
+ dev_err(hdev->dev,
+ "mapping already exists for virt_addr 0x%llx\n",
+ virt_addr);
+
+ for (i = 0 ; i <= hop_last ; i++)
+ dev_dbg(hdev->dev, "hop%d pte: 0x%llx (0x%llx)\n",
+ i, *(u64 *) (uintptr_t) hop_pte_addr[i],
+ hop_pte_addr[i]);
+
+ rc = -EINVAL;
+ goto err;
+ }
+
+ curr_pte = (scrambled_phys_addr & HOP_PHYS_ADDR_MASK)
+ | mmu_prop->last_mask | PAGE_PRESENT_MASK;
+
+ /* Write the PTEs */
+ hl_mmu_dr_write_final_pte(ctx, hop_pte_addr[hop_last], curr_pte);
+
+ /* for each new hop, add its address to the table of previous-hop */
+ for (i = 1 ; i <= hop_last ; i++) {
+ if (hop_new[i]) {
+ curr_pte = (hop_addr[i] & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
+ hl_mmu_dr_write_pte(ctx, hop_pte_addr[i - 1], curr_pte);
+
+ if (i - 1)
+ hl_mmu_dr_get_pte(ctx, hop_addr[i - 1]);
+ }
+ }
+ hl_mmu_dr_get_pte(ctx, hop_addr[hop_last]);
+
+ return 0;
+
+err:
+ for (i = 1 ; i <= hop_last ; i++)
+ if (hop_new[i] && (hop_addr[i] != U64_MAX))
+ hl_mmu_dr_free_hop(ctx, hop_addr[i]);
+
+ return rc;
+}
+
+/*
+ * hl_mmu_v2_swap_out - marks all mapping of the given ctx as swapped out
+ *
+ * @ctx: pointer to the context structure
+ *
+ */
+static void hl_mmu_v2_swap_out(struct hl_ctx *ctx)
+{
+
+}
+
+/*
+ * hl_mmu_v2_swap_in - marks all mapping of the given ctx as swapped in
+ *
+ * @ctx: pointer to the context structure
+ *
+ */
+static void hl_mmu_v2_swap_in(struct hl_ctx *ctx)
+{
+
+}
+
+static int hl_mmu_v2_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops)
+{
+ struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;
+ struct hl_device *hdev = ctx->hdev;
+ struct hl_mmu_properties *mmu_prop;
+ bool is_dram_addr;
+ int i;
+
+ is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
+ prop->dmmu.start_addr,
+ prop->dmmu.end_addr);
+
+ /* device resident in V2 are allowed only for HMMU */
+ if (!is_dram_addr)
+ return -EINVAL;
+
+ mmu_prop = &prop->dmmu;
+ hops->range_type = HL_VA_RANGE_TYPE_DRAM;
+
+ hops->scrambled_vaddr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);
+
+ hops->hop_info[0].hop_addr = hl_mmu_dr_get_phys_hop0_addr(ctx);
+ hops->hop_info[0].hop_pte_addr = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,
+ hops->hop_info[0].hop_addr,
+ hops->scrambled_vaddr);
+ if (hops->hop_info[0].hop_pte_addr == U64_MAX)
+ return -EFAULT;
+
+ hops->hop_info[0].hop_pte_val = hdev->asic_funcs->read_pte(hdev,
+ hops->hop_info[0].hop_pte_addr);
+ if (hops->hop_info[0].hop_pte_val == U64_MAX)
+ return -EFAULT;
+
+ for (i = 1 ; i < mmu_prop->num_hops ; i++) {
+ hops->hop_info[i].hop_addr =
+ hl_mmu_get_next_hop_addr(ctx, hops->hop_info[i - 1].hop_pte_val);
+ if (hops->hop_info[i].hop_addr == ULLONG_MAX)
+ return -EFAULT;
+
+ hops->hop_info[i].hop_pte_addr =
+ hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
+ hops->hop_info[i].hop_addr,
+ hops->scrambled_vaddr);
+ if (hops->hop_info[i].hop_pte_addr == U64_MAX)
+ return -EFAULT;
+
+ hops->hop_info[i].hop_pte_val =
+ hdev->asic_funcs->read_pte(hdev,
+ hops->hop_info[i].hop_pte_addr);
+
+ if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
+ return -EFAULT;
+
+ if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask)
+ break;
+ }
+
+ /* if passed over all hops then no last hop was found */
+ if (i == mmu_prop->num_hops)
+ return -EFAULT;
+
+ if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
+ return -EFAULT;
+
+ if (hops->scrambled_vaddr != virt_addr)
+ hops->unscrambled_paddr = hdev->asic_funcs->descramble_addr
+ (hdev, hops->hop_info[i].hop_pte_val);
+ else
+ hops->unscrambled_paddr = hops->hop_info[i].hop_pte_val;
+
+ hops->used_hops = i + 1;
+
+ return 0;
+}
+
+/*
+ * hl_mmu_v2_prepare - prepare mmu_if for working with mmu v2
+ *
+ * @hdev: pointer to the device structure
+ * @mmu_if: pointer to the mmu interface structure
+ */
+void hl_mmu_v2_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu)
+{
+ mmu->init = hl_mmu_dr_init;
+ mmu->fini = hl_mmu_dr_fini;
+ mmu->ctx_init = hl_mmu_v2_ctx_init;
+ mmu->ctx_fini = hl_mmu_v2_ctx_fini;
+ mmu->map = hl_mmu_v2_map;
+ mmu->unmap = hl_mmu_v2_unmap;
+ mmu->flush = hl_mmu_dr_flush;
+ mmu->swap_out = hl_mmu_v2_swap_out;
+ mmu->swap_in = hl_mmu_v2_swap_in;
+ mmu->get_tlb_info = hl_mmu_v2_get_tlb_info;
+}
diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c b/drivers/accel/habanalabs/common/mmu/mmu_v2_hr.c
index afe7ef964f82..31507b2a431b 100644
--- a/drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c
+++ b/drivers/accel/habanalabs/common/mmu/mmu_v2_hr.c
@@ -47,7 +47,7 @@ static inline int hl_mmu_v2_hr_init(struct hl_device *hdev)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- return hl_mmu_hr_init(hdev, &hdev->mmu_priv.hr, prop->mmu_hop_table_size,
+ return hl_mmu_hr_init(hdev, &hdev->mmu_priv.hr, prop->pmmu.hop_table_size,
prop->mmu_pgt_size);
}
@@ -65,7 +65,7 @@ static inline void hl_mmu_v2_hr_fini(struct hl_device *hdev)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- hl_mmu_hr_fini(hdev, &hdev->mmu_priv.hr, prop->mmu_hop_table_size);
+ hl_mmu_hr_fini(hdev, &hdev->mmu_priv.hr, prop->pmmu.hop_table_size);
}
/**
@@ -108,7 +108,7 @@ static void hl_mmu_v2_hr_ctx_fini(struct hl_ctx *ctx)
"pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",
pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);
hl_mmu_hr_free_hop_remove_pgt(pgt_info, &ctx->hdev->mmu_priv.hr,
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
}
}
@@ -150,7 +150,7 @@ static int _hl_mmu_v2_hr_unmap(struct hl_ctx *ctx,
curr_pte = *(u64 *) (uintptr_t) hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
if ((i < hop_last) && (curr_pte & mmu_prop->last_mask)) {
hop_last = i;
@@ -169,14 +169,14 @@ static int _hl_mmu_v2_hr_unmap(struct hl_ctx *ctx,
for (i = hop_last ; i > 0 ; i--) {
hl_mmu_hr_clear_pte(ctx, hops_pgt_info[i], hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
if (hl_mmu_hr_put_pte(ctx, hops_pgt_info[i], &ctx->hdev->mmu_priv.hr,
- ctx->hdev->asic_prop.mmu_hop_table_size))
+ ctx->hdev->asic_prop.pmmu.hop_table_size))
goto mapped;
}
hl_mmu_hr_clear_pte(ctx, hops_pgt_info[0], hop_pte_phys_addr[0],
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
mapped:
return 0;
@@ -255,7 +255,7 @@ static int _hl_mmu_v2_hr_map(struct hl_ctx *ctx,
scrambled_virt_addr);
curr_pte = *(u64 *) (uintptr_t) hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
}
if (curr_pte & PAGE_PRESENT_MASK) {
@@ -268,7 +268,7 @@ static int _hl_mmu_v2_hr_map(struct hl_ctx *ctx,
*(u64 *) (uintptr_t)
hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size),
+ ctx->hdev->asic_prop.pmmu.hop_table_size),
hop_pte_phys_addr[i]);
rc = -EINVAL;
goto err;
@@ -279,7 +279,7 @@ static int _hl_mmu_v2_hr_map(struct hl_ctx *ctx,
/* Write the PTEs */
hl_mmu_hr_write_pte(ctx, hops_pgt_info[hop_last], hop_pte_phys_addr[hop_last], curr_pte,
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
/* for each new hop, add its address to the table of previous-hop */
for (i = 1 ; i <= hop_last ; i++) {
@@ -287,7 +287,7 @@ static int _hl_mmu_v2_hr_map(struct hl_ctx *ctx,
curr_pte = (hops_pgt_info[i]->phys_addr & HOP_PHYS_ADDR_MASK) |
PAGE_PRESENT_MASK;
hl_mmu_hr_write_pte(ctx, hops_pgt_info[i - 1], hop_pte_phys_addr[i - 1],
- curr_pte, ctx->hdev->asic_prop.mmu_hop_table_size);
+ curr_pte, ctx->hdev->asic_prop.pmmu.hop_table_size);
if (i - 1)
hl_mmu_hr_get_pte(ctx, &ctx->hdev->mmu_func[MMU_HR_PGT].hr_funcs,
hops_pgt_info[i - 1]->phys_addr);
@@ -303,7 +303,7 @@ err:
for (i = 1 ; i <= hop_last ; i++)
if (hop_new[i] && hops_pgt_info[i])
hl_mmu_hr_free_hop_remove_pgt(hops_pgt_info[i], &ctx->hdev->mmu_priv.hr,
- ctx->hdev->asic_prop.mmu_hop_table_size);
+ ctx->hdev->asic_prop.pmmu.hop_table_size);
return rc;
}
diff --git a/drivers/misc/habanalabs/common/pci/Makefile b/drivers/accel/habanalabs/common/pci/Makefile
index dc922a686683..dc922a686683 100644
--- a/drivers/misc/habanalabs/common/pci/Makefile
+++ b/drivers/accel/habanalabs/common/pci/Makefile
diff --git a/drivers/accel/habanalabs/common/pci/pci.c b/drivers/accel/habanalabs/common/pci/pci.c
new file mode 100644
index 000000000000..81cbd8697d4c
--- /dev/null
+++ b/drivers/accel/habanalabs/common/pci/pci.c
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2019 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "../habanalabs.h"
+#include "../../include/hw_ip/pci/pci_general.h"
+
+#include <linux/pci.h>
+
+#include <trace/events/habanalabs.h>
+
+#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 100)
+
+#define IATU_REGION_CTRL_REGION_EN_MASK BIT(31)
+#define IATU_REGION_CTRL_MATCH_MODE_MASK BIT(30)
+#define IATU_REGION_CTRL_NUM_MATCH_EN_MASK BIT(19)
+#define IATU_REGION_CTRL_BAR_NUM_MASK GENMASK(10, 8)
+
+/**
+ * hl_pci_bars_map() - Map PCI BARs.
+ * @hdev: Pointer to hl_device structure.
+ * @name: Array of BAR names.
+ * @is_wc: Array with flag per BAR whether a write-combined mapping is needed.
+ *
+ * Request PCI regions and map them to kernel virtual addresses.
+ *
+ * Return: 0 on success, non-zero for failure.
+ */
+int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3],
+ bool is_wc[3])
+{
+ struct pci_dev *pdev = hdev->pdev;
+ int rc, i, bar;
+
+ rc = pci_request_regions(pdev, HL_NAME);
+ if (rc) {
+ dev_err(hdev->dev, "Cannot obtain PCI resources\n");
+ return rc;
+ }
+
+ for (i = 0 ; i < 3 ; i++) {
+ bar = i * 2; /* 64-bit BARs */
+ hdev->pcie_bar[bar] = is_wc[i] ?
+ pci_ioremap_wc_bar(pdev, bar) :
+ pci_ioremap_bar(pdev, bar);
+ if (!hdev->pcie_bar[bar]) {
+ dev_err(hdev->dev, "pci_ioremap%s_bar failed for %s\n",
+ is_wc[i] ? "_wc" : "", name[i]);
+ rc = -ENODEV;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ for (i = 2 ; i >= 0 ; i--) {
+ bar = i * 2; /* 64-bit BARs */
+ if (hdev->pcie_bar[bar])
+ iounmap(hdev->pcie_bar[bar]);
+ }
+
+ pci_release_regions(pdev);
+
+ return rc;
+}
+
+/**
+ * hl_pci_bars_unmap() - Unmap PCI BARS.
+ * @hdev: Pointer to hl_device structure.
+ *
+ * Release all PCI BARs and unmap their virtual addresses.
+ */
+static void hl_pci_bars_unmap(struct hl_device *hdev)
+{
+ struct pci_dev *pdev = hdev->pdev;
+ int i, bar;
+
+ for (i = 2 ; i >= 0 ; i--) {
+ bar = i * 2; /* 64-bit BARs */
+ iounmap(hdev->pcie_bar[bar]);
+ }
+
+ pci_release_regions(pdev);
+}
+
+int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data)
+{
+ struct pci_dev *pdev = hdev->pdev;
+ ktime_t timeout;
+ u64 msec;
+ u32 val;
+
+ if (hdev->pldm)
+ msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC;
+ else
+ msec = HL_PCI_ELBI_TIMEOUT_MSEC;
+
+ /* Clear previous status */
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0);
+
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_ADDR, (u32) addr);
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL, 0);
+
+ timeout = ktime_add_ms(ktime_get(), msec);
+ for (;;) {
+ pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val);
+ if (val & PCI_CONFIG_ELBI_STS_MASK)
+ break;
+ if (ktime_compare(ktime_get(), timeout) > 0) {
+ pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS,
+ &val);
+ break;
+ }
+
+ usleep_range(300, 500);
+ }
+
+ if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE) {
+ pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_DATA, data);
+
+ if (unlikely(trace_habanalabs_elbi_read_enabled()))
+ trace_habanalabs_elbi_read(&hdev->pdev->dev, (u32) addr, val);
+
+ return 0;
+ }
+
+ if (val & PCI_CONFIG_ELBI_STS_ERR) {
+ dev_err(hdev->dev, "Error reading from ELBI\n");
+ return -EIO;
+ }
+
+ if (!(val & PCI_CONFIG_ELBI_STS_MASK)) {
+ dev_err(hdev->dev, "ELBI read didn't finish in time\n");
+ return -EIO;
+ }
+
+ dev_err(hdev->dev, "ELBI read has undefined bits in status\n");
+ return -EIO;
+}
+
+/**
+ * hl_pci_elbi_write() - Write through the ELBI interface.
+ * @hdev: Pointer to hl_device structure.
+ * @addr: Address to write to
+ * @data: Data to write
+ *
+ * Return: 0 on success, negative value for failure.
+ */
+static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data)
+{
+ struct pci_dev *pdev = hdev->pdev;
+ ktime_t timeout;
+ u64 msec;
+ u32 val;
+
+ if (hdev->pldm)
+ msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC;
+ else
+ msec = HL_PCI_ELBI_TIMEOUT_MSEC;
+
+ /* Clear previous status */
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0);
+
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_ADDR, (u32) addr);
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_DATA, data);
+ pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL,
+ PCI_CONFIG_ELBI_CTRL_WRITE);
+
+ timeout = ktime_add_ms(ktime_get(), msec);
+ for (;;) {
+ pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val);
+ if (val & PCI_CONFIG_ELBI_STS_MASK)
+ break;
+ if (ktime_compare(ktime_get(), timeout) > 0) {
+ pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS,
+ &val);
+ break;
+ }
+
+ usleep_range(300, 500);
+ }
+
+ if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE) {
+ if (unlikely(trace_habanalabs_elbi_write_enabled()))
+ trace_habanalabs_elbi_write(&hdev->pdev->dev, (u32) addr, val);
+ return 0;
+ }
+
+ if (val & PCI_CONFIG_ELBI_STS_ERR)
+ return -EIO;
+
+ if (!(val & PCI_CONFIG_ELBI_STS_MASK)) {
+ dev_err(hdev->dev, "ELBI write didn't finish in time\n");
+ return -EIO;
+ }
+
+ dev_err(hdev->dev, "ELBI write has undefined bits in status\n");
+ return -EIO;
+}
+
+/**
+ * hl_pci_iatu_write() - iatu write routine.
+ * @hdev: Pointer to hl_device structure.
+ * @addr: Address to write to
+ * @data: Data to write
+ *
+ * Return: 0 on success, negative value for failure.
+ */
+int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 dbi_offset;
+ int rc;
+
+ dbi_offset = addr & 0xFFF;
+
+ /* Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
+ * in case the firmware security is enabled
+ */
+ hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0x00300000);
+
+ rc = hl_pci_elbi_write(hdev, prop->pcie_dbi_base_address + dbi_offset,
+ data);
+
+ if (rc)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * hl_pci_set_inbound_region() - Configure inbound region
+ * @hdev: Pointer to hl_device structure.
+ * @region: Inbound region number.
+ * @pci_region: Inbound region parameters.
+ *
+ * Configure the iATU inbound region.
+ *
+ * Return: 0 on success, negative value for failure.
+ */
+int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region,
+ struct hl_inbound_pci_region *pci_region)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 bar_phys_base, region_base, region_end_address;
+ u32 offset, ctrl_reg_val;
+ int rc = 0;
+
+ /* region offset */
+ offset = (0x200 * region) + 0x100;
+
+ if (pci_region->mode == PCI_ADDRESS_MATCH_MODE) {
+ bar_phys_base = hdev->pcie_bar_phys[pci_region->bar];
+ region_base = bar_phys_base + pci_region->offset_in_bar;
+ region_end_address = region_base + pci_region->size - 1;
+
+ rc |= hl_pci_iatu_write(hdev, offset + 0x8,
+ lower_32_bits(region_base));
+ rc |= hl_pci_iatu_write(hdev, offset + 0xC,
+ upper_32_bits(region_base));
+ rc |= hl_pci_iatu_write(hdev, offset + 0x10,
+ lower_32_bits(region_end_address));
+ }
+
+ /* Point to the specified address */
+ rc |= hl_pci_iatu_write(hdev, offset + 0x14, lower_32_bits(pci_region->addr));
+ rc |= hl_pci_iatu_write(hdev, offset + 0x18, upper_32_bits(pci_region->addr));
+
+ /* Set bar type as memory */
+ rc |= hl_pci_iatu_write(hdev, offset + 0x0, 0);
+
+ /* Enable + bar/address match + match enable + bar number */
+ ctrl_reg_val = FIELD_PREP(IATU_REGION_CTRL_REGION_EN_MASK, 1);
+ ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_MATCH_MODE_MASK, pci_region->mode);
+ ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_NUM_MATCH_EN_MASK, 1);
+
+ if (pci_region->mode == PCI_BAR_MATCH_MODE)
+ ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_BAR_NUM_MASK, pci_region->bar);
+
+ rc |= hl_pci_iatu_write(hdev, offset + 0x4, ctrl_reg_val);
+
+ /* Return the DBI window to the default location
+ * Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
+ * in case the firmware security is enabled
+ */
+ hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
+
+ if (rc)
+ dev_err(hdev->dev, "failed to map bar %u to 0x%08llx\n",
+ pci_region->bar, pci_region->addr);
+
+ return rc;
+}
+
+/**
+ * hl_pci_set_outbound_region() - Configure outbound region 0
+ * @hdev: Pointer to hl_device structure.
+ * @pci_region: Outbound region parameters.
+ *
+ * Configure the iATU outbound region 0.
+ *
+ * Return: 0 on success, negative value for failure.
+ */
+int hl_pci_set_outbound_region(struct hl_device *hdev,
+ struct hl_outbound_pci_region *pci_region)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 outbound_region_end_address;
+ int rc = 0;
+
+ /* Outbound Region 0 */
+ outbound_region_end_address =
+ pci_region->addr + pci_region->size - 1;
+ rc |= hl_pci_iatu_write(hdev, 0x008,
+ lower_32_bits(pci_region->addr));
+ rc |= hl_pci_iatu_write(hdev, 0x00C,
+ upper_32_bits(pci_region->addr));
+ rc |= hl_pci_iatu_write(hdev, 0x010,
+ lower_32_bits(outbound_region_end_address));
+ rc |= hl_pci_iatu_write(hdev, 0x014, 0);
+
+ rc |= hl_pci_iatu_write(hdev, 0x018, 0);
+
+ rc |= hl_pci_iatu_write(hdev, 0x020,
+ upper_32_bits(outbound_region_end_address));
+ /* Increase region size */
+ rc |= hl_pci_iatu_write(hdev, 0x000, 0x00002000);
+ /* Enable */
+ rc |= hl_pci_iatu_write(hdev, 0x004, 0x80000000);
+
+ /* Return the DBI window to the default location
+ * Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
+ * in case the firmware security is enabled
+ */
+ hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
+
+ return rc;
+}
+
+/**
+ * hl_get_pci_memory_region() - get PCI region for given address
+ * @hdev: Pointer to hl_device structure.
+ * @addr: device address
+ *
+ * @return region index on success, otherwise PCI_REGION_NUMBER (invalid
+ * region index)
+ */
+enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr)
+{
+ int i;
+
+ for (i = 0 ; i < PCI_REGION_NUMBER ; i++) {
+ struct pci_mem_region *region = &hdev->pci_mem_region[i];
+
+ if (!region->used)
+ continue;
+
+ if ((addr >= region->region_base) &&
+ (addr < region->region_base + region->region_size))
+ return i;
+ }
+
+ return PCI_REGION_NUMBER;
+}
+
+/**
+ * hl_pci_init() - PCI initialization code.
+ * @hdev: Pointer to hl_device structure.
+ *
+ * Set DMA masks, initialize the PCI controller and map the PCI BARs.
+ *
+ * Return: 0 on success, non-zero for failure.
+ */
+int hl_pci_init(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct pci_dev *pdev = hdev->pdev;
+ int rc;
+
+ rc = pci_enable_device_mem(pdev);
+ if (rc) {
+ dev_err(hdev->dev, "can't enable PCI device\n");
+ return rc;
+ }
+
+ pci_set_master(pdev);
+
+ rc = hdev->asic_funcs->pci_bars_map(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to map PCI BAR addresses\n");
+ goto disable_device;
+ }
+
+ rc = hdev->asic_funcs->init_iatu(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "PCI controller was not initialized successfully\n");
+ goto unmap_pci_bars;
+ }
+
+ /* Driver must sleep in order for FW to finish the iATU configuration */
+ if (hdev->asic_prop.iatu_done_by_fw)
+ usleep_range(2000, 3000);
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(prop->dma_mask));
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to set dma mask to %d bits, error %d\n",
+ prop->dma_mask, rc);
+ goto unmap_pci_bars;
+ }
+
+ dma_set_max_seg_size(&pdev->dev, U32_MAX);
+
+ return 0;
+
+unmap_pci_bars:
+ hl_pci_bars_unmap(hdev);
+disable_device:
+ pci_disable_device(pdev);
+
+ return rc;
+}
+
+/**
+ * hl_pci_fini() - PCI finalization code.
+ * @hdev: Pointer to hl_device structure
+ *
+ * Unmap PCI bars and disable PCI device.
+ */
+void hl_pci_fini(struct hl_device *hdev)
+{
+ hl_pci_bars_unmap(hdev);
+
+ pci_disable_device(hdev->pdev);
+}
diff --git a/drivers/accel/habanalabs/common/security.c b/drivers/accel/habanalabs/common/security.c
new file mode 100644
index 000000000000..5402a3cd0491
--- /dev/null
+++ b/drivers/accel/habanalabs/common/security.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2020 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+
+static const char * const hl_glbl_error_cause[] = {
+ "Error due to un-priv read",
+ "Error due to un-secure read",
+ "Error due to read from unmapped reg",
+ "Error due to un-priv write",
+ "Error due to un-secure write",
+ "Error due to write to unmapped reg",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "External I/F write sec violation",
+ "External I/F write to un-mapped reg",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "N/A",
+ "Read to write only",
+ "Write to read only"
+};
+
+/**
+ * hl_get_pb_block - return the relevant block within the block array
+ *
+ * @hdev: pointer to hl_device structure
+ * @mm_reg_addr: register address in the desired block
+ * @pb_blocks: blocks array
+ * @array_size: blocks array size
+ *
+ */
+static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,
+ const u32 pb_blocks[], int array_size)
+{
+ int i;
+ u32 start_addr, end_addr;
+
+ for (i = 0 ; i < array_size ; i++) {
+ start_addr = pb_blocks[i];
+ end_addr = start_addr + HL_BLOCK_SIZE;
+
+ if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))
+ return i;
+ }
+
+ dev_err(hdev->dev, "No protection domain was found for 0x%x\n",
+ mm_reg_addr);
+ return -EDOM;
+}
+
+/**
+ * hl_unset_pb_in_block - clear a specific protection bit in a block
+ *
+ * @hdev: pointer to hl_device structure
+ * @reg_offset: register offset will be converted to bit offset in pb block
+ * @sgs_entry: pb array
+ *
+ */
+static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,
+ struct hl_block_glbl_sec *sgs_entry)
+{
+ if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) {
+ dev_err(hdev->dev,
+ "Register offset(%d) is out of range(%d) or invalid\n",
+ reg_offset, HL_BLOCK_SIZE);
+ return -EINVAL;
+ }
+
+ UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,
+ (reg_offset & (HL_BLOCK_SIZE - 1)) >> 2);
+
+ return 0;
+}
+
+/**
+ * hl_unsecure_register - locate the relevant block for this register and
+ * remove corresponding protection bit
+ *
+ * @hdev: pointer to hl_device structure
+ * @mm_reg_addr: register address to unsecure
+ * @offset: additional offset to the register address
+ * @pb_blocks: blocks array
+ * @sgs_array: pb array
+ * @array_size: blocks array size
+ *
+ */
+int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
+ const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
+ int array_size)
+{
+ u32 reg_offset;
+ int block_num;
+
+ block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,
+ array_size);
+ if (block_num < 0)
+ return block_num;
+
+ reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];
+
+ return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);
+}
+
+/**
+ * hl_unsecure_register_range - locate the relevant block for this register
+ * range and remove corresponding protection bit
+ *
+ * @hdev: pointer to hl_device structure
+ * @mm_reg_range: register address range to unsecure
+ * @offset: additional offset to the register address
+ * @pb_blocks: blocks array
+ * @sgs_array: pb array
+ * @array_size: blocks array size
+ *
+ */
+static int hl_unsecure_register_range(struct hl_device *hdev,
+ struct range mm_reg_range, int offset, const u32 pb_blocks[],
+ struct hl_block_glbl_sec sgs_array[],
+ int array_size)
+{
+ u32 reg_offset;
+ int i, block_num, rc = 0;
+
+ block_num = hl_get_pb_block(hdev,
+ mm_reg_range.start + offset, pb_blocks,
+ array_size);
+ if (block_num < 0)
+ return block_num;
+
+ for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) {
+ reg_offset = (i + offset) - pb_blocks[block_num];
+ rc |= hl_unset_pb_in_block(hdev, reg_offset,
+ &sgs_array[block_num]);
+ }
+
+ return rc;
+}
+
+/**
+ * hl_unsecure_registers - locate the relevant block for all registers and
+ * remove corresponding protection bit
+ *
+ * @hdev: pointer to hl_device structure
+ * @mm_reg_array: register address array to unsecure
+ * @mm_array_size: register array size
+ * @offset: additional offset to the register address
+ * @pb_blocks: blocks array
+ * @sgs_array: pb array
+ * @blocks_array_size: blocks array size
+ *
+ */
+int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
+ int mm_array_size, int offset, const u32 pb_blocks[],
+ struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
+{
+ int i, rc = 0;
+
+ for (i = 0 ; i < mm_array_size ; i++) {
+ rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,
+ pb_blocks, sgs_array, blocks_array_size);
+
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+/**
+ * hl_unsecure_registers_range - locate the relevant block for all register
+ * ranges and remove corresponding protection bit
+ *
+ * @hdev: pointer to hl_device structure
+ * @mm_reg_range_array: register address range array to unsecure
+ * @mm_array_size: register array size
+ * @offset: additional offset to the register address
+ * @pb_blocks: blocks array
+ * @sgs_array: pb array
+ * @blocks_array_size: blocks array size
+ *
+ */
+static int hl_unsecure_registers_range(struct hl_device *hdev,
+ const struct range mm_reg_range_array[], int mm_array_size,
+ int offset, const u32 pb_blocks[],
+ struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
+{
+ int i, rc = 0;
+
+ for (i = 0 ; i < mm_array_size ; i++) {
+ rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],
+ offset, pb_blocks, sgs_array, blocks_array_size);
+
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+/**
+ * hl_ack_pb_security_violations - Ack security violation
+ *
+ * @hdev: pointer to hl_device structure
+ * @pb_blocks: blocks array
+ * @block_offset: additional offset to the block
+ * @array_size: blocks array size
+ *
+ */
+static void hl_ack_pb_security_violations(struct hl_device *hdev,
+ const u32 pb_blocks[], u32 block_offset, int array_size)
+{
+ int i;
+ u32 cause, addr, block_base;
+
+ for (i = 0 ; i < array_size ; i++) {
+ block_base = pb_blocks[i] + block_offset;
+ cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);
+ if (cause) {
+ addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);
+ hdev->asic_funcs->pb_print_security_errors(hdev,
+ block_base, cause, addr);
+ WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);
+ }
+ }
+}
+
+/**
+ * hl_config_glbl_sec - set pb in HW according to given pb array
+ *
+ * @hdev: pointer to hl_device structure
+ * @pb_blocks: blocks array
+ * @sgs_array: pb array
+ * @block_offset: additional offset to the block
+ * @array_size: blocks array size
+ *
+ */
+void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
+ struct hl_block_glbl_sec sgs_array[], u32 block_offset,
+ int array_size)
+{
+ int i, j;
+ u32 sgs_base;
+
+ if (hdev->pldm)
+ usleep_range(100, 1000);
+
+ for (i = 0 ; i < array_size ; i++) {
+ sgs_base = block_offset + pb_blocks[i] +
+ HL_BLOCK_GLBL_SEC_OFFS;
+
+ for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)
+ WREG32(sgs_base + j * sizeof(u32),
+ sgs_array[i].sec_array[j]);
+ }
+}
+
+/**
+ * hl_secure_block - locally memsets a block to 0
+ *
+ * @hdev: pointer to hl_device structure
+ * @sgs_array: pb array to clear
+ * @array_size: blocks array size
+ *
+ */
+void hl_secure_block(struct hl_device *hdev,
+ struct hl_block_glbl_sec sgs_array[], int array_size)
+{
+ int i;
+
+ for (i = 0 ; i < array_size ; i++)
+ memset((char *)(sgs_array[i].sec_array), 0,
+ HL_BLOCK_GLBL_SEC_SIZE);
+}
+
+/**
+ * hl_init_pb_with_mask - set selected pb instances with mask in HW according
+ * to given configuration
+ *
+ * @hdev: pointer to hl_device structure
+ * @num_dcores: number of decores to apply configuration to
+ * set to HL_PB_SHARED if need to apply only once
+ * @dcore_offset: offset between dcores
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @user_regs_array: unsecured register array
+ * @user_regs_array_size: unsecured register array size
+ * @mask: enabled instances mask: 1- enabled, 0- disabled
+ */
+int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
+ u32 dcore_offset, u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size,
+ const u32 *user_regs_array, u32 user_regs_array_size, u64 mask)
+{
+ int i, j;
+ struct hl_block_glbl_sec *glbl_sec;
+
+ glbl_sec = kcalloc(blocks_array_size,
+ sizeof(struct hl_block_glbl_sec),
+ GFP_KERNEL);
+ if (!glbl_sec)
+ return -ENOMEM;
+
+ hl_secure_block(hdev, glbl_sec, blocks_array_size);
+ hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, 0,
+ pb_blocks, glbl_sec, blocks_array_size);
+
+ /* Fill all blocks with the same configuration */
+ for (i = 0 ; i < num_dcores ; i++) {
+ for (j = 0 ; j < num_instances ; j++) {
+ int seq = i * num_instances + j;
+
+ if (!(mask & BIT_ULL(seq)))
+ continue;
+
+ hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
+ i * dcore_offset + j * instance_offset,
+ blocks_array_size);
+ }
+ }
+
+ kfree(glbl_sec);
+
+ return 0;
+}
+
+/**
+ * hl_init_pb - set pb in HW according to given configuration
+ *
+ * @hdev: pointer to hl_device structure
+ * @num_dcores: number of decores to apply configuration to
+ * set to HL_PB_SHARED if need to apply only once
+ * @dcore_offset: offset between dcores
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @user_regs_array: unsecured register array
+ * @user_regs_array_size: unsecured register array size
+ *
+ */
+int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
+ u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size,
+ const u32 *user_regs_array, u32 user_regs_array_size)
+{
+ return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
+ num_instances, instance_offset, pb_blocks,
+ blocks_array_size, user_regs_array,
+ user_regs_array_size, ULLONG_MAX);
+}
+
+/**
+ * hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to
+ * given configuration unsecurring registers
+ * ranges instead of specific registers
+ *
+ * @hdev: pointer to hl_device structure
+ * @num_dcores: number of decores to apply configuration to
+ * set to HL_PB_SHARED if need to apply only once
+ * @dcore_offset: offset between dcores
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @user_regs_range_array: unsecured register range array
+ * @user_regs_range_array_size: unsecured register range array size
+ * @mask: enabled instances mask: 1- enabled, 0- disabled
+ */
+int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
+ u32 dcore_offset, u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size,
+ const struct range *user_regs_range_array,
+ u32 user_regs_range_array_size, u64 mask)
+{
+ int i, j, rc = 0;
+ struct hl_block_glbl_sec *glbl_sec;
+
+ glbl_sec = kcalloc(blocks_array_size,
+ sizeof(struct hl_block_glbl_sec),
+ GFP_KERNEL);
+ if (!glbl_sec)
+ return -ENOMEM;
+
+ hl_secure_block(hdev, glbl_sec, blocks_array_size);
+ rc = hl_unsecure_registers_range(hdev, user_regs_range_array,
+ user_regs_range_array_size, 0, pb_blocks, glbl_sec,
+ blocks_array_size);
+ if (rc)
+ goto free_glbl_sec;
+
+ /* Fill all blocks with the same configuration */
+ for (i = 0 ; i < num_dcores ; i++) {
+ for (j = 0 ; j < num_instances ; j++) {
+ int seq = i * num_instances + j;
+
+ if (!(mask & BIT_ULL(seq)))
+ continue;
+
+ hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
+ i * dcore_offset + j * instance_offset,
+ blocks_array_size);
+ }
+ }
+
+free_glbl_sec:
+ kfree(glbl_sec);
+
+ return rc;
+}
+
+/**
+ * hl_init_pb_ranges - set pb in HW according to given configuration unsecurring
+ * registers ranges instead of specific registers
+ *
+ * @hdev: pointer to hl_device structure
+ * @num_dcores: number of decores to apply configuration to
+ * set to HL_PB_SHARED if need to apply only once
+ * @dcore_offset: offset between dcores
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @user_regs_range_array: unsecured register range array
+ * @user_regs_range_array_size: unsecured register range array size
+ *
+ */
+int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
+ u32 dcore_offset, u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size,
+ const struct range *user_regs_range_array,
+ u32 user_regs_range_array_size)
+{
+ return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,
+ num_instances, instance_offset, pb_blocks,
+ blocks_array_size, user_regs_range_array,
+ user_regs_range_array_size, ULLONG_MAX);
+}
+
+/**
+ * hl_init_pb_single_dcore - set pb for a single docre in HW
+ * according to given configuration
+ *
+ * @hdev: pointer to hl_device structure
+ * @dcore_offset: offset from the dcore0
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @user_regs_array: unsecured register array
+ * @user_regs_array_size: unsecured register array size
+ *
+ */
+int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
+ u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size,
+ const u32 *user_regs_array, u32 user_regs_array_size)
+{
+ int i, rc = 0;
+ struct hl_block_glbl_sec *glbl_sec;
+
+ glbl_sec = kcalloc(blocks_array_size,
+ sizeof(struct hl_block_glbl_sec),
+ GFP_KERNEL);
+ if (!glbl_sec)
+ return -ENOMEM;
+
+ hl_secure_block(hdev, glbl_sec, blocks_array_size);
+ rc = hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size,
+ 0, pb_blocks, glbl_sec, blocks_array_size);
+ if (rc)
+ goto free_glbl_sec;
+
+ /* Fill all blocks with the same configuration */
+ for (i = 0 ; i < num_instances ; i++)
+ hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
+ dcore_offset + i * instance_offset,
+ blocks_array_size);
+
+free_glbl_sec:
+ kfree(glbl_sec);
+
+ return rc;
+}
+
+/**
+ * hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according
+ * to given configuration unsecurring
+ * registers ranges instead of specific
+ * registers
+ *
+ * @hdev: pointer to hl_device structure
+ * @dcore_offset: offset from the dcore0
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @user_regs_range_array: unsecured register range array
+ * @user_regs_range_array_size: unsecured register range array size
+ *
+ */
+int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
+ u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size,
+ const struct range *user_regs_range_array, u32 user_regs_range_array_size)
+{
+ int i;
+ struct hl_block_glbl_sec *glbl_sec;
+
+ glbl_sec = kcalloc(blocks_array_size,
+ sizeof(struct hl_block_glbl_sec),
+ GFP_KERNEL);
+ if (!glbl_sec)
+ return -ENOMEM;
+
+ hl_secure_block(hdev, glbl_sec, blocks_array_size);
+ hl_unsecure_registers_range(hdev, user_regs_range_array,
+ user_regs_range_array_size, 0, pb_blocks, glbl_sec,
+ blocks_array_size);
+
+ /* Fill all blocks with the same configuration */
+ for (i = 0 ; i < num_instances ; i++)
+ hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
+ dcore_offset + i * instance_offset,
+ blocks_array_size);
+
+ kfree(glbl_sec);
+
+ return 0;
+}
+
+/**
+ * hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration
+ *
+ * @hdev: pointer to hl_device structure
+ * @num_dcores: number of decores to apply configuration to
+ * set to HL_PB_SHARED if need to apply only once
+ * @dcore_offset: offset between dcores
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ * @mask: enabled instances mask: 1- enabled, 0- disabled
+ *
+ */
+void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
+ u32 dcore_offset, u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size, u64 mask)
+{
+ int i, j;
+
+ /* ack all blocks */
+ for (i = 0 ; i < num_dcores ; i++) {
+ for (j = 0 ; j < num_instances ; j++) {
+ int seq = i * num_instances + j;
+
+ if (!(mask & BIT_ULL(seq)))
+ continue;
+
+ hl_ack_pb_security_violations(hdev, pb_blocks,
+ i * dcore_offset + j * instance_offset,
+ blocks_array_size);
+ }
+ }
+}
+
+/**
+ * hl_ack_pb - ack pb in HW according to given configuration
+ *
+ * @hdev: pointer to hl_device structure
+ * @num_dcores: number of decores to apply configuration to
+ * set to HL_PB_SHARED if need to apply only once
+ * @dcore_offset: offset between dcores
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ *
+ */
+void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
+ u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size)
+{
+ hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,
+ instance_offset, pb_blocks, blocks_array_size,
+ ULLONG_MAX);
+}
+
+/**
+ * hl_ack_pb_single_dcore - ack pb for single docre in HW
+ * according to given configuration
+ *
+ * @hdev: pointer to hl_device structure
+ * @dcore_offset: offset from dcore0
+ * @num_instances: number of instances to apply configuration to
+ * @instance_offset: offset between instances
+ * @pb_blocks: blocks array
+ * @blocks_array_size: blocks array size
+ *
+ */
+void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
+ u32 num_instances, u32 instance_offset,
+ const u32 pb_blocks[], u32 blocks_array_size)
+{
+ int i;
+
+ /* ack all blocks */
+ for (i = 0 ; i < num_instances ; i++)
+ hl_ack_pb_security_violations(hdev, pb_blocks,
+ dcore_offset + i * instance_offset,
+ blocks_array_size);
+
+}
+
+static u32 hl_automated_get_block_base_addr(struct hl_device *hdev,
+ struct hl_special_block_info *block_info,
+ u32 major, u32 minor, u32 sub_minor)
+{
+ u32 fw_block_base_address = block_info->base_addr +
+ major * block_info->major_offset +
+ minor * block_info->minor_offset +
+ sub_minor * block_info->sub_minor_offset;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ /* Calculation above returns an address for FW use, and therefore should
+ * be casted for driver use.
+ */
+ return (fw_block_base_address - lower_32_bits(prop->cfg_base_address));
+}
+
+static bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg,
+ int block_type)
+{
+ int i;
+
+ /* Check if block type is listed in the exclusion list of block types */
+ for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++)
+ if (block_type == skip_blocks_cfg->block_types[i])
+ return true;
+
+ return false;
+}
+
+static bool hl_check_block_range_exclusion(struct hl_device *hdev,
+ struct hl_skip_blocks_cfg *skip_blocks_cfg,
+ struct hl_special_block_info *block_info,
+ u32 major, u32 minor, u32 sub_minor)
+{
+ u32 blocks_in_range, block_base_addr_in_range, block_base_addr;
+ int i, j;
+
+ block_base_addr = hl_automated_get_block_base_addr(hdev, block_info,
+ major, minor, sub_minor);
+
+ for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) {
+ blocks_in_range = (skip_blocks_cfg->block_ranges[i].end -
+ skip_blocks_cfg->block_ranges[i].start) /
+ HL_BLOCK_SIZE + 1;
+ for (j = 0 ; j < blocks_in_range ; j++) {
+ block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start +
+ j * HL_BLOCK_SIZE;
+ if (block_base_addr == block_base_addr_in_range)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int hl_read_glbl_errors(struct hl_device *hdev,
+ u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_special_block_info *special_blocks = prop->special_blocks;
+ struct hl_special_block_info *current_block = &special_blocks[blk_idx];
+ u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base,
+ base = current_block->base_addr - lower_32_bits(prop->cfg_base_address);
+ int i;
+
+ block_base = base + major * current_block->major_offset +
+ minor * current_block->minor_offset +
+ sub_minor * current_block->sub_minor_offset;
+
+ glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET;
+ cause_val = RREG32(glbl_err_cause);
+ if (!cause_val)
+ return 0;
+
+ glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET;
+ addr_val = RREG32(glbl_err_addr);
+
+ for (i = 0 ; i <= prop->glbl_err_max_cause_num ; i++) {
+ if (cause_val & BIT(i))
+ dev_err_ratelimited(hdev->dev,
+ "%s, addr %#llx\n",
+ hl_glbl_error_cause[i],
+ prop->cfg_base_address + block_base +
+ FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val));
+ }
+
+ WREG32(glbl_err_cause, cause_val);
+
+ return 0;
+}
+
+void hl_check_for_glbl_errors(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_special_blocks_cfg special_blocks_cfg;
+ struct iterate_special_ctx glbl_err_iter;
+ int rc;
+
+ memset(&special_blocks_cfg, 0, sizeof(special_blocks_cfg));
+ special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg;
+
+ glbl_err_iter.fn = &hl_read_glbl_errors;
+ glbl_err_iter.data = &special_blocks_cfg;
+
+ rc = hl_iterate_special_blocks(hdev, &glbl_err_iter);
+ if (rc)
+ dev_err_ratelimited(hdev->dev,
+ "Could not iterate special blocks, glbl error check failed\n");
+}
+
+int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx)
+{
+ struct hl_special_blocks_cfg *special_blocks_cfg =
+ (struct hl_special_blocks_cfg *)ctx->data;
+ struct hl_skip_blocks_cfg *skip_blocks_cfg =
+ special_blocks_cfg->skip_blocks_cfg;
+ u32 major, minor, sub_minor, blk_idx, num_blocks;
+ struct hl_special_block_info *block_info_arr;
+ int rc;
+
+ block_info_arr = hdev->asic_prop.special_blocks;
+ if (!block_info_arr)
+ return -EINVAL;
+
+ num_blocks = hdev->asic_prop.num_of_special_blocks;
+
+ for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) {
+ if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type))
+ continue;
+
+ for (major = 0 ; major < block_info_arr->major ; major++) {
+ minor = 0;
+ do {
+ sub_minor = 0;
+ do {
+ if ((hl_check_block_range_exclusion(hdev,
+ skip_blocks_cfg, block_info_arr,
+ major, minor, sub_minor)) ||
+ (skip_blocks_cfg->skip_block_hook &&
+ skip_blocks_cfg->skip_block_hook(hdev,
+ special_blocks_cfg,
+ blk_idx, major, minor, sub_minor))) {
+ sub_minor++;
+ continue;
+ }
+
+ rc = ctx->fn(hdev, blk_idx, major, minor,
+ sub_minor, ctx->data);
+ if (rc)
+ return rc;
+
+ sub_minor++;
+ } while (sub_minor < block_info_arr->sub_minor);
+
+ minor++;
+ } while (minor < block_info_arr->minor);
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/accel/habanalabs/common/security.h b/drivers/accel/habanalabs/common/security.h
new file mode 100644
index 000000000000..476f70687c09
--- /dev/null
+++ b/drivers/accel/habanalabs/common/security.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2016-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+#ifndef SECURITY_H_
+#define SECURITY_H_
+
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+struct hl_device;
+
+/* special blocks */
+#define HL_GLBL_ERR_ADDRESS_MASK GENMASK(11, 0)
+/* GLBL_ERR_ADDR register offset from the start of the block */
+#define HL_GLBL_ERR_ADDR_OFFSET 0xF44
+/* GLBL_ERR_CAUSE register offset from the start of the block */
+#define HL_GLBL_ERR_CAUSE_OFFSET 0xF48
+
+/*
+ * struct hl_special_block_info - stores address details of a particular type of
+ * IP block which has a SPECIAL part.
+ *
+ * @block_type: block type as described in every ASIC's block_types enum.
+ * @base_addr: base address of the first block of particular type,
+ * e.g., address of NIC0_UMR0_0 of 'NIC_UMR' block.
+ * @major: number of major blocks of particular type.
+ * @minor: number of minor blocks of particular type.
+ * @sub_minor: number of sub minor blocks of particular type.
+ * @major_offset: address gap between 2 consecutive major blocks of particular type,
+ * e.g., offset between NIC0_UMR0_0 and NIC1_UMR0_0 is 0x80000.
+ * @minor_offset: address gap between 2 consecutive minor blocks of particular type,
+ * e.g., offset between NIC0_UMR0_0 and NIC0_UMR1_0 is 0x20000.
+ * @sub_minor_offset: address gap between 2 consecutive sub_minor blocks of particular
+ * type, e.g., offset between NIC0_UMR0_0 and NIC0_UMR0_1 is 0x1000.
+ *
+ * e.g., in Gaudi2, NIC_UMR blocks can be interpreted as:
+ * NIC<major>_UMR<minor>_<sub_minor> where major=12, minor=2, sub_minor=15.
+ * In other words, for each of 12 major numbers (i.e 0 to 11) there are
+ * 2 blocks with different minor numbers (i.e. 0 to 1). Again, for each minor
+ * number there are 15 blocks with different sub_minor numbers (i.e. 0 to 14).
+ * So different blocks are NIC0_UMR0_0, NIC0_UMR0_1, ..., NIC0_UMR1_0, ....,
+ * NIC11_UMR1_14.
+ *
+ * Struct's formatted data is located in the SOL-based auto-generated protbits headers.
+ */
+struct hl_special_block_info {
+ int block_type;
+ u32 base_addr;
+ u32 major;
+ u32 minor;
+ u32 sub_minor;
+ u32 major_offset;
+ u32 minor_offset;
+ u32 sub_minor_offset;
+};
+
+/*
+ * struct hl_automated_pb_cfg - represents configurations of a particular type
+ * of IP block which has protection bits.
+ *
+ * @addr: address details as described in hl_automation_pb_addr struct.
+ * @prot_map: each bit corresponds to one among 32 protection configuration regs
+ * (e.g., SPECIAL_GLBL_PRIV). '1' means 0xffffffff and '0' means 0x0
+ * to be written into the corresponding protection configuration reg.
+ * This bit is meaningful if same bit in data_map is 0, otherwise ignored.
+ * @data_map: each bit corresponds to one among 32 protection configuration regs
+ * (e.g., SPECIAL_GLBL_PRIV). '1' means corresponding protection
+ * configuration reg is to be written with a value in array pointed
+ * by 'data', otherwise the value is decided by 'prot_map'.
+ * @data: pointer to data array which stores the config value(s) to be written
+ * to corresponding protection configuration reg(s).
+ * @data_size: size of the data array.
+ *
+ * Each bit of 'data_map' and 'prot_map' fields corresponds to one among 32
+ * protection configuration registers e.g., SPECIAL GLBL PRIV regs (starting at
+ * offset 0xE80). '1' in 'data_map' means protection configuration to be done
+ * using configuration in data array. '0' in 'data_map" means protection
+ * configuration to be done as per the value of corresponding bit in 'prot_map'.
+ * '1' in 'prot_map' means the register to be programmed with 0xFFFFFFFF
+ * (all non-protected). '0' in 'prot_map' means the register to be programmed
+ * with 0x0 (all protected).
+ *
+ * e.g., prot_map = 0x00000001, data_map = 0xC0000000 , data = {0xff, 0x12}
+ * SPECIAL_GLBL_PRIV[0] = 0xFFFFFFFF
+ * SPECIAL_GLBL_PRIV[1..29] = 0x0
+ * SPECIAL_GLBL_PRIV[30] = 0xFF
+ * SPECIAL_GLBL_PRIV[31] = 0x12
+ */
+struct hl_automated_pb_cfg {
+ struct hl_special_block_info addr;
+ u32 prot_map;
+ u32 data_map;
+ const u32 *data;
+ u8 data_size;
+};
+
+/* struct hl_special_blocks_cfg - holds special blocks cfg data.
+ *
+ * @priv_automated_pb_cfg: points to the main privileged PB array.
+ * @sec_automated_pb_cfg: points to the main secured PB array.
+ * @skip_blocks_cfg: holds arrays of block types & block ranges to be excluded.
+ * @priv_cfg_size: size of the main privileged PB array.
+ * @sec_cfg_size: size of the main secured PB array.
+ * @prot_lvl_priv: indication if it's a privileged/secured PB configurations.
+ */
+struct hl_special_blocks_cfg {
+ struct hl_automated_pb_cfg *priv_automated_pb_cfg;
+ struct hl_automated_pb_cfg *sec_automated_pb_cfg;
+ struct hl_skip_blocks_cfg *skip_blocks_cfg;
+ u32 priv_cfg_size;
+ u32 sec_cfg_size;
+ u8 prot_lvl_priv;
+};
+
+/* Automated security */
+
+/* struct hl_skip_blocks_cfg - holds arrays of block types & block ranges to be
+ * excluded from special blocks configurations.
+ *
+ * @block_types: an array of block types NOT to be configured.
+ * @block_types_len: len of an array of block types not to be configured.
+ * @block_ranges: an array of block ranges not to be configured.
+ * @block_ranges_len: len of an array of block ranges not to be configured.
+ * @skip_block_hook: hook that will be called before initializing special blocks.
+ */
+struct hl_skip_blocks_cfg {
+ int *block_types;
+ size_t block_types_len;
+ struct range *block_ranges;
+ size_t block_ranges_len;
+ bool (*skip_block_hook)(struct hl_device *hdev,
+ struct hl_special_blocks_cfg *special_blocks_cfg,
+ u32 blk_idx, u32 major, u32 minor, u32 sub_minor);
+};
+
+/**
+ * struct iterate_special_ctx - HW module special block iterator
+ * @fn: function to apply to each HW module special block instance
+ * @data: optional internal data to the function iterator
+ */
+struct iterate_special_ctx {
+ /*
+ * callback for the HW module special block iterator
+ * @hdev: pointer to the habanalabs device structure
+ * @block_id: block (ASIC specific definition can be dcore/hdcore)
+ * @major: major block index within block_id
+ * @minor: minor block index within the major block
+ * @sub_minor: sub_minor block index within the minor block
+ * @data: function specific data
+ */
+ int (*fn)(struct hl_device *hdev, u32 block_id, u32 major, u32 minor,
+ u32 sub_minor, void *data);
+ void *data;
+};
+
+int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx);
+void hl_check_for_glbl_errors(struct hl_device *hdev);
+
+#endif /* SECURITY_H_ */
diff --git a/drivers/misc/habanalabs/common/state_dump.c b/drivers/accel/habanalabs/common/state_dump.c
index 74726907c95e..3a9931f24259 100644
--- a/drivers/misc/habanalabs/common/state_dump.c
+++ b/drivers/accel/habanalabs/common/state_dump.c
@@ -6,7 +6,7 @@
*/
#include <linux/vmalloc.h>
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include "habanalabs.h"
/**
diff --git a/drivers/accel/habanalabs/common/sysfs.c b/drivers/accel/habanalabs/common/sysfs.c
new file mode 100644
index 000000000000..8f55ba3b4e73
--- /dev/null
+++ b/drivers/accel/habanalabs/common/sysfs.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2016-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "habanalabs.h"
+
+#include <linux/pci.h>
+#include <linux/types.h>
+
+static ssize_t clk_max_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long value;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
+ if (value < 0)
+ return value;
+
+ hdev->asic_prop.max_freq_value = value;
+
+ return sprintf(buf, "%lu\n", (value / 1000 / 1000));
+}
+
+static ssize_t clk_max_freq_mhz_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ int rc;
+ u64 value;
+
+ if (!hl_device_operational(hdev, NULL)) {
+ count = -ENODEV;
+ goto fail;
+ }
+
+ rc = kstrtoull(buf, 0, &value);
+ if (rc) {
+ count = -EINVAL;
+ goto fail;
+ }
+
+ hdev->asic_prop.max_freq_value = value * 1000 * 1000;
+
+ hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index, hdev->asic_prop.max_freq_value);
+
+fail:
+ return count;
+}
+
+static ssize_t clk_cur_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long value;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
+ if (value < 0)
+ return value;
+
+ return sprintf(buf, "%lu\n", (value / 1000 / 1000));
+}
+
+static DEVICE_ATTR_RW(clk_max_freq_mhz);
+static DEVICE_ATTR_RO(clk_cur_freq_mhz);
+
+static struct attribute *hl_dev_clk_attrs[] = {
+ &dev_attr_clk_max_freq_mhz.attr,
+ &dev_attr_clk_cur_freq_mhz.attr,
+ NULL,
+};
+
+static ssize_t vrm_ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ struct cpucp_info *cpucp_info;
+ u32 infineon_second_stage_version;
+ u32 infineon_second_stage_first_instance;
+ u32 infineon_second_stage_second_instance;
+ u32 infineon_second_stage_third_instance;
+ u32 mask = 0xff;
+
+ cpucp_info = &hdev->asic_prop.cpucp_info;
+
+ infineon_second_stage_version = le32_to_cpu(cpucp_info->infineon_second_stage_version);
+ infineon_second_stage_first_instance = infineon_second_stage_version & mask;
+ infineon_second_stage_second_instance =
+ (infineon_second_stage_version >> 8) & mask;
+ infineon_second_stage_third_instance =
+ (infineon_second_stage_version >> 16) & mask;
+
+ if (cpucp_info->infineon_version && cpucp_info->infineon_second_stage_version)
+ return sprintf(buf, "%#04x %#04x:%#04x:%#04x\n",
+ le32_to_cpu(cpucp_info->infineon_version),
+ infineon_second_stage_first_instance,
+ infineon_second_stage_second_instance,
+ infineon_second_stage_third_instance);
+ else if (cpucp_info->infineon_second_stage_version)
+ return sprintf(buf, "%#04x:%#04x:%#04x\n",
+ infineon_second_stage_first_instance,
+ infineon_second_stage_second_instance,
+ infineon_second_stage_third_instance);
+ else if (cpucp_info->infineon_version)
+ return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version));
+
+ return 0;
+}
+
+static DEVICE_ATTR_RO(vrm_ver);
+
+static struct attribute *hl_dev_vrm_attrs[] = {
+ &dev_attr_vrm_ver.attr,
+ NULL,
+};
+
+static ssize_t uboot_ver_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", hdev->asic_prop.uboot_ver);
+}
+
+static ssize_t armcp_kernel_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.kernel_version);
+}
+
+static ssize_t armcp_ver_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.cpucp_version);
+}
+
+static ssize_t cpld_ver_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%08x%08x\n",
+ le32_to_cpu(hdev->asic_prop.cpucp_info.cpld_timestamp),
+ le32_to_cpu(hdev->asic_prop.cpucp_info.cpld_version));
+}
+
+static ssize_t cpucp_kernel_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.kernel_version);
+}
+
+static ssize_t cpucp_ver_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.cpucp_version);
+}
+
+static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.fuse_version);
+}
+
+static ssize_t thermal_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.thermal_version);
+}
+
+static ssize_t fw_os_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.fw_os_version);
+}
+
+static ssize_t preboot_btl_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", hdev->asic_prop.preboot_ver);
+}
+
+static ssize_t soft_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long value;
+ int rc;
+
+ rc = kstrtoul(buf, 0, &value);
+
+ if (rc) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ if (!hdev->asic_prop.allow_inference_soft_reset) {
+ dev_err(hdev->dev, "Device does not support inference soft-reset\n");
+ goto out;
+ }
+
+ dev_warn(hdev->dev, "Inference Soft-Reset requested through sysfs\n");
+
+ hl_device_reset(hdev, 0);
+
+out:
+ return count;
+}
+
+static ssize_t hard_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long value;
+ int rc;
+
+ rc = kstrtoul(buf, 0, &value);
+
+ if (rc) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ dev_warn(hdev->dev, "Hard-Reset requested through sysfs\n");
+
+ hl_device_reset(hdev, HL_DRV_RESET_HARD);
+
+out:
+ return count;
+}
+
+static ssize_t device_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ char *str;
+
+ switch (hdev->asic_type) {
+ case ASIC_GOYA:
+ str = "GOYA";
+ break;
+ case ASIC_GAUDI:
+ str = "GAUDI";
+ break;
+ case ASIC_GAUDI_SEC:
+ str = "GAUDI SEC";
+ break;
+ case ASIC_GAUDI2:
+ str = "GAUDI2";
+ break;
+ case ASIC_GAUDI2B:
+ str = "GAUDI2B";
+ break;
+ case ASIC_GAUDI2C:
+ str = "GAUDI2C";
+ break;
+ case ASIC_GAUDI2D:
+ str = "GAUDI2D";
+ break;
+ default:
+ dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
+ hdev->asic_type);
+ return -EINVAL;
+ }
+
+ return sprintf(buf, "%s\n", str);
+}
+
+static ssize_t pci_addr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%04x:%02x:%02x.%x\n",
+ pci_domain_nr(hdev->pdev->bus),
+ hdev->pdev->bus->number,
+ PCI_SLOT(hdev->pdev->devfn),
+ PCI_FUNC(hdev->pdev->devfn));
+}
+
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ char str[HL_STR_MAX];
+
+ strscpy(str, hdev->status[hl_device_status(hdev)], HL_STR_MAX);
+
+ /* use uppercase for backward compatibility */
+ str[0] = 'A' + (str[0] - 'a');
+
+ return sprintf(buf, "%s\n", str);
+}
+
+static ssize_t soft_reset_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", hdev->reset_info.compute_reset_cnt);
+}
+
+static ssize_t hard_reset_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", hdev->reset_info.hard_reset_cnt);
+}
+
+static ssize_t max_power_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ long val;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ val = hl_fw_get_max_power(hdev);
+ if (val < 0)
+ return val;
+
+ return sprintf(buf, "%lu\n", val);
+}
+
+static ssize_t max_power_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ unsigned long value;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL)) {
+ count = -ENODEV;
+ goto out;
+ }
+
+ rc = kstrtoul(buf, 0, &value);
+
+ if (rc) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ hdev->max_power = value;
+ hl_fw_set_max_power(hdev);
+
+out:
+ return count;
+}
+
+static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr, char *buf, loff_t offset,
+ size_t max_size)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct hl_device *hdev = dev_get_drvdata(dev);
+ char *data;
+ int rc;
+
+ if (!hl_device_operational(hdev, NULL))
+ return -ENODEV;
+
+ if (!max_size)
+ return -EINVAL;
+
+ data = kzalloc(max_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ rc = hdev->asic_funcs->get_eeprom_data(hdev, data, max_size);
+ if (rc)
+ goto out;
+
+ memcpy(buf, data, max_size);
+
+out:
+ kfree(data);
+
+ return max_size;
+}
+
+static ssize_t security_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", hdev->asic_prop.fw_security_enabled);
+}
+
+static ssize_t module_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", le32_to_cpu(hdev->asic_prop.cpucp_info.card_location));
+}
+
+static ssize_t parent_device_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hl_device *hdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", HL_DEV_NAME(hdev));
+}
+
+static DEVICE_ATTR_RO(armcp_kernel_ver);
+static DEVICE_ATTR_RO(armcp_ver);
+static DEVICE_ATTR_RO(cpld_ver);
+static DEVICE_ATTR_RO(cpucp_kernel_ver);
+static DEVICE_ATTR_RO(cpucp_ver);
+static DEVICE_ATTR_RO(device_type);
+static DEVICE_ATTR_RO(fuse_ver);
+static DEVICE_ATTR_WO(hard_reset);
+static DEVICE_ATTR_RO(hard_reset_cnt);
+static DEVICE_ATTR_RW(max_power);
+static DEVICE_ATTR_RO(pci_addr);
+static DEVICE_ATTR_RO(preboot_btl_ver);
+static DEVICE_ATTR_WO(soft_reset);
+static DEVICE_ATTR_RO(soft_reset_cnt);
+static DEVICE_ATTR_RO(status);
+static DEVICE_ATTR_RO(thermal_ver);
+static DEVICE_ATTR_RO(uboot_ver);
+static DEVICE_ATTR_RO(fw_os_ver);
+static DEVICE_ATTR_RO(security_enabled);
+static DEVICE_ATTR_RO(module_id);
+static DEVICE_ATTR_RO(parent_device);
+
+static const struct bin_attribute bin_attr_eeprom = {
+ .attr = {.name = "eeprom", .mode = (0444)},
+ .size = PAGE_SIZE,
+ .read = eeprom_read_handler
+};
+
+static struct attribute *hl_dev_attrs[] = {
+ &dev_attr_armcp_kernel_ver.attr,
+ &dev_attr_armcp_ver.attr,
+ &dev_attr_cpld_ver.attr,
+ &dev_attr_cpucp_kernel_ver.attr,
+ &dev_attr_cpucp_ver.attr,
+ &dev_attr_device_type.attr,
+ &dev_attr_fuse_ver.attr,
+ &dev_attr_hard_reset.attr,
+ &dev_attr_hard_reset_cnt.attr,
+ &dev_attr_max_power.attr,
+ &dev_attr_pci_addr.attr,
+ &dev_attr_preboot_btl_ver.attr,
+ &dev_attr_status.attr,
+ &dev_attr_thermal_ver.attr,
+ &dev_attr_uboot_ver.attr,
+ &dev_attr_fw_os_ver.attr,
+ &dev_attr_security_enabled.attr,
+ &dev_attr_module_id.attr,
+ &dev_attr_parent_device.attr,
+ NULL,
+};
+
+static const struct bin_attribute *const hl_dev_bin_attrs[] = {
+ &bin_attr_eeprom,
+ NULL
+};
+
+static struct attribute_group hl_dev_attr_group = {
+ .attrs = hl_dev_attrs,
+ .bin_attrs = hl_dev_bin_attrs,
+};
+
+static struct attribute_group hl_dev_clks_attr_group;
+static struct attribute_group hl_dev_vrm_attr_group;
+
+static const struct attribute_group *hl_dev_attr_groups[] = {
+ &hl_dev_attr_group,
+ &hl_dev_clks_attr_group,
+ &hl_dev_vrm_attr_group,
+ NULL,
+};
+
+static struct attribute *hl_dev_inference_attrs[] = {
+ &dev_attr_soft_reset.attr,
+ &dev_attr_soft_reset_cnt.attr,
+ NULL,
+};
+
+static struct attribute_group hl_dev_inference_attr_group = {
+ .attrs = hl_dev_inference_attrs,
+};
+
+static const struct attribute_group *hl_dev_inference_attr_groups[] = {
+ &hl_dev_inference_attr_group,
+ NULL,
+};
+
+void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp)
+{
+ dev_clk_attr_grp->attrs = hl_dev_clk_attrs;
+}
+
+void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp)
+{
+ dev_vrm_attr_grp->attrs = hl_dev_vrm_attrs;
+}
+
+int hl_sysfs_init(struct hl_device *hdev)
+{
+ int rc;
+
+ hdev->max_power = hdev->asic_prop.max_power_default;
+
+ hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group, &hl_dev_vrm_attr_group);
+
+ rc = device_add_groups(hdev->dev, hl_dev_attr_groups);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to add groups to device, error %d\n", rc);
+ return rc;
+ }
+
+ if (!hdev->asic_prop.allow_inference_soft_reset)
+ return 0;
+
+ rc = device_add_groups(hdev->dev, hl_dev_inference_attr_groups);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to add groups to device, error %d\n", rc);
+ goto remove_groups;
+ }
+
+ return 0;
+
+remove_groups:
+ device_remove_groups(hdev->dev, hl_dev_attr_groups);
+ return rc;
+}
+
+void hl_sysfs_fini(struct hl_device *hdev)
+{
+ device_remove_groups(hdev->dev, hl_dev_attr_groups);
+
+ if (!hdev->asic_prop.allow_inference_soft_reset)
+ return;
+
+ device_remove_groups(hdev->dev, hl_dev_inference_attr_groups);
+}
diff --git a/drivers/misc/habanalabs/gaudi/Makefile b/drivers/accel/habanalabs/gaudi/Makefile
index 10577c33a816..10577c33a816 100644
--- a/drivers/misc/habanalabs/gaudi/Makefile
+++ b/drivers/accel/habanalabs/gaudi/Makefile
diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/accel/habanalabs/gaudi/gaudi.c
index cb2988e2c7a8..34771d75da9d 100644
--- a/drivers/misc/habanalabs/gaudi/gaudi.c
+++ b/drivers/accel/habanalabs/gaudi/gaudi.c
@@ -63,6 +63,10 @@
#define GAUDI_LINUX_FW_FILE "habanalabs/gaudi/gaudi-fit.itb"
#define GAUDI_TPC_FW_FILE "habanalabs/gaudi/gaudi_tpc.bin"
+MODULE_FIRMWARE(GAUDI_BOOT_FIT_FILE);
+MODULE_FIRMWARE(GAUDI_LINUX_FW_FILE);
+MODULE_FIRMWARE(GAUDI_TPC_FW_FILE);
+
#define GAUDI_DMA_POOL_BLK_SIZE 0x100 /* 256 bytes */
#define GAUDI_RESET_TIMEOUT_MSEC 2000 /* 2000ms */
@@ -114,13 +118,6 @@ static u32 gaudi_stream_master[GAUDI_STREAM_MASTER_ARR_SIZE] = {
GAUDI_QUEUE_ID_DMA_1_3
};
-static const char gaudi_irq_name[GAUDI_MSI_ENTRIES][GAUDI_MAX_STRING_LEN] = {
- "gaudi cq 0_0", "gaudi cq 0_1", "gaudi cq 0_2", "gaudi cq 0_3",
- "gaudi cq 1_0", "gaudi cq 1_1", "gaudi cq 1_2", "gaudi cq 1_3",
- "gaudi cq 5_0", "gaudi cq 5_1", "gaudi cq 5_2", "gaudi cq 5_3",
- "gaudi cpu eq"
-};
-
static const u8 gaudi_dma_assignment[GAUDI_DMA_MAX] = {
[GAUDI_PCI_DMA_1] = GAUDI_ENGINE_ID_DMA_0,
[GAUDI_PCI_DMA_2] = GAUDI_ENGINE_ID_DMA_1,
@@ -617,8 +614,6 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
else
prop->mmu_pgt_size = MMU_PAGE_TABLES_SIZE;
prop->mmu_pte_size = HL_PTE_SIZE;
- prop->mmu_hop_table_size = HOP_TABLE_SIZE_512_PTE;
- prop->mmu_hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE;
prop->dram_page_size = PAGE_SIZE_2MB;
prop->device_mem_alloc_default_page_size = prop->dram_page_size;
prop->dram_supports_virtual_memory = false;
@@ -640,8 +635,8 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
prop->pmmu.num_hops = MMU_ARCH_5_HOPS;
prop->pmmu.last_mask = LAST_MASK;
/* TODO: will be duplicated until implementing per-MMU props */
- prop->pmmu.hop_table_size = prop->mmu_hop_table_size;
- prop->pmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size;
+ prop->pmmu.hop_table_size = HOP_TABLE_SIZE_512_PTE;
+ prop->pmmu.hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE;
/* PMMU and HPMMU are the same except of page size */
memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu));
@@ -652,10 +647,12 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
prop->dmmu.start_addr = (VA_HOST_SPACE_START + VA_HOST_SPACE_SIZE / 2);
prop->dmmu.end_addr = VA_HOST_SPACE_END;
prop->dmmu.page_size = PAGE_SIZE_2MB;
+ prop->dmmu.pgt_size = prop->mmu_pgt_size;
prop->cfg_size = CFG_SIZE;
prop->max_asid = MAX_ASID;
prop->num_of_events = GAUDI_EVENT_SIZE;
+ prop->max_num_of_engines = GAUDI_ENGINE_ID_SIZE;
prop->tpc_enabled_mask = TPC_ENABLED_MASK;
set_default_power_values(hdev);
@@ -666,7 +663,7 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
prop->pcie_dbi_base_address = mmPCIE_DBI_BASE;
prop->pcie_aux_dbi_reg_addr = CFG_BASE + mmPCIE_AUX_DBI;
- strncpy(prop->cpucp_info.card_name, GAUDI_DEFAULT_CARD_NAME,
+ strscpy_pad(prop->cpucp_info.card_name, GAUDI_DEFAULT_CARD_NAME,
CARD_NAME_MAX_LEN);
prop->max_pending_cs = GAUDI_MAX_PENDING_CS;
@@ -679,6 +676,10 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
(num_sync_stream_queues * HL_RSVD_MONS);
prop->first_available_user_interrupt = USHRT_MAX;
+ prop->tpc_interrupt_id = USHRT_MAX;
+
+ /* single msi */
+ prop->eq_interrupt_id = 0;
for (i = 0 ; i < HL_MAX_DCORES ; i++)
prop->first_available_cq[i] = USHRT_MAX;
@@ -701,6 +702,8 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev)
prop->dma_mask = 48;
+ prop->hbw_flush_reg = mmPCIE_WRAP_RR_ELBI_RD_SEC_REG_CTRL;
+
return 0;
}
@@ -865,13 +868,18 @@ pci_init:
rc = hl_fw_read_preboot_status(hdev);
if (rc) {
if (hdev->reset_on_preboot_fail)
+ /* we are already on failure flow, so don't check if hw_fini fails. */
hdev->asic_funcs->hw_fini(hdev, true, false);
goto pci_fini;
}
if (gaudi_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
dev_dbg(hdev->dev, "H/W state is dirty, must reset before initializing\n");
- hdev->asic_funcs->hw_fini(hdev, true, false);
+ rc = hdev->asic_funcs->hw_fini(hdev, true, false);
+ if (rc) {
+ dev_err(hdev->dev, "failed to reset HW in dirty state (%d)\n", rc);
+ goto pci_fini;
+ }
}
return 0;
@@ -899,12 +907,13 @@ static int gaudi_early_fini(struct hl_device *hdev)
*/
static int gaudi_fetch_psoc_frequency(struct hl_device *hdev)
{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
u32 nr = 0, nf = 0, od = 0, div_fctr = 0, pll_clk, div_sel;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS], freq;
int rc;
- if (hdev->asic_prop.fw_security_enabled) {
+ if ((hdev->fw_components & FW_TYPE_LINUX) &&
+ (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PLL_INFO_EN)) {
struct gaudi_device *gaudi = hdev->asic_specific;
if (!(gaudi->hw_cap_initialized & HW_CAP_CPU_Q))
@@ -939,9 +948,7 @@ static int gaudi_fetch_psoc_frequency(struct hl_device *hdev)
else
freq = pll_clk / (div_fctr + 1);
} else {
- dev_warn(hdev->dev,
- "Received invalid div select value: %d",
- div_sel);
+ dev_warn(hdev->dev, "Received invalid div select value: %#x", div_sel);
freq = 0;
}
}
@@ -985,9 +992,10 @@ static int _gaudi_init_tpc_mem(struct hl_device *hdev,
init_tpc_mem_pkt->ctl = cpu_to_le32(ctl);
init_tpc_mem_pkt->src_addr = cpu_to_le64(tpc_kernel_src_addr);
- dst_addr = (prop->sram_user_base_address &
- GAUDI_PKT_LIN_DMA_DST_ADDR_MASK) >>
- GAUDI_PKT_LIN_DMA_DST_ADDR_SHIFT;
+
+ /* TPC_CMD is configured with I$ prefetch enabled, so address should be aligned to 8KB */
+ dst_addr = FIELD_PREP(GAUDI_PKT_LIN_DMA_DST_ADDR_MASK,
+ round_up(prop->sram_user_base_address, SZ_8K));
init_tpc_mem_pkt->dst_addr |= cpu_to_le64(dst_addr);
job = hl_cs_allocate_job(hdev, QUEUE_TYPE_EXT, true);
@@ -1464,8 +1472,7 @@ static int gaudi_collective_wait_create_job(struct hl_device *hdev,
}
/* Allocate internal mapped CB for non patched CBs */
- cb = hl_cb_kernel_create(hdev, cb_size,
- hdev->mmu_enable && !patched_cb);
+ cb = hl_cb_kernel_create(hdev, cb_size, !patched_cb);
if (!cb) {
atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
atomic64_inc(&cntr->out_of_mem_drop_cnt);
@@ -1632,10 +1639,8 @@ static int gaudi_late_init(struct hl_device *hdev)
}
rc = hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_ENABLE_PCI_ACCESS, 0x0);
- if (rc) {
- dev_err(hdev->dev, "Failed to enable PCI access from CPU\n");
+ if (rc)
return rc;
- }
/* Scrub both SRAM and DRAM */
rc = hdev->asic_funcs->scrub_device_mem(hdev);
@@ -1683,23 +1688,7 @@ disable_pci_access:
static void gaudi_late_fini(struct hl_device *hdev)
{
- const struct hwmon_channel_info **channel_info_arr;
- int i = 0;
-
- if (!hdev->hl_chip_info->info)
- return;
-
- channel_info_arr = hdev->hl_chip_info->info;
-
- while (channel_info_arr[i]) {
- kfree(channel_info_arr[i]->config);
- kfree(channel_info_arr[i]);
- i++;
- }
-
- kfree(channel_info_arr);
-
- hdev->hl_chip_info->info = NULL;
+ hl_hwmon_release_resources(hdev);
}
static int gaudi_alloc_cpu_accessible_dma_mem(struct hl_device *hdev)
@@ -2024,38 +2013,6 @@ static int gaudi_enable_msi_single(struct hl_device *hdev)
return rc;
}
-static int gaudi_enable_msi_multi(struct hl_device *hdev)
-{
- int cq_cnt = hdev->asic_prop.completion_queues_count;
- int rc, i, irq_cnt_init, irq;
-
- for (i = 0, irq_cnt_init = 0 ; i < cq_cnt ; i++, irq_cnt_init++) {
- irq = gaudi_pci_irq_vector(hdev, i, false);
- rc = request_irq(irq, hl_irq_handler_cq, 0, gaudi_irq_name[i],
- &hdev->completion_queue[i]);
- if (rc) {
- dev_err(hdev->dev, "Failed to request IRQ %d", irq);
- goto free_irqs;
- }
- }
-
- irq = gaudi_pci_irq_vector(hdev, GAUDI_EVENT_QUEUE_MSI_IDX, true);
- rc = request_irq(irq, hl_irq_handler_eq, 0, gaudi_irq_name[cq_cnt],
- &hdev->event_queue);
- if (rc) {
- dev_err(hdev->dev, "Failed to request IRQ %d", irq);
- goto free_irqs;
- }
-
- return 0;
-
-free_irqs:
- for (i = 0 ; i < irq_cnt_init ; i++)
- free_irq(gaudi_pci_irq_vector(hdev, i, false),
- &hdev->completion_queue[i]);
- return rc;
-}
-
static int gaudi_enable_msi(struct hl_device *hdev)
{
struct gaudi_device *gaudi = hdev->asic_specific;
@@ -2070,14 +2027,7 @@ static int gaudi_enable_msi(struct hl_device *hdev)
return rc;
}
- if (rc < NUMBER_OF_INTERRUPTS) {
- gaudi->multi_msi_mode = false;
- rc = gaudi_enable_msi_single(hdev);
- } else {
- gaudi->multi_msi_mode = true;
- rc = gaudi_enable_msi_multi(hdev);
- }
-
+ rc = gaudi_enable_msi_single(hdev);
if (rc)
goto free_pci_irq_vectors;
@@ -2093,47 +2043,23 @@ free_pci_irq_vectors:
static void gaudi_sync_irqs(struct hl_device *hdev)
{
struct gaudi_device *gaudi = hdev->asic_specific;
- int i, cq_cnt = hdev->asic_prop.completion_queues_count;
if (!(gaudi->hw_cap_initialized & HW_CAP_MSI))
return;
/* Wait for all pending IRQs to be finished */
- if (gaudi->multi_msi_mode) {
- for (i = 0 ; i < cq_cnt ; i++)
- synchronize_irq(gaudi_pci_irq_vector(hdev, i, false));
-
- synchronize_irq(gaudi_pci_irq_vector(hdev,
- GAUDI_EVENT_QUEUE_MSI_IDX,
- true));
- } else {
- synchronize_irq(gaudi_pci_irq_vector(hdev, 0, false));
- }
+ synchronize_irq(gaudi_pci_irq_vector(hdev, 0, false));
}
static void gaudi_disable_msi(struct hl_device *hdev)
{
struct gaudi_device *gaudi = hdev->asic_specific;
- int i, irq, cq_cnt = hdev->asic_prop.completion_queues_count;
if (!(gaudi->hw_cap_initialized & HW_CAP_MSI))
return;
gaudi_sync_irqs(hdev);
-
- if (gaudi->multi_msi_mode) {
- irq = gaudi_pci_irq_vector(hdev, GAUDI_EVENT_QUEUE_MSI_IDX,
- true);
- free_irq(irq, &hdev->event_queue);
-
- for (i = 0 ; i < cq_cnt ; i++) {
- irq = gaudi_pci_irq_vector(hdev, i, false);
- free_irq(irq, &hdev->completion_queue[i]);
- }
- } else {
- free_irq(gaudi_pci_irq_vector(hdev, 0, false), hdev);
- }
-
+ free_irq(gaudi_pci_irq_vector(hdev, 0, false), hdev);
pci_free_irq_vectors(hdev->pdev);
gaudi->hw_cap_initialized &= ~HW_CAP_MSI;
@@ -3718,21 +3644,18 @@ static int gaudi_mmu_init(struct hl_device *hdev)
u64 hop0_addr;
int rc, i;
- if (!hdev->mmu_enable)
- return 0;
-
if (gaudi->hw_cap_initialized & HW_CAP_MMU)
return 0;
for (i = 0 ; i < prop->max_asid ; i++) {
hop0_addr = prop->mmu_pgt_addr +
- (i * prop->mmu_hop_table_size);
+ (i * prop->dmmu.hop_table_size);
rc = gaudi_mmu_update_asid_hop0_addr(hdev, i, hop0_addr);
if (rc) {
dev_err(hdev->dev,
"failed to set hop0 addr for asid %d\n", i);
- goto err;
+ return rc;
}
}
@@ -3743,7 +3666,9 @@ static int gaudi_mmu_init(struct hl_device *hdev)
/* mem cache invalidation */
WREG32(mmSTLB_MEM_CACHE_INVALIDATION, 1);
- hl_mmu_invalidate_cache(hdev, true, 0);
+ rc = hl_mmu_invalidate_cache(hdev, true, 0);
+ if (rc)
+ return rc;
WREG32(mmMMU_UP_MMU_ENABLE, 1);
WREG32(mmMMU_UP_SPI_MASK, 0xF);
@@ -3759,9 +3684,6 @@ static int gaudi_mmu_init(struct hl_device *hdev)
gaudi->hw_cap_initialized |= HW_CAP_MMU;
return 0;
-
-err:
- return rc;
}
static int gaudi_load_firmware_to_device(struct hl_device *hdev)
@@ -3929,11 +3851,7 @@ static int gaudi_init_cpu_queues(struct hl_device *hdev, u32 cpu_timeout)
WREG32(mmCPU_IF_PF_PQ_PI, 0);
- if (gaudi->multi_msi_mode)
- WREG32(mmCPU_IF_QUEUE_INIT, PQ_INIT_STATUS_READY_FOR_CP);
- else
- WREG32(mmCPU_IF_QUEUE_INIT,
- PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI);
+ WREG32(mmCPU_IF_QUEUE_INIT, PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI);
irq_handler_offset = prop->gic_interrupts_enable ?
mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR :
@@ -4082,7 +4000,7 @@ disable_queues:
return rc;
}
-static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
+static int gaudi_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
{
struct cpu_dyn_regs *dyn_regs =
&hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
@@ -4092,7 +4010,7 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset
if (!hard_reset) {
dev_err(hdev->dev, "GAUDI doesn't support soft-reset\n");
- return;
+ return 0;
}
if (hdev->pldm) {
@@ -4213,10 +4131,10 @@ skip_reset:
msleep(reset_timeout_ms);
status = RREG32(mmPSOC_GLOBAL_CONF_BTM_FSM);
- if (status & PSOC_GLOBAL_CONF_BTM_FSM_STATE_MASK)
- dev_err(hdev->dev,
- "Timeout while waiting for device to reset 0x%x\n",
- status);
+ if (status & PSOC_GLOBAL_CONF_BTM_FSM_STATE_MASK) {
+ dev_err(hdev->dev, "Timeout while waiting for device to reset 0x%x\n", status);
+ return -ETIMEDOUT;
+ }
if (gaudi) {
gaudi->hw_cap_initialized &= ~(HW_CAP_CPU | HW_CAP_CPU_Q | HW_CAP_HBM |
@@ -4229,17 +4147,12 @@ skip_reset:
hdev->device_cpu_is_halted = false;
}
+ return 0;
}
static int gaudi_suspend(struct hl_device *hdev)
{
- int rc;
-
- rc = hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
- if (rc)
- dev_err(hdev->dev, "Failed to disable PCI access from CPU\n");
-
- return rc;
+ return hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
}
static int gaudi_resume(struct hl_device *hdev)
@@ -4252,13 +4165,32 @@ static int gaudi_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
{
int rc;
- vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
- VM_DONTCOPY | VM_NORESERVE;
+ vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
+ VM_DONTCOPY | VM_NORESERVE);
+
+#ifdef _HAS_DMA_MMAP_COHERENT
+ /*
+ * If dma_alloc_coherent() returns a vmalloc address, set VM_MIXEDMAP
+ * so vm_insert_page() can handle it safely. Without this, the kernel
+ * may BUG_ON due to VM_PFNMAP.
+ */
+ if (is_vmalloc_addr(cpu_addr))
+ vm_flags_set(vma, VM_MIXEDMAP);
rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr,
(dma_addr - HOST_PHYS_BASE), size);
if (rc)
dev_err(hdev->dev, "dma_mmap_coherent error %d", rc);
+#else
+
+ rc = remap_pfn_range(vma, vma->vm_start,
+ virt_to_phys(cpu_addr) >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (rc)
+ dev_err(hdev->dev, "remap_pfn_range error %d", rc);
+
+ #endif
+
return rc;
}
@@ -4701,8 +4633,7 @@ static int gaudi_scrub_device_dram(struct hl_device *hdev, u64 val)
static int gaudi_scrub_device_mem(struct hl_device *hdev)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 wait_to_idle_time = hdev->pdev ? HBM_SCRUBBING_TIMEOUT_US :
- min_t(u64, HBM_SCRUBBING_TIMEOUT_US * 10, HL_SIM_MAX_TIMEOUT_US);
+ u64 wait_to_idle_time = HBM_SCRUBBING_TIMEOUT_US;
u64 addr, size, val = hdev->memory_scrub_val;
ktime_t timeout;
int rc = 0;
@@ -4723,7 +4654,7 @@ static int gaudi_scrub_device_mem(struct hl_device *hdev)
addr = prop->sram_user_base_address;
size = hdev->pldm ? 0x10000 : prop->sram_size - SRAM_USER_BASE_OFFSET;
- dev_dbg(hdev->dev, "Scrubing SRAM: 0x%09llx - 0x%09llx val: 0x%llx\n",
+ dev_dbg(hdev->dev, "Scrubbing SRAM: 0x%09llx - 0x%09llx val: 0x%llx\n",
addr, addr + size, val);
rc = gaudi_memset_device_memory(hdev, addr, size, val);
if (rc) {
@@ -4986,7 +4917,7 @@ static int gaudi_pin_memory_before_cs(struct hl_device *hdev,
list_add_tail(&userptr->job_node, parser->job_userptr_list);
- rc = hdev->asic_funcs->asic_dma_map_sgtable(hdev, userptr->sgt, dir);
+ rc = hl_dma_map_sgtable(hdev, userptr->sgt, dir);
if (rc) {
dev_err(hdev->dev, "failed to map sgt with DMA region\n");
goto unpin_memory;
@@ -5609,7 +5540,6 @@ static void gaudi_add_end_of_cb_packets(struct hl_device *hdev, void *kernel_add
u32 len, u32 original_len, u64 cq_addr, u32 cq_val,
u32 msi_vec, bool eb)
{
- struct gaudi_device *gaudi = hdev->asic_specific;
struct packet_msg_prot *cq_pkt;
struct packet_nop *cq_padding;
u64 msi_addr;
@@ -5639,12 +5569,7 @@ static void gaudi_add_end_of_cb_packets(struct hl_device *hdev, void *kernel_add
tmp |= FIELD_PREP(GAUDI_PKT_CTL_MB_MASK, 1);
cq_pkt->ctl = cpu_to_le32(tmp);
cq_pkt->value = cpu_to_le32(1);
-
- if (gaudi->multi_msi_mode)
- msi_addr = mmPCIE_MSI_INTR_0 + msi_vec * 4;
- else
- msi_addr = mmPCIE_CORE_MSI_REQ;
-
+ msi_addr = hdev->pdev ? mmPCIE_CORE_MSI_REQ : mmPCIE_MSI_INTR_0 + msi_vec * 4;
cq_pkt->addr = cpu_to_le64(CFG_BASE + msi_addr);
}
@@ -6448,12 +6373,6 @@ static int gaudi_send_job_on_qman0(struct hl_device *hdev,
else
timeout = HL_DEVICE_TIMEOUT_USEC;
- if (!hdev->asic_funcs->is_device_idle(hdev, NULL, 0, NULL)) {
- dev_err_ratelimited(hdev->dev,
- "Can't send driver job on QMAN0 because the device is not idle\n");
- return -EBUSY;
- }
-
fence_ptr = hl_asic_dma_pool_zalloc(hdev, 4, GFP_KERNEL, &fence_dma_addr);
if (!fence_ptr) {
dev_err(hdev->dev,
@@ -6521,8 +6440,8 @@ event_not_supported:
}
static const char *gaudi_get_razwi_initiator_dma_name(struct hl_device *hdev, u32 x_y,
- bool is_write, s32 *engine_id_1,
- s32 *engine_id_2)
+ bool is_write, u16 *engine_id_1,
+ u16 *engine_id_2)
{
u32 dma_id[2], dma_offset, err_cause[2], mask, i;
@@ -6619,7 +6538,7 @@ unknown_initiator:
}
static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, bool is_write,
- u32 *engine_id_1, u32 *engine_id_2)
+ u16 *engine_id_1, u16 *engine_id_2)
{
u32 val, x_y, axi_id;
@@ -6735,8 +6654,8 @@ static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, bool i
return "unknown initiator";
}
-static void gaudi_print_and_get_razwi_info(struct hl_device *hdev, u32 *engine_id_1,
- u32 *engine_id_2)
+static void gaudi_print_and_get_razwi_info(struct hl_device *hdev, u16 *engine_id_1,
+ u16 *engine_id_2, bool *is_read, bool *is_write)
{
if (RREG32(mmMMU_UP_RAZWI_WRITE_VLD)) {
@@ -6744,6 +6663,7 @@ static void gaudi_print_and_get_razwi_info(struct hl_device *hdev, u32 *engine_i
"RAZWI event caused by illegal write of %s\n",
gaudi_get_razwi_initiator_name(hdev, true, engine_id_1, engine_id_2));
WREG32(mmMMU_UP_RAZWI_WRITE_VLD, 0);
+ *is_write = true;
}
if (RREG32(mmMMU_UP_RAZWI_READ_VLD)) {
@@ -6751,10 +6671,11 @@ static void gaudi_print_and_get_razwi_info(struct hl_device *hdev, u32 *engine_i
"RAZWI event caused by illegal read of %s\n",
gaudi_get_razwi_initiator_name(hdev, false, engine_id_1, engine_id_2));
WREG32(mmMMU_UP_RAZWI_READ_VLD, 0);
+ *is_read = true;
}
}
-static void gaudi_print_and_get_mmu_error_info(struct hl_device *hdev, u64 *addr, u8 *type)
+static void gaudi_print_and_get_mmu_error_info(struct hl_device *hdev, u64 *addr, u64 *event_mask)
{
struct gaudi_device *gaudi = hdev->asic_specific;
u32 val;
@@ -6769,7 +6690,7 @@ static void gaudi_print_and_get_mmu_error_info(struct hl_device *hdev, u64 *addr
*addr |= RREG32(mmMMU_UP_PAGE_ERROR_CAPTURE_VA);
dev_err_ratelimited(hdev->dev, "MMU page fault on va 0x%llx\n", *addr);
- *type = HL_RAZWI_PAGE_FAULT;
+ hl_handle_page_fault(hdev, *addr, 0, true, event_mask);
WREG32(mmMMU_UP_PAGE_ERROR_CAPTURE, 0);
}
@@ -6781,7 +6702,6 @@ static void gaudi_print_and_get_mmu_error_info(struct hl_device *hdev, u64 *addr
*addr |= RREG32(mmMMU_UP_ACCESS_ERROR_CAPTURE_VA);
dev_err_ratelimited(hdev->dev, "MMU access error on va 0x%llx\n", *addr);
- *type = HL_RAZWI_MMU_ACCESS_ERROR;
WREG32(mmMMU_UP_ACCESS_ERROR_CAPTURE, 0);
}
@@ -6911,9 +6831,9 @@ static void gaudi_handle_sw_config_stream_data(struct hl_device *hdev, u32 strea
stream, cq_ptr, size);
if (event_mask & HL_NOTIFIER_EVENT_UNDEFINED_OPCODE) {
- hdev->last_error.undef_opcode.cq_addr = cq_ptr;
- hdev->last_error.undef_opcode.cq_size = size;
- hdev->last_error.undef_opcode.stream_id = stream;
+ hdev->captured_err_info.undef_opcode.cq_addr = cq_ptr;
+ hdev->captured_err_info.undef_opcode.cq_size = size;
+ hdev->captured_err_info.undef_opcode.stream_id = stream;
}
}
@@ -6979,7 +6899,7 @@ static void gaudi_handle_last_pqes_on_err(struct hl_device *hdev, u32 qid_base,
}
if (event_mask & HL_NOTIFIER_EVENT_UNDEFINED_OPCODE) {
- struct undefined_opcode_info *undef_opcode = &hdev->last_error.undef_opcode;
+ struct undefined_opcode_info *undef_opcode = &hdev->captured_err_info.undef_opcode;
u32 arr_idx = undef_opcode->cb_addr_streams_len;
if (arr_idx == 0) {
@@ -7063,11 +6983,11 @@ static void gaudi_handle_qman_err_generic(struct hl_device *hdev,
}
/* check for undefined opcode */
if (glbl_sts_val & TPC0_QM_GLBL_STS1_CP_UNDEF_CMD_ERR_MASK &&
- hdev->last_error.undef_opcode.write_enable) {
- memset(&hdev->last_error.undef_opcode, 0,
- sizeof(hdev->last_error.undef_opcode));
+ hdev->captured_err_info.undef_opcode.write_enable) {
+ memset(&hdev->captured_err_info.undef_opcode, 0,
+ sizeof(hdev->captured_err_info.undef_opcode));
- hdev->last_error.undef_opcode.write_enable = false;
+ hdev->captured_err_info.undef_opcode.write_enable = false;
*event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;
}
@@ -7233,12 +7153,6 @@ static void gaudi_handle_qman_err(struct hl_device *hdev, u16 event_type, u64 *e
switch (event_type) {
case GAUDI_EVENT_TPC0_QM ... GAUDI_EVENT_TPC7_QM:
- /* In TPC QM event, notify on TPC assertion. While there isn't
- * a specific event for assertion yet, the FW generates QM event.
- * The SW upper layer will inspect an internal mapped area to indicate
- * if the event is a tpc assertion or tpc QM.
- */
- *event_mask |= HL_NOTIFIER_EVENT_TPC_ASSERT;
index = event_type - GAUDI_EVENT_TPC0_QM;
qid_base = GAUDI_QUEUE_ID_TPC_0_0 + index * QMAN_STREAMS;
qman_base = mmTPC0_QM_BASE + index * TPC_QMAN_OFFSET;
@@ -7322,47 +7236,45 @@ static void gaudi_handle_qman_err(struct hl_device *hdev, u16 event_type, u64 *e
}
static void gaudi_print_irq_info(struct hl_device *hdev, u16 event_type,
- bool razwi)
+ bool check_razwi, u64 *event_mask)
{
- u32 engine_id_1, engine_id_2;
+ bool is_read = false, is_write = false;
+ u16 engine_id[2], num_of_razwi_eng = 0;
char desc[64] = "";
u64 razwi_addr = 0;
- u8 razwi_type;
- int rc;
+ u8 razwi_flags = 0;
/*
* Init engine id by default as not valid and only if razwi initiated from engine with
* engine id it will get valid value.
- * Init razwi type to default, will be changed only if razwi caused by page fault of
- * MMU access error
*/
- engine_id_1 = U16_MAX;
- engine_id_2 = U16_MAX;
- razwi_type = U8_MAX;
+ engine_id[0] = HL_RAZWI_NA_ENG_ID;
+ engine_id[1] = HL_RAZWI_NA_ENG_ID;
gaudi_get_event_desc(event_type, desc, sizeof(desc));
dev_err_ratelimited(hdev->dev, "Received H/W interrupt %d [\"%s\"]\n",
event_type, desc);
- if (razwi) {
- gaudi_print_and_get_razwi_info(hdev, &engine_id_1, &engine_id_2);
- gaudi_print_and_get_mmu_error_info(hdev, &razwi_addr, &razwi_type);
+ if (check_razwi) {
+ gaudi_print_and_get_razwi_info(hdev, &engine_id[0], &engine_id[1], &is_read,
+ &is_write);
+ gaudi_print_and_get_mmu_error_info(hdev, &razwi_addr, event_mask);
- /* In case it's the first razwi, save its parameters*/
- rc = atomic_cmpxchg(&hdev->last_error.razwi.write_enable, 1, 0);
- if (rc) {
- hdev->last_error.razwi.timestamp = ktime_get();
- hdev->last_error.razwi.addr = razwi_addr;
- hdev->last_error.razwi.engine_id_1 = engine_id_1;
- hdev->last_error.razwi.engine_id_2 = engine_id_2;
- /*
- * If first engine id holds non valid value the razwi initiator
- * does not have engine id
- */
- hdev->last_error.razwi.non_engine_initiator = (engine_id_1 == U16_MAX);
- hdev->last_error.razwi.type = razwi_type;
+ if (is_read)
+ razwi_flags |= HL_RAZWI_READ;
+ if (is_write)
+ razwi_flags |= HL_RAZWI_WRITE;
+ if (engine_id[0] != HL_RAZWI_NA_ENG_ID) {
+ if (engine_id[1] != HL_RAZWI_NA_ENG_ID)
+ num_of_razwi_eng = 2;
+ else
+ num_of_razwi_eng = 1;
}
+
+ if (razwi_flags)
+ hl_handle_razwi(hdev, razwi_addr, engine_id, num_of_razwi_eng,
+ razwi_flags, event_mask);
}
}
@@ -7371,8 +7283,8 @@ static void gaudi_print_out_of_sync_info(struct hl_device *hdev,
{
struct hl_hw_queue *q = &hdev->kernel_queues[GAUDI_QUEUE_ID_CPU_PQ];
- dev_err(hdev->dev, "Out of sync with FW, FW: pi=%u, ci=%u, LKD: pi=%u, ci=%u\n",
- sync_err->pi, sync_err->ci, q->pi, atomic_read(&q->ci));
+ dev_err(hdev->dev, "Out of sync with FW, FW: pi=%u, ci=%u, LKD: pi=%u, ci=%d\n",
+ le32_to_cpu(sync_err->pi), le32_to_cpu(sync_err->ci), q->pi, atomic_read(&q->ci));
}
static void gaudi_print_fw_alive_info(struct hl_device *hdev,
@@ -7380,9 +7292,10 @@ static void gaudi_print_fw_alive_info(struct hl_device *hdev,
{
dev_err(hdev->dev,
"FW alive report: severity=%s, process_id=%u, thread_id=%u, uptime=%llu seconds\n",
- (fw_alive->severity == FW_ALIVE_SEVERITY_MINOR) ?
- "Minor" : "Critical", fw_alive->process_id,
- fw_alive->thread_id, fw_alive->uptime_seconds);
+ (fw_alive->severity == FW_ALIVE_SEVERITY_MINOR) ? "Minor" : "Critical",
+ le32_to_cpu(fw_alive->process_id),
+ le32_to_cpu(fw_alive->thread_id),
+ le64_to_cpu(fw_alive->uptime_seconds));
}
static void gaudi_print_nic_axi_irq_info(struct hl_device *hdev, u16 event_type,
@@ -7427,7 +7340,7 @@ static void gaudi_print_nic_axi_irq_info(struct hl_device *hdev, u16 event_type,
event_type, desc);
}
-static int gaudi_non_hard_reset_late_init(struct hl_device *hdev)
+static int gaudi_compute_reset_late_init(struct hl_device *hdev)
{
/* GAUDI doesn't support any reset except hard-reset */
return -EPERM;
@@ -7607,7 +7520,7 @@ static int tpc_krn_event_to_tpc_id(u16 tpc_dec_event_type)
return (tpc_dec_event_type - GAUDI_EVENT_TPC0_KRN_ERR) / 6;
}
-static void gaudi_print_clk_change_info(struct hl_device *hdev, u16 event_type)
+static void gaudi_print_clk_change_info(struct hl_device *hdev, u16 event_type, u64 *event_mask)
{
ktime_t zero_time = ktime_set(0, 0);
@@ -7635,6 +7548,7 @@ static void gaudi_print_clk_change_info(struct hl_device *hdev, u16 event_type)
hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_THERMAL;
hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].start = ktime_get();
hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = zero_time;
+ *event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
dev_info_ratelimited(hdev->dev,
"Clock throttling due to overheating\n");
break;
@@ -7642,6 +7556,7 @@ static void gaudi_print_clk_change_info(struct hl_device *hdev, u16 event_type)
case GAUDI_EVENT_FIX_THERMAL_ENV_E:
hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_THERMAL;
hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = ktime_get();
+ *event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
dev_info_ratelimited(hdev->dev,
"Thermal envelop is safe, back to optimal clock\n");
break;
@@ -7658,6 +7573,7 @@ static void gaudi_print_clk_change_info(struct hl_device *hdev, u16 event_type)
static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry)
{
struct gaudi_device *gaudi = hdev->asic_specific;
+ struct hl_info_fw_err_info fw_err_info;
u64 data = le64_to_cpu(eq_entry->data[0]), event_mask = 0;
u32 ctl = le32_to_cpu(eq_entry->hdr.ctl);
u32 fw_fatal_err_flag = 0, flags = 0;
@@ -7700,8 +7616,9 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_HBM_0_DERR ... GAUDI_EVENT_HBM_3_DERR:
case GAUDI_EVENT_MMU_DERR:
case GAUDI_EVENT_NIC0_CS_DBG_DERR ... GAUDI_EVENT_NIC4_CS_DBG_DERR:
- gaudi_print_irq_info(hdev, event_type, true);
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data);
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR;
goto reset_device;
@@ -7709,30 +7626,33 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_AXI_ECC:
case GAUDI_EVENT_L2_RAM_ECC:
case GAUDI_EVENT_PLL0 ... GAUDI_EVENT_PLL17:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
goto reset_device;
case GAUDI_EVENT_HBM0_SPI_0:
case GAUDI_EVENT_HBM1_SPI_0:
case GAUDI_EVENT_HBM2_SPI_0:
case GAUDI_EVENT_HBM3_SPI_0:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
gaudi_hbm_read_interrupts(hdev,
gaudi_hbm_event_to_dev(event_type),
&eq_entry->hbm_ecc_data);
fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
goto reset_device;
case GAUDI_EVENT_HBM0_SPI_1:
case GAUDI_EVENT_HBM1_SPI_1:
case GAUDI_EVENT_HBM2_SPI_1:
case GAUDI_EVENT_HBM3_SPI_1:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
gaudi_hbm_read_interrupts(hdev,
gaudi_hbm_event_to_dev(event_type),
&eq_entry->hbm_ecc_data);
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
break;
case GAUDI_EVENT_TPC0_DEC:
@@ -7743,10 +7663,17 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_TPC5_DEC:
case GAUDI_EVENT_TPC6_DEC:
case GAUDI_EVENT_TPC7_DEC:
- gaudi_print_irq_info(hdev, event_type, true);
+ /* In TPC DEC event, notify on TPC assertion. While there isn't
+ * a specific event for assertion yet, the FW generates TPC DEC event.
+ * The SW upper layer will inspect an internal mapped area to indicate
+ * if the event is a TPC Assertion or a "real" TPC DEC.
+ */
+ event_mask |= HL_NOTIFIER_EVENT_TPC_ASSERT;
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
reset_required = gaudi_tpc_read_interrupts(hdev,
tpc_dec_event_to_tpc_id(event_type),
"AXI_SLV_DEC_Error");
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
if (reset_required) {
dev_err(hdev->dev, "reset required due to %s\n",
gaudi_irq_map_table[event_type].name);
@@ -7755,6 +7682,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
goto reset_device;
} else {
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
}
break;
@@ -7766,10 +7694,11 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_TPC5_KRN_ERR:
case GAUDI_EVENT_TPC6_KRN_ERR:
case GAUDI_EVENT_TPC7_KRN_ERR:
- gaudi_print_irq_info(hdev, event_type, true);
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
reset_required = gaudi_tpc_read_interrupts(hdev,
tpc_krn_event_to_tpc_id(event_type),
"KRN_ERR");
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
if (reset_required) {
dev_err(hdev->dev, "reset required due to %s\n",
gaudi_irq_map_table[event_type].name);
@@ -7778,6 +7707,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
goto reset_device;
} else {
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
}
break;
@@ -7803,12 +7733,28 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_HBM_0_SERR ... GAUDI_EVENT_HBM_3_SERR:
fallthrough;
case GAUDI_EVENT_MMU_SERR:
- gaudi_print_irq_info(hdev, event_type, true);
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data);
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
break;
case GAUDI_EVENT_PCIE_DEC:
+ case GAUDI_EVENT_CPU_AXI_SPLITTER:
+ case GAUDI_EVENT_PSOC_AXI_DEC:
+ case GAUDI_EVENT_PSOC_PRSTN_FALL:
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
+ hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI_EVENT_MMU_PAGE_FAULT:
+ case GAUDI_EVENT_MMU_WR_PERM:
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
+ hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
case GAUDI_EVENT_MME0_WBC_RSP:
case GAUDI_EVENT_MME0_SBAB0_RSP:
case GAUDI_EVENT_MME1_WBC_RSP:
@@ -7817,11 +7763,6 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_MME2_SBAB0_RSP:
case GAUDI_EVENT_MME3_WBC_RSP:
case GAUDI_EVENT_MME3_SBAB0_RSP:
- case GAUDI_EVENT_CPU_AXI_SPLITTER:
- case GAUDI_EVENT_PSOC_AXI_DEC:
- case GAUDI_EVENT_PSOC_PRSTN_FALL:
- case GAUDI_EVENT_MMU_PAGE_FAULT:
- case GAUDI_EVENT_MMU_WR_PERM:
case GAUDI_EVENT_RAZWI_OR_ADC:
case GAUDI_EVENT_MME0_QM ... GAUDI_EVENT_MME2_QM:
case GAUDI_EVENT_DMA0_QM ... GAUDI_EVENT_DMA7_QM:
@@ -7838,13 +7779,15 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_NIC4_QM1:
case GAUDI_EVENT_DMA0_CORE ... GAUDI_EVENT_DMA7_CORE:
case GAUDI_EVENT_TPC0_QM ... GAUDI_EVENT_TPC7_QM:
- gaudi_print_irq_info(hdev, event_type, true);
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
gaudi_handle_qman_err(hdev, event_type, &event_mask);
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= (HL_NOTIFIER_EVENT_USER_ENGINE_ERR | HL_NOTIFIER_EVENT_DEVICE_RESET);
break;
case GAUDI_EVENT_RAZWI_OR_ADC_SW:
- gaudi_print_irq_info(hdev, event_type, true);
+ gaudi_print_irq_info(hdev, event_type, true, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
goto reset_device;
case GAUDI_EVENT_TPC0_BMON_SPMU:
@@ -7856,20 +7799,23 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
case GAUDI_EVENT_TPC6_BMON_SPMU:
case GAUDI_EVENT_TPC7_BMON_SPMU:
case GAUDI_EVENT_DMA_BM_CH0 ... GAUDI_EVENT_DMA_BM_CH7:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
break;
case GAUDI_EVENT_NIC_SEI_0 ... GAUDI_EVENT_NIC_SEI_4:
gaudi_print_nic_axi_irq_info(hdev, event_type, &data);
hl_fw_unmask_irq(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
break;
case GAUDI_EVENT_DMA_IF_SEI_0 ... GAUDI_EVENT_DMA_IF_SEI_3:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
gaudi_print_sm_sei_info(hdev, event_type,
&eq_entry->sm_sei_data);
rc = hl_state_dump(hdev);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
if (rc)
dev_err(hdev->dev,
"Error during system state dump %d\n", rc);
@@ -7880,7 +7826,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
break;
case GAUDI_EVENT_FIX_POWER_ENV_S ... GAUDI_EVENT_FIX_THERMAL_ENV_E:
- gaudi_print_clk_change_info(hdev, event_type);
+ gaudi_print_clk_change_info(hdev, event_type, &event_mask);
hl_fw_unmask_irq(hdev, event_type);
break;
@@ -7889,20 +7835,27 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr
dev_err(hdev->dev,
"Received high temp H/W interrupt %d (cause %d)\n",
event_type, cause);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
break;
case GAUDI_EVENT_DEV_RESET_REQ:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
goto reset_device;
case GAUDI_EVENT_PKT_QUEUE_OUT_SYNC:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
gaudi_print_out_of_sync_info(hdev, &eq_entry->pkt_sync_err);
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
goto reset_device;
case GAUDI_EVENT_FW_ALIVE_S:
- gaudi_print_irq_info(hdev, event_type, false);
+ gaudi_print_irq_info(hdev, event_type, false, &event_mask);
gaudi_print_fw_alive_info(hdev, &eq_entry->fw_alive);
+ fw_err_info.err_type = HL_INFO_FW_REPORTED_ERR;
+ fw_err_info.event_id = event_type;
+ fw_err_info.event_mask = &event_mask;
+ hl_handle_fw_err(hdev, &fw_err_info);
goto reset_device;
default:
@@ -7932,14 +7885,18 @@ reset_device:
reset_required = false;
}
- /* despite reset doesn't execute. a notification on
- * occurred event needs to be sent here
- */
- hl_notifier_event_send_all(hdev, event_mask);
- if (reset_required)
- hl_device_reset(hdev, flags);
- else
+ if (reset_required) {
+ /* escalate general hw errors to critical/fatal error */
+ if (event_mask & HL_NOTIFIER_EVENT_GENERAL_HW_ERR)
+ hl_handle_critical_hw_err(hdev, event_type, &event_mask);
+
+ hl_device_cond_reset(hdev, flags, event_mask);
+ } else {
hl_fw_unmask_irq(hdev, event_type);
+ /* Notification on occurred event needs to be sent although reset is not executed */
+ if (event_mask)
+ hl_notifier_event_send_all(hdev, event_mask);
+ }
}
static void *gaudi_get_events_stat(struct hl_device *hdev, bool aggregate, u32 *size)
@@ -8056,7 +8013,7 @@ static int gaudi_cpucp_info_get(struct hl_device *hdev)
return rc;
if (!strlen(prop->cpucp_info.card_name))
- strncpy(prop->cpucp_info.card_name, GAUDI_DEFAULT_CARD_NAME,
+ strscpy_pad(prop->cpucp_info.card_name, GAUDI_DEFAULT_CARD_NAME,
CARD_NAME_MAX_LEN);
hdev->card_type = le32_to_cpu(hdev->asic_prop.cpucp_info.card_type);
@@ -8066,8 +8023,8 @@ static int gaudi_cpucp_info_get(struct hl_device *hdev)
return 0;
}
-static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
- u8 mask_len, struct seq_file *s)
+static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
{
struct gaudi_device *gaudi = hdev->asic_specific;
const char *fmt = "%-5d%-9s%#-14x%#-12x%#x\n";
@@ -8079,8 +8036,8 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
u64 offset;
int i, dma_id, port;
- if (s)
- seq_puts(s,
+ if (e)
+ hl_engine_data_sprintf(e,
"\nDMA is_idle QM_GLBL_STS0 QM_CGM_STS DMA_CORE_STS0\n"
"--- ------- ------------ ---------- -------------\n");
@@ -8097,14 +8054,14 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GAUDI_ENGINE_ID_DMA_0 + dma_id, mask);
- if (s)
- seq_printf(s, fmt, dma_id,
+ if (e)
+ hl_engine_data_sprintf(e, fmt, dma_id,
is_eng_idle ? "Y" : "N", qm_glbl_sts0,
qm_cgm_sts, dma_core_sts0);
}
- if (s)
- seq_puts(s,
+ if (e)
+ hl_engine_data_sprintf(e,
"\nTPC is_idle QM_GLBL_STS0 QM_CGM_STS CFG_STATUS\n"
"--- ------- ------------ ---------- ----------\n");
@@ -8119,14 +8076,14 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GAUDI_ENGINE_ID_TPC_0 + i, mask);
- if (s)
- seq_printf(s, fmt, i,
+ if (e)
+ hl_engine_data_sprintf(e, fmt, i,
is_eng_idle ? "Y" : "N",
qm_glbl_sts0, qm_cgm_sts, tpc_cfg_sts);
}
- if (s)
- seq_puts(s,
+ if (e)
+ hl_engine_data_sprintf(e,
"\nMME is_idle QM_GLBL_STS0 QM_CGM_STS ARCH_STATUS\n"
"--- ------- ------------ ---------- -----------\n");
@@ -8147,20 +8104,21 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GAUDI_ENGINE_ID_MME_0 + i, mask);
- if (s) {
+ if (e) {
if (!is_slave)
- seq_printf(s, fmt, i,
+ hl_engine_data_sprintf(e, fmt, i,
is_eng_idle ? "Y" : "N",
qm_glbl_sts0, qm_cgm_sts, mme_arch_sts);
else
- seq_printf(s, mme_slave_fmt, i,
+ hl_engine_data_sprintf(e, mme_slave_fmt, i,
is_eng_idle ? "Y" : "N", "-",
"-", mme_arch_sts);
}
}
- if (s)
- seq_puts(s, "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n"
+ if (e)
+ hl_engine_data_sprintf(e,
+ "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n"
"--- ------- ------------ ----------\n");
for (i = 0 ; i < (NIC_NUMBER_OF_ENGINES / 2) ; i++) {
@@ -8174,8 +8132,8 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GAUDI_ENGINE_ID_NIC_0 + port, mask);
- if (s)
- seq_printf(s, nic_fmt, port,
+ if (e)
+ hl_engine_data_sprintf(e, nic_fmt, port,
is_eng_idle ? "Y" : "N",
qm_glbl_sts0, qm_cgm_sts);
}
@@ -8189,15 +8147,15 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GAUDI_ENGINE_ID_NIC_0 + port, mask);
- if (s)
- seq_printf(s, nic_fmt, port,
+ if (e)
+ hl_engine_data_sprintf(e, nic_fmt, port,
is_eng_idle ? "Y" : "N",
qm_glbl_sts0, qm_cgm_sts);
}
}
- if (s)
- seq_puts(s, "\n");
+ if (e)
+ hl_engine_data_sprintf(e, "\n");
return is_idle;
}
@@ -8392,20 +8350,27 @@ static int gaudi_internal_cb_pool_init(struct hl_device *hdev,
goto destroy_internal_cb_pool;
}
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
+
rc = hl_mmu_map_contiguous(ctx, hdev->internal_cb_va_base,
hdev->internal_cb_pool_dma_addr,
HOST_SPACE_INTERNAL_CB_SZ);
-
- hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR);
- mutex_unlock(&ctx->mmu_lock);
-
if (rc)
goto unreserve_internal_cb_pool;
+ rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR);
+ if (rc)
+ goto unmap_internal_cb_pool;
+
+ mutex_unlock(&hdev->mmu_lock);
+
return 0;
+unmap_internal_cb_pool:
+ hl_mmu_unmap_contiguous(ctx, hdev->internal_cb_va_base,
+ HOST_SPACE_INTERNAL_CB_SZ);
unreserve_internal_cb_pool:
+ mutex_unlock(&hdev->mmu_lock);
hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base,
HOST_SPACE_INTERNAL_CB_SZ);
destroy_internal_cb_pool:
@@ -8425,13 +8390,13 @@ static void gaudi_internal_cb_pool_fini(struct hl_device *hdev,
if (!(gaudi->hw_cap_initialized & HW_CAP_MMU))
return;
- mutex_lock(&ctx->mmu_lock);
+ mutex_lock(&hdev->mmu_lock);
hl_mmu_unmap_contiguous(ctx, hdev->internal_cb_va_base,
HOST_SPACE_INTERNAL_CB_SZ);
hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base,
HOST_SPACE_INTERNAL_CB_SZ);
hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
- mutex_unlock(&ctx->mmu_lock);
+ mutex_unlock(&hdev->mmu_lock);
gen_pool_destroy(hdev->internal_cb_pool);
@@ -9120,6 +9085,16 @@ static u32 *gaudi_get_stream_master_qid_arr(void)
return gaudi_stream_master;
}
+static int gaudi_set_dram_properties(struct hl_device *hdev)
+{
+ return 0;
+}
+
+static int gaudi_set_binning_masks(struct hl_device *hdev)
+{
+ return 0;
+}
+
static void gaudi_check_if_razwi_happened(struct hl_device *hdev)
{
}
@@ -9148,6 +9123,11 @@ static void gaudi_add_device_attr(struct hl_device *hdev, struct attribute_group
dev_vrm_attr_grp->attrs = gaudi_vrm_dev_attrs;
}
+static int gaudi_send_device_activity(struct hl_device *hdev, bool open)
+{
+ return 0;
+}
+
static const struct hl_asic_funcs gaudi_funcs = {
.early_init = gaudi_early_init,
.early_fini = gaudi_early_fini,
@@ -9173,9 +9153,9 @@ static const struct hl_asic_funcs gaudi_funcs = {
.asic_dma_pool_free = gaudi_dma_pool_free,
.cpu_accessible_dma_pool_alloc = gaudi_cpu_accessible_dma_pool_alloc,
.cpu_accessible_dma_pool_free = gaudi_cpu_accessible_dma_pool_free,
- .hl_dma_unmap_sgtable = hl_dma_unmap_sgtable,
+ .dma_unmap_sgtable = hl_asic_dma_unmap_sgtable,
.cs_parser = gaudi_cs_parser,
- .asic_dma_map_sgtable = hl_dma_map_sgtable,
+ .dma_map_sgtable = hl_asic_dma_map_sgtable,
.add_end_of_cb_packets = gaudi_add_end_of_cb_packets,
.update_eq_ci = gaudi_update_eq_ci,
.context_switch = gaudi_context_switch,
@@ -9192,11 +9172,9 @@ static const struct hl_asic_funcs gaudi_funcs = {
.send_heartbeat = gaudi_send_heartbeat,
.debug_coresight = gaudi_debug_coresight,
.is_device_idle = gaudi_is_device_idle,
- .non_hard_reset_late_init = gaudi_non_hard_reset_late_init,
+ .compute_reset_late_init = gaudi_compute_reset_late_init,
.hw_queues_lock = gaudi_hw_queues_lock,
.hw_queues_unlock = gaudi_hw_queues_unlock,
- .kdma_lock = NULL,
- .kdma_unlock = NULL,
.get_pci_id = gaudi_get_pci_id,
.get_eeprom_data = gaudi_get_eeprom_data,
.get_monitor_dump = gaudi_get_monitor_dump,
@@ -9242,6 +9220,9 @@ static const struct hl_asic_funcs gaudi_funcs = {
.mmu_get_real_page_size = hl_mmu_get_real_page_size,
.access_dev_mem = hl_access_dev_mem,
.set_dram_bar_base = gaudi_set_hbm_bar_base,
+ .send_device_activity = gaudi_send_device_activity,
+ .set_dram_properties = gaudi_set_dram_properties,
+ .set_binning_masks = gaudi_set_binning_masks,
};
/**
diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/accel/habanalabs/gaudi/gaudiP.h
index 4fbcf3f0afe5..831be53bb9d7 100644
--- a/drivers/misc/habanalabs/gaudi/gaudiP.h
+++ b/drivers/accel/habanalabs/gaudi/gaudiP.h
@@ -8,9 +8,9 @@
#ifndef GAUDIP_H_
#define GAUDIP_H_
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include "../common/habanalabs.h"
-#include "../include/common/hl_boot_if.h"
+#include <linux/habanalabs/hl_boot_if.h>
#include "../include/gaudi/gaudi_packets.h"
#include "../include/gaudi/gaudi.h"
#include "../include/gaudi/gaudi_async_events.h"
@@ -28,20 +28,8 @@
#define NUMBER_OF_COLLECTIVE_QUEUES 12
#define NUMBER_OF_SOBS_IN_GRP 11
-/*
- * Number of MSI interrupts IDS:
- * Each completion queue has 1 ID
- * The event queue has 1 ID
- */
-#define NUMBER_OF_INTERRUPTS (NUMBER_OF_CMPLT_QUEUES + \
- NUMBER_OF_CPU_HW_QUEUES)
-
#define GAUDI_STREAM_MASTER_ARR_SIZE 8
-#if (NUMBER_OF_INTERRUPTS > GAUDI_MSI_ENTRIES)
-#error "Number of MSI interrupts must be smaller or equal to GAUDI_MSI_ENTRIES"
-#endif
-
#define CORESIGHT_TIMEOUT_USEC 100000 /* 100 ms */
#define GAUDI_MAX_CLK_FREQ 2200000000ull /* 2200 MHz */
@@ -324,8 +312,6 @@ struct gaudi_internal_qman_info {
* signal we can use this engine in later code paths.
* Each bit is cleared upon reset of its corresponding H/W
* engine.
- * @multi_msi_mode: whether we are working in multi MSI single MSI mode.
- * Multi MSI is possible only with IOMMU enabled.
* @mmu_cache_inv_pi: PI for MMU cache invalidation flow. The H/W expects an
* 8-bit value so use u8.
*/
@@ -345,7 +331,6 @@ struct gaudi_device {
u32 events_stat[GAUDI_EVENT_SIZE];
u32 events_stat_aggregate[GAUDI_EVENT_SIZE];
u32 hw_cap_initialized;
- u8 multi_msi_mode;
u8 mmu_cache_inv_pi;
};
diff --git a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c b/drivers/accel/habanalabs/gaudi/gaudi_coresight.c
index 08108f5fed67..1168fefa33f4 100644
--- a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c
+++ b/drivers/accel/habanalabs/gaudi/gaudi_coresight.c
@@ -11,7 +11,8 @@
#include "../include/gaudi/gaudi_masks.h"
#include "../include/gaudi/gaudi_reg_map.h"
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
+
#define SPMU_SECTION_SIZE MME0_ACC_SPMU_MAX_OFFSET
#define SPMU_EVENT_TYPES_OFFSET 0x400
#define SPMU_MAX_COUNTERS 6
@@ -481,6 +482,11 @@ static int gaudi_config_etf(struct hl_device *hdev,
WREG32(base_reg + 0xFB0, CORESIGHT_UNLOCK);
+ val = RREG32(base_reg + 0x20);
+
+ if ((!params->enable && val == 0x0) || (params->enable && val != 0x0))
+ return 0;
+
val = RREG32(base_reg + 0x304);
val |= 0x1000;
WREG32(base_reg + 0x304, val);
@@ -579,6 +585,13 @@ static int gaudi_config_etr(struct hl_device *hdev,
WREG32(mmPSOC_ETR_LAR, CORESIGHT_UNLOCK);
+ val = RREG32(mmPSOC_ETR_CTL);
+
+ if ((!params->enable && val == 0x0) || (params->enable && val != 0x0))
+ return 0;
+
+
+
val = RREG32(mmPSOC_ETR_FFCR);
val |= 0x1000;
WREG32(mmPSOC_ETR_FFCR, val);
diff --git a/drivers/misc/habanalabs/gaudi/gaudi_security.c b/drivers/accel/habanalabs/gaudi/gaudi_security.c
index 81a3c79a8bc6..81a3c79a8bc6 100644
--- a/drivers/misc/habanalabs/gaudi/gaudi_security.c
+++ b/drivers/accel/habanalabs/gaudi/gaudi_security.c
diff --git a/drivers/misc/habanalabs/gaudi2/Makefile b/drivers/accel/habanalabs/gaudi2/Makefile
index 1e047883ba74..1e047883ba74 100644
--- a/drivers/misc/habanalabs/gaudi2/Makefile
+++ b/drivers/accel/habanalabs/gaudi2/Makefile
diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2.c b/drivers/accel/habanalabs/gaudi2/gaudi2.c
new file mode 100644
index 000000000000..b8c0689dba64
--- /dev/null
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2.c
@@ -0,0 +1,12008 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2020-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+#include "gaudi2P.h"
+#include "gaudi2_masks.h"
+#include "../include/gaudi2/gaudi2_special_blocks.h"
+#include "../include/hw_ip/mmu/mmu_general.h"
+#include "../include/hw_ip/mmu/mmu_v2_0.h"
+#include "../include/gaudi2/gaudi2_packets.h"
+#include "../include/gaudi2/gaudi2_reg_map.h"
+#include "../include/gaudi2/gaudi2_async_ids_map_extended.h"
+#include "../include/gaudi2/arc/gaudi2_arc_common_packets.h"
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+#include <linux/iommu.h>
+
+#define GAUDI2_DMA_POOL_BLK_SIZE SZ_256 /* 256 bytes */
+
+#define GAUDI2_RESET_TIMEOUT_MSEC 2000 /* 2000ms */
+
+#define GAUDI2_RESET_POLL_TIMEOUT_USEC 500000 /* 500ms */
+#define GAUDI2_PLDM_HRESET_TIMEOUT_MSEC 25000 /* 25s */
+#define GAUDI2_PLDM_SRESET_TIMEOUT_MSEC 25000 /* 25s */
+#define GAUDI2_PLDM_RESET_POLL_TIMEOUT_USEC 3000000 /* 3s */
+#define GAUDI2_RESET_POLL_CNT 3
+#define GAUDI2_RESET_WAIT_MSEC 1 /* 1ms */
+#define GAUDI2_CPU_RESET_WAIT_MSEC 100 /* 100ms */
+#define GAUDI2_PLDM_RESET_WAIT_MSEC 1000 /* 1s */
+#define GAUDI2_CB_POOL_CB_CNT 512
+#define GAUDI2_CB_POOL_CB_SIZE SZ_128K /* 128KB */
+#define GAUDI2_MSG_TO_CPU_TIMEOUT_USEC 4000000 /* 4s */
+#define GAUDI2_WAIT_FOR_BL_TIMEOUT_USEC 25000000 /* 25s */
+#define GAUDI2_TEST_QUEUE_WAIT_USEC 100000 /* 100ms */
+#define GAUDI2_PLDM_TEST_QUEUE_WAIT_USEC 1000000 /* 1s */
+
+#define GAUDI2_ALLOC_CPU_MEM_RETRY_CNT 3
+
+/*
+ * since the code already has built-in support for binning of up to MAX_FAULTY_TPCS TPCs
+ * and the code relies on that value (for array size etc..) we define another value
+ * for MAX faulty TPCs which reflects the cluster binning requirements
+ */
+#define MAX_CLUSTER_BINNING_FAULTY_TPCS 1
+#define MAX_FAULTY_XBARS 1
+#define MAX_FAULTY_EDMAS 1
+#define MAX_FAULTY_DECODERS 1
+
+#define GAUDI2_TPC_FULL_MASK 0x1FFFFFF
+#define GAUDI2_HIF_HMMU_FULL_MASK 0xFFFF
+#define GAUDI2_DECODER_FULL_MASK 0x3FF
+
+#define GAUDI2_NA_EVENT_CAUSE 0xFF
+#define GAUDI2_NUM_OF_QM_ERR_CAUSE 18
+#define GAUDI2_NUM_OF_LOWER_QM_ERR_CAUSE 25
+#define GAUDI2_NUM_OF_QM_ARB_ERR_CAUSE 3
+#define GAUDI2_NUM_OF_ARC_SEI_ERR_CAUSE 14
+#define GAUDI2_NUM_OF_CPU_SEI_ERR_CAUSE 3
+#define GAUDI2_NUM_OF_QM_SEI_ERR_CAUSE 2
+#define GAUDI2_NUM_OF_ROT_ERR_CAUSE 22
+#define GAUDI2_NUM_OF_TPC_INTR_CAUSE 31
+#define GAUDI2_NUM_OF_DEC_ERR_CAUSE 25
+#define GAUDI2_NUM_OF_MME_ERR_CAUSE 16
+#define GAUDI2_NUM_OF_MME_WAP_ERR_CAUSE 7
+#define GAUDI2_NUM_OF_DMA_CORE_INTR_CAUSE 8
+#define GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE 19
+#define GAUDI2_NUM_OF_HBM_SEI_CAUSE 9
+#define GAUDI2_NUM_OF_SM_SEI_ERR_CAUSE 3
+#define GAUDI2_NUM_OF_PCIE_ADDR_DEC_ERR_CAUSE 3
+#define GAUDI2_NUM_OF_PMMU_FATAL_ERR_CAUSE 2
+#define GAUDI2_NUM_OF_HIF_FATAL_ERR_CAUSE 2
+#define GAUDI2_NUM_OF_AXI_DRAIN_ERR_CAUSE 2
+#define GAUDI2_NUM_OF_HBM_MC_SPI_CAUSE 5
+
+#define GAUDI2_MMU_CACHE_INV_TIMEOUT_USEC (MMU_CONFIG_TIMEOUT_USEC * 10)
+#define GAUDI2_PLDM_MMU_TIMEOUT_USEC (MMU_CONFIG_TIMEOUT_USEC * 200)
+#define GAUDI2_ARB_WDT_TIMEOUT (0x1000000)
+
+#define GAUDI2_VDEC_TIMEOUT_USEC 10000 /* 10ms */
+#define GAUDI2_PLDM_VDEC_TIMEOUT_USEC (GAUDI2_VDEC_TIMEOUT_USEC * 100)
+
+#define KDMA_TIMEOUT_USEC USEC_PER_SEC
+
+#define IS_DMA_IDLE(dma_core_sts0) \
+ (!((dma_core_sts0) & (DCORE0_EDMA0_CORE_STS0_BUSY_MASK)))
+
+#define IS_DMA_HALTED(dma_core_sts1) \
+ ((dma_core_sts1) & (DCORE0_EDMA0_CORE_STS1_IS_HALT_MASK))
+
+#define IS_MME_IDLE(mme_arch_sts) (((mme_arch_sts) & MME_ARCH_IDLE_MASK) == MME_ARCH_IDLE_MASK)
+
+#define IS_TPC_IDLE(tpc_cfg_sts) (((tpc_cfg_sts) & (TPC_IDLE_MASK)) == (TPC_IDLE_MASK))
+
+#define IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts) \
+ ((((qm_glbl_sts0) & (QM_IDLE_MASK)) == (QM_IDLE_MASK)) && \
+ (((qm_glbl_sts1) & (QM_ARC_IDLE_MASK)) == (QM_ARC_IDLE_MASK)) && \
+ (((qm_cgm_sts) & (CGM_IDLE_MASK)) == (CGM_IDLE_MASK)))
+
+#define PCIE_DEC_EN_MASK 0x300
+#define DEC_WORK_STATE_IDLE 0
+#define DEC_WORK_STATE_PEND 3
+#define IS_DEC_IDLE(dec_swreg15) \
+ (((dec_swreg15) & DCORE0_DEC0_CMD_SWREG15_SW_WORK_STATE_MASK) == DEC_WORK_STATE_IDLE || \
+ ((dec_swreg15) & DCORE0_DEC0_CMD_SWREG15_SW_WORK_STATE_MASK) == DEC_WORK_STATE_PEND)
+
+/* HBM MMU address scrambling parameters */
+#define GAUDI2_HBM_MMU_SCRM_MEM_SIZE SZ_8M
+#define GAUDI2_HBM_MMU_SCRM_DIV_SHIFT 26
+#define GAUDI2_HBM_MMU_SCRM_MOD_SHIFT 0
+#define GAUDI2_HBM_MMU_SCRM_ADDRESS_MASK DRAM_VA_HINT_MASK
+#define GAUDI2_COMPENSATE_TLB_PAGE_SIZE_FACTOR 16
+#define MMU_RANGE_INV_VA_LSB_SHIFT 12
+#define MMU_RANGE_INV_VA_MSB_SHIFT 44
+#define MMU_RANGE_INV_EN_SHIFT 0
+#define MMU_RANGE_INV_ASID_EN_SHIFT 1
+#define MMU_RANGE_INV_ASID_SHIFT 2
+
+/* The last SPI_SEI cause bit, "burst_fifo_full", is expected to be triggered in PMMU because it has
+ * a 2 entries FIFO, and hence it is not enabled for it.
+ */
+#define GAUDI2_PMMU_SPI_SEI_ENABLE_MASK GENMASK(GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE - 2, 0)
+#define GAUDI2_HMMU_SPI_SEI_ENABLE_MASK GENMASK(GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE - 1, 0)
+
+#define GAUDI2_MAX_STRING_LEN 64
+
+#define GAUDI2_VDEC_MSIX_ENTRIES (GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM - \
+ GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM + 1)
+
+#define ENGINE_ID_DCORE_OFFSET (GAUDI2_DCORE1_ENGINE_ID_EDMA_0 - GAUDI2_DCORE0_ENGINE_ID_EDMA_0)
+
+/* RAZWI initiator coordinates */
+#define RAZWI_GET_AXUSER_XY(x) \
+ ((x & 0xF8001FF0) >> 4)
+
+#define RAZWI_GET_AXUSER_LOW_XY(x) \
+ ((x & 0x00001FF0) >> 4)
+
+#define RAZWI_INITIATOR_AXUER_L_X_SHIFT 0
+#define RAZWI_INITIATOR_AXUER_L_X_MASK 0x1F
+#define RAZWI_INITIATOR_AXUER_L_Y_SHIFT 5
+#define RAZWI_INITIATOR_AXUER_L_Y_MASK 0xF
+
+#define RAZWI_INITIATOR_AXUER_H_X_SHIFT 23
+#define RAZWI_INITIATOR_AXUER_H_X_MASK 0x1F
+
+#define RAZWI_INITIATOR_ID_X_Y_LOW(x, y) \
+ ((((y) & RAZWI_INITIATOR_AXUER_L_Y_MASK) << RAZWI_INITIATOR_AXUER_L_Y_SHIFT) | \
+ (((x) & RAZWI_INITIATOR_AXUER_L_X_MASK) << RAZWI_INITIATOR_AXUER_L_X_SHIFT))
+
+#define RAZWI_INITIATOR_ID_X_HIGH(x) \
+ (((x) & RAZWI_INITIATOR_AXUER_H_X_MASK) << RAZWI_INITIATOR_AXUER_H_X_SHIFT)
+
+#define RAZWI_INITIATOR_ID_X_Y(xl, yl, xh) \
+ (RAZWI_INITIATOR_ID_X_Y_LOW(xl, yl) | RAZWI_INITIATOR_ID_X_HIGH(xh))
+
+#define PSOC_RAZWI_ENG_STR_SIZE 128
+#define PSOC_RAZWI_MAX_ENG_PER_RTR 5
+
+/* HW scrambles only bits 0-25 */
+#define HW_UNSCRAMBLED_BITS_MASK GENMASK_ULL(63, 26)
+
+#define GAUDI2_GLBL_ERR_MAX_CAUSE_NUM 17
+
+struct gaudi2_razwi_info {
+ u32 axuser_xy;
+ u32 rtr_ctrl;
+ u16 eng_id;
+ char *eng_name;
+};
+
+static struct gaudi2_razwi_info common_razwi_info[] = {
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 0), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_DEC_0, "DEC0"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 4), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_DEC_1, "DEC1"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 18), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_DEC_0, "DEC2"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 14), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_DEC_1, "DEC3"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 0), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_DEC_0, "DEC4"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 4), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_DEC_1, "DEC5"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 18), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_DEC_0, "DEC6"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 14), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_DEC_1, "DEC7"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 6), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_PCIE_ENGINE_ID_DEC_0, "DEC8"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 7), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_PCIE_ENGINE_ID_DEC_0, "DEC9"},
+ {RAZWI_INITIATOR_ID_X_Y(3, 4, 2), mmDCORE0_RTR1_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_TPC_0, "TPC0"},
+ {RAZWI_INITIATOR_ID_X_Y(3, 4, 4), mmDCORE0_RTR1_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_TPC_1, "TPC1"},
+ {RAZWI_INITIATOR_ID_X_Y(4, 4, 2), mmDCORE0_RTR2_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_TPC_2, "TPC2"},
+ {RAZWI_INITIATOR_ID_X_Y(4, 4, 4), mmDCORE0_RTR2_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_TPC_3, "TPC3"},
+ {RAZWI_INITIATOR_ID_X_Y(5, 4, 2), mmDCORE0_RTR3_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_TPC_4, "TPC4"},
+ {RAZWI_INITIATOR_ID_X_Y(5, 4, 4), mmDCORE0_RTR3_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_TPC_5, "TPC5"},
+ {RAZWI_INITIATOR_ID_X_Y(16, 4, 14), mmDCORE1_RTR6_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_TPC_0, "TPC6"},
+ {RAZWI_INITIATOR_ID_X_Y(16, 4, 16), mmDCORE1_RTR6_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_TPC_1, "TPC7"},
+ {RAZWI_INITIATOR_ID_X_Y(15, 4, 14), mmDCORE1_RTR5_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_TPC_2, "TPC8"},
+ {RAZWI_INITIATOR_ID_X_Y(15, 4, 16), mmDCORE1_RTR5_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_TPC_3, "TPC9"},
+ {RAZWI_INITIATOR_ID_X_Y(14, 4, 14), mmDCORE1_RTR4_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_TPC_4, "TPC10"},
+ {RAZWI_INITIATOR_ID_X_Y(14, 4, 16), mmDCORE1_RTR4_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_TPC_5, "TPC11"},
+ {RAZWI_INITIATOR_ID_X_Y(5, 11, 2), mmDCORE2_RTR3_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_TPC_0, "TPC12"},
+ {RAZWI_INITIATOR_ID_X_Y(5, 11, 4), mmDCORE2_RTR3_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_TPC_1, "TPC13"},
+ {RAZWI_INITIATOR_ID_X_Y(4, 11, 2), mmDCORE2_RTR2_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_TPC_2, "TPC14"},
+ {RAZWI_INITIATOR_ID_X_Y(4, 11, 4), mmDCORE2_RTR2_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_TPC_3, "TPC15"},
+ {RAZWI_INITIATOR_ID_X_Y(3, 11, 2), mmDCORE2_RTR1_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_TPC_4, "TPC16"},
+ {RAZWI_INITIATOR_ID_X_Y(3, 11, 4), mmDCORE2_RTR1_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_TPC_5, "TPC17"},
+ {RAZWI_INITIATOR_ID_X_Y(14, 11, 14), mmDCORE3_RTR4_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_0, "TPC18"},
+ {RAZWI_INITIATOR_ID_X_Y(14, 11, 16), mmDCORE3_RTR4_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_1, "TPC19"},
+ {RAZWI_INITIATOR_ID_X_Y(15, 11, 14), mmDCORE3_RTR5_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_2, "TPC20"},
+ {RAZWI_INITIATOR_ID_X_Y(15, 11, 16), mmDCORE3_RTR5_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_3, "TPC21"},
+ {RAZWI_INITIATOR_ID_X_Y(16, 11, 14), mmDCORE3_RTR6_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_4, "TPC22"},
+ {RAZWI_INITIATOR_ID_X_Y(16, 11, 16), mmDCORE3_RTR6_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_5, "TPC23"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 2), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_TPC_5, "TPC24"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 8), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC0_0, "NIC0"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 10), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC0_1, "NIC1"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 12), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC1_0, "NIC2"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 14), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC1_1, "NIC3"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 15), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC2_0, "NIC4"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 2), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC2_1, "NIC5"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 4), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC3_0, "NIC6"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 6), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC3_1, "NIC7"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 8), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC4_0, "NIC8"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 12), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC4_1, "NIC9"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 14), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC5_0, "NIC10"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 16), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_NIC5_1, "NIC11"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 2), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_PDMA_0, "PDMA0"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 3), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_PDMA_1, "PDMA1"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 4), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "PMMU"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 4, 5), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "PCIE"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 16), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_ARC_FARM, "ARC_FARM"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 4, 17), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_KDMA, "KDMA"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 5, 1), mmSFT0_HBW_RTR_IF1_RTR_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_EDMA_0, "EDMA0"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 5, 1), mmSFT0_HBW_RTR_IF0_RTR_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_EDMA_1, "EDMA1"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 5, 18), mmSFT1_HBW_RTR_IF1_RTR_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_EDMA_0, "EDMA2"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 5, 18), mmSFT1_HBW_RTR_IF0_RTR_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_EDMA_1, "EDMA3"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 10, 1), mmSFT2_HBW_RTR_IF0_RTR_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_EDMA_0, "EDMA4"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 10, 1), mmSFT2_HBW_RTR_IF1_RTR_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_EDMA_1, "EDMA5"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 10, 18), mmSFT2_HBW_RTR_IF0_RTR_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_EDMA_0, "EDMA6"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 10, 18), mmSFT2_HBW_RTR_IF1_RTR_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_EDMA_1, "EDMA7"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 5, 0), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU0"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 5, 19), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU1"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 5, 0), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU2"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 5, 19), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU3"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 5, 0), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU4"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 5, 19), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU5"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 5, 0), mmDCORE0_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU6"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 5, 19), mmDCORE1_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU7"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 10, 0), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU8"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 10, 19), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU9"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 10, 0), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU10"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 10, 19), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU11"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 10, 0), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU12"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 10, 19), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU13"},
+ {RAZWI_INITIATOR_ID_X_Y(1, 10, 0), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU14"},
+ {RAZWI_INITIATOR_ID_X_Y(18, 10, 19), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_SIZE, "HMMU15"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 2), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_ROT_0, "ROT0"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 16), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_ROT_1, "ROT1"},
+ {RAZWI_INITIATOR_ID_X_Y(2, 11, 2), mmDCORE2_RTR0_CTRL_BASE,
+ GAUDI2_ENGINE_ID_PSOC, "CPU"},
+ {RAZWI_INITIATOR_ID_X_Y(17, 11, 11), mmDCORE3_RTR7_CTRL_BASE,
+ GAUDI2_ENGINE_ID_PSOC, "PSOC"}
+};
+
+static struct gaudi2_razwi_info mme_razwi_info[] = {
+ /* MME X high coordinate is N/A, hence using only low coordinates */
+ {RAZWI_INITIATOR_ID_X_Y_LOW(7, 4), mmDCORE0_RTR5_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_WAP0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(9, 4), mmDCORE0_RTR7_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_WAP1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(8, 4), mmDCORE0_RTR6_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_CTRL_WR"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(9, 4), mmDCORE0_RTR7_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_CTRL_RD"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(6, 4), mmDCORE0_RTR4_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_SBTE0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(6, 4), mmDCORE0_RTR4_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_SBTE1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(7, 4), mmDCORE0_RTR5_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_SBTE2"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(8, 4), mmDCORE0_RTR6_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_SBTE3"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(9, 4), mmDCORE0_RTR7_CTRL_BASE,
+ GAUDI2_DCORE0_ENGINE_ID_MME, "MME0_SBTE4"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(12, 4), mmDCORE1_RTR2_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_WAP0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(10, 4), mmDCORE1_RTR0_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_WAP1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(11, 4), mmDCORE1_RTR1_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_CTRL_WR"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(10, 4), mmDCORE1_RTR0_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_CTRL_RD"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(13, 4), mmDCORE1_RTR3_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_SBTE0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(13, 4), mmDCORE1_RTR3_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_SBTE1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(12, 4), mmDCORE1_RTR2_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_SBTE2"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(11, 4), mmDCORE1_RTR1_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_SBTE3"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(10, 4), mmDCORE1_RTR0_CTRL_BASE,
+ GAUDI2_DCORE1_ENGINE_ID_MME, "MME1_SBTE4"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(7, 11), mmDCORE2_RTR5_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_WAP0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(9, 11), mmDCORE2_RTR7_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_WAP1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(8, 11), mmDCORE2_RTR6_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_CTRL_WR"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(9, 11), mmDCORE2_RTR7_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_CTRL_RD"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(6, 11), mmDCORE2_RTR4_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_SBTE0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(6, 11), mmDCORE2_RTR4_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_SBTE1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(7, 11), mmDCORE2_RTR5_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_SBTE2"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(8, 11), mmDCORE2_RTR6_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_SBTE3"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(9, 11), mmDCORE2_RTR7_CTRL_BASE,
+ GAUDI2_DCORE2_ENGINE_ID_MME, "MME2_SBTE4"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(12, 11), mmDCORE3_RTR2_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_WAP0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(10, 11), mmDCORE3_RTR0_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_WAP1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(11, 11), mmDCORE3_RTR1_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_CTRL_WR"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(10, 11), mmDCORE3_RTR0_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_CTRL_RD"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(13, 11), mmDCORE3_RTR3_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_SBTE0"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(13, 11), mmDCORE3_RTR3_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_SBTE1"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(12, 11), mmDCORE3_RTR2_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_SBTE2"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(11, 11), mmDCORE3_RTR1_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_SBTE3"},
+ {RAZWI_INITIATOR_ID_X_Y_LOW(10, 11), mmDCORE3_RTR0_CTRL_BASE,
+ GAUDI2_DCORE3_ENGINE_ID_MME, "MME3_SBTE4"}
+};
+
+enum hl_pmmu_fatal_cause {
+ LATENCY_RD_OUT_FIFO_OVERRUN,
+ LATENCY_WR_OUT_FIFO_OVERRUN,
+};
+
+enum hl_pcie_drain_ind_cause {
+ LBW_AXI_DRAIN_IND,
+ HBW_AXI_DRAIN_IND
+};
+
+static const u32 cluster_hmmu_hif_enabled_mask[GAUDI2_HBM_NUM] = {
+ [HBM_ID0] = 0xFFFC,
+ [HBM_ID1] = 0xFFCF,
+ [HBM_ID2] = 0xF7F7,
+ [HBM_ID3] = 0x7F7F,
+ [HBM_ID4] = 0xFCFF,
+ [HBM_ID5] = 0xCFFF,
+};
+
+static const u8 xbar_edge_to_hbm_cluster[EDMA_ID_SIZE] = {
+ [0] = HBM_ID0,
+ [1] = HBM_ID1,
+ [2] = HBM_ID4,
+ [3] = HBM_ID5,
+};
+
+static const u8 edma_to_hbm_cluster[EDMA_ID_SIZE] = {
+ [EDMA_ID_DCORE0_INSTANCE0] = HBM_ID0,
+ [EDMA_ID_DCORE0_INSTANCE1] = HBM_ID2,
+ [EDMA_ID_DCORE1_INSTANCE0] = HBM_ID1,
+ [EDMA_ID_DCORE1_INSTANCE1] = HBM_ID3,
+ [EDMA_ID_DCORE2_INSTANCE0] = HBM_ID2,
+ [EDMA_ID_DCORE2_INSTANCE1] = HBM_ID4,
+ [EDMA_ID_DCORE3_INSTANCE0] = HBM_ID3,
+ [EDMA_ID_DCORE3_INSTANCE1] = HBM_ID5,
+};
+
+static const int gaudi2_qman_async_event_id[] = {
+ [GAUDI2_QUEUE_ID_PDMA_0_0] = GAUDI2_EVENT_PDMA0_QM,
+ [GAUDI2_QUEUE_ID_PDMA_0_1] = GAUDI2_EVENT_PDMA0_QM,
+ [GAUDI2_QUEUE_ID_PDMA_0_2] = GAUDI2_EVENT_PDMA0_QM,
+ [GAUDI2_QUEUE_ID_PDMA_0_3] = GAUDI2_EVENT_PDMA0_QM,
+ [GAUDI2_QUEUE_ID_PDMA_1_0] = GAUDI2_EVENT_PDMA1_QM,
+ [GAUDI2_QUEUE_ID_PDMA_1_1] = GAUDI2_EVENT_PDMA1_QM,
+ [GAUDI2_QUEUE_ID_PDMA_1_2] = GAUDI2_EVENT_PDMA1_QM,
+ [GAUDI2_QUEUE_ID_PDMA_1_3] = GAUDI2_EVENT_PDMA1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0] = GAUDI2_EVENT_HDMA0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_1] = GAUDI2_EVENT_HDMA0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_2] = GAUDI2_EVENT_HDMA0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_3] = GAUDI2_EVENT_HDMA0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0] = GAUDI2_EVENT_HDMA1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_1] = GAUDI2_EVENT_HDMA1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_2] = GAUDI2_EVENT_HDMA1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3] = GAUDI2_EVENT_HDMA1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_0] = GAUDI2_EVENT_MME0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_1] = GAUDI2_EVENT_MME0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_2] = GAUDI2_EVENT_MME0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_3] = GAUDI2_EVENT_MME0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_0] = GAUDI2_EVENT_TPC0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_1] = GAUDI2_EVENT_TPC0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_2] = GAUDI2_EVENT_TPC0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_3] = GAUDI2_EVENT_TPC0_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_0] = GAUDI2_EVENT_TPC1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_1] = GAUDI2_EVENT_TPC1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_2] = GAUDI2_EVENT_TPC1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_3] = GAUDI2_EVENT_TPC1_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_0] = GAUDI2_EVENT_TPC2_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_1] = GAUDI2_EVENT_TPC2_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_2] = GAUDI2_EVENT_TPC2_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_3] = GAUDI2_EVENT_TPC2_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_0] = GAUDI2_EVENT_TPC3_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_1] = GAUDI2_EVENT_TPC3_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_2] = GAUDI2_EVENT_TPC3_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_3] = GAUDI2_EVENT_TPC3_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_0] = GAUDI2_EVENT_TPC4_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_1] = GAUDI2_EVENT_TPC4_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_2] = GAUDI2_EVENT_TPC4_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_3] = GAUDI2_EVENT_TPC4_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_0] = GAUDI2_EVENT_TPC5_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_1] = GAUDI2_EVENT_TPC5_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_2] = GAUDI2_EVENT_TPC5_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_3] = GAUDI2_EVENT_TPC5_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_0] = GAUDI2_EVENT_TPC24_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_1] = GAUDI2_EVENT_TPC24_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_2] = GAUDI2_EVENT_TPC24_QM,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_3] = GAUDI2_EVENT_TPC24_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0] = GAUDI2_EVENT_HDMA2_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_1] = GAUDI2_EVENT_HDMA2_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_2] = GAUDI2_EVENT_HDMA2_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_3] = GAUDI2_EVENT_HDMA2_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0] = GAUDI2_EVENT_HDMA3_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_1] = GAUDI2_EVENT_HDMA3_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_2] = GAUDI2_EVENT_HDMA3_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3] = GAUDI2_EVENT_HDMA3_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_0] = GAUDI2_EVENT_MME1_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_1] = GAUDI2_EVENT_MME1_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_2] = GAUDI2_EVENT_MME1_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_3] = GAUDI2_EVENT_MME1_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_0] = GAUDI2_EVENT_TPC6_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_1] = GAUDI2_EVENT_TPC6_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_2] = GAUDI2_EVENT_TPC6_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_3] = GAUDI2_EVENT_TPC6_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_0] = GAUDI2_EVENT_TPC7_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_1] = GAUDI2_EVENT_TPC7_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_2] = GAUDI2_EVENT_TPC7_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_3] = GAUDI2_EVENT_TPC7_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_0] = GAUDI2_EVENT_TPC8_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_1] = GAUDI2_EVENT_TPC8_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_2] = GAUDI2_EVENT_TPC8_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_3] = GAUDI2_EVENT_TPC8_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_0] = GAUDI2_EVENT_TPC9_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_1] = GAUDI2_EVENT_TPC9_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_2] = GAUDI2_EVENT_TPC9_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_3] = GAUDI2_EVENT_TPC9_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_0] = GAUDI2_EVENT_TPC10_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_1] = GAUDI2_EVENT_TPC10_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_2] = GAUDI2_EVENT_TPC10_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_3] = GAUDI2_EVENT_TPC10_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_0] = GAUDI2_EVENT_TPC11_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_1] = GAUDI2_EVENT_TPC11_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_2] = GAUDI2_EVENT_TPC11_QM,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_3] = GAUDI2_EVENT_TPC11_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0] = GAUDI2_EVENT_HDMA4_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_1] = GAUDI2_EVENT_HDMA4_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_2] = GAUDI2_EVENT_HDMA4_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_3] = GAUDI2_EVENT_HDMA4_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0] = GAUDI2_EVENT_HDMA5_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_1] = GAUDI2_EVENT_HDMA5_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_2] = GAUDI2_EVENT_HDMA5_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3] = GAUDI2_EVENT_HDMA5_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_0] = GAUDI2_EVENT_MME2_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_1] = GAUDI2_EVENT_MME2_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_2] = GAUDI2_EVENT_MME2_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_3] = GAUDI2_EVENT_MME2_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_0] = GAUDI2_EVENT_TPC12_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_1] = GAUDI2_EVENT_TPC12_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_2] = GAUDI2_EVENT_TPC12_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_3] = GAUDI2_EVENT_TPC12_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_0] = GAUDI2_EVENT_TPC13_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_1] = GAUDI2_EVENT_TPC13_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_2] = GAUDI2_EVENT_TPC13_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_3] = GAUDI2_EVENT_TPC13_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_0] = GAUDI2_EVENT_TPC14_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_1] = GAUDI2_EVENT_TPC14_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_2] = GAUDI2_EVENT_TPC14_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_3] = GAUDI2_EVENT_TPC14_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_0] = GAUDI2_EVENT_TPC15_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_1] = GAUDI2_EVENT_TPC15_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_2] = GAUDI2_EVENT_TPC15_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_3] = GAUDI2_EVENT_TPC15_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_0] = GAUDI2_EVENT_TPC16_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_1] = GAUDI2_EVENT_TPC16_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_2] = GAUDI2_EVENT_TPC16_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_3] = GAUDI2_EVENT_TPC16_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_0] = GAUDI2_EVENT_TPC17_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_1] = GAUDI2_EVENT_TPC17_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_2] = GAUDI2_EVENT_TPC17_QM,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_3] = GAUDI2_EVENT_TPC17_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0] = GAUDI2_EVENT_HDMA6_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_1] = GAUDI2_EVENT_HDMA6_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_2] = GAUDI2_EVENT_HDMA6_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_3] = GAUDI2_EVENT_HDMA6_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0] = GAUDI2_EVENT_HDMA7_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_1] = GAUDI2_EVENT_HDMA7_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_2] = GAUDI2_EVENT_HDMA7_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3] = GAUDI2_EVENT_HDMA7_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_0] = GAUDI2_EVENT_MME3_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_1] = GAUDI2_EVENT_MME3_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_2] = GAUDI2_EVENT_MME3_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_3] = GAUDI2_EVENT_MME3_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_0] = GAUDI2_EVENT_TPC18_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_1] = GAUDI2_EVENT_TPC18_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_2] = GAUDI2_EVENT_TPC18_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_3] = GAUDI2_EVENT_TPC18_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_0] = GAUDI2_EVENT_TPC19_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_1] = GAUDI2_EVENT_TPC19_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_2] = GAUDI2_EVENT_TPC19_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_3] = GAUDI2_EVENT_TPC19_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_0] = GAUDI2_EVENT_TPC20_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_1] = GAUDI2_EVENT_TPC20_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_2] = GAUDI2_EVENT_TPC20_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_3] = GAUDI2_EVENT_TPC20_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_0] = GAUDI2_EVENT_TPC21_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_1] = GAUDI2_EVENT_TPC21_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_2] = GAUDI2_EVENT_TPC21_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_3] = GAUDI2_EVENT_TPC21_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_0] = GAUDI2_EVENT_TPC22_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_1] = GAUDI2_EVENT_TPC22_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_2] = GAUDI2_EVENT_TPC22_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_3] = GAUDI2_EVENT_TPC22_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_0] = GAUDI2_EVENT_TPC23_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_1] = GAUDI2_EVENT_TPC23_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_2] = GAUDI2_EVENT_TPC23_QM,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_3] = GAUDI2_EVENT_TPC23_QM,
+ [GAUDI2_QUEUE_ID_NIC_0_0] = GAUDI2_EVENT_NIC0_QM0,
+ [GAUDI2_QUEUE_ID_NIC_0_1] = GAUDI2_EVENT_NIC0_QM0,
+ [GAUDI2_QUEUE_ID_NIC_0_2] = GAUDI2_EVENT_NIC0_QM0,
+ [GAUDI2_QUEUE_ID_NIC_0_3] = GAUDI2_EVENT_NIC0_QM0,
+ [GAUDI2_QUEUE_ID_NIC_1_0] = GAUDI2_EVENT_NIC0_QM1,
+ [GAUDI2_QUEUE_ID_NIC_1_1] = GAUDI2_EVENT_NIC0_QM1,
+ [GAUDI2_QUEUE_ID_NIC_1_2] = GAUDI2_EVENT_NIC0_QM1,
+ [GAUDI2_QUEUE_ID_NIC_1_3] = GAUDI2_EVENT_NIC0_QM1,
+ [GAUDI2_QUEUE_ID_NIC_2_0] = GAUDI2_EVENT_NIC1_QM0,
+ [GAUDI2_QUEUE_ID_NIC_2_1] = GAUDI2_EVENT_NIC1_QM0,
+ [GAUDI2_QUEUE_ID_NIC_2_2] = GAUDI2_EVENT_NIC1_QM0,
+ [GAUDI2_QUEUE_ID_NIC_2_3] = GAUDI2_EVENT_NIC1_QM0,
+ [GAUDI2_QUEUE_ID_NIC_3_0] = GAUDI2_EVENT_NIC1_QM1,
+ [GAUDI2_QUEUE_ID_NIC_3_1] = GAUDI2_EVENT_NIC1_QM1,
+ [GAUDI2_QUEUE_ID_NIC_3_2] = GAUDI2_EVENT_NIC1_QM1,
+ [GAUDI2_QUEUE_ID_NIC_3_3] = GAUDI2_EVENT_NIC1_QM1,
+ [GAUDI2_QUEUE_ID_NIC_4_0] = GAUDI2_EVENT_NIC2_QM0,
+ [GAUDI2_QUEUE_ID_NIC_4_1] = GAUDI2_EVENT_NIC2_QM0,
+ [GAUDI2_QUEUE_ID_NIC_4_2] = GAUDI2_EVENT_NIC2_QM0,
+ [GAUDI2_QUEUE_ID_NIC_4_3] = GAUDI2_EVENT_NIC2_QM0,
+ [GAUDI2_QUEUE_ID_NIC_5_0] = GAUDI2_EVENT_NIC2_QM1,
+ [GAUDI2_QUEUE_ID_NIC_5_1] = GAUDI2_EVENT_NIC2_QM1,
+ [GAUDI2_QUEUE_ID_NIC_5_2] = GAUDI2_EVENT_NIC2_QM1,
+ [GAUDI2_QUEUE_ID_NIC_5_3] = GAUDI2_EVENT_NIC2_QM1,
+ [GAUDI2_QUEUE_ID_NIC_6_0] = GAUDI2_EVENT_NIC3_QM0,
+ [GAUDI2_QUEUE_ID_NIC_6_1] = GAUDI2_EVENT_NIC3_QM0,
+ [GAUDI2_QUEUE_ID_NIC_6_2] = GAUDI2_EVENT_NIC3_QM0,
+ [GAUDI2_QUEUE_ID_NIC_6_3] = GAUDI2_EVENT_NIC3_QM0,
+ [GAUDI2_QUEUE_ID_NIC_7_0] = GAUDI2_EVENT_NIC3_QM1,
+ [GAUDI2_QUEUE_ID_NIC_7_1] = GAUDI2_EVENT_NIC3_QM1,
+ [GAUDI2_QUEUE_ID_NIC_7_2] = GAUDI2_EVENT_NIC3_QM1,
+ [GAUDI2_QUEUE_ID_NIC_7_3] = GAUDI2_EVENT_NIC3_QM1,
+ [GAUDI2_QUEUE_ID_NIC_8_0] = GAUDI2_EVENT_NIC4_QM0,
+ [GAUDI2_QUEUE_ID_NIC_8_1] = GAUDI2_EVENT_NIC4_QM0,
+ [GAUDI2_QUEUE_ID_NIC_8_2] = GAUDI2_EVENT_NIC4_QM0,
+ [GAUDI2_QUEUE_ID_NIC_8_3] = GAUDI2_EVENT_NIC4_QM0,
+ [GAUDI2_QUEUE_ID_NIC_9_0] = GAUDI2_EVENT_NIC4_QM1,
+ [GAUDI2_QUEUE_ID_NIC_9_1] = GAUDI2_EVENT_NIC4_QM1,
+ [GAUDI2_QUEUE_ID_NIC_9_2] = GAUDI2_EVENT_NIC4_QM1,
+ [GAUDI2_QUEUE_ID_NIC_9_3] = GAUDI2_EVENT_NIC4_QM1,
+ [GAUDI2_QUEUE_ID_NIC_10_0] = GAUDI2_EVENT_NIC5_QM0,
+ [GAUDI2_QUEUE_ID_NIC_10_1] = GAUDI2_EVENT_NIC5_QM0,
+ [GAUDI2_QUEUE_ID_NIC_10_2] = GAUDI2_EVENT_NIC5_QM0,
+ [GAUDI2_QUEUE_ID_NIC_10_3] = GAUDI2_EVENT_NIC5_QM0,
+ [GAUDI2_QUEUE_ID_NIC_11_0] = GAUDI2_EVENT_NIC5_QM1,
+ [GAUDI2_QUEUE_ID_NIC_11_1] = GAUDI2_EVENT_NIC5_QM1,
+ [GAUDI2_QUEUE_ID_NIC_11_2] = GAUDI2_EVENT_NIC5_QM1,
+ [GAUDI2_QUEUE_ID_NIC_11_3] = GAUDI2_EVENT_NIC5_QM1,
+ [GAUDI2_QUEUE_ID_NIC_12_0] = GAUDI2_EVENT_NIC6_QM0,
+ [GAUDI2_QUEUE_ID_NIC_12_1] = GAUDI2_EVENT_NIC6_QM0,
+ [GAUDI2_QUEUE_ID_NIC_12_2] = GAUDI2_EVENT_NIC6_QM0,
+ [GAUDI2_QUEUE_ID_NIC_12_3] = GAUDI2_EVENT_NIC6_QM0,
+ [GAUDI2_QUEUE_ID_NIC_13_0] = GAUDI2_EVENT_NIC6_QM1,
+ [GAUDI2_QUEUE_ID_NIC_13_1] = GAUDI2_EVENT_NIC6_QM1,
+ [GAUDI2_QUEUE_ID_NIC_13_2] = GAUDI2_EVENT_NIC6_QM1,
+ [GAUDI2_QUEUE_ID_NIC_13_3] = GAUDI2_EVENT_NIC6_QM1,
+ [GAUDI2_QUEUE_ID_NIC_14_0] = GAUDI2_EVENT_NIC7_QM0,
+ [GAUDI2_QUEUE_ID_NIC_14_1] = GAUDI2_EVENT_NIC7_QM0,
+ [GAUDI2_QUEUE_ID_NIC_14_2] = GAUDI2_EVENT_NIC7_QM0,
+ [GAUDI2_QUEUE_ID_NIC_14_3] = GAUDI2_EVENT_NIC7_QM0,
+ [GAUDI2_QUEUE_ID_NIC_15_0] = GAUDI2_EVENT_NIC7_QM1,
+ [GAUDI2_QUEUE_ID_NIC_15_1] = GAUDI2_EVENT_NIC7_QM1,
+ [GAUDI2_QUEUE_ID_NIC_15_2] = GAUDI2_EVENT_NIC7_QM1,
+ [GAUDI2_QUEUE_ID_NIC_15_3] = GAUDI2_EVENT_NIC7_QM1,
+ [GAUDI2_QUEUE_ID_NIC_16_0] = GAUDI2_EVENT_NIC8_QM0,
+ [GAUDI2_QUEUE_ID_NIC_16_1] = GAUDI2_EVENT_NIC8_QM0,
+ [GAUDI2_QUEUE_ID_NIC_16_2] = GAUDI2_EVENT_NIC8_QM0,
+ [GAUDI2_QUEUE_ID_NIC_16_3] = GAUDI2_EVENT_NIC8_QM0,
+ [GAUDI2_QUEUE_ID_NIC_17_0] = GAUDI2_EVENT_NIC8_QM1,
+ [GAUDI2_QUEUE_ID_NIC_17_1] = GAUDI2_EVENT_NIC8_QM1,
+ [GAUDI2_QUEUE_ID_NIC_17_2] = GAUDI2_EVENT_NIC8_QM1,
+ [GAUDI2_QUEUE_ID_NIC_17_3] = GAUDI2_EVENT_NIC8_QM1,
+ [GAUDI2_QUEUE_ID_NIC_18_0] = GAUDI2_EVENT_NIC9_QM0,
+ [GAUDI2_QUEUE_ID_NIC_18_1] = GAUDI2_EVENT_NIC9_QM0,
+ [GAUDI2_QUEUE_ID_NIC_18_2] = GAUDI2_EVENT_NIC9_QM0,
+ [GAUDI2_QUEUE_ID_NIC_18_3] = GAUDI2_EVENT_NIC9_QM0,
+ [GAUDI2_QUEUE_ID_NIC_19_0] = GAUDI2_EVENT_NIC9_QM1,
+ [GAUDI2_QUEUE_ID_NIC_19_1] = GAUDI2_EVENT_NIC9_QM1,
+ [GAUDI2_QUEUE_ID_NIC_19_2] = GAUDI2_EVENT_NIC9_QM1,
+ [GAUDI2_QUEUE_ID_NIC_19_3] = GAUDI2_EVENT_NIC9_QM1,
+ [GAUDI2_QUEUE_ID_NIC_20_0] = GAUDI2_EVENT_NIC10_QM0,
+ [GAUDI2_QUEUE_ID_NIC_20_1] = GAUDI2_EVENT_NIC10_QM0,
+ [GAUDI2_QUEUE_ID_NIC_20_2] = GAUDI2_EVENT_NIC10_QM0,
+ [GAUDI2_QUEUE_ID_NIC_20_3] = GAUDI2_EVENT_NIC10_QM0,
+ [GAUDI2_QUEUE_ID_NIC_21_0] = GAUDI2_EVENT_NIC10_QM1,
+ [GAUDI2_QUEUE_ID_NIC_21_1] = GAUDI2_EVENT_NIC10_QM1,
+ [GAUDI2_QUEUE_ID_NIC_21_2] = GAUDI2_EVENT_NIC10_QM1,
+ [GAUDI2_QUEUE_ID_NIC_21_3] = GAUDI2_EVENT_NIC10_QM1,
+ [GAUDI2_QUEUE_ID_NIC_22_0] = GAUDI2_EVENT_NIC11_QM0,
+ [GAUDI2_QUEUE_ID_NIC_22_1] = GAUDI2_EVENT_NIC11_QM0,
+ [GAUDI2_QUEUE_ID_NIC_22_2] = GAUDI2_EVENT_NIC11_QM0,
+ [GAUDI2_QUEUE_ID_NIC_22_3] = GAUDI2_EVENT_NIC11_QM0,
+ [GAUDI2_QUEUE_ID_NIC_23_0] = GAUDI2_EVENT_NIC11_QM1,
+ [GAUDI2_QUEUE_ID_NIC_23_1] = GAUDI2_EVENT_NIC11_QM1,
+ [GAUDI2_QUEUE_ID_NIC_23_2] = GAUDI2_EVENT_NIC11_QM1,
+ [GAUDI2_QUEUE_ID_NIC_23_3] = GAUDI2_EVENT_NIC11_QM1,
+ [GAUDI2_QUEUE_ID_ROT_0_0] = GAUDI2_EVENT_ROTATOR0_ROT0_QM,
+ [GAUDI2_QUEUE_ID_ROT_0_1] = GAUDI2_EVENT_ROTATOR0_ROT0_QM,
+ [GAUDI2_QUEUE_ID_ROT_0_2] = GAUDI2_EVENT_ROTATOR0_ROT0_QM,
+ [GAUDI2_QUEUE_ID_ROT_0_3] = GAUDI2_EVENT_ROTATOR0_ROT0_QM,
+ [GAUDI2_QUEUE_ID_ROT_1_0] = GAUDI2_EVENT_ROTATOR1_ROT1_QM,
+ [GAUDI2_QUEUE_ID_ROT_1_1] = GAUDI2_EVENT_ROTATOR1_ROT1_QM,
+ [GAUDI2_QUEUE_ID_ROT_1_2] = GAUDI2_EVENT_ROTATOR1_ROT1_QM,
+ [GAUDI2_QUEUE_ID_ROT_1_3] = GAUDI2_EVENT_ROTATOR1_ROT1_QM
+};
+
+static const int gaudi2_dma_core_async_event_id[] = {
+ [DMA_CORE_ID_EDMA0] = GAUDI2_EVENT_HDMA0_CORE,
+ [DMA_CORE_ID_EDMA1] = GAUDI2_EVENT_HDMA1_CORE,
+ [DMA_CORE_ID_EDMA2] = GAUDI2_EVENT_HDMA2_CORE,
+ [DMA_CORE_ID_EDMA3] = GAUDI2_EVENT_HDMA3_CORE,
+ [DMA_CORE_ID_EDMA4] = GAUDI2_EVENT_HDMA4_CORE,
+ [DMA_CORE_ID_EDMA5] = GAUDI2_EVENT_HDMA5_CORE,
+ [DMA_CORE_ID_EDMA6] = GAUDI2_EVENT_HDMA6_CORE,
+ [DMA_CORE_ID_EDMA7] = GAUDI2_EVENT_HDMA7_CORE,
+ [DMA_CORE_ID_PDMA0] = GAUDI2_EVENT_PDMA0_CORE,
+ [DMA_CORE_ID_PDMA1] = GAUDI2_EVENT_PDMA1_CORE,
+ [DMA_CORE_ID_KDMA] = GAUDI2_EVENT_KDMA0_CORE,
+};
+
+const char *gaudi2_engine_id_str[] = {
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_EDMA_0),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_EDMA_1),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_MME),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_0),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_1),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_2),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_3),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_4),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_5),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_DEC_0),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_DEC_1),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_EDMA_0),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_EDMA_1),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_MME),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_TPC_0),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_TPC_1),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_TPC_2),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_TPC_3),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_TPC_4),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_TPC_5),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_DEC_0),
+ __stringify(GAUDI2_DCORE1_ENGINE_ID_DEC_1),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_EDMA_0),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_EDMA_1),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_MME),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_TPC_0),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_TPC_1),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_TPC_2),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_TPC_3),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_TPC_4),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_TPC_5),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_DEC_0),
+ __stringify(GAUDI2_DCORE2_ENGINE_ID_DEC_1),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_EDMA_0),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_EDMA_1),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_MME),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_TPC_0),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_TPC_1),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_TPC_2),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_TPC_3),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_TPC_4),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_TPC_5),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_DEC_0),
+ __stringify(GAUDI2_DCORE3_ENGINE_ID_DEC_1),
+ __stringify(GAUDI2_DCORE0_ENGINE_ID_TPC_6),
+ __stringify(GAUDI2_ENGINE_ID_PDMA_0),
+ __stringify(GAUDI2_ENGINE_ID_PDMA_1),
+ __stringify(GAUDI2_ENGINE_ID_ROT_0),
+ __stringify(GAUDI2_ENGINE_ID_ROT_1),
+ __stringify(GAUDI2_PCIE_ENGINE_ID_DEC_0),
+ __stringify(GAUDI2_PCIE_ENGINE_ID_DEC_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC0_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC0_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC1_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC1_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC2_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC2_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC3_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC3_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC4_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC4_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC5_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC5_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC6_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC6_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC7_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC7_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC8_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC8_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC9_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC9_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC10_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC10_1),
+ __stringify(GAUDI2_ENGINE_ID_NIC11_0),
+ __stringify(GAUDI2_ENGINE_ID_NIC11_1),
+ __stringify(GAUDI2_ENGINE_ID_PCIE),
+ __stringify(GAUDI2_ENGINE_ID_PSOC),
+ __stringify(GAUDI2_ENGINE_ID_ARC_FARM),
+ __stringify(GAUDI2_ENGINE_ID_KDMA),
+ __stringify(GAUDI2_ENGINE_ID_SIZE),
+};
+
+const char *gaudi2_queue_id_str[] = {
+ __stringify(GAUDI2_QUEUE_ID_PDMA_0_0),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_0_1),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_0_2),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_0_3),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_1_0),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_1_1),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_1_2),
+ __stringify(GAUDI2_QUEUE_ID_PDMA_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_MME_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_MME_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_MME_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_MME_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_2_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_2_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_2_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_2_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_3_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_3_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_3_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_3_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_4_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_4_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_4_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_4_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_5_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_5_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_5_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_5_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_6_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_6_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_6_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE0_TPC_6_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_MME_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_MME_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_MME_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_MME_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_2_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_2_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_2_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_2_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_3_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_3_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_3_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_3_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_4_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_4_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_4_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_4_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_5_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_5_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_5_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE1_TPC_5_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_MME_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_MME_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_MME_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_MME_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_2_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_2_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_2_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_2_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_3_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_3_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_3_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_3_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_4_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_4_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_4_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_4_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_5_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_5_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_5_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE2_TPC_5_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_MME_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_MME_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_MME_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_MME_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_0_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_0_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_0_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_0_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_1_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_1_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_1_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_1_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_2_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_2_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_2_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_2_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_3_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_3_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_3_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_3_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_4_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_4_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_4_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_4_3),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_5_0),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_5_1),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_5_2),
+ __stringify(GAUDI2_QUEUE_ID_DCORE3_TPC_5_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_0_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_0_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_0_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_0_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_1_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_1_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_1_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_1_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_2_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_2_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_2_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_2_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_3_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_3_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_3_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_3_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_4_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_4_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_4_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_4_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_5_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_5_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_5_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_5_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_6_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_6_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_6_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_6_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_7_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_7_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_7_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_7_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_8_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_8_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_8_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_8_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_9_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_9_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_9_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_9_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_10_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_10_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_10_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_10_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_11_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_11_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_11_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_11_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_12_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_12_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_12_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_12_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_13_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_13_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_13_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_13_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_14_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_14_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_14_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_14_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_15_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_15_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_15_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_15_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_16_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_16_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_16_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_16_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_17_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_17_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_17_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_17_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_18_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_18_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_18_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_18_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_19_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_19_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_19_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_19_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_20_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_20_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_20_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_20_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_21_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_21_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_21_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_21_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_22_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_22_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_22_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_22_3),
+ __stringify(GAUDI2_QUEUE_ID_NIC_23_0),
+ __stringify(GAUDI2_QUEUE_ID_NIC_23_1),
+ __stringify(GAUDI2_QUEUE_ID_NIC_23_2),
+ __stringify(GAUDI2_QUEUE_ID_NIC_23_3),
+ __stringify(GAUDI2_QUEUE_ID_ROT_0_0),
+ __stringify(GAUDI2_QUEUE_ID_ROT_0_1),
+ __stringify(GAUDI2_QUEUE_ID_ROT_0_2),
+ __stringify(GAUDI2_QUEUE_ID_ROT_0_3),
+ __stringify(GAUDI2_QUEUE_ID_ROT_1_0),
+ __stringify(GAUDI2_QUEUE_ID_ROT_1_1),
+ __stringify(GAUDI2_QUEUE_ID_ROT_1_2),
+ __stringify(GAUDI2_QUEUE_ID_ROT_1_3),
+ __stringify(GAUDI2_QUEUE_ID_CPU_PQ),
+ __stringify(GAUDI2_QUEUE_ID_SIZE),
+};
+
+static const char * const gaudi2_qm_sei_error_cause[GAUDI2_NUM_OF_QM_SEI_ERR_CAUSE] = {
+ "qman sei intr",
+ "arc sei intr"
+};
+
+static const char * const gaudi2_cpu_sei_error_cause[GAUDI2_NUM_OF_CPU_SEI_ERR_CAUSE] = {
+ "AXI_TERMINATOR WR",
+ "AXI_TERMINATOR RD",
+ "AXI SPLIT SEI Status"
+};
+
+static const char * const gaudi2_arc_sei_error_cause[GAUDI2_NUM_OF_ARC_SEI_ERR_CAUSE] = {
+ "cbu_bresp_sei_intr_cause",
+ "cbu_rresp_sei_intr_cause",
+ "lbu_bresp_sei_intr_cause",
+ "lbu_rresp_sei_intr_cause",
+ "cbu_axi_split_intr_cause",
+ "lbu_axi_split_intr_cause",
+ "arc_ip_excptn_sei_intr_cause",
+ "dmi_bresp_sei_intr_cause",
+ "aux2apb_err_sei_intr_cause",
+ "cfg_lbw_wr_terminated_intr_cause",
+ "cfg_lbw_rd_terminated_intr_cause",
+ "cfg_dccm_wr_terminated_intr_cause",
+ "cfg_dccm_rd_terminated_intr_cause",
+ "cfg_hbw_rd_terminated_intr_cause"
+};
+
+static const char * const gaudi2_dec_error_cause[GAUDI2_NUM_OF_DEC_ERR_CAUSE] = {
+ "msix_vcd_hbw_sei",
+ "msix_l2c_hbw_sei",
+ "msix_nrm_hbw_sei",
+ "msix_abnrm_hbw_sei",
+ "msix_vcd_lbw_sei",
+ "msix_l2c_lbw_sei",
+ "msix_nrm_lbw_sei",
+ "msix_abnrm_lbw_sei",
+ "apb_vcd_lbw_sei",
+ "apb_l2c_lbw_sei",
+ "apb_nrm_lbw_sei",
+ "apb_abnrm_lbw_sei",
+ "dec_sei",
+ "dec_apb_sei",
+ "trc_apb_sei",
+ "lbw_mstr_if_sei",
+ "axi_split_bresp_err_sei",
+ "hbw_axi_wr_viol_sei",
+ "hbw_axi_rd_viol_sei",
+ "lbw_axi_wr_viol_sei",
+ "lbw_axi_rd_viol_sei",
+ "vcd_spi",
+ "l2c_spi",
+ "nrm_spi",
+ "abnrm_spi",
+};
+
+static const char * const gaudi2_qman_error_cause[GAUDI2_NUM_OF_QM_ERR_CAUSE] = {
+ "PQ AXI HBW error",
+ "CQ AXI HBW error",
+ "CP AXI HBW error",
+ "CP error due to undefined OPCODE",
+ "CP encountered STOP OPCODE",
+ "CP AXI LBW error",
+ "CP WRREG32 or WRBULK returned error",
+ "N/A",
+ "FENCE 0 inc over max value and clipped",
+ "FENCE 1 inc over max value and clipped",
+ "FENCE 2 inc over max value and clipped",
+ "FENCE 3 inc over max value and clipped",
+ "FENCE 0 dec under min value and clipped",
+ "FENCE 1 dec under min value and clipped",
+ "FENCE 2 dec under min value and clipped",
+ "FENCE 3 dec under min value and clipped",
+ "CPDMA Up overflow",
+ "PQC L2H error"
+};
+
+static const char * const gaudi2_lower_qman_error_cause[GAUDI2_NUM_OF_LOWER_QM_ERR_CAUSE] = {
+ "RSVD0",
+ "CQ AXI HBW error",
+ "CP AXI HBW error",
+ "CP error due to undefined OPCODE",
+ "CP encountered STOP OPCODE",
+ "CP AXI LBW error",
+ "CP WRREG32 or WRBULK returned error",
+ "N/A",
+ "FENCE 0 inc over max value and clipped",
+ "FENCE 1 inc over max value and clipped",
+ "FENCE 2 inc over max value and clipped",
+ "FENCE 3 inc over max value and clipped",
+ "FENCE 0 dec under min value and clipped",
+ "FENCE 1 dec under min value and clipped",
+ "FENCE 2 dec under min value and clipped",
+ "FENCE 3 dec under min value and clipped",
+ "CPDMA Up overflow",
+ "RSVD17",
+ "CQ_WR_IFIFO_CI_ERR",
+ "CQ_WR_CTL_CI_ERR",
+ "ARC_CQF_RD_ERR",
+ "ARC_CQ_WR_IFIFO_CI_ERR",
+ "ARC_CQ_WR_CTL_CI_ERR",
+ "ARC_AXI_ERR",
+ "CP_SWITCH_WDT_ERR"
+};
+
+static const char * const gaudi2_qman_arb_error_cause[GAUDI2_NUM_OF_QM_ARB_ERR_CAUSE] = {
+ "Choice push while full error",
+ "Choice Q watchdog error",
+ "MSG AXI LBW returned with error"
+};
+
+static const char * const guadi2_rot_error_cause[GAUDI2_NUM_OF_ROT_ERR_CAUSE] = {
+ "qm_axi_err",
+ "qm_trace_fence_events",
+ "qm_sw_err",
+ "qm_cp_sw_stop",
+ "lbw_mstr_rresp_err",
+ "lbw_mstr_bresp_err",
+ "lbw_msg_slverr",
+ "hbw_msg_slverr",
+ "wbc_slverr",
+ "hbw_mstr_rresp_err",
+ "hbw_mstr_bresp_err",
+ "sb_resp_intr",
+ "mrsb_resp_intr",
+ "core_dw_status_0",
+ "core_dw_status_1",
+ "core_dw_status_2",
+ "core_dw_status_3",
+ "core_dw_status_4",
+ "core_dw_status_5",
+ "core_dw_status_6",
+ "core_dw_status_7",
+ "async_arc2cpu_sei_intr",
+};
+
+static const char * const gaudi2_tpc_interrupts_cause[GAUDI2_NUM_OF_TPC_INTR_CAUSE] = {
+ "tpc_address_exceed_slm",
+ "tpc_div_by_0",
+ "tpc_spu_mac_overflow",
+ "tpc_spu_addsub_overflow",
+ "tpc_spu_abs_overflow",
+ "tpc_spu_fma_fp_dst_nan",
+ "tpc_spu_fma_fp_dst_inf",
+ "tpc_spu_convert_fp_dst_nan",
+ "tpc_spu_convert_fp_dst_inf",
+ "tpc_spu_fp_dst_denorm",
+ "tpc_vpu_mac_overflow",
+ "tpc_vpu_addsub_overflow",
+ "tpc_vpu_abs_overflow",
+ "tpc_vpu_convert_fp_dst_nan",
+ "tpc_vpu_convert_fp_dst_inf",
+ "tpc_vpu_fma_fp_dst_nan",
+ "tpc_vpu_fma_fp_dst_inf",
+ "tpc_vpu_fp_dst_denorm",
+ "tpc_assertions",
+ "tpc_illegal_instruction",
+ "tpc_pc_wrap_around",
+ "tpc_qm_sw_err",
+ "tpc_hbw_rresp_err",
+ "tpc_hbw_bresp_err",
+ "tpc_lbw_rresp_err",
+ "tpc_lbw_bresp_err",
+ "st_unlock_already_locked",
+ "invalid_lock_access",
+ "LD_L protection violation",
+ "ST_L protection violation",
+ "D$ L0CS mismatch",
+};
+
+static const char * const guadi2_mme_error_cause[GAUDI2_NUM_OF_MME_ERR_CAUSE] = {
+ "agu_resp_intr",
+ "qman_axi_err",
+ "wap sei (wbc axi err)",
+ "arc sei",
+ "cfg access error",
+ "qm_sw_err",
+ "sbte_dbg_intr_0",
+ "sbte_dbg_intr_1",
+ "sbte_dbg_intr_2",
+ "sbte_dbg_intr_3",
+ "sbte_dbg_intr_4",
+ "sbte_prtn_intr_0",
+ "sbte_prtn_intr_1",
+ "sbte_prtn_intr_2",
+ "sbte_prtn_intr_3",
+ "sbte_prtn_intr_4",
+};
+
+static const char * const guadi2_mme_wap_error_cause[GAUDI2_NUM_OF_MME_WAP_ERR_CAUSE] = {
+ "WBC ERR RESP_0",
+ "WBC ERR RESP_1",
+ "AP SOURCE POS INF",
+ "AP SOURCE NEG INF",
+ "AP SOURCE NAN",
+ "AP RESULT POS INF",
+ "AP RESULT NEG INF",
+};
+
+static const char * const gaudi2_dma_core_interrupts_cause[GAUDI2_NUM_OF_DMA_CORE_INTR_CAUSE] = {
+ "HBW Read returned with error RRESP",
+ "HBW write returned with error BRESP",
+ "LBW write returned with error BRESP",
+ "descriptor_fifo_overflow",
+ "KDMA SB LBW Read returned with error",
+ "KDMA WBC LBW Write returned with error",
+ "TRANSPOSE ENGINE DESC FIFO OVERFLOW",
+ "WRONG CFG FOR COMMIT IN LIN DMA"
+};
+
+static const char * const gaudi2_kdma_core_interrupts_cause[GAUDI2_NUM_OF_DMA_CORE_INTR_CAUSE] = {
+ "HBW/LBW Read returned with error RRESP",
+ "HBW/LBW write returned with error BRESP",
+ "LBW write returned with error BRESP",
+ "descriptor_fifo_overflow",
+ "KDMA SB LBW Read returned with error",
+ "KDMA WBC LBW Write returned with error",
+ "TRANSPOSE ENGINE DESC FIFO OVERFLOW",
+ "WRONG CFG FOR COMMIT IN LIN DMA"
+};
+
+struct gaudi2_sm_sei_cause_data {
+ const char *cause_name;
+ const char *log_name;
+};
+
+static const struct gaudi2_sm_sei_cause_data
+gaudi2_sm_sei_cause[GAUDI2_NUM_OF_SM_SEI_ERR_CAUSE] = {
+ {"calculated SO value overflow/underflow", "SOB ID"},
+ {"payload address of monitor is not aligned to 4B", "monitor addr"},
+ {"armed monitor write got BRESP (SLVERR or DECERR)", "AXI id"},
+};
+
+static const char * const
+gaudi2_pmmu_fatal_interrupts_cause[GAUDI2_NUM_OF_PMMU_FATAL_ERR_CAUSE] = {
+ "LATENCY_RD_OUT_FIFO_OVERRUN",
+ "LATENCY_WR_OUT_FIFO_OVERRUN",
+};
+
+static const char * const
+gaudi2_hif_fatal_interrupts_cause[GAUDI2_NUM_OF_HIF_FATAL_ERR_CAUSE] = {
+ "LATENCY_RD_OUT_FIFO_OVERRUN",
+ "LATENCY_WR_OUT_FIFO_OVERRUN",
+};
+
+static const char * const
+gaudi2_psoc_axi_drain_interrupts_cause[GAUDI2_NUM_OF_AXI_DRAIN_ERR_CAUSE] = {
+ "AXI drain HBW",
+ "AXI drain LBW",
+};
+
+static const char * const
+gaudi2_pcie_addr_dec_error_cause[GAUDI2_NUM_OF_PCIE_ADDR_DEC_ERR_CAUSE] = {
+ "HBW error response",
+ "LBW error response",
+ "TLP is blocked by RR"
+};
+
+static const int gaudi2_queue_id_to_engine_id[] = {
+ [GAUDI2_QUEUE_ID_PDMA_0_0...GAUDI2_QUEUE_ID_PDMA_0_3] = GAUDI2_ENGINE_ID_PDMA_0,
+ [GAUDI2_QUEUE_ID_PDMA_1_0...GAUDI2_QUEUE_ID_PDMA_1_3] = GAUDI2_ENGINE_ID_PDMA_1,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE0_EDMA_0_3] =
+ GAUDI2_DCORE0_ENGINE_ID_EDMA_0,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0...GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3] =
+ GAUDI2_DCORE0_ENGINE_ID_EDMA_1,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE1_EDMA_0_3] =
+ GAUDI2_DCORE1_ENGINE_ID_EDMA_0,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0...GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3] =
+ GAUDI2_DCORE1_ENGINE_ID_EDMA_1,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE2_EDMA_0_3] =
+ GAUDI2_DCORE2_ENGINE_ID_EDMA_0,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0...GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3] =
+ GAUDI2_DCORE2_ENGINE_ID_EDMA_1,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE3_EDMA_0_3] =
+ GAUDI2_DCORE3_ENGINE_ID_EDMA_0,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0...GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3] =
+ GAUDI2_DCORE3_ENGINE_ID_EDMA_1,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_0...GAUDI2_QUEUE_ID_DCORE0_MME_0_3] =
+ GAUDI2_DCORE0_ENGINE_ID_MME,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_0...GAUDI2_QUEUE_ID_DCORE1_MME_0_3] =
+ GAUDI2_DCORE1_ENGINE_ID_MME,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_0...GAUDI2_QUEUE_ID_DCORE2_MME_0_3] =
+ GAUDI2_DCORE2_ENGINE_ID_MME,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_0...GAUDI2_QUEUE_ID_DCORE3_MME_0_3] =
+ GAUDI2_DCORE3_ENGINE_ID_MME,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_0...GAUDI2_QUEUE_ID_DCORE0_TPC_0_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_0,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_0...GAUDI2_QUEUE_ID_DCORE0_TPC_1_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_1,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_0...GAUDI2_QUEUE_ID_DCORE0_TPC_2_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_2,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_0...GAUDI2_QUEUE_ID_DCORE0_TPC_3_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_3,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_0...GAUDI2_QUEUE_ID_DCORE0_TPC_4_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_4,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_0...GAUDI2_QUEUE_ID_DCORE0_TPC_5_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_5,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_0...GAUDI2_QUEUE_ID_DCORE0_TPC_6_3] =
+ GAUDI2_DCORE0_ENGINE_ID_TPC_6,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_0...GAUDI2_QUEUE_ID_DCORE1_TPC_0_3] =
+ GAUDI2_DCORE1_ENGINE_ID_TPC_0,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_0...GAUDI2_QUEUE_ID_DCORE1_TPC_1_3] =
+ GAUDI2_DCORE1_ENGINE_ID_TPC_1,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_0...GAUDI2_QUEUE_ID_DCORE1_TPC_2_3] =
+ GAUDI2_DCORE1_ENGINE_ID_TPC_2,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_0...GAUDI2_QUEUE_ID_DCORE1_TPC_3_3] =
+ GAUDI2_DCORE1_ENGINE_ID_TPC_3,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_0...GAUDI2_QUEUE_ID_DCORE1_TPC_4_3] =
+ GAUDI2_DCORE1_ENGINE_ID_TPC_4,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_0...GAUDI2_QUEUE_ID_DCORE1_TPC_5_3] =
+ GAUDI2_DCORE1_ENGINE_ID_TPC_5,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_0...GAUDI2_QUEUE_ID_DCORE2_TPC_0_3] =
+ GAUDI2_DCORE2_ENGINE_ID_TPC_0,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_0...GAUDI2_QUEUE_ID_DCORE2_TPC_1_3] =
+ GAUDI2_DCORE2_ENGINE_ID_TPC_1,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_0...GAUDI2_QUEUE_ID_DCORE2_TPC_2_3] =
+ GAUDI2_DCORE2_ENGINE_ID_TPC_2,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_0...GAUDI2_QUEUE_ID_DCORE2_TPC_3_3] =
+ GAUDI2_DCORE2_ENGINE_ID_TPC_3,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_0...GAUDI2_QUEUE_ID_DCORE2_TPC_4_3] =
+ GAUDI2_DCORE2_ENGINE_ID_TPC_4,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_0...GAUDI2_QUEUE_ID_DCORE2_TPC_5_3] =
+ GAUDI2_DCORE2_ENGINE_ID_TPC_5,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_0...GAUDI2_QUEUE_ID_DCORE3_TPC_0_3] =
+ GAUDI2_DCORE3_ENGINE_ID_TPC_0,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_0...GAUDI2_QUEUE_ID_DCORE3_TPC_1_3] =
+ GAUDI2_DCORE3_ENGINE_ID_TPC_1,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_0...GAUDI2_QUEUE_ID_DCORE3_TPC_2_3] =
+ GAUDI2_DCORE3_ENGINE_ID_TPC_2,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_0...GAUDI2_QUEUE_ID_DCORE3_TPC_3_3] =
+ GAUDI2_DCORE3_ENGINE_ID_TPC_3,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_0...GAUDI2_QUEUE_ID_DCORE3_TPC_4_3] =
+ GAUDI2_DCORE3_ENGINE_ID_TPC_4,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_0...GAUDI2_QUEUE_ID_DCORE3_TPC_5_3] =
+ GAUDI2_DCORE3_ENGINE_ID_TPC_5,
+ [GAUDI2_QUEUE_ID_NIC_0_0...GAUDI2_QUEUE_ID_NIC_0_3] = GAUDI2_ENGINE_ID_NIC0_0,
+ [GAUDI2_QUEUE_ID_NIC_1_0...GAUDI2_QUEUE_ID_NIC_1_3] = GAUDI2_ENGINE_ID_NIC0_1,
+ [GAUDI2_QUEUE_ID_NIC_2_0...GAUDI2_QUEUE_ID_NIC_2_3] = GAUDI2_ENGINE_ID_NIC1_0,
+ [GAUDI2_QUEUE_ID_NIC_3_0...GAUDI2_QUEUE_ID_NIC_3_3] = GAUDI2_ENGINE_ID_NIC1_1,
+ [GAUDI2_QUEUE_ID_NIC_4_0...GAUDI2_QUEUE_ID_NIC_4_3] = GAUDI2_ENGINE_ID_NIC2_0,
+ [GAUDI2_QUEUE_ID_NIC_5_0...GAUDI2_QUEUE_ID_NIC_5_3] = GAUDI2_ENGINE_ID_NIC2_1,
+ [GAUDI2_QUEUE_ID_NIC_6_0...GAUDI2_QUEUE_ID_NIC_6_3] = GAUDI2_ENGINE_ID_NIC3_0,
+ [GAUDI2_QUEUE_ID_NIC_7_0...GAUDI2_QUEUE_ID_NIC_7_3] = GAUDI2_ENGINE_ID_NIC3_1,
+ [GAUDI2_QUEUE_ID_NIC_8_0...GAUDI2_QUEUE_ID_NIC_8_3] = GAUDI2_ENGINE_ID_NIC4_0,
+ [GAUDI2_QUEUE_ID_NIC_9_0...GAUDI2_QUEUE_ID_NIC_9_3] = GAUDI2_ENGINE_ID_NIC4_1,
+ [GAUDI2_QUEUE_ID_NIC_10_0...GAUDI2_QUEUE_ID_NIC_10_3] = GAUDI2_ENGINE_ID_NIC5_0,
+ [GAUDI2_QUEUE_ID_NIC_11_0...GAUDI2_QUEUE_ID_NIC_11_3] = GAUDI2_ENGINE_ID_NIC5_1,
+ [GAUDI2_QUEUE_ID_NIC_12_0...GAUDI2_QUEUE_ID_NIC_12_3] = GAUDI2_ENGINE_ID_NIC6_0,
+ [GAUDI2_QUEUE_ID_NIC_13_0...GAUDI2_QUEUE_ID_NIC_13_3] = GAUDI2_ENGINE_ID_NIC6_1,
+ [GAUDI2_QUEUE_ID_NIC_14_0...GAUDI2_QUEUE_ID_NIC_14_3] = GAUDI2_ENGINE_ID_NIC7_0,
+ [GAUDI2_QUEUE_ID_NIC_15_0...GAUDI2_QUEUE_ID_NIC_15_3] = GAUDI2_ENGINE_ID_NIC7_1,
+ [GAUDI2_QUEUE_ID_NIC_16_0...GAUDI2_QUEUE_ID_NIC_16_3] = GAUDI2_ENGINE_ID_NIC8_0,
+ [GAUDI2_QUEUE_ID_NIC_17_0...GAUDI2_QUEUE_ID_NIC_17_3] = GAUDI2_ENGINE_ID_NIC8_1,
+ [GAUDI2_QUEUE_ID_NIC_18_0...GAUDI2_QUEUE_ID_NIC_18_3] = GAUDI2_ENGINE_ID_NIC9_0,
+ [GAUDI2_QUEUE_ID_NIC_19_0...GAUDI2_QUEUE_ID_NIC_19_3] = GAUDI2_ENGINE_ID_NIC9_1,
+ [GAUDI2_QUEUE_ID_NIC_20_0...GAUDI2_QUEUE_ID_NIC_20_3] = GAUDI2_ENGINE_ID_NIC10_0,
+ [GAUDI2_QUEUE_ID_NIC_21_0...GAUDI2_QUEUE_ID_NIC_21_3] = GAUDI2_ENGINE_ID_NIC10_1,
+ [GAUDI2_QUEUE_ID_NIC_22_0...GAUDI2_QUEUE_ID_NIC_22_3] = GAUDI2_ENGINE_ID_NIC11_0,
+ [GAUDI2_QUEUE_ID_NIC_23_0...GAUDI2_QUEUE_ID_NIC_23_3] = GAUDI2_ENGINE_ID_NIC11_1,
+ [GAUDI2_QUEUE_ID_ROT_0_0...GAUDI2_QUEUE_ID_ROT_0_3] = GAUDI2_ENGINE_ID_ROT_0,
+ [GAUDI2_QUEUE_ID_ROT_1_0...GAUDI2_QUEUE_ID_ROT_1_3] = GAUDI2_ENGINE_ID_ROT_1,
+};
+
+const u32 gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_SIZE] = {
+ [GAUDI2_QUEUE_ID_PDMA_0_0] = mmPDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_0_1] = mmPDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_0_2] = mmPDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_0_3] = mmPDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_1_0] = mmPDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_1_1] = mmPDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_1_2] = mmPDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_PDMA_1_3] = mmPDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0] = mmDCORE0_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_1] = mmDCORE0_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_2] = mmDCORE0_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_3] = mmDCORE0_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0] = mmDCORE0_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_1] = mmDCORE0_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_2] = mmDCORE0_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3] = mmDCORE0_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_0] = mmDCORE0_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_1] = mmDCORE0_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_2] = mmDCORE0_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_3] = mmDCORE0_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_0] = mmDCORE0_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_1] = mmDCORE0_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_2] = mmDCORE0_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_3] = mmDCORE0_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_0] = mmDCORE0_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_1] = mmDCORE0_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_2] = mmDCORE0_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_3] = mmDCORE0_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_0] = mmDCORE0_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_1] = mmDCORE0_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_2] = mmDCORE0_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_3] = mmDCORE0_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_0] = mmDCORE0_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_1] = mmDCORE0_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_2] = mmDCORE0_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_3] = mmDCORE0_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_0] = mmDCORE0_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_1] = mmDCORE0_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_2] = mmDCORE0_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_3] = mmDCORE0_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_0] = mmDCORE0_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_1] = mmDCORE0_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_2] = mmDCORE0_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_3] = mmDCORE0_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_0] = mmDCORE0_TPC6_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_1] = mmDCORE0_TPC6_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_2] = mmDCORE0_TPC6_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_3] = mmDCORE0_TPC6_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0] = mmDCORE1_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_1] = mmDCORE1_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_2] = mmDCORE1_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_3] = mmDCORE1_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0] = mmDCORE1_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_1] = mmDCORE1_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_2] = mmDCORE1_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3] = mmDCORE1_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_0] = mmDCORE1_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_1] = mmDCORE1_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_2] = mmDCORE1_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_3] = mmDCORE1_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_0] = mmDCORE1_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_1] = mmDCORE1_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_2] = mmDCORE1_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_3] = mmDCORE1_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_0] = mmDCORE1_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_1] = mmDCORE1_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_2] = mmDCORE1_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_3] = mmDCORE1_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_0] = mmDCORE1_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_1] = mmDCORE1_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_2] = mmDCORE1_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_3] = mmDCORE1_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_0] = mmDCORE1_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_1] = mmDCORE1_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_2] = mmDCORE1_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_3] = mmDCORE1_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_0] = mmDCORE1_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_1] = mmDCORE1_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_2] = mmDCORE1_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_3] = mmDCORE1_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_0] = mmDCORE1_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_1] = mmDCORE1_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_2] = mmDCORE1_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_3] = mmDCORE1_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0] = mmDCORE2_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_1] = mmDCORE2_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_2] = mmDCORE2_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_3] = mmDCORE2_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0] = mmDCORE2_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_1] = mmDCORE2_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_2] = mmDCORE2_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3] = mmDCORE2_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_0] = mmDCORE2_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_1] = mmDCORE2_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_2] = mmDCORE2_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_3] = mmDCORE2_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_0] = mmDCORE2_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_1] = mmDCORE2_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_2] = mmDCORE2_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_3] = mmDCORE2_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_0] = mmDCORE2_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_1] = mmDCORE2_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_2] = mmDCORE2_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_3] = mmDCORE2_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_0] = mmDCORE2_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_1] = mmDCORE2_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_2] = mmDCORE2_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_3] = mmDCORE2_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_0] = mmDCORE2_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_1] = mmDCORE2_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_2] = mmDCORE2_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_3] = mmDCORE2_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_0] = mmDCORE2_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_1] = mmDCORE2_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_2] = mmDCORE2_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_3] = mmDCORE2_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_0] = mmDCORE2_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_1] = mmDCORE2_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_2] = mmDCORE2_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_3] = mmDCORE2_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0] = mmDCORE3_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_1] = mmDCORE3_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_2] = mmDCORE3_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_3] = mmDCORE3_EDMA0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0] = mmDCORE3_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_1] = mmDCORE3_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_2] = mmDCORE3_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3] = mmDCORE3_EDMA1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_0] = mmDCORE3_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_1] = mmDCORE3_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_2] = mmDCORE3_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_3] = mmDCORE3_MME_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_0] = mmDCORE3_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_1] = mmDCORE3_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_2] = mmDCORE3_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_3] = mmDCORE3_TPC0_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_0] = mmDCORE3_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_1] = mmDCORE3_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_2] = mmDCORE3_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_3] = mmDCORE3_TPC1_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_0] = mmDCORE3_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_1] = mmDCORE3_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_2] = mmDCORE3_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_3] = mmDCORE3_TPC2_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_0] = mmDCORE3_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_1] = mmDCORE3_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_2] = mmDCORE3_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_3] = mmDCORE3_TPC3_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_0] = mmDCORE3_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_1] = mmDCORE3_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_2] = mmDCORE3_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_3] = mmDCORE3_TPC4_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_0] = mmDCORE3_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_1] = mmDCORE3_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_2] = mmDCORE3_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_3] = mmDCORE3_TPC5_QM_BASE,
+ [GAUDI2_QUEUE_ID_NIC_0_0] = mmNIC0_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_0_1] = mmNIC0_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_0_2] = mmNIC0_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_0_3] = mmNIC0_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_1_0] = mmNIC0_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_1_1] = mmNIC0_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_1_2] = mmNIC0_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_1_3] = mmNIC0_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_2_0] = mmNIC1_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_2_1] = mmNIC1_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_2_2] = mmNIC1_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_2_3] = mmNIC1_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_3_0] = mmNIC1_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_3_1] = mmNIC1_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_3_2] = mmNIC1_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_3_3] = mmNIC1_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_4_0] = mmNIC2_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_4_1] = mmNIC2_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_4_2] = mmNIC2_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_4_3] = mmNIC2_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_5_0] = mmNIC2_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_5_1] = mmNIC2_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_5_2] = mmNIC2_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_5_3] = mmNIC2_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_6_0] = mmNIC3_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_6_1] = mmNIC3_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_6_2] = mmNIC3_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_6_3] = mmNIC3_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_7_0] = mmNIC3_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_7_1] = mmNIC3_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_7_2] = mmNIC3_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_7_3] = mmNIC3_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_8_0] = mmNIC4_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_8_1] = mmNIC4_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_8_2] = mmNIC4_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_8_3] = mmNIC4_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_9_0] = mmNIC4_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_9_1] = mmNIC4_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_9_2] = mmNIC4_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_9_3] = mmNIC4_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_10_0] = mmNIC5_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_10_1] = mmNIC5_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_10_2] = mmNIC5_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_10_3] = mmNIC5_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_11_0] = mmNIC5_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_11_1] = mmNIC5_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_11_2] = mmNIC5_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_11_3] = mmNIC5_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_12_0] = mmNIC6_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_12_1] = mmNIC6_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_12_2] = mmNIC6_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_12_3] = mmNIC6_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_13_0] = mmNIC6_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_13_1] = mmNIC6_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_13_2] = mmNIC6_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_13_3] = mmNIC6_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_14_0] = mmNIC7_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_14_1] = mmNIC7_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_14_2] = mmNIC7_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_14_3] = mmNIC7_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_15_0] = mmNIC7_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_15_1] = mmNIC7_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_15_2] = mmNIC7_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_15_3] = mmNIC7_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_16_0] = mmNIC8_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_16_1] = mmNIC8_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_16_2] = mmNIC8_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_16_3] = mmNIC8_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_17_0] = mmNIC8_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_17_1] = mmNIC8_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_17_2] = mmNIC8_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_17_3] = mmNIC8_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_18_0] = mmNIC9_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_18_1] = mmNIC9_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_18_2] = mmNIC9_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_18_3] = mmNIC9_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_19_0] = mmNIC9_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_19_1] = mmNIC9_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_19_2] = mmNIC9_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_19_3] = mmNIC9_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_20_0] = mmNIC10_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_20_1] = mmNIC10_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_20_2] = mmNIC10_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_20_3] = mmNIC10_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_21_0] = mmNIC10_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_21_1] = mmNIC10_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_21_2] = mmNIC10_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_21_3] = mmNIC10_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_22_0] = mmNIC11_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_22_1] = mmNIC11_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_22_2] = mmNIC11_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_22_3] = mmNIC11_QM0_BASE,
+ [GAUDI2_QUEUE_ID_NIC_23_0] = mmNIC11_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_23_1] = mmNIC11_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_23_2] = mmNIC11_QM1_BASE,
+ [GAUDI2_QUEUE_ID_NIC_23_3] = mmNIC11_QM1_BASE,
+ [GAUDI2_QUEUE_ID_ROT_0_0] = mmROT0_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_0_1] = mmROT0_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_0_2] = mmROT0_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_0_3] = mmROT0_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_1_0] = mmROT1_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_1_1] = mmROT1_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_1_2] = mmROT1_QM_BASE,
+ [GAUDI2_QUEUE_ID_ROT_1_3] = mmROT1_QM_BASE
+};
+
+static const u32 gaudi2_arc_blocks_bases[NUM_ARC_CPUS] = {
+ [CPU_ID_SCHED_ARC0] = mmARC_FARM_ARC0_AUX_BASE,
+ [CPU_ID_SCHED_ARC1] = mmARC_FARM_ARC1_AUX_BASE,
+ [CPU_ID_SCHED_ARC2] = mmARC_FARM_ARC2_AUX_BASE,
+ [CPU_ID_SCHED_ARC3] = mmARC_FARM_ARC3_AUX_BASE,
+ [CPU_ID_SCHED_ARC4] = mmDCORE1_MME_QM_ARC_AUX_BASE,
+ [CPU_ID_SCHED_ARC5] = mmDCORE3_MME_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC0] = mmDCORE0_TPC0_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC1] = mmDCORE0_TPC1_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC2] = mmDCORE0_TPC2_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC3] = mmDCORE0_TPC3_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC4] = mmDCORE0_TPC4_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC5] = mmDCORE0_TPC5_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC6] = mmDCORE1_TPC0_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC7] = mmDCORE1_TPC1_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC8] = mmDCORE1_TPC2_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC9] = mmDCORE1_TPC3_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC10] = mmDCORE1_TPC4_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC11] = mmDCORE1_TPC5_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC12] = mmDCORE2_TPC0_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC13] = mmDCORE2_TPC1_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC14] = mmDCORE2_TPC2_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC15] = mmDCORE2_TPC3_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC16] = mmDCORE2_TPC4_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC17] = mmDCORE2_TPC5_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC18] = mmDCORE3_TPC0_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC19] = mmDCORE3_TPC1_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC20] = mmDCORE3_TPC2_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC21] = mmDCORE3_TPC3_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC22] = mmDCORE3_TPC4_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC23] = mmDCORE3_TPC5_QM_ARC_AUX_BASE,
+ [CPU_ID_TPC_QMAN_ARC24] = mmDCORE0_TPC6_QM_ARC_AUX_BASE,
+ [CPU_ID_MME_QMAN_ARC0] = mmDCORE0_MME_QM_ARC_AUX_BASE,
+ [CPU_ID_MME_QMAN_ARC1] = mmDCORE2_MME_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC0] = mmDCORE0_EDMA0_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC1] = mmDCORE0_EDMA1_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC2] = mmDCORE1_EDMA0_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC3] = mmDCORE1_EDMA1_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC4] = mmDCORE2_EDMA0_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC5] = mmDCORE2_EDMA1_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC6] = mmDCORE3_EDMA0_QM_ARC_AUX_BASE,
+ [CPU_ID_EDMA_QMAN_ARC7] = mmDCORE3_EDMA1_QM_ARC_AUX_BASE,
+ [CPU_ID_PDMA_QMAN_ARC0] = mmPDMA0_QM_ARC_AUX_BASE,
+ [CPU_ID_PDMA_QMAN_ARC1] = mmPDMA1_QM_ARC_AUX_BASE,
+ [CPU_ID_ROT_QMAN_ARC0] = mmROT0_QM_ARC_AUX_BASE,
+ [CPU_ID_ROT_QMAN_ARC1] = mmROT1_QM_ARC_AUX_BASE,
+ [CPU_ID_NIC_QMAN_ARC0] = mmNIC0_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC1] = mmNIC0_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC2] = mmNIC1_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC3] = mmNIC1_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC4] = mmNIC2_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC5] = mmNIC2_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC6] = mmNIC3_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC7] = mmNIC3_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC8] = mmNIC4_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC9] = mmNIC4_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC10] = mmNIC5_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC11] = mmNIC5_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC12] = mmNIC6_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC13] = mmNIC6_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC14] = mmNIC7_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC15] = mmNIC7_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC16] = mmNIC8_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC17] = mmNIC8_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC18] = mmNIC9_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC19] = mmNIC9_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC20] = mmNIC10_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC21] = mmNIC10_QM_ARC_AUX1_BASE,
+ [CPU_ID_NIC_QMAN_ARC22] = mmNIC11_QM_ARC_AUX0_BASE,
+ [CPU_ID_NIC_QMAN_ARC23] = mmNIC11_QM_ARC_AUX1_BASE,
+};
+
+static const u32 gaudi2_arc_dccm_bases[NUM_ARC_CPUS] = {
+ [CPU_ID_SCHED_ARC0] = mmARC_FARM_ARC0_DCCM0_BASE,
+ [CPU_ID_SCHED_ARC1] = mmARC_FARM_ARC1_DCCM0_BASE,
+ [CPU_ID_SCHED_ARC2] = mmARC_FARM_ARC2_DCCM0_BASE,
+ [CPU_ID_SCHED_ARC3] = mmARC_FARM_ARC3_DCCM0_BASE,
+ [CPU_ID_SCHED_ARC4] = mmDCORE1_MME_QM_ARC_DCCM_BASE,
+ [CPU_ID_SCHED_ARC5] = mmDCORE3_MME_QM_ARC_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC0] = mmDCORE0_TPC0_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC1] = mmDCORE0_TPC1_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC2] = mmDCORE0_TPC2_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC3] = mmDCORE0_TPC3_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC4] = mmDCORE0_TPC4_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC5] = mmDCORE0_TPC5_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC6] = mmDCORE1_TPC0_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC7] = mmDCORE1_TPC1_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC8] = mmDCORE1_TPC2_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC9] = mmDCORE1_TPC3_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC10] = mmDCORE1_TPC4_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC11] = mmDCORE1_TPC5_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC12] = mmDCORE2_TPC0_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC13] = mmDCORE2_TPC1_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC14] = mmDCORE2_TPC2_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC15] = mmDCORE2_TPC3_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC16] = mmDCORE2_TPC4_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC17] = mmDCORE2_TPC5_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC18] = mmDCORE3_TPC0_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC19] = mmDCORE3_TPC1_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC20] = mmDCORE3_TPC2_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC21] = mmDCORE3_TPC3_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC22] = mmDCORE3_TPC4_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC23] = mmDCORE3_TPC5_QM_DCCM_BASE,
+ [CPU_ID_TPC_QMAN_ARC24] = mmDCORE0_TPC6_QM_DCCM_BASE,
+ [CPU_ID_MME_QMAN_ARC0] = mmDCORE0_MME_QM_ARC_DCCM_BASE,
+ [CPU_ID_MME_QMAN_ARC1] = mmDCORE2_MME_QM_ARC_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC0] = mmDCORE0_EDMA0_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC1] = mmDCORE0_EDMA1_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC2] = mmDCORE1_EDMA0_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC3] = mmDCORE1_EDMA1_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC4] = mmDCORE2_EDMA0_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC5] = mmDCORE2_EDMA1_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC6] = mmDCORE3_EDMA0_QM_DCCM_BASE,
+ [CPU_ID_EDMA_QMAN_ARC7] = mmDCORE3_EDMA1_QM_DCCM_BASE,
+ [CPU_ID_PDMA_QMAN_ARC0] = mmPDMA0_QM_ARC_DCCM_BASE,
+ [CPU_ID_PDMA_QMAN_ARC1] = mmPDMA1_QM_ARC_DCCM_BASE,
+ [CPU_ID_ROT_QMAN_ARC0] = mmROT0_QM_ARC_DCCM_BASE,
+ [CPU_ID_ROT_QMAN_ARC1] = mmROT1_QM_ARC_DCCM_BASE,
+ [CPU_ID_NIC_QMAN_ARC0] = mmNIC0_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC1] = mmNIC0_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC2] = mmNIC1_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC3] = mmNIC1_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC4] = mmNIC2_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC5] = mmNIC2_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC6] = mmNIC3_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC7] = mmNIC3_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC8] = mmNIC4_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC9] = mmNIC4_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC10] = mmNIC5_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC11] = mmNIC5_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC12] = mmNIC6_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC13] = mmNIC6_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC14] = mmNIC7_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC15] = mmNIC7_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC16] = mmNIC8_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC17] = mmNIC8_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC18] = mmNIC9_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC19] = mmNIC9_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC20] = mmNIC10_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC21] = mmNIC10_QM_DCCM1_BASE,
+ [CPU_ID_NIC_QMAN_ARC22] = mmNIC11_QM_DCCM0_BASE,
+ [CPU_ID_NIC_QMAN_ARC23] = mmNIC11_QM_DCCM1_BASE,
+};
+
+const u32 gaudi2_mme_ctrl_lo_blocks_bases[MME_ID_SIZE] = {
+ [MME_ID_DCORE0] = mmDCORE0_MME_CTRL_LO_BASE,
+ [MME_ID_DCORE1] = mmDCORE1_MME_CTRL_LO_BASE,
+ [MME_ID_DCORE2] = mmDCORE2_MME_CTRL_LO_BASE,
+ [MME_ID_DCORE3] = mmDCORE3_MME_CTRL_LO_BASE,
+};
+
+static const u32 gaudi2_queue_id_to_arc_id[GAUDI2_QUEUE_ID_SIZE] = {
+ [GAUDI2_QUEUE_ID_PDMA_0_0] = CPU_ID_PDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_PDMA_0_1] = CPU_ID_PDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_PDMA_0_2] = CPU_ID_PDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_PDMA_0_3] = CPU_ID_PDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_PDMA_1_0] = CPU_ID_PDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_PDMA_1_1] = CPU_ID_PDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_PDMA_1_2] = CPU_ID_PDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_PDMA_1_3] = CPU_ID_PDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0] = CPU_ID_EDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_1] = CPU_ID_EDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_2] = CPU_ID_EDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_0_3] = CPU_ID_EDMA_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0] = CPU_ID_EDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_1] = CPU_ID_EDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_2] = CPU_ID_EDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3] = CPU_ID_EDMA_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_0] = CPU_ID_MME_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_1] = CPU_ID_MME_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_2] = CPU_ID_MME_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_MME_0_3] = CPU_ID_MME_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_0] = CPU_ID_TPC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_1] = CPU_ID_TPC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_2] = CPU_ID_TPC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_0_3] = CPU_ID_TPC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_0] = CPU_ID_TPC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_1] = CPU_ID_TPC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_2] = CPU_ID_TPC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_1_3] = CPU_ID_TPC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_0] = CPU_ID_TPC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_1] = CPU_ID_TPC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_2] = CPU_ID_TPC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_2_3] = CPU_ID_TPC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_0] = CPU_ID_TPC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_1] = CPU_ID_TPC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_2] = CPU_ID_TPC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_3_3] = CPU_ID_TPC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_0] = CPU_ID_TPC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_1] = CPU_ID_TPC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_2] = CPU_ID_TPC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_4_3] = CPU_ID_TPC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_0] = CPU_ID_TPC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_1] = CPU_ID_TPC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_2] = CPU_ID_TPC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_5_3] = CPU_ID_TPC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_0] = CPU_ID_TPC_QMAN_ARC24,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_1] = CPU_ID_TPC_QMAN_ARC24,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_2] = CPU_ID_TPC_QMAN_ARC24,
+ [GAUDI2_QUEUE_ID_DCORE0_TPC_6_3] = CPU_ID_TPC_QMAN_ARC24,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0] = CPU_ID_EDMA_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_1] = CPU_ID_EDMA_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_2] = CPU_ID_EDMA_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_0_3] = CPU_ID_EDMA_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0] = CPU_ID_EDMA_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_1] = CPU_ID_EDMA_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_2] = CPU_ID_EDMA_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3] = CPU_ID_EDMA_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_0] = CPU_ID_SCHED_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_1] = CPU_ID_SCHED_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_2] = CPU_ID_SCHED_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE1_MME_0_3] = CPU_ID_SCHED_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_0] = CPU_ID_TPC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_1] = CPU_ID_TPC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_2] = CPU_ID_TPC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_0_3] = CPU_ID_TPC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_0] = CPU_ID_TPC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_1] = CPU_ID_TPC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_2] = CPU_ID_TPC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_1_3] = CPU_ID_TPC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_0] = CPU_ID_TPC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_1] = CPU_ID_TPC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_2] = CPU_ID_TPC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_2_3] = CPU_ID_TPC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_0] = CPU_ID_TPC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_1] = CPU_ID_TPC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_2] = CPU_ID_TPC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_3_3] = CPU_ID_TPC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_0] = CPU_ID_TPC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_1] = CPU_ID_TPC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_2] = CPU_ID_TPC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_4_3] = CPU_ID_TPC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_0] = CPU_ID_TPC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_1] = CPU_ID_TPC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_2] = CPU_ID_TPC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_DCORE1_TPC_5_3] = CPU_ID_TPC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0] = CPU_ID_EDMA_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_1] = CPU_ID_EDMA_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_2] = CPU_ID_EDMA_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_0_3] = CPU_ID_EDMA_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0] = CPU_ID_EDMA_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_1] = CPU_ID_EDMA_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_2] = CPU_ID_EDMA_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3] = CPU_ID_EDMA_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_0] = CPU_ID_MME_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_1] = CPU_ID_MME_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_2] = CPU_ID_MME_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE2_MME_0_3] = CPU_ID_MME_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_0] = CPU_ID_TPC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_1] = CPU_ID_TPC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_2] = CPU_ID_TPC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_0_3] = CPU_ID_TPC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_0] = CPU_ID_TPC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_1] = CPU_ID_TPC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_2] = CPU_ID_TPC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_1_3] = CPU_ID_TPC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_0] = CPU_ID_TPC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_1] = CPU_ID_TPC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_2] = CPU_ID_TPC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_2_3] = CPU_ID_TPC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_0] = CPU_ID_TPC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_1] = CPU_ID_TPC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_2] = CPU_ID_TPC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_3_3] = CPU_ID_TPC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_0] = CPU_ID_TPC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_1] = CPU_ID_TPC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_2] = CPU_ID_TPC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_4_3] = CPU_ID_TPC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_0] = CPU_ID_TPC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_1] = CPU_ID_TPC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_2] = CPU_ID_TPC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_DCORE2_TPC_5_3] = CPU_ID_TPC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0] = CPU_ID_EDMA_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_1] = CPU_ID_EDMA_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_2] = CPU_ID_EDMA_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_0_3] = CPU_ID_EDMA_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0] = CPU_ID_EDMA_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_1] = CPU_ID_EDMA_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_2] = CPU_ID_EDMA_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3] = CPU_ID_EDMA_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_0] = CPU_ID_SCHED_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_1] = CPU_ID_SCHED_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_2] = CPU_ID_SCHED_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE3_MME_0_3] = CPU_ID_SCHED_ARC5,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_0] = CPU_ID_TPC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_1] = CPU_ID_TPC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_2] = CPU_ID_TPC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_0_3] = CPU_ID_TPC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_0] = CPU_ID_TPC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_1] = CPU_ID_TPC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_2] = CPU_ID_TPC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_1_3] = CPU_ID_TPC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_0] = CPU_ID_TPC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_1] = CPU_ID_TPC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_2] = CPU_ID_TPC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_2_3] = CPU_ID_TPC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_0] = CPU_ID_TPC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_1] = CPU_ID_TPC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_2] = CPU_ID_TPC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_3_3] = CPU_ID_TPC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_0] = CPU_ID_TPC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_1] = CPU_ID_TPC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_2] = CPU_ID_TPC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_4_3] = CPU_ID_TPC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_0] = CPU_ID_TPC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_1] = CPU_ID_TPC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_2] = CPU_ID_TPC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_DCORE3_TPC_5_3] = CPU_ID_TPC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_NIC_0_0] = CPU_ID_NIC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_NIC_0_1] = CPU_ID_NIC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_NIC_0_2] = CPU_ID_NIC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_NIC_0_3] = CPU_ID_NIC_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_NIC_1_0] = CPU_ID_NIC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_NIC_1_1] = CPU_ID_NIC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_NIC_1_2] = CPU_ID_NIC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_NIC_1_3] = CPU_ID_NIC_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_NIC_2_0] = CPU_ID_NIC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_NIC_2_1] = CPU_ID_NIC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_NIC_2_2] = CPU_ID_NIC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_NIC_2_3] = CPU_ID_NIC_QMAN_ARC2,
+ [GAUDI2_QUEUE_ID_NIC_3_0] = CPU_ID_NIC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_NIC_3_1] = CPU_ID_NIC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_NIC_3_2] = CPU_ID_NIC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_NIC_3_3] = CPU_ID_NIC_QMAN_ARC3,
+ [GAUDI2_QUEUE_ID_NIC_4_0] = CPU_ID_NIC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_NIC_4_1] = CPU_ID_NIC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_NIC_4_2] = CPU_ID_NIC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_NIC_4_3] = CPU_ID_NIC_QMAN_ARC4,
+ [GAUDI2_QUEUE_ID_NIC_5_0] = CPU_ID_NIC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_NIC_5_1] = CPU_ID_NIC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_NIC_5_2] = CPU_ID_NIC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_NIC_5_3] = CPU_ID_NIC_QMAN_ARC5,
+ [GAUDI2_QUEUE_ID_NIC_6_0] = CPU_ID_NIC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_NIC_6_1] = CPU_ID_NIC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_NIC_6_2] = CPU_ID_NIC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_NIC_6_3] = CPU_ID_NIC_QMAN_ARC6,
+ [GAUDI2_QUEUE_ID_NIC_7_0] = CPU_ID_NIC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_NIC_7_1] = CPU_ID_NIC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_NIC_7_2] = CPU_ID_NIC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_NIC_7_3] = CPU_ID_NIC_QMAN_ARC7,
+ [GAUDI2_QUEUE_ID_NIC_8_0] = CPU_ID_NIC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_NIC_8_1] = CPU_ID_NIC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_NIC_8_2] = CPU_ID_NIC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_NIC_8_3] = CPU_ID_NIC_QMAN_ARC8,
+ [GAUDI2_QUEUE_ID_NIC_9_0] = CPU_ID_NIC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_NIC_9_1] = CPU_ID_NIC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_NIC_9_2] = CPU_ID_NIC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_NIC_9_3] = CPU_ID_NIC_QMAN_ARC9,
+ [GAUDI2_QUEUE_ID_NIC_10_0] = CPU_ID_NIC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_NIC_10_1] = CPU_ID_NIC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_NIC_10_2] = CPU_ID_NIC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_NIC_10_3] = CPU_ID_NIC_QMAN_ARC10,
+ [GAUDI2_QUEUE_ID_NIC_11_0] = CPU_ID_NIC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_NIC_11_1] = CPU_ID_NIC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_NIC_11_2] = CPU_ID_NIC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_NIC_11_3] = CPU_ID_NIC_QMAN_ARC11,
+ [GAUDI2_QUEUE_ID_NIC_12_0] = CPU_ID_NIC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_NIC_12_1] = CPU_ID_NIC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_NIC_12_2] = CPU_ID_NIC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_NIC_12_3] = CPU_ID_NIC_QMAN_ARC12,
+ [GAUDI2_QUEUE_ID_NIC_13_0] = CPU_ID_NIC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_NIC_13_1] = CPU_ID_NIC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_NIC_13_2] = CPU_ID_NIC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_NIC_13_3] = CPU_ID_NIC_QMAN_ARC13,
+ [GAUDI2_QUEUE_ID_NIC_14_0] = CPU_ID_NIC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_NIC_14_1] = CPU_ID_NIC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_NIC_14_2] = CPU_ID_NIC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_NIC_14_3] = CPU_ID_NIC_QMAN_ARC14,
+ [GAUDI2_QUEUE_ID_NIC_15_0] = CPU_ID_NIC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_NIC_15_1] = CPU_ID_NIC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_NIC_15_2] = CPU_ID_NIC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_NIC_15_3] = CPU_ID_NIC_QMAN_ARC15,
+ [GAUDI2_QUEUE_ID_NIC_16_0] = CPU_ID_NIC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_NIC_16_1] = CPU_ID_NIC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_NIC_16_2] = CPU_ID_NIC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_NIC_16_3] = CPU_ID_NIC_QMAN_ARC16,
+ [GAUDI2_QUEUE_ID_NIC_17_0] = CPU_ID_NIC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_NIC_17_1] = CPU_ID_NIC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_NIC_17_2] = CPU_ID_NIC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_NIC_17_3] = CPU_ID_NIC_QMAN_ARC17,
+ [GAUDI2_QUEUE_ID_NIC_18_0] = CPU_ID_NIC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_NIC_18_1] = CPU_ID_NIC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_NIC_18_2] = CPU_ID_NIC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_NIC_18_3] = CPU_ID_NIC_QMAN_ARC18,
+ [GAUDI2_QUEUE_ID_NIC_19_0] = CPU_ID_NIC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_NIC_19_1] = CPU_ID_NIC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_NIC_19_2] = CPU_ID_NIC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_NIC_19_3] = CPU_ID_NIC_QMAN_ARC19,
+ [GAUDI2_QUEUE_ID_NIC_20_0] = CPU_ID_NIC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_NIC_20_1] = CPU_ID_NIC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_NIC_20_2] = CPU_ID_NIC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_NIC_20_3] = CPU_ID_NIC_QMAN_ARC20,
+ [GAUDI2_QUEUE_ID_NIC_21_0] = CPU_ID_NIC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_NIC_21_1] = CPU_ID_NIC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_NIC_21_2] = CPU_ID_NIC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_NIC_21_3] = CPU_ID_NIC_QMAN_ARC21,
+ [GAUDI2_QUEUE_ID_NIC_22_0] = CPU_ID_NIC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_NIC_22_1] = CPU_ID_NIC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_NIC_22_2] = CPU_ID_NIC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_NIC_22_3] = CPU_ID_NIC_QMAN_ARC22,
+ [GAUDI2_QUEUE_ID_NIC_23_0] = CPU_ID_NIC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_NIC_23_1] = CPU_ID_NIC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_NIC_23_2] = CPU_ID_NIC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_NIC_23_3] = CPU_ID_NIC_QMAN_ARC23,
+ [GAUDI2_QUEUE_ID_ROT_0_0] = CPU_ID_ROT_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_ROT_0_1] = CPU_ID_ROT_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_ROT_0_2] = CPU_ID_ROT_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_ROT_0_3] = CPU_ID_ROT_QMAN_ARC0,
+ [GAUDI2_QUEUE_ID_ROT_1_0] = CPU_ID_ROT_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_ROT_1_1] = CPU_ID_ROT_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_ROT_1_2] = CPU_ID_ROT_QMAN_ARC1,
+ [GAUDI2_QUEUE_ID_ROT_1_3] = CPU_ID_ROT_QMAN_ARC1
+};
+
+const u32 gaudi2_dma_core_blocks_bases[DMA_CORE_ID_SIZE] = {
+ [DMA_CORE_ID_PDMA0] = mmPDMA0_CORE_BASE,
+ [DMA_CORE_ID_PDMA1] = mmPDMA1_CORE_BASE,
+ [DMA_CORE_ID_EDMA0] = mmDCORE0_EDMA0_CORE_BASE,
+ [DMA_CORE_ID_EDMA1] = mmDCORE0_EDMA1_CORE_BASE,
+ [DMA_CORE_ID_EDMA2] = mmDCORE1_EDMA0_CORE_BASE,
+ [DMA_CORE_ID_EDMA3] = mmDCORE1_EDMA1_CORE_BASE,
+ [DMA_CORE_ID_EDMA4] = mmDCORE2_EDMA0_CORE_BASE,
+ [DMA_CORE_ID_EDMA5] = mmDCORE2_EDMA1_CORE_BASE,
+ [DMA_CORE_ID_EDMA6] = mmDCORE3_EDMA0_CORE_BASE,
+ [DMA_CORE_ID_EDMA7] = mmDCORE3_EDMA1_CORE_BASE,
+ [DMA_CORE_ID_KDMA] = mmARC_FARM_KDMA_BASE
+};
+
+const u32 gaudi2_mme_acc_blocks_bases[MME_ID_SIZE] = {
+ [MME_ID_DCORE0] = mmDCORE0_MME_ACC_BASE,
+ [MME_ID_DCORE1] = mmDCORE1_MME_ACC_BASE,
+ [MME_ID_DCORE2] = mmDCORE2_MME_ACC_BASE,
+ [MME_ID_DCORE3] = mmDCORE3_MME_ACC_BASE
+};
+
+static const u32 gaudi2_tpc_cfg_blocks_bases[TPC_ID_SIZE] = {
+ [TPC_ID_DCORE0_TPC0] = mmDCORE0_TPC0_CFG_BASE,
+ [TPC_ID_DCORE0_TPC1] = mmDCORE0_TPC1_CFG_BASE,
+ [TPC_ID_DCORE0_TPC2] = mmDCORE0_TPC2_CFG_BASE,
+ [TPC_ID_DCORE0_TPC3] = mmDCORE0_TPC3_CFG_BASE,
+ [TPC_ID_DCORE0_TPC4] = mmDCORE0_TPC4_CFG_BASE,
+ [TPC_ID_DCORE0_TPC5] = mmDCORE0_TPC5_CFG_BASE,
+ [TPC_ID_DCORE1_TPC0] = mmDCORE1_TPC0_CFG_BASE,
+ [TPC_ID_DCORE1_TPC1] = mmDCORE1_TPC1_CFG_BASE,
+ [TPC_ID_DCORE1_TPC2] = mmDCORE1_TPC2_CFG_BASE,
+ [TPC_ID_DCORE1_TPC3] = mmDCORE1_TPC3_CFG_BASE,
+ [TPC_ID_DCORE1_TPC4] = mmDCORE1_TPC4_CFG_BASE,
+ [TPC_ID_DCORE1_TPC5] = mmDCORE1_TPC5_CFG_BASE,
+ [TPC_ID_DCORE2_TPC0] = mmDCORE2_TPC0_CFG_BASE,
+ [TPC_ID_DCORE2_TPC1] = mmDCORE2_TPC1_CFG_BASE,
+ [TPC_ID_DCORE2_TPC2] = mmDCORE2_TPC2_CFG_BASE,
+ [TPC_ID_DCORE2_TPC3] = mmDCORE2_TPC3_CFG_BASE,
+ [TPC_ID_DCORE2_TPC4] = mmDCORE2_TPC4_CFG_BASE,
+ [TPC_ID_DCORE2_TPC5] = mmDCORE2_TPC5_CFG_BASE,
+ [TPC_ID_DCORE3_TPC0] = mmDCORE3_TPC0_CFG_BASE,
+ [TPC_ID_DCORE3_TPC1] = mmDCORE3_TPC1_CFG_BASE,
+ [TPC_ID_DCORE3_TPC2] = mmDCORE3_TPC2_CFG_BASE,
+ [TPC_ID_DCORE3_TPC3] = mmDCORE3_TPC3_CFG_BASE,
+ [TPC_ID_DCORE3_TPC4] = mmDCORE3_TPC4_CFG_BASE,
+ [TPC_ID_DCORE3_TPC5] = mmDCORE3_TPC5_CFG_BASE,
+ [TPC_ID_DCORE0_TPC6] = mmDCORE0_TPC6_CFG_BASE,
+};
+
+static const u32 gaudi2_tpc_eml_cfg_blocks_bases[TPC_ID_SIZE] = {
+ [TPC_ID_DCORE0_TPC0] = mmDCORE0_TPC0_EML_CFG_BASE,
+ [TPC_ID_DCORE0_TPC1] = mmDCORE0_TPC1_EML_CFG_BASE,
+ [TPC_ID_DCORE0_TPC2] = mmDCORE0_TPC2_EML_CFG_BASE,
+ [TPC_ID_DCORE0_TPC3] = mmDCORE0_TPC3_EML_CFG_BASE,
+ [TPC_ID_DCORE0_TPC4] = mmDCORE0_TPC4_EML_CFG_BASE,
+ [TPC_ID_DCORE0_TPC5] = mmDCORE0_TPC5_EML_CFG_BASE,
+ [TPC_ID_DCORE1_TPC0] = mmDCORE1_TPC0_EML_CFG_BASE,
+ [TPC_ID_DCORE1_TPC1] = mmDCORE1_TPC1_EML_CFG_BASE,
+ [TPC_ID_DCORE1_TPC2] = mmDCORE1_TPC2_EML_CFG_BASE,
+ [TPC_ID_DCORE1_TPC3] = mmDCORE1_TPC3_EML_CFG_BASE,
+ [TPC_ID_DCORE1_TPC4] = mmDCORE1_TPC4_EML_CFG_BASE,
+ [TPC_ID_DCORE1_TPC5] = mmDCORE1_TPC5_EML_CFG_BASE,
+ [TPC_ID_DCORE2_TPC0] = mmDCORE2_TPC0_EML_CFG_BASE,
+ [TPC_ID_DCORE2_TPC1] = mmDCORE2_TPC1_EML_CFG_BASE,
+ [TPC_ID_DCORE2_TPC2] = mmDCORE2_TPC2_EML_CFG_BASE,
+ [TPC_ID_DCORE2_TPC3] = mmDCORE2_TPC3_EML_CFG_BASE,
+ [TPC_ID_DCORE2_TPC4] = mmDCORE2_TPC4_EML_CFG_BASE,
+ [TPC_ID_DCORE2_TPC5] = mmDCORE2_TPC5_EML_CFG_BASE,
+ [TPC_ID_DCORE3_TPC0] = mmDCORE3_TPC0_EML_CFG_BASE,
+ [TPC_ID_DCORE3_TPC1] = mmDCORE3_TPC1_EML_CFG_BASE,
+ [TPC_ID_DCORE3_TPC2] = mmDCORE3_TPC2_EML_CFG_BASE,
+ [TPC_ID_DCORE3_TPC3] = mmDCORE3_TPC3_EML_CFG_BASE,
+ [TPC_ID_DCORE3_TPC4] = mmDCORE3_TPC4_EML_CFG_BASE,
+ [TPC_ID_DCORE3_TPC5] = mmDCORE3_TPC5_EML_CFG_BASE,
+ [TPC_ID_DCORE0_TPC6] = mmDCORE0_TPC6_EML_CFG_BASE,
+};
+
+const u32 gaudi2_rot_blocks_bases[ROTATOR_ID_SIZE] = {
+ [ROTATOR_ID_0] = mmROT0_BASE,
+ [ROTATOR_ID_1] = mmROT1_BASE
+};
+
+static const u32 gaudi2_tpc_id_to_queue_id[TPC_ID_SIZE] = {
+ [TPC_ID_DCORE0_TPC0] = GAUDI2_QUEUE_ID_DCORE0_TPC_0_0,
+ [TPC_ID_DCORE0_TPC1] = GAUDI2_QUEUE_ID_DCORE0_TPC_1_0,
+ [TPC_ID_DCORE0_TPC2] = GAUDI2_QUEUE_ID_DCORE0_TPC_2_0,
+ [TPC_ID_DCORE0_TPC3] = GAUDI2_QUEUE_ID_DCORE0_TPC_3_0,
+ [TPC_ID_DCORE0_TPC4] = GAUDI2_QUEUE_ID_DCORE0_TPC_4_0,
+ [TPC_ID_DCORE0_TPC5] = GAUDI2_QUEUE_ID_DCORE0_TPC_5_0,
+ [TPC_ID_DCORE1_TPC0] = GAUDI2_QUEUE_ID_DCORE1_TPC_0_0,
+ [TPC_ID_DCORE1_TPC1] = GAUDI2_QUEUE_ID_DCORE1_TPC_1_0,
+ [TPC_ID_DCORE1_TPC2] = GAUDI2_QUEUE_ID_DCORE1_TPC_2_0,
+ [TPC_ID_DCORE1_TPC3] = GAUDI2_QUEUE_ID_DCORE1_TPC_3_0,
+ [TPC_ID_DCORE1_TPC4] = GAUDI2_QUEUE_ID_DCORE1_TPC_4_0,
+ [TPC_ID_DCORE1_TPC5] = GAUDI2_QUEUE_ID_DCORE1_TPC_5_0,
+ [TPC_ID_DCORE2_TPC0] = GAUDI2_QUEUE_ID_DCORE2_TPC_0_0,
+ [TPC_ID_DCORE2_TPC1] = GAUDI2_QUEUE_ID_DCORE2_TPC_1_0,
+ [TPC_ID_DCORE2_TPC2] = GAUDI2_QUEUE_ID_DCORE2_TPC_2_0,
+ [TPC_ID_DCORE2_TPC3] = GAUDI2_QUEUE_ID_DCORE2_TPC_3_0,
+ [TPC_ID_DCORE2_TPC4] = GAUDI2_QUEUE_ID_DCORE2_TPC_4_0,
+ [TPC_ID_DCORE2_TPC5] = GAUDI2_QUEUE_ID_DCORE2_TPC_5_0,
+ [TPC_ID_DCORE3_TPC0] = GAUDI2_QUEUE_ID_DCORE3_TPC_0_0,
+ [TPC_ID_DCORE3_TPC1] = GAUDI2_QUEUE_ID_DCORE3_TPC_1_0,
+ [TPC_ID_DCORE3_TPC2] = GAUDI2_QUEUE_ID_DCORE3_TPC_2_0,
+ [TPC_ID_DCORE3_TPC3] = GAUDI2_QUEUE_ID_DCORE3_TPC_3_0,
+ [TPC_ID_DCORE3_TPC4] = GAUDI2_QUEUE_ID_DCORE3_TPC_4_0,
+ [TPC_ID_DCORE3_TPC5] = GAUDI2_QUEUE_ID_DCORE3_TPC_5_0,
+ [TPC_ID_DCORE0_TPC6] = GAUDI2_QUEUE_ID_DCORE0_TPC_6_0,
+};
+
+static const u32 gaudi2_rot_id_to_queue_id[ROTATOR_ID_SIZE] = {
+ [ROTATOR_ID_0] = GAUDI2_QUEUE_ID_ROT_0_0,
+ [ROTATOR_ID_1] = GAUDI2_QUEUE_ID_ROT_1_0,
+};
+
+static const u32 gaudi2_tpc_engine_id_to_tpc_id[] = {
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_0] = TPC_ID_DCORE0_TPC0,
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_1] = TPC_ID_DCORE0_TPC1,
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_2] = TPC_ID_DCORE0_TPC2,
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_3] = TPC_ID_DCORE0_TPC3,
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_4] = TPC_ID_DCORE0_TPC4,
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_5] = TPC_ID_DCORE0_TPC5,
+ [GAUDI2_DCORE1_ENGINE_ID_TPC_0] = TPC_ID_DCORE1_TPC0,
+ [GAUDI2_DCORE1_ENGINE_ID_TPC_1] = TPC_ID_DCORE1_TPC1,
+ [GAUDI2_DCORE1_ENGINE_ID_TPC_2] = TPC_ID_DCORE1_TPC2,
+ [GAUDI2_DCORE1_ENGINE_ID_TPC_3] = TPC_ID_DCORE1_TPC3,
+ [GAUDI2_DCORE1_ENGINE_ID_TPC_4] = TPC_ID_DCORE1_TPC4,
+ [GAUDI2_DCORE1_ENGINE_ID_TPC_5] = TPC_ID_DCORE1_TPC5,
+ [GAUDI2_DCORE2_ENGINE_ID_TPC_0] = TPC_ID_DCORE2_TPC0,
+ [GAUDI2_DCORE2_ENGINE_ID_TPC_1] = TPC_ID_DCORE2_TPC1,
+ [GAUDI2_DCORE2_ENGINE_ID_TPC_2] = TPC_ID_DCORE2_TPC2,
+ [GAUDI2_DCORE2_ENGINE_ID_TPC_3] = TPC_ID_DCORE2_TPC3,
+ [GAUDI2_DCORE2_ENGINE_ID_TPC_4] = TPC_ID_DCORE2_TPC4,
+ [GAUDI2_DCORE2_ENGINE_ID_TPC_5] = TPC_ID_DCORE2_TPC5,
+ [GAUDI2_DCORE3_ENGINE_ID_TPC_0] = TPC_ID_DCORE3_TPC0,
+ [GAUDI2_DCORE3_ENGINE_ID_TPC_1] = TPC_ID_DCORE3_TPC1,
+ [GAUDI2_DCORE3_ENGINE_ID_TPC_2] = TPC_ID_DCORE3_TPC2,
+ [GAUDI2_DCORE3_ENGINE_ID_TPC_3] = TPC_ID_DCORE3_TPC3,
+ [GAUDI2_DCORE3_ENGINE_ID_TPC_4] = TPC_ID_DCORE3_TPC4,
+ [GAUDI2_DCORE3_ENGINE_ID_TPC_5] = TPC_ID_DCORE3_TPC5,
+ /* the PCI TPC is placed last (mapped liked HW) */
+ [GAUDI2_DCORE0_ENGINE_ID_TPC_6] = TPC_ID_DCORE0_TPC6,
+};
+
+static const u32 gaudi2_mme_engine_id_to_mme_id[] = {
+ [GAUDI2_DCORE0_ENGINE_ID_MME] = MME_ID_DCORE0,
+ [GAUDI2_DCORE1_ENGINE_ID_MME] = MME_ID_DCORE1,
+ [GAUDI2_DCORE2_ENGINE_ID_MME] = MME_ID_DCORE2,
+ [GAUDI2_DCORE3_ENGINE_ID_MME] = MME_ID_DCORE3,
+};
+
+static const u32 gaudi2_edma_engine_id_to_edma_id[] = {
+ [GAUDI2_ENGINE_ID_PDMA_0] = DMA_CORE_ID_PDMA0,
+ [GAUDI2_ENGINE_ID_PDMA_1] = DMA_CORE_ID_PDMA1,
+ [GAUDI2_DCORE0_ENGINE_ID_EDMA_0] = DMA_CORE_ID_EDMA0,
+ [GAUDI2_DCORE0_ENGINE_ID_EDMA_1] = DMA_CORE_ID_EDMA1,
+ [GAUDI2_DCORE1_ENGINE_ID_EDMA_0] = DMA_CORE_ID_EDMA2,
+ [GAUDI2_DCORE1_ENGINE_ID_EDMA_1] = DMA_CORE_ID_EDMA3,
+ [GAUDI2_DCORE2_ENGINE_ID_EDMA_0] = DMA_CORE_ID_EDMA4,
+ [GAUDI2_DCORE2_ENGINE_ID_EDMA_1] = DMA_CORE_ID_EDMA5,
+ [GAUDI2_DCORE3_ENGINE_ID_EDMA_0] = DMA_CORE_ID_EDMA6,
+ [GAUDI2_DCORE3_ENGINE_ID_EDMA_1] = DMA_CORE_ID_EDMA7,
+ [GAUDI2_ENGINE_ID_KDMA] = DMA_CORE_ID_KDMA,
+};
+
+const u32 edma_stream_base[NUM_OF_EDMA_PER_DCORE * NUM_OF_DCORES] = {
+ GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0,
+ GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0,
+ GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0,
+ GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0,
+};
+
+static const char gaudi2_vdec_irq_name[GAUDI2_VDEC_MSIX_ENTRIES][GAUDI2_MAX_STRING_LEN] = {
+ "gaudi2 vdec 0_0", "gaudi2 vdec 0_0 abnormal",
+ "gaudi2 vdec 0_1", "gaudi2 vdec 0_1 abnormal",
+ "gaudi2 vdec 1_0", "gaudi2 vdec 1_0 abnormal",
+ "gaudi2 vdec 1_1", "gaudi2 vdec 1_1 abnormal",
+ "gaudi2 vdec 2_0", "gaudi2 vdec 2_0 abnormal",
+ "gaudi2 vdec 2_1", "gaudi2 vdec 2_1 abnormal",
+ "gaudi2 vdec 3_0", "gaudi2 vdec 3_0 abnormal",
+ "gaudi2 vdec 3_1", "gaudi2 vdec 3_1 abnormal",
+ "gaudi2 vdec s_0", "gaudi2 vdec s_0 abnormal",
+ "gaudi2 vdec s_1", "gaudi2 vdec s_1 abnormal"
+};
+
+enum rtr_id {
+ DCORE0_RTR0,
+ DCORE0_RTR1,
+ DCORE0_RTR2,
+ DCORE0_RTR3,
+ DCORE0_RTR4,
+ DCORE0_RTR5,
+ DCORE0_RTR6,
+ DCORE0_RTR7,
+ DCORE1_RTR0,
+ DCORE1_RTR1,
+ DCORE1_RTR2,
+ DCORE1_RTR3,
+ DCORE1_RTR4,
+ DCORE1_RTR5,
+ DCORE1_RTR6,
+ DCORE1_RTR7,
+ DCORE2_RTR0,
+ DCORE2_RTR1,
+ DCORE2_RTR2,
+ DCORE2_RTR3,
+ DCORE2_RTR4,
+ DCORE2_RTR5,
+ DCORE2_RTR6,
+ DCORE2_RTR7,
+ DCORE3_RTR0,
+ DCORE3_RTR1,
+ DCORE3_RTR2,
+ DCORE3_RTR3,
+ DCORE3_RTR4,
+ DCORE3_RTR5,
+ DCORE3_RTR6,
+ DCORE3_RTR7,
+};
+
+static const u32 gaudi2_tpc_initiator_hbw_rtr_id[NUM_OF_TPC_PER_DCORE * NUM_OF_DCORES + 1] = {
+ DCORE0_RTR1, DCORE0_RTR1, DCORE0_RTR2, DCORE0_RTR2, DCORE0_RTR3, DCORE0_RTR3,
+ DCORE1_RTR6, DCORE1_RTR6, DCORE1_RTR5, DCORE1_RTR5, DCORE1_RTR4, DCORE1_RTR4,
+ DCORE2_RTR3, DCORE2_RTR3, DCORE2_RTR2, DCORE2_RTR2, DCORE2_RTR1, DCORE2_RTR1,
+ DCORE3_RTR4, DCORE3_RTR4, DCORE3_RTR5, DCORE3_RTR5, DCORE3_RTR6, DCORE3_RTR6,
+ DCORE0_RTR0
+};
+
+static const u32 gaudi2_tpc_initiator_lbw_rtr_id[NUM_OF_TPC_PER_DCORE * NUM_OF_DCORES + 1] = {
+ DCORE0_RTR1, DCORE0_RTR1, DCORE0_RTR1, DCORE0_RTR1, DCORE0_RTR2, DCORE0_RTR2,
+ DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR6, DCORE1_RTR6, DCORE1_RTR5, DCORE1_RTR5,
+ DCORE2_RTR2, DCORE2_RTR2, DCORE2_RTR1, DCORE2_RTR1, DCORE2_RTR0, DCORE2_RTR0,
+ DCORE3_RTR5, DCORE3_RTR5, DCORE3_RTR6, DCORE3_RTR6, DCORE3_RTR7, DCORE3_RTR7,
+ DCORE0_RTR0
+};
+
+static const u32 gaudi2_dec_initiator_hbw_rtr_id[NUMBER_OF_DEC] = {
+ DCORE0_RTR0, DCORE0_RTR0, DCORE1_RTR7, DCORE1_RTR7, DCORE2_RTR0, DCORE2_RTR0,
+ DCORE3_RTR7, DCORE3_RTR7, DCORE0_RTR0, DCORE0_RTR0
+};
+
+static const u32 gaudi2_dec_initiator_lbw_rtr_id[NUMBER_OF_DEC] = {
+ DCORE0_RTR1, DCORE0_RTR1, DCORE1_RTR6, DCORE1_RTR6, DCORE2_RTR1, DCORE2_RTR1,
+ DCORE3_RTR6, DCORE3_RTR6, DCORE0_RTR0, DCORE0_RTR0
+};
+
+static const u32 gaudi2_nic_initiator_hbw_rtr_id[NIC_NUMBER_OF_MACROS] = {
+ DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE2_RTR0,
+ DCORE2_RTR0, DCORE2_RTR0, DCORE2_RTR0, DCORE3_RTR7, DCORE3_RTR7, DCORE3_RTR7
+};
+
+static const u32 gaudi2_nic_initiator_lbw_rtr_id[NIC_NUMBER_OF_MACROS] = {
+ DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE2_RTR0,
+ DCORE2_RTR0, DCORE2_RTR0, DCORE2_RTR0, DCORE3_RTR7, DCORE3_RTR7, DCORE3_RTR7
+};
+
+static const u32 gaudi2_edma_initiator_hbw_sft[NUM_OF_EDMA_PER_DCORE * NUM_OF_DCORES] = {
+ mmSFT0_HBW_RTR_IF1_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT0_HBW_RTR_IF0_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT1_HBW_RTR_IF1_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT1_HBW_RTR_IF0_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT2_HBW_RTR_IF0_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT2_HBW_RTR_IF1_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT3_HBW_RTR_IF0_MSTR_IF_RR_SHRD_HBW_BASE,
+ mmSFT3_HBW_RTR_IF1_MSTR_IF_RR_SHRD_HBW_BASE
+};
+
+static const u32 gaudi2_pdma_initiator_hbw_rtr_id[NUM_OF_PDMA] = {
+ DCORE0_RTR0, DCORE0_RTR0
+};
+
+static const u32 gaudi2_pdma_initiator_lbw_rtr_id[NUM_OF_PDMA] = {
+ DCORE0_RTR2, DCORE0_RTR2
+};
+
+static const u32 gaudi2_rot_initiator_hbw_rtr_id[NUM_OF_ROT] = {
+ DCORE2_RTR0, DCORE3_RTR7
+};
+
+static const u32 gaudi2_rot_initiator_lbw_rtr_id[NUM_OF_ROT] = {
+ DCORE2_RTR2, DCORE3_RTR5
+};
+
+struct mme_initiators_rtr_id {
+ u32 wap0;
+ u32 wap1;
+ u32 write;
+ u32 read;
+ u32 sbte0;
+ u32 sbte1;
+ u32 sbte2;
+ u32 sbte3;
+ u32 sbte4;
+};
+
+enum mme_initiators {
+ MME_WAP0 = 0,
+ MME_WAP1,
+ MME_WRITE,
+ MME_READ,
+ MME_SBTE0,
+ MME_SBTE1,
+ MME_SBTE2,
+ MME_SBTE3,
+ MME_SBTE4,
+ MME_INITIATORS_MAX
+};
+
+static const struct mme_initiators_rtr_id
+gaudi2_mme_initiator_rtr_id[NUM_OF_MME_PER_DCORE * NUM_OF_DCORES] = {
+ { .wap0 = 5, .wap1 = 7, .write = 6, .read = 7,
+ .sbte0 = 7, .sbte1 = 4, .sbte2 = 4, .sbte3 = 5, .sbte4 = 6},
+ { .wap0 = 10, .wap1 = 8, .write = 9, .read = 8,
+ .sbte0 = 11, .sbte1 = 11, .sbte2 = 10, .sbte3 = 9, .sbte4 = 8},
+ { .wap0 = 21, .wap1 = 23, .write = 22, .read = 23,
+ .sbte0 = 20, .sbte1 = 20, .sbte2 = 21, .sbte3 = 22, .sbte4 = 23},
+ { .wap0 = 30, .wap1 = 28, .write = 29, .read = 30,
+ .sbte0 = 31, .sbte1 = 31, .sbte2 = 30, .sbte3 = 29, .sbte4 = 28},
+};
+
+enum razwi_event_sources {
+ RAZWI_TPC,
+ RAZWI_MME,
+ RAZWI_EDMA,
+ RAZWI_PDMA,
+ RAZWI_NIC,
+ RAZWI_DEC,
+ RAZWI_ROT,
+ RAZWI_ARC_FARM
+};
+
+struct hbm_mc_error_causes {
+ u32 mask;
+ char cause[50];
+};
+
+static struct hl_special_block_info gaudi2_special_blocks[] = GAUDI2_SPECIAL_BLOCKS;
+
+/* Special blocks iterator is currently used to configure security protection bits,
+ * and read global errors. Most HW blocks are addressable and those who aren't (N/A)-
+ * must be skipped. Following configurations are commonly used for both PB config
+ * and global error reading, since currently they both share the same settings.
+ * Once it changes, we must remember to use separate configurations for either one.
+ */
+static int gaudi2_iterator_skip_block_types[] = {
+ GAUDI2_BLOCK_TYPE_PLL,
+ GAUDI2_BLOCK_TYPE_EU_BIST,
+ GAUDI2_BLOCK_TYPE_HBM,
+ GAUDI2_BLOCK_TYPE_XFT
+};
+
+static struct range gaudi2_iterator_skip_block_ranges[] = {
+ /* Skip all PSOC blocks except for PSOC_GLOBAL_CONF */
+ {mmPSOC_I2C_M0_BASE, mmPSOC_EFUSE_BASE},
+ {mmPSOC_BTL_BASE, mmPSOC_MSTR_IF_RR_SHRD_HBW_BASE},
+ /* Skip all CPU blocks except for CPU_IF */
+ {mmCPU_CA53_CFG_BASE, mmCPU_CA53_CFG_BASE},
+ {mmCPU_TIMESTAMP_BASE, mmCPU_MSTR_IF_RR_SHRD_HBW_BASE}
+};
+
+static struct hbm_mc_error_causes hbm_mc_spi[GAUDI2_NUM_OF_HBM_MC_SPI_CAUSE] = {
+ {HBM_MC_SPI_TEMP_PIN_CHG_MASK, "temperature pins changed"},
+ {HBM_MC_SPI_THR_ENG_MASK, "temperature-based throttling engaged"},
+ {HBM_MC_SPI_THR_DIS_ENG_MASK, "temperature-based throttling disengaged"},
+ {HBM_MC_SPI_IEEE1500_COMP_MASK, "IEEE1500 op comp"},
+ {HBM_MC_SPI_IEEE1500_PAUSED_MASK, "IEEE1500 op paused"},
+};
+
+static const char * const hbm_mc_sei_cause[GAUDI2_NUM_OF_HBM_SEI_CAUSE] = {
+ [HBM_SEI_CMD_PARITY_EVEN] = "SEI C/A parity even",
+ [HBM_SEI_CMD_PARITY_ODD] = "SEI C/A parity odd",
+ [HBM_SEI_READ_ERR] = "SEI read data error",
+ [HBM_SEI_WRITE_DATA_PARITY_ERR] = "SEI write data parity error",
+ [HBM_SEI_CATTRIP] = "SEI CATTRIP asserted",
+ [HBM_SEI_MEM_BIST_FAIL] = "SEI memory BIST fail",
+ [HBM_SEI_DFI] = "SEI DFI error",
+ [HBM_SEI_INV_TEMP_READ_OUT] = "SEI invalid temp read",
+ [HBM_SEI_BIST_FAIL] = "SEI BIST fail"
+};
+
+struct mmu_spi_sei_cause {
+ char cause[50];
+ int clear_bit;
+};
+
+static const struct mmu_spi_sei_cause gaudi2_mmu_spi_sei[GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE] = {
+ {"page fault", 1}, /* INTERRUPT_CLR[1] */
+ {"page access", 1}, /* INTERRUPT_CLR[1] */
+ {"bypass ddr", 2}, /* INTERRUPT_CLR[2] */
+ {"multi hit", 2}, /* INTERRUPT_CLR[2] */
+ {"mmu rei0", -1}, /* no clear register bit */
+ {"mmu rei1", -1}, /* no clear register bit */
+ {"stlb rei0", -1}, /* no clear register bit */
+ {"stlb rei1", -1}, /* no clear register bit */
+ {"rr privileged write hit", 2}, /* INTERRUPT_CLR[2] */
+ {"rr privileged read hit", 2}, /* INTERRUPT_CLR[2] */
+ {"rr secure write hit", 2}, /* INTERRUPT_CLR[2] */
+ {"rr secure read hit", 2}, /* INTERRUPT_CLR[2] */
+ {"bist_fail no use", 2}, /* INTERRUPT_CLR[2] */
+ {"bist_fail no use", 2}, /* INTERRUPT_CLR[2] */
+ {"bist_fail no use", 2}, /* INTERRUPT_CLR[2] */
+ {"bist_fail no use", 2}, /* INTERRUPT_CLR[2] */
+ {"slave error", 16}, /* INTERRUPT_CLR[16] */
+ {"dec error", 17}, /* INTERRUPT_CLR[17] */
+ {"burst fifo full", 2} /* INTERRUPT_CLR[2] */
+};
+
+struct gaudi2_cache_invld_params {
+ u64 start_va;
+ u64 end_va;
+ u32 inv_start_val;
+ u32 flags;
+ bool range_invalidation;
+};
+
+struct gaudi2_tpc_idle_data {
+ struct engines_data *e;
+ unsigned long *mask;
+ bool *is_idle;
+ const char *tpc_fmt;
+};
+
+struct gaudi2_tpc_mmu_data {
+ u32 rw_asid;
+};
+
+static s64 gaudi2_state_dump_specs_props[SP_MAX] = {0};
+
+static int gaudi2_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size, u64 val);
+static bool gaudi2_is_queue_enabled(struct hl_device *hdev, u32 hw_queue_id);
+static bool gaudi2_is_arc_enabled(struct hl_device *hdev, u64 arc_id);
+static void gaudi2_clr_arc_id_cap(struct hl_device *hdev, u64 arc_id);
+static void gaudi2_set_arc_id_cap(struct hl_device *hdev, u64 arc_id);
+static void gaudi2_memset_device_lbw(struct hl_device *hdev, u32 addr, u32 size, u32 val);
+static int gaudi2_send_job_to_kdma(struct hl_device *hdev, u64 src_addr, u64 dst_addr, u32 size,
+ bool is_memset);
+static bool gaudi2_get_tpc_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e);
+static bool gaudi2_get_mme_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e);
+static bool gaudi2_get_edma_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e);
+static u64 gaudi2_mmu_scramble_addr(struct hl_device *hdev, u64 raw_addr);
+static u64 gaudi2_mmu_descramble_addr(struct hl_device *hdev, u64 scrambled_addr);
+
+static void gaudi2_init_scrambler_hbm(struct hl_device *hdev)
+{
+
+}
+
+static u32 gaudi2_get_signal_cb_size(struct hl_device *hdev)
+{
+ return sizeof(struct packet_msg_short);
+}
+
+static u32 gaudi2_get_wait_cb_size(struct hl_device *hdev)
+{
+ return sizeof(struct packet_msg_short) * 4 + sizeof(struct packet_fence);
+}
+
+void gaudi2_iterate_tpcs(struct hl_device *hdev, struct iterate_module_ctx *ctx)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int dcore, inst, tpc_seq;
+ u32 offset;
+
+ /* init the return code */
+ ctx->rc = 0;
+
+ for (dcore = 0; dcore < NUM_OF_DCORES; dcore++) {
+ for (inst = 0; inst < NUM_OF_TPC_PER_DCORE; inst++) {
+ tpc_seq = dcore * NUM_OF_TPC_PER_DCORE + inst;
+
+ if (!(prop->tpc_enabled_mask & BIT(tpc_seq)))
+ continue;
+
+ offset = (DCORE_OFFSET * dcore) + (DCORE_TPC_OFFSET * inst);
+
+ ctx->fn(hdev, dcore, inst, offset, ctx);
+ if (ctx->rc) {
+ dev_err(hdev->dev, "TPC iterator failed for DCORE%d TPC%d\n",
+ dcore, inst);
+ return;
+ }
+ }
+ }
+
+ if (!(prop->tpc_enabled_mask & BIT(TPC_ID_DCORE0_TPC6)))
+ return;
+
+ /* special check for PCI TPC (DCORE0_TPC6) */
+ offset = DCORE_TPC_OFFSET * (NUM_DCORE0_TPC - 1);
+ ctx->fn(hdev, 0, NUM_DCORE0_TPC - 1, offset, ctx);
+ if (ctx->rc)
+ dev_err(hdev->dev, "TPC iterator failed for DCORE0 TPC6\n");
+}
+
+static bool gaudi2_host_phys_addr_valid(u64 addr)
+{
+ if ((addr < HOST_PHYS_BASE_0 + HOST_PHYS_SIZE_0) || (addr >= HOST_PHYS_BASE_1))
+ return true;
+
+ return false;
+}
+
+static int set_number_of_functional_hbms(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u8 faulty_hbms = hweight64(hdev->dram_binning);
+
+ /* check if all HBMs should be used */
+ if (!faulty_hbms) {
+ dev_dbg(hdev->dev, "All HBM are in use (no binning)\n");
+ prop->num_functional_hbms = GAUDI2_HBM_NUM;
+ return 0;
+ }
+
+ /*
+ * check for error condition in which number of binning
+ * candidates is higher than the maximum supported by the
+ * driver (in which case binning mask shall be ignored and driver will
+ * set the default)
+ */
+ if (faulty_hbms > MAX_FAULTY_HBMS) {
+ dev_err(hdev->dev,
+ "HBM binning supports max of %d faulty HBMs, supplied mask 0x%llx.\n",
+ MAX_FAULTY_HBMS, hdev->dram_binning);
+ return -EINVAL;
+ }
+
+ /*
+ * by default, number of functional HBMs in Gaudi2 is always
+ * GAUDI2_HBM_NUM - 1.
+ */
+ prop->num_functional_hbms = GAUDI2_HBM_NUM - faulty_hbms;
+ return 0;
+}
+
+static bool gaudi2_is_edma_queue_id(u32 queue_id)
+{
+
+ switch (queue_id) {
+ case GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3:
+ case GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3:
+ case GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3:
+ case GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int gaudi2_set_dram_properties(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 hbm_drv_base_offset = 0, edma_pq_base_addr;
+ u32 basic_hbm_page_size, edma_idx = 0;
+ int rc, i;
+
+ rc = set_number_of_functional_hbms(hdev);
+ if (rc)
+ return -EINVAL;
+
+ /*
+ * Due to HW bug in which TLB size is x16 smaller than expected we use a workaround
+ * in which we are using x16 bigger page size to be able to populate the entire
+ * HBM mappings in the TLB
+ */
+ basic_hbm_page_size = prop->num_functional_hbms * SZ_8M;
+ prop->dram_page_size = GAUDI2_COMPENSATE_TLB_PAGE_SIZE_FACTOR * basic_hbm_page_size;
+ prop->device_mem_alloc_default_page_size = prop->dram_page_size;
+ prop->dram_size = prop->num_functional_hbms * SZ_16G;
+ prop->dram_base_address = DRAM_PHYS_BASE;
+ prop->dram_end_address = prop->dram_base_address + prop->dram_size;
+ prop->dram_supports_virtual_memory = true;
+
+ prop->dram_user_base_address = DRAM_PHYS_BASE + prop->dram_page_size;
+ prop->dram_hints_align_mask = ~GAUDI2_HBM_MMU_SCRM_ADDRESS_MASK;
+ prop->hints_dram_reserved_va_range.start_addr = RESERVED_VA_RANGE_FOR_ARC_ON_HBM_START;
+ prop->hints_dram_reserved_va_range.end_addr = RESERVED_VA_RANGE_FOR_ARC_ON_HBM_END;
+
+ /* since DRAM page size differs from DMMU page size we need to allocate
+ * DRAM memory in units of dram_page size and mapping this memory in
+ * units of DMMU page size. we overcome this size mismatch using a
+ * scrambling routine which takes a DRAM page and converts it to a DMMU
+ * page.
+ * We therefore:
+ * 1. partition the virtual address space to DRAM-page (whole) pages.
+ * (suppose we get n such pages)
+ * 2. limit the amount of virtual address space we got from 1 above to
+ * a multiple of 64M as we don't want the scrambled address to cross
+ * the DRAM virtual address space.
+ * ( m = (n * DRAM_page_size) / DMMU_page_size).
+ * 3. determine the and address accordingly
+ * end_addr = start_addr + m * 48M
+ *
+ * the DRAM address MSBs (63:48) are not part of the roundup calculation
+ */
+ prop->dmmu.start_addr = prop->dram_base_address +
+ (prop->dram_page_size *
+ DIV_ROUND_UP_SECTOR_T(prop->dram_size, prop->dram_page_size));
+ prop->dmmu.end_addr = prop->dmmu.start_addr + prop->dram_page_size *
+ div_u64((VA_HBM_SPACE_END - prop->dmmu.start_addr), prop->dmmu.page_size);
+ /*
+ * Driver can't share an (48MB) HBM page with the F/W in order to prevent FW to block
+ * the driver part by range register, so it must start at the next (48MB) page
+ */
+ hbm_drv_base_offset = roundup(CPU_FW_IMAGE_SIZE, prop->num_functional_hbms * SZ_8M);
+
+ /*
+ * The NIC driver section size and the HMMU page tables section in the HBM needs
+ * to be the remaining size in the first dram page after taking into
+ * account the F/W image size
+ */
+
+ /* Reserve region in HBM for HMMU page tables */
+ prop->mmu_pgt_addr = DRAM_PHYS_BASE + hbm_drv_base_offset +
+ ((prop->dram_page_size - hbm_drv_base_offset) -
+ (HMMU_PAGE_TABLES_SIZE + EDMA_PQS_SIZE + EDMA_SCRATCHPAD_SIZE));
+
+ /* Set EDMA PQs HBM addresses */
+ edma_pq_base_addr = prop->mmu_pgt_addr + HMMU_PAGE_TABLES_SIZE;
+
+ for (i = 0 ; i < GAUDI2_QUEUE_ID_CPU_PQ ; i++) {
+ if (gaudi2_is_edma_queue_id(i)) {
+ prop->hw_queues_props[i].q_dram_bd_address = edma_pq_base_addr +
+ (edma_idx * HL_QUEUE_SIZE_IN_BYTES);
+ edma_idx++;
+ }
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_fixed_properties(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hw_queue_properties *q_props;
+ u32 num_sync_stream_queues = 0;
+ int i, rc;
+
+ prop->max_queues = GAUDI2_QUEUE_ID_SIZE;
+ prop->hw_queues_props = kcalloc(prop->max_queues, sizeof(struct hw_queue_properties),
+ GFP_KERNEL);
+
+ if (!prop->hw_queues_props)
+ return -ENOMEM;
+
+ q_props = prop->hw_queues_props;
+
+ for (i = 0 ; i < GAUDI2_QUEUE_ID_CPU_PQ ; i++) {
+ q_props[i].type = QUEUE_TYPE_HW;
+ q_props[i].driver_only = 0;
+
+ if (i >= GAUDI2_QUEUE_ID_NIC_0_0 && i <= GAUDI2_QUEUE_ID_NIC_23_3) {
+ q_props[i].supports_sync_stream = 0;
+ } else {
+ q_props[i].supports_sync_stream = 1;
+ num_sync_stream_queues++;
+ }
+
+ q_props[i].cb_alloc_flags = CB_ALLOC_USER;
+
+ if (gaudi2_is_edma_queue_id(i))
+ q_props[i].dram_bd = 1;
+ }
+
+ q_props[GAUDI2_QUEUE_ID_CPU_PQ].type = QUEUE_TYPE_CPU;
+ q_props[GAUDI2_QUEUE_ID_CPU_PQ].driver_only = 1;
+ q_props[GAUDI2_QUEUE_ID_CPU_PQ].cb_alloc_flags = CB_ALLOC_KERNEL;
+
+ prop->cache_line_size = DEVICE_CACHE_LINE_SIZE;
+ prop->cfg_base_address = CFG_BASE;
+ prop->device_dma_offset_for_host_access = HOST_PHYS_BASE_0;
+ prop->host_base_address = HOST_PHYS_BASE_0;
+ prop->host_end_address = prop->host_base_address + HOST_PHYS_SIZE_0;
+ prop->max_pending_cs = GAUDI2_MAX_PENDING_CS;
+ prop->completion_queues_count = GAUDI2_RESERVED_CQ_NUMBER;
+ prop->user_dec_intr_count = NUMBER_OF_DEC;
+ prop->user_interrupt_count = GAUDI2_IRQ_NUM_USER_LAST - GAUDI2_IRQ_NUM_USER_FIRST + 1;
+ prop->completion_mode = HL_COMPLETION_MODE_CS;
+ prop->sync_stream_first_sob = GAUDI2_RESERVED_SOB_NUMBER;
+ prop->sync_stream_first_mon = GAUDI2_RESERVED_MON_NUMBER;
+
+ prop->sram_base_address = SRAM_BASE_ADDR;
+ prop->sram_size = SRAM_SIZE;
+ prop->sram_end_address = prop->sram_base_address + prop->sram_size;
+ prop->sram_user_base_address = prop->sram_base_address + SRAM_USER_BASE_OFFSET;
+
+ prop->hints_range_reservation = true;
+
+ prop->rotator_enabled_mask = BIT(NUM_OF_ROT) - 1;
+
+ prop->max_asid = 2;
+
+ prop->dmmu.pgt_size = HMMU_PAGE_TABLES_SIZE;
+ prop->mmu_pte_size = HL_PTE_SIZE;
+
+ prop->dmmu.hop_shifts[MMU_HOP0] = DHOP0_SHIFT;
+ prop->dmmu.hop_shifts[MMU_HOP1] = DHOP1_SHIFT;
+ prop->dmmu.hop_shifts[MMU_HOP2] = DHOP2_SHIFT;
+ prop->dmmu.hop_shifts[MMU_HOP3] = DHOP3_SHIFT;
+ prop->dmmu.hop_masks[MMU_HOP0] = DHOP0_MASK;
+ prop->dmmu.hop_masks[MMU_HOP1] = DHOP1_MASK;
+ prop->dmmu.hop_masks[MMU_HOP2] = DHOP2_MASK;
+ prop->dmmu.hop_masks[MMU_HOP3] = DHOP3_MASK;
+ prop->dmmu.page_size = PAGE_SIZE_1GB;
+ prop->dmmu.num_hops = MMU_ARCH_4_HOPS;
+ prop->dmmu.last_mask = LAST_MASK;
+ prop->dmmu.host_resident = 0;
+ prop->dmmu.hop_table_size = HOP_TABLE_SIZE_512_PTE;
+ prop->dmmu.hop0_tables_total_size = HOP_TABLE_SIZE_512_PTE * prop->max_asid;
+
+ /* As we need to set the pgt address in dram for HMMU init so we cannot
+ * wait to the fw cpucp info to set the dram props as mmu init comes before
+ * hw init
+ */
+ rc = hdev->asic_funcs->set_dram_properties(hdev);
+ if (rc)
+ goto free_qprops;
+
+ prop->mmu_pgt_size = PMMU_PAGE_TABLES_SIZE;
+
+ prop->pmmu.pgt_size = prop->mmu_pgt_size;
+ hdev->pmmu_huge_range = true;
+ prop->pmmu.host_resident = 1;
+ prop->pmmu.num_hops = MMU_ARCH_6_HOPS;
+ prop->pmmu.last_mask = LAST_MASK;
+ prop->pmmu.hop_table_size = HOP_TABLE_SIZE_512_PTE;
+ prop->pmmu.hop0_tables_total_size = HOP_TABLE_SIZE_512_PTE * prop->max_asid;
+
+ prop->hints_host_reserved_va_range.start_addr = RESERVED_VA_FOR_VIRTUAL_MSIX_DOORBELL_START;
+ prop->hints_host_reserved_va_range.end_addr = RESERVED_VA_RANGE_FOR_ARC_ON_HOST_END;
+ prop->hints_host_hpage_reserved_va_range.start_addr =
+ RESERVED_VA_RANGE_FOR_ARC_ON_HOST_HPAGE_START;
+ prop->hints_host_hpage_reserved_va_range.end_addr =
+ RESERVED_VA_RANGE_FOR_ARC_ON_HOST_HPAGE_END;
+
+ if (PAGE_SIZE == SZ_64K) {
+ prop->pmmu.hop_shifts[MMU_HOP0] = HOP0_SHIFT_64K;
+ prop->pmmu.hop_shifts[MMU_HOP1] = HOP1_SHIFT_64K;
+ prop->pmmu.hop_shifts[MMU_HOP2] = HOP2_SHIFT_64K;
+ prop->pmmu.hop_shifts[MMU_HOP3] = HOP3_SHIFT_64K;
+ prop->pmmu.hop_shifts[MMU_HOP4] = HOP4_SHIFT_64K;
+ prop->pmmu.hop_shifts[MMU_HOP5] = HOP5_SHIFT_64K;
+ prop->pmmu.hop_masks[MMU_HOP0] = HOP0_MASK_64K;
+ prop->pmmu.hop_masks[MMU_HOP1] = HOP1_MASK_64K;
+ prop->pmmu.hop_masks[MMU_HOP2] = HOP2_MASK_64K;
+ prop->pmmu.hop_masks[MMU_HOP3] = HOP3_MASK_64K;
+ prop->pmmu.hop_masks[MMU_HOP4] = HOP4_MASK_64K;
+ prop->pmmu.hop_masks[MMU_HOP5] = HOP5_MASK_64K;
+ prop->pmmu.start_addr = VA_HOST_SPACE_PAGE_START;
+ prop->pmmu.end_addr = VA_HOST_SPACE_PAGE_END;
+ prop->pmmu.page_size = PAGE_SIZE_64KB;
+
+ /* shifts and masks are the same in PMMU and HPMMU */
+ memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu));
+ prop->pmmu_huge.page_size = PAGE_SIZE_16MB;
+ prop->pmmu_huge.start_addr = VA_HOST_SPACE_HPAGE_START;
+ prop->pmmu_huge.end_addr = VA_HOST_SPACE_HPAGE_END;
+ } else {
+ prop->pmmu.hop_shifts[MMU_HOP0] = HOP0_SHIFT_4K;
+ prop->pmmu.hop_shifts[MMU_HOP1] = HOP1_SHIFT_4K;
+ prop->pmmu.hop_shifts[MMU_HOP2] = HOP2_SHIFT_4K;
+ prop->pmmu.hop_shifts[MMU_HOP3] = HOP3_SHIFT_4K;
+ prop->pmmu.hop_shifts[MMU_HOP4] = HOP4_SHIFT_4K;
+ prop->pmmu.hop_shifts[MMU_HOP5] = HOP5_SHIFT_4K;
+ prop->pmmu.hop_masks[MMU_HOP0] = HOP0_MASK_4K;
+ prop->pmmu.hop_masks[MMU_HOP1] = HOP1_MASK_4K;
+ prop->pmmu.hop_masks[MMU_HOP2] = HOP2_MASK_4K;
+ prop->pmmu.hop_masks[MMU_HOP3] = HOP3_MASK_4K;
+ prop->pmmu.hop_masks[MMU_HOP4] = HOP4_MASK_4K;
+ prop->pmmu.hop_masks[MMU_HOP5] = HOP5_MASK_4K;
+ prop->pmmu.start_addr = VA_HOST_SPACE_PAGE_START;
+ prop->pmmu.end_addr = VA_HOST_SPACE_PAGE_END;
+ prop->pmmu.page_size = PAGE_SIZE_4KB;
+
+ /* shifts and masks are the same in PMMU and HPMMU */
+ memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu));
+ prop->pmmu_huge.page_size = PAGE_SIZE_2MB;
+ prop->pmmu_huge.start_addr = VA_HOST_SPACE_HPAGE_START;
+ prop->pmmu_huge.end_addr = VA_HOST_SPACE_HPAGE_END;
+ }
+
+ prop->max_num_of_engines = GAUDI2_ENGINE_ID_SIZE;
+ prop->num_engine_cores = CPU_ID_MAX;
+ prop->cfg_size = CFG_SIZE;
+ prop->num_of_events = GAUDI2_EVENT_SIZE;
+
+ prop->supports_engine_modes = true;
+
+ prop->dc_power_default = DC_POWER_DEFAULT;
+
+ prop->cb_pool_cb_cnt = GAUDI2_CB_POOL_CB_CNT;
+ prop->cb_pool_cb_size = GAUDI2_CB_POOL_CB_SIZE;
+ prop->pcie_dbi_base_address = CFG_BASE + mmPCIE_DBI_BASE;
+ prop->pcie_aux_dbi_reg_addr = CFG_BASE + mmPCIE_AUX_DBI;
+
+ strscpy_pad(prop->cpucp_info.card_name, GAUDI2_DEFAULT_CARD_NAME, CARD_NAME_MAX_LEN);
+
+ prop->mme_master_slave_mode = 1;
+
+ prop->first_available_user_sob[0] = GAUDI2_RESERVED_SOB_NUMBER +
+ (num_sync_stream_queues * HL_RSVD_SOBS);
+
+ prop->first_available_user_mon[0] = GAUDI2_RESERVED_MON_NUMBER +
+ (num_sync_stream_queues * HL_RSVD_MONS);
+
+ prop->first_available_user_interrupt = GAUDI2_IRQ_NUM_USER_FIRST;
+ prop->tpc_interrupt_id = GAUDI2_IRQ_NUM_TPC_ASSERT;
+ prop->eq_interrupt_id = GAUDI2_IRQ_NUM_EVENT_QUEUE;
+
+ prop->first_available_cq[0] = GAUDI2_RESERVED_CQ_NUMBER;
+
+ prop->fw_cpu_boot_dev_sts0_valid = false;
+ prop->fw_cpu_boot_dev_sts1_valid = false;
+ prop->hard_reset_done_by_fw = false;
+ prop->gic_interrupts_enable = true;
+
+ prop->server_type = HL_SERVER_TYPE_UNKNOWN;
+
+ prop->max_dec = NUMBER_OF_DEC;
+
+ prop->clk_pll_index = HL_GAUDI2_MME_PLL;
+
+ prop->dma_mask = 64;
+
+ prop->hbw_flush_reg = mmPCIE_WRAP_SPECIAL_GLBL_SPARE_0;
+
+ prop->supports_advanced_cpucp_rc = true;
+
+ return 0;
+
+free_qprops:
+ kfree(prop->hw_queues_props);
+ return rc;
+}
+
+static int gaudi2_pci_bars_map(struct hl_device *hdev)
+{
+ static const char * const name[] = {"CFG_SRAM", "MSIX", "DRAM"};
+ bool is_wc[3] = {false, false, true};
+ int rc;
+
+ rc = hl_pci_bars_map(hdev, name, is_wc);
+ if (rc)
+ return rc;
+
+ hdev->rmmio = hdev->pcie_bar[SRAM_CFG_BAR_ID] + (CFG_BASE - STM_FLASH_BASE_ADDR);
+
+ return 0;
+}
+
+static u64 gaudi2_set_hbm_bar_base(struct hl_device *hdev, u64 addr)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct hl_inbound_pci_region pci_region;
+ u64 old_addr = addr;
+ int rc;
+
+ if ((gaudi2) && (gaudi2->dram_bar_cur_addr == addr))
+ return old_addr;
+
+ if (hdev->asic_prop.iatu_done_by_fw)
+ return U64_MAX;
+
+ /* Inbound Region 2 - Bar 4 - Point to DRAM */
+ pci_region.mode = PCI_BAR_MATCH_MODE;
+ pci_region.bar = DRAM_BAR_ID;
+ pci_region.addr = addr;
+ rc = hl_pci_set_inbound_region(hdev, 2, &pci_region);
+ if (rc)
+ return U64_MAX;
+
+ if (gaudi2) {
+ old_addr = gaudi2->dram_bar_cur_addr;
+ gaudi2->dram_bar_cur_addr = addr;
+ }
+
+ return old_addr;
+}
+
+static int gaudi2_init_iatu(struct hl_device *hdev)
+{
+ struct hl_inbound_pci_region inbound_region;
+ struct hl_outbound_pci_region outbound_region;
+ u32 bar_addr_low, bar_addr_high;
+ int rc;
+
+ if (hdev->asic_prop.iatu_done_by_fw)
+ return 0;
+
+ /* Temporary inbound Region 0 - Bar 0 - Point to CFG
+ * We must map this region in BAR match mode in order to
+ * fetch BAR physical base address
+ */
+ inbound_region.mode = PCI_BAR_MATCH_MODE;
+ inbound_region.bar = SRAM_CFG_BAR_ID;
+ /* Base address must be aligned to Bar size which is 256 MB */
+ inbound_region.addr = STM_FLASH_BASE_ADDR - STM_FLASH_ALIGNED_OFF;
+ rc = hl_pci_set_inbound_region(hdev, 0, &inbound_region);
+ if (rc)
+ return rc;
+
+ /* Fetch physical BAR address */
+ bar_addr_high = RREG32(mmPCIE_DBI_BAR1_REG + STM_FLASH_ALIGNED_OFF);
+ bar_addr_low = RREG32(mmPCIE_DBI_BAR0_REG + STM_FLASH_ALIGNED_OFF) & ~0xF;
+
+ hdev->pcie_bar_phys[SRAM_CFG_BAR_ID] = (u64)bar_addr_high << 32 | bar_addr_low;
+
+ /* Inbound Region 0 - Bar 0 - Point to CFG */
+ inbound_region.mode = PCI_ADDRESS_MATCH_MODE;
+ inbound_region.bar = SRAM_CFG_BAR_ID;
+ inbound_region.offset_in_bar = 0;
+ inbound_region.addr = STM_FLASH_BASE_ADDR;
+ inbound_region.size = CFG_REGION_SIZE;
+ rc = hl_pci_set_inbound_region(hdev, 0, &inbound_region);
+ if (rc)
+ return rc;
+
+ /* Inbound Region 1 - Bar 0 - Point to BAR0_RESERVED + SRAM */
+ inbound_region.mode = PCI_ADDRESS_MATCH_MODE;
+ inbound_region.bar = SRAM_CFG_BAR_ID;
+ inbound_region.offset_in_bar = CFG_REGION_SIZE;
+ inbound_region.addr = BAR0_RSRVD_BASE_ADDR;
+ inbound_region.size = BAR0_RSRVD_SIZE + SRAM_SIZE;
+ rc = hl_pci_set_inbound_region(hdev, 1, &inbound_region);
+ if (rc)
+ return rc;
+
+ /* Inbound Region 2 - Bar 4 - Point to DRAM */
+ inbound_region.mode = PCI_BAR_MATCH_MODE;
+ inbound_region.bar = DRAM_BAR_ID;
+ inbound_region.addr = DRAM_PHYS_BASE;
+ rc = hl_pci_set_inbound_region(hdev, 2, &inbound_region);
+ if (rc)
+ return rc;
+
+ /* Outbound Region 0 - Point to Host */
+ outbound_region.addr = HOST_PHYS_BASE_0;
+ outbound_region.size = HOST_PHYS_SIZE_0;
+ rc = hl_pci_set_outbound_region(hdev, &outbound_region);
+
+ return rc;
+}
+
+static enum hl_device_hw_state gaudi2_get_hw_state(struct hl_device *hdev)
+{
+ return RREG32(mmHW_STATE);
+}
+
+static int gaudi2_tpc_binning_init_prop(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ /*
+ * check for error condition in which number of binning candidates
+ * is higher than the maximum supported by the driver
+ */
+ if (hweight64(hdev->tpc_binning) > MAX_CLUSTER_BINNING_FAULTY_TPCS) {
+ dev_err(hdev->dev, "TPC binning is supported for max of %d faulty TPCs, provided mask 0x%llx\n",
+ MAX_CLUSTER_BINNING_FAULTY_TPCS,
+ hdev->tpc_binning);
+ return -EINVAL;
+ }
+
+ prop->tpc_binning_mask = hdev->tpc_binning;
+ prop->tpc_enabled_mask = GAUDI2_TPC_FULL_MASK;
+
+ return 0;
+}
+
+static int gaudi2_set_tpc_binning_masks(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hw_queue_properties *q_props = prop->hw_queues_props;
+ u64 tpc_binning_mask;
+ u8 subst_idx = 0;
+ int i, rc;
+
+ rc = gaudi2_tpc_binning_init_prop(hdev);
+ if (rc)
+ return rc;
+
+ tpc_binning_mask = prop->tpc_binning_mask;
+
+ for (i = 0 ; i < MAX_FAULTY_TPCS ; i++) {
+ u8 subst_seq, binned, qid_base;
+
+ if (tpc_binning_mask == 0)
+ break;
+
+ if (subst_idx == 0) {
+ subst_seq = TPC_ID_DCORE0_TPC6;
+ qid_base = GAUDI2_QUEUE_ID_DCORE0_TPC_6_0;
+ } else {
+ subst_seq = TPC_ID_DCORE3_TPC5;
+ qid_base = GAUDI2_QUEUE_ID_DCORE3_TPC_5_0;
+ }
+
+
+ /* clear bit from mask */
+ binned = __ffs(tpc_binning_mask);
+ /*
+ * Coverity complains about possible out-of-bound access in
+ * clear_bit
+ */
+ if (binned >= TPC_ID_SIZE) {
+ dev_err(hdev->dev,
+ "Invalid binned TPC (binning mask: %llx)\n",
+ tpc_binning_mask);
+ return -EINVAL;
+ }
+ clear_bit(binned, (unsigned long *)&tpc_binning_mask);
+
+ /* also clear replacing TPC bit from enabled mask */
+ clear_bit(subst_seq, (unsigned long *)&prop->tpc_enabled_mask);
+
+ /* bin substite TPC's Qs */
+ q_props[qid_base].binned = 1;
+ q_props[qid_base + 1].binned = 1;
+ q_props[qid_base + 2].binned = 1;
+ q_props[qid_base + 3].binned = 1;
+
+ subst_idx++;
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_dec_binning_masks(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u8 num_faulty;
+
+ num_faulty = hweight32(hdev->decoder_binning);
+
+ /*
+ * check for error condition in which number of binning candidates
+ * is higher than the maximum supported by the driver
+ */
+ if (num_faulty > MAX_FAULTY_DECODERS) {
+ dev_err(hdev->dev, "decoder binning is supported for max of single faulty decoder, provided mask 0x%x\n",
+ hdev->decoder_binning);
+ return -EINVAL;
+ }
+
+ prop->decoder_binning_mask = (hdev->decoder_binning & GAUDI2_DECODER_FULL_MASK);
+
+ if (prop->decoder_binning_mask)
+ prop->decoder_enabled_mask = (GAUDI2_DECODER_FULL_MASK & ~BIT(DEC_ID_PCIE_VDEC1));
+ else
+ prop->decoder_enabled_mask = GAUDI2_DECODER_FULL_MASK;
+
+ return 0;
+}
+
+static void gaudi2_set_dram_binning_masks(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ /* check if we should override default binning */
+ if (!hdev->dram_binning) {
+ prop->dram_binning_mask = 0;
+ prop->dram_enabled_mask = GAUDI2_DRAM_FULL_MASK;
+ return;
+ }
+
+ /* set DRAM binning constraints */
+ prop->faulty_dram_cluster_map |= hdev->dram_binning;
+ prop->dram_binning_mask = hdev->dram_binning;
+ prop->dram_enabled_mask = GAUDI2_DRAM_FULL_MASK & ~BIT(HBM_ID5);
+}
+
+static int gaudi2_set_edma_binning_masks(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hw_queue_properties *q_props;
+ u8 seq, num_faulty;
+
+ num_faulty = hweight32(hdev->edma_binning);
+
+ /*
+ * check for error condition in which number of binning candidates
+ * is higher than the maximum supported by the driver
+ */
+ if (num_faulty > MAX_FAULTY_EDMAS) {
+ dev_err(hdev->dev,
+ "EDMA binning is supported for max of single faulty EDMA, provided mask 0x%x\n",
+ hdev->edma_binning);
+ return -EINVAL;
+ }
+
+ if (!hdev->edma_binning) {
+ prop->edma_binning_mask = 0;
+ prop->edma_enabled_mask = GAUDI2_EDMA_FULL_MASK;
+ return 0;
+ }
+
+ seq = __ffs((unsigned long)hdev->edma_binning);
+
+ /* set binning constraints */
+ prop->faulty_dram_cluster_map |= BIT(edma_to_hbm_cluster[seq]);
+ prop->edma_binning_mask = hdev->edma_binning;
+ prop->edma_enabled_mask = GAUDI2_EDMA_FULL_MASK & ~BIT(EDMA_ID_DCORE3_INSTANCE1);
+
+ /* bin substitute EDMA's queue */
+ q_props = prop->hw_queues_props;
+ q_props[GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0].binned = 1;
+ q_props[GAUDI2_QUEUE_ID_DCORE3_EDMA_1_1].binned = 1;
+ q_props[GAUDI2_QUEUE_ID_DCORE3_EDMA_1_2].binned = 1;
+ q_props[GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3].binned = 1;
+
+ return 0;
+}
+
+static int gaudi2_set_xbar_edge_enable_mask(struct hl_device *hdev, u32 xbar_edge_iso_mask)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u8 num_faulty, seq;
+
+ /* check if we should override default binning */
+ if (!xbar_edge_iso_mask) {
+ prop->xbar_edge_enabled_mask = GAUDI2_XBAR_EDGE_FULL_MASK;
+ return 0;
+ }
+
+ /*
+ * note that it can be set to value other than 0 only after cpucp packet (i.e.
+ * only the FW can set a redundancy value). for user it'll always be 0.
+ */
+ num_faulty = hweight32(xbar_edge_iso_mask);
+
+ /*
+ * check for error condition in which number of binning candidates
+ * is higher than the maximum supported by the driver
+ */
+ if (num_faulty > MAX_FAULTY_XBARS) {
+ dev_err(hdev->dev, "we cannot have more than %d faulty XBAR EDGE\n",
+ MAX_FAULTY_XBARS);
+ return -EINVAL;
+ }
+
+ seq = __ffs((unsigned long)xbar_edge_iso_mask);
+
+ /* set binning constraints */
+ prop->faulty_dram_cluster_map |= BIT(xbar_edge_to_hbm_cluster[seq]);
+ prop->xbar_edge_enabled_mask = (~xbar_edge_iso_mask) & GAUDI2_XBAR_EDGE_FULL_MASK;
+
+ return 0;
+}
+
+static int gaudi2_set_cluster_binning_masks_common(struct hl_device *hdev, u8 xbar_edge_iso_mask)
+{
+ int rc;
+
+ /*
+ * mark all clusters as good, each component will "fail" cluster
+ * based on eFuse/user values.
+ * If more than single cluster is faulty- the chip is unusable
+ */
+ hdev->asic_prop.faulty_dram_cluster_map = 0;
+
+ gaudi2_set_dram_binning_masks(hdev);
+
+ rc = gaudi2_set_edma_binning_masks(hdev);
+ if (rc)
+ return rc;
+
+ rc = gaudi2_set_xbar_edge_enable_mask(hdev, xbar_edge_iso_mask);
+ if (rc)
+ return rc;
+
+
+ /* always initially set to full mask */
+ hdev->asic_prop.hmmu_hif_enabled_mask = GAUDI2_HIF_HMMU_FULL_MASK;
+
+ return 0;
+}
+
+static int gaudi2_set_cluster_binning_masks(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc;
+
+ rc = gaudi2_set_cluster_binning_masks_common(hdev, prop->cpucp_info.xbar_binning_mask);
+ if (rc)
+ return rc;
+
+ /* if we have DRAM binning reported by FW we should perform cluster config */
+ if (prop->faulty_dram_cluster_map) {
+ u8 cluster_seq = __ffs((unsigned long)prop->faulty_dram_cluster_map);
+
+ prop->hmmu_hif_enabled_mask = cluster_hmmu_hif_enabled_mask[cluster_seq];
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_binning_masks(struct hl_device *hdev)
+{
+ int rc;
+
+ rc = gaudi2_set_cluster_binning_masks(hdev);
+ if (rc)
+ return rc;
+
+ rc = gaudi2_set_tpc_binning_masks(hdev);
+ if (rc)
+ return rc;
+
+ rc = gaudi2_set_dec_binning_masks(hdev);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int gaudi2_cpucp_info_get(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ long max_power;
+ u64 dram_size;
+ int rc;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q))
+ return 0;
+
+ /* No point of asking this information again when not doing hard reset, as the device
+ * CPU hasn't been reset
+ */
+ if (hdev->reset_info.in_compute_reset)
+ return 0;
+
+ rc = hl_fw_cpucp_handshake(hdev, mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_DEV_STS1, mmCPU_BOOT_ERR0,
+ mmCPU_BOOT_ERR1);
+ if (rc)
+ return rc;
+
+ dram_size = le64_to_cpu(prop->cpucp_info.dram_size);
+ if (dram_size) {
+ /* we can have wither 5 or 6 HBMs. other values are invalid */
+
+ if ((dram_size != ((GAUDI2_HBM_NUM - 1) * SZ_16G)) &&
+ (dram_size != (GAUDI2_HBM_NUM * SZ_16G))) {
+ dev_err(hdev->dev,
+ "F/W reported invalid DRAM size %llu. Trying to use default size %llu\n",
+ dram_size, prop->dram_size);
+ dram_size = prop->dram_size;
+ }
+
+ prop->dram_size = dram_size;
+ prop->dram_end_address = prop->dram_base_address + dram_size;
+ }
+
+ if (!strlen(prop->cpucp_info.card_name))
+ strscpy_pad(prop->cpucp_info.card_name, GAUDI2_DEFAULT_CARD_NAME,
+ CARD_NAME_MAX_LEN);
+
+ /* Overwrite binning masks with the actual binning values from F/W */
+ hdev->dram_binning = prop->cpucp_info.dram_binning_mask;
+ hdev->edma_binning = prop->cpucp_info.edma_binning_mask;
+ hdev->tpc_binning = le64_to_cpu(prop->cpucp_info.tpc_binning_mask);
+ hdev->decoder_binning = lower_32_bits(le64_to_cpu(prop->cpucp_info.decoder_binning_mask));
+
+ dev_dbg(hdev->dev, "Read binning masks: tpc: 0x%llx, dram: 0x%llx, edma: 0x%x, dec: 0x%x\n",
+ hdev->tpc_binning, hdev->dram_binning, hdev->edma_binning,
+ hdev->decoder_binning);
+
+ /*
+ * at this point the DRAM parameters need to be updated according to data obtained
+ * from the FW
+ */
+ rc = hdev->asic_funcs->set_dram_properties(hdev);
+ if (rc)
+ return rc;
+
+ rc = hdev->asic_funcs->set_binning_masks(hdev);
+ if (rc)
+ return rc;
+
+ max_power = hl_fw_get_max_power(hdev);
+ if (max_power < 0)
+ return max_power;
+
+ prop->max_power_default = (u64) max_power;
+
+ return 0;
+}
+
+static int gaudi2_fetch_psoc_frequency(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS];
+ int rc;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q))
+ return 0;
+
+ rc = hl_fw_cpucp_pll_info_get(hdev, HL_GAUDI2_CPU_PLL, pll_freq_arr);
+ if (rc)
+ return rc;
+
+ hdev->asic_prop.psoc_timestamp_frequency = pll_freq_arr[3];
+
+ return 0;
+}
+
+static int gaudi2_mmu_clear_pgt_range(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_MMU_MASK))
+ return 0;
+
+ if (prop->dmmu.host_resident)
+ return 0;
+
+ rc = gaudi2_memset_device_memory(hdev, prop->mmu_pgt_addr, prop->dmmu.pgt_size, 0);
+ if (rc)
+ dev_err(hdev->dev, "Failed to clear mmu pgt");
+
+ return rc;
+}
+
+static int gaudi2_early_init(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct pci_dev *pdev = hdev->pdev;
+ resource_size_t pci_bar_size;
+ int rc;
+
+ rc = gaudi2_set_fixed_properties(hdev);
+ if (rc)
+ return rc;
+
+ /* Check BAR sizes */
+ pci_bar_size = pci_resource_len(pdev, SRAM_CFG_BAR_ID);
+
+ if (pci_bar_size != CFG_BAR_SIZE) {
+ dev_err(hdev->dev, "Not " HL_NAME "? BAR %d size %pa, expecting %llu\n",
+ SRAM_CFG_BAR_ID, &pci_bar_size, CFG_BAR_SIZE);
+ rc = -ENODEV;
+ goto free_queue_props;
+ }
+
+ pci_bar_size = pci_resource_len(pdev, MSIX_BAR_ID);
+ if (pci_bar_size != MSIX_BAR_SIZE) {
+ dev_err(hdev->dev, "Not " HL_NAME "? BAR %d size %pa, expecting %llu\n",
+ MSIX_BAR_ID, &pci_bar_size, MSIX_BAR_SIZE);
+ rc = -ENODEV;
+ goto free_queue_props;
+ }
+
+ prop->dram_pci_bar_size = pci_resource_len(pdev, DRAM_BAR_ID);
+ hdev->dram_pci_bar_start = pci_resource_start(pdev, DRAM_BAR_ID);
+
+ /*
+ * Only in pldm driver config iATU
+ */
+ if (hdev->pldm)
+ hdev->asic_prop.iatu_done_by_fw = false;
+ else
+ hdev->asic_prop.iatu_done_by_fw = true;
+
+ rc = hl_pci_init(hdev);
+ if (rc)
+ goto free_queue_props;
+
+ /* Before continuing in the initialization, we need to read the preboot
+ * version to determine whether we run with a security-enabled firmware
+ */
+ rc = hl_fw_read_preboot_status(hdev);
+ if (rc) {
+ if (hdev->reset_on_preboot_fail)
+ hdev->asic_funcs->hw_fini(hdev, true, false);
+ goto pci_fini;
+ }
+
+ if (gaudi2_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
+ dev_dbg(hdev->dev, "H/W state is dirty, must reset before initializing\n");
+ rc = hdev->asic_funcs->hw_fini(hdev, true, false);
+ if (rc) {
+ dev_err(hdev->dev, "failed to reset HW in dirty state (%d)\n", rc);
+ goto pci_fini;
+ }
+
+ rc = hl_fw_read_preboot_status(hdev);
+ if (rc) {
+ if (hdev->reset_on_preboot_fail)
+ hdev->asic_funcs->hw_fini(hdev, true, false);
+ goto pci_fini;
+ }
+ }
+
+ return 0;
+
+pci_fini:
+ hl_pci_fini(hdev);
+free_queue_props:
+ kfree(hdev->asic_prop.hw_queues_props);
+ return rc;
+}
+
+static int gaudi2_early_fini(struct hl_device *hdev)
+{
+ kfree(hdev->asic_prop.hw_queues_props);
+ hl_pci_fini(hdev);
+
+ return 0;
+}
+
+static bool gaudi2_is_arc_nic_owned(u64 arc_id)
+{
+ switch (arc_id) {
+ case CPU_ID_NIC_QMAN_ARC0...CPU_ID_NIC_QMAN_ARC23:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool gaudi2_is_arc_tpc_owned(u64 arc_id)
+{
+ switch (arc_id) {
+ case CPU_ID_TPC_QMAN_ARC0...CPU_ID_TPC_QMAN_ARC24:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void gaudi2_init_arcs(struct hl_device *hdev)
+{
+ struct cpu_dyn_regs *dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 arc_id;
+ u32 i;
+
+ for (i = CPU_ID_SCHED_ARC0 ; i <= CPU_ID_SCHED_ARC3 ; i++) {
+ if (gaudi2_is_arc_enabled(hdev, i))
+ continue;
+
+ gaudi2_set_arc_id_cap(hdev, i);
+ }
+
+ for (i = GAUDI2_QUEUE_ID_PDMA_0_0 ; i < GAUDI2_QUEUE_ID_CPU_PQ ; i += 4) {
+ if (!gaudi2_is_queue_enabled(hdev, i))
+ continue;
+
+ arc_id = gaudi2_queue_id_to_arc_id[i];
+ if (gaudi2_is_arc_enabled(hdev, arc_id))
+ continue;
+
+ if (gaudi2_is_arc_nic_owned(arc_id) &&
+ !(hdev->nic_ports_mask & BIT_ULL(arc_id - CPU_ID_NIC_QMAN_ARC0)))
+ continue;
+
+ if (gaudi2_is_arc_tpc_owned(arc_id) && !(gaudi2->tpc_hw_cap_initialized &
+ BIT_ULL(arc_id - CPU_ID_TPC_QMAN_ARC0)))
+ continue;
+
+ gaudi2_set_arc_id_cap(hdev, arc_id);
+ }
+
+ /* Fetch ARC scratchpad address */
+ hdev->asic_prop.engine_core_interrupt_reg_addr =
+ CFG_BASE + le32_to_cpu(dyn_regs->eng_arc_irq_ctrl);
+}
+
+static int gaudi2_scrub_arc_dccm(struct hl_device *hdev, u32 cpu_id)
+{
+ u32 reg_base, reg_val;
+ int rc;
+
+ switch (cpu_id) {
+ case CPU_ID_SCHED_ARC0 ... CPU_ID_SCHED_ARC3:
+ /* Each ARC scheduler has 2 consecutive DCCM blocks */
+ rc = gaudi2_send_job_to_kdma(hdev, 0, CFG_BASE + gaudi2_arc_dccm_bases[cpu_id],
+ ARC_DCCM_BLOCK_SIZE * 2, true);
+ if (rc)
+ return rc;
+ break;
+ case CPU_ID_SCHED_ARC4:
+ case CPU_ID_SCHED_ARC5:
+ case CPU_ID_MME_QMAN_ARC0:
+ case CPU_ID_MME_QMAN_ARC1:
+ reg_base = gaudi2_arc_blocks_bases[cpu_id];
+
+ /* Scrub lower DCCM block */
+ rc = gaudi2_send_job_to_kdma(hdev, 0, CFG_BASE + gaudi2_arc_dccm_bases[cpu_id],
+ ARC_DCCM_BLOCK_SIZE, true);
+ if (rc)
+ return rc;
+
+ /* Switch to upper DCCM block */
+ reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_MME_ARC_UPPER_DCCM_EN_VAL_MASK, 1);
+ WREG32(reg_base + ARC_DCCM_UPPER_EN_OFFSET, reg_val);
+
+ /* Scrub upper DCCM block */
+ rc = gaudi2_send_job_to_kdma(hdev, 0, CFG_BASE + gaudi2_arc_dccm_bases[cpu_id],
+ ARC_DCCM_BLOCK_SIZE, true);
+ if (rc)
+ return rc;
+
+ /* Switch to lower DCCM block */
+ reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_MME_ARC_UPPER_DCCM_EN_VAL_MASK, 0);
+ WREG32(reg_base + ARC_DCCM_UPPER_EN_OFFSET, reg_val);
+ break;
+ default:
+ rc = gaudi2_send_job_to_kdma(hdev, 0, CFG_BASE + gaudi2_arc_dccm_bases[cpu_id],
+ ARC_DCCM_BLOCK_SIZE, true);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int gaudi2_scrub_arcs_dccm(struct hl_device *hdev)
+{
+ u16 arc_id;
+ int rc;
+
+ for (arc_id = CPU_ID_SCHED_ARC0 ; arc_id < CPU_ID_MAX ; arc_id++) {
+ if (!gaudi2_is_arc_enabled(hdev, arc_id))
+ continue;
+
+ rc = gaudi2_scrub_arc_dccm(hdev, arc_id);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int gaudi2_late_init(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int rc;
+
+ rc = hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_ENABLE_PCI_ACCESS,
+ gaudi2->virt_msix_db_dma_addr);
+ if (rc)
+ return rc;
+
+ rc = gaudi2_fetch_psoc_frequency(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to fetch psoc frequency\n");
+ goto disable_pci_access;
+ }
+
+ rc = gaudi2_mmu_clear_pgt_range(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to clear MMU page tables range\n");
+ goto disable_pci_access;
+ }
+
+ gaudi2_init_arcs(hdev);
+
+ rc = gaudi2_scrub_arcs_dccm(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to scrub arcs DCCM\n");
+ goto disable_pci_access;
+ }
+
+ gaudi2_init_security(hdev);
+
+ return 0;
+
+disable_pci_access:
+ hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
+
+ return rc;
+}
+
+static void gaudi2_late_fini(struct hl_device *hdev)
+{
+ hl_hwmon_release_resources(hdev);
+}
+
+static void gaudi2_user_mapped_dec_init(struct gaudi2_device *gaudi2, u32 start_idx)
+{
+ struct user_mapped_block *blocks = gaudi2->mapped_blocks;
+
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE0_DEC0_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE0_DEC1_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE1_DEC0_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE1_DEC1_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE2_DEC0_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE2_DEC1_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE3_DEC0_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmDCORE3_DEC1_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx++], mmPCIE_DEC0_CMD_BASE, HL_BLOCK_SIZE);
+ HL_USR_MAPPED_BLK_INIT(&blocks[start_idx], mmPCIE_DEC1_CMD_BASE, HL_BLOCK_SIZE);
+}
+
+static void gaudi2_user_mapped_blocks_init(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct user_mapped_block *blocks = gaudi2->mapped_blocks;
+ u32 block_size, umr_start_idx, num_umr_blocks;
+ int i;
+
+ for (i = 0 ; i < NUM_ARC_CPUS ; i++) {
+ if (i >= CPU_ID_SCHED_ARC0 && i <= CPU_ID_SCHED_ARC3)
+ block_size = ARC_DCCM_BLOCK_SIZE * 2;
+ else
+ block_size = ARC_DCCM_BLOCK_SIZE;
+
+ blocks[i].address = gaudi2_arc_dccm_bases[i];
+ blocks[i].size = block_size;
+ }
+
+ blocks[NUM_ARC_CPUS].address = mmARC_FARM_ARC0_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 1].address = mmARC_FARM_ARC1_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 1].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 2].address = mmARC_FARM_ARC2_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 2].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 3].address = mmARC_FARM_ARC3_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 3].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 4].address = mmDCORE0_MME_QM_ARC_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 4].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 5].address = mmDCORE1_MME_QM_ARC_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 5].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 6].address = mmDCORE2_MME_QM_ARC_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 6].size = HL_BLOCK_SIZE;
+
+ blocks[NUM_ARC_CPUS + 7].address = mmDCORE3_MME_QM_ARC_ACP_ENG_BASE;
+ blocks[NUM_ARC_CPUS + 7].size = HL_BLOCK_SIZE;
+
+ umr_start_idx = NUM_ARC_CPUS + NUM_OF_USER_ACP_BLOCKS;
+ num_umr_blocks = NIC_NUMBER_OF_ENGINES * NUM_OF_USER_NIC_UMR_BLOCKS;
+ for (i = 0 ; i < num_umr_blocks ; i++) {
+ u8 nic_id, umr_block_id;
+
+ nic_id = i / NUM_OF_USER_NIC_UMR_BLOCKS;
+ umr_block_id = i % NUM_OF_USER_NIC_UMR_BLOCKS;
+
+ blocks[umr_start_idx + i].address =
+ mmNIC0_UMR0_0_UNSECURE_DOORBELL0_BASE +
+ (nic_id / NIC_NUMBER_OF_QM_PER_MACRO) * NIC_OFFSET +
+ (nic_id % NIC_NUMBER_OF_QM_PER_MACRO) * NIC_QM_OFFSET +
+ umr_block_id * NIC_UMR_OFFSET;
+ blocks[umr_start_idx + i].size = HL_BLOCK_SIZE;
+ }
+
+ /* Expose decoder HW configuration block to user */
+ gaudi2_user_mapped_dec_init(gaudi2, USR_MAPPED_BLK_DEC_START_IDX);
+
+ for (i = 1; i < NUM_OF_DCORES; ++i) {
+ blocks[USR_MAPPED_BLK_SM_START_IDX + 2 * (i - 1)].size = SM_OBJS_BLOCK_SIZE;
+ blocks[USR_MAPPED_BLK_SM_START_IDX + 2 * (i - 1) + 1].size = HL_BLOCK_SIZE;
+
+ blocks[USR_MAPPED_BLK_SM_START_IDX + 2 * (i - 1)].address =
+ mmDCORE0_SYNC_MNGR_OBJS_BASE + i * DCORE_OFFSET;
+
+ blocks[USR_MAPPED_BLK_SM_START_IDX + 2 * (i - 1) + 1].address =
+ mmDCORE0_SYNC_MNGR_GLBL_BASE + i * DCORE_OFFSET;
+ }
+}
+
+static int gaudi2_alloc_cpu_accessible_dma_mem(struct hl_device *hdev)
+{
+ dma_addr_t dma_addr_arr[GAUDI2_ALLOC_CPU_MEM_RETRY_CNT] = {}, end_addr;
+ void *virt_addr_arr[GAUDI2_ALLOC_CPU_MEM_RETRY_CNT] = {};
+ int i, j, rc = 0;
+
+ /* The device ARC works with 32-bits addresses, and because there is a single HW register
+ * that holds the extension bits (49..28), these bits must be identical in all the allocated
+ * range.
+ */
+
+ for (i = 0 ; i < GAUDI2_ALLOC_CPU_MEM_RETRY_CNT ; i++) {
+ virt_addr_arr[i] = hl_asic_dma_alloc_coherent(hdev, HL_CPU_ACCESSIBLE_MEM_SIZE,
+ &dma_addr_arr[i], GFP_KERNEL | __GFP_ZERO);
+ if (!virt_addr_arr[i]) {
+ rc = -ENOMEM;
+ goto free_dma_mem_arr;
+ }
+
+ end_addr = dma_addr_arr[i] + HL_CPU_ACCESSIBLE_MEM_SIZE - 1;
+ if (GAUDI2_ARC_PCI_MSB_ADDR(dma_addr_arr[i]) == GAUDI2_ARC_PCI_MSB_ADDR(end_addr))
+ break;
+ }
+
+ if (i == GAUDI2_ALLOC_CPU_MEM_RETRY_CNT) {
+ dev_err(hdev->dev,
+ "MSB of ARC accessible DMA memory are not identical in all range\n");
+ rc = -EFAULT;
+ goto free_dma_mem_arr;
+ }
+
+ hdev->cpu_accessible_dma_mem = virt_addr_arr[i];
+ hdev->cpu_accessible_dma_address = dma_addr_arr[i];
+
+free_dma_mem_arr:
+ for (j = 0 ; j < i ; j++)
+ hl_asic_dma_free_coherent(hdev, HL_CPU_ACCESSIBLE_MEM_SIZE, virt_addr_arr[j],
+ dma_addr_arr[j]);
+
+ return rc;
+}
+
+static void gaudi2_set_pci_memory_regions(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct pci_mem_region *region;
+
+ /* CFG */
+ region = &hdev->pci_mem_region[PCI_REGION_CFG];
+ region->region_base = CFG_BASE;
+ region->region_size = CFG_SIZE;
+ region->offset_in_bar = CFG_BASE - STM_FLASH_BASE_ADDR;
+ region->bar_size = CFG_BAR_SIZE;
+ region->bar_id = SRAM_CFG_BAR_ID;
+ region->used = 1;
+
+ /* SRAM */
+ region = &hdev->pci_mem_region[PCI_REGION_SRAM];
+ region->region_base = SRAM_BASE_ADDR;
+ region->region_size = SRAM_SIZE;
+ region->offset_in_bar = CFG_REGION_SIZE + BAR0_RSRVD_SIZE;
+ region->bar_size = CFG_BAR_SIZE;
+ region->bar_id = SRAM_CFG_BAR_ID;
+ region->used = 1;
+
+ /* DRAM */
+ region = &hdev->pci_mem_region[PCI_REGION_DRAM];
+ region->region_base = DRAM_PHYS_BASE;
+ region->region_size = hdev->asic_prop.dram_size;
+ region->offset_in_bar = 0;
+ region->bar_size = prop->dram_pci_bar_size;
+ region->bar_id = DRAM_BAR_ID;
+ region->used = 1;
+}
+
+static void gaudi2_user_interrupt_setup(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int i, j, k;
+
+ /* Initialize TPC interrupt */
+ HL_USR_INTR_STRUCT_INIT(hdev->tpc_interrupt, hdev, 0, HL_USR_INTERRUPT_TPC);
+
+ /* Initialize unexpected error interrupt */
+ HL_USR_INTR_STRUCT_INIT(hdev->unexpected_error_interrupt, hdev, 0,
+ HL_USR_INTERRUPT_UNEXPECTED);
+
+ /* Initialize common user CQ interrupt */
+ HL_USR_INTR_STRUCT_INIT(hdev->common_user_cq_interrupt, hdev,
+ HL_COMMON_USER_CQ_INTERRUPT_ID, HL_USR_INTERRUPT_CQ);
+
+ /* Initialize common decoder interrupt */
+ HL_USR_INTR_STRUCT_INIT(hdev->common_decoder_interrupt, hdev,
+ HL_COMMON_DEC_INTERRUPT_ID, HL_USR_INTERRUPT_DECODER);
+
+ /* User interrupts structure holds both decoder and user interrupts from various engines.
+ * We first initialize the decoder interrupts and then we add the user interrupts.
+ * The only limitation is that the last decoder interrupt id must be smaller
+ * then GAUDI2_IRQ_NUM_USER_FIRST. This is checked at compilation time.
+ */
+
+ /* Initialize decoder interrupts, expose only normal interrupts,
+ * error interrupts to be handled by driver
+ */
+ for (i = GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM, j = 0 ; i <= GAUDI2_IRQ_NUM_SHARED_DEC1_NRM;
+ i += 2, j++)
+ HL_USR_INTR_STRUCT_INIT(hdev->user_interrupt[j], hdev, i,
+ HL_USR_INTERRUPT_DECODER);
+
+ for (i = GAUDI2_IRQ_NUM_USER_FIRST, k = 0 ; k < prop->user_interrupt_count; i++, j++, k++)
+ HL_USR_INTR_STRUCT_INIT(hdev->user_interrupt[j], hdev, i, HL_USR_INTERRUPT_CQ);
+}
+
+static inline int gaudi2_get_non_zero_random_int(void)
+{
+ int rand = get_random_u32();
+
+ return rand ? rand : 1;
+}
+
+static void gaudi2_special_blocks_free(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct hl_skip_blocks_cfg *skip_special_blocks_cfg =
+ &prop->skip_special_blocks_cfg;
+
+ kfree(prop->special_blocks);
+ kfree(skip_special_blocks_cfg->block_types);
+ kfree(skip_special_blocks_cfg->block_ranges);
+}
+
+static void gaudi2_special_blocks_iterator_free(struct hl_device *hdev)
+{
+ gaudi2_special_blocks_free(hdev);
+}
+
+static bool gaudi2_special_block_skip(struct hl_device *hdev,
+ struct hl_special_blocks_cfg *special_blocks_cfg,
+ u32 blk_idx, u32 major, u32 minor, u32 sub_minor)
+{
+ return false;
+}
+
+static int gaudi2_special_blocks_config(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int i, rc;
+
+ /* Configure Special blocks */
+ prop->glbl_err_max_cause_num = GAUDI2_GLBL_ERR_MAX_CAUSE_NUM;
+ prop->num_of_special_blocks = ARRAY_SIZE(gaudi2_special_blocks);
+ prop->special_blocks = kmalloc_array(prop->num_of_special_blocks,
+ sizeof(*prop->special_blocks), GFP_KERNEL);
+ if (!prop->special_blocks)
+ return -ENOMEM;
+
+ for (i = 0 ; i < prop->num_of_special_blocks ; i++)
+ memcpy(&prop->special_blocks[i], &gaudi2_special_blocks[i],
+ sizeof(*prop->special_blocks));
+
+ /* Configure when to skip Special blocks */
+ memset(&prop->skip_special_blocks_cfg, 0, sizeof(prop->skip_special_blocks_cfg));
+ prop->skip_special_blocks_cfg.skip_block_hook = gaudi2_special_block_skip;
+
+ if (ARRAY_SIZE(gaudi2_iterator_skip_block_types)) {
+ prop->skip_special_blocks_cfg.block_types =
+ kmalloc_array(ARRAY_SIZE(gaudi2_iterator_skip_block_types),
+ sizeof(gaudi2_iterator_skip_block_types[0]), GFP_KERNEL);
+ if (!prop->skip_special_blocks_cfg.block_types) {
+ rc = -ENOMEM;
+ goto free_special_blocks;
+ }
+
+ memcpy(prop->skip_special_blocks_cfg.block_types, gaudi2_iterator_skip_block_types,
+ sizeof(gaudi2_iterator_skip_block_types));
+
+ prop->skip_special_blocks_cfg.block_types_len =
+ ARRAY_SIZE(gaudi2_iterator_skip_block_types);
+ }
+
+ if (ARRAY_SIZE(gaudi2_iterator_skip_block_ranges)) {
+ prop->skip_special_blocks_cfg.block_ranges =
+ kmalloc_array(ARRAY_SIZE(gaudi2_iterator_skip_block_ranges),
+ sizeof(gaudi2_iterator_skip_block_ranges[0]), GFP_KERNEL);
+ if (!prop->skip_special_blocks_cfg.block_ranges) {
+ rc = -ENOMEM;
+ goto free_skip_special_blocks_types;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(gaudi2_iterator_skip_block_ranges) ; i++)
+ memcpy(&prop->skip_special_blocks_cfg.block_ranges[i],
+ &gaudi2_iterator_skip_block_ranges[i],
+ sizeof(struct range));
+
+ prop->skip_special_blocks_cfg.block_ranges_len =
+ ARRAY_SIZE(gaudi2_iterator_skip_block_ranges);
+ }
+
+ return 0;
+
+free_skip_special_blocks_types:
+ kfree(prop->skip_special_blocks_cfg.block_types);
+free_special_blocks:
+ kfree(prop->special_blocks);
+
+ return rc;
+}
+
+static int gaudi2_special_blocks_iterator_config(struct hl_device *hdev)
+{
+ return gaudi2_special_blocks_config(hdev);
+}
+
+static void gaudi2_test_queues_msgs_free(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_queues_test_info *msg_info = gaudi2->queues_test_info;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_TESTED_QS ; i++) {
+ /* bail-out if this is an allocation failure point */
+ if (!msg_info[i].kern_addr)
+ break;
+
+ hl_asic_dma_pool_free(hdev, msg_info[i].kern_addr, msg_info[i].dma_addr);
+ msg_info[i].kern_addr = NULL;
+ }
+}
+
+static int gaudi2_test_queues_msgs_alloc(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_queues_test_info *msg_info = gaudi2->queues_test_info;
+ int i, rc;
+
+ /* allocate a message-short buf for each Q we intend to test */
+ for (i = 0 ; i < GAUDI2_NUM_TESTED_QS ; i++) {
+ msg_info[i].kern_addr =
+ (void *)hl_asic_dma_pool_zalloc(hdev, sizeof(struct packet_msg_short),
+ GFP_KERNEL, &msg_info[i].dma_addr);
+ if (!msg_info[i].kern_addr) {
+ dev_err(hdev->dev,
+ "Failed to allocate dma memory for H/W queue %d testing\n", i);
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+ }
+
+ return 0;
+
+err_exit:
+ gaudi2_test_queues_msgs_free(hdev);
+ return rc;
+}
+
+static int gaudi2_sw_init(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2;
+ int i, rc;
+
+ /* Allocate device structure */
+ gaudi2 = kzalloc(sizeof(*gaudi2), GFP_KERNEL);
+ if (!gaudi2)
+ return -ENOMEM;
+
+ for (i = 0 ; i < ARRAY_SIZE(gaudi2_irq_map_table) ; i++) {
+ if (gaudi2_irq_map_table[i].msg || !gaudi2_irq_map_table[i].valid)
+ continue;
+
+ if (gaudi2->num_of_valid_hw_events == GAUDI2_EVENT_SIZE) {
+ dev_err(hdev->dev, "H/W events array exceeds the limit of %u events\n",
+ GAUDI2_EVENT_SIZE);
+ rc = -EINVAL;
+ goto free_gaudi2_device;
+ }
+
+ gaudi2->hw_events[gaudi2->num_of_valid_hw_events++] = gaudi2_irq_map_table[i].fc_id;
+ }
+
+ for (i = 0 ; i < MME_NUM_OF_LFSR_SEEDS ; i++)
+ gaudi2->lfsr_rand_seeds[i] = gaudi2_get_non_zero_random_int();
+
+ gaudi2->cpucp_info_get = gaudi2_cpucp_info_get;
+
+ hdev->asic_specific = gaudi2;
+
+ /* Create DMA pool for small allocations.
+ * Use DEVICE_CACHE_LINE_SIZE for alignment since the NIC memory-mapped
+ * PI/CI registers allocated from this pool have this restriction
+ */
+ hdev->dma_pool = dma_pool_create(dev_name(hdev->dev), &hdev->pdev->dev,
+ GAUDI2_DMA_POOL_BLK_SIZE, DEVICE_CACHE_LINE_SIZE, 0);
+ if (!hdev->dma_pool) {
+ dev_err(hdev->dev, "failed to create DMA pool\n");
+ rc = -ENOMEM;
+ goto free_gaudi2_device;
+ }
+
+ rc = gaudi2_alloc_cpu_accessible_dma_mem(hdev);
+ if (rc)
+ goto free_dma_pool;
+
+ hdev->cpu_accessible_dma_pool = gen_pool_create(ilog2(32), -1);
+ if (!hdev->cpu_accessible_dma_pool) {
+ dev_err(hdev->dev, "Failed to create CPU accessible DMA pool\n");
+ rc = -ENOMEM;
+ goto free_cpu_dma_mem;
+ }
+
+ rc = gen_pool_add(hdev->cpu_accessible_dma_pool, (uintptr_t) hdev->cpu_accessible_dma_mem,
+ HL_CPU_ACCESSIBLE_MEM_SIZE, -1);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to add memory to CPU accessible DMA pool\n");
+ rc = -EFAULT;
+ goto free_cpu_accessible_dma_pool;
+ }
+
+ gaudi2->virt_msix_db_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, prop->pmmu.page_size,
+ &gaudi2->virt_msix_db_dma_addr);
+ if (!gaudi2->virt_msix_db_cpu_addr) {
+ dev_err(hdev->dev, "Failed to allocate DMA memory for virtual MSI-X doorbell\n");
+ rc = -ENOMEM;
+ goto free_cpu_accessible_dma_pool;
+ }
+
+ spin_lock_init(&gaudi2->hw_queues_lock);
+
+ gaudi2->scratchpad_bus_address = prop->mmu_pgt_addr + HMMU_PAGE_TABLES_SIZE + EDMA_PQS_SIZE;
+
+ gaudi2_user_mapped_blocks_init(hdev);
+
+ /* Initialize user interrupts */
+ gaudi2_user_interrupt_setup(hdev);
+
+ hdev->supports_coresight = true;
+ hdev->supports_sync_stream = true;
+ hdev->supports_cb_mapping = true;
+ hdev->supports_wait_for_multi_cs = false;
+
+ prop->supports_compute_reset = true;
+
+ /* Event queue sanity check added in FW version 1.11 */
+ if (hl_fw_version_cmp(hdev, 1, 11, 0) < 0)
+ hdev->event_queue.check_eqe_index = false;
+ else
+ hdev->event_queue.check_eqe_index = true;
+
+ hdev->asic_funcs->set_pci_memory_regions(hdev);
+
+ rc = gaudi2_special_blocks_iterator_config(hdev);
+ if (rc)
+ goto free_virt_msix_db_mem;
+
+ rc = gaudi2_test_queues_msgs_alloc(hdev);
+ if (rc)
+ goto special_blocks_free;
+
+ hdev->heartbeat_debug_info.cpu_queue_id = GAUDI2_QUEUE_ID_CPU_PQ;
+
+ return 0;
+
+special_blocks_free:
+ gaudi2_special_blocks_iterator_free(hdev);
+free_virt_msix_db_mem:
+ hl_cpu_accessible_dma_pool_free(hdev, prop->pmmu.page_size, gaudi2->virt_msix_db_cpu_addr);
+free_cpu_accessible_dma_pool:
+ gen_pool_destroy(hdev->cpu_accessible_dma_pool);
+free_cpu_dma_mem:
+ hl_asic_dma_free_coherent(hdev, HL_CPU_ACCESSIBLE_MEM_SIZE, hdev->cpu_accessible_dma_mem,
+ hdev->cpu_accessible_dma_address);
+free_dma_pool:
+ dma_pool_destroy(hdev->dma_pool);
+free_gaudi2_device:
+ kfree(gaudi2);
+ return rc;
+}
+
+static int gaudi2_sw_fini(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ gaudi2_test_queues_msgs_free(hdev);
+
+ gaudi2_special_blocks_iterator_free(hdev);
+
+ hl_cpu_accessible_dma_pool_free(hdev, prop->pmmu.page_size, gaudi2->virt_msix_db_cpu_addr);
+
+ gen_pool_destroy(hdev->cpu_accessible_dma_pool);
+
+ hl_asic_dma_free_coherent(hdev, HL_CPU_ACCESSIBLE_MEM_SIZE, hdev->cpu_accessible_dma_mem,
+ hdev->cpu_accessible_dma_address);
+
+ dma_pool_destroy(hdev->dma_pool);
+
+ kfree(gaudi2);
+
+ return 0;
+}
+
+static void gaudi2_stop_qman_common(struct hl_device *hdev, u32 reg_base)
+{
+ WREG32(reg_base + QM_GLBL_CFG1_OFFSET, QM_GLBL_CFG1_PQF_STOP |
+ QM_GLBL_CFG1_CQF_STOP |
+ QM_GLBL_CFG1_CP_STOP);
+
+ /* stop also the ARC */
+ WREG32(reg_base + QM_GLBL_CFG2_OFFSET, QM_GLBL_CFG2_ARC_CQF_STOP);
+}
+
+static void gaudi2_flush_qman_common(struct hl_device *hdev, u32 reg_base)
+{
+ WREG32(reg_base + QM_GLBL_CFG1_OFFSET, QM_GLBL_CFG1_PQF_FLUSH |
+ QM_GLBL_CFG1_CQF_FLUSH |
+ QM_GLBL_CFG1_CP_FLUSH);
+}
+
+static void gaudi2_flush_qman_arc_common(struct hl_device *hdev, u32 reg_base)
+{
+ WREG32(reg_base + QM_GLBL_CFG2_OFFSET, QM_GLBL_CFG2_ARC_CQF_FLUSH);
+}
+
+/**
+ * gaudi2_clear_qm_fence_counters_common - clear QM's fence counters
+ *
+ * @hdev: pointer to the habanalabs device structure
+ * @queue_id: queue to clear fence counters to
+ * @skip_fence: if true set maximum fence value to all fence counters to avoid
+ * getting stuck on any fence value. otherwise set all fence
+ * counters to 0 (standard clear of fence counters)
+ */
+static void gaudi2_clear_qm_fence_counters_common(struct hl_device *hdev, u32 queue_id,
+ bool skip_fence)
+{
+ u32 size, reg_base;
+ u32 addr, val;
+
+ reg_base = gaudi2_qm_blocks_bases[queue_id];
+
+ addr = reg_base + QM_CP_FENCE0_CNT_0_OFFSET;
+ size = mmPDMA0_QM_CP_BARRIER_CFG - mmPDMA0_QM_CP_FENCE0_CNT_0;
+
+ /*
+ * in case we want to make sure that QM that is stuck on a fence will
+ * be released we should set the fence counter to a higher value that
+ * the value the QM waiting for. to comply with any fence counter of
+ * any value we set maximum fence value to all counters
+ */
+ val = skip_fence ? U32_MAX : 0;
+ gaudi2_memset_device_lbw(hdev, addr, size, val);
+}
+
+static void gaudi2_qman_manual_flush_common(struct hl_device *hdev, u32 queue_id)
+{
+ u32 reg_base = gaudi2_qm_blocks_bases[queue_id];
+
+ gaudi2_clear_qm_fence_counters_common(hdev, queue_id, true);
+ gaudi2_flush_qman_common(hdev, reg_base);
+ gaudi2_flush_qman_arc_common(hdev, reg_base);
+}
+
+static void gaudi2_stop_dma_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int dcore, inst;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_PDMA_MASK))
+ goto stop_edma_qmans;
+
+ /* Stop CPs of PDMA QMANs */
+ gaudi2_stop_qman_common(hdev, mmPDMA0_QM_BASE);
+ gaudi2_stop_qman_common(hdev, mmPDMA1_QM_BASE);
+
+stop_edma_qmans:
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_EDMA_MASK))
+ return;
+
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (inst = 0 ; inst < NUM_OF_EDMA_PER_DCORE ; inst++) {
+ u8 seq = dcore * NUM_OF_EDMA_PER_DCORE + inst;
+ u32 qm_base;
+
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_EDMA_SHIFT + seq)))
+ continue;
+
+ qm_base = mmDCORE0_EDMA0_QM_BASE + dcore * DCORE_OFFSET +
+ inst * DCORE_EDMA_OFFSET;
+
+ /* Stop CPs of EDMA QMANs */
+ gaudi2_stop_qman_common(hdev, qm_base);
+ }
+ }
+}
+
+static void gaudi2_stop_mme_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 offset, i;
+
+ offset = mmDCORE1_MME_QM_BASE - mmDCORE0_MME_QM_BASE;
+
+ for (i = 0 ; i < NUM_OF_DCORES ; i++) {
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_MME_SHIFT + i)))
+ continue;
+
+ gaudi2_stop_qman_common(hdev, mmDCORE0_MME_QM_BASE + (i * offset));
+ }
+}
+
+static void gaudi2_stop_tpc_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+ int i;
+
+ if (!(gaudi2->tpc_hw_cap_initialized & HW_CAP_TPC_MASK))
+ return;
+
+ for (i = 0 ; i < TPC_ID_SIZE ; i++) {
+ if (!(gaudi2->tpc_hw_cap_initialized & BIT_ULL(HW_CAP_TPC_SHIFT + i)))
+ continue;
+
+ reg_base = gaudi2_qm_blocks_bases[gaudi2_tpc_id_to_queue_id[i]];
+ gaudi2_stop_qman_common(hdev, reg_base);
+ }
+}
+
+static void gaudi2_stop_rot_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+ int i;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_ROT_MASK))
+ return;
+
+ for (i = 0 ; i < ROTATOR_ID_SIZE ; i++) {
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_ROT_SHIFT + i)))
+ continue;
+
+ reg_base = gaudi2_qm_blocks_bases[gaudi2_rot_id_to_queue_id[i]];
+ gaudi2_stop_qman_common(hdev, reg_base);
+ }
+}
+
+static void gaudi2_stop_nic_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base, queue_id;
+ int i;
+
+ if (!(gaudi2->nic_hw_cap_initialized & HW_CAP_NIC_MASK))
+ return;
+
+ queue_id = GAUDI2_QUEUE_ID_NIC_0_0;
+
+ for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
+ if (!(hdev->nic_ports_mask & BIT(i)))
+ continue;
+
+ reg_base = gaudi2_qm_blocks_bases[queue_id];
+ gaudi2_stop_qman_common(hdev, reg_base);
+ }
+}
+
+static void gaudi2_stall_dma_common(struct hl_device *hdev, u32 reg_base)
+{
+ u32 reg_val;
+
+ reg_val = FIELD_PREP(PDMA0_CORE_CFG_1_HALT_MASK, 0x1);
+ WREG32(reg_base + DMA_CORE_CFG_1_OFFSET, reg_val);
+}
+
+static void gaudi2_dma_stall(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int dcore, inst;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_PDMA_MASK))
+ goto stall_edma;
+
+ gaudi2_stall_dma_common(hdev, mmPDMA0_CORE_BASE);
+ gaudi2_stall_dma_common(hdev, mmPDMA1_CORE_BASE);
+
+stall_edma:
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_EDMA_MASK))
+ return;
+
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (inst = 0 ; inst < NUM_OF_EDMA_PER_DCORE ; inst++) {
+ u8 seq = dcore * NUM_OF_EDMA_PER_DCORE + inst;
+ u32 core_base;
+
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_EDMA_SHIFT + seq)))
+ continue;
+
+ core_base = mmDCORE0_EDMA0_CORE_BASE + dcore * DCORE_OFFSET +
+ inst * DCORE_EDMA_OFFSET;
+
+ /* Stall CPs of EDMA QMANs */
+ gaudi2_stall_dma_common(hdev, core_base);
+ }
+ }
+}
+
+static void gaudi2_mme_stall(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 offset, i;
+
+ offset = mmDCORE1_MME_CTRL_LO_QM_STALL - mmDCORE0_MME_CTRL_LO_QM_STALL;
+
+ for (i = 0 ; i < NUM_OF_DCORES ; i++)
+ if (gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_MME_SHIFT + i))
+ WREG32(mmDCORE0_MME_CTRL_LO_QM_STALL + (i * offset), 1);
+}
+
+static void gaudi2_tpc_stall(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+ int i;
+
+ if (!(gaudi2->tpc_hw_cap_initialized & HW_CAP_TPC_MASK))
+ return;
+
+ for (i = 0 ; i < TPC_ID_SIZE ; i++) {
+ if (!(gaudi2->tpc_hw_cap_initialized & BIT_ULL(HW_CAP_TPC_SHIFT + i)))
+ continue;
+
+ reg_base = gaudi2_tpc_cfg_blocks_bases[i];
+ WREG32(reg_base + TPC_CFG_STALL_OFFSET, 1);
+ }
+}
+
+static void gaudi2_rotator_stall(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_val;
+ int i;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_ROT_MASK))
+ return;
+
+ reg_val = FIELD_PREP(ROT_MSS_HALT_WBC_MASK, 0x1) |
+ FIELD_PREP(ROT_MSS_HALT_RSB_MASK, 0x1) |
+ FIELD_PREP(ROT_MSS_HALT_MRSB_MASK, 0x1);
+
+ for (i = 0 ; i < ROTATOR_ID_SIZE ; i++) {
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_ROT_SHIFT + i)))
+ continue;
+
+ WREG32(mmROT0_MSS_HALT + i * ROT_OFFSET, reg_val);
+ }
+}
+
+static void gaudi2_disable_qman_common(struct hl_device *hdev, u32 reg_base)
+{
+ WREG32(reg_base + QM_GLBL_CFG0_OFFSET, 0);
+}
+
+static void gaudi2_disable_dma_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int dcore, inst;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_PDMA_MASK))
+ goto stop_edma_qmans;
+
+ gaudi2_disable_qman_common(hdev, mmPDMA0_QM_BASE);
+ gaudi2_disable_qman_common(hdev, mmPDMA1_QM_BASE);
+
+stop_edma_qmans:
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_EDMA_MASK))
+ return;
+
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (inst = 0 ; inst < NUM_OF_EDMA_PER_DCORE ; inst++) {
+ u8 seq = dcore * NUM_OF_EDMA_PER_DCORE + inst;
+ u32 qm_base;
+
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_EDMA_SHIFT + seq)))
+ continue;
+
+ qm_base = mmDCORE0_EDMA0_QM_BASE + dcore * DCORE_OFFSET +
+ inst * DCORE_EDMA_OFFSET;
+
+ /* Disable CPs of EDMA QMANs */
+ gaudi2_disable_qman_common(hdev, qm_base);
+ }
+ }
+}
+
+static void gaudi2_disable_mme_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 offset, i;
+
+ offset = mmDCORE1_MME_QM_BASE - mmDCORE0_MME_QM_BASE;
+
+ for (i = 0 ; i < NUM_OF_DCORES ; i++)
+ if (gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_MME_SHIFT + i))
+ gaudi2_disable_qman_common(hdev, mmDCORE0_MME_QM_BASE + (i * offset));
+}
+
+static void gaudi2_disable_tpc_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+ int i;
+
+ if (!(gaudi2->tpc_hw_cap_initialized & HW_CAP_TPC_MASK))
+ return;
+
+ for (i = 0 ; i < TPC_ID_SIZE ; i++) {
+ if (!(gaudi2->tpc_hw_cap_initialized & BIT_ULL(HW_CAP_TPC_SHIFT + i)))
+ continue;
+
+ reg_base = gaudi2_qm_blocks_bases[gaudi2_tpc_id_to_queue_id[i]];
+ gaudi2_disable_qman_common(hdev, reg_base);
+ }
+}
+
+static void gaudi2_disable_rot_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+ int i;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_ROT_MASK))
+ return;
+
+ for (i = 0 ; i < ROTATOR_ID_SIZE ; i++) {
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_ROT_SHIFT + i)))
+ continue;
+
+ reg_base = gaudi2_qm_blocks_bases[gaudi2_rot_id_to_queue_id[i]];
+ gaudi2_disable_qman_common(hdev, reg_base);
+ }
+}
+
+static void gaudi2_disable_nic_qmans(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base, queue_id;
+ int i;
+
+ if (!(gaudi2->nic_hw_cap_initialized & HW_CAP_NIC_MASK))
+ return;
+
+ queue_id = GAUDI2_QUEUE_ID_NIC_0_0;
+
+ for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
+ if (!(hdev->nic_ports_mask & BIT(i)))
+ continue;
+
+ reg_base = gaudi2_qm_blocks_bases[queue_id];
+ gaudi2_disable_qman_common(hdev, reg_base);
+ }
+}
+
+static void gaudi2_enable_timestamp(struct hl_device *hdev)
+{
+ /* Disable the timestamp counter */
+ WREG32(mmPSOC_TIMESTAMP_BASE, 0);
+
+ /* Zero the lower/upper parts of the 64-bit counter */
+ WREG32(mmPSOC_TIMESTAMP_BASE + 0xC, 0);
+ WREG32(mmPSOC_TIMESTAMP_BASE + 0x8, 0);
+
+ /* Enable the counter */
+ WREG32(mmPSOC_TIMESTAMP_BASE, 1);
+}
+
+static void gaudi2_disable_timestamp(struct hl_device *hdev)
+{
+ /* Disable the timestamp counter */
+ WREG32(mmPSOC_TIMESTAMP_BASE, 0);
+}
+
+static const char *gaudi2_irq_name(u16 irq_number)
+{
+ switch (irq_number) {
+ case GAUDI2_IRQ_NUM_EVENT_QUEUE:
+ return "gaudi2 cpu eq";
+ case GAUDI2_IRQ_NUM_COMPLETION:
+ return "gaudi2 completion";
+ case GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM ... GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM:
+ return gaudi2_vdec_irq_name[irq_number - GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM];
+ case GAUDI2_IRQ_NUM_TPC_ASSERT:
+ return "gaudi2 tpc assert";
+ case GAUDI2_IRQ_NUM_UNEXPECTED_ERROR:
+ return "gaudi2 unexpected error";
+ case GAUDI2_IRQ_NUM_USER_FIRST ... GAUDI2_IRQ_NUM_USER_LAST:
+ return "gaudi2 user completion";
+ case GAUDI2_IRQ_NUM_EQ_ERROR:
+ return "gaudi2 eq error";
+ default:
+ return "invalid";
+ }
+}
+
+static void gaudi2_dec_disable_msix(struct hl_device *hdev, u32 max_irq_num)
+{
+ int i, irq, relative_idx;
+ struct hl_dec *dec;
+
+ for (i = GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM ; i < max_irq_num ; i++) {
+ irq = pci_irq_vector(hdev->pdev, i);
+ relative_idx = i - GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM;
+
+ dec = hdev->dec + relative_idx / 2;
+
+ /* We pass different structures depending on the irq handler. For the abnormal
+ * interrupt we pass hl_dec and for the regular interrupt we pass the relevant
+ * user_interrupt entry
+ */
+ free_irq(irq, ((relative_idx % 2) ?
+ (void *) dec :
+ (void *) &hdev->user_interrupt[dec->core_id]));
+ }
+}
+
+static int gaudi2_dec_enable_msix(struct hl_device *hdev)
+{
+ int rc, i, irq_init_cnt, irq, relative_idx;
+ struct hl_dec *dec;
+
+ for (i = GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM, irq_init_cnt = 0;
+ i <= GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM;
+ i++, irq_init_cnt++) {
+
+ irq = pci_irq_vector(hdev->pdev, i);
+ relative_idx = i - GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM;
+
+ /* We pass different structures depending on the irq handler. For the abnormal
+ * interrupt we pass hl_dec and for the regular interrupt we pass the relevant
+ * user_interrupt entry
+ *
+ * TODO: change the dec abnrm to threaded irq
+ */
+
+ dec = hdev->dec + relative_idx / 2;
+ if (relative_idx % 2) {
+ rc = request_irq(irq, hl_irq_handler_dec_abnrm, 0,
+ gaudi2_irq_name(i), (void *) dec);
+ } else {
+ rc = request_irq(irq, hl_irq_user_interrupt_handler, 0, gaudi2_irq_name(i),
+ (void *) &hdev->user_interrupt[dec->core_id]);
+ }
+
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_dec_irqs;
+ }
+ }
+
+ return 0;
+
+free_dec_irqs:
+ gaudi2_dec_disable_msix(hdev, (GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM + irq_init_cnt));
+ return rc;
+}
+
+static int gaudi2_enable_msix(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int rc, irq, i, j, user_irq_init_cnt;
+ struct hl_cq *cq;
+
+ if (gaudi2->hw_cap_initialized & HW_CAP_MSIX)
+ return 0;
+
+ hl_init_cpu_for_irq(hdev);
+
+ rc = pci_alloc_irq_vectors(hdev->pdev, GAUDI2_MSIX_ENTRIES, GAUDI2_MSIX_ENTRIES,
+ PCI_IRQ_MSIX);
+ if (rc < 0) {
+ dev_err(hdev->dev, "MSI-X: Failed to enable support -- %d/%d\n",
+ GAUDI2_MSIX_ENTRIES, rc);
+ return rc;
+ }
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_COMPLETION);
+ cq = &hdev->completion_queue[GAUDI2_RESERVED_CQ_CS_COMPLETION];
+ rc = request_irq(irq, hl_irq_handler_cq, 0, gaudi2_irq_name(GAUDI2_IRQ_NUM_COMPLETION), cq);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_irq_vectors;
+ }
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EVENT_QUEUE);
+ rc = request_irq(irq, hl_irq_handler_eq, 0, gaudi2_irq_name(GAUDI2_IRQ_NUM_EVENT_QUEUE),
+ &hdev->event_queue);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_completion_irq;
+ }
+
+ rc = gaudi2_dec_enable_msix(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to enable decoder IRQ");
+ goto free_event_irq;
+ }
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_TPC_ASSERT);
+ rc = request_threaded_irq(irq, NULL, hl_irq_user_interrupt_thread_handler, IRQF_ONESHOT,
+ gaudi2_irq_name(GAUDI2_IRQ_NUM_TPC_ASSERT),
+ &hdev->tpc_interrupt);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_dec_irq;
+ }
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_UNEXPECTED_ERROR);
+ rc = request_threaded_irq(irq, NULL, hl_irq_user_interrupt_thread_handler, IRQF_ONESHOT,
+ gaudi2_irq_name(GAUDI2_IRQ_NUM_UNEXPECTED_ERROR),
+ &hdev->unexpected_error_interrupt);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_tpc_irq;
+ }
+
+ for (i = GAUDI2_IRQ_NUM_USER_FIRST, j = prop->user_dec_intr_count, user_irq_init_cnt = 0;
+ user_irq_init_cnt < prop->user_interrupt_count;
+ i++, j++, user_irq_init_cnt++) {
+
+ irq = pci_irq_vector(hdev->pdev, i);
+ hl_set_irq_affinity(hdev, irq);
+ rc = request_irq(irq, hl_irq_user_interrupt_handler, 0, gaudi2_irq_name(i),
+ &hdev->user_interrupt[j]);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_user_irq;
+ }
+ }
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EQ_ERROR);
+ rc = request_threaded_irq(irq, NULL, hl_irq_eq_error_interrupt_thread_handler,
+ IRQF_ONESHOT, gaudi2_irq_name(GAUDI2_IRQ_NUM_EQ_ERROR),
+ hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to request IRQ %d", irq);
+ goto free_user_irq;
+ }
+
+ gaudi2->hw_cap_initialized |= HW_CAP_MSIX;
+
+ return 0;
+
+free_user_irq:
+ for (i = GAUDI2_IRQ_NUM_USER_FIRST, j = prop->user_dec_intr_count;
+ i < GAUDI2_IRQ_NUM_USER_FIRST + user_irq_init_cnt ; i++, j++) {
+
+ irq = pci_irq_vector(hdev->pdev, i);
+ irq_set_affinity_and_hint(irq, NULL);
+ free_irq(irq, &hdev->user_interrupt[j]);
+ }
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_UNEXPECTED_ERROR);
+ free_irq(irq, &hdev->unexpected_error_interrupt);
+free_tpc_irq:
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_TPC_ASSERT);
+ free_irq(irq, &hdev->tpc_interrupt);
+free_dec_irq:
+ gaudi2_dec_disable_msix(hdev, GAUDI2_IRQ_NUM_DEC_LAST + 1);
+free_event_irq:
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EVENT_QUEUE);
+ free_irq(irq, cq);
+
+free_completion_irq:
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_COMPLETION);
+ free_irq(irq, cq);
+
+free_irq_vectors:
+ pci_free_irq_vectors(hdev->pdev);
+
+ return rc;
+}
+
+static void gaudi2_sync_irqs(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int i, j;
+ int irq;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_MSIX))
+ return;
+
+ /* Wait for all pending IRQs to be finished */
+ synchronize_irq(pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_COMPLETION));
+
+ for (i = GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM ; i <= GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM ; i++) {
+ irq = pci_irq_vector(hdev->pdev, i);
+ synchronize_irq(irq);
+ }
+
+ synchronize_irq(pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_TPC_ASSERT));
+ synchronize_irq(pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_UNEXPECTED_ERROR));
+
+ for (i = GAUDI2_IRQ_NUM_USER_FIRST, j = 0 ; j < hdev->asic_prop.user_interrupt_count;
+ i++, j++) {
+ irq = pci_irq_vector(hdev->pdev, i);
+ synchronize_irq(irq);
+ }
+
+ synchronize_irq(pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EVENT_QUEUE));
+ synchronize_irq(pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EQ_ERROR));
+}
+
+static void gaudi2_disable_msix(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct hl_cq *cq;
+ int irq, i, j, k;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_MSIX))
+ return;
+
+ gaudi2_sync_irqs(hdev);
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EVENT_QUEUE);
+ free_irq(irq, &hdev->event_queue);
+
+ gaudi2_dec_disable_msix(hdev, GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM + 1);
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_TPC_ASSERT);
+ free_irq(irq, &hdev->tpc_interrupt);
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_UNEXPECTED_ERROR);
+ free_irq(irq, &hdev->unexpected_error_interrupt);
+
+ for (i = GAUDI2_IRQ_NUM_USER_FIRST, j = prop->user_dec_intr_count, k = 0;
+ k < hdev->asic_prop.user_interrupt_count ; i++, j++, k++) {
+
+ irq = pci_irq_vector(hdev->pdev, i);
+ irq_set_affinity_and_hint(irq, NULL);
+ free_irq(irq, &hdev->user_interrupt[j]);
+ }
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_COMPLETION);
+ cq = &hdev->completion_queue[GAUDI2_RESERVED_CQ_CS_COMPLETION];
+ free_irq(irq, cq);
+
+ irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EQ_ERROR);
+ free_irq(irq, hdev);
+
+ pci_free_irq_vectors(hdev->pdev);
+
+ gaudi2->hw_cap_initialized &= ~HW_CAP_MSIX;
+}
+
+static void gaudi2_stop_dcore_dec(struct hl_device *hdev, int dcore_id)
+{
+ u32 reg_val = FIELD_PREP(DCORE0_VDEC0_BRDG_CTRL_GRACEFUL_STOP_MASK, 0x1);
+ u32 graceful_pend_mask = DCORE0_VDEC0_BRDG_CTRL_GRACEFUL_PEND_MASK;
+ u32 timeout_usec, dec_id, dec_bit, offset, graceful;
+ int rc;
+
+ if (hdev->pldm)
+ timeout_usec = GAUDI2_PLDM_VDEC_TIMEOUT_USEC;
+ else
+ timeout_usec = GAUDI2_VDEC_TIMEOUT_USEC;
+
+ for (dec_id = 0 ; dec_id < NUM_OF_DEC_PER_DCORE ; dec_id++) {
+ dec_bit = dcore_id * NUM_OF_DEC_PER_DCORE + dec_id;
+ if (!(hdev->asic_prop.decoder_enabled_mask & BIT(dec_bit)))
+ continue;
+
+ offset = dcore_id * DCORE_OFFSET + dec_id * DCORE_VDEC_OFFSET;
+
+ WREG32(mmDCORE0_DEC0_CMD_SWREG16 + offset, 0);
+
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_GRACEFUL + offset, reg_val);
+
+ /* Wait till all traffic from decoder stops
+ * before apply core reset.
+ */
+ rc = hl_poll_timeout(
+ hdev,
+ mmDCORE0_VDEC0_BRDG_CTRL_GRACEFUL + offset,
+ graceful,
+ (graceful & graceful_pend_mask),
+ 100,
+ timeout_usec);
+ if (rc)
+ dev_err(hdev->dev,
+ "Failed to stop traffic from DCORE%d Decoder %d\n",
+ dcore_id, dec_id);
+ }
+}
+
+static void gaudi2_stop_pcie_dec(struct hl_device *hdev)
+{
+ u32 reg_val = FIELD_PREP(DCORE0_VDEC0_BRDG_CTRL_GRACEFUL_STOP_MASK, 0x1);
+ u32 graceful_pend_mask = PCIE_VDEC0_BRDG_CTRL_GRACEFUL_PEND_MASK;
+ u32 timeout_usec, dec_id, dec_bit, offset, graceful;
+ int rc;
+
+ if (hdev->pldm)
+ timeout_usec = GAUDI2_PLDM_VDEC_TIMEOUT_USEC;
+ else
+ timeout_usec = GAUDI2_VDEC_TIMEOUT_USEC;
+
+ for (dec_id = 0 ; dec_id < NUM_OF_DEC_PER_DCORE ; dec_id++) {
+ dec_bit = PCIE_DEC_SHIFT + dec_id;
+ if (!(hdev->asic_prop.decoder_enabled_mask & BIT(dec_bit)))
+ continue;
+
+ offset = dec_id * PCIE_VDEC_OFFSET;
+
+ WREG32(mmPCIE_DEC0_CMD_SWREG16 + offset, 0);
+
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_GRACEFUL + offset, reg_val);
+
+ /* Wait till all traffic from decoder stops
+ * before apply core reset.
+ */
+ rc = hl_poll_timeout(
+ hdev,
+ mmPCIE_VDEC0_BRDG_CTRL_GRACEFUL + offset,
+ graceful,
+ (graceful & graceful_pend_mask),
+ 100,
+ timeout_usec);
+ if (rc)
+ dev_err(hdev->dev,
+ "Failed to stop traffic from PCIe Decoder %d\n",
+ dec_id);
+ }
+}
+
+static void gaudi2_stop_dec(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int dcore_id;
+
+ if ((gaudi2->dec_hw_cap_initialized & HW_CAP_DEC_MASK) == 0)
+ return;
+
+ for (dcore_id = 0 ; dcore_id < NUM_OF_DCORES ; dcore_id++)
+ gaudi2_stop_dcore_dec(hdev, dcore_id);
+
+ gaudi2_stop_pcie_dec(hdev);
+}
+
+static void gaudi2_set_arc_running_mode(struct hl_device *hdev, u32 cpu_id, u32 run_mode)
+{
+ u32 reg_base, reg_val;
+
+ reg_base = gaudi2_arc_blocks_bases[cpu_id];
+ if (run_mode == HL_ENGINE_CORE_RUN)
+ reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_RUN_REQ_MASK, 1);
+ else
+ reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_HALT_REQ_MASK, 1);
+
+ WREG32(reg_base + ARC_HALT_REQ_OFFSET, reg_val);
+}
+
+static void gaudi2_halt_arcs(struct hl_device *hdev)
+{
+ u16 arc_id;
+
+ for (arc_id = CPU_ID_SCHED_ARC0; arc_id < CPU_ID_MAX; arc_id++) {
+ if (gaudi2_is_arc_enabled(hdev, arc_id))
+ gaudi2_set_arc_running_mode(hdev, arc_id, HL_ENGINE_CORE_HALT);
+ }
+}
+
+static int gaudi2_verify_arc_running_mode(struct hl_device *hdev, u32 cpu_id, u32 run_mode)
+{
+ int rc;
+ u32 reg_base, val, ack_mask, timeout_usec = 100000;
+
+ if (hdev->pldm)
+ timeout_usec *= 100;
+
+ reg_base = gaudi2_arc_blocks_bases[cpu_id];
+ if (run_mode == HL_ENGINE_CORE_RUN)
+ ack_mask = ARC_FARM_ARC0_AUX_RUN_HALT_ACK_RUN_ACK_MASK;
+ else
+ ack_mask = ARC_FARM_ARC0_AUX_RUN_HALT_ACK_HALT_ACK_MASK;
+
+ rc = hl_poll_timeout(hdev, reg_base + ARC_HALT_ACK_OFFSET,
+ val, ((val & ack_mask) == ack_mask),
+ 1000, timeout_usec);
+
+ if (!rc) {
+ /* Clear */
+ val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_RUN_REQ_MASK, 0);
+ WREG32(reg_base + ARC_HALT_REQ_OFFSET, val);
+ }
+
+ return rc;
+}
+
+static void gaudi2_reset_arcs(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u16 arc_id;
+
+ if (!gaudi2)
+ return;
+
+ for (arc_id = CPU_ID_SCHED_ARC0; arc_id < CPU_ID_MAX; arc_id++)
+ if (gaudi2_is_arc_enabled(hdev, arc_id))
+ gaudi2_clr_arc_id_cap(hdev, arc_id);
+}
+
+static void gaudi2_nic_qmans_manual_flush(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 queue_id;
+ int i;
+
+ if (!(gaudi2->nic_hw_cap_initialized & HW_CAP_NIC_MASK))
+ return;
+
+ queue_id = GAUDI2_QUEUE_ID_NIC_0_0;
+
+ for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
+ if (!(hdev->nic_ports_mask & BIT(i)))
+ continue;
+
+ gaudi2_qman_manual_flush_common(hdev, queue_id);
+ }
+}
+
+static int gaudi2_set_engine_cores(struct hl_device *hdev, u32 *core_ids,
+ u32 num_cores, u32 core_command)
+{
+ int i, rc;
+
+ for (i = 0 ; i < num_cores ; i++) {
+ if (gaudi2_is_arc_enabled(hdev, core_ids[i]))
+ gaudi2_set_arc_running_mode(hdev, core_ids[i], core_command);
+ }
+
+ for (i = 0 ; i < num_cores ; i++) {
+ if (gaudi2_is_arc_enabled(hdev, core_ids[i])) {
+ rc = gaudi2_verify_arc_running_mode(hdev, core_ids[i], core_command);
+
+ if (rc) {
+ dev_err(hdev->dev, "failed to %s arc: %d\n",
+ (core_command == HL_ENGINE_CORE_HALT) ?
+ "HALT" : "RUN", core_ids[i]);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_tpc_engine_mode(struct hl_device *hdev, u32 engine_id, u32 engine_command)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base, reg_addr, reg_val, tpc_id;
+
+ if (!(gaudi2->tpc_hw_cap_initialized & HW_CAP_TPC_MASK))
+ return 0;
+
+ tpc_id = gaudi2_tpc_engine_id_to_tpc_id[engine_id];
+ if (!(gaudi2->tpc_hw_cap_initialized & BIT_ULL(HW_CAP_TPC_SHIFT + tpc_id)))
+ return 0;
+
+ reg_base = gaudi2_tpc_cfg_blocks_bases[tpc_id];
+ reg_addr = reg_base + TPC_CFG_STALL_OFFSET;
+ reg_val = FIELD_PREP(DCORE0_TPC0_CFG_TPC_STALL_V_MASK,
+ (engine_command == HL_ENGINE_STALL) ? 1 : 0);
+ WREG32(reg_addr, reg_val);
+
+ if (engine_command == HL_ENGINE_RESUME) {
+ reg_base = gaudi2_tpc_eml_cfg_blocks_bases[tpc_id];
+ reg_addr = reg_base + TPC_EML_CFG_DBG_CNT_OFFSET;
+ RMWREG32(reg_addr, 0x1, DCORE0_TPC0_EML_CFG_DBG_CNT_DBG_EXIT_MASK);
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_mme_engine_mode(struct hl_device *hdev, u32 engine_id, u32 engine_command)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base, reg_addr, reg_val, mme_id;
+
+ mme_id = gaudi2_mme_engine_id_to_mme_id[engine_id];
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_MME_SHIFT + mme_id)))
+ return 0;
+
+ reg_base = gaudi2_mme_ctrl_lo_blocks_bases[mme_id];
+ reg_addr = reg_base + MME_CTRL_LO_QM_STALL_OFFSET;
+ reg_val = FIELD_PREP(DCORE0_MME_CTRL_LO_QM_STALL_V_MASK,
+ (engine_command == HL_ENGINE_STALL) ? 1 : 0);
+ WREG32(reg_addr, reg_val);
+
+ return 0;
+}
+
+static int gaudi2_set_edma_engine_mode(struct hl_device *hdev, u32 engine_id, u32 engine_command)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base, reg_addr, reg_val, edma_id;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_EDMA_MASK))
+ return 0;
+
+ edma_id = gaudi2_edma_engine_id_to_edma_id[engine_id];
+ if (!(gaudi2->hw_cap_initialized & BIT_ULL(HW_CAP_EDMA_SHIFT + edma_id)))
+ return 0;
+
+ reg_base = gaudi2_dma_core_blocks_bases[edma_id];
+ reg_addr = reg_base + EDMA_CORE_CFG_STALL_OFFSET;
+ reg_val = FIELD_PREP(DCORE0_EDMA0_CORE_CFG_1_HALT_MASK,
+ (engine_command == HL_ENGINE_STALL) ? 1 : 0);
+ WREG32(reg_addr, reg_val);
+
+ if (engine_command == HL_ENGINE_STALL) {
+ reg_val = FIELD_PREP(DCORE0_EDMA0_CORE_CFG_1_HALT_MASK, 0x1) |
+ FIELD_PREP(DCORE0_EDMA0_CORE_CFG_1_FLUSH_MASK, 0x1);
+ WREG32(reg_addr, reg_val);
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_engine_modes(struct hl_device *hdev,
+ u32 *engine_ids, u32 num_engines, u32 engine_command)
+{
+ int i, rc;
+
+ for (i = 0 ; i < num_engines ; ++i) {
+ switch (engine_ids[i]) {
+ case GAUDI2_DCORE0_ENGINE_ID_TPC_0 ... GAUDI2_DCORE0_ENGINE_ID_TPC_5:
+ case GAUDI2_DCORE1_ENGINE_ID_TPC_0 ... GAUDI2_DCORE1_ENGINE_ID_TPC_5:
+ case GAUDI2_DCORE2_ENGINE_ID_TPC_0 ... GAUDI2_DCORE2_ENGINE_ID_TPC_5:
+ case GAUDI2_DCORE3_ENGINE_ID_TPC_0 ... GAUDI2_DCORE3_ENGINE_ID_TPC_5:
+ rc = gaudi2_set_tpc_engine_mode(hdev, engine_ids[i], engine_command);
+ if (rc)
+ return rc;
+
+ break;
+ case GAUDI2_DCORE0_ENGINE_ID_MME:
+ case GAUDI2_DCORE1_ENGINE_ID_MME:
+ case GAUDI2_DCORE2_ENGINE_ID_MME:
+ case GAUDI2_DCORE3_ENGINE_ID_MME:
+ rc = gaudi2_set_mme_engine_mode(hdev, engine_ids[i], engine_command);
+ if (rc)
+ return rc;
+
+ break;
+ case GAUDI2_DCORE0_ENGINE_ID_EDMA_0 ... GAUDI2_DCORE0_ENGINE_ID_EDMA_1:
+ case GAUDI2_DCORE1_ENGINE_ID_EDMA_0 ... GAUDI2_DCORE1_ENGINE_ID_EDMA_1:
+ case GAUDI2_DCORE2_ENGINE_ID_EDMA_0 ... GAUDI2_DCORE2_ENGINE_ID_EDMA_1:
+ case GAUDI2_DCORE3_ENGINE_ID_EDMA_0 ... GAUDI2_DCORE3_ENGINE_ID_EDMA_1:
+ rc = gaudi2_set_edma_engine_mode(hdev, engine_ids[i], engine_command);
+ if (rc)
+ return rc;
+
+ break;
+ default:
+ dev_err(hdev->dev, "Invalid engine ID %u\n", engine_ids[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int gaudi2_set_engines(struct hl_device *hdev, u32 *engine_ids,
+ u32 num_engines, u32 engine_command)
+{
+ switch (engine_command) {
+ case HL_ENGINE_CORE_HALT:
+ case HL_ENGINE_CORE_RUN:
+ return gaudi2_set_engine_cores(hdev, engine_ids, num_engines, engine_command);
+
+ case HL_ENGINE_STALL:
+ case HL_ENGINE_RESUME:
+ return gaudi2_set_engine_modes(hdev, engine_ids, num_engines, engine_command);
+
+ default:
+ dev_err(hdev->dev, "failed to execute command id %u\n", engine_command);
+ return -EINVAL;
+ }
+}
+
+static void gaudi2_halt_engines(struct hl_device *hdev, bool hard_reset, bool fw_reset)
+{
+ u32 wait_timeout_ms;
+
+ if (hdev->pldm)
+ wait_timeout_ms = GAUDI2_PLDM_RESET_WAIT_MSEC;
+ else
+ wait_timeout_ms = GAUDI2_RESET_WAIT_MSEC;
+
+ if (fw_reset || hdev->cpld_shutdown)
+ goto skip_engines;
+
+ gaudi2_stop_dma_qmans(hdev);
+ gaudi2_stop_mme_qmans(hdev);
+ gaudi2_stop_tpc_qmans(hdev);
+ gaudi2_stop_rot_qmans(hdev);
+ gaudi2_stop_nic_qmans(hdev);
+ msleep(wait_timeout_ms);
+
+ gaudi2_halt_arcs(hdev);
+ gaudi2_dma_stall(hdev);
+ gaudi2_mme_stall(hdev);
+ gaudi2_tpc_stall(hdev);
+ gaudi2_rotator_stall(hdev);
+
+ msleep(wait_timeout_ms);
+
+ gaudi2_stop_dec(hdev);
+
+ /*
+ * in case of soft reset do a manual flush for QMANs (currently called
+ * only for NIC QMANs
+ */
+ if (!hard_reset)
+ gaudi2_nic_qmans_manual_flush(hdev);
+
+ gaudi2_disable_dma_qmans(hdev);
+ gaudi2_disable_mme_qmans(hdev);
+ gaudi2_disable_tpc_qmans(hdev);
+ gaudi2_disable_rot_qmans(hdev);
+ gaudi2_disable_nic_qmans(hdev);
+ gaudi2_disable_timestamp(hdev);
+
+skip_engines:
+ if (hard_reset) {
+ gaudi2_disable_msix(hdev);
+ return;
+ }
+
+ gaudi2_sync_irqs(hdev);
+}
+
+static void gaudi2_init_firmware_preload_params(struct hl_device *hdev)
+{
+ struct pre_fw_load_props *pre_fw_load = &hdev->fw_loader.pre_fw_load;
+
+ pre_fw_load->cpu_boot_status_reg = mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS;
+ pre_fw_load->sts_boot_dev_sts0_reg = mmCPU_BOOT_DEV_STS0;
+ pre_fw_load->sts_boot_dev_sts1_reg = mmCPU_BOOT_DEV_STS1;
+ pre_fw_load->boot_err0_reg = mmCPU_BOOT_ERR0;
+ pre_fw_load->boot_err1_reg = mmCPU_BOOT_ERR1;
+ pre_fw_load->wait_for_preboot_timeout = GAUDI2_PREBOOT_REQ_TIMEOUT_USEC;
+ pre_fw_load->wait_for_preboot_extended_timeout =
+ GAUDI2_PREBOOT_EXTENDED_REQ_TIMEOUT_USEC;
+}
+
+static void gaudi2_init_firmware_loader(struct hl_device *hdev)
+{
+ struct fw_load_mgr *fw_loader = &hdev->fw_loader;
+ struct dynamic_fw_load_mgr *dynamic_loader;
+ struct cpu_dyn_regs *dyn_regs;
+
+ /* fill common fields */
+ fw_loader->fw_comp_loaded = FW_TYPE_NONE;
+ fw_loader->boot_fit_img.image_name = GAUDI2_BOOT_FIT_FILE;
+ fw_loader->linux_img.image_name = GAUDI2_LINUX_FW_FILE;
+ fw_loader->boot_fit_timeout = GAUDI2_BOOT_FIT_REQ_TIMEOUT_USEC;
+ fw_loader->skip_bmc = false;
+ fw_loader->sram_bar_id = SRAM_CFG_BAR_ID;
+ fw_loader->dram_bar_id = DRAM_BAR_ID;
+ fw_loader->cpu_timeout = GAUDI2_CPU_TIMEOUT_USEC;
+
+ /* here we update initial values for few specific dynamic regs (as
+ * before reading the first descriptor from FW those value has to be
+ * hard-coded). in later stages of the protocol those values will be
+ * updated automatically by reading the FW descriptor so data there
+ * will always be up-to-date
+ */
+ dynamic_loader = &hdev->fw_loader.dynamic_loader;
+ dyn_regs = &dynamic_loader->comm_desc.cpu_dyn_regs;
+ dyn_regs->kmd_msg_to_cpu = cpu_to_le32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU);
+ dyn_regs->cpu_cmd_status_to_host = cpu_to_le32(mmCPU_CMD_STATUS_TO_HOST);
+ dynamic_loader->wait_for_bl_timeout = GAUDI2_WAIT_FOR_BL_TIMEOUT_USEC;
+}
+
+static int gaudi2_init_cpu(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int rc;
+
+ if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU))
+ return 0;
+
+ if (gaudi2->hw_cap_initialized & HW_CAP_CPU)
+ return 0;
+
+ rc = hl_fw_init_cpu(hdev);
+ if (rc)
+ return rc;
+
+ gaudi2->hw_cap_initialized |= HW_CAP_CPU;
+
+ return 0;
+}
+
+static int gaudi2_init_cpu_queues(struct hl_device *hdev, u32 cpu_timeout)
+{
+ struct hl_hw_queue *cpu_pq = &hdev->kernel_queues[GAUDI2_QUEUE_ID_CPU_PQ];
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct cpu_dyn_regs *dyn_regs;
+ struct hl_eq *eq;
+ u32 status;
+ int err;
+
+ if (!hdev->cpu_queues_enable)
+ return 0;
+
+ if (gaudi2->hw_cap_initialized & HW_CAP_CPU_Q)
+ return 0;
+
+ eq = &hdev->event_queue;
+
+ dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+
+ WREG32(mmCPU_IF_PQ_BASE_ADDR_LOW, lower_32_bits(cpu_pq->bus_address));
+ WREG32(mmCPU_IF_PQ_BASE_ADDR_HIGH, upper_32_bits(cpu_pq->bus_address));
+
+ WREG32(mmCPU_IF_EQ_BASE_ADDR_LOW, lower_32_bits(eq->bus_address));
+ WREG32(mmCPU_IF_EQ_BASE_ADDR_HIGH, upper_32_bits(eq->bus_address));
+
+ WREG32(mmCPU_IF_CQ_BASE_ADDR_LOW, lower_32_bits(hdev->cpu_accessible_dma_address));
+ WREG32(mmCPU_IF_CQ_BASE_ADDR_HIGH, upper_32_bits(hdev->cpu_accessible_dma_address));
+
+ WREG32(mmCPU_IF_PQ_LENGTH, HL_QUEUE_SIZE_IN_BYTES);
+ WREG32(mmCPU_IF_EQ_LENGTH, HL_EQ_SIZE_IN_BYTES);
+ WREG32(mmCPU_IF_CQ_LENGTH, HL_CPU_ACCESSIBLE_MEM_SIZE);
+
+ /* Used for EQ CI */
+ WREG32(mmCPU_IF_EQ_RD_OFFS, 0);
+
+ WREG32(mmCPU_IF_PF_PQ_PI, 0);
+
+ WREG32(mmCPU_IF_QUEUE_INIT, PQ_INIT_STATUS_READY_FOR_CP);
+
+ /* Let the ARC know we are ready as it is now handling those queues */
+
+ WREG32(le32_to_cpu(dyn_regs->gic_host_pi_upd_irq),
+ gaudi2_irq_map_table[GAUDI2_EVENT_CPU_PI_UPDATE].cpu_id);
+
+ err = hl_poll_timeout(
+ hdev,
+ mmCPU_IF_QUEUE_INIT,
+ status,
+ (status == PQ_INIT_STATUS_READY_FOR_HOST),
+ 1000,
+ cpu_timeout);
+
+ if (err) {
+ dev_err(hdev->dev, "Failed to communicate with device CPU (timeout)\n");
+ return -EIO;
+ }
+
+ /* update FW application security bits */
+ if (prop->fw_cpu_boot_dev_sts0_valid)
+ prop->fw_app_cpu_boot_dev_sts0 = RREG32(mmCPU_BOOT_DEV_STS0);
+
+ if (prop->fw_cpu_boot_dev_sts1_valid)
+ prop->fw_app_cpu_boot_dev_sts1 = RREG32(mmCPU_BOOT_DEV_STS1);
+
+ gaudi2->hw_cap_initialized |= HW_CAP_CPU_Q;
+ return 0;
+}
+
+static void gaudi2_init_qman_pq(struct hl_device *hdev, u32 reg_base,
+ u32 queue_id_base)
+{
+ struct hl_hw_queue *q;
+ u32 pq_id, pq_offset;
+
+ for (pq_id = 0 ; pq_id < NUM_OF_PQ_PER_QMAN ; pq_id++) {
+ q = &hdev->kernel_queues[queue_id_base + pq_id];
+ pq_offset = pq_id * 4;
+
+ if (q->dram_bd) {
+ WREG32(reg_base + QM_PQ_BASE_LO_0_OFFSET + pq_offset,
+ lower_32_bits(q->pq_dram_address));
+ WREG32(reg_base + QM_PQ_BASE_HI_0_OFFSET + pq_offset,
+ upper_32_bits(q->pq_dram_address));
+ } else {
+ WREG32(reg_base + QM_PQ_BASE_LO_0_OFFSET + pq_offset,
+ lower_32_bits(q->bus_address));
+ WREG32(reg_base + QM_PQ_BASE_HI_0_OFFSET + pq_offset,
+ upper_32_bits(q->bus_address));
+ }
+ WREG32(reg_base + QM_PQ_SIZE_0_OFFSET + pq_offset, ilog2(HL_QUEUE_LENGTH));
+ WREG32(reg_base + QM_PQ_PI_0_OFFSET + pq_offset, 0);
+ WREG32(reg_base + QM_PQ_CI_0_OFFSET + pq_offset, 0);
+ }
+}
+
+static void gaudi2_init_qman_cp(struct hl_device *hdev, u32 reg_base)
+{
+ u32 cp_id, cp_offset, mtr_base_lo, mtr_base_hi, so_base_lo, so_base_hi;
+
+ mtr_base_lo = lower_32_bits(CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0);
+ mtr_base_hi = upper_32_bits(CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0);
+ so_base_lo = lower_32_bits(CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0);
+ so_base_hi = upper_32_bits(CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0);
+
+ for (cp_id = 0 ; cp_id < NUM_OF_CP_PER_QMAN; cp_id++) {
+ cp_offset = cp_id * 4;
+
+ WREG32(reg_base + QM_CP_MSG_BASE0_ADDR_LO_0_OFFSET + cp_offset, mtr_base_lo);
+ WREG32(reg_base + QM_CP_MSG_BASE0_ADDR_HI_0_OFFSET + cp_offset, mtr_base_hi);
+ WREG32(reg_base + QM_CP_MSG_BASE1_ADDR_LO_0_OFFSET + cp_offset, so_base_lo);
+ WREG32(reg_base + QM_CP_MSG_BASE1_ADDR_HI_0_OFFSET + cp_offset, so_base_hi);
+ }
+
+ /* allow QMANs to accept work from ARC CQF */
+ WREG32(reg_base + QM_CP_CFG_OFFSET, FIELD_PREP(PDMA0_QM_CP_CFG_SWITCH_EN_MASK, 0x1));
+}
+
+static void gaudi2_init_qman_pqc(struct hl_device *hdev, u32 reg_base,
+ u32 queue_id_base)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 pq_id, pq_offset, so_base_lo, so_base_hi;
+
+ so_base_lo = lower_32_bits(CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0);
+ so_base_hi = upper_32_bits(CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0);
+
+ for (pq_id = 0 ; pq_id < NUM_OF_PQ_PER_QMAN ; pq_id++) {
+ pq_offset = pq_id * 4;
+
+ /* Configure QMAN HBW to scratchpad as it is not needed */
+ WREG32(reg_base + QM_PQC_HBW_BASE_LO_0_OFFSET + pq_offset,
+ lower_32_bits(gaudi2->scratchpad_bus_address));
+ WREG32(reg_base + QM_PQC_HBW_BASE_HI_0_OFFSET + pq_offset,
+ upper_32_bits(gaudi2->scratchpad_bus_address));
+ WREG32(reg_base + QM_PQC_SIZE_0_OFFSET + pq_offset,
+ ilog2(PAGE_SIZE / sizeof(struct hl_cq_entry)));
+
+ WREG32(reg_base + QM_PQC_PI_0_OFFSET + pq_offset, 0);
+ WREG32(reg_base + QM_PQC_LBW_WDATA_0_OFFSET + pq_offset, QM_PQC_LBW_WDATA);
+ WREG32(reg_base + QM_PQC_LBW_BASE_LO_0_OFFSET + pq_offset, so_base_lo);
+ WREG32(reg_base + QM_PQC_LBW_BASE_HI_0_OFFSET + pq_offset, so_base_hi);
+ }
+
+ /* Enable QMAN H/W completion */
+ WREG32(reg_base + QM_PQC_CFG_OFFSET, 1 << PDMA0_QM_PQC_CFG_EN_SHIFT);
+}
+
+static u32 gaudi2_get_dyn_sp_reg(struct hl_device *hdev, u32 queue_id_base)
+{
+ struct cpu_dyn_regs *dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+ u32 sp_reg_addr;
+
+ switch (queue_id_base) {
+ case GAUDI2_QUEUE_ID_PDMA_0_0...GAUDI2_QUEUE_ID_PDMA_1_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3:
+ sp_reg_addr = le32_to_cpu(dyn_regs->gic_dma_qm_irq_ctrl);
+ break;
+ case GAUDI2_QUEUE_ID_DCORE0_MME_0_0...GAUDI2_QUEUE_ID_DCORE0_MME_0_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE1_MME_0_0...GAUDI2_QUEUE_ID_DCORE1_MME_0_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE2_MME_0_0...GAUDI2_QUEUE_ID_DCORE2_MME_0_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE3_MME_0_0...GAUDI2_QUEUE_ID_DCORE3_MME_0_3:
+ sp_reg_addr = le32_to_cpu(dyn_regs->gic_mme_qm_irq_ctrl);
+ break;
+ case GAUDI2_QUEUE_ID_DCORE0_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE0_TPC_6_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE1_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE1_TPC_5_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE2_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE2_TPC_5_3:
+ fallthrough;
+ case GAUDI2_QUEUE_ID_DCORE3_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE3_TPC_5_3:
+ sp_reg_addr = le32_to_cpu(dyn_regs->gic_tpc_qm_irq_ctrl);
+ break;
+ case GAUDI2_QUEUE_ID_ROT_0_0...GAUDI2_QUEUE_ID_ROT_1_3:
+ sp_reg_addr = le32_to_cpu(dyn_regs->gic_rot_qm_irq_ctrl);
+ break;
+ case GAUDI2_QUEUE_ID_NIC_0_0...GAUDI2_QUEUE_ID_NIC_23_3:
+ sp_reg_addr = le32_to_cpu(dyn_regs->gic_nic_qm_irq_ctrl);
+ break;
+ default:
+ dev_err(hdev->dev, "Unexpected h/w queue %d\n", queue_id_base);
+ return 0;
+ }
+
+ return sp_reg_addr;
+}
+
+static void gaudi2_init_qman_common(struct hl_device *hdev, u32 reg_base,
+ u32 queue_id_base)
+{
+ u32 glbl_prot = QMAN_MAKE_TRUSTED, irq_handler_offset;
+ int map_table_entry;
+
+ WREG32(reg_base + QM_GLBL_PROT_OFFSET, glbl_prot);
+
+ irq_handler_offset = gaudi2_get_dyn_sp_reg(hdev, queue_id_base);
+ WREG32(reg_base + QM_GLBL_ERR_ADDR_LO_OFFSET, lower_32_bits(CFG_BASE + irq_handler_offset));
+ WREG32(reg_base + QM_GLBL_ERR_ADDR_HI_OFFSET, upper_32_bits(CFG_BASE + irq_handler_offset));
+
+ map_table_entry = gaudi2_qman_async_event_id[queue_id_base];
+ WREG32(reg_base + QM_GLBL_ERR_WDATA_OFFSET,
+ gaudi2_irq_map_table[map_table_entry].cpu_id);
+
+ WREG32(reg_base + QM_ARB_ERR_MSG_EN_OFFSET, QM_ARB_ERR_MSG_EN_MASK);
+
+ WREG32(reg_base + QM_ARB_SLV_CHOISE_WDT_OFFSET, GAUDI2_ARB_WDT_TIMEOUT);
+ WREG32(reg_base + QM_GLBL_CFG1_OFFSET, 0);
+ WREG32(reg_base + QM_GLBL_CFG2_OFFSET, 0);
+
+ /* Enable the QMAN channel.
+ * PDMA QMAN configuration is different, as we do not allow user to
+ * access some of the CPs.
+ * PDMA0: CP2/3 are reserved for the ARC usage.
+ * PDMA1: CP1/2/3 are reserved for the ARC usage.
+ */
+ if (reg_base == gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_PDMA_1_0])
+ WREG32(reg_base + QM_GLBL_CFG0_OFFSET, PDMA1_QMAN_ENABLE);
+ else if (reg_base == gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_PDMA_0_0])
+ WREG32(reg_base + QM_GLBL_CFG0_OFFSET, PDMA0_QMAN_ENABLE);
+ else
+ WREG32(reg_base + QM_GLBL_CFG0_OFFSET, QMAN_ENABLE);
+}
+
+static void gaudi2_init_qman(struct hl_device *hdev, u32 reg_base,
+ u32 queue_id_base)
+{
+ u32 pq_id;
+
+ for (pq_id = 0 ; pq_id < NUM_OF_PQ_PER_QMAN ; pq_id++)
+ hdev->kernel_queues[queue_id_base + pq_id].cq_id = GAUDI2_RESERVED_CQ_CS_COMPLETION;
+
+ gaudi2_init_qman_pq(hdev, reg_base, queue_id_base);
+ gaudi2_init_qman_cp(hdev, reg_base);
+ gaudi2_init_qman_pqc(hdev, reg_base, queue_id_base);
+ gaudi2_init_qman_common(hdev, reg_base, queue_id_base);
+}
+
+static void gaudi2_init_dma_core(struct hl_device *hdev, u32 reg_base,
+ u32 dma_core_id, bool is_secure)
+{
+ u32 prot, irq_handler_offset;
+ struct cpu_dyn_regs *dyn_regs;
+ int map_table_entry;
+
+ prot = 1 << ARC_FARM_KDMA_PROT_ERR_VAL_SHIFT;
+ if (is_secure)
+ prot |= 1 << ARC_FARM_KDMA_PROT_VAL_SHIFT;
+
+ WREG32(reg_base + DMA_CORE_PROT_OFFSET, prot);
+
+ dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+ irq_handler_offset = le32_to_cpu(dyn_regs->gic_dma_core_irq_ctrl);
+
+ WREG32(reg_base + DMA_CORE_ERRMSG_ADDR_LO_OFFSET,
+ lower_32_bits(CFG_BASE + irq_handler_offset));
+
+ WREG32(reg_base + DMA_CORE_ERRMSG_ADDR_HI_OFFSET,
+ upper_32_bits(CFG_BASE + irq_handler_offset));
+
+ map_table_entry = gaudi2_dma_core_async_event_id[dma_core_id];
+ WREG32(reg_base + DMA_CORE_ERRMSG_WDATA_OFFSET,
+ gaudi2_irq_map_table[map_table_entry].cpu_id);
+
+ /* Enable the DMA channel */
+ WREG32(reg_base + DMA_CORE_CFG_0_OFFSET, 1 << ARC_FARM_KDMA_CFG_0_EN_SHIFT);
+}
+
+static void gaudi2_init_kdma(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+
+ if ((gaudi2->hw_cap_initialized & HW_CAP_KDMA) == HW_CAP_KDMA)
+ return;
+
+ reg_base = gaudi2_dma_core_blocks_bases[DMA_CORE_ID_KDMA];
+
+ gaudi2_init_dma_core(hdev, reg_base, DMA_CORE_ID_KDMA, true);
+
+ gaudi2->hw_cap_initialized |= HW_CAP_KDMA;
+}
+
+static void gaudi2_init_pdma(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_base;
+
+ if ((gaudi2->hw_cap_initialized & HW_CAP_PDMA_MASK) == HW_CAP_PDMA_MASK)
+ return;
+
+ reg_base = gaudi2_dma_core_blocks_bases[DMA_CORE_ID_PDMA0];
+ gaudi2_init_dma_core(hdev, reg_base, DMA_CORE_ID_PDMA0, false);
+
+ reg_base = gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_PDMA_0_0];
+ gaudi2_init_qman(hdev, reg_base, GAUDI2_QUEUE_ID_PDMA_0_0);
+
+ reg_base = gaudi2_dma_core_blocks_bases[DMA_CORE_ID_PDMA1];
+ gaudi2_init_dma_core(hdev, reg_base, DMA_CORE_ID_PDMA1, false);
+
+ reg_base = gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_PDMA_1_0];
+ gaudi2_init_qman(hdev, reg_base, GAUDI2_QUEUE_ID_PDMA_1_0);
+
+ gaudi2->hw_cap_initialized |= HW_CAP_PDMA_MASK;
+}
+
+static void gaudi2_init_edma_instance(struct hl_device *hdev, u8 seq)
+{
+ u32 reg_base, base_edma_core_id, base_edma_qman_id;
+
+ base_edma_core_id = DMA_CORE_ID_EDMA0 + seq;
+ base_edma_qman_id = edma_stream_base[seq];
+
+ reg_base = gaudi2_dma_core_blocks_bases[base_edma_core_id];
+ gaudi2_init_dma_core(hdev, reg_base, base_edma_core_id, false);
+
+ reg_base = gaudi2_qm_blocks_bases[base_edma_qman_id];
+ gaudi2_init_qman(hdev, reg_base, base_edma_qman_id);
+}
+
+static void gaudi2_init_edma(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int dcore, inst;
+
+ if ((gaudi2->hw_cap_initialized & HW_CAP_EDMA_MASK) == HW_CAP_EDMA_MASK)
+ return;
+
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (inst = 0 ; inst < NUM_OF_EDMA_PER_DCORE ; inst++) {
+ u8 seq = dcore * NUM_OF_EDMA_PER_DCORE + inst;
+
+ if (!(prop->edma_enabled_mask & BIT(seq)))
+ continue;
+
+ gaudi2_init_edma_instance(hdev, seq);
+
+ gaudi2->hw_cap_initialized |= BIT_ULL(HW_CAP_EDMA_SHIFT + seq);
+ }
+ }
+}
+
+/*
+ * gaudi2_arm_monitors_for_virt_msix_db() - Arm monitors for writing to the virtual MSI-X doorbell.
+ * @hdev: pointer to habanalabs device structure.
+ * @sob_id: sync object ID.
+ * @first_mon_id: ID of first monitor out of 3 consecutive monitors.
+ * @interrupt_id: interrupt ID.
+ *
+ * Some initiators cannot have HBW address in their completion address registers, and thus cannot
+ * write directly to the HBW host memory of the virtual MSI-X doorbell.
+ * Instead, they are configured to LBW write to a sync object, and a monitor will do the HBW write.
+ *
+ * The mechanism in the sync manager block is composed of a master monitor with 3 messages.
+ * In addition to the HBW write, the other 2 messages are for preparing the monitor to next
+ * completion, by decrementing the sync object value and re-arming the monitor.
+ */
+static void gaudi2_arm_monitors_for_virt_msix_db(struct hl_device *hdev, u32 sob_id,
+ u32 first_mon_id, u32 interrupt_id)
+{
+ u32 sob_offset, first_mon_offset, mon_offset, payload, sob_group, mode, arm, config;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 addr;
+ u8 mask;
+
+ /* Reset the SOB value */
+ sob_offset = sob_id * sizeof(u32);
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_offset, 0);
+
+ /* Configure 3 monitors:
+ * 1. Write interrupt ID to the virtual MSI-X doorbell (master monitor)
+ * 2. Decrement SOB value by 1.
+ * 3. Re-arm the master monitor.
+ */
+
+ first_mon_offset = first_mon_id * sizeof(u32);
+
+ /* 2nd monitor: Decrement SOB value by 1 */
+ mon_offset = first_mon_offset + sizeof(u32);
+
+ addr = CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_offset;
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 + mon_offset, lower_32_bits(addr));
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRH_0 + mon_offset, upper_32_bits(addr));
+
+ payload = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_VAL_MASK, 0x7FFF) | /* "-1" */
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_SIGN_MASK, 1) |
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_INC_MASK, 1);
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_DATA_0 + mon_offset, payload);
+
+ /* 3rd monitor: Re-arm the master monitor */
+ mon_offset = first_mon_offset + 2 * sizeof(u32);
+
+ addr = CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_MON_ARM_0 + first_mon_offset;
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 + mon_offset, lower_32_bits(addr));
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRH_0 + mon_offset, upper_32_bits(addr));
+
+ sob_group = sob_id / 8;
+ mask = ~BIT(sob_id & 0x7);
+ mode = 0; /* comparison mode is "greater than or equal to" */
+ arm = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_SID_MASK, sob_group) |
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_MASK_MASK, mask) |
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_SOP_MASK, mode) |
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_SOD_MASK, 1);
+
+ payload = arm;
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_DATA_0 + mon_offset, payload);
+
+ /* 1st monitor (master): Write interrupt ID to the virtual MSI-X doorbell */
+ mon_offset = first_mon_offset;
+
+ config = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_CONFIG_WR_NUM_MASK, 2); /* "2": 3 writes */
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_CONFIG_0 + mon_offset, config);
+
+ addr = gaudi2->virt_msix_db_dma_addr;
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 + mon_offset, lower_32_bits(addr));
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRH_0 + mon_offset, upper_32_bits(addr));
+
+ payload = interrupt_id;
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_DATA_0 + mon_offset, payload);
+
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_ARM_0 + mon_offset, arm);
+}
+
+static void gaudi2_prepare_sm_for_virt_msix_db(struct hl_device *hdev)
+{
+ u32 decoder_id, sob_id, first_mon_id, interrupt_id;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ /* Decoder normal/abnormal interrupts */
+ for (decoder_id = 0 ; decoder_id < NUMBER_OF_DEC ; ++decoder_id) {
+ if (!(prop->decoder_enabled_mask & BIT(decoder_id)))
+ continue;
+
+ sob_id = GAUDI2_RESERVED_SOB_DEC_NRM_FIRST + decoder_id;
+ first_mon_id = GAUDI2_RESERVED_MON_DEC_NRM_FIRST + 3 * decoder_id;
+ interrupt_id = GAUDI2_IRQ_NUM_DCORE0_DEC0_NRM + 2 * decoder_id;
+ gaudi2_arm_monitors_for_virt_msix_db(hdev, sob_id, first_mon_id, interrupt_id);
+
+ sob_id = GAUDI2_RESERVED_SOB_DEC_ABNRM_FIRST + decoder_id;
+ first_mon_id = GAUDI2_RESERVED_MON_DEC_ABNRM_FIRST + 3 * decoder_id;
+ interrupt_id += 1;
+ gaudi2_arm_monitors_for_virt_msix_db(hdev, sob_id, first_mon_id, interrupt_id);
+ }
+}
+
+static void gaudi2_init_sm(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 cq_address;
+ u32 reg_val;
+ int i;
+
+ /* Enable HBW/LBW CQ for completion monitors */
+ reg_val = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_CONFIG_CQ_EN_MASK, 1);
+ reg_val |= FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_CONFIG_LBW_EN_MASK, 1);
+
+ for (i = 0 ; i < GAUDI2_MAX_PENDING_CS ; i++)
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_CONFIG_0 + (4 * i), reg_val);
+
+ /* Enable only HBW CQ for KDMA completion monitor */
+ reg_val = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_CONFIG_CQ_EN_MASK, 1);
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_CONFIG_0 + (4 * i), reg_val);
+
+ /* Init CQ0 DB - configure the monitor to trigger MSI-X interrupt */
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_0, lower_32_bits(gaudi2->virt_msix_db_dma_addr));
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_0, upper_32_bits(gaudi2->virt_msix_db_dma_addr));
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_LBW_DATA_0, GAUDI2_IRQ_NUM_COMPLETION);
+
+ for (i = 0 ; i < GAUDI2_RESERVED_CQ_NUMBER ; i++) {
+ cq_address =
+ hdev->completion_queue[i].bus_address;
+
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_0 + (4 * i),
+ lower_32_bits(cq_address));
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_0 + (4 * i),
+ upper_32_bits(cq_address));
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_0 + (4 * i),
+ ilog2(HL_CQ_SIZE_IN_BYTES));
+ }
+
+ /* Configure kernel ASID and MMU BP*/
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_ASID_SEC, 0x10000);
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_ASID_NONE_SEC_PRIV, 0);
+
+ /* Initialize sync objects and monitors which are used for the virtual MSI-X doorbell */
+ gaudi2_prepare_sm_for_virt_msix_db(hdev);
+}
+
+static void gaudi2_init_mme_acc(struct hl_device *hdev, u32 reg_base)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 reg_val;
+ int i;
+
+ reg_val = FIELD_PREP(MME_ACC_INTR_MASK_WBC_ERR_RESP_MASK, 0);
+ reg_val |= FIELD_PREP(MME_ACC_INTR_MASK_AP_SRC_POS_INF_MASK, 1);
+ reg_val |= FIELD_PREP(MME_ACC_INTR_MASK_AP_SRC_NEG_INF_MASK, 1);
+ reg_val |= FIELD_PREP(MME_ACC_INTR_MASK_AP_SRC_NAN_MASK, 1);
+ reg_val |= FIELD_PREP(MME_ACC_INTR_MASK_AP_RESULT_POS_INF_MASK, 1);
+ reg_val |= FIELD_PREP(MME_ACC_INTR_MASK_AP_RESULT_NEG_INF_MASK, 1);
+
+ WREG32(reg_base + MME_ACC_INTR_MASK_OFFSET, reg_val);
+ WREG32(reg_base + MME_ACC_AP_LFSR_POLY_OFFSET, 0x80DEADAF);
+
+ for (i = 0 ; i < MME_NUM_OF_LFSR_SEEDS ; i++) {
+ WREG32(reg_base + MME_ACC_AP_LFSR_SEED_SEL_OFFSET, i);
+ WREG32(reg_base + MME_ACC_AP_LFSR_SEED_WDATA_OFFSET, gaudi2->lfsr_rand_seeds[i]);
+ }
+}
+
+static void gaudi2_init_dcore_mme(struct hl_device *hdev, int dcore_id,
+ bool config_qman_only)
+{
+ u32 queue_id_base, reg_base;
+
+ switch (dcore_id) {
+ case 0:
+ queue_id_base = GAUDI2_QUEUE_ID_DCORE0_MME_0_0;
+ break;
+ case 1:
+ queue_id_base = GAUDI2_QUEUE_ID_DCORE1_MME_0_0;
+ break;
+ case 2:
+ queue_id_base = GAUDI2_QUEUE_ID_DCORE2_MME_0_0;
+ break;
+ case 3:
+ queue_id_base = GAUDI2_QUEUE_ID_DCORE3_MME_0_0;
+ break;
+ default:
+ dev_err(hdev->dev, "Invalid dcore id %u\n", dcore_id);
+ return;
+ }
+
+ if (!config_qman_only) {
+ reg_base = gaudi2_mme_acc_blocks_bases[dcore_id];
+ gaudi2_init_mme_acc(hdev, reg_base);
+ }
+
+ reg_base = gaudi2_qm_blocks_bases[queue_id_base];
+ gaudi2_init_qman(hdev, reg_base, queue_id_base);
+}
+
+static void gaudi2_init_mme(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int i;
+
+ if ((gaudi2->hw_cap_initialized & HW_CAP_MME_MASK) == HW_CAP_MME_MASK)
+ return;
+
+ for (i = 0 ; i < NUM_OF_DCORES ; i++) {
+ gaudi2_init_dcore_mme(hdev, i, false);
+
+ gaudi2->hw_cap_initialized |= BIT_ULL(HW_CAP_MME_SHIFT + i);
+ }
+}
+
+static void gaudi2_init_tpc_cfg(struct hl_device *hdev, u32 reg_base)
+{
+ /* Mask arithmetic and QM interrupts in TPC */
+ WREG32(reg_base + TPC_CFG_TPC_INTR_MASK_OFFSET, 0x23FFFE);
+
+ /* Set 16 cache lines */
+ WREG32(reg_base + TPC_CFG_MSS_CONFIG_OFFSET,
+ 2 << DCORE0_TPC0_CFG_MSS_CONFIG_ICACHE_FETCH_LINE_NUM_SHIFT);
+}
+
+struct gaudi2_tpc_init_cfg_data {
+ enum gaudi2_queue_id dcore_tpc_qid_base[NUM_OF_DCORES];
+};
+
+static void gaudi2_init_tpc_config(struct hl_device *hdev, int dcore, int inst,
+ u32 offset, struct iterate_module_ctx *ctx)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_tpc_init_cfg_data *cfg_data = ctx->data;
+ u32 queue_id_base;
+ u8 seq;
+
+ queue_id_base = cfg_data->dcore_tpc_qid_base[dcore] + (inst * NUM_OF_PQ_PER_QMAN);
+
+ if (dcore == 0 && inst == (NUM_DCORE0_TPC - 1))
+ /* gets last sequence number */
+ seq = NUM_OF_DCORES * NUM_OF_TPC_PER_DCORE;
+ else
+ seq = dcore * NUM_OF_TPC_PER_DCORE + inst;
+
+ gaudi2_init_tpc_cfg(hdev, mmDCORE0_TPC0_CFG_BASE + offset);
+ gaudi2_init_qman(hdev, mmDCORE0_TPC0_QM_BASE + offset, queue_id_base);
+
+ gaudi2->tpc_hw_cap_initialized |= BIT_ULL(HW_CAP_TPC_SHIFT + seq);
+}
+
+static void gaudi2_init_tpc(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_tpc_init_cfg_data init_cfg_data;
+ struct iterate_module_ctx tpc_iter;
+
+ if (!hdev->asic_prop.tpc_enabled_mask)
+ return;
+
+ if ((gaudi2->tpc_hw_cap_initialized & HW_CAP_TPC_MASK) == HW_CAP_TPC_MASK)
+ return;
+
+ init_cfg_data.dcore_tpc_qid_base[0] = GAUDI2_QUEUE_ID_DCORE0_TPC_0_0;
+ init_cfg_data.dcore_tpc_qid_base[1] = GAUDI2_QUEUE_ID_DCORE1_TPC_0_0;
+ init_cfg_data.dcore_tpc_qid_base[2] = GAUDI2_QUEUE_ID_DCORE2_TPC_0_0;
+ init_cfg_data.dcore_tpc_qid_base[3] = GAUDI2_QUEUE_ID_DCORE3_TPC_0_0;
+ tpc_iter.fn = &gaudi2_init_tpc_config;
+ tpc_iter.data = &init_cfg_data;
+ gaudi2_iterate_tpcs(hdev, &tpc_iter);
+}
+
+static void gaudi2_init_rotator(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 i, reg_base, queue_id;
+
+ queue_id = GAUDI2_QUEUE_ID_ROT_0_0;
+
+ for (i = 0 ; i < NUM_OF_ROT ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
+ reg_base = gaudi2_qm_blocks_bases[queue_id];
+ gaudi2_init_qman(hdev, reg_base, queue_id);
+
+ gaudi2->hw_cap_initialized |= BIT_ULL(HW_CAP_ROT_SHIFT + i);
+ }
+}
+
+static void gaudi2_init_vdec_brdg_ctrl(struct hl_device *hdev, u64 base_addr, u32 decoder_id)
+{
+ u32 sob_id;
+
+ /* VCMD normal interrupt */
+ sob_id = GAUDI2_RESERVED_SOB_DEC_NRM_FIRST + decoder_id;
+ WREG32(base_addr + BRDG_CTRL_NRM_MSIX_LBW_AWADDR,
+ mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_id * sizeof(u32));
+ WREG32(base_addr + BRDG_CTRL_NRM_MSIX_LBW_WDATA, GAUDI2_SOB_INCREMENT_BY_ONE);
+
+ /* VCMD abnormal interrupt */
+ sob_id = GAUDI2_RESERVED_SOB_DEC_ABNRM_FIRST + decoder_id;
+ WREG32(base_addr + BRDG_CTRL_ABNRM_MSIX_LBW_AWADDR,
+ mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_id * sizeof(u32));
+ WREG32(base_addr + BRDG_CTRL_ABNRM_MSIX_LBW_WDATA, GAUDI2_SOB_INCREMENT_BY_ONE);
+}
+
+static void gaudi2_init_dec(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 dcore_id, dec_id, dec_bit;
+ u64 base_addr;
+
+ if (!hdev->asic_prop.decoder_enabled_mask)
+ return;
+
+ if ((gaudi2->dec_hw_cap_initialized & HW_CAP_DEC_MASK) == HW_CAP_DEC_MASK)
+ return;
+
+ for (dcore_id = 0 ; dcore_id < NUM_OF_DCORES ; dcore_id++)
+ for (dec_id = 0 ; dec_id < NUM_OF_DEC_PER_DCORE ; dec_id++) {
+ dec_bit = dcore_id * NUM_OF_DEC_PER_DCORE + dec_id;
+
+ if (!(hdev->asic_prop.decoder_enabled_mask & BIT(dec_bit)))
+ continue;
+
+ base_addr = mmDCORE0_DEC0_CMD_BASE +
+ BRDG_CTRL_BLOCK_OFFSET +
+ dcore_id * DCORE_OFFSET +
+ dec_id * DCORE_VDEC_OFFSET;
+
+ gaudi2_init_vdec_brdg_ctrl(hdev, base_addr, dec_bit);
+
+ gaudi2->dec_hw_cap_initialized |= BIT_ULL(HW_CAP_DEC_SHIFT + dec_bit);
+ }
+
+ for (dec_id = 0 ; dec_id < NUM_OF_PCIE_VDEC ; dec_id++) {
+ dec_bit = PCIE_DEC_SHIFT + dec_id;
+ if (!(hdev->asic_prop.decoder_enabled_mask & BIT(dec_bit)))
+ continue;
+
+ base_addr = mmPCIE_DEC0_CMD_BASE + BRDG_CTRL_BLOCK_OFFSET +
+ dec_id * DCORE_VDEC_OFFSET;
+
+ gaudi2_init_vdec_brdg_ctrl(hdev, base_addr, dec_bit);
+
+ gaudi2->dec_hw_cap_initialized |= BIT_ULL(HW_CAP_DEC_SHIFT + dec_bit);
+ }
+}
+
+static int gaudi2_mmu_update_asid_hop0_addr(struct hl_device *hdev,
+ u32 stlb_base, u32 asid, u64 phys_addr)
+{
+ u32 status, timeout_usec;
+ int rc;
+
+ if (hdev->pldm || !hdev->pdev)
+ timeout_usec = GAUDI2_PLDM_MMU_TIMEOUT_USEC;
+ else
+ timeout_usec = MMU_CONFIG_TIMEOUT_USEC;
+
+ WREG32(stlb_base + STLB_ASID_OFFSET, asid);
+ WREG32(stlb_base + STLB_HOP0_PA43_12_OFFSET, phys_addr >> MMU_HOP0_PA43_12_SHIFT);
+ WREG32(stlb_base + STLB_HOP0_PA63_44_OFFSET, phys_addr >> MMU_HOP0_PA63_44_SHIFT);
+ WREG32(stlb_base + STLB_BUSY_OFFSET, 0x80000000);
+
+ rc = hl_poll_timeout(
+ hdev,
+ stlb_base + STLB_BUSY_OFFSET,
+ status,
+ !(status & 0x80000000),
+ 1000,
+ timeout_usec);
+
+ if (rc) {
+ dev_err(hdev->dev, "Timeout during MMU hop0 config of asid %d\n", asid);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void gaudi2_mmu_send_invalidate_cache_cmd(struct hl_device *hdev, u32 stlb_base,
+ u32 start_offset, u32 inv_start_val,
+ u32 flags)
+{
+ /* clear PMMU mem line cache (only needed in mmu range invalidation) */
+ if (flags & MMU_OP_CLEAR_MEMCACHE)
+ WREG32(mmPMMU_HBW_STLB_MEM_CACHE_INVALIDATION, 0x1);
+
+ if (flags & MMU_OP_SKIP_LOW_CACHE_INV)
+ return;
+
+ WREG32(stlb_base + start_offset, inv_start_val);
+}
+
+static int gaudi2_mmu_invalidate_cache_status_poll(struct hl_device *hdev, u32 stlb_base,
+ struct gaudi2_cache_invld_params *inv_params)
+{
+ u32 status, timeout_usec, start_offset;
+ int rc;
+
+ timeout_usec = (hdev->pldm) ? GAUDI2_PLDM_MMU_TIMEOUT_USEC :
+ GAUDI2_MMU_CACHE_INV_TIMEOUT_USEC;
+
+ /* poll PMMU mem line cache (only needed in mmu range invalidation) */
+ if (inv_params->flags & MMU_OP_CLEAR_MEMCACHE) {
+ rc = hl_poll_timeout(
+ hdev,
+ mmPMMU_HBW_STLB_MEM_CACHE_INV_STATUS,
+ status,
+ status & 0x1,
+ 1000,
+ timeout_usec);
+
+ if (rc)
+ return rc;
+
+ /* Need to manually reset the status to 0 */
+ WREG32(mmPMMU_HBW_STLB_MEM_CACHE_INV_STATUS, 0x0);
+ }
+
+ /* Lower cache does not work with cache lines, hence we can skip its
+ * invalidation upon map and invalidate only upon unmap
+ */
+ if (inv_params->flags & MMU_OP_SKIP_LOW_CACHE_INV)
+ return 0;
+
+ start_offset = inv_params->range_invalidation ?
+ STLB_RANGE_CACHE_INVALIDATION_OFFSET : STLB_INV_ALL_START_OFFSET;
+
+ rc = hl_poll_timeout(
+ hdev,
+ stlb_base + start_offset,
+ status,
+ !(status & 0x1),
+ 1000,
+ timeout_usec);
+
+ return rc;
+}
+
+bool gaudi2_is_hmmu_enabled(struct hl_device *hdev, int dcore_id, int hmmu_id)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 hw_cap;
+
+ hw_cap = HW_CAP_DCORE0_DMMU0 << (NUM_OF_HMMU_PER_DCORE * dcore_id + hmmu_id);
+
+ if (gaudi2->hw_cap_initialized & hw_cap)
+ return true;
+
+ return false;
+}
+
+/* this function shall be called only for HMMUs for which capability bit is set */
+static inline u32 get_hmmu_stlb_base(int dcore_id, int hmmu_id)
+{
+ u32 offset;
+
+ offset = (u32) (dcore_id * DCORE_OFFSET + hmmu_id * DCORE_HMMU_OFFSET);
+ return (u32)(mmDCORE0_HMMU0_STLB_BASE + offset);
+}
+
+static void gaudi2_mmu_invalidate_cache_trigger(struct hl_device *hdev, u32 stlb_base,
+ struct gaudi2_cache_invld_params *inv_params)
+{
+ u32 start_offset;
+
+ if (inv_params->range_invalidation) {
+ /* Set the addresses range
+ * Note: that the start address we set in register, is not included in
+ * the range of the invalidation, by design.
+ * that's why we need to set lower address than the one we actually
+ * want to be included in the range invalidation.
+ */
+ u64 start = inv_params->start_va - 1;
+
+ start_offset = STLB_RANGE_CACHE_INVALIDATION_OFFSET;
+
+ WREG32(stlb_base + STLB_RANGE_INV_START_LSB_OFFSET,
+ start >> MMU_RANGE_INV_VA_LSB_SHIFT);
+
+ WREG32(stlb_base + STLB_RANGE_INV_START_MSB_OFFSET,
+ start >> MMU_RANGE_INV_VA_MSB_SHIFT);
+
+ WREG32(stlb_base + STLB_RANGE_INV_END_LSB_OFFSET,
+ inv_params->end_va >> MMU_RANGE_INV_VA_LSB_SHIFT);
+
+ WREG32(stlb_base + STLB_RANGE_INV_END_MSB_OFFSET,
+ inv_params->end_va >> MMU_RANGE_INV_VA_MSB_SHIFT);
+ } else {
+ start_offset = STLB_INV_ALL_START_OFFSET;
+ }
+
+ gaudi2_mmu_send_invalidate_cache_cmd(hdev, stlb_base, start_offset,
+ inv_params->inv_start_val, inv_params->flags);
+}
+
+static inline void gaudi2_hmmu_invalidate_cache_trigger(struct hl_device *hdev,
+ int dcore_id, int hmmu_id,
+ struct gaudi2_cache_invld_params *inv_params)
+{
+ u32 stlb_base = get_hmmu_stlb_base(dcore_id, hmmu_id);
+
+ gaudi2_mmu_invalidate_cache_trigger(hdev, stlb_base, inv_params);
+}
+
+static inline int gaudi2_hmmu_invalidate_cache_status_poll(struct hl_device *hdev,
+ int dcore_id, int hmmu_id,
+ struct gaudi2_cache_invld_params *inv_params)
+{
+ u32 stlb_base = get_hmmu_stlb_base(dcore_id, hmmu_id);
+
+ return gaudi2_mmu_invalidate_cache_status_poll(hdev, stlb_base, inv_params);
+}
+
+static int gaudi2_hmmus_invalidate_cache(struct hl_device *hdev,
+ struct gaudi2_cache_invld_params *inv_params)
+{
+ int dcore_id, hmmu_id;
+
+ /* first send all invalidation commands */
+ for (dcore_id = 0 ; dcore_id < NUM_OF_DCORES ; dcore_id++) {
+ for (hmmu_id = 0 ; hmmu_id < NUM_OF_HMMU_PER_DCORE ; hmmu_id++) {
+ if (!gaudi2_is_hmmu_enabled(hdev, dcore_id, hmmu_id))
+ continue;
+
+ gaudi2_hmmu_invalidate_cache_trigger(hdev, dcore_id, hmmu_id, inv_params);
+ }
+ }
+
+ /* next, poll all invalidations status */
+ for (dcore_id = 0 ; dcore_id < NUM_OF_DCORES ; dcore_id++) {
+ for (hmmu_id = 0 ; hmmu_id < NUM_OF_HMMU_PER_DCORE ; hmmu_id++) {
+ int rc;
+
+ if (!gaudi2_is_hmmu_enabled(hdev, dcore_id, hmmu_id))
+ continue;
+
+ rc = gaudi2_hmmu_invalidate_cache_status_poll(hdev, dcore_id, hmmu_id,
+ inv_params);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int gaudi2_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_cache_invld_params invld_params;
+ int rc = 0;
+
+ if (hdev->reset_info.hard_reset_pending)
+ return rc;
+
+ invld_params.range_invalidation = false;
+ invld_params.inv_start_val = 1;
+
+ if ((flags & MMU_OP_USERPTR) && (gaudi2->hw_cap_initialized & HW_CAP_PMMU)) {
+ invld_params.flags = flags;
+ gaudi2_mmu_invalidate_cache_trigger(hdev, mmPMMU_HBW_STLB_BASE, &invld_params);
+ rc = gaudi2_mmu_invalidate_cache_status_poll(hdev, mmPMMU_HBW_STLB_BASE,
+ &invld_params);
+ } else if (flags & MMU_OP_PHYS_PACK) {
+ invld_params.flags = 0;
+ rc = gaudi2_hmmus_invalidate_cache(hdev, &invld_params);
+ }
+
+ return rc;
+}
+
+static int gaudi2_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,
+ u32 flags, u32 asid, u64 va, u64 size)
+{
+ struct gaudi2_cache_invld_params invld_params = {0};
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 start_va, end_va;
+ u32 inv_start_val;
+ int rc = 0;
+
+ if (hdev->reset_info.hard_reset_pending)
+ return 0;
+
+ inv_start_val = (1 << MMU_RANGE_INV_EN_SHIFT |
+ 1 << MMU_RANGE_INV_ASID_EN_SHIFT |
+ asid << MMU_RANGE_INV_ASID_SHIFT);
+ start_va = va;
+ end_va = start_va + size;
+
+ if ((flags & MMU_OP_USERPTR) && (gaudi2->hw_cap_initialized & HW_CAP_PMMU)) {
+ /* As range invalidation does not support zero address we will
+ * do full invalidation in this case
+ */
+ if (start_va) {
+ invld_params.range_invalidation = true;
+ invld_params.start_va = start_va;
+ invld_params.end_va = end_va;
+ invld_params.inv_start_val = inv_start_val;
+ invld_params.flags = flags | MMU_OP_CLEAR_MEMCACHE;
+ } else {
+ invld_params.range_invalidation = false;
+ invld_params.inv_start_val = 1;
+ invld_params.flags = flags;
+ }
+
+
+ gaudi2_mmu_invalidate_cache_trigger(hdev, mmPMMU_HBW_STLB_BASE, &invld_params);
+ rc = gaudi2_mmu_invalidate_cache_status_poll(hdev, mmPMMU_HBW_STLB_BASE,
+ &invld_params);
+ if (rc)
+ return rc;
+
+ } else if (flags & MMU_OP_PHYS_PACK) {
+ invld_params.start_va = gaudi2_mmu_scramble_addr(hdev, start_va);
+ invld_params.end_va = gaudi2_mmu_scramble_addr(hdev, end_va);
+ invld_params.inv_start_val = inv_start_val;
+ invld_params.flags = flags;
+ rc = gaudi2_hmmus_invalidate_cache(hdev, &invld_params);
+ }
+
+ return rc;
+}
+
+static int gaudi2_mmu_update_hop0_addr(struct hl_device *hdev, u32 stlb_base,
+ bool host_resident_pgt)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 hop0_addr;
+ u32 asid, max_asid = prop->max_asid;
+ int rc;
+
+ /* it takes too much time to init all of the ASIDs on palladium */
+ if (hdev->pldm)
+ max_asid = min((u32) 8, max_asid);
+
+ for (asid = 0 ; asid < max_asid ; asid++) {
+ if (host_resident_pgt)
+ hop0_addr = hdev->mmu_priv.hr.mmu_asid_hop0[asid].phys_addr;
+ else
+ hop0_addr = prop->mmu_pgt_addr + (asid * prop->dmmu.hop_table_size);
+
+ rc = gaudi2_mmu_update_asid_hop0_addr(hdev, stlb_base, asid, hop0_addr);
+ if (rc) {
+ dev_err(hdev->dev, "failed to set hop0 addr for asid %d\n", asid);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int gaudi2_mmu_init_common(struct hl_device *hdev, u32 mmu_base, u32 stlb_base,
+ bool host_resident_pgt)
+{
+ u32 status, timeout_usec;
+ int rc;
+
+ if (hdev->pldm || !hdev->pdev)
+ timeout_usec = GAUDI2_PLDM_MMU_TIMEOUT_USEC;
+ else
+ timeout_usec = GAUDI2_MMU_CACHE_INV_TIMEOUT_USEC;
+
+ WREG32(stlb_base + STLB_INV_ALL_START_OFFSET, 1);
+
+ rc = hl_poll_timeout(
+ hdev,
+ stlb_base + STLB_SRAM_INIT_OFFSET,
+ status,
+ !status,
+ 1000,
+ timeout_usec);
+
+ if (rc)
+ dev_notice_ratelimited(hdev->dev, "Timeout when waiting for MMU SRAM init\n");
+
+ rc = gaudi2_mmu_update_hop0_addr(hdev, stlb_base, host_resident_pgt);
+ if (rc)
+ return rc;
+
+ WREG32(mmu_base + MMU_BYPASS_OFFSET, 0);
+
+ rc = hl_poll_timeout(
+ hdev,
+ stlb_base + STLB_INV_ALL_START_OFFSET,
+ status,
+ !status,
+ 1000,
+ timeout_usec);
+
+ if (rc)
+ dev_notice_ratelimited(hdev->dev, "Timeout when waiting for MMU invalidate all\n");
+
+ WREG32(mmu_base + MMU_ENABLE_OFFSET, 1);
+
+ return rc;
+}
+
+static int gaudi2_pci_mmu_init(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 mmu_base, stlb_base;
+ int rc;
+
+ if (gaudi2->hw_cap_initialized & HW_CAP_PMMU)
+ return 0;
+
+ mmu_base = mmPMMU_HBW_MMU_BASE;
+ stlb_base = mmPMMU_HBW_STLB_BASE;
+
+ RMWREG32_SHIFTED(stlb_base + STLB_HOP_CONFIGURATION_OFFSET,
+ (0 << PMMU_HBW_STLB_HOP_CONFIGURATION_FIRST_HOP_SHIFT) |
+ (5 << PMMU_HBW_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_SMALL_P_SHIFT) |
+ (4 << PMMU_HBW_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_LARGE_P_SHIFT) |
+ (5 << PMMU_HBW_STLB_HOP_CONFIGURATION_LAST_HOP_SHIFT) |
+ (5 << PMMU_HBW_STLB_HOP_CONFIGURATION_FOLLOWER_HOP_SHIFT),
+ PMMU_HBW_STLB_HOP_CONFIGURATION_FIRST_HOP_MASK |
+ PMMU_HBW_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_SMALL_P_MASK |
+ PMMU_HBW_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_LARGE_P_MASK |
+ PMMU_HBW_STLB_HOP_CONFIGURATION_LAST_HOP_MASK |
+ PMMU_HBW_STLB_HOP_CONFIGURATION_FOLLOWER_HOP_MASK);
+
+ WREG32(stlb_base + STLB_LL_LOOKUP_MASK_63_32_OFFSET, 0);
+
+ if (PAGE_SIZE == SZ_64K) {
+ /* Set page sizes to 64K on hop5 and 16M on hop4 + enable 8 bit hops */
+ RMWREG32_SHIFTED(mmu_base + MMU_STATIC_MULTI_PAGE_SIZE_OFFSET,
+ FIELD_PREP(DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_HOP5_PAGE_SIZE_MASK, 4) |
+ FIELD_PREP(DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_HOP4_PAGE_SIZE_MASK, 3) |
+ FIELD_PREP(
+ DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_CFG_8_BITS_HOP_MODE_EN_MASK,
+ 1),
+ DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_HOP5_PAGE_SIZE_MASK |
+ DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_HOP4_PAGE_SIZE_MASK |
+ DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_CFG_8_BITS_HOP_MODE_EN_MASK);
+ }
+
+ WREG32(mmu_base + MMU_SPI_SEI_MASK_OFFSET, GAUDI2_PMMU_SPI_SEI_ENABLE_MASK);
+
+ rc = gaudi2_mmu_init_common(hdev, mmu_base, stlb_base, prop->pmmu.host_resident);
+ if (rc)
+ return rc;
+
+ gaudi2->hw_cap_initialized |= HW_CAP_PMMU;
+
+ return 0;
+}
+
+static int gaudi2_dcore_hmmu_init(struct hl_device *hdev, int dcore_id,
+ int hmmu_id)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 offset, mmu_base, stlb_base, hw_cap;
+ u8 dmmu_seq;
+ int rc;
+
+ dmmu_seq = NUM_OF_HMMU_PER_DCORE * dcore_id + hmmu_id;
+ hw_cap = HW_CAP_DCORE0_DMMU0 << dmmu_seq;
+
+ /*
+ * return if DMMU is already initialized or if it's not out of
+ * isolation (due to cluster binning)
+ */
+ if ((gaudi2->hw_cap_initialized & hw_cap) || !(prop->hmmu_hif_enabled_mask & BIT(dmmu_seq)))
+ return 0;
+
+ offset = (u32) (dcore_id * DCORE_OFFSET + hmmu_id * DCORE_HMMU_OFFSET);
+ mmu_base = mmDCORE0_HMMU0_MMU_BASE + offset;
+ stlb_base = mmDCORE0_HMMU0_STLB_BASE + offset;
+
+ RMWREG32(mmu_base + MMU_STATIC_MULTI_PAGE_SIZE_OFFSET, 5 /* 64MB */,
+ MMU_STATIC_MULTI_PAGE_SIZE_HOP4_PAGE_SIZE_MASK);
+
+ RMWREG32_SHIFTED(stlb_base + STLB_HOP_CONFIGURATION_OFFSET,
+ FIELD_PREP(DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FIRST_HOP_MASK, 0) |
+ FIELD_PREP(DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_SMALL_P_MASK, 3) |
+ FIELD_PREP(DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_LARGE_P_MASK, 3) |
+ FIELD_PREP(DCORE0_HMMU0_STLB_HOP_CONFIGURATION_LAST_HOP_MASK, 3) |
+ FIELD_PREP(DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FOLLOWER_HOP_MASK, 3),
+ DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FIRST_HOP_MASK |
+ DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_SMALL_P_MASK |
+ DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FIRST_LOOKUP_HOP_LARGE_P_MASK |
+ DCORE0_HMMU0_STLB_HOP_CONFIGURATION_LAST_HOP_MASK |
+ DCORE0_HMMU0_STLB_HOP_CONFIGURATION_FOLLOWER_HOP_MASK);
+
+ RMWREG32(stlb_base + STLB_HOP_CONFIGURATION_OFFSET, 1,
+ STLB_HOP_CONFIGURATION_ONLY_LARGE_PAGE_MASK);
+
+ WREG32(mmu_base + MMU_SPI_SEI_MASK_OFFSET, GAUDI2_HMMU_SPI_SEI_ENABLE_MASK);
+
+ rc = gaudi2_mmu_init_common(hdev, mmu_base, stlb_base, prop->dmmu.host_resident);
+ if (rc)
+ return rc;
+
+ gaudi2->hw_cap_initialized |= hw_cap;
+
+ return 0;
+}
+
+static int gaudi2_hbm_mmu_init(struct hl_device *hdev)
+{
+ int rc, dcore_id, hmmu_id;
+
+ for (dcore_id = 0 ; dcore_id < NUM_OF_DCORES ; dcore_id++)
+ for (hmmu_id = 0 ; hmmu_id < NUM_OF_HMMU_PER_DCORE; hmmu_id++) {
+ rc = gaudi2_dcore_hmmu_init(hdev, dcore_id, hmmu_id);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int gaudi2_mmu_init(struct hl_device *hdev)
+{
+ int rc;
+
+ rc = gaudi2_pci_mmu_init(hdev);
+ if (rc)
+ return rc;
+
+ rc = gaudi2_hbm_mmu_init(hdev);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int gaudi2_hw_init(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int rc;
+
+ /* Let's mark in the H/W that we have reached this point. We check
+ * this value in the reset_before_init function to understand whether
+ * we need to reset the chip before doing H/W init. This register is
+ * cleared by the H/W upon H/W reset
+ */
+ WREG32(mmHW_STATE, HL_DEVICE_HW_STATE_DIRTY);
+
+ /* Perform read from the device to make sure device is up */
+ RREG32(mmHW_STATE);
+
+ /* If iATU is done by FW, the HBM bar ALWAYS points to DRAM_PHYS_BASE.
+ * So we set it here and if anyone tries to move it later to
+ * a different address, there will be an error
+ */
+ if (hdev->asic_prop.iatu_done_by_fw)
+ gaudi2->dram_bar_cur_addr = DRAM_PHYS_BASE;
+
+ /*
+ * Before pushing u-boot/linux to device, need to set the hbm bar to
+ * base address of dram
+ */
+ if (gaudi2_set_hbm_bar_base(hdev, DRAM_PHYS_BASE) == U64_MAX) {
+ dev_err(hdev->dev, "failed to map HBM bar to DRAM base address\n");
+ return -EIO;
+ }
+
+ rc = gaudi2_init_cpu(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize CPU\n");
+ return rc;
+ }
+
+ gaudi2_init_scrambler_hbm(hdev);
+ gaudi2_init_kdma(hdev);
+
+ rc = gaudi2_init_cpu_queues(hdev, GAUDI2_CPU_TIMEOUT_USEC);
+ if (rc) {
+ dev_err(hdev->dev, "failed to initialize CPU H/W queues %d\n", rc);
+ return rc;
+ }
+
+ rc = gaudi2->cpucp_info_get(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to get cpucp info\n");
+ return rc;
+ }
+
+ rc = gaudi2_mmu_init(hdev);
+ if (rc)
+ return rc;
+
+ gaudi2_init_pdma(hdev);
+ gaudi2_init_edma(hdev);
+ gaudi2_init_sm(hdev);
+ gaudi2_init_tpc(hdev);
+ gaudi2_init_mme(hdev);
+ gaudi2_init_rotator(hdev);
+ gaudi2_init_dec(hdev);
+ gaudi2_enable_timestamp(hdev);
+
+ rc = gaudi2_coresight_init(hdev);
+ if (rc)
+ goto disable_queues;
+
+ rc = gaudi2_enable_msix(hdev);
+ if (rc)
+ goto disable_queues;
+
+ /* Perform read from the device to flush all configuration */
+ RREG32(mmHW_STATE);
+
+ return 0;
+
+disable_queues:
+ gaudi2_disable_dma_qmans(hdev);
+ gaudi2_disable_mme_qmans(hdev);
+ gaudi2_disable_tpc_qmans(hdev);
+ gaudi2_disable_rot_qmans(hdev);
+ gaudi2_disable_nic_qmans(hdev);
+
+ gaudi2_disable_timestamp(hdev);
+
+ return rc;
+}
+
+/**
+ * gaudi2_send_hard_reset_cmd - common function to handle reset
+ *
+ * @hdev: pointer to the habanalabs device structure
+ *
+ * This function handles the various possible scenarios for reset.
+ * It considers if reset is handled by driver\FW and what FW components are loaded
+ */
+static void gaudi2_send_hard_reset_cmd(struct hl_device *hdev)
+{
+ struct cpu_dyn_regs *dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+ bool heartbeat_reset, preboot_only, cpu_initialized = false;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 cpu_boot_status;
+
+ preboot_only = (hdev->fw_loader.fw_comp_loaded == FW_TYPE_PREBOOT_CPU);
+ heartbeat_reset = (hdev->reset_info.curr_reset_cause == HL_RESET_CAUSE_HEARTBEAT);
+
+ /*
+ * Handle corner case where failure was at cpu management app load,
+ * and driver didn't detect any failure while loading the FW,
+ * then at such scenario driver will send only HALT_MACHINE
+ * and no one will respond to this request since FW already back to preboot
+ * and it cannot handle such cmd.
+ * In this case next time the management app loads it'll check on events register
+ * which will still have the halt indication, and will reboot the device.
+ * The solution is to let preboot clear all relevant registers before next boot
+ * once driver send COMMS_RST_DEV.
+ */
+ cpu_boot_status = RREG32(mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS);
+
+ if (gaudi2 && (gaudi2->hw_cap_initialized & HW_CAP_CPU) &&
+ (cpu_boot_status == CPU_BOOT_STATUS_SRAM_AVAIL))
+ cpu_initialized = true;
+
+ /*
+ * when Linux/Bootfit exist this write to the SP can be interpreted in 2 ways:
+ * 1. FW reset: FW initiate the reset sequence
+ * 2. driver reset: FW will start HALT sequence (the preparations for the
+ * reset but not the reset itself as it is not implemented
+ * on their part) and LKD will wait to let FW complete the
+ * sequence before issuing the reset
+ */
+ if (!preboot_only && cpu_initialized) {
+ WREG32(le32_to_cpu(dyn_regs->gic_host_halt_irq),
+ gaudi2_irq_map_table[GAUDI2_EVENT_CPU_HALT_MACHINE].cpu_id);
+
+ msleep(GAUDI2_CPU_RESET_WAIT_MSEC);
+ }
+
+ /*
+ * When working with preboot (without Linux/Boot fit) we can
+ * communicate only using the COMMS commands to issue halt/reset.
+ *
+ * For the case in which we are working with Linux/Bootfit this is a hail-mary
+ * attempt to revive the card in the small chance that the f/w has
+ * experienced a watchdog event, which caused it to return back to preboot.
+ * In that case, triggering reset through GIC won't help. We need to
+ * trigger the reset as if Linux wasn't loaded.
+ *
+ * We do it only if the reset cause was HB, because that would be the
+ * indication of such an event.
+ *
+ * In case watchdog hasn't expired but we still got HB, then this won't
+ * do any damage.
+ */
+
+ if (heartbeat_reset || preboot_only || !cpu_initialized) {
+ if (hdev->asic_prop.hard_reset_done_by_fw)
+ hl_fw_ask_hard_reset_without_linux(hdev);
+ else
+ hl_fw_ask_halt_machine_without_linux(hdev);
+ }
+}
+
+/**
+ * gaudi2_execute_hard_reset - execute hard reset by driver/FW
+ *
+ * @hdev: pointer to the habanalabs device structure
+ *
+ * This function executes hard reset based on if driver/FW should do the reset
+ */
+static void gaudi2_execute_hard_reset(struct hl_device *hdev)
+{
+ if (hdev->asic_prop.hard_reset_done_by_fw) {
+ gaudi2_send_hard_reset_cmd(hdev);
+ return;
+ }
+
+ /* Set device to handle FLR by H/W as we will put the device
+ * CPU to halt mode
+ */
+ WREG32(mmPCIE_AUX_FLR_CTRL,
+ (PCIE_AUX_FLR_CTRL_HW_CTRL_MASK | PCIE_AUX_FLR_CTRL_INT_MASK_MASK));
+
+ gaudi2_send_hard_reset_cmd(hdev);
+
+ WREG32(mmPSOC_RESET_CONF_SW_ALL_RST, 1);
+}
+
+/**
+ * gaudi2_execute_soft_reset - execute soft reset by driver/FW
+ *
+ * @hdev: pointer to the habanalabs device structure
+ * @driver_performs_reset: true if driver should perform reset instead of f/w.
+ * @poll_timeout_us: time to wait for response from f/w.
+ *
+ * This function executes soft reset based on if driver/FW should do the reset
+ */
+static int gaudi2_execute_soft_reset(struct hl_device *hdev, bool driver_performs_reset,
+ u32 poll_timeout_us)
+{
+ if (!driver_performs_reset)
+ return hl_fw_send_soft_reset(hdev);
+
+ /* Block access to engines, QMANs and SM during reset, these
+ * RRs will be reconfigured after soft reset.
+ * PCIE_MSIX is left unsecured to allow NIC packets processing during the reset.
+ */
+ gaudi2_write_rr_to_all_lbw_rtrs(hdev, RR_TYPE_LONG, NUM_LONG_LBW_RR - 1,
+ mmDCORE0_TPC0_QM_DCCM_BASE, mmPCIE_MSIX_BASE);
+
+ gaudi2_write_rr_to_all_lbw_rtrs(hdev, RR_TYPE_LONG, NUM_LONG_LBW_RR - 2,
+ mmPCIE_MSIX_BASE + HL_BLOCK_SIZE,
+ mmPCIE_VDEC1_MSTR_IF_RR_SHRD_HBW_BASE + HL_BLOCK_SIZE);
+
+ WREG32(mmPSOC_RESET_CONF_SOFT_RST, 1);
+ return 0;
+}
+
+static void gaudi2_poll_btm_indication(struct hl_device *hdev, u32 poll_timeout_us)
+{
+ int i, rc = 0;
+ u32 reg_val;
+
+ /* We poll the BTM done indication multiple times after reset due to
+ * a HW errata 'GAUDI2_0300'
+ */
+ for (i = 0 ; i < GAUDI2_RESET_POLL_CNT ; i++)
+ rc = hl_poll_timeout(
+ hdev,
+ mmPSOC_GLOBAL_CONF_BTM_FSM,
+ reg_val,
+ reg_val == 0,
+ 1000,
+ poll_timeout_us);
+
+ if (rc)
+ dev_err(hdev->dev, "Timeout while waiting for device to reset 0x%x\n", reg_val);
+}
+
+static int gaudi2_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 poll_timeout_us, reset_sleep_ms;
+ bool driver_performs_reset = false;
+ int rc;
+
+ if (hdev->pldm) {
+ reset_sleep_ms = hard_reset ? GAUDI2_PLDM_HRESET_TIMEOUT_MSEC :
+ GAUDI2_PLDM_SRESET_TIMEOUT_MSEC;
+ poll_timeout_us = GAUDI2_PLDM_RESET_POLL_TIMEOUT_USEC;
+ } else {
+ reset_sleep_ms = GAUDI2_RESET_TIMEOUT_MSEC;
+ poll_timeout_us = GAUDI2_RESET_POLL_TIMEOUT_USEC;
+ }
+
+ if (fw_reset)
+ goto skip_reset;
+
+ gaudi2_reset_arcs(hdev);
+
+ if (hard_reset) {
+ driver_performs_reset = !hdev->asic_prop.hard_reset_done_by_fw;
+ gaudi2_execute_hard_reset(hdev);
+ } else {
+ /*
+ * As we have to support also work with preboot only (which does not supports
+ * soft reset) we have to make sure that security is disabled before letting driver
+ * do the reset. user shall control the BFE flags to avoid asking soft reset in
+ * secured device with preboot only.
+ */
+ driver_performs_reset = (hdev->fw_components == FW_TYPE_PREBOOT_CPU &&
+ !hdev->asic_prop.fw_security_enabled);
+ rc = gaudi2_execute_soft_reset(hdev, driver_performs_reset, poll_timeout_us);
+ if (rc)
+ return rc;
+ }
+
+skip_reset:
+ if (driver_performs_reset || hard_reset) {
+ /*
+ * Instead of waiting for BTM indication we should wait for preboot ready:
+ * Consider the below scenario:
+ * 1. FW update is being triggered
+ * - setting the dirty bit
+ * 2. hard reset will be triggered due to the dirty bit
+ * 3. FW initiates the reset:
+ * - dirty bit cleared
+ * - BTM indication cleared
+ * - preboot ready indication cleared
+ * 4. during hard reset:
+ * - BTM indication will be set
+ * - BIST test performed and another reset triggered
+ * 5. only after this reset the preboot will set the preboot ready
+ *
+ * when polling on BTM indication alone we can lose sync with FW while trying to
+ * communicate with FW that is during reset.
+ * to overcome this we will always wait to preboot ready indication
+ */
+
+ /* without this sleep reset will not work */
+ msleep(reset_sleep_ms);
+
+ if (hdev->fw_components & FW_TYPE_PREBOOT_CPU)
+ hl_fw_wait_preboot_ready(hdev);
+ else
+ gaudi2_poll_btm_indication(hdev, poll_timeout_us);
+ }
+
+ if (!gaudi2)
+ return 0;
+
+ gaudi2->dec_hw_cap_initialized &= ~(HW_CAP_DEC_MASK);
+ gaudi2->tpc_hw_cap_initialized &= ~(HW_CAP_TPC_MASK);
+
+ /*
+ * Clear NIC capability mask in order for driver to re-configure
+ * NIC QMANs. NIC ports will not be re-configured during soft
+ * reset as we call gaudi2_nic_init only during hard reset
+ */
+ gaudi2->nic_hw_cap_initialized &= ~(HW_CAP_NIC_MASK);
+
+ if (hard_reset) {
+ gaudi2->hw_cap_initialized &=
+ ~(HW_CAP_DRAM | HW_CAP_CLK_GATE | HW_CAP_HBM_SCRAMBLER_MASK |
+ HW_CAP_PMMU | HW_CAP_CPU | HW_CAP_CPU_Q |
+ HW_CAP_SRAM_SCRAMBLER | HW_CAP_DMMU_MASK |
+ HW_CAP_PDMA_MASK | HW_CAP_EDMA_MASK | HW_CAP_KDMA |
+ HW_CAP_MME_MASK | HW_CAP_ROT_MASK);
+
+ memset(gaudi2->events_stat, 0, sizeof(gaudi2->events_stat));
+ } else {
+ gaudi2->hw_cap_initialized &=
+ ~(HW_CAP_CLK_GATE | HW_CAP_HBM_SCRAMBLER_SW_RESET |
+ HW_CAP_PDMA_MASK | HW_CAP_EDMA_MASK | HW_CAP_MME_MASK |
+ HW_CAP_ROT_MASK);
+ }
+ return 0;
+}
+
+static int gaudi2_suspend(struct hl_device *hdev)
+{
+ return hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
+}
+
+static int gaudi2_resume(struct hl_device *hdev)
+{
+ return gaudi2_init_iatu(hdev);
+}
+
+static int gaudi2_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
+ void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+ int rc;
+
+ vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
+ VM_DONTCOPY | VM_NORESERVE);
+
+#ifdef _HAS_DMA_MMAP_COHERENT
+ /*
+ * If dma_alloc_coherent() returns a vmalloc address, set VM_MIXEDMAP
+ * so vm_insert_page() can handle it safely. Without this, the kernel
+ * may BUG_ON due to VM_PFNMAP.
+ */
+ if (is_vmalloc_addr(cpu_addr))
+ vm_flags_set(vma, VM_MIXEDMAP);
+
+ rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr, dma_addr, size);
+ if (rc)
+ dev_err(hdev->dev, "dma_mmap_coherent error %d", rc);
+
+#else
+
+ rc = remap_pfn_range(vma, vma->vm_start,
+ virt_to_phys(cpu_addr) >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (rc)
+ dev_err(hdev->dev, "remap_pfn_range error %d", rc);
+
+#endif
+
+ return rc;
+}
+
+static bool gaudi2_is_queue_enabled(struct hl_device *hdev, u32 hw_queue_id)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 hw_cap_mask = 0;
+ u64 hw_tpc_cap_bit = 0;
+ u64 hw_nic_cap_bit = 0;
+ u64 hw_test_cap_bit = 0;
+
+ switch (hw_queue_id) {
+ case GAUDI2_QUEUE_ID_PDMA_0_0:
+ case GAUDI2_QUEUE_ID_PDMA_0_1:
+ case GAUDI2_QUEUE_ID_PDMA_1_0:
+ hw_cap_mask = HW_CAP_PDMA_MASK;
+ break;
+ case GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3:
+ hw_test_cap_bit = HW_CAP_EDMA_SHIFT +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0) >> 2);
+ break;
+ case GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE1_EDMA_1_3:
+ hw_test_cap_bit = HW_CAP_EDMA_SHIFT + NUM_OF_EDMA_PER_DCORE +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0) >> 2);
+ break;
+ case GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE2_EDMA_1_3:
+ hw_test_cap_bit = HW_CAP_EDMA_SHIFT + 2 * NUM_OF_EDMA_PER_DCORE +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0) >> 2);
+ break;
+ case GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE3_EDMA_1_3:
+ hw_test_cap_bit = HW_CAP_EDMA_SHIFT + 3 * NUM_OF_EDMA_PER_DCORE +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0) >> 2);
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE0_MME_0_0 ... GAUDI2_QUEUE_ID_DCORE0_MME_0_3:
+ hw_test_cap_bit = HW_CAP_MME_SHIFT;
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE1_MME_0_0 ... GAUDI2_QUEUE_ID_DCORE1_MME_0_3:
+ hw_test_cap_bit = HW_CAP_MME_SHIFT + 1;
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE2_MME_0_0 ... GAUDI2_QUEUE_ID_DCORE2_MME_0_3:
+ hw_test_cap_bit = HW_CAP_MME_SHIFT + 2;
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE3_MME_0_0 ... GAUDI2_QUEUE_ID_DCORE3_MME_0_3:
+ hw_test_cap_bit = HW_CAP_MME_SHIFT + 3;
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE0_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE0_TPC_5_3:
+ hw_tpc_cap_bit = HW_CAP_TPC_SHIFT +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE0_TPC_0_0) >> 2);
+
+ /* special case where cap bit refers to the first queue id */
+ if (!hw_tpc_cap_bit)
+ return !!(gaudi2->tpc_hw_cap_initialized & BIT_ULL(0));
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE1_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE1_TPC_5_3:
+ hw_tpc_cap_bit = HW_CAP_TPC_SHIFT + NUM_OF_TPC_PER_DCORE +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE1_TPC_0_0) >> 2);
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE2_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE2_TPC_5_3:
+ hw_tpc_cap_bit = HW_CAP_TPC_SHIFT + (2 * NUM_OF_TPC_PER_DCORE) +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE2_TPC_0_0) >> 2);
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE3_TPC_0_0 ... GAUDI2_QUEUE_ID_DCORE3_TPC_5_3:
+ hw_tpc_cap_bit = HW_CAP_TPC_SHIFT + (3 * NUM_OF_TPC_PER_DCORE) +
+ ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE3_TPC_0_0) >> 2);
+ break;
+
+ case GAUDI2_QUEUE_ID_DCORE0_TPC_6_0 ... GAUDI2_QUEUE_ID_DCORE0_TPC_6_3:
+ hw_tpc_cap_bit = HW_CAP_TPC_SHIFT + (4 * NUM_OF_TPC_PER_DCORE);
+ break;
+
+ case GAUDI2_QUEUE_ID_ROT_0_0 ... GAUDI2_QUEUE_ID_ROT_1_3:
+ hw_test_cap_bit = HW_CAP_ROT_SHIFT + ((hw_queue_id - GAUDI2_QUEUE_ID_ROT_0_0) >> 2);
+ break;
+
+ case GAUDI2_QUEUE_ID_NIC_0_0 ... GAUDI2_QUEUE_ID_NIC_23_3:
+ hw_nic_cap_bit = HW_CAP_NIC_SHIFT + ((hw_queue_id - GAUDI2_QUEUE_ID_NIC_0_0) >> 2);
+
+ /* special case where cap bit refers to the first queue id */
+ if (!hw_nic_cap_bit)
+ return !!(gaudi2->nic_hw_cap_initialized & BIT_ULL(0));
+ break;
+
+ case GAUDI2_QUEUE_ID_CPU_PQ:
+ return !!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q);
+
+ default:
+ return false;
+ }
+
+ if (hw_tpc_cap_bit)
+ return !!(gaudi2->tpc_hw_cap_initialized & BIT_ULL(hw_tpc_cap_bit));
+
+ if (hw_nic_cap_bit)
+ return !!(gaudi2->nic_hw_cap_initialized & BIT_ULL(hw_nic_cap_bit));
+
+ if (hw_test_cap_bit)
+ hw_cap_mask = BIT_ULL(hw_test_cap_bit);
+
+ return !!(gaudi2->hw_cap_initialized & hw_cap_mask);
+}
+
+static bool gaudi2_is_arc_enabled(struct hl_device *hdev, u64 arc_id)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ switch (arc_id) {
+ case CPU_ID_SCHED_ARC0 ... CPU_ID_SCHED_ARC5:
+ case CPU_ID_MME_QMAN_ARC0...CPU_ID_ROT_QMAN_ARC1:
+ return !!(gaudi2->active_hw_arc & BIT_ULL(arc_id));
+
+ case CPU_ID_TPC_QMAN_ARC0...CPU_ID_TPC_QMAN_ARC24:
+ return !!(gaudi2->active_tpc_arc & BIT_ULL(arc_id - CPU_ID_TPC_QMAN_ARC0));
+
+ case CPU_ID_NIC_QMAN_ARC0...CPU_ID_NIC_QMAN_ARC23:
+ return !!(gaudi2->active_nic_arc & BIT_ULL(arc_id - CPU_ID_NIC_QMAN_ARC0));
+
+ default:
+ return false;
+ }
+}
+
+static void gaudi2_clr_arc_id_cap(struct hl_device *hdev, u64 arc_id)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ switch (arc_id) {
+ case CPU_ID_SCHED_ARC0 ... CPU_ID_SCHED_ARC5:
+ case CPU_ID_MME_QMAN_ARC0...CPU_ID_ROT_QMAN_ARC1:
+ gaudi2->active_hw_arc &= ~(BIT_ULL(arc_id));
+ break;
+
+ case CPU_ID_TPC_QMAN_ARC0...CPU_ID_TPC_QMAN_ARC24:
+ gaudi2->active_tpc_arc &= ~(BIT_ULL(arc_id - CPU_ID_TPC_QMAN_ARC0));
+ break;
+
+ case CPU_ID_NIC_QMAN_ARC0...CPU_ID_NIC_QMAN_ARC23:
+ gaudi2->active_nic_arc &= ~(BIT_ULL(arc_id - CPU_ID_NIC_QMAN_ARC0));
+ break;
+
+ default:
+ return;
+ }
+}
+
+static void gaudi2_set_arc_id_cap(struct hl_device *hdev, u64 arc_id)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ switch (arc_id) {
+ case CPU_ID_SCHED_ARC0 ... CPU_ID_SCHED_ARC5:
+ case CPU_ID_MME_QMAN_ARC0...CPU_ID_ROT_QMAN_ARC1:
+ gaudi2->active_hw_arc |= BIT_ULL(arc_id);
+ break;
+
+ case CPU_ID_TPC_QMAN_ARC0...CPU_ID_TPC_QMAN_ARC24:
+ gaudi2->active_tpc_arc |= BIT_ULL(arc_id - CPU_ID_TPC_QMAN_ARC0);
+ break;
+
+ case CPU_ID_NIC_QMAN_ARC0...CPU_ID_NIC_QMAN_ARC23:
+ gaudi2->active_nic_arc |= BIT_ULL(arc_id - CPU_ID_NIC_QMAN_ARC0);
+ break;
+
+ default:
+ return;
+ }
+}
+
+static void gaudi2_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi)
+{
+ struct cpu_dyn_regs *dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+ u32 pq_offset, reg_base, db_reg_offset, db_value;
+
+ if (hw_queue_id != GAUDI2_QUEUE_ID_CPU_PQ) {
+ /*
+ * QMAN has 4 successive PQ_PI registers, 1 for each of the QMAN PQs.
+ * Masking the H/W queue ID with 0x3 extracts the QMAN internal PQ
+ * number.
+ */
+ pq_offset = (hw_queue_id & 0x3) * 4;
+ reg_base = gaudi2_qm_blocks_bases[hw_queue_id];
+ db_reg_offset = reg_base + QM_PQ_PI_0_OFFSET + pq_offset;
+ } else {
+ db_reg_offset = mmCPU_IF_PF_PQ_PI;
+ }
+
+ db_value = pi;
+
+ /* ring the doorbell */
+ WREG32(db_reg_offset, db_value);
+
+ if (hw_queue_id == GAUDI2_QUEUE_ID_CPU_PQ) {
+ /* make sure device CPU will read latest data from host */
+ mb();
+ WREG32(le32_to_cpu(dyn_regs->gic_host_pi_upd_irq),
+ gaudi2_irq_map_table[GAUDI2_EVENT_CPU_PI_UPDATE].cpu_id);
+ }
+}
+
+static void gaudi2_pqe_write(struct hl_device *hdev, __le64 *pqe, struct hl_bd *bd)
+{
+ __le64 *pbd = (__le64 *) bd;
+
+ /* The QMANs are on the host memory so a simple copy suffice */
+ pqe[0] = pbd[0];
+ pqe[1] = pbd[1];
+}
+
+static void *gaudi2_dma_alloc_coherent(struct hl_device *hdev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flags)
+{
+ return dma_alloc_coherent(&hdev->pdev->dev, size, dma_handle, flags);
+}
+
+static void gaudi2_dma_free_coherent(struct hl_device *hdev, size_t size,
+ void *cpu_addr, dma_addr_t dma_handle)
+{
+ dma_free_coherent(&hdev->pdev->dev, size, cpu_addr, dma_handle);
+}
+
+static int gaudi2_send_cpu_message(struct hl_device *hdev, u32 *msg, u16 len,
+ u32 timeout, u64 *result)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q)) {
+ if (result)
+ *result = 0;
+ return 0;
+ }
+
+ if (!timeout)
+ timeout = GAUDI2_MSG_TO_CPU_TIMEOUT_USEC;
+
+ return hl_fw_send_cpu_message(hdev, GAUDI2_QUEUE_ID_CPU_PQ, msg, len, timeout, result);
+}
+
+static void *gaudi2_dma_pool_zalloc(struct hl_device *hdev, size_t size,
+ gfp_t mem_flags, dma_addr_t *dma_handle)
+{
+ if (size > GAUDI2_DMA_POOL_BLK_SIZE)
+ return NULL;
+
+ return dma_pool_zalloc(hdev->dma_pool, mem_flags, dma_handle);
+}
+
+static void gaudi2_dma_pool_free(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr)
+{
+ dma_pool_free(hdev->dma_pool, vaddr, dma_addr);
+}
+
+static void *gaudi2_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ return hl_fw_cpu_accessible_dma_pool_alloc(hdev, size, dma_handle);
+}
+
+static void gaudi2_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr)
+{
+ hl_fw_cpu_accessible_dma_pool_free(hdev, size, vaddr);
+}
+
+static int gaudi2_validate_cb_address(struct hl_device *hdev, struct hl_cs_parser *parser)
+{
+ struct asic_fixed_properties *asic_prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!gaudi2_is_queue_enabled(hdev, parser->hw_queue_id)) {
+ dev_err(hdev->dev, "h/w queue %s is disabled\n",
+ GAUDI2_QUEUE_ID_TO_STR(parser->hw_queue_id));
+ return -EINVAL;
+ }
+
+ /* Just check if CB address is valid */
+
+ if (hl_mem_area_inside_range((u64) (uintptr_t) parser->user_cb,
+ parser->user_cb_size,
+ asic_prop->sram_user_base_address,
+ asic_prop->sram_end_address))
+ return 0;
+
+ if (hl_mem_area_inside_range((u64) (uintptr_t) parser->user_cb,
+ parser->user_cb_size,
+ asic_prop->dram_user_base_address,
+ asic_prop->dram_end_address))
+ return 0;
+
+ if ((gaudi2->hw_cap_initialized & HW_CAP_DMMU_MASK) &&
+ hl_mem_area_inside_range((u64) (uintptr_t) parser->user_cb,
+ parser->user_cb_size,
+ asic_prop->dmmu.start_addr,
+ asic_prop->dmmu.end_addr))
+ return 0;
+
+ if (gaudi2->hw_cap_initialized & HW_CAP_PMMU) {
+ if (hl_mem_area_inside_range((u64) (uintptr_t) parser->user_cb,
+ parser->user_cb_size,
+ asic_prop->pmmu.start_addr,
+ asic_prop->pmmu.end_addr) ||
+ hl_mem_area_inside_range(
+ (u64) (uintptr_t) parser->user_cb,
+ parser->user_cb_size,
+ asic_prop->pmmu_huge.start_addr,
+ asic_prop->pmmu_huge.end_addr))
+ return 0;
+
+ } else if (gaudi2_host_phys_addr_valid((u64) (uintptr_t) parser->user_cb)) {
+ if (!hdev->pdev)
+ return 0;
+
+ if (!device_iommu_mapped(&hdev->pdev->dev))
+ return 0;
+ }
+
+ dev_err(hdev->dev, "CB address %p + 0x%x for internal QMAN is not valid\n",
+ parser->user_cb, parser->user_cb_size);
+
+ return -EFAULT;
+}
+
+static int gaudi2_cs_parser(struct hl_device *hdev, struct hl_cs_parser *parser)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!parser->is_kernel_allocated_cb)
+ return gaudi2_validate_cb_address(hdev, parser);
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_PMMU)) {
+ dev_err(hdev->dev, "PMMU not initialized - Unsupported mode in Gaudi2\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gaudi2_send_heartbeat(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q))
+ return 0;
+
+ return hl_fw_send_heartbeat(hdev);
+}
+
+/* This is an internal helper function, used to update the KDMA mmu props.
+ * Should be called with a proper kdma lock.
+ */
+static void gaudi2_kdma_set_mmbp_asid(struct hl_device *hdev,
+ bool mmu_bypass, u32 asid)
+{
+ u32 rw_asid, rw_mmu_bp;
+
+ rw_asid = (asid << ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_RD_SHIFT) |
+ (asid << ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_WR_SHIFT);
+
+ rw_mmu_bp = (!!mmu_bypass << ARC_FARM_KDMA_CTX_AXUSER_HB_MMU_BP_RD_SHIFT) |
+ (!!mmu_bypass << ARC_FARM_KDMA_CTX_AXUSER_HB_MMU_BP_WR_SHIFT);
+
+ WREG32(mmARC_FARM_KDMA_CTX_AXUSER_HB_ASID, rw_asid);
+ WREG32(mmARC_FARM_KDMA_CTX_AXUSER_HB_MMU_BP, rw_mmu_bp);
+}
+
+static void gaudi2_arm_cq_monitor(struct hl_device *hdev, u32 sob_id, u32 mon_id, u32 cq_id,
+ u32 mon_payload, u32 sync_value)
+{
+ u32 sob_offset, mon_offset, sync_group_id, mode, mon_arm;
+ u8 mask;
+
+ sob_offset = sob_id * 4;
+ mon_offset = mon_id * 4;
+
+ /* Reset the SOB value */
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_offset, 0);
+
+ /* Configure this address with CQ_ID 0 because CQ_EN is set */
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 + mon_offset, cq_id);
+
+ /* Configure this address with CS index because CQ_EN is set */
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_DATA_0 + mon_offset, mon_payload);
+
+ sync_group_id = sob_id / 8;
+ mask = ~(1 << (sob_id & 0x7));
+ mode = 1; /* comparison mode is "equal to" */
+
+ mon_arm = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_SOD_MASK, sync_value);
+ mon_arm |= FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_SOP_MASK, mode);
+ mon_arm |= FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_MASK_MASK, mask);
+ mon_arm |= FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_MON_ARM_SID_MASK, sync_group_id);
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_MON_ARM_0 + mon_offset, mon_arm);
+}
+
+/* This is an internal helper function used by gaudi2_send_job_to_kdma only */
+static int gaudi2_send_job_to_kdma(struct hl_device *hdev,
+ u64 src_addr, u64 dst_addr,
+ u32 size, bool is_memset)
+{
+ u32 comp_val, commit_mask, *polling_addr, timeout, status = 0;
+ struct hl_cq_entry *cq_base;
+ struct hl_cq *cq;
+ u64 comp_addr;
+ int rc;
+
+ gaudi2_arm_cq_monitor(hdev, GAUDI2_RESERVED_SOB_KDMA_COMPLETION,
+ GAUDI2_RESERVED_MON_KDMA_COMPLETION,
+ GAUDI2_RESERVED_CQ_KDMA_COMPLETION, 1, 1);
+
+ comp_addr = CFG_BASE + mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 +
+ (GAUDI2_RESERVED_SOB_KDMA_COMPLETION * sizeof(u32));
+
+ comp_val = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_INC_MASK, 1) |
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_VAL_MASK, 1);
+
+ WREG32(mmARC_FARM_KDMA_CTX_SRC_BASE_LO, lower_32_bits(src_addr));
+ WREG32(mmARC_FARM_KDMA_CTX_SRC_BASE_HI, upper_32_bits(src_addr));
+ WREG32(mmARC_FARM_KDMA_CTX_DST_BASE_LO, lower_32_bits(dst_addr));
+ WREG32(mmARC_FARM_KDMA_CTX_DST_BASE_HI, upper_32_bits(dst_addr));
+ WREG32(mmARC_FARM_KDMA_CTX_WR_COMP_ADDR_LO, lower_32_bits(comp_addr));
+ WREG32(mmARC_FARM_KDMA_CTX_WR_COMP_ADDR_HI, upper_32_bits(comp_addr));
+ WREG32(mmARC_FARM_KDMA_CTX_WR_COMP_WDATA, comp_val);
+ WREG32(mmARC_FARM_KDMA_CTX_DST_TSIZE_0, size);
+
+ commit_mask = FIELD_PREP(ARC_FARM_KDMA_CTX_COMMIT_LIN_MASK, 1) |
+ FIELD_PREP(ARC_FARM_KDMA_CTX_COMMIT_WR_COMP_EN_MASK, 1);
+
+ if (is_memset)
+ commit_mask |= FIELD_PREP(ARC_FARM_KDMA_CTX_COMMIT_MEM_SET_MASK, 1);
+
+ WREG32(mmARC_FARM_KDMA_CTX_COMMIT, commit_mask);
+
+ /* Wait for completion */
+ cq = &hdev->completion_queue[GAUDI2_RESERVED_CQ_KDMA_COMPLETION];
+ cq_base = cq->kernel_address;
+ polling_addr = (u32 *)&cq_base[cq->ci];
+
+ if (hdev->pldm)
+ /* for each 1MB 20 second of timeout */
+ timeout = ((size / SZ_1M) + 1) * USEC_PER_SEC * 20;
+ else
+ timeout = KDMA_TIMEOUT_USEC;
+
+ /* Polling */
+ rc = hl_poll_timeout_memory(
+ hdev,
+ polling_addr,
+ status,
+ (status == 1),
+ 1000,
+ timeout,
+ true);
+
+ *polling_addr = 0;
+
+ if (rc) {
+ dev_err(hdev->dev, "Timeout while waiting for KDMA to be idle\n");
+ WREG32(mmARC_FARM_KDMA_CFG_1, 1 << ARC_FARM_KDMA_CFG_1_HALT_SHIFT);
+ return rc;
+ }
+
+ cq->ci = hl_cq_inc_ptr(cq->ci);
+
+ return 0;
+}
+
+static void gaudi2_memset_device_lbw(struct hl_device *hdev, u32 addr, u32 size, u32 val)
+{
+ u32 i;
+
+ for (i = 0 ; i < size ; i += sizeof(u32))
+ WREG32(addr + i, val);
+}
+
+static void gaudi2_qman_set_test_mode(struct hl_device *hdev, u32 hw_queue_id, bool enable)
+{
+ u32 reg_base = gaudi2_qm_blocks_bases[hw_queue_id];
+
+ if (enable) {
+ WREG32(reg_base + QM_GLBL_PROT_OFFSET, QMAN_MAKE_TRUSTED_TEST_MODE);
+ WREG32(reg_base + QM_PQC_CFG_OFFSET, 0);
+ } else {
+ WREG32(reg_base + QM_GLBL_PROT_OFFSET, QMAN_MAKE_TRUSTED);
+ WREG32(reg_base + QM_PQC_CFG_OFFSET, 1 << PDMA0_QM_PQC_CFG_EN_SHIFT);
+ }
+}
+
+static inline u32 gaudi2_test_queue_hw_queue_id_to_sob_id(struct hl_device *hdev, u32 hw_queue_id)
+{
+ return hdev->asic_prop.first_available_user_sob[0] +
+ hw_queue_id - GAUDI2_QUEUE_ID_PDMA_0_0;
+}
+
+static void gaudi2_test_queue_clear(struct hl_device *hdev, u32 hw_queue_id)
+{
+ u32 sob_offset = gaudi2_test_queue_hw_queue_id_to_sob_id(hdev, hw_queue_id) * 4;
+ u32 sob_addr = mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_offset;
+
+ /* Reset the SOB value */
+ WREG32(sob_addr, 0);
+}
+
+static int gaudi2_test_queue_send_msg_short(struct hl_device *hdev, u32 hw_queue_id, u32 sob_val,
+ struct gaudi2_queues_test_info *msg_info)
+{
+ u32 sob_offset = gaudi2_test_queue_hw_queue_id_to_sob_id(hdev, hw_queue_id) * 4;
+ u32 tmp, sob_base = 1;
+ struct packet_msg_short *msg_short_pkt = msg_info->kern_addr;
+ size_t pkt_size = sizeof(struct packet_msg_short);
+ int rc;
+
+ tmp = (PACKET_MSG_SHORT << GAUDI2_PKT_CTL_OPCODE_SHIFT) |
+ (1 << GAUDI2_PKT_CTL_EB_SHIFT) |
+ (1 << GAUDI2_PKT_CTL_MB_SHIFT) |
+ (sob_base << GAUDI2_PKT_SHORT_CTL_BASE_SHIFT) |
+ (sob_offset << GAUDI2_PKT_SHORT_CTL_ADDR_SHIFT);
+
+ msg_short_pkt->value = cpu_to_le32(sob_val);
+ msg_short_pkt->ctl = cpu_to_le32(tmp);
+
+ rc = hl_hw_queue_send_cb_no_cmpl(hdev, hw_queue_id, pkt_size, msg_info->dma_addr);
+ if (rc)
+ dev_err(hdev->dev,
+ "Failed to send msg_short packet to H/W queue %s\n",
+ GAUDI2_QUEUE_ID_TO_STR(hw_queue_id));
+
+ return rc;
+}
+
+static int gaudi2_test_queue_wait_completion(struct hl_device *hdev, u32 hw_queue_id, u32 sob_val)
+{
+ u32 sob_offset = gaudi2_test_queue_hw_queue_id_to_sob_id(hdev, hw_queue_id) * 4;
+ u32 sob_addr = mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_offset;
+ u32 timeout_usec, tmp;
+ int rc;
+
+ if (hdev->pldm)
+ timeout_usec = GAUDI2_PLDM_TEST_QUEUE_WAIT_USEC;
+ else
+ timeout_usec = GAUDI2_TEST_QUEUE_WAIT_USEC;
+
+ rc = hl_poll_timeout(
+ hdev,
+ sob_addr,
+ tmp,
+ (tmp == sob_val),
+ 1000,
+ timeout_usec);
+
+ if (rc == -ETIMEDOUT) {
+ dev_err(hdev->dev, "H/W queue %s test failed (SOB_OBJ_0 == 0x%x)\n",
+ GAUDI2_QUEUE_ID_TO_STR(hw_queue_id), tmp);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+static int gaudi2_test_cpu_queue(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ /*
+ * check capability here as send_cpu_message() won't update the result
+ * value if no capability
+ */
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q))
+ return 0;
+
+ return hl_fw_test_cpu_queue(hdev);
+}
+
+static int gaudi2_test_queues(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_queues_test_info *msg_info;
+ u32 sob_val = 0x5a5a;
+ int i, rc;
+
+ /* send test message on all enabled Qs */
+ for (i = GAUDI2_QUEUE_ID_PDMA_0_0 ; i < GAUDI2_QUEUE_ID_CPU_PQ; i++) {
+ if (!gaudi2_is_queue_enabled(hdev, i) || gaudi2_is_edma_queue_id(i))
+ continue;
+
+ msg_info = &gaudi2->queues_test_info[i - GAUDI2_QUEUE_ID_PDMA_0_0];
+ gaudi2_qman_set_test_mode(hdev, i, true);
+ gaudi2_test_queue_clear(hdev, i);
+ rc = gaudi2_test_queue_send_msg_short(hdev, i, sob_val, msg_info);
+ if (rc)
+ goto done;
+ }
+
+ rc = gaudi2_test_cpu_queue(hdev);
+ if (rc)
+ goto done;
+
+ /* verify that all messages were processed */
+ for (i = GAUDI2_QUEUE_ID_PDMA_0_0 ; i < GAUDI2_QUEUE_ID_CPU_PQ; i++) {
+ if (!gaudi2_is_queue_enabled(hdev, i) || gaudi2_is_edma_queue_id(i))
+ continue;
+
+ rc = gaudi2_test_queue_wait_completion(hdev, i, sob_val);
+ if (rc)
+ /* chip is not usable, no need for cleanups, just bail-out with error */
+ goto done;
+
+ gaudi2_test_queue_clear(hdev, i);
+ gaudi2_qman_set_test_mode(hdev, i, false);
+ }
+
+done:
+ return rc;
+}
+
+static int gaudi2_compute_reset_late_init(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ size_t irq_arr_size;
+ int rc;
+
+ gaudi2_init_arcs(hdev);
+
+ rc = gaudi2_scrub_arcs_dccm(hdev);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to scrub arcs DCCM\n");
+ return rc;
+ }
+
+ gaudi2_init_security(hdev);
+
+ /* Unmask all IRQs since some could have been received during the soft reset */
+ irq_arr_size = gaudi2->num_of_valid_hw_events * sizeof(gaudi2->hw_events[0]);
+ return hl_fw_unmask_irq_arr(hdev, gaudi2->hw_events, irq_arr_size);
+}
+
+static bool gaudi2_get_edma_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ u32 qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts, dma_core_sts0, dma_core_sts1;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ unsigned long *mask = (unsigned long *) mask_arr;
+ const char *edma_fmt = "%-6d%-6d%-9s%#-14x%#-15x%#x\n";
+ bool is_idle = true, is_eng_idle;
+ int engine_idx, i, j;
+ u64 offset;
+
+ if (e)
+ hl_engine_data_sprintf(e,
+ "\nCORE EDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0 DMA_CORE_STS1\n"
+ "---- ---- ------- ------------ ------------- -------------\n");
+
+ for (i = 0; i < NUM_OF_DCORES; i++) {
+ for (j = 0 ; j < NUM_OF_EDMA_PER_DCORE ; j++) {
+ int seq = i * NUM_OF_EDMA_PER_DCORE + j;
+
+ if (!(prop->edma_enabled_mask & BIT(seq)))
+ continue;
+
+ engine_idx = GAUDI2_DCORE0_ENGINE_ID_EDMA_0 +
+ i * GAUDI2_ENGINE_ID_DCORE_OFFSET + j;
+ offset = i * DCORE_OFFSET + j * DCORE_EDMA_OFFSET;
+
+ dma_core_sts0 = RREG32(mmDCORE0_EDMA0_CORE_STS0 + offset);
+ dma_core_sts1 = RREG32(mmDCORE0_EDMA0_CORE_STS1 + offset);
+
+ qm_glbl_sts0 = RREG32(mmDCORE0_EDMA0_QM_GLBL_STS0 + offset);
+ qm_glbl_sts1 = RREG32(mmDCORE0_EDMA0_QM_GLBL_STS1 + offset);
+ qm_cgm_sts = RREG32(mmDCORE0_EDMA0_QM_CGM_STS + offset);
+
+ is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts) &&
+ IS_DMA_IDLE(dma_core_sts0) && !IS_DMA_HALTED(dma_core_sts1);
+ is_idle &= is_eng_idle;
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+
+ if (e)
+ hl_engine_data_sprintf(e, edma_fmt, i, j, is_eng_idle ? "Y" : "N",
+ qm_glbl_sts0, dma_core_sts0, dma_core_sts1);
+ }
+ }
+
+ return is_idle;
+}
+
+static bool gaudi2_get_pdma_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ u32 qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts, dma_core_sts0, dma_core_sts1;
+ unsigned long *mask = (unsigned long *) mask_arr;
+ const char *pdma_fmt = "%-6d%-9s%#-14x%#-15x%#x\n";
+ bool is_idle = true, is_eng_idle;
+ int engine_idx, i;
+ u64 offset;
+
+ if (e)
+ hl_engine_data_sprintf(e,
+ "\nPDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0 DMA_CORE_STS1\n"
+ "---- ------- ------------ ------------- -------------\n");
+
+ for (i = 0 ; i < NUM_OF_PDMA ; i++) {
+ engine_idx = GAUDI2_ENGINE_ID_PDMA_0 + i;
+ offset = i * PDMA_OFFSET;
+ dma_core_sts0 = RREG32(mmPDMA0_CORE_STS0 + offset);
+ dma_core_sts1 = RREG32(mmPDMA0_CORE_STS1 + offset);
+
+ qm_glbl_sts0 = RREG32(mmPDMA0_QM_GLBL_STS0 + offset);
+ qm_glbl_sts1 = RREG32(mmPDMA0_QM_GLBL_STS1 + offset);
+ qm_cgm_sts = RREG32(mmPDMA0_QM_CGM_STS + offset);
+
+ is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts) &&
+ IS_DMA_IDLE(dma_core_sts0) && !IS_DMA_HALTED(dma_core_sts1);
+ is_idle &= is_eng_idle;
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+
+ if (e)
+ hl_engine_data_sprintf(e, pdma_fmt, i, is_eng_idle ? "Y" : "N",
+ qm_glbl_sts0, dma_core_sts0, dma_core_sts1);
+ }
+
+ return is_idle;
+}
+
+static bool gaudi2_get_nic_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ unsigned long *mask = (unsigned long *) mask_arr;
+ const char *nic_fmt = "%-5d%-9s%#-14x%#-12x\n";
+ u32 qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts;
+ bool is_idle = true, is_eng_idle;
+ int engine_idx, i;
+ u64 offset = 0;
+
+ /* NIC, twelve macros in Full chip */
+ if (e && hdev->nic_ports_mask)
+ hl_engine_data_sprintf(e,
+ "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n"
+ "--- ------- ------------ ----------\n");
+
+ for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++) {
+ if (!(i & 1))
+ offset = i / 2 * NIC_OFFSET;
+ else
+ offset += NIC_QM_OFFSET;
+
+ if (!(hdev->nic_ports_mask & BIT(i)))
+ continue;
+
+ engine_idx = GAUDI2_ENGINE_ID_NIC0_0 + i;
+
+
+ qm_glbl_sts0 = RREG32(mmNIC0_QM0_GLBL_STS0 + offset);
+ qm_glbl_sts1 = RREG32(mmNIC0_QM0_GLBL_STS1 + offset);
+ qm_cgm_sts = RREG32(mmNIC0_QM0_CGM_STS + offset);
+
+ is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts);
+ is_idle &= is_eng_idle;
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+
+ if (e)
+ hl_engine_data_sprintf(e, nic_fmt, i, is_eng_idle ? "Y" : "N",
+ qm_glbl_sts0, qm_cgm_sts);
+ }
+
+ return is_idle;
+}
+
+static bool gaudi2_get_mme_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ u32 qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts, mme_arch_sts;
+ unsigned long *mask = (unsigned long *) mask_arr;
+ const char *mme_fmt = "%-5d%-6s%-9s%#-14x%#x\n";
+ bool is_idle = true, is_eng_idle;
+ int engine_idx, i;
+ u64 offset;
+
+ if (e)
+ hl_engine_data_sprintf(e,
+ "\nMME Stub is_idle QM_GLBL_STS0 MME_ARCH_STATUS\n"
+ "--- ---- ------- ------------ ---------------\n");
+ /* MME, one per Dcore */
+ for (i = 0 ; i < NUM_OF_DCORES ; i++) {
+ engine_idx = GAUDI2_DCORE0_ENGINE_ID_MME + i * GAUDI2_ENGINE_ID_DCORE_OFFSET;
+ offset = i * DCORE_OFFSET;
+
+ qm_glbl_sts0 = RREG32(mmDCORE0_MME_QM_GLBL_STS0 + offset);
+ qm_glbl_sts1 = RREG32(mmDCORE0_MME_QM_GLBL_STS1 + offset);
+ qm_cgm_sts = RREG32(mmDCORE0_MME_QM_CGM_STS + offset);
+
+ is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts);
+ is_idle &= is_eng_idle;
+
+ mme_arch_sts = RREG32(mmDCORE0_MME_CTRL_LO_ARCH_STATUS + offset);
+ is_eng_idle &= IS_MME_IDLE(mme_arch_sts);
+ is_idle &= is_eng_idle;
+
+ if (e)
+ hl_engine_data_sprintf(e, mme_fmt, i, "N",
+ is_eng_idle ? "Y" : "N",
+ qm_glbl_sts0,
+ mme_arch_sts);
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+ }
+
+ return is_idle;
+}
+
+static void gaudi2_is_tpc_engine_idle(struct hl_device *hdev, int dcore, int inst, u32 offset,
+ struct iterate_module_ctx *ctx)
+{
+ struct gaudi2_tpc_idle_data *idle_data = ctx->data;
+ u32 tpc_cfg_sts, qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts;
+ bool is_eng_idle;
+ int engine_idx;
+
+ if ((dcore == 0) && (inst == (NUM_DCORE0_TPC - 1)))
+ engine_idx = GAUDI2_DCORE0_ENGINE_ID_TPC_6;
+ else
+ engine_idx = GAUDI2_DCORE0_ENGINE_ID_TPC_0 +
+ dcore * GAUDI2_ENGINE_ID_DCORE_OFFSET + inst;
+
+ tpc_cfg_sts = RREG32(mmDCORE0_TPC0_CFG_STATUS + offset);
+ qm_glbl_sts0 = RREG32(mmDCORE0_TPC0_QM_GLBL_STS0 + offset);
+ qm_glbl_sts1 = RREG32(mmDCORE0_TPC0_QM_GLBL_STS1 + offset);
+ qm_cgm_sts = RREG32(mmDCORE0_TPC0_QM_CGM_STS + offset);
+
+ is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts) &&
+ IS_TPC_IDLE(tpc_cfg_sts);
+ *(idle_data->is_idle) &= is_eng_idle;
+
+ if (idle_data->mask && !is_eng_idle)
+ set_bit(engine_idx, idle_data->mask);
+
+ if (idle_data->e)
+ hl_engine_data_sprintf(idle_data->e,
+ idle_data->tpc_fmt, dcore, inst,
+ is_eng_idle ? "Y" : "N",
+ qm_glbl_sts0, qm_cgm_sts, tpc_cfg_sts);
+}
+
+static bool gaudi2_get_tpc_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ unsigned long *mask = (unsigned long *) mask_arr;
+ bool is_idle = true;
+
+ struct gaudi2_tpc_idle_data tpc_idle_data = {
+ .tpc_fmt = "%-6d%-5d%-9s%#-14x%#-12x%#x\n",
+ .e = e,
+ .mask = mask,
+ .is_idle = &is_idle,
+ };
+ struct iterate_module_ctx tpc_iter = {
+ .fn = &gaudi2_is_tpc_engine_idle,
+ .data = &tpc_idle_data,
+ };
+
+ if (e && prop->tpc_enabled_mask)
+ hl_engine_data_sprintf(e,
+ "\nCORE TPC is_idle QM_GLBL_STS0 QM_CGM_STS STATUS\n"
+ "---- --- ------- ------------ ---------- ------\n");
+
+ gaudi2_iterate_tpcs(hdev, &tpc_iter);
+
+ return *tpc_idle_data.is_idle;
+}
+
+static bool gaudi2_get_decoder_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ unsigned long *mask = (unsigned long *) mask_arr;
+ const char *pcie_dec_fmt = "%-10d%-9s%#x\n";
+ const char *dec_fmt = "%-6d%-5d%-9s%#x\n";
+ bool is_idle = true, is_eng_idle;
+ u32 dec_swreg15, dec_enabled_bit;
+ int engine_idx, i, j;
+ u64 offset;
+
+ /* Decoders, two each Dcore and two shared PCIe decoders */
+ if (e && (prop->decoder_enabled_mask & (~PCIE_DEC_EN_MASK)))
+ hl_engine_data_sprintf(e,
+ "\nCORE DEC is_idle VSI_CMD_SWREG15\n"
+ "---- --- ------- ---------------\n");
+
+ for (i = 0 ; i < NUM_OF_DCORES ; i++) {
+ for (j = 0 ; j < NUM_OF_DEC_PER_DCORE ; j++) {
+ dec_enabled_bit = 1 << (i * NUM_OF_DEC_PER_DCORE + j);
+ if (!(prop->decoder_enabled_mask & dec_enabled_bit))
+ continue;
+
+ engine_idx = GAUDI2_DCORE0_ENGINE_ID_DEC_0 +
+ i * GAUDI2_ENGINE_ID_DCORE_OFFSET + j;
+ offset = i * DCORE_OFFSET + j * DCORE_DEC_OFFSET;
+
+ dec_swreg15 = RREG32(mmDCORE0_DEC0_CMD_SWREG15 + offset);
+ is_eng_idle = IS_DEC_IDLE(dec_swreg15);
+ is_idle &= is_eng_idle;
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+
+ if (e)
+ hl_engine_data_sprintf(e, dec_fmt, i, j,
+ is_eng_idle ? "Y" : "N", dec_swreg15);
+ }
+ }
+
+ if (e && (prop->decoder_enabled_mask & PCIE_DEC_EN_MASK))
+ hl_engine_data_sprintf(e,
+ "\nPCIe DEC is_idle VSI_CMD_SWREG15\n"
+ "-------- ------- ---------------\n");
+
+ /* Check shared(PCIe) decoders */
+ for (i = 0 ; i < NUM_OF_DEC_PER_DCORE ; i++) {
+ dec_enabled_bit = PCIE_DEC_SHIFT + i;
+ if (!(prop->decoder_enabled_mask & BIT(dec_enabled_bit)))
+ continue;
+
+ engine_idx = GAUDI2_PCIE_ENGINE_ID_DEC_0 + i;
+ offset = i * DCORE_DEC_OFFSET;
+ dec_swreg15 = RREG32(mmPCIE_DEC0_CMD_SWREG15 + offset);
+ is_eng_idle = IS_DEC_IDLE(dec_swreg15);
+ is_idle &= is_eng_idle;
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+
+ if (e)
+ hl_engine_data_sprintf(e, pcie_dec_fmt, i,
+ is_eng_idle ? "Y" : "N", dec_swreg15);
+ }
+
+ return is_idle;
+}
+
+static bool gaudi2_get_rotator_idle_status(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ const char *rot_fmt = "%-6d%-5d%-9s%#-14x%#-14x%#x\n";
+ unsigned long *mask = (unsigned long *) mask_arr;
+ u32 qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts;
+ bool is_idle = true, is_eng_idle;
+ int engine_idx, i;
+ u64 offset;
+
+ if (e)
+ hl_engine_data_sprintf(e,
+ "\nCORE ROT is_idle QM_GLBL_STS0 QM_GLBL_STS1 QM_CGM_STS\n"
+ "---- --- ------- ------------ ------------ ----------\n");
+
+ for (i = 0 ; i < NUM_OF_ROT ; i++) {
+ engine_idx = GAUDI2_ENGINE_ID_ROT_0 + i;
+
+ offset = i * ROT_OFFSET;
+
+ qm_glbl_sts0 = RREG32(mmROT0_QM_GLBL_STS0 + offset);
+ qm_glbl_sts1 = RREG32(mmROT0_QM_GLBL_STS1 + offset);
+ qm_cgm_sts = RREG32(mmROT0_QM_CGM_STS + offset);
+
+ is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts);
+ is_idle &= is_eng_idle;
+
+ if (mask && !is_eng_idle)
+ set_bit(engine_idx, mask);
+
+ if (e)
+ hl_engine_data_sprintf(e, rot_fmt, i, 0, is_eng_idle ? "Y" : "N",
+ qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts);
+ }
+
+ return is_idle;
+}
+
+static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
+{
+ bool is_idle = true;
+
+ is_idle &= gaudi2_get_edma_idle_status(hdev, mask_arr, mask_len, e);
+ is_idle &= gaudi2_get_pdma_idle_status(hdev, mask_arr, mask_len, e);
+ is_idle &= gaudi2_get_nic_idle_status(hdev, mask_arr, mask_len, e);
+ is_idle &= gaudi2_get_mme_idle_status(hdev, mask_arr, mask_len, e);
+ is_idle &= gaudi2_get_tpc_idle_status(hdev, mask_arr, mask_len, e);
+ is_idle &= gaudi2_get_decoder_idle_status(hdev, mask_arr, mask_len, e);
+ is_idle &= gaudi2_get_rotator_idle_status(hdev, mask_arr, mask_len, e);
+
+ return is_idle;
+}
+
+static void gaudi2_hw_queues_lock(struct hl_device *hdev)
+ __acquires(&gaudi2->hw_queues_lock)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ spin_lock(&gaudi2->hw_queues_lock);
+}
+
+static void gaudi2_hw_queues_unlock(struct hl_device *hdev)
+ __releases(&gaudi2->hw_queues_lock)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ spin_unlock(&gaudi2->hw_queues_lock);
+}
+
+static u32 gaudi2_get_pci_id(struct hl_device *hdev)
+{
+ return hdev->pdev->device;
+}
+
+static int gaudi2_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q))
+ return 0;
+
+ return hl_fw_get_eeprom_data(hdev, data, max_size);
+}
+
+static void gaudi2_update_eq_ci(struct hl_device *hdev, u32 val)
+{
+ WREG32(mmCPU_IF_EQ_RD_OFFS, val);
+}
+
+static void *gaudi2_get_events_stat(struct hl_device *hdev, bool aggregate, u32 *size)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (aggregate) {
+ *size = (u32) sizeof(gaudi2->events_stat_aggregate);
+ return gaudi2->events_stat_aggregate;
+ }
+
+ *size = (u32) sizeof(gaudi2->events_stat);
+ return gaudi2->events_stat;
+}
+
+static void gaudi2_mmu_vdec_dcore_prepare(struct hl_device *hdev, int dcore_id,
+ int dcore_vdec_id, u32 rw_asid, u32 rw_mmu_bp)
+{
+ u32 offset = (mmDCORE0_VDEC1_BRDG_CTRL_BASE - mmDCORE0_VDEC0_BRDG_CTRL_BASE) *
+ dcore_vdec_id + DCORE_OFFSET * dcore_id;
+
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_DEC_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_DEC_HB_ASID + offset, rw_asid);
+
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_ABNRM_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_ABNRM_HB_ASID + offset, rw_asid);
+
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_L2C_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_L2C_HB_ASID + offset, rw_asid);
+
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_NRM_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_NRM_HB_ASID + offset, rw_asid);
+
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_VCD_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmDCORE0_VDEC0_BRDG_CTRL_AXUSER_MSIX_VCD_HB_ASID + offset, rw_asid);
+}
+
+static void gaudi2_mmu_dcore_prepare(struct hl_device *hdev, int dcore_id, u32 asid)
+{
+ u32 rw_asid = (asid << ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_RD_SHIFT) |
+ (asid << ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_WR_SHIFT);
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 dcore_offset = dcore_id * DCORE_OFFSET;
+ u32 vdec_id, i, ports_offset, reg_val;
+ u8 edma_seq_base;
+
+ /* EDMA */
+ edma_seq_base = dcore_id * NUM_OF_EDMA_PER_DCORE;
+ if (prop->edma_enabled_mask & BIT(edma_seq_base)) {
+ WREG32(mmDCORE0_EDMA0_QM_AXUSER_NONSECURED_HB_MMU_BP + dcore_offset, 0);
+ WREG32(mmDCORE0_EDMA0_QM_AXUSER_NONSECURED_HB_ASID + dcore_offset, rw_asid);
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_AXUSER_HB_MMU_BP + dcore_offset, 0);
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_AXUSER_HB_ASID + dcore_offset, rw_asid);
+ }
+
+ if (prop->edma_enabled_mask & BIT(edma_seq_base + 1)) {
+ WREG32(mmDCORE0_EDMA1_QM_AXUSER_NONSECURED_HB_MMU_BP + dcore_offset, 0);
+ WREG32(mmDCORE0_EDMA1_QM_AXUSER_NONSECURED_HB_ASID + dcore_offset, rw_asid);
+ WREG32(mmDCORE0_EDMA1_CORE_CTX_AXUSER_HB_ASID + dcore_offset, rw_asid);
+ WREG32(mmDCORE0_EDMA1_CORE_CTX_AXUSER_HB_MMU_BP + dcore_offset, 0);
+ }
+
+ /* Sync Mngr */
+ WREG32(mmDCORE0_SYNC_MNGR_GLBL_ASID_NONE_SEC_PRIV + dcore_offset, asid);
+ /*
+ * Sync Mngrs on dcores 1 - 3 are exposed to user, so must use user ASID
+ * for any access type
+ */
+ if (dcore_id > 0) {
+ reg_val = (asid << DCORE0_SYNC_MNGR_MSTR_IF_AXUSER_HB_ASID_RD_SHIFT) |
+ (asid << DCORE0_SYNC_MNGR_MSTR_IF_AXUSER_HB_ASID_WR_SHIFT);
+ WREG32(mmDCORE0_SYNC_MNGR_MSTR_IF_AXUSER_HB_ASID + dcore_offset, reg_val);
+ WREG32(mmDCORE0_SYNC_MNGR_MSTR_IF_AXUSER_HB_MMU_BP + dcore_offset, 0);
+ }
+
+ WREG32(mmDCORE0_MME_CTRL_LO_MME_AXUSER_HB_MMU_BP + dcore_offset, 0);
+ WREG32(mmDCORE0_MME_CTRL_LO_MME_AXUSER_HB_ASID + dcore_offset, rw_asid);
+
+ for (i = 0 ; i < NUM_OF_MME_SBTE_PORTS ; i++) {
+ ports_offset = i * DCORE_MME_SBTE_OFFSET;
+ WREG32(mmDCORE0_MME_SBTE0_MSTR_IF_AXUSER_HB_MMU_BP +
+ dcore_offset + ports_offset, 0);
+ WREG32(mmDCORE0_MME_SBTE0_MSTR_IF_AXUSER_HB_ASID +
+ dcore_offset + ports_offset, rw_asid);
+ }
+
+ for (i = 0 ; i < NUM_OF_MME_WB_PORTS ; i++) {
+ ports_offset = i * DCORE_MME_WB_OFFSET;
+ WREG32(mmDCORE0_MME_WB0_MSTR_IF_AXUSER_HB_MMU_BP +
+ dcore_offset + ports_offset, 0);
+ WREG32(mmDCORE0_MME_WB0_MSTR_IF_AXUSER_HB_ASID +
+ dcore_offset + ports_offset, rw_asid);
+ }
+
+ WREG32(mmDCORE0_MME_QM_AXUSER_NONSECURED_HB_MMU_BP + dcore_offset, 0);
+ WREG32(mmDCORE0_MME_QM_AXUSER_NONSECURED_HB_ASID + dcore_offset, rw_asid);
+
+ /*
+ * Decoders
+ */
+ for (vdec_id = 0 ; vdec_id < NUM_OF_DEC_PER_DCORE ; vdec_id++) {
+ if (prop->decoder_enabled_mask & BIT(dcore_id * NUM_OF_DEC_PER_DCORE + vdec_id))
+ gaudi2_mmu_vdec_dcore_prepare(hdev, dcore_id, vdec_id, rw_asid, 0);
+ }
+}
+
+static void gudi2_mmu_vdec_shared_prepare(struct hl_device *hdev,
+ int shared_vdec_id, u32 rw_asid, u32 rw_mmu_bp)
+{
+ u32 offset = (mmPCIE_VDEC1_BRDG_CTRL_BASE - mmPCIE_VDEC0_BRDG_CTRL_BASE) * shared_vdec_id;
+
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_DEC_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_DEC_HB_ASID + offset, rw_asid);
+
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_ABNRM_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_ABNRM_HB_ASID + offset, rw_asid);
+
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_L2C_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_L2C_HB_ASID + offset, rw_asid);
+
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_NRM_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_NRM_HB_ASID + offset, rw_asid);
+
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_VCD_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmPCIE_VDEC0_BRDG_CTRL_AXUSER_MSIX_VCD_HB_ASID + offset, rw_asid);
+}
+
+static void gudi2_mmu_arc_farm_arc_dup_eng_prepare(struct hl_device *hdev, int arc_farm_id,
+ u32 rw_asid, u32 rw_mmu_bp)
+{
+ u32 offset = (mmARC_FARM_ARC1_DUP_ENG_BASE - mmARC_FARM_ARC0_DUP_ENG_BASE) * arc_farm_id;
+
+ WREG32(mmARC_FARM_ARC0_DUP_ENG_AXUSER_HB_MMU_BP + offset, rw_mmu_bp);
+ WREG32(mmARC_FARM_ARC0_DUP_ENG_AXUSER_HB_ASID + offset, rw_asid);
+}
+
+static void gaudi2_arc_mmu_prepare(struct hl_device *hdev, u32 cpu_id, u32 asid)
+{
+ u32 reg_base, reg_offset, reg_val = 0;
+
+ reg_base = gaudi2_arc_blocks_bases[cpu_id];
+
+ /* Enable MMU and configure asid for all relevant ARC regions */
+ reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_ARC_REGION_CFG_MMU_BP_MASK, 0);
+ reg_val |= FIELD_PREP(ARC_FARM_ARC0_AUX_ARC_REGION_CFG_0_ASID_MASK, asid);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION3_GENERAL);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION4_HBM0_FW);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION5_HBM1_GC_DATA);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION6_HBM2_GC_DATA);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION7_HBM3_GC_DATA);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION9_PCIE);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION10_GENERAL);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION11_GENERAL);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION12_GENERAL);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION13_GENERAL);
+ WREG32(reg_base + reg_offset, reg_val);
+
+ reg_offset = ARC_REGION_CFG_OFFSET(ARC_REGION14_GENERAL);
+ WREG32(reg_base + reg_offset, reg_val);
+}
+
+static int gaudi2_arc_mmu_prepare_all(struct hl_device *hdev, u32 asid)
+{
+ int i;
+
+ if (hdev->fw_components & FW_TYPE_BOOT_CPU)
+ return hl_fw_cpucp_engine_core_asid_set(hdev, asid);
+
+ for (i = CPU_ID_SCHED_ARC0 ; i < NUM_OF_ARC_FARMS_ARC ; i++)
+ gaudi2_arc_mmu_prepare(hdev, i, asid);
+
+ for (i = GAUDI2_QUEUE_ID_PDMA_0_0 ; i < GAUDI2_QUEUE_ID_CPU_PQ ; i += 4) {
+ if (!gaudi2_is_queue_enabled(hdev, i))
+ continue;
+
+ gaudi2_arc_mmu_prepare(hdev, gaudi2_queue_id_to_arc_id[i], asid);
+ }
+
+ return 0;
+}
+
+static int gaudi2_mmu_shared_prepare(struct hl_device *hdev, u32 asid)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 rw_asid, offset;
+ int rc, i;
+
+ rw_asid = FIELD_PREP(ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_RD_MASK, asid) |
+ FIELD_PREP(ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_WR_MASK, asid);
+
+ WREG32(mmPDMA0_QM_AXUSER_NONSECURED_HB_ASID, rw_asid);
+ WREG32(mmPDMA0_QM_AXUSER_NONSECURED_HB_MMU_BP, 0);
+ WREG32(mmPDMA0_CORE_CTX_AXUSER_HB_ASID, rw_asid);
+ WREG32(mmPDMA0_CORE_CTX_AXUSER_HB_MMU_BP, 0);
+
+ WREG32(mmPDMA1_QM_AXUSER_NONSECURED_HB_ASID, rw_asid);
+ WREG32(mmPDMA1_QM_AXUSER_NONSECURED_HB_MMU_BP, 0);
+ WREG32(mmPDMA1_CORE_CTX_AXUSER_HB_ASID, rw_asid);
+ WREG32(mmPDMA1_CORE_CTX_AXUSER_HB_MMU_BP, 0);
+
+ /* ROT */
+ for (i = 0 ; i < NUM_OF_ROT ; i++) {
+ offset = i * ROT_OFFSET;
+ WREG32(mmROT0_QM_AXUSER_NONSECURED_HB_ASID + offset, rw_asid);
+ WREG32(mmROT0_QM_AXUSER_NONSECURED_HB_MMU_BP + offset, 0);
+ RMWREG32(mmROT0_CPL_QUEUE_AWUSER + offset, asid, MMUBP_ASID_MASK);
+ RMWREG32(mmROT0_DESC_HBW_ARUSER_LO + offset, asid, MMUBP_ASID_MASK);
+ RMWREG32(mmROT0_DESC_HBW_AWUSER_LO + offset, asid, MMUBP_ASID_MASK);
+ }
+
+ /* Shared Decoders are the last bits in the decoders mask */
+ if (prop->decoder_enabled_mask & BIT(NUM_OF_DCORES * NUM_OF_DEC_PER_DCORE + 0))
+ gudi2_mmu_vdec_shared_prepare(hdev, 0, rw_asid, 0);
+
+ if (prop->decoder_enabled_mask & BIT(NUM_OF_DCORES * NUM_OF_DEC_PER_DCORE + 1))
+ gudi2_mmu_vdec_shared_prepare(hdev, 1, rw_asid, 0);
+
+ /* arc farm arc dup eng */
+ for (i = 0 ; i < NUM_OF_ARC_FARMS_ARC ; i++)
+ gudi2_mmu_arc_farm_arc_dup_eng_prepare(hdev, i, rw_asid, 0);
+
+ rc = gaudi2_arc_mmu_prepare_all(hdev, asid);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static void gaudi2_tpc_mmu_prepare(struct hl_device *hdev, int dcore, int inst, u32 offset,
+ struct iterate_module_ctx *ctx)
+{
+ struct gaudi2_tpc_mmu_data *mmu_data = ctx->data;
+
+ WREG32(mmDCORE0_TPC0_CFG_AXUSER_HB_MMU_BP + offset, 0);
+ WREG32(mmDCORE0_TPC0_CFG_AXUSER_HB_ASID + offset, mmu_data->rw_asid);
+ WREG32(mmDCORE0_TPC0_QM_AXUSER_NONSECURED_HB_MMU_BP + offset, 0);
+ WREG32(mmDCORE0_TPC0_QM_AXUSER_NONSECURED_HB_ASID + offset, mmu_data->rw_asid);
+}
+
+/* zero the MMUBP and set the ASID */
+static int gaudi2_mmu_prepare(struct hl_device *hdev, u32 asid)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ struct gaudi2_tpc_mmu_data tpc_mmu_data;
+ struct iterate_module_ctx tpc_iter = {
+ .fn = &gaudi2_tpc_mmu_prepare,
+ .data = &tpc_mmu_data,
+ };
+ int rc, i;
+
+ if (asid & ~DCORE0_HMMU0_STLB_ASID_ASID_MASK) {
+ dev_crit(hdev->dev, "asid %u is too big\n", asid);
+ return -EINVAL;
+ }
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_MMU_MASK))
+ return 0;
+
+ rc = gaudi2_mmu_shared_prepare(hdev, asid);
+ if (rc)
+ return rc;
+
+ /* configure DCORE MMUs */
+ tpc_mmu_data.rw_asid = (asid << ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_RD_SHIFT) |
+ (asid << ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_WR_SHIFT);
+ gaudi2_iterate_tpcs(hdev, &tpc_iter);
+ for (i = 0 ; i < NUM_OF_DCORES ; i++)
+ gaudi2_mmu_dcore_prepare(hdev, i, asid);
+
+ return 0;
+}
+
+static inline bool is_info_event(u32 event)
+{
+ switch (event) {
+ case GAUDI2_EVENT_CPU_CPLD_SHUTDOWN_CAUSE:
+ case GAUDI2_EVENT_CPU_FIX_POWER_ENV_S ... GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_E:
+ case GAUDI2_EVENT_ARC_PWR_BRK_ENTRY ... GAUDI2_EVENT_ARC_PWR_RD_MODE3:
+
+ /* return in case of NIC status event - these events are received periodically and not as
+ * an indication to an error.
+ */
+ case GAUDI2_EVENT_CPU0_STATUS_NIC0_ENG0 ... GAUDI2_EVENT_CPU11_STATUS_NIC11_ENG1:
+ case GAUDI2_EVENT_ARC_EQ_HEARTBEAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void gaudi2_print_event(struct hl_device *hdev, u16 event_type,
+ bool ratelimited, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (ratelimited)
+ dev_err_ratelimited(hdev->dev, "%s: %pV\n",
+ gaudi2_irq_map_table[event_type].valid ?
+ gaudi2_irq_map_table[event_type].name : "N/A Event", &vaf);
+ else
+ dev_err(hdev->dev, "%s: %pV\n",
+ gaudi2_irq_map_table[event_type].valid ?
+ gaudi2_irq_map_table[event_type].name : "N/A Event", &vaf);
+
+ va_end(args);
+}
+
+static bool gaudi2_handle_ecc_event(struct hl_device *hdev, u16 event_type,
+ struct hl_eq_ecc_data *ecc_data)
+{
+ u64 ecc_address = 0, ecc_syndrome = 0;
+ u8 memory_wrapper_idx = 0;
+ bool has_block_id = false;
+ u16 block_id;
+
+ if (hl_fw_version_cmp(hdev, 1, 12, 0) >= 0)
+ has_block_id = true;
+
+ ecc_address = le64_to_cpu(ecc_data->ecc_address);
+ ecc_syndrome = le64_to_cpu(ecc_data->ecc_syndrom);
+ memory_wrapper_idx = ecc_data->memory_wrapper_idx;
+
+ if (has_block_id) {
+ block_id = le16_to_cpu(ecc_data->block_id);
+ gaudi2_print_event(hdev, event_type, !ecc_data->is_critical,
+ "ECC error detected. address: %#llx. Syndrome: %#llx. wrapper id %u. block id %#x. critical %u.",
+ ecc_address, ecc_syndrome, memory_wrapper_idx, block_id,
+ ecc_data->is_critical);
+ } else {
+ gaudi2_print_event(hdev, event_type, !ecc_data->is_critical,
+ "ECC error detected. address: %#llx. Syndrome: %#llx. wrapper id %u. critical %u.",
+ ecc_address, ecc_syndrome, memory_wrapper_idx, ecc_data->is_critical);
+ }
+
+ return !!ecc_data->is_critical;
+}
+
+static void handle_lower_qman_data_on_err(struct hl_device *hdev, u64 qman_base, u32 engine_id)
+{
+ struct undefined_opcode_info *undef_opcode = &hdev->captured_err_info.undef_opcode;
+ u64 cq_ptr, cp_current_inst;
+ u32 lo, hi, cq_size, cp_sts;
+ bool is_arc_cq;
+
+ cp_sts = RREG32(qman_base + QM_CP_STS_4_OFFSET);
+ is_arc_cq = FIELD_GET(PDMA0_QM_CP_STS_CUR_CQ_MASK, cp_sts); /* 0 - legacy CQ, 1 - ARC_CQ */
+
+ if (is_arc_cq) {
+ lo = RREG32(qman_base + QM_ARC_CQ_PTR_LO_STS_OFFSET);
+ hi = RREG32(qman_base + QM_ARC_CQ_PTR_HI_STS_OFFSET);
+ cq_ptr = ((u64) hi) << 32 | lo;
+ cq_size = RREG32(qman_base + QM_ARC_CQ_TSIZE_STS_OFFSET);
+ } else {
+ lo = RREG32(qman_base + QM_CQ_PTR_LO_STS_4_OFFSET);
+ hi = RREG32(qman_base + QM_CQ_PTR_HI_STS_4_OFFSET);
+ cq_ptr = ((u64) hi) << 32 | lo;
+ cq_size = RREG32(qman_base + QM_CQ_TSIZE_STS_4_OFFSET);
+ }
+
+ lo = RREG32(qman_base + QM_CP_CURRENT_INST_LO_4_OFFSET);
+ hi = RREG32(qman_base + QM_CP_CURRENT_INST_HI_4_OFFSET);
+ cp_current_inst = ((u64) hi) << 32 | lo;
+
+ dev_info(hdev->dev,
+ "LowerQM. %sCQ: {ptr %#llx, size %u}, CP: {instruction %#018llx}\n",
+ is_arc_cq ? "ARC_" : "", cq_ptr, cq_size, cp_current_inst);
+
+ if (undef_opcode->write_enable) {
+ memset(undef_opcode, 0, sizeof(*undef_opcode));
+ undef_opcode->timestamp = ktime_get();
+ undef_opcode->cq_addr = cq_ptr;
+ undef_opcode->cq_size = cq_size;
+ undef_opcode->engine_id = engine_id;
+ undef_opcode->stream_id = QMAN_STREAMS;
+ undef_opcode->write_enable = 0;
+ }
+}
+
+static int gaudi2_handle_qman_err_generic(struct hl_device *hdev, u16 event_type,
+ u64 qman_base, u32 qid_base, u64 *event_mask)
+{
+ u32 i, j, glbl_sts_val, arb_err_val, num_error_causes, error_count = 0;
+ u64 glbl_sts_addr, arb_err_addr;
+ char reg_desc[32];
+
+ glbl_sts_addr = qman_base + (mmDCORE0_TPC0_QM_GLBL_ERR_STS_0 - mmDCORE0_TPC0_QM_BASE);
+ arb_err_addr = qman_base + (mmDCORE0_TPC0_QM_ARB_ERR_CAUSE - mmDCORE0_TPC0_QM_BASE);
+
+ /* Iterate through all stream GLBL_ERR_STS registers + Lower CP */
+ for (i = 0 ; i < QMAN_STREAMS + 1 ; i++) {
+ glbl_sts_val = RREG32(glbl_sts_addr + 4 * i);
+
+ if (!glbl_sts_val)
+ continue;
+
+ if (i == QMAN_STREAMS) {
+ snprintf(reg_desc, ARRAY_SIZE(reg_desc), "LowerQM");
+ num_error_causes = GAUDI2_NUM_OF_LOWER_QM_ERR_CAUSE;
+ } else {
+ snprintf(reg_desc, ARRAY_SIZE(reg_desc), "stream%u", i);
+ num_error_causes = GAUDI2_NUM_OF_QM_ERR_CAUSE;
+ }
+
+ for (j = 0 ; j < num_error_causes ; j++)
+ if (glbl_sts_val & BIT(j)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "%s. err cause: %s", reg_desc,
+ i == QMAN_STREAMS ?
+ gaudi2_lower_qman_error_cause[j] :
+ gaudi2_qman_error_cause[j]);
+ error_count++;
+ }
+
+ /* Check for undefined opcode error in lower QM */
+ if ((i == QMAN_STREAMS) &&
+ (glbl_sts_val & PDMA0_QM_GLBL_ERR_STS_CP_UNDEF_CMD_ERR_MASK)) {
+ handle_lower_qman_data_on_err(hdev, qman_base,
+ gaudi2_queue_id_to_engine_id[qid_base]);
+ *event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;
+ }
+ }
+
+ arb_err_val = RREG32(arb_err_addr);
+
+ if (!arb_err_val)
+ goto out;
+
+ for (j = 0 ; j < GAUDI2_NUM_OF_QM_ARB_ERR_CAUSE ; j++) {
+ if (arb_err_val & BIT(j)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "ARB_ERR. err cause: %s",
+ gaudi2_qman_arb_error_cause[j]);
+ error_count++;
+ }
+ }
+
+out:
+ return error_count;
+}
+
+static void gaudi2_razwi_rr_hbw_shared_printf_info(struct hl_device *hdev,
+ u64 rtr_mstr_if_base_addr, bool is_write, char *name,
+ enum gaudi2_engine_id id, u64 *event_mask)
+{
+ u32 razwi_hi, razwi_lo, razwi_xy;
+ u16 eng_id = id;
+ u8 rd_wr_flag;
+
+ if (is_write) {
+ razwi_hi = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_HI);
+ razwi_lo = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_LO);
+ razwi_xy = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_XY);
+ rd_wr_flag = HL_RAZWI_WRITE;
+ } else {
+ razwi_hi = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_HI);
+ razwi_lo = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_LO);
+ razwi_xy = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_XY);
+ rd_wr_flag = HL_RAZWI_READ;
+ }
+
+ hl_handle_razwi(hdev, (u64)razwi_hi << 32 | razwi_lo, &eng_id, 1,
+ rd_wr_flag | HL_RAZWI_HBW, event_mask);
+
+ dev_err_ratelimited(hdev->dev,
+ "%s-RAZWI SHARED RR HBW %s error, address %#llx, Initiator coordinates 0x%x\n",
+ name, is_write ? "WR" : "RD", (u64)razwi_hi << 32 | razwi_lo, razwi_xy);
+}
+
+static void gaudi2_razwi_rr_lbw_shared_printf_info(struct hl_device *hdev,
+ u64 rtr_mstr_if_base_addr, bool is_write, char *name,
+ enum gaudi2_engine_id id, u64 *event_mask)
+{
+ u64 razwi_addr = CFG_BASE;
+ u32 razwi_xy;
+ u16 eng_id = id;
+ u8 rd_wr_flag;
+
+ if (is_write) {
+ razwi_addr += RREG32(rtr_mstr_if_base_addr + RR_SHRD_LBW_AW_RAZWI);
+ razwi_xy = RREG32(rtr_mstr_if_base_addr + RR_SHRD_LBW_AW_RAZWI_XY);
+ rd_wr_flag = HL_RAZWI_WRITE;
+ } else {
+ razwi_addr += RREG32(rtr_mstr_if_base_addr + RR_SHRD_LBW_AR_RAZWI);
+ razwi_xy = RREG32(rtr_mstr_if_base_addr + RR_SHRD_LBW_AR_RAZWI_XY);
+ rd_wr_flag = HL_RAZWI_READ;
+ }
+
+ hl_handle_razwi(hdev, razwi_addr, &eng_id, 1, rd_wr_flag | HL_RAZWI_LBW, event_mask);
+ dev_err_ratelimited(hdev->dev,
+ "%s-RAZWI SHARED RR LBW %s error, mstr_if 0x%llx, captured address 0x%llX Initiator coordinates 0x%x\n",
+ name, is_write ? "WR" : "RD", rtr_mstr_if_base_addr, razwi_addr,
+ razwi_xy);
+}
+
+static enum gaudi2_engine_id gaudi2_razwi_calc_engine_id(struct hl_device *hdev,
+ enum razwi_event_sources module, u8 module_idx)
+{
+ switch (module) {
+ case RAZWI_TPC:
+ if (module_idx == (NUM_OF_TPC_PER_DCORE * NUM_OF_DCORES))
+ return GAUDI2_DCORE0_ENGINE_ID_TPC_6;
+ return (((module_idx / NUM_OF_TPC_PER_DCORE) * ENGINE_ID_DCORE_OFFSET) +
+ (module_idx % NUM_OF_TPC_PER_DCORE) +
+ (GAUDI2_DCORE0_ENGINE_ID_TPC_0 - GAUDI2_DCORE0_ENGINE_ID_EDMA_0));
+
+ case RAZWI_MME:
+ return ((GAUDI2_DCORE0_ENGINE_ID_MME - GAUDI2_DCORE0_ENGINE_ID_EDMA_0) +
+ (module_idx * ENGINE_ID_DCORE_OFFSET));
+
+ case RAZWI_EDMA:
+ return (((module_idx / NUM_OF_EDMA_PER_DCORE) * ENGINE_ID_DCORE_OFFSET) +
+ (module_idx % NUM_OF_EDMA_PER_DCORE));
+
+ case RAZWI_PDMA:
+ return (GAUDI2_ENGINE_ID_PDMA_0 + module_idx);
+
+ case RAZWI_NIC:
+ return (GAUDI2_ENGINE_ID_NIC0_0 + (NIC_NUMBER_OF_QM_PER_MACRO * module_idx));
+
+ case RAZWI_DEC:
+ if (module_idx == 8)
+ return GAUDI2_PCIE_ENGINE_ID_DEC_0;
+
+ if (module_idx == 9)
+ return GAUDI2_PCIE_ENGINE_ID_DEC_1;
+ ;
+ return (((module_idx / NUM_OF_DEC_PER_DCORE) * ENGINE_ID_DCORE_OFFSET) +
+ (module_idx % NUM_OF_DEC_PER_DCORE) +
+ (GAUDI2_DCORE0_ENGINE_ID_DEC_0 - GAUDI2_DCORE0_ENGINE_ID_EDMA_0));
+
+ case RAZWI_ROT:
+ return GAUDI2_ENGINE_ID_ROT_0 + module_idx;
+
+ case RAZWI_ARC_FARM:
+ return GAUDI2_ENGINE_ID_ARC_FARM;
+
+ default:
+ return GAUDI2_ENGINE_ID_SIZE;
+ }
+}
+
+/*
+ * This function handles RR(Range register) hit events.
+ * raised be initiators not PSOC RAZWI.
+ */
+static void gaudi2_ack_module_razwi_event_handler(struct hl_device *hdev,
+ enum razwi_event_sources module, u8 module_idx,
+ u8 module_sub_idx, u64 *event_mask)
+{
+ bool via_sft = false;
+ u32 hbw_rtr_id, lbw_rtr_id, dcore_id, dcore_rtr_id, eng_id, binned_idx;
+ u64 hbw_rtr_mstr_if_base_addr, lbw_rtr_mstr_if_base_addr;
+ u32 hbw_shrd_aw = 0, hbw_shrd_ar = 0;
+ u32 lbw_shrd_aw = 0, lbw_shrd_ar = 0;
+ char initiator_name[64];
+
+ switch (module) {
+ case RAZWI_TPC:
+ sprintf(initiator_name, "TPC_%u", module_idx);
+ if (hdev->tpc_binning) {
+ binned_idx = __ffs(hdev->tpc_binning);
+ if (binned_idx == module_idx)
+ module_idx = TPC_ID_DCORE0_TPC6;
+ }
+
+ hbw_rtr_id = gaudi2_tpc_initiator_hbw_rtr_id[module_idx];
+ lbw_rtr_id = gaudi2_tpc_initiator_lbw_rtr_id[module_idx];
+ break;
+ case RAZWI_MME:
+ sprintf(initiator_name, "MME_%u", module_idx);
+ switch (module_sub_idx) {
+ case MME_WAP0:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].wap0;
+ break;
+ case MME_WAP1:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].wap1;
+ break;
+ case MME_WRITE:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].write;
+ break;
+ case MME_READ:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].read;
+ break;
+ case MME_SBTE0:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].sbte0;
+ break;
+ case MME_SBTE1:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].sbte1;
+ break;
+ case MME_SBTE2:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].sbte2;
+ break;
+ case MME_SBTE3:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].sbte3;
+ break;
+ case MME_SBTE4:
+ hbw_rtr_id = gaudi2_mme_initiator_rtr_id[module_idx].sbte4;
+ break;
+ default:
+ return;
+ }
+ lbw_rtr_id = hbw_rtr_id;
+ break;
+ case RAZWI_EDMA:
+ hbw_rtr_mstr_if_base_addr = gaudi2_edma_initiator_hbw_sft[module_idx];
+ dcore_id = module_idx / NUM_OF_EDMA_PER_DCORE;
+ /* SFT has separate MSTR_IF for LBW, only there we can
+ * read the LBW razwi related registers
+ */
+ lbw_rtr_mstr_if_base_addr = mmSFT0_LBW_RTR_IF_MSTR_IF_RR_SHRD_HBW_BASE +
+ dcore_id * SFT_DCORE_OFFSET;
+ via_sft = true;
+ sprintf(initiator_name, "EDMA_%u", module_idx);
+ break;
+ case RAZWI_PDMA:
+ hbw_rtr_id = gaudi2_pdma_initiator_hbw_rtr_id[module_idx];
+ lbw_rtr_id = gaudi2_pdma_initiator_lbw_rtr_id[module_idx];
+ sprintf(initiator_name, "PDMA_%u", module_idx);
+ break;
+ case RAZWI_NIC:
+ hbw_rtr_id = gaudi2_nic_initiator_hbw_rtr_id[module_idx];
+ lbw_rtr_id = gaudi2_nic_initiator_lbw_rtr_id[module_idx];
+ sprintf(initiator_name, "NIC_%u", module_idx);
+ break;
+ case RAZWI_DEC:
+ sprintf(initiator_name, "DEC_%u", module_idx);
+ if (hdev->decoder_binning) {
+ binned_idx = __ffs(hdev->decoder_binning);
+ if (binned_idx == module_idx)
+ module_idx = DEC_ID_PCIE_VDEC1;
+ }
+ hbw_rtr_id = gaudi2_dec_initiator_hbw_rtr_id[module_idx];
+ lbw_rtr_id = gaudi2_dec_initiator_lbw_rtr_id[module_idx];
+ break;
+ case RAZWI_ROT:
+ hbw_rtr_id = gaudi2_rot_initiator_hbw_rtr_id[module_idx];
+ lbw_rtr_id = gaudi2_rot_initiator_lbw_rtr_id[module_idx];
+ sprintf(initiator_name, "ROT_%u", module_idx);
+ break;
+ case RAZWI_ARC_FARM:
+ lbw_rtr_id = DCORE1_RTR5;
+ hbw_rtr_id = DCORE1_RTR7;
+ sprintf(initiator_name, "ARC_FARM_%u", module_idx);
+ break;
+ default:
+ return;
+ }
+
+ /* Find router mstr_if register base */
+ if (!via_sft) {
+ dcore_id = hbw_rtr_id / NUM_OF_RTR_PER_DCORE;
+ dcore_rtr_id = hbw_rtr_id % NUM_OF_RTR_PER_DCORE;
+ hbw_rtr_mstr_if_base_addr = mmDCORE0_RTR0_CTRL_BASE +
+ dcore_id * DCORE_OFFSET +
+ dcore_rtr_id * DCORE_RTR_OFFSET +
+ RTR_MSTR_IF_OFFSET;
+ lbw_rtr_mstr_if_base_addr = hbw_rtr_mstr_if_base_addr +
+ (((s32)lbw_rtr_id - hbw_rtr_id) * DCORE_RTR_OFFSET);
+ }
+
+ /* Find out event cause by reading "RAZWI_HAPPENED" registers */
+ hbw_shrd_aw = RREG32(hbw_rtr_mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_HAPPENED);
+ hbw_shrd_ar = RREG32(hbw_rtr_mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_HAPPENED);
+ lbw_shrd_aw = RREG32(lbw_rtr_mstr_if_base_addr + RR_SHRD_LBW_AW_RAZWI_HAPPENED);
+ lbw_shrd_ar = RREG32(lbw_rtr_mstr_if_base_addr + RR_SHRD_LBW_AR_RAZWI_HAPPENED);
+
+ eng_id = gaudi2_razwi_calc_engine_id(hdev, module, module_idx);
+ if (hbw_shrd_aw) {
+ gaudi2_razwi_rr_hbw_shared_printf_info(hdev, hbw_rtr_mstr_if_base_addr, true,
+ initiator_name, eng_id, event_mask);
+
+ /* Clear event indication */
+ WREG32(hbw_rtr_mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_HAPPENED, hbw_shrd_aw);
+ }
+
+ if (hbw_shrd_ar) {
+ gaudi2_razwi_rr_hbw_shared_printf_info(hdev, hbw_rtr_mstr_if_base_addr, false,
+ initiator_name, eng_id, event_mask);
+
+ /* Clear event indication */
+ WREG32(hbw_rtr_mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_HAPPENED, hbw_shrd_ar);
+ }
+
+ if (lbw_shrd_aw) {
+ gaudi2_razwi_rr_lbw_shared_printf_info(hdev, lbw_rtr_mstr_if_base_addr, true,
+ initiator_name, eng_id, event_mask);
+
+ /* Clear event indication */
+ WREG32(lbw_rtr_mstr_if_base_addr + RR_SHRD_LBW_AW_RAZWI_HAPPENED, lbw_shrd_aw);
+ }
+
+ if (lbw_shrd_ar) {
+ gaudi2_razwi_rr_lbw_shared_printf_info(hdev, lbw_rtr_mstr_if_base_addr, false,
+ initiator_name, eng_id, event_mask);
+
+ /* Clear event indication */
+ WREG32(lbw_rtr_mstr_if_base_addr + RR_SHRD_LBW_AR_RAZWI_HAPPENED, lbw_shrd_ar);
+ }
+}
+
+static void gaudi2_check_if_razwi_happened(struct hl_device *hdev)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u8 mod_idx, sub_mod;
+
+ /* check all TPCs */
+ for (mod_idx = 0 ; mod_idx < (NUM_OF_TPC_PER_DCORE * NUM_OF_DCORES + 1) ; mod_idx++) {
+ if (prop->tpc_enabled_mask & BIT(mod_idx))
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_TPC, mod_idx, 0, NULL);
+ }
+
+ /* check all MMEs */
+ for (mod_idx = 0 ; mod_idx < (NUM_OF_MME_PER_DCORE * NUM_OF_DCORES) ; mod_idx++)
+ for (sub_mod = MME_WAP0 ; sub_mod < MME_INITIATORS_MAX ; sub_mod++)
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_MME, mod_idx,
+ sub_mod, NULL);
+
+ /* check all EDMAs */
+ for (mod_idx = 0 ; mod_idx < (NUM_OF_EDMA_PER_DCORE * NUM_OF_DCORES) ; mod_idx++)
+ if (prop->edma_enabled_mask & BIT(mod_idx))
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_EDMA, mod_idx, 0, NULL);
+
+ /* check all PDMAs */
+ for (mod_idx = 0 ; mod_idx < NUM_OF_PDMA ; mod_idx++)
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_PDMA, mod_idx, 0, NULL);
+
+ /* check all NICs */
+ for (mod_idx = 0 ; mod_idx < NIC_NUMBER_OF_PORTS ; mod_idx++)
+ if (hdev->nic_ports_mask & BIT(mod_idx))
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_NIC, mod_idx >> 1, 0,
+ NULL);
+
+ /* check all DECs */
+ for (mod_idx = 0 ; mod_idx < NUMBER_OF_DEC ; mod_idx++)
+ if (prop->decoder_enabled_mask & BIT(mod_idx))
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_DEC, mod_idx, 0, NULL);
+
+ /* check all ROTs */
+ for (mod_idx = 0 ; mod_idx < NUM_OF_ROT ; mod_idx++)
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_ROT, mod_idx, 0, NULL);
+}
+
+static int gaudi2_psoc_razwi_get_engines(struct gaudi2_razwi_info *razwi_info, u32 array_size,
+ u32 axuser_xy, u32 *base, u16 *eng_id,
+ char *eng_name)
+{
+
+ int i, num_of_eng = 0;
+ u16 str_size = 0;
+
+ for (i = 0 ; i < array_size ; i++) {
+ if (axuser_xy != razwi_info[i].axuser_xy)
+ continue;
+
+ eng_id[num_of_eng] = razwi_info[i].eng_id;
+ base[num_of_eng] = razwi_info[i].rtr_ctrl;
+ if (!num_of_eng)
+ str_size += scnprintf(eng_name + str_size,
+ PSOC_RAZWI_ENG_STR_SIZE - str_size, "%s",
+ razwi_info[i].eng_name);
+ else
+ str_size += scnprintf(eng_name + str_size,
+ PSOC_RAZWI_ENG_STR_SIZE - str_size, " or %s",
+ razwi_info[i].eng_name);
+ num_of_eng++;
+ }
+
+ return num_of_eng;
+}
+
+static bool gaudi2_handle_psoc_razwi_happened(struct hl_device *hdev, u32 razwi_reg,
+ u64 *event_mask)
+{
+ u32 axuser_xy = RAZWI_GET_AXUSER_XY(razwi_reg), addr_hi = 0, addr_lo = 0;
+ u32 base[PSOC_RAZWI_MAX_ENG_PER_RTR];
+ u16 num_of_eng, eng_id[PSOC_RAZWI_MAX_ENG_PER_RTR];
+ char eng_name_str[PSOC_RAZWI_ENG_STR_SIZE];
+ bool razwi_happened = false;
+ u64 addr;
+ int i;
+
+ num_of_eng = gaudi2_psoc_razwi_get_engines(common_razwi_info, ARRAY_SIZE(common_razwi_info),
+ axuser_xy, base, eng_id, eng_name_str);
+
+ /* If no match for XY coordinates, try to find it in MME razwi table */
+ if (!num_of_eng) {
+ axuser_xy = RAZWI_GET_AXUSER_LOW_XY(razwi_reg);
+ num_of_eng = gaudi2_psoc_razwi_get_engines(mme_razwi_info,
+ ARRAY_SIZE(mme_razwi_info),
+ axuser_xy, base, eng_id,
+ eng_name_str);
+ }
+
+ for (i = 0 ; i < num_of_eng ; i++) {
+ if (RREG32(base[i] + DEC_RAZWI_HBW_AW_SET)) {
+ addr_hi = RREG32(base[i] + DEC_RAZWI_HBW_AW_ADDR_HI);
+ addr_lo = RREG32(base[i] + DEC_RAZWI_HBW_AW_ADDR_LO);
+ addr = ((u64)addr_hi << 32) + addr_lo;
+ if (addr) {
+ dev_err(hdev->dev,
+ "PSOC HBW AW RAZWI: %s, address (aligned to 128 byte): 0x%llX\n",
+ eng_name_str, addr);
+ hl_handle_razwi(hdev, addr, &eng_id[0],
+ num_of_eng, HL_RAZWI_HBW | HL_RAZWI_WRITE, event_mask);
+ razwi_happened = true;
+ }
+ }
+
+ if (RREG32(base[i] + DEC_RAZWI_HBW_AR_SET)) {
+ addr_hi = RREG32(base[i] + DEC_RAZWI_HBW_AR_ADDR_HI);
+ addr_lo = RREG32(base[i] + DEC_RAZWI_HBW_AR_ADDR_LO);
+ addr = ((u64)addr_hi << 32) + addr_lo;
+ if (addr) {
+ dev_err(hdev->dev,
+ "PSOC HBW AR RAZWI: %s, address (aligned to 128 byte): 0x%llX\n",
+ eng_name_str, addr);
+ hl_handle_razwi(hdev, addr, &eng_id[0],
+ num_of_eng, HL_RAZWI_HBW | HL_RAZWI_READ, event_mask);
+ razwi_happened = true;
+ }
+ }
+
+ if (RREG32(base[i] + DEC_RAZWI_LBW_AW_SET)) {
+ addr_lo = RREG32(base[i] + DEC_RAZWI_LBW_AW_ADDR);
+ if (addr_lo) {
+ dev_err(hdev->dev,
+ "PSOC LBW AW RAZWI: %s, address (aligned to 128 byte): 0x%X\n",
+ eng_name_str, addr_lo);
+ hl_handle_razwi(hdev, addr_lo, &eng_id[0],
+ num_of_eng, HL_RAZWI_LBW | HL_RAZWI_WRITE, event_mask);
+ razwi_happened = true;
+ }
+ }
+
+ if (RREG32(base[i] + DEC_RAZWI_LBW_AR_SET)) {
+ addr_lo = RREG32(base[i] + DEC_RAZWI_LBW_AR_ADDR);
+ if (addr_lo) {
+ dev_err(hdev->dev,
+ "PSOC LBW AR RAZWI: %s, address (aligned to 128 byte): 0x%X\n",
+ eng_name_str, addr_lo);
+ hl_handle_razwi(hdev, addr_lo, &eng_id[0],
+ num_of_eng, HL_RAZWI_LBW | HL_RAZWI_READ, event_mask);
+ razwi_happened = true;
+ }
+ }
+ /* In common case the loop will break, when there is only one engine id, or
+ * several engines with the same router. The exceptional case is with psoc razwi
+ * from EDMA, where it's possible to get axuser id which fits 2 routers (2
+ * interfaces of sft router). In this case, maybe the first router won't hold info
+ * and we will need to iterate on the other router.
+ */
+ if (razwi_happened)
+ break;
+ }
+
+ return razwi_happened;
+}
+
+/* PSOC RAZWI interrupt occurs only when trying to access a bad address */
+static int gaudi2_ack_psoc_razwi_event_handler(struct hl_device *hdev, u64 *event_mask)
+{
+ u32 razwi_mask_info, razwi_intr = 0, error_count = 0;
+
+ if (hdev->pldm || !(hdev->fw_components & FW_TYPE_LINUX)) {
+ razwi_intr = RREG32(mmPSOC_GLOBAL_CONF_RAZWI_INTERRUPT);
+ if (!razwi_intr)
+ return 0;
+ }
+
+ razwi_mask_info = RREG32(mmPSOC_GLOBAL_CONF_RAZWI_MASK_INFO);
+
+ dev_err_ratelimited(hdev->dev,
+ "PSOC RAZWI interrupt: Mask %d, AR %d, AW %d, AXUSER_L 0x%x AXUSER_H 0x%x\n",
+ FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_MASK_MASK, razwi_mask_info),
+ FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AR_MASK, razwi_mask_info),
+ FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AW_MASK, razwi_mask_info),
+ FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_L_MASK, razwi_mask_info),
+ FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_H_MASK, razwi_mask_info));
+
+ if (gaudi2_handle_psoc_razwi_happened(hdev, razwi_mask_info, event_mask))
+ error_count++;
+ else
+ dev_err_ratelimited(hdev->dev,
+ "PSOC RAZWI interrupt: invalid razwi info (0x%x)\n",
+ razwi_mask_info);
+
+ /* Clear Interrupts only on pldm or if f/w doesn't handle interrupts */
+ if (hdev->pldm || !(hdev->fw_components & FW_TYPE_LINUX))
+ WREG32(mmPSOC_GLOBAL_CONF_RAZWI_INTERRUPT, razwi_intr);
+
+ return error_count;
+}
+
+static int _gaudi2_handle_qm_sei_err(struct hl_device *hdev, u64 qman_base, u16 event_type)
+{
+ u32 i, sts_val, sts_clr_val = 0, error_count = 0;
+
+ sts_val = RREG32(qman_base + QM_SEI_STATUS_OFFSET);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_QM_SEI_ERR_CAUSE ; i++) {
+ if (sts_val & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_qm_sei_error_cause[i]);
+ sts_clr_val |= BIT(i);
+ error_count++;
+ }
+ }
+
+ WREG32(qman_base + QM_SEI_STATUS_OFFSET, sts_clr_val);
+
+ return error_count;
+}
+
+static int gaudi2_handle_qm_sei_err(struct hl_device *hdev, u16 event_type,
+ bool extended_err_check, u64 *event_mask)
+{
+ enum razwi_event_sources module;
+ u32 error_count = 0;
+ u64 qman_base;
+ u8 index;
+
+ switch (event_type) {
+ case GAUDI2_EVENT_TPC0_AXI_ERR_RSP ... GAUDI2_EVENT_TPC23_AXI_ERR_RSP:
+ index = event_type - GAUDI2_EVENT_TPC0_AXI_ERR_RSP;
+ qman_base = mmDCORE0_TPC0_QM_BASE +
+ (index / NUM_OF_TPC_PER_DCORE) * DCORE_OFFSET +
+ (index % NUM_OF_TPC_PER_DCORE) * DCORE_TPC_OFFSET;
+ module = RAZWI_TPC;
+ break;
+ case GAUDI2_EVENT_TPC24_AXI_ERR_RSP:
+ qman_base = mmDCORE0_TPC6_QM_BASE;
+ module = RAZWI_TPC;
+ break;
+ case GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME1_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME2_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME3_CTRL_AXI_ERROR_RESPONSE:
+ index = (event_type - GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE) /
+ (GAUDI2_EVENT_MME1_CTRL_AXI_ERROR_RESPONSE -
+ GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE);
+ qman_base = mmDCORE0_MME_QM_BASE + index * DCORE_OFFSET;
+ module = RAZWI_MME;
+ break;
+ case GAUDI2_EVENT_PDMA_CH0_AXI_ERR_RSP:
+ case GAUDI2_EVENT_PDMA_CH1_AXI_ERR_RSP:
+ index = event_type - GAUDI2_EVENT_PDMA_CH0_AXI_ERR_RSP;
+ qman_base = mmPDMA0_QM_BASE + index * PDMA_OFFSET;
+ module = RAZWI_PDMA;
+ break;
+ case GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_ROTATOR1_AXI_ERROR_RESPONSE:
+ index = event_type - GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE;
+ qman_base = mmROT0_QM_BASE + index * ROT_OFFSET;
+ module = RAZWI_ROT;
+ break;
+ default:
+ return 0;
+ }
+
+ error_count = _gaudi2_handle_qm_sei_err(hdev, qman_base, event_type);
+
+ /* There is a single event per NIC macro, so should check its both QMAN blocks */
+ if (event_type >= GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE &&
+ event_type <= GAUDI2_EVENT_NIC11_AXI_ERROR_RESPONSE)
+ error_count += _gaudi2_handle_qm_sei_err(hdev,
+ qman_base + NIC_QM_OFFSET, event_type);
+
+ if (extended_err_check) {
+ /* check if RAZWI happened */
+ gaudi2_ack_module_razwi_event_handler(hdev, module, 0, 0, event_mask);
+ hl_check_for_glbl_errors(hdev);
+ }
+
+ return error_count;
+}
+
+static int gaudi2_handle_qman_err(struct hl_device *hdev, u16 event_type, u64 *event_mask)
+{
+ u32 qid_base, error_count = 0;
+ u64 qman_base;
+ u8 index = 0;
+
+ switch (event_type) {
+ case GAUDI2_EVENT_TPC0_QM ... GAUDI2_EVENT_TPC5_QM:
+ index = event_type - GAUDI2_EVENT_TPC0_QM;
+ qid_base = GAUDI2_QUEUE_ID_DCORE0_TPC_0_0 + index * QMAN_STREAMS;
+ qman_base = mmDCORE0_TPC0_QM_BASE + index * DCORE_TPC_OFFSET;
+ break;
+ case GAUDI2_EVENT_TPC6_QM ... GAUDI2_EVENT_TPC11_QM:
+ index = event_type - GAUDI2_EVENT_TPC6_QM;
+ qid_base = GAUDI2_QUEUE_ID_DCORE1_TPC_0_0 + index * QMAN_STREAMS;
+ qman_base = mmDCORE1_TPC0_QM_BASE + index * DCORE_TPC_OFFSET;
+ break;
+ case GAUDI2_EVENT_TPC12_QM ... GAUDI2_EVENT_TPC17_QM:
+ index = event_type - GAUDI2_EVENT_TPC12_QM;
+ qid_base = GAUDI2_QUEUE_ID_DCORE2_TPC_0_0 + index * QMAN_STREAMS;
+ qman_base = mmDCORE2_TPC0_QM_BASE + index * DCORE_TPC_OFFSET;
+ break;
+ case GAUDI2_EVENT_TPC18_QM ... GAUDI2_EVENT_TPC23_QM:
+ index = event_type - GAUDI2_EVENT_TPC18_QM;
+ qid_base = GAUDI2_QUEUE_ID_DCORE3_TPC_0_0 + index * QMAN_STREAMS;
+ qman_base = mmDCORE3_TPC0_QM_BASE + index * DCORE_TPC_OFFSET;
+ break;
+ case GAUDI2_EVENT_TPC24_QM:
+ qid_base = GAUDI2_QUEUE_ID_DCORE0_TPC_6_0;
+ qman_base = mmDCORE0_TPC6_QM_BASE;
+ break;
+ case GAUDI2_EVENT_MME0_QM:
+ qid_base = GAUDI2_QUEUE_ID_DCORE0_MME_0_0;
+ qman_base = mmDCORE0_MME_QM_BASE;
+ break;
+ case GAUDI2_EVENT_MME1_QM:
+ qid_base = GAUDI2_QUEUE_ID_DCORE1_MME_0_0;
+ qman_base = mmDCORE1_MME_QM_BASE;
+ break;
+ case GAUDI2_EVENT_MME2_QM:
+ qid_base = GAUDI2_QUEUE_ID_DCORE2_MME_0_0;
+ qman_base = mmDCORE2_MME_QM_BASE;
+ break;
+ case GAUDI2_EVENT_MME3_QM:
+ qid_base = GAUDI2_QUEUE_ID_DCORE3_MME_0_0;
+ qman_base = mmDCORE3_MME_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA0_QM:
+ index = 0;
+ qid_base = GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0;
+ qman_base = mmDCORE0_EDMA0_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA1_QM:
+ index = 1;
+ qid_base = GAUDI2_QUEUE_ID_DCORE0_EDMA_1_0;
+ qman_base = mmDCORE0_EDMA1_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA2_QM:
+ index = 2;
+ qid_base = GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0;
+ qman_base = mmDCORE1_EDMA0_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA3_QM:
+ index = 3;
+ qid_base = GAUDI2_QUEUE_ID_DCORE1_EDMA_1_0;
+ qman_base = mmDCORE1_EDMA1_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA4_QM:
+ index = 4;
+ qid_base = GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0;
+ qman_base = mmDCORE2_EDMA0_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA5_QM:
+ index = 5;
+ qid_base = GAUDI2_QUEUE_ID_DCORE2_EDMA_1_0;
+ qman_base = mmDCORE2_EDMA1_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA6_QM:
+ index = 6;
+ qid_base = GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0;
+ qman_base = mmDCORE3_EDMA0_QM_BASE;
+ break;
+ case GAUDI2_EVENT_HDMA7_QM:
+ index = 7;
+ qid_base = GAUDI2_QUEUE_ID_DCORE3_EDMA_1_0;
+ qman_base = mmDCORE3_EDMA1_QM_BASE;
+ break;
+ case GAUDI2_EVENT_PDMA0_QM:
+ qid_base = GAUDI2_QUEUE_ID_PDMA_0_0;
+ qman_base = mmPDMA0_QM_BASE;
+ break;
+ case GAUDI2_EVENT_PDMA1_QM:
+ qid_base = GAUDI2_QUEUE_ID_PDMA_1_0;
+ qman_base = mmPDMA1_QM_BASE;
+ break;
+ case GAUDI2_EVENT_ROTATOR0_ROT0_QM:
+ qid_base = GAUDI2_QUEUE_ID_ROT_0_0;
+ qman_base = mmROT0_QM_BASE;
+ break;
+ case GAUDI2_EVENT_ROTATOR1_ROT1_QM:
+ qid_base = GAUDI2_QUEUE_ID_ROT_1_0;
+ qman_base = mmROT1_QM_BASE;
+ break;
+ default:
+ return 0;
+ }
+
+ error_count = gaudi2_handle_qman_err_generic(hdev, event_type, qman_base,
+ qid_base, event_mask);
+
+ /* Handle EDMA QM SEI here because there is no AXI error response event for EDMA */
+ if (event_type >= GAUDI2_EVENT_HDMA2_QM && event_type <= GAUDI2_EVENT_HDMA5_QM) {
+ error_count += _gaudi2_handle_qm_sei_err(hdev, qman_base, event_type);
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_EDMA, index, 0, event_mask);
+ }
+
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static int gaudi2_handle_arc_farm_sei_err(struct hl_device *hdev, u16 event_type, u64 *event_mask)
+{
+ u32 i, sts_val, sts_clr_val, error_count = 0, arc_farm;
+
+ for (arc_farm = 0 ; arc_farm < NUM_OF_ARC_FARMS_ARC ; arc_farm++) {
+ sts_clr_val = 0;
+ sts_val = RREG32(mmARC_FARM_ARC0_AUX_ARC_SEI_INTR_STS +
+ (arc_farm * ARC_FARM_OFFSET));
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_ARC_SEI_ERR_CAUSE ; i++) {
+ if (sts_val & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "ARC FARM ARC %u err cause: %s",
+ arc_farm, gaudi2_arc_sei_error_cause[i]);
+ sts_clr_val |= BIT(i);
+ error_count++;
+ }
+ }
+ WREG32(mmARC_FARM_ARC0_AUX_ARC_SEI_INTR_CLR + (arc_farm * ARC_FARM_OFFSET),
+ sts_clr_val);
+ }
+
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_ARC_FARM, 0, 0, event_mask);
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static int gaudi2_handle_cpu_sei_err(struct hl_device *hdev, u16 event_type)
+{
+ u32 i, sts_val, sts_clr_val = 0, error_count = 0;
+
+ sts_val = RREG32(mmCPU_IF_CPU_SEI_INTR_STS);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_CPU_SEI_ERR_CAUSE ; i++) {
+ if (sts_val & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_cpu_sei_error_cause[i]);
+ sts_clr_val |= BIT(i);
+ error_count++;
+ }
+ }
+
+ hl_check_for_glbl_errors(hdev);
+
+ WREG32(mmCPU_IF_CPU_SEI_INTR_CLR, sts_clr_val);
+
+ return error_count;
+}
+
+static int gaudi2_handle_rot_err(struct hl_device *hdev, u8 rot_index, u16 event_type,
+ struct hl_eq_razwi_with_intr_cause *razwi_with_intr_cause,
+ u64 *event_mask)
+{
+ u64 intr_cause_data = le64_to_cpu(razwi_with_intr_cause->intr_cause.intr_cause_data);
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_ROT_ERR_CAUSE ; i++)
+ if (intr_cause_data & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", guadi2_rot_error_cause[i]);
+ error_count++;
+ }
+
+ /* check if RAZWI happened */
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_ROT, rot_index, 0, event_mask);
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static int gaudi2_tpc_ack_interrupts(struct hl_device *hdev, u8 tpc_index, u16 event_type,
+ struct hl_eq_razwi_with_intr_cause *razwi_with_intr_cause,
+ u64 *event_mask)
+{
+ u64 intr_cause_data = le64_to_cpu(razwi_with_intr_cause->intr_cause.intr_cause_data);
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_TPC_INTR_CAUSE ; i++)
+ if (intr_cause_data & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "interrupt cause: %s", gaudi2_tpc_interrupts_cause[i]);
+ error_count++;
+ }
+
+ /* check if RAZWI happened */
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_TPC, tpc_index, 0, event_mask);
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static int gaudi2_handle_dec_err(struct hl_device *hdev, u8 dec_index, u16 event_type,
+ u64 *event_mask)
+{
+ u32 sts_addr, sts_val, sts_clr_val = 0, error_count = 0;
+ int i;
+
+ if (dec_index < NUM_OF_VDEC_PER_DCORE * NUM_OF_DCORES)
+ /* DCORE DEC */
+ sts_addr = mmDCORE0_VDEC0_BRDG_CTRL_CAUSE_INTR +
+ DCORE_OFFSET * (dec_index / NUM_OF_DEC_PER_DCORE) +
+ DCORE_VDEC_OFFSET * (dec_index % NUM_OF_DEC_PER_DCORE);
+ else
+ /* PCIE DEC */
+ sts_addr = mmPCIE_VDEC0_BRDG_CTRL_CAUSE_INTR + PCIE_VDEC_OFFSET *
+ (dec_index - NUM_OF_VDEC_PER_DCORE * NUM_OF_DCORES);
+
+ sts_val = RREG32(sts_addr);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_DEC_ERR_CAUSE ; i++) {
+ if (sts_val & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_dec_error_cause[i]);
+ sts_clr_val |= BIT(i);
+ error_count++;
+ }
+ }
+
+ /* check if RAZWI happened */
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_DEC, dec_index, 0, event_mask);
+ hl_check_for_glbl_errors(hdev);
+
+ /* Write 1 clear errors */
+ WREG32(sts_addr, sts_clr_val);
+
+ return error_count;
+}
+
+static int gaudi2_handle_mme_err(struct hl_device *hdev, u8 mme_index, u16 event_type,
+ u64 *event_mask)
+{
+ u32 sts_addr, sts_val, sts_clr_addr, sts_clr_val = 0, error_count = 0;
+ int i;
+
+ sts_addr = mmDCORE0_MME_CTRL_LO_INTR_CAUSE + DCORE_OFFSET * mme_index;
+ sts_clr_addr = mmDCORE0_MME_CTRL_LO_INTR_CLEAR + DCORE_OFFSET * mme_index;
+
+ sts_val = RREG32(sts_addr);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_MME_ERR_CAUSE ; i++) {
+ if (sts_val & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", guadi2_mme_error_cause[i]);
+ sts_clr_val |= BIT(i);
+ error_count++;
+ }
+ }
+
+ /* check if RAZWI happened */
+ for (i = MME_WRITE ; i < MME_INITIATORS_MAX ; i++)
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_MME, mme_index, i, event_mask);
+
+ hl_check_for_glbl_errors(hdev);
+
+ WREG32(sts_clr_addr, sts_clr_val);
+
+ return error_count;
+}
+
+static int gaudi2_handle_mme_sbte_err(struct hl_device *hdev, u16 event_type)
+{
+ /*
+ * We have a single error cause here but the report mechanism is
+ * buggy. Hence there is no good reason to fetch the cause so we
+ * just check for glbl_errors and exit.
+ */
+ hl_check_for_glbl_errors(hdev);
+
+ return GAUDI2_NA_EVENT_CAUSE;
+}
+
+static int gaudi2_handle_mme_wap_err(struct hl_device *hdev, u8 mme_index, u16 event_type,
+ u64 *event_mask)
+{
+ u32 sts_addr, sts_val, sts_clr_addr, sts_clr_val = 0, error_count = 0;
+ int i;
+
+ sts_addr = mmDCORE0_MME_ACC_INTR_CAUSE + DCORE_OFFSET * mme_index;
+ sts_clr_addr = mmDCORE0_MME_ACC_INTR_CLEAR + DCORE_OFFSET * mme_index;
+
+ sts_val = RREG32(sts_addr);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_MME_WAP_ERR_CAUSE ; i++) {
+ if (sts_val & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", guadi2_mme_wap_error_cause[i]);
+ sts_clr_val |= BIT(i);
+ error_count++;
+ }
+ }
+
+ /* check if RAZWI happened on WAP0/1 */
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_MME, mme_index, MME_WAP0, event_mask);
+ gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_MME, mme_index, MME_WAP1, event_mask);
+ hl_check_for_glbl_errors(hdev);
+
+ WREG32(sts_clr_addr, sts_clr_val);
+
+ return error_count;
+}
+
+static int gaudi2_handle_kdma_core_event(struct hl_device *hdev, u16 event_type,
+ u64 intr_cause_data)
+{
+ u32 error_count = 0;
+ int i;
+
+ /* If an AXI read or write error is received, an error is reported and
+ * interrupt message is sent. Due to an HW errata, when reading the cause
+ * register of the KDMA engine, the reported error is always HBW even if
+ * the actual error caused by a LBW KDMA transaction.
+ */
+ for (i = 0 ; i < GAUDI2_NUM_OF_DMA_CORE_INTR_CAUSE ; i++)
+ if (intr_cause_data & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_kdma_core_interrupts_cause[i]);
+ error_count++;
+ }
+
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static int gaudi2_handle_dma_core_event(struct hl_device *hdev, u16 event_type, u64 intr_cause)
+{
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_DMA_CORE_INTR_CAUSE ; i++)
+ if (intr_cause & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_dma_core_interrupts_cause[i]);
+ error_count++;
+ }
+
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static void gaudi2_print_pcie_mstr_rr_mstr_if_razwi_info(struct hl_device *hdev, u64 *event_mask)
+{
+ u32 mstr_if_base_addr = mmPCIE_MSTR_RR_MSTR_IF_RR_SHRD_HBW_BASE, razwi_happened_addr;
+
+ razwi_happened_addr = mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_HAPPENED;
+ if (RREG32(razwi_happened_addr)) {
+ gaudi2_razwi_rr_hbw_shared_printf_info(hdev, mstr_if_base_addr, true, "PCIE",
+ GAUDI2_ENGINE_ID_PCIE, event_mask);
+ WREG32(razwi_happened_addr, 0x1);
+ }
+
+ razwi_happened_addr = mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_HAPPENED;
+ if (RREG32(razwi_happened_addr)) {
+ gaudi2_razwi_rr_hbw_shared_printf_info(hdev, mstr_if_base_addr, false, "PCIE",
+ GAUDI2_ENGINE_ID_PCIE, event_mask);
+ WREG32(razwi_happened_addr, 0x1);
+ }
+
+ razwi_happened_addr = mstr_if_base_addr + RR_SHRD_LBW_AW_RAZWI_HAPPENED;
+ if (RREG32(razwi_happened_addr)) {
+ gaudi2_razwi_rr_lbw_shared_printf_info(hdev, mstr_if_base_addr, true, "PCIE",
+ GAUDI2_ENGINE_ID_PCIE, event_mask);
+ WREG32(razwi_happened_addr, 0x1);
+ }
+
+ razwi_happened_addr = mstr_if_base_addr + RR_SHRD_LBW_AR_RAZWI_HAPPENED;
+ if (RREG32(razwi_happened_addr)) {
+ gaudi2_razwi_rr_lbw_shared_printf_info(hdev, mstr_if_base_addr, false, "PCIE",
+ GAUDI2_ENGINE_ID_PCIE, event_mask);
+ WREG32(razwi_happened_addr, 0x1);
+ }
+}
+
+static int gaudi2_print_pcie_addr_dec_info(struct hl_device *hdev, u16 event_type,
+ u64 intr_cause_data, u64 *event_mask)
+{
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_PCIE_ADDR_DEC_ERR_CAUSE ; i++) {
+ if (!(intr_cause_data & BIT_ULL(i)))
+ continue;
+
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_pcie_addr_dec_error_cause[i]);
+ error_count++;
+
+ switch (intr_cause_data & BIT_ULL(i)) {
+ case PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK:
+ hl_check_for_glbl_errors(hdev);
+ break;
+ case PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK:
+ gaudi2_print_pcie_mstr_rr_mstr_if_razwi_info(hdev, event_mask);
+ break;
+ }
+ }
+
+ return error_count;
+}
+
+static int gaudi2_handle_pif_fatal(struct hl_device *hdev, u16 event_type,
+ u64 intr_cause_data)
+
+{
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_PMMU_FATAL_ERR_CAUSE ; i++) {
+ if (intr_cause_data & BIT_ULL(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_pmmu_fatal_interrupts_cause[i]);
+ error_count++;
+ }
+ }
+
+ return error_count;
+}
+
+static int gaudi2_handle_hif_fatal(struct hl_device *hdev, u16 event_type, u64 intr_cause_data)
+{
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_HIF_FATAL_ERR_CAUSE ; i++) {
+ if (intr_cause_data & BIT_ULL(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_hif_fatal_interrupts_cause[i]);
+ error_count++;
+ }
+ }
+
+ return error_count;
+}
+
+static void gaudi2_handle_page_error(struct hl_device *hdev, u64 mmu_base, bool is_pmmu,
+ u64 *event_mask)
+{
+ u32 valid, val;
+ u64 addr;
+
+ valid = RREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_ACCESS_PAGE_ERROR_VALID));
+
+ if (!(valid & DCORE0_HMMU0_MMU_ACCESS_PAGE_ERROR_VALID_PAGE_ERR_VALID_ENTRY_MASK))
+ return;
+
+ val = RREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_PAGE_ERROR_CAPTURE));
+ addr = val & DCORE0_HMMU0_MMU_PAGE_ERROR_CAPTURE_VA_63_32_MASK;
+ addr <<= 32;
+ addr |= RREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_PAGE_ERROR_CAPTURE_VA));
+
+ if (is_pmmu) {
+ dev_err_ratelimited(hdev->dev, "PMMU page fault on va 0x%llx\n", addr);
+ } else {
+ addr = gaudi2_mmu_descramble_addr(hdev, addr);
+ addr &= HW_UNSCRAMBLED_BITS_MASK;
+ dev_err_ratelimited(hdev->dev, "HMMU page fault on va range 0x%llx - 0x%llx\n",
+ addr, addr + ~HW_UNSCRAMBLED_BITS_MASK);
+ }
+
+ hl_handle_page_fault(hdev, addr, 0, is_pmmu, event_mask);
+
+ WREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_ACCESS_PAGE_ERROR_VALID), 0);
+}
+
+static void gaudi2_handle_access_error(struct hl_device *hdev, u64 mmu_base, bool is_pmmu)
+{
+ u32 valid, val;
+ u64 addr;
+
+ valid = RREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_ACCESS_PAGE_ERROR_VALID));
+
+ if (!(valid & DCORE0_HMMU0_MMU_ACCESS_PAGE_ERROR_VALID_ACCESS_ERR_VALID_ENTRY_MASK))
+ return;
+
+ val = RREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_ACCESS_ERROR_CAPTURE));
+ addr = val & DCORE0_HMMU0_MMU_ACCESS_ERROR_CAPTURE_VA_63_32_MASK;
+ addr <<= 32;
+ addr |= RREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_ACCESS_ERROR_CAPTURE_VA));
+
+ if (!is_pmmu)
+ addr = gaudi2_mmu_descramble_addr(hdev, addr);
+
+ dev_err_ratelimited(hdev->dev, "%s access error on va 0x%llx\n",
+ is_pmmu ? "PMMU" : "HMMU", addr);
+ WREG32(mmu_base + MMU_OFFSET(mmDCORE0_HMMU0_MMU_ACCESS_PAGE_ERROR_VALID), 0);
+}
+
+static int gaudi2_handle_mmu_spi_sei_generic(struct hl_device *hdev, u16 event_type,
+ u64 mmu_base, bool is_pmmu, u64 *event_mask)
+{
+ u32 spi_sei_cause, interrupt_clr = 0x0, error_count = 0;
+ int i;
+
+ spi_sei_cause = RREG32(mmu_base + MMU_SPI_SEI_CAUSE_OFFSET);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE ; i++) {
+ if (spi_sei_cause & BIT(i)) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s", gaudi2_mmu_spi_sei[i].cause);
+
+ if (i == 0)
+ gaudi2_handle_page_error(hdev, mmu_base, is_pmmu, event_mask);
+ else if (i == 1)
+ gaudi2_handle_access_error(hdev, mmu_base, is_pmmu);
+
+ if (gaudi2_mmu_spi_sei[i].clear_bit >= 0)
+ interrupt_clr |= BIT(gaudi2_mmu_spi_sei[i].clear_bit);
+
+ error_count++;
+ }
+ }
+
+ /* Clear cause */
+ WREG32_AND(mmu_base + MMU_SPI_SEI_CAUSE_OFFSET, ~spi_sei_cause);
+
+ /* Clear interrupt */
+ WREG32(mmu_base + MMU_INTERRUPT_CLR_OFFSET, interrupt_clr);
+
+ return error_count;
+}
+
+static int gaudi2_handle_sm_err(struct hl_device *hdev, u16 event_type, u8 sm_index)
+{
+ u32 sei_cause_addr, sei_cause_val, sei_cause_cause, sei_cause_log,
+ cq_intr_addr, cq_intr_val, cq_intr_queue_index, error_count = 0;
+ int i;
+
+ sei_cause_addr = mmDCORE0_SYNC_MNGR_GLBL_SM_SEI_CAUSE + DCORE_OFFSET * sm_index;
+ cq_intr_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_INTR + DCORE_OFFSET * sm_index;
+
+ sei_cause_val = RREG32(sei_cause_addr);
+ sei_cause_cause = FIELD_GET(DCORE0_SYNC_MNGR_GLBL_SM_SEI_CAUSE_CAUSE_MASK, sei_cause_val);
+ cq_intr_val = RREG32(cq_intr_addr);
+
+ /* SEI interrupt */
+ if (sei_cause_cause) {
+ /* There are corresponding SEI_CAUSE_log bits for every SEI_CAUSE_cause bit */
+ sei_cause_log = FIELD_GET(DCORE0_SYNC_MNGR_GLBL_SM_SEI_CAUSE_LOG_MASK,
+ sei_cause_val);
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_SM_SEI_ERR_CAUSE ; i++) {
+ if (!(sei_cause_cause & BIT(i)))
+ continue;
+
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s. %s: 0x%X",
+ gaudi2_sm_sei_cause[i].cause_name,
+ gaudi2_sm_sei_cause[i].log_name,
+ sei_cause_log);
+ error_count++;
+ break;
+ }
+
+ /* Clear SM_SEI_CAUSE */
+ WREG32(sei_cause_addr, 0);
+ }
+
+ /* CQ interrupt */
+ if (cq_intr_val & DCORE0_SYNC_MNGR_GLBL_CQ_INTR_CQ_SEC_INTR_MASK) {
+ cq_intr_queue_index =
+ FIELD_GET(DCORE0_SYNC_MNGR_GLBL_CQ_INTR_CQ_INTR_QUEUE_INDEX_MASK,
+ cq_intr_val);
+
+ dev_err_ratelimited(hdev->dev, "SM%u err. err cause: CQ_INTR. queue index: %u\n",
+ sm_index, cq_intr_queue_index);
+ error_count++;
+
+ /* Clear CQ_INTR */
+ WREG32(cq_intr_addr, 0);
+ }
+
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static u64 get_hmmu_base(u16 event_type)
+{
+ u8 dcore, index_in_dcore;
+
+ switch (event_type) {
+ case GAUDI2_EVENT_HMMU_0_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU0_SPI_BASE ... GAUDI2_EVENT_HMMU0_SECURITY_ERROR:
+ dcore = 0;
+ index_in_dcore = 0;
+ break;
+ case GAUDI2_EVENT_HMMU_1_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU1_SPI_BASE ... GAUDI2_EVENT_HMMU1_SECURITY_ERROR:
+ dcore = 1;
+ index_in_dcore = 0;
+ break;
+ case GAUDI2_EVENT_HMMU_2_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU2_SPI_BASE ... GAUDI2_EVENT_HMMU2_SECURITY_ERROR:
+ dcore = 0;
+ index_in_dcore = 1;
+ break;
+ case GAUDI2_EVENT_HMMU_3_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU3_SPI_BASE ... GAUDI2_EVENT_HMMU3_SECURITY_ERROR:
+ dcore = 1;
+ index_in_dcore = 1;
+ break;
+ case GAUDI2_EVENT_HMMU_4_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU4_SPI_BASE ... GAUDI2_EVENT_HMMU4_SECURITY_ERROR:
+ dcore = 3;
+ index_in_dcore = 2;
+ break;
+ case GAUDI2_EVENT_HMMU_5_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU5_SPI_BASE ... GAUDI2_EVENT_HMMU5_SECURITY_ERROR:
+ dcore = 2;
+ index_in_dcore = 2;
+ break;
+ case GAUDI2_EVENT_HMMU_6_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU6_SPI_BASE ... GAUDI2_EVENT_HMMU6_SECURITY_ERROR:
+ dcore = 3;
+ index_in_dcore = 3;
+ break;
+ case GAUDI2_EVENT_HMMU_7_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU7_SPI_BASE ... GAUDI2_EVENT_HMMU7_SECURITY_ERROR:
+ dcore = 2;
+ index_in_dcore = 3;
+ break;
+ case GAUDI2_EVENT_HMMU_8_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU8_SPI_BASE ... GAUDI2_EVENT_HMMU8_SECURITY_ERROR:
+ dcore = 0;
+ index_in_dcore = 2;
+ break;
+ case GAUDI2_EVENT_HMMU_9_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU9_SPI_BASE ... GAUDI2_EVENT_HMMU9_SECURITY_ERROR:
+ dcore = 1;
+ index_in_dcore = 2;
+ break;
+ case GAUDI2_EVENT_HMMU_10_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU10_SPI_BASE ... GAUDI2_EVENT_HMMU10_SECURITY_ERROR:
+ dcore = 0;
+ index_in_dcore = 3;
+ break;
+ case GAUDI2_EVENT_HMMU_11_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU11_SPI_BASE ... GAUDI2_EVENT_HMMU11_SECURITY_ERROR:
+ dcore = 1;
+ index_in_dcore = 3;
+ break;
+ case GAUDI2_EVENT_HMMU_12_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU12_SPI_BASE ... GAUDI2_EVENT_HMMU12_SECURITY_ERROR:
+ dcore = 3;
+ index_in_dcore = 0;
+ break;
+ case GAUDI2_EVENT_HMMU_13_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU13_SPI_BASE ... GAUDI2_EVENT_HMMU13_SECURITY_ERROR:
+ dcore = 2;
+ index_in_dcore = 0;
+ break;
+ case GAUDI2_EVENT_HMMU_14_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU14_SPI_BASE ... GAUDI2_EVENT_HMMU14_SECURITY_ERROR:
+ dcore = 3;
+ index_in_dcore = 1;
+ break;
+ case GAUDI2_EVENT_HMMU_15_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU15_SPI_BASE ... GAUDI2_EVENT_HMMU15_SECURITY_ERROR:
+ dcore = 2;
+ index_in_dcore = 1;
+ break;
+ default:
+ return ULONG_MAX;
+ }
+
+ return mmDCORE0_HMMU0_MMU_BASE + dcore * DCORE_OFFSET + index_in_dcore * DCORE_HMMU_OFFSET;
+}
+
+static int gaudi2_handle_mmu_spi_sei_err(struct hl_device *hdev, u16 event_type, u64 *event_mask)
+{
+ bool is_pmmu = false;
+ u32 error_count = 0;
+ u64 mmu_base;
+
+ switch (event_type) {
+ case GAUDI2_EVENT_HMMU_0_AXI_ERR_RSP ... GAUDI2_EVENT_HMMU_12_AXI_ERR_RSP:
+ case GAUDI2_EVENT_HMMU0_SPI_BASE ... GAUDI2_EVENT_HMMU12_SECURITY_ERROR:
+ mmu_base = get_hmmu_base(event_type);
+ break;
+
+ case GAUDI2_EVENT_PMMU0_PAGE_FAULT_WR_PERM ... GAUDI2_EVENT_PMMU0_SECURITY_ERROR:
+ case GAUDI2_EVENT_PMMU_AXI_ERR_RSP_0:
+ is_pmmu = true;
+ mmu_base = mmPMMU_HBW_MMU_BASE;
+ break;
+ default:
+ return 0;
+ }
+
+ if (mmu_base == ULONG_MAX)
+ return 0;
+
+ error_count = gaudi2_handle_mmu_spi_sei_generic(hdev, event_type, mmu_base,
+ is_pmmu, event_mask);
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+
+/* returns true if hard reset is required (ECC DERR or Read parity), false otherwise (ECC SERR) */
+static bool gaudi2_hbm_sei_handle_read_err(struct hl_device *hdev,
+ struct hl_eq_hbm_sei_read_err_intr_info *rd_err_data, u32 err_cnt)
+{
+ bool require_hard_reset = false;
+ u32 addr, beat, beat_shift;
+
+ dev_err_ratelimited(hdev->dev,
+ "READ ERROR count: ECC SERR: %d, ECC DERR: %d, RD_PARITY: %d\n",
+ FIELD_GET(HBM_ECC_SERR_CNTR_MASK, err_cnt),
+ FIELD_GET(HBM_ECC_DERR_CNTR_MASK, err_cnt),
+ FIELD_GET(HBM_RD_PARITY_CNTR_MASK, err_cnt));
+
+ addr = le32_to_cpu(rd_err_data->dbg_rd_err_addr.rd_addr_val);
+ dev_err_ratelimited(hdev->dev,
+ "READ ERROR address: sid(%u), bg(%u), ba(%u), col(%u), row(%u)\n",
+ FIELD_GET(HBM_RD_ADDR_SID_MASK, addr),
+ FIELD_GET(HBM_RD_ADDR_BG_MASK, addr),
+ FIELD_GET(HBM_RD_ADDR_BA_MASK, addr),
+ FIELD_GET(HBM_RD_ADDR_COL_MASK, addr),
+ FIELD_GET(HBM_RD_ADDR_ROW_MASK, addr));
+
+ /* For each beat (RDQS edge), look for possible errors and print relevant info */
+ for (beat = 0 ; beat < 4 ; beat++) {
+ if (le32_to_cpu(rd_err_data->dbg_rd_err_misc) &
+ (HBM_RD_ERR_SERR_BEAT0_MASK << beat))
+ dev_err_ratelimited(hdev->dev, "Beat%d ECC SERR: DM: %#x, Syndrome: %#x\n",
+ beat,
+ le32_to_cpu(rd_err_data->dbg_rd_err_dm),
+ le32_to_cpu(rd_err_data->dbg_rd_err_syndrome));
+
+ if (le32_to_cpu(rd_err_data->dbg_rd_err_misc) &
+ (HBM_RD_ERR_DERR_BEAT0_MASK << beat)) {
+ dev_err_ratelimited(hdev->dev, "Beat%d ECC DERR: DM: %#x, Syndrome: %#x\n",
+ beat,
+ le32_to_cpu(rd_err_data->dbg_rd_err_dm),
+ le32_to_cpu(rd_err_data->dbg_rd_err_syndrome));
+ require_hard_reset = true;
+ }
+
+ beat_shift = beat * HBM_RD_ERR_BEAT_SHIFT;
+ if (le32_to_cpu(rd_err_data->dbg_rd_err_misc) &
+ (HBM_RD_ERR_PAR_ERR_BEAT0_MASK << beat_shift)) {
+ dev_err_ratelimited(hdev->dev,
+ "Beat%d read PARITY: DM: %#x, PAR data: %#x\n",
+ beat,
+ le32_to_cpu(rd_err_data->dbg_rd_err_dm),
+ (le32_to_cpu(rd_err_data->dbg_rd_err_misc) &
+ (HBM_RD_ERR_PAR_DATA_BEAT0_MASK << beat_shift)) >>
+ (HBM_RD_ERR_PAR_DATA_BEAT0_SHIFT + beat_shift));
+ require_hard_reset = true;
+ }
+
+ dev_err_ratelimited(hdev->dev, "Beat%d DQ data:\n", beat);
+ dev_err_ratelimited(hdev->dev, "\t0x%08x\n",
+ le32_to_cpu(rd_err_data->dbg_rd_err_data[beat * 2]));
+ dev_err_ratelimited(hdev->dev, "\t0x%08x\n",
+ le32_to_cpu(rd_err_data->dbg_rd_err_data[beat * 2 + 1]));
+ }
+
+ return require_hard_reset;
+}
+
+static void gaudi2_hbm_sei_print_wr_par_info(struct hl_device *hdev,
+ struct hl_eq_hbm_sei_wr_par_intr_info *wr_par_err_data, u32 err_cnt)
+{
+ struct hbm_sei_wr_cmd_address *wr_cmd_addr = wr_par_err_data->dbg_last_wr_cmds;
+ u32 i, curr_addr, derr = wr_par_err_data->dbg_derr;
+
+ dev_err_ratelimited(hdev->dev, "WRITE PARITY ERROR count: %d\n", err_cnt);
+
+ dev_err_ratelimited(hdev->dev, "CK-0 DERR: 0x%02x, CK-1 DERR: 0x%02x\n",
+ derr & 0x3, derr & 0xc);
+
+ /* JIRA H6-3286 - the following prints may not be valid */
+ dev_err_ratelimited(hdev->dev, "Last latched write commands addresses:\n");
+ for (i = 0 ; i < HBM_WR_PAR_CMD_LIFO_LEN ; i++) {
+ curr_addr = le32_to_cpu(wr_cmd_addr[i].dbg_wr_cmd_addr);
+ dev_err_ratelimited(hdev->dev,
+ "\twrite cmd[%u]: Address: SID(%u) BG(%u) BA(%u) COL(%u).\n",
+ i,
+ FIELD_GET(WR_PAR_LAST_CMD_SID_MASK, curr_addr),
+ FIELD_GET(WR_PAR_LAST_CMD_BG_MASK, curr_addr),
+ FIELD_GET(WR_PAR_LAST_CMD_BA_MASK, curr_addr),
+ FIELD_GET(WR_PAR_LAST_CMD_COL_MASK, curr_addr));
+ }
+}
+
+static void gaudi2_hbm_sei_print_ca_par_info(struct hl_device *hdev,
+ struct hl_eq_hbm_sei_ca_par_intr_info *ca_par_err_data, u32 err_cnt)
+{
+ __le32 *col_cmd = ca_par_err_data->dbg_col;
+ __le16 *row_cmd = ca_par_err_data->dbg_row;
+ u32 i;
+
+ dev_err_ratelimited(hdev->dev, "CA ERROR count: %d\n", err_cnt);
+
+ dev_err_ratelimited(hdev->dev, "Last latched C&R bus commands:\n");
+ for (i = 0 ; i < HBM_CA_ERR_CMD_LIFO_LEN ; i++)
+ dev_err_ratelimited(hdev->dev, "cmd%u: ROW(0x%04x) COL(0x%05x)\n", i,
+ le16_to_cpu(row_cmd[i]) & (u16)GENMASK(13, 0),
+ le32_to_cpu(col_cmd[i]) & (u32)GENMASK(17, 0));
+}
+
+/* Returns true if hard reset is needed or false otherwise */
+static bool gaudi2_handle_hbm_mc_sei_err(struct hl_device *hdev, u16 event_type,
+ struct hl_eq_hbm_sei_data *sei_data)
+{
+ bool require_hard_reset = false;
+ u32 hbm_id, mc_id, cause_idx;
+
+ hbm_id = (event_type - GAUDI2_EVENT_HBM0_MC0_SEI_SEVERE) / 4;
+ mc_id = ((event_type - GAUDI2_EVENT_HBM0_MC0_SEI_SEVERE) / 2) % 2;
+
+ cause_idx = sei_data->hdr.sei_cause;
+ if (cause_idx > GAUDI2_NUM_OF_HBM_SEI_CAUSE - 1) {
+ gaudi2_print_event(hdev, event_type, true,
+ "err cause: %s",
+ "Invalid HBM SEI event cause (%d) provided by FW", cause_idx);
+ return true;
+ }
+
+ gaudi2_print_event(hdev, event_type, !sei_data->hdr.is_critical,
+ "System %s Error Interrupt - HBM(%u) MC(%u) MC_CH(%u) MC_PC(%u). Error cause: %s",
+ sei_data->hdr.is_critical ? "Critical" : "Non-critical",
+ hbm_id, mc_id, sei_data->hdr.mc_channel, sei_data->hdr.mc_pseudo_channel,
+ hbm_mc_sei_cause[cause_idx]);
+
+ /* Print error-specific info */
+ switch (cause_idx) {
+ case HBM_SEI_CATTRIP:
+ require_hard_reset = true;
+ break;
+
+ case HBM_SEI_CMD_PARITY_EVEN:
+ gaudi2_hbm_sei_print_ca_par_info(hdev, &sei_data->ca_parity_even_info,
+ le32_to_cpu(sei_data->hdr.cnt));
+ require_hard_reset = true;
+ break;
+
+ case HBM_SEI_CMD_PARITY_ODD:
+ gaudi2_hbm_sei_print_ca_par_info(hdev, &sei_data->ca_parity_odd_info,
+ le32_to_cpu(sei_data->hdr.cnt));
+ require_hard_reset = true;
+ break;
+
+ case HBM_SEI_WRITE_DATA_PARITY_ERR:
+ gaudi2_hbm_sei_print_wr_par_info(hdev, &sei_data->wr_parity_info,
+ le32_to_cpu(sei_data->hdr.cnt));
+ require_hard_reset = true;
+ break;
+
+ case HBM_SEI_READ_ERR:
+ /* Unlike other SEI events, read error requires further processing of the
+ * raw data in order to determine the root cause.
+ */
+ require_hard_reset = gaudi2_hbm_sei_handle_read_err(hdev,
+ &sei_data->read_err_info,
+ le32_to_cpu(sei_data->hdr.cnt));
+ break;
+
+ default:
+ break;
+ }
+
+ require_hard_reset |= !!sei_data->hdr.is_critical;
+
+ return require_hard_reset;
+}
+
+static int gaudi2_handle_hbm_cattrip(struct hl_device *hdev, u16 event_type,
+ u64 intr_cause_data)
+{
+ if (intr_cause_data) {
+ gaudi2_print_event(hdev, event_type, true,
+ "temperature error cause: %#llx", intr_cause_data);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int gaudi2_handle_hbm_mc_spi(struct hl_device *hdev, u64 intr_cause_data)
+{
+ u32 i, error_count = 0;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_HBM_MC_SPI_CAUSE ; i++)
+ if (intr_cause_data & hbm_mc_spi[i].mask) {
+ dev_dbg(hdev->dev, "HBM spi event: notification cause(%s)\n",
+ hbm_mc_spi[i].cause);
+ error_count++;
+ }
+
+ return error_count;
+}
+
+static void gaudi2_print_clk_change_info(struct hl_device *hdev, u16 event_type, u64 *event_mask)
+{
+ ktime_t zero_time = ktime_set(0, 0);
+
+ mutex_lock(&hdev->clk_throttling.lock);
+
+ switch (event_type) {
+ case GAUDI2_EVENT_CPU_FIX_POWER_ENV_S:
+ hdev->clk_throttling.current_reason |= HL_CLK_THROTTLE_POWER;
+ hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_POWER;
+ hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].start = ktime_get();
+ hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = zero_time;
+ dev_dbg_ratelimited(hdev->dev, "Clock throttling due to power consumption\n");
+ break;
+
+ case GAUDI2_EVENT_CPU_FIX_POWER_ENV_E:
+ hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_POWER;
+ hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = ktime_get();
+ dev_dbg_ratelimited(hdev->dev, "Power envelop is safe, back to optimal clock\n");
+ break;
+
+ case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_S:
+ hdev->clk_throttling.current_reason |= HL_CLK_THROTTLE_THERMAL;
+ hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_THERMAL;
+ hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].start = ktime_get();
+ hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = zero_time;
+ *event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ dev_info_ratelimited(hdev->dev, "Clock throttling due to overheating\n");
+ break;
+
+ case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_E:
+ hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_THERMAL;
+ hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = ktime_get();
+ *event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ dev_info_ratelimited(hdev->dev, "Thermal envelop is safe, back to optimal clock\n");
+ break;
+
+ default:
+ dev_err(hdev->dev, "Received invalid clock change event %d\n", event_type);
+ break;
+ }
+
+ mutex_unlock(&hdev->clk_throttling.lock);
+}
+
+static void gaudi2_print_out_of_sync_info(struct hl_device *hdev, u16 event_type,
+ struct cpucp_pkt_sync_err *sync_err)
+{
+ struct hl_hw_queue *q = &hdev->kernel_queues[GAUDI2_QUEUE_ID_CPU_PQ];
+
+ gaudi2_print_event(hdev, event_type, false,
+ "FW: pi=%u, ci=%u, LKD: pi=%u, ci=%d",
+ le32_to_cpu(sync_err->pi), le32_to_cpu(sync_err->ci),
+ q->pi, atomic_read(&q->ci));
+}
+
+static int gaudi2_handle_pcie_p2p_msix(struct hl_device *hdev, u16 event_type)
+{
+ u32 p2p_intr, msix_gw_intr, error_count = 0;
+
+ p2p_intr = RREG32(mmPCIE_WRAP_P2P_INTR);
+ msix_gw_intr = RREG32(mmPCIE_WRAP_MSIX_GW_INTR);
+
+ if (p2p_intr) {
+ gaudi2_print_event(hdev, event_type, true,
+ "pcie p2p transaction terminated due to security, req_id(0x%x)",
+ RREG32(mmPCIE_WRAP_P2P_REQ_ID));
+
+ WREG32(mmPCIE_WRAP_P2P_INTR, 0x1);
+ error_count++;
+ }
+
+ if (msix_gw_intr) {
+ gaudi2_print_event(hdev, event_type, true,
+ "pcie msi-x gen denied due to vector num check failure, vec(0x%X)",
+ RREG32(mmPCIE_WRAP_MSIX_GW_VEC));
+
+ WREG32(mmPCIE_WRAP_MSIX_GW_INTR, 0x1);
+ error_count++;
+ }
+
+ return error_count;
+}
+
+static int gaudi2_handle_pcie_drain(struct hl_device *hdev,
+ struct hl_eq_pcie_drain_ind_data *drain_data)
+{
+ u64 cause, error_count = 0;
+
+ cause = le64_to_cpu(drain_data->intr_cause.intr_cause_data);
+
+ if (cause & BIT_ULL(0)) {
+ dev_err_ratelimited(hdev->dev, "PCIE AXI drain LBW completed\n");
+ error_count++;
+ }
+
+ if (cause & BIT_ULL(1)) {
+ dev_err_ratelimited(hdev->dev, "PCIE AXI drain HBW completed\n");
+ error_count++;
+ }
+
+ return error_count;
+}
+
+static int gaudi2_handle_psoc_drain(struct hl_device *hdev, u64 intr_cause_data)
+{
+ u32 error_count = 0;
+ int i;
+
+ for (i = 0 ; i < GAUDI2_NUM_OF_AXI_DRAIN_ERR_CAUSE ; i++) {
+ if (intr_cause_data & BIT_ULL(i)) {
+ dev_err_ratelimited(hdev->dev, "PSOC %s completed\n",
+ gaudi2_psoc_axi_drain_interrupts_cause[i]);
+ error_count++;
+ }
+ }
+
+ hl_check_for_glbl_errors(hdev);
+
+ return error_count;
+}
+
+static void gaudi2_print_cpu_pkt_failure_info(struct hl_device *hdev, u16 event_type,
+ struct cpucp_pkt_sync_err *sync_err)
+{
+ struct hl_hw_queue *q = &hdev->kernel_queues[GAUDI2_QUEUE_ID_CPU_PQ];
+
+ gaudi2_print_event(hdev, event_type, false,
+ "FW reported sanity check failure, FW: pi=%u, ci=%u, LKD: pi=%u, ci=%d",
+ le32_to_cpu(sync_err->pi), le32_to_cpu(sync_err->ci), q->pi, atomic_read(&q->ci));
+}
+
+static int hl_arc_event_handle(struct hl_device *hdev, u16 event_type,
+ struct hl_eq_engine_arc_intr_data *data)
+{
+ struct hl_engine_arc_dccm_queue_full_irq *q;
+ u32 intr_type, engine_id;
+ u64 payload;
+
+ intr_type = le32_to_cpu(data->intr_type);
+ engine_id = le32_to_cpu(data->engine_id);
+ payload = le64_to_cpu(data->payload);
+
+ switch (intr_type) {
+ case ENGINE_ARC_DCCM_QUEUE_FULL_IRQ:
+ q = (struct hl_engine_arc_dccm_queue_full_irq *) &payload;
+
+ gaudi2_print_event(hdev, event_type, true,
+ "ARC DCCM Full event: Eng: %s, Intr_type: %u, Qidx: %u",
+ GAUDI2_ENG_ID_TO_STR(engine_id), intr_type, q->queue_index);
+ return 1;
+ default:
+ gaudi2_print_event(hdev, event_type, true, "Unknown ARC event type");
+ return 0;
+ }
+}
+
+static u16 event_id_to_engine_id(struct hl_device *hdev, u16 event_type)
+{
+ enum gaudi2_block_types type = GAUDI2_BLOCK_TYPE_MAX;
+ u16 index;
+
+ switch (event_type) {
+ case GAUDI2_EVENT_TPC0_AXI_ERR_RSP ... GAUDI2_EVENT_TPC24_AXI_ERR_RSP:
+ index = event_type - GAUDI2_EVENT_TPC0_AXI_ERR_RSP;
+ type = GAUDI2_BLOCK_TYPE_TPC;
+ break;
+ case GAUDI2_EVENT_TPC0_QM ... GAUDI2_EVENT_TPC24_QM:
+ index = event_type - GAUDI2_EVENT_TPC0_QM;
+ type = GAUDI2_BLOCK_TYPE_TPC;
+ break;
+ case GAUDI2_EVENT_MME0_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME0_SPI_BASE ... GAUDI2_EVENT_MME0_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME0_QM:
+ index = 0;
+ type = GAUDI2_BLOCK_TYPE_MME;
+ break;
+ case GAUDI2_EVENT_MME1_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME1_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME1_SPI_BASE ... GAUDI2_EVENT_MME1_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME1_QM:
+ index = 1;
+ type = GAUDI2_BLOCK_TYPE_MME;
+ break;
+ case GAUDI2_EVENT_MME2_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME2_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME2_SPI_BASE ... GAUDI2_EVENT_MME2_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME2_QM:
+ index = 2;
+ type = GAUDI2_BLOCK_TYPE_MME;
+ break;
+ case GAUDI2_EVENT_MME3_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME3_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME3_SPI_BASE ... GAUDI2_EVENT_MME3_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME3_QM:
+ index = 3;
+ type = GAUDI2_BLOCK_TYPE_MME;
+ break;
+ case GAUDI2_EVENT_KDMA_CH0_AXI_ERR_RSP:
+ case GAUDI2_EVENT_KDMA_BM_SPMU:
+ case GAUDI2_EVENT_KDMA0_CORE:
+ return GAUDI2_ENGINE_ID_KDMA;
+ case GAUDI2_EVENT_PDMA_CH0_AXI_ERR_RSP:
+ case GAUDI2_EVENT_PDMA0_CORE:
+ case GAUDI2_EVENT_PDMA0_BM_SPMU:
+ case GAUDI2_EVENT_PDMA0_QM:
+ return GAUDI2_ENGINE_ID_PDMA_0;
+ case GAUDI2_EVENT_PDMA_CH1_AXI_ERR_RSP:
+ case GAUDI2_EVENT_PDMA1_CORE:
+ case GAUDI2_EVENT_PDMA1_BM_SPMU:
+ case GAUDI2_EVENT_PDMA1_QM:
+ return GAUDI2_ENGINE_ID_PDMA_1;
+ case GAUDI2_EVENT_DEC0_AXI_ERR_RSPONSE ... GAUDI2_EVENT_DEC9_AXI_ERR_RSPONSE:
+ index = event_type - GAUDI2_EVENT_DEC0_AXI_ERR_RSPONSE;
+ type = GAUDI2_BLOCK_TYPE_DEC;
+ break;
+ case GAUDI2_EVENT_DEC0_SPI ... GAUDI2_EVENT_DEC9_BMON_SPMU:
+ index = (event_type - GAUDI2_EVENT_DEC0_SPI) >> 1;
+ type = GAUDI2_BLOCK_TYPE_DEC;
+ break;
+ case GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE ... GAUDI2_EVENT_NIC11_AXI_ERROR_RESPONSE:
+ index = event_type - GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE;
+ return GAUDI2_ENGINE_ID_NIC0_0 + (index * 2);
+ case GAUDI2_EVENT_NIC0_QM0 ... GAUDI2_EVENT_NIC11_QM1:
+ index = event_type - GAUDI2_EVENT_NIC0_QM0;
+ return GAUDI2_ENGINE_ID_NIC0_0 + index;
+ case GAUDI2_EVENT_NIC0_BMON_SPMU ... GAUDI2_EVENT_NIC11_SW_ERROR:
+ index = event_type - GAUDI2_EVENT_NIC0_BMON_SPMU;
+ return GAUDI2_ENGINE_ID_NIC0_0 + (index * 2);
+ case GAUDI2_EVENT_TPC0_BMON_SPMU ... GAUDI2_EVENT_TPC24_KERNEL_ERR:
+ index = (event_type - GAUDI2_EVENT_TPC0_BMON_SPMU) >> 1;
+ type = GAUDI2_BLOCK_TYPE_TPC;
+ break;
+ case GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_ROTATOR0_BMON_SPMU:
+ case GAUDI2_EVENT_ROTATOR0_ROT0_QM:
+ return GAUDI2_ENGINE_ID_ROT_0;
+ case GAUDI2_EVENT_ROTATOR1_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_ROTATOR1_BMON_SPMU:
+ case GAUDI2_EVENT_ROTATOR1_ROT1_QM:
+ return GAUDI2_ENGINE_ID_ROT_1;
+ case GAUDI2_EVENT_HDMA0_BM_SPMU:
+ case GAUDI2_EVENT_HDMA0_QM:
+ case GAUDI2_EVENT_HDMA0_CORE:
+ return GAUDI2_DCORE0_ENGINE_ID_EDMA_0;
+ case GAUDI2_EVENT_HDMA1_BM_SPMU:
+ case GAUDI2_EVENT_HDMA1_QM:
+ case GAUDI2_EVENT_HDMA1_CORE:
+ return GAUDI2_DCORE0_ENGINE_ID_EDMA_1;
+ case GAUDI2_EVENT_HDMA2_BM_SPMU:
+ case GAUDI2_EVENT_HDMA2_QM:
+ case GAUDI2_EVENT_HDMA2_CORE:
+ return GAUDI2_DCORE1_ENGINE_ID_EDMA_0;
+ case GAUDI2_EVENT_HDMA3_BM_SPMU:
+ case GAUDI2_EVENT_HDMA3_QM:
+ case GAUDI2_EVENT_HDMA3_CORE:
+ return GAUDI2_DCORE1_ENGINE_ID_EDMA_1;
+ case GAUDI2_EVENT_HDMA4_BM_SPMU:
+ case GAUDI2_EVENT_HDMA4_QM:
+ case GAUDI2_EVENT_HDMA4_CORE:
+ return GAUDI2_DCORE2_ENGINE_ID_EDMA_0;
+ case GAUDI2_EVENT_HDMA5_BM_SPMU:
+ case GAUDI2_EVENT_HDMA5_QM:
+ case GAUDI2_EVENT_HDMA5_CORE:
+ return GAUDI2_DCORE2_ENGINE_ID_EDMA_1;
+ case GAUDI2_EVENT_HDMA6_BM_SPMU:
+ case GAUDI2_EVENT_HDMA6_QM:
+ case GAUDI2_EVENT_HDMA6_CORE:
+ return GAUDI2_DCORE3_ENGINE_ID_EDMA_0;
+ case GAUDI2_EVENT_HDMA7_BM_SPMU:
+ case GAUDI2_EVENT_HDMA7_QM:
+ case GAUDI2_EVENT_HDMA7_CORE:
+ return GAUDI2_DCORE3_ENGINE_ID_EDMA_1;
+ default:
+ break;
+ }
+
+ switch (type) {
+ case GAUDI2_BLOCK_TYPE_TPC:
+ switch (index) {
+ case TPC_ID_DCORE0_TPC0 ... TPC_ID_DCORE0_TPC5:
+ return GAUDI2_DCORE0_ENGINE_ID_TPC_0 + index;
+ case TPC_ID_DCORE1_TPC0 ... TPC_ID_DCORE1_TPC5:
+ return GAUDI2_DCORE1_ENGINE_ID_TPC_0 + index - TPC_ID_DCORE1_TPC0;
+ case TPC_ID_DCORE2_TPC0 ... TPC_ID_DCORE2_TPC5:
+ return GAUDI2_DCORE2_ENGINE_ID_TPC_0 + index - TPC_ID_DCORE2_TPC0;
+ case TPC_ID_DCORE3_TPC0 ... TPC_ID_DCORE3_TPC5:
+ return GAUDI2_DCORE3_ENGINE_ID_TPC_0 + index - TPC_ID_DCORE3_TPC0;
+ default:
+ break;
+ }
+ break;
+ case GAUDI2_BLOCK_TYPE_MME:
+ switch (index) {
+ case MME_ID_DCORE0: return GAUDI2_DCORE0_ENGINE_ID_MME;
+ case MME_ID_DCORE1: return GAUDI2_DCORE1_ENGINE_ID_MME;
+ case MME_ID_DCORE2: return GAUDI2_DCORE2_ENGINE_ID_MME;
+ case MME_ID_DCORE3: return GAUDI2_DCORE3_ENGINE_ID_MME;
+ default:
+ break;
+ }
+ break;
+ case GAUDI2_BLOCK_TYPE_DEC:
+ switch (index) {
+ case DEC_ID_DCORE0_DEC0: return GAUDI2_DCORE0_ENGINE_ID_DEC_0;
+ case DEC_ID_DCORE0_DEC1: return GAUDI2_DCORE0_ENGINE_ID_DEC_1;
+ case DEC_ID_DCORE1_DEC0: return GAUDI2_DCORE1_ENGINE_ID_DEC_0;
+ case DEC_ID_DCORE1_DEC1: return GAUDI2_DCORE1_ENGINE_ID_DEC_1;
+ case DEC_ID_DCORE2_DEC0: return GAUDI2_DCORE2_ENGINE_ID_DEC_0;
+ case DEC_ID_DCORE2_DEC1: return GAUDI2_DCORE2_ENGINE_ID_DEC_1;
+ case DEC_ID_DCORE3_DEC0: return GAUDI2_DCORE3_ENGINE_ID_DEC_0;
+ case DEC_ID_DCORE3_DEC1: return GAUDI2_DCORE3_ENGINE_ID_DEC_1;
+ case DEC_ID_PCIE_VDEC0: return GAUDI2_PCIE_ENGINE_ID_DEC_0;
+ case DEC_ID_PCIE_VDEC1: return GAUDI2_PCIE_ENGINE_ID_DEC_1;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return U16_MAX;
+}
+
+static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ bool reset_required = false, is_critical = false;
+ u32 index, ctl, reset_flags = 0, error_count = 0;
+ u64 event_mask = 0;
+ u16 event_type;
+
+ ctl = le32_to_cpu(eq_entry->hdr.ctl);
+ event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK) >> EQ_CTL_EVENT_TYPE_SHIFT);
+
+ if (event_type >= GAUDI2_EVENT_SIZE) {
+ dev_err(hdev->dev, "Event type %u exceeds maximum of %u",
+ event_type, GAUDI2_EVENT_SIZE - 1);
+ return;
+ }
+
+ gaudi2->events_stat[event_type]++;
+ gaudi2->events_stat_aggregate[event_type]++;
+
+ switch (event_type) {
+ case GAUDI2_EVENT_PCIE_CORE_SERR ... GAUDI2_EVENT_ARC0_ECC_DERR:
+ fallthrough;
+ case GAUDI2_EVENT_ROTATOR0_SERR ... GAUDI2_EVENT_ROTATOR1_DERR:
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ reset_required = gaudi2_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data);
+ is_critical = eq_entry->ecc_data.is_critical;
+ error_count++;
+ break;
+
+ case GAUDI2_EVENT_TPC0_QM ... GAUDI2_EVENT_PDMA1_QM:
+ fallthrough;
+ case GAUDI2_EVENT_ROTATOR0_ROT0_QM ... GAUDI2_EVENT_ROTATOR1_ROT1_QM:
+ fallthrough;
+ case GAUDI2_EVENT_NIC0_QM0 ... GAUDI2_EVENT_NIC11_QM1:
+ error_count = gaudi2_handle_qman_err(hdev, event_type, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_ARC_AXI_ERROR_RESPONSE_0:
+ error_count = gaudi2_handle_arc_farm_sei_err(hdev, event_type, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_CPU_AXI_ERR_RSP:
+ error_count = gaudi2_handle_cpu_sei_err(hdev, event_type);
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_CRITICL_FW_ERR;
+ break;
+
+ case GAUDI2_EVENT_PDMA_CH0_AXI_ERR_RSP:
+ case GAUDI2_EVENT_PDMA_CH1_AXI_ERR_RSP:
+ error_count = gaudi2_handle_qm_sei_err(hdev, event_type, true, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_ROTATOR1_AXI_ERROR_RESPONSE:
+ index = event_type - GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE;
+ error_count = gaudi2_handle_rot_err(hdev, index, event_type,
+ &eq_entry->razwi_with_intr_cause, &event_mask);
+ error_count += gaudi2_handle_qm_sei_err(hdev, event_type, false, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_TPC0_AXI_ERR_RSP ... GAUDI2_EVENT_TPC24_AXI_ERR_RSP:
+ index = event_type - GAUDI2_EVENT_TPC0_AXI_ERR_RSP;
+ error_count = gaudi2_tpc_ack_interrupts(hdev, index, event_type,
+ &eq_entry->razwi_with_intr_cause, &event_mask);
+ error_count += gaudi2_handle_qm_sei_err(hdev, event_type, false, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_DEC0_AXI_ERR_RSPONSE ... GAUDI2_EVENT_DEC9_AXI_ERR_RSPONSE:
+ index = event_type - GAUDI2_EVENT_DEC0_AXI_ERR_RSPONSE;
+ error_count = gaudi2_handle_dec_err(hdev, index, event_type, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_TPC0_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC1_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC2_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC3_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC4_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC5_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC6_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC7_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC8_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC9_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC10_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC11_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC12_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC13_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC14_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC15_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC16_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC17_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC18_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC19_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC20_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC21_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC22_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC23_KERNEL_ERR:
+ case GAUDI2_EVENT_TPC24_KERNEL_ERR:
+ index = (event_type - GAUDI2_EVENT_TPC0_KERNEL_ERR) /
+ (GAUDI2_EVENT_TPC1_KERNEL_ERR - GAUDI2_EVENT_TPC0_KERNEL_ERR);
+ error_count = gaudi2_tpc_ack_interrupts(hdev, index, event_type,
+ &eq_entry->razwi_with_intr_cause, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_DEC0_SPI:
+ case GAUDI2_EVENT_DEC1_SPI:
+ case GAUDI2_EVENT_DEC2_SPI:
+ case GAUDI2_EVENT_DEC3_SPI:
+ case GAUDI2_EVENT_DEC4_SPI:
+ case GAUDI2_EVENT_DEC5_SPI:
+ case GAUDI2_EVENT_DEC6_SPI:
+ case GAUDI2_EVENT_DEC7_SPI:
+ case GAUDI2_EVENT_DEC8_SPI:
+ case GAUDI2_EVENT_DEC9_SPI:
+ index = (event_type - GAUDI2_EVENT_DEC0_SPI) /
+ (GAUDI2_EVENT_DEC1_SPI - GAUDI2_EVENT_DEC0_SPI);
+ error_count = gaudi2_handle_dec_err(hdev, index, event_type, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME1_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME2_CTRL_AXI_ERROR_RESPONSE:
+ case GAUDI2_EVENT_MME3_CTRL_AXI_ERROR_RESPONSE:
+ index = (event_type - GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE) /
+ (GAUDI2_EVENT_MME1_CTRL_AXI_ERROR_RESPONSE -
+ GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE);
+ error_count = gaudi2_handle_mme_err(hdev, index, event_type, &event_mask);
+ error_count += gaudi2_handle_qm_sei_err(hdev, event_type, false, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_MME0_QMAN_SW_ERROR:
+ case GAUDI2_EVENT_MME1_QMAN_SW_ERROR:
+ case GAUDI2_EVENT_MME2_QMAN_SW_ERROR:
+ case GAUDI2_EVENT_MME3_QMAN_SW_ERROR:
+ index = (event_type - GAUDI2_EVENT_MME0_QMAN_SW_ERROR) /
+ (GAUDI2_EVENT_MME1_QMAN_SW_ERROR -
+ GAUDI2_EVENT_MME0_QMAN_SW_ERROR);
+ error_count = gaudi2_handle_mme_err(hdev, index, event_type, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_MME0_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME1_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME2_WAP_SOURCE_RESULT_INVALID:
+ case GAUDI2_EVENT_MME3_WAP_SOURCE_RESULT_INVALID:
+ index = (event_type - GAUDI2_EVENT_MME0_WAP_SOURCE_RESULT_INVALID) /
+ (GAUDI2_EVENT_MME1_WAP_SOURCE_RESULT_INVALID -
+ GAUDI2_EVENT_MME0_WAP_SOURCE_RESULT_INVALID);
+ error_count = gaudi2_handle_mme_wap_err(hdev, index, event_type, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_KDMA_CH0_AXI_ERR_RSP:
+ case GAUDI2_EVENT_KDMA0_CORE:
+ error_count = gaudi2_handle_kdma_core_event(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_HDMA2_CORE ... GAUDI2_EVENT_HDMA5_CORE:
+ error_count = gaudi2_handle_dma_core_event(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_PDMA0_CORE ... GAUDI2_EVENT_PDMA1_CORE:
+ error_count = gaudi2_handle_dma_core_event(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_PCIE_ADDR_DEC_ERR:
+ error_count = gaudi2_print_pcie_addr_dec_info(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data), &event_mask);
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_HMMU0_PAGE_FAULT_OR_WR_PERM ... GAUDI2_EVENT_HMMU12_SECURITY_ERROR:
+ case GAUDI2_EVENT_HMMU_0_AXI_ERR_RSP ... GAUDI2_EVENT_HMMU_12_AXI_ERR_RSP:
+ case GAUDI2_EVENT_PMMU0_PAGE_FAULT_WR_PERM ... GAUDI2_EVENT_PMMU0_SECURITY_ERROR:
+ case GAUDI2_EVENT_PMMU_AXI_ERR_RSP_0:
+ error_count = gaudi2_handle_mmu_spi_sei_err(hdev, event_type, &event_mask);
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_HIF0_FATAL ... GAUDI2_EVENT_HIF12_FATAL:
+ error_count = gaudi2_handle_hif_fatal(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_PMMU_FATAL_0:
+ error_count = gaudi2_handle_pif_fatal(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_PSOC63_RAZWI_OR_PID_MIN_MAX_INTERRUPT:
+ error_count = gaudi2_ack_psoc_razwi_event_handler(hdev, &event_mask);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_HBM0_MC0_SEI_SEVERE ... GAUDI2_EVENT_HBM5_MC1_SEI_NON_SEVERE:
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ if (gaudi2_handle_hbm_mc_sei_err(hdev, event_type, &eq_entry->sei_data)) {
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ reset_required = true;
+ is_critical = eq_entry->sei_data.hdr.is_critical;
+ }
+ error_count++;
+ break;
+
+ case GAUDI2_EVENT_HBM_CATTRIP_0 ... GAUDI2_EVENT_HBM_CATTRIP_5:
+ error_count = gaudi2_handle_hbm_cattrip(hdev, event_type,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_HBM0_MC0_SPI ... GAUDI2_EVENT_HBM5_MC1_SPI:
+ error_count = gaudi2_handle_hbm_mc_spi(hdev,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_PCIE_DRAIN_COMPLETE:
+ error_count = gaudi2_handle_pcie_drain(hdev, &eq_entry->pcie_drain_ind_data);
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ if (hl_fw_version_cmp(hdev, 1, 13, 0) >= 0)
+ is_critical = true;
+ break;
+
+ case GAUDI2_EVENT_PSOC59_RPM_ERROR_OR_DRAIN:
+ error_count = gaudi2_handle_psoc_drain(hdev,
+ le64_to_cpu(eq_entry->intr_cause.intr_cause_data));
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_CPU_AXI_ECC:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_CPU_L2_RAM_ECC:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_MME0_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME0_SBTE4_AXI_ERR_RSP:
+ case GAUDI2_EVENT_MME1_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME1_SBTE4_AXI_ERR_RSP:
+ case GAUDI2_EVENT_MME2_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME2_SBTE4_AXI_ERR_RSP:
+ case GAUDI2_EVENT_MME3_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME3_SBTE4_AXI_ERR_RSP:
+ error_count = gaudi2_handle_mme_sbte_err(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+ case GAUDI2_EVENT_VM0_ALARM_A ... GAUDI2_EVENT_VM3_ALARM_B:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_PSOC_AXI_ERR_RSP:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_PSOC_PRSTN_FALL:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_PCIE_APB_TIMEOUT:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_PCIE_FATAL_ERR:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_TPC0_BMON_SPMU:
+ case GAUDI2_EVENT_TPC1_BMON_SPMU:
+ case GAUDI2_EVENT_TPC2_BMON_SPMU:
+ case GAUDI2_EVENT_TPC3_BMON_SPMU:
+ case GAUDI2_EVENT_TPC4_BMON_SPMU:
+ case GAUDI2_EVENT_TPC5_BMON_SPMU:
+ case GAUDI2_EVENT_TPC6_BMON_SPMU:
+ case GAUDI2_EVENT_TPC7_BMON_SPMU:
+ case GAUDI2_EVENT_TPC8_BMON_SPMU:
+ case GAUDI2_EVENT_TPC9_BMON_SPMU:
+ case GAUDI2_EVENT_TPC10_BMON_SPMU:
+ case GAUDI2_EVENT_TPC11_BMON_SPMU:
+ case GAUDI2_EVENT_TPC12_BMON_SPMU:
+ case GAUDI2_EVENT_TPC13_BMON_SPMU:
+ case GAUDI2_EVENT_TPC14_BMON_SPMU:
+ case GAUDI2_EVENT_TPC15_BMON_SPMU:
+ case GAUDI2_EVENT_TPC16_BMON_SPMU:
+ case GAUDI2_EVENT_TPC17_BMON_SPMU:
+ case GAUDI2_EVENT_TPC18_BMON_SPMU:
+ case GAUDI2_EVENT_TPC19_BMON_SPMU:
+ case GAUDI2_EVENT_TPC20_BMON_SPMU:
+ case GAUDI2_EVENT_TPC21_BMON_SPMU:
+ case GAUDI2_EVENT_TPC22_BMON_SPMU:
+ case GAUDI2_EVENT_TPC23_BMON_SPMU:
+ case GAUDI2_EVENT_TPC24_BMON_SPMU:
+ case GAUDI2_EVENT_MME0_CTRL_BMON_SPMU:
+ case GAUDI2_EVENT_MME0_SBTE_BMON_SPMU:
+ case GAUDI2_EVENT_MME0_WAP_BMON_SPMU:
+ case GAUDI2_EVENT_MME1_CTRL_BMON_SPMU:
+ case GAUDI2_EVENT_MME1_SBTE_BMON_SPMU:
+ case GAUDI2_EVENT_MME1_WAP_BMON_SPMU:
+ case GAUDI2_EVENT_MME2_CTRL_BMON_SPMU:
+ case GAUDI2_EVENT_MME2_SBTE_BMON_SPMU:
+ case GAUDI2_EVENT_MME2_WAP_BMON_SPMU:
+ case GAUDI2_EVENT_MME3_CTRL_BMON_SPMU:
+ case GAUDI2_EVENT_MME3_SBTE_BMON_SPMU:
+ case GAUDI2_EVENT_MME3_WAP_BMON_SPMU:
+ case GAUDI2_EVENT_HDMA2_BM_SPMU ... GAUDI2_EVENT_PDMA1_BM_SPMU:
+ fallthrough;
+ case GAUDI2_EVENT_DEC0_BMON_SPMU:
+ case GAUDI2_EVENT_DEC1_BMON_SPMU:
+ case GAUDI2_EVENT_DEC2_BMON_SPMU:
+ case GAUDI2_EVENT_DEC3_BMON_SPMU:
+ case GAUDI2_EVENT_DEC4_BMON_SPMU:
+ case GAUDI2_EVENT_DEC5_BMON_SPMU:
+ case GAUDI2_EVENT_DEC6_BMON_SPMU:
+ case GAUDI2_EVENT_DEC7_BMON_SPMU:
+ case GAUDI2_EVENT_DEC8_BMON_SPMU:
+ case GAUDI2_EVENT_DEC9_BMON_SPMU:
+ case GAUDI2_EVENT_ROTATOR0_BMON_SPMU ... GAUDI2_EVENT_SM3_BMON_SPMU:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_CPU_FIX_POWER_ENV_S:
+ case GAUDI2_EVENT_CPU_FIX_POWER_ENV_E:
+ case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_S:
+ case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_E:
+ gaudi2_print_clk_change_info(hdev, event_type, &event_mask);
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ break;
+
+ case GAUDI2_EVENT_CPU_PKT_QUEUE_OUT_SYNC:
+ gaudi2_print_out_of_sync_info(hdev, event_type, &eq_entry->pkt_sync_err);
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_PCIE_FLR_REQUESTED:
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ /* Do nothing- FW will handle it */
+ break;
+
+ case GAUDI2_EVENT_PCIE_P2P_MSIX:
+ error_count = gaudi2_handle_pcie_p2p_msix(hdev, event_type);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_SM0_AXI_ERROR_RESPONSE ... GAUDI2_EVENT_SM3_AXI_ERROR_RESPONSE:
+ index = event_type - GAUDI2_EVENT_SM0_AXI_ERROR_RESPONSE;
+ error_count = gaudi2_handle_sm_err(hdev, event_type, index);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_PSOC_MME_PLL_LOCK_ERR ... GAUDI2_EVENT_DCORE2_HBM_PLL_LOCK_ERR:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_CPU_CPLD_SHUTDOWN_CAUSE:
+ dev_info(hdev->dev, "CPLD shutdown cause, reset reason: 0x%llx\n",
+ le64_to_cpu(eq_entry->data[0]));
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+ case GAUDI2_EVENT_CPU_CPLD_SHUTDOWN_EVENT:
+ dev_err(hdev->dev, "CPLD shutdown event, reset reason: 0x%llx\n",
+ le64_to_cpu(eq_entry->data[0]));
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ hl_eq_cpld_shutdown_event_handle(hdev, event_type, &event_mask);
+ break;
+
+ case GAUDI2_EVENT_CPU_PKT_SANITY_FAILED:
+ gaudi2_print_cpu_pkt_failure_info(hdev, event_type, &eq_entry->pkt_sync_err);
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ reset_flags |= HL_DRV_RESET_FW_FATAL_ERR;
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ break;
+
+ case GAUDI2_EVENT_ARC_DCCM_FULL:
+ error_count = hl_arc_event_handle(hdev, event_type, &eq_entry->arc_data);
+ event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
+ break;
+
+ case GAUDI2_EVENT_CPU_FP32_NOT_SUPPORTED:
+ case GAUDI2_EVENT_CPU_DEV_RESET_REQ:
+ event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ is_critical = true;
+ break;
+
+ case GAUDI2_EVENT_ARC_PWR_BRK_ENTRY:
+ case GAUDI2_EVENT_ARC_PWR_BRK_EXT:
+ case GAUDI2_EVENT_ARC_PWR_RD_MODE0:
+ case GAUDI2_EVENT_ARC_PWR_RD_MODE1:
+ case GAUDI2_EVENT_ARC_PWR_RD_MODE2:
+ case GAUDI2_EVENT_ARC_PWR_RD_MODE3:
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ dev_info_ratelimited(hdev->dev, "%s event received\n",
+ gaudi2_irq_map_table[event_type].name);
+ break;
+
+ case GAUDI2_EVENT_ARC_EQ_HEARTBEAT:
+ hl_eq_heartbeat_event_handle(hdev);
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ break;
+ default:
+ if (gaudi2_irq_map_table[event_type].valid) {
+ dev_err_ratelimited(hdev->dev, "Cannot find handler for event %d\n",
+ event_type);
+ error_count = GAUDI2_NA_EVENT_CAUSE;
+ }
+ }
+
+ if (event_mask & HL_NOTIFIER_EVENT_USER_ENGINE_ERR)
+ hl_capture_engine_err(hdev, event_id_to_engine_id(hdev, event_type), error_count);
+
+ /* Make sure to dump an error in case no error cause was printed so far.
+ * Note that although we have counted the errors, we use this number as
+ * a boolean.
+ */
+ if (error_count == GAUDI2_NA_EVENT_CAUSE && !is_info_event(event_type))
+ gaudi2_print_event(hdev, event_type, true, "%d", event_type);
+ else if (error_count == 0)
+ gaudi2_print_event(hdev, event_type, true,
+ "No error cause for H/W event %u", event_type);
+
+ if ((gaudi2_irq_map_table[event_type].reset != EVENT_RESET_TYPE_NONE) || reset_required) {
+ if (reset_required ||
+ (gaudi2_irq_map_table[event_type].reset == EVENT_RESET_TYPE_HARD))
+ reset_flags |= HL_DRV_RESET_HARD;
+
+ if (hdev->hard_reset_on_fw_events ||
+ (hdev->asic_prop.fw_security_enabled && is_critical))
+ goto reset_device;
+ }
+
+ /* Send unmask irq only for interrupts not classified as MSG */
+ if (!gaudi2_irq_map_table[event_type].msg)
+ hl_fw_unmask_irq(hdev, event_type);
+
+ if (event_mask)
+ hl_notifier_event_send_all(hdev, event_mask);
+
+ return;
+
+reset_device:
+ if (hdev->asic_prop.fw_security_enabled && is_critical) {
+ reset_flags |= HL_DRV_RESET_BYPASS_REQ_TO_FW;
+ event_mask |= HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE;
+ } else {
+ reset_flags |= HL_DRV_RESET_DELAY;
+ }
+ /* escalate general hw errors to critical/fatal error */
+ if (event_mask & HL_NOTIFIER_EVENT_GENERAL_HW_ERR)
+ hl_handle_critical_hw_err(hdev, event_type, &event_mask);
+
+ hl_debugfs_cfg_access_history_dump(hdev);
+ event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
+ hl_device_cond_reset(hdev, reset_flags, event_mask);
+}
+
+static int gaudi2_memset_memory_chunk_using_edma_qm(struct hl_device *hdev,
+ struct packet_lin_dma *lin_dma_pkt,
+ u64 phys_addr, u32 hw_queue_id, u32 size, u64 addr, u32 val)
+{
+ u32 ctl, pkt_size;
+ int rc = 0, i;
+
+ ctl = FIELD_PREP(GAUDI2_PKT_CTL_OPCODE_MASK, PACKET_LIN_DMA);
+ ctl |= FIELD_PREP(GAUDI2_PKT_LIN_DMA_CTL_MEMSET_MASK, 1);
+ ctl |= FIELD_PREP(GAUDI2_PKT_LIN_DMA_CTL_WRCOMP_MASK, 1);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_EB_MASK, 1);
+
+ lin_dma_pkt->ctl = cpu_to_le32(ctl);
+ lin_dma_pkt->src_addr = cpu_to_le64(val);
+ lin_dma_pkt->dst_addr = cpu_to_le64(addr);
+ lin_dma_pkt->tsize = cpu_to_le32(size);
+
+ pkt_size = sizeof(struct packet_lin_dma);
+
+ for (i = 0; i < 3; i++) {
+ rc = hdev->asic_funcs->access_dev_mem(hdev, PCI_REGION_DRAM,
+ phys_addr + (i * sizeof(u64)),
+ ((u64 *)(lin_dma_pkt)) + i, DEBUGFS_WRITE64);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to copy lin_dma packet to HBM (%#llx)\n",
+ phys_addr);
+ return rc;
+ }
+ }
+
+ rc = hl_hw_queue_send_cb_no_cmpl(hdev, hw_queue_id, pkt_size, phys_addr);
+ if (rc)
+ dev_err(hdev->dev, "Failed to send lin_dma packet to H/W queue %s\n",
+ GAUDI2_QUEUE_ID_TO_STR(hw_queue_id));
+
+ return rc;
+}
+
+static int gaudi2_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size, u64 val)
+{
+ u32 edma_queues_id[] = {GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE1_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE2_EDMA_0_0,
+ GAUDI2_QUEUE_ID_DCORE3_EDMA_0_0};
+ u32 chunk_size, dcore, edma_idx, sob_offset, sob_addr, comp_val,
+ old_mmubp, mmubp, num_of_pkts, busy, pkt_size, cb_len;
+ u64 comp_addr, cur_addr = addr, end_addr = addr + size;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc = 0, dma_num = 0, i;
+ void *lin_dma_pkts_arr;
+
+ if (prop->edma_enabled_mask == 0) {
+ dev_info(hdev->dev, "non of the EDMA engines is enabled - skip dram scrubbing\n");
+ return -EIO;
+ }
+
+ sob_offset = hdev->asic_prop.first_available_user_sob[0] * 4;
+ sob_addr = mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + sob_offset;
+ comp_addr = CFG_BASE + sob_addr;
+ comp_val = FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_INC_MASK, 1) |
+ FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_VAL_MASK, 1);
+ mmubp = FIELD_PREP(ARC_FARM_KDMA_CTX_AXUSER_HB_MMU_BP_WR_MASK, 1) |
+ FIELD_PREP(ARC_FARM_KDMA_CTX_AXUSER_HB_MMU_BP_RD_MASK, 1);
+
+ /* Calculate how many lin dma pkts we'll need */
+ num_of_pkts = div64_u64(round_up(size, SZ_2G), SZ_2G);
+ pkt_size = sizeof(struct packet_lin_dma);
+ cb_len = pkt_size * num_of_pkts;
+
+ /*
+ * if we're not scrubing HMMU or NIC reserved sections in hbm,
+ * then it the scrubing of the user section, as we use the start of the user section
+ * to store the CB of the EDMA QM, so shift the start address of the scrubbing accordingly
+ * and scrub the CB section before leaving this function.
+ */
+ if ((addr >= prop->dram_user_base_address) &&
+ (addr < prop->dram_user_base_address + cb_len))
+ cur_addr += (prop->dram_user_base_address + cb_len) - addr;
+
+ lin_dma_pkts_arr = kvcalloc(num_of_pkts, pkt_size, GFP_KERNEL);
+ if (!lin_dma_pkts_arr)
+ return -ENOMEM;
+
+ /*
+ * set mmu bypass for the scrubbing - all ddmas are configured the same so save
+ * only the first one to restore later
+ * also set the sob addr for all edma cores for completion.
+ * set QM as trusted to allow it to access physical address with MMU bp.
+ */
+ old_mmubp = RREG32(mmDCORE0_EDMA0_CORE_CTX_AXUSER_HB_MMU_BP);
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (edma_idx = 0 ; edma_idx < NUM_OF_EDMA_PER_DCORE ; edma_idx++) {
+ u32 edma_offset = dcore * DCORE_OFFSET + edma_idx * DCORE_EDMA_OFFSET;
+ u32 edma_bit = dcore * NUM_OF_EDMA_PER_DCORE + edma_idx;
+
+ if (!(prop->edma_enabled_mask & BIT(edma_bit)))
+ continue;
+
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_AXUSER_HB_MMU_BP +
+ edma_offset, mmubp);
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_WR_COMP_ADDR_LO + edma_offset,
+ lower_32_bits(comp_addr));
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_WR_COMP_ADDR_HI + edma_offset,
+ upper_32_bits(comp_addr));
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_WR_COMP_WDATA + edma_offset,
+ comp_val);
+ gaudi2_qman_set_test_mode(hdev,
+ edma_queues_id[dcore] + 4 * edma_idx, true);
+ }
+ }
+
+ WREG32(sob_addr, 0);
+
+ while (cur_addr < end_addr) {
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (edma_idx = 0 ; edma_idx < NUM_OF_EDMA_PER_DCORE ; edma_idx++) {
+ u32 edma_bit = dcore * NUM_OF_EDMA_PER_DCORE + edma_idx;
+
+ if (!(prop->edma_enabled_mask & BIT(edma_bit)))
+ continue;
+
+ chunk_size = min_t(u64, SZ_2G, end_addr - cur_addr);
+
+ rc = gaudi2_memset_memory_chunk_using_edma_qm(hdev,
+ (struct packet_lin_dma *)lin_dma_pkts_arr + dma_num,
+ prop->dram_user_base_address + (dma_num * pkt_size),
+ edma_queues_id[dcore] + edma_idx * 4,
+ chunk_size, cur_addr, val);
+ if (rc)
+ goto end;
+
+ dma_num++;
+ cur_addr += chunk_size;
+ if (cur_addr == end_addr)
+ goto edma_wait;
+ }
+ }
+ }
+
+edma_wait:
+ rc = hl_poll_timeout(hdev, sob_addr, busy, (busy == dma_num), 1000, 1000000);
+ if (rc) {
+ dev_err(hdev->dev, "DMA Timeout during HBM scrubbing(sob: 0x%x, dma_num: 0x%x)\n",
+ busy, dma_num);
+ goto end;
+ }
+end:
+ for (dcore = 0 ; dcore < NUM_OF_DCORES ; dcore++) {
+ for (edma_idx = 0 ; edma_idx < NUM_OF_EDMA_PER_DCORE ; edma_idx++) {
+ u32 edma_offset = dcore * DCORE_OFFSET + edma_idx * DCORE_EDMA_OFFSET;
+ u32 edma_bit = dcore * NUM_OF_EDMA_PER_DCORE + edma_idx;
+
+ if (!(prop->edma_enabled_mask & BIT(edma_bit)))
+ continue;
+
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_AXUSER_HB_MMU_BP + edma_offset, old_mmubp);
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_WR_COMP_ADDR_LO + edma_offset, 0);
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_WR_COMP_ADDR_HI + edma_offset, 0);
+ WREG32(mmDCORE0_EDMA0_CORE_CTX_WR_COMP_WDATA + edma_offset, 0);
+ gaudi2_qman_set_test_mode(hdev,
+ edma_queues_id[dcore] + 4 * edma_idx, false);
+ }
+ }
+
+ memset(lin_dma_pkts_arr, 0, sizeof(u64));
+
+ /* Zero the HBM area where we copied the CB */
+ for (i = 0; i < cb_len / sizeof(u64); i += sizeof(u64))
+ rc = hdev->asic_funcs->access_dev_mem(hdev, PCI_REGION_DRAM,
+ prop->dram_user_base_address + i,
+ (u64 *)(lin_dma_pkts_arr), DEBUGFS_WRITE64);
+ WREG32(sob_addr, 0);
+
+ kvfree(lin_dma_pkts_arr);
+
+ return rc;
+}
+
+static int gaudi2_scrub_device_dram(struct hl_device *hdev, u64 val)
+{
+ int rc;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 size = prop->dram_end_address - prop->dram_user_base_address;
+
+ rc = gaudi2_memset_device_memory(hdev, prop->dram_user_base_address, size, val);
+
+ if (rc)
+ dev_err(hdev->dev, "Failed to scrub dram, address: 0x%llx size: %llu\n",
+ prop->dram_user_base_address, size);
+ return rc;
+}
+
+static int gaudi2_scrub_device_mem(struct hl_device *hdev)
+{
+ int rc;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u64 val = hdev->memory_scrub_val;
+ u64 addr, size;
+
+ if (!hdev->memory_scrub)
+ return 0;
+
+ /* scrub SRAM */
+ addr = prop->sram_user_base_address;
+ size = hdev->pldm ? 0x10000 : (prop->sram_size - SRAM_USER_BASE_OFFSET);
+ dev_dbg(hdev->dev, "Scrubbing SRAM: 0x%09llx - 0x%09llx, val: 0x%llx\n",
+ addr, addr + size, val);
+ rc = gaudi2_memset_device_memory(hdev, addr, size, val);
+ if (rc) {
+ dev_err(hdev->dev, "scrubbing SRAM failed (%d)\n", rc);
+ return rc;
+ }
+
+ /* scrub DRAM */
+ rc = gaudi2_scrub_device_dram(hdev, val);
+ if (rc) {
+ dev_err(hdev->dev, "scrubbing DRAM failed (%d)\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+static void gaudi2_restore_user_sm_registers(struct hl_device *hdev)
+{
+ u64 addr, mon_sts_addr, mon_cfg_addr, cq_lbw_l_addr, cq_lbw_h_addr,
+ cq_lbw_data_addr, cq_base_l_addr, cq_base_h_addr, cq_size_addr;
+ u32 val, size, offset;
+ int dcore_id;
+
+ offset = hdev->asic_prop.first_available_cq[0] * 4;
+ cq_lbw_l_addr = mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_0 + offset;
+ cq_lbw_h_addr = mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_0 + offset;
+ cq_lbw_data_addr = mmDCORE0_SYNC_MNGR_GLBL_LBW_DATA_0 + offset;
+ cq_base_l_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_0 + offset;
+ cq_base_h_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_0 + offset;
+ cq_size_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_0 + offset;
+ size = mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_0 -
+ (mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_0 + offset);
+
+ /* memset dcore0 CQ registers */
+ gaudi2_memset_device_lbw(hdev, cq_lbw_l_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_lbw_h_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_lbw_data_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_base_l_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_base_h_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_size_addr, size, 0);
+
+ cq_lbw_l_addr = mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_0 + DCORE_OFFSET;
+ cq_lbw_h_addr = mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_0 + DCORE_OFFSET;
+ cq_lbw_data_addr = mmDCORE0_SYNC_MNGR_GLBL_LBW_DATA_0 + DCORE_OFFSET;
+ cq_base_l_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_0 + DCORE_OFFSET;
+ cq_base_h_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_0 + DCORE_OFFSET;
+ cq_size_addr = mmDCORE0_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_0 + DCORE_OFFSET;
+ size = mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_0 - mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_0;
+
+ for (dcore_id = 1 ; dcore_id < NUM_OF_DCORES ; dcore_id++) {
+ gaudi2_memset_device_lbw(hdev, cq_lbw_l_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_lbw_h_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_lbw_data_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_base_l_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_base_h_addr, size, 0);
+ gaudi2_memset_device_lbw(hdev, cq_size_addr, size, 0);
+
+ cq_lbw_l_addr += DCORE_OFFSET;
+ cq_lbw_h_addr += DCORE_OFFSET;
+ cq_lbw_data_addr += DCORE_OFFSET;
+ cq_base_l_addr += DCORE_OFFSET;
+ cq_base_h_addr += DCORE_OFFSET;
+ cq_size_addr += DCORE_OFFSET;
+ }
+
+ offset = hdev->asic_prop.first_available_user_mon[0] * 4;
+ addr = mmDCORE0_SYNC_MNGR_OBJS_MON_STATUS_0 + offset;
+ val = 1 << DCORE0_SYNC_MNGR_OBJS_MON_STATUS_PROT_SHIFT;
+ size = mmDCORE0_SYNC_MNGR_OBJS_SM_SEC_0 - (mmDCORE0_SYNC_MNGR_OBJS_MON_STATUS_0 + offset);
+
+ /* memset dcore0 monitors */
+ gaudi2_memset_device_lbw(hdev, addr, size, val);
+
+ addr = mmDCORE0_SYNC_MNGR_OBJS_MON_CONFIG_0 + offset;
+ gaudi2_memset_device_lbw(hdev, addr, size, 0);
+
+ mon_sts_addr = mmDCORE0_SYNC_MNGR_OBJS_MON_STATUS_0 + DCORE_OFFSET;
+ mon_cfg_addr = mmDCORE0_SYNC_MNGR_OBJS_MON_CONFIG_0 + DCORE_OFFSET;
+ size = mmDCORE0_SYNC_MNGR_OBJS_SM_SEC_0 - mmDCORE0_SYNC_MNGR_OBJS_MON_STATUS_0;
+
+ for (dcore_id = 1 ; dcore_id < NUM_OF_DCORES ; dcore_id++) {
+ gaudi2_memset_device_lbw(hdev, mon_sts_addr, size, val);
+ gaudi2_memset_device_lbw(hdev, mon_cfg_addr, size, 0);
+ mon_sts_addr += DCORE_OFFSET;
+ mon_cfg_addr += DCORE_OFFSET;
+ }
+
+ offset = hdev->asic_prop.first_available_user_sob[0] * 4;
+ addr = mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + offset;
+ val = 0;
+ size = mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 -
+ (mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + offset);
+
+ /* memset dcore0 sobs */
+ gaudi2_memset_device_lbw(hdev, addr, size, val);
+
+ addr = mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + DCORE_OFFSET;
+ size = mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 - mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0;
+
+ for (dcore_id = 1 ; dcore_id < NUM_OF_DCORES ; dcore_id++) {
+ gaudi2_memset_device_lbw(hdev, addr, size, val);
+ addr += DCORE_OFFSET;
+ }
+
+ /* Flush all WREG to prevent race */
+ val = RREG32(mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + offset);
+}
+
+static void gaudi2_restore_user_qm_registers(struct hl_device *hdev)
+{
+ u32 reg_base, hw_queue_id;
+
+ for (hw_queue_id = GAUDI2_QUEUE_ID_PDMA_0_0 ; hw_queue_id <= GAUDI2_QUEUE_ID_ROT_1_0;
+ hw_queue_id += NUM_OF_PQ_PER_QMAN) {
+ if (!gaudi2_is_queue_enabled(hdev, hw_queue_id))
+ continue;
+
+ gaudi2_clear_qm_fence_counters_common(hdev, hw_queue_id, false);
+
+ reg_base = gaudi2_qm_blocks_bases[hw_queue_id];
+ WREG32(reg_base + QM_ARB_CFG_0_OFFSET, 0);
+ }
+
+ /* Flush all WREG to prevent race */
+ RREG32(mmPDMA0_QM_ARB_CFG_0);
+}
+
+static void gaudi2_restore_nic_qm_registers(struct hl_device *hdev)
+{
+ u32 reg_base, hw_queue_id;
+
+ for (hw_queue_id = GAUDI2_QUEUE_ID_NIC_0_0 ; hw_queue_id <= GAUDI2_QUEUE_ID_NIC_23_3;
+ hw_queue_id += NUM_OF_PQ_PER_QMAN) {
+ if (!gaudi2_is_queue_enabled(hdev, hw_queue_id))
+ continue;
+
+ gaudi2_clear_qm_fence_counters_common(hdev, hw_queue_id, false);
+
+ reg_base = gaudi2_qm_blocks_bases[hw_queue_id];
+ WREG32(reg_base + QM_ARB_CFG_0_OFFSET, 0);
+ }
+
+ /* Flush all WREG to prevent race */
+ RREG32(mmPDMA0_QM_ARB_CFG_0);
+}
+
+static int gaudi2_context_switch(struct hl_device *hdev, u32 asid)
+{
+ return 0;
+}
+
+static void gaudi2_restore_phase_topology(struct hl_device *hdev)
+{
+}
+
+static void gaudi2_init_block_instances(struct hl_device *hdev, u32 block_idx,
+ struct dup_block_ctx *cfg_ctx)
+{
+ u64 block_base = cfg_ctx->base + block_idx * cfg_ctx->block_off;
+ u8 seq;
+ int i;
+
+ for (i = 0 ; i < cfg_ctx->instances ; i++) {
+ seq = block_idx * cfg_ctx->instances + i;
+
+ /* skip disabled instance */
+ if (!(cfg_ctx->enabled_mask & BIT_ULL(seq)))
+ continue;
+
+ cfg_ctx->instance_cfg_fn(hdev, block_base + i * cfg_ctx->instance_off,
+ cfg_ctx->data);
+ }
+}
+
+static void gaudi2_init_blocks_with_mask(struct hl_device *hdev, struct dup_block_ctx *cfg_ctx,
+ u64 mask)
+{
+ int i;
+
+ cfg_ctx->enabled_mask = mask;
+
+ for (i = 0 ; i < cfg_ctx->blocks ; i++)
+ gaudi2_init_block_instances(hdev, i, cfg_ctx);
+}
+
+void gaudi2_init_blocks(struct hl_device *hdev, struct dup_block_ctx *cfg_ctx)
+{
+ gaudi2_init_blocks_with_mask(hdev, cfg_ctx, U64_MAX);
+}
+
+static int gaudi2_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, void *blob_addr)
+{
+ void *host_mem_virtual_addr;
+ dma_addr_t host_mem_dma_addr;
+ u64 reserved_va_base;
+ u32 pos, size_left, size_to_dma;
+ struct hl_ctx *ctx;
+ int rc = 0;
+
+ /* Fetch the ctx */
+ ctx = hl_get_compute_ctx(hdev);
+ if (!ctx) {
+ dev_err(hdev->dev, "No ctx available\n");
+ return -EINVAL;
+ }
+
+ /* Allocate buffers for read and for poll */
+ host_mem_virtual_addr = hl_asic_dma_alloc_coherent(hdev, SZ_2M, &host_mem_dma_addr,
+ GFP_KERNEL | __GFP_ZERO);
+ if (host_mem_virtual_addr == NULL) {
+ dev_err(hdev->dev, "Failed to allocate memory for KDMA read\n");
+ rc = -ENOMEM;
+ goto put_ctx;
+ }
+
+ /* Reserve VM region on asic side */
+ reserved_va_base = hl_reserve_va_block(hdev, ctx, HL_VA_RANGE_TYPE_HOST, SZ_2M,
+ HL_MMU_VA_ALIGNMENT_NOT_NEEDED);
+ if (!reserved_va_base) {
+ dev_err(hdev->dev, "Failed to reserve vmem on asic\n");
+ rc = -ENOMEM;
+ goto free_data_buffer;
+ }
+
+ /* Create mapping on asic side */
+ mutex_lock(&hdev->mmu_lock);
+
+ rc = hl_mmu_map_contiguous(ctx, reserved_va_base, host_mem_dma_addr, SZ_2M);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to create mapping on asic mmu\n");
+ goto unreserve_va;
+ }
+
+ rc = hl_mmu_invalidate_cache_range(hdev, false,
+ MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV,
+ ctx->asid, reserved_va_base, SZ_2M);
+ if (rc) {
+ hl_mmu_unmap_contiguous(ctx, reserved_va_base, SZ_2M);
+ goto unreserve_va;
+ }
+
+ mutex_unlock(&hdev->mmu_lock);
+
+ /* Enable MMU on KDMA */
+ gaudi2_kdma_set_mmbp_asid(hdev, false, ctx->asid);
+
+ pos = 0;
+ size_left = size;
+ size_to_dma = SZ_2M;
+
+ while (size_left > 0) {
+ if (size_left < SZ_2M)
+ size_to_dma = size_left;
+
+ rc = gaudi2_send_job_to_kdma(hdev, addr, reserved_va_base, size_to_dma, false);
+ if (rc)
+ break;
+
+ memcpy(blob_addr + pos, host_mem_virtual_addr, size_to_dma);
+
+ if (size_left <= SZ_2M)
+ break;
+
+ pos += SZ_2M;
+ addr += SZ_2M;
+ size_left -= SZ_2M;
+ }
+
+ gaudi2_kdma_set_mmbp_asid(hdev, true, HL_KERNEL_ASID_ID);
+
+ mutex_lock(&hdev->mmu_lock);
+
+ rc = hl_mmu_unmap_contiguous(ctx, reserved_va_base, SZ_2M);
+ if (rc)
+ goto unreserve_va;
+
+ rc = hl_mmu_invalidate_cache_range(hdev, false, MMU_OP_USERPTR,
+ ctx->asid, reserved_va_base, SZ_2M);
+
+unreserve_va:
+ mutex_unlock(&hdev->mmu_lock);
+ hl_unreserve_va_block(hdev, ctx, reserved_va_base, SZ_2M);
+free_data_buffer:
+ hl_asic_dma_free_coherent(hdev, SZ_2M, host_mem_virtual_addr, host_mem_dma_addr);
+put_ctx:
+ hl_ctx_put(ctx);
+
+ return rc;
+}
+
+static int gaudi2_internal_cb_pool_init(struct hl_device *hdev, struct hl_ctx *ctx)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int min_alloc_order, rc;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_PMMU))
+ return 0;
+
+ hdev->internal_cb_pool_virt_addr = hl_asic_dma_alloc_coherent(hdev,
+ HOST_SPACE_INTERNAL_CB_SZ,
+ &hdev->internal_cb_pool_dma_addr,
+ GFP_KERNEL | __GFP_ZERO);
+
+ if (!hdev->internal_cb_pool_virt_addr)
+ return -ENOMEM;
+
+ min_alloc_order = ilog2(min(gaudi2_get_signal_cb_size(hdev),
+ gaudi2_get_wait_cb_size(hdev)));
+
+ hdev->internal_cb_pool = gen_pool_create(min_alloc_order, -1);
+ if (!hdev->internal_cb_pool) {
+ dev_err(hdev->dev, "Failed to create internal CB pool\n");
+ rc = -ENOMEM;
+ goto free_internal_cb_pool;
+ }
+
+ rc = gen_pool_add(hdev->internal_cb_pool, (uintptr_t) hdev->internal_cb_pool_virt_addr,
+ HOST_SPACE_INTERNAL_CB_SZ, -1);
+ if (rc) {
+ dev_err(hdev->dev, "Failed to add memory to internal CB pool\n");
+ rc = -EFAULT;
+ goto destroy_internal_cb_pool;
+ }
+
+ hdev->internal_cb_va_base = hl_reserve_va_block(hdev, ctx, HL_VA_RANGE_TYPE_HOST,
+ HOST_SPACE_INTERNAL_CB_SZ, HL_MMU_VA_ALIGNMENT_NOT_NEEDED);
+
+ if (!hdev->internal_cb_va_base) {
+ rc = -ENOMEM;
+ goto destroy_internal_cb_pool;
+ }
+
+ mutex_lock(&hdev->mmu_lock);
+
+ rc = hl_mmu_map_contiguous(ctx, hdev->internal_cb_va_base, hdev->internal_cb_pool_dma_addr,
+ HOST_SPACE_INTERNAL_CB_SZ);
+ if (rc)
+ goto unreserve_internal_cb_pool;
+
+ rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR);
+ if (rc)
+ goto unmap_internal_cb_pool;
+
+ mutex_unlock(&hdev->mmu_lock);
+
+ return 0;
+
+unmap_internal_cb_pool:
+ hl_mmu_unmap_contiguous(ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ);
+unreserve_internal_cb_pool:
+ mutex_unlock(&hdev->mmu_lock);
+ hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ);
+destroy_internal_cb_pool:
+ gen_pool_destroy(hdev->internal_cb_pool);
+free_internal_cb_pool:
+ hl_asic_dma_free_coherent(hdev, HOST_SPACE_INTERNAL_CB_SZ, hdev->internal_cb_pool_virt_addr,
+ hdev->internal_cb_pool_dma_addr);
+
+ return rc;
+}
+
+static void gaudi2_internal_cb_pool_fini(struct hl_device *hdev, struct hl_ctx *ctx)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_PMMU))
+ return;
+
+ mutex_lock(&hdev->mmu_lock);
+ hl_mmu_unmap_contiguous(ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ);
+ hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ);
+ hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
+ mutex_unlock(&hdev->mmu_lock);
+
+ gen_pool_destroy(hdev->internal_cb_pool);
+
+ hl_asic_dma_free_coherent(hdev, HOST_SPACE_INTERNAL_CB_SZ, hdev->internal_cb_pool_virt_addr,
+ hdev->internal_cb_pool_dma_addr);
+}
+
+static void gaudi2_restore_user_registers(struct hl_device *hdev)
+{
+ gaudi2_restore_user_sm_registers(hdev);
+ gaudi2_restore_user_qm_registers(hdev);
+}
+
+static int gaudi2_map_virtual_msix_doorbell_memory(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int rc;
+
+ rc = hl_mmu_map_page(ctx, RESERVED_VA_FOR_VIRTUAL_MSIX_DOORBELL_START,
+ gaudi2->virt_msix_db_dma_addr, prop->pmmu.page_size, true);
+ if (rc)
+ dev_err(hdev->dev, "Failed to map VA %#llx for virtual MSI-X doorbell memory\n",
+ RESERVED_VA_FOR_VIRTUAL_MSIX_DOORBELL_START);
+
+ return rc;
+}
+
+static void gaudi2_unmap_virtual_msix_doorbell_memory(struct hl_ctx *ctx)
+{
+ struct hl_device *hdev = ctx->hdev;
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ int rc;
+
+ rc = hl_mmu_unmap_page(ctx, RESERVED_VA_FOR_VIRTUAL_MSIX_DOORBELL_START,
+ prop->pmmu.page_size, true);
+ if (rc)
+ dev_err(hdev->dev, "Failed to unmap VA %#llx of virtual MSI-X doorbell memory\n",
+ RESERVED_VA_FOR_VIRTUAL_MSIX_DOORBELL_START);
+}
+
+static int gaudi2_ctx_init(struct hl_ctx *ctx)
+{
+ int rc;
+
+ if (ctx->asid == HL_KERNEL_ASID_ID)
+ return 0;
+
+ rc = gaudi2_mmu_prepare(ctx->hdev, ctx->asid);
+ if (rc)
+ return rc;
+
+ /* No need to clear user registers if the device has just
+ * performed reset, we restore only nic qm registers
+ */
+ if (ctx->hdev->reset_upon_device_release)
+ gaudi2_restore_nic_qm_registers(ctx->hdev);
+ else
+ gaudi2_restore_user_registers(ctx->hdev);
+
+ rc = gaudi2_internal_cb_pool_init(ctx->hdev, ctx);
+ if (rc)
+ return rc;
+
+ rc = gaudi2_map_virtual_msix_doorbell_memory(ctx);
+ if (rc)
+ gaudi2_internal_cb_pool_fini(ctx->hdev, ctx);
+
+ return rc;
+}
+
+static void gaudi2_ctx_fini(struct hl_ctx *ctx)
+{
+ if (ctx->asid == HL_KERNEL_ASID_ID)
+ return;
+
+ gaudi2_internal_cb_pool_fini(ctx->hdev, ctx);
+
+ gaudi2_unmap_virtual_msix_doorbell_memory(ctx);
+}
+
+static int gaudi2_pre_schedule_cs(struct hl_cs *cs)
+{
+ struct hl_device *hdev = cs->ctx->hdev;
+ int index = cs->sequence & (hdev->asic_prop.max_pending_cs - 1);
+ u32 mon_payload, sob_id, mon_id;
+
+ if (!cs_needs_completion(cs))
+ return 0;
+
+ /*
+ * First 64 SOB/MON are reserved for driver for QMAN auto completion
+ * mechanism. Each SOB/MON pair are used for a pending CS with the same
+ * cyclic index. The SOB value is increased when each of the CS jobs is
+ * completed. When the SOB reaches the number of CS jobs, the monitor
+ * generates MSI-X interrupt.
+ */
+
+ sob_id = mon_id = index;
+ mon_payload = (1 << CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT) |
+ (1 << CQ_ENTRY_READY_SHIFT) | index;
+
+ gaudi2_arm_cq_monitor(hdev, sob_id, mon_id, GAUDI2_RESERVED_CQ_CS_COMPLETION, mon_payload,
+ cs->jobs_cnt);
+
+ return 0;
+}
+
+static u32 gaudi2_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx)
+{
+ return HL_INVALID_QUEUE;
+}
+
+static u32 gaudi2_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id, u32 size, bool eb)
+{
+ struct hl_cb *cb = data;
+ struct packet_msg_short *pkt;
+ u32 value, ctl, pkt_size = sizeof(*pkt);
+
+ pkt = (struct packet_msg_short *) (uintptr_t) (cb->kernel_address + size);
+ memset(pkt, 0, pkt_size);
+
+ /* Inc by 1, Mode ADD */
+ value = FIELD_PREP(GAUDI2_PKT_SHORT_VAL_SOB_SYNC_VAL_MASK, 1);
+ value |= FIELD_PREP(GAUDI2_PKT_SHORT_VAL_SOB_MOD_MASK, 1);
+
+ ctl = FIELD_PREP(GAUDI2_PKT_SHORT_CTL_ADDR_MASK, sob_id * 4);
+ ctl |= FIELD_PREP(GAUDI2_PKT_SHORT_CTL_BASE_MASK, 1); /* SOB base */
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_OPCODE_MASK, PACKET_MSG_SHORT);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_EB_MASK, eb);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_MB_MASK, 1);
+
+ pkt->value = cpu_to_le32(value);
+ pkt->ctl = cpu_to_le32(ctl);
+
+ return size + pkt_size;
+}
+
+static u32 gaudi2_add_mon_msg_short(struct packet_msg_short *pkt, u32 value, u16 addr)
+{
+ u32 ctl, pkt_size = sizeof(*pkt);
+
+ memset(pkt, 0, pkt_size);
+
+ ctl = FIELD_PREP(GAUDI2_PKT_SHORT_CTL_ADDR_MASK, addr);
+ ctl |= FIELD_PREP(GAUDI2_PKT_SHORT_CTL_BASE_MASK, 0); /* MON base */
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_OPCODE_MASK, PACKET_MSG_SHORT);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_EB_MASK, 0);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_MB_MASK, 0);
+
+ pkt->value = cpu_to_le32(value);
+ pkt->ctl = cpu_to_le32(ctl);
+
+ return pkt_size;
+}
+
+static u32 gaudi2_add_arm_monitor_pkt(struct hl_device *hdev, struct packet_msg_short *pkt,
+ u16 sob_base, u8 sob_mask, u16 sob_val, u16 addr)
+{
+ u32 ctl, value, pkt_size = sizeof(*pkt);
+ u8 mask;
+
+ if (hl_gen_sob_mask(sob_base, sob_mask, &mask)) {
+ dev_err(hdev->dev, "sob_base %u (mask %#x) is not valid\n", sob_base, sob_mask);
+ return 0;
+ }
+
+ memset(pkt, 0, pkt_size);
+
+ value = FIELD_PREP(GAUDI2_PKT_SHORT_VAL_MON_SYNC_GID_MASK, sob_base / 8);
+ value |= FIELD_PREP(GAUDI2_PKT_SHORT_VAL_MON_SYNC_VAL_MASK, sob_val);
+ value |= FIELD_PREP(GAUDI2_PKT_SHORT_VAL_MON_MODE_MASK, 0); /* GREATER OR EQUAL*/
+ value |= FIELD_PREP(GAUDI2_PKT_SHORT_VAL_MON_MASK_MASK, mask);
+
+ ctl = FIELD_PREP(GAUDI2_PKT_SHORT_CTL_ADDR_MASK, addr);
+ ctl |= FIELD_PREP(GAUDI2_PKT_SHORT_CTL_BASE_MASK, 0); /* MON base */
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_OPCODE_MASK, PACKET_MSG_SHORT);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_EB_MASK, 0);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_MB_MASK, 1);
+
+ pkt->value = cpu_to_le32(value);
+ pkt->ctl = cpu_to_le32(ctl);
+
+ return pkt_size;
+}
+
+static u32 gaudi2_add_fence_pkt(struct packet_fence *pkt)
+{
+ u32 ctl, cfg, pkt_size = sizeof(*pkt);
+
+ memset(pkt, 0, pkt_size);
+
+ cfg = FIELD_PREP(GAUDI2_PKT_FENCE_CFG_DEC_VAL_MASK, 1);
+ cfg |= FIELD_PREP(GAUDI2_PKT_FENCE_CFG_TARGET_VAL_MASK, 1);
+ cfg |= FIELD_PREP(GAUDI2_PKT_FENCE_CFG_ID_MASK, 2);
+
+ ctl = FIELD_PREP(GAUDI2_PKT_CTL_OPCODE_MASK, PACKET_FENCE);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_EB_MASK, 0);
+ ctl |= FIELD_PREP(GAUDI2_PKT_CTL_MB_MASK, 1);
+
+ pkt->cfg = cpu_to_le32(cfg);
+ pkt->ctl = cpu_to_le32(ctl);
+
+ return pkt_size;
+}
+
+static u32 gaudi2_gen_wait_cb(struct hl_device *hdev, struct hl_gen_wait_properties *prop)
+{
+ struct hl_cb *cb = prop->data;
+ void *buf = (void *) (uintptr_t) (cb->kernel_address);
+
+ u64 monitor_base, fence_addr = 0;
+ u32 stream_index, size = prop->size;
+ u16 msg_addr_offset;
+
+ stream_index = prop->q_idx % 4;
+ fence_addr = CFG_BASE + gaudi2_qm_blocks_bases[prop->q_idx] +
+ QM_FENCE2_OFFSET + stream_index * 4;
+
+ /*
+ * monitor_base should be the content of the base0 address registers,
+ * so it will be added to the msg short offsets
+ */
+ monitor_base = mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0;
+
+ /* First monitor config packet: low address of the sync */
+ msg_addr_offset = (mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 + prop->mon_id * 4) -
+ monitor_base;
+
+ size += gaudi2_add_mon_msg_short(buf + size, (u32) fence_addr, msg_addr_offset);
+
+ /* Second monitor config packet: high address of the sync */
+ msg_addr_offset = (mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_ADDRH_0 + prop->mon_id * 4) -
+ monitor_base;
+
+ size += gaudi2_add_mon_msg_short(buf + size, (u32) (fence_addr >> 32), msg_addr_offset);
+
+ /*
+ * Third monitor config packet: the payload, i.e. what to write when the
+ * sync triggers
+ */
+ msg_addr_offset = (mmDCORE0_SYNC_MNGR_OBJS_MON_PAY_DATA_0 + prop->mon_id * 4) -
+ monitor_base;
+
+ size += gaudi2_add_mon_msg_short(buf + size, 1, msg_addr_offset);
+
+ /* Fourth monitor config packet: bind the monitor to a sync object */
+ msg_addr_offset = (mmDCORE0_SYNC_MNGR_OBJS_MON_ARM_0 + prop->mon_id * 4) - monitor_base;
+
+ size += gaudi2_add_arm_monitor_pkt(hdev, buf + size, prop->sob_base, prop->sob_mask,
+ prop->sob_val, msg_addr_offset);
+
+ /* Fence packet */
+ size += gaudi2_add_fence_pkt(buf + size);
+
+ return size;
+}
+
+static void gaudi2_reset_sob(struct hl_device *hdev, void *data)
+{
+ struct hl_hw_sob *hw_sob = data;
+
+ dev_dbg(hdev->dev, "reset SOB, q_idx: %d, sob_id: %d\n", hw_sob->q_idx, hw_sob->sob_id);
+
+ WREG32(mmDCORE0_SYNC_MNGR_OBJS_SOB_OBJ_0 + hw_sob->sob_id * 4, 0);
+
+ kref_init(&hw_sob->kref);
+}
+
+static void gaudi2_reset_sob_group(struct hl_device *hdev, u16 sob_group)
+{
+}
+
+static u64 gaudi2_get_device_time(struct hl_device *hdev)
+{
+ u64 device_time = ((u64) RREG32(mmPSOC_TIMESTAMP_CNTCVU)) << 32;
+
+ return device_time | RREG32(mmPSOC_TIMESTAMP_CNTCVL);
+}
+
+static int gaudi2_collective_wait_init_cs(struct hl_cs *cs)
+{
+ return 0;
+}
+
+static int gaudi2_collective_wait_create_jobs(struct hl_device *hdev, struct hl_ctx *ctx,
+ struct hl_cs *cs, u32 wait_queue_id,
+ u32 collective_engine_id, u32 encaps_signal_offset)
+{
+ return -EINVAL;
+}
+
+/*
+ * hl_mmu_scramble - converts a dram (non power of 2) page-size aligned address
+ * to DMMU page-size address (64MB) before mapping it in
+ * the MMU.
+ * The operation is performed on both the virtual and physical addresses.
+ * for device with 6 HBMs the scramble is:
+ * (addr[47:0] / 48M) * 64M + addr % 48M + addr[63:48]
+ *
+ * Example:
+ * =============================================================================
+ * Allocated DRAM Reserved VA scrambled VA for MMU mapping Scrambled PA
+ * Phys address in MMU last
+ * HOP
+ * =============================================================================
+ * PA1 0x3000000 VA1 0x9C000000 SVA1= (VA1/48M)*64M 0xD0000000 <- PA1/48M 0x1
+ * PA2 0x9000000 VA2 0x9F000000 SVA2= (VA2/48M)*64M 0xD4000000 <- PA2/48M 0x3
+ * =============================================================================
+ */
+static u64 gaudi2_mmu_scramble_addr(struct hl_device *hdev, u64 raw_addr)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 divisor, mod_va;
+ u64 div_va;
+
+ /* accept any address in the DRAM address space */
+ if (hl_mem_area_inside_range(raw_addr, sizeof(raw_addr), DRAM_PHYS_BASE,
+ VA_HBM_SPACE_END)) {
+
+ divisor = prop->num_functional_hbms * GAUDI2_HBM_MMU_SCRM_MEM_SIZE;
+ div_va = div_u64_rem(raw_addr & GAUDI2_HBM_MMU_SCRM_ADDRESS_MASK, divisor, &mod_va);
+ return (raw_addr & ~GAUDI2_HBM_MMU_SCRM_ADDRESS_MASK) |
+ (div_va << GAUDI2_HBM_MMU_SCRM_DIV_SHIFT) |
+ (mod_va << GAUDI2_HBM_MMU_SCRM_MOD_SHIFT);
+ }
+
+ return raw_addr;
+}
+
+static u64 gaudi2_mmu_descramble_addr(struct hl_device *hdev, u64 scrambled_addr)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+ u32 divisor, mod_va;
+ u64 div_va;
+
+ /* accept any address in the DRAM address space */
+ if (hl_mem_area_inside_range(scrambled_addr, sizeof(scrambled_addr), DRAM_PHYS_BASE,
+ VA_HBM_SPACE_END)) {
+
+ divisor = prop->num_functional_hbms * GAUDI2_HBM_MMU_SCRM_MEM_SIZE;
+ div_va = div_u64_rem(scrambled_addr & GAUDI2_HBM_MMU_SCRM_ADDRESS_MASK,
+ PAGE_SIZE_64MB, &mod_va);
+
+ return ((scrambled_addr & ~GAUDI2_HBM_MMU_SCRM_ADDRESS_MASK) +
+ (div_va * divisor + mod_va));
+ }
+
+ return scrambled_addr;
+}
+
+static u32 gaudi2_get_dec_base_addr(struct hl_device *hdev, u32 core_id)
+{
+ u32 base = 0, dcore_id, dec_id;
+
+ if (core_id >= NUMBER_OF_DEC) {
+ dev_err(hdev->dev, "Unexpected core number %d for DEC\n", core_id);
+ goto out;
+ }
+
+ if (core_id < 8) {
+ dcore_id = core_id / NUM_OF_DEC_PER_DCORE;
+ dec_id = core_id % NUM_OF_DEC_PER_DCORE;
+
+ base = mmDCORE0_DEC0_CMD_BASE + dcore_id * DCORE_OFFSET +
+ dec_id * DCORE_VDEC_OFFSET;
+ } else {
+ /* PCIe Shared Decoder */
+ base = mmPCIE_DEC0_CMD_BASE + ((core_id % 8) * PCIE_VDEC_OFFSET);
+ }
+out:
+ return base;
+}
+
+static int gaudi2_get_hw_block_id(struct hl_device *hdev, u64 block_addr,
+ u32 *block_size, u32 *block_id)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ int i;
+
+ for (i = 0 ; i < NUM_USER_MAPPED_BLOCKS ; i++) {
+ if (block_addr == CFG_BASE + gaudi2->mapped_blocks[i].address) {
+ *block_id = i;
+ if (block_size)
+ *block_size = gaudi2->mapped_blocks[i].size;
+ return 0;
+ }
+ }
+
+ dev_err(hdev->dev, "Invalid block address %#llx", block_addr);
+
+ return -EINVAL;
+}
+
+static int gaudi2_block_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
+ u32 block_id, u32 block_size)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 offset_in_bar;
+ u64 address;
+ int rc;
+
+ if (block_id >= NUM_USER_MAPPED_BLOCKS) {
+ dev_err(hdev->dev, "Invalid block id %u", block_id);
+ return -EINVAL;
+ }
+
+ /* we allow mapping only an entire block */
+ if (block_size != gaudi2->mapped_blocks[block_id].size) {
+ dev_err(hdev->dev, "Invalid block size %u", block_size);
+ return -EINVAL;
+ }
+
+ offset_in_bar = CFG_BASE + gaudi2->mapped_blocks[block_id].address - STM_FLASH_BASE_ADDR;
+
+ address = pci_resource_start(hdev->pdev, SRAM_CFG_BAR_ID) + offset_in_bar;
+
+ vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
+ VM_DONTCOPY | VM_NORESERVE);
+
+ rc = remap_pfn_range(vma, vma->vm_start, address >> PAGE_SHIFT,
+ block_size, vma->vm_page_prot);
+ if (rc)
+ dev_err(hdev->dev, "remap_pfn_range error %d", rc);
+
+ return rc;
+}
+
+static void gaudi2_enable_events_from_fw(struct hl_device *hdev)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ struct cpu_dyn_regs *dyn_regs = &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
+ u32 irq_handler_offset = le32_to_cpu(dyn_regs->gic_host_ints_irq);
+
+ if (gaudi2->hw_cap_initialized & HW_CAP_CPU_Q)
+ WREG32(irq_handler_offset,
+ gaudi2_irq_map_table[GAUDI2_EVENT_CPU_INTS_REGISTER].cpu_id);
+}
+
+static int gaudi2_get_mmu_base(struct hl_device *hdev, u64 mmu_id, u32 *mmu_base)
+{
+ switch (mmu_id) {
+ case HW_CAP_DCORE0_DMMU0:
+ *mmu_base = mmDCORE0_HMMU0_MMU_BASE;
+ break;
+ case HW_CAP_DCORE0_DMMU1:
+ *mmu_base = mmDCORE0_HMMU1_MMU_BASE;
+ break;
+ case HW_CAP_DCORE0_DMMU2:
+ *mmu_base = mmDCORE0_HMMU2_MMU_BASE;
+ break;
+ case HW_CAP_DCORE0_DMMU3:
+ *mmu_base = mmDCORE0_HMMU3_MMU_BASE;
+ break;
+ case HW_CAP_DCORE1_DMMU0:
+ *mmu_base = mmDCORE1_HMMU0_MMU_BASE;
+ break;
+ case HW_CAP_DCORE1_DMMU1:
+ *mmu_base = mmDCORE1_HMMU1_MMU_BASE;
+ break;
+ case HW_CAP_DCORE1_DMMU2:
+ *mmu_base = mmDCORE1_HMMU2_MMU_BASE;
+ break;
+ case HW_CAP_DCORE1_DMMU3:
+ *mmu_base = mmDCORE1_HMMU3_MMU_BASE;
+ break;
+ case HW_CAP_DCORE2_DMMU0:
+ *mmu_base = mmDCORE2_HMMU0_MMU_BASE;
+ break;
+ case HW_CAP_DCORE2_DMMU1:
+ *mmu_base = mmDCORE2_HMMU1_MMU_BASE;
+ break;
+ case HW_CAP_DCORE2_DMMU2:
+ *mmu_base = mmDCORE2_HMMU2_MMU_BASE;
+ break;
+ case HW_CAP_DCORE2_DMMU3:
+ *mmu_base = mmDCORE2_HMMU3_MMU_BASE;
+ break;
+ case HW_CAP_DCORE3_DMMU0:
+ *mmu_base = mmDCORE3_HMMU0_MMU_BASE;
+ break;
+ case HW_CAP_DCORE3_DMMU1:
+ *mmu_base = mmDCORE3_HMMU1_MMU_BASE;
+ break;
+ case HW_CAP_DCORE3_DMMU2:
+ *mmu_base = mmDCORE3_HMMU2_MMU_BASE;
+ break;
+ case HW_CAP_DCORE3_DMMU3:
+ *mmu_base = mmDCORE3_HMMU3_MMU_BASE;
+ break;
+ case HW_CAP_PMMU:
+ *mmu_base = mmPMMU_HBW_MMU_BASE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void gaudi2_ack_mmu_error(struct hl_device *hdev, u64 mmu_id)
+{
+ bool is_pmmu = (mmu_id == HW_CAP_PMMU);
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u32 mmu_base;
+
+ if (!(gaudi2->hw_cap_initialized & mmu_id))
+ return;
+
+ if (gaudi2_get_mmu_base(hdev, mmu_id, &mmu_base))
+ return;
+
+ gaudi2_handle_page_error(hdev, mmu_base, is_pmmu, NULL);
+ gaudi2_handle_access_error(hdev, mmu_base, is_pmmu);
+}
+
+static int gaudi2_ack_mmu_page_fault_or_access_error(struct hl_device *hdev, u64 mmu_cap_mask)
+{
+ u32 i, mmu_id, num_of_hmmus = NUM_OF_HMMU_PER_DCORE * NUM_OF_DCORES;
+
+ /* check all HMMUs */
+ for (i = 0 ; i < num_of_hmmus ; i++) {
+ mmu_id = HW_CAP_DCORE0_DMMU0 << i;
+
+ if (mmu_cap_mask & mmu_id)
+ gaudi2_ack_mmu_error(hdev, mmu_id);
+ }
+
+ /* check PMMU */
+ if (mmu_cap_mask & HW_CAP_PMMU)
+ gaudi2_ack_mmu_error(hdev, HW_CAP_PMMU);
+
+ return 0;
+}
+
+static void gaudi2_get_msi_info(__le32 *table)
+{
+ table[CPUCP_EVENT_QUEUE_MSI_TYPE] = cpu_to_le32(GAUDI2_EVENT_QUEUE_MSIX_IDX);
+ table[CPUCP_EVENT_QUEUE_ERR_MSI_TYPE] = cpu_to_le32(GAUDI2_IRQ_NUM_EQ_ERROR);
+}
+
+static int gaudi2_map_pll_idx_to_fw_idx(u32 pll_idx)
+{
+ switch (pll_idx) {
+ case HL_GAUDI2_CPU_PLL: return CPU_PLL;
+ case HL_GAUDI2_PCI_PLL: return PCI_PLL;
+ case HL_GAUDI2_NIC_PLL: return NIC_PLL;
+ case HL_GAUDI2_DMA_PLL: return DMA_PLL;
+ case HL_GAUDI2_MESH_PLL: return MESH_PLL;
+ case HL_GAUDI2_MME_PLL: return MME_PLL;
+ case HL_GAUDI2_TPC_PLL: return TPC_PLL;
+ case HL_GAUDI2_IF_PLL: return IF_PLL;
+ case HL_GAUDI2_SRAM_PLL: return SRAM_PLL;
+ case HL_GAUDI2_HBM_PLL: return HBM_PLL;
+ case HL_GAUDI2_VID_PLL: return VID_PLL;
+ case HL_GAUDI2_MSS_PLL: return MSS_PLL;
+ default: return -EINVAL;
+ }
+}
+
+static int gaudi2_gen_sync_to_engine_map(struct hl_device *hdev, struct hl_sync_to_engine_map *map)
+{
+ /* Not implemented */
+ return 0;
+}
+
+static int gaudi2_monitor_valid(struct hl_mon_state_dump *mon)
+{
+ /* Not implemented */
+ return 0;
+}
+
+static int gaudi2_print_single_monitor(char **buf, size_t *size, size_t *offset,
+ struct hl_device *hdev, struct hl_mon_state_dump *mon)
+{
+ /* Not implemented */
+ return 0;
+}
+
+
+static int gaudi2_print_fences_single_engine(struct hl_device *hdev, u64 base_offset,
+ u64 status_base_offset, enum hl_sync_engine_type engine_type,
+ u32 engine_id, char **buf, size_t *size, size_t *offset)
+{
+ /* Not implemented */
+ return 0;
+}
+
+
+static struct hl_state_dump_specs_funcs gaudi2_state_dump_funcs = {
+ .monitor_valid = gaudi2_monitor_valid,
+ .print_single_monitor = gaudi2_print_single_monitor,
+ .gen_sync_to_engine_map = gaudi2_gen_sync_to_engine_map,
+ .print_fences_single_engine = gaudi2_print_fences_single_engine,
+};
+
+static void gaudi2_state_dump_init(struct hl_device *hdev)
+{
+ /* Not implemented */
+ hdev->state_dump_specs.props = gaudi2_state_dump_specs_props;
+ hdev->state_dump_specs.funcs = gaudi2_state_dump_funcs;
+}
+
+static u32 gaudi2_get_sob_addr(struct hl_device *hdev, u32 sob_id)
+{
+ return 0;
+}
+
+static u32 *gaudi2_get_stream_master_qid_arr(void)
+{
+ return NULL;
+}
+
+static void gaudi2_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp,
+ struct attribute_group *dev_vrm_attr_grp)
+{
+ hl_sysfs_add_dev_clk_attr(hdev, dev_clk_attr_grp);
+ hl_sysfs_add_dev_vrm_attr(hdev, dev_vrm_attr_grp);
+}
+
+static int gaudi2_mmu_get_real_page_size(struct hl_device *hdev, struct hl_mmu_properties *mmu_prop,
+ u32 page_size, u32 *real_page_size, bool is_dram_addr)
+{
+ struct asic_fixed_properties *prop = &hdev->asic_prop;
+
+ /* for host pages the page size must be */
+ if (!is_dram_addr) {
+ if (page_size % mmu_prop->page_size)
+ goto page_size_err;
+
+ *real_page_size = mmu_prop->page_size;
+ return 0;
+ }
+
+ if ((page_size % prop->dram_page_size) || (prop->dram_page_size > mmu_prop->page_size))
+ goto page_size_err;
+
+ /*
+ * MMU page size is different from DRAM page size (more precisely, DMMU page is greater
+ * than DRAM page size).
+ * for this reason work with the DRAM page size and let the MMU scrambling routine handle
+ * this mismatch when calculating the address to place in the MMU page table.
+ * (in that case also make sure that the dram_page_size is not greater than the
+ * mmu page size)
+ */
+ *real_page_size = prop->dram_page_size;
+
+ return 0;
+
+page_size_err:
+ dev_err(hdev->dev, "page size of 0x%X is not 0x%X aligned, can't map\n",
+ page_size, mmu_prop->page_size >> 10);
+ return -EFAULT;
+}
+
+static int gaudi2_get_monitor_dump(struct hl_device *hdev, void *data)
+{
+ return -EOPNOTSUPP;
+}
+
+int gaudi2_send_device_activity(struct hl_device *hdev, bool open)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q))
+ return 0;
+
+ return hl_fw_send_device_activity(hdev, open);
+}
+
+static u64 gaudi2_read_pte(struct hl_device *hdev, u64 addr)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+ u64 val;
+
+ if (hdev->reset_info.hard_reset_pending)
+ return U64_MAX;
+
+ val = readq(hdev->pcie_bar[DRAM_BAR_ID] + (addr - gaudi2->dram_bar_cur_addr));
+
+ return val;
+}
+
+static void gaudi2_write_pte(struct hl_device *hdev, u64 addr, u64 val)
+{
+ struct gaudi2_device *gaudi2 = hdev->asic_specific;
+
+ if (hdev->reset_info.hard_reset_pending)
+ return;
+
+ writeq(val, hdev->pcie_bar[DRAM_BAR_ID] + (addr - gaudi2->dram_bar_cur_addr));
+}
+
+static const struct hl_asic_funcs gaudi2_funcs = {
+ .early_init = gaudi2_early_init,
+ .early_fini = gaudi2_early_fini,
+ .late_init = gaudi2_late_init,
+ .late_fini = gaudi2_late_fini,
+ .sw_init = gaudi2_sw_init,
+ .sw_fini = gaudi2_sw_fini,
+ .hw_init = gaudi2_hw_init,
+ .hw_fini = gaudi2_hw_fini,
+ .halt_engines = gaudi2_halt_engines,
+ .suspend = gaudi2_suspend,
+ .resume = gaudi2_resume,
+ .mmap = gaudi2_mmap,
+ .ring_doorbell = gaudi2_ring_doorbell,
+ .pqe_write = gaudi2_pqe_write,
+ .asic_dma_alloc_coherent = gaudi2_dma_alloc_coherent,
+ .asic_dma_free_coherent = gaudi2_dma_free_coherent,
+ .scrub_device_mem = gaudi2_scrub_device_mem,
+ .scrub_device_dram = gaudi2_scrub_device_dram,
+ .get_int_queue_base = NULL,
+ .test_queues = gaudi2_test_queues,
+ .asic_dma_pool_zalloc = gaudi2_dma_pool_zalloc,
+ .asic_dma_pool_free = gaudi2_dma_pool_free,
+ .cpu_accessible_dma_pool_alloc = gaudi2_cpu_accessible_dma_pool_alloc,
+ .cpu_accessible_dma_pool_free = gaudi2_cpu_accessible_dma_pool_free,
+ .dma_unmap_sgtable = hl_asic_dma_unmap_sgtable,
+ .cs_parser = gaudi2_cs_parser,
+ .dma_map_sgtable = hl_asic_dma_map_sgtable,
+ .add_end_of_cb_packets = NULL,
+ .update_eq_ci = gaudi2_update_eq_ci,
+ .context_switch = gaudi2_context_switch,
+ .restore_phase_topology = gaudi2_restore_phase_topology,
+ .debugfs_read_dma = gaudi2_debugfs_read_dma,
+ .add_device_attr = gaudi2_add_device_attr,
+ .handle_eqe = gaudi2_handle_eqe,
+ .get_events_stat = gaudi2_get_events_stat,
+ .read_pte = gaudi2_read_pte,
+ .write_pte = gaudi2_write_pte,
+ .mmu_invalidate_cache = gaudi2_mmu_invalidate_cache,
+ .mmu_invalidate_cache_range = gaudi2_mmu_invalidate_cache_range,
+ .mmu_prefetch_cache_range = NULL,
+ .send_heartbeat = gaudi2_send_heartbeat,
+ .debug_coresight = gaudi2_debug_coresight,
+ .is_device_idle = gaudi2_is_device_idle,
+ .compute_reset_late_init = gaudi2_compute_reset_late_init,
+ .hw_queues_lock = gaudi2_hw_queues_lock,
+ .hw_queues_unlock = gaudi2_hw_queues_unlock,
+ .get_pci_id = gaudi2_get_pci_id,
+ .get_eeprom_data = gaudi2_get_eeprom_data,
+ .get_monitor_dump = gaudi2_get_monitor_dump,
+ .send_cpu_message = gaudi2_send_cpu_message,
+ .pci_bars_map = gaudi2_pci_bars_map,
+ .init_iatu = gaudi2_init_iatu,
+ .rreg = hl_rreg,
+ .wreg = hl_wreg,
+ .halt_coresight = gaudi2_halt_coresight,
+ .ctx_init = gaudi2_ctx_init,
+ .ctx_fini = gaudi2_ctx_fini,
+ .pre_schedule_cs = gaudi2_pre_schedule_cs,
+ .get_queue_id_for_cq = gaudi2_get_queue_id_for_cq,
+ .load_firmware_to_device = NULL,
+ .load_boot_fit_to_device = NULL,
+ .get_signal_cb_size = gaudi2_get_signal_cb_size,
+ .get_wait_cb_size = gaudi2_get_wait_cb_size,
+ .gen_signal_cb = gaudi2_gen_signal_cb,
+ .gen_wait_cb = gaudi2_gen_wait_cb,
+ .reset_sob = gaudi2_reset_sob,
+ .reset_sob_group = gaudi2_reset_sob_group,
+ .get_device_time = gaudi2_get_device_time,
+ .pb_print_security_errors = gaudi2_pb_print_security_errors,
+ .collective_wait_init_cs = gaudi2_collective_wait_init_cs,
+ .collective_wait_create_jobs = gaudi2_collective_wait_create_jobs,
+ .get_dec_base_addr = gaudi2_get_dec_base_addr,
+ .scramble_addr = gaudi2_mmu_scramble_addr,
+ .descramble_addr = gaudi2_mmu_descramble_addr,
+ .ack_protection_bits_errors = gaudi2_ack_protection_bits_errors,
+ .get_hw_block_id = gaudi2_get_hw_block_id,
+ .hw_block_mmap = gaudi2_block_mmap,
+ .enable_events_from_fw = gaudi2_enable_events_from_fw,
+ .ack_mmu_errors = gaudi2_ack_mmu_page_fault_or_access_error,
+ .get_msi_info = gaudi2_get_msi_info,
+ .map_pll_idx_to_fw_idx = gaudi2_map_pll_idx_to_fw_idx,
+ .init_firmware_preload_params = gaudi2_init_firmware_preload_params,
+ .init_firmware_loader = gaudi2_init_firmware_loader,
+ .init_cpu_scrambler_dram = gaudi2_init_scrambler_hbm,
+ .state_dump_init = gaudi2_state_dump_init,
+ .get_sob_addr = &gaudi2_get_sob_addr,
+ .set_pci_memory_regions = gaudi2_set_pci_memory_regions,
+ .get_stream_master_qid_arr = gaudi2_get_stream_master_qid_arr,
+ .check_if_razwi_happened = gaudi2_check_if_razwi_happened,
+ .mmu_get_real_page_size = gaudi2_mmu_get_real_page_size,
+ .access_dev_mem = hl_access_dev_mem,
+ .set_dram_bar_base = gaudi2_set_hbm_bar_base,
+ .set_engine_cores = gaudi2_set_engine_cores,
+ .set_engines = gaudi2_set_engines,
+ .send_device_activity = gaudi2_send_device_activity,
+ .set_dram_properties = gaudi2_set_dram_properties,
+ .set_binning_masks = gaudi2_set_binning_masks,
+};
+
+void gaudi2_set_asic_funcs(struct hl_device *hdev)
+{
+ hdev->asic_funcs = &gaudi2_funcs;
+}
diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2P.h b/drivers/accel/habanalabs/gaudi2/gaudi2P.h
index e4bc4009f05b..bdf5c1bd2d63 100644
--- a/drivers/misc/habanalabs/gaudi2/gaudi2P.h
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2P.h
@@ -8,24 +8,19 @@
#ifndef GAUDI2P_H_
#define GAUDI2P_H_
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#include "../common/habanalabs.h"
-#include "../include/common/hl_boot_if.h"
+#include <linux/habanalabs/hl_boot_if.h>
#include "../include/gaudi2/gaudi2.h"
#include "../include/gaudi2/gaudi2_packets.h"
#include "../include/gaudi2/gaudi2_fw_if.h"
#include "../include/gaudi2/gaudi2_async_events.h"
-#include "../include/gaudi2/gaudi2_async_virt_events.h"
#define GAUDI2_LINUX_FW_FILE "habanalabs/gaudi2/gaudi2-fit.itb"
#define GAUDI2_BOOT_FIT_FILE "habanalabs/gaudi2/gaudi2-boot-fit.itb"
-#define MMU_PAGE_TABLES_INITIAL_SIZE 0x10000000 /* 256MB */
-
#define GAUDI2_CPU_TIMEOUT_USEC 30000000 /* 30s */
-#define GAUDI2_FPGA_CPU_TIMEOUT 100000000 /* 100s */
-
#define NUMBER_OF_PDMA_QUEUES 2
#define NUMBER_OF_EDMA_QUEUES 8
#define NUMBER_OF_MME_QUEUES 4
@@ -87,6 +82,7 @@
#define CORESIGHT_TIMEOUT_USEC 100000 /* 100 ms */
#define GAUDI2_PREBOOT_REQ_TIMEOUT_USEC 25000000 /* 25s */
+#define GAUDI2_PREBOOT_EXTENDED_REQ_TIMEOUT_USEC 85000000 /* 85s */
#define GAUDI2_BOOT_FIT_REQ_TIMEOUT_USEC 10000000 /* 10s */
@@ -101,7 +97,7 @@
#define GAUDI2_DEFAULT_CARD_NAME "HL225"
#define QMAN_STREAMS 4
-#define PQ_FETCHER_CACHE_SIZE 8
+
#define NUM_OF_MME_SBTE_PORTS 5
#define NUM_OF_MME_WB_PORTS 2
@@ -111,13 +107,11 @@
/* DRAM Memory Map */
#define CPU_FW_IMAGE_SIZE 0x10000000 /* 256MB */
-
-/* This define should be used only when working in a debug mode without dram.
- * When working with dram, the driver size will be calculated dynamically.
- */
-#define NIC_DEFAULT_DRV_SIZE 0x20000000 /* 512MB */
-
#define CPU_FW_IMAGE_ADDR DRAM_PHYS_BASE
+#define PMMU_PAGE_TABLES_SIZE 0x10000000 /* 256MB */
+#define EDMA_PQS_SIZE SZ_2M
+#define EDMA_SCRATCHPAD_SIZE SZ_1M
+#define HMMU_PAGE_TABLES_SIZE SZ_1M
#define NIC_NUMBER_OF_PORTS NIC_NUMBER_OF_ENGINES
@@ -140,9 +134,6 @@
#define VA_HOST_SPACE_HPAGE_START 0xFFF0800000000000ull
#define VA_HOST_SPACE_HPAGE_END 0xFFF1000000000000ull /* 140TB */
-#define VA_HOST_SPACE_USER_MAPPED_CB_START 0xFFF1000000000000ull
-#define VA_HOST_SPACE_USER_MAPPED_CB_END 0xFFF1000100000000ull /* 4GB */
-
/* 140TB */
#define VA_HOST_SPACE_PAGE_SIZE (VA_HOST_SPACE_PAGE_END - VA_HOST_SPACE_PAGE_START)
@@ -246,6 +237,18 @@
#define GAUDI2_SOB_INCREMENT_BY_ONE (FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_VAL_MASK, 1) | \
FIELD_PREP(DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_INC_MASK, 1))
+#define GAUDI2_NUM_TESTED_QS (GAUDI2_QUEUE_ID_CPU_PQ - GAUDI2_QUEUE_ID_PDMA_0_0)
+
+
+extern const char *gaudi2_engine_id_str[];
+extern const char *gaudi2_queue_id_str[];
+
+#define GAUDI2_ENG_ID_TO_STR(initiator) ((initiator) >= GAUDI2_ENGINE_ID_SIZE ? "not found" : \
+ gaudi2_engine_id_str[initiator])
+
+#define GAUDI2_QUEUE_ID_TO_STR(initiator) ((initiator) >= GAUDI2_QUEUE_ID_SIZE ? "not found" : \
+ gaudi2_queue_id_str[initiator])
+
enum gaudi2_reserved_sob_id {
GAUDI2_RESERVED_SOB_CS_COMPLETION_FIRST,
GAUDI2_RESERVED_SOB_CS_COMPLETION_LAST =
@@ -390,7 +393,9 @@ enum gaudi2_edma_id {
/* User interrupt count is aligned with HW CQ count.
* We have 64 CQ's per dcore, CQ0 in dcore 0 is reserved for legacy mode
*/
-#define GAUDI2_NUM_USER_INTERRUPTS 255
+#define GAUDI2_NUM_USER_INTERRUPTS 64
+#define GAUDI2_NUM_RESERVED_INTERRUPTS 1
+#define GAUDI2_TOTAL_USER_INTERRUPTS (GAUDI2_NUM_USER_INTERRUPTS + GAUDI2_NUM_RESERVED_INTERRUPTS)
enum gaudi2_irq_num {
GAUDI2_IRQ_NUM_EVENT_QUEUE = GAUDI2_EVENT_QUEUE_MSIX_IDX,
@@ -414,13 +419,17 @@ enum gaudi2_irq_num {
GAUDI2_IRQ_NUM_SHARED_DEC0_ABNRM,
GAUDI2_IRQ_NUM_SHARED_DEC1_NRM,
GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM,
+ GAUDI2_IRQ_NUM_DEC_LAST = GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM,
GAUDI2_IRQ_NUM_COMPLETION,
GAUDI2_IRQ_NUM_NIC_PORT_FIRST,
GAUDI2_IRQ_NUM_NIC_PORT_LAST = (GAUDI2_IRQ_NUM_NIC_PORT_FIRST + NIC_NUMBER_OF_PORTS - 1),
- GAUDI2_IRQ_NUM_RESERVED_FIRST,
- GAUDI2_IRQ_NUM_RESERVED_LAST = (GAUDI2_MSIX_ENTRIES - GAUDI2_NUM_USER_INTERRUPTS - 1),
+ GAUDI2_IRQ_NUM_TPC_ASSERT,
+ GAUDI2_IRQ_NUM_EQ_ERROR,
GAUDI2_IRQ_NUM_USER_FIRST,
GAUDI2_IRQ_NUM_USER_LAST = (GAUDI2_IRQ_NUM_USER_FIRST + GAUDI2_NUM_USER_INTERRUPTS - 1),
+ GAUDI2_IRQ_NUM_RESERVED_FIRST,
+ GAUDI2_IRQ_NUM_RESERVED_LAST = (GAUDI2_MSIX_ENTRIES - GAUDI2_NUM_RESERVED_INTERRUPTS - 1),
+ GAUDI2_IRQ_NUM_UNEXPECTED_ERROR = RESERVED_MSIX_UNEXPECTED_USER_ERROR_INTERRUPT,
GAUDI2_IRQ_NUM_LAST = (GAUDI2_MSIX_ENTRIES - 1)
};
@@ -452,13 +461,23 @@ struct dup_block_ctx {
};
/**
+ * struct gaudi2_queues_test_info - Holds the address of a the messages used for testing the
+ * device queues.
+ * @dma_addr: the address used by the HW for accessing the message.
+ * @kern_addr: The address used by the driver for accessing the message.
+ */
+struct gaudi2_queues_test_info {
+ dma_addr_t dma_addr;
+ void *kern_addr;
+};
+
+/**
* struct gaudi2_device - ASIC specific manage structure.
* @cpucp_info_get: get information on device from CPU-CP
* @mapped_blocks: array that holds the base address and size of all blocks
* the user can map.
* @lfsr_rand_seeds: array of MME ACC random seeds to set.
* @hw_queues_lock: protects the H/W queues from concurrent access.
- * @kdma_lock: protects the KDMA engine from concurrent access.
* @scratchpad_kernel_address: general purpose PAGE_SIZE contiguous memory,
* this memory region should be write-only.
* currently used for HBW QMAN writes which is
@@ -510,9 +529,7 @@ struct dup_block_ctx {
* @flush_db_fifo: flag to force flush DB FIFO after a write.
* @hbm_cfg: HBM subsystem settings
* @hw_queues_lock_mutex: used by simulator instead of hw_queues_lock.
- * @kdma_lock_mutex: used by simulator instead of kdma_lock.
- * @use_deprecated_event_mappings: use old event mappings which are about to be
- * deprecated
+ * @queues_test_info: information used by the driver when testing the HW queues.
*/
struct gaudi2_device {
int (*cpucp_info_get)(struct hl_device *hdev);
@@ -521,7 +538,6 @@ struct gaudi2_device {
int lfsr_rand_seeds[MME_NUM_OF_LFSR_SEEDS];
spinlock_t hw_queues_lock;
- spinlock_t kdma_lock;
void *scratchpad_kernel_address;
dma_addr_t scratchpad_bus_address;
@@ -541,6 +557,44 @@ struct gaudi2_device {
u32 events_stat[GAUDI2_EVENT_SIZE];
u32 events_stat_aggregate[GAUDI2_EVENT_SIZE];
u32 num_of_valid_hw_events;
+
+ /* Queue testing */
+ struct gaudi2_queues_test_info queues_test_info[GAUDI2_NUM_TESTED_QS];
+};
+
+/*
+ * Types of the Gaudi2 IP blocks, used by special blocks iterator.
+ * Required for scenarios where only particular block types can be
+ * addressed (e.g., special PLDM images).
+ */
+enum gaudi2_block_types {
+ GAUDI2_BLOCK_TYPE_PLL,
+ GAUDI2_BLOCK_TYPE_RTR,
+ GAUDI2_BLOCK_TYPE_CPU,
+ GAUDI2_BLOCK_TYPE_HIF,
+ GAUDI2_BLOCK_TYPE_HBM,
+ GAUDI2_BLOCK_TYPE_NIC,
+ GAUDI2_BLOCK_TYPE_PCIE,
+ GAUDI2_BLOCK_TYPE_PCIE_PMA,
+ GAUDI2_BLOCK_TYPE_PDMA,
+ GAUDI2_BLOCK_TYPE_EDMA,
+ GAUDI2_BLOCK_TYPE_PMMU,
+ GAUDI2_BLOCK_TYPE_PSOC,
+ GAUDI2_BLOCK_TYPE_ROT,
+ GAUDI2_BLOCK_TYPE_ARC_FARM,
+ GAUDI2_BLOCK_TYPE_DEC,
+ GAUDI2_BLOCK_TYPE_MME,
+ GAUDI2_BLOCK_TYPE_EU_BIST,
+ GAUDI2_BLOCK_TYPE_SYNC_MNGR,
+ GAUDI2_BLOCK_TYPE_STLB,
+ GAUDI2_BLOCK_TYPE_TPC,
+ GAUDI2_BLOCK_TYPE_HMMU,
+ GAUDI2_BLOCK_TYPE_SRAM,
+ GAUDI2_BLOCK_TYPE_XBAR,
+ GAUDI2_BLOCK_TYPE_KDMA,
+ GAUDI2_BLOCK_TYPE_XDMA,
+ GAUDI2_BLOCK_TYPE_XFT,
+ GAUDI2_BLOCK_TYPE_MAX
};
extern const u32 gaudi2_dma_core_blocks_bases[DMA_CORE_ID_SIZE];
@@ -562,5 +616,6 @@ void gaudi2_pb_print_security_errors(struct hl_device *hdev, u32 block_addr, u32
u32 offended_addr);
int gaudi2_init_security(struct hl_device *hdev);
void gaudi2_ack_protection_bits_errors(struct hl_device *hdev);
+int gaudi2_send_device_activity(struct hl_device *hdev, bool open);
#endif /* GAUDI2P_H_ */
diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_coresight.c b/drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c
index 56c6ab692482..bc3c57bda5cd 100644
--- a/drivers/misc/habanalabs/gaudi2/gaudi2_coresight.c
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c
@@ -5,7 +5,7 @@
* All Rights Reserved.
*/
#include "gaudi2_coresight_regs.h"
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#define GAUDI2_PLDM_CORESIGHT_TIMEOUT_USEC (CORESIGHT_TIMEOUT_USEC * 2000)
#define SPMU_MAX_COUNTERS 6
@@ -151,8 +151,8 @@ static u64 debug_stm_regs[GAUDI2_STM_LAST + 1] = {
[GAUDI2_STM_DCORE3_VDEC1_CS] = mmDCORE3_VDEC1_CS_STM_BASE,
[GAUDI2_STM_PCIE] = mmPCIE_STM_BASE,
[GAUDI2_STM_PSOC] = mmPSOC_STM_BASE,
- [GAUDI2_STM_PSOC_ARC0_CS] = mmPSOC_ARC0_CS_STM_BASE,
- [GAUDI2_STM_PSOC_ARC1_CS] = mmPSOC_ARC1_CS_STM_BASE,
+ [GAUDI2_STM_PSOC_ARC0_CS] = 0,
+ [GAUDI2_STM_PSOC_ARC1_CS] = 0,
[GAUDI2_STM_PDMA0_CS] = mmPDMA0_CS_STM_BASE,
[GAUDI2_STM_PDMA1_CS] = mmPDMA1_CS_STM_BASE,
[GAUDI2_STM_CPU] = mmCPU_STM_BASE,
@@ -293,8 +293,8 @@ static u64 debug_etf_regs[GAUDI2_ETF_LAST + 1] = {
[GAUDI2_ETF_DCORE3_VDEC1_CS] = mmDCORE3_VDEC1_CS_ETF_BASE,
[GAUDI2_ETF_PCIE] = mmPCIE_ETF_BASE,
[GAUDI2_ETF_PSOC] = mmPSOC_ETF_BASE,
- [GAUDI2_ETF_PSOC_ARC0_CS] = mmPSOC_ARC0_CS_ETF_BASE,
- [GAUDI2_ETF_PSOC_ARC1_CS] = mmPSOC_ARC1_CS_ETF_BASE,
+ [GAUDI2_ETF_PSOC_ARC0_CS] = 0,
+ [GAUDI2_ETF_PSOC_ARC1_CS] = 0,
[GAUDI2_ETF_PDMA0_CS] = mmPDMA0_CS_ETF_BASE,
[GAUDI2_ETF_PDMA1_CS] = mmPDMA1_CS_ETF_BASE,
[GAUDI2_ETF_CPU_0] = mmCPU_ETF_0_BASE,
@@ -436,8 +436,8 @@ static u64 debug_funnel_regs[GAUDI2_FUNNEL_LAST + 1] = {
[GAUDI2_FUNNEL_DCORE3_RTR6] = mmDCORE3_RTR6_FUNNEL_BASE,
[GAUDI2_FUNNEL_DCORE3_RTR7] = mmDCORE3_RTR7_FUNNEL_BASE,
[GAUDI2_FUNNEL_PSOC] = mmPSOC_FUNNEL_BASE,
- [GAUDI2_FUNNEL_PSOC_ARC0] = mmPSOC_ARC0_FUNNEL_BASE,
- [GAUDI2_FUNNEL_PSOC_ARC1] = mmPSOC_ARC1_FUNNEL_BASE,
+ [GAUDI2_FUNNEL_PSOC_ARC0] = 0,
+ [GAUDI2_FUNNEL_PSOC_ARC1] = 0,
[GAUDI2_FUNNEL_XDMA] = mmXDMA_FUNNEL_BASE,
[GAUDI2_FUNNEL_CPU] = mmCPU_FUNNEL_BASE,
[GAUDI2_FUNNEL_PMMU] = mmPMMU_FUNNEL_BASE,
@@ -766,10 +766,10 @@ static u64 debug_bmon_regs[GAUDI2_BMON_LAST + 1] = {
[GAUDI2_BMON_PCIE_MSTR_RD] = mmPCIE_BMON_MSTR_RD_BASE,
[GAUDI2_BMON_PCIE_SLV_WR] = mmPCIE_BMON_SLV_WR_BASE,
[GAUDI2_BMON_PCIE_SLV_RD] = mmPCIE_BMON_SLV_RD_BASE,
- [GAUDI2_BMON_PSOC_ARC0_0] = mmPSOC_ARC0_BMON_0_BASE,
- [GAUDI2_BMON_PSOC_ARC0_1] = mmPSOC_ARC0_BMON_1_BASE,
- [GAUDI2_BMON_PSOC_ARC1_0] = mmPSOC_ARC1_BMON_0_BASE,
- [GAUDI2_BMON_PSOC_ARC1_1] = mmPSOC_ARC1_BMON_1_BASE,
+ [GAUDI2_BMON_PSOC_ARC0_0] = 0,
+ [GAUDI2_BMON_PSOC_ARC0_1] = 0,
+ [GAUDI2_BMON_PSOC_ARC1_0] = 0,
+ [GAUDI2_BMON_PSOC_ARC1_1] = 0,
[GAUDI2_BMON_PDMA0_0] = mmPDMA0_BMON_0_BASE,
[GAUDI2_BMON_PDMA0_1] = mmPDMA0_BMON_1_BASE,
[GAUDI2_BMON_PDMA1_0] = mmPDMA1_BMON_0_BASE,
@@ -968,8 +968,8 @@ static u64 debug_spmu_regs[GAUDI2_SPMU_LAST + 1] = {
[GAUDI2_SPMU_DCORE3_VDEC0_CS] = mmDCORE3_VDEC0_CS_SPMU_BASE,
[GAUDI2_SPMU_DCORE3_VDEC1_CS] = mmDCORE3_VDEC1_CS_SPMU_BASE,
[GAUDI2_SPMU_PCIE] = mmPCIE_SPMU_BASE,
- [GAUDI2_SPMU_PSOC_ARC0_CS] = mmPSOC_ARC0_CS_SPMU_BASE,
- [GAUDI2_SPMU_PSOC_ARC1_CS] = mmPSOC_ARC1_CS_SPMU_BASE,
+ [GAUDI2_SPMU_PSOC_ARC0_CS] = 0,
+ [GAUDI2_SPMU_PSOC_ARC1_CS] = 0,
[GAUDI2_SPMU_PDMA0_CS] = mmPDMA0_CS_SPMU_BASE,
[GAUDI2_SPMU_PDMA1_CS] = mmPDMA1_CS_SPMU_BASE,
[GAUDI2_SPMU_PMMU_CS] = mmPMMU_CS_SPMU_BASE,
@@ -2092,6 +2092,11 @@ static int gaudi2_config_etf(struct hl_device *hdev, struct hl_debug_params *par
if (rc)
return -EIO;
+ val = RREG32(base_reg + mmETF_CTL_OFFSET);
+
+ if ((!params->enable && val == 0x0) || (params->enable && val != 0x0))
+ return 0;
+
val = RREG32(base_reg + mmETF_FFCR_OFFSET);
val |= 0x1000;
WREG32(base_reg + mmETF_FFCR_OFFSET, val);
@@ -2120,10 +2125,17 @@ static int gaudi2_config_etf(struct hl_device *hdev, struct hl_debug_params *par
if (!input)
return -EINVAL;
+ val = RREG32(base_reg + mmETF_RSZ_OFFSET) << 2;
+ if (val) {
+ val = ffs(val);
+ WREG32(base_reg + mmETF_PSCR_OFFSET, val);
+ } else {
+ WREG32(base_reg + mmETF_PSCR_OFFSET, 0x10);
+ }
+
WREG32(base_reg + mmETF_BUFWM_OFFSET, 0x3FFC);
WREG32(base_reg + mmETF_MODE_OFFSET, input->sink_mode);
WREG32(base_reg + mmETF_FFCR_OFFSET, 0x4001);
- WREG32(base_reg + mmETF_PSCR_OFFSET, 0x10);
WREG32(base_reg + mmETF_CTL_OFFSET, 1);
} else {
WREG32(base_reg + mmETF_BUFWM_OFFSET, 0);
@@ -2189,6 +2201,11 @@ static int gaudi2_config_etr(struct hl_device *hdev, struct hl_ctx *ctx,
if (rc)
return -EIO;
+ val = RREG32(mmPSOC_ETR_CTL);
+
+ if ((!params->enable && val == 0x0) || (params->enable && val != 0x0))
+ return 0;
+
val = RREG32(mmPSOC_ETR_FFCR);
val |= 0x1000;
WREG32(mmPSOC_ETR_FFCR, val);
@@ -2376,10 +2393,10 @@ static int gaudi2_config_bmon(struct hl_device *hdev, struct hl_debug_params *pa
WREG32(base_reg + mmBMON_ADDRH_S2_OFFSET, upper_32_bits(input->start_addr2));
WREG32(base_reg + mmBMON_ADDRL_E2_OFFSET, lower_32_bits(input->end_addr2));
WREG32(base_reg + mmBMON_ADDRH_E2_OFFSET, upper_32_bits(input->end_addr2));
- WREG32(base_reg + mmBMON_ADDRL_S3_OFFSET, lower_32_bits(input->start_addr2));
- WREG32(base_reg + mmBMON_ADDRH_S3_OFFSET, upper_32_bits(input->start_addr2));
- WREG32(base_reg + mmBMON_ADDRL_E3_OFFSET, lower_32_bits(input->end_addr2));
- WREG32(base_reg + mmBMON_ADDRH_E3_OFFSET, upper_32_bits(input->end_addr2));
+ WREG32(base_reg + mmBMON_ADDRL_S3_OFFSET, lower_32_bits(input->start_addr3));
+ WREG32(base_reg + mmBMON_ADDRH_S3_OFFSET, upper_32_bits(input->start_addr3));
+ WREG32(base_reg + mmBMON_ADDRL_E3_OFFSET, lower_32_bits(input->end_addr3));
+ WREG32(base_reg + mmBMON_ADDRH_E3_OFFSET, upper_32_bits(input->end_addr3));
WREG32(base_reg + mmBMON_IDL_OFFSET, 0x0);
WREG32(base_reg + mmBMON_IDH_OFFSET, 0x0);
@@ -2409,7 +2426,7 @@ static int gaudi2_config_bmon(struct hl_device *hdev, struct hl_debug_params *pa
WREG32(base_reg + mmBMON_ADDRH_E3_OFFSET, 0);
WREG32(base_reg + mmBMON_REDUCTION_OFFSET, 0);
WREG32(base_reg + mmBMON_STM_TRC_OFFSET, 0x7 | (0xA << 8));
- WREG32(base_reg + mmBMON_CR_OFFSET, 0x77 | 0xf << 24);
+ WREG32(base_reg + mmBMON_CR_OFFSET, 0x41);
}
return 0;
@@ -2483,7 +2500,8 @@ static int gaudi2_config_spmu(struct hl_device *hdev, struct hl_debug_params *pa
* set enabled events mask based on input->event_types_num
*/
event_mask = 0x80000000;
- event_mask |= GENMASK(input->event_types_num, 0);
+ if (input->event_types_num)
+ event_mask |= GENMASK(input->event_types_num - 1, 0);
WREG32(base_reg + mmSPMU_PMCNTENSET_EL0_OFFSET, event_mask);
} else {
@@ -2657,7 +2675,7 @@ int gaudi2_coresight_init(struct hl_device *hdev)
/*
* Mask out all the disabled binned offsets.
* so when user request to configure a binned or masked out component,
- * driver will ignore programing it ( happens when offset value is set to 0x0 )
+ * driver will ignore programming it ( happens when offset value is set to 0x0 )
* this is being set in gaudi2_coresight_set_disabled_components
*/
diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_coresight_regs.h b/drivers/accel/habanalabs/gaudi2/gaudi2_coresight_regs.h
index df8729286e06..df8729286e06 100644
--- a/drivers/misc/habanalabs/gaudi2/gaudi2_coresight_regs.h
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2_coresight_regs.h
diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h b/drivers/accel/habanalabs/gaudi2/gaudi2_masks.h
index eed16d642a5a..e6664c4a2cf5 100644
--- a/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2_masks.h
@@ -51,12 +51,18 @@
(0x1F << PDMA0_QM_GLBL_CFG0_CP_EN_SHIFT) | \
(0x1 << PDMA0_QM_GLBL_CFG0_ARC_CQF_EN_SHIFT))
-#define PDMA1_QMAN_ENABLE \
+#define PDMA0_QMAN_ENABLE \
((0x3 << PDMA0_QM_GLBL_CFG0_PQF_EN_SHIFT) | \
(0x1F << PDMA0_QM_GLBL_CFG0_CQF_EN_SHIFT) | \
(0x1F << PDMA0_QM_GLBL_CFG0_CP_EN_SHIFT) | \
(0x1 << PDMA0_QM_GLBL_CFG0_ARC_CQF_EN_SHIFT))
+#define PDMA1_QMAN_ENABLE \
+ ((0x1 << PDMA0_QM_GLBL_CFG0_PQF_EN_SHIFT) | \
+ (0x1F << PDMA0_QM_GLBL_CFG0_CQF_EN_SHIFT) | \
+ (0x1F << PDMA0_QM_GLBL_CFG0_CP_EN_SHIFT) | \
+ (0x1 << PDMA0_QM_GLBL_CFG0_ARC_CQF_EN_SHIFT))
+
/* QM_IDLE_MASK is valid for all engines QM idle check */
#define QM_IDLE_MASK (DCORE0_EDMA0_QM_GLBL_STS0_PQF_IDLE_MASK | \
DCORE0_EDMA0_QM_GLBL_STS0_CQF_IDLE_MASK | \
@@ -73,7 +79,6 @@
DCORE0_MME_CTRL_LO_ARCH_STATUS_QM_RDY_MASK)
#define TPC_IDLE_MASK (DCORE0_TPC0_CFG_STATUS_SCALAR_PIPE_EMPTY_MASK | \
- DCORE0_TPC0_CFG_STATUS_VECTOR_PIPE_EMPTY_MASK | \
DCORE0_TPC0_CFG_STATUS_IQ_EMPTY_MASK | \
DCORE0_TPC0_CFG_STATUS_SB_EMPTY_MASK | \
DCORE0_TPC0_CFG_STATUS_QM_IDLE_MASK | \
@@ -81,6 +86,8 @@
#define DCORE0_TPC0_QM_CGM_STS_AGENT_IDLE_MASK 0x100
+#define DCORE0_TPC0_EML_CFG_DBG_CNT_DBG_EXIT_MASK 0x40
+
/* CGM_IDLE_MASK is valid for all engines CGM idle check */
#define CGM_IDLE_MASK DCORE0_TPC0_QM_CGM_STS_AGENT_IDLE_MASK
@@ -138,4 +145,17 @@
#define DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_SIGN_SHIFT 15
#define DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_SIGN_MASK 0x8000
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_SHIFT 0
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_MASK 0x1
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_SHIFT 1
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK 0x2
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_SHIFT 2
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK 0x4
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_MASK_SHIFT 3
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_MASK_MASK 0x8
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK_SHIFT 4
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK_MASK 0x10
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK_SHIFT 5
+#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK_MASK 0x20
+
#endif /* GAUDI2_MASKS_H_ */
diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_security.c b/drivers/accel/habanalabs/gaudi2/gaudi2_security.c
index 89a06ff5ba34..307ccb912ccd 100644
--- a/drivers/misc/habanalabs/gaudi2/gaudi2_security.c
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2_security.c
@@ -479,6 +479,8 @@ static const u32 gaudi2_pb_dcr0_edma0_unsecured_regs[] = {
mmDCORE0_EDMA0_CORE_CTX_TE_NUMROWS,
mmDCORE0_EDMA0_CORE_CTX_IDX,
mmDCORE0_EDMA0_CORE_CTX_IDX_INC,
+ mmDCORE0_EDMA0_CORE_WR_COMP_MAX_OUTSTAND,
+ mmDCORE0_EDMA0_CORE_RD_LBW_RATE_LIM_CFG,
mmDCORE0_EDMA0_QM_CQ_CFG0_0,
mmDCORE0_EDMA0_QM_CQ_CFG0_1,
mmDCORE0_EDMA0_QM_CQ_CFG0_2,
@@ -1533,6 +1535,10 @@ static const u32 gaudi2_pb_dcr0_tpc0_unsecured_regs[] = {
mmDCORE0_TPC0_CFG_QM_KERNEL_CONFIG,
mmDCORE0_TPC0_CFG_QM_KERNEL_ID,
mmDCORE0_TPC0_CFG_QM_POWER_LOOP,
+ mmDCORE0_TPC0_CFG_TSB_CFG_MTRR_2_0,
+ mmDCORE0_TPC0_CFG_TSB_CFG_MTRR_2_1,
+ mmDCORE0_TPC0_CFG_TSB_CFG_MTRR_2_2,
+ mmDCORE0_TPC0_CFG_TSB_CFG_MTRR_2_3,
mmDCORE0_TPC0_CFG_LUT_FUNC32_BASE2_ADDR_LO,
mmDCORE0_TPC0_CFG_LUT_FUNC32_BASE2_ADDR_HI,
mmDCORE0_TPC0_CFG_LUT_FUNC64_BASE2_ADDR_LO,
@@ -1541,6 +1547,7 @@ static const u32 gaudi2_pb_dcr0_tpc0_unsecured_regs[] = {
mmDCORE0_TPC0_CFG_LUT_FUNC128_BASE2_ADDR_HI,
mmDCORE0_TPC0_CFG_LUT_FUNC256_BASE2_ADDR_LO,
mmDCORE0_TPC0_CFG_LUT_FUNC256_BASE2_ADDR_HI,
+ mmDCORE0_TPC0_CFG_FP8_143_BIAS,
mmDCORE0_TPC0_CFG_ROUND_CSR,
mmDCORE0_TPC0_CFG_CONV_ROUND_CSR,
mmDCORE0_TPC0_CFG_SEMAPHORE,
@@ -1561,6 +1568,7 @@ static const u32 gaudi2_pb_dcr0_tpc0_unsecured_regs[] = {
mmDCORE0_TPC0_CFG_LUT_FUNC128_BASE_ADDR_HI,
mmDCORE0_TPC0_CFG_LUT_FUNC256_BASE_ADDR_LO,
mmDCORE0_TPC0_CFG_LUT_FUNC256_BASE_ADDR_HI,
+ mmDCORE0_TPC0_CFG_KERNEL_KERNEL_CONFIG,
mmDCORE0_TPC0_CFG_KERNEL_SRF_0,
mmDCORE0_TPC0_CFG_KERNEL_SRF_1,
mmDCORE0_TPC0_CFG_KERNEL_SRF_2,
@@ -1594,6 +1602,8 @@ static const u32 gaudi2_pb_dcr0_tpc0_unsecured_regs[] = {
mmDCORE0_TPC0_CFG_KERNEL_SRF_30,
mmDCORE0_TPC0_CFG_KERNEL_SRF_31,
mmDCORE0_TPC0_CFG_TPC_SB_L0CD,
+ mmDCORE0_TPC0_CFG_TPC_COUNT,
+ mmDCORE0_TPC0_CFG_TPC_ID,
mmDCORE0_TPC0_CFG_QM_KERNEL_ID_INC,
mmDCORE0_TPC0_CFG_QM_TID_BASE_SIZE_HIGH_DIM_0,
mmDCORE0_TPC0_CFG_QM_TID_BASE_SIZE_HIGH_DIM_1,
@@ -1666,6 +1676,10 @@ static const u32 gaudi2_pb_dcr0_sm_glbl[] = {
mmDCORE0_SYNC_MNGR_GLBL_BASE,
};
+static const u32 gaudi2_pb_dcr1_sm_glbl[] = {
+ mmDCORE1_SYNC_MNGR_GLBL_BASE,
+};
+
static const struct range gaudi2_pb_dcr0_sm_glbl_unsecured_regs[] = {
{mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_1, mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_63},
{mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_1, mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_63},
@@ -1678,14 +1692,14 @@ static const struct range gaudi2_pb_dcr0_sm_glbl_unsecured_regs[] = {
};
static const struct range gaudi2_pb_dcr_x_sm_glbl_unsecured_regs[] = {
- {mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_0, mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_63},
- {mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_0, mmDCORE0_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_63},
- {mmDCORE0_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_0, mmDCORE0_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_63},
- {mmDCORE0_SYNC_MNGR_GLBL_CQ_PI_0, mmDCORE0_SYNC_MNGR_GLBL_CQ_PI_63},
- {mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_0, mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_L_63},
- {mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_0, mmDCORE0_SYNC_MNGR_GLBL_LBW_ADDR_H_63},
- {mmDCORE0_SYNC_MNGR_GLBL_LBW_DATA_0, mmDCORE0_SYNC_MNGR_GLBL_LBW_DATA_63},
- {mmDCORE0_SYNC_MNGR_GLBL_CQ_INC_MODE_0, mmDCORE0_SYNC_MNGR_GLBL_CQ_INC_MODE_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_0, mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_0, mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_0, mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_0, mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_0, mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_0, mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_0, mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_63},
+ {mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_0, mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_63},
};
static const u32 gaudi2_pb_arc_sched[] = {
@@ -1764,6 +1778,7 @@ static const struct range gaudi2_pb_nic0_qm_arc_aux0_unsecured_regs[] = {
{mmNIC0_QM_ARC_AUX0_CLUSTER_NUM, mmNIC0_QM_ARC_AUX0_WAKE_UP_EVENT},
{mmNIC0_QM_ARC_AUX0_ARC_RST_REQ, mmNIC0_QM_ARC_AUX0_CID_OFFSET_7},
{mmNIC0_QM_ARC_AUX0_SCRATCHPAD_0, mmNIC0_QM_ARC_AUX0_INFLIGHT_LBU_RD_CNT},
+ {mmNIC0_QM_ARC_AUX0_CBU_EARLY_BRESP_EN, mmNIC0_QM_ARC_AUX0_CBU_EARLY_BRESP_EN},
{mmNIC0_QM_ARC_AUX0_LBU_EARLY_BRESP_EN, mmNIC0_QM_ARC_AUX0_LBU_EARLY_BRESP_EN},
{mmNIC0_QM_ARC_AUX0_DCCM_QUEUE_BASE_ADDR_0, mmNIC0_QM_ARC_AUX0_DCCM_QUEUE_ALERT_MSG},
{mmNIC0_QM_ARC_AUX0_DCCM_Q_PUSH_FIFO_CNT, mmNIC0_QM_ARC_AUX0_QMAN_ARC_CQ_SHADOW_CI},
@@ -2559,6 +2574,10 @@ static const u32 gaudi2_pb_pcie[] = {
mmPCIE_WRAP_BASE,
};
+static const u32 gaudi2_pb_pcie_unsecured_regs[] = {
+ mmPCIE_WRAP_SPECIAL_GLBL_SPARE_0,
+};
+
static const u32 gaudi2_pb_thermal_sensor0[] = {
mmDCORE0_XFT_BASE,
mmDCORE0_TSTDVS_BASE,
@@ -2583,9 +2602,9 @@ struct gaudi2_tpc_pb_data {
};
static void gaudi2_config_tpcs_glbl_sec(struct hl_device *hdev, int dcore, int inst, u32 offset,
- void *data)
+ struct iterate_module_ctx *ctx)
{
- struct gaudi2_tpc_pb_data *pb_data = (struct gaudi2_tpc_pb_data *)data;
+ struct gaudi2_tpc_pb_data *pb_data = ctx->data;
hl_config_glbl_sec(hdev, gaudi2_pb_dcr0_tpc0, pb_data->glbl_sec,
offset, pb_data->block_array_size);
@@ -2660,15 +2679,14 @@ static int gaudi2_init_pb_tpc(struct hl_device *hdev)
struct gaudi2_tpc_arc_pb_data {
u32 unsecured_regs_arr_size;
u32 arc_regs_arr_size;
- int rc;
};
static void gaudi2_config_tpcs_pb_ranges(struct hl_device *hdev, int dcore, int inst, u32 offset,
- void *data)
+ struct iterate_module_ctx *ctx)
{
- struct gaudi2_tpc_arc_pb_data *pb_data = (struct gaudi2_tpc_arc_pb_data *)data;
+ struct gaudi2_tpc_arc_pb_data *pb_data = ctx->data;
- pb_data->rc |= hl_init_pb_ranges(hdev, HL_PB_SHARED, HL_PB_NA, 1,
+ ctx->rc = hl_init_pb_ranges(hdev, HL_PB_SHARED, HL_PB_NA, 1,
offset, gaudi2_pb_dcr0_tpc0_arc,
pb_data->arc_regs_arr_size,
gaudi2_pb_dcr0_tpc0_arc_unsecured_regs,
@@ -2683,12 +2701,12 @@ static int gaudi2_init_pb_tpc_arc(struct hl_device *hdev)
tpc_arc_pb_data.arc_regs_arr_size = ARRAY_SIZE(gaudi2_pb_dcr0_tpc0_arc);
tpc_arc_pb_data.unsecured_regs_arr_size =
ARRAY_SIZE(gaudi2_pb_dcr0_tpc0_arc_unsecured_regs);
- tpc_arc_pb_data.rc = 0;
+
tpc_iter.fn = &gaudi2_config_tpcs_pb_ranges;
tpc_iter.data = &tpc_arc_pb_data;
gaudi2_iterate_tpcs(hdev, &tpc_iter);
- return tpc_arc_pb_data.rc;
+ return tpc_iter.rc;
}
static int gaudi2_init_pb_sm_objs(struct hl_device *hdev)
@@ -2891,7 +2909,7 @@ static void gaudi2_init_lbw_range_registers_secure(struct hl_device *hdev)
* - range 11: NIC11_CFG + *_DBG (not including TPC_DBG)
*
* If F/W security is not enabled:
- * - ranges 12,13: PSOC_CFG (excluding PSOC_TIMESTAMP)
+ * - ranges 12,13: PSOC_CFG (excluding PSOC_TIMESTAMP, PSOC_EFUSE and PSOC_GLOBAL_CONF)
*/
u64 lbw_range_min_short[] = {
mmNIC0_TX_AXUSER_BASE,
@@ -2907,7 +2925,7 @@ static void gaudi2_init_lbw_range_registers_secure(struct hl_device *hdev)
mmNIC10_TX_AXUSER_BASE,
mmNIC11_TX_AXUSER_BASE,
mmPSOC_I2C_M0_BASE,
- mmPSOC_EFUSE_BASE
+ mmPSOC_GPIO0_BASE
};
u64 lbw_range_max_short[] = {
mmNIC0_MAC_CH3_MAC_PCS_BASE + HL_BLOCK_SIZE,
@@ -3203,6 +3221,7 @@ static void gaudi2_init_range_registers(struct hl_device *hdev)
*/
static int gaudi2_init_protection_bits(struct hl_device *hdev)
{
+ u32 *user_regs_array = NULL, user_regs_array_size = 0, engine_core_intr_reg;
struct asic_fixed_properties *prop = &hdev->asic_prop;
u32 instance_offset;
int rc = 0;
@@ -3354,14 +3373,6 @@ static int gaudi2_init_protection_bits(struct hl_device *hdev)
/* Sync Manager GLBL */
- /* Unsecure all CQ registers */
- rc |= hl_init_pb_ranges(hdev, NUM_OF_DCORES, DCORE_OFFSET,
- HL_PB_SINGLE_INSTANCE, HL_PB_NA,
- gaudi2_pb_dcr0_sm_glbl,
- ARRAY_SIZE(gaudi2_pb_dcr0_sm_glbl),
- gaudi2_pb_dcr_x_sm_glbl_unsecured_regs,
- ARRAY_SIZE(gaudi2_pb_dcr_x_sm_glbl_unsecured_regs));
-
/* Secure Dcore0 CQ0 registers */
rc |= hl_init_pb_ranges(hdev, HL_PB_SHARED, HL_PB_NA,
HL_PB_SINGLE_INSTANCE, HL_PB_NA,
@@ -3370,14 +3381,35 @@ static int gaudi2_init_protection_bits(struct hl_device *hdev)
gaudi2_pb_dcr0_sm_glbl_unsecured_regs,
ARRAY_SIZE(gaudi2_pb_dcr0_sm_glbl_unsecured_regs));
+ /* Unsecure all other CQ registers */
+ rc |= hl_init_pb_ranges(hdev, NUM_OF_DCORES - 1, DCORE_OFFSET,
+ HL_PB_SINGLE_INSTANCE, HL_PB_NA,
+ gaudi2_pb_dcr1_sm_glbl,
+ ARRAY_SIZE(gaudi2_pb_dcr1_sm_glbl),
+ gaudi2_pb_dcr_x_sm_glbl_unsecured_regs,
+ ARRAY_SIZE(gaudi2_pb_dcr_x_sm_glbl_unsecured_regs));
+
/* PSOC.
* Except for PSOC_GLOBAL_CONF, skip when security is enabled in F/W, because the blocks are
* protected by privileged RR.
+ * For PSOC_GLOBAL_CONF, need to un-secure the scratchpad register which is used for engine
+ * cores to raise events towards F/W.
*/
+ engine_core_intr_reg = (u32) (hdev->asic_prop.engine_core_interrupt_reg_addr - CFG_BASE);
+ if (engine_core_intr_reg >= mmPSOC_GLOBAL_CONF_SCRATCHPAD_0 &&
+ engine_core_intr_reg <= mmPSOC_GLOBAL_CONF_SCRATCHPAD_31) {
+ user_regs_array = &engine_core_intr_reg;
+ user_regs_array_size = 1;
+ } else {
+ dev_err(hdev->dev,
+ "Engine cores register for interrupts (%#x) is not a PSOC scratchpad register\n",
+ engine_core_intr_reg);
+ }
+
rc |= hl_init_pb(hdev, HL_PB_SHARED, HL_PB_NA,
HL_PB_SINGLE_INSTANCE, HL_PB_NA,
gaudi2_pb_psoc_global_conf, ARRAY_SIZE(gaudi2_pb_psoc_global_conf),
- NULL, HL_PB_NA);
+ user_regs_array, user_regs_array_size);
if (!hdev->asic_prop.fw_security_enabled)
rc |= hl_init_pb(hdev, HL_PB_SHARED, HL_PB_NA,
@@ -3419,7 +3451,8 @@ static int gaudi2_init_protection_bits(struct hl_device *hdev)
rc |= hl_init_pb(hdev, HL_PB_SHARED, HL_PB_NA,
HL_PB_SINGLE_INSTANCE, HL_PB_NA,
gaudi2_pb_pcie, ARRAY_SIZE(gaudi2_pb_pcie),
- NULL, HL_PB_NA);
+ gaudi2_pb_pcie_unsecured_regs,
+ ARRAY_SIZE(gaudi2_pb_pcie_unsecured_regs));
/* Thermal Sensor.
* Skip when security is enabled in F/W, because the blocks are protected by privileged RR.
@@ -3431,15 +3464,6 @@ static int gaudi2_init_protection_bits(struct hl_device *hdev)
ARRAY_SIZE(gaudi2_pb_thermal_sensor0), NULL, HL_PB_NA);
}
- /* HBM */
- /* Temporarily skip until SW-63348 is solved
- * instance_offset = mmHBM1_MC0_BASE - mmHBM0_MC0_BASE;
- * rc |= hl_init_pb_with_mask(hdev, HL_PB_SHARED, HL_PB_NA, GAUDI2_HBM_NUM,
- * instance_offset, gaudi2_pb_hbm,
- * ARRAY_SIZE(gaudi2_pb_hbm), NULL, HL_PB_NA,
- * prop->dram_enabled_mask);
- */
-
/* Scheduler ARCs */
instance_offset = mmARC_FARM_ARC1_AUX_BASE - mmARC_FARM_ARC0_AUX_BASE;
rc |= hl_init_pb_ranges(hdev, HL_PB_SHARED, HL_PB_NA,
@@ -3547,9 +3571,9 @@ struct gaudi2_ack_pb_tpc_data {
};
static void gaudi2_ack_pb_tpc_config(struct hl_device *hdev, int dcore, int inst, u32 offset,
- void *data)
+ struct iterate_module_ctx *ctx)
{
- struct gaudi2_ack_pb_tpc_data *pb_data = (struct gaudi2_ack_pb_tpc_data *)data;
+ struct gaudi2_ack_pb_tpc_data *pb_data = ctx->data;
hl_ack_pb_single_dcore(hdev, offset, HL_PB_SINGLE_INSTANCE, HL_PB_NA,
gaudi2_pb_dcr0_tpc0, pb_data->tpc_regs_array_size);
diff --git a/drivers/misc/habanalabs/goya/Makefile b/drivers/accel/habanalabs/goya/Makefile
index b3f3b7b96683..b3f3b7b96683 100644
--- a/drivers/misc/habanalabs/goya/Makefile
+++ b/drivers/accel/habanalabs/goya/Makefile
diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/accel/habanalabs/goya/goya.c
index db4487c33582..84768e306269 100644
--- a/drivers/misc/habanalabs/goya/goya.c
+++ b/drivers/accel/habanalabs/goya/goya.c
@@ -413,8 +413,6 @@ int goya_set_fixed_properties(struct hl_device *hdev)
else
prop->mmu_pgt_size = MMU_PAGE_TABLES_SIZE;
prop->mmu_pte_size = HL_PTE_SIZE;
- prop->mmu_hop_table_size = HOP_TABLE_SIZE_512_PTE;
- prop->mmu_hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE;
prop->dram_page_size = PAGE_SIZE_2MB;
prop->device_mem_alloc_default_page_size = prop->dram_page_size;
prop->dram_supports_virtual_memory = true;
@@ -435,8 +433,8 @@ int goya_set_fixed_properties(struct hl_device *hdev)
prop->dmmu.num_hops = MMU_ARCH_5_HOPS;
prop->dmmu.last_mask = LAST_MASK;
/* TODO: will be duplicated until implementing per-MMU props */
- prop->dmmu.hop_table_size = prop->mmu_hop_table_size;
- prop->dmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size;
+ prop->dmmu.hop_table_size = HOP_TABLE_SIZE_512_PTE;
+ prop->dmmu.hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE;
/* shifts and masks are the same in PMMU and DMMU */
memcpy(&prop->pmmu, &prop->dmmu, sizeof(prop->dmmu));
@@ -446,8 +444,8 @@ int goya_set_fixed_properties(struct hl_device *hdev)
prop->pmmu.num_hops = MMU_ARCH_5_HOPS;
prop->pmmu.last_mask = LAST_MASK;
/* TODO: will be duplicated until implementing per-MMU props */
- prop->pmmu.hop_table_size = prop->mmu_hop_table_size;
- prop->pmmu.hop0_tables_total_size = prop->mmu_hop0_tables_total_size;
+ prop->pmmu.hop_table_size = HOP_TABLE_SIZE_512_PTE;
+ prop->pmmu.hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE;
/* PMMU and HPMMU are the same except of page size */
memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu));
@@ -466,12 +464,14 @@ int goya_set_fixed_properties(struct hl_device *hdev)
prop->pcie_dbi_base_address = mmPCIE_DBI_BASE;
prop->pcie_aux_dbi_reg_addr = CFG_BASE + mmPCIE_AUX_DBI;
- strncpy(prop->cpucp_info.card_name, GOYA_DEFAULT_CARD_NAME,
+ strscpy_pad(prop->cpucp_info.card_name, GOYA_DEFAULT_CARD_NAME,
CARD_NAME_MAX_LEN);
prop->max_pending_cs = GOYA_MAX_PENDING_CS;
prop->first_available_user_interrupt = USHRT_MAX;
+ prop->tpc_interrupt_id = USHRT_MAX;
+ prop->eq_interrupt_id = GOYA_EVENT_QUEUE_MSIX_IDX;
for (i = 0 ; i < HL_MAX_DCORES ; i++)
prop->first_available_cq[i] = USHRT_MAX;
@@ -668,13 +668,18 @@ pci_init:
rc = hl_fw_read_preboot_status(hdev);
if (rc) {
if (hdev->reset_on_preboot_fail)
+ /* we are already on failure flow, so don't check if hw_fini fails. */
hdev->asic_funcs->hw_fini(hdev, true, false);
goto pci_fini;
}
if (goya_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
dev_dbg(hdev->dev, "H/W state is dirty, must reset before initializing\n");
- hdev->asic_funcs->hw_fini(hdev, true, false);
+ rc = hdev->asic_funcs->hw_fini(hdev, true, false);
+ if (rc) {
+ dev_err(hdev->dev, "failed to reset HW in dirty state (%d)\n", rc);
+ goto pci_fini;
+ }
}
if (!hdev->pldm) {
@@ -888,11 +893,8 @@ int goya_late_init(struct hl_device *hdev)
WREG32(mmMMU_LOG2_DDR_SIZE, ilog2(prop->dram_size));
rc = hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_ENABLE_PCI_ACCESS, 0x0);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to enable PCI access from CPU %d\n", rc);
+ if (rc)
return rc;
- }
/* force setting to low frequency */
goya->curr_pll_profile = PLL_LOW;
@@ -916,26 +918,11 @@ int goya_late_init(struct hl_device *hdev)
*/
void goya_late_fini(struct hl_device *hdev)
{
- const struct hwmon_channel_info **channel_info_arr;
struct goya_device *goya = hdev->asic_specific;
- int i = 0;
cancel_delayed_work_sync(&goya->goya_work->work_freq);
- if (!hdev->hl_chip_info->info)
- return;
-
- channel_info_arr = hdev->hl_chip_info->info;
-
- while (channel_info_arr[i]) {
- kfree(channel_info_arr[i]->config);
- kfree(channel_info_arr[i]);
- i++;
- }
-
- kfree(channel_info_arr);
-
- hdev->hl_chip_info->info = NULL;
+ hl_hwmon_release_resources(hdev);
}
static void goya_set_pci_memory_regions(struct hl_device *hdev)
@@ -1040,6 +1027,7 @@ static int goya_sw_init(struct hl_device *hdev)
hdev->asic_prop.supports_compute_reset = true;
hdev->asic_prop.allow_inference_soft_reset = true;
hdev->supports_wait_for_multi_cs = false;
+ hdev->supports_ctx_switch = true;
hdev->asic_funcs->set_pci_memory_regions(hdev);
@@ -2678,9 +2666,6 @@ int goya_mmu_init(struct hl_device *hdev)
u64 hop0_addr;
int rc, i;
- if (!hdev->mmu_enable)
- return 0;
-
if (goya->hw_cap_initialized & HW_CAP_MMU)
return 0;
@@ -2688,7 +2673,7 @@ int goya_mmu_init(struct hl_device *hdev)
for (i = 0 ; i < prop->max_asid ; i++) {
hop0_addr = prop->mmu_pgt_addr +
- (i * prop->mmu_hop_table_size);
+ (i * prop->dmmu.hop_table_size);
rc = goya_mmu_update_asid_hop0_addr(hdev, i, hop0_addr);
if (rc) {
@@ -2796,7 +2781,7 @@ disable_queues:
return rc;
}
-static void goya_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
+static int goya_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
{
struct goya_device *goya = hdev->asic_specific;
u32 reset_timeout_ms, cpu_timeout_ms, status;
@@ -2842,17 +2827,17 @@ static void goya_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
msleep(reset_timeout_ms);
status = RREG32(mmPSOC_GLOBAL_CONF_BTM_FSM);
- if (status & PSOC_GLOBAL_CONF_BTM_FSM_STATE_MASK)
- dev_err(hdev->dev,
- "Timeout while waiting for device to reset 0x%x\n",
- status);
+ if (status & PSOC_GLOBAL_CONF_BTM_FSM_STATE_MASK) {
+ dev_err(hdev->dev, "Timeout while waiting for device to reset 0x%x\n", status);
+ return -ETIMEDOUT;
+ }
if (!hard_reset && goya) {
goya->hw_cap_initialized &= ~(HW_CAP_DMA | HW_CAP_MME |
HW_CAP_GOLDEN | HW_CAP_TPC);
WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR,
GOYA_ASYNC_EVENT_ID_SOFT_RESET);
- return;
+ return 0;
}
/* Chicken bit to re-initiate boot sequencer flow */
@@ -2871,17 +2856,12 @@ static void goya_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset)
memset(goya->events_stat, 0, sizeof(goya->events_stat));
}
+ return 0;
}
int goya_suspend(struct hl_device *hdev)
{
- int rc;
-
- rc = hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
- if (rc)
- dev_err(hdev->dev, "Failed to disable PCI access from CPU\n");
-
- return rc;
+ return hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
}
int goya_resume(struct hl_device *hdev)
@@ -2894,8 +2874,8 @@ static int goya_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
{
int rc;
- vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
- VM_DONTCOPY | VM_NORESERVE;
+ vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
+ VM_DONTCOPY | VM_NORESERVE);
rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr,
(dma_addr - HOST_PHYS_BASE), size);
@@ -3367,7 +3347,7 @@ static int goya_pin_memory_before_cs(struct hl_device *hdev,
list_add_tail(&userptr->job_node, parser->job_userptr_list);
- rc = hdev->asic_funcs->asic_dma_map_sgtable(hdev, userptr->sgt, dir);
+ rc = hl_dma_map_sgtable(hdev, userptr->sgt, dir);
if (rc) {
dev_err(hdev->dev, "failed to map sgt with DMA region\n");
goto unpin_memory;
@@ -4489,8 +4469,8 @@ static void goya_print_out_of_sync_info(struct hl_device *hdev,
{
struct hl_hw_queue *q = &hdev->kernel_queues[GOYA_QUEUE_ID_CPU_PQ];
- dev_err(hdev->dev, "Out of sync with FW, FW: pi=%u, ci=%u, LKD: pi=%u, ci=%u\n",
- sync_err->pi, sync_err->ci, q->pi, atomic_read(&q->ci));
+ dev_err(hdev->dev, "Out of sync with FW, FW: pi=%u, ci=%u, LKD: pi=%u, ci=%d\n",
+ le32_to_cpu(sync_err->pi), le32_to_cpu(sync_err->ci), q->pi, atomic_read(&q->ci));
}
static void goya_print_irq_info(struct hl_device *hdev, u16 event_type,
@@ -4559,7 +4539,7 @@ static int goya_unmask_irq_arr(struct hl_device *hdev, u32 *irq_arr,
return rc;
}
-static int goya_non_hard_reset_late_init(struct hl_device *hdev)
+static int goya_compute_reset_late_init(struct hl_device *hdev)
{
/*
* Unmask all IRQs since some could have been received
@@ -5131,14 +5111,14 @@ int goya_cpucp_info_get(struct hl_device *hdev)
}
if (!strlen(prop->cpucp_info.card_name))
- strncpy(prop->cpucp_info.card_name, GOYA_DEFAULT_CARD_NAME,
+ strscpy_pad(prop->cpucp_info.card_name, GOYA_DEFAULT_CARD_NAME,
CARD_NAME_MAX_LEN);
return 0;
}
-static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
- u8 mask_len, struct seq_file *s)
+static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
+ struct engines_data *e)
{
const char *fmt = "%-5d%-9s%#-14x%#-16x%#x\n";
const char *dma_fmt = "%-5d%-9s%#-14x%#x\n";
@@ -5149,9 +5129,9 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
u64 offset;
int i;
- if (s)
- seq_puts(s, "\nDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0\n"
- "--- ------- ------------ -------------\n");
+ if (e)
+ hl_engine_data_sprintf(e, "\nDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0\n"
+ "--- ------- ------------ -------------\n");
offset = mmDMA_QM_1_GLBL_STS0 - mmDMA_QM_0_GLBL_STS0;
@@ -5164,13 +5144,13 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GOYA_ENGINE_ID_DMA_0 + i, mask);
- if (s)
- seq_printf(s, dma_fmt, i, is_eng_idle ? "Y" : "N",
+ if (e)
+ hl_engine_data_sprintf(e, dma_fmt, i, is_eng_idle ? "Y" : "N",
qm_glbl_sts0, dma_core_sts0);
}
- if (s)
- seq_puts(s,
+ if (e)
+ hl_engine_data_sprintf(e,
"\nTPC is_idle QM_GLBL_STS0 CMDQ_GLBL_STS0 CFG_STATUS\n"
"--- ------- ------------ -------------- ----------\n");
@@ -5187,13 +5167,13 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GOYA_ENGINE_ID_TPC_0 + i, mask);
- if (s)
- seq_printf(s, fmt, i, is_eng_idle ? "Y" : "N",
+ if (e)
+ hl_engine_data_sprintf(e, fmt, i, is_eng_idle ? "Y" : "N",
qm_glbl_sts0, cmdq_glbl_sts0, tpc_cfg_sts);
}
- if (s)
- seq_puts(s,
+ if (e)
+ hl_engine_data_sprintf(e,
"\nMME is_idle QM_GLBL_STS0 CMDQ_GLBL_STS0 ARCH_STATUS\n"
"--- ------- ------------ -------------- -----------\n");
@@ -5207,10 +5187,10 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr,
if (mask && !is_eng_idle)
set_bit(GOYA_ENGINE_ID_MME_0, mask);
- if (s) {
- seq_printf(s, fmt, 0, is_eng_idle ? "Y" : "N", qm_glbl_sts0,
+ if (e) {
+ hl_engine_data_sprintf(e, fmt, 0, is_eng_idle ? "Y" : "N", qm_glbl_sts0,
cmdq_glbl_sts0, mme_arch_sts);
- seq_puts(s, "\n");
+ hl_engine_data_sprintf(e, "\n");
}
return is_idle;
@@ -5434,6 +5414,21 @@ static int goya_scrub_device_dram(struct hl_device *hdev, u64 val)
return -EOPNOTSUPP;
}
+static int goya_set_dram_properties(struct hl_device *hdev)
+{
+ return 0;
+}
+
+static int goya_set_binning_masks(struct hl_device *hdev)
+{
+ return 0;
+}
+
+static int goya_send_device_activity(struct hl_device *hdev, bool open)
+{
+ return 0;
+}
+
static const struct hl_asic_funcs goya_funcs = {
.early_init = goya_early_init,
.early_fini = goya_early_fini,
@@ -5459,9 +5454,9 @@ static const struct hl_asic_funcs goya_funcs = {
.asic_dma_pool_free = goya_dma_pool_free,
.cpu_accessible_dma_pool_alloc = goya_cpu_accessible_dma_pool_alloc,
.cpu_accessible_dma_pool_free = goya_cpu_accessible_dma_pool_free,
- .hl_dma_unmap_sgtable = hl_dma_unmap_sgtable,
+ .dma_unmap_sgtable = hl_asic_dma_unmap_sgtable,
.cs_parser = goya_cs_parser,
- .asic_dma_map_sgtable = hl_dma_map_sgtable,
+ .dma_map_sgtable = hl_asic_dma_map_sgtable,
.add_end_of_cb_packets = goya_add_end_of_cb_packets,
.update_eq_ci = goya_update_eq_ci,
.context_switch = goya_context_switch,
@@ -5478,11 +5473,9 @@ static const struct hl_asic_funcs goya_funcs = {
.send_heartbeat = goya_send_heartbeat,
.debug_coresight = goya_debug_coresight,
.is_device_idle = goya_is_device_idle,
- .non_hard_reset_late_init = goya_non_hard_reset_late_init,
+ .compute_reset_late_init = goya_compute_reset_late_init,
.hw_queues_lock = goya_hw_queues_lock,
.hw_queues_unlock = goya_hw_queues_unlock,
- .kdma_lock = NULL,
- .kdma_unlock = NULL,
.get_pci_id = goya_get_pci_id,
.get_eeprom_data = goya_get_eeprom_data,
.get_monitor_dump = goya_get_monitor_dump,
@@ -5528,6 +5521,9 @@ static const struct hl_asic_funcs goya_funcs = {
.mmu_get_real_page_size = hl_mmu_get_real_page_size,
.access_dev_mem = hl_access_dev_mem,
.set_dram_bar_base = goya_set_ddr_bar_base,
+ .send_device_activity = goya_send_device_activity,
+ .set_dram_properties = goya_set_dram_properties,
+ .set_binning_masks = goya_set_binning_masks,
};
/*
diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/accel/habanalabs/goya/goyaP.h
index d6ec43d6f6b0..194c2ae157cd 100644
--- a/drivers/misc/habanalabs/goya/goyaP.h
+++ b/drivers/accel/habanalabs/goya/goyaP.h
@@ -8,9 +8,9 @@
#ifndef GOYAP_H_
#define GOYAP_H_
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
+#include <linux/habanalabs/hl_boot_if.h>
#include "../common/habanalabs.h"
-#include "../include/common/hl_boot_if.h"
#include "../include/goya/goya_packets.h"
#include "../include/goya/goya.h"
#include "../include/goya/goya_async_events.h"
diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/accel/habanalabs/goya/goya_coresight.c
index 2c5133cfae65..3827ea4c02f7 100644
--- a/drivers/misc/habanalabs/goya/goya_coresight.c
+++ b/drivers/accel/habanalabs/goya/goya_coresight.c
@@ -10,7 +10,7 @@
#include "../include/goya/asic_reg/goya_regs.h"
#include "../include/goya/asic_reg/goya_masks.h"
-#include <uapi/misc/habanalabs.h>
+#include <uapi/drm/habanalabs_accel.h>
#define GOYA_PLDM_CORESIGHT_TIMEOUT_USEC (CORESIGHT_TIMEOUT_USEC * 100)
@@ -315,6 +315,11 @@ static int goya_config_etf(struct hl_device *hdev,
WREG32(base_reg + 0xFB0, CORESIGHT_UNLOCK);
+ val = RREG32(base_reg + 0x20);
+
+ if ((!params->enable && val == 0x0) || (params->enable && val != 0x0))
+ return 0;
+
val = RREG32(base_reg + 0x304);
val |= 0x1000;
WREG32(base_reg + 0x304, val);
@@ -371,13 +376,8 @@ static int goya_etr_validate_address(struct hl_device *hdev, u64 addr,
return false;
}
- if (hdev->mmu_enable) {
- range_start = prop->dmmu.start_addr;
- range_end = prop->dmmu.end_addr;
- } else {
- range_start = prop->dram_user_base_address;
- range_end = prop->dram_end_address;
- }
+ range_start = prop->dmmu.start_addr;
+ range_end = prop->dmmu.end_addr;
return hl_mem_area_inside_range(addr, size, range_start, range_end);
}
@@ -391,6 +391,11 @@ static int goya_config_etr(struct hl_device *hdev,
WREG32(mmPSOC_ETR_LAR, CORESIGHT_UNLOCK);
+ val = RREG32(mmPSOC_ETR_CTL);
+
+ if ((!params->enable && val == 0x0) || (params->enable && val != 0x0))
+ return 0;
+
val = RREG32(mmPSOC_ETR_FFCR);
val |= 0x1000;
WREG32(mmPSOC_ETR_FFCR, val);
@@ -571,7 +576,6 @@ static int goya_config_spmu(struct hl_device *hdev,
struct hl_debug_params *params)
{
u64 base_reg;
- struct hl_debug_params_spmu *input = params->input;
u64 *output;
u32 output_arr_len;
u32 events_num;
@@ -587,7 +591,7 @@ static int goya_config_spmu(struct hl_device *hdev,
base_reg = debug_spmu_regs[params->reg_idx] - CFG_BASE;
if (params->enable) {
- input = params->input;
+ struct hl_debug_params_spmu *input = params->input;
if (!input)
return -EINVAL;
diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/accel/habanalabs/goya/goya_hwmgr.c
index b595721751c1..b595721751c1 100644
--- a/drivers/misc/habanalabs/goya/goya_hwmgr.c
+++ b/drivers/accel/habanalabs/goya/goya_hwmgr.c
diff --git a/drivers/misc/habanalabs/goya/goya_security.c b/drivers/accel/habanalabs/goya/goya_security.c
index 14c3bae3ccdc..14c3bae3ccdc 100644
--- a/drivers/misc/habanalabs/goya/goya_security.c
+++ b/drivers/accel/habanalabs/goya/goya_security.c
diff --git a/drivers/misc/habanalabs/include/common/qman_if.h b/drivers/accel/habanalabs/include/common/qman_if.h
index 7ed7739575ee..7ed7739575ee 100644
--- a/drivers/misc/habanalabs/include/common/qman_if.h
+++ b/drivers/accel/habanalabs/include/common/qman_if.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/cpu_if_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/cpu_if_regs.h
index cf80e31317ad..cf80e31317ad 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/cpu_if_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/cpu_if_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_core_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_core_masks.h
index d079a37acab8..d079a37acab8 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_core_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_core_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_core_regs.h
index 1fdd5d5fc6d2..1fdd5d5fc6d2 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_qm_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_qm_masks.h
index 48376aabc3ba..48376aabc3ba 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_qm_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_qm_regs.h
index 8e56a93d88a1..8e56a93d88a1 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma1_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma1_core_regs.h
index 4d8d8f26c5d4..4d8d8f26c5d4 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma1_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma1_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma1_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma1_qm_regs.h
index c3ef300849be..c3ef300849be 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma1_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma1_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma2_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma2_core_regs.h
index a42862cd5ae0..a42862cd5ae0 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma2_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma2_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma2_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma2_qm_regs.h
index 8c4d4e016852..8c4d4e016852 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma2_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma2_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma3_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma3_core_regs.h
index fb145f416fe6..fb145f416fe6 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma3_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma3_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma3_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma3_qm_regs.h
index a4b461ca3d94..a4b461ca3d94 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma3_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma3_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma4_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma4_core_regs.h
index 192d11404b1c..192d11404b1c 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma4_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma4_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma4_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma4_qm_regs.h
index f0cbda0d1e4d..f0cbda0d1e4d 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma4_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma4_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma5_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma5_core_regs.h
index 6e07c6fb6fc9..6e07c6fb6fc9 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma5_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma5_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma5_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma5_qm_regs.h
index 0faea21756c5..0faea21756c5 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma5_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma5_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma6_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma6_core_regs.h
index 4962c13e2e2e..4962c13e2e2e 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma6_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma6_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma6_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma6_qm_regs.h
index af87adb94c94..af87adb94c94 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma6_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma6_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma7_core_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma7_core_regs.h
index 8dd705d20195..8dd705d20195 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma7_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma7_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma7_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma7_qm_regs.h
index d6c631f63e3e..d6c631f63e3e 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma7_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma7_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch0_regs.h
index 8c1c72df4469..8c1c72df4469 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch1_regs.h
index b2b593fcec30..b2b593fcec30 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_down_ch1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_regs.h
index 8a10c6a76156..8a10c6a76156 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_n_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_n_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch0_regs.h
index cd61289a1e8a..cd61289a1e8a 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch1_regs.h
index 3f32370a14c7..3f32370a14c7 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_down_ch1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_regs.h
index 78c18da7154b..78c18da7154b 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_e_s_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_e_s_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch0_regs.h
index 4ccaf8712948..4ccaf8712948 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch1_regs.h
index 9236f4183084..9236f4183084 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_down_ch1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_regs.h
index da60893a5fab..da60893a5fab 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_n_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_n_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch0_regs.h
index 56ffc920d58a..56ffc920d58a 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch1_regs.h
index cbc642918deb..cbc642918deb 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_down_ch1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_regs.h
index 2382bc41bea6..2382bc41bea6 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/dma_if_w_s_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/dma_if_w_s_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_blocks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/gaudi_blocks.h
index c7596aac7a5c..c7596aac7a5c 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_blocks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/gaudi_blocks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/gaudi_regs.h
index 1a6576666794..23ee8691db46 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/gaudi_regs.h
@@ -320,4 +320,6 @@
#define mmPSOC_TPC_PLL_NR 0xC73100
#define mmIF_W_PLL_NR 0x488100
+#define mmPCIE_WRAP_RR_ELBI_RD_SEC_REG_CTRL 0xC01208
+
#endif /* ASIC_REG_GAUDI_REGS_H_ */
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_ctrl_regs.h
index 083d073a0128..083d073a0128 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_qm_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_qm_masks.h
index e6dd30ce0ca7..e6dd30ce0ca7 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_qm_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_qm_regs.h
index 4f078b328b00..4f078b328b00 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme1_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme1_ctrl_regs.h
index 6c07f7d45490..6c07f7d45490 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme1_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme1_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme2_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme2_ctrl_regs.h
index a1f2eb8b91bd..a1f2eb8b91bd 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme2_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme2_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme2_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme2_qm_regs.h
index c1ea6a422010..c1ea6a422010 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme2_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme2_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme3_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme3_ctrl_regs.h
index 36f6edc72e3d..36f6edc72e3d 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mme3_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mme3_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/mmu_up_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/mmu_up_regs.h
index 61465b599850..61465b599850 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/mmu_up_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/mmu_up_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm0_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm0_masks.h
index bd37b6452133..bd37b6452133 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm0_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm0_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm0_regs.h
index 7c97f4041b8e..7c97f4041b8e 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm1_regs.h
index fe96c575b5c6..fe96c575b5c6 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic0_qm1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic0_qm1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic1_qm0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic1_qm0_regs.h
index 0d1caf057ad0..0d1caf057ad0 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic1_qm0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic1_qm0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic1_qm1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic1_qm1_regs.h
index 1b115ee6d6f0..1b115ee6d6f0 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic1_qm1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic1_qm1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic2_qm0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic2_qm0_regs.h
index a89116a4586f..a89116a4586f 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic2_qm0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic2_qm0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic2_qm1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic2_qm1_regs.h
index b7f091ddc89c..b7f091ddc89c 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic2_qm1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic2_qm1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic3_qm0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic3_qm0_regs.h
index 4712cc62b009..4712cc62b009 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic3_qm0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic3_qm0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic3_qm1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic3_qm1_regs.h
index 7fa040f65004..7fa040f65004 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic3_qm1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic3_qm1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic4_qm0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic4_qm0_regs.h
index 99d5319672dd..99d5319672dd 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic4_qm0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic4_qm0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic4_qm1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic4_qm1_regs.h
index 34b21b21da52..34b21b21da52 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nic4_qm1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nic4_qm1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_0_regs.h
index 2efa2a54deb4..2efa2a54deb4 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_1_regs.h
index a6047d4e2560..a6047d4e2560 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_2_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_2_regs.h
index 9de8442f9bc2..9de8442f9bc2 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_2_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_2_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_3_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_3_regs.h
index 34fd47685edd..34fd47685edd 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_3_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_3_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_4_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_4_regs.h
index 543a98f81767..543a98f81767 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_4_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_4_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_5_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_5_regs.h
index 95486b7ddf1d..95486b7ddf1d 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_5_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_5_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_6_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_6_regs.h
index b79c59887b21..b79c59887b21 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_6_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_6_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_7_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_7_regs.h
index 3a6a34ba2958..3a6a34ba2958 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_7_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/nif_rtr_ctrl_7_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h
index 2585c70f59ef..2585c70f59ef 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_etr_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_etr_regs.h
index b7c33e025db5..b7c33e025db5 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_etr_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_etr_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_global_conf_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_global_conf_masks.h
index 6703e678ee9f..6703e678ee9f 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_global_conf_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_global_conf_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_global_conf_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_global_conf_regs.h
index 1b5cfcc1d85f..1b5cfcc1d85f 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_global_conf_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_global_conf_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_timestamp_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_timestamp_regs.h
index 9ce24597d4b0..9ce24597d4b0 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_timestamp_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/psoc_timestamp_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_0_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_0_regs.h
index ddf824392cf7..ddf824392cf7 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_1_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_1_regs.h
index c6d517dbbd54..c6d517dbbd54 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_2_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_2_regs.h
index 330e5b42d679..330e5b42d679 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_2_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_2_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_3_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_3_regs.h
index d749f1968e5e..d749f1968e5e 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_3_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_3_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_4_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_4_regs.h
index ad48773c4bbd..ad48773c4bbd 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_4_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_4_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_5_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_5_regs.h
index 6c27850ca3f5..6c27850ca3f5 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_5_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_5_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_6_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_6_regs.h
index a9ea89aa6405..a9ea89aa6405 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_6_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_6_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_7_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_7_regs.h
index a37772c531d9..a37772c531d9 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_7_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/sif_rtr_ctrl_7_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/stlb_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/stlb_regs.h
index 07d2a9000102..07d2a9000102 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/stlb_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/stlb_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_cfg_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_cfg_masks.h
index 8f67c11c8de9..8f67c11c8de9 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_cfg_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_cfg_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_cfg_regs.h
index b82a906265a8..b82a906265a8 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_qm_masks.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_qm_masks.h
index 8e71532c6f36..8e71532c6f36 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_qm_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_qm_regs.h
index f9e310ab6df2..f9e310ab6df2 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc1_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc1_cfg_regs.h
index 6736c476d979..6736c476d979 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc1_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc1_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc1_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc1_qm_regs.h
index af10ef7a87d9..af10ef7a87d9 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc1_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc1_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc2_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc2_cfg_regs.h
index 3e77c37952bc..3e77c37952bc 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc2_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc2_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc2_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc2_qm_regs.h
index 2919e2fa58f8..2919e2fa58f8 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc2_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc2_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc3_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc3_cfg_regs.h
index 6d42469659f1..6d42469659f1 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc3_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc3_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc3_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc3_qm_regs.h
index 5f2a0fd86c9e..5f2a0fd86c9e 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc3_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc3_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc4_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc4_cfg_regs.h
index 7a9447f39a74..7a9447f39a74 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc4_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc4_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc4_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc4_qm_regs.h
index 80e63402f6e0..80e63402f6e0 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc4_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc4_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc5_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc5_cfg_regs.h
index f428f891935a..f428f891935a 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc5_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc5_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc5_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc5_qm_regs.h
index cd3a810ff4c4..cd3a810ff4c4 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc5_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc5_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc6_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc6_cfg_regs.h
index eb251e72813f..eb251e72813f 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc6_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc6_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc6_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc6_qm_regs.h
index e35ef7fd8b1c..e35ef7fd8b1c 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc6_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc6_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc7_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc7_cfg_regs.h
index 1887b10e58e2..1887b10e58e2 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc7_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc7_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc7_qm_regs.h b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc7_qm_regs.h
index 5c36c972c027..5c36c972c027 100644
--- a/drivers/misc/habanalabs/include/gaudi/asic_reg/tpc7_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi/asic_reg/tpc7_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi.h b/drivers/accel/habanalabs/include/gaudi/gaudi.h
index ffae107b1693..ffae107b1693 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi.h
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h b/drivers/accel/habanalabs/include/gaudi/gaudi_async_events.h
index c07ed4ed304c..c07ed4ed304c 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_async_events.h
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h b/drivers/accel/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h
index 479b6b038254..479b6b038254 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_coresight.h b/drivers/accel/habanalabs/include/gaudi/gaudi_coresight.h
index c45cc7f4d4d7..c45cc7f4d4d7 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_coresight.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_coresight.h
diff --git a/drivers/accel/habanalabs/include/gaudi/gaudi_fw_if.h b/drivers/accel/habanalabs/include/gaudi/gaudi_fw_if.h
new file mode 100644
index 000000000000..a2547f306750
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_fw_if.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2019-2020 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+#ifndef GAUDI_FW_IF_H
+#define GAUDI_FW_IF_H
+
+#define GAUDI_EVENT_QUEUE_MSI_IDX 8
+#define GAUDI_NIC_PORT1_MSI_IDX 10
+#define GAUDI_NIC_PORT3_MSI_IDX 12
+#define GAUDI_NIC_PORT5_MSI_IDX 14
+#define GAUDI_NIC_PORT7_MSI_IDX 16
+#define GAUDI_NIC_PORT9_MSI_IDX 18
+
+#define UBOOT_FW_OFFSET 0x100000 /* 1MB in SRAM */
+#define LINUX_FW_OFFSET 0x800000 /* 8MB in HBM */
+
+/* HBM thermal delta in [Deg] added to composite (CTemp) */
+#define HBM_TEMP_ADJUST_COEFF 6
+
+enum gaudi_nic_axi_error {
+ RXB,
+ RXE,
+ TXS,
+ TXE,
+ QPC_RESP,
+ NON_AXI_ERR,
+ TMR,
+};
+
+/*
+ * struct eq_nic_sei_event - describes an AXI error cause.
+ * @axi_error_cause: one of the events defined in enum gaudi_nic_axi_error.
+ * @id: can be either 0 or 1, to further describe unit with interrupt cause
+ * (i.e. TXE0 or TXE1).
+ * @pad[6]: padding structure to 64bit.
+ */
+struct eq_nic_sei_event {
+ __u8 axi_error_cause;
+ __u8 id;
+ __u8 pad[6];
+};
+
+struct gaudi_cold_rst_data {
+ union {
+ struct {
+ u32 spsram_init_done : 1;
+ u32 reserved : 31;
+ };
+ __le32 data;
+ };
+};
+
+#define GAUDI_PLL_FREQ_LOW 200000000 /* 200 MHz */
+
+#endif /* GAUDI_FW_IF_H */
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h b/drivers/accel/habanalabs/include/gaudi/gaudi_masks.h
index 880c57b26c63..880c57b26c63 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_packets.h b/drivers/accel/habanalabs/include/gaudi/gaudi_packets.h
index 66fc083a7c6a..66fc083a7c6a 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_packets.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_packets.h
diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h b/drivers/accel/habanalabs/include/gaudi/gaudi_reg_map.h
index 92f25c2ae083..92f25c2ae083 100644
--- a/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h
+++ b/drivers/accel/habanalabs/include/gaudi/gaudi_reg_map.h
diff --git a/drivers/accel/habanalabs/include/gaudi2/arc/gaudi2_arc_common_packets.h b/drivers/accel/habanalabs/include/gaudi2/arc/gaudi2_arc_common_packets.h
new file mode 100644
index 000000000000..22a6ab9a7f47
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi2/arc/gaudi2_arc_common_packets.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 HabanaLabs Ltd.
+ * All Rights Reserved.
+ */
+
+#ifndef __GAUDI2_ARC_COMMON_PACKETS_H__
+#define __GAUDI2_ARC_COMMON_PACKETS_H__
+
+enum {
+ CPU_ID_SCHED_ARC0 = 0, /* FARM_ARC0 */
+ CPU_ID_SCHED_ARC1 = 1, /* FARM_ARC1 */
+ CPU_ID_SCHED_ARC2 = 2, /* FARM_ARC2 */
+ CPU_ID_SCHED_ARC3 = 3, /* FARM_ARC3 */
+ /* Dcore1 MME Engine ARC instance used as scheduler */
+ CPU_ID_SCHED_ARC4 = 4, /* DCORE1_MME0 */
+ /* Dcore3 MME Engine ARC instance used as scheduler */
+ CPU_ID_SCHED_ARC5 = 5, /* DCORE3_MME0 */
+
+ CPU_ID_TPC_QMAN_ARC0 = 6, /* DCORE0_TPC0 */
+ CPU_ID_TPC_QMAN_ARC1 = 7, /* DCORE0_TPC1 */
+ CPU_ID_TPC_QMAN_ARC2 = 8, /* DCORE0_TPC2 */
+ CPU_ID_TPC_QMAN_ARC3 = 9, /* DCORE0_TPC3 */
+ CPU_ID_TPC_QMAN_ARC4 = 10, /* DCORE0_TPC4 */
+ CPU_ID_TPC_QMAN_ARC5 = 11, /* DCORE0_TPC5 */
+ CPU_ID_TPC_QMAN_ARC6 = 12, /* DCORE1_TPC0 */
+ CPU_ID_TPC_QMAN_ARC7 = 13, /* DCORE1_TPC1 */
+ CPU_ID_TPC_QMAN_ARC8 = 14, /* DCORE1_TPC2 */
+ CPU_ID_TPC_QMAN_ARC9 = 15, /* DCORE1_TPC3 */
+ CPU_ID_TPC_QMAN_ARC10 = 16, /* DCORE1_TPC4 */
+ CPU_ID_TPC_QMAN_ARC11 = 17, /* DCORE1_TPC5 */
+ CPU_ID_TPC_QMAN_ARC12 = 18, /* DCORE2_TPC0 */
+ CPU_ID_TPC_QMAN_ARC13 = 19, /* DCORE2_TPC1 */
+ CPU_ID_TPC_QMAN_ARC14 = 20, /* DCORE2_TPC2 */
+ CPU_ID_TPC_QMAN_ARC15 = 21, /* DCORE2_TPC3 */
+ CPU_ID_TPC_QMAN_ARC16 = 22, /* DCORE2_TPC4 */
+ CPU_ID_TPC_QMAN_ARC17 = 23, /* DCORE2_TPC5 */
+ CPU_ID_TPC_QMAN_ARC18 = 24, /* DCORE3_TPC0 */
+ CPU_ID_TPC_QMAN_ARC19 = 25, /* DCORE3_TPC1 */
+ CPU_ID_TPC_QMAN_ARC20 = 26, /* DCORE3_TPC2 */
+ CPU_ID_TPC_QMAN_ARC21 = 27, /* DCORE3_TPC3 */
+ CPU_ID_TPC_QMAN_ARC22 = 28, /* DCORE3_TPC4 */
+ CPU_ID_TPC_QMAN_ARC23 = 29, /* DCORE3_TPC5 */
+ CPU_ID_TPC_QMAN_ARC24 = 30, /* DCORE0_TPC6 - Never present */
+
+ CPU_ID_MME_QMAN_ARC0 = 31, /* DCORE0_MME0 */
+ CPU_ID_MME_QMAN_ARC1 = 32, /* DCORE2_MME0 */
+
+ CPU_ID_EDMA_QMAN_ARC0 = 33, /* DCORE0_EDMA0 */
+ CPU_ID_EDMA_QMAN_ARC1 = 34, /* DCORE0_EDMA1 */
+ CPU_ID_EDMA_QMAN_ARC2 = 35, /* DCORE1_EDMA0 */
+ CPU_ID_EDMA_QMAN_ARC3 = 36, /* DCORE1_EDMA1 */
+ CPU_ID_EDMA_QMAN_ARC4 = 37, /* DCORE2_EDMA0 */
+ CPU_ID_EDMA_QMAN_ARC5 = 38, /* DCORE2_EDMA1 */
+ CPU_ID_EDMA_QMAN_ARC6 = 39, /* DCORE3_EDMA0 */
+ CPU_ID_EDMA_QMAN_ARC7 = 40, /* DCORE3_EDMA1 */
+
+ CPU_ID_PDMA_QMAN_ARC0 = 41, /* DCORE0_PDMA0 */
+ CPU_ID_PDMA_QMAN_ARC1 = 42, /* DCORE0_PDMA1 */
+
+ CPU_ID_ROT_QMAN_ARC0 = 43, /* ROT0 */
+ CPU_ID_ROT_QMAN_ARC1 = 44, /* ROT1 */
+
+ CPU_ID_NIC_QMAN_ARC0 = 45, /* NIC0_0 */
+ CPU_ID_NIC_QMAN_ARC1 = 46, /* NIC0_1 */
+ CPU_ID_NIC_QMAN_ARC2 = 47, /* NIC1_0 */
+ CPU_ID_NIC_QMAN_ARC3 = 48, /* NIC1_1 */
+ CPU_ID_NIC_QMAN_ARC4 = 49, /* NIC2_0 */
+ CPU_ID_NIC_QMAN_ARC5 = 50, /* NIC2_1 */
+ CPU_ID_NIC_QMAN_ARC6 = 51, /* NIC3_0 */
+ CPU_ID_NIC_QMAN_ARC7 = 52, /* NIC3_1 */
+ CPU_ID_NIC_QMAN_ARC8 = 53, /* NIC4_0 */
+ CPU_ID_NIC_QMAN_ARC9 = 54, /* NIC4_1 */
+ CPU_ID_NIC_QMAN_ARC10 = 55, /* NIC5_0 */
+ CPU_ID_NIC_QMAN_ARC11 = 56, /* NIC5_1 */
+ CPU_ID_NIC_QMAN_ARC12 = 57, /* NIC6_0 */
+ CPU_ID_NIC_QMAN_ARC13 = 58, /* NIC6_1 */
+ CPU_ID_NIC_QMAN_ARC14 = 59, /* NIC7_0 */
+ CPU_ID_NIC_QMAN_ARC15 = 60, /* NIC7_1 */
+ CPU_ID_NIC_QMAN_ARC16 = 61, /* NIC8_0 */
+ CPU_ID_NIC_QMAN_ARC17 = 62, /* NIC8_1 */
+ CPU_ID_NIC_QMAN_ARC18 = 63, /* NIC9_0 */
+ CPU_ID_NIC_QMAN_ARC19 = 64, /* NIC9_1 */
+ CPU_ID_NIC_QMAN_ARC20 = 65, /* NIC10_0 */
+ CPU_ID_NIC_QMAN_ARC21 = 66, /* NIC10_1 */
+ CPU_ID_NIC_QMAN_ARC22 = 67, /* NIC11_0 */
+ CPU_ID_NIC_QMAN_ARC23 = 68, /* NIC11_1 */
+
+ CPU_ID_MAX = 69,
+ CPU_ID_SCHED_MAX = 6,
+
+ CPU_ID_ALL = 0xFE,
+ CPU_ID_INVALID = 0xFF,
+};
+
+enum arc_regions_t {
+ ARC_REGION0_UNSED = 0,
+ /*
+ * Extension registers
+ * None
+ */
+ ARC_REGION1_SRAM = 1,
+ /*
+ * Extension registers
+ * AUX_SRAM_LSB_ADDR
+ * AUX_SRAM_MSB_ADDR
+ * ARC Address: 0x1000_0000
+ */
+ ARC_REGION2_CFG = 2,
+ /*
+ * Extension registers
+ * AUX_CFG_LSB_ADDR
+ * AUX_CFG_MSB_ADDR
+ * ARC Address: 0x2000_0000
+ */
+ ARC_REGION3_GENERAL = 3,
+ /*
+ * Extension registers
+ * AUX_GENERAL_PURPOSE_LSB_ADDR_0
+ * AUX_GENERAL_PURPOSE_MSB_ADDR_0
+ * ARC Address: 0x3000_0000
+ */
+ ARC_REGION4_HBM0_FW = 4,
+ /*
+ * Extension registers
+ * AUX_HBM0_LSB_ADDR
+ * AUX_HBM0_MSB_ADDR
+ * AUX_HBM0_OFFSET
+ * ARC Address: 0x4000_0000
+ */
+ ARC_REGION5_HBM1_GC_DATA = 5,
+ /*
+ * Extension registers
+ * AUX_HBM1_LSB_ADDR
+ * AUX_HBM1_MSB_ADDR
+ * AUX_HBM1_OFFSET
+ * ARC Address: 0x5000_0000
+ */
+ ARC_REGION6_HBM2_GC_DATA = 6,
+ /*
+ * Extension registers
+ * AUX_HBM2_LSB_ADDR
+ * AUX_HBM2_MSB_ADDR
+ * AUX_HBM2_OFFSET
+ * ARC Address: 0x6000_0000
+ */
+ ARC_REGION7_HBM3_GC_DATA = 7,
+ /*
+ * Extension registers
+ * AUX_HBM3_LSB_ADDR
+ * AUX_HBM3_MSB_ADDR
+ * AUX_HBM3_OFFSET
+ * ARC Address: 0x7000_0000
+ */
+ ARC_REGION8_DCCM = 8,
+ /*
+ * Extension registers
+ * None
+ * ARC Address: 0x8000_0000
+ */
+ ARC_REGION9_PCIE = 9,
+ /*
+ * Extension registers
+ * AUX_PCIE_LSB_ADDR
+ * AUX_PCIE_MSB_ADDR
+ * ARC Address: 0x9000_0000
+ */
+ ARC_REGION10_GENERAL = 10,
+ /*
+ * Extension registers
+ * AUX_GENERAL_PURPOSE_LSB_ADDR_1
+ * AUX_GENERAL_PURPOSE_MSB_ADDR_1
+ * ARC Address: 0xA000_0000
+ */
+ ARC_REGION11_GENERAL = 11,
+ /*
+ * Extension registers
+ * AUX_GENERAL_PURPOSE_LSB_ADDR_2
+ * AUX_GENERAL_PURPOSE_MSB_ADDR_2
+ * ARC Address: 0xB000_0000
+ */
+ ARC_REGION12_GENERAL = 12,
+ /*
+ * Extension registers
+ * AUX_GENERAL_PURPOSE_LSB_ADDR_3
+ * AUX_GENERAL_PURPOSE_MSB_ADDR_3
+ * ARC Address: 0xC000_0000
+ */
+ ARC_REGION13_GENERAL = 13,
+ /*
+ * Extension registers
+ * AUX_GENERAL_PURPOSE_LSB_ADDR_4
+ * AUX_GENERAL_PURPOSE_MSB_ADDR_4
+ * ARC Address: 0xD000_0000
+ */
+ ARC_REGION14_GENERAL = 14,
+ /*
+ * Extension registers
+ * AUX_GENERAL_PURPOSE_LSB_ADDR_5
+ * AUX_GENERAL_PURPOSE_MSB_ADDR_5
+ * ARC Address: 0xE000_0000
+ */
+ ARC_REGION15_LBU = 15
+ /*
+ * Extension registers
+ * None
+ * ARC Address: 0xF000_0000
+ */
+};
+
+#endif /* __GAUDI2_ARC_COMMON_PACKETS_H__ */
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_acp_eng_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_acp_eng_regs.h
index 1974df13b5f9..1974df13b5f9 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_acp_eng_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_acp_eng_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_masks.h
index fc2c52af6509..fc2c52af6509 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_regs.h
index 5345b5faa3a2..5345b5faa3a2 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_axuser_regs.h
index bde077eed285..bde077eed285 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_regs.h
index 491af75c12c3..491af75c12c3 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_arc0_dup_eng_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_masks.h
index 12d6a124a2e9..12d6a124a2e9 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_regs.h
index 23f9d2df52a7..23f9d2df52a7 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_masks.h
index bee4de0b28d6..bee4de0b28d6 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_regs.h
index b9f09e8199e6..b9f09e8199e6 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_ctx_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_kdma_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_kdma_cgm_regs.h
index d6dd2c066fa9..d6dd2c066fa9 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_kdma_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_kdma_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_masks.h
index 5903dbacec80..5903dbacec80 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_regs.h
index e312cf810c0e..e312cf810c0e 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/arc_farm_kdma_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/cpu_if_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/cpu_if_regs.h
index 9b3eceec9d5d..9b3eceec9d5d 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/cpu_if_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/cpu_if_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_masks.h
index 296ab832013f..296ab832013f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_regs.h
index e26f0d77c9dc..e26f0d77c9dc 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_dec0_cmd_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_axuser_regs.h
index 8de48939243b..8de48939243b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_regs.h
index f73e76c8f5bd..f73e76c8f5bd 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_ctx_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_masks.h
index d600f6bf70d8..d600f6bf70d8 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_regs.h
index 84f068e4c602..84f068e4c602 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_arc_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_arc_aux_regs.h
index 0fc45300df81..0fc45300df81 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_arc_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_arc_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_axuser_nonsecured_regs.h
index 88d2a133f129..88d2a133f129 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_cgm_regs.h
index 0b0a76a5b2a0..0b0a76a5b2a0 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_masks.h
index 102e2a65811c..102e2a65811c 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_regs.h
index 32d475b9ed11..32d475b9ed11 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_core_ctx_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_core_ctx_axuser_regs.h
index b608a634562f..b608a634562f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_core_ctx_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_core_ctx_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_qm_axuser_nonsecured_regs.h
index c3a462f2a9ac..c3a462f2a9ac 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_edma1_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_masks.h
index df51eac10dd7..2965b6a3b423 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_masks.h
@@ -150,8 +150,7 @@
#define DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_HOP1_PAGE_SIZE_SHIFT 16
#define DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_HOP1_PAGE_SIZE_MASK 0xF0000
#define DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_CFG_8_BITS_HOP_MODE_EN_SHIFT 20
-#define DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_CFG_8_BITS_HOP_MODE_EN_MASK \
-0x100000
+#define DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_CFG_8_BITS_HOP_MODE_EN_MASK 0x100000
/* DCORE0_HMMU0_MMU_CORE_SEP_CACHE_RNG */
#define DCORE0_HMMU0_MMU_CORE_SEP_CACHE_RNG_CORE_SET_MASK_SHIFT 0
@@ -235,23 +234,19 @@
/* DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_63_32 */
#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_63_32_ILLEGAL_ADDR_63_32_SHIFT 0
-#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_63_32_ILLEGAL_ADDR_63_32_MASK \
-0xFFFFFFFF
+#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_63_32_ILLEGAL_ADDR_63_32_MASK 0xFFFFFFFF
/* DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_31_0 */
#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_31_0_ILLEGAL_ADDR_31_0_SHIFT 0
-#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_31_0_ILLEGAL_ADDR_31_0_MASK \
-0xFFFFFFFF
+#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_WRITE_31_0_ILLEGAL_ADDR_31_0_MASK 0xFFFFFFFF
/* DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_63_32 */
#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_63_32_ILLEGAL_ADDR_63_32_SHIFT 0
-#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_63_32_ILLEGAL_ADDR_63_32_MASK \
-0xFFFFFFFF
+#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_63_32_ILLEGAL_ADDR_63_32_MASK 0xFFFFFFFF
/* DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_31_0 */
#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_31_0_ILLEGAL_ADDR_31_0_SHIFT 0
-#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_31_0_ILLEGAL_ADDR_31_0_MASK \
-0xFFFFFFFF
+#define DCORE0_HMMU0_MMU_ILLEGAL_ADDR_READ_31_0_ILLEGAL_ADDR_31_0_MASK 0xFFFFFFFF
/* DCORE0_HMMU0_MMU_RAZWI_WRITE_VLD */
#define DCORE0_HMMU0_MMU_RAZWI_WRITE_VLD_R_SHIFT 0
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_regs.h
index 08ccd695ec89..08ccd695ec89 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_mmu_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_masks.h
index 192eba5f07bb..a311778b21e7 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_masks.h
@@ -92,8 +92,7 @@
#define DCORE0_HMMU0_STLB_HOP_CONFIGURATION_ONLY_LARGE_PAGE_SHIFT 20
#define DCORE0_HMMU0_STLB_HOP_CONFIGURATION_ONLY_LARGE_PAGE_MASK 0x100000
#define DCORE0_HMMU0_STLB_HOP_CONFIGURATION_LARGE_PAGE_INDICATION_BIT_SHIFT 21
-#define DCORE0_HMMU0_STLB_HOP_CONFIGURATION_LARGE_PAGE_INDICATION_BIT_MASK \
-0x7E00000
+#define DCORE0_HMMU0_STLB_HOP_CONFIGURATION_LARGE_PAGE_INDICATION_BIT_MASK 0x7E00000
/* DCORE0_HMMU0_STLB_LINK_LIST_LOOKUP_MASK_63_32 */
#define DCORE0_HMMU0_STLB_LINK_LIST_LOOKUP_MASK_63_32_R_SHIFT 0
@@ -228,12 +227,8 @@
#define DCORE0_HMMU0_STLB_MEM_READ_ARPROT_R_MASK 0x7
/* DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION */
-#define \
-DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_SHIFT \
-0
-#define \
-DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_MASK \
-0x1
+#define DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_SHIFT 0
+#define DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_MASK 0x1
#define DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_INVALIDATION_ASID_EN_SHIFT 1
#define DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_INVALIDATION_ASID_EN_MASK 0x2
#define DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_INVALIDATION_ASID_SHIFT 2
@@ -261,53 +256,43 @@ DCORE0_HMMU0_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_MASK \
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_0 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_0_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_0_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_0_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_1 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_1_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_1_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_1_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_2 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_2_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_2_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_2_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_3 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_3_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_3_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_3_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_4 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_4_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_4_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_4_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_5 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_5_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_5_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_5_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_6 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_6_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_6_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_6_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_7 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_7_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_7_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_7_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_8 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_8_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_8_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_8_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_9 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_9_ASID_POLY_MATRIX_H3_SHIFT 0
-#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_9_ASID_POLY_MATRIX_H3_MASK \
-0x1FF
+#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MATRIX_H3_9_ASID_POLY_MATRIX_H3_MASK 0x1FF
/* DCORE0_HMMU0_STLB_ASID_SCR_POLY_MAT_H3_10 */
#define DCORE0_HMMU0_STLB_ASID_SCR_POLY_MAT_H3_10_ASID_POLY_MATRIX_H3_SHIFT 0
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_regs.h
index 864a259f68e2..864a259f68e2 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_hmmu0_stlb_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_acc_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_acc_regs.h
index 07bed3ec740e..07bed3ec740e 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_acc_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_acc_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_master_regs.h
index c9043979fd69..c9043979fd69 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_slave_regs.h
index 7d74aea4576f..7d74aea4576f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout0_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_master_regs.h
index f6f519eb5f6f..f6f519eb5f6f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_slave_regs.h
index 0e0c056ade9b..0e0c056ade9b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_cout1_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_master_regs.h
index 34c6134a2f93..34c6134a2f93 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_slave_regs.h
index 55065032f87c..55065032f87c 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in0_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_master_regs.h
index 6022b387eacf..6022b387eacf 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_slave_regs.h
index f9c9b01f0d1a..f9c9b01f0d1a 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in1_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_master_regs.h
index d96119b8c435..d96119b8c435 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_slave_regs.h
index c80d6817efe1..c80d6817efe1 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in2_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_master_regs.h
index 753b31dc1760..753b31dc1760 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_slave_regs.h
index f68d043edcd9..f68d043edcd9 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in3_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_master_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_master_regs.h
index a6dce326bd74..a6dce326bd74 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_master_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_master_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_slave_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_slave_regs.h
index 5ace0f43cc78..5ace0f43cc78 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_slave_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_agu_in4_slave_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_base_addr_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_base_addr_regs.h
index b375393dfdc0..b375393dfdc0 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_base_addr_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_base_addr_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_end_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_end_regs.h
index 7c22b9383f3c..fb53feb0a1a6 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_end_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_end_regs.h
@@ -20,8 +20,7 @@
*****************************************
*/
-#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_CONV_KERNEL_SIZE_MINUS_1 \
-0x40CB280
+#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_CONV_KERNEL_SIZE_MINUS_1 0x40CB280
#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_CONV_LOW 0x40CB284
@@ -29,8 +28,7 @@
#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_OUTER_LOOP 0x40CB28C
-#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_NUM_ITERATIONS_MINUS_1 \
-0x40CB290
+#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_NUM_ITERATIONS_MINUS_1 0x40CB290
#define mmDCORE0_MME_CTRL_LO_ARCH_NON_TENSOR_END_SB_REPEAT 0x40CB294
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_start_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_start_regs.h
index d17c165faf8b..d17c165faf8b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_start_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_non_tensor_start_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_a_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_a_regs.h
index 7b77884e0024..7b77884e0024 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_a_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_a_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_b_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_b_regs.h
index a2a2ba454d6d..a2a2ba454d6d 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_b_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_b_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h
index 7ad7b197cf87..7ad7b197cf87 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_masks.h
index f699661d76aa..da0c94075e64 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_masks.h
@@ -78,8 +78,7 @@
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_MASTER_WAIT_SLAVE_FENCE_SHIFT 15
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_MASTER_WAIT_SLAVE_FENCE_MASK 0x8000
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE_SEND_FENCE2MASTER_SHIFT 16
-#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE_SEND_FENCE2MASTER_MASK \
-0x10000
+#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE_SEND_FENCE2MASTER_MASK 0x10000
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE_SIGNAL_EN_SHIFT 17
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE_SIGNAL_EN_MASK 0x20000
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE0_USE_SLV_ADR_SHIFT 18
@@ -87,11 +86,9 @@
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE1_USE_SLV_ADR_SHIFT 19
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE1_USE_SLV_ADR_MASK 0x80000
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE0_USE_MSTR_ADR_PLUS4_SHIFT 20
-#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE0_USE_MSTR_ADR_PLUS4_MASK \
-0x100000
+#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE0_USE_MSTR_ADR_PLUS4_MASK 0x100000
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE1_USE_MSTR_ADR_PLUS4_SHIFT 21
-#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE1_USE_MSTR_ADR_PLUS4_MASK \
-0x200000
+#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_DW0_SLAVE1_USE_MSTR_ADR_PLUS4_MASK 0x200000
/* DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_ADDR0 */
#define DCORE0_MME_CTRL_LO_ARCH_SYNC_OBJ_ADDR0_V_SHIFT 0
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_mme_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_mme_axuser_regs.h
index a51617a6f1fb..a51617a6f1fb 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_mme_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_mme_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_regs.h
index 1b91c9c13132..1b91c9c13132 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_ctrl_lo_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_acp_eng_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_acp_eng_regs.h
index f702fe6e9365..f702fe6e9365 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_acp_eng_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_acp_eng_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_aux_regs.h
index 917f8ab88373..917f8ab88373 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_axuser_regs.h
index c7ebaf73c51e..c7ebaf73c51e 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_regs.h
index 61654e37335b..61654e37335b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_arc_dup_eng_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_nonsecured_regs.h
index 32089b8250ed..32089b8250ed 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_secured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_secured_regs.h
index e168c1cc2a7d..e168c1cc2a7d 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_secured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_axuser_secured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_cgm_regs.h
index 543aba18ef68..543aba18ef68 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_regs.h
index c45583fcc2cf..c45583fcc2cf 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_masks.h
index 077ae5232790..077ae5232790 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_mstr_if_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_mstr_if_axuser_regs.h
index 211fa2c2c35b..211fa2c2c35b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_mstr_if_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_sbte0_mstr_if_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_wb0_mstr_if_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_wb0_mstr_if_axuser_regs.h
index 374a01d2b8d5..374a01d2b8d5 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_mme_wb0_mstr_if_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_mme_wb0_mstr_if_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_ctrl_regs.h
index 22f4d6c805c5..22f4d6c805c5 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_hbw_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_hbw_regs.h
index 3a7290b3a5c9..3a7290b3a5c9 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_hbw_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_hbw_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_lbw_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_lbw_regs.h
index 5b52b88fee0f..5b52b88fee0f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_lbw_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_prvt_lbw_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_hbw_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_hbw_regs.h
index d9b3f5cd392b..d9b3f5cd392b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_hbw_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_hbw_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_lbw_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_lbw_regs.h
index 1bba940d3031..1bba940d3031 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_lbw_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_rtr0_mstr_if_rr_shrd_lbw_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_masks.h
index f21540501cdd..f21540501cdd 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_regs.h
index c3c4991e6660..c3c4991e6660 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_glbl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_masks.h
index 76b273a41255..76b273a41255 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_regs.h
index 0bddc734329f..0bddc734329f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_mstr_if_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_masks.h
index 3a5b27df0ab4..3a5b27df0ab4 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_regs.h
index 8f082a1c9b1b..8f082a1c9b1b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_sync_mngr_objs_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_axuser_regs.h
index 2d4a22680a23..2d4a22680a23 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_regs.h
index cdab39debd2c..cdab39debd2c 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_tensor_0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_tensor_0_regs.h
index 4ef1c1edc5f7..4ef1c1edc5f7 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_tensor_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_kernel_tensor_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_masks.h
index cdecbd0f9d84..cdecbd0f9d84 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_regs.h
index 4cd9e26a150f..4cd9e26a150f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_sync_object_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_sync_object_regs.h
index 8da278a3f3fe..8da278a3f3fe 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_sync_object_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_sync_object_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_tensor_0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_tensor_0_regs.h
index 2e4ff06e4858..2e4ff06e4858 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_tensor_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_qm_tensor_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_regs.h
index 4d48f0c6880b..4d48f0c6880b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_special_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_special_regs.h
index 76ab8a1a7f31..76ab8a1a7f31 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_special_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_cfg_special_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_busmon_0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_busmon_0_regs.h
index f07da4a24f06..f07da4a24f06 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_busmon_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_busmon_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_etf_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_etf_regs.h
index aee9cbc78c3d..aee9cbc78c3d 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_etf_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_etf_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_funnel_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_funnel_regs.h
index dee670b666ee..dee670b666ee 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_funnel_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_funnel_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_spmu_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_spmu_regs.h
index 580ae57476bd..580ae57476bd 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_spmu_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_spmu_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_stm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_stm_regs.h
index 91686c563fe5..91686c563fe5 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_stm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_eml_stm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_arc_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_arc_aux_regs.h
index e007dabc5382..e007dabc5382 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_arc_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_arc_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_axuser_nonsecured_regs.h
index 149b85f5f045..149b85f5f045 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_cgm_regs.h
index d4aad1875ad6..d4aad1875ad6 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_regs.h
index cca8683cbca1..cca8683cbca1 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_tpc0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_dec_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_dec_regs.h
index e68667cc795a..e68667cc795a 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_dec_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_dec_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h
index f7ffdcbd1a76..f7ffdcbd1a76 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h
index 4c1bb5306cba..4c1bb5306cba 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h
index e413905ffe25..e413905ffe25 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h
index bce75ac6e279..bce75ac6e279 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_masks.h
index 68dd98459c86..1c02f3dfdb6e 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_masks.h
@@ -106,8 +106,7 @@
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWBURST_VIOL_SHIFT 2
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWBURST_VIOL_MASK 0x4
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWADDR_SIZE_ALIGN_VIOL_SHIFT 3
-#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWADDR_SIZE_ALIGN_VIOL_MASK \
-0x8
+#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWADDR_SIZE_ALIGN_VIOL_MASK 0x8
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWSIZE_VIOL_SHIFT 4
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_AWSIZE_VIOL_MASK 0x10
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARLEN_GT_31_SHIFT 5
@@ -117,8 +116,7 @@
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARBURST_VIOL_SHIFT 7
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARBURST_VIOL_MASK 0x80
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARADDR_SIZE_ALIGN_VIOL_SHIFT 8
-#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARADDR_SIZE_ALIGN_VIOL_MASK \
-0x100
+#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARADDR_SIZE_ALIGN_VIOL_MASK 0x100
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARSIZE_VIOL_SHIFT 9
#define DCORE0_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARSIZE_VIOL_MASK 0x200
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_regs.h
index d2844307a6bf..d2844307a6bf 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_brdg_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_ctrl_special_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_ctrl_special_regs.h
index 89b522b12998..89b522b12998 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_ctrl_special_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore0_vdec0_ctrl_special_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore1_mme_ctrl_lo_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_mme_ctrl_lo_regs.h
index 622613dc76fb..622613dc76fb 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore1_mme_ctrl_lo_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_mme_ctrl_lo_regs.h
diff --git a/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_sync_mngr_glbl_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_sync_mngr_glbl_regs.h
new file mode 100644
index 000000000000..3d3802755814
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore1_sync_mngr_glbl_regs.h
@@ -0,0 +1,1203 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2016-2020 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+/************************************
+ ** This is an auto-generated file **
+ ** DO NOT EDIT BELOW **
+ ************************************/
+
+#ifndef ASIC_REG_DCORE1_SYNC_MNGR_GLBL_REGS_H_
+#define ASIC_REG_DCORE1_SYNC_MNGR_GLBL_REGS_H_
+
+/*
+ *****************************************
+ * DCORE1_SYNC_MNGR_GLBL
+ * (Prototype: SOB_GLBL)
+ *****************************************
+ */
+
+#define mmDCORE1_SYNC_MNGR_GLBL_SM_SEI_MASK 0x431E000
+
+#define mmDCORE1_SYNC_MNGR_GLBL_SM_SEI_CAUSE 0x431E004
+
+#define mmDCORE1_SYNC_MNGR_GLBL_L2H_CPMR_L 0x431E008
+
+#define mmDCORE1_SYNC_MNGR_GLBL_L2H_CPMR_H 0x431E00C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_L2H_MASK_L 0x431E020
+
+#define mmDCORE1_SYNC_MNGR_GLBL_L2H_MASK_H 0x431E024
+
+#define mmDCORE1_SYNC_MNGR_GLBL_ASID_SEC 0x431E030
+
+#define mmDCORE1_SYNC_MNGR_GLBL_ASID_PRIV_ONLY 0x431E034
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DELAY 0x431E038
+
+#define mmDCORE1_SYNC_MNGR_GLBL_PI_SIZE 0x431E03C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_SOB_ONLY 0x431E040
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INTR 0x431E044
+
+#define mmDCORE1_SYNC_MNGR_GLBL_ASID_NONE_SEC_PRIV 0x431E048
+
+#define mmDCORE1_SYNC_MNGR_GLBL_PI_INC_MODE_SIZE 0x431E04C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_0 0x431E050
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_1 0x431E054
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_2 0x431E058
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_3 0x431E05C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_4 0x431E060
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_5 0x431E064
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_6 0x431E068
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_7 0x431E06C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_8 0x431E070
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_9 0x431E074
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_10 0x431E078
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_11 0x431E07C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_12 0x431E080
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_13 0x431E084
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_14 0x431E088
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_15 0x431E08C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_16 0x431E090
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_17 0x431E094
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_18 0x431E098
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_19 0x431E09C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_20 0x431E0A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_21 0x431E0A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_22 0x431E0A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_23 0x431E0AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_24 0x431E0B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_25 0x431E0B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_26 0x431E0B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_27 0x431E0BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_28 0x431E0C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_29 0x431E0C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_30 0x431E0C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_31 0x431E0CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_32 0x431E0D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_33 0x431E0D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_34 0x431E0D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_35 0x431E0DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_36 0x431E0E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_37 0x431E0E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_38 0x431E0E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_39 0x431E0EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_40 0x431E0F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_41 0x431E0F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_42 0x431E0F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_43 0x431E0FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_44 0x431E100
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_45 0x431E104
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_46 0x431E108
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_47 0x431E10C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_48 0x431E110
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_49 0x431E114
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_50 0x431E118
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_51 0x431E11C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_52 0x431E120
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_53 0x431E124
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_54 0x431E128
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_55 0x431E12C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_56 0x431E130
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_57 0x431E134
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_58 0x431E138
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_59 0x431E13C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_60 0x431E140
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_61 0x431E144
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_62 0x431E148
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_L_63 0x431E14C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_0 0x431E150
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_1 0x431E154
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_2 0x431E158
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_3 0x431E15C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_4 0x431E160
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_5 0x431E164
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_6 0x431E168
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_7 0x431E16C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_8 0x431E170
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_9 0x431E174
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_10 0x431E178
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_11 0x431E17C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_12 0x431E180
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_13 0x431E184
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_14 0x431E188
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_15 0x431E18C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_16 0x431E190
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_17 0x431E194
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_18 0x431E198
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_19 0x431E19C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_20 0x431E1A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_21 0x431E1A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_22 0x431E1A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_23 0x431E1AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_24 0x431E1B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_25 0x431E1B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_26 0x431E1B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_27 0x431E1BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_28 0x431E1C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_29 0x431E1C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_30 0x431E1C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_31 0x431E1CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_32 0x431E1D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_33 0x431E1D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_34 0x431E1D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_35 0x431E1DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_36 0x431E1E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_37 0x431E1E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_38 0x431E1E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_39 0x431E1EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_40 0x431E1F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_41 0x431E1F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_42 0x431E1F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_43 0x431E1FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_44 0x431E200
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_45 0x431E204
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_46 0x431E208
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_47 0x431E20C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_48 0x431E210
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_49 0x431E214
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_50 0x431E218
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_51 0x431E21C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_52 0x431E220
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_53 0x431E224
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_54 0x431E228
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_55 0x431E22C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_56 0x431E230
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_57 0x431E234
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_58 0x431E238
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_59 0x431E23C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_60 0x431E240
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_61 0x431E244
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_62 0x431E248
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_BASE_ADDR_H_63 0x431E24C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_0 0x431E250
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_1 0x431E254
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_2 0x431E258
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_3 0x431E25C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_4 0x431E260
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_5 0x431E264
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_6 0x431E268
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_7 0x431E26C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_8 0x431E270
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_9 0x431E274
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_10 0x431E278
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_11 0x431E27C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_12 0x431E280
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_13 0x431E284
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_14 0x431E288
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_15 0x431E28C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_16 0x431E290
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_17 0x431E294
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_18 0x431E298
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_19 0x431E29C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_20 0x431E2A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_21 0x431E2A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_22 0x431E2A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_23 0x431E2AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_24 0x431E2B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_25 0x431E2B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_26 0x431E2B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_27 0x431E2BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_28 0x431E2C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_29 0x431E2C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_30 0x431E2C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_31 0x431E2CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_32 0x431E2D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_33 0x431E2D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_34 0x431E2D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_35 0x431E2DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_36 0x431E2E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_37 0x431E2E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_38 0x431E2E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_39 0x431E2EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_40 0x431E2F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_41 0x431E2F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_42 0x431E2F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_43 0x431E2FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_44 0x431E300
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_45 0x431E304
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_46 0x431E308
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_47 0x431E30C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_48 0x431E310
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_49 0x431E314
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_50 0x431E318
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_51 0x431E31C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_52 0x431E320
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_53 0x431E324
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_54 0x431E328
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_55 0x431E32C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_56 0x431E330
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_57 0x431E334
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_58 0x431E338
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_59 0x431E33C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_60 0x431E340
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_61 0x431E344
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_62 0x431E348
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SIZE_LOG2_63 0x431E34C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_0 0x431E350
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_1 0x431E354
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_2 0x431E358
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_3 0x431E35C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_4 0x431E360
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_5 0x431E364
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_6 0x431E368
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_7 0x431E36C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_8 0x431E370
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_9 0x431E374
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_10 0x431E378
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_11 0x431E37C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_12 0x431E380
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_13 0x431E384
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_14 0x431E388
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_15 0x431E38C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_16 0x431E390
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_17 0x431E394
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_18 0x431E398
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_19 0x431E39C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_20 0x431E3A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_21 0x431E3A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_22 0x431E3A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_23 0x431E3AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_24 0x431E3B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_25 0x431E3B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_26 0x431E3B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_27 0x431E3BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_28 0x431E3C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_29 0x431E3C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_30 0x431E3C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_31 0x431E3CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_32 0x431E3D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_33 0x431E3D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_34 0x431E3D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_35 0x431E3DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_36 0x431E3E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_37 0x431E3E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_38 0x431E3E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_39 0x431E3EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_40 0x431E3F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_41 0x431E3F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_42 0x431E3F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_43 0x431E3FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_44 0x431E400
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_45 0x431E404
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_46 0x431E408
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_47 0x431E40C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_48 0x431E410
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_49 0x431E414
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_50 0x431E418
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_51 0x431E41C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_52 0x431E420
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_53 0x431E424
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_54 0x431E428
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_55 0x431E42C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_56 0x431E430
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_57 0x431E434
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_58 0x431E438
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_59 0x431E43C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_60 0x431E440
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_61 0x431E444
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_62 0x431E448
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_PI_63 0x431E44C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_0 0x431E450
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_1 0x431E454
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_2 0x431E458
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_3 0x431E45C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_4 0x431E460
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_5 0x431E464
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_6 0x431E468
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_7 0x431E46C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_8 0x431E470
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_9 0x431E474
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_10 0x431E478
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_11 0x431E47C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_12 0x431E480
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_13 0x431E484
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_14 0x431E488
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_15 0x431E48C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_16 0x431E490
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_17 0x431E494
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_18 0x431E498
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_19 0x431E49C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_20 0x431E4A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_21 0x431E4A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_22 0x431E4A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_23 0x431E4AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_24 0x431E4B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_25 0x431E4B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_26 0x431E4B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_27 0x431E4BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_28 0x431E4C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_29 0x431E4C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_30 0x431E4C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_31 0x431E4CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_32 0x431E4D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_33 0x431E4D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_34 0x431E4D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_35 0x431E4DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_36 0x431E4E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_37 0x431E4E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_38 0x431E4E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_39 0x431E4EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_40 0x431E4F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_41 0x431E4F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_42 0x431E4F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_43 0x431E4FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_44 0x431E500
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_45 0x431E504
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_46 0x431E508
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_47 0x431E50C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_48 0x431E510
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_49 0x431E514
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_50 0x431E518
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_51 0x431E51C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_52 0x431E520
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_53 0x431E524
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_54 0x431E528
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_55 0x431E52C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_56 0x431E530
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_57 0x431E534
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_58 0x431E538
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_59 0x431E53C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_60 0x431E540
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_61 0x431E544
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_62 0x431E548
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_SEC_63 0x431E54C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_0 0x431E550
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_1 0x431E554
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_2 0x431E558
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_3 0x431E55C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_4 0x431E560
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_5 0x431E564
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_6 0x431E568
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_7 0x431E56C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_8 0x431E570
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_9 0x431E574
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_10 0x431E578
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_11 0x431E57C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_12 0x431E580
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_13 0x431E584
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_14 0x431E588
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_15 0x431E58C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_16 0x431E590
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_17 0x431E594
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_18 0x431E598
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_19 0x431E59C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_20 0x431E5A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_21 0x431E5A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_22 0x431E5A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_23 0x431E5AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_24 0x431E5B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_25 0x431E5B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_26 0x431E5B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_27 0x431E5BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_28 0x431E5C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_29 0x431E5C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_30 0x431E5C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_31 0x431E5CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_32 0x431E5D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_33 0x431E5D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_34 0x431E5D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_35 0x431E5DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_36 0x431E5E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_37 0x431E5E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_38 0x431E5E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_39 0x431E5EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_40 0x431E5F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_41 0x431E5F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_42 0x431E5F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_43 0x431E5FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_44 0x431E600
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_45 0x431E604
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_46 0x431E608
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_47 0x431E60C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_48 0x431E610
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_49 0x431E614
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_50 0x431E618
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_51 0x431E61C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_52 0x431E620
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_53 0x431E624
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_54 0x431E628
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_55 0x431E62C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_56 0x431E630
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_57 0x431E634
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_58 0x431E638
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_59 0x431E63C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_60 0x431E640
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_61 0x431E644
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_62 0x431E648
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_L_63 0x431E64C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_0 0x431E650
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_1 0x431E654
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_2 0x431E658
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_3 0x431E65C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_4 0x431E660
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_5 0x431E664
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_6 0x431E668
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_7 0x431E66C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_8 0x431E670
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_9 0x431E674
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_10 0x431E678
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_11 0x431E67C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_12 0x431E680
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_13 0x431E684
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_14 0x431E688
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_15 0x431E68C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_16 0x431E690
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_17 0x431E694
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_18 0x431E698
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_19 0x431E69C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_20 0x431E6A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_21 0x431E6A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_22 0x431E6A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_23 0x431E6AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_24 0x431E6B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_25 0x431E6B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_26 0x431E6B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_27 0x431E6BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_28 0x431E6C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_29 0x431E6C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_30 0x431E6C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_31 0x431E6CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_32 0x431E6D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_33 0x431E6D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_34 0x431E6D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_35 0x431E6DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_36 0x431E6E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_37 0x431E6E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_38 0x431E6E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_39 0x431E6EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_40 0x431E6F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_41 0x431E6F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_42 0x431E6F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_43 0x431E6FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_44 0x431E700
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_45 0x431E704
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_46 0x431E708
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_47 0x431E70C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_48 0x431E710
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_49 0x431E714
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_50 0x431E718
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_51 0x431E71C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_52 0x431E720
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_53 0x431E724
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_54 0x431E728
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_55 0x431E72C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_56 0x431E730
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_57 0x431E734
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_58 0x431E738
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_59 0x431E73C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_60 0x431E740
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_61 0x431E744
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_62 0x431E748
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_ADDR_H_63 0x431E74C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_0 0x431E750
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_1 0x431E754
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_2 0x431E758
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_3 0x431E75C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_4 0x431E760
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_5 0x431E764
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_6 0x431E768
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_7 0x431E76C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_8 0x431E770
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_9 0x431E774
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_10 0x431E778
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_11 0x431E77C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_12 0x431E780
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_13 0x431E784
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_14 0x431E788
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_15 0x431E78C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_16 0x431E790
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_17 0x431E794
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_18 0x431E798
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_19 0x431E79C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_20 0x431E7A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_21 0x431E7A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_22 0x431E7A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_23 0x431E7AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_24 0x431E7B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_25 0x431E7B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_26 0x431E7B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_27 0x431E7BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_28 0x431E7C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_29 0x431E7C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_30 0x431E7C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_31 0x431E7CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_32 0x431E7D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_33 0x431E7D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_34 0x431E7D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_35 0x431E7DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_36 0x431E7E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_37 0x431E7E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_38 0x431E7E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_39 0x431E7EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_40 0x431E7F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_41 0x431E7F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_42 0x431E7F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_43 0x431E7FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_44 0x431E800
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_45 0x431E804
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_46 0x431E808
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_47 0x431E80C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_48 0x431E810
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_49 0x431E814
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_50 0x431E818
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_51 0x431E81C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_52 0x431E820
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_53 0x431E824
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_54 0x431E828
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_55 0x431E82C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_56 0x431E830
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_57 0x431E834
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_58 0x431E838
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_59 0x431E83C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_60 0x431E840
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_61 0x431E844
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_62 0x431E848
+
+#define mmDCORE1_SYNC_MNGR_GLBL_LBW_DATA_63 0x431E84C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_0 0x431E850
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_1 0x431E854
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_2 0x431E858
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_3 0x431E85C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_4 0x431E860
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_5 0x431E864
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_6 0x431E868
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_7 0x431E86C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_8 0x431E870
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_9 0x431E874
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_10 0x431E878
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_11 0x431E87C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_12 0x431E880
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_13 0x431E884
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_14 0x431E888
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_15 0x431E88C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_16 0x431E890
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_17 0x431E894
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_18 0x431E898
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_19 0x431E89C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_20 0x431E8A0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_21 0x431E8A4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_22 0x431E8A8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_23 0x431E8AC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_24 0x431E8B0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_25 0x431E8B4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_26 0x431E8B8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_27 0x431E8BC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_28 0x431E8C0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_29 0x431E8C4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_30 0x431E8C8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_31 0x431E8CC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_32 0x431E8D0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_33 0x431E8D4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_34 0x431E8D8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_35 0x431E8DC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_36 0x431E8E0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_37 0x431E8E4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_38 0x431E8E8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_39 0x431E8EC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_40 0x431E8F0
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_41 0x431E8F4
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_42 0x431E8F8
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_43 0x431E8FC
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_44 0x431E900
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_45 0x431E904
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_46 0x431E908
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_47 0x431E90C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_48 0x431E910
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_49 0x431E914
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_50 0x431E918
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_51 0x431E91C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_52 0x431E920
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_53 0x431E924
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_54 0x431E928
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_55 0x431E92C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_56 0x431E930
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_57 0x431E934
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_58 0x431E938
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_59 0x431E93C
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_60 0x431E940
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_61 0x431E944
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_62 0x431E948
+
+#define mmDCORE1_SYNC_MNGR_GLBL_CQ_INC_MODE_63 0x431E94C
+
+#endif /* ASIC_REG_DCORE1_SYNC_MNGR_GLBL_REGS_H_ */
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore3_mme_ctrl_lo_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore3_mme_ctrl_lo_regs.h
index b06469f5a279..b06469f5a279 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/dcore3_mme_ctrl_lo_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/dcore3_mme_ctrl_lo_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_blocks_linux_driver.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/gaudi2_blocks_linux_driver.h
index 3caee4515ad6..3caee4515ad6 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_blocks_linux_driver.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/gaudi2_blocks_linux_driver.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h
index d0e2c68a639f..d21fcd3880b4 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
*
- * Copyright 2020-2022 HabanaLabs, Ltd.
+ * Copyright 2020-2023 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
@@ -31,6 +31,7 @@
#include "dcore0_sync_mngr_objs_regs.h"
#include "dcore0_sync_mngr_glbl_regs.h"
#include "dcore0_sync_mngr_mstr_if_axuser_regs.h"
+#include "dcore1_sync_mngr_glbl_regs.h"
#include "pdma0_qm_arc_aux_regs.h"
#include "pdma0_core_ctx_regs.h"
#include "pdma0_core_regs.h"
@@ -132,6 +133,7 @@
#include "dcore0_mme_ctrl_lo_arch_tensor_a_regs.h"
#include "dcore0_mme_ctrl_lo_arch_tensor_b_regs.h"
#include "dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h"
+#include "pcie_wrap_special_regs.h"
#include "pdma0_qm_masks.h"
#include "pdma0_core_masks.h"
@@ -162,6 +164,8 @@
#define mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR 0x4800040
+#define mmDCORE0_TPC0_EML_CFG_DBG_CNT 0x40000
+
#define SM_OBJS_PROT_BITS_OFFS 0x14000
#define DCORE_OFFSET (mmDCORE1_TPC0_QM_BASE - mmDCORE0_TPC0_QM_BASE)
@@ -183,7 +187,10 @@
#define TPC_CFG_STALL_ON_ERR_OFFSET (mmDCORE0_TPC0_CFG_STALL_ON_ERR - mmDCORE0_TPC0_CFG_BASE)
#define TPC_CFG_TPC_INTR_MASK_OFFSET (mmDCORE0_TPC0_CFG_TPC_INTR_MASK - mmDCORE0_TPC0_CFG_BASE)
#define TPC_CFG_MSS_CONFIG_OFFSET (mmDCORE0_TPC0_CFG_MSS_CONFIG - mmDCORE0_TPC0_CFG_BASE)
+#define TPC_EML_CFG_DBG_CNT_OFFSET (mmDCORE0_TPC0_EML_CFG_DBG_CNT - mmDCORE0_TPC0_EML_CFG_BASE)
+#define EDMA_CORE_CFG_STALL_OFFSET (mmDCORE0_EDMA0_CORE_CFG_1 - mmDCORE0_EDMA0_CORE_BASE)
+#define MME_CTRL_LO_QM_STALL_OFFSET (mmDCORE0_MME_CTRL_LO_QM_STALL - mmDCORE0_MME_CTRL_LO_BASE)
#define MME_ACC_INTR_MASK_OFFSET (mmDCORE0_MME_ACC_INTR_MASK - mmDCORE0_MME_ACC_BASE)
#define MME_ACC_WR_AXI_AGG_COUT0_OFFSET (mmDCORE0_MME_ACC_WR_AXI_AGG_COUT0 - mmDCORE0_MME_ACC_BASE)
#define MME_ACC_WR_AXI_AGG_COUT1_OFFSET (mmDCORE0_MME_ACC_WR_AXI_AGG_COUT1 - mmDCORE0_MME_ACC_BASE)
@@ -235,10 +242,23 @@
#define QM_FENCE2_OFFSET (mmPDMA0_QM_CP_FENCE2_RDATA_0 - mmPDMA0_QM_BASE)
#define QM_SEI_STATUS_OFFSET (mmPDMA0_QM_SEI_STATUS - mmPDMA0_QM_BASE)
+#define QM_CQ_TSIZE_STS_4_OFFSET (mmPDMA0_QM_CQ_TSIZE_STS_4 - mmPDMA0_QM_BASE)
+#define QM_CQ_PTR_LO_STS_4_OFFSET (mmPDMA0_QM_CQ_PTR_LO_STS_4 - mmPDMA0_QM_BASE)
+#define QM_CQ_PTR_HI_STS_4_OFFSET (mmPDMA0_QM_CQ_PTR_HI_STS_4 - mmPDMA0_QM_BASE)
+
+#define QM_ARC_CQ_TSIZE_STS_OFFSET (mmPDMA0_QM_ARC_CQ_TSIZE_STS - mmPDMA0_QM_BASE)
+#define QM_ARC_CQ_PTR_LO_STS_OFFSET (mmPDMA0_QM_ARC_CQ_PTR_LO_STS - mmPDMA0_QM_BASE)
+#define QM_ARC_CQ_PTR_HI_STS_OFFSET (mmPDMA0_QM_ARC_CQ_PTR_HI_STS - mmPDMA0_QM_BASE)
+
+#define QM_CP_STS_4_OFFSET (mmPDMA0_QM_CP_STS_4 - mmPDMA0_QM_BASE)
+#define QM_CP_CURRENT_INST_LO_4_OFFSET (mmPDMA0_QM_CP_CURRENT_INST_LO_4 - mmPDMA0_QM_BASE)
+#define QM_CP_CURRENT_INST_HI_4_OFFSET (mmPDMA0_QM_CP_CURRENT_INST_HI_4 - mmPDMA0_QM_BASE)
+
#define SFT_OFFSET (mmSFT1_HBW_RTR_IF0_RTR_H3_BASE - mmSFT0_HBW_RTR_IF0_RTR_H3_BASE)
#define SFT_IF_RTR_OFFSET (mmSFT0_HBW_RTR_IF1_RTR_H3_BASE - mmSFT0_HBW_RTR_IF0_RTR_H3_BASE)
#define ARC_HALT_REQ_OFFSET (mmARC_FARM_ARC0_AUX_RUN_HALT_REQ - mmARC_FARM_ARC0_AUX_BASE)
+#define ARC_HALT_ACK_OFFSET (mmARC_FARM_ARC0_AUX_RUN_HALT_ACK - mmARC_FARM_ARC0_AUX_BASE)
#define ARC_REGION_CFG_OFFSET(region) \
(mmARC_FARM_ARC0_AUX_ARC_REGION_CFG_0 + (region * 4) - mmARC_FARM_ARC0_AUX_BASE)
@@ -535,6 +555,8 @@
#define HBM_MC_SPI_IEEE1500_COMP_MASK BIT(3)
#define HBM_MC_SPI_IEEE1500_PAUSED_MASK BIT(4)
+#define ARC_FARM_OFFSET (mmARC_FARM_ARC1_AUX_BASE - mmARC_FARM_ARC0_AUX_BASE)
+
#include "nic0_qpc0_regs.h"
#include "nic0_qm0_regs.h"
#include "nic0_qm_arc_aux0_regs.h"
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm0_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm0_cgm_regs.h
index d49906a68511..d49906a68511 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm0_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm0_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm0_regs.h
index acb19c1cd4bd..acb19c1cd4bd 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm_arc_aux0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm_arc_aux0_regs.h
index 5f380a44dd21..5f380a44dd21 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qm_arc_aux0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qm_arc_aux0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qpc0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qpc0_regs.h
index eaee29da4244..eaee29da4244 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_qpc0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_qpc0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_completion_queue_ci_1_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_completion_queue_ci_1_regs.h
index 2153319a50a0..2153319a50a0 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_completion_queue_ci_1_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_completion_queue_ci_1_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_unsecure_doorbell0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_unsecure_doorbell0_regs.h
index de8eac74c2fb..de8eac74c2fb 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_unsecure_doorbell0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/nic0_umr0_0_unsecure_doorbell0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_aux_regs.h
index 44182fc18234..44182fc18234 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dbi_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dbi_regs.h
index cc5842ec6ceb..2ee79d8e62d0 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dbi_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dbi_regs.h
@@ -48,8 +48,7 @@
#define mmPCIE_DBI_PCI_CAP_PTR_REG 0x4C02034
-#define mmPCIE_DBI_MAX_LATENCY_MIN_GRANT_INTERRUPT_PIN_INTERRUPT_LINE_REG \
-0x4C0203C
+#define mmPCIE_DBI_MAX_LATENCY_MIN_GRANT_INTERRUPT_PIN_INTERRUPT_LINE_REG 0x4C0203C
#define mmPCIE_DBI_CAP_ID_NXT_PTR_REG 0x4C02040
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_masks.h
index 2b5af010c7a5..2b5af010c7a5 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_regs.h
index dc7d3f6a4b50..dc7d3f6a4b50 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_dec0_cmd_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_dec_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_dec_regs.h
index 242c6525bd71..242c6525bd71 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_dec_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_dec_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h
index 98d035463561..98d035463561 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_abnrm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h
index 33ef37619417..33ef37619417 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_l2c_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h
index c4587d5d6406..c4587d5d6406 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_nrm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h
index 35349ad375d0..35349ad375d0 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_axuser_msix_vcd_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_masks.h
index d29837883216..7a96aebf08b3 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_masks.h
@@ -116,8 +116,7 @@
#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARBURST_VIOL_SHIFT 7
#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARBURST_VIOL_MASK 0x80
#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARADDR_SIZE_ALIGN_VIOL_SHIFT 8
-#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARADDR_SIZE_ALIGN_VIOL_MASK \
-0x100
+#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARADDR_SIZE_ALIGN_VIOL_MASK 0x100
#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARSIZE_VIOL_SHIFT 9
#define PCIE_VDEC0_BRDG_CTRL_HBW_AXI_VIOL_CAUSE_ARSIZE_VIOL_MASK 0x200
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_regs.h
index c7badd212f2b..c7badd212f2b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_brdg_ctrl_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_ctrl_special_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_ctrl_special_regs.h
index 491b0cd935af..491b0cd935af 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_ctrl_special_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_vdec0_ctrl_special_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_regs.h
index a09422f2f281..a09422f2f281 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_regs.h
diff --git a/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h
new file mode 100644
index 000000000000..46558e7a7f63
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2016-2020 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+/************************************
+ ** This is an auto-generated file **
+ ** DO NOT EDIT BELOW **
+ ************************************/
+
+#ifndef ASIC_REG_PCIE_WRAP_SPECIAL_REGS_H_
+#define ASIC_REG_PCIE_WRAP_SPECIAL_REGS_H_
+
+/*
+ *****************************************
+ * PCIE_WRAP_SPECIAL
+ * (Prototype: SPECIAL_REGS)
+ *****************************************
+ */
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_0 0x4C01E80
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_1 0x4C01E84
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_2 0x4C01E88
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_3 0x4C01E8C
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_4 0x4C01E90
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_5 0x4C01E94
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_6 0x4C01E98
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_7 0x4C01E9C
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_8 0x4C01EA0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_9 0x4C01EA4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_10 0x4C01EA8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_11 0x4C01EAC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_12 0x4C01EB0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_13 0x4C01EB4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_14 0x4C01EB8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_15 0x4C01EBC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_16 0x4C01EC0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_17 0x4C01EC4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_18 0x4C01EC8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_19 0x4C01ECC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_20 0x4C01ED0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_21 0x4C01ED4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_22 0x4C01ED8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_23 0x4C01EDC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_24 0x4C01EE0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_25 0x4C01EE4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_26 0x4C01EE8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_27 0x4C01EEC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_28 0x4C01EF0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_29 0x4C01EF4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_30 0x4C01EF8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_31 0x4C01EFC
+
+#define mmPCIE_WRAP_SPECIAL_MEM_GW_DATA 0x4C01F00
+
+#define mmPCIE_WRAP_SPECIAL_MEM_GW_REQ 0x4C01F04
+
+#define mmPCIE_WRAP_SPECIAL_MEM_NUMOF 0x4C01F0C
+
+#define mmPCIE_WRAP_SPECIAL_MEM_ECC_SEL 0x4C01F10
+
+#define mmPCIE_WRAP_SPECIAL_MEM_ECC_CTL 0x4C01F14
+
+#define mmPCIE_WRAP_SPECIAL_MEM_ECC_ERR_MASK 0x4C01F18
+
+#define mmPCIE_WRAP_SPECIAL_MEM_ECC_GLBL_ERR_MASK 0x4C01F1C
+
+#define mmPCIE_WRAP_SPECIAL_MEM_ECC_ERR_STS 0x4C01F20
+
+#define mmPCIE_WRAP_SPECIAL_MEM_ECC_ERR_ADDR 0x4C01F24
+
+#define mmPCIE_WRAP_SPECIAL_MEM_RM 0x4C01F28
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_ERR_MASK 0x4C01F40
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_ERR_ADDR 0x4C01F44
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_ERR_CAUSE 0x4C01F48
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_0 0x4C01F60
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_1 0x4C01F64
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_2 0x4C01F68
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_3 0x4C01F6C
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_0 0x4C01F80
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_1 0x4C01F84
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_2 0x4C01F88
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_3 0x4C01F8C
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_4 0x4C01F90
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_5 0x4C01F94
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_6 0x4C01F98
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_7 0x4C01F9C
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_8 0x4C01FA0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_9 0x4C01FA4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_10 0x4C01FA8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_11 0x4C01FAC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_12 0x4C01FB0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_13 0x4C01FB4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_14 0x4C01FB8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_15 0x4C01FBC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_16 0x4C01FC0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_17 0x4C01FC4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_18 0x4C01FC8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_19 0x4C01FCC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_20 0x4C01FD0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_21 0x4C01FD4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_22 0x4C01FD8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_23 0x4C01FDC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_24 0x4C01FE0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_25 0x4C01FE4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_26 0x4C01FE8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_27 0x4C01FEC
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_28 0x4C01FF0
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_29 0x4C01FF4
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_30 0x4C01FF8
+
+#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_31 0x4C01FFC
+
+#endif /* ASIC_REG_PCIE_WRAP_SPECIAL_REGS_H_ */
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_axuser_regs.h
index bacbe4c6fc3c..bacbe4c6fc3c 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_regs.h
index 02b57f07cfaf..02b57f07cfaf 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_ctx_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_masks.h
index 909cda03c246..909cda03c246 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_regs.h
index 84079b5077e2..84079b5077e2 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_special_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_special_masks.h
index 15d257e3830e..15d257e3830e 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_core_special_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_core_special_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_arc_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_arc_aux_regs.h
index 9b1cb609d134..9b1cb609d134 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_arc_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_arc_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_nonsecured_regs.h
index d2e0756ec5f2..d2e0756ec5f2 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_secured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_secured_regs.h
index 8bf0516b83f7..8bf0516b83f7 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_secured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_axuser_secured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_cgm_regs.h
index 96c0ce176e73..96c0ce176e73 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_masks.h
index b79cae8f5571..b79cae8f5571 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_regs.h
index 77d803c938d4..77d803c938d4 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma1_core_ctx_axuser_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma1_core_ctx_axuser_regs.h
index ccc6dfd22dd7..ccc6dfd22dd7 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma1_core_ctx_axuser_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma1_core_ctx_axuser_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma1_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma1_qm_axuser_nonsecured_regs.h
index 5fd72d050fff..5fd72d050fff 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pdma1_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pdma1_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_masks.h
index 0276506ea523..b4f32632cd36 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_masks.h
@@ -228,8 +228,7 @@
/* PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION */
#define PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_SHIFT 0
-#define PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_MASK \
-0x1
+#define PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION_RANGE_INVALIDATION_ENABLE_MASK 0x1
#define PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION_INVALIDATION_ASID_EN_SHIFT 1
#define PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION_INVALIDATION_ASID_EN_MASK 0x2
#define PMMU_HBW_STLB_RANGE_CACHE_INVALIDATION_INVALIDATION_ASID_SHIFT 2
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_regs.h
index 87c66c08e24a..87c66c08e24a 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_hbw_stlb_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_pif_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_pif_regs.h
index dd12793734b4..dd12793734b4 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pmmu_pif_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/pmmu_pif_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_etr_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_etr_masks.h
index 42e67c1059c4..42e67c1059c4 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_etr_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_etr_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_etr_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_etr_regs.h
index 980a3e0054c5..980a3e0054c5 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_etr_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_etr_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_masks.h
index 9be3d656da3a..85a81e2cb546 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_masks.h
@@ -1306,11 +1306,9 @@
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_PC_LOC2_SHIFT 12
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_PC_LOC2_MASK 0x3F000
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_PC_LOC3_SHIFT 18
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_PC_LOC3_MASK \
-0xFC0000
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_PC_LOC3_MASK 0xFC0000
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_HBM_LOC0_SHIFT 24
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_HBM_LOC0_MASK \
-0x3F000000
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL0_ADDR_EXTMEM_HBM_LOC0_MASK 0x3F000000
/* PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1 */
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_ADDR_EXTMEM_HBM_LOC1_SHIFT 0
@@ -1322,24 +1320,17 @@
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_CNT_EN_SHIFT 13
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_CNT_EN_MASK 0x2000
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_EN_SHIFT 14
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_EN_MASK \
-0x4000
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_MASK_SHIFT \
-16
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_MASK_MASK \
-0xFF0000
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_EN_MASK 0x4000
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_MASK_SHIFT 16
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_NON_LIN_HBM_ALL_ADDR_MASK_MASK 0xFF0000
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_HBM_NUM_SHIFT 24
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL1_HBM_NUM_MASK 0x7000000
/* PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2 */
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_HBM_CNT_MASK_SHIFT \
-0
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_HBM_CNT_MASK_MASK \
-0xFFFF
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_EXTM_PC_MASK_SHIFT \
-16
-#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_EXTM_PC_MASK_MASK \
-0xFFFF0000
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_HBM_CNT_MASK_SHIFT 0
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_HBM_CNT_MASK_MASK 0xFFFF
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_EXTM_PC_MASK_SHIFT 16
+#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL2_SCRAM_NONLIN_EXTM_PC_MASK_MASK 0xFFFF0000
/* PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL3 */
#define PSOC_GLOBAL_CONF_AXI_DRAIN_NL_SRC_CTRL3_HBM_MAP0_SHIFT 0
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_regs.h
index 48980fa8e37b..48980fa8e37b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_global_conf_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_masks.h
index e0cf35226e7f..e0cf35226e7f 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_regs.h
index 6a89624f01d1..6a89624f01d1 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_reset_conf_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_timestamp_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_timestamp_regs.h
index 699becc28887..699becc28887 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/psoc_timestamp_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/psoc_timestamp_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_desc_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_desc_regs.h
index 79320320ebcb..79320320ebcb 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_desc_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_desc_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_masks.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_masks.h
index f2e739ede3d9..f2e739ede3d9 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_masks.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_masks.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_arc_aux_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_arc_aux_regs.h
index e83daa33d737..e83daa33d737 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_arc_aux_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_arc_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_axuser_nonsecured_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_axuser_nonsecured_regs.h
index 8e040a2ef1c1..8e040a2ef1c1 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_axuser_nonsecured_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_axuser_nonsecured_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_cgm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_cgm_regs.h
index 077ae2347a3d..077ae2347a3d 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_cgm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_cgm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_regs.h
index de3c85510af2..de3c85510af2 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_regs.h
index 7d85dc5559da..7d85dc5559da 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/rot0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/rot0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/xbar_edge_0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/xbar_edge_0_regs.h
index e8aebd7f5f85..e8aebd7f5f85 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/xbar_edge_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/xbar_edge_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/xbar_mid_0_regs.h b/drivers/accel/habanalabs/include/gaudi2/asic_reg/xbar_mid_0_regs.h
index 3d39d1a94851..3d39d1a94851 100644
--- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/xbar_mid_0_regs.h
+++ b/drivers/accel/habanalabs/include/gaudi2/asic_reg/xbar_mid_0_regs.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/gaudi2.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2.h
index 5b4f9e108798..753d46a2836b 100644
--- a/drivers/misc/habanalabs/include/gaudi2/gaudi2.h
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2.h
@@ -63,7 +63,9 @@
#define RESERVED_VA_RANGE_FOR_ARC_ON_HOST_HPAGE_START 0xFFF0F80000000000ull
#define RESERVED_VA_RANGE_FOR_ARC_ON_HOST_HPAGE_END 0xFFF0FFFFFFFFFFFFull
-#define GAUDI2_MSIX_ENTRIES 512
+#define RESERVED_MSIX_UNEXPECTED_USER_ERROR_INTERRUPT 127
+
+#define GAUDI2_MSIX_ENTRIES 128
#define QMAN_PQ_ENTRY_SIZE 16 /* Bytes */
diff --git a/drivers/misc/habanalabs/include/gaudi2/gaudi2_async_events.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_async_events.h
index 34406770a76a..a426410139af 100644
--- a/drivers/misc/habanalabs/include/gaudi2/gaudi2_async_events.h
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_async_events.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0
*
- * Copyright 2018-2021 HabanaLabs, Ltd.
+ * Copyright 2018-2022 HabanaLabs, Ltd.
* All Rights Reserved.
*
*/
@@ -957,6 +957,15 @@ enum gaudi2_async_event_id {
GAUDI2_EVENT_CPU11_STATUS_NIC11_ENG0 = 1317,
GAUDI2_EVENT_CPU11_STATUS_NIC11_ENG1 = 1318,
GAUDI2_EVENT_ARC_DCCM_FULL = 1319,
+ GAUDI2_EVENT_CPU_FP32_NOT_SUPPORTED = 1320,
+ GAUDI2_EVENT_CPU_DEV_RESET_REQ = 1321,
+ GAUDI2_EVENT_ARC_PWR_BRK_ENTRY = 1322,
+ GAUDI2_EVENT_ARC_PWR_BRK_EXT = 1323,
+ GAUDI2_EVENT_ARC_PWR_RD_MODE0 = 1324,
+ GAUDI2_EVENT_ARC_PWR_RD_MODE1 = 1325,
+ GAUDI2_EVENT_ARC_PWR_RD_MODE2 = 1326,
+ GAUDI2_EVENT_ARC_PWR_RD_MODE3 = 1327,
+ GAUDI2_EVENT_ARC_EQ_HEARTBEAT = 1328,
GAUDI2_EVENT_SIZE,
};
diff --git a/drivers/accel/habanalabs/include/gaudi2/gaudi2_async_ids_map_extended.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_async_ids_map_extended.h
new file mode 100644
index 000000000000..82d639990cca
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_async_ids_map_extended.h
@@ -0,0 +1,2692 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2018-2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+/************************************
+ ** This is an auto-generated file **
+ ** DO NOT EDIT BELOW **
+ ************************************/
+
+enum event_reset_type {
+ EVENT_RESET_TYPE_NONE,
+ EVENT_RESET_TYPE_COMPUTE,
+ EVENT_RESET_TYPE_HARD,
+};
+
+#ifndef __GAUDI2_ASYNC_IDS_MAP_EVENTS_EXT_H_
+#define __GAUDI2_ASYNC_IDS_MAP_EVENTS_EXT_H_
+
+struct gaudi2_async_events_ids_map {
+ int fc_id;
+ int cpu_id;
+ int valid;
+ int msg;
+ int reset;
+ char name[64];
+};
+
+static struct gaudi2_async_events_ids_map gaudi2_irq_map_table[] = {
+ { .fc_id = 0, .cpu_id = 0, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1, .cpu_id = 1, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 2, .cpu_id = 2, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 3, .cpu_id = 3, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 4, .cpu_id = 4, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 5, .cpu_id = 5, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 6, .cpu_id = 6, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 7, .cpu_id = 7, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 8, .cpu_id = 8, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 9, .cpu_id = 9, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 10, .cpu_id = 10, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 11, .cpu_id = 11, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 12, .cpu_id = 12, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 13, .cpu_id = 13, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 14, .cpu_id = 14, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 15, .cpu_id = 15, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 16, .cpu_id = 16, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 17, .cpu_id = 17, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 18, .cpu_id = 18, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 19, .cpu_id = 19, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 20, .cpu_id = 20, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 21, .cpu_id = 21, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 22, .cpu_id = 22, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 23, .cpu_id = 23, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 24, .cpu_id = 24, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 25, .cpu_id = 25, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 26, .cpu_id = 26, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 27, .cpu_id = 27, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 28, .cpu_id = 28, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 29, .cpu_id = 29, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 30, .cpu_id = 30, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 31, .cpu_id = 31, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 32, .cpu_id = 32, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PCIE_CORE_SERR" },
+ { .fc_id = 33, .cpu_id = 33, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PCIE_CORE_DERR" },
+ { .fc_id = 34, .cpu_id = 34, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PCIE_IF_SERR" },
+ { .fc_id = 35, .cpu_id = 35, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PCIE_IF_DERR" },
+ { .fc_id = 36, .cpu_id = 36, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PCIE_PHY_SERR" },
+ { .fc_id = 37, .cpu_id = 37, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PCIE_PHY_DERR" },
+ { .fc_id = 38, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC0_ECC_SERR" },
+ { .fc_id = 39, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC1_ECC_SERR" },
+ { .fc_id = 40, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC2_ECC_SERR" },
+ { .fc_id = 41, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC3_ECC_SERR" },
+ { .fc_id = 42, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC4_ECC_SERR" },
+ { .fc_id = 43, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC5_ECC_SERR" },
+ { .fc_id = 44, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC6_ECC_SERR" },
+ { .fc_id = 45, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC7_ECC_SERR" },
+ { .fc_id = 46, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC8_ECC_SERR" },
+ { .fc_id = 47, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC9_ECC_SERR" },
+ { .fc_id = 48, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC10_ECC_SERR" },
+ { .fc_id = 49, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC11_ECC_SERR" },
+ { .fc_id = 50, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC12_ECC_SERR" },
+ { .fc_id = 51, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC13_ECC_SERR" },
+ { .fc_id = 52, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC14_ECC_SERR" },
+ { .fc_id = 53, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC15_ECC_SERR" },
+ { .fc_id = 54, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC16_ECC_SERR" },
+ { .fc_id = 55, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC17_ECC_SERR" },
+ { .fc_id = 56, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC18_ECC_SERR" },
+ { .fc_id = 57, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC19_ECC_SERR" },
+ { .fc_id = 58, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC20_ECC_SERR" },
+ { .fc_id = 59, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC21_ECC_SERR" },
+ { .fc_id = 60, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC22_ECC_SERR" },
+ { .fc_id = 61, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC23_ECC_SERR" },
+ { .fc_id = 62, .cpu_id = 38, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC24_ECC_SERR" },
+ { .fc_id = 63, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC0_ECC_DERR" },
+ { .fc_id = 64, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC1_ECC_DERR" },
+ { .fc_id = 65, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC2_ECC_DERR" },
+ { .fc_id = 66, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC3_ECC_DERR" },
+ { .fc_id = 67, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC4_ECC_DERR" },
+ { .fc_id = 68, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC5_ECC_DERR" },
+ { .fc_id = 69, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC6_ECC_DERR" },
+ { .fc_id = 70, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC7_ECC_DERR" },
+ { .fc_id = 71, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC8_ECC_DERR" },
+ { .fc_id = 72, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC9_ECC_DERR" },
+ { .fc_id = 73, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC10_ECC_DERR" },
+ { .fc_id = 74, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC11_ECC_DERR" },
+ { .fc_id = 75, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC12_ECC_DERR" },
+ { .fc_id = 76, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC13_ECC_DERR" },
+ { .fc_id = 77, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC14_ECC_DERR" },
+ { .fc_id = 78, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC15_ECC_DERR" },
+ { .fc_id = 79, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC16_ECC_DERR" },
+ { .fc_id = 80, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC17_ECC_DERR" },
+ { .fc_id = 81, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC18_ECC_DERR" },
+ { .fc_id = 82, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC19_ECC_DERR" },
+ { .fc_id = 83, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC20_ECC_DERR" },
+ { .fc_id = 84, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC21_ECC_DERR" },
+ { .fc_id = 85, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC22_ECC_DERR" },
+ { .fc_id = 86, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC23_ECC_DERR" },
+ { .fc_id = 87, .cpu_id = 39, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "TPC24_ECC_DERR" },
+ { .fc_id = 88, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_SBTE0_ECC_SERR" },
+ { .fc_id = 89, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_SBTE1_ECC_SERR" },
+ { .fc_id = 90, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_SBTE2_ECC_SERR" },
+ { .fc_id = 91, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_SBTE3_ECC_SERR" },
+ { .fc_id = 92, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_SBTE4_ECC_SERR" },
+ { .fc_id = 93, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_CTRL_ECC_SERR" },
+ { .fc_id = 94, .cpu_id = 40, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_WAP_ECC_SERR" },
+ { .fc_id = 95, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_SBTE0_ECC_SERR" },
+ { .fc_id = 96, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_SBTE1_ECC_SERR" },
+ { .fc_id = 97, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_SBTE2_ECC_SERR" },
+ { .fc_id = 98, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_SBTE3_ECC_SERR" },
+ { .fc_id = 99, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_SBTE4_ECC_SERR" },
+ { .fc_id = 100, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_CTRL_ECC_SERR" },
+ { .fc_id = 101, .cpu_id = 41, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_WAP_ECC_SERR" },
+ { .fc_id = 102, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_SBTE0_ECC_SERR" },
+ { .fc_id = 103, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_SBTE1_ECC_SERR" },
+ { .fc_id = 104, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_SBTE2_ECC_SERR" },
+ { .fc_id = 105, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_SBTE3_ECC_SERR" },
+ { .fc_id = 106, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_SBTE4_ECC_SERR" },
+ { .fc_id = 107, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_CTRL_ECC_SERR" },
+ { .fc_id = 108, .cpu_id = 42, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_WAP_ECC_SERR" },
+ { .fc_id = 109, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_SBTE0_ECC_SERR" },
+ { .fc_id = 110, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_SBTE1_ECC_SERR" },
+ { .fc_id = 111, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_SBTE2_ECC_SERR" },
+ { .fc_id = 112, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_SBTE3_ECC_SERR" },
+ { .fc_id = 113, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_SBTE4_ECC_SERR" },
+ { .fc_id = 114, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_CTRL_ECC_SERR" },
+ { .fc_id = 115, .cpu_id = 43, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_WAP_ECC_SERR" },
+ { .fc_id = 116, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_SBTE0_ECC_DERR" },
+ { .fc_id = 117, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_SBTE1_ECC_DERR" },
+ { .fc_id = 118, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_SBTE2_ECC_DERR" },
+ { .fc_id = 119, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_SBTE3_ECC_DERR" },
+ { .fc_id = 120, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_SBTE4_ECC_DERR" },
+ { .fc_id = 121, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_CTRL_ECC_DERR" },
+ { .fc_id = 122, .cpu_id = 44, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME0_WAP_ECC_DERR" },
+ { .fc_id = 123, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_SBTE0_ECC_DERR" },
+ { .fc_id = 124, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_SBTE1_ECC_DERR" },
+ { .fc_id = 125, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_SBTE2_ECC_DERR" },
+ { .fc_id = 126, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_SBTE3_ECC_DERR" },
+ { .fc_id = 127, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_SBTE4_ECC_DERR" },
+ { .fc_id = 128, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_CTRL_ECC_DERR" },
+ { .fc_id = 129, .cpu_id = 45, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME1_WAP_ECC_DERR" },
+ { .fc_id = 130, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_SBTE0_ECC_DERR" },
+ { .fc_id = 131, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_SBTE1_ECC_DERR" },
+ { .fc_id = 132, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_SBTE2_ECC_DERR" },
+ { .fc_id = 133, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_SBTE3_ECC_DERR" },
+ { .fc_id = 134, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_SBTE4_ECC_DERR" },
+ { .fc_id = 135, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_CTRL_ECC_DERR" },
+ { .fc_id = 136, .cpu_id = 46, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME2_WAP_ECC_DERR" },
+ { .fc_id = 137, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_SBTE0_ECC_DERR" },
+ { .fc_id = 138, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_SBTE1_ECC_DERR" },
+ { .fc_id = 139, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_SBTE2_ECC_DERR" },
+ { .fc_id = 140, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_SBTE3_ECC_DERR" },
+ { .fc_id = 141, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_SBTE4_ECC_DERR" },
+ { .fc_id = 142, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_CTRL_ECC_DERR" },
+ { .fc_id = 143, .cpu_id = 47, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "MME3_WAP_ECC_DERR" },
+ { .fc_id = 144, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA2_ECC_SERR" },
+ { .fc_id = 145, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA3_ECC_SERR" },
+ { .fc_id = 146, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA0_ECC_SERR" },
+ { .fc_id = 147, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA1_ECC_SERR" },
+ { .fc_id = 148, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA6_ECC_SERR" },
+ { .fc_id = 149, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA7_ECC_SERR" },
+ { .fc_id = 150, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA4_ECC_SERR" },
+ { .fc_id = 151, .cpu_id = 48, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA5_ECC_SERR" },
+ { .fc_id = 152, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA2_ECC_DERR" },
+ { .fc_id = 153, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA3_ECC_DERR" },
+ { .fc_id = 154, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA0_ECC_DERR" },
+ { .fc_id = 155, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA1_ECC_DERR" },
+ { .fc_id = 156, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA6_ECC_DERR" },
+ { .fc_id = 157, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA7_ECC_DERR" },
+ { .fc_id = 158, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA4_ECC_DERR" },
+ { .fc_id = 159, .cpu_id = 49, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "EDMA5_ECC_DERR" },
+ { .fc_id = 160, .cpu_id = 50, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "KDMA0_ECC_SERR" },
+ { .fc_id = 161, .cpu_id = 51, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PDMA0_ECC_SERR" },
+ { .fc_id = 162, .cpu_id = 51, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PDMA1_ECC_SERR" },
+ { .fc_id = 163, .cpu_id = 52, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "KDMA0_ECC_DERR" },
+ { .fc_id = 164, .cpu_id = 53, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PDMA0_ECC_DERR" },
+ { .fc_id = 165, .cpu_id = 53, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PDMA1_ECC_DERR" },
+ { .fc_id = 166, .cpu_id = 54, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "CPU_IF_ECC_SERR" },
+ { .fc_id = 167, .cpu_id = 55, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "CPU_IF_ECC_DERR" },
+ { .fc_id = 168, .cpu_id = 56, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PSOC_MEM_SERR" },
+ { .fc_id = 169, .cpu_id = 57, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PSOC_MEM_DERR" },
+ { .fc_id = 170, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM0_ECC_SERR" },
+ { .fc_id = 171, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM1_ECC_SERR" },
+ { .fc_id = 172, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM2_ECC_SERR" },
+ { .fc_id = 173, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM3_ECC_SERR" },
+ { .fc_id = 174, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM4_ECC_SERR" },
+ { .fc_id = 175, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM5_ECC_SERR" },
+ { .fc_id = 176, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM6_ECC_SERR" },
+ { .fc_id = 177, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM7_ECC_SERR" },
+ { .fc_id = 178, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM8_ECC_SERR" },
+ { .fc_id = 179, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM9_ECC_SERR" },
+ { .fc_id = 180, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM10_ECC_SERR" },
+ { .fc_id = 181, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM11_ECC_SERR" },
+ { .fc_id = 182, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM12_ECC_SERR" },
+ { .fc_id = 183, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM13_ECC_SERR" },
+ { .fc_id = 184, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM14_ECC_SERR" },
+ { .fc_id = 185, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM15_ECC_SERR" },
+ { .fc_id = 186, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM16_ECC_SERR" },
+ { .fc_id = 187, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM17_ECC_SERR" },
+ { .fc_id = 188, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM18_ECC_SERR" },
+ { .fc_id = 189, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM19_ECC_SERR" },
+ { .fc_id = 190, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM20_ECC_SERR" },
+ { .fc_id = 191, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM21_ECC_SERR" },
+ { .fc_id = 192, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM22_ECC_SERR" },
+ { .fc_id = 193, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM23_ECC_SERR" },
+ { .fc_id = 194, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM24_ECC_SERR" },
+ { .fc_id = 195, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM25_ECC_SERR" },
+ { .fc_id = 196, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM26_ECC_SERR" },
+ { .fc_id = 197, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM27_ECC_SERR" },
+ { .fc_id = 198, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM28_ECC_SERR" },
+ { .fc_id = 199, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM29_ECC_SERR" },
+ { .fc_id = 200, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM30_ECC_SERR" },
+ { .fc_id = 201, .cpu_id = 58, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SRAM31_ECC_SERR" },
+ { .fc_id = 202, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM0_ECC_DERR" },
+ { .fc_id = 203, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM1_ECC_DERR" },
+ { .fc_id = 204, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM2_ECC_DERR" },
+ { .fc_id = 205, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM3_ECC_DERR" },
+ { .fc_id = 206, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM4_ECC_DERR" },
+ { .fc_id = 207, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM5_ECC_DERR" },
+ { .fc_id = 208, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM6_ECC_DERR" },
+ { .fc_id = 209, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM7_ECC_DERR" },
+ { .fc_id = 210, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM8_ECC_DERR" },
+ { .fc_id = 211, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM9_ECC_DERR" },
+ { .fc_id = 212, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM10_ECC_DERR" },
+ { .fc_id = 213, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM11_ECC_DERR" },
+ { .fc_id = 214, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM12_ECC_DERR" },
+ { .fc_id = 215, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM13_ECC_DERR" },
+ { .fc_id = 216, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM14_ECC_DERR" },
+ { .fc_id = 217, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM15_ECC_DERR" },
+ { .fc_id = 218, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM16_ECC_DERR" },
+ { .fc_id = 219, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM17_ECC_DERR" },
+ { .fc_id = 220, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM18_ECC_DERR" },
+ { .fc_id = 221, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM19_ECC_DERR" },
+ { .fc_id = 222, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM20_ECC_DERR" },
+ { .fc_id = 223, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM21_ECC_DERR" },
+ { .fc_id = 224, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM22_ECC_DERR" },
+ { .fc_id = 225, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM23_ECC_DERR" },
+ { .fc_id = 226, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM24_ECC_DERR" },
+ { .fc_id = 227, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM25_ECC_DERR" },
+ { .fc_id = 228, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM26_ECC_DERR" },
+ { .fc_id = 229, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM27_ECC_DERR" },
+ { .fc_id = 230, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM28_ECC_DERR" },
+ { .fc_id = 231, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM29_ECC_DERR" },
+ { .fc_id = 232, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM30_ECC_DERR" },
+ { .fc_id = 233, .cpu_id = 59, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SRAM31_ECC_DERR" },
+ { .fc_id = 234, .cpu_id = 60, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "GIC500" },
+ { .fc_id = 235, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_0_MC0_ECC_SERR" },
+ { .fc_id = 236, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_1_MC0_ECC_SERR" },
+ { .fc_id = 237, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_2_MC0_ECC_SERR" },
+ { .fc_id = 238, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_3_MC0_ECC_SERR" },
+ { .fc_id = 239, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_4_MC0_ECC_SERR" },
+ { .fc_id = 240, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_5_MC0_ECC_SERR" },
+ { .fc_id = 241, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_0_MC1_ECC_SERR" },
+ { .fc_id = 242, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_1_MC1_ECC_SERR" },
+ { .fc_id = 243, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_2_MC1_ECC_SERR" },
+ { .fc_id = 244, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_3_MC1_ECC_SERR" },
+ { .fc_id = 245, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_4_MC1_ECC_SERR" },
+ { .fc_id = 246, .cpu_id = 61, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM_5_MC1_ECC_SERR" },
+ { .fc_id = 247, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_0_MC0_ECC_DERR" },
+ { .fc_id = 248, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_1_MC0_ECC_DERR" },
+ { .fc_id = 249, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_2_MC0_ECC_DERR" },
+ { .fc_id = 250, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_3_MC0_ECC_DERR" },
+ { .fc_id = 251, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_4_MC0_ECC_DERR" },
+ { .fc_id = 252, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_5_MC0_ECC_DERR" },
+ { .fc_id = 253, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_0_MC1_ECC_DERR" },
+ { .fc_id = 254, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_1_MC1_ECC_DERR" },
+ { .fc_id = 255, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_2_MC1_ECC_DERR" },
+ { .fc_id = 256, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_3_MC1_ECC_DERR" },
+ { .fc_id = 257, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_4_MC1_ECC_DERR" },
+ { .fc_id = 258, .cpu_id = 62, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_5_MC1_ECC_DERR" },
+ { .fc_id = 259, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_0_ECC_SERR" },
+ { .fc_id = 260, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_1_ECC_SERR" },
+ { .fc_id = 261, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_2_ECC_SERR" },
+ { .fc_id = 262, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_3_ECC_SERR" },
+ { .fc_id = 263, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_8_ECC_SERR" },
+ { .fc_id = 264, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_9_ECC_SERR" },
+ { .fc_id = 265, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_10_ECC_SERR" },
+ { .fc_id = 266, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_11_ECC_SERR" },
+ { .fc_id = 267, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_7_ECC_SERR" },
+ { .fc_id = 268, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_6_ECC_SERR" },
+ { .fc_id = 269, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_5_ECC_SERR" },
+ { .fc_id = 270, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_4_ECC_SERR" },
+ { .fc_id = 271, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_15_ECC_SERR" },
+ { .fc_id = 272, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_14_ECC_SERR" },
+ { .fc_id = 273, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_13_ECC_SERR" },
+ { .fc_id = 274, .cpu_id = 63, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU_12_ECC_SERR" },
+ { .fc_id = 275, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_0_ECC_DERR" },
+ { .fc_id = 276, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_1_ECC_DERR" },
+ { .fc_id = 277, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_2_ECC_DERR" },
+ { .fc_id = 278, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_3_ECC_DERR" },
+ { .fc_id = 279, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_8_ECC_DERR" },
+ { .fc_id = 280, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_9_ECC_DERR" },
+ { .fc_id = 281, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_10_ECC_DERR" },
+ { .fc_id = 282, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_11_ECC_DERR" },
+ { .fc_id = 283, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_7_ECC_DERR" },
+ { .fc_id = 284, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_6_ECC_DERR" },
+ { .fc_id = 285, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_5_ECC_DERR" },
+ { .fc_id = 286, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_4_ECC_DERR" },
+ { .fc_id = 287, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_15_ECC_DERR" },
+ { .fc_id = 288, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_14_ECC_DERR" },
+ { .fc_id = 289, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_13_ECC_DERR" },
+ { .fc_id = 290, .cpu_id = 64, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_12_ECC_DERR" },
+ { .fc_id = 291, .cpu_id = 65, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PMMU_ECC_SERR" },
+ { .fc_id = 292, .cpu_id = 66, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PMMU_ECC_DERR" },
+ { .fc_id = 293, .cpu_id = 67, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 294, .cpu_id = 68, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 295, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC0_VCD_ECC_SERR" },
+ { .fc_id = 296, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC1_VCD_ECC_SERR" },
+ { .fc_id = 297, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC2_VCD_ECC_SERR" },
+ { .fc_id = 298, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC3_VCD_ECC_SERR" },
+ { .fc_id = 299, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC4_VCD_ECC_SERR" },
+ { .fc_id = 300, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC5_VCD_ECC_SERR" },
+ { .fc_id = 301, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC6_VCD_ECC_SERR" },
+ { .fc_id = 302, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC7_VCD_ECC_SERR" },
+ { .fc_id = 303, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC8_VCD_ECC_SERR" },
+ { .fc_id = 304, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC9_VCD_ECC_SERR" },
+ { .fc_id = 305, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC0_L2C_ECC_SERR" },
+ { .fc_id = 306, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC1_L2C_ECC_SERR" },
+ { .fc_id = 307, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC2_L2C_ECC_SERR" },
+ { .fc_id = 308, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC3_L2C_ECC_SERR" },
+ { .fc_id = 309, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC4_L2C_ECC_SERR" },
+ { .fc_id = 310, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC5_L2C_ECC_SERR" },
+ { .fc_id = 311, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC6_L2C_ECC_SERR" },
+ { .fc_id = 312, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC7_L2C_ECC_SERR" },
+ { .fc_id = 313, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC8_L2C_ECC_SERR" },
+ { .fc_id = 314, .cpu_id = 69, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC9_L2C_ECC_SERR" },
+ { .fc_id = 315, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC0_VCD_ECC_DERR" },
+ { .fc_id = 316, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC1_VCD_ECC_DERR" },
+ { .fc_id = 317, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC2_VCD_ECC_DERR" },
+ { .fc_id = 318, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC3_VCD_ECC_DERR" },
+ { .fc_id = 319, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC4_VCD_ECC_DERR" },
+ { .fc_id = 320, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC5_VCD_ECC_DERR" },
+ { .fc_id = 321, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC6_VCD_ECC_DERR" },
+ { .fc_id = 322, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC7_VCD_ECC_DERR" },
+ { .fc_id = 323, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC8_VCD_ECC_DERR" },
+ { .fc_id = 324, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC9_VCD_ECC_DERR" },
+ { .fc_id = 325, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC0_L2C_ECC_DERR" },
+ { .fc_id = 326, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC1_L2C_ECC_DERR" },
+ { .fc_id = 327, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC2_L2C_ECC_DERR" },
+ { .fc_id = 328, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC3_L2C_ECC_DERR" },
+ { .fc_id = 329, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC4_L2C_ECC_DERR" },
+ { .fc_id = 330, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC5_L2C_ECC_DERR" },
+ { .fc_id = 331, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC6_L2C_ECC_DERR" },
+ { .fc_id = 332, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC7_L2C_ECC_DERR" },
+ { .fc_id = 333, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC8_L2C_ECC_DERR" },
+ { .fc_id = 334, .cpu_id = 70, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEC9_L2C_ECC_DERR" },
+ { .fc_id = 335, .cpu_id = 71, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 336, .cpu_id = 72, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 337, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF0_ECC_SERR" },
+ { .fc_id = 338, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF1_ECC_SERR" },
+ { .fc_id = 339, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF2_ECC_SERR" },
+ { .fc_id = 340, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF3_ECC_SERR" },
+ { .fc_id = 341, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF8_ECC_SERR" },
+ { .fc_id = 342, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF9_ECC_SERR" },
+ { .fc_id = 343, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF10_ECC_SERR" },
+ { .fc_id = 344, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF11_ECC_SERR" },
+ { .fc_id = 345, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF7_ECC_SERR" },
+ { .fc_id = 346, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF6_ECC_SERR" },
+ { .fc_id = 347, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF5_ECC_SERR" },
+ { .fc_id = 348, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF4_ECC_SERR" },
+ { .fc_id = 349, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF15_ECC_SERR" },
+ { .fc_id = 350, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF14_ECC_SERR" },
+ { .fc_id = 351, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF13_ECC_SERR" },
+ { .fc_id = 352, .cpu_id = 73, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HIF12_ECC_SERR" },
+ { .fc_id = 353, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF0_ECC_DERR" },
+ { .fc_id = 354, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF1_ECC_DERR" },
+ { .fc_id = 355, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF2_ECC_DERR" },
+ { .fc_id = 356, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF3_ECC_DERR" },
+ { .fc_id = 357, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF8_ECC_DERR" },
+ { .fc_id = 358, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF9_ECC_DERR" },
+ { .fc_id = 359, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF10_ECC_DERR" },
+ { .fc_id = 360, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF11_ECC_DERR" },
+ { .fc_id = 361, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF7_ECC_DERR" },
+ { .fc_id = 362, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF6_ECC_DERR" },
+ { .fc_id = 363, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF5_ECC_DERR" },
+ { .fc_id = 364, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF4_ECC_DERR" },
+ { .fc_id = 365, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF15_ECC_DERR" },
+ { .fc_id = 366, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF14_ECC_DERR" },
+ { .fc_id = 367, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF13_ECC_DERR" },
+ { .fc_id = 368, .cpu_id = 74, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF12_ECC_DERR" },
+ { .fc_id = 369, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC0_ECC_SERR" },
+ { .fc_id = 370, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC1_ECC_SERR" },
+ { .fc_id = 371, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC2_ECC_SERR" },
+ { .fc_id = 372, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC3_ECC_SERR" },
+ { .fc_id = 373, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC4_ECC_SERR" },
+ { .fc_id = 374, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC5_ECC_SERR" },
+ { .fc_id = 375, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC6_ECC_SERR" },
+ { .fc_id = 376, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC7_ECC_SERR" },
+ { .fc_id = 377, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC8_ECC_SERR" },
+ { .fc_id = 378, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC9_ECC_SERR" },
+ { .fc_id = 379, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC10_ECC_SERR" },
+ { .fc_id = 380, .cpu_id = 75, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC11_ECC_SERR" },
+ { .fc_id = 381, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC0_ECC_DERR" },
+ { .fc_id = 382, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC1_ECC_DERR" },
+ { .fc_id = 383, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC2_ECC_DERR" },
+ { .fc_id = 384, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC3_ECC_DERR" },
+ { .fc_id = 385, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC4_ECC_DERR" },
+ { .fc_id = 386, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC5_ECC_DERR" },
+ { .fc_id = 387, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC6_ECC_DERR" },
+ { .fc_id = 388, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC7_ECC_DERR" },
+ { .fc_id = 389, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC8_ECC_DERR" },
+ { .fc_id = 390, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC9_ECC_DERR" },
+ { .fc_id = 391, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC10_ECC_DERR" },
+ { .fc_id = 392, .cpu_id = 76, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC11_ECC_DERR" },
+ { .fc_id = 393, .cpu_id = 77, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SM0_ECC_DERR" },
+ { .fc_id = 394, .cpu_id = 77, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SM1_ECC_DERR" },
+ { .fc_id = 395, .cpu_id = 77, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SM2_ECC_DERR" },
+ { .fc_id = 396, .cpu_id = 77, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "SM3_ECC_DERR" },
+ { .fc_id = 397, .cpu_id = 78, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM0_ECC_SERR" },
+ { .fc_id = 398, .cpu_id = 78, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM1_ECC_SERR" },
+ { .fc_id = 399, .cpu_id = 78, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM2_ECC_SERR" },
+ { .fc_id = 400, .cpu_id = 78, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM3_ECC_SERR" },
+ { .fc_id = 401, .cpu_id = 79, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "XBAR0_ECC_SERR" },
+ { .fc_id = 402, .cpu_id = 79, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "XBAR1_ECC_SERR" },
+ { .fc_id = 403, .cpu_id = 79, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "XBAR2_ECC_SERR" },
+ { .fc_id = 404, .cpu_id = 79, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "XBAR3_ECC_SERR" },
+ { .fc_id = 405, .cpu_id = 80, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "XBAR0_ECC_DERR" },
+ { .fc_id = 406, .cpu_id = 80, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "XBAR1_ECC_DERR" },
+ { .fc_id = 407, .cpu_id = 80, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "XBAR2_ECC_DERR" },
+ { .fc_id = 408, .cpu_id = 80, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "XBAR3_ECC_DERR" },
+ { .fc_id = 409, .cpu_id = 81, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "ARC0_ECC_SERR" },
+ { .fc_id = 410, .cpu_id = 82, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "ARC0_ECC_DERR" },
+ { .fc_id = 411, .cpu_id = 83, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 412, .cpu_id = 84, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PCIE_ADDR_DEC_ERR" },
+ { .fc_id = 413, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC0_AXI_ERR_RSP" },
+ { .fc_id = 414, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC1_AXI_ERR_RSP" },
+ { .fc_id = 415, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC2_AXI_ERR_RSP" },
+ { .fc_id = 416, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC3_AXI_ERR_RSP" },
+ { .fc_id = 417, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC4_AXI_ERR_RSP" },
+ { .fc_id = 418, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC5_AXI_ERR_RSP" },
+ { .fc_id = 419, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC0_AXI_ERR_RSP" },
+ { .fc_id = 420, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC1_AXI_ERR_RSP" },
+ { .fc_id = 421, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC2_AXI_ERR_RSP" },
+ { .fc_id = 422, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC3_AXI_ERR_RSP" },
+ { .fc_id = 423, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC4_AXI_ERR_RSP" },
+ { .fc_id = 424, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC5_AXI_ERR_RSP" },
+ { .fc_id = 425, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC0_AXI_ERR_RSP" },
+ { .fc_id = 426, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC1_AXI_ERR_RSP" },
+ { .fc_id = 427, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC2_AXI_ERR_RSP" },
+ { .fc_id = 428, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC3_AXI_ERR_RSP" },
+ { .fc_id = 429, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC4_AXI_ERR_RSP" },
+ { .fc_id = 430, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC5_AXI_ERR_RSP" },
+ { .fc_id = 431, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC0_AXI_ERR_RSP" },
+ { .fc_id = 432, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC1_AXI_ERR_RSP" },
+ { .fc_id = 433, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC2_AXI_ERR_RSP" },
+ { .fc_id = 434, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC3_AXI_ERR_RSP" },
+ { .fc_id = 435, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC4_AXI_ERR_RSP" },
+ { .fc_id = 436, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC5_AXI_ERR_RSP" },
+ { .fc_id = 437, .cpu_id = 85, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE4_TPC0_AXI_ERR_RSP" },
+ { .fc_id = 438, .cpu_id = 86, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "AXI_ECC" },
+ { .fc_id = 439, .cpu_id = 87, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "L2_RAM_ECC" },
+ { .fc_id = 440, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_SBTE0_AXI_ERR_RSP" },
+ { .fc_id = 441, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_SBTE1_AXI_ERR_RSP" },
+ { .fc_id = 442, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_SBTE2_AXI_ERR_RSP" },
+ { .fc_id = 443, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_SBTE3_AXI_ERR_RSP" },
+ { .fc_id = 444, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_SBTE4_AXI_ERR_RSP" },
+ { .fc_id = 445, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_CTRL_AXI_ERROR_RESPONSE" },
+ { .fc_id = 446, .cpu_id = 88, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_QMAN_SW_ERROR" },
+ { .fc_id = 447, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_SBTE0_AXI_ERR_RSP" },
+ { .fc_id = 448, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_SBTE1_AXI_ERR_RSP" },
+ { .fc_id = 449, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_SBTE2_AXI_ERR_RSP" },
+ { .fc_id = 450, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_SBTE3_AXI_ERR_RSP" },
+ { .fc_id = 451, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_SBTE4_AXI_ERR_RSP" },
+ { .fc_id = 452, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_CTRL_AXI_ERROR_RESPONSE" },
+ { .fc_id = 453, .cpu_id = 89, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_QMAN_SW_ERROR" },
+ { .fc_id = 454, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_SBTE0_AXI_ERR_RSP" },
+ { .fc_id = 455, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_SBTE1_AXI_ERR_RSP" },
+ { .fc_id = 456, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_SBTE2_AXI_ERR_RSP" },
+ { .fc_id = 457, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_SBTE3_AXI_ERR_RSP" },
+ { .fc_id = 458, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_SBTE4_AXI_ERR_RSP" },
+ { .fc_id = 459, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_CTRL_AXI_ERROR_RESPONSE" },
+ { .fc_id = 460, .cpu_id = 90, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_QMAN_SW_ERROR" },
+ { .fc_id = 461, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_SBTE0_AXI_ERR_RSP" },
+ { .fc_id = 462, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_SBTE1_AXI_ERR_RSP" },
+ { .fc_id = 463, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_SBTE2_AXI_ERR_RSP" },
+ { .fc_id = 464, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_SBTE3_AXI_ERR_RSP" },
+ { .fc_id = 465, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_SBTE4_AXI_ERR_RSP" },
+ { .fc_id = 466, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_CTRL_AXI_ERROR_RESPONSE" },
+ { .fc_id = 467, .cpu_id = 91, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_QMAN_SW_ERROR" },
+ { .fc_id = 468, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PSOC_MME_PLL_LOCK_ERR" },
+ { .fc_id = 469, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PSOC_CPU_PLL_LOCK_ERR" },
+ { .fc_id = 470, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_TPC_PLL_LOCK_ERR" },
+ { .fc_id = 471, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_NIC_PLL_LOCK_ERR" },
+ { .fc_id = 472, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_XBAR_MMU_PLL_LOCK_ERR" },
+ { .fc_id = 473, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_XBAR_DMA_PLL_LOCK_ERR" },
+ { .fc_id = 474, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_XBAR_IF_PLL_LOCK_ERR" },
+ { .fc_id = 475, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_XBAR_BANK_PLL_LOCK_ERR" },
+ { .fc_id = 476, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_XBAR_MMU_PLL_LOCK_ERR" },
+ { .fc_id = 477, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_XBAR_DMA_PLL_LOCK_ERR" },
+ { .fc_id = 478, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_XBAR_IF_PLL_LOCK_ERR" },
+ { .fc_id = 479, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_XBAR_MESH_PLL_LOCK_ERR" },
+ { .fc_id = 480, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_TPC_PLL_LOCK_ERR" },
+ { .fc_id = 481, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_NIC_PLL_LOCK_ERR" },
+ { .fc_id = 482, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PMMU_MME_PLL_LOCK_ERR" },
+ { .fc_id = 483, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_TPC_PLL_LOCK_ERR" },
+ { .fc_id = 484, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_PCI_PLL_LOCK_ERR" },
+ { .fc_id = 485, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_XBAR_MMU_PLL_LOCK_ERR" },
+ { .fc_id = 486, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_XBAR_DMA_PLL_LOCK_ERR" },
+ { .fc_id = 487, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_XBAR_IF_PLL_LOCK_ERR" },
+ { .fc_id = 488, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_XBAR_MESH_PLL_LOCK_ERR" },
+ { .fc_id = 489, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_XBAR_MMU_PLL_LOCK_ERR" },
+ { .fc_id = 490, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_XBAR_DMA_PLL_LOCK_ERR" },
+ { .fc_id = 491, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_XBAR_IF_PLL_LOCK_ERR" },
+ { .fc_id = 492, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_XBAR_BANK_PLL_LOCK_ERR" },
+ { .fc_id = 493, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_TPC_PLL_LOCK_ERR" },
+ { .fc_id = 494, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PSOC_VID_PLL_LOCK_ERR" },
+ { .fc_id = 495, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PMMU_VID_PLL_LOCK_ERR" },
+ { .fc_id = 496, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE3_HBM_PLL_LOCK_ERR" },
+ { .fc_id = 497, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_XBAR_HBM_PLL_LOCK_ERR" },
+ { .fc_id = 498, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE1_HBM_PLL_LOCK_ERR" },
+ { .fc_id = 499, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE0_HBM_PLL_LOCK_ERR" },
+ { .fc_id = 500, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_XBAR_HBM_PLL_LOCK_ERR" },
+ { .fc_id = 501, .cpu_id = 92, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DCORE2_HBM_PLL_LOCK_ERR" },
+ { .fc_id = 502, .cpu_id = 93, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "CPU_AXI_ERR_RSP" },
+ { .fc_id = 503, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_0_AXI_ERR_RSP" },
+ { .fc_id = 504, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_1_AXI_ERR_RSP" },
+ { .fc_id = 505, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_2_AXI_ERR_RSP" },
+ { .fc_id = 506, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_3_AXI_ERR_RSP" },
+ { .fc_id = 507, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_8_AXI_ERR_RSP" },
+ { .fc_id = 508, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_9_AXI_ERR_RSP" },
+ { .fc_id = 509, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_10_AXI_ERR_RSP" },
+ { .fc_id = 510, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_11_AXI_ERR_RSP" },
+ { .fc_id = 511, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_7_AXI_ERR_RSP" },
+ { .fc_id = 512, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_6_AXI_ERR_RSP" },
+ { .fc_id = 513, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_5_AXI_ERR_RSP" },
+ { .fc_id = 514, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_4_AXI_ERR_RSP" },
+ { .fc_id = 515, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_15_AXI_ERR_RSP" },
+ { .fc_id = 516, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_14_AXI_ERR_RSP" },
+ { .fc_id = 517, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_13_AXI_ERR_RSP" },
+ { .fc_id = 518, .cpu_id = 94, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU_12_AXI_ERR_RSP" },
+ { .fc_id = 519, .cpu_id = 95, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PMMU_FATAL" },
+ { .fc_id = 520, .cpu_id = 96, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PMMU_AXI_ERR_RSP" },
+ { .fc_id = 521, .cpu_id = 97, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM0_ALARM_A" },
+ { .fc_id = 522, .cpu_id = 98, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM0_ALARM_B" },
+ { .fc_id = 523, .cpu_id = 99, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM1_ALARM_A" },
+ { .fc_id = 524, .cpu_id = 100, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM1_ALARM_B" },
+ { .fc_id = 525, .cpu_id = 101, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM2_ALARM_A" },
+ { .fc_id = 526, .cpu_id = 102, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM2_ALARM_B" },
+ { .fc_id = 527, .cpu_id = 103, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM3_ALARM_A" },
+ { .fc_id = 528, .cpu_id = 104, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "VM3_ALARM_B" },
+ { .fc_id = 529, .cpu_id = 105, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PSOC_AXI_ERR_RSP" },
+ { .fc_id = 530, .cpu_id = 106, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PSOC_PRSTN_FALL" },
+ { .fc_id = 531, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 532, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 533, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 534, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 535, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 536, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 537, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 538, .cpu_id = 107, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 539, .cpu_id = 108, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "KDMA_CH0_AXI_ERR_RSP" },
+ { .fc_id = 540, .cpu_id = 109, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "PDMA_CH0_AXI_ERR_RSP" },
+ { .fc_id = 541, .cpu_id = 109, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "PDMA_CH1_AXI_ERR_RSP" },
+ { .fc_id = 542, .cpu_id = 110, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_CATTRIP_0" },
+ { .fc_id = 543, .cpu_id = 111, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_CATTRIP_1" },
+ { .fc_id = 544, .cpu_id = 112, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_CATTRIP_2" },
+ { .fc_id = 545, .cpu_id = 113, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_CATTRIP_3" },
+ { .fc_id = 546, .cpu_id = 114, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_CATTRIP_4" },
+ { .fc_id = 547, .cpu_id = 115, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM_CATTRIP_5" },
+ { .fc_id = 548, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM0_MC0_SEI_SEVERE" },
+ { .fc_id = 549, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM0_MC0_SEI_NON_SEVERE" },
+ { .fc_id = 550, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM0_MC1_SEI_SEVERE" },
+ { .fc_id = 551, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM0_MC1_SEI_NON_SEVERE" },
+ { .fc_id = 552, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM1_MC0_SEI_SEVERE" },
+ { .fc_id = 553, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM1_MC0_SEI_NON_SEVERE" },
+ { .fc_id = 554, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM1_MC1_SEI_SEVERE" },
+ { .fc_id = 555, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM1_MC1_SEI_NON_SEVERE" },
+ { .fc_id = 556, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM2_MC0_SEI_SEVERE" },
+ { .fc_id = 557, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM2_MC0_SEI_NON_SEVERE" },
+ { .fc_id = 558, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM2_MC1_SEI_SEVERE" },
+ { .fc_id = 559, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM2_MC1_SEI_NON_SEVERE" },
+ { .fc_id = 560, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM3_MC0_SEI_SEVERE" },
+ { .fc_id = 561, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM3_MC0_SEI_NON_SEVERE" },
+ { .fc_id = 562, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM3_MC1_SEI_SEVERE" },
+ { .fc_id = 563, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM3_MC1_SEI_NON_SEVERE" },
+ { .fc_id = 564, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM4_MC0_SEI_SEVERE" },
+ { .fc_id = 565, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM4_MC0_SEI_NON_SEVERE" },
+ { .fc_id = 566, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM4_MC1_SEI_SEVERE" },
+ { .fc_id = 567, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM4_MC1_SEI_NON_SEVERE" },
+ { .fc_id = 568, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM5_MC0_SEI_SEVERE" },
+ { .fc_id = 569, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM5_MC0_SEI_NON_SEVERE" },
+ { .fc_id = 570, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HBM5_MC1_SEI_SEVERE" },
+ { .fc_id = 571, .cpu_id = 116, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM5_MC1_SEI_NON_SEVERE" },
+ { .fc_id = 572, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC0_AXI_ERR_RSPONSE" },
+ { .fc_id = 573, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC1_AXI_ERR_RSPONSE" },
+ { .fc_id = 574, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC2_AXI_ERR_RSPONSE" },
+ { .fc_id = 575, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC3_AXI_ERR_RSPONSE" },
+ { .fc_id = 576, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC4_AXI_ERR_RSPONSE" },
+ { .fc_id = 577, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC5_AXI_ERR_RSPONSE" },
+ { .fc_id = 578, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC6_AXI_ERR_RSPONSE" },
+ { .fc_id = 579, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC7_AXI_ERR_RSPONSE" },
+ { .fc_id = 580, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC8_AXI_ERR_RSPONSE" },
+ { .fc_id = 581, .cpu_id = 117, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC9_AXI_ERR_RSPONSE" },
+ { .fc_id = 582, .cpu_id = 118, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 583, .cpu_id = 119, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 584, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF0_FATAL" },
+ { .fc_id = 585, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF1_FATAL" },
+ { .fc_id = 586, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF2_FATAL" },
+ { .fc_id = 587, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF3_FATAL" },
+ { .fc_id = 588, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF8_FATAL" },
+ { .fc_id = 589, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF9_FATAL" },
+ { .fc_id = 590, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF10_FATAL" },
+ { .fc_id = 591, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF11_FATAL" },
+ { .fc_id = 592, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF7_FATAL" },
+ { .fc_id = 593, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF6_FATAL" },
+ { .fc_id = 594, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF5_FATAL" },
+ { .fc_id = 595, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF4_FATAL" },
+ { .fc_id = 596, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF15_FATAL" },
+ { .fc_id = 597, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF14_FATAL" },
+ { .fc_id = 598, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF13_FATAL" },
+ { .fc_id = 599, .cpu_id = 120, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HIF12_FATAL" },
+ { .fc_id = 600, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC0_AXI_ERROR_RESPONSE" },
+ { .fc_id = 601, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC1_AXI_ERROR_RESPONSE" },
+ { .fc_id = 602, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC2_AXI_ERROR_RESPONSE" },
+ { .fc_id = 603, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC3_AXI_ERROR_RESPONSE" },
+ { .fc_id = 604, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC4_AXI_ERROR_RESPONSE" },
+ { .fc_id = 605, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC5_AXI_ERROR_RESPONSE" },
+ { .fc_id = 606, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC6_AXI_ERROR_RESPONSE" },
+ { .fc_id = 607, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC7_AXI_ERROR_RESPONSE" },
+ { .fc_id = 608, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC8_AXI_ERROR_RESPONSE" },
+ { .fc_id = 609, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC9_AXI_ERROR_RESPONSE" },
+ { .fc_id = 610, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC10_AXI_ERROR_RESPONSE" },
+ { .fc_id = 611, .cpu_id = 121, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC11_AXI_ERROR_RESPONSE" },
+ { .fc_id = 612, .cpu_id = 122, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "SM0_AXI_ERROR_RESPONSE" },
+ { .fc_id = 613, .cpu_id = 122, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "SM1_AXI_ERROR_RESPONSE" },
+ { .fc_id = 614, .cpu_id = 122, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "SM2_AXI_ERROR_RESPONSE" },
+ { .fc_id = 615, .cpu_id = 122, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "SM3_AXI_ERROR_RESPONSE" },
+ { .fc_id = 616, .cpu_id = 123, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "ARC_AXI_ERROR_RESPONSE" },
+ { .fc_id = 617, .cpu_id = 124, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 618, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 619, .cpu_id = 125, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PCIE_FLR_REQUESTED" },
+ { .fc_id = 620, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 621, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 622, .cpu_id = 125, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PCIE_APB_TIMEOUT" },
+ { .fc_id = 623, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 624, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 625, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 626, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 627, .cpu_id = 125, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PCIE_FATAL_ERR" },
+ { .fc_id = 628, .cpu_id = 125, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 629, .cpu_id = 126, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 630, .cpu_id = 127, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 631, .cpu_id = 128, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PCIE_P2P_MSIX" },
+ { .fc_id = 632, .cpu_id = 129, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PCIE_DRAIN_COMPLETE" },
+ { .fc_id = 633, .cpu_id = 130, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC0_BMON_SPMU" },
+ { .fc_id = 634, .cpu_id = 131, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC0_KERNEL_ERR" },
+ { .fc_id = 635, .cpu_id = 132, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC1_BMON_SPMU" },
+ { .fc_id = 636, .cpu_id = 133, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC1_KERNEL_ERR" },
+ { .fc_id = 637, .cpu_id = 134, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC2_BMON_SPMU" },
+ { .fc_id = 638, .cpu_id = 135, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC2_KERNEL_ERR" },
+ { .fc_id = 639, .cpu_id = 136, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC3_BMON_SPMU" },
+ { .fc_id = 640, .cpu_id = 137, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC3_KERNEL_ERR" },
+ { .fc_id = 641, .cpu_id = 138, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC4_BMON_SPMU" },
+ { .fc_id = 642, .cpu_id = 139, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC4_KERNEL_ERR" },
+ { .fc_id = 643, .cpu_id = 140, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC5_BMON_SPMU" },
+ { .fc_id = 644, .cpu_id = 141, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC5_KERNEL_ERR" },
+ { .fc_id = 645, .cpu_id = 150, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC6_BMON_SPMU" },
+ { .fc_id = 646, .cpu_id = 151, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC0_KERNEL_ERR" },
+ { .fc_id = 647, .cpu_id = 152, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC7_BMON_SPMU" },
+ { .fc_id = 648, .cpu_id = 153, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC1_KERNEL_ERR" },
+ { .fc_id = 649, .cpu_id = 146, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC8_BMON_SPMU" },
+ { .fc_id = 650, .cpu_id = 147, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC2_KERNEL_ERR" },
+ { .fc_id = 651, .cpu_id = 148, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC9_BMON_SPMU" },
+ { .fc_id = 652, .cpu_id = 149, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC3_KERNEL_ERR" },
+ { .fc_id = 653, .cpu_id = 142, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC10_BMON_SPMU" },
+ { .fc_id = 654, .cpu_id = 143, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC4_KERNEL_ERR" },
+ { .fc_id = 655, .cpu_id = 144, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC11_BMON_SPMU" },
+ { .fc_id = 656, .cpu_id = 145, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC5_KERNEL_ERR" },
+ { .fc_id = 657, .cpu_id = 162, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC12_BMON_SPMU" },
+ { .fc_id = 658, .cpu_id = 163, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC0_KERNEL_ERR" },
+ { .fc_id = 659, .cpu_id = 164, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC13_BMON_SPMU" },
+ { .fc_id = 660, .cpu_id = 165, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC1_KERNEL_ERR" },
+ { .fc_id = 661, .cpu_id = 158, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC14_BMON_SPMU" },
+ { .fc_id = 662, .cpu_id = 159, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC2_KERNEL_ERR" },
+ { .fc_id = 663, .cpu_id = 160, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC15_BMON_SPMU" },
+ { .fc_id = 664, .cpu_id = 161, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC3_KERNEL_ERR" },
+ { .fc_id = 665, .cpu_id = 154, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC16_BMON_SPMU" },
+ { .fc_id = 666, .cpu_id = 155, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC4_KERNEL_ERR" },
+ { .fc_id = 667, .cpu_id = 156, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC17_BMON_SPMU" },
+ { .fc_id = 668, .cpu_id = 157, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC5_KERNEL_ERR" },
+ { .fc_id = 669, .cpu_id = 166, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC18_BMON_SPMU" },
+ { .fc_id = 670, .cpu_id = 167, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC0_KERNEL_ERR" },
+ { .fc_id = 671, .cpu_id = 168, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC19_BMON_SPMU" },
+ { .fc_id = 672, .cpu_id = 169, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC1_KERNEL_ERR" },
+ { .fc_id = 673, .cpu_id = 170, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC20_BMON_SPMU" },
+ { .fc_id = 674, .cpu_id = 171, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC2_KERNEL_ERR" },
+ { .fc_id = 675, .cpu_id = 172, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC21_BMON_SPMU" },
+ { .fc_id = 676, .cpu_id = 173, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC3_KERNEL_ERR" },
+ { .fc_id = 677, .cpu_id = 174, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC22_BMON_SPMU" },
+ { .fc_id = 678, .cpu_id = 175, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC4_KERNEL_ERR" },
+ { .fc_id = 679, .cpu_id = 176, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC23_BMON_SPMU" },
+ { .fc_id = 680, .cpu_id = 177, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC5_KERNEL_ERR" },
+ { .fc_id = 681, .cpu_id = 178, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "TPC24_BMON_SPMU" },
+ { .fc_id = 682, .cpu_id = 179, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE4_TPC0_KERNEL_ERR" },
+ { .fc_id = 683, .cpu_id = 180, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 684, .cpu_id = 180, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 685, .cpu_id = 180, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 686, .cpu_id = 180, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 687, .cpu_id = 180, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 688, .cpu_id = 180, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_CTRL_BMON_SPMU" },
+ { .fc_id = 689, .cpu_id = 180, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_SBTE_BMON_SPMU" },
+ { .fc_id = 690, .cpu_id = 180, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME0_WAP_BMON_SPMU" },
+ { .fc_id = 691, .cpu_id = 180, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_WAP_SOURCE_RESULT_INVALID" },
+ { .fc_id = 692, .cpu_id = 181, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 693, .cpu_id = 181, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 694, .cpu_id = 181, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 695, .cpu_id = 181, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 696, .cpu_id = 181, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 697, .cpu_id = 181, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_CTRL_BMON_SPMU" },
+ { .fc_id = 698, .cpu_id = 181, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_SBTE_BMON_SPMU" },
+ { .fc_id = 699, .cpu_id = 181, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME1_WAP_BMON_SPMU" },
+ { .fc_id = 700, .cpu_id = 181, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_WAP_SOURCE_RESULT_INVALID" },
+ { .fc_id = 701, .cpu_id = 182, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 702, .cpu_id = 182, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 703, .cpu_id = 182, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 704, .cpu_id = 182, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 705, .cpu_id = 182, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 706, .cpu_id = 182, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_CTRL_BMON_SPMU" },
+ { .fc_id = 707, .cpu_id = 182, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_SBTE_BMON_SPMU" },
+ { .fc_id = 708, .cpu_id = 182, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME2_WAP_BMON_SPMU" },
+ { .fc_id = 709, .cpu_id = 182, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_WAP_SOURCE_RESULT_INVALID" },
+ { .fc_id = 710, .cpu_id = 183, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 711, .cpu_id = 183, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 712, .cpu_id = 183, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 713, .cpu_id = 183, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 714, .cpu_id = 183, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 715, .cpu_id = 183, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_CTRL_BMON_SPMU" },
+ { .fc_id = 716, .cpu_id = 183, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_SBTE_BMON_SPMU" },
+ { .fc_id = 717, .cpu_id = 183, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "MME3_WAP_BMON_SPMU" },
+ { .fc_id = 718, .cpu_id = 183, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_WAP_SOURCE_RESULT_INVALID" },
+ { .fc_id = 719, .cpu_id = 184, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 720, .cpu_id = 184, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU0_PAGE_FAULT_OR_WR_PERM" },
+ { .fc_id = 721, .cpu_id = 184, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU0_SECURITY_ERROR" },
+ { .fc_id = 722, .cpu_id = 185, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 723, .cpu_id = 185, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU1_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 724, .cpu_id = 185, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU1_SECURITY_ERROR" },
+ { .fc_id = 725, .cpu_id = 186, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 726, .cpu_id = 186, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU2_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 727, .cpu_id = 186, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU2_SECURITY_ERROR" },
+ { .fc_id = 728, .cpu_id = 187, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 729, .cpu_id = 187, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU3_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 730, .cpu_id = 187, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU3_SECURITY_ERROR" },
+ { .fc_id = 731, .cpu_id = 188, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 732, .cpu_id = 188, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU8_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 733, .cpu_id = 188, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU8_SECURITY_ERROR" },
+ { .fc_id = 734, .cpu_id = 189, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 735, .cpu_id = 189, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU9_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 736, .cpu_id = 189, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU9_SECURITY_ERROR" },
+ { .fc_id = 737, .cpu_id = 190, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 738, .cpu_id = 190, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU10_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 739, .cpu_id = 190, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU10_SECURITY_ERROR" },
+ { .fc_id = 740, .cpu_id = 191, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 741, .cpu_id = 191, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU11_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 742, .cpu_id = 191, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU11_SECURITY_ERROR" },
+ { .fc_id = 743, .cpu_id = 192, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 744, .cpu_id = 192, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU7_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 745, .cpu_id = 192, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU7_SECURITY_ERROR" },
+ { .fc_id = 746, .cpu_id = 193, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 747, .cpu_id = 193, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU6_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 748, .cpu_id = 193, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU6_SECURITY_ERROR" },
+ { .fc_id = 749, .cpu_id = 194, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 750, .cpu_id = 194, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU5_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 751, .cpu_id = 194, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU5_SECURITY_ERROR" },
+ { .fc_id = 752, .cpu_id = 195, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 753, .cpu_id = 195, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU4_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 754, .cpu_id = 195, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU4_SECURITY_ERROR" },
+ { .fc_id = 755, .cpu_id = 196, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 756, .cpu_id = 196, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU15_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 757, .cpu_id = 196, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU15_SECURITY_ERROR" },
+ { .fc_id = 758, .cpu_id = 197, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 759, .cpu_id = 197, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU14_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 760, .cpu_id = 197, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU14_SECURITY_ERROR" },
+ { .fc_id = 761, .cpu_id = 198, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 762, .cpu_id = 198, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU13_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 763, .cpu_id = 198, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU13_SECURITY_ERROR" },
+ { .fc_id = 764, .cpu_id = 199, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 765, .cpu_id = 199, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HMMU12_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 766, .cpu_id = 199, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "HMMU12_SECURITY_ERROR" },
+ { .fc_id = 767, .cpu_id = 200, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 768, .cpu_id = 201, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PMMU0_PAGE_FAULT_WR_PERM" },
+ { .fc_id = 769, .cpu_id = 202, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PMMU0_SECURITY_ERROR" },
+ { .fc_id = 770, .cpu_id = 203, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA2_BM_SPMU" },
+ { .fc_id = 771, .cpu_id = 204, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 772, .cpu_id = 205, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA3_BM_SPMU" },
+ { .fc_id = 773, .cpu_id = 206, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 774, .cpu_id = 207, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA0_BM_SPMU" },
+ { .fc_id = 775, .cpu_id = 208, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 776, .cpu_id = 209, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA1_BM_SPMU" },
+ { .fc_id = 777, .cpu_id = 210, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 778, .cpu_id = 211, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA6_BM_SPMU" },
+ { .fc_id = 779, .cpu_id = 212, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 780, .cpu_id = 213, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA7_BM_SPMU" },
+ { .fc_id = 781, .cpu_id = 214, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 782, .cpu_id = 215, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA4_BM_SPMU" },
+ { .fc_id = 783, .cpu_id = 216, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 784, .cpu_id = 217, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EDMA5_BM_SPMU" },
+ { .fc_id = 785, .cpu_id = 218, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 786, .cpu_id = 219, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "KDMA_BM_SPMU" },
+ { .fc_id = 787, .cpu_id = 220, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 788, .cpu_id = 221, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PDMA0_BM_SPMU" },
+ { .fc_id = 789, .cpu_id = 222, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PDMA1_BM_SPMU" },
+ { .fc_id = 790, .cpu_id = 223, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM0_MC0_SPI" },
+ { .fc_id = 791, .cpu_id = 224, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM0_MC1_SPI" },
+ { .fc_id = 792, .cpu_id = 225, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM1_MC0_SPI" },
+ { .fc_id = 793, .cpu_id = 226, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM1_MC1_SPI" },
+ { .fc_id = 794, .cpu_id = 227, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM2_MC0_SPI" },
+ { .fc_id = 795, .cpu_id = 228, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM2_MC1_SPI" },
+ { .fc_id = 796, .cpu_id = 229, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM3_MC0_SPI" },
+ { .fc_id = 797, .cpu_id = 230, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM3_MC1_SPI" },
+ { .fc_id = 798, .cpu_id = 231, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM4_MC0_SPI" },
+ { .fc_id = 799, .cpu_id = 232, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM4_MC1_SPI" },
+ { .fc_id = 800, .cpu_id = 233, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM5_MC0_SPI" },
+ { .fc_id = 801, .cpu_id = 234, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HBM5_MC1_SPI" },
+ { .fc_id = 802, .cpu_id = 235, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 803, .cpu_id = 236, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 804, .cpu_id = 237, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 805, .cpu_id = 238, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 806, .cpu_id = 239, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 807, .cpu_id = 240, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 808, .cpu_id = 241, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 809, .cpu_id = 242, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 810, .cpu_id = 243, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 811, .cpu_id = 244, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 812, .cpu_id = 245, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 813, .cpu_id = 246, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 814, .cpu_id = 247, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 815, .cpu_id = 248, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 816, .cpu_id = 249, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 817, .cpu_id = 250, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 818, .cpu_id = 251, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 819, .cpu_id = 252, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 820, .cpu_id = 253, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 821, .cpu_id = 254, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 822, .cpu_id = 255, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 823, .cpu_id = 256, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 824, .cpu_id = 257, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 825, .cpu_id = 258, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 826, .cpu_id = 259, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 827, .cpu_id = 260, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 828, .cpu_id = 261, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 829, .cpu_id = 262, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 830, .cpu_id = 263, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 831, .cpu_id = 264, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 832, .cpu_id = 265, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 833, .cpu_id = 266, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 834, .cpu_id = 267, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 835, .cpu_id = 268, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 836, .cpu_id = 269, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 837, .cpu_id = 270, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 838, .cpu_id = 271, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 839, .cpu_id = 272, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 840, .cpu_id = 273, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 841, .cpu_id = 274, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 842, .cpu_id = 275, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 843, .cpu_id = 276, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 844, .cpu_id = 277, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 845, .cpu_id = 278, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 846, .cpu_id = 279, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 847, .cpu_id = 280, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 848, .cpu_id = 281, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 849, .cpu_id = 282, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 850, .cpu_id = 283, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 851, .cpu_id = 284, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 852, .cpu_id = 285, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 853, .cpu_id = 286, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 854, .cpu_id = 287, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "" },
+ { .fc_id = 855, .cpu_id = 288, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "" },
+ { .fc_id = 856, .cpu_id = 289, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 857, .cpu_id = 290, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 858, .cpu_id = 291, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 859, .cpu_id = 292, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 860, .cpu_id = 293, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 861, .cpu_id = 294, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 862, .cpu_id = 295, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 863, .cpu_id = 296, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 864, .cpu_id = 297, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 865, .cpu_id = 298, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 866, .cpu_id = 299, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 867, .cpu_id = 300, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 868, .cpu_id = 301, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 869, .cpu_id = 302, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 870, .cpu_id = 303, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 871, .cpu_id = 304, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "RPM_ERROR_OR_DRAIN" },
+ { .fc_id = 872, .cpu_id = 305, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 873, .cpu_id = 306, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 874, .cpu_id = 307, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 875, .cpu_id = 308, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "RAZWI_OR_PID_MIN_MAX_INTERRUPT" },
+ { .fc_id = 876, .cpu_id = 309, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 877, .cpu_id = 310, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 878, .cpu_id = 311, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 879, .cpu_id = 312, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "" },
+ { .fc_id = 880, .cpu_id = 313, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 881, .cpu_id = 314, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 882, .cpu_id = 315, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 883, .cpu_id = 316, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 884, .cpu_id = 317, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 885, .cpu_id = 318, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 886, .cpu_id = 319, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 887, .cpu_id = 320, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 888, .cpu_id = 321, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 889, .cpu_id = 322, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 890, .cpu_id = 323, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 891, .cpu_id = 324, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 892, .cpu_id = 325, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 893, .cpu_id = 326, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 894, .cpu_id = 327, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 895, .cpu_id = 328, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 896, .cpu_id = 329, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC0_SPI" },
+ { .fc_id = 897, .cpu_id = 329, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC0_BMON_SPMU" },
+ { .fc_id = 898, .cpu_id = 330, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC1_SPI" },
+ { .fc_id = 899, .cpu_id = 330, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC1_BMON_SPMU" },
+ { .fc_id = 900, .cpu_id = 331, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC2_SPI" },
+ { .fc_id = 901, .cpu_id = 331, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC2_BMON_SPMU" },
+ { .fc_id = 902, .cpu_id = 332, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC3_SPI" },
+ { .fc_id = 903, .cpu_id = 332, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC3_BMON_SPMU" },
+ { .fc_id = 904, .cpu_id = 333, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC4_SPI" },
+ { .fc_id = 905, .cpu_id = 333, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC4_BMON_SPMU" },
+ { .fc_id = 906, .cpu_id = 334, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC5_SPI" },
+ { .fc_id = 907, .cpu_id = 334, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC5_BMON_SPMU" },
+ { .fc_id = 908, .cpu_id = 335, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC6_SPI" },
+ { .fc_id = 909, .cpu_id = 335, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC6_BMON_SPMU" },
+ { .fc_id = 910, .cpu_id = 336, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC7_SPI" },
+ { .fc_id = 911, .cpu_id = 336, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC7_BMON_SPMU" },
+ { .fc_id = 912, .cpu_id = 337, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC8_SPI" },
+ { .fc_id = 913, .cpu_id = 337, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC8_BMON_SPMU" },
+ { .fc_id = 914, .cpu_id = 338, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DEC9_SPI" },
+ { .fc_id = 915, .cpu_id = 338, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "DEC9_BMON_SPMU" },
+ { .fc_id = 916, .cpu_id = 339, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 917, .cpu_id = 340, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 918, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 919, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 920, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 921, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 922, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 923, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 924, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 925, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 926, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 927, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 928, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 929, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 930, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 931, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 932, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 933, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 934, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 935, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 936, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 937, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 938, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 939, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 940, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 941, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 942, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 943, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 944, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 945, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 946, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 947, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 948, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 949, .cpu_id = 341, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 950, .cpu_id = 342, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 951, .cpu_id = 343, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC0_BMON_SPMU" },
+ { .fc_id = 952, .cpu_id = 343, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC0_SW_ERROR" },
+ { .fc_id = 953, .cpu_id = 343, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 954, .cpu_id = 343, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 955, .cpu_id = 344, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC1_BMON_SPMU" },
+ { .fc_id = 956, .cpu_id = 344, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC1_SW_ERROR" },
+ { .fc_id = 957, .cpu_id = 344, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 958, .cpu_id = 344, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 959, .cpu_id = 345, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC2_BMON_SPMU" },
+ { .fc_id = 960, .cpu_id = 345, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC2_SW_ERROR" },
+ { .fc_id = 961, .cpu_id = 345, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 962, .cpu_id = 345, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 963, .cpu_id = 346, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC3_BMON_SPMU" },
+ { .fc_id = 964, .cpu_id = 346, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC3_SW_ERROR" },
+ { .fc_id = 965, .cpu_id = 346, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 966, .cpu_id = 346, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 967, .cpu_id = 347, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC4_BMON_SPMU" },
+ { .fc_id = 968, .cpu_id = 347, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC4_SW_ERROR" },
+ { .fc_id = 969, .cpu_id = 347, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 970, .cpu_id = 347, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 971, .cpu_id = 348, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC5_BMON_SPMU" },
+ { .fc_id = 972, .cpu_id = 348, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC5_SW_ERROR" },
+ { .fc_id = 973, .cpu_id = 348, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 974, .cpu_id = 348, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 975, .cpu_id = 349, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC6_BMON_SPMU" },
+ { .fc_id = 976, .cpu_id = 349, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC6_SW_ERROR" },
+ { .fc_id = 977, .cpu_id = 349, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 978, .cpu_id = 349, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 979, .cpu_id = 350, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC7_BMON_SPMU" },
+ { .fc_id = 980, .cpu_id = 350, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC7_SW_ERROR" },
+ { .fc_id = 981, .cpu_id = 350, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 982, .cpu_id = 350, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 983, .cpu_id = 351, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC8_BMON_SPMU" },
+ { .fc_id = 984, .cpu_id = 351, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC8_SW_ERROR" },
+ { .fc_id = 985, .cpu_id = 351, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 986, .cpu_id = 351, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 987, .cpu_id = 352, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC9_BMON_SPMU" },
+ { .fc_id = 988, .cpu_id = 352, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC9_SW_ERROR" },
+ { .fc_id = 989, .cpu_id = 352, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 990, .cpu_id = 352, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 991, .cpu_id = 353, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC10_BMON_SPMU" },
+ { .fc_id = 992, .cpu_id = 353, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC10_SW_ERROR" },
+ { .fc_id = 993, .cpu_id = 353, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 994, .cpu_id = 353, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 995, .cpu_id = 354, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC11_BMON_SPMU" },
+ { .fc_id = 996, .cpu_id = 354, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "NIC11_SW_ERROR" },
+ { .fc_id = 997, .cpu_id = 354, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 998, .cpu_id = 354, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 999, .cpu_id = 355, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1000, .cpu_id = 356, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1001, .cpu_id = 357, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1002, .cpu_id = 358, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1003, .cpu_id = 359, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1004, .cpu_id = 360, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1005, .cpu_id = 361, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1006, .cpu_id = 362, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1007, .cpu_id = 363, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1008, .cpu_id = 368, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1009, .cpu_id = 369, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1010, .cpu_id = 366, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1011, .cpu_id = 367, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1012, .cpu_id = 364, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1013, .cpu_id = 365, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1014, .cpu_id = 374, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1015, .cpu_id = 375, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1016, .cpu_id = 372, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1017, .cpu_id = 373, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1018, .cpu_id = 370, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1019, .cpu_id = 371, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1020, .cpu_id = 376, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1021, .cpu_id = 377, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1022, .cpu_id = 378, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1023, .cpu_id = 379, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1024, .cpu_id = 380, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1025, .cpu_id = 381, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1026, .cpu_id = 382, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1027, .cpu_id = 383, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1028, .cpu_id = 384, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1029, .cpu_id = 385, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1030, .cpu_id = 386, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1031, .cpu_id = 387, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1032, .cpu_id = 388, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1033, .cpu_id = 389, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1034, .cpu_id = 390, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1035, .cpu_id = 391, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1036, .cpu_id = 392, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1037, .cpu_id = 393, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1038, .cpu_id = 394, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1039, .cpu_id = 395, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1040, .cpu_id = 396, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1041, .cpu_id = 397, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1042, .cpu_id = 398, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1043, .cpu_id = 399, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1044, .cpu_id = 400, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1045, .cpu_id = 401, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1046, .cpu_id = 402, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1047, .cpu_id = 403, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1048, .cpu_id = 404, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1049, .cpu_id = 405, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1050, .cpu_id = 406, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1051, .cpu_id = 407, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1052, .cpu_id = 408, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1053, .cpu_id = 409, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1054, .cpu_id = 410, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1055, .cpu_id = 411, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1056, .cpu_id = 412, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1057, .cpu_id = 413, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1058, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1059, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1060, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1061, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1062, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1063, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1064, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1065, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1066, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1067, .cpu_id = 414, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1068, .cpu_id = 415, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1069, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1070, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1071, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1072, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1073, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1074, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1075, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1076, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1077, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1078, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1079, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1080, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1081, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1082, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1083, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1084, .cpu_id = 416, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1085, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1086, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1087, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1088, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1089, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1090, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1091, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1092, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1093, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1094, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1095, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1096, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1097, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1098, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1099, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1100, .cpu_id = 417, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1101, .cpu_id = 418, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1102, .cpu_id = 419, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1103, .cpu_id = 420, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1104, .cpu_id = 421, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1105, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1106, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1107, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1108, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1109, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1110, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1111, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1112, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1113, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1114, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1115, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1116, .cpu_id = 422, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1117, .cpu_id = 423, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1118, .cpu_id = 424, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "ROTATOR0_SERR" },
+ { .fc_id = 1119, .cpu_id = 425, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "ROTATOR1_SERR" },
+ { .fc_id = 1120, .cpu_id = 426, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "ROTATOR0_DERR" },
+ { .fc_id = 1121, .cpu_id = 427, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "ROTATOR1_DERR" },
+ { .fc_id = 1122, .cpu_id = 428, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "ROTATOR0_AXI_ERROR_RESPONSE" },
+ { .fc_id = 1123, .cpu_id = 429, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "ROTATOR1_AXI_ERROR_RESPONSE" },
+ { .fc_id = 1124, .cpu_id = 430, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1125, .cpu_id = 431, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1126, .cpu_id = 432, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "ROTATOR0_BMON_SPMU" },
+ { .fc_id = 1127, .cpu_id = 433, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1128, .cpu_id = 434, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "ROTATOR1_BMON_SPMU" },
+ { .fc_id = 1129, .cpu_id = 435, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1130, .cpu_id = 436, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM0_BMON_SPMU" },
+ { .fc_id = 1131, .cpu_id = 437, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM1_BMON_SPMU" },
+ { .fc_id = 1132, .cpu_id = 438, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM2_BMON_SPMU" },
+ { .fc_id = 1133, .cpu_id = 439, .valid = 1, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SM3_BMON_SPMU" },
+ { .fc_id = 1134, .cpu_id = 440, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1135, .cpu_id = 441, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1136, .cpu_id = 442, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1137, .cpu_id = 443, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1138, .cpu_id = 444, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1139, .cpu_id = 445, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1140, .cpu_id = 446, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1141, .cpu_id = 447, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1142, .cpu_id = 448, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1143, .cpu_id = 449, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1144, .cpu_id = 450, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1145, .cpu_id = 451, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1146, .cpu_id = 452, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1147, .cpu_id = 453, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1148, .cpu_id = 454, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1149, .cpu_id = 455, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1150, .cpu_id = 456, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1151, .cpu_id = 457, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1152, .cpu_id = 458, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1153, .cpu_id = 459, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1154, .cpu_id = 460, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1155, .cpu_id = 461, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1156, .cpu_id = 462, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1157, .cpu_id = 463, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1158, .cpu_id = 464, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1159, .cpu_id = 465, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1160, .cpu_id = 466, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1161, .cpu_id = 467, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1162, .cpu_id = 468, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1163, .cpu_id = 469, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1164, .cpu_id = 470, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1165, .cpu_id = 471, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1166, .cpu_id = 472, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1167, .cpu_id = 473, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1168, .cpu_id = 474, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1169, .cpu_id = 475, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1170, .cpu_id = 476, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1171, .cpu_id = 477, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1172, .cpu_id = 478, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1173, .cpu_id = 479, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1174, .cpu_id = 480, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PSOC_DMA_QM" },
+ { .fc_id = 1175, .cpu_id = 481, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1176, .cpu_id = 482, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1177, .cpu_id = 483, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1178, .cpu_id = 484, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1179, .cpu_id = 485, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1180, .cpu_id = 486, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1181, .cpu_id = 487, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1182, .cpu_id = 488, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1183, .cpu_id = 489, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1184, .cpu_id = 490, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1185, .cpu_id = 491, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1186, .cpu_id = 492, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1187, .cpu_id = 493, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1188, .cpu_id = 494, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1189, .cpu_id = 495, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1190, .cpu_id = 496, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1191, .cpu_id = 497, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1192, .cpu_id = 498, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1193, .cpu_id = 499, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1194, .cpu_id = 500, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1195, .cpu_id = 501, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1196, .cpu_id = 502, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1197, .cpu_id = 503, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1198, .cpu_id = 504, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1199, .cpu_id = 505, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1200, .cpu_id = 506, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1201, .cpu_id = 507, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1202, .cpu_id = 508, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1203, .cpu_id = 509, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1204, .cpu_id = 510, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1205, .cpu_id = 511, .valid = 0, .msg = 0, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1206, .cpu_id = 512, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC0_QM" },
+ { .fc_id = 1207, .cpu_id = 513, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC1_QM" },
+ { .fc_id = 1208, .cpu_id = 514, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC2_QM" },
+ { .fc_id = 1209, .cpu_id = 515, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC3_QM" },
+ { .fc_id = 1210, .cpu_id = 516, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC4_QM" },
+ { .fc_id = 1211, .cpu_id = 517, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE0_TPC5_QM" },
+ { .fc_id = 1212, .cpu_id = 518, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC0_QM" },
+ { .fc_id = 1213, .cpu_id = 519, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC1_QM" },
+ { .fc_id = 1214, .cpu_id = 520, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC2_QM" },
+ { .fc_id = 1215, .cpu_id = 521, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC3_QM" },
+ { .fc_id = 1216, .cpu_id = 522, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC4_QM" },
+ { .fc_id = 1217, .cpu_id = 523, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE1_TPC5_QM" },
+ { .fc_id = 1218, .cpu_id = 524, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC0_QM" },
+ { .fc_id = 1219, .cpu_id = 525, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC1_QM" },
+ { .fc_id = 1220, .cpu_id = 526, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC2_QM" },
+ { .fc_id = 1221, .cpu_id = 527, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC3_QM" },
+ { .fc_id = 1222, .cpu_id = 528, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC4_QM" },
+ { .fc_id = 1223, .cpu_id = 529, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE2_TPC5_QM" },
+ { .fc_id = 1224, .cpu_id = 530, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC0_QM" },
+ { .fc_id = 1225, .cpu_id = 531, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC1_QM" },
+ { .fc_id = 1226, .cpu_id = 532, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC2_QM" },
+ { .fc_id = 1227, .cpu_id = 533, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC3_QM" },
+ { .fc_id = 1228, .cpu_id = 534, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC4_QM" },
+ { .fc_id = 1229, .cpu_id = 535, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE3_TPC5_QM" },
+ { .fc_id = 1230, .cpu_id = 536, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "DCORE4_TPC0_QM" },
+ { .fc_id = 1231, .cpu_id = 537, .valid = 0, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "" },
+ { .fc_id = 1232, .cpu_id = 538, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME0_QM" },
+ { .fc_id = 1233, .cpu_id = 539, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME1_QM" },
+ { .fc_id = 1234, .cpu_id = 540, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME2_QM" },
+ { .fc_id = 1235, .cpu_id = 541, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "MME3_QM" },
+ { .fc_id = 1236, .cpu_id = 542, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA2_QM" },
+ { .fc_id = 1237, .cpu_id = 543, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA3_QM" },
+ { .fc_id = 1238, .cpu_id = 544, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA0_QM" },
+ { .fc_id = 1239, .cpu_id = 545, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA1_QM" },
+ { .fc_id = 1240, .cpu_id = 546, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA6_QM" },
+ { .fc_id = 1241, .cpu_id = 547, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA7_QM" },
+ { .fc_id = 1242, .cpu_id = 548, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA4_QM" },
+ { .fc_id = 1243, .cpu_id = 549, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA5_QM" },
+ { .fc_id = 1244, .cpu_id = 550, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "PDMA0_QM" },
+ { .fc_id = 1245, .cpu_id = 551, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "PDMA1_QM" },
+ { .fc_id = 1246, .cpu_id = 552, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PI_UPDATE" },
+ { .fc_id = 1247, .cpu_id = 553, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "HALT_MACHINE" },
+ { .fc_id = 1248, .cpu_id = 554, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "INTS_REGISTER" },
+ { .fc_id = 1249, .cpu_id = 555, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "ROT0_QM" },
+ { .fc_id = 1250, .cpu_id = 556, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "ROT1_QM" },
+ { .fc_id = 1251, .cpu_id = 557, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "SOFT_RESET" },
+ { .fc_id = 1252, .cpu_id = 558, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "CPLD_SHUTDOWN_CAUSE" },
+ { .fc_id = 1253, .cpu_id = 559, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "FIX_POWER_ENV_S" },
+ { .fc_id = 1254, .cpu_id = 560, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "FIX_POWER_ENV_E" },
+ { .fc_id = 1255, .cpu_id = 561, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "FIX_THERMAL_ENV_S" },
+ { .fc_id = 1256, .cpu_id = 562, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "FIX_THERMAL_ENV_E" },
+ { .fc_id = 1257, .cpu_id = 563, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "CPLD_SHUTDOWN_EVENT" },
+ { .fc_id = 1258, .cpu_id = 564, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "PKT_QUEUE_OUT_SYNC" },
+ { .fc_id = 1259, .cpu_id = 565, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA2_CORE" },
+ { .fc_id = 1260, .cpu_id = 566, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA3_CORE" },
+ { .fc_id = 1261, .cpu_id = 567, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA0_CORE" },
+ { .fc_id = 1262, .cpu_id = 568, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA1_CORE" },
+ { .fc_id = 1263, .cpu_id = 569, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA6_CORE" },
+ { .fc_id = 1264, .cpu_id = 570, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA7_CORE" },
+ { .fc_id = 1265, .cpu_id = 571, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA4_CORE" },
+ { .fc_id = 1266, .cpu_id = 572, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "EDMA5_CORE" },
+ { .fc_id = 1267, .cpu_id = 573, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "PDMA0_CORE" },
+ { .fc_id = 1268, .cpu_id = 574, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "PDMA1_CORE" },
+ { .fc_id = 1269, .cpu_id = 575, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "KDMA0_CORE" },
+ { .fc_id = 1270, .cpu_id = 576, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC0_QM0" },
+ { .fc_id = 1271, .cpu_id = 577, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC0_QM1" },
+ { .fc_id = 1272, .cpu_id = 578, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC1_QM0" },
+ { .fc_id = 1273, .cpu_id = 579, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC1_QM1" },
+ { .fc_id = 1274, .cpu_id = 580, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC2_QM0" },
+ { .fc_id = 1275, .cpu_id = 581, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC2_QM1" },
+ { .fc_id = 1276, .cpu_id = 582, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC3_QM0" },
+ { .fc_id = 1277, .cpu_id = 583, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC3_QM1" },
+ { .fc_id = 1278, .cpu_id = 584, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC4_QM0" },
+ { .fc_id = 1279, .cpu_id = 585, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC4_QM1" },
+ { .fc_id = 1280, .cpu_id = 586, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC5_QM0" },
+ { .fc_id = 1281, .cpu_id = 587, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC5_QM1" },
+ { .fc_id = 1282, .cpu_id = 588, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC6_QM0" },
+ { .fc_id = 1283, .cpu_id = 589, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC6_QM1" },
+ { .fc_id = 1284, .cpu_id = 590, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC7_QM0" },
+ { .fc_id = 1285, .cpu_id = 591, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC7_QM1" },
+ { .fc_id = 1286, .cpu_id = 592, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC8_QM0" },
+ { .fc_id = 1287, .cpu_id = 593, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC8_QM1" },
+ { .fc_id = 1288, .cpu_id = 594, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC9_QM0" },
+ { .fc_id = 1289, .cpu_id = 595, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC9_QM1" },
+ { .fc_id = 1290, .cpu_id = 596, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC10_QM0" },
+ { .fc_id = 1291, .cpu_id = 597, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC10_QM1" },
+ { .fc_id = 1292, .cpu_id = 598, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC11_QM0" },
+ { .fc_id = 1293, .cpu_id = 599, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "NIC11_QM1" },
+ { .fc_id = 1294, .cpu_id = 600, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "CPU_PKT_SANITY_FAILED" },
+ { .fc_id = 1295, .cpu_id = 601, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC0_ENG0" },
+ { .fc_id = 1296, .cpu_id = 602, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC0_ENG1" },
+ { .fc_id = 1297, .cpu_id = 603, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC1_ENG0" },
+ { .fc_id = 1298, .cpu_id = 604, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC1_ENG1" },
+ { .fc_id = 1299, .cpu_id = 605, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC2_ENG0" },
+ { .fc_id = 1300, .cpu_id = 606, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC2_ENG1" },
+ { .fc_id = 1301, .cpu_id = 607, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC3_ENG0" },
+ { .fc_id = 1302, .cpu_id = 608, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC3_ENG1" },
+ { .fc_id = 1303, .cpu_id = 609, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC4_ENG0" },
+ { .fc_id = 1304, .cpu_id = 610, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC4_ENG1" },
+ { .fc_id = 1305, .cpu_id = 611, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC5_ENG0" },
+ { .fc_id = 1306, .cpu_id = 612, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC5_ENG1" },
+ { .fc_id = 1307, .cpu_id = 613, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC6_ENG0" },
+ { .fc_id = 1308, .cpu_id = 614, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC6_ENG1" },
+ { .fc_id = 1309, .cpu_id = 615, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC7_ENG0" },
+ { .fc_id = 1310, .cpu_id = 616, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC7_ENG1" },
+ { .fc_id = 1311, .cpu_id = 617, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC8_ENG0" },
+ { .fc_id = 1312, .cpu_id = 618, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC8_ENG1" },
+ { .fc_id = 1313, .cpu_id = 619, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC9_ENG0" },
+ { .fc_id = 1314, .cpu_id = 620, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC9_ENG1" },
+ { .fc_id = 1315, .cpu_id = 621, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC10_ENG0" },
+ { .fc_id = 1316, .cpu_id = 622, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC10_ENG1" },
+ { .fc_id = 1317, .cpu_id = 623, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC11_ENG0" },
+ { .fc_id = 1318, .cpu_id = 624, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "STATUS_NIC11_ENG1" },
+ { .fc_id = 1319, .cpu_id = 625, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_COMPUTE,
+ .name = "ARC_DCCM_FULL" },
+ { .fc_id = 1320, .cpu_id = 626, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "FP32_NOT_SUPPORTED" },
+ { .fc_id = 1321, .cpu_id = 627, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_HARD,
+ .name = "DEV_RESET_REQ" },
+ { .fc_id = 1322, .cpu_id = 628, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PWR_BRK_ENTRY" },
+ { .fc_id = 1323, .cpu_id = 629, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PWR_BRK_EXT" },
+ { .fc_id = 1324, .cpu_id = 630, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PWR_RD_MODE0" },
+ { .fc_id = 1325, .cpu_id = 631, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PWR_RD_MODE1" },
+ { .fc_id = 1326, .cpu_id = 632, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PWR_RD_MODE2" },
+ { .fc_id = 1327, .cpu_id = 633, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "PWR_RD_MODE3" },
+ { .fc_id = 1328, .cpu_id = 634, .valid = 1, .msg = 1, .reset = EVENT_RESET_TYPE_NONE,
+ .name = "EQ_HEARTBEAT" },
+};
+
+#endif /* __GAUDI2_ASYNC_IDS_MAP_EVENTS_EXT_H_ */
diff --git a/drivers/misc/habanalabs/include/gaudi2/gaudi2_coresight.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_coresight.h
index 14f09d7758c7..14f09d7758c7 100644
--- a/drivers/misc/habanalabs/include/gaudi2/gaudi2_coresight.h
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_coresight.h
diff --git a/drivers/misc/habanalabs/include/gaudi2/gaudi2_fw_if.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_fw_if.h
index e4a7d5725096..6ea936c9594e 100644
--- a/drivers/misc/habanalabs/include/gaudi2/gaudi2_fw_if.h
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_fw_if.h
@@ -20,28 +20,38 @@
#define GAUDI2_NUM_MME 4
+#define NUM_OF_GPIOS_PER_PORT 16
+#define GAUDI2_WD_GPIO (62 % NUM_OF_GPIOS_PER_PORT)
+
#define GAUDI2_ARCPID_TX_MB_SIZE 0x1000
#define GAUDI2_ARCPID_RX_MB_SIZE 0x400
#define GAUDI2_ARM_TX_MB_SIZE 0x400
#define GAUDI2_ARM_RX_MB_SIZE 0x1800
#define GAUDI2_DCCM_BASE_ADDR 0x27020000
-#define GAUDI2_ARCPID_TX_MB_ADDR GAUDI2_DCCM_BASE_ADDR
-
-#define GAUDI2_ARCPID_RX_MB_ADDR (GAUDI2_ARCPID_TX_MB_ADDR + \
- GAUDI2_ARCPID_TX_MB_SIZE)
#define GAUDI2_ARM_TX_MB_ADDR GAUDI2_MAILBOX_BASE_ADDR
#define GAUDI2_ARM_RX_MB_ADDR (GAUDI2_ARM_TX_MB_ADDR + \
GAUDI2_ARM_TX_MB_SIZE)
+#define GAUDI2_ARCPID_TX_MB_ADDR (GAUDI2_ARM_RX_MB_ADDR + GAUDI2_ARM_RX_MB_SIZE)
+
+#define GAUDI2_ARCPID_RX_MB_ADDR (GAUDI2_ARCPID_TX_MB_ADDR + GAUDI2_ARCPID_TX_MB_SIZE)
+
#define GAUDI2_ARM_TX_MB_OFFSET (GAUDI2_ARM_TX_MB_ADDR - \
GAUDI2_SP_SRAM_BASE_ADDR)
#define GAUDI2_ARM_RX_MB_OFFSET (GAUDI2_ARM_RX_MB_ADDR - \
GAUDI2_SP_SRAM_BASE_ADDR)
+#define POWER_MODE_LEVELS { \
+ 150000, /* 00 */ \
+ 250000, /* 01 */ \
+ 400000, /* 10 */ \
+ /* 11: Normal mode */ \
+}
+
enum gaudi2_fw_status {
GAUDI2_PID_STATUS_UP = 0x1, /* PID on ARC0 is up */
GAUDI2_ARM_STATUS_UP = 0x2, /* ARM Linux Boot complete */
@@ -49,21 +59,6 @@ enum gaudi2_fw_status {
GAUDI2_STATUS_LAST = 0xFF
};
-struct gaudi2_cold_rst_data {
- union {
- struct {
- u32 recovery_flag: 1;
- u32 validation_flag: 1;
- u32 efuse_read_flag: 1;
- u32 spsram_init_done : 1;
- u32 fake_security_enable : 1;
- u32 fake_sig_validation_en : 1;
- u32 reserved : 26;
- };
- __le32 data;
- };
-};
-
enum gaudi2_rst_src {
HL_COLD_RST = 1,
HL_MANUAL_RST = 2,
@@ -77,10 +72,10 @@ enum gaudi2_rst_src {
};
struct gaudi2_redundancy_ctx {
- int redundant_hbm;
- int redundant_edma;
- int redundant_tpc;
- int redundant_vdec;
+ __le32 redundant_hbm;
+ __le32 redundant_edma;
+ __le32 redundant_tpc;
+ __le32 redundant_vdec;
__le64 hbm_mask;
__le64 edma_mask;
__le64 tpc_mask;
diff --git a/drivers/misc/habanalabs/include/gaudi2/gaudi2_packets.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_packets.h
index 8bf90fc18bf5..a812f8503f90 100644
--- a/drivers/misc/habanalabs/include/gaudi2/gaudi2_packets.h
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_packets.h
@@ -59,7 +59,7 @@ struct gaudi2_packet {
/* The rest of the packet data follows. Use the corresponding
* packet_XXX struct to deference the data, based on packet type
*/
- u8 contents[0];
+ u8 contents[];
};
struct packet_nop {
@@ -80,7 +80,7 @@ struct packet_wreg32 {
struct packet_wreg_bulk {
__le32 size64;
__le32 ctl;
- __le64 values[0]; /* data starts here */
+ __le64 values[]; /* data starts here */
};
struct packet_msg_long {
diff --git a/drivers/accel/habanalabs/include/gaudi2/gaudi2_reg_map.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_reg_map.h
new file mode 100644
index 000000000000..1e9c056e437d
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_reg_map.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2020 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ *
+ */
+
+#ifndef GAUDI2_REG_MAP_H_
+#define GAUDI2_REG_MAP_H_
+
+/*
+ * PSOC scratch-pad registers
+ */
+#define mmHW_STATE mmCPU_IF_KMD_HW_DIRTY_STATUS
+#define mmPID_STATUS_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_1
+#define mmARM_STATUS_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_2
+#define mmGIC_TPC_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_3
+#define mmGIC_MME_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_4
+#define mmGIC_DMA_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_5
+#define mmGIC_ROT_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_6
+#define mmGIC_NIC_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_7
+#define mmGIC_DMA_CR_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_8
+#define mmGIC_HOST_PI_UPD_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_9
+#define mmGIC_HOST_HALT_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_10
+#define mmGIC_HOST_INTS_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_11
+#define mmGIC_HOST_SOFT_RST_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_12
+#define mmCPU_RST_STATUS_TO_HOST mmPSOC_GLOBAL_CONF_SCRATCHPAD_14
+/*
+ * Single scratchpad register used for all ARCs to notify dccm queue full event to FW.
+ * So a new event would overwrite any unhandled previous event. In other words, incase
+ * of multiple events before previous ones are handled, last one would be considered.
+ */
+#define mmENGINE_ARC_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_15
+#define mmPID_CFG_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_18
+#define mmGIC_RAZWI_STATUS_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_19
+#define mmCPU_BOOT_DEV_STS0 mmPSOC_GLOBAL_CONF_SCRATCHPAD_20
+#define mmCPU_BOOT_DEV_STS1 mmPSOC_GLOBAL_CONF_SCRATCHPAD_21
+#define mmCPU_CMD_STATUS_TO_HOST mmPSOC_GLOBAL_CONF_SCRATCHPAD_23
+#define mmCPU_BOOT_ERR0 mmPSOC_GLOBAL_CONF_SCRATCHPAD_24
+#define mmCPU_BOOT_ERR1 mmPSOC_GLOBAL_CONF_SCRATCHPAD_25
+#define mmUPD_STS mmPSOC_GLOBAL_CONF_SCRATCHPAD_26
+#define mmUPD_CMD mmPSOC_GLOBAL_CONF_SCRATCHPAD_27
+#define mmPPBOOT_VER_OFFSET mmPSOC_GLOBAL_CONF_SCRATCHPAD_28
+#define mmRDWR_TEST mmPSOC_GLOBAL_CONF_SCRATCHPAD_30
+#define mmBTL_ID mmPSOC_GLOBAL_CONF_SCRATCHPAD_31
+#define mmRST_SRC mmPSOC_GLOBAL_CONF_COLD_RST_FLOPS_0
+#define mmCOLD_RST_DATA mmPSOC_GLOBAL_CONF_COLD_RST_FLOPS_2
+#define mmUPD_PENDING_STS mmPSOC_GLOBAL_CONF_COLD_RST_FLOPS_3
+#define mmPID_CMD_REQ_REG mmPSOC_PID_PID_CMD_0
+#define mmPID_CMD_REQ_REG_HI mmPSOC_PID_PID_CMD_1
+#define mmPID_CMD_RSP_REG mmPSOC_PID_PID_CMD_2
+#define mmPID_CMD_RSP_REG_HI mmPSOC_PID_PID_CMD_3
+#define mmPID_CMD_TELEMETRY_REG_0 mmPSOC_PID_PID_CMD_4
+#define mmPID_CMD_TELEMETRY_REG_0_HI mmPSOC_PID_PID_CMD_5
+#define mmPID_CMD_TELEMETRY_REG_1 mmPSOC_PID_PID_CMD_6
+#define mmPID_CMD_TELEMETRY_REG_1_HI mmPSOC_PID_PID_CMD_7
+#define mmWD_GPIO_OUTSET_REG mmPSOC_GPIO3_OUTENSET
+#define mmWD_GPIO_DATAOUT_REG mmPSOC_GPIO3_DATAOUT
+#define mmSTM_PROFILER_SPE_REG mmPSOC_STM_STMSPER
+
+/* Registers below are used to pass the boot_if data between ARM and ARC1 */
+#define mmARM_MSG_BOOT_ERR_SET mmCPU_IF_SPECIAL_GLBL_SPARE_0
+#define mmARM_MSG_BOOT_ERR_CLR mmCPU_IF_SPECIAL_GLBL_SPARE_1
+#define mmARM_MSG_BOOT_DEV_STS_SET mmCPU_IF_SPECIAL_GLBL_SPARE_2
+#define mmARM_MSG_BOOT_DEV_STS_CLR mmCPU_IF_SPECIAL_GLBL_SPARE_3
+#define mmMGMT_MSG_BOOT_ERR mmCPU_MSTR_IF_SPECIAL_GLBL_SPARE_0
+#define mmMGMT_MSG_BOOT_DEV_STS mmCPU_MSTR_IF_SPECIAL_GLBL_SPARE_1
+
+#endif /* GAUDI2_REG_MAP_H_ */
diff --git a/drivers/accel/habanalabs/include/gaudi2/gaudi2_special_blocks.h b/drivers/accel/habanalabs/include/gaudi2/gaudi2_special_blocks.h
new file mode 100644
index 000000000000..a55668f92dd1
--- /dev/null
+++ b/drivers/accel/habanalabs/include/gaudi2/gaudi2_special_blocks.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2022 HabanaLabs, Ltd.
+ * All Rights Reserved.
+ */
+
+/*
+ * This file was generated automatically.
+ * DON'T EDIT THIS FILE.
+ */
+
+#ifndef GAUDI2_SPECIAL_BLOCKS_H
+#define GAUDI2_SPECIAL_BLOCKS_H
+
+#define GAUDI2_SPECIAL_BLOCKS { \
+ { GAUDI2_BLOCK_TYPE_TPC, 0xfc008000, 4, 6, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_TPC, 0xfc00a000, 4, 6, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_TPC, 0xfc00b000, 4, 6, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_TPC, 0xfc00c000, 4, 6, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HMMU, 0xfc080000, 4, 4, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HMMU, 0xfc081000, 4, 4, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HMMU, 0xfc083000, 4, 4, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HMMU, 0xfc084000, 4, 4, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0c8000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0c9000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0ca000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0cb000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0cc000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_EU_BIST, 0xfc0cd000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0ce000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0cf000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0d0000, 4, 5, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0d1000, 4, 5, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0f8000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_MME, 0xfc0f9000, 4, 2, 0, 0x200000, 0x1000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_SYNC_MNGR, 0xfc11e000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_SYNC_MNGR, 0xfc11f000, 4, 0, 0, 0x200000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HIF, 0xfc120000, 4, 4, 0, 0x200000, 0x4000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc140000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc141000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc142000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc143000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc144000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc145000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_SRAM, 0xfc180000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfc181000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_SRAM, 0xfc182000, 4, 8, 0, 0x200000, 0x8000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_EDMA, 0xfc1c8000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_EDMA, 0xfc1ca000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_EDMA, 0xfc1cb000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_EDMA, 0xfc1cc000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_DEC, 0xfc1e3000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_DEC, 0xfc1e4000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_DEC, 0xfc1e5000, 4, 2, 0, 0x200000, 0x10000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc01000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc04000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc07000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc10000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc14000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc15000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PCIE, 0xfcc16000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc4a000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc4b000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc4e000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc4f000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc53000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc54000, 2, 0, 0, 0x1000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc58000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc59000, 2, 0, 0, 0x3000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc5a000, 2, 0, 0, 0x3000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc5b000, 2, 0, 0, 0x3000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc60000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc61000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc62000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc63000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc64000, 3, 0, 0, 0x1000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcc6c000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcc6d000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcc6e000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc74000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc76000, 3, 0, 0, 0x1000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc79000, 2, 0, 0, 0x1000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc7b000, 3, 0, 0, 0x1000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PSOC, 0xfcc7f000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PDMA, 0xfcc88000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PDMA, 0xfcc8a000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PDMA, 0xfcc8b000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PDMA, 0xfcc8c000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_CPU, 0xfccc0000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_CPU, 0xfccc1000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_CPU, 0xfccc3000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PMMU, 0xfcd00000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PMMU, 0xfcd01000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PMMU, 0xfcd02000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PMMU, 0xfcd03000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd04000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd05000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_XBAR, 0xfcd40000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd41000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd42000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd43000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd44000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_XBAR, 0xfcd48000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd55000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd64000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd65000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcd74000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ROT, 0xfce08000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ROT, 0xfce0a000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ROT, 0xfce0b000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ROT, 0xfce0c000, 2, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce40000, 4, 2, 0, 0x10000, 0x4000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce41000, 4, 2, 0, 0x10000, 0x4000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce42000, 4, 2, 0, 0x10000, 0x4000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce43000, 4, 2, 0, 0x10000, 0x4000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce48000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce49000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce4a000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce4b000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_RTR, 0xfce4c000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce81000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce82000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce88000, 4, 0, 0, 0x20000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce89000, 4, 0, 0, 0x20000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce8b000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce8c000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_ARC_FARM, 0xfce8f000, 4, 0, 0, 0x20000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_DEC, 0xfcf03000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_DEC, 0xfcf04000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_DEC, 0xfcf05000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_XFT, 0xfcf40000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcf41000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcf42000, 4, 0, 0, 0x10000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcf43000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcf53000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_PLL, 0xfcf73000, 1, 0, 0, 0x0, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HBM, 0xfd000000, 6, 2, 0, 0x80000, 0x20000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_HBM, 0xfd001000, 6, 2, 8, 0x80000, 0x20000, 0x1000 }, \
+ { GAUDI2_BLOCK_TYPE_HBM, 0xfd009000, 6, 2, 0, 0x80000, 0x20000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd400000, 12, 2, 15, 0x80000, 0x20000, 0x1000 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd418000, 12, 2, 0, 0x80000, 0x20000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd41a000, 12, 2, 0, 0x80000, 0x20000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd41f000, 12, 2, 0, 0x80000, 0x20000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd448000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd449000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd44a000, 12, 2, 0, 0x80000, 0x1000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd44c000, 12, 2, 0, 0x80000, 0x1000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd450000, 12, 2, 0, 0x80000, 0x1000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd452000, 12, 2, 0, 0x80000, 0x1000, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd454000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd455000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd460000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd468000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+ { GAUDI2_BLOCK_TYPE_NIC, 0xfd469000, 12, 0, 0, 0x80000, 0x0, 0x0 }, \
+}
+
+#endif
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_masks.h
index 4e0dbbbbde20..4e0dbbbbde20 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_regs.h
index f3faf1aad91a..f3faf1aad91a 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_ca53_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_if_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_if_regs.h
index cf657918962a..cf657918962a 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_if_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_if_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_pll_regs.h
index 8c8f9726d4b9..8c8f9726d4b9 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/cpu_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/cpu_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h
index 028143408401..028143408401 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_0_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_0_regs.h
index 0b246fe6ad04..0b246fe6ad04 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_0_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_0_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_1_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_1_regs.h
index 5449031722f2..5449031722f2 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_1_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_1_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_2_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_2_regs.h
index a4768521d18a..a4768521d18a 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_2_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_2_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_3_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_3_regs.h
index 619d01897ff8..619d01897ff8 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_3_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_3_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_4_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_4_regs.h
index 038617e163f1..038617e163f1 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_ch_4_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_ch_4_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_macro_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_macro_masks.h
index f43b564af1be..f43b564af1be 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_macro_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_macro_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_macro_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_macro_regs.h
index c3bfc1b8e3fd..c3bfc1b8e3fd 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_macro_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_macro_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_nrtr_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_nrtr_masks.h
index bc977488c072..bc977488c072 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_nrtr_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_nrtr_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_nrtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_nrtr_regs.h
index c4abc7ff1fc6..c4abc7ff1fc6 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_nrtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_nrtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_0_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_0_masks.h
index b17f72c31ab6..b17f72c31ab6 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_0_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_0_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_0_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_0_regs.h
index bf360b301154..bf360b301154 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_0_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_0_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_1_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_1_regs.h
index 51d432d05ac4..51d432d05ac4 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_1_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_1_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_2_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_2_regs.h
index 18fc0c2b6cc2..18fc0c2b6cc2 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_2_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_2_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_3_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_3_regs.h
index 6cf7204bf5cc..6cf7204bf5cc 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_3_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_3_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_4_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_4_regs.h
index 36fef2682875..36fef2682875 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/dma_qm_4_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/dma_qm_4_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/goya_blocks.h b/drivers/accel/habanalabs/include/goya/asic_reg/goya_blocks.h
index 85b15010cd7a..85b15010cd7a 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/goya_blocks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/goya_blocks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/goya_masks.h
index 9ff3cb245580..9ff3cb245580 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/goya_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/goya_regs.h
index ce65c9da5c60..ce65c9da5c60 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/goya_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/ic_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/ic_pll_regs.h
index 4ae7fed8b18c..4ae7fed8b18c 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/ic_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/ic_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mc_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mc_pll_regs.h
index 6d35d852798b..6d35d852798b 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mc_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mc_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme1_rtr_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme1_rtr_masks.h
index 6c23f8b96e7e..6c23f8b96e7e 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme1_rtr_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme1_rtr_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme1_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme1_rtr_regs.h
index 122e9d529939..122e9d529939 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme1_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme1_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme2_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme2_rtr_regs.h
index 00ce2252bbfb..00ce2252bbfb 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme2_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme2_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme3_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme3_rtr_regs.h
index 8e3eb7fd2070..8e3eb7fd2070 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme3_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme3_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme4_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme4_rtr_regs.h
index 79b67bbc8567..79b67bbc8567 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme4_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme4_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme5_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme5_rtr_regs.h
index 0ac3c37ce47f..0ac3c37ce47f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme5_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme5_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme6_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme6_rtr_regs.h
index 50c49cce72a6..50c49cce72a6 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme6_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme6_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme_cmdq_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme_cmdq_masks.h
index fe7d95bdcef9..fe7d95bdcef9 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme_cmdq_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme_cmdq_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme_cmdq_regs.h
index 5f8b85d2b4b1..5f8b85d2b4b1 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme_masks.h
index 1882c413cbe0..1882c413cbe0 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme_qm_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme_qm_masks.h
index e464e381555c..e464e381555c 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme_qm_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme_qm_regs.h
index 538708beffc9..538708beffc9 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mme_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mme_regs.h
index 0396cbfd5c89..0396cbfd5c89 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mme_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mme_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mmu_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/mmu_masks.h
index c3e69062b135..c3e69062b135 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mmu_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mmu_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/mmu_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/mmu_regs.h
index 7ec81f12031e..7ec81f12031e 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/mmu_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/mmu_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/pci_nrtr_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/pci_nrtr_masks.h
index ceb59f2e28b3..ceb59f2e28b3 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/pci_nrtr_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/pci_nrtr_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/pci_nrtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/pci_nrtr_regs.h
index dd067f301ac2..dd067f301ac2 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/pci_nrtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/pci_nrtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/pcie_aux_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/pcie_aux_regs.h
index 35b1d8ac6f63..35b1d8ac6f63 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/pcie_aux_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/pcie_aux_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/pcie_wrap_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/pcie_wrap_regs.h
index d1e55aace4a0..d1e55aace4a0 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/pcie_wrap_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/pcie_wrap_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_emmc_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_emmc_pll_regs.h
index 9271ea95ebe9..9271ea95ebe9 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_emmc_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_emmc_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_etr_regs.h
index b7c33e025db5..b7c33e025db5 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_etr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_global_conf_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_global_conf_masks.h
index 324266653c9a..324266653c9a 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_global_conf_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_global_conf_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_global_conf_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_global_conf_regs.h
index 8141f422e712..8141f422e712 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_global_conf_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_global_conf_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_mme_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_mme_pll_regs.h
index 4789ebb9c337..4789ebb9c337 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_mme_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_mme_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_pci_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_pci_pll_regs.h
index 27a296ea6c3d..27a296ea6c3d 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_pci_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_pci_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_spi_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_spi_regs.h
index 66aee7fa6b1e..66aee7fa6b1e 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_spi_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_spi_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_timestamp_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_timestamp_regs.h
index 9ce24597d4b0..9ce24597d4b0 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_timestamp_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/psoc_timestamp_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x0_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x0_rtr_regs.h
index 2ea1770b078f..2ea1770b078f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x0_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x0_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x1_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x1_rtr_regs.h
index 37e0713efa73..37e0713efa73 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x1_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x1_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x2_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x2_rtr_regs.h
index d2572279a2b9..d2572279a2b9 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x2_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x2_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x3_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x3_rtr_regs.h
index 68c5b402c506..68c5b402c506 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x3_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x3_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x4_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x4_rtr_regs.h
index a42f1ba06d28..a42f1ba06d28 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/sram_y0_x4_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/sram_y0_x4_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/stlb_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/stlb_masks.h
index 94f2ed4a36bd..94f2ed4a36bd 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/stlb_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/stlb_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/stlb_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/stlb_regs.h
index 35013f65acd2..35013f65acd2 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/stlb_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/stlb_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cfg_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cfg_masks.h
index 89c9507a512f..89c9507a512f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cfg_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cfg_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cfg_regs.h
index 7d71c4b73a5e..7d71c4b73a5e 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cmdq_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cmdq_masks.h
index 9395f2458771..9395f2458771 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cmdq_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cmdq_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cmdq_regs.h
index bc51df573bf0..bc51df573bf0 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_masks.h
index 553c6b6bd5ec..553c6b6bd5ec 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_regs.h
index 8495479c3659..8495479c3659 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_eml_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_nrtr_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_nrtr_masks.h
index 43fafcf01041..43fafcf01041 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_nrtr_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_nrtr_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_nrtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_nrtr_regs.h
index ce3346dd2042..ce3346dd2042 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_nrtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_nrtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_qm_masks.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_qm_masks.h
index 2e4b45947944..2e4b45947944 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_qm_masks.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_qm_masks.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_qm_regs.h
index 4fa09eb88878..4fa09eb88878 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc0_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc0_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_cfg_regs.h
index 928eef1808ae..928eef1808ae 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_cmdq_regs.h
index 30ae0f307328..30ae0f307328 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_qm_regs.h
index b95de4f95ba9..b95de4f95ba9 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_rtr_regs.h
index 0f91e307879e..0f91e307879e 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc1_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc1_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_cfg_regs.h
index 73421227f35b..73421227f35b 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_cmdq_regs.h
index 27b66bf2da9f..27b66bf2da9f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_qm_regs.h
index 31e5b2f53905..31e5b2f53905 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_rtr_regs.h
index 4eddeaa15d94..4eddeaa15d94 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc2_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc2_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_cfg_regs.h
index ce573a1a8361..ce573a1a8361 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_cmdq_regs.h
index 11d81fca0a0f..11d81fca0a0f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_qm_regs.h
index e41595a19e69..e41595a19e69 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_rtr_regs.h
index 34a438b1efe5..34a438b1efe5 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc3_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc3_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_cfg_regs.h
index d44caf0fc1bb..d44caf0fc1bb 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_cmdq_regs.h
index f13a6532961f..f13a6532961f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_qm_regs.h
index db081fc17cfc..db081fc17cfc 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_rtr_regs.h
index 8c5372303b28..8c5372303b28 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc4_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc4_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_cfg_regs.h
index 5139fde71011..5139fde71011 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_cmdq_regs.h
index 1e7cd6e1e888..1e7cd6e1e888 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_qm_regs.h
index ac0d3820cd6b..ac0d3820cd6b 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_rtr_regs.h
index 57f83bc3b17d..57f83bc3b17d 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc5_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc5_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_cfg_regs.h
index 94e0191c06c1..94e0191c06c1 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_cmdq_regs.h
index 7a1a0e87b225..7a1a0e87b225 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_qm_regs.h
index 80fa0fe0f60f..80fa0fe0f60f 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_rtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_rtr_regs.h
index d6cae8b8af66..d6cae8b8af66 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc6_rtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc6_rtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_cfg_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_cfg_regs.h
index 234147adb779..234147adb779 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_cfg_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_cfg_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_cmdq_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_cmdq_regs.h
index 4c160632fe7d..4c160632fe7d 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_cmdq_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_cmdq_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_nrtr_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_nrtr_regs.h
index 0c13d4d167aa..0c13d4d167aa 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_nrtr_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_nrtr_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_qm_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_qm_regs.h
index cbe11425bfb0..cbe11425bfb0 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc7_qm_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc7_qm_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/tpc_pll_regs.h b/drivers/accel/habanalabs/include/goya/asic_reg/tpc_pll_regs.h
index e25e19660a9d..e25e19660a9d 100644
--- a/drivers/misc/habanalabs/include/goya/asic_reg/tpc_pll_regs.h
+++ b/drivers/accel/habanalabs/include/goya/asic_reg/tpc_pll_regs.h
diff --git a/drivers/misc/habanalabs/include/goya/goya.h b/drivers/accel/habanalabs/include/goya/goya.h
index 1b4ca435021d..1b4ca435021d 100644
--- a/drivers/misc/habanalabs/include/goya/goya.h
+++ b/drivers/accel/habanalabs/include/goya/goya.h
diff --git a/drivers/misc/habanalabs/include/goya/goya_async_events.h b/drivers/accel/habanalabs/include/goya/goya_async_events.h
index 09081401cb1d..09081401cb1d 100644
--- a/drivers/misc/habanalabs/include/goya/goya_async_events.h
+++ b/drivers/accel/habanalabs/include/goya/goya_async_events.h
diff --git a/drivers/misc/habanalabs/include/goya/goya_coresight.h b/drivers/accel/habanalabs/include/goya/goya_coresight.h
index 6e933c0ca5cd..6e933c0ca5cd 100644
--- a/drivers/misc/habanalabs/include/goya/goya_coresight.h
+++ b/drivers/accel/habanalabs/include/goya/goya_coresight.h
diff --git a/drivers/misc/habanalabs/include/goya/goya_fw_if.h b/drivers/accel/habanalabs/include/goya/goya_fw_if.h
index bc05f86c73ac..bc05f86c73ac 100644
--- a/drivers/misc/habanalabs/include/goya/goya_fw_if.h
+++ b/drivers/accel/habanalabs/include/goya/goya_fw_if.h
diff --git a/drivers/misc/habanalabs/include/goya/goya_packets.h b/drivers/accel/habanalabs/include/goya/goya_packets.h
index 896799204fb0..896799204fb0 100644
--- a/drivers/misc/habanalabs/include/goya/goya_packets.h
+++ b/drivers/accel/habanalabs/include/goya/goya_packets.h
diff --git a/drivers/misc/habanalabs/include/goya/goya_reg_map.h b/drivers/accel/habanalabs/include/goya/goya_reg_map.h
index f3ab282cafa4..f3ab282cafa4 100644
--- a/drivers/misc/habanalabs/include/goya/goya_reg_map.h
+++ b/drivers/accel/habanalabs/include/goya/goya_reg_map.h
diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_general.h
index d408feecd483..b4a5e95be354 100644
--- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h
+++ b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_general.h
@@ -26,6 +26,8 @@
#define LAST_MASK 0x0000000000800ull
#define FLAGS_MASK 0x0000000000FFFull
+#define MMU_ARCH_3_HOPS 3
+#define MMU_ARCH_4_HOPS 4
#define MMU_ARCH_5_HOPS 5
#define MMU_ARCH_6_HOPS 6
diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v1_0.h
index 86511002e367..86511002e367 100644
--- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h
+++ b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v1_0.h
diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v1_1.h
index 9c727a5d47b4..9c727a5d47b4 100644
--- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h
+++ b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v1_1.h
diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v2_0.h b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v2_0.h
index cd7bf25d2da9..cd7bf25d2da9 100644
--- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v2_0.h
+++ b/drivers/accel/habanalabs/include/hw_ip/mmu/mmu_v2_0.h
diff --git a/drivers/misc/habanalabs/include/hw_ip/pci/pci_general.h b/drivers/accel/habanalabs/include/hw_ip/pci/pci_general.h
index d232081d4e0f..a75faa00197f 100644
--- a/drivers/misc/habanalabs/include/hw_ip/pci/pci_general.h
+++ b/drivers/accel/habanalabs/include/hw_ip/pci/pci_general.h
@@ -20,4 +20,13 @@
#define PCI_CONFIG_ELBI_STS_MASK (PCI_CONFIG_ELBI_STS_ERR | \
PCI_CONFIG_ELBI_STS_DONE)
+enum hl_revision_id {
+ /* PCI revision ID 0 is not legal */
+ REV_ID_INVALID = 0x00,
+ REV_ID_A = 0x01,
+ REV_ID_B = 0x02,
+ REV_ID_C = 0x03,
+ REV_ID_D = 0x04
+};
+
#endif /* INCLUDE_PCI_GENERAL_H_ */
diff --git a/drivers/accel/ivpu/Kconfig b/drivers/accel/ivpu/Kconfig
new file mode 100644
index 000000000000..9e055b5ce03d
--- /dev/null
+++ b/drivers/accel/ivpu/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_IVPU
+ tristate "Intel NPU (Neural Processing Unit)"
+ depends on DRM_ACCEL
+ depends on X86_64 && !UML
+ depends on PCI && PCI_MSI
+ select FW_LOADER
+ select DRM_GEM_SHMEM_HELPER
+ select GENERIC_ALLOCATOR
+ select WANT_DEV_COREDUMP
+ help
+ Choose this option if you have a system with an 14th generation
+ Intel CPU (Meteor Lake) or newer. Intel NPU (formerly called Intel VPU)
+ is a CPU-integrated inference accelerator for Computer Vision
+ and Deep Learning applications.
+
+ If "M" is selected, the module will be called intel_vpu.
+
+config DRM_ACCEL_IVPU_DEBUG
+ bool "Intel NPU debug mode"
+ depends on DRM_ACCEL_IVPU
+ help
+ Choose this option to enable additional
+ debug features for the Intel NPU driver:
+ - Always print debug messages regardless of dyndbg config,
+ - Enable unsafe module params.
diff --git a/drivers/accel/ivpu/Makefile b/drivers/accel/ivpu/Makefile
new file mode 100644
index 000000000000..dbf76b8a5b4c
--- /dev/null
+++ b/drivers/accel/ivpu/Makefile
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2023-2024 Intel Corporation
+
+intel_vpu-y := \
+ ivpu_drv.o \
+ ivpu_fw.o \
+ ivpu_fw_log.o \
+ ivpu_gem.o \
+ ivpu_gem_userptr.o \
+ ivpu_hw.o \
+ ivpu_hw_btrs.o \
+ ivpu_hw_ip.o \
+ ivpu_ipc.o \
+ ivpu_job.o \
+ ivpu_jsm_msg.o \
+ ivpu_mmu.o \
+ ivpu_mmu_context.o \
+ ivpu_ms.o \
+ ivpu_pm.o \
+ ivpu_sysfs.o \
+ ivpu_trace_points.o
+
+intel_vpu-$(CONFIG_DEBUG_FS) += ivpu_debugfs.o
+intel_vpu-$(CONFIG_DEV_COREDUMP) += ivpu_coredump.o
+
+obj-$(CONFIG_DRM_ACCEL_IVPU) += intel_vpu.o
+
+subdir-ccflags-$(CONFIG_DRM_ACCEL_IVPU_DEBUG) += -DDEBUG
+
+CFLAGS_ivpu_trace_points.o = -I$(src)
diff --git a/drivers/accel/ivpu/ivpu_coredump.c b/drivers/accel/ivpu/ivpu_coredump.c
new file mode 100644
index 000000000000..16ad0c30818c
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_coredump.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <linux/devcoredump.h>
+#include <linux/firmware.h>
+
+#include "ivpu_coredump.h"
+#include "ivpu_fw.h"
+#include "ivpu_gem.h"
+#include "vpu_boot_api.h"
+
+#define CRASH_DUMP_HEADER "Intel NPU crash dump"
+#define CRASH_DUMP_HEADERS_SIZE SZ_4K
+
+void ivpu_dev_coredump(struct ivpu_device *vdev)
+{
+ struct drm_print_iterator pi = {};
+ struct drm_printer p;
+ size_t coredump_size;
+ char *coredump;
+
+ coredump_size = CRASH_DUMP_HEADERS_SIZE + FW_VERSION_HEADER_SIZE +
+ ivpu_bo_size(vdev->fw->mem_log_crit) + ivpu_bo_size(vdev->fw->mem_log_verb);
+ coredump = vmalloc(coredump_size);
+ if (!coredump)
+ return;
+
+ pi.data = coredump;
+ pi.remain = coredump_size;
+ p = drm_coredump_printer(&pi);
+
+ drm_printf(&p, "%s\n", CRASH_DUMP_HEADER);
+ drm_printf(&p, "FW version: %s\n", vdev->fw->version);
+ ivpu_fw_log_print(vdev, false, &p);
+
+ dev_coredumpv(vdev->drm.dev, coredump, pi.offset, GFP_KERNEL);
+}
diff --git a/drivers/accel/ivpu/ivpu_coredump.h b/drivers/accel/ivpu/ivpu_coredump.h
new file mode 100644
index 000000000000..8efb09d02441
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_coredump.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_COREDUMP_H__
+#define __IVPU_COREDUMP_H__
+
+#include <drm/drm_print.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_fw_log.h"
+
+#ifdef CONFIG_DEV_COREDUMP
+void ivpu_dev_coredump(struct ivpu_device *vdev);
+#else
+static inline void ivpu_dev_coredump(struct ivpu_device *vdev)
+{
+ struct drm_printer p = drm_info_printer(vdev->drm.dev);
+
+ ivpu_fw_log_print(vdev, false, &p);
+}
+#endif
+
+#endif /* __IVPU_COREDUMP_H__ */
diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c
new file mode 100644
index 000000000000..3bd85ee6c26b
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_debugfs.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+#include <linux/fault-inject.h>
+
+#include <drm/drm_debugfs.h>
+#include <drm/drm_file.h>
+#include <drm/drm_print.h>
+
+#include <uapi/drm/ivpu_accel.h>
+
+#include "ivpu_debugfs.h"
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_fw_log.h"
+#include "ivpu_gem.h"
+#include "ivpu_hw.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_pm.h"
+
+static inline struct ivpu_device *seq_to_ivpu(struct seq_file *s)
+{
+ struct drm_debugfs_entry *entry = s->private;
+
+ return to_ivpu_device(entry->dev);
+}
+
+static int bo_list_show(struct seq_file *s, void *v)
+{
+ struct drm_printer p = drm_seq_file_printer(s);
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ ivpu_bo_list(&vdev->drm, &p);
+
+ return 0;
+}
+
+static int fw_name_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ seq_printf(s, "%s\n", vdev->fw->name);
+ return 0;
+}
+
+static int fw_version_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ seq_printf(s, "%s\n", vdev->fw->version);
+ return 0;
+}
+
+static int fw_trace_capability_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+ u64 trace_hw_component_mask;
+ u32 trace_destination_mask;
+ int ret;
+
+ ret = ivpu_jsm_trace_get_capability(vdev, &trace_destination_mask,
+ &trace_hw_component_mask);
+ if (!ret) {
+ seq_printf(s,
+ "trace_destination_mask: %#18x\n"
+ "trace_hw_component_mask: %#18llx\n",
+ trace_destination_mask, trace_hw_component_mask);
+ }
+ return 0;
+}
+
+static int fw_trace_config_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+ /**
+ * WA: VPU_JSM_MSG_TRACE_GET_CONFIG command is not working yet,
+ * so we use values from vdev->fw instead of calling ivpu_jsm_trace_get_config()
+ */
+ u32 trace_level = vdev->fw->trace_level;
+ u32 trace_destination_mask = vdev->fw->trace_destination_mask;
+ u64 trace_hw_component_mask = vdev->fw->trace_hw_component_mask;
+
+ seq_printf(s,
+ "trace_level: %#18x\n"
+ "trace_destination_mask: %#18x\n"
+ "trace_hw_component_mask: %#18llx\n",
+ trace_level, trace_destination_mask, trace_hw_component_mask);
+
+ return 0;
+}
+
+static int last_bootmode_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ seq_printf(s, "%s\n", (vdev->pm->is_warmboot) ? "warmboot" : "coldboot");
+
+ return 0;
+}
+
+static int reset_counter_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ seq_printf(s, "%d\n", atomic_read(&vdev->pm->reset_counter));
+ return 0;
+}
+
+static int reset_pending_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ seq_printf(s, "%d\n", atomic_read(&vdev->pm->reset_pending));
+ return 0;
+}
+
+static int firewall_irq_counter_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = seq_to_ivpu(s);
+
+ seq_printf(s, "%d\n", atomic_read(&vdev->hw->firewall_irq_counter));
+ return 0;
+}
+
+static const struct drm_debugfs_info vdev_debugfs_list[] = {
+ {"bo_list", bo_list_show, 0},
+ {"fw_name", fw_name_show, 0},
+ {"fw_version", fw_version_show, 0},
+ {"fw_trace_capability", fw_trace_capability_show, 0},
+ {"fw_trace_config", fw_trace_config_show, 0},
+ {"last_bootmode", last_bootmode_show, 0},
+ {"reset_counter", reset_counter_show, 0},
+ {"reset_pending", reset_pending_show, 0},
+ {"firewall_irq_counter", firewall_irq_counter_show, 0},
+};
+
+static int dvfs_mode_get(void *data, u64 *dvfs_mode)
+{
+ struct ivpu_device *vdev = (struct ivpu_device *)data;
+
+ *dvfs_mode = vdev->fw->dvfs_mode;
+ return 0;
+}
+
+static int dvfs_mode_set(void *data, u64 dvfs_mode)
+{
+ struct ivpu_device *vdev = (struct ivpu_device *)data;
+
+ vdev->fw->dvfs_mode = (u32)dvfs_mode;
+ return pci_try_reset_function(to_pci_dev(vdev->drm.dev));
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(dvfs_mode_fops, dvfs_mode_get, dvfs_mode_set, "%llu\n");
+
+static ssize_t
+fw_dyndbg_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
+{
+ struct ivpu_device *vdev = file->private_data;
+ char buffer[VPU_DYNDBG_CMD_MAX_LEN] = {};
+ int ret;
+
+ if (size >= VPU_DYNDBG_CMD_MAX_LEN)
+ return -EINVAL;
+
+ ret = strncpy_from_user(buffer, user_buf, size);
+ if (ret < 0)
+ return ret;
+
+ ivpu_jsm_dyndbg_control(vdev, buffer, size);
+ return size;
+}
+
+static const struct file_operations fw_dyndbg_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = fw_dyndbg_fops_write,
+};
+
+static int fw_log_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = s->private;
+ struct drm_printer p = drm_seq_file_printer(s);
+
+ ivpu_fw_log_print(vdev, true, &p);
+ return 0;
+}
+
+static int fw_log_fops_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fw_log_show, inode->i_private);
+}
+
+static ssize_t
+fw_log_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
+{
+ struct seq_file *s = file->private_data;
+ struct ivpu_device *vdev = s->private;
+
+ if (!size)
+ return -EINVAL;
+
+ ivpu_fw_log_mark_read(vdev);
+ return size;
+}
+
+static const struct file_operations fw_log_fops = {
+ .owner = THIS_MODULE,
+ .open = fw_log_fops_open,
+ .write = fw_log_fops_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t
+fw_profiling_freq_fops_write(struct file *file, const char __user *user_buf,
+ size_t size, loff_t *pos)
+{
+ struct ivpu_device *vdev = file->private_data;
+ bool enable;
+ int ret;
+
+ ret = kstrtobool_from_user(user_buf, size, &enable);
+ if (ret < 0)
+ return ret;
+
+ ivpu_hw_profiling_freq_drive(vdev, enable);
+
+ ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev));
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static const struct file_operations fw_profiling_freq_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = fw_profiling_freq_fops_write,
+};
+
+static ssize_t
+fw_trace_destination_mask_fops_write(struct file *file, const char __user *user_buf,
+ size_t size, loff_t *pos)
+{
+ struct ivpu_device *vdev = file->private_data;
+ struct ivpu_fw_info *fw = vdev->fw;
+ u32 trace_destination_mask;
+ int ret;
+
+ ret = kstrtou32_from_user(user_buf, size, 0, &trace_destination_mask);
+ if (ret < 0)
+ return ret;
+
+ fw->trace_destination_mask = trace_destination_mask;
+
+ ivpu_jsm_trace_set_config(vdev, fw->trace_level, trace_destination_mask,
+ fw->trace_hw_component_mask);
+
+ return size;
+}
+
+static const struct file_operations fw_trace_destination_mask_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = fw_trace_destination_mask_fops_write,
+};
+
+static ssize_t
+fw_trace_hw_comp_mask_fops_write(struct file *file, const char __user *user_buf,
+ size_t size, loff_t *pos)
+{
+ struct ivpu_device *vdev = file->private_data;
+ struct ivpu_fw_info *fw = vdev->fw;
+ u64 trace_hw_component_mask;
+ int ret;
+
+ ret = kstrtou64_from_user(user_buf, size, 0, &trace_hw_component_mask);
+ if (ret < 0)
+ return ret;
+
+ fw->trace_hw_component_mask = trace_hw_component_mask;
+
+ ivpu_jsm_trace_set_config(vdev, fw->trace_level, fw->trace_destination_mask,
+ trace_hw_component_mask);
+
+ return size;
+}
+
+static const struct file_operations fw_trace_hw_comp_mask_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = fw_trace_hw_comp_mask_fops_write,
+};
+
+static ssize_t
+fw_trace_level_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
+{
+ struct ivpu_device *vdev = file->private_data;
+ struct ivpu_fw_info *fw = vdev->fw;
+ u32 trace_level;
+ int ret;
+
+ ret = kstrtou32_from_user(user_buf, size, 0, &trace_level);
+ if (ret < 0)
+ return ret;
+
+ fw->trace_level = trace_level;
+
+ ivpu_jsm_trace_set_config(vdev, trace_level, fw->trace_destination_mask,
+ fw->trace_hw_component_mask);
+
+ return size;
+}
+
+static const struct file_operations fw_trace_level_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = fw_trace_level_fops_write,
+};
+
+static ssize_t
+ivpu_force_recovery_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
+{
+ struct ivpu_device *vdev = file->private_data;
+ int ret;
+
+ if (!size)
+ return -EINVAL;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ ivpu_pm_trigger_recovery(vdev, "debugfs");
+ flush_work(&vdev->pm->recovery_work);
+ ivpu_rpm_put(vdev);
+ return size;
+}
+
+static const struct file_operations ivpu_force_recovery_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = ivpu_force_recovery_fn,
+};
+
+static int ivpu_reset_engine_fn(void *data, u64 val)
+{
+ struct ivpu_device *vdev = (struct ivpu_device *)data;
+
+ return ivpu_jsm_reset_engine(vdev, (u32)val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(ivpu_reset_engine_fops, NULL, ivpu_reset_engine_fn, "0x%02llx\n");
+
+static int ivpu_resume_engine_fn(void *data, u64 val)
+{
+ struct ivpu_device *vdev = (struct ivpu_device *)data;
+
+ return ivpu_jsm_hws_resume_engine(vdev, (u32)val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(ivpu_resume_engine_fops, NULL, ivpu_resume_engine_fn, "0x%02llx\n");
+
+static int dct_active_get(void *data, u64 *active_percent)
+{
+ struct ivpu_device *vdev = data;
+
+ *active_percent = vdev->pm->dct_active_percent;
+
+ return 0;
+}
+
+static int dct_active_set(void *data, u64 active_percent)
+{
+ struct ivpu_device *vdev = data;
+ int ret;
+
+ if (active_percent > 100)
+ return -EINVAL;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ if (active_percent)
+ ret = ivpu_pm_dct_enable(vdev, active_percent);
+ else
+ ret = ivpu_pm_dct_disable(vdev);
+
+ ivpu_rpm_put(vdev);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");
+
+static void print_priority_band(struct seq_file *s, struct ivpu_hw_info *hw,
+ int band, const char *name)
+{
+ seq_printf(s, "%-9s: grace_period %9u process_grace_period %9u process_quantum %9u\n",
+ name,
+ hw->hws.grace_period[band],
+ hw->hws.process_grace_period[band],
+ hw->hws.process_quantum[band]);
+}
+
+static int priority_bands_show(struct seq_file *s, void *v)
+{
+ struct ivpu_device *vdev = s->private;
+ struct ivpu_hw_info *hw = vdev->hw;
+
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE, "Idle");
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL, "Normal");
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS, "Focus");
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME, "Realtime");
+
+ return 0;
+}
+
+static int priority_bands_fops_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, priority_bands_show, inode->i_private);
+}
+
+static ssize_t
+priority_bands_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
+{
+ struct seq_file *s = file->private_data;
+ struct ivpu_device *vdev = s->private;
+ char buf[64];
+ u32 grace_period;
+ u32 process_grace_period;
+ u32 process_quantum;
+ u32 band;
+ int ret;
+
+ if (size >= sizeof(buf))
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, user_buf, size);
+ if (ret < 0)
+ return ret;
+
+ buf[ret] = '\0';
+ ret = sscanf(buf, "%u %u %u %u", &band, &grace_period, &process_grace_period,
+ &process_quantum);
+ if (ret != 4)
+ return -EINVAL;
+
+ if (band >= VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT)
+ return -EINVAL;
+
+ vdev->hw->hws.grace_period[band] = grace_period;
+ vdev->hw->hws.process_grace_period[band] = process_grace_period;
+ vdev->hw->hws.process_quantum[band] = process_quantum;
+
+ return size;
+}
+
+static const struct file_operations ivpu_hws_priority_bands_fops = {
+ .owner = THIS_MODULE,
+ .open = priority_bands_fops_open,
+ .write = priority_bands_fops_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void ivpu_debugfs_init(struct ivpu_device *vdev)
+{
+ struct dentry *debugfs_root = vdev->drm.debugfs_root;
+
+ drm_debugfs_add_files(&vdev->drm, vdev_debugfs_list, ARRAY_SIZE(vdev_debugfs_list));
+
+ debugfs_create_file("force_recovery", 0200, debugfs_root, vdev,
+ &ivpu_force_recovery_fops);
+
+ debugfs_create_file("dvfs_mode", 0644, debugfs_root, vdev,
+ &dvfs_mode_fops);
+
+ debugfs_create_file("fw_dyndbg", 0200, debugfs_root, vdev,
+ &fw_dyndbg_fops);
+ debugfs_create_file("fw_log", 0644, debugfs_root, vdev,
+ &fw_log_fops);
+ debugfs_create_file("fw_trace_destination_mask", 0200, debugfs_root, vdev,
+ &fw_trace_destination_mask_fops);
+ debugfs_create_file("fw_trace_hw_comp_mask", 0200, debugfs_root, vdev,
+ &fw_trace_hw_comp_mask_fops);
+ debugfs_create_file("fw_trace_level", 0200, debugfs_root, vdev,
+ &fw_trace_level_fops);
+ debugfs_create_file("hws_priority_bands", 0200, debugfs_root, vdev,
+ &ivpu_hws_priority_bands_fops);
+
+ debugfs_create_file("reset_engine", 0200, debugfs_root, vdev,
+ &ivpu_reset_engine_fops);
+ debugfs_create_file("resume_engine", 0200, debugfs_root, vdev,
+ &ivpu_resume_engine_fops);
+
+ if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX) {
+ debugfs_create_file("fw_profiling_freq_drive", 0200,
+ debugfs_root, vdev, &fw_profiling_freq_fops);
+ debugfs_create_file("dct", 0644, debugfs_root, vdev, &ivpu_dct_fops);
+ }
+
+#ifdef CONFIG_FAULT_INJECTION
+ fault_create_debugfs_attr("fail_hw", debugfs_root, &ivpu_hw_failure);
+#endif
+}
diff --git a/drivers/accel/ivpu/ivpu_debugfs.h b/drivers/accel/ivpu/ivpu_debugfs.h
new file mode 100644
index 000000000000..49ae9ea78287
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_debugfs.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_DEBUGFS_H__
+#define __IVPU_DEBUGFS_H__
+
+struct ivpu_device;
+
+#if defined(CONFIG_DEBUG_FS)
+void ivpu_debugfs_init(struct ivpu_device *vdev);
+#else
+static inline void ivpu_debugfs_init(struct ivpu_device *vdev) { }
+#endif
+
+#endif /* __IVPU_DEBUGFS_H__ */
diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c
new file mode 100644
index 000000000000..3d6fccdefdd6
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_drv.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <generated/utsrelease.h>
+
+#include <drm/drm_accel.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_prime.h>
+
+#include "ivpu_coredump.h"
+#include "ivpu_debugfs.h"
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_fw_log.h"
+#include "ivpu_gem.h"
+#include "ivpu_hw.h"
+#include "ivpu_ipc.h"
+#include "ivpu_job.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_mmu.h"
+#include "ivpu_mmu_context.h"
+#include "ivpu_ms.h"
+#include "ivpu_pm.h"
+#include "ivpu_sysfs.h"
+#include "vpu_boot_api.h"
+
+#ifndef DRIVER_VERSION_STR
+#define DRIVER_VERSION_STR "1.0.0 " UTS_RELEASE
+#endif
+
+int ivpu_dbg_mask;
+module_param_named(dbg_mask, ivpu_dbg_mask, int, 0644);
+MODULE_PARM_DESC(dbg_mask, "Driver debug mask. See IVPU_DBG_* macros.");
+
+int ivpu_test_mode;
+#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG)
+module_param_named_unsafe(test_mode, ivpu_test_mode, int, 0644);
+MODULE_PARM_DESC(test_mode, "Test mode mask. See IVPU_TEST_MODE_* macros.");
+#endif
+
+u8 ivpu_pll_min_ratio;
+module_param_named(pll_min_ratio, ivpu_pll_min_ratio, byte, 0644);
+MODULE_PARM_DESC(pll_min_ratio, "Minimum PLL ratio used to set NPU frequency");
+
+u8 ivpu_pll_max_ratio = U8_MAX;
+module_param_named(pll_max_ratio, ivpu_pll_max_ratio, byte, 0644);
+MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set NPU frequency");
+
+int ivpu_sched_mode = IVPU_SCHED_MODE_AUTO;
+module_param_named(sched_mode, ivpu_sched_mode, int, 0444);
+MODULE_PARM_DESC(sched_mode, "Scheduler mode: -1 - Use default scheduler, 0 - Use OS scheduler (supported on 27XX - 50XX), 1 - Use HW scheduler");
+
+bool ivpu_disable_mmu_cont_pages;
+module_param_named(disable_mmu_cont_pages, ivpu_disable_mmu_cont_pages, bool, 0444);
+MODULE_PARM_DESC(disable_mmu_cont_pages, "Disable MMU contiguous pages optimization");
+
+bool ivpu_force_snoop;
+module_param_named(force_snoop, ivpu_force_snoop, bool, 0444);
+MODULE_PARM_DESC(force_snoop, "Force snooping for NPU host memory access");
+
+struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+
+ kref_get(&file_priv->ref);
+
+ ivpu_dbg(vdev, KREF, "file_priv get: ctx %u refcount %u\n",
+ file_priv->ctx.id, kref_read(&file_priv->ref));
+
+ return file_priv;
+}
+
+static void file_priv_unbind(struct ivpu_device *vdev, struct ivpu_file_priv *file_priv)
+{
+ mutex_lock(&file_priv->lock);
+ if (file_priv->bound) {
+ ivpu_dbg(vdev, FILE, "file_priv unbind: ctx %u\n", file_priv->ctx.id);
+
+ ivpu_cmdq_release_all_locked(file_priv);
+ ivpu_bo_unbind_all_bos_from_context(vdev, &file_priv->ctx);
+ ivpu_mmu_context_fini(vdev, &file_priv->ctx);
+ file_priv->bound = false;
+ drm_WARN_ON(&vdev->drm, !xa_erase_irq(&vdev->context_xa, file_priv->ctx.id));
+ }
+ mutex_unlock(&file_priv->lock);
+}
+
+static void file_priv_release(struct kref *ref)
+{
+ struct ivpu_file_priv *file_priv = container_of(ref, struct ivpu_file_priv, ref);
+ struct ivpu_device *vdev = file_priv->vdev;
+
+ ivpu_dbg(vdev, FILE, "file_priv release: ctx %u bound %d\n",
+ file_priv->ctx.id, (bool)file_priv->bound);
+
+ pm_runtime_get_sync(vdev->drm.dev);
+ mutex_lock(&vdev->context_list_lock);
+ file_priv_unbind(vdev, file_priv);
+ drm_WARN_ON(&vdev->drm, !xa_empty(&file_priv->cmdq_xa));
+ xa_destroy(&file_priv->cmdq_xa);
+ mutex_unlock(&vdev->context_list_lock);
+ pm_runtime_put_autosuspend(vdev->drm.dev);
+
+ mutex_destroy(&file_priv->ms_lock);
+ mutex_destroy(&file_priv->lock);
+ kfree(file_priv);
+}
+
+void ivpu_file_priv_put(struct ivpu_file_priv **link)
+{
+ struct ivpu_file_priv *file_priv = *link;
+ struct ivpu_device *vdev = file_priv->vdev;
+
+ ivpu_dbg(vdev, KREF, "file_priv put: ctx %u refcount %u\n",
+ file_priv->ctx.id, kref_read(&file_priv->ref));
+
+ *link = NULL;
+ kref_put(&file_priv->ref, file_priv_release);
+}
+
+bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability)
+{
+ switch (capability) {
+ case DRM_IVPU_CAP_METRIC_STREAMER:
+ return true;
+ case DRM_IVPU_CAP_DMA_MEMORY_RANGE:
+ return true;
+ case DRM_IVPU_CAP_BO_CREATE_FROM_USERPTR:
+ return true;
+ case DRM_IVPU_CAP_MANAGE_CMDQ:
+ return vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW;
+ default:
+ return false;
+ }
+}
+
+static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
+ struct drm_ivpu_param *args = data;
+ int ret = 0;
+ int idx;
+
+ if (!drm_dev_enter(dev, &idx))
+ return -ENODEV;
+
+ switch (args->param) {
+ case DRM_IVPU_PARAM_DEVICE_ID:
+ args->value = pdev->device;
+ break;
+ case DRM_IVPU_PARAM_DEVICE_REVISION:
+ args->value = pdev->revision;
+ break;
+ case DRM_IVPU_PARAM_PLATFORM_TYPE:
+ args->value = vdev->platform;
+ break;
+ case DRM_IVPU_PARAM_CORE_CLOCK_RATE:
+ args->value = ivpu_hw_dpu_max_freq_get(vdev);
+ break;
+ case DRM_IVPU_PARAM_NUM_CONTEXTS:
+ args->value = ivpu_get_context_count(vdev);
+ break;
+ case DRM_IVPU_PARAM_CONTEXT_BASE_ADDRESS:
+ args->value = vdev->hw->ranges.user.start;
+ break;
+ case DRM_IVPU_PARAM_CONTEXT_ID:
+ args->value = file_priv->ctx.id;
+ break;
+ case DRM_IVPU_PARAM_FW_API_VERSION:
+ if (args->index < VPU_FW_API_VER_NUM) {
+ struct vpu_firmware_header *fw_hdr;
+
+ fw_hdr = (struct vpu_firmware_header *)vdev->fw->file->data;
+ args->value = fw_hdr->api_version[args->index];
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ case DRM_IVPU_PARAM_ENGINE_HEARTBEAT:
+ ret = ivpu_jsm_get_heartbeat(vdev, args->index, &args->value);
+ break;
+ case DRM_IVPU_PARAM_UNIQUE_INFERENCE_ID:
+ args->value = (u64)atomic64_inc_return(&vdev->unique_id_counter);
+ break;
+ case DRM_IVPU_PARAM_TILE_CONFIG:
+ args->value = vdev->hw->tile_fuse;
+ break;
+ case DRM_IVPU_PARAM_SKU:
+ args->value = vdev->hw->sku;
+ break;
+ case DRM_IVPU_PARAM_CAPABILITIES:
+ args->value = ivpu_is_capable(vdev, args->index);
+ break;
+ case DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE:
+ args->value = ivpu_fw_preempt_buf_size(vdev);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ drm_dev_exit(idx);
+ return ret;
+}
+
+static int ivpu_set_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_param *args = data;
+ int ret = 0;
+
+ switch (args->param) {
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ivpu_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct ivpu_device *vdev = to_ivpu_device(dev);
+ struct ivpu_file_priv *file_priv;
+ u32 ctx_id;
+ int idx, ret;
+
+ if (!drm_dev_enter(dev, &idx))
+ return -ENODEV;
+
+ file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
+ if (!file_priv) {
+ ret = -ENOMEM;
+ goto err_dev_exit;
+ }
+
+ INIT_LIST_HEAD(&file_priv->ms_instance_list);
+
+ file_priv->vdev = vdev;
+ file_priv->bound = true;
+ kref_init(&file_priv->ref);
+ mutex_init(&file_priv->lock);
+ mutex_init(&file_priv->ms_lock);
+
+ mutex_lock(&vdev->context_list_lock);
+
+ ret = xa_alloc_irq(&vdev->context_xa, &ctx_id, file_priv,
+ vdev->context_xa_limit, GFP_KERNEL);
+ if (ret) {
+ ivpu_err(vdev, "Failed to allocate context id: %d\n", ret);
+ goto err_unlock;
+ }
+
+ ivpu_mmu_context_init(vdev, &file_priv->ctx, ctx_id);
+
+ file_priv->job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1));
+ file_priv->job_limit.max = file_priv->job_limit.min | IVPU_JOB_ID_JOB_MASK;
+
+ xa_init_flags(&file_priv->cmdq_xa, XA_FLAGS_ALLOC1);
+ file_priv->cmdq_limit.min = IVPU_CMDQ_MIN_ID;
+ file_priv->cmdq_limit.max = IVPU_CMDQ_MAX_ID;
+
+ mutex_unlock(&vdev->context_list_lock);
+ drm_dev_exit(idx);
+
+ file->driver_priv = file_priv;
+
+ ivpu_dbg(vdev, FILE, "file_priv create: ctx %u process %s pid %d\n",
+ ctx_id, current->comm, task_pid_nr(current));
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&vdev->context_list_lock);
+ mutex_destroy(&file_priv->ms_lock);
+ mutex_destroy(&file_priv->lock);
+ kfree(file_priv);
+err_dev_exit:
+ drm_dev_exit(idx);
+ return ret;
+}
+
+static void ivpu_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = to_ivpu_device(dev);
+
+ ivpu_dbg(vdev, FILE, "file_priv close: ctx %u process %s pid %d\n",
+ file_priv->ctx.id, current->comm, task_pid_nr(current));
+
+ ivpu_ms_cleanup(file_priv);
+ ivpu_file_priv_put(&file_priv);
+}
+
+static const struct drm_ioctl_desc ivpu_drm_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(IVPU_GET_PARAM, ivpu_get_param_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_SET_PARAM, ivpu_set_param_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_BO_CREATE, ivpu_bo_create_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_BO_INFO, ivpu_bo_info_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_SUBMIT, ivpu_submit_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_BO_WAIT, ivpu_bo_wait_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_START, ivpu_ms_start_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_GET_DATA, ivpu_ms_get_data_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_STOP, ivpu_ms_stop_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_GET_INFO, ivpu_ms_get_info_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_CMDQ_CREATE, ivpu_cmdq_create_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_CMDQ_DESTROY, ivpu_cmdq_destroy_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_CMDQ_SUBMIT, ivpu_cmdq_submit_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_BO_CREATE_FROM_USERPTR, ivpu_bo_create_from_userptr_ioctl, 0),
+};
+
+static int ivpu_wait_for_ready(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_consumer cons;
+ struct ivpu_ipc_hdr ipc_hdr;
+ unsigned long timeout;
+ int ret;
+
+ if (ivpu_test_mode & IVPU_TEST_MODE_FW_TEST)
+ return 0;
+
+ ivpu_ipc_consumer_add(vdev, &cons, IVPU_IPC_CHAN_BOOT_MSG, NULL);
+
+ timeout = jiffies + msecs_to_jiffies(vdev->timeout.boot);
+ while (1) {
+ ivpu_ipc_irq_handler(vdev);
+ ret = ivpu_ipc_receive(vdev, &cons, &ipc_hdr, NULL, 0);
+ if (ret != -ETIMEDOUT || time_after_eq(jiffies, timeout))
+ break;
+
+ cond_resched();
+ }
+
+ ivpu_ipc_consumer_del(vdev, &cons);
+
+ if (!ret && ipc_hdr.data_addr != IVPU_IPC_BOOT_MSG_DATA_ADDR) {
+ ivpu_err(vdev, "Invalid NPU ready message: 0x%x\n",
+ ipc_hdr.data_addr);
+ return -EIO;
+ }
+
+ if (!ret)
+ ivpu_dbg(vdev, PM, "NPU ready message received successfully\n");
+
+ return ret;
+}
+
+static int ivpu_hw_sched_init(struct ivpu_device *vdev)
+{
+ int ret = 0;
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
+ ret = ivpu_jsm_hws_setup_priority_bands(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to enable hw scheduler: %d", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ivpu_boot() - Start VPU firmware
+ * @vdev: VPU device
+ *
+ * This function is paired with ivpu_shutdown() but it doesn't power up the
+ * VPU because power up has to be called very early in ivpu_probe().
+ */
+int ivpu_boot(struct ivpu_device *vdev)
+{
+ int ret;
+
+ drm_WARN_ON(&vdev->drm, atomic_read(&vdev->job_timeout_counter));
+ drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
+
+ ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem_bp));
+
+ ret = ivpu_hw_boot_fw(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to start the firmware: %d\n", ret);
+ return ret;
+ }
+
+ ret = ivpu_wait_for_ready(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to boot the firmware: %d\n", ret);
+ goto err_diagnose_failure;
+ }
+
+ ivpu_hw_irq_clear(vdev);
+ enable_irq(vdev->irq);
+ ivpu_hw_irq_enable(vdev);
+ ivpu_ipc_enable(vdev);
+
+ if (ivpu_fw_is_cold_boot(vdev)) {
+ ret = ivpu_pm_dct_init(vdev);
+ if (ret)
+ goto err_disable_ipc;
+
+ ret = ivpu_hw_sched_init(vdev);
+ if (ret)
+ goto err_disable_ipc;
+ }
+
+ return 0;
+
+err_disable_ipc:
+ ivpu_ipc_disable(vdev);
+ ivpu_hw_irq_disable(vdev);
+ disable_irq(vdev->irq);
+err_diagnose_failure:
+ ivpu_hw_diagnose_failure(vdev);
+ ivpu_mmu_evtq_dump(vdev);
+ ivpu_dev_coredump(vdev);
+ return ret;
+}
+
+void ivpu_prepare_for_reset(struct ivpu_device *vdev)
+{
+ ivpu_hw_irq_disable(vdev);
+ disable_irq(vdev->irq);
+ flush_work(&vdev->irq_ipc_work);
+ flush_work(&vdev->irq_dct_work);
+ flush_work(&vdev->context_abort_work);
+ ivpu_ipc_disable(vdev);
+ ivpu_mmu_disable(vdev);
+}
+
+int ivpu_shutdown(struct ivpu_device *vdev)
+{
+ int ret;
+
+ /* Save PCI state before powering down as it sometimes gets corrupted if NPU hangs */
+ pci_save_state(to_pci_dev(vdev->drm.dev));
+
+ ret = ivpu_hw_power_down(vdev);
+ if (ret)
+ ivpu_warn(vdev, "Failed to power down HW: %d\n", ret);
+
+ pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot);
+
+ return ret;
+}
+
+static const struct file_operations ivpu_fops = {
+ .owner = THIS_MODULE,
+ DRM_ACCEL_FOPS,
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = drm_show_fdinfo,
+#endif
+};
+
+static const struct drm_driver driver = {
+ .driver_features = DRIVER_GEM | DRIVER_COMPUTE_ACCEL,
+
+ .open = ivpu_open,
+ .postclose = ivpu_postclose,
+
+ .gem_create_object = ivpu_gem_create_object,
+ .gem_prime_import = ivpu_gem_prime_import,
+
+ .ioctls = ivpu_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls),
+ .fops = &ivpu_fops,
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = drm_show_memory_stats,
+#endif
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+
+ .major = 1,
+};
+
+static int ivpu_irq_init(struct ivpu_device *vdev)
+{
+ struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
+ int ret;
+
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
+ if (ret < 0) {
+ ivpu_err(vdev, "Failed to allocate a MSI IRQ: %d\n", ret);
+ return ret;
+ }
+
+ INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn);
+ INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn);
+ INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn);
+
+ ivpu_irq_handlers_init(vdev);
+
+ vdev->irq = pci_irq_vector(pdev, 0);
+
+ ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
+ IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to request an IRQ %d\n", ret);
+
+ return ret;
+}
+
+static int ivpu_pci_init(struct ivpu_device *vdev)
+{
+ struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
+ struct resource *bar0 = &pdev->resource[0];
+ struct resource *bar4 = &pdev->resource[4];
+ int ret;
+
+ ivpu_dbg(vdev, MISC, "Mapping BAR0 (RegV) %pR\n", bar0);
+ vdev->regv = devm_ioremap_resource(vdev->drm.dev, bar0);
+ if (IS_ERR(vdev->regv)) {
+ ivpu_err(vdev, "Failed to map bar 0: %pe\n", vdev->regv);
+ return PTR_ERR(vdev->regv);
+ }
+
+ ivpu_dbg(vdev, MISC, "Mapping BAR4 (RegB) %pR\n", bar4);
+ vdev->regb = devm_ioremap_resource(vdev->drm.dev, bar4);
+ if (IS_ERR(vdev->regb)) {
+ ivpu_err(vdev, "Failed to map bar 4: %pe\n", vdev->regb);
+ return PTR_ERR(vdev->regb);
+ }
+
+ ret = dma_set_mask_and_coherent(vdev->drm.dev, DMA_BIT_MASK(vdev->hw->dma_bits));
+ if (ret) {
+ ivpu_err(vdev, "Failed to set DMA mask: %d\n", ret);
+ return ret;
+ }
+ dma_set_max_seg_size(vdev->drm.dev, UINT_MAX);
+
+ /* Clear any pending errors */
+ pcie_capability_clear_word(pdev, PCI_EXP_DEVSTA, 0x3f);
+
+ /* NPU does not require 10m D3hot delay */
+ pdev->d3hot_delay = 0;
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to enable PCI device: %d\n", ret);
+ return ret;
+ }
+
+ pci_set_master(pdev);
+
+ return 0;
+}
+
+static int ivpu_dev_init(struct ivpu_device *vdev)
+{
+ int ret;
+
+ vdev->hw = drmm_kzalloc(&vdev->drm, sizeof(*vdev->hw), GFP_KERNEL);
+ if (!vdev->hw)
+ return -ENOMEM;
+
+ vdev->mmu = drmm_kzalloc(&vdev->drm, sizeof(*vdev->mmu), GFP_KERNEL);
+ if (!vdev->mmu)
+ return -ENOMEM;
+
+ vdev->fw = drmm_kzalloc(&vdev->drm, sizeof(*vdev->fw), GFP_KERNEL);
+ if (!vdev->fw)
+ return -ENOMEM;
+
+ vdev->ipc = drmm_kzalloc(&vdev->drm, sizeof(*vdev->ipc), GFP_KERNEL);
+ if (!vdev->ipc)
+ return -ENOMEM;
+
+ vdev->pm = drmm_kzalloc(&vdev->drm, sizeof(*vdev->pm), GFP_KERNEL);
+ if (!vdev->pm)
+ return -ENOMEM;
+
+ if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX)
+ vdev->hw->dma_bits = 48;
+ else
+ vdev->hw->dma_bits = 38;
+
+ vdev->platform = IVPU_PLATFORM_INVALID;
+ vdev->context_xa_limit.min = IVPU_USER_CONTEXT_MIN_SSID;
+ vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID;
+ atomic64_set(&vdev->unique_id_counter, 0);
+ atomic_set(&vdev->job_timeout_counter, 0);
+ xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
+ xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
+ xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);
+ INIT_LIST_HEAD(&vdev->bo_list);
+
+ vdev->db_limit.min = IVPU_MIN_DB;
+ vdev->db_limit.max = IVPU_MAX_DB;
+
+ ret = drmm_mutex_init(&vdev->drm, &vdev->context_list_lock);
+ if (ret)
+ goto err_xa_destroy;
+
+ ret = drmm_mutex_init(&vdev->drm, &vdev->submitted_jobs_lock);
+ if (ret)
+ goto err_xa_destroy;
+
+ ret = drmm_mutex_init(&vdev->drm, &vdev->bo_list_lock);
+ if (ret)
+ goto err_xa_destroy;
+
+ ret = ivpu_pci_init(vdev);
+ if (ret)
+ goto err_xa_destroy;
+
+ ret = ivpu_irq_init(vdev);
+ if (ret)
+ goto err_xa_destroy;
+
+ /* Init basic HW info based on buttress registers which are accessible before power up */
+ ret = ivpu_hw_init(vdev);
+ if (ret)
+ goto err_xa_destroy;
+
+ /* Power up early so the rest of init code can access VPU registers */
+ ret = ivpu_hw_power_up(vdev);
+ if (ret)
+ goto err_shutdown;
+
+ ivpu_mmu_global_context_init(vdev);
+
+ ret = ivpu_mmu_init(vdev);
+ if (ret)
+ goto err_mmu_gctx_fini;
+
+ ret = ivpu_mmu_reserved_context_init(vdev);
+ if (ret)
+ goto err_mmu_gctx_fini;
+
+ ret = ivpu_fw_init(vdev);
+ if (ret)
+ goto err_mmu_rctx_fini;
+
+ ret = ivpu_ipc_init(vdev);
+ if (ret)
+ goto err_fw_fini;
+
+ ivpu_pm_init(vdev);
+
+ ret = ivpu_boot(vdev);
+ if (ret)
+ goto err_ipc_fini;
+
+ ivpu_job_done_consumer_init(vdev);
+ ivpu_pm_enable(vdev);
+
+ return 0;
+
+err_ipc_fini:
+ ivpu_ipc_fini(vdev);
+err_fw_fini:
+ ivpu_fw_fini(vdev);
+err_mmu_rctx_fini:
+ ivpu_mmu_reserved_context_fini(vdev);
+err_mmu_gctx_fini:
+ ivpu_mmu_global_context_fini(vdev);
+err_shutdown:
+ ivpu_shutdown(vdev);
+err_xa_destroy:
+ xa_destroy(&vdev->db_xa);
+ xa_destroy(&vdev->submitted_jobs_xa);
+ xa_destroy(&vdev->context_xa);
+ return ret;
+}
+
+static void ivpu_bo_unbind_all_user_contexts(struct ivpu_device *vdev)
+{
+ struct ivpu_file_priv *file_priv;
+ unsigned long ctx_id;
+
+ mutex_lock(&vdev->context_list_lock);
+
+ xa_for_each(&vdev->context_xa, ctx_id, file_priv)
+ file_priv_unbind(vdev, file_priv);
+
+ mutex_unlock(&vdev->context_list_lock);
+}
+
+static void ivpu_dev_fini(struct ivpu_device *vdev)
+{
+ ivpu_jobs_abort_all(vdev);
+ ivpu_pm_disable_recovery(vdev);
+ ivpu_pm_disable(vdev);
+ ivpu_prepare_for_reset(vdev);
+ ivpu_shutdown(vdev);
+
+ ivpu_ms_cleanup_all(vdev);
+ ivpu_job_done_consumer_fini(vdev);
+ ivpu_bo_unbind_all_user_contexts(vdev);
+
+ ivpu_ipc_fini(vdev);
+ ivpu_fw_fini(vdev);
+ ivpu_mmu_reserved_context_fini(vdev);
+ ivpu_mmu_global_context_fini(vdev);
+
+ drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->db_xa));
+ xa_destroy(&vdev->db_xa);
+ drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
+ xa_destroy(&vdev->submitted_jobs_xa);
+ drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->context_xa));
+ xa_destroy(&vdev->context_xa);
+}
+
+static struct pci_device_id ivpu_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_MTL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_ARL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PTL_P) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_WCL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_NVL) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, ivpu_pci_ids);
+
+static int ivpu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct ivpu_device *vdev;
+ int ret;
+
+ vdev = devm_drm_dev_alloc(&pdev->dev, &driver, struct ivpu_device, drm);
+ if (IS_ERR(vdev))
+ return PTR_ERR(vdev);
+
+ pci_set_drvdata(pdev, vdev);
+
+ ret = ivpu_dev_init(vdev);
+ if (ret)
+ return ret;
+
+ ivpu_debugfs_init(vdev);
+ ivpu_sysfs_init(vdev);
+
+ ret = drm_dev_register(&vdev->drm, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register DRM device: %d\n", ret);
+ ivpu_dev_fini(vdev);
+ }
+
+ return ret;
+}
+
+static void ivpu_remove(struct pci_dev *pdev)
+{
+ struct ivpu_device *vdev = pci_get_drvdata(pdev);
+
+ drm_dev_unplug(&vdev->drm);
+ ivpu_dev_fini(vdev);
+}
+
+static const struct dev_pm_ops ivpu_drv_pci_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(ivpu_pm_suspend_cb, ivpu_pm_resume_cb)
+ SET_RUNTIME_PM_OPS(ivpu_pm_runtime_suspend_cb, ivpu_pm_runtime_resume_cb, NULL)
+};
+
+static const struct pci_error_handlers ivpu_drv_pci_err = {
+ .reset_prepare = ivpu_pm_reset_prepare_cb,
+ .reset_done = ivpu_pm_reset_done_cb,
+};
+
+static struct pci_driver ivpu_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ivpu_pci_ids,
+ .probe = ivpu_probe,
+ .remove = ivpu_remove,
+ .driver = {
+ .pm = &ivpu_drv_pci_pm,
+ },
+ .err_handler = &ivpu_drv_pci_err,
+};
+
+module_pci_driver(ivpu_pci_driver);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
+MODULE_VERSION(DRIVER_VERSION_STR);
diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h
new file mode 100644
index 000000000000..5b34b6f50e69
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_drv.h
@@ -0,0 +1,316 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#ifndef __IVPU_DRV_H__
+#define __IVPU_DRV_H__
+
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_mm.h>
+#include <drm/drm_print.h>
+
+#include <linux/pci.h>
+#include <linux/xarray.h>
+#include <uapi/drm/ivpu_accel.h>
+
+#include "ivpu_mmu_context.h"
+#include "ivpu_ipc.h"
+
+#define DRIVER_NAME "intel_vpu"
+#define DRIVER_DESC "Driver for Intel NPU (Neural Processing Unit)"
+
+#define PCI_DEVICE_ID_MTL 0x7d1d
+#define PCI_DEVICE_ID_ARL 0xad1d
+#define PCI_DEVICE_ID_LNL 0x643e
+#define PCI_DEVICE_ID_PTL_P 0xb03e
+#define PCI_DEVICE_ID_WCL 0xfd3e
+#define PCI_DEVICE_ID_NVL 0xd71d
+
+#define IVPU_HW_IP_37XX 37
+#define IVPU_HW_IP_40XX 40
+#define IVPU_HW_IP_50XX 50
+#define IVPU_HW_IP_60XX 60
+
+#define IVPU_HW_IP_REV_LNL_B0 4
+
+#define IVPU_HW_BTRS_MTL 1
+#define IVPU_HW_BTRS_LNL 2
+
+#define IVPU_GLOBAL_CONTEXT_MMU_SSID 0
+/* SSID 1 is used by the VPU to represent reserved context */
+#define IVPU_RESERVED_CONTEXT_MMU_SSID 1
+#define IVPU_USER_CONTEXT_MIN_SSID 2
+#define IVPU_USER_CONTEXT_MAX_SSID (IVPU_USER_CONTEXT_MIN_SSID + 63)
+
+#define IVPU_MIN_DB 1
+#define IVPU_MAX_DB 255
+
+#define IVPU_JOB_ID_JOB_MASK GENMASK(7, 0)
+#define IVPU_JOB_ID_CONTEXT_MASK GENMASK(31, 8)
+
+#define IVPU_NUM_PRIORITIES 4
+#define IVPU_NUM_CMDQS_PER_CTX (IVPU_NUM_PRIORITIES)
+
+#define IVPU_CMDQ_MIN_ID 1
+#define IVPU_CMDQ_MAX_ID 255
+
+#define IVPU_PLATFORM_SILICON 0
+#define IVPU_PLATFORM_SIMICS 2
+#define IVPU_PLATFORM_FPGA 3
+#define IVPU_PLATFORM_HSLE 4
+#define IVPU_PLATFORM_INVALID 8
+
+#define IVPU_SCHED_MODE_AUTO -1
+
+#define IVPU_DBG_REG BIT(0)
+#define IVPU_DBG_IRQ BIT(1)
+#define IVPU_DBG_MMU BIT(2)
+#define IVPU_DBG_FILE BIT(3)
+#define IVPU_DBG_MISC BIT(4)
+#define IVPU_DBG_FW_BOOT BIT(5)
+#define IVPU_DBG_PM BIT(6)
+#define IVPU_DBG_IPC BIT(7)
+#define IVPU_DBG_BO BIT(8)
+#define IVPU_DBG_JOB BIT(9)
+#define IVPU_DBG_JSM BIT(10)
+#define IVPU_DBG_KREF BIT(11)
+#define IVPU_DBG_RPM BIT(12)
+#define IVPU_DBG_MMU_MAP BIT(13)
+#define IVPU_DBG_IOCTL BIT(14)
+
+#define ivpu_err(vdev, fmt, ...) \
+ drm_err(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
+
+#define ivpu_err_ratelimited(vdev, fmt, ...) \
+ drm_err_ratelimited(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
+
+#define ivpu_warn(vdev, fmt, ...) \
+ drm_warn(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
+
+#define ivpu_warn_ratelimited(vdev, fmt, ...) \
+ drm_err_ratelimited(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
+
+#define ivpu_info(vdev, fmt, ...) drm_info(&(vdev)->drm, fmt, ##__VA_ARGS__)
+
+#define ivpu_dbg(vdev, type, fmt, args...) do { \
+ if (unlikely(IVPU_DBG_##type & ivpu_dbg_mask)) \
+ dev_dbg((vdev)->drm.dev, "[%s] " fmt, #type, ##args); \
+} while (0)
+
+#define IVPU_WA(wa_name) (vdev->wa.wa_name)
+
+#define IVPU_PRINT_WA(wa_name) do { \
+ if (IVPU_WA(wa_name)) \
+ ivpu_dbg(vdev, MISC, "Using WA: " #wa_name "\n"); \
+} while (0)
+
+struct ivpu_wa_table {
+ bool punit_disabled;
+ bool clear_runtime_mem;
+ bool interrupt_clear_with_0;
+ bool disable_clock_relinquish;
+ bool disable_d0i3_msg;
+ bool wp0_during_power_up;
+ bool disable_d0i2;
+};
+
+struct ivpu_hw_info;
+struct ivpu_mmu_info;
+struct ivpu_fw_info;
+struct ivpu_ipc_info;
+struct ivpu_pm_info;
+
+struct ivpu_device {
+ struct drm_device drm;
+ void __iomem *regb;
+ void __iomem *regv;
+ u32 platform;
+ u32 irq;
+
+ struct ivpu_wa_table wa;
+ struct ivpu_hw_info *hw;
+ struct ivpu_mmu_info *mmu;
+ struct ivpu_fw_info *fw;
+ struct ivpu_ipc_info *ipc;
+ struct ivpu_pm_info *pm;
+
+ struct ivpu_mmu_context gctx;
+ struct ivpu_mmu_context rctx;
+ struct mutex context_list_lock; /* Protects user context addition/removal */
+ struct xarray context_xa;
+ struct xa_limit context_xa_limit;
+
+ struct xarray db_xa;
+ struct xa_limit db_limit;
+ u32 db_next;
+
+ struct work_struct irq_ipc_work;
+ struct work_struct irq_dct_work;
+ struct work_struct context_abort_work;
+
+ struct mutex bo_list_lock; /* Protects bo_list */
+ struct list_head bo_list;
+
+ struct mutex submitted_jobs_lock; /* Protects submitted_jobs */
+ struct xarray submitted_jobs_xa;
+ struct ivpu_ipc_consumer job_done_consumer;
+ atomic_t job_timeout_counter;
+
+ atomic64_t unique_id_counter;
+
+ ktime_t busy_start_ts;
+ ktime_t busy_time;
+
+ struct {
+ int boot;
+ int jsm;
+ int tdr;
+ int inference;
+ int autosuspend;
+ int d0i3_entry_msg;
+ int state_dump_msg;
+ } timeout;
+};
+
+/*
+ * file_priv has its own refcount (ref) that allows user space to close the fd
+ * without blocking even if VPU is still processing some jobs.
+ */
+struct ivpu_file_priv {
+ struct kref ref;
+ struct ivpu_device *vdev;
+ struct mutex lock; /* Protects cmdq */
+ struct xarray cmdq_xa;
+ struct ivpu_mmu_context ctx;
+ struct mutex ms_lock; /* Protects ms_instance_list, ms_info_bo */
+ struct list_head ms_instance_list;
+ struct ivpu_bo *ms_info_bo;
+ struct xa_limit job_limit;
+ u32 job_id_next;
+ struct xa_limit cmdq_limit;
+ u32 cmdq_id_next;
+ bool has_mmu_faults;
+ bool bound;
+ bool aborted;
+};
+
+extern int ivpu_dbg_mask;
+extern u8 ivpu_pll_min_ratio;
+extern u8 ivpu_pll_max_ratio;
+extern int ivpu_sched_mode;
+extern bool ivpu_disable_mmu_cont_pages;
+extern bool ivpu_force_snoop;
+
+#define IVPU_TEST_MODE_FW_TEST BIT(0)
+#define IVPU_TEST_MODE_NULL_HW BIT(1)
+#define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2)
+#define IVPU_TEST_MODE_D0I3_MSG_DISABLE BIT(4)
+#define IVPU_TEST_MODE_D0I3_MSG_ENABLE BIT(5)
+#define IVPU_TEST_MODE_MIP_DISABLE BIT(6)
+#define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8)
+#define IVPU_TEST_MODE_TURBO_ENABLE BIT(9)
+#define IVPU_TEST_MODE_TURBO_DISABLE BIT(10)
+#define IVPU_TEST_MODE_CLK_RELINQ_DISABLE BIT(11)
+#define IVPU_TEST_MODE_CLK_RELINQ_ENABLE BIT(12)
+#define IVPU_TEST_MODE_D0I2_DISABLE BIT(13)
+extern int ivpu_test_mode;
+
+struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv);
+void ivpu_file_priv_put(struct ivpu_file_priv **link);
+
+int ivpu_boot(struct ivpu_device *vdev);
+int ivpu_shutdown(struct ivpu_device *vdev);
+void ivpu_prepare_for_reset(struct ivpu_device *vdev);
+bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability);
+
+static inline u8 ivpu_revision(struct ivpu_device *vdev)
+{
+ return to_pci_dev(vdev->drm.dev)->revision;
+}
+
+static inline u16 ivpu_device_id(struct ivpu_device *vdev)
+{
+ return to_pci_dev(vdev->drm.dev)->device;
+}
+
+static inline int ivpu_hw_ip_gen(struct ivpu_device *vdev)
+{
+ switch (ivpu_device_id(vdev)) {
+ case PCI_DEVICE_ID_MTL:
+ case PCI_DEVICE_ID_ARL:
+ return IVPU_HW_IP_37XX;
+ case PCI_DEVICE_ID_LNL:
+ return IVPU_HW_IP_40XX;
+ case PCI_DEVICE_ID_PTL_P:
+ case PCI_DEVICE_ID_WCL:
+ return IVPU_HW_IP_50XX;
+ case PCI_DEVICE_ID_NVL:
+ return IVPU_HW_IP_60XX;
+ default:
+ dump_stack();
+ ivpu_err(vdev, "Unknown NPU IP generation\n");
+ return 0;
+ }
+}
+
+static inline int ivpu_hw_btrs_gen(struct ivpu_device *vdev)
+{
+ switch (ivpu_device_id(vdev)) {
+ case PCI_DEVICE_ID_MTL:
+ case PCI_DEVICE_ID_ARL:
+ return IVPU_HW_BTRS_MTL;
+ case PCI_DEVICE_ID_LNL:
+ case PCI_DEVICE_ID_PTL_P:
+ case PCI_DEVICE_ID_WCL:
+ case PCI_DEVICE_ID_NVL:
+ return IVPU_HW_BTRS_LNL;
+ default:
+ dump_stack();
+ ivpu_err(vdev, "Unknown buttress generation\n");
+ return 0;
+ }
+}
+
+static inline struct ivpu_device *to_ivpu_device(struct drm_device *dev)
+{
+ return container_of(dev, struct ivpu_device, drm);
+}
+
+static inline u32 ivpu_get_context_count(struct ivpu_device *vdev)
+{
+ struct xa_limit ctx_limit = vdev->context_xa_limit;
+
+ return (ctx_limit.max - ctx_limit.min + 1);
+}
+
+static inline u32 ivpu_get_platform(struct ivpu_device *vdev)
+{
+ WARN_ON_ONCE(vdev->platform == IVPU_PLATFORM_INVALID);
+ return vdev->platform;
+}
+
+static inline bool ivpu_is_silicon(struct ivpu_device *vdev)
+{
+ return ivpu_get_platform(vdev) == IVPU_PLATFORM_SILICON;
+}
+
+static inline bool ivpu_is_simics(struct ivpu_device *vdev)
+{
+ return ivpu_get_platform(vdev) == IVPU_PLATFORM_SIMICS;
+}
+
+static inline bool ivpu_is_fpga(struct ivpu_device *vdev)
+{
+ return ivpu_get_platform(vdev) == IVPU_PLATFORM_FPGA ||
+ ivpu_get_platform(vdev) == IVPU_PLATFORM_HSLE;
+}
+
+static inline bool ivpu_is_force_snoop_enabled(struct ivpu_device *vdev)
+{
+ return ivpu_force_snoop;
+}
+
+#endif /* __IVPU_DRV_H__ */
diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c
new file mode 100644
index 000000000000..48386d2cddbb
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_fw.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#include <linux/firmware.h>
+#include <linux/highmem.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+
+#include "vpu_boot_api.h"
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_fw_log.h"
+#include "ivpu_gem.h"
+#include "ivpu_hw.h"
+#include "ivpu_ipc.h"
+#include "ivpu_pm.h"
+
+#define FW_SHAVE_NN_MAX_SIZE SZ_2M
+#define FW_FILE_IMAGE_OFFSET (VPU_FW_HEADER_SIZE + FW_VERSION_HEADER_SIZE)
+#define FW_PREEMPT_BUF_MIN_SIZE SZ_4K
+#define FW_PREEMPT_BUF_MAX_SIZE SZ_32M
+
+#define WATCHDOG_MSS_REDIRECT 32
+#define WATCHDOG_NCE_REDIRECT 33
+
+#define ADDR_TO_L2_CACHE_CFG(addr) ((addr) >> 31)
+
+/* Check if FW API is compatible with the driver */
+#define IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, name, min_major) \
+ ivpu_fw_check_api(vdev, fw_hdr, #name, \
+ VPU_##name##_API_VER_INDEX, \
+ VPU_##name##_API_VER_MAJOR, \
+ VPU_##name##_API_VER_MINOR, min_major)
+
+/* Check if API version is lower that the given version */
+#define IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, name, major, minor) \
+ ivpu_fw_check_api_ver_lt(vdev, fw_hdr, #name, VPU_##name##_API_VER_INDEX, major, minor)
+
+#define IVPU_FOCUS_PRESENT_TIMER_MS 1000
+
+static char *ivpu_firmware;
+#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG)
+module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644);
+MODULE_PARM_DESC(firmware, "NPU firmware binary in /lib/firmware/..");
+#endif
+
+static struct {
+ int gen;
+ const char *name;
+} fw_names[] = {
+ { IVPU_HW_IP_37XX, "intel/vpu/vpu_37xx_v1.bin" },
+ { IVPU_HW_IP_37XX, "intel/vpu/vpu_37xx_v0.0.bin" },
+ { IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v1.bin" },
+ { IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v0.0.bin" },
+ { IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v1.bin" },
+ { IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v0.0.bin" },
+ { IVPU_HW_IP_60XX, "intel/vpu/vpu_60xx_v1.bin" },
+};
+
+/* Production fw_names from the table above */
+MODULE_FIRMWARE("intel/vpu/vpu_37xx_v1.bin");
+MODULE_FIRMWARE("intel/vpu/vpu_40xx_v1.bin");
+MODULE_FIRMWARE("intel/vpu/vpu_50xx_v1.bin");
+MODULE_FIRMWARE("intel/vpu/vpu_60xx_v1.bin");
+
+static int ivpu_fw_request(struct ivpu_device *vdev)
+{
+ int ret = -ENOENT;
+ int i;
+
+ if (ivpu_firmware) {
+ ret = request_firmware(&vdev->fw->file, ivpu_firmware, vdev->drm.dev);
+ if (!ret)
+ vdev->fw->name = ivpu_firmware;
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fw_names); i++) {
+ if (fw_names[i].gen != ivpu_hw_ip_gen(vdev))
+ continue;
+
+ ret = firmware_request_nowarn(&vdev->fw->file, fw_names[i].name, vdev->drm.dev);
+ if (!ret) {
+ vdev->fw->name = fw_names[i].name;
+ return 0;
+ }
+ }
+
+ ivpu_err(vdev, "Failed to request firmware: %d\n", ret);
+ return ret;
+}
+
+static int
+ivpu_fw_check_api(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr,
+ const char *str, int index, u16 expected_major, u16 expected_minor,
+ u16 min_major)
+{
+ u16 major = (u16)(fw_hdr->api_version[index] >> 16);
+ u16 minor = (u16)(fw_hdr->api_version[index]);
+
+ if (major < min_major) {
+ ivpu_err(vdev, "Incompatible FW %s API version: %d.%d, required %d.0 or later\n",
+ str, major, minor, min_major);
+ return -EINVAL;
+ }
+ if (major != expected_major) {
+ ivpu_warn(vdev, "Major FW %s API version different: %d.%d (expected %d.%d)\n",
+ str, major, minor, expected_major, expected_minor);
+ }
+ ivpu_dbg(vdev, FW_BOOT, "FW %s API version: %d.%d (expected %d.%d)\n",
+ str, major, minor, expected_major, expected_minor);
+
+ return 0;
+}
+
+static bool
+ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr,
+ const char *str, int index, u16 major, u16 minor)
+{
+ u16 fw_major = (u16)(fw_hdr->api_version[index] >> 16);
+ u16 fw_minor = (u16)(fw_hdr->api_version[index]);
+
+ if (fw_major < major || (fw_major == major && fw_minor < minor))
+ return true;
+
+ return false;
+}
+
+bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range)
+{
+ u64 addr_end;
+
+ if (!range || check_add_overflow(addr, size, &addr_end))
+ return false;
+
+ if (addr < range->start || addr_end > range->end)
+ return false;
+
+ return true;
+}
+
+static u32
+ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr)
+{
+ if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX &&
+ ivpu_sched_mode == VPU_SCHEDULING_MODE_OS) {
+ ivpu_warn(vdev, "OS sched mode is not supported, using HW mode\n");
+ return VPU_SCHEDULING_MODE_HW;
+ }
+
+ if (ivpu_sched_mode != IVPU_SCHED_MODE_AUTO)
+ return ivpu_sched_mode;
+
+ if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, JSM, 3, 24))
+ return VPU_SCHEDULING_MODE_OS;
+
+ return VPU_SCHEDULING_MODE_HW;
+}
+
+static void
+ivpu_preemption_config_parse(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+ u32 primary_preempt_buf_size, secondary_preempt_buf_size;
+
+ if (fw_hdr->preemption_buffer_1_max_size)
+ primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size;
+ else
+ primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
+
+ if (fw_hdr->preemption_buffer_2_max_size)
+ secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_max_size;
+ else
+ secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size;
+
+ ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, secondary: %u\n",
+ primary_preempt_buf_size, secondary_preempt_buf_size);
+
+ if (primary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE ||
+ secondary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE) {
+ ivpu_warn(vdev, "Preemption buffers size too small\n");
+ return;
+ }
+
+ if (primary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE ||
+ secondary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE) {
+ ivpu_warn(vdev, "Preemption buffers size too big\n");
+ return;
+ }
+
+ if (fw->sched_mode != VPU_SCHEDULING_MODE_HW)
+ return;
+
+ if (ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE)
+ return;
+
+ vdev->fw->primary_preempt_buf_size = ALIGN(primary_preempt_buf_size, PAGE_SIZE);
+ vdev->fw->secondary_preempt_buf_size = ALIGN(secondary_preempt_buf_size, PAGE_SIZE);
+}
+
+static int ivpu_fw_parse(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+ const struct vpu_firmware_header *fw_hdr = (const void *)fw->file->data;
+ struct ivpu_addr_range fw_image_range;
+ u64 boot_params_addr, boot_params_size;
+ u64 fw_version_addr, fw_version_size;
+ u64 runtime_addr, runtime_size;
+ u64 image_load_addr, image_size;
+
+ if (fw->file->size <= FW_FILE_IMAGE_OFFSET) {
+ ivpu_err(vdev, "Firmware file is too small: %zu\n", fw->file->size);
+ return -EINVAL;
+ }
+
+ if (fw_hdr->header_version != VPU_FW_HEADER_VERSION) {
+ ivpu_err(vdev, "Invalid firmware header version: %u\n", fw_hdr->header_version);
+ return -EINVAL;
+ }
+
+ boot_params_addr = fw_hdr->boot_params_load_address;
+ boot_params_size = SZ_4K;
+
+ if (!ivpu_is_within_range(boot_params_addr, boot_params_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid boot params address: 0x%llx\n", boot_params_addr);
+ return -EINVAL;
+ }
+
+ fw_version_addr = fw_hdr->firmware_version_load_address;
+ fw_version_size = ALIGN(fw_hdr->firmware_version_size, SZ_4K);
+
+ if (fw_version_size != SZ_4K) {
+ ivpu_err(vdev, "Invalid firmware version size: %u\n",
+ fw_hdr->firmware_version_size);
+ return -EINVAL;
+ }
+
+ if (!ivpu_is_within_range(fw_version_addr, fw_version_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid firmware version address: 0x%llx\n", fw_version_addr);
+ return -EINVAL;
+ }
+
+ runtime_addr = fw_hdr->image_load_address;
+ runtime_size = fw_hdr->runtime_size - boot_params_size - fw_version_size;
+
+ image_load_addr = fw_hdr->image_load_address;
+ image_size = fw_hdr->image_size;
+
+ if (!ivpu_is_within_range(runtime_addr, runtime_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx and size %llu\n",
+ runtime_addr, runtime_size);
+ return -EINVAL;
+ }
+
+ if (FW_FILE_IMAGE_OFFSET + image_size > fw->file->size) {
+ ivpu_err(vdev, "Invalid image size: %llu\n", image_size);
+ return -EINVAL;
+ }
+
+ if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n",
+ image_load_addr, image_size);
+ return -EINVAL;
+ }
+
+ if (ivpu_hw_range_init(vdev, &fw_image_range, image_load_addr, image_size))
+ return -EINVAL;
+
+ if (!ivpu_is_within_range(fw_hdr->entry_point, SZ_4K, &fw_image_range)) {
+ ivpu_err(vdev, "Invalid entry point: 0x%llx\n", fw_hdr->entry_point);
+ return -EINVAL;
+ }
+
+ if (fw_hdr->shave_nn_fw_size > FW_SHAVE_NN_MAX_SIZE) {
+ ivpu_err(vdev, "SHAVE NN firmware is too big: %u\n", fw_hdr->shave_nn_fw_size);
+ return -EINVAL;
+ }
+
+ ivpu_dbg(vdev, FW_BOOT, "Header version: 0x%x, format 0x%x\n",
+ fw_hdr->header_version, fw_hdr->image_format);
+
+ if (!scnprintf(fw->version, sizeof(fw->version), "%s", fw->file->data + VPU_FW_HEADER_SIZE))
+ ivpu_warn(vdev, "Missing firmware version\n");
+
+ ivpu_info(vdev, "Firmware: %s, version: %s\n", fw->name, fw->version);
+
+ if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, BOOT, 3))
+ return -EINVAL;
+ if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3))
+ return -EINVAL;
+
+ fw->boot_params_addr = boot_params_addr;
+ fw->boot_params_size = boot_params_size;
+ fw->fw_version_addr = fw_version_addr;
+ fw->fw_version_size = fw_version_size;
+ fw->runtime_addr = runtime_addr;
+ fw->runtime_size = runtime_size;
+ fw->image_load_offset = image_load_addr - runtime_addr;
+ fw->image_size = image_size;
+ fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size);
+
+ fw->cold_boot_entry_point = fw_hdr->entry_point;
+ fw->entry_point = fw->cold_boot_entry_point;
+
+ fw->trace_level = min_t(u32, ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
+ fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING;
+ fw->trace_hw_component_mask = -1;
+
+ fw->dvfs_mode = 0;
+
+ fw->sched_mode = ivpu_fw_sched_mode_select(vdev, fw_hdr);
+ ivpu_info(vdev, "Scheduler mode: %s\n", fw->sched_mode ? "HW" : "OS");
+
+ ivpu_preemption_config_parse(vdev, fw_hdr);
+ ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s supported\n",
+ ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not");
+
+ if (fw_hdr->ro_section_start_address &&
+ !ivpu_is_within_range(fw_hdr->ro_section_start_address, fw_hdr->ro_section_size,
+ &fw_image_range)) {
+ ivpu_err(vdev, "Invalid read-only section: start address 0x%llx, size %u\n",
+ fw_hdr->ro_section_start_address, fw_hdr->ro_section_size);
+ return -EINVAL;
+ }
+
+ fw->read_only_addr = fw_hdr->ro_section_start_address;
+ fw->read_only_size = fw_hdr->ro_section_size;
+
+ ivpu_dbg(vdev, FW_BOOT, "Boot params: address 0x%llx, size %llu\n",
+ fw->boot_params_addr, fw->boot_params_size);
+ ivpu_dbg(vdev, FW_BOOT, "FW version: address 0x%llx, size %llu\n",
+ fw->fw_version_addr, fw->fw_version_size);
+ ivpu_dbg(vdev, FW_BOOT, "Runtime: address 0x%llx, size %u\n",
+ fw->runtime_addr, fw->runtime_size);
+ ivpu_dbg(vdev, FW_BOOT, "Image load offset: 0x%llx, size %u\n",
+ fw->image_load_offset, fw->image_size);
+ ivpu_dbg(vdev, FW_BOOT, "Read-only section: address 0x%llx, size %u\n",
+ fw->read_only_addr, fw->read_only_size);
+ ivpu_dbg(vdev, FW_BOOT, "FW entry point: 0x%llx\n", fw->entry_point);
+ ivpu_dbg(vdev, FW_BOOT, "SHAVE NN size: %u\n", fw->shave_nn_size);
+
+ return 0;
+}
+
+static void ivpu_fw_release(struct ivpu_device *vdev)
+{
+ release_firmware(vdev->fw->file);
+}
+
+/* Initialize workarounds that depend on FW version */
+static void
+ivpu_fw_init_wa(struct ivpu_device *vdev)
+{
+ const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data;
+
+ if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) ||
+ (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE))
+ vdev->wa.disable_d0i3_msg = true;
+
+ /* Force enable the feature for testing purposes */
+ if (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_ENABLE)
+ vdev->wa.disable_d0i3_msg = false;
+
+ IVPU_PRINT_WA(disable_d0i3_msg);
+}
+
+static int ivpu_fw_mem_init(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+ int log_verb_size;
+ int ret;
+
+ fw->mem_bp = ivpu_bo_create_runtime(vdev, fw->boot_params_addr, fw->boot_params_size,
+ DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem_bp) {
+ ivpu_err(vdev, "Failed to create firmware boot params memory buffer\n");
+ return -ENOMEM;
+ }
+
+ fw->mem_fw_ver = ivpu_bo_create_runtime(vdev, fw->fw_version_addr, fw->fw_version_size,
+ DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem_fw_ver) {
+ ivpu_err(vdev, "Failed to create firmware version memory buffer\n");
+ ret = -ENOMEM;
+ goto err_free_bp;
+ }
+
+ fw->mem = ivpu_bo_create_runtime(vdev, fw->runtime_addr, fw->runtime_size,
+ DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem) {
+ ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n");
+ ret = -ENOMEM;
+ goto err_free_fw_ver;
+ }
+
+ ret = ivpu_mmu_context_set_pages_ro(vdev, &vdev->gctx, fw->read_only_addr,
+ fw->read_only_size);
+ if (ret) {
+ ivpu_err(vdev, "Failed to set firmware image read-only\n");
+ goto err_free_fw_mem;
+ }
+
+ fw->mem_log_crit = ivpu_bo_create_global(vdev, IVPU_FW_CRITICAL_BUFFER_SIZE,
+ DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem_log_crit) {
+ ivpu_err(vdev, "Failed to create critical log buffer\n");
+ ret = -ENOMEM;
+ goto err_free_fw_mem;
+ }
+
+ if (ivpu_fw_log_level <= IVPU_FW_LOG_INFO)
+ log_verb_size = IVPU_FW_VERBOSE_BUFFER_LARGE_SIZE;
+ else
+ log_verb_size = IVPU_FW_VERBOSE_BUFFER_SMALL_SIZE;
+
+ fw->mem_log_verb = ivpu_bo_create_global(vdev, log_verb_size,
+ DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem_log_verb) {
+ ivpu_err(vdev, "Failed to create verbose log buffer\n");
+ ret = -ENOMEM;
+ goto err_free_log_crit;
+ }
+
+ if (fw->shave_nn_size) {
+ fw->mem_shave_nn = ivpu_bo_create(vdev, &vdev->gctx, &vdev->hw->ranges.shave,
+ fw->shave_nn_size, DRM_IVPU_BO_WC);
+ if (!fw->mem_shave_nn) {
+ ivpu_err(vdev, "Failed to create shavenn buffer\n");
+ ret = -ENOMEM;
+ goto err_free_log_verb;
+ }
+ }
+
+ return 0;
+
+err_free_log_verb:
+ ivpu_bo_free(fw->mem_log_verb);
+err_free_log_crit:
+ ivpu_bo_free(fw->mem_log_crit);
+err_free_fw_mem:
+ ivpu_bo_free(fw->mem);
+err_free_fw_ver:
+ ivpu_bo_free(fw->mem_fw_ver);
+err_free_bp:
+ ivpu_bo_free(fw->mem_bp);
+ return ret;
+}
+
+static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+
+ if (fw->mem_shave_nn) {
+ ivpu_bo_free(fw->mem_shave_nn);
+ fw->mem_shave_nn = NULL;
+ }
+
+ ivpu_bo_free(fw->mem_log_verb);
+ ivpu_bo_free(fw->mem_log_crit);
+ ivpu_bo_free(fw->mem);
+ ivpu_bo_free(fw->mem_fw_ver);
+ ivpu_bo_free(fw->mem_bp);
+
+ fw->mem_log_verb = NULL;
+ fw->mem_log_crit = NULL;
+ fw->mem = NULL;
+ fw->mem_fw_ver = NULL;
+ fw->mem_bp = NULL;
+}
+
+int ivpu_fw_init(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ret = ivpu_fw_request(vdev);
+ if (ret)
+ return ret;
+
+ ret = ivpu_fw_parse(vdev);
+ if (ret)
+ goto err_fw_release;
+
+ ivpu_fw_init_wa(vdev);
+
+ ret = ivpu_fw_mem_init(vdev);
+ if (ret)
+ goto err_fw_release;
+
+ ivpu_fw_load(vdev);
+
+ return 0;
+
+err_fw_release:
+ ivpu_fw_release(vdev);
+ return ret;
+}
+
+void ivpu_fw_fini(struct ivpu_device *vdev)
+{
+ ivpu_fw_mem_fini(vdev);
+ ivpu_fw_release(vdev);
+}
+
+void ivpu_fw_load(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+ u64 image_end_offset = fw->image_load_offset + fw->image_size;
+
+ memset(ivpu_bo_vaddr(fw->mem), 0, fw->image_load_offset);
+ memcpy(ivpu_bo_vaddr(fw->mem) + fw->image_load_offset,
+ fw->file->data + FW_FILE_IMAGE_OFFSET, fw->image_size);
+
+ if (IVPU_WA(clear_runtime_mem)) {
+ u8 *start = ivpu_bo_vaddr(fw->mem) + image_end_offset;
+ u64 size = ivpu_bo_size(fw->mem) - image_end_offset;
+
+ memset(start, 0, size);
+ }
+
+ wmb(); /* Flush WC buffers after writing fw->mem */
+}
+
+static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_params *boot_params)
+{
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.magic = 0x%x\n",
+ boot_params->magic);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_id = 0x%x\n",
+ boot_params->vpu_id);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_count = 0x%x\n",
+ boot_params->vpu_count);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.frequency = %u\n",
+ boot_params->frequency);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.perf_clk_frequency = %u\n",
+ boot_params->perf_clk_frequency);
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_header_area_start = 0x%llx\n",
+ boot_params->ipc_header_area_start);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_header_area_size = 0x%x\n",
+ boot_params->ipc_header_area_size);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.shared_region_base = 0x%llx\n",
+ boot_params->shared_region_base);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.shared_region_size = 0x%x\n",
+ boot_params->shared_region_size);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_payload_area_start = 0x%llx\n",
+ boot_params->ipc_payload_area_start);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_payload_area_size = 0x%x\n",
+ boot_params->ipc_payload_area_size);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.global_aliased_pio_base = 0x%llx\n",
+ boot_params->global_aliased_pio_base);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.global_aliased_pio_size = 0x%x\n",
+ boot_params->global_aliased_pio_size);
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.autoconfig = 0x%x\n",
+ boot_params->autoconfig);
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].use = 0x%x\n",
+ boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].use);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg = 0x%x\n",
+ boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg);
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.shave_nn_fw_base = 0x%llx\n",
+ boot_params->shave_nn_fw_base);
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.watchdog_irq_mss = 0x%x\n",
+ boot_params->watchdog_irq_mss);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.watchdog_irq_nce = 0x%x\n",
+ boot_params->watchdog_irq_nce);
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.host_version_id = 0x%x\n",
+ boot_params->host_version_id);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.si_stepping = 0x%x\n",
+ boot_params->si_stepping);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.device_id = 0x%llx\n",
+ boot_params->device_id);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.feature_exclusion = 0x%llx\n",
+ boot_params->feature_exclusion);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.sku = 0x%llx\n",
+ boot_params->sku);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.min_freq_pll_ratio = 0x%x\n",
+ boot_params->min_freq_pll_ratio);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.pn_freq_pll_ratio = 0x%x\n",
+ boot_params->pn_freq_pll_ratio);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.max_freq_pll_ratio = 0x%x\n",
+ boot_params->max_freq_pll_ratio);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.default_trace_level = 0x%x\n",
+ boot_params->default_trace_level);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.tracing_buff_message_format_mask = 0x%llx\n",
+ boot_params->tracing_buff_message_format_mask);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.trace_destination_mask = 0x%x\n",
+ boot_params->trace_destination_mask);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.trace_hw_component_mask = 0x%llx\n",
+ boot_params->trace_hw_component_mask);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.boot_type = 0x%x\n",
+ boot_params->boot_type);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.punit_telemetry_sram_base = 0x%llx\n",
+ boot_params->punit_telemetry_sram_base);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.punit_telemetry_sram_size = 0x%llx\n",
+ boot_params->punit_telemetry_sram_size);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_telemetry_enable = 0x%x\n",
+ boot_params->vpu_telemetry_enable);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_scheduling_mode = 0x%x\n",
+ boot_params->vpu_scheduling_mode);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.dvfs_mode = %u\n",
+ boot_params->dvfs_mode);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_delayed_entry = %d\n",
+ boot_params->d0i3_delayed_entry);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n",
+ boot_params->d0i3_residency_time_us);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n",
+ boot_params->d0i3_entry_vpu_ts);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n",
+ boot_params->system_time_us);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.power_profile = 0x%x\n",
+ boot_params->power_profile);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_uses_ecc_mca_signal = 0x%x\n",
+ boot_params->vpu_uses_ecc_mca_signal);
+}
+
+void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params)
+{
+ struct ivpu_bo *ipc_mem_rx = vdev->ipc->mem_rx;
+
+ /* In case of warm boot only update variable params */
+ if (!ivpu_fw_is_cold_boot(vdev)) {
+ boot_params->d0i3_residency_time_us =
+ ktime_us_delta(ktime_get_boottime(), vdev->hw->d0i3_entry_host_ts);
+ boot_params->d0i3_entry_vpu_ts = vdev->hw->d0i3_entry_vpu_ts;
+ boot_params->system_time_us = ktime_to_us(ktime_get_real());
+
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n",
+ boot_params->d0i3_residency_time_us);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n",
+ boot_params->d0i3_entry_vpu_ts);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n",
+ boot_params->system_time_us);
+
+ boot_params->save_restore_ret_address = 0;
+ vdev->pm->is_warmboot = true;
+ wmb(); /* Flush WC buffers after writing save_restore_ret_address */
+ return;
+ }
+
+ memset(boot_params, 0, sizeof(*boot_params));
+ vdev->pm->is_warmboot = false;
+
+ boot_params->magic = VPU_BOOT_PARAMS_MAGIC;
+ boot_params->vpu_id = to_pci_dev(vdev->drm.dev)->bus->number;
+
+ /*
+ * This param is a debug firmware feature. It switches default clock
+ * to higher resolution one for fine-grained and more accurate firmware
+ * task profiling.
+ */
+ boot_params->perf_clk_frequency = ivpu_hw_profiling_freq_get(vdev);
+
+ /*
+ * Uncached region of VPU address space, covers IPC buffers, job queues
+ * and log buffers, programmable to L2$ Uncached by VPU MTRR
+ */
+ boot_params->shared_region_base = vdev->hw->ranges.global.start;
+ boot_params->shared_region_size = vdev->hw->ranges.global.end -
+ vdev->hw->ranges.global.start;
+
+ boot_params->ipc_header_area_start = ipc_mem_rx->vpu_addr;
+ boot_params->ipc_header_area_size = ivpu_bo_size(ipc_mem_rx) / 2;
+
+ boot_params->ipc_payload_area_start = ipc_mem_rx->vpu_addr + ivpu_bo_size(ipc_mem_rx) / 2;
+ boot_params->ipc_payload_area_size = ivpu_bo_size(ipc_mem_rx) / 2;
+
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ boot_params->global_aliased_pio_base = vdev->hw->ranges.user.start;
+ boot_params->global_aliased_pio_size = ivpu_hw_range_size(&vdev->hw->ranges.user);
+ }
+
+ /* Allow configuration for L2C_PAGE_TABLE with boot param value */
+ boot_params->autoconfig = 1;
+
+ /* Enable L2 cache for first 2GB of high memory */
+ boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].use = 1;
+ boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg =
+ ADDR_TO_L2_CACHE_CFG(vdev->hw->ranges.shave.start);
+
+ if (vdev->fw->mem_shave_nn)
+ boot_params->shave_nn_fw_base = vdev->fw->mem_shave_nn->vpu_addr;
+
+ boot_params->watchdog_irq_mss = WATCHDOG_MSS_REDIRECT;
+ boot_params->watchdog_irq_nce = WATCHDOG_NCE_REDIRECT;
+ boot_params->si_stepping = ivpu_revision(vdev);
+ boot_params->device_id = ivpu_device_id(vdev);
+ boot_params->feature_exclusion = vdev->hw->tile_fuse;
+ boot_params->sku = vdev->hw->sku;
+
+ boot_params->min_freq_pll_ratio = vdev->hw->pll.min_ratio;
+ boot_params->pn_freq_pll_ratio = vdev->hw->pll.pn_ratio;
+ boot_params->max_freq_pll_ratio = vdev->hw->pll.max_ratio;
+
+ boot_params->default_trace_level = vdev->fw->trace_level;
+ boot_params->tracing_buff_message_format_mask = BIT(VPU_TRACING_FORMAT_STRING);
+ boot_params->trace_destination_mask = vdev->fw->trace_destination_mask;
+ boot_params->trace_hw_component_mask = vdev->fw->trace_hw_component_mask;
+ boot_params->crit_tracing_buff_addr = vdev->fw->mem_log_crit->vpu_addr;
+ boot_params->crit_tracing_buff_size = ivpu_bo_size(vdev->fw->mem_log_crit);
+ boot_params->verbose_tracing_buff_addr = vdev->fw->mem_log_verb->vpu_addr;
+ boot_params->verbose_tracing_buff_size = ivpu_bo_size(vdev->fw->mem_log_verb);
+
+ boot_params->punit_telemetry_sram_base = ivpu_hw_telemetry_offset_get(vdev);
+ boot_params->punit_telemetry_sram_size = ivpu_hw_telemetry_size_get(vdev);
+ boot_params->vpu_telemetry_enable = ivpu_hw_telemetry_enable_get(vdev);
+ boot_params->vpu_scheduling_mode = vdev->fw->sched_mode;
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
+ boot_params->vpu_focus_present_timer_ms = IVPU_FOCUS_PRESENT_TIMER_MS;
+ boot_params->dvfs_mode = vdev->fw->dvfs_mode;
+ if (!IVPU_WA(disable_d0i3_msg))
+ boot_params->d0i3_delayed_entry = 1;
+ boot_params->d0i3_residency_time_us = 0;
+ boot_params->d0i3_entry_vpu_ts = 0;
+ if (IVPU_WA(disable_d0i2))
+ boot_params->power_profile |= BIT(1);
+ boot_params->vpu_uses_ecc_mca_signal =
+ ivpu_hw_uses_ecc_mca_signal(vdev) ? VPU_BOOT_MCA_ECC_BOTH : 0;
+
+ boot_params->system_time_us = ktime_to_us(ktime_get_real());
+ wmb(); /* Flush WC buffers after writing bootparams */
+
+ ivpu_fw_boot_params_print(vdev, boot_params);
+}
diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h
new file mode 100644
index 000000000000..00945892b55e
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_fw.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#ifndef __IVPU_FW_H__
+#define __IVPU_FW_H__
+
+#include "vpu_jsm_api.h"
+
+#define FW_VERSION_HEADER_SIZE SZ_4K
+#define FW_VERSION_STR_SIZE SZ_256
+
+struct ivpu_device;
+struct ivpu_bo;
+struct vpu_boot_params;
+
+struct ivpu_fw_info {
+ const struct firmware *file;
+ const char *name;
+ char version[FW_VERSION_STR_SIZE];
+ struct ivpu_bo *mem_bp;
+ struct ivpu_bo *mem_fw_ver;
+ struct ivpu_bo *mem;
+ struct ivpu_bo *mem_shave_nn;
+ struct ivpu_bo *mem_log_crit;
+ struct ivpu_bo *mem_log_verb;
+ u64 boot_params_addr;
+ u64 boot_params_size;
+ u64 fw_version_addr;
+ u64 fw_version_size;
+ u64 runtime_addr;
+ u32 runtime_size;
+ u64 image_load_offset;
+ u32 image_size;
+ u32 shave_nn_size;
+ u64 entry_point; /* Cold or warm boot entry point for next boot */
+ u64 cold_boot_entry_point;
+ u32 trace_level;
+ u32 trace_destination_mask;
+ u64 trace_hw_component_mask;
+ u32 dvfs_mode;
+ u32 primary_preempt_buf_size;
+ u32 secondary_preempt_buf_size;
+ u64 read_only_addr;
+ u32 read_only_size;
+ u32 sched_mode;
+ u64 last_heartbeat;
+};
+
+bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range);
+int ivpu_fw_init(struct ivpu_device *vdev);
+void ivpu_fw_fini(struct ivpu_device *vdev);
+void ivpu_fw_load(struct ivpu_device *vdev);
+void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params);
+
+static inline bool ivpu_fw_is_cold_boot(struct ivpu_device *vdev)
+{
+ return vdev->fw->entry_point == vdev->fw->cold_boot_entry_point;
+}
+
+static inline u32 ivpu_fw_preempt_buf_size(struct ivpu_device *vdev)
+{
+ return vdev->fw->primary_preempt_buf_size + vdev->fw->secondary_preempt_buf_size;
+}
+
+#endif /* __IVPU_FW_H__ */
diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c
new file mode 100644
index 000000000000..337c906b0210
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_fw_log.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <linux/ctype.h>
+#include <linux/highmem.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+
+#include "vpu_boot_api.h"
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_fw_log.h"
+#include "ivpu_gem.h"
+
+#define IVPU_FW_LOG_LINE_LENGTH 256
+
+unsigned int ivpu_fw_log_level = IVPU_FW_LOG_ERROR;
+module_param_named(fw_log_level, ivpu_fw_log_level, uint, 0444);
+MODULE_PARM_DESC(fw_log_level,
+ "NPU firmware default log level: debug=" __stringify(IVPU_FW_LOG_DEBUG)
+ " info=" __stringify(IVPU_FW_LOG_INFO)
+ " warn=" __stringify(IVPU_FW_LOG_WARN)
+ " error=" __stringify(IVPU_FW_LOG_ERROR)
+ " fatal=" __stringify(IVPU_FW_LOG_FATAL));
+
+static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *offset,
+ struct vpu_tracing_buffer_header **out_log)
+{
+ struct vpu_tracing_buffer_header *log;
+
+ if ((*offset + sizeof(*log)) > ivpu_bo_size(bo))
+ return -EINVAL;
+
+ log = ivpu_bo_vaddr(bo) + *offset;
+
+ if (log->vpu_canary_start != VPU_TRACING_BUFFER_CANARY)
+ return -EINVAL;
+
+ if (log->header_size < sizeof(*log) || log->header_size > 1024) {
+ ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size);
+ return -EINVAL;
+ }
+ if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) {
+ ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
+ return -EINVAL;
+ }
+
+ *out_log = log;
+ *offset += log->size;
+
+ ivpu_dbg(vdev, FW_BOOT,
+ "FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d",
+ log->name, log->write_index, log->size, log->wrap_count, log->header_version,
+ log->header_size, log->format, log->alignment);
+
+ return 0;
+}
+
+static void fw_log_print_lines(char *buffer, u32 size, struct drm_printer *p)
+{
+ char line[IVPU_FW_LOG_LINE_LENGTH];
+ u32 index = 0;
+
+ if (!size || !buffer)
+ return;
+
+ while (size--) {
+ if (*buffer == '\n' || *buffer == 0) {
+ line[index] = 0;
+ if (index != 0)
+ drm_printf(p, "%s\n", line);
+ index = 0;
+ buffer++;
+ continue;
+ }
+ if (index == IVPU_FW_LOG_LINE_LENGTH - 1) {
+ line[index] = 0;
+ index = 0;
+ drm_printf(p, "%s\n", line);
+ }
+ if (*buffer != '\r' && (isprint(*buffer) || iscntrl(*buffer)))
+ line[index++] = *buffer;
+ buffer++;
+ }
+ line[index] = 0;
+ if (index != 0)
+ drm_printf(p, "%s", line);
+}
+
+static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const char *prefix,
+ bool only_new_msgs, struct drm_printer *p)
+{
+ char *log_data = (void *)log + log->header_size;
+ u32 data_size = log->size - log->header_size;
+ u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0;
+ u32 log_end = READ_ONCE(log->write_index);
+
+ if (log->wrap_count == log->read_wrap_count) {
+ if (log_end <= log_start) {
+ drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name);
+ return;
+ }
+ } else if (log->wrap_count == log->read_wrap_count + 1) {
+ if (log_end > log_start)
+ log_start = log_end;
+ } else {
+ log_start = log_end;
+ }
+
+ drm_printf(p, "==== %s \"%s\" log start ====\n", prefix, log->name);
+ if (log_end > log_start) {
+ fw_log_print_lines(log_data + log_start, log_end - log_start, p);
+ } else {
+ fw_log_print_lines(log_data + log_start, data_size - log_start, p);
+ fw_log_print_lines(log_data, log_end, p);
+ }
+ drm_printf(p, "\n\x1b[0m"); /* add new line and clear formatting */
+ drm_printf(p, "==== %s \"%s\" log end ====\n", prefix, log->name);
+}
+
+static void
+fw_log_print_all_in_bo(struct ivpu_device *vdev, const char *name,
+ struct ivpu_bo *bo, bool only_new_msgs, struct drm_printer *p)
+{
+ struct vpu_tracing_buffer_header *log;
+ u32 next = 0;
+
+ while (fw_log_from_bo(vdev, bo, &next, &log) == 0)
+ fw_log_print_buffer(log, name, only_new_msgs, p);
+}
+
+void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_printer *p)
+{
+ fw_log_print_all_in_bo(vdev, "NPU critical", vdev->fw->mem_log_crit, only_new_msgs, p);
+ fw_log_print_all_in_bo(vdev, "NPU verbose", vdev->fw->mem_log_verb, only_new_msgs, p);
+}
+
+void ivpu_fw_log_mark_read(struct ivpu_device *vdev)
+{
+ struct vpu_tracing_buffer_header *log;
+ u32 next;
+
+ next = 0;
+ while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &log) == 0) {
+ log->read_index = READ_ONCE(log->write_index);
+ log->read_wrap_count = READ_ONCE(log->wrap_count);
+ }
+
+ next = 0;
+ while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &log) == 0) {
+ log->read_index = READ_ONCE(log->write_index);
+ log->read_wrap_count = READ_ONCE(log->wrap_count);
+ }
+}
+
+void ivpu_fw_log_reset(struct ivpu_device *vdev)
+{
+ struct vpu_tracing_buffer_header *log;
+ u32 next;
+
+ next = 0;
+ while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &log) == 0) {
+ log->read_index = 0;
+ log->read_wrap_count = 0;
+ }
+
+ next = 0;
+ while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &log) == 0) {
+ log->read_index = 0;
+ log->read_wrap_count = 0;
+ }
+}
diff --git a/drivers/accel/ivpu/ivpu_fw_log.h b/drivers/accel/ivpu/ivpu_fw_log.h
new file mode 100644
index 000000000000..8bb528a73cb7
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_fw_log.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_FW_LOG_H__
+#define __IVPU_FW_LOG_H__
+
+#include <linux/types.h>
+
+#include "ivpu_drv.h"
+
+#define IVPU_FW_LOG_DEFAULT 0
+#define IVPU_FW_LOG_DEBUG 1
+#define IVPU_FW_LOG_INFO 2
+#define IVPU_FW_LOG_WARN 3
+#define IVPU_FW_LOG_ERROR 4
+#define IVPU_FW_LOG_FATAL 5
+
+#define IVPU_FW_VERBOSE_BUFFER_SMALL_SIZE SZ_1M
+#define IVPU_FW_VERBOSE_BUFFER_LARGE_SIZE SZ_8M
+#define IVPU_FW_CRITICAL_BUFFER_SIZE SZ_512K
+
+extern unsigned int ivpu_fw_log_level;
+
+void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_printer *p);
+void ivpu_fw_log_mark_read(struct ivpu_device *vdev);
+void ivpu_fw_log_reset(struct ivpu_device *vdev);
+
+
+#endif /* __IVPU_FW_LOG_H__ */
diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c
new file mode 100644
index 000000000000..ece68f570b7e
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_gem.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/set_memory.h>
+#include <linux/xarray.h>
+
+#include <drm/drm_cache.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_file.h>
+#include <drm/drm_utils.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_gem.h"
+#include "ivpu_hw.h"
+#include "ivpu_mmu.h"
+#include "ivpu_mmu_context.h"
+
+MODULE_IMPORT_NS("DMA_BUF");
+
+static const struct drm_gem_object_funcs ivpu_gem_funcs;
+
+static inline void ivpu_dbg_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, const char *action)
+{
+ ivpu_dbg(vdev, BO,
+ "%6s: bo %8p size %9zu ctx %d vpu_addr %9llx pages %d sgt %d mmu_mapped %d wc %d imported %d\n",
+ action, bo, ivpu_bo_size(bo), bo->ctx_id, bo->vpu_addr,
+ (bool)bo->base.pages, (bool)bo->base.sgt, bo->mmu_mapped, bo->base.map_wc,
+ (bool)drm_gem_is_imported(&bo->base.base));
+}
+
+static inline int ivpu_bo_lock(struct ivpu_bo *bo)
+{
+ return dma_resv_lock(bo->base.base.resv, NULL);
+}
+
+static inline void ivpu_bo_unlock(struct ivpu_bo *bo)
+{
+ dma_resv_unlock(bo->base.base.resv);
+}
+
+static struct sg_table *ivpu_bo_map_attachment(struct ivpu_device *vdev, struct ivpu_bo *bo)
+{
+ struct sg_table *sgt;
+
+ drm_WARN_ON(&vdev->drm, !bo->base.base.import_attach);
+
+ ivpu_bo_lock(bo);
+
+ sgt = bo->base.sgt;
+ if (!sgt) {
+ sgt = dma_buf_map_attachment(bo->base.base.import_attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt))
+ ivpu_err(vdev, "Failed to map BO in IOMMU: %ld\n", PTR_ERR(sgt));
+ else
+ bo->base.sgt = sgt;
+ }
+
+ ivpu_bo_unlock(bo);
+
+ return sgt;
+}
+
+/*
+ * ivpu_bo_bind() - pin the backing physical pages and map them to VPU.
+ *
+ * This function pins physical memory pages, then maps the physical pages
+ * to IOMMU address space and finally updates the VPU MMU page tables
+ * to allow the VPU to translate VPU address to IOMMU address.
+ */
+int __must_check ivpu_bo_bind(struct ivpu_bo *bo)
+{
+ struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
+ struct sg_table *sgt;
+ int ret = 0;
+
+ ivpu_dbg_bo(vdev, bo, "bind");
+
+ if (bo->base.base.import_attach)
+ sgt = ivpu_bo_map_attachment(vdev, bo);
+ else
+ sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ ivpu_err(vdev, "Failed to map BO in IOMMU: %d\n", ret);
+ return ret;
+ }
+
+ ivpu_bo_lock(bo);
+
+ if (!bo->mmu_mapped) {
+ drm_WARN_ON(&vdev->drm, !bo->ctx);
+ ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt,
+ ivpu_bo_is_snooped(bo), ivpu_bo_is_read_only(bo));
+ if (ret) {
+ ivpu_err(vdev, "Failed to map BO in MMU: %d\n", ret);
+ goto unlock;
+ }
+ bo->mmu_mapped = true;
+ }
+
+unlock:
+ ivpu_bo_unlock(bo);
+
+ return ret;
+}
+
+static int
+ivpu_bo_alloc_vpu_addr(struct ivpu_bo *bo, struct ivpu_mmu_context *ctx,
+ const struct ivpu_addr_range *range)
+{
+ struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
+ int idx, ret;
+
+ if (!drm_dev_enter(&vdev->drm, &idx))
+ return -ENODEV;
+
+ ivpu_bo_lock(bo);
+
+ ret = ivpu_mmu_context_insert_node(ctx, range, ivpu_bo_size(bo), &bo->mm_node);
+ if (!ret) {
+ bo->ctx = ctx;
+ bo->ctx_id = ctx->id;
+ bo->vpu_addr = bo->mm_node.start;
+ ivpu_dbg_bo(vdev, bo, "vaddr");
+ }
+
+ ivpu_bo_unlock(bo);
+
+ drm_dev_exit(idx);
+
+ return ret;
+}
+
+static void ivpu_bo_unbind_locked(struct ivpu_bo *bo)
+{
+ struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
+
+ dma_resv_assert_held(bo->base.base.resv);
+
+ if (bo->mmu_mapped) {
+ drm_WARN_ON(&vdev->drm, !bo->ctx);
+ drm_WARN_ON(&vdev->drm, !bo->vpu_addr);
+ drm_WARN_ON(&vdev->drm, !bo->base.sgt);
+ ivpu_mmu_context_unmap_sgt(vdev, bo->ctx, bo->vpu_addr, bo->base.sgt);
+ bo->mmu_mapped = false;
+ }
+
+ if (bo->ctx) {
+ ivpu_mmu_context_remove_node(bo->ctx, &bo->mm_node);
+ bo->ctx = NULL;
+ }
+
+ if (bo->base.sgt) {
+ if (bo->base.base.import_attach) {
+ dma_buf_unmap_attachment(bo->base.base.import_attach,
+ bo->base.sgt, DMA_BIDIRECTIONAL);
+ } else {
+ dma_unmap_sgtable(vdev->drm.dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(bo->base.sgt);
+ kfree(bo->base.sgt);
+ }
+ bo->base.sgt = NULL;
+ }
+}
+
+void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
+{
+ struct ivpu_bo *bo;
+
+ if (drm_WARN_ON(&vdev->drm, !ctx))
+ return;
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_for_each_entry(bo, &vdev->bo_list, bo_list_node) {
+ ivpu_bo_lock(bo);
+ if (bo->ctx == ctx) {
+ ivpu_dbg_bo(vdev, bo, "unbind");
+ ivpu_bo_unbind_locked(bo);
+ }
+ ivpu_bo_unlock(bo);
+ }
+ mutex_unlock(&vdev->bo_list_lock);
+}
+
+struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size)
+{
+ struct ivpu_bo *bo;
+
+ if (size == 0 || !PAGE_ALIGNED(size))
+ return ERR_PTR(-EINVAL);
+
+ bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+ if (!bo)
+ return ERR_PTR(-ENOMEM);
+
+ bo->base.base.funcs = &ivpu_gem_funcs;
+ bo->base.pages_mark_dirty_on_put = true; /* VPU can dirty a BO anytime */
+
+ INIT_LIST_HEAD(&bo->bo_list_node);
+
+ return &bo->base.base;
+}
+
+struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct ivpu_device *vdev = to_ivpu_device(dev);
+ struct device *attach_dev = dev->dev;
+ struct dma_buf_attachment *attach;
+ struct drm_gem_object *obj;
+ struct ivpu_bo *bo;
+ int ret;
+
+ attach = dma_buf_attach(dma_buf, attach_dev);
+ if (IS_ERR(attach))
+ return ERR_CAST(attach);
+
+ get_dma_buf(dma_buf);
+
+ obj = drm_gem_shmem_prime_import_sg_table(dev, attach, NULL);
+ if (IS_ERR(obj)) {
+ ret = PTR_ERR(obj);
+ goto fail_detach;
+ }
+
+ obj->import_attach = attach;
+ obj->resv = dma_buf->resv;
+
+ bo = to_ivpu_bo(obj);
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_add_tail(&bo->bo_list_node, &vdev->bo_list);
+ mutex_unlock(&vdev->bo_list_lock);
+
+ ivpu_dbg(vdev, BO, "import: bo %8p size %9zu\n", bo, ivpu_bo_size(bo));
+
+ return obj;
+
+fail_detach:
+ dma_buf_detach(dma_buf, attach);
+ dma_buf_put(dma_buf);
+
+ return ERR_PTR(ret);
+}
+
+static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags)
+{
+ struct drm_gem_shmem_object *shmem;
+ struct ivpu_bo *bo;
+
+ switch (flags & DRM_IVPU_BO_CACHE_MASK) {
+ case DRM_IVPU_BO_CACHED:
+ case DRM_IVPU_BO_WC:
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ shmem = drm_gem_shmem_create(&vdev->drm, size);
+ if (IS_ERR(shmem))
+ return ERR_CAST(shmem);
+
+ bo = to_ivpu_bo(&shmem->base);
+ bo->base.map_wc = flags & DRM_IVPU_BO_WC;
+ bo->flags = flags;
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_add_tail(&bo->bo_list_node, &vdev->bo_list);
+ mutex_unlock(&vdev->bo_list_lock);
+
+ ivpu_dbg(vdev, BO, " alloc: bo %8p size %9llu\n", bo, size);
+
+ return bo;
+}
+
+static int ivpu_gem_bo_open(struct drm_gem_object *obj, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_bo *bo = to_ivpu_bo(obj);
+ struct ivpu_addr_range *range;
+
+ if (bo->ctx) {
+ ivpu_dbg(vdev, IOCTL, "Can't add BO %pe to ctx %u: already in ctx %u\n",
+ bo, file_priv->ctx.id, bo->ctx->id);
+ return -EALREADY;
+ }
+
+ if (bo->flags & DRM_IVPU_BO_SHAVE_MEM)
+ range = &vdev->hw->ranges.shave;
+ else if (bo->flags & DRM_IVPU_BO_DMA_MEM)
+ range = &vdev->hw->ranges.dma;
+ else
+ range = &vdev->hw->ranges.user;
+
+ return ivpu_bo_alloc_vpu_addr(bo, &file_priv->ctx, range);
+}
+
+static void ivpu_gem_bo_free(struct drm_gem_object *obj)
+{
+ struct ivpu_device *vdev = to_ivpu_device(obj->dev);
+ struct ivpu_bo *bo = to_ivpu_bo(obj);
+
+ ivpu_dbg_bo(vdev, bo, "free");
+
+ drm_WARN_ON(&vdev->drm, list_empty(&bo->bo_list_node));
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_del(&bo->bo_list_node);
+
+ drm_WARN_ON(&vdev->drm, !drm_gem_is_imported(&bo->base.base) &&
+ !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ));
+ drm_WARN_ON(&vdev->drm, ivpu_bo_size(bo) == 0);
+ drm_WARN_ON(&vdev->drm, bo->base.vaddr);
+
+ ivpu_bo_lock(bo);
+ ivpu_bo_unbind_locked(bo);
+ ivpu_bo_unlock(bo);
+
+ mutex_unlock(&vdev->bo_list_lock);
+
+ drm_WARN_ON(&vdev->drm, bo->mmu_mapped);
+ drm_WARN_ON(&vdev->drm, bo->ctx);
+
+ drm_WARN_ON(obj->dev, refcount_read(&bo->base.pages_use_count) > 1);
+ drm_WARN_ON(obj->dev, bo->base.base.vma_node.vm_files.rb_node);
+ drm_gem_shmem_free(&bo->base);
+}
+
+static enum drm_gem_object_status ivpu_gem_status(struct drm_gem_object *obj)
+{
+ struct ivpu_bo *bo = to_ivpu_bo(obj);
+ enum drm_gem_object_status status = 0;
+
+ if (ivpu_bo_is_resident(bo))
+ status |= DRM_GEM_OBJECT_RESIDENT;
+
+ return status;
+}
+
+static const struct drm_gem_object_funcs ivpu_gem_funcs = {
+ .free = ivpu_gem_bo_free,
+ .open = ivpu_gem_bo_open,
+ .print_info = drm_gem_shmem_object_print_info,
+ .pin = drm_gem_shmem_object_pin,
+ .unpin = drm_gem_shmem_object_unpin,
+ .get_sg_table = drm_gem_shmem_object_get_sg_table,
+ .vmap = drm_gem_shmem_object_vmap,
+ .vunmap = drm_gem_shmem_object_vunmap,
+ .mmap = drm_gem_shmem_object_mmap,
+ .status = ivpu_gem_status,
+ .vm_ops = &drm_gem_shmem_vm_ops,
+};
+
+int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct drm_ivpu_bo_create *args = data;
+ u64 size = PAGE_ALIGN(args->size);
+ struct ivpu_bo *bo;
+ int ret;
+
+ if (args->flags & ~DRM_IVPU_BO_FLAGS) {
+ ivpu_dbg(vdev, IOCTL, "Invalid BO flags 0x%x\n", args->flags);
+ return -EINVAL;
+ }
+
+ if (size == 0) {
+ ivpu_dbg(vdev, IOCTL, "Invalid BO size %llu\n", args->size);
+ return -EINVAL;
+ }
+
+ bo = ivpu_bo_alloc(vdev, size, args->flags);
+ if (IS_ERR(bo)) {
+ ivpu_dbg(vdev, IOCTL, "Failed to allocate BO: %pe ctx %u size %llu flags 0x%x\n",
+ bo, file_priv->ctx.id, args->size, args->flags);
+ return PTR_ERR(bo);
+ }
+
+ drm_WARN_ON(&vdev->drm, bo->base.base.handle_count != 0);
+
+ ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
+ if (ret) {
+ ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",
+ bo, file_priv->ctx.id, args->size, args->flags);
+ } else {
+ args->vpu_addr = bo->vpu_addr;
+ drm_WARN_ON(&vdev->drm, bo->base.base.handle_count != 1);
+ }
+
+ drm_gem_object_put(&bo->base.base);
+
+ return ret;
+}
+
+struct ivpu_bo *
+ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ struct ivpu_addr_range *range, u64 size, u32 flags)
+{
+ struct iosys_map map;
+ struct ivpu_bo *bo;
+ int ret;
+
+ if (drm_WARN_ON(&vdev->drm, !range))
+ return NULL;
+
+ drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(range->start));
+ drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(range->end));
+ drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(size));
+
+ bo = ivpu_bo_alloc(vdev, size, flags);
+ if (IS_ERR(bo)) {
+ ivpu_err(vdev, "Failed to allocate BO: %pe vpu_addr 0x%llx size %llu flags 0x%x\n",
+ bo, range->start, size, flags);
+ return NULL;
+ }
+
+ ret = ivpu_bo_alloc_vpu_addr(bo, ctx, range);
+ if (ret) {
+ ivpu_err(vdev, "Failed to allocate NPU address for BO: %pe ctx %u size %llu: %d\n",
+ bo, ctx->id, size, ret);
+ goto err_put;
+ }
+
+ ret = ivpu_bo_bind(bo);
+ if (ret)
+ goto err_put;
+
+ if (flags & DRM_IVPU_BO_MAPPABLE) {
+ ivpu_bo_lock(bo);
+ ret = drm_gem_shmem_vmap_locked(&bo->base, &map);
+ ivpu_bo_unlock(bo);
+
+ if (ret)
+ goto err_put;
+ }
+
+ return bo;
+
+err_put:
+ drm_gem_object_put(&bo->base.base);
+ return NULL;
+}
+
+struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags)
+{
+ struct ivpu_addr_range range;
+
+ if (!ivpu_is_within_range(addr, size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid runtime BO address 0x%llx size %llu\n", addr, size);
+ return NULL;
+ }
+
+ if (ivpu_hw_range_init(vdev, &range, addr, size))
+ return NULL;
+
+ return ivpu_bo_create(vdev, &vdev->gctx, &range, size, flags);
+}
+
+struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags)
+{
+ return ivpu_bo_create(vdev, &vdev->gctx, &vdev->hw->ranges.global, size, flags);
+}
+
+void ivpu_bo_free(struct ivpu_bo *bo)
+{
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(bo->base.vaddr);
+
+ if (bo->flags & DRM_IVPU_BO_MAPPABLE) {
+ ivpu_bo_lock(bo);
+ drm_gem_shmem_vunmap_locked(&bo->base, &map);
+ ivpu_bo_unlock(bo);
+ }
+
+ drm_gem_object_put(&bo->base.base);
+}
+
+int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_bo_info *args = data;
+ struct drm_gem_object *obj;
+ struct ivpu_bo *bo;
+ int ret = 0;
+
+ obj = drm_gem_object_lookup(file, args->handle);
+ if (!obj)
+ return -ENOENT;
+
+ bo = to_ivpu_bo(obj);
+
+ ivpu_bo_lock(bo);
+ args->flags = bo->flags;
+ args->mmap_offset = drm_vma_node_offset_addr(&obj->vma_node);
+ args->vpu_addr = bo->vpu_addr;
+ args->size = obj->size;
+ ivpu_bo_unlock(bo);
+
+ drm_gem_object_put(obj);
+ return ret;
+}
+
+int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_bo_wait *args = data;
+ struct drm_gem_object *obj;
+ unsigned long timeout;
+ long ret;
+
+ timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
+
+ /* Add 1 jiffy to ensure the wait function never times out before intended timeout_ns */
+ timeout += 1;
+
+ obj = drm_gem_object_lookup(file, args->handle);
+ if (!obj)
+ return -EINVAL;
+
+ ret = dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_READ, true, timeout);
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ } else if (ret > 0) {
+ ret = 0;
+ args->job_status = to_ivpu_bo(obj)->job_status;
+ }
+
+ drm_gem_object_put(obj);
+
+ return ret;
+}
+
+static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p)
+{
+ ivpu_bo_lock(bo);
+
+ drm_printf(p, "%-9p %-3u 0x%-12llx %-10lu 0x%-8x %-4u",
+ bo, bo->ctx_id, bo->vpu_addr, bo->base.base.size,
+ bo->flags, kref_read(&bo->base.base.refcount));
+
+ if (bo->base.pages)
+ drm_printf(p, " has_pages");
+
+ if (bo->mmu_mapped)
+ drm_printf(p, " mmu_mapped");
+
+ if (drm_gem_is_imported(&bo->base.base))
+ drm_printf(p, " imported");
+
+ drm_printf(p, "\n");
+
+ ivpu_bo_unlock(bo);
+}
+
+void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p)
+{
+ struct ivpu_device *vdev = to_ivpu_device(dev);
+ struct ivpu_bo *bo;
+
+ drm_printf(p, "%-9s %-3s %-14s %-10s %-10s %-4s %s\n",
+ "bo", "ctx", "vpu_addr", "size", "flags", "refs", "attribs");
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_for_each_entry(bo, &vdev->bo_list, bo_list_node)
+ ivpu_bo_print_info(bo, p);
+ mutex_unlock(&vdev->bo_list_lock);
+}
+
+void ivpu_bo_list_print(struct drm_device *dev)
+{
+ struct drm_printer p = drm_info_printer(dev->dev);
+
+ ivpu_bo_list(dev, &p);
+}
diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h
new file mode 100644
index 000000000000..0c3350f22b55
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_gem.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+#ifndef __IVPU_GEM_H__
+#define __IVPU_GEM_H__
+
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_mm.h>
+
+struct ivpu_file_priv;
+
+struct ivpu_bo {
+ struct drm_gem_shmem_object base;
+ struct ivpu_mmu_context *ctx;
+ struct list_head bo_list_node;
+ struct drm_mm_node mm_node;
+
+ u64 vpu_addr;
+ u32 flags;
+ u32 job_status; /* Valid only for command buffer */
+ u32 ctx_id;
+ bool mmu_mapped;
+};
+
+int ivpu_bo_bind(struct ivpu_bo *bo);
+void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
+
+struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size);
+struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf);
+struct ivpu_bo *ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ struct ivpu_addr_range *range, u64 size, u32 flags);
+struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags);
+struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags);
+void ivpu_bo_free(struct ivpu_bo *bo);
+
+int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
+
+void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p);
+void ivpu_bo_list_print(struct drm_device *dev);
+
+static inline struct ivpu_bo *to_ivpu_bo(struct drm_gem_object *obj)
+{
+ return container_of(obj, struct ivpu_bo, base.base);
+}
+
+static inline void *ivpu_bo_vaddr(struct ivpu_bo *bo)
+{
+ return bo->base.vaddr;
+}
+
+static inline size_t ivpu_bo_size(struct ivpu_bo *bo)
+{
+ return bo->base.base.size;
+}
+
+static inline u32 ivpu_bo_cache_mode(struct ivpu_bo *bo)
+{
+ return bo->flags & DRM_IVPU_BO_CACHE_MASK;
+}
+
+static inline struct ivpu_device *ivpu_bo_to_vdev(struct ivpu_bo *bo)
+{
+ return to_ivpu_device(bo->base.base.dev);
+}
+
+static inline bool ivpu_bo_is_snooped(struct ivpu_bo *bo)
+{
+ if (ivpu_is_force_snoop_enabled(ivpu_bo_to_vdev(bo)))
+ return true;
+
+ return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED;
+}
+
+static inline bool ivpu_bo_is_read_only(struct ivpu_bo *bo)
+{
+ return bo->flags & DRM_IVPU_BO_READ_ONLY;
+}
+
+static inline bool ivpu_bo_is_resident(struct ivpu_bo *bo)
+{
+ return !!bo->base.pages;
+}
+
+static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr)
+{
+ if (vpu_addr < bo->vpu_addr)
+ return NULL;
+
+ if (vpu_addr >= (bo->vpu_addr + ivpu_bo_size(bo)))
+ return NULL;
+
+ return ivpu_bo_vaddr(bo) + (vpu_addr - bo->vpu_addr);
+}
+
+static inline u32 cpu_to_vpu_addr(struct ivpu_bo *bo, void *cpu_addr)
+{
+ if (cpu_addr < ivpu_bo_vaddr(bo))
+ return 0;
+
+ if (cpu_addr >= (ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)))
+ return 0;
+
+ return bo->vpu_addr + (cpu_addr - ivpu_bo_vaddr(bo));
+}
+
+static inline bool ivpu_bo_is_mappable(struct ivpu_bo *bo)
+{
+ return bo->flags & DRM_IVPU_BO_MAPPABLE;
+}
+
+#endif /* __IVPU_GEM_H__ */
diff --git a/drivers/accel/ivpu/ivpu_gem_userptr.c b/drivers/accel/ivpu/ivpu_gem_userptr.c
new file mode 100644
index 000000000000..25ba606164c0
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_gem_userptr.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/err.h>
+#include <linux/highmem.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_gem.h"
+
+static struct sg_table *
+ivpu_gem_userptr_dmabuf_map(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct sg_table *sgt = attachment->dmabuf->priv;
+ int ret;
+
+ ret = dma_map_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return sgt;
+}
+
+static void ivpu_gem_userptr_dmabuf_unmap(struct dma_buf_attachment *attachment,
+ struct sg_table *sgt,
+ enum dma_data_direction direction)
+{
+ dma_unmap_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void ivpu_gem_userptr_dmabuf_release(struct dma_buf *dma_buf)
+{
+ struct sg_table *sgt = dma_buf->priv;
+ struct sg_page_iter page_iter;
+ struct page *page;
+
+ for_each_sgtable_page(sgt, &page_iter, 0) {
+ page = sg_page_iter_page(&page_iter);
+ unpin_user_page(page);
+ }
+
+ sg_free_table(sgt);
+ kfree(sgt);
+}
+
+static const struct dma_buf_ops ivpu_gem_userptr_dmabuf_ops = {
+ .map_dma_buf = ivpu_gem_userptr_dmabuf_map,
+ .unmap_dma_buf = ivpu_gem_userptr_dmabuf_unmap,
+ .release = ivpu_gem_userptr_dmabuf_release,
+};
+
+static struct dma_buf *
+ivpu_create_userptr_dmabuf(struct ivpu_device *vdev, void __user *user_ptr,
+ size_t size, uint32_t flags)
+{
+ struct dma_buf_export_info exp_info = {};
+ struct dma_buf *dma_buf;
+ struct sg_table *sgt;
+ struct page **pages;
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+ unsigned int gup_flags = FOLL_LONGTERM;
+ int ret, i, pinned;
+
+ /* Add FOLL_WRITE only if the BO is not read-only */
+ if (!(flags & DRM_IVPU_BO_READ_ONLY))
+ gup_flags |= FOLL_WRITE;
+
+ pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
+
+ pinned = pin_user_pages_fast((unsigned long)user_ptr, nr_pages, gup_flags, pages);
+ if (pinned < 0) {
+ ret = pinned;
+ ivpu_dbg(vdev, IOCTL, "Failed to pin user pages: %d\n", ret);
+ goto free_pages_array;
+ }
+
+ if (pinned != nr_pages) {
+ ivpu_dbg(vdev, IOCTL, "Pinned %d pages, expected %lu\n", pinned, nr_pages);
+ ret = -EFAULT;
+ goto unpin_pages;
+ }
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ ret = -ENOMEM;
+ goto unpin_pages;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, pages, nr_pages, 0, size, GFP_KERNEL);
+ if (ret) {
+ ivpu_dbg(vdev, IOCTL, "Failed to create sg table: %d\n", ret);
+ goto free_sgt;
+ }
+
+ exp_info.exp_name = "ivpu_userptr_dmabuf";
+ exp_info.owner = THIS_MODULE;
+ exp_info.ops = &ivpu_gem_userptr_dmabuf_ops;
+ exp_info.size = size;
+ exp_info.flags = O_RDWR | O_CLOEXEC;
+ exp_info.priv = sgt;
+
+ dma_buf = dma_buf_export(&exp_info);
+ if (IS_ERR(dma_buf)) {
+ ret = PTR_ERR(dma_buf);
+ ivpu_dbg(vdev, IOCTL, "Failed to export userptr dma-buf: %d\n", ret);
+ goto free_sg_table;
+ }
+
+ kvfree(pages);
+ return dma_buf;
+
+free_sg_table:
+ sg_free_table(sgt);
+free_sgt:
+ kfree(sgt);
+unpin_pages:
+ for (i = 0; i < pinned; i++)
+ unpin_user_page(pages[i]);
+free_pages_array:
+ kvfree(pages);
+ return ERR_PTR(ret);
+}
+
+static struct ivpu_bo *
+ivpu_bo_create_from_userptr(struct ivpu_device *vdev, void __user *user_ptr,
+ size_t size, uint32_t flags)
+{
+ struct dma_buf *dma_buf;
+ struct drm_gem_object *obj;
+ struct ivpu_bo *bo;
+
+ dma_buf = ivpu_create_userptr_dmabuf(vdev, user_ptr, size, flags);
+ if (IS_ERR(dma_buf))
+ return ERR_CAST(dma_buf);
+
+ obj = ivpu_gem_prime_import(&vdev->drm, dma_buf);
+ if (IS_ERR(obj)) {
+ dma_buf_put(dma_buf);
+ return ERR_CAST(obj);
+ }
+
+ dma_buf_put(dma_buf);
+
+ bo = to_ivpu_bo(obj);
+ bo->flags = flags;
+
+ return bo;
+}
+
+int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_bo_create_from_userptr *args = data;
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = to_ivpu_device(dev);
+ void __user *user_ptr = u64_to_user_ptr(args->user_ptr);
+ struct ivpu_bo *bo;
+ int ret;
+
+ if (args->flags & ~(DRM_IVPU_BO_HIGH_MEM | DRM_IVPU_BO_DMA_MEM | DRM_IVPU_BO_READ_ONLY)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid BO flags: 0x%x\n", args->flags);
+ return -EINVAL;
+ }
+
+ if (!args->user_ptr || !args->size) {
+ ivpu_dbg(vdev, IOCTL, "Userptr or size are zero: ptr %llx size %llu\n",
+ args->user_ptr, args->size);
+ return -EINVAL;
+ }
+
+ if (!PAGE_ALIGNED(args->user_ptr) || !PAGE_ALIGNED(args->size)) {
+ ivpu_dbg(vdev, IOCTL, "Userptr or size not page aligned: ptr %llx size %llu\n",
+ args->user_ptr, args->size);
+ return -EINVAL;
+ }
+
+ if (!access_ok(user_ptr, args->size)) {
+ ivpu_dbg(vdev, IOCTL, "Userptr is not accessible: ptr %llx size %llu\n",
+ args->user_ptr, args->size);
+ return -EFAULT;
+ }
+
+ bo = ivpu_bo_create_from_userptr(vdev, user_ptr, args->size, args->flags);
+ if (IS_ERR(bo))
+ return PTR_ERR(bo);
+
+ ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
+ if (ret) {
+ ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",
+ bo, file_priv->ctx.id, args->size, args->flags);
+ } else {
+ ivpu_dbg(vdev, BO, "Created userptr BO: handle=%u vpu_addr=0x%llx size=%llu flags=0x%x\n",
+ args->handle, bo->vpu_addr, args->size, bo->flags);
+ args->vpu_addr = bo->vpu_addr;
+ }
+
+ drm_gem_object_put(&bo->base.base);
+
+ return ret;
+}
diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c
new file mode 100644
index 000000000000..d69cd0d93569
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 - 2024 Intel Corporation
+ */
+
+#include "ivpu_drv.h"
+#include "ivpu_hw.h"
+#include "ivpu_hw_btrs.h"
+#include "ivpu_hw_ip.h"
+
+#include <asm/msr-index.h>
+#include <asm/msr.h>
+#include <linux/dmi.h>
+#include <linux/fault-inject.h>
+#include <linux/pm_runtime.h>
+
+#ifdef CONFIG_FAULT_INJECTION
+DECLARE_FAULT_ATTR(ivpu_hw_failure);
+
+static char *ivpu_fail_hw;
+module_param_named_unsafe(fail_hw, ivpu_fail_hw, charp, 0444);
+MODULE_PARM_DESC(fail_hw, "<interval>,<probability>,<space>,<times>");
+#endif
+
+#define FW_SHARED_MEM_ALIGNMENT SZ_512K /* VPU MTRR limitation */
+
+#define ECC_MCA_SIGNAL_ENABLE_MASK 0xff
+
+static char *platform_to_str(u32 platform)
+{
+ switch (platform) {
+ case IVPU_PLATFORM_SILICON:
+ return "SILICON";
+ case IVPU_PLATFORM_SIMICS:
+ return "SIMICS";
+ case IVPU_PLATFORM_FPGA:
+ return "FPGA";
+ case IVPU_PLATFORM_HSLE:
+ return "HSLE";
+ default:
+ return "Invalid platform";
+ }
+}
+
+static void platform_init(struct ivpu_device *vdev)
+{
+ int platform = ivpu_hw_btrs_platform_read(vdev);
+
+ ivpu_dbg(vdev, MISC, "Platform type: %s (%d)\n", platform_to_str(platform), platform);
+
+ switch (platform) {
+ case IVPU_PLATFORM_SILICON:
+ case IVPU_PLATFORM_SIMICS:
+ case IVPU_PLATFORM_FPGA:
+ case IVPU_PLATFORM_HSLE:
+ vdev->platform = platform;
+ break;
+
+ default:
+ ivpu_err(vdev, "Invalid platform type: %d\n", platform);
+ break;
+ }
+}
+
+static void wa_init(struct ivpu_device *vdev)
+{
+ vdev->wa.punit_disabled = false;
+ vdev->wa.clear_runtime_mem = false;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ vdev->wa.interrupt_clear_with_0 = ivpu_hw_btrs_irqs_clear_with_0_mtl(vdev);
+
+ if (ivpu_device_id(vdev) == PCI_DEVICE_ID_LNL &&
+ ivpu_revision(vdev) < IVPU_HW_IP_REV_LNL_B0)
+ vdev->wa.disable_clock_relinquish = true;
+
+ if (ivpu_test_mode & IVPU_TEST_MODE_CLK_RELINQ_ENABLE)
+ vdev->wa.disable_clock_relinquish = false;
+
+ if (ivpu_test_mode & IVPU_TEST_MODE_CLK_RELINQ_DISABLE)
+ vdev->wa.disable_clock_relinquish = true;
+
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ vdev->wa.wp0_during_power_up = true;
+
+ if (ivpu_test_mode & IVPU_TEST_MODE_D0I2_DISABLE)
+ vdev->wa.disable_d0i2 = true;
+
+ IVPU_PRINT_WA(punit_disabled);
+ IVPU_PRINT_WA(clear_runtime_mem);
+ IVPU_PRINT_WA(interrupt_clear_with_0);
+ IVPU_PRINT_WA(disable_clock_relinquish);
+ IVPU_PRINT_WA(wp0_during_power_up);
+ IVPU_PRINT_WA(disable_d0i2);
+}
+
+static void timeouts_init(struct ivpu_device *vdev)
+{
+ if (ivpu_test_mode & IVPU_TEST_MODE_DISABLE_TIMEOUTS) {
+ vdev->timeout.boot = -1;
+ vdev->timeout.jsm = -1;
+ vdev->timeout.tdr = -1;
+ vdev->timeout.inference = -1;
+ vdev->timeout.autosuspend = -1;
+ vdev->timeout.d0i3_entry_msg = -1;
+ } else if (ivpu_is_fpga(vdev)) {
+ vdev->timeout.boot = 50;
+ vdev->timeout.jsm = 15000;
+ vdev->timeout.tdr = 30000;
+ vdev->timeout.inference = 900000;
+ vdev->timeout.autosuspend = -1;
+ vdev->timeout.d0i3_entry_msg = 500;
+ vdev->timeout.state_dump_msg = 10000;
+ } else if (ivpu_is_simics(vdev)) {
+ vdev->timeout.boot = 50;
+ vdev->timeout.jsm = 500;
+ vdev->timeout.tdr = 10000;
+ vdev->timeout.inference = 300000;
+ vdev->timeout.autosuspend = 100;
+ vdev->timeout.d0i3_entry_msg = 100;
+ vdev->timeout.state_dump_msg = 10;
+ } else {
+ vdev->timeout.boot = 1000;
+ vdev->timeout.jsm = 500;
+ vdev->timeout.tdr = 2000;
+ vdev->timeout.inference = 60000;
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ vdev->timeout.autosuspend = 10;
+ else
+ vdev->timeout.autosuspend = 100;
+ vdev->timeout.d0i3_entry_msg = 5;
+ vdev->timeout.state_dump_msg = 100;
+ }
+}
+
+static void priority_bands_init(struct ivpu_device *vdev)
+{
+ /* Idle */
+ vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE] = 0;
+ vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE] = 50000;
+ vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE] = 160000;
+ /* Normal */
+ vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL] = 50000;
+ vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL] = 50000;
+ vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL] = 300000;
+ /* Focus */
+ vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS] = 50000;
+ vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS] = 50000;
+ vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS] = 200000;
+ /* Realtime */
+ vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 0;
+ vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 50000;
+ vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 200000;
+}
+
+int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start, u64 size)
+{
+ u64 end;
+
+ if (!range || check_add_overflow(start, size, &end)) {
+ ivpu_err(vdev, "Invalid range: start 0x%llx size %llu\n", start, size);
+ return -EINVAL;
+ }
+
+ range->start = start;
+ range->end = end;
+
+ return 0;
+}
+
+static void memory_ranges_init(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x84800000, SZ_64M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0xa0000000, 511 * SZ_1M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x180000000, SZ_2G);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.dma, 0x200000000, SZ_128G);
+ } else {
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x80000000, SZ_64M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x80000000, SZ_2G);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0x100000000, SZ_256G);
+ vdev->hw->ranges.dma = vdev->hw->ranges.user;
+ }
+
+ drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vdev->hw->ranges.global.start,
+ FW_SHARED_MEM_ALIGNMENT));
+}
+
+static int wp_enable(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_wp_drive(vdev, true);
+}
+
+static int wp_disable(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_wp_drive(vdev, false);
+}
+
+int ivpu_hw_power_up(struct ivpu_device *vdev)
+{
+ int ret;
+
+ if (IVPU_WA(wp0_during_power_up)) {
+ /* WP requests may fail when powering down, so issue WP 0 here */
+ ret = wp_disable(vdev);
+ if (ret)
+ ivpu_warn(vdev, "Failed to disable workpoint: %d\n", ret);
+ }
+
+ ret = ivpu_hw_btrs_d0i3_disable(vdev);
+ if (ret)
+ ivpu_warn(vdev, "Failed to disable D0I3: %d\n", ret);
+
+ ret = wp_enable(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to enable workpoint: %d\n", ret);
+ return ret;
+ }
+
+ if (ivpu_hw_btrs_gen(vdev) >= IVPU_HW_BTRS_LNL) {
+ if (IVPU_WA(disable_clock_relinquish))
+ ivpu_hw_btrs_clock_relinquish_disable_lnl(vdev);
+ ivpu_hw_btrs_profiling_freq_reg_set_lnl(vdev);
+ ivpu_hw_btrs_ats_print_lnl(vdev);
+ }
+
+ ret = ivpu_hw_ip_host_ss_configure(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to configure host SS: %d\n", ret);
+ return ret;
+ }
+
+ ivpu_hw_ip_idle_gen_disable(vdev);
+
+ ret = ivpu_hw_btrs_wait_for_clock_res_own_ack(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for clock resource own ACK\n");
+ return ret;
+ }
+
+ ret = ivpu_hw_ip_pwr_domain_enable(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to enable power domain: %d\n", ret);
+ return ret;
+ }
+
+ ret = ivpu_hw_ip_host_ss_axi_enable(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to enable AXI: %d\n", ret);
+ return ret;
+ }
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_LNL)
+ ivpu_hw_btrs_set_port_arbitration_weights_lnl(vdev);
+
+ ret = ivpu_hw_ip_top_noc_enable(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to enable TOP NOC: %d\n", ret);
+
+ return ret;
+}
+
+static void save_d0i3_entry_timestamp(struct ivpu_device *vdev)
+{
+ vdev->hw->d0i3_entry_host_ts = ktime_get_boottime();
+ vdev->hw->d0i3_entry_vpu_ts = ivpu_hw_ip_read_perf_timer_counter(vdev);
+}
+
+int ivpu_hw_reset(struct ivpu_device *vdev)
+{
+ int ret = 0;
+
+ if (ivpu_hw_btrs_ip_reset(vdev)) {
+ ivpu_err(vdev, "Failed to reset NPU IP\n");
+ ret = -EIO;
+ }
+
+ if (wp_disable(vdev)) {
+ ivpu_err(vdev, "Failed to disable workpoint\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+int ivpu_hw_power_down(struct ivpu_device *vdev)
+{
+ int ret = 0;
+
+ save_d0i3_entry_timestamp(vdev);
+
+ if (!ivpu_hw_is_idle(vdev))
+ ivpu_warn(vdev, "NPU not idle during power down\n");
+
+ if (ivpu_hw_reset(vdev)) {
+ ivpu_err(vdev, "Failed to reset NPU\n");
+ ret = -EIO;
+ }
+
+ if (ivpu_hw_btrs_d0i3_enable(vdev)) {
+ ivpu_err(vdev, "Failed to enter D0I3\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+int ivpu_hw_init(struct ivpu_device *vdev)
+{
+ ivpu_hw_btrs_info_init(vdev);
+ ivpu_hw_btrs_freq_ratios_init(vdev);
+ priority_bands_init(vdev);
+ memory_ranges_init(vdev);
+ platform_init(vdev);
+ wa_init(vdev);
+ timeouts_init(vdev);
+ atomic_set(&vdev->hw->firewall_irq_counter, 0);
+
+#ifdef CONFIG_FAULT_INJECTION
+ if (ivpu_fail_hw)
+ setup_fault_attr(&ivpu_hw_failure, ivpu_fail_hw);
+#endif
+
+ return 0;
+}
+
+int ivpu_hw_boot_fw(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ivpu_hw_ip_snoop_disable(vdev);
+ ivpu_hw_ip_tbu_mmu_enable(vdev);
+ ret = ivpu_hw_ip_soc_cpu_boot(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to boot SOC CPU: %d\n", ret);
+
+ return ret;
+}
+
+void ivpu_hw_profiling_freq_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ vdev->hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT;
+ return;
+ }
+
+ if (enable)
+ vdev->hw->pll.profiling_freq = PLL_PROFILING_FREQ_HIGH;
+ else
+ vdev->hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT;
+}
+
+void ivpu_irq_handlers_init(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_37xx;
+ else
+ vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_40xx;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ vdev->hw->irq.btrs_irq_handler = ivpu_hw_btrs_irq_handler_mtl;
+ else
+ vdev->hw->irq.btrs_irq_handler = ivpu_hw_btrs_irq_handler_lnl;
+}
+
+void ivpu_hw_irq_enable(struct ivpu_device *vdev)
+{
+ ivpu_hw_ip_irq_enable(vdev);
+ ivpu_hw_btrs_irq_enable(vdev);
+}
+
+void ivpu_hw_irq_disable(struct ivpu_device *vdev)
+{
+ ivpu_hw_btrs_irq_disable(vdev);
+ ivpu_hw_ip_irq_disable(vdev);
+}
+
+irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
+{
+ struct ivpu_device *vdev = ptr;
+ bool ip_handled, btrs_handled;
+
+ ivpu_hw_btrs_global_int_disable(vdev);
+
+ btrs_handled = ivpu_hw_btrs_irq_handler(vdev, irq);
+ if (!ivpu_hw_is_idle((vdev)) || !btrs_handled)
+ ip_handled = ivpu_hw_ip_irq_handler(vdev, irq);
+ else
+ ip_handled = false;
+
+ /* Re-enable global interrupts to re-trigger MSI for pending interrupts */
+ ivpu_hw_btrs_global_int_enable(vdev);
+
+ if (!ip_handled && !btrs_handled)
+ return IRQ_NONE;
+
+ pm_runtime_mark_last_busy(vdev->drm.dev);
+ return IRQ_HANDLED;
+}
+
+bool ivpu_hw_uses_ecc_mca_signal(struct ivpu_device *vdev)
+{
+ unsigned long long msr_integrity_caps;
+ int ret;
+
+ if (ivpu_hw_ip_gen(vdev) < IVPU_HW_IP_50XX)
+ return false;
+
+ ret = rdmsrq_safe(MSR_INTEGRITY_CAPS, &msr_integrity_caps);
+ if (ret) {
+ ivpu_warn(vdev, "Error reading MSR_INTEGRITY_CAPS: %d", ret);
+ return false;
+ }
+
+ ivpu_dbg(vdev, MISC, "MSR_INTEGRITY_CAPS: 0x%llx\n", msr_integrity_caps);
+
+ return msr_integrity_caps & ECC_MCA_SIGNAL_ENABLE_MASK;
+}
diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h
new file mode 100644
index 000000000000..b6d0f0d0dccc
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_H__
+#define __IVPU_HW_H__
+
+#include "ivpu_drv.h"
+#include "ivpu_hw_btrs.h"
+#include "ivpu_hw_ip.h"
+
+struct ivpu_addr_range {
+ resource_size_t start;
+ resource_size_t end;
+};
+
+struct ivpu_hw_info {
+ struct {
+ bool (*btrs_irq_handler)(struct ivpu_device *vdev, int irq);
+ bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq);
+ } irq;
+ struct {
+ struct ivpu_addr_range runtime;
+ struct ivpu_addr_range global;
+ struct ivpu_addr_range user;
+ struct ivpu_addr_range shave;
+ struct ivpu_addr_range dma;
+ } ranges;
+ struct {
+ u8 min_ratio;
+ u8 max_ratio;
+ /*
+ * Pll ratio for the efficiency frequency. The VPU has optimum
+ * performance to power ratio at this frequency.
+ */
+ u8 pn_ratio;
+ u32 profiling_freq;
+ } pll;
+ struct {
+ u32 grace_period[VPU_HWS_NUM_PRIORITY_BANDS];
+ u32 process_quantum[VPU_HWS_NUM_PRIORITY_BANDS];
+ u32 process_grace_period[VPU_HWS_NUM_PRIORITY_BANDS];
+ } hws;
+ u32 tile_fuse;
+ u32 sku;
+ u16 config;
+ int dma_bits;
+ ktime_t d0i3_entry_host_ts;
+ u64 d0i3_entry_vpu_ts;
+ atomic_t firewall_irq_counter;
+};
+
+int ivpu_hw_init(struct ivpu_device *vdev);
+int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start,
+ u64 size);
+int ivpu_hw_power_up(struct ivpu_device *vdev);
+int ivpu_hw_power_down(struct ivpu_device *vdev);
+int ivpu_hw_reset(struct ivpu_device *vdev);
+int ivpu_hw_boot_fw(struct ivpu_device *vdev);
+void ivpu_hw_profiling_freq_drive(struct ivpu_device *vdev, bool enable);
+void ivpu_irq_handlers_init(struct ivpu_device *vdev);
+void ivpu_hw_irq_enable(struct ivpu_device *vdev);
+void ivpu_hw_irq_disable(struct ivpu_device *vdev);
+irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr);
+bool ivpu_hw_uses_ecc_mca_signal(struct ivpu_device *vdev);
+
+static inline u32 ivpu_hw_btrs_irq_handler(struct ivpu_device *vdev, int irq)
+{
+ return vdev->hw->irq.btrs_irq_handler(vdev, irq);
+}
+
+static inline u32 ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq)
+{
+ return vdev->hw->irq.ip_irq_handler(vdev, irq);
+}
+
+static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range)
+{
+ return range->end - range->start;
+}
+
+static inline u32 ivpu_hw_dpu_max_freq_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_dpu_max_freq_get(vdev);
+}
+
+static inline u32 ivpu_hw_dpu_freq_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_dpu_freq_get(vdev);
+}
+
+static inline void ivpu_hw_irq_clear(struct ivpu_device *vdev)
+{
+ ivpu_hw_ip_irq_clear(vdev);
+}
+
+static inline u32 ivpu_hw_profiling_freq_get(struct ivpu_device *vdev)
+{
+ return vdev->hw->pll.profiling_freq;
+}
+
+static inline void ivpu_hw_diagnose_failure(struct ivpu_device *vdev)
+{
+ ivpu_hw_ip_diagnose_failure(vdev);
+ ivpu_hw_btrs_diagnose_failure(vdev);
+}
+
+static inline u32 ivpu_hw_telemetry_offset_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_telemetry_offset_get(vdev);
+}
+
+static inline u32 ivpu_hw_telemetry_size_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_telemetry_size_get(vdev);
+}
+
+static inline u32 ivpu_hw_telemetry_enable_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_telemetry_enable_get(vdev);
+}
+
+static inline bool ivpu_hw_is_idle(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_is_idle(vdev);
+}
+
+static inline int ivpu_hw_wait_for_idle(struct ivpu_device *vdev)
+{
+ return ivpu_hw_btrs_wait_for_idle(vdev);
+}
+
+static inline void ivpu_hw_ipc_tx_set(struct ivpu_device *vdev, u32 vpu_addr)
+{
+ ivpu_hw_ip_ipc_tx_set(vdev, vpu_addr);
+}
+
+static inline void ivpu_hw_db_set(struct ivpu_device *vdev, u32 db_id)
+{
+ ivpu_hw_ip_db_set(vdev, db_id);
+}
+
+static inline u32 ivpu_hw_ipc_rx_addr_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_ip_ipc_rx_addr_get(vdev);
+}
+
+static inline u32 ivpu_hw_ipc_rx_count_get(struct ivpu_device *vdev)
+{
+ return ivpu_hw_ip_ipc_rx_count_get(vdev);
+}
+
+#endif /* __IVPU_HW_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_37xx_reg.h b/drivers/accel/ivpu/ivpu_hw_37xx_reg.h
new file mode 100644
index 000000000000..cf5e2f01049c
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_37xx_reg.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_37XX_REG_H__
+#define __IVPU_HW_37XX_REG_H__
+
+#include <linux/bits.h>
+
+#define VPU_37XX_HOST_SS_CPR_CLK_SET 0x00000084u
+#define VPU_37XX_HOST_SS_CPR_CLK_SET_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_SS_CPR_CLK_SET_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_37XX_HOST_SS_CPR_CLK_SET_MSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_37XX_HOST_SS_CPR_RST_SET 0x00000094u
+#define VPU_37XX_HOST_SS_CPR_RST_SET_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_SS_CPR_RST_SET_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_37XX_HOST_SS_CPR_RST_SET_MSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_37XX_HOST_SS_CPR_RST_CLR 0x00000098u
+#define VPU_37XX_HOST_SS_CPR_RST_CLR_AON_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_SS_CPR_RST_CLR_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_SS_CPR_RST_CLR_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_37XX_HOST_SS_CPR_RST_CLR_MSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_37XX_HOST_SS_HW_VERSION 0x00000108u
+#define VPU_37XX_HOST_SS_HW_VERSION_SOC_REVISION_MASK GENMASK(7, 0)
+#define VPU_37XX_HOST_SS_HW_VERSION_SOC_NUMBER_MASK GENMASK(15, 8)
+#define VPU_37XX_HOST_SS_HW_VERSION_VPU_GENERATION_MASK GENMASK(23, 16)
+
+#define VPU_37XX_HOST_SS_GEN_CTRL 0x00000118u
+#define VPU_37XX_HOST_SS_GEN_CTRL_PS_MASK GENMASK(31, 29)
+
+#define VPU_37XX_HOST_SS_NOC_QREQN 0x00000154u
+#define VPU_37XX_HOST_SS_NOC_QREQN_TOP_SOCMMIO_MASK BIT_MASK(0)
+
+#define VPU_37XX_HOST_SS_NOC_QACCEPTN 0x00000158u
+#define VPU_37XX_HOST_SS_NOC_QACCEPTN_TOP_SOCMMIO_MASK BIT_MASK(0)
+
+#define VPU_37XX_HOST_SS_NOC_QDENY 0x0000015cu
+#define VPU_37XX_HOST_SS_NOC_QDENY_TOP_SOCMMIO_MASK BIT_MASK(0)
+
+#define VPU_37XX_TOP_NOC_QREQN 0x00000160u
+#define VPU_37XX_TOP_NOC_QREQN_CPU_CTRL_MASK BIT_MASK(0)
+#define VPU_37XX_TOP_NOC_QREQN_HOSTIF_L2CACHE_MASK BIT_MASK(1)
+
+#define VPU_37XX_TOP_NOC_QACCEPTN 0x00000164u
+#define VPU_37XX_TOP_NOC_QACCEPTN_CPU_CTRL_MASK BIT_MASK(0)
+#define VPU_37XX_TOP_NOC_QACCEPTN_HOSTIF_L2CACHE_MASK BIT_MASK(1)
+
+#define VPU_37XX_TOP_NOC_QDENY 0x00000168u
+#define VPU_37XX_TOP_NOC_QDENY_CPU_CTRL_MASK BIT_MASK(0)
+#define VPU_37XX_TOP_NOC_QDENY_HOSTIF_L2CACHE_MASK BIT_MASK(1)
+
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN 0x00000170u
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_CSS_ROM_CMX_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_CSS_DBG_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_CSS_CTRL_MASK BIT_MASK(2)
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_DEC400_MASK BIT_MASK(3)
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_MSS_NCE_MASK BIT_MASK(4)
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_MSS_MBI_MASK BIT_MASK(5)
+#define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_MSS_MBI_CMX_MASK BIT_MASK(6)
+
+#define VPU_37XX_HOST_SS_ICB_STATUS_0 0x00010210u
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_TIMER_0_INT_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_TIMER_1_INT_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_TIMER_2_INT_MASK BIT_MASK(2)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_TIMER_3_INT_MASK BIT_MASK(3)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_HOST_IPC_FIFO_INT_MASK BIT_MASK(4)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_0_INT_MASK BIT_MASK(5)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_1_INT_MASK BIT_MASK(6)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_2_INT_MASK BIT_MASK(7)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_NOC_FIREWALL_INT_MASK BIT_MASK(8)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_CPU_INT_REDIRECT_0_INT_MASK BIT_MASK(30)
+#define VPU_37XX_HOST_SS_ICB_STATUS_0_CPU_INT_REDIRECT_1_INT_MASK BIT_MASK(31)
+
+#define VPU_37XX_HOST_SS_ICB_STATUS_1 0x00010214u
+#define VPU_37XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_2_INT_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_3_INT_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_4_INT_MASK BIT_MASK(2)
+
+#define VPU_37XX_HOST_SS_ICB_CLEAR_0 0x00010220u
+#define VPU_37XX_HOST_SS_ICB_CLEAR_1 0x00010224u
+#define VPU_37XX_HOST_SS_ICB_ENABLE_0 0x00010240u
+
+#define VPU_37XX_HOST_SS_TIM_IPC_FIFO_ATM 0x000200f4u
+
+#define VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT 0x000200fcu
+#define VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT_READ_POINTER_MASK GENMASK(7, 0)
+#define VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT_WRITE_POINTER_MASK GENMASK(15, 8)
+#define VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT_FILL_LEVEL_MASK GENMASK(23, 16)
+#define VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT_RSVD0_MASK GENMASK(31, 24)
+
+#define VPU_37XX_HOST_SS_AON_PWR_ISO_EN0 0x00030020u
+#define VPU_37XX_HOST_SS_AON_PWR_ISO_EN0_MSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0 0x00030024u
+#define VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0_MSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0 0x00030028u
+#define VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0_MSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0 0x0003002cu
+#define VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0_MSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN 0x00030200u
+#define VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN_EN_MASK BIT_MASK(0)
+
+#define VPU_37XX_HOST_SS_AON_DPU_ACTIVE 0x00030204u
+#define VPU_37XX_HOST_SS_AON_DPU_ACTIVE_DPU_ACTIVE_MASK BIT_MASK(0)
+
+#define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO 0x00041040u
+#define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO_DONE_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO_IOSF_RS_ID_MASK GENMASK(2, 1)
+#define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO_IMAGE_LOCATION_MASK GENMASK(31, 3)
+
+#define VPU_37XX_HOST_SS_WORKPOINT_CONFIG_MIRROR 0x00082020u
+#define VPU_37XX_HOST_SS_WORKPOINT_CONFIG_MIRROR_FINAL_PLL_FREQ_MASK GENMASK(15, 0)
+#define VPU_37XX_HOST_SS_WORKPOINT_CONFIG_MIRROR_CONFIG_ID_MASK GENMASK(31, 16)
+
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES 0x00360000u
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_CACHE_OVERRIDE_EN_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_AWCACHE_OVERRIDE_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_ARCACHE_OVERRIDE_MASK BIT_MASK(2)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_NOSNOOP_OVERRIDE_EN_MASK BIT_MASK(3)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_AW_NOSNOOP_OVERRIDE_MASK BIT_MASK(4)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_AR_NOSNOOP_OVERRIDE_MASK BIT_MASK(5)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AW_CONTEXT_FLAG_MASK GENMASK(10, 6)
+#define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AR_CONTEXT_FLAG_MASK GENMASK(15, 11)
+
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV 0x00360004u
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU0_AWMMUSSIDV_MASK BIT_MASK(0)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU0_ARMMUSSIDV_MASK BIT_MASK(1)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU1_AWMMUSSIDV_MASK BIT_MASK(2)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU1_ARMMUSSIDV_MASK BIT_MASK(3)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU2_AWMMUSSIDV_MASK BIT_MASK(4)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU2_ARMMUSSIDV_MASK BIT_MASK(5)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU3_AWMMUSSIDV_MASK BIT_MASK(6)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU3_ARMMUSSIDV_MASK BIT_MASK(7)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU4_AWMMUSSIDV_MASK BIT_MASK(8)
+#define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU4_ARMMUSSIDV_MASK BIT_MASK(9)
+
+#define VPU_37XX_CPU_SS_DSU_LEON_RT_BASE 0x04000000u
+#define VPU_37XX_CPU_SS_DSU_LEON_RT_DSU_CTRL 0x04000000u
+#define VPU_37XX_CPU_SS_DSU_LEON_RT_PC_REG 0x04400010u
+#define VPU_37XX_CPU_SS_DSU_LEON_RT_NPC_REG 0x04400014u
+#define VPU_37XX_CPU_SS_DSU_LEON_RT_DSU_TRAP_REG 0x04400020u
+
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_CLK_SET 0x06010004u
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_CLK_SET_CPU_DSU_MASK BIT_MASK(1)
+
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_RST_CLR 0x06010018u
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_RST_CLR_CPU_DSU_MASK BIT_MASK(1)
+
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC 0x06010040u
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC_IRQI_RSTRUN0_MASK BIT_MASK(0)
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC_IRQI_RESUME0_MASK BIT_MASK(1)
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC_IRQI_RSTRUN1_MASK BIT_MASK(2)
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC_IRQI_RESUME1_MASK BIT_MASK(3)
+#define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC_IRQI_RSTVEC_MASK GENMASK(31, 4)
+
+#define VPU_37XX_CPU_SS_TIM_WATCHDOG 0x0602009cu
+#define VPU_37XX_CPU_SS_TIM_WDOG_EN 0x060200a4u
+#define VPU_37XX_CPU_SS_TIM_SAFE 0x060200a8u
+#define VPU_37XX_CPU_SS_TIM_IPC_FIFO 0x060200f0u
+
+#define VPU_37XX_CPU_SS_TIM_GEN_CONFIG 0x06021008u
+#define VPU_37XX_CPU_SS_TIM_GEN_CONFIG_WDOG_TO_INT_CLR_MASK BIT_MASK(9)
+
+#define VPU_37XX_CPU_SS_TIM_PERF_FREE_CNT 0x06029000u
+
+#define VPU_37XX_CPU_SS_DOORBELL_0 0x06300000u
+#define VPU_37XX_CPU_SS_DOORBELL_0_SET_MASK BIT_MASK(0)
+
+#define VPU_37XX_CPU_SS_DOORBELL_1 0x06301000u
+
+#endif /* __IVPU_HW_37XX_REG_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h
new file mode 100644
index 000000000000..fc0ee8d637f9
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_40XX_REG_H__
+#define __IVPU_HW_40XX_REG_H__
+
+#include <linux/bits.h>
+
+#define VPU_40XX_HOST_SS_CPR_CLK_EN 0x00000080u
+#define VPU_40XX_HOST_SS_CPR_CLK_EN_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_CPR_CLK_EN_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_40XX_HOST_SS_CPR_CLK_EN_CSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_40XX_HOST_SS_CPR_CLK_SET 0x00000084u
+#define VPU_40XX_HOST_SS_CPR_CLK_SET_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_CPR_CLK_SET_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_40XX_HOST_SS_CPR_CLK_SET_MSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_40XX_HOST_SS_CPR_RST_EN 0x00000090u
+#define VPU_40XX_HOST_SS_CPR_RST_EN_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_CPR_RST_EN_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_40XX_HOST_SS_CPR_RST_EN_CSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_40XX_HOST_SS_CPR_RST_SET 0x00000094u
+#define VPU_40XX_HOST_SS_CPR_RST_SET_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_CPR_RST_SET_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_40XX_HOST_SS_CPR_RST_SET_MSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_40XX_HOST_SS_CPR_RST_CLR 0x00000098u
+#define VPU_40XX_HOST_SS_CPR_RST_CLR_TOP_NOC_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_CPR_RST_CLR_DSS_MAS_MASK BIT_MASK(10)
+#define VPU_40XX_HOST_SS_CPR_RST_CLR_MSS_MAS_MASK BIT_MASK(11)
+
+#define VPU_40XX_HOST_SS_HW_VERSION 0x00000108u
+#define VPU_40XX_HOST_SS_HW_VERSION_SOC_REVISION_MASK GENMASK(7, 0)
+#define VPU_40XX_HOST_SS_HW_VERSION_SOC_NUMBER_MASK GENMASK(15, 8)
+#define VPU_40XX_HOST_SS_HW_VERSION_VPU_GENERATION_MASK GENMASK(23, 16)
+
+#define VPU_40XX_HOST_SS_SW_VERSION 0x0000010cu
+
+#define VPU_40XX_HOST_SS_GEN_CTRL 0x00000118u
+#define VPU_40XX_HOST_SS_GEN_CTRL_PS_MASK GENMASK(31, 29)
+
+#define VPU_40XX_HOST_SS_NOC_QREQN 0x00000154u
+#define VPU_40XX_HOST_SS_NOC_QREQN_TOP_SOCMMIO_MASK BIT_MASK(0)
+
+#define VPU_40XX_HOST_SS_NOC_QACCEPTN 0x00000158u
+#define VPU_40XX_HOST_SS_NOC_QACCEPTN_TOP_SOCMMIO_MASK BIT_MASK(0)
+
+#define VPU_40XX_HOST_SS_NOC_QDENY 0x0000015cu
+#define VPU_40XX_HOST_SS_NOC_QDENY_TOP_SOCMMIO_MASK BIT_MASK(0)
+
+#define VPU_40XX_TOP_NOC_QREQN 0x00000160u
+#define VPU_40XX_TOP_NOC_QREQN_CPU_CTRL_MASK BIT_MASK(0)
+#define VPU_40XX_TOP_NOC_QREQN_HOSTIF_L2CACHE_MASK BIT_MASK(2)
+
+#define VPU_40XX_TOP_NOC_QACCEPTN 0x00000164u
+#define VPU_40XX_TOP_NOC_QACCEPTN_CPU_CTRL_MASK BIT_MASK(0)
+#define VPU_40XX_TOP_NOC_QACCEPTN_HOSTIF_L2CACHE_MASK BIT_MASK(2)
+
+#define VPU_40XX_TOP_NOC_QDENY 0x00000168u
+#define VPU_40XX_TOP_NOC_QDENY_CPU_CTRL_MASK BIT_MASK(0)
+#define VPU_40XX_TOP_NOC_QDENY_HOSTIF_L2CACHE_MASK BIT_MASK(2)
+
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN 0x00000170u
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_CSS_ROM_CMX_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_CSS_DBG_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_CSS_CTRL_MASK BIT_MASK(2)
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_DEC400_MASK BIT_MASK(3)
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_MSS_NCE_MASK BIT_MASK(4)
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_MSS_MBI_MASK BIT_MASK(5)
+#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_MSS_MBI_CMX_MASK BIT_MASK(6)
+
+#define VPU_40XX_HOST_SS_ICB_STATUS_0 0x00010210u
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_0_INT_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_1_INT_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_2_INT_MASK BIT_MASK(2)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_3_INT_MASK BIT_MASK(3)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_HOST_IPC_FIFO_INT_MASK BIT_MASK(4)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_0_INT_MASK BIT_MASK(5)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_1_INT_MASK BIT_MASK(6)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_2_INT_MASK BIT_MASK(7)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_NOC_FIREWALL_INT_MASK BIT_MASK(8)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_CPU_INT_REDIRECT_0_INT_MASK BIT_MASK(30)
+#define VPU_40XX_HOST_SS_ICB_STATUS_0_CPU_INT_REDIRECT_1_INT_MASK BIT_MASK(31)
+
+#define VPU_40XX_HOST_SS_ICB_STATUS_1 0x00010214u
+#define VPU_40XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_2_INT_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_3_INT_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_4_INT_MASK BIT_MASK(2)
+
+#define VPU_40XX_HOST_SS_ICB_CLEAR_0 0x00010220u
+#define VPU_40XX_HOST_SS_ICB_CLEAR_1 0x00010224u
+#define VPU_40XX_HOST_SS_ICB_ENABLE_0 0x00010240u
+#define VPU_40XX_HOST_SS_ICB_ENABLE_1 0x00010244u
+
+#define VPU_40XX_HOST_SS_TIM_IPC_FIFO_ATM 0x000200f4u
+
+#define VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT 0x000200fcu
+#define VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT_FILL_LEVEL_MASK GENMASK(23, 16)
+
+#define VPU_40XX_HOST_SS_AON_PWR_ISO_EN0 0x00030020u
+#define VPU_40XX_HOST_SS_AON_PWR_ISO_EN0_CSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0 0x00030024u
+#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0_CSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0 0x00030028u
+#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0_CSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_STATUS0 0x0003002cu
+#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_STATUS0_CSS_CPU_MASK BIT_MASK(3)
+
+#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY 0x00030068u
+#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST_DLY_MASK GENMASK(7, 0)
+#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST1_DLY_MASK GENMASK(15, 8)
+#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST2_DLY_MASK GENMASK(23, 16)
+
+#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY 0x0003006cu
+#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY_STATUS_DLY_MASK GENMASK(7, 0)
+
+#define VPU_40XX_HOST_SS_AON_IDLE_GEN 0x00030200u
+#define VPU_40XX_HOST_SS_AON_IDLE_GEN_EN_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_SS_AON_IDLE_GEN_HW_PG_EN_MASK BIT_MASK(1)
+
+#define VPU_40XX_HOST_SS_AON_DPU_ACTIVE 0x00030204u
+#define VPU_40XX_HOST_SS_AON_DPU_ACTIVE_DPU_ACTIVE_MASK BIT_MASK(0)
+
+#define VPU_50XX_HOST_SS_AON_FABRIC_REQ_OVERRIDE 0x00030210u
+#define VPU_50XX_HOST_SS_AON_FABRIC_REQ_OVERRIDE_REQ_OVERRIDE_MASK BIT_MASK(0)
+
+#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO 0x00040040u
+#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_DONE_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IOSF_RS_ID_MASK GENMASK(2, 1)
+#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IMAGE_LOCATION_MASK GENMASK(31, 3)
+
+#define VPU_40XX_HOST_SS_WORKPOINT_CONFIG_MIRROR 0x00082020u
+#define VPU_40XX_HOST_SS_WORKPOINT_CONFIG_MIRROR_FINAL_PLL_FREQ_MASK GENMASK(15, 0)
+#define VPU_40XX_HOST_SS_WORKPOINT_CONFIG_MIRROR_CONFIG_ID_MASK GENMASK(31, 16)
+
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES 0x00360000u
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_CACHE_OVERRIDE_EN_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AWCACHE_OVERRIDE_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_ARCACHE_OVERRIDE_MASK BIT_MASK(2)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_SNOOP_OVERRIDE_EN_MASK BIT_MASK(3)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AW_SNOOP_OVERRIDE_MASK BIT_MASK(4)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AR_SNOOP_OVERRIDE_MASK BIT_MASK(5)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AW_CONTEXT_FLAG_MASK GENMASK(10, 6)
+#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AR_CONTEXT_FLAG_MASK GENMASK(15, 11)
+
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV 0x00360004u
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU0_AWMMUSSIDV_MASK BIT_MASK(0)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU0_ARMMUSSIDV_MASK BIT_MASK(1)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU1_AWMMUSSIDV_MASK BIT_MASK(2)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU1_ARMMUSSIDV_MASK BIT_MASK(3)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU2_AWMMUSSIDV_MASK BIT_MASK(4)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU2_ARMMUSSIDV_MASK BIT_MASK(5)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU3_AWMMUSSIDV_MASK BIT_MASK(6)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU3_ARMMUSSIDV_MASK BIT_MASK(7)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU4_AWMMUSSIDV_MASK BIT_MASK(8)
+#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU4_ARMMUSSIDV_MASK BIT_MASK(9)
+
+#define VPU_40XX_CPU_SS_DSU_LEON_RT_BASE 0x04000000u
+#define VPU_40XX_CPU_SS_DSU_LEON_RT_DSU_CTRL 0x04000000u
+#define VPU_40XX_CPU_SS_DSU_LEON_RT_PC_REG 0x04400010u
+#define VPU_40XX_CPU_SS_DSU_LEON_RT_NPC_REG 0x04400014u
+#define VPU_40XX_CPU_SS_DSU_LEON_RT_DSU_TRAP_REG 0x04400020u
+
+#define VPU_40XX_CPU_SS_TIM_WATCHDOG 0x0102009cu
+#define VPU_40XX_CPU_SS_TIM_WDOG_EN 0x010200a4u
+#define VPU_40XX_CPU_SS_TIM_SAFE 0x010200a8u
+
+#define VPU_40XX_CPU_SS_TIM_GEN_CONFIG 0x01021008u
+#define VPU_40XX_CPU_SS_TIM_GEN_CONFIG_WDOG_TO_INT_CLR_MASK BIT_MASK(9)
+
+#define VPU_40XX_CPU_SS_CPR_NOC_QREQN 0x01010030u
+#define VPU_40XX_CPU_SS_CPR_NOC_QREQN_TOP_MMIO_MASK BIT_MASK(0)
+
+#define VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN 0x01010034u
+#define VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN_TOP_MMIO_MASK BIT_MASK(0)
+
+#define VPU_40XX_CPU_SS_CPR_NOC_QDENY 0x01010038u
+#define VPU_40XX_CPU_SS_CPR_NOC_QDENY_TOP_MMIO_MASK BIT_MASK(0)
+
+#define VPU_40XX_CPU_SS_TIM_IPC_FIFO 0x010200f0u
+#define VPU_40XX_CPU_SS_TIM_PERF_EXT_FREE_CNT 0x01029008u
+
+#define VPU_40XX_CPU_SS_DOORBELL_0 0x01300000u
+#define VPU_40XX_CPU_SS_DOORBELL_0_SET_MASK BIT_MASK(0)
+
+#define VPU_40XX_CPU_SS_DOORBELL_1 0x01301000u
+
+#endif /* __IVPU_HW_40XX_REG_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c
new file mode 100644
index 000000000000..06e65c592618
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_btrs.c
@@ -0,0 +1,905 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#include <linux/units.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_hw.h"
+#include "ivpu_hw_btrs.h"
+#include "ivpu_hw_btrs_lnl_reg.h"
+#include "ivpu_hw_btrs_mtl_reg.h"
+#include "ivpu_hw_reg_io.h"
+#include "ivpu_pm.h"
+
+#define BTRS_MTL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR)) | \
+ (REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, UFI_ERR)))
+
+#define BTRS_LNL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR)) | \
+ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI0_ERR)) | \
+ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI1_ERR)) | \
+ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR0_ERR)) | \
+ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR)) | \
+ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR)))
+
+#define BTRS_MTL_ALL_IRQ_MASK (BTRS_MTL_IRQ_MASK | (REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, \
+ FREQ_CHANGE)))
+
+#define BTRS_IRQ_DISABLE_MASK ((u32)-1)
+
+#define BTRS_LNL_ALL_IRQ_MASK ((u32)-1)
+
+
+#define PLL_CDYN_DEFAULT 0x80
+#define PLL_EPP_DEFAULT 0x80
+#define PLL_REF_CLK_FREQ 50000000ull
+#define PLL_RATIO_TO_FREQ(x) ((x) * PLL_REF_CLK_FREQ)
+
+#define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC)
+#define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC)
+#define TIMEOUT_US (150 * USEC_PER_MSEC)
+
+/* Work point configuration values */
+#define WP_CONFIG(tile, ratio) (((tile) << 8) | (ratio))
+#define MTL_CONFIG_1_TILE 0x01
+#define MTL_CONFIG_2_TILE 0x02
+#define MTL_PLL_RATIO_5_3 0x01
+#define MTL_PLL_RATIO_4_3 0x02
+#define BTRS_MTL_TILE_FUSE_ENABLE_BOTH 0x0
+#define BTRS_MTL_TILE_SKU_BOTH 0x3630
+
+#define BTRS_LNL_TILE_MAX_NUM 6
+#define BTRS_LNL_TILE_MAX_MASK 0x3f
+
+#define WEIGHTS_DEFAULT 0xf711f711u
+#define WEIGHTS_ATS_DEFAULT 0x0000f711u
+
+#define DCT_REQ 0x2
+#define DCT_ENABLE 0x1
+#define DCT_DISABLE 0x0
+
+static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio);
+
+int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev)
+{
+ REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, BTRS_MTL_ALL_IRQ_MASK);
+ if (REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) == BTRS_MTL_ALL_IRQ_MASK) {
+ /* Writing 1s does not clear the interrupt status register */
+ REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, 0x0);
+ return true;
+ }
+
+ return false;
+}
+
+static void freq_ratios_init_mtl(struct ivpu_device *vdev)
+{
+ struct ivpu_hw_info *hw = vdev->hw;
+ u32 fmin_fuse, fmax_fuse;
+
+ fmin_fuse = REGB_RD32(VPU_HW_BTRS_MTL_FMIN_FUSE);
+ hw->pll.min_ratio = REG_GET_FLD(VPU_HW_BTRS_MTL_FMIN_FUSE, MIN_RATIO, fmin_fuse);
+ hw->pll.pn_ratio = REG_GET_FLD(VPU_HW_BTRS_MTL_FMIN_FUSE, PN_RATIO, fmin_fuse);
+
+ fmax_fuse = REGB_RD32(VPU_HW_BTRS_MTL_FMAX_FUSE);
+ hw->pll.max_ratio = REG_GET_FLD(VPU_HW_BTRS_MTL_FMAX_FUSE, MAX_RATIO, fmax_fuse);
+}
+
+static void freq_ratios_init_lnl(struct ivpu_device *vdev)
+{
+ struct ivpu_hw_info *hw = vdev->hw;
+ u32 fmin_fuse, fmax_fuse;
+
+ fmin_fuse = REGB_RD32(VPU_HW_BTRS_LNL_FMIN_FUSE);
+ hw->pll.min_ratio = REG_GET_FLD(VPU_HW_BTRS_LNL_FMIN_FUSE, MIN_RATIO, fmin_fuse);
+ hw->pll.pn_ratio = REG_GET_FLD(VPU_HW_BTRS_LNL_FMIN_FUSE, PN_RATIO, fmin_fuse);
+
+ fmax_fuse = REGB_RD32(VPU_HW_BTRS_LNL_FMAX_FUSE);
+ hw->pll.max_ratio = REG_GET_FLD(VPU_HW_BTRS_LNL_FMAX_FUSE, MAX_RATIO, fmax_fuse);
+}
+
+void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev)
+{
+ struct ivpu_hw_info *hw = vdev->hw;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ freq_ratios_init_mtl(vdev);
+ else
+ freq_ratios_init_lnl(vdev);
+
+ hw->pll.min_ratio = clamp_t(u8, ivpu_pll_min_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
+ hw->pll.max_ratio = clamp_t(u8, ivpu_pll_max_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
+ hw->pll.pn_ratio = clamp_t(u8, hw->pll.pn_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
+}
+
+static bool tile_disable_check(u32 config)
+{
+ /* Allowed values: 0 or one bit from range 0-5 (6 tiles) */
+ if (config == 0)
+ return true;
+
+ if (config > BIT(BTRS_LNL_TILE_MAX_NUM - 1))
+ return false;
+
+ if ((config & (config - 1)) == 0)
+ return true;
+
+ return false;
+}
+
+static int read_tile_config_fuse(struct ivpu_device *vdev, u32 *tile_fuse_config)
+{
+ u32 fuse;
+ u32 config;
+
+ fuse = REGB_RD32(VPU_HW_BTRS_LNL_TILE_FUSE);
+ if (!REG_TEST_FLD(VPU_HW_BTRS_LNL_TILE_FUSE, VALID, fuse)) {
+ ivpu_err(vdev, "Fuse: invalid (0x%x)\n", fuse);
+ return -EIO;
+ }
+
+ config = REG_GET_FLD(VPU_HW_BTRS_LNL_TILE_FUSE, CONFIG, fuse);
+ if (!tile_disable_check(config))
+ ivpu_warn(vdev, "More than 1 tile disabled, tile fuse config mask: 0x%x\n", config);
+
+ ivpu_dbg(vdev, MISC, "Tile disable config mask: 0x%x\n", config);
+
+ *tile_fuse_config = config;
+ return 0;
+}
+
+static int info_init_mtl(struct ivpu_device *vdev)
+{
+ struct ivpu_hw_info *hw = vdev->hw;
+
+ hw->tile_fuse = BTRS_MTL_TILE_FUSE_ENABLE_BOTH;
+ hw->sku = BTRS_MTL_TILE_SKU_BOTH;
+ hw->config = WP_CONFIG(MTL_CONFIG_2_TILE, MTL_PLL_RATIO_4_3);
+
+ return 0;
+}
+
+static int info_init_lnl(struct ivpu_device *vdev)
+{
+ struct ivpu_hw_info *hw = vdev->hw;
+ u32 tile_fuse_config;
+ int ret;
+
+ ret = read_tile_config_fuse(vdev, &tile_fuse_config);
+ if (ret)
+ return ret;
+
+ hw->tile_fuse = tile_fuse_config;
+ hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT;
+
+ return 0;
+}
+
+int ivpu_hw_btrs_info_init(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return info_init_mtl(vdev);
+ else
+ return info_init_lnl(vdev);
+}
+
+static int wp_request_sync(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return REGB_POLL_FLD(VPU_HW_BTRS_MTL_WP_REQ_CMD, SEND, 0, PLL_TIMEOUT_US);
+ else
+ return REGB_POLL_FLD(VPU_HW_BTRS_LNL_WP_REQ_CMD, SEND, 0, PLL_TIMEOUT_US);
+}
+
+static int wait_for_status_ready(struct ivpu_device *vdev, bool enable)
+{
+ u32 exp_val = enable ? 0x1 : 0x0;
+
+ if (IVPU_WA(punit_disabled))
+ return 0;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, READY, exp_val, PLL_TIMEOUT_US);
+ else
+ return REGB_POLL_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, READY, exp_val, PLL_TIMEOUT_US);
+}
+
+struct wp_request {
+ u16 min;
+ u16 max;
+ u16 target;
+ u16 cfg;
+ u16 epp;
+ u16 cdyn;
+};
+
+static void wp_request_mtl(struct ivpu_device *vdev, struct wp_request *wp)
+{
+ u32 val;
+
+ val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0, MIN_RATIO, wp->min, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0, MAX_RATIO, wp->max, val);
+ REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0, val);
+
+ val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1, TARGET_RATIO, wp->target, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1, EPP, PLL_EPP_DEFAULT, val);
+ REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1, val);
+
+ val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2, CONFIG, wp->cfg, val);
+ REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2, val);
+
+ val = REGB_RD32(VPU_HW_BTRS_MTL_WP_REQ_CMD);
+ val = REG_SET_FLD(VPU_HW_BTRS_MTL_WP_REQ_CMD, SEND, val);
+ REGB_WR32(VPU_HW_BTRS_MTL_WP_REQ_CMD, val);
+}
+
+static void wp_request_lnl(struct ivpu_device *vdev, struct wp_request *wp)
+{
+ u32 val;
+
+ val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0, MIN_RATIO, wp->min, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0, MAX_RATIO, wp->max, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0, val);
+
+ val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1, TARGET_RATIO, wp->target, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1, EPP, wp->epp, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1, val);
+
+ val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2, CONFIG, wp->cfg, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2, CDYN, wp->cdyn, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2, val);
+
+ val = REGB_RD32(VPU_HW_BTRS_LNL_WP_REQ_CMD);
+ val = REG_SET_FLD(VPU_HW_BTRS_LNL_WP_REQ_CMD, SEND, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_WP_REQ_CMD, val);
+}
+
+static void wp_request(struct ivpu_device *vdev, struct wp_request *wp)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ wp_request_mtl(vdev, wp);
+ else
+ wp_request_lnl(vdev, wp);
+}
+
+static int wp_request_send(struct ivpu_device *vdev, struct wp_request *wp)
+{
+ int ret;
+
+ ret = wp_request_sync(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to sync before workpoint request: %d\n", ret);
+ return ret;
+ }
+
+ wp_request(vdev, wp);
+
+ ret = wp_request_sync(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to sync after workpoint request: %d\n", ret);
+
+ return ret;
+}
+
+static void prepare_wp_request(struct ivpu_device *vdev, struct wp_request *wp, bool enable)
+{
+ struct ivpu_hw_info *hw = vdev->hw;
+
+ wp->min = hw->pll.min_ratio;
+ wp->max = hw->pll.max_ratio;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
+ wp->target = enable ? hw->pll.pn_ratio : 0;
+ wp->cfg = enable ? hw->config : 0;
+ wp->cdyn = 0;
+ wp->epp = 0;
+ } else {
+ wp->target = hw->pll.pn_ratio;
+ wp->cfg = 0;
+ wp->cdyn = enable ? PLL_CDYN_DEFAULT : 0;
+ wp->epp = enable ? PLL_EPP_DEFAULT : 0;
+ }
+}
+
+static int wait_for_pll_lock(struct ivpu_device *vdev, bool enable)
+{
+ u32 exp_val = enable ? 0x1 : 0x0;
+
+ if (ivpu_hw_btrs_gen(vdev) != IVPU_HW_BTRS_MTL)
+ return 0;
+
+ if (IVPU_WA(punit_disabled))
+ return 0;
+
+ return REGB_POLL_FLD(VPU_HW_BTRS_MTL_PLL_STATUS, LOCK, exp_val, PLL_TIMEOUT_US);
+}
+
+static int wait_for_cdyn_deassert(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return 0;
+
+ return REGB_POLL_FLD(VPU_HW_BTRS_LNL_CDYN, CDYN, 0, PLL_TIMEOUT_US);
+}
+
+int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
+{
+ struct wp_request wp;
+ int ret;
+
+ if (IVPU_WA(punit_disabled)) {
+ ivpu_dbg(vdev, PM, "Skipping workpoint request\n");
+ return 0;
+ }
+
+ prepare_wp_request(vdev, &wp, enable);
+
+ ivpu_dbg(vdev, PM, "PLL workpoint request: %lu MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n",
+ pll_ratio_to_dpu_freq(vdev, wp.target) / HZ_PER_MHZ, wp.cfg, wp.epp, wp.cdyn);
+
+ ret = wp_request_send(vdev, &wp);
+ if (ret) {
+ ivpu_err(vdev, "Failed to send workpoint request: %d\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_pll_lock(vdev, enable);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for PLL lock\n");
+ return ret;
+ }
+
+ ret = wait_for_status_ready(vdev, enable);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for NPU ready status\n");
+ return ret;
+ }
+
+ if (!enable) {
+ ret = wait_for_cdyn_deassert(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for CDYN deassert\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int d0i3_drive_mtl(struct ivpu_device *vdev, bool enable)
+{
+ int ret;
+ u32 val;
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
+ if (ret) {
+ ivpu_err(vdev, "Failed to sync before D0i3 transition: %d\n", ret);
+ return ret;
+ }
+
+ val = REGB_RD32(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL);
+ if (enable)
+ val = REG_SET_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, I3, val);
+ else
+ val = REG_CLR_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, I3, val);
+ REGB_WR32(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, val);
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
+ if (ret)
+ ivpu_err(vdev, "Failed to sync after D0i3 transition: %d\n", ret);
+
+ return ret;
+}
+
+static int d0i3_drive_lnl(struct ivpu_device *vdev, bool enable)
+{
+ int ret;
+ u32 val;
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
+ if (ret) {
+ ivpu_err(vdev, "Failed to sync before D0i3 transition: %d\n", ret);
+ return ret;
+ }
+
+ val = REGB_RD32(VPU_HW_BTRS_LNL_D0I3_CONTROL);
+ if (enable)
+ val = REG_SET_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, I3, val);
+ else
+ val = REG_CLR_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, I3, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_D0I3_CONTROL, val);
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_D0I3_CONTROL, INPROGRESS, 0, TIMEOUT_US);
+ if (ret) {
+ ivpu_err(vdev, "Failed to sync after D0i3 transition: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int d0i3_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return d0i3_drive_mtl(vdev, enable);
+ else
+ return d0i3_drive_lnl(vdev, enable);
+}
+
+int ivpu_hw_btrs_d0i3_enable(struct ivpu_device *vdev)
+{
+ int ret;
+
+ if (IVPU_WA(punit_disabled))
+ return 0;
+
+ ret = d0i3_drive(vdev, true);
+ if (ret)
+ ivpu_err(vdev, "Failed to enable D0i3: %d\n", ret);
+
+ udelay(5); /* VPU requires 5 us to complete the transition */
+
+ return ret;
+}
+
+int ivpu_hw_btrs_d0i3_disable(struct ivpu_device *vdev)
+{
+ int ret;
+
+ if (IVPU_WA(punit_disabled))
+ return 0;
+
+ ret = d0i3_drive(vdev, false);
+ if (ret)
+ ivpu_err(vdev, "Failed to disable D0i3: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_hw_btrs_wait_for_clock_res_own_ack(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return 0;
+
+ return REGB_POLL_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, CLOCK_RESOURCE_OWN_ACK, 1, TIMEOUT_US);
+}
+
+void ivpu_hw_btrs_set_port_arbitration_weights_lnl(struct ivpu_device *vdev)
+{
+ REGB_WR32(VPU_HW_BTRS_LNL_PORT_ARBITRATION_WEIGHTS, WEIGHTS_DEFAULT);
+ REGB_WR32(VPU_HW_BTRS_LNL_PORT_ARBITRATION_WEIGHTS_ATS, WEIGHTS_ATS_DEFAULT);
+}
+
+static int ip_reset_mtl(struct ivpu_device *vdev)
+{
+ int ret;
+ u32 val;
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for TRIGGER bit\n");
+ return ret;
+ }
+
+ val = REGB_RD32(VPU_HW_BTRS_MTL_VPU_IP_RESET);
+ val = REG_SET_FLD(VPU_HW_BTRS_MTL_VPU_IP_RESET, TRIGGER, val);
+ REGB_WR32(VPU_HW_BTRS_MTL_VPU_IP_RESET, val);
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US);
+ if (ret)
+ ivpu_err(vdev, "Timed out waiting for RESET completion\n");
+
+ return ret;
+}
+
+static int ip_reset_lnl(struct ivpu_device *vdev)
+{
+ int ret;
+ u32 val;
+
+ ivpu_hw_btrs_clock_relinquish_disable_lnl(vdev);
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_IP_RESET, TRIGGER, 0, TIMEOUT_US);
+ if (ret) {
+ ivpu_err(vdev, "Wait for *_TRIGGER timed out\n");
+ return ret;
+ }
+
+ val = REGB_RD32(VPU_HW_BTRS_LNL_IP_RESET);
+ val = REG_SET_FLD(VPU_HW_BTRS_LNL_IP_RESET, TRIGGER, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_IP_RESET, val);
+
+ ret = REGB_POLL_FLD(VPU_HW_BTRS_LNL_IP_RESET, TRIGGER, 0, TIMEOUT_US);
+ if (ret)
+ ivpu_err(vdev, "Timed out waiting for RESET completion\n");
+
+ return ret;
+}
+
+int ivpu_hw_btrs_ip_reset(struct ivpu_device *vdev)
+{
+ if (IVPU_WA(punit_disabled))
+ return 0;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return ip_reset_mtl(vdev);
+ else
+ return ip_reset_lnl(vdev);
+}
+
+void ivpu_hw_btrs_profiling_freq_reg_set_lnl(struct ivpu_device *vdev)
+{
+ u32 val = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
+
+ if (vdev->hw->pll.profiling_freq == PLL_PROFILING_FREQ_DEFAULT)
+ val = REG_CLR_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, PERF_CLK, val);
+ else
+ val = REG_SET_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, PERF_CLK, val);
+
+ REGB_WR32(VPU_HW_BTRS_LNL_VPU_STATUS, val);
+}
+
+void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev)
+{
+ ivpu_dbg(vdev, MISC, "Buttress ATS: %s\n",
+ REGB_RD32(VPU_HW_BTRS_LNL_HM_ATS) ? "Enable" : "Disable");
+}
+
+void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev)
+{
+ u32 val = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
+
+ val = REG_SET_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, DISABLE_CLK_RELINQUISH, val);
+ REGB_WR32(VPU_HW_BTRS_LNL_VPU_STATUS, val);
+}
+
+bool ivpu_hw_btrs_is_idle(struct ivpu_device *vdev)
+{
+ u32 val;
+
+ if (IVPU_WA(punit_disabled))
+ return true;
+
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
+ val = REGB_RD32(VPU_HW_BTRS_MTL_VPU_STATUS);
+
+ return REG_TEST_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, READY, val) &&
+ REG_TEST_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, IDLE, val);
+ } else {
+ val = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
+
+ return REG_TEST_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, READY, val) &&
+ REG_TEST_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, IDLE, val);
+ }
+}
+
+int ivpu_hw_btrs_wait_for_idle(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return REGB_POLL_FLD(VPU_HW_BTRS_MTL_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US);
+ else
+ return REGB_POLL_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US);
+}
+
+static u32 pll_config_get_mtl(struct ivpu_device *vdev)
+{
+ return REGB_RD32(VPU_HW_BTRS_MTL_CURRENT_PLL);
+}
+
+static u32 pll_config_get_lnl(struct ivpu_device *vdev)
+{
+ return REGB_RD32(VPU_HW_BTRS_LNL_PLL_FREQ);
+}
+
+static u32 pll_ratio_to_dpu_freq_mtl(u16 ratio)
+{
+ return (PLL_RATIO_TO_FREQ(ratio) * 2) / 3;
+}
+
+static u32 pll_ratio_to_dpu_freq_lnl(u16 ratio)
+{
+ return PLL_RATIO_TO_FREQ(ratio) / 2;
+}
+
+static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return pll_ratio_to_dpu_freq_mtl(ratio);
+ else
+ return pll_ratio_to_dpu_freq_lnl(ratio);
+}
+
+u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev)
+{
+ return pll_ratio_to_dpu_freq(vdev, vdev->hw->pll.max_ratio);
+}
+
+u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return pll_ratio_to_dpu_freq_mtl(pll_config_get_mtl(vdev));
+ else
+ return pll_ratio_to_dpu_freq_lnl(pll_config_get_lnl(vdev));
+}
+
+/* Handler for IRQs from Buttress core (irqB) */
+bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq)
+{
+ u32 status = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_MTL_IRQ_MASK;
+ bool schedule_recovery = false;
+
+ if (!status)
+ return false;
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
+ u32 pll = pll_config_get_mtl(vdev);
+
+ ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz",
+ pll, pll_ratio_to_dpu_freq_mtl(pll) / HZ_PER_MHZ);
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, status)) {
+ ivpu_err(vdev, "ATS_ERR irq 0x%016llx", REGB_RD64(VPU_HW_BTRS_MTL_ATS_ERR_LOG_0));
+ REGB_WR32(VPU_HW_BTRS_MTL_ATS_ERR_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, UFI_ERR, status)) {
+ u32 ufi_log = REGB_RD32(VPU_HW_BTRS_MTL_UFI_ERR_LOG);
+
+ ivpu_err(vdev, "UFI_ERR irq (0x%08x) opcode: 0x%02lx axi_id: 0x%02lx cq_id: 0x%03lx",
+ ufi_log, REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, OPCODE, ufi_log),
+ REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, AXI_ID, ufi_log),
+ REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, CQ_ID, ufi_log));
+ REGB_WR32(VPU_HW_BTRS_MTL_UFI_ERR_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ /* This must be done after interrupts are cleared at the source. */
+ if (IVPU_WA(interrupt_clear_with_0))
+ /*
+ * Writing 1 triggers an interrupt, so we can't perform read update write.
+ * Clear local interrupt status by writing 0 to all bits.
+ */
+ REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, 0x0);
+ else
+ REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, status);
+
+ if (schedule_recovery)
+ ivpu_pm_trigger_recovery(vdev, "Buttress IRQ");
+
+ return true;
+}
+
+/* Handler for IRQs from Buttress core (irqB) */
+bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
+{
+ u32 status = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
+ bool schedule_recovery = false;
+
+ if (!status)
+ return false;
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
+ ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
+ queue_work(system_percpu_wq, &vdev->irq_dct_work);
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
+ u32 pll = pll_config_get_lnl(vdev);
+
+ ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz",
+ pll, pll_ratio_to_dpu_freq_lnl(pll) / HZ_PER_MHZ);
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, status)) {
+ ivpu_err(vdev, "ATS_ERR LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",
+ REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG1),
+ REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG2));
+ REGB_WR32(VPU_HW_BTRS_LNL_ATS_ERR_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI0_ERR, status)) {
+ ivpu_err(vdev, "CFI0_ERR 0x%08x", REGB_RD32(VPU_HW_BTRS_LNL_CFI0_ERR_LOG));
+ REGB_WR32(VPU_HW_BTRS_LNL_CFI0_ERR_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI1_ERR, status)) {
+ ivpu_err(vdev, "CFI1_ERR 0x%08x", REGB_RD32(VPU_HW_BTRS_LNL_CFI1_ERR_LOG));
+ REGB_WR32(VPU_HW_BTRS_LNL_CFI1_ERR_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR0_ERR, status)) {
+ ivpu_err(vdev, "IMR_ERR_CFI0 LOW: 0x%08x HIGH: 0x%08x",
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_LOW),
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_HIGH));
+ REGB_WR32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR, status)) {
+ ivpu_err(vdev, "IMR_ERR_CFI1 LOW: 0x%08x HIGH: 0x%08x",
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_LOW),
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_HIGH));
+ REGB_WR32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_CLEAR, 0x1);
+ schedule_recovery = true;
+ }
+
+ /* This must be done after interrupts are cleared at the source. */
+ REGB_WR32(VPU_HW_BTRS_LNL_INTERRUPT_STAT, status);
+
+ if (schedule_recovery)
+ ivpu_pm_trigger_recovery(vdev, "Buttress IRQ");
+
+ return true;
+}
+
+int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable)
+{
+ u32 val = REGB_RD32(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW);
+ u32 cmd = REG_GET_FLD(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW, CMD, val);
+ u32 param1 = REG_GET_FLD(VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW, PARAM1, val);
+
+ if (cmd != DCT_REQ) {
+ ivpu_err_ratelimited(vdev, "Unsupported PCODE command: 0x%x\n", cmd);
+ return -EBADR;
+ }
+
+ switch (param1) {
+ case DCT_ENABLE:
+ *enable = true;
+ return 0;
+ case DCT_DISABLE:
+ *enable = false;
+ return 0;
+ default:
+ ivpu_err_ratelimited(vdev, "Invalid PARAM1 value: %u\n", param1);
+ return -EINVAL;
+ }
+}
+
+void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u8 active_percent)
+{
+ u32 val = 0;
+ u32 cmd = enable ? DCT_ENABLE : DCT_DISABLE;
+
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, CMD, DCT_REQ, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, PARAM1, cmd, val);
+ val = REG_SET_FLD_NUM(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, PARAM2, active_percent, val);
+
+ REGB_WR32(VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS, val);
+}
+
+u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return REGB_RD32(VPU_HW_BTRS_MTL_VPU_TELEMETRY_OFFSET);
+ else
+ return REGB_RD32(VPU_HW_BTRS_LNL_VPU_TELEMETRY_OFFSET);
+}
+
+u32 ivpu_hw_btrs_telemetry_size_get(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return REGB_RD32(VPU_HW_BTRS_MTL_VPU_TELEMETRY_SIZE);
+ else
+ return REGB_RD32(VPU_HW_BTRS_LNL_VPU_TELEMETRY_SIZE);
+}
+
+u32 ivpu_hw_btrs_telemetry_enable_get(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return REGB_RD32(VPU_HW_BTRS_MTL_VPU_TELEMETRY_ENABLE);
+ else
+ return REGB_RD32(VPU_HW_BTRS_LNL_VPU_TELEMETRY_ENABLE);
+}
+
+void ivpu_hw_btrs_global_int_disable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x1);
+ else
+ REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x1);
+}
+
+void ivpu_hw_btrs_global_int_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x0);
+ else
+ REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x0);
+}
+
+void ivpu_hw_btrs_irq_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
+ REGB_WR32(VPU_HW_BTRS_MTL_LOCAL_INT_MASK, (u32)(~BTRS_MTL_IRQ_MASK));
+ REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x0);
+ } else {
+ REGB_WR32(VPU_HW_BTRS_LNL_LOCAL_INT_MASK, (u32)(~BTRS_LNL_IRQ_MASK));
+ REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x0);
+ }
+}
+
+void ivpu_hw_btrs_irq_disable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) {
+ REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x1);
+ REGB_WR32(VPU_HW_BTRS_MTL_LOCAL_INT_MASK, BTRS_IRQ_DISABLE_MASK);
+ } else {
+ REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x1);
+ REGB_WR32(VPU_HW_BTRS_LNL_LOCAL_INT_MASK, BTRS_IRQ_DISABLE_MASK);
+ }
+}
+
+static void diagnose_failure_mtl(struct ivpu_device *vdev)
+{
+ u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_MTL_IRQ_MASK;
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, reg))
+ ivpu_err(vdev, "ATS_ERR irq 0x%016llx", REGB_RD64(VPU_HW_BTRS_MTL_ATS_ERR_LOG_0));
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, UFI_ERR, reg)) {
+ u32 log = REGB_RD32(VPU_HW_BTRS_MTL_UFI_ERR_LOG);
+
+ ivpu_err(vdev, "UFI_ERR irq (0x%08x) opcode: 0x%02lx axi_id: 0x%02lx cq_id: 0x%03lx",
+ log, REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, OPCODE, log),
+ REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, AXI_ID, log),
+ REG_GET_FLD(VPU_HW_BTRS_MTL_UFI_ERR_LOG, CQ_ID, log));
+ }
+}
+
+static void diagnose_failure_lnl(struct ivpu_device *vdev)
+{
+ u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) {
+ ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",
+ REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG1),
+ REGB_RD32(VPU_HW_BTRS_LNL_ATS_ERR_LOG2));
+ }
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI0_ERR, reg))
+ ivpu_err(vdev, "CFI0_ERR_LOG 0x%08x\n", REGB_RD32(VPU_HW_BTRS_LNL_CFI0_ERR_LOG));
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, CFI1_ERR, reg))
+ ivpu_err(vdev, "CFI1_ERR_LOG 0x%08x\n", REGB_RD32(VPU_HW_BTRS_LNL_CFI1_ERR_LOG));
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR0_ERR, reg))
+ ivpu_err(vdev, "IMR_ERR_CFI0 LOW: 0x%08x HIGH: 0x%08x\n",
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_LOW),
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI0_HIGH));
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR, reg))
+ ivpu_err(vdev, "IMR_ERR_CFI1 LOW: 0x%08x HIGH: 0x%08x\n",
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_LOW),
+ REGB_RD32(VPU_HW_BTRS_LNL_IMR_ERR_CFI1_HIGH));
+
+ if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, reg))
+ ivpu_err(vdev, "Survivability IRQ\n");
+}
+
+void ivpu_hw_btrs_diagnose_failure(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return diagnose_failure_mtl(vdev);
+ else
+ return diagnose_failure_lnl(vdev);
+}
+
+int ivpu_hw_btrs_platform_read(struct ivpu_device *vdev)
+{
+ u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
+
+ return REG_GET_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, PLATFORM, reg);
+}
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.h b/drivers/accel/ivpu/ivpu_hw_btrs.h
new file mode 100644
index 000000000000..c4c10e22f30f
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_btrs.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_BTRS_H__
+#define __IVPU_HW_BTRS_H__
+
+#include "ivpu_drv.h"
+#include "ivpu_hw_37xx_reg.h"
+#include "ivpu_hw_40xx_reg.h"
+#include "ivpu_hw_reg_io.h"
+
+#define PLL_PROFILING_FREQ_DEFAULT 38400000
+#define PLL_PROFILING_FREQ_HIGH 400000000
+
+#define DCT_DEFAULT_ACTIVE_PERCENT 30u
+#define DCT_PERIOD_US 35300u
+
+int ivpu_hw_btrs_info_init(struct ivpu_device *vdev);
+void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev);
+int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev);
+int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable);
+int ivpu_hw_btrs_wait_for_clock_res_own_ack(struct ivpu_device *vdev);
+int ivpu_hw_btrs_d0i3_enable(struct ivpu_device *vdev);
+int ivpu_hw_btrs_d0i3_disable(struct ivpu_device *vdev);
+void ivpu_hw_btrs_set_port_arbitration_weights_lnl(struct ivpu_device *vdev);
+bool ivpu_hw_btrs_is_idle(struct ivpu_device *vdev);
+int ivpu_hw_btrs_wait_for_idle(struct ivpu_device *vdev);
+int ivpu_hw_btrs_ip_reset(struct ivpu_device *vdev);
+void ivpu_hw_btrs_profiling_freq_reg_set_lnl(struct ivpu_device *vdev);
+void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev);
+void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev);
+u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev);
+u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev);
+bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq);
+bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq);
+int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);
+void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u8 active_percent);
+u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev);
+u32 ivpu_hw_btrs_telemetry_size_get(struct ivpu_device *vdev);
+u32 ivpu_hw_btrs_telemetry_enable_get(struct ivpu_device *vdev);
+void ivpu_hw_btrs_global_int_enable(struct ivpu_device *vdev);
+void ivpu_hw_btrs_global_int_disable(struct ivpu_device *vdev);
+void ivpu_hw_btrs_irq_enable(struct ivpu_device *vdev);
+void ivpu_hw_btrs_irq_disable(struct ivpu_device *vdev);
+void ivpu_hw_btrs_diagnose_failure(struct ivpu_device *vdev);
+int ivpu_hw_btrs_platform_read(struct ivpu_device *vdev);
+
+#endif /* __IVPU_HW_BTRS_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h b/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h
new file mode 100644
index 000000000000..a81a9ba540fa
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_BTRS_LNL_REG_H__
+#define __IVPU_HW_BTRS_LNL_REG_H__
+
+#include <linux/bits.h>
+
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT 0x00000000u
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_FREQ_CHANGE_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_ATS_ERR_MASK BIT_MASK(1)
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_CFI0_ERR_MASK BIT_MASK(2)
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_CFI1_ERR_MASK BIT_MASK(3)
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_IMR0_ERR_MASK BIT_MASK(4)
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_IMR1_ERR_MASK BIT_MASK(5)
+#define VPU_HW_BTRS_LNL_INTERRUPT_STAT_SURV_ERR_MASK BIT_MASK(6)
+
+#define VPU_HW_BTRS_LNL_LOCAL_INT_MASK 0x00000004u
+#define VPU_HW_BTRS_LNL_GLOBAL_INT_MASK 0x00000008u
+
+#define VPU_HW_BTRS_LNL_HM_ATS 0x0000000cu
+
+#define VPU_HW_BTRS_LNL_ATS_ERR_LOG1 0x00000010u
+#define VPU_HW_BTRS_LNL_ATS_ERR_LOG2 0x00000014u
+#define VPU_HW_BTRS_LNL_ATS_ERR_CLEAR 0x00000018u
+
+#define VPU_HW_BTRS_LNL_CFI0_ERR_LOG 0x0000001cu
+#define VPU_HW_BTRS_LNL_CFI0_ERR_CLEAR 0x00000020u
+
+#define VPU_HW_BTRS_LNL_PORT_ARBITRATION_WEIGHTS_ATS 0x00000024u
+
+#define VPU_HW_BTRS_LNL_CFI1_ERR_LOG 0x00000040u
+#define VPU_HW_BTRS_LNL_CFI1_ERR_CLEAR 0x00000044u
+
+#define VPU_HW_BTRS_LNL_IMR_ERR_CFI0_LOW 0x00000048u
+#define VPU_HW_BTRS_LNL_IMR_ERR_CFI0_HIGH 0x0000004cu
+#define VPU_HW_BTRS_LNL_IMR_ERR_CFI0_CLEAR 0x00000050u
+
+#define VPU_HW_BTRS_LNL_PORT_ARBITRATION_WEIGHTS 0x00000054u
+
+#define VPU_HW_BTRS_LNL_IMR_ERR_CFI1_LOW 0x00000058u
+#define VPU_HW_BTRS_LNL_IMR_ERR_CFI1_HIGH 0x0000005cu
+#define VPU_HW_BTRS_LNL_IMR_ERR_CFI1_CLEAR 0x00000060u
+
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS 0x00000070u
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_CMD_MASK GENMASK(7, 0)
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_PARAM1_MASK GENMASK(15, 8)
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_PARAM2_MASK GENMASK(23, 16)
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_STATUS_PARAM3_MASK GENMASK(31, 24)
+
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW 0x00000074u
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW_CMD_MASK GENMASK(7, 0)
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW_PARAM1_MASK GENMASK(15, 8)
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW_PARAM2_MASK GENMASK(23, 16)
+#define VPU_HW_BTRS_LNL_PCODE_MAILBOX_SHADOW_PARAM3_MASK GENMASK(31, 24)
+
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0 0x00000130u
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0_MIN_RATIO_MASK GENMASK(15, 0)
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD0_MAX_RATIO_MASK GENMASK(31, 16)
+
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1 0x00000134u
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1_TARGET_RATIO_MASK GENMASK(15, 0)
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD1_EPP_MASK GENMASK(31, 16)
+
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2 0x00000138u
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2_CONFIG_MASK GENMASK(15, 0)
+#define VPU_HW_BTRS_LNL_WP_REQ_PAYLOAD2_CDYN_MASK GENMASK(31, 16)
+
+#define VPU_HW_BTRS_LNL_WP_REQ_CMD 0x0000013cu
+#define VPU_HW_BTRS_LNL_WP_REQ_CMD_SEND_MASK BIT_MASK(0)
+
+#define VPU_HW_BTRS_LNL_PLL_FREQ 0x00000148u
+#define VPU_HW_BTRS_LNL_PLL_FREQ_RATIO_MASK GENMASK(15, 0)
+
+#define VPU_HW_BTRS_LNL_CDYN 0x0000014cu
+#define VPU_HW_BTRS_LNL_CDYN_CDYN_MASK GENMASK(15, 0)
+
+#define VPU_HW_BTRS_LNL_TILE_FUSE 0x00000150u
+#define VPU_HW_BTRS_LNL_TILE_FUSE_VALID_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_LNL_TILE_FUSE_CONFIG_MASK GENMASK(6, 1)
+
+#define VPU_HW_BTRS_LNL_VPU_STATUS 0x00000154u
+#define VPU_HW_BTRS_LNL_VPU_STATUS_READY_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_IDLE_MASK BIT_MASK(1)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_DUP_IDLE_MASK BIT_MASK(2)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_CLOCK_RESOURCE_OWN_ACK_MASK BIT_MASK(6)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_POWER_RESOURCE_OWN_ACK_MASK BIT_MASK(7)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_PERF_CLK_MASK BIT_MASK(11)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_DISABLE_CLK_RELINQUISH_MASK BIT_MASK(12)
+#define VPU_HW_BTRS_LNL_VPU_STATUS_PLATFORM_MASK GENMASK(31, 29)
+
+#define VPU_HW_BTRS_LNL_IP_RESET 0x00000160u
+#define VPU_HW_BTRS_LNL_IP_RESET_TRIGGER_MASK BIT_MASK(0)
+
+#define VPU_HW_BTRS_LNL_D0I3_CONTROL 0x00000164u
+#define VPU_HW_BTRS_LNL_D0I3_CONTROL_INPROGRESS_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_LNL_D0I3_CONTROL_I3_MASK BIT_MASK(2)
+
+#define VPU_HW_BTRS_LNL_VPU_TELEMETRY_OFFSET 0x00000168u
+#define VPU_HW_BTRS_LNL_VPU_TELEMETRY_SIZE 0x0000016cu
+#define VPU_HW_BTRS_LNL_VPU_TELEMETRY_ENABLE 0x00000170u
+
+#define VPU_HW_BTRS_LNL_FMIN_FUSE 0x00000174u
+#define VPU_HW_BTRS_LNL_FMIN_FUSE_MIN_RATIO_MASK GENMASK(7, 0)
+#define VPU_HW_BTRS_LNL_FMIN_FUSE_PN_RATIO_MASK GENMASK(15, 8)
+
+#define VPU_HW_BTRS_LNL_FMAX_FUSE 0x00000178u
+#define VPU_HW_BTRS_LNL_FMAX_FUSE_MAX_RATIO_MASK GENMASK(7, 0)
+
+#endif /* __IVPU_HW_BTRS_LNL_REG_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h b/drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h
new file mode 100644
index 000000000000..e93d539e066f
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_BTRS_MTL_REG_H__
+#define __IVPU_HW_BTRS_MTL_REG_H__
+
+#include <linux/bits.h>
+
+#define VPU_HW_BTRS_MTL_INTERRUPT_TYPE 0x00000000u
+
+#define VPU_HW_BTRS_MTL_INTERRUPT_STAT 0x00000004u
+#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_FREQ_CHANGE_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_ATS_ERR_MASK BIT_MASK(1)
+#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_UFI_ERR_MASK BIT_MASK(2)
+
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0 0x00000008u
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0_MIN_RATIO_MASK GENMASK(15, 0)
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0_MAX_RATIO_MASK GENMASK(31, 16)
+
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1 0x0000000cu
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1_TARGET_RATIO_MASK GENMASK(15, 0)
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1_EPP_MASK GENMASK(31, 16)
+
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2 0x00000010u
+#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2_CONFIG_MASK GENMASK(15, 0)
+
+#define VPU_HW_BTRS_MTL_WP_REQ_CMD 0x00000014u
+#define VPU_HW_BTRS_MTL_WP_REQ_CMD_SEND_MASK BIT_MASK(0)
+
+#define VPU_HW_BTRS_MTL_WP_DOWNLOAD 0x00000018u
+#define VPU_HW_BTRS_MTL_WP_DOWNLOAD_TARGET_RATIO_MASK GENMASK(15, 0)
+
+#define VPU_HW_BTRS_MTL_CURRENT_PLL 0x0000001cu
+#define VPU_HW_BTRS_MTL_CURRENT_PLL_RATIO_MASK GENMASK(15, 0)
+
+#define VPU_HW_BTRS_MTL_PLL_ENABLE 0x00000020u
+
+#define VPU_HW_BTRS_MTL_FMIN_FUSE 0x00000024u
+#define VPU_HW_BTRS_MTL_FMIN_FUSE_MIN_RATIO_MASK GENMASK(7, 0)
+#define VPU_HW_BTRS_MTL_FMIN_FUSE_PN_RATIO_MASK GENMASK(15, 8)
+
+#define VPU_HW_BTRS_MTL_FMAX_FUSE 0x00000028u
+#define VPU_HW_BTRS_MTL_FMAX_FUSE_MAX_RATIO_MASK GENMASK(7, 0)
+
+#define VPU_HW_BTRS_MTL_TILE_FUSE 0x0000002cu
+#define VPU_HW_BTRS_MTL_TILE_FUSE_VALID_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_MTL_TILE_FUSE_SKU_MASK GENMASK(3, 2)
+
+#define VPU_HW_BTRS_MTL_LOCAL_INT_MASK 0x00000030u
+#define VPU_HW_BTRS_MTL_GLOBAL_INT_MASK 0x00000034u
+
+#define VPU_HW_BTRS_MTL_PLL_STATUS 0x00000040u
+#define VPU_HW_BTRS_MTL_PLL_STATUS_LOCK_MASK BIT_MASK(1)
+
+#define VPU_HW_BTRS_MTL_VPU_STATUS 0x00000044u
+#define VPU_HW_BTRS_MTL_VPU_STATUS_READY_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_MTL_VPU_STATUS_IDLE_MASK BIT_MASK(1)
+
+#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL 0x00000060u
+#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL_INPROGRESS_MASK BIT_MASK(0)
+#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL_I3_MASK BIT_MASK(2)
+
+#define VPU_HW_BTRS_MTL_VPU_IP_RESET 0x00000050u
+#define VPU_HW_BTRS_MTL_VPU_IP_RESET_TRIGGER_MASK BIT_MASK(0)
+
+#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_OFFSET 0x00000080u
+#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_SIZE 0x00000084u
+#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_ENABLE 0x00000088u
+
+#define VPU_HW_BTRS_MTL_ATS_ERR_LOG_0 0x000000a0u
+#define VPU_HW_BTRS_MTL_ATS_ERR_LOG_1 0x000000a4u
+#define VPU_HW_BTRS_MTL_ATS_ERR_CLEAR 0x000000a8u
+
+#define VPU_HW_BTRS_MTL_UFI_ERR_LOG 0x000000b0u
+#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_CQ_ID_MASK GENMASK(11, 0)
+#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_AXI_ID_MASK GENMASK(19, 12)
+#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_OPCODE_MASK GENMASK(24, 20)
+
+#define VPU_HW_BTRS_MTL_UFI_ERR_CLEAR 0x000000b4u
+
+#endif /* __IVPU_HW_BTRS_MTL_REG_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c
new file mode 100644
index 000000000000..06aa1e7dc50b
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_ip.c
@@ -0,0 +1,1199 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_hw.h"
+#include "ivpu_hw_37xx_reg.h"
+#include "ivpu_hw_40xx_reg.h"
+#include "ivpu_hw_btrs.h"
+#include "ivpu_hw_ip.h"
+#include "ivpu_hw_reg_io.h"
+#include "ivpu_mmu.h"
+#include "ivpu_pm.h"
+
+#define PWR_ISLAND_STATUS_TIMEOUT_US (5 * USEC_PER_MSEC)
+
+#define TIM_SAFE_ENABLE 0xf1d0dead
+#define TIM_WATCHDOG_RESET_VALUE 0xffffffff
+
+#define ICB_0_IRQ_MASK_37XX ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT)))
+
+#define ICB_1_IRQ_MASK_37XX ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_2_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_3_INT)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_4_INT)))
+
+#define ICB_0_1_IRQ_MASK_37XX ((((u64)ICB_1_IRQ_MASK_37XX) << 32) | ICB_0_IRQ_MASK_37XX)
+
+#define ICB_0_IRQ_MASK_40XX ((REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT)))
+
+#define ICB_1_IRQ_MASK_40XX ((REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_2_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_3_INT)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_4_INT)))
+
+#define ICB_0_1_IRQ_MASK_40XX ((((u64)ICB_1_IRQ_MASK_40XX) << 32) | ICB_0_IRQ_MASK_40XX)
+
+#define ITF_FIREWALL_VIOLATION_MASK_37XX ((REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_ROM_CMX)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_DBG)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_CTRL)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, DEC400)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_NCE)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI)) | \
+ (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI_CMX)))
+
+#define ITF_FIREWALL_VIOLATION_MASK_40XX ((REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, CSS_ROM_CMX)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, CSS_DBG)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, CSS_CTRL)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, DEC400)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, MSS_NCE)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI)) | \
+ (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI_CMX)))
+
+static int wait_for_ip_bar(struct ivpu_device *vdev)
+{
+ return REGV_POLL_FLD(VPU_37XX_HOST_SS_CPR_RST_CLR, AON, 0, 100);
+}
+
+static void host_ss_rst_clr(struct ivpu_device *vdev)
+{
+ u32 val = 0;
+
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_RST_CLR, TOP_NOC, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_RST_CLR, DSS_MAS, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_RST_CLR, MSS_MAS, val);
+
+ REGV_WR32(VPU_37XX_HOST_SS_CPR_RST_CLR, val);
+}
+
+static int host_ss_noc_qreqn_check_37xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QREQN);
+
+ if (!REG_TEST_FLD_NUM(VPU_37XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int host_ss_noc_qreqn_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QREQN);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int host_ss_noc_qreqn_check(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return host_ss_noc_qreqn_check_37xx(vdev, exp_val);
+ else
+ return host_ss_noc_qreqn_check_40xx(vdev, exp_val);
+}
+
+static int host_ss_noc_qacceptn_check_37xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QACCEPTN);
+
+ if (!REG_TEST_FLD_NUM(VPU_37XX_HOST_SS_NOC_QACCEPTN, TOP_SOCMMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int host_ss_noc_qacceptn_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QACCEPTN);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_HOST_SS_NOC_QACCEPTN, TOP_SOCMMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int host_ss_noc_qacceptn_check(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return host_ss_noc_qacceptn_check_37xx(vdev, exp_val);
+ else
+ return host_ss_noc_qacceptn_check_40xx(vdev, exp_val);
+}
+
+static int host_ss_noc_qdeny_check_37xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QDENY);
+
+ if (!REG_TEST_FLD_NUM(VPU_37XX_HOST_SS_NOC_QDENY, TOP_SOCMMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int host_ss_noc_qdeny_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QDENY);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_HOST_SS_NOC_QDENY, TOP_SOCMMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int host_ss_noc_qdeny_check(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return host_ss_noc_qdeny_check_37xx(vdev, exp_val);
+ else
+ return host_ss_noc_qdeny_check_40xx(vdev, exp_val);
+}
+
+static int top_noc_qrenqn_check_37xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QREQN);
+
+ if (!REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QREQN, CPU_CTRL, exp_val, val) ||
+ !REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int top_noc_qrenqn_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QREQN);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QREQN, CPU_CTRL, exp_val, val) ||
+ !REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int top_noc_qreqn_check(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return top_noc_qrenqn_check_37xx(vdev, exp_val);
+ else
+ return top_noc_qrenqn_check_40xx(vdev, exp_val);
+}
+
+int ivpu_hw_ip_host_ss_configure(struct ivpu_device *vdev)
+{
+ int ret;
+
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ ret = wait_for_ip_bar(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for NPU IP bar\n");
+ return ret;
+ }
+ host_ss_rst_clr(vdev);
+ }
+
+ ret = host_ss_noc_qreqn_check(vdev, 0x0);
+ if (ret) {
+ ivpu_err(vdev, "Failed qreqn check: %d\n", ret);
+ return ret;
+ }
+
+ ret = host_ss_noc_qacceptn_check(vdev, 0x0);
+ if (ret) {
+ ivpu_err(vdev, "Failed qacceptn check: %d\n", ret);
+ return ret;
+ }
+
+ ret = host_ss_noc_qdeny_check(vdev, 0x0);
+ if (ret)
+ ivpu_err(vdev, "Failed qdeny check %d\n", ret);
+
+ return ret;
+}
+
+static void idle_gen_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN, EN, val);
+ else
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN, EN, val);
+
+ REGV_WR32(VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN, val);
+}
+
+static void idle_gen_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_IDLE_GEN);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_IDLE_GEN, EN, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_IDLE_GEN, EN, val);
+
+ REGV_WR32(VPU_40XX_HOST_SS_AON_IDLE_GEN, val);
+}
+
+void ivpu_hw_ip_idle_gen_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ idle_gen_drive_37xx(vdev, true);
+ else
+ idle_gen_drive_40xx(vdev, true);
+}
+
+void ivpu_hw_ip_idle_gen_disable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ idle_gen_drive_37xx(vdev, false);
+ else
+ idle_gen_drive_40xx(vdev, false);
+}
+
+static void
+pwr_island_delay_set_50xx(struct ivpu_device *vdev, u32 post, u32 post1, u32 post2, u32 status)
+{
+ u32 val;
+
+ val = REGV_RD32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY);
+ val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST_DLY, post, val);
+ val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST1_DLY, post1, val);
+ val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST2_DLY, post2, val);
+ REGV_WR32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, val);
+
+ val = REGV_RD32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY);
+ val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY, STATUS_DLY, status, val);
+ REGV_WR32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY, val);
+}
+
+static void pwr_island_trickle_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, MSS_CPU, val);
+ else
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, MSS_CPU, val);
+
+ REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, val);
+}
+
+static void pwr_island_trickle_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, CSS_CPU, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, CSS_CPU, val);
+
+ REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, val);
+}
+
+static void pwr_island_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
+
+ REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
+}
+
+static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, MSS_CPU, val);
+ else
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, MSS_CPU, val);
+
+ REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
+}
+
+static void pwr_island_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ pwr_island_trickle_drive_37xx(vdev, true);
+ ndelay(500);
+ pwr_island_drive_37xx(vdev, true);
+ } else {
+ pwr_island_trickle_drive_40xx(vdev, true);
+ ndelay(500);
+ pwr_island_drive_40xx(vdev, true);
+ }
+}
+
+static int wait_for_pwr_island_status(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (IVPU_WA(punit_disabled))
+ return 0;
+
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return REGV_POLL_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0, MSS_CPU, exp_val,
+ PWR_ISLAND_STATUS_TIMEOUT_US);
+ else
+ return REGV_POLL_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_STATUS0, CSS_CPU, exp_val,
+ PWR_ISLAND_STATUS_TIMEOUT_US);
+}
+
+static void pwr_island_isolation_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISO_EN0);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISO_EN0, MSS_CPU, val);
+ else
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISO_EN0, MSS_CPU, val);
+
+ REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISO_EN0, val);
+}
+
+static void pwr_island_isolation_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0, CSS_CPU, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0, CSS_CPU, val);
+
+ REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0, val);
+}
+
+static void pwr_island_isolation_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ pwr_island_isolation_drive_37xx(vdev, enable);
+ else
+ pwr_island_isolation_drive_40xx(vdev, enable);
+}
+
+static void pwr_island_isolation_disable(struct ivpu_device *vdev)
+{
+ pwr_island_isolation_drive(vdev, false);
+}
+
+static void host_ss_clk_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_CPR_CLK_SET);
+
+ if (enable) {
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_CLK_SET, TOP_NOC, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_CLK_SET, DSS_MAS, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_CLK_SET, MSS_MAS, val);
+ } else {
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_CPR_CLK_SET, TOP_NOC, val);
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_CPR_CLK_SET, DSS_MAS, val);
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_CPR_CLK_SET, MSS_MAS, val);
+ }
+
+ REGV_WR32(VPU_37XX_HOST_SS_CPR_CLK_SET, val);
+}
+
+static void host_ss_clk_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_CPR_CLK_EN);
+
+ if (enable) {
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_CPR_CLK_EN, TOP_NOC, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_CPR_CLK_EN, DSS_MAS, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_CPR_CLK_EN, CSS_MAS, val);
+ } else {
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_CPR_CLK_EN, TOP_NOC, val);
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_CPR_CLK_EN, DSS_MAS, val);
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_CPR_CLK_EN, CSS_MAS, val);
+ }
+
+ REGV_WR32(VPU_40XX_HOST_SS_CPR_CLK_EN, val);
+}
+
+static void host_ss_clk_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ host_ss_clk_drive_37xx(vdev, enable);
+ else
+ host_ss_clk_drive_40xx(vdev, enable);
+}
+
+static void host_ss_clk_enable(struct ivpu_device *vdev)
+{
+ host_ss_clk_drive(vdev, true);
+}
+
+static void host_ss_rst_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_CPR_RST_SET);
+
+ if (enable) {
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_RST_SET, TOP_NOC, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_RST_SET, DSS_MAS, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_CPR_RST_SET, MSS_MAS, val);
+ } else {
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_CPR_RST_SET, TOP_NOC, val);
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_CPR_RST_SET, DSS_MAS, val);
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_CPR_RST_SET, MSS_MAS, val);
+ }
+
+ REGV_WR32(VPU_37XX_HOST_SS_CPR_RST_SET, val);
+}
+
+static void host_ss_rst_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_CPR_RST_EN);
+
+ if (enable) {
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_CPR_RST_EN, TOP_NOC, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_CPR_RST_EN, DSS_MAS, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_CPR_RST_EN, CSS_MAS, val);
+ } else {
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_CPR_RST_EN, TOP_NOC, val);
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_CPR_RST_EN, DSS_MAS, val);
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_CPR_RST_EN, CSS_MAS, val);
+ }
+
+ REGV_WR32(VPU_40XX_HOST_SS_CPR_RST_EN, val);
+}
+
+static void host_ss_rst_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ host_ss_rst_drive_37xx(vdev, enable);
+ else
+ host_ss_rst_drive_40xx(vdev, enable);
+}
+
+static void host_ss_rst_enable(struct ivpu_device *vdev)
+{
+ host_ss_rst_drive(vdev, true);
+}
+
+static void host_ss_noc_qreqn_top_socmmio_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QREQN);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, val);
+ else
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, val);
+ REGV_WR32(VPU_37XX_HOST_SS_NOC_QREQN, val);
+}
+
+static void host_ss_noc_qreqn_top_socmmio_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QREQN);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, val);
+ REGV_WR32(VPU_40XX_HOST_SS_NOC_QREQN, val);
+}
+
+static void host_ss_noc_qreqn_top_socmmio_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ host_ss_noc_qreqn_top_socmmio_drive_37xx(vdev, enable);
+ else
+ host_ss_noc_qreqn_top_socmmio_drive_40xx(vdev, enable);
+}
+
+static int host_ss_axi_drive(struct ivpu_device *vdev, bool enable)
+{
+ int ret;
+
+ host_ss_noc_qreqn_top_socmmio_drive(vdev, enable);
+
+ ret = host_ss_noc_qacceptn_check(vdev, enable ? 0x1 : 0x0);
+ if (ret) {
+ ivpu_err(vdev, "Failed HOST SS NOC QACCEPTN check: %d\n", ret);
+ return ret;
+ }
+
+ ret = host_ss_noc_qdeny_check(vdev, 0x0);
+ if (ret)
+ ivpu_err(vdev, "Failed HOST SS NOC QDENY check: %d\n", ret);
+
+ return ret;
+}
+
+static void top_noc_qreqn_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QREQN);
+
+ if (enable) {
+ val = REG_SET_FLD(VPU_40XX_TOP_NOC_QREQN, CPU_CTRL, val);
+ val = REG_SET_FLD(VPU_40XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, val);
+ } else {
+ val = REG_CLR_FLD(VPU_40XX_TOP_NOC_QREQN, CPU_CTRL, val);
+ val = REG_CLR_FLD(VPU_40XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, val);
+ }
+
+ REGV_WR32(VPU_40XX_TOP_NOC_QREQN, val);
+}
+
+static void top_noc_qreqn_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QREQN);
+
+ if (enable) {
+ val = REG_SET_FLD(VPU_37XX_TOP_NOC_QREQN, CPU_CTRL, val);
+ val = REG_SET_FLD(VPU_37XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, val);
+ } else {
+ val = REG_CLR_FLD(VPU_37XX_TOP_NOC_QREQN, CPU_CTRL, val);
+ val = REG_CLR_FLD(VPU_37XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, val);
+ }
+
+ REGV_WR32(VPU_37XX_TOP_NOC_QREQN, val);
+}
+
+static void top_noc_qreqn_drive(struct ivpu_device *vdev, bool enable)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ top_noc_qreqn_drive_37xx(vdev, enable);
+ else
+ top_noc_qreqn_drive_40xx(vdev, enable);
+}
+
+int ivpu_hw_ip_host_ss_axi_enable(struct ivpu_device *vdev)
+{
+ return host_ss_axi_drive(vdev, true);
+}
+
+static int top_noc_qacceptn_check_37xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QACCEPTN);
+
+ if (!REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QACCEPTN, CPU_CTRL, exp_val, val) ||
+ !REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QACCEPTN, HOSTIF_L2CACHE, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int top_noc_qacceptn_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QACCEPTN);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QACCEPTN, CPU_CTRL, exp_val, val) ||
+ !REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QACCEPTN, HOSTIF_L2CACHE, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int top_noc_qacceptn_check(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return top_noc_qacceptn_check_37xx(vdev, exp_val);
+ else
+ return top_noc_qacceptn_check_40xx(vdev, exp_val);
+}
+
+static int top_noc_qdeny_check_37xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QDENY);
+
+ if (!REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QDENY, CPU_CTRL, exp_val, val) ||
+ !REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QDENY, HOSTIF_L2CACHE, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int top_noc_qdeny_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QDENY);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QDENY, CPU_CTRL, exp_val, val) ||
+ !REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QDENY, HOSTIF_L2CACHE, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int top_noc_qdeny_check(struct ivpu_device *vdev, u32 exp_val)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return top_noc_qdeny_check_37xx(vdev, exp_val);
+ else
+ return top_noc_qdeny_check_40xx(vdev, exp_val);
+}
+
+static int top_noc_drive(struct ivpu_device *vdev, bool enable)
+{
+ int ret;
+
+ top_noc_qreqn_drive(vdev, enable);
+
+ ret = top_noc_qacceptn_check(vdev, enable ? 0x1 : 0x0);
+ if (ret) {
+ ivpu_err(vdev, "Failed TOP NOC QACCEPTN check: %d\n", ret);
+ return ret;
+ }
+
+ ret = top_noc_qdeny_check(vdev, 0x0);
+ if (ret)
+ ivpu_err(vdev, "Failed TOP NOC QDENY check: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_hw_ip_top_noc_enable(struct ivpu_device *vdev)
+{
+ return top_noc_drive(vdev, true);
+}
+
+static void dpu_active_drive_37xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_DPU_ACTIVE);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_DPU_ACTIVE, DPU_ACTIVE, val);
+ else
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_DPU_ACTIVE, DPU_ACTIVE, val);
+
+ REGV_WR32(VPU_37XX_HOST_SS_AON_DPU_ACTIVE, val);
+}
+
+static void pwr_island_delay_set(struct ivpu_device *vdev)
+{
+ bool high = vdev->hw->pll.profiling_freq == PLL_PROFILING_FREQ_HIGH;
+ u32 post, post1, post2, status;
+
+ if (ivpu_hw_ip_gen(vdev) < IVPU_HW_IP_50XX)
+ return;
+
+ switch (ivpu_device_id(vdev)) {
+ case PCI_DEVICE_ID_WCL:
+ case PCI_DEVICE_ID_PTL_P:
+ post = high ? 18 : 0;
+ post1 = 0;
+ post2 = 0;
+ status = high ? 46 : 3;
+ break;
+
+ case PCI_DEVICE_ID_NVL:
+ post = high ? 198 : 17;
+ post1 = 0;
+ post2 = high ? 198 : 17;
+ status = 0;
+ break;
+
+ default:
+ dump_stack();
+ ivpu_err(vdev, "Unknown device ID\n");
+ return;
+ }
+
+ pwr_island_delay_set_50xx(vdev, post, post1, post2, status);
+}
+
+int ivpu_hw_ip_pwr_domain_enable(struct ivpu_device *vdev)
+{
+ int ret;
+
+ pwr_island_delay_set(vdev);
+ pwr_island_enable(vdev);
+
+ ret = wait_for_pwr_island_status(vdev, 0x1);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for power island status\n");
+ return ret;
+ }
+
+ ret = top_noc_qreqn_check(vdev, 0x0);
+ if (ret) {
+ ivpu_err(vdev, "Failed TOP NOC QREQN check %d\n", ret);
+ return ret;
+ }
+
+ host_ss_clk_enable(vdev);
+ pwr_island_isolation_disable(vdev);
+ host_ss_rst_enable(vdev);
+
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ dpu_active_drive_37xx(vdev, true);
+
+ return ret;
+}
+
+u64 ivpu_hw_ip_read_perf_timer_counter(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return REGV_RD64(VPU_37XX_CPU_SS_TIM_PERF_FREE_CNT);
+ else
+ return REGV_RD64(VPU_40XX_CPU_SS_TIM_PERF_EXT_FREE_CNT);
+}
+
+static void ivpu_hw_ip_snoop_disable_37xx(struct ivpu_device *vdev)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES);
+
+ val = REG_SET_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, NOSNOOP_OVERRIDE_EN, val);
+ val = REG_CLR_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, AW_NOSNOOP_OVERRIDE, val);
+
+ if (ivpu_is_force_snoop_enabled(vdev))
+ val = REG_CLR_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, AR_NOSNOOP_OVERRIDE, val);
+ else
+ val = REG_SET_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, AR_NOSNOOP_OVERRIDE, val);
+
+ REGV_WR32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, val);
+}
+
+static void ivpu_hw_ip_snoop_disable_40xx(struct ivpu_device *vdev)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES);
+
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, SNOOP_OVERRIDE_EN, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AW_SNOOP_OVERRIDE, val);
+
+ if (ivpu_is_force_snoop_enabled(vdev))
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AR_SNOOP_OVERRIDE, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AR_SNOOP_OVERRIDE, val);
+
+ REGV_WR32(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, val);
+}
+
+void ivpu_hw_ip_snoop_disable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return ivpu_hw_ip_snoop_disable_37xx(vdev);
+ else
+ return ivpu_hw_ip_snoop_disable_40xx(vdev);
+}
+
+static void ivpu_hw_ip_tbu_mmu_enable_37xx(struct ivpu_device *vdev)
+{
+ u32 val = REGV_RD32(VPU_37XX_HOST_IF_TBU_MMUSSIDV);
+
+ val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val);
+
+ REGV_WR32(VPU_37XX_HOST_IF_TBU_MMUSSIDV, val);
+}
+
+static void ivpu_hw_ip_tbu_mmu_enable_40xx(struct ivpu_device *vdev)
+{
+ u32 val = REGV_RD32(VPU_40XX_HOST_IF_TBU_MMUSSIDV);
+
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU1_AWMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU1_ARMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val);
+
+ REGV_WR32(VPU_40XX_HOST_IF_TBU_MMUSSIDV, val);
+}
+
+void ivpu_hw_ip_tbu_mmu_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return ivpu_hw_ip_tbu_mmu_enable_37xx(vdev);
+ else
+ return ivpu_hw_ip_tbu_mmu_enable_40xx(vdev);
+}
+
+static int soc_cpu_boot_37xx(struct ivpu_device *vdev)
+{
+ u32 val;
+
+ val = REGV_RD32(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC);
+ val = REG_SET_FLD(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, IRQI_RSTRUN0, val);
+
+ val = REG_CLR_FLD(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, IRQI_RSTVEC, val);
+ REGV_WR32(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, val);
+
+ val = REG_SET_FLD(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, IRQI_RESUME0, val);
+ REGV_WR32(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, val);
+
+ val = REG_CLR_FLD(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, IRQI_RESUME0, val);
+ REGV_WR32(VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC, val);
+
+ val = vdev->fw->entry_point >> 9;
+ REGV_WR32(VPU_37XX_HOST_SS_LOADING_ADDRESS_LO, val);
+
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_LOADING_ADDRESS_LO, DONE, val);
+ REGV_WR32(VPU_37XX_HOST_SS_LOADING_ADDRESS_LO, val);
+
+ ivpu_dbg(vdev, PM, "Booting firmware, mode: %s\n",
+ vdev->fw->entry_point == vdev->fw->cold_boot_entry_point ? "cold boot" : "resume");
+
+ return 0;
+}
+
+static int cpu_noc_qacceptn_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN, TOP_MMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int cpu_noc_qdeny_check_40xx(struct ivpu_device *vdev, u32 exp_val)
+{
+ u32 val = REGV_RD32(VPU_40XX_CPU_SS_CPR_NOC_QDENY);
+
+ if (!REG_TEST_FLD_NUM(VPU_40XX_CPU_SS_CPR_NOC_QDENY, TOP_MMIO, exp_val, val))
+ return -EIO;
+
+ return 0;
+}
+
+static void cpu_noc_top_mmio_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(VPU_40XX_CPU_SS_CPR_NOC_QREQN);
+
+ if (enable)
+ val = REG_SET_FLD(VPU_40XX_CPU_SS_CPR_NOC_QREQN, TOP_MMIO, val);
+ else
+ val = REG_CLR_FLD(VPU_40XX_CPU_SS_CPR_NOC_QREQN, TOP_MMIO, val);
+ REGV_WR32(VPU_40XX_CPU_SS_CPR_NOC_QREQN, val);
+}
+
+static int soc_cpu_drive_40xx(struct ivpu_device *vdev, bool enable)
+{
+ int ret;
+
+ cpu_noc_top_mmio_drive_40xx(vdev, enable);
+
+ ret = cpu_noc_qacceptn_check_40xx(vdev, enable ? 0x1 : 0x0);
+ if (ret) {
+ ivpu_err(vdev, "Failed qacceptn check: %d\n", ret);
+ return ret;
+ }
+
+ ret = cpu_noc_qdeny_check_40xx(vdev, 0x0);
+ if (ret)
+ ivpu_err(vdev, "Failed qdeny check: %d\n", ret);
+
+ return ret;
+}
+
+static int soc_cpu_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX)
+ return 0;
+
+ return soc_cpu_drive_40xx(vdev, true);
+}
+
+static int soc_cpu_boot_40xx(struct ivpu_device *vdev)
+{
+ int ret;
+ u32 val;
+ u64 val64;
+
+ ret = soc_cpu_enable(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to enable SOC CPU: %d\n", ret);
+ return ret;
+ }
+
+ val64 = vdev->fw->entry_point;
+ val64 <<= ffs(VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IMAGE_LOCATION_MASK) - 1;
+ REGV_WR64(VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO, val64);
+
+ val = REGV_RD32(VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO);
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO, DONE, val);
+ REGV_WR32(VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO, val);
+
+ ivpu_dbg(vdev, PM, "Booting firmware, mode: %s\n",
+ ivpu_fw_is_cold_boot(vdev) ? "cold boot" : "resume");
+
+ return 0;
+}
+
+int ivpu_hw_ip_soc_cpu_boot(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return soc_cpu_boot_37xx(vdev);
+ else
+ return soc_cpu_boot_40xx(vdev);
+}
+
+static void wdt_disable_37xx(struct ivpu_device *vdev)
+{
+ u32 val;
+
+ /* Enable writing and set non-zero WDT value */
+ REGV_WR32(VPU_37XX_CPU_SS_TIM_SAFE, TIM_SAFE_ENABLE);
+ REGV_WR32(VPU_37XX_CPU_SS_TIM_WATCHDOG, TIM_WATCHDOG_RESET_VALUE);
+
+ /* Enable writing and disable watchdog timer */
+ REGV_WR32(VPU_37XX_CPU_SS_TIM_SAFE, TIM_SAFE_ENABLE);
+ REGV_WR32(VPU_37XX_CPU_SS_TIM_WDOG_EN, 0);
+
+ /* Now clear the timeout interrupt */
+ val = REGV_RD32(VPU_37XX_CPU_SS_TIM_GEN_CONFIG);
+ val = REG_CLR_FLD(VPU_37XX_CPU_SS_TIM_GEN_CONFIG, WDOG_TO_INT_CLR, val);
+ REGV_WR32(VPU_37XX_CPU_SS_TIM_GEN_CONFIG, val);
+}
+
+static void wdt_disable_40xx(struct ivpu_device *vdev)
+{
+ u32 val;
+
+ REGV_WR32(VPU_40XX_CPU_SS_TIM_SAFE, TIM_SAFE_ENABLE);
+ REGV_WR32(VPU_40XX_CPU_SS_TIM_WATCHDOG, TIM_WATCHDOG_RESET_VALUE);
+
+ REGV_WR32(VPU_40XX_CPU_SS_TIM_SAFE, TIM_SAFE_ENABLE);
+ REGV_WR32(VPU_40XX_CPU_SS_TIM_WDOG_EN, 0);
+
+ val = REGV_RD32(VPU_40XX_CPU_SS_TIM_GEN_CONFIG);
+ val = REG_CLR_FLD(VPU_40XX_CPU_SS_TIM_GEN_CONFIG, WDOG_TO_INT_CLR, val);
+ REGV_WR32(VPU_40XX_CPU_SS_TIM_GEN_CONFIG, val);
+}
+
+void ivpu_hw_ip_wdt_disable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return wdt_disable_37xx(vdev);
+ else
+ return wdt_disable_40xx(vdev);
+}
+
+static u32 ipc_rx_count_get_37xx(struct ivpu_device *vdev)
+{
+ u32 count = readl(vdev->regv + VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT);
+
+ return REG_GET_FLD(VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT, FILL_LEVEL, count);
+}
+
+static u32 ipc_rx_count_get_40xx(struct ivpu_device *vdev)
+{
+ u32 count = readl(vdev->regv + VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT);
+
+ return REG_GET_FLD(VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT, FILL_LEVEL, count);
+}
+
+u32 ivpu_hw_ip_ipc_rx_count_get(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return ipc_rx_count_get_37xx(vdev);
+ else
+ return ipc_rx_count_get_40xx(vdev);
+}
+
+void ivpu_hw_ip_irq_enable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ REGV_WR32(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, ITF_FIREWALL_VIOLATION_MASK_37XX);
+ REGV_WR64(VPU_37XX_HOST_SS_ICB_ENABLE_0, ICB_0_1_IRQ_MASK_37XX);
+ } else {
+ REGV_WR32(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, ITF_FIREWALL_VIOLATION_MASK_40XX);
+ REGV_WR64(VPU_40XX_HOST_SS_ICB_ENABLE_0, ICB_0_1_IRQ_MASK_40XX);
+ }
+}
+
+void ivpu_hw_ip_irq_disable(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ REGV_WR64(VPU_37XX_HOST_SS_ICB_ENABLE_0, 0x0ull);
+ REGV_WR32(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, 0x0);
+ } else {
+ REGV_WR64(VPU_40XX_HOST_SS_ICB_ENABLE_0, 0x0ull);
+ REGV_WR32(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, 0x0ul);
+ }
+}
+
+static void diagnose_failure_37xx(struct ivpu_device *vdev)
+{
+ u32 reg = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_37XX;
+
+ if (ipc_rx_count_get_37xx(vdev))
+ ivpu_err(vdev, "IPC FIFO queue not empty, missed IPC IRQ");
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, reg))
+ ivpu_err(vdev, "WDT MSS timeout detected\n");
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT, reg))
+ ivpu_err(vdev, "WDT NCE timeout detected\n");
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, reg))
+ ivpu_err(vdev, "NOC Firewall irq detected\n");
+}
+
+static void diagnose_failure_40xx(struct ivpu_device *vdev)
+{
+ u32 reg = REGV_RD32(VPU_40XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_40XX;
+
+ if (ipc_rx_count_get_40xx(vdev))
+ ivpu_err(vdev, "IPC FIFO queue not empty, missed IPC IRQ");
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, reg))
+ ivpu_err(vdev, "WDT MSS timeout detected\n");
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT, reg))
+ ivpu_err(vdev, "WDT NCE timeout detected\n");
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, reg))
+ ivpu_err(vdev, "NOC Firewall irq detected\n");
+}
+
+void ivpu_hw_ip_diagnose_failure(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ diagnose_failure_37xx(vdev);
+ else
+ diagnose_failure_40xx(vdev);
+}
+
+void ivpu_hw_ip_irq_clear(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ REGV_WR64(VPU_37XX_HOST_SS_ICB_CLEAR_0, ICB_0_1_IRQ_MASK_37XX);
+ else
+ REGV_WR64(VPU_40XX_HOST_SS_ICB_CLEAR_0, ICB_0_1_IRQ_MASK_40XX);
+}
+
+static void irq_wdt_nce_handler(struct ivpu_device *vdev)
+{
+ ivpu_pm_trigger_recovery(vdev, "WDT NCE IRQ");
+}
+
+static void irq_wdt_mss_handler(struct ivpu_device *vdev)
+{
+ ivpu_hw_ip_wdt_disable(vdev);
+ ivpu_pm_trigger_recovery(vdev, "WDT MSS IRQ");
+}
+
+static void irq_noc_firewall_handler(struct ivpu_device *vdev)
+{
+ atomic_inc(&vdev->hw->firewall_irq_counter);
+
+ ivpu_dbg(vdev, IRQ, "NOC Firewall interrupt detected, counter %d\n",
+ atomic_read(&vdev->hw->firewall_irq_counter));
+}
+
+/* Handler for IRQs from NPU core */
+bool ivpu_hw_ip_irq_handler_37xx(struct ivpu_device *vdev, int irq)
+{
+ u32 status = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_37XX;
+
+ if (!status)
+ return false;
+
+ REGV_WR32(VPU_37XX_HOST_SS_ICB_CLEAR_0, status);
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT, status))
+ ivpu_mmu_irq_evtq_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT, status))
+ ivpu_ipc_irq_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT, status))
+ ivpu_dbg(vdev, IRQ, "MMU sync complete\n");
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT, status))
+ ivpu_mmu_irq_gerr_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, status))
+ irq_wdt_mss_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT, status))
+ irq_wdt_nce_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, status))
+ irq_noc_firewall_handler(vdev);
+
+ return true;
+}
+
+/* Handler for IRQs from NPU core */
+bool ivpu_hw_ip_irq_handler_40xx(struct ivpu_device *vdev, int irq)
+{
+ u32 status = REGV_RD32(VPU_40XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_40XX;
+
+ if (!status)
+ return false;
+
+ REGV_WR32(VPU_40XX_HOST_SS_ICB_CLEAR_0, status);
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT, status))
+ ivpu_mmu_irq_evtq_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT, status))
+ ivpu_ipc_irq_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT, status))
+ ivpu_dbg(vdev, IRQ, "MMU sync complete\n");
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT, status))
+ ivpu_mmu_irq_gerr_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, status))
+ irq_wdt_mss_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT, status))
+ irq_wdt_nce_handler(vdev);
+
+ if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, status))
+ irq_noc_firewall_handler(vdev);
+
+ return true;
+}
+
+static void db_set_37xx(struct ivpu_device *vdev, u32 db_id)
+{
+ u32 reg_stride = VPU_37XX_CPU_SS_DOORBELL_1 - VPU_37XX_CPU_SS_DOORBELL_0;
+ u32 val = REG_FLD(VPU_37XX_CPU_SS_DOORBELL_0, SET);
+
+ REGV_WR32I(VPU_37XX_CPU_SS_DOORBELL_0, reg_stride, db_id, val);
+}
+
+static void db_set_40xx(struct ivpu_device *vdev, u32 db_id)
+{
+ u32 reg_stride = VPU_40XX_CPU_SS_DOORBELL_1 - VPU_40XX_CPU_SS_DOORBELL_0;
+ u32 val = REG_FLD(VPU_40XX_CPU_SS_DOORBELL_0, SET);
+
+ REGV_WR32I(VPU_40XX_CPU_SS_DOORBELL_0, reg_stride, db_id, val);
+}
+
+void ivpu_hw_ip_db_set(struct ivpu_device *vdev, u32 db_id)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ db_set_37xx(vdev, db_id);
+ else
+ db_set_40xx(vdev, db_id);
+}
+
+u32 ivpu_hw_ip_ipc_rx_addr_get(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ return REGV_RD32(VPU_37XX_HOST_SS_TIM_IPC_FIFO_ATM);
+ else
+ return REGV_RD32(VPU_40XX_HOST_SS_TIM_IPC_FIFO_ATM);
+}
+
+void ivpu_hw_ip_ipc_tx_set(struct ivpu_device *vdev, u32 vpu_addr)
+{
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
+ REGV_WR32(VPU_37XX_CPU_SS_TIM_IPC_FIFO, vpu_addr);
+ else
+ REGV_WR32(VPU_40XX_CPU_SS_TIM_IPC_FIFO, vpu_addr);
+}
diff --git a/drivers/accel/ivpu/ivpu_hw_ip.h b/drivers/accel/ivpu/ivpu_hw_ip.h
new file mode 100644
index 000000000000..5b1b391aa577
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_ip.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_IP_H__
+#define __IVPU_HW_IP_H__
+
+#include "ivpu_drv.h"
+
+int ivpu_hw_ip_host_ss_configure(struct ivpu_device *vdev);
+void ivpu_hw_ip_idle_gen_enable(struct ivpu_device *vdev);
+void ivpu_hw_ip_idle_gen_disable(struct ivpu_device *vdev);
+int ivpu_hw_ip_pwr_domain_enable(struct ivpu_device *vdev);
+int ivpu_hw_ip_host_ss_axi_enable(struct ivpu_device *vdev);
+int ivpu_hw_ip_top_noc_enable(struct ivpu_device *vdev);
+u64 ivpu_hw_ip_read_perf_timer_counter(struct ivpu_device *vdev);
+void ivpu_hw_ip_snoop_disable(struct ivpu_device *vdev);
+void ivpu_hw_ip_tbu_mmu_enable(struct ivpu_device *vdev);
+int ivpu_hw_ip_soc_cpu_boot(struct ivpu_device *vdev);
+void ivpu_hw_ip_wdt_disable(struct ivpu_device *vdev);
+void ivpu_hw_ip_diagnose_failure(struct ivpu_device *vdev);
+u32 ivpu_hw_ip_ipc_rx_count_get(struct ivpu_device *vdev);
+void ivpu_hw_ip_irq_clear(struct ivpu_device *vdev);
+bool ivpu_hw_ip_irq_handler_37xx(struct ivpu_device *vdev, int irq);
+bool ivpu_hw_ip_irq_handler_40xx(struct ivpu_device *vdev, int irq);
+void ivpu_hw_ip_db_set(struct ivpu_device *vdev, u32 db_id);
+u32 ivpu_hw_ip_ipc_rx_addr_get(struct ivpu_device *vdev);
+void ivpu_hw_ip_ipc_tx_set(struct ivpu_device *vdev, u32 vpu_addr);
+void ivpu_hw_ip_irq_enable(struct ivpu_device *vdev);
+void ivpu_hw_ip_irq_disable(struct ivpu_device *vdev);
+void ivpu_hw_ip_diagnose_failure(struct ivpu_device *vdev);
+void ivpu_hw_ip_fabric_req_override_enable_50xx(struct ivpu_device *vdev);
+void ivpu_hw_ip_fabric_req_override_disable_50xx(struct ivpu_device *vdev);
+
+#endif /* __IVPU_HW_IP_H__ */
diff --git a/drivers/accel/ivpu/ivpu_hw_reg_io.h b/drivers/accel/ivpu/ivpu_hw_reg_io.h
new file mode 100644
index 000000000000..66259b0ead02
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_hw_reg_io.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_HW_REG_IO_H__
+#define __IVPU_HW_REG_IO_H__
+
+#include <linux/bitfield.h>
+#include <linux/fault-inject.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "ivpu_drv.h"
+
+#define REG_POLL_SLEEP_US 50
+#define REG_IO_ERROR 0xffffffff
+
+#define REGB_RD32(reg) ivpu_hw_reg_rd32(vdev, vdev->regb, (reg), #reg, __func__)
+#define REGB_RD64(reg) ivpu_hw_reg_rd64(vdev, vdev->regb, (reg), #reg, __func__)
+#define REGB_WR32(reg, val) ivpu_hw_reg_wr32(vdev, vdev->regb, (reg), (val), #reg, __func__)
+#define REGB_WR64(reg, val) ivpu_hw_reg_wr64(vdev, vdev->regb, (reg), (val), #reg, __func__)
+
+#define REGV_RD32(reg) ivpu_hw_reg_rd32(vdev, vdev->regv, (reg), #reg, __func__)
+#define REGV_RD64(reg) ivpu_hw_reg_rd64(vdev, vdev->regv, (reg), #reg, __func__)
+#define REGV_WR32(reg, val) ivpu_hw_reg_wr32(vdev, vdev->regv, (reg), (val), #reg, __func__)
+#define REGV_WR64(reg, val) ivpu_hw_reg_wr64(vdev, vdev->regv, (reg), (val), #reg, __func__)
+
+#define REGV_WR32I(reg, stride, index, val) \
+ ivpu_hw_reg_wr32_index(vdev, vdev->regv, (reg), (stride), (index), (val), #reg, __func__)
+
+#define REG_FLD(REG, FLD) \
+ (REG##_##FLD##_MASK)
+#define REG_FLD_NUM(REG, FLD, num) \
+ FIELD_PREP(REG##_##FLD##_MASK, num)
+#define REG_GET_FLD(REG, FLD, val) \
+ FIELD_GET(REG##_##FLD##_MASK, val)
+#define REG_CLR_FLD(REG, FLD, val) \
+ ((val) & ~(REG##_##FLD##_MASK))
+#define REG_SET_FLD(REG, FLD, val) \
+ ((val) | (REG##_##FLD##_MASK))
+#define REG_SET_FLD_NUM(REG, FLD, num, val) \
+ (((val) & ~(REG##_##FLD##_MASK)) | FIELD_PREP(REG##_##FLD##_MASK, num))
+#define REG_TEST_FLD(REG, FLD, val) \
+ ((REG##_##FLD##_MASK) == ((val) & (REG##_##FLD##_MASK)))
+#define REG_TEST_FLD_NUM(REG, FLD, num, val) \
+ ((num) == FIELD_GET(REG##_##FLD##_MASK, val))
+
+#define REGB_POLL_FLD(reg, fld, exp_fld_val, timeout_us) \
+ ivpu_hw_reg_poll_fld(vdev, vdev->regb, reg, reg##_##fld##_MASK, \
+ FIELD_PREP(reg##_##fld##_MASK, exp_fld_val), timeout_us, \
+ __func__, #reg, #fld)
+
+#define REGV_POLL_FLD(reg, fld, exp_fld_val, timeout_us) \
+ ivpu_hw_reg_poll_fld(vdev, vdev->regv, reg, reg##_##fld##_MASK, \
+ FIELD_PREP(reg##_##fld##_MASK, exp_fld_val), timeout_us, \
+ __func__, #reg, #fld)
+
+extern struct fault_attr ivpu_hw_failure;
+
+static inline int __must_check
+ivpu_hw_reg_poll_fld(struct ivpu_device *vdev, void __iomem *base,
+ u32 reg_offset, u32 reg_mask, u32 exp_masked_val, u32 timeout_us,
+ const char *func_name, const char *reg_name, const char *fld_name)
+{
+ u32 reg_val;
+ int ret;
+
+ ivpu_dbg(vdev, REG, "%s : %s (0x%08x) POLL %s started (exp_val 0x%x)\n",
+ func_name, reg_name, reg_offset, fld_name, exp_masked_val);
+
+ ret = read_poll_timeout(readl, reg_val, (reg_val & reg_mask) == exp_masked_val,
+ REG_POLL_SLEEP_US, timeout_us, false, base + reg_offset);
+
+#ifdef CONFIG_FAULT_INJECTION
+ if (should_fail(&ivpu_hw_failure, 1))
+ ret = -ETIMEDOUT;
+#endif
+
+ ivpu_dbg(vdev, REG, "%s : %s (0x%08x) POLL %s %s (reg_val 0x%08x)\n",
+ func_name, reg_name, reg_offset, fld_name, ret ? "ETIMEDOUT" : "OK", reg_val);
+
+ return ret;
+}
+
+static inline u32
+ivpu_hw_reg_rd32(struct ivpu_device *vdev, void __iomem *base, u32 reg,
+ const char *name, const char *func)
+{
+ u32 val = readl(base + reg);
+
+ ivpu_dbg(vdev, REG, "%s : %s (0x%08x) RD: 0x%08x\n", func, name, reg, val);
+ return val;
+}
+
+static inline u64
+ivpu_hw_reg_rd64(struct ivpu_device *vdev, void __iomem *base, u32 reg,
+ const char *name, const char *func)
+{
+ u64 val = readq(base + reg);
+
+ ivpu_dbg(vdev, REG, "%s : %s (0x%08x) RD: 0x%016llx\n", func, name, reg, val);
+ return val;
+}
+
+static inline void
+ivpu_hw_reg_wr32(struct ivpu_device *vdev, void __iomem *base, u32 reg, u32 val,
+ const char *name, const char *func)
+{
+ ivpu_dbg(vdev, REG, "%s : %s (0x%08x) WR: 0x%08x\n", func, name, reg, val);
+ writel(val, base + reg);
+}
+
+static inline void
+ivpu_hw_reg_wr64(struct ivpu_device *vdev, void __iomem *base, u32 reg, u64 val,
+ const char *name, const char *func)
+{
+ ivpu_dbg(vdev, REG, "%s : %s (0x%08x) WR: 0x%016llx\n", func, name, reg, val);
+ writeq(val, base + reg);
+}
+
+static inline void
+ivpu_hw_reg_wr32_index(struct ivpu_device *vdev, void __iomem *base, u32 reg,
+ u32 stride, u32 index, u32 val, const char *name,
+ const char *func)
+{
+ reg += index * stride;
+
+ ivpu_dbg(vdev, REG, "%s WR: %s_%d (0x%08x) <= 0x%08x\n", func, name, index, reg, val);
+ writel(val, base + reg);
+}
+
+#endif /* __IVPU_HW_REG_IO_H__ */
diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c
new file mode 100644
index 000000000000..1f13bf95b2b3
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_ipc.c
@@ -0,0 +1,593 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <linux/genalloc.h>
+#include <linux/highmem.h>
+#include <linux/pm_runtime.h>
+#include <linux/wait.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_gem.h"
+#include "ivpu_hw.h"
+#include "ivpu_hw_reg_io.h"
+#include "ivpu_ipc.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_pm.h"
+#include "ivpu_trace.h"
+
+#define IPC_MAX_RX_MSG 128
+
+struct ivpu_ipc_tx_buf {
+ struct ivpu_ipc_hdr ipc;
+ struct vpu_jsm_msg jsm;
+};
+
+static void ivpu_ipc_msg_dump(struct ivpu_device *vdev, char *c,
+ struct ivpu_ipc_hdr *ipc_hdr, u32 vpu_addr)
+{
+ ivpu_dbg(vdev, IPC,
+ "%s: vpu:0x%x (data_addr:0x%08x, data_size:0x%x, channel:0x%x, src_node:0x%x, dst_node:0x%x, status:0x%x)",
+ c, vpu_addr, ipc_hdr->data_addr, ipc_hdr->data_size, ipc_hdr->channel,
+ ipc_hdr->src_node, ipc_hdr->dst_node, ipc_hdr->status);
+}
+
+static void ivpu_jsm_msg_dump(struct ivpu_device *vdev, char *c,
+ struct vpu_jsm_msg *jsm_msg, u32 vpu_addr)
+{
+ u32 *payload = (u32 *)&jsm_msg->payload;
+
+ ivpu_dbg(vdev, JSM,
+ "%s: vpu:0x%08x (type:%s, status:0x%x, id: 0x%x, result: 0x%x, payload:0x%x 0x%x 0x%x 0x%x 0x%x)\n",
+ c, vpu_addr, ivpu_jsm_msg_type_to_str(jsm_msg->type),
+ jsm_msg->status, jsm_msg->request_id, jsm_msg->result,
+ payload[0], payload[1], payload[2], payload[3], payload[4]);
+}
+
+static void
+ivpu_ipc_rx_mark_free(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
+ struct vpu_jsm_msg *jsm_msg)
+{
+ ipc_hdr->status = IVPU_IPC_HDR_FREE;
+ if (jsm_msg)
+ jsm_msg->status = VPU_JSM_MSG_FREE;
+ wmb(); /* Flush WC buffers for message statuses */
+}
+
+static void ivpu_ipc_mem_fini(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+
+ ivpu_bo_free(ipc->mem_rx);
+ ivpu_bo_free(ipc->mem_tx);
+}
+
+static int
+ivpu_ipc_tx_prepare(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ struct vpu_jsm_msg *req)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_tx_buf *tx_buf;
+ u32 tx_buf_vpu_addr;
+ u32 jsm_vpu_addr;
+
+ tx_buf_vpu_addr = gen_pool_alloc(ipc->mm_tx, sizeof(*tx_buf));
+ if (!tx_buf_vpu_addr) {
+ ivpu_err_ratelimited(vdev, "Failed to reserve IPC buffer, size %ld\n",
+ sizeof(*tx_buf));
+ return -ENOMEM;
+ }
+
+ tx_buf = ivpu_to_cpu_addr(ipc->mem_tx, tx_buf_vpu_addr);
+ if (drm_WARN_ON(&vdev->drm, !tx_buf)) {
+ gen_pool_free(ipc->mm_tx, tx_buf_vpu_addr, sizeof(*tx_buf));
+ return -EIO;
+ }
+
+ jsm_vpu_addr = tx_buf_vpu_addr + offsetof(struct ivpu_ipc_tx_buf, jsm);
+
+ if (tx_buf->ipc.status != IVPU_IPC_HDR_FREE)
+ ivpu_warn_ratelimited(vdev, "IPC message vpu:0x%x not released by firmware\n",
+ tx_buf_vpu_addr);
+
+ if (tx_buf->jsm.status != VPU_JSM_MSG_FREE)
+ ivpu_warn_ratelimited(vdev, "JSM message vpu:0x%x not released by firmware\n",
+ jsm_vpu_addr);
+
+ memset(tx_buf, 0, sizeof(*tx_buf));
+ tx_buf->ipc.data_addr = jsm_vpu_addr;
+ /* TODO: Set data_size to actual JSM message size, not union of all messages */
+ tx_buf->ipc.data_size = sizeof(*req);
+ tx_buf->ipc.channel = cons->channel;
+ tx_buf->ipc.src_node = 0;
+ tx_buf->ipc.dst_node = 1;
+ tx_buf->ipc.status = IVPU_IPC_HDR_ALLOCATED;
+ tx_buf->jsm.type = req->type;
+ tx_buf->jsm.status = VPU_JSM_MSG_ALLOCATED;
+ tx_buf->jsm.payload = req->payload;
+
+ req->request_id = atomic_inc_return(&ipc->request_id);
+ tx_buf->jsm.request_id = req->request_id;
+ cons->request_id = req->request_id;
+ wmb(); /* Flush WC buffers for IPC, JSM msgs */
+
+ cons->tx_vpu_addr = tx_buf_vpu_addr;
+
+ ivpu_jsm_msg_dump(vdev, "TX", &tx_buf->jsm, jsm_vpu_addr);
+ ivpu_ipc_msg_dump(vdev, "TX", &tx_buf->ipc, tx_buf_vpu_addr);
+
+ return 0;
+}
+
+static void ivpu_ipc_tx_release(struct ivpu_device *vdev, u32 vpu_addr)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+
+ if (vpu_addr)
+ gen_pool_free(ipc->mm_tx, vpu_addr, sizeof(struct ivpu_ipc_tx_buf));
+}
+
+static void ivpu_ipc_tx(struct ivpu_device *vdev, u32 vpu_addr)
+{
+ ivpu_hw_ipc_tx_set(vdev, vpu_addr);
+}
+
+static void
+ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ struct ivpu_ipc_hdr *ipc_hdr, struct vpu_jsm_msg *jsm_msg)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_rx_msg *rx_msg;
+
+ lockdep_assert_held(&ipc->cons_lock);
+
+ rx_msg = kzalloc(sizeof(*rx_msg), GFP_ATOMIC);
+ if (!rx_msg) {
+ ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
+ return;
+ }
+
+ atomic_inc(&ipc->rx_msg_count);
+
+ rx_msg->ipc_hdr = ipc_hdr;
+ rx_msg->jsm_msg = jsm_msg;
+ rx_msg->callback = cons->rx_callback;
+
+ if (rx_msg->callback) {
+ list_add_tail(&rx_msg->link, &ipc->cb_msg_list);
+ } else {
+ spin_lock(&cons->rx_lock);
+ list_add_tail(&rx_msg->link, &cons->rx_msg_list);
+ spin_unlock(&cons->rx_lock);
+ wake_up(&cons->rx_msg_wq);
+ }
+}
+
+static void
+ivpu_ipc_rx_msg_del(struct ivpu_device *vdev, struct ivpu_ipc_rx_msg *rx_msg)
+{
+ list_del(&rx_msg->link);
+ ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg);
+ atomic_dec(&vdev->ipc->rx_msg_count);
+ kfree(rx_msg);
+}
+
+void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ u32 channel, ivpu_ipc_rx_callback_t rx_callback)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+
+ INIT_LIST_HEAD(&cons->link);
+ cons->channel = channel;
+ cons->tx_vpu_addr = 0;
+ cons->request_id = 0;
+ cons->aborted = false;
+ cons->rx_callback = rx_callback;
+ spin_lock_init(&cons->rx_lock);
+ INIT_LIST_HEAD(&cons->rx_msg_list);
+ init_waitqueue_head(&cons->rx_msg_wq);
+
+ spin_lock_irq(&ipc->cons_lock);
+ list_add_tail(&cons->link, &ipc->cons_list);
+ spin_unlock_irq(&ipc->cons_lock);
+}
+
+void ivpu_ipc_consumer_del(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_rx_msg *rx_msg, *r;
+
+ spin_lock_irq(&ipc->cons_lock);
+ list_del(&cons->link);
+ spin_unlock_irq(&ipc->cons_lock);
+
+ spin_lock_irq(&cons->rx_lock);
+ list_for_each_entry_safe(rx_msg, r, &cons->rx_msg_list, link)
+ ivpu_ipc_rx_msg_del(vdev, rx_msg);
+ spin_unlock_irq(&cons->rx_lock);
+
+ ivpu_ipc_tx_release(vdev, cons->tx_vpu_addr);
+}
+
+int ivpu_ipc_send(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, struct vpu_jsm_msg *req)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ int ret;
+
+ mutex_lock(&ipc->lock);
+
+ if (!ipc->on) {
+ ret = -EAGAIN;
+ goto unlock;
+ }
+
+ ret = ivpu_ipc_tx_prepare(vdev, cons, req);
+ if (ret)
+ goto unlock;
+
+ ivpu_ipc_tx(vdev, cons->tx_vpu_addr);
+ trace_jsm("[tx]", req);
+
+unlock:
+ mutex_unlock(&ipc->lock);
+ return ret;
+}
+
+static bool ivpu_ipc_rx_need_wakeup(struct ivpu_ipc_consumer *cons)
+{
+ bool ret;
+
+ spin_lock_irq(&cons->rx_lock);
+ ret = !list_empty(&cons->rx_msg_list) || cons->aborted;
+ spin_unlock_irq(&cons->rx_lock);
+
+ return ret;
+}
+
+int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ struct ivpu_ipc_hdr *ipc_buf,
+ struct vpu_jsm_msg *jsm_msg, unsigned long timeout_ms)
+{
+ struct ivpu_ipc_rx_msg *rx_msg;
+ int wait_ret, ret = 0;
+
+ if (drm_WARN_ONCE(&vdev->drm, cons->rx_callback, "Consumer works only in async mode\n"))
+ return -EINVAL;
+
+ wait_ret = wait_event_timeout(cons->rx_msg_wq,
+ ivpu_ipc_rx_need_wakeup(cons),
+ msecs_to_jiffies(timeout_ms));
+
+ if (wait_ret == 0)
+ return -ETIMEDOUT;
+
+ spin_lock_irq(&cons->rx_lock);
+ if (cons->aborted) {
+ spin_unlock_irq(&cons->rx_lock);
+ return -ECANCELED;
+ }
+ rx_msg = list_first_entry_or_null(&cons->rx_msg_list, struct ivpu_ipc_rx_msg, link);
+ if (!rx_msg) {
+ spin_unlock_irq(&cons->rx_lock);
+ return -EAGAIN;
+ }
+
+ if (ipc_buf)
+ memcpy(ipc_buf, rx_msg->ipc_hdr, sizeof(*ipc_buf));
+ if (rx_msg->jsm_msg) {
+ u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg));
+
+ if (rx_msg->jsm_msg->result != VPU_JSM_STATUS_SUCCESS) {
+ ivpu_err(vdev, "IPC resp result error: %d\n", rx_msg->jsm_msg->result);
+ ret = -EBADMSG;
+ }
+
+ if (jsm_msg)
+ memcpy(jsm_msg, rx_msg->jsm_msg, size);
+ trace_jsm("[rx]", rx_msg->jsm_msg);
+ }
+
+ ivpu_ipc_rx_msg_del(vdev, rx_msg);
+ spin_unlock_irq(&cons->rx_lock);
+ return ret;
+}
+
+int
+ivpu_ipc_send_receive_internal(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
+ enum vpu_ipc_msg_type expected_resp_type,
+ struct vpu_jsm_msg *resp, u32 channel, unsigned long timeout_ms)
+{
+ struct ivpu_ipc_consumer cons;
+ int ret;
+
+ drm_WARN_ON(&vdev->drm, pm_runtime_status_suspended(vdev->drm.dev) &&
+ pm_runtime_enabled(vdev->drm.dev));
+
+ ivpu_ipc_consumer_add(vdev, &cons, channel, NULL);
+
+ ret = ivpu_ipc_send(vdev, &cons, req);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "IPC send failed: %d\n", ret);
+ goto consumer_del;
+ }
+
+ ret = ivpu_ipc_receive(vdev, &cons, NULL, resp, timeout_ms);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "IPC receive failed: type %s, ret %d\n",
+ ivpu_jsm_msg_type_to_str(req->type), ret);
+ goto consumer_del;
+ }
+
+ if (resp->type != expected_resp_type) {
+ ivpu_warn_ratelimited(vdev, "Invalid JSM response type: 0x%x\n", resp->type);
+ ret = -EBADE;
+ }
+
+consumer_del:
+ ivpu_ipc_consumer_del(vdev, &cons);
+ return ret;
+}
+
+int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
+ enum vpu_ipc_msg_type expected_resp, struct vpu_jsm_msg *resp,
+ u32 channel, unsigned long timeout_ms)
+{
+ struct vpu_jsm_msg hb_req = { .type = VPU_JSM_MSG_QUERY_ENGINE_HB };
+ struct vpu_jsm_msg hb_resp;
+ int ret, hb_ret;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ ret = ivpu_ipc_send_receive_internal(vdev, req, expected_resp, resp, channel, timeout_ms);
+ if (ret != -ETIMEDOUT)
+ goto rpm_put;
+
+ hb_ret = ivpu_ipc_send_receive_internal(vdev, &hb_req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE,
+ &hb_resp, VPU_IPC_CHAN_ASYNC_CMD,
+ vdev->timeout.jsm);
+ if (hb_ret == -ETIMEDOUT)
+ ivpu_pm_trigger_recovery(vdev, "IPC timeout");
+
+rpm_put:
+ ivpu_rpm_put(vdev);
+ return ret;
+}
+
+int ivpu_ipc_send_and_wait(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
+ u32 channel, unsigned long timeout_ms)
+{
+ struct ivpu_ipc_consumer cons;
+ int ret;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ ivpu_ipc_consumer_add(vdev, &cons, channel, NULL);
+
+ ret = ivpu_ipc_send(vdev, &cons, req);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "IPC send failed: %d\n", ret);
+ goto consumer_del;
+ }
+
+ msleep(timeout_ms);
+
+consumer_del:
+ ivpu_ipc_consumer_del(vdev, &cons);
+ ivpu_rpm_put(vdev);
+ return ret;
+}
+
+static bool
+ivpu_ipc_match_consumer(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ struct ivpu_ipc_hdr *ipc_hdr, struct vpu_jsm_msg *jsm_msg)
+{
+ if (cons->channel != ipc_hdr->channel)
+ return false;
+
+ if (!jsm_msg || jsm_msg->request_id == cons->request_id)
+ return true;
+
+ return false;
+}
+
+void ivpu_ipc_irq_handler(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_consumer *cons;
+ struct ivpu_ipc_hdr *ipc_hdr;
+ struct vpu_jsm_msg *jsm_msg;
+ unsigned long flags;
+ bool dispatched;
+ u32 vpu_addr;
+
+ /*
+ * Driver needs to purge all messages from IPC FIFO to clear IPC interrupt.
+ * Without purge IPC FIFO to 0 next IPC interrupts won't be generated.
+ */
+ while (ivpu_hw_ipc_rx_count_get(vdev)) {
+ vpu_addr = ivpu_hw_ipc_rx_addr_get(vdev);
+ if (vpu_addr == REG_IO_ERROR) {
+ ivpu_err_ratelimited(vdev, "Failed to read IPC rx addr register\n");
+ return;
+ }
+
+ ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr);
+ if (!ipc_hdr) {
+ ivpu_warn_ratelimited(vdev, "IPC msg 0x%x out of range\n", vpu_addr);
+ continue;
+ }
+ ivpu_ipc_msg_dump(vdev, "RX", ipc_hdr, vpu_addr);
+
+ jsm_msg = NULL;
+ if (ipc_hdr->channel != IVPU_IPC_CHAN_BOOT_MSG) {
+ jsm_msg = ivpu_to_cpu_addr(ipc->mem_rx, ipc_hdr->data_addr);
+ if (!jsm_msg) {
+ ivpu_warn_ratelimited(vdev, "JSM msg 0x%x out of range\n",
+ ipc_hdr->data_addr);
+ ivpu_ipc_rx_mark_free(vdev, ipc_hdr, NULL);
+ continue;
+ }
+ ivpu_jsm_msg_dump(vdev, "RX", jsm_msg, ipc_hdr->data_addr);
+ }
+
+ if (atomic_read(&ipc->rx_msg_count) > IPC_MAX_RX_MSG) {
+ ivpu_warn_ratelimited(vdev, "IPC RX msg dropped, msg count %d\n",
+ IPC_MAX_RX_MSG);
+ ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
+ continue;
+ }
+
+ dispatched = false;
+ spin_lock_irqsave(&ipc->cons_lock, flags);
+ list_for_each_entry(cons, &ipc->cons_list, link) {
+ if (ivpu_ipc_match_consumer(vdev, cons, ipc_hdr, jsm_msg)) {
+ ivpu_ipc_rx_msg_add(vdev, cons, ipc_hdr, jsm_msg);
+ dispatched = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ipc->cons_lock, flags);
+
+ if (!dispatched) {
+ ivpu_dbg(vdev, IPC, "IPC RX msg 0x%x dropped (no consumer)\n", vpu_addr);
+ ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
+ }
+ }
+
+ queue_work(system_percpu_wq, &vdev->irq_ipc_work);
+}
+
+void ivpu_ipc_irq_work_fn(struct work_struct *work)
+{
+ struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work);
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_rx_msg *rx_msg, *r;
+ struct list_head cb_msg_list;
+
+ INIT_LIST_HEAD(&cb_msg_list);
+
+ spin_lock_irq(&ipc->cons_lock);
+ list_splice_tail_init(&ipc->cb_msg_list, &cb_msg_list);
+ spin_unlock_irq(&ipc->cons_lock);
+
+ list_for_each_entry_safe(rx_msg, r, &cb_msg_list, link) {
+ rx_msg->callback(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg);
+ ivpu_ipc_rx_msg_del(vdev, rx_msg);
+ }
+}
+
+int ivpu_ipc_init(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ int ret;
+
+ ipc->mem_tx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!ipc->mem_tx) {
+ ivpu_err(vdev, "Failed to allocate mem_tx\n");
+ return -ENOMEM;
+ }
+
+ ipc->mem_rx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!ipc->mem_rx) {
+ ivpu_err(vdev, "Failed to allocate mem_rx\n");
+ ret = -ENOMEM;
+ goto err_free_tx;
+ }
+
+ ipc->mm_tx = devm_gen_pool_create(vdev->drm.dev, __ffs(IVPU_IPC_ALIGNMENT),
+ -1, "TX_IPC_JSM");
+ if (IS_ERR(ipc->mm_tx)) {
+ ret = PTR_ERR(ipc->mm_tx);
+ ivpu_err(vdev, "Failed to create gen pool, %pe\n", ipc->mm_tx);
+ goto err_free_rx;
+ }
+
+ ret = gen_pool_add(ipc->mm_tx, ipc->mem_tx->vpu_addr, ivpu_bo_size(ipc->mem_tx), -1);
+ if (ret) {
+ ivpu_err(vdev, "gen_pool_add failed, ret %d\n", ret);
+ goto err_free_rx;
+ }
+
+ spin_lock_init(&ipc->cons_lock);
+ INIT_LIST_HEAD(&ipc->cons_list);
+ INIT_LIST_HEAD(&ipc->cb_msg_list);
+ ret = drmm_mutex_init(&vdev->drm, &ipc->lock);
+ if (ret) {
+ ivpu_err(vdev, "Failed to initialize ipc->lock, ret %d\n", ret);
+ goto err_free_rx;
+ }
+ ivpu_ipc_reset(vdev);
+ return 0;
+
+err_free_rx:
+ ivpu_bo_free(ipc->mem_rx);
+err_free_tx:
+ ivpu_bo_free(ipc->mem_tx);
+ return ret;
+}
+
+void ivpu_ipc_fini(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+
+ drm_WARN_ON(&vdev->drm, !list_empty(&ipc->cons_list));
+ drm_WARN_ON(&vdev->drm, !list_empty(&ipc->cb_msg_list));
+ drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0);
+
+ ivpu_ipc_mem_fini(vdev);
+}
+
+void ivpu_ipc_enable(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+
+ mutex_lock(&ipc->lock);
+ ipc->on = true;
+ mutex_unlock(&ipc->lock);
+}
+
+void ivpu_ipc_disable(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+ struct ivpu_ipc_consumer *cons, *c;
+ struct ivpu_ipc_rx_msg *rx_msg, *r;
+
+ drm_WARN_ON(&vdev->drm, !list_empty(&ipc->cb_msg_list));
+
+ mutex_lock(&ipc->lock);
+ ipc->on = false;
+ mutex_unlock(&ipc->lock);
+
+ spin_lock_irq(&ipc->cons_lock);
+ list_for_each_entry_safe(cons, c, &ipc->cons_list, link) {
+ spin_lock(&cons->rx_lock);
+ if (!cons->rx_callback)
+ cons->aborted = true;
+ list_for_each_entry_safe(rx_msg, r, &cons->rx_msg_list, link)
+ ivpu_ipc_rx_msg_del(vdev, rx_msg);
+ spin_unlock(&cons->rx_lock);
+ wake_up(&cons->rx_msg_wq);
+ }
+ spin_unlock_irq(&ipc->cons_lock);
+
+ drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0);
+}
+
+void ivpu_ipc_reset(struct ivpu_device *vdev)
+{
+ struct ivpu_ipc_info *ipc = vdev->ipc;
+
+ mutex_lock(&ipc->lock);
+ drm_WARN_ON(&vdev->drm, ipc->on);
+
+ memset(ivpu_bo_vaddr(ipc->mem_tx), 0, ivpu_bo_size(ipc->mem_tx));
+ memset(ivpu_bo_vaddr(ipc->mem_rx), 0, ivpu_bo_size(ipc->mem_rx));
+ wmb(); /* Flush WC buffers for TX and RX rings */
+
+ mutex_unlock(&ipc->lock);
+}
diff --git a/drivers/accel/ivpu/ivpu_ipc.h b/drivers/accel/ivpu/ivpu_ipc.h
new file mode 100644
index 000000000000..b524a1985b9d
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_ipc.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_IPC_H__
+#define __IVPU_IPC_H__
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "vpu_jsm_api.h"
+
+struct ivpu_bo;
+
+/* VPU FW boot notification */
+#define IVPU_IPC_CHAN_BOOT_MSG 0x3ff
+#define IVPU_IPC_BOOT_MSG_DATA_ADDR 0x424f4f54
+
+/* The alignment to be used for IPC Buffers and IPC Data. */
+#define IVPU_IPC_ALIGNMENT 64
+
+#define IVPU_IPC_HDR_FREE 0
+#define IVPU_IPC_HDR_ALLOCATED 1
+
+/**
+ * struct ivpu_ipc_hdr - The IPC message header structure, exchanged
+ * with the VPU device firmware.
+ * @data_addr: The VPU address of the payload (JSM message)
+ * @data_size: The size of the payload.
+ * @channel: The channel used.
+ * @src_node: The Node ID of the sender.
+ * @dst_node: The Node ID of the intended receiver.
+ * @status: IPC buffer usage status
+ */
+struct ivpu_ipc_hdr {
+ u32 data_addr;
+ u32 data_size;
+ u16 channel;
+ u8 src_node;
+ u8 dst_node;
+ u8 status;
+} __packed __aligned(IVPU_IPC_ALIGNMENT);
+
+typedef void (*ivpu_ipc_rx_callback_t)(struct ivpu_device *vdev,
+ struct ivpu_ipc_hdr *ipc_hdr,
+ struct vpu_jsm_msg *jsm_msg);
+
+struct ivpu_ipc_rx_msg {
+ struct list_head link;
+ struct ivpu_ipc_hdr *ipc_hdr;
+ struct vpu_jsm_msg *jsm_msg;
+ ivpu_ipc_rx_callback_t callback;
+};
+
+struct ivpu_ipc_consumer {
+ struct list_head link;
+ u32 channel;
+ u32 tx_vpu_addr;
+ u32 request_id;
+ bool aborted;
+ ivpu_ipc_rx_callback_t rx_callback;
+
+ spinlock_t rx_lock; /* Protects rx_msg_list and aborted */
+ struct list_head rx_msg_list;
+ wait_queue_head_t rx_msg_wq;
+};
+
+struct ivpu_ipc_info {
+ struct gen_pool *mm_tx;
+ struct ivpu_bo *mem_tx;
+ struct ivpu_bo *mem_rx;
+
+ atomic_t rx_msg_count;
+
+ spinlock_t cons_lock; /* Protects cons_list and cb_msg_list */
+ struct list_head cons_list;
+ struct list_head cb_msg_list;
+
+ atomic_t request_id;
+ struct mutex lock; /* Lock on status */
+ bool on;
+};
+
+int ivpu_ipc_init(struct ivpu_device *vdev);
+void ivpu_ipc_fini(struct ivpu_device *vdev);
+
+void ivpu_ipc_enable(struct ivpu_device *vdev);
+void ivpu_ipc_disable(struct ivpu_device *vdev);
+void ivpu_ipc_reset(struct ivpu_device *vdev);
+
+void ivpu_ipc_irq_handler(struct ivpu_device *vdev);
+void ivpu_ipc_irq_work_fn(struct work_struct *work);
+
+void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ u32 channel, ivpu_ipc_rx_callback_t callback);
+void ivpu_ipc_consumer_del(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons);
+
+int ivpu_ipc_send(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ struct vpu_jsm_msg *req);
+int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
+ struct ivpu_ipc_hdr *ipc_buf, struct vpu_jsm_msg *jsm_msg,
+ unsigned long timeout_ms);
+int ivpu_ipc_send_receive_internal(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
+ enum vpu_ipc_msg_type expected_resp_type,
+ struct vpu_jsm_msg *resp, u32 channel, unsigned long timeout_ms);
+int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
+ enum vpu_ipc_msg_type expected_resp, struct vpu_jsm_msg *resp,
+ u32 channel, unsigned long timeout_ms);
+int ivpu_ipc_send_and_wait(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
+ u32 channel, unsigned long timeout_ms);
+
+#endif /* __IVPU_IPC_H__ */
diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c
new file mode 100644
index 000000000000..4f8564e2878a
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_job.c
@@ -0,0 +1,1153 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#include <drm/drm_file.h>
+
+#include <linux/bitfield.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <uapi/drm/ivpu_accel.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_hw.h"
+#include "ivpu_ipc.h"
+#include "ivpu_job.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_mmu.h"
+#include "ivpu_pm.h"
+#include "ivpu_trace.h"
+#include "vpu_boot_api.h"
+
+#define CMD_BUF_IDX 0
+#define JOB_MAX_BUFFER_COUNT 65535
+
+static void ivpu_cmdq_ring_db(struct ivpu_device *vdev, struct ivpu_cmdq *cmdq)
+{
+ ivpu_hw_db_set(vdev, cmdq->db_id);
+}
+
+static int ivpu_preemption_buffers_create(struct ivpu_device *vdev,
+ struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ if (ivpu_fw_preempt_buf_size(vdev) == 0)
+ return 0;
+
+ cmdq->primary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.user,
+ vdev->fw->primary_preempt_buf_size,
+ DRM_IVPU_BO_WC);
+ if (!cmdq->primary_preempt_buf) {
+ ivpu_err(vdev, "Failed to create primary preemption buffer\n");
+ return -ENOMEM;
+ }
+
+ cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.dma,
+ vdev->fw->secondary_preempt_buf_size,
+ DRM_IVPU_BO_WC);
+ if (!cmdq->secondary_preempt_buf) {
+ ivpu_err(vdev, "Failed to create secondary preemption buffer\n");
+ goto err_free_primary;
+ }
+
+ return 0;
+
+err_free_primary:
+ ivpu_bo_free(cmdq->primary_preempt_buf);
+ cmdq->primary_preempt_buf = NULL;
+ return -ENOMEM;
+}
+
+static void ivpu_preemption_buffers_free(struct ivpu_device *vdev,
+ struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ if (cmdq->primary_preempt_buf)
+ ivpu_bo_free(cmdq->primary_preempt_buf);
+ if (cmdq->secondary_preempt_buf)
+ ivpu_bo_free(cmdq->secondary_preempt_buf);
+}
+
+static int ivpu_preemption_job_init(struct ivpu_device *vdev, struct ivpu_file_priv *file_priv,
+ struct ivpu_cmdq *cmdq, struct ivpu_job *job)
+{
+ int ret;
+
+ /* Use preemption buffer provided by the user space */
+ if (job->primary_preempt_buf)
+ return 0;
+
+ if (!cmdq->primary_preempt_buf) {
+ /* Allocate per command queue preemption buffers */
+ ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq);
+ if (ret)
+ return ret;
+ }
+
+ /* Use preemption buffers allocated by the kernel */
+ job->primary_preempt_buf = cmdq->primary_preempt_buf;
+ job->secondary_preempt_buf = cmdq->secondary_preempt_buf;
+
+ return 0;
+}
+
+static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_cmdq *cmdq;
+
+ cmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL);
+ if (!cmdq)
+ return NULL;
+
+ cmdq->mem = ivpu_bo_create_global(vdev, SZ_4K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!cmdq->mem)
+ goto err_free_cmdq;
+
+ return cmdq;
+
+err_free_cmdq:
+ kfree(cmdq);
+ return NULL;
+}
+
+/**
+ * ivpu_cmdq_get_entry_count - Calculate the number of entries in the command queue.
+ * @cmdq: Pointer to the command queue structure.
+ *
+ * Returns the number of entries that can fit in the command queue memory.
+ */
+static inline u32 ivpu_cmdq_get_entry_count(struct ivpu_cmdq *cmdq)
+{
+ size_t size = ivpu_bo_size(cmdq->mem) - sizeof(struct vpu_job_queue_header);
+
+ return size / sizeof(struct vpu_job_queue_entry);
+}
+
+/**
+ * ivpu_cmdq_get_flags - Get command queue flags based on input flags and test mode.
+ * @vdev: Pointer to the ivpu device structure.
+ * @flags: Input flags to determine the command queue flags.
+ *
+ * Returns the calculated command queue flags, considering both the input flags
+ * and the current test mode settings.
+ */
+static u32 ivpu_cmdq_get_flags(struct ivpu_device *vdev, u32 flags)
+{
+ u32 cmdq_flags = 0;
+
+ if ((flags & DRM_IVPU_CMDQ_FLAG_TURBO) && (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX))
+ cmdq_flags |= VPU_JOB_QUEUE_FLAGS_TURBO_MODE;
+
+ /* Test mode can override the TURBO flag coming from the application */
+ if (ivpu_test_mode & IVPU_TEST_MODE_TURBO_ENABLE)
+ cmdq_flags |= VPU_JOB_QUEUE_FLAGS_TURBO_MODE;
+ if (ivpu_test_mode & IVPU_TEST_MODE_TURBO_DISABLE)
+ cmdq_flags &= ~VPU_JOB_QUEUE_FLAGS_TURBO_MODE;
+
+ return cmdq_flags;
+}
+
+static void ivpu_cmdq_free(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ ivpu_preemption_buffers_free(file_priv->vdev, file_priv, cmdq);
+ ivpu_bo_free(cmdq->mem);
+ kfree(cmdq);
+}
+
+static struct ivpu_cmdq *ivpu_cmdq_create(struct ivpu_file_priv *file_priv, u8 priority, u32 flags)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_cmdq *cmdq = NULL;
+ int ret;
+
+ lockdep_assert_held(&file_priv->lock);
+
+ cmdq = ivpu_cmdq_alloc(file_priv);
+ if (!cmdq) {
+ ivpu_err(vdev, "Failed to allocate command queue\n");
+ return NULL;
+ }
+ ret = xa_alloc_cyclic(&file_priv->cmdq_xa, &cmdq->id, cmdq, file_priv->cmdq_limit,
+ &file_priv->cmdq_id_next, GFP_KERNEL);
+ if (ret < 0) {
+ ivpu_err(vdev, "Failed to allocate command queue ID: %d\n", ret);
+ goto err_free_cmdq;
+ }
+
+ cmdq->entry_count = ivpu_cmdq_get_entry_count(cmdq);
+ cmdq->priority = priority;
+
+ cmdq->jobq = (struct vpu_job_queue *)ivpu_bo_vaddr(cmdq->mem);
+ cmdq->jobq->header.engine_idx = VPU_ENGINE_COMPUTE;
+ cmdq->jobq->header.flags = ivpu_cmdq_get_flags(vdev, flags);
+
+ ivpu_dbg(vdev, JOB, "Command queue %d created, ctx %d, flags 0x%08x\n",
+ cmdq->id, file_priv->ctx.id, cmdq->jobq->header.flags);
+ return cmdq;
+
+err_free_cmdq:
+ ivpu_cmdq_free(file_priv, cmdq);
+ return NULL;
+}
+
+static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 engine,
+ u8 priority)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ int ret;
+
+ ret = ivpu_jsm_hws_create_cmdq(vdev, file_priv->ctx.id, file_priv->ctx.id, cmdq->id,
+ task_pid_nr(current), engine,
+ cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
+ if (ret)
+ return ret;
+
+ ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id,
+ priority);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ int ret;
+
+ ret = xa_alloc_cyclic(&vdev->db_xa, &cmdq->db_id, NULL, vdev->db_limit, &vdev->db_next,
+ GFP_KERNEL);
+ if (ret < 0) {
+ ivpu_err(vdev, "Failed to allocate doorbell ID: %d\n", ret);
+ return ret;
+ }
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
+ ret = ivpu_jsm_hws_register_db(vdev, file_priv->ctx.id, cmdq->id, cmdq->db_id,
+ cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
+ else
+ ret = ivpu_jsm_register_db(vdev, file_priv->ctx.id, cmdq->db_id,
+ cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
+
+ if (!ret) {
+ ivpu_dbg(vdev, JOB, "DB %d registered to cmdq %d ctx %d priority %d\n",
+ cmdq->db_id, cmdq->id, file_priv->ctx.id, cmdq->priority);
+ } else {
+ xa_erase(&vdev->db_xa, cmdq->db_id);
+ cmdq->db_id = 0;
+ }
+
+ return ret;
+}
+
+static void ivpu_cmdq_jobq_reset(struct ivpu_device *vdev, struct vpu_job_queue *jobq)
+{
+ jobq->header.head = 0;
+ jobq->header.tail = 0;
+
+ wmb(); /* Flush WC buffer for jobq->header */
+}
+
+static int ivpu_cmdq_register(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ int ret;
+
+ lockdep_assert_held(&file_priv->lock);
+
+ if (cmdq->db_id)
+ return 0;
+
+ ivpu_cmdq_jobq_reset(vdev, cmdq->jobq);
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
+ ret = ivpu_hws_cmdq_init(file_priv, cmdq, VPU_ENGINE_COMPUTE, cmdq->priority);
+ if (ret)
+ return ret;
+ }
+
+ ret = ivpu_register_db(file_priv, cmdq);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ivpu_cmdq_unregister(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ int ret;
+
+ lockdep_assert_held(&file_priv->lock);
+
+ if (!cmdq->db_id)
+ return 0;
+
+ ret = ivpu_jsm_unregister_db(vdev, cmdq->db_id);
+ if (!ret)
+ ivpu_dbg(vdev, JOB, "DB %d unregistered\n", cmdq->db_id);
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
+ ret = ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id);
+ if (!ret)
+ ivpu_dbg(vdev, JOB, "Command queue %d destroyed, ctx %d\n",
+ cmdq->id, file_priv->ctx.id);
+ }
+
+ xa_erase(&file_priv->vdev->db_xa, cmdq->db_id);
+ cmdq->db_id = 0;
+
+ return 0;
+}
+
+static inline u8 ivpu_job_to_jsm_priority(u8 priority)
+{
+ if (priority == DRM_IVPU_JOB_PRIORITY_DEFAULT)
+ return VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL;
+
+ return priority - 1;
+}
+
+static void ivpu_cmdq_destroy(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
+{
+ ivpu_cmdq_unregister(file_priv, cmdq);
+ xa_erase(&file_priv->cmdq_xa, cmdq->id);
+ ivpu_cmdq_free(file_priv, cmdq);
+}
+
+static struct ivpu_cmdq *ivpu_cmdq_acquire_legacy(struct ivpu_file_priv *file_priv, u8 priority)
+{
+ struct ivpu_cmdq *cmdq;
+ unsigned long id;
+
+ lockdep_assert_held(&file_priv->lock);
+
+ xa_for_each(&file_priv->cmdq_xa, id, cmdq)
+ if (cmdq->is_legacy && cmdq->priority == priority)
+ break;
+
+ if (!cmdq) {
+ cmdq = ivpu_cmdq_create(file_priv, priority, 0);
+ if (!cmdq)
+ return NULL;
+ cmdq->is_legacy = true;
+ }
+
+ return cmdq;
+}
+
+static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u32 cmdq_id)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_cmdq *cmdq;
+
+ lockdep_assert_held(&file_priv->lock);
+
+ cmdq = xa_load(&file_priv->cmdq_xa, cmdq_id);
+ if (!cmdq) {
+ ivpu_dbg(vdev, IOCTL, "Failed to find command queue with ID: %u\n", cmdq_id);
+ return NULL;
+ }
+
+ return cmdq;
+}
+
+void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv)
+{
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
+
+ lockdep_assert_held(&file_priv->lock);
+
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
+ ivpu_cmdq_destroy(file_priv, cmdq);
+}
+
+/*
+ * Mark the doorbell as unregistered
+ * This function needs to be called when the VPU hardware is restarted
+ * and FW loses job queue state. The next time job queue is used it
+ * will be registered again.
+ */
+static void ivpu_cmdq_reset(struct ivpu_file_priv *file_priv)
+{
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
+
+ mutex_lock(&file_priv->lock);
+
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) {
+ xa_erase(&file_priv->vdev->db_xa, cmdq->db_id);
+ cmdq->db_id = 0;
+ }
+
+ mutex_unlock(&file_priv->lock);
+}
+
+void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev)
+{
+ struct ivpu_file_priv *file_priv;
+ unsigned long ctx_id;
+
+ mutex_lock(&vdev->context_list_lock);
+
+ xa_for_each(&vdev->context_xa, ctx_id, file_priv)
+ ivpu_cmdq_reset(file_priv);
+
+ mutex_unlock(&vdev->context_list_lock);
+}
+
+void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
+
+ lockdep_assert_held(&file_priv->lock);
+ ivpu_dbg(vdev, JOB, "Context ID: %u abort\n", file_priv->ctx.id);
+
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
+ ivpu_cmdq_unregister(file_priv, cmdq);
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_OS)
+ ivpu_jsm_context_release(vdev, file_priv->ctx.id);
+
+ ivpu_mmu_disable_ssid_events(vdev, file_priv->ctx.id);
+
+ file_priv->aborted = true;
+}
+
+static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job)
+{
+ struct ivpu_device *vdev = job->vdev;
+ struct vpu_job_queue_header *header = &cmdq->jobq->header;
+ struct vpu_job_queue_entry *entry;
+ u32 tail = READ_ONCE(header->tail);
+ u32 next_entry = (tail + 1) % cmdq->entry_count;
+
+ /* Check if there is space left in job queue */
+ if (next_entry == header->head) {
+ ivpu_dbg(vdev, JOB, "Job queue full: ctx %d cmdq %d db %d head %d tail %d\n",
+ job->file_priv->ctx.id, cmdq->id, cmdq->db_id, header->head, tail);
+ return -EBUSY;
+ }
+
+ entry = &cmdq->jobq->slot[tail].job;
+ entry->batch_buf_addr = job->cmd_buf_vpu_addr;
+ entry->job_id = job->job_id;
+ entry->flags = 0;
+ if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_SUBMISSION))
+ entry->flags = VPU_JOB_FLAGS_NULL_SUBMISSION_MASK;
+
+ if (job->primary_preempt_buf) {
+ entry->primary_preempt_buf_addr = job->primary_preempt_buf->vpu_addr;
+ entry->primary_preempt_buf_size = ivpu_bo_size(job->primary_preempt_buf);
+ }
+
+ if (job->secondary_preempt_buf) {
+ entry->secondary_preempt_buf_addr = job->secondary_preempt_buf->vpu_addr;
+ entry->secondary_preempt_buf_size = ivpu_bo_size(job->secondary_preempt_buf);
+ }
+
+ wmb(); /* Ensure that tail is updated after filling entry */
+ header->tail = next_entry;
+ wmb(); /* Flush WC buffer for jobq header */
+
+ return 0;
+}
+
+struct ivpu_fence {
+ struct dma_fence base;
+ spinlock_t lock; /* protects base */
+ struct ivpu_device *vdev;
+};
+
+static inline struct ivpu_fence *to_vpu_fence(struct dma_fence *fence)
+{
+ return container_of(fence, struct ivpu_fence, base);
+}
+
+static const char *ivpu_fence_get_driver_name(struct dma_fence *fence)
+{
+ return DRIVER_NAME;
+}
+
+static const char *ivpu_fence_get_timeline_name(struct dma_fence *fence)
+{
+ struct ivpu_fence *ivpu_fence = to_vpu_fence(fence);
+
+ return dev_name(ivpu_fence->vdev->drm.dev);
+}
+
+static const struct dma_fence_ops ivpu_fence_ops = {
+ .get_driver_name = ivpu_fence_get_driver_name,
+ .get_timeline_name = ivpu_fence_get_timeline_name,
+};
+
+static struct dma_fence *ivpu_fence_create(struct ivpu_device *vdev)
+{
+ struct ivpu_fence *fence;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return NULL;
+
+ fence->vdev = vdev;
+ spin_lock_init(&fence->lock);
+ dma_fence_init(&fence->base, &ivpu_fence_ops, &fence->lock, dma_fence_context_alloc(1), 1);
+
+ return &fence->base;
+}
+
+static void ivpu_job_destroy(struct ivpu_job *job)
+{
+ struct ivpu_device *vdev = job->vdev;
+ u32 i;
+
+ ivpu_dbg(vdev, JOB, "Job destroyed: id %3u ctx %2d cmdq_id %u engine %d",
+ job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx);
+
+ for (i = 0; i < job->bo_count; i++)
+ if (job->bos[i])
+ drm_gem_object_put(&job->bos[i]->base.base);
+
+ dma_fence_put(job->done_fence);
+ ivpu_file_priv_put(&job->file_priv);
+ kfree(job);
+}
+
+static struct ivpu_job *
+ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_job *job;
+
+ job = kzalloc(struct_size(job, bos, bo_count), GFP_KERNEL);
+ if (!job)
+ return NULL;
+
+ job->vdev = vdev;
+ job->engine_idx = engine_idx;
+ job->bo_count = bo_count;
+ job->done_fence = ivpu_fence_create(vdev);
+ if (!job->done_fence) {
+ ivpu_err(vdev, "Failed to create a fence\n");
+ goto err_free_job;
+ }
+
+ job->file_priv = ivpu_file_priv_get(file_priv);
+
+ trace_job("create", job);
+ ivpu_dbg(vdev, JOB, "Job created: ctx %2d engine %d", file_priv->ctx.id, job->engine_idx);
+ return job;
+
+err_free_job:
+ kfree(job);
+ return NULL;
+}
+
+static struct ivpu_job *ivpu_job_remove_from_submitted_jobs(struct ivpu_device *vdev, u32 job_id)
+{
+ struct ivpu_job *job;
+
+ lockdep_assert_held(&vdev->submitted_jobs_lock);
+
+ job = xa_erase(&vdev->submitted_jobs_xa, job_id);
+ if (xa_empty(&vdev->submitted_jobs_xa) && job) {
+ vdev->busy_time = ktime_add(ktime_sub(ktime_get(), vdev->busy_start_ts),
+ vdev->busy_time);
+ }
+
+ return job;
+}
+
+bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status)
+{
+ lockdep_assert_held(&vdev->submitted_jobs_lock);
+
+ switch (job_status) {
+ case VPU_JSM_STATUS_PROCESSING_ERR:
+ case VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN ... VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX:
+ {
+ struct ivpu_job *job = xa_load(&vdev->submitted_jobs_xa, job_id);
+
+ if (!job)
+ return false;
+
+ /* Trigger an engine reset */
+ guard(mutex)(&job->file_priv->lock);
+
+ job->job_status = job_status;
+
+ if (job->file_priv->has_mmu_faults)
+ return false;
+
+ /*
+ * Mark context as faulty and defer destruction of the job to jobs abort thread
+ * handler to synchronize between both faults and jobs returning context violation
+ * status and ensure both are handled in the same way
+ */
+ job->file_priv->has_mmu_faults = true;
+ queue_work(system_percpu_wq, &vdev->context_abort_work);
+ return true;
+ }
+ default:
+ /* Complete job with error status, engine reset not required */
+ break;
+ }
+
+ return false;
+}
+
+static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
+{
+ struct ivpu_job *job;
+
+ lockdep_assert_held(&vdev->submitted_jobs_lock);
+
+ job = xa_load(&vdev->submitted_jobs_xa, job_id);
+ if (!job)
+ return -ENOENT;
+
+ ivpu_job_remove_from_submitted_jobs(vdev, job_id);
+
+ if (job->job_status == VPU_JSM_STATUS_SUCCESS) {
+ if (job->file_priv->has_mmu_faults)
+ job->job_status = DRM_IVPU_JOB_STATUS_ABORTED;
+ else
+ job->job_status = job_status;
+ }
+
+ job->bos[CMD_BUF_IDX]->job_status = job->job_status;
+ dma_fence_signal(job->done_fence);
+
+ trace_job("done", job);
+ ivpu_dbg(vdev, JOB, "Job complete: id %3u ctx %2d cmdq_id %u engine %d status 0x%x\n",
+ job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx,
+ job->job_status);
+
+ ivpu_job_destroy(job);
+ ivpu_stop_job_timeout_detection(vdev);
+
+ ivpu_rpm_put(vdev);
+
+ if (!xa_empty(&vdev->submitted_jobs_xa))
+ ivpu_start_job_timeout_detection(vdev);
+
+ return 0;
+}
+
+void ivpu_jobs_abort_all(struct ivpu_device *vdev)
+{
+ struct ivpu_job *job;
+ unsigned long id;
+
+ mutex_lock(&vdev->submitted_jobs_lock);
+
+ xa_for_each(&vdev->submitted_jobs_xa, id, job)
+ ivpu_job_signal_and_destroy(vdev, id, DRM_IVPU_JOB_STATUS_ABORTED);
+
+ mutex_unlock(&vdev->submitted_jobs_lock);
+}
+
+void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id)
+{
+ struct ivpu_job *job;
+ unsigned long id;
+
+ mutex_lock(&vdev->submitted_jobs_lock);
+
+ xa_for_each(&vdev->submitted_jobs_xa, id, job)
+ if (job->file_priv->ctx.id == ctx_id && job->cmdq_id == cmdq_id)
+ ivpu_job_signal_and_destroy(vdev, id, DRM_IVPU_JOB_STATUS_ABORTED);
+
+ mutex_unlock(&vdev->submitted_jobs_lock);
+}
+
+static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
+{
+ struct ivpu_file_priv *file_priv = job->file_priv;
+ struct ivpu_device *vdev = job->vdev;
+ struct ivpu_cmdq *cmdq;
+ bool is_first_job;
+ int ret;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&vdev->submitted_jobs_lock);
+ mutex_lock(&file_priv->lock);
+
+ if (cmdq_id == 0)
+ cmdq = ivpu_cmdq_acquire_legacy(file_priv, priority);
+ else
+ cmdq = ivpu_cmdq_acquire(file_priv, cmdq_id);
+ if (!cmdq) {
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
+ ret = ivpu_cmdq_register(file_priv, cmdq);
+ if (ret) {
+ ivpu_err(vdev, "Failed to register command queue: %d\n", ret);
+ goto err_unlock;
+ }
+
+ ret = ivpu_preemption_job_init(vdev, file_priv, cmdq, job);
+ if (ret) {
+ ivpu_err(vdev, "Failed to initialize preemption buffers for job %d: %d\n",
+ job->job_id, ret);
+ goto err_unlock;
+ }
+
+ job->cmdq_id = cmdq->id;
+
+ is_first_job = xa_empty(&vdev->submitted_jobs_xa);
+ ret = xa_alloc_cyclic(&vdev->submitted_jobs_xa, &job->job_id, job, file_priv->job_limit,
+ &file_priv->job_id_next, GFP_KERNEL);
+ if (ret < 0) {
+ ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n",
+ file_priv->ctx.id);
+ ret = -EBUSY;
+ goto err_unlock;
+ }
+
+ ret = ivpu_cmdq_push_job(cmdq, job);
+ if (ret)
+ goto err_erase_xa;
+
+ ivpu_start_job_timeout_detection(vdev);
+
+ if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_HW)) {
+ cmdq->jobq->header.head = cmdq->jobq->header.tail;
+ wmb(); /* Flush WC buffer for jobq header */
+ } else {
+ ivpu_cmdq_ring_db(vdev, cmdq);
+ if (is_first_job)
+ vdev->busy_start_ts = ktime_get();
+ }
+
+ trace_job("submit", job);
+ ivpu_dbg(vdev, JOB, "Job submitted: id %3u ctx %2d cmdq_id %u engine %d prio %d addr 0x%llx next %d\n",
+ job->job_id, file_priv->ctx.id, cmdq->id, job->engine_idx, cmdq->priority,
+ job->cmd_buf_vpu_addr, cmdq->jobq->header.tail);
+
+ mutex_unlock(&file_priv->lock);
+
+ if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_HW)) {
+ ivpu_job_signal_and_destroy(vdev, job->job_id, VPU_JSM_STATUS_SUCCESS);
+ }
+
+ mutex_unlock(&vdev->submitted_jobs_lock);
+
+ return 0;
+
+err_erase_xa:
+ xa_erase(&vdev->submitted_jobs_xa, job->job_id);
+err_unlock:
+ mutex_unlock(&file_priv->lock);
+ mutex_unlock(&vdev->submitted_jobs_lock);
+ ivpu_rpm_put(vdev);
+ return ret;
+}
+
+static int
+ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 *buf_handles,
+ u32 buf_count, u32 commands_offset, u32 preempt_buffer_index)
+{
+ struct ivpu_file_priv *file_priv = job->file_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ww_acquire_ctx acquire_ctx;
+ enum dma_resv_usage usage;
+ struct ivpu_bo *bo;
+ int ret;
+ u32 i;
+
+ for (i = 0; i < buf_count; i++) {
+ struct drm_gem_object *obj = drm_gem_object_lookup(file, buf_handles[i]);
+
+ if (!obj) {
+ ivpu_dbg(vdev, IOCTL, "Failed to lookup GEM object with handle %u\n",
+ buf_handles[i]);
+ return -ENOENT;
+ }
+
+ job->bos[i] = to_ivpu_bo(obj);
+
+ ret = ivpu_bo_bind(job->bos[i]);
+ if (ret)
+ return ret;
+ }
+
+ bo = job->bos[CMD_BUF_IDX];
+ if (!dma_resv_test_signaled(bo->base.base.resv, DMA_RESV_USAGE_READ)) {
+ ivpu_dbg(vdev, IOCTL, "Buffer is already in use by another job\n");
+ return -EBUSY;
+ }
+
+ if (commands_offset >= ivpu_bo_size(bo)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid commands offset %u for buffer size %zu\n",
+ commands_offset, ivpu_bo_size(bo));
+ return -EINVAL;
+ }
+
+ job->cmd_buf_vpu_addr = bo->vpu_addr + commands_offset;
+
+ if (preempt_buffer_index) {
+ struct ivpu_bo *preempt_bo = job->bos[preempt_buffer_index];
+
+ if (ivpu_bo_size(preempt_bo) < ivpu_fw_preempt_buf_size(vdev)) {
+ ivpu_dbg(vdev, IOCTL, "Preemption buffer is too small\n");
+ return -EINVAL;
+ }
+ if (ivpu_bo_is_mappable(preempt_bo)) {
+ ivpu_dbg(vdev, IOCTL, "Preemption buffer cannot be mappable\n");
+ return -EINVAL;
+ }
+ job->primary_preempt_buf = preempt_bo;
+ }
+
+ ret = drm_gem_lock_reservations((struct drm_gem_object **)job->bos, buf_count,
+ &acquire_ctx);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "Failed to lock reservations: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < buf_count; i++) {
+ ret = dma_resv_reserve_fences(job->bos[i]->base.base.resv, 1);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "Failed to reserve fences: %d\n", ret);
+ goto unlock_reservations;
+ }
+ }
+
+ for (i = 0; i < buf_count; i++) {
+ usage = (i == CMD_BUF_IDX) ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_BOOKKEEP;
+ dma_resv_add_fence(job->bos[i]->base.base.resv, job->done_fence, usage);
+ }
+
+unlock_reservations:
+ drm_gem_unlock_reservations((struct drm_gem_object **)job->bos, buf_count, &acquire_ctx);
+
+ wmb(); /* Flush write combining buffers */
+
+ return ret;
+}
+
+static int ivpu_submit(struct drm_file *file, struct ivpu_file_priv *file_priv, u32 cmdq_id,
+ u32 buffer_count, u32 engine, void __user *buffers_ptr, u32 cmds_offset,
+ u32 preempt_buffer_index, u8 priority)
+{
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_job *job;
+ u32 *buf_handles;
+ int idx, ret;
+
+ buf_handles = kcalloc(buffer_count, sizeof(u32), GFP_KERNEL);
+ if (!buf_handles)
+ return -ENOMEM;
+
+ ret = copy_from_user(buf_handles, buffers_ptr, buffer_count * sizeof(u32));
+ if (ret) {
+ ret = -EFAULT;
+ goto err_free_handles;
+ }
+
+ if (!drm_dev_enter(&vdev->drm, &idx)) {
+ ret = -ENODEV;
+ goto err_free_handles;
+ }
+
+ ivpu_dbg(vdev, JOB, "Submit ioctl: ctx %u cmdq_id %u buf_count %u\n",
+ file_priv->ctx.id, cmdq_id, buffer_count);
+
+ job = ivpu_job_create(file_priv, engine, buffer_count);
+ if (!job) {
+ ret = -ENOMEM;
+ goto err_exit_dev;
+ }
+
+ ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, buffer_count, cmds_offset,
+ preempt_buffer_index);
+ if (ret)
+ goto err_destroy_job;
+
+ down_read(&vdev->pm->reset_lock);
+ ret = ivpu_job_submit(job, priority, cmdq_id);
+ up_read(&vdev->pm->reset_lock);
+ if (ret)
+ goto err_signal_fence;
+
+ drm_dev_exit(idx);
+ kfree(buf_handles);
+ return ret;
+
+err_signal_fence:
+ dma_fence_signal(job->done_fence);
+err_destroy_job:
+ ivpu_job_destroy(job);
+err_exit_dev:
+ drm_dev_exit(idx);
+err_free_handles:
+ kfree(buf_handles);
+ return ret;
+}
+
+int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct drm_ivpu_submit *args = data;
+ u8 priority;
+
+ if (args->engine != DRM_IVPU_ENGINE_COMPUTE) {
+ ivpu_dbg(vdev, IOCTL, "Invalid engine %d\n", args->engine);
+ return -EINVAL;
+ }
+
+ if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME) {
+ ivpu_dbg(vdev, IOCTL, "Invalid priority %d\n", args->priority);
+ return -EINVAL;
+ }
+
+ if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT) {
+ ivpu_dbg(vdev, IOCTL, "Invalid buffer count %u\n", args->buffer_count);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(args->commands_offset, 8)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid commands offset %u\n", args->commands_offset);
+ return -EINVAL;
+ }
+
+ if (!file_priv->ctx.id) {
+ ivpu_dbg(vdev, IOCTL, "Context not initialized\n");
+ return -EINVAL;
+ }
+
+ if (file_priv->has_mmu_faults) {
+ ivpu_dbg(vdev, IOCTL, "Context %u has MMU faults\n", file_priv->ctx.id);
+ return -EBADFD;
+ }
+
+ priority = ivpu_job_to_jsm_priority(args->priority);
+
+ return ivpu_submit(file, file_priv, 0, args->buffer_count, args->engine,
+ (void __user *)args->buffers_ptr, args->commands_offset, 0, priority);
+}
+
+int ivpu_cmdq_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct drm_ivpu_cmdq_submit *args = data;
+
+ if (!ivpu_is_capable(file_priv->vdev, DRM_IVPU_CAP_MANAGE_CMDQ)) {
+ ivpu_dbg(vdev, IOCTL, "Command queue management not supported\n");
+ return -ENODEV;
+ }
+
+ if (args->cmdq_id < IVPU_CMDQ_MIN_ID || args->cmdq_id > IVPU_CMDQ_MAX_ID) {
+ ivpu_dbg(vdev, IOCTL, "Invalid command queue ID %u\n", args->cmdq_id);
+ return -EINVAL;
+ }
+
+ if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT) {
+ ivpu_dbg(vdev, IOCTL, "Invalid buffer count %u\n", args->buffer_count);
+ return -EINVAL;
+ }
+
+ if (args->preempt_buffer_index >= args->buffer_count) {
+ ivpu_dbg(vdev, IOCTL, "Invalid preemption buffer index %u\n",
+ args->preempt_buffer_index);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(args->commands_offset, 8)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid commands offset %u\n", args->commands_offset);
+ return -EINVAL;
+ }
+
+ if (!file_priv->ctx.id) {
+ ivpu_dbg(vdev, IOCTL, "Context not initialized\n");
+ return -EINVAL;
+ }
+
+ if (file_priv->has_mmu_faults) {
+ ivpu_dbg(vdev, IOCTL, "Context %u has MMU faults\n", file_priv->ctx.id);
+ return -EBADFD;
+ }
+
+ return ivpu_submit(file, file_priv, args->cmdq_id, args->buffer_count, VPU_ENGINE_COMPUTE,
+ (void __user *)args->buffers_ptr, args->commands_offset,
+ args->preempt_buffer_index, 0);
+}
+
+int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct drm_ivpu_cmdq_create *args = data;
+ struct ivpu_cmdq *cmdq;
+ int ret;
+
+ if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ)) {
+ ivpu_dbg(vdev, IOCTL, "Command queue management not supported\n");
+ return -ENODEV;
+ }
+
+ if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME) {
+ ivpu_dbg(vdev, IOCTL, "Invalid priority %d\n", args->priority);
+ return -EINVAL;
+ }
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&file_priv->lock);
+
+ cmdq = ivpu_cmdq_create(file_priv, ivpu_job_to_jsm_priority(args->priority), args->flags);
+ if (cmdq)
+ args->cmdq_id = cmdq->id;
+
+ mutex_unlock(&file_priv->lock);
+
+ ivpu_rpm_put(vdev);
+
+ return cmdq ? 0 : -ENOMEM;
+}
+
+int ivpu_cmdq_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct drm_ivpu_cmdq_destroy *args = data;
+ struct ivpu_cmdq *cmdq;
+ u32 cmdq_id = 0;
+ int ret;
+
+ if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ)) {
+ ivpu_dbg(vdev, IOCTL, "Command queue management not supported\n");
+ return -ENODEV;
+ }
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&file_priv->lock);
+
+ cmdq = xa_load(&file_priv->cmdq_xa, args->cmdq_id);
+ if (!cmdq || cmdq->is_legacy) {
+ ret = -ENOENT;
+ } else {
+ cmdq_id = cmdq->id;
+ ivpu_cmdq_destroy(file_priv, cmdq);
+ ret = 0;
+ }
+
+ mutex_unlock(&file_priv->lock);
+
+ /* Abort any pending jobs only if cmdq was destroyed */
+ if (!ret)
+ ivpu_cmdq_abort_all_jobs(vdev, file_priv->ctx.id, cmdq_id);
+
+ ivpu_rpm_put(vdev);
+
+ return ret;
+}
+
+static void
+ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
+ struct vpu_jsm_msg *jsm_msg)
+{
+ struct vpu_ipc_msg_payload_job_done *payload;
+
+ if (!jsm_msg) {
+ ivpu_err(vdev, "IPC message has no JSM payload\n");
+ return;
+ }
+
+ if (jsm_msg->result != VPU_JSM_STATUS_SUCCESS) {
+ ivpu_err(vdev, "Invalid JSM message result: %d\n", jsm_msg->result);
+ return;
+ }
+
+ payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload;
+
+ mutex_lock(&vdev->submitted_jobs_lock);
+ if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status))
+ /* No engine error, complete the job normally */
+ ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
+ mutex_unlock(&vdev->submitted_jobs_lock);
+}
+
+void ivpu_job_done_consumer_init(struct ivpu_device *vdev)
+{
+ ivpu_ipc_consumer_add(vdev, &vdev->job_done_consumer,
+ VPU_IPC_CHAN_JOB_RET, ivpu_job_done_callback);
+}
+
+void ivpu_job_done_consumer_fini(struct ivpu_device *vdev)
+{
+ ivpu_ipc_consumer_del(vdev, &vdev->job_done_consumer);
+}
+
+void ivpu_context_abort_work_fn(struct work_struct *work)
+{
+ struct ivpu_device *vdev = container_of(work, struct ivpu_device, context_abort_work);
+ struct ivpu_file_priv *file_priv;
+ struct ivpu_job *job;
+ unsigned long ctx_id;
+ unsigned long id;
+
+ if (drm_WARN_ON(&vdev->drm, pm_runtime_get_if_active(vdev->drm.dev) <= 0))
+ return;
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
+ if (ivpu_jsm_reset_engine(vdev, 0))
+ goto runtime_put;
+
+ mutex_lock(&vdev->context_list_lock);
+ xa_for_each(&vdev->context_xa, ctx_id, file_priv) {
+ if (!file_priv->has_mmu_faults || file_priv->aborted)
+ continue;
+
+ mutex_lock(&file_priv->lock);
+ ivpu_context_abort_locked(file_priv);
+ mutex_unlock(&file_priv->lock);
+ }
+ mutex_unlock(&vdev->context_list_lock);
+
+ /*
+ * We will not receive new MMU event interrupts until existing events are discarded
+ * however, we want to discard these events only after aborting the faulty context
+ * to avoid generating new faults from that context
+ */
+ ivpu_mmu_discard_events(vdev);
+
+ if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW)
+ goto runtime_put;
+
+ if (ivpu_jsm_hws_resume_engine(vdev, 0))
+ goto runtime_put;
+ /*
+ * In hardware scheduling mode NPU already has stopped processing jobs
+ * and won't send us any further notifications, thus we have to free job related resources
+ * and notify userspace
+ */
+ mutex_lock(&vdev->submitted_jobs_lock);
+ xa_for_each(&vdev->submitted_jobs_xa, id, job)
+ if (job->file_priv->aborted)
+ ivpu_job_signal_and_destroy(vdev, job->job_id, DRM_IVPU_JOB_STATUS_ABORTED);
+ mutex_unlock(&vdev->submitted_jobs_lock);
+
+runtime_put:
+ pm_runtime_put_autosuspend(vdev->drm.dev);
+}
diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h
new file mode 100644
index 000000000000..3ab61e6a5616
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_job.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#ifndef __IVPU_JOB_H__
+#define __IVPU_JOB_H__
+
+#include <linux/kref.h>
+#include <linux/idr.h>
+
+#include "ivpu_gem.h"
+
+struct ivpu_device;
+struct ivpu_file_priv;
+
+/**
+ * struct ivpu_cmdq - Represents a command queue for submitting jobs to the VPU.
+ * Tracks queue memory, preemption buffers, and metadata for job management.
+ * @jobq: Pointer to job queue memory shared with the device
+ * @primary_preempt_buf: Primary preemption buffer for this queue (optional)
+ * @secondary_preempt_buf: Secondary preemption buffer for this queue (optional)
+ * @mem: Memory allocated for the job queue, shared with device
+ * @entry_count: Number of job entries in the queue
+ * @id: Unique command queue ID
+ * @db_id: Doorbell ID assigned to this job queue
+ * @priority: Priority level of the command queue
+ * @is_legacy: True if this is a legacy command queue
+ */
+struct ivpu_cmdq {
+ struct vpu_job_queue *jobq;
+ struct ivpu_bo *primary_preempt_buf;
+ struct ivpu_bo *secondary_preempt_buf;
+ struct ivpu_bo *mem;
+ u32 entry_count;
+ u32 id;
+ u32 db_id;
+ u8 priority;
+ bool is_legacy;
+};
+
+/**
+ * struct ivpu_job - Representing a batch or DMA buffer submitted to the VPU.
+ * Each job is a unit of execution, tracked by job_id for status reporting from VPU FW.
+ * The structure holds all resources and metadata needed for job submission, execution,
+ * and completion handling.
+ * @vdev: Pointer to the VPU device
+ * @file_priv: The client context that submitted this job
+ * @done_fence: Fence signaled when job completes
+ * @cmd_buf_vpu_addr: VPU address of the command buffer for this job
+ * @cmdq_id: Command queue ID used for submission
+ * @job_id: Unique job ID for tracking and status reporting
+ * @engine_idx: Engine index for job execution
+ * @job_status: Status reported by firmware for this job
+ * @primary_preempt_buf: Primary preemption buffer for job
+ * @secondary_preempt_buf: Secondary preemption buffer for job (optional)
+ * @bo_count: Number of buffer objects associated with this job
+ * @bos: Array of buffer objects used by the job (batch buffer is at index 0)
+ */
+struct ivpu_job {
+ struct ivpu_device *vdev;
+ struct ivpu_file_priv *file_priv;
+ struct dma_fence *done_fence;
+ u64 cmd_buf_vpu_addr;
+ u32 cmdq_id;
+ u32 job_id;
+ u32 engine_idx;
+ u32 job_status;
+ struct ivpu_bo *primary_preempt_buf;
+ struct ivpu_bo *secondary_preempt_buf;
+ size_t bo_count;
+ struct ivpu_bo *bos[] __counted_by(bo_count);
+};
+
+int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_cmdq_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_cmdq_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+
+void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv);
+
+void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv);
+void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev);
+void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id);
+
+void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
+void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
+bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status);
+void ivpu_context_abort_work_fn(struct work_struct *work);
+
+void ivpu_jobs_abort_all(struct ivpu_device *vdev);
+
+#endif /* __IVPU_JOB_H__ */
diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c
new file mode 100644
index 000000000000..0256b2dfefc1
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_jsm_msg.c
@@ -0,0 +1,562 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include "ivpu_drv.h"
+#include "ivpu_hw.h"
+#include "ivpu_ipc.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_pm.h"
+#include "vpu_jsm_api.h"
+
+const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type)
+{
+ #define IVPU_CASE_TO_STR(x) case x: return #x
+ switch (type) {
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_UNKNOWN);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_ENGINE_RESET);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_ENGINE_PREEMPT);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_REGISTER_DB);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_UNREGISTER_DB);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_QUERY_ENGINE_HB);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_GET_POWER_LEVEL_COUNT);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_GET_POWER_LEVEL);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_POWER_LEVEL);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_OPEN);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_CLOSE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_SET_CONFIG);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_GET_CONFIG);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_GET_CAPABILITY);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_GET_NAME);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SSID_RELEASE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_START);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_STOP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_UPDATE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_INFO);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_CREATE_CMD_QUEUE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DESTROY_CMD_QUEUE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_REGISTER_DB);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_RESUME_CMDQ);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SUSPEND_CMDQ);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_ENGINE_RESUME);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_STATE_DUMP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_STATE_DUMP_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_BLOB_DEINIT_DEPRECATED);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DYNDBG_CONTROL);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_JOB_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_NATIVE_FENCE_SIGNALLED);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_ENGINE_RESET_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_ENGINE_PREEMPT_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_REGISTER_DB_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_UNREGISTER_DB_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_QUERY_ENGINE_HB_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_GET_POWER_LEVEL_COUNT_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_GET_POWER_LEVEL_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_POWER_LEVEL_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_OPEN_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_CLOSE_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_SET_CONFIG_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_GET_CONFIG_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_GET_CAPABILITY_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_TRACE_GET_NAME_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SSID_RELEASE_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_START_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_STOP_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_UPDATE_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_INFO_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_METRIC_STREAMER_NOTIFICATION);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_BLOB_DEINIT_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DYNDBG_CONTROL_RSP);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_PWR_D0I3_ENTER);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_PWR_D0I3_ENTER_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE_DONE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE);
+ IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE_DONE);
+ }
+ #undef IVPU_CASE_TO_STR
+
+ return "Unknown JSM message type";
+}
+
+int ivpu_jsm_register_db(struct ivpu_device *vdev, u32 ctx_id, u32 db_id,
+ u64 jobq_base, u32 jobq_size)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_REGISTER_DB };
+ struct vpu_jsm_msg resp;
+ int ret = 0;
+
+ req.payload.register_db.db_idx = db_id;
+ req.payload.register_db.jobq_base = jobq_base;
+ req.payload.register_db.jobq_size = jobq_size;
+ req.payload.register_db.host_ssid = ctx_id;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_REGISTER_DB_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_err_ratelimited(vdev, "Failed to register doorbell %u: %d\n", db_id, ret);
+
+ return ret;
+}
+
+int ivpu_jsm_unregister_db(struct ivpu_device *vdev, u32 db_id)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_UNREGISTER_DB };
+ struct vpu_jsm_msg resp;
+ int ret = 0;
+
+ req.payload.unregister_db.db_idx = db_id;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_UNREGISTER_DB_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to unregister doorbell %u: %d\n", db_id, ret);
+
+ return ret;
+}
+
+int ivpu_jsm_get_heartbeat(struct ivpu_device *vdev, u32 engine, u64 *heartbeat)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_QUERY_ENGINE_HB };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ if (engine != VPU_ENGINE_COMPUTE)
+ return -EINVAL;
+
+ req.payload.query_engine_hb.engine_idx = engine;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_err_ratelimited(vdev, "Failed to get heartbeat from engine %d: %d\n",
+ engine, ret);
+ return ret;
+ }
+
+ *heartbeat = resp.payload.query_engine_hb_done.heartbeat;
+ return ret;
+}
+
+int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_ENGINE_RESET };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ if (engine != VPU_ENGINE_COMPUTE)
+ return -EINVAL;
+
+ req.payload.engine_reset.engine_idx = engine;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_ENGINE_RESET_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_err_ratelimited(vdev, "Failed to reset engine %d: %d\n", engine, ret);
+ ivpu_pm_trigger_recovery(vdev, "Engine reset failed");
+ }
+
+ return ret;
+}
+
+int ivpu_jsm_preempt_engine(struct ivpu_device *vdev, u32 engine, u32 preempt_id)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_ENGINE_PREEMPT };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ if (engine != VPU_ENGINE_COMPUTE)
+ return -EINVAL;
+
+ req.payload.engine_preempt.engine_idx = engine;
+ req.payload.engine_preempt.preempt_id = preempt_id;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_ENGINE_PREEMPT_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_err_ratelimited(vdev, "Failed to preempt engine %d: %d\n", engine, ret);
+
+ return ret;
+}
+
+int ivpu_jsm_dyndbg_control(struct ivpu_device *vdev, char *command, size_t size)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_DYNDBG_CONTROL };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ strscpy(req.payload.dyndbg_control.dyndbg_cmd, command, VPU_DYNDBG_CMD_MAX_LEN);
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_DYNDBG_CONTROL_RSP, &resp,
+ VPU_IPC_CHAN_GEN_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to send command \"%s\": ret %d\n",
+ command, ret);
+
+ return ret;
+}
+
+int ivpu_jsm_trace_get_capability(struct ivpu_device *vdev, u32 *trace_destination_mask,
+ u64 *trace_hw_component_mask)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_TRACE_GET_CAPABILITY };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_TRACE_GET_CAPABILITY_RSP, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "Failed to get trace capability: %d\n", ret);
+ return ret;
+ }
+
+ *trace_destination_mask = resp.payload.trace_capability.trace_destination_mask;
+ *trace_hw_component_mask = resp.payload.trace_capability.trace_hw_component_mask;
+
+ return ret;
+}
+
+int ivpu_jsm_trace_set_config(struct ivpu_device *vdev, u32 trace_level, u32 trace_destination_mask,
+ u64 trace_hw_component_mask)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_TRACE_SET_CONFIG };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.trace_config.trace_level = trace_level;
+ req.payload.trace_config.trace_destination_mask = trace_destination_mask;
+ req.payload.trace_config.trace_hw_component_mask = trace_hw_component_mask;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_TRACE_SET_CONFIG_RSP, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to set config: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_context_release(struct ivpu_device *vdev, u32 host_ssid)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_SSID_RELEASE };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.ssid_release.host_ssid = host_ssid;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_SSID_RELEASE_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to release context: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_pwr_d0i3_enter(struct ivpu_device *vdev)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_PWR_D0I3_ENTER };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ if (IVPU_WA(disable_d0i3_msg))
+ return 0;
+
+ req.payload.pwr_d0i3_enter.send_response = 1;
+
+ ret = ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_PWR_D0I3_ENTER_DONE, &resp,
+ VPU_IPC_CHAN_GEN_CMD, vdev->timeout.d0i3_entry_msg);
+ if (ret)
+ return ret;
+
+ return ivpu_hw_wait_for_idle(vdev);
+}
+
+int ivpu_jsm_hws_create_cmdq(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_group, u32 cmdq_id,
+ u32 pid, u32 engine, u64 cmdq_base, u32 cmdq_size)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_CREATE_CMD_QUEUE };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.hws_create_cmdq.host_ssid = ctx_id;
+ req.payload.hws_create_cmdq.process_id = pid;
+ req.payload.hws_create_cmdq.engine_idx = engine;
+ req.payload.hws_create_cmdq.cmdq_group = cmdq_group;
+ req.payload.hws_create_cmdq.cmdq_id = cmdq_id;
+ req.payload.hws_create_cmdq.cmdq_base = cmdq_base;
+ req.payload.hws_create_cmdq.cmdq_size = cmdq_size;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to create command queue: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_hws_destroy_cmdq(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_DESTROY_CMD_QUEUE };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.hws_destroy_cmdq.host_ssid = ctx_id;
+ req.payload.hws_destroy_cmdq.cmdq_id = cmdq_id;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to destroy command queue: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_hws_register_db(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id, u32 db_id,
+ u64 cmdq_base, u32 cmdq_size)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_HWS_REGISTER_DB };
+ struct vpu_jsm_msg resp;
+ int ret = 0;
+
+ req.payload.hws_register_db.db_id = db_id;
+ req.payload.hws_register_db.host_ssid = ctx_id;
+ req.payload.hws_register_db.cmdq_id = cmdq_id;
+ req.payload.hws_register_db.cmdq_base = cmdq_base;
+ req.payload.hws_register_db.cmdq_size = cmdq_size;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_REGISTER_DB_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_err_ratelimited(vdev, "Failed to register doorbell %u: %d\n", db_id, ret);
+
+ return ret;
+}
+
+int ivpu_jsm_hws_resume_engine(struct ivpu_device *vdev, u32 engine)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_HWS_ENGINE_RESUME };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ if (engine != VPU_ENGINE_COMPUTE)
+ return -EINVAL;
+
+ req.payload.hws_resume_engine.engine_idx = engine;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_err_ratelimited(vdev, "Failed to resume engine %d: %d\n", engine, ret);
+ ivpu_pm_trigger_recovery(vdev, "Engine resume failed");
+ }
+
+ return ret;
+}
+
+int ivpu_jsm_hws_set_context_sched_properties(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id,
+ u32 priority)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.hws_set_context_sched_properties.host_ssid = ctx_id;
+ req.payload.hws_set_context_sched_properties.cmdq_id = cmdq_id;
+ req.payload.hws_set_context_sched_properties.priority_band = priority;
+ req.payload.hws_set_context_sched_properties.realtime_priority_level = 0;
+ req.payload.hws_set_context_sched_properties.in_process_priority = 0;
+ req.payload.hws_set_context_sched_properties.context_quantum = 20000;
+ req.payload.hws_set_context_sched_properties.grace_period_same_priority = 10000;
+ req.payload.hws_set_context_sched_properties.grace_period_lower_priority = 0;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to set context sched properties: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_hws_set_scheduling_log(struct ivpu_device *vdev, u32 engine_idx, u32 host_ssid,
+ u64 vpu_log_buffer_va)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.hws_set_scheduling_log.engine_idx = engine_idx;
+ req.payload.hws_set_scheduling_log.host_ssid = host_ssid;
+ req.payload.hws_set_scheduling_log.vpu_log_buffer_va = vpu_log_buffer_va;
+ req.payload.hws_set_scheduling_log.notify_index = 0;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to set scheduling log: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_hws_setup_priority_bands(struct ivpu_device *vdev)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP };
+ struct vpu_jsm_msg resp;
+ struct ivpu_hw_info *hw = vdev->hw;
+ struct vpu_ipc_msg_payload_hws_priority_band_setup *setup =
+ &req.payload.hws_priority_band_setup;
+ int ret;
+
+ for (int band = VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE;
+ band < VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT; band++) {
+ setup->grace_period[band] = hw->hws.grace_period[band];
+ setup->process_grace_period[band] = hw->hws.process_grace_period[band];
+ setup->process_quantum[band] = hw->hws.process_quantum[band];
+ }
+ setup->normal_band_percentage = 10;
+
+ ret = ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP_RSP,
+ &resp, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to set priority bands: %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_metric_streamer_start(struct ivpu_device *vdev, u64 metric_group_mask,
+ u64 sampling_rate, u64 buffer_addr, u64 buffer_size)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_METRIC_STREAMER_START };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.metric_streamer_start.metric_group_mask = metric_group_mask;
+ req.payload.metric_streamer_start.sampling_rate = sampling_rate;
+ req.payload.metric_streamer_start.buffer_addr = buffer_addr;
+ req.payload.metric_streamer_start.buffer_size = buffer_size;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_METRIC_STREAMER_START_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "Failed to start metric streamer: ret %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int ivpu_jsm_metric_streamer_stop(struct ivpu_device *vdev, u64 metric_group_mask)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_METRIC_STREAMER_STOP };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.metric_streamer_stop.metric_group_mask = metric_group_mask;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_METRIC_STREAMER_STOP_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to stop metric streamer: ret %d\n", ret);
+
+ return ret;
+}
+
+int ivpu_jsm_metric_streamer_update(struct ivpu_device *vdev, u64 metric_group_mask,
+ u64 buffer_addr, u64 buffer_size, u64 *bytes_written)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_METRIC_STREAMER_UPDATE };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.metric_streamer_update.metric_group_mask = metric_group_mask;
+ req.payload.metric_streamer_update.buffer_addr = buffer_addr;
+ req.payload.metric_streamer_update.buffer_size = buffer_size;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_METRIC_STREAMER_UPDATE_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "Failed to update metric streamer: ret %d\n", ret);
+ return ret;
+ }
+
+ if (buffer_size && resp.payload.metric_streamer_done.bytes_written > buffer_size) {
+ ivpu_warn_ratelimited(vdev, "MS buffer overflow: bytes_written %#llx > buffer_size %#llx\n",
+ resp.payload.metric_streamer_done.bytes_written, buffer_size);
+ return -EOVERFLOW;
+ }
+
+ *bytes_written = resp.payload.metric_streamer_done.bytes_written;
+
+ return ret;
+}
+
+int ivpu_jsm_metric_streamer_info(struct ivpu_device *vdev, u64 metric_group_mask, u64 buffer_addr,
+ u64 buffer_size, u32 *sample_size, u64 *info_size)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_METRIC_STREAMER_INFO };
+ struct vpu_jsm_msg resp;
+ int ret;
+
+ req.payload.metric_streamer_start.metric_group_mask = metric_group_mask;
+ req.payload.metric_streamer_start.buffer_addr = buffer_addr;
+ req.payload.metric_streamer_start.buffer_size = buffer_size;
+
+ ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_METRIC_STREAMER_INFO_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+ if (ret) {
+ ivpu_warn_ratelimited(vdev, "Failed to get metric streamer info: ret %d\n", ret);
+ return ret;
+ }
+
+ if (!resp.payload.metric_streamer_done.sample_size) {
+ ivpu_warn_ratelimited(vdev, "Invalid sample size\n");
+ return -EBADMSG;
+ }
+
+ if (sample_size)
+ *sample_size = resp.payload.metric_streamer_done.sample_size;
+ if (info_size)
+ *info_size = resp.payload.metric_streamer_done.bytes_written;
+
+ return ret;
+}
+
+int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_DCT_ENABLE };
+ struct vpu_jsm_msg resp;
+
+ req.payload.pwr_dct_control.dct_active_us = active_us;
+ req.payload.pwr_dct_control.dct_inactive_us = inactive_us;
+
+ return ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_DCT_ENABLE_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+}
+
+int ivpu_jsm_dct_disable(struct ivpu_device *vdev)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_DCT_DISABLE };
+ struct vpu_jsm_msg resp;
+
+ return ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_DCT_DISABLE_DONE, &resp,
+ VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
+}
+
+int ivpu_jsm_state_dump(struct ivpu_device *vdev)
+{
+ struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_STATE_DUMP };
+
+ return ivpu_ipc_send_and_wait(vdev, &req, VPU_IPC_CHAN_ASYNC_CMD,
+ vdev->timeout.state_dump_msg);
+}
diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.h b/drivers/accel/ivpu/ivpu_jsm_msg.h
new file mode 100644
index 000000000000..9e84d3526a14
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_jsm_msg.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_JSM_MSG_H__
+#define __IVPU_JSM_MSG_H__
+
+#include "vpu_jsm_api.h"
+
+const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type);
+
+int ivpu_jsm_register_db(struct ivpu_device *vdev, u32 ctx_id, u32 db_id,
+ u64 jobq_base, u32 jobq_size);
+int ivpu_jsm_unregister_db(struct ivpu_device *vdev, u32 db_id);
+int ivpu_jsm_get_heartbeat(struct ivpu_device *vdev, u32 engine, u64 *heartbeat);
+int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine);
+int ivpu_jsm_preempt_engine(struct ivpu_device *vdev, u32 engine, u32 preempt_id);
+int ivpu_jsm_dyndbg_control(struct ivpu_device *vdev, char *command, size_t size);
+int ivpu_jsm_trace_get_capability(struct ivpu_device *vdev, u32 *trace_destination_mask,
+ u64 *trace_hw_component_mask);
+int ivpu_jsm_trace_set_config(struct ivpu_device *vdev, u32 trace_level, u32 trace_destination_mask,
+ u64 trace_hw_component_mask);
+int ivpu_jsm_context_release(struct ivpu_device *vdev, u32 host_ssid);
+int ivpu_jsm_pwr_d0i3_enter(struct ivpu_device *vdev);
+int ivpu_jsm_hws_create_cmdq(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_group, u32 cmdq_id,
+ u32 pid, u32 engine, u64 cmdq_base, u32 cmdq_size);
+int ivpu_jsm_hws_destroy_cmdq(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id);
+int ivpu_jsm_hws_register_db(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id, u32 db_id,
+ u64 cmdq_base, u32 cmdq_size);
+int ivpu_jsm_hws_resume_engine(struct ivpu_device *vdev, u32 engine);
+int ivpu_jsm_hws_set_context_sched_properties(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id,
+ u32 priority);
+int ivpu_jsm_hws_set_scheduling_log(struct ivpu_device *vdev, u32 engine_idx, u32 host_ssid,
+ u64 vpu_log_buffer_va);
+int ivpu_jsm_hws_setup_priority_bands(struct ivpu_device *vdev);
+int ivpu_jsm_metric_streamer_start(struct ivpu_device *vdev, u64 metric_group_mask,
+ u64 sampling_rate, u64 buffer_addr, u64 buffer_size);
+int ivpu_jsm_metric_streamer_stop(struct ivpu_device *vdev, u64 metric_group_mask);
+int ivpu_jsm_metric_streamer_update(struct ivpu_device *vdev, u64 metric_group_mask,
+ u64 buffer_addr, u64 buffer_size, u64 *bytes_written);
+int ivpu_jsm_metric_streamer_info(struct ivpu_device *vdev, u64 metric_group_mask, u64 buffer_addr,
+ u64 buffer_size, u32 *sample_size, u64 *info_size);
+int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us);
+int ivpu_jsm_dct_disable(struct ivpu_device *vdev);
+int ivpu_jsm_state_dump(struct ivpu_device *vdev);
+
+#endif
diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c
new file mode 100644
index 000000000000..e1baf6b64935
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_mmu.c
@@ -0,0 +1,1029 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/highmem.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_hw.h"
+#include "ivpu_hw_reg_io.h"
+#include "ivpu_mmu.h"
+#include "ivpu_mmu_context.h"
+#include "ivpu_pm.h"
+
+#define IVPU_MMU_REG_IDR0 0x00200000u
+#define IVPU_MMU_REG_IDR1 0x00200004u
+#define IVPU_MMU_REG_IDR3 0x0020000cu
+#define IVPU_MMU_REG_IDR5 0x00200014u
+#define IVPU_MMU_REG_CR0 0x00200020u
+#define IVPU_MMU_REG_CR0ACK 0x00200024u
+#define IVPU_MMU_REG_CR0ACK_VAL_MASK GENMASK(31, 0)
+#define IVPU_MMU_REG_CR0_ATSCHK_MASK BIT(4)
+#define IVPU_MMU_REG_CR0_CMDQEN_MASK BIT(3)
+#define IVPU_MMU_REG_CR0_EVTQEN_MASK BIT(2)
+#define IVPU_MMU_REG_CR0_PRIQEN_MASK BIT(1)
+#define IVPU_MMU_REG_CR0_SMMUEN_MASK BIT(0)
+
+#define IVPU_MMU_REG_CR1 0x00200028u
+#define IVPU_MMU_REG_CR2 0x0020002cu
+#define IVPU_MMU_REG_IRQ_CTRL 0x00200050u
+#define IVPU_MMU_REG_IRQ_CTRLACK 0x00200054u
+#define IVPU_MMU_REG_IRQ_CTRLACK_VAL_MASK GENMASK(31, 0)
+
+#define IVPU_MMU_REG_GERROR 0x00200060u
+#define IVPU_MMU_REG_GERROR_CMDQ_MASK BIT_MASK(0)
+#define IVPU_MMU_REG_GERROR_EVTQ_ABT_MASK BIT_MASK(2)
+#define IVPU_MMU_REG_GERROR_PRIQ_ABT_MASK BIT_MASK(3)
+#define IVPU_MMU_REG_GERROR_MSI_CMDQ_ABT_MASK BIT_MASK(4)
+#define IVPU_MMU_REG_GERROR_MSI_EVTQ_ABT_MASK BIT_MASK(5)
+#define IVPU_MMU_REG_GERROR_MSI_PRIQ_ABT_MASK BIT_MASK(6)
+#define IVPU_MMU_REG_GERROR_MSI_ABT_MASK BIT_MASK(7)
+
+#define IVPU_MMU_REG_GERRORN 0x00200064u
+
+#define IVPU_MMU_REG_STRTAB_BASE 0x00200080u
+#define IVPU_MMU_REG_STRTAB_BASE_CFG 0x00200088u
+#define IVPU_MMU_REG_CMDQ_BASE 0x00200090u
+#define IVPU_MMU_REG_CMDQ_PROD 0x00200098u
+#define IVPU_MMU_REG_CMDQ_CONS 0x0020009cu
+#define IVPU_MMU_REG_CMDQ_CONS_VAL_MASK GENMASK(23, 0)
+#define IVPU_MMU_REG_CMDQ_CONS_ERR_MASK GENMASK(30, 24)
+#define IVPU_MMU_REG_EVTQ_BASE 0x002000a0u
+#define IVPU_MMU_REG_EVTQ_PROD 0x002000a8u
+#define IVPU_MMU_REG_EVTQ_CONS 0x002000acu
+#define IVPU_MMU_REG_EVTQ_PROD_SEC (0x002000a8u + SZ_64K)
+#define IVPU_MMU_REG_EVTQ_CONS_SEC (0x002000acu + SZ_64K)
+
+#define IVPU_MMU_IDR0_REF 0x080f3e0f
+#define IVPU_MMU_IDR0_REF_SIMICS 0x080f3e1f
+#define IVPU_MMU_IDR1_REF 0x0e739d18
+#define IVPU_MMU_IDR3_REF 0x0000003c
+#define IVPU_MMU_IDR5_REF 0x00040070
+#define IVPU_MMU_IDR5_REF_SIMICS 0x00000075
+#define IVPU_MMU_IDR5_REF_FPGA 0x00800075
+
+#define IVPU_MMU_CDTAB_ENT_SIZE 64
+#define IVPU_MMU_CDTAB_ENT_COUNT_LOG2 8 /* 256 entries */
+#define IVPU_MMU_CDTAB_ENT_COUNT ((u32)1 << IVPU_MMU_CDTAB_ENT_COUNT_LOG2)
+
+#define IVPU_MMU_STREAM_ID0 0
+#define IVPU_MMU_STREAM_ID3 3
+
+#define IVPU_MMU_STRTAB_ENT_SIZE 64
+#define IVPU_MMU_STRTAB_ENT_COUNT 4
+#define IVPU_MMU_STRTAB_CFG_LOG2SIZE 2
+#define IVPU_MMU_STRTAB_CFG IVPU_MMU_STRTAB_CFG_LOG2SIZE
+
+#define IVPU_MMU_Q_COUNT_LOG2 4 /* 16 entries */
+#define IVPU_MMU_Q_COUNT ((u32)1 << IVPU_MMU_Q_COUNT_LOG2)
+#define IVPU_MMU_Q_WRAP_MASK GENMASK(IVPU_MMU_Q_COUNT_LOG2, 0)
+#define IVPU_MMU_Q_IDX_MASK (IVPU_MMU_Q_COUNT - 1)
+#define IVPU_MMU_Q_IDX(val) ((val) & IVPU_MMU_Q_IDX_MASK)
+#define IVPU_MMU_Q_WRP(val) ((val) & IVPU_MMU_Q_COUNT)
+
+#define IVPU_MMU_CMDQ_CMD_SIZE 16
+#define IVPU_MMU_CMDQ_SIZE (IVPU_MMU_Q_COUNT * IVPU_MMU_CMDQ_CMD_SIZE)
+
+#define IVPU_MMU_EVTQ_CMD_SIZE 32
+#define IVPU_MMU_EVTQ_SIZE (IVPU_MMU_Q_COUNT * IVPU_MMU_EVTQ_CMD_SIZE)
+
+#define IVPU_MMU_CMD_OPCODE GENMASK(7, 0)
+
+#define IVPU_MMU_CMD_SYNC_0_CS GENMASK(13, 12)
+#define IVPU_MMU_CMD_SYNC_0_MSH GENMASK(23, 22)
+#define IVPU_MMU_CMD_SYNC_0_MSI_ATTR GENMASK(27, 24)
+#define IVPU_MMU_CMD_SYNC_0_MSI_ATTR GENMASK(27, 24)
+#define IVPU_MMU_CMD_SYNC_0_MSI_DATA GENMASK(63, 32)
+
+#define IVPU_MMU_CMD_CFGI_0_SSEC BIT(10)
+#define IVPU_MMU_CMD_CFGI_0_SSV BIT(11)
+#define IVPU_MMU_CMD_CFGI_0_SSID GENMASK(31, 12)
+#define IVPU_MMU_CMD_CFGI_0_SID GENMASK(63, 32)
+#define IVPU_MMU_CMD_CFGI_1_RANGE GENMASK(4, 0)
+
+#define IVPU_MMU_CMD_TLBI_0_ASID GENMASK(63, 48)
+#define IVPU_MMU_CMD_TLBI_0_VMID GENMASK(47, 32)
+
+#define CMD_PREFETCH_CFG 0x1
+#define CMD_CFGI_STE 0x3
+#define CMD_CFGI_ALL 0x4
+#define CMD_CFGI_CD 0x5
+#define CMD_CFGI_CD_ALL 0x6
+#define CMD_TLBI_NH_ASID 0x11
+#define CMD_TLBI_EL2_ALL 0x20
+#define CMD_TLBI_NSNH_ALL 0x30
+#define CMD_SYNC 0x46
+
+#define IVPU_MMU_EVT_F_UUT 0x01
+#define IVPU_MMU_EVT_C_BAD_STREAMID 0x02
+#define IVPU_MMU_EVT_F_STE_FETCH 0x03
+#define IVPU_MMU_EVT_C_BAD_STE 0x04
+#define IVPU_MMU_EVT_F_BAD_ATS_TREQ 0x05
+#define IVPU_MMU_EVT_F_STREAM_DISABLED 0x06
+#define IVPU_MMU_EVT_F_TRANSL_FORBIDDEN 0x07
+#define IVPU_MMU_EVT_C_BAD_SUBSTREAMID 0x08
+#define IVPU_MMU_EVT_F_CD_FETCH 0x09
+#define IVPU_MMU_EVT_C_BAD_CD 0x0a
+#define IVPU_MMU_EVT_F_WALK_EABT 0x0b
+#define IVPU_MMU_EVT_F_TRANSLATION 0x10
+#define IVPU_MMU_EVT_F_ADDR_SIZE 0x11
+#define IVPU_MMU_EVT_F_ACCESS 0x12
+#define IVPU_MMU_EVT_F_PERMISSION 0x13
+#define IVPU_MMU_EVT_F_TLB_CONFLICT 0x20
+#define IVPU_MMU_EVT_F_CFG_CONFLICT 0x21
+#define IVPU_MMU_EVT_E_PAGE_REQUEST 0x24
+#define IVPU_MMU_EVT_F_VMS_FETCH 0x25
+
+#define IVPU_MMU_EVT_OP_MASK GENMASK_ULL(7, 0)
+#define IVPU_MMU_EVT_SSID_MASK GENMASK_ULL(31, 12)
+
+#define IVPU_MMU_Q_BASE_RWA BIT(62)
+#define IVPU_MMU_Q_BASE_ADDR_MASK GENMASK_ULL(51, 5)
+#define IVPU_MMU_STRTAB_BASE_RA BIT(62)
+#define IVPU_MMU_STRTAB_BASE_ADDR_MASK GENMASK_ULL(51, 6)
+
+#define IVPU_MMU_IRQ_EVTQ_EN BIT(2)
+#define IVPU_MMU_IRQ_GERROR_EN BIT(0)
+
+#define IVPU_MMU_CR1_TABLE_SH GENMASK(11, 10)
+#define IVPU_MMU_CR1_TABLE_OC GENMASK(9, 8)
+#define IVPU_MMU_CR1_TABLE_IC GENMASK(7, 6)
+#define IVPU_MMU_CR1_QUEUE_SH GENMASK(5, 4)
+#define IVPU_MMU_CR1_QUEUE_OC GENMASK(3, 2)
+#define IVPU_MMU_CR1_QUEUE_IC GENMASK(1, 0)
+#define IVPU_MMU_CACHE_NC 0
+#define IVPU_MMU_CACHE_WB 1
+#define IVPU_MMU_CACHE_WT 2
+#define IVPU_MMU_SH_NSH 0
+#define IVPU_MMU_SH_OSH 2
+#define IVPU_MMU_SH_ISH 3
+
+#define IVPU_MMU_CMDQ_OP GENMASK_ULL(7, 0)
+
+#define IVPU_MMU_CD_0_TCR_T0SZ GENMASK_ULL(5, 0)
+#define IVPU_MMU_CD_0_TCR_TG0 GENMASK_ULL(7, 6)
+#define IVPU_MMU_CD_0_TCR_IRGN0 GENMASK_ULL(9, 8)
+#define IVPU_MMU_CD_0_TCR_ORGN0 GENMASK_ULL(11, 10)
+#define IVPU_MMU_CD_0_TCR_SH0 GENMASK_ULL(13, 12)
+#define IVPU_MMU_CD_0_TCR_EPD0 BIT_ULL(14)
+#define IVPU_MMU_CD_0_TCR_EPD1 BIT_ULL(30)
+#define IVPU_MMU_CD_0_ENDI BIT(15)
+#define IVPU_MMU_CD_0_V BIT(31)
+#define IVPU_MMU_CD_0_TCR_IPS GENMASK_ULL(34, 32)
+#define IVPU_MMU_CD_0_TCR_TBI0 BIT_ULL(38)
+#define IVPU_MMU_CD_0_AA64 BIT(41)
+#define IVPU_MMU_CD_0_S BIT(44)
+#define IVPU_MMU_CD_0_R BIT(45)
+#define IVPU_MMU_CD_0_A BIT(46)
+#define IVPU_MMU_CD_0_ASET BIT(47)
+#define IVPU_MMU_CD_0_ASID GENMASK_ULL(63, 48)
+
+#define IVPU_MMU_T0SZ_48BIT 16
+#define IVPU_MMU_T0SZ_38BIT 26
+
+#define IVPU_MMU_IPS_48BIT 5
+#define IVPU_MMU_IPS_44BIT 4
+#define IVPU_MMU_IPS_42BIT 3
+#define IVPU_MMU_IPS_40BIT 2
+#define IVPU_MMU_IPS_36BIT 1
+#define IVPU_MMU_IPS_32BIT 0
+
+#define IVPU_MMU_CD_1_TTB0_MASK GENMASK_ULL(51, 4)
+
+#define IVPU_MMU_STE_0_S1CDMAX GENMASK_ULL(63, 59)
+#define IVPU_MMU_STE_0_S1FMT GENMASK_ULL(5, 4)
+#define IVPU_MMU_STE_0_S1FMT_LINEAR 0
+#define IVPU_MMU_STE_DWORDS 8
+#define IVPU_MMU_STE_0_CFG_S1_TRANS 5
+#define IVPU_MMU_STE_0_CFG GENMASK_ULL(3, 1)
+#define IVPU_MMU_STE_0_S1CTXPTR_MASK GENMASK_ULL(51, 6)
+#define IVPU_MMU_STE_0_V BIT(0)
+
+#define IVPU_MMU_STE_1_STRW_NSEL1 0ul
+#define IVPU_MMU_STE_1_CONT GENMASK_ULL(16, 13)
+#define IVPU_MMU_STE_1_STRW GENMASK_ULL(31, 30)
+#define IVPU_MMU_STE_1_PRIVCFG GENMASK_ULL(49, 48)
+#define IVPU_MMU_STE_1_PRIVCFG_UNPRIV 2ul
+#define IVPU_MMU_STE_1_INSTCFG GENMASK_ULL(51, 50)
+#define IVPU_MMU_STE_1_INSTCFG_DATA 2ul
+#define IVPU_MMU_STE_1_MEV BIT(19)
+#define IVPU_MMU_STE_1_S1STALLD BIT(27)
+#define IVPU_MMU_STE_1_S1C_CACHE_NC 0ul
+#define IVPU_MMU_STE_1_S1C_CACHE_WBRA 1ul
+#define IVPU_MMU_STE_1_S1C_CACHE_WT 2ul
+#define IVPU_MMU_STE_1_S1C_CACHE_WB 3ul
+#define IVPU_MMU_STE_1_S1CIR GENMASK_ULL(3, 2)
+#define IVPU_MMU_STE_1_S1COR GENMASK_ULL(5, 4)
+#define IVPU_MMU_STE_1_S1CSH GENMASK_ULL(7, 6)
+#define IVPU_MMU_STE_1_S1DSS GENMASK_ULL(1, 0)
+#define IVPU_MMU_STE_1_S1DSS_TERMINATE 0x0
+
+#define IVPU_MMU_REG_TIMEOUT_US (10 * USEC_PER_MSEC)
+#define IVPU_MMU_QUEUE_TIMEOUT_US (100 * USEC_PER_MSEC)
+
+#define IVPU_MMU_GERROR_ERR_MASK ((REG_FLD(IVPU_MMU_REG_GERROR, CMDQ)) | \
+ (REG_FLD(IVPU_MMU_REG_GERROR, EVTQ_ABT)) | \
+ (REG_FLD(IVPU_MMU_REG_GERROR, PRIQ_ABT)) | \
+ (REG_FLD(IVPU_MMU_REG_GERROR, MSI_CMDQ_ABT)) | \
+ (REG_FLD(IVPU_MMU_REG_GERROR, MSI_EVTQ_ABT)) | \
+ (REG_FLD(IVPU_MMU_REG_GERROR, MSI_PRIQ_ABT)) | \
+ (REG_FLD(IVPU_MMU_REG_GERROR, MSI_ABT)))
+
+#define IVPU_MMU_CERROR_NONE 0x0
+#define IVPU_MMU_CERROR_ILL 0x1
+#define IVPU_MMU_CERROR_ABT 0x2
+#define IVPU_MMU_CERROR_ATC_INV_SYNC 0x3
+
+static const char *ivpu_mmu_event_to_str(u32 cmd)
+{
+ switch (cmd) {
+ case IVPU_MMU_EVT_F_UUT:
+ return "Unsupported Upstream Transaction";
+ case IVPU_MMU_EVT_C_BAD_STREAMID:
+ return "Transaction StreamID out of range";
+ case IVPU_MMU_EVT_F_STE_FETCH:
+ return "Fetch of STE caused external abort";
+ case IVPU_MMU_EVT_C_BAD_STE:
+ return "Used STE invalid";
+ case IVPU_MMU_EVT_F_BAD_ATS_TREQ:
+ return "Address Request disallowed for a StreamID";
+ case IVPU_MMU_EVT_F_STREAM_DISABLED:
+ return "Transaction marks non-substream disabled";
+ case IVPU_MMU_EVT_F_TRANSL_FORBIDDEN:
+ return "MMU bypass is disallowed for this StreamID";
+ case IVPU_MMU_EVT_C_BAD_SUBSTREAMID:
+ return "Invalid StreamID";
+ case IVPU_MMU_EVT_F_CD_FETCH:
+ return "Fetch of CD caused external abort";
+ case IVPU_MMU_EVT_C_BAD_CD:
+ return "Fetched CD invalid";
+ case IVPU_MMU_EVT_F_WALK_EABT:
+ return " An external abort occurred fetching a TLB";
+ case IVPU_MMU_EVT_F_TRANSLATION:
+ return "Translation fault";
+ case IVPU_MMU_EVT_F_ADDR_SIZE:
+ return " Output address caused address size fault";
+ case IVPU_MMU_EVT_F_ACCESS:
+ return "Access flag fault";
+ case IVPU_MMU_EVT_F_PERMISSION:
+ return "Permission fault occurred on page access";
+ case IVPU_MMU_EVT_F_TLB_CONFLICT:
+ return "A TLB conflict";
+ case IVPU_MMU_EVT_F_CFG_CONFLICT:
+ return "A configuration cache conflict";
+ case IVPU_MMU_EVT_E_PAGE_REQUEST:
+ return "Page request hint from a client device";
+ case IVPU_MMU_EVT_F_VMS_FETCH:
+ return "Fetch of VMS caused external abort";
+ default:
+ return "Unknown event";
+ }
+}
+
+static const char *ivpu_mmu_cmdq_err_to_str(u32 err)
+{
+ switch (err) {
+ case IVPU_MMU_CERROR_NONE:
+ return "No error";
+ case IVPU_MMU_CERROR_ILL:
+ return "Illegal command";
+ case IVPU_MMU_CERROR_ABT:
+ return "External abort on command queue read";
+ case IVPU_MMU_CERROR_ATC_INV_SYNC:
+ return "Sync failed to complete ATS invalidation";
+ default:
+ return "Unknown error";
+ }
+}
+
+static void ivpu_mmu_config_check(struct ivpu_device *vdev)
+{
+ u32 val_ref;
+ u32 val;
+
+ if (ivpu_is_simics(vdev))
+ val_ref = IVPU_MMU_IDR0_REF_SIMICS;
+ else
+ val_ref = IVPU_MMU_IDR0_REF;
+
+ val = REGV_RD32(IVPU_MMU_REG_IDR0);
+ if (val != val_ref)
+ ivpu_dbg(vdev, MMU, "IDR0 0x%x != IDR0_REF 0x%x\n", val, val_ref);
+
+ val = REGV_RD32(IVPU_MMU_REG_IDR1);
+ if (val != IVPU_MMU_IDR1_REF)
+ ivpu_dbg(vdev, MMU, "IDR1 0x%x != IDR1_REF 0x%x\n", val, IVPU_MMU_IDR1_REF);
+
+ val = REGV_RD32(IVPU_MMU_REG_IDR3);
+ if (val != IVPU_MMU_IDR3_REF)
+ ivpu_dbg(vdev, MMU, "IDR3 0x%x != IDR3_REF 0x%x\n", val, IVPU_MMU_IDR3_REF);
+
+ if (ivpu_is_simics(vdev))
+ val_ref = IVPU_MMU_IDR5_REF_SIMICS;
+ else if (ivpu_is_fpga(vdev))
+ val_ref = IVPU_MMU_IDR5_REF_FPGA;
+ else
+ val_ref = IVPU_MMU_IDR5_REF;
+
+ val = REGV_RD32(IVPU_MMU_REG_IDR5);
+ if (val != val_ref)
+ ivpu_dbg(vdev, MMU, "IDR5 0x%x != IDR5_REF 0x%x\n", val, val_ref);
+}
+
+static int ivpu_mmu_cdtab_alloc(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
+ size_t size = IVPU_MMU_CDTAB_ENT_COUNT * IVPU_MMU_CDTAB_ENT_SIZE;
+
+ cdtab->base = dmam_alloc_coherent(vdev->drm.dev, size, &cdtab->dma, GFP_KERNEL);
+ if (!cdtab->base)
+ return -ENOMEM;
+
+ ivpu_dbg(vdev, MMU, "CDTAB alloc: dma=%pad size=%zu\n", &cdtab->dma, size);
+
+ return 0;
+}
+
+static int ivpu_mmu_strtab_alloc(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_strtab *strtab = &mmu->strtab;
+ size_t size = IVPU_MMU_STRTAB_ENT_COUNT * IVPU_MMU_STRTAB_ENT_SIZE;
+
+ strtab->base = dmam_alloc_coherent(vdev->drm.dev, size, &strtab->dma, GFP_KERNEL);
+ if (!strtab->base)
+ return -ENOMEM;
+
+ strtab->base_cfg = IVPU_MMU_STRTAB_CFG;
+ strtab->dma_q = IVPU_MMU_STRTAB_BASE_RA;
+ strtab->dma_q |= strtab->dma & IVPU_MMU_STRTAB_BASE_ADDR_MASK;
+
+ ivpu_dbg(vdev, MMU, "STRTAB alloc: dma=%pad dma_q=%pad size=%zu\n",
+ &strtab->dma, &strtab->dma_q, size);
+
+ return 0;
+}
+
+static int ivpu_mmu_cmdq_alloc(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_queue *q = &mmu->cmdq;
+
+ q->base = dmam_alloc_coherent(vdev->drm.dev, IVPU_MMU_CMDQ_SIZE, &q->dma, GFP_KERNEL);
+ if (!q->base)
+ return -ENOMEM;
+
+ q->dma_q = IVPU_MMU_Q_BASE_RWA;
+ q->dma_q |= q->dma & IVPU_MMU_Q_BASE_ADDR_MASK;
+ q->dma_q |= IVPU_MMU_Q_COUNT_LOG2;
+
+ ivpu_dbg(vdev, MMU, "CMDQ alloc: dma=%pad dma_q=%pad size=%u\n",
+ &q->dma, &q->dma_q, IVPU_MMU_CMDQ_SIZE);
+
+ return 0;
+}
+
+static int ivpu_mmu_evtq_alloc(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_queue *q = &mmu->evtq;
+
+ q->base = dmam_alloc_coherent(vdev->drm.dev, IVPU_MMU_EVTQ_SIZE, &q->dma, GFP_KERNEL);
+ if (!q->base)
+ return -ENOMEM;
+
+ q->dma_q = IVPU_MMU_Q_BASE_RWA;
+ q->dma_q |= q->dma & IVPU_MMU_Q_BASE_ADDR_MASK;
+ q->dma_q |= IVPU_MMU_Q_COUNT_LOG2;
+
+ ivpu_dbg(vdev, MMU, "EVTQ alloc: dma=%pad dma_q=%pad size=%u\n",
+ &q->dma, &q->dma_q, IVPU_MMU_EVTQ_SIZE);
+
+ return 0;
+}
+
+static int ivpu_mmu_structs_alloc(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ret = ivpu_mmu_cdtab_alloc(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to allocate cdtab: %d\n", ret);
+ return ret;
+ }
+
+ ret = ivpu_mmu_strtab_alloc(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to allocate strtab: %d\n", ret);
+ return ret;
+ }
+
+ ret = ivpu_mmu_cmdq_alloc(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to allocate cmdq: %d\n", ret);
+ return ret;
+ }
+
+ ret = ivpu_mmu_evtq_alloc(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to allocate evtq: %d\n", ret);
+
+ return ret;
+}
+
+static int ivpu_mmu_reg_write_cr0(struct ivpu_device *vdev, u32 val)
+{
+ REGV_WR32(IVPU_MMU_REG_CR0, val);
+
+ return REGV_POLL_FLD(IVPU_MMU_REG_CR0ACK, VAL, val, IVPU_MMU_REG_TIMEOUT_US);
+}
+
+static int ivpu_mmu_reg_write_irq_ctrl(struct ivpu_device *vdev, u32 val)
+{
+ REGV_WR32(IVPU_MMU_REG_IRQ_CTRL, val);
+
+ return REGV_POLL_FLD(IVPU_MMU_REG_IRQ_CTRLACK, VAL, val, IVPU_MMU_REG_TIMEOUT_US);
+}
+
+static int ivpu_mmu_irqs_setup(struct ivpu_device *vdev)
+{
+ u32 irq_ctrl = IVPU_MMU_IRQ_EVTQ_EN | IVPU_MMU_IRQ_GERROR_EN;
+ int ret;
+
+ ret = ivpu_mmu_reg_write_irq_ctrl(vdev, 0);
+ if (ret)
+ return ret;
+
+ return ivpu_mmu_reg_write_irq_ctrl(vdev, irq_ctrl);
+}
+
+static int ivpu_mmu_cmdq_wait_for_cons(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_queue *cmdq = &vdev->mmu->cmdq;
+ int ret;
+
+ ret = REGV_POLL_FLD(IVPU_MMU_REG_CMDQ_CONS, VAL, cmdq->prod,
+ IVPU_MMU_QUEUE_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ cmdq->cons = cmdq->prod;
+
+ return 0;
+}
+
+static bool ivpu_mmu_queue_is_full(struct ivpu_mmu_queue *q)
+{
+ return ((IVPU_MMU_Q_IDX(q->prod) == IVPU_MMU_Q_IDX(q->cons)) &&
+ (IVPU_MMU_Q_WRP(q->prod) != IVPU_MMU_Q_WRP(q->cons)));
+}
+
+static bool ivpu_mmu_queue_is_empty(struct ivpu_mmu_queue *q)
+{
+ return ((IVPU_MMU_Q_IDX(q->prod) == IVPU_MMU_Q_IDX(q->cons)) &&
+ (IVPU_MMU_Q_WRP(q->prod) == IVPU_MMU_Q_WRP(q->cons)));
+}
+
+static int ivpu_mmu_cmdq_cmd_write(struct ivpu_device *vdev, const char *name, u64 data0, u64 data1)
+{
+ struct ivpu_mmu_queue *cmdq = &vdev->mmu->cmdq;
+ u64 *queue_buffer = cmdq->base;
+ int idx = IVPU_MMU_Q_IDX(cmdq->prod) * (IVPU_MMU_CMDQ_CMD_SIZE / sizeof(*queue_buffer));
+
+ if (ivpu_mmu_queue_is_full(cmdq)) {
+ ivpu_err(vdev, "Failed to write MMU CMD %s\n", name);
+ return -EBUSY;
+ }
+
+ queue_buffer[idx] = data0;
+ queue_buffer[idx + 1] = data1;
+ cmdq->prod = (cmdq->prod + 1) & IVPU_MMU_Q_WRAP_MASK;
+
+ ivpu_dbg(vdev, MMU, "CMD write: %s data: 0x%llx 0x%llx\n", name, data0, data1);
+
+ return 0;
+}
+
+static int ivpu_mmu_cmdq_sync(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_queue *q = &vdev->mmu->cmdq;
+ u64 val;
+ int ret;
+
+ val = FIELD_PREP(IVPU_MMU_CMD_OPCODE, CMD_SYNC);
+
+ ret = ivpu_mmu_cmdq_cmd_write(vdev, "SYNC", val, 0);
+ if (ret)
+ return ret;
+
+ if (!ivpu_is_force_snoop_enabled(vdev))
+ clflush_cache_range(q->base, IVPU_MMU_CMDQ_SIZE);
+ REGV_WR32(IVPU_MMU_REG_CMDQ_PROD, q->prod);
+
+ ret = ivpu_mmu_cmdq_wait_for_cons(vdev);
+ if (ret) {
+ u32 err;
+
+ val = REGV_RD32(IVPU_MMU_REG_CMDQ_CONS);
+ err = REG_GET_FLD(IVPU_MMU_REG_CMDQ_CONS, ERR, val);
+
+ ivpu_err(vdev, "Timed out waiting for MMU consumer: %d, error: %s\n", ret,
+ ivpu_mmu_cmdq_err_to_str(err));
+ ivpu_hw_diagnose_failure(vdev);
+ }
+
+ return ret;
+}
+
+static int ivpu_mmu_cmdq_write_cfgi_all(struct ivpu_device *vdev)
+{
+ u64 data0 = FIELD_PREP(IVPU_MMU_CMD_OPCODE, CMD_CFGI_ALL);
+ u64 data1 = FIELD_PREP(IVPU_MMU_CMD_CFGI_1_RANGE, 0x1f);
+
+ return ivpu_mmu_cmdq_cmd_write(vdev, "CFGI_ALL", data0, data1);
+}
+
+static int ivpu_mmu_cmdq_write_tlbi_nh_asid(struct ivpu_device *vdev, u16 ssid)
+{
+ u64 val = FIELD_PREP(IVPU_MMU_CMD_OPCODE, CMD_TLBI_NH_ASID) |
+ FIELD_PREP(IVPU_MMU_CMD_TLBI_0_ASID, ssid);
+
+ return ivpu_mmu_cmdq_cmd_write(vdev, "TLBI_NH_ASID", val, 0);
+}
+
+static int ivpu_mmu_cmdq_write_tlbi_nsnh_all(struct ivpu_device *vdev)
+{
+ u64 val = FIELD_PREP(IVPU_MMU_CMD_OPCODE, CMD_TLBI_NSNH_ALL);
+
+ return ivpu_mmu_cmdq_cmd_write(vdev, "TLBI_NSNH_ALL", val, 0);
+}
+
+static int ivpu_mmu_reset(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ u32 val;
+ int ret;
+
+ memset(mmu->cmdq.base, 0, IVPU_MMU_CMDQ_SIZE);
+ if (!ivpu_is_force_snoop_enabled(vdev))
+ clflush_cache_range(mmu->cmdq.base, IVPU_MMU_CMDQ_SIZE);
+ mmu->cmdq.prod = 0;
+ mmu->cmdq.cons = 0;
+
+ memset(mmu->evtq.base, 0, IVPU_MMU_EVTQ_SIZE);
+ mmu->evtq.prod = 0;
+ mmu->evtq.cons = 0;
+
+ ret = ivpu_mmu_reg_write_cr0(vdev, 0);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(IVPU_MMU_CR1_TABLE_SH, IVPU_MMU_SH_ISH) |
+ FIELD_PREP(IVPU_MMU_CR1_TABLE_OC, IVPU_MMU_CACHE_WB) |
+ FIELD_PREP(IVPU_MMU_CR1_TABLE_IC, IVPU_MMU_CACHE_WB) |
+ FIELD_PREP(IVPU_MMU_CR1_QUEUE_SH, IVPU_MMU_SH_ISH) |
+ FIELD_PREP(IVPU_MMU_CR1_QUEUE_OC, IVPU_MMU_CACHE_WB) |
+ FIELD_PREP(IVPU_MMU_CR1_QUEUE_IC, IVPU_MMU_CACHE_WB);
+ REGV_WR32(IVPU_MMU_REG_CR1, val);
+
+ REGV_WR64(IVPU_MMU_REG_STRTAB_BASE, mmu->strtab.dma_q);
+ REGV_WR32(IVPU_MMU_REG_STRTAB_BASE_CFG, mmu->strtab.base_cfg);
+
+ REGV_WR64(IVPU_MMU_REG_CMDQ_BASE, mmu->cmdq.dma_q);
+ REGV_WR32(IVPU_MMU_REG_CMDQ_PROD, 0);
+ REGV_WR32(IVPU_MMU_REG_CMDQ_CONS, 0);
+
+ val = REG_SET_FLD(IVPU_MMU_REG_CR0, CMDQEN, 0);
+ ret = ivpu_mmu_reg_write_cr0(vdev, val);
+ if (ret)
+ return ret;
+
+ ret = ivpu_mmu_cmdq_write_cfgi_all(vdev);
+ if (ret)
+ return ret;
+
+ ret = ivpu_mmu_cmdq_write_tlbi_nsnh_all(vdev);
+ if (ret)
+ return ret;
+
+ ret = ivpu_mmu_cmdq_sync(vdev);
+ if (ret)
+ return ret;
+
+ REGV_WR64(IVPU_MMU_REG_EVTQ_BASE, mmu->evtq.dma_q);
+ REGV_WR32(IVPU_MMU_REG_EVTQ_PROD_SEC, 0);
+ REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, 0);
+
+ val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
+ ret = ivpu_mmu_reg_write_cr0(vdev, val);
+ if (ret)
+ return ret;
+
+ val = REG_SET_FLD(IVPU_MMU_REG_CR0, ATSCHK, val);
+ ret = ivpu_mmu_reg_write_cr0(vdev, val);
+ if (ret)
+ return ret;
+
+ ret = ivpu_mmu_irqs_setup(vdev);
+ if (ret)
+ return ret;
+
+ val = REG_SET_FLD(IVPU_MMU_REG_CR0, SMMUEN, val);
+ return ivpu_mmu_reg_write_cr0(vdev, val);
+}
+
+static void ivpu_mmu_strtab_link_cd(struct ivpu_device *vdev, u32 sid)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_strtab *strtab = &mmu->strtab;
+ struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
+ u64 *entry = strtab->base + (sid * IVPU_MMU_STRTAB_ENT_SIZE);
+ u64 str[2];
+
+ str[0] = FIELD_PREP(IVPU_MMU_STE_0_CFG, IVPU_MMU_STE_0_CFG_S1_TRANS) |
+ FIELD_PREP(IVPU_MMU_STE_0_S1CDMAX, IVPU_MMU_CDTAB_ENT_COUNT_LOG2) |
+ FIELD_PREP(IVPU_MMU_STE_0_S1FMT, IVPU_MMU_STE_0_S1FMT_LINEAR) |
+ IVPU_MMU_STE_0_V |
+ (cdtab->dma & IVPU_MMU_STE_0_S1CTXPTR_MASK);
+
+ str[1] = FIELD_PREP(IVPU_MMU_STE_1_S1DSS, IVPU_MMU_STE_1_S1DSS_TERMINATE) |
+ FIELD_PREP(IVPU_MMU_STE_1_S1CIR, IVPU_MMU_STE_1_S1C_CACHE_NC) |
+ FIELD_PREP(IVPU_MMU_STE_1_S1COR, IVPU_MMU_STE_1_S1C_CACHE_NC) |
+ FIELD_PREP(IVPU_MMU_STE_1_S1CSH, IVPU_MMU_SH_NSH) |
+ FIELD_PREP(IVPU_MMU_STE_1_PRIVCFG, IVPU_MMU_STE_1_PRIVCFG_UNPRIV) |
+ FIELD_PREP(IVPU_MMU_STE_1_INSTCFG, IVPU_MMU_STE_1_INSTCFG_DATA) |
+ FIELD_PREP(IVPU_MMU_STE_1_STRW, IVPU_MMU_STE_1_STRW_NSEL1) |
+ FIELD_PREP(IVPU_MMU_STE_1_CONT, IVPU_MMU_STRTAB_CFG_LOG2SIZE) |
+ IVPU_MMU_STE_1_MEV |
+ IVPU_MMU_STE_1_S1STALLD;
+
+ WRITE_ONCE(entry[1], str[1]);
+ WRITE_ONCE(entry[0], str[0]);
+
+ if (!ivpu_is_force_snoop_enabled(vdev))
+ clflush_cache_range(entry, IVPU_MMU_STRTAB_ENT_SIZE);
+
+ ivpu_dbg(vdev, MMU, "STRTAB write entry (SSID=%u): 0x%llx, 0x%llx\n", sid, str[0], str[1]);
+}
+
+static int ivpu_mmu_strtab_init(struct ivpu_device *vdev)
+{
+ ivpu_mmu_strtab_link_cd(vdev, IVPU_MMU_STREAM_ID0);
+ ivpu_mmu_strtab_link_cd(vdev, IVPU_MMU_STREAM_ID3);
+
+ return 0;
+}
+
+int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ int ret = 0;
+
+ mutex_lock(&mmu->lock);
+ if (!mmu->on)
+ goto unlock;
+
+ ret = ivpu_mmu_cmdq_write_tlbi_nh_asid(vdev, ssid);
+ if (ret)
+ goto unlock;
+
+ ret = ivpu_mmu_cmdq_sync(vdev);
+unlock:
+ mutex_unlock(&mmu->lock);
+ return ret;
+}
+
+static int ivpu_mmu_cdtab_entry_set(struct ivpu_device *vdev, u32 ssid, u64 cd_dma, bool valid)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
+ u64 *entry;
+ u64 cd[4];
+ int ret = 0;
+
+ if (ssid > IVPU_MMU_CDTAB_ENT_COUNT)
+ return -EINVAL;
+
+ entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE);
+ drm_WARN_ON(&vdev->drm, (entry[0] & IVPU_MMU_CD_0_V) == valid);
+
+ cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, IVPU_MMU_T0SZ_48BIT) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_TG0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_IRGN0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_ORGN0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_SH0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, IVPU_MMU_IPS_48BIT) |
+ FIELD_PREP(IVPU_MMU_CD_0_ASID, ssid) |
+ IVPU_MMU_CD_0_TCR_EPD1 |
+ IVPU_MMU_CD_0_AA64 |
+ IVPU_MMU_CD_0_R |
+ IVPU_MMU_CD_0_ASET;
+ cd[1] = cd_dma & IVPU_MMU_CD_1_TTB0_MASK;
+ cd[2] = 0;
+ cd[3] = 0x0000000000007444;
+
+ /* For global and reserved contexts generate memory fault on VPU */
+ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID || ssid == IVPU_RESERVED_CONTEXT_MMU_SSID)
+ cd[0] |= IVPU_MMU_CD_0_A;
+
+ if (valid)
+ cd[0] |= IVPU_MMU_CD_0_V;
+
+ WRITE_ONCE(entry[1], cd[1]);
+ WRITE_ONCE(entry[2], cd[2]);
+ WRITE_ONCE(entry[3], cd[3]);
+ WRITE_ONCE(entry[0], cd[0]);
+
+ if (!ivpu_is_force_snoop_enabled(vdev))
+ clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);
+
+ ivpu_dbg(vdev, MMU, "CDTAB set %s entry (SSID=%u, dma=%pad): 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",
+ valid ? "valid" : "invalid", ssid, &cd_dma, cd[0], cd[1], cd[2], cd[3]);
+
+ mutex_lock(&mmu->lock);
+ if (!mmu->on)
+ goto unlock;
+
+ ret = ivpu_mmu_cmdq_write_cfgi_all(vdev);
+ if (ret)
+ goto err_invalidate;
+
+ ret = ivpu_mmu_cmdq_sync(vdev);
+ if (ret)
+ goto err_invalidate;
+unlock:
+ mutex_unlock(&mmu->lock);
+ return 0;
+
+err_invalidate:
+ WRITE_ONCE(entry[0], 0);
+ mutex_unlock(&mmu->lock);
+ return ret;
+}
+
+int ivpu_mmu_init(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ int ret;
+
+ ivpu_dbg(vdev, MMU, "Init..\n");
+
+ ivpu_mmu_config_check(vdev);
+
+ ret = drmm_mutex_init(&vdev->drm, &mmu->lock);
+ if (ret)
+ return ret;
+
+ ret = ivpu_mmu_structs_alloc(vdev);
+ if (ret)
+ return ret;
+
+ ret = ivpu_mmu_strtab_init(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to initialize strtab: %d\n", ret);
+ return ret;
+ }
+
+ ret = ivpu_mmu_enable(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to resume MMU: %d\n", ret);
+ return ret;
+ }
+
+ ivpu_dbg(vdev, MMU, "Init done\n");
+
+ return 0;
+}
+
+int ivpu_mmu_enable(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ int ret;
+
+ mutex_lock(&mmu->lock);
+
+ mmu->on = true;
+
+ ret = ivpu_mmu_reset(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to reset MMU: %d\n", ret);
+ goto err;
+ }
+
+ ret = ivpu_mmu_cmdq_write_cfgi_all(vdev);
+ if (ret)
+ goto err;
+
+ ret = ivpu_mmu_cmdq_write_tlbi_nsnh_all(vdev);
+ if (ret)
+ goto err;
+
+ ret = ivpu_mmu_cmdq_sync(vdev);
+ if (ret)
+ goto err;
+
+ mutex_unlock(&mmu->lock);
+
+ return 0;
+err:
+ mmu->on = false;
+ mutex_unlock(&mmu->lock);
+ return ret;
+}
+
+void ivpu_mmu_disable(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+
+ mutex_lock(&mmu->lock);
+ mmu->on = false;
+ mutex_unlock(&mmu->lock);
+}
+
+static void ivpu_mmu_dump_event(struct ivpu_device *vdev, u32 *event)
+{
+ u32 ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]);
+ u32 op = FIELD_GET(IVPU_MMU_EVT_OP_MASK, event[0]);
+ u64 fetch_addr = ((u64)event[7]) << 32 | event[6];
+ u64 in_addr = ((u64)event[5]) << 32 | event[4];
+ u32 sid = event[1];
+
+ ivpu_err_ratelimited(vdev, "MMU EVTQ: 0x%x (%s) SSID: %d SID: %d, e[2] %08x, e[3] %08x, in addr: 0x%llx, fetch addr: 0x%llx\n",
+ op, ivpu_mmu_event_to_str(op), ssid, sid,
+ event[2], event[3], in_addr, fetch_addr);
+}
+
+static u32 *ivpu_mmu_get_event(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_queue *evtq = &vdev->mmu->evtq;
+ u32 idx = IVPU_MMU_Q_IDX(evtq->cons);
+ u32 *evt = evtq->base + (idx * IVPU_MMU_EVTQ_CMD_SIZE);
+
+ evtq->prod = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
+ if (ivpu_mmu_queue_is_empty(evtq))
+ return NULL;
+
+ evtq->cons = (evtq->cons + 1) & IVPU_MMU_Q_WRAP_MASK;
+ return evt;
+}
+
+static int ivpu_mmu_evtq_set(struct ivpu_device *vdev, bool enable)
+{
+ u32 val = REGV_RD32(IVPU_MMU_REG_CR0);
+
+ if (enable)
+ val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
+ else
+ val = REG_CLR_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
+ REGV_WR32(IVPU_MMU_REG_CR0, val);
+
+ return REGV_POLL_FLD(IVPU_MMU_REG_CR0ACK, VAL, val, IVPU_MMU_REG_TIMEOUT_US);
+}
+
+static int ivpu_mmu_evtq_enable(struct ivpu_device *vdev)
+{
+ return ivpu_mmu_evtq_set(vdev, true);
+}
+
+static int ivpu_mmu_evtq_disable(struct ivpu_device *vdev)
+{
+ return ivpu_mmu_evtq_set(vdev, false);
+}
+
+void ivpu_mmu_discard_events(struct ivpu_device *vdev)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+
+ mutex_lock(&mmu->lock);
+ /*
+ * Disable event queue (stop MMU from updating the producer)
+ * to allow synchronization of consumer and producer indexes
+ */
+ ivpu_mmu_evtq_disable(vdev);
+
+ vdev->mmu->evtq.cons = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
+ REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
+ vdev->mmu->evtq.prod = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
+
+ ivpu_mmu_evtq_enable(vdev);
+
+ drm_WARN_ON_ONCE(&vdev->drm, vdev->mmu->evtq.cons != vdev->mmu->evtq.prod);
+
+ mutex_unlock(&mmu->lock);
+}
+
+int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid)
+{
+ struct ivpu_mmu_info *mmu = vdev->mmu;
+ struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
+ u64 *entry;
+ u64 val;
+
+ if (ssid > IVPU_MMU_CDTAB_ENT_COUNT)
+ return -EINVAL;
+
+ mutex_lock(&mmu->lock);
+
+ entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE);
+
+ val = READ_ONCE(entry[0]);
+ val &= ~IVPU_MMU_CD_0_R;
+ WRITE_ONCE(entry[0], val);
+
+ if (!ivpu_is_force_snoop_enabled(vdev))
+ clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);
+
+ ivpu_mmu_cmdq_write_cfgi_all(vdev);
+ ivpu_mmu_cmdq_sync(vdev);
+
+ mutex_unlock(&mmu->lock);
+
+ return 0;
+}
+
+void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
+{
+ struct ivpu_file_priv *file_priv;
+ u32 *event;
+ u32 ssid;
+
+ ivpu_dbg(vdev, IRQ, "MMU event queue\n");
+
+ while ((event = ivpu_mmu_get_event(vdev))) {
+ ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, *event);
+ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID ||
+ ssid == IVPU_RESERVED_CONTEXT_MMU_SSID) {
+ ivpu_mmu_dump_event(vdev, event);
+ ivpu_pm_trigger_recovery(vdev, "MMU event");
+ return;
+ }
+
+ file_priv = xa_load(&vdev->context_xa, ssid);
+ if (file_priv) {
+ if (!READ_ONCE(file_priv->has_mmu_faults)) {
+ ivpu_mmu_dump_event(vdev, event);
+ WRITE_ONCE(file_priv->has_mmu_faults, true);
+ }
+ }
+ }
+
+ queue_work(system_percpu_wq, &vdev->context_abort_work);
+}
+
+void ivpu_mmu_evtq_dump(struct ivpu_device *vdev)
+{
+ u32 *event;
+
+ while ((event = ivpu_mmu_get_event(vdev)) != NULL)
+ ivpu_mmu_dump_event(vdev, event);
+}
+
+void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev)
+{
+ u32 gerror_val, gerrorn_val, active;
+
+ ivpu_dbg(vdev, IRQ, "MMU error\n");
+
+ gerror_val = REGV_RD32(IVPU_MMU_REG_GERROR);
+ gerrorn_val = REGV_RD32(IVPU_MMU_REG_GERRORN);
+
+ active = gerror_val ^ gerrorn_val;
+ if (!(active & IVPU_MMU_GERROR_ERR_MASK))
+ return;
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, MSI_ABT, active))
+ ivpu_warn_ratelimited(vdev, "MMU MSI ABT write aborted\n");
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, MSI_PRIQ_ABT, active))
+ ivpu_warn_ratelimited(vdev, "MMU PRIQ MSI ABT write aborted\n");
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, MSI_EVTQ_ABT, active))
+ ivpu_warn_ratelimited(vdev, "MMU EVTQ MSI ABT write aborted\n");
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, MSI_CMDQ_ABT, active))
+ ivpu_warn_ratelimited(vdev, "MMU CMDQ MSI ABT write aborted\n");
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, PRIQ_ABT, active))
+ ivpu_err_ratelimited(vdev, "MMU PRIQ write aborted\n");
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, EVTQ_ABT, active))
+ ivpu_err_ratelimited(vdev, "MMU EVTQ write aborted\n");
+
+ if (REG_TEST_FLD(IVPU_MMU_REG_GERROR, CMDQ, active))
+ ivpu_err_ratelimited(vdev, "MMU CMDQ write aborted\n");
+
+ REGV_WR32(IVPU_MMU_REG_GERRORN, gerror_val);
+}
+
+int ivpu_mmu_cd_set(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable)
+{
+ return ivpu_mmu_cdtab_entry_set(vdev, ssid, pgtable->pgd_dma, true);
+}
+
+void ivpu_mmu_cd_clear(struct ivpu_device *vdev, int ssid)
+{
+ ivpu_mmu_cdtab_entry_set(vdev, ssid, 0, false);
+}
diff --git a/drivers/accel/ivpu/ivpu_mmu.h b/drivers/accel/ivpu/ivpu_mmu.h
new file mode 100644
index 000000000000..1ce7529746ad
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_mmu.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_MMU_H__
+#define __IVPU_MMU_H__
+
+struct ivpu_device;
+
+struct ivpu_mmu_cdtab {
+ void *base;
+ dma_addr_t dma;
+};
+
+struct ivpu_mmu_strtab {
+ void *base;
+ dma_addr_t dma;
+ u64 dma_q;
+ u32 base_cfg;
+};
+
+struct ivpu_mmu_queue {
+ void *base;
+ dma_addr_t dma;
+ u64 dma_q;
+ u32 prod;
+ u32 cons;
+};
+
+struct ivpu_mmu_info {
+ struct mutex lock; /* Protects cdtab, strtab, cmdq, on */
+ struct ivpu_mmu_cdtab cdtab;
+ struct ivpu_mmu_strtab strtab;
+ struct ivpu_mmu_queue cmdq;
+ struct ivpu_mmu_queue evtq;
+ bool on;
+};
+
+int ivpu_mmu_init(struct ivpu_device *vdev);
+void ivpu_mmu_disable(struct ivpu_device *vdev);
+int ivpu_mmu_enable(struct ivpu_device *vdev);
+int ivpu_mmu_cd_set(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable);
+void ivpu_mmu_cd_clear(struct ivpu_device *vdev, int ssid);
+int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid);
+
+void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev);
+void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev);
+void ivpu_mmu_evtq_dump(struct ivpu_device *vdev);
+void ivpu_mmu_discard_events(struct ivpu_device *vdev);
+int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid);
+
+#endif /* __IVPU_MMU_H__ */
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c
new file mode 100644
index 000000000000..87ad593ef47d
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_mmu_context.c
@@ -0,0 +1,640 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/highmem.h>
+#include <linux/set_memory.h>
+#include <linux/vmalloc.h>
+
+#include <drm/drm_cache.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_hw.h"
+#include "ivpu_mmu.h"
+#include "ivpu_mmu_context.h"
+
+#define IVPU_MMU_VPU_ADDRESS_MASK GENMASK(47, 12)
+#define IVPU_MMU_PGD_INDEX_MASK GENMASK(47, 39)
+#define IVPU_MMU_PUD_INDEX_MASK GENMASK(38, 30)
+#define IVPU_MMU_PMD_INDEX_MASK GENMASK(29, 21)
+#define IVPU_MMU_PTE_INDEX_MASK GENMASK(20, 12)
+#define IVPU_MMU_ENTRY_FLAGS_MASK (BIT(52) | GENMASK(11, 0))
+#define IVPU_MMU_ENTRY_FLAG_CONT BIT(52)
+#define IVPU_MMU_ENTRY_FLAG_NG BIT(11)
+#define IVPU_MMU_ENTRY_FLAG_AF BIT(10)
+#define IVPU_MMU_ENTRY_FLAG_RO BIT(7)
+#define IVPU_MMU_ENTRY_FLAG_USER BIT(6)
+#define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2)
+#define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE BIT(1)
+#define IVPU_MMU_ENTRY_FLAG_VALID BIT(0)
+
+#define IVPU_MMU_PAGE_SIZE SZ_4K
+#define IVPU_MMU_CONT_PAGES_SIZE (IVPU_MMU_PAGE_SIZE * 16)
+#define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE)
+#define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE)
+#define IVPU_MMU_PUD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PMD_MAP_SIZE)
+#define IVPU_MMU_PGD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PUD_MAP_SIZE)
+#define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64))
+
+#define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000
+#define IVPU_MMU_ENTRY_VALID (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID)
+#define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK)
+#define IVPU_MMU_ENTRY_MAPPED (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \
+ IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID)
+
+static void *ivpu_pgtable_alloc_page(struct ivpu_device *vdev, dma_addr_t *dma)
+{
+ dma_addr_t dma_addr;
+ struct page *page;
+ void *cpu;
+
+ page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+ if (!page)
+ return NULL;
+
+ set_pages_array_wc(&page, 1);
+
+ dma_addr = dma_map_page(vdev->drm.dev, page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(vdev->drm.dev, dma_addr))
+ goto err_free_page;
+
+ cpu = vmap(&page, 1, VM_MAP, pgprot_writecombine(PAGE_KERNEL));
+ if (!cpu)
+ goto err_dma_unmap_page;
+
+
+ *dma = dma_addr;
+ return cpu;
+
+err_dma_unmap_page:
+ dma_unmap_page(vdev->drm.dev, dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+err_free_page:
+ put_page(page);
+ return NULL;
+}
+
+static void ivpu_pgtable_free_page(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr)
+{
+ struct page *page;
+
+ if (cpu_addr) {
+ page = vmalloc_to_page(cpu_addr);
+ vunmap(cpu_addr);
+ dma_unmap_page(vdev->drm.dev, dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ set_pages_array_wb(&page, 1);
+ put_page(page);
+ }
+}
+
+static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
+{
+ int pgd_idx, pud_idx, pmd_idx;
+ dma_addr_t pud_dma, pmd_dma, pte_dma;
+ u64 *pud_dma_ptr, *pmd_dma_ptr, *pte_dma_ptr;
+
+ for (pgd_idx = 0; pgd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_idx) {
+ pud_dma_ptr = pgtable->pud_ptrs[pgd_idx];
+ pud_dma = pgtable->pgd_dma_ptr[pgd_idx];
+
+ if (!pud_dma_ptr)
+ continue;
+
+ for (pud_idx = 0; pud_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pud_idx) {
+ pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx];
+ pmd_dma = pgtable->pud_ptrs[pgd_idx][pud_idx];
+
+ if (!pmd_dma_ptr)
+ continue;
+
+ for (pmd_idx = 0; pmd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_idx) {
+ pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx];
+ pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx];
+
+ ivpu_pgtable_free_page(vdev, pte_dma_ptr, pte_dma);
+ }
+
+ kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]);
+ ivpu_pgtable_free_page(vdev, pmd_dma_ptr, pmd_dma);
+ }
+
+ kfree(pgtable->pmd_ptrs[pgd_idx]);
+ kfree(pgtable->pte_ptrs[pgd_idx]);
+ ivpu_pgtable_free_page(vdev, pud_dma_ptr, pud_dma);
+ }
+
+ ivpu_pgtable_free_page(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma);
+ pgtable->pgd_dma_ptr = NULL;
+ pgtable->pgd_dma = 0;
+}
+
+static u64*
+ivpu_mmu_ensure_pgd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
+{
+ u64 *pgd_dma_ptr = pgtable->pgd_dma_ptr;
+ dma_addr_t pgd_dma;
+
+ if (pgd_dma_ptr)
+ return pgd_dma_ptr;
+
+ pgd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pgd_dma);
+ if (!pgd_dma_ptr)
+ return NULL;
+
+ pgtable->pgd_dma_ptr = pgd_dma_ptr;
+ pgtable->pgd_dma = pgd_dma;
+
+ return pgd_dma_ptr;
+}
+
+static u64*
+ivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx)
+{
+ u64 *pud_dma_ptr = pgtable->pud_ptrs[pgd_idx];
+ dma_addr_t pud_dma;
+
+ if (pud_dma_ptr)
+ return pud_dma_ptr;
+
+ pud_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pud_dma);
+ if (!pud_dma_ptr)
+ return NULL;
+
+ drm_WARN_ON(&vdev->drm, pgtable->pmd_ptrs[pgd_idx]);
+ pgtable->pmd_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
+ if (!pgtable->pmd_ptrs[pgd_idx])
+ goto err_free_pud_dma_ptr;
+
+ drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx]);
+ pgtable->pte_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
+ if (!pgtable->pte_ptrs[pgd_idx])
+ goto err_free_pmd_ptrs;
+
+ pgtable->pud_ptrs[pgd_idx] = pud_dma_ptr;
+ pgtable->pgd_dma_ptr[pgd_idx] = pud_dma | IVPU_MMU_ENTRY_VALID;
+
+ return pud_dma_ptr;
+
+err_free_pmd_ptrs:
+ kfree(pgtable->pmd_ptrs[pgd_idx]);
+
+err_free_pud_dma_ptr:
+ ivpu_pgtable_free_page(vdev, pud_dma_ptr, pud_dma);
+ return NULL;
+}
+
+static u64*
+ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx,
+ int pud_idx)
+{
+ u64 *pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx];
+ dma_addr_t pmd_dma;
+
+ if (pmd_dma_ptr)
+ return pmd_dma_ptr;
+
+ pmd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pmd_dma);
+ if (!pmd_dma_ptr)
+ return NULL;
+
+ drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx][pud_idx]);
+ pgtable->pte_ptrs[pgd_idx][pud_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL);
+ if (!pgtable->pte_ptrs[pgd_idx][pud_idx])
+ goto err_free_pmd_dma_ptr;
+
+ pgtable->pmd_ptrs[pgd_idx][pud_idx] = pmd_dma_ptr;
+ pgtable->pud_ptrs[pgd_idx][pud_idx] = pmd_dma | IVPU_MMU_ENTRY_VALID;
+
+ return pmd_dma_ptr;
+
+err_free_pmd_dma_ptr:
+ ivpu_pgtable_free_page(vdev, pmd_dma_ptr, pmd_dma);
+ return NULL;
+}
+
+static u64*
+ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable,
+ int pgd_idx, int pud_idx, int pmd_idx)
+{
+ u64 *pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx];
+ dma_addr_t pte_dma;
+
+ if (pte_dma_ptr)
+ return pte_dma_ptr;
+
+ pte_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pte_dma);
+ if (!pte_dma_ptr)
+ return NULL;
+
+ pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma_ptr;
+ pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma | IVPU_MMU_ENTRY_VALID;
+
+ return pte_dma_ptr;
+}
+
+static int
+ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, dma_addr_t dma_addr, u64 prot)
+{
+ u64 *pte;
+ int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
+ int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
+ int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
+ int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
+
+ drm_WARN_ON(&vdev->drm, ctx->id == IVPU_RESERVED_CONTEXT_MMU_SSID);
+
+ /* Allocate PGD - first level page table if needed */
+ if (!ivpu_mmu_ensure_pgd(vdev, &ctx->pgtable))
+ return -ENOMEM;
+
+ /* Allocate PUD - second level page table if needed */
+ if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx))
+ return -ENOMEM;
+
+ /* Allocate PMD - third level page table if needed */
+ if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_idx, pud_idx))
+ return -ENOMEM;
+
+ /* Allocate PTE - fourth level page table if needed */
+ pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_idx, pud_idx, pmd_idx);
+ if (!pte)
+ return -ENOMEM;
+
+ /* Update PTE */
+ pte[pte_idx] = dma_addr | prot;
+
+ return 0;
+}
+
+static int
+ivpu_mmu_context_map_cont_64k(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr,
+ dma_addr_t dma_addr, u64 prot)
+{
+ size_t size = IVPU_MMU_CONT_PAGES_SIZE;
+
+ drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vpu_addr, size));
+ drm_WARN_ON(&vdev->drm, !IS_ALIGNED(dma_addr, size));
+
+ prot |= IVPU_MMU_ENTRY_FLAG_CONT;
+
+ while (size) {
+ int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot);
+
+ if (ret)
+ return ret;
+
+ size -= IVPU_MMU_PAGE_SIZE;
+ vpu_addr += IVPU_MMU_PAGE_SIZE;
+ dma_addr += IVPU_MMU_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr)
+{
+ int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
+ int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
+ int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
+ int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
+
+ /* Update PTE with dummy physical address and clear flags */
+ ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID;
+}
+
+static int
+ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, dma_addr_t dma_addr, size_t size, u64 prot)
+{
+ int map_size;
+ int ret;
+
+ while (size) {
+ if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE &&
+ IS_ALIGNED(vpu_addr | dma_addr, IVPU_MMU_CONT_PAGES_SIZE)) {
+ ret = ivpu_mmu_context_map_cont_64k(vdev, ctx, vpu_addr, dma_addr, prot);
+ map_size = IVPU_MMU_CONT_PAGES_SIZE;
+ } else {
+ ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot);
+ map_size = IVPU_MMU_PAGE_SIZE;
+ }
+
+ if (ret)
+ return ret;
+
+ vpu_addr += map_size;
+ dma_addr += map_size;
+ size -= map_size;
+ }
+
+ return 0;
+}
+
+static void ivpu_mmu_context_set_page_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr)
+{
+ int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
+ int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
+ int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
+ int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
+
+ ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] |= IVPU_MMU_ENTRY_FLAG_RO;
+}
+
+static void ivpu_mmu_context_split_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr)
+{
+ int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr);
+ int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr);
+ int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
+ int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
+
+ ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] &= ~IVPU_MMU_ENTRY_FLAG_CONT;
+}
+
+static void ivpu_mmu_context_split_64k_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr)
+{
+ u64 start = ALIGN_DOWN(vpu_addr, IVPU_MMU_CONT_PAGES_SIZE);
+ u64 end = ALIGN(vpu_addr, IVPU_MMU_CONT_PAGES_SIZE);
+ u64 offset = 0;
+
+ ivpu_dbg(vdev, MMU_MAP, "Split 64K page ctx: %u vpu_addr: 0x%llx\n", ctx->id, vpu_addr);
+
+ while (start + offset < end) {
+ ivpu_mmu_context_split_page(vdev, ctx, start + offset);
+ offset += IVPU_MMU_PAGE_SIZE;
+ }
+}
+
+int
+ivpu_mmu_context_set_pages_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr,
+ size_t size)
+{
+ u64 end = vpu_addr + size;
+ size_t size_left = size;
+ int ret;
+
+ if (size == 0)
+ return 0;
+
+ if (drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vpu_addr | size, IVPU_MMU_PAGE_SIZE)))
+ return -EINVAL;
+
+ mutex_lock(&ctx->lock);
+
+ ivpu_dbg(vdev, MMU_MAP, "Set read-only pages ctx: %u vpu_addr: 0x%llx size: %lu\n",
+ ctx->id, vpu_addr, size);
+
+ if (!ivpu_disable_mmu_cont_pages) {
+ /* Split 64K contiguous page at the beginning if needed */
+ if (!IS_ALIGNED(vpu_addr, IVPU_MMU_CONT_PAGES_SIZE))
+ ivpu_mmu_context_split_64k_page(vdev, ctx, vpu_addr);
+
+ /* Split 64K contiguous page at the end if needed */
+ if (!IS_ALIGNED(vpu_addr + size, IVPU_MMU_CONT_PAGES_SIZE))
+ ivpu_mmu_context_split_64k_page(vdev, ctx, vpu_addr + size);
+ }
+
+ while (size_left) {
+ if (vpu_addr < end)
+ ivpu_mmu_context_set_page_ro(vdev, ctx, vpu_addr);
+
+ vpu_addr += IVPU_MMU_PAGE_SIZE;
+ size_left -= IVPU_MMU_PAGE_SIZE;
+ }
+
+ /* Ensure page table modifications are flushed from wc buffers to memory */
+ wmb();
+
+ mutex_unlock(&ctx->lock);
+ ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
+ if (ret)
+ ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
+
+ return 0;
+}
+
+static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size)
+{
+ while (size) {
+ ivpu_mmu_context_unmap_page(ctx, vpu_addr);
+ vpu_addr += IVPU_MMU_PAGE_SIZE;
+ size -= IVPU_MMU_PAGE_SIZE;
+ }
+}
+
+int
+ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only)
+{
+ size_t start_vpu_addr = vpu_addr;
+ struct scatterlist *sg;
+ int ret;
+ u64 prot;
+ u64 i;
+
+ if (drm_WARN_ON(&vdev->drm, !ctx))
+ return -EINVAL;
+
+ if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE))
+ return -EINVAL;
+
+ if (vpu_addr & ~IVPU_MMU_VPU_ADDRESS_MASK)
+ return -EINVAL;
+
+ prot = IVPU_MMU_ENTRY_MAPPED;
+ if (llc_coherent)
+ prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT;
+ if (read_only)
+ prot |= IVPU_MMU_ENTRY_FLAG_RO;
+
+ mutex_lock(&ctx->lock);
+
+ for_each_sgtable_dma_sg(sgt, sg, i) {
+ dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset;
+ size_t size = sg_dma_len(sg) + sg->offset;
+
+ ivpu_dbg(vdev, MMU_MAP, "Map ctx: %u dma_addr: 0x%llx vpu_addr: 0x%llx size: %lu\n",
+ ctx->id, dma_addr, vpu_addr, size);
+
+ ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot);
+ if (ret) {
+ ivpu_err(vdev, "Failed to map context pages\n");
+ goto err_unmap_pages;
+ }
+ vpu_addr += size;
+ }
+
+ if (!ctx->is_cd_valid) {
+ ret = ivpu_mmu_cd_set(vdev, ctx->id, &ctx->pgtable);
+ if (ret) {
+ ivpu_err(vdev, "Failed to set context descriptor for context %u: %d\n",
+ ctx->id, ret);
+ goto err_unmap_pages;
+ }
+ ctx->is_cd_valid = true;
+ }
+
+ /* Ensure page table modifications are flushed from wc buffers to memory */
+ wmb();
+
+ ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
+ if (ret) {
+ ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
+ goto err_unmap_pages;
+ }
+
+ mutex_unlock(&ctx->lock);
+ return 0;
+
+err_unmap_pages:
+ ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, vpu_addr - start_vpu_addr);
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+void
+ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, struct sg_table *sgt)
+{
+ struct scatterlist *sg;
+ int ret;
+ u64 i;
+
+ if (drm_WARN_ON(&vdev->drm, !ctx))
+ return;
+
+ mutex_lock(&ctx->lock);
+
+ for_each_sgtable_dma_sg(sgt, sg, i) {
+ dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset;
+ size_t size = sg_dma_len(sg) + sg->offset;
+
+ ivpu_dbg(vdev, MMU_MAP, "Unmap ctx: %u dma_addr: 0x%llx vpu_addr: 0x%llx size: %lu\n",
+ ctx->id, dma_addr, vpu_addr, size);
+
+ ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size);
+ vpu_addr += size;
+ }
+
+ /* Ensure page table modifications are flushed from wc buffers to memory */
+ wmb();
+
+ mutex_unlock(&ctx->lock);
+
+ ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
+ if (ret)
+ ivpu_warn_ratelimited(vdev, "Failed to invalidate TLB for ctx %u: %d\n",
+ ctx->id, ret);
+}
+
+int
+ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range,
+ u64 size, struct drm_mm_node *node)
+{
+ int ret;
+
+ WARN_ON(!range);
+
+ mutex_lock(&ctx->lock);
+ if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE) {
+ ret = drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0,
+ range->start, range->end, DRM_MM_INSERT_BEST);
+ if (!ret)
+ goto unlock;
+ }
+
+ ret = drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0,
+ range->start, range->end, DRM_MM_INSERT_BEST);
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+void
+ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node)
+{
+ mutex_lock(&ctx->lock);
+ drm_mm_remove_node(node);
+ mutex_unlock(&ctx->lock);
+}
+
+void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id)
+{
+ u64 start, end;
+
+ mutex_init(&ctx->lock);
+
+ if (!context_id) {
+ start = vdev->hw->ranges.runtime.start;
+ end = vdev->hw->ranges.shave.end;
+ } else {
+ start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
+ end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
+ }
+
+ drm_mm_init(&ctx->mm, start, end - start);
+ ctx->id = context_id;
+}
+
+void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
+{
+ if (ctx->is_cd_valid) {
+ ivpu_mmu_cd_clear(vdev, ctx->id);
+ ctx->is_cd_valid = false;
+ }
+
+ mutex_destroy(&ctx->lock);
+ ivpu_mmu_pgtables_free(vdev, &ctx->pgtable);
+ drm_mm_takedown(&ctx->mm);
+}
+
+void ivpu_mmu_global_context_init(struct ivpu_device *vdev)
+{
+ ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID);
+}
+
+void ivpu_mmu_global_context_fini(struct ivpu_device *vdev)
+{
+ ivpu_mmu_context_fini(vdev, &vdev->gctx);
+}
+
+int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ivpu_mmu_context_init(vdev, &vdev->rctx, IVPU_RESERVED_CONTEXT_MMU_SSID);
+
+ mutex_lock(&vdev->rctx.lock);
+
+ if (!ivpu_mmu_ensure_pgd(vdev, &vdev->rctx.pgtable)) {
+ ivpu_err(vdev, "Failed to allocate root page table for reserved context\n");
+ ret = -ENOMEM;
+ goto err_ctx_fini;
+ }
+
+ ret = ivpu_mmu_cd_set(vdev, vdev->rctx.id, &vdev->rctx.pgtable);
+ if (ret) {
+ ivpu_err(vdev, "Failed to set context descriptor for reserved context\n");
+ goto err_ctx_fini;
+ }
+
+ mutex_unlock(&vdev->rctx.lock);
+ return ret;
+
+err_ctx_fini:
+ mutex_unlock(&vdev->rctx.lock);
+ ivpu_mmu_context_fini(vdev, &vdev->rctx);
+ return ret;
+}
+
+void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev)
+{
+ ivpu_mmu_cd_clear(vdev, vdev->rctx.id);
+ ivpu_mmu_context_fini(vdev, &vdev->rctx);
+}
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.h b/drivers/accel/ivpu/ivpu_mmu_context.h
new file mode 100644
index 000000000000..663a11a9db11
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_mmu_context.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2023 Intel Corporation
+ */
+
+#ifndef __IVPU_MMU_CONTEXT_H__
+#define __IVPU_MMU_CONTEXT_H__
+
+#include <drm/drm_mm.h>
+
+struct ivpu_device;
+struct ivpu_file_priv;
+struct ivpu_addr_range;
+
+#define IVPU_MMU_PGTABLE_ENTRIES 512ull
+
+struct ivpu_mmu_pgtable {
+ u64 ***pte_ptrs[IVPU_MMU_PGTABLE_ENTRIES];
+ u64 **pmd_ptrs[IVPU_MMU_PGTABLE_ENTRIES];
+ u64 *pud_ptrs[IVPU_MMU_PGTABLE_ENTRIES];
+ u64 *pgd_dma_ptr;
+ dma_addr_t pgd_dma;
+};
+
+struct ivpu_mmu_context {
+ struct mutex lock; /* Protects: mm, pgtable, is_cd_valid */
+ struct drm_mm mm;
+ struct ivpu_mmu_pgtable pgtable;
+ bool is_cd_valid;
+ u32 id;
+};
+
+void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id);
+void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
+void ivpu_mmu_global_context_init(struct ivpu_device *vdev);
+void ivpu_mmu_global_context_fini(struct ivpu_device *vdev);
+int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev);
+void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev);
+
+int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range,
+ u64 size, struct drm_mm_node *node);
+void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node);
+
+int ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only);
+void ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, struct sg_table *sgt);
+int ivpu_mmu_context_set_pages_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+ u64 vpu_addr, size_t size);
+
+#endif /* __IVPU_MMU_CONTEXT_H__ */
diff --git a/drivers/accel/ivpu/ivpu_ms.c b/drivers/accel/ivpu/ivpu_ms.c
new file mode 100644
index 000000000000..1d9c1cb17924
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_ms.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <drm/drm_file.h>
+#include <linux/pm_runtime.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_gem.h"
+#include "ivpu_hw.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_ms.h"
+#include "ivpu_pm.h"
+
+#define MS_INFO_BUFFER_SIZE SZ_64K
+#define MS_NUM_BUFFERS 2
+#define MS_READ_PERIOD_MULTIPLIER 2
+#define MS_MIN_SAMPLE_PERIOD_NS 1000000
+
+static struct ivpu_ms_instance *
+get_instance_by_mask(struct ivpu_file_priv *file_priv, u64 metric_mask)
+{
+ struct ivpu_ms_instance *ms;
+
+ lockdep_assert_held(&file_priv->ms_lock);
+
+ list_for_each_entry(ms, &file_priv->ms_instance_list, ms_instance_node)
+ if (ms->mask == metric_mask)
+ return ms;
+
+ return NULL;
+}
+
+int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct drm_ivpu_metric_streamer_start *args = data;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_ms_instance *ms;
+ u32 sample_size;
+ u64 buf_size;
+ int ret;
+
+ if (!args->metric_group_mask || !args->read_period_samples ||
+ args->sampling_period_ns < MS_MIN_SAMPLE_PERIOD_NS)
+ return -EINVAL;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&file_priv->ms_lock);
+
+ if (get_instance_by_mask(file_priv, args->metric_group_mask)) {
+ ivpu_dbg(vdev, IOCTL, "Instance already exists (mask %#llx)\n",
+ args->metric_group_mask);
+ ret = -EALREADY;
+ goto unlock;
+ }
+
+ ms = kzalloc(sizeof(*ms), GFP_KERNEL);
+ if (!ms) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ms->mask = args->metric_group_mask;
+
+ ret = ivpu_jsm_metric_streamer_info(vdev, ms->mask, 0, 0, &sample_size, NULL);
+ if (ret)
+ goto err_free_ms;
+
+ buf_size = PAGE_ALIGN((u64)args->read_period_samples * sample_size *
+ MS_READ_PERIOD_MULTIPLIER * MS_NUM_BUFFERS);
+ if (buf_size > ivpu_hw_range_size(&vdev->hw->ranges.global)) {
+ ivpu_dbg(vdev, IOCTL, "Requested MS buffer size %llu exceeds range size %llu\n",
+ buf_size, ivpu_hw_range_size(&vdev->hw->ranges.global));
+ ret = -EINVAL;
+ goto err_free_ms;
+ }
+
+ ms->bo = ivpu_bo_create_global(vdev, buf_size, DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
+ if (!ms->bo) {
+ ivpu_dbg(vdev, IOCTL, "Failed to allocate MS buffer (size %llu)\n", buf_size);
+ ret = -ENOMEM;
+ goto err_free_ms;
+ }
+
+ ms->buff_size = ivpu_bo_size(ms->bo) / MS_NUM_BUFFERS;
+ ms->active_buff_vpu_addr = ms->bo->vpu_addr;
+ ms->inactive_buff_vpu_addr = ms->bo->vpu_addr + ms->buff_size;
+ ms->active_buff_ptr = ivpu_bo_vaddr(ms->bo);
+ ms->inactive_buff_ptr = ivpu_bo_vaddr(ms->bo) + ms->buff_size;
+
+ ret = ivpu_jsm_metric_streamer_start(vdev, ms->mask, args->sampling_period_ns,
+ ms->active_buff_vpu_addr, ms->buff_size);
+ if (ret)
+ goto err_free_bo;
+
+ args->sample_size = sample_size;
+ args->max_data_size = ivpu_bo_size(ms->bo);
+ list_add_tail(&ms->ms_instance_node, &file_priv->ms_instance_list);
+ goto unlock;
+
+err_free_bo:
+ ivpu_bo_free(ms->bo);
+err_free_ms:
+ kfree(ms);
+unlock:
+ mutex_unlock(&file_priv->ms_lock);
+
+ ivpu_rpm_put(vdev);
+ return ret;
+}
+
+static int
+copy_leftover_bytes(struct ivpu_ms_instance *ms,
+ void __user *user_ptr, u64 user_size, u64 *user_bytes_copied)
+{
+ u64 copy_bytes;
+
+ if (ms->leftover_bytes) {
+ copy_bytes = min(user_size - *user_bytes_copied, ms->leftover_bytes);
+ if (copy_to_user(user_ptr + *user_bytes_copied, ms->leftover_addr, copy_bytes))
+ return -EFAULT;
+
+ ms->leftover_bytes -= copy_bytes;
+ ms->leftover_addr += copy_bytes;
+ *user_bytes_copied += copy_bytes;
+ }
+
+ return 0;
+}
+
+static int
+copy_samples_to_user(struct ivpu_device *vdev, struct ivpu_ms_instance *ms,
+ void __user *user_ptr, u64 user_size, u64 *user_bytes_copied)
+{
+ u64 bytes_written;
+ int ret;
+
+ *user_bytes_copied = 0;
+
+ ret = copy_leftover_bytes(ms, user_ptr, user_size, user_bytes_copied);
+ if (ret)
+ return ret;
+
+ if (*user_bytes_copied == user_size)
+ return 0;
+
+ ret = ivpu_jsm_metric_streamer_update(vdev, ms->mask, ms->inactive_buff_vpu_addr,
+ ms->buff_size, &bytes_written);
+ if (ret)
+ return ret;
+
+ swap(ms->active_buff_vpu_addr, ms->inactive_buff_vpu_addr);
+ swap(ms->active_buff_ptr, ms->inactive_buff_ptr);
+
+ ms->leftover_bytes = bytes_written;
+ ms->leftover_addr = ms->inactive_buff_ptr;
+
+ return copy_leftover_bytes(ms, user_ptr, user_size, user_bytes_copied);
+}
+
+int ivpu_ms_get_data_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_metric_streamer_get_data *args = data;
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_ms_instance *ms;
+ u64 bytes_written;
+ int ret;
+
+ if (!args->metric_group_mask)
+ return -EINVAL;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&file_priv->ms_lock);
+
+ ms = get_instance_by_mask(file_priv, args->metric_group_mask);
+ if (!ms) {
+ ivpu_dbg(vdev, IOCTL, "Instance doesn't exist for mask: %#llx\n",
+ args->metric_group_mask);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (!args->buffer_size) {
+ ret = ivpu_jsm_metric_streamer_update(vdev, ms->mask, 0, 0, &bytes_written);
+ if (ret)
+ goto unlock;
+ args->data_size = bytes_written + ms->leftover_bytes;
+ goto unlock;
+ }
+
+ if (!args->buffer_ptr) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = copy_samples_to_user(vdev, ms, u64_to_user_ptr(args->buffer_ptr),
+ args->buffer_size, &args->data_size);
+unlock:
+ mutex_unlock(&file_priv->ms_lock);
+
+ ivpu_rpm_put(vdev);
+ return ret;
+}
+
+static void free_instance(struct ivpu_file_priv *file_priv, struct ivpu_ms_instance *ms)
+{
+ lockdep_assert_held(&file_priv->ms_lock);
+
+ list_del(&ms->ms_instance_node);
+ ivpu_jsm_metric_streamer_stop(file_priv->vdev, ms->mask);
+ ivpu_bo_free(ms->bo);
+ kfree(ms);
+}
+
+int ivpu_ms_stop_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct drm_ivpu_metric_streamer_stop *args = data;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_ms_instance *ms;
+ int ret;
+
+ if (!args->metric_group_mask)
+ return -EINVAL;
+
+ ret = ivpu_rpm_get(vdev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&file_priv->ms_lock);
+
+ ms = get_instance_by_mask(file_priv, args->metric_group_mask);
+ if (ms)
+ free_instance(file_priv, ms);
+
+ mutex_unlock(&file_priv->ms_lock);
+
+ ivpu_rpm_put(vdev);
+ return ms ? 0 : -EINVAL;
+}
+
+static inline struct ivpu_bo *get_ms_info_bo(struct ivpu_file_priv *file_priv)
+{
+ lockdep_assert_held(&file_priv->ms_lock);
+
+ if (file_priv->ms_info_bo)
+ return file_priv->ms_info_bo;
+
+ file_priv->ms_info_bo = ivpu_bo_create_global(file_priv->vdev, MS_INFO_BUFFER_SIZE,
+ DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
+ return file_priv->ms_info_bo;
+}
+
+int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_metric_streamer_get_data *args = data;
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
+ struct ivpu_bo *bo;
+ u64 info_size;
+ int ret;
+
+ if (!args->metric_group_mask)
+ return -EINVAL;
+
+ if (!args->buffer_size)
+ return ivpu_jsm_metric_streamer_info(vdev, args->metric_group_mask,
+ 0, 0, NULL, &args->data_size);
+ if (!args->buffer_ptr)
+ return -EINVAL;
+
+ mutex_lock(&file_priv->ms_lock);
+
+ bo = get_ms_info_bo(file_priv);
+ if (!bo) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = ivpu_jsm_metric_streamer_info(vdev, args->metric_group_mask, bo->vpu_addr,
+ ivpu_bo_size(bo), NULL, &info_size);
+ if (ret)
+ goto unlock;
+
+ if (args->buffer_size < info_size) {
+ ret = -ENOSPC;
+ goto unlock;
+ }
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer_ptr), ivpu_bo_vaddr(bo), info_size))
+ ret = -EFAULT;
+
+ args->data_size = info_size;
+unlock:
+ mutex_unlock(&file_priv->ms_lock);
+
+ return ret;
+}
+
+void ivpu_ms_cleanup(struct ivpu_file_priv *file_priv)
+{
+ struct ivpu_ms_instance *ms, *tmp;
+ struct ivpu_device *vdev = file_priv->vdev;
+
+ pm_runtime_get_sync(vdev->drm.dev);
+
+ mutex_lock(&file_priv->ms_lock);
+
+ if (file_priv->ms_info_bo) {
+ ivpu_bo_free(file_priv->ms_info_bo);
+ file_priv->ms_info_bo = NULL;
+ }
+
+ list_for_each_entry_safe(ms, tmp, &file_priv->ms_instance_list, ms_instance_node)
+ free_instance(file_priv, ms);
+
+ mutex_unlock(&file_priv->ms_lock);
+
+ pm_runtime_put_autosuspend(vdev->drm.dev);
+}
+
+void ivpu_ms_cleanup_all(struct ivpu_device *vdev)
+{
+ struct ivpu_file_priv *file_priv;
+ unsigned long ctx_id;
+
+ mutex_lock(&vdev->context_list_lock);
+
+ xa_for_each(&vdev->context_xa, ctx_id, file_priv)
+ ivpu_ms_cleanup(file_priv);
+
+ mutex_unlock(&vdev->context_list_lock);
+}
diff --git a/drivers/accel/ivpu/ivpu_ms.h b/drivers/accel/ivpu/ivpu_ms.h
new file mode 100644
index 000000000000..fbd5ebebc3d9
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_ms.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+#ifndef __IVPU_MS_H__
+#define __IVPU_MS_H__
+
+#include <linux/list.h>
+
+struct drm_device;
+struct drm_file;
+struct ivpu_bo;
+struct ivpu_device;
+struct ivpu_file_priv;
+
+struct ivpu_ms_instance {
+ struct ivpu_bo *bo;
+ struct list_head ms_instance_node;
+ u64 mask;
+ u64 buff_size;
+ u64 active_buff_vpu_addr;
+ u64 inactive_buff_vpu_addr;
+ void *active_buff_ptr;
+ void *inactive_buff_ptr;
+ u64 leftover_bytes;
+ void *leftover_addr;
+};
+
+int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_ms_stop_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_ms_get_data_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+void ivpu_ms_cleanup(struct ivpu_file_priv *file_priv);
+void ivpu_ms_cleanup_all(struct ivpu_device *vdev);
+
+#endif /* __IVPU_MS_H__ */
diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c
new file mode 100644
index 000000000000..480c075d87f6
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_pm.c
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#include <linux/highmem.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/reboot.h>
+
+#include "ivpu_coredump.h"
+#include "ivpu_drv.h"
+#include "ivpu_fw.h"
+#include "ivpu_fw_log.h"
+#include "ivpu_hw.h"
+#include "ivpu_ipc.h"
+#include "ivpu_job.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_mmu.h"
+#include "ivpu_ms.h"
+#include "ivpu_pm.h"
+#include "ivpu_trace.h"
+#include "vpu_boot_api.h"
+
+static bool ivpu_disable_recovery;
+#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG)
+module_param_named_unsafe(disable_recovery, ivpu_disable_recovery, bool, 0644);
+MODULE_PARM_DESC(disable_recovery, "Disables recovery when NPU hang is detected");
+#endif
+
+static unsigned long ivpu_tdr_timeout_ms;
+module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, ulong, 0644);
+MODULE_PARM_DESC(tdr_timeout_ms, "Timeout for device hang detection, in milliseconds, 0 - default");
+
+static unsigned long ivpu_inference_timeout_ms;
+module_param_named(inference_timeout_ms, ivpu_inference_timeout_ms, ulong, 0644);
+MODULE_PARM_DESC(inference_timeout_ms, "Inference maximum duration, in milliseconds, 0 - default");
+
+#define PM_RESCHEDULE_LIMIT 5
+
+static void ivpu_pm_prepare_cold_boot(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+
+ ivpu_cmdq_reset_all_contexts(vdev);
+ ivpu_ipc_reset(vdev);
+ ivpu_fw_log_reset(vdev);
+ ivpu_fw_load(vdev);
+ fw->entry_point = fw->cold_boot_entry_point;
+ fw->last_heartbeat = 0;
+}
+
+static void ivpu_pm_prepare_warm_boot(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+ struct vpu_boot_params *bp = ivpu_bo_vaddr(fw->mem_bp);
+
+ if (!bp->save_restore_ret_address) {
+ ivpu_pm_prepare_cold_boot(vdev);
+ return;
+ }
+
+ ivpu_dbg(vdev, FW_BOOT, "Save/restore entry point %llx", bp->save_restore_ret_address);
+ fw->entry_point = bp->save_restore_ret_address;
+}
+
+static int ivpu_suspend(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ivpu_prepare_for_reset(vdev);
+
+ ret = ivpu_shutdown(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to shutdown NPU: %d\n", ret);
+
+ return ret;
+}
+
+static int ivpu_resume(struct ivpu_device *vdev)
+{
+ int ret;
+
+retry:
+ pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D0);
+ pci_restore_state(to_pci_dev(vdev->drm.dev));
+
+ ret = ivpu_hw_power_up(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to power up HW: %d\n", ret);
+ goto err_power_down;
+ }
+
+ ret = ivpu_mmu_enable(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to resume MMU: %d\n", ret);
+ goto err_power_down;
+ }
+
+ ret = ivpu_boot(vdev);
+ if (ret)
+ goto err_mmu_disable;
+
+ return 0;
+
+err_mmu_disable:
+ ivpu_mmu_disable(vdev);
+err_power_down:
+ ivpu_hw_power_down(vdev);
+ pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot);
+
+ if (!ivpu_fw_is_cold_boot(vdev)) {
+ ivpu_pm_prepare_cold_boot(vdev);
+ goto retry;
+ } else {
+ ivpu_err(vdev, "Failed to resume the FW: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static void ivpu_pm_reset_begin(struct ivpu_device *vdev)
+{
+ pm_runtime_disable(vdev->drm.dev);
+
+ atomic_inc(&vdev->pm->reset_counter);
+ atomic_set(&vdev->pm->reset_pending, 1);
+ down_write(&vdev->pm->reset_lock);
+}
+
+static void ivpu_pm_reset_complete(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ivpu_pm_prepare_cold_boot(vdev);
+ ivpu_jobs_abort_all(vdev);
+ ivpu_ms_cleanup_all(vdev);
+
+ ret = ivpu_resume(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Failed to resume NPU: %d\n", ret);
+ pm_runtime_set_suspended(vdev->drm.dev);
+ } else {
+ pm_runtime_set_active(vdev->drm.dev);
+ }
+
+ up_write(&vdev->pm->reset_lock);
+ atomic_set(&vdev->pm->reset_pending, 0);
+
+ pm_runtime_mark_last_busy(vdev->drm.dev);
+ pm_runtime_enable(vdev->drm.dev);
+}
+
+static void ivpu_pm_recovery_work(struct work_struct *work)
+{
+ struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, recovery_work);
+ struct ivpu_device *vdev = pm->vdev;
+ char *evt[2] = {"IVPU_PM_EVENT=IVPU_RECOVER", NULL};
+
+ ivpu_err(vdev, "Recovering the NPU (reset #%d)\n", atomic_read(&vdev->pm->reset_counter));
+
+ ivpu_pm_reset_begin(vdev);
+
+ if (!pm_runtime_status_suspended(vdev->drm.dev)) {
+ ivpu_jsm_state_dump(vdev);
+ ivpu_dev_coredump(vdev);
+ ivpu_suspend(vdev);
+ }
+
+ ivpu_pm_reset_complete(vdev);
+
+ kobject_uevent_env(&vdev->drm.dev->kobj, KOBJ_CHANGE, evt);
+}
+
+void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason)
+{
+ ivpu_err(vdev, "Recovery triggered by %s\n", reason);
+
+ if (ivpu_disable_recovery) {
+ ivpu_err(vdev, "Recovery not available when disable_recovery param is set\n");
+ return;
+ }
+
+ /* Trigger recovery if it's not in progress */
+ if (atomic_cmpxchg(&vdev->pm->reset_pending, 0, 1) == 0) {
+ ivpu_hw_diagnose_failure(vdev);
+ ivpu_hw_irq_disable(vdev); /* Disable IRQ early to protect from IRQ storm */
+ queue_work(system_dfl_wq, &vdev->pm->recovery_work);
+ }
+}
+
+static void ivpu_job_timeout_work(struct work_struct *work)
+{
+ struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, job_timeout_work.work);
+ struct ivpu_device *vdev = pm->vdev;
+ unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr;
+ unsigned long inference_timeout_ms = ivpu_inference_timeout_ms ? ivpu_inference_timeout_ms :
+ vdev->timeout.inference;
+ u64 inference_max_retries;
+ u64 heartbeat;
+
+ if (ivpu_jsm_get_heartbeat(vdev, 0, &heartbeat) || heartbeat <= vdev->fw->last_heartbeat) {
+ ivpu_err(vdev, "Job timeout detected, heartbeat not progressed\n");
+ goto recovery;
+ }
+
+ inference_max_retries = DIV_ROUND_UP(inference_timeout_ms, timeout_ms);
+ if (atomic_fetch_inc(&vdev->job_timeout_counter) >= inference_max_retries) {
+ ivpu_err(vdev, "Job timeout detected, heartbeat limit (%lld) exceeded\n",
+ inference_max_retries);
+ goto recovery;
+ }
+
+ vdev->fw->last_heartbeat = heartbeat;
+ ivpu_start_job_timeout_detection(vdev);
+ return;
+
+recovery:
+ atomic_set(&vdev->job_timeout_counter, 0);
+ ivpu_pm_trigger_recovery(vdev, "TDR");
+}
+
+void ivpu_start_job_timeout_detection(struct ivpu_device *vdev)
+{
+ unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr;
+
+ /* No-op if already queued */
+ queue_delayed_work(system_percpu_wq, &vdev->pm->job_timeout_work,
+ msecs_to_jiffies(timeout_ms));
+}
+
+void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev)
+{
+ cancel_delayed_work_sync(&vdev->pm->job_timeout_work);
+ atomic_set(&vdev->job_timeout_counter, 0);
+}
+
+int ivpu_pm_suspend_cb(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ unsigned long timeout;
+
+ trace_pm("suspend");
+ ivpu_dbg(vdev, PM, "Suspend..\n");
+
+ timeout = jiffies + msecs_to_jiffies(vdev->timeout.tdr);
+ while (!ivpu_hw_is_idle(vdev)) {
+ cond_resched();
+ if (time_after_eq(jiffies, timeout)) {
+ ivpu_err(vdev, "Failed to enter idle on system suspend\n");
+ return -EBUSY;
+ }
+ }
+
+ ivpu_jsm_pwr_d0i3_enter(vdev);
+
+ ivpu_suspend(vdev);
+ ivpu_pm_prepare_warm_boot(vdev);
+
+ ivpu_dbg(vdev, PM, "Suspend done.\n");
+ trace_pm("suspend done");
+
+ return 0;
+}
+
+int ivpu_pm_resume_cb(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ int ret;
+
+ trace_pm("resume");
+ ivpu_dbg(vdev, PM, "Resume..\n");
+
+ ret = ivpu_resume(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to resume: %d\n", ret);
+
+ ivpu_dbg(vdev, PM, "Resume done.\n");
+ trace_pm("resume done");
+
+ return ret;
+}
+
+int ivpu_pm_runtime_suspend_cb(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ int ret, ret_d0i3;
+ bool is_idle;
+
+ drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
+ drm_WARN_ON(&vdev->drm, work_pending(&vdev->pm->recovery_work));
+
+ trace_pm("runtime suspend");
+ ivpu_dbg(vdev, PM, "Runtime suspend..\n");
+
+ ivpu_mmu_disable(vdev);
+
+ is_idle = ivpu_hw_is_idle(vdev) || vdev->pm->dct_active_percent;
+ if (!is_idle)
+ ivpu_err(vdev, "NPU is not idle before autosuspend\n");
+
+ ret_d0i3 = ivpu_jsm_pwr_d0i3_enter(vdev);
+ if (ret_d0i3)
+ ivpu_err(vdev, "Failed to prepare for d0i3: %d\n", ret_d0i3);
+
+ ret = ivpu_suspend(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to suspend NPU: %d\n", ret);
+
+ if (!is_idle || ret_d0i3) {
+ ivpu_err(vdev, "Forcing cold boot due to previous errors\n");
+ atomic_inc(&vdev->pm->reset_counter);
+ ivpu_dev_coredump(vdev);
+ ivpu_pm_prepare_cold_boot(vdev);
+ } else {
+ ivpu_pm_prepare_warm_boot(vdev);
+ }
+
+ ivpu_dbg(vdev, PM, "Runtime suspend done.\n");
+ trace_pm("runtime suspend done");
+
+ return 0;
+}
+
+int ivpu_pm_runtime_resume_cb(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ int ret;
+
+ trace_pm("runtime resume");
+ ivpu_dbg(vdev, PM, "Runtime resume..\n");
+
+ ret = ivpu_resume(vdev);
+ if (ret)
+ ivpu_err(vdev, "Failed to set RESUME state: %d\n", ret);
+
+ ivpu_dbg(vdev, PM, "Runtime resume done.\n");
+ trace_pm("runtime resume done");
+
+ return ret;
+}
+
+int ivpu_rpm_get(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(vdev->drm.dev);
+ if (ret < 0) {
+ ivpu_err(vdev, "Failed to resume NPU: %d\n", ret);
+ pm_runtime_set_suspended(vdev->drm.dev);
+ }
+
+ return ret;
+}
+
+void ivpu_rpm_put(struct ivpu_device *vdev)
+{
+ pm_runtime_put_autosuspend(vdev->drm.dev);
+}
+
+void ivpu_pm_reset_prepare_cb(struct pci_dev *pdev)
+{
+ struct ivpu_device *vdev = pci_get_drvdata(pdev);
+
+ ivpu_dbg(vdev, PM, "Pre-reset..\n");
+
+ ivpu_pm_reset_begin(vdev);
+
+ if (!pm_runtime_status_suspended(vdev->drm.dev)) {
+ ivpu_prepare_for_reset(vdev);
+ ivpu_hw_reset(vdev);
+ }
+
+ ivpu_dbg(vdev, PM, "Pre-reset done.\n");
+}
+
+void ivpu_pm_reset_done_cb(struct pci_dev *pdev)
+{
+ struct ivpu_device *vdev = pci_get_drvdata(pdev);
+
+ ivpu_dbg(vdev, PM, "Post-reset..\n");
+
+ ivpu_pm_reset_complete(vdev);
+
+ ivpu_dbg(vdev, PM, "Post-reset done.\n");
+}
+
+void ivpu_pm_init(struct ivpu_device *vdev)
+{
+ struct device *dev = vdev->drm.dev;
+ struct ivpu_pm_info *pm = vdev->pm;
+ int delay;
+
+ pm->vdev = vdev;
+
+ init_rwsem(&pm->reset_lock);
+ atomic_set(&pm->reset_pending, 0);
+ atomic_set(&pm->reset_counter, 0);
+
+ INIT_WORK(&pm->recovery_work, ivpu_pm_recovery_work);
+ INIT_DELAYED_WORK(&pm->job_timeout_work, ivpu_job_timeout_work);
+
+ if (ivpu_disable_recovery)
+ delay = -1;
+ else
+ delay = vdev->timeout.autosuspend;
+
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, delay);
+ pm_runtime_set_active(dev);
+
+ ivpu_dbg(vdev, PM, "Autosuspend delay = %d\n", delay);
+}
+
+void ivpu_pm_disable_recovery(struct ivpu_device *vdev)
+{
+ drm_WARN_ON(&vdev->drm, delayed_work_pending(&vdev->pm->job_timeout_work));
+ disable_work_sync(&vdev->pm->recovery_work);
+}
+
+void ivpu_pm_enable(struct ivpu_device *vdev)
+{
+ struct device *dev = vdev->drm.dev;
+
+ pm_runtime_allow(dev);
+ pm_runtime_put_autosuspend(dev);
+}
+
+void ivpu_pm_disable(struct ivpu_device *vdev)
+{
+ pm_runtime_get_noresume(vdev->drm.dev);
+ pm_runtime_forbid(vdev->drm.dev);
+}
+
+int ivpu_pm_dct_init(struct ivpu_device *vdev)
+{
+ if (vdev->pm->dct_active_percent)
+ return ivpu_pm_dct_enable(vdev, vdev->pm->dct_active_percent);
+
+ return 0;
+}
+
+int ivpu_pm_dct_enable(struct ivpu_device *vdev, u8 active_percent)
+{
+ u32 active_us, inactive_us;
+ int ret;
+
+ if (active_percent == 0 || active_percent > 100)
+ return -EINVAL;
+
+ active_us = (DCT_PERIOD_US * active_percent) / 100;
+ inactive_us = DCT_PERIOD_US - active_us;
+
+ vdev->pm->dct_active_percent = active_percent;
+
+ ivpu_dbg(vdev, PM, "DCT requested %u%% (D0: %uus, D0i2: %uus)\n",
+ active_percent, active_us, inactive_us);
+
+ ret = ivpu_jsm_dct_enable(vdev, active_us, inactive_us);
+ if (ret) {
+ ivpu_err_ratelimited(vdev, "Failed to enable DCT: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ivpu_pm_dct_disable(struct ivpu_device *vdev)
+{
+ int ret;
+
+ vdev->pm->dct_active_percent = 0;
+
+ ivpu_dbg(vdev, PM, "DCT requested to be disabled\n");
+
+ ret = ivpu_jsm_dct_disable(vdev);
+ if (ret) {
+ ivpu_err_ratelimited(vdev, "Failed to disable DCT: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void ivpu_pm_irq_dct_work_fn(struct work_struct *work)
+{
+ struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_dct_work);
+ bool enable;
+ int ret;
+
+ if (ivpu_hw_btrs_dct_get_request(vdev, &enable))
+ return;
+
+ if (enable)
+ ret = ivpu_pm_dct_enable(vdev, DCT_DEFAULT_ACTIVE_PERCENT);
+ else
+ ret = ivpu_pm_dct_disable(vdev);
+
+ if (!ret) {
+ /* Convert percent to U1.7 format */
+ u8 val = DIV_ROUND_CLOSEST(vdev->pm->dct_active_percent * 128, 100);
+
+ ivpu_hw_btrs_dct_set_status(vdev, enable, val);
+ }
+
+}
diff --git a/drivers/accel/ivpu/ivpu_pm.h b/drivers/accel/ivpu/ivpu_pm.h
new file mode 100644
index 000000000000..a2aa7a27f32e
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_pm.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __IVPU_PM_H__
+#define __IVPU_PM_H__
+
+#include <linux/rwsem.h>
+#include <linux/types.h>
+
+struct ivpu_device;
+
+struct ivpu_pm_info {
+ struct ivpu_device *vdev;
+ struct delayed_work job_timeout_work;
+ struct work_struct recovery_work;
+ struct rw_semaphore reset_lock;
+ atomic_t reset_counter;
+ atomic_t reset_pending;
+ bool is_warmboot;
+ u8 dct_active_percent;
+};
+
+void ivpu_pm_init(struct ivpu_device *vdev);
+void ivpu_pm_enable(struct ivpu_device *vdev);
+void ivpu_pm_disable(struct ivpu_device *vdev);
+void ivpu_pm_disable_recovery(struct ivpu_device *vdev);
+
+int ivpu_pm_suspend_cb(struct device *dev);
+int ivpu_pm_resume_cb(struct device *dev);
+int ivpu_pm_runtime_suspend_cb(struct device *dev);
+int ivpu_pm_runtime_resume_cb(struct device *dev);
+
+void ivpu_pm_reset_prepare_cb(struct pci_dev *pdev);
+void ivpu_pm_reset_done_cb(struct pci_dev *pdev);
+
+int __must_check ivpu_rpm_get(struct ivpu_device *vdev);
+void ivpu_rpm_put(struct ivpu_device *vdev);
+
+void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason);
+void ivpu_start_job_timeout_detection(struct ivpu_device *vdev);
+void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev);
+
+int ivpu_pm_dct_init(struct ivpu_device *vdev);
+int ivpu_pm_dct_enable(struct ivpu_device *vdev, u8 active_percent);
+int ivpu_pm_dct_disable(struct ivpu_device *vdev);
+void ivpu_pm_irq_dct_work_fn(struct work_struct *work);
+
+#endif /* __IVPU_PM_H__ */
diff --git a/drivers/accel/ivpu/ivpu_sysfs.c b/drivers/accel/ivpu/ivpu_sysfs.c
new file mode 100644
index 000000000000..d250a10caca9
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_sysfs.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/units.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_gem.h"
+#include "ivpu_fw.h"
+#include "ivpu_hw.h"
+#include "ivpu_sysfs.h"
+
+/**
+ * DOC: npu_busy_time_us
+ *
+ * npu_busy_time_us is the time that the device spent executing jobs.
+ * The time is counted when and only when there are jobs submitted to firmware.
+ *
+ * This time can be used to measure the utilization of NPU, either by calculating
+ * npu_busy_time_us difference between two timepoints (i.e. measuring the time
+ * that the NPU was active during some workload) or monitoring utilization percentage
+ * by reading npu_busy_time_us periodically.
+ *
+ * When reading the value periodically, it shouldn't be read too often as it may have
+ * an impact on job submission performance. Recommended period is 1 second.
+ */
+static ssize_t
+npu_busy_time_us_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ ktime_t total, now = 0;
+
+ mutex_lock(&vdev->submitted_jobs_lock);
+
+ total = vdev->busy_time;
+ if (!xa_empty(&vdev->submitted_jobs_xa))
+ now = ktime_sub(ktime_get(), vdev->busy_start_ts);
+ mutex_unlock(&vdev->submitted_jobs_lock);
+
+ return sysfs_emit(buf, "%lld\n", ktime_to_us(ktime_add(total, now)));
+}
+
+static DEVICE_ATTR_RO(npu_busy_time_us);
+
+/**
+ * DOC: npu_memory_utilization
+ *
+ * The npu_memory_utilization is used to report in bytes a current NPU memory utilization.
+ *
+ */
+static ssize_t
+npu_memory_utilization_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ struct ivpu_bo *bo;
+ u64 total_npu_memory = 0;
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_for_each_entry(bo, &vdev->bo_list, bo_list_node)
+ if (ivpu_bo_is_resident(bo))
+ total_npu_memory += ivpu_bo_size(bo);
+ mutex_unlock(&vdev->bo_list_lock);
+
+ return sysfs_emit(buf, "%lld\n", total_npu_memory);
+}
+
+static DEVICE_ATTR_RO(npu_memory_utilization);
+
+/**
+ * DOC: sched_mode
+ *
+ * The sched_mode is used to report current NPU scheduling mode.
+ *
+ * It returns following strings:
+ * - "HW" - Hardware Scheduler mode
+ * - "OS" - Operating System Scheduler mode
+ *
+ */
+static ssize_t
+sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+
+ return sysfs_emit(buf, "%s\n", vdev->fw->sched_mode ? "HW" : "OS");
+}
+
+static DEVICE_ATTR_RO(sched_mode);
+
+/**
+ * DOC: npu_max_frequency
+ *
+ * The npu_max_frequency shows maximum frequency in MHz of the NPU's data
+ * processing unit
+ */
+static ssize_t
+npu_max_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ u32 freq = ivpu_hw_dpu_max_freq_get(vdev);
+
+ return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
+}
+
+static DEVICE_ATTR_RO(npu_max_frequency_mhz);
+
+/**
+ * DOC: npu_current_frequency_mhz
+ *
+ * The npu_current_frequency_mhz shows current frequency in MHz of the NPU's
+ * data processing unit
+ */
+static ssize_t
+npu_current_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct ivpu_device *vdev = to_ivpu_device(drm);
+ u32 freq = 0;
+
+ /* Read frequency only if device is active, otherwise frequency is 0 */
+ if (pm_runtime_get_if_active(vdev->drm.dev) > 0) {
+ freq = ivpu_hw_dpu_freq_get(vdev);
+
+ pm_runtime_put_autosuspend(vdev->drm.dev);
+ }
+
+ return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
+}
+
+static DEVICE_ATTR_RO(npu_current_frequency_mhz);
+
+static struct attribute *ivpu_dev_attrs[] = {
+ &dev_attr_npu_busy_time_us.attr,
+ &dev_attr_npu_memory_utilization.attr,
+ &dev_attr_sched_mode.attr,
+ &dev_attr_npu_max_frequency_mhz.attr,
+ &dev_attr_npu_current_frequency_mhz.attr,
+ NULL,
+};
+
+static struct attribute_group ivpu_dev_attr_group = {
+ .attrs = ivpu_dev_attrs,
+};
+
+void ivpu_sysfs_init(struct ivpu_device *vdev)
+{
+ int ret;
+
+ ret = devm_device_add_group(vdev->drm.dev, &ivpu_dev_attr_group);
+ if (ret)
+ ivpu_warn(vdev, "Failed to add group to device, ret %d", ret);
+}
diff --git a/drivers/accel/ivpu/ivpu_sysfs.h b/drivers/accel/ivpu/ivpu_sysfs.h
new file mode 100644
index 000000000000..9836f09b35a3
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_sysfs.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+#ifndef __IVPU_SYSFS_H__
+#define __IVPU_SYSFS_H__
+
+#include "ivpu_drv.h"
+
+void ivpu_sysfs_init(struct ivpu_device *vdev);
+
+#endif /* __IVPU_SYSFS_H__ */
diff --git a/drivers/accel/ivpu/ivpu_trace.h b/drivers/accel/ivpu/ivpu_trace.h
new file mode 100644
index 000000000000..eb792038e701
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_trace.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#if !defined(__IVPU_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ)
+#define __IVPU_TRACE_H__
+
+#include <linux/tracepoint.h>
+#include "ivpu_drv.h"
+#include "ivpu_job.h"
+#include "vpu_jsm_api.h"
+#include "ivpu_jsm_msg.h"
+#include "ivpu_ipc.h"
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM vpu
+#define TRACE_INCLUDE_FILE ivpu_trace
+
+TRACE_EVENT(pm,
+ TP_PROTO(const char *event),
+ TP_ARGS(event),
+ TP_STRUCT__entry(__field(const char *, event)),
+ TP_fast_assign(__entry->event = event;),
+ TP_printk("%s", __entry->event)
+);
+
+TRACE_EVENT(job,
+ TP_PROTO(const char *event, struct ivpu_job *job),
+ TP_ARGS(event, job),
+ TP_STRUCT__entry(__field(const char *, event)
+ __field(u32, ctx_id)
+ __field(u32, engine_id)
+ __field(u32, job_id)
+ ),
+ TP_fast_assign(__entry->event = event;
+ __entry->ctx_id = job->file_priv->ctx.id;
+ __entry->engine_id = job->engine_idx;
+ __entry->job_id = job->job_id;),
+ TP_printk("%s context:%d engine:%d job:%d",
+ __entry->event,
+ __entry->ctx_id,
+ __entry->engine_id,
+ __entry->job_id)
+);
+
+TRACE_EVENT(jsm,
+ TP_PROTO(const char *event, struct vpu_jsm_msg *msg),
+ TP_ARGS(event, msg),
+ TP_STRUCT__entry(__field(const char *, event)
+ __field(const char *, type)
+ __field(enum vpu_ipc_msg_status, status)
+ __field(u32, request_id)
+ __field(u32, result)
+ ),
+ TP_fast_assign(__entry->event = event;
+ __entry->type = ivpu_jsm_msg_type_to_str(msg->type);
+ __entry->status = msg->status;
+ __entry->request_id = msg->request_id;
+ __entry->result = msg->result;),
+ TP_printk("%s type:%s, status:%#x, id:%#x, result:%#x",
+ __entry->event,
+ __entry->type,
+ __entry->status,
+ __entry->request_id,
+ __entry->result)
+);
+
+#endif /* __IVPU_TRACE_H__ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/drivers/accel/ivpu/ivpu_trace_points.c b/drivers/accel/ivpu/ivpu_trace_points.c
new file mode 100644
index 000000000000..f8fb99de0de3
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_trace_points.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2024 Intel Corporation
+ */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "ivpu_trace.h"
+#endif
diff --git a/drivers/accel/ivpu/vpu_boot_api.h b/drivers/accel/ivpu/vpu_boot_api.h
new file mode 100644
index 000000000000..218468bbbcad
--- /dev/null
+++ b/drivers/accel/ivpu/vpu_boot_api.h
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2020-2024, Intel Corporation.
+ */
+
+#ifndef VPU_BOOT_API_H
+#define VPU_BOOT_API_H
+
+/*
+ * The below values will be used to construct the version info this way:
+ * fw_bin_header->api_version[VPU_BOOT_API_VER_ID] = (VPU_BOOT_API_VER_MAJOR << 16) |
+ * VPU_BOOT_API_VER_MINOR;
+ * VPU_BOOT_API_VER_PATCH will be ignored. KMD and compatibility is not affected if this changes
+ * This information is collected by using vpuip_2/application/vpuFirmware/make_std_fw_image.py
+ * If a header is missing this info we ignore the header, if a header is missing or contains
+ * partial info a build error will be generated.
+ */
+
+/*
+ * Major version changes that break backward compatibility.
+ * Major version must start from 1 and can only be incremented.
+ */
+#define VPU_BOOT_API_VER_MAJOR 3
+
+/*
+ * Minor version changes when API backward compatibility is preserved.
+ * Resets to 0 if Major version is incremented.
+ */
+#define VPU_BOOT_API_VER_MINOR 28
+
+/*
+ * API header changed (field names, documentation, formatting) but API itself has not been changed
+ */
+#define VPU_BOOT_API_VER_PATCH 3
+
+/*
+ * Index in the API version table
+ * Must be unique for each API
+ */
+#define VPU_BOOT_API_VER_INDEX 0
+
+#pragma pack(push, 4)
+
+/*
+ * Firmware image header format
+ */
+#define VPU_FW_HEADER_SIZE 4096
+#define VPU_FW_HEADER_VERSION 0x1
+#define VPU_FW_VERSION_SIZE 32
+#define VPU_FW_API_VER_NUM 16
+
+struct vpu_firmware_header {
+ u32 header_version;
+ u32 image_format;
+ u64 image_load_address;
+ u32 image_size;
+ u64 entry_point;
+ u8 vpu_version[VPU_FW_VERSION_SIZE];
+ u32 compression_type;
+ u64 firmware_version_load_address;
+ u32 firmware_version_size;
+ u64 boot_params_load_address;
+ u32 api_version[VPU_FW_API_VER_NUM];
+ /* Size of memory require for firmware execution */
+ u32 runtime_size;
+ u32 shave_nn_fw_size;
+ /*
+ * Size of primary preemption buffer, assuming a 2-job submission queue.
+ * NOTE: host driver is expected to adapt size accordingly to actual
+ * submission queue size and device capabilities.
+ */
+ u32 preemption_buffer_1_size;
+ /*
+ * Size of secondary preemption buffer, assuming a 2-job submission queue.
+ * NOTE: host driver is expected to adapt size accordingly to actual
+ * submission queue size and device capabilities.
+ */
+ u32 preemption_buffer_2_size;
+ /*
+ * Maximum preemption buffer size that the FW can use: no need for the host
+ * driver to allocate more space than that specified by these fields.
+ * A value of 0 means no declared limit.
+ */
+ u32 preemption_buffer_1_max_size;
+ u32 preemption_buffer_2_max_size;
+ /* Space reserved for future preemption-related fields. */
+ u32 preemption_reserved[4];
+ /* FW image read only section start address, 4KB aligned */
+ u64 ro_section_start_address;
+ /* FW image read only section size, 4KB aligned */
+ u32 ro_section_size;
+ u32 reserved;
+};
+
+/*
+ * Firmware boot parameters format
+ */
+
+#define VPU_BOOT_PLL_COUNT 3
+#define VPU_BOOT_PLL_OUT_COUNT 4
+
+/** Values for boot_type field */
+#define VPU_BOOT_TYPE_COLDBOOT 0
+#define VPU_BOOT_TYPE_WARMBOOT 1
+
+/** Value for magic filed */
+#define VPU_BOOT_PARAMS_MAGIC 0x10000
+
+/** VPU scheduling mode. By default, OS scheduling is used. */
+#define VPU_SCHEDULING_MODE_OS 0
+#define VPU_SCHEDULING_MODE_HW 1
+
+enum VPU_BOOT_L2_CACHE_CFG_TYPE {
+ VPU_BOOT_L2_CACHE_CFG_UPA = 0,
+ VPU_BOOT_L2_CACHE_CFG_NN = 1,
+ VPU_BOOT_L2_CACHE_CFG_NUM = 2
+};
+
+/** VPU MCA ECC signalling mode. By default, no signalling is used */
+enum VPU_BOOT_MCA_ECC_SIGNAL_TYPE {
+ VPU_BOOT_MCA_ECC_NONE = 0,
+ VPU_BOOT_MCA_ECC_CORR = 1,
+ VPU_BOOT_MCA_ECC_FATAL = 2,
+ VPU_BOOT_MCA_ECC_BOTH = 3
+};
+
+/**
+ * Logging destinations.
+ *
+ * Logging output can be directed to different logging destinations. This enum
+ * defines the list of logging destinations supported by the VPU firmware (NOTE:
+ * a specific VPU FW binary may support only a subset of such output
+ * destinations, depending on the target platform and compile options).
+ */
+enum vpu_trace_destination {
+ VPU_TRACE_DESTINATION_PIPEPRINT = 0x1,
+ VPU_TRACE_DESTINATION_VERBOSE_TRACING = 0x2,
+ VPU_TRACE_DESTINATION_NORTH_PEAK = 0x4,
+};
+
+/*
+ * Processor bit shifts (for loggable HW components).
+ */
+#define VPU_TRACE_PROC_BIT_RESERVED 0
+#define VPU_TRACE_PROC_BIT_LRT 1
+#define VPU_TRACE_PROC_BIT_LNN 2
+#define VPU_TRACE_PROC_BIT_SHV_0 3
+#define VPU_TRACE_PROC_BIT_SHV_1 4
+#define VPU_TRACE_PROC_BIT_SHV_2 5
+#define VPU_TRACE_PROC_BIT_SHV_3 6
+#define VPU_TRACE_PROC_BIT_SHV_4 7
+#define VPU_TRACE_PROC_BIT_SHV_5 8
+#define VPU_TRACE_PROC_BIT_SHV_6 9
+#define VPU_TRACE_PROC_BIT_SHV_7 10
+#define VPU_TRACE_PROC_BIT_SHV_8 11
+#define VPU_TRACE_PROC_BIT_SHV_9 12
+#define VPU_TRACE_PROC_BIT_SHV_10 13
+#define VPU_TRACE_PROC_BIT_SHV_11 14
+#define VPU_TRACE_PROC_BIT_SHV_12 15
+#define VPU_TRACE_PROC_BIT_SHV_13 16
+#define VPU_TRACE_PROC_BIT_SHV_14 17
+#define VPU_TRACE_PROC_BIT_SHV_15 18
+#define VPU_TRACE_PROC_BIT_ACT_SHV_0 19
+#define VPU_TRACE_PROC_BIT_ACT_SHV_1 20
+#define VPU_TRACE_PROC_BIT_ACT_SHV_2 21
+#define VPU_TRACE_PROC_BIT_ACT_SHV_3 22
+#define VPU_TRACE_PROC_NO_OF_HW_DEVS 23
+
+/* VPU 30xx HW component IDs are sequential, so define first and last IDs. */
+#define VPU_TRACE_PROC_BIT_30XX_FIRST VPU_TRACE_PROC_BIT_LRT
+#define VPU_TRACE_PROC_BIT_30XX_LAST VPU_TRACE_PROC_BIT_SHV_15
+
+struct vpu_boot_l2_cache_config {
+ u8 use;
+ u8 cfg;
+};
+
+struct vpu_warm_boot_section {
+ u32 src;
+ u32 dst;
+ u32 size;
+ u32 core_id;
+ u32 is_clear_op;
+};
+
+/*
+ * When HW scheduling mode is enabled, a present period is defined.
+ * It will be used by VPU to swap between normal and focus priorities
+ * to prevent starving of normal priority band (when implemented).
+ * Host must provide a valid value at boot time in
+ * `vpu_focus_present_timer_ms`. If the value provided by the host is not within the
+ * defined range a default value will be used. Here we define the min. and max.
+ * allowed values and the and default value of the present period. Units are milliseconds.
+ */
+#define VPU_PRESENT_CALL_PERIOD_MS_DEFAULT 50
+#define VPU_PRESENT_CALL_PERIOD_MS_MIN 16
+#define VPU_PRESENT_CALL_PERIOD_MS_MAX 10000
+
+/**
+ * Macros to enable various power profiles within the NPU.
+ * To be defined as part of 32 bit mask.
+ */
+#define POWER_PROFILE_SURVIVABILITY 0x1
+
+/**
+ * Enum for dvfs_mode boot param.
+ */
+enum vpu_governor {
+ VPU_GOV_DEFAULT = 0, /* Default Governor for the system */
+ VPU_GOV_MAX_PERFORMANCE = 1, /* Maximum performance governor */
+ VPU_GOV_ON_DEMAND = 2, /* On Demand frequency control governor */
+ VPU_GOV_POWER_SAVE = 3, /* Power save governor */
+ VPU_GOV_ON_DEMAND_PRIORITY_AWARE = 4 /* On Demand priority based governor */
+};
+
+struct vpu_boot_params {
+ u32 magic;
+ u32 vpu_id;
+ u32 vpu_count;
+ u32 pad0[5];
+ /* Clock frequencies: 0x20 - 0xFF */
+ u32 frequency;
+ u32 pll[VPU_BOOT_PLL_COUNT][VPU_BOOT_PLL_OUT_COUNT];
+ u32 perf_clk_frequency;
+ u32 pad1[42];
+ /* Memory regions: 0x100 - 0x1FF */
+ u64 ipc_header_area_start;
+ u32 ipc_header_area_size;
+ u64 shared_region_base;
+ u32 shared_region_size;
+ u64 ipc_payload_area_start;
+ u32 ipc_payload_area_size;
+ u64 global_aliased_pio_base;
+ u32 global_aliased_pio_size;
+ u32 autoconfig;
+ struct vpu_boot_l2_cache_config cache_defaults[VPU_BOOT_L2_CACHE_CFG_NUM];
+ u64 global_memory_allocator_base;
+ u32 global_memory_allocator_size;
+ /**
+ * ShaveNN FW section VPU base address
+ * On VPU2.7 HW this address must be within 2GB range starting from L2C_PAGE_TABLE base
+ */
+ u64 shave_nn_fw_base;
+ u64 save_restore_ret_address; /* stores the address of FW's restore entry point */
+ u32 pad2[43];
+ /* IRQ re-direct numbers: 0x200 - 0x2FF */
+ s32 watchdog_irq_mss;
+ s32 watchdog_irq_nce;
+ /* ARM -> VPU doorbell interrupt. ARM is notifying VPU of async command or compute job. */
+ u32 host_to_vpu_irq;
+ /* VPU -> ARM job done interrupt. VPU is notifying ARM of compute job completion. */
+ u32 job_done_irq;
+ /* VPU -> ARM IRQ line to use to request MMU update. */
+ u32 mmu_update_request_irq;
+ /* ARM -> VPU IRQ line to use to notify of MMU update completion. */
+ u32 mmu_update_done_irq;
+ /* ARM -> VPU IRQ line to use to request power level change. */
+ u32 set_power_level_irq;
+ /* VPU -> ARM IRQ line to use to notify of power level change completion. */
+ u32 set_power_level_done_irq;
+ /* VPU -> ARM IRQ line to use to notify of VPU idle state change */
+ u32 set_vpu_idle_update_irq;
+ /* VPU -> ARM IRQ line to use to request counter reset. */
+ u32 metric_query_event_irq;
+ /* ARM -> VPU IRQ line to use to notify of counter reset completion. */
+ u32 metric_query_event_done_irq;
+ /* VPU -> ARM IRQ line to use to notify of preemption completion. */
+ u32 preemption_done_irq;
+ /* Padding. */
+ u32 pad3[52];
+ /* Silicon information: 0x300 - 0x3FF */
+ u32 host_version_id;
+ u32 si_stepping;
+ u64 device_id;
+ u64 feature_exclusion;
+ u64 sku;
+ /** PLL ratio for minimum clock frequency */
+ u32 min_freq_pll_ratio;
+ /** PLL ratio for maximum clock frequency */
+ u32 max_freq_pll_ratio;
+ /**
+ * Initial log level threshold (messages with log level severity less than
+ * the threshold will not be logged); applies to every enabled logging
+ * destination and loggable HW component. See 'mvLog_t' enum for acceptable
+ * values.
+ * TODO: EISW-33556: Move log level definition (mvLog_t) to this file.
+ */
+ u32 default_trace_level;
+ u32 boot_type;
+ u64 punit_telemetry_sram_base;
+ u64 punit_telemetry_sram_size;
+ u32 vpu_telemetry_enable;
+ u64 crit_tracing_buff_addr;
+ u32 crit_tracing_buff_size;
+ u64 verbose_tracing_buff_addr;
+ u32 verbose_tracing_buff_size;
+ u64 verbose_tracing_sw_component_mask; /* TO BE REMOVED */
+ /**
+ * Mask of destinations to which logging messages are delivered; bitwise OR
+ * of values defined in vpu_trace_destination enum.
+ */
+ u32 trace_destination_mask;
+ /**
+ * Mask of hardware components for which logging is enabled; bitwise OR of
+ * bits defined by the VPU_TRACE_PROC_BIT_* macros.
+ */
+ u64 trace_hw_component_mask;
+ /** Mask of trace message formats supported by the driver */
+ u64 tracing_buff_message_format_mask;
+ u64 trace_reserved_1[2];
+ /**
+ * Period at which the VPU reads the temp sensor values into MMIO, on
+ * platforms where that is necessary (in ms). 0 to disable reads.
+ */
+ u32 temp_sensor_period_ms;
+ /** PLL ratio for efficient clock frequency */
+ u32 pn_freq_pll_ratio;
+ /**
+ * DVFS Mode:
+ * 0 - Default, DVFS mode selected by the firmware
+ * 1 - Max Performance
+ * 2 - On Demand
+ * 3 - Power Save
+ * 4 - On Demand Priority Aware
+ */
+ u32 dvfs_mode;
+ /**
+ * Depending on DVFS Mode:
+ * On-demand: Default if 0.
+ * Bit 0-7 - uint8_t: Highest residency percent
+ * Bit 8-15 - uint8_t: High residency percent
+ * Bit 16-23 - uint8_t: Low residency percent
+ * Bit 24-31 - uint8_t: Lowest residency percent
+ * Bit 32-35 - unsigned 4b: PLL Ratio increase amount on highest residency
+ * Bit 36-39 - unsigned 4b: PLL Ratio increase amount on high residency
+ * Bit 40-43 - unsigned 4b: PLL Ratio decrease amount on low residency
+ * Bit 44-47 - unsigned 4b: PLL Ratio decrease amount on lowest frequency
+ * Bit 48-55 - uint8_t: Period (ms) for residency decisions
+ * Bit 56-63 - uint8_t: Averaging windows (as multiples of period. Max: 30 decimal)
+ * Power Save/Max Performance: Unused
+ */
+ u64 dvfs_param;
+ /**
+ * D0i3 delayed entry
+ * Bit0: Disable CPU state save on D0i2 entry flow.
+ * 0: Every D0i2 entry saves state. Save state IPC message ignored.
+ * 1: IPC message required to save state on D0i3 entry flow.
+ */
+ u32 d0i3_delayed_entry;
+ /* Time spent by VPU in D0i3 state */
+ u64 d0i3_residency_time_us;
+ /* Value of VPU perf counter at the time of entering D0i3 state . */
+ u64 d0i3_entry_vpu_ts;
+ /*
+ * The system time of the host operating system in microseconds.
+ * E.g the number of microseconds since 1st of January 1970, or whatever
+ * date the host operating system uses to maintain system time.
+ * This value will be used to track system time on the VPU.
+ * The KMD is required to update this value on every VPU reset.
+ */
+ u64 system_time_us;
+ u32 pad4[2];
+ /*
+ * The delta between device monotonic time and the current value of the
+ * HW timestamp register, in ticks. Written by the firmware during boot.
+ * Can be used by the KMD to calculate device time.
+ */
+ u64 device_time_delta_ticks;
+ u32 pad7[14];
+ /* Warm boot information: 0x400 - 0x43F */
+ u32 warm_boot_sections_count;
+ u32 warm_boot_start_address_reference;
+ u32 warm_boot_section_info_address_offset;
+ u32 pad5[13];
+ /* Power States transitions timestamps: 0x440 - 0x46F*/
+ struct {
+ /* VPU_IDLE -> VPU_ACTIVE transition initiated timestamp */
+ u64 vpu_active_state_requested;
+ /* VPU_IDLE -> VPU_ACTIVE transition completed timestamp */
+ u64 vpu_active_state_achieved;
+ /* VPU_ACTIVE -> VPU_IDLE transition initiated timestamp */
+ u64 vpu_idle_state_requested;
+ /* VPU_ACTIVE -> VPU_IDLE transition completed timestamp */
+ u64 vpu_idle_state_achieved;
+ /* VPU_IDLE -> VPU_STANDBY transition initiated timestamp */
+ u64 vpu_standby_state_requested;
+ /* VPU_IDLE -> VPU_STANDBY transition completed timestamp */
+ u64 vpu_standby_state_achieved;
+ } power_states_timestamps;
+ /* VPU scheduling mode. Values defined by VPU_SCHEDULING_MODE_* macros. */
+ u32 vpu_scheduling_mode;
+ /* Present call period in milliseconds. */
+ u32 vpu_focus_present_timer_ms;
+ /* VPU ECC Signaling */
+ u32 vpu_uses_ecc_mca_signal;
+ /* Values defined by POWER_PROFILE* macros */
+ u32 power_profile;
+ /* Microsecond value for DCT active cycle */
+ u32 dct_active_us;
+ /* Microsecond value for DCT inactive cycle */
+ u32 dct_inactive_us;
+ /* Unused/reserved: 0x488 - 0xFFF */
+ u32 pad6[734];
+};
+
+/* Magic numbers set between host and vpu to detect corruption of tracing init */
+#define VPU_TRACING_BUFFER_CANARY (0xCAFECAFE)
+
+/* Tracing buffer message format definitions */
+#define VPU_TRACING_FORMAT_STRING 0
+#define VPU_TRACING_FORMAT_MIPI 2
+/*
+ * Header of the tracing buffer.
+ * The below defined header will be stored at the beginning of
+ * each allocated tracing buffer, followed by a series of 256b
+ * of ASCII trace message entries.
+ */
+struct vpu_tracing_buffer_header {
+ /**
+ * Magic number set by host to detect corruption
+ * @see VPU_TRACING_BUFFER_CANARY
+ */
+ u32 host_canary_start;
+ /* offset from start of buffer for trace entries */
+ u32 read_index;
+ /* keeps track of wrapping on the reader side */
+ u32 read_wrap_count;
+ u32 pad_to_cache_line_size_0[13];
+ /* End of first cache line */
+
+ /**
+ * Magic number set by host to detect corruption
+ * @see VPU_TRACING_BUFFER_CANARY
+ */
+ u32 vpu_canary_start;
+ /* offset from start of buffer from write start */
+ u32 write_index;
+ /* counter for buffer wrapping */
+ u32 wrap_count;
+ /* legacy field - do not use */
+ u32 reserved_0;
+ /**
+ * Size of the log buffer include this header (@header_size) and space
+ * reserved for all messages. If @alignment` is greater that 0 the @Size
+ * must be multiple of @Alignment.
+ */
+ u32 size;
+ /* Header version */
+ u16 header_version;
+ /* Header size */
+ u16 header_size;
+ /*
+ * Format of the messages in the trace buffer
+ * 0 - null terminated string
+ * 1 - size + null terminated string
+ * 2 - MIPI-SysT encoding
+ */
+ u32 format;
+ /*
+ * Message alignment
+ * 0 - messages are place 1 after another
+ * n - every message starts and multiple on offset
+ */
+ u32 alignment; /* 64, 128, 256 */
+ /* Name of the logging entity, i.e "LRT", "LNN", "SHV0", etc */
+ char name[16];
+ u32 pad_to_cache_line_size_1[4];
+ /* End of second cache line */
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/accel/ivpu/vpu_jsm_api.h b/drivers/accel/ivpu/vpu_jsm_api.h
new file mode 100644
index 000000000000..bca6a44dc041
--- /dev/null
+++ b/drivers/accel/ivpu/vpu_jsm_api.h
@@ -0,0 +1,1726 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2020-2025, Intel Corporation.
+ */
+
+/**
+ * @addtogroup Jsm
+ * @{
+ */
+
+/**
+ * @file
+ * @brief JSM shared definitions
+ */
+#ifndef VPU_JSM_API_H
+#define VPU_JSM_API_H
+
+/*
+ * Major version changes that break backward compatibility
+ */
+#define VPU_JSM_API_VER_MAJOR 3
+
+/*
+ * Minor version changes when API backward compatibility is preserved.
+ */
+#define VPU_JSM_API_VER_MINOR 33
+
+/*
+ * API header changed (field names, documentation, formatting) but API itself has not been changed
+ */
+#define VPU_JSM_API_VER_PATCH 0
+
+/*
+ * Index in the API version table
+ */
+#define VPU_JSM_API_VER_INDEX 4
+
+/*
+ * Number of Priority Bands for Hardware Scheduling
+ * Bands: Idle(0), Normal(1), Focus(2), RealTime(3)
+ */
+#define VPU_HWS_NUM_PRIORITY_BANDS 4
+
+/* Max number of impacted contexts that can be dealt with the engine reset command */
+#define VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS 3
+
+/*
+ * Pack the API structures to enforce binary compatibility
+ * Align to 8 bytes for optimal performance
+ */
+#pragma pack(push, 8)
+
+/*
+ * Engine indexes.
+ */
+#define VPU_ENGINE_COMPUTE 0
+#define VPU_ENGINE_NB 1
+
+/*
+ * VPU status values.
+ */
+#define VPU_JSM_STATUS_SUCCESS 0x0U
+#define VPU_JSM_STATUS_PARSING_ERR 0x1U
+#define VPU_JSM_STATUS_PROCESSING_ERR 0x2U
+#define VPU_JSM_STATUS_PREEMPTED 0x3U
+#define VPU_JSM_STATUS_ABORTED 0x4U
+#define VPU_JSM_STATUS_USER_CTX_VIOL_ERR 0x5U
+#define VPU_JSM_STATUS_GLOBAL_CTX_VIOL_ERR 0x6U
+#define VPU_JSM_STATUS_MVNCI_WRONG_INPUT_FORMAT 0x7U
+#define VPU_JSM_STATUS_MVNCI_UNSUPPORTED_NETWORK_ELEMENT 0x8U
+#define VPU_JSM_STATUS_MVNCI_INVALID_HANDLE 0x9U
+#define VPU_JSM_STATUS_MVNCI_OUT_OF_RESOURCES 0xAU
+#define VPU_JSM_STATUS_MVNCI_NOT_IMPLEMENTED 0xBU
+#define VPU_JSM_STATUS_MVNCI_INTERNAL_ERROR 0xCU
+/* @deprecated (use VPU_JSM_STATUS_PREEMPTED_MID_COMMAND instead) */
+#define VPU_JSM_STATUS_PREEMPTED_MID_INFERENCE 0xDU
+/* Job status returned when the job was preempted mid-command */
+#define VPU_JSM_STATUS_PREEMPTED_MID_COMMAND 0xDU
+/* Range of status codes that require engine reset */
+#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN 0xEU
+#define VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW 0xEU
+#define VPU_JSM_STATUS_MVNCI_PREEMPTION_TIMED_OUT 0xFU
+#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX 0x1FU
+
+/*
+ * Host <-> VPU IPC channels.
+ * ASYNC commands use a high priority channel, other messages use low-priority ones.
+ */
+#define VPU_IPC_CHAN_ASYNC_CMD 0
+#define VPU_IPC_CHAN_GEN_CMD 10
+#define VPU_IPC_CHAN_JOB_RET 11
+
+/*
+ * Job flags bit masks.
+ */
+enum {
+ /*
+ * Null submission mask.
+ * When set, batch buffer's commands are not processed but returned as
+ * successful immediately, except fences and timestamps.
+ * When cleared, batch buffer's commands are processed normally.
+ * Used for testing and profiling purposes.
+ */
+ VPU_JOB_FLAGS_NULL_SUBMISSION_MASK = (1 << 0U),
+ /*
+ * Inline command mask.
+ * When set, the object in job queue is an inline command (see struct vpu_inline_cmd below).
+ * When cleared, the object in job queue is a job (see struct vpu_job_queue_entry below).
+ */
+ VPU_JOB_FLAGS_INLINE_CMD_MASK = (1 << 1U),
+ /*
+ * VPU private data mask.
+ * Reserved for the VPU to store private data about the job (or inline command)
+ * while being processed.
+ */
+ VPU_JOB_FLAGS_PRIVATE_DATA_MASK = 0xFFFF0000U
+};
+
+/*
+ * Job queue flags bit masks.
+ */
+enum {
+ /*
+ * No job done notification mask.
+ * When set, indicates that no job done notification should be sent for any
+ * job from this queue. When cleared, indicates that job done notification
+ * should be sent for every job completed from this queue.
+ */
+ VPU_JOB_QUEUE_FLAGS_NO_JOB_DONE_MASK = (1 << 0U),
+ /*
+ * Native fence usage mask.
+ * When set, indicates that job queue uses native fences (as inline commands
+ * in job queue). Such queues may also use legacy fences (as commands in batch buffers).
+ * When cleared, indicates the job queue only uses legacy fences.
+ * NOTES:
+ * 1. For queues using native fences, VPU expects that all jobs in the queue
+ * are immediately followed by an inline command object. This object is expected
+ * to be a fence signal command in most cases, but can also be a NOP in case the host
+ * does not need per-job fence signalling. Other inline commands objects can be
+ * inserted between "job and inline command" pairs.
+ * 2. Native fence queues are only supported on VPU 40xx onwards.
+ */
+ VPU_JOB_QUEUE_FLAGS_USE_NATIVE_FENCE_MASK = (1 << 1U),
+ /*
+ * Enable turbo mode for testing NPU performance; not recommended for regular usage.
+ */
+ VPU_JOB_QUEUE_FLAGS_TURBO_MODE = (1 << 2U),
+ /*
+ * Queue error detection mode flag
+ * For 'interactive' queues (this bit not set), the FW will identify queues that have not
+ * completed a job inside the TDR timeout as in error as part of engine reset sequence.
+ * For 'non-interactive' queues (this bit set), the FW will identify queues that have not
+ * progressed the heartbeat inside the non-interactive no-progress timeout as in error as
+ * part of engine reset sequence. Additionally, there is an upper limit applied to these
+ * queues: even if they progress the heartbeat, if they run longer than non-interactive
+ * timeout, then the FW will also identify them as in error.
+ */
+ VPU_JOB_QUEUE_FLAGS_NON_INTERACTIVE = (1 << 3U)
+};
+
+/*
+ * Max length (including trailing NULL char) of trace entity name (e.g., the
+ * name of a logging destination or a loggable HW component).
+ */
+#define VPU_TRACE_ENTITY_NAME_MAX_LEN 32
+
+/*
+ * Max length (including trailing NULL char) of a dyndbg command.
+ *
+ * NOTE: 96 is used so that the size of 'struct vpu_ipc_msg' in the JSM API is
+ * 128 bytes (multiple of 64 bytes, the cache line size).
+ */
+#define VPU_DYNDBG_CMD_MAX_LEN 96
+
+/*
+ * For HWS command queue scheduling, we can prioritise command queues inside the
+ * same process with a relative in-process priority. Valid values for relative
+ * priority are given below - max and min.
+ */
+#define VPU_HWS_COMMAND_QUEUE_MAX_IN_PROCESS_PRIORITY 7
+#define VPU_HWS_COMMAND_QUEUE_MIN_IN_PROCESS_PRIORITY -7
+
+/*
+ * For HWS priority scheduling, we can have multiple realtime priority bands.
+ * They are numbered 0 to a MAX.
+ */
+#define VPU_HWS_MAX_REALTIME_PRIORITY_LEVEL 31U
+
+/*
+ * vpu_jsm_engine_reset_context flag definitions
+ */
+#define VPU_ENGINE_RESET_CONTEXT_FLAG_COLLATERAL_DAMAGE_MASK BIT(0)
+#define VPU_ENGINE_RESET_CONTEXT_HANG_PRIMARY_CAUSE 0
+#define VPU_ENGINE_RESET_CONTEXT_COLLATERAL_DAMAGE 1
+
+/*
+ * Invalid command queue handle identifier. Applies to cmdq_id and cmdq_group
+ * in this API.
+ */
+#define VPU_HWS_INVALID_CMDQ_HANDLE 0ULL
+
+/*
+ * Inline commands types.
+ */
+/*
+ * NOP.
+ * VPU does nothing other than consuming the inline command object.
+ */
+#define VPU_INLINE_CMD_TYPE_NOP 0x0
+/*
+ * Fence wait.
+ * VPU waits for the fence current value to reach monitored value.
+ * Fence wait operations are executed upon job dispatching. While waiting for
+ * the fence to be satisfied, VPU blocks fetching of the next objects in the queue.
+ * Jobs present in the queue prior to the fence wait object may be processed
+ * concurrently.
+ */
+#define VPU_INLINE_CMD_TYPE_FENCE_WAIT 0x1
+/*
+ * Fence signal.
+ * VPU sets the fence current value to the provided value. If new current value
+ * is equal to or higher than monitored value, VPU sends fence signalled notification
+ * to the host. Fence signal operations are executed upon completion of all the jobs
+ * present in the queue prior to them, and in-order relative to each other in the queue.
+ * But jobs in-between them may be processed concurrently and may complete out-of-order.
+ */
+#define VPU_INLINE_CMD_TYPE_FENCE_SIGNAL 0x2
+
+/**
+ * Job scheduling priority bands for both hardware scheduling and OS scheduling.
+ */
+enum vpu_job_scheduling_priority_band {
+ VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE = 0,
+ VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL = 1,
+ VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS = 2,
+ VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME = 3,
+ VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT = 4,
+};
+
+/**
+ * Job format.
+ * Jobs defines the actual workloads to be executed by a given engine.
+ */
+struct vpu_job_queue_entry {
+ /** Address of VPU commands batch buffer */
+ u64 batch_buf_addr;
+ /** Job ID */
+ u32 job_id;
+ /** Flags bit field, see VPU_JOB_FLAGS_* above */
+ u32 flags;
+ /**
+ * Doorbell ring timestamp taken by KMD from SoC's global system clock, in
+ * microseconds. NPU can convert this value to its own fixed clock's timebase,
+ * to match other profiling timestamps.
+ */
+ u64 doorbell_timestamp;
+ /** Extra id for job tracking, used only in the firmware perf traces */
+ u64 host_tracking_id;
+ /** Address of the primary preemption buffer to use for this job */
+ u64 primary_preempt_buf_addr;
+ /** Size of the primary preemption buffer to use for this job */
+ u32 primary_preempt_buf_size;
+ /** Size of secondary preemption buffer to use for this job */
+ u32 secondary_preempt_buf_size;
+ /** Address of secondary preemption buffer to use for this job */
+ u64 secondary_preempt_buf_addr;
+ u64 reserved_0;
+};
+
+/**
+ * Inline command format.
+ * Inline commands are the commands executed at scheduler level (typically,
+ * synchronization directives). Inline command and job objects must be of
+ * the same size and have flags field at same offset.
+ */
+struct vpu_inline_cmd {
+ u64 reserved_0;
+ /** Inline command type, see VPU_INLINE_CMD_TYPE_* defines. */
+ u32 type;
+ /** Flags bit field, see VPU_JOB_FLAGS_* above. */
+ u32 flags;
+ /** Inline command payload. Depends on inline command type. */
+ union payload {
+ /** Fence (wait and signal) commands' payload. */
+ struct fence {
+ /** Fence object handle. */
+ u64 fence_handle;
+ /** User VA of the current fence value. */
+ u64 current_value_va;
+ /** User VA of the monitored fence value (read-only). */
+ u64 monitored_value_va;
+ /** Value to wait for or write in fence location. */
+ u64 value;
+ /** User VA of the log buffer in which to add log entry on completion. */
+ u64 log_buffer_va;
+ /** NPU private data. */
+ u64 npu_private_data;
+ } fence;
+ /**
+ * Other commands do not have a payload:
+ * Payload definition for future inline commands can be inserted here.
+ */
+ u64 reserved_1[6];
+ } payload;
+};
+
+/**
+ * Job queue slots can be populated either with job objects or inline command objects.
+ */
+union vpu_jobq_slot {
+ struct vpu_job_queue_entry job;
+ struct vpu_inline_cmd inline_cmd;
+};
+
+/**
+ * Job queue control registers.
+ */
+struct vpu_job_queue_header {
+ u32 engine_idx;
+ u32 head;
+ u32 tail;
+ u32 flags;
+ /** Set to 1 to indicate priority_band field is valid */
+ u32 priority_band_valid;
+ /**
+ * Priority for the work of this job queue, valid only if the HWS is NOT used
+ * and the @ref priority_band_valid is set to 1. It is applied only during
+ * the @ref VPU_JSM_MSG_REGISTER_DB message processing.
+ * The device firmware might use the priority_band to optimize the power
+ * management logic, but it will not affect the order of jobs.
+ * Available priority bands: @see enum vpu_job_scheduling_priority_band
+ */
+ u32 priority_band;
+ /** Inside realtime band assigns a further priority, limited to 0..31 range */
+ u32 realtime_priority_level;
+ u32 reserved_0[9];
+};
+
+/*
+ * Job queue format.
+ */
+struct vpu_job_queue {
+ struct vpu_job_queue_header header;
+ union vpu_jobq_slot slot[];
+};
+
+/**
+ * Logging entity types.
+ *
+ * This enum defines the different types of entities involved in logging.
+ */
+enum vpu_trace_entity_type {
+ /** Logging destination (entity where logs can be stored / printed). */
+ VPU_TRACE_ENTITY_TYPE_DESTINATION = 1,
+ /** Loggable HW component (HW entity that can be logged). */
+ VPU_TRACE_ENTITY_TYPE_HW_COMPONENT = 2,
+};
+
+/**
+ * HWS specific log buffer header details.
+ * Total size is 32 bytes.
+ */
+struct vpu_hws_log_buffer_header {
+ /** Written by VPU after adding a log entry. Initialised by host to 0. */
+ u32 first_free_entry_index;
+ /** Incremented by VPU every time the VPU writes the 0th entry; initialised by host to 0. */
+ u32 wraparound_count;
+ /**
+ * This is the number of buffers that can be stored in the log buffer provided by the host.
+ * It is written by host before passing buffer to VPU. VPU should consider it read-only.
+ */
+ u64 num_of_entries;
+ u64 reserved[2];
+};
+
+/**
+ * HWS specific log buffer entry details.
+ * Total size is 32 bytes.
+ */
+struct vpu_hws_log_buffer_entry {
+ /** VPU timestamp must be an invariant timer tick (not impacted by DVFS) */
+ u64 vpu_timestamp;
+ /**
+ * Operation type:
+ * 0 - context state change
+ * 1 - queue new work
+ * 2 - queue unwait sync object
+ * 3 - queue no more work
+ * 4 - queue wait sync object
+ */
+ u32 operation_type;
+ u32 reserved;
+ /** Operation data depends on operation type */
+ u64 operation_data[2];
+};
+
+/* Native fence log buffer types. */
+enum vpu_hws_native_fence_log_type {
+ VPU_HWS_NATIVE_FENCE_LOG_TYPE_WAITS = 1,
+ VPU_HWS_NATIVE_FENCE_LOG_TYPE_SIGNALS = 2
+};
+
+/** HWS native fence log buffer header. */
+struct vpu_hws_native_fence_log_header {
+ union {
+ struct {
+ /** Index of the first free entry in buffer. */
+ u32 first_free_entry_idx;
+ /**
+ * Incremented whenever the NPU wraps around the buffer and writes
+ * to the first entry again.
+ */
+ u32 wraparound_count;
+ };
+ /** Field allowing atomic update of both fields above. */
+ u64 atomic_wraparound_and_entry_idx;
+ };
+ /** Log buffer type, see enum vpu_hws_native_fence_log_type. */
+ u64 type;
+ /** Allocated number of entries in the log buffer. */
+ u64 entry_nb;
+ u64 reserved[2];
+};
+
+/** Native fence log operation types. */
+enum vpu_hws_native_fence_log_op {
+ VPU_HWS_NATIVE_FENCE_LOG_OP_SIGNAL_EXECUTED = 0,
+ VPU_HWS_NATIVE_FENCE_LOG_OP_WAIT_UNBLOCKED = 1
+};
+
+/** HWS native fence log entry. */
+struct vpu_hws_native_fence_log_entry {
+ /** Newly signaled/unblocked fence value. */
+ u64 fence_value;
+ /** Native fence object handle to which this operation belongs. */
+ u64 fence_handle;
+ /** Operation type, see enum vpu_hws_native_fence_log_op. */
+ u64 op_type;
+ u64 reserved_0;
+ /**
+ * VPU_HWS_NATIVE_FENCE_LOG_OP_WAIT_UNBLOCKED only: Timestamp at which fence
+ * wait was started (in NPU SysTime).
+ */
+ u64 fence_wait_start_ts;
+ u64 reserved_1;
+ /** Timestamp at which fence operation was completed (in NPU SysTime). */
+ u64 fence_end_ts;
+};
+
+/** Native fence log buffer. */
+struct vpu_hws_native_fence_log_buffer {
+ struct vpu_hws_native_fence_log_header header;
+ struct vpu_hws_native_fence_log_entry entry[];
+};
+
+/*
+ * Host <-> VPU IPC messages types.
+ */
+enum vpu_ipc_msg_type {
+ /** Unsupported command */
+ VPU_JSM_MSG_UNKNOWN = 0xFFFFFFFF,
+
+ /** IPC Host -> Device, base id for async commands */
+ VPU_JSM_MSG_ASYNC_CMD = 0x1100,
+ /**
+ * Reset engine. The NPU cancels all the jobs currently executing on the target
+ * engine making the engine become idle and then does a HW reset, before returning
+ * to the host.
+ * @see struct vpu_ipc_msg_payload_engine_reset
+ */
+ VPU_JSM_MSG_ENGINE_RESET = VPU_JSM_MSG_ASYNC_CMD,
+ /**
+ * Preempt engine. The NPU stops (preempts) all the jobs currently
+ * executing on the target engine making the engine become idle and ready to
+ * execute new jobs.
+ * NOTE: The NPU does not remove unstarted jobs (if any) from job queues of
+ * the target engine, but it stops processing them (until the queue doorbell
+ * is rung again); the host is responsible to reset the job queue, either
+ * after preemption or when resubmitting jobs to the queue.
+ * @see vpu_ipc_msg_payload_engine_preempt
+ */
+ VPU_JSM_MSG_ENGINE_PREEMPT = 0x1101,
+ /**
+ * OS scheduling doorbell register command
+ * @see vpu_ipc_msg_payload_register_db
+ */
+ VPU_JSM_MSG_REGISTER_DB = 0x1102,
+ /**
+ * OS scheduling doorbell unregister command
+ * @see vpu_ipc_msg_payload_unregister_db
+ */
+ VPU_JSM_MSG_UNREGISTER_DB = 0x1103,
+ /**
+ * Query engine heartbeat. Heartbeat is expected to increase monotonically
+ * and increase while work is being progressed by NPU.
+ * @see vpu_ipc_msg_payload_query_engine_hb
+ */
+ VPU_JSM_MSG_QUERY_ENGINE_HB = 0x1104,
+ VPU_JSM_MSG_GET_POWER_LEVEL_COUNT = 0x1105,
+ VPU_JSM_MSG_GET_POWER_LEVEL = 0x1106,
+ VPU_JSM_MSG_SET_POWER_LEVEL = 0x1107,
+ /* @deprecated */
+ VPU_JSM_MSG_METRIC_STREAMER_OPEN = 0x1108,
+ /* @deprecated */
+ VPU_JSM_MSG_METRIC_STREAMER_CLOSE = 0x1109,
+ /** Configure logging (used to modify configuration passed in boot params). */
+ VPU_JSM_MSG_TRACE_SET_CONFIG = 0x110a,
+ /** Return current logging configuration. */
+ VPU_JSM_MSG_TRACE_GET_CONFIG = 0x110b,
+ /**
+ * Get masks of destinations and HW components supported by the firmware
+ * (may vary between HW generations and FW compile
+ * time configurations)
+ */
+ VPU_JSM_MSG_TRACE_GET_CAPABILITY = 0x110c,
+ /** Get the name of a destination or HW component. */
+ VPU_JSM_MSG_TRACE_GET_NAME = 0x110d,
+ /**
+ * Release resource associated with host ssid . All jobs that belong to the host_ssid
+ * aborted and removed from internal scheduling queues. All doorbells assigned
+ * to the host_ssid are unregistered and any internal FW resources belonging to
+ * the host_ssid are released.
+ * @see vpu_ipc_msg_payload_ssid_release
+ */
+ VPU_JSM_MSG_SSID_RELEASE = 0x110e,
+ /**
+ * Start collecting metric data.
+ * @see vpu_jsm_metric_streamer_start
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_START = 0x110f,
+ /**
+ * Stop collecting metric data. This command will return success if it is called
+ * for a metric stream that has already been stopped or was never started.
+ * @see vpu_jsm_metric_streamer_stop
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_STOP = 0x1110,
+ /**
+ * Update current and next buffer for metric data collection. This command can
+ * also be used to request information about the number of collected samples
+ * and the amount of data written to the buffer.
+ * @see vpu_jsm_metric_streamer_update
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_UPDATE = 0x1111,
+ /**
+ * Request description of selected metric groups and metric counters within
+ * each group. The VPU will write the description of groups and counters to
+ * the buffer specified in the command structure.
+ * @see vpu_jsm_metric_streamer_start
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_INFO = 0x1112,
+ /**
+ * Control command: Priority band setup
+ * @see vpu_ipc_msg_payload_hws_priority_band_setup
+ */
+ VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP = 0x1113,
+ /**
+ * Control command: Create command queue
+ * @see vpu_ipc_msg_payload_hws_create_cmdq
+ */
+ VPU_JSM_MSG_CREATE_CMD_QUEUE = 0x1114,
+ /**
+ * Control command: Destroy command queue
+ * @see vpu_ipc_msg_payload_hws_destroy_cmdq
+ */
+ VPU_JSM_MSG_DESTROY_CMD_QUEUE = 0x1115,
+ /**
+ * Control command: Set context scheduling properties
+ * @see vpu_ipc_msg_payload_hws_set_context_sched_properties
+ */
+ VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES = 0x1116,
+ /**
+ * Register a doorbell to notify VPU of new work. The doorbell may later be
+ * deallocated or reassigned to another context.
+ * @see vpu_jsm_hws_register_db
+ */
+ VPU_JSM_MSG_HWS_REGISTER_DB = 0x1117,
+ /**
+ * Control command: Log buffer setting
+ * @see vpu_ipc_msg_payload_hws_set_scheduling_log
+ */
+ VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG = 0x1118,
+ /**
+ * Control command: Suspend command queue.
+ * @see vpu_ipc_msg_payload_hws_suspend_cmdq
+ */
+ VPU_JSM_MSG_HWS_SUSPEND_CMDQ = 0x1119,
+ /**
+ * Control command: Resume command queue
+ * @see vpu_ipc_msg_payload_hws_resume_cmdq
+ */
+ VPU_JSM_MSG_HWS_RESUME_CMDQ = 0x111a,
+ /**
+ * Control command: Resume engine after reset
+ * @see vpu_ipc_msg_payload_hws_resume_engine
+ */
+ VPU_JSM_MSG_HWS_ENGINE_RESUME = 0x111b,
+ /**
+ * Control command: Enable survivability/DCT mode
+ * @see vpu_ipc_msg_payload_pwr_dct_control
+ */
+ VPU_JSM_MSG_DCT_ENABLE = 0x111c,
+ /**
+ * Control command: Disable survivability/DCT mode
+ * This command has no payload
+ */
+ VPU_JSM_MSG_DCT_DISABLE = 0x111d,
+ /**
+ * Dump VPU state. To be used for debug purposes only.
+ * This command has no payload.
+ * NOTE: Please introduce new ASYNC commands before this one.
+ */
+ VPU_JSM_MSG_STATE_DUMP = 0x11FF,
+
+ /** IPC Host -> Device, base id for general commands */
+ VPU_JSM_MSG_GENERAL_CMD = 0x1200,
+ /** Unsupported command */
+ VPU_JSM_MSG_BLOB_DEINIT_DEPRECATED = VPU_JSM_MSG_GENERAL_CMD,
+ /**
+ * Control dyndbg behavior by executing a dyndbg command; equivalent to
+ * Linux command:
+ * @verbatim echo '<dyndbg_cmd>' > <debugfs>/dynamic_debug/control @endverbatim
+ * @see vpu_ipc_msg_payload_dyndbg_control
+ */
+ VPU_JSM_MSG_DYNDBG_CONTROL = 0x1201,
+ /**
+ * Perform the save procedure for the D0i3 entry
+ */
+ VPU_JSM_MSG_PWR_D0I3_ENTER = 0x1202,
+
+ /**
+ * IPC Device -> Host, Job completion
+ * @see struct vpu_ipc_msg_payload_job_done
+ */
+ VPU_JSM_MSG_JOB_DONE = 0x2100,
+ /**
+ * IPC Device -> Host, Fence signalled
+ * @see vpu_ipc_msg_payload_native_fence_signalled
+ */
+ VPU_JSM_MSG_NATIVE_FENCE_SIGNALLED = 0x2101,
+
+ /* IPC Device -> Host, Async command completion */
+ VPU_JSM_MSG_ASYNC_CMD_DONE = 0x2200,
+ /**
+ * IPC Device -> Host, engine reset complete
+ * @see vpu_ipc_msg_payload_engine_reset_done
+ */
+ VPU_JSM_MSG_ENGINE_RESET_DONE = VPU_JSM_MSG_ASYNC_CMD_DONE,
+ /**
+ * Preempt complete message
+ * @see vpu_ipc_msg_payload_engine_preempt_done
+ */
+ VPU_JSM_MSG_ENGINE_PREEMPT_DONE = 0x2201,
+ VPU_JSM_MSG_REGISTER_DB_DONE = 0x2202,
+ VPU_JSM_MSG_UNREGISTER_DB_DONE = 0x2203,
+ /**
+ * Response to query engine heartbeat.
+ * @see vpu_ipc_msg_payload_query_engine_hb_done
+ */
+ VPU_JSM_MSG_QUERY_ENGINE_HB_DONE = 0x2204,
+ VPU_JSM_MSG_GET_POWER_LEVEL_COUNT_DONE = 0x2205,
+ VPU_JSM_MSG_GET_POWER_LEVEL_DONE = 0x2206,
+ VPU_JSM_MSG_SET_POWER_LEVEL_DONE = 0x2207,
+ /* @deprecated */
+ VPU_JSM_MSG_METRIC_STREAMER_OPEN_DONE = 0x2208,
+ /* @deprecated */
+ VPU_JSM_MSG_METRIC_STREAMER_CLOSE_DONE = 0x2209,
+ /** Response to VPU_JSM_MSG_TRACE_SET_CONFIG. */
+ VPU_JSM_MSG_TRACE_SET_CONFIG_RSP = 0x220a,
+ /** Response to VPU_JSM_MSG_TRACE_GET_CONFIG. */
+ VPU_JSM_MSG_TRACE_GET_CONFIG_RSP = 0x220b,
+ /** Response to VPU_JSM_MSG_TRACE_GET_CAPABILITY. */
+ VPU_JSM_MSG_TRACE_GET_CAPABILITY_RSP = 0x220c,
+ /** Response to VPU_JSM_MSG_TRACE_GET_NAME. */
+ VPU_JSM_MSG_TRACE_GET_NAME_RSP = 0x220d,
+ /**
+ * Response to VPU_JSM_MSG_SSID_RELEASE.
+ * @see vpu_ipc_msg_payload_ssid_release
+ */
+ VPU_JSM_MSG_SSID_RELEASE_DONE = 0x220e,
+ /**
+ * Response to VPU_JSM_MSG_METRIC_STREAMER_START.
+ * VPU will return an error result if metric collection cannot be started,
+ * e.g. when the specified metric mask is invalid.
+ * @see vpu_jsm_metric_streamer_done
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_START_DONE = 0x220f,
+ /**
+ * Response to VPU_JSM_MSG_METRIC_STREAMER_STOP.
+ * Returns information about collected metric data.
+ * @see vpu_jsm_metric_streamer_done
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_STOP_DONE = 0x2210,
+ /**
+ * Response to VPU_JSM_MSG_METRIC_STREAMER_UPDATE.
+ * Returns information about collected metric data.
+ * @see vpu_jsm_metric_streamer_done
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_UPDATE_DONE = 0x2211,
+ /**
+ * Response to VPU_JSM_MSG_METRIC_STREAMER_INFO.
+ * Returns a description of the metric groups and metric counters.
+ * @see vpu_jsm_metric_streamer_done
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_INFO_DONE = 0x2212,
+ /**
+ * Asynchronous event sent from the VPU to the host either when the current
+ * metric buffer is full or when the VPU has collected a multiple of
+ * @ref vpu_jsm_metric_streamer_start::notify_sample_count samples as indicated
+ * through the start command (VPU_JSM_MSG_METRIC_STREAMER_START). Returns
+ * information about collected metric data.
+ * @see vpu_jsm_metric_streamer_done
+ */
+ VPU_JSM_MSG_METRIC_STREAMER_NOTIFICATION = 0x2213,
+ /**
+ * Response to control command: Priority band setup
+ * @see vpu_ipc_msg_payload_hws_priority_band_setup
+ */
+ VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP_RSP = 0x2214,
+ /**
+ * Response to control command: Create command queue
+ * @see vpu_ipc_msg_payload_hws_create_cmdq_rsp
+ */
+ VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP = 0x2215,
+ /**
+ * Response to control command: Destroy command queue
+ * @see vpu_ipc_msg_payload_hws_destroy_cmdq
+ */
+ VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP = 0x2216,
+ /**
+ * Response to control command: Set context scheduling properties
+ * @see vpu_ipc_msg_payload_hws_set_context_sched_properties
+ */
+ VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP = 0x2217,
+ /**
+ * Response to control command: Log buffer setting
+ * @see vpu_ipc_msg_payload_hws_set_scheduling_log
+ */
+ VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP = 0x2218,
+ /**
+ * IPC Device -> Host, HWS notify index entry of log buffer written
+ * @see vpu_ipc_msg_payload_hws_scheduling_log_notification
+ */
+ VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION = 0x2219,
+ /**
+ * IPC Device -> Host, HWS completion of a context suspend request
+ * @see vpu_ipc_msg_payload_hws_suspend_cmdq
+ */
+ VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE = 0x221a,
+ /**
+ * Response to control command: Resume command queue
+ * @see vpu_ipc_msg_payload_hws_resume_cmdq
+ */
+ VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP = 0x221b,
+ /**
+ * Response to control command: Resume engine command response
+ * @see vpu_ipc_msg_payload_hws_resume_engine
+ */
+ VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE = 0x221c,
+ /**
+ * Response to control command: Enable survivability/DCT mode
+ * This command has no payload
+ */
+ VPU_JSM_MSG_DCT_ENABLE_DONE = 0x221d,
+ /**
+ * Response to control command: Disable survivability/DCT mode
+ * This command has no payload
+ */
+ VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e,
+ /**
+ * Response to state dump control command.
+ * This command has no payload.
+ * NOTE: Please introduce new ASYNC responses before this one.
+ */
+ VPU_JSM_MSG_STATE_DUMP_RSP = 0x22FF,
+
+ /* IPC Device -> Host, General command completion */
+ VPU_JSM_MSG_GENERAL_CMD_DONE = 0x2300,
+ VPU_JSM_MSG_BLOB_DEINIT_DONE = VPU_JSM_MSG_GENERAL_CMD_DONE,
+ /** Response to VPU_JSM_MSG_DYNDBG_CONTROL. */
+ VPU_JSM_MSG_DYNDBG_CONTROL_RSP = 0x2301,
+ /**
+ * Acknowledgment of completion of the save procedure initiated by
+ * VPU_JSM_MSG_PWR_D0I3_ENTER
+ */
+ VPU_JSM_MSG_PWR_D0I3_ENTER_DONE = 0x2302,
+};
+
+enum vpu_ipc_msg_status { VPU_JSM_MSG_FREE, VPU_JSM_MSG_ALLOCATED };
+
+/**
+ * Engine reset request payload
+ * @see VPU_JSM_MSG_ENGINE_RESET
+ */
+struct vpu_ipc_msg_payload_engine_reset {
+ /** Engine to be reset. */
+ u32 engine_idx;
+ /** Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * Engine preemption request struct
+ * @see VPU_JSM_MSG_ENGINE_PREEMPT
+ */
+struct vpu_ipc_msg_payload_engine_preempt {
+ /** Engine to be preempted. */
+ u32 engine_idx;
+ /** ID of the preemption request. */
+ u32 preempt_id;
+};
+
+/**
+ * Register doorbell command structure.
+ * This structure supports doorbell registration for only OS scheduling.
+ * @see VPU_JSM_MSG_REGISTER_DB
+ */
+struct vpu_ipc_msg_payload_register_db {
+ /** Index of the doorbell to register. */
+ u32 db_idx;
+ /** Reserved */
+ u32 reserved_0;
+ /** Virtual address in Global GTT pointing to the start of job queue. */
+ u64 jobq_base;
+ /** Size of the job queue in bytes. */
+ u32 jobq_size;
+ /** Host sub-stream ID for the context assigned to the doorbell. */
+ u32 host_ssid;
+};
+
+/**
+ * Unregister doorbell command structure.
+ * Request structure to unregister a doorbell for both HW and OS scheduling.
+ * @see VPU_JSM_MSG_UNREGISTER_DB
+ */
+struct vpu_ipc_msg_payload_unregister_db {
+ /** Index of the doorbell to unregister. */
+ u32 db_idx;
+ /** Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * Heartbeat request structure
+ * @see VPU_JSM_MSG_QUERY_ENGINE_HB
+ */
+struct vpu_ipc_msg_payload_query_engine_hb {
+ /** Engine to return heartbeat value. */
+ u32 engine_idx;
+ /** Reserved */
+ u32 reserved_0;
+};
+
+struct vpu_ipc_msg_payload_power_level {
+ /**
+ * Requested power level. The power level value is in the
+ * range [0, power_level_count-1] where power_level_count
+ * is the number of available power levels as returned by
+ * the get power level count command. A power level of 0
+ * corresponds to the maximum possible power level, while
+ * power_level_count-1 corresponds to the minimum possible
+ * power level. Values outside of this range are not
+ * considered to be valid.
+ */
+ u32 power_level;
+ /* Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * Structure for requesting ssid release
+ * @see VPU_JSM_MSG_SSID_RELEASE
+ */
+struct vpu_ipc_msg_payload_ssid_release {
+ /** Host sub-stream ID for the context to be released. */
+ u32 host_ssid;
+ /** Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * @brief Metric streamer start command structure.
+ * This structure is also used with VPU_JSM_MSG_METRIC_STREAMER_INFO to request metric
+ * groups and metric counters description from the firmware.
+ * @see VPU_JSM_MSG_METRIC_STREAMER_START
+ * @see VPU_JSM_MSG_METRIC_STREAMER_INFO
+ */
+struct vpu_jsm_metric_streamer_start {
+ /**
+ * Bitmask to select the desired metric groups.
+ * A metric group can belong only to one metric streamer instance at a time.
+ * Since each metric streamer instance has a unique set of metric groups, it
+ * can also identify a metric streamer instance if more than one instance was
+ * started. If the VPU device does not support multiple metric streamer instances,
+ * then VPU_JSM_MSG_METRIC_STREAMER_START will return an error even if the second
+ * instance has different groups to the first.
+ */
+ u64 metric_group_mask;
+ /** Sampling rate in nanoseconds. */
+ u64 sampling_rate;
+ /**
+ * If > 0 the VPU will send a VPU_JSM_MSG_METRIC_STREAMER_NOTIFICATION message
+ * after every @ref notify_sample_count samples is collected or dropped by the VPU.
+ * If set to UINT_MAX the VPU will only generate a notification when the metric
+ * buffer is full. If set to 0 the VPU will never generate a notification.
+ */
+ u32 notify_sample_count;
+ u32 reserved_0;
+ /**
+ * Address and size of the buffer where the VPU will write metric data. The
+ * VPU writes all counters from enabled metric groups one after another. If
+ * there is no space left to write data at the next sample period the VPU
+ * will switch to the next buffer (@ref next_buffer_addr) and will optionally
+ * send a notification to the host driver if @ref notify_sample_count is non-zero.
+ * If @ref next_buffer_addr is NULL the VPU will stop collecting metric data.
+ */
+ u64 buffer_addr;
+ u64 buffer_size;
+ /**
+ * Address and size of the next buffer to write metric data to after the initial
+ * buffer is full. If the address is NULL the VPU will stop collecting metric
+ * data.
+ */
+ u64 next_buffer_addr;
+ u64 next_buffer_size;
+};
+
+/**
+ * @brief Metric streamer stop command structure.
+ * @see VPU_JSM_MSG_METRIC_STREAMER_STOP
+ */
+struct vpu_jsm_metric_streamer_stop {
+ /** Bitmask to select the desired metric groups. */
+ u64 metric_group_mask;
+};
+
+/**
+ * Provide VPU FW with buffers to write metric data.
+ * @see VPU_JSM_MSG_METRIC_STREAMER_UPDATE
+ */
+struct vpu_jsm_metric_streamer_update {
+ /** Metric group mask that identifies metric streamer instance. */
+ u64 metric_group_mask;
+ /**
+ * Address and size of the buffer where the VPU will write metric data.
+ * This member dictates how the update operation should perform:
+ * 1. client needs information about the number of collected samples and the
+ * amount of data written to the current buffer
+ * 2. client wants to switch to a new buffer
+ *
+ * Case 1. is identified by the buffer address being 0 or the same as the
+ * currently used buffer address. In this case the buffer size is ignored and
+ * the size of the current buffer is unchanged. The VPU will return an update
+ * in the vpu_jsm_metric_streamer_done structure. The internal writing position
+ * into the buffer is not changed.
+ *
+ * Case 2. is identified by the address being non-zero and differs from the
+ * current buffer address. The VPU will immediately switch data collection to
+ * the new buffer. Then the VPU will return an update in the
+ * vpu_jsm_metric_streamer_done structure.
+ */
+ u64 buffer_addr;
+ u64 buffer_size;
+ /**
+ * Address and size of the next buffer to write metric data after the initial
+ * buffer is full. If the address is NULL the VPU will stop collecting metric
+ * data but will continue to record dropped samples.
+ *
+ * Note that there is a hazard possible if both buffer_addr and the next_buffer_addr
+ * are non-zero in same update request. It is the host's responsibility to ensure
+ * that both addresses make sense even if the VPU just switched to writing samples
+ * from the current to the next buffer.
+ */
+ u64 next_buffer_addr;
+ u64 next_buffer_size;
+};
+
+/**
+ * Device -> host job completion message.
+ * @see VPU_JSM_MSG_JOB_DONE
+ */
+struct vpu_ipc_msg_payload_job_done {
+ /** Engine to which the job was submitted. */
+ u32 engine_idx;
+ /** Index of the doorbell to which the job was submitted */
+ u32 db_idx;
+ /** ID of the completed job */
+ u32 job_id;
+ /** Status of the completed job */
+ u32 job_status;
+ /** Host SSID */
+ u32 host_ssid;
+ /** Zero Padding */
+ u32 reserved_0;
+ /** Command queue id */
+ u64 cmdq_id;
+};
+
+/**
+ * Notification message upon native fence signalling.
+ * @see VPU_JSM_MSG_NATIVE_FENCE_SIGNALLED
+ */
+struct vpu_ipc_msg_payload_native_fence_signalled {
+ /** Engine ID. */
+ u32 engine_idx;
+ /** Host SSID. */
+ u32 host_ssid;
+ /** CMDQ ID */
+ u64 cmdq_id;
+ /** Fence object handle. */
+ u64 fence_handle;
+};
+
+/**
+ * vpu_ipc_msg_payload_engine_reset_done will contain an array of this structure
+ * which contains which queues caused reset if FW was able to detect any error.
+ * @see vpu_ipc_msg_payload_engine_reset_done
+ */
+struct vpu_jsm_engine_reset_context {
+ /** Host SSID */
+ u32 host_ssid;
+ /** Zero Padding */
+ u32 reserved_0;
+ /** Command queue id */
+ u64 cmdq_id;
+ /** See VPU_ENGINE_RESET_CONTEXT_* defines */
+ u64 flags;
+};
+
+/**
+ * Engine reset response.
+ * @see VPU_JSM_MSG_ENGINE_RESET_DONE
+ */
+struct vpu_ipc_msg_payload_engine_reset_done {
+ /** Engine ordinal */
+ u32 engine_idx;
+ /** Number of impacted contexts */
+ u32 num_impacted_contexts;
+ /** Array of impacted command queue ids and their flags */
+ struct vpu_jsm_engine_reset_context
+ impacted_contexts[VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS];
+};
+
+/**
+ * Preemption response struct
+ * @see VPU_JSM_MSG_ENGINE_PREEMPT_DONE
+ */
+struct vpu_ipc_msg_payload_engine_preempt_done {
+ /** Engine preempted. */
+ u32 engine_idx;
+ /** ID of the preemption request. */
+ u32 preempt_id;
+};
+
+/**
+ * Response structure for register doorbell command for both OS
+ * and HW scheduling.
+ * @see VPU_JSM_MSG_REGISTER_DB
+ * @see VPU_JSM_MSG_HWS_REGISTER_DB
+ */
+struct vpu_ipc_msg_payload_register_db_done {
+ /* Index of the registered doorbell. */
+ u32 db_idx;
+ /* Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * Response structure for unregister doorbell command for both OS
+ * and HW scheduling.
+ * @see VPU_JSM_MSG_UNREGISTER_DB
+ */
+struct vpu_ipc_msg_payload_unregister_db_done {
+ /* Index of the unregistered doorbell. */
+ u32 db_idx;
+ /* Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * Structure for heartbeat response
+ * @see VPU_JSM_MSG_QUERY_ENGINE_HB_DONE
+ */
+struct vpu_ipc_msg_payload_query_engine_hb_done {
+ /** Engine returning heartbeat value. */
+ u32 engine_idx;
+ /** Reserved */
+ u32 reserved_0;
+ /** Heartbeat value. */
+ u64 heartbeat;
+};
+
+struct vpu_ipc_msg_payload_get_power_level_count_done {
+ /**
+ * Number of supported power levels. The maximum possible
+ * value of power_level_count is 16 but this may vary across
+ * implementations.
+ */
+ u32 power_level_count;
+ /* Reserved */
+ u32 reserved_0;
+ /**
+ * Power consumption limit for each supported power level in
+ * [0-100%] range relative to power level 0.
+ */
+ u8 power_limit[16];
+};
+
+/**
+ * HWS priority band setup request / response
+ * @see VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP
+ */
+struct vpu_ipc_msg_payload_hws_priority_band_setup {
+ /*
+ * Grace period in 100ns units when preempting another priority band for
+ * this priority band
+ */
+ u32 grace_period[VPU_HWS_NUM_PRIORITY_BANDS];
+ /*
+ * Default quantum in 100ns units for scheduling across processes
+ * within a priority band
+ * Minimum value supported by NPU is 1ms (10000 in 100ns units).
+ */
+ u32 process_quantum[VPU_HWS_NUM_PRIORITY_BANDS];
+ /*
+ * Default grace period in 100ns units for processes that preempt each
+ * other within a priority band
+ */
+ u32 process_grace_period[VPU_HWS_NUM_PRIORITY_BANDS];
+ /*
+ * For normal priority band, specifies the target VPU percentage
+ * in situations when it's starved by the focus band.
+ */
+ u32 normal_band_percentage;
+ /*
+ * TDR timeout value in milliseconds. Default value of 0 meaning no timeout.
+ */
+ u32 tdr_timeout;
+ /* Non-interactive queue timeout for no progress of heartbeat in milliseconds.
+ * Default value of 0 meaning no timeout.
+ */
+ u32 non_interactive_no_progress_timeout;
+ /*
+ * Non-interactive queue upper limit timeout value in milliseconds. Default
+ * value of 0 meaning no timeout.
+ */
+ u32 non_interactive_timeout;
+};
+
+/**
+ * @brief HWS create command queue request.
+ * Host will create a command queue via this command.
+ * Note: Cmdq group is a handle of an object which
+ * may contain one or more command queues.
+ * @see VPU_JSM_MSG_CREATE_CMD_QUEUE
+ */
+struct vpu_ipc_msg_payload_hws_create_cmdq {
+ /* Process id */
+ u64 process_id;
+ /* Host SSID */
+ u32 host_ssid;
+ /* Engine for which queue is being created */
+ u32 engine_idx;
+ /* Cmdq group: only used for HWS logging of state changes */
+ u64 cmdq_group;
+ /* Command queue id */
+ u64 cmdq_id;
+ /* Command queue base */
+ u64 cmdq_base;
+ /* Command queue size */
+ u32 cmdq_size;
+ /* Zero padding */
+ u32 reserved_0;
+};
+
+/**
+ * HWS create command queue response.
+ * @see VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP
+ */
+struct vpu_ipc_msg_payload_hws_create_cmdq_rsp {
+ /** Process id */
+ u64 process_id;
+ /** Host SSID */
+ u32 host_ssid;
+ /** Engine for which queue is being created */
+ u32 engine_idx;
+ /** Command queue group */
+ u64 cmdq_group;
+ /** Command queue id */
+ u64 cmdq_id;
+};
+
+/**
+ * HWS destroy command queue request / response
+ * @see VPU_JSM_MSG_DESTROY_CMD_QUEUE
+ * @see VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP
+ */
+struct vpu_ipc_msg_payload_hws_destroy_cmdq {
+ /** Host SSID */
+ u32 host_ssid;
+ /** Zero Padding */
+ u32 reserved;
+ /** Command queue id */
+ u64 cmdq_id;
+};
+
+/**
+ * HWS set context scheduling properties request / response
+ * @see VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES
+ * @see VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP
+ */
+struct vpu_ipc_msg_payload_hws_set_context_sched_properties {
+ /** Host SSID */
+ u32 host_ssid;
+ /** Zero Padding */
+ u32 reserved_0;
+ /** Command queue id */
+ u64 cmdq_id;
+ /**
+ * Priority band to assign to work of this context.
+ * Available priority bands: @see enum vpu_job_scheduling_priority_band
+ */
+ u32 priority_band;
+ /** Inside realtime band assigns a further priority */
+ u32 realtime_priority_level;
+ /** Priority relative to other contexts in the same process */
+ s32 in_process_priority;
+ /** Zero padding / Reserved */
+ u32 reserved_1;
+ /**
+ * Context quantum relative to other contexts of same priority in the same process
+ * Minimum value supported by NPU is 1ms (10000 in 100ns units).
+ */
+ u64 context_quantum;
+ /** Grace period when preempting context of the same priority within the same process */
+ u64 grace_period_same_priority;
+ /** Grace period when preempting context of a lower priority within the same process */
+ u64 grace_period_lower_priority;
+};
+
+/**
+ * Register doorbell command structure.
+ * This structure supports doorbell registration for both HW and OS scheduling.
+ * Note: Queue base and size are added here so that the same structure can be used for
+ * OS scheduling and HW scheduling. For OS scheduling, cmdq_id will be ignored
+ * and cmdq_base and cmdq_size will be used. For HW scheduling, cmdq_base and cmdq_size will be
+ * ignored and cmdq_id is used.
+ * @see VPU_JSM_MSG_HWS_REGISTER_DB
+ */
+struct vpu_jsm_hws_register_db {
+ /** Index of the doorbell to register. */
+ u32 db_id;
+ /** Host sub-stream ID for the context assigned to the doorbell. */
+ u32 host_ssid;
+ /** ID of the command queue associated with the doorbell. */
+ u64 cmdq_id;
+ /** Virtual address pointing to the start of command queue. */
+ u64 cmdq_base;
+ /** Size of the command queue in bytes. */
+ u64 cmdq_size;
+};
+
+/**
+ * Structure to set another buffer to be used for scheduling-related logging.
+ * The size of the logging buffer and the number of entries is defined as part of the
+ * buffer itself as described next.
+ * The log buffer received from the host is made up of;
+ * - header: 32 bytes in size, as shown in @ref vpu_hws_log_buffer_header.
+ * The header contains the number of log entries in the buffer.
+ * - log entry: 0 to n-1, each log entry is 32 bytes in size, as shown in
+ * @ref vpu_hws_log_buffer_entry.
+ * The entry contains the VPU timestamp, operation type and data.
+ * The host should provide the notify index value of log buffer to VPU. This is a
+ * value defined within the log buffer and when written to will generate the
+ * scheduling log notification.
+ * The host should set engine_idx and vpu_log_buffer_va to 0 to disable logging
+ * for a particular engine.
+ * VPU will handle one log buffer for each of supported engines.
+ * VPU should allow the logging to consume one host_ssid.
+ * @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG
+ * @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP
+ * @see VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION
+ */
+struct vpu_ipc_msg_payload_hws_set_scheduling_log {
+ /** Engine ordinal */
+ u32 engine_idx;
+ /** Host SSID */
+ u32 host_ssid;
+ /**
+ * VPU log buffer virtual address.
+ * Set to 0 to disable logging for this engine.
+ */
+ u64 vpu_log_buffer_va;
+ /**
+ * Notify index of log buffer. VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION
+ * is generated when an event log is written to this index.
+ */
+ u64 notify_index;
+ /**
+ * Field is now deprecated, will be removed when KMD is updated to support removal
+ */
+ u32 enable_extra_events;
+ /** Zero Padding */
+ u32 reserved_0;
+};
+
+/**
+ * The scheduling log notification is generated by VPU when it writes
+ * an event into the log buffer at the notify_index. VPU notifies host with
+ * VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION. This is an asynchronous
+ * message from VPU to host.
+ * @see VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION
+ * @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG
+ */
+struct vpu_ipc_msg_payload_hws_scheduling_log_notification {
+ /** Engine ordinal */
+ u32 engine_idx;
+ /** Zero Padding */
+ u32 reserved_0;
+};
+
+/**
+ * HWS suspend command queue request and done structure.
+ * Host will request the suspend of contexts and VPU will;
+ * - Suspend all work on this context
+ * - Preempt any running work
+ * - Asynchronously perform the above and return success immediately once
+ * all items above are started successfully
+ * - Notify the host of completion of these operations via
+ * VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE
+ * - Reject any other context operations on a context with an in-flight
+ * suspend request running
+ * Same structure used when VPU notifies host of completion of a context suspend
+ * request. The ids and suspend fence value reported in this command will match
+ * the one in the request from the host to suspend the context. Once suspend is
+ * complete, VPU will not access any data relating to this command queue until
+ * it is resumed.
+ * @see VPU_JSM_MSG_HWS_SUSPEND_CMDQ
+ * @see VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE
+ */
+struct vpu_ipc_msg_payload_hws_suspend_cmdq {
+ /** Host SSID */
+ u32 host_ssid;
+ /** Zero Padding */
+ u32 reserved_0;
+ /** Command queue id */
+ u64 cmdq_id;
+ /**
+ * Suspend fence value - reported by the VPU suspend context
+ * completed once suspend is complete.
+ */
+ u64 suspend_fence_value;
+};
+
+/**
+ * HWS Resume command queue request / response structure.
+ * Host will request the resume of a context;
+ * - VPU will resume all work on this context
+ * - Scheduler will allow this context to be scheduled
+ * @see VPU_JSM_MSG_HWS_RESUME_CMDQ
+ * @see VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP
+ */
+struct vpu_ipc_msg_payload_hws_resume_cmdq {
+ /** Host SSID */
+ u32 host_ssid;
+ /** Zero Padding */
+ u32 reserved_0;
+ /** Command queue id */
+ u64 cmdq_id;
+};
+
+/**
+ * HWS Resume engine request / response structure.
+ * After a HWS engine reset, all scheduling is stopped on VPU until an engine resume.
+ * Host shall send this command to resume scheduling of any valid queue.
+ * @see VPU_JSM_MSG_HWS_ENGINE_RESUME
+ * @see VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE
+ */
+struct vpu_ipc_msg_payload_hws_resume_engine {
+ /** Engine to be resumed */
+ u32 engine_idx;
+ /** Reserved */
+ u32 reserved_0;
+};
+
+/**
+ * Payload for VPU_JSM_MSG_TRACE_SET_CONFIG[_RSP] and
+ * VPU_JSM_MSG_TRACE_GET_CONFIG_RSP messages.
+ *
+ * The payload is interpreted differently depending on the type of message:
+ *
+ * - For VPU_JSM_MSG_TRACE_SET_CONFIG, the payload specifies the desired
+ * logging configuration to be set.
+ *
+ * - For VPU_JSM_MSG_TRACE_SET_CONFIG_RSP, the payload reports the logging
+ * configuration that was set after a VPU_JSM_MSG_TRACE_SET_CONFIG request.
+ * The host can compare this payload with the one it sent in the
+ * VPU_JSM_MSG_TRACE_SET_CONFIG request to check whether or not the
+ * configuration was set as desired.
+ *
+ * - VPU_JSM_MSG_TRACE_GET_CONFIG_RSP, the payload reports the current logging
+ * configuration.
+ */
+struct vpu_ipc_msg_payload_trace_config {
+ /**
+ * Logging level (currently set or to be set); see 'mvLog_t' enum for
+ * acceptable values. The specified logging level applies to all
+ * destinations and HW components
+ */
+ u32 trace_level;
+ /**
+ * Bitmask of logging destinations (currently enabled or to be enabled);
+ * bitwise OR of values defined in logging_destination enum.
+ */
+ u32 trace_destination_mask;
+ /**
+ * Bitmask of loggable HW components (currently enabled or to be enabled);
+ * bitwise OR of values defined in loggable_hw_component enum.
+ */
+ u64 trace_hw_component_mask;
+ u64 reserved_0; /**< Reserved for future extensions. */
+};
+
+/**
+ * Payload for VPU_JSM_MSG_TRACE_GET_CAPABILITY_RSP messages.
+ */
+struct vpu_ipc_msg_payload_trace_capability_rsp {
+ u32 trace_destination_mask; /**< Bitmask of supported logging destinations. */
+ u32 reserved_0;
+ u64 trace_hw_component_mask; /**< Bitmask of supported loggable HW components. */
+ u64 reserved_1; /**< Reserved for future extensions. */
+};
+
+/**
+ * Payload for VPU_JSM_MSG_TRACE_GET_NAME requests.
+ */
+struct vpu_ipc_msg_payload_trace_get_name {
+ /**
+ * The type of the entity to query name for; see logging_entity_type for
+ * possible values.
+ */
+ u32 entity_type;
+ u32 reserved_0;
+ /**
+ * The ID of the entity to query name for; possible values depends on the
+ * entity type.
+ */
+ u64 entity_id;
+};
+
+/**
+ * Payload for VPU_JSM_MSG_TRACE_GET_NAME_RSP responses.
+ */
+struct vpu_ipc_msg_payload_trace_get_name_rsp {
+ /**
+ * The type of the entity whose name was queried; see logging_entity_type
+ * for possible values.
+ */
+ u32 entity_type;
+ u32 reserved_0;
+ /**
+ * The ID of the entity whose name was queried; possible values depends on
+ * the entity type.
+ */
+ u64 entity_id;
+ /** Reserved for future extensions. */
+ u64 reserved_1;
+ /** The name of the entity. */
+ char entity_name[VPU_TRACE_ENTITY_NAME_MAX_LEN];
+};
+
+/**
+ * Data sent from the VPU to the host in all metric streamer response messages
+ * and in asynchronous notification.
+ * @see VPU_JSM_MSG_METRIC_STREAMER_START_DONE
+ * @see VPU_JSM_MSG_METRIC_STREAMER_STOP_DONE
+ * @see VPU_JSM_MSG_METRIC_STREAMER_UPDATE_DONE
+ * @see VPU_JSM_MSG_METRIC_STREAMER_INFO_DONE
+ * @see VPU_JSM_MSG_METRIC_STREAMER_NOTIFICATION
+ */
+struct vpu_jsm_metric_streamer_done {
+ /** Metric group mask that identifies metric streamer instance. */
+ u64 metric_group_mask;
+ /**
+ * Size in bytes of single sample - total size of all enabled counters.
+ * Some VPU implementations may align sample_size to more than 8 bytes.
+ */
+ u32 sample_size;
+ u32 reserved_0;
+ /**
+ * Number of samples collected since the metric streamer was started.
+ * This will be 0 if the metric streamer was not started.
+ */
+ u32 samples_collected;
+ /**
+ * Number of samples dropped since the metric streamer was started. This
+ * is incremented every time the metric streamer is not able to write
+ * collected samples because the current buffer is full and there is no
+ * next buffer to switch to.
+ */
+ u32 samples_dropped;
+ /** Address of the buffer that contains the latest metric data. */
+ u64 buffer_addr;
+ /**
+ * Number of bytes written into the metric data buffer. In response to the
+ * VPU_JSM_MSG_METRIC_STREAMER_INFO request this field contains the size of
+ * all group and counter descriptors. The size is updated even if the buffer
+ * in the request was NULL or too small to hold descriptors of all counters
+ */
+ u64 bytes_written;
+};
+
+/**
+ * Metric group description placed in the metric buffer after successful completion
+ * of the VPU_JSM_MSG_METRIC_STREAMER_INFO command. This is followed by one or more
+ * @ref vpu_jsm_metric_counter_descriptor records.
+ * @see VPU_JSM_MSG_METRIC_STREAMER_INFO
+ */
+struct vpu_jsm_metric_group_descriptor {
+ /**
+ * Offset to the next metric group (8-byte aligned). If this offset is 0 this
+ * is the last descriptor. The value of metric_info_size must be greater than
+ * or equal to sizeof(struct vpu_jsm_metric_group_descriptor) + name_string_size
+ * + description_string_size and must be 8-byte aligned.
+ */
+ u32 next_metric_group_info_offset;
+ /**
+ * Offset to the first metric counter description record (8-byte aligned).
+ * @see vpu_jsm_metric_counter_descriptor
+ */
+ u32 next_metric_counter_info_offset;
+ /** Index of the group. This corresponds to bit index in metric_group_mask. */
+ u32 group_id;
+ /** Number of counters in the metric group. */
+ u32 num_counters;
+ /** Data size for all counters, must be a multiple of 8 bytes.*/
+ u32 metric_group_data_size;
+ /**
+ * Metric group domain number. Cannot use multiple, simultaneous metric groups
+ * from the same domain.
+ */
+ u32 domain;
+ /**
+ * Counter name string size. The string must include a null termination character.
+ * The FW may use a fixed size name or send a different name for each counter.
+ * If the VPU uses fixed size strings, all characters from the end of the name
+ * to the of the fixed size character array must be zeroed.
+ */
+ u32 name_string_size;
+ /** Counter description string size, @see name_string_size */
+ u32 description_string_size;
+ u64 reserved_0;
+ /**
+ * Right after this structure, the VPU writes name and description of
+ * the metric group.
+ */
+};
+
+/**
+ * Metric counter description, placed in the buffer after vpu_jsm_metric_group_descriptor.
+ * @see VPU_JSM_MSG_METRIC_STREAMER_INFO
+ */
+struct vpu_jsm_metric_counter_descriptor {
+ /**
+ * Offset to the next counter in a group (8-byte aligned). If this offset is
+ * 0 this is the last counter in the group.
+ */
+ u32 next_metric_counter_info_offset;
+ /**
+ * Offset to the counter data from the start of samples in this metric group.
+ * Note that metric_data_offset % metric_data_size must be 0.
+ */
+ u32 metric_data_offset;
+ /** Size of the metric counter data in bytes. */
+ u32 metric_data_size;
+ /** Metric type, see Level Zero API for definitions. */
+ u32 tier;
+ /** Metric type, see set_metric_type_t for definitions. */
+ u32 metric_type;
+ /** Metric type, see set_value_type_t for definitions. */
+ u32 metric_value_type;
+ /**
+ * Counter name string size. The string must include a null termination character.
+ * The FW may use a fixed size name or send a different name for each counter.
+ * If the VPU uses fixed size strings, all characters from the end of the name
+ * to the of the fixed size character array must be zeroed.
+ */
+ u32 name_string_size;
+ /** Counter description string size, @see name_string_size */
+ u32 description_string_size;
+ /** Counter component name string size, @see name_string_size */
+ u32 component_string_size;
+ /** Counter string size, @see name_string_size */
+ u32 units_string_size;
+ u64 reserved_0;
+ /**
+ * Right after this structure, the VPU writes name, description
+ * component and unit strings.
+ */
+};
+
+/**
+ * Payload for @ref VPU_JSM_MSG_DYNDBG_CONTROL requests.
+ *
+ * VPU_JSM_MSG_DYNDBG_CONTROL requests are used to control the VPU FW dynamic debug
+ * feature, which allows developers to selectively enable/disable code to obtain
+ * additional FW information. This is equivalent to the dynamic debug functionality
+ * provided by Linux. The host can control dynamic debug behavior by sending dyndbg
+ * commands, using the same syntax as for Linux dynamic debug commands.
+ *
+ * @see https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html.
+ *
+ * NOTE:
+ * As the dynamic debug feature uses MVLOG messages to provide information, the host
+ * must first set the logging level to MVLOG_DEBUG, using the @ref VPU_JSM_MSG_TRACE_SET_CONFIG
+ * command.
+ */
+struct vpu_ipc_msg_payload_dyndbg_control {
+ /**
+ * Dyndbg command to be executed.
+ */
+ char dyndbg_cmd[VPU_DYNDBG_CMD_MAX_LEN];
+};
+
+/**
+ * Payload for VPU_JSM_MSG_PWR_D0I3_ENTER
+ *
+ * This is a bi-directional payload.
+ */
+struct vpu_ipc_msg_payload_pwr_d0i3_enter {
+ /**
+ * 0: VPU_JSM_MSG_PWR_D0I3_ENTER_DONE is not sent to the host driver
+ * The driver will poll for D0i2 Idle state transitions.
+ * 1: VPU_JSM_MSG_PWR_D0I3_ENTER_DONE is sent after VPU state save is complete
+ */
+ u32 send_response;
+ u32 reserved_0;
+};
+
+/**
+ * Payload for @ref VPU_JSM_MSG_DCT_ENABLE message.
+ *
+ * Default values for DCT active/inactive times are 5.3ms and 30ms respectively,
+ * corresponding to a 85% duty cycle. This payload allows the host to tune these
+ * values according to application requirements.
+ */
+struct vpu_ipc_msg_payload_pwr_dct_control {
+ /** Duty cycle active time in microseconds */
+ u32 dct_active_us;
+ /** Duty cycle inactive time in microseconds */
+ u32 dct_inactive_us;
+};
+
+/*
+ * Payloads union, used to define complete message format.
+ */
+union vpu_ipc_msg_payload {
+ struct vpu_ipc_msg_payload_engine_reset engine_reset;
+ struct vpu_ipc_msg_payload_engine_preempt engine_preempt;
+ struct vpu_ipc_msg_payload_register_db register_db;
+ struct vpu_ipc_msg_payload_unregister_db unregister_db;
+ struct vpu_ipc_msg_payload_query_engine_hb query_engine_hb;
+ struct vpu_ipc_msg_payload_power_level power_level;
+ struct vpu_jsm_metric_streamer_start metric_streamer_start;
+ struct vpu_jsm_metric_streamer_stop metric_streamer_stop;
+ struct vpu_jsm_metric_streamer_update metric_streamer_update;
+ struct vpu_ipc_msg_payload_ssid_release ssid_release;
+ struct vpu_jsm_hws_register_db hws_register_db;
+ struct vpu_ipc_msg_payload_job_done job_done;
+ struct vpu_ipc_msg_payload_native_fence_signalled native_fence_signalled;
+ struct vpu_ipc_msg_payload_engine_reset_done engine_reset_done;
+ struct vpu_ipc_msg_payload_engine_preempt_done engine_preempt_done;
+ struct vpu_ipc_msg_payload_register_db_done register_db_done;
+ struct vpu_ipc_msg_payload_unregister_db_done unregister_db_done;
+ struct vpu_ipc_msg_payload_query_engine_hb_done query_engine_hb_done;
+ struct vpu_ipc_msg_payload_get_power_level_count_done get_power_level_count_done;
+ struct vpu_jsm_metric_streamer_done metric_streamer_done;
+ struct vpu_ipc_msg_payload_trace_config trace_config;
+ struct vpu_ipc_msg_payload_trace_capability_rsp trace_capability;
+ struct vpu_ipc_msg_payload_trace_get_name trace_get_name;
+ struct vpu_ipc_msg_payload_trace_get_name_rsp trace_get_name_rsp;
+ struct vpu_ipc_msg_payload_dyndbg_control dyndbg_control;
+ struct vpu_ipc_msg_payload_hws_priority_band_setup hws_priority_band_setup;
+ struct vpu_ipc_msg_payload_hws_create_cmdq hws_create_cmdq;
+ struct vpu_ipc_msg_payload_hws_create_cmdq_rsp hws_create_cmdq_rsp;
+ struct vpu_ipc_msg_payload_hws_destroy_cmdq hws_destroy_cmdq;
+ struct vpu_ipc_msg_payload_hws_set_context_sched_properties
+ hws_set_context_sched_properties;
+ struct vpu_ipc_msg_payload_hws_set_scheduling_log hws_set_scheduling_log;
+ struct vpu_ipc_msg_payload_hws_scheduling_log_notification hws_scheduling_log_notification;
+ struct vpu_ipc_msg_payload_hws_suspend_cmdq hws_suspend_cmdq;
+ struct vpu_ipc_msg_payload_hws_resume_cmdq hws_resume_cmdq;
+ struct vpu_ipc_msg_payload_hws_resume_engine hws_resume_engine;
+ struct vpu_ipc_msg_payload_pwr_d0i3_enter pwr_d0i3_enter;
+ struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control;
+};
+
+/**
+ * Host <-> NPU IPC message base structure.
+ *
+ * NOTE: All instances of this object must be aligned on a 64B boundary
+ * to allow proper handling of VPU cache operations.
+ */
+struct vpu_jsm_msg {
+ /** Reserved */
+ u64 reserved_0;
+ /** Message type, see @ref vpu_ipc_msg_type. */
+ u32 type;
+ /** Buffer status, see @ref vpu_ipc_msg_status. */
+ u32 status;
+ /**
+ * Request ID, provided by the host in a request message and passed
+ * back by VPU in the response message.
+ */
+ u32 request_id;
+ /** Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */
+ u32 result;
+ u64 reserved_1;
+ /** Message payload depending on message type, see vpu_ipc_msg_payload union. */
+ union vpu_ipc_msg_payload payload;
+};
+
+#pragma pack(pop)
+
+#endif
+
+///@}
diff --git a/drivers/accel/qaic/Kconfig b/drivers/accel/qaic/Kconfig
new file mode 100644
index 000000000000..116e42d152ca
--- /dev/null
+++ b/drivers/accel/qaic/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Qualcomm Cloud AI accelerators driver
+#
+
+config DRM_ACCEL_QAIC
+ tristate "Qualcomm Cloud AI accelerators"
+ depends on DRM_ACCEL
+ depends on PCI && HAS_IOMEM
+ depends on MHI_BUS
+ select CRC32
+ select WANT_DEV_COREDUMP
+ help
+ Enables driver for Qualcomm's Cloud AI accelerator PCIe cards that are
+ designed to accelerate Deep Learning inference workloads.
+
+ The driver manages the PCIe devices and provides an IOCTL interface
+ for users to submit workloads to the devices.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qaic.
diff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile
new file mode 100644
index 000000000000..71f727b74da3
--- /dev/null
+++ b/drivers/accel/qaic/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for Qualcomm Cloud AI accelerators driver
+#
+
+obj-$(CONFIG_DRM_ACCEL_QAIC) := qaic.o
+
+qaic-y := \
+ mhi_controller.o \
+ qaic_control.o \
+ qaic_data.o \
+ qaic_drv.o \
+ qaic_ras.o \
+ qaic_ssr.o \
+ qaic_sysfs.o \
+ qaic_timesync.o \
+ sahara.o
+
+qaic-$(CONFIG_DEBUG_FS) += qaic_debugfs.o
diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
new file mode 100644
index 000000000000..13a14c6c6168
--- /dev/null
+++ b/drivers/accel/qaic/mhi_controller.c
@@ -0,0 +1,976 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/memblock.h>
+#include <linux/mhi.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+
+#include "mhi_controller.h"
+#include "qaic.h"
+
+#define MAX_RESET_TIME_SEC 25
+
+static unsigned int mhi_timeout_ms = 2000; /* 2 sec default */
+module_param(mhi_timeout_ms, uint, 0600);
+MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value");
+
+static const char *fw_image_paths[FAMILY_MAX] = {
+ [FAMILY_AIC100] = "qcom/aic100/sbl.bin",
+ [FAMILY_AIC200] = "qcom/aic200/sbl.bin",
+};
+
+static const struct mhi_channel_config aic100_channels[] = {
+ {
+ .name = "QAIC_LOOPBACK",
+ .num = 0,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_LOOPBACK",
+ .num = 1,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SAHARA",
+ .num = 2,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SAHARA",
+ .num = 3,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_DIAG",
+ .num = 4,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_DIAG",
+ .num = 5,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SSR",
+ .num = 6,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SSR",
+ .num = 7,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_QDSS",
+ .num = 8,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_QDSS",
+ .num = 9,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_CONTROL",
+ .num = 10,
+ .num_elements = 128,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_CONTROL",
+ .num = 11,
+ .num_elements = 128,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_LOGGING",
+ .num = 12,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_LOGGING",
+ .num = 13,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_STATUS",
+ .num = 14,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_STATUS",
+ .num = 15,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TELEMETRY",
+ .num = 16,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TELEMETRY",
+ .num = 17,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_DEBUG",
+ .num = 18,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_DEBUG",
+ .num = 19,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TIMESYNC",
+ .num = 20,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TIMESYNC",
+ .num = 21,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TIMESYNC_PERIODIC",
+ .num = 22,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TIMESYNC_PERIODIC",
+ .num = 23,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "IPCR",
+ .num = 24,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "IPCR",
+ .num = 25,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = true,
+ .wake_capable = false,
+ },
+};
+
+static const struct mhi_channel_config aic200_channels[] = {
+ {
+ .name = "QAIC_LOOPBACK",
+ .num = 0,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_LOOPBACK",
+ .num = 1,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SAHARA",
+ .num = 2,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SAHARA",
+ .num = 3,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SSR",
+ .num = 6,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_SSR",
+ .num = 7,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_CONTROL",
+ .num = 10,
+ .num_elements = 128,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_CONTROL",
+ .num = 11,
+ .num_elements = 128,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_LOGGING",
+ .num = 12,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_LOGGING",
+ .num = 13,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_SBL,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_STATUS",
+ .num = 14,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_STATUS",
+ .num = 15,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TELEMETRY",
+ .num = 16,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TELEMETRY",
+ .num = 17,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TIMESYNC_PERIODIC",
+ .num = 22,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "QAIC_TIMESYNC_PERIODIC",
+ .num = 23,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "IPCR",
+ .num = 24,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_TO_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = false,
+ .wake_capable = false,
+ },
+ {
+ .name = "IPCR",
+ .num = 25,
+ .num_elements = 32,
+ .local_elements = 0,
+ .event_ring = 0,
+ .dir = DMA_FROM_DEVICE,
+ .ee_mask = MHI_CH_EE_AMSS,
+ .pollcfg = 0,
+ .doorbell = MHI_DB_BRST_DISABLE,
+ .lpm_notify = false,
+ .offload_channel = false,
+ .doorbell_mode_switch = false,
+ .auto_queue = true,
+ .wake_capable = false,
+ },
+};
+
+static struct mhi_event_config aic100_events[] = {
+ {
+ .num_elements = 32,
+ .irq_moderation_ms = 0,
+ .irq = 0,
+ .channel = U32_MAX,
+ .priority = 1,
+ .mode = MHI_DB_BRST_DISABLE,
+ .data_type = MHI_ER_CTRL,
+ .hardware_event = false,
+ .client_managed = false,
+ .offload_channel = false,
+ },
+};
+
+static struct mhi_event_config aic200_events[] = {
+ {
+ .num_elements = 32,
+ .irq_moderation_ms = 0,
+ .irq = 0,
+ .channel = U32_MAX,
+ .priority = 1,
+ .mode = MHI_DB_BRST_DISABLE,
+ .data_type = MHI_ER_CTRL,
+ .hardware_event = false,
+ .client_managed = false,
+ .offload_channel = false,
+ },
+};
+
+static struct mhi_controller_config mhi_cntrl_configs[] = {
+ [FAMILY_AIC100] = {
+ .max_channels = 128,
+ .timeout_ms = 0, /* controlled by mhi_timeout */
+ .buf_len = 0,
+ .num_channels = ARRAY_SIZE(aic100_channels),
+ .ch_cfg = aic100_channels,
+ .num_events = ARRAY_SIZE(aic100_events),
+ .event_cfg = aic100_events,
+ .use_bounce_buf = false,
+ .m2_no_db = false,
+ },
+ [FAMILY_AIC200] = {
+ .max_channels = 128,
+ .timeout_ms = 0, /* controlled by mhi_timeout */
+ .buf_len = 0,
+ .num_channels = ARRAY_SIZE(aic200_channels),
+ .ch_cfg = aic200_channels,
+ .num_events = ARRAY_SIZE(aic200_events),
+ .event_cfg = aic200_events,
+ .use_bounce_buf = false,
+ .m2_no_db = false,
+ },
+};
+
+static int mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 *out)
+{
+ u32 tmp;
+
+ /*
+ * SOC_HW_VERSION quirk
+ * The SOC_HW_VERSION register (offset 0x224) is not reliable and
+ * may contain uninitialized values, including 0xFFFFFFFF. This could
+ * cause a false positive link down error. Instead, intercept any
+ * reads and provide the correct value of the register.
+ */
+ if (addr - mhi_cntrl->regs == 0x224) {
+ *out = 0x60110200;
+ return 0;
+ }
+
+ tmp = readl_relaxed(addr);
+ if (tmp == U32_MAX)
+ return -EIO;
+
+ *out = tmp;
+
+ return 0;
+}
+
+static void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 val)
+{
+ writel_relaxed(val, addr);
+}
+
+static int mhi_runtime_get(struct mhi_controller *mhi_cntrl)
+{
+ return 0;
+}
+
+static void mhi_runtime_put(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static void mhi_status_cb(struct mhi_controller *mhi_cntrl, enum mhi_callback reason)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_cntrl->cntrl_dev));
+
+ /* this event occurs in atomic context */
+ if (reason == MHI_CB_FATAL_ERROR)
+ pci_err(qdev->pdev, "Fatal error received from device. Attempting to recover\n");
+ /* this event occurs in non-atomic context */
+ if (reason == MHI_CB_SYS_ERROR)
+ qaic_dev_reset_clean_local_state(qdev);
+}
+
+static int mhi_reset_and_async_power_up(struct mhi_controller *mhi_cntrl)
+{
+ u8 time_sec = 1;
+ int current_ee;
+ int ret;
+
+ /* Reset the device to bring the device in PBL EE */
+ mhi_soc_reset(mhi_cntrl);
+
+ /*
+ * Keep checking the execution environment(EE) after every 1 second
+ * interval.
+ */
+ do {
+ msleep(1000);
+ current_ee = mhi_get_exec_env(mhi_cntrl);
+ } while (current_ee != MHI_EE_PBL && time_sec++ <= MAX_RESET_TIME_SEC);
+
+ /* If the device is in PBL EE retry power up */
+ if (current_ee == MHI_EE_PBL)
+ ret = mhi_async_power_up(mhi_cntrl);
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar,
+ int mhi_irq, bool shared_msi, int family)
+{
+ struct mhi_controller_config mhi_config = mhi_cntrl_configs[family];
+ struct mhi_controller *mhi_cntrl;
+ int ret;
+
+ mhi_cntrl = devm_kzalloc(&pci_dev->dev, sizeof(*mhi_cntrl), GFP_KERNEL);
+ if (!mhi_cntrl)
+ return ERR_PTR(-ENOMEM);
+
+ mhi_cntrl->cntrl_dev = &pci_dev->dev;
+
+ /*
+ * Covers the entire possible physical ram region. Remote side is
+ * going to calculate a size of this range, so subtract 1 to prevent
+ * rollover.
+ */
+ mhi_cntrl->iova_start = 0;
+ mhi_cntrl->iova_stop = PHYS_ADDR_MAX - 1;
+ mhi_cntrl->status_cb = mhi_status_cb;
+ mhi_cntrl->runtime_get = mhi_runtime_get;
+ mhi_cntrl->runtime_put = mhi_runtime_put;
+ mhi_cntrl->read_reg = mhi_read_reg;
+ mhi_cntrl->write_reg = mhi_write_reg;
+ mhi_cntrl->regs = mhi_bar;
+ mhi_cntrl->reg_len = SZ_4K;
+ mhi_cntrl->nr_irqs = 1;
+ mhi_cntrl->irq = devm_kmalloc(&pci_dev->dev, sizeof(*mhi_cntrl->irq), GFP_KERNEL);
+
+ if (!mhi_cntrl->irq)
+ return ERR_PTR(-ENOMEM);
+
+ mhi_cntrl->irq[0] = mhi_irq;
+
+ if (shared_msi) /* MSI shared with data path, no IRQF_NO_SUSPEND */
+ mhi_cntrl->irq_flags = IRQF_SHARED;
+
+ mhi_cntrl->fw_image = fw_image_paths[family];
+
+ if (family == FAMILY_AIC200) {
+ mhi_cntrl->name = "AIC200";
+ mhi_cntrl->seg_len = SZ_512K;
+ } else {
+ mhi_cntrl->name = "AIC100";
+ }
+
+ /* use latest configured timeout */
+ mhi_config.timeout_ms = mhi_timeout_ms;
+ ret = mhi_register_controller(mhi_cntrl, &mhi_config);
+ if (ret) {
+ pci_err(pci_dev, "mhi_register_controller failed %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ ret = mhi_prepare_for_power_up(mhi_cntrl);
+ if (ret) {
+ pci_err(pci_dev, "mhi_prepare_for_power_up failed %d\n", ret);
+ goto prepare_power_up_fail;
+ }
+
+ ret = mhi_async_power_up(mhi_cntrl);
+ /*
+ * If EIO is returned it is possible that device is in SBL EE, which is
+ * undesired. SOC reset the device and try to power up again.
+ */
+ if (ret == -EIO && MHI_EE_SBL == mhi_get_exec_env(mhi_cntrl)) {
+ pci_err(pci_dev, "Found device in SBL at MHI init. Attempting a reset.\n");
+ ret = mhi_reset_and_async_power_up(mhi_cntrl);
+ }
+
+ if (ret) {
+ pci_err(pci_dev, "mhi_async_power_up failed %d\n", ret);
+ goto power_up_fail;
+ }
+
+ return mhi_cntrl;
+
+power_up_fail:
+ mhi_unprepare_after_power_down(mhi_cntrl);
+prepare_power_up_fail:
+ mhi_unregister_controller(mhi_cntrl);
+ return ERR_PTR(ret);
+}
+
+void qaic_mhi_free_controller(struct mhi_controller *mhi_cntrl, bool link_up)
+{
+ mhi_power_down(mhi_cntrl, link_up);
+ mhi_unprepare_after_power_down(mhi_cntrl);
+ mhi_unregister_controller(mhi_cntrl);
+}
+
+void qaic_mhi_start_reset(struct mhi_controller *mhi_cntrl)
+{
+ mhi_power_down(mhi_cntrl, true);
+}
+
+void qaic_mhi_reset_done(struct mhi_controller *mhi_cntrl)
+{
+ struct pci_dev *pci_dev = container_of(mhi_cntrl->cntrl_dev, struct pci_dev, dev);
+ int ret;
+
+ ret = mhi_async_power_up(mhi_cntrl);
+ if (ret)
+ pci_err(pci_dev, "mhi_async_power_up failed after reset %d\n", ret);
+}
diff --git a/drivers/accel/qaic/mhi_controller.h b/drivers/accel/qaic/mhi_controller.h
new file mode 100644
index 000000000000..8939f6ae185e
--- /dev/null
+++ b/drivers/accel/qaic/mhi_controller.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef MHICONTROLLERQAIC_H_
+#define MHICONTROLLERQAIC_H_
+
+struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar,
+ int mhi_irq, bool shared_msi, int family);
+void qaic_mhi_free_controller(struct mhi_controller *mhi_cntrl, bool link_up);
+void qaic_mhi_start_reset(struct mhi_controller *mhi_cntrl);
+void qaic_mhi_reset_done(struct mhi_controller *mhi_cntrl);
+
+#endif /* MHICONTROLLERQAIC_H_ */
diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h
new file mode 100644
index 000000000000..fa7a8155658c
--- /dev/null
+++ b/drivers/accel/qaic/qaic.h
@@ -0,0 +1,360 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _QAIC_H_
+#define _QAIC_H_
+
+#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/mhi.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/srcu.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
+
+#define QAIC_DBC_BASE SZ_128K
+#define QAIC_DBC_SIZE SZ_4K
+#define QAIC_SSR_DBC_SENTINEL U32_MAX /* No ongoing SSR sentinel */
+
+#define QAIC_NO_PARTITION -1
+
+#define QAIC_DBC_OFF(i) ((i) * QAIC_DBC_SIZE + QAIC_DBC_BASE)
+
+#define to_qaic_bo(obj) container_of(obj, struct qaic_bo, base)
+#define to_qaic_drm_device(dev) container_of(dev, struct qaic_drm_device, drm)
+#define to_drm(qddev) (&(qddev)->drm)
+#define to_accel_kdev(qddev) (to_drm(qddev)->accel->kdev) /* Return Linux device of accel node */
+#define to_qaic_device(dev) (to_qaic_drm_device((dev))->qdev)
+
+enum aic_families {
+ FAMILY_AIC100,
+ FAMILY_AIC200,
+ FAMILY_MAX,
+};
+
+enum __packed dev_states {
+ /* Device is offline or will be very soon */
+ QAIC_OFFLINE,
+ /* Device is booting, not clear if it's in a usable state */
+ QAIC_BOOT,
+ /* Device is fully operational */
+ QAIC_ONLINE,
+};
+
+enum dbc_states {
+ /* DBC is free and can be activated */
+ DBC_STATE_IDLE,
+ /* DBC is activated and a workload is running on device */
+ DBC_STATE_ASSIGNED,
+ /* Sub-system associated with this workload has crashed and it will shutdown soon */
+ DBC_STATE_BEFORE_SHUTDOWN,
+ /* Sub-system associated with this workload has crashed and it has shutdown */
+ DBC_STATE_AFTER_SHUTDOWN,
+ /* Sub-system associated with this workload is shutdown and it will be powered up soon */
+ DBC_STATE_BEFORE_POWER_UP,
+ /* Sub-system associated with this workload is now powered up */
+ DBC_STATE_AFTER_POWER_UP,
+ DBC_STATE_MAX,
+};
+
+extern bool datapath_polling;
+
+struct qaic_user {
+ /* Uniquely identifies this user for the device */
+ int handle;
+ struct kref ref_count;
+ /* Char device opened by this user */
+ struct qaic_drm_device *qddev;
+ /* Node in list of users that opened this drm device */
+ struct list_head node;
+ /* SRCU used to synchronize this user during cleanup */
+ struct srcu_struct qddev_lock;
+ atomic_t chunk_id;
+};
+
+struct dma_bridge_chan {
+ /* Pointer to device strcut maintained by driver */
+ struct qaic_device *qdev;
+ /* ID of this DMA bridge channel(DBC) */
+ unsigned int id;
+ /* Synchronizes access to xfer_list */
+ spinlock_t xfer_lock;
+ /* Base address of request queue */
+ void *req_q_base;
+ /* Base address of response queue */
+ void *rsp_q_base;
+ /*
+ * Base bus address of request queue. Response queue bus address can be
+ * calculated by adding request queue size to this variable
+ */
+ dma_addr_t dma_addr;
+ /* Total size of request and response queue in byte */
+ u32 total_size;
+ /* Capacity of request/response queue */
+ u32 nelem;
+ /* The user that opened this DBC */
+ struct qaic_user *usr;
+ /*
+ * Request ID of next memory handle that goes in request queue. One
+ * memory handle can enqueue more than one request elements, all
+ * this requests that belong to same memory handle have same request ID
+ */
+ u16 next_req_id;
+ /* true: DBC is in use; false: DBC not in use */
+ bool in_use;
+ /*
+ * Base address of device registers. Used to read/write request and
+ * response queue's head and tail pointer of this DBC.
+ */
+ void __iomem *dbc_base;
+ /* Synchronizes access to Request queue's head and tail pointer */
+ struct mutex req_lock;
+ /* Head of list where each node is a memory handle queued in request queue */
+ struct list_head xfer_list;
+ /* Synchronizes DBC readers during cleanup */
+ struct srcu_struct ch_lock;
+ /*
+ * When this DBC is released, any thread waiting on this wait queue is
+ * woken up
+ */
+ wait_queue_head_t dbc_release;
+ /* Head of list where each node is a bo associated with this DBC */
+ struct list_head bo_lists;
+ /* The irq line for this DBC. Used for polling */
+ unsigned int irq;
+ /* Polling work item to simulate interrupts */
+ struct work_struct poll_work;
+ /* Represents various states of this DBC from enum dbc_states */
+ unsigned int state;
+};
+
+struct qaic_device {
+ /* Pointer to base PCI device struct of our physical device */
+ struct pci_dev *pdev;
+ /* Req. ID of request that will be queued next in MHI control device */
+ u32 next_seq_num;
+ /* Base address of the MHI bar */
+ void __iomem *bar_mhi;
+ /* Base address of the DBCs bar */
+ void __iomem *bar_dbc;
+ /* Controller structure for MHI devices */
+ struct mhi_controller *mhi_cntrl;
+ /* MHI control channel device */
+ struct mhi_device *cntl_ch;
+ /* List of requests queued in MHI control device */
+ struct list_head cntl_xfer_list;
+ /* Synchronizes MHI control device transactions and its xfer list */
+ struct mutex cntl_mutex;
+ /* Array of DBC struct of this device */
+ struct dma_bridge_chan *dbc;
+ /* Work queue for tasks related to MHI control device */
+ struct workqueue_struct *cntl_wq;
+ /* Synchronizes all the users of device during cleanup */
+ struct srcu_struct dev_lock;
+ /* Track the state of the device during resets */
+ enum dev_states dev_state;
+ /* true: single MSI is used to operate device */
+ bool single_msi;
+ /*
+ * true: A tx MHI transaction has failed and a rx buffer is still queued
+ * in control device. Such a buffer is considered lost rx buffer
+ * false: No rx buffer is lost in control device
+ */
+ bool cntl_lost_buf;
+ /* Maximum number of DBC supported by this device */
+ u32 num_dbc;
+ /* Reference to the drm_device for this device when it is created */
+ struct qaic_drm_device *qddev;
+ /* Generate the CRC of a control message */
+ u32 (*gen_crc)(void *msg);
+ /* Validate the CRC of a control message */
+ bool (*valid_crc)(void *msg);
+ /* MHI "QAIC_TIMESYNC" channel device */
+ struct mhi_device *qts_ch;
+ /* Work queue for tasks related to MHI "QAIC_TIMESYNC" channel */
+ struct workqueue_struct *qts_wq;
+ /* MHI "QAIC_TIMESYNC_PERIODIC" channel device */
+ struct mhi_device *mqts_ch;
+ /* Head of list of page allocated by MHI bootlog device */
+ struct list_head bootlog;
+ /* MHI bootlog channel device */
+ struct mhi_device *bootlog_ch;
+ /* Work queue for tasks related to MHI bootlog device */
+ struct workqueue_struct *bootlog_wq;
+ /* Synchronizes access of pages in MHI bootlog device */
+ struct mutex bootlog_mutex;
+ /* MHI RAS channel device */
+ struct mhi_device *ras_ch;
+ /* Correctable error count */
+ unsigned int ce_count;
+ /* Un-correctable error count */
+ unsigned int ue_count;
+ /* Un-correctable non-fatal error count */
+ unsigned int ue_nf_count;
+ /* MHI SSR channel device */
+ struct mhi_device *ssr_ch;
+ /* Work queue for tasks related to MHI SSR device */
+ struct workqueue_struct *ssr_wq;
+ /* Buffer to collect SSR crashdump via SSR MHI channel */
+ void *ssr_mhi_buf;
+ /* DBC which is under SSR. Sentinel U32_MAX would mean that no SSR in progress */
+ u32 ssr_dbc;
+};
+
+struct qaic_drm_device {
+ /* The drm device struct of this drm device */
+ struct drm_device drm;
+ /* Pointer to the root device struct driven by this driver */
+ struct qaic_device *qdev;
+ /*
+ * The physical device can be partition in number of logical devices.
+ * And each logical device is given a partition id. This member stores
+ * that id. QAIC_NO_PARTITION is a sentinel used to mark that this drm
+ * device is the actual physical device
+ */
+ s32 partition_id;
+ /* Head in list of users who have opened this drm device */
+ struct list_head users;
+ /* Synchronizes access to users list */
+ struct mutex users_mutex;
+ /* Pointer to array of DBC sysfs attributes */
+ void *sysfs_attrs;
+};
+
+struct qaic_bo {
+ struct drm_gem_object base;
+ /* Scatter/gather table for allocate/imported BO */
+ struct sg_table *sgt;
+ /* Head in list of slices of this BO */
+ struct list_head slices;
+ /* Total nents, for all slices of this BO */
+ int total_slice_nents;
+ /*
+ * Direction of transfer. It can assume only two value DMA_TO_DEVICE and
+ * DMA_FROM_DEVICE.
+ */
+ int dir;
+ /* The pointer of the DBC which operates on this BO */
+ struct dma_bridge_chan *dbc;
+ /* Number of slice that belongs to this buffer */
+ u32 nr_slice;
+ /* Number of slice that have been transferred by DMA engine */
+ u32 nr_slice_xfer_done;
+ /*
+ * If true then user has attached slicing information to this BO by
+ * calling DRM_IOCTL_QAIC_ATTACH_SLICE_BO ioctl.
+ */
+ bool sliced;
+ /* Request ID of this BO if it is queued for execution */
+ u16 req_id;
+ /* Wait on this for completion of DMA transfer of this BO */
+ struct completion xfer_done;
+ /*
+ * Node in linked list where head is dbc->xfer_list.
+ * This link list contain BO's that are queued for DMA transfer.
+ */
+ struct list_head xfer_list;
+ /*
+ * Node in linked list where head is dbc->bo_lists.
+ * This link list contain BO's that are associated with the DBC it is
+ * linked to.
+ */
+ struct list_head bo_list;
+ struct {
+ /*
+ * Latest timestamp(ns) at which kernel received a request to
+ * execute this BO
+ */
+ u64 req_received_ts;
+ /*
+ * Latest timestamp(ns) at which kernel enqueued requests of
+ * this BO for execution in DMA queue
+ */
+ u64 req_submit_ts;
+ /*
+ * Latest timestamp(ns) at which kernel received a completion
+ * interrupt for requests of this BO
+ */
+ u64 req_processed_ts;
+ /*
+ * Number of elements already enqueued in DMA queue before
+ * enqueuing requests of this BO
+ */
+ u32 queue_level_before;
+ } perf_stats;
+ /* Synchronizes BO operations */
+ struct mutex lock;
+};
+
+struct bo_slice {
+ /* Mapped pages */
+ struct sg_table *sgt;
+ /* Number of requests required to queue in DMA queue */
+ int nents;
+ /* See enum dma_data_direction */
+ int dir;
+ /* Actual requests that will be copied in DMA queue */
+ struct dbc_req *reqs;
+ struct kref ref_count;
+ /* true: No DMA transfer required */
+ bool no_xfer;
+ /* Pointer to the parent BO handle */
+ struct qaic_bo *bo;
+ /* Node in list of slices maintained by parent BO */
+ struct list_head slice;
+ /* Size of this slice in bytes */
+ u64 size;
+ /* Offset of this slice in buffer */
+ u64 offset;
+};
+
+int get_dbc_req_elem_size(void);
+int get_dbc_rsp_elem_size(void);
+int get_cntl_version(struct qaic_device *qdev, struct qaic_user *usr, u16 *major, u16 *minor);
+int qaic_manage_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+void qaic_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result);
+
+void qaic_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result);
+
+int qaic_control_open(struct qaic_device *qdev);
+void qaic_control_close(struct qaic_device *qdev);
+void qaic_release_usr(struct qaic_device *qdev, struct qaic_user *usr);
+
+irqreturn_t dbc_irq_threaded_fn(int irq, void *data);
+irqreturn_t dbc_irq_handler(int irq, void *data);
+int disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr);
+void enable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr);
+void wakeup_dbc(struct qaic_device *qdev, u32 dbc_id);
+void release_dbc(struct qaic_device *qdev, u32 dbc_id);
+void qaic_data_get_fifo_info(struct dma_bridge_chan *dbc, u32 *head, u32 *tail);
+
+void wake_all_cntl(struct qaic_device *qdev);
+void qaic_dev_reset_clean_local_state(struct qaic_device *qdev);
+
+struct drm_gem_object *qaic_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf);
+
+int qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qaic_detach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+void qaic_irq_polling_work(struct work_struct *work);
+void qaic_dbc_enter_ssr(struct qaic_device *qdev, u32 dbc_id);
+void qaic_dbc_exit_ssr(struct qaic_device *qdev);
+
+/* qaic_sysfs.c */
+int qaic_sysfs_init(struct qaic_drm_device *qddev);
+void qaic_sysfs_remove(struct qaic_drm_device *qddev);
+void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state);
+
+#endif /* _QAIC_H_ */
diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
new file mode 100644
index 000000000000..428d8f65bff3
--- /dev/null
+++ b/drivers/accel/qaic/qaic_control.c
@@ -0,0 +1,1560 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <asm/byteorder.h>
+#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/mhi.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+#include <linux/sched/signal.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <uapi/drm/qaic_accel.h>
+
+#include "qaic.h"
+
+#define MANAGE_MAGIC_NUMBER ((__force __le32)0x43494151) /* "QAIC" in little endian */
+#define QAIC_DBC_Q_GAP SZ_256
+#define QAIC_DBC_Q_BUF_ALIGN SZ_4K
+#define QAIC_MANAGE_WIRE_MSG_LENGTH SZ_64K /* Max DMA message length */
+#define QAIC_WRAPPER_MAX_SIZE SZ_4K
+#define QAIC_MHI_RETRY_WAIT_MS 100
+#define QAIC_MHI_RETRY_MAX 20
+
+static unsigned int control_resp_timeout_s = 60; /* 60 sec default */
+module_param(control_resp_timeout_s, uint, 0600);
+MODULE_PARM_DESC(control_resp_timeout_s, "Timeout for NNC responses from QSM");
+
+struct manage_msg {
+ u32 len;
+ u32 count;
+ u8 data[];
+};
+
+/*
+ * wire encoding structures for the manage protocol.
+ * All fields are little endian on the wire
+ */
+struct wire_msg_hdr {
+ __le32 crc32; /* crc of everything following this field in the message */
+ __le32 magic_number;
+ __le32 sequence_number;
+ __le32 len; /* length of this message */
+ __le32 count; /* number of transactions in this message */
+ __le32 handle; /* unique id to track the resources consumed */
+ __le32 partition_id; /* partition id for the request (signed) */
+ __le32 padding; /* must be 0 */
+} __packed;
+
+struct wire_msg {
+ struct wire_msg_hdr hdr;
+ u8 data[];
+} __packed;
+
+struct wire_trans_hdr {
+ __le32 type;
+ __le32 len;
+} __packed;
+
+/* Each message sent from driver to device are organized in a list of wrapper_msg */
+struct wrapper_msg {
+ struct list_head list;
+ struct kref ref_count;
+ u32 len; /* length of data to transfer */
+ struct wrapper_list *head;
+ union {
+ struct wire_msg msg;
+ struct wire_trans_hdr trans;
+ };
+};
+
+struct wrapper_list {
+ struct list_head list;
+ spinlock_t lock; /* Protects the list state during additions and removals */
+};
+
+struct wire_trans_passthrough {
+ struct wire_trans_hdr hdr;
+ u8 data[];
+} __packed;
+
+struct wire_addr_size_pair {
+ __le64 addr;
+ __le64 size;
+} __packed;
+
+struct wire_trans_dma_xfer {
+ struct wire_trans_hdr hdr;
+ __le32 tag;
+ __le32 count;
+ __le32 dma_chunk_id;
+ __le32 padding;
+ struct wire_addr_size_pair data[];
+} __packed;
+
+/* Initiated by device to continue the DMA xfer of a large piece of data */
+struct wire_trans_dma_xfer_cont {
+ struct wire_trans_hdr hdr;
+ __le32 dma_chunk_id;
+ __le32 padding;
+ __le64 xferred_size;
+} __packed;
+
+struct wire_trans_activate_to_dev {
+ struct wire_trans_hdr hdr;
+ __le64 req_q_addr;
+ __le64 rsp_q_addr;
+ __le32 req_q_size;
+ __le32 rsp_q_size;
+ __le32 buf_len;
+ __le32 options; /* unused, but BIT(16) has meaning to the device */
+} __packed;
+
+struct wire_trans_activate_from_dev {
+ struct wire_trans_hdr hdr;
+ __le32 status;
+ __le32 dbc_id;
+ __le64 options; /* unused */
+} __packed;
+
+struct wire_trans_deactivate_from_dev {
+ struct wire_trans_hdr hdr;
+ __le32 status;
+ __le32 dbc_id;
+} __packed;
+
+struct wire_trans_terminate_to_dev {
+ struct wire_trans_hdr hdr;
+ __le32 handle;
+ __le32 padding;
+} __packed;
+
+struct wire_trans_terminate_from_dev {
+ struct wire_trans_hdr hdr;
+ __le32 status;
+ __le32 padding;
+} __packed;
+
+struct wire_trans_status_to_dev {
+ struct wire_trans_hdr hdr;
+} __packed;
+
+struct wire_trans_status_from_dev {
+ struct wire_trans_hdr hdr;
+ __le16 major;
+ __le16 minor;
+ __le32 status;
+ __le64 status_flags;
+} __packed;
+
+struct wire_trans_validate_part_to_dev {
+ struct wire_trans_hdr hdr;
+ __le32 part_id;
+ __le32 padding;
+} __packed;
+
+struct wire_trans_validate_part_from_dev {
+ struct wire_trans_hdr hdr;
+ __le32 status;
+ __le32 padding;
+} __packed;
+
+struct xfer_queue_elem {
+ /*
+ * Node in list of ongoing transfer request on control channel.
+ * Maintained by root device struct.
+ */
+ struct list_head list;
+ /* Sequence number of this transfer request */
+ u32 seq_num;
+ /* This is used to wait on until completion of transfer request */
+ struct completion xfer_done;
+ /* Received data from device */
+ void *buf;
+};
+
+struct dma_xfer {
+ /* Node in list of DMA transfers which is used for cleanup */
+ struct list_head list;
+ /* SG table of memory used for DMA */
+ struct sg_table *sgt;
+ /* Array pages used for DMA */
+ struct page **page_list;
+ /* Number of pages used for DMA */
+ unsigned long nr_pages;
+};
+
+struct ioctl_resources {
+ /* List of all DMA transfers which is used later for cleanup */
+ struct list_head dma_xfers;
+ /* Base address of request queue which belongs to a DBC */
+ void *buf;
+ /*
+ * Base bus address of request queue which belongs to a DBC. Response
+ * queue base bus address can be calculated by adding size of request
+ * queue to base bus address of request queue.
+ */
+ dma_addr_t dma_addr;
+ /* Total size of request queue and response queue in byte */
+ u32 total_size;
+ /* Total number of elements that can be queued in each of request and response queue */
+ u32 nelem;
+ /* Base address of response queue which belongs to a DBC */
+ void *rsp_q_base;
+ /* Status of the NNC message received */
+ u32 status;
+ /* DBC id of the DBC received from device */
+ u32 dbc_id;
+ /*
+ * DMA transfer request messages can be big in size and it may not be
+ * possible to send them in one shot. In such cases the messages are
+ * broken into chunks, this field stores ID of such chunks.
+ */
+ u32 dma_chunk_id;
+ /* Total number of bytes transferred for a DMA xfer request */
+ u64 xferred_dma_size;
+ /* Header of transaction message received from user. Used during DMA xfer request. */
+ void *trans_hdr;
+};
+
+struct resp_work {
+ struct work_struct work;
+ struct qaic_device *qdev;
+ void *buf;
+};
+
+/*
+ * Since we're working with little endian messages, its useful to be able to
+ * increment without filling a whole line with conversions back and forth just
+ * to add one(1) to a message count.
+ */
+static __le32 incr_le32(__le32 val)
+{
+ return cpu_to_le32(le32_to_cpu(val) + 1);
+}
+
+static u32 gen_crc(void *msg)
+{
+ struct wrapper_list *wrappers = msg;
+ struct wrapper_msg *w;
+ u32 crc = ~0;
+
+ list_for_each_entry(w, &wrappers->list, list)
+ crc = crc32(crc, &w->msg, w->len);
+
+ return crc ^ ~0;
+}
+
+static u32 gen_crc_stub(void *msg)
+{
+ return 0;
+}
+
+static bool valid_crc(void *msg)
+{
+ struct wire_msg_hdr *hdr = msg;
+ bool ret;
+ u32 crc;
+
+ /*
+ * The output of this algorithm is always converted to the native
+ * endianness.
+ */
+ crc = le32_to_cpu(hdr->crc32);
+ hdr->crc32 = 0;
+ ret = (crc32(~0, msg, le32_to_cpu(hdr->len)) ^ ~0) == crc;
+ hdr->crc32 = cpu_to_le32(crc);
+ return ret;
+}
+
+static bool valid_crc_stub(void *msg)
+{
+ return true;
+}
+
+static void free_wrapper(struct kref *ref)
+{
+ struct wrapper_msg *wrapper = container_of(ref, struct wrapper_msg, ref_count);
+
+ list_del(&wrapper->list);
+ kfree(wrapper);
+}
+
+static void save_dbc_buf(struct qaic_device *qdev, struct ioctl_resources *resources,
+ struct qaic_user *usr)
+{
+ u32 dbc_id = resources->dbc_id;
+
+ if (resources->buf) {
+ wait_event_interruptible(qdev->dbc[dbc_id].dbc_release, !qdev->dbc[dbc_id].in_use);
+ qdev->dbc[dbc_id].req_q_base = resources->buf;
+ qdev->dbc[dbc_id].rsp_q_base = resources->rsp_q_base;
+ qdev->dbc[dbc_id].dma_addr = resources->dma_addr;
+ qdev->dbc[dbc_id].total_size = resources->total_size;
+ qdev->dbc[dbc_id].nelem = resources->nelem;
+ enable_dbc(qdev, dbc_id, usr);
+ qdev->dbc[dbc_id].in_use = true;
+ resources->buf = NULL;
+ set_dbc_state(qdev, dbc_id, DBC_STATE_ASSIGNED);
+ }
+}
+
+static void free_dbc_buf(struct qaic_device *qdev, struct ioctl_resources *resources)
+{
+ if (resources->buf)
+ dma_free_coherent(&qdev->pdev->dev, resources->total_size, resources->buf,
+ resources->dma_addr);
+ resources->buf = NULL;
+}
+
+static void free_dma_xfers(struct qaic_device *qdev, struct ioctl_resources *resources)
+{
+ struct dma_xfer *xfer;
+ struct dma_xfer *x;
+ int i;
+
+ list_for_each_entry_safe(xfer, x, &resources->dma_xfers, list) {
+ dma_unmap_sgtable(&qdev->pdev->dev, xfer->sgt, DMA_TO_DEVICE, 0);
+ sg_free_table(xfer->sgt);
+ kfree(xfer->sgt);
+ for (i = 0; i < xfer->nr_pages; ++i)
+ put_page(xfer->page_list[i]);
+ kfree(xfer->page_list);
+ list_del(&xfer->list);
+ kfree(xfer);
+ }
+}
+
+static struct wrapper_msg *add_wrapper(struct wrapper_list *wrappers, u32 size)
+{
+ struct wrapper_msg *w = kzalloc(size, GFP_KERNEL);
+
+ if (!w)
+ return NULL;
+ list_add_tail(&w->list, &wrappers->list);
+ kref_init(&w->ref_count);
+ w->head = wrappers;
+ return w;
+}
+
+static int encode_passthrough(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
+ u32 *user_len)
+{
+ struct qaic_manage_trans_passthrough *in_trans = trans;
+ struct wire_trans_passthrough *out_trans;
+ struct wrapper_msg *trans_wrapper;
+ struct wrapper_msg *wrapper;
+ struct wire_msg *msg;
+ u32 msg_hdr_len;
+
+ wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
+ msg = &wrapper->msg;
+ msg_hdr_len = le32_to_cpu(msg->hdr.len);
+
+ if (in_trans->hdr.len % 8 != 0)
+ return -EINVAL;
+
+ if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_WIRE_MSG_LENGTH)
+ return -ENOSPC;
+
+ trans_wrapper = add_wrapper(wrappers,
+ offsetof(struct wrapper_msg, trans) + in_trans->hdr.len);
+ if (!trans_wrapper)
+ return -ENOMEM;
+ trans_wrapper->len = in_trans->hdr.len;
+ out_trans = (struct wire_trans_passthrough *)&trans_wrapper->trans;
+
+ memcpy(out_trans->data, in_trans->data, in_trans->hdr.len - sizeof(in_trans->hdr));
+ msg->hdr.len = cpu_to_le32(msg_hdr_len + in_trans->hdr.len);
+ msg->hdr.count = incr_le32(msg->hdr.count);
+ *user_len += in_trans->hdr.len;
+ out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_PASSTHROUGH_TO_DEV);
+ out_trans->hdr.len = cpu_to_le32(in_trans->hdr.len);
+
+ return 0;
+}
+
+/* returns error code for failure, 0 if enough pages alloc'd, 1 if dma_cont is needed */
+static int find_and_map_user_pages(struct qaic_device *qdev,
+ struct qaic_manage_trans_dma_xfer *in_trans,
+ struct ioctl_resources *resources, struct dma_xfer *xfer)
+{
+ u64 xfer_start_addr, remaining, end, total;
+ unsigned long need_pages;
+ struct page **page_list;
+ unsigned long nr_pages;
+ struct sg_table *sgt;
+ int ret;
+ int i;
+
+ if (check_add_overflow(in_trans->addr, resources->xferred_dma_size, &xfer_start_addr))
+ return -EINVAL;
+
+ if (in_trans->size < resources->xferred_dma_size)
+ return -EINVAL;
+ remaining = in_trans->size - resources->xferred_dma_size;
+ if (remaining == 0)
+ return -EINVAL;
+
+ if (check_add_overflow(xfer_start_addr, remaining, &end))
+ return -EINVAL;
+
+ total = remaining + offset_in_page(xfer_start_addr);
+ if (total >= SIZE_MAX)
+ return -EINVAL;
+
+ need_pages = DIV_ROUND_UP(total, PAGE_SIZE);
+
+ nr_pages = need_pages;
+
+ while (1) {
+ page_list = kmalloc_array(nr_pages, sizeof(*page_list), GFP_KERNEL | __GFP_NOWARN);
+ if (!page_list) {
+ nr_pages = nr_pages / 2;
+ if (!nr_pages)
+ return -ENOMEM;
+ } else {
+ break;
+ }
+ }
+
+ ret = get_user_pages_fast(xfer_start_addr, nr_pages, 0, page_list);
+ if (ret < 0)
+ goto free_page_list;
+ if (ret != nr_pages) {
+ nr_pages = ret;
+ ret = -EFAULT;
+ goto put_pages;
+ }
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ ret = -ENOMEM;
+ goto put_pages;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, page_list, nr_pages,
+ offset_in_page(xfer_start_addr),
+ remaining, GFP_KERNEL);
+ if (ret) {
+ ret = -ENOMEM;
+ goto free_sgt;
+ }
+
+ ret = dma_map_sgtable(&qdev->pdev->dev, sgt, DMA_TO_DEVICE, 0);
+ if (ret)
+ goto free_table;
+
+ xfer->sgt = sgt;
+ xfer->page_list = page_list;
+ xfer->nr_pages = nr_pages;
+
+ return need_pages > nr_pages ? 1 : 0;
+
+free_table:
+ sg_free_table(sgt);
+free_sgt:
+ kfree(sgt);
+put_pages:
+ for (i = 0; i < nr_pages; ++i)
+ put_page(page_list[i]);
+free_page_list:
+ kfree(page_list);
+ return ret;
+}
+
+/* returns error code for failure, 0 if everything was encoded, 1 if dma_cont is needed */
+static int encode_addr_size_pairs(struct dma_xfer *xfer, struct wrapper_list *wrappers,
+ struct ioctl_resources *resources, u32 msg_hdr_len, u32 *size,
+ struct wire_trans_dma_xfer **out_trans)
+{
+ struct wrapper_msg *trans_wrapper;
+ struct sg_table *sgt = xfer->sgt;
+ struct wire_addr_size_pair *asp;
+ struct scatterlist *sg;
+ struct wrapper_msg *w;
+ unsigned int dma_len;
+ u64 dma_chunk_len;
+ void *boundary;
+ int nents_dma;
+ int nents;
+ int i;
+
+ nents = sgt->nents;
+ nents_dma = nents;
+ *size = QAIC_MANAGE_WIRE_MSG_LENGTH - msg_hdr_len - sizeof(**out_trans);
+ for_each_sgtable_dma_sg(sgt, sg, i) {
+ *size -= sizeof(*asp);
+ /* Save 1K for possible follow-up transactions. */
+ if (*size < SZ_1K) {
+ nents_dma = i;
+ break;
+ }
+ }
+
+ trans_wrapper = add_wrapper(wrappers, QAIC_WRAPPER_MAX_SIZE);
+ if (!trans_wrapper)
+ return -ENOMEM;
+ *out_trans = (struct wire_trans_dma_xfer *)&trans_wrapper->trans;
+
+ asp = (*out_trans)->data;
+ boundary = (void *)trans_wrapper + QAIC_WRAPPER_MAX_SIZE;
+ *size = 0;
+
+ dma_len = 0;
+ w = trans_wrapper;
+ dma_chunk_len = 0;
+ for_each_sg(sgt->sgl, sg, nents_dma, i) {
+ asp->size = cpu_to_le64(dma_len);
+ dma_chunk_len += dma_len;
+ if (dma_len) {
+ asp++;
+ if ((void *)asp + sizeof(*asp) > boundary) {
+ w->len = (void *)asp - (void *)&w->msg;
+ *size += w->len;
+ w = add_wrapper(wrappers, QAIC_WRAPPER_MAX_SIZE);
+ if (!w)
+ return -ENOMEM;
+ boundary = (void *)w + QAIC_WRAPPER_MAX_SIZE;
+ asp = (struct wire_addr_size_pair *)&w->msg;
+ }
+ }
+ asp->addr = cpu_to_le64(sg_dma_address(sg));
+ dma_len = sg_dma_len(sg);
+ }
+ /* finalize the last segment */
+ asp->size = cpu_to_le64(dma_len);
+ w->len = (void *)asp + sizeof(*asp) - (void *)&w->msg;
+ *size += w->len;
+ dma_chunk_len += dma_len;
+ resources->xferred_dma_size += dma_chunk_len;
+
+ return nents_dma < nents ? 1 : 0;
+}
+
+static void cleanup_xfer(struct qaic_device *qdev, struct dma_xfer *xfer)
+{
+ int i;
+
+ dma_unmap_sgtable(&qdev->pdev->dev, xfer->sgt, DMA_TO_DEVICE, 0);
+ sg_free_table(xfer->sgt);
+ kfree(xfer->sgt);
+ for (i = 0; i < xfer->nr_pages; ++i)
+ put_page(xfer->page_list[i]);
+ kfree(xfer->page_list);
+}
+
+static int encode_dma(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
+ u32 *user_len, struct ioctl_resources *resources, struct qaic_user *usr)
+{
+ struct qaic_manage_trans_dma_xfer *in_trans = trans;
+ struct wire_trans_dma_xfer *out_trans;
+ struct wrapper_msg *wrapper;
+ struct dma_xfer *xfer;
+ struct wire_msg *msg;
+ bool need_cont_dma;
+ u32 msg_hdr_len;
+ u32 size;
+ int ret;
+
+ wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
+ msg = &wrapper->msg;
+ msg_hdr_len = le32_to_cpu(msg->hdr.len);
+
+ /* There should be enough space to hold at least one ASP entry. */
+ if (size_add(msg_hdr_len, sizeof(*out_trans) + sizeof(struct wire_addr_size_pair)) >
+ QAIC_MANAGE_WIRE_MSG_LENGTH)
+ return -ENOMEM;
+
+ xfer = kmalloc(sizeof(*xfer), GFP_KERNEL);
+ if (!xfer)
+ return -ENOMEM;
+
+ ret = find_and_map_user_pages(qdev, in_trans, resources, xfer);
+ if (ret < 0)
+ goto free_xfer;
+
+ need_cont_dma = (bool)ret;
+
+ ret = encode_addr_size_pairs(xfer, wrappers, resources, msg_hdr_len, &size, &out_trans);
+ if (ret < 0)
+ goto cleanup_xfer;
+
+ need_cont_dma = need_cont_dma || (bool)ret;
+
+ msg->hdr.len = cpu_to_le32(msg_hdr_len + size);
+ msg->hdr.count = incr_le32(msg->hdr.count);
+
+ out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_DMA_XFER_TO_DEV);
+ out_trans->hdr.len = cpu_to_le32(size);
+ out_trans->tag = cpu_to_le32(in_trans->tag);
+ out_trans->count = cpu_to_le32((size - sizeof(*out_trans)) /
+ sizeof(struct wire_addr_size_pair));
+
+ *user_len += in_trans->hdr.len;
+
+ if (resources->dma_chunk_id) {
+ out_trans->dma_chunk_id = cpu_to_le32(resources->dma_chunk_id);
+ } else if (need_cont_dma) {
+ while (resources->dma_chunk_id == 0)
+ resources->dma_chunk_id = atomic_inc_return(&usr->chunk_id);
+
+ out_trans->dma_chunk_id = cpu_to_le32(resources->dma_chunk_id);
+ }
+ resources->trans_hdr = trans;
+
+ list_add(&xfer->list, &resources->dma_xfers);
+ return 0;
+
+cleanup_xfer:
+ cleanup_xfer(qdev, xfer);
+free_xfer:
+ kfree(xfer);
+ return ret;
+}
+
+static int encode_activate(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
+ u32 *user_len, struct ioctl_resources *resources)
+{
+ struct qaic_manage_trans_activate_to_dev *in_trans = trans;
+ struct wire_trans_activate_to_dev *out_trans;
+ struct wrapper_msg *trans_wrapper;
+ struct wrapper_msg *wrapper;
+ struct wire_msg *msg;
+ dma_addr_t dma_addr;
+ u32 msg_hdr_len;
+ void *buf;
+ u32 nelem;
+ u32 size;
+ int ret;
+
+ wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
+ msg = &wrapper->msg;
+ msg_hdr_len = le32_to_cpu(msg->hdr.len);
+
+ if (size_add(msg_hdr_len, sizeof(*out_trans)) > QAIC_MANAGE_WIRE_MSG_LENGTH)
+ return -ENOSPC;
+
+ if (!in_trans->queue_size)
+ return -EINVAL;
+
+ if (in_trans->pad)
+ return -EINVAL;
+
+ nelem = in_trans->queue_size;
+ if (check_mul_overflow((u32)(get_dbc_req_elem_size() + get_dbc_rsp_elem_size()),
+ nelem,
+ &size))
+ return -EINVAL;
+
+ if (size + QAIC_DBC_Q_GAP + QAIC_DBC_Q_BUF_ALIGN < size)
+ return -EINVAL;
+
+ size = ALIGN((size + QAIC_DBC_Q_GAP), QAIC_DBC_Q_BUF_ALIGN);
+
+ buf = dma_alloc_coherent(&qdev->pdev->dev, size, &dma_addr, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ trans_wrapper = add_wrapper(wrappers,
+ offsetof(struct wrapper_msg, trans) + sizeof(*out_trans));
+ if (!trans_wrapper) {
+ ret = -ENOMEM;
+ goto free_dma;
+ }
+ trans_wrapper->len = sizeof(*out_trans);
+ out_trans = (struct wire_trans_activate_to_dev *)&trans_wrapper->trans;
+
+ out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_ACTIVATE_TO_DEV);
+ out_trans->hdr.len = cpu_to_le32(sizeof(*out_trans));
+ out_trans->buf_len = cpu_to_le32(size);
+ out_trans->req_q_addr = cpu_to_le64(dma_addr);
+ out_trans->req_q_size = cpu_to_le32(nelem);
+ out_trans->rsp_q_addr = cpu_to_le64(dma_addr + size - nelem * get_dbc_rsp_elem_size());
+ out_trans->rsp_q_size = cpu_to_le32(nelem);
+ out_trans->options = cpu_to_le32(in_trans->options);
+
+ *user_len += in_trans->hdr.len;
+ msg->hdr.len = cpu_to_le32(msg_hdr_len + sizeof(*out_trans));
+ msg->hdr.count = incr_le32(msg->hdr.count);
+
+ resources->buf = buf;
+ resources->dma_addr = dma_addr;
+ resources->total_size = size;
+ resources->nelem = nelem;
+ resources->rsp_q_base = buf + size - nelem * get_dbc_rsp_elem_size();
+ return 0;
+
+free_dma:
+ dma_free_coherent(&qdev->pdev->dev, size, buf, dma_addr);
+ return ret;
+}
+
+static int encode_deactivate(struct qaic_device *qdev, void *trans,
+ u32 *user_len, struct qaic_user *usr)
+{
+ struct qaic_manage_trans_deactivate *in_trans = trans;
+
+ if (in_trans->dbc_id >= qdev->num_dbc || in_trans->pad)
+ return -EINVAL;
+
+ *user_len += in_trans->hdr.len;
+
+ return disable_dbc(qdev, in_trans->dbc_id, usr);
+}
+
+static int encode_status(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
+ u32 *user_len)
+{
+ struct qaic_manage_trans_status_to_dev *in_trans = trans;
+ struct wire_trans_status_to_dev *out_trans;
+ struct wrapper_msg *trans_wrapper;
+ struct wrapper_msg *wrapper;
+ struct wire_msg *msg;
+ u32 msg_hdr_len;
+
+ wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
+ msg = &wrapper->msg;
+ msg_hdr_len = le32_to_cpu(msg->hdr.len);
+
+ if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_WIRE_MSG_LENGTH)
+ return -ENOSPC;
+
+ trans_wrapper = add_wrapper(wrappers, sizeof(*trans_wrapper));
+ if (!trans_wrapper)
+ return -ENOMEM;
+
+ trans_wrapper->len = sizeof(*out_trans);
+ out_trans = (struct wire_trans_status_to_dev *)&trans_wrapper->trans;
+
+ out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_STATUS_TO_DEV);
+ out_trans->hdr.len = cpu_to_le32(in_trans->hdr.len);
+ msg->hdr.len = cpu_to_le32(msg_hdr_len + in_trans->hdr.len);
+ msg->hdr.count = incr_le32(msg->hdr.count);
+ *user_len += in_trans->hdr.len;
+
+ return 0;
+}
+
+static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
+ struct wrapper_list *wrappers, struct ioctl_resources *resources,
+ struct qaic_user *usr)
+{
+ struct qaic_manage_trans_hdr *trans_hdr;
+ struct wrapper_msg *wrapper;
+ struct wire_msg *msg;
+ u32 user_len = 0;
+ int ret;
+ int i;
+
+ if (!user_msg->count ||
+ user_msg->len < sizeof(*trans_hdr)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
+ msg = &wrapper->msg;
+
+ msg->hdr.len = cpu_to_le32(sizeof(msg->hdr));
+
+ if (resources->dma_chunk_id) {
+ ret = encode_dma(qdev, resources->trans_hdr, wrappers, &user_len, resources, usr);
+ msg->hdr.count = cpu_to_le32(1);
+ goto out;
+ }
+
+ for (i = 0; i < user_msg->count; ++i) {
+ if (user_len > user_msg->len - sizeof(*trans_hdr)) {
+ ret = -EINVAL;
+ break;
+ }
+ trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
+ if (trans_hdr->len < sizeof(trans_hdr) ||
+ size_add(user_len, trans_hdr->len) > user_msg->len) {
+ ret = -EINVAL;
+ break;
+ }
+
+ switch (trans_hdr->type) {
+ case QAIC_TRANS_PASSTHROUGH_FROM_USR:
+ ret = encode_passthrough(qdev, trans_hdr, wrappers, &user_len);
+ break;
+ case QAIC_TRANS_DMA_XFER_FROM_USR:
+ ret = encode_dma(qdev, trans_hdr, wrappers, &user_len, resources, usr);
+ break;
+ case QAIC_TRANS_ACTIVATE_FROM_USR:
+ ret = encode_activate(qdev, trans_hdr, wrappers, &user_len, resources);
+ break;
+ case QAIC_TRANS_DEACTIVATE_FROM_USR:
+ ret = encode_deactivate(qdev, trans_hdr, &user_len, usr);
+ break;
+ case QAIC_TRANS_STATUS_FROM_USR:
+ ret = encode_status(qdev, trans_hdr, wrappers, &user_len);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ goto out;
+ }
+
+ if (user_len != user_msg->len)
+ ret = -EINVAL;
+out:
+ if (ret) {
+ free_dma_xfers(qdev, resources);
+ free_dbc_buf(qdev, resources);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int decode_passthrough(struct qaic_device *qdev, void *trans, struct manage_msg *user_msg,
+ u32 *msg_len)
+{
+ struct qaic_manage_trans_passthrough *out_trans;
+ struct wire_trans_passthrough *in_trans = trans;
+ u32 len;
+
+ out_trans = (void *)user_msg->data + user_msg->len;
+
+ len = le32_to_cpu(in_trans->hdr.len);
+ if (len % 8 != 0)
+ return -EINVAL;
+
+ if (user_msg->len + len > QAIC_MANAGE_MAX_MSG_LENGTH)
+ return -ENOSPC;
+
+ memcpy(out_trans->data, in_trans->data, len - sizeof(in_trans->hdr));
+ user_msg->len += len;
+ *msg_len += len;
+ out_trans->hdr.type = le32_to_cpu(in_trans->hdr.type);
+ out_trans->hdr.len = len;
+
+ return 0;
+}
+
+static int decode_activate(struct qaic_device *qdev, void *trans, struct manage_msg *user_msg,
+ u32 *msg_len, struct ioctl_resources *resources, struct qaic_user *usr)
+{
+ struct qaic_manage_trans_activate_from_dev *out_trans;
+ struct wire_trans_activate_from_dev *in_trans = trans;
+ u32 len;
+
+ out_trans = (void *)user_msg->data + user_msg->len;
+
+ len = le32_to_cpu(in_trans->hdr.len);
+ if (user_msg->len + len > QAIC_MANAGE_MAX_MSG_LENGTH)
+ return -ENOSPC;
+
+ user_msg->len += len;
+ *msg_len += len;
+ out_trans->hdr.type = le32_to_cpu(in_trans->hdr.type);
+ out_trans->hdr.len = len;
+ out_trans->status = le32_to_cpu(in_trans->status);
+ out_trans->dbc_id = le32_to_cpu(in_trans->dbc_id);
+ out_trans->options = le64_to_cpu(in_trans->options);
+
+ if (!resources->buf)
+ /* how did we get an activate response without a request? */
+ return -EINVAL;
+
+ if (out_trans->dbc_id >= qdev->num_dbc)
+ /*
+ * The device assigned an invalid resource, which should never
+ * happen. Return an error so the user can try to recover.
+ */
+ return -ENODEV;
+
+ if (out_trans->status)
+ /*
+ * Allocating resources failed on device side. This is not an
+ * expected behaviour, user is expected to handle this situation.
+ */
+ return -ECANCELED;
+
+ resources->status = out_trans->status;
+ resources->dbc_id = out_trans->dbc_id;
+ save_dbc_buf(qdev, resources, usr);
+
+ return 0;
+}
+
+static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len,
+ struct qaic_user *usr)
+{
+ struct wire_trans_deactivate_from_dev *in_trans = trans;
+ u32 dbc_id = le32_to_cpu(in_trans->dbc_id);
+ u32 status = le32_to_cpu(in_trans->status);
+
+ if (dbc_id >= qdev->num_dbc)
+ /*
+ * The device assigned an invalid resource, which should never
+ * happen. Inject an error so the user can try to recover.
+ */
+ return -ENODEV;
+
+ if (status) {
+ /*
+ * Releasing resources failed on the device side, which puts
+ * us in a bind since they may still be in use, so enable the
+ * dbc. User is expected to retry deactivation.
+ */
+ enable_dbc(qdev, dbc_id, usr);
+ return -ECANCELED;
+ }
+
+ release_dbc(qdev, dbc_id);
+ set_dbc_state(qdev, dbc_id, DBC_STATE_IDLE);
+ *msg_len += sizeof(*in_trans);
+
+ return 0;
+}
+
+static int decode_status(struct qaic_device *qdev, void *trans, struct manage_msg *user_msg,
+ u32 *user_len, struct wire_msg *msg)
+{
+ struct qaic_manage_trans_status_from_dev *out_trans;
+ struct wire_trans_status_from_dev *in_trans = trans;
+ u32 len;
+
+ out_trans = (void *)user_msg->data + user_msg->len;
+
+ len = le32_to_cpu(in_trans->hdr.len);
+ if (user_msg->len + len > QAIC_MANAGE_MAX_MSG_LENGTH)
+ return -ENOSPC;
+
+ out_trans->hdr.type = QAIC_TRANS_STATUS_FROM_DEV;
+ out_trans->hdr.len = len;
+ out_trans->major = le16_to_cpu(in_trans->major);
+ out_trans->minor = le16_to_cpu(in_trans->minor);
+ out_trans->status_flags = le64_to_cpu(in_trans->status_flags);
+ out_trans->status = le32_to_cpu(in_trans->status);
+ *user_len += le32_to_cpu(in_trans->hdr.len);
+ user_msg->len += len;
+
+ if (out_trans->status)
+ return -ECANCELED;
+ if (out_trans->status_flags & BIT(0) && !valid_crc(msg))
+ return -EPIPE;
+
+ return 0;
+}
+
+static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
+ struct wire_msg *msg, struct ioctl_resources *resources,
+ struct qaic_user *usr)
+{
+ u32 msg_hdr_len = le32_to_cpu(msg->hdr.len);
+ struct wire_trans_hdr *trans_hdr;
+ u32 msg_len = 0;
+ int ret;
+ int i;
+
+ if (msg_hdr_len < sizeof(*trans_hdr) ||
+ msg_hdr_len > QAIC_MANAGE_MAX_MSG_LENGTH)
+ return -EINVAL;
+
+ user_msg->len = 0;
+ user_msg->count = le32_to_cpu(msg->hdr.count);
+
+ for (i = 0; i < user_msg->count; ++i) {
+ u32 hdr_len;
+
+ if (msg_len > msg_hdr_len - sizeof(*trans_hdr))
+ return -EINVAL;
+
+ trans_hdr = (struct wire_trans_hdr *)(msg->data + msg_len);
+ hdr_len = le32_to_cpu(trans_hdr->len);
+ if (hdr_len < sizeof(*trans_hdr) ||
+ size_add(msg_len, hdr_len) > msg_hdr_len)
+ return -EINVAL;
+
+ switch (le32_to_cpu(trans_hdr->type)) {
+ case QAIC_TRANS_PASSTHROUGH_FROM_DEV:
+ ret = decode_passthrough(qdev, trans_hdr, user_msg, &msg_len);
+ break;
+ case QAIC_TRANS_ACTIVATE_FROM_DEV:
+ ret = decode_activate(qdev, trans_hdr, user_msg, &msg_len, resources, usr);
+ break;
+ case QAIC_TRANS_DEACTIVATE_FROM_DEV:
+ ret = decode_deactivate(qdev, trans_hdr, &msg_len, usr);
+ break;
+ case QAIC_TRANS_STATUS_FROM_DEV:
+ ret = decode_status(qdev, trans_hdr, user_msg, &msg_len, msg);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+ }
+
+ if (msg_len != (msg_hdr_len - sizeof(msg->hdr)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u32 seq_num,
+ bool ignore_signal)
+{
+ struct xfer_queue_elem elem;
+ struct wire_msg *out_buf;
+ struct wrapper_msg *w;
+ long ret = -EAGAIN;
+ int xfer_count = 0;
+ int retry_count;
+
+ /* Allow QAIC_BOOT state since we need to check control protocol version */
+ if (qdev->dev_state == QAIC_OFFLINE) {
+ mutex_unlock(&qdev->cntl_mutex);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Attempt to avoid a partial commit of a message */
+ list_for_each_entry(w, &wrappers->list, list)
+ xfer_count++;
+
+ for (retry_count = 0; retry_count < QAIC_MHI_RETRY_MAX; retry_count++) {
+ if (xfer_count <= mhi_get_free_desc_count(qdev->cntl_ch, DMA_TO_DEVICE)) {
+ ret = 0;
+ break;
+ }
+ msleep_interruptible(QAIC_MHI_RETRY_WAIT_MS);
+ if (signal_pending(current))
+ break;
+ }
+
+ if (ret) {
+ mutex_unlock(&qdev->cntl_mutex);
+ return ERR_PTR(ret);
+ }
+
+ elem.seq_num = seq_num;
+ elem.buf = NULL;
+ init_completion(&elem.xfer_done);
+ if (likely(!qdev->cntl_lost_buf)) {
+ /*
+ * The max size of request to device is QAIC_MANAGE_WIRE_MSG_LENGTH.
+ * The max size of response from device is QAIC_MANAGE_MAX_MSG_LENGTH.
+ */
+ out_buf = kmalloc(QAIC_MANAGE_MAX_MSG_LENGTH, GFP_KERNEL);
+ if (!out_buf) {
+ mutex_unlock(&qdev->cntl_mutex);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = mhi_queue_buf(qdev->cntl_ch, DMA_FROM_DEVICE, out_buf,
+ QAIC_MANAGE_MAX_MSG_LENGTH, MHI_EOT);
+ if (ret) {
+ mutex_unlock(&qdev->cntl_mutex);
+ return ERR_PTR(ret);
+ }
+ } else {
+ /*
+ * we lost a buffer because we queued a recv buf, but then
+ * queuing the corresponding tx buf failed. To try to avoid
+ * a memory leak, lets reclaim it and use it for this
+ * transaction.
+ */
+ qdev->cntl_lost_buf = false;
+ }
+
+ list_for_each_entry(w, &wrappers->list, list) {
+ kref_get(&w->ref_count);
+ ret = mhi_queue_buf(qdev->cntl_ch, DMA_TO_DEVICE, &w->msg, w->len,
+ list_is_last(&w->list, &wrappers->list) ? MHI_EOT : MHI_CHAIN);
+ if (ret) {
+ qdev->cntl_lost_buf = true;
+ kref_put(&w->ref_count, free_wrapper);
+ mutex_unlock(&qdev->cntl_mutex);
+ return ERR_PTR(ret);
+ }
+ }
+
+ list_add_tail(&elem.list, &qdev->cntl_xfer_list);
+ mutex_unlock(&qdev->cntl_mutex);
+
+ if (ignore_signal)
+ ret = wait_for_completion_timeout(&elem.xfer_done, control_resp_timeout_s * HZ);
+ else
+ ret = wait_for_completion_interruptible_timeout(&elem.xfer_done,
+ control_resp_timeout_s * HZ);
+ /*
+ * not using _interruptable because we have to cleanup or we'll
+ * likely cause memory corruption
+ */
+ mutex_lock(&qdev->cntl_mutex);
+ if (!list_empty(&elem.list))
+ list_del(&elem.list);
+ if (!ret && !elem.buf)
+ ret = -ETIMEDOUT;
+ else if (ret > 0 && !elem.buf)
+ ret = -EIO;
+ mutex_unlock(&qdev->cntl_mutex);
+
+ if (ret < 0) {
+ kfree(elem.buf);
+ return ERR_PTR(ret);
+ } else if (!qdev->valid_crc(elem.buf)) {
+ kfree(elem.buf);
+ return ERR_PTR(-EPIPE);
+ }
+
+ return elem.buf;
+}
+
+/* Add a transaction to abort the outstanding DMA continuation */
+static int abort_dma_cont(struct qaic_device *qdev, struct wrapper_list *wrappers, u32 dma_chunk_id)
+{
+ struct wire_trans_dma_xfer *out_trans;
+ u32 size = sizeof(*out_trans);
+ struct wrapper_msg *wrapper;
+ struct wrapper_msg *w;
+ struct wire_msg *msg;
+
+ wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
+ msg = &wrapper->msg;
+
+ /* Remove all but the first wrapper which has the msg header */
+ list_for_each_entry_safe(wrapper, w, &wrappers->list, list)
+ if (!list_is_first(&wrapper->list, &wrappers->list))
+ kref_put(&wrapper->ref_count, free_wrapper);
+
+ wrapper = add_wrapper(wrappers, sizeof(*wrapper));
+
+ if (!wrapper)
+ return -ENOMEM;
+
+ out_trans = (struct wire_trans_dma_xfer *)&wrapper->trans;
+ out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_DMA_XFER_TO_DEV);
+ out_trans->hdr.len = cpu_to_le32(size);
+ out_trans->tag = cpu_to_le32(0);
+ out_trans->count = cpu_to_le32(0);
+ out_trans->dma_chunk_id = cpu_to_le32(dma_chunk_id);
+
+ msg->hdr.len = cpu_to_le32(size + sizeof(*msg));
+ msg->hdr.count = cpu_to_le32(1);
+ wrapper->len = size;
+
+ return 0;
+}
+
+static struct wrapper_list *alloc_wrapper_list(void)
+{
+ struct wrapper_list *wrappers;
+
+ wrappers = kmalloc(sizeof(*wrappers), GFP_KERNEL);
+ if (!wrappers)
+ return NULL;
+ INIT_LIST_HEAD(&wrappers->list);
+ spin_lock_init(&wrappers->lock);
+
+ return wrappers;
+}
+
+static int qaic_manage_msg_xfer(struct qaic_device *qdev, struct qaic_user *usr,
+ struct manage_msg *user_msg, struct ioctl_resources *resources,
+ struct wire_msg **rsp)
+{
+ struct wrapper_list *wrappers;
+ struct wrapper_msg *wrapper;
+ struct wrapper_msg *w;
+ bool all_done = false;
+ struct wire_msg *msg;
+ int ret;
+
+ wrappers = alloc_wrapper_list();
+ if (!wrappers)
+ return -ENOMEM;
+
+ wrapper = add_wrapper(wrappers, sizeof(*wrapper));
+ if (!wrapper) {
+ kfree(wrappers);
+ return -ENOMEM;
+ }
+
+ msg = &wrapper->msg;
+ wrapper->len = sizeof(*msg);
+
+ ret = encode_message(qdev, user_msg, wrappers, resources, usr);
+ if (ret && resources->dma_chunk_id)
+ ret = abort_dma_cont(qdev, wrappers, resources->dma_chunk_id);
+ if (ret)
+ goto encode_failed;
+
+ ret = mutex_lock_interruptible(&qdev->cntl_mutex);
+ if (ret)
+ goto lock_failed;
+
+ msg->hdr.magic_number = MANAGE_MAGIC_NUMBER;
+ msg->hdr.sequence_number = cpu_to_le32(qdev->next_seq_num++);
+
+ if (usr) {
+ msg->hdr.handle = cpu_to_le32(usr->handle);
+ msg->hdr.partition_id = cpu_to_le32(usr->qddev->partition_id);
+ } else {
+ msg->hdr.handle = 0;
+ msg->hdr.partition_id = cpu_to_le32(QAIC_NO_PARTITION);
+ }
+
+ msg->hdr.padding = cpu_to_le32(0);
+ msg->hdr.crc32 = cpu_to_le32(qdev->gen_crc(wrappers));
+
+ /* msg_xfer releases the mutex */
+ *rsp = msg_xfer(qdev, wrappers, qdev->next_seq_num - 1, false);
+ if (IS_ERR(*rsp))
+ ret = PTR_ERR(*rsp);
+
+lock_failed:
+ free_dma_xfers(qdev, resources);
+encode_failed:
+ spin_lock(&wrappers->lock);
+ list_for_each_entry_safe(wrapper, w, &wrappers->list, list)
+ kref_put(&wrapper->ref_count, free_wrapper);
+ all_done = list_empty(&wrappers->list);
+ spin_unlock(&wrappers->lock);
+ if (all_done)
+ kfree(wrappers);
+
+ return ret;
+}
+
+static int qaic_manage(struct qaic_device *qdev, struct qaic_user *usr, struct manage_msg *user_msg)
+{
+ struct wire_trans_dma_xfer_cont *dma_cont = NULL;
+ struct ioctl_resources resources;
+ struct wire_msg *rsp = NULL;
+ int ret;
+
+ memset(&resources, 0, sizeof(struct ioctl_resources));
+
+ INIT_LIST_HEAD(&resources.dma_xfers);
+
+ if (user_msg->len > QAIC_MANAGE_MAX_MSG_LENGTH ||
+ user_msg->count > QAIC_MANAGE_MAX_MSG_LENGTH / sizeof(struct qaic_manage_trans_hdr))
+ return -EINVAL;
+
+dma_xfer_continue:
+ ret = qaic_manage_msg_xfer(qdev, usr, user_msg, &resources, &rsp);
+ if (ret)
+ return ret;
+ /* dma_cont should be the only transaction if present */
+ if (le32_to_cpu(rsp->hdr.count) == 1) {
+ dma_cont = (struct wire_trans_dma_xfer_cont *)rsp->data;
+ if (le32_to_cpu(dma_cont->hdr.type) != QAIC_TRANS_DMA_XFER_CONT)
+ dma_cont = NULL;
+ }
+ if (dma_cont) {
+ if (le32_to_cpu(dma_cont->dma_chunk_id) == resources.dma_chunk_id &&
+ le64_to_cpu(dma_cont->xferred_size) == resources.xferred_dma_size) {
+ kfree(rsp);
+ goto dma_xfer_continue;
+ }
+
+ ret = -EINVAL;
+ goto dma_cont_failed;
+ }
+
+ ret = decode_message(qdev, user_msg, rsp, &resources, usr);
+
+dma_cont_failed:
+ free_dbc_buf(qdev, &resources);
+ kfree(rsp);
+ return ret;
+}
+
+int qaic_manage_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_manage_msg *user_msg = data;
+ struct qaic_device *qdev;
+ struct manage_msg *msg;
+ struct qaic_user *usr;
+ u8 __user *user_data;
+ int qdev_rcu_id;
+ int usr_rcu_id;
+ int ret;
+
+ if (user_msg->len > QAIC_MANAGE_MAX_MSG_LENGTH)
+ return -EINVAL;
+
+ usr = file_priv->driver_priv;
+
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return -ENODEV;
+ }
+
+ qdev = usr->qddev->qdev;
+
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return -ENODEV;
+ }
+
+ msg = kzalloc(QAIC_MANAGE_MAX_MSG_LENGTH + sizeof(*msg), GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ msg->len = user_msg->len;
+ msg->count = user_msg->count;
+
+ user_data = u64_to_user_ptr(user_msg->data);
+
+ if (copy_from_user(msg->data, user_data, user_msg->len)) {
+ ret = -EFAULT;
+ goto free_msg;
+ }
+
+ ret = qaic_manage(qdev, usr, msg);
+
+ /*
+ * If the qaic_manage() is successful then we copy the message onto
+ * userspace memory but we have an exception for -ECANCELED.
+ * For -ECANCELED, it means that device has NACKed the message with a
+ * status error code which userspace would like to know.
+ */
+ if (ret == -ECANCELED || !ret) {
+ if (copy_to_user(user_data, msg->data, msg->len)) {
+ ret = -EFAULT;
+ } else {
+ user_msg->len = msg->len;
+ user_msg->count = msg->count;
+ }
+ }
+
+free_msg:
+ kfree(msg);
+out:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+int get_cntl_version(struct qaic_device *qdev, struct qaic_user *usr, u16 *major, u16 *minor)
+{
+ struct qaic_manage_trans_status_from_dev *status_result;
+ struct qaic_manage_trans_status_to_dev *status_query;
+ struct manage_msg *user_msg;
+ int ret;
+
+ user_msg = kmalloc(sizeof(*user_msg) + sizeof(*status_result), GFP_KERNEL);
+ if (!user_msg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ user_msg->len = sizeof(*status_query);
+ user_msg->count = 1;
+
+ status_query = (struct qaic_manage_trans_status_to_dev *)user_msg->data;
+ status_query->hdr.type = QAIC_TRANS_STATUS_FROM_USR;
+ status_query->hdr.len = sizeof(status_query->hdr);
+
+ ret = qaic_manage(qdev, usr, user_msg);
+ if (ret)
+ goto kfree_user_msg;
+ status_result = (struct qaic_manage_trans_status_from_dev *)user_msg->data;
+ *major = status_result->major;
+ *minor = status_result->minor;
+
+ if (status_result->status_flags & BIT(0)) { /* device is using CRC */
+ /* By default qdev->gen_crc is programmed to generate CRC */
+ qdev->valid_crc = valid_crc;
+ } else {
+ /* By default qdev->valid_crc is programmed to bypass CRC */
+ qdev->gen_crc = gen_crc_stub;
+ }
+
+kfree_user_msg:
+ kfree(user_msg);
+out:
+ return ret;
+}
+
+static void resp_worker(struct work_struct *work)
+{
+ struct resp_work *resp = container_of(work, struct resp_work, work);
+ struct qaic_device *qdev = resp->qdev;
+ struct wire_msg *msg = resp->buf;
+ struct xfer_queue_elem *elem;
+ struct xfer_queue_elem *i;
+ bool found = false;
+
+ mutex_lock(&qdev->cntl_mutex);
+ list_for_each_entry_safe(elem, i, &qdev->cntl_xfer_list, list) {
+ if (elem->seq_num == le32_to_cpu(msg->hdr.sequence_number)) {
+ found = true;
+ list_del_init(&elem->list);
+ elem->buf = msg;
+ complete_all(&elem->xfer_done);
+ break;
+ }
+ }
+ mutex_unlock(&qdev->cntl_mutex);
+
+ if (!found)
+ /* request must have timed out, drop packet */
+ kfree(msg);
+
+ kfree(resp);
+}
+
+static void free_wrapper_from_list(struct wrapper_list *wrappers, struct wrapper_msg *wrapper)
+{
+ bool all_done = false;
+
+ spin_lock(&wrappers->lock);
+ kref_put(&wrapper->ref_count, free_wrapper);
+ all_done = list_empty(&wrappers->list);
+ spin_unlock(&wrappers->lock);
+
+ if (all_done)
+ kfree(wrappers);
+}
+
+void qaic_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct wire_msg *msg = mhi_result->buf_addr;
+ struct wrapper_msg *wrapper = container_of(msg, struct wrapper_msg, msg);
+
+ free_wrapper_from_list(wrapper->head, wrapper);
+}
+
+void qaic_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct wire_msg *msg = mhi_result->buf_addr;
+ struct resp_work *resp;
+
+ if (mhi_result->transaction_status || msg->hdr.magic_number != MANAGE_MAGIC_NUMBER) {
+ kfree(msg);
+ return;
+ }
+
+ resp = kmalloc(sizeof(*resp), GFP_ATOMIC);
+ if (!resp) {
+ kfree(msg);
+ return;
+ }
+
+ INIT_WORK(&resp->work, resp_worker);
+ resp->qdev = qdev;
+ resp->buf = msg;
+ queue_work(qdev->cntl_wq, &resp->work);
+}
+
+int qaic_control_open(struct qaic_device *qdev)
+{
+ if (!qdev->cntl_ch)
+ return -ENODEV;
+
+ qdev->cntl_lost_buf = false;
+ /*
+ * By default qaic should assume that device has CRC enabled.
+ * Qaic comes to know if device has CRC enabled or disabled during the
+ * device status transaction, which is the first transaction performed
+ * on control channel.
+ *
+ * So CRC validation of first device status transaction response is
+ * ignored (by calling valid_crc_stub) and is done later during decoding
+ * if device has CRC enabled.
+ * Now that qaic knows whether device has CRC enabled or not it acts
+ * accordingly.
+ */
+ qdev->gen_crc = gen_crc;
+ qdev->valid_crc = valid_crc_stub;
+
+ return mhi_prepare_for_transfer(qdev->cntl_ch);
+}
+
+void qaic_control_close(struct qaic_device *qdev)
+{
+ mhi_unprepare_from_transfer(qdev->cntl_ch);
+}
+
+void qaic_release_usr(struct qaic_device *qdev, struct qaic_user *usr)
+{
+ struct wire_trans_terminate_to_dev *trans;
+ struct wrapper_list *wrappers;
+ struct wrapper_msg *wrapper;
+ struct wire_msg *msg;
+ struct wire_msg *rsp;
+
+ wrappers = alloc_wrapper_list();
+ if (!wrappers)
+ return;
+
+ wrapper = add_wrapper(wrappers, sizeof(*wrapper) + sizeof(*msg) + sizeof(*trans));
+ if (!wrapper)
+ return;
+
+ msg = &wrapper->msg;
+
+ trans = (struct wire_trans_terminate_to_dev *)msg->data;
+
+ trans->hdr.type = cpu_to_le32(QAIC_TRANS_TERMINATE_TO_DEV);
+ trans->hdr.len = cpu_to_le32(sizeof(*trans));
+ trans->handle = cpu_to_le32(usr->handle);
+
+ mutex_lock(&qdev->cntl_mutex);
+ wrapper->len = sizeof(msg->hdr) + sizeof(*trans);
+ msg->hdr.magic_number = MANAGE_MAGIC_NUMBER;
+ msg->hdr.sequence_number = cpu_to_le32(qdev->next_seq_num++);
+ msg->hdr.len = cpu_to_le32(wrapper->len);
+ msg->hdr.count = cpu_to_le32(1);
+ msg->hdr.handle = cpu_to_le32(usr->handle);
+ msg->hdr.padding = cpu_to_le32(0);
+ msg->hdr.crc32 = cpu_to_le32(qdev->gen_crc(wrappers));
+
+ /*
+ * msg_xfer releases the mutex
+ * We don't care about the return of msg_xfer since we will not do
+ * anything different based on what happens.
+ * We ignore pending signals since one will be set if the user is
+ * killed, and we need give the device a chance to cleanup, otherwise
+ * DMA may still be in progress when we return.
+ */
+ rsp = msg_xfer(qdev, wrappers, qdev->next_seq_num - 1, true);
+ if (!IS_ERR(rsp))
+ kfree(rsp);
+ free_wrapper_from_list(wrappers, wrapper);
+}
+
+void wake_all_cntl(struct qaic_device *qdev)
+{
+ struct xfer_queue_elem *elem;
+ struct xfer_queue_elem *i;
+
+ mutex_lock(&qdev->cntl_mutex);
+ list_for_each_entry_safe(elem, i, &qdev->cntl_xfer_list, list) {
+ list_del_init(&elem->list);
+ complete_all(&elem->xfer_done);
+ }
+ mutex_unlock(&qdev->cntl_mutex);
+}
diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c
new file mode 100644
index 000000000000..60cb4d65d48e
--- /dev/null
+++ b/drivers/accel/qaic/qaic_data.c
@@ -0,0 +1,2072 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock.h>
+#include <linux/srcu.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
+#include <uapi/drm/qaic_accel.h>
+
+#include "qaic.h"
+
+#define SEM_VAL_MASK GENMASK_ULL(11, 0)
+#define SEM_INDEX_MASK GENMASK_ULL(4, 0)
+#define BULK_XFER BIT(3)
+#define GEN_COMPLETION BIT(4)
+#define INBOUND_XFER 1
+#define OUTBOUND_XFER 2
+#define REQHP_OFF 0x0 /* we read this */
+#define REQTP_OFF 0x4 /* we write this */
+#define RSPHP_OFF 0x8 /* we write this */
+#define RSPTP_OFF 0xc /* we read this */
+
+#define ENCODE_SEM(val, index, sync, cmd, flags) \
+ ({ \
+ FIELD_PREP(GENMASK(11, 0), (val)) | \
+ FIELD_PREP(GENMASK(20, 16), (index)) | \
+ FIELD_PREP(BIT(22), (sync)) | \
+ FIELD_PREP(GENMASK(26, 24), (cmd)) | \
+ FIELD_PREP(GENMASK(30, 29), (flags)) | \
+ FIELD_PREP(BIT(31), (cmd) ? 1 : 0); \
+ })
+#define NUM_EVENTS 128
+#define NUM_DELAYS 10
+#define fifo_at(base, offset) ((base) + (offset) * get_dbc_req_elem_size())
+
+static unsigned int wait_exec_default_timeout_ms = 5000; /* 5 sec default */
+module_param(wait_exec_default_timeout_ms, uint, 0600);
+MODULE_PARM_DESC(wait_exec_default_timeout_ms, "Default timeout for DRM_IOCTL_QAIC_WAIT_BO");
+
+static unsigned int datapath_poll_interval_us = 100; /* 100 usec default */
+module_param(datapath_poll_interval_us, uint, 0600);
+MODULE_PARM_DESC(datapath_poll_interval_us,
+ "Amount of time to sleep between activity when datapath polling is enabled");
+
+struct dbc_req {
+ /*
+ * A request ID is assigned to each memory handle going in DMA queue.
+ * As a single memory handle can enqueue multiple elements in DMA queue
+ * all of them will have the same request ID.
+ */
+ __le16 req_id;
+ /* Future use */
+ __u8 seq_id;
+ /*
+ * Special encoded variable
+ * 7 0 - Do not force to generate MSI after DMA is completed
+ * 1 - Force to generate MSI after DMA is completed
+ * 6:5 Reserved
+ * 4 1 - Generate completion element in the response queue
+ * 0 - No Completion Code
+ * 3 0 - DMA request is a Link list transfer
+ * 1 - DMA request is a Bulk transfer
+ * 2 Reserved
+ * 1:0 00 - No DMA transfer involved
+ * 01 - DMA transfer is part of inbound transfer
+ * 10 - DMA transfer has outbound transfer
+ * 11 - NA
+ */
+ __u8 cmd;
+ __le32 resv;
+ /* Source address for the transfer */
+ __le64 src_addr;
+ /* Destination address for the transfer */
+ __le64 dest_addr;
+ /* Length of transfer request */
+ __le32 len;
+ __le32 resv2;
+ /* Doorbell address */
+ __le64 db_addr;
+ /*
+ * Special encoded variable
+ * 7 1 - Doorbell(db) write
+ * 0 - No doorbell write
+ * 6:2 Reserved
+ * 1:0 00 - 32 bit access, db address must be aligned to 32bit-boundary
+ * 01 - 16 bit access, db address must be aligned to 16bit-boundary
+ * 10 - 8 bit access, db address must be aligned to 8bit-boundary
+ * 11 - Reserved
+ */
+ __u8 db_len;
+ __u8 resv3;
+ __le16 resv4;
+ /* 32 bit data written to doorbell address */
+ __le32 db_data;
+ /*
+ * Special encoded variable
+ * All the fields of sem_cmdX are passed from user and all are ORed
+ * together to form sem_cmd.
+ * 0:11 Semaphore value
+ * 15:12 Reserved
+ * 20:16 Semaphore index
+ * 21 Reserved
+ * 22 Semaphore Sync
+ * 23 Reserved
+ * 26:24 Semaphore command
+ * 28:27 Reserved
+ * 29 Semaphore DMA out bound sync fence
+ * 30 Semaphore DMA in bound sync fence
+ * 31 Enable semaphore command
+ */
+ __le32 sem_cmd0;
+ __le32 sem_cmd1;
+ __le32 sem_cmd2;
+ __le32 sem_cmd3;
+} __packed;
+
+struct dbc_rsp {
+ /* Request ID of the memory handle whose DMA transaction is completed */
+ __le16 req_id;
+ /* Status of the DMA transaction. 0 : Success otherwise failure */
+ __le16 status;
+} __packed;
+
+static inline bool bo_queued(struct qaic_bo *bo)
+{
+ return !list_empty(&bo->xfer_list);
+}
+
+inline int get_dbc_req_elem_size(void)
+{
+ return sizeof(struct dbc_req);
+}
+
+inline int get_dbc_rsp_elem_size(void)
+{
+ return sizeof(struct dbc_rsp);
+}
+
+static void free_slice(struct kref *kref)
+{
+ struct bo_slice *slice = container_of(kref, struct bo_slice, ref_count);
+
+ slice->bo->total_slice_nents -= slice->nents;
+ list_del(&slice->slice);
+ drm_gem_object_put(&slice->bo->base);
+ sg_free_table(slice->sgt);
+ kfree(slice->sgt);
+ kvfree(slice->reqs);
+ kfree(slice);
+}
+
+static int clone_range_of_sgt_for_slice(struct qaic_device *qdev, struct sg_table **sgt_out,
+ struct sg_table *sgt_in, u64 size, u64 offset)
+{
+ struct scatterlist *sg, *sgn, *sgf, *sgl;
+ unsigned int len, nents, offf, offl;
+ struct sg_table *sgt;
+ size_t total_len;
+ int ret, j;
+
+ /* find out number of relevant nents needed for this mem */
+ total_len = 0;
+ sgf = NULL;
+ sgl = NULL;
+ nents = 0;
+ offf = 0;
+ offl = 0;
+
+ size = size ? size : PAGE_SIZE;
+ for_each_sgtable_dma_sg(sgt_in, sg, j) {
+ len = sg_dma_len(sg);
+
+ if (!len)
+ continue;
+ if (offset >= total_len && offset < total_len + len) {
+ sgf = sg;
+ offf = offset - total_len;
+ }
+ if (sgf)
+ nents++;
+ if (offset + size >= total_len &&
+ offset + size <= total_len + len) {
+ sgl = sg;
+ offl = offset + size - total_len;
+ break;
+ }
+ total_len += len;
+ }
+
+ if (!sgf || !sgl) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
+ if (ret)
+ goto free_sgt;
+
+ /* copy relevant sg node and fix page and length */
+ sgn = sgf;
+ for_each_sgtable_dma_sg(sgt, sg, j) {
+ memcpy(sg, sgn, sizeof(*sg));
+ if (sgn == sgf) {
+ sg_dma_address(sg) += offf;
+ sg_dma_len(sg) -= offf;
+ sg_set_page(sg, sg_page(sgn), sg_dma_len(sg), offf);
+ } else {
+ offf = 0;
+ }
+ if (sgn == sgl) {
+ sg_dma_len(sg) = offl - offf;
+ sg_set_page(sg, sg_page(sgn), offl - offf, offf);
+ sg_mark_end(sg);
+ break;
+ }
+ sgn = sg_next(sgn);
+ }
+
+ *sgt_out = sgt;
+ return ret;
+
+free_sgt:
+ kfree(sgt);
+out:
+ *sgt_out = NULL;
+ return ret;
+}
+
+static int encode_reqs(struct qaic_device *qdev, struct bo_slice *slice,
+ struct qaic_attach_slice_entry *req)
+{
+ __le64 db_addr = cpu_to_le64(req->db_addr);
+ __le32 db_data = cpu_to_le32(req->db_data);
+ struct scatterlist *sg;
+ __u8 cmd = BULK_XFER;
+ int presync_sem;
+ u64 dev_addr;
+ __u8 db_len;
+ int i;
+
+ if (!slice->no_xfer)
+ cmd |= (slice->dir == DMA_TO_DEVICE ? INBOUND_XFER : OUTBOUND_XFER);
+
+ if (req->db_len && !IS_ALIGNED(req->db_addr, req->db_len / 8))
+ return -EINVAL;
+
+ presync_sem = req->sem0.presync + req->sem1.presync + req->sem2.presync + req->sem3.presync;
+ if (presync_sem > 1)
+ return -EINVAL;
+
+ presync_sem = req->sem0.presync << 0 | req->sem1.presync << 1 |
+ req->sem2.presync << 2 | req->sem3.presync << 3;
+
+ switch (req->db_len) {
+ case 32:
+ db_len = BIT(7);
+ break;
+ case 16:
+ db_len = BIT(7) | 1;
+ break;
+ case 8:
+ db_len = BIT(7) | 2;
+ break;
+ case 0:
+ db_len = 0; /* doorbell is not active for this command */
+ break;
+ default:
+ return -EINVAL; /* should never hit this */
+ }
+
+ /*
+ * When we end up splitting up a single request (ie a buf slice) into
+ * multiple DMA requests, we have to manage the sync data carefully.
+ * There can only be one presync sem. That needs to be on every xfer
+ * so that the DMA engine doesn't transfer data before the receiver is
+ * ready. We only do the doorbell and postsync sems after the xfer.
+ * To guarantee previous xfers for the request are complete, we use a
+ * fence.
+ */
+ dev_addr = req->dev_addr;
+ for_each_sgtable_dma_sg(slice->sgt, sg, i) {
+ slice->reqs[i].cmd = cmd;
+ slice->reqs[i].src_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ?
+ sg_dma_address(sg) : dev_addr);
+ slice->reqs[i].dest_addr = cpu_to_le64(slice->dir == DMA_TO_DEVICE ?
+ dev_addr : sg_dma_address(sg));
+ /*
+ * sg_dma_len(sg) returns size of a DMA segment, maximum DMA
+ * segment size is set to UINT_MAX by qaic and hence return
+ * values of sg_dma_len(sg) can never exceed u32 range. So,
+ * by down sizing we are not corrupting the value.
+ */
+ slice->reqs[i].len = cpu_to_le32((u32)sg_dma_len(sg));
+ switch (presync_sem) {
+ case BIT(0):
+ slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val,
+ req->sem0.index,
+ req->sem0.presync,
+ req->sem0.cmd,
+ req->sem0.flags));
+ break;
+ case BIT(1):
+ slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val,
+ req->sem1.index,
+ req->sem1.presync,
+ req->sem1.cmd,
+ req->sem1.flags));
+ break;
+ case BIT(2):
+ slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val,
+ req->sem2.index,
+ req->sem2.presync,
+ req->sem2.cmd,
+ req->sem2.flags));
+ break;
+ case BIT(3):
+ slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val,
+ req->sem3.index,
+ req->sem3.presync,
+ req->sem3.cmd,
+ req->sem3.flags));
+ break;
+ }
+ dev_addr += sg_dma_len(sg);
+ }
+ /* add post transfer stuff to last segment */
+ i--;
+ slice->reqs[i].cmd |= GEN_COMPLETION;
+ slice->reqs[i].db_addr = db_addr;
+ slice->reqs[i].db_len = db_len;
+ slice->reqs[i].db_data = db_data;
+ /*
+ * Add a fence if we have more than one request going to the hardware
+ * representing the entirety of the user request, and the user request
+ * has no presync condition.
+ * Fences are expensive, so we try to avoid them. We rely on the
+ * hardware behavior to avoid needing one when there is a presync
+ * condition. When a presync exists, all requests for that same
+ * presync will be queued into a fifo. Thus, since we queue the
+ * post xfer activity only on the last request we queue, the hardware
+ * will ensure that the last queued request is processed last, thus
+ * making sure the post xfer activity happens at the right time without
+ * a fence.
+ */
+ if (i && !presync_sem)
+ req->sem0.flags |= (slice->dir == DMA_TO_DEVICE ?
+ QAIC_SEM_INSYNCFENCE : QAIC_SEM_OUTSYNCFENCE);
+ slice->reqs[i].sem_cmd0 = cpu_to_le32(ENCODE_SEM(req->sem0.val, req->sem0.index,
+ req->sem0.presync, req->sem0.cmd,
+ req->sem0.flags));
+ slice->reqs[i].sem_cmd1 = cpu_to_le32(ENCODE_SEM(req->sem1.val, req->sem1.index,
+ req->sem1.presync, req->sem1.cmd,
+ req->sem1.flags));
+ slice->reqs[i].sem_cmd2 = cpu_to_le32(ENCODE_SEM(req->sem2.val, req->sem2.index,
+ req->sem2.presync, req->sem2.cmd,
+ req->sem2.flags));
+ slice->reqs[i].sem_cmd3 = cpu_to_le32(ENCODE_SEM(req->sem3.val, req->sem3.index,
+ req->sem3.presync, req->sem3.cmd,
+ req->sem3.flags));
+
+ return 0;
+}
+
+static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
+ struct qaic_attach_slice_entry *slice_ent)
+{
+ struct sg_table *sgt = NULL;
+ struct bo_slice *slice;
+ int ret;
+
+ ret = clone_range_of_sgt_for_slice(qdev, &sgt, bo->sgt, slice_ent->size, slice_ent->offset);
+ if (ret)
+ goto out;
+
+ slice = kmalloc(sizeof(*slice), GFP_KERNEL);
+ if (!slice) {
+ ret = -ENOMEM;
+ goto free_sgt;
+ }
+
+ slice->reqs = kvcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
+ if (!slice->reqs) {
+ ret = -ENOMEM;
+ goto free_slice;
+ }
+
+ slice->no_xfer = !slice_ent->size;
+ slice->sgt = sgt;
+ slice->nents = sgt->nents;
+ slice->dir = bo->dir;
+ slice->bo = bo;
+ slice->size = slice_ent->size;
+ slice->offset = slice_ent->offset;
+
+ ret = encode_reqs(qdev, slice, slice_ent);
+ if (ret)
+ goto free_req;
+
+ bo->total_slice_nents += sgt->nents;
+ kref_init(&slice->ref_count);
+ drm_gem_object_get(&bo->base);
+ list_add_tail(&slice->slice, &bo->slices);
+
+ return 0;
+
+free_req:
+ kvfree(slice->reqs);
+free_slice:
+ kfree(slice);
+free_sgt:
+ sg_free_table(sgt);
+ kfree(sgt);
+out:
+ return ret;
+}
+
+static int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 size)
+{
+ struct scatterlist *sg;
+ struct sg_table *sgt;
+ struct page **pages;
+ int *pages_order;
+ int buf_extra;
+ int max_order;
+ int nr_pages;
+ int ret = 0;
+ int i, j, k;
+ int order;
+
+ if (size) {
+ nr_pages = DIV_ROUND_UP(size, PAGE_SIZE);
+ /*
+ * calculate how much extra we are going to allocate, to remove
+ * later
+ */
+ buf_extra = (PAGE_SIZE - size % PAGE_SIZE) % PAGE_SIZE;
+ max_order = min(MAX_PAGE_ORDER, get_order(size));
+ } else {
+ /* allocate a single page for book keeping */
+ nr_pages = 1;
+ buf_extra = 0;
+ max_order = 0;
+ }
+
+ pages = kvmalloc_array(nr_pages, sizeof(*pages) + sizeof(*pages_order), GFP_KERNEL);
+ if (!pages) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pages_order = (void *)pages + sizeof(*pages) * nr_pages;
+
+ /*
+ * Allocate requested memory using alloc_pages. It is possible to allocate
+ * the requested memory in multiple chunks by calling alloc_pages
+ * multiple times. Use SG table to handle multiple allocated pages.
+ */
+ i = 0;
+ while (nr_pages > 0) {
+ order = min(get_order(nr_pages * PAGE_SIZE), max_order);
+ while (1) {
+ pages[i] = alloc_pages(GFP_KERNEL | GFP_HIGHUSER |
+ __GFP_NOWARN | __GFP_ZERO |
+ (order ? __GFP_NORETRY : __GFP_RETRY_MAYFAIL),
+ order);
+ if (pages[i])
+ break;
+ if (!order--) {
+ ret = -ENOMEM;
+ goto free_partial_alloc;
+ }
+ }
+
+ max_order = order;
+ pages_order[i] = order;
+
+ nr_pages -= 1 << order;
+ if (nr_pages <= 0)
+ /* account for over allocation */
+ buf_extra += abs(nr_pages) * PAGE_SIZE;
+ i++;
+ }
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ ret = -ENOMEM;
+ goto free_partial_alloc;
+ }
+
+ if (sg_alloc_table(sgt, i, GFP_KERNEL)) {
+ ret = -ENOMEM;
+ goto free_sgt;
+ }
+
+ /* Populate the SG table with the allocated memory pages */
+ sg = sgt->sgl;
+ for (k = 0; k < i; k++, sg = sg_next(sg)) {
+ /* Last entry requires special handling */
+ if (k < i - 1) {
+ sg_set_page(sg, pages[k], PAGE_SIZE << pages_order[k], 0);
+ } else {
+ sg_set_page(sg, pages[k], (PAGE_SIZE << pages_order[k]) - buf_extra, 0);
+ sg_mark_end(sg);
+ }
+ }
+
+ kvfree(pages);
+ *sgt_out = sgt;
+ return ret;
+
+free_sgt:
+ kfree(sgt);
+free_partial_alloc:
+ for (j = 0; j < i; j++)
+ __free_pages(pages[j], pages_order[j]);
+ kvfree(pages);
+out:
+ *sgt_out = NULL;
+ return ret;
+}
+
+static bool invalid_sem(struct qaic_sem *sem)
+{
+ if (sem->val & ~SEM_VAL_MASK || sem->index & ~SEM_INDEX_MASK ||
+ !(sem->presync == 0 || sem->presync == 1) || sem->pad ||
+ sem->flags & ~(QAIC_SEM_INSYNCFENCE | QAIC_SEM_OUTSYNCFENCE) ||
+ sem->cmd > QAIC_SEM_WAIT_GT_0)
+ return true;
+ return false;
+}
+
+static int qaic_validate_req(struct qaic_device *qdev, struct qaic_attach_slice_entry *slice_ent,
+ u32 count, u64 total_size)
+{
+ u64 total;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!(slice_ent[i].db_len == 32 || slice_ent[i].db_len == 16 ||
+ slice_ent[i].db_len == 8 || slice_ent[i].db_len == 0) ||
+ invalid_sem(&slice_ent[i].sem0) || invalid_sem(&slice_ent[i].sem1) ||
+ invalid_sem(&slice_ent[i].sem2) || invalid_sem(&slice_ent[i].sem3))
+ return -EINVAL;
+
+ if (check_add_overflow(slice_ent[i].offset, slice_ent[i].size, &total) ||
+ total > total_size)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void qaic_free_sgt(struct sg_table *sgt)
+{
+ struct scatterlist *sg;
+
+ if (!sgt)
+ return;
+
+ for (sg = sgt->sgl; sg; sg = sg_next(sg))
+ if (sg_page(sg))
+ __free_pages(sg_page(sg), get_order(sg->length));
+ sg_free_table(sgt);
+ kfree(sgt);
+}
+
+static void qaic_gem_print_info(struct drm_printer *p, unsigned int indent,
+ const struct drm_gem_object *obj)
+{
+ struct qaic_bo *bo = to_qaic_bo(obj);
+
+ drm_printf_indent(p, indent, "BO DMA direction %d\n", bo->dir);
+}
+
+static const struct vm_operations_struct drm_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct qaic_bo *bo = to_qaic_bo(obj);
+ unsigned long offset = 0;
+ struct scatterlist *sg;
+ int ret = 0;
+
+ if (drm_gem_is_imported(obj))
+ return -EINVAL;
+
+ for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) {
+ if (sg_page(sg)) {
+ ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)),
+ sg->length, vma->vm_page_prot);
+ if (ret)
+ goto out;
+ offset += sg->length;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static void qaic_free_object(struct drm_gem_object *obj)
+{
+ struct qaic_bo *bo = to_qaic_bo(obj);
+
+ if (drm_gem_is_imported(obj)) {
+ /* DMABUF/PRIME Path */
+ drm_prime_gem_destroy(obj, NULL);
+ } else {
+ /* Private buffer allocation path */
+ qaic_free_sgt(bo->sgt);
+ }
+
+ mutex_destroy(&bo->lock);
+ drm_gem_object_release(obj);
+ kfree(bo);
+}
+
+static struct sg_table *qaic_get_sg_table(struct drm_gem_object *obj)
+{
+ struct qaic_bo *bo = to_qaic_bo(obj);
+ struct scatterlist *sg, *sg_in;
+ struct sg_table *sgt, *sgt_in;
+ int i;
+
+ sgt_in = bo->sgt;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ if (sg_alloc_table(sgt, sgt_in->orig_nents, GFP_KERNEL)) {
+ kfree(sgt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sg = sgt->sgl;
+ for_each_sgtable_sg(sgt_in, sg_in, i) {
+ memcpy(sg, sg_in, sizeof(*sg));
+ sg = sg_next(sg);
+ }
+
+ return sgt;
+}
+
+static const struct drm_gem_object_funcs qaic_gem_funcs = {
+ .free = qaic_free_object,
+ .get_sg_table = qaic_get_sg_table,
+ .print_info = qaic_gem_print_info,
+ .mmap = qaic_gem_object_mmap,
+ .vm_ops = &drm_vm_ops,
+};
+
+static void qaic_init_bo(struct qaic_bo *bo, bool reinit)
+{
+ if (reinit) {
+ bo->sliced = false;
+ reinit_completion(&bo->xfer_done);
+ } else {
+ mutex_init(&bo->lock);
+ init_completion(&bo->xfer_done);
+ }
+ complete_all(&bo->xfer_done);
+ INIT_LIST_HEAD(&bo->slices);
+ INIT_LIST_HEAD(&bo->xfer_list);
+}
+
+static struct qaic_bo *qaic_alloc_init_bo(void)
+{
+ struct qaic_bo *bo;
+
+ bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+ if (!bo)
+ return ERR_PTR(-ENOMEM);
+
+ qaic_init_bo(bo, false);
+
+ return bo;
+}
+
+int qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_create_bo *args = data;
+ int usr_rcu_id, qdev_rcu_id;
+ struct drm_gem_object *obj;
+ struct qaic_device *qdev;
+ struct qaic_user *usr;
+ struct qaic_bo *bo;
+ size_t size;
+ int ret;
+
+ if (args->pad)
+ return -EINVAL;
+
+ size = PAGE_ALIGN(args->size);
+ if (size == 0)
+ return -EINVAL;
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ bo = qaic_alloc_init_bo();
+ if (IS_ERR(bo)) {
+ ret = PTR_ERR(bo);
+ goto unlock_dev_srcu;
+ }
+ obj = &bo->base;
+
+ drm_gem_private_object_init(dev, obj, size);
+
+ obj->funcs = &qaic_gem_funcs;
+ ret = create_sgt(qdev, &bo->sgt, size);
+ if (ret)
+ goto free_bo;
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto free_bo;
+
+ ret = drm_gem_handle_create(file_priv, obj, &args->handle);
+ if (ret)
+ goto free_bo;
+
+ drm_gem_object_put(obj);
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+
+ return 0;
+
+free_bo:
+ drm_gem_object_put(obj);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+int qaic_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_mmap_bo *args = data;
+ int usr_rcu_id, qdev_rcu_id;
+ struct drm_gem_object *obj;
+ struct qaic_device *qdev;
+ struct qaic_user *usr;
+ int ret = 0;
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ obj = drm_gem_object_lookup(file_priv, args->handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto unlock_dev_srcu;
+ }
+
+ args->offset = drm_vma_node_offset_addr(&obj->vma_node);
+
+ drm_gem_object_put(obj);
+
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+struct drm_gem_object *qaic_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)
+{
+ struct dma_buf_attachment *attach;
+ struct drm_gem_object *obj;
+ struct qaic_bo *bo;
+ int ret;
+
+ bo = qaic_alloc_init_bo();
+ if (IS_ERR(bo)) {
+ ret = PTR_ERR(bo);
+ goto out;
+ }
+
+ obj = &bo->base;
+ get_dma_buf(dma_buf);
+
+ attach = dma_buf_attach(dma_buf, dev->dev);
+ if (IS_ERR(attach)) {
+ ret = PTR_ERR(attach);
+ goto attach_fail;
+ }
+
+ if (!attach->dmabuf->size) {
+ ret = -EINVAL;
+ goto size_align_fail;
+ }
+
+ drm_gem_private_object_init(dev, obj, attach->dmabuf->size);
+ /*
+ * skipping dma_buf_map_attachment() as we do not know the direction
+ * just yet. Once the direction is known in the subsequent IOCTL to
+ * attach slicing, we can do it then.
+ */
+
+ obj->funcs = &qaic_gem_funcs;
+ obj->import_attach = attach;
+ obj->resv = dma_buf->resv;
+
+ return obj;
+
+size_align_fail:
+ dma_buf_detach(dma_buf, attach);
+attach_fail:
+ dma_buf_put(dma_buf);
+ kfree(bo);
+out:
+ return ERR_PTR(ret);
+}
+
+static int qaic_prepare_import_bo(struct qaic_bo *bo, struct qaic_attach_slice_hdr *hdr)
+{
+ struct drm_gem_object *obj = &bo->base;
+ struct sg_table *sgt;
+ int ret;
+
+ sgt = dma_buf_map_attachment(obj->import_attach, hdr->dir);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ return ret;
+ }
+
+ bo->sgt = sgt;
+
+ return 0;
+}
+
+static int qaic_prepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo,
+ struct qaic_attach_slice_hdr *hdr)
+{
+ int ret;
+
+ ret = dma_map_sgtable(&qdev->pdev->dev, bo->sgt, hdr->dir, 0);
+ if (ret)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int qaic_prepare_bo(struct qaic_device *qdev, struct qaic_bo *bo,
+ struct qaic_attach_slice_hdr *hdr)
+{
+ int ret;
+
+ if (drm_gem_is_imported(&bo->base))
+ ret = qaic_prepare_import_bo(bo, hdr);
+ else
+ ret = qaic_prepare_export_bo(qdev, bo, hdr);
+ bo->dir = hdr->dir;
+ bo->dbc = &qdev->dbc[hdr->dbc_id];
+ bo->nr_slice = hdr->count;
+
+ return ret;
+}
+
+static void qaic_unprepare_import_bo(struct qaic_bo *bo)
+{
+ dma_buf_unmap_attachment(bo->base.import_attach, bo->sgt, bo->dir);
+ bo->sgt = NULL;
+}
+
+static void qaic_unprepare_export_bo(struct qaic_device *qdev, struct qaic_bo *bo)
+{
+ dma_unmap_sgtable(&qdev->pdev->dev, bo->sgt, bo->dir, 0);
+}
+
+static void qaic_unprepare_bo(struct qaic_device *qdev, struct qaic_bo *bo)
+{
+ if (drm_gem_is_imported(&bo->base))
+ qaic_unprepare_import_bo(bo);
+ else
+ qaic_unprepare_export_bo(qdev, bo);
+
+ bo->dir = 0;
+ bo->dbc = NULL;
+ bo->nr_slice = 0;
+}
+
+static void qaic_free_slices_bo(struct qaic_bo *bo)
+{
+ struct bo_slice *slice, *temp;
+
+ list_for_each_entry_safe(slice, temp, &bo->slices, slice)
+ kref_put(&slice->ref_count, free_slice);
+ if (WARN_ON_ONCE(bo->total_slice_nents != 0))
+ bo->total_slice_nents = 0;
+ bo->nr_slice = 0;
+}
+
+static int qaic_attach_slicing_bo(struct qaic_device *qdev, struct qaic_bo *bo,
+ struct qaic_attach_slice_hdr *hdr,
+ struct qaic_attach_slice_entry *slice_ent)
+{
+ int ret, i;
+
+ for (i = 0; i < hdr->count; i++) {
+ ret = qaic_map_one_slice(qdev, bo, &slice_ent[i]);
+ if (ret) {
+ qaic_free_slices_bo(bo);
+ return ret;
+ }
+ }
+
+ if (bo->total_slice_nents > bo->dbc->nelem) {
+ qaic_free_slices_bo(bo);
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_attach_slice_entry *slice_ent;
+ struct qaic_attach_slice *args = data;
+ int rcu_id, usr_rcu_id, qdev_rcu_id;
+ struct dma_bridge_chan *dbc;
+ struct drm_gem_object *obj;
+ struct qaic_device *qdev;
+ unsigned long arg_size;
+ struct qaic_user *usr;
+ u8 __user *user_data;
+ struct qaic_bo *bo;
+ int ret;
+
+ if (args->hdr.count == 0)
+ return -EINVAL;
+
+ if (check_mul_overflow((unsigned long)args->hdr.count,
+ (unsigned long)sizeof(*slice_ent),
+ &arg_size))
+ return -EINVAL;
+
+ if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE))
+ return -EINVAL;
+
+ if (args->data == 0)
+ return -EINVAL;
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ if (args->hdr.dbc_id >= qdev->num_dbc) {
+ ret = -EINVAL;
+ goto unlock_dev_srcu;
+ }
+
+ user_data = u64_to_user_ptr(args->data);
+
+ slice_ent = memdup_user(user_data, arg_size);
+ if (IS_ERR(slice_ent)) {
+ ret = PTR_ERR(slice_ent);
+ goto unlock_dev_srcu;
+ }
+
+ obj = drm_gem_object_lookup(file_priv, args->hdr.handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto free_slice_ent;
+ }
+
+ ret = qaic_validate_req(qdev, slice_ent, args->hdr.count, obj->size);
+ if (ret)
+ goto put_bo;
+
+ bo = to_qaic_bo(obj);
+ ret = mutex_lock_interruptible(&bo->lock);
+ if (ret)
+ goto put_bo;
+
+ if (bo->sliced) {
+ ret = -EINVAL;
+ goto unlock_bo;
+ }
+
+ dbc = &qdev->dbc[args->hdr.dbc_id];
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+ if (dbc->usr != usr) {
+ ret = -EINVAL;
+ goto unlock_ch_srcu;
+ }
+
+ if (dbc->id == qdev->ssr_dbc) {
+ ret = -EPIPE;
+ goto unlock_ch_srcu;
+ }
+
+ ret = qaic_prepare_bo(qdev, bo, &args->hdr);
+ if (ret)
+ goto unlock_ch_srcu;
+
+ ret = qaic_attach_slicing_bo(qdev, bo, &args->hdr, slice_ent);
+ if (ret)
+ goto unprepare_bo;
+
+ if (args->hdr.dir == DMA_TO_DEVICE)
+ dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, args->hdr.dir);
+
+ bo->sliced = true;
+ list_add_tail(&bo->bo_list, &bo->dbc->bo_lists);
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ mutex_unlock(&bo->lock);
+ kfree(slice_ent);
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+
+ return 0;
+
+unprepare_bo:
+ qaic_unprepare_bo(qdev, bo);
+unlock_ch_srcu:
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+unlock_bo:
+ mutex_unlock(&bo->lock);
+put_bo:
+ drm_gem_object_put(obj);
+free_slice_ent:
+ kfree(slice_ent);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+static inline u32 fifo_space_avail(u32 head, u32 tail, u32 q_size)
+{
+ u32 avail = head - tail - 1;
+
+ if (head <= tail)
+ avail += q_size;
+
+ return avail;
+}
+
+static inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, u32 dbc_id,
+ u32 head, u32 *ptail)
+{
+ struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
+ struct dbc_req *reqs = slice->reqs;
+ u32 tail = *ptail;
+ u32 avail;
+
+ avail = fifo_space_avail(head, tail, dbc->nelem);
+ if (avail < slice->nents)
+ return -EAGAIN;
+
+ if (tail + slice->nents > dbc->nelem) {
+ avail = dbc->nelem - tail;
+ avail = min_t(u32, avail, slice->nents);
+ memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * avail);
+ reqs += avail;
+ avail = slice->nents - avail;
+ if (avail)
+ memcpy(dbc->req_q_base, reqs, sizeof(*reqs) * avail);
+ } else {
+ memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * slice->nents);
+ }
+
+ *ptail = (tail + slice->nents) % dbc->nelem;
+
+ return 0;
+}
+
+static inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice,
+ u64 resize, struct dma_bridge_chan *dbc, u32 head,
+ u32 *ptail)
+{
+ struct dbc_req *reqs = slice->reqs;
+ struct dbc_req *last_req;
+ u32 tail = *ptail;
+ u64 last_bytes;
+ u32 first_n;
+ u32 avail;
+
+ avail = fifo_space_avail(head, tail, dbc->nelem);
+
+ /*
+ * After this for loop is complete, first_n represents the index
+ * of the last DMA request of this slice that needs to be
+ * transferred after resizing and last_bytes represents DMA size
+ * of that request.
+ */
+ last_bytes = resize;
+ for (first_n = 0; first_n < slice->nents; first_n++)
+ if (last_bytes > le32_to_cpu(reqs[first_n].len))
+ last_bytes -= le32_to_cpu(reqs[first_n].len);
+ else
+ break;
+
+ if (avail < (first_n + 1))
+ return -EAGAIN;
+
+ if (first_n) {
+ if (tail + first_n > dbc->nelem) {
+ avail = dbc->nelem - tail;
+ avail = min_t(u32, avail, first_n);
+ memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * avail);
+ last_req = reqs + avail;
+ avail = first_n - avail;
+ if (avail)
+ memcpy(dbc->req_q_base, last_req, sizeof(*reqs) * avail);
+ } else {
+ memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * first_n);
+ }
+ }
+
+ /*
+ * Copy over the last entry. Here we need to adjust len to the left over
+ * size, and set src and dst to the entry it is copied to.
+ */
+ last_req = fifo_at(dbc->req_q_base, (tail + first_n) % dbc->nelem);
+ memcpy(last_req, reqs + slice->nents - 1, sizeof(*reqs));
+
+ /*
+ * last_bytes holds size of a DMA segment, maximum DMA segment size is
+ * set to UINT_MAX by qaic and hence last_bytes can never exceed u32
+ * range. So, by down sizing we are not corrupting the value.
+ */
+ last_req->len = cpu_to_le32((u32)last_bytes);
+ last_req->src_addr = reqs[first_n].src_addr;
+ last_req->dest_addr = reqs[first_n].dest_addr;
+ if (!last_bytes)
+ /* Disable DMA transfer */
+ last_req->cmd = GENMASK(7, 2) & reqs[first_n].cmd;
+
+ *ptail = (tail + first_n + 1) % dbc->nelem;
+
+ return 0;
+}
+
+static int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *file_priv,
+ struct qaic_execute_entry *exec, unsigned int count,
+ bool is_partial, struct dma_bridge_chan *dbc, u32 head,
+ u32 *tail)
+{
+ struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec;
+ struct drm_gem_object *obj;
+ struct bo_slice *slice;
+ unsigned long flags;
+ struct qaic_bo *bo;
+ int i, j;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ /*
+ * ref count will be decremented when the transfer of this
+ * buffer is complete. It is inside dbc_irq_threaded_fn().
+ */
+ obj = drm_gem_object_lookup(file_priv,
+ is_partial ? pexec[i].handle : exec[i].handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto failed_to_send_bo;
+ }
+
+ bo = to_qaic_bo(obj);
+ ret = mutex_lock_interruptible(&bo->lock);
+ if (ret)
+ goto failed_to_send_bo;
+
+ if (!bo->sliced) {
+ ret = -EINVAL;
+ goto unlock_bo;
+ }
+
+ if (is_partial && pexec[i].resize > bo->base.size) {
+ ret = -EINVAL;
+ goto unlock_bo;
+ }
+
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ if (bo_queued(bo)) {
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ ret = -EINVAL;
+ goto unlock_bo;
+ }
+
+ bo->req_id = dbc->next_req_id++;
+
+ list_for_each_entry(slice, &bo->slices, slice) {
+ for (j = 0; j < slice->nents; j++)
+ slice->reqs[j].req_id = cpu_to_le16(bo->req_id);
+
+ if (is_partial && (!pexec[i].resize || pexec[i].resize <= slice->offset))
+ /* Configure the slice for no DMA transfer */
+ ret = copy_partial_exec_reqs(qdev, slice, 0, dbc, head, tail);
+ else if (is_partial && pexec[i].resize < slice->offset + slice->size)
+ /* Configure the slice to be partially DMA transferred */
+ ret = copy_partial_exec_reqs(qdev, slice,
+ pexec[i].resize - slice->offset, dbc,
+ head, tail);
+ else
+ ret = copy_exec_reqs(qdev, slice, dbc->id, head, tail);
+ if (ret) {
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ goto unlock_bo;
+ }
+ }
+ reinit_completion(&bo->xfer_done);
+ list_add_tail(&bo->xfer_list, &dbc->xfer_list);
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ dma_sync_sgtable_for_device(&qdev->pdev->dev, bo->sgt, bo->dir);
+ mutex_unlock(&bo->lock);
+ }
+
+ return 0;
+
+unlock_bo:
+ mutex_unlock(&bo->lock);
+failed_to_send_bo:
+ if (likely(obj))
+ drm_gem_object_put(obj);
+ for (j = 0; j < i; j++) {
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ bo = list_last_entry(&dbc->xfer_list, struct qaic_bo, xfer_list);
+ obj = &bo->base;
+ list_del_init(&bo->xfer_list);
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
+ drm_gem_object_put(obj);
+ }
+ return ret;
+}
+
+static void update_profiling_data(struct drm_file *file_priv,
+ struct qaic_execute_entry *exec, unsigned int count,
+ bool is_partial, u64 received_ts, u64 submit_ts, u32 queue_level)
+{
+ struct qaic_partial_execute_entry *pexec = (struct qaic_partial_execute_entry *)exec;
+ struct drm_gem_object *obj;
+ struct qaic_bo *bo;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ /*
+ * Since we already committed the BO to hardware, the only way
+ * this should fail is a pending signal. We can't cancel the
+ * submit to hardware, so we have to just skip the profiling
+ * data. In case the signal is not fatal to the process, we
+ * return success so that the user doesn't try to resubmit.
+ */
+ obj = drm_gem_object_lookup(file_priv,
+ is_partial ? pexec[i].handle : exec[i].handle);
+ if (!obj)
+ break;
+ bo = to_qaic_bo(obj);
+ bo->perf_stats.req_received_ts = received_ts;
+ bo->perf_stats.req_submit_ts = submit_ts;
+ bo->perf_stats.queue_level_before = queue_level;
+ queue_level += bo->total_slice_nents;
+ drm_gem_object_put(obj);
+ }
+}
+
+static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv,
+ bool is_partial)
+{
+ struct qaic_execute *args = data;
+ struct qaic_execute_entry *exec;
+ struct dma_bridge_chan *dbc;
+ int usr_rcu_id, qdev_rcu_id;
+ struct qaic_device *qdev;
+ struct qaic_user *usr;
+ u64 received_ts;
+ u32 queue_level;
+ u64 submit_ts;
+ int rcu_id;
+ u32 head;
+ u32 tail;
+ u64 size;
+ int ret;
+
+ received_ts = ktime_get_ns();
+
+ size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec);
+ if (args->hdr.count == 0)
+ return -EINVAL;
+
+ exec = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, size);
+ if (IS_ERR(exec))
+ return PTR_ERR(exec);
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ if (args->hdr.dbc_id >= qdev->num_dbc) {
+ ret = -EINVAL;
+ goto unlock_dev_srcu;
+ }
+
+ dbc = &qdev->dbc[args->hdr.dbc_id];
+
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+ if (!dbc->usr || dbc->usr->handle != usr->handle) {
+ ret = -EPERM;
+ goto release_ch_rcu;
+ }
+
+ if (dbc->id == qdev->ssr_dbc) {
+ ret = -EPIPE;
+ goto release_ch_rcu;
+ }
+
+ ret = mutex_lock_interruptible(&dbc->req_lock);
+ if (ret)
+ goto release_ch_rcu;
+
+ head = readl(dbc->dbc_base + REQHP_OFF);
+ tail = readl(dbc->dbc_base + REQTP_OFF);
+
+ if (head == U32_MAX || tail == U32_MAX) {
+ /* PCI link error */
+ ret = -ENODEV;
+ goto unlock_req_lock;
+ }
+
+ queue_level = head <= tail ? tail - head : dbc->nelem - (head - tail);
+
+ ret = send_bo_list_to_device(qdev, file_priv, exec, args->hdr.count, is_partial, dbc,
+ head, &tail);
+ if (ret)
+ goto unlock_req_lock;
+
+ /* Finalize commit to hardware */
+ submit_ts = ktime_get_ns();
+ writel(tail, dbc->dbc_base + REQTP_OFF);
+ mutex_unlock(&dbc->req_lock);
+
+ update_profiling_data(file_priv, exec, args->hdr.count, is_partial, received_ts,
+ submit_ts, queue_level);
+
+ if (datapath_polling)
+ schedule_work(&dbc->poll_work);
+
+unlock_req_lock:
+ if (ret)
+ mutex_unlock(&dbc->req_lock);
+release_ch_rcu:
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ kfree(exec);
+ return ret;
+}
+
+int qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ return __qaic_execute_bo_ioctl(dev, data, file_priv, false);
+}
+
+int qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ return __qaic_execute_bo_ioctl(dev, data, file_priv, true);
+}
+
+/*
+ * Our interrupt handling is a bit more complicated than a simple ideal, but
+ * sadly necessary.
+ *
+ * Each dbc has a completion queue. Entries in the queue correspond to DMA
+ * requests which the device has processed. The hardware already has a built
+ * in irq mitigation. When the device puts an entry into the queue, it will
+ * only trigger an interrupt if the queue was empty. Therefore, when adding
+ * the Nth event to a non-empty queue, the hardware doesn't trigger an
+ * interrupt. This means the host doesn't get additional interrupts signaling
+ * the same thing - the queue has something to process.
+ * This behavior can be overridden in the DMA request.
+ * This means that when the host receives an interrupt, it is required to
+ * drain the queue.
+ *
+ * This behavior is what NAPI attempts to accomplish, although we can't use
+ * NAPI as we don't have a netdev. We use threaded irqs instead.
+ *
+ * However, there is a situation where the host drains the queue fast enough
+ * that every event causes an interrupt. Typically this is not a problem as
+ * the rate of events would be low. However, that is not the case with
+ * lprnet for example. On an Intel Xeon D-2191 where we run 8 instances of
+ * lprnet, the host receives roughly 80k interrupts per second from the device
+ * (per /proc/interrupts). While NAPI documentation indicates the host should
+ * just chug along, sadly that behavior causes instability in some hosts.
+ *
+ * Therefore, we implement an interrupt disable scheme similar to NAPI. The
+ * key difference is that we will delay after draining the queue for a small
+ * time to allow additional events to come in via polling. Using the above
+ * lprnet workload, this reduces the number of interrupts processed from
+ * ~80k/sec to about 64 in 5 minutes and appears to solve the system
+ * instability.
+ */
+irqreturn_t dbc_irq_handler(int irq, void *data)
+{
+ struct dma_bridge_chan *dbc = data;
+ int rcu_id;
+ u32 head;
+ u32 tail;
+
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+
+ if (datapath_polling) {
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ /*
+ * Normally datapath_polling will not have irqs enabled, but
+ * when running with only one MSI the interrupt is shared with
+ * MHI so it cannot be disabled. Return ASAP instead.
+ */
+ return IRQ_HANDLED;
+ }
+
+ if (!dbc->usr) {
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return IRQ_HANDLED;
+ }
+
+ head = readl(dbc->dbc_base + RSPHP_OFF);
+ if (head == U32_MAX) { /* PCI link error */
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return IRQ_NONE;
+ }
+
+ tail = readl(dbc->dbc_base + RSPTP_OFF);
+ if (tail == U32_MAX) { /* PCI link error */
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return IRQ_NONE;
+ }
+
+ if (head == tail) { /* queue empty */
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return IRQ_NONE;
+ }
+
+ if (!dbc->qdev->single_msi)
+ disable_irq_nosync(irq);
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return IRQ_WAKE_THREAD;
+}
+
+void qaic_irq_polling_work(struct work_struct *work)
+{
+ struct dma_bridge_chan *dbc = container_of(work, struct dma_bridge_chan, poll_work);
+ unsigned long flags;
+ int rcu_id;
+ u32 head;
+ u32 tail;
+
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+
+ while (1) {
+ if (dbc->qdev->dev_state != QAIC_ONLINE) {
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return;
+ }
+ if (!dbc->usr) {
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return;
+ }
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ if (list_empty(&dbc->xfer_list)) {
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return;
+ }
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+
+ head = readl(dbc->dbc_base + RSPHP_OFF);
+ if (head == U32_MAX) { /* PCI link error */
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return;
+ }
+
+ tail = readl(dbc->dbc_base + RSPTP_OFF);
+ if (tail == U32_MAX) { /* PCI link error */
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return;
+ }
+
+ if (head != tail) {
+ irq_wake_thread(dbc->irq, dbc);
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return;
+ }
+
+ cond_resched();
+ usleep_range(datapath_poll_interval_us, 2 * datapath_poll_interval_us);
+ }
+}
+
+irqreturn_t dbc_irq_threaded_fn(int irq, void *data)
+{
+ struct dma_bridge_chan *dbc = data;
+ int event_count = NUM_EVENTS;
+ int delay_count = NUM_DELAYS;
+ struct qaic_device *qdev;
+ struct qaic_bo *bo, *i;
+ struct dbc_rsp *rsp;
+ unsigned long flags;
+ int rcu_id;
+ u16 status;
+ u16 req_id;
+ u32 head;
+ u32 tail;
+
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+ qdev = dbc->qdev;
+
+ head = readl(dbc->dbc_base + RSPHP_OFF);
+ if (head == U32_MAX) /* PCI link error */
+ goto error_out;
+
+read_fifo:
+
+ if (!event_count) {
+ event_count = NUM_EVENTS;
+ cond_resched();
+ }
+
+ /*
+ * if this channel isn't assigned or gets unassigned during processing
+ * we have nothing further to do
+ */
+ if (!dbc->usr)
+ goto error_out;
+
+ tail = readl(dbc->dbc_base + RSPTP_OFF);
+ if (tail == U32_MAX) /* PCI link error */
+ goto error_out;
+
+ if (head == tail) { /* queue empty */
+ if (delay_count) {
+ --delay_count;
+ usleep_range(100, 200);
+ goto read_fifo; /* check for a new event */
+ }
+ goto normal_out;
+ }
+
+ delay_count = NUM_DELAYS;
+ while (head != tail) {
+ if (!event_count)
+ break;
+ --event_count;
+ rsp = dbc->rsp_q_base + head * sizeof(*rsp);
+ req_id = le16_to_cpu(rsp->req_id);
+ status = le16_to_cpu(rsp->status);
+ if (status)
+ pci_dbg(qdev->pdev, "req_id %d failed with status %d\n", req_id, status);
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ /*
+ * A BO can receive multiple interrupts, since a BO can be
+ * divided into multiple slices and a buffer receives as many
+ * interrupts as slices. So until it receives interrupts for
+ * all the slices we cannot mark that buffer complete.
+ */
+ list_for_each_entry_safe(bo, i, &dbc->xfer_list, xfer_list) {
+ if (bo->req_id == req_id)
+ bo->nr_slice_xfer_done++;
+ else
+ continue;
+
+ if (bo->nr_slice_xfer_done < bo->nr_slice)
+ break;
+
+ /*
+ * At this point we have received all the interrupts for
+ * BO, which means BO execution is complete.
+ */
+ dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
+ bo->nr_slice_xfer_done = 0;
+ list_del_init(&bo->xfer_list);
+ bo->perf_stats.req_processed_ts = ktime_get_ns();
+ complete_all(&bo->xfer_done);
+ drm_gem_object_put(&bo->base);
+ break;
+ }
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ head = (head + 1) % dbc->nelem;
+ }
+
+ /*
+ * Update the head pointer of response queue and let the device know
+ * that we have consumed elements from the queue.
+ */
+ writel(head, dbc->dbc_base + RSPHP_OFF);
+
+ /* elements might have been put in the queue while we were processing */
+ goto read_fifo;
+
+normal_out:
+ if (!qdev->single_msi && likely(!datapath_polling))
+ enable_irq(irq);
+ else if (unlikely(datapath_polling))
+ schedule_work(&dbc->poll_work);
+ /* checking the fifo and enabling irqs is a race, missed event check */
+ tail = readl(dbc->dbc_base + RSPTP_OFF);
+ if (tail != U32_MAX && head != tail) {
+ if (!qdev->single_msi && likely(!datapath_polling))
+ disable_irq_nosync(irq);
+ goto read_fifo;
+ }
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ return IRQ_HANDLED;
+
+error_out:
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+ if (!qdev->single_msi && likely(!datapath_polling))
+ enable_irq(irq);
+ else if (unlikely(datapath_polling))
+ schedule_work(&dbc->poll_work);
+
+ return IRQ_HANDLED;
+}
+
+int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_wait *args = data;
+ int usr_rcu_id, qdev_rcu_id;
+ struct dma_bridge_chan *dbc;
+ struct drm_gem_object *obj;
+ struct qaic_device *qdev;
+ unsigned long timeout;
+ struct qaic_user *usr;
+ struct qaic_bo *bo;
+ int rcu_id;
+ int ret;
+
+ if (args->pad != 0)
+ return -EINVAL;
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ if (args->dbc_id >= qdev->num_dbc) {
+ ret = -EINVAL;
+ goto unlock_dev_srcu;
+ }
+
+ dbc = &qdev->dbc[args->dbc_id];
+
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+ if (dbc->usr != usr) {
+ ret = -EPERM;
+ goto unlock_ch_srcu;
+ }
+
+ if (dbc->id == qdev->ssr_dbc) {
+ ret = -EPIPE;
+ goto unlock_ch_srcu;
+ }
+
+ obj = drm_gem_object_lookup(file_priv, args->handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto unlock_ch_srcu;
+ }
+
+ bo = to_qaic_bo(obj);
+ timeout = args->timeout ? args->timeout : wait_exec_default_timeout_ms;
+ timeout = msecs_to_jiffies(timeout);
+ ret = wait_for_completion_interruptible_timeout(&bo->xfer_done, timeout);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto put_obj;
+ }
+ if (ret > 0)
+ ret = 0;
+
+ if (!dbc->usr)
+ ret = -EPERM;
+
+ if (dbc->id == qdev->ssr_dbc)
+ ret = -EPIPE;
+
+put_obj:
+ drm_gem_object_put(obj);
+unlock_ch_srcu:
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_perf_stats_entry *ent = NULL;
+ struct qaic_perf_stats *args = data;
+ int usr_rcu_id, qdev_rcu_id;
+ struct drm_gem_object *obj;
+ struct qaic_device *qdev;
+ struct qaic_user *usr;
+ struct qaic_bo *bo;
+ int ret = 0;
+ int i;
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ if (args->hdr.dbc_id >= qdev->num_dbc) {
+ ret = -EINVAL;
+ goto unlock_dev_srcu;
+ }
+
+ ent = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, sizeof(*ent));
+ if (IS_ERR(ent)) {
+ ret = PTR_ERR(ent);
+ goto unlock_dev_srcu;
+ }
+
+ for (i = 0; i < args->hdr.count; i++) {
+ obj = drm_gem_object_lookup(file_priv, ent[i].handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto free_ent;
+ }
+ bo = to_qaic_bo(obj);
+ if (!bo->sliced) {
+ drm_gem_object_put(obj);
+ ret = -EINVAL;
+ goto free_ent;
+ }
+ if (bo->dbc->id != args->hdr.dbc_id) {
+ drm_gem_object_put(obj);
+ ret = -EINVAL;
+ goto free_ent;
+ }
+ /*
+ * perf stats ioctl is called before wait ioctl is complete then
+ * the latency information is invalid.
+ */
+ if (bo->perf_stats.req_processed_ts < bo->perf_stats.req_submit_ts) {
+ ent[i].device_latency_us = 0;
+ } else {
+ ent[i].device_latency_us = div_u64((bo->perf_stats.req_processed_ts -
+ bo->perf_stats.req_submit_ts), 1000);
+ }
+ ent[i].submit_latency_us = div_u64((bo->perf_stats.req_submit_ts -
+ bo->perf_stats.req_received_ts), 1000);
+ ent[i].queue_level_before = bo->perf_stats.queue_level_before;
+ ent[i].num_queue_element = bo->total_slice_nents;
+ drm_gem_object_put(obj);
+ }
+
+ if (copy_to_user(u64_to_user_ptr(args->data), ent, args->hdr.count * sizeof(*ent)))
+ ret = -EFAULT;
+
+free_ent:
+ kfree(ent);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+static void detach_slice_bo(struct qaic_device *qdev, struct qaic_bo *bo)
+{
+ qaic_free_slices_bo(bo);
+ qaic_unprepare_bo(qdev, bo);
+ qaic_init_bo(bo, true);
+ list_del(&bo->bo_list);
+ drm_gem_object_put(&bo->base);
+}
+
+int qaic_detach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qaic_detach_slice *args = data;
+ int rcu_id, usr_rcu_id, qdev_rcu_id;
+ struct dma_bridge_chan *dbc;
+ struct drm_gem_object *obj;
+ struct qaic_device *qdev;
+ struct qaic_user *usr;
+ unsigned long flags;
+ struct qaic_bo *bo;
+ int ret;
+
+ if (args->pad != 0)
+ return -EINVAL;
+
+ usr = file_priv->driver_priv;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (!usr->qddev) {
+ ret = -ENODEV;
+ goto unlock_usr_srcu;
+ }
+
+ qdev = usr->qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+
+ obj = drm_gem_object_lookup(file_priv, args->handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto unlock_dev_srcu;
+ }
+
+ bo = to_qaic_bo(obj);
+ ret = mutex_lock_interruptible(&bo->lock);
+ if (ret)
+ goto put_bo;
+
+ if (!bo->sliced) {
+ ret = -EINVAL;
+ goto unlock_bo;
+ }
+
+ dbc = bo->dbc;
+ rcu_id = srcu_read_lock(&dbc->ch_lock);
+ if (dbc->usr != usr) {
+ ret = -EINVAL;
+ goto unlock_ch_srcu;
+ }
+
+ /* Check if BO is committed to H/W for DMA */
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ if (bo_queued(bo)) {
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ ret = -EBUSY;
+ goto unlock_ch_srcu;
+ }
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+
+ detach_slice_bo(qdev, bo);
+
+unlock_ch_srcu:
+ srcu_read_unlock(&dbc->ch_lock, rcu_id);
+unlock_bo:
+ mutex_unlock(&bo->lock);
+put_bo:
+ drm_gem_object_put(obj);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+unlock_usr_srcu:
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ return ret;
+}
+
+static void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc)
+{
+ unsigned long flags;
+ struct qaic_bo *bo;
+
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ while (!list_empty(&dbc->xfer_list)) {
+ bo = list_first_entry(&dbc->xfer_list, typeof(*bo), xfer_list);
+ list_del_init(&bo->xfer_list);
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ bo->nr_slice_xfer_done = 0;
+ bo->req_id = 0;
+ bo->perf_stats.req_received_ts = 0;
+ bo->perf_stats.req_submit_ts = 0;
+ bo->perf_stats.req_processed_ts = 0;
+ bo->perf_stats.queue_level_before = 0;
+ dma_sync_sgtable_for_cpu(&qdev->pdev->dev, bo->sgt, bo->dir);
+ complete_all(&bo->xfer_done);
+ drm_gem_object_put(&bo->base);
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ }
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+}
+
+static void sync_empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc)
+{
+ empty_xfer_list(qdev, dbc);
+ synchronize_srcu(&dbc->ch_lock);
+ /*
+ * Threads holding channel lock, may add more elements in the xfer_list.
+ * Flush out these elements from xfer_list.
+ */
+ empty_xfer_list(qdev, dbc);
+}
+
+int disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
+{
+ if (!qdev->dbc[dbc_id].usr || qdev->dbc[dbc_id].usr->handle != usr->handle)
+ return -EPERM;
+
+ qdev->dbc[dbc_id].usr = NULL;
+ synchronize_srcu(&qdev->dbc[dbc_id].ch_lock);
+ return 0;
+}
+
+/**
+ * enable_dbc - Enable the DBC. DBCs are disabled by removing the context of
+ * user. Add user context back to DBC to enable it. This function trusts the
+ * DBC ID passed and expects the DBC to be disabled.
+ * @qdev: qaic device handle
+ * @dbc_id: ID of the DBC
+ * @usr: User context
+ */
+void enable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
+{
+ qdev->dbc[dbc_id].usr = usr;
+}
+
+void wakeup_dbc(struct qaic_device *qdev, u32 dbc_id)
+{
+ struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
+
+ dbc->usr = NULL;
+ sync_empty_xfer_list(qdev, dbc);
+}
+
+void release_dbc(struct qaic_device *qdev, u32 dbc_id)
+{
+ struct qaic_bo *bo, *bo_temp;
+ struct dma_bridge_chan *dbc;
+
+ dbc = &qdev->dbc[dbc_id];
+ if (!dbc->in_use)
+ return;
+
+ wakeup_dbc(qdev, dbc_id);
+
+ dma_free_coherent(&qdev->pdev->dev, dbc->total_size, dbc->req_q_base, dbc->dma_addr);
+ dbc->total_size = 0;
+ dbc->req_q_base = NULL;
+ dbc->dma_addr = 0;
+ dbc->nelem = 0;
+ dbc->usr = NULL;
+
+ list_for_each_entry_safe(bo, bo_temp, &dbc->bo_lists, bo_list) {
+ drm_gem_object_get(&bo->base);
+ mutex_lock(&bo->lock);
+ detach_slice_bo(qdev, bo);
+ mutex_unlock(&bo->lock);
+ drm_gem_object_put(&bo->base);
+ }
+
+ dbc->in_use = false;
+ wake_up(&dbc->dbc_release);
+}
+
+void qaic_data_get_fifo_info(struct dma_bridge_chan *dbc, u32 *head, u32 *tail)
+{
+ if (!dbc || !head || !tail)
+ return;
+
+ *head = readl(dbc->dbc_base + REQHP_OFF);
+ *tail = readl(dbc->dbc_base + REQTP_OFF);
+}
+
+/*
+ * qaic_dbc_enter_ssr - Prepare to enter in sub system reset(SSR) for given DBC ID.
+ * @qdev: qaic device handle
+ * @dbc_id: ID of the DBC which will enter SSR
+ *
+ * The device will automatically deactivate the workload as not
+ * all errors can be silently recovered. The user will be
+ * notified and will need to decide the required recovery
+ * action to take.
+ */
+void qaic_dbc_enter_ssr(struct qaic_device *qdev, u32 dbc_id)
+{
+ qdev->ssr_dbc = dbc_id;
+ release_dbc(qdev, dbc_id);
+}
+
+/*
+ * qaic_dbc_exit_ssr - Prepare to exit from sub system reset(SSR) for given DBC ID.
+ * @qdev: qaic device handle
+ *
+ * The DBC returns to an operational state and begins accepting work after exiting SSR.
+ */
+void qaic_dbc_exit_ssr(struct qaic_device *qdev)
+{
+ qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
+}
diff --git a/drivers/accel/qaic/qaic_debugfs.c b/drivers/accel/qaic/qaic_debugfs.c
new file mode 100644
index 000000000000..8dc4fe5bb560
--- /dev/null
+++ b/drivers/accel/qaic/qaic_debugfs.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/mhi.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "qaic.h"
+#include "qaic_debugfs.h"
+
+#define BOOTLOG_POOL_SIZE 16
+#define BOOTLOG_MSG_SIZE 512
+#define QAIC_DBC_DIR_NAME 9
+
+struct bootlog_msg {
+ /* Buffer for bootlog messages */
+ char str[BOOTLOG_MSG_SIZE];
+ /* Root struct of device, used to access device resources */
+ struct qaic_device *qdev;
+ /* Work struct to schedule work coming on QAIC_LOGGING channel */
+ struct work_struct work;
+};
+
+struct bootlog_page {
+ /* Node in list of bootlog pages maintained by root device struct */
+ struct list_head node;
+ /* Total size of the buffer that holds the bootlogs. It is PAGE_SIZE */
+ unsigned int size;
+ /* Offset for the next bootlog */
+ unsigned int offset;
+};
+
+static int bootlog_show(struct seq_file *s, void *unused)
+{
+ struct bootlog_page *page;
+ struct qaic_device *qdev;
+ void *page_end;
+ void *log;
+
+ qdev = s->private;
+ mutex_lock(&qdev->bootlog_mutex);
+ list_for_each_entry(page, &qdev->bootlog, node) {
+ log = page + 1;
+ page_end = (void *)page + page->offset;
+ while (log < page_end) {
+ seq_printf(s, "%s", (char *)log);
+ log += strlen(log) + 1;
+ }
+ }
+ mutex_unlock(&qdev->bootlog_mutex);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(bootlog);
+
+static int fifo_size_show(struct seq_file *s, void *unused)
+{
+ struct dma_bridge_chan *dbc = s->private;
+
+ seq_printf(s, "%u\n", dbc->nelem);
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(fifo_size);
+
+static int queued_show(struct seq_file *s, void *unused)
+{
+ struct dma_bridge_chan *dbc = s->private;
+ u32 tail = 0, head = 0;
+
+ qaic_data_get_fifo_info(dbc, &head, &tail);
+
+ if (head == U32_MAX || tail == U32_MAX)
+ seq_printf(s, "%u\n", 0);
+ else if (head > tail)
+ seq_printf(s, "%u\n", dbc->nelem - head + tail);
+ else
+ seq_printf(s, "%u\n", tail - head);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(queued);
+
+void qaic_debugfs_init(struct qaic_drm_device *qddev)
+{
+ struct qaic_device *qdev = qddev->qdev;
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_dir;
+ char name[QAIC_DBC_DIR_NAME];
+ u32 i;
+
+ debugfs_root = to_drm(qddev)->debugfs_root;
+
+ debugfs_create_file("bootlog", 0400, debugfs_root, qdev, &bootlog_fops);
+ /*
+ * 256 dbcs per device is likely the max we will ever see and lets static checking see a
+ * reasonable range.
+ */
+ for (i = 0; i < qdev->num_dbc && i < 256; ++i) {
+ snprintf(name, QAIC_DBC_DIR_NAME, "dbc%03u", i);
+ debugfs_dir = debugfs_create_dir(name, debugfs_root);
+ debugfs_create_file("fifo_size", 0400, debugfs_dir, &qdev->dbc[i], &fifo_size_fops);
+ debugfs_create_file("queued", 0400, debugfs_dir, &qdev->dbc[i], &queued_fops);
+ }
+}
+
+static struct bootlog_page *alloc_bootlog_page(struct qaic_device *qdev)
+{
+ struct bootlog_page *page;
+
+ page = (struct bootlog_page *)devm_get_free_pages(&qdev->pdev->dev, GFP_KERNEL, 0);
+ if (!page)
+ return page;
+
+ page->size = PAGE_SIZE;
+ page->offset = sizeof(*page);
+ list_add_tail(&page->node, &qdev->bootlog);
+
+ return page;
+}
+
+static int reset_bootlog(struct qaic_device *qdev)
+{
+ struct bootlog_page *page;
+ struct bootlog_page *i;
+
+ mutex_lock(&qdev->bootlog_mutex);
+ list_for_each_entry_safe(page, i, &qdev->bootlog, node) {
+ list_del(&page->node);
+ devm_free_pages(&qdev->pdev->dev, (unsigned long)page);
+ }
+
+ page = alloc_bootlog_page(qdev);
+ mutex_unlock(&qdev->bootlog_mutex);
+ if (!page)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void *bootlog_get_space(struct qaic_device *qdev, unsigned int size)
+{
+ struct bootlog_page *page;
+
+ page = list_last_entry(&qdev->bootlog, struct bootlog_page, node);
+
+ if (size_add(size, sizeof(*page)) > page->size)
+ return NULL;
+
+ if (page->offset + size > page->size) {
+ page = alloc_bootlog_page(qdev);
+ if (!page)
+ return NULL;
+ }
+
+ return (void *)page + page->offset;
+}
+
+static void bootlog_commit(struct qaic_device *qdev, unsigned int size)
+{
+ struct bootlog_page *page;
+
+ page = list_last_entry(&qdev->bootlog, struct bootlog_page, node);
+
+ page->offset += size;
+}
+
+static void bootlog_log(struct work_struct *work)
+{
+ struct bootlog_msg *msg = container_of(work, struct bootlog_msg, work);
+ unsigned int len = strlen(msg->str) + 1;
+ struct qaic_device *qdev = msg->qdev;
+ void *log;
+
+ mutex_lock(&qdev->bootlog_mutex);
+ log = bootlog_get_space(qdev, len);
+ if (log) {
+ memcpy(log, msg, len);
+ bootlog_commit(qdev, len);
+ }
+ mutex_unlock(&qdev->bootlog_mutex);
+
+ if (mhi_queue_buf(qdev->bootlog_ch, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, MHI_EOT))
+ devm_kfree(&qdev->pdev->dev, msg);
+}
+
+static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ struct bootlog_msg *msg;
+ int i, ret;
+
+ qdev->bootlog_wq = alloc_ordered_workqueue("qaic_bootlog", 0);
+ if (!qdev->bootlog_wq) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = reset_bootlog(qdev);
+ if (ret)
+ goto destroy_workqueue;
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ goto destroy_workqueue;
+
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+ qdev->bootlog_ch = mhi_dev;
+
+ for (i = 0; i < BOOTLOG_POOL_SIZE; i++) {
+ msg = devm_kzalloc(&qdev->pdev->dev, sizeof(*msg), GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto mhi_unprepare;
+ }
+
+ msg->qdev = qdev;
+ INIT_WORK(&msg->work, bootlog_log);
+
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, MHI_EOT);
+ if (ret)
+ goto mhi_unprepare;
+ }
+
+ return 0;
+
+mhi_unprepare:
+ mhi_unprepare_from_transfer(mhi_dev);
+destroy_workqueue:
+ destroy_workqueue(qdev->bootlog_wq);
+out:
+ return ret;
+}
+
+static void qaic_bootlog_mhi_remove(struct mhi_device *mhi_dev)
+{
+ struct qaic_device *qdev;
+
+ qdev = dev_get_drvdata(&mhi_dev->dev);
+
+ mhi_unprepare_from_transfer(qdev->bootlog_ch);
+ destroy_workqueue(qdev->bootlog_wq);
+ qdev->bootlog_ch = NULL;
+}
+
+static void qaic_bootlog_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+}
+
+static void qaic_bootlog_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct bootlog_msg *msg = mhi_result->buf_addr;
+
+ if (mhi_result->transaction_status) {
+ devm_kfree(&qdev->pdev->dev, msg);
+ return;
+ }
+
+ /* Force a null at the end of the transferred string */
+ msg->str[mhi_result->bytes_xferd - 1] = 0;
+
+ queue_work(qdev->bootlog_wq, &msg->work);
+}
+
+static const struct mhi_device_id qaic_bootlog_mhi_match_table[] = {
+ { .chan = "QAIC_LOGGING", },
+ {},
+};
+
+static struct mhi_driver qaic_bootlog_mhi_driver = {
+ .id_table = qaic_bootlog_mhi_match_table,
+ .remove = qaic_bootlog_mhi_remove,
+ .probe = qaic_bootlog_mhi_probe,
+ .ul_xfer_cb = qaic_bootlog_mhi_ul_xfer_cb,
+ .dl_xfer_cb = qaic_bootlog_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_bootlog",
+ },
+};
+
+int qaic_bootlog_register(void)
+{
+ return mhi_driver_register(&qaic_bootlog_mhi_driver);
+}
+
+void qaic_bootlog_unregister(void)
+{
+ mhi_driver_unregister(&qaic_bootlog_mhi_driver);
+}
diff --git a/drivers/accel/qaic/qaic_debugfs.h b/drivers/accel/qaic/qaic_debugfs.h
new file mode 100644
index 000000000000..05e74f84cf9f
--- /dev/null
+++ b/drivers/accel/qaic/qaic_debugfs.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#ifndef __QAIC_DEBUGFS_H__
+#define __QAIC_DEBUGFS_H__
+
+#include <drm/drm_file.h>
+
+#ifdef CONFIG_DEBUG_FS
+int qaic_bootlog_register(void);
+void qaic_bootlog_unregister(void);
+void qaic_debugfs_init(struct qaic_drm_device *qddev);
+#else
+static inline int qaic_bootlog_register(void) { return 0; }
+static inline void qaic_bootlog_unregister(void) {}
+static inline void qaic_debugfs_init(struct qaic_drm_device *qddev) {}
+#endif /* CONFIG_DEBUG_FS */
+#endif /* __QAIC_DEBUGFS_H__ */
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
new file mode 100644
index 000000000000..4c70bd949d53
--- /dev/null
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -0,0 +1,860 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/kref.h>
+#include <linux/mhi.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_managed.h>
+#include <uapi/drm/qaic_accel.h>
+
+#include "mhi_controller.h"
+#include "qaic.h"
+#include "qaic_debugfs.h"
+#include "qaic_ras.h"
+#include "qaic_ssr.h"
+#include "qaic_timesync.h"
+#include "sahara.h"
+
+MODULE_IMPORT_NS("DMA_BUF");
+
+#define PCI_DEVICE_ID_QCOM_AIC080 0xa080
+#define PCI_DEVICE_ID_QCOM_AIC100 0xa100
+#define PCI_DEVICE_ID_QCOM_AIC200 0xa110
+#define QAIC_NAME "qaic"
+#define QAIC_DESC "Qualcomm Cloud AI Accelerators"
+#define CNTL_MAJOR 5
+#define CNTL_MINOR 0
+
+struct qaic_device_config {
+ /* Indicates the AIC family the device belongs to */
+ int family;
+ /* A bitmask representing the available BARs */
+ int bar_mask;
+ /* An index value used to identify the MHI controller BAR */
+ unsigned int mhi_bar_idx;
+ /* An index value used to identify the DBCs BAR */
+ unsigned int dbc_bar_idx;
+};
+
+static const struct qaic_device_config aic080_config = {
+ .family = FAMILY_AIC100,
+ .bar_mask = BIT(0) | BIT(2) | BIT(4),
+ .mhi_bar_idx = 0,
+ .dbc_bar_idx = 2,
+};
+
+static const struct qaic_device_config aic100_config = {
+ .family = FAMILY_AIC100,
+ .bar_mask = BIT(0) | BIT(2) | BIT(4),
+ .mhi_bar_idx = 0,
+ .dbc_bar_idx = 2,
+};
+
+static const struct qaic_device_config aic200_config = {
+ .family = FAMILY_AIC200,
+ .bar_mask = BIT(0) | BIT(1) | BIT(2) | BIT(4),
+ .mhi_bar_idx = 1,
+ .dbc_bar_idx = 2,
+};
+
+bool datapath_polling;
+module_param(datapath_polling, bool, 0400);
+MODULE_PARM_DESC(datapath_polling, "Operate the datapath in polling mode");
+static bool link_up;
+static DEFINE_IDA(qaic_usrs);
+
+static void qaicm_wq_release(struct drm_device *dev, void *res)
+{
+ struct workqueue_struct *wq = res;
+
+ destroy_workqueue(wq);
+}
+
+static struct workqueue_struct *qaicm_wq_init(struct drm_device *dev, const char *name)
+{
+ struct workqueue_struct *wq;
+ int ret;
+
+ wq = alloc_workqueue("%s", WQ_UNBOUND, 0, name);
+ if (!wq)
+ return ERR_PTR(-ENOMEM);
+ ret = drmm_add_action_or_reset(dev, qaicm_wq_release, wq);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return wq;
+}
+
+static void qaicm_srcu_release(struct drm_device *dev, void *res)
+{
+ struct srcu_struct *lock = res;
+
+ cleanup_srcu_struct(lock);
+}
+
+static int qaicm_srcu_init(struct drm_device *dev, struct srcu_struct *lock)
+{
+ int ret;
+
+ ret = init_srcu_struct(lock);
+ if (ret)
+ return ret;
+
+ return drmm_add_action_or_reset(dev, qaicm_srcu_release, lock);
+}
+
+static void qaicm_pci_release(struct drm_device *dev, void *res)
+{
+ struct qaic_device *qdev = to_qaic_device(dev);
+
+ pci_set_drvdata(qdev->pdev, NULL);
+}
+
+static void free_usr(struct kref *kref)
+{
+ struct qaic_user *usr = container_of(kref, struct qaic_user, ref_count);
+
+ cleanup_srcu_struct(&usr->qddev_lock);
+ ida_free(&qaic_usrs, usr->handle);
+ kfree(usr);
+}
+
+static int qaic_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct qaic_drm_device *qddev = to_qaic_drm_device(dev);
+ struct qaic_device *qdev = qddev->qdev;
+ struct qaic_user *usr;
+ int rcu_id;
+ int ret;
+
+ rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ ret = -ENODEV;
+ goto dev_unlock;
+ }
+
+ usr = kmalloc(sizeof(*usr), GFP_KERNEL);
+ if (!usr) {
+ ret = -ENOMEM;
+ goto dev_unlock;
+ }
+
+ usr->handle = ida_alloc(&qaic_usrs, GFP_KERNEL);
+ if (usr->handle < 0) {
+ ret = usr->handle;
+ goto free_usr;
+ }
+ usr->qddev = qddev;
+ atomic_set(&usr->chunk_id, 0);
+ init_srcu_struct(&usr->qddev_lock);
+ kref_init(&usr->ref_count);
+
+ ret = mutex_lock_interruptible(&qddev->users_mutex);
+ if (ret)
+ goto cleanup_usr;
+
+ list_add(&usr->node, &qddev->users);
+ mutex_unlock(&qddev->users_mutex);
+
+ file->driver_priv = usr;
+
+ srcu_read_unlock(&qdev->dev_lock, rcu_id);
+ return 0;
+
+cleanup_usr:
+ cleanup_srcu_struct(&usr->qddev_lock);
+ ida_free(&qaic_usrs, usr->handle);
+free_usr:
+ kfree(usr);
+dev_unlock:
+ srcu_read_unlock(&qdev->dev_lock, rcu_id);
+ return ret;
+}
+
+static void qaic_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ struct qaic_user *usr = file->driver_priv;
+ struct qaic_drm_device *qddev;
+ struct qaic_device *qdev;
+ int qdev_rcu_id;
+ int usr_rcu_id;
+ int i;
+
+ qddev = usr->qddev;
+ usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
+ if (qddev) {
+ qdev = qddev->qdev;
+ qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state == QAIC_ONLINE) {
+ qaic_release_usr(qdev, usr);
+ for (i = 0; i < qdev->num_dbc; ++i)
+ if (qdev->dbc[i].usr && qdev->dbc[i].usr->handle == usr->handle)
+ release_dbc(qdev, i);
+ }
+ srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
+
+ mutex_lock(&qddev->users_mutex);
+ if (!list_empty(&usr->node))
+ list_del_init(&usr->node);
+ mutex_unlock(&qddev->users_mutex);
+ }
+
+ srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
+ kref_put(&usr->ref_count, free_usr);
+
+ file->driver_priv = NULL;
+}
+
+DEFINE_DRM_ACCEL_FOPS(qaic_accel_fops);
+
+static const struct drm_ioctl_desc qaic_drm_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(QAIC_MANAGE, qaic_manage_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_CREATE_BO, qaic_create_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_MMAP_BO, qaic_mmap_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_ATTACH_SLICE_BO, qaic_attach_slice_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_EXECUTE_BO, qaic_execute_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_PARTIAL_EXECUTE_BO, qaic_partial_execute_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_WAIT_BO, qaic_wait_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_PERF_STATS_BO, qaic_perf_stats_bo_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(QAIC_DETACH_SLICE_BO, qaic_detach_slice_bo_ioctl, 0),
+};
+
+static const struct drm_driver qaic_accel_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_COMPUTE_ACCEL,
+
+ .name = QAIC_NAME,
+ .desc = QAIC_DESC,
+
+ .fops = &qaic_accel_fops,
+ .open = qaic_open,
+ .postclose = qaic_postclose,
+
+ .ioctls = qaic_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(qaic_drm_ioctls),
+ .gem_prime_import = qaic_gem_prime_import,
+};
+
+static int qaic_create_drm_device(struct qaic_device *qdev, s32 partition_id)
+{
+ struct qaic_drm_device *qddev = qdev->qddev;
+ struct drm_device *drm = to_drm(qddev);
+ int ret;
+
+ /* Hold off implementing partitions until the uapi is determined */
+ if (partition_id != QAIC_NO_PARTITION)
+ return -EINVAL;
+
+ qddev->partition_id = partition_id;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret) {
+ pci_dbg(qdev->pdev, "drm_dev_register failed %d\n", ret);
+ return ret;
+ }
+
+ ret = qaic_sysfs_init(qddev);
+ if (ret) {
+ drm_dev_unregister(drm);
+ pci_dbg(qdev->pdev, "qaic_sysfs_init failed %d\n", ret);
+ return ret;
+ }
+
+ qaic_debugfs_init(qddev);
+
+ return ret;
+}
+
+static void qaic_destroy_drm_device(struct qaic_device *qdev, s32 partition_id)
+{
+ struct qaic_drm_device *qddev = qdev->qddev;
+ struct drm_device *drm = to_drm(qddev);
+ struct qaic_user *usr;
+
+ qaic_sysfs_remove(qddev);
+ drm_dev_unregister(drm);
+ qddev->partition_id = 0;
+ /*
+ * Existing users get unresolvable errors till they close FDs.
+ * Need to sync carefully with users calling close(). The
+ * list of users can be modified elsewhere when the lock isn't
+ * held here, but the sync'ing the srcu with the mutex held
+ * could deadlock. Grab the mutex so that the list will be
+ * unmodified. The user we get will exist as long as the
+ * lock is held. Signal that the qcdev is going away, and
+ * grab a reference to the user so they don't go away for
+ * synchronize_srcu(). Then release the mutex to avoid
+ * deadlock and make sure the user has observed the signal.
+ * With the lock released, we cannot maintain any state of the
+ * user list.
+ */
+ mutex_lock(&qddev->users_mutex);
+ while (!list_empty(&qddev->users)) {
+ usr = list_first_entry(&qddev->users, struct qaic_user, node);
+ list_del_init(&usr->node);
+ kref_get(&usr->ref_count);
+ usr->qddev = NULL;
+ mutex_unlock(&qddev->users_mutex);
+ synchronize_srcu(&usr->qddev_lock);
+ kref_put(&usr->ref_count, free_usr);
+ mutex_lock(&qddev->users_mutex);
+ }
+ mutex_unlock(&qddev->users_mutex);
+}
+
+static int qaic_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ u16 major = -1, minor = -1;
+ struct qaic_device *qdev;
+ int ret;
+
+ /*
+ * Invoking this function indicates that the control channel to the
+ * device is available. We use that as a signal to indicate that
+ * the device side firmware has booted. The device side firmware
+ * manages the device resources, so we need to communicate with it
+ * via the control channel in order to utilize the device. Therefore
+ * we wait until this signal to create the drm dev that userspace will
+ * use to control the device, because without the device side firmware,
+ * userspace can't do anything useful.
+ */
+
+ qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+ qdev->cntl_ch = mhi_dev;
+
+ ret = qaic_control_open(qdev);
+ if (ret) {
+ pci_dbg(qdev->pdev, "%s: control_open failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ qdev->dev_state = QAIC_BOOT;
+ ret = get_cntl_version(qdev, NULL, &major, &minor);
+ if (ret || major != CNTL_MAJOR || minor > CNTL_MINOR) {
+ pci_err(qdev->pdev, "%s: Control protocol version (%d.%d) not supported. Supported version is (%d.%d). Ret: %d\n",
+ __func__, major, minor, CNTL_MAJOR, CNTL_MINOR, ret);
+ ret = -EINVAL;
+ goto close_control;
+ }
+ qdev->dev_state = QAIC_ONLINE;
+ kobject_uevent(&(to_accel_kdev(qdev->qddev))->kobj, KOBJ_ONLINE);
+
+ return ret;
+
+close_control:
+ qaic_control_close(qdev);
+ return ret;
+}
+
+static void qaic_mhi_remove(struct mhi_device *mhi_dev)
+{
+/* This is redundant since we have already observed the device crash */
+}
+
+static void qaic_notify_reset(struct qaic_device *qdev)
+{
+ int i;
+
+ kobject_uevent(&(to_accel_kdev(qdev->qddev))->kobj, KOBJ_OFFLINE);
+ qdev->dev_state = QAIC_OFFLINE;
+ /* wake up any waiters to avoid waiting for timeouts at sync */
+ wake_all_cntl(qdev);
+ for (i = 0; i < qdev->num_dbc; ++i)
+ wakeup_dbc(qdev, i);
+ synchronize_srcu(&qdev->dev_lock);
+}
+
+void qaic_dev_reset_clean_local_state(struct qaic_device *qdev)
+{
+ int i;
+
+ qaic_notify_reset(qdev);
+
+ /* start tearing things down */
+ qaic_clean_up_ssr(qdev);
+ for (i = 0; i < qdev->num_dbc; ++i)
+ release_dbc(qdev, i);
+}
+
+static struct qaic_device *create_qdev(struct pci_dev *pdev,
+ const struct qaic_device_config *config)
+{
+ struct device *dev = &pdev->dev;
+ struct qaic_drm_device *qddev;
+ struct qaic_device *qdev;
+ struct drm_device *drm;
+ int i, ret;
+
+ qdev = devm_kzalloc(dev, sizeof(*qdev), GFP_KERNEL);
+ if (!qdev)
+ return NULL;
+
+ qdev->dev_state = QAIC_OFFLINE;
+ qdev->num_dbc = 16;
+ qdev->dbc = devm_kcalloc(dev, qdev->num_dbc, sizeof(*qdev->dbc), GFP_KERNEL);
+ if (!qdev->dbc)
+ return NULL;
+
+ qddev = devm_drm_dev_alloc(&pdev->dev, &qaic_accel_driver, struct qaic_drm_device, drm);
+ if (IS_ERR(qddev))
+ return NULL;
+
+ drm = to_drm(qddev);
+ pci_set_drvdata(pdev, qdev);
+
+ ret = drmm_mutex_init(drm, &qddev->users_mutex);
+ if (ret)
+ return NULL;
+ ret = drmm_add_action_or_reset(drm, qaicm_pci_release, NULL);
+ if (ret)
+ return NULL;
+ ret = drmm_mutex_init(drm, &qdev->cntl_mutex);
+ if (ret)
+ return NULL;
+ ret = drmm_mutex_init(drm, &qdev->bootlog_mutex);
+ if (ret)
+ return NULL;
+
+ qdev->cntl_wq = qaicm_wq_init(drm, "qaic_cntl");
+ if (IS_ERR(qdev->cntl_wq))
+ return NULL;
+ qdev->qts_wq = qaicm_wq_init(drm, "qaic_ts");
+ if (IS_ERR(qdev->qts_wq))
+ return NULL;
+ qdev->ssr_wq = qaicm_wq_init(drm, "qaic_ssr");
+ if (IS_ERR(qdev->ssr_wq))
+ return NULL;
+
+ ret = qaicm_srcu_init(drm, &qdev->dev_lock);
+ if (ret)
+ return NULL;
+
+ ret = qaic_ssr_init(qdev, drm);
+ if (ret)
+ pci_info(pdev, "QAIC SSR crashdump collection not supported.\n");
+
+ qdev->qddev = qddev;
+ qdev->pdev = pdev;
+ qddev->qdev = qdev;
+
+ INIT_LIST_HEAD(&qdev->cntl_xfer_list);
+ INIT_LIST_HEAD(&qdev->bootlog);
+ INIT_LIST_HEAD(&qddev->users);
+
+ for (i = 0; i < qdev->num_dbc; ++i) {
+ spin_lock_init(&qdev->dbc[i].xfer_lock);
+ qdev->dbc[i].qdev = qdev;
+ qdev->dbc[i].id = i;
+ INIT_LIST_HEAD(&qdev->dbc[i].xfer_list);
+ ret = qaicm_srcu_init(drm, &qdev->dbc[i].ch_lock);
+ if (ret)
+ return NULL;
+ init_waitqueue_head(&qdev->dbc[i].dbc_release);
+ INIT_LIST_HEAD(&qdev->dbc[i].bo_lists);
+ ret = drmm_mutex_init(drm, &qdev->dbc[i].req_lock);
+ if (ret)
+ return NULL;
+ }
+
+ return qdev;
+}
+
+static int init_pci(struct qaic_device *qdev, struct pci_dev *pdev,
+ const struct qaic_device_config *config)
+{
+ int bars;
+ int ret;
+
+ bars = pci_select_bars(pdev, IORESOURCE_MEM) & 0x3f;
+
+ /* make sure the device has the expected BARs */
+ if (bars != config->bar_mask) {
+ pci_dbg(pdev, "%s: expected BARs %#x not found in device. Found %#x\n",
+ __func__, config->bar_mask, bars);
+ return -EINVAL;
+ }
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (ret)
+ return ret;
+ dma_set_max_seg_size(&pdev->dev, UINT_MAX);
+
+ qdev->bar_mhi = devm_ioremap_resource(&pdev->dev, &pdev->resource[config->mhi_bar_idx]);
+ if (IS_ERR(qdev->bar_mhi))
+ return PTR_ERR(qdev->bar_mhi);
+
+ qdev->bar_dbc = devm_ioremap_resource(&pdev->dev, &pdev->resource[config->dbc_bar_idx]);
+ if (IS_ERR(qdev->bar_dbc))
+ return PTR_ERR(qdev->bar_dbc);
+
+ /* Managed release since we use pcim_enable_device above */
+ pci_set_master(pdev);
+
+ return 0;
+}
+
+static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
+{
+ int irq_count = qdev->num_dbc + 1;
+ int mhi_irq;
+ int ret;
+ int i;
+
+ /* Managed release since we use pcim_enable_device */
+ ret = pci_alloc_irq_vectors(pdev, irq_count, irq_count, PCI_IRQ_MSI | PCI_IRQ_MSIX);
+ if (ret == -ENOSPC) {
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Operate in one MSI mode. All interrupts will be directed to
+ * MSI0; every interrupt will wake up all the interrupt handlers
+ * (MHI and DBC[0-15]). Since the interrupt is now shared, it is
+ * not disabled during DBC threaded handler, but only one thread
+ * will be allowed to run per DBC, so while it can be
+ * interrupted, it shouldn't race with itself.
+ */
+ qdev->single_msi = true;
+ pci_info(pdev, "Allocating %d MSIs failed, operating in 1 MSI mode. Performance may be impacted.\n",
+ irq_count);
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ mhi_irq = pci_irq_vector(pdev, 0);
+ if (mhi_irq < 0)
+ return mhi_irq;
+
+ for (i = 0; i < qdev->num_dbc; ++i) {
+ ret = devm_request_threaded_irq(&pdev->dev,
+ pci_irq_vector(pdev, qdev->single_msi ? 0 : i + 1),
+ dbc_irq_handler, dbc_irq_threaded_fn, IRQF_SHARED,
+ "qaic_dbc", &qdev->dbc[i]);
+ if (ret)
+ return ret;
+
+ if (datapath_polling) {
+ qdev->dbc[i].irq = pci_irq_vector(pdev, qdev->single_msi ? 0 : i + 1);
+ if (!qdev->single_msi)
+ disable_irq_nosync(qdev->dbc[i].irq);
+ INIT_WORK(&qdev->dbc[i].poll_work, qaic_irq_polling_work);
+ }
+ }
+
+ return mhi_irq;
+}
+
+static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct qaic_device_config *config = (struct qaic_device_config *)id->driver_data;
+ struct qaic_device *qdev;
+ int mhi_irq;
+ int ret;
+ int i;
+
+ qdev = create_qdev(pdev, config);
+ if (!qdev)
+ return -ENOMEM;
+
+ ret = init_pci(qdev, pdev, config);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < qdev->num_dbc; ++i)
+ qdev->dbc[i].dbc_base = qdev->bar_dbc + QAIC_DBC_OFF(i);
+
+ mhi_irq = init_msi(qdev, pdev);
+ if (mhi_irq < 0)
+ return mhi_irq;
+
+ ret = qaic_create_drm_device(qdev, QAIC_NO_PARTITION);
+ if (ret)
+ return ret;
+
+ qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_mhi, mhi_irq,
+ qdev->single_msi, config->family);
+ if (IS_ERR(qdev->mhi_cntrl)) {
+ ret = PTR_ERR(qdev->mhi_cntrl);
+ qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void qaic_pci_remove(struct pci_dev *pdev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(pdev);
+
+ if (!qdev)
+ return;
+
+ qaic_dev_reset_clean_local_state(qdev);
+ qaic_mhi_free_controller(qdev->mhi_cntrl, link_up);
+ qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
+}
+
+static void qaic_pci_shutdown(struct pci_dev *pdev)
+{
+ /* see qaic_exit for what link_up is doing */
+ link_up = true;
+ qaic_pci_remove(pdev);
+}
+
+static pci_ers_result_t qaic_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t error)
+{
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static void qaic_pci_reset_prepare(struct pci_dev *pdev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(pdev);
+
+ qaic_notify_reset(qdev);
+ qaic_mhi_start_reset(qdev->mhi_cntrl);
+ qaic_dev_reset_clean_local_state(qdev);
+}
+
+static void qaic_pci_reset_done(struct pci_dev *pdev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(pdev);
+
+ qaic_mhi_reset_done(qdev->mhi_cntrl);
+}
+
+static const struct mhi_device_id qaic_mhi_match_table[] = {
+ { .chan = "QAIC_CONTROL", },
+ {},
+};
+
+static struct mhi_driver qaic_mhi_driver = {
+ .id_table = qaic_mhi_match_table,
+ .remove = qaic_mhi_remove,
+ .probe = qaic_mhi_probe,
+ .ul_xfer_cb = qaic_mhi_ul_xfer_cb,
+ .dl_xfer_cb = qaic_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_mhi",
+ },
+};
+
+static const struct pci_device_id qaic_ids[] = {
+ { PCI_DEVICE_DATA(QCOM, AIC080, (kernel_ulong_t)&aic080_config), },
+ { PCI_DEVICE_DATA(QCOM, AIC100, (kernel_ulong_t)&aic100_config), },
+ { PCI_DEVICE_DATA(QCOM, AIC200, (kernel_ulong_t)&aic200_config), },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, qaic_ids);
+
+static const struct pci_error_handlers qaic_pci_err_handler = {
+ .error_detected = qaic_pci_error_detected,
+ .reset_prepare = qaic_pci_reset_prepare,
+ .reset_done = qaic_pci_reset_done,
+};
+
+static bool qaic_is_under_reset(struct qaic_device *qdev)
+{
+ int rcu_id;
+ bool ret;
+
+ rcu_id = srcu_read_lock(&qdev->dev_lock);
+ ret = qdev->dev_state != QAIC_ONLINE;
+ srcu_read_unlock(&qdev->dev_lock, rcu_id);
+ return ret;
+}
+
+static bool qaic_data_path_busy(struct qaic_device *qdev)
+{
+ bool ret = false;
+ int dev_rcu_id;
+ int i;
+
+ dev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
+ return false;
+ }
+ for (i = 0; i < qdev->num_dbc; i++) {
+ struct dma_bridge_chan *dbc = &qdev->dbc[i];
+ unsigned long flags;
+ int ch_rcu_id;
+
+ ch_rcu_id = srcu_read_lock(&dbc->ch_lock);
+ if (!dbc->usr || !dbc->in_use) {
+ srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
+ continue;
+ }
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ ret = !list_empty(&dbc->xfer_list);
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
+ if (ret)
+ break;
+ }
+ srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
+ return ret;
+}
+
+static int qaic_pm_suspend(struct device *dev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+
+ dev_dbg(dev, "Suspending..\n");
+ if (qaic_data_path_busy(qdev)) {
+ dev_dbg(dev, "Device's datapath is busy. Aborting suspend..\n");
+ return -EBUSY;
+ }
+ if (qaic_is_under_reset(qdev)) {
+ dev_dbg(dev, "Device is under reset. Aborting suspend..\n");
+ return -EBUSY;
+ }
+ qaic_mqts_ch_stop_timer(qdev->mqts_ch);
+ qaic_pci_reset_prepare(qdev->pdev);
+ pci_save_state(qdev->pdev);
+ pci_disable_device(qdev->pdev);
+ pci_set_power_state(qdev->pdev, PCI_D3hot);
+ return 0;
+}
+
+static int qaic_pm_resume(struct device *dev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+ int ret;
+
+ dev_dbg(dev, "Resuming..\n");
+ pci_set_power_state(qdev->pdev, PCI_D0);
+ pci_restore_state(qdev->pdev);
+ ret = pci_enable_device(qdev->pdev);
+ if (ret) {
+ dev_err(dev, "pci_enable_device failed on resume %d\n", ret);
+ return ret;
+ }
+ pci_set_master(qdev->pdev);
+ qaic_pci_reset_done(qdev->pdev);
+ return 0;
+}
+
+static const struct dev_pm_ops qaic_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(qaic_pm_suspend, qaic_pm_resume)
+};
+
+static struct pci_driver qaic_pci_driver = {
+ .name = QAIC_NAME,
+ .id_table = qaic_ids,
+ .probe = qaic_pci_probe,
+ .remove = qaic_pci_remove,
+ .shutdown = qaic_pci_shutdown,
+ .err_handler = &qaic_pci_err_handler,
+ .driver = {
+ .pm = pm_sleep_ptr(&qaic_pm_ops),
+ },
+};
+
+static int __init qaic_init(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&qaic_pci_driver);
+ if (ret) {
+ pr_debug("qaic: pci_register_driver failed %d\n", ret);
+ return ret;
+ }
+
+ ret = mhi_driver_register(&qaic_mhi_driver);
+ if (ret) {
+ pr_debug("qaic: mhi_driver_register failed %d\n", ret);
+ goto free_pci;
+ }
+
+ ret = sahara_register();
+ if (ret) {
+ pr_debug("qaic: sahara_register failed %d\n", ret);
+ goto free_mhi;
+ }
+
+ ret = qaic_timesync_init();
+ if (ret)
+ pr_debug("qaic: qaic_timesync_init failed %d\n", ret);
+
+ ret = qaic_bootlog_register();
+ if (ret)
+ pr_debug("qaic: qaic_bootlog_register failed %d\n", ret);
+
+ ret = qaic_ras_register();
+ if (ret)
+ pr_debug("qaic: qaic_ras_register failed %d\n", ret);
+ ret = qaic_ssr_register();
+ if (ret) {
+ pr_debug("qaic: qaic_ssr_register failed %d\n", ret);
+ goto free_bootlog;
+ }
+
+ return 0;
+
+free_bootlog:
+ qaic_bootlog_unregister();
+free_mhi:
+ mhi_driver_unregister(&qaic_mhi_driver);
+free_pci:
+ pci_unregister_driver(&qaic_pci_driver);
+ return ret;
+}
+
+static void __exit qaic_exit(void)
+{
+ /*
+ * We assume that qaic_pci_remove() is called due to a hotplug event
+ * which would mean that the link is down, and thus
+ * qaic_mhi_free_controller() should not try to access the device during
+ * cleanup.
+ * We call pci_unregister_driver() below, which also triggers
+ * qaic_pci_remove(), but since this is module exit, we expect the link
+ * to the device to be up, in which case qaic_mhi_free_controller()
+ * should try to access the device during cleanup to put the device in
+ * a sane state.
+ * For that reason, we set link_up here to let qaic_mhi_free_controller
+ * know the expected link state. Since the module is going to be
+ * removed at the end of this, we don't need to worry about
+ * reinitializing the link_up state after the cleanup is done.
+ */
+ link_up = true;
+ qaic_ssr_unregister();
+ qaic_ras_unregister();
+ qaic_bootlog_unregister();
+ qaic_timesync_deinit();
+ sahara_unregister();
+ mhi_driver_unregister(&qaic_mhi_driver);
+ pci_unregister_driver(&qaic_pci_driver);
+}
+
+module_init(qaic_init);
+module_exit(qaic_exit);
+
+MODULE_AUTHOR(QAIC_DESC " Kernel Driver Team");
+MODULE_DESCRIPTION(QAIC_DESC " Accel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c
new file mode 100644
index 000000000000..f1d52a710136
--- /dev/null
+++ b/drivers/accel/qaic/qaic_ras.c
@@ -0,0 +1,642 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
+
+#include <asm/byteorder.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mhi.h>
+
+#include "qaic.h"
+#include "qaic_ras.h"
+
+#define MAGIC 0x55AA
+#define VERSION 0x2
+#define HDR_SZ 12
+#define NUM_TEMP_LVL 3
+#define POWER_BREAK BIT(0)
+
+enum msg_type {
+ MSG_PUSH, /* async push from device */
+ MSG_REQ, /* sync request to device */
+ MSG_RESP, /* sync response from device */
+};
+
+enum err_type {
+ CE, /* correctable error */
+ UE, /* uncorrectable error */
+ UE_NF, /* uncorrectable error that is non-fatal, expect a disruption */
+ ERR_TYPE_MAX,
+};
+
+static const char * const err_type_str[] = {
+ [CE] = "Correctable",
+ [UE] = "Uncorrectable",
+ [UE_NF] = "Uncorrectable Non-Fatal",
+};
+
+static const char * const err_class_str[] = {
+ [CE] = "Warning",
+ [UE] = "Fatal",
+ [UE_NF] = "Warning",
+};
+
+enum err_source {
+ SOC_MEM,
+ PCIE,
+ DDR,
+ SYS_BUS1,
+ SYS_BUS2,
+ NSP_MEM,
+ TSENS,
+};
+
+static const char * const err_src_str[TSENS + 1] = {
+ [SOC_MEM] = "SoC Memory",
+ [PCIE] = "PCIE",
+ [DDR] = "DDR",
+ [SYS_BUS1] = "System Bus source 1",
+ [SYS_BUS2] = "System Bus source 2",
+ [NSP_MEM] = "NSP Memory",
+ [TSENS] = "Temperature Sensors",
+};
+
+struct ras_data {
+ /* header start */
+ /* Magic number to validate the message */
+ u16 magic;
+ /* RAS version number */
+ u16 ver;
+ u32 seq_num;
+ /* RAS message type */
+ u8 type;
+ u8 id;
+ /* Size of RAS message without the header in byte */
+ u16 len;
+ /* header end */
+ s32 result;
+ /*
+ * Error source
+ * 0 : SoC Memory
+ * 1 : PCIE
+ * 2 : DDR
+ * 3 : System Bus source 1
+ * 4 : System Bus source 2
+ * 5 : NSP Memory
+ * 6 : Temperature Sensors
+ */
+ u32 source;
+ /*
+ * Stores the error type, there are three types of error in RAS
+ * 0 : correctable error (CE)
+ * 1 : uncorrectable error (UE)
+ * 2 : uncorrectable error that is non-fatal (UE_NF)
+ */
+ u32 err_type;
+ u32 err_threshold;
+ u32 ce_count;
+ u32 ue_count;
+ u32 intr_num;
+ /* Data specific to error source */
+ u8 syndrome[64];
+} __packed;
+
+struct soc_mem_syndrome {
+ u64 error_address[8];
+} __packed;
+
+struct nsp_mem_syndrome {
+ u32 error_address[8];
+ u8 nsp_id;
+} __packed;
+
+struct ddr_syndrome {
+ u32 count;
+ u32 irq_status;
+ u32 data_31_0[2];
+ u32 data_63_32[2];
+ u32 data_95_64[2];
+ u32 data_127_96[2];
+ u32 addr_lsb;
+ u16 addr_msb;
+ u16 parity_bits;
+ u16 instance;
+ u16 err_type;
+} __packed;
+
+struct tsens_syndrome {
+ u32 threshold_type;
+ s32 temp;
+} __packed;
+
+struct sysbus1_syndrome {
+ u32 slave;
+ u32 err_type;
+ u16 addr[8];
+ u8 instance;
+} __packed;
+
+struct sysbus2_syndrome {
+ u32 lsb3;
+ u32 msb3;
+ u32 lsb2;
+ u32 msb2;
+ u32 ext_id;
+ u16 path;
+ u16 op_type;
+ u16 len;
+ u16 redirect;
+ u8 valid;
+ u8 word_error;
+ u8 non_secure;
+ u8 opc;
+ u8 error_code;
+ u8 trans_type;
+ u8 addr_space;
+ u8 instance;
+} __packed;
+
+struct pcie_syndrome {
+ /* CE info */
+ u32 bad_tlp;
+ u32 bad_dllp;
+ u32 replay_rollover;
+ u32 replay_timeout;
+ u32 rx_err;
+ u32 internal_ce_count;
+ /* UE_NF info */
+ u32 fc_timeout;
+ u32 poison_tlp;
+ u32 ecrc_err;
+ u32 unsupported_req;
+ u32 completer_abort;
+ u32 completion_timeout;
+ /* UE info */
+ u32 addr;
+ u8 index;
+ /*
+ * Flag to indicate specific event of PCIe
+ * BIT(0): Power break (low power)
+ * BIT(1) to BIT(7): Reserved
+ */
+ u8 flag;
+} __packed;
+
+static const char * const threshold_type_str[NUM_TEMP_LVL] = {
+ [0] = "lower",
+ [1] = "upper",
+ [2] = "critical",
+};
+
+static void ras_msg_to_cpu(struct ras_data *msg)
+{
+ struct sysbus1_syndrome *sysbus1_syndrome = (struct sysbus1_syndrome *)&msg->syndrome[0];
+ struct sysbus2_syndrome *sysbus2_syndrome = (struct sysbus2_syndrome *)&msg->syndrome[0];
+ struct soc_mem_syndrome *soc_syndrome = (struct soc_mem_syndrome *)&msg->syndrome[0];
+ struct nsp_mem_syndrome *nsp_syndrome = (struct nsp_mem_syndrome *)&msg->syndrome[0];
+ struct tsens_syndrome *tsens_syndrome = (struct tsens_syndrome *)&msg->syndrome[0];
+ struct pcie_syndrome *pcie_syndrome = (struct pcie_syndrome *)&msg->syndrome[0];
+ struct ddr_syndrome *ddr_syndrome = (struct ddr_syndrome *)&msg->syndrome[0];
+ int i;
+
+ le16_to_cpus(&msg->magic);
+ le16_to_cpus(&msg->ver);
+ le32_to_cpus(&msg->seq_num);
+ le16_to_cpus(&msg->len);
+ le32_to_cpus(&msg->result);
+ le32_to_cpus(&msg->source);
+ le32_to_cpus(&msg->err_type);
+ le32_to_cpus(&msg->err_threshold);
+ le32_to_cpus(&msg->ce_count);
+ le32_to_cpus(&msg->ue_count);
+ le32_to_cpus(&msg->intr_num);
+
+ switch (msg->source) {
+ case SOC_MEM:
+ for (i = 0; i < 8; i++)
+ le64_to_cpus(&soc_syndrome->error_address[i]);
+ break;
+ case PCIE:
+ le32_to_cpus(&pcie_syndrome->bad_tlp);
+ le32_to_cpus(&pcie_syndrome->bad_dllp);
+ le32_to_cpus(&pcie_syndrome->replay_rollover);
+ le32_to_cpus(&pcie_syndrome->replay_timeout);
+ le32_to_cpus(&pcie_syndrome->rx_err);
+ le32_to_cpus(&pcie_syndrome->internal_ce_count);
+ le32_to_cpus(&pcie_syndrome->fc_timeout);
+ le32_to_cpus(&pcie_syndrome->poison_tlp);
+ le32_to_cpus(&pcie_syndrome->ecrc_err);
+ le32_to_cpus(&pcie_syndrome->unsupported_req);
+ le32_to_cpus(&pcie_syndrome->completer_abort);
+ le32_to_cpus(&pcie_syndrome->completion_timeout);
+ le32_to_cpus(&pcie_syndrome->addr);
+ break;
+ case DDR:
+ le16_to_cpus(&ddr_syndrome->instance);
+ le16_to_cpus(&ddr_syndrome->err_type);
+ le32_to_cpus(&ddr_syndrome->count);
+ le32_to_cpus(&ddr_syndrome->irq_status);
+ le32_to_cpus(&ddr_syndrome->data_31_0[0]);
+ le32_to_cpus(&ddr_syndrome->data_31_0[1]);
+ le32_to_cpus(&ddr_syndrome->data_63_32[0]);
+ le32_to_cpus(&ddr_syndrome->data_63_32[1]);
+ le32_to_cpus(&ddr_syndrome->data_95_64[0]);
+ le32_to_cpus(&ddr_syndrome->data_95_64[1]);
+ le32_to_cpus(&ddr_syndrome->data_127_96[0]);
+ le32_to_cpus(&ddr_syndrome->data_127_96[1]);
+ le16_to_cpus(&ddr_syndrome->parity_bits);
+ le16_to_cpus(&ddr_syndrome->addr_msb);
+ le32_to_cpus(&ddr_syndrome->addr_lsb);
+ break;
+ case SYS_BUS1:
+ le32_to_cpus(&sysbus1_syndrome->slave);
+ le32_to_cpus(&sysbus1_syndrome->err_type);
+ for (i = 0; i < 8; i++)
+ le16_to_cpus(&sysbus1_syndrome->addr[i]);
+ break;
+ case SYS_BUS2:
+ le16_to_cpus(&sysbus2_syndrome->op_type);
+ le16_to_cpus(&sysbus2_syndrome->len);
+ le16_to_cpus(&sysbus2_syndrome->redirect);
+ le16_to_cpus(&sysbus2_syndrome->path);
+ le32_to_cpus(&sysbus2_syndrome->ext_id);
+ le32_to_cpus(&sysbus2_syndrome->lsb2);
+ le32_to_cpus(&sysbus2_syndrome->msb2);
+ le32_to_cpus(&sysbus2_syndrome->lsb3);
+ le32_to_cpus(&sysbus2_syndrome->msb3);
+ break;
+ case NSP_MEM:
+ for (i = 0; i < 8; i++)
+ le32_to_cpus(&nsp_syndrome->error_address[i]);
+ break;
+ case TSENS:
+ le32_to_cpus(&tsens_syndrome->threshold_type);
+ le32_to_cpus(&tsens_syndrome->temp);
+ break;
+ }
+}
+
+static void decode_ras_msg(struct qaic_device *qdev, struct ras_data *msg)
+{
+ struct sysbus1_syndrome *sysbus1_syndrome = (struct sysbus1_syndrome *)&msg->syndrome[0];
+ struct sysbus2_syndrome *sysbus2_syndrome = (struct sysbus2_syndrome *)&msg->syndrome[0];
+ struct soc_mem_syndrome *soc_syndrome = (struct soc_mem_syndrome *)&msg->syndrome[0];
+ struct nsp_mem_syndrome *nsp_syndrome = (struct nsp_mem_syndrome *)&msg->syndrome[0];
+ struct tsens_syndrome *tsens_syndrome = (struct tsens_syndrome *)&msg->syndrome[0];
+ struct pcie_syndrome *pcie_syndrome = (struct pcie_syndrome *)&msg->syndrome[0];
+ struct ddr_syndrome *ddr_syndrome = (struct ddr_syndrome *)&msg->syndrome[0];
+ char *class;
+ char *level;
+
+ if (msg->magic != MAGIC) {
+ pci_warn(qdev->pdev, "Dropping RAS message with invalid magic %x\n", msg->magic);
+ return;
+ }
+
+ if (!msg->ver || msg->ver > VERSION) {
+ pci_warn(qdev->pdev, "Dropping RAS message with invalid version %d\n", msg->ver);
+ return;
+ }
+
+ if (msg->type != MSG_PUSH) {
+ pci_warn(qdev->pdev, "Dropping non-PUSH RAS message\n");
+ return;
+ }
+
+ if (msg->len != sizeof(*msg) - HDR_SZ) {
+ pci_warn(qdev->pdev, "Dropping RAS message with invalid len %d\n", msg->len);
+ return;
+ }
+
+ if (msg->err_type >= ERR_TYPE_MAX) {
+ pci_warn(qdev->pdev, "Dropping RAS message with err type %d\n", msg->err_type);
+ return;
+ }
+
+ if (msg->err_type == UE)
+ level = KERN_ERR;
+ else
+ level = KERN_WARNING;
+
+ switch (msg->source) {
+ case SOC_MEM:
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n",
+ err_class_str[msg->err_type],
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold,
+ soc_syndrome->error_address[0],
+ soc_syndrome->error_address[1],
+ soc_syndrome->error_address[2],
+ soc_syndrome->error_address[3],
+ soc_syndrome->error_address[4],
+ soc_syndrome->error_address[5],
+ soc_syndrome->error_address[6],
+ soc_syndrome->error_address[7]);
+ break;
+ case PCIE:
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\n",
+ err_class_str[msg->err_type],
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold);
+
+ switch (msg->err_type) {
+ case CE:
+ /*
+ * Modeled after AER prints. This continues the dev_printk() from a few
+ * lines up. We reduce duplication of code, but also avoid re-printing the
+ * PCI device info so that the end result looks uniform to the log user.
+ */
+ printk(KERN_WARNING pr_fmt("Syndrome:\n Bad TLP count %d\n Bad DLLP count %d\n Replay Rollover count %d\n Replay Timeout count %d\n Recv Error count %d\n Internal CE count %d\n"),
+ pcie_syndrome->bad_tlp,
+ pcie_syndrome->bad_dllp,
+ pcie_syndrome->replay_rollover,
+ pcie_syndrome->replay_timeout,
+ pcie_syndrome->rx_err,
+ pcie_syndrome->internal_ce_count);
+ if (msg->ver > 0x1)
+ pr_warn(" Power break %s\n",
+ pcie_syndrome->flag & POWER_BREAK ? "ON" : "OFF");
+ break;
+ case UE:
+ printk(KERN_ERR pr_fmt("Syndrome:\n Index %d\n Address 0x%x\n"),
+ pcie_syndrome->index, pcie_syndrome->addr);
+ break;
+ case UE_NF:
+ printk(KERN_WARNING pr_fmt("Syndrome:\n FC timeout count %d\n Poisoned TLP count %d\n ECRC error count %d\n Unsupported request count %d\n Completer abort count %d\n Completion timeout count %d\n"),
+ pcie_syndrome->fc_timeout,
+ pcie_syndrome->poison_tlp,
+ pcie_syndrome->ecrc_err,
+ pcie_syndrome->unsupported_req,
+ pcie_syndrome->completer_abort,
+ pcie_syndrome->completion_timeout);
+ break;
+ default:
+ break;
+ }
+ break;
+ case DDR:
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n Instance %d\n Count %d\n Data 31_0 0x%x 0x%x\n Data 63_32 0x%x 0x%x\n Data 95_64 0x%x 0x%x\n Data 127_96 0x%x 0x%x\n Parity bits 0x%x\n Address msb 0x%x\n Address lsb 0x%x\n",
+ err_class_str[msg->err_type],
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold,
+ ddr_syndrome->instance,
+ ddr_syndrome->count,
+ ddr_syndrome->data_31_0[1],
+ ddr_syndrome->data_31_0[0],
+ ddr_syndrome->data_63_32[1],
+ ddr_syndrome->data_63_32[0],
+ ddr_syndrome->data_95_64[1],
+ ddr_syndrome->data_95_64[0],
+ ddr_syndrome->data_127_96[1],
+ ddr_syndrome->data_127_96[0],
+ ddr_syndrome->parity_bits,
+ ddr_syndrome->addr_msb,
+ ddr_syndrome->addr_lsb);
+ break;
+ case SYS_BUS1:
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n instance %d\n %s\n err_type %d\n address0 0x%x\n address1 0x%x\n address2 0x%x\n address3 0x%x\n address4 0x%x\n address5 0x%x\n address6 0x%x\n address7 0x%x\n",
+ err_class_str[msg->err_type],
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold,
+ sysbus1_syndrome->instance,
+ sysbus1_syndrome->slave ? "Slave" : "Master",
+ sysbus1_syndrome->err_type,
+ sysbus1_syndrome->addr[0],
+ sysbus1_syndrome->addr[1],
+ sysbus1_syndrome->addr[2],
+ sysbus1_syndrome->addr[3],
+ sysbus1_syndrome->addr[4],
+ sysbus1_syndrome->addr[5],
+ sysbus1_syndrome->addr[6],
+ sysbus1_syndrome->addr[7]);
+ break;
+ case SYS_BUS2:
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n instance %d\n valid %d\n word error %d\n non-secure %d\n opc %d\n error code %d\n transaction type %d\n address space %d\n operation type %d\n len %d\n redirect %d\n path %d\n ext_id %d\n lsb2 %d\n msb2 %d\n lsb3 %d\n msb3 %d\n",
+ err_class_str[msg->err_type],
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold,
+ sysbus2_syndrome->instance,
+ sysbus2_syndrome->valid,
+ sysbus2_syndrome->word_error,
+ sysbus2_syndrome->non_secure,
+ sysbus2_syndrome->opc,
+ sysbus2_syndrome->error_code,
+ sysbus2_syndrome->trans_type,
+ sysbus2_syndrome->addr_space,
+ sysbus2_syndrome->op_type,
+ sysbus2_syndrome->len,
+ sysbus2_syndrome->redirect,
+ sysbus2_syndrome->path,
+ sysbus2_syndrome->ext_id,
+ sysbus2_syndrome->lsb2,
+ sysbus2_syndrome->msb2,
+ sysbus2_syndrome->lsb3,
+ sysbus2_syndrome->msb3);
+ break;
+ case NSP_MEM:
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n NSP ID %d\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n",
+ err_class_str[msg->err_type],
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold,
+ nsp_syndrome->nsp_id,
+ nsp_syndrome->error_address[0],
+ nsp_syndrome->error_address[1],
+ nsp_syndrome->error_address[2],
+ nsp_syndrome->error_address[3],
+ nsp_syndrome->error_address[4],
+ nsp_syndrome->error_address[5],
+ nsp_syndrome->error_address[6],
+ nsp_syndrome->error_address[7]);
+ break;
+ case TSENS:
+ if (tsens_syndrome->threshold_type >= NUM_TEMP_LVL) {
+ pci_warn(qdev->pdev, "Dropping RAS message with invalid temp threshold %d\n",
+ tsens_syndrome->threshold_type);
+ break;
+ }
+
+ if (msg->err_type)
+ class = "Fatal";
+ else if (tsens_syndrome->threshold_type)
+ class = "Critical";
+ else
+ class = "Warning";
+
+ dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n %s threshold\n %d deg C\n",
+ class,
+ err_type_str[msg->err_type],
+ "error from",
+ err_src_str[msg->source],
+ msg->err_threshold,
+ threshold_type_str[tsens_syndrome->threshold_type],
+ tsens_syndrome->temp);
+ break;
+ }
+
+ /* Uncorrectable errors are fatal */
+ if (msg->err_type == UE)
+ mhi_soc_reset(qdev->mhi_cntrl);
+
+ switch (msg->err_type) {
+ case CE:
+ if (qdev->ce_count != UINT_MAX)
+ qdev->ce_count++;
+ break;
+ case UE:
+ if (qdev->ce_count != UINT_MAX)
+ qdev->ue_count++;
+ break;
+ case UE_NF:
+ if (qdev->ce_count != UINT_MAX)
+ qdev->ue_nf_count++;
+ break;
+ default:
+ /* not possible */
+ break;
+ }
+}
+
+static ssize_t ce_count_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+
+ return sysfs_emit(buf, "%d\n", qdev->ce_count);
+}
+
+static ssize_t ue_count_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+
+ return sysfs_emit(buf, "%d\n", qdev->ue_count);
+}
+
+static ssize_t ue_nonfatal_count_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+
+ return sysfs_emit(buf, "%d\n", qdev->ue_nf_count);
+}
+
+static DEVICE_ATTR_RO(ce_count);
+static DEVICE_ATTR_RO(ue_count);
+static DEVICE_ATTR_RO(ue_nonfatal_count);
+
+static struct attribute *ras_attrs[] = {
+ &dev_attr_ce_count.attr,
+ &dev_attr_ue_count.attr,
+ &dev_attr_ue_nonfatal_count.attr,
+ NULL,
+};
+
+static struct attribute_group ras_group = {
+ .attrs = ras_attrs,
+};
+
+static int qaic_ras_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ struct ras_data *resp;
+ int ret;
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ return ret;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ mhi_unprepare_from_transfer(mhi_dev);
+ return -ENOMEM;
+ }
+
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp, sizeof(*resp), MHI_EOT);
+ if (ret) {
+ kfree(resp);
+ mhi_unprepare_from_transfer(mhi_dev);
+ return ret;
+ }
+
+ ret = device_add_group(&qdev->pdev->dev, &ras_group);
+ if (ret) {
+ mhi_unprepare_from_transfer(mhi_dev);
+ pci_dbg(qdev->pdev, "ras add sysfs failed %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+ qdev->ras_ch = mhi_dev;
+
+ return ret;
+}
+
+static void qaic_ras_mhi_remove(struct mhi_device *mhi_dev)
+{
+ struct qaic_device *qdev;
+
+ qdev = dev_get_drvdata(&mhi_dev->dev);
+ qdev->ras_ch = NULL;
+ device_remove_group(&qdev->pdev->dev, &ras_group);
+ mhi_unprepare_from_transfer(mhi_dev);
+}
+
+static void qaic_ras_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) {}
+
+static void qaic_ras_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct ras_data *msg = mhi_result->buf_addr;
+ int ret;
+
+ if (mhi_result->transaction_status) {
+ kfree(msg);
+ return;
+ }
+
+ ras_msg_to_cpu(msg);
+ decode_ras_msg(qdev, msg);
+
+ ret = mhi_queue_buf(qdev->ras_ch, DMA_FROM_DEVICE, msg, sizeof(*msg), MHI_EOT);
+ if (ret) {
+ dev_err(&mhi_dev->dev, "Cannot requeue RAS recv buf %d\n", ret);
+ kfree(msg);
+ }
+}
+
+static const struct mhi_device_id qaic_ras_mhi_match_table[] = {
+ { .chan = "QAIC_STATUS", },
+ {},
+};
+
+static struct mhi_driver qaic_ras_mhi_driver = {
+ .id_table = qaic_ras_mhi_match_table,
+ .remove = qaic_ras_mhi_remove,
+ .probe = qaic_ras_mhi_probe,
+ .ul_xfer_cb = qaic_ras_mhi_ul_xfer_cb,
+ .dl_xfer_cb = qaic_ras_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_ras",
+ },
+};
+
+int qaic_ras_register(void)
+{
+ return mhi_driver_register(&qaic_ras_mhi_driver);
+}
+
+void qaic_ras_unregister(void)
+{
+ mhi_driver_unregister(&qaic_ras_mhi_driver);
+}
diff --git a/drivers/accel/qaic/qaic_ras.h b/drivers/accel/qaic/qaic_ras.h
new file mode 100644
index 000000000000..d44a4eeeb060
--- /dev/null
+++ b/drivers/accel/qaic/qaic_ras.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+
+#ifndef __QAIC_RAS_H__
+#define __QAIC_RAS_H__
+
+int qaic_ras_register(void);
+void qaic_ras_unregister(void);
+
+#endif /* __QAIC_RAS_H__ */
diff --git a/drivers/accel/qaic/qaic_ssr.c b/drivers/accel/qaic/qaic_ssr.c
new file mode 100644
index 000000000000..9b662d690371
--- /dev/null
+++ b/drivers/accel/qaic/qaic_ssr.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <asm/byteorder.h>
+#include <drm/drm_file.h>
+#include <drm/drm_managed.h>
+#include <linux/devcoredump.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mhi.h>
+#include <linux/workqueue.h>
+
+#include "qaic.h"
+#include "qaic_ssr.h"
+
+#define SSR_RESP_MSG_SZ 32
+#define SSR_MHI_BUF_SIZE SZ_64K
+#define SSR_MEM_READ_DATA_SIZE ((u64)SSR_MHI_BUF_SIZE - sizeof(struct ssr_crashdump))
+#define SSR_MEM_READ_CHUNK_SIZE ((u64)SSR_MEM_READ_DATA_SIZE - sizeof(struct ssr_memory_read_rsp))
+
+#define DEBUG_TRANSFER_INFO BIT(0)
+#define DEBUG_TRANSFER_INFO_RSP BIT(1)
+#define MEMORY_READ BIT(2)
+#define MEMORY_READ_RSP BIT(3)
+#define DEBUG_TRANSFER_DONE BIT(4)
+#define DEBUG_TRANSFER_DONE_RSP BIT(5)
+#define SSR_EVENT BIT(8)
+#define SSR_EVENT_RSP BIT(9)
+
+#define SSR_EVENT_NACK BIT(0)
+#define BEFORE_SHUTDOWN BIT(1)
+#define AFTER_SHUTDOWN BIT(2)
+#define BEFORE_POWER_UP BIT(3)
+#define AFTER_POWER_UP BIT(4)
+
+struct debug_info_table {
+ /* Save preferences. Default is mandatory */
+ u64 save_perf;
+ /* Base address of the debug region */
+ u64 mem_base;
+ /* Size of debug region in bytes */
+ u64 len;
+ /* Description */
+ char desc[20];
+ /* Filename of debug region */
+ char filename[20];
+};
+
+struct _ssr_hdr {
+ __le32 cmd;
+ __le32 len;
+ __le32 dbc_id;
+};
+
+struct ssr_hdr {
+ u32 cmd;
+ u32 len;
+ u32 dbc_id;
+};
+
+struct ssr_debug_transfer_info {
+ struct ssr_hdr hdr;
+ u32 resv;
+ u64 tbl_addr;
+ u64 tbl_len;
+} __packed;
+
+struct ssr_debug_transfer_info_rsp {
+ struct _ssr_hdr hdr;
+ __le32 ret;
+} __packed;
+
+struct ssr_memory_read {
+ struct _ssr_hdr hdr;
+ __le32 resv;
+ __le64 addr;
+ __le64 len;
+} __packed;
+
+struct ssr_memory_read_rsp {
+ struct _ssr_hdr hdr;
+ __le32 resv;
+ u8 data[];
+} __packed;
+
+struct ssr_debug_transfer_done {
+ struct _ssr_hdr hdr;
+ __le32 resv;
+} __packed;
+
+struct ssr_debug_transfer_done_rsp {
+ struct _ssr_hdr hdr;
+ __le32 ret;
+} __packed;
+
+struct ssr_event {
+ struct ssr_hdr hdr;
+ u32 event;
+} __packed;
+
+struct ssr_event_rsp {
+ struct _ssr_hdr hdr;
+ __le32 event;
+} __packed;
+
+struct ssr_resp {
+ /* Work struct to schedule work coming on QAIC_SSR channel */
+ struct work_struct work;
+ /* Root struct of device, used to access device resources */
+ struct qaic_device *qdev;
+ /* Buffer used by MHI for transfer requests */
+ u8 data[] __aligned(8);
+};
+
+/* SSR crashdump book keeping structure */
+struct ssr_dump_info {
+ /* DBC associated with this SSR crashdump */
+ struct dma_bridge_chan *dbc;
+ /*
+ * It will be used when we complete the crashdump download and switch
+ * to waiting on SSR events
+ */
+ struct ssr_resp *resp;
+ /* MEMORY READ request MHI buffer.*/
+ struct ssr_memory_read *read_buf_req;
+ /* TRUE: ->read_buf_req is queued for MHI transaction. FALSE: Otherwise */
+ bool read_buf_req_queued;
+ /* Address of table in host */
+ void *tbl_addr;
+ /* Total size of table */
+ u64 tbl_len;
+ /* Offset of table(->tbl_addr) where the new chunk will be dumped */
+ u64 tbl_off;
+ /* Address of table in device/target */
+ u64 tbl_addr_dev;
+ /* Ptr to the entire dump */
+ void *dump_addr;
+ /* Entire crashdump size */
+ u64 dump_sz;
+ /* Offset of crashdump(->dump_addr) where the new chunk will be dumped */
+ u64 dump_off;
+ /* Points to the table entry we are currently downloading */
+ struct debug_info_table *tbl_ent;
+ /* Offset in the current table entry(->tbl_ent) for next chuck */
+ u64 tbl_ent_off;
+};
+
+struct ssr_crashdump {
+ /*
+ * Points to a book keeping struct maintained by MHI SSR device while
+ * downloading a SSR crashdump. It is NULL when crashdump downloading
+ * not in progress.
+ */
+ struct ssr_dump_info *dump_info;
+ /* Work struct to schedule work coming on QAIC_SSR channel */
+ struct work_struct work;
+ /* Root struct of device, used to access device resources */
+ struct qaic_device *qdev;
+ /* Buffer used by MHI for transfer requests */
+ u8 data[];
+};
+
+#define QAIC_SSR_DUMP_V1_MAGIC 0x1234567890abcdef
+#define QAIC_SSR_DUMP_V1_VER 1
+struct dump_file_meta {
+ u64 magic;
+ u64 version;
+ u64 size; /* Total size of the entire dump */
+ u64 tbl_len; /* Length of the table in byte */
+};
+
+/*
+ * Layout of crashdump
+ * +------------------------------------------+
+ * | Crashdump Meta structure |
+ * | type: struct dump_file_meta |
+ * +------------------------------------------+
+ * | Crashdump Table |
+ * | type: array of struct debug_info_table |
+ * | |
+ * | |
+ * | |
+ * +------------------------------------------+
+ * | Crashdump |
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * +------------------------------------------+
+ */
+
+static void free_ssr_dump_info(struct ssr_crashdump *ssr_crash)
+{
+ struct ssr_dump_info *dump_info = ssr_crash->dump_info;
+
+ ssr_crash->dump_info = NULL;
+ if (!dump_info)
+ return;
+ if (!dump_info->read_buf_req_queued)
+ kfree(dump_info->read_buf_req);
+ vfree(dump_info->tbl_addr);
+ vfree(dump_info->dump_addr);
+ kfree(dump_info);
+}
+
+void qaic_clean_up_ssr(struct qaic_device *qdev)
+{
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+
+ if (!ssr_crash)
+ return;
+
+ qaic_dbc_exit_ssr(qdev);
+ free_ssr_dump_info(ssr_crash);
+}
+
+static int alloc_dump(struct ssr_dump_info *dump_info)
+{
+ struct debug_info_table *tbl_ent = dump_info->tbl_addr;
+ struct dump_file_meta *dump_meta;
+ u64 tbl_sz_lp = 0;
+ u64 dump_size = 0;
+
+ while (tbl_sz_lp < dump_info->tbl_len) {
+ le64_to_cpus(&tbl_ent->save_perf);
+ le64_to_cpus(&tbl_ent->mem_base);
+ le64_to_cpus(&tbl_ent->len);
+
+ if (tbl_ent->len == 0)
+ return -EINVAL;
+
+ dump_size += tbl_ent->len;
+ tbl_ent++;
+ tbl_sz_lp += sizeof(*tbl_ent);
+ }
+
+ dump_info->dump_sz = dump_size + dump_info->tbl_len + sizeof(*dump_meta);
+ dump_info->dump_addr = vzalloc(dump_info->dump_sz);
+ if (!dump_info->dump_addr)
+ return -ENOMEM;
+
+ /* Copy crashdump meta and table */
+ dump_meta = dump_info->dump_addr;
+ dump_meta->magic = QAIC_SSR_DUMP_V1_MAGIC;
+ dump_meta->version = QAIC_SSR_DUMP_V1_VER;
+ dump_meta->size = dump_info->dump_sz;
+ dump_meta->tbl_len = dump_info->tbl_len;
+ memcpy(dump_info->dump_addr + sizeof(*dump_meta), dump_info->tbl_addr, dump_info->tbl_len);
+ /* Offset by crashdump meta and table (copied above) */
+ dump_info->dump_off = dump_info->tbl_len + sizeof(*dump_meta);
+
+ return 0;
+}
+
+static int send_xfer_done(struct qaic_device *qdev, void *resp, u32 dbc_id)
+{
+ struct ssr_debug_transfer_done *xfer_done;
+ int ret;
+
+ xfer_done = kmalloc(sizeof(*xfer_done), GFP_KERNEL);
+ if (!xfer_done) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp, SSR_RESP_MSG_SZ, MHI_EOT);
+ if (ret)
+ goto free_xfer_done;
+
+ xfer_done->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_DONE);
+ xfer_done->hdr.len = cpu_to_le32(sizeof(*xfer_done));
+ xfer_done->hdr.dbc_id = cpu_to_le32(dbc_id);
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, xfer_done, sizeof(*xfer_done), MHI_EOT);
+ if (ret)
+ goto free_xfer_done;
+
+ return 0;
+
+free_xfer_done:
+ kfree(xfer_done);
+out:
+ return ret;
+}
+
+static int mem_read_req(struct qaic_device *qdev, u64 dest_addr, u64 dest_len)
+{
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ struct ssr_memory_read *read_buf_req;
+ struct ssr_dump_info *dump_info;
+ int ret;
+
+ dump_info = ssr_crash->dump_info;
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, ssr_crash->data, SSR_MEM_READ_DATA_SIZE,
+ MHI_EOT);
+ if (ret)
+ goto out;
+
+ read_buf_req = dump_info->read_buf_req;
+ read_buf_req->hdr.cmd = cpu_to_le32(MEMORY_READ);
+ read_buf_req->hdr.len = cpu_to_le32(sizeof(*read_buf_req));
+ read_buf_req->hdr.dbc_id = cpu_to_le32(qdev->ssr_dbc);
+ read_buf_req->addr = cpu_to_le64(dest_addr);
+ read_buf_req->len = cpu_to_le64(dest_len);
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, read_buf_req, sizeof(*read_buf_req),
+ MHI_EOT);
+ if (!ret)
+ dump_info->read_buf_req_queued = true;
+
+out:
+ return ret;
+}
+
+static int ssr_copy_table(struct ssr_dump_info *dump_info, void *data, u64 len)
+{
+ if (len > dump_info->tbl_len - dump_info->tbl_off)
+ return -EINVAL;
+
+ memcpy(dump_info->tbl_addr + dump_info->tbl_off, data, len);
+ dump_info->tbl_off += len;
+
+ /* Entire table has been downloaded, alloc dump memory */
+ if (dump_info->tbl_off == dump_info->tbl_len) {
+ dump_info->tbl_ent = dump_info->tbl_addr;
+ return alloc_dump(dump_info);
+ }
+
+ return 0;
+}
+
+static int ssr_copy_dump(struct ssr_dump_info *dump_info, void *data, u64 len)
+{
+ struct debug_info_table *tbl_ent;
+
+ tbl_ent = dump_info->tbl_ent;
+
+ if (len > tbl_ent->len - dump_info->tbl_ent_off)
+ return -EINVAL;
+
+ memcpy(dump_info->dump_addr + dump_info->dump_off, data, len);
+ dump_info->dump_off += len;
+ dump_info->tbl_ent_off += len;
+
+ /*
+ * Current segment (a entry in table) of the crashdump is complete,
+ * move to next one
+ */
+ if (tbl_ent->len == dump_info->tbl_ent_off) {
+ dump_info->tbl_ent++;
+ dump_info->tbl_ent_off = 0;
+ }
+
+ return 0;
+}
+
+static void ssr_dump_worker(struct work_struct *work)
+{
+ struct ssr_crashdump *ssr_crash = container_of(work, struct ssr_crashdump, work);
+ struct qaic_device *qdev = ssr_crash->qdev;
+ struct ssr_memory_read_rsp *mem_rd_resp;
+ struct debug_info_table *tbl_ent;
+ struct ssr_dump_info *dump_info;
+ u64 dest_addr, dest_len;
+ struct _ssr_hdr *_hdr;
+ struct ssr_hdr hdr;
+ u64 data_len;
+ int ret;
+
+ mem_rd_resp = (struct ssr_memory_read_rsp *)ssr_crash->data;
+ _hdr = &mem_rd_resp->hdr;
+ hdr.cmd = le32_to_cpu(_hdr->cmd);
+ hdr.len = le32_to_cpu(_hdr->len);
+ hdr.dbc_id = le32_to_cpu(_hdr->dbc_id);
+
+ if (hdr.dbc_id != qdev->ssr_dbc)
+ goto reset_device;
+
+ dump_info = ssr_crash->dump_info;
+ if (!dump_info)
+ goto reset_device;
+
+ if (hdr.cmd != MEMORY_READ_RSP)
+ goto free_dump_info;
+
+ if (hdr.len > SSR_MEM_READ_DATA_SIZE)
+ goto free_dump_info;
+
+ data_len = hdr.len - sizeof(*mem_rd_resp);
+
+ if (dump_info->tbl_off < dump_info->tbl_len) /* Chunk belongs to table */
+ ret = ssr_copy_table(dump_info, mem_rd_resp->data, data_len);
+ else /* Chunk belongs to crashdump */
+ ret = ssr_copy_dump(dump_info, mem_rd_resp->data, data_len);
+
+ if (ret)
+ goto free_dump_info;
+
+ if (dump_info->tbl_off < dump_info->tbl_len) {
+ /* Continue downloading table */
+ dest_addr = dump_info->tbl_addr_dev + dump_info->tbl_off;
+ dest_len = min(SSR_MEM_READ_CHUNK_SIZE, dump_info->tbl_len - dump_info->tbl_off);
+ ret = mem_read_req(qdev, dest_addr, dest_len);
+ } else if (dump_info->dump_off < dump_info->dump_sz) {
+ /* Continue downloading crashdump */
+ tbl_ent = dump_info->tbl_ent;
+ dest_addr = tbl_ent->mem_base + dump_info->tbl_ent_off;
+ dest_len = min(SSR_MEM_READ_CHUNK_SIZE, tbl_ent->len - dump_info->tbl_ent_off);
+ ret = mem_read_req(qdev, dest_addr, dest_len);
+ } else {
+ /* Crashdump download complete */
+ ret = send_xfer_done(qdev, dump_info->resp->data, hdr.dbc_id);
+ }
+
+ /* Most likely a MHI xfer has failed */
+ if (ret)
+ goto free_dump_info;
+
+ return;
+
+free_dump_info:
+ /* Free the allocated memory */
+ free_ssr_dump_info(ssr_crash);
+reset_device:
+ /*
+ * After subsystem crashes in device crashdump collection begins but
+ * something went wrong while collecting crashdump, now instead of
+ * handling this error we just reset the device as the best effort has
+ * been made
+ */
+ mhi_soc_reset(qdev->mhi_cntrl);
+}
+
+static struct ssr_dump_info *alloc_dump_info(struct qaic_device *qdev,
+ struct ssr_debug_transfer_info *debug_info)
+{
+ struct ssr_dump_info *dump_info;
+ int ret;
+
+ le64_to_cpus(&debug_info->tbl_len);
+ le64_to_cpus(&debug_info->tbl_addr);
+
+ if (debug_info->tbl_len == 0 ||
+ debug_info->tbl_len % sizeof(struct debug_info_table) != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Allocate SSR crashdump book keeping structure */
+ dump_info = kzalloc(sizeof(*dump_info), GFP_KERNEL);
+ if (!dump_info) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Buffer used to send MEMORY READ request to device via MHI */
+ dump_info->read_buf_req = kzalloc(sizeof(*dump_info->read_buf_req), GFP_KERNEL);
+ if (!dump_info->read_buf_req) {
+ ret = -ENOMEM;
+ goto free_dump_info;
+ }
+
+ /* Crashdump meta table buffer */
+ dump_info->tbl_addr = vzalloc(debug_info->tbl_len);
+ if (!dump_info->tbl_addr) {
+ ret = -ENOMEM;
+ goto free_read_buf_req;
+ }
+
+ dump_info->tbl_addr_dev = debug_info->tbl_addr;
+ dump_info->tbl_len = debug_info->tbl_len;
+
+ return dump_info;
+
+free_read_buf_req:
+ kfree(dump_info->read_buf_req);
+free_dump_info:
+ kfree(dump_info);
+out:
+ return ERR_PTR(ret);
+}
+
+static int dbg_xfer_info_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
+ struct ssr_debug_transfer_info *debug_info)
+{
+ struct ssr_debug_transfer_info_rsp *debug_rsp;
+ struct ssr_crashdump *ssr_crash = NULL;
+ int ret = 0, ret2;
+
+ debug_rsp = kmalloc(sizeof(*debug_rsp), GFP_KERNEL);
+ if (!debug_rsp)
+ return -ENOMEM;
+
+ if (!qdev->ssr_mhi_buf) {
+ ret = -ENOMEM;
+ goto send_rsp;
+ }
+
+ if (dbc->state != DBC_STATE_BEFORE_POWER_UP) {
+ ret = -EINVAL;
+ goto send_rsp;
+ }
+
+ ssr_crash = qdev->ssr_mhi_buf;
+ ssr_crash->dump_info = alloc_dump_info(qdev, debug_info);
+ if (IS_ERR(ssr_crash->dump_info)) {
+ ret = PTR_ERR(ssr_crash->dump_info);
+ ssr_crash->dump_info = NULL;
+ }
+
+send_rsp:
+ debug_rsp->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_INFO_RSP);
+ debug_rsp->hdr.len = cpu_to_le32(sizeof(*debug_rsp));
+ debug_rsp->hdr.dbc_id = cpu_to_le32(dbc->id);
+ /*
+ * 0 = Return an ACK confirming the host is ready to download crashdump
+ * 1 = Return an NACK confirming the host is not ready to download crashdump
+ */
+ debug_rsp->ret = cpu_to_le32(ret ? 1 : 0);
+
+ ret2 = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, debug_rsp, sizeof(*debug_rsp), MHI_EOT);
+ if (ret2) {
+ free_ssr_dump_info(ssr_crash);
+ kfree(debug_rsp);
+ return ret2;
+ }
+
+ return ret;
+}
+
+static void dbg_xfer_done_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
+ struct ssr_debug_transfer_done_rsp *xfer_rsp)
+{
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ u32 status = le32_to_cpu(xfer_rsp->ret);
+ struct device *dev = &qdev->pdev->dev;
+ struct ssr_dump_info *dump_info;
+
+ dump_info = ssr_crash->dump_info;
+ if (!dump_info)
+ return;
+
+ if (status) {
+ free_ssr_dump_info(ssr_crash);
+ return;
+ }
+
+ dev_coredumpv(dev, dump_info->dump_addr, dump_info->dump_sz, GFP_KERNEL);
+ /* dev_coredumpv will free dump_info->dump_addr */
+ dump_info->dump_addr = NULL;
+ free_ssr_dump_info(ssr_crash);
+}
+
+static void ssr_worker(struct work_struct *work)
+{
+ struct ssr_resp *resp = container_of(work, struct ssr_resp, work);
+ struct ssr_hdr *hdr = (struct ssr_hdr *)resp->data;
+ struct ssr_dump_info *dump_info = NULL;
+ struct qaic_device *qdev = resp->qdev;
+ struct ssr_crashdump *ssr_crash;
+ struct ssr_event_rsp *event_rsp;
+ struct dma_bridge_chan *dbc;
+ struct ssr_event *event;
+ u32 ssr_event_ack;
+ int ret;
+
+ le32_to_cpus(&hdr->cmd);
+ le32_to_cpus(&hdr->len);
+ le32_to_cpus(&hdr->dbc_id);
+
+ if (hdr->len > SSR_RESP_MSG_SZ)
+ goto out;
+
+ if (hdr->dbc_id >= qdev->num_dbc)
+ goto out;
+
+ dbc = &qdev->dbc[hdr->dbc_id];
+
+ switch (hdr->cmd) {
+ case DEBUG_TRANSFER_INFO:
+ ret = dbg_xfer_info_rsp(qdev, dbc, (struct ssr_debug_transfer_info *)resp->data);
+ if (ret)
+ break;
+
+ ssr_crash = qdev->ssr_mhi_buf;
+ dump_info = ssr_crash->dump_info;
+ dump_info->dbc = dbc;
+ dump_info->resp = resp;
+
+ /* Start by downloading debug table */
+ ret = mem_read_req(qdev, dump_info->tbl_addr_dev,
+ min(dump_info->tbl_len, SSR_MEM_READ_CHUNK_SIZE));
+ if (ret) {
+ free_ssr_dump_info(ssr_crash);
+ break;
+ }
+
+ /*
+ * Till now everything went fine, which means that we will be
+ * collecting crashdump chunk by chunk. Do not queue a response
+ * buffer for SSR cmds till the crashdump is complete.
+ */
+ return;
+ case SSR_EVENT:
+ event = (struct ssr_event *)hdr;
+ le32_to_cpus(&event->event);
+ ssr_event_ack = event->event;
+ ssr_crash = qdev->ssr_mhi_buf;
+
+ switch (event->event) {
+ case BEFORE_SHUTDOWN:
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_SHUTDOWN);
+ qaic_dbc_enter_ssr(qdev, hdr->dbc_id);
+ break;
+ case AFTER_SHUTDOWN:
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_SHUTDOWN);
+ break;
+ case BEFORE_POWER_UP:
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_POWER_UP);
+ break;
+ case AFTER_POWER_UP:
+ /*
+ * If dump info is a non NULL value it means that we
+ * have received this SSR event while downloading a
+ * crashdump for this DBC is still in progress. NACK
+ * the SSR event
+ */
+ if (ssr_crash && ssr_crash->dump_info) {
+ free_ssr_dump_info(ssr_crash);
+ ssr_event_ack = SSR_EVENT_NACK;
+ break;
+ }
+
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_POWER_UP);
+ break;
+ default:
+ break;
+ }
+
+ event_rsp = kmalloc(sizeof(*event_rsp), GFP_KERNEL);
+ if (!event_rsp)
+ break;
+
+ event_rsp->hdr.cmd = cpu_to_le32(SSR_EVENT_RSP);
+ event_rsp->hdr.len = cpu_to_le32(sizeof(*event_rsp));
+ event_rsp->hdr.dbc_id = cpu_to_le32(hdr->dbc_id);
+ event_rsp->event = cpu_to_le32(ssr_event_ack);
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, event_rsp, sizeof(*event_rsp),
+ MHI_EOT);
+ if (ret)
+ kfree(event_rsp);
+
+ if (event->event == AFTER_POWER_UP && ssr_event_ack != SSR_EVENT_NACK) {
+ qaic_dbc_exit_ssr(qdev);
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_IDLE);
+ }
+
+ break;
+ case DEBUG_TRANSFER_DONE_RSP:
+ dbg_xfer_done_rsp(qdev, dbc, (struct ssr_debug_transfer_done_rsp *)hdr);
+ break;
+ default:
+ break;
+ }
+
+out:
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
+ if (ret)
+ kfree(resp);
+}
+
+static int qaic_ssr_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ struct ssr_resp *resp;
+ int ret;
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ return ret;
+
+ resp = kzalloc(sizeof(*resp) + SSR_RESP_MSG_SZ, GFP_KERNEL);
+ if (!resp) {
+ mhi_unprepare_from_transfer(mhi_dev);
+ return -ENOMEM;
+ }
+
+ resp->qdev = qdev;
+ INIT_WORK(&resp->work, ssr_worker);
+
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
+ if (ret) {
+ kfree(resp);
+ mhi_unprepare_from_transfer(mhi_dev);
+ return ret;
+ }
+
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+ qdev->ssr_ch = mhi_dev;
+
+ return 0;
+}
+
+static void qaic_ssr_mhi_remove(struct mhi_device *mhi_dev)
+{
+ struct qaic_device *qdev;
+
+ qdev = dev_get_drvdata(&mhi_dev->dev);
+ mhi_unprepare_from_transfer(qdev->ssr_ch);
+ qdev->ssr_ch = NULL;
+}
+
+static void qaic_ssr_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ struct _ssr_hdr *hdr = mhi_result->buf_addr;
+ struct ssr_dump_info *dump_info;
+
+ if (mhi_result->transaction_status) {
+ kfree(mhi_result->buf_addr);
+ return;
+ }
+
+ /*
+ * MEMORY READ is used to download crashdump. And crashdump is
+ * downloaded chunk by chunk in a series of MEMORY READ SSR commands.
+ * Hence to avoid too many kmalloc() and kfree() of the same MEMORY READ
+ * request buffer, we allocate only one such buffer and free it only
+ * once.
+ */
+ if (le32_to_cpu(hdr->cmd) == MEMORY_READ) {
+ dump_info = ssr_crash->dump_info;
+ if (dump_info) {
+ dump_info->read_buf_req_queued = false;
+ return;
+ }
+ }
+
+ kfree(mhi_result->buf_addr);
+}
+
+static void qaic_ssr_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct ssr_resp *resp = container_of(mhi_result->buf_addr, struct ssr_resp, data);
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ bool memory_read_rsp = false;
+
+ if (ssr_crash && ssr_crash->data == mhi_result->buf_addr)
+ memory_read_rsp = true;
+
+ if (mhi_result->transaction_status) {
+ /* Do not free SSR crashdump buffer as it allocated via managed APIs */
+ if (!memory_read_rsp)
+ kfree(resp);
+ return;
+ }
+
+ if (memory_read_rsp)
+ queue_work(qdev->ssr_wq, &ssr_crash->work);
+ else
+ queue_work(qdev->ssr_wq, &resp->work);
+}
+
+static const struct mhi_device_id qaic_ssr_mhi_match_table[] = {
+ { .chan = "QAIC_SSR", },
+ {},
+};
+
+static struct mhi_driver qaic_ssr_mhi_driver = {
+ .id_table = qaic_ssr_mhi_match_table,
+ .remove = qaic_ssr_mhi_remove,
+ .probe = qaic_ssr_mhi_probe,
+ .ul_xfer_cb = qaic_ssr_mhi_ul_xfer_cb,
+ .dl_xfer_cb = qaic_ssr_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_ssr",
+ },
+};
+
+int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm)
+{
+ struct ssr_crashdump *ssr_crash;
+
+ qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
+
+ /*
+ * Device requests only one SSR at a time. So allocating only one
+ * buffer to download crashdump is good enough.
+ */
+ ssr_crash = drmm_kzalloc(drm, SSR_MHI_BUF_SIZE, GFP_KERNEL);
+ if (!ssr_crash)
+ return -ENOMEM;
+
+ ssr_crash->qdev = qdev;
+ INIT_WORK(&ssr_crash->work, ssr_dump_worker);
+ qdev->ssr_mhi_buf = ssr_crash;
+
+ return 0;
+}
+
+int qaic_ssr_register(void)
+{
+ return mhi_driver_register(&qaic_ssr_mhi_driver);
+}
+
+void qaic_ssr_unregister(void)
+{
+ mhi_driver_unregister(&qaic_ssr_mhi_driver);
+}
diff --git a/drivers/accel/qaic/qaic_ssr.h b/drivers/accel/qaic/qaic_ssr.h
new file mode 100644
index 000000000000..97ccff305750
--- /dev/null
+++ b/drivers/accel/qaic/qaic_ssr.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __QAIC_SSR_H__
+#define __QAIC_SSR_H__
+
+struct drm_device;
+struct qaic_device;
+
+int qaic_ssr_register(void);
+void qaic_ssr_unregister(void);
+void qaic_clean_up_ssr(struct qaic_device *qdev);
+int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm);
+#endif /* __QAIC_SSR_H__ */
diff --git a/drivers/accel/qaic/qaic_sysfs.c b/drivers/accel/qaic/qaic_sysfs.c
new file mode 100644
index 000000000000..e0afb0ffb589
--- /dev/null
+++ b/drivers/accel/qaic/qaic_sysfs.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2025, The Linux Foundation. All rights reserved. */
+
+#include <drm/drm_file.h>
+#include <drm/drm_managed.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#include "qaic.h"
+
+#define NAME_LEN 14
+
+struct dbc_attribute {
+ struct device_attribute dev_attr;
+ u32 dbc_id;
+ char name[NAME_LEN];
+};
+
+static ssize_t dbc_state_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+ struct dbc_attribute *dbc_attr = container_of(a, struct dbc_attribute, dev_attr);
+ struct drm_minor *minor = dev_get_drvdata(dev);
+ struct qaic_device *qdev;
+
+ qdev = to_qaic_device(minor->dev);
+ return sysfs_emit(buf, "%d\n", qdev->dbc[dbc_attr->dbc_id].state);
+}
+
+void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state)
+{
+ struct device *kdev = to_accel_kdev(qdev->qddev);
+ char *envp[3] = {};
+ char state_str[16];
+ char id_str[12];
+
+ envp[0] = id_str;
+ envp[1] = state_str;
+
+ if (state >= DBC_STATE_MAX)
+ return;
+ if (dbc_id >= qdev->num_dbc)
+ return;
+ if (state == qdev->dbc[dbc_id].state)
+ return;
+
+ scnprintf(id_str, ARRAY_SIZE(id_str), "DBC_ID=%d", dbc_id);
+ scnprintf(state_str, ARRAY_SIZE(state_str), "DBC_STATE=%d", state);
+
+ qdev->dbc[dbc_id].state = state;
+ kobject_uevent_env(&kdev->kobj, KOBJ_CHANGE, envp);
+}
+
+int qaic_sysfs_init(struct qaic_drm_device *qddev)
+{
+ struct device *kdev = to_accel_kdev(qddev);
+ struct drm_device *drm = to_drm(qddev);
+ u32 num_dbc = qddev->qdev->num_dbc;
+ struct dbc_attribute *dbc_attrs;
+ int i, ret;
+
+ dbc_attrs = drmm_kcalloc(drm, num_dbc, sizeof(*dbc_attrs), GFP_KERNEL);
+ if (!dbc_attrs)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dbc; ++i) {
+ struct dbc_attribute *dbc_attr = &dbc_attrs[i];
+
+ sysfs_attr_init(&dbc_attr->dev_attr.attr);
+ dbc_attr->dbc_id = i;
+ scnprintf(dbc_attr->name, NAME_LEN, "dbc%d_state", i);
+ dbc_attr->dev_attr.attr.name = dbc_attr->name;
+ dbc_attr->dev_attr.attr.mode = 0444;
+ dbc_attr->dev_attr.show = dbc_state_show;
+ ret = sysfs_create_file(&kdev->kobj, &dbc_attr->dev_attr.attr);
+ if (ret) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ dbc_attr = &dbc_attrs[j];
+ sysfs_remove_file(&kdev->kobj, &dbc_attr->dev_attr.attr);
+ }
+ drmm_kfree(drm, dbc_attrs);
+ return ret;
+ }
+ }
+
+ qddev->sysfs_attrs = dbc_attrs;
+ return 0;
+}
+
+void qaic_sysfs_remove(struct qaic_drm_device *qddev)
+{
+ struct dbc_attribute *dbc_attrs = qddev->sysfs_attrs;
+ struct device *kdev = to_accel_kdev(qddev);
+ u32 num_dbc = qddev->qdev->num_dbc;
+ int i;
+
+ if (!dbc_attrs)
+ return;
+
+ qddev->sysfs_attrs = NULL;
+ for (i = 0; i < num_dbc; ++i)
+ sysfs_remove_file(&kdev->kobj, &dbc_attrs[i].dev_attr.attr);
+ drmm_kfree(to_drm(qddev), dbc_attrs);
+}
diff --git a/drivers/accel/qaic/qaic_timesync.c b/drivers/accel/qaic/qaic_timesync.c
new file mode 100644
index 000000000000..8af2475f4f36
--- /dev/null
+++ b/drivers/accel/qaic/qaic_timesync.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/mhi.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/time64.h>
+#include <linux/timer.h>
+
+#include "qaic.h"
+#include "qaic_timesync.h"
+
+#define QTIMER_REG_OFFSET 0xa28
+#define QAIC_TIMESYNC_SIGNATURE 0x55aa
+#define QAIC_CONV_QTIMER_TO_US(qtimer) (mul_u64_u32_div(qtimer, 10, 192))
+
+static unsigned int timesync_delay_ms = 1000; /* 1 sec default */
+module_param(timesync_delay_ms, uint, 0600);
+MODULE_PARM_DESC(timesync_delay_ms, "Delay in ms between two consecutive timesync operations");
+
+enum qts_msg_type {
+ QAIC_TS_CMD_TO_HOST,
+ QAIC_TS_SYNC_REQ,
+ QAIC_TS_ACK_TO_HOST,
+ QAIC_TS_MSG_TYPE_MAX
+};
+
+/**
+ * struct qts_hdr - Timesync message header structure.
+ * @signature: Unique signature to identify the timesync message.
+ * @reserved_1: Reserved for future use.
+ * @reserved_2: Reserved for future use.
+ * @msg_type: sub-type of the timesync message.
+ * @reserved_3: Reserved for future use.
+ */
+struct qts_hdr {
+ __le16 signature;
+ __le16 reserved_1;
+ u8 reserved_2;
+ u8 msg_type;
+ __le16 reserved_3;
+} __packed;
+
+/**
+ * struct qts_timeval - Structure to carry time information.
+ * @tv_sec: Seconds part of the time.
+ * @tv_usec: uS (microseconds) part of the time.
+ */
+struct qts_timeval {
+ __le64 tv_sec;
+ __le64 tv_usec;
+} __packed;
+
+/**
+ * struct qts_host_time_sync_msg_data - Structure to denote the timesync message.
+ * @header: Header of the timesync message.
+ * @data: Time information.
+ */
+struct qts_host_time_sync_msg_data {
+ struct qts_hdr header;
+ struct qts_timeval data;
+} __packed;
+
+/**
+ * struct mqts_dev - MHI QAIC Timesync Control device.
+ * @qdev: Pointer to the root device struct driven by QAIC driver.
+ * @mhi_dev: Pointer to associated MHI device.
+ * @timer: Timer handle used for timesync.
+ * @qtimer_addr: Device QTimer register pointer.
+ * @buff_in_use: atomic variable to track if the sync_msg buffer is in use.
+ * @dev: Device pointer to qdev->pdev->dev stored for easy access.
+ * @sync_msg: Buffer used to send timesync message over MHI.
+ */
+struct mqts_dev {
+ struct qaic_device *qdev;
+ struct mhi_device *mhi_dev;
+ struct timer_list timer;
+ void __iomem *qtimer_addr;
+ atomic_t buff_in_use;
+ struct device *dev;
+ struct qts_host_time_sync_msg_data *sync_msg;
+};
+
+struct qts_resp_msg {
+ struct qts_hdr hdr;
+} __packed;
+
+struct qts_resp {
+ struct qts_resp_msg data;
+ struct work_struct work;
+ struct qaic_device *qdev;
+};
+
+#ifdef readq
+static u64 read_qtimer(const volatile void __iomem *addr)
+{
+ return readq(addr);
+}
+#else
+static u64 read_qtimer(const volatile void __iomem *addr)
+{
+ u64 low, high;
+
+ low = readl(addr);
+ high = readl(addr + sizeof(u32));
+ return low | (high << 32);
+}
+#endif
+
+static void qaic_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
+
+ dev_dbg(mqtsdev->dev, "%s status: %d xfer_len: %zu\n", __func__,
+ mhi_result->transaction_status, mhi_result->bytes_xferd);
+
+ atomic_set(&mqtsdev->buff_in_use, 0);
+}
+
+static void qaic_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
+
+ dev_err(mqtsdev->dev, "%s no data expected on dl channel\n", __func__);
+}
+
+static void qaic_timesync_timer(struct timer_list *t)
+{
+ struct mqts_dev *mqtsdev = timer_container_of(mqtsdev, t, timer);
+ struct qts_host_time_sync_msg_data *sync_msg;
+ u64 device_qtimer_us;
+ u64 device_qtimer;
+ u64 host_time_us;
+ u64 offset_us;
+ u64 host_sec;
+ int ret;
+
+ if (atomic_read(&mqtsdev->buff_in_use)) {
+ dev_dbg(mqtsdev->dev, "%s buffer not free, schedule next cycle\n", __func__);
+ goto mod_timer;
+ }
+ atomic_set(&mqtsdev->buff_in_use, 1);
+
+ sync_msg = mqtsdev->sync_msg;
+ sync_msg->header.signature = cpu_to_le16(QAIC_TIMESYNC_SIGNATURE);
+ sync_msg->header.msg_type = QAIC_TS_SYNC_REQ;
+ /* Read host UTC time and convert to uS*/
+ host_time_us = div_u64(ktime_get_real_ns(), NSEC_PER_USEC);
+ device_qtimer = read_qtimer(mqtsdev->qtimer_addr);
+ device_qtimer_us = QAIC_CONV_QTIMER_TO_US(device_qtimer);
+ /* Offset between host UTC and device time */
+ offset_us = host_time_us - device_qtimer_us;
+
+ host_sec = div_u64(offset_us, USEC_PER_SEC);
+ sync_msg->data.tv_usec = cpu_to_le64(offset_us - host_sec * USEC_PER_SEC);
+ sync_msg->data.tv_sec = cpu_to_le64(host_sec);
+ ret = mhi_queue_buf(mqtsdev->mhi_dev, DMA_TO_DEVICE, sync_msg, sizeof(*sync_msg), MHI_EOT);
+ if (ret && (ret != -EAGAIN)) {
+ dev_err(mqtsdev->dev, "%s unable to queue to mhi:%d\n", __func__, ret);
+ return;
+ } else if (ret == -EAGAIN) {
+ atomic_set(&mqtsdev->buff_in_use, 0);
+ }
+
+mod_timer:
+ ret = mod_timer(t, jiffies + msecs_to_jiffies(timesync_delay_ms));
+ if (ret)
+ dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret);
+}
+
+void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev)
+{
+ struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
+
+ timer_delete_sync(&mqtsdev->timer);
+}
+
+static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ struct mqts_dev *mqtsdev;
+ struct timer_list *timer;
+ int ret;
+
+ mqtsdev = kzalloc(sizeof(*mqtsdev), GFP_KERNEL);
+ if (!mqtsdev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ timer = &mqtsdev->timer;
+ mqtsdev->mhi_dev = mhi_dev;
+ mqtsdev->qdev = qdev;
+ mqtsdev->dev = &qdev->pdev->dev;
+
+ mqtsdev->sync_msg = kzalloc(sizeof(*mqtsdev->sync_msg), GFP_KERNEL);
+ if (!mqtsdev->sync_msg) {
+ ret = -ENOMEM;
+ goto free_mqts_dev;
+ }
+ atomic_set(&mqtsdev->buff_in_use, 0);
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ goto free_sync_msg;
+
+ /* Qtimer register pointer */
+ mqtsdev->qtimer_addr = qdev->bar_mhi + QTIMER_REG_OFFSET;
+ timer_setup(timer, qaic_timesync_timer, 0);
+ timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
+ add_timer(timer);
+ dev_set_drvdata(&mhi_dev->dev, mqtsdev);
+ qdev->mqts_ch = mhi_dev;
+
+ return 0;
+
+free_sync_msg:
+ kfree(mqtsdev->sync_msg);
+free_mqts_dev:
+ kfree(mqtsdev);
+out:
+ return ret;
+};
+
+static void qaic_timesync_remove(struct mhi_device *mhi_dev)
+{
+ struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
+
+ mqtsdev->qdev->mqts_ch = NULL;
+ timer_delete_sync(&mqtsdev->timer);
+ mhi_unprepare_from_transfer(mqtsdev->mhi_dev);
+ kfree(mqtsdev->sync_msg);
+ kfree(mqtsdev);
+}
+
+static const struct mhi_device_id qaic_timesync_match_table[] = {
+ { .chan = "QAIC_TIMESYNC_PERIODIC"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(mhi, qaic_timesync_match_table);
+
+static struct mhi_driver qaic_timesync_driver = {
+ .id_table = qaic_timesync_match_table,
+ .remove = qaic_timesync_remove,
+ .probe = qaic_timesync_probe,
+ .ul_xfer_cb = qaic_timesync_ul_xfer_cb,
+ .dl_xfer_cb = qaic_timesync_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_timesync_periodic",
+ },
+};
+
+static void qaic_boot_timesync_worker(struct work_struct *work)
+{
+ struct qts_resp *resp = container_of(work, struct qts_resp, work);
+ struct qts_host_time_sync_msg_data *req;
+ struct qts_resp_msg data = resp->data;
+ struct qaic_device *qdev = resp->qdev;
+ struct mhi_device *mhi_dev;
+ struct timespec64 ts;
+ int ret;
+
+ mhi_dev = qdev->qts_ch;
+ /* Queue the response message beforehand to avoid race conditions */
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT);
+ if (ret) {
+ kfree(resp);
+ dev_warn(&mhi_dev->dev, "Failed to re-queue response buffer %d\n", ret);
+ return;
+ }
+
+ switch (data.hdr.msg_type) {
+ case QAIC_TS_CMD_TO_HOST:
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ break;
+
+ req->header = data.hdr;
+ req->header.msg_type = QAIC_TS_SYNC_REQ;
+ ktime_get_real_ts64(&ts);
+ req->data.tv_sec = cpu_to_le64(ts.tv_sec);
+ req->data.tv_usec = cpu_to_le64(div_u64(ts.tv_nsec, NSEC_PER_USEC));
+
+ ret = mhi_queue_buf(mhi_dev, DMA_TO_DEVICE, req, sizeof(*req), MHI_EOT);
+ if (ret) {
+ kfree(req);
+ dev_dbg(&mhi_dev->dev, "Failed to send request message. Error %d\n", ret);
+ }
+ break;
+ case QAIC_TS_ACK_TO_HOST:
+ dev_dbg(&mhi_dev->dev, "ACK received from device\n");
+ break;
+ default:
+ dev_err(&mhi_dev->dev, "Invalid message type %u.\n", data.hdr.msg_type);
+ }
+}
+
+static int qaic_boot_timesync_queue_resp(struct mhi_device *mhi_dev, struct qaic_device *qdev)
+{
+ struct qts_resp *resp;
+ int ret;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ resp->qdev = qdev;
+ INIT_WORK(&resp->work, qaic_boot_timesync_worker);
+
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT);
+ if (ret) {
+ kfree(resp);
+ dev_warn(&mhi_dev->dev, "Failed to queue response buffer %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void qaic_boot_timesync_remove(struct mhi_device *mhi_dev)
+{
+ struct qaic_device *qdev;
+
+ qdev = dev_get_drvdata(&mhi_dev->dev);
+ mhi_unprepare_from_transfer(qdev->qts_ch);
+ qdev->qts_ch = NULL;
+}
+
+static int qaic_boot_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ int ret;
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ return ret;
+
+ qdev->qts_ch = mhi_dev;
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+
+ ret = qaic_boot_timesync_queue_resp(mhi_dev, qdev);
+ if (ret) {
+ dev_set_drvdata(&mhi_dev->dev, NULL);
+ qdev->qts_ch = NULL;
+ mhi_unprepare_from_transfer(mhi_dev);
+ }
+
+ return ret;
+}
+
+static void qaic_boot_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ kfree(mhi_result->buf_addr);
+}
+
+static void qaic_boot_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct qts_resp *resp = container_of(mhi_result->buf_addr, struct qts_resp, data);
+
+ if (mhi_result->transaction_status || mhi_result->bytes_xferd != sizeof(resp->data)) {
+ kfree(resp);
+ return;
+ }
+
+ queue_work(resp->qdev->qts_wq, &resp->work);
+}
+
+static const struct mhi_device_id qaic_boot_timesync_match_table[] = {
+ { .chan = "QAIC_TIMESYNC"},
+ {},
+};
+
+static struct mhi_driver qaic_boot_timesync_driver = {
+ .id_table = qaic_boot_timesync_match_table,
+ .remove = qaic_boot_timesync_remove,
+ .probe = qaic_boot_timesync_probe,
+ .ul_xfer_cb = qaic_boot_timesync_ul_xfer_cb,
+ .dl_xfer_cb = qaic_boot_timesync_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_timesync",
+ },
+};
+
+int qaic_timesync_init(void)
+{
+ int ret;
+
+ ret = mhi_driver_register(&qaic_timesync_driver);
+ if (ret)
+ return ret;
+ ret = mhi_driver_register(&qaic_boot_timesync_driver);
+
+ return ret;
+}
+
+void qaic_timesync_deinit(void)
+{
+ mhi_driver_unregister(&qaic_boot_timesync_driver);
+ mhi_driver_unregister(&qaic_timesync_driver);
+}
diff --git a/drivers/accel/qaic/qaic_timesync.h b/drivers/accel/qaic/qaic_timesync.h
new file mode 100644
index 000000000000..77b9c2b55057
--- /dev/null
+++ b/drivers/accel/qaic/qaic_timesync.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __QAIC_TIMESYNC_H__
+#define __QAIC_TIMESYNC_H__
+
+#include <linux/mhi.h>
+
+int qaic_timesync_init(void);
+void qaic_timesync_deinit(void);
+void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev);
+#endif /* __QAIC_TIMESYNC_H__ */
diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c
new file mode 100644
index 000000000000..fd3c3b2d1fd3
--- /dev/null
+++ b/drivers/accel/qaic/sahara.c
@@ -0,0 +1,935 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/devcoredump.h>
+#include <linux/firmware.h>
+#include <linux/limits.h>
+#include <linux/mhi.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/overflow.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+#include "sahara.h"
+
+#define SAHARA_HELLO_CMD 0x1 /* Min protocol version 1.0 */
+#define SAHARA_HELLO_RESP_CMD 0x2 /* Min protocol version 1.0 */
+#define SAHARA_READ_DATA_CMD 0x3 /* Min protocol version 1.0 */
+#define SAHARA_END_OF_IMAGE_CMD 0x4 /* Min protocol version 1.0 */
+#define SAHARA_DONE_CMD 0x5 /* Min protocol version 1.0 */
+#define SAHARA_DONE_RESP_CMD 0x6 /* Min protocol version 1.0 */
+#define SAHARA_RESET_CMD 0x7 /* Min protocol version 1.0 */
+#define SAHARA_RESET_RESP_CMD 0x8 /* Min protocol version 1.0 */
+#define SAHARA_MEM_DEBUG_CMD 0x9 /* Min protocol version 2.0 */
+#define SAHARA_MEM_READ_CMD 0xa /* Min protocol version 2.0 */
+#define SAHARA_CMD_READY_CMD 0xb /* Min protocol version 2.1 */
+#define SAHARA_SWITCH_MODE_CMD 0xc /* Min protocol version 2.1 */
+#define SAHARA_EXECUTE_CMD 0xd /* Min protocol version 2.1 */
+#define SAHARA_EXECUTE_RESP_CMD 0xe /* Min protocol version 2.1 */
+#define SAHARA_EXECUTE_DATA_CMD 0xf /* Min protocol version 2.1 */
+#define SAHARA_MEM_DEBUG64_CMD 0x10 /* Min protocol version 2.5 */
+#define SAHARA_MEM_READ64_CMD 0x11 /* Min protocol version 2.5 */
+#define SAHARA_READ_DATA64_CMD 0x12 /* Min protocol version 2.8 */
+#define SAHARA_RESET_STATE_CMD 0x13 /* Min protocol version 2.9 */
+#define SAHARA_WRITE_DATA_CMD 0x14 /* Min protocol version 3.0 */
+
+#define SAHARA_PACKET_MAX_SIZE 0xffffU /* MHI_MAX_MTU */
+#define SAHARA_TRANSFER_MAX_SIZE 0x80000
+#define SAHARA_READ_MAX_SIZE 0xfff0U /* Avoid unaligned requests */
+#define SAHARA_NUM_TX_BUF DIV_ROUND_UP(SAHARA_TRANSFER_MAX_SIZE,\
+ SAHARA_PACKET_MAX_SIZE)
+#define SAHARA_IMAGE_ID_NONE U32_MAX
+
+#define SAHARA_VERSION 2
+#define SAHARA_SUCCESS 0
+#define SAHARA_TABLE_ENTRY_STR_LEN 20
+
+#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
+#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
+#define SAHARA_MODE_MEMORY_DEBUG 0x2
+#define SAHARA_MODE_COMMAND 0x3
+
+#define SAHARA_HELLO_LENGTH 0x30
+#define SAHARA_READ_DATA_LENGTH 0x14
+#define SAHARA_END_OF_IMAGE_LENGTH 0x10
+#define SAHARA_DONE_LENGTH 0x8
+#define SAHARA_RESET_LENGTH 0x8
+#define SAHARA_MEM_DEBUG64_LENGTH 0x18
+#define SAHARA_MEM_READ64_LENGTH 0x18
+
+struct sahara_packet {
+ __le32 cmd;
+ __le32 length;
+
+ union {
+ struct {
+ __le32 version;
+ __le32 version_compat;
+ __le32 max_length;
+ __le32 mode;
+ } hello;
+ struct {
+ __le32 version;
+ __le32 version_compat;
+ __le32 status;
+ __le32 mode;
+ } hello_resp;
+ struct {
+ __le32 image;
+ __le32 offset;
+ __le32 length;
+ } read_data;
+ struct {
+ __le32 image;
+ __le32 status;
+ } end_of_image;
+ struct {
+ __le64 table_address;
+ __le64 table_length;
+ } memory_debug64;
+ struct {
+ __le64 memory_address;
+ __le64 memory_length;
+ } memory_read64;
+ };
+};
+
+struct sahara_debug_table_entry64 {
+ __le64 type;
+ __le64 address;
+ __le64 length;
+ char description[SAHARA_TABLE_ENTRY_STR_LEN];
+ char filename[SAHARA_TABLE_ENTRY_STR_LEN];
+};
+
+struct sahara_dump_table_entry {
+ u64 type;
+ u64 address;
+ u64 length;
+ char description[SAHARA_TABLE_ENTRY_STR_LEN];
+ char filename[SAHARA_TABLE_ENTRY_STR_LEN];
+};
+
+#define SAHARA_DUMP_V1_MAGIC 0x1234567890abcdef
+#define SAHARA_DUMP_V1_VER 1
+struct sahara_memory_dump_meta_v1 {
+ u64 magic;
+ u64 version;
+ u64 dump_size;
+ u64 table_size;
+};
+
+/*
+ * Layout of crashdump provided to user via devcoredump
+ * +------------------------------------------+
+ * | Crashdump Meta structure |
+ * | type: struct sahara_memory_dump_meta_v1 |
+ * +------------------------------------------+
+ * | Crashdump Table |
+ * | type: array of struct |
+ * | sahara_dump_table_entry |
+ * | |
+ * | |
+ * +------------------------------------------+
+ * | Crashdump |
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * +------------------------------------------+
+ *
+ * First is the metadata header. Userspace can use the magic number to verify
+ * the content type, and then check the version for the rest of the format.
+ * New versions should keep the magic number location/value, and version
+ * location, but increment the version value.
+ *
+ * For v1, the metadata lists the size of the entire dump (header + table +
+ * dump) and the size of the table. Then the dump image table, which describes
+ * the contents of the dump. Finally all the images are listed in order, with
+ * no deadspace in between. Userspace can use the sizes listed in the image
+ * table to reconstruct the individual images.
+ */
+
+struct sahara_context {
+ struct sahara_packet *tx[SAHARA_NUM_TX_BUF];
+ struct sahara_packet *rx;
+ struct work_struct fw_work;
+ struct work_struct dump_work;
+ struct work_struct read_data_work;
+ struct mhi_device *mhi_dev;
+ const char * const *image_table;
+ u32 table_size;
+ u32 active_image_id;
+ const struct firmware *firmware;
+ u64 dump_table_address;
+ u64 dump_table_length;
+ size_t rx_size;
+ size_t rx_size_requested;
+ void *mem_dump;
+ size_t mem_dump_sz;
+ struct sahara_dump_table_entry *dump_image;
+ u64 dump_image_offset;
+ void *mem_dump_freespace;
+ u64 dump_images_left;
+ u32 read_data_offset;
+ u32 read_data_length;
+ bool is_mem_dump_mode;
+ bool non_streaming;
+};
+
+static const char * const aic100_image_table[] = {
+ [1] = "qcom/aic100/fw1.bin",
+ [2] = "qcom/aic100/fw2.bin",
+ [4] = "qcom/aic100/fw4.bin",
+ [5] = "qcom/aic100/fw5.bin",
+ [6] = "qcom/aic100/fw6.bin",
+ [8] = "qcom/aic100/fw8.bin",
+ [9] = "qcom/aic100/fw9.bin",
+ [10] = "qcom/aic100/fw10.bin",
+};
+
+static const char * const aic200_image_table[] = {
+ [5] = "qcom/aic200/uefi.elf",
+ [12] = "qcom/aic200/aic200-nsp.bin",
+ [23] = "qcom/aic200/aop.mbn",
+ [32] = "qcom/aic200/tz.mbn",
+ [33] = "qcom/aic200/hypvm.mbn",
+ [38] = "qcom/aic200/xbl_config.elf",
+ [39] = "qcom/aic200/aic200_abl.elf",
+ [40] = "qcom/aic200/apdp.mbn",
+ [41] = "qcom/aic200/devcfg.mbn",
+ [42] = "qcom/aic200/sec.elf",
+ [43] = "qcom/aic200/aic200-hlos.elf",
+ [49] = "qcom/aic200/shrm.elf",
+ [50] = "qcom/aic200/cpucp.elf",
+ [51] = "qcom/aic200/aop_devcfg.mbn",
+ [54] = "qcom/aic200/qupv3fw.elf",
+ [57] = "qcom/aic200/cpucp_dtbs.elf",
+ [62] = "qcom/aic200/uefi_dtbs.elf",
+ [63] = "qcom/aic200/xbl_ac_config.mbn",
+ [64] = "qcom/aic200/tz_ac_config.mbn",
+ [65] = "qcom/aic200/hyp_ac_config.mbn",
+ [66] = "qcom/aic200/pdp.elf",
+ [67] = "qcom/aic200/pdp_cdb.elf",
+ [68] = "qcom/aic200/sdi.mbn",
+ [69] = "qcom/aic200/dcd.mbn",
+ [73] = "qcom/aic200/gearvm.mbn",
+ [74] = "qcom/aic200/sti.bin",
+ [76] = "qcom/aic200/tz_qti_config.mbn",
+ [78] = "qcom/aic200/pvs.bin",
+};
+
+static bool is_streaming(struct sahara_context *context)
+{
+ return !context->non_streaming;
+}
+
+static int sahara_find_image(struct sahara_context *context, u32 image_id)
+{
+ int ret;
+
+ if (image_id == context->active_image_id)
+ return 0;
+
+ if (context->active_image_id != SAHARA_IMAGE_ID_NONE) {
+ dev_err(&context->mhi_dev->dev, "image id %d is not valid as %d is active\n",
+ image_id, context->active_image_id);
+ return -EINVAL;
+ }
+
+ if (image_id >= context->table_size || !context->image_table[image_id]) {
+ dev_err(&context->mhi_dev->dev, "request for unknown image: %d\n", image_id);
+ return -EINVAL;
+ }
+
+ /*
+ * This image might be optional. The device may continue without it.
+ * Only the device knows. Suppress error messages that could suggest an
+ * a problem when we were actually able to continue.
+ */
+ ret = firmware_request_nowarn(&context->firmware,
+ context->image_table[image_id],
+ &context->mhi_dev->dev);
+ if (ret) {
+ dev_dbg(&context->mhi_dev->dev, "request for image id %d / file %s failed %d\n",
+ image_id, context->image_table[image_id], ret);
+ return ret;
+ }
+
+ context->active_image_id = image_id;
+
+ return 0;
+}
+
+static void sahara_release_image(struct sahara_context *context)
+{
+ if (context->active_image_id != SAHARA_IMAGE_ID_NONE)
+ release_firmware(context->firmware);
+ context->active_image_id = SAHARA_IMAGE_ID_NONE;
+}
+
+static void sahara_send_reset(struct sahara_context *context)
+{
+ int ret;
+
+ context->is_mem_dump_mode = false;
+ context->read_data_offset = 0;
+ context->read_data_length = 0;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_RESET_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send reset response %d\n", ret);
+}
+
+static void sahara_hello(struct sahara_context *context)
+{
+ int ret;
+
+ dev_dbg(&context->mhi_dev->dev,
+ "HELLO cmd received. length:%d version:%d version_compat:%d max_length:%d mode:%d\n",
+ le32_to_cpu(context->rx->length),
+ le32_to_cpu(context->rx->hello.version),
+ le32_to_cpu(context->rx->hello.version_compat),
+ le32_to_cpu(context->rx->hello.max_length),
+ le32_to_cpu(context->rx->hello.mode));
+
+ if (le32_to_cpu(context->rx->length) != SAHARA_HELLO_LENGTH) {
+ dev_err(&context->mhi_dev->dev, "Malformed hello packet - length %d\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+ if (le32_to_cpu(context->rx->hello.version) != SAHARA_VERSION) {
+ dev_err(&context->mhi_dev->dev, "Unsupported hello packet - version %d\n",
+ le32_to_cpu(context->rx->hello.version));
+ return;
+ }
+
+ if (le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_PENDING &&
+ le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE &&
+ le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_MEMORY_DEBUG) {
+ dev_err(&context->mhi_dev->dev, "Unsupported hello packet - mode %d\n",
+ le32_to_cpu(context->rx->hello.mode));
+ return;
+ }
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_HELLO_RESP_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_HELLO_LENGTH);
+ context->tx[0]->hello_resp.version = cpu_to_le32(SAHARA_VERSION);
+ context->tx[0]->hello_resp.version_compat = cpu_to_le32(SAHARA_VERSION);
+ context->tx[0]->hello_resp.status = cpu_to_le32(SAHARA_SUCCESS);
+ context->tx[0]->hello_resp.mode = context->rx->hello_resp.mode;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_HELLO_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret);
+}
+
+static int read_data_helper(struct sahara_context *context, int buf_index)
+{
+ enum mhi_flags mhi_flag;
+ u32 pkt_data_len;
+ int ret;
+
+ pkt_data_len = min(context->read_data_length, SAHARA_PACKET_MAX_SIZE);
+
+ memcpy(context->tx[buf_index],
+ &context->firmware->data[context->read_data_offset],
+ pkt_data_len);
+
+ context->read_data_offset += pkt_data_len;
+ context->read_data_length -= pkt_data_len;
+
+ if (is_streaming(context) || !context->read_data_length)
+ mhi_flag = MHI_EOT;
+ else
+ mhi_flag = MHI_CHAIN;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
+ context->tx[buf_index], pkt_data_len, mhi_flag);
+ if (ret) {
+ dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sahara_read_data(struct sahara_context *context)
+{
+ u32 image_id, data_offset, data_len;
+ int ret;
+ int i;
+
+ dev_dbg(&context->mhi_dev->dev,
+ "READ_DATA cmd received. length:%d image:%d offset:%d data_length:%d\n",
+ le32_to_cpu(context->rx->length),
+ le32_to_cpu(context->rx->read_data.image),
+ le32_to_cpu(context->rx->read_data.offset),
+ le32_to_cpu(context->rx->read_data.length));
+
+ if (le32_to_cpu(context->rx->length) != SAHARA_READ_DATA_LENGTH) {
+ dev_err(&context->mhi_dev->dev, "Malformed read_data packet - length %d\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+
+ image_id = le32_to_cpu(context->rx->read_data.image);
+ data_offset = le32_to_cpu(context->rx->read_data.offset);
+ data_len = le32_to_cpu(context->rx->read_data.length);
+
+ ret = sahara_find_image(context, image_id);
+ if (ret) {
+ sahara_send_reset(context);
+ return;
+ }
+
+ /*
+ * Image is released when the device is done with it via
+ * SAHARA_END_OF_IMAGE_CMD. sahara_send_reset() will either cause the
+ * device to retry the operation with a modification, or decide to be
+ * done with the image and trigger SAHARA_END_OF_IMAGE_CMD.
+ * release_image() is called from SAHARA_END_OF_IMAGE_CMD. processing
+ * and is not needed here on error.
+ */
+
+ if (context->non_streaming && data_len > SAHARA_TRANSFER_MAX_SIZE) {
+ dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data len %d exceeds max xfer size %d\n",
+ data_len, SAHARA_TRANSFER_MAX_SIZE);
+ sahara_send_reset(context);
+ return;
+ }
+
+ if (data_offset >= context->firmware->size) {
+ dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data offset %d exceeds file size %zu\n",
+ data_offset, context->firmware->size);
+ sahara_send_reset(context);
+ return;
+ }
+
+ if (size_add(data_offset, data_len) > context->firmware->size) {
+ dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data offset %d and length %d exceeds file size %zu\n",
+ data_offset, data_len, context->firmware->size);
+ sahara_send_reset(context);
+ return;
+ }
+
+ context->read_data_offset = data_offset;
+ context->read_data_length = data_len;
+
+ if (is_streaming(context)) {
+ schedule_work(&context->read_data_work);
+ return;
+ }
+
+ for (i = 0; i < SAHARA_NUM_TX_BUF && context->read_data_length; ++i) {
+ ret = read_data_helper(context, i);
+ if (ret)
+ break;
+ }
+}
+
+static void sahara_end_of_image(struct sahara_context *context)
+{
+ int ret;
+
+ dev_dbg(&context->mhi_dev->dev,
+ "END_OF_IMAGE cmd received. length:%d image:%d status:%d\n",
+ le32_to_cpu(context->rx->length),
+ le32_to_cpu(context->rx->end_of_image.image),
+ le32_to_cpu(context->rx->end_of_image.status));
+
+ if (le32_to_cpu(context->rx->length) != SAHARA_END_OF_IMAGE_LENGTH) {
+ dev_err(&context->mhi_dev->dev, "Malformed end_of_image packet - length %d\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+
+ if (context->active_image_id != SAHARA_IMAGE_ID_NONE &&
+ le32_to_cpu(context->rx->end_of_image.image) != context->active_image_id) {
+ dev_err(&context->mhi_dev->dev, "Malformed end_of_image packet - image %d is not the active image\n",
+ le32_to_cpu(context->rx->end_of_image.image));
+ return;
+ }
+
+ sahara_release_image(context);
+
+ if (le32_to_cpu(context->rx->end_of_image.status))
+ return;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_DONE_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_DONE_LENGTH);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_DONE_LENGTH, MHI_EOT);
+ if (ret)
+ dev_dbg(&context->mhi_dev->dev, "Unable to send done response %d\n", ret);
+}
+
+static void sahara_memory_debug64(struct sahara_context *context)
+{
+ int ret;
+
+ dev_dbg(&context->mhi_dev->dev,
+ "MEMORY DEBUG64 cmd received. length:%d table_address:%#llx table_length:%#llx\n",
+ le32_to_cpu(context->rx->length),
+ le64_to_cpu(context->rx->memory_debug64.table_address),
+ le64_to_cpu(context->rx->memory_debug64.table_length));
+
+ if (le32_to_cpu(context->rx->length) != SAHARA_MEM_DEBUG64_LENGTH) {
+ dev_err(&context->mhi_dev->dev, "Malformed memory debug64 packet - length %d\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+
+ context->dump_table_address = le64_to_cpu(context->rx->memory_debug64.table_address);
+ context->dump_table_length = le64_to_cpu(context->rx->memory_debug64.table_length);
+
+ if (context->dump_table_length % sizeof(struct sahara_debug_table_entry64) != 0 ||
+ !context->dump_table_length) {
+ dev_err(&context->mhi_dev->dev, "Malformed memory debug64 packet - table length %lld\n",
+ context->dump_table_length);
+ return;
+ }
+
+ /*
+ * From this point, the protocol flips. We make memory_read requests to
+ * the device, and the device responds with the raw data. If the device
+ * has an error, it will send an End of Image command. First we need to
+ * request the memory dump table so that we know where all the pieces
+ * of the dump are that we can consume.
+ */
+
+ context->is_mem_dump_mode = true;
+
+ /*
+ * Assume that the table is smaller than our MTU so that we can read it
+ * in one shot. The spec does not put an upper limit on the table, but
+ * no known device will exceed this.
+ */
+ if (context->dump_table_length > SAHARA_PACKET_MAX_SIZE) {
+ dev_err(&context->mhi_dev->dev, "Memory dump table length %lld exceeds supported size. Discarding dump\n",
+ context->dump_table_length);
+ sahara_send_reset(context);
+ return;
+ }
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH);
+ context->tx[0]->memory_read64.memory_address = cpu_to_le64(context->dump_table_address);
+ context->tx[0]->memory_read64.memory_length = cpu_to_le64(context->dump_table_length);
+
+ context->rx_size_requested = context->dump_table_length;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_MEM_READ64_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send read for dump table %d\n", ret);
+}
+
+static void sahara_processing(struct work_struct *work)
+{
+ struct sahara_context *context = container_of(work, struct sahara_context, fw_work);
+ int ret;
+
+ switch (le32_to_cpu(context->rx->cmd)) {
+ case SAHARA_HELLO_CMD:
+ sahara_hello(context);
+ break;
+ case SAHARA_READ_DATA_CMD:
+ sahara_read_data(context);
+ break;
+ case SAHARA_END_OF_IMAGE_CMD:
+ sahara_end_of_image(context);
+ break;
+ case SAHARA_DONE_RESP_CMD:
+ /* Intentional do nothing as we don't need to exit an app */
+ break;
+ case SAHARA_RESET_RESP_CMD:
+ /* Intentional do nothing as we don't need to exit an app */
+ break;
+ case SAHARA_MEM_DEBUG64_CMD:
+ sahara_memory_debug64(context);
+ break;
+ default:
+ dev_err(&context->mhi_dev->dev, "Unknown command %d\n",
+ le32_to_cpu(context->rx->cmd));
+ break;
+ }
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE, context->rx,
+ SAHARA_PACKET_MAX_SIZE, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret);
+}
+
+static void sahara_parse_dump_table(struct sahara_context *context)
+{
+ struct sahara_dump_table_entry *image_out_table;
+ struct sahara_debug_table_entry64 *dev_table;
+ struct sahara_memory_dump_meta_v1 *dump_meta;
+ u64 table_nents;
+ u64 dump_length;
+ u64 mul_bytes;
+ int ret;
+ u64 i;
+
+ table_nents = context->dump_table_length / sizeof(*dev_table);
+ context->dump_images_left = table_nents;
+ dump_length = 0;
+
+ dev_table = (struct sahara_debug_table_entry64 *)(context->rx);
+ for (i = 0; i < table_nents; ++i) {
+ /* Do not trust the device, ensure the strings are terminated */
+ dev_table[i].description[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
+ dev_table[i].filename[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
+
+ if (check_add_overflow(dump_length,
+ le64_to_cpu(dev_table[i].length),
+ &dump_length)) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+
+ dev_dbg(&context->mhi_dev->dev,
+ "Memory dump table entry %lld type: %lld address: %#llx length: %#llx description: \"%s\" filename \"%s\"\n",
+ i,
+ le64_to_cpu(dev_table[i].type),
+ le64_to_cpu(dev_table[i].address),
+ le64_to_cpu(dev_table[i].length),
+ dev_table[i].description,
+ dev_table[i].filename);
+ }
+
+ if (check_add_overflow(dump_length, (u64)sizeof(*dump_meta), &dump_length)) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+ if (check_mul_overflow((u64)sizeof(*image_out_table), table_nents, &mul_bytes)) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+ if (check_add_overflow(dump_length, mul_bytes, &dump_length)) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+
+ context->mem_dump_sz = dump_length;
+ context->mem_dump = vzalloc(dump_length);
+ if (!context->mem_dump) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+
+ /* Populate the dump metadata and table for userspace */
+ dump_meta = context->mem_dump;
+ dump_meta->magic = SAHARA_DUMP_V1_MAGIC;
+ dump_meta->version = SAHARA_DUMP_V1_VER;
+ dump_meta->dump_size = dump_length;
+ dump_meta->table_size = context->dump_table_length;
+
+ image_out_table = context->mem_dump + sizeof(*dump_meta);
+ for (i = 0; i < table_nents; ++i) {
+ image_out_table[i].type = le64_to_cpu(dev_table[i].type);
+ image_out_table[i].address = le64_to_cpu(dev_table[i].address);
+ image_out_table[i].length = le64_to_cpu(dev_table[i].length);
+ strscpy(image_out_table[i].description, dev_table[i].description,
+ SAHARA_TABLE_ENTRY_STR_LEN);
+ strscpy(image_out_table[i].filename,
+ dev_table[i].filename,
+ SAHARA_TABLE_ENTRY_STR_LEN);
+ }
+
+ context->mem_dump_freespace = &image_out_table[i];
+
+ /* Done parsing the table, switch to image dump mode */
+ context->dump_table_length = 0;
+
+ /* Request the first chunk of the first image */
+ context->dump_image = &image_out_table[0];
+ dump_length = min_t(u64, context->dump_image->length, SAHARA_READ_MAX_SIZE);
+ /* Avoid requesting EOI sized data so that we can identify errors */
+ if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
+ dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
+
+ context->dump_image_offset = dump_length;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH);
+ context->tx[0]->memory_read64.memory_address = cpu_to_le64(context->dump_image->address);
+ context->tx[0]->memory_read64.memory_length = cpu_to_le64(dump_length);
+
+ context->rx_size_requested = dump_length;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_MEM_READ64_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send read for dump content %d\n", ret);
+}
+
+static void sahara_parse_dump_image(struct sahara_context *context)
+{
+ u64 dump_length;
+ int ret;
+
+ memcpy(context->mem_dump_freespace, context->rx, context->rx_size);
+ context->mem_dump_freespace += context->rx_size;
+
+ if (context->dump_image_offset >= context->dump_image->length) {
+ /* Need to move to next image */
+ context->dump_image++;
+ context->dump_images_left--;
+ context->dump_image_offset = 0;
+
+ if (!context->dump_images_left) {
+ /* Dump done */
+ dev_coredumpv(context->mhi_dev->mhi_cntrl->cntrl_dev,
+ context->mem_dump,
+ context->mem_dump_sz,
+ GFP_KERNEL);
+ context->mem_dump = NULL;
+ sahara_send_reset(context);
+ return;
+ }
+ }
+
+ /* Get next image chunk */
+ dump_length = context->dump_image->length - context->dump_image_offset;
+ dump_length = min_t(u64, dump_length, SAHARA_READ_MAX_SIZE);
+ /* Avoid requesting EOI sized data so that we can identify errors */
+ if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
+ dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH);
+ context->tx[0]->memory_read64.memory_address =
+ cpu_to_le64(context->dump_image->address + context->dump_image_offset);
+ context->tx[0]->memory_read64.memory_length = cpu_to_le64(dump_length);
+
+ context->dump_image_offset += dump_length;
+ context->rx_size_requested = dump_length;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_MEM_READ64_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev,
+ "Unable to send read for dump content %d\n", ret);
+}
+
+static void sahara_dump_processing(struct work_struct *work)
+{
+ struct sahara_context *context = container_of(work, struct sahara_context, dump_work);
+ int ret;
+
+ /*
+ * We should get the expected raw data, but if the device has an error
+ * it is supposed to send EOI with an error code.
+ */
+ if (context->rx_size != context->rx_size_requested &&
+ context->rx_size != SAHARA_END_OF_IMAGE_LENGTH) {
+ dev_err(&context->mhi_dev->dev,
+ "Unexpected response to read_data. Expected size: %#zx got: %#zx\n",
+ context->rx_size_requested,
+ context->rx_size);
+ goto error;
+ }
+
+ if (context->rx_size == SAHARA_END_OF_IMAGE_LENGTH &&
+ le32_to_cpu(context->rx->cmd) == SAHARA_END_OF_IMAGE_CMD) {
+ dev_err(&context->mhi_dev->dev,
+ "Unexpected EOI response to read_data. Status: %d\n",
+ le32_to_cpu(context->rx->end_of_image.status));
+ goto error;
+ }
+
+ if (context->rx_size == SAHARA_END_OF_IMAGE_LENGTH &&
+ le32_to_cpu(context->rx->cmd) != SAHARA_END_OF_IMAGE_CMD) {
+ dev_err(&context->mhi_dev->dev,
+ "Invalid EOI response to read_data. CMD: %d\n",
+ le32_to_cpu(context->rx->cmd));
+ goto error;
+ }
+
+ /*
+ * Need to know if we received the dump table, or part of a dump image.
+ * Since we get raw data, we cannot tell from the data itself. Instead,
+ * we use the stored dump_table_length, which we zero after we read and
+ * process the entire table.
+ */
+ if (context->dump_table_length)
+ sahara_parse_dump_table(context);
+ else
+ sahara_parse_dump_image(context);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE, context->rx,
+ SAHARA_PACKET_MAX_SIZE, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret);
+
+ return;
+
+error:
+ vfree(context->mem_dump);
+ context->mem_dump = NULL;
+ sahara_send_reset(context);
+}
+
+static void sahara_read_data_processing(struct work_struct *work)
+{
+ struct sahara_context *context = container_of(work, struct sahara_context, read_data_work);
+
+ read_data_helper(context, 0);
+}
+
+static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct sahara_context *context;
+ int ret;
+ int i;
+
+ context = devm_kzalloc(&mhi_dev->dev, sizeof(*context), GFP_KERNEL);
+ if (!context)
+ return -ENOMEM;
+
+ context->rx = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
+ if (!context->rx)
+ return -ENOMEM;
+
+ if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
+ context->image_table = aic200_image_table;
+ context->table_size = ARRAY_SIZE(aic200_image_table);
+ } else {
+ context->image_table = aic100_image_table;
+ context->table_size = ARRAY_SIZE(aic100_image_table);
+ context->non_streaming = true;
+ }
+
+ /*
+ * There are two firmware implementations for READ_DATA handling.
+ * The older "SBL" implementation defines a Sahara transfer size, and
+ * expects that the response is a single transport transfer. If the
+ * FW wants to transfer a file that is larger than the transfer size,
+ * the FW will issue multiple READ_DATA commands. For this
+ * implementation, we need to allocate enough buffers to contain the
+ * entire Sahara transfer size.
+ *
+ * The newer "XBL" implementation does not define a maximum transfer
+ * size and instead expects the data to be streamed over using the
+ * transport level MTU. The FW will issue a single READ_DATA command
+ * of whatever size, and consume multiple transport level transfers
+ * until the expected amount of data is consumed. For this
+ * implementation we only need a single buffer of the transport MTU
+ * but we'll need to be able to use it multiple times for a single
+ * READ_DATA request.
+ *
+ * AIC100 is the SBL implementation and defines SAHARA_TRANSFER_MAX_SIZE
+ * and we need 9x SAHARA_PACKET_MAX_SIZE to cover that. We can use
+ * MHI_CHAIN to link multiple buffers into a single transfer but the
+ * remote side will not consume the buffers until it sees an EOT, thus
+ * we need to allocate enough buffers to put in the tx fifo to cover an
+ * entire READ_DATA request of the max size.
+ *
+ * AIC200 is the XBL implementation, and so a single buffer will work.
+ */
+ for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
+ context->tx[i] = devm_kzalloc(&mhi_dev->dev,
+ SAHARA_PACKET_MAX_SIZE,
+ GFP_KERNEL);
+ if (!context->tx[i])
+ return -ENOMEM;
+ if (is_streaming(context))
+ break;
+ }
+
+ context->mhi_dev = mhi_dev;
+ INIT_WORK(&context->fw_work, sahara_processing);
+ INIT_WORK(&context->dump_work, sahara_dump_processing);
+ INIT_WORK(&context->read_data_work, sahara_read_data_processing);
+
+ context->active_image_id = SAHARA_IMAGE_ID_NONE;
+ dev_set_drvdata(&mhi_dev->dev, context);
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ return ret;
+
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, context->rx, SAHARA_PACKET_MAX_SIZE, MHI_EOT);
+ if (ret) {
+ mhi_unprepare_from_transfer(mhi_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sahara_mhi_remove(struct mhi_device *mhi_dev)
+{
+ struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
+
+ cancel_work_sync(&context->fw_work);
+ cancel_work_sync(&context->dump_work);
+ vfree(context->mem_dump);
+ sahara_release_image(context);
+ mhi_unprepare_from_transfer(mhi_dev);
+}
+
+static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
+
+ if (!mhi_result->transaction_status && context->read_data_length && is_streaming(context))
+ schedule_work(&context->read_data_work);
+}
+
+static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
+
+ if (!mhi_result->transaction_status) {
+ context->rx_size = mhi_result->bytes_xferd;
+ if (context->is_mem_dump_mode)
+ schedule_work(&context->dump_work);
+ else
+ schedule_work(&context->fw_work);
+ }
+
+}
+
+static const struct mhi_device_id sahara_mhi_match_table[] = {
+ { .chan = "QAIC_SAHARA", },
+ {},
+};
+
+static struct mhi_driver sahara_mhi_driver = {
+ .id_table = sahara_mhi_match_table,
+ .remove = sahara_mhi_remove,
+ .probe = sahara_mhi_probe,
+ .ul_xfer_cb = sahara_mhi_ul_xfer_cb,
+ .dl_xfer_cb = sahara_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "sahara",
+ },
+};
+
+int sahara_register(void)
+{
+ return mhi_driver_register(&sahara_mhi_driver);
+}
+
+void sahara_unregister(void)
+{
+ mhi_driver_unregister(&sahara_mhi_driver);
+}
diff --git a/drivers/accel/qaic/sahara.h b/drivers/accel/qaic/sahara.h
new file mode 100644
index 000000000000..640208acc0d1
--- /dev/null
+++ b/drivers/accel/qaic/sahara.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#ifndef __SAHARA_H__
+#define __SAHARA_H__
+
+int sahara_register(void);
+void sahara_unregister(void);
+#endif /* __SAHARA_H__ */
diff --git a/drivers/accel/rocket/Kconfig b/drivers/accel/rocket/Kconfig
new file mode 100644
index 000000000000..16465abe0660
--- /dev/null
+++ b/drivers/accel/rocket/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_ROCKET
+ tristate "Rocket (support for Rockchip NPUs)"
+ depends on DRM_ACCEL
+ depends on (ARCH_ROCKCHIP && ARM64) || COMPILE_TEST
+ depends on ROCKCHIP_IOMMU || COMPILE_TEST
+ depends on MMU
+ select DRM_SCHED
+ select DRM_GEM_SHMEM_HELPER
+ help
+ Choose this option if you have a Rockchip SoC that contains a
+ compatible Neural Processing Unit (NPU), such as the RK3588. Called by
+ Rockchip either RKNN or RKNPU, it accelerates inference of neural
+ networks.
+
+ The interface exposed to userspace is described in
+ include/uapi/drm/rocket_accel.h and is used by the Rocket userspace
+ driver in Mesa3D.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rocket.
diff --git a/drivers/accel/rocket/Makefile b/drivers/accel/rocket/Makefile
new file mode 100644
index 000000000000..3713dfe223d6
--- /dev/null
+++ b/drivers/accel/rocket/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_ROCKET) := rocket.o
+
+rocket-y := \
+ rocket_core.o \
+ rocket_device.o \
+ rocket_drv.o \
+ rocket_gem.o \
+ rocket_job.o
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
new file mode 100644
index 000000000000..abe7719c1db4
--- /dev/null
+++ b/drivers/accel/rocket/rocket_core.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "rocket_core.h"
+#include "rocket_job.h"
+
+int rocket_core_init(struct rocket_core *core)
+{
+ struct device *dev = core->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ u32 version;
+ int err = 0;
+
+ core->resets[0].id = "srst_a";
+ core->resets[1].id = "srst_h";
+ err = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(core->resets),
+ core->resets);
+ if (err)
+ return dev_err_probe(dev, err, "failed to get resets for core %d\n", core->index);
+
+ err = devm_clk_bulk_get(dev, ARRAY_SIZE(core->clks), core->clks);
+ if (err)
+ return dev_err_probe(dev, err, "failed to get clocks for core %d\n", core->index);
+
+ core->pc_iomem = devm_platform_ioremap_resource_byname(pdev, "pc");
+ if (IS_ERR(core->pc_iomem)) {
+ dev_err(dev, "couldn't find PC registers %ld\n", PTR_ERR(core->pc_iomem));
+ return PTR_ERR(core->pc_iomem);
+ }
+
+ core->cna_iomem = devm_platform_ioremap_resource_byname(pdev, "cna");
+ if (IS_ERR(core->cna_iomem)) {
+ dev_err(dev, "couldn't find CNA registers %ld\n", PTR_ERR(core->cna_iomem));
+ return PTR_ERR(core->cna_iomem);
+ }
+
+ core->core_iomem = devm_platform_ioremap_resource_byname(pdev, "core");
+ if (IS_ERR(core->core_iomem)) {
+ dev_err(dev, "couldn't find CORE registers %ld\n", PTR_ERR(core->core_iomem));
+ return PTR_ERR(core->core_iomem);
+ }
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ if (err)
+ return err;
+
+ core->iommu_group = iommu_group_get(dev);
+
+ err = rocket_job_init(core);
+ if (err)
+ return err;
+
+ pm_runtime_use_autosuspend(dev);
+
+ /*
+ * As this NPU will be most often used as part of a media pipeline that
+ * ends presenting in a display, choose 50 ms (~3 frames at 60Hz) as an
+ * autosuspend delay as that will keep the device powered up while the
+ * pipeline is running.
+ */
+ pm_runtime_set_autosuspend_delay(dev, 50);
+
+ pm_runtime_enable(dev);
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err) {
+ rocket_job_fini(core);
+ return err;
+ }
+
+ version = rocket_pc_readl(core, VERSION);
+ version += rocket_pc_readl(core, VERSION_NUM) & 0xffff;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ dev_info(dev, "Rockchip NPU core %d version: %d\n", core->index, version);
+
+ return 0;
+}
+
+void rocket_core_fini(struct rocket_core *core)
+{
+ pm_runtime_dont_use_autosuspend(core->dev);
+ pm_runtime_disable(core->dev);
+ iommu_group_put(core->iommu_group);
+ core->iommu_group = NULL;
+ rocket_job_fini(core);
+}
+
+void rocket_core_reset(struct rocket_core *core)
+{
+ reset_control_bulk_assert(ARRAY_SIZE(core->resets), core->resets);
+
+ udelay(10);
+
+ reset_control_bulk_deassert(ARRAY_SIZE(core->resets), core->resets);
+}
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
new file mode 100644
index 000000000000..f6d7382854ca
--- /dev/null
+++ b/drivers/accel/rocket/rocket_core.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#ifndef __ROCKET_CORE_H__
+#define __ROCKET_CORE_H__
+
+#include <drm/gpu_scheduler.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mutex_types.h>
+#include <linux/reset.h>
+
+#include "rocket_registers.h"
+
+#define rocket_pc_readl(core, reg) \
+ readl((core)->pc_iomem + (REG_PC_##reg))
+#define rocket_pc_writel(core, reg, value) \
+ writel(value, (core)->pc_iomem + (REG_PC_##reg))
+
+#define rocket_cna_readl(core, reg) \
+ readl((core)->cna_iomem + (REG_CNA_##reg) - REG_CNA_S_STATUS)
+#define rocket_cna_writel(core, reg, value) \
+ writel(value, (core)->cna_iomem + (REG_CNA_##reg) - REG_CNA_S_STATUS)
+
+#define rocket_core_readl(core, reg) \
+ readl((core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+#define rocket_core_writel(core, reg, value) \
+ writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+
+struct rocket_core {
+ struct device *dev;
+ struct rocket_device *rdev;
+ unsigned int index;
+
+ int irq;
+ void __iomem *pc_iomem;
+ void __iomem *cna_iomem;
+ void __iomem *core_iomem;
+ struct clk_bulk_data clks[4];
+ struct reset_control_bulk_data resets[2];
+
+ struct iommu_group *iommu_group;
+
+ struct mutex job_lock;
+ struct rocket_job *in_flight_job;
+
+ spinlock_t fence_lock;
+
+ struct {
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ atomic_t pending;
+ } reset;
+
+ struct drm_gpu_scheduler sched;
+ u64 fence_context;
+ u64 emit_seqno;
+};
+
+int rocket_core_init(struct rocket_core *core);
+void rocket_core_fini(struct rocket_core *core);
+void rocket_core_reset(struct rocket_core *core);
+
+#endif
diff --git a/drivers/accel/rocket/rocket_device.c b/drivers/accel/rocket/rocket_device.c
new file mode 100644
index 000000000000..46e6ee1e72c5
--- /dev/null
+++ b/drivers/accel/rocket/rocket_device.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#include <drm/drm_drv.h>
+#include <linux/array_size.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include "rocket_device.h"
+
+struct rocket_device *rocket_device_init(struct platform_device *pdev,
+ const struct drm_driver *rocket_drm_driver)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *core_node;
+ struct rocket_device *rdev;
+ struct drm_device *ddev;
+ unsigned int num_cores = 0;
+ int err;
+
+ rdev = devm_drm_dev_alloc(dev, rocket_drm_driver, struct rocket_device, ddev);
+ if (IS_ERR(rdev))
+ return rdev;
+
+ ddev = &rdev->ddev;
+ dev_set_drvdata(dev, rdev);
+
+ for_each_compatible_node(core_node, NULL, "rockchip,rk3588-rknn-core")
+ if (of_device_is_available(core_node))
+ num_cores++;
+
+ rdev->cores = devm_kcalloc(dev, num_cores, sizeof(*rdev->cores), GFP_KERNEL);
+ if (!rdev->cores)
+ return ERR_PTR(-ENOMEM);
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ if (err)
+ return ERR_PTR(err);
+
+ err = devm_mutex_init(dev, &rdev->sched_lock);
+ if (err)
+ return ERR_PTR(-ENOMEM);
+
+ err = drm_dev_register(ddev, 0);
+ if (err)
+ return ERR_PTR(err);
+
+ return rdev;
+}
+
+void rocket_device_fini(struct rocket_device *rdev)
+{
+ WARN_ON(rdev->num_cores > 0);
+
+ drm_dev_unregister(&rdev->ddev);
+}
diff --git a/drivers/accel/rocket/rocket_device.h b/drivers/accel/rocket/rocket_device.h
new file mode 100644
index 000000000000..ce662abc01d3
--- /dev/null
+++ b/drivers/accel/rocket/rocket_device.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#ifndef __ROCKET_DEVICE_H__
+#define __ROCKET_DEVICE_H__
+
+#include <drm/drm_device.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+
+#include "rocket_core.h"
+
+struct rocket_device {
+ struct drm_device ddev;
+
+ struct mutex sched_lock;
+
+ struct rocket_core *cores;
+ unsigned int num_cores;
+};
+
+struct rocket_device *rocket_device_init(struct platform_device *pdev,
+ const struct drm_driver *rocket_drm_driver);
+void rocket_device_fini(struct rocket_device *rdev);
+#define to_rocket_device(drm_dev) \
+ ((struct rocket_device *)(container_of((drm_dev), struct rocket_device, ddev)))
+
+#endif /* __ROCKET_DEVICE_H__ */
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
new file mode 100644
index 000000000000..5c0b63f0a8f0
--- /dev/null
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/rocket_accel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iommu.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rocket_drv.h"
+#include "rocket_gem.h"
+#include "rocket_job.h"
+
+/*
+ * Facade device, used to expose a single DRM device to userspace, that
+ * schedules jobs to any RKNN cores in the system.
+ */
+static struct platform_device *drm_dev;
+static struct rocket_device *rdev;
+
+static void
+rocket_iommu_domain_destroy(struct kref *kref)
+{
+ struct rocket_iommu_domain *domain = container_of(kref, struct rocket_iommu_domain, kref);
+
+ iommu_domain_free(domain->domain);
+ domain->domain = NULL;
+ kfree(domain);
+}
+
+static struct rocket_iommu_domain*
+rocket_iommu_domain_create(struct device *dev)
+{
+ struct rocket_iommu_domain *domain = kmalloc(sizeof(*domain), GFP_KERNEL);
+ void *err;
+
+ if (!domain)
+ return ERR_PTR(-ENOMEM);
+
+ domain->domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(domain->domain)) {
+ err = ERR_CAST(domain->domain);
+ kfree(domain);
+ return err;
+ }
+ kref_init(&domain->kref);
+
+ return domain;
+}
+
+struct rocket_iommu_domain *
+rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv)
+{
+ kref_get(&rocket_priv->domain->kref);
+ return rocket_priv->domain;
+}
+
+void
+rocket_iommu_domain_put(struct rocket_iommu_domain *domain)
+{
+ kref_put(&domain->kref, rocket_iommu_domain_destroy);
+}
+
+static int
+rocket_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct rocket_device *rdev = to_rocket_device(dev);
+ struct rocket_file_priv *rocket_priv;
+ u64 start, end;
+ int ret;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EINVAL;
+
+ rocket_priv = kzalloc(sizeof(*rocket_priv), GFP_KERNEL);
+ if (!rocket_priv) {
+ ret = -ENOMEM;
+ goto err_put_mod;
+ }
+
+ rocket_priv->rdev = rdev;
+ rocket_priv->domain = rocket_iommu_domain_create(rdev->cores[0].dev);
+ if (IS_ERR(rocket_priv->domain)) {
+ ret = PTR_ERR(rocket_priv->domain);
+ goto err_free;
+ }
+
+ file->driver_priv = rocket_priv;
+
+ start = rocket_priv->domain->domain->geometry.aperture_start;
+ end = rocket_priv->domain->domain->geometry.aperture_end;
+ drm_mm_init(&rocket_priv->mm, start, end - start + 1);
+ mutex_init(&rocket_priv->mm_lock);
+
+ ret = rocket_job_open(rocket_priv);
+ if (ret)
+ goto err_mm_takedown;
+
+ return 0;
+
+err_mm_takedown:
+ mutex_destroy(&rocket_priv->mm_lock);
+ drm_mm_takedown(&rocket_priv->mm);
+ rocket_iommu_domain_put(rocket_priv->domain);
+err_free:
+ kfree(rocket_priv);
+err_put_mod:
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static void
+rocket_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ struct rocket_file_priv *rocket_priv = file->driver_priv;
+
+ rocket_job_close(rocket_priv);
+ mutex_destroy(&rocket_priv->mm_lock);
+ drm_mm_takedown(&rocket_priv->mm);
+ rocket_iommu_domain_put(rocket_priv->domain);
+ kfree(rocket_priv);
+ module_put(THIS_MODULE);
+}
+
+static const struct drm_ioctl_desc rocket_drm_driver_ioctls[] = {
+#define ROCKET_IOCTL(n, func) \
+ DRM_IOCTL_DEF_DRV(ROCKET_##n, rocket_ioctl_##func, 0)
+
+ ROCKET_IOCTL(CREATE_BO, create_bo),
+ ROCKET_IOCTL(SUBMIT, submit),
+ ROCKET_IOCTL(PREP_BO, prep_bo),
+ ROCKET_IOCTL(FINI_BO, fini_bo),
+};
+
+DEFINE_DRM_ACCEL_FOPS(rocket_accel_driver_fops);
+
+/*
+ * Rocket driver version:
+ * - 1.0 - initial interface
+ */
+static const struct drm_driver rocket_drm_driver = {
+ .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
+ .open = rocket_open,
+ .postclose = rocket_postclose,
+ .gem_create_object = rocket_gem_create_object,
+ .ioctls = rocket_drm_driver_ioctls,
+ .num_ioctls = ARRAY_SIZE(rocket_drm_driver_ioctls),
+ .fops = &rocket_accel_driver_fops,
+ .name = "rocket",
+ .desc = "rocket DRM",
+};
+
+static int rocket_probe(struct platform_device *pdev)
+{
+ if (rdev == NULL) {
+ /* First core probing, initialize DRM device. */
+ rdev = rocket_device_init(drm_dev, &rocket_drm_driver);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to initialize rocket device\n");
+ return PTR_ERR(rdev);
+ }
+ }
+
+ unsigned int core = rdev->num_cores;
+
+ dev_set_drvdata(&pdev->dev, rdev);
+
+ rdev->cores[core].rdev = rdev;
+ rdev->cores[core].dev = &pdev->dev;
+ rdev->cores[core].index = core;
+
+ rdev->num_cores++;
+
+ return rocket_core_init(&rdev->cores[core]);
+}
+
+static void rocket_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ for (unsigned int core = 0; core < rdev->num_cores; core++) {
+ if (rdev->cores[core].dev == dev) {
+ rocket_core_fini(&rdev->cores[core]);
+ rdev->num_cores--;
+ break;
+ }
+ }
+
+ if (rdev->num_cores == 0) {
+ /* Last core removed, deinitialize DRM device. */
+ rocket_device_fini(rdev);
+ rdev = NULL;
+ }
+}
+
+static const struct of_device_id dt_match[] = {
+ { .compatible = "rockchip,rk3588-rknn-core" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
+static int find_core_for_dev(struct device *dev)
+{
+ struct rocket_device *rdev = dev_get_drvdata(dev);
+
+ for (unsigned int core = 0; core < rdev->num_cores; core++) {
+ if (dev == rdev->cores[core].dev)
+ return core;
+ }
+
+ return -1;
+}
+
+static int rocket_device_runtime_resume(struct device *dev)
+{
+ struct rocket_device *rdev = dev_get_drvdata(dev);
+ int core = find_core_for_dev(dev);
+ int err = 0;
+
+ if (core < 0)
+ return -ENODEV;
+
+ err = clk_bulk_prepare_enable(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
+ if (err) {
+ dev_err(dev, "failed to enable (%d) clocks for core %d\n", err, core);
+ return err;
+ }
+
+ return 0;
+}
+
+static int rocket_device_runtime_suspend(struct device *dev)
+{
+ struct rocket_device *rdev = dev_get_drvdata(dev);
+ int core = find_core_for_dev(dev);
+
+ if (core < 0)
+ return -ENODEV;
+
+ if (!rocket_job_is_idle(&rdev->cores[core]))
+ return -EBUSY;
+
+ clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
+
+ return 0;
+}
+
+EXPORT_GPL_DEV_PM_OPS(rocket_pm_ops) = {
+ RUNTIME_PM_OPS(rocket_device_runtime_suspend, rocket_device_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver rocket_driver = {
+ .probe = rocket_probe,
+ .remove = rocket_remove,
+ .driver = {
+ .name = "rocket",
+ .pm = pm_ptr(&rocket_pm_ops),
+ .of_match_table = dt_match,
+ },
+};
+
+static int __init rocket_register(void)
+{
+ drm_dev = platform_device_register_simple("rknn", -1, NULL, 0);
+ if (IS_ERR(drm_dev))
+ return PTR_ERR(drm_dev);
+
+ return platform_driver_register(&rocket_driver);
+}
+
+static void __exit rocket_unregister(void)
+{
+ platform_driver_unregister(&rocket_driver);
+
+ platform_device_unregister(drm_dev);
+}
+
+module_init(rocket_register);
+module_exit(rocket_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DRM driver for the Rockchip NPU IP");
+MODULE_AUTHOR("Tomeu Vizoso");
diff --git a/drivers/accel/rocket/rocket_drv.h b/drivers/accel/rocket/rocket_drv.h
new file mode 100644
index 000000000000..2c673bb99ccc
--- /dev/null
+++ b/drivers/accel/rocket/rocket_drv.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#ifndef __ROCKET_DRV_H__
+#define __ROCKET_DRV_H__
+
+#include <drm/drm_mm.h>
+#include <drm/gpu_scheduler.h>
+
+#include "rocket_device.h"
+
+extern const struct dev_pm_ops rocket_pm_ops;
+
+struct rocket_iommu_domain {
+ struct iommu_domain *domain;
+ struct kref kref;
+};
+
+struct rocket_file_priv {
+ struct rocket_device *rdev;
+
+ struct rocket_iommu_domain *domain;
+ struct drm_mm mm;
+ struct mutex mm_lock;
+
+ struct drm_sched_entity sched_entity;
+};
+
+struct rocket_iommu_domain *rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv);
+void rocket_iommu_domain_put(struct rocket_iommu_domain *domain);
+
+#endif
diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c
new file mode 100644
index 000000000000..624c4ecf5a34
--- /dev/null
+++ b/drivers/accel/rocket/rocket_gem.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+#include <drm/drm_utils.h>
+#include <drm/rocket_accel.h>
+#include <linux/dma-mapping.h>
+#include <linux/iommu.h>
+
+#include "rocket_drv.h"
+#include "rocket_gem.h"
+
+static void rocket_gem_bo_free(struct drm_gem_object *obj)
+{
+ struct rocket_gem_object *bo = to_rocket_bo(obj);
+ struct rocket_file_priv *rocket_priv = bo->driver_priv;
+ size_t unmapped;
+
+ drm_WARN_ON(obj->dev, refcount_read(&bo->base.pages_use_count) > 1);
+
+ unmapped = iommu_unmap(bo->domain->domain, bo->mm.start, bo->size);
+ drm_WARN_ON(obj->dev, unmapped != bo->size);
+
+ mutex_lock(&rocket_priv->mm_lock);
+ drm_mm_remove_node(&bo->mm);
+ mutex_unlock(&rocket_priv->mm_lock);
+
+ rocket_iommu_domain_put(bo->domain);
+ bo->domain = NULL;
+
+ drm_gem_shmem_free(&bo->base);
+}
+
+static const struct drm_gem_object_funcs rocket_gem_funcs = {
+ .free = rocket_gem_bo_free,
+ .print_info = drm_gem_shmem_object_print_info,
+ .pin = drm_gem_shmem_object_pin,
+ .unpin = drm_gem_shmem_object_unpin,
+ .get_sg_table = drm_gem_shmem_object_get_sg_table,
+ .vmap = drm_gem_shmem_object_vmap,
+ .vunmap = drm_gem_shmem_object_vunmap,
+ .mmap = drm_gem_shmem_object_mmap,
+ .vm_ops = &drm_gem_shmem_vm_ops,
+};
+
+struct drm_gem_object *rocket_gem_create_object(struct drm_device *dev, size_t size)
+{
+ struct rocket_gem_object *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj->base.base.funcs = &rocket_gem_funcs;
+
+ return &obj->base.base;
+}
+
+int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct rocket_file_priv *rocket_priv = file->driver_priv;
+ struct drm_rocket_create_bo *args = data;
+ struct drm_gem_shmem_object *shmem_obj;
+ struct rocket_gem_object *rkt_obj;
+ struct drm_gem_object *gem_obj;
+ struct sg_table *sgt;
+ int ret;
+
+ shmem_obj = drm_gem_shmem_create(dev, args->size);
+ if (IS_ERR(shmem_obj))
+ return PTR_ERR(shmem_obj);
+
+ gem_obj = &shmem_obj->base;
+ rkt_obj = to_rocket_bo(gem_obj);
+
+ rkt_obj->driver_priv = rocket_priv;
+ rkt_obj->domain = rocket_iommu_domain_get(rocket_priv);
+ rkt_obj->size = args->size;
+ rkt_obj->offset = 0;
+
+ ret = drm_gem_handle_create(file, gem_obj, &args->handle);
+ drm_gem_object_put(gem_obj);
+ if (ret)
+ goto err;
+
+ sgt = drm_gem_shmem_get_pages_sgt(shmem_obj);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto err;
+ }
+
+ mutex_lock(&rocket_priv->mm_lock);
+ ret = drm_mm_insert_node_generic(&rocket_priv->mm, &rkt_obj->mm,
+ rkt_obj->size, PAGE_SIZE,
+ 0, 0);
+ mutex_unlock(&rocket_priv->mm_lock);
+
+ ret = iommu_map_sgtable(rocket_priv->domain->domain,
+ rkt_obj->mm.start,
+ shmem_obj->sgt,
+ IOMMU_READ | IOMMU_WRITE);
+ if (ret < 0 || ret < args->size) {
+ drm_err(dev, "failed to map buffer: size=%d request_size=%u\n",
+ ret, args->size);
+ ret = -ENOMEM;
+ goto err_remove_node;
+ }
+
+ /* iommu_map_sgtable might have aligned the size */
+ rkt_obj->size = ret;
+ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
+ args->dma_address = rkt_obj->mm.start;
+
+ return 0;
+
+err_remove_node:
+ mutex_lock(&rocket_priv->mm_lock);
+ drm_mm_remove_node(&rkt_obj->mm);
+ mutex_unlock(&rocket_priv->mm_lock);
+
+err:
+ drm_gem_shmem_object_free(gem_obj);
+
+ return ret;
+}
+
+int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_rocket_prep_bo *args = data;
+ unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
+ struct drm_gem_object *gem_obj;
+ struct drm_gem_shmem_object *shmem_obj;
+ long ret = 0;
+
+ if (args->reserved != 0) {
+ drm_dbg(dev, "Reserved field in drm_rocket_prep_bo struct should be 0.\n");
+ return -EINVAL;
+ }
+
+ gem_obj = drm_gem_object_lookup(file, args->handle);
+ if (!gem_obj)
+ return -ENOENT;
+
+ ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_WRITE, true, timeout);
+ if (!ret)
+ ret = timeout ? -ETIMEDOUT : -EBUSY;
+
+ shmem_obj = &to_rocket_bo(gem_obj)->base;
+
+ dma_sync_sgtable_for_cpu(dev->dev, shmem_obj->sgt, DMA_BIDIRECTIONAL);
+
+ drm_gem_object_put(gem_obj);
+
+ return ret;
+}
+
+int rocket_ioctl_fini_bo(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_rocket_fini_bo *args = data;
+ struct drm_gem_shmem_object *shmem_obj;
+ struct rocket_gem_object *rkt_obj;
+ struct drm_gem_object *gem_obj;
+
+ if (args->reserved != 0) {
+ drm_dbg(dev, "Reserved field in drm_rocket_fini_bo struct should be 0.\n");
+ return -EINVAL;
+ }
+
+ gem_obj = drm_gem_object_lookup(file, args->handle);
+ if (!gem_obj)
+ return -ENOENT;
+
+ rkt_obj = to_rocket_bo(gem_obj);
+ shmem_obj = &rkt_obj->base;
+
+ dma_sync_sgtable_for_device(dev->dev, shmem_obj->sgt, DMA_BIDIRECTIONAL);
+
+ drm_gem_object_put(gem_obj);
+
+ return 0;
+}
diff --git a/drivers/accel/rocket/rocket_gem.h b/drivers/accel/rocket/rocket_gem.h
new file mode 100644
index 000000000000..240430334509
--- /dev/null
+++ b/drivers/accel/rocket/rocket_gem.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#ifndef __ROCKET_GEM_H__
+#define __ROCKET_GEM_H__
+
+#include <drm/drm_gem_shmem_helper.h>
+
+struct rocket_gem_object {
+ struct drm_gem_shmem_object base;
+
+ struct rocket_file_priv *driver_priv;
+
+ struct rocket_iommu_domain *domain;
+ struct drm_mm_node mm;
+ size_t size;
+ u32 offset;
+};
+
+struct drm_gem_object *rocket_gem_create_object(struct drm_device *dev, size_t size);
+
+int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file *file);
+
+int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *file);
+
+int rocket_ioctl_fini_bo(struct drm_device *dev, void *data, struct drm_file *file);
+
+static inline
+struct rocket_gem_object *to_rocket_bo(struct drm_gem_object *obj)
+{
+ return container_of(to_drm_gem_shmem_obj(obj), struct rocket_gem_object, base);
+}
+
+#endif
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
new file mode 100644
index 000000000000..acd606160dc9
--- /dev/null
+++ b/drivers/accel/rocket/rocket_job.c
@@ -0,0 +1,637 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
+/* Copyright 2019 Collabora ltd. */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#include <drm/drm_print.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/rocket_accel.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rocket_core.h"
+#include "rocket_device.h"
+#include "rocket_drv.h"
+#include "rocket_job.h"
+#include "rocket_registers.h"
+
+#define JOB_TIMEOUT_MS 500
+
+static struct rocket_job *
+to_rocket_job(struct drm_sched_job *sched_job)
+{
+ return container_of(sched_job, struct rocket_job, base);
+}
+
+static const char *rocket_fence_get_driver_name(struct dma_fence *fence)
+{
+ return "rocket";
+}
+
+static const char *rocket_fence_get_timeline_name(struct dma_fence *fence)
+{
+ return "rockchip-npu";
+}
+
+static const struct dma_fence_ops rocket_fence_ops = {
+ .get_driver_name = rocket_fence_get_driver_name,
+ .get_timeline_name = rocket_fence_get_timeline_name,
+};
+
+static struct dma_fence *rocket_fence_create(struct rocket_core *core)
+{
+ struct dma_fence *fence;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return ERR_PTR(-ENOMEM);
+
+ dma_fence_init(fence, &rocket_fence_ops, &core->fence_lock,
+ core->fence_context, ++core->emit_seqno);
+
+ return fence;
+}
+
+static int
+rocket_copy_tasks(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_rocket_job *job,
+ struct rocket_job *rjob)
+{
+ int ret = 0;
+
+ if (job->task_struct_size < sizeof(struct drm_rocket_task))
+ return -EINVAL;
+
+ rjob->task_count = job->task_count;
+
+ if (!rjob->task_count)
+ return 0;
+
+ rjob->tasks = kvmalloc_array(job->task_count, sizeof(*rjob->tasks), GFP_KERNEL);
+ if (!rjob->tasks) {
+ drm_dbg(dev, "Failed to allocate task array\n");
+ return -ENOMEM;
+ }
+
+ for (int i = 0; i < rjob->task_count; i++) {
+ struct drm_rocket_task task = {0};
+
+ if (copy_from_user(&task,
+ u64_to_user_ptr(job->tasks) + i * job->task_struct_size,
+ sizeof(task))) {
+ drm_dbg(dev, "Failed to copy incoming tasks\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ if (task.regcmd_count == 0) {
+ drm_dbg(dev, "regcmd_count field in drm_rocket_task should be > 0.\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ rjob->tasks[i].regcmd = task.regcmd;
+ rjob->tasks[i].regcmd_count = task.regcmd_count;
+ }
+
+ return 0;
+
+fail:
+ kvfree(rjob->tasks);
+ return ret;
+}
+
+static void rocket_job_hw_submit(struct rocket_core *core, struct rocket_job *job)
+{
+ struct rocket_task *task;
+ unsigned int extra_bit;
+
+ /* Don't queue the job if a reset is in progress */
+ if (atomic_read(&core->reset.pending))
+ return;
+
+ /* GO ! */
+
+ task = &job->tasks[job->next_task_idx];
+ job->next_task_idx++;
+
+ rocket_pc_writel(core, BASE_ADDRESS, 0x1);
+
+ /* From rknpu, in the TRM this bit is marked as reserved */
+ extra_bit = 0x10000000 * core->index;
+ rocket_cna_writel(core, S_POINTER, CNA_S_POINTER_POINTER_PP_EN(1) |
+ CNA_S_POINTER_EXECUTER_PP_EN(1) |
+ CNA_S_POINTER_POINTER_PP_MODE(1) |
+ extra_bit);
+
+ rocket_core_writel(core, S_POINTER, CORE_S_POINTER_POINTER_PP_EN(1) |
+ CORE_S_POINTER_EXECUTER_PP_EN(1) |
+ CORE_S_POINTER_POINTER_PP_MODE(1) |
+ extra_bit);
+
+ rocket_pc_writel(core, BASE_ADDRESS, task->regcmd);
+ rocket_pc_writel(core, REGISTER_AMOUNTS,
+ PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT((task->regcmd_count + 1) / 2 - 1));
+
+ rocket_pc_writel(core, INTERRUPT_MASK, PC_INTERRUPT_MASK_DPU_0 | PC_INTERRUPT_MASK_DPU_1);
+ rocket_pc_writel(core, INTERRUPT_CLEAR, PC_INTERRUPT_CLEAR_DPU_0 | PC_INTERRUPT_CLEAR_DPU_1);
+
+ rocket_pc_writel(core, TASK_CON, PC_TASK_CON_RESERVED_0(1) |
+ PC_TASK_CON_TASK_COUNT_CLEAR(1) |
+ PC_TASK_CON_TASK_NUMBER(1) |
+ PC_TASK_CON_TASK_PP_EN(1));
+
+ rocket_pc_writel(core, TASK_DMA_BASE_ADDR, PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR(0x0));
+
+ rocket_pc_writel(core, OPERATION_ENABLE, PC_OPERATION_ENABLE_OP_EN(1));
+
+ dev_dbg(core->dev, "Submitted regcmd at 0x%llx to core %d", task->regcmd, core->index);
+}
+
+static int rocket_acquire_object_fences(struct drm_gem_object **bos,
+ int bo_count,
+ struct drm_sched_job *job,
+ bool is_write)
+{
+ int i, ret;
+
+ for (i = 0; i < bo_count; i++) {
+ ret = dma_resv_reserve_fences(bos[i]->resv, 1);
+ if (ret)
+ return ret;
+
+ ret = drm_sched_job_add_implicit_dependencies(job, bos[i],
+ is_write);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rocket_attach_object_fences(struct drm_gem_object **bos,
+ int bo_count,
+ struct dma_fence *fence)
+{
+ int i;
+
+ for (i = 0; i < bo_count; i++)
+ dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
+}
+
+static int rocket_job_push(struct rocket_job *job)
+{
+ struct rocket_device *rdev = job->rdev;
+ struct drm_gem_object **bos;
+ struct ww_acquire_ctx acquire_ctx;
+ int ret = 0;
+
+ bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
+ GFP_KERNEL);
+ memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
+ memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
+
+ ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
+ if (ret)
+ goto err;
+
+ scoped_guard(mutex, &rdev->sched_lock) {
+ drm_sched_job_arm(&job->base);
+
+ job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished);
+
+ ret = rocket_acquire_object_fences(job->in_bos, job->in_bo_count, &job->base, false);
+ if (ret)
+ goto err_unlock;
+
+ ret = rocket_acquire_object_fences(job->out_bos, job->out_bo_count, &job->base, true);
+ if (ret)
+ goto err_unlock;
+
+ kref_get(&job->refcount); /* put by scheduler job completion */
+
+ drm_sched_entity_push_job(&job->base);
+ }
+
+ rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
+
+err_unlock:
+ drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
+err:
+ kvfree(bos);
+
+ return ret;
+}
+
+static void rocket_job_cleanup(struct kref *ref)
+{
+ struct rocket_job *job = container_of(ref, struct rocket_job,
+ refcount);
+ unsigned int i;
+
+ rocket_iommu_domain_put(job->domain);
+
+ dma_fence_put(job->done_fence);
+ dma_fence_put(job->inference_done_fence);
+
+ if (job->in_bos) {
+ for (i = 0; i < job->in_bo_count; i++)
+ drm_gem_object_put(job->in_bos[i]);
+
+ kvfree(job->in_bos);
+ }
+
+ if (job->out_bos) {
+ for (i = 0; i < job->out_bo_count; i++)
+ drm_gem_object_put(job->out_bos[i]);
+
+ kvfree(job->out_bos);
+ }
+
+ kvfree(job->tasks);
+
+ kfree(job);
+}
+
+static void rocket_job_put(struct rocket_job *job)
+{
+ kref_put(&job->refcount, rocket_job_cleanup);
+}
+
+static void rocket_job_free(struct drm_sched_job *sched_job)
+{
+ struct rocket_job *job = to_rocket_job(sched_job);
+
+ drm_sched_job_cleanup(sched_job);
+
+ rocket_job_put(job);
+}
+
+static struct rocket_core *sched_to_core(struct rocket_device *rdev,
+ struct drm_gpu_scheduler *sched)
+{
+ unsigned int core;
+
+ for (core = 0; core < rdev->num_cores; core++) {
+ if (&rdev->cores[core].sched == sched)
+ return &rdev->cores[core];
+ }
+
+ return NULL;
+}
+
+static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
+{
+ struct rocket_job *job = to_rocket_job(sched_job);
+ struct rocket_device *rdev = job->rdev;
+ struct rocket_core *core = sched_to_core(rdev, sched_job->sched);
+ struct dma_fence *fence = NULL;
+ int ret;
+
+ if (unlikely(job->base.s_fence->finished.error))
+ return NULL;
+
+ /*
+ * Nothing to execute: can happen if the job has finished while
+ * we were resetting the NPU.
+ */
+ if (job->next_task_idx == job->task_count)
+ return NULL;
+
+ fence = rocket_fence_create(core);
+ if (IS_ERR(fence))
+ return fence;
+
+ if (job->done_fence)
+ dma_fence_put(job->done_fence);
+ job->done_fence = dma_fence_get(fence);
+
+ ret = pm_runtime_get_sync(core->dev);
+ if (ret < 0)
+ return fence;
+
+ ret = iommu_attach_group(job->domain->domain, core->iommu_group);
+ if (ret < 0)
+ return fence;
+
+ scoped_guard(mutex, &core->job_lock) {
+ core->in_flight_job = job;
+ rocket_job_hw_submit(core, job);
+ }
+
+ return fence;
+}
+
+static void rocket_job_handle_irq(struct rocket_core *core)
+{
+ pm_runtime_mark_last_busy(core->dev);
+
+ rocket_pc_writel(core, OPERATION_ENABLE, 0x0);
+ rocket_pc_writel(core, INTERRUPT_CLEAR, 0x1ffff);
+
+ scoped_guard(mutex, &core->job_lock)
+ if (core->in_flight_job) {
+ if (core->in_flight_job->next_task_idx < core->in_flight_job->task_count) {
+ rocket_job_hw_submit(core, core->in_flight_job);
+ return;
+ }
+
+ iommu_detach_group(NULL, iommu_group_get(core->dev));
+ dma_fence_signal(core->in_flight_job->done_fence);
+ pm_runtime_put_autosuspend(core->dev);
+ core->in_flight_job = NULL;
+ }
+}
+
+static void
+rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
+{
+ if (!atomic_read(&core->reset.pending))
+ return;
+
+ drm_sched_stop(&core->sched, bad);
+
+ /*
+ * Remaining interrupts have been handled, but we might still have
+ * stuck jobs. Let's make sure the PM counters stay balanced by
+ * manually calling pm_runtime_put_noidle().
+ */
+ scoped_guard(mutex, &core->job_lock) {
+ if (core->in_flight_job)
+ pm_runtime_put_noidle(core->dev);
+
+ iommu_detach_group(NULL, core->iommu_group);
+
+ core->in_flight_job = NULL;
+ }
+
+ /* Proceed with reset now. */
+ rocket_core_reset(core);
+
+ /* NPU has been reset, we can clear the reset pending bit. */
+ atomic_set(&core->reset.pending, 0);
+
+ /* Restart the scheduler */
+ drm_sched_start(&core->sched, 0);
+}
+
+static enum drm_gpu_sched_stat rocket_job_timedout(struct drm_sched_job *sched_job)
+{
+ struct rocket_job *job = to_rocket_job(sched_job);
+ struct rocket_device *rdev = job->rdev;
+ struct rocket_core *core = sched_to_core(rdev, sched_job->sched);
+
+ dev_err(core->dev, "NPU job timed out");
+
+ atomic_set(&core->reset.pending, 1);
+ rocket_reset(core, sched_job);
+
+ return DRM_GPU_SCHED_STAT_RESET;
+}
+
+static void rocket_reset_work(struct work_struct *work)
+{
+ struct rocket_core *core;
+
+ core = container_of(work, struct rocket_core, reset.work);
+ rocket_reset(core, NULL);
+}
+
+static const struct drm_sched_backend_ops rocket_sched_ops = {
+ .run_job = rocket_job_run,
+ .timedout_job = rocket_job_timedout,
+ .free_job = rocket_job_free
+};
+
+static irqreturn_t rocket_job_irq_handler_thread(int irq, void *data)
+{
+ struct rocket_core *core = data;
+
+ rocket_job_handle_irq(core);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rocket_job_irq_handler(int irq, void *data)
+{
+ struct rocket_core *core = data;
+ u32 raw_status = rocket_pc_readl(core, INTERRUPT_RAW_STATUS);
+
+ WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_READ_ERROR);
+ WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_WRITE_ERROR);
+
+ if (!(raw_status & PC_INTERRUPT_RAW_STATUS_DPU_0 ||
+ raw_status & PC_INTERRUPT_RAW_STATUS_DPU_1))
+ return IRQ_NONE;
+
+ rocket_pc_writel(core, INTERRUPT_MASK, 0x0);
+
+ return IRQ_WAKE_THREAD;
+}
+
+int rocket_job_init(struct rocket_core *core)
+{
+ struct drm_sched_init_args args = {
+ .ops = &rocket_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
+ .credit_limit = 1,
+ .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
+ .name = dev_name(core->dev),
+ .dev = core->dev,
+ };
+ int ret;
+
+ INIT_WORK(&core->reset.work, rocket_reset_work);
+ spin_lock_init(&core->fence_lock);
+ mutex_init(&core->job_lock);
+
+ core->irq = platform_get_irq(to_platform_device(core->dev), 0);
+ if (core->irq < 0)
+ return core->irq;
+
+ ret = devm_request_threaded_irq(core->dev, core->irq,
+ rocket_job_irq_handler,
+ rocket_job_irq_handler_thread,
+ IRQF_SHARED, dev_name(core->dev),
+ core);
+ if (ret) {
+ dev_err(core->dev, "failed to request job irq");
+ return ret;
+ }
+
+ core->reset.wq = alloc_ordered_workqueue("rocket-reset-%d", 0, core->index);
+ if (!core->reset.wq)
+ return -ENOMEM;
+
+ core->fence_context = dma_fence_context_alloc(1);
+
+ args.timeout_wq = core->reset.wq;
+ ret = drm_sched_init(&core->sched, &args);
+ if (ret) {
+ dev_err(core->dev, "Failed to create scheduler: %d.", ret);
+ goto err_sched;
+ }
+
+ return 0;
+
+err_sched:
+ drm_sched_fini(&core->sched);
+
+ destroy_workqueue(core->reset.wq);
+ return ret;
+}
+
+void rocket_job_fini(struct rocket_core *core)
+{
+ drm_sched_fini(&core->sched);
+
+ cancel_work_sync(&core->reset.work);
+ destroy_workqueue(core->reset.wq);
+}
+
+int rocket_job_open(struct rocket_file_priv *rocket_priv)
+{
+ struct rocket_device *rdev = rocket_priv->rdev;
+ struct drm_gpu_scheduler **scheds = kmalloc_array(rdev->num_cores,
+ sizeof(*scheds),
+ GFP_KERNEL);
+ unsigned int core;
+ int ret;
+
+ for (core = 0; core < rdev->num_cores; core++)
+ scheds[core] = &rdev->cores[core].sched;
+
+ ret = drm_sched_entity_init(&rocket_priv->sched_entity,
+ DRM_SCHED_PRIORITY_NORMAL,
+ scheds,
+ rdev->num_cores, NULL);
+ if (WARN_ON(ret))
+ return ret;
+
+ return 0;
+}
+
+void rocket_job_close(struct rocket_file_priv *rocket_priv)
+{
+ struct drm_sched_entity *entity = &rocket_priv->sched_entity;
+
+ kfree(entity->sched_list);
+ drm_sched_entity_destroy(entity);
+}
+
+int rocket_job_is_idle(struct rocket_core *core)
+{
+ /* If there are any jobs in this HW queue, we're not idle */
+ if (atomic_read(&core->sched.credit_count))
+ return false;
+
+ return true;
+}
+
+static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
+ struct drm_rocket_job *job)
+{
+ struct rocket_device *rdev = to_rocket_device(dev);
+ struct rocket_file_priv *file_priv = file->driver_priv;
+ struct rocket_job *rjob = NULL;
+ int ret = 0;
+
+ if (job->task_count == 0)
+ return -EINVAL;
+
+ rjob = kzalloc(sizeof(*rjob), GFP_KERNEL);
+ if (!rjob)
+ return -ENOMEM;
+
+ kref_init(&rjob->refcount);
+
+ rjob->rdev = rdev;
+
+ ret = drm_sched_job_init(&rjob->base,
+ &file_priv->sched_entity,
+ 1, NULL, file->client_id);
+ if (ret)
+ goto out_put_job;
+
+ ret = rocket_copy_tasks(dev, file, job, rjob);
+ if (ret)
+ goto out_cleanup_job;
+
+ ret = drm_gem_objects_lookup(file, u64_to_user_ptr(job->in_bo_handles),
+ job->in_bo_handle_count, &rjob->in_bos);
+ if (ret)
+ goto out_cleanup_job;
+
+ rjob->in_bo_count = job->in_bo_handle_count;
+
+ ret = drm_gem_objects_lookup(file, u64_to_user_ptr(job->out_bo_handles),
+ job->out_bo_handle_count, &rjob->out_bos);
+ if (ret)
+ goto out_cleanup_job;
+
+ rjob->out_bo_count = job->out_bo_handle_count;
+
+ rjob->domain = rocket_iommu_domain_get(file_priv);
+
+ ret = rocket_job_push(rjob);
+ if (ret)
+ goto out_cleanup_job;
+
+out_cleanup_job:
+ if (ret)
+ drm_sched_job_cleanup(&rjob->base);
+out_put_job:
+ rocket_job_put(rjob);
+
+ return ret;
+}
+
+int rocket_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_rocket_submit *args = data;
+ struct drm_rocket_job *jobs;
+ int ret = 0;
+ unsigned int i = 0;
+
+ if (args->job_count == 0)
+ return 0;
+
+ if (args->job_struct_size < sizeof(struct drm_rocket_job)) {
+ drm_dbg(dev, "job_struct_size field in drm_rocket_submit struct is too small.\n");
+ return -EINVAL;
+ }
+
+ if (args->reserved != 0) {
+ drm_dbg(dev, "Reserved field in drm_rocket_submit struct should be 0.\n");
+ return -EINVAL;
+ }
+
+ jobs = kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL);
+ if (!jobs) {
+ drm_dbg(dev, "Failed to allocate incoming job array\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < args->job_count; i++) {
+ if (copy_from_user(&jobs[i],
+ u64_to_user_ptr(args->jobs) + i * args->job_struct_size,
+ sizeof(*jobs))) {
+ ret = -EFAULT;
+ drm_dbg(dev, "Failed to copy incoming job array\n");
+ goto exit;
+ }
+ }
+
+
+ for (i = 0; i < args->job_count; i++)
+ rocket_ioctl_submit_job(dev, file, &jobs[i]);
+
+exit:
+ kvfree(jobs);
+
+ return ret;
+}
diff --git a/drivers/accel/rocket/rocket_job.h b/drivers/accel/rocket/rocket_job.h
new file mode 100644
index 000000000000..4ae00feec3b9
--- /dev/null
+++ b/drivers/accel/rocket/rocket_job.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+
+#ifndef __ROCKET_JOB_H__
+#define __ROCKET_JOB_H__
+
+#include <drm/drm_drv.h>
+#include <drm/gpu_scheduler.h>
+
+#include "rocket_core.h"
+#include "rocket_drv.h"
+
+struct rocket_task {
+ u64 regcmd;
+ u32 regcmd_count;
+};
+
+struct rocket_job {
+ struct drm_sched_job base;
+
+ struct rocket_device *rdev;
+
+ struct drm_gem_object **in_bos;
+ struct drm_gem_object **out_bos;
+
+ u32 in_bo_count;
+ u32 out_bo_count;
+
+ struct rocket_task *tasks;
+ u32 task_count;
+ u32 next_task_idx;
+
+ /* Fence to be signaled by drm-sched once its done with the job */
+ struct dma_fence *inference_done_fence;
+
+ /* Fence to be signaled by IRQ handler when the job is complete. */
+ struct dma_fence *done_fence;
+
+ struct rocket_iommu_domain *domain;
+
+ struct kref refcount;
+};
+
+int rocket_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file);
+
+int rocket_job_init(struct rocket_core *core);
+void rocket_job_fini(struct rocket_core *core);
+int rocket_job_open(struct rocket_file_priv *rocket_priv);
+void rocket_job_close(struct rocket_file_priv *rocket_priv);
+int rocket_job_is_idle(struct rocket_core *core);
+
+#endif
diff --git a/drivers/accel/rocket/rocket_registers.h b/drivers/accel/rocket/rocket_registers.h
new file mode 100644
index 000000000000..9aef614c3470
--- /dev/null
+++ b/drivers/accel/rocket/rocket_registers.h
@@ -0,0 +1,4404 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+
+#ifndef __ROCKET_REGISTERS_XML__
+#define __ROCKET_REGISTERS_XML__
+
+/* Autogenerated file, DO NOT EDIT manually!
+
+This file was generated by the rules-ng-ng gen_header.py tool in this git repository:
+http://gitlab.freedesktop.org/mesa/mesa/
+git clone https://gitlab.freedesktop.org/mesa/mesa.git
+
+The rules-ng-ng source files this header was generated from are:
+
+- /home/tomeu/src/mesa/src/gallium/drivers/rocket/registers.xml ( 60076 bytes, from Wed Jun 12 10:02:25 2024)
+
+Copyright (C) 2024-2025 by the following authors:
+- Tomeu Vizoso <tomeu@tomeuvizoso.net>
+*/
+
+#define REG_PC_VERSION 0x00000000
+#define PC_VERSION_VERSION__MASK 0xffffffff
+#define PC_VERSION_VERSION__SHIFT 0
+static inline uint32_t PC_VERSION_VERSION(uint32_t val)
+{
+ return ((val) << PC_VERSION_VERSION__SHIFT) & PC_VERSION_VERSION__MASK;
+}
+
+#define REG_PC_VERSION_NUM 0x00000004
+#define PC_VERSION_NUM_VERSION_NUM__MASK 0xffffffff
+#define PC_VERSION_NUM_VERSION_NUM__SHIFT 0
+static inline uint32_t PC_VERSION_NUM_VERSION_NUM(uint32_t val)
+{
+ return ((val) << PC_VERSION_NUM_VERSION_NUM__SHIFT) & PC_VERSION_NUM_VERSION_NUM__MASK;
+}
+
+#define REG_PC_OPERATION_ENABLE 0x00000008
+#define PC_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define PC_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t PC_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_OPERATION_ENABLE_RESERVED_0__SHIFT) & PC_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define PC_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define PC_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t PC_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << PC_OPERATION_ENABLE_OP_EN__SHIFT) & PC_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_PC_BASE_ADDRESS 0x00000010
+#define PC_BASE_ADDRESS_PC_SOURCE_ADDR__MASK 0xfffffff0
+#define PC_BASE_ADDRESS_PC_SOURCE_ADDR__SHIFT 4
+static inline uint32_t PC_BASE_ADDRESS_PC_SOURCE_ADDR(uint32_t val)
+{
+ return ((val) << PC_BASE_ADDRESS_PC_SOURCE_ADDR__SHIFT) & PC_BASE_ADDRESS_PC_SOURCE_ADDR__MASK;
+}
+#define PC_BASE_ADDRESS_RESERVED_0__MASK 0x0000000e
+#define PC_BASE_ADDRESS_RESERVED_0__SHIFT 1
+static inline uint32_t PC_BASE_ADDRESS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_BASE_ADDRESS_RESERVED_0__SHIFT) & PC_BASE_ADDRESS_RESERVED_0__MASK;
+}
+#define PC_BASE_ADDRESS_PC_SEL__MASK 0x00000001
+#define PC_BASE_ADDRESS_PC_SEL__SHIFT 0
+static inline uint32_t PC_BASE_ADDRESS_PC_SEL(uint32_t val)
+{
+ return ((val) << PC_BASE_ADDRESS_PC_SEL__SHIFT) & PC_BASE_ADDRESS_PC_SEL__MASK;
+}
+
+#define REG_PC_REGISTER_AMOUNTS 0x00000014
+#define PC_REGISTER_AMOUNTS_RESERVED_0__MASK 0xffff0000
+#define PC_REGISTER_AMOUNTS_RESERVED_0__SHIFT 16
+static inline uint32_t PC_REGISTER_AMOUNTS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_REGISTER_AMOUNTS_RESERVED_0__SHIFT) & PC_REGISTER_AMOUNTS_RESERVED_0__MASK;
+}
+#define PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__MASK 0x0000ffff
+#define PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__SHIFT 0
+static inline uint32_t PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT(uint32_t val)
+{
+ return ((val) << PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__SHIFT) & PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__MASK;
+}
+
+#define REG_PC_INTERRUPT_MASK 0x00000020
+#define PC_INTERRUPT_MASK_RESERVED_0__MASK 0xffffc000
+#define PC_INTERRUPT_MASK_RESERVED_0__SHIFT 14
+static inline uint32_t PC_INTERRUPT_MASK_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_INTERRUPT_MASK_RESERVED_0__SHIFT) & PC_INTERRUPT_MASK_RESERVED_0__MASK;
+}
+#define PC_INTERRUPT_MASK_DMA_WRITE_ERROR 0x00002000
+#define PC_INTERRUPT_MASK_DMA_READ_ERROR 0x00001000
+#define PC_INTERRUPT_MASK_PPU_1 0x00000800
+#define PC_INTERRUPT_MASK_PPU_0 0x00000400
+#define PC_INTERRUPT_MASK_DPU_1 0x00000200
+#define PC_INTERRUPT_MASK_DPU_0 0x00000100
+#define PC_INTERRUPT_MASK_CORE_1 0x00000080
+#define PC_INTERRUPT_MASK_CORE_0 0x00000040
+#define PC_INTERRUPT_MASK_CNA_CSC_1 0x00000020
+#define PC_INTERRUPT_MASK_CNA_CSC_0 0x00000010
+#define PC_INTERRUPT_MASK_CNA_WEIGHT_1 0x00000008
+#define PC_INTERRUPT_MASK_CNA_WEIGHT_0 0x00000004
+#define PC_INTERRUPT_MASK_CNA_FEATURE_1 0x00000002
+#define PC_INTERRUPT_MASK_CNA_FEATURE_0 0x00000001
+
+#define REG_PC_INTERRUPT_CLEAR 0x00000024
+#define PC_INTERRUPT_CLEAR_RESERVED_0__MASK 0xffffc000
+#define PC_INTERRUPT_CLEAR_RESERVED_0__SHIFT 14
+static inline uint32_t PC_INTERRUPT_CLEAR_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_INTERRUPT_CLEAR_RESERVED_0__SHIFT) & PC_INTERRUPT_CLEAR_RESERVED_0__MASK;
+}
+#define PC_INTERRUPT_CLEAR_DMA_WRITE_ERROR 0x00002000
+#define PC_INTERRUPT_CLEAR_DMA_READ_ERROR 0x00001000
+#define PC_INTERRUPT_CLEAR_PPU_1 0x00000800
+#define PC_INTERRUPT_CLEAR_PPU_0 0x00000400
+#define PC_INTERRUPT_CLEAR_DPU_1 0x00000200
+#define PC_INTERRUPT_CLEAR_DPU_0 0x00000100
+#define PC_INTERRUPT_CLEAR_CORE_1 0x00000080
+#define PC_INTERRUPT_CLEAR_CORE_0 0x00000040
+#define PC_INTERRUPT_CLEAR_CNA_CSC_1 0x00000020
+#define PC_INTERRUPT_CLEAR_CNA_CSC_0 0x00000010
+#define PC_INTERRUPT_CLEAR_CNA_WEIGHT_1 0x00000008
+#define PC_INTERRUPT_CLEAR_CNA_WEIGHT_0 0x00000004
+#define PC_INTERRUPT_CLEAR_CNA_FEATURE_1 0x00000002
+#define PC_INTERRUPT_CLEAR_CNA_FEATURE_0 0x00000001
+
+#define REG_PC_INTERRUPT_STATUS 0x00000028
+#define PC_INTERRUPT_STATUS_RESERVED_0__MASK 0xffffc000
+#define PC_INTERRUPT_STATUS_RESERVED_0__SHIFT 14
+static inline uint32_t PC_INTERRUPT_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_INTERRUPT_STATUS_RESERVED_0__SHIFT) & PC_INTERRUPT_STATUS_RESERVED_0__MASK;
+}
+#define PC_INTERRUPT_STATUS_DMA_WRITE_ERROR 0x00002000
+#define PC_INTERRUPT_STATUS_DMA_READ_ERROR 0x00001000
+#define PC_INTERRUPT_STATUS_PPU_1 0x00000800
+#define PC_INTERRUPT_STATUS_PPU_0 0x00000400
+#define PC_INTERRUPT_STATUS_DPU_1 0x00000200
+#define PC_INTERRUPT_STATUS_DPU_0 0x00000100
+#define PC_INTERRUPT_STATUS_CORE_1 0x00000080
+#define PC_INTERRUPT_STATUS_CORE_0 0x00000040
+#define PC_INTERRUPT_STATUS_CNA_CSC_1 0x00000020
+#define PC_INTERRUPT_STATUS_CNA_CSC_0 0x00000010
+#define PC_INTERRUPT_STATUS_CNA_WEIGHT_1 0x00000008
+#define PC_INTERRUPT_STATUS_CNA_WEIGHT_0 0x00000004
+#define PC_INTERRUPT_STATUS_CNA_FEATURE_1 0x00000002
+#define PC_INTERRUPT_STATUS_CNA_FEATURE_0 0x00000001
+
+#define REG_PC_INTERRUPT_RAW_STATUS 0x0000002c
+#define PC_INTERRUPT_RAW_STATUS_RESERVED_0__MASK 0xffffc000
+#define PC_INTERRUPT_RAW_STATUS_RESERVED_0__SHIFT 14
+static inline uint32_t PC_INTERRUPT_RAW_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_INTERRUPT_RAW_STATUS_RESERVED_0__SHIFT) & PC_INTERRUPT_RAW_STATUS_RESERVED_0__MASK;
+}
+#define PC_INTERRUPT_RAW_STATUS_DMA_WRITE_ERROR 0x00002000
+#define PC_INTERRUPT_RAW_STATUS_DMA_READ_ERROR 0x00001000
+#define PC_INTERRUPT_RAW_STATUS_PPU_1 0x00000800
+#define PC_INTERRUPT_RAW_STATUS_PPU_0 0x00000400
+#define PC_INTERRUPT_RAW_STATUS_DPU_1 0x00000200
+#define PC_INTERRUPT_RAW_STATUS_DPU_0 0x00000100
+#define PC_INTERRUPT_RAW_STATUS_CORE_1 0x00000080
+#define PC_INTERRUPT_RAW_STATUS_CORE_0 0x00000040
+#define PC_INTERRUPT_RAW_STATUS_CNA_CSC_1 0x00000020
+#define PC_INTERRUPT_RAW_STATUS_CNA_CSC_0 0x00000010
+#define PC_INTERRUPT_RAW_STATUS_CNA_WEIGHT_1 0x00000008
+#define PC_INTERRUPT_RAW_STATUS_CNA_WEIGHT_0 0x00000004
+#define PC_INTERRUPT_RAW_STATUS_CNA_FEATURE_1 0x00000002
+#define PC_INTERRUPT_RAW_STATUS_CNA_FEATURE_0 0x00000001
+
+#define REG_PC_TASK_CON 0x00000030
+#define PC_TASK_CON_RESERVED_0__MASK 0xffffc000
+#define PC_TASK_CON_RESERVED_0__SHIFT 14
+static inline uint32_t PC_TASK_CON_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_TASK_CON_RESERVED_0__SHIFT) & PC_TASK_CON_RESERVED_0__MASK;
+}
+#define PC_TASK_CON_TASK_COUNT_CLEAR__MASK 0x00002000
+#define PC_TASK_CON_TASK_COUNT_CLEAR__SHIFT 13
+static inline uint32_t PC_TASK_CON_TASK_COUNT_CLEAR(uint32_t val)
+{
+ return ((val) << PC_TASK_CON_TASK_COUNT_CLEAR__SHIFT) & PC_TASK_CON_TASK_COUNT_CLEAR__MASK;
+}
+#define PC_TASK_CON_TASK_PP_EN__MASK 0x00001000
+#define PC_TASK_CON_TASK_PP_EN__SHIFT 12
+static inline uint32_t PC_TASK_CON_TASK_PP_EN(uint32_t val)
+{
+ return ((val) << PC_TASK_CON_TASK_PP_EN__SHIFT) & PC_TASK_CON_TASK_PP_EN__MASK;
+}
+#define PC_TASK_CON_TASK_NUMBER__MASK 0x00000fff
+#define PC_TASK_CON_TASK_NUMBER__SHIFT 0
+static inline uint32_t PC_TASK_CON_TASK_NUMBER(uint32_t val)
+{
+ return ((val) << PC_TASK_CON_TASK_NUMBER__SHIFT) & PC_TASK_CON_TASK_NUMBER__MASK;
+}
+
+#define REG_PC_TASK_DMA_BASE_ADDR 0x00000034
+#define PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__MASK 0xfffffff0
+#define PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__SHIFT 4
+static inline uint32_t PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR(uint32_t val)
+{
+ return ((val) << PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__SHIFT) & PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR__MASK;
+}
+#define PC_TASK_DMA_BASE_ADDR_RESERVED_0__MASK 0x0000000f
+#define PC_TASK_DMA_BASE_ADDR_RESERVED_0__SHIFT 0
+static inline uint32_t PC_TASK_DMA_BASE_ADDR_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_TASK_DMA_BASE_ADDR_RESERVED_0__SHIFT) & PC_TASK_DMA_BASE_ADDR_RESERVED_0__MASK;
+}
+
+#define REG_PC_TASK_STATUS 0x0000003c
+#define PC_TASK_STATUS_RESERVED_0__MASK 0xf0000000
+#define PC_TASK_STATUS_RESERVED_0__SHIFT 28
+static inline uint32_t PC_TASK_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PC_TASK_STATUS_RESERVED_0__SHIFT) & PC_TASK_STATUS_RESERVED_0__MASK;
+}
+#define PC_TASK_STATUS_TASK_STATUS__MASK 0x0fffffff
+#define PC_TASK_STATUS_TASK_STATUS__SHIFT 0
+static inline uint32_t PC_TASK_STATUS_TASK_STATUS(uint32_t val)
+{
+ return ((val) << PC_TASK_STATUS_TASK_STATUS__SHIFT) & PC_TASK_STATUS_TASK_STATUS__MASK;
+}
+
+#define REG_CNA_S_STATUS 0x00001000
+#define CNA_S_STATUS_RESERVED_0__MASK 0xfffc0000
+#define CNA_S_STATUS_RESERVED_0__SHIFT 18
+static inline uint32_t CNA_S_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_S_STATUS_RESERVED_0__SHIFT) & CNA_S_STATUS_RESERVED_0__MASK;
+}
+#define CNA_S_STATUS_STATUS_1__MASK 0x00030000
+#define CNA_S_STATUS_STATUS_1__SHIFT 16
+static inline uint32_t CNA_S_STATUS_STATUS_1(uint32_t val)
+{
+ return ((val) << CNA_S_STATUS_STATUS_1__SHIFT) & CNA_S_STATUS_STATUS_1__MASK;
+}
+#define CNA_S_STATUS_RESERVED_1__MASK 0x0000fffc
+#define CNA_S_STATUS_RESERVED_1__SHIFT 2
+static inline uint32_t CNA_S_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_S_STATUS_RESERVED_1__SHIFT) & CNA_S_STATUS_RESERVED_1__MASK;
+}
+#define CNA_S_STATUS_STATUS_0__MASK 0x00000003
+#define CNA_S_STATUS_STATUS_0__SHIFT 0
+static inline uint32_t CNA_S_STATUS_STATUS_0(uint32_t val)
+{
+ return ((val) << CNA_S_STATUS_STATUS_0__SHIFT) & CNA_S_STATUS_STATUS_0__MASK;
+}
+
+#define REG_CNA_S_POINTER 0x00001004
+#define CNA_S_POINTER_RESERVED_0__MASK 0xfffe0000
+#define CNA_S_POINTER_RESERVED_0__SHIFT 17
+static inline uint32_t CNA_S_POINTER_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_RESERVED_0__SHIFT) & CNA_S_POINTER_RESERVED_0__MASK;
+}
+#define CNA_S_POINTER_EXECUTER__MASK 0x00010000
+#define CNA_S_POINTER_EXECUTER__SHIFT 16
+static inline uint32_t CNA_S_POINTER_EXECUTER(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_EXECUTER__SHIFT) & CNA_S_POINTER_EXECUTER__MASK;
+}
+#define CNA_S_POINTER_RESERVED_1__MASK 0x0000ffc0
+#define CNA_S_POINTER_RESERVED_1__SHIFT 6
+static inline uint32_t CNA_S_POINTER_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_RESERVED_1__SHIFT) & CNA_S_POINTER_RESERVED_1__MASK;
+}
+#define CNA_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020
+#define CNA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5
+static inline uint32_t CNA_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & CNA_S_POINTER_EXECUTER_PP_CLEAR__MASK;
+}
+#define CNA_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010
+#define CNA_S_POINTER_POINTER_PP_CLEAR__SHIFT 4
+static inline uint32_t CNA_S_POINTER_POINTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_POINTER_PP_CLEAR__SHIFT) & CNA_S_POINTER_POINTER_PP_CLEAR__MASK;
+}
+#define CNA_S_POINTER_POINTER_PP_MODE__MASK 0x00000008
+#define CNA_S_POINTER_POINTER_PP_MODE__SHIFT 3
+static inline uint32_t CNA_S_POINTER_POINTER_PP_MODE(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_POINTER_PP_MODE__SHIFT) & CNA_S_POINTER_POINTER_PP_MODE__MASK;
+}
+#define CNA_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004
+#define CNA_S_POINTER_EXECUTER_PP_EN__SHIFT 2
+static inline uint32_t CNA_S_POINTER_EXECUTER_PP_EN(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_EXECUTER_PP_EN__SHIFT) & CNA_S_POINTER_EXECUTER_PP_EN__MASK;
+}
+#define CNA_S_POINTER_POINTER_PP_EN__MASK 0x00000002
+#define CNA_S_POINTER_POINTER_PP_EN__SHIFT 1
+static inline uint32_t CNA_S_POINTER_POINTER_PP_EN(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_POINTER_PP_EN__SHIFT) & CNA_S_POINTER_POINTER_PP_EN__MASK;
+}
+#define CNA_S_POINTER_POINTER__MASK 0x00000001
+#define CNA_S_POINTER_POINTER__SHIFT 0
+static inline uint32_t CNA_S_POINTER_POINTER(uint32_t val)
+{
+ return ((val) << CNA_S_POINTER_POINTER__SHIFT) & CNA_S_POINTER_POINTER__MASK;
+}
+
+#define REG_CNA_OPERATION_ENABLE 0x00001008
+#define CNA_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define CNA_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t CNA_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_OPERATION_ENABLE_RESERVED_0__SHIFT) & CNA_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define CNA_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define CNA_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t CNA_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << CNA_OPERATION_ENABLE_OP_EN__SHIFT) & CNA_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_CNA_CONV_CON1 0x0000100c
+#define CNA_CONV_CON1_RESERVED_0__MASK 0x80000000
+#define CNA_CONV_CON1_RESERVED_0__SHIFT 31
+static inline uint32_t CNA_CONV_CON1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_RESERVED_0__SHIFT) & CNA_CONV_CON1_RESERVED_0__MASK;
+}
+#define CNA_CONV_CON1_NONALIGN_DMA__MASK 0x40000000
+#define CNA_CONV_CON1_NONALIGN_DMA__SHIFT 30
+static inline uint32_t CNA_CONV_CON1_NONALIGN_DMA(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_NONALIGN_DMA__SHIFT) & CNA_CONV_CON1_NONALIGN_DMA__MASK;
+}
+#define CNA_CONV_CON1_GROUP_LINE_OFF__MASK 0x20000000
+#define CNA_CONV_CON1_GROUP_LINE_OFF__SHIFT 29
+static inline uint32_t CNA_CONV_CON1_GROUP_LINE_OFF(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_GROUP_LINE_OFF__SHIFT) & CNA_CONV_CON1_GROUP_LINE_OFF__MASK;
+}
+#define CNA_CONV_CON1_RESERVED_1__MASK 0x1ffe0000
+#define CNA_CONV_CON1_RESERVED_1__SHIFT 17
+static inline uint32_t CNA_CONV_CON1_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_RESERVED_1__SHIFT) & CNA_CONV_CON1_RESERVED_1__MASK;
+}
+#define CNA_CONV_CON1_DECONV__MASK 0x00010000
+#define CNA_CONV_CON1_DECONV__SHIFT 16
+static inline uint32_t CNA_CONV_CON1_DECONV(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_DECONV__SHIFT) & CNA_CONV_CON1_DECONV__MASK;
+}
+#define CNA_CONV_CON1_ARGB_IN__MASK 0x0000f000
+#define CNA_CONV_CON1_ARGB_IN__SHIFT 12
+static inline uint32_t CNA_CONV_CON1_ARGB_IN(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_ARGB_IN__SHIFT) & CNA_CONV_CON1_ARGB_IN__MASK;
+}
+#define CNA_CONV_CON1_RESERVED_2__MASK 0x00000c00
+#define CNA_CONV_CON1_RESERVED_2__SHIFT 10
+static inline uint32_t CNA_CONV_CON1_RESERVED_2(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_RESERVED_2__SHIFT) & CNA_CONV_CON1_RESERVED_2__MASK;
+}
+#define CNA_CONV_CON1_PROC_PRECISION__MASK 0x00000380
+#define CNA_CONV_CON1_PROC_PRECISION__SHIFT 7
+static inline uint32_t CNA_CONV_CON1_PROC_PRECISION(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_PROC_PRECISION__SHIFT) & CNA_CONV_CON1_PROC_PRECISION__MASK;
+}
+#define CNA_CONV_CON1_IN_PRECISION__MASK 0x00000070
+#define CNA_CONV_CON1_IN_PRECISION__SHIFT 4
+static inline uint32_t CNA_CONV_CON1_IN_PRECISION(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_IN_PRECISION__SHIFT) & CNA_CONV_CON1_IN_PRECISION__MASK;
+}
+#define CNA_CONV_CON1_CONV_MODE__MASK 0x0000000f
+#define CNA_CONV_CON1_CONV_MODE__SHIFT 0
+static inline uint32_t CNA_CONV_CON1_CONV_MODE(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON1_CONV_MODE__SHIFT) & CNA_CONV_CON1_CONV_MODE__MASK;
+}
+
+#define REG_CNA_CONV_CON2 0x00001010
+#define CNA_CONV_CON2_RESERVED_0__MASK 0xff000000
+#define CNA_CONV_CON2_RESERVED_0__SHIFT 24
+static inline uint32_t CNA_CONV_CON2_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_RESERVED_0__SHIFT) & CNA_CONV_CON2_RESERVED_0__MASK;
+}
+#define CNA_CONV_CON2_KERNEL_GROUP__MASK 0x00ff0000
+#define CNA_CONV_CON2_KERNEL_GROUP__SHIFT 16
+static inline uint32_t CNA_CONV_CON2_KERNEL_GROUP(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_KERNEL_GROUP__SHIFT) & CNA_CONV_CON2_KERNEL_GROUP__MASK;
+}
+#define CNA_CONV_CON2_RESERVED_1__MASK 0x0000c000
+#define CNA_CONV_CON2_RESERVED_1__SHIFT 14
+static inline uint32_t CNA_CONV_CON2_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_RESERVED_1__SHIFT) & CNA_CONV_CON2_RESERVED_1__MASK;
+}
+#define CNA_CONV_CON2_FEATURE_GRAINS__MASK 0x00003ff0
+#define CNA_CONV_CON2_FEATURE_GRAINS__SHIFT 4
+static inline uint32_t CNA_CONV_CON2_FEATURE_GRAINS(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_FEATURE_GRAINS__SHIFT) & CNA_CONV_CON2_FEATURE_GRAINS__MASK;
+}
+#define CNA_CONV_CON2_RESERVED_2__MASK 0x00000008
+#define CNA_CONV_CON2_RESERVED_2__SHIFT 3
+static inline uint32_t CNA_CONV_CON2_RESERVED_2(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_RESERVED_2__SHIFT) & CNA_CONV_CON2_RESERVED_2__MASK;
+}
+#define CNA_CONV_CON2_CSC_WO_EN__MASK 0x00000004
+#define CNA_CONV_CON2_CSC_WO_EN__SHIFT 2
+static inline uint32_t CNA_CONV_CON2_CSC_WO_EN(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_CSC_WO_EN__SHIFT) & CNA_CONV_CON2_CSC_WO_EN__MASK;
+}
+#define CNA_CONV_CON2_CSC_DO_EN__MASK 0x00000002
+#define CNA_CONV_CON2_CSC_DO_EN__SHIFT 1
+static inline uint32_t CNA_CONV_CON2_CSC_DO_EN(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_CSC_DO_EN__SHIFT) & CNA_CONV_CON2_CSC_DO_EN__MASK;
+}
+#define CNA_CONV_CON2_CMD_FIFO_SRST__MASK 0x00000001
+#define CNA_CONV_CON2_CMD_FIFO_SRST__SHIFT 0
+static inline uint32_t CNA_CONV_CON2_CMD_FIFO_SRST(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON2_CMD_FIFO_SRST__SHIFT) & CNA_CONV_CON2_CMD_FIFO_SRST__MASK;
+}
+
+#define REG_CNA_CONV_CON3 0x00001014
+#define CNA_CONV_CON3_RESERVED_0__MASK 0x80000000
+#define CNA_CONV_CON3_RESERVED_0__SHIFT 31
+static inline uint32_t CNA_CONV_CON3_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_RESERVED_0__SHIFT) & CNA_CONV_CON3_RESERVED_0__MASK;
+}
+#define CNA_CONV_CON3_NN_MODE__MASK 0x70000000
+#define CNA_CONV_CON3_NN_MODE__SHIFT 28
+static inline uint32_t CNA_CONV_CON3_NN_MODE(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_NN_MODE__SHIFT) & CNA_CONV_CON3_NN_MODE__MASK;
+}
+#define CNA_CONV_CON3_RESERVED_1__MASK 0x0c000000
+#define CNA_CONV_CON3_RESERVED_1__SHIFT 26
+static inline uint32_t CNA_CONV_CON3_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_RESERVED_1__SHIFT) & CNA_CONV_CON3_RESERVED_1__MASK;
+}
+#define CNA_CONV_CON3_ATROUS_Y_DILATION__MASK 0x03e00000
+#define CNA_CONV_CON3_ATROUS_Y_DILATION__SHIFT 21
+static inline uint32_t CNA_CONV_CON3_ATROUS_Y_DILATION(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_ATROUS_Y_DILATION__SHIFT) & CNA_CONV_CON3_ATROUS_Y_DILATION__MASK;
+}
+#define CNA_CONV_CON3_ATROUS_X_DILATION__MASK 0x001f0000
+#define CNA_CONV_CON3_ATROUS_X_DILATION__SHIFT 16
+static inline uint32_t CNA_CONV_CON3_ATROUS_X_DILATION(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_ATROUS_X_DILATION__SHIFT) & CNA_CONV_CON3_ATROUS_X_DILATION__MASK;
+}
+#define CNA_CONV_CON3_RESERVED_2__MASK 0x0000c000
+#define CNA_CONV_CON3_RESERVED_2__SHIFT 14
+static inline uint32_t CNA_CONV_CON3_RESERVED_2(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_RESERVED_2__SHIFT) & CNA_CONV_CON3_RESERVED_2__MASK;
+}
+#define CNA_CONV_CON3_DECONV_Y_STRIDE__MASK 0x00003800
+#define CNA_CONV_CON3_DECONV_Y_STRIDE__SHIFT 11
+static inline uint32_t CNA_CONV_CON3_DECONV_Y_STRIDE(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_DECONV_Y_STRIDE__SHIFT) & CNA_CONV_CON3_DECONV_Y_STRIDE__MASK;
+}
+#define CNA_CONV_CON3_DECONV_X_STRIDE__MASK 0x00000700
+#define CNA_CONV_CON3_DECONV_X_STRIDE__SHIFT 8
+static inline uint32_t CNA_CONV_CON3_DECONV_X_STRIDE(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_DECONV_X_STRIDE__SHIFT) & CNA_CONV_CON3_DECONV_X_STRIDE__MASK;
+}
+#define CNA_CONV_CON3_RESERVED_3__MASK 0x000000c0
+#define CNA_CONV_CON3_RESERVED_3__SHIFT 6
+static inline uint32_t CNA_CONV_CON3_RESERVED_3(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_RESERVED_3__SHIFT) & CNA_CONV_CON3_RESERVED_3__MASK;
+}
+#define CNA_CONV_CON3_CONV_Y_STRIDE__MASK 0x00000038
+#define CNA_CONV_CON3_CONV_Y_STRIDE__SHIFT 3
+static inline uint32_t CNA_CONV_CON3_CONV_Y_STRIDE(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_CONV_Y_STRIDE__SHIFT) & CNA_CONV_CON3_CONV_Y_STRIDE__MASK;
+}
+#define CNA_CONV_CON3_CONV_X_STRIDE__MASK 0x00000007
+#define CNA_CONV_CON3_CONV_X_STRIDE__SHIFT 0
+static inline uint32_t CNA_CONV_CON3_CONV_X_STRIDE(uint32_t val)
+{
+ return ((val) << CNA_CONV_CON3_CONV_X_STRIDE__SHIFT) & CNA_CONV_CON3_CONV_X_STRIDE__MASK;
+}
+
+#define REG_CNA_DATA_SIZE0 0x00001020
+#define CNA_DATA_SIZE0_RESERVED_0__MASK 0xf8000000
+#define CNA_DATA_SIZE0_RESERVED_0__SHIFT 27
+static inline uint32_t CNA_DATA_SIZE0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE0_RESERVED_0__SHIFT) & CNA_DATA_SIZE0_RESERVED_0__MASK;
+}
+#define CNA_DATA_SIZE0_DATAIN_WIDTH__MASK 0x07ff0000
+#define CNA_DATA_SIZE0_DATAIN_WIDTH__SHIFT 16
+static inline uint32_t CNA_DATA_SIZE0_DATAIN_WIDTH(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE0_DATAIN_WIDTH__SHIFT) & CNA_DATA_SIZE0_DATAIN_WIDTH__MASK;
+}
+#define CNA_DATA_SIZE0_RESERVED_1__MASK 0x0000f800
+#define CNA_DATA_SIZE0_RESERVED_1__SHIFT 11
+static inline uint32_t CNA_DATA_SIZE0_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE0_RESERVED_1__SHIFT) & CNA_DATA_SIZE0_RESERVED_1__MASK;
+}
+#define CNA_DATA_SIZE0_DATAIN_HEIGHT__MASK 0x000007ff
+#define CNA_DATA_SIZE0_DATAIN_HEIGHT__SHIFT 0
+static inline uint32_t CNA_DATA_SIZE0_DATAIN_HEIGHT(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE0_DATAIN_HEIGHT__SHIFT) & CNA_DATA_SIZE0_DATAIN_HEIGHT__MASK;
+}
+
+#define REG_CNA_DATA_SIZE1 0x00001024
+#define CNA_DATA_SIZE1_RESERVED_0__MASK 0xc0000000
+#define CNA_DATA_SIZE1_RESERVED_0__SHIFT 30
+static inline uint32_t CNA_DATA_SIZE1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE1_RESERVED_0__SHIFT) & CNA_DATA_SIZE1_RESERVED_0__MASK;
+}
+#define CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__MASK 0x3fff0000
+#define CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__SHIFT 16
+static inline uint32_t CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__SHIFT) & CNA_DATA_SIZE1_DATAIN_CHANNEL_REAL__MASK;
+}
+#define CNA_DATA_SIZE1_DATAIN_CHANNEL__MASK 0x0000ffff
+#define CNA_DATA_SIZE1_DATAIN_CHANNEL__SHIFT 0
+static inline uint32_t CNA_DATA_SIZE1_DATAIN_CHANNEL(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE1_DATAIN_CHANNEL__SHIFT) & CNA_DATA_SIZE1_DATAIN_CHANNEL__MASK;
+}
+
+#define REG_CNA_DATA_SIZE2 0x00001028
+#define CNA_DATA_SIZE2_RESERVED_0__MASK 0xfffff800
+#define CNA_DATA_SIZE2_RESERVED_0__SHIFT 11
+static inline uint32_t CNA_DATA_SIZE2_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE2_RESERVED_0__SHIFT) & CNA_DATA_SIZE2_RESERVED_0__MASK;
+}
+#define CNA_DATA_SIZE2_DATAOUT_WIDTH__MASK 0x000007ff
+#define CNA_DATA_SIZE2_DATAOUT_WIDTH__SHIFT 0
+static inline uint32_t CNA_DATA_SIZE2_DATAOUT_WIDTH(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE2_DATAOUT_WIDTH__SHIFT) & CNA_DATA_SIZE2_DATAOUT_WIDTH__MASK;
+}
+
+#define REG_CNA_DATA_SIZE3 0x0000102c
+#define CNA_DATA_SIZE3_RESERVED_0__MASK 0xff000000
+#define CNA_DATA_SIZE3_RESERVED_0__SHIFT 24
+static inline uint32_t CNA_DATA_SIZE3_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE3_RESERVED_0__SHIFT) & CNA_DATA_SIZE3_RESERVED_0__MASK;
+}
+#define CNA_DATA_SIZE3_SURF_MODE__MASK 0x00c00000
+#define CNA_DATA_SIZE3_SURF_MODE__SHIFT 22
+static inline uint32_t CNA_DATA_SIZE3_SURF_MODE(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE3_SURF_MODE__SHIFT) & CNA_DATA_SIZE3_SURF_MODE__MASK;
+}
+#define CNA_DATA_SIZE3_DATAOUT_ATOMICS__MASK 0x003fffff
+#define CNA_DATA_SIZE3_DATAOUT_ATOMICS__SHIFT 0
+static inline uint32_t CNA_DATA_SIZE3_DATAOUT_ATOMICS(uint32_t val)
+{
+ return ((val) << CNA_DATA_SIZE3_DATAOUT_ATOMICS__SHIFT) & CNA_DATA_SIZE3_DATAOUT_ATOMICS__MASK;
+}
+
+#define REG_CNA_WEIGHT_SIZE0 0x00001030
+#define CNA_WEIGHT_SIZE0_WEIGHT_BYTES__MASK 0xffffffff
+#define CNA_WEIGHT_SIZE0_WEIGHT_BYTES__SHIFT 0
+static inline uint32_t CNA_WEIGHT_SIZE0_WEIGHT_BYTES(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE0_WEIGHT_BYTES__SHIFT) & CNA_WEIGHT_SIZE0_WEIGHT_BYTES__MASK;
+}
+
+#define REG_CNA_WEIGHT_SIZE1 0x00001034
+#define CNA_WEIGHT_SIZE1_RESERVED_0__MASK 0xfff80000
+#define CNA_WEIGHT_SIZE1_RESERVED_0__SHIFT 19
+static inline uint32_t CNA_WEIGHT_SIZE1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE1_RESERVED_0__SHIFT) & CNA_WEIGHT_SIZE1_RESERVED_0__MASK;
+}
+#define CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__MASK 0x0007ffff
+#define CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__SHIFT 0
+static inline uint32_t CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__SHIFT) & CNA_WEIGHT_SIZE1_WEIGHT_BYTES_PER_KERNEL__MASK;
+}
+
+#define REG_CNA_WEIGHT_SIZE2 0x00001038
+#define CNA_WEIGHT_SIZE2_RESERVED_0__MASK 0xe0000000
+#define CNA_WEIGHT_SIZE2_RESERVED_0__SHIFT 29
+static inline uint32_t CNA_WEIGHT_SIZE2_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE2_RESERVED_0__SHIFT) & CNA_WEIGHT_SIZE2_RESERVED_0__MASK;
+}
+#define CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__MASK 0x1f000000
+#define CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__SHIFT 24
+static inline uint32_t CNA_WEIGHT_SIZE2_WEIGHT_WIDTH(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__SHIFT) & CNA_WEIGHT_SIZE2_WEIGHT_WIDTH__MASK;
+}
+#define CNA_WEIGHT_SIZE2_RESERVED_1__MASK 0x00e00000
+#define CNA_WEIGHT_SIZE2_RESERVED_1__SHIFT 21
+static inline uint32_t CNA_WEIGHT_SIZE2_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE2_RESERVED_1__SHIFT) & CNA_WEIGHT_SIZE2_RESERVED_1__MASK;
+}
+#define CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__MASK 0x001f0000
+#define CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__SHIFT 16
+static inline uint32_t CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__SHIFT) & CNA_WEIGHT_SIZE2_WEIGHT_HEIGHT__MASK;
+}
+#define CNA_WEIGHT_SIZE2_RESERVED_2__MASK 0x0000c000
+#define CNA_WEIGHT_SIZE2_RESERVED_2__SHIFT 14
+static inline uint32_t CNA_WEIGHT_SIZE2_RESERVED_2(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE2_RESERVED_2__SHIFT) & CNA_WEIGHT_SIZE2_RESERVED_2__MASK;
+}
+#define CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__MASK 0x00003fff
+#define CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__SHIFT 0
+static inline uint32_t CNA_WEIGHT_SIZE2_WEIGHT_KERNELS(uint32_t val)
+{
+ return ((val) << CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__SHIFT) & CNA_WEIGHT_SIZE2_WEIGHT_KERNELS__MASK;
+}
+
+#define REG_CNA_CBUF_CON0 0x00001040
+#define CNA_CBUF_CON0_RESERVED_0__MASK 0xffffc000
+#define CNA_CBUF_CON0_RESERVED_0__SHIFT 14
+static inline uint32_t CNA_CBUF_CON0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_RESERVED_0__SHIFT) & CNA_CBUF_CON0_RESERVED_0__MASK;
+}
+#define CNA_CBUF_CON0_WEIGHT_REUSE__MASK 0x00002000
+#define CNA_CBUF_CON0_WEIGHT_REUSE__SHIFT 13
+static inline uint32_t CNA_CBUF_CON0_WEIGHT_REUSE(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_WEIGHT_REUSE__SHIFT) & CNA_CBUF_CON0_WEIGHT_REUSE__MASK;
+}
+#define CNA_CBUF_CON0_DATA_REUSE__MASK 0x00001000
+#define CNA_CBUF_CON0_DATA_REUSE__SHIFT 12
+static inline uint32_t CNA_CBUF_CON0_DATA_REUSE(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_DATA_REUSE__SHIFT) & CNA_CBUF_CON0_DATA_REUSE__MASK;
+}
+#define CNA_CBUF_CON0_RESERVED_1__MASK 0x00000800
+#define CNA_CBUF_CON0_RESERVED_1__SHIFT 11
+static inline uint32_t CNA_CBUF_CON0_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_RESERVED_1__SHIFT) & CNA_CBUF_CON0_RESERVED_1__MASK;
+}
+#define CNA_CBUF_CON0_FC_DATA_BANK__MASK 0x00000700
+#define CNA_CBUF_CON0_FC_DATA_BANK__SHIFT 8
+static inline uint32_t CNA_CBUF_CON0_FC_DATA_BANK(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_FC_DATA_BANK__SHIFT) & CNA_CBUF_CON0_FC_DATA_BANK__MASK;
+}
+#define CNA_CBUF_CON0_WEIGHT_BANK__MASK 0x000000f0
+#define CNA_CBUF_CON0_WEIGHT_BANK__SHIFT 4
+static inline uint32_t CNA_CBUF_CON0_WEIGHT_BANK(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_WEIGHT_BANK__SHIFT) & CNA_CBUF_CON0_WEIGHT_BANK__MASK;
+}
+#define CNA_CBUF_CON0_DATA_BANK__MASK 0x0000000f
+#define CNA_CBUF_CON0_DATA_BANK__SHIFT 0
+static inline uint32_t CNA_CBUF_CON0_DATA_BANK(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON0_DATA_BANK__SHIFT) & CNA_CBUF_CON0_DATA_BANK__MASK;
+}
+
+#define REG_CNA_CBUF_CON1 0x00001044
+#define CNA_CBUF_CON1_RESERVED_0__MASK 0xffffc000
+#define CNA_CBUF_CON1_RESERVED_0__SHIFT 14
+static inline uint32_t CNA_CBUF_CON1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON1_RESERVED_0__SHIFT) & CNA_CBUF_CON1_RESERVED_0__MASK;
+}
+#define CNA_CBUF_CON1_DATA_ENTRIES__MASK 0x00003fff
+#define CNA_CBUF_CON1_DATA_ENTRIES__SHIFT 0
+static inline uint32_t CNA_CBUF_CON1_DATA_ENTRIES(uint32_t val)
+{
+ return ((val) << CNA_CBUF_CON1_DATA_ENTRIES__SHIFT) & CNA_CBUF_CON1_DATA_ENTRIES__MASK;
+}
+
+#define REG_CNA_CVT_CON0 0x0000104c
+#define CNA_CVT_CON0_RESERVED_0__MASK 0xf0000000
+#define CNA_CVT_CON0_RESERVED_0__SHIFT 28
+static inline uint32_t CNA_CVT_CON0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_RESERVED_0__SHIFT) & CNA_CVT_CON0_RESERVED_0__MASK;
+}
+#define CNA_CVT_CON0_CVT_TRUNCATE_3__MASK 0x0fc00000
+#define CNA_CVT_CON0_CVT_TRUNCATE_3__SHIFT 22
+static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_3(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_3__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_3__MASK;
+}
+#define CNA_CVT_CON0_CVT_TRUNCATE_2__MASK 0x003f0000
+#define CNA_CVT_CON0_CVT_TRUNCATE_2__SHIFT 16
+static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_2(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_2__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_2__MASK;
+}
+#define CNA_CVT_CON0_CVT_TRUNCATE_1__MASK 0x0000fc00
+#define CNA_CVT_CON0_CVT_TRUNCATE_1__SHIFT 10
+static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_1(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_1__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_1__MASK;
+}
+#define CNA_CVT_CON0_CVT_TRUNCATE_0__MASK 0x000003f0
+#define CNA_CVT_CON0_CVT_TRUNCATE_0__SHIFT 4
+static inline uint32_t CNA_CVT_CON0_CVT_TRUNCATE_0(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_CVT_TRUNCATE_0__SHIFT) & CNA_CVT_CON0_CVT_TRUNCATE_0__MASK;
+}
+#define CNA_CVT_CON0_DATA_SIGN__MASK 0x00000008
+#define CNA_CVT_CON0_DATA_SIGN__SHIFT 3
+static inline uint32_t CNA_CVT_CON0_DATA_SIGN(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_DATA_SIGN__SHIFT) & CNA_CVT_CON0_DATA_SIGN__MASK;
+}
+#define CNA_CVT_CON0_ROUND_TYPE__MASK 0x00000004
+#define CNA_CVT_CON0_ROUND_TYPE__SHIFT 2
+static inline uint32_t CNA_CVT_CON0_ROUND_TYPE(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_ROUND_TYPE__SHIFT) & CNA_CVT_CON0_ROUND_TYPE__MASK;
+}
+#define CNA_CVT_CON0_CVT_TYPE__MASK 0x00000002
+#define CNA_CVT_CON0_CVT_TYPE__SHIFT 1
+static inline uint32_t CNA_CVT_CON0_CVT_TYPE(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_CVT_TYPE__SHIFT) & CNA_CVT_CON0_CVT_TYPE__MASK;
+}
+#define CNA_CVT_CON0_CVT_BYPASS__MASK 0x00000001
+#define CNA_CVT_CON0_CVT_BYPASS__SHIFT 0
+static inline uint32_t CNA_CVT_CON0_CVT_BYPASS(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON0_CVT_BYPASS__SHIFT) & CNA_CVT_CON0_CVT_BYPASS__MASK;
+}
+
+#define REG_CNA_CVT_CON1 0x00001050
+#define CNA_CVT_CON1_CVT_SCALE0__MASK 0xffff0000
+#define CNA_CVT_CON1_CVT_SCALE0__SHIFT 16
+static inline uint32_t CNA_CVT_CON1_CVT_SCALE0(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON1_CVT_SCALE0__SHIFT) & CNA_CVT_CON1_CVT_SCALE0__MASK;
+}
+#define CNA_CVT_CON1_CVT_OFFSET0__MASK 0x0000ffff
+#define CNA_CVT_CON1_CVT_OFFSET0__SHIFT 0
+static inline uint32_t CNA_CVT_CON1_CVT_OFFSET0(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON1_CVT_OFFSET0__SHIFT) & CNA_CVT_CON1_CVT_OFFSET0__MASK;
+}
+
+#define REG_CNA_CVT_CON2 0x00001054
+#define CNA_CVT_CON2_CVT_SCALE1__MASK 0xffff0000
+#define CNA_CVT_CON2_CVT_SCALE1__SHIFT 16
+static inline uint32_t CNA_CVT_CON2_CVT_SCALE1(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON2_CVT_SCALE1__SHIFT) & CNA_CVT_CON2_CVT_SCALE1__MASK;
+}
+#define CNA_CVT_CON2_CVT_OFFSET1__MASK 0x0000ffff
+#define CNA_CVT_CON2_CVT_OFFSET1__SHIFT 0
+static inline uint32_t CNA_CVT_CON2_CVT_OFFSET1(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON2_CVT_OFFSET1__SHIFT) & CNA_CVT_CON2_CVT_OFFSET1__MASK;
+}
+
+#define REG_CNA_CVT_CON3 0x00001058
+#define CNA_CVT_CON3_CVT_SCALE2__MASK 0xffff0000
+#define CNA_CVT_CON3_CVT_SCALE2__SHIFT 16
+static inline uint32_t CNA_CVT_CON3_CVT_SCALE2(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON3_CVT_SCALE2__SHIFT) & CNA_CVT_CON3_CVT_SCALE2__MASK;
+}
+#define CNA_CVT_CON3_CVT_OFFSET2__MASK 0x0000ffff
+#define CNA_CVT_CON3_CVT_OFFSET2__SHIFT 0
+static inline uint32_t CNA_CVT_CON3_CVT_OFFSET2(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON3_CVT_OFFSET2__SHIFT) & CNA_CVT_CON3_CVT_OFFSET2__MASK;
+}
+
+#define REG_CNA_CVT_CON4 0x0000105c
+#define CNA_CVT_CON4_CVT_SCALE3__MASK 0xffff0000
+#define CNA_CVT_CON4_CVT_SCALE3__SHIFT 16
+static inline uint32_t CNA_CVT_CON4_CVT_SCALE3(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON4_CVT_SCALE3__SHIFT) & CNA_CVT_CON4_CVT_SCALE3__MASK;
+}
+#define CNA_CVT_CON4_CVT_OFFSET3__MASK 0x0000ffff
+#define CNA_CVT_CON4_CVT_OFFSET3__SHIFT 0
+static inline uint32_t CNA_CVT_CON4_CVT_OFFSET3(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON4_CVT_OFFSET3__SHIFT) & CNA_CVT_CON4_CVT_OFFSET3__MASK;
+}
+
+#define REG_CNA_FC_CON0 0x00001060
+#define CNA_FC_CON0_FC_SKIP_DATA__MASK 0xffff0000
+#define CNA_FC_CON0_FC_SKIP_DATA__SHIFT 16
+static inline uint32_t CNA_FC_CON0_FC_SKIP_DATA(uint32_t val)
+{
+ return ((val) << CNA_FC_CON0_FC_SKIP_DATA__SHIFT) & CNA_FC_CON0_FC_SKIP_DATA__MASK;
+}
+#define CNA_FC_CON0_RESERVED_0__MASK 0x0000fffe
+#define CNA_FC_CON0_RESERVED_0__SHIFT 1
+static inline uint32_t CNA_FC_CON0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_FC_CON0_RESERVED_0__SHIFT) & CNA_FC_CON0_RESERVED_0__MASK;
+}
+#define CNA_FC_CON0_FC_SKIP_EN__MASK 0x00000001
+#define CNA_FC_CON0_FC_SKIP_EN__SHIFT 0
+static inline uint32_t CNA_FC_CON0_FC_SKIP_EN(uint32_t val)
+{
+ return ((val) << CNA_FC_CON0_FC_SKIP_EN__SHIFT) & CNA_FC_CON0_FC_SKIP_EN__MASK;
+}
+
+#define REG_CNA_FC_CON1 0x00001064
+#define CNA_FC_CON1_RESERVED_0__MASK 0xfffe0000
+#define CNA_FC_CON1_RESERVED_0__SHIFT 17
+static inline uint32_t CNA_FC_CON1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_FC_CON1_RESERVED_0__SHIFT) & CNA_FC_CON1_RESERVED_0__MASK;
+}
+#define CNA_FC_CON1_DATA_OFFSET__MASK 0x0001ffff
+#define CNA_FC_CON1_DATA_OFFSET__SHIFT 0
+static inline uint32_t CNA_FC_CON1_DATA_OFFSET(uint32_t val)
+{
+ return ((val) << CNA_FC_CON1_DATA_OFFSET__SHIFT) & CNA_FC_CON1_DATA_OFFSET__MASK;
+}
+
+#define REG_CNA_PAD_CON0 0x00001068
+#define CNA_PAD_CON0_RESERVED_0__MASK 0xffffff00
+#define CNA_PAD_CON0_RESERVED_0__SHIFT 8
+static inline uint32_t CNA_PAD_CON0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_PAD_CON0_RESERVED_0__SHIFT) & CNA_PAD_CON0_RESERVED_0__MASK;
+}
+#define CNA_PAD_CON0_PAD_LEFT__MASK 0x000000f0
+#define CNA_PAD_CON0_PAD_LEFT__SHIFT 4
+static inline uint32_t CNA_PAD_CON0_PAD_LEFT(uint32_t val)
+{
+ return ((val) << CNA_PAD_CON0_PAD_LEFT__SHIFT) & CNA_PAD_CON0_PAD_LEFT__MASK;
+}
+#define CNA_PAD_CON0_PAD_TOP__MASK 0x0000000f
+#define CNA_PAD_CON0_PAD_TOP__SHIFT 0
+static inline uint32_t CNA_PAD_CON0_PAD_TOP(uint32_t val)
+{
+ return ((val) << CNA_PAD_CON0_PAD_TOP__SHIFT) & CNA_PAD_CON0_PAD_TOP__MASK;
+}
+
+#define REG_CNA_FEATURE_DATA_ADDR 0x00001070
+#define CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__MASK 0xffffffff
+#define CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__SHIFT 0
+static inline uint32_t CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR(uint32_t val)
+{
+ return ((val) << CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__SHIFT) & CNA_FEATURE_DATA_ADDR_FEATURE_BASE_ADDR__MASK;
+}
+
+#define REG_CNA_FC_CON2 0x00001074
+#define CNA_FC_CON2_RESERVED_0__MASK 0xfffe0000
+#define CNA_FC_CON2_RESERVED_0__SHIFT 17
+static inline uint32_t CNA_FC_CON2_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_FC_CON2_RESERVED_0__SHIFT) & CNA_FC_CON2_RESERVED_0__MASK;
+}
+#define CNA_FC_CON2_WEIGHT_OFFSET__MASK 0x0001ffff
+#define CNA_FC_CON2_WEIGHT_OFFSET__SHIFT 0
+static inline uint32_t CNA_FC_CON2_WEIGHT_OFFSET(uint32_t val)
+{
+ return ((val) << CNA_FC_CON2_WEIGHT_OFFSET__SHIFT) & CNA_FC_CON2_WEIGHT_OFFSET__MASK;
+}
+
+#define REG_CNA_DMA_CON0 0x00001078
+#define CNA_DMA_CON0_OV4K_BYPASS__MASK 0x80000000
+#define CNA_DMA_CON0_OV4K_BYPASS__SHIFT 31
+static inline uint32_t CNA_DMA_CON0_OV4K_BYPASS(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON0_OV4K_BYPASS__SHIFT) & CNA_DMA_CON0_OV4K_BYPASS__MASK;
+}
+#define CNA_DMA_CON0_RESERVED_0__MASK 0x7ff00000
+#define CNA_DMA_CON0_RESERVED_0__SHIFT 20
+static inline uint32_t CNA_DMA_CON0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON0_RESERVED_0__SHIFT) & CNA_DMA_CON0_RESERVED_0__MASK;
+}
+#define CNA_DMA_CON0_WEIGHT_BURST_LEN__MASK 0x000f0000
+#define CNA_DMA_CON0_WEIGHT_BURST_LEN__SHIFT 16
+static inline uint32_t CNA_DMA_CON0_WEIGHT_BURST_LEN(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON0_WEIGHT_BURST_LEN__SHIFT) & CNA_DMA_CON0_WEIGHT_BURST_LEN__MASK;
+}
+#define CNA_DMA_CON0_RESERVED_1__MASK 0x0000fff0
+#define CNA_DMA_CON0_RESERVED_1__SHIFT 4
+static inline uint32_t CNA_DMA_CON0_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON0_RESERVED_1__SHIFT) & CNA_DMA_CON0_RESERVED_1__MASK;
+}
+#define CNA_DMA_CON0_DATA_BURST_LEN__MASK 0x0000000f
+#define CNA_DMA_CON0_DATA_BURST_LEN__SHIFT 0
+static inline uint32_t CNA_DMA_CON0_DATA_BURST_LEN(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON0_DATA_BURST_LEN__SHIFT) & CNA_DMA_CON0_DATA_BURST_LEN__MASK;
+}
+
+#define REG_CNA_DMA_CON1 0x0000107c
+#define CNA_DMA_CON1_RESERVED_0__MASK 0xf0000000
+#define CNA_DMA_CON1_RESERVED_0__SHIFT 28
+static inline uint32_t CNA_DMA_CON1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON1_RESERVED_0__SHIFT) & CNA_DMA_CON1_RESERVED_0__MASK;
+}
+#define CNA_DMA_CON1_LINE_STRIDE__MASK 0x0fffffff
+#define CNA_DMA_CON1_LINE_STRIDE__SHIFT 0
+static inline uint32_t CNA_DMA_CON1_LINE_STRIDE(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON1_LINE_STRIDE__SHIFT) & CNA_DMA_CON1_LINE_STRIDE__MASK;
+}
+
+#define REG_CNA_DMA_CON2 0x00001080
+#define CNA_DMA_CON2_RESERVED_0__MASK 0xf0000000
+#define CNA_DMA_CON2_RESERVED_0__SHIFT 28
+static inline uint32_t CNA_DMA_CON2_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON2_RESERVED_0__SHIFT) & CNA_DMA_CON2_RESERVED_0__MASK;
+}
+#define CNA_DMA_CON2_SURF_STRIDE__MASK 0x0fffffff
+#define CNA_DMA_CON2_SURF_STRIDE__SHIFT 0
+static inline uint32_t CNA_DMA_CON2_SURF_STRIDE(uint32_t val)
+{
+ return ((val) << CNA_DMA_CON2_SURF_STRIDE__SHIFT) & CNA_DMA_CON2_SURF_STRIDE__MASK;
+}
+
+#define REG_CNA_FC_DATA_SIZE0 0x00001084
+#define CNA_FC_DATA_SIZE0_RESERVED_0__MASK 0xc0000000
+#define CNA_FC_DATA_SIZE0_RESERVED_0__SHIFT 30
+static inline uint32_t CNA_FC_DATA_SIZE0_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_FC_DATA_SIZE0_RESERVED_0__SHIFT) & CNA_FC_DATA_SIZE0_RESERVED_0__MASK;
+}
+#define CNA_FC_DATA_SIZE0_DMA_WIDTH__MASK 0x3fff0000
+#define CNA_FC_DATA_SIZE0_DMA_WIDTH__SHIFT 16
+static inline uint32_t CNA_FC_DATA_SIZE0_DMA_WIDTH(uint32_t val)
+{
+ return ((val) << CNA_FC_DATA_SIZE0_DMA_WIDTH__SHIFT) & CNA_FC_DATA_SIZE0_DMA_WIDTH__MASK;
+}
+#define CNA_FC_DATA_SIZE0_RESERVED_1__MASK 0x0000f800
+#define CNA_FC_DATA_SIZE0_RESERVED_1__SHIFT 11
+static inline uint32_t CNA_FC_DATA_SIZE0_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_FC_DATA_SIZE0_RESERVED_1__SHIFT) & CNA_FC_DATA_SIZE0_RESERVED_1__MASK;
+}
+#define CNA_FC_DATA_SIZE0_DMA_HEIGHT__MASK 0x000007ff
+#define CNA_FC_DATA_SIZE0_DMA_HEIGHT__SHIFT 0
+static inline uint32_t CNA_FC_DATA_SIZE0_DMA_HEIGHT(uint32_t val)
+{
+ return ((val) << CNA_FC_DATA_SIZE0_DMA_HEIGHT__SHIFT) & CNA_FC_DATA_SIZE0_DMA_HEIGHT__MASK;
+}
+
+#define REG_CNA_FC_DATA_SIZE1 0x00001088
+#define CNA_FC_DATA_SIZE1_RESERVED_0__MASK 0xffff0000
+#define CNA_FC_DATA_SIZE1_RESERVED_0__SHIFT 16
+static inline uint32_t CNA_FC_DATA_SIZE1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_FC_DATA_SIZE1_RESERVED_0__SHIFT) & CNA_FC_DATA_SIZE1_RESERVED_0__MASK;
+}
+#define CNA_FC_DATA_SIZE1_DMA_CHANNEL__MASK 0x0000ffff
+#define CNA_FC_DATA_SIZE1_DMA_CHANNEL__SHIFT 0
+static inline uint32_t CNA_FC_DATA_SIZE1_DMA_CHANNEL(uint32_t val)
+{
+ return ((val) << CNA_FC_DATA_SIZE1_DMA_CHANNEL__SHIFT) & CNA_FC_DATA_SIZE1_DMA_CHANNEL__MASK;
+}
+
+#define REG_CNA_CLK_GATE 0x00001090
+#define CNA_CLK_GATE_RESERVED_0__MASK 0xffffffe0
+#define CNA_CLK_GATE_RESERVED_0__SHIFT 5
+static inline uint32_t CNA_CLK_GATE_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_CLK_GATE_RESERVED_0__SHIFT) & CNA_CLK_GATE_RESERVED_0__MASK;
+}
+#define CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__MASK 0x00000010
+#define CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__SHIFT 4
+static inline uint32_t CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE(uint32_t val)
+{
+ return ((val) << CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CBUF_CS_DISABLE_CLKGATE__MASK;
+}
+#define CNA_CLK_GATE_RESERVED_1__MASK 0x00000008
+#define CNA_CLK_GATE_RESERVED_1__SHIFT 3
+static inline uint32_t CNA_CLK_GATE_RESERVED_1(uint32_t val)
+{
+ return ((val) << CNA_CLK_GATE_RESERVED_1__SHIFT) & CNA_CLK_GATE_RESERVED_1__MASK;
+}
+#define CNA_CLK_GATE_CSC_DISABLE_CLKGATE__MASK 0x00000004
+#define CNA_CLK_GATE_CSC_DISABLE_CLKGATE__SHIFT 2
+static inline uint32_t CNA_CLK_GATE_CSC_DISABLE_CLKGATE(uint32_t val)
+{
+ return ((val) << CNA_CLK_GATE_CSC_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CSC_DISABLE_CLKGATE__MASK;
+}
+#define CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__MASK 0x00000002
+#define CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__SHIFT 1
+static inline uint32_t CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE(uint32_t val)
+{
+ return ((val) << CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CNA_WEIGHT_DISABLE_CLKGATE__MASK;
+}
+#define CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__MASK 0x00000001
+#define CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__SHIFT 0
+static inline uint32_t CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE(uint32_t val)
+{
+ return ((val) << CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__SHIFT) & CNA_CLK_GATE_CNA_FEATURE_DISABLE_CLKGATE__MASK;
+}
+
+#define REG_CNA_DCOMP_CTRL 0x00001100
+#define CNA_DCOMP_CTRL_RESERVED_0__MASK 0xfffffff0
+#define CNA_DCOMP_CTRL_RESERVED_0__SHIFT 4
+static inline uint32_t CNA_DCOMP_CTRL_RESERVED_0(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_CTRL_RESERVED_0__SHIFT) & CNA_DCOMP_CTRL_RESERVED_0__MASK;
+}
+#define CNA_DCOMP_CTRL_WT_DEC_BYPASS__MASK 0x00000008
+#define CNA_DCOMP_CTRL_WT_DEC_BYPASS__SHIFT 3
+static inline uint32_t CNA_DCOMP_CTRL_WT_DEC_BYPASS(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_CTRL_WT_DEC_BYPASS__SHIFT) & CNA_DCOMP_CTRL_WT_DEC_BYPASS__MASK;
+}
+#define CNA_DCOMP_CTRL_DECOMP_CONTROL__MASK 0x00000007
+#define CNA_DCOMP_CTRL_DECOMP_CONTROL__SHIFT 0
+static inline uint32_t CNA_DCOMP_CTRL_DECOMP_CONTROL(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_CTRL_DECOMP_CONTROL__SHIFT) & CNA_DCOMP_CTRL_DECOMP_CONTROL__MASK;
+}
+
+#define REG_CNA_DCOMP_REGNUM 0x00001104
+#define CNA_DCOMP_REGNUM_DCOMP_REGNUM__MASK 0xffffffff
+#define CNA_DCOMP_REGNUM_DCOMP_REGNUM__SHIFT 0
+static inline uint32_t CNA_DCOMP_REGNUM_DCOMP_REGNUM(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_REGNUM_DCOMP_REGNUM__SHIFT) & CNA_DCOMP_REGNUM_DCOMP_REGNUM__MASK;
+}
+
+#define REG_CNA_DCOMP_ADDR0 0x00001110
+#define CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__MASK 0xffffffff
+#define CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__SHIFT 0
+static inline uint32_t CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__SHIFT) & CNA_DCOMP_ADDR0_DECOMPRESS_ADDR0__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT0 0x00001140
+#define CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__SHIFT) & CNA_DCOMP_AMOUNT0_DCOMP_AMOUNT0__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT1 0x00001144
+#define CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__SHIFT) & CNA_DCOMP_AMOUNT1_DCOMP_AMOUNT1__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT2 0x00001148
+#define CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__SHIFT) & CNA_DCOMP_AMOUNT2_DCOMP_AMOUNT2__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT3 0x0000114c
+#define CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__SHIFT) & CNA_DCOMP_AMOUNT3_DCOMP_AMOUNT3__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT4 0x00001150
+#define CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__SHIFT) & CNA_DCOMP_AMOUNT4_DCOMP_AMOUNT4__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT5 0x00001154
+#define CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__SHIFT) & CNA_DCOMP_AMOUNT5_DCOMP_AMOUNT5__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT6 0x00001158
+#define CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__SHIFT) & CNA_DCOMP_AMOUNT6_DCOMP_AMOUNT6__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT7 0x0000115c
+#define CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__SHIFT) & CNA_DCOMP_AMOUNT7_DCOMP_AMOUNT7__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT8 0x00001160
+#define CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__SHIFT) & CNA_DCOMP_AMOUNT8_DCOMP_AMOUNT8__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT9 0x00001164
+#define CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__SHIFT) & CNA_DCOMP_AMOUNT9_DCOMP_AMOUNT9__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT10 0x00001168
+#define CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__SHIFT) & CNA_DCOMP_AMOUNT10_DCOMP_AMOUNT10__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT11 0x0000116c
+#define CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__SHIFT) & CNA_DCOMP_AMOUNT11_DCOMP_AMOUNT11__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT12 0x00001170
+#define CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__SHIFT) & CNA_DCOMP_AMOUNT12_DCOMP_AMOUNT12__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT13 0x00001174
+#define CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__SHIFT) & CNA_DCOMP_AMOUNT13_DCOMP_AMOUNT13__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT14 0x00001178
+#define CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__SHIFT) & CNA_DCOMP_AMOUNT14_DCOMP_AMOUNT14__MASK;
+}
+
+#define REG_CNA_DCOMP_AMOUNT15 0x0000117c
+#define CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__MASK 0xffffffff
+#define CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__SHIFT 0
+static inline uint32_t CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15(uint32_t val)
+{
+ return ((val) << CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__SHIFT) & CNA_DCOMP_AMOUNT15_DCOMP_AMOUNT15__MASK;
+}
+
+#define REG_CNA_CVT_CON5 0x00001180
+#define CNA_CVT_CON5_PER_CHANNEL_CVT_EN__MASK 0xffffffff
+#define CNA_CVT_CON5_PER_CHANNEL_CVT_EN__SHIFT 0
+static inline uint32_t CNA_CVT_CON5_PER_CHANNEL_CVT_EN(uint32_t val)
+{
+ return ((val) << CNA_CVT_CON5_PER_CHANNEL_CVT_EN__SHIFT) & CNA_CVT_CON5_PER_CHANNEL_CVT_EN__MASK;
+}
+
+#define REG_CNA_PAD_CON1 0x00001184
+#define CNA_PAD_CON1_PAD_VALUE__MASK 0xffffffff
+#define CNA_PAD_CON1_PAD_VALUE__SHIFT 0
+static inline uint32_t CNA_PAD_CON1_PAD_VALUE(uint32_t val)
+{
+ return ((val) << CNA_PAD_CON1_PAD_VALUE__SHIFT) & CNA_PAD_CON1_PAD_VALUE__MASK;
+}
+
+#define REG_CORE_S_STATUS 0x00003000
+#define CORE_S_STATUS_RESERVED_0__MASK 0xfffc0000
+#define CORE_S_STATUS_RESERVED_0__SHIFT 18
+static inline uint32_t CORE_S_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_S_STATUS_RESERVED_0__SHIFT) & CORE_S_STATUS_RESERVED_0__MASK;
+}
+#define CORE_S_STATUS_STATUS_1__MASK 0x00030000
+#define CORE_S_STATUS_STATUS_1__SHIFT 16
+static inline uint32_t CORE_S_STATUS_STATUS_1(uint32_t val)
+{
+ return ((val) << CORE_S_STATUS_STATUS_1__SHIFT) & CORE_S_STATUS_STATUS_1__MASK;
+}
+#define CORE_S_STATUS_RESERVED_1__MASK 0x0000fffc
+#define CORE_S_STATUS_RESERVED_1__SHIFT 2
+static inline uint32_t CORE_S_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << CORE_S_STATUS_RESERVED_1__SHIFT) & CORE_S_STATUS_RESERVED_1__MASK;
+}
+#define CORE_S_STATUS_STATUS_0__MASK 0x00000003
+#define CORE_S_STATUS_STATUS_0__SHIFT 0
+static inline uint32_t CORE_S_STATUS_STATUS_0(uint32_t val)
+{
+ return ((val) << CORE_S_STATUS_STATUS_0__SHIFT) & CORE_S_STATUS_STATUS_0__MASK;
+}
+
+#define REG_CORE_S_POINTER 0x00003004
+#define CORE_S_POINTER_RESERVED_0__MASK 0xfffe0000
+#define CORE_S_POINTER_RESERVED_0__SHIFT 17
+static inline uint32_t CORE_S_POINTER_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_RESERVED_0__SHIFT) & CORE_S_POINTER_RESERVED_0__MASK;
+}
+#define CORE_S_POINTER_EXECUTER__MASK 0x00010000
+#define CORE_S_POINTER_EXECUTER__SHIFT 16
+static inline uint32_t CORE_S_POINTER_EXECUTER(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_EXECUTER__SHIFT) & CORE_S_POINTER_EXECUTER__MASK;
+}
+#define CORE_S_POINTER_RESERVED_1__MASK 0x0000ffc0
+#define CORE_S_POINTER_RESERVED_1__SHIFT 6
+static inline uint32_t CORE_S_POINTER_RESERVED_1(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_RESERVED_1__SHIFT) & CORE_S_POINTER_RESERVED_1__MASK;
+}
+#define CORE_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020
+#define CORE_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5
+static inline uint32_t CORE_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & CORE_S_POINTER_EXECUTER_PP_CLEAR__MASK;
+}
+#define CORE_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010
+#define CORE_S_POINTER_POINTER_PP_CLEAR__SHIFT 4
+static inline uint32_t CORE_S_POINTER_POINTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_POINTER_PP_CLEAR__SHIFT) & CORE_S_POINTER_POINTER_PP_CLEAR__MASK;
+}
+#define CORE_S_POINTER_POINTER_PP_MODE__MASK 0x00000008
+#define CORE_S_POINTER_POINTER_PP_MODE__SHIFT 3
+static inline uint32_t CORE_S_POINTER_POINTER_PP_MODE(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_POINTER_PP_MODE__SHIFT) & CORE_S_POINTER_POINTER_PP_MODE__MASK;
+}
+#define CORE_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004
+#define CORE_S_POINTER_EXECUTER_PP_EN__SHIFT 2
+static inline uint32_t CORE_S_POINTER_EXECUTER_PP_EN(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_EXECUTER_PP_EN__SHIFT) & CORE_S_POINTER_EXECUTER_PP_EN__MASK;
+}
+#define CORE_S_POINTER_POINTER_PP_EN__MASK 0x00000002
+#define CORE_S_POINTER_POINTER_PP_EN__SHIFT 1
+static inline uint32_t CORE_S_POINTER_POINTER_PP_EN(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_POINTER_PP_EN__SHIFT) & CORE_S_POINTER_POINTER_PP_EN__MASK;
+}
+#define CORE_S_POINTER_POINTER__MASK 0x00000001
+#define CORE_S_POINTER_POINTER__SHIFT 0
+static inline uint32_t CORE_S_POINTER_POINTER(uint32_t val)
+{
+ return ((val) << CORE_S_POINTER_POINTER__SHIFT) & CORE_S_POINTER_POINTER__MASK;
+}
+
+#define REG_CORE_OPERATION_ENABLE 0x00003008
+#define CORE_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define CORE_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t CORE_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_OPERATION_ENABLE_RESERVED_0__SHIFT) & CORE_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define CORE_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define CORE_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t CORE_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << CORE_OPERATION_ENABLE_OP_EN__SHIFT) & CORE_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_CORE_MAC_GATING 0x0000300c
+#define CORE_MAC_GATING_RESERVED_0__MASK 0xf8000000
+#define CORE_MAC_GATING_RESERVED_0__SHIFT 27
+static inline uint32_t CORE_MAC_GATING_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_MAC_GATING_RESERVED_0__SHIFT) & CORE_MAC_GATING_RESERVED_0__MASK;
+}
+#define CORE_MAC_GATING_SLCG_OP_EN__MASK 0x07ffffff
+#define CORE_MAC_GATING_SLCG_OP_EN__SHIFT 0
+static inline uint32_t CORE_MAC_GATING_SLCG_OP_EN(uint32_t val)
+{
+ return ((val) << CORE_MAC_GATING_SLCG_OP_EN__SHIFT) & CORE_MAC_GATING_SLCG_OP_EN__MASK;
+}
+
+#define REG_CORE_MISC_CFG 0x00003010
+#define CORE_MISC_CFG_RESERVED_0__MASK 0xfff00000
+#define CORE_MISC_CFG_RESERVED_0__SHIFT 20
+static inline uint32_t CORE_MISC_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_RESERVED_0__SHIFT) & CORE_MISC_CFG_RESERVED_0__MASK;
+}
+#define CORE_MISC_CFG_SOFT_GATING__MASK 0x000fc000
+#define CORE_MISC_CFG_SOFT_GATING__SHIFT 14
+static inline uint32_t CORE_MISC_CFG_SOFT_GATING(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_SOFT_GATING__SHIFT) & CORE_MISC_CFG_SOFT_GATING__MASK;
+}
+#define CORE_MISC_CFG_RESERVED_1__MASK 0x00003800
+#define CORE_MISC_CFG_RESERVED_1__SHIFT 11
+static inline uint32_t CORE_MISC_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_RESERVED_1__SHIFT) & CORE_MISC_CFG_RESERVED_1__MASK;
+}
+#define CORE_MISC_CFG_PROC_PRECISION__MASK 0x00000700
+#define CORE_MISC_CFG_PROC_PRECISION__SHIFT 8
+static inline uint32_t CORE_MISC_CFG_PROC_PRECISION(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_PROC_PRECISION__SHIFT) & CORE_MISC_CFG_PROC_PRECISION__MASK;
+}
+#define CORE_MISC_CFG_RESERVED_2__MASK 0x000000fc
+#define CORE_MISC_CFG_RESERVED_2__SHIFT 2
+static inline uint32_t CORE_MISC_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_RESERVED_2__SHIFT) & CORE_MISC_CFG_RESERVED_2__MASK;
+}
+#define CORE_MISC_CFG_DW_EN__MASK 0x00000002
+#define CORE_MISC_CFG_DW_EN__SHIFT 1
+static inline uint32_t CORE_MISC_CFG_DW_EN(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_DW_EN__SHIFT) & CORE_MISC_CFG_DW_EN__MASK;
+}
+#define CORE_MISC_CFG_QD_EN__MASK 0x00000001
+#define CORE_MISC_CFG_QD_EN__SHIFT 0
+static inline uint32_t CORE_MISC_CFG_QD_EN(uint32_t val)
+{
+ return ((val) << CORE_MISC_CFG_QD_EN__SHIFT) & CORE_MISC_CFG_QD_EN__MASK;
+}
+
+#define REG_CORE_DATAOUT_SIZE_0 0x00003014
+#define CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__MASK 0xffff0000
+#define CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__SHIFT 16
+static inline uint32_t CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT(uint32_t val)
+{
+ return ((val) << CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__SHIFT) & CORE_DATAOUT_SIZE_0_DATAOUT_HEIGHT__MASK;
+}
+#define CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__MASK 0x0000ffff
+#define CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__SHIFT 0
+static inline uint32_t CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH(uint32_t val)
+{
+ return ((val) << CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__SHIFT) & CORE_DATAOUT_SIZE_0_DATAOUT_WIDTH__MASK;
+}
+
+#define REG_CORE_DATAOUT_SIZE_1 0x00003018
+#define CORE_DATAOUT_SIZE_1_RESERVED_0__MASK 0xffff0000
+#define CORE_DATAOUT_SIZE_1_RESERVED_0__SHIFT 16
+static inline uint32_t CORE_DATAOUT_SIZE_1_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_DATAOUT_SIZE_1_RESERVED_0__SHIFT) & CORE_DATAOUT_SIZE_1_RESERVED_0__MASK;
+}
+#define CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__MASK 0x0000ffff
+#define CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__SHIFT 0
+static inline uint32_t CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL(uint32_t val)
+{
+ return ((val) << CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__SHIFT) & CORE_DATAOUT_SIZE_1_DATAOUT_CHANNEL__MASK;
+}
+
+#define REG_CORE_CLIP_TRUNCATE 0x0000301c
+#define CORE_CLIP_TRUNCATE_RESERVED_0__MASK 0xffffff80
+#define CORE_CLIP_TRUNCATE_RESERVED_0__SHIFT 7
+static inline uint32_t CORE_CLIP_TRUNCATE_RESERVED_0(uint32_t val)
+{
+ return ((val) << CORE_CLIP_TRUNCATE_RESERVED_0__SHIFT) & CORE_CLIP_TRUNCATE_RESERVED_0__MASK;
+}
+#define CORE_CLIP_TRUNCATE_ROUND_TYPE__MASK 0x00000040
+#define CORE_CLIP_TRUNCATE_ROUND_TYPE__SHIFT 6
+static inline uint32_t CORE_CLIP_TRUNCATE_ROUND_TYPE(uint32_t val)
+{
+ return ((val) << CORE_CLIP_TRUNCATE_ROUND_TYPE__SHIFT) & CORE_CLIP_TRUNCATE_ROUND_TYPE__MASK;
+}
+#define CORE_CLIP_TRUNCATE_RESERVED_1__MASK 0x00000020
+#define CORE_CLIP_TRUNCATE_RESERVED_1__SHIFT 5
+static inline uint32_t CORE_CLIP_TRUNCATE_RESERVED_1(uint32_t val)
+{
+ return ((val) << CORE_CLIP_TRUNCATE_RESERVED_1__SHIFT) & CORE_CLIP_TRUNCATE_RESERVED_1__MASK;
+}
+#define CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__MASK 0x0000001f
+#define CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__SHIFT 0
+static inline uint32_t CORE_CLIP_TRUNCATE_CLIP_TRUNCATE(uint32_t val)
+{
+ return ((val) << CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__SHIFT) & CORE_CLIP_TRUNCATE_CLIP_TRUNCATE__MASK;
+}
+
+#define REG_DPU_S_STATUS 0x00004000
+#define DPU_S_STATUS_RESERVED_0__MASK 0xfffc0000
+#define DPU_S_STATUS_RESERVED_0__SHIFT 18
+static inline uint32_t DPU_S_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_S_STATUS_RESERVED_0__SHIFT) & DPU_S_STATUS_RESERVED_0__MASK;
+}
+#define DPU_S_STATUS_STATUS_1__MASK 0x00030000
+#define DPU_S_STATUS_STATUS_1__SHIFT 16
+static inline uint32_t DPU_S_STATUS_STATUS_1(uint32_t val)
+{
+ return ((val) << DPU_S_STATUS_STATUS_1__SHIFT) & DPU_S_STATUS_STATUS_1__MASK;
+}
+#define DPU_S_STATUS_RESERVED_1__MASK 0x0000fffc
+#define DPU_S_STATUS_RESERVED_1__SHIFT 2
+static inline uint32_t DPU_S_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_S_STATUS_RESERVED_1__SHIFT) & DPU_S_STATUS_RESERVED_1__MASK;
+}
+#define DPU_S_STATUS_STATUS_0__MASK 0x00000003
+#define DPU_S_STATUS_STATUS_0__SHIFT 0
+static inline uint32_t DPU_S_STATUS_STATUS_0(uint32_t val)
+{
+ return ((val) << DPU_S_STATUS_STATUS_0__SHIFT) & DPU_S_STATUS_STATUS_0__MASK;
+}
+
+#define REG_DPU_S_POINTER 0x00004004
+#define DPU_S_POINTER_RESERVED_0__MASK 0xfffe0000
+#define DPU_S_POINTER_RESERVED_0__SHIFT 17
+static inline uint32_t DPU_S_POINTER_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_RESERVED_0__SHIFT) & DPU_S_POINTER_RESERVED_0__MASK;
+}
+#define DPU_S_POINTER_EXECUTER__MASK 0x00010000
+#define DPU_S_POINTER_EXECUTER__SHIFT 16
+static inline uint32_t DPU_S_POINTER_EXECUTER(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_EXECUTER__SHIFT) & DPU_S_POINTER_EXECUTER__MASK;
+}
+#define DPU_S_POINTER_RESERVED_1__MASK 0x0000ffc0
+#define DPU_S_POINTER_RESERVED_1__SHIFT 6
+static inline uint32_t DPU_S_POINTER_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_RESERVED_1__SHIFT) & DPU_S_POINTER_RESERVED_1__MASK;
+}
+#define DPU_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020
+#define DPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5
+static inline uint32_t DPU_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & DPU_S_POINTER_EXECUTER_PP_CLEAR__MASK;
+}
+#define DPU_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010
+#define DPU_S_POINTER_POINTER_PP_CLEAR__SHIFT 4
+static inline uint32_t DPU_S_POINTER_POINTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_POINTER_PP_CLEAR__SHIFT) & DPU_S_POINTER_POINTER_PP_CLEAR__MASK;
+}
+#define DPU_S_POINTER_POINTER_PP_MODE__MASK 0x00000008
+#define DPU_S_POINTER_POINTER_PP_MODE__SHIFT 3
+static inline uint32_t DPU_S_POINTER_POINTER_PP_MODE(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_POINTER_PP_MODE__SHIFT) & DPU_S_POINTER_POINTER_PP_MODE__MASK;
+}
+#define DPU_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004
+#define DPU_S_POINTER_EXECUTER_PP_EN__SHIFT 2
+static inline uint32_t DPU_S_POINTER_EXECUTER_PP_EN(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_EXECUTER_PP_EN__SHIFT) & DPU_S_POINTER_EXECUTER_PP_EN__MASK;
+}
+#define DPU_S_POINTER_POINTER_PP_EN__MASK 0x00000002
+#define DPU_S_POINTER_POINTER_PP_EN__SHIFT 1
+static inline uint32_t DPU_S_POINTER_POINTER_PP_EN(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_POINTER_PP_EN__SHIFT) & DPU_S_POINTER_POINTER_PP_EN__MASK;
+}
+#define DPU_S_POINTER_POINTER__MASK 0x00000001
+#define DPU_S_POINTER_POINTER__SHIFT 0
+static inline uint32_t DPU_S_POINTER_POINTER(uint32_t val)
+{
+ return ((val) << DPU_S_POINTER_POINTER__SHIFT) & DPU_S_POINTER_POINTER__MASK;
+}
+
+#define REG_DPU_OPERATION_ENABLE 0x00004008
+#define DPU_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define DPU_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t DPU_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_OPERATION_ENABLE_RESERVED_0__SHIFT) & DPU_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define DPU_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define DPU_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t DPU_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << DPU_OPERATION_ENABLE_OP_EN__SHIFT) & DPU_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_DPU_FEATURE_MODE_CFG 0x0000400c
+#define DPU_FEATURE_MODE_CFG_COMB_USE__MASK 0x80000000
+#define DPU_FEATURE_MODE_CFG_COMB_USE__SHIFT 31
+static inline uint32_t DPU_FEATURE_MODE_CFG_COMB_USE(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_COMB_USE__SHIFT) & DPU_FEATURE_MODE_CFG_COMB_USE__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_TP_EN__MASK 0x40000000
+#define DPU_FEATURE_MODE_CFG_TP_EN__SHIFT 30
+static inline uint32_t DPU_FEATURE_MODE_CFG_TP_EN(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_TP_EN__SHIFT) & DPU_FEATURE_MODE_CFG_TP_EN__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_RGP_TYPE__MASK 0x3c000000
+#define DPU_FEATURE_MODE_CFG_RGP_TYPE__SHIFT 26
+static inline uint32_t DPU_FEATURE_MODE_CFG_RGP_TYPE(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_RGP_TYPE__SHIFT) & DPU_FEATURE_MODE_CFG_RGP_TYPE__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_NONALIGN__MASK 0x02000000
+#define DPU_FEATURE_MODE_CFG_NONALIGN__SHIFT 25
+static inline uint32_t DPU_FEATURE_MODE_CFG_NONALIGN(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_NONALIGN__SHIFT) & DPU_FEATURE_MODE_CFG_NONALIGN__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_SURF_LEN__MASK 0x01fffe00
+#define DPU_FEATURE_MODE_CFG_SURF_LEN__SHIFT 9
+static inline uint32_t DPU_FEATURE_MODE_CFG_SURF_LEN(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_SURF_LEN__SHIFT) & DPU_FEATURE_MODE_CFG_SURF_LEN__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_BURST_LEN__MASK 0x000001e0
+#define DPU_FEATURE_MODE_CFG_BURST_LEN__SHIFT 5
+static inline uint32_t DPU_FEATURE_MODE_CFG_BURST_LEN(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_BURST_LEN__SHIFT) & DPU_FEATURE_MODE_CFG_BURST_LEN__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_CONV_MODE__MASK 0x00000018
+#define DPU_FEATURE_MODE_CFG_CONV_MODE__SHIFT 3
+static inline uint32_t DPU_FEATURE_MODE_CFG_CONV_MODE(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_CONV_MODE__SHIFT) & DPU_FEATURE_MODE_CFG_CONV_MODE__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_OUTPUT_MODE__MASK 0x00000006
+#define DPU_FEATURE_MODE_CFG_OUTPUT_MODE__SHIFT 1
+static inline uint32_t DPU_FEATURE_MODE_CFG_OUTPUT_MODE(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_OUTPUT_MODE__SHIFT) & DPU_FEATURE_MODE_CFG_OUTPUT_MODE__MASK;
+}
+#define DPU_FEATURE_MODE_CFG_FLYING_MODE__MASK 0x00000001
+#define DPU_FEATURE_MODE_CFG_FLYING_MODE__SHIFT 0
+static inline uint32_t DPU_FEATURE_MODE_CFG_FLYING_MODE(uint32_t val)
+{
+ return ((val) << DPU_FEATURE_MODE_CFG_FLYING_MODE__SHIFT) & DPU_FEATURE_MODE_CFG_FLYING_MODE__MASK;
+}
+
+#define REG_DPU_DATA_FORMAT 0x00004010
+#define DPU_DATA_FORMAT_OUT_PRECISION__MASK 0xe0000000
+#define DPU_DATA_FORMAT_OUT_PRECISION__SHIFT 29
+static inline uint32_t DPU_DATA_FORMAT_OUT_PRECISION(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_OUT_PRECISION__SHIFT) & DPU_DATA_FORMAT_OUT_PRECISION__MASK;
+}
+#define DPU_DATA_FORMAT_IN_PRECISION__MASK 0x1c000000
+#define DPU_DATA_FORMAT_IN_PRECISION__SHIFT 26
+static inline uint32_t DPU_DATA_FORMAT_IN_PRECISION(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_IN_PRECISION__SHIFT) & DPU_DATA_FORMAT_IN_PRECISION__MASK;
+}
+#define DPU_DATA_FORMAT_EW_TRUNCATE_NEG__MASK 0x03ff0000
+#define DPU_DATA_FORMAT_EW_TRUNCATE_NEG__SHIFT 16
+static inline uint32_t DPU_DATA_FORMAT_EW_TRUNCATE_NEG(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_EW_TRUNCATE_NEG__SHIFT) & DPU_DATA_FORMAT_EW_TRUNCATE_NEG__MASK;
+}
+#define DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__MASK 0x0000fc00
+#define DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__SHIFT 10
+static inline uint32_t DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__SHIFT) & DPU_DATA_FORMAT_BN_MUL_SHIFT_VALUE_NEG__MASK;
+}
+#define DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__MASK 0x000003f0
+#define DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__SHIFT 4
+static inline uint32_t DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__SHIFT) & DPU_DATA_FORMAT_BS_MUL_SHIFT_VALUE_NEG__MASK;
+}
+#define DPU_DATA_FORMAT_MC_SURF_OUT__MASK 0x00000008
+#define DPU_DATA_FORMAT_MC_SURF_OUT__SHIFT 3
+static inline uint32_t DPU_DATA_FORMAT_MC_SURF_OUT(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_MC_SURF_OUT__SHIFT) & DPU_DATA_FORMAT_MC_SURF_OUT__MASK;
+}
+#define DPU_DATA_FORMAT_PROC_PRECISION__MASK 0x00000007
+#define DPU_DATA_FORMAT_PROC_PRECISION__SHIFT 0
+static inline uint32_t DPU_DATA_FORMAT_PROC_PRECISION(uint32_t val)
+{
+ return ((val) << DPU_DATA_FORMAT_PROC_PRECISION__SHIFT) & DPU_DATA_FORMAT_PROC_PRECISION__MASK;
+}
+
+#define REG_DPU_OFFSET_PEND 0x00004014
+#define DPU_OFFSET_PEND_RESERVED_0__MASK 0xffff0000
+#define DPU_OFFSET_PEND_RESERVED_0__SHIFT 16
+static inline uint32_t DPU_OFFSET_PEND_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_OFFSET_PEND_RESERVED_0__SHIFT) & DPU_OFFSET_PEND_RESERVED_0__MASK;
+}
+#define DPU_OFFSET_PEND_OFFSET_PEND__MASK 0x0000ffff
+#define DPU_OFFSET_PEND_OFFSET_PEND__SHIFT 0
+static inline uint32_t DPU_OFFSET_PEND_OFFSET_PEND(uint32_t val)
+{
+ return ((val) << DPU_OFFSET_PEND_OFFSET_PEND__SHIFT) & DPU_OFFSET_PEND_OFFSET_PEND__MASK;
+}
+
+#define REG_DPU_DST_BASE_ADDR 0x00004020
+#define DPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK 0xffffffff
+#define DPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT 0
+static inline uint32_t DPU_DST_BASE_ADDR_DST_BASE_ADDR(uint32_t val)
+{
+ return ((val) << DPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT) & DPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK;
+}
+
+#define REG_DPU_DST_SURF_STRIDE 0x00004024
+#define DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK 0xfffffff0
+#define DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT 4
+static inline uint32_t DPU_DST_SURF_STRIDE_DST_SURF_STRIDE(uint32_t val)
+{
+ return ((val) << DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT) & DPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK;
+}
+#define DPU_DST_SURF_STRIDE_RESERVED_0__MASK 0x0000000f
+#define DPU_DST_SURF_STRIDE_RESERVED_0__SHIFT 0
+static inline uint32_t DPU_DST_SURF_STRIDE_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_DST_SURF_STRIDE_RESERVED_0__SHIFT) & DPU_DST_SURF_STRIDE_RESERVED_0__MASK;
+}
+
+#define REG_DPU_DATA_CUBE_WIDTH 0x00004030
+#define DPU_DATA_CUBE_WIDTH_RESERVED_0__MASK 0xffffe000
+#define DPU_DATA_CUBE_WIDTH_RESERVED_0__SHIFT 13
+static inline uint32_t DPU_DATA_CUBE_WIDTH_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_WIDTH_RESERVED_0__SHIFT) & DPU_DATA_CUBE_WIDTH_RESERVED_0__MASK;
+}
+#define DPU_DATA_CUBE_WIDTH_WIDTH__MASK 0x00001fff
+#define DPU_DATA_CUBE_WIDTH_WIDTH__SHIFT 0
+static inline uint32_t DPU_DATA_CUBE_WIDTH_WIDTH(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_WIDTH_WIDTH__SHIFT) & DPU_DATA_CUBE_WIDTH_WIDTH__MASK;
+}
+
+#define REG_DPU_DATA_CUBE_HEIGHT 0x00004034
+#define DPU_DATA_CUBE_HEIGHT_RESERVED_0__MASK 0xfe000000
+#define DPU_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT 25
+static inline uint32_t DPU_DATA_CUBE_HEIGHT_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT) & DPU_DATA_CUBE_HEIGHT_RESERVED_0__MASK;
+}
+#define DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__MASK 0x01c00000
+#define DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__SHIFT 22
+static inline uint32_t DPU_DATA_CUBE_HEIGHT_MINMAX_CTL(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__SHIFT) & DPU_DATA_CUBE_HEIGHT_MINMAX_CTL__MASK;
+}
+#define DPU_DATA_CUBE_HEIGHT_RESERVED_1__MASK 0x003fe000
+#define DPU_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT 13
+static inline uint32_t DPU_DATA_CUBE_HEIGHT_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT) & DPU_DATA_CUBE_HEIGHT_RESERVED_1__MASK;
+}
+#define DPU_DATA_CUBE_HEIGHT_HEIGHT__MASK 0x00001fff
+#define DPU_DATA_CUBE_HEIGHT_HEIGHT__SHIFT 0
+static inline uint32_t DPU_DATA_CUBE_HEIGHT_HEIGHT(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_HEIGHT_HEIGHT__SHIFT) & DPU_DATA_CUBE_HEIGHT_HEIGHT__MASK;
+}
+
+#define REG_DPU_DATA_CUBE_NOTCH_ADDR 0x00004038
+#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__MASK 0xe0000000
+#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__SHIFT 29
+static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_0__MASK;
+}
+#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__MASK 0x1fff0000
+#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__SHIFT 16
+static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_1__MASK;
+}
+#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__MASK 0x0000e000
+#define DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__SHIFT 13
+static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_RESERVED_1__MASK;
+}
+#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__MASK 0x00001fff
+#define DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__SHIFT 0
+static inline uint32_t DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__SHIFT) & DPU_DATA_CUBE_NOTCH_ADDR_NOTCH_ADDR_0__MASK;
+}
+
+#define REG_DPU_DATA_CUBE_CHANNEL 0x0000403c
+#define DPU_DATA_CUBE_CHANNEL_RESERVED_0__MASK 0xe0000000
+#define DPU_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT 29
+static inline uint32_t DPU_DATA_CUBE_CHANNEL_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT) & DPU_DATA_CUBE_CHANNEL_RESERVED_0__MASK;
+}
+#define DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__MASK 0x1fff0000
+#define DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__SHIFT 16
+static inline uint32_t DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__SHIFT) & DPU_DATA_CUBE_CHANNEL_ORIG_CHANNEL__MASK;
+}
+#define DPU_DATA_CUBE_CHANNEL_RESERVED_1__MASK 0x0000e000
+#define DPU_DATA_CUBE_CHANNEL_RESERVED_1__SHIFT 13
+static inline uint32_t DPU_DATA_CUBE_CHANNEL_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_CHANNEL_RESERVED_1__SHIFT) & DPU_DATA_CUBE_CHANNEL_RESERVED_1__MASK;
+}
+#define DPU_DATA_CUBE_CHANNEL_CHANNEL__MASK 0x00001fff
+#define DPU_DATA_CUBE_CHANNEL_CHANNEL__SHIFT 0
+static inline uint32_t DPU_DATA_CUBE_CHANNEL_CHANNEL(uint32_t val)
+{
+ return ((val) << DPU_DATA_CUBE_CHANNEL_CHANNEL__SHIFT) & DPU_DATA_CUBE_CHANNEL_CHANNEL__MASK;
+}
+
+#define REG_DPU_BS_CFG 0x00004040
+#define DPU_BS_CFG_RESERVED_0__MASK 0xfff00000
+#define DPU_BS_CFG_RESERVED_0__SHIFT 20
+static inline uint32_t DPU_BS_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_RESERVED_0__SHIFT) & DPU_BS_CFG_RESERVED_0__MASK;
+}
+#define DPU_BS_CFG_BS_ALU_ALGO__MASK 0x000f0000
+#define DPU_BS_CFG_BS_ALU_ALGO__SHIFT 16
+static inline uint32_t DPU_BS_CFG_BS_ALU_ALGO(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_ALU_ALGO__SHIFT) & DPU_BS_CFG_BS_ALU_ALGO__MASK;
+}
+#define DPU_BS_CFG_RESERVED_1__MASK 0x0000fe00
+#define DPU_BS_CFG_RESERVED_1__SHIFT 9
+static inline uint32_t DPU_BS_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_RESERVED_1__SHIFT) & DPU_BS_CFG_RESERVED_1__MASK;
+}
+#define DPU_BS_CFG_BS_ALU_SRC__MASK 0x00000100
+#define DPU_BS_CFG_BS_ALU_SRC__SHIFT 8
+static inline uint32_t DPU_BS_CFG_BS_ALU_SRC(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_ALU_SRC__SHIFT) & DPU_BS_CFG_BS_ALU_SRC__MASK;
+}
+#define DPU_BS_CFG_BS_RELUX_EN__MASK 0x00000080
+#define DPU_BS_CFG_BS_RELUX_EN__SHIFT 7
+static inline uint32_t DPU_BS_CFG_BS_RELUX_EN(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_RELUX_EN__SHIFT) & DPU_BS_CFG_BS_RELUX_EN__MASK;
+}
+#define DPU_BS_CFG_BS_RELU_BYPASS__MASK 0x00000040
+#define DPU_BS_CFG_BS_RELU_BYPASS__SHIFT 6
+static inline uint32_t DPU_BS_CFG_BS_RELU_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_RELU_BYPASS__SHIFT) & DPU_BS_CFG_BS_RELU_BYPASS__MASK;
+}
+#define DPU_BS_CFG_BS_MUL_PRELU__MASK 0x00000020
+#define DPU_BS_CFG_BS_MUL_PRELU__SHIFT 5
+static inline uint32_t DPU_BS_CFG_BS_MUL_PRELU(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_MUL_PRELU__SHIFT) & DPU_BS_CFG_BS_MUL_PRELU__MASK;
+}
+#define DPU_BS_CFG_BS_MUL_BYPASS__MASK 0x00000010
+#define DPU_BS_CFG_BS_MUL_BYPASS__SHIFT 4
+static inline uint32_t DPU_BS_CFG_BS_MUL_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_MUL_BYPASS__SHIFT) & DPU_BS_CFG_BS_MUL_BYPASS__MASK;
+}
+#define DPU_BS_CFG_RESERVED_2__MASK 0x0000000c
+#define DPU_BS_CFG_RESERVED_2__SHIFT 2
+static inline uint32_t DPU_BS_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_RESERVED_2__SHIFT) & DPU_BS_CFG_RESERVED_2__MASK;
+}
+#define DPU_BS_CFG_BS_ALU_BYPASS__MASK 0x00000002
+#define DPU_BS_CFG_BS_ALU_BYPASS__SHIFT 1
+static inline uint32_t DPU_BS_CFG_BS_ALU_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_ALU_BYPASS__SHIFT) & DPU_BS_CFG_BS_ALU_BYPASS__MASK;
+}
+#define DPU_BS_CFG_BS_BYPASS__MASK 0x00000001
+#define DPU_BS_CFG_BS_BYPASS__SHIFT 0
+static inline uint32_t DPU_BS_CFG_BS_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BS_CFG_BS_BYPASS__SHIFT) & DPU_BS_CFG_BS_BYPASS__MASK;
+}
+
+#define REG_DPU_BS_ALU_CFG 0x00004044
+#define DPU_BS_ALU_CFG_BS_ALU_OPERAND__MASK 0xffffffff
+#define DPU_BS_ALU_CFG_BS_ALU_OPERAND__SHIFT 0
+static inline uint32_t DPU_BS_ALU_CFG_BS_ALU_OPERAND(uint32_t val)
+{
+ return ((val) << DPU_BS_ALU_CFG_BS_ALU_OPERAND__SHIFT) & DPU_BS_ALU_CFG_BS_ALU_OPERAND__MASK;
+}
+
+#define REG_DPU_BS_MUL_CFG 0x00004048
+#define DPU_BS_MUL_CFG_BS_MUL_OPERAND__MASK 0xffff0000
+#define DPU_BS_MUL_CFG_BS_MUL_OPERAND__SHIFT 16
+static inline uint32_t DPU_BS_MUL_CFG_BS_MUL_OPERAND(uint32_t val)
+{
+ return ((val) << DPU_BS_MUL_CFG_BS_MUL_OPERAND__SHIFT) & DPU_BS_MUL_CFG_BS_MUL_OPERAND__MASK;
+}
+#define DPU_BS_MUL_CFG_RESERVED_0__MASK 0x0000c000
+#define DPU_BS_MUL_CFG_RESERVED_0__SHIFT 14
+static inline uint32_t DPU_BS_MUL_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_BS_MUL_CFG_RESERVED_0__SHIFT) & DPU_BS_MUL_CFG_RESERVED_0__MASK;
+}
+#define DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__MASK 0x00003f00
+#define DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__SHIFT 8
+static inline uint32_t DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE(uint32_t val)
+{
+ return ((val) << DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__SHIFT) & DPU_BS_MUL_CFG_BS_MUL_SHIFT_VALUE__MASK;
+}
+#define DPU_BS_MUL_CFG_RESERVED_1__MASK 0x000000fc
+#define DPU_BS_MUL_CFG_RESERVED_1__SHIFT 2
+static inline uint32_t DPU_BS_MUL_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_BS_MUL_CFG_RESERVED_1__SHIFT) & DPU_BS_MUL_CFG_RESERVED_1__MASK;
+}
+#define DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__MASK 0x00000002
+#define DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__SHIFT 1
+static inline uint32_t DPU_BS_MUL_CFG_BS_TRUNCATE_SRC(uint32_t val)
+{
+ return ((val) << DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__SHIFT) & DPU_BS_MUL_CFG_BS_TRUNCATE_SRC__MASK;
+}
+#define DPU_BS_MUL_CFG_BS_MUL_SRC__MASK 0x00000001
+#define DPU_BS_MUL_CFG_BS_MUL_SRC__SHIFT 0
+static inline uint32_t DPU_BS_MUL_CFG_BS_MUL_SRC(uint32_t val)
+{
+ return ((val) << DPU_BS_MUL_CFG_BS_MUL_SRC__SHIFT) & DPU_BS_MUL_CFG_BS_MUL_SRC__MASK;
+}
+
+#define REG_DPU_BS_RELUX_CMP_VALUE 0x0000404c
+#define DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__MASK 0xffffffff
+#define DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__SHIFT 0
+static inline uint32_t DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT(uint32_t val)
+{
+ return ((val) << DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__SHIFT) & DPU_BS_RELUX_CMP_VALUE_BS_RELUX_CMP_DAT__MASK;
+}
+
+#define REG_DPU_BS_OW_CFG 0x00004050
+#define DPU_BS_OW_CFG_RGP_CNTER__MASK 0xf0000000
+#define DPU_BS_OW_CFG_RGP_CNTER__SHIFT 28
+static inline uint32_t DPU_BS_OW_CFG_RGP_CNTER(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_RGP_CNTER__SHIFT) & DPU_BS_OW_CFG_RGP_CNTER__MASK;
+}
+#define DPU_BS_OW_CFG_TP_ORG_EN__MASK 0x08000000
+#define DPU_BS_OW_CFG_TP_ORG_EN__SHIFT 27
+static inline uint32_t DPU_BS_OW_CFG_TP_ORG_EN(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_TP_ORG_EN__SHIFT) & DPU_BS_OW_CFG_TP_ORG_EN__MASK;
+}
+#define DPU_BS_OW_CFG_RESERVED_0__MASK 0x07fff800
+#define DPU_BS_OW_CFG_RESERVED_0__SHIFT 11
+static inline uint32_t DPU_BS_OW_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_RESERVED_0__SHIFT) & DPU_BS_OW_CFG_RESERVED_0__MASK;
+}
+#define DPU_BS_OW_CFG_SIZE_E_2__MASK 0x00000700
+#define DPU_BS_OW_CFG_SIZE_E_2__SHIFT 8
+static inline uint32_t DPU_BS_OW_CFG_SIZE_E_2(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_SIZE_E_2__SHIFT) & DPU_BS_OW_CFG_SIZE_E_2__MASK;
+}
+#define DPU_BS_OW_CFG_SIZE_E_1__MASK 0x000000e0
+#define DPU_BS_OW_CFG_SIZE_E_1__SHIFT 5
+static inline uint32_t DPU_BS_OW_CFG_SIZE_E_1(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_SIZE_E_1__SHIFT) & DPU_BS_OW_CFG_SIZE_E_1__MASK;
+}
+#define DPU_BS_OW_CFG_SIZE_E_0__MASK 0x0000001c
+#define DPU_BS_OW_CFG_SIZE_E_0__SHIFT 2
+static inline uint32_t DPU_BS_OW_CFG_SIZE_E_0(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_SIZE_E_0__SHIFT) & DPU_BS_OW_CFG_SIZE_E_0__MASK;
+}
+#define DPU_BS_OW_CFG_OD_BYPASS__MASK 0x00000002
+#define DPU_BS_OW_CFG_OD_BYPASS__SHIFT 1
+static inline uint32_t DPU_BS_OW_CFG_OD_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_OD_BYPASS__SHIFT) & DPU_BS_OW_CFG_OD_BYPASS__MASK;
+}
+#define DPU_BS_OW_CFG_OW_SRC__MASK 0x00000001
+#define DPU_BS_OW_CFG_OW_SRC__SHIFT 0
+static inline uint32_t DPU_BS_OW_CFG_OW_SRC(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_CFG_OW_SRC__SHIFT) & DPU_BS_OW_CFG_OW_SRC__MASK;
+}
+
+#define REG_DPU_BS_OW_OP 0x00004054
+#define DPU_BS_OW_OP_RESERVED_0__MASK 0xffff0000
+#define DPU_BS_OW_OP_RESERVED_0__SHIFT 16
+static inline uint32_t DPU_BS_OW_OP_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_OP_RESERVED_0__SHIFT) & DPU_BS_OW_OP_RESERVED_0__MASK;
+}
+#define DPU_BS_OW_OP_OW_OP__MASK 0x0000ffff
+#define DPU_BS_OW_OP_OW_OP__SHIFT 0
+static inline uint32_t DPU_BS_OW_OP_OW_OP(uint32_t val)
+{
+ return ((val) << DPU_BS_OW_OP_OW_OP__SHIFT) & DPU_BS_OW_OP_OW_OP__MASK;
+}
+
+#define REG_DPU_WDMA_SIZE_0 0x00004058
+#define DPU_WDMA_SIZE_0_RESERVED_0__MASK 0xf0000000
+#define DPU_WDMA_SIZE_0_RESERVED_0__SHIFT 28
+static inline uint32_t DPU_WDMA_SIZE_0_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_0_RESERVED_0__SHIFT) & DPU_WDMA_SIZE_0_RESERVED_0__MASK;
+}
+#define DPU_WDMA_SIZE_0_TP_PRECISION__MASK 0x08000000
+#define DPU_WDMA_SIZE_0_TP_PRECISION__SHIFT 27
+static inline uint32_t DPU_WDMA_SIZE_0_TP_PRECISION(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_0_TP_PRECISION__SHIFT) & DPU_WDMA_SIZE_0_TP_PRECISION__MASK;
+}
+#define DPU_WDMA_SIZE_0_SIZE_C_WDMA__MASK 0x07ff0000
+#define DPU_WDMA_SIZE_0_SIZE_C_WDMA__SHIFT 16
+static inline uint32_t DPU_WDMA_SIZE_0_SIZE_C_WDMA(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_0_SIZE_C_WDMA__SHIFT) & DPU_WDMA_SIZE_0_SIZE_C_WDMA__MASK;
+}
+#define DPU_WDMA_SIZE_0_RESERVED_1__MASK 0x0000e000
+#define DPU_WDMA_SIZE_0_RESERVED_1__SHIFT 13
+static inline uint32_t DPU_WDMA_SIZE_0_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_0_RESERVED_1__SHIFT) & DPU_WDMA_SIZE_0_RESERVED_1__MASK;
+}
+#define DPU_WDMA_SIZE_0_CHANNEL_WDMA__MASK 0x00001fff
+#define DPU_WDMA_SIZE_0_CHANNEL_WDMA__SHIFT 0
+static inline uint32_t DPU_WDMA_SIZE_0_CHANNEL_WDMA(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_0_CHANNEL_WDMA__SHIFT) & DPU_WDMA_SIZE_0_CHANNEL_WDMA__MASK;
+}
+
+#define REG_DPU_WDMA_SIZE_1 0x0000405c
+#define DPU_WDMA_SIZE_1_RESERVED_0__MASK 0xe0000000
+#define DPU_WDMA_SIZE_1_RESERVED_0__SHIFT 29
+static inline uint32_t DPU_WDMA_SIZE_1_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_1_RESERVED_0__SHIFT) & DPU_WDMA_SIZE_1_RESERVED_0__MASK;
+}
+#define DPU_WDMA_SIZE_1_HEIGHT_WDMA__MASK 0x1fff0000
+#define DPU_WDMA_SIZE_1_HEIGHT_WDMA__SHIFT 16
+static inline uint32_t DPU_WDMA_SIZE_1_HEIGHT_WDMA(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_1_HEIGHT_WDMA__SHIFT) & DPU_WDMA_SIZE_1_HEIGHT_WDMA__MASK;
+}
+#define DPU_WDMA_SIZE_1_RESERVED_1__MASK 0x0000e000
+#define DPU_WDMA_SIZE_1_RESERVED_1__SHIFT 13
+static inline uint32_t DPU_WDMA_SIZE_1_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_1_RESERVED_1__SHIFT) & DPU_WDMA_SIZE_1_RESERVED_1__MASK;
+}
+#define DPU_WDMA_SIZE_1_WIDTH_WDMA__MASK 0x00001fff
+#define DPU_WDMA_SIZE_1_WIDTH_WDMA__SHIFT 0
+static inline uint32_t DPU_WDMA_SIZE_1_WIDTH_WDMA(uint32_t val)
+{
+ return ((val) << DPU_WDMA_SIZE_1_WIDTH_WDMA__SHIFT) & DPU_WDMA_SIZE_1_WIDTH_WDMA__MASK;
+}
+
+#define REG_DPU_BN_CFG 0x00004060
+#define DPU_BN_CFG_RESERVED_0__MASK 0xfff00000
+#define DPU_BN_CFG_RESERVED_0__SHIFT 20
+static inline uint32_t DPU_BN_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_RESERVED_0__SHIFT) & DPU_BN_CFG_RESERVED_0__MASK;
+}
+#define DPU_BN_CFG_BN_ALU_ALGO__MASK 0x000f0000
+#define DPU_BN_CFG_BN_ALU_ALGO__SHIFT 16
+static inline uint32_t DPU_BN_CFG_BN_ALU_ALGO(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_ALU_ALGO__SHIFT) & DPU_BN_CFG_BN_ALU_ALGO__MASK;
+}
+#define DPU_BN_CFG_RESERVED_1__MASK 0x0000fe00
+#define DPU_BN_CFG_RESERVED_1__SHIFT 9
+static inline uint32_t DPU_BN_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_RESERVED_1__SHIFT) & DPU_BN_CFG_RESERVED_1__MASK;
+}
+#define DPU_BN_CFG_BN_ALU_SRC__MASK 0x00000100
+#define DPU_BN_CFG_BN_ALU_SRC__SHIFT 8
+static inline uint32_t DPU_BN_CFG_BN_ALU_SRC(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_ALU_SRC__SHIFT) & DPU_BN_CFG_BN_ALU_SRC__MASK;
+}
+#define DPU_BN_CFG_BN_RELUX_EN__MASK 0x00000080
+#define DPU_BN_CFG_BN_RELUX_EN__SHIFT 7
+static inline uint32_t DPU_BN_CFG_BN_RELUX_EN(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_RELUX_EN__SHIFT) & DPU_BN_CFG_BN_RELUX_EN__MASK;
+}
+#define DPU_BN_CFG_BN_RELU_BYPASS__MASK 0x00000040
+#define DPU_BN_CFG_BN_RELU_BYPASS__SHIFT 6
+static inline uint32_t DPU_BN_CFG_BN_RELU_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_RELU_BYPASS__SHIFT) & DPU_BN_CFG_BN_RELU_BYPASS__MASK;
+}
+#define DPU_BN_CFG_BN_MUL_PRELU__MASK 0x00000020
+#define DPU_BN_CFG_BN_MUL_PRELU__SHIFT 5
+static inline uint32_t DPU_BN_CFG_BN_MUL_PRELU(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_MUL_PRELU__SHIFT) & DPU_BN_CFG_BN_MUL_PRELU__MASK;
+}
+#define DPU_BN_CFG_BN_MUL_BYPASS__MASK 0x00000010
+#define DPU_BN_CFG_BN_MUL_BYPASS__SHIFT 4
+static inline uint32_t DPU_BN_CFG_BN_MUL_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_MUL_BYPASS__SHIFT) & DPU_BN_CFG_BN_MUL_BYPASS__MASK;
+}
+#define DPU_BN_CFG_RESERVED_2__MASK 0x0000000c
+#define DPU_BN_CFG_RESERVED_2__SHIFT 2
+static inline uint32_t DPU_BN_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_RESERVED_2__SHIFT) & DPU_BN_CFG_RESERVED_2__MASK;
+}
+#define DPU_BN_CFG_BN_ALU_BYPASS__MASK 0x00000002
+#define DPU_BN_CFG_BN_ALU_BYPASS__SHIFT 1
+static inline uint32_t DPU_BN_CFG_BN_ALU_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_ALU_BYPASS__SHIFT) & DPU_BN_CFG_BN_ALU_BYPASS__MASK;
+}
+#define DPU_BN_CFG_BN_BYPASS__MASK 0x00000001
+#define DPU_BN_CFG_BN_BYPASS__SHIFT 0
+static inline uint32_t DPU_BN_CFG_BN_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_BN_CFG_BN_BYPASS__SHIFT) & DPU_BN_CFG_BN_BYPASS__MASK;
+}
+
+#define REG_DPU_BN_ALU_CFG 0x00004064
+#define DPU_BN_ALU_CFG_BN_ALU_OPERAND__MASK 0xffffffff
+#define DPU_BN_ALU_CFG_BN_ALU_OPERAND__SHIFT 0
+static inline uint32_t DPU_BN_ALU_CFG_BN_ALU_OPERAND(uint32_t val)
+{
+ return ((val) << DPU_BN_ALU_CFG_BN_ALU_OPERAND__SHIFT) & DPU_BN_ALU_CFG_BN_ALU_OPERAND__MASK;
+}
+
+#define REG_DPU_BN_MUL_CFG 0x00004068
+#define DPU_BN_MUL_CFG_BN_MUL_OPERAND__MASK 0xffff0000
+#define DPU_BN_MUL_CFG_BN_MUL_OPERAND__SHIFT 16
+static inline uint32_t DPU_BN_MUL_CFG_BN_MUL_OPERAND(uint32_t val)
+{
+ return ((val) << DPU_BN_MUL_CFG_BN_MUL_OPERAND__SHIFT) & DPU_BN_MUL_CFG_BN_MUL_OPERAND__MASK;
+}
+#define DPU_BN_MUL_CFG_RESERVED_0__MASK 0x0000c000
+#define DPU_BN_MUL_CFG_RESERVED_0__SHIFT 14
+static inline uint32_t DPU_BN_MUL_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_BN_MUL_CFG_RESERVED_0__SHIFT) & DPU_BN_MUL_CFG_RESERVED_0__MASK;
+}
+#define DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__MASK 0x00003f00
+#define DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__SHIFT 8
+static inline uint32_t DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE(uint32_t val)
+{
+ return ((val) << DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__SHIFT) & DPU_BN_MUL_CFG_BN_MUL_SHIFT_VALUE__MASK;
+}
+#define DPU_BN_MUL_CFG_RESERVED_1__MASK 0x000000fc
+#define DPU_BN_MUL_CFG_RESERVED_1__SHIFT 2
+static inline uint32_t DPU_BN_MUL_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_BN_MUL_CFG_RESERVED_1__SHIFT) & DPU_BN_MUL_CFG_RESERVED_1__MASK;
+}
+#define DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__MASK 0x00000002
+#define DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__SHIFT 1
+static inline uint32_t DPU_BN_MUL_CFG_BN_TRUNCATE_SRC(uint32_t val)
+{
+ return ((val) << DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__SHIFT) & DPU_BN_MUL_CFG_BN_TRUNCATE_SRC__MASK;
+}
+#define DPU_BN_MUL_CFG_BN_MUL_SRC__MASK 0x00000001
+#define DPU_BN_MUL_CFG_BN_MUL_SRC__SHIFT 0
+static inline uint32_t DPU_BN_MUL_CFG_BN_MUL_SRC(uint32_t val)
+{
+ return ((val) << DPU_BN_MUL_CFG_BN_MUL_SRC__SHIFT) & DPU_BN_MUL_CFG_BN_MUL_SRC__MASK;
+}
+
+#define REG_DPU_BN_RELUX_CMP_VALUE 0x0000406c
+#define DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__MASK 0xffffffff
+#define DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__SHIFT 0
+static inline uint32_t DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT(uint32_t val)
+{
+ return ((val) << DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__SHIFT) & DPU_BN_RELUX_CMP_VALUE_BN_RELUX_CMP_DAT__MASK;
+}
+
+#define REG_DPU_EW_CFG 0x00004070
+#define DPU_EW_CFG_EW_CVT_TYPE__MASK 0x80000000
+#define DPU_EW_CFG_EW_CVT_TYPE__SHIFT 31
+static inline uint32_t DPU_EW_CFG_EW_CVT_TYPE(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_CVT_TYPE__SHIFT) & DPU_EW_CFG_EW_CVT_TYPE__MASK;
+}
+#define DPU_EW_CFG_EW_CVT_ROUND__MASK 0x40000000
+#define DPU_EW_CFG_EW_CVT_ROUND__SHIFT 30
+static inline uint32_t DPU_EW_CFG_EW_CVT_ROUND(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_CVT_ROUND__SHIFT) & DPU_EW_CFG_EW_CVT_ROUND__MASK;
+}
+#define DPU_EW_CFG_EW_DATA_MODE__MASK 0x30000000
+#define DPU_EW_CFG_EW_DATA_MODE__SHIFT 28
+static inline uint32_t DPU_EW_CFG_EW_DATA_MODE(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_DATA_MODE__SHIFT) & DPU_EW_CFG_EW_DATA_MODE__MASK;
+}
+#define DPU_EW_CFG_RESERVED_0__MASK 0x0f000000
+#define DPU_EW_CFG_RESERVED_0__SHIFT 24
+static inline uint32_t DPU_EW_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_RESERVED_0__SHIFT) & DPU_EW_CFG_RESERVED_0__MASK;
+}
+#define DPU_EW_CFG_EDATA_SIZE__MASK 0x00c00000
+#define DPU_EW_CFG_EDATA_SIZE__SHIFT 22
+static inline uint32_t DPU_EW_CFG_EDATA_SIZE(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EDATA_SIZE__SHIFT) & DPU_EW_CFG_EDATA_SIZE__MASK;
+}
+#define DPU_EW_CFG_EW_EQUAL_EN__MASK 0x00200000
+#define DPU_EW_CFG_EW_EQUAL_EN__SHIFT 21
+static inline uint32_t DPU_EW_CFG_EW_EQUAL_EN(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_EQUAL_EN__SHIFT) & DPU_EW_CFG_EW_EQUAL_EN__MASK;
+}
+#define DPU_EW_CFG_EW_BINARY_EN__MASK 0x00100000
+#define DPU_EW_CFG_EW_BINARY_EN__SHIFT 20
+static inline uint32_t DPU_EW_CFG_EW_BINARY_EN(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_BINARY_EN__SHIFT) & DPU_EW_CFG_EW_BINARY_EN__MASK;
+}
+#define DPU_EW_CFG_EW_ALU_ALGO__MASK 0x000f0000
+#define DPU_EW_CFG_EW_ALU_ALGO__SHIFT 16
+static inline uint32_t DPU_EW_CFG_EW_ALU_ALGO(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_ALU_ALGO__SHIFT) & DPU_EW_CFG_EW_ALU_ALGO__MASK;
+}
+#define DPU_EW_CFG_RESERVED_1__MASK 0x0000f800
+#define DPU_EW_CFG_RESERVED_1__SHIFT 11
+static inline uint32_t DPU_EW_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_RESERVED_1__SHIFT) & DPU_EW_CFG_RESERVED_1__MASK;
+}
+#define DPU_EW_CFG_EW_RELUX_EN__MASK 0x00000400
+#define DPU_EW_CFG_EW_RELUX_EN__SHIFT 10
+static inline uint32_t DPU_EW_CFG_EW_RELUX_EN(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_RELUX_EN__SHIFT) & DPU_EW_CFG_EW_RELUX_EN__MASK;
+}
+#define DPU_EW_CFG_EW_RELU_BYPASS__MASK 0x00000200
+#define DPU_EW_CFG_EW_RELU_BYPASS__SHIFT 9
+static inline uint32_t DPU_EW_CFG_EW_RELU_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_RELU_BYPASS__SHIFT) & DPU_EW_CFG_EW_RELU_BYPASS__MASK;
+}
+#define DPU_EW_CFG_EW_OP_CVT_BYPASS__MASK 0x00000100
+#define DPU_EW_CFG_EW_OP_CVT_BYPASS__SHIFT 8
+static inline uint32_t DPU_EW_CFG_EW_OP_CVT_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_OP_CVT_BYPASS__SHIFT) & DPU_EW_CFG_EW_OP_CVT_BYPASS__MASK;
+}
+#define DPU_EW_CFG_EW_LUT_BYPASS__MASK 0x00000080
+#define DPU_EW_CFG_EW_LUT_BYPASS__SHIFT 7
+static inline uint32_t DPU_EW_CFG_EW_LUT_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_LUT_BYPASS__SHIFT) & DPU_EW_CFG_EW_LUT_BYPASS__MASK;
+}
+#define DPU_EW_CFG_EW_OP_SRC__MASK 0x00000040
+#define DPU_EW_CFG_EW_OP_SRC__SHIFT 6
+static inline uint32_t DPU_EW_CFG_EW_OP_SRC(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_OP_SRC__SHIFT) & DPU_EW_CFG_EW_OP_SRC__MASK;
+}
+#define DPU_EW_CFG_EW_MUL_PRELU__MASK 0x00000020
+#define DPU_EW_CFG_EW_MUL_PRELU__SHIFT 5
+static inline uint32_t DPU_EW_CFG_EW_MUL_PRELU(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_MUL_PRELU__SHIFT) & DPU_EW_CFG_EW_MUL_PRELU__MASK;
+}
+#define DPU_EW_CFG_RESERVED_2__MASK 0x00000018
+#define DPU_EW_CFG_RESERVED_2__SHIFT 3
+static inline uint32_t DPU_EW_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_RESERVED_2__SHIFT) & DPU_EW_CFG_RESERVED_2__MASK;
+}
+#define DPU_EW_CFG_EW_OP_TYPE__MASK 0x00000004
+#define DPU_EW_CFG_EW_OP_TYPE__SHIFT 2
+static inline uint32_t DPU_EW_CFG_EW_OP_TYPE(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_OP_TYPE__SHIFT) & DPU_EW_CFG_EW_OP_TYPE__MASK;
+}
+#define DPU_EW_CFG_EW_OP_BYPASS__MASK 0x00000002
+#define DPU_EW_CFG_EW_OP_BYPASS__SHIFT 1
+static inline uint32_t DPU_EW_CFG_EW_OP_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_OP_BYPASS__SHIFT) & DPU_EW_CFG_EW_OP_BYPASS__MASK;
+}
+#define DPU_EW_CFG_EW_BYPASS__MASK 0x00000001
+#define DPU_EW_CFG_EW_BYPASS__SHIFT 0
+static inline uint32_t DPU_EW_CFG_EW_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_EW_CFG_EW_BYPASS__SHIFT) & DPU_EW_CFG_EW_BYPASS__MASK;
+}
+
+#define REG_DPU_EW_CVT_OFFSET_VALUE 0x00004074
+#define DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__MASK 0xffffffff
+#define DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__SHIFT 0
+static inline uint32_t DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET(uint32_t val)
+{
+ return ((val) << DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__SHIFT) & DPU_EW_CVT_OFFSET_VALUE_EW_OP_CVT_OFFSET__MASK;
+}
+
+#define REG_DPU_EW_CVT_SCALE_VALUE 0x00004078
+#define DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__MASK 0xffc00000
+#define DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__SHIFT 22
+static inline uint32_t DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE(uint32_t val)
+{
+ return ((val) << DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__SHIFT) & DPU_EW_CVT_SCALE_VALUE_EW_TRUNCATE__MASK;
+}
+#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__MASK 0x003f0000
+#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__SHIFT 16
+static inline uint32_t DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT(uint32_t val)
+{
+ return ((val) << DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__SHIFT) & DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SHIFT__MASK;
+}
+#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__MASK 0x0000ffff
+#define DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__SHIFT 0
+static inline uint32_t DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE(uint32_t val)
+{
+ return ((val) << DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__SHIFT) & DPU_EW_CVT_SCALE_VALUE_EW_OP_CVT_SCALE__MASK;
+}
+
+#define REG_DPU_EW_RELUX_CMP_VALUE 0x0000407c
+#define DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__MASK 0xffffffff
+#define DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__SHIFT 0
+static inline uint32_t DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT(uint32_t val)
+{
+ return ((val) << DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__SHIFT) & DPU_EW_RELUX_CMP_VALUE_EW_RELUX_CMP_DAT__MASK;
+}
+
+#define REG_DPU_OUT_CVT_OFFSET 0x00004080
+#define DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__MASK 0xffffffff
+#define DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__SHIFT 0
+static inline uint32_t DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__SHIFT) & DPU_OUT_CVT_OFFSET_OUT_CVT_OFFSET__MASK;
+}
+
+#define REG_DPU_OUT_CVT_SCALE 0x00004084
+#define DPU_OUT_CVT_SCALE_RESERVED_0__MASK 0xfffe0000
+#define DPU_OUT_CVT_SCALE_RESERVED_0__SHIFT 17
+static inline uint32_t DPU_OUT_CVT_SCALE_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SCALE_RESERVED_0__SHIFT) & DPU_OUT_CVT_SCALE_RESERVED_0__MASK;
+}
+#define DPU_OUT_CVT_SCALE_FP32TOFP16_EN__MASK 0x00010000
+#define DPU_OUT_CVT_SCALE_FP32TOFP16_EN__SHIFT 16
+static inline uint32_t DPU_OUT_CVT_SCALE_FP32TOFP16_EN(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SCALE_FP32TOFP16_EN__SHIFT) & DPU_OUT_CVT_SCALE_FP32TOFP16_EN__MASK;
+}
+#define DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__MASK 0x0000ffff
+#define DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__SHIFT 0
+static inline uint32_t DPU_OUT_CVT_SCALE_OUT_CVT_SCALE(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__SHIFT) & DPU_OUT_CVT_SCALE_OUT_CVT_SCALE__MASK;
+}
+
+#define REG_DPU_OUT_CVT_SHIFT 0x00004088
+#define DPU_OUT_CVT_SHIFT_CVT_TYPE__MASK 0x80000000
+#define DPU_OUT_CVT_SHIFT_CVT_TYPE__SHIFT 31
+static inline uint32_t DPU_OUT_CVT_SHIFT_CVT_TYPE(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SHIFT_CVT_TYPE__SHIFT) & DPU_OUT_CVT_SHIFT_CVT_TYPE__MASK;
+}
+#define DPU_OUT_CVT_SHIFT_CVT_ROUND__MASK 0x40000000
+#define DPU_OUT_CVT_SHIFT_CVT_ROUND__SHIFT 30
+static inline uint32_t DPU_OUT_CVT_SHIFT_CVT_ROUND(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SHIFT_CVT_ROUND__SHIFT) & DPU_OUT_CVT_SHIFT_CVT_ROUND__MASK;
+}
+#define DPU_OUT_CVT_SHIFT_RESERVED_0__MASK 0x3ff00000
+#define DPU_OUT_CVT_SHIFT_RESERVED_0__SHIFT 20
+static inline uint32_t DPU_OUT_CVT_SHIFT_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SHIFT_RESERVED_0__SHIFT) & DPU_OUT_CVT_SHIFT_RESERVED_0__MASK;
+}
+#define DPU_OUT_CVT_SHIFT_MINUS_EXP__MASK 0x000ff000
+#define DPU_OUT_CVT_SHIFT_MINUS_EXP__SHIFT 12
+static inline uint32_t DPU_OUT_CVT_SHIFT_MINUS_EXP(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SHIFT_MINUS_EXP__SHIFT) & DPU_OUT_CVT_SHIFT_MINUS_EXP__MASK;
+}
+#define DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__MASK 0x00000fff
+#define DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__SHIFT 0
+static inline uint32_t DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT(uint32_t val)
+{
+ return ((val) << DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__SHIFT) & DPU_OUT_CVT_SHIFT_OUT_CVT_SHIFT__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_0 0x00004090
+#define DPU_EW_OP_VALUE_0_EW_OPERAND_0__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_0_EW_OPERAND_0__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_0_EW_OPERAND_0(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_0_EW_OPERAND_0__SHIFT) & DPU_EW_OP_VALUE_0_EW_OPERAND_0__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_1 0x00004094
+#define DPU_EW_OP_VALUE_1_EW_OPERAND_1__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_1_EW_OPERAND_1__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_1_EW_OPERAND_1(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_1_EW_OPERAND_1__SHIFT) & DPU_EW_OP_VALUE_1_EW_OPERAND_1__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_2 0x00004098
+#define DPU_EW_OP_VALUE_2_EW_OPERAND_2__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_2_EW_OPERAND_2__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_2_EW_OPERAND_2(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_2_EW_OPERAND_2__SHIFT) & DPU_EW_OP_VALUE_2_EW_OPERAND_2__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_3 0x0000409c
+#define DPU_EW_OP_VALUE_3_EW_OPERAND_3__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_3_EW_OPERAND_3__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_3_EW_OPERAND_3(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_3_EW_OPERAND_3__SHIFT) & DPU_EW_OP_VALUE_3_EW_OPERAND_3__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_4 0x000040a0
+#define DPU_EW_OP_VALUE_4_EW_OPERAND_4__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_4_EW_OPERAND_4__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_4_EW_OPERAND_4(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_4_EW_OPERAND_4__SHIFT) & DPU_EW_OP_VALUE_4_EW_OPERAND_4__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_5 0x000040a4
+#define DPU_EW_OP_VALUE_5_EW_OPERAND_5__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_5_EW_OPERAND_5__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_5_EW_OPERAND_5(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_5_EW_OPERAND_5__SHIFT) & DPU_EW_OP_VALUE_5_EW_OPERAND_5__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_6 0x000040a8
+#define DPU_EW_OP_VALUE_6_EW_OPERAND_6__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_6_EW_OPERAND_6__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_6_EW_OPERAND_6(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_6_EW_OPERAND_6__SHIFT) & DPU_EW_OP_VALUE_6_EW_OPERAND_6__MASK;
+}
+
+#define REG_DPU_EW_OP_VALUE_7 0x000040ac
+#define DPU_EW_OP_VALUE_7_EW_OPERAND_7__MASK 0xffffffff
+#define DPU_EW_OP_VALUE_7_EW_OPERAND_7__SHIFT 0
+static inline uint32_t DPU_EW_OP_VALUE_7_EW_OPERAND_7(uint32_t val)
+{
+ return ((val) << DPU_EW_OP_VALUE_7_EW_OPERAND_7__SHIFT) & DPU_EW_OP_VALUE_7_EW_OPERAND_7__MASK;
+}
+
+#define REG_DPU_SURFACE_ADD 0x000040c0
+#define DPU_SURFACE_ADD_SURF_ADD__MASK 0xfffffff0
+#define DPU_SURFACE_ADD_SURF_ADD__SHIFT 4
+static inline uint32_t DPU_SURFACE_ADD_SURF_ADD(uint32_t val)
+{
+ return ((val) << DPU_SURFACE_ADD_SURF_ADD__SHIFT) & DPU_SURFACE_ADD_SURF_ADD__MASK;
+}
+#define DPU_SURFACE_ADD_RESERVED_0__MASK 0x0000000f
+#define DPU_SURFACE_ADD_RESERVED_0__SHIFT 0
+static inline uint32_t DPU_SURFACE_ADD_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_SURFACE_ADD_RESERVED_0__SHIFT) & DPU_SURFACE_ADD_RESERVED_0__MASK;
+}
+
+#define REG_DPU_LUT_ACCESS_CFG 0x00004100
+#define DPU_LUT_ACCESS_CFG_RESERVED_0__MASK 0xfffc0000
+#define DPU_LUT_ACCESS_CFG_RESERVED_0__SHIFT 18
+static inline uint32_t DPU_LUT_ACCESS_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_CFG_RESERVED_0__SHIFT) & DPU_LUT_ACCESS_CFG_RESERVED_0__MASK;
+}
+#define DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__MASK 0x00020000
+#define DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__SHIFT 17
+static inline uint32_t DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__SHIFT) & DPU_LUT_ACCESS_CFG_LUT_ACCESS_TYPE__MASK;
+}
+#define DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__MASK 0x00010000
+#define DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__SHIFT 16
+static inline uint32_t DPU_LUT_ACCESS_CFG_LUT_TABLE_ID(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__SHIFT) & DPU_LUT_ACCESS_CFG_LUT_TABLE_ID__MASK;
+}
+#define DPU_LUT_ACCESS_CFG_RESERVED_1__MASK 0x0000fc00
+#define DPU_LUT_ACCESS_CFG_RESERVED_1__SHIFT 10
+static inline uint32_t DPU_LUT_ACCESS_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_CFG_RESERVED_1__SHIFT) & DPU_LUT_ACCESS_CFG_RESERVED_1__MASK;
+}
+#define DPU_LUT_ACCESS_CFG_LUT_ADDR__MASK 0x000003ff
+#define DPU_LUT_ACCESS_CFG_LUT_ADDR__SHIFT 0
+static inline uint32_t DPU_LUT_ACCESS_CFG_LUT_ADDR(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_CFG_LUT_ADDR__SHIFT) & DPU_LUT_ACCESS_CFG_LUT_ADDR__MASK;
+}
+
+#define REG_DPU_LUT_ACCESS_DATA 0x00004104
+#define DPU_LUT_ACCESS_DATA_RESERVED_0__MASK 0xffff0000
+#define DPU_LUT_ACCESS_DATA_RESERVED_0__SHIFT 16
+static inline uint32_t DPU_LUT_ACCESS_DATA_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_DATA_RESERVED_0__SHIFT) & DPU_LUT_ACCESS_DATA_RESERVED_0__MASK;
+}
+#define DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__MASK 0x0000ffff
+#define DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__SHIFT 0
+static inline uint32_t DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA(uint32_t val)
+{
+ return ((val) << DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__SHIFT) & DPU_LUT_ACCESS_DATA_LUT_ACCESS_DATA__MASK;
+}
+
+#define REG_DPU_LUT_CFG 0x00004108
+#define DPU_LUT_CFG_RESERVED_0__MASK 0xffffff00
+#define DPU_LUT_CFG_RESERVED_0__SHIFT 8
+static inline uint32_t DPU_LUT_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_RESERVED_0__SHIFT) & DPU_LUT_CFG_RESERVED_0__MASK;
+}
+#define DPU_LUT_CFG_LUT_CAL_SEL__MASK 0x00000080
+#define DPU_LUT_CFG_LUT_CAL_SEL__SHIFT 7
+static inline uint32_t DPU_LUT_CFG_LUT_CAL_SEL(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_CAL_SEL__SHIFT) & DPU_LUT_CFG_LUT_CAL_SEL__MASK;
+}
+#define DPU_LUT_CFG_LUT_HYBRID_PRIORITY__MASK 0x00000040
+#define DPU_LUT_CFG_LUT_HYBRID_PRIORITY__SHIFT 6
+static inline uint32_t DPU_LUT_CFG_LUT_HYBRID_PRIORITY(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_HYBRID_PRIORITY__SHIFT) & DPU_LUT_CFG_LUT_HYBRID_PRIORITY__MASK;
+}
+#define DPU_LUT_CFG_LUT_OFLOW_PRIORITY__MASK 0x00000020
+#define DPU_LUT_CFG_LUT_OFLOW_PRIORITY__SHIFT 5
+static inline uint32_t DPU_LUT_CFG_LUT_OFLOW_PRIORITY(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_OFLOW_PRIORITY__SHIFT) & DPU_LUT_CFG_LUT_OFLOW_PRIORITY__MASK;
+}
+#define DPU_LUT_CFG_LUT_UFLOW_PRIORITY__MASK 0x00000010
+#define DPU_LUT_CFG_LUT_UFLOW_PRIORITY__SHIFT 4
+static inline uint32_t DPU_LUT_CFG_LUT_UFLOW_PRIORITY(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_UFLOW_PRIORITY__SHIFT) & DPU_LUT_CFG_LUT_UFLOW_PRIORITY__MASK;
+}
+#define DPU_LUT_CFG_LUT_LO_LE_MUX__MASK 0x0000000c
+#define DPU_LUT_CFG_LUT_LO_LE_MUX__SHIFT 2
+static inline uint32_t DPU_LUT_CFG_LUT_LO_LE_MUX(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_LO_LE_MUX__SHIFT) & DPU_LUT_CFG_LUT_LO_LE_MUX__MASK;
+}
+#define DPU_LUT_CFG_LUT_EXPAND_EN__MASK 0x00000002
+#define DPU_LUT_CFG_LUT_EXPAND_EN__SHIFT 1
+static inline uint32_t DPU_LUT_CFG_LUT_EXPAND_EN(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_EXPAND_EN__SHIFT) & DPU_LUT_CFG_LUT_EXPAND_EN__MASK;
+}
+#define DPU_LUT_CFG_LUT_ROAD_SEL__MASK 0x00000001
+#define DPU_LUT_CFG_LUT_ROAD_SEL__SHIFT 0
+static inline uint32_t DPU_LUT_CFG_LUT_ROAD_SEL(uint32_t val)
+{
+ return ((val) << DPU_LUT_CFG_LUT_ROAD_SEL__SHIFT) & DPU_LUT_CFG_LUT_ROAD_SEL__MASK;
+}
+
+#define REG_DPU_LUT_INFO 0x0000410c
+#define DPU_LUT_INFO_RESERVED_0__MASK 0xff000000
+#define DPU_LUT_INFO_RESERVED_0__SHIFT 24
+static inline uint32_t DPU_LUT_INFO_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_LUT_INFO_RESERVED_0__SHIFT) & DPU_LUT_INFO_RESERVED_0__MASK;
+}
+#define DPU_LUT_INFO_LUT_LO_INDEX_SELECT__MASK 0x00ff0000
+#define DPU_LUT_INFO_LUT_LO_INDEX_SELECT__SHIFT 16
+static inline uint32_t DPU_LUT_INFO_LUT_LO_INDEX_SELECT(uint32_t val)
+{
+ return ((val) << DPU_LUT_INFO_LUT_LO_INDEX_SELECT__SHIFT) & DPU_LUT_INFO_LUT_LO_INDEX_SELECT__MASK;
+}
+#define DPU_LUT_INFO_LUT_LE_INDEX_SELECT__MASK 0x0000ff00
+#define DPU_LUT_INFO_LUT_LE_INDEX_SELECT__SHIFT 8
+static inline uint32_t DPU_LUT_INFO_LUT_LE_INDEX_SELECT(uint32_t val)
+{
+ return ((val) << DPU_LUT_INFO_LUT_LE_INDEX_SELECT__SHIFT) & DPU_LUT_INFO_LUT_LE_INDEX_SELECT__MASK;
+}
+#define DPU_LUT_INFO_RESERVED_1__MASK 0x000000ff
+#define DPU_LUT_INFO_RESERVED_1__SHIFT 0
+static inline uint32_t DPU_LUT_INFO_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_LUT_INFO_RESERVED_1__SHIFT) & DPU_LUT_INFO_RESERVED_1__MASK;
+}
+
+#define REG_DPU_LUT_LE_START 0x00004110
+#define DPU_LUT_LE_START_LUT_LE_START__MASK 0xffffffff
+#define DPU_LUT_LE_START_LUT_LE_START__SHIFT 0
+static inline uint32_t DPU_LUT_LE_START_LUT_LE_START(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_START_LUT_LE_START__SHIFT) & DPU_LUT_LE_START_LUT_LE_START__MASK;
+}
+
+#define REG_DPU_LUT_LE_END 0x00004114
+#define DPU_LUT_LE_END_LUT_LE_END__MASK 0xffffffff
+#define DPU_LUT_LE_END_LUT_LE_END__SHIFT 0
+static inline uint32_t DPU_LUT_LE_END_LUT_LE_END(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_END_LUT_LE_END__SHIFT) & DPU_LUT_LE_END_LUT_LE_END__MASK;
+}
+
+#define REG_DPU_LUT_LO_START 0x00004118
+#define DPU_LUT_LO_START_LUT_LO_START__MASK 0xffffffff
+#define DPU_LUT_LO_START_LUT_LO_START__SHIFT 0
+static inline uint32_t DPU_LUT_LO_START_LUT_LO_START(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_START_LUT_LO_START__SHIFT) & DPU_LUT_LO_START_LUT_LO_START__MASK;
+}
+
+#define REG_DPU_LUT_LO_END 0x0000411c
+#define DPU_LUT_LO_END_LUT_LO_END__MASK 0xffffffff
+#define DPU_LUT_LO_END_LUT_LO_END__SHIFT 0
+static inline uint32_t DPU_LUT_LO_END_LUT_LO_END(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_END_LUT_LO_END__SHIFT) & DPU_LUT_LO_END_LUT_LO_END__MASK;
+}
+
+#define REG_DPU_LUT_LE_SLOPE_SCALE 0x00004120
+#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__MASK 0xffff0000
+#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__SHIFT 16
+static inline uint32_t DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__SHIFT) & DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_OFLOW_SCALE__MASK;
+}
+#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__MASK 0x0000ffff
+#define DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__SHIFT 0
+static inline uint32_t DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__SHIFT) & DPU_LUT_LE_SLOPE_SCALE_LUT_LE_SLOPE_UFLOW_SCALE__MASK;
+}
+
+#define REG_DPU_LUT_LE_SLOPE_SHIFT 0x00004124
+#define DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__MASK 0xfffffc00
+#define DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__SHIFT 10
+static inline uint32_t DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__SHIFT) & DPU_LUT_LE_SLOPE_SHIFT_RESERVED_0__MASK;
+}
+#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__MASK 0x000003e0
+#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__SHIFT 5
+static inline uint32_t DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__SHIFT) & DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_OFLOW_SHIFT__MASK;
+}
+#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__MASK 0x0000001f
+#define DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__SHIFT 0
+static inline uint32_t DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT(uint32_t val)
+{
+ return ((val) << DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__SHIFT) & DPU_LUT_LE_SLOPE_SHIFT_LUT_LE_SLOPE_UFLOW_SHIFT__MASK;
+}
+
+#define REG_DPU_LUT_LO_SLOPE_SCALE 0x00004128
+#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__MASK 0xffff0000
+#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__SHIFT 16
+static inline uint32_t DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__SHIFT) & DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_OFLOW_SCALE__MASK;
+}
+#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__MASK 0x0000ffff
+#define DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__SHIFT 0
+static inline uint32_t DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__SHIFT) & DPU_LUT_LO_SLOPE_SCALE_LUT_LO_SLOPE_UFLOW_SCALE__MASK;
+}
+
+#define REG_DPU_LUT_LO_SLOPE_SHIFT 0x0000412c
+#define DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__MASK 0xfffffc00
+#define DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__SHIFT 10
+static inline uint32_t DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__SHIFT) & DPU_LUT_LO_SLOPE_SHIFT_RESERVED_0__MASK;
+}
+#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__MASK 0x000003e0
+#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__SHIFT 5
+static inline uint32_t DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__SHIFT) & DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_OFLOW_SHIFT__MASK;
+}
+#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__MASK 0x0000001f
+#define DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__SHIFT 0
+static inline uint32_t DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT(uint32_t val)
+{
+ return ((val) << DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__SHIFT) & DPU_LUT_LO_SLOPE_SHIFT_LUT_LO_SLOPE_UFLOW_SHIFT__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_S_STATUS 0x00005000
+#define DPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK 0xfffc0000
+#define DPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT 18
+static inline uint32_t DPU_RDMA_RDMA_S_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK 0x00030000
+#define DPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT 16
+static inline uint32_t DPU_RDMA_RDMA_S_STATUS_STATUS_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT) & DPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK;
+}
+#define DPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK 0x0000fffc
+#define DPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT 2
+static inline uint32_t DPU_RDMA_RDMA_S_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK;
+}
+#define DPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK 0x00000003
+#define DPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_S_STATUS_STATUS_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT) & DPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_S_POINTER 0x00005004
+#define DPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK 0xfffe0000
+#define DPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT 17
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK 0x00010000
+#define DPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT 16
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_EXECUTER(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT) & DPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK 0x0000ffc0
+#define DPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT 6
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020
+#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010
+#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK 0x00000008
+#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT 3
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004
+#define DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT 2
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT) & DPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK 0x00000002
+#define DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT 1
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK;
+}
+#define DPU_RDMA_RDMA_S_POINTER_POINTER__MASK 0x00000001
+#define DPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_S_POINTER_POINTER(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT) & DPU_RDMA_RDMA_S_POINTER_POINTER__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_OPERATION_ENABLE 0x00005008
+#define DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT) & DPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_DATA_CUBE_WIDTH 0x0000500c
+#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__MASK 0xffffe000
+#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__SHIFT 13
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_WIDTH_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__MASK 0x00001fff
+#define DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_WIDTH_WIDTH__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_DATA_CUBE_HEIGHT 0x00005010
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__MASK 0xe0000000
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT 29
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__MASK 0x1fff0000
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__SHIFT 16
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_EW_LINE_NOTCH_ADDR__MASK;
+}
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__MASK 0x0000e000
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT 13
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_RESERVED_1__MASK;
+}
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__MASK 0x00001fff
+#define DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_HEIGHT_HEIGHT__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_DATA_CUBE_CHANNEL 0x00005014
+#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__MASK 0xffffe000
+#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT 13
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__MASK 0x00001fff
+#define DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__SHIFT) & DPU_RDMA_RDMA_DATA_CUBE_CHANNEL_CHANNEL__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_SRC_BASE_ADDR 0x00005018
+#define DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK 0xffffffff
+#define DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_BRDMA_CFG 0x0000501c
+#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__MASK 0xffffffe0
+#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__SHIFT 5
+static inline uint32_t DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__MASK 0x0000001e
+#define DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__SHIFT 1
+static inline uint32_t DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__SHIFT) & DPU_RDMA_RDMA_BRDMA_CFG_BRDMA_DATA_USE__MASK;
+}
+#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__MASK 0x00000001
+#define DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_BRDMA_CFG_RESERVED_1__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_BS_BASE_ADDR 0x00005020
+#define DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__MASK 0xffffffff
+#define DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_BS_BASE_ADDR_BS_BASE_ADDR__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_NRDMA_CFG 0x00005028
+#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__MASK 0xffffffe0
+#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__SHIFT 5
+static inline uint32_t DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__MASK 0x0000001e
+#define DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__SHIFT 1
+static inline uint32_t DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__SHIFT) & DPU_RDMA_RDMA_NRDMA_CFG_NRDMA_DATA_USE__MASK;
+}
+#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__MASK 0x00000001
+#define DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_NRDMA_CFG_RESERVED_1__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_BN_BASE_ADDR 0x0000502c
+#define DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__MASK 0xffffffff
+#define DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_BN_BASE_ADDR_BN_BASE_ADDR__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_ERDMA_CFG 0x00005034
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__MASK 0xc0000000
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__SHIFT 30
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_MODE__MASK;
+}
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__MASK 0x20000000
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__SHIFT 29
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_SURF_MODE__MASK;
+}
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__MASK 0x10000000
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__SHIFT 28
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_NONALIGN__MASK;
+}
+#define DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__MASK 0x0ffffff0
+#define DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__MASK 0x0000000c
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__SHIFT 2
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DATA_SIZE__MASK;
+}
+#define DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__MASK 0x00000002
+#define DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__SHIFT 1
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_OV4K_BYPASS__MASK;
+}
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__MASK 0x00000001
+#define DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__SHIFT) & DPU_RDMA_RDMA_ERDMA_CFG_ERDMA_DISABLE__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_EW_BASE_ADDR 0x00005038
+#define DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__MASK 0xffffffff
+#define DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__SHIFT) & DPU_RDMA_RDMA_EW_BASE_ADDR_EW_BASE_ADDR__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_EW_SURF_STRIDE 0x00005040
+#define DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__MASK 0xfffffff0
+#define DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__SHIFT) & DPU_RDMA_RDMA_EW_SURF_STRIDE_EW_SURF_STRIDE__MASK;
+}
+#define DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__MASK 0x0000000f
+#define DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_EW_SURF_STRIDE_RESERVED_0__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_FEATURE_MODE_CFG 0x00005044
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__MASK 0xfffc0000
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__SHIFT 18
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__MASK 0x00038000
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__SHIFT 15
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_IN_PRECISION__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__MASK 0x00007800
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__SHIFT 11
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_BURST_LEN__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__MASK 0x00000700
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__SHIFT 8
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_COMB_USE__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__MASK 0x000000e0
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__SHIFT 5
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_PROC_PRECISION__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__MASK 0x00000010
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_DISABLE__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__MASK 0x00000008
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__SHIFT 3
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_MRDMA_FP16TOFP32_EN__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__MASK 0x00000006
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__SHIFT 1
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_CONV_MODE__MASK;
+}
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__MASK 0x00000001
+#define DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__SHIFT) & DPU_RDMA_RDMA_FEATURE_MODE_CFG_FLYING_MODE__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_SRC_DMA_CFG 0x00005048
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__MASK 0xfff80000
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__SHIFT 19
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_LINE_NOTCH_ADDR__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__MASK 0x0007c000
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__SHIFT 14
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__MASK 0x00002000
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__SHIFT 13
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_POOLING_METHOD__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__MASK 0x00001000
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__SHIFT 12
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_UNPOOLING_EN__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__MASK 0x00000e00
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__SHIFT 9
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_HEIGHT__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__MASK 0x000001c0
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__SHIFT 6
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_STRIDE_WIDTH__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__MASK 0x00000038
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__SHIFT 3
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_HEIGHT__MASK;
+}
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__MASK 0x00000007
+#define DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__SHIFT) & DPU_RDMA_RDMA_SRC_DMA_CFG_KERNEL_WIDTH__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_SURF_NOTCH 0x0000504c
+#define DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__MASK 0xfffffff0
+#define DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__SHIFT) & DPU_RDMA_RDMA_SURF_NOTCH_SURF_NOTCH_ADDR__MASK;
+}
+#define DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__MASK 0x0000000f
+#define DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_SURF_NOTCH_RESERVED_0__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_PAD_CFG 0x00005064
+#define DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__MASK 0xffff0000
+#define DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__SHIFT 16
+static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_PAD_VALUE__MASK;
+}
+#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__MASK 0x0000ff80
+#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__SHIFT 7
+static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_RESERVED_0__MASK;
+}
+#define DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__MASK 0x00000070
+#define DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_PAD_TOP(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_PAD_TOP__MASK;
+}
+#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__MASK 0x00000008
+#define DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__SHIFT 3
+static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_RESERVED_1__MASK;
+}
+#define DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__MASK 0x00000007
+#define DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__SHIFT) & DPU_RDMA_RDMA_PAD_CFG_PAD_LEFT__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_WEIGHT 0x00005068
+#define DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__MASK 0xff000000
+#define DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__SHIFT 24
+static inline uint32_t DPU_RDMA_RDMA_WEIGHT_E_WEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_E_WEIGHT__MASK;
+}
+#define DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__MASK 0x00ff0000
+#define DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__SHIFT 16
+static inline uint32_t DPU_RDMA_RDMA_WEIGHT_N_WEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_N_WEIGHT__MASK;
+}
+#define DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__MASK 0x0000ff00
+#define DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__SHIFT 8
+static inline uint32_t DPU_RDMA_RDMA_WEIGHT_B_WEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_B_WEIGHT__MASK;
+}
+#define DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__MASK 0x000000ff
+#define DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_WEIGHT_M_WEIGHT(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__SHIFT) & DPU_RDMA_RDMA_WEIGHT_M_WEIGHT__MASK;
+}
+
+#define REG_DPU_RDMA_RDMA_EW_SURF_NOTCH 0x0000506c
+#define DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__MASK 0xfffffff0
+#define DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__SHIFT 4
+static inline uint32_t DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__SHIFT) & DPU_RDMA_RDMA_EW_SURF_NOTCH_EW_SURF_NOTCH__MASK;
+}
+#define DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__MASK 0x0000000f
+#define DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__SHIFT 0
+static inline uint32_t DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0(uint32_t val)
+{
+ return ((val) << DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__SHIFT) & DPU_RDMA_RDMA_EW_SURF_NOTCH_RESERVED_0__MASK;
+}
+
+#define REG_PPU_S_STATUS 0x00006000
+#define PPU_S_STATUS_RESERVED_0__MASK 0xfffc0000
+#define PPU_S_STATUS_RESERVED_0__SHIFT 18
+static inline uint32_t PPU_S_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_S_STATUS_RESERVED_0__SHIFT) & PPU_S_STATUS_RESERVED_0__MASK;
+}
+#define PPU_S_STATUS_STATUS_1__MASK 0x00030000
+#define PPU_S_STATUS_STATUS_1__SHIFT 16
+static inline uint32_t PPU_S_STATUS_STATUS_1(uint32_t val)
+{
+ return ((val) << PPU_S_STATUS_STATUS_1__SHIFT) & PPU_S_STATUS_STATUS_1__MASK;
+}
+#define PPU_S_STATUS_RESERVED_1__MASK 0x0000fffc
+#define PPU_S_STATUS_RESERVED_1__SHIFT 2
+static inline uint32_t PPU_S_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_S_STATUS_RESERVED_1__SHIFT) & PPU_S_STATUS_RESERVED_1__MASK;
+}
+#define PPU_S_STATUS_STATUS_0__MASK 0x00000003
+#define PPU_S_STATUS_STATUS_0__SHIFT 0
+static inline uint32_t PPU_S_STATUS_STATUS_0(uint32_t val)
+{
+ return ((val) << PPU_S_STATUS_STATUS_0__SHIFT) & PPU_S_STATUS_STATUS_0__MASK;
+}
+
+#define REG_PPU_S_POINTER 0x00006004
+#define PPU_S_POINTER_RESERVED_0__MASK 0xfffe0000
+#define PPU_S_POINTER_RESERVED_0__SHIFT 17
+static inline uint32_t PPU_S_POINTER_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_RESERVED_0__SHIFT) & PPU_S_POINTER_RESERVED_0__MASK;
+}
+#define PPU_S_POINTER_EXECUTER__MASK 0x00010000
+#define PPU_S_POINTER_EXECUTER__SHIFT 16
+static inline uint32_t PPU_S_POINTER_EXECUTER(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_EXECUTER__SHIFT) & PPU_S_POINTER_EXECUTER__MASK;
+}
+#define PPU_S_POINTER_RESERVED_1__MASK 0x0000ffc0
+#define PPU_S_POINTER_RESERVED_1__SHIFT 6
+static inline uint32_t PPU_S_POINTER_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_RESERVED_1__SHIFT) & PPU_S_POINTER_RESERVED_1__MASK;
+}
+#define PPU_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020
+#define PPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5
+static inline uint32_t PPU_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & PPU_S_POINTER_EXECUTER_PP_CLEAR__MASK;
+}
+#define PPU_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010
+#define PPU_S_POINTER_POINTER_PP_CLEAR__SHIFT 4
+static inline uint32_t PPU_S_POINTER_POINTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_POINTER_PP_CLEAR__SHIFT) & PPU_S_POINTER_POINTER_PP_CLEAR__MASK;
+}
+#define PPU_S_POINTER_POINTER_PP_MODE__MASK 0x00000008
+#define PPU_S_POINTER_POINTER_PP_MODE__SHIFT 3
+static inline uint32_t PPU_S_POINTER_POINTER_PP_MODE(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_POINTER_PP_MODE__SHIFT) & PPU_S_POINTER_POINTER_PP_MODE__MASK;
+}
+#define PPU_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004
+#define PPU_S_POINTER_EXECUTER_PP_EN__SHIFT 2
+static inline uint32_t PPU_S_POINTER_EXECUTER_PP_EN(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_EXECUTER_PP_EN__SHIFT) & PPU_S_POINTER_EXECUTER_PP_EN__MASK;
+}
+#define PPU_S_POINTER_POINTER_PP_EN__MASK 0x00000002
+#define PPU_S_POINTER_POINTER_PP_EN__SHIFT 1
+static inline uint32_t PPU_S_POINTER_POINTER_PP_EN(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_POINTER_PP_EN__SHIFT) & PPU_S_POINTER_POINTER_PP_EN__MASK;
+}
+#define PPU_S_POINTER_POINTER__MASK 0x00000001
+#define PPU_S_POINTER_POINTER__SHIFT 0
+static inline uint32_t PPU_S_POINTER_POINTER(uint32_t val)
+{
+ return ((val) << PPU_S_POINTER_POINTER__SHIFT) & PPU_S_POINTER_POINTER__MASK;
+}
+
+#define REG_PPU_OPERATION_ENABLE 0x00006008
+#define PPU_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define PPU_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t PPU_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_ENABLE_RESERVED_0__SHIFT) & PPU_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define PPU_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define PPU_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t PPU_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_ENABLE_OP_EN__SHIFT) & PPU_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_PPU_DATA_CUBE_IN_WIDTH 0x0000600c
+#define PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__MASK 0xffffe000
+#define PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_DATA_CUBE_IN_WIDTH_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__SHIFT) & PPU_DATA_CUBE_IN_WIDTH_RESERVED_0__MASK;
+}
+#define PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK 0x00001fff
+#define PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT 0
+static inline uint32_t PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT) & PPU_DATA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK;
+}
+
+#define REG_PPU_DATA_CUBE_IN_HEIGHT 0x00006010
+#define PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__MASK 0xffffe000
+#define PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT) & PPU_DATA_CUBE_IN_HEIGHT_RESERVED_0__MASK;
+}
+#define PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK 0x00001fff
+#define PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT 0
+static inline uint32_t PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT) & PPU_DATA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK;
+}
+
+#define REG_PPU_DATA_CUBE_IN_CHANNEL 0x00006014
+#define PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__MASK 0xffffe000
+#define PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT) & PPU_DATA_CUBE_IN_CHANNEL_RESERVED_0__MASK;
+}
+#define PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK 0x00001fff
+#define PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT 0
+static inline uint32_t PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT) & PPU_DATA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK;
+}
+
+#define REG_PPU_DATA_CUBE_OUT_WIDTH 0x00006018
+#define PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__MASK 0xffffe000
+#define PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__SHIFT) & PPU_DATA_CUBE_OUT_WIDTH_RESERVED_0__MASK;
+}
+#define PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__MASK 0x00001fff
+#define PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__SHIFT 0
+static inline uint32_t PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__SHIFT) & PPU_DATA_CUBE_OUT_WIDTH_CUBE_OUT_WIDTH__MASK;
+}
+
+#define REG_PPU_DATA_CUBE_OUT_HEIGHT 0x0000601c
+#define PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__MASK 0xffffe000
+#define PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__SHIFT) & PPU_DATA_CUBE_OUT_HEIGHT_RESERVED_0__MASK;
+}
+#define PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__MASK 0x00001fff
+#define PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__SHIFT 0
+static inline uint32_t PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__SHIFT) & PPU_DATA_CUBE_OUT_HEIGHT_CUBE_OUT_HEIGHT__MASK;
+}
+
+#define REG_PPU_DATA_CUBE_OUT_CHANNEL 0x00006020
+#define PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__MASK 0xffffe000
+#define PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__SHIFT) & PPU_DATA_CUBE_OUT_CHANNEL_RESERVED_0__MASK;
+}
+#define PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__MASK 0x00001fff
+#define PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__SHIFT 0
+static inline uint32_t PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL(uint32_t val)
+{
+ return ((val) << PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__SHIFT) & PPU_DATA_CUBE_OUT_CHANNEL_CUBE_OUT_CHANNEL__MASK;
+}
+
+#define REG_PPU_OPERATION_MODE_CFG 0x00006024
+#define PPU_OPERATION_MODE_CFG_RESERVED_0__MASK 0x80000000
+#define PPU_OPERATION_MODE_CFG_RESERVED_0__SHIFT 31
+static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_0__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_0__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_INDEX_EN__MASK 0x40000000
+#define PPU_OPERATION_MODE_CFG_INDEX_EN__SHIFT 30
+static inline uint32_t PPU_OPERATION_MODE_CFG_INDEX_EN(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_INDEX_EN__SHIFT) & PPU_OPERATION_MODE_CFG_INDEX_EN__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_RESERVED_1__MASK 0x20000000
+#define PPU_OPERATION_MODE_CFG_RESERVED_1__SHIFT 29
+static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_1__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_1__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_NOTCH_ADDR__MASK 0x1fff0000
+#define PPU_OPERATION_MODE_CFG_NOTCH_ADDR__SHIFT 16
+static inline uint32_t PPU_OPERATION_MODE_CFG_NOTCH_ADDR(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_NOTCH_ADDR__SHIFT) & PPU_OPERATION_MODE_CFG_NOTCH_ADDR__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_RESERVED_2__MASK 0x0000ff00
+#define PPU_OPERATION_MODE_CFG_RESERVED_2__SHIFT 8
+static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_2__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_2__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_USE_CNT__MASK 0x000000e0
+#define PPU_OPERATION_MODE_CFG_USE_CNT__SHIFT 5
+static inline uint32_t PPU_OPERATION_MODE_CFG_USE_CNT(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_USE_CNT__SHIFT) & PPU_OPERATION_MODE_CFG_USE_CNT__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_FLYING_MODE__MASK 0x00000010
+#define PPU_OPERATION_MODE_CFG_FLYING_MODE__SHIFT 4
+static inline uint32_t PPU_OPERATION_MODE_CFG_FLYING_MODE(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_FLYING_MODE__SHIFT) & PPU_OPERATION_MODE_CFG_FLYING_MODE__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_RESERVED_3__MASK 0x0000000c
+#define PPU_OPERATION_MODE_CFG_RESERVED_3__SHIFT 2
+static inline uint32_t PPU_OPERATION_MODE_CFG_RESERVED_3(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_RESERVED_3__SHIFT) & PPU_OPERATION_MODE_CFG_RESERVED_3__MASK;
+}
+#define PPU_OPERATION_MODE_CFG_POOLING_METHOD__MASK 0x00000003
+#define PPU_OPERATION_MODE_CFG_POOLING_METHOD__SHIFT 0
+static inline uint32_t PPU_OPERATION_MODE_CFG_POOLING_METHOD(uint32_t val)
+{
+ return ((val) << PPU_OPERATION_MODE_CFG_POOLING_METHOD__SHIFT) & PPU_OPERATION_MODE_CFG_POOLING_METHOD__MASK;
+}
+
+#define REG_PPU_POOLING_KERNEL_CFG 0x00006034
+#define PPU_POOLING_KERNEL_CFG_RESERVED_0__MASK 0xff000000
+#define PPU_POOLING_KERNEL_CFG_RESERVED_0__SHIFT 24
+static inline uint32_t PPU_POOLING_KERNEL_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_RESERVED_0__SHIFT) & PPU_POOLING_KERNEL_CFG_RESERVED_0__MASK;
+}
+#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__MASK 0x00f00000
+#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__SHIFT 20
+static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_HEIGHT__MASK;
+}
+#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__MASK 0x000f0000
+#define PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__SHIFT 16
+static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_STRIDE_WIDTH__MASK;
+}
+#define PPU_POOLING_KERNEL_CFG_RESERVED_1__MASK 0x0000f000
+#define PPU_POOLING_KERNEL_CFG_RESERVED_1__SHIFT 12
+static inline uint32_t PPU_POOLING_KERNEL_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_RESERVED_1__SHIFT) & PPU_POOLING_KERNEL_CFG_RESERVED_1__MASK;
+}
+#define PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__MASK 0x00000f00
+#define PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__SHIFT 8
+static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_HEIGHT__MASK;
+}
+#define PPU_POOLING_KERNEL_CFG_RESERVED_2__MASK 0x000000f0
+#define PPU_POOLING_KERNEL_CFG_RESERVED_2__SHIFT 4
+static inline uint32_t PPU_POOLING_KERNEL_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_RESERVED_2__SHIFT) & PPU_POOLING_KERNEL_CFG_RESERVED_2__MASK;
+}
+#define PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__MASK 0x0000000f
+#define PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__SHIFT 0
+static inline uint32_t PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH(uint32_t val)
+{
+ return ((val) << PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__SHIFT) & PPU_POOLING_KERNEL_CFG_KERNEL_WIDTH__MASK;
+}
+
+#define REG_PPU_RECIP_KERNEL_WIDTH 0x00006038
+#define PPU_RECIP_KERNEL_WIDTH_RESERVED_0__MASK 0xfffe0000
+#define PPU_RECIP_KERNEL_WIDTH_RESERVED_0__SHIFT 17
+static inline uint32_t PPU_RECIP_KERNEL_WIDTH_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RECIP_KERNEL_WIDTH_RESERVED_0__SHIFT) & PPU_RECIP_KERNEL_WIDTH_RESERVED_0__MASK;
+}
+#define PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__MASK 0x0001ffff
+#define PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__SHIFT 0
+static inline uint32_t PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH(uint32_t val)
+{
+ return ((val) << PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__SHIFT) & PPU_RECIP_KERNEL_WIDTH_RECIP_KERNEL_WIDTH__MASK;
+}
+
+#define REG_PPU_RECIP_KERNEL_HEIGHT 0x0000603c
+#define PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__MASK 0xfffe0000
+#define PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__SHIFT 17
+static inline uint32_t PPU_RECIP_KERNEL_HEIGHT_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__SHIFT) & PPU_RECIP_KERNEL_HEIGHT_RESERVED_0__MASK;
+}
+#define PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__MASK 0x0001ffff
+#define PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__SHIFT 0
+static inline uint32_t PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT(uint32_t val)
+{
+ return ((val) << PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__SHIFT) & PPU_RECIP_KERNEL_HEIGHT_RECIP_KERNEL_HEIGHT__MASK;
+}
+
+#define REG_PPU_POOLING_PADDING_CFG 0x00006040
+#define PPU_POOLING_PADDING_CFG_RESERVED_0__MASK 0xffff8000
+#define PPU_POOLING_PADDING_CFG_RESERVED_0__SHIFT 15
+static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_0__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_0__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_PAD_BOTTOM__MASK 0x00007000
+#define PPU_POOLING_PADDING_CFG_PAD_BOTTOM__SHIFT 12
+static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_BOTTOM(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_PAD_BOTTOM__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_BOTTOM__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_RESERVED_1__MASK 0x00000800
+#define PPU_POOLING_PADDING_CFG_RESERVED_1__SHIFT 11
+static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_1__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_1__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_PAD_RIGHT__MASK 0x00000700
+#define PPU_POOLING_PADDING_CFG_PAD_RIGHT__SHIFT 8
+static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_RIGHT(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_PAD_RIGHT__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_RIGHT__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_RESERVED_2__MASK 0x00000080
+#define PPU_POOLING_PADDING_CFG_RESERVED_2__SHIFT 7
+static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_2(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_2__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_2__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_PAD_TOP__MASK 0x00000070
+#define PPU_POOLING_PADDING_CFG_PAD_TOP__SHIFT 4
+static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_TOP(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_PAD_TOP__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_TOP__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_RESERVED_3__MASK 0x00000008
+#define PPU_POOLING_PADDING_CFG_RESERVED_3__SHIFT 3
+static inline uint32_t PPU_POOLING_PADDING_CFG_RESERVED_3(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_RESERVED_3__SHIFT) & PPU_POOLING_PADDING_CFG_RESERVED_3__MASK;
+}
+#define PPU_POOLING_PADDING_CFG_PAD_LEFT__MASK 0x00000007
+#define PPU_POOLING_PADDING_CFG_PAD_LEFT__SHIFT 0
+static inline uint32_t PPU_POOLING_PADDING_CFG_PAD_LEFT(uint32_t val)
+{
+ return ((val) << PPU_POOLING_PADDING_CFG_PAD_LEFT__SHIFT) & PPU_POOLING_PADDING_CFG_PAD_LEFT__MASK;
+}
+
+#define REG_PPU_PADDING_VALUE_1_CFG 0x00006044
+#define PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__MASK 0xffffffff
+#define PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__SHIFT 0
+static inline uint32_t PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0(uint32_t val)
+{
+ return ((val) << PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__SHIFT) & PPU_PADDING_VALUE_1_CFG_PAD_VALUE_0__MASK;
+}
+
+#define REG_PPU_PADDING_VALUE_2_CFG 0x00006048
+#define PPU_PADDING_VALUE_2_CFG_RESERVED_0__MASK 0xfffffff8
+#define PPU_PADDING_VALUE_2_CFG_RESERVED_0__SHIFT 3
+static inline uint32_t PPU_PADDING_VALUE_2_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_PADDING_VALUE_2_CFG_RESERVED_0__SHIFT) & PPU_PADDING_VALUE_2_CFG_RESERVED_0__MASK;
+}
+#define PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__MASK 0x00000007
+#define PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__SHIFT 0
+static inline uint32_t PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1(uint32_t val)
+{
+ return ((val) << PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__SHIFT) & PPU_PADDING_VALUE_2_CFG_PAD_VALUE_1__MASK;
+}
+
+#define REG_PPU_DST_BASE_ADDR 0x00006070
+#define PPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK 0xfffffff0
+#define PPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT 4
+static inline uint32_t PPU_DST_BASE_ADDR_DST_BASE_ADDR(uint32_t val)
+{
+ return ((val) << PPU_DST_BASE_ADDR_DST_BASE_ADDR__SHIFT) & PPU_DST_BASE_ADDR_DST_BASE_ADDR__MASK;
+}
+#define PPU_DST_BASE_ADDR_RESERVED_0__MASK 0x0000000f
+#define PPU_DST_BASE_ADDR_RESERVED_0__SHIFT 0
+static inline uint32_t PPU_DST_BASE_ADDR_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DST_BASE_ADDR_RESERVED_0__SHIFT) & PPU_DST_BASE_ADDR_RESERVED_0__MASK;
+}
+
+#define REG_PPU_DST_SURF_STRIDE 0x0000607c
+#define PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK 0xfffffff0
+#define PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT 4
+static inline uint32_t PPU_DST_SURF_STRIDE_DST_SURF_STRIDE(uint32_t val)
+{
+ return ((val) << PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__SHIFT) & PPU_DST_SURF_STRIDE_DST_SURF_STRIDE__MASK;
+}
+#define PPU_DST_SURF_STRIDE_RESERVED_0__MASK 0x0000000f
+#define PPU_DST_SURF_STRIDE_RESERVED_0__SHIFT 0
+static inline uint32_t PPU_DST_SURF_STRIDE_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_DST_SURF_STRIDE_RESERVED_0__SHIFT) & PPU_DST_SURF_STRIDE_RESERVED_0__MASK;
+}
+
+#define REG_PPU_DATA_FORMAT 0x00006084
+#define PPU_DATA_FORMAT_INDEX_ADD__MASK 0xfffffff0
+#define PPU_DATA_FORMAT_INDEX_ADD__SHIFT 4
+static inline uint32_t PPU_DATA_FORMAT_INDEX_ADD(uint32_t val)
+{
+ return ((val) << PPU_DATA_FORMAT_INDEX_ADD__SHIFT) & PPU_DATA_FORMAT_INDEX_ADD__MASK;
+}
+#define PPU_DATA_FORMAT_DPU_FLYIN__MASK 0x00000008
+#define PPU_DATA_FORMAT_DPU_FLYIN__SHIFT 3
+static inline uint32_t PPU_DATA_FORMAT_DPU_FLYIN(uint32_t val)
+{
+ return ((val) << PPU_DATA_FORMAT_DPU_FLYIN__SHIFT) & PPU_DATA_FORMAT_DPU_FLYIN__MASK;
+}
+#define PPU_DATA_FORMAT_PROC_PRECISION__MASK 0x00000007
+#define PPU_DATA_FORMAT_PROC_PRECISION__SHIFT 0
+static inline uint32_t PPU_DATA_FORMAT_PROC_PRECISION(uint32_t val)
+{
+ return ((val) << PPU_DATA_FORMAT_PROC_PRECISION__SHIFT) & PPU_DATA_FORMAT_PROC_PRECISION__MASK;
+}
+
+#define REG_PPU_MISC_CTRL 0x000060dc
+#define PPU_MISC_CTRL_SURF_LEN__MASK 0xffff0000
+#define PPU_MISC_CTRL_SURF_LEN__SHIFT 16
+static inline uint32_t PPU_MISC_CTRL_SURF_LEN(uint32_t val)
+{
+ return ((val) << PPU_MISC_CTRL_SURF_LEN__SHIFT) & PPU_MISC_CTRL_SURF_LEN__MASK;
+}
+#define PPU_MISC_CTRL_RESERVED_0__MASK 0x0000fe00
+#define PPU_MISC_CTRL_RESERVED_0__SHIFT 9
+static inline uint32_t PPU_MISC_CTRL_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_MISC_CTRL_RESERVED_0__SHIFT) & PPU_MISC_CTRL_RESERVED_0__MASK;
+}
+#define PPU_MISC_CTRL_MC_SURF_OUT__MASK 0x00000100
+#define PPU_MISC_CTRL_MC_SURF_OUT__SHIFT 8
+static inline uint32_t PPU_MISC_CTRL_MC_SURF_OUT(uint32_t val)
+{
+ return ((val) << PPU_MISC_CTRL_MC_SURF_OUT__SHIFT) & PPU_MISC_CTRL_MC_SURF_OUT__MASK;
+}
+#define PPU_MISC_CTRL_NONALIGN__MASK 0x00000080
+#define PPU_MISC_CTRL_NONALIGN__SHIFT 7
+static inline uint32_t PPU_MISC_CTRL_NONALIGN(uint32_t val)
+{
+ return ((val) << PPU_MISC_CTRL_NONALIGN__SHIFT) & PPU_MISC_CTRL_NONALIGN__MASK;
+}
+#define PPU_MISC_CTRL_RESERVED_1__MASK 0x00000070
+#define PPU_MISC_CTRL_RESERVED_1__SHIFT 4
+static inline uint32_t PPU_MISC_CTRL_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_MISC_CTRL_RESERVED_1__SHIFT) & PPU_MISC_CTRL_RESERVED_1__MASK;
+}
+#define PPU_MISC_CTRL_BURST_LEN__MASK 0x0000000f
+#define PPU_MISC_CTRL_BURST_LEN__SHIFT 0
+static inline uint32_t PPU_MISC_CTRL_BURST_LEN(uint32_t val)
+{
+ return ((val) << PPU_MISC_CTRL_BURST_LEN__SHIFT) & PPU_MISC_CTRL_BURST_LEN__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_S_STATUS 0x00007000
+#define PPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK 0xfffc0000
+#define PPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT 18
+static inline uint32_t PPU_RDMA_RDMA_S_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_STATUS_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_S_STATUS_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK 0x00030000
+#define PPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT 16
+static inline uint32_t PPU_RDMA_RDMA_S_STATUS_STATUS_1(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_STATUS_STATUS_1__SHIFT) & PPU_RDMA_RDMA_S_STATUS_STATUS_1__MASK;
+}
+#define PPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK 0x0000fffc
+#define PPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT 2
+static inline uint32_t PPU_RDMA_RDMA_S_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_STATUS_RESERVED_1__SHIFT) & PPU_RDMA_RDMA_S_STATUS_RESERVED_1__MASK;
+}
+#define PPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK 0x00000003
+#define PPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_S_STATUS_STATUS_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_STATUS_STATUS_0__SHIFT) & PPU_RDMA_RDMA_S_STATUS_STATUS_0__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_S_POINTER 0x00007004
+#define PPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK 0xfffe0000
+#define PPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT 17
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_S_POINTER_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK 0x00010000
+#define PPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT 16
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_EXECUTER(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_EXECUTER__SHIFT) & PPU_RDMA_RDMA_S_POINTER_EXECUTER__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK 0x0000ffc0
+#define PPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT 6
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_RESERVED_1(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_RESERVED_1__SHIFT) & PPU_RDMA_RDMA_S_POINTER_RESERVED_1__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK 0x00000020
+#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT 5
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__SHIFT) & PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_CLEAR__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK 0x00000010
+#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT 4
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER_PP_CLEAR__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK 0x00000008
+#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT 3
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER_PP_MODE__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK 0x00000004
+#define PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT 2
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__SHIFT) & PPU_RDMA_RDMA_S_POINTER_EXECUTER_PP_EN__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK 0x00000002
+#define PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT 1
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER_PP_EN__MASK;
+}
+#define PPU_RDMA_RDMA_S_POINTER_POINTER__MASK 0x00000001
+#define PPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_S_POINTER_POINTER(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_S_POINTER_POINTER__SHIFT) & PPU_RDMA_RDMA_S_POINTER_POINTER__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_OPERATION_ENABLE 0x00007008
+#define PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK 0xfffffffe
+#define PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT 1
+static inline uint32_t PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK 0x00000001
+#define PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__SHIFT) & PPU_RDMA_RDMA_OPERATION_ENABLE_OP_EN__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_CUBE_IN_WIDTH 0x0000700c
+#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__MASK 0xffffe000
+#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_WIDTH_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK 0x00001fff
+#define PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_WIDTH_CUBE_IN_WIDTH__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_CUBE_IN_HEIGHT 0x00007010
+#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__MASK 0xffffe000
+#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_HEIGHT_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK 0x00001fff
+#define PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_HEIGHT_CUBE_IN_HEIGHT__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_CUBE_IN_CHANNEL 0x00007014
+#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__MASK 0xffffe000
+#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT 13
+static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_CHANNEL_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK 0x00001fff
+#define PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__SHIFT) & PPU_RDMA_RDMA_CUBE_IN_CHANNEL_CUBE_IN_CHANNEL__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_SRC_BASE_ADDR 0x0000701c
+#define PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK 0xffffffff
+#define PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__SHIFT) & PPU_RDMA_RDMA_SRC_BASE_ADDR_SRC_BASE_ADDR__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_SRC_LINE_STRIDE 0x00007024
+#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__MASK 0xfffffff0
+#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__SHIFT 4
+static inline uint32_t PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__SHIFT) & PPU_RDMA_RDMA_SRC_LINE_STRIDE_SRC_LINE_STRIDE__MASK;
+}
+#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__MASK 0x0000000f
+#define PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_SRC_LINE_STRIDE_RESERVED_0__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_SRC_SURF_STRIDE 0x00007028
+#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__MASK 0xfffffff0
+#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__SHIFT 4
+static inline uint32_t PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__SHIFT) & PPU_RDMA_RDMA_SRC_SURF_STRIDE_SRC_SURF_STRIDE__MASK;
+}
+#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__MASK 0x0000000f
+#define PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_SRC_SURF_STRIDE_RESERVED_0__MASK;
+}
+
+#define REG_PPU_RDMA_RDMA_DATA_FORMAT 0x00007030
+#define PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__MASK 0xfffffffc
+#define PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__SHIFT 2
+static inline uint32_t PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__SHIFT) & PPU_RDMA_RDMA_DATA_FORMAT_RESERVED_0__MASK;
+}
+#define PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__MASK 0x00000003
+#define PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__SHIFT 0
+static inline uint32_t PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION(uint32_t val)
+{
+ return ((val) << PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__SHIFT) & PPU_RDMA_RDMA_DATA_FORMAT_IN_PRECISION__MASK;
+}
+
+#define REG_DDMA_CFG_OUTSTANDING 0x00008000
+#define DDMA_CFG_OUTSTANDING_RESERVED_0__MASK 0xffff0000
+#define DDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT 16
+static inline uint32_t DDMA_CFG_OUTSTANDING_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT) & DDMA_CFG_OUTSTANDING_RESERVED_0__MASK;
+}
+#define DDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK 0x0000ff00
+#define DDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT 8
+static inline uint32_t DDMA_CFG_OUTSTANDING_WR_OS_CNT(uint32_t val)
+{
+ return ((val) << DDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT) & DDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK;
+}
+#define DDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK 0x000000ff
+#define DDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT 0
+static inline uint32_t DDMA_CFG_OUTSTANDING_RD_OS_CNT(uint32_t val)
+{
+ return ((val) << DDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT) & DDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK;
+}
+
+#define REG_DDMA_RD_WEIGHT_0 0x00008004
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK 0xff000000
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT 24
+static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP(uint32_t val)
+{
+ return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK;
+}
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK 0x00ff0000
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT 16
+static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU(uint32_t val)
+{
+ return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK;
+}
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK 0x0000ff00
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT 8
+static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL(uint32_t val)
+{
+ return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK;
+}
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK 0x000000ff
+#define DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT 0
+static inline uint32_t DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE(uint32_t val)
+{
+ return ((val) << DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT) & DDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK;
+}
+
+#define REG_DDMA_WR_WEIGHT_0 0x00008008
+#define DDMA_WR_WEIGHT_0_RESERVED_0__MASK 0xffff0000
+#define DDMA_WR_WEIGHT_0_RESERVED_0__SHIFT 16
+static inline uint32_t DDMA_WR_WEIGHT_0_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_WR_WEIGHT_0_RESERVED_0__SHIFT) & DDMA_WR_WEIGHT_0_RESERVED_0__MASK;
+}
+#define DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK 0x0000ff00
+#define DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT 8
+static inline uint32_t DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP(uint32_t val)
+{
+ return ((val) << DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT) & DDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK;
+}
+#define DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK 0x000000ff
+#define DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT 0
+static inline uint32_t DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU(uint32_t val)
+{
+ return ((val) << DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT) & DDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK;
+}
+
+#define REG_DDMA_CFG_ID_ERROR 0x0000800c
+#define DDMA_CFG_ID_ERROR_RESERVED_0__MASK 0xfffffc00
+#define DDMA_CFG_ID_ERROR_RESERVED_0__SHIFT 10
+static inline uint32_t DDMA_CFG_ID_ERROR_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_ID_ERROR_RESERVED_0__SHIFT) & DDMA_CFG_ID_ERROR_RESERVED_0__MASK;
+}
+#define DDMA_CFG_ID_ERROR_WR_RESP_ID__MASK 0x000003c0
+#define DDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT 6
+static inline uint32_t DDMA_CFG_ID_ERROR_WR_RESP_ID(uint32_t val)
+{
+ return ((val) << DDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT) & DDMA_CFG_ID_ERROR_WR_RESP_ID__MASK;
+}
+#define DDMA_CFG_ID_ERROR_RESERVED_1__MASK 0x00000020
+#define DDMA_CFG_ID_ERROR_RESERVED_1__SHIFT 5
+static inline uint32_t DDMA_CFG_ID_ERROR_RESERVED_1(uint32_t val)
+{
+ return ((val) << DDMA_CFG_ID_ERROR_RESERVED_1__SHIFT) & DDMA_CFG_ID_ERROR_RESERVED_1__MASK;
+}
+#define DDMA_CFG_ID_ERROR_RD_RESP_ID__MASK 0x0000001f
+#define DDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT 0
+static inline uint32_t DDMA_CFG_ID_ERROR_RD_RESP_ID(uint32_t val)
+{
+ return ((val) << DDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT) & DDMA_CFG_ID_ERROR_RD_RESP_ID__MASK;
+}
+
+#define REG_DDMA_RD_WEIGHT_1 0x00008010
+#define DDMA_RD_WEIGHT_1_RESERVED_0__MASK 0xffffff00
+#define DDMA_RD_WEIGHT_1_RESERVED_0__SHIFT 8
+static inline uint32_t DDMA_RD_WEIGHT_1_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_RD_WEIGHT_1_RESERVED_0__SHIFT) & DDMA_RD_WEIGHT_1_RESERVED_0__MASK;
+}
+#define DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK 0x000000ff
+#define DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT 0
+static inline uint32_t DDMA_RD_WEIGHT_1_RD_WEIGHT_PC(uint32_t val)
+{
+ return ((val) << DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT) & DDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK;
+}
+
+#define REG_DDMA_CFG_DMA_FIFO_CLR 0x00008014
+#define DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK 0xfffffffe
+#define DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT 1
+static inline uint32_t DDMA_CFG_DMA_FIFO_CLR_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT) & DDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK;
+}
+#define DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK 0x00000001
+#define DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT 0
+static inline uint32_t DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT) & DDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK;
+}
+
+#define REG_DDMA_CFG_DMA_ARB 0x00008018
+#define DDMA_CFG_DMA_ARB_RESERVED_0__MASK 0xfffffc00
+#define DDMA_CFG_DMA_ARB_RESERVED_0__SHIFT 10
+static inline uint32_t DDMA_CFG_DMA_ARB_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_RESERVED_0__SHIFT) & DDMA_CFG_DMA_ARB_RESERVED_0__MASK;
+}
+#define DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK 0x00000200
+#define DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT 9
+static inline uint32_t DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT) & DDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK;
+}
+#define DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK 0x00000100
+#define DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT 8
+static inline uint32_t DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT) & DDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK;
+}
+#define DDMA_CFG_DMA_ARB_RESERVED_1__MASK 0x00000080
+#define DDMA_CFG_DMA_ARB_RESERVED_1__SHIFT 7
+static inline uint32_t DDMA_CFG_DMA_ARB_RESERVED_1(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_RESERVED_1__SHIFT) & DDMA_CFG_DMA_ARB_RESERVED_1__MASK;
+}
+#define DDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK 0x00000070
+#define DDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT 4
+static inline uint32_t DDMA_CFG_DMA_ARB_WR_FIX_ARB(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT) & DDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK;
+}
+#define DDMA_CFG_DMA_ARB_RESERVED_2__MASK 0x00000008
+#define DDMA_CFG_DMA_ARB_RESERVED_2__SHIFT 3
+static inline uint32_t DDMA_CFG_DMA_ARB_RESERVED_2(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_RESERVED_2__SHIFT) & DDMA_CFG_DMA_ARB_RESERVED_2__MASK;
+}
+#define DDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK 0x00000007
+#define DDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT 0
+static inline uint32_t DDMA_CFG_DMA_ARB_RD_FIX_ARB(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT) & DDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK;
+}
+
+#define REG_DDMA_CFG_DMA_RD_QOS 0x00008020
+#define DDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK 0xfffffc00
+#define DDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT 10
+static inline uint32_t DDMA_CFG_DMA_RD_QOS_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT) & DDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK;
+}
+#define DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK 0x00000300
+#define DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT 8
+static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_PC_QOS(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK;
+}
+#define DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK 0x000000c0
+#define DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT 6
+static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK;
+}
+#define DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK 0x00000030
+#define DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT 4
+static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK;
+}
+#define DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK 0x0000000c
+#define DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT 2
+static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK;
+}
+#define DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK 0x00000003
+#define DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT 0
+static inline uint32_t DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT) & DDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK;
+}
+
+#define REG_DDMA_CFG_DMA_RD_CFG 0x00008024
+#define DDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK 0xffffe000
+#define DDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT 13
+static inline uint32_t DDMA_CFG_DMA_RD_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT) & DDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK;
+}
+#define DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK 0x00001000
+#define DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT 12
+static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARLOCK(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK;
+}
+#define DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK 0x00000f00
+#define DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT 8
+static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARCACHE(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK;
+}
+#define DDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK 0x000000e0
+#define DDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT 5
+static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARPROT(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK;
+}
+#define DDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK 0x00000018
+#define DDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT 3
+static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARBURST(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK;
+}
+#define DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK 0x00000007
+#define DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT 0
+static inline uint32_t DDMA_CFG_DMA_RD_CFG_RD_ARSIZE(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT) & DDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK;
+}
+
+#define REG_DDMA_CFG_DMA_WR_CFG 0x00008028
+#define DDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK 0xffffe000
+#define DDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT 13
+static inline uint32_t DDMA_CFG_DMA_WR_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT) & DDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK;
+}
+#define DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK 0x00001000
+#define DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT 12
+static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWLOCK(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK;
+}
+#define DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK 0x00000f00
+#define DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT 8
+static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWCACHE(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK;
+}
+#define DDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK 0x000000e0
+#define DDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT 5
+static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWPROT(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK;
+}
+#define DDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK 0x00000018
+#define DDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT 3
+static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWBURST(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK;
+}
+#define DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK 0x00000007
+#define DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT 0
+static inline uint32_t DDMA_CFG_DMA_WR_CFG_WR_AWSIZE(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT) & DDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK;
+}
+
+#define REG_DDMA_CFG_DMA_WSTRB 0x0000802c
+#define DDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK 0xffffffff
+#define DDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT 0
+static inline uint32_t DDMA_CFG_DMA_WSTRB_WR_WSTRB(uint32_t val)
+{
+ return ((val) << DDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT) & DDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK;
+}
+
+#define REG_DDMA_CFG_STATUS 0x00008030
+#define DDMA_CFG_STATUS_RESERVED_0__MASK 0xfffffe00
+#define DDMA_CFG_STATUS_RESERVED_0__SHIFT 9
+static inline uint32_t DDMA_CFG_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << DDMA_CFG_STATUS_RESERVED_0__SHIFT) & DDMA_CFG_STATUS_RESERVED_0__MASK;
+}
+#define DDMA_CFG_STATUS_IDEL__MASK 0x00000100
+#define DDMA_CFG_STATUS_IDEL__SHIFT 8
+static inline uint32_t DDMA_CFG_STATUS_IDEL(uint32_t val)
+{
+ return ((val) << DDMA_CFG_STATUS_IDEL__SHIFT) & DDMA_CFG_STATUS_IDEL__MASK;
+}
+#define DDMA_CFG_STATUS_RESERVED_1__MASK 0x000000ff
+#define DDMA_CFG_STATUS_RESERVED_1__SHIFT 0
+static inline uint32_t DDMA_CFG_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << DDMA_CFG_STATUS_RESERVED_1__SHIFT) & DDMA_CFG_STATUS_RESERVED_1__MASK;
+}
+
+#define REG_SDMA_CFG_OUTSTANDING 0x00009000
+#define SDMA_CFG_OUTSTANDING_RESERVED_0__MASK 0xffff0000
+#define SDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT 16
+static inline uint32_t SDMA_CFG_OUTSTANDING_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_OUTSTANDING_RESERVED_0__SHIFT) & SDMA_CFG_OUTSTANDING_RESERVED_0__MASK;
+}
+#define SDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK 0x0000ff00
+#define SDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT 8
+static inline uint32_t SDMA_CFG_OUTSTANDING_WR_OS_CNT(uint32_t val)
+{
+ return ((val) << SDMA_CFG_OUTSTANDING_WR_OS_CNT__SHIFT) & SDMA_CFG_OUTSTANDING_WR_OS_CNT__MASK;
+}
+#define SDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK 0x000000ff
+#define SDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT 0
+static inline uint32_t SDMA_CFG_OUTSTANDING_RD_OS_CNT(uint32_t val)
+{
+ return ((val) << SDMA_CFG_OUTSTANDING_RD_OS_CNT__SHIFT) & SDMA_CFG_OUTSTANDING_RD_OS_CNT__MASK;
+}
+
+#define REG_SDMA_RD_WEIGHT_0 0x00009004
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK 0xff000000
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT 24
+static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP(uint32_t val)
+{
+ return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_PDP__MASK;
+}
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK 0x00ff0000
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT 16
+static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU(uint32_t val)
+{
+ return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_DPU__MASK;
+}
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK 0x0000ff00
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT 8
+static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL(uint32_t val)
+{
+ return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_KERNEL__MASK;
+}
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK 0x000000ff
+#define SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT 0
+static inline uint32_t SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE(uint32_t val)
+{
+ return ((val) << SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__SHIFT) & SDMA_RD_WEIGHT_0_RD_WEIGHT_FEATURE__MASK;
+}
+
+#define REG_SDMA_WR_WEIGHT_0 0x00009008
+#define SDMA_WR_WEIGHT_0_RESERVED_0__MASK 0xffff0000
+#define SDMA_WR_WEIGHT_0_RESERVED_0__SHIFT 16
+static inline uint32_t SDMA_WR_WEIGHT_0_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_WR_WEIGHT_0_RESERVED_0__SHIFT) & SDMA_WR_WEIGHT_0_RESERVED_0__MASK;
+}
+#define SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK 0x0000ff00
+#define SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT 8
+static inline uint32_t SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP(uint32_t val)
+{
+ return ((val) << SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__SHIFT) & SDMA_WR_WEIGHT_0_WR_WEIGHT_PDP__MASK;
+}
+#define SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK 0x000000ff
+#define SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT 0
+static inline uint32_t SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU(uint32_t val)
+{
+ return ((val) << SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__SHIFT) & SDMA_WR_WEIGHT_0_WR_WEIGHT_DPU__MASK;
+}
+
+#define REG_SDMA_CFG_ID_ERROR 0x0000900c
+#define SDMA_CFG_ID_ERROR_RESERVED_0__MASK 0xfffffc00
+#define SDMA_CFG_ID_ERROR_RESERVED_0__SHIFT 10
+static inline uint32_t SDMA_CFG_ID_ERROR_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_ID_ERROR_RESERVED_0__SHIFT) & SDMA_CFG_ID_ERROR_RESERVED_0__MASK;
+}
+#define SDMA_CFG_ID_ERROR_WR_RESP_ID__MASK 0x000003c0
+#define SDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT 6
+static inline uint32_t SDMA_CFG_ID_ERROR_WR_RESP_ID(uint32_t val)
+{
+ return ((val) << SDMA_CFG_ID_ERROR_WR_RESP_ID__SHIFT) & SDMA_CFG_ID_ERROR_WR_RESP_ID__MASK;
+}
+#define SDMA_CFG_ID_ERROR_RESERVED_1__MASK 0x00000020
+#define SDMA_CFG_ID_ERROR_RESERVED_1__SHIFT 5
+static inline uint32_t SDMA_CFG_ID_ERROR_RESERVED_1(uint32_t val)
+{
+ return ((val) << SDMA_CFG_ID_ERROR_RESERVED_1__SHIFT) & SDMA_CFG_ID_ERROR_RESERVED_1__MASK;
+}
+#define SDMA_CFG_ID_ERROR_RD_RESP_ID__MASK 0x0000001f
+#define SDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT 0
+static inline uint32_t SDMA_CFG_ID_ERROR_RD_RESP_ID(uint32_t val)
+{
+ return ((val) << SDMA_CFG_ID_ERROR_RD_RESP_ID__SHIFT) & SDMA_CFG_ID_ERROR_RD_RESP_ID__MASK;
+}
+
+#define REG_SDMA_RD_WEIGHT_1 0x00009010
+#define SDMA_RD_WEIGHT_1_RESERVED_0__MASK 0xffffff00
+#define SDMA_RD_WEIGHT_1_RESERVED_0__SHIFT 8
+static inline uint32_t SDMA_RD_WEIGHT_1_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_RD_WEIGHT_1_RESERVED_0__SHIFT) & SDMA_RD_WEIGHT_1_RESERVED_0__MASK;
+}
+#define SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK 0x000000ff
+#define SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT 0
+static inline uint32_t SDMA_RD_WEIGHT_1_RD_WEIGHT_PC(uint32_t val)
+{
+ return ((val) << SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__SHIFT) & SDMA_RD_WEIGHT_1_RD_WEIGHT_PC__MASK;
+}
+
+#define REG_SDMA_CFG_DMA_FIFO_CLR 0x00009014
+#define SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK 0xfffffffe
+#define SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT 1
+static inline uint32_t SDMA_CFG_DMA_FIFO_CLR_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__SHIFT) & SDMA_CFG_DMA_FIFO_CLR_RESERVED_0__MASK;
+}
+#define SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK 0x00000001
+#define SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT 0
+static inline uint32_t SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__SHIFT) & SDMA_CFG_DMA_FIFO_CLR_DMA_FIFO_CLR__MASK;
+}
+
+#define REG_SDMA_CFG_DMA_ARB 0x00009018
+#define SDMA_CFG_DMA_ARB_RESERVED_0__MASK 0xfffffc00
+#define SDMA_CFG_DMA_ARB_RESERVED_0__SHIFT 10
+static inline uint32_t SDMA_CFG_DMA_ARB_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_RESERVED_0__SHIFT) & SDMA_CFG_DMA_ARB_RESERVED_0__MASK;
+}
+#define SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK 0x00000200
+#define SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT 9
+static inline uint32_t SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__SHIFT) & SDMA_CFG_DMA_ARB_WR_ARBIT_MODEL__MASK;
+}
+#define SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK 0x00000100
+#define SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT 8
+static inline uint32_t SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__SHIFT) & SDMA_CFG_DMA_ARB_RD_ARBIT_MODEL__MASK;
+}
+#define SDMA_CFG_DMA_ARB_RESERVED_1__MASK 0x00000080
+#define SDMA_CFG_DMA_ARB_RESERVED_1__SHIFT 7
+static inline uint32_t SDMA_CFG_DMA_ARB_RESERVED_1(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_RESERVED_1__SHIFT) & SDMA_CFG_DMA_ARB_RESERVED_1__MASK;
+}
+#define SDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK 0x00000070
+#define SDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT 4
+static inline uint32_t SDMA_CFG_DMA_ARB_WR_FIX_ARB(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_WR_FIX_ARB__SHIFT) & SDMA_CFG_DMA_ARB_WR_FIX_ARB__MASK;
+}
+#define SDMA_CFG_DMA_ARB_RESERVED_2__MASK 0x00000008
+#define SDMA_CFG_DMA_ARB_RESERVED_2__SHIFT 3
+static inline uint32_t SDMA_CFG_DMA_ARB_RESERVED_2(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_RESERVED_2__SHIFT) & SDMA_CFG_DMA_ARB_RESERVED_2__MASK;
+}
+#define SDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK 0x00000007
+#define SDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT 0
+static inline uint32_t SDMA_CFG_DMA_ARB_RD_FIX_ARB(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_ARB_RD_FIX_ARB__SHIFT) & SDMA_CFG_DMA_ARB_RD_FIX_ARB__MASK;
+}
+
+#define REG_SDMA_CFG_DMA_RD_QOS 0x00009020
+#define SDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK 0xfffffc00
+#define SDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT 10
+static inline uint32_t SDMA_CFG_DMA_RD_QOS_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_QOS_RESERVED_0__SHIFT) & SDMA_CFG_DMA_RD_QOS_RESERVED_0__MASK;
+}
+#define SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK 0x00000300
+#define SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT 8
+static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_PC_QOS(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_PC_QOS__MASK;
+}
+#define SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK 0x000000c0
+#define SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT 6
+static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_PPU_QOS__MASK;
+}
+#define SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK 0x00000030
+#define SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT 4
+static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_DPU_QOS__MASK;
+}
+#define SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK 0x0000000c
+#define SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT 2
+static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_KERNEL_QOS__MASK;
+}
+#define SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK 0x00000003
+#define SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT 0
+static inline uint32_t SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__SHIFT) & SDMA_CFG_DMA_RD_QOS_RD_FEATURE_QOS__MASK;
+}
+
+#define REG_SDMA_CFG_DMA_RD_CFG 0x00009024
+#define SDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK 0xffffe000
+#define SDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT 13
+static inline uint32_t SDMA_CFG_DMA_RD_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_CFG_RESERVED_0__SHIFT) & SDMA_CFG_DMA_RD_CFG_RESERVED_0__MASK;
+}
+#define SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK 0x00001000
+#define SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT 12
+static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARLOCK(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARLOCK__MASK;
+}
+#define SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK 0x00000f00
+#define SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT 8
+static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARCACHE(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARCACHE__MASK;
+}
+#define SDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK 0x000000e0
+#define SDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT 5
+static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARPROT(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARPROT__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARPROT__MASK;
+}
+#define SDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK 0x00000018
+#define SDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT 3
+static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARBURST(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARBURST__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARBURST__MASK;
+}
+#define SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK 0x00000007
+#define SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT 0
+static inline uint32_t SDMA_CFG_DMA_RD_CFG_RD_ARSIZE(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__SHIFT) & SDMA_CFG_DMA_RD_CFG_RD_ARSIZE__MASK;
+}
+
+#define REG_SDMA_CFG_DMA_WR_CFG 0x00009028
+#define SDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK 0xffffe000
+#define SDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT 13
+static inline uint32_t SDMA_CFG_DMA_WR_CFG_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WR_CFG_RESERVED_0__SHIFT) & SDMA_CFG_DMA_WR_CFG_RESERVED_0__MASK;
+}
+#define SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK 0x00001000
+#define SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT 12
+static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWLOCK(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWLOCK__MASK;
+}
+#define SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK 0x00000f00
+#define SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT 8
+static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWCACHE(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWCACHE__MASK;
+}
+#define SDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK 0x000000e0
+#define SDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT 5
+static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWPROT(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWPROT__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWPROT__MASK;
+}
+#define SDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK 0x00000018
+#define SDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT 3
+static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWBURST(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWBURST__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWBURST__MASK;
+}
+#define SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK 0x00000007
+#define SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT 0
+static inline uint32_t SDMA_CFG_DMA_WR_CFG_WR_AWSIZE(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__SHIFT) & SDMA_CFG_DMA_WR_CFG_WR_AWSIZE__MASK;
+}
+
+#define REG_SDMA_CFG_DMA_WSTRB 0x0000902c
+#define SDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK 0xffffffff
+#define SDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT 0
+static inline uint32_t SDMA_CFG_DMA_WSTRB_WR_WSTRB(uint32_t val)
+{
+ return ((val) << SDMA_CFG_DMA_WSTRB_WR_WSTRB__SHIFT) & SDMA_CFG_DMA_WSTRB_WR_WSTRB__MASK;
+}
+
+#define REG_SDMA_CFG_STATUS 0x00009030
+#define SDMA_CFG_STATUS_RESERVED_0__MASK 0xfffffe00
+#define SDMA_CFG_STATUS_RESERVED_0__SHIFT 9
+static inline uint32_t SDMA_CFG_STATUS_RESERVED_0(uint32_t val)
+{
+ return ((val) << SDMA_CFG_STATUS_RESERVED_0__SHIFT) & SDMA_CFG_STATUS_RESERVED_0__MASK;
+}
+#define SDMA_CFG_STATUS_IDEL__MASK 0x00000100
+#define SDMA_CFG_STATUS_IDEL__SHIFT 8
+static inline uint32_t SDMA_CFG_STATUS_IDEL(uint32_t val)
+{
+ return ((val) << SDMA_CFG_STATUS_IDEL__SHIFT) & SDMA_CFG_STATUS_IDEL__MASK;
+}
+#define SDMA_CFG_STATUS_RESERVED_1__MASK 0x000000ff
+#define SDMA_CFG_STATUS_RESERVED_1__SHIFT 0
+static inline uint32_t SDMA_CFG_STATUS_RESERVED_1(uint32_t val)
+{
+ return ((val) << SDMA_CFG_STATUS_RESERVED_1__SHIFT) & SDMA_CFG_STATUS_RESERVED_1__MASK;
+}
+
+#define REG_GLOBAL_OPERATION_ENABLE 0x0000f008
+#define GLOBAL_OPERATION_ENABLE_RESERVED_0__MASK 0xffffff80
+#define GLOBAL_OPERATION_ENABLE_RESERVED_0__SHIFT 7
+static inline uint32_t GLOBAL_OPERATION_ENABLE_RESERVED_0(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_RESERVED_0__SHIFT) & GLOBAL_OPERATION_ENABLE_RESERVED_0__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__MASK 0x00000040
+#define GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__SHIFT 6
+static inline uint32_t GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_PPU_RDMA_OP_EN__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_PPU_OP_EN__MASK 0x00000020
+#define GLOBAL_OPERATION_ENABLE_PPU_OP_EN__SHIFT 5
+static inline uint32_t GLOBAL_OPERATION_ENABLE_PPU_OP_EN(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_PPU_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_PPU_OP_EN__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__MASK 0x00000010
+#define GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__SHIFT 4
+static inline uint32_t GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_DPU_RDMA_OP_EN__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_DPU_OP_EN__MASK 0x00000008
+#define GLOBAL_OPERATION_ENABLE_DPU_OP_EN__SHIFT 3
+static inline uint32_t GLOBAL_OPERATION_ENABLE_DPU_OP_EN(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_DPU_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_DPU_OP_EN__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_CORE_OP_EN__MASK 0x00000004
+#define GLOBAL_OPERATION_ENABLE_CORE_OP_EN__SHIFT 2
+static inline uint32_t GLOBAL_OPERATION_ENABLE_CORE_OP_EN(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_CORE_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_CORE_OP_EN__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_RESERVED_1__MASK 0x00000002
+#define GLOBAL_OPERATION_ENABLE_RESERVED_1__SHIFT 1
+static inline uint32_t GLOBAL_OPERATION_ENABLE_RESERVED_1(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_RESERVED_1__SHIFT) & GLOBAL_OPERATION_ENABLE_RESERVED_1__MASK;
+}
+#define GLOBAL_OPERATION_ENABLE_CNA_OP_EN__MASK 0x00000001
+#define GLOBAL_OPERATION_ENABLE_CNA_OP_EN__SHIFT 0
+static inline uint32_t GLOBAL_OPERATION_ENABLE_CNA_OP_EN(uint32_t val)
+{
+ return ((val) << GLOBAL_OPERATION_ENABLE_CNA_OP_EN__SHIFT) & GLOBAL_OPERATION_ENABLE_CNA_OP_EN__MASK;
+}
+
+#endif /* __ROCKET_REGISTERS_XML__ */
diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c
index c4d54a5326b1..06b43b678d6e 100644
--- a/drivers/accessibility/braille/braille_console.c
+++ b/drivers/accessibility/braille/braille_console.c
@@ -24,7 +24,6 @@
MODULE_AUTHOR("samuel.thibault@ens-lyon.org");
MODULE_DESCRIPTION("braille device");
-MODULE_LICENSE("GPL");
/*
* Braille device support part.
diff --git a/drivers/accessibility/speakup/Kconfig b/drivers/accessibility/speakup/Kconfig
index 07ecbbde0384..e84fb617acc4 100644
--- a/drivers/accessibility/speakup/Kconfig
+++ b/drivers/accessibility/speakup/Kconfig
@@ -46,6 +46,7 @@ if SPEAKUP
config SPEAKUP_SERIALIO
def_bool y
depends on ISA || COMPILE_TEST
+ depends on HAS_IOPORT
config SPEAKUP_SYNTH_ACNTSA
tristate "Accent SA synthesizer support"
diff --git a/drivers/accessibility/speakup/Makefile b/drivers/accessibility/speakup/Makefile
index ba69b0803d42..14ba1cca87f4 100644
--- a/drivers/accessibility/speakup/Makefile
+++ b/drivers/accessibility/speakup/Makefile
@@ -40,7 +40,7 @@ hostprogs += makemapdata
makemapdata-objs := makemapdata.o
quiet_cmd_mkmap = MKMAP $@
- cmd_mkmap = TOPDIR=$(srctree) $(obj)/makemapdata > $@
+ cmd_mkmap = TOPDIR=$(srctree) SPKDIR=$(src) $(obj)/makemapdata > $@
$(obj)/mapdata.h: $(obj)/makemapdata
$(call cmd,mkmap)
diff --git a/drivers/accessibility/speakup/devsynth.c b/drivers/accessibility/speakup/devsynth.c
index d30571663585..e3d909bd0480 100644
--- a/drivers/accessibility/speakup/devsynth.c
+++ b/drivers/accessibility/speakup/devsynth.c
@@ -7,9 +7,10 @@
#include "speakup.h"
#include "spk_priv.h"
-static int misc_registered;
+static int synth_registered, synthu_registered;
static int dev_opened;
+/* Latin1 version */
static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
size_t nbytes, loff_t *ppos)
{
@@ -34,6 +35,65 @@ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
return (ssize_t)nbytes;
}
+/* UTF-8 version */
+static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
+ size_t nbytes, loff_t *ppos)
+{
+ size_t count = nbytes, consumed, want;
+ const char __user *ptr = buffer;
+ size_t bytes;
+ unsigned long flags;
+ unsigned char buf[256];
+ u16 ubuf[256];
+ size_t in, out;
+
+ if (!synth)
+ return -ENODEV;
+
+ want = 1;
+ while (count >= want) {
+ /* Copy some UTF-8 piece from userland */
+ bytes = min(count, sizeof(buf));
+ if (copy_from_user(buf, ptr, bytes))
+ return -EFAULT;
+
+ /* Convert to u16 */
+ for (in = 0, out = 0; in < bytes; in += consumed) {
+ s32 value;
+
+ value = synth_utf8_get(buf + in, bytes - in, &consumed, &want);
+ if (value == -1) {
+ /* Invalid or incomplete */
+
+ if (want > bytes - in)
+ /* We don't have it all yet, stop here
+ * and wait for the rest
+ */
+ bytes = in;
+
+ continue;
+ }
+
+ if (value < 0x10000)
+ ubuf[out++] = value;
+ }
+
+ count -= bytes;
+ ptr += bytes;
+
+ /* And speak this up */
+ if (out) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ for (in = 0; in < out; in++)
+ synth_buffer_add(ubuf[in]);
+ synth_start();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ }
+ }
+
+ return (ssize_t)(nbytes - count);
+}
+
static ssize_t speakup_file_read(struct file *fp, char __user *buf,
size_t nbytes, loff_t *ppos)
{
@@ -62,31 +122,57 @@ static const struct file_operations synth_fops = {
.release = speakup_file_release,
};
+static const struct file_operations synthu_fops = {
+ .read = speakup_file_read,
+ .write = speakup_file_writeu,
+ .open = speakup_file_open,
+ .release = speakup_file_release,
+};
+
static struct miscdevice synth_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "synth",
.fops = &synth_fops,
};
+static struct miscdevice synthu_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "synthu",
+ .fops = &synthu_fops,
+};
+
void speakup_register_devsynth(void)
{
- if (misc_registered != 0)
- return;
-/* zero it so if register fails, deregister will not ref invalid ptrs */
- if (misc_register(&synth_device)) {
- pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
- } else {
- pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
- MISC_MAJOR, synth_device.minor);
- misc_registered = 1;
+ if (!synth_registered) {
+ if (misc_register(&synth_device)) {
+ pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
+ } else {
+ pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
+ MISC_MAJOR, synth_device.minor);
+ synth_registered = 1;
+ }
+ }
+ if (!synthu_registered) {
+ if (misc_register(&synthu_device)) {
+ pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");
+ } else {
+ pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",
+ MISC_MAJOR, synthu_device.minor);
+ synthu_registered = 1;
+ }
}
}
void speakup_unregister_devsynth(void)
{
- if (!misc_registered)
- return;
- pr_info("speakup: unregistering synth device /dev/synth\n");
- misc_deregister(&synth_device);
- misc_registered = 0;
+ if (synth_registered) {
+ pr_info("speakup: unregistering synth device /dev/synth\n");
+ misc_deregister(&synth_device);
+ synth_registered = 0;
+ }
+ if (synthu_registered) {
+ pr_info("speakup: unregistering synth device /dev/synthu\n");
+ misc_deregister(&synthu_device);
+ synthu_registered = 0;
+ }
}
diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c
index 0125000e00d9..0882bab10fb8 100644
--- a/drivers/accessibility/speakup/genmap.c
+++ b/drivers/accessibility/speakup/genmap.c
@@ -10,7 +10,6 @@
#include <stdio.h>
#include <libgen.h>
#include <string.h>
-#include <linux/version.h>
#include <ctype.h>
#include "utils.h"
diff --git a/drivers/accessibility/speakup/kobjects.c b/drivers/accessibility/speakup/kobjects.c
index 41ae24ab5d08..0dfdb6608e02 100644
--- a/drivers/accessibility/speakup/kobjects.c
+++ b/drivers/accessibility/speakup/kobjects.c
@@ -413,27 +413,24 @@ static ssize_t synth_direct_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
- u_char tmp[256];
- int len;
- int bytes;
- const char *ptr = buf;
+ char *unescaped;
unsigned long flags;
if (!synth)
return -EPERM;
- len = strlen(buf);
+ unescaped = kstrdup(buf, GFP_KERNEL);
+ if (!unescaped)
+ return -ENOMEM;
+
+ string_unescape_any_inplace(unescaped);
+
spin_lock_irqsave(&speakup_info.spinlock, flags);
- while (len > 0) {
- bytes = min_t(size_t, len, 250);
- strncpy(tmp, ptr, bytes);
- tmp[bytes] = '\0';
- string_unescape_any_inplace(tmp);
- synth_printf("%s", tmp);
- ptr += bytes;
- len -= bytes;
- }
+ synth_write(unescaped, strlen(unescaped));
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+
+ kfree(unescaped);
+
return count;
}
@@ -914,6 +911,8 @@ static struct kobj_attribute say_word_ctl_attribute =
__ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
static struct kobj_attribute spell_delay_attribute =
__ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
+static struct kobj_attribute cur_phonetic_attribute =
+ __ATTR(cur_phonetic, 0644, spk_var_show, spk_var_store);
/*
* These attributes are i18n related.
@@ -967,6 +966,7 @@ static struct attribute *main_attrs[] = {
&say_control_attribute.attr,
&say_word_ctl_attribute.attr,
&spell_delay_attribute.attr,
+ &cur_phonetic_attribute.attr,
NULL,
};
diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c
index f52265293482..e68cf1d83787 100644
--- a/drivers/accessibility/speakup/main.c
+++ b/drivers/accessibility/speakup/main.c
@@ -65,6 +65,7 @@ int spk_key_echo, spk_say_word_ctl;
int spk_say_ctrl, spk_bell_pos;
short spk_punc_mask;
int spk_punc_level, spk_reading_punc;
+int spk_cur_phonetic;
char spk_str_caps_start[MAXVARLEN + 1] = "\0";
char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
char spk_str_pause[MAXVARLEN + 1] = "\0";
@@ -573,7 +574,7 @@ static u_long get_word(struct vc_data *vc)
}
attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
buf[cnt++] = attr_ch;
- while (tmpx < vc->vc_cols - 1) {
+ while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) {
tmp_pos += 2;
tmpx++;
ch = get_char(vc, (u_short *)tmp_pos, &temp);
@@ -1171,13 +1172,13 @@ static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
if (cursor_track == read_all_mode) {
switch (value) {
case KVAL(K_SHIFT):
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
spk_shut_up &= 0xfe;
spk_do_flush();
read_all_doc(vc);
break;
case KVAL(K_CTRL):
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
cursor_track = prev_cursor_track;
spk_shut_up &= 0xfe;
spk_do_flush();
@@ -1268,20 +1269,29 @@ int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
return 0;
}
-static struct var_t spk_vars[] = {
+enum spk_vars_id {
+ BELL_POS_ID = 0, SPELL_DELAY_ID, ATTRIB_BLEEP_ID,
+ BLEEPS_ID, BLEEP_TIME_ID, PUNC_LEVEL_ID,
+ READING_PUNC_ID, CURSOR_TIME_ID, SAY_CONTROL_ID,
+ SAY_WORD_CTL_ID, NO_INTERRUPT_ID, KEY_ECHO_ID,
+ CUR_PHONETIC_ID, V_LAST_VAR_ID, NB_ID
+};
+
+static struct var_t spk_vars[NB_ID] = {
/* bell must be first to set high limit */
- {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
- {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
- {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
- {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
- {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
- {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
- {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
- {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
- {SAY_CONTROL, TOGGLE_0},
- {SAY_WORD_CTL, TOGGLE_0},
- {NO_INTERRUPT, TOGGLE_0},
- {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
+ [BELL_POS_ID] = { BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
+ [SPELL_DELAY_ID] = { SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
+ [ATTRIB_BLEEP_ID] = { ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
+ [BLEEPS_ID] = { BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
+ [BLEEP_TIME_ID] = { BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
+ [PUNC_LEVEL_ID] = { PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
+ [READING_PUNC_ID] = { READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
+ [CURSOR_TIME_ID] = { CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
+ [SAY_CONTROL_ID] = { SAY_CONTROL, TOGGLE_0},
+ [SAY_WORD_CTL_ID] = {SAY_WORD_CTL, TOGGLE_0},
+ [NO_INTERRUPT_ID] = { NO_INTERRUPT, TOGGLE_0},
+ [KEY_ECHO_ID] = { KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
+ [CUR_PHONETIC_ID] = { CUR_PHONETIC, .u.n = {NULL, 0, 0, 1, 0, 0, NULL} },
V_LAST_VAR
};
@@ -1389,7 +1399,7 @@ static void start_read_all_timer(struct vc_data *vc, enum read_all_command comma
static void kbd_fakekey2(struct vc_data *vc, enum read_all_command command)
{
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
speakup_fake_down_arrow();
start_read_all_timer(vc, command);
}
@@ -1405,7 +1415,7 @@ static void read_all_doc(struct vc_data *vc)
cursor_track = read_all_mode;
spk_reset_index_count(0);
if (get_sentence_buf(vc, 0) == -1) {
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
if (!in_keyboard_notifier)
speakup_fake_down_arrow();
start_read_all_timer(vc, RA_DOWN_ARROW);
@@ -1418,7 +1428,7 @@ static void read_all_doc(struct vc_data *vc)
static void stop_read_all(struct vc_data *vc)
{
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
cursor_track = prev_cursor_track;
spk_shut_up &= 0xfe;
spk_do_flush();
@@ -1518,7 +1528,7 @@ static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return NOTIFY_STOP;
}
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
spk_shut_up &= 0xfe;
spk_do_flush();
start_read_all_timer(vc, value + 1);
@@ -1682,7 +1692,7 @@ static void cursor_done(struct timer_list *unused)
struct vc_data *vc = vc_cons[cursor_con].d;
unsigned long flags;
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
spin_lock_irqsave(&speakup_info.spinlock, flags);
if (cursor_con != fg_console) {
is_cursor = 0;
@@ -1712,8 +1722,12 @@ static void cursor_done(struct timer_list *unused)
speakup_win_say(vc);
else if (is_cursor == 1 || is_cursor == 4)
say_line_from_to(vc, 0, vc->vc_cols, 0);
- else
- say_char(vc);
+ else {
+ if (spk_cur_phonetic == 1)
+ say_phonetic_char(vc);
+ else
+ say_char(vc);
+ }
spk_keydown = 0;
is_cursor = 0;
out:
@@ -1778,7 +1792,7 @@ static void speakup_con_update(struct vc_data *vc)
{
unsigned long flags;
- if (!speakup_console[vc->vc_num] || spk_parked)
+ if (!speakup_console[vc->vc_num] || spk_parked || !synth)
return;
if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
/* Speakup output, discard */
@@ -2319,7 +2333,7 @@ static void __exit speakup_exit(void)
speakup_unregister_devsynth();
speakup_cancel_selection();
speakup_cancel_paste();
- del_timer_sync(&cursor_timer);
+ timer_delete_sync(&cursor_timer);
kthread_stop(speakup_task);
speakup_task = NULL;
mutex_lock(&spk_mutex);
@@ -2423,7 +2437,7 @@ error_task:
error_vtnotifier:
unregister_keyboard_notifier(&keyboard_notifier_block);
- del_timer(&cursor_timer);
+ timer_delete(&cursor_timer);
error_kbdnotifier:
speakup_unregister_devsynth();
@@ -2453,5 +2467,33 @@ out:
return err;
}
+module_param_named(bell_pos, spk_vars[BELL_POS_ID].u.n.default_val, int, 0444);
+module_param_named(spell_delay, spk_vars[SPELL_DELAY_ID].u.n.default_val, int, 0444);
+module_param_named(attrib_bleep, spk_vars[ATTRIB_BLEEP_ID].u.n.default_val, int, 0444);
+module_param_named(bleeps, spk_vars[BLEEPS_ID].u.n.default_val, int, 0444);
+module_param_named(bleep_time, spk_vars[BLEEP_TIME_ID].u.n.default_val, int, 0444);
+module_param_named(punc_level, spk_vars[PUNC_LEVEL_ID].u.n.default_val, int, 0444);
+module_param_named(reading_punc, spk_vars[READING_PUNC_ID].u.n.default_val, int, 0444);
+module_param_named(cursor_time, spk_vars[CURSOR_TIME_ID].u.n.default_val, int, 0444);
+module_param_named(say_control, spk_vars[SAY_CONTROL_ID].u.n.default_val, int, 0444);
+module_param_named(say_word_ctl, spk_vars[SAY_WORD_CTL_ID].u.n.default_val, int, 0444);
+module_param_named(no_interrupt, spk_vars[NO_INTERRUPT_ID].u.n.default_val, int, 0444);
+module_param_named(key_echo, spk_vars[KEY_ECHO_ID].u.n.default_val, int, 0444);
+module_param_named(cur_phonetic, spk_vars[CUR_PHONETIC_ID].u.n.default_val, int, 0444);
+
+MODULE_PARM_DESC(bell_pos, "This works much like a typewriter bell. If for example 72 is echoed to bell_pos, it will beep the PC speaker when typing on a line past character 72.");
+MODULE_PARM_DESC(spell_delay, "This controls how fast a word is spelled when speakup's spell word review command is pressed.");
+MODULE_PARM_DESC(attrib_bleep, "Beeps the PC speaker when there is an attribute change such as background color when using speakup review commands. One = on, zero = off.");
+MODULE_PARM_DESC(bleeps, "This controls whether one hears beeps through the PC speaker when using speakup review commands.");
+MODULE_PARM_DESC(bleep_time, "This controls the duration of the PC speaker beeps speakup produces.");
+MODULE_PARM_DESC(punc_level, "Controls the level of punctuation spoken as the screen is displayed, not reviewed.");
+MODULE_PARM_DESC(reading_punc, "It controls the level of punctuation when reviewing the screen with speakup's screen review commands.");
+MODULE_PARM_DESC(cursor_time, "This controls cursor delay when using arrow keys.");
+MODULE_PARM_DESC(say_control, "This controls if speakup speaks shift, alt and control when those keys are pressed or not.");
+MODULE_PARM_DESC(say_word_ctl, "Sets the say_word_ctl on load.");
+MODULE_PARM_DESC(no_interrupt, "Controls if typing interrupts output from speakup.");
+MODULE_PARM_DESC(key_echo, "Controls if speakup speaks keys when they are typed. One = on zero = off or don't echo keys.");
+MODULE_PARM_DESC(cur_phonetic, "Controls if speakup speaks letters phonetically during navigation. One = on zero = off or don't speak phonetically.");
+
module_init(speakup_init);
module_exit(speakup_exit);
diff --git a/drivers/accessibility/speakup/makemapdata.c b/drivers/accessibility/speakup/makemapdata.c
index 81db9ebf1fff..55e4ef8a93dc 100644
--- a/drivers/accessibility/speakup/makemapdata.c
+++ b/drivers/accessibility/speakup/makemapdata.c
@@ -10,7 +10,6 @@
#include <stdio.h>
#include <libgen.h>
#include <string.h>
-#include <linux/version.h>
#include <ctype.h>
#include "utils.h"
@@ -51,12 +50,15 @@ main(int argc, char *argv[])
{
int value, i;
struct st_key *this;
- const char *dir_name;
+ const char *dir_name, *spk_dir_name;
char *cp;
dir_name = getenv("TOPDIR");
if (!dir_name)
dir_name = ".";
+ spk_dir_name = getenv("SPKDIR");
+ if (!spk_dir_name)
+ spk_dir_name = "drivers/accessibility/speakup";
bzero(key_table, sizeof(key_table));
add_key("shift", 1, is_shift);
add_key("altgr", 2, is_shift);
@@ -83,7 +85,7 @@ main(int argc, char *argv[])
add_key(def_name, value, is_input);
}
- open_input(dir_name, "drivers/accessibility/speakup/spk_priv_keyinfo.h");
+ open_input(spk_dir_name, "spk_priv_keyinfo.h");
while (get_define()) {
if (strlen(def_val) > 5) {
//if (def_val[0] == '(')
diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h
index 33594f5a7983..54f1226ea061 100644
--- a/drivers/accessibility/speakup/speakup.h
+++ b/drivers/accessibility/speakup/speakup.h
@@ -76,7 +76,9 @@ int speakup_paste_selection(struct tty_struct *tty);
void speakup_cancel_paste(void);
void speakup_register_devsynth(void);
void speakup_unregister_devsynth(void);
+s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want);
void synth_write(const char *buf, size_t count);
+void synth_writeu(const char *buf, size_t count);
int synth_supports_indexing(void);
extern struct vc_data *spk_sel_cons;
@@ -105,6 +107,7 @@ extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level;
extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps;
extern int spk_bleep_time, spk_bell_pos;
extern int spk_spell_delay, spk_key_echo;
+extern int spk_cur_phonetic;
extern short spk_punc_mask;
extern short spk_pitch_shift, synth_flags;
extern bool spk_quiet_boot;
diff --git a/drivers/accessibility/speakup/speakup_acntpc.c b/drivers/accessibility/speakup/speakup_acntpc.c
index a55b60754eb1..a27e6bbf05da 100644
--- a/drivers/accessibility/speakup/speakup_acntpc.c
+++ b/drivers/accessibility/speakup/speakup_acntpc.c
@@ -34,14 +34,23 @@ static int synth_port_control;
static int port_forced;
static unsigned int synth_portlist[] = { 0x2a8, 0 };
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\033P8" } },
- { CAPS_STOP, .u.s = {"\033P5" } },
- { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
- { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
- { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\033P8" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\033P5" } },
+ [RATE_ID] = { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
+ [PITCH_ID] = { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -305,9 +314,22 @@ static void accent_release(struct spk_synth *synth)
module_param_hw_named(port, port_forced, int, ioport, 0444);
module_param_named(start, synth_acntpc.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_acntpc);
diff --git a/drivers/accessibility/speakup/speakup_acntsa.c b/drivers/accessibility/speakup/speakup_acntsa.c
index 2697c51ed6b5..26bb9f9399d3 100644
--- a/drivers/accessibility/speakup/speakup_acntsa.c
+++ b/drivers/accessibility/speakup/speakup_acntsa.c
@@ -19,14 +19,24 @@
static int synth_probe(struct spk_synth *synth);
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\033P8" } },
- { CAPS_STOP, .u.s = {"\033P5" } },
- { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
- { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
- { VOL, .u.n = {"\033A%d", 9, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\033P8" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\033P5" } },
+ [RATE_ID] = { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
+ [PITCH_ID] = { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\033A%d", 9, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -129,10 +139,21 @@ static int synth_probe(struct spk_synth *synth)
module_param_named(ser, synth_acntsa.ser, int, 0444);
module_param_named(dev, synth_acntsa.dev_name, charp, 0444);
module_param_named(start, synth_acntsa.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_acntsa);
diff --git a/drivers/accessibility/speakup/speakup_apollo.c b/drivers/accessibility/speakup/speakup_apollo.c
index c84a7e0864b7..d2fbb3f57221 100644
--- a/drivers/accessibility/speakup/speakup_apollo.c
+++ b/drivers/accessibility/speakup/speakup_apollo.c
@@ -24,15 +24,28 @@
static void do_catch_up(struct spk_synth *synth);
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"cap, " } },
- { CAPS_STOP, .u.s = {"" } },
- { RATE, .u.n = {"@W%d", 6, 1, 9, 0, 0, NULL } },
- { PITCH, .u.n = {"@F%x", 10, 0, 15, 0, 0, NULL } },
- { VOL, .u.n = {"@A%x", 10, 0, 15, 0, 0, NULL } },
- { VOICE, .u.n = {"@V%d", 1, 1, 6, 0, 0, NULL } },
- { LANG, .u.n = {"@=%d,", 1, 1, 4, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, VOICE_ID, LANG_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"cap, " } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"" } },
+ [RATE_ID] = { RATE, .u.n = {"@W%d", 6, 1, 9, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"@F%x", 10, 0, 15, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"@A%x", 10, 0, 15, 0, 0, NULL } },
+ [VOICE_ID] = { VOICE, .u.n = {"@V%d", 1, 1, 6, 0, 0, NULL } },
+ [LANG_ID] = { LANG, .u.n = {"@=%d,", 1, 1, 4, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -193,10 +206,25 @@ static void do_catch_up(struct spk_synth *synth)
module_param_named(ser, synth_apollo.ser, int, 0444);
module_param_named(dev, synth_apollo.dev_name, charp, 0444);
module_param_named(start, synth_apollo.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(lang, vars[LANG_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(voice, "Set the voice variable on load.");
+MODULE_PARM_DESC(lang, "Set the lang variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
+
module_spk_synth(synth_apollo);
diff --git a/drivers/accessibility/speakup/speakup_audptr.c b/drivers/accessibility/speakup/speakup_audptr.c
index 4d16d60db9b2..55813f3e40ff 100644
--- a/drivers/accessibility/speakup/speakup_audptr.c
+++ b/drivers/accessibility/speakup/speakup_audptr.c
@@ -19,15 +19,24 @@
static int synth_probe(struct spk_synth *synth);
static void synth_flush(struct spk_synth *synth);
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x05[f99]" } },
- { CAPS_STOP, .u.s = {"\x05[f80]" } },
- { RATE, .u.n = {"\x05[r%d]", 10, 0, 20, 100, -10, NULL } },
- { PITCH, .u.n = {"\x05[f%d]", 80, 39, 4500, 0, 0, NULL } },
- { VOL, .u.n = {"\x05[g%d]", 21, 0, 40, 0, 0, NULL } },
- { TONE, .u.n = {"\x05[s%d]", 9, 0, 63, 0, 0, NULL } },
- { PUNCT, .u.n = {"\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID, PUNCT_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x05[f99]" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x05[f80]" } },
+ [RATE_ID] = { RATE, .u.n = {"\x05[r%d]", 10, 0, 20, 100, -10, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x05[f%d]", 80, 39, 4500, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x05[g%d]", 21, 0, 40, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x05[s%d]", 9, 0, 63, 0, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -158,10 +167,25 @@ static int synth_probe(struct spk_synth *synth)
module_param_named(ser, synth_audptr.ser, int, 0444);
module_param_named(dev, synth_audptr.dev_name, charp, 0444);
module_param_named(start, synth_audptr.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_audptr);
diff --git a/drivers/accessibility/speakup/speakup_bns.c b/drivers/accessibility/speakup/speakup_bns.c
index b8103eb117b8..60507756499c 100644
--- a/drivers/accessibility/speakup/speakup_bns.c
+++ b/drivers/accessibility/speakup/speakup_bns.c
@@ -16,14 +16,23 @@
#define SYNTH_CLEAR 0x18
#define PROCSPEECH '\r'
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x05\x31\x32P" } },
- { CAPS_STOP, .u.s = {"\x05\x38P" } },
- { RATE, .u.n = {"\x05%dE", 8, 1, 16, 0, 0, NULL } },
- { PITCH, .u.n = {"\x05%dP", 8, 0, 16, 0, 0, NULL } },
- { VOL, .u.n = {"\x05%dV", 8, 0, 16, 0, 0, NULL } },
- { TONE, .u.n = {"\x05%dT", 8, 0, 16, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x05\x31\x32P" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x05\x38P" } },
+ [RATE_ID] = { RATE, .u.n = {"\x05%dE", 8, 1, 16, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x05%dP", 8, 0, 16, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x05%dV", 8, 0, 16, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x05%dT", 8, 0, 16, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -113,10 +122,21 @@ static struct spk_synth synth_bns = {
module_param_named(ser, synth_bns.ser, int, 0444);
module_param_named(dev, synth_bns.dev_name, charp, 0444);
module_param_named(start, synth_bns.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
module_spk_synth(synth_bns);
diff --git a/drivers/accessibility/speakup/speakup_decext.c b/drivers/accessibility/speakup/speakup_decext.c
index eaebf62300a4..271bcf279bf9 100644
--- a/drivers/accessibility/speakup/speakup_decext.c
+++ b/drivers/accessibility/speakup/speakup_decext.c
@@ -38,16 +38,25 @@ static void synth_flush(struct spk_synth *synth);
static int in_escape;
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"[:dv ap 222]" } },
- { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
- { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL } },
- { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL } },
- { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
- { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL } },
- { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
- { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID, INFLECTION_ID,
+ VOL_ID, PUNCT_ID, VOICE_ID,
+ DIRECT_ID, V_LAST_ID,
+ NB_ID,
+};
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 222]" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
+ [RATE_ID] = { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL } },
+ [INFLECTION_ID] = { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
+ [VOICE_ID] = { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -225,10 +234,25 @@ static void synth_flush(struct spk_synth *synth)
module_param_named(ser, synth_decext.ser, int, 0444);
module_param_named(dev, synth_decext.dev_name, charp, 0444);
module_param_named(start, synth_decext.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(inflection, "Set the inflection variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(voice, "Set the voice variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
module_spk_synth(synth_decext);
diff --git a/drivers/accessibility/speakup/speakup_decpc.c b/drivers/accessibility/speakup/speakup_decpc.c
index dec314dee214..083ca9265805 100644
--- a/drivers/accessibility/speakup/speakup_decpc.c
+++ b/drivers/accessibility/speakup/speakup_decpc.c
@@ -134,16 +134,27 @@ static int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 };
static int in_escape, is_flushing;
static int dt_stat, dma_state;
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"[:dv ap 200]" } },
- { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
- { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } },
- { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } },
- { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
- { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } },
- { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
- { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID, INFLECTION_ID,
+ VOL_ID, PUNCT_ID, VOICE_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID,
+};
+
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 200]" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
+ [RATE_ID] = { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } },
+ [INFLECTION_ID] = { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
+ [VOICE_ID] = { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -483,8 +494,25 @@ static void dtpc_release(struct spk_synth *synth)
}
module_param_named(start, synth_dec_pc.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
+
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(inflection, "Set the inflection variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(voice, "Set the voice variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
module_spk_synth(synth_dec_pc);
diff --git a/drivers/accessibility/speakup/speakup_dectlk.c b/drivers/accessibility/speakup/speakup_dectlk.c
index 2a7e8d727904..56334405d865 100644
--- a/drivers/accessibility/speakup/speakup_dectlk.c
+++ b/drivers/accessibility/speakup/speakup_dectlk.c
@@ -40,16 +40,24 @@ static int is_flushing;
static DEFINE_SPINLOCK(flush_lock);
static DECLARE_WAIT_QUEUE_HEAD(flush);
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"[:dv ap 160] " } },
- { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } },
- { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } },
- { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL } },
- { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
- { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } },
- { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } },
- { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID, INFLECTION_ID,
+ VOL_ID, PUNCT_ID, VOICE_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID,
+};
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 160] " } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } },
+ [RATE_ID] = { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL } },
+ [INFLECTION_ID] = { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } },
+ [VOICE_ID] = { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -306,10 +314,27 @@ static void synth_flush(struct spk_synth *synth)
module_param_named(ser, synth_dectlk.ser, int, 0444);
module_param_named(dev, synth_dectlk.dev_name, charp, 0444);
module_param_named(start, synth_dectlk.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(inflection, "Set the inflection variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(voice, "Set the voice variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_dectlk);
diff --git a/drivers/accessibility/speakup/speakup_dtlk.c b/drivers/accessibility/speakup/speakup_dtlk.c
index 6f01e010aaf4..fa826568937b 100644
--- a/drivers/accessibility/speakup/speakup_dtlk.c
+++ b/drivers/accessibility/speakup/speakup_dtlk.c
@@ -37,17 +37,27 @@ static unsigned int synth_portlist[] = {
static u_char synth_status;
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x01+35p" } },
- { CAPS_STOP, .u.s = {"\x01-35p" } },
- { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
- { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
- { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
- { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
- { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
- { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID, PUNCT_ID,
+ VOICE_ID, FREQUENCY_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID,
+};
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x01+35p" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x01-35p" } },
+ [RATE_ID] = { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
+ [VOICE_ID] = { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
+ [FREQUENCY_ID] = { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -376,9 +386,27 @@ static void dtlk_release(struct spk_synth *synth)
module_param_hw_named(port, port_forced, int, ioport, 0444);
module_param_named(start, synth_dtlk.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(frequency, vars[FREQUENCY_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(voice, "Set the voice variable on load.");
+MODULE_PARM_DESC(frequency, "Set the frequency variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_dtlk);
diff --git a/drivers/accessibility/speakup/speakup_dummy.c b/drivers/accessibility/speakup/speakup_dummy.c
index 34f11cd47073..52b2c5d44576 100644
--- a/drivers/accessibility/speakup/speakup_dummy.c
+++ b/drivers/accessibility/speakup/speakup_dummy.c
@@ -18,16 +18,30 @@
#define DRV_VERSION "2.11"
#define SYNTH_CLEAR '!'
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"CAPS_START\n" } },
- { CAPS_STOP, .u.s = {"CAPS_STOP\n" } },
- { PAUSE, .u.s = {"PAUSE\n"} },
- { RATE, .u.n = {"RATE %d\n", 8, 1, 16, 0, 0, NULL } },
- { PITCH, .u.n = {"PITCH %d\n", 8, 0, 16, 0, 0, NULL } },
- { INFLECTION, .u.n = {"INFLECTION %d\n", 8, 0, 16, 0, 0, NULL } },
- { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL } },
- { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ PAUSE_ID,
+ RATE_ID, PITCH_ID, INFLECTION_ID,
+ VOL_ID, TONE_ID, PUNCT_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"CAPS_START\n" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"CAPS_STOP\n" } },
+ [PAUSE_ID] = { PAUSE, .u.s = {"PAUSE\n"} },
+ [RATE_ID] = { RATE, .u.n = {"RATE %d\n", 8, 1, 16, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"PITCH %d\n", 8, 0, 16, 0, 0, NULL } },
+ [INFLECTION_ID] = { INFLECTION, .u.n = {"INFLECTION %d\n", 8, 0, 16, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"PUNCT %d\n", 0, 0, 3, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -42,6 +56,8 @@ static struct kobj_attribute pitch_attribute =
__ATTR(pitch, 0644, spk_var_show, spk_var_store);
static struct kobj_attribute inflection_attribute =
__ATTR(inflection, 0644, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, 0644, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
__ATTR(rate, 0644, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
@@ -69,6 +85,7 @@ static struct attribute *synth_attrs[] = {
&caps_stop_attribute.attr,
&pitch_attribute.attr,
&inflection_attribute.attr,
+ &punct_attribute.attr,
&rate_attribute.attr,
&tone_attribute.attr,
&vol_attribute.attr,
@@ -125,10 +142,28 @@ static struct spk_synth synth_dummy = {
module_param_named(ser, synth_dummy.ser, int, 0444);
module_param_named(dev, synth_dummy.dev_name, charp, 0444);
module_param_named(start, synth_dummy.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(inflection, "Set the inflection variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_dummy);
diff --git a/drivers/accessibility/speakup/speakup_keypc.c b/drivers/accessibility/speakup/speakup_keypc.c
index f61b62f1ea4d..9356f6379560 100644
--- a/drivers/accessibility/speakup/speakup_keypc.c
+++ b/drivers/accessibility/speakup/speakup_keypc.c
@@ -33,12 +33,21 @@ static int synth_port;
static int port_forced;
static unsigned int synth_portlist[] = { 0x2a8, 0 };
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"[f130]" } },
- { CAPS_STOP, .u.s = {"[f90]" } },
- { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } },
- { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"[f130]" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[f90]" } },
+ [RATE_ID] = { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -305,9 +314,17 @@ static void keynote_release(struct spk_synth *synth)
module_param_hw_named(port, port_forced, int, ioport, 0444);
module_param_named(start, synth_keypc.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
+
module_spk_synth(synth_keypc);
diff --git a/drivers/accessibility/speakup/speakup_ltlk.c b/drivers/accessibility/speakup/speakup_ltlk.c
index f885cfaa27c8..1e279ae143bf 100644
--- a/drivers/accessibility/speakup/speakup_ltlk.c
+++ b/drivers/accessibility/speakup/speakup_ltlk.c
@@ -18,17 +18,28 @@
static int synth_probe(struct spk_synth *synth);
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x01+35p" } },
- { CAPS_STOP, .u.s = {"\x01-35p" } },
- { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
- { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
- { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
- { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
- { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
- { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID, PUNCT_ID,
+ VOICE_ID, FREQUENCY_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x01+35p" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x01-35p" } },
+ [RATE_ID] = { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
+ [VOICE_ID] = { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
+ [FREQUENCY_ID] = { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -160,10 +171,30 @@ static int synth_probe(struct spk_synth *synth)
module_param_named(ser, synth_ltlk.ser, int, 0444);
module_param_named(dev, synth_ltlk.dev_name, charp, 0444);
module_param_named(start, synth_ltlk.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(frequency, vars[FREQUENCY_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(voice, "Set the voice variable on load.");
+MODULE_PARM_DESC(frequency, "Set the frequency variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
module_spk_synth(synth_ltlk);
diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c
index 99f1d4ac426a..6d446824677b 100644
--- a/drivers/accessibility/speakup/speakup_soft.c
+++ b/drivers/accessibility/speakup/speakup_soft.c
@@ -26,25 +26,37 @@
static int softsynth_probe(struct spk_synth *synth);
static void softsynth_release(struct spk_synth *synth);
static int softsynth_is_alive(struct spk_synth *synth);
+static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var);
static unsigned char get_index(struct spk_synth *synth);
static struct miscdevice synth_device, synthu_device;
static int init_pos;
static int misc_registered;
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x01+3p" } },
- { CAPS_STOP, .u.s = {"\x01-3p" } },
- { PAUSE, .u.n = {"\x01P" } },
- { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } },
- { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } },
- { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } },
- { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
- { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL } },
- { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
- { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+enum default_vars_id {
+ DIRECT_ID = 0, CAPS_START_ID, CAPS_STOP_ID,
+ PAUSE_ID, RATE_ID, PITCH_ID, INFLECTION_ID,
+ VOL_ID, TONE_ID, PUNCT_ID, VOICE_ID,
+ FREQUENCY_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+static struct var_t vars[NB_ID] = {
+
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x01+3p" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x01-3p" } },
+ [PAUSE_ID] = { PAUSE, .u.n = {"\x01P" } },
+ [RATE_ID] = { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } },
+ [INFLECTION_ID] = { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"\x01%db", 0, 0, 3, 0, 0, NULL } },
+ [VOICE_ID] = { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
+ [FREQUENCY_ID] = { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
V_LAST_VAR
};
@@ -133,7 +145,7 @@ static struct spk_synth synth_soft = {
.catch_up = NULL,
.flush = NULL,
.is_alive = softsynth_is_alive,
- .synth_adjust = NULL,
+ .synth_adjust = softsynth_adjust,
.read_buff_add = NULL,
.get_index = get_index,
.indexing = {
@@ -426,9 +438,50 @@ static int softsynth_is_alive(struct spk_synth *synth)
return 0;
}
+static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var)
+{
+ struct st_var_header *punc_level_var;
+ struct var_t *var_data;
+
+ if (var->var_id != PUNC_LEVEL)
+ return 0;
+
+ /* We want to set the the speech synthesis punctuation level
+ * accordingly, so it properly tunes speaking A_PUNC characters */
+ var_data = var->data;
+ if (!var_data)
+ return 0;
+ punc_level_var = spk_get_var_header(PUNCT);
+ if (!punc_level_var)
+ return 0;
+ spk_set_num_var(var_data->u.n.value, punc_level_var, E_SET);
+
+ return 1;
+}
+
module_param_named(start, synth_soft.startup, short, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
+module_param_named(frequency, vars[FREQUENCY_ID].u.n.default_val, int, 0444);
+
+
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+MODULE_PARM_DESC(rate, "Sets the rate of the synthesizer.");
+MODULE_PARM_DESC(pitch, "Sets the pitch of the synthesizer.");
+MODULE_PARM_DESC(inflection, "Sets the inflection of the synthesizer.");
+MODULE_PARM_DESC(vol, "Sets the volume of the speech synthesizer.");
+MODULE_PARM_DESC(tone, "Sets the tone of the speech synthesizer.");
+MODULE_PARM_DESC(punct, "Sets the amount of punctuation spoken by the synthesizer.");
+MODULE_PARM_DESC(voice, "Sets the voice used by the synthesizer.");
+MODULE_PARM_DESC(frequency, "Sets the frequency of speech synthesizer.");
module_spk_synth(synth_soft);
diff --git a/drivers/accessibility/speakup/speakup_spkout.c b/drivers/accessibility/speakup/speakup_spkout.c
index 5e3bb3aa98b6..d3f26095b0ee 100644
--- a/drivers/accessibility/speakup/speakup_spkout.c
+++ b/drivers/accessibility/speakup/speakup_spkout.c
@@ -18,15 +18,26 @@
static void synth_flush(struct spk_synth *synth);
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x05P+" } },
- { CAPS_STOP, .u.s = {"\x05P-" } },
- { RATE, .u.n = {"\x05R%d", 7, 0, 9, 0, 0, NULL } },
- { PITCH, .u.n = {"\x05P%d", 3, 0, 9, 0, 0, NULL } },
- { VOL, .u.n = {"\x05V%d", 9, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\x05T%c", 8, 0, 25, 65, 0, NULL } },
- { PUNCT, .u.n = {"\x05M%c", 0, 0, 3, 0, 0, "nsma" } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID, PUNCT_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x05P+" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x05P-" } },
+ [RATE_ID] = { RATE, .u.n = {"\x05R%d", 7, 0, 9, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x05P%d", 3, 0, 9, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x05V%d", 9, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x05T%c", 8, 0, 25, 65, 0, NULL } },
+ [PUNCT_ID] = { PUNCT, .u.n = {"\x05M%c", 0, 0, 3, 0, 0, "nsma" } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -124,10 +135,24 @@ static void synth_flush(struct spk_synth *synth)
module_param_named(ser, synth_spkout.ser, int, 0444);
module_param_named(dev, synth_spkout.dev_name, charp, 0444);
module_param_named(start, synth_spkout.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(punct, "Set the punct variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
+
module_spk_synth(synth_spkout);
diff --git a/drivers/accessibility/speakup/speakup_txprt.c b/drivers/accessibility/speakup/speakup_txprt.c
index 9e781347f7eb..4d0a0d4c41f0 100644
--- a/drivers/accessibility/speakup/speakup_txprt.c
+++ b/drivers/accessibility/speakup/speakup_txprt.c
@@ -16,14 +16,29 @@
#define SYNTH_CLEAR 0x18
#define PROCSPEECH '\r' /* process speech char */
-static struct var_t vars[] = {
- { CAPS_START, .u.s = {"\x05P8" } },
- { CAPS_STOP, .u.s = {"\x05P5" } },
- { RATE, .u.n = {"\x05R%d", 5, 0, 9, 0, 0, NULL } },
- { PITCH, .u.n = {"\x05P%d", 5, 0, 9, 0, 0, NULL } },
- { VOL, .u.n = {"\x05V%d", 5, 0, 9, 0, 0, NULL } },
- { TONE, .u.n = {"\x05T%c", 12, 0, 25, 61, 0, NULL } },
- { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+
+
+
+enum default_vars_id {
+ CAPS_START_ID = 0, CAPS_STOP_ID,
+ RATE_ID, PITCH_ID,
+ VOL_ID, TONE_ID,
+ DIRECT_ID, V_LAST_VAR_ID,
+ NB_ID
+};
+
+
+
+
+
+static struct var_t vars[NB_ID] = {
+ [CAPS_START_ID] = { CAPS_START, .u.s = {"\x05P8" } },
+ [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\x05P5" } },
+ [RATE_ID] = { RATE, .u.n = {"\x05R%d", 5, 0, 9, 0, 0, NULL } },
+ [PITCH_ID] = { PITCH, .u.n = {"\x05P%d", 5, 0, 9, 0, 0, NULL } },
+ [VOL_ID] = { VOL, .u.n = {"\x05V%d", 5, 0, 9, 0, 0, NULL } },
+ [TONE_ID] = { TONE, .u.n = {"\x05T%c", 12, 0, 25, 61, 0, NULL } },
+ [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
V_LAST_VAR
};
@@ -112,10 +127,24 @@ static struct spk_synth synth_txprt = {
module_param_named(ser, synth_txprt.ser, int, 0444);
module_param_named(dev, synth_txprt.dev_name, charp, 0444);
module_param_named(start, synth_txprt.startup, short, 0444);
+module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
+module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
+module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
+module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
+module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
+
+
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+MODULE_PARM_DESC(rate, "Set the rate variable on load.");
+MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
+MODULE_PARM_DESC(vol, "Set the vol variable on load.");
+MODULE_PARM_DESC(tone, "Set the tone variable on load.");
+MODULE_PARM_DESC(direct, "Set the direct variable on load.");
+
+
module_spk_synth(synth_txprt);
diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c
index 08cf8a17754b..4c0a6e1f019d 100644
--- a/drivers/accessibility/speakup/spk_ttyio.c
+++ b/drivers/accessibility/speakup/spk_ttyio.c
@@ -71,15 +71,14 @@ static void spk_ttyio_ldisc_close(struct tty_struct *tty)
kfree(tty->disc_data);
}
-static int spk_ttyio_receive_buf2(struct tty_struct *tty,
- const unsigned char *cp,
- const char *fp, int count)
+static size_t spk_ttyio_receive_buf2(struct tty_struct *tty, const u8 *cp,
+ const u8 *fp, size_t count)
{
struct spk_ldisc_data *ldisc_data = tty->disc_data;
struct spk_synth *synth = ldisc_data->synth;
if (synth->read_buff_add) {
- int i;
+ unsigned int i;
for (i = 0; i < count; i++)
synth->read_buff_add(cp[i]);
@@ -354,6 +353,9 @@ void spk_ttyio_release(struct spk_synth *in_synth)
{
struct tty_struct *tty = in_synth->dev;
+ if (tty == NULL)
+ return;
+
tty_lock(tty);
if (tty->ops->close)
diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h
index 6a96ad94bc3f..08011518a28a 100644
--- a/drivers/accessibility/speakup/spk_types.h
+++ b/drivers/accessibility/speakup/spk_types.h
@@ -49,7 +49,7 @@ enum var_id_t {
RATE, PITCH, VOL, TONE, PUNCT, VOICE, FREQUENCY, LANG,
DIRECT, PAUSE,
CAPS_START, CAPS_STOP, CHARTAB, INFLECTION, FLUSH,
- MAXVARS
+ CUR_PHONETIC, MAXVARS
};
typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch,
@@ -195,7 +195,7 @@ struct spk_synth {
void (*catch_up)(struct spk_synth *synth);
void (*flush)(struct spk_synth *synth);
int (*is_alive)(struct spk_synth *synth);
- int (*synth_adjust)(struct st_var_header *var);
+ int (*synth_adjust)(struct spk_synth *synth, struct st_var_header *var);
void (*read_buff_add)(u_char c);
unsigned char (*get_index)(struct spk_synth *synth);
struct synth_indexing indexing;
diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c
index eea2a2fa4f01..d8addbf3ad0d 100644
--- a/drivers/accessibility/speakup/synth.c
+++ b/drivers/accessibility/speakup/synth.c
@@ -208,17 +208,104 @@ void spk_do_flush(void)
wake_up_process(speakup_task);
}
-void synth_write(const char *buf, size_t count)
+void synth_write(const char *_buf, size_t count)
{
+ const unsigned char *buf = (const unsigned char *) _buf;
+
while (count--)
synth_buffer_add(*buf++);
synth_start();
}
+/* Consume one utf-8 character from buf (that contains up to count bytes),
+ * returns the unicode codepoint if valid, -1 otherwise.
+ * In all cases, returns the number of consumed bytes in *consumed,
+ * and the minimum number of bytes that would be needed for the next character
+ * in *want.
+ */
+s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want)
+{
+ unsigned char c = buf[0];
+ int nbytes = 8 - fls(c ^ 0xff);
+ u32 value;
+ size_t i;
+
+ switch (nbytes) {
+ case 8: /* 0xff */
+ case 7: /* 0xfe */
+ case 1: /* 0x80 */
+ /* Invalid, drop */
+ *consumed = 1;
+ *want = 1;
+ return -1;
+
+ case 0:
+ /* ASCII, take as such */
+ *consumed = 1;
+ *want = 1;
+ return c;
+
+ default:
+ /* 2..6-byte UTF-8 */
+
+ if (count < nbytes) {
+ /* We don't have it all */
+ *consumed = 0;
+ *want = nbytes;
+ return -1;
+ }
+
+ /* First byte */
+ value = c & ((1u << (7 - nbytes)) - 1);
+
+ /* Other bytes */
+ for (i = 1; i < nbytes; i++) {
+ c = buf[i];
+ if ((c & 0xc0) != 0x80) {
+ /* Invalid, drop the head */
+ *consumed = i;
+ *want = 1;
+ return -1;
+ }
+ value = (value << 6) | (c & 0x3f);
+ }
+
+ *consumed = nbytes;
+ *want = 1;
+ return value;
+ }
+}
+
+void synth_writeu(const char *buf, size_t count)
+{
+ size_t i, consumed, want;
+
+ /* Convert to u16 */
+ for (i = 0; i < count; i++) {
+ s32 value;
+
+ value = synth_utf8_get(buf + i, count - i, &consumed, &want);
+ if (value == -1) {
+ /* Invalid or incomplete */
+
+ if (want > count - i)
+ /* We don't have it all, stop */
+ count = i;
+
+ continue;
+ }
+
+ if (value < 0x10000)
+ synth_buffer_add(value);
+ }
+
+ synth_start();
+}
+
void synth_printf(const char *fmt, ...)
{
va_list args;
- unsigned char buf[160], *p;
+ unsigned char buf[160];
int r;
va_start(args, fmt);
@@ -227,10 +314,7 @@ void synth_printf(const char *fmt, ...)
if (r > sizeof(buf) - 1)
r = sizeof(buf) - 1;
- p = buf;
- while (r--)
- synth_buffer_add(*p++);
- synth_start();
+ synth_writeu(buf, r);
}
EXPORT_SYMBOL_GPL(synth_printf);
@@ -437,7 +521,7 @@ void synth_release(void)
spin_lock_irqsave(&speakup_info.spinlock, flags);
pr_info("releasing synth %s\n", synth->name);
synth->alive = 0;
- del_timer(&thread_timer);
+ timer_delete(&thread_timer);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
if (synth->attributes.name)
sysfs_remove_group(speakup_kobj, &synth->attributes);
diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h
index 4bf2ee8ac246..4ce9a12f7664 100644
--- a/drivers/accessibility/speakup/utils.h
+++ b/drivers/accessibility/speakup/utils.h
@@ -54,7 +54,7 @@ static inline int oops(const char *msg, const char *info)
static inline struct st_key *hash_name(char *name)
{
- u_char *pn = (u_char *)name;
+ unsigned char *pn = (unsigned char *)name;
int hash = 0;
while (*pn) {
diff --git a/drivers/accessibility/speakup/varhandlers.c b/drivers/accessibility/speakup/varhandlers.c
index 067c0da97dcb..462f8d879053 100644
--- a/drivers/accessibility/speakup/varhandlers.c
+++ b/drivers/accessibility/speakup/varhandlers.c
@@ -48,6 +48,7 @@ static struct st_var_header var_headers[] = {
{ "chartab", CHARTAB, VAR_PROC, NULL, NULL },
{ "direct", DIRECT, VAR_NUM, NULL, NULL },
{ "pause", PAUSE, VAR_STRING, spk_str_pause, NULL },
+ { "cur_phonetic", CUR_PHONETIC, VAR_NUM, &spk_cur_phonetic, NULL },
};
static struct st_var_header *var_ptrs[MAXVARS] = { NULL, NULL, NULL };
@@ -138,6 +139,7 @@ struct st_var_header *spk_get_var_header(enum var_id_t var_id)
return NULL;
return p_header;
}
+EXPORT_SYMBOL_GPL(spk_get_var_header);
struct st_var_header *spk_var_header_by_name(const char *name)
{
@@ -221,15 +223,17 @@ int spk_set_num_var(int input, struct st_var_header *var, int how)
*p_val = val;
if (var->var_id == PUNC_LEVEL) {
spk_punc_mask = spk_punc_masks[val];
- return 0;
}
if (var_data->u.n.multiplier != 0)
val *= var_data->u.n.multiplier;
val += var_data->u.n.offset;
- if (var->var_id < FIRST_SYNTH_VAR || !synth)
+
+ if (!synth)
+ return 0;
+ if (synth->synth_adjust && synth->synth_adjust(synth, var))
+ return 0;
+ if (var->var_id < FIRST_SYNTH_VAR)
return 0;
- if (synth->synth_adjust)
- return synth->synth_adjust(var);
if (!var_data->u.n.synth_fmt)
return 0;
@@ -245,6 +249,7 @@ int spk_set_num_var(int input, struct st_var_header *var, int how)
synth_printf("%s", cp);
return 0;
}
+EXPORT_SYMBOL_GPL(spk_set_num_var);
int spk_set_string_var(const char *page, struct st_var_header *var, int len)
{
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 7802d8846a8d..ca00a5dbcf75 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -12,6 +12,7 @@ menuconfig ACPI
select PNP
select NLS
select CRC32
+ select FIRMWARE_TABLE
default y if X86
help
Advanced Configuration and Power Interface (ACPI) support for
@@ -27,9 +28,6 @@ menuconfig ACPI
Management (APM) specification. If both ACPI and APM support
are configured, ACPI is used.
- The project home page for the Linux ACPI subsystem is here:
- <https://01.org/linux-acpi>
-
Linux support for ACPI is based on Intel Corporation's ACPI
Component Architecture (ACPI CA). For more information on the
ACPI CA, see:
@@ -63,6 +61,10 @@ config ACPI_CCA_REQUIRED
config ACPI_TABLE_LIB
bool
+config ACPI_THERMAL_LIB
+ depends on THERMAL
+ bool
+
config ACPI_DEBUGGER
bool "AML debugger interface"
select ACPI_DEBUG
@@ -93,7 +95,7 @@ config ACPI_SPCR_TABLE
config ACPI_FPDT
bool "ACPI Firmware Performance Data Table (FPDT) support"
- depends on X86_64
+ depends on X86_64 || ARM64
help
Enable support for the Firmware Performance Data Table (FPDT).
This table provides information on the timing of the system
@@ -130,8 +132,17 @@ config ACPI_REV_OVERRIDE_POSSIBLE
makes it possible to force the kernel to return "5" as the supported
ACPI revision via the "acpi_rev_override" command line switch.
+config ACPI_EC
+ bool "Embedded Controller"
+ depends on HAS_IOPORT
+ default X86 || LOONGARCH
+ help
+ This driver handles communication with the microcontroller
+ on many x86/LoongArch laptops and other machines.
+
config ACPI_EC_DEBUGFS
tristate "EC read/write access through /sys/kernel/debug/ec"
+ depends on ACPI_EC
help
Say N to disable Embedded Controller /sys/kernel/debug interface
@@ -212,6 +223,7 @@ config ACPI_VIDEO
tristate "Video"
depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
+ depends on ACPI_WMI || !X86
select THERMAL
help
This driver implements the ACPI Extensions For Display Adapters
@@ -259,7 +271,7 @@ config ACPI_CPU_FREQ_PSS
config ACPI_PROCESSOR_CSTATE
def_bool y
depends on ACPI_PROCESSOR
- depends on IA64 || X86
+ depends on X86
config ACPI_PROCESSOR_IDLE
bool
@@ -283,9 +295,9 @@ config ACPI_CPPC_LIB
config ACPI_PROCESSOR
tristate "Processor"
- depends on X86 || IA64 || ARM64 || LOONGARCH
+ depends on X86 || ARM64 || LOONGARCH || RISCV
select ACPI_PROCESSOR_IDLE
- select ACPI_CPU_FREQ_PSS if X86 || IA64 || LOONGARCH
+ select ACPI_CPU_FREQ_PSS if X86 || LOONGARCH
select THERMAL
default y
help
@@ -311,7 +323,6 @@ config ACPI_HOTPLUG_CPU
bool
depends on ACPI_PROCESSOR && HOTPLUG_CPU
select ACPI_CONTAINER
- default y
config ACPI_PROCESSOR_AGGREGATOR
tristate "Processor Aggregator"
@@ -328,6 +339,7 @@ config ACPI_THERMAL
tristate "Thermal Zone"
depends on ACPI_PROCESSOR
select THERMAL
+ select ACPI_THERMAL_LIB
default y
help
This driver supports ACPI thermal zones. Most mobile and
@@ -347,7 +359,6 @@ config ACPI_CUSTOM_DSDT_FILE
depends on !STANDALONE
help
This option supports a custom DSDT by linking it into the kernel.
- See Documentation/admin-guide/acpi/dsdt-override.rst
Enter the full path name to the file which includes the AmlCode
or dsdt_aml_code declaration.
@@ -383,6 +394,7 @@ config ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD
config ACPI_DEBUG
bool "Debug Statements"
+ default y
help
The ACPI subsystem can produce debug output. Saying Y enables this
output and increases the kernel size by around 50K.
@@ -431,7 +443,7 @@ config ACPI_HOTPLUG_IOAPIC
config ACPI_SBS
tristate "Smart Battery System"
- depends on X86
+ depends on X86 && ACPI_EC
select POWER_SUPPLY
help
This driver supports the Smart Battery System, another
@@ -441,29 +453,15 @@ config ACPI_SBS
the modules will be called sbs and sbshc.
config ACPI_HED
- tristate "Hardware Error Device"
+ bool "Hardware Error Device"
help
This driver supports the Hardware Error Device (PNP0C33),
which is used to report some hardware errors notified via
SCI, mainly the corrected errors.
-config ACPI_CUSTOM_METHOD
- tristate "Allow ACPI methods to be inserted/replaced at run time"
- depends on DEBUG_FS
- help
- This debug facility allows ACPI AML methods to be inserted and/or
- replaced without rebooting the system. For details refer to:
- Documentation/firmware-guide/acpi/method-customizing.rst.
-
- NOTE: This option is security sensitive, because it allows arbitrary
- kernel memory to be written to by root (uid=0) users, allowing them
- to bypass certain security measures (e.g. if root is not allowed to
- load additional kernel modules after boot, this feature may be used
- to override that restriction).
-
config ACPI_BGRT
bool "Boottime Graphics Resource Table support"
- depends on EFI && (X86 || ARM64)
+ depends on EFI
help
This driver adds support for exposing the ACPI Boottime Graphics
Resource Table, which allows the operating system to obtain
@@ -472,7 +470,6 @@ config ACPI_BGRT
config ACPI_REDUCED_HARDWARE_ONLY
bool "Hardware-reduced ACPI support only" if EXPERT
- def_bool n
help
This config item changes the way the ACPI code is built. When this
option is selected, the kernel will use a specialized version of
@@ -482,6 +479,9 @@ config ACPI_REDUCED_HARDWARE_ONLY
If you are unsure what to do, do not enable this option.
+config ACPI_NHLT
+ bool
+
source "drivers/acpi/nfit/Kconfig"
source "drivers/acpi/numa/Kconfig"
source "drivers/acpi/apei/Kconfig"
@@ -545,10 +545,14 @@ config ACPI_PFRUT
if ARM64
source "drivers/acpi/arm64/Kconfig"
+endif
+
+if RISCV
+source "drivers/acpi/riscv/Kconfig"
+endif
config ACPI_PPTT
bool
-endif
config ACPI_PCC
bool "ACPI PCC Address Space"
@@ -567,6 +571,19 @@ config ACPI_PCC
Enable this feature if you want to set up and install the PCC Address
Space handler to handle PCC OpRegion in the firmware.
+config ACPI_FFH
+ bool "ACPI FFH Address Space"
+ default n
+ help
+ The FFH(Fixed Function Hardware) Address Space also referred as FFH
+ Operation Region allows to define platform specific opregion.
+
+ Enable this feature if you want to set up and install the FFH Address
+ Space handler to handle FFH OpRegion in the firmware.
+
+config ACPI_MRRM
+ bool
+
source "drivers/acpi/pmic/Kconfig"
config ACPI_VIOT
@@ -574,7 +591,7 @@ config ACPI_VIOT
config ACPI_PRMT
bool "Platform Runtime Mechanism Support"
- depends on EFI && (X86_64 || ARM64)
+ depends on EFI_RUNTIME_WRAPPERS && (X86_64 || ARM64)
default y
help
Platform Runtime Mechanism (PRM) is a firmware interface exposing a
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 0002eecbf870..d1b0affb844f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -5,6 +5,10 @@
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
+ifdef CONFIG_TRACE_BRANCH_PROFILING
+CFLAGS_processor_idle.o += -DDISABLE_BRANCH_PROFILING
+endif
+
#
# ACPI Boot-Time Table Parsing
#
@@ -14,7 +18,6 @@ tables.o: $(src)/../../include/$(CONFIG_ACPI_CUSTOM_DSDT_FILE) ;
endif
obj-$(CONFIG_ACPI) += tables.o
-obj-$(CONFIG_X86) += blacklist.o
#
# ACPI Core Subsystem (Interpreter)
@@ -37,29 +40,23 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o
# ACPI Bus and Device Drivers
#
acpi-y += bus.o glue.o
-acpi-y += scan.o
+acpi-y += scan.o mipi-disco-img.o
acpi-y += resource.o
acpi-y += acpi_processor.o
acpi-y += processor_core.o
acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
-acpi-y += ec.o
+acpi-$(CONFIG_ACPI_EC) += ec.o
acpi-$(CONFIG_ACPI_DOCK) += dock.o
acpi-$(CONFIG_PCI) += pci_root.o pci_link.o pci_irq.o
obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o
-acpi-$(CONFIG_PCI) += acpi_lpss.o
acpi-y += acpi_apd.o
acpi-y += acpi_platform.o
acpi-y += acpi_pnp.o
-acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
acpi-y += power.o
acpi-y += event.o
acpi-y += evged.o
acpi-y += sysfs.o
acpi-y += property.o
-acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
-acpi-$(CONFIG_X86) += x86/apple.o
-acpi-$(CONFIG_X86) += x86/utils.o
-acpi-$(CONFIG_X86) += x86/s2idle.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_FPDT) += acpi_fpdt.o
@@ -68,6 +65,8 @@ acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
acpi-$(CONFIG_ACPI_PRMT) += prmt.o
acpi-$(CONFIG_ACPI_PCC) += acpi_pcc.o
+acpi-$(CONFIG_ACPI_FFH) += acpi_ffh.o
+acpi-$(CONFIG_ACPI_MRRM) += acpi_mrrm.o
# Address translation
acpi-$(CONFIG_ACPI_ADXL) += acpi_adxl.o
@@ -83,15 +82,18 @@ obj-$(CONFIG_ACPI_TINY_POWER_BUTTON) += tiny-power-button.o
obj-$(CONFIG_ACPI_FAN) += fan.o
fan-objs := fan_core.o
fan-objs += fan_attr.o
+fan-$(CONFIG_HWMON) += fan_hwmon.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-$(CONFIG_ACPI_TAD) += acpi_tad.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-$(CONFIG_ACPI) += container.o
+obj-$(CONFIG_ACPI_THERMAL_LIB) += thermal_lib.o
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
obj-$(CONFIG_ACPI_PLATFORM_PROFILE) += platform_profile.o
obj-$(CONFIG_ACPI_NFIT) += nfit/
+obj-$(CONFIG_ACPI_NHLT) += nhlt.o
obj-$(CONFIG_ACPI_NUMA) += numa/
obj-$(CONFIG_ACPI) += acpi_memhotplug.o
obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o
@@ -100,7 +102,6 @@ obj-$(CONFIG_ACPI_SBS) += sbshc.o
obj-$(CONFIG_ACPI_SBS) += sbs.o
obj-$(CONFIG_ACPI_HED) += hed.o
obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
-obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
@@ -130,3 +131,6 @@ obj-y += dptf/
obj-$(CONFIG_ARM64) += arm64/
obj-$(CONFIG_ACPI_VIOT) += viot.o
+
+obj-$(CONFIG_RISCV) += riscv/
+obj-$(CONFIG_X86) += x86/
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index c29e41bfcf35..1f69be8f51a2 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
+#include <linux/string_choices.h>
#include <linux/acpi.h>
#include <acpi/battery.h>
@@ -32,14 +33,10 @@ MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI AC Adapter Driver");
MODULE_LICENSE("GPL");
-static int acpi_ac_add(struct acpi_device *device);
-static int acpi_ac_remove(struct acpi_device *device);
-static void acpi_ac_notify(struct acpi_device *device, u32 event);
+static int acpi_ac_probe(struct platform_device *pdev);
+static void acpi_ac_remove(struct platform_device *pdev);
-struct acpi_ac_bl {
- const char *hid;
- int hrv;
-};
+static void acpi_ac_notify(acpi_handle handle, u32 event, void *data);
static const struct acpi_device_id ac_device_ids[] = {
{"ACPI0003", 0},
@@ -55,19 +52,6 @@ static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
static int ac_sleep_before_get_state_ms;
static int ac_only;
-static struct acpi_driver acpi_ac_driver = {
- .name = "ac",
- .class = ACPI_AC_CLASS,
- .ids = ac_device_ids,
- .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
- .ops = {
- .add = acpi_ac_add,
- .remove = acpi_ac_remove,
- .notify = acpi_ac_notify,
- },
- .drv.pm = &acpi_ac_pm,
-};
-
struct acpi_ac {
struct power_supply *charger;
struct power_supply_desc charger_desc;
@@ -128,21 +112,19 @@ static int get_ac_property(struct power_supply *psy,
return 0;
}
-static enum power_supply_property ac_props[] = {
+static const enum power_supply_property ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
/* Driver Model */
-static void acpi_ac_notify(struct acpi_device *device, u32 event)
+static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
{
- struct acpi_ac *ac = acpi_driver_data(device);
-
- if (!ac)
- return;
+ struct acpi_ac *ac = data;
+ struct acpi_device *adev = ac->device;
switch (event) {
default:
- acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+ acpi_handle_debug(adev->handle, "Unsupported event [0x%x]\n",
event);
fallthrough;
case ACPI_AC_NOTIFY_STATUS:
@@ -159,11 +141,11 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event)
msleep(ac_sleep_before_get_state_ms);
acpi_ac_get_state(ac);
- acpi_bus_generate_netlink_event(device->pnp.device_class,
- dev_name(&device->dev), event,
+ acpi_bus_generate_netlink_event(adev->pnp.device_class,
+ dev_name(&adev->dev), event,
(u32) ac->state);
- acpi_notifier_call_chain(device, event, (u32) ac->state);
- kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
+ acpi_notifier_call_chain(adev, event, (u32) ac->state);
+ power_supply_changed(ac->charger);
}
}
@@ -219,51 +201,59 @@ static const struct dmi_system_id ac_dmi_table[] __initconst = {
{},
};
-static int acpi_ac_add(struct acpi_device *device)
+static int acpi_ac_probe(struct platform_device *pdev)
{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
struct power_supply_config psy_cfg = {};
- int result = 0;
- struct acpi_ac *ac = NULL;
-
-
- if (!device)
- return -EINVAL;
+ struct acpi_ac *ac;
+ int result;
ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
if (!ac)
return -ENOMEM;
- ac->device = device;
- strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_AC_CLASS);
- device->driver_data = ac;
+ ac->device = adev;
+ strscpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME);
+ strscpy(acpi_device_class(adev), ACPI_AC_CLASS);
+
+ platform_set_drvdata(pdev, ac);
result = acpi_ac_get_state(ac);
if (result)
- goto end;
+ goto err_release_ac;
psy_cfg.drv_data = ac;
- ac->charger_desc.name = acpi_device_bid(device);
+ ac->charger_desc.name = acpi_device_bid(adev);
ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS;
ac->charger_desc.properties = ac_props;
ac->charger_desc.num_properties = ARRAY_SIZE(ac_props);
ac->charger_desc.get_property = get_ac_property;
- ac->charger = power_supply_register(&ac->device->dev,
+ ac->charger = power_supply_register(&pdev->dev,
&ac->charger_desc, &psy_cfg);
if (IS_ERR(ac->charger)) {
result = PTR_ERR(ac->charger);
- goto end;
+ goto err_release_ac;
}
- pr_info("%s [%s] (%s)\n", acpi_device_name(device),
- acpi_device_bid(device), ac->state ? "on-line" : "off-line");
+ pr_info("%s [%s] (%s-line)\n", acpi_device_name(adev),
+ acpi_device_bid(adev), str_on_off(ac->state));
ac->battery_nb.notifier_call = acpi_ac_battery_notify;
register_acpi_notifier(&ac->battery_nb);
-end:
+
+ result = acpi_dev_install_notify_handler(adev, ACPI_ALL_NOTIFY,
+ acpi_ac_notify, ac);
if (result)
- kfree(ac);
+ goto err_unregister;
+
+ return 0;
+
+err_unregister:
+ power_supply_unregister(ac->charger);
+ unregister_acpi_notifier(&ac->battery_nb);
+err_release_ac:
+ kfree(ac);
return result;
}
@@ -271,21 +261,14 @@ end:
#ifdef CONFIG_PM_SLEEP
static int acpi_ac_resume(struct device *dev)
{
- struct acpi_ac *ac;
+ struct acpi_ac *ac = dev_get_drvdata(dev);
unsigned int old_state;
- if (!dev)
- return -EINVAL;
-
- ac = acpi_driver_data(to_acpi_device(dev));
- if (!ac)
- return -EINVAL;
-
old_state = ac->state;
if (acpi_ac_get_state(ac))
return 0;
if (old_state != ac->state)
- kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
+ power_supply_changed(ac->charger);
return 0;
}
@@ -293,23 +276,28 @@ static int acpi_ac_resume(struct device *dev)
#define acpi_ac_resume NULL
#endif
-static int acpi_ac_remove(struct acpi_device *device)
+static void acpi_ac_remove(struct platform_device *pdev)
{
- struct acpi_ac *ac = NULL;
-
- if (!device || !acpi_driver_data(device))
- return -EINVAL;
-
- ac = acpi_driver_data(device);
+ struct acpi_ac *ac = platform_get_drvdata(pdev);
+ acpi_dev_remove_notify_handler(ac->device, ACPI_ALL_NOTIFY,
+ acpi_ac_notify);
power_supply_unregister(ac->charger);
unregister_acpi_notifier(&ac->battery_nb);
kfree(ac);
-
- return 0;
}
+static struct platform_driver acpi_ac_driver = {
+ .probe = acpi_ac_probe,
+ .remove = acpi_ac_remove,
+ .driver = {
+ .name = "ac",
+ .acpi_match_table = ac_device_ids,
+ .pm = &acpi_ac_pm,
+ },
+};
+
static int __init acpi_ac_init(void)
{
int result;
@@ -322,7 +310,7 @@ static int __init acpi_ac_init(void)
dmi_check_system(ac_dmi_table);
- result = acpi_bus_register_driver(&acpi_ac_driver);
+ result = platform_driver_register(&acpi_ac_driver);
if (result < 0)
return -ENODEV;
@@ -331,7 +319,7 @@ static int __init acpi_ac_init(void)
static void __exit acpi_ac_exit(void)
{
- acpi_bus_unregister_driver(&acpi_ac_driver);
+ platform_driver_unregister(&acpi_ac_driver);
}
module_init(acpi_ac_init);
module_exit(acpi_ac_exit);
diff --git a/drivers/acpi/acpi_amba.c b/drivers/acpi/acpi_amba.c
deleted file mode 100644
index ab8a4e0191b1..000000000000
--- a/drivers/acpi/acpi_amba.c
+++ /dev/null
@@ -1,128 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-
-/*
- * ACPI support for platform bus type.
- *
- * Copyright (C) 2015, Linaro Ltd
- * Author: Graeme Gregory <graeme.gregory@linaro.org>
- */
-
-#include <linux/acpi.h>
-#include <linux/amba/bus.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include "internal.h"
-
-static const struct acpi_device_id amba_id_list[] = {
- {"ARMH0061", 0}, /* PL061 GPIO Device */
- {"ARMHC500", 0}, /* ARM CoreSight ETM4x */
- {"ARMHC501", 0}, /* ARM CoreSight ETR */
- {"ARMHC502", 0}, /* ARM CoreSight STM */
- {"ARMHC503", 0}, /* ARM CoreSight Debug */
- {"ARMHC979", 0}, /* ARM CoreSight TPIU */
- {"ARMHC97C", 0}, /* ARM CoreSight SoC-400 TMC, SoC-600 ETF/ETB */
- {"ARMHC98D", 0}, /* ARM CoreSight Dynamic Replicator */
- {"ARMHC9CA", 0}, /* ARM CoreSight CATU */
- {"ARMHC9FF", 0}, /* ARM CoreSight Dynamic Funnel */
- {"", 0},
-};
-
-static void amba_register_dummy_clk(void)
-{
- static struct clk *amba_dummy_clk;
-
- /* If clock already registered */
- if (amba_dummy_clk)
- return;
-
- amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 0);
- clk_register_clkdev(amba_dummy_clk, "apb_pclk", NULL);
-}
-
-static int amba_handler_attach(struct acpi_device *adev,
- const struct acpi_device_id *id)
-{
- struct amba_device *dev;
- struct resource_entry *rentry;
- struct list_head resource_list;
- bool address_found = false;
- int irq_no = 0;
- int ret;
-
- /* If the ACPI node already has a physical device attached, skip it. */
- if (adev->physical_node_count)
- return 0;
-
- dev = amba_device_alloc(dev_name(&adev->dev), 0, 0);
- if (!dev) {
- dev_err(&adev->dev, "%s(): amba_device_alloc() failed\n",
- __func__);
- return -ENOMEM;
- }
-
- INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
- if (ret < 0)
- goto err_free;
-
- list_for_each_entry(rentry, &resource_list, node) {
- switch (resource_type(rentry->res)) {
- case IORESOURCE_MEM:
- if (!address_found) {
- dev->res = *rentry->res;
- dev->res.name = dev_name(&dev->dev);
- address_found = true;
- }
- break;
- case IORESOURCE_IRQ:
- if (irq_no < AMBA_NR_IRQS)
- dev->irq[irq_no++] = rentry->res->start;
- break;
- default:
- dev_warn(&adev->dev, "Invalid resource\n");
- break;
- }
- }
-
- acpi_dev_free_resource_list(&resource_list);
-
- /*
- * If the ACPI node has a parent and that parent has a physical device
- * attached to it, that physical device should be the parent of
- * the amba device we are about to create.
- */
- if (adev->parent)
- dev->dev.parent = acpi_get_first_physical_node(adev->parent);
-
- ACPI_COMPANION_SET(&dev->dev, adev);
-
- ret = amba_device_add(dev, &iomem_resource);
- if (ret) {
- dev_err(&adev->dev, "%s(): amba_device_add() failed (%d)\n",
- __func__, ret);
- goto err_free;
- }
-
- return 1;
-
-err_free:
- amba_device_put(dev);
- return ret;
-}
-
-static struct acpi_scan_handler amba_handler = {
- .ids = amba_id_list,
- .attach = amba_handler_attach,
-};
-
-void __init acpi_amba_init(void)
-{
- amba_register_dummy_clk();
- acpi_scan_add_handler(&amba_handler);
-}
diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c
index ad245bbd965e..49539f7528c6 100644
--- a/drivers/acpi/acpi_apd.c
+++ b/drivers/acpi/acpi_apd.c
@@ -60,12 +60,6 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
}
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
-static int misc_check_res(struct acpi_resource *ares, void *data)
-{
- struct resource res;
-
- return !acpi_dev_resource_memory(ares, &res);
-}
static int fch_misc_setup(struct apd_private_data *pdata)
{
@@ -82,16 +76,17 @@ static int fch_misc_setup(struct apd_private_data *pdata)
return -ENOMEM;
INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(adev, &resource_list, misc_check_res,
- NULL);
+ ret = acpi_dev_get_memory_resources(adev, &resource_list);
if (ret < 0)
return -ENOENT;
if (!acpi_dev_get_property(adev, "clk-name", ACPI_TYPE_STRING, &obj)) {
clk_data->name = devm_kzalloc(&adev->dev, obj->string.length,
GFP_KERNEL);
+ if (!clk_data->name)
+ return -ENOMEM;
- strcpy(clk_data->name, obj->string.pointer);
+ strscpy(clk_data->name, obj->string.pointer, obj->string.length);
} else {
/* Set default name to mclk if entry missing in firmware */
clk_data->name = "mclk";
@@ -123,6 +118,11 @@ static const struct apd_device_desc wt_i2c_desc = {
.fixed_clk_rate = 150000000,
};
+static const struct apd_device_desc wt_i3c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 125000000,
+};
+
static struct property_entry uart_properties[] = {
PROPERTY_ENTRY_U32("reg-io-width", 4),
PROPERTY_ENTRY_U32("reg-shift", 2),
@@ -236,6 +236,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
{ "AMD0030", },
{ "AMD0040", APD_ADDR(fch_misc_desc)},
{ "AMDI0010", APD_ADDR(wt_i2c_desc) },
+ { "AMDI0015", APD_ADDR(wt_i3c_desc) },
{ "AMDI0019", APD_ADDR(wt_i2c_desc) },
{ "AMDI0020", APD_ADDR(cz_uart_desc) },
{ "AMDI0022", APD_ADDR(cz_uart_desc) },
diff --git a/drivers/acpi/acpi_cmos_rtc.c b/drivers/acpi/acpi_cmos_rtc.c
deleted file mode 100644
index 4cf4aef7ce0c..000000000000
--- a/drivers/acpi/acpi_cmos_rtc.c
+++ /dev/null
@@ -1,87 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ACPI support for CMOS RTC Address Space access
- *
- * Copyright (C) 2013, Intel Corporation
- * Authors: Lan Tianyu <tianyu.lan@intel.com>
- */
-
-#define pr_fmt(fmt) "ACPI: " fmt
-
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mc146818rtc.h>
-
-#include "internal.h"
-
-static const struct acpi_device_id acpi_cmos_rtc_ids[] = {
- { "PNP0B00" },
- { "PNP0B01" },
- { "PNP0B02" },
- {}
-};
-
-static acpi_status
-acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address,
- u32 bits, u64 *value64,
- void *handler_context, void *region_context)
-{
- int i;
- u8 *value = (u8 *)value64;
-
- if (address > 0xff || !value64)
- return AE_BAD_PARAMETER;
-
- if (function != ACPI_WRITE && function != ACPI_READ)
- return AE_BAD_PARAMETER;
-
- spin_lock_irq(&rtc_lock);
-
- for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value)
- if (function == ACPI_READ)
- *value = CMOS_READ(address);
- else
- CMOS_WRITE(*value, address);
-
- spin_unlock_irq(&rtc_lock);
-
- return AE_OK;
-}
-
-static int acpi_install_cmos_rtc_space_handler(struct acpi_device *adev,
- const struct acpi_device_id *id)
-{
- acpi_status status;
-
- status = acpi_install_address_space_handler(adev->handle,
- ACPI_ADR_SPACE_CMOS,
- &acpi_cmos_rtc_space_handler,
- NULL, NULL);
- if (ACPI_FAILURE(status)) {
- pr_err("Error installing CMOS-RTC region handler\n");
- return -ENODEV;
- }
-
- return 1;
-}
-
-static void acpi_remove_cmos_rtc_space_handler(struct acpi_device *adev)
-{
- if (ACPI_FAILURE(acpi_remove_address_space_handler(adev->handle,
- ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler)))
- pr_err("Error removing CMOS-RTC region handler\n");
-}
-
-static struct acpi_scan_handler cmos_rtc_handler = {
- .ids = acpi_cmos_rtc_ids,
- .attach = acpi_install_cmos_rtc_space_handler,
- .detach = acpi_remove_cmos_rtc_space_handler,
-};
-
-void __init acpi_cmos_rtc_init(void)
-{
- acpi_scan_add_handler(&cmos_rtc_handler);
-}
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index d50261d05f3a..515b20d0b698 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -569,11 +569,11 @@ static int acpi_aml_release(struct inode *inode, struct file *file)
return 0;
}
-static int acpi_aml_read_user(char __user *buf, int len)
+static ssize_t acpi_aml_read_user(char __user *buf, size_t len)
{
- int ret;
struct circ_buf *crc = &acpi_aml_io.out_crc;
- int n;
+ ssize_t ret;
+ size_t n;
char *p;
ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
@@ -582,7 +582,7 @@ static int acpi_aml_read_user(char __user *buf, int len)
/* sync head before removing logs */
smp_rmb();
p = &crc->buf[crc->tail];
- n = min(len, circ_count_to_end(crc));
+ n = min_t(size_t, len, circ_count_to_end(crc));
if (copy_to_user(buf, p, n)) {
ret = -EFAULT;
goto out;
@@ -599,8 +599,8 @@ out:
static ssize_t acpi_aml_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- int ret = 0;
- int size = 0;
+ ssize_t ret = 0;
+ ssize_t size = 0;
if (!count)
return 0;
@@ -639,11 +639,11 @@ again:
return size > 0 ? size : ret;
}
-static int acpi_aml_write_user(const char __user *buf, int len)
+static ssize_t acpi_aml_write_user(const char __user *buf, size_t len)
{
- int ret;
struct circ_buf *crc = &acpi_aml_io.in_crc;
- int n;
+ ssize_t ret;
+ size_t n;
char *p;
ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
@@ -652,7 +652,7 @@ static int acpi_aml_write_user(const char __user *buf, int len)
/* sync tail before inserting cmds */
smp_mb();
p = &crc->buf[crc->head];
- n = min(len, circ_space_to_end(crc));
+ n = min_t(size_t, len, circ_space_to_end(crc));
if (copy_from_user(p, buf, n)) {
ret = -EFAULT;
goto out;
@@ -663,14 +663,14 @@ static int acpi_aml_write_user(const char __user *buf, int len)
ret = n;
out:
acpi_aml_unlock_fifo(ACPI_AML_IN_USER, ret >= 0);
- return n;
+ return ret;
}
static ssize_t acpi_aml_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- int ret = 0;
- int size = 0;
+ ssize_t ret = 0;
+ ssize_t size = 0;
if (!count)
return 0;
diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c
index 72f1fb77abcd..f6b9562779de 100644
--- a/drivers/acpi/acpi_extlog.c
+++ b/drivers/acpi/acpi_extlog.c
@@ -12,8 +12,10 @@
#include <linux/ratelimit.h>
#include <linux/edac.h>
#include <linux/ras.h>
+#include <acpi/ghes.h>
#include <asm/cpu.h>
#include <asm/mce.h>
+#include <asm/msr.h>
#include "apei/apei-internal.h"
#include <ras/ras_event.h>
@@ -138,15 +140,20 @@ static int extlog_print(struct notifier_block *nb, unsigned long val,
int cpu = mce->extcpu;
struct acpi_hest_generic_status *estatus, *tmp;
struct acpi_hest_generic_data *gdata;
- const guid_t *fru_id = &guid_null;
- char *fru_text = "";
+ const guid_t *fru_id;
+ char *fru_text;
guid_t *sec_type;
static u32 err_seq;
estatus = extlog_elog_entry_check(cpu, bank);
- if (estatus == NULL || (mce->kflags & MCE_HANDLED_CEC))
+ if (!estatus)
return NOTIFY_DONE;
+ if (mce->kflags & MCE_HANDLED_CEC) {
+ estatus->block_status = 0;
+ return NOTIFY_DONE;
+ }
+
memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN);
/* clear record status to enable BIOS to update it again */
estatus->block_status = 0;
@@ -160,17 +167,23 @@ static int extlog_print(struct notifier_block *nb, unsigned long val,
/* log event via trace */
err_seq++;
- gdata = (struct acpi_hest_generic_data *)(tmp + 1);
- if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
- fru_id = (guid_t *)gdata->fru_id;
- if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
- fru_text = gdata->fru_text;
- sec_type = (guid_t *)gdata->section_type;
- if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
- struct cper_sec_mem_err *mem = (void *)(gdata + 1);
- if (gdata->error_data_length >= sizeof(*mem))
- trace_extlog_mem_event(mem, err_seq, fru_id, fru_text,
- (u8)gdata->error_severity);
+ apei_estatus_for_each_section(tmp, gdata) {
+ if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
+ fru_id = (guid_t *)gdata->fru_id;
+ else
+ fru_id = &guid_null;
+ if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
+ fru_text = gdata->fru_text;
+ else
+ fru_text = "";
+ sec_type = (guid_t *)gdata->section_type;
+ if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
+ struct cper_sec_mem_err *mem = acpi_hest_get_payload(gdata);
+
+ if (gdata->error_data_length >= sizeof(*mem))
+ trace_extlog_mem_event(mem, err_seq, fru_id, fru_text,
+ (u8)gdata->error_severity);
+ }
}
out:
@@ -222,7 +235,7 @@ static int __init extlog_init(void)
u64 cap;
int rc;
- if (rdmsrl_safe(MSR_IA32_MCG_CAP, &cap) ||
+ if (rdmsrq_safe(MSR_IA32_MCG_CAP, &cap) ||
!(cap & MCG_ELOG_P) ||
!extlog_get_l1addr())
return -ENODEV;
@@ -239,6 +252,10 @@ static int __init extlog_init(void)
}
extlog_l1_hdr = acpi_os_map_iomem(l1_dirbase, l1_hdr_size);
+ if (!extlog_l1_hdr) {
+ rc = -ENOMEM;
+ goto err_release_l1_hdr;
+ }
l1_head = (struct extlog_l1_head *)extlog_l1_hdr;
l1_size = l1_head->total_len;
l1_percpu_entry = l1_head->entries;
@@ -256,6 +273,10 @@ static int __init extlog_init(void)
goto err;
}
extlog_l1_addr = acpi_os_map_iomem(l1_dirbase, l1_size);
+ if (!extlog_l1_addr) {
+ rc = -ENOMEM;
+ goto err_release_l1_dir;
+ }
l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size);
/* remap elog table */
@@ -267,6 +288,10 @@ static int __init extlog_init(void)
goto err_release_l1_dir;
}
elog_addr = acpi_os_map_iomem(elog_base, elog_size);
+ if (!elog_addr) {
+ rc = -ENOMEM;
+ goto err_release_elog;
+ }
rc = -ENOMEM;
/* allocate buffer to save elog record */
@@ -288,6 +313,8 @@ err_release_l1_dir:
if (extlog_l1_addr)
acpi_os_unmap_iomem(extlog_l1_addr, l1_size);
release_mem_region(l1_dirbase, l1_size);
+err_release_l1_hdr:
+ release_mem_region(l1_dirbase, l1_hdr_size);
err:
pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n");
return rc;
@@ -296,9 +323,10 @@ err:
static void __exit extlog_exit(void)
{
mce_unregister_decode_chain(&extlog_mce_dec);
- ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN;
- if (extlog_l1_addr)
+ if (extlog_l1_addr) {
+ ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN;
acpi_os_unmap_iomem(extlog_l1_addr, l1_size);
+ }
if (elog_addr)
acpi_os_unmap_iomem(elog_addr, elog_size);
release_mem_region(elog_base, elog_size);
diff --git a/drivers/acpi/acpi_ffh.c b/drivers/acpi/acpi_ffh.c
new file mode 100644
index 000000000000..8d5126963dc7
--- /dev/null
+++ b/drivers/acpi/acpi_ffh.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Sudeep Holla <sudeep.holla@arm.com>
+ * Copyright 2022 Arm Limited
+ */
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/io.h>
+
+static struct acpi_ffh_info ffh_ctx;
+
+int __weak acpi_ffh_address_space_arch_setup(void *handler_ctxt,
+ void **region_ctxt)
+{
+ return -EOPNOTSUPP;
+}
+
+int __weak acpi_ffh_address_space_arch_handler(acpi_integer *value,
+ void *region_context)
+{
+ return -EOPNOTSUPP;
+}
+
+static acpi_status
+acpi_ffh_address_space_setup(acpi_handle region_handle, u32 function,
+ void *handler_context, void **region_context)
+{
+ return acpi_ffh_address_space_arch_setup(handler_context,
+ region_context);
+}
+
+static acpi_status
+acpi_ffh_address_space_handler(u32 function, acpi_physical_address addr,
+ u32 bits, acpi_integer *value,
+ void *handler_context, void *region_context)
+{
+ return acpi_ffh_address_space_arch_handler(value, region_context);
+}
+
+void __init acpi_init_ffh(void)
+{
+ acpi_status status;
+
+ status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_FIXED_HARDWARE,
+ &acpi_ffh_address_space_handler,
+ &acpi_ffh_address_space_setup,
+ &ffh_ctx);
+ if (ACPI_FAILURE(status))
+ pr_alert("OperationRegion handler could not be installed\n");
+}
diff --git a/drivers/acpi/acpi_fpdt.c b/drivers/acpi/acpi_fpdt.c
index 6922a44b3ce7..271092f2700a 100644
--- a/drivers/acpi/acpi_fpdt.c
+++ b/drivers/acpi/acpi_fpdt.c
@@ -143,6 +143,23 @@ static const struct attribute_group boot_attr_group = {
static struct kobject *fpdt_kobj;
+#if defined CONFIG_X86 && defined CONFIG_PHYS_ADDR_T_64BIT
+#include <linux/processor.h>
+static bool fpdt_address_valid(u64 address)
+{
+ /*
+ * On some systems the table contains invalid addresses
+ * with unsuppored high address bits set, check for this.
+ */
+ return !(address >> boot_cpu_data.x86_phys_bits);
+}
+#else
+static bool fpdt_address_valid(u64 address)
+{
+ return true;
+}
+#endif
+
static int fpdt_process_subtable(u64 address, u32 subtable_type)
{
struct fpdt_subtable_header *subtable_header;
@@ -151,6 +168,11 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type)
u32 length, offset;
int result;
+ if (!fpdt_address_valid(address)) {
+ pr_info(FW_BUG "invalid physical address: 0x%llx!\n", address);
+ return -EINVAL;
+ }
+
subtable_header = acpi_os_map_memory(address, sizeof(*subtable_header));
if (!subtable_header)
return -ENOMEM;
@@ -172,12 +194,19 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type)
record_header = (void *)subtable_header + offset;
offset += record_header->length;
+ if (!record_header->length) {
+ pr_err(FW_BUG "Zero-length record found in FPTD.\n");
+ result = -EINVAL;
+ goto err;
+ }
+
switch (record_header->type) {
case RECORD_S3_RESUME:
if (subtable_type != SUBTABLE_S3PT) {
pr_err(FW_BUG "Invalid record %d for subtable %s\n",
record_header->type, signature);
- return -EINVAL;
+ result = -EINVAL;
+ goto err;
}
if (record_resume) {
pr_err("Duplicate resume performance record found.\n");
@@ -186,7 +215,7 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type)
record_resume = (struct resume_performance_record *)record_header;
result = sysfs_create_group(fpdt_kobj, &resume_attr_group);
if (result)
- return result;
+ goto err;
break;
case RECORD_S3_SUSPEND:
if (subtable_type != SUBTABLE_S3PT) {
@@ -201,13 +230,14 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type)
record_suspend = (struct suspend_performance_record *)record_header;
result = sysfs_create_group(fpdt_kobj, &suspend_attr_group);
if (result)
- return result;
+ goto err;
break;
case RECORD_BOOT:
if (subtable_type != SUBTABLE_FBPT) {
pr_err(FW_BUG "Invalid %d for subtable %s\n",
record_header->type, signature);
- return -EINVAL;
+ result = -EINVAL;
+ goto err;
}
if (record_boot) {
pr_err("Duplicate boot performance record found.\n");
@@ -216,7 +246,7 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type)
record_boot = (struct boot_performance_record *)record_header;
result = sysfs_create_group(fpdt_kobj, &boot_attr_group);
if (result)
- return result;
+ goto err;
break;
default:
@@ -225,6 +255,18 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type)
}
}
return 0;
+
+err:
+ if (record_boot)
+ sysfs_remove_group(fpdt_kobj, &boot_attr_group);
+
+ if (record_suspend)
+ sysfs_remove_group(fpdt_kobj, &suspend_attr_group);
+
+ if (record_resume)
+ sysfs_remove_group(fpdt_kobj, &resume_attr_group);
+
+ return result;
}
static int __init acpi_init_fpdt(void)
@@ -233,6 +275,7 @@ static int __init acpi_init_fpdt(void)
struct acpi_table_header *header;
struct fpdt_subtable_entry *subtable;
u32 offset = sizeof(*header);
+ int result;
status = acpi_get_table(ACPI_SIG_FPDT, 0, &header);
@@ -241,8 +284,8 @@ static int __init acpi_init_fpdt(void)
fpdt_kobj = kobject_create_and_add("fpdt", acpi_kobj);
if (!fpdt_kobj) {
- acpi_put_table(header);
- return -ENOMEM;
+ result = -ENOMEM;
+ goto err_nomem;
}
while (offset < header->length) {
@@ -250,8 +293,10 @@ static int __init acpi_init_fpdt(void)
switch (subtable->type) {
case SUBTABLE_FBPT:
case SUBTABLE_S3PT:
- fpdt_process_subtable(subtable->address,
+ result = fpdt_process_subtable(subtable->address,
subtable->type);
+ if (result)
+ goto err_subtable;
break;
default:
/* Other types are reserved in ACPI 6.4 spec. */
@@ -260,6 +305,12 @@ static int __init acpi_init_fpdt(void)
offset += sizeof(*subtable);
}
return 0;
+err_subtable:
+ kobject_put(fpdt_kobj);
+
+err_nomem:
+ acpi_put_table(header);
+ return result;
}
fs_initcall(acpi_init_fpdt);
diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c
index 0555f68c2dfd..5fba4dab5d08 100644
--- a/drivers/acpi/acpi_ipmi.c
+++ b/drivers/acpi/acpi_ipmi.c
@@ -22,6 +22,8 @@ MODULE_LICENSE("GPL");
/* the IPMI timeout is 5s */
#define IPMI_TIMEOUT (5000)
#define ACPI_IPMI_MAX_MSG_LENGTH 64
+/* 2s should be suffient for SMI being selected */
+#define ACPI_IPMI_SMI_SELECTION_TIMEOUT (2 * HZ)
struct acpi_ipmi_device {
/* the device list attached to driver_data.ipmi_devices */
@@ -54,6 +56,7 @@ struct ipmi_driver_data {
* to this selected global IPMI system interface.
*/
struct acpi_ipmi_device *selected_smi;
+ struct completion smi_selection_done;
};
struct acpi_ipmi_msg {
@@ -463,8 +466,10 @@ static void ipmi_register_bmc(int iface, struct device *dev)
if (temp->handle == handle)
goto err_lock;
}
- if (!driver_data.selected_smi)
+ if (!driver_data.selected_smi) {
driver_data.selected_smi = ipmi_device;
+ complete(&driver_data.smi_selection_done);
+ }
list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
mutex_unlock(&driver_data.ipmi_lock);
@@ -578,6 +583,20 @@ out_msg:
return status;
}
+int acpi_wait_for_acpi_ipmi(void)
+{
+ long ret;
+
+ ret = wait_for_completion_interruptible_timeout(&driver_data.smi_selection_done,
+ ACPI_IPMI_SMI_SELECTION_TIMEOUT);
+
+ if (ret <= 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_wait_for_acpi_ipmi);
+
static int __init acpi_ipmi_init(void)
{
int result;
@@ -586,6 +605,8 @@ static int __init acpi_ipmi_init(void)
if (acpi_disabled)
return 0;
+ init_completion(&driver_data.smi_selection_done);
+
status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_IPMI,
&acpi_ipmi_space_handler,
diff --git a/drivers/acpi/acpi_lpit.c b/drivers/acpi/acpi_lpit.c
index 50540d4d4948..b8d98b1b48ae 100644
--- a/drivers/acpi/acpi_lpit.c
+++ b/drivers/acpi/acpi_lpit.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <asm/msr.h>
#include <asm/tsc.h>
+#include "internal.h"
struct lpit_residency_info {
struct acpi_generic_address gaddr;
@@ -38,7 +39,7 @@ static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
return 0;
}
- err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter);
+ err = rdmsrq_safe(residency_info_ffh.gaddr.address, counter);
if (!err) {
u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset +
residency_info_ffh.gaddr. bit_width - 1,
@@ -97,8 +98,14 @@ EXPORT_SYMBOL_GPL(lpit_read_residency_count_address);
static void lpit_update_residency(struct lpit_residency_info *info,
struct acpi_lpit_native *lpit_native)
{
+ struct device *dev_root = bus_get_dev_root(&cpu_subsys);
+
+ /* Silently fail, if cpuidle attribute group is not present */
+ if (!dev_root)
+ return;
+
info->frequency = lpit_native->counter_frequency ?
- lpit_native->counter_frequency : tsc_khz * 1000;
+ lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U);
if (!info->frequency)
info->frequency = 1;
@@ -107,18 +114,18 @@ static void lpit_update_residency(struct lpit_residency_info *info,
info->iomem_addr = ioremap(info->gaddr.address,
info->gaddr.bit_width / 8);
if (!info->iomem_addr)
- return;
+ goto exit;
- /* Silently fail, if cpuidle attribute group is not present */
- sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
+ sysfs_add_file_to_group(&dev_root->kobj,
&dev_attr_low_power_idle_system_residency_us.attr,
"cpuidle");
} else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
- /* Silently fail, if cpuidle attribute group is not present */
- sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
+ sysfs_add_file_to_group(&dev_root->kobj,
&dev_attr_low_power_idle_cpu_residency_us.attr,
"cpuidle");
}
+exit:
+ put_device(dev_root);
}
static void lpit_process(u64 begin, u64 end)
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
deleted file mode 100644
index c4d4d21391d7..000000000000
--- a/drivers/acpi/acpi_lpss.c
+++ /dev/null
@@ -1,1388 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ACPI support for Intel Lynxpoint LPSS.
- *
- * Copyright (C) 2013, Intel Corporation
- * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
- * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/dmi.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/x86/clk-lpss.h>
-#include <linux/platform_data/x86/pmc_atom.h>
-#include <linux/pm_domain.h>
-#include <linux/pm_runtime.h>
-#include <linux/pwm.h>
-#include <linux/pxa2xx_ssp.h>
-#include <linux/suspend.h>
-#include <linux/delay.h>
-
-#include "internal.h"
-
-#ifdef CONFIG_X86_INTEL_LPSS
-
-#include <asm/cpu_device_id.h>
-#include <asm/intel-family.h>
-#include <asm/iosf_mbi.h>
-
-#define LPSS_ADDR(desc) ((unsigned long)&desc)
-
-#define LPSS_CLK_SIZE 0x04
-#define LPSS_LTR_SIZE 0x18
-
-/* Offsets relative to LPSS_PRIVATE_OFFSET */
-#define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16))
-#define LPSS_RESETS 0x04
-#define LPSS_RESETS_RESET_FUNC BIT(0)
-#define LPSS_RESETS_RESET_APB BIT(1)
-#define LPSS_GENERAL 0x08
-#define LPSS_GENERAL_LTR_MODE_SW BIT(2)
-#define LPSS_GENERAL_UART_RTS_OVRD BIT(3)
-#define LPSS_SW_LTR 0x10
-#define LPSS_AUTO_LTR 0x14
-#define LPSS_LTR_SNOOP_REQ BIT(15)
-#define LPSS_LTR_SNOOP_MASK 0x0000FFFF
-#define LPSS_LTR_SNOOP_LAT_1US 0x800
-#define LPSS_LTR_SNOOP_LAT_32US 0xC00
-#define LPSS_LTR_SNOOP_LAT_SHIFT 5
-#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000
-#define LPSS_LTR_MAX_VAL 0x3FF
-#define LPSS_TX_INT 0x20
-#define LPSS_TX_INT_MASK BIT(1)
-
-#define LPSS_PRV_REG_COUNT 9
-
-/* LPSS Flags */
-#define LPSS_CLK BIT(0)
-#define LPSS_CLK_GATE BIT(1)
-#define LPSS_CLK_DIVIDER BIT(2)
-#define LPSS_LTR BIT(3)
-#define LPSS_SAVE_CTX BIT(4)
-/*
- * For some devices the DSDT AML code for another device turns off the device
- * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff)
- * as ctx register values.
- * Luckily these devices always use the same ctx register values, so we can
- * work around this by saving the ctx registers once on activation.
- */
-#define LPSS_SAVE_CTX_ONCE BIT(5)
-#define LPSS_NO_D3_DELAY BIT(6)
-
-struct lpss_private_data;
-
-struct lpss_device_desc {
- unsigned int flags;
- const char *clk_con_id;
- unsigned int prv_offset;
- size_t prv_size_override;
- const struct property_entry *properties;
- void (*setup)(struct lpss_private_data *pdata);
- bool resume_from_noirq;
-};
-
-static const struct lpss_device_desc lpss_dma_desc = {
- .flags = LPSS_CLK,
-};
-
-struct lpss_private_data {
- struct acpi_device *adev;
- void __iomem *mmio_base;
- resource_size_t mmio_size;
- unsigned int fixed_clk_rate;
- struct clk *clk;
- const struct lpss_device_desc *dev_desc;
- u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
-};
-
-/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */
-static u32 pmc_atom_d3_mask = 0xfe000ffe;
-
-/* LPSS run time quirks */
-static unsigned int lpss_quirks;
-
-/*
- * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device.
- *
- * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover
- * it can be powered off automatically whenever the last LPSS device goes down.
- * In case of no power any access to the DMA controller will hang the system.
- * The behaviour is reproduced on some HP laptops based on Intel BayTrail as
- * well as on ASuS T100TA transformer.
- *
- * This quirk overrides power state of entire LPSS island to keep DMA powered
- * on whenever we have at least one other device in use.
- */
-#define LPSS_QUIRK_ALWAYS_POWER_ON BIT(0)
-
-/* UART Component Parameter Register */
-#define LPSS_UART_CPR 0xF4
-#define LPSS_UART_CPR_AFCE BIT(4)
-
-static void lpss_uart_setup(struct lpss_private_data *pdata)
-{
- unsigned int offset;
- u32 val;
-
- offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
- val = readl(pdata->mmio_base + offset);
- writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
-
- val = readl(pdata->mmio_base + LPSS_UART_CPR);
- if (!(val & LPSS_UART_CPR_AFCE)) {
- offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
- val = readl(pdata->mmio_base + offset);
- val |= LPSS_GENERAL_UART_RTS_OVRD;
- writel(val, pdata->mmio_base + offset);
- }
-}
-
-static void lpss_deassert_reset(struct lpss_private_data *pdata)
-{
- unsigned int offset;
- u32 val;
-
- offset = pdata->dev_desc->prv_offset + LPSS_RESETS;
- val = readl(pdata->mmio_base + offset);
- val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC;
- writel(val, pdata->mmio_base + offset);
-}
-
-/*
- * BYT PWM used for backlight control by the i915 driver on systems without
- * the Crystal Cove PMIC.
- */
-static struct pwm_lookup byt_pwm_lookup[] = {
- PWM_LOOKUP_WITH_MODULE("80860F09:00", 0, "0000:00:02.0",
- "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
- "pwm-lpss-platform"),
-};
-
-static void byt_pwm_setup(struct lpss_private_data *pdata)
-{
- struct acpi_device *adev = pdata->adev;
-
- /* Only call pwm_add_table for the first PWM controller */
- if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1"))
- return;
-
- pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup));
-}
-
-#define LPSS_I2C_ENABLE 0x6c
-
-static void byt_i2c_setup(struct lpss_private_data *pdata)
-{
- const char *uid_str = acpi_device_uid(pdata->adev);
- acpi_handle handle = pdata->adev->handle;
- unsigned long long shared_host = 0;
- acpi_status status;
- long uid = 0;
-
- /* Expected to always be true, but better safe then sorry */
- if (uid_str && !kstrtol(uid_str, 10, &uid) && uid) {
- /* Detect I2C bus shared with PUNIT and ignore its d3 status */
- status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
- if (ACPI_SUCCESS(status) && shared_host)
- pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1));
- }
-
- lpss_deassert_reset(pdata);
-
- if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
- pdata->fixed_clk_rate = 133000000;
-
- writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
-}
-
-/* BSW PWM used for backlight control by the i915 driver */
-static struct pwm_lookup bsw_pwm_lookup[] = {
- PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0",
- "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
- "pwm-lpss-platform"),
-};
-
-static void bsw_pwm_setup(struct lpss_private_data *pdata)
-{
- struct acpi_device *adev = pdata->adev;
-
- /* Only call pwm_add_table for the first PWM controller */
- if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1"))
- return;
-
- pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup));
-}
-
-static const struct property_entry lpt_spi_properties[] = {
- PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_LPT_SSP),
- { }
-};
-
-static const struct lpss_device_desc lpt_spi_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR
- | LPSS_SAVE_CTX,
- .prv_offset = 0x800,
- .properties = lpt_spi_properties,
-};
-
-static const struct lpss_device_desc lpt_i2c_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR | LPSS_SAVE_CTX,
- .prv_offset = 0x800,
-};
-
-static struct property_entry uart_properties[] = {
- PROPERTY_ENTRY_U32("reg-io-width", 4),
- PROPERTY_ENTRY_U32("reg-shift", 2),
- PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
- { },
-};
-
-static const struct lpss_device_desc lpt_uart_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR
- | LPSS_SAVE_CTX,
- .clk_con_id = "baudclk",
- .prv_offset = 0x800,
- .setup = lpss_uart_setup,
- .properties = uart_properties,
-};
-
-static const struct lpss_device_desc lpt_sdio_dev_desc = {
- .flags = LPSS_LTR,
- .prv_offset = 0x1000,
- .prv_size_override = 0x1018,
-};
-
-static const struct lpss_device_desc byt_pwm_dev_desc = {
- .flags = LPSS_SAVE_CTX,
- .prv_offset = 0x800,
- .setup = byt_pwm_setup,
-};
-
-static const struct lpss_device_desc bsw_pwm_dev_desc = {
- .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY,
- .prv_offset = 0x800,
- .setup = bsw_pwm_setup,
- .resume_from_noirq = true,
-};
-
-static const struct lpss_device_desc byt_uart_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
- .clk_con_id = "baudclk",
- .prv_offset = 0x800,
- .setup = lpss_uart_setup,
- .properties = uart_properties,
-};
-
-static const struct lpss_device_desc bsw_uart_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
- | LPSS_NO_D3_DELAY,
- .clk_con_id = "baudclk",
- .prv_offset = 0x800,
- .setup = lpss_uart_setup,
- .properties = uart_properties,
-};
-
-static const struct property_entry byt_spi_properties[] = {
- PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BYT_SSP),
- { }
-};
-
-static const struct lpss_device_desc byt_spi_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
- .prv_offset = 0x400,
- .properties = byt_spi_properties,
-};
-
-static const struct lpss_device_desc byt_sdio_dev_desc = {
- .flags = LPSS_CLK,
-};
-
-static const struct lpss_device_desc byt_i2c_dev_desc = {
- .flags = LPSS_CLK | LPSS_SAVE_CTX,
- .prv_offset = 0x800,
- .setup = byt_i2c_setup,
- .resume_from_noirq = true,
-};
-
-static const struct lpss_device_desc bsw_i2c_dev_desc = {
- .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
- .prv_offset = 0x800,
- .setup = byt_i2c_setup,
- .resume_from_noirq = true,
-};
-
-static const struct property_entry bsw_spi_properties[] = {
- PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP),
- { }
-};
-
-static const struct lpss_device_desc bsw_spi_dev_desc = {
- .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
- | LPSS_NO_D3_DELAY,
- .prv_offset = 0x400,
- .setup = lpss_deassert_reset,
- .properties = bsw_spi_properties,
-};
-
-static const struct x86_cpu_id lpss_cpu_ids[] = {
- X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL),
- {}
-};
-
-#else
-
-#define LPSS_ADDR(desc) (0UL)
-
-#endif /* CONFIG_X86_INTEL_LPSS */
-
-static const struct acpi_device_id acpi_lpss_device_ids[] = {
- /* Generic LPSS devices */
- { "INTL9C60", LPSS_ADDR(lpss_dma_desc) },
-
- /* Lynxpoint LPSS devices */
- { "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) },
- { "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) },
- { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) },
- { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) },
- { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) },
- { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) },
- { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) },
- { "INT33C7", },
-
- /* BayTrail LPSS devices */
- { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) },
- { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) },
- { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) },
- { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) },
- { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) },
- { "INT33B2", },
- { "INT33FC", },
-
- /* Braswell LPSS devices */
- { "80862286", LPSS_ADDR(lpss_dma_desc) },
- { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
- { "8086228A", LPSS_ADDR(bsw_uart_dev_desc) },
- { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) },
- { "808622C0", LPSS_ADDR(lpss_dma_desc) },
- { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) },
-
- /* Broadwell LPSS devices */
- { "INT3430", LPSS_ADDR(lpt_spi_dev_desc) },
- { "INT3431", LPSS_ADDR(lpt_spi_dev_desc) },
- { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) },
- { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) },
- { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) },
- { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) },
- { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
- { "INT3437", },
-
- /* Wildcat Point LPSS devices */
- { "INT3438", LPSS_ADDR(lpt_spi_dev_desc) },
-
- { }
-};
-
-#ifdef CONFIG_X86_INTEL_LPSS
-
-static int is_memory(struct acpi_resource *res, void *not_used)
-{
- struct resource r;
-
- return !acpi_dev_resource_memory(res, &r);
-}
-
-/* LPSS main clock device. */
-static struct platform_device *lpss_clk_dev;
-
-static inline void lpt_register_clock_device(void)
-{
- lpss_clk_dev = platform_device_register_simple("clk-lpss-atom",
- PLATFORM_DEVID_NONE,
- NULL, 0);
-}
-
-static int register_device_clock(struct acpi_device *adev,
- struct lpss_private_data *pdata)
-{
- const struct lpss_device_desc *dev_desc = pdata->dev_desc;
- const char *devname = dev_name(&adev->dev);
- struct clk *clk;
- struct lpss_clk_data *clk_data;
- const char *parent, *clk_name;
- void __iomem *prv_base;
-
- if (!lpss_clk_dev)
- lpt_register_clock_device();
-
- if (IS_ERR(lpss_clk_dev))
- return PTR_ERR(lpss_clk_dev);
-
- clk_data = platform_get_drvdata(lpss_clk_dev);
- if (!clk_data)
- return -ENODEV;
- clk = clk_data->clk;
-
- if (!pdata->mmio_base
- || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
- return -ENODATA;
-
- parent = clk_data->name;
- prv_base = pdata->mmio_base + dev_desc->prv_offset;
-
- if (pdata->fixed_clk_rate) {
- clk = clk_register_fixed_rate(NULL, devname, parent, 0,
- pdata->fixed_clk_rate);
- goto out;
- }
-
- if (dev_desc->flags & LPSS_CLK_GATE) {
- clk = clk_register_gate(NULL, devname, parent, 0,
- prv_base, 0, 0, NULL);
- parent = devname;
- }
-
- if (dev_desc->flags & LPSS_CLK_DIVIDER) {
- /* Prevent division by zero */
- if (!readl(prv_base))
- writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
-
- clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
- if (!clk_name)
- return -ENOMEM;
- clk = clk_register_fractional_divider(NULL, clk_name, parent,
- CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
- prv_base, 1, 15, 16, 15, 0, NULL);
- parent = clk_name;
-
- clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
- if (!clk_name) {
- kfree(parent);
- return -ENOMEM;
- }
- clk = clk_register_gate(NULL, clk_name, parent,
- CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
- prv_base, 31, 0, NULL);
- kfree(parent);
- kfree(clk_name);
- }
-out:
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- pdata->clk = clk;
- clk_register_clkdev(clk, dev_desc->clk_con_id, devname);
- return 0;
-}
-
-struct lpss_device_links {
- const char *supplier_hid;
- const char *supplier_uid;
- const char *consumer_hid;
- const char *consumer_uid;
- u32 flags;
- const struct dmi_system_id *dep_missing_ids;
-};
-
-/* Please keep this list sorted alphabetically by vendor and model */
-static const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = {
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"),
- },
- },
- {}
-};
-
-/*
- * The _DEP method is used to identify dependencies but instead of creating
- * device links for every handle in _DEP, only links in the following list are
- * created. That is necessary because, in the general case, _DEP can refer to
- * devices that might not have drivers, or that are on different buses, or where
- * the supplier is not enumerated until after the consumer is probed.
- */
-static const struct lpss_device_links lpss_device_links[] = {
- /* CHT External sdcard slot controller depends on PMIC I2C ctrl */
- {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME},
- /* CHT iGPU depends on PMIC I2C controller */
- {"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
- /* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */
- {"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME,
- i2c1_dep_missing_dmi_ids},
- /* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */
- {"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
- /* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */
- {"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
-};
-
-static bool acpi_lpss_is_supplier(struct acpi_device *adev,
- const struct lpss_device_links *link)
-{
- return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid);
-}
-
-static bool acpi_lpss_is_consumer(struct acpi_device *adev,
- const struct lpss_device_links *link)
-{
- return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid);
-}
-
-struct hid_uid {
- const char *hid;
- const char *uid;
-};
-
-static int match_hid_uid(struct device *dev, const void *data)
-{
- struct acpi_device *adev = ACPI_COMPANION(dev);
- const struct hid_uid *id = data;
-
- if (!adev)
- return 0;
-
- return acpi_dev_hid_uid_match(adev, id->hid, id->uid);
-}
-
-static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
-{
- struct device *dev;
-
- struct hid_uid data = {
- .hid = hid,
- .uid = uid,
- };
-
- dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
- if (dev)
- return dev;
-
- return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
-}
-
-static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
-{
- struct acpi_handle_list dep_devices;
- acpi_status status;
- int i;
-
- if (!acpi_has_method(adev->handle, "_DEP"))
- return false;
-
- status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
- &dep_devices);
- if (ACPI_FAILURE(status)) {
- dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
- return false;
- }
-
- for (i = 0; i < dep_devices.count; i++) {
- if (dep_devices.handles[i] == handle)
- return true;
- }
-
- return false;
-}
-
-static void acpi_lpss_link_consumer(struct device *dev1,
- const struct lpss_device_links *link)
-{
- struct device *dev2;
-
- dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
- if (!dev2)
- return;
-
- if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
- || acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
- device_link_add(dev2, dev1, link->flags);
-
- put_device(dev2);
-}
-
-static void acpi_lpss_link_supplier(struct device *dev1,
- const struct lpss_device_links *link)
-{
- struct device *dev2;
-
- dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
- if (!dev2)
- return;
-
- if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
- || acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
- device_link_add(dev1, dev2, link->flags);
-
- put_device(dev2);
-}
-
-static void acpi_lpss_create_device_links(struct acpi_device *adev,
- struct platform_device *pdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
- const struct lpss_device_links *link = &lpss_device_links[i];
-
- if (acpi_lpss_is_supplier(adev, link))
- acpi_lpss_link_consumer(&pdev->dev, link);
-
- if (acpi_lpss_is_consumer(adev, link))
- acpi_lpss_link_supplier(&pdev->dev, link);
- }
-}
-
-static int acpi_lpss_create_device(struct acpi_device *adev,
- const struct acpi_device_id *id)
-{
- const struct lpss_device_desc *dev_desc;
- struct lpss_private_data *pdata;
- struct resource_entry *rentry;
- struct list_head resource_list;
- struct platform_device *pdev;
- int ret;
-
- dev_desc = (const struct lpss_device_desc *)id->driver_data;
- if (!dev_desc) {
- pdev = acpi_create_platform_device(adev, NULL);
- return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
- }
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL);
- if (ret < 0)
- goto err_out;
-
- list_for_each_entry(rentry, &resource_list, node)
- if (resource_type(rentry->res) == IORESOURCE_MEM) {
- if (dev_desc->prv_size_override)
- pdata->mmio_size = dev_desc->prv_size_override;
- else
- pdata->mmio_size = resource_size(rentry->res);
- pdata->mmio_base = ioremap(rentry->res->start,
- pdata->mmio_size);
- break;
- }
-
- acpi_dev_free_resource_list(&resource_list);
-
- if (!pdata->mmio_base) {
- /* Avoid acpi_bus_attach() instantiating a pdev for this dev. */
- adev->pnp.type.platform_id = 0;
- /* Skip the device, but continue the namespace scan. */
- ret = 0;
- goto err_out;
- }
-
- pdata->adev = adev;
- pdata->dev_desc = dev_desc;
-
- if (dev_desc->setup)
- dev_desc->setup(pdata);
-
- if (dev_desc->flags & LPSS_CLK) {
- ret = register_device_clock(adev, pdata);
- if (ret) {
- /* Skip the device, but continue the namespace scan. */
- ret = 0;
- goto err_out;
- }
- }
-
- /*
- * This works around a known issue in ACPI tables where LPSS devices
- * have _PS0 and _PS3 without _PSC (and no power resources), so
- * acpi_bus_init_power() will assume that the BIOS has put them into D0.
- */
- acpi_device_fix_up_power(adev);
-
- adev->driver_data = pdata;
- pdev = acpi_create_platform_device(adev, dev_desc->properties);
- if (!IS_ERR_OR_NULL(pdev)) {
- acpi_lpss_create_device_links(adev, pdev);
- return 1;
- }
-
- ret = PTR_ERR(pdev);
- adev->driver_data = NULL;
-
- err_out:
- kfree(pdata);
- return ret;
-}
-
-static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
-{
- return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
-}
-
-static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
- unsigned int reg)
-{
- writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
-}
-
-static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
-{
- struct acpi_device *adev = ACPI_COMPANION(dev);
- struct lpss_private_data *pdata;
- unsigned long flags;
- int ret;
-
- if (WARN_ON(!adev))
- return -ENODEV;
-
- spin_lock_irqsave(&dev->power.lock, flags);
- if (pm_runtime_suspended(dev)) {
- ret = -EAGAIN;
- goto out;
- }
- pdata = acpi_driver_data(adev);
- if (WARN_ON(!pdata || !pdata->mmio_base)) {
- ret = -ENODEV;
- goto out;
- }
- *val = __lpss_reg_read(pdata, reg);
- ret = 0;
-
- out:
- spin_unlock_irqrestore(&dev->power.lock, flags);
- return ret;
-}
-
-static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- u32 ltr_value = 0;
- unsigned int reg;
- int ret;
-
- reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR;
- ret = lpss_reg_read(dev, reg, &ltr_value);
- if (ret)
- return ret;
-
- return sysfs_emit(buf, "%08x\n", ltr_value);
-}
-
-static ssize_t lpss_ltr_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u32 ltr_mode = 0;
- char *outstr;
- int ret;
-
- ret = lpss_reg_read(dev, LPSS_GENERAL, &ltr_mode);
- if (ret)
- return ret;
-
- outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto";
- return sprintf(buf, "%s\n", outstr);
-}
-
-static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL);
-static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL);
-static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL);
-
-static struct attribute *lpss_attrs[] = {
- &dev_attr_auto_ltr.attr,
- &dev_attr_sw_ltr.attr,
- &dev_attr_ltr_mode.attr,
- NULL,
-};
-
-static const struct attribute_group lpss_attr_group = {
- .attrs = lpss_attrs,
- .name = "lpss_ltr",
-};
-
-static void acpi_lpss_set_ltr(struct device *dev, s32 val)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- u32 ltr_mode, ltr_val;
-
- ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
- if (val < 0) {
- if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
- ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
- __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
- }
- return;
- }
- ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
- if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
- ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
- val = LPSS_LTR_MAX_VAL;
- } else if (val > LPSS_LTR_MAX_VAL) {
- ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
- val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
- } else {
- ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
- }
- ltr_val |= val;
- __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
- if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
- ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
- __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
- }
-}
-
-#ifdef CONFIG_PM
-/**
- * acpi_lpss_save_ctx() - Save the private registers of LPSS device
- * @dev: LPSS device
- * @pdata: pointer to the private data of the LPSS device
- *
- * Most LPSS devices have private registers which may loose their context when
- * the device is powered down. acpi_lpss_save_ctx() saves those registers into
- * prv_reg_ctx array.
- */
-static void acpi_lpss_save_ctx(struct device *dev,
- struct lpss_private_data *pdata)
-{
- unsigned int i;
-
- for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
- unsigned long offset = i * sizeof(u32);
-
- pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset);
- dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n",
- pdata->prv_reg_ctx[i], offset);
- }
-}
-
-/**
- * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device
- * @dev: LPSS device
- * @pdata: pointer to the private data of the LPSS device
- *
- * Restores the registers that were previously stored with acpi_lpss_save_ctx().
- */
-static void acpi_lpss_restore_ctx(struct device *dev,
- struct lpss_private_data *pdata)
-{
- unsigned int i;
-
- for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
- unsigned long offset = i * sizeof(u32);
-
- __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
- dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
- pdata->prv_reg_ctx[i], offset);
- }
-}
-
-static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata)
-{
- /*
- * The following delay is needed or the subsequent write operations may
- * fail. The LPSS devices are actually PCI devices and the PCI spec
- * expects 10ms delay before the device can be accessed after D3 to D0
- * transition. However some platforms like BSW does not need this delay.
- */
- unsigned int delay = 10; /* default 10ms delay */
-
- if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY)
- delay = 0;
-
- msleep(delay);
-}
-
-static int acpi_lpss_activate(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- int ret;
-
- ret = acpi_dev_resume(dev);
- if (ret)
- return ret;
-
- acpi_lpss_d3_to_d0_delay(pdata);
-
- /*
- * This is called only on ->probe() stage where a device is either in
- * known state defined by BIOS or most likely powered off. Due to this
- * we have to deassert reset line to be sure that ->probe() will
- * recognize the device.
- */
- if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
- lpss_deassert_reset(pdata);
-
-#ifdef CONFIG_PM
- if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE)
- acpi_lpss_save_ctx(dev, pdata);
-#endif
-
- return 0;
-}
-
-static void acpi_lpss_dismiss(struct device *dev)
-{
- acpi_dev_suspend(dev, false);
-}
-
-/* IOSF SB for LPSS island */
-#define LPSS_IOSF_UNIT_LPIOEP 0xA0
-#define LPSS_IOSF_UNIT_LPIO1 0xAB
-#define LPSS_IOSF_UNIT_LPIO2 0xAC
-
-#define LPSS_IOSF_PMCSR 0x84
-#define LPSS_PMCSR_D0 0
-#define LPSS_PMCSR_D3hot 3
-#define LPSS_PMCSR_Dx_MASK GENMASK(1, 0)
-
-#define LPSS_IOSF_GPIODEF0 0x154
-#define LPSS_GPIODEF0_DMA1_D3 BIT(2)
-#define LPSS_GPIODEF0_DMA2_D3 BIT(3)
-#define LPSS_GPIODEF0_DMA_D3_MASK GENMASK(3, 2)
-#define LPSS_GPIODEF0_DMA_LLP BIT(13)
-
-static DEFINE_MUTEX(lpss_iosf_mutex);
-static bool lpss_iosf_d3_entered = true;
-
-static void lpss_iosf_enter_d3_state(void)
-{
- u32 value1 = 0;
- u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
- u32 value2 = LPSS_PMCSR_D3hot;
- u32 mask2 = LPSS_PMCSR_Dx_MASK;
- /*
- * PMC provides an information about actual status of the LPSS devices.
- * Here we read the values related to LPSS power island, i.e. LPSS
- * devices, excluding both LPSS DMA controllers, along with SCC domain.
- */
- u32 func_dis, d3_sts_0, pmc_status;
- int ret;
-
- ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
- if (ret)
- return;
-
- mutex_lock(&lpss_iosf_mutex);
-
- ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0);
- if (ret)
- goto exit;
-
- /*
- * Get the status of entire LPSS power island per device basis.
- * Shutdown both LPSS DMA controllers if and only if all other devices
- * are already in D3hot.
- */
- pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask;
- if (pmc_status)
- goto exit;
-
- iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
- LPSS_IOSF_PMCSR, value2, mask2);
-
- iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
- LPSS_IOSF_PMCSR, value2, mask2);
-
- iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
- LPSS_IOSF_GPIODEF0, value1, mask1);
-
- lpss_iosf_d3_entered = true;
-
-exit:
- mutex_unlock(&lpss_iosf_mutex);
-}
-
-static void lpss_iosf_exit_d3_state(void)
-{
- u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 |
- LPSS_GPIODEF0_DMA_LLP;
- u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
- u32 value2 = LPSS_PMCSR_D0;
- u32 mask2 = LPSS_PMCSR_Dx_MASK;
-
- mutex_lock(&lpss_iosf_mutex);
-
- if (!lpss_iosf_d3_entered)
- goto exit;
-
- lpss_iosf_d3_entered = false;
-
- iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
- LPSS_IOSF_GPIODEF0, value1, mask1);
-
- iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
- LPSS_IOSF_PMCSR, value2, mask2);
-
- iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
- LPSS_IOSF_PMCSR, value2, mask2);
-
-exit:
- mutex_unlock(&lpss_iosf_mutex);
-}
-
-static int acpi_lpss_suspend(struct device *dev, bool wakeup)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- int ret;
-
- if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
- acpi_lpss_save_ctx(dev, pdata);
-
- ret = acpi_dev_suspend(dev, wakeup);
-
- /*
- * This call must be last in the sequence, otherwise PMC will return
- * wrong status for devices being about to be powered off. See
- * lpss_iosf_enter_d3_state() for further information.
- */
- if (acpi_target_system_state() == ACPI_STATE_S0 &&
- lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
- lpss_iosf_enter_d3_state();
-
- return ret;
-}
-
-static int acpi_lpss_resume(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- int ret;
-
- /*
- * This call is kept first to be in symmetry with
- * acpi_lpss_runtime_suspend() one.
- */
- if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
- lpss_iosf_exit_d3_state();
-
- ret = acpi_dev_resume(dev);
- if (ret)
- return ret;
-
- acpi_lpss_d3_to_d0_delay(pdata);
-
- if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
- acpi_lpss_restore_ctx(dev, pdata);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int acpi_lpss_do_suspend_late(struct device *dev)
-{
- int ret;
-
- if (dev_pm_skip_suspend(dev))
- return 0;
-
- ret = pm_generic_suspend_late(dev);
- return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
-}
-
-static int acpi_lpss_suspend_late(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-
- if (pdata->dev_desc->resume_from_noirq)
- return 0;
-
- return acpi_lpss_do_suspend_late(dev);
-}
-
-static int acpi_lpss_suspend_noirq(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- int ret;
-
- if (pdata->dev_desc->resume_from_noirq) {
- /*
- * The driver's ->suspend_late callback will be invoked by
- * acpi_lpss_do_suspend_late(), with the assumption that the
- * driver really wanted to run that code in ->suspend_noirq, but
- * it could not run after acpi_dev_suspend() and the driver
- * expected the latter to be called in the "late" phase.
- */
- ret = acpi_lpss_do_suspend_late(dev);
- if (ret)
- return ret;
- }
-
- return acpi_subsys_suspend_noirq(dev);
-}
-
-static int acpi_lpss_do_resume_early(struct device *dev)
-{
- int ret = acpi_lpss_resume(dev);
-
- return ret ? ret : pm_generic_resume_early(dev);
-}
-
-static int acpi_lpss_resume_early(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-
- if (pdata->dev_desc->resume_from_noirq)
- return 0;
-
- if (dev_pm_skip_resume(dev))
- return 0;
-
- return acpi_lpss_do_resume_early(dev);
-}
-
-static int acpi_lpss_resume_noirq(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- int ret;
-
- /* Follow acpi_subsys_resume_noirq(). */
- if (dev_pm_skip_resume(dev))
- return 0;
-
- ret = pm_generic_resume_noirq(dev);
- if (ret)
- return ret;
-
- if (!pdata->dev_desc->resume_from_noirq)
- return 0;
-
- /*
- * The driver's ->resume_early callback will be invoked by
- * acpi_lpss_do_resume_early(), with the assumption that the driver
- * really wanted to run that code in ->resume_noirq, but it could not
- * run before acpi_dev_resume() and the driver expected the latter to be
- * called in the "early" phase.
- */
- return acpi_lpss_do_resume_early(dev);
-}
-
-static int acpi_lpss_do_restore_early(struct device *dev)
-{
- int ret = acpi_lpss_resume(dev);
-
- return ret ? ret : pm_generic_restore_early(dev);
-}
-
-static int acpi_lpss_restore_early(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-
- if (pdata->dev_desc->resume_from_noirq)
- return 0;
-
- return acpi_lpss_do_restore_early(dev);
-}
-
-static int acpi_lpss_restore_noirq(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
- int ret;
-
- ret = pm_generic_restore_noirq(dev);
- if (ret)
- return ret;
-
- if (!pdata->dev_desc->resume_from_noirq)
- return 0;
-
- /* This is analogous to what happens in acpi_lpss_resume_noirq(). */
- return acpi_lpss_do_restore_early(dev);
-}
-
-static int acpi_lpss_do_poweroff_late(struct device *dev)
-{
- int ret = pm_generic_poweroff_late(dev);
-
- return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
-}
-
-static int acpi_lpss_poweroff_late(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-
- if (dev_pm_skip_suspend(dev))
- return 0;
-
- if (pdata->dev_desc->resume_from_noirq)
- return 0;
-
- return acpi_lpss_do_poweroff_late(dev);
-}
-
-static int acpi_lpss_poweroff_noirq(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-
- if (dev_pm_skip_suspend(dev))
- return 0;
-
- if (pdata->dev_desc->resume_from_noirq) {
- /* This is analogous to the acpi_lpss_suspend_noirq() case. */
- int ret = acpi_lpss_do_poweroff_late(dev);
-
- if (ret)
- return ret;
- }
-
- return pm_generic_poweroff_noirq(dev);
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static int acpi_lpss_runtime_suspend(struct device *dev)
-{
- int ret = pm_generic_runtime_suspend(dev);
-
- return ret ? ret : acpi_lpss_suspend(dev, true);
-}
-
-static int acpi_lpss_runtime_resume(struct device *dev)
-{
- int ret = acpi_lpss_resume(dev);
-
- return ret ? ret : pm_generic_runtime_resume(dev);
-}
-#endif /* CONFIG_PM */
-
-static struct dev_pm_domain acpi_lpss_pm_domain = {
-#ifdef CONFIG_PM
- .activate = acpi_lpss_activate,
- .dismiss = acpi_lpss_dismiss,
-#endif
- .ops = {
-#ifdef CONFIG_PM
-#ifdef CONFIG_PM_SLEEP
- .prepare = acpi_subsys_prepare,
- .complete = acpi_subsys_complete,
- .suspend = acpi_subsys_suspend,
- .suspend_late = acpi_lpss_suspend_late,
- .suspend_noirq = acpi_lpss_suspend_noirq,
- .resume_noirq = acpi_lpss_resume_noirq,
- .resume_early = acpi_lpss_resume_early,
- .freeze = acpi_subsys_freeze,
- .poweroff = acpi_subsys_poweroff,
- .poweroff_late = acpi_lpss_poweroff_late,
- .poweroff_noirq = acpi_lpss_poweroff_noirq,
- .restore_noirq = acpi_lpss_restore_noirq,
- .restore_early = acpi_lpss_restore_early,
-#endif
- .runtime_suspend = acpi_lpss_runtime_suspend,
- .runtime_resume = acpi_lpss_runtime_resume,
-#endif
- },
-};
-
-static int acpi_lpss_platform_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct platform_device *pdev = to_platform_device(data);
- struct lpss_private_data *pdata;
- struct acpi_device *adev;
- const struct acpi_device_id *id;
-
- id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
- if (!id || !id->driver_data)
- return 0;
-
- adev = ACPI_COMPANION(&pdev->dev);
- if (!adev)
- return 0;
-
- pdata = acpi_driver_data(adev);
- if (!pdata)
- return 0;
-
- if (pdata->mmio_base &&
- pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
- dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n");
- return 0;
- }
-
- switch (action) {
- case BUS_NOTIFY_BIND_DRIVER:
- dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
- break;
- case BUS_NOTIFY_DRIVER_NOT_BOUND:
- case BUS_NOTIFY_UNBOUND_DRIVER:
- dev_pm_domain_set(&pdev->dev, NULL);
- break;
- case BUS_NOTIFY_ADD_DEVICE:
- dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
- if (pdata->dev_desc->flags & LPSS_LTR)
- return sysfs_create_group(&pdev->dev.kobj,
- &lpss_attr_group);
- break;
- case BUS_NOTIFY_DEL_DEVICE:
- if (pdata->dev_desc->flags & LPSS_LTR)
- sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
- dev_pm_domain_set(&pdev->dev, NULL);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static struct notifier_block acpi_lpss_nb = {
- .notifier_call = acpi_lpss_platform_notify,
-};
-
-static void acpi_lpss_bind(struct device *dev)
-{
- struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-
- if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
- return;
-
- if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
- dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
- else
- dev_err(dev, "MMIO size insufficient to access LTR\n");
-}
-
-static void acpi_lpss_unbind(struct device *dev)
-{
- dev->power.set_latency_tolerance = NULL;
-}
-
-static struct acpi_scan_handler lpss_handler = {
- .ids = acpi_lpss_device_ids,
- .attach = acpi_lpss_create_device,
- .bind = acpi_lpss_bind,
- .unbind = acpi_lpss_unbind,
-};
-
-void __init acpi_lpss_init(void)
-{
- const struct x86_cpu_id *id;
- int ret;
-
- ret = lpss_atom_clk_init();
- if (ret)
- return;
-
- id = x86_match_cpu(lpss_cpu_ids);
- if (id)
- lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON;
-
- bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
- acpi_scan_add_handler(&lpss_handler);
-}
-
-#else
-
-static struct acpi_scan_handler lpss_handler = {
- .ids = acpi_lpss_device_ids,
-};
-
-void __init acpi_lpss_init(void)
-{
- acpi_scan_add_handler(&lpss_handler);
-}
-
-#endif /* CONFIG_X86_INTEL_LPSS */
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index 24f662d8bd39..d0c1a71007d0 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -211,8 +211,7 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
if (!info->length)
continue;
- if (mhp_supports_memmap_on_memory(info->length))
- mhp_flags |= MHP_MEMMAP_ON_MEMORY;
+ mhp_flags |= MHP_MEMMAP_ON_MEMORY;
result = __add_memory(mgid, info->start_addr, info->length,
mhp_flags);
diff --git a/drivers/acpi/acpi_mrrm.c b/drivers/acpi/acpi_mrrm.c
new file mode 100644
index 000000000000..6d69554c940e
--- /dev/null
+++ b/drivers/acpi/acpi_mrrm.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025, Intel Corporation.
+ *
+ * Memory Range and Region Mapping (MRRM) structure
+ *
+ * Parse and report the platform's MRRM table in /sys.
+ */
+
+#define pr_fmt(fmt) "acpi/mrrm: " fmt
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+
+/* Default assume one memory region covering all system memory, per the spec */
+static int max_mem_region = 1;
+
+/* Access for use by resctrl file system */
+int acpi_mrrm_max_mem_region(void)
+{
+ return max_mem_region;
+}
+
+struct mrrm_mem_range_entry {
+ u64 base;
+ u64 length;
+ int node;
+ u8 local_region_id;
+ u8 remote_region_id;
+};
+
+static struct mrrm_mem_range_entry *mrrm_mem_range_entry;
+static u32 mrrm_mem_entry_num;
+
+static int get_node_num(struct mrrm_mem_range_entry *e)
+{
+ unsigned int nid;
+
+ for_each_online_node(nid) {
+ for (int z = 0; z < MAX_NR_ZONES; z++) {
+ struct zone *zone = NODE_DATA(nid)->node_zones + z;
+
+ if (!populated_zone(zone))
+ continue;
+ if (zone_intersects(zone, PHYS_PFN(e->base), PHYS_PFN(e->length)))
+ return zone_to_nid(zone);
+ }
+ }
+
+ return -ENOENT;
+}
+
+static __init int acpi_parse_mrrm(struct acpi_table_header *table)
+{
+ struct acpi_mrrm_mem_range_entry *mre_entry;
+ struct acpi_table_mrrm *mrrm;
+ void *mre, *mrrm_end;
+ int mre_count = 0;
+
+ mrrm = (struct acpi_table_mrrm *)table;
+ if (!mrrm)
+ return -ENODEV;
+
+ if (mrrm->header.revision != 1)
+ return -EINVAL;
+
+ if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
+ return -EOPNOTSUPP;
+
+ mrrm_end = (void *)mrrm + mrrm->header.length - 1;
+ mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
+ while (mre < mrrm_end) {
+ mre_entry = mre;
+ mre_count++;
+ mre += mre_entry->header.length;
+ }
+ if (!mre_count) {
+ pr_info(FW_BUG "No ranges listed in MRRM table\n");
+ return -EINVAL;
+ }
+
+ mrrm_mem_range_entry = kmalloc_array(mre_count, sizeof(*mrrm_mem_range_entry),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!mrrm_mem_range_entry)
+ return -ENOMEM;
+
+ mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
+ while (mre < mrrm_end) {
+ struct mrrm_mem_range_entry *e;
+
+ mre_entry = mre;
+ e = mrrm_mem_range_entry + mrrm_mem_entry_num;
+
+ e->base = mre_entry->addr_base;
+ e->length = mre_entry->addr_len;
+ e->node = get_node_num(e);
+
+ if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL)
+ e->local_region_id = mre_entry->local_region_id;
+ else
+ e->local_region_id = -1;
+ if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE)
+ e->remote_region_id = mre_entry->remote_region_id;
+ else
+ e->remote_region_id = -1;
+
+ mrrm_mem_entry_num++;
+ mre += mre_entry->header.length;
+ }
+
+ max_mem_region = mrrm->max_mem_region;
+
+ return 0;
+}
+
+#define RANGE_ATTR(name, fmt) \
+static ssize_t name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ struct mrrm_mem_range_entry *mre; \
+ const char *kname = kobject_name(kobj); \
+ int n, ret; \
+ \
+ ret = kstrtoint(kname + 5, 10, &n); \
+ if (ret) \
+ return ret; \
+ \
+ mre = mrrm_mem_range_entry + n; \
+ \
+ return sysfs_emit(buf, fmt, mre->name); \
+} \
+static struct kobj_attribute name##_attr = __ATTR_RO(name)
+
+RANGE_ATTR(base, "0x%llx\n");
+RANGE_ATTR(length, "0x%llx\n");
+RANGE_ATTR(node, "%d\n");
+RANGE_ATTR(local_region_id, "%d\n");
+RANGE_ATTR(remote_region_id, "%d\n");
+
+static struct attribute *memory_range_attrs[] = {
+ &base_attr.attr,
+ &length_attr.attr,
+ &node_attr.attr,
+ &local_region_id_attr.attr,
+ &remote_region_id_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(memory_range);
+
+static __init int add_boot_memory_ranges(void)
+{
+ struct kobject *pkobj, *kobj, **kobjs;
+ int ret = -EINVAL;
+ char name[16];
+ int i;
+
+ pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
+ if (!pkobj)
+ return -ENOMEM;
+
+ kobjs = kcalloc(mrrm_mem_entry_num, sizeof(*kobjs), GFP_KERNEL);
+ if (!kobjs) {
+ kobject_put(pkobj);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < mrrm_mem_entry_num; i++) {
+ scnprintf(name, sizeof(name), "range%d", i);
+ kobj = kobject_create_and_add(name, pkobj);
+ if (!kobj) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = sysfs_create_groups(kobj, memory_range_groups);
+ if (ret) {
+ kobject_put(kobj);
+ goto cleanup;
+ }
+ kobjs[i] = kobj;
+ }
+
+ kfree(kobjs);
+ return 0;
+
+cleanup:
+ for (int j = 0; j < i; j++) {
+ if (kobjs[j]) {
+ sysfs_remove_groups(kobjs[j], memory_range_groups);
+ kobject_put(kobjs[j]);
+ }
+ }
+ kfree(kobjs);
+ kobject_put(pkobj);
+ return ret;
+}
+
+static __init int mrrm_init(void)
+{
+ int ret;
+
+ ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm);
+ if (ret < 0)
+ return ret;
+
+ return add_boot_memory_ranges();
+}
+device_initcall(mrrm_init);
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
index ec0e22a1e25d..c9a0bcaba2e4 100644
--- a/drivers/acpi/acpi_pad.c
+++ b/drivers/acpi/acpi_pad.c
@@ -18,16 +18,22 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <asm/cpuid/api.h>
#include <asm/mwait.h>
#include <xen/xen.h>
#define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad"
#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80
+
+#define ACPI_PROCESSOR_AGGREGATOR_STATUS_SUCCESS 0
+#define ACPI_PROCESSOR_AGGREGATOR_STATUS_NO_ACTION 1
+
static DEFINE_MUTEX(isolated_cpus_lock);
static DEFINE_MUTEX(round_robin_lock);
-static unsigned long power_saving_mwait_eax;
+static unsigned int power_saving_mwait_eax;
static unsigned char tsc_detected_unstable;
static unsigned char tsc_marked_unstable;
@@ -41,10 +47,8 @@ static void power_saving_mwait_init(void)
if (!boot_cpu_has(X86_FEATURE_MWAIT))
return;
- if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
- return;
- cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
+ cpuid(CPUID_LEAF_MWAIT, &eax, &ebx, &ecx, &edx);
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
!(ecx & CPUID5_ECX_INTERRUPT_BREAK))
@@ -66,6 +70,7 @@ static void power_saving_mwait_init(void)
case X86_VENDOR_AMD:
case X86_VENDOR_INTEL:
case X86_VENDOR_ZHAOXIN:
+ case X86_VENDOR_CENTAUR:
/*
* AMD Fam10h TSC will tick in all
* C/P/S0/S1 states when this bit is set.
@@ -99,7 +104,7 @@ static void round_robin_cpu(unsigned int tsk_index)
for_each_cpu(cpu, pad_busy_cpus)
cpumask_or(tmp, tmp, topology_sibling_cpumask(cpu));
cpumask_andnot(tmp, cpu_online_mask, tmp);
- /* avoid HT sibilings if possible */
+ /* avoid HT siblings if possible */
if (cpumask_empty(tmp))
cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus);
if (cpumask_empty(tmp)) {
@@ -130,8 +135,10 @@ static void exit_round_robin(unsigned int tsk_index)
{
struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits);
- cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus);
- tsk_in_cpu[tsk_index] = -1;
+ if (tsk_in_cpu[tsk_index] != -1) {
+ cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus);
+ tsk_in_cpu[tsk_index] = -1;
+ }
}
static unsigned int idle_pct = 5; /* percentage */
@@ -287,7 +294,7 @@ static ssize_t rrtime_store(struct device *dev,
static ssize_t rrtime_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return scnprintf(buf, PAGE_SIZE, "%d\n", round_robin_time);
+ return sysfs_emit(buf, "%d\n", round_robin_time);
}
static DEVICE_ATTR_RW(rrtime);
@@ -309,7 +316,7 @@ static ssize_t idlepct_store(struct device *dev,
static ssize_t idlepct_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return scnprintf(buf, PAGE_SIZE, "%d\n", idle_pct);
+ return sysfs_emit(buf, "%d\n", idle_pct);
}
static DEVICE_ATTR_RW(idlepct);
@@ -335,33 +342,14 @@ static ssize_t idlecpus_show(struct device *dev,
static DEVICE_ATTR_RW(idlecpus);
-static int acpi_pad_add_sysfs(struct acpi_device *device)
-{
- int result;
-
- result = device_create_file(&device->dev, &dev_attr_idlecpus);
- if (result)
- return -ENODEV;
- result = device_create_file(&device->dev, &dev_attr_idlepct);
- if (result) {
- device_remove_file(&device->dev, &dev_attr_idlecpus);
- return -ENODEV;
- }
- result = device_create_file(&device->dev, &dev_attr_rrtime);
- if (result) {
- device_remove_file(&device->dev, &dev_attr_idlecpus);
- device_remove_file(&device->dev, &dev_attr_idlepct);
- return -ENODEV;
- }
- return 0;
-}
+static struct attribute *acpi_pad_attrs[] = {
+ &dev_attr_idlecpus.attr,
+ &dev_attr_idlepct.attr,
+ &dev_attr_rrtime.attr,
+ NULL
+};
-static void acpi_pad_remove_sysfs(struct acpi_device *device)
-{
- device_remove_file(&device->dev, &dev_attr_idlecpus);
- device_remove_file(&device->dev, &dev_attr_idlepct);
- device_remove_file(&device->dev, &dev_attr_rrtime);
-}
+ATTRIBUTE_GROUPS(acpi_pad);
/*
* Query firmware how many CPUs should be idle
@@ -399,29 +387,36 @@ static void acpi_pad_handle_notify(acpi_handle handle)
.length = 4,
.pointer = (void *)&idle_cpus,
};
+ u32 status;
mutex_lock(&isolated_cpus_lock);
num_cpus = acpi_pad_pur(handle);
if (num_cpus < 0) {
- mutex_unlock(&isolated_cpus_lock);
- return;
+ /* The ACPI specification says that if no action was performed when
+ * processing the _PUR object, _OST should still be evaluated, albeit
+ * with a different status code.
+ */
+ status = ACPI_PROCESSOR_AGGREGATOR_STATUS_NO_ACTION;
+ } else {
+ status = ACPI_PROCESSOR_AGGREGATOR_STATUS_SUCCESS;
+ acpi_pad_idle_cpus(num_cpus);
}
- acpi_pad_idle_cpus(num_cpus);
+
idle_cpus = acpi_pad_idle_cpus_num();
- acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, 0, &param);
+ acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, status, &param);
mutex_unlock(&isolated_cpus_lock);
}
static void acpi_pad_notify(acpi_handle handle, u32 event,
void *data)
{
- struct acpi_device *device = data;
+ struct acpi_device *adev = data;
switch (event) {
case ACPI_PROCESSOR_AGGREGATOR_NOTIFY:
acpi_pad_handle_notify(handle);
- acpi_bus_generate_netlink_event(device->pnp.device_class,
- dev_name(&device->dev), event, 0);
+ acpi_bus_generate_netlink_event(adev->pnp.device_class,
+ dev_name(&adev->dev), event, 0);
break;
default:
pr_warn("Unsupported event [0x%x]\n", event);
@@ -429,36 +424,33 @@ static void acpi_pad_notify(acpi_handle handle, u32 event,
}
}
-static int acpi_pad_add(struct acpi_device *device)
+static int acpi_pad_probe(struct platform_device *pdev)
{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
acpi_status status;
- strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS);
+ strscpy(acpi_device_name(adev), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
+ strscpy(acpi_device_class(adev), ACPI_PROCESSOR_AGGREGATOR_CLASS);
- if (acpi_pad_add_sysfs(device))
- return -ENODEV;
+ status = acpi_install_notify_handler(adev->handle,
+ ACPI_DEVICE_NOTIFY, acpi_pad_notify, adev);
- status = acpi_install_notify_handler(device->handle,
- ACPI_DEVICE_NOTIFY, acpi_pad_notify, device);
- if (ACPI_FAILURE(status)) {
- acpi_pad_remove_sysfs(device);
+ if (ACPI_FAILURE(status))
return -ENODEV;
- }
return 0;
}
-static int acpi_pad_remove(struct acpi_device *device)
+static void acpi_pad_remove(struct platform_device *pdev)
{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+
mutex_lock(&isolated_cpus_lock);
acpi_pad_idle_cpus(0);
mutex_unlock(&isolated_cpus_lock);
- acpi_remove_notify_handler(device->handle,
+ acpi_remove_notify_handler(adev->handle,
ACPI_DEVICE_NOTIFY, acpi_pad_notify);
- acpi_pad_remove_sysfs(device);
- return 0;
}
static const struct acpi_device_id pad_device_ids[] = {
@@ -467,13 +459,13 @@ static const struct acpi_device_id pad_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, pad_device_ids);
-static struct acpi_driver acpi_pad_driver = {
- .name = "processor_aggregator",
- .class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
- .ids = pad_device_ids,
- .ops = {
- .add = acpi_pad_add,
- .remove = acpi_pad_remove,
+static struct platform_driver acpi_pad_driver = {
+ .probe = acpi_pad_probe,
+ .remove = acpi_pad_remove,
+ .driver = {
+ .dev_groups = acpi_pad_groups,
+ .name = "processor_aggregator",
+ .acpi_match_table = pad_device_ids,
},
};
@@ -487,12 +479,12 @@ static int __init acpi_pad_init(void)
if (power_saving_mwait_eax == 0)
return -EINVAL;
- return acpi_bus_register_driver(&acpi_pad_driver);
+ return platform_driver_register(&acpi_pad_driver);
}
static void __exit acpi_pad_exit(void)
{
- acpi_bus_unregister_driver(&acpi_pad_driver);
+ platform_driver_unregister(&acpi_pad_driver);
}
module_init(acpi_pad_init);
diff --git a/drivers/acpi/acpi_pcc.c b/drivers/acpi/acpi_pcc.c
index a12b55d81209..97064e943768 100644
--- a/drivers/acpi/acpi_pcc.c
+++ b/drivers/acpi/acpi_pcc.c
@@ -23,9 +23,14 @@
#include <acpi/pcc.h>
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define PCC_CMD_WAIT_RETRIES_NUM 500ULL
+
struct pcc_data {
struct pcc_mbox_chan *pcc_chan;
- void __iomem *pcc_comm_addr;
struct completion done;
struct mbox_client cl;
struct acpi_pcc_info ctx;
@@ -47,6 +52,7 @@ acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
struct pcc_data *data;
struct acpi_pcc_info *ctx = handler_context;
struct pcc_mbox_chan *pcc_chan;
+ static acpi_status ret;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -63,20 +69,27 @@ acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
if (IS_ERR(data->pcc_chan)) {
pr_err("Failed to find PCC channel for subspace %d\n",
ctx->subspace_id);
- return AE_NOT_FOUND;
+ ret = AE_NOT_FOUND;
+ goto err_free_data;
}
pcc_chan = data->pcc_chan;
- data->pcc_comm_addr = acpi_os_ioremap(pcc_chan->shmem_base_addr,
- pcc_chan->shmem_size);
- if (!data->pcc_comm_addr) {
- pr_err("Failed to ioremap PCC comm region mem for %d\n",
+ if (!pcc_chan->mchan->mbox->txdone_irq) {
+ pr_err("This channel-%d does not support interrupt.\n",
ctx->subspace_id);
- return AE_NO_MEMORY;
+ ret = AE_SUPPORT;
+ goto err_free_channel;
}
*region_context = data;
return AE_OK;
+
+err_free_channel:
+ pcc_mbox_free_channel(data->pcc_chan);
+err_free_data:
+ kfree(data);
+
+ return ret;
}
static acpi_status
@@ -86,22 +99,33 @@ acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
{
int ret;
struct pcc_data *data = region_context;
+ u64 usecs_lat;
reinit_completion(&data->done);
/* Write to Shared Memory */
- memcpy_toio(data->pcc_comm_addr, (void *)value, data->ctx.length);
+ memcpy_toio(data->pcc_chan->shmem, (void *)value, data->ctx.length);
ret = mbox_send_message(data->pcc_chan->mchan, NULL);
if (ret < 0)
return AE_ERROR;
- if (data->pcc_chan->mchan->mbox->txdone_irq)
- wait_for_completion(&data->done);
+ /*
+ * pcc_chan->latency is just a Nominal value. In reality the remote
+ * processor could be much slower to reply. So add an arbitrary
+ * amount of wait on top of Nominal.
+ */
+ usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency;
+ ret = wait_for_completion_timeout(&data->done,
+ usecs_to_jiffies(usecs_lat));
+ if (ret == 0) {
+ pr_err("PCC command executed timeout!\n");
+ return AE_TIME;
+ }
- mbox_client_txdone(data->pcc_chan->mchan, ret);
+ mbox_chan_txdone(data->pcc_chan->mchan, ret);
- memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length);
+ memcpy_fromio(value, data->pcc_chan->shmem, data->ctx.length);
return AE_OK;
}
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index de3cbf152dee..48d15dd785f6 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -9,6 +9,7 @@
*/
#include <linux/acpi.h>
+#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
@@ -19,14 +20,17 @@
#include "internal.h"
+/* Exclude devices that have no _CRS resources provided */
+#define ACPI_ALLOW_WO_RESOURCES BIT(0)
+
static const struct acpi_device_id forbidden_id_list[] = {
+ {"ACPI0009", 0}, /* IOxAPIC */
+ {"ACPI000A", 0}, /* IOAPIC */
{"PNP0000", 0}, /* PIC */
{"PNP0100", 0}, /* Timer */
{"PNP0200", 0}, /* AT DMA Controller */
- {"ACPI0009", 0}, /* IOxAPIC */
- {"ACPI000A", 0}, /* IOAPIC */
- {"SMB0001", 0}, /* ACPI SMBUS virtual device */
- {"", 0},
+ {ACPI_SMBUS_MS_HID, ACPI_ALLOW_WO_RESOURCES}, /* ACPI SMBUS virtual device */
+ { }
};
static struct platform_device *acpi_platform_device_find_by_companion(struct acpi_device *adev)
@@ -78,11 +82,20 @@ static void acpi_platform_fill_resource(struct acpi_device *adev,
* If the device has parent we need to take its resources into
* account as well because this device might consume part of those.
*/
- parent = acpi_get_first_physical_node(adev->parent);
+ parent = acpi_get_first_physical_node(acpi_dev_parent(adev));
if (parent && dev_is_pci(parent))
dest->parent = pci_find_resource(to_pci_dev(parent), dest);
}
+static unsigned int acpi_platform_resource_count(struct acpi_resource *ares, void *data)
+{
+ bool *has_resources = data;
+
+ *has_resources = true;
+
+ return AE_CTRL_TERMINATE;
+}
+
/**
* acpi_create_platform_device - Create platform device for ACPI device node
* @adev: ACPI device node to create a platform device for.
@@ -97,8 +110,10 @@ static void acpi_platform_fill_resource(struct acpi_device *adev,
struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
const struct property_entry *properties)
{
+ struct acpi_device *parent = acpi_dev_parent(adev);
struct platform_device *pdev = NULL;
struct platform_device_info pdevinfo;
+ const struct acpi_device_id *match;
struct resource_entry *rentry;
struct list_head resource_list;
struct resource *resources = NULL;
@@ -108,18 +123,27 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
if (adev->physical_node_count)
return NULL;
- if (!acpi_match_device_ids(adev, forbidden_id_list))
- return ERR_PTR(-EINVAL);
+ match = acpi_match_acpi_device(forbidden_id_list, adev);
+ if (match) {
+ if (match->driver_data & ACPI_ALLOW_WO_RESOURCES) {
+ bool has_resources = false;
+
+ acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ acpi_platform_resource_count, &has_resources);
+ if (has_resources)
+ return ERR_PTR(-EINVAL);
+ } else {
+ return ERR_PTR(-EINVAL);
+ }
+ }
INIT_LIST_HEAD(&resource_list);
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
- if (count < 0) {
+ if (count < 0)
return NULL;
- } else if (count > 0) {
- resources = kcalloc(count, sizeof(struct resource),
- GFP_KERNEL);
+ if (count > 0) {
+ resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
if (!resources) {
- dev_err(&adev->dev, "No memory for resources\n");
acpi_dev_free_resource_list(&resource_list);
return ERR_PTR(-ENOMEM);
}
@@ -137,10 +161,9 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
* attached to it, that physical device should be the parent of the
* platform device we are about to create.
*/
- pdevinfo.parent = adev->parent ?
- acpi_get_first_physical_node(adev->parent) : NULL;
+ pdevinfo.parent = parent ? acpi_get_first_physical_node(parent) : NULL;
pdevinfo.name = dev_name(&adev->dev);
- pdevinfo.id = -1;
+ pdevinfo.id = PLATFORM_DEVID_NONE;
pdevinfo.res = resources;
pdevinfo.num_res = count;
pdevinfo.fwnode = acpi_fwnode_handle(adev);
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
index ffdcfcd4a10d..4ad88187dc7a 100644
--- a/drivers/acpi/acpi_pnp.c
+++ b/drivers/acpi/acpi_pnp.c
@@ -120,8 +120,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = {
{"IBM0071"},
/* smsc-ircc2 */
{"SMCf010"},
- /* sb1000 */
- {"GIC1000"},
/* parport_pc */
{"PNP0400"}, /* Standard LPT Printer Port */
{"PNP0401"}, /* ECP Printer Port */
@@ -348,10 +346,24 @@ static bool acpi_pnp_match(const char *idstr, const struct acpi_device_id **matc
return false;
}
+/*
+ * If one of the device IDs below is present in the list of device IDs of a
+ * given ACPI device object, the PNP scan handler will not attach to that
+ * object, because there is a proper non-PNP driver in the kernel for the
+ * device represented by it.
+ */
+static const struct acpi_device_id acpi_nonpnp_device_ids[] = {
+ {"INT3F0D"},
+ {"INTC1080"},
+ {"INTC1081"},
+ {"INTC1099"},
+ {""},
+};
+
static int acpi_pnp_attach(struct acpi_device *adev,
const struct acpi_device_id *id)
{
- return 1;
+ return !!acpi_match_device_ids(adev, acpi_nonpnp_device_ids);
}
static struct acpi_scan_handler acpi_pnp_handler = {
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 6737b1cbf6d6..7ec1dc04fd11 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -9,17 +9,23 @@
* Copyright (C) 2013, Intel Corporation
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*/
+#define pr_fmt(fmt) "ACPI: " fmt
#include <linux/acpi.h>
+#include <linux/cpu.h>
#include <linux/device.h>
+#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <acpi/processor.h>
#include <asm/cpu.h>
+#include <xen/xen.h>
+
#include "internal.h"
DEFINE_PER_CPU(struct acpi_processor *, processors);
@@ -29,6 +35,17 @@ EXPORT_PER_CPU_SYMBOL(processors);
struct acpi_processor_errata errata __read_mostly;
EXPORT_SYMBOL_GPL(errata);
+acpi_handle acpi_get_processor_handle(int cpu)
+{
+ struct acpi_processor *pr;
+
+ pr = per_cpu(processors, cpu);
+ if (pr)
+ return pr->handle;
+
+ return NULL;
+}
+
static int acpi_processor_errata_piix4(struct pci_dev *dev)
{
u8 value1 = 0;
@@ -148,39 +165,73 @@ static int acpi_processor_errata(void)
return result;
}
-/* Initialization */
-#ifdef CONFIG_ACPI_HOTPLUG_CPU
-int __weak acpi_map_cpu(acpi_handle handle,
- phys_cpuid_t physid, u32 acpi_id, int *pcpu)
+/* Create a platform device to represent a CPU frequency control mechanism. */
+static void cpufreq_add_device(const char *name)
{
- return -ENODEV;
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_simple(name, PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(pdev))
+ pr_info("%s device creation failed: %pe\n", name, pdev);
}
-int __weak acpi_unmap_cpu(int cpu)
+#ifdef CONFIG_X86
+/* Check presence of Processor Clocking Control by searching for \_SB.PCCH. */
+static void __init acpi_pcc_cpufreq_init(void)
{
- return -ENODEV;
+ acpi_status status;
+ acpi_handle handle;
+
+ status = acpi_get_handle(NULL, "\\_SB", &handle);
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (acpi_has_method(handle, "PCCH"))
+ cpufreq_add_device("pcc-cpufreq");
}
+#else
+static void __init acpi_pcc_cpufreq_init(void) {}
+#endif /* CONFIG_X86 */
-int __weak arch_register_cpu(int cpu)
+/* Initialization */
+static DEFINE_PER_CPU(void *, processor_device_array);
+
+static int acpi_processor_set_per_cpu(struct acpi_processor *pr,
+ struct acpi_device *device)
{
- return -ENODEV;
-}
+ BUG_ON(pr->id >= nr_cpu_ids);
+
+ /*
+ * Buggy BIOS check.
+ * ACPI id of processors can be reported wrongly by the BIOS.
+ * Don't trust it blindly
+ */
+ if (per_cpu(processor_device_array, pr->id) != NULL &&
+ per_cpu(processor_device_array, pr->id) != device) {
+ dev_warn(&device->dev,
+ "BIOS reported wrong ACPI id %d for the processor\n",
+ pr->id);
+ return -EINVAL;
+ }
+ /*
+ * processor_device_array is not cleared on errors to allow buggy BIOS
+ * checks.
+ */
+ per_cpu(processor_device_array, pr->id) = device;
+ per_cpu(processors, pr->id) = pr;
-void __weak arch_unregister_cpu(int cpu) {}
+ return 0;
+}
-static int acpi_processor_hotadd_init(struct acpi_processor *pr)
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+static int acpi_processor_hotadd_init(struct acpi_processor *pr,
+ struct acpi_device *device)
{
- unsigned long long sta;
- acpi_status status;
int ret;
if (invalid_phys_cpuid(pr->phys_id))
return -ENODEV;
- status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta);
- if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
- return -ENODEV;
-
cpu_maps_update_begin();
cpus_write_lock();
@@ -188,19 +239,26 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
if (ret)
goto out;
+ ret = acpi_processor_set_per_cpu(pr, device);
+ if (ret) {
+ acpi_unmap_cpu(pr->id);
+ goto out;
+ }
+
ret = arch_register_cpu(pr->id);
if (ret) {
+ /* Leave the processor device array in place to detect buggy bios */
+ per_cpu(processors, pr->id) = NULL;
acpi_unmap_cpu(pr->id);
goto out;
}
/*
- * CPU got hot-added, but cpu_data is not initialized yet. Set a flag
- * to delay cpu_idle/throttling initialization and do it when the CPU
- * gets online for the first time.
+ * CPU got hot-added, but cpu_data is not initialized yet. Do
+ * cpu_idle/throttling initialization when the CPU gets online for
+ * the first time.
*/
pr_info("CPU%d has been hot-added\n", pr->id);
- pr->flags.need_hotplug_init = 1;
out:
cpus_write_unlock();
@@ -208,7 +266,8 @@ out:
return ret;
}
#else
-static inline int acpi_processor_hotadd_init(struct acpi_processor *pr)
+static inline int acpi_processor_hotadd_init(struct acpi_processor *pr,
+ struct acpi_device *device)
{
return -ENODEV;
}
@@ -216,13 +275,14 @@ static inline int acpi_processor_hotadd_init(struct acpi_processor *pr)
static int acpi_processor_get_info(struct acpi_device *device)
{
- union acpi_object object = { 0 };
+ union acpi_object object = { .processor = { 0 } };
struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
struct acpi_processor *pr = acpi_driver_data(device);
int device_declaration = 0;
acpi_status status = AE_OK;
static int cpu0_initialized;
unsigned long long value;
+ int ret;
acpi_processor_errata();
@@ -280,30 +340,38 @@ static int acpi_processor_get_info(struct acpi_device *device)
dev_dbg(&device->dev, "Failed to get CPU physical ID.\n");
pr->id = acpi_map_cpuid(pr->phys_id, pr->acpi_id);
- if (!cpu0_initialized && !acpi_has_cpu_in_madt()) {
+ if (!cpu0_initialized) {
cpu0_initialized = 1;
/*
* Handle UP system running SMP kernel, with no CPU
* entry in MADT
*/
- if (invalid_logical_cpuid(pr->id) && (num_online_cpus() == 1))
+ if (!acpi_has_cpu_in_madt() && invalid_logical_cpuid(pr->id) &&
+ (num_online_cpus() == 1))
pr->id = 0;
+ /*
+ * Check availability of Processor Performance Control by
+ * looking at the presence of the _PCT object under the first
+ * processor definition.
+ */
+ if (acpi_has_method(pr->handle, "_PCT"))
+ cpufreq_add_device("acpi-cpufreq");
}
/*
- * Extra Processor objects may be enumerated on MP systems with
- * less than the max # of CPUs. They should be ignored _iff
- * they are physically not present.
- *
- * NOTE: Even if the processor has a cpuid, it may not be present
- * because cpuid <-> apicid mapping is persistent now.
+ * This code is not called unless we know the CPU is present and
+ * enabled. The two paths are:
+ * a) Initially present CPUs on architectures that do not defer
+ * their arch_register_cpu() calls until this point.
+ * b) Hotplugged CPUs (enabled bit in _STA has transitioned from not
+ * enabled to enabled)
*/
- if (invalid_logical_cpuid(pr->id) || !cpu_present(pr->id)) {
- int ret = acpi_processor_hotadd_init(pr);
-
- if (ret)
- return ret;
- }
+ if (!get_cpu_device(pr->id))
+ ret = acpi_processor_hotadd_init(pr, device);
+ else
+ ret = acpi_processor_set_per_cpu(pr, device);
+ if (ret)
+ return ret;
/*
* On some boxes several processors use the same processor bus id.
@@ -348,8 +416,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
* (cpu_data(cpu)) values, like CPU feature flags, family, model, etc.
* Such things have to be put in and set up by the processor driver's .probe().
*/
-static DEFINE_PER_CPU(void *, processor_device_array);
-
static int acpi_processor_add(struct acpi_device *device,
const struct acpi_device_id *id)
{
@@ -357,6 +423,9 @@ static int acpi_processor_add(struct acpi_device *device,
struct device *dev;
int result = 0;
+ if (!acpi_device_is_enabled(device))
+ return -ENODEV;
+
pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
if (!pr)
return -ENOMEM;
@@ -367,45 +436,23 @@ static int acpi_processor_add(struct acpi_device *device,
}
pr->handle = device->handle;
- strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS);
+ strscpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS);
device->driver_data = pr;
result = acpi_processor_get_info(device);
if (result) /* Processor is not physically present or unavailable */
- return 0;
-
- BUG_ON(pr->id >= nr_cpu_ids);
-
- /*
- * Buggy BIOS check.
- * ACPI id of processors can be reported wrongly by the BIOS.
- * Don't trust it blindly
- */
- if (per_cpu(processor_device_array, pr->id) != NULL &&
- per_cpu(processor_device_array, pr->id) != device) {
- dev_warn(&device->dev,
- "BIOS reported wrong ACPI id %d for the processor\n",
- pr->id);
- /* Give up, but do not abort the namespace scan. */
- goto err;
- }
- /*
- * processor_device_array is not cleared on errors to allow buggy BIOS
- * checks.
- */
- per_cpu(processor_device_array, pr->id) = device;
- per_cpu(processors, pr->id) = pr;
+ goto err_clear_driver_data;
dev = get_cpu_device(pr->id);
if (!dev) {
result = -ENODEV;
- goto err;
+ goto err_clear_per_cpu;
}
result = acpi_bind_one(dev, device);
if (result)
- goto err;
+ goto err_clear_per_cpu;
pr->dev = dev;
@@ -416,10 +463,11 @@ static int acpi_processor_add(struct acpi_device *device,
dev_err(dev, "Processor driver could not be attached\n");
acpi_unbind_one(dev);
- err:
- free_cpumask_var(pr->throttling.shared_cpu_map);
- device->driver_data = NULL;
+ err_clear_per_cpu:
per_cpu(processors, pr->id) = NULL;
+ err_clear_driver_data:
+ device->driver_data = NULL;
+ free_cpumask_var(pr->throttling.shared_cpu_map);
err_free_pr:
kfree(pr);
return result;
@@ -427,7 +475,7 @@ static int acpi_processor_add(struct acpi_device *device,
#ifdef CONFIG_ACPI_HOTPLUG_CPU
/* Removal */
-static void acpi_processor_remove(struct acpi_device *device)
+static void acpi_processor_post_eject(struct acpi_device *device)
{
struct acpi_processor *pr;
@@ -449,10 +497,6 @@ static void acpi_processor_remove(struct acpi_device *device)
device_release_driver(pr->dev);
acpi_unbind_one(pr->dev);
- /* Clean up. */
- per_cpu(processor_device_array, pr->id) = NULL;
- per_cpu(processors, pr->id) = NULL;
-
cpu_maps_update_begin();
cpus_write_lock();
@@ -460,6 +504,10 @@ static void acpi_processor_remove(struct acpi_device *device)
arch_unregister_cpu(pr->id);
acpi_unmap_cpu(pr->id);
+ /* Clean up. */
+ per_cpu(processor_device_array, pr->id) = NULL;
+ per_cpu(processors, pr->id) = NULL;
+
cpus_write_unlock();
cpu_maps_update_done();
@@ -471,54 +519,110 @@ static void acpi_processor_remove(struct acpi_device *device)
}
#endif /* CONFIG_ACPI_HOTPLUG_CPU */
-#ifdef CONFIG_X86
-static bool acpi_hwp_native_thermal_lvt_set;
-static acpi_status __init acpi_hwp_native_thermal_lvt_osc(acpi_handle handle,
- u32 lvl,
- void *context,
- void **rv)
+#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
+bool __init processor_physically_present(acpi_handle handle)
{
- u8 sb_uuid_str[] = "4077A616-290C-47BE-9EBD-D87058713953";
- u32 capbuf[2];
+ int cpuid, type;
+ u32 acpi_id;
+ acpi_status status;
+ acpi_object_type acpi_type;
+ unsigned long long tmp;
+ union acpi_object object = {};
+ struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
+
+ status = acpi_get_type(handle, &acpi_type);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ switch (acpi_type) {
+ case ACPI_TYPE_PROCESSOR:
+ status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return false;
+ acpi_id = object.processor.proc_id;
+ break;
+ case ACPI_TYPE_DEVICE:
+ status = acpi_evaluate_integer(handle, METHOD_NAME__UID,
+ NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return false;
+ acpi_id = tmp;
+ break;
+ default:
+ return false;
+ }
+
+ if (xen_initial_domain())
+ /*
+ * When running as a Xen dom0 the number of processors Linux
+ * sees can be different from the real number of processors on
+ * the system, and we still need to execute _PDC or _OSC for
+ * all of them.
+ */
+ return xen_processor_present(acpi_id);
+
+ type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0;
+ cpuid = acpi_get_cpuid(handle, type, acpi_id);
+
+ return !invalid_logical_cpuid(cpuid);
+}
+
+/* vendor specific UUID indicating an Intel platform */
+static u8 sb_uuid_str[] = "4077A616-290C-47BE-9EBD-D87058713953";
+
+static acpi_status __init acpi_processor_osc(acpi_handle handle, u32 lvl,
+ void *context, void **rv)
+{
+ u32 capbuf[2] = {};
struct acpi_osc_context osc_context = {
.uuid_str = sb_uuid_str,
.rev = 1,
.cap.length = 8,
.cap.pointer = capbuf,
};
+ acpi_status status;
- if (acpi_hwp_native_thermal_lvt_set)
- return AE_CTRL_TERMINATE;
+ if (!processor_physically_present(handle))
+ return AE_OK;
- capbuf[0] = 0x0000;
- capbuf[1] = 0x1000; /* set bit 12 */
+ arch_acpi_set_proc_cap_bits(&capbuf[OSC_SUPPORT_DWORD]);
- if (ACPI_SUCCESS(acpi_run_osc(handle, &osc_context))) {
- if (osc_context.ret.pointer && osc_context.ret.length > 1) {
- u32 *capbuf_ret = osc_context.ret.pointer;
+ status = acpi_run_osc(handle, &osc_context);
+ if (ACPI_FAILURE(status))
+ return status;
- if (capbuf_ret[1] & 0x1000) {
- acpi_handle_info(handle,
- "_OSC native thermal LVT Acked\n");
- acpi_hwp_native_thermal_lvt_set = true;
- }
- }
- kfree(osc_context.ret.pointer);
- }
+ kfree(osc_context.ret.pointer);
return AE_OK;
}
-void __init acpi_early_processor_osc(void)
+static bool __init acpi_early_processor_osc(void)
+{
+ acpi_status status;
+
+ acpi_proc_quirk_mwait_check();
+
+ status = acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_processor_osc, NULL,
+ NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ status = acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, acpi_processor_osc,
+ NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ return true;
+}
+
+void __init acpi_early_processor_control_setup(void)
{
- if (boot_cpu_has(X86_FEATURE_HWP)) {
- acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX,
- acpi_hwp_native_thermal_lvt_osc,
- NULL, NULL, NULL);
- acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID,
- acpi_hwp_native_thermal_lvt_osc,
- NULL, NULL);
+ if (acpi_early_processor_osc()) {
+ pr_debug("_OSC evaluated successfully for all CPUs\n");
+ } else {
+ pr_debug("_OSC evaluation for CPUs failed, trying _PDC\n");
+ acpi_early_processor_set_pdc();
}
}
#endif
@@ -539,7 +643,7 @@ static struct acpi_scan_handler processor_handler = {
.ids = processor_device_ids,
.attach = acpi_processor_add,
#ifdef CONFIG_ACPI_HOTPLUG_CPU
- .detach = acpi_processor_remove,
+ .post_eject = acpi_processor_post_eject,
#endif
.hotplug = {
.enabled = true,
@@ -686,6 +790,7 @@ void __init acpi_processor_init(void)
acpi_processor_check_duplicates();
acpi_scan_add_handler_with_hotplug(&processor_handler, "processor");
acpi_scan_add_handler(&processor_container_handler);
+ acpi_pcc_cpufreq_init();
}
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
@@ -710,7 +815,7 @@ bool acpi_processor_claim_cst_control(void)
cst_control_claimed = true;
return true;
}
-EXPORT_SYMBOL_GPL(acpi_processor_claim_cst_control);
+EXPORT_SYMBOL_NS_GPL(acpi_processor_claim_cst_control, "ACPI_PROCESSOR_IDLE");
/**
* acpi_processor_evaluate_cst - Evaluate the processor _CST control method.
@@ -880,7 +985,7 @@ int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
memcpy(&info->states[++last_index], &cx, sizeof(cx));
}
- acpi_handle_info(handle, "Found %d idle states\n", last_index);
+ acpi_handle_debug(handle, "Found %d idle states\n", last_index);
info->count = last_index;
@@ -889,5 +994,5 @@ end:
return ret;
}
-EXPORT_SYMBOL_GPL(acpi_processor_evaluate_cst);
+EXPORT_SYMBOL_NS_GPL(acpi_processor_evaluate_cst, "ACPI_PROCESSOR_IDLE");
#endif /* CONFIG_ACPI_PROCESSOR_CSTATE */
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
index e9b8e8305e23..6d870d97ada6 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -27,6 +27,7 @@
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
+MODULE_DESCRIPTION("ACPI Time and Alarm (TAD) Device Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rafael J. Wysocki");
@@ -89,19 +90,18 @@ static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)
args[0].buffer.pointer = (u8 *)rt;
args[0].buffer.length = sizeof(*rt);
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status) || retval)
return -EIO;
return 0;
}
-static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)
+static int acpi_tad_evaluate_grt(struct device *dev, struct acpi_tad_rt *rt)
{
acpi_handle handle = ACPI_HANDLE(dev);
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER };
@@ -110,12 +110,7 @@ static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)
acpi_status status;
int ret = -EIO;
- pm_runtime_get_sync(dev);
-
status = acpi_evaluate_object(handle, "_GRT", NULL, &output);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status))
goto out_free;
@@ -138,6 +133,21 @@ out_free:
return ret;
}
+static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)
+{
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ ret = acpi_tad_evaluate_grt(dev, rt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static char *acpi_tad_rt_next_field(char *s, int *val)
{
char *p;
@@ -232,7 +242,7 @@ static ssize_t time_show(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n",
+ return sysfs_emit(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n",
rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second,
rt.tz, rt.daylight);
}
@@ -265,12 +275,11 @@ static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
args[0].integer.value = timer_id;
args[1].integer.value = value;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -313,12 +322,11 @@ static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,
args[0].integer.value = timer_id;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status))
return -EIO;
@@ -369,12 +377,11 @@ static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
args[0].integer.value = timer_id;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -410,12 +417,11 @@ static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
args[0].integer.value = timer_id;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status))
return -EIO;
@@ -427,7 +433,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
{
struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
- return sprintf(buf, "0x%02X\n", dd->capabilities);
+ return sysfs_emit(buf, "0x%02X\n", dd->capabilities);
}
static DEVICE_ATTR_RO(caps);
@@ -554,30 +560,34 @@ static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)
return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED);
}
-static int acpi_tad_remove(struct platform_device *pdev)
+static void acpi_tad_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ acpi_handle handle = ACPI_HANDLE(dev);
struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
device_init_wakeup(dev, false);
- pm_runtime_get_sync(dev);
+ if (dd->capabilities & ACPI_TAD_RT)
+ sysfs_remove_group(&dev->kobj, &acpi_tad_time_attr_group);
if (dd->capabilities & ACPI_TAD_DC_WAKE)
sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group);
sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);
- acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
- acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
- if (dd->capabilities & ACPI_TAD_DC_WAKE) {
- acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
- acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+ scoped_guard(pm_runtime_noresume, dev) {
+ acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
+ acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
+ if (dd->capabilities & ACPI_TAD_DC_WAKE) {
+ acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
+ acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+ }
}
- pm_runtime_put_sync(dev);
+ pm_runtime_suspend(dev);
pm_runtime_disable(dev);
- return 0;
+ acpi_remove_cmos_rtc_space_handler(handle);
}
static int acpi_tad_probe(struct platform_device *pdev)
@@ -589,6 +599,11 @@ static int acpi_tad_probe(struct platform_device *pdev)
unsigned long long caps;
int ret;
+ ret = acpi_install_cmos_rtc_space_handler(handle);
+ if (ret < 0) {
+ dev_info(dev, "Unable to install space handler\n");
+ return -ENODEV;
+ }
/*
* Initialization failure messages are mostly about firmware issues, so
* print them at the "info" level.
@@ -596,22 +611,27 @@ static int acpi_tad_probe(struct platform_device *pdev)
status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);
if (ACPI_FAILURE(status)) {
dev_info(dev, "Unable to get capabilities\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto remove_handler;
}
if (!(caps & ACPI_TAD_AC_WAKE)) {
dev_info(dev, "Unsupported capabilities\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto remove_handler;
}
if (!acpi_has_method(handle, "_PRW")) {
dev_info(dev, "Missing _PRW\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto remove_handler;
}
dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
- if (!dd)
- return -ENOMEM;
+ if (!dd) {
+ ret = -ENOMEM;
+ goto remove_handler;
+ }
dd->capabilities = caps;
dev_set_drvdata(dev, dd);
@@ -653,6 +673,11 @@ static int acpi_tad_probe(struct platform_device *pdev)
fail:
acpi_tad_remove(pdev);
+ /* Don't fallthrough because cmos rtc space handler is removed in acpi_tad_remove() */
+ return ret;
+
+remove_handler:
+ acpi_remove_cmos_rtc_space_handler(handle);
return ret;
}
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 5cbe2196176d..be8e7e18abca 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -27,6 +27,7 @@
#include <linux/acpi.h>
#include <acpi/video.h>
#include <linux/uaccess.h>
+#include <linux/string_choices.h>
#define ACPI_VIDEO_BUS_NAME "Video Bus"
#define ACPI_VIDEO_DEVICE_NAME "Video Device"
@@ -47,9 +48,6 @@ module_param(brightness_switch_enabled, bool, 0644);
static bool allow_duplicates;
module_param(allow_duplicates, bool, 0644);
-static int disable_backlight_sysfs_if = -1;
-module_param(disable_backlight_sysfs_if, int, 0444);
-
#define REPORT_OUTPUT_KEY_EVENTS 0x01
#define REPORT_BRIGHTNESS_KEY_EVENTS 0x02
static int report_key_events = -1;
@@ -70,7 +68,7 @@ MODULE_PARM_DESC(hw_changes_brightness,
static bool device_id_scheme = false;
module_param(device_id_scheme, bool, 0444);
-static int only_lcd = -1;
+static int only_lcd;
module_param(only_lcd, int, 0444);
static bool may_report_brightness_keys;
@@ -79,9 +77,8 @@ static DEFINE_MUTEX(register_count_mutex);
static DEFINE_MUTEX(video_list_lock);
static LIST_HEAD(video_bus_head);
static int acpi_video_bus_add(struct acpi_device *device);
-static int acpi_video_bus_remove(struct acpi_device *device);
-static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
-void acpi_video_detect_exit(void);
+static void acpi_video_bus_remove(struct acpi_device *device);
+static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data);
/*
* Indices in the _BCL method response: the first two items are special,
@@ -108,7 +105,6 @@ static struct acpi_driver acpi_video_bus = {
.ops = {
.add = acpi_video_bus_add,
.remove = acpi_video_bus_remove,
- .notify = acpi_video_bus_notify,
},
};
@@ -258,8 +254,7 @@ static const struct backlight_ops acpi_backlight_ops = {
static int video_get_max_state(struct thermal_cooling_device *cooling_dev,
unsigned long *state)
{
- struct acpi_device *device = cooling_dev->devdata;
- struct acpi_video_device *video = acpi_driver_data(device);
+ struct acpi_video_device *video = cooling_dev->devdata;
*state = video->brightness->count - ACPI_VIDEO_FIRST_LEVEL - 1;
return 0;
@@ -268,8 +263,7 @@ static int video_get_max_state(struct thermal_cooling_device *cooling_dev,
static int video_get_cur_state(struct thermal_cooling_device *cooling_dev,
unsigned long *state)
{
- struct acpi_device *device = cooling_dev->devdata;
- struct acpi_video_device *video = acpi_driver_data(device);
+ struct acpi_video_device *video = cooling_dev->devdata;
unsigned long long level;
int offset;
@@ -288,8 +282,7 @@ static int video_get_cur_state(struct thermal_cooling_device *cooling_dev,
static int
video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state)
{
- struct acpi_device *device = cooling_dev->devdata;
- struct acpi_video_device *video = acpi_driver_data(device);
+ struct acpi_video_device *video = cooling_dev->devdata;
int level;
if (state >= video->brightness->count - ACPI_VIDEO_FIRST_LEVEL)
@@ -382,14 +375,6 @@ static int video_set_bqc_offset(const struct dmi_system_id *d)
return 0;
}
-static int video_disable_backlight_sysfs_if(
- const struct dmi_system_id *d)
-{
- if (disable_backlight_sysfs_if == -1)
- disable_backlight_sysfs_if = 1;
- return 0;
-}
-
static int video_set_device_id_scheme(const struct dmi_system_id *d)
{
device_id_scheme = true;
@@ -463,40 +448,6 @@ static const struct dmi_system_id video_dmi_table[] = {
},
/*
- * Some machines have a broken acpi-video interface for brightness
- * control, but still need an acpi_video_device_lcd_set_level() call
- * on resume to turn the backlight power on. We Enable backlight
- * control on these systems, but do not register a backlight sysfs
- * as brightness control does not work.
- */
- {
- /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
- .callback = video_disable_backlight_sysfs_if,
- .ident = "Toshiba Portege R700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
- DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"),
- },
- },
- {
- /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */
- .callback = video_disable_backlight_sysfs_if,
- .ident = "Toshiba Portege R830",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
- DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"),
- },
- },
- {
- /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
- .callback = video_disable_backlight_sysfs_if,
- .ident = "Toshiba Satellite R830",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
- DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE R830"),
- },
- },
- /*
* Some machine's _DOD IDs don't have bit 31(Device ID Scheme) set
* but the IDs actually follow the Device ID Scheme.
*/
@@ -550,6 +501,15 @@ static const struct dmi_system_id video_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"),
},
},
+ {
+ .callback = video_set_report_key_events,
+ .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS),
+ .ident = "COLORFUL X15 AT 23",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "COLORFUL"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X15 AT 23"),
+ },
+ },
/*
* Some machines change the brightness themselves when a brightness
* hotkey gets pressed, despite us telling them not to. In this case
@@ -651,43 +611,62 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
return 0;
}
+/**
+ * acpi_video_device_EDID() - Get EDID from ACPI _DDC
+ * @device: video output device (LCD, CRT, ..)
+ * @edid: address for returned EDID pointer
+ * @length: _DDC length to request (must be a multiple of 128)
+ *
+ * Get EDID from ACPI _DDC. On success, a pointer to the EDID data is written
+ * to the @edid address, and the length of the EDID is returned. The caller is
+ * responsible for freeing the edid pointer.
+ *
+ * Return the length of EDID (positive value) on success or error (negative
+ * value).
+ */
static int
-acpi_video_device_EDID(struct acpi_video_device *device,
- union acpi_object **edid, ssize_t length)
+acpi_video_device_EDID(struct acpi_video_device *device, void **edid, int length)
{
- int status;
+ acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
-
+ int ret;
*edid = NULL;
if (!device)
return -ENODEV;
- if (length == 128)
- arg0.integer.value = 1;
- else if (length == 256)
- arg0.integer.value = 2;
- else
+ if (!length || (length % 128))
return -EINVAL;
+ arg0.integer.value = length / 128;
+
status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
obj = buffer.pointer;
- if (obj && obj->type == ACPI_TYPE_BUFFER)
- *edid = obj;
- else {
- acpi_handle_info(device->dev->handle, "Invalid _DDC data\n");
- status = -EFAULT;
- kfree(obj);
+ /*
+ * Some buggy implementations incorrectly return the EDID buffer in an ACPI package.
+ * In this case, extract the buffer from the package.
+ */
+ if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 1)
+ obj = &obj->package.elements[0];
+
+ if (obj && obj->type == ACPI_TYPE_BUFFER) {
+ *edid = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
+ ret = *edid ? obj->buffer.length : -ENOMEM;
+ } else {
+ acpi_handle_debug(device->dev->handle,
+ "Invalid _DDC data for length %d\n", length);
+ ret = -EFAULT;
}
- return status;
+ kfree(buffer.pointer);
+ return ret;
}
/* bus */
@@ -1170,9 +1149,8 @@ static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg)
return -ENOMEM;
}
- strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
- device->driver_data = data;
+ strscpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
data->device_id = device_id;
data->video = video;
@@ -1478,9 +1456,7 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
{
struct acpi_video_bus *video;
struct acpi_video_device *video_device;
- union acpi_object *buffer = NULL;
- acpi_status status;
- int i, length;
+ int i, length, ret;
if (!device || !acpi_driver_data(device))
return -EINVAL;
@@ -1489,7 +1465,6 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
for (i = 0; i < video->attached_count; i++) {
video_device = video->attached_array[i].bind_info;
- length = 256;
if (!video_device)
continue;
@@ -1520,21 +1495,11 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
continue;
}
- status = acpi_video_device_EDID(video_device, &buffer, length);
-
- if (ACPI_FAILURE(status) || !buffer ||
- buffer->type != ACPI_TYPE_BUFFER) {
- length = 128;
- status = acpi_video_device_EDID(video_device, &buffer,
- length);
- if (ACPI_FAILURE(status) || !buffer ||
- buffer->type != ACPI_TYPE_BUFFER) {
- continue;
- }
+ for (length = 512; length > 0; length -= 128) {
+ ret = acpi_video_device_EDID(video_device, edid, length);
+ if (ret > 0)
+ return ret;
}
-
- *edid = buffer->buffer.pointer;
- return length;
}
return -ENODEV;
@@ -1573,8 +1538,9 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
acpi_osi_is_win8() ? 0 : 1);
}
-static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
+static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
{
+ struct acpi_device *device = data;
struct acpi_video_bus *video = acpi_driver_data(device);
struct input_dev *input;
int keycode = 0;
@@ -1758,20 +1724,17 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
if (result)
return;
- if (disable_backlight_sysfs_if > 0)
- return;
-
name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
if (!name)
return;
count++;
- acpi_get_parent(device->dev->handle, &acpi_parent);
-
- pdev = acpi_get_pci_dev(acpi_parent);
- if (pdev) {
- parent = &pdev->dev;
- pci_dev_put(pdev);
+ if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) {
+ pdev = acpi_get_pci_dev(acpi_parent);
+ if (pdev) {
+ parent = &pdev->dev;
+ pci_dev_put(pdev);
+ }
}
memset(&props, 0, sizeof(struct backlight_properties));
@@ -1796,8 +1759,8 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
device->backlight->props.brightness =
acpi_video_get_brightness(device->backlight);
- device->cooling_dev = thermal_cooling_device_register("LCD",
- device->dev, &video_cooling_ops);
+ device->cooling_dev = thermal_cooling_device_register("LCD", device,
+ &video_cooling_ops);
if (IS_ERR(device->cooling_dev)) {
/*
* Set cooling_dev to NULL so we don't crash trying to free it.
@@ -1859,8 +1822,6 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
if (video->backlight_registered)
return 0;
- acpi_video_run_bcl_for_osi(video);
-
if (acpi_video_get_backlight_type() != acpi_backlight_video)
return 0;
@@ -1998,8 +1959,10 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
struct acpi_video_device *dev;
mutex_lock(&video->device_list_lock);
- list_for_each_entry(dev, &video->video_device_list, entry)
+ list_for_each_entry(dev, &video->video_device_list, entry) {
acpi_video_dev_remove_notify_handler(dev);
+ cancel_delayed_work_sync(&dev->switch_brightness_work);
+ }
mutex_unlock(&video->device_list_lock);
acpi_video_bus_stop_devices(video);
@@ -2026,11 +1989,12 @@ static int instance;
static int acpi_video_bus_add(struct acpi_device *device)
{
struct acpi_video_bus *video;
+ bool auto_detect;
int error;
acpi_status status;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
- device->parent->handle, 1,
+ acpi_dev_parent(device)->handle, 1,
acpi_video_bus_match, NULL,
device, NULL);
if (status == AE_ALREADY_EXISTS) {
@@ -2061,8 +2025,8 @@ static int acpi_video_bus_add(struct acpi_device *device)
}
video->device = device;
- strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
- strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
+ strscpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
+ strscpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
device->driver_data = video;
acpi_video_bus_find_cap(video);
@@ -2077,20 +2041,54 @@ static int acpi_video_bus_add(struct acpi_device *device)
if (error)
goto err_put_video;
+ /*
+ * HP ZBook Fury 16 G10 requires ACPI video's child devices have _PS0
+ * evaluated to have functional panel brightness control.
+ */
+ acpi_device_fix_up_power_children(device);
+
pr_info("%s [%s] (multi-head: %s rom: %s post: %s)\n",
ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
- video->flags.multihead ? "yes" : "no",
- video->flags.rom ? "yes" : "no",
- video->flags.post ? "yes" : "no");
+ str_yes_no(video->flags.multihead),
+ str_yes_no(video->flags.rom),
+ str_yes_no(video->flags.post));
mutex_lock(&video_list_lock);
list_add_tail(&video->entry, &video_bus_head);
mutex_unlock(&video_list_lock);
- acpi_video_bus_register_backlight(video);
- acpi_video_bus_add_notify_handler(video);
+ /*
+ * If backlight-type auto-detection is used then a native backlight may
+ * show up later and this may change the result from video to native.
+ * Therefor normally the userspace visible /sys/class/backlight device
+ * gets registered separately by the GPU driver calling
+ * acpi_video_register_backlight() when an internal panel is detected.
+ * Register the backlight now when not using auto-detection, so that
+ * when the kernel cmdline or DMI-quirks are used the backlight will
+ * get registered even if acpi_video_register_backlight() is not called.
+ */
+ acpi_video_run_bcl_for_osi(video);
+ if (__acpi_video_get_backlight_type(false, &auto_detect) == acpi_backlight_video &&
+ !auto_detect)
+ acpi_video_bus_register_backlight(video);
+
+ error = acpi_video_bus_add_notify_handler(video);
+ if (error)
+ goto err_del;
+
+ error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_video_bus_notify, device);
+ if (error)
+ goto err_remove;
return 0;
+err_remove:
+ acpi_video_bus_remove_notify_handler(video);
+err_del:
+ mutex_lock(&video_list_lock);
+ list_del(&video->entry);
+ mutex_unlock(&video_list_lock);
+ acpi_video_bus_unregister_backlight(video);
err_put_video:
acpi_video_bus_put_devices(video);
kfree(video->attached_array);
@@ -2101,28 +2099,29 @@ err_free_video:
return error;
}
-static int acpi_video_bus_remove(struct acpi_device *device)
+static void acpi_video_bus_remove(struct acpi_device *device)
{
struct acpi_video_bus *video = NULL;
if (!device || !acpi_driver_data(device))
- return -EINVAL;
+ return;
video = acpi_driver_data(device);
- acpi_video_bus_remove_notify_handler(video);
- acpi_video_bus_unregister_backlight(video);
- acpi_video_bus_put_devices(video);
+ acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_video_bus_notify);
mutex_lock(&video_list_lock);
list_del(&video->entry);
mutex_unlock(&video_list_lock);
+ acpi_video_bus_remove_notify_handler(video);
+ acpi_video_bus_unregister_backlight(video);
+ acpi_video_bus_put_devices(video);
+
kfree(video->attached_array);
kfree(video);
-
- return 0;
}
static int __init is_i740(struct pci_dev *dev)
@@ -2156,57 +2155,6 @@ static int __init intel_opregion_present(void)
return opregion;
}
-/* Check if the chassis-type indicates there is no builtin LCD panel */
-static bool dmi_is_desktop(void)
-{
- const char *chassis_type;
- unsigned long type;
-
- chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
- if (!chassis_type)
- return false;
-
- if (kstrtoul(chassis_type, 10, &type) != 0)
- return false;
-
- switch (type) {
- case 0x03: /* Desktop */
- case 0x04: /* Low Profile Desktop */
- case 0x05: /* Pizza Box */
- case 0x06: /* Mini Tower */
- case 0x07: /* Tower */
- case 0x10: /* Lunch Box */
- case 0x11: /* Main Server Chassis */
- return true;
- }
-
- return false;
-}
-
-/*
- * We're seeing a lot of bogus backlight interfaces on newer machines
- * without a LCD such as desktops, servers and HDMI sticks. Checking the
- * lcd flag fixes this, enable this by default on any machines which are:
- * 1. Win8 ready (where we also prefer the native backlight driver, so
- * normally the acpi_video code should not register there anyways); *and*
- * 2.1 Report a desktop/server DMI chassis-type, or
- * 2.2 Are an ACPI-reduced-hardware platform (and thus won't use the EC for
- backlight control)
- */
-static bool should_check_lcd_flag(void)
-{
- if (!acpi_osi_is_win8())
- return false;
-
- if (dmi_is_desktop())
- return true;
-
- if (acpi_reduced_hardware())
- return true;
-
- return false;
-}
-
int acpi_video_register(void)
{
int ret = 0;
@@ -2220,9 +2168,6 @@ int acpi_video_register(void)
goto leave;
}
- if (only_lcd == -1)
- only_lcd = should_check_lcd_flag();
-
dmi_check_system(video_dmi_table);
ret = acpi_bus_register_driver(&acpi_video_bus);
@@ -2253,19 +2198,16 @@ void acpi_video_unregister(void)
}
EXPORT_SYMBOL(acpi_video_unregister);
-void acpi_video_unregister_backlight(void)
+void acpi_video_register_backlight(void)
{
struct acpi_video_bus *video;
- mutex_lock(&register_count_mutex);
- if (register_count) {
- mutex_lock(&video_list_lock);
- list_for_each_entry(video, &video_bus_head, entry)
- acpi_video_bus_unregister_backlight(video);
- mutex_unlock(&video_list_lock);
- }
- mutex_unlock(&register_count_mutex);
+ mutex_lock(&video_list_lock);
+ list_for_each_entry(video, &video_bus_head, entry)
+ acpi_video_bus_register_backlight(video);
+ mutex_unlock(&video_list_lock);
}
+EXPORT_SYMBOL(acpi_video_register_backlight);
bool acpi_video_handles_brightness_key_presses(void)
{
@@ -2302,7 +2244,6 @@ static int __init acpi_video_init(void)
static void __exit acpi_video_exit(void)
{
- acpi_video_detect_exit();
acpi_video_unregister();
}
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
index ca28183f4d13..14b24157799c 100644
--- a/drivers/acpi/acpi_watchdog.c
+++ b/drivers/acpi/acpi_watchdog.c
@@ -81,7 +81,7 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
return wdat;
}
-/**
+/*
* Returns true if this system should prefer ACPI based watchdog instead of
* the native one (which are typically the same hardware).
*/
@@ -179,7 +179,7 @@ void __init acpi_watchdog_init(void)
pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE,
resources, nresources);
if (IS_ERR(pdev))
- pr_err("Device creation failed: %ld\n", PTR_ERR(pdev));
+ pr_err("Device creation failed: %pe\n", pdev);
kfree(resources);
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index 59700433a96e..8d18af396de9 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -3,8 +3,9 @@
# Makefile for ACPICA Core interpreter
#
-ccflags-y := -Os -D_LINUX -DBUILDING_ACPICA
+ccflags-y := -D_LINUX -DBUILDING_ACPICA
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
+CFLAGS_tbfind.o += $(call cc-disable-warning, stringop-truncation)
# use acpi.o to put all files here into acpi.o modparam namespace
obj-y += acpi.o
@@ -155,6 +156,7 @@ acpi-y += \
utalloc.o \
utascii.o \
utbuffer.o \
+ utcksum.o \
utcopy.o \
utexcep.o \
utdebug.o \
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
index 0a50b4912515..d7d4649ce66f 100644
--- a/drivers/acpi/acpica/acapps.h
+++ b/drivers/acpi/acpica/acapps.h
@@ -3,7 +3,7 @@
*
* Module Name: acapps - common include for ACPI applications/tools
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -17,7 +17,7 @@
/* Common info for tool signons */
#define ACPICA_NAME "Intel ACPI Component Architecture"
-#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2022 Intel Corporation"
+#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2025 Intel Corporation"
#if ACPI_MACHINE_WIDTH == 64
#define ACPI_WIDTH " (64-bit version)"
diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h
index bb329e34ee7d..662231f4f881 100644
--- a/drivers/acpi/acpica/accommon.h
+++ b/drivers/acpi/acpica/accommon.h
@@ -3,7 +3,7 @@
*
* Name: accommon.h - Common include files for generation of ACPICA source
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acconvert.h b/drivers/acpi/acpica/acconvert.h
index 476d21e67767..24998f2d7539 100644
--- a/drivers/acpi/acpica/acconvert.h
+++ b/drivers/acpi/acpica/acconvert.h
@@ -3,7 +3,7 @@
*
* Module Name: acapps - common include for ACPI applications/tools
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index d629716aa5b2..91241bd6917a 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -3,7 +3,7 @@
*
* Name: acdebug.h - ACPI/AML debugger
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -287,4 +287,6 @@ struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name);
void acpi_db_uint32_to_hex_string(u32 value, char *buffer);
+void acpi_db_generate_interrupt(char *gsiv_arg);
+
#endif /* __ACDEBUG_H__ */
diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h
index fe2c3630a38d..5d48a344b35f 100644
--- a/drivers/acpi/acpica/acdispat.h
+++ b/drivers/acpi/acpica/acdispat.h
@@ -3,7 +3,7 @@
*
* Name: acdispat.h - dispatcher (parser to interpreter interface)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 922f559a3e59..b40fb3a5ac8a 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -3,7 +3,7 @@
*
* Name: acevents.h - Event subcomponent prototypes and defines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -188,7 +188,7 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj,
u8 acpi_ns_is_locked);
void
-acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
+acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, u32 max_depth,
acpi_adr_space_type space_id, u32 function);
acpi_status
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 088d6a7d052c..c8a750d2674c 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -3,7 +3,7 @@
*
* Name: acglobal.h - Declarations for global variables
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -24,15 +24,12 @@ ACPI_GLOBAL(struct acpi_table_list, acpi_gbl_root_table_list);
ACPI_GLOBAL(struct acpi_table_header *, acpi_gbl_DSDT);
ACPI_GLOBAL(struct acpi_table_header, acpi_gbl_original_dsdt_header);
+ACPI_INIT_GLOBAL(char *, acpi_gbl_CDAT, NULL);
ACPI_INIT_GLOBAL(u32, acpi_gbl_dsdt_index, ACPI_INVALID_TABLE_INDEX);
ACPI_INIT_GLOBAL(u32, acpi_gbl_facs_index, ACPI_INVALID_TABLE_INDEX);
ACPI_INIT_GLOBAL(u32, acpi_gbl_xfacs_index, ACPI_INVALID_TABLE_INDEX);
ACPI_INIT_GLOBAL(u32, acpi_gbl_fadt_index, ACPI_INVALID_TABLE_INDEX);
-
-#if (!ACPI_REDUCED_HARDWARE)
-ACPI_GLOBAL(struct acpi_table_facs *, acpi_gbl_FACS);
-
-#endif /* !ACPI_REDUCED_HARDWARE */
+ACPI_INIT_GLOBAL(struct acpi_table_facs *, acpi_gbl_FACS, NULL);
/* These addresses are calculated from the FADT Event Block addresses */
@@ -128,6 +125,7 @@ ACPI_GLOBAL(acpi_table_handler, acpi_gbl_table_handler);
ACPI_GLOBAL(void *, acpi_gbl_table_handler_context);
ACPI_GLOBAL(acpi_interface_handler, acpi_gbl_interface_handler);
ACPI_GLOBAL(struct acpi_sci_handler_info *, acpi_gbl_sci_handler_list);
+ACPI_GLOBAL(struct acpi_ged_handler_info *, acpi_gbl_ged_handler_list);
/* Owner ID support */
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
index 6f2787506b50..6aec56c65fa0 100644
--- a/drivers/acpi/acpica/achware.h
+++ b/drivers/acpi/acpica/achware.h
@@ -3,7 +3,7 @@
*
* Name: achware.h -- hardware specific interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -101,12 +101,8 @@ acpi_status
acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
acpi_event_status *event_status);
-acpi_status acpi_hw_disable_all_gpes(void);
-
acpi_status acpi_hw_enable_all_runtime_gpes(void);
-acpi_status acpi_hw_enable_all_wakeup_gpes(void);
-
u8 acpi_hw_check_all_gpes(acpi_handle gpe_skip_device, u32 gpe_skip_number);
acpi_status
diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h
index 6bdf133a2767..1ee6ac9b2baf 100644
--- a/drivers/acpi/acpica/acinterp.h
+++ b/drivers/acpi/acpica/acinterp.h
@@ -3,7 +3,7 @@
*
* Name: acinterp.h - Interpreter subcomponent prototypes and defines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -120,6 +120,9 @@ void
acpi_ex_trace_point(acpi_trace_event_type type,
u8 begin, u8 *aml, char *pathname);
+void
+acpi_ex_trace_args(union acpi_operand_object **params, u32 count);
+
/*
* exfield - ACPI AML (p-code) execution - field manipulation
*/
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 901b1543b869..f98640086f4e 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -3,7 +3,7 @@
*
* Name: aclocal.h - Internal data types used across the ACPI subsystem
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -293,7 +293,7 @@ acpi_status (*acpi_internal_method) (struct acpi_walk_state * walk_state);
* expected_return_btypes - Allowed type(s) for the return value
*/
struct acpi_name_info {
- char name[ACPI_NAMESEG_SIZE];
+ char name[ACPI_NAMESEG_SIZE] ACPI_NONSTRING;
u16 argument_list;
u8 expected_btypes;
};
@@ -370,7 +370,7 @@ typedef acpi_status (*acpi_object_converter) (struct acpi_namespace_node *
converted_object);
struct acpi_simple_repair_info {
- char name[ACPI_NAMESEG_SIZE];
+ char name[ACPI_NAMESEG_SIZE] ACPI_NONSTRING;
u32 unexpected_btypes;
u32 package_index;
acpi_object_converter object_converter;
@@ -543,6 +543,14 @@ struct acpi_field_info {
u32 pkg_length;
};
+/* Information about the interrupt ID and _EVT of a GED device */
+
+struct acpi_ged_handler_info {
+ struct acpi_ged_handler_info *next;
+ u32 int_id; /* The interrupt ID that triggers the execution of the evt_method. */
+ struct acpi_namespace_node *evt_method; /* The _EVT method to be executed when an interrupt with ID = int_ID is received */
+};
+
/*****************************************************************************
*
* Generic "state" object for stacks
@@ -560,25 +568,28 @@ struct acpi_field_info {
u8 descriptor_type; /* To differentiate various internal objs */\
u8 flags; \
u16 value; \
- u16 state;
+ u16 state
/* There are 2 bytes available here until the next natural alignment boundary */
struct acpi_common_state {
-ACPI_STATE_COMMON};
+ ACPI_STATE_COMMON;
+};
/*
* Update state - used to traverse complex objects such as packages
*/
struct acpi_update_state {
- ACPI_STATE_COMMON union acpi_operand_object *object;
+ ACPI_STATE_COMMON;
+ union acpi_operand_object *object;
};
/*
* Pkg state - used to traverse nested package structures
*/
struct acpi_pkg_state {
- ACPI_STATE_COMMON u32 index;
+ ACPI_STATE_COMMON;
+ u32 index;
union acpi_operand_object *source_object;
union acpi_operand_object *dest_object;
struct acpi_walk_state *walk_state;
@@ -591,7 +602,8 @@ struct acpi_pkg_state {
* Allows nesting of these constructs
*/
struct acpi_control_state {
- ACPI_STATE_COMMON u16 opcode;
+ ACPI_STATE_COMMON;
+ u16 opcode;
union acpi_parse_object *predicate_op;
u8 *aml_predicate_start; /* Start of if/while predicate */
u8 *package_end; /* End of if/while block */
@@ -602,11 +614,13 @@ struct acpi_control_state {
* Scope state - current scope during namespace lookups
*/
struct acpi_scope_state {
- ACPI_STATE_COMMON struct acpi_namespace_node *node;
+ ACPI_STATE_COMMON;
+ struct acpi_namespace_node *node;
};
struct acpi_pscope_state {
- ACPI_STATE_COMMON u32 arg_count; /* Number of fixed arguments */
+ ACPI_STATE_COMMON;
+ u32 arg_count; /* Number of fixed arguments */
union acpi_parse_object *op; /* Current op being parsed */
u8 *arg_end; /* Current argument end */
u8 *pkg_end; /* Current package end */
@@ -618,7 +632,8 @@ struct acpi_pscope_state {
* states are created when there are nested control methods executing.
*/
struct acpi_thread_state {
- ACPI_STATE_COMMON u8 current_sync_level; /* Mutex Sync (nested acquire) level */
+ ACPI_STATE_COMMON;
+ u8 current_sync_level; /* Mutex Sync (nested acquire) level */
struct acpi_walk_state *walk_state_list; /* Head of list of walk_states for this thread */
union acpi_operand_object *acquired_mutex_list; /* List of all currently acquired mutexes */
acpi_thread_id thread_id; /* Running thread ID */
@@ -629,8 +644,8 @@ struct acpi_thread_state {
* AML arguments
*/
struct acpi_result_values {
- ACPI_STATE_COMMON
- union acpi_operand_object *obj_desc[ACPI_RESULTS_FRAME_OBJ_NUM];
+ ACPI_STATE_COMMON;
+ union acpi_operand_object *obj_desc[ACPI_RESULTS_FRAME_OBJ_NUM];
};
typedef
@@ -652,7 +667,8 @@ struct acpi_global_notify_handler {
* handler/dispatcher.
*/
struct acpi_notify_info {
- ACPI_STATE_COMMON u8 handler_list_id;
+ ACPI_STATE_COMMON;
+ u8 handler_list_id;
struct acpi_namespace_node *node;
union acpi_operand_object *handler_list_head;
struct acpi_global_notify_handler *global;
@@ -1074,6 +1090,8 @@ struct acpi_port_info {
#define ACPI_ADDRESS_TYPE_IO_RANGE 1
#define ACPI_ADDRESS_TYPE_BUS_NUMBER_RANGE 2
+#define ACPI_ADDRESS_TYPE_PCC_NUMBER 0xA
+
/* Resource descriptor types and masks */
#define ACPI_RESOURCE_NAME_LARGE 0x80
@@ -1122,7 +1140,8 @@ struct acpi_port_info {
#define ACPI_RESOURCE_NAME_PIN_GROUP 0x90
#define ACPI_RESOURCE_NAME_PIN_GROUP_FUNCTION 0x91
#define ACPI_RESOURCE_NAME_PIN_GROUP_CONFIG 0x92
-#define ACPI_RESOURCE_NAME_LARGE_MAX 0x92
+#define ACPI_RESOURCE_NAME_CLOCK_INPUT 0x93
+#define ACPI_RESOURCE_NAME_LARGE_MAX 0x93
/*****************************************************************************
*
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index 2f3e609df47d..4e9402c02410 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -3,7 +3,7 @@
*
* Name: acmacros.h - C macros for the entire subsystem.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 7b27b9cc5916..13f050fecb49 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -3,7 +3,7 @@
*
* Name: acnamesp.h - Namespace subcomponent prototypes and defines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 6af5dc995085..6ffcc7a0a0c2 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -3,7 +3,7 @@
*
* Name: acobject.h - Definition of union acpi_operand_object (Internal object only)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -48,7 +48,7 @@
u8 descriptor_type; /* To differentiate various internal objs */\
u8 type; /* acpi_object_type */\
u16 reference_count; /* For object deletion management */\
- u8 flags;
+ u8 flags
/*
* Note: There are 3 bytes available here before the
* next natural alignment boundary (for both 32/64 cases)
@@ -71,10 +71,12 @@
*****************************************************************************/
struct acpi_object_common {
-ACPI_OBJECT_COMMON_HEADER};
+ ACPI_OBJECT_COMMON_HEADER;
+};
struct acpi_object_integer {
- ACPI_OBJECT_COMMON_HEADER u8 fill[3]; /* Prevent warning on some compilers */
+ ACPI_OBJECT_COMMON_HEADER;
+ u8 fill[3]; /* Prevent warning on some compilers */
u64 value;
};
@@ -86,23 +88,26 @@ struct acpi_object_integer {
*/
#define ACPI_COMMON_BUFFER_INFO(_type) \
_type *pointer; \
- u32 length;
+ u32 length
/* Null terminated, ASCII characters only */
struct acpi_object_string {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(char) /* String in AML stream or allocated string */
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_BUFFER_INFO(char); /* String in AML stream or allocated string */
};
struct acpi_object_buffer {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(u8) /* Buffer in AML stream or allocated buffer */
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_BUFFER_INFO(u8); /* Buffer in AML stream or allocated buffer */
u32 aml_length;
u8 *aml_start;
struct acpi_namespace_node *node; /* Link back to parent node */
};
struct acpi_object_package {
- ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Link back to parent node */
+ ACPI_OBJECT_COMMON_HEADER;
+ struct acpi_namespace_node *node; /* Link back to parent node */
union acpi_operand_object **elements; /* Array of pointers to acpi_objects */
u8 *aml_start;
u32 aml_length;
@@ -116,11 +121,13 @@ struct acpi_object_package {
*****************************************************************************/
struct acpi_object_event {
- ACPI_OBJECT_COMMON_HEADER acpi_semaphore os_semaphore; /* Actual OS synchronization object */
+ ACPI_OBJECT_COMMON_HEADER;
+ acpi_semaphore os_semaphore; /* Actual OS synchronization object */
};
struct acpi_object_mutex {
- ACPI_OBJECT_COMMON_HEADER u8 sync_level; /* 0-15, specified in Mutex() call */
+ ACPI_OBJECT_COMMON_HEADER;
+ u8 sync_level; /* 0-15, specified in Mutex() call */
u16 acquisition_depth; /* Allow multiple Acquires, same thread */
acpi_mutex os_mutex; /* Actual OS synchronization object */
acpi_thread_id thread_id; /* Current owner of the mutex */
@@ -132,7 +139,8 @@ struct acpi_object_mutex {
};
struct acpi_object_region {
- ACPI_OBJECT_COMMON_HEADER u8 space_id;
+ ACPI_OBJECT_COMMON_HEADER;
+ u8 space_id;
struct acpi_namespace_node *node; /* Containing namespace node */
union acpi_operand_object *handler; /* Handler for region access */
union acpi_operand_object *next;
@@ -142,7 +150,8 @@ struct acpi_object_region {
};
struct acpi_object_method {
- ACPI_OBJECT_COMMON_HEADER u8 info_flags;
+ ACPI_OBJECT_COMMON_HEADER;
+ u8 info_flags;
u8 param_count;
u8 sync_level;
union acpi_operand_object *mutex;
@@ -178,33 +187,43 @@ struct acpi_object_method {
*/
#define ACPI_COMMON_NOTIFY_INFO \
union acpi_operand_object *notify_list[2]; /* Handlers for system/device notifies */\
- union acpi_operand_object *handler; /* Handler for Address space */
+ union acpi_operand_object *handler /* Handler for Address space */
/* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */
struct acpi_object_notify_common {
-ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO};
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_NOTIFY_INFO;
+};
struct acpi_object_device {
- ACPI_OBJECT_COMMON_HEADER
- ACPI_COMMON_NOTIFY_INFO struct acpi_gpe_block_info *gpe_block;
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_NOTIFY_INFO;
+ struct acpi_gpe_block_info *gpe_block;
};
struct acpi_object_power_resource {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO u32 system_level;
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_NOTIFY_INFO;
+ u32 system_level;
u32 resource_order;
};
struct acpi_object_processor {
- ACPI_OBJECT_COMMON_HEADER
- /* The next two fields take advantage of the 3-byte space before NOTIFY_INFO */
+ ACPI_OBJECT_COMMON_HEADER;
+
+ /* The next two fields take advantage of the 3-byte space before NOTIFY_INFO */
+
u8 proc_id;
u8 length;
- ACPI_COMMON_NOTIFY_INFO acpi_io_address address;
+ ACPI_COMMON_NOTIFY_INFO;
+ acpi_io_address address;
};
struct acpi_object_thermal_zone {
-ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO};
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_NOTIFY_INFO;
+};
/******************************************************************************
*
@@ -226,17 +245,21 @@ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO};
u32 base_byte_offset; /* Byte offset within containing object */\
u32 value; /* Value to store into the Bank or Index register */\
u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\
- u8 access_length; /* For serial regions/fields */
+ u8 access_length /* For serial regions/fields */
/* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */
struct acpi_object_field_common {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_FIELD_INFO;
+ union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */
};
struct acpi_object_region_field {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length;
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_FIELD_INFO;
+ u16 resource_length;
union acpi_operand_object *region_obj; /* Containing op_region object */
u8 *resource_buffer; /* resource_template for serial regions/fields */
u16 pin_number_index; /* Index relative to previous Connection/Template */
@@ -244,16 +267,20 @@ struct acpi_object_region_field {
};
struct acpi_object_bank_field {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing op_region object */
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_FIELD_INFO;
+ union acpi_operand_object *region_obj; /* Containing op_region object */
union acpi_operand_object *bank_obj; /* bank_select Register object */
};
struct acpi_object_index_field {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO
- /*
- * No "RegionObj" pointer needed since the Index and Data registers
- * are each field definitions unto themselves.
- */
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_FIELD_INFO;
+
+ /*
+ * No "RegionObj" pointer needed since the Index and Data registers
+ * are each field definitions unto themselves.
+ */
union acpi_operand_object *index_obj; /* Index register */
union acpi_operand_object *data_obj; /* Data register */
};
@@ -261,7 +288,9 @@ struct acpi_object_index_field {
/* The buffer_field is different in that it is part of a Buffer, not an op_region */
struct acpi_object_buffer_field {
- ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u8 is_create_field; /* Special case for objects created by create_field() */
+ ACPI_OBJECT_COMMON_HEADER;
+ ACPI_COMMON_FIELD_INFO;
+ u8 is_create_field; /* Special case for objects created by create_field() */
union acpi_operand_object *buffer_obj; /* Containing Buffer object */
};
@@ -272,7 +301,8 @@ struct acpi_object_buffer_field {
*****************************************************************************/
struct acpi_object_notify_handler {
- ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */
+ ACPI_OBJECT_COMMON_HEADER;
+ struct acpi_namespace_node *node; /* Parent device */
u32 handler_type; /* Type: Device/System/Both */
acpi_notify_handler handler; /* Handler address */
void *context;
@@ -280,7 +310,8 @@ struct acpi_object_notify_handler {
};
struct acpi_object_addr_handler {
- ACPI_OBJECT_COMMON_HEADER u8 space_id;
+ ACPI_OBJECT_COMMON_HEADER;
+ u8 space_id;
u8 handler_flags;
acpi_adr_space_handler handler;
struct acpi_namespace_node *node; /* Parent device */
@@ -307,7 +338,8 @@ struct acpi_object_addr_handler {
* The Reference.Class differentiates these types.
*/
struct acpi_object_reference {
- ACPI_OBJECT_COMMON_HEADER u8 class; /* Reference Class */
+ ACPI_OBJECT_COMMON_HEADER;
+ u8 class; /* Reference Class */
u8 target_type; /* Used for Index Op */
u8 resolved; /* Reference has been resolved to a value */
void *object; /* name_op=>HANDLE to obj, index_op=>union acpi_operand_object */
@@ -340,7 +372,8 @@ typedef enum {
* Currently: Region and field_unit types
*/
struct acpi_object_extra {
- ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */
+ ACPI_OBJECT_COMMON_HEADER;
+ struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */
struct acpi_namespace_node *scope_node;
void *region_context; /* Region-specific data */
u8 *aml_start;
@@ -350,14 +383,16 @@ struct acpi_object_extra {
/* Additional data that can be attached to namespace nodes */
struct acpi_object_data {
- ACPI_OBJECT_COMMON_HEADER acpi_object_handler handler;
+ ACPI_OBJECT_COMMON_HEADER;
+ acpi_object_handler handler;
void *pointer;
};
/* Structure used when objects are cached for reuse */
struct acpi_object_cache_list {
- ACPI_OBJECT_COMMON_HEADER union acpi_operand_object *next; /* Link for object cache and internal lists */
+ ACPI_OBJECT_COMMON_HEADER;
+ union acpi_operand_object *next; /* Link for object cache and internal lists */
};
/******************************************************************************
diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h
index a224926bd9c8..a2a9e51d7ac6 100644
--- a/drivers/acpi/acpica/acopcode.h
+++ b/drivers/acpi/acpica/acopcode.h
@@ -3,7 +3,7 @@
*
* Name: acopcode.h - AML opcode information for the AML parser and interpreter
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h
index 4511c2bd8bc3..65a15dee092b 100644
--- a/drivers/acpi/acpica/acparser.h
+++ b/drivers/acpi/acpica/acparser.h
@@ -3,7 +3,7 @@
*
* Module Name: acparser.h - AML Parser subcomponent prototypes and defines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h
index f7d65a20026b..da2c45880cc7 100644
--- a/drivers/acpi/acpica/acpredef.h
+++ b/drivers/acpi/acpica/acpredef.h
@@ -3,7 +3,7 @@
*
* Name: acpredef - Information table for ACPI predefined methods and objects
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -440,6 +440,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
{{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
METHOD_NO_RETURN_VALUE}},
+ {{"_DSC", METHOD_0ARGS,
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
{{"_DSD", METHOD_0ARGS, /* ACPI 6.0 */
METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */
PACKAGE_INFO(ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1,
@@ -447,7 +450,8 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
{{"_DSM",
METHOD_4ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER,
- ACPI_TYPE_PACKAGE),
+ ACPI_TYPE_ANY | ACPI_TYPE_PACKAGE) |
+ ARG_COUNT_IS_MINIMUM,
METHOD_RETURNS(ACPI_RTYPE_ALL)}}, /* Must return a value, but it can be of any type */
{{"_DSS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h
index f7749c63d277..e8a92be5adae 100644
--- a/drivers/acpi/acpica/acresrc.h
+++ b/drivers/acpi/acpica/acresrc.h
@@ -3,7 +3,7 @@
*
* Name: acresrc.h - Resource Manager function prototypes
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -306,6 +306,7 @@ extern struct acpi_rsconvert_info acpi_rs_convert_pin_config[];
extern struct acpi_rsconvert_info acpi_rs_convert_pin_group[];
extern struct acpi_rsconvert_info acpi_rs_convert_pin_group_function[];
extern struct acpi_rsconvert_info acpi_rs_convert_pin_group_config[];
+extern struct acpi_rsconvert_info acpi_rs_convert_clock_input[];
/* These resources require separate get/set tables */
@@ -361,6 +362,7 @@ extern struct acpi_rsdump_info acpi_rs_dump_pin_config[];
extern struct acpi_rsdump_info acpi_rs_dump_pin_group[];
extern struct acpi_rsdump_info acpi_rs_dump_pin_group_function[];
extern struct acpi_rsdump_info acpi_rs_dump_pin_group_config[];
+extern struct acpi_rsdump_info acpi_rs_dump_clock_input[];
#endif
#endif /* __ACRESRC_H__ */
diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h
index b859de96a1e4..e690f604cfa0 100644
--- a/drivers/acpi/acpica/acstruct.h
+++ b/drivers/acpi/acpica/acstruct.h
@@ -3,7 +3,7 @@
*
* Name: acstruct.h - Internal structs
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h
index f8d7bfd737df..ebef72bf58d0 100644
--- a/drivers/acpi/acpica/actables.h
+++ b/drivers/acpi/acpica/actables.h
@@ -3,7 +3,7 @@
*
* Name: actables.h - ACPI table management
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -124,11 +124,6 @@ void
acpi_tb_print_table_header(acpi_physical_address address,
struct acpi_table_header *header);
-u8 acpi_tb_checksum(u8 *buffer, u32 length);
-
-acpi_status
-acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length);
-
void acpi_tb_check_dsdt_header(void);
struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index);
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 6e6270f96bfb..3990d509bbab 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -3,7 +3,7 @@
*
* Name: acutils.h -- prototypes for the common (subsystem-wide) procedures
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -53,6 +53,8 @@ extern const char *acpi_gbl_sb_decode[];
extern const char *acpi_gbl_fc_decode[];
extern const char *acpi_gbl_pt_decode[];
extern const char *acpi_gbl_ptyp_decode[];
+extern const char *acpi_gbl_clock_input_mode[];
+extern const char *acpi_gbl_clock_input_scale[];
#endif
/*
@@ -159,6 +161,19 @@ u8 acpi_ut_valid_name_char(char character, u32 position);
void acpi_ut_check_and_repair_ascii(u8 *name, char *repaired_name, u32 count);
/*
+ * utcksum - Checksum utilities
+ */
+u8 acpi_ut_generate_checksum(void *table, u32 length, u8 original_checksum);
+
+u8 acpi_ut_checksum(u8 *buffer, u32 length);
+
+acpi_status
+acpi_ut_verify_cdat_checksum(struct acpi_table_cdat *cdat_table, u32 length);
+
+acpi_status
+acpi_ut_verify_checksum(struct acpi_table_header *table, u32 length);
+
+/*
* utnonansi - Non-ANSI C library functions
*/
void acpi_ut_strupr(char *src_string);
diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h
index 62a7ec277513..c5b544a006c5 100644
--- a/drivers/acpi/acpica/amlcode.h
+++ b/drivers/acpi/acpica/amlcode.h
@@ -5,7 +5,7 @@
* Declarations and definitions contained herein are derived
* directly from the ACPI specification.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h
index b31779ce204a..54d6e51e0b9a 100644
--- a/drivers/acpi/acpica/amlresrc.h
+++ b/drivers/acpi/acpica/amlresrc.h
@@ -3,7 +3,7 @@
*
* Module Name: amlresrc.h - AML resource descriptors
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -70,6 +70,8 @@
#define ACPI_RESTAG_TYPE "_TTP" /* Translation(1), Static (0) */
#define ACPI_RESTAG_XFERTYPE "_SIZ" /* 8(0), 8And16(1), 16(2) */
#define ACPI_RESTAG_VENDORDATA "_VEN"
+#define ACPI_RESTAG_FQN "_FQN"
+#define ACPI_RESTAG_FQD "_FQD"
/* Default sizes for "small" resource descriptors */
@@ -259,7 +261,10 @@ struct aml_resource_address16 {
struct aml_resource_extended_irq {
AML_RESOURCE_LARGE_HEADER_COMMON u8 flags;
u8 interrupt_count;
- u32 interrupts[1];
+ union {
+ u32 interrupt;
+ ACPI_FLEX_ARRAY(u32, interrupts);
+ };
/* res_source_index, res_source optional fields follow */
};
@@ -427,6 +432,20 @@ struct aml_resource_pin_config {
*/
};
+#define AML_RESOURCE_CLOCK_INPUT_REVISION 1 /* ACPI 6.5 */
+
+struct aml_resource_clock_input {
+ AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id;
+ u16 flags;
+ u16 frequency_divisor;
+ u32 frequency_numerator;
+ /*
+ * Optional fields follow immediately:
+ * 1) Resource Source index
+ * 2) Resource Source String
+ */
+};
+
#define AML_RESOURCE_PIN_CONFIG_REVISION 1 /* ACPI 6.2 */
struct aml_resource_pin_group {
@@ -485,10 +504,6 @@ struct aml_resource_pin_group_config {
#define AML_RESOURCE_PIN_GROUP_CONFIG_REVISION 1 /* ACPI 6.2 */
-/* restore default alignment */
-
-#pragma pack()
-
/* Union of all resource descriptors, so we can allocate the worst case */
union aml_resource {
@@ -533,6 +548,7 @@ union aml_resource {
struct aml_resource_pin_group pin_group;
struct aml_resource_pin_group_function pin_group_function;
struct aml_resource_pin_group_config pin_group_config;
+ struct aml_resource_clock_input clock_input;
/* Utility overlays */
@@ -542,6 +558,10 @@ union aml_resource {
u8 byte_item;
};
+/* restore default alignment */
+
+#pragma pack()
+
/* Interfaces used by both the disassembler and compiler */
void
diff --git a/drivers/acpi/acpica/dbcmds.c b/drivers/acpi/acpica/dbcmds.c
index 9eb68e0751c7..3d99a9048585 100644
--- a/drivers/acpi/acpica/dbcmds.c
+++ b/drivers/acpi/acpica/dbcmds.c
@@ -1010,6 +1010,64 @@ void acpi_db_display_resources(char *object_arg)
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
}
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_generate_ged
+ *
+ * PARAMETERS: ged_arg - Raw GED number, ascii string
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Simulate firing of a GED
+ *
+ ******************************************************************************/
+
+void acpi_db_generate_interrupt(char *gsiv_arg)
+{
+ u32 gsiv_number;
+ struct acpi_ged_handler_info *ged_info = acpi_gbl_ged_handler_list;
+
+ if (!ged_info) {
+ acpi_os_printf("No GED handling present\n");
+ }
+
+ gsiv_number = strtoul(gsiv_arg, NULL, 0);
+
+ while (ged_info) {
+
+ if (ged_info->int_id == gsiv_number) {
+ struct acpi_object_list arg_list;
+ union acpi_object arg0;
+ acpi_handle evt_handle = ged_info->evt_method;
+ acpi_status status;
+
+ acpi_os_printf("Evaluate GED _EVT (GSIV=%d)\n",
+ gsiv_number);
+
+ if (!evt_handle) {
+ acpi_os_printf("Undefined _EVT method\n");
+ return;
+ }
+
+ arg0.integer.type = ACPI_TYPE_INTEGER;
+ arg0.integer.value = gsiv_number;
+
+ arg_list.count = 1;
+ arg_list.pointer = &arg0;
+
+ status =
+ acpi_evaluate_object(evt_handle, NULL, &arg_list,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could not evaluate _EVT\n");
+ return;
+ }
+
+ }
+ ged_info = ged_info->next;
+ }
+}
+
#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c
index 2b84ac093698..8dbab6932049 100644
--- a/drivers/acpi/acpica/dbconvert.c
+++ b/drivers/acpi/acpica/dbconvert.c
@@ -174,6 +174,8 @@ acpi_status acpi_db_convert_to_package(char *string, union acpi_object *object)
elements =
ACPI_ALLOCATE_ZEROED(DB_DEFAULT_PKG_ELEMENTS *
sizeof(union acpi_object));
+ if (!elements)
+ return (AE_NO_MEMORY);
this = string;
for (i = 0; i < (DB_DEFAULT_PKG_ELEMENTS - 1); i++) {
diff --git a/drivers/acpi/acpica/dbhistry.c b/drivers/acpi/acpica/dbhistry.c
index 105e6ceaa887..554ae35108bd 100644
--- a/drivers/acpi/acpica/dbhistry.c
+++ b/drivers/acpi/acpica/dbhistry.c
@@ -3,7 +3,7 @@
*
* Module Name: dbhistry - debugger HISTORY command
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index b8a48923064f..861b12c334ab 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -106,6 +106,7 @@ enum acpi_ex_debugger_commands {
CMD_THREADS,
CMD_TEST,
+ CMD_INTERRUPT,
#endif
};
@@ -185,6 +186,7 @@ static const struct acpi_db_command_info acpi_gbl_db_commands[] = {
{"THREADS", 3},
{"TEST", 1},
+ {"INTERRUPT", 1},
#endif
{NULL, 0}
};
@@ -318,6 +320,7 @@ static const struct acpi_db_command_help acpi_gbl_db_command_help[] = {
{1, " Gpes", "Display info on all GPE devices\n"},
{1, " Sci", "Generate an SCI\n"},
{1, " Sleep [SleepState]", "Simulate sleep/wake sequence(s) (0-5)\n"},
+ {1, " Interrupt <GSIV>", "Simulate an interrupt\n"},
#endif
{0, NULL, NULL}
};
@@ -1064,6 +1067,11 @@ acpi_db_command_dispatch(char *input_buffer,
acpi_os_printf("Event command not implemented\n");
break;
+ case CMD_INTERRUPT:
+
+ acpi_db_generate_interrupt(acpi_gbl_db_args[1]);
+ break;
+
case CMD_GPE:
acpi_db_generate_gpe(acpi_gbl_db_args[1], acpi_gbl_db_args[2]);
diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c
index 3615e1a6efd8..c9131259f717 100644
--- a/drivers/acpi/acpica/dbnames.c
+++ b/drivers/acpi/acpica/dbnames.c
@@ -550,8 +550,12 @@ acpi_db_walk_for_fields(acpi_handle obj_handle,
ACPI_FREE(buffer.pointer);
buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
- acpi_evaluate_object(obj_handle, NULL, NULL, &buffer);
-
+ status = acpi_evaluate_object(obj_handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("Could Not evaluate object %p\n",
+ obj_handle);
+ return (AE_OK);
+ }
/*
* Since this is a field unit, surround the output in braces
*/
@@ -652,6 +656,9 @@ acpi_status acpi_db_display_objects(char *obj_type_arg, char *display_count_arg)
object_info =
ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_object_info));
+ if (!object_info)
+ return (AE_NO_MEMORY);
+
/* Walk the namespace from the root */
(void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c
index 2963d1579c05..e2f00c54cb36 100644
--- a/drivers/acpi/acpica/dsargs.c
+++ b/drivers/acpi/acpica/dsargs.c
@@ -4,7 +4,7 @@
* Module Name: dsargs - Support for execution of dynamic arguments for static
* objects (regions, fields, buffer fields, etc.)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
index 8492619149d1..c1f79d7a2026 100644
--- a/drivers/acpi/acpica/dscontrol.c
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -4,7 +4,7 @@
* Module Name: dscontrol - Support for execution control opcodes -
* if/else/while/return
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dsdebug.c b/drivers/acpi/acpica/dsdebug.c
index 2d99ccf5bde7..274b74255551 100644
--- a/drivers/acpi/acpica/dsdebug.c
+++ b/drivers/acpi/acpica/dsdebug.c
@@ -3,7 +3,7 @@
*
* Module Name: dsdebug - Parser/Interpreter interface - debugging
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c
index de175f1b4beb..df132c9089c7 100644
--- a/drivers/acpi/acpica/dsfield.c
+++ b/drivers/acpi/acpica/dsfield.c
@@ -3,7 +3,7 @@
*
* Module Name: dsfield - Dispatcher field routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c
index dffd54fdbd51..57cd9e2d1109 100644
--- a/drivers/acpi/acpica/dsinit.c
+++ b/drivers/acpi/acpica/dsinit.c
@@ -3,7 +3,7 @@
*
* Module Name: dsinit - Object initialization namespace walk
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index ae2e768830bf..45ec32e81903 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -3,7 +3,7 @@
*
* Module Name: dsmethod - Parser/Interpreter interface - control method parsing
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -462,7 +462,6 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
struct acpi_walk_state *next_walk_state = NULL;
union acpi_operand_object *obj_desc;
struct acpi_evaluate_info *info;
- u32 i;
ACPI_FUNCTION_TRACE_PTR(ds_call_control_method, this_walk_state);
@@ -483,6 +482,20 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
return_ACPI_STATUS(AE_NULL_OBJECT);
}
+ if (this_walk_state->num_operands < obj_desc->method.param_count) {
+ ACPI_ERROR((AE_INFO, "Missing argument(s) for method [%4.4s]",
+ acpi_ut_get_node_name(method_node)));
+
+ return_ACPI_STATUS(AE_AML_TOO_FEW_ARGUMENTS);
+ }
+
+ else if (this_walk_state->num_operands > obj_desc->method.param_count) {
+ ACPI_ERROR((AE_INFO, "Too many arguments for method [%4.4s]",
+ acpi_ut_get_node_name(method_node)));
+
+ return_ACPI_STATUS(AE_AML_TOO_MANY_ARGUMENTS);
+ }
+
/* Init for new method, possibly wait on method mutex */
status =
@@ -517,7 +530,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
if (!info) {
status = AE_NO_MEMORY;
- goto cleanup;
+ goto pop_walk_state;
}
info->parameters = &this_walk_state->operands[0];
@@ -529,7 +542,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
ACPI_FREE(info);
if (ACPI_FAILURE(status)) {
- goto cleanup;
+ goto pop_walk_state;
}
next_walk_state->method_nesting_depth =
@@ -539,14 +552,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
* Delete the operands on the previous walkstate operand stack
* (they were copied to new objects)
*/
- for (i = 0; i < obj_desc->method.param_count; i++) {
- acpi_ut_remove_reference(this_walk_state->operands[i]);
- this_walk_state->operands[i] = NULL;
- }
-
- /* Clear the operand stack */
-
- this_walk_state->num_operands = 0;
+ acpi_ds_clear_operands(this_walk_state);
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"**** Begin nested execution of [%4.4s] **** WalkState=%p\n",
@@ -575,6 +581,12 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
return_ACPI_STATUS(status);
+pop_walk_state:
+
+ /* On error, pop the walk state to be deleted from thread */
+
+ acpi_ds_pop_walk_state(thread);
+
cleanup:
/* On error, we must terminate the method properly */
diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c
index eca50517ad82..5393de4dbc4c 100644
--- a/drivers/acpi/acpica/dsmthdat.c
+++ b/drivers/acpi/acpica/dsmthdat.c
@@ -188,6 +188,7 @@ acpi_ds_method_data_init_args(union acpi_operand_object **params,
index++;
}
+ acpi_ex_trace_args(params, index);
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%u args passed to method\n", index));
return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c
index e3dfc734ace9..1bf7eec49899 100644
--- a/drivers/acpi/acpica/dsobject.c
+++ b/drivers/acpi/acpica/dsobject.c
@@ -3,7 +3,7 @@
*
* Module Name: dsobject - Dispatcher object management routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 2b9b6a974ca9..5699b0872848 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -3,7 +3,7 @@
*
* Module Name: dsopcode - Dispatcher support for regions and fields
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dspkginit.c b/drivers/acpi/acpica/dspkginit.c
index 1624d6e7dc46..1ed2386fab82 100644
--- a/drivers/acpi/acpica/dspkginit.c
+++ b/drivers/acpi/acpica/dspkginit.c
@@ -3,7 +3,7 @@
*
* Module Name: dspkginit - Completion of deferred package initialization
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index fb9ed5e1da89..baf6a1f27605 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -668,6 +668,8 @@ acpi_ds_create_operands(struct acpi_walk_state *walk_state,
union acpi_parse_object *arguments[ACPI_OBJ_NUM_OPERANDS];
u32 arg_count = 0;
u32 index = walk_state->num_operands;
+ u32 prev_num_operands = walk_state->num_operands;
+ u32 new_num_operands;
u32 i;
ACPI_FUNCTION_TRACE_PTR(ds_create_operands, first_arg);
@@ -696,6 +698,7 @@ acpi_ds_create_operands(struct acpi_walk_state *walk_state,
/* Create the interpreter arguments, in reverse order */
+ new_num_operands = index;
index--;
for (i = 0; i < arg_count; i++) {
arg = arguments[index];
@@ -720,7 +723,11 @@ cleanup:
* pop everything off of the operand stack and delete those
* objects
*/
- acpi_ds_obj_stack_pop_and_delete(arg_count, walk_state);
+ walk_state->num_operands = (u8)(i);
+ acpi_ds_obj_stack_pop_and_delete(new_num_operands, walk_state);
+
+ /* Restore operand count */
+ walk_state->num_operands = (u8)(prev_num_operands);
ACPI_EXCEPTION((AE_INFO, status, "While creating Arg %u", index));
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index e8ad41387f84..5c5c6d8a4e48 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -4,7 +4,7 @@
* Module Name: dswexec - Dispatcher method execution callbacks;
* dispatch to interpreter.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -389,9 +389,11 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
/*
* All opcodes require operand resolution, with the only exceptions
- * being the object_type and size_of operators.
+ * being the object_type and size_of operators as well as opcodes that
+ * take no arguments.
*/
- if (!(walk_state->op_info->flags & AML_NO_OPERAND_RESOLVE)) {
+ if (!(walk_state->op_info->flags & AML_NO_OPERAND_RESOLVE) &&
+ (walk_state->op_info->flags & AML_HAS_ARGS)) {
/* Resolve all operands */
diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c
index 9f6573646ab5..666419b6a5c6 100644
--- a/drivers/acpi/acpica/dswload.c
+++ b/drivers/acpi/acpica/dswload.c
@@ -3,7 +3,7 @@
*
* Module Name: dswload - Dispatcher first pass namespace load callbacks
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c
index 778df616aaa0..bfc54c914757 100644
--- a/drivers/acpi/acpica/dswload2.c
+++ b/drivers/acpi/acpica/dswload2.c
@@ -3,7 +3,7 @@
*
* Module Name: dswload2 - Dispatcher second pass namespace load callbacks
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c
index 634b9100f674..375a8fa43d9d 100644
--- a/drivers/acpi/acpica/dswscope.c
+++ b/drivers/acpi/acpica/dswscope.c
@@ -3,7 +3,7 @@
*
* Module Name: dswscope - Scope stack manipulation
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c
index 0aa735d3b93c..02aaddb89df9 100644
--- a/drivers/acpi/acpica/dswstate.c
+++ b/drivers/acpi/acpica/dswstate.c
@@ -3,7 +3,7 @@
*
* Module Name: dswstate - Dispatcher parse tree walk management routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -146,8 +146,8 @@ acpi_ds_result_push(union acpi_operand_object *object,
if (!object) {
ACPI_ERROR((AE_INFO,
- "Null Object! Obj=%p State=%p Num=%u",
- object, walk_state, walk_state->result_count));
+ "Null Object! State=%p Num=%u",
+ walk_state, walk_state->result_count));
return (AE_BAD_PARAMETER);
}
@@ -576,9 +576,14 @@ acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state,
ACPI_FUNCTION_TRACE(ds_init_aml_walk);
walk_state->parser_state.aml =
- walk_state->parser_state.aml_start = aml_start;
- walk_state->parser_state.aml_end =
- walk_state->parser_state.pkg_end = aml_start + aml_length;
+ walk_state->parser_state.aml_start =
+ walk_state->parser_state.aml_end =
+ walk_state->parser_state.pkg_end = aml_start;
+ /* Avoid undefined behavior: applying zero offset to null pointer */
+ if (aml_length != 0) {
+ walk_state->parser_state.aml_end += aml_length;
+ walk_state->parser_state.pkg_end += aml_length;
+ }
/* The next_op of the next_walk will be the beginning of the method */
diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c
index df596d46dd97..6cdd39c987b8 100644
--- a/drivers/acpi/acpica/evevent.c
+++ b/drivers/acpi/acpica/evevent.c
@@ -3,7 +3,7 @@
*
* Module Name: evevent - Fixed Event handling and dispatch
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c
index 9aab54797ded..df2a4ab0e0da 100644
--- a/drivers/acpi/acpica/evglock.c
+++ b/drivers/acpi/acpica/evglock.c
@@ -3,7 +3,7 @@
*
* Module Name: evglock - Global Lock support
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -42,6 +42,10 @@ acpi_status acpi_ev_init_global_lock_handler(void)
return_ACPI_STATUS(AE_OK);
}
+ if (!acpi_gbl_use_global_lock) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
/* Attempt installation of the global lock handler */
status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL,
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index a6bb480d631c..ba65b2ea49b2 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -3,7 +3,7 @@
*
* Module Name: evgpe - General Purpose Event handling and dispatch
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index 39fe4566310b..fadd93caf1d5 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -3,7 +3,7 @@
*
* Module Name: evgpeblk - GPE block creation and initialization.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c
index 2f1a75fee61c..eb769739420e 100644
--- a/drivers/acpi/acpica/evgpeinit.c
+++ b/drivers/acpi/acpica/evgpeinit.c
@@ -3,7 +3,7 @@
*
* Module Name: evgpeinit - System GPE initialization and update
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -413,6 +413,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
gpe_event_info->flags &= ~(ACPI_GPE_DISPATCH_MASK);
gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD);
gpe_event_info->dispatch.method_node = method_node;
+ walk_info->count++;
ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
"Registered GPE method %s as GPE number 0x%.2X\n",
diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c
index c32eb57aa21d..d15b1d75c8ec 100644
--- a/drivers/acpi/acpica/evgpeutil.c
+++ b/drivers/acpi/acpica/evgpeutil.c
@@ -3,7 +3,7 @@
*
* Module Name: evgpeutil - GPE utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
index be9a05498adc..5a35dae945e2 100644
--- a/drivers/acpi/acpica/evhandler.c
+++ b/drivers/acpi/acpica/evhandler.c
@@ -3,7 +3,7 @@
*
* Module Name: evhandler - Support for Address Space handlers
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index 6172cddc1b39..04a23a6c3bb1 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -3,7 +3,7 @@
*
* Module Name: evmisc - Miscellaneous event manager support functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index b96b3a7e78e5..fa3475da7ea9 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -3,7 +3,7 @@
*
* Module Name: evregion - Operation Region support
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -65,6 +65,7 @@ acpi_status acpi_ev_initialize_op_regions(void)
acpi_gbl_default_address_spaces
[i])) {
acpi_ev_execute_reg_methods(acpi_gbl_root_node,
+ ACPI_UINT32_MAX,
acpi_gbl_default_address_spaces
[i], ACPI_REG_CONNECT);
}
@@ -172,6 +173,15 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
ctx->subspace_id = (u8)region_obj->region.address;
}
+ if (region_obj->region.space_id ==
+ ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ struct acpi_ffh_info *ctx =
+ handler_desc->address_space.context;
+
+ ctx->length = region_obj->region.length;
+ ctx->offset = region_obj->region.address;
+ }
+
/*
* We must exit the interpreter because the region setup will
* potentially execute control methods (for example, the _REG method
@@ -663,6 +673,7 @@ cleanup1:
* FUNCTION: acpi_ev_execute_reg_methods
*
* PARAMETERS: node - Namespace node for the device
+ * max_depth - Depth to which search for _REG
* space_id - The address space ID
* function - Passed to _REG: On (1) or Off (0)
*
@@ -674,7 +685,7 @@ cleanup1:
******************************************************************************/
void
-acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
+acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, u32 max_depth,
acpi_adr_space_type space_id, u32 function)
{
struct acpi_reg_walk_info info;
@@ -708,7 +719,7 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
* regions and _REG methods. (i.e. handlers must be installed for all
* regions of this Space ID before we can run any _REG methods)
*/
- (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
+ (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, max_depth,
ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, NULL,
&info, NULL);
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index ca4ba6b351fe..b03952798af5 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -3,7 +3,7 @@
*
* Module Name: evrgnini- ACPI address_space (op_region) init
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -430,7 +430,7 @@ acpi_ev_data_table_region_setup(acpi_handle handle,
{
union acpi_operand_object *region_desc =
(union acpi_operand_object *)handle;
- struct acpi_data_table_space_context *local_region_context;
+ struct acpi_data_table_mapping *local_region_context;
ACPI_FUNCTION_TRACE(ev_data_table_region_setup);
@@ -445,7 +445,7 @@ acpi_ev_data_table_region_setup(acpi_handle handle,
/* Create a new context */
local_region_context =
- ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_data_table_space_context));
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_data_table_mapping));
if (!(local_region_context)) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 18219abba108..86a8d41c079c 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -3,7 +3,7 @@
*
* Module Name: evxface - External interfaces for ACPI events
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 8187b081e0a6..4b052908d2e7 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -3,7 +3,7 @@
*
* Module Name: evxfevnt - External Interfaces, ACPI event disable/enable
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
index 340947e412bb..60dacec1b121 100644
--- a/drivers/acpi/acpica/evxfgpe.c
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -3,7 +3,7 @@
*
* Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 0a8372bf6a77..bccc672c934c 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -4,7 +4,7 @@
* Module Name: evxfregn - External Interfaces, ACPI Operation Regions and
* Address Spaces.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -20,13 +20,14 @@ ACPI_MODULE_NAME("evxfregn")
/*******************************************************************************
*
- * FUNCTION: acpi_install_address_space_handler
+ * FUNCTION: acpi_install_address_space_handler_internal
*
* PARAMETERS: device - Handle for the device
* space_id - The address space ID
* handler - Address of the handler
* setup - Address of the setup function
* context - Value passed to the handler on each access
+ * Run_reg - Run _REG methods for this address space?
*
* RETURN: Status
*
@@ -37,13 +38,16 @@ ACPI_MODULE_NAME("evxfregn")
* are executed here, and these methods can only be safely executed after
* the default handlers have been installed and the hardware has been
* initialized (via acpi_enable_subsystem.)
+ * To avoid this problem pass FALSE for Run_Reg and later on call
+ * acpi_execute_reg_methods() to execute _REG.
*
******************************************************************************/
-acpi_status
-acpi_install_address_space_handler(acpi_handle device,
- acpi_adr_space_type space_id,
- acpi_adr_space_handler handler,
- acpi_adr_space_setup setup, void *context)
+static acpi_status
+acpi_install_address_space_handler_internal(acpi_handle device,
+ acpi_adr_space_type space_id,
+ acpi_adr_space_handler handler,
+ acpi_adr_space_setup setup,
+ void *context, u8 run_reg)
{
struct acpi_namespace_node *node;
acpi_status status;
@@ -80,14 +84,41 @@ acpi_install_address_space_handler(acpi_handle device,
/* Run all _REG methods for this address space */
- acpi_ev_execute_reg_methods(node, space_id, ACPI_REG_CONNECT);
+ if (run_reg) {
+ acpi_ev_execute_reg_methods(node, ACPI_UINT32_MAX, space_id,
+ ACPI_REG_CONNECT);
+ }
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(status);
}
+acpi_status
+acpi_install_address_space_handler(acpi_handle device,
+ acpi_adr_space_type space_id,
+ acpi_adr_space_handler handler,
+ acpi_adr_space_setup setup, void *context)
+{
+ return acpi_install_address_space_handler_internal(device, space_id,
+ handler, setup,
+ context, TRUE);
+}
+
ACPI_EXPORT_SYMBOL(acpi_install_address_space_handler)
+acpi_status
+acpi_install_address_space_handler_no_reg(acpi_handle device,
+ acpi_adr_space_type space_id,
+ acpi_adr_space_handler handler,
+ acpi_adr_space_setup setup,
+ void *context)
+{
+ return acpi_install_address_space_handler_internal(device, space_id,
+ handler, setup,
+ context, FALSE);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_address_space_handler_no_reg)
/*******************************************************************************
*
@@ -201,8 +232,6 @@ acpi_remove_address_space_handler(acpi_handle device,
/* Now we can delete the handler object */
- acpi_os_release_mutex(handler_obj->address_space.
- context_mutex);
acpi_ut_remove_reference(handler_obj);
goto unlock_and_exit;
}
@@ -228,3 +257,54 @@ unlock_and_exit:
}
ACPI_EXPORT_SYMBOL(acpi_remove_address_space_handler)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_execute_reg_methods
+ *
+ * PARAMETERS: device - Handle for the device
+ * max_depth - Depth to which search for _REG
+ * space_id - The address space ID
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute _REG for all op_regions of a given space_id.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_execute_reg_methods(acpi_handle device, u32 max_depth,
+ acpi_adr_space_type space_id)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_execute_reg_methods);
+
+ /* Parameter validation */
+
+ if (!device) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Convert and validate the device handle */
+
+ node = acpi_ns_validate_handle(device);
+ if (node) {
+
+ /* Run all _REG methods for this address space */
+
+ acpi_ev_execute_reg_methods(node, max_depth, space_id,
+ ACPI_REG_CONNECT);
+ } else {
+ status = AE_BAD_PARAMETER;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_execute_reg_methods)
diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c
index 66201742f499..c248c9b162fa 100644
--- a/drivers/acpi/acpica/exconcat.c
+++ b/drivers/acpi/acpica/exconcat.c
@@ -3,7 +3,7 @@
*
* Module Name: exconcat - Concatenate-type AML operators
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
index d7d74ef87b18..4d7dd0fc6b07 100644
--- a/drivers/acpi/acpica/exconfig.c
+++ b/drivers/acpi/acpica/exconfig.c
@@ -3,7 +3,7 @@
*
* Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -295,8 +295,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
target));
}
if (target->common.type != ACPI_TYPE_INTEGER) {
- ACPI_EXCEPTION((AE_INFO, AE_TYPE,
- "Type not integer: %X\n", target->common.type));
+ ACPI_ERROR((AE_INFO, "Type not integer: %X",
+ target->common.type));
return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
}
diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c
index 8de5d47ad485..fded9bfc2436 100644
--- a/drivers/acpi/acpica/exconvrt.c
+++ b/drivers/acpi/acpica/exconvrt.c
@@ -3,7 +3,7 @@
*
* Module Name: exconvrt - Object conversion routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -17,7 +17,8 @@ ACPI_MODULE_NAME("exconvrt")
/* Local prototypes */
static u32
-acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 max_length);
+acpi_ex_convert_to_ascii(u64 integer,
+ u16 base, u8 *string, u8 max_length, u8 leading_zeros);
/*******************************************************************************
*
@@ -225,8 +226,8 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
/* Copy the string to the buffer */
new_buf = return_desc->buffer.pointer;
- strncpy((char *)new_buf, (char *)obj_desc->string.pointer,
- obj_desc->string.length);
+ memcpy((char *)new_buf, (char *)obj_desc->string.pointer,
+ obj_desc->string.length);
break;
default:
@@ -249,6 +250,7 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
* base - ACPI_STRING_DECIMAL or ACPI_STRING_HEX
* string - Where the string is returned
* data_width - Size of data item to be converted, in bytes
+ * leading_zeros - Allow leading zeros
*
* RETURN: Actual string length
*
@@ -257,7 +259,8 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
******************************************************************************/
static u32
-acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width)
+acpi_ex_convert_to_ascii(u64 integer,
+ u16 base, u8 *string, u8 data_width, u8 leading_zeros)
{
u64 digit;
u32 i;
@@ -266,7 +269,8 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width)
u32 hex_length;
u32 decimal_length;
u32 remainder;
- u8 supress_zeros;
+ u8 supress_zeros = !leading_zeros;
+ u8 hex_char;
ACPI_FUNCTION_ENTRY();
@@ -293,7 +297,6 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width)
break;
}
- supress_zeros = TRUE; /* No leading zeros */
remainder = 0;
for (i = decimal_length; i > 0; i--) {
@@ -328,8 +331,17 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width)
/* Get one hex digit, most significant digits first */
- string[k] = (u8)
+ hex_char = (u8)
acpi_ut_hex_to_ascii_char(integer, ACPI_MUL_4(j));
+
+ /* Supress leading zeros until the first non-zero character */
+
+ if (hex_char == ACPI_ASCII_ZERO && supress_zeros) {
+ continue;
+ }
+
+ supress_zeros = FALSE;
+ string[k] = hex_char;
k++;
}
break;
@@ -379,6 +391,7 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
u32 string_length = 0;
u16 base = 16;
u8 separator = ',';
+ u8 leading_zeros;
ACPI_FUNCTION_TRACE_PTR(ex_convert_to_string, obj_desc);
@@ -400,14 +413,26 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
* Make room for the maximum decimal number size
*/
string_length = ACPI_MAX_DECIMAL_DIGITS;
+ leading_zeros = FALSE;
base = 10;
break;
+ case ACPI_EXPLICIT_CONVERT_HEX:
+ /*
+ * From to_hex_string.
+ *
+ * Supress leading zeros and append "0x"
+ */
+ string_length =
+ ACPI_MUL_2(acpi_gbl_integer_byte_width) + 2;
+ leading_zeros = FALSE;
+ break;
default:
/* Two hex string characters for each integer byte */
string_length = ACPI_MUL_2(acpi_gbl_integer_byte_width);
+ leading_zeros = TRUE;
break;
}
@@ -422,17 +447,32 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
}
new_buf = return_desc->buffer.pointer;
+ if (type == ACPI_EXPLICIT_CONVERT_HEX) {
+
+ /* Append "0x" prefix for explicit hex conversion */
+
+ *new_buf++ = '0';
+ *new_buf++ = 'x';
+ }
/* Convert integer to string */
string_length =
acpi_ex_convert_to_ascii(obj_desc->integer.value, base,
new_buf,
- acpi_gbl_integer_byte_width);
+ acpi_gbl_integer_byte_width,
+ leading_zeros);
/* Null terminate at the correct place */
return_desc->string.length = string_length;
+ if (type == ACPI_EXPLICIT_CONVERT_HEX) {
+
+ /* Take "0x" prefix into account */
+
+ return_desc->string.length += 2;
+ }
+
new_buf[string_length] = 0;
break;
@@ -448,6 +488,7 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
* From ACPI: "If the input is a buffer, it is converted to a
* a string of decimal values separated by commas."
*/
+ leading_zeros = FALSE;
base = 10;
/*
@@ -475,6 +516,7 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
*
* Each hex number is prefixed with 0x (11/2018)
*/
+ leading_zeros = TRUE;
separator = ' ';
string_length = (obj_desc->buffer.length * 5);
break;
@@ -488,6 +530,7 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
*
* Each hex number is prefixed with 0x (11/2018)
*/
+ leading_zeros = TRUE;
separator = ',';
string_length = (obj_desc->buffer.length * 5);
break;
@@ -528,7 +571,8 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc,
new_buf += acpi_ex_convert_to_ascii((u64) obj_desc->
buffer.pointer[i],
- base, new_buf, 1);
+ base, new_buf, 1,
+ leading_zeros);
/* Each digit is separated by either a comma or space */
diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c
index fb2453fa9442..052c69567997 100644
--- a/drivers/acpi/acpica/excreate.c
+++ b/drivers/acpi/acpica/excreate.c
@@ -3,7 +3,7 @@
*
* Module Name: excreate - Named object creation
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c
index 8a99aadb9d15..81a07a52b73c 100644
--- a/drivers/acpi/acpica/exdebug.c
+++ b/drivers/acpi/acpica/exdebug.c
@@ -3,7 +3,7 @@
*
* Module Name: exdebug - Support for stores to the AML Debug Object
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c
index 24b3d041b3e5..d8aeebaab70a 100644
--- a/drivers/acpi/acpica/exdump.c
+++ b/drivers/acpi/acpica/exdump.c
@@ -3,7 +3,7 @@
*
* Module Name: exdump - Interpreter debug output routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
index 2b89a496de65..ced3ff9d0a86 100644
--- a/drivers/acpi/acpica/exfield.c
+++ b/drivers/acpi/acpica/exfield.c
@@ -3,7 +3,7 @@
*
* Module Name: exfield - AML execution - field_unit read/write
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -141,7 +141,9 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
|| obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_IPMI
|| obj_desc->field.region_obj->region.space_id ==
- ACPI_ADR_SPACE_PLATFORM_RT)) {
+ ACPI_ADR_SPACE_PLATFORM_RT
+ || obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_FIXED_HARDWARE)) {
/* SMBus, GSBus, IPMI serial */
@@ -305,7 +307,9 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
|| obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_IPMI
|| obj_desc->field.region_obj->region.space_id ==
- ACPI_ADR_SPACE_PLATFORM_RT)) {
+ ACPI_ADR_SPACE_PLATFORM_RT
+ || obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_FIXED_HARDWARE)) {
/* SMBus, GSBus, IPMI serial */
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index d769cea1468b..0771934c0455 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -3,7 +3,7 @@
*
* Module Name: exfldio - Aml Field I/O
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c
index b4bac8c60a13..07cbac58ed21 100644
--- a/drivers/acpi/acpica/exmisc.c
+++ b/drivers/acpi/acpica/exmisc.c
@@ -3,7 +3,7 @@
*
* Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index e9dcfa1e93eb..1fa013197fcf 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/drivers/acpi/acpica/exmutex.c
@@ -3,7 +3,7 @@
*
* Module Name: exmutex - ASL Mutex Acquire/Release functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c
index 318eb769058d..76ab73c37e90 100644
--- a/drivers/acpi/acpica/exnames.c
+++ b/drivers/acpi/acpica/exnames.c
@@ -3,7 +3,7 @@
*
* Module Name: exnames - interpreter/scanner name load/execute
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c
index d108a1a86f12..6ac7e0ca5c9d 100644
--- a/drivers/acpi/acpica/exoparg1.c
+++ b/drivers/acpi/acpica/exoparg1.c
@@ -3,7 +3,7 @@
*
* Module Name: exoparg1 - AML execution - opcodes with 1 argument
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c
index ebf7c89d52d9..a94fa4d70e99 100644
--- a/drivers/acpi/acpica/exoparg2.c
+++ b/drivers/acpi/acpica/exoparg2.c
@@ -3,7 +3,7 @@
*
* Module Name: exoparg2 - AML execution - opcodes with 2 arguments
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c
index 4b069bd6bc71..bf08110ed6d2 100644
--- a/drivers/acpi/acpica/exoparg3.c
+++ b/drivers/acpi/acpica/exoparg3.c
@@ -3,7 +3,7 @@
*
* Module Name: exoparg3 - AML execution - opcodes with 3 arguments
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c
index 2a506ef386cf..cb078e39abf7 100644
--- a/drivers/acpi/acpica/exoparg6.c
+++ b/drivers/acpi/acpica/exoparg6.c
@@ -3,7 +3,7 @@
*
* Module Name: exoparg6 - AML execution - opcodes with 6 arguments
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c
index 08f06797386a..1b1a006e82de 100644
--- a/drivers/acpi/acpica/exprep.c
+++ b/drivers/acpi/acpica/exprep.c
@@ -3,7 +3,7 @@
*
* Module Name: exprep - ACPI AML field prep utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -437,6 +437,9 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
if (info->connection_node) {
second_desc = info->connection_node->object;
+ if (second_desc == NULL) {
+ break;
+ }
if (!(second_desc->common.flags & AOPOBJ_DATA_VALID)) {
status =
acpi_ds_get_buffer_arguments(second_desc);
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
index 4ff35852c0b3..a390a1c2b0ab 100644
--- a/drivers/acpi/acpica/exregion.c
+++ b/drivers/acpi/acpica/exregion.c
@@ -3,7 +3,7 @@
*
* Module Name: exregion - ACPI default op_region (address space) handlers
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -44,7 +44,6 @@ acpi_ex_system_memory_space_handler(u32 function,
struct acpi_mem_mapping *mm = mem_info->cur_mm;
u32 length;
acpi_size map_length;
- acpi_size page_boundary_map_length;
#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED
u32 remainder;
#endif
@@ -138,26 +137,8 @@ acpi_ex_system_memory_space_handler(u32 function,
map_length = (acpi_size)
((mem_info->address + mem_info->length) - address);
- /*
- * If mapping the entire remaining portion of the region will cross
- * a page boundary, just map up to the page boundary, do not cross.
- * On some systems, crossing a page boundary while mapping regions
- * can cause warnings if the pages have different attributes
- * due to resource management.
- *
- * This has the added benefit of constraining a single mapping to
- * one page, which is similar to the original code that used a 4k
- * maximum window.
- */
- page_boundary_map_length = (acpi_size)
- (ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address);
- if (page_boundary_map_length == 0) {
- page_boundary_map_length = ACPI_DEFAULT_PAGE_SIZE;
- }
-
- if (map_length > page_boundary_map_length) {
- map_length = page_boundary_map_length;
- }
+ if (map_length > ACPI_DEFAULT_PAGE_SIZE)
+ map_length = ACPI_DEFAULT_PAGE_SIZE;
/* Create a new mapping starting at the address given */
@@ -509,12 +490,12 @@ acpi_ex_data_table_space_handler(u32 function,
u64 *value,
void *handler_context, void *region_context)
{
- struct acpi_data_table_space_context *mapping;
+ struct acpi_data_table_mapping *mapping;
char *pointer;
ACPI_FUNCTION_TRACE(ex_data_table_space_handler);
- mapping = (struct acpi_data_table_space_context *) region_context;
+ mapping = (struct acpi_data_table_mapping *) region_context;
pointer = ACPI_CAST_PTR(char, mapping->pointer) +
(address - ACPI_PTR_TO_PHYSADDR(mapping->pointer));
diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c
index b81506d73447..dd83631090fc 100644
--- a/drivers/acpi/acpica/exresnte.c
+++ b/drivers/acpi/acpica/exresnte.c
@@ -3,7 +3,7 @@
*
* Module Name: exresnte - AML Interpreter object resolution
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c
index 61ee7fb46006..4589de3f3012 100644
--- a/drivers/acpi/acpica/exresolv.c
+++ b/drivers/acpi/acpica/exresolv.c
@@ -3,7 +3,7 @@
*
* Module Name: exresolv - AML Interpreter object resolution
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c
index 3342780230af..782ee353a709 100644
--- a/drivers/acpi/acpica/exresop.c
+++ b/drivers/acpi/acpica/exresop.c
@@ -3,7 +3,7 @@
*
* Module Name: exresop - AML Interpreter operand/object resolution
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exserial.c b/drivers/acpi/acpica/exserial.c
index 4da20d7845df..6d2581ec22ad 100644
--- a/drivers/acpi/acpica/exserial.c
+++ b/drivers/acpi/acpica/exserial.c
@@ -3,7 +3,7 @@
*
* Module Name: exserial - field_unit support for serial address spaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -201,6 +201,12 @@ acpi_ex_read_serial_bus(union acpi_operand_object *obj_desc,
function = ACPI_READ;
break;
+ case ACPI_ADR_SPACE_FIXED_HARDWARE:
+
+ buffer_length = ACPI_FFH_INPUT_BUFFER_SIZE;
+ function = ACPI_READ;
+ break;
+
default:
return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
}
@@ -323,6 +329,12 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
function = ACPI_WRITE;
break;
+ case ACPI_ADR_SPACE_FIXED_HARDWARE:
+
+ buffer_length = ACPI_FFH_INPUT_BUFFER_SIZE;
+ function = ACPI_WRITE;
+ break;
+
default:
return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
}
@@ -337,8 +349,7 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
/* Copy the input buffer data to the transfer buffer */
buffer = buffer_desc->buffer.pointer;
- data_length = (buffer_length < source_desc->buffer.length ?
- buffer_length : source_desc->buffer.length);
+ data_length = ACPI_MIN(buffer_length, source_desc->buffer.length);
memcpy(buffer, source_desc->buffer.pointer, data_length);
/* Lock entire transaction if requested */
diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c
index f99e8cf27a6c..cbc42207496d 100644
--- a/drivers/acpi/acpica/exstore.c
+++ b/drivers/acpi/acpica/exstore.c
@@ -3,7 +3,7 @@
*
* Module Name: exstore - AML Interpreter object store support
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c
index c848b328e760..0470b2639831 100644
--- a/drivers/acpi/acpica/exstoren.c
+++ b/drivers/acpi/acpica/exstoren.c
@@ -4,7 +4,7 @@
* Module Name: exstoren - AML Interpreter object store support,
* Store to Node (namespace object)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c
index 45c757bbf9a9..5b168fbc03e8 100644
--- a/drivers/acpi/acpica/exstorob.c
+++ b/drivers/acpi/acpica/exstorob.c
@@ -3,7 +3,7 @@
*
* Module Name: exstorob - AML object store support, store to object
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c
index 7b5470f404f3..7f843c9d8a06 100644
--- a/drivers/acpi/acpica/exsystem.c
+++ b/drivers/acpi/acpica/exsystem.c
@@ -3,7 +3,7 @@
*
* Module Name: exsystem - Interface to OS services
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -133,14 +133,15 @@ acpi_status acpi_ex_system_do_stall(u32 how_long_us)
* (ACPI specifies 100 usec as max, but this gives some slack in
* order to support existing BIOSs)
*/
- ACPI_ERROR((AE_INFO,
- "Time parameter is too large (%u)", how_long_us));
+ ACPI_ERROR_ONCE((AE_INFO,
+ "Time parameter is too large (%u)",
+ how_long_us));
status = AE_AML_OPERAND_VALUE;
} else {
if (how_long_us > 100) {
- ACPI_WARNING((AE_INFO,
- "Time parameter %u us > 100 us violating ACPI spec, please fix the firmware.",
- how_long_us));
+ ACPI_WARNING_ONCE((AE_INFO,
+ "Time parameter %u us > 100 us violating ACPI spec, please fix the firmware.",
+ how_long_us));
}
acpi_os_stall(how_long_us);
}
diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c
index b570d7a7e134..36934d4f26fb 100644
--- a/drivers/acpi/acpica/extrace.c
+++ b/drivers/acpi/acpica/extrace.c
@@ -3,7 +3,7 @@
*
* Module Name: extrace - Support for interpreter execution tracing
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -136,9 +136,9 @@ acpi_ex_trace_point(acpi_trace_event_type type,
if (pathname) {
ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
- "%s %s [0x%p:%s] execution.\n",
+ "%s %s [%s] execution.\n",
acpi_ex_get_trace_event_name(type),
- begin ? "Begin" : "End", aml, pathname));
+ begin ? "Begin" : "End", pathname));
} else {
ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
"%s %s [0x%p] execution.\n",
@@ -149,6 +149,57 @@ acpi_ex_trace_point(acpi_trace_event_type type,
/*******************************************************************************
*
+ * FUNCTION: acpi_ex_trace_args
+ *
+ * PARAMETERS: params - AML method arguments
+ * count - numer of method arguments
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Trace any arguments
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_trace_args(union acpi_operand_object **params, u32 count)
+{
+ u32 i;
+
+ ACPI_FUNCTION_NAME(ex_trace_args);
+
+ for (i = 0; i < count; i++) {
+ union acpi_operand_object *obj_desc = params[i];
+
+ if (!i) {
+ ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, " "));
+ }
+
+ switch (obj_desc->common.type) {
+ case ACPI_TYPE_INTEGER:
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TRACE_POINT, "%llx", obj_desc->integer.value));
+ break;
+ case ACPI_TYPE_STRING:
+ if (!obj_desc->string.length) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TRACE_POINT, "NULL"));
+ continue;
+ }
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_TRACE_POINT, _COMPONENT))
+ acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX);
+ break;
+ default:
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TRACE_POINT, "Unknown"));
+ break;
+ }
+ if (i+1 == count) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TRACE_POINT, "\n"));
+ } else {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_TRACE_POINT, ", "));
+ }
+ }
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ex_start_trace_method
*
* PARAMETERS: method_node - Node of the method
diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c
index 87f01ce1c1aa..cc10c0732218 100644
--- a/drivers/acpi/acpica/exutils.c
+++ b/drivers/acpi/acpica/exutils.c
@@ -3,7 +3,7 @@
*
* Module Name: exutils - interpreter/scanner utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c
index 2f1c2fc8bd2a..a1e1fa787566 100644
--- a/drivers/acpi/acpica/hwacpi.c
+++ b/drivers/acpi/acpica/hwacpi.c
@@ -3,7 +3,7 @@
*
* Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
index d8597e052912..631fd8e2b774 100644
--- a/drivers/acpi/acpica/hwesleep.c
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -4,7 +4,7 @@
* Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the
* extended FADT-V5 sleep registers.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index 13d54a48e6e9..386f4759c317 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -3,7 +3,7 @@
*
* Module Name: hwgpe - Low level GPE enable/disable/clear functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index bd936476dda9..87d78bef6323 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -4,7 +4,7 @@
* Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
* original/legacy sleep/PM registers.
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c
index 46f3ae03ab99..a5e0bccae6a4 100644
--- a/drivers/acpi/acpica/hwtimer.c
+++ b/drivers/acpi/acpica/hwtimer.c
@@ -3,7 +3,7 @@
*
* Name: hwtimer.c - ACPI Power Management Timer Interface
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c
index 915b26448d2c..496fd9e49f0b 100644
--- a/drivers/acpi/acpica/hwvalid.c
+++ b/drivers/acpi/acpica/hwvalid.c
@@ -3,7 +3,7 @@
*
* Module Name: hwvalid - I/O request validation
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -23,8 +23,8 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width);
*
* The table is used to implement the Microsoft port access rules that
* first appeared in Windows XP. Some ports are always illegal, and some
- * ports are only illegal if the BIOS calls _OSI with a win_XP string or
- * later (meaning that the BIOS itelf is post-XP.)
+ * ports are only illegal if the BIOS calls _OSI with nothing newer than
+ * the specific _OSI strings.
*
* This provides ACPICA with the desired port protections and
* Microsoft compatibility.
@@ -145,7 +145,8 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width)
/* Port illegality may depend on the _OSI calls made by the BIOS */
- if (acpi_gbl_osi_data >= port_info->osi_dependency) {
+ if (port_info->osi_dependency == ACPI_ALWAYS_ILLEGAL ||
+ acpi_gbl_osi_data == port_info->osi_dependency) {
ACPI_DEBUG_PRINT((ACPI_DB_VALUES,
"Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)\n",
ACPI_FORMAT_UINT64(address),
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index 55d9b897e70f..847cd1b2493d 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -3,7 +3,7 @@
*
* Module Name: hwxface - Public ACPICA hardware interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
index aff51ceea02c..9aabe30416da 100644
--- a/drivers/acpi/acpica/hwxfsleep.c
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -3,7 +3,7 @@
*
* Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -16,20 +16,11 @@
ACPI_MODULE_NAME("hwxfsleep")
/* Local prototypes */
-#if (!ACPI_REDUCED_HARDWARE)
static acpi_status
acpi_hw_set_firmware_waking_vector(struct acpi_table_facs *facs,
acpi_physical_address physical_address,
acpi_physical_address physical_address64);
-#endif
-
-/*
- * These functions are removed for the ACPI_REDUCED_HARDWARE case:
- * acpi_set_firmware_waking_vector
- * acpi_enter_sleep_state_s4bios
- */
-#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: acpi_hw_set_firmware_waking_vector
@@ -115,6 +106,12 @@ acpi_set_firmware_waking_vector(acpi_physical_address physical_address,
ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
+/*
+ * These functions are removed for the ACPI_REDUCED_HARDWARE case:
+ * acpi_enter_sleep_state_s4bios
+ */
+
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: acpi_enter_sleep_state_s4bios
diff --git a/drivers/acpi/acpica/nsarguments.c b/drivers/acpi/acpica/nsarguments.c
index 22586b90e532..366d54a1d157 100644
--- a/drivers/acpi/acpica/nsarguments.c
+++ b/drivers/acpi/acpica/nsarguments.c
@@ -3,7 +3,7 @@
*
* Module Name: nsarguments - Validation of args for ACPI predefined methods
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c
index b02555fe38f0..f05a92b88642 100644
--- a/drivers/acpi/acpica/nsconvert.c
+++ b/drivers/acpi/acpica/nsconvert.c
@@ -4,7 +4,7 @@
* Module Name: nsconvert - Object conversions for objects returned by
* predefined methods
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index f154824d4eb6..6dc20486ad51 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -3,7 +3,7 @@
*
* Module Name: nsdump - table dumping routines for debug
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c
index b9a88b7b518b..d5b16aaec233 100644
--- a/drivers/acpi/acpica/nsdumpdv.c
+++ b/drivers/acpi/acpica/nsdumpdv.c
@@ -3,7 +3,7 @@
*
* Module Name: nsdump - table dumping routines for debug
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c
index 3e6207ad18d8..03373e7f7978 100644
--- a/drivers/acpi/acpica/nsinit.c
+++ b/drivers/acpi/acpica/nsinit.c
@@ -3,7 +3,7 @@
*
* Module Name: nsinit - namespace initialization
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c
index 880260a30c0c..6ec4c646fff7 100644
--- a/drivers/acpi/acpica/nsload.c
+++ b/drivers/acpi/acpica/nsload.c
@@ -3,7 +3,7 @@
*
* Module Name: nsload - namespace loading/expanding/contracting procedures
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c
index d91153f65700..22aeeeb56cff 100644
--- a/drivers/acpi/acpica/nsnames.c
+++ b/drivers/acpi/acpica/nsnames.c
@@ -194,7 +194,7 @@ acpi_ns_build_normalized_path(struct acpi_namespace_node *node,
char *full_path, u32 path_size, u8 no_trailing)
{
u32 length = 0, i;
- char name[ACPI_NAMESEG_SIZE];
+ char name[ACPI_NAMESEG_SIZE] ACPI_NONSTRING;
u8 do_no_trailing;
char c, *left, *right;
struct acpi_namespace_node *next_node;
diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c
index 4b893676ab5c..959e6379bc4c 100644
--- a/drivers/acpi/acpica/nsparse.c
+++ b/drivers/acpi/acpica/nsparse.c
@@ -3,7 +3,7 @@
*
* Module Name: nsparse - namespace interface to AML parser
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index c0db6690bb32..81995ee48c49 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -3,7 +3,7 @@
*
* Module Name: nspredef - Validation of ACPI predefined methods and objects
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c
index 82932c9a774b..ca137ce5674f 100644
--- a/drivers/acpi/acpica/nsprepkg.c
+++ b/drivers/acpi/acpica/nsprepkg.c
@@ -3,7 +3,7 @@
*
* Module Name: nsprepkg - Validation of package objects for predefined names
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index 367fcd201f96..accfdcfb7e62 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -3,7 +3,7 @@
*
* Module Name: nsrepair - Repair for objects returned by predefined methods
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -181,8 +181,9 @@ acpi_ns_simple_repair(struct acpi_evaluate_info *info,
* Try to fix if there was no return object. Warning if failed to fix.
*/
if (!return_object) {
- if (expected_btypes && (!(expected_btypes & ACPI_RTYPE_NONE))) {
- if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
+ if (expected_btypes) {
+ if (!(expected_btypes & ACPI_RTYPE_NONE) &&
+ package_index != ACPI_NOT_PACKAGE_ELEMENT) {
ACPI_WARN_PREDEFINED((AE_INFO,
info->full_pathname,
ACPI_WARN_ALWAYS,
@@ -196,14 +197,15 @@ acpi_ns_simple_repair(struct acpi_evaluate_info *info,
if (ACPI_SUCCESS(status)) {
return (AE_OK); /* Repair was successful */
}
- } else {
+ }
+
+ if (expected_btypes != ACPI_RTYPE_NONE) {
ACPI_WARN_PREDEFINED((AE_INFO,
info->full_pathname,
ACPI_WARN_ALWAYS,
"Missing expected return value"));
+ return (AE_AML_NO_RETURN_VALUE);
}
-
- return (AE_AML_NO_RETURN_VALUE);
}
}
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index dd533c887e3a..8dbb870f40d2 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -4,7 +4,7 @@
* Module Name: nsrepair2 - Repair for objects returned by specific
* predefined methods
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -25,7 +25,7 @@ acpi_status (*acpi_repair_function) (struct acpi_evaluate_info * info,
return_object_ptr);
typedef struct acpi_repair_info {
- char name[ACPI_NAMESEG_SIZE];
+ char name[ACPI_NAMESEG_SIZE] ACPI_NONSTRING;
acpi_repair_function repair_function;
} acpi_repair_info;
@@ -499,7 +499,7 @@ acpi_ns_repair_HID(struct acpi_evaluate_info *info,
char *source;
char *dest;
- ACPI_FUNCTION_NAME(ns_repair_HID);
+ ACPI_FUNCTION_TRACE(ns_repair_HID);
/* We only care about string _HID objects (not integers) */
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index ef531b145add..49cc07e2ac5a 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -4,7 +4,7 @@
* Module Name: nsutils - Utilities for accessing ACPI namespace, accessing
* parents and siblings and Scope manipulation
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c
index 82a0dae349e2..5670ff5a43cd 100644
--- a/drivers/acpi/acpica/nswalk.c
+++ b/drivers/acpi/acpica/nswalk.c
@@ -3,7 +3,7 @@
*
* Module Name: nswalk - Functions for walking the ACPI namespace
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -169,9 +169,12 @@ acpi_ns_walk_namespace(acpi_object_type type,
if (start_node == ACPI_ROOT_OBJECT) {
start_node = acpi_gbl_root_node;
- if (!start_node) {
- return_ACPI_STATUS(AE_NO_NAMESPACE);
- }
+ }
+
+ /* Avoid walking the namespace if the StartNode is NULL */
+
+ if (!start_node) {
+ return_ACPI_STATUS(AE_NO_NAMESPACE);
}
/* Null child means "get first node" */
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index b2cfdfef3194..1db831545ec8 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -4,7 +4,7 @@
* Module Name: nsxfname - Public interfaces to the ACPI subsystem
* ACPI Namespace oriented interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -44,7 +44,7 @@ static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
acpi_status
acpi_get_handle(acpi_handle parent,
- acpi_string pathname, acpi_handle *ret_handle)
+ const char *pathname, acpi_handle *ret_handle)
{
acpi_status status;
struct acpi_namespace_node *node = NULL;
diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c
index f7ec5606098c..6f6ae38ec044 100644
--- a/drivers/acpi/acpica/psargs.c
+++ b/drivers/acpi/acpica/psargs.c
@@ -3,7 +3,7 @@
*
* Module Name: psargs - Parse AML opcode arguments
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -25,6 +25,8 @@ acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state);
static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state
*parser_state);
+static void acpi_ps_free_field_list(union acpi_parse_object *start);
+
/*******************************************************************************
*
* FUNCTION: acpi_ps_get_next_package_length
@@ -685,6 +687,39 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state
/*******************************************************************************
*
+ * FUNCTION: acpi_ps_free_field_list
+ *
+ * PARAMETERS: start - First Op in field list
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Free all Op objects inside a field list.
+ *
+ ******************************************************************************/
+
+static void acpi_ps_free_field_list(union acpi_parse_object *start)
+{
+ union acpi_parse_object *cur = start;
+ union acpi_parse_object *next;
+ union acpi_parse_object *arg;
+
+ while (cur) {
+ next = cur->common.next;
+
+ /* AML_INT_CONNECTION_OP can have a single argument */
+
+ arg = acpi_ps_get_arg(cur, 0);
+ if (arg) {
+ acpi_ps_free_op(arg);
+ }
+
+ acpi_ps_free_op(cur);
+ cur = next;
+ }
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ps_get_next_arg
*
* PARAMETERS: walk_state - Current state
@@ -751,6 +786,10 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
while (parser_state->aml < parser_state->pkg_end) {
field = acpi_ps_get_next_field(parser_state);
if (!field) {
+ if (arg) {
+ acpi_ps_free_field_list(arg);
+ }
+
return_ACPI_STATUS(AE_NO_MEMORY);
}
@@ -820,6 +859,10 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
acpi_ps_get_next_namepath(walk_state, parser_state,
arg,
ACPI_NOT_METHOD_CALL);
+ if (ACPI_FAILURE(status)) {
+ acpi_ps_free_op(arg);
+ return_ACPI_STATUS(status);
+ }
} else {
/* Single complex argument, nothing returned */
@@ -854,6 +897,10 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
acpi_ps_get_next_namepath(walk_state, parser_state,
arg,
ACPI_POSSIBLE_METHOD_CALL);
+ if (ACPI_FAILURE(status)) {
+ acpi_ps_free_op(arg);
+ return_ACPI_STATUS(status);
+ }
if (arg->common.aml_opcode == AML_INT_METHODCALL_OP) {
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c
index 840512fa9fc6..c989cadf271c 100644
--- a/drivers/acpi/acpica/psloop.c
+++ b/drivers/acpi/acpica/psloop.c
@@ -3,7 +3,7 @@
*
* Module Name: psloop - Main AML parse loop
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c
index bca249e67c6b..496a1c1d5b0b 100644
--- a/drivers/acpi/acpica/psobject.c
+++ b/drivers/acpi/acpica/psobject.c
@@ -3,7 +3,7 @@
*
* Module Name: psobject - Support for parse objects
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -636,7 +636,8 @@ acpi_status
acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
union acpi_parse_object *op, acpi_status status)
{
- acpi_status status2;
+ acpi_status return_status = status;
+ u8 ascending = TRUE;
ACPI_FUNCTION_TRACE_PTR(ps_complete_final_op, walk_state);
@@ -650,7 +651,7 @@ acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
op));
do {
if (op) {
- if (walk_state->ascending_callback != NULL) {
+ if (ascending && walk_state->ascending_callback != NULL) {
walk_state->op = op;
walk_state->op_info =
acpi_ps_get_opcode_info(op->common.
@@ -672,49 +673,26 @@ acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
}
if (status == AE_CTRL_TERMINATE) {
- status = AE_OK;
-
- /* Clean up */
- do {
- if (op) {
- status2 =
- acpi_ps_complete_this_op
- (walk_state, op);
- if (ACPI_FAILURE
- (status2)) {
- return_ACPI_STATUS
- (status2);
- }
- }
-
- acpi_ps_pop_scope(&
- (walk_state->
- parser_state),
- &op,
- &walk_state->
- arg_types,
- &walk_state->
- arg_count);
-
- } while (op);
-
- return_ACPI_STATUS(status);
+ ascending = FALSE;
+ return_status = AE_CTRL_TERMINATE;
}
else if (ACPI_FAILURE(status)) {
/* First error is most important */
- (void)
- acpi_ps_complete_this_op(walk_state,
- op);
- return_ACPI_STATUS(status);
+ ascending = FALSE;
+ return_status = status;
}
}
- status2 = acpi_ps_complete_this_op(walk_state, op);
- if (ACPI_FAILURE(status2)) {
- return_ACPI_STATUS(status2);
+ status = acpi_ps_complete_this_op(walk_state, op);
+ if (ACPI_FAILURE(status)) {
+ ascending = FALSE;
+ if (ACPI_SUCCESS(return_status) ||
+ return_status == AE_CTRL_TERMINATE) {
+ return_status = status;
+ }
}
}
@@ -724,5 +702,5 @@ acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
} while (op);
- return_ACPI_STATUS(status);
+ return_ACPI_STATUS(return_status);
}
diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c
index bef69e87a0a2..bf6103986f48 100644
--- a/drivers/acpi/acpica/psopcode.c
+++ b/drivers/acpi/acpica/psopcode.c
@@ -3,7 +3,7 @@
*
* Module Name: psopcode - Parser/Interpreter opcode information table
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -603,7 +603,7 @@ const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = {
/* 7E */ ACPI_OP("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY,
AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R,
- AML_FLAGS_EXEC_0A_0T_1R),
+ AML_FLAGS_EXEC_0A_0T_1R | AML_NO_OPERAND_RESOLVE),
/* ACPI 5.0 opcodes */
diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c
index f10afe699ad7..532ea307a675 100644
--- a/drivers/acpi/acpica/psopinfo.c
+++ b/drivers/acpi/acpica/psopinfo.c
@@ -3,7 +3,7 @@
*
* Module Name: psopinfo - AML opcode information functions and dispatch tables
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -34,7 +34,7 @@ static const u8 acpi_gbl_argument_count[] =
const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode)
{
-#ifdef ACPI_DEBUG_OUTPUT
+#if defined ACPI_ASL_COMPILER && defined ACPI_DEBUG_OUTPUT
const char *opcode_name = "Unknown AML opcode";
#endif
@@ -102,11 +102,11 @@ const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode)
default:
break;
}
-#endif
/* Unknown AML opcode */
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%4.4X]\n", opcode_name, opcode));
+#endif
return (&acpi_gbl_aml_op_info[_UNK]);
}
diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c
index ba93f359760a..55a416e56fd8 100644
--- a/drivers/acpi/acpica/psparse.c
+++ b/drivers/acpi/acpica/psparse.c
@@ -3,7 +3,7 @@
*
* Module Name: psparse - Parser top level AML parse routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c
index 400f001631ea..c4e4483f0a0b 100644
--- a/drivers/acpi/acpica/psscope.c
+++ b/drivers/acpi/acpica/psscope.c
@@ -3,7 +3,7 @@
*
* Module Name: psscope - Parser scope stack management routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c
index 3012a9342367..5a285d3f2cdb 100644
--- a/drivers/acpi/acpica/pstree.c
+++ b/drivers/acpi/acpica/pstree.c
@@ -3,7 +3,7 @@
*
* Module Name: pstree - Parser op tree manipulation/traversal/search
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c
index 49b39aeded12..ada1dc304d25 100644
--- a/drivers/acpi/acpica/psutils.c
+++ b/drivers/acpi/acpica/psutils.c
@@ -3,7 +3,7 @@
*
* Module Name: psutils - Parser miscellaneous utilities (Parser only)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c
index 7735a01dab90..2f3ebcd8aebe 100644
--- a/drivers/acpi/acpica/pswalk.c
+++ b/drivers/acpi/acpica/pswalk.c
@@ -3,7 +3,7 @@
*
* Module Name: pswalk - Parser routines to walk parsed op tree(s)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c
index a6509aeb2955..d480de075a90 100644
--- a/drivers/acpi/acpica/psxface.c
+++ b/drivers/acpi/acpica/psxface.c
@@ -3,7 +3,7 @@
*
* Module Name: psxface - Parser external interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c
index 5737c3af1902..f92010e667cd 100644
--- a/drivers/acpi/acpica/rsaddr.c
+++ b/drivers/acpi/acpica/rsaddr.c
@@ -277,7 +277,8 @@ acpi_rs_get_address_common(struct acpi_resource *resource,
/* Validate the Resource Type */
if ((aml->address.resource_type > 2) &&
- (aml->address.resource_type < 0xC0)) {
+ (aml->address.resource_type < 0xC0) &&
+ (aml->address.resource_type != 0x0A)) {
return (FALSE);
}
diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c
index 90583db459a2..242daf45e20e 100644
--- a/drivers/acpi/acpica/rscalc.c
+++ b/drivers/acpi/acpica/rscalc.c
@@ -320,6 +320,16 @@ acpi_rs_get_aml_length(struct acpi_resource *resource,
break;
+ case ACPI_RESOURCE_TYPE_CLOCK_INPUT:
+
+ total_size = (acpi_rs_length)(total_size +
+ resource->data.
+ clock_input.
+ resource_source.
+ string_length);
+
+ break;
+
case ACPI_RESOURCE_TYPE_SERIAL_BUS:
total_size =
@@ -596,15 +606,17 @@ acpi_rs_get_list_length(u8 *aml_buffer,
}
break;
- case ACPI_RESOURCE_NAME_SERIAL_BUS:
+ case ACPI_RESOURCE_NAME_SERIAL_BUS:{
- minimum_aml_resource_length =
- acpi_gbl_resource_aml_serial_bus_sizes
- [aml_resource->common_serial_bus.type];
- extra_struct_bytes +=
- aml_resource->common_serial_bus.resource_length -
- minimum_aml_resource_length;
- break;
+ minimum_aml_resource_length =
+ acpi_gbl_resource_aml_serial_bus_sizes
+ [aml_resource->common_serial_bus.type];
+ extra_struct_bytes +=
+ aml_resource->common_serial_bus.
+ resource_length -
+ minimum_aml_resource_length;
+ break;
+ }
case ACPI_RESOURCE_NAME_PIN_CONFIG:
@@ -650,6 +662,13 @@ acpi_rs_get_list_length(u8 *aml_buffer,
break;
+ case ACPI_RESOURCE_NAME_CLOCK_INPUT:
+ extra_struct_bytes =
+ acpi_rs_stream_option_length(resource_length,
+ minimum_aml_resource_length);
+
+ break;
+
default:
break;
diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c
index 611bc71c193f..5b7d7074ce4f 100644
--- a/drivers/acpi/acpica/rsdump.c
+++ b/drivers/acpi/acpica/rsdump.c
@@ -48,6 +48,7 @@ static void acpi_rs_dump_address_common(union acpi_resource_data *resource);
static void
acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table);
+#ifdef ACPI_DEBUGGER
/*******************************************************************************
*
* FUNCTION: acpi_rs_dump_resource_list
@@ -160,6 +161,7 @@ void acpi_rs_dump_irq_list(u8 *route_table)
prt_element, prt_element->length);
}
}
+#endif
/*******************************************************************************
*
diff --git a/drivers/acpi/acpica/rsdumpinfo.c b/drivers/acpi/acpica/rsdumpinfo.c
index b8b37449011b..998a79cc09c2 100644
--- a/drivers/acpi/acpica/rsdumpinfo.c
+++ b/drivers/acpi/acpica/rsdumpinfo.c
@@ -301,6 +301,23 @@ struct acpi_rsdump_info acpi_rs_dump_pin_function[10] = {
"VendorData", NULL},
};
+struct acpi_rsdump_info acpi_rs_dump_clock_input[7] = {
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_clock_input),
+ "ClockInput", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(clock_input.revision_id), "RevisionId",
+ NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(clock_input.frequency_numerator),
+ "FrequencyNumerator", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(clock_input.frequency_divisor),
+ "FrequencyDivisor", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(clock_input.scale), "Scale",
+ acpi_gbl_clock_input_scale},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(clock_input.mode), "Mode",
+ acpi_gbl_clock_input_mode},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(clock_input.resource_source),
+ "ResourceSource", NULL},
+};
+
struct acpi_rsdump_info acpi_rs_dump_pin_config[11] = {
{ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_pin_config),
"PinConfig", NULL},
diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c
index eaeb7ab58c2a..ad7465ddfe13 100644
--- a/drivers/acpi/acpica/rsinfo.c
+++ b/drivers/acpi/acpica/rsinfo.c
@@ -49,6 +49,7 @@ struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[] = {
acpi_rs_convert_pin_group, /* 0x16, ACPI_RESOURCE_TYPE_PIN_GROUP */
acpi_rs_convert_pin_group_function, /* 0x17, ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION */
acpi_rs_convert_pin_group_config, /* 0x18, ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG */
+ acpi_rs_convert_clock_input, /* 0x19, ACPI_RESOURCE_TYPE_CLOCK_INPUT */
};
/* Dispatch tables for AML-to-resource (Get Resource) conversion functions */
@@ -94,6 +95,7 @@ struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[] = {
acpi_rs_convert_pin_group, /* 0x10, ACPI_RESOURCE_NAME_PIN_GROUP */
acpi_rs_convert_pin_group_function, /* 0x11, ACPI_RESOURCE_NAME_PIN_GROUP_FUNCTION */
acpi_rs_convert_pin_group_config, /* 0x12, ACPI_RESOURCE_NAME_PIN_GROUP_CONFIG */
+ acpi_rs_convert_clock_input, /* 0x13, ACPI_RESOURCE_NAME_CLOCK_INPUT */
};
/* Subtype table for serial_bus -- I2C, SPI, UART, and CSI2 */
@@ -136,6 +138,7 @@ struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[] = {
acpi_rs_dump_pin_group, /* ACPI_RESOURCE_TYPE_PIN_GROUP */
acpi_rs_dump_pin_group_function, /* ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION */
acpi_rs_dump_pin_group_config, /* ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG */
+ acpi_rs_dump_clock_input, /* ACPI_RESOURCE_TYPE_CLOCK_INPUT */
};
struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[] = {
@@ -178,6 +181,7 @@ const u8 acpi_gbl_aml_resource_sizes[] = {
sizeof(struct aml_resource_pin_group), /* ACPI_RESOURCE_TYPE_PIN_GROUP */
sizeof(struct aml_resource_pin_group_function), /* ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION */
sizeof(struct aml_resource_pin_group_config), /* ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG */
+ sizeof(struct aml_resource_clock_input), /* ACPI_RESOURCE_TYPE_CLOCK_INPUT */
};
const u8 acpi_gbl_resource_struct_sizes[] = {
@@ -221,6 +225,7 @@ const u8 acpi_gbl_resource_struct_sizes[] = {
ACPI_RS_SIZE(struct acpi_resource_pin_group),
ACPI_RS_SIZE(struct acpi_resource_pin_group_function),
ACPI_RS_SIZE(struct acpi_resource_pin_group_config),
+ ACPI_RS_SIZE(struct acpi_resource_clock_input),
};
const u8 acpi_gbl_aml_resource_serial_bus_sizes[] = {
diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c
index c2dd9aae4745..6e8e98cf598d 100644
--- a/drivers/acpi/acpica/rsmisc.c
+++ b/drivers/acpi/acpica/rsmisc.c
@@ -194,7 +194,8 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource,
case ACPI_RSC_COUNT_SERIAL_VEN:
- item_count = ACPI_GET16(source) - info->value;
+ ACPI_MOVE_16_TO_16(&temp16, source);
+ item_count = temp16 - info->value;
resource->length = resource->length + item_count;
ACPI_SET16(destination, item_count);
@@ -202,9 +203,10 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource,
case ACPI_RSC_COUNT_SERIAL_RES:
+ ACPI_MOVE_16_TO_16(&temp16, source);
item_count = (aml_resource_length +
sizeof(struct aml_resource_large_header))
- - ACPI_GET16(source) - info->value;
+ - temp16 - info->value;
resource->length = resource->length + item_count;
ACPI_SET16(destination, item_count);
@@ -289,9 +291,9 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource,
/* Copy the resource_source string */
+ ACPI_MOVE_16_TO_16(&temp16, source);
source =
- ACPI_ADD_PTR(void, aml,
- (ACPI_GET16(source) + info->value));
+ ACPI_ADD_PTR(void, aml, (temp16 + info->value));
acpi_rs_move_data(target, source, item_count,
info->opcode);
break;
diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c
index f9267956535c..279bfa27da94 100644
--- a/drivers/acpi/acpica/rsserial.c
+++ b/drivers/acpi/acpica/rsserial.c
@@ -111,6 +111,55 @@ struct acpi_rsconvert_info acpi_rs_convert_gpio[18] = {
/*******************************************************************************
*
+ * acpi_rs_convert_clock_input
+ *
+ ******************************************************************************/
+
+struct acpi_rsconvert_info acpi_rs_convert_clock_input[8] = {
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_CLOCK_INPUT,
+ ACPI_RS_SIZE(struct acpi_resource_clock_input),
+ ACPI_RSC_TABLE_SIZE(acpi_rs_convert_clock_input)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_CLOCK_INPUT,
+ sizeof(struct aml_resource_clock_input),
+ 0}
+ ,
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.clock_input.revision_id),
+ AML_OFFSET(clock_input.revision_id),
+ 1}
+ ,
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.clock_input.mode),
+ AML_OFFSET(clock_input.flags),
+ 0}
+ ,
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.clock_input.scale),
+ AML_OFFSET(clock_input.flags),
+ 1}
+ ,
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.clock_input.frequency_divisor),
+ AML_OFFSET(clock_input.frequency_divisor),
+ 2}
+ ,
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.clock_input.frequency_numerator),
+ AML_OFFSET(clock_input.frequency_numerator),
+ 4}
+ ,
+
+ /* Resource Source */
+ {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.clock_input.resource_source),
+ 0,
+ sizeof(struct aml_resource_clock_input)}
+ ,
+
+};
+
+/*******************************************************************************
+ *
* acpi_rs_convert_pinfunction
*
******************************************************************************/
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
index a7642b34ce48..5b98e09fff76 100644
--- a/drivers/acpi/acpica/tbdata.c
+++ b/drivers/acpi/acpica/tbdata.c
@@ -3,7 +3,7 @@
*
* Module Name: tbdata - Table manager data structure functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -522,7 +522,7 @@ acpi_tb_verify_temp_table(struct acpi_table_desc *table_desc,
/* Verify the checksum */
status =
- acpi_tb_verify_checksum(table_desc->pointer,
+ acpi_ut_verify_checksum(table_desc->pointer,
table_desc->length);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
index 31d7ea84a360..c6658b2f3027 100644
--- a/drivers/acpi/acpica/tbfadt.c
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -3,7 +3,7 @@
*
* Module Name: tbfadt - FADT table utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -298,7 +298,7 @@ void acpi_tb_parse_fadt(void)
* Validate the FADT checksum before we copy the table. Ignore
* checksum error as we want to try to get the DSDT and FACS.
*/
- (void)acpi_tb_verify_checksum(table, length);
+ (void)acpi_ut_verify_checksum(table, length);
/* Create a local copy of the FADT in common ACPI 2.0+ format */
@@ -315,23 +315,19 @@ void acpi_tb_parse_fadt(void)
ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
NULL, FALSE, TRUE, &acpi_gbl_dsdt_index);
- /* If Hardware Reduced flag is set, there is no FACS */
-
- if (!acpi_gbl_reduced_hardware) {
- if (acpi_gbl_FADT.facs) {
- acpi_tb_install_standard_table((acpi_physical_address)
- acpi_gbl_FADT.facs,
- ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
- NULL, FALSE, TRUE,
- &acpi_gbl_facs_index);
- }
- if (acpi_gbl_FADT.Xfacs) {
- acpi_tb_install_standard_table((acpi_physical_address)
- acpi_gbl_FADT.Xfacs,
- ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
- NULL, FALSE, TRUE,
- &acpi_gbl_xfacs_index);
- }
+ if (acpi_gbl_FADT.facs) {
+ acpi_tb_install_standard_table((acpi_physical_address)
+ acpi_gbl_FADT.facs,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ NULL, FALSE, TRUE,
+ &acpi_gbl_facs_index);
+ }
+ if (acpi_gbl_FADT.Xfacs) {
+ acpi_tb_install_standard_table((acpi_physical_address)
+ acpi_gbl_FADT.Xfacs,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ NULL, FALSE, TRUE,
+ &acpi_gbl_xfacs_index);
}
}
diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c
index c31a5ddb0ffd..d71a73216380 100644
--- a/drivers/acpi/acpica/tbfind.c
+++ b/drivers/acpi/acpica/tbfind.c
@@ -3,7 +3,7 @@
*
* Module Name: tbfind - find table
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -57,8 +57,8 @@ acpi_tb_find_table(char *signature,
memset(&header, 0, sizeof(struct acpi_table_header));
ACPI_COPY_NAMESEG(header.signature, signature);
- strncpy(header.oem_id, oem_id, ACPI_OEM_ID_SIZE);
- strncpy(header.oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE);
+ memcpy(header.oem_id, oem_id, ACPI_OEM_ID_SIZE);
+ memcpy(header.oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE);
/* Search for the table */
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 499efcaf798d..ee9b85bc238b 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -3,7 +3,7 @@
*
* Module Name: tbinstal - ACPI table installation and removal
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c
index 595547db28c0..e5631027f7f1 100644
--- a/drivers/acpi/acpica/tbprint.c
+++ b/drivers/acpi/acpica/tbprint.c
@@ -3,13 +3,14 @@
*
* Module Name: tbprint - Table output utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
#include "actables.h"
+#include "acutils.h"
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME("tbprint")
@@ -39,7 +40,7 @@ static void acpi_tb_fix_string(char *string, acpi_size length)
{
while (length && *string) {
- if (!isprint((int)*string)) {
+ if (!isprint((int)(u8)*string)) {
*string = '?';
}
@@ -94,6 +95,11 @@ acpi_tb_print_table_header(acpi_physical_address address,
{
struct acpi_table_header local_header;
+#pragma GCC diagnostic push
+#if defined(__GNUC__) && __GNUC__ >= 11
+#pragma GCC diagnostic ignored "-Wstringop-overread"
+#endif
+
if (ACPI_COMPARE_NAMESEG(header->signature, ACPI_SIG_FACS)) {
/* FACS only has signature and length fields */
@@ -120,6 +126,14 @@ acpi_tb_print_table_header(acpi_physical_address address,
ACPI_CAST_PTR(struct acpi_table_rsdp,
header)->revision,
local_header.oem_id));
+ } else if (acpi_gbl_CDAT && !acpi_ut_valid_nameseg(header->signature)) {
+
+ /* CDAT does not use the common ACPI table header */
+
+ ACPI_INFO(("%-4.4s 0x%8.8X%8.8X %06X",
+ ACPI_SIG_CDAT, ACPI_FORMAT_UINT64(address),
+ ACPI_CAST_PTR(struct acpi_table_cdat,
+ header)->length));
} else {
/* Standard ACPI table with full common header */
@@ -134,78 +148,5 @@ acpi_tb_print_table_header(acpi_physical_address address,
local_header.asl_compiler_id,
local_header.asl_compiler_revision));
}
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_tb_validate_checksum
- *
- * PARAMETERS: table - ACPI table to verify
- * length - Length of entire table
- *
- * RETURN: Status
- *
- * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns
- * exception on bad checksum.
- *
- ******************************************************************************/
-
-acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length)
-{
- u8 checksum;
-
- /*
- * FACS/S3PT:
- * They are the odd tables, have no standard ACPI header and no checksum
- */
-
- if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_S3PT) ||
- ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_FACS)) {
- return (AE_OK);
- }
-
- /* Compute the checksum on the table */
-
- checksum = acpi_tb_checksum(ACPI_CAST_PTR(u8, table), length);
-
- /* Checksum ok? (should be zero) */
-
- if (checksum) {
- ACPI_BIOS_WARNING((AE_INFO,
- "Incorrect checksum in table [%4.4s] - 0x%2.2X, "
- "should be 0x%2.2X",
- table->signature, table->checksum,
- (u8)(table->checksum - checksum)));
-
-#if (ACPI_CHECKSUM_ABORT)
- return (AE_BAD_CHECKSUM);
-#endif
- }
-
- return (AE_OK);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_tb_checksum
- *
- * PARAMETERS: buffer - Pointer to memory region to be checked
- * length - Length of this memory region
- *
- * RETURN: Checksum (u8)
- *
- * DESCRIPTION: Calculates circular checksum of memory region.
- *
- ******************************************************************************/
-
-u8 acpi_tb_checksum(u8 *buffer, u32 length)
-{
- u8 sum = 0;
- u8 *end = buffer + length;
-
- while (buffer < end) {
- sum = (u8)(sum + *(buffer++));
- }
-
- return (sum);
+#pragma GCC diagnostic pop
}
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
index 633a823be65f..fa64851c7b62 100644
--- a/drivers/acpi/acpica/tbutils.c
+++ b/drivers/acpi/acpica/tbutils.c
@@ -3,7 +3,7 @@
*
* Module Name: tbutils - ACPI Table utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -18,7 +18,6 @@ ACPI_MODULE_NAME("tbutils")
static acpi_physical_address
acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size);
-#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: acpi_tb_initialize_facs
@@ -36,12 +35,7 @@ acpi_status acpi_tb_initialize_facs(void)
{
struct acpi_table_facs *facs;
- /* If Hardware Reduced flag is set, there is no FACS */
-
- if (acpi_gbl_reduced_hardware) {
- acpi_gbl_FACS = NULL;
- return (AE_OK);
- } else if (acpi_gbl_FADT.Xfacs &&
+ if (acpi_gbl_FADT.Xfacs &&
(!acpi_gbl_FADT.facs
|| !acpi_gbl_use32_bit_facs_addresses)) {
(void)acpi_get_table_by_index(acpi_gbl_xfacs_index,
@@ -61,7 +55,6 @@ acpi_status acpi_tb_initialize_facs(void)
return (AE_OK);
}
-#endif /* !ACPI_REDUCED_HARDWARE */
/*******************************************************************************
*
@@ -165,6 +158,7 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index)
static acpi_physical_address
acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
{
+ u32 address32;
u64 address64;
/*
@@ -176,8 +170,8 @@ acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
* 32-bit platform, RSDT: Return 32-bit table entry
* 64-bit platform, RSDT: Expand 32-bit to 64-bit and return
*/
- return ((acpi_physical_address)
- (*ACPI_CAST_PTR(u32, table_entry)));
+ ACPI_MOVE_32_TO_32(&address32, table_entry);
+ return address32;
} else {
/*
* 32-bit platform, XSDT: Truncate 64-bit to 32-bit and return
@@ -299,7 +293,7 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
/* Validate the root table checksum */
- status = acpi_tb_verify_checksum(table, length);
+ status = acpi_ut_verify_checksum(table, length);
if (ACPI_FAILURE(status)) {
acpi_os_unmap_memory(table, length);
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
index 37da09dca940..a8f07d2641b6 100644
--- a/drivers/acpi/acpica/tbxface.c
+++ b/drivers/acpi/acpica/tbxface.c
@@ -3,7 +3,7 @@
*
* Module Name: tbxface - ACPI table-oriented external interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c
index 258796e02be1..2a17c60a9a39 100644
--- a/drivers/acpi/acpica/tbxfload.c
+++ b/drivers/acpi/acpica/tbxfload.c
@@ -3,7 +3,7 @@
*
* Module Name: tbxfload - Table load/unload external interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c
index 3d09e3f6bd43..961577ba9486 100644
--- a/drivers/acpi/acpica/tbxfroot.c
+++ b/drivers/acpi/acpica/tbxfroot.c
@@ -3,7 +3,7 @@
*
* Module Name: tbxfroot - Find the root ACPI table (RSDT)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -74,14 +74,14 @@ acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp)
/* Check the standard checksum */
- if (acpi_tb_checksum((u8 *) rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) {
+ if (acpi_ut_checksum((u8 *)rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) {
return (AE_BAD_CHECKSUM);
}
/* Check extended checksum if table version >= 2 */
if ((rsdp->revision >= 2) &&
- (acpi_tb_checksum((u8 *) rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)) {
+ (acpi_ut_checksum((u8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)) {
return (AE_BAD_CHECKSUM);
}
@@ -114,6 +114,7 @@ acpi_find_root_pointer(acpi_physical_address *table_address)
u8 *table_ptr;
u8 *mem_rover;
u32 physical_address;
+ u32 ebda_window_size;
ACPI_FUNCTION_TRACE(acpi_find_root_pointer);
@@ -139,26 +140,37 @@ acpi_find_root_pointer(acpi_physical_address *table_address)
/* EBDA present? */
- if (physical_address > 0x400) {
+ /*
+ * Check that the EBDA pointer from memory is sane and does not point
+ * above valid low memory
+ */
+ if (physical_address > 0x400 && physical_address < 0xA0000) {
+ /*
+ * Calculate the scan window size
+ * The EBDA is not guaranteed to be larger than a ki_b and in case
+ * that it is smaller, the scanning function would leave the low
+ * memory and continue to the VGA range.
+ */
+ ebda_window_size = ACPI_MIN(ACPI_EBDA_WINDOW_SIZE,
+ 0xA0000 - physical_address);
+
/*
- * 1b) Search EBDA paragraphs (EBDA is required to be a
- * minimum of 1K length)
+ * 1b) Search EBDA paragraphs
*/
table_ptr = acpi_os_map_memory((acpi_physical_address)
physical_address,
- ACPI_EBDA_WINDOW_SIZE);
+ ebda_window_size);
if (!table_ptr) {
ACPI_ERROR((AE_INFO,
"Could not map memory at 0x%8.8X for length %u",
- physical_address, ACPI_EBDA_WINDOW_SIZE));
+ physical_address, ebda_window_size));
return_ACPI_STATUS(AE_NO_MEMORY);
}
mem_rover =
- acpi_tb_scan_memory_for_rsdp(table_ptr,
- ACPI_EBDA_WINDOW_SIZE);
- acpi_os_unmap_memory(table_ptr, ACPI_EBDA_WINDOW_SIZE);
+ acpi_tb_scan_memory_for_rsdp(table_ptr, ebda_window_size);
+ acpi_os_unmap_memory(table_ptr, ebda_window_size);
if (mem_rover) {
diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c
index 915321806cd7..c673d6c95e0a 100644
--- a/drivers/acpi/acpica/utaddress.c
+++ b/drivers/acpi/acpica/utaddress.c
@@ -3,7 +3,7 @@
*
* Module Name: utaddress - op_region address range check
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c
index 2bab6017d827..2418a312733a 100644
--- a/drivers/acpi/acpica/utalloc.c
+++ b/drivers/acpi/acpica/utalloc.c
@@ -3,7 +3,7 @@
*
* Module Name: utalloc - local memory allocation routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utascii.c b/drivers/acpi/acpica/utascii.c
index 72fb7e9ec485..259c28d3fecd 100644
--- a/drivers/acpi/acpica/utascii.c
+++ b/drivers/acpi/acpica/utascii.c
@@ -3,7 +3,7 @@
*
* Module Name: utascii - Utility ascii functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c
index 59c4050b8e91..f6e6e98e9523 100644
--- a/drivers/acpi/acpica/utbuffer.c
+++ b/drivers/acpi/acpica/utbuffer.c
@@ -3,7 +3,7 @@
*
* Module Name: utbuffer - Buffer dump routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c
index 5425968dd2a8..cabec193febb 100644
--- a/drivers/acpi/acpica/utcache.c
+++ b/drivers/acpi/acpica/utcache.c
@@ -3,7 +3,7 @@
*
* Module Name: utcache - local cache allocation routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -251,9 +251,9 @@ void *acpi_os_acquire_object(struct acpi_memory_list *cache)
} else {
/* The cache is empty, create a new object */
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
ACPI_MEM_TRACKING(cache->total_allocated++);
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
if ((cache->total_allocated - cache->total_freed) >
cache->max_occupied) {
cache->max_occupied =
diff --git a/drivers/acpi/acpica/utcksum.c b/drivers/acpi/acpica/utcksum.c
new file mode 100644
index 000000000000..e6f6030b3a3f
--- /dev/null
+++ b/drivers/acpi/acpica/utcksum.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/******************************************************************************
+ *
+ * Module Name: utcksum - Support generating table checksums
+ *
+ * Copyright (C) 2000 - 2025, Intel Corp.
+ *
+ *****************************************************************************/
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acutils.h"
+
+/* This module used for application-level code only */
+
+#define _COMPONENT ACPI_CA_DISASSEMBLER
+ACPI_MODULE_NAME("utcksum")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_verify_checksum
+ *
+ * PARAMETERS: table - ACPI table to verify
+ * length - Length of entire table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns
+ * exception on bad checksum.
+ * Note: We don't have to check for a CDAT here, since CDAT is
+ * not in the RSDT/XSDT, and the CDAT table is never installed
+ * via ACPICA.
+ *
+ ******************************************************************************/
+acpi_status acpi_ut_verify_checksum(struct acpi_table_header *table, u32 length)
+{
+ u8 checksum;
+
+ /*
+ * FACS/S3PT:
+ * They are the odd tables, have no standard ACPI header and no checksum
+ */
+ if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_S3PT) ||
+ ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_FACS)) {
+ return (AE_OK);
+ }
+
+ /* Compute the checksum on the table */
+
+ length = table->length;
+ checksum =
+ acpi_ut_generate_checksum(ACPI_CAST_PTR(u8, table), length,
+ table->checksum);
+
+ /* Computed checksum matches table? */
+
+ if (checksum != table->checksum) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "Incorrect checksum in table [%4.4s] - 0x%2.2X, "
+ "should be 0x%2.2X",
+ table->signature, table->checksum,
+ table->checksum - checksum));
+
+#if (ACPI_CHECKSUM_ABORT)
+ return (AE_BAD_CHECKSUM);
+#endif
+ }
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_verify_cdat_checksum
+ *
+ * PARAMETERS: table - CDAT ACPI table to verify
+ * length - Length of entire table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Verifies that the CDAT table checksums to zero. Optionally
+ * returns an exception on bad checksum.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ut_verify_cdat_checksum(struct acpi_table_cdat *cdat_table, u32 length)
+{
+ u8 checksum;
+
+ /* Compute the checksum on the table */
+
+ checksum = acpi_ut_generate_checksum(ACPI_CAST_PTR(u8, cdat_table),
+ cdat_table->length,
+ cdat_table->checksum);
+
+ /* Computed checksum matches table? */
+
+ if (checksum != cdat_table->checksum) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "Incorrect checksum in table [%4.4s] - 0x%2.2X, "
+ "should be 0x%2.2X",
+ acpi_gbl_CDAT, cdat_table->checksum,
+ checksum));
+
+#if (ACPI_CHECKSUM_ABORT)
+ return (AE_BAD_CHECKSUM);
+#endif
+ }
+
+ cdat_table->checksum = checksum;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_generate_checksum
+ *
+ * PARAMETERS: table - Pointer to table to be checksummed
+ * length - Length of the table
+ * original_checksum - Value of the checksum field
+ *
+ * RETURN: 8 bit checksum of buffer
+ *
+ * DESCRIPTION: Computes an 8 bit checksum of the table.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_generate_checksum(void *table, u32 length, u8 original_checksum)
+{
+ u8 checksum;
+
+ /* Sum the entire table as-is */
+
+ checksum = acpi_ut_checksum((u8 *)table, length);
+
+ /* Subtract off the existing checksum value in the table */
+
+ checksum = (u8)(checksum - original_checksum);
+
+ /* Compute and return the final checksum */
+
+ checksum = (u8)(0 - checksum);
+ return (checksum);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_checksum
+ *
+ * PARAMETERS: buffer - Pointer to memory region to be checked
+ * length - Length of this memory region
+ *
+ * RETURN: Checksum (u8)
+ *
+ * DESCRIPTION: Calculates circular checksum of memory region.
+ *
+ ******************************************************************************/
+
+u8 acpi_ut_checksum(u8 *buffer, u32 length)
+{
+ u8 sum = 0;
+ u8 *end = buffer + length;
+
+ while (buffer < end) {
+ sum = (u8)(sum + *(buffer++));
+ }
+
+ return (sum);
+}
diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c
index 400b9e15a709..80458e70ac2b 100644
--- a/drivers/acpi/acpica/utcopy.c
+++ b/drivers/acpi/acpica/utcopy.c
@@ -3,7 +3,7 @@
*
* Module Name: utcopy - Internal to external object translation utilities
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -916,13 +916,6 @@ acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj,
status = acpi_ut_walk_package_tree(source_obj, dest_obj,
acpi_ut_copy_ielement_to_ielement,
walk_state);
- if (ACPI_FAILURE(status)) {
-
- /* On failure, delete the destination package object */
-
- acpi_ut_remove_reference(dest_obj);
- }
-
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
index 64ed546cf19c..9f197e293c7e 100644
--- a/drivers/acpi/acpica/utdebug.c
+++ b/drivers/acpi/acpica/utdebug.c
@@ -3,7 +3,7 @@
*
* Module Name: utdebug - Debug print/trace routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -37,7 +37,12 @@ void acpi_ut_init_stack_ptr_trace(void)
{
acpi_size current_sp;
+#pragma GCC diagnostic push
+#if defined(__GNUC__) && __GNUC__ >= 12
+#pragma GCC diagnostic ignored "-Wdangling-pointer="
+#endif
acpi_gbl_entry_stack_pointer = &current_sp;
+#pragma GCC diagnostic pop
}
/*******************************************************************************
@@ -57,7 +62,12 @@ void acpi_ut_track_stack_ptr(void)
acpi_size current_sp;
if (&current_sp < acpi_gbl_lowest_stack_pointer) {
+#pragma GCC diagnostic push
+#if defined(__GNUC__) && __GNUC__ >= 12
+#pragma GCC diagnostic ignored "-Wdangling-pointer="
+#endif
acpi_gbl_lowest_stack_pointer = &current_sp;
+#pragma GCC diagnostic pop
}
if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) {
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
index 3176393a729d..b82130d1a8bc 100644
--- a/drivers/acpi/acpica/utdecode.c
+++ b/drivers/acpi/acpica/utdecode.c
@@ -3,7 +3,7 @@
*
* Module Name: utdecode - Utility decoding routines (value-to-string)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index 8d7736d2d269..e8180099d01f 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -140,7 +140,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
(void)
acpi_os_delete_semaphore
(acpi_gbl_global_lock_semaphore);
- acpi_gbl_global_lock_semaphore = NULL;
+ acpi_gbl_global_lock_semaphore = ACPI_SEMAPHORE_NULL;
acpi_os_delete_mutex(object->mutex.os_mutex);
acpi_gbl_global_lock_mutex = NULL;
@@ -157,7 +157,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
object, object->event.os_semaphore));
(void)acpi_os_delete_semaphore(object->event.os_semaphore);
- object->event.os_semaphore = NULL;
+ object->event.os_semaphore = ACPI_SEMAPHORE_NULL;
break;
case ACPI_TYPE_METHOD:
@@ -404,7 +404,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
object, object->common.type,
acpi_ut_get_object_type_name(object),
new_count));
- message = "Incremement";
+ message = "Increment";
break;
case REF_DECREMENT:
diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c
index df20d46ed8b7..abc6583ed369 100644
--- a/drivers/acpi/acpica/uteval.c
+++ b/drivers/acpi/acpica/uteval.c
@@ -3,7 +3,7 @@
*
* Module Name: uteval - Object evaluation
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index cda6e16dddf7..97c55a113bae 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -3,7 +3,7 @@
*
* Module Name: utglobal - Global variables for the ACPI subsystem
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c
index c811ee2a8160..8cd050e9cad5 100644
--- a/drivers/acpi/acpica/uthex.c
+++ b/drivers/acpi/acpica/uthex.c
@@ -3,7 +3,7 @@
*
* Module Name: uthex -- Hex/ASCII support functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c
index b6caab49f1bd..eb88335dea2c 100644
--- a/drivers/acpi/acpica/utids.c
+++ b/drivers/acpi/acpica/utids.c
@@ -3,7 +3,7 @@
*
* Module Name: utids - support for device Ids - HID, UID, CID, SUB, CLS
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
index 18177e4f26f7..4bef97e8223a 100644
--- a/drivers/acpi/acpica/utinit.c
+++ b/drivers/acpi/acpica/utinit.c
@@ -3,7 +3,7 @@
*
* Module Name: utinit - Common ACPI subsystem initialization
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -154,7 +154,7 @@ acpi_status acpi_ut_init_globals(void)
/* Global Lock support */
- acpi_gbl_global_lock_semaphore = NULL;
+ acpi_gbl_global_lock_semaphore = ACPI_SEMAPHORE_NULL;
acpi_gbl_global_lock_mutex = NULL;
acpi_gbl_global_lock_acquired = FALSE;
acpi_gbl_global_lock_handle = 0;
diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c
index 84abdbf5cfca..123dbcbc60bc 100644
--- a/drivers/acpi/acpica/utlock.c
+++ b/drivers/acpi/acpica/utlock.c
@@ -3,7 +3,7 @@
*
* Module Name: utlock - Reader/Writer lock interfaces
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c
index d3667bfff401..272e46208263 100644
--- a/drivers/acpi/acpica/utobject.c
+++ b/drivers/acpi/acpica/utobject.c
@@ -3,7 +3,7 @@
*
* Module Name: utobject - ACPI object create/delete/size/cache routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
index b8ab0a3cb5b9..f6ac16729e42 100644
--- a/drivers/acpi/acpica/utosi.c
+++ b/drivers/acpi/acpica/utosi.c
@@ -3,7 +3,7 @@
*
* Module Name: utosi - Support for the _OSI predefined control method
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -75,6 +75,7 @@ static struct acpi_interface_info acpi_default_supported_interfaces[] = {
{"Windows 2019", NULL, 0, ACPI_OSI_WIN_10_19H1}, /* Windows 10 version 1903 - Added 08/2019 */
{"Windows 2020", NULL, 0, ACPI_OSI_WIN_10_20H1}, /* Windows 10 version 2004 - Added 08/2021 */
{"Windows 2021", NULL, 0, ACPI_OSI_WIN_11}, /* Windows 11 - Added 01/2022 */
+ {"Windows 2022", NULL, 0, ACPI_OSI_WIN_11_22H2}, /* Windows 11 version 22H2 - Added 04/2024 */
/* Feature Group Strings */
diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c
index 2524f013be7a..d9bd80e2d32a 100644
--- a/drivers/acpi/acpica/utpredef.c
+++ b/drivers/acpi/acpica/utpredef.c
@@ -3,7 +3,7 @@
*
* Module Name: utpredef - support functions for predefined names
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c
index d5aa2109847f..423d10569736 100644
--- a/drivers/acpi/acpica/utprint.c
+++ b/drivers/acpi/acpica/utprint.c
@@ -3,7 +3,7 @@
*
* Module Name: utprint - Formatted printing routines
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -333,11 +333,8 @@ int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
pos = string;
- if (size != ACPI_UINT32_MAX) {
- end = string + size;
- } else {
- end = ACPI_CAST_PTR(char, ACPI_UINT32_MAX);
- }
+ size = ACPI_MIN(size, ACPI_PTR_DIFF(ACPI_MAX_PTR, string));
+ end = string + size;
for (; *format; ++format) {
if (*format != '%') {
diff --git a/drivers/acpi/acpica/utresdecode.c b/drivers/acpi/acpica/utresdecode.c
index 85730fcd7d00..d801d9069841 100644
--- a/drivers/acpi/acpica/utresdecode.c
+++ b/drivers/acpi/acpica/utresdecode.c
@@ -284,4 +284,15 @@ const char *acpi_gbl_ptyp_decode[] = {
"Input Schmitt Trigger",
};
+const char *acpi_gbl_clock_input_mode[] = {
+ "Fixed",
+ "Variable",
+};
+
+const char *acpi_gbl_clock_input_scale[] = {
+ "Hz",
+ "KHz",
+ "MHz",
+};
+
#endif
diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c
index 16f9a7035b39..e1cc3d348750 100644
--- a/drivers/acpi/acpica/utresrc.c
+++ b/drivers/acpi/acpica/utresrc.c
@@ -57,6 +57,8 @@ const u8 acpi_gbl_resource_aml_sizes[] = {
ACPI_AML_SIZE_LARGE(struct aml_resource_pin_group),
ACPI_AML_SIZE_LARGE(struct aml_resource_pin_group_function),
ACPI_AML_SIZE_LARGE(struct aml_resource_pin_group_config),
+ ACPI_AML_SIZE_LARGE(struct aml_resource_clock_input),
+
};
const u8 acpi_gbl_resource_aml_serial_bus_sizes[] = {
@@ -114,6 +116,7 @@ static const u8 acpi_gbl_resource_types[] = {
ACPI_VARIABLE_LENGTH, /* 10 pin_group */
ACPI_VARIABLE_LENGTH, /* 11 pin_group_function */
ACPI_VARIABLE_LENGTH, /* 12 pin_group_config */
+ ACPI_VARIABLE_LENGTH, /* 13 clock_input */
};
/*******************************************************************************
diff --git a/drivers/acpi/acpica/utstring.c b/drivers/acpi/acpica/utstring.c
index c39b5483045d..aae71b8c55d2 100644
--- a/drivers/acpi/acpica/utstring.c
+++ b/drivers/acpi/acpica/utstring.c
@@ -145,7 +145,7 @@ void acpi_ut_repair_name(char *name)
return;
}
- ACPI_COPY_NAMESEG(&original_name, name);
+ ACPI_COPY_NAMESEG(&original_name, &name[0]);
/* Check each character in the name */
@@ -156,10 +156,10 @@ void acpi_ut_repair_name(char *name)
/*
* Replace a bad character with something printable, yet technically
- * still invalid. This prevents any collisions with existing "good"
+ * "odd". This prevents any collisions with existing "good"
* names in the namespace.
*/
- name[i] = '*';
+ name[i] = '_';
found_bad_char = TRUE;
}
@@ -169,8 +169,8 @@ void acpi_ut_repair_name(char *name)
if (!acpi_gbl_enable_interpreter_slack) {
ACPI_WARNING((AE_INFO,
- "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]",
- original_name, name));
+ "Invalid character(s) in name (0x%.8X) %p, repaired: [%4.4s]",
+ original_name, name, &name[0]));
} else {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Invalid character(s) in name (0x%.8X), repaired: [%4.4s]",
diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c
index a06988ac409d..a99c4c9e3d39 100644
--- a/drivers/acpi/acpica/uttrack.c
+++ b/drivers/acpi/acpica/uttrack.c
@@ -3,7 +3,7 @@
*
* Module Name: uttrack - Memory allocation tracking routines (debug only)
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c
index e24bc670b53e..0682554934ca 100644
--- a/drivers/acpi/acpica/utuuid.c
+++ b/drivers/acpi/acpica/utuuid.c
@@ -3,7 +3,7 @@
*
* Module Name: utuuid -- UUID support functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index 86e76b443da7..56942b5f026b 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -3,7 +3,7 @@
*
* Module Name: utxface - External interfaces, miscellaneous utility functions
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c
index f2acec3a0ee3..c1702f8fba67 100644
--- a/drivers/acpi/acpica/utxfinit.c
+++ b/drivers/acpi/acpica/utxfinit.c
@@ -3,7 +3,7 @@
*
* Module Name: utxfinit - External interfaces for ACPICA initialization
*
- * Copyright (C) 2000 - 2022, Intel Corp.
+ * Copyright (C) 2000 - 2025, Intel Corp.
*
*****************************************************************************/
@@ -120,6 +120,18 @@ acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags)
*/
acpi_gbl_early_initialization = FALSE;
+ /*
+ * Obtain a permanent mapping for the FACS. This is required for the
+ * Global Lock and the Firmware Waking Vector
+ */
+ if (!(flags & ACPI_NO_FACS_INIT)) {
+ status = acpi_tb_initialize_facs();
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO, "Could not map the FACS table"));
+ return_ACPI_STATUS(status);
+ }
+ }
+
#if (!ACPI_REDUCED_HARDWARE)
/* Enable ACPI mode */
@@ -138,18 +150,6 @@ acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags)
}
/*
- * Obtain a permanent mapping for the FACS. This is required for the
- * Global Lock and the Firmware Waking Vector
- */
- if (!(flags & ACPI_NO_FACS_INIT)) {
- status = acpi_tb_initialize_facs();
- if (ACPI_FAILURE(status)) {
- ACPI_WARNING((AE_INFO, "Could not map the FACS table"));
- return_ACPI_STATUS(status);
- }
- }
-
- /*
* Initialize ACPI Event handling (Fixed and General Purpose)
*
* Note1: We must have the hardware and events initialized before we can
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index 6b18f8bc7be3..070c07d68dfb 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -23,6 +23,7 @@ config ACPI_APEI_GHES
select ACPI_HED
select IRQ_WORK
select GENERIC_ALLOCATOR
+ select ARM_SDE_INTERFACE if ARM64
help
Generic Hardware Error Source provides a way to report
platform hardware errors (such as that from chipset). It
@@ -60,6 +61,19 @@ config ACPI_APEI_EINJ
mainly used for debugging and testing the other parts of
APEI and some other RAS features.
+config ACPI_APEI_EINJ_CXL
+ bool "CXL Error INJection Support"
+ default ACPI_APEI_EINJ
+ depends on ACPI_APEI_EINJ
+ depends on CXL_BUS && CXL_BUS <= ACPI_APEI_EINJ
+ help
+ Support for CXL protocol Error INJection through debugfs/cxl.
+ Availability and which errors are supported is dependent on
+ the host platform. Look to ACPI v6.5 section 18.6.4 and kernel
+ EINJ documentation for more information.
+
+ If unsure say 'n'
+
config ACPI_APEI_ERST_DEBUG
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
depends on ACPI_APEI
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
index 4dfac2128737..2c474e6477e1 100644
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -2,6 +2,8 @@
obj-$(CONFIG_ACPI_APEI) += apei.o
obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
+einj-y := einj-core.o
+einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
apei-y := apei-base.o hest.o erst.o bert.o
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
index 9f49272cad39..9c84f3da7c09 100644
--- a/drivers/acpi/apei/apei-base.c
+++ b/drivers/acpi/apei/apei-base.c
@@ -25,10 +25,10 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/kref.h>
-#include <linux/rculist.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>
-#include <asm/unaligned.h>
+#include <acpi/apei.h>
+#include <linux/unaligned.h>
#include "apei-internal.h"
@@ -125,12 +125,9 @@ EXPORT_SYMBOL_GPL(apei_exec_write_register);
int apei_exec_write_register_value(struct apei_exec_context *ctx,
struct acpi_whea_header *entry)
{
- int rc;
-
ctx->value = entry->value;
- rc = apei_exec_write_register(ctx, entry);
- return rc;
+ return apei_exec_write_register(ctx, entry);
}
EXPORT_SYMBOL_GPL(apei_exec_write_register_value);
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
index 1d6ef9654725..77c10a7a7a9f 100644
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -7,7 +7,6 @@
#ifndef APEI_INTERNAL_H
#define APEI_INTERNAL_H
-#include <linux/cper.h>
#include <linux/acpi.h>
struct apei_exec_context;
@@ -130,10 +129,23 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus)
return sizeof(*estatus) + estatus->data_length;
}
-void cper_estatus_print(const char *pfx,
- const struct acpi_hest_generic_status *estatus);
-int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus);
-int cper_estatus_check(const struct acpi_hest_generic_status *estatus);
-
int apei_osc_setup(void);
+
+int einj_get_available_error_type(u32 *type, int einj_action);
+int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
+ u64 param4);
+int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
+ u64 param3, u64 param4);
+bool einj_is_cxl_error_type(u64 type);
+int einj_validate_error_type(u64 type);
+
+#ifndef ACPI_EINJ_CXL_CACHE_CORRECTABLE
+#define ACPI_EINJ_CXL_CACHE_CORRECTABLE BIT(12)
+#define ACPI_EINJ_CXL_CACHE_UNCORRECTABLE BIT(13)
+#define ACPI_EINJ_CXL_CACHE_FATAL BIT(14)
+#define ACPI_EINJ_CXL_MEM_CORRECTABLE BIT(15)
+#define ACPI_EINJ_CXL_MEM_UNCORRECTABLE BIT(16)
+#define ACPI_EINJ_CXL_MEM_FATAL BIT(17)
+#endif
+
#endif
diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c
index 45973aa6e06d..5427e49e646b 100644
--- a/drivers/acpi/apei/bert.c
+++ b/drivers/acpi/apei/bert.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
+#include <linux/cper.h>
#include <linux/io.h>
#include "apei-internal.h"
@@ -33,7 +34,7 @@
#define ACPI_BERT_PRINT_MAX_RECORDS 5
#define ACPI_BERT_PRINT_MAX_LEN 1024
-static int bert_disable;
+static int bert_disable __initdata;
/*
* Print "all" the error records in the BERT table, but avoid huge spam to
@@ -90,6 +91,9 @@ static void __init bert_print_all(struct acpi_bert_region *region,
if (skipped)
pr_info(HW_ERR "Skipped %d error records\n", skipped);
+
+ if (printed + skipped)
+ pr_info("Total records found: %d\n", printed + skipped);
}
static int __init setup_bert_disable(char *str)
diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c
new file mode 100644
index 000000000000..305c240a303f
--- /dev/null
+++ b/drivers/acpi/apei/einj-core.c
@@ -0,0 +1,1201 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * APEI Error INJection support
+ *
+ * EINJ provides a hardware error injection mechanism, this is useful
+ * for debugging and testing of other APEI and RAS features.
+ *
+ * For more information about EINJ, please refer to ACPI Specification
+ * version 4.0, section 17.5.
+ *
+ * Copyright 2009-2010 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/device/faux.h>
+#include <linux/unaligned.h>
+
+#include "apei-internal.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "EINJ: " fmt
+
+#define SLEEP_UNIT_MIN 1000 /* 1ms */
+#define SLEEP_UNIT_MAX 5000 /* 5ms */
+/* Firmware should respond within 1 seconds */
+#define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC)
+#define COMPONENT_LEN 16
+#define ACPI65_EINJV2_SUPP BIT(30)
+#define ACPI5_VENDOR_BIT BIT(31)
+#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
+ ACPI_EINJ_MEMORY_UNCORRECTABLE | \
+ ACPI_EINJ_MEMORY_FATAL)
+#define CXL_ERROR_MASK (ACPI_EINJ_CXL_CACHE_CORRECTABLE | \
+ ACPI_EINJ_CXL_CACHE_UNCORRECTABLE | \
+ ACPI_EINJ_CXL_CACHE_FATAL | \
+ ACPI_EINJ_CXL_MEM_CORRECTABLE | \
+ ACPI_EINJ_CXL_MEM_UNCORRECTABLE | \
+ ACPI_EINJ_CXL_MEM_FATAL)
+
+/*
+ * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
+ */
+static int acpi5;
+
+struct syndrome_array {
+ union {
+ u8 acpi_id[COMPONENT_LEN];
+ u8 device_id[COMPONENT_LEN];
+ u8 pcie_sbdf[COMPONENT_LEN];
+ u8 vendor_id[COMPONENT_LEN];
+ } comp_id;
+ union {
+ u8 proc_synd[COMPONENT_LEN];
+ u8 mem_synd[COMPONENT_LEN];
+ u8 pcie_synd[COMPONENT_LEN];
+ u8 vendor_synd[COMPONENT_LEN];
+ } comp_synd;
+};
+
+struct einjv2_extension_struct {
+ u32 length;
+ u16 revision;
+ u16 component_arr_count;
+ struct syndrome_array component_arr[] __counted_by(component_arr_count);
+};
+
+struct set_error_type_with_address {
+ u32 type;
+ u32 vendor_extension;
+ u32 flags;
+ u32 apicid;
+ u64 memory_address;
+ u64 memory_address_range;
+ u32 pcie_sbdf;
+ struct einjv2_extension_struct einjv2_struct;
+};
+enum {
+ SETWA_FLAGS_APICID = 1,
+ SETWA_FLAGS_MEM = 2,
+ SETWA_FLAGS_PCIE_SBDF = 4,
+ SETWA_FLAGS_EINJV2 = 8,
+};
+
+/*
+ * Vendor extensions for platform specific operations
+ */
+struct vendor_error_type_extension {
+ u32 length;
+ u32 pcie_sbdf;
+ u16 vendor_id;
+ u16 device_id;
+ u8 rev_id;
+ u8 reserved[3];
+};
+
+static u32 notrigger;
+
+static u32 vendor_flags;
+static struct debugfs_blob_wrapper vendor_blob;
+static struct debugfs_blob_wrapper vendor_errors;
+static char vendor_dev[64];
+
+static u32 max_nr_components;
+static u32 available_error_type;
+static u32 available_error_type_v2;
+static struct syndrome_array *syndrome_data;
+
+/*
+ * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
+ * EINJ table through an unpublished extension. Use with caution as
+ * most will ignore the parameter and make their own choice of address
+ * for error injection. This extension is used only if
+ * param_extension module parameter is specified.
+ */
+struct einj_parameter {
+ u64 type;
+ u64 reserved1;
+ u64 reserved2;
+ u64 param1;
+ u64 param2;
+};
+
+#define EINJ_OP_BUSY 0x1
+#define EINJ_STATUS_SUCCESS 0x0
+#define EINJ_STATUS_FAIL 0x1
+#define EINJ_STATUS_INVAL 0x2
+
+#define EINJ_TAB_ENTRY(tab) \
+ ((struct acpi_whea_header *)((char *)(tab) + \
+ sizeof(struct acpi_table_einj)))
+
+static bool param_extension;
+module_param(param_extension, bool, 0);
+
+static struct acpi_table_einj *einj_tab;
+
+static struct apei_resources einj_resources;
+
+static struct apei_exec_ins_type einj_ins_type[] = {
+ [ACPI_EINJ_READ_REGISTER] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_read_register,
+ },
+ [ACPI_EINJ_READ_REGISTER_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_read_register_value,
+ },
+ [ACPI_EINJ_WRITE_REGISTER] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_write_register,
+ },
+ [ACPI_EINJ_WRITE_REGISTER_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_write_register_value,
+ },
+ [ACPI_EINJ_NOOP] = {
+ .flags = 0,
+ .run = apei_exec_noop,
+ },
+};
+
+/*
+ * Prevent EINJ interpreter to run simultaneously, because the
+ * corresponding firmware implementation may not work properly when
+ * invoked simultaneously.
+ */
+static DEFINE_MUTEX(einj_mutex);
+
+/*
+ * Exported APIs use this flag to exit early if einj_probe() failed.
+ */
+bool einj_initialized __ro_after_init;
+
+static void __iomem *einj_param;
+static u32 v5param_size;
+static u32 v66param_size;
+static bool is_v2;
+
+static void einj_exec_ctx_init(struct apei_exec_context *ctx)
+{
+ apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type),
+ EINJ_TAB_ENTRY(einj_tab), einj_tab->entries);
+}
+
+static int __einj_get_available_error_type(u32 *type, int einj_action)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ einj_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, einj_action);
+ if (rc)
+ return rc;
+ *type = apei_exec_ctx_get_output(&ctx);
+
+ return 0;
+}
+
+/* Get error injection capabilities of the platform */
+int einj_get_available_error_type(u32 *type, int einj_action)
+{
+ int rc;
+
+ mutex_lock(&einj_mutex);
+ rc = __einj_get_available_error_type(type, einj_action);
+ mutex_unlock(&einj_mutex);
+
+ return rc;
+}
+
+static int einj_get_available_error_types(u32 *type1, u32 *type2)
+{
+ int rc;
+
+ rc = einj_get_available_error_type(type1, ACPI_EINJ_GET_ERROR_TYPE);
+ if (rc)
+ return rc;
+ if (*type1 & ACPI65_EINJV2_SUPP) {
+ rc = einj_get_available_error_type(type2,
+ ACPI_EINJV2_GET_ERROR_TYPE);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int einj_timedout(u64 *t)
+{
+ if ((s64)*t < SLEEP_UNIT_MIN) {
+ pr_warn(FW_WARN "Firmware does not respond in time\n");
+ return 1;
+ }
+ *t -= SLEEP_UNIT_MIN;
+ usleep_range(SLEEP_UNIT_MIN, SLEEP_UNIT_MAX);
+
+ return 0;
+}
+
+static void get_oem_vendor_struct(u64 paddr, int offset,
+ struct vendor_error_type_extension *v)
+{
+ unsigned long vendor_size;
+ u64 target_pa = paddr + offset + sizeof(struct vendor_error_type_extension);
+
+ vendor_size = v->length - sizeof(struct vendor_error_type_extension);
+
+ if (vendor_size)
+ vendor_errors.data = acpi_os_map_memory(target_pa, vendor_size);
+
+ if (vendor_errors.data)
+ vendor_errors.size = vendor_size;
+}
+
+static void check_vendor_extension(u64 paddr,
+ struct set_error_type_with_address *v5param)
+{
+ int offset = v5param->vendor_extension;
+ struct vendor_error_type_extension v;
+ struct vendor_error_type_extension __iomem *p;
+ u32 sbdf;
+
+ if (!offset)
+ return;
+ p = acpi_os_map_iomem(paddr + offset, sizeof(*p));
+ if (!p)
+ return;
+ memcpy_fromio(&v, p, sizeof(v));
+ get_oem_vendor_struct(paddr, offset, &v);
+ sbdf = v.pcie_sbdf;
+ sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
+ sbdf >> 24, (sbdf >> 16) & 0xff,
+ (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
+ v.vendor_id, v.device_id, v.rev_id);
+ acpi_os_unmap_iomem(p, sizeof(v));
+}
+
+static u32 einjv2_init(struct einjv2_extension_struct *e)
+{
+ if (e->revision != 1) {
+ pr_info("Unknown v2 extension revision %u\n", e->revision);
+ return 0;
+ }
+ if (e->length < sizeof(*e) || e->length > PAGE_SIZE) {
+ pr_info(FW_BUG "Bad1 v2 extension length %u\n", e->length);
+ return 0;
+ }
+ if ((e->length - sizeof(*e)) % sizeof(e->component_arr[0])) {
+ pr_info(FW_BUG "Bad2 v2 extension length %u\n", e->length);
+ return 0;
+ }
+
+ return (e->length - sizeof(*e)) / sizeof(e->component_arr[0]);
+}
+
+static void __iomem *einj_get_parameter_address(void)
+{
+ int i;
+ u64 pa_v4 = 0, pa_v5 = 0;
+ struct acpi_whea_header *entry;
+
+ entry = EINJ_TAB_ENTRY(einj_tab);
+ for (i = 0; i < einj_tab->entries; i++) {
+ if (entry->action == ACPI_EINJ_SET_ERROR_TYPE &&
+ entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
+ entry->register_region.space_id ==
+ ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ pa_v4 = get_unaligned(&entry->register_region.address);
+ if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
+ entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
+ entry->register_region.space_id ==
+ ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ pa_v5 = get_unaligned(&entry->register_region.address);
+ entry++;
+ }
+ if (pa_v5) {
+ struct set_error_type_with_address v5param;
+ struct set_error_type_with_address __iomem *p;
+
+ v5param_size = sizeof(v5param);
+ p = acpi_os_map_iomem(pa_v5, sizeof(*p));
+ if (p) {
+ memcpy_fromio(&v5param, p, v5param_size);
+ acpi5 = 1;
+ check_vendor_extension(pa_v5, &v5param);
+ if (available_error_type & ACPI65_EINJV2_SUPP) {
+ struct einjv2_extension_struct *e;
+
+ e = &v5param.einjv2_struct;
+ max_nr_components = einjv2_init(e);
+
+ /* remap including einjv2_extension_struct */
+ acpi_os_unmap_iomem(p, v5param_size);
+ v66param_size = v5param_size - sizeof(*e) + e->length;
+ p = acpi_os_map_iomem(pa_v5, v66param_size);
+ }
+
+ return p;
+ }
+ }
+ if (param_extension && pa_v4) {
+ struct einj_parameter v4param;
+ struct einj_parameter __iomem *p;
+
+ p = acpi_os_map_iomem(pa_v4, sizeof(*p));
+ if (!p)
+ return NULL;
+ memcpy_fromio(&v4param, p, sizeof(v4param));
+ if (v4param.reserved1 || v4param.reserved2) {
+ acpi_os_unmap_iomem(p, sizeof(v4param));
+ return NULL;
+ }
+ return p;
+ }
+
+ return NULL;
+}
+
+/* do sanity check to trigger table */
+static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
+{
+ if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger))
+ return -EINVAL;
+ if (trigger_tab->table_size > PAGE_SIZE ||
+ trigger_tab->table_size < trigger_tab->header_size)
+ return -EINVAL;
+ if (trigger_tab->entry_count !=
+ (trigger_tab->table_size - trigger_tab->header_size) /
+ sizeof(struct acpi_einj_entry))
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct acpi_generic_address *einj_get_trigger_parameter_region(
+ struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2)
+{
+ int i;
+ struct acpi_whea_header *entry;
+
+ entry = (struct acpi_whea_header *)
+ ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
+ for (i = 0; i < trigger_tab->entry_count; i++) {
+ if (entry->action == ACPI_EINJ_TRIGGER_ERROR &&
+ entry->instruction <= ACPI_EINJ_WRITE_REGISTER_VALUE &&
+ entry->register_region.space_id ==
+ ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+ (entry->register_region.address & param2) == (param1 & param2))
+ return &entry->register_region;
+ entry++;
+ }
+
+ return NULL;
+}
+/* Execute instructions in trigger error action table */
+static int __einj_error_trigger(u64 trigger_paddr, u32 type,
+ u64 param1, u64 param2)
+{
+ struct acpi_einj_trigger trigger_tab;
+ struct acpi_einj_trigger *full_trigger_tab;
+ struct apei_exec_context trigger_ctx;
+ struct apei_resources trigger_resources;
+ struct acpi_whea_header *trigger_entry;
+ struct resource *r;
+ u32 table_size;
+ int rc = -EIO;
+ struct acpi_generic_address *trigger_param_region = NULL;
+ struct acpi_einj_trigger __iomem *p = NULL;
+
+ r = request_mem_region(trigger_paddr, sizeof(trigger_tab),
+ "APEI EINJ Trigger Table");
+ if (!r) {
+ pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n",
+ (unsigned long long)trigger_paddr,
+ (unsigned long long)trigger_paddr +
+ sizeof(trigger_tab) - 1);
+ goto out;
+ }
+ p = ioremap_cache(trigger_paddr, sizeof(*p));
+ if (!p) {
+ pr_err("Failed to map trigger table!\n");
+ goto out_rel_header;
+ }
+ memcpy_fromio(&trigger_tab, p, sizeof(trigger_tab));
+ rc = einj_check_trigger_header(&trigger_tab);
+ if (rc) {
+ pr_warn(FW_BUG "Invalid trigger error action table.\n");
+ goto out_rel_header;
+ }
+
+ /* No action structures in the TRIGGER_ERROR table, nothing to do */
+ if (!trigger_tab.entry_count)
+ goto out_rel_header;
+
+ rc = -EIO;
+ table_size = trigger_tab.table_size;
+ full_trigger_tab = kmalloc(table_size, GFP_KERNEL);
+ if (!full_trigger_tab)
+ goto out_rel_header;
+ r = request_mem_region(trigger_paddr + sizeof(trigger_tab),
+ table_size - sizeof(trigger_tab),
+ "APEI EINJ Trigger Table");
+ if (!r) {
+ pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
+ (unsigned long long)trigger_paddr + sizeof(trigger_tab),
+ (unsigned long long)trigger_paddr + table_size - 1);
+ goto out_free_trigger_tab;
+ }
+ iounmap(p);
+ p = ioremap_cache(trigger_paddr, table_size);
+ if (!p) {
+ pr_err("Failed to map trigger table!\n");
+ goto out_rel_entry;
+ }
+ memcpy_fromio(full_trigger_tab, p, table_size);
+ trigger_entry = (struct acpi_whea_header *)
+ ((char *)full_trigger_tab + sizeof(struct acpi_einj_trigger));
+ apei_resources_init(&trigger_resources);
+ apei_exec_ctx_init(&trigger_ctx, einj_ins_type,
+ ARRAY_SIZE(einj_ins_type),
+ trigger_entry, trigger_tab.entry_count);
+ rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources);
+ if (rc)
+ goto out_fini;
+ rc = apei_resources_sub(&trigger_resources, &einj_resources);
+ if (rc)
+ goto out_fini;
+ /*
+ * Some firmware will access target address specified in
+ * param1 to trigger the error when injecting memory error.
+ * This will cause resource conflict with regular memory. So
+ * remove it from trigger table resources.
+ */
+ if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
+ struct apei_resources addr_resources;
+
+ apei_resources_init(&addr_resources);
+ trigger_param_region = einj_get_trigger_parameter_region(
+ full_trigger_tab, param1, param2);
+ if (trigger_param_region) {
+ rc = apei_resources_add(&addr_resources,
+ trigger_param_region->address,
+ trigger_param_region->bit_width/8, true);
+ if (rc)
+ goto out_fini;
+ rc = apei_resources_sub(&trigger_resources,
+ &addr_resources);
+ }
+ apei_resources_fini(&addr_resources);
+ if (rc)
+ goto out_fini;
+ }
+ rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
+ if (rc)
+ goto out_fini;
+ rc = apei_exec_pre_map_gars(&trigger_ctx);
+ if (rc)
+ goto out_release;
+
+ rc = apei_exec_run(&trigger_ctx, ACPI_EINJ_TRIGGER_ERROR);
+
+ apei_exec_post_unmap_gars(&trigger_ctx);
+out_release:
+ apei_resources_release(&trigger_resources);
+out_fini:
+ apei_resources_fini(&trigger_resources);
+out_rel_entry:
+ release_mem_region(trigger_paddr + sizeof(trigger_tab),
+ table_size - sizeof(trigger_tab));
+out_free_trigger_tab:
+ kfree(full_trigger_tab);
+out_rel_header:
+ release_mem_region(trigger_paddr, sizeof(trigger_tab));
+out:
+ if (p)
+ iounmap(p);
+
+ return rc;
+}
+
+static bool is_end_of_list(u8 *val)
+{
+ for (int i = 0; i < COMPONENT_LEN; ++i) {
+ if (val[i] != 0xFF)
+ return false;
+ }
+ return true;
+}
+static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
+ u64 param3, u64 param4)
+{
+ struct apei_exec_context ctx;
+ u32 param_size = is_v2 ? v66param_size : v5param_size;
+ u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
+ int i, rc;
+
+ einj_exec_ctx_init(&ctx);
+
+ rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, type);
+ if (acpi5) {
+ struct set_error_type_with_address *v5param;
+
+ v5param = kmalloc(param_size, GFP_KERNEL);
+ if (!v5param)
+ return -ENOMEM;
+
+ memcpy_fromio(v5param, einj_param, param_size);
+ v5param->type = type;
+ if (type & ACPI5_VENDOR_BIT) {
+ switch (vendor_flags) {
+ case SETWA_FLAGS_APICID:
+ v5param->apicid = param1;
+ break;
+ case SETWA_FLAGS_MEM:
+ v5param->memory_address = param1;
+ v5param->memory_address_range = param2;
+ break;
+ case SETWA_FLAGS_PCIE_SBDF:
+ v5param->pcie_sbdf = param1;
+ break;
+ }
+ v5param->flags = vendor_flags;
+ } else if (flags) {
+ v5param->flags = flags;
+ v5param->memory_address = param1;
+ v5param->memory_address_range = param2;
+
+ if (is_v2) {
+ for (i = 0; i < max_nr_components; i++) {
+ if (is_end_of_list(syndrome_data[i].comp_id.acpi_id))
+ break;
+ v5param->einjv2_struct.component_arr[i].comp_id =
+ syndrome_data[i].comp_id;
+ v5param->einjv2_struct.component_arr[i].comp_synd =
+ syndrome_data[i].comp_synd;
+ }
+ v5param->einjv2_struct.component_arr_count = i;
+ } else {
+ v5param->apicid = param3;
+ v5param->pcie_sbdf = param4;
+ }
+ } else {
+ switch (type) {
+ case ACPI_EINJ_PROCESSOR_CORRECTABLE:
+ case ACPI_EINJ_PROCESSOR_UNCORRECTABLE:
+ case ACPI_EINJ_PROCESSOR_FATAL:
+ v5param->apicid = param1;
+ v5param->flags = SETWA_FLAGS_APICID;
+ break;
+ case ACPI_EINJ_MEMORY_CORRECTABLE:
+ case ACPI_EINJ_MEMORY_UNCORRECTABLE:
+ case ACPI_EINJ_MEMORY_FATAL:
+ v5param->memory_address = param1;
+ v5param->memory_address_range = param2;
+ v5param->flags = SETWA_FLAGS_MEM;
+ break;
+ case ACPI_EINJ_PCIX_CORRECTABLE:
+ case ACPI_EINJ_PCIX_UNCORRECTABLE:
+ case ACPI_EINJ_PCIX_FATAL:
+ v5param->pcie_sbdf = param1;
+ v5param->flags = SETWA_FLAGS_PCIE_SBDF;
+ break;
+ }
+ }
+ memcpy_toio(einj_param, v5param, param_size);
+ kfree(v5param);
+ } else {
+ rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
+ if (rc)
+ return rc;
+ if (einj_param) {
+ struct einj_parameter v4param;
+
+ memcpy_fromio(&v4param, einj_param, sizeof(v4param));
+ v4param.param1 = param1;
+ v4param.param2 = param2;
+ memcpy_toio(einj_param, &v4param, sizeof(v4param));
+ }
+ }
+ rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!(val & EINJ_OP_BUSY))
+ break;
+ if (einj_timedout(&timeout))
+ return -EIO;
+ }
+ rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (val == EINJ_STATUS_FAIL)
+ return -EBUSY;
+ else if (val == EINJ_STATUS_INVAL)
+ return -EINVAL;
+
+ /*
+ * The error is injected into the platform successfully, then it needs
+ * to trigger the error.
+ */
+ rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE);
+ if (rc)
+ return rc;
+ trigger_paddr = apei_exec_ctx_get_output(&ctx);
+ if (notrigger == 0) {
+ rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
+ if (rc)
+ return rc;
+ }
+ rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
+
+ return rc;
+}
+
+/* Allow almost all types of address except MMIO. */
+static bool is_allowed_range(u64 base_addr, u64 size)
+{
+ int i;
+ /*
+ * MMIO region is usually claimed with IORESOURCE_MEM + IORES_DESC_NONE.
+ * However, IORES_DESC_NONE is treated like a wildcard when we check if
+ * region intersects with known resource. So do an allow list check for
+ * IORES_DESCs that definitely or most likely not MMIO.
+ */
+ int non_mmio_desc[] = {
+ IORES_DESC_CRASH_KERNEL,
+ IORES_DESC_ACPI_TABLES,
+ IORES_DESC_ACPI_NV_STORAGE,
+ IORES_DESC_PERSISTENT_MEMORY,
+ IORES_DESC_PERSISTENT_MEMORY_LEGACY,
+ /* Treat IORES_DESC_DEVICE_PRIVATE_MEMORY as MMIO. */
+ IORES_DESC_RESERVED,
+ IORES_DESC_SOFT_RESERVED,
+ };
+
+ if (region_intersects(base_addr, size, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)
+ == REGION_INTERSECTS)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(non_mmio_desc); ++i) {
+ if (region_intersects(base_addr, size, IORESOURCE_MEM, non_mmio_desc[i])
+ == REGION_INTERSECTS)
+ return true;
+ }
+
+ if (arch_is_platform_page(base_addr))
+ return true;
+
+ return false;
+}
+
+/* Inject the specified hardware error */
+int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
+ u64 param4)
+{
+ int rc;
+ u64 base_addr, size;
+
+ /* If user manually set "flags", make sure it is legal */
+ if (flags && (flags & ~(SETWA_FLAGS_APICID | SETWA_FLAGS_MEM |
+ SETWA_FLAGS_PCIE_SBDF | SETWA_FLAGS_EINJV2)))
+ return -EINVAL;
+
+ /* check if type is a valid EINJv2 error type */
+ if (is_v2) {
+ if (!(type & available_error_type_v2))
+ return -EINVAL;
+ }
+ /*
+ * We need extra sanity checks for memory errors.
+ * Other types leap directly to injection.
+ */
+
+ /* ensure param1/param2 existed */
+ if (!(param_extension || acpi5))
+ goto inject;
+
+ /* ensure injection is memory related */
+ if (type & ACPI5_VENDOR_BIT) {
+ if (vendor_flags != SETWA_FLAGS_MEM)
+ goto inject;
+ } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) {
+ goto inject;
+ }
+
+ /*
+ * Injections targeting a CXL 1.0/1.1 port have to be injected
+ * via the einj_cxl_rch_error_inject() path as that does the proper
+ * validation of the given RCRB base (MMIO) address.
+ */
+ if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM))
+ return -EINVAL;
+
+ /*
+ * Disallow crazy address masks that give BIOS leeway to pick
+ * injection address almost anywhere. Insist on page or
+ * better granularity and that target address is normal RAM or
+ * as long as is not MMIO.
+ */
+ base_addr = param1 & param2;
+ size = ~param2 + 1;
+
+ if ((param2 & PAGE_MASK) != PAGE_MASK)
+ return -EINVAL;
+
+ if (!is_allowed_range(base_addr, size))
+ return -EINVAL;
+
+ if (is_zero_pfn(base_addr >> PAGE_SHIFT))
+ return -EADDRINUSE;
+
+inject:
+ mutex_lock(&einj_mutex);
+ rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
+ mutex_unlock(&einj_mutex);
+
+ return rc;
+}
+
+int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
+ u64 param3, u64 param4)
+{
+ int rc;
+
+ if (!(einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)))
+ return -EINVAL;
+
+ mutex_lock(&einj_mutex);
+ rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
+ mutex_unlock(&einj_mutex);
+
+ return rc;
+}
+
+static u32 error_type;
+static u32 error_flags;
+static u64 error_param1;
+static u64 error_param2;
+static u64 error_param3;
+static u64 error_param4;
+static struct dentry *einj_debug_dir;
+static char einj_buf[32];
+static bool einj_v2_enabled;
+static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
+ { BIT(0), "Processor Correctable" },
+ { BIT(1), "Processor Uncorrectable non-fatal" },
+ { BIT(2), "Processor Uncorrectable fatal" },
+ { BIT(3), "Memory Correctable" },
+ { BIT(4), "Memory Uncorrectable non-fatal" },
+ { BIT(5), "Memory Uncorrectable fatal" },
+ { BIT(6), "PCI Express Correctable" },
+ { BIT(7), "PCI Express Uncorrectable non-fatal" },
+ { BIT(8), "PCI Express Uncorrectable fatal" },
+ { BIT(9), "Platform Correctable" },
+ { BIT(10), "Platform Uncorrectable non-fatal" },
+ { BIT(11), "Platform Uncorrectable fatal"},
+ { BIT(31), "Vendor Defined Error Types" },
+};
+
+static struct { u32 mask; const char *str; } const einjv2_error_type_string[] = {
+ { BIT(0), "EINJV2 Processor Error" },
+ { BIT(1), "EINJV2 Memory Error" },
+ { BIT(2), "EINJV2 PCI Express Error" },
+};
+
+static int available_error_type_show(struct seq_file *m, void *v)
+{
+
+ for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
+ if (available_error_type & einj_error_type_string[pos].mask)
+ seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask,
+ einj_error_type_string[pos].str);
+ if ((available_error_type & ACPI65_EINJV2_SUPP) && einj_v2_enabled) {
+ for (int pos = 0; pos < ARRAY_SIZE(einjv2_error_type_string); pos++) {
+ if (available_error_type_v2 & einjv2_error_type_string[pos].mask)
+ seq_printf(m, "V2_0x%08x\t%s\n", einjv2_error_type_string[pos].mask,
+ einjv2_error_type_string[pos].str);
+ }
+ }
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(available_error_type);
+
+static ssize_t error_type_get(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(buf, count, ppos, einj_buf, strlen(einj_buf));
+}
+
+bool einj_is_cxl_error_type(u64 type)
+{
+ return (type & CXL_ERROR_MASK) && (!(type & ACPI5_VENDOR_BIT));
+}
+
+int einj_validate_error_type(u64 type)
+{
+ u32 tval, vendor;
+
+ /* Only low 32 bits for error type are valid */
+ if (type & GENMASK_ULL(63, 32))
+ return -EINVAL;
+
+ /*
+ * Vendor defined types have 0x80000000 bit set, and
+ * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
+ */
+ vendor = type & ACPI5_VENDOR_BIT;
+ tval = type & GENMASK(30, 0);
+
+ /* Only one error type can be specified */
+ if (tval & (tval - 1))
+ return -EINVAL;
+ if (!vendor)
+ if (!(type & (available_error_type | available_error_type_v2)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t error_type_set(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc;
+ u64 val;
+
+ /* Leave the last character for the NUL terminator */
+ if (count > sizeof(einj_buf) - 1)
+ return -EINVAL;
+
+ memset(einj_buf, 0, sizeof(einj_buf));
+ if (copy_from_user(einj_buf, buf, count))
+ return -EFAULT;
+
+ if (strncmp(einj_buf, "V2_", 3) == 0) {
+ if (!sscanf(einj_buf, "V2_%llx", &val))
+ return -EINVAL;
+ is_v2 = true;
+ } else {
+ if (!sscanf(einj_buf, "%llx", &val))
+ return -EINVAL;
+ is_v2 = false;
+ }
+
+ rc = einj_validate_error_type(val);
+ if (rc)
+ return rc;
+
+ error_type = val;
+
+ return count;
+}
+
+static const struct file_operations error_type_fops = {
+ .read = error_type_get,
+ .write = error_type_set,
+};
+
+static int error_inject_set(void *data, u64 val)
+{
+ if (!error_type)
+ return -EINVAL;
+
+ if (is_v2)
+ error_flags |= SETWA_FLAGS_EINJV2;
+ else
+ error_flags &= ~SETWA_FLAGS_EINJV2;
+
+ return einj_error_inject(error_type, error_flags, error_param1, error_param2,
+ error_param3, error_param4);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n");
+
+static int einj_check_table(struct acpi_table_einj *einj_tab)
+{
+ if ((einj_tab->header_length !=
+ (sizeof(struct acpi_table_einj) - sizeof(einj_tab->header)))
+ && (einj_tab->header_length != sizeof(struct acpi_table_einj)))
+ return -EINVAL;
+ if (einj_tab->header.length < sizeof(struct acpi_table_einj))
+ return -EINVAL;
+ if (einj_tab->entries !=
+ (einj_tab->header.length - sizeof(struct acpi_table_einj)) /
+ sizeof(struct acpi_einj_entry))
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t u128_read(struct file *f, char __user *buf, size_t count, loff_t *off)
+{
+ char output[2 * COMPONENT_LEN + 1];
+ u8 *data = f->f_inode->i_private;
+ int i;
+
+ if (*off >= sizeof(output))
+ return 0;
+
+ for (i = 0; i < COMPONENT_LEN; i++)
+ sprintf(output + 2 * i, "%.02x", data[COMPONENT_LEN - i - 1]);
+ output[2 * COMPONENT_LEN] = '\n';
+
+ return simple_read_from_buffer(buf, count, off, output, sizeof(output));
+}
+
+static ssize_t u128_write(struct file *f, const char __user *buf, size_t count, loff_t *off)
+{
+ char input[2 + 2 * COMPONENT_LEN + 2];
+ u8 *save = f->f_inode->i_private;
+ u8 tmp[COMPONENT_LEN];
+ char byte[3] = {};
+ char *s, *e;
+ ssize_t c;
+ long val;
+ int i;
+
+ /* Require that user supply whole input line in one write(2) syscall */
+ if (*off)
+ return -EINVAL;
+
+ c = simple_write_to_buffer(input, sizeof(input), off, buf, count);
+ if (c < 0)
+ return c;
+
+ if (c < 1 || input[c - 1] != '\n')
+ return -EINVAL;
+
+ /* Empty line means invalidate this entry */
+ if (c == 1) {
+ memset(save, 0xff, COMPONENT_LEN);
+ return c;
+ }
+
+ if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X'))
+ s = input + 2;
+ else
+ s = input;
+ e = input + c - 1;
+
+ for (i = 0; i < COMPONENT_LEN; i++) {
+ byte[1] = *--e;
+ byte[0] = e > s ? *--e : '0';
+ if (kstrtol(byte, 16, &val))
+ return -EINVAL;
+ tmp[i] = val;
+ if (e <= s)
+ break;
+ }
+ while (++i < COMPONENT_LEN)
+ tmp[i] = 0;
+
+ memcpy(save, tmp, COMPONENT_LEN);
+
+ return c;
+}
+
+static const struct file_operations u128_fops = {
+ .read = u128_read,
+ .write = u128_write,
+};
+
+static bool setup_einjv2_component_files(void)
+{
+ char name[32];
+
+ syndrome_data = kcalloc(max_nr_components, sizeof(syndrome_data[0]), GFP_KERNEL);
+ if (!syndrome_data)
+ return false;
+
+ for (int i = 0; i < max_nr_components; i++) {
+ sprintf(name, "component_id%d", i);
+ debugfs_create_file(name, 0600, einj_debug_dir,
+ &syndrome_data[i].comp_id, &u128_fops);
+ sprintf(name, "component_syndrome%d", i);
+ debugfs_create_file(name, 0600, einj_debug_dir,
+ &syndrome_data[i].comp_synd, &u128_fops);
+ }
+
+ return true;
+}
+
+static int __init einj_probe(struct faux_device *fdev)
+{
+ int rc;
+ acpi_status status;
+ struct apei_exec_context ctx;
+
+ status = acpi_get_table(ACPI_SIG_EINJ, 0,
+ (struct acpi_table_header **)&einj_tab);
+ if (status == AE_NOT_FOUND) {
+ pr_debug("EINJ table not found.\n");
+ return -ENODEV;
+ } else if (ACPI_FAILURE(status)) {
+ pr_err("Failed to get EINJ table: %s\n",
+ acpi_format_exception(status));
+ return -EINVAL;
+ }
+
+ rc = einj_check_table(einj_tab);
+ if (rc) {
+ pr_warn(FW_BUG "Invalid EINJ table.\n");
+ goto err_put_table;
+ }
+
+ rc = einj_get_available_error_types(&available_error_type, &available_error_type_v2);
+ if (rc)
+ goto err_put_table;
+
+ rc = -ENOMEM;
+ einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir());
+
+ debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir,
+ NULL, &available_error_type_fops);
+ debugfs_create_file_unsafe("error_type", 0600, einj_debug_dir,
+ NULL, &error_type_fops);
+ debugfs_create_file_unsafe("error_inject", 0200, einj_debug_dir,
+ NULL, &error_inject_fops);
+
+ apei_resources_init(&einj_resources);
+ einj_exec_ctx_init(&ctx);
+ rc = apei_exec_collect_resources(&ctx, &einj_resources);
+ if (rc) {
+ pr_err("Error collecting EINJ resources.\n");
+ goto err_fini;
+ }
+
+ rc = apei_resources_request(&einj_resources, "APEI EINJ");
+ if (rc) {
+ pr_err("Error requesting memory/port resources.\n");
+ goto err_fini;
+ }
+
+ rc = apei_exec_pre_map_gars(&ctx);
+ if (rc) {
+ pr_err("Error pre-mapping GARs.\n");
+ goto err_release;
+ }
+
+ einj_param = einj_get_parameter_address();
+ if ((param_extension || acpi5) && einj_param) {
+ debugfs_create_x32("flags", S_IRUSR | S_IWUSR, einj_debug_dir,
+ &error_flags);
+ debugfs_create_x64("param1", S_IRUSR | S_IWUSR, einj_debug_dir,
+ &error_param1);
+ debugfs_create_x64("param2", S_IRUSR | S_IWUSR, einj_debug_dir,
+ &error_param2);
+ debugfs_create_x64("param3", S_IRUSR | S_IWUSR, einj_debug_dir,
+ &error_param3);
+ debugfs_create_x64("param4", S_IRUSR | S_IWUSR, einj_debug_dir,
+ &error_param4);
+ debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &notrigger);
+ if (available_error_type & ACPI65_EINJV2_SUPP)
+ einj_v2_enabled = setup_einjv2_component_files();
+ }
+
+ if (vendor_dev[0]) {
+ vendor_blob.data = vendor_dev;
+ vendor_blob.size = strlen(vendor_dev);
+ debugfs_create_blob("vendor", S_IRUSR, einj_debug_dir,
+ &vendor_blob);
+ debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &vendor_flags);
+ }
+
+ if (vendor_errors.size)
+ debugfs_create_blob("oem_error", 0600, einj_debug_dir,
+ &vendor_errors);
+
+ pr_info("Error INJection is initialized.\n");
+
+ return 0;
+
+err_release:
+ apei_resources_release(&einj_resources);
+err_fini:
+ apei_resources_fini(&einj_resources);
+ debugfs_remove_recursive(einj_debug_dir);
+err_put_table:
+ acpi_put_table((struct acpi_table_header *)einj_tab);
+
+ return rc;
+}
+
+static void einj_remove(struct faux_device *fdev)
+{
+ struct apei_exec_context ctx;
+
+ if (einj_param) {
+ acpi_size size;
+
+ if (v66param_size)
+ size = v66param_size;
+ else if (acpi5)
+ size = v5param_size;
+ else
+ size = sizeof(struct einj_parameter);
+
+ acpi_os_unmap_iomem(einj_param, size);
+ if (vendor_errors.size)
+ acpi_os_unmap_memory(vendor_errors.data, vendor_errors.size);
+ }
+ einj_exec_ctx_init(&ctx);
+ apei_exec_post_unmap_gars(&ctx);
+ apei_resources_release(&einj_resources);
+ apei_resources_fini(&einj_resources);
+ debugfs_remove_recursive(einj_debug_dir);
+ kfree(syndrome_data);
+ acpi_put_table((struct acpi_table_header *)einj_tab);
+}
+
+static struct faux_device *einj_dev;
+static struct faux_device_ops einj_device_ops = {
+ .probe = einj_probe,
+ .remove = einj_remove,
+};
+
+static int __init einj_init(void)
+{
+ if (acpi_disabled) {
+ pr_debug("ACPI disabled.\n");
+ return -ENODEV;
+ }
+
+ einj_dev = faux_device_create("acpi-einj", NULL, &einj_device_ops);
+
+ if (einj_dev)
+ einj_initialized = true;
+
+ return 0;
+}
+
+static void __exit einj_exit(void)
+{
+ faux_device_destroy(einj_dev);
+}
+
+module_init(einj_init);
+module_exit(einj_exit);
+
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("APEI Error INJection support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/apei/einj-cxl.c b/drivers/acpi/apei/einj-cxl.c
new file mode 100644
index 000000000000..e70a416ec925
--- /dev/null
+++ b/drivers/acpi/apei/einj-cxl.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CXL Error INJection support. Used by CXL core to inject
+ * protocol errors into CXL ports.
+ *
+ * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ *
+ * Author: Ben Cheatham <benjamin.cheatham@amd.com>
+ */
+#include <linux/seq_file.h>
+#include <linux/pci.h>
+#include <cxl/einj.h>
+
+#include "apei-internal.h"
+
+/* Defined in einj-core.c */
+extern bool einj_initialized;
+
+static struct { u32 mask; const char *str; } const einj_cxl_error_type_string[] = {
+ { ACPI_EINJ_CXL_CACHE_CORRECTABLE, "CXL.cache Protocol Correctable" },
+ { ACPI_EINJ_CXL_CACHE_UNCORRECTABLE, "CXL.cache Protocol Uncorrectable non-fatal" },
+ { ACPI_EINJ_CXL_CACHE_FATAL, "CXL.cache Protocol Uncorrectable fatal" },
+ { ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Protocol Correctable" },
+ { ACPI_EINJ_CXL_MEM_UNCORRECTABLE, "CXL.mem Protocol Uncorrectable non-fatal" },
+ { ACPI_EINJ_CXL_MEM_FATAL, "CXL.mem Protocol Uncorrectable fatal" },
+};
+
+int einj_cxl_available_error_type_show(struct seq_file *m, void *v)
+{
+ int cxl_err, rc;
+ u32 available_error_type = 0;
+
+ rc = einj_get_available_error_type(&available_error_type, ACPI_EINJ_GET_ERROR_TYPE);
+ if (rc)
+ return rc;
+
+ for (int pos = 0; pos < ARRAY_SIZE(einj_cxl_error_type_string); pos++) {
+ cxl_err = ACPI_EINJ_CXL_CACHE_CORRECTABLE << pos;
+
+ if (available_error_type & cxl_err)
+ seq_printf(m, "0x%08x\t%s\n",
+ einj_cxl_error_type_string[pos].mask,
+ einj_cxl_error_type_string[pos].str);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_available_error_type_show, "CXL");
+
+static int cxl_dport_get_sbdf(struct pci_dev *dport_dev, u64 *sbdf)
+{
+ struct pci_bus *pbus;
+ struct pci_host_bridge *bridge;
+ u64 seg = 0, bus;
+
+ pbus = dport_dev->bus;
+ bridge = pci_find_host_bridge(pbus);
+
+ if (!bridge)
+ return -ENODEV;
+
+ if (bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET)
+ seg = bridge->domain_nr;
+
+ bus = pbus->number;
+ *sbdf = (seg << 24) | (bus << 16) | (dport_dev->devfn << 8);
+
+ return 0;
+}
+
+int einj_cxl_inject_rch_error(u64 rcrb, u64 type)
+{
+ int rc;
+
+ /* Only CXL error types can be specified */
+ if (!einj_is_cxl_error_type(type))
+ return -EINVAL;
+
+ rc = einj_validate_error_type(type);
+ if (rc)
+ return rc;
+
+ return einj_cxl_rch_error_inject(type, 0x2, rcrb, GENMASK_ULL(63, 0),
+ 0, 0);
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_rch_error, "CXL");
+
+int einj_cxl_inject_error(struct pci_dev *dport, u64 type)
+{
+ u64 param4 = 0;
+ int rc;
+
+ /* Only CXL error types can be specified */
+ if (!einj_is_cxl_error_type(type))
+ return -EINVAL;
+
+ rc = einj_validate_error_type(type);
+ if (rc)
+ return rc;
+
+ rc = cxl_dport_get_sbdf(dport, &param4);
+ if (rc)
+ return rc;
+
+ return einj_error_inject(type, 0x4, 0, 0, 0, param4);
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_error, "CXL");
+
+bool einj_cxl_is_initialized(void)
+{
+ return einj_initialized;
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_is_initialized, "CXL");
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
deleted file mode 100644
index 6b583373c58a..000000000000
--- a/drivers/acpi/apei/einj.c
+++ /dev/null
@@ -1,799 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * APEI Error INJection support
- *
- * EINJ provides a hardware error injection mechanism, this is useful
- * for debugging and testing of other APEI and RAS features.
- *
- * For more information about EINJ, please refer to ACPI Specification
- * version 4.0, section 17.5.
- *
- * Copyright 2009-2010 Intel Corp.
- * Author: Huang Ying <ying.huang@intel.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/nmi.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <asm/unaligned.h>
-
-#include "apei-internal.h"
-
-#undef pr_fmt
-#define pr_fmt(fmt) "EINJ: " fmt
-
-#define SLEEP_UNIT_MIN 1000 /* 1ms */
-#define SLEEP_UNIT_MAX 5000 /* 5ms */
-/* Firmware should respond within 1 seconds */
-#define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC)
-#define ACPI5_VENDOR_BIT BIT(31)
-#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
- ACPI_EINJ_MEMORY_UNCORRECTABLE | \
- ACPI_EINJ_MEMORY_FATAL)
-
-/*
- * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
- */
-static int acpi5;
-
-struct set_error_type_with_address {
- u32 type;
- u32 vendor_extension;
- u32 flags;
- u32 apicid;
- u64 memory_address;
- u64 memory_address_range;
- u32 pcie_sbdf;
-};
-enum {
- SETWA_FLAGS_APICID = 1,
- SETWA_FLAGS_MEM = 2,
- SETWA_FLAGS_PCIE_SBDF = 4,
-};
-
-/*
- * Vendor extensions for platform specific operations
- */
-struct vendor_error_type_extension {
- u32 length;
- u32 pcie_sbdf;
- u16 vendor_id;
- u16 device_id;
- u8 rev_id;
- u8 reserved[3];
-};
-
-static u32 notrigger;
-
-static u32 vendor_flags;
-static struct debugfs_blob_wrapper vendor_blob;
-static char vendor_dev[64];
-
-/*
- * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
- * EINJ table through an unpublished extension. Use with caution as
- * most will ignore the parameter and make their own choice of address
- * for error injection. This extension is used only if
- * param_extension module parameter is specified.
- */
-struct einj_parameter {
- u64 type;
- u64 reserved1;
- u64 reserved2;
- u64 param1;
- u64 param2;
-};
-
-#define EINJ_OP_BUSY 0x1
-#define EINJ_STATUS_SUCCESS 0x0
-#define EINJ_STATUS_FAIL 0x1
-#define EINJ_STATUS_INVAL 0x2
-
-#define EINJ_TAB_ENTRY(tab) \
- ((struct acpi_whea_header *)((char *)(tab) + \
- sizeof(struct acpi_table_einj)))
-
-static bool param_extension;
-module_param(param_extension, bool, 0);
-
-static struct acpi_table_einj *einj_tab;
-
-static struct apei_resources einj_resources;
-
-static struct apei_exec_ins_type einj_ins_type[] = {
- [ACPI_EINJ_READ_REGISTER] = {
- .flags = APEI_EXEC_INS_ACCESS_REGISTER,
- .run = apei_exec_read_register,
- },
- [ACPI_EINJ_READ_REGISTER_VALUE] = {
- .flags = APEI_EXEC_INS_ACCESS_REGISTER,
- .run = apei_exec_read_register_value,
- },
- [ACPI_EINJ_WRITE_REGISTER] = {
- .flags = APEI_EXEC_INS_ACCESS_REGISTER,
- .run = apei_exec_write_register,
- },
- [ACPI_EINJ_WRITE_REGISTER_VALUE] = {
- .flags = APEI_EXEC_INS_ACCESS_REGISTER,
- .run = apei_exec_write_register_value,
- },
- [ACPI_EINJ_NOOP] = {
- .flags = 0,
- .run = apei_exec_noop,
- },
-};
-
-/*
- * Prevent EINJ interpreter to run simultaneously, because the
- * corresponding firmware implementation may not work properly when
- * invoked simultaneously.
- */
-static DEFINE_MUTEX(einj_mutex);
-
-static void *einj_param;
-
-static void einj_exec_ctx_init(struct apei_exec_context *ctx)
-{
- apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type),
- EINJ_TAB_ENTRY(einj_tab), einj_tab->entries);
-}
-
-static int __einj_get_available_error_type(u32 *type)
-{
- struct apei_exec_context ctx;
- int rc;
-
- einj_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE);
- if (rc)
- return rc;
- *type = apei_exec_ctx_get_output(&ctx);
-
- return 0;
-}
-
-/* Get error injection capabilities of the platform */
-static int einj_get_available_error_type(u32 *type)
-{
- int rc;
-
- mutex_lock(&einj_mutex);
- rc = __einj_get_available_error_type(type);
- mutex_unlock(&einj_mutex);
-
- return rc;
-}
-
-static int einj_timedout(u64 *t)
-{
- if ((s64)*t < SLEEP_UNIT_MIN) {
- pr_warn(FW_WARN "Firmware does not respond in time\n");
- return 1;
- }
- *t -= SLEEP_UNIT_MIN;
- usleep_range(SLEEP_UNIT_MIN, SLEEP_UNIT_MAX);
-
- return 0;
-}
-
-static void check_vendor_extension(u64 paddr,
- struct set_error_type_with_address *v5param)
-{
- int offset = v5param->vendor_extension;
- struct vendor_error_type_extension *v;
- u32 sbdf;
-
- if (!offset)
- return;
- v = acpi_os_map_iomem(paddr + offset, sizeof(*v));
- if (!v)
- return;
- sbdf = v->pcie_sbdf;
- sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
- sbdf >> 24, (sbdf >> 16) & 0xff,
- (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
- v->vendor_id, v->device_id, v->rev_id);
- acpi_os_unmap_iomem(v, sizeof(*v));
-}
-
-static void *einj_get_parameter_address(void)
-{
- int i;
- u64 pa_v4 = 0, pa_v5 = 0;
- struct acpi_whea_header *entry;
-
- entry = EINJ_TAB_ENTRY(einj_tab);
- for (i = 0; i < einj_tab->entries; i++) {
- if (entry->action == ACPI_EINJ_SET_ERROR_TYPE &&
- entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
- entry->register_region.space_id ==
- ACPI_ADR_SPACE_SYSTEM_MEMORY)
- pa_v4 = get_unaligned(&entry->register_region.address);
- if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
- entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
- entry->register_region.space_id ==
- ACPI_ADR_SPACE_SYSTEM_MEMORY)
- pa_v5 = get_unaligned(&entry->register_region.address);
- entry++;
- }
- if (pa_v5) {
- struct set_error_type_with_address *v5param;
-
- v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param));
- if (v5param) {
- acpi5 = 1;
- check_vendor_extension(pa_v5, v5param);
- return v5param;
- }
- }
- if (param_extension && pa_v4) {
- struct einj_parameter *v4param;
-
- v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param));
- if (!v4param)
- return NULL;
- if (v4param->reserved1 || v4param->reserved2) {
- acpi_os_unmap_iomem(v4param, sizeof(*v4param));
- return NULL;
- }
- return v4param;
- }
-
- return NULL;
-}
-
-/* do sanity check to trigger table */
-static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
-{
- if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger))
- return -EINVAL;
- if (trigger_tab->table_size > PAGE_SIZE ||
- trigger_tab->table_size < trigger_tab->header_size)
- return -EINVAL;
- if (trigger_tab->entry_count !=
- (trigger_tab->table_size - trigger_tab->header_size) /
- sizeof(struct acpi_einj_entry))
- return -EINVAL;
-
- return 0;
-}
-
-static struct acpi_generic_address *einj_get_trigger_parameter_region(
- struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2)
-{
- int i;
- struct acpi_whea_header *entry;
-
- entry = (struct acpi_whea_header *)
- ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
- for (i = 0; i < trigger_tab->entry_count; i++) {
- if (entry->action == ACPI_EINJ_TRIGGER_ERROR &&
- entry->instruction <= ACPI_EINJ_WRITE_REGISTER_VALUE &&
- entry->register_region.space_id ==
- ACPI_ADR_SPACE_SYSTEM_MEMORY &&
- (entry->register_region.address & param2) == (param1 & param2))
- return &entry->register_region;
- entry++;
- }
-
- return NULL;
-}
-/* Execute instructions in trigger error action table */
-static int __einj_error_trigger(u64 trigger_paddr, u32 type,
- u64 param1, u64 param2)
-{
- struct acpi_einj_trigger *trigger_tab = NULL;
- struct apei_exec_context trigger_ctx;
- struct apei_resources trigger_resources;
- struct acpi_whea_header *trigger_entry;
- struct resource *r;
- u32 table_size;
- int rc = -EIO;
- struct acpi_generic_address *trigger_param_region = NULL;
-
- r = request_mem_region(trigger_paddr, sizeof(*trigger_tab),
- "APEI EINJ Trigger Table");
- if (!r) {
- pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n",
- (unsigned long long)trigger_paddr,
- (unsigned long long)trigger_paddr +
- sizeof(*trigger_tab) - 1);
- goto out;
- }
- trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab));
- if (!trigger_tab) {
- pr_err("Failed to map trigger table!\n");
- goto out_rel_header;
- }
- rc = einj_check_trigger_header(trigger_tab);
- if (rc) {
- pr_warn(FW_BUG "Invalid trigger error action table.\n");
- goto out_rel_header;
- }
-
- /* No action structures in the TRIGGER_ERROR table, nothing to do */
- if (!trigger_tab->entry_count)
- goto out_rel_header;
-
- rc = -EIO;
- table_size = trigger_tab->table_size;
- r = request_mem_region(trigger_paddr + sizeof(*trigger_tab),
- table_size - sizeof(*trigger_tab),
- "APEI EINJ Trigger Table");
- if (!r) {
- pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
- (unsigned long long)trigger_paddr + sizeof(*trigger_tab),
- (unsigned long long)trigger_paddr + table_size - 1);
- goto out_rel_header;
- }
- iounmap(trigger_tab);
- trigger_tab = ioremap_cache(trigger_paddr, table_size);
- if (!trigger_tab) {
- pr_err("Failed to map trigger table!\n");
- goto out_rel_entry;
- }
- trigger_entry = (struct acpi_whea_header *)
- ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
- apei_resources_init(&trigger_resources);
- apei_exec_ctx_init(&trigger_ctx, einj_ins_type,
- ARRAY_SIZE(einj_ins_type),
- trigger_entry, trigger_tab->entry_count);
- rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources);
- if (rc)
- goto out_fini;
- rc = apei_resources_sub(&trigger_resources, &einj_resources);
- if (rc)
- goto out_fini;
- /*
- * Some firmware will access target address specified in
- * param1 to trigger the error when injecting memory error.
- * This will cause resource conflict with regular memory. So
- * remove it from trigger table resources.
- */
- if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
- struct apei_resources addr_resources;
- apei_resources_init(&addr_resources);
- trigger_param_region = einj_get_trigger_parameter_region(
- trigger_tab, param1, param2);
- if (trigger_param_region) {
- rc = apei_resources_add(&addr_resources,
- trigger_param_region->address,
- trigger_param_region->bit_width/8, true);
- if (rc)
- goto out_fini;
- rc = apei_resources_sub(&trigger_resources,
- &addr_resources);
- }
- apei_resources_fini(&addr_resources);
- if (rc)
- goto out_fini;
- }
- rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
- if (rc)
- goto out_fini;
- rc = apei_exec_pre_map_gars(&trigger_ctx);
- if (rc)
- goto out_release;
-
- rc = apei_exec_run(&trigger_ctx, ACPI_EINJ_TRIGGER_ERROR);
-
- apei_exec_post_unmap_gars(&trigger_ctx);
-out_release:
- apei_resources_release(&trigger_resources);
-out_fini:
- apei_resources_fini(&trigger_resources);
-out_rel_entry:
- release_mem_region(trigger_paddr + sizeof(*trigger_tab),
- table_size - sizeof(*trigger_tab));
-out_rel_header:
- release_mem_region(trigger_paddr, sizeof(*trigger_tab));
-out:
- if (trigger_tab)
- iounmap(trigger_tab);
-
- return rc;
-}
-
-static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
- u64 param3, u64 param4)
-{
- struct apei_exec_context ctx;
- u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
- int rc;
-
- einj_exec_ctx_init(&ctx);
-
- rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION);
- if (rc)
- return rc;
- apei_exec_ctx_set_input(&ctx, type);
- if (acpi5) {
- struct set_error_type_with_address *v5param = einj_param;
-
- v5param->type = type;
- if (type & ACPI5_VENDOR_BIT) {
- switch (vendor_flags) {
- case SETWA_FLAGS_APICID:
- v5param->apicid = param1;
- break;
- case SETWA_FLAGS_MEM:
- v5param->memory_address = param1;
- v5param->memory_address_range = param2;
- break;
- case SETWA_FLAGS_PCIE_SBDF:
- v5param->pcie_sbdf = param1;
- break;
- }
- v5param->flags = vendor_flags;
- } else if (flags) {
- v5param->flags = flags;
- v5param->memory_address = param1;
- v5param->memory_address_range = param2;
- v5param->apicid = param3;
- v5param->pcie_sbdf = param4;
- } else {
- switch (type) {
- case ACPI_EINJ_PROCESSOR_CORRECTABLE:
- case ACPI_EINJ_PROCESSOR_UNCORRECTABLE:
- case ACPI_EINJ_PROCESSOR_FATAL:
- v5param->apicid = param1;
- v5param->flags = SETWA_FLAGS_APICID;
- break;
- case ACPI_EINJ_MEMORY_CORRECTABLE:
- case ACPI_EINJ_MEMORY_UNCORRECTABLE:
- case ACPI_EINJ_MEMORY_FATAL:
- v5param->memory_address = param1;
- v5param->memory_address_range = param2;
- v5param->flags = SETWA_FLAGS_MEM;
- break;
- case ACPI_EINJ_PCIX_CORRECTABLE:
- case ACPI_EINJ_PCIX_UNCORRECTABLE:
- case ACPI_EINJ_PCIX_FATAL:
- v5param->pcie_sbdf = param1;
- v5param->flags = SETWA_FLAGS_PCIE_SBDF;
- break;
- }
- }
- } else {
- rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
- if (rc)
- return rc;
- if (einj_param) {
- struct einj_parameter *v4param = einj_param;
- v4param->param1 = param1;
- v4param->param2 = param2;
- }
- }
- rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
- if (rc)
- return rc;
- for (;;) {
- rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS);
- if (rc)
- return rc;
- val = apei_exec_ctx_get_output(&ctx);
- if (!(val & EINJ_OP_BUSY))
- break;
- if (einj_timedout(&timeout))
- return -EIO;
- }
- rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS);
- if (rc)
- return rc;
- val = apei_exec_ctx_get_output(&ctx);
- if (val != EINJ_STATUS_SUCCESS)
- return -EBUSY;
-
- rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE);
- if (rc)
- return rc;
- trigger_paddr = apei_exec_ctx_get_output(&ctx);
- if (notrigger == 0) {
- rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
- if (rc)
- return rc;
- }
- rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
-
- return rc;
-}
-
-/* Inject the specified hardware error */
-static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
- u64 param3, u64 param4)
-{
- int rc;
- u64 base_addr, size;
-
- /* If user manually set "flags", make sure it is legal */
- if (flags && (flags &
- ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF)))
- return -EINVAL;
-
- /*
- * We need extra sanity checks for memory errors.
- * Other types leap directly to injection.
- */
-
- /* ensure param1/param2 existed */
- if (!(param_extension || acpi5))
- goto inject;
-
- /* ensure injection is memory related */
- if (type & ACPI5_VENDOR_BIT) {
- if (vendor_flags != SETWA_FLAGS_MEM)
- goto inject;
- } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
- goto inject;
-
- /*
- * Disallow crazy address masks that give BIOS leeway to pick
- * injection address almost anywhere. Insist on page or
- * better granularity and that target address is normal RAM or
- * NVDIMM.
- */
- base_addr = param1 & param2;
- size = ~param2 + 1;
-
- if (((param2 & PAGE_MASK) != PAGE_MASK) ||
- ((region_intersects(base_addr, size, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)
- != REGION_INTERSECTS) &&
- (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY)
- != REGION_INTERSECTS) &&
- (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_SOFT_RESERVED)
- != REGION_INTERSECTS) &&
- !arch_is_platform_page(base_addr)))
- return -EINVAL;
-
- if (is_zero_pfn(base_addr >> PAGE_SHIFT))
- return -EADDRINUSE;
-
-inject:
- mutex_lock(&einj_mutex);
- rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
- mutex_unlock(&einj_mutex);
-
- return rc;
-}
-
-static u32 error_type;
-static u32 error_flags;
-static u64 error_param1;
-static u64 error_param2;
-static u64 error_param3;
-static u64 error_param4;
-static struct dentry *einj_debug_dir;
-
-static int available_error_type_show(struct seq_file *m, void *v)
-{
- int rc;
- u32 available_error_type = 0;
-
- rc = einj_get_available_error_type(&available_error_type);
- if (rc)
- return rc;
- if (available_error_type & 0x0001)
- seq_printf(m, "0x00000001\tProcessor Correctable\n");
- if (available_error_type & 0x0002)
- seq_printf(m, "0x00000002\tProcessor Uncorrectable non-fatal\n");
- if (available_error_type & 0x0004)
- seq_printf(m, "0x00000004\tProcessor Uncorrectable fatal\n");
- if (available_error_type & 0x0008)
- seq_printf(m, "0x00000008\tMemory Correctable\n");
- if (available_error_type & 0x0010)
- seq_printf(m, "0x00000010\tMemory Uncorrectable non-fatal\n");
- if (available_error_type & 0x0020)
- seq_printf(m, "0x00000020\tMemory Uncorrectable fatal\n");
- if (available_error_type & 0x0040)
- seq_printf(m, "0x00000040\tPCI Express Correctable\n");
- if (available_error_type & 0x0080)
- seq_printf(m, "0x00000080\tPCI Express Uncorrectable non-fatal\n");
- if (available_error_type & 0x0100)
- seq_printf(m, "0x00000100\tPCI Express Uncorrectable fatal\n");
- if (available_error_type & 0x0200)
- seq_printf(m, "0x00000200\tPlatform Correctable\n");
- if (available_error_type & 0x0400)
- seq_printf(m, "0x00000400\tPlatform Uncorrectable non-fatal\n");
- if (available_error_type & 0x0800)
- seq_printf(m, "0x00000800\tPlatform Uncorrectable fatal\n");
-
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(available_error_type);
-
-static int error_type_get(void *data, u64 *val)
-{
- *val = error_type;
-
- return 0;
-}
-
-static int error_type_set(void *data, u64 val)
-{
- int rc;
- u32 available_error_type = 0;
- u32 tval, vendor;
-
- /*
- * Vendor defined types have 0x80000000 bit set, and
- * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
- */
- vendor = val & ACPI5_VENDOR_BIT;
- tval = val & 0x7fffffff;
-
- /* Only one error type can be specified */
- if (tval & (tval - 1))
- return -EINVAL;
- if (!vendor) {
- rc = einj_get_available_error_type(&available_error_type);
- if (rc)
- return rc;
- if (!(val & available_error_type))
- return -EINVAL;
- }
- error_type = val;
-
- return 0;
-}
-
-DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set,
- "0x%llx\n");
-
-static int error_inject_set(void *data, u64 val)
-{
- if (!error_type)
- return -EINVAL;
-
- return einj_error_inject(error_type, error_flags, error_param1, error_param2,
- error_param3, error_param4);
-}
-
-DEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n");
-
-static int einj_check_table(struct acpi_table_einj *einj_tab)
-{
- if ((einj_tab->header_length !=
- (sizeof(struct acpi_table_einj) - sizeof(einj_tab->header)))
- && (einj_tab->header_length != sizeof(struct acpi_table_einj)))
- return -EINVAL;
- if (einj_tab->header.length < sizeof(struct acpi_table_einj))
- return -EINVAL;
- if (einj_tab->entries !=
- (einj_tab->header.length - sizeof(struct acpi_table_einj)) /
- sizeof(struct acpi_einj_entry))
- return -EINVAL;
-
- return 0;
-}
-
-static int __init einj_init(void)
-{
- int rc;
- acpi_status status;
- struct apei_exec_context ctx;
-
- if (acpi_disabled) {
- pr_info("ACPI disabled.\n");
- return -ENODEV;
- }
-
- status = acpi_get_table(ACPI_SIG_EINJ, 0,
- (struct acpi_table_header **)&einj_tab);
- if (status == AE_NOT_FOUND) {
- pr_warn("EINJ table not found.\n");
- return -ENODEV;
- }
- else if (ACPI_FAILURE(status)) {
- pr_err("Failed to get EINJ table: %s\n",
- acpi_format_exception(status));
- return -EINVAL;
- }
-
- rc = einj_check_table(einj_tab);
- if (rc) {
- pr_warn(FW_BUG "Invalid EINJ table.\n");
- goto err_put_table;
- }
-
- rc = -ENOMEM;
- einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir());
-
- debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir,
- NULL, &available_error_type_fops);
- debugfs_create_file_unsafe("error_type", 0600, einj_debug_dir,
- NULL, &error_type_fops);
- debugfs_create_file_unsafe("error_inject", 0200, einj_debug_dir,
- NULL, &error_inject_fops);
-
- apei_resources_init(&einj_resources);
- einj_exec_ctx_init(&ctx);
- rc = apei_exec_collect_resources(&ctx, &einj_resources);
- if (rc) {
- pr_err("Error collecting EINJ resources.\n");
- goto err_fini;
- }
-
- rc = apei_resources_request(&einj_resources, "APEI EINJ");
- if (rc) {
- pr_err("Error requesting memory/port resources.\n");
- goto err_fini;
- }
-
- rc = apei_exec_pre_map_gars(&ctx);
- if (rc) {
- pr_err("Error pre-mapping GARs.\n");
- goto err_release;
- }
-
- einj_param = einj_get_parameter_address();
- if ((param_extension || acpi5) && einj_param) {
- debugfs_create_x32("flags", S_IRUSR | S_IWUSR, einj_debug_dir,
- &error_flags);
- debugfs_create_x64("param1", S_IRUSR | S_IWUSR, einj_debug_dir,
- &error_param1);
- debugfs_create_x64("param2", S_IRUSR | S_IWUSR, einj_debug_dir,
- &error_param2);
- debugfs_create_x64("param3", S_IRUSR | S_IWUSR, einj_debug_dir,
- &error_param3);
- debugfs_create_x64("param4", S_IRUSR | S_IWUSR, einj_debug_dir,
- &error_param4);
- debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
- einj_debug_dir, &notrigger);
- }
-
- if (vendor_dev[0]) {
- vendor_blob.data = vendor_dev;
- vendor_blob.size = strlen(vendor_dev);
- debugfs_create_blob("vendor", S_IRUSR, einj_debug_dir,
- &vendor_blob);
- debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR,
- einj_debug_dir, &vendor_flags);
- }
-
- pr_info("Error INJection is initialized.\n");
-
- return 0;
-
-err_release:
- apei_resources_release(&einj_resources);
-err_fini:
- apei_resources_fini(&einj_resources);
- debugfs_remove_recursive(einj_debug_dir);
-err_put_table:
- acpi_put_table((struct acpi_table_header *)einj_tab);
-
- return rc;
-}
-
-static void __exit einj_exit(void)
-{
- struct apei_exec_context ctx;
-
- if (einj_param) {
- acpi_size size = (acpi5) ?
- sizeof(struct set_error_type_with_address) :
- sizeof(struct einj_parameter);
-
- acpi_os_unmap_iomem(einj_param, size);
- }
- einj_exec_ctx_init(&ctx);
- apei_exec_post_unmap_gars(&ctx);
- apei_resources_release(&einj_resources);
- apei_resources_fini(&einj_resources);
- debugfs_remove_recursive(einj_debug_dir);
- acpi_put_table((struct acpi_table_header *)einj_tab);
-}
-
-module_init(einj_init);
-module_exit(einj_exit);
-
-MODULE_AUTHOR("Huang Ying");
-MODULE_DESCRIPTION("APEI Error INJection support");
-MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/apei/erst-dbg.c b/drivers/acpi/apei/erst-dbg.c
index 8bc71cdc2270..ff0e8bf8e97a 100644
--- a/drivers/acpi/apei/erst-dbg.c
+++ b/drivers/acpi/apei/erst-dbg.c
@@ -60,9 +60,8 @@ static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
switch (cmd) {
case APEI_ERST_CLEAR_RECORD:
- rc = copy_from_user(&record_id, (void __user *)arg,
- sizeof(record_id));
- if (rc)
+ if (copy_from_user(&record_id, (void __user *)arg,
+ sizeof(record_id)))
return -EFAULT;
return erst_clear(record_id);
case APEI_ERST_GET_RECORD_COUNT:
@@ -175,8 +174,7 @@ static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf,
erst_dbg_buf = p;
erst_dbg_buf_len = usize;
}
- rc = copy_from_user(erst_dbg_buf, ubuf, usize);
- if (rc) {
+ if (copy_from_user(erst_dbg_buf, ubuf, usize)) {
rc = -EFAULT;
goto out;
}
@@ -199,7 +197,6 @@ static const struct file_operations erst_dbg_ops = {
.read = erst_dbg_read,
.write = erst_dbg_write,
.unlocked_ioctl = erst_dbg_ioctl,
- .llseek = no_llseek,
};
static struct miscdevice erst_dbg_dev = {
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 31b077eedb58..bf65e3461531 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -59,6 +59,10 @@ static struct acpi_table_erst *erst_tab;
#define ERST_RANGE_NVRAM 0x0002
#define ERST_RANGE_SLOW 0x0004
+/* ERST Exec max timings */
+#define ERST_EXEC_TIMING_MAX_MASK 0xFFFFFFFF00000000
+#define ERST_EXEC_TIMING_MAX_SHIFT 32
+
/*
* ERST Error Log Address Range, used as buffer for reading/writing
* error records.
@@ -68,6 +72,7 @@ static struct erst_erange {
u64 size;
void __iomem *vaddr;
u32 attr;
+ u64 timings;
} erst_erange;
/*
@@ -97,6 +102,19 @@ static inline int erst_errno(int command_status)
}
}
+static inline u64 erst_get_timeout(void)
+{
+ u64 timeout = FIRMWARE_TIMEOUT;
+
+ if (erst_erange.attr & ERST_RANGE_SLOW) {
+ timeout = ((erst_erange.timings & ERST_EXEC_TIMING_MAX_MASK) >>
+ ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_MSEC;
+ if (timeout < FIRMWARE_TIMEOUT)
+ timeout = FIRMWARE_TIMEOUT;
+ }
+ return timeout;
+}
+
static int erst_timedout(u64 *t, u64 spin_unit)
{
if ((s64)*t < spin_unit) {
@@ -191,9 +209,11 @@ static int erst_exec_stall_while_true(struct apei_exec_context *ctx,
{
int rc;
u64 val;
- u64 timeout = FIRMWARE_TIMEOUT;
+ u64 timeout;
u64 stall_time;
+ timeout = erst_get_timeout();
+
if (ctx->var1 > FIRMWARE_MAX_STALL) {
if (!in_nmi())
pr_warn(FW_WARN
@@ -389,6 +409,13 @@ static int erst_get_erange(struct erst_erange *range)
if (rc)
return rc;
range->attr = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_TIMINGS);
+ if (rc == 0)
+ range->timings = apei_exec_ctx_get_output(&ctx);
+ else if (rc == -ENOENT)
+ range->timings = 0;
+ else
+ return rc;
return 0;
}
@@ -621,10 +648,12 @@ EXPORT_SYMBOL_GPL(erst_get_record_id_end);
static int __erst_write_to_storage(u64 offset)
{
struct apei_exec_context ctx;
- u64 timeout = FIRMWARE_TIMEOUT;
+ u64 timeout;
u64 val;
int rc;
+ timeout = erst_get_timeout();
+
erst_exec_ctx_init(&ctx);
rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);
if (rc)
@@ -660,10 +689,12 @@ static int __erst_write_to_storage(u64 offset)
static int __erst_read_from_storage(u64 record_id, u64 offset)
{
struct apei_exec_context ctx;
- u64 timeout = FIRMWARE_TIMEOUT;
+ u64 timeout;
u64 val;
int rc;
+ timeout = erst_get_timeout();
+
erst_exec_ctx_init(&ctx);
rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);
if (rc)
@@ -703,10 +734,12 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)
static int __erst_clear_from_storage(u64 record_id)
{
struct apei_exec_context ctx;
- u64 timeout = FIRMWARE_TIMEOUT;
+ u64 timeout;
u64 val;
int rc;
+ timeout = erst_get_timeout();
+
erst_exec_ctx_init(&ctx);
rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);
if (rc)
@@ -1020,14 +1053,10 @@ static int reader_pos;
static int erst_open_pstore(struct pstore_info *psi)
{
- int rc;
-
if (erst_disable)
return -ENODEV;
- rc = erst_get_record_id_begin(&reader_pos);
-
- return rc;
+ return erst_get_record_id_begin(&reader_pos);
}
static int erst_close_pstore(struct pstore_info *psi)
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index d91ad378c00d..0dc767392a6c 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -22,10 +22,12 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/acpi.h>
+#include <linux/bitfield.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/cper.h>
+#include <linux/cleanup.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/ratelimit.h>
@@ -33,6 +35,7 @@
#include <linux/irq_work.h>
#include <linux/llist.h>
#include <linux/genalloc.h>
+#include <linux/kfifo.h>
#include <linux/pci.h>
#include <linux/pfn.h>
#include <linux/aer.h>
@@ -41,12 +44,14 @@
#include <linux/uuid.h>
#include <linux/ras.h>
#include <linux/task_work.h>
+#include <linux/vmcore_info.h>
#include <acpi/actbl1.h>
#include <acpi/ghes.h>
#include <acpi/apei.h>
#include <asm/fixmap.h>
#include <asm/tlbflush.h>
+#include <cxl/event.h>
#include <ras/ras_event.h>
#include "apei-internal.h"
@@ -94,12 +99,28 @@
#define FIX_APEI_GHES_SDEI_CRITICAL __end_of_fixed_addresses
#endif
+static ATOMIC_NOTIFIER_HEAD(ghes_report_chain);
+
static inline bool is_hest_type_generic_v2(struct ghes *ghes)
{
return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
}
/*
+ * A platform may describe one error source for the handling of synchronous
+ * errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI
+ * or External Interrupt). On x86, the HEST notifications are always
+ * asynchronous, so only SEA on ARM is delivered as a synchronous
+ * notification.
+ */
+static inline bool is_hest_sync_notify(struct ghes *ghes)
+{
+ u8 notify_type = ghes->generic->notify.type;
+
+ return notify_type == ACPI_HEST_NOTIFY_SEA;
+}
+
+/*
* This driver isn't really modular, however for the time being,
* continuing to use module_param is the easiest way to remain
* compatible with existing boot arg use cases.
@@ -108,6 +129,13 @@ bool ghes_disable;
module_param_named(disable, ghes_disable, bool, 0);
/*
+ * "ghes.edac_force_enable" forcibly enables ghes_edac and skips the platform
+ * check.
+ */
+static bool ghes_edac_force_enable;
+module_param_named(edac_force_enable, ghes_edac_force_enable, bool, 0);
+
+/*
* All error sources notified with HED (Hardware Error Device) share a
* single notifier callback, so they need to be linked and checked one
* by one. This holds true for NMI too.
@@ -119,6 +147,13 @@ static LIST_HEAD(ghes_hed);
static DEFINE_MUTEX(ghes_list_mutex);
/*
+ * A list of GHES devices which are given to the corresponding EDAC driver
+ * ghes_edac for further use.
+ */
+static LIST_HEAD(ghes_devs);
+static DEFINE_MUTEX(ghes_devs_mutex);
+
+/*
* Because the memory area used to transfer hardware error information
* from BIOS to Linux can be determined only in NMI, IRQ or timer
* handler, but general ioremap can not be used in atomic context, so
@@ -136,13 +171,10 @@ struct ghes_vendor_record_entry {
};
static struct gen_pool *ghes_estatus_pool;
-static unsigned long ghes_estatus_pool_size_request;
-static struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
+static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
static atomic_t ghes_estatus_cache_alloced;
-static int ghes_panic_timeout __read_mostly = 30;
-
static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx)
{
phys_addr_t paddr;
@@ -163,7 +195,7 @@ static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_idx)
clear_fixmap(fixmap_idx);
}
-int ghes_estatus_pool_init(int num_ghes)
+int ghes_estatus_pool_init(unsigned int num_ghes)
{
unsigned long addr, len;
int rc;
@@ -175,7 +207,6 @@ int ghes_estatus_pool_init(int num_ghes)
len = GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX;
len += (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE);
- ghes_estatus_pool_size_request = PAGE_ALIGN(len);
addr = (unsigned long)vmalloc(PAGE_ALIGN(len));
if (!addr)
goto err_pool_alloc;
@@ -195,6 +226,20 @@ err_pool_alloc:
return -ENOMEM;
}
+/**
+ * ghes_estatus_pool_region_free - free previously allocated memory
+ * from the ghes_estatus_pool.
+ * @addr: address of memory to free.
+ * @size: size of memory to free.
+ *
+ * Returns none.
+ */
+void ghes_estatus_pool_region_free(unsigned long addr, u32 size)
+{
+ gen_pool_free(ghes_estatus_pool, addr, size);
+}
+EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free);
+
static int map_gen_v2(struct ghes *ghes)
{
return apei_map_generic_address(&ghes->generic_v2->read_ack_register);
@@ -421,39 +466,58 @@ static void ghes_clear_estatus(struct ghes *ghes,
ghes_ack_error(ghes->generic_v2);
}
-/*
- * Called as task_work before returning to user-space.
- * Ensure any queued work has been done before we return to the context that
- * triggered the notification.
+/**
+ * struct ghes_task_work - for synchronous RAS event
+ *
+ * @twork: callback_head for task work
+ * @pfn: page frame number of corrupted page
+ * @flags: work control flags
+ *
+ * Structure to pass task work to be handled before
+ * returning to user-space via task_work_add().
*/
-static void ghes_kick_task_work(struct callback_head *head)
+struct ghes_task_work {
+ struct callback_head twork;
+ u64 pfn;
+ int flags;
+};
+
+static void memory_failure_cb(struct callback_head *twork)
{
- struct acpi_hest_generic_status *estatus;
- struct ghes_estatus_node *estatus_node;
- u32 node_len;
+ struct ghes_task_work *twcb = container_of(twork, struct ghes_task_work, twork);
+ int ret;
- estatus_node = container_of(head, struct ghes_estatus_node, task_work);
- if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE))
- memory_failure_queue_kick(estatus_node->task_work_cpu);
+ ret = memory_failure(twcb->pfn, twcb->flags);
+ gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb));
- estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
- node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus));
- gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len);
+ if (!ret || ret == -EHWPOISON || ret == -EOPNOTSUPP)
+ return;
+
+ pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\n",
+ twcb->pfn, current->comm, task_pid_nr(current));
+ force_sig(SIGBUS);
}
static bool ghes_do_memory_failure(u64 physical_addr, int flags)
{
+ struct ghes_task_work *twcb;
unsigned long pfn;
if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE))
return false;
pfn = PHYS_PFN(physical_addr);
- if (!pfn_valid(pfn) && !arch_is_platform_page(physical_addr)) {
- pr_warn_ratelimited(FW_WARN GHES_PFX
- "Invalid address in generic error data: %#llx\n",
- physical_addr);
- return false;
+
+ if (flags == MF_ACTION_REQUIRED && current->mm) {
+ twcb = (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb));
+ if (!twcb)
+ return false;
+
+ twcb->pfn = pfn;
+ twcb->flags = flags;
+ init_task_work(&twcb->twork, memory_failure_cb);
+ task_work_add(current, &twcb->twork, TWA_RESUME);
+ return true;
}
memory_failure_queue(pfn, flags);
@@ -461,7 +525,7 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags)
}
static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
- int sev)
+ int sev, bool sync)
{
int flags = -1;
int sec_sev = ghes_severity(gdata->error_severity);
@@ -475,7 +539,7 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
(gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
flags = MF_SOFT_OFFLINE;
if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
- flags = 0;
+ flags = sync ? MF_ACTION_REQUIRED : 0;
if (flags != -1)
return ghes_do_memory_failure(mem_err->physical_addr, flags);
@@ -483,25 +547,26 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
return false;
}
-static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int sev)
+static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
+ int sev, bool sync)
{
struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata);
+ int flags = sync ? MF_ACTION_REQUIRED : 0;
+ char error_type[120];
bool queued = false;
int sec_sev, i;
char *p;
- log_arm_hw_error(err);
-
sec_sev = ghes_severity(gdata->error_severity);
+ log_arm_hw_error(err, sec_sev);
if (sev != GHES_SEV_RECOVERABLE || sec_sev != GHES_SEV_RECOVERABLE)
return false;
p = (char *)(err + 1);
for (i = 0; i < err->err_info_num; i++) {
struct cper_arm_err_info *err_info = (struct cper_arm_err_info *)p;
- bool is_cache = (err_info->type == CPER_ARM_CACHE_ERROR);
+ bool is_cache = err_info->type & CPER_ARM_CACHE_ERROR;
bool has_pa = (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR);
- const char *error_type = "unknown error";
/*
* The field (err_info->error_info & BIT(26)) is fixed to set to
@@ -510,17 +575,20 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int s
* and don't filter out 'corrected' error here.
*/
if (is_cache && has_pa) {
- queued = ghes_do_memory_failure(err_info->physical_fault_addr, 0);
+ queued = ghes_do_memory_failure(err_info->physical_fault_addr, flags);
p += err_info->length;
continue;
}
- if (err_info->type < ARRAY_SIZE(cper_proc_error_type_strs))
- error_type = cper_proc_error_type_strs[err_info->type];
+ cper_bits_to_str(error_type, sizeof(error_type),
+ FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type),
+ cper_proc_error_type_strs,
+ ARRAY_SIZE(cper_proc_error_type_strs));
pr_warn_ratelimited(FW_WARN GHES_PFX
- "Unhandled processor error type: %s\n",
- error_type);
+ "Unhandled processor error type 0x%02x: %s%s\n",
+ err_info->type, error_type,
+ (err_info->type & ~CPER_ARM_ERR_TYPE_MASK) ? " with reserved bit(s)" : "");
p += err_info->length;
}
@@ -550,6 +618,7 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) {
unsigned int devfn;
int aer_severity;
+ u8 *aer_info;
devfn = PCI_DEVFN(pcie_err->device_id.device,
pcie_err->device_id.function);
@@ -563,11 +632,17 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
if (gdata->flags & CPER_SEC_RESET)
aer_severity = AER_FATAL;
+ aer_info = (void *)gen_pool_alloc(ghes_estatus_pool,
+ sizeof(struct aer_capability_regs));
+ if (!aer_info)
+ return;
+ memcpy(aer_info, pcie_err->aer_info, sizeof(struct aer_capability_regs));
+
aer_recover_queue(pcie_err->device_id.segment,
pcie_err->device_id.bus,
devfn, aer_severity,
(struct aer_capability_regs *)
- pcie_err->aer_info);
+ aer_info);
}
#endif
}
@@ -622,7 +697,209 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
schedule_work(&entry->work);
}
-static bool ghes_do_proc(struct ghes *ghes,
+/* Room for 8 entries */
+#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8
+static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
+ CXL_CPER_PROT_ERR_FIFO_DEPTH);
+
+/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
+static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
+struct work_struct *cxl_cper_prot_err_work;
+
+static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
+ int severity)
+{
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+ struct cxl_cper_prot_err_work_data wd;
+ u8 *dvsec_start, *cap_start;
+
+ if (!(prot_err->valid_bits & PROT_ERR_VALID_AGENT_ADDRESS)) {
+ pr_err_ratelimited("CXL CPER invalid agent type\n");
+ return;
+ }
+
+ if (!(prot_err->valid_bits & PROT_ERR_VALID_ERROR_LOG)) {
+ pr_err_ratelimited("CXL CPER invalid protocol error log\n");
+ return;
+ }
+
+ if (prot_err->err_len != sizeof(struct cxl_ras_capability_regs)) {
+ pr_err_ratelimited("CXL CPER invalid RAS Cap size (%u)\n",
+ prot_err->err_len);
+ return;
+ }
+
+ if (!(prot_err->valid_bits & PROT_ERR_VALID_SERIAL_NUMBER))
+ pr_warn(FW_WARN "CXL CPER no device serial number\n");
+
+ guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
+
+ if (!cxl_cper_prot_err_work)
+ return;
+
+ switch (prot_err->agent_type) {
+ case RCD:
+ case DEVICE:
+ case LD:
+ case FMLD:
+ case RP:
+ case DSP:
+ case USP:
+ memcpy(&wd.prot_err, prot_err, sizeof(wd.prot_err));
+
+ dvsec_start = (u8 *)(prot_err + 1);
+ cap_start = dvsec_start + prot_err->dvsec_len;
+
+ memcpy(&wd.ras_cap, cap_start, sizeof(wd.ras_cap));
+ wd.severity = cper_severity_to_aer(severity);
+ break;
+ default:
+ pr_err_ratelimited("CXL CPER invalid agent type: %d\n",
+ prot_err->agent_type);
+ return;
+ }
+
+ if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) {
+ pr_err_ratelimited("CXL CPER kfifo overflow\n");
+ return;
+ }
+
+ schedule_work(cxl_cper_prot_err_work);
+#endif
+}
+
+int cxl_cper_register_prot_err_work(struct work_struct *work)
+{
+ if (cxl_cper_prot_err_work)
+ return -EINVAL;
+
+ guard(spinlock)(&cxl_cper_prot_err_work_lock);
+ cxl_cper_prot_err_work = work;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
+
+int cxl_cper_unregister_prot_err_work(struct work_struct *work)
+{
+ if (cxl_cper_prot_err_work != work)
+ return -EINVAL;
+
+ guard(spinlock)(&cxl_cper_prot_err_work_lock);
+ cxl_cper_prot_err_work = NULL;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
+
+int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
+{
+ return kfifo_get(&cxl_cper_prot_err_fifo, wd);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
+
+/* Room for 8 entries for each of the 4 event log queues */
+#define CXL_CPER_FIFO_DEPTH 32
+DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
+
+/* Synchronize schedule_work() with cxl_cper_work changes */
+static DEFINE_SPINLOCK(cxl_cper_work_lock);
+struct work_struct *cxl_cper_work;
+
+static void cxl_cper_post_event(enum cxl_event_type event_type,
+ struct cxl_cper_event_rec *rec)
+{
+ struct cxl_cper_work_data wd;
+
+ if (rec->hdr.length <= sizeof(rec->hdr) ||
+ rec->hdr.length > sizeof(*rec)) {
+ pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
+ rec->hdr.length);
+ return;
+ }
+
+ if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
+ pr_err(FW_WARN "CXL CPER invalid event\n");
+ return;
+ }
+
+ guard(spinlock_irqsave)(&cxl_cper_work_lock);
+
+ if (!cxl_cper_work)
+ return;
+
+ wd.event_type = event_type;
+ memcpy(&wd.rec, rec, sizeof(wd.rec));
+
+ if (!kfifo_put(&cxl_cper_fifo, wd)) {
+ pr_err_ratelimited("CXL CPER kfifo overflow\n");
+ return;
+ }
+
+ schedule_work(cxl_cper_work);
+}
+
+int cxl_cper_register_work(struct work_struct *work)
+{
+ if (cxl_cper_work)
+ return -EINVAL;
+
+ guard(spinlock)(&cxl_cper_work_lock);
+ cxl_cper_work = work;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
+
+int cxl_cper_unregister_work(struct work_struct *work)
+{
+ if (cxl_cper_work != work)
+ return -EINVAL;
+
+ guard(spinlock)(&cxl_cper_work_lock);
+ cxl_cper_work = NULL;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
+
+int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
+{
+ return kfifo_get(&cxl_cper_fifo, wd);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL");
+
+static void ghes_log_hwerr(int sev, guid_t *sec_type)
+{
+ if (sev != CPER_SEV_RECOVERABLE)
+ return;
+
+ if (guid_equal(sec_type, &CPER_SEC_PROC_ARM) ||
+ guid_equal(sec_type, &CPER_SEC_PROC_GENERIC) ||
+ guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
+ hwerr_log_error_type(HWERR_RECOV_CPU);
+ return;
+ }
+
+ if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR) ||
+ guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID) ||
+ guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID) ||
+ guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) {
+ hwerr_log_error_type(HWERR_RECOV_CXL);
+ return;
+ }
+
+ if (guid_equal(sec_type, &CPER_SEC_PCIE) ||
+ guid_equal(sec_type, &CPER_SEC_PCI_X_BUS)) {
+ hwerr_log_error_type(HWERR_RECOV_PCI);
+ return;
+ }
+
+ if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
+ hwerr_log_error_type(HWERR_RECOV_MEMORY);
+ return;
+ }
+
+ hwerr_log_error_type(HWERR_RECOV_OTHERS);
+}
+
+static void ghes_do_proc(struct ghes *ghes,
const struct acpi_hest_generic_status *estatus)
{
int sev, sec_sev;
@@ -631,6 +908,7 @@ static bool ghes_do_proc(struct ghes *ghes,
const guid_t *fru_id = &guid_null;
char *fru_text = "";
bool queued = false;
+ bool sync = is_hest_sync_notify(ghes);
sev = ghes_severity(estatus->error_severity);
apei_estatus_for_each_section(estatus, gdata) {
@@ -642,19 +920,34 @@ static bool ghes_do_proc(struct ghes *ghes,
if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
fru_text = gdata->fru_text;
+ ghes_log_hwerr(sev, sec_type);
if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
- ghes_edac_report_mem_error(sev, mem_err);
+ atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err);
arch_apei_report_mem_error(sev, mem_err);
- queued = ghes_handle_memory_failure(gdata, sev);
- }
- else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
+ queued = ghes_handle_memory_failure(gdata, sev, sync);
+ } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
ghes_handle_aer(gdata);
- }
- else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
- queued = ghes_handle_arm_hw_error(gdata, sev);
+ } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
+ queued = ghes_handle_arm_hw_error(gdata, sev, sync);
+ } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
+ struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
+
+ cxl_cper_post_prot_err(prot_err, gdata->error_severity);
+ } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) {
+ struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
+
+ cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec);
+ } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) {
+ struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
+
+ cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec);
+ } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) {
+ struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
+
+ cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec);
} else {
void *err = acpi_hest_get_payload(gdata);
@@ -665,7 +958,16 @@ static bool ghes_do_proc(struct ghes *ghes,
}
}
- return queued;
+ /*
+ * If no memory failure work is queued for abnormal synchronous
+ * errors, do a force kill.
+ */
+ if (sync && !queued) {
+ dev_err(ghes->dev,
+ HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n",
+ current->comm, task_pid_nr(current));
+ force_sig(SIGBUS);
+ }
}
static void __ghes_print_estatus(const char *pfx,
@@ -773,48 +1075,42 @@ static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
return cache;
}
-static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
+static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
{
+ struct ghes_estatus_cache *cache;
u32 len;
+ cache = container_of(head, struct ghes_estatus_cache, rcu);
len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
len = GHES_ESTATUS_CACHE_LEN(len);
gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
atomic_dec(&ghes_estatus_cache_alloced);
}
-static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
-{
- struct ghes_estatus_cache *cache;
-
- cache = container_of(head, struct ghes_estatus_cache, rcu);
- ghes_estatus_cache_free(cache);
-}
-
-static void ghes_estatus_cache_add(
- struct acpi_hest_generic *generic,
- struct acpi_hest_generic_status *estatus)
+static void
+ghes_estatus_cache_add(struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
{
- int i, slot = -1, count;
unsigned long long now, duration, period, max_period = 0;
- struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
+ struct ghes_estatus_cache *cache, *new_cache;
+ struct ghes_estatus_cache __rcu *victim;
+ int i, slot = -1, count;
new_cache = ghes_estatus_cache_alloc(generic, estatus);
- if (new_cache == NULL)
+ if (!new_cache)
return;
+
rcu_read_lock();
now = sched_clock();
for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
cache = rcu_dereference(ghes_estatus_caches[i]);
if (cache == NULL) {
slot = i;
- slot_cache = NULL;
break;
}
duration = now - cache->time_in;
if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
slot = i;
- slot_cache = cache;
break;
}
count = atomic_read(&cache->count);
@@ -823,32 +1119,48 @@ static void ghes_estatus_cache_add(
if (period > max_period) {
max_period = period;
slot = i;
- slot_cache = cache;
}
}
- /* new_cache must be put into array after its contents are written */
- smp_wmb();
- if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
- slot_cache, new_cache) == slot_cache) {
- if (slot_cache)
- call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
- } else
- ghes_estatus_cache_free(new_cache);
rcu_read_unlock();
+
+ if (slot != -1) {
+ /*
+ * Use release semantics to ensure that ghes_estatus_cached()
+ * running on another CPU will see the updated cache fields if
+ * it can see the new value of the pointer.
+ */
+ victim = xchg_release(&ghes_estatus_caches[slot],
+ RCU_INITIALIZER(new_cache));
+
+ /*
+ * At this point, victim may point to a cached item different
+ * from the one based on which we selected the slot. Instead of
+ * going to the loop again to pick another slot, let's just
+ * drop the other item anyway: this may cause a false cache
+ * miss later on, but that won't cause any problems.
+ */
+ if (victim)
+ call_rcu(&unrcu_pointer(victim)->rcu,
+ ghes_estatus_cache_rcu_free);
+ }
}
static void __ghes_panic(struct ghes *ghes,
struct acpi_hest_generic_status *estatus,
u64 buf_paddr, enum fixed_addresses fixmap_idx)
{
+ const char *msg = GHES_PFX "Fatal hardware error";
+
__ghes_print_estatus(KERN_EMERG, ghes->generic, estatus);
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK);
+
ghes_clear_estatus(ghes, estatus, buf_paddr, fixmap_idx);
- /* reboot to log the error! */
if (!panic_timeout)
- panic_timeout = ghes_panic_timeout;
- panic("Fatal hardware error!");
+ pr_emerg("%s but panic disabled\n", msg);
+
+ panic(msg);
}
static int ghes_proc(struct ghes *ghes)
@@ -893,7 +1205,7 @@ static void ghes_add_timer(struct ghes *ghes)
static void ghes_poll_func(struct timer_list *t)
{
- struct ghes *ghes = from_timer(ghes, t, timer);
+ struct ghes *ghes = timer_container_of(ghes, t, timer);
unsigned long flags;
spin_lock_irqsave(&ghes_notify_lock_irq, flags);
@@ -926,12 +1238,10 @@ static int ghes_notify_hed(struct notifier_block *this, unsigned long event,
int ret = NOTIFY_DONE;
spin_lock_irqsave(&ghes_notify_lock_irq, flags);
- rcu_read_lock();
list_for_each_entry_rcu(ghes, &ghes_hed, list) {
if (!ghes_proc(ghes))
ret = NOTIFY_OK;
}
- rcu_read_unlock();
spin_unlock_irqrestore(&ghes_notify_lock_irq, flags);
return ret;
@@ -961,9 +1271,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
struct ghes_estatus_node *estatus_node;
struct acpi_hest_generic *generic;
struct acpi_hest_generic_status *estatus;
- bool task_work_pending;
u32 len, node_len;
- int ret;
llnode = llist_del_all(&ghes_estatus_llist);
/*
@@ -978,25 +1286,16 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
len = cper_estatus_len(estatus);
node_len = GHES_ESTATUS_NODE_LEN(len);
- task_work_pending = ghes_do_proc(estatus_node->ghes, estatus);
+
+ ghes_do_proc(estatus_node->ghes, estatus);
+
if (!ghes_estatus_cached(estatus)) {
generic = estatus_node->generic;
if (ghes_print_estatus(NULL, generic, estatus))
ghes_estatus_cache_add(generic, estatus);
}
-
- if (task_work_pending && current->mm != &init_mm) {
- estatus_node->task_work.func = ghes_kick_task_work;
- estatus_node->task_work_cpu = smp_processor_id();
- ret = task_work_add(current, &estatus_node->task_work,
- TWA_RESUME);
- if (ret)
- estatus_node->task_work.func = NULL;
- }
-
- if (!estatus_node->task_work.func)
- gen_pool_free(ghes_estatus_pool,
- (unsigned long)estatus_node, node_len);
+ gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
+ node_len);
llnode = next;
}
@@ -1057,7 +1356,6 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
estatus_node->ghes = ghes;
estatus_node->generic = ghes->generic;
- estatus_node->task_work.func = NULL;
estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) {
@@ -1376,7 +1674,11 @@ static int ghes_probe(struct platform_device *ghes_dev)
platform_set_drvdata(ghes_dev, ghes);
- ghes_edac_register(ghes, &ghes_dev->dev);
+ ghes->dev = &ghes_dev->dev;
+
+ mutex_lock(&ghes_devs_mutex);
+ list_add_tail(&ghes->elist, &ghes_devs);
+ mutex_unlock(&ghes_devs_mutex);
/* Handle any pending errors right away */
spin_lock_irqsave(&ghes_notify_lock_irq, flags);
@@ -1393,7 +1695,7 @@ err:
return rc;
}
-static int ghes_remove(struct platform_device *ghes_dev)
+static void ghes_remove(struct platform_device *ghes_dev)
{
int rc;
struct ghes *ghes;
@@ -1405,7 +1707,7 @@ static int ghes_remove(struct platform_device *ghes_dev)
ghes->flags |= GHES_EXITING;
switch (generic->notify.type) {
case ACPI_HEST_NOTIFY_POLLED:
- del_timer_sync(&ghes->timer);
+ timer_shutdown_sync(&ghes->timer);
break;
case ACPI_HEST_NOTIFY_EXTERNAL:
free_irq(ghes->irq, ghes);
@@ -1430,8 +1732,15 @@ static int ghes_remove(struct platform_device *ghes_dev)
break;
case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
rc = apei_sdei_unregister_ghes(ghes);
- if (rc)
- return rc;
+ if (rc) {
+ /*
+ * Returning early results in a resource leak, but we're
+ * only here if stopping the hardware failed.
+ */
+ dev_err(&ghes_dev->dev, "Failed to unregister ghes (%pe)\n",
+ ERR_PTR(rc));
+ return;
+ }
break;
default:
BUG();
@@ -1440,13 +1749,11 @@ static int ghes_remove(struct platform_device *ghes_dev)
ghes_fini(ghes);
- ghes_edac_unregister(ghes);
+ mutex_lock(&ghes_devs_mutex);
+ list_del(&ghes->elist);
+ mutex_unlock(&ghes_devs_mutex);
kfree(ghes);
-
- platform_set_drvdata(ghes_dev, NULL);
-
- return 0;
}
static struct platform_driver ghes_platform_driver = {
@@ -1461,7 +1768,7 @@ void __init acpi_ghes_init(void)
{
int rc;
- sdei_init();
+ acpi_sdei_init();
if (acpi_disabled)
return;
@@ -1497,3 +1804,43 @@ void __init acpi_ghes_init(void)
else
pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
}
+
+/*
+ * Known x86 systems that prefer GHES error reporting:
+ */
+static struct acpi_platform_list plat_list[] = {
+ {"HPE ", "Server ", 0, ACPI_SIG_FADT, all_versions},
+ { } /* End */
+};
+
+struct list_head *ghes_get_devices(void)
+{
+ int idx = -1;
+
+ if (IS_ENABLED(CONFIG_X86)) {
+ idx = acpi_match_platform_list(plat_list);
+ if (idx < 0) {
+ if (!ghes_edac_force_enable)
+ return NULL;
+
+ pr_warn_once("Force-loading ghes_edac on an unsupported platform. You're on your own!\n");
+ }
+ } else if (list_empty(&ghes_devs)) {
+ return NULL;
+ }
+
+ return &ghes_devs;
+}
+EXPORT_SYMBOL_GPL(ghes_get_devices);
+
+void ghes_register_report_chain(struct notifier_block *nb)
+{
+ atomic_notifier_chain_register(&ghes_report_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_register_report_chain);
+
+void ghes_unregister_report_chain(struct notifier_block *nb)
+{
+ atomic_notifier_chain_unregister(&ghes_report_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_unregister_report_chain);
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index 6aef1ee5e1bd..20d757687e3d 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -37,6 +37,20 @@ EXPORT_SYMBOL_GPL(hest_disable);
static struct acpi_table_hest *__read_mostly hest_tab;
+/*
+ * Since GHES_ASSIST is not supported, skip initialization of GHES_ASSIST
+ * structures for MCA.
+ * During HEST parsing, detected MCA error sources are cached from early
+ * table entries so that the Flags and Source Id fields from these cached
+ * values are then referred to in later table entries to determine if the
+ * encountered GHES_ASSIST structure should be initialized.
+ */
+static struct {
+ struct acpi_hest_ia_corrected *cmc;
+ struct acpi_hest_ia_machine_check *mc;
+ struct acpi_hest_ia_deferred_check *dmc;
+} mces;
+
static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
[ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */
[ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
@@ -70,22 +84,54 @@ static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
len = sizeof(*cmc) + cmc->num_hardware_banks *
sizeof(struct acpi_hest_ia_error_bank);
+ mces.cmc = cmc;
} else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
struct acpi_hest_ia_machine_check *mc;
mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
len = sizeof(*mc) + mc->num_hardware_banks *
sizeof(struct acpi_hest_ia_error_bank);
+ mces.mc = mc;
} else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) {
struct acpi_hest_ia_deferred_check *mc;
mc = (struct acpi_hest_ia_deferred_check *)hest_hdr;
len = sizeof(*mc) + mc->num_hardware_banks *
sizeof(struct acpi_hest_ia_error_bank);
+ mces.dmc = mc;
}
BUG_ON(len == -1);
return len;
};
+/*
+ * GHES and GHESv2 structures share the same format, starting from
+ * Source Id and ending in Error Status Block Length (inclusive).
+ */
+static bool is_ghes_assist_struct(struct acpi_hest_header *hest_hdr)
+{
+ struct acpi_hest_generic *ghes;
+ u16 related_source_id;
+
+ if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR &&
+ hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2)
+ return false;
+
+ ghes = (struct acpi_hest_generic *)hest_hdr;
+ related_source_id = ghes->related_source_id;
+
+ if (mces.cmc && mces.cmc->flags & ACPI_HEST_GHES_ASSIST &&
+ related_source_id == mces.cmc->header.source_id)
+ return true;
+ if (mces.mc && mces.mc->flags & ACPI_HEST_GHES_ASSIST &&
+ related_source_id == mces.mc->header.source_id)
+ return true;
+ if (mces.dmc && mces.dmc->flags & ACPI_HEST_GHES_ASSIST &&
+ related_source_id == mces.dmc->header.source_id)
+ return true;
+
+ return false;
+}
+
typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data);
static int apei_hest_parse(apei_hest_func_t func, void *data)
@@ -114,6 +160,11 @@ static int apei_hest_parse(apei_hest_func_t func, void *data)
return -EINVAL;
}
+ if (is_ghes_assist_struct(hest_hdr)) {
+ hest_hdr = (void *)hest_hdr + len;
+ continue;
+ }
+
rc = func(hest_hdr, data);
if (rc)
return rc;
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
index d4a72835f328..f2fd79f22e7d 100644
--- a/drivers/acpi/arm64/Kconfig
+++ b/drivers/acpi/arm64/Kconfig
@@ -18,3 +18,9 @@ config ACPI_AGDI
reset command.
If set, the kernel parses AGDI table and listens for the command.
+
+config ACPI_APMT
+ bool
+
+config ACPI_MPAM
+ bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 7b9e4045659d..9390b57cb564 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -1,5 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ACPI_AGDI) += agdi.o
-obj-$(CONFIG_ACPI_IORT) += iort.o
+obj-$(CONFIG_ACPI_APMT) += apmt.o
+obj-$(CONFIG_ACPI_FFH) += ffh.o
obj-$(CONFIG_ACPI_GTDT) += gtdt.o
-obj-y += dma.o
+obj-$(CONFIG_ACPI_IORT) += iort.o
+obj-$(CONFIG_ACPI_MPAM) += mpam.o
+obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o
+obj-$(CONFIG_ARM_AMBA) += amba.o
+obj-y += dma.o init.o
+obj-y += thermal_cpufreq.o
diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c
index cf31abd0ed1b..e0df3daa4abf 100644
--- a/drivers/acpi/arm64/agdi.c
+++ b/drivers/acpi/arm64/agdi.c
@@ -9,11 +9,11 @@
#define pr_fmt(fmt) "ACPI: AGDI: " fmt
#include <linux/acpi.h>
-#include <linux/acpi_agdi.h>
#include <linux/arm_sdei.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include "init.h"
struct agdi_data {
int sdei_event;
@@ -58,14 +58,17 @@ static int agdi_probe(struct platform_device *pdev)
return agdi_sdei_probe(pdev, adata);
}
-static int agdi_remove(struct platform_device *pdev)
+static void agdi_remove(struct platform_device *pdev)
{
struct agdi_data *adata = dev_get_platdata(&pdev->dev);
int err, i;
err = sdei_event_disable(adata->sdei_event);
- if (err)
- return err;
+ if (err) {
+ dev_err(&pdev->dev, "Failed to disable sdei-event #%d (%pe)\n",
+ adata->sdei_event, ERR_PTR(err));
+ return;
+ }
for (i = 0; i < 3; i++) {
err = sdei_event_unregister(adata->sdei_event);
@@ -75,7 +78,9 @@ static int agdi_remove(struct platform_device *pdev)
schedule();
}
- return err;
+ if (err)
+ dev_err(&pdev->dev, "Failed to unregister sdei-event #%d (%pe)\n",
+ adata->sdei_event, ERR_PTR(err));
}
static struct platform_driver agdi_driver = {
diff --git a/drivers/acpi/arm64/amba.c b/drivers/acpi/arm64/amba.c
new file mode 100644
index 000000000000..1350083bce5f
--- /dev/null
+++ b/drivers/acpi/arm64/amba.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * ACPI support for platform bus type.
+ *
+ * Copyright (C) 2015, Linaro Ltd
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
+ */
+
+#include <linux/acpi.h>
+#include <linux/amba/bus.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "init.h"
+
+static const struct acpi_device_id amba_id_list[] = {
+ {"ARMH0061", 0}, /* PL061 GPIO Device */
+ {"ARMH0330", 0}, /* ARM DMA Controller DMA-330 */
+ {"", 0},
+};
+
+static void amba_register_dummy_clk(void)
+{
+ struct clk *amba_dummy_clk;
+
+ amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 0);
+ clk_register_clkdev(amba_dummy_clk, "apb_pclk", NULL);
+}
+
+static int amba_handler_attach(struct acpi_device *adev,
+ const struct acpi_device_id *id)
+{
+ struct acpi_device *parent = acpi_dev_parent(adev);
+ struct amba_device *dev;
+ struct resource_entry *rentry;
+ struct list_head resource_list;
+ bool address_found = false;
+ int irq_no = 0;
+ int ret;
+
+ /* If the ACPI node already has a physical device attached, skip it. */
+ if (adev->physical_node_count)
+ return 0;
+
+ dev = amba_device_alloc(dev_name(&adev->dev), 0, 0);
+ if (!dev) {
+ dev_err(&adev->dev, "%s(): amba_device_alloc() failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+ if (ret < 0)
+ goto err_free;
+
+ list_for_each_entry(rentry, &resource_list, node) {
+ switch (resource_type(rentry->res)) {
+ case IORESOURCE_MEM:
+ if (!address_found) {
+ dev->res = *rentry->res;
+ dev->res.name = dev_name(&dev->dev);
+ address_found = true;
+ }
+ break;
+ case IORESOURCE_IRQ:
+ if (irq_no < AMBA_NR_IRQS)
+ dev->irq[irq_no++] = rentry->res->start;
+ break;
+ default:
+ dev_warn(&adev->dev, "Invalid resource\n");
+ break;
+ }
+ }
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ /*
+ * If the ACPI node has a parent and that parent has a physical device
+ * attached to it, that physical device should be the parent of
+ * the amba device we are about to create.
+ */
+ if (parent)
+ dev->dev.parent = acpi_get_first_physical_node(parent);
+
+ device_set_node(&dev->dev, acpi_fwnode_handle(adev));
+
+ ret = amba_device_add(dev, &iomem_resource);
+ if (ret) {
+ dev_err(&adev->dev, "%s(): amba_device_add() failed (%d)\n",
+ __func__, ret);
+ goto err_free;
+ }
+
+ return 1;
+
+err_free:
+ amba_device_put(dev);
+ return ret;
+}
+
+static struct acpi_scan_handler amba_handler = {
+ .ids = amba_id_list,
+ .attach = amba_handler_attach,
+};
+
+void __init acpi_amba_init(void)
+{
+ amba_register_dummy_clk();
+ acpi_scan_add_handler(&amba_handler);
+}
diff --git a/drivers/acpi/arm64/apmt.c b/drivers/acpi/arm64/apmt.c
new file mode 100644
index 000000000000..bb010f6164e5
--- /dev/null
+++ b/drivers/acpi/arm64/apmt.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM APMT table support.
+ * Design document number: ARM DEN0117.
+ *
+ * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
+ *
+ */
+
+#define pr_fmt(fmt) "ACPI: APMT: " fmt
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include "init.h"
+
+#define DEV_NAME "arm-cs-arch-pmu"
+
+/* There can be up to 3 resources: page 0 and 1 address, and interrupt. */
+#define DEV_MAX_RESOURCE_COUNT 3
+
+/* Root pointer to the mapped APMT table */
+static struct acpi_table_header *apmt_table;
+
+static int __init apmt_init_resources(struct resource *res,
+ struct acpi_apmt_node *node)
+{
+ int irq, trigger;
+ int num_res = 0;
+
+ res[num_res].start = node->base_address0;
+ res[num_res].end = node->base_address0 + SZ_4K - 1;
+ res[num_res].flags = IORESOURCE_MEM;
+
+ num_res++;
+
+ if (node->flags & ACPI_APMT_FLAGS_DUAL_PAGE) {
+ res[num_res].start = node->base_address1;
+ res[num_res].end = node->base_address1 + SZ_4K - 1;
+ res[num_res].flags = IORESOURCE_MEM;
+
+ num_res++;
+ }
+
+ if (node->ovflw_irq != 0) {
+ trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE);
+ trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ?
+ ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
+ irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger,
+ ACPI_ACTIVE_HIGH);
+
+ if (irq <= 0) {
+ pr_warn("APMT could not register gsi hwirq %d\n", irq);
+ return num_res;
+ }
+
+ res[num_res].start = irq;
+ res[num_res].end = irq;
+ res[num_res].flags = IORESOURCE_IRQ;
+
+ num_res++;
+ }
+
+ return num_res;
+}
+
+/**
+ * apmt_add_platform_device() - Allocate a platform device for APMT node
+ * @node: Pointer to device ACPI APMT node
+ * @fwnode: fwnode associated with the APMT node
+ *
+ * Returns: 0 on success, <0 failure
+ */
+static int __init apmt_add_platform_device(struct acpi_apmt_node *node,
+ struct fwnode_handle *fwnode)
+{
+ struct platform_device *pdev;
+ int ret, count;
+ struct resource res[DEV_MAX_RESOURCE_COUNT];
+
+ pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return -ENOMEM;
+
+ memset(res, 0, sizeof(res));
+
+ count = apmt_init_resources(res, node);
+
+ ret = platform_device_add_resources(pdev, res, count);
+ if (ret)
+ goto dev_put;
+
+ /*
+ * Add a copy of APMT node pointer to platform_data to be used to
+ * retrieve APMT data information.
+ */
+ ret = platform_device_add_data(pdev, &node, sizeof(node));
+ if (ret)
+ goto dev_put;
+
+ pdev->dev.fwnode = fwnode;
+
+ ret = platform_device_add(pdev);
+
+ if (ret)
+ goto dev_put;
+
+ return 0;
+
+dev_put:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+static int __init apmt_init_platform_devices(void)
+{
+ struct acpi_apmt_node *apmt_node;
+ struct acpi_table_apmt *apmt;
+ struct fwnode_handle *fwnode;
+ u64 offset, end;
+ int ret;
+
+ /*
+ * apmt_table and apmt both point to the start of APMT table, but
+ * have different struct types
+ */
+ apmt = (struct acpi_table_apmt *)apmt_table;
+ offset = sizeof(*apmt);
+ end = apmt->header.length;
+
+ while (offset < end) {
+ apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt,
+ offset);
+
+ fwnode = acpi_alloc_fwnode_static();
+ if (!fwnode)
+ return -ENOMEM;
+
+ ret = apmt_add_platform_device(apmt_node, fwnode);
+ if (ret) {
+ acpi_free_fwnode_static(fwnode);
+ return ret;
+ }
+
+ offset += apmt_node->length;
+ }
+
+ return 0;
+}
+
+void __init acpi_apmt_init(void)
+{
+ acpi_status status;
+ int ret;
+
+ /**
+ * APMT table nodes will be used at runtime after the apmt init,
+ * so we don't need to call acpi_put_table() to release
+ * the APMT table mapping.
+ */
+ status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table);
+
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ const char *msg = acpi_format_exception(status);
+
+ pr_err("Failed to get APMT table, %s\n", msg);
+ }
+
+ return;
+ }
+
+ ret = apmt_init_platform_devices();
+ if (ret) {
+ pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret);
+ acpi_put_table(apmt_table);
+ }
+}
diff --git a/drivers/acpi/arm64/cpuidle.c b/drivers/acpi/arm64/cpuidle.c
new file mode 100644
index 000000000000..801f9c450142
--- /dev/null
+++ b/drivers/acpi/arm64/cpuidle.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ARM64 CPU idle arch support
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+#include <linux/psci.h>
+#include <acpi/processor.h>
+
+#define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags))
+
+static int psci_acpi_cpu_init_idle(unsigned int cpu)
+{
+ int i, count;
+ struct acpi_lpi_state *lpi;
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+
+ if (unlikely(!pr || !pr->flags.has_lpi))
+ return -EINVAL;
+
+ /*
+ * If the PSCI cpu_suspend function hook has not been initialized
+ * idle states must not be enabled, so bail out
+ */
+ if (!psci_ops.cpu_suspend)
+ return -EOPNOTSUPP;
+
+ count = pr->power.count - 1;
+ if (count <= 0)
+ return -ENODEV;
+
+ for (i = 0; i < count; i++) {
+ u32 state;
+
+ lpi = &pr->power.lpi_states[i + 1];
+ /*
+ * Only bits[31:0] represent a PSCI power_state while
+ * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification
+ */
+ state = lpi->address;
+ if (!psci_power_state_is_valid(state)) {
+ pr_warn("Invalid PSCI power state %#x\n", state);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int acpi_processor_ffh_lpi_probe(unsigned int cpu)
+{
+ return psci_acpi_cpu_init_idle(cpu);
+}
+
+__cpuidle int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
+{
+ u32 state = lpi->address;
+
+ if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags))
+ return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM_RCU(psci_cpu_suspend_enter,
+ lpi->index, state);
+ else
+ return CPU_PM_CPU_IDLE_ENTER_PARAM_RCU(psci_cpu_suspend_enter,
+ lpi->index, state);
+}
diff --git a/drivers/acpi/arm64/dma.c b/drivers/acpi/arm64/dma.c
index f16739ad3cc0..f30f138352b7 100644
--- a/drivers/acpi/arm64/dma.c
+++ b/drivers/acpi/arm64/dma.c
@@ -4,11 +4,11 @@
#include <linux/device.h>
#include <linux/dma-direct.h>
-void acpi_arch_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
+void acpi_arch_dma_setup(struct device *dev)
{
int ret;
u64 end, mask;
- u64 dmaaddr = 0, size = 0, offset = 0;
+ const struct bus_dma_region *map = NULL;
/*
* If @dev is expected to be DMA-capable then the bus code that created
@@ -22,29 +22,31 @@ void acpi_arch_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
}
if (dev->coherent_dma_mask)
- size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1);
+ end = dev->coherent_dma_mask;
else
- size = 1ULL << 32;
+ end = (1ULL << 32) - 1;
+
+ if (dev->dma_range_map) {
+ dev_dbg(dev, "dma_range_map already set\n");
+ return;
+ }
+
+ ret = acpi_dma_get_range(dev, &map);
+ if (!ret && map) {
+ end = dma_range_map_max(map);
+ dev->dma_range_map = map;
+ }
- ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size);
if (ret == -ENODEV)
- ret = iort_dma_get_ranges(dev, &size);
+ ret = iort_dma_get_ranges(dev, &end);
if (!ret) {
/*
* Limit coherent and dma mask based on size retrieved from
* firmware.
*/
- end = dmaaddr + size - 1;
mask = DMA_BIT_MASK(ilog2(end) + 1);
dev->bus_dma_limit = end;
dev->coherent_dma_mask = min(dev->coherent_dma_mask, mask);
*dev->dma_mask = min(*dev->dma_mask, mask);
}
-
- *dma_addr = dmaaddr;
- *dma_size = size;
-
- ret = dma_direct_set_offset(dev, dmaaddr + offset, dmaaddr, size);
-
- dev_dbg(dev, "dma_offset(%#08llx)%s\n", offset, ret ? " failed!" : "");
}
diff --git a/drivers/acpi/arm64/ffh.c b/drivers/acpi/arm64/ffh.c
new file mode 100644
index 000000000000..877edc6557e9
--- /dev/null
+++ b/drivers/acpi/arm64/ffh.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/slab.h>
+
+/*
+ * Implements ARM64 specific callbacks to support ACPI FFH Operation Region as
+ * specified in https://developer.arm.com/docs/den0048/latest
+ */
+struct acpi_ffh_data {
+ struct acpi_ffh_info info;
+ void (*invoke_ffh_fn)(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *args,
+ struct arm_smccc_quirk *res);
+ void (*invoke_ffh64_fn)(const struct arm_smccc_1_2_regs *args,
+ struct arm_smccc_1_2_regs *res);
+};
+
+int acpi_ffh_address_space_arch_setup(void *handler_ctxt, void **region_ctxt)
+{
+ enum arm_smccc_conduit conduit;
+ struct acpi_ffh_data *ffh_ctxt;
+
+ if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
+ return -EOPNOTSUPP;
+
+ conduit = arm_smccc_1_1_get_conduit();
+ if (conduit == SMCCC_CONDUIT_NONE) {
+ pr_err("%s: invalid SMCCC conduit\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ ffh_ctxt = kzalloc(sizeof(*ffh_ctxt), GFP_KERNEL);
+ if (!ffh_ctxt)
+ return -ENOMEM;
+
+ if (conduit == SMCCC_CONDUIT_SMC) {
+ ffh_ctxt->invoke_ffh_fn = __arm_smccc_smc;
+ ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_smc;
+ } else {
+ ffh_ctxt->invoke_ffh_fn = __arm_smccc_hvc;
+ ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_hvc;
+ }
+
+ memcpy(ffh_ctxt, handler_ctxt, sizeof(ffh_ctxt->info));
+
+ *region_ctxt = ffh_ctxt;
+ return AE_OK;
+}
+
+static bool acpi_ffh_smccc_owner_allowed(u32 fid)
+{
+ int owner = ARM_SMCCC_OWNER_NUM(fid);
+
+ if (owner == ARM_SMCCC_OWNER_STANDARD ||
+ owner == ARM_SMCCC_OWNER_SIP || owner == ARM_SMCCC_OWNER_OEM)
+ return true;
+
+ return false;
+}
+
+int acpi_ffh_address_space_arch_handler(acpi_integer *value, void *region_context)
+{
+ int ret = 0;
+ struct acpi_ffh_data *ffh_ctxt = region_context;
+
+ if (ffh_ctxt->info.offset == 0) {
+ /* SMC/HVC 32bit call */
+ struct arm_smccc_res res;
+ u32 a[8] = { 0 }, *ptr = (u32 *)value;
+
+ if (!ARM_SMCCC_IS_FAST_CALL(*ptr) || ARM_SMCCC_IS_64(*ptr) ||
+ !acpi_ffh_smccc_owner_allowed(*ptr) ||
+ ffh_ctxt->info.length > 32) {
+ ret = AE_ERROR;
+ } else {
+ int idx, len = ffh_ctxt->info.length >> 2;
+
+ for (idx = 0; idx < len; idx++)
+ a[idx] = *(ptr + idx);
+
+ ffh_ctxt->invoke_ffh_fn(a[0], a[1], a[2], a[3], a[4],
+ a[5], a[6], a[7], &res, NULL);
+ memcpy(value, &res, sizeof(res));
+ }
+
+ } else if (ffh_ctxt->info.offset == 1) {
+ /* SMC/HVC 64bit call */
+ struct arm_smccc_1_2_regs *r = (struct arm_smccc_1_2_regs *)value;
+
+ if (!ARM_SMCCC_IS_FAST_CALL(r->a0) || !ARM_SMCCC_IS_64(r->a0) ||
+ !acpi_ffh_smccc_owner_allowed(r->a0) ||
+ ffh_ctxt->info.length > sizeof(*r)) {
+ ret = AE_ERROR;
+ } else {
+ ffh_ctxt->invoke_ffh64_fn(r, r);
+ memcpy(value, r, ffh_ctxt->info.length);
+ }
+ } else {
+ ret = AE_ERROR;
+ }
+
+ return ret;
+}
diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
index c0e77c1c8e09..ffc867bac2d6 100644
--- a/drivers/acpi/arm64/gtdt.c
+++ b/drivers/acpi/arm64/gtdt.c
@@ -36,19 +36,25 @@ struct acpi_gtdt_descriptor {
static struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata;
-static inline __init void *next_platform_timer(void *platform_timer)
+static __init bool platform_timer_valid(void *platform_timer)
{
struct acpi_gtdt_header *gh = platform_timer;
- platform_timer += gh->length;
- if (platform_timer < acpi_gtdt_desc.gtdt_end)
- return platform_timer;
+ return (platform_timer >= (void *)(acpi_gtdt_desc.gtdt + 1) &&
+ platform_timer < acpi_gtdt_desc.gtdt_end &&
+ gh->length != 0 &&
+ platform_timer + gh->length <= acpi_gtdt_desc.gtdt_end);
+}
+
+static __init void *next_platform_timer(void *platform_timer)
+{
+ struct acpi_gtdt_header *gh = platform_timer;
- return NULL;
+ return platform_timer + gh->length;
}
#define for_each_platform_timer(_g) \
- for (_g = acpi_gtdt_desc.platform_timer; _g; \
+ for (_g = acpi_gtdt_desc.platform_timer; platform_timer_valid(_g);\
_g = next_platform_timer(_g))
static inline bool is_timer_block(void *platform_timer)
@@ -157,6 +163,7 @@ int __init acpi_gtdt_init(struct acpi_table_header *table,
{
void *platform_timer;
struct acpi_table_gtdt *gtdt;
+ u32 cnt = 0;
gtdt = container_of(table, struct acpi_table_gtdt, header);
acpi_gtdt_desc.gtdt = gtdt;
@@ -176,14 +183,22 @@ int __init acpi_gtdt_init(struct acpi_table_header *table,
return 0;
}
- platform_timer = (void *)gtdt + gtdt->platform_timer_offset;
- if (platform_timer < (void *)table + sizeof(struct acpi_table_gtdt)) {
- pr_err(FW_BUG "invalid timer data.\n");
- return -EINVAL;
+ acpi_gtdt_desc.platform_timer = (void *)gtdt + gtdt->platform_timer_offset;
+ for_each_platform_timer(platform_timer)
+ cnt++;
+
+ if (cnt != gtdt->platform_timer_count) {
+ cnt = min(cnt, gtdt->platform_timer_count);
+ pr_err(FW_BUG "limiting Platform Timer count to %d\n", cnt);
}
- acpi_gtdt_desc.platform_timer = platform_timer;
+
+ if (!cnt) {
+ acpi_gtdt_desc.platform_timer = NULL;
+ return 0;
+ }
+
if (platform_timer_count)
- *platform_timer_count = gtdt->platform_timer_count;
+ *platform_timer_count = cnt;
return 0;
}
@@ -283,45 +298,11 @@ error:
if (frame->virt_irq > 0)
acpi_unregister_gsi(gtdt_frame->virtual_timer_interrupt);
frame->virt_irq = 0;
- } while (i-- >= 0 && gtdt_frame--);
+ } while (i-- > 0 && gtdt_frame--);
return -EINVAL;
}
-/**
- * acpi_arch_timer_mem_init() - Get the info of all GT blocks in GTDT table.
- * @timer_mem: The pointer to the array of struct arch_timer_mem for returning
- * the result of parsing. The element number of this array should
- * be platform_timer_count(the total number of platform timers).
- * @timer_count: It points to a integer variable which is used for storing the
- * number of GT blocks we have parsed.
- *
- * Return: 0 if success, -EINVAL/-ENODEV if error.
- */
-int __init acpi_arch_timer_mem_init(struct arch_timer_mem *timer_mem,
- int *timer_count)
-{
- int ret;
- void *platform_timer;
-
- *timer_count = 0;
- for_each_platform_timer(platform_timer) {
- if (is_timer_block(platform_timer)) {
- ret = gtdt_parse_timer_block(platform_timer, timer_mem);
- if (ret)
- return ret;
- timer_mem++;
- (*timer_count)++;
- }
- }
-
- if (*timer_count)
- pr_info("found %d memory-mapped timer block(s).\n",
- *timer_count);
-
- return 0;
-}
-
/*
* Initialize a SBSA generic Watchdog platform device info from GTDT
*/
@@ -352,7 +333,7 @@ static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd,
}
irq = map_gt_gsi(wd->timer_interrupt, wd->timer_flags);
- res[2] = (struct resource)DEFINE_RES_IRQ(irq);
+ res[2] = DEFINE_RES_IRQ(irq);
if (irq <= 0) {
pr_warn("failed to map the Watchdog interrupt.\n");
nr_res--;
@@ -373,11 +354,11 @@ static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd,
return 0;
}
-static int __init gtdt_sbsa_gwdt_init(void)
+static int __init gtdt_platform_timer_init(void)
{
void *platform_timer;
struct acpi_table_header *table;
- int ret, timer_count, gwdt_count = 0;
+ int ret, timer_count, gwdt_count = 0, mmio_timer_count = 0;
if (acpi_disabled)
return 0;
@@ -399,20 +380,41 @@ static int __init gtdt_sbsa_gwdt_init(void)
goto out_put_gtdt;
for_each_platform_timer(platform_timer) {
+ ret = 0;
+
if (is_non_secure_watchdog(platform_timer)) {
ret = gtdt_import_sbsa_gwdt(platform_timer, gwdt_count);
if (ret)
- break;
+ continue;
gwdt_count++;
+ } else if (is_timer_block(platform_timer)) {
+ struct arch_timer_mem atm = {};
+ struct platform_device *pdev;
+
+ ret = gtdt_parse_timer_block(platform_timer, &atm);
+ if (ret)
+ continue;
+
+ pdev = platform_device_register_data(NULL, "gtdt-arm-mmio-timer",
+ mmio_timer_count, &atm,
+ sizeof(atm));
+ if (IS_ERR(pdev)) {
+ pr_err("Can't register timer %d\n", mmio_timer_count);
+ continue;
+ }
+
+ mmio_timer_count++;
}
}
if (gwdt_count)
pr_info("found %d SBSA generic Watchdog(s).\n", gwdt_count);
+ if (mmio_timer_count)
+ pr_info("found %d Generic MMIO timer(s).\n", mmio_timer_count);
out_put_gtdt:
acpi_put_table(table);
return ret;
}
-device_initcall(gtdt_sbsa_gwdt_init);
+device_initcall(gtdt_platform_timer_init);
diff --git a/drivers/acpi/arm64/init.c b/drivers/acpi/arm64/init.c
new file mode 100644
index 000000000000..7a47d8095a7d
--- /dev/null
+++ b/drivers/acpi/arm64/init.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/acpi.h>
+#include "init.h"
+
+void __init acpi_arch_init(void)
+{
+ if (IS_ENABLED(CONFIG_ACPI_AGDI))
+ acpi_agdi_init();
+ if (IS_ENABLED(CONFIG_ACPI_APMT))
+ acpi_apmt_init();
+ if (IS_ENABLED(CONFIG_ACPI_IORT))
+ acpi_iort_init();
+ if (IS_ENABLED(CONFIG_ARM_AMBA))
+ acpi_amba_init();
+}
diff --git a/drivers/acpi/arm64/init.h b/drivers/acpi/arm64/init.h
new file mode 100644
index 000000000000..dcc277977194
--- /dev/null
+++ b/drivers/acpi/arm64/init.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/init.h>
+
+void __init acpi_agdi_init(void);
+void __init acpi_apmt_init(void);
+void __init acpi_iort_init(void);
+void __init acpi_amba_init(void);
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index ca2aed86b540..65f0f56ad753 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-map-ops.h>
+#include "init.h"
#define IORT_TYPE_MASK(type) (1 << (type))
#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -402,6 +403,10 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
return NULL;
}
+#ifndef ACPI_IORT_SMMU_V3_DEVICEID_VALID
+#define ACPI_IORT_SMMU_V3_DEVICEID_VALID (1 << 4)
+#endif
+
static int iort_get_id_mapping_index(struct acpi_iort_node *node)
{
struct acpi_iort_smmu_v3 *smmu;
@@ -418,12 +423,16 @@ static int iort_get_id_mapping_index(struct acpi_iort_node *node)
smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
/*
- * ID mapping index is only ignored if all interrupts are
- * GSIV based
+ * Until IORT E.e (node rev. 5), the ID mapping index was
+ * defined to be valid unless all interrupts are GSIV-based.
*/
- if (smmu->event_gsiv && smmu->pri_gsiv && smmu->gerr_gsiv
- && smmu->sync_gsiv)
+ if (node->revision < 5) {
+ if (smmu->event_gsiv && smmu->pri_gsiv &&
+ smmu->gerr_gsiv && smmu->sync_gsiv)
+ return -EINVAL;
+ } else if (!(smmu->flags & ACPI_IORT_SMMU_V3_DEVICEID_VALID)) {
return -EINVAL;
+ }
if (smmu->id_mapping_index >= node->mapping_count) {
pr_err(FW_BUG "[node %p type %d] ID mapping index overflows valid mappings\n",
@@ -813,7 +822,7 @@ static struct iommu_iort_rmr_data *iort_rmr_alloc(
return NULL;
/* Create a copy of SIDs array to associate with this rmr_data */
- sids_copy = kmemdup(sids, num_sids * sizeof(*sids), GFP_KERNEL);
+ sids_copy = kmemdup_array(sids, num_sids, sizeof(*sids), GFP_KERNEL);
if (!sids_copy) {
kfree(rmr_data);
return NULL;
@@ -928,8 +937,10 @@ static u32 *iort_rmr_alloc_sids(u32 *sids, u32 count, u32 id_start,
new_sids = krealloc_array(sids, count + new_count,
sizeof(*new_sids), GFP_KERNEL);
- if (!new_sids)
+ if (!new_sids) {
+ kfree(sids);
return NULL;
+ }
for (i = count; i < total_count; i++)
new_sids[i] = id_start++;
@@ -998,9 +1009,6 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
for (i = 0; i < node->mapping_count; i++, map++) {
struct acpi_iort_node *parent;
- if (!map->id_count)
- continue;
-
parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
map->output_reference);
if (parent != iommu)
@@ -1142,7 +1150,8 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
struct iommu_resv_region *region;
region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K,
- prot, IOMMU_RESV_MSI);
+ prot, IOMMU_RESV_MSI,
+ GFP_KERNEL);
if (region)
list_add_tail(&region->list, head);
}
@@ -1211,13 +1220,24 @@ static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED;
}
+static bool iort_pci_rc_supports_canwbs(struct acpi_iort_node *node)
+{
+ struct acpi_iort_memory_access *memory_access;
+ struct acpi_iort_root_complex *pci_rc;
+
+ pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+ memory_access =
+ (struct acpi_iort_memory_access *)&pci_rc->memory_properties;
+ return memory_access->memory_flags & ACPI_IORT_MF_CANWBS;
+}
+
static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
u32 streamid)
{
- const struct iommu_ops *ops;
struct fwnode_handle *iort_fwnode;
- if (!node)
+ /* If there's no SMMU driver at all, give up now */
+ if (!node || !iort_iommu_driver_enabled(node->type))
return -ENODEV;
iort_fwnode = iort_get_fwnode(node);
@@ -1225,19 +1245,10 @@ static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
return -ENODEV;
/*
- * If the ops look-up fails, this means that either
- * the SMMU drivers have not been probed yet or that
- * the SMMU drivers are not built in the kernel;
- * Depending on whether the SMMU drivers are built-in
- * in the kernel or not, defer the IOMMU configuration
- * or just abort it.
+ * If the SMMU drivers are enabled but not loaded/probed
+ * yet, this will defer.
*/
- ops = iommu_ops_from_fwnode(iort_fwnode);
- if (!ops)
- return iort_iommu_driver_enabled(node->type) ?
- -EPROBE_DEFER : -ENODEV;
-
- return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, ops);
+ return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode);
}
struct iort_pci_alias_info {
@@ -1337,6 +1348,8 @@ int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
fwspec = dev_iommu_fwspec_get(dev);
if (fwspec && iort_pci_rc_supports_ats(node))
fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
+ if (fwspec && iort_pci_rc_supports_canwbs(node))
+ fwspec->flags |= IOMMU_FWSPEC_PCI_RC_CANWBS;
} else {
node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
iort_match_node_callback, dev);
@@ -1360,7 +1373,7 @@ int iort_iommu_configure_id(struct device *dev, const u32 *input_id)
{ return -ENODEV; }
#endif
-static int nc_dma_get_range(struct device *dev, u64 *size)
+static int nc_dma_get_range(struct device *dev, u64 *limit)
{
struct acpi_iort_node *node;
struct acpi_iort_named_component *ncomp;
@@ -1377,13 +1390,13 @@ static int nc_dma_get_range(struct device *dev, u64 *size)
return -EINVAL;
}
- *size = ncomp->memory_address_limit >= 64 ? U64_MAX :
- 1ULL<<ncomp->memory_address_limit;
+ *limit = ncomp->memory_address_limit >= 64 ? U64_MAX :
+ (1ULL << ncomp->memory_address_limit) - 1;
return 0;
}
-static int rc_dma_get_range(struct device *dev, u64 *size)
+static int rc_dma_get_range(struct device *dev, u64 *limit)
{
struct acpi_iort_node *node;
struct acpi_iort_root_complex *rc;
@@ -1401,8 +1414,8 @@ static int rc_dma_get_range(struct device *dev, u64 *size)
return -EINVAL;
}
- *size = rc->memory_address_limit >= 64 ? U64_MAX :
- 1ULL<<rc->memory_address_limit;
+ *limit = rc->memory_address_limit >= 64 ? U64_MAX :
+ (1ULL << rc->memory_address_limit) - 1;
return 0;
}
@@ -1410,16 +1423,16 @@ static int rc_dma_get_range(struct device *dev, u64 *size)
/**
* iort_dma_get_ranges() - Look up DMA addressing limit for the device
* @dev: device to lookup
- * @size: DMA range size result pointer
+ * @limit: DMA limit result pointer
*
* Return: 0 on success, an error otherwise.
*/
-int iort_dma_get_ranges(struct device *dev, u64 *size)
+int iort_dma_get_ranges(struct device *dev, u64 *limit)
{
if (dev_is_pci(dev))
- return rc_dma_get_range(dev, size);
+ return rc_dma_get_range(dev, limit);
else
- return nc_dma_get_range(dev, size);
+ return nc_dma_get_range(dev, limit);
}
static void __init acpi_iort_register_irq(int hwirq, const char *name,
@@ -1701,7 +1714,19 @@ static void __init arm_smmu_v3_pmcg_init_resources(struct resource *res,
static struct acpi_platform_list pmcg_plat_info[] __initdata = {
/* HiSilicon Hip08 Platform */
{"HISI ", "HIP08 ", 0, ACPI_SIG_IORT, greater_than_or_equal,
- "Erratum #162001800", IORT_SMMU_V3_PMCG_HISI_HIP08},
+ "Erratum #162001800, Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP08},
+ /* HiSilicon Hip09 Platform */
+ {"HISI ", "HIP09 ", 0, ACPI_SIG_IORT, greater_than_or_equal,
+ "Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP09},
+ {"HISI ", "HIP09A ", 0, ACPI_SIG_IORT, greater_than_or_equal,
+ "Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP09},
+ /* HiSilicon Hip10/11 Platform uses the same SMMU IP with Hip09 */
+ {"HISI ", "HIP10 ", 0, ACPI_SIG_IORT, greater_than_or_equal,
+ "Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP09},
+ {"HISI ", "HIP10C ", 0, ACPI_SIG_IORT, greater_than_or_equal,
+ "Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP09},
+ {"HISI ", "HIP11 ", 0, ACPI_SIG_IORT, greater_than_or_equal,
+ "Erratum #162001900", IORT_SMMU_V3_PMCG_HISI_HIP09},
{ }
};
diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c
new file mode 100644
index 000000000000..84963a20c3e7
--- /dev/null
+++ b/drivers/acpi/arm64/mpam.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Arm Ltd.
+
+/* Parse the MPAM ACPI table feeding the discovered nodes into the driver */
+
+#define pr_fmt(fmt) "ACPI MPAM: " fmt
+
+#include <linux/acpi.h>
+#include <linux/arm_mpam.h>
+#include <linux/bits.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/platform_device.h>
+
+#include <acpi/processor.h>
+
+/*
+ * Flags for acpi_table_mpam_msc.*_interrupt_flags.
+ * See 2.1.1 Interrupt Flags, Table 5, of DEN0065B_MPAM_ACPI_3.0-bet.
+ */
+#define ACPI_MPAM_MSC_IRQ_MODE BIT(0)
+#define ACPI_MPAM_MSC_IRQ_TYPE_MASK GENMASK(2, 1)
+#define ACPI_MPAM_MSC_IRQ_TYPE_WIRED 0
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_MASK BIT(3)
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_PROCESSOR 0
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_PROCESSOR_CONTAINER 1
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_VALID BIT(4)
+
+/*
+ * Encodings for the MSC node body interface type field.
+ * See 2.1 MPAM MSC node, Table 4 of DEN0065B_MPAM_ACPI_3.0-bet.
+ */
+#define ACPI_MPAM_MSC_IFACE_MMIO 0x00
+#define ACPI_MPAM_MSC_IFACE_PCC 0x0a
+
+static bool _is_ppi_partition(u32 flags)
+{
+ u32 aff_type, is_ppi;
+ bool ret;
+
+ is_ppi = FIELD_GET(ACPI_MPAM_MSC_IRQ_AFFINITY_VALID, flags);
+ if (!is_ppi)
+ return false;
+
+ aff_type = FIELD_GET(ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_MASK, flags);
+ ret = (aff_type == ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_PROCESSOR_CONTAINER);
+ if (ret)
+ pr_err_once("Partitioned interrupts not supported\n");
+
+ return ret;
+}
+
+static int acpi_mpam_register_irq(struct platform_device *pdev,
+ u32 intid, u32 flags)
+{
+ int irq;
+ u32 int_type;
+ int trigger;
+
+ if (!intid)
+ return -EINVAL;
+
+ if (_is_ppi_partition(flags))
+ return -EINVAL;
+
+ trigger = FIELD_GET(ACPI_MPAM_MSC_IRQ_MODE, flags);
+ int_type = FIELD_GET(ACPI_MPAM_MSC_IRQ_TYPE_MASK, flags);
+ if (int_type != ACPI_MPAM_MSC_IRQ_TYPE_WIRED)
+ return -EINVAL;
+
+ irq = acpi_register_gsi(&pdev->dev, intid, trigger, ACPI_ACTIVE_HIGH);
+ if (irq < 0)
+ pr_err_once("Failed to register interrupt 0x%x with ACPI\n", intid);
+
+ return irq;
+}
+
+static void acpi_mpam_parse_irqs(struct platform_device *pdev,
+ struct acpi_mpam_msc_node *tbl_msc,
+ struct resource *res, int *res_idx)
+{
+ u32 flags, intid;
+ int irq;
+
+ intid = tbl_msc->overflow_interrupt;
+ flags = tbl_msc->overflow_interrupt_flags;
+ irq = acpi_mpam_register_irq(pdev, intid, flags);
+ if (irq > 0)
+ res[(*res_idx)++] = DEFINE_RES_IRQ_NAMED(irq, "overflow");
+
+ intid = tbl_msc->error_interrupt;
+ flags = tbl_msc->error_interrupt_flags;
+ irq = acpi_mpam_register_irq(pdev, intid, flags);
+ if (irq > 0)
+ res[(*res_idx)++] = DEFINE_RES_IRQ_NAMED(irq, "error");
+}
+
+static int acpi_mpam_parse_resource(struct mpam_msc *msc,
+ struct acpi_mpam_resource_node *res)
+{
+ int level, nid;
+ u32 cache_id;
+
+ switch (res->locator_type) {
+ case ACPI_MPAM_LOCATION_TYPE_PROCESSOR_CACHE:
+ cache_id = res->locator.cache_locator.cache_reference;
+ level = find_acpi_cache_level_from_id(cache_id);
+ if (level <= 0) {
+ pr_err_once("Bad level (%d) for cache with id %u\n", level, cache_id);
+ return -EINVAL;
+ }
+ return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_CACHE,
+ level, cache_id);
+ case ACPI_MPAM_LOCATION_TYPE_MEMORY:
+ nid = pxm_to_node(res->locator.memory_locator.proximity_domain);
+ if (nid == NUMA_NO_NODE) {
+ pr_debug("Bad proximity domain %lld, using node 0 instead\n",
+ res->locator.memory_locator.proximity_domain);
+ nid = 0;
+ }
+ return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_MEMORY,
+ MPAM_CLASS_ID_DEFAULT, nid);
+ default:
+ /* These get discovered later and are treated as unknown */
+ return 0;
+ }
+}
+
+int acpi_mpam_parse_resources(struct mpam_msc *msc,
+ struct acpi_mpam_msc_node *tbl_msc)
+{
+ int i, err;
+ char *ptr, *table_end;
+ struct acpi_mpam_resource_node *resource;
+
+ table_end = (char *)tbl_msc + tbl_msc->length;
+ ptr = (char *)(tbl_msc + 1);
+ for (i = 0; i < tbl_msc->num_resource_nodes; i++) {
+ u64 max_deps, remaining_table;
+
+ if (ptr + sizeof(*resource) > table_end)
+ return -EINVAL;
+
+ resource = (struct acpi_mpam_resource_node *)ptr;
+
+ remaining_table = table_end - ptr;
+ max_deps = remaining_table / sizeof(struct acpi_mpam_func_deps);
+ if (resource->num_functional_deps > max_deps) {
+ pr_debug("MSC has impossible number of functional dependencies\n");
+ return -EINVAL;
+ }
+
+ err = acpi_mpam_parse_resource(msc, resource);
+ if (err)
+ return err;
+
+ ptr += sizeof(*resource);
+ ptr += resource->num_functional_deps * sizeof(struct acpi_mpam_func_deps);
+ }
+
+ return 0;
+}
+
+/*
+ * Creates the device power management link and returns true if the
+ * acpi id is valid and usable for cpu affinity. This is the case
+ * when the linked device is a processor or a processor container.
+ */
+static bool __init parse_msc_pm_link(struct acpi_mpam_msc_node *tbl_msc,
+ struct platform_device *pdev,
+ u32 *acpi_id)
+{
+ char hid[sizeof(tbl_msc->hardware_id_linked_device) + 1] = { 0 };
+ bool acpi_id_valid = false;
+ struct acpi_device *buddy;
+ char uid[11];
+ int len;
+
+ memcpy(hid, &tbl_msc->hardware_id_linked_device,
+ sizeof(tbl_msc->hardware_id_linked_device));
+
+ if (!strcmp(hid, ACPI_PROCESSOR_CONTAINER_HID)) {
+ *acpi_id = tbl_msc->instance_id_linked_device;
+ acpi_id_valid = true;
+ }
+
+ len = snprintf(uid, sizeof(uid), "%u",
+ tbl_msc->instance_id_linked_device);
+ if (len >= sizeof(uid)) {
+ pr_debug("Failed to convert uid of device for power management.");
+ return acpi_id_valid;
+ }
+
+ buddy = acpi_dev_get_first_match_dev(hid, uid, -1);
+ if (buddy) {
+ device_link_add(&pdev->dev, &buddy->dev, DL_FLAG_STATELESS);
+ acpi_dev_put(buddy);
+ }
+
+ return acpi_id_valid;
+}
+
+static int decode_interface_type(struct acpi_mpam_msc_node *tbl_msc,
+ enum mpam_msc_iface *iface)
+{
+ switch (tbl_msc->interface_type) {
+ case ACPI_MPAM_MSC_IFACE_MMIO:
+ *iface = MPAM_IFACE_MMIO;
+ return 0;
+ case ACPI_MPAM_MSC_IFACE_PCC:
+ *iface = MPAM_IFACE_PCC;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct platform_device * __init acpi_mpam_parse_msc(struct acpi_mpam_msc_node *tbl_msc)
+{
+ struct platform_device *pdev __free(platform_device_put) =
+ platform_device_alloc("mpam_msc", tbl_msc->identifier);
+ int next_res = 0, next_prop = 0, err;
+ /* pcc, nrdy, affinity and a sentinel */
+ struct property_entry props[4] = { 0 };
+ /* mmio, 2xirq, no sentinel. */
+ struct resource res[3] = { 0 };
+ struct acpi_device *companion;
+ enum mpam_msc_iface iface;
+ char uid[16];
+ u32 acpi_id;
+
+ if (!pdev)
+ return ERR_PTR(-ENOMEM);
+
+ /* Some power management is described in the namespace: */
+ err = snprintf(uid, sizeof(uid), "%u", tbl_msc->identifier);
+ if (err > 0 && err < sizeof(uid)) {
+ companion = acpi_dev_get_first_match_dev("ARMHAA5C", uid, -1);
+ if (companion) {
+ ACPI_COMPANION_SET(&pdev->dev, companion);
+ acpi_dev_put(companion);
+ } else {
+ pr_debug("MSC.%u: missing namespace entry\n", tbl_msc->identifier);
+ }
+ }
+
+ if (decode_interface_type(tbl_msc, &iface)) {
+ pr_debug("MSC.%u: unknown interface type\n", tbl_msc->identifier);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (iface == MPAM_IFACE_MMIO) {
+ res[next_res++] = DEFINE_RES_MEM_NAMED(tbl_msc->base_address,
+ tbl_msc->mmio_size,
+ "MPAM:MSC");
+ } else if (iface == MPAM_IFACE_PCC) {
+ props[next_prop++] = PROPERTY_ENTRY_U32("pcc-channel",
+ tbl_msc->base_address);
+ }
+
+ acpi_mpam_parse_irqs(pdev, tbl_msc, res, &next_res);
+
+ WARN_ON_ONCE(next_res > ARRAY_SIZE(res));
+ err = platform_device_add_resources(pdev, res, next_res);
+ if (err)
+ return ERR_PTR(err);
+
+ props[next_prop++] = PROPERTY_ENTRY_U32("arm,not-ready-us",
+ tbl_msc->max_nrdy_usec);
+
+ /*
+ * The MSC's CPU affinity is described via its linked power
+ * management device, but only if it points at a Processor or
+ * Processor Container.
+ */
+ if (parse_msc_pm_link(tbl_msc, pdev, &acpi_id))
+ props[next_prop++] = PROPERTY_ENTRY_U32("cpu_affinity", acpi_id);
+
+ WARN_ON_ONCE(next_prop > ARRAY_SIZE(props) - 1);
+ err = device_create_managed_software_node(&pdev->dev, props, NULL);
+ if (err)
+ return ERR_PTR(err);
+
+ /*
+ * Stash the table entry for acpi_mpam_parse_resources() to discover
+ * what this MSC controls.
+ */
+ err = platform_device_add_data(pdev, tbl_msc, tbl_msc->length);
+ if (err)
+ return ERR_PTR(err);
+
+ err = platform_device_add(pdev);
+ if (err)
+ return ERR_PTR(err);
+
+ return_ptr(pdev);
+}
+
+static int __init acpi_mpam_parse(void)
+{
+ char *table_end, *table_offset;
+ struct acpi_mpam_msc_node *tbl_msc;
+ struct platform_device *pdev;
+
+ if (acpi_disabled || !system_supports_mpam())
+ return 0;
+
+ struct acpi_table_header *table __free(acpi_put_table) =
+ acpi_get_table_pointer(ACPI_SIG_MPAM, 0);
+
+ if (IS_ERR(table))
+ return 0;
+
+ if (table->revision < 1) {
+ pr_debug("MPAM ACPI table revision %d not supported\n", table->revision);
+ return 0;
+ }
+
+ table_offset = (char *)(table + 1);
+ table_end = (char *)table + table->length;
+
+ while (table_offset < table_end) {
+ tbl_msc = (struct acpi_mpam_msc_node *)table_offset;
+ if (table_offset + sizeof(*tbl_msc) > table_end ||
+ table_offset + tbl_msc->length > table_end) {
+ pr_err("MSC entry overlaps end of ACPI table\n");
+ return -EINVAL;
+ }
+ table_offset += tbl_msc->length;
+
+ /*
+ * If any of the reserved fields are set, make no attempt to
+ * parse the MSC structure. This MSC will still be counted by
+ * acpi_mpam_count_msc(), meaning the MPAM driver can't probe
+ * against all MSC, and will never be enabled. There is no way
+ * to enable it safely, because we cannot determine safe
+ * system-wide partid and pmg ranges in this situation.
+ */
+ if (tbl_msc->reserved || tbl_msc->reserved1 || tbl_msc->reserved2) {
+ pr_err_once("Unrecognised MSC, MPAM not usable\n");
+ pr_debug("MSC.%u: reserved field set\n", tbl_msc->identifier);
+ continue;
+ }
+
+ if (!tbl_msc->mmio_size) {
+ pr_debug("MSC.%u: marked as disabled\n", tbl_msc->identifier);
+ continue;
+ }
+
+ pdev = acpi_mpam_parse_msc(tbl_msc);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+ }
+
+ return 0;
+}
+
+/**
+ * acpi_mpam_count_msc() - Count the number of MSC described by firmware.
+ *
+ * Returns the number of MSCs, or zero for an error.
+ *
+ * This can be called before or in parallel with acpi_mpam_parse().
+ */
+int acpi_mpam_count_msc(void)
+{
+ char *table_end, *table_offset;
+ struct acpi_mpam_msc_node *tbl_msc;
+ int count = 0;
+
+ if (acpi_disabled || !system_supports_mpam())
+ return 0;
+
+ struct acpi_table_header *table __free(acpi_put_table) =
+ acpi_get_table_pointer(ACPI_SIG_MPAM, 0);
+
+ if (IS_ERR(table))
+ return 0;
+
+ if (table->revision < 1)
+ return 0;
+
+ table_offset = (char *)(table + 1);
+ table_end = (char *)table + table->length;
+
+ while (table_offset < table_end) {
+ tbl_msc = (struct acpi_mpam_msc_node *)table_offset;
+
+ if (table_offset + sizeof(*tbl_msc) > table_end)
+ return -EINVAL;
+ if (tbl_msc->length < sizeof(*tbl_msc))
+ return -EINVAL;
+ if (tbl_msc->length > table_end - table_offset)
+ return -EINVAL;
+ table_offset += tbl_msc->length;
+
+ if (!tbl_msc->mmio_size)
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+/*
+ * Call after ACPI devices have been created, which happens behind acpi_scan_init()
+ * called from subsys_initcall(). PCC requires the mailbox driver, which is
+ * initialised from postcore_initcall().
+ */
+subsys_initcall_sync(acpi_mpam_parse);
diff --git a/drivers/acpi/arm64/thermal_cpufreq.c b/drivers/acpi/arm64/thermal_cpufreq.c
new file mode 100644
index 000000000000..582854914c5c
--- /dev/null
+++ b/drivers/acpi/arm64/thermal_cpufreq.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/acpi.h>
+#include <linux/export.h>
+
+#include "../internal.h"
+
+#define SMCCC_SOC_ID_T241 0x036b0241
+
+int acpi_arch_thermal_cpufreq_pctg(void)
+{
+ s32 soc_id = arm_smccc_get_soc_id_version();
+
+ /*
+ * Check JEP106 code for NVIDIA Tegra241 chip (036b:0241) and
+ * reduce the CPUFREQ Thermal reduction percentage to 5%.
+ */
+ if (soc_id == SMCCC_SOC_ID_T241)
+ return 5;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_arch_thermal_cpufreq_pctg);
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 306513fec1e1..34181fa52e93 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -10,7 +10,6 @@
#define pr_fmt(fmt) "ACPI: battery: " fmt
-#include <linux/async.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
@@ -22,7 +21,7 @@
#include <linux/suspend.h>
#include <linux/types.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/acpi.h>
#include <linux/power_supply.h>
@@ -38,17 +37,18 @@
/* Battery power unit: 0 means mW, 1 means mA */
#define ACPI_BATTERY_POWER_UNIT_MA 1
-#define ACPI_BATTERY_STATE_DISCHARGING 0x1
-#define ACPI_BATTERY_STATE_CHARGING 0x2
-#define ACPI_BATTERY_STATE_CRITICAL 0x4
+#define ACPI_BATTERY_STATE_DISCHARGING 0x1
+#define ACPI_BATTERY_STATE_CHARGING 0x2
+#define ACPI_BATTERY_STATE_CRITICAL 0x4
+#define ACPI_BATTERY_STATE_CHARGE_LIMITING 0x8
+
+#define MAX_STRING_LENGTH 64
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
MODULE_DESCRIPTION("ACPI Battery Driver");
MODULE_LICENSE("GPL");
-static async_cookie_t async_cookie;
-static bool battery_driver_registered;
static int battery_bix_broken_package;
static int battery_notification_delay_ms;
static int battery_ac_is_broken;
@@ -91,8 +91,7 @@ enum {
};
struct acpi_battery {
- struct mutex lock;
- struct mutex sysfs_lock;
+ struct mutex update_lock;
struct power_supply *bat;
struct power_supply_desc bat_desc;
struct acpi_device *device;
@@ -118,10 +117,10 @@ struct acpi_battery {
int capacity_granularity_1;
int capacity_granularity_2;
int alarm;
- char model_number[32];
- char serial_number[32];
- char type[32];
- char oem_info[32];
+ char model_number[MAX_STRING_LENGTH];
+ char serial_number[MAX_STRING_LENGTH];
+ char type[MAX_STRING_LENGTH];
+ char oem_info[MAX_STRING_LENGTH];
int state;
int power_unit;
unsigned long flags;
@@ -153,7 +152,7 @@ static int acpi_battery_get_state(struct acpi_battery *battery);
static int acpi_battery_is_charged(struct acpi_battery *battery)
{
- /* charging, discharging or critical low */
+ /* charging, discharging, critical low or charge limited */
if (battery->state != 0)
return 0;
@@ -213,6 +212,8 @@ static int acpi_battery_get_property(struct power_supply *psy,
val->intval = acpi_battery_handle_discharging(battery);
else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (battery->state & ACPI_BATTERY_STATE_CHARGE_LIMITING)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
else if (acpi_battery_is_charged(battery))
val->intval = POWER_SUPPLY_STATUS_FULL;
else
@@ -277,8 +278,8 @@ static int acpi_battery_get_property(struct power_supply *psy,
full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
ret = -ENODEV;
else
- val->intval = battery->capacity_now * 100/
- full_capacity;
+ val->intval = DIV_ROUND_CLOSEST_ULL(battery->capacity_now * 100ULL,
+ full_capacity);
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
if (battery->state & ACPI_BATTERY_STATE_CRITICAL)
@@ -306,7 +307,7 @@ static int acpi_battery_get_property(struct power_supply *psy,
return ret;
}
-static enum power_supply_property charge_battery_props[] = {
+static const enum power_supply_property charge_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -324,7 +325,7 @@ static enum power_supply_property charge_battery_props[] = {
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
-static enum power_supply_property charge_battery_full_cap_broken_props[] = {
+static const enum power_supply_property charge_battery_full_cap_broken_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -338,7 +339,7 @@ static enum power_supply_property charge_battery_full_cap_broken_props[] = {
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
-static enum power_supply_property energy_battery_props[] = {
+static const enum power_supply_property energy_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -356,7 +357,7 @@ static enum power_supply_property energy_battery_props[] = {
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
-static enum power_supply_property energy_battery_full_cap_broken_props[] = {
+static const enum power_supply_property energy_battery_full_cap_broken_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -437,16 +438,25 @@ static int extract_package(struct acpi_battery *battery,
element = &package->package.elements[i];
if (offsets[i].mode) {
u8 *ptr = (u8 *)battery + offsets[i].offset;
+ u32 len = MAX_STRING_LENGTH;
+
+ switch (element->type) {
+ case ACPI_TYPE_BUFFER:
+ if (len > element->buffer.length + 1)
+ len = element->buffer.length + 1;
- if (element->type == ACPI_TYPE_STRING ||
- element->type == ACPI_TYPE_BUFFER)
- strncpy(ptr, element->string.pointer, 32);
- else if (element->type == ACPI_TYPE_INTEGER) {
- strncpy(ptr, (u8 *)&element->integer.value,
- sizeof(u64));
- ptr[sizeof(u64)] = 0;
- } else
+ fallthrough;
+ case ACPI_TYPE_STRING:
+ strscpy(ptr, element->string.pointer, len);
+
+ break;
+ case ACPI_TYPE_INTEGER:
+ strscpy(ptr, (u8 *)&element->integer.value, sizeof(u64) + 1);
+
+ break;
+ default:
*ptr = 0; /* don't have value */
+ }
} else {
int *x = (int *)((u8 *)battery + offsets[i].offset);
*x = (element->type == ACPI_TYPE_INTEGER) ?
@@ -524,11 +534,9 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status = AE_ERROR;
- mutex_lock(&battery->lock);
status = acpi_evaluate_object(battery->device->handle,
use_bix ? "_BIX":"_BIF",
NULL, &buffer);
- mutex_unlock(&battery->lock);
if (ACPI_FAILURE(status)) {
acpi_handle_info(battery->device->handle,
@@ -565,11 +573,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
msecs_to_jiffies(cache_time)))
return 0;
- mutex_lock(&battery->lock);
status = acpi_evaluate_object(battery->device->handle, "_BST",
NULL, &buffer);
- mutex_unlock(&battery->lock);
-
if (ACPI_FAILURE(status)) {
acpi_handle_info(battery->device->handle,
"_BST evaluation failed: %s",
@@ -617,11 +622,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
!test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
return -ENODEV;
- mutex_lock(&battery->lock);
status = acpi_execute_simple_method(battery->device->handle, "_BTP",
battery->alarm);
- mutex_unlock(&battery->lock);
-
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -650,7 +652,7 @@ static ssize_t acpi_battery_alarm_show(struct device *dev,
{
struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
- return sprintf(buf, "%d\n", battery->alarm * 1000);
+ return sysfs_emit(buf, "%d\n", battery->alarm * 1000);
}
static ssize_t acpi_battery_alarm_store(struct device *dev,
@@ -667,12 +669,18 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
return count;
}
-static const struct device_attribute alarm_attr = {
+static struct device_attribute alarm_attr = {
.attr = {.name = "alarm", .mode = 0644},
.show = acpi_battery_alarm_show,
.store = acpi_battery_alarm_store,
};
+static struct attribute *acpi_battery_attrs[] = {
+ &alarm_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(acpi_battery);
+
/*
* The Battery Hooking API
*
@@ -686,27 +694,35 @@ static LIST_HEAD(acpi_battery_list);
static LIST_HEAD(battery_hook_list);
static DEFINE_MUTEX(hook_mutex);
-static void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock)
+static void battery_hook_unregister_unlocked(struct acpi_battery_hook *hook)
{
struct acpi_battery *battery;
+
/*
* In order to remove a hook, we first need to
* de-register all the batteries that are registered.
*/
- if (lock)
- mutex_lock(&hook_mutex);
list_for_each_entry(battery, &acpi_battery_list, list) {
- hook->remove_battery(battery->bat);
+ if (!hook->remove_battery(battery->bat, hook))
+ power_supply_changed(battery->bat);
}
- list_del(&hook->list);
- if (lock)
- mutex_unlock(&hook_mutex);
- pr_info("extension unregistered: %s\n", hook->name);
+ list_del_init(&hook->list);
+
+ pr_info("hook unregistered: %s\n", hook->name);
}
void battery_hook_unregister(struct acpi_battery_hook *hook)
{
- __battery_hook_unregister(hook, 1);
+ mutex_lock(&hook_mutex);
+ /*
+ * Ignore already unregistered battery hooks. This might happen
+ * if a battery hook was previously unloaded due to an error when
+ * adding a new battery.
+ */
+ if (!list_empty(&hook->list))
+ battery_hook_unregister_unlocked(hook);
+
+ mutex_unlock(&hook_mutex);
}
EXPORT_SYMBOL_GPL(battery_hook_unregister);
@@ -715,7 +731,6 @@ void battery_hook_register(struct acpi_battery_hook *hook)
struct acpi_battery *battery;
mutex_lock(&hook_mutex);
- INIT_LIST_HEAD(&hook->list);
list_add(&hook->list, &battery_hook_list);
/*
* Now that the driver is registered, we need
@@ -724,24 +739,41 @@ void battery_hook_register(struct acpi_battery_hook *hook)
* its attributes.
*/
list_for_each_entry(battery, &acpi_battery_list, list) {
- if (hook->add_battery(battery->bat)) {
+ if (hook->add_battery(battery->bat, hook)) {
/*
* If a add-battery returns non-zero,
- * the registration of the extension has failed,
+ * the registration of the hook has failed,
* and we will not add it to the list of loaded
* hooks.
*/
- pr_err("extension failed to load: %s", hook->name);
- __battery_hook_unregister(hook, 0);
+ pr_err("hook failed to load: %s", hook->name);
+ battery_hook_unregister_unlocked(hook);
goto end;
}
+
+ power_supply_changed(battery->bat);
}
- pr_info("new extension: %s\n", hook->name);
+ pr_info("new hook: %s\n", hook->name);
end:
mutex_unlock(&hook_mutex);
}
EXPORT_SYMBOL_GPL(battery_hook_register);
+static void devm_battery_hook_unregister(void *data)
+{
+ struct acpi_battery_hook *hook = data;
+
+ battery_hook_unregister(hook);
+}
+
+int devm_battery_hook_register(struct device *dev, struct acpi_battery_hook *hook)
+{
+ battery_hook_register(hook);
+
+ return devm_add_action_or_reset(dev, devm_battery_hook_unregister, hook);
+}
+EXPORT_SYMBOL_GPL(devm_battery_hook_register);
+
/*
* This function gets called right after the battery sysfs
* attributes have been added, so that the drivers that
@@ -762,14 +794,14 @@ static void battery_hook_add_battery(struct acpi_battery *battery)
* during the battery module initialization.
*/
list_for_each_entry_safe(hook_node, tmp, &battery_hook_list, list) {
- if (hook_node->add_battery(battery->bat)) {
+ if (hook_node->add_battery(battery->bat, hook_node)) {
/*
- * The notification of the extensions has failed, to
- * prevent further errors we will unload the extension.
+ * The notification of the hook has failed, to
+ * prevent further errors we will unload the hook.
*/
- pr_err("error in extension, unloading: %s",
+ pr_err("error in hook, unloading: %s",
hook_node->name);
- __battery_hook_unregister(hook_node, 0);
+ battery_hook_unregister_unlocked(hook_node);
}
}
mutex_unlock(&hook_mutex);
@@ -785,7 +817,7 @@ static void battery_hook_remove_battery(struct acpi_battery *battery)
* custom attributes from the battery.
*/
list_for_each_entry(hook, &battery_hook_list, list) {
- hook->remove_battery(battery->bat);
+ hook->remove_battery(battery->bat, hook);
}
/* Then, just remove the battery from the list */
list_del(&battery->list);
@@ -802,14 +834,18 @@ static void __exit battery_hook_exit(void)
* need to remove the hooks.
*/
list_for_each_entry_safe(hook, ptr, &battery_hook_list, list) {
- __battery_hook_unregister(hook, 1);
+ battery_hook_unregister(hook);
}
mutex_destroy(&hook_mutex);
}
static int sysfs_add_battery(struct acpi_battery *battery)
{
- struct power_supply_config psy_cfg = { .drv_data = battery, };
+ struct power_supply_config psy_cfg = {
+ .drv_data = battery,
+ .attr_grp = acpi_battery_groups,
+ .no_wakeup_source = true,
+ };
bool full_cap_broken = false;
if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) &&
@@ -844,7 +880,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
battery->bat_desc.get_property = acpi_battery_get_property;
- battery->bat = power_supply_register_no_ws(&battery->device->dev,
+ battery->bat = power_supply_register(&battery->device->dev,
&battery->bat_desc, &psy_cfg);
if (IS_ERR(battery->bat)) {
@@ -854,21 +890,17 @@ static int sysfs_add_battery(struct acpi_battery *battery)
return result;
}
battery_hook_add_battery(battery);
- return device_create_file(&battery->bat->dev, &alarm_attr);
+ return 0;
}
static void sysfs_remove_battery(struct acpi_battery *battery)
{
- mutex_lock(&battery->sysfs_lock);
- if (!battery->bat) {
- mutex_unlock(&battery->sysfs_lock);
+ if (!battery->bat)
return;
- }
+
battery_hook_remove_battery(battery);
- device_remove_file(&battery->bat->dev, &alarm_attr);
power_supply_unregister(battery->bat);
battery->bat = NULL;
- mutex_unlock(&battery->sysfs_lock);
}
static void find_battery(const struct dmi_header *dm, void *private)
@@ -1020,13 +1052,17 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
}
/* Driver Interface */
-static void acpi_battery_notify(struct acpi_device *device, u32 event)
+static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
{
+ struct acpi_device *device = data;
struct acpi_battery *battery = acpi_driver_data(device);
struct power_supply *old;
if (!battery)
return;
+
+ guard(mutex)(&battery->update_lock);
+
old = battery->bat;
/*
* On Acer Aspire V5-573G notifications are sometimes triggered too
@@ -1049,21 +1085,22 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
}
static int battery_notify(struct notifier_block *nb,
- unsigned long mode, void *_unused)
+ unsigned long mode, void *_unused)
{
struct acpi_battery *battery = container_of(nb, struct acpi_battery,
pm_nb);
- int result;
- switch (mode) {
- case PM_POST_HIBERNATION:
- case PM_POST_SUSPEND:
+ if (mode == PM_POST_SUSPEND || mode == PM_POST_HIBERNATION) {
+ guard(mutex)(&battery->update_lock);
+
if (!acpi_battery_present(battery))
return 0;
if (battery->bat) {
acpi_battery_refresh(battery);
} else {
+ int result;
+
result = acpi_battery_get_info(battery);
if (result)
return result;
@@ -1075,7 +1112,6 @@ static int battery_notify(struct notifier_block *nb,
acpi_battery_init_alarm(battery);
acpi_battery_get_state(battery);
- break;
}
return 0;
@@ -1153,6 +1189,8 @@ static int acpi_battery_update_retry(struct acpi_battery *battery)
{
int retry, ret;
+ guard(mutex)(&battery->update_lock);
+
for (retry = 5; retry; retry--) {
ret = acpi_battery_update(battery, false);
if (!ret)
@@ -1163,10 +1201,17 @@ static int acpi_battery_update_retry(struct acpi_battery *battery)
return ret;
}
+static void sysfs_battery_cleanup(struct acpi_battery *battery)
+{
+ guard(mutex)(&battery->update_lock);
+
+ sysfs_remove_battery(battery);
+}
+
static int acpi_battery_add(struct acpi_device *device)
{
int result = 0;
- struct acpi_battery *battery = NULL;
+ struct acpi_battery *battery;
if (!device)
return -EINVAL;
@@ -1174,15 +1219,18 @@ static int acpi_battery_add(struct acpi_device *device)
if (device->dep_unmet)
return -EPROBE_DEFER;
- battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
+ battery = devm_kzalloc(&device->dev, sizeof(*battery), GFP_KERNEL);
if (!battery)
return -ENOMEM;
battery->device = device;
- strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
+ strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
device->driver_data = battery;
- mutex_init(&battery->lock);
- mutex_init(&battery->sysfs_lock);
+
+ result = devm_mutex_init(&device->dev, &battery->update_lock);
+ if (result)
+ return result;
+
if (acpi_has_method(battery->device->handle, "_BIX"))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
@@ -1194,37 +1242,48 @@ static int acpi_battery_add(struct acpi_device *device)
device->status.battery_present ? "present" : "absent");
battery->pm_nb.notifier_call = battery_notify;
- register_pm_notifier(&battery->pm_nb);
+ result = register_pm_notifier(&battery->pm_nb);
+ if (result)
+ goto fail;
device_init_wakeup(&device->dev, 1);
- return result;
+ result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY,
+ acpi_battery_notify, device);
+ if (result)
+ goto fail_pm;
+ return 0;
+
+fail_pm:
+ device_init_wakeup(&device->dev, 0);
+ unregister_pm_notifier(&battery->pm_nb);
fail:
- sysfs_remove_battery(battery);
- mutex_destroy(&battery->lock);
- mutex_destroy(&battery->sysfs_lock);
- kfree(battery);
+ sysfs_battery_cleanup(battery);
+
return result;
}
-static int acpi_battery_remove(struct acpi_device *device)
+static void acpi_battery_remove(struct acpi_device *device)
{
- struct acpi_battery *battery = NULL;
+ struct acpi_battery *battery;
if (!device || !acpi_driver_data(device))
- return -EINVAL;
- device_init_wakeup(&device->dev, 0);
+ return;
+
battery = acpi_driver_data(device);
+
+ acpi_dev_remove_notify_handler(device, ACPI_ALL_NOTIFY,
+ acpi_battery_notify);
+
+ device_init_wakeup(&device->dev, 0);
unregister_pm_notifier(&battery->pm_nb);
+
+ guard(mutex)(&battery->update_lock);
+
sysfs_remove_battery(battery);
- mutex_destroy(&battery->lock);
- mutex_destroy(&battery->sysfs_lock);
- kfree(battery);
- return 0;
}
-#ifdef CONFIG_PM_SLEEP
/* this is needed to learn about changes made in suspended state */
static int acpi_battery_resume(struct device *dev)
{
@@ -1238,57 +1297,41 @@ static int acpi_battery_resume(struct device *dev)
return -EINVAL;
battery->update_time = 0;
+
+ guard(mutex)(&battery->update_lock);
+
acpi_battery_update(battery, true);
return 0;
}
-#else
-#define acpi_battery_resume NULL
-#endif
-static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
static struct acpi_driver acpi_battery_driver = {
.name = "battery",
.class = ACPI_BATTERY_CLASS,
.ids = battery_device_ids,
- .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
.add = acpi_battery_add,
.remove = acpi_battery_remove,
- .notify = acpi_battery_notify,
},
- .drv.pm = &acpi_battery_pm,
+ .drv.pm = pm_sleep_ptr(&acpi_battery_pm),
+ .drv.probe_type = PROBE_PREFER_ASYNCHRONOUS,
};
-static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
-{
- int result;
-
- if (acpi_quirk_skip_acpi_ac_and_battery())
- return;
-
- dmi_check_system(bat_dmi_table);
-
- result = acpi_bus_register_driver(&acpi_battery_driver);
- battery_driver_registered = (result == 0);
-}
-
static int __init acpi_battery_init(void)
{
- if (acpi_disabled)
+ if (acpi_disabled || acpi_quirk_skip_acpi_ac_and_battery())
return -ENODEV;
- async_cookie = async_schedule(acpi_battery_init_async, NULL);
- return 0;
+ dmi_check_system(bat_dmi_table);
+
+ return acpi_bus_register_driver(&acpi_battery_driver);
}
static void __exit acpi_battery_exit(void)
{
- async_synchronize_cookie(async_cookie + 1);
- if (battery_driver_registered) {
- acpi_bus_unregister_driver(&acpi_battery_driver);
- battery_hook_exit();
- }
+ acpi_bus_unregister_driver(&acpi_battery_driver);
+ battery_hook_exit();
}
module_init(acpi_battery_init);
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
index e4fb9e225ddf..0fdd581ef96f 100644
--- a/drivers/acpi/bgrt.c
+++ b/drivers/acpi/bgrt.c
@@ -29,14 +29,7 @@ BGRT_SHOW(type, image_type);
BGRT_SHOW(xoffset, image_offset_x);
BGRT_SHOW(yoffset, image_offset_y);
-static ssize_t image_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off, size_t count)
-{
- memcpy(buf, attr->private + off, count);
- return count;
-}
-
-static BIN_ATTR_RO(image, 0); /* size gets filled in later */
+static __ro_after_init BIN_ATTR_SIMPLE_RO(image);
static struct attribute *bgrt_attributes[] = {
&bgrt_attr_version.attr,
@@ -47,7 +40,7 @@ static struct attribute *bgrt_attributes[] = {
NULL,
};
-static struct bin_attribute *bgrt_bin_attributes[] = {
+static const struct bin_attribute *const bgrt_bin_attributes[] = {
&bin_attr_image,
NULL,
};
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index c0d20d997891..a984ccd4a2a0 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -26,8 +26,6 @@
#include <asm/mpspec.h>
#include <linux/dmi.h>
#endif
-#include <linux/acpi_agdi.h>
-#include <linux/acpi_iort.h>
#include <linux/acpi_viot.h>
#include <linux/pci.h>
#include <acpi/apei.h>
@@ -114,6 +112,17 @@ int acpi_bus_get_status(struct acpi_device *device)
if (ACPI_FAILURE(status))
return -ENODEV;
+ if (!device->status.present && device->status.enabled) {
+ pr_info(FW_BUG "Device [%s] status [%08x]: not present and enabled\n",
+ device->pnp.bus_id, (u32)sta);
+ device->status.enabled = 0;
+ /*
+ * The status is clearly invalid, so clear the functional bit as
+ * well to avoid attempting to use the device.
+ */
+ device->status.functional = 0;
+ }
+
acpi_set_device_status(device, sta);
if (device->status.functional && !device->status.present) {
@@ -318,11 +327,20 @@ static void acpi_bus_osc_negotiate_platform_control(void)
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT;
if (IS_ENABLED(CONFIG_ACPI_PROCESSOR))
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT;
+ if (IS_ENABLED(CONFIG_ACPI_THERMAL))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT;
+ if (IS_ENABLED(CONFIG_ACPI_BATTERY))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT;
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT;
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT;
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_OVER_16_PSTATES_SUPPORT;
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GED_SUPPORT;
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT;
if (IS_ENABLED(CONFIG_ACPI_PRMT))
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT;
+ if (IS_ENABLED(CONFIG_ACPI_FFH))
+ capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FFH_OPR_SUPPORT;
#ifdef CONFIG_ARM64
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT;
@@ -408,7 +426,7 @@ static void acpi_bus_decode_usb_osc(const char *msg, u32 bits)
static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A";
static void acpi_bus_osc_negotiate_usb_control(void)
{
- u32 capbuf[3];
+ u32 capbuf[3], *capbuf_ret;
struct acpi_osc_context context = {
.uuid_str = sb_usb_uuid_str,
.rev = 1,
@@ -428,7 +446,12 @@ static void acpi_bus_osc_negotiate_usb_control(void)
control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING |
OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN;
- capbuf[OSC_QUERY_DWORD] = 0;
+ /*
+ * Run _OSC first with query bit set, trying to get control over
+ * all tunneling. The platform can then clear out bits in the
+ * control dword that it does not want to grant to the OS.
+ */
+ capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
capbuf[OSC_SUPPORT_DWORD] = 0;
capbuf[OSC_CONTROL_DWORD] = control;
@@ -441,8 +464,29 @@ static void acpi_bus_osc_negotiate_usb_control(void)
goto out_free;
}
+ /*
+ * Run _OSC again now with query bit clear and the control dword
+ * matching what the platform granted (which may not have all
+ * the control bits set).
+ */
+ capbuf_ret = context.ret.pointer;
+
+ capbuf[OSC_QUERY_DWORD] = 0;
+ capbuf[OSC_CONTROL_DWORD] = capbuf_ret[OSC_CONTROL_DWORD];
+
+ kfree(context.ret.pointer);
+
+ status = acpi_run_osc(handle, &context);
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (context.ret.length != sizeof(capbuf)) {
+ pr_info("USB4 _OSC: returned invalid length buffer\n");
+ goto out_free;
+ }
+
osc_sb_native_usb4_control =
- control & acpi_osc_ctx_get_pci_control(&context);
+ control & acpi_osc_ctx_get_pci_control(&context);
acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control);
acpi_bus_decode_usb_osc("USB4 _OSC: OS controls",
@@ -457,84 +501,66 @@ out_free:
-------------------------------------------------------------------------- */
/**
- * acpi_bus_notify
- * ---------------
- * Callback for all 'system-level' device notifications (values 0x00-0x7F).
+ * acpi_bus_notify - Global system-level (0x00-0x7F) notifications handler
+ * @handle: Target ACPI object.
+ * @type: Notification type.
+ * @data: Ignored.
+ *
+ * This only handles notifications related to device hotplug.
*/
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
struct acpi_device *adev;
- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
- bool hotplug_event = false;
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
- hotplug_event = true;
break;
case ACPI_NOTIFY_DEVICE_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
- hotplug_event = true;
break;
case ACPI_NOTIFY_DEVICE_WAKE:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n");
- break;
+ return;
case ACPI_NOTIFY_EJECT_REQUEST:
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
- hotplug_event = true;
break;
case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n");
/* TBD: Exactly what does 'light' mean? */
- break;
+ return;
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
acpi_handle_err(handle, "Device cannot be configured due "
"to a frequency mismatch\n");
- break;
+ return;
case ACPI_NOTIFY_BUS_MODE_MISMATCH:
acpi_handle_err(handle, "Device cannot be configured due "
"to a bus mode mismatch\n");
- break;
+ return;
case ACPI_NOTIFY_POWER_FAULT:
acpi_handle_err(handle, "Device has suffered a power fault\n");
- break;
+ return;
default:
acpi_handle_debug(handle, "Unknown event type 0x%x\n", type);
- break;
- }
-
- adev = acpi_bus_get_acpi_device(handle);
- if (!adev)
- goto err;
-
- if (adev->dev.driver) {
- struct acpi_driver *driver = to_acpi_driver(adev->dev.driver);
-
- if (driver && driver->ops.notify &&
- (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
- driver->ops.notify(adev, type);
- }
-
- if (!hotplug_event) {
- acpi_bus_put_acpi_device(adev);
return;
}
- if (ACPI_SUCCESS(acpi_hotplug_schedule(adev, type)))
+ adev = acpi_get_acpi_dev(handle);
+
+ if (adev && ACPI_SUCCESS(acpi_hotplug_schedule(adev, type)))
return;
- acpi_bus_put_acpi_device(adev);
+ acpi_put_acpi_dev(adev);
- err:
- acpi_evaluate_ost(handle, type, ost_code, NULL);
+ acpi_evaluate_ost(handle, type, ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
}
static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
@@ -545,57 +571,56 @@ static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
acpi_drv->ops.notify(device, event);
}
-static void acpi_notify_device_fixed(void *data)
+static int acpi_device_install_notify_handler(struct acpi_device *device,
+ struct acpi_driver *acpi_drv)
{
- struct acpi_device *device = data;
+ u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
+ ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
+ acpi_status status;
- /* Fixed hardware devices have no handles */
- acpi_notify_device(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
+ status = acpi_install_notify_handler(device->handle, type,
+ acpi_notify_device, device);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return 0;
}
-static u32 acpi_device_fixed_event(void *data)
+static void acpi_device_remove_notify_handler(struct acpi_device *device,
+ struct acpi_driver *acpi_drv)
{
- acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_notify_device_fixed, data);
- return ACPI_INTERRUPT_HANDLED;
+ u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
+ ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
+
+ acpi_remove_notify_handler(device->handle, type,
+ acpi_notify_device);
+
+ acpi_os_wait_events_complete();
}
-static int acpi_device_install_notify_handler(struct acpi_device *device)
+int acpi_dev_install_notify_handler(struct acpi_device *adev,
+ u32 handler_type,
+ acpi_notify_handler handler, void *context)
{
acpi_status status;
- if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
- status =
- acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
- acpi_device_fixed_event,
- device);
- else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
- status =
- acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
- acpi_device_fixed_event,
- device);
- else
- status = acpi_install_notify_handler(device->handle,
- ACPI_DEVICE_NOTIFY,
- acpi_notify_device,
- device);
-
+ status = acpi_install_notify_handler(adev->handle, handler_type,
+ handler, context);
if (ACPI_FAILURE(status))
- return -EINVAL;
+ return -ENODEV;
+
return 0;
}
+EXPORT_SYMBOL_GPL(acpi_dev_install_notify_handler);
-static void acpi_device_remove_notify_handler(struct acpi_device *device)
+void acpi_dev_remove_notify_handler(struct acpi_device *adev,
+ u32 handler_type,
+ acpi_notify_handler handler)
{
- if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
- acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
- acpi_device_fixed_event);
- else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
- acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
- acpi_device_fixed_event);
- else
- acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
- acpi_notify_device);
+ acpi_remove_notify_handler(adev->handle, handler_type, handler);
+ acpi_os_wait_events_complete();
}
+EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler);
/* Handle events targeting \_SB device (at present only graceful shutdown) */
@@ -629,8 +654,9 @@ static void acpi_sb_notify(acpi_handle handle, u32 event, void *data)
if (event == ACPI_SB_NOTIFY_SHUTDOWN_REQUEST) {
if (!work_busy(&acpi_sb_work))
schedule_work(&acpi_sb_work);
- } else
+ } else {
pr_warn("event %x is not supported by \\_SB device\n", event);
+ }
}
static int __init acpi_setup_sb_notify_handler(void)
@@ -724,7 +750,7 @@ bool acpi_device_is_first_physical_node(struct acpi_device *adev,
* resources available from it but they will be matched normally using functions
* provided by their bus types (and analogously for their modalias).
*/
-struct acpi_device *acpi_companion_match(const struct device *dev)
+const struct acpi_device *acpi_companion_match(const struct device *dev)
{
struct acpi_device *adev;
@@ -748,7 +774,7 @@ struct acpi_device *acpi_companion_match(const struct device *dev)
* identifiers and a _DSD object with the "compatible" property, use that
* property to match against the given list of identifiers.
*/
-static bool acpi_of_match_device(struct acpi_device *adev,
+static bool acpi_of_match_device(const struct acpi_device *adev,
const struct of_device_id *of_match_table,
const struct of_device_id **of_id)
{
@@ -802,7 +828,7 @@ static bool acpi_of_modalias(struct acpi_device *adev,
str = obj->string.pointer;
chr = strchr(str, ',');
- strlcpy(modalias, chr ? chr + 1 : str, len);
+ strscpy(modalias, chr ? chr + 1 : str, len);
return true;
}
@@ -814,15 +840,16 @@ static bool acpi_of_modalias(struct acpi_device *adev,
* @modalias: Pointer to buffer that modalias value will be copied into
* @len: Length of modalias buffer
*
- * This is a counterpart of of_modalias_node() for struct acpi_device objects.
- * If there is a compatible string for @adev, it will be copied to @modalias
- * with the vendor prefix stripped; otherwise, @default_id will be used.
+ * This is a counterpart of of_alias_from_compatible() for struct acpi_device
+ * objects. If there is a compatible string for @adev, it will be copied to
+ * @modalias with the vendor prefix stripped; otherwise, @default_id will be
+ * used.
*/
void acpi_set_modalias(struct acpi_device *adev, const char *default_id,
char *modalias, size_t len)
{
if (!acpi_of_modalias(adev, modalias, len))
- strlcpy(modalias, default_id, len);
+ strscpy(modalias, default_id, len);
}
EXPORT_SYMBOL_GPL(acpi_set_modalias);
@@ -849,7 +876,7 @@ static bool __acpi_match_device_cls(const struct acpi_device_id *id,
return true;
}
-static bool __acpi_match_device(struct acpi_device *device,
+static bool __acpi_match_device(const struct acpi_device *device,
const struct acpi_device_id *acpi_ids,
const struct of_device_id *of_ids,
const struct acpi_device_id **acpi_id,
@@ -892,6 +919,26 @@ out_acpi_match:
}
/**
+ * acpi_match_acpi_device - Match an ACPI device against a given list of ACPI IDs
+ * @ids: Array of struct acpi_device_id objects to match against.
+ * @adev: The ACPI device pointer to match.
+ *
+ * Match the ACPI device @adev against a given list of ACPI IDs @ids.
+ *
+ * Return:
+ * a pointer to the first matching ACPI ID on success or %NULL on failure.
+ */
+const struct acpi_device_id *acpi_match_acpi_device(const struct acpi_device_id *ids,
+ const struct acpi_device *adev)
+{
+ const struct acpi_device_id *id = NULL;
+
+ __acpi_match_device(adev, ids, NULL, &id, NULL);
+ return id;
+}
+EXPORT_SYMBOL_GPL(acpi_match_acpi_device);
+
+/**
* acpi_match_device - Match a struct device against a given list of ACPI IDs
* @ids: Array of struct acpi_device_id object to match against.
* @dev: The device structure to match.
@@ -905,10 +952,7 @@ out_acpi_match:
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
const struct device *dev)
{
- const struct acpi_device_id *id = NULL;
-
- __acpi_match_device(acpi_companion_match(dev), ids, NULL, &id, NULL);
- return id;
+ return acpi_match_acpi_device(ids, acpi_companion_match(dev));
}
EXPORT_SYMBOL_GPL(acpi_match_device);
@@ -925,12 +969,13 @@ static const void *acpi_of_device_get_match_data(const struct device *dev)
const void *acpi_device_get_match_data(const struct device *dev)
{
+ const struct acpi_device_id *acpi_ids = dev->driver->acpi_match_table;
const struct acpi_device_id *match;
- if (!dev->driver->acpi_match_table)
+ if (!acpi_ids)
return acpi_of_device_get_match_data(dev);
- match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ match = acpi_match_device(acpi_ids, dev);
if (!match)
return NULL;
@@ -948,14 +993,13 @@ EXPORT_SYMBOL(acpi_match_device_ids);
bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
- if (!drv->acpi_match_table)
- return acpi_of_match_device(ACPI_COMPANION(dev),
- drv->of_match_table,
- NULL);
-
- return __acpi_match_device(acpi_companion_match(dev),
- drv->acpi_match_table, drv->of_match_table,
- NULL, NULL);
+ const struct acpi_device_id *acpi_ids = drv->acpi_match_table;
+ const struct of_device_id *of_ids = drv->of_match_table;
+
+ if (!acpi_ids)
+ return acpi_of_match_device(ACPI_COMPANION(dev), of_ids, NULL);
+
+ return __acpi_match_device(acpi_companion_match(dev), acpi_ids, of_ids, NULL, NULL);
}
EXPORT_SYMBOL_GPL(acpi_driver_match_device);
@@ -964,28 +1008,26 @@ EXPORT_SYMBOL_GPL(acpi_driver_match_device);
-------------------------------------------------------------------------- */
/**
- * acpi_bus_register_driver - register a driver with the ACPI bus
+ * __acpi_bus_register_driver - register a driver with the ACPI bus
* @driver: driver being registered
+ * @owner: owning module/driver
*
* Registers a driver with the ACPI bus. Searches the namespace for all
* devices that match the driver's criteria and binds. Returns zero for
* success or a negative error status for failure.
*/
-int acpi_bus_register_driver(struct acpi_driver *driver)
+int __acpi_bus_register_driver(struct acpi_driver *driver, struct module *owner)
{
- int ret;
-
if (acpi_disabled)
return -ENODEV;
driver->drv.name = driver->name;
driver->drv.bus = &acpi_bus_type;
- driver->drv.owner = driver->owner;
+ driver->drv.owner = owner;
- ret = driver_register(&driver->drv);
- return ret;
+ return driver_register(&driver->drv);
}
-EXPORT_SYMBOL(acpi_bus_register_driver);
+EXPORT_SYMBOL(__acpi_bus_register_driver);
/**
* acpi_bus_unregister_driver - unregisters a driver with the ACPI bus
@@ -1005,16 +1047,16 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver);
ACPI Bus operations
-------------------------------------------------------------------------- */
-static int acpi_bus_match(struct device *dev, struct device_driver *drv)
+static int acpi_bus_match(struct device *dev, const struct device_driver *drv)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_driver *acpi_drv = to_acpi_driver(drv);
+ const struct acpi_driver *acpi_drv = to_acpi_driver(drv);
return acpi_dev->flags.match_driver
&& !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
}
-static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
return __acpi_device_uevent_modalias(to_acpi_device(dev), env);
}
@@ -1032,14 +1074,16 @@ static int acpi_device_probe(struct device *dev)
return -ENOSYS;
ret = acpi_drv->ops.add(acpi_dev);
- if (ret)
+ if (ret) {
+ acpi_dev->driver_data = NULL;
return ret;
+ }
pr_debug("Driver [%s] successfully bound to device [%s]\n",
acpi_drv->name, acpi_dev->pnp.bus_id);
if (acpi_drv->ops.notify) {
- ret = acpi_device_install_notify_handler(acpi_dev);
+ ret = acpi_device_install_notify_handler(acpi_dev, acpi_drv);
if (ret) {
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
@@ -1062,7 +1106,7 @@ static void acpi_device_remove(struct device *dev)
struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
if (acpi_drv->ops.notify)
- acpi_device_remove_notify_handler(acpi_dev);
+ acpi_device_remove_notify_handler(acpi_dev, acpi_drv);
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
@@ -1072,7 +1116,7 @@ static void acpi_device_remove(struct device *dev)
put_device(dev);
}
-struct bus_type acpi_bus_type = {
+const struct bus_type acpi_bus_type = {
.name = "acpi",
.match = acpi_bus_match,
.probe = acpi_device_probe,
@@ -1159,6 +1203,9 @@ static int __init acpi_bus_init_irq(void)
case ACPI_IRQ_MODEL_LPIC:
message = "LPIC";
break;
+ case ACPI_IRQ_MODEL_RINTC:
+ message = "RINTC";
+ break;
default:
pr_info("Unknown interrupt routing model\n");
return -ENODEV;
@@ -1323,9 +1370,6 @@ static int __init acpi_bus_init(void)
goto error1;
}
- /* Set capability bits for _OSC under processor scope */
- acpi_early_processor_osc();
-
/*
* _OSC method may exist in module level code,
* so it must be run after ACPI_FULL_INITIALIZATION
@@ -1341,7 +1385,7 @@ static int __init acpi_bus_init(void)
acpi_sysfs_init();
- acpi_early_processor_set_pdc();
+ acpi_early_processor_control_setup();
/*
* Maybe EC region is required at bus_scan/acpi_get_devices. So it
@@ -1362,7 +1406,7 @@ static int __init acpi_bus_init(void)
goto error1;
/*
- * Register the for all standard device notifications.
+ * Register for all standard device notifications.
*/
status =
acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY,
@@ -1390,6 +1434,8 @@ static int __init acpi_bus_init(void)
struct kobject *acpi_kobj;
EXPORT_SYMBOL_GPL(acpi_kobj);
+void __weak __init acpi_arch_init(void) { }
+
static int __init acpi_init(void)
{
int result;
@@ -1400,8 +1446,10 @@ static int __init acpi_init(void)
}
acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
- if (!acpi_kobj)
- pr_debug("%s: kset create error\n", __func__);
+ if (!acpi_kobj) {
+ pr_err("Failed to register kobject\n");
+ return -ENOMEM;
+ }
init_prmt();
acpi_init_pcc();
@@ -1411,12 +1459,13 @@ static int __init acpi_init(void)
disable_acpi();
return result;
}
+ acpi_init_ffh();
pci_mmcfg_late_init();
- acpi_iort_init();
acpi_viot_early_init();
acpi_hest_init();
acpi_ghes_init();
+ acpi_arch_init();
acpi_scan_init();
acpi_ec_init();
acpi_debugfs_init();
@@ -1425,7 +1474,6 @@ static int __init acpi_init(void)
acpi_debugger_init();
acpi_setup_sb_notify_handler();
acpi_viot_init();
- acpi_agdi_init();
return 0;
}
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 1f9b9a4c38c7..3c6dd9b4ba0a 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -24,6 +24,7 @@
#define ACPI_BUTTON_CLASS "button"
#define ACPI_BUTTON_FILE_STATE "state"
#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
+#define ACPI_BUTTON_NOTIFY_WAKE 0x02
#define ACPI_BUTTON_NOTIFY_STATUS 0x80
#define ACPI_BUTTON_SUBCLASS_POWER "power"
@@ -78,6 +79,15 @@ static const struct dmi_system_id dmi_lid_quirks[] = {
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
},
{
+ /* Nextbook Ares 8A tablet, _LID device always reports lid closed */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+ DMI_MATCH(DMI_BIOS_VERSION, "M882"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
+ },
+ {
/*
* Lenovo Yoga 9 14ITL5, initial notification of the LID device
* never happens.
@@ -121,12 +131,22 @@ static const struct dmi_system_id dmi_lid_quirks[] = {
},
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
},
+ {
+ /*
+ * Samsung galaxybook2 ,initial _LID device notification returns
+ * lid closed.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "750XED"),
+ },
+ .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
+ },
{}
};
static int acpi_button_add(struct acpi_device *device);
-static int acpi_button_remove(struct acpi_device *device);
-static void acpi_button_notify(struct acpi_device *device, u32 event);
+static void acpi_button_remove(struct acpi_device *device);
#ifdef CONFIG_PM_SLEEP
static int acpi_button_suspend(struct device *dev);
@@ -144,7 +164,6 @@ static struct acpi_driver acpi_button_driver = {
.ops = {
.add = acpi_button_add,
.remove = acpi_button_remove,
- .notify = acpi_button_notify,
},
.drv.pm = &acpi_button_pm,
};
@@ -400,45 +419,70 @@ static void acpi_lid_initialize_state(struct acpi_device *device)
button->lid_state_initialized = true;
}
-static void acpi_button_notify(struct acpi_device *device, u32 event)
+static void acpi_lid_notify(acpi_handle handle, u32 event, void *data)
{
- struct acpi_button *button = acpi_driver_data(device);
+ struct acpi_device *device = data;
+ struct acpi_button *button;
+
+ if (event != ACPI_BUTTON_NOTIFY_STATUS) {
+ acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+ event);
+ return;
+ }
+
+ button = acpi_driver_data(device);
+ if (!button->lid_state_initialized)
+ return;
+
+ acpi_lid_update_state(device, true);
+}
+
+static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_device *device = data;
+ struct acpi_button *button;
struct input_dev *input;
+ int keycode;
switch (event) {
- case ACPI_FIXED_HARDWARE_EVENT:
- event = ACPI_BUTTON_NOTIFY_STATUS;
- fallthrough;
case ACPI_BUTTON_NOTIFY_STATUS:
- input = button->input;
- if (button->type == ACPI_BUTTON_TYPE_LID) {
- if (button->lid_state_initialized)
- acpi_lid_update_state(device, true);
- } else {
- int keycode;
-
- acpi_pm_wakeup_event(&device->dev);
- if (button->suspended)
- break;
-
- keycode = test_bit(KEY_SLEEP, input->keybit) ?
- KEY_SLEEP : KEY_POWER;
- input_report_key(input, keycode, 1);
- input_sync(input);
- input_report_key(input, keycode, 0);
- input_sync(input);
-
- acpi_bus_generate_netlink_event(
- device->pnp.device_class,
- dev_name(&device->dev),
- event, ++button->pushed);
- }
+ break;
+ case ACPI_BUTTON_NOTIFY_WAKE:
break;
default:
acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
event);
- break;
+ return;
}
+
+ acpi_pm_wakeup_event(&device->dev);
+
+ button = acpi_driver_data(device);
+ if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE)
+ return;
+
+ input = button->input;
+ keycode = test_bit(KEY_SLEEP, input->keybit) ? KEY_SLEEP : KEY_POWER;
+
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev),
+ event, ++button->pushed);
+}
+
+static void acpi_button_notify_run(void *data)
+{
+ acpi_button_notify(NULL, ACPI_BUTTON_NOTIFY_STATUS, data);
+}
+
+static u32 acpi_button_event(void *data)
+{
+ acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_button_notify_run, data);
+ return ACPI_INTERRUPT_HANDLED;
}
#ifdef CONFIG_PM_SLEEP
@@ -453,6 +497,7 @@ static int acpi_button_suspend(struct device *dev)
static int acpi_button_resume(struct device *dev)
{
+ struct input_dev *input;
struct acpi_device *device = to_acpi_device(dev);
struct acpi_button *button = acpi_driver_data(device);
@@ -462,6 +507,14 @@ static int acpi_button_resume(struct device *dev)
button->last_time = ktime_get();
acpi_lid_initialize_state(device);
}
+
+ if (button->type == ACPI_BUTTON_TYPE_POWER) {
+ input = button->input;
+ input_report_key(input, KEY_WAKEUP, 1);
+ input_sync(input);
+ input_report_key(input, KEY_WAKEUP, 0);
+ input_sync(input);
+ }
return 0;
}
#endif
@@ -480,11 +533,13 @@ static int acpi_lid_input_open(struct input_dev *input)
static int acpi_button_add(struct acpi_device *device)
{
+ acpi_notify_handler handler;
struct acpi_button *button;
struct input_dev *input;
const char *hid = acpi_device_hid(device);
+ acpi_status status;
char *name, *class;
- int error;
+ int error = 0;
if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
@@ -508,30 +563,36 @@ static int acpi_button_add(struct acpi_device *device)
if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
!strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
button->type = ACPI_BUTTON_TYPE_POWER;
- strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
+ handler = acpi_button_notify;
+ strscpy(name, ACPI_BUTTON_DEVICE_NAME_POWER, MAX_ACPI_DEVICE_NAME_LEN);
sprintf(class, "%s/%s",
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
button->type = ACPI_BUTTON_TYPE_SLEEP;
- strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
+ handler = acpi_button_notify;
+ strscpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP, MAX_ACPI_DEVICE_NAME_LEN);
sprintf(class, "%s/%s",
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
} else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
button->type = ACPI_BUTTON_TYPE_LID;
- strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
+ handler = acpi_lid_notify;
+ strscpy(name, ACPI_BUTTON_DEVICE_NAME_LID, MAX_ACPI_DEVICE_NAME_LEN);
sprintf(class, "%s/%s",
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
input->open = acpi_lid_input_open;
} else {
pr_info("Unsupported hid [%s]\n", hid);
error = -ENODEV;
- goto err_free_input;
}
- error = acpi_button_add_fs(device);
- if (error)
- goto err_free_input;
+ if (!error)
+ error = acpi_button_add_fs(device);
+
+ if (error) {
+ input_free_device(input);
+ goto err_free_button;
+ }
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
@@ -544,6 +605,7 @@ static int acpi_button_add(struct acpi_device *device)
switch (button->type) {
case ACPI_BUTTON_TYPE_POWER:
input_set_capability(input, EV_KEY, KEY_POWER);
+ input_set_capability(input, EV_KEY, KEY_WAKEUP);
break;
case ACPI_BUTTON_TYPE_SLEEP:
@@ -557,8 +619,33 @@ static int acpi_button_add(struct acpi_device *device)
input_set_drvdata(input, device);
error = input_register_device(input);
- if (error)
+ if (error) {
+ input_free_device(input);
goto err_remove_fs;
+ }
+
+ switch (device->device_type) {
+ case ACPI_BUS_TYPE_POWER_BUTTON:
+ status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+ acpi_button_event,
+ device);
+ break;
+ case ACPI_BUS_TYPE_SLEEP_BUTTON:
+ status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+ acpi_button_event,
+ device);
+ break;
+ default:
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_ALL_NOTIFY, handler,
+ device);
+ break;
+ }
+ if (ACPI_FAILURE(status)) {
+ error = -ENODEV;
+ goto err_input_unregister;
+ }
+
if (button->type == ACPI_BUTTON_TYPE_LID) {
/*
* This assumes there's only one lid device, or if there are
@@ -571,23 +658,40 @@ static int acpi_button_add(struct acpi_device *device)
pr_info("%s [%s]\n", name, acpi_device_bid(device));
return 0;
- err_remove_fs:
+err_input_unregister:
+ input_unregister_device(input);
+err_remove_fs:
acpi_button_remove_fs(device);
- err_free_input:
- input_free_device(input);
- err_free_button:
+err_free_button:
kfree(button);
return error;
}
-static int acpi_button_remove(struct acpi_device *device)
+static void acpi_button_remove(struct acpi_device *device)
{
struct acpi_button *button = acpi_driver_data(device);
+ switch (device->device_type) {
+ case ACPI_BUS_TYPE_POWER_BUTTON:
+ acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+ acpi_button_event);
+ break;
+ case ACPI_BUS_TYPE_SLEEP_BUTTON:
+ acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+ acpi_button_event);
+ break;
+ default:
+ acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ button->type == ACPI_BUTTON_TYPE_LID ?
+ acpi_lid_notify :
+ acpi_button_notify);
+ break;
+ }
+ acpi_os_wait_events_complete();
+
acpi_button_remove_fs(device);
input_unregister_device(button->input);
kfree(button);
- return 0;
}
static int param_set_lid_init_state(const char *val,
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 1e15a9f25ae9..3bdeeee3414e 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -39,12 +39,14 @@
#include <linux/rwsem.h>
#include <linux/wait.h>
#include <linux/topology.h>
+#include <linux/dmi.h>
+#include <linux/units.h>
+#include <linux/unaligned.h>
#include <acpi/cppc_acpi.h>
struct cppc_pcc_data {
struct pcc_mbox_chan *pcc_channel;
- void __iomem *pcc_comm_addr;
bool pcc_channel_acquired;
unsigned int deadline_us;
unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
@@ -92,7 +94,7 @@ static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx);
static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
/* pcc mapped address + header size + offset within PCC subspace */
-#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_comm_addr + \
+#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_channel->shmem + \
0x8 + (offs))
/* Check if a CPC register is in PCC */
@@ -100,6 +102,11 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
(cpc)->cpc_entry.reg.space_id == \
ACPI_ADR_SPACE_PLATFORM_COMM)
+/* Check if a CPC register is in FFH */
+#define CPC_IN_FFH(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
+ (cpc)->cpc_entry.reg.space_id == \
+ ACPI_ADR_SPACE_FIXED_HARDWARE)
+
/* Check if a CPC register is in SystemMemory */
#define CPC_IN_SYSTEM_MEMORY(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
(cpc)->cpc_entry.reg.space_id == \
@@ -121,6 +128,20 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \
!!(cpc)->cpc_entry.int_value : \
!IS_NULL_REG(&(cpc)->cpc_entry.reg))
+
+/*
+ * Each bit indicates the optionality of the register in per-cpu
+ * cpc_regs[] with the corresponding index. 0 means mandatory and 1
+ * means optional.
+ */
+#define REG_OPTIONAL (0x1FC7D0)
+
+/*
+ * Use the index of the register in per-cpu cpc_regs[] to check if
+ * it's an optional one.
+ */
+#define IS_OPTIONAL_CPC_REG(reg_idx) (REG_OPTIONAL & (1U << (reg_idx)))
+
/*
* Arbitrary Retries in case the remote processor is slow to respond
* to PCC commands. Keeping it high enough to cover emulators where
@@ -148,7 +169,7 @@ __ATTR(_name, 0444, show_##_name, NULL)
if (ret) \
return ret; \
\
- return scnprintf(buf, PAGE_SIZE, "%llu\n", \
+ return sysfs_emit(buf, "%llu\n", \
(u64)st_name.member_name); \
} \
define_one_cppc_ro(member_name)
@@ -157,12 +178,23 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf);
+show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, guaranteed_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq);
show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);
show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
+/* Check for valid access_width, otherwise, fallback to using bit_width */
+#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
+
+/* Shift and apply the mask for CPC reads/writes */
+#define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \
+ GENMASK(((reg)->bit_width) - 1, 0))
+#define MASK_VAL_WRITE(reg, prev_val, val) \
+ ((((val) & GENMASK(((reg)->bit_width) - 1, 0)) << (reg)->bit_offset) | \
+ ((prev_val) & ~(GENMASK(((reg)->bit_width) - 1, 0) << (reg)->bit_offset))) \
+
static ssize_t show_feedback_ctrs(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -174,7 +206,7 @@ static ssize_t show_feedback_ctrs(struct kobject *kobj,
if (ret)
return ret;
- return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n",
+ return sysfs_emit(buf, "ref:%llu del:%llu\n",
fb_ctrs.reference, fb_ctrs.delivered);
}
define_one_cppc_ro(feedback_ctrs);
@@ -186,6 +218,7 @@ static struct attribute *cppc_attrs[] = {
&highest_perf.attr,
&lowest_perf.attr,
&lowest_nonlinear_perf.attr,
+ &guaranteed_perf.attr,
&nominal_perf.attr,
&nominal_freq.attr,
&lowest_freq.attr,
@@ -193,7 +226,7 @@ static struct attribute *cppc_attrs[] = {
};
ATTRIBUTE_GROUPS(cppc);
-static struct kobj_type cppc_ktype = {
+static const struct kobj_type cppc_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_groups = cppc_groups,
};
@@ -203,7 +236,7 @@ static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit)
int ret, status;
struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id];
struct acpi_pcct_shared_memory __iomem *generic_comm_base =
- pcc_ss_data->pcc_comm_addr;
+ pcc_ss_data->pcc_channel->shmem;
if (!pcc_ss_data->platform_owns_pcc)
return 0;
@@ -238,7 +271,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
int ret = -EIO, i;
struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id];
struct acpi_pcct_shared_memory __iomem *generic_comm_base =
- pcc_ss_data->pcc_comm_addr;
+ pcc_ss_data->pcc_channel->shmem;
unsigned int time_delta;
/*
@@ -424,7 +457,10 @@ bool acpi_cpc_valid(void)
struct cpc_desc *cpc_ptr;
int cpu;
- for_each_present_cpu(cpu) {
+ if (acpi_disabled)
+ return false;
+
+ for_each_online_cpu(cpu) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
if (!cpc_ptr)
return false;
@@ -440,7 +476,7 @@ bool cppc_allow_fast_switch(void)
struct cpc_desc *cpc_ptr;
int cpu;
- for_each_possible_cpu(cpu) {
+ for_each_online_cpu(cpu) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF];
if (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
@@ -548,15 +584,6 @@ static int register_pcc_channel(int pcc_ss_idx)
pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate;
pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency;
- pcc_data[pcc_ss_idx]->pcc_comm_addr =
- acpi_os_ioremap(pcc_chan->shmem_base_addr,
- pcc_chan->shmem_size);
- if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) {
- pr_err("Failed to ioremap PCC comm region mem for %d\n",
- pcc_ss_idx);
- return -ENOMEM;
- }
-
/* Set flag so that we don't come here for each CPU. */
pcc_data[pcc_ss_idx]->pcc_channel_acquired = true;
}
@@ -592,6 +619,7 @@ bool __weak cpc_supported_by_cpu(void)
/**
* pcc_data_alloc() - Allocate the pcc_data memory for pcc subspace
+ * @pcc_ss_id: PCC Subspace index as in the PCC client ACPI package.
*
* Check and allocate the cppc_pcc_data memory.
* In some processor configurations it is possible that same subspace
@@ -647,10 +675,6 @@ static int pcc_data_alloc(int pcc_ss_id)
* )
*/
-#ifndef arch_init_invariance_cppc
-static inline void arch_init_invariance_cppc(void) { }
-#endif
-
/**
* acpi_cppc_processor_probe - Search for per CPU _CPC objects.
* @pr: Ptr to acpi_processor containing this CPU's logical ID.
@@ -672,8 +696,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
if (!osc_sb_cppc2_support_acked) {
pr_debug("CPPC v2 _OSC not acked\n");
- if (!cpc_supported_by_cpu())
+ if (!cpc_supported_by_cpu()) {
+ pr_debug("CPPC is not supported by the CPU\n");
return -ENODEV;
+ }
}
/* Parse the ACPI _CPC table for this CPU. */
@@ -724,7 +750,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
}
/*
- * Disregard _CPC if the number of entries in the return pachage is not
+ * Disregard _CPC if the number of entries in the return package is not
* as expected, but support future revisions being proper supersets of
* the v3 and only causing more entries to be returned by _CPC.
*/
@@ -773,6 +799,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
if (gas_t->address) {
void __iomem *addr;
+ size_t access_width;
if (!osc_cpc_flexible_adr_space_confirmed) {
pr_debug("Flexible address space capability not supported\n");
@@ -780,7 +807,8 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
}
- addr = ioremap(gas_t->address, gas_t->bit_width/8);
+ access_width = GET_BIT_WIDTH(gas_t) / 8;
+ addr = ioremap(gas_t->address, access_width);
if (!addr)
goto out_free;
cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
@@ -839,6 +867,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
/* Store CPU Logical ID */
cpc_ptr->cpu_id = pr->id;
+ raw_spin_lock_init(&cpc_ptr->rmw_lock);
/* Parse PSD data for this CPU */
ret = acpi_get_psd(cpc_ptr, handle);
@@ -876,8 +905,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
}
- arch_init_invariance_cppc();
-
kfree(output.pointer);
return 0;
@@ -976,6 +1003,7 @@ int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
{
void __iomem *vaddr = NULL;
+ int size;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cpc_reg *reg = &reg_res->cpc_entry.reg;
@@ -985,14 +1013,15 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
}
*val = 0;
+ size = GET_BIT_WIDTH(reg);
- if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
- u32 width = 8 << (reg->access_width - 1);
+ if (IS_ENABLED(CONFIG_HAS_IOPORT) &&
+ reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
u32 val_u32;
acpi_status status;
status = acpi_os_read_port((acpi_io_address)reg->address,
- &val_u32, width);
+ &val_u32, size);
if (ACPI_FAILURE(status)) {
pr_debug("Error: Failed to read SystemIO port %llx\n",
reg->address);
@@ -1001,17 +1030,24 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
*val = val_u32;
return 0;
- } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
+ } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0) {
+ /*
+ * For registers in PCC space, the register size is determined
+ * by the bit width field; the access size is used to indicate
+ * the PCC subspace id.
+ */
+ size = reg->bit_width;
vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
+ }
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
vaddr = reg_res->sys_mem_vaddr;
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
return cpc_read_ffh(cpu, reg, val);
else
return acpi_os_read_memory((acpi_physical_address)reg->address,
- val, reg->bit_width);
+ val, size);
- switch (reg->bit_width) {
+ switch (size) {
case 8:
*val = readb_relaxed(vaddr);
break;
@@ -1025,27 +1061,41 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
*val = readq_relaxed(vaddr);
break;
default:
- pr_debug("Error: Cannot read %u bit width from PCC for ss: %d\n",
- reg->bit_width, pcc_ss_id);
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ pr_debug("Error: Cannot read %u bit width from system memory: 0x%llx\n",
+ size, reg->address);
+ } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
+ pr_debug("Error: Cannot read %u bit width from PCC for ss: %d\n",
+ size, pcc_ss_id);
+ }
return -EFAULT;
}
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ *val = MASK_VAL_READ(reg, *val);
+
return 0;
}
static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
{
int ret_val = 0;
+ int size;
+ u64 prev_val;
void __iomem *vaddr = NULL;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cpc_reg *reg = &reg_res->cpc_entry.reg;
+ struct cpc_desc *cpc_desc;
+ unsigned long flags;
- if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
- u32 width = 8 << (reg->access_width - 1);
+ size = GET_BIT_WIDTH(reg);
+
+ if (IS_ENABLED(CONFIG_HAS_IOPORT) &&
+ reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
acpi_status status;
status = acpi_os_write_port((acpi_io_address)reg->address,
- (u32)val, width);
+ (u32)val, size);
if (ACPI_FAILURE(status)) {
pr_debug("Error: Failed to write SystemIO port %llx\n",
reg->address);
@@ -1053,17 +1103,52 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
}
return 0;
- } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0)
+ } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0) {
+ /*
+ * For registers in PCC space, the register size is determined
+ * by the bit width field; the access size is used to indicate
+ * the PCC subspace id.
+ */
+ size = reg->bit_width;
vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
+ }
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
vaddr = reg_res->sys_mem_vaddr;
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
return cpc_write_ffh(cpu, reg, val);
else
return acpi_os_write_memory((acpi_physical_address)reg->address,
- val, reg->bit_width);
+ val, size);
- switch (reg->bit_width) {
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+ return -ENODEV;
+ }
+
+ raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
+ switch (size) {
+ case 8:
+ prev_val = readb_relaxed(vaddr);
+ break;
+ case 16:
+ prev_val = readw_relaxed(vaddr);
+ break;
+ case 32:
+ prev_val = readl_relaxed(vaddr);
+ break;
+ case 64:
+ prev_val = readq_relaxed(vaddr);
+ break;
+ default:
+ raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags);
+ return -EFAULT;
+ }
+ val = MASK_VAL_WRITE(reg, prev_val, val);
+ }
+
+ switch (size) {
case 8:
writeb_relaxed(val, vaddr);
break;
@@ -1077,52 +1162,123 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
writeq_relaxed(val, vaddr);
break;
default:
- pr_debug("Error: Cannot write %u bit width to PCC for ss: %d\n",
- reg->bit_width, pcc_ss_id);
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ pr_debug("Error: Cannot write %u bit width to system memory: 0x%llx\n",
+ size, reg->address);
+ } else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
+ pr_debug("Error: Cannot write %u bit width to PCC for ss: %d\n",
+ size, pcc_ss_id);
+ }
ret_val = -EFAULT;
break;
}
+ if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags);
+
return ret_val;
}
-static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf)
+static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val)
{
- struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ int ret;
+
+ if (pcc_ss_id < 0) {
+ pr_debug("Invalid pcc_ss_id\n");
+ return -ENODEV;
+ }
+
+ pcc_ss_data = pcc_data[pcc_ss_id];
+
+ down_write(&pcc_ss_data->pcc_lock);
+
+ if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0)
+ ret = cpc_read(cpu, reg, val);
+ else
+ ret = -EIO;
+
+ up_write(&pcc_ss_data->pcc_lock);
+
+ return ret;
+}
+
+static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val)
+{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
struct cpc_register_resource *reg;
+ if (val == NULL)
+ return -EINVAL;
+
if (!cpc_desc) {
- pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
return -ENODEV;
}
reg = &cpc_desc->cpc_regs[reg_idx];
- if (CPC_IN_PCC(reg)) {
- int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
- struct cppc_pcc_data *pcc_ss_data = NULL;
- int ret = 0;
-
- if (pcc_ss_id < 0)
- return -EIO;
+ if ((reg->type == ACPI_TYPE_INTEGER && IS_OPTIONAL_CPC_REG(reg_idx) &&
+ !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER &&
+ IS_NULL_REG(&reg->cpc_entry.reg))) {
+ pr_debug("CPC register is not supported\n");
+ return -EOPNOTSUPP;
+ }
- pcc_ss_data = pcc_data[pcc_ss_id];
+ if (CPC_IN_PCC(reg))
+ return cppc_get_reg_val_in_pcc(cpu, reg, val);
- down_write(&pcc_ss_data->pcc_lock);
+ return cpc_read(cpu, reg, val);
+}
- if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0)
- cpc_read(cpunum, reg, perf);
- else
- ret = -EIO;
+static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val)
+{
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ int ret;
- up_write(&pcc_ss_data->pcc_lock);
+ if (pcc_ss_id < 0) {
+ pr_debug("Invalid pcc_ss_id\n");
+ return -ENODEV;
+ }
+ ret = cpc_write(cpu, reg, val);
+ if (ret)
return ret;
+
+ pcc_ss_data = pcc_data[pcc_ss_id];
+
+ down_write(&pcc_ss_data->pcc_lock);
+ /* after writing CPC, transfer the ownership of PCC to platform */
+ ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
+ up_write(&pcc_ss_data->pcc_lock);
+
+ return ret;
+}
+
+static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val)
+{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+ struct cpc_register_resource *reg;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+ return -ENODEV;
}
- cpc_read(cpunum, reg, perf);
+ reg = &cpc_desc->cpc_regs[reg_idx];
- return 0;
+ /* if a register is writeable, it must be a buffer and not null */
+ if ((reg->type != ACPI_TYPE_BUFFER) || IS_NULL_REG(&reg->cpc_entry.reg)) {
+ pr_debug("CPC register is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (CPC_IN_PCC(reg))
+ return cppc_set_reg_val_in_pcc(cpu, reg, val);
+
+ return cpc_write(cpu, reg, val);
}
/**
@@ -1134,7 +1290,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf)
*/
int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
{
- return cppc_get_perf(cpunum, DESIRED_PERF, desired_perf);
+ return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf);
}
EXPORT_SYMBOL_GPL(cppc_get_desired_perf);
@@ -1147,8 +1303,34 @@ EXPORT_SYMBOL_GPL(cppc_get_desired_perf);
*/
int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf)
{
- return cppc_get_perf(cpunum, NOMINAL_PERF, nominal_perf);
+ return cppc_get_reg_val(cpunum, NOMINAL_PERF, nominal_perf);
+}
+
+/**
+ * cppc_get_highest_perf - Get the highest performance register value.
+ * @cpunum: CPU from which to get highest performance.
+ * @highest_perf: Return address.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_get_highest_perf(int cpunum, u64 *highest_perf)
+{
+ return cppc_get_reg_val(cpunum, HIGHEST_PERF, highest_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_highest_perf);
+
+/**
+ * cppc_get_epp_perf - Get the epp register value.
+ * @cpunum: CPU from which to get epp preference value.
+ * @epp_perf: Return address.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_get_epp_perf(int cpunum, u64 *epp_perf)
+{
+ return cppc_get_reg_val(cpunum, ENERGY_PERF, epp_perf);
}
+EXPORT_SYMBOL_GPL(cppc_get_epp_perf);
/**
* cppc_get_perf_caps - Get a CPU's performance capabilities.
@@ -1241,6 +1423,48 @@ out_err:
EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
/**
+ * cppc_perf_ctrs_in_pcc - Check if any perf counters are in a PCC region.
+ *
+ * CPPC has flexibility about how CPU performance counters are accessed.
+ * One of the choices is PCC regions, which can have a high access latency. This
+ * routine allows callers of cppc_get_perf_ctrs() to know this ahead of time.
+ *
+ * Return: true if any of the counters are in PCC regions, false otherwise
+ */
+bool cppc_perf_ctrs_in_pcc(void)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ struct cpc_register_resource *ref_perf_reg;
+ struct cpc_desc *cpc_desc;
+
+ cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+
+ if (CPC_IN_PCC(&cpc_desc->cpc_regs[DELIVERED_CTR]) ||
+ CPC_IN_PCC(&cpc_desc->cpc_regs[REFERENCE_CTR]) ||
+ CPC_IN_PCC(&cpc_desc->cpc_regs[CTR_WRAP_TIME]))
+ return true;
+
+
+ ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
+
+ /*
+ * If reference perf register is not supported then we should
+ * use the nominal perf value
+ */
+ if (!CPC_SUPPORTED(ref_perf_reg))
+ ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
+
+ if (CPC_IN_PCC(ref_perf_reg))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(cppc_perf_ctrs_in_pcc);
+
+/**
* cppc_get_perf_ctrs - Read a CPU's performance feedback counters.
* @cpunum: CPU from which to read counters.
* @perf_fb_ctrs: ptr to cppc_perf_fb_ctrs. See cppc_acpi.h
@@ -1320,48 +1544,191 @@ out_err:
}
EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
-/**
- * cppc_set_enable - Set to enable CPPC on the processor by writing the
- * Continuous Performance Control package EnableRegister field.
- * @cpu: CPU for which to enable CPPC register.
- * @enable: 0 - disable, 1 - enable CPPC feature on the processor.
- *
- * Return: 0 for success, -ERRNO or -EIO otherwise.
+/*
+ * Set Energy Performance Preference Register value through
+ * Performance Controls Interface
*/
-int cppc_set_enable(int cpu, bool enable)
+int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
{
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
- struct cpc_register_resource *enable_reg;
+ struct cpc_register_resource *epp_set_reg;
+ struct cpc_register_resource *auto_sel_reg;
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
struct cppc_pcc_data *pcc_ss_data = NULL;
- int ret = -EINVAL;
+ int ret;
if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpu);
- return -EINVAL;
+ return -ENODEV;
}
- enable_reg = &cpc_desc->cpc_regs[ENABLE];
+ auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
+ epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
- if (CPC_IN_PCC(enable_reg)) {
+ if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
+ if (pcc_ss_id < 0) {
+ pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
+ return -ENODEV;
+ }
- if (pcc_ss_id < 0)
- return -EIO;
+ if (CPC_SUPPORTED(auto_sel_reg)) {
+ ret = cpc_write(cpu, auto_sel_reg, enable);
+ if (ret)
+ return ret;
+ }
- ret = cpc_write(cpu, enable_reg, enable);
- if (ret)
- return ret;
+ if (CPC_SUPPORTED(epp_set_reg)) {
+ ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+ if (ret)
+ return ret;
+ }
pcc_ss_data = pcc_data[pcc_ss_id];
down_write(&pcc_ss_data->pcc_lock);
- /* after writing CPC, transfer the ownership of PCC to platfrom */
+ /* after writing CPC, transfer the ownership of PCC to platform */
ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
up_write(&pcc_ss_data->pcc_lock);
+ } else if (osc_cpc_flexible_adr_space_confirmed &&
+ CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
+ ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+ } else {
+ ret = -ENOTSUPP;
+ pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
+
+/**
+ * cppc_set_epp() - Write the EPP register.
+ * @cpu: CPU on which to write register.
+ * @epp_val: Value to write to the EPP register.
+ */
+int cppc_set_epp(int cpu, u64 epp_val)
+{
+ if (epp_val > CPPC_ENERGY_PERF_MAX)
+ return -EINVAL;
+
+ return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val);
+}
+EXPORT_SYMBOL_GPL(cppc_set_epp);
+
+/**
+ * cppc_get_auto_act_window() - Read autonomous activity window register.
+ * @cpu: CPU from which to read register.
+ * @auto_act_window: Return address.
+ *
+ * According to ACPI 6.5, s8.4.6.1.6, the value read from the autonomous
+ * activity window register consists of two parts: a 7 bits value indicate
+ * significand and a 3 bits value indicate exponent.
+ */
+int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
+{
+ unsigned int exp;
+ u64 val, sig;
+ int ret;
+
+ if (auto_act_window == NULL)
+ return -EINVAL;
+
+ ret = cppc_get_reg_val(cpu, AUTO_ACT_WINDOW, &val);
+ if (ret)
return ret;
+
+ sig = val & CPPC_AUTO_ACT_WINDOW_MAX_SIG;
+ exp = (val >> CPPC_AUTO_ACT_WINDOW_SIG_BIT_SIZE) & CPPC_AUTO_ACT_WINDOW_MAX_EXP;
+ *auto_act_window = sig * int_pow(10, exp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_get_auto_act_window);
+
+/**
+ * cppc_set_auto_act_window() - Write autonomous activity window register.
+ * @cpu: CPU on which to write register.
+ * @auto_act_window: usec value to write to the autonomous activity window register.
+ *
+ * According to ACPI 6.5, s8.4.6.1.6, the value to write to the autonomous
+ * activity window register consists of two parts: a 7 bits value indicate
+ * significand and a 3 bits value indicate exponent.
+ */
+int cppc_set_auto_act_window(int cpu, u64 auto_act_window)
+{
+ /* The max value to store is 1270000000 */
+ u64 max_val = CPPC_AUTO_ACT_WINDOW_MAX_SIG * int_pow(10, CPPC_AUTO_ACT_WINDOW_MAX_EXP);
+ int exp = 0;
+ u64 val;
+
+ if (auto_act_window > max_val)
+ return -EINVAL;
+
+ /*
+ * The max significand is 127, when auto_act_window is larger than
+ * 129, discard the precision of the last digit and increase the
+ * exponent by 1.
+ */
+ while (auto_act_window > CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH) {
+ auto_act_window /= 10;
+ exp += 1;
}
- return cpc_write(cpu, enable_reg, enable);
+ /* For 128 and 129, cut it to 127. */
+ if (auto_act_window > CPPC_AUTO_ACT_WINDOW_MAX_SIG)
+ auto_act_window = CPPC_AUTO_ACT_WINDOW_MAX_SIG;
+
+ val = (exp << CPPC_AUTO_ACT_WINDOW_SIG_BIT_SIZE) + auto_act_window;
+
+ return cppc_set_reg_val(cpu, AUTO_ACT_WINDOW, val);
+}
+EXPORT_SYMBOL_GPL(cppc_set_auto_act_window);
+
+/**
+ * cppc_get_auto_sel() - Read autonomous selection register.
+ * @cpu: CPU from which to read register.
+ * @enable: Return address.
+ */
+int cppc_get_auto_sel(int cpu, bool *enable)
+{
+ u64 auto_sel;
+ int ret;
+
+ if (enable == NULL)
+ return -EINVAL;
+
+ ret = cppc_get_reg_val(cpu, AUTO_SEL_ENABLE, &auto_sel);
+ if (ret)
+ return ret;
+
+ *enable = (bool)auto_sel;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_get_auto_sel);
+
+/**
+ * cppc_set_auto_sel - Write autonomous selection register.
+ * @cpu : CPU to which to write register.
+ * @enable : the desired value of autonomous selection resiter to be updated.
+ */
+int cppc_set_auto_sel(int cpu, bool enable)
+{
+ return cppc_set_reg_val(cpu, AUTO_SEL_ENABLE, enable);
+}
+EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
+
+/**
+ * cppc_set_enable - Set to enable CPPC on the processor by writing the
+ * Continuous Performance Control package EnableRegister field.
+ * @cpu: CPU for which to enable CPPC register.
+ * @enable: 0 - disable, 1 - enable CPPC feature on the processor.
+ *
+ * Return: 0 for success, -ERRNO or -EIO otherwise.
+ */
+int cppc_set_enable(int cpu, bool enable)
+{
+ return cppc_set_reg_val(cpu, ENABLE, enable);
}
EXPORT_SYMBOL_GPL(cppc_set_enable);
@@ -1375,7 +1742,7 @@ EXPORT_SYMBOL_GPL(cppc_set_enable);
int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
{
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
- struct cpc_register_resource *desired_reg;
+ struct cpc_register_resource *desired_reg, *min_perf_reg, *max_perf_reg;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cppc_pcc_data *pcc_ss_data = NULL;
int ret = 0;
@@ -1386,6 +1753,8 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
}
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+ min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
+ max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
/*
* This is Phase-I where we want to write to CPC registers
@@ -1394,7 +1763,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
* Since read_lock can be acquired by multiple CPUs simultaneously we
* achieve that goal here
*/
- if (CPC_IN_PCC(desired_reg)) {
+ if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
@@ -1417,13 +1786,19 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
cpc_desc->write_cmd_status = 0;
}
+ cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
+
/*
- * Skip writing MIN/MAX until Linux knows how to come up with
- * useful values.
+ * Only write if min_perf and max_perf not zero. Some drivers pass zero
+ * value to min and max perf, but they don't mean to set the zero value,
+ * they just don't want to write to those registers.
*/
- cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
+ if (perf_ctrls->min_perf)
+ cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf);
+ if (perf_ctrls->max_perf)
+ cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf);
- if (CPC_IN_PCC(desired_reg))
+ if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg))
up_read(&pcc_ss_data->pcc_lock); /* END Phase-I */
/*
* This is Phase-II where we transfer the ownership of PCC to Platform
@@ -1471,7 +1846,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
* case during a CMD_READ and if there are pending writes it delivers
* the write command before servicing the read command
*/
- if (CPC_IN_PCC(desired_reg)) {
+ if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) {
if (down_write_trylock(&pcc_ss_data->pcc_lock)) {/* BEGIN Phase-II */
/* Update only if there are pending write commands */
if (pcc_ss_data->pending_pcc_write_cmd)
@@ -1491,6 +1866,7 @@ EXPORT_SYMBOL_GPL(cppc_set_perf);
/**
* cppc_get_transition_latency - returns frequency transition latency in ns
+ * @cpu_num: CPU number for per_cpu().
*
* ACPI CPPC does not explicitly specify how a platform can specify the
* transition latency for performance change requests. The closest we have
@@ -1500,7 +1876,7 @@ EXPORT_SYMBOL_GPL(cppc_set_perf);
* If desired_reg is in the SystemMemory or SystemIo ACPI address space,
* then assume there is no latency.
*/
-unsigned int cppc_get_transition_latency(int cpu_num)
+int cppc_get_transition_latency(int cpu_num)
{
/*
* Expected transition latency is based on the PCCT timing values
@@ -1513,32 +1889,143 @@ unsigned int cppc_get_transition_latency(int cpu_num)
* completion of a command before issuing the next command,
* in microseconds.
*/
- unsigned int latency_ns = 0;
struct cpc_desc *cpc_desc;
struct cpc_register_resource *desired_reg;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu_num);
struct cppc_pcc_data *pcc_ss_data;
+ int latency_ns = 0;
cpc_desc = per_cpu(cpc_desc_ptr, cpu_num);
if (!cpc_desc)
- return CPUFREQ_ETERNAL;
+ return -ENODATA;
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
if (CPC_IN_SYSTEM_MEMORY(desired_reg) || CPC_IN_SYSTEM_IO(desired_reg))
return 0;
- else if (!CPC_IN_PCC(desired_reg))
- return CPUFREQ_ETERNAL;
- if (pcc_ss_id < 0)
- return CPUFREQ_ETERNAL;
+ if (!CPC_IN_PCC(desired_reg) || pcc_ss_id < 0)
+ return -ENODATA;
pcc_ss_data = pcc_data[pcc_ss_id];
if (pcc_ss_data->pcc_mpar)
latency_ns = 60 * (1000 * 1000 * 1000 / pcc_ss_data->pcc_mpar);
- latency_ns = max(latency_ns, pcc_ss_data->pcc_nominal * 1000);
- latency_ns = max(latency_ns, pcc_ss_data->pcc_mrtt * 1000);
+ latency_ns = max_t(int, latency_ns, pcc_ss_data->pcc_nominal * 1000);
+ latency_ns = max_t(int, latency_ns, pcc_ss_data->pcc_mrtt * 1000);
return latency_ns;
}
EXPORT_SYMBOL_GPL(cppc_get_transition_latency);
+
+/* Minimum struct length needed for the DMI processor entry we want */
+#define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48
+
+/* Offset in the DMI processor structure for the max frequency */
+#define DMI_PROCESSOR_MAX_SPEED 0x14
+
+/* Callback function used to retrieve the max frequency from DMI */
+static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
+{
+ const u8 *dmi_data = (const u8 *)dm;
+ u16 *mhz = (u16 *)private;
+
+ if (dm->type == DMI_ENTRY_PROCESSOR &&
+ dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) {
+ u16 val = (u16)get_unaligned((const u16 *)
+ (dmi_data + DMI_PROCESSOR_MAX_SPEED));
+ *mhz = umax(val, *mhz);
+ }
+}
+
+/* Look up the max frequency in DMI */
+static u64 cppc_get_dmi_max_khz(void)
+{
+ u16 mhz = 0;
+
+ dmi_walk(cppc_find_dmi_mhz, &mhz);
+
+ /*
+ * Real stupid fallback value, just in case there is no
+ * actual value set.
+ */
+ mhz = mhz ? mhz : 1;
+
+ return KHZ_PER_MHZ * mhz;
+}
+
+/*
+ * If CPPC lowest_freq and nominal_freq registers are exposed then we can
+ * use them to convert perf to freq and vice versa. The conversion is
+ * extrapolated as an affine function passing by the 2 points:
+ * - (Low perf, Low freq)
+ * - (Nominal perf, Nominal freq)
+ */
+unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf)
+{
+ s64 retval, offset = 0;
+ static u64 max_khz;
+ u64 mul, div;
+
+ if (caps->lowest_freq && caps->nominal_freq) {
+ /* Avoid special case when nominal_freq is equal to lowest_freq */
+ if (caps->lowest_freq == caps->nominal_freq) {
+ mul = caps->nominal_freq;
+ div = caps->nominal_perf;
+ } else {
+ mul = caps->nominal_freq - caps->lowest_freq;
+ div = caps->nominal_perf - caps->lowest_perf;
+ }
+ mul *= KHZ_PER_MHZ;
+ offset = caps->nominal_freq * KHZ_PER_MHZ -
+ div64_u64(caps->nominal_perf * mul, div);
+ } else {
+ if (!max_khz)
+ max_khz = cppc_get_dmi_max_khz();
+ mul = max_khz;
+ div = caps->highest_perf;
+ }
+
+ retval = offset + div64_u64(perf * mul, div);
+ if (retval >= 0)
+ return retval;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_perf_to_khz);
+
+unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq)
+{
+ s64 retval, offset = 0;
+ static u64 max_khz;
+ u64 mul, div;
+
+ if (caps->lowest_freq && caps->nominal_freq) {
+ /* Avoid special case when nominal_freq is equal to lowest_freq */
+ if (caps->lowest_freq == caps->nominal_freq) {
+ mul = caps->nominal_perf;
+ div = caps->nominal_freq;
+ } else {
+ mul = caps->nominal_perf - caps->lowest_perf;
+ div = caps->nominal_freq - caps->lowest_freq;
+ }
+ /*
+ * We don't need to convert to kHz for computing offset and can
+ * directly use nominal_freq and lowest_freq as the div64_u64
+ * will remove the frequency unit.
+ */
+ offset = caps->nominal_perf -
+ div64_u64(caps->nominal_freq * mul, div);
+ /* But we need it for computing the perf level. */
+ div *= KHZ_PER_MHZ;
+ } else {
+ if (!max_khz)
+ max_khz = cppc_get_dmi_max_khz();
+ mul = caps->highest_perf;
+ div = max_khz;
+ }
+
+ retval = offset + div64_u64(freq * mul, div);
+ if (retval >= 0)
+ return retval;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_khz_to_perf);
diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c
deleted file mode 100644
index d39a9b474727..000000000000
--- a/drivers/acpi/custom_method.c
+++ /dev/null
@@ -1,103 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * custom_method.c - debugfs interface for customizing ACPI control method
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/uaccess.h>
-#include <linux/debugfs.h>
-#include <linux/acpi.h>
-#include <linux/security.h>
-
-#include "internal.h"
-
-MODULE_LICENSE("GPL");
-
-static struct dentry *cm_dentry;
-
-/* /sys/kernel/debug/acpi/custom_method */
-
-static ssize_t cm_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- static char *buf;
- static u32 max_size;
- static u32 uncopied_bytes;
-
- struct acpi_table_header table;
- acpi_status status;
- int ret;
-
- ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
- if (ret)
- return ret;
-
- if (!(*ppos)) {
- /* parse the table header to get the table length */
- if (count <= sizeof(struct acpi_table_header))
- return -EINVAL;
- if (copy_from_user(&table, user_buf,
- sizeof(struct acpi_table_header)))
- return -EFAULT;
- uncopied_bytes = max_size = table.length;
- /* make sure the buf is not allocated */
- kfree(buf);
- buf = kzalloc(max_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- }
-
- if (buf == NULL)
- return -EINVAL;
-
- if ((*ppos > max_size) ||
- (*ppos + count > max_size) ||
- (*ppos + count < count) ||
- (count > uncopied_bytes)) {
- kfree(buf);
- buf = NULL;
- return -EINVAL;
- }
-
- if (copy_from_user(buf + (*ppos), user_buf, count)) {
- kfree(buf);
- buf = NULL;
- return -EFAULT;
- }
-
- uncopied_bytes -= count;
- *ppos += count;
-
- if (!uncopied_bytes) {
- status = acpi_install_method(buf);
- kfree(buf);
- buf = NULL;
- if (ACPI_FAILURE(status))
- return -EINVAL;
- add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
- }
-
- return count;
-}
-
-static const struct file_operations cm_fops = {
- .write = cm_write,
- .llseek = default_llseek,
-};
-
-static int __init acpi_custom_method_init(void)
-{
- cm_dentry = debugfs_create_file("custom_method", S_IWUSR,
- acpi_debugfs_dir, NULL, &cm_fops);
- return 0;
-}
-
-static void __exit acpi_custom_method_exit(void)
-{
- debugfs_remove(cm_dentry);
-}
-
-module_init(acpi_custom_method_init);
-module_exit(acpi_custom_method_exit);
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 9dce1245689c..4e0583274b8f 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -75,15 +75,17 @@ static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state)
int acpi_device_get_power(struct acpi_device *device, int *state)
{
int result = ACPI_STATE_UNKNOWN;
+ struct acpi_device *parent;
int error;
if (!device || !state)
return -EINVAL;
+ parent = acpi_dev_parent(device);
+
if (!device->flags.power_manageable) {
/* TBD: Non-recursive algorithm for walking up hierarchy. */
- *state = device->parent ?
- device->parent->power.state : ACPI_STATE_D0;
+ *state = parent ? parent->power.state : ACPI_STATE_D0;
goto out;
}
@@ -122,10 +124,10 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
* point, the fact that the device is in D0 implies that the parent has
* to be in D0 too, except if ignore_parent is set.
*/
- if (!device->power.flags.ignore_parent && device->parent
- && device->parent->power.state == ACPI_STATE_UNKNOWN
- && result == ACPI_STATE_D0)
- device->parent->power.state = ACPI_STATE_D0;
+ if (!device->power.flags.ignore_parent && parent &&
+ parent->power.state == ACPI_STATE_UNKNOWN &&
+ result == ACPI_STATE_D0)
+ parent->power.state = ACPI_STATE_D0;
*state = result;
@@ -191,13 +193,17 @@ int acpi_device_set_power(struct acpi_device *device, int state)
return -ENODEV;
}
- if (!device->power.flags.ignore_parent && device->parent &&
- state < device->parent->power.state) {
- acpi_handle_debug(device->handle,
- "Cannot transition to %s for parent in %s\n",
- acpi_power_state_string(state),
- acpi_power_state_string(device->parent->power.state));
- return -ENODEV;
+ if (!device->power.flags.ignore_parent) {
+ struct acpi_device *parent;
+
+ parent = acpi_dev_parent(device);
+ if (parent && state < parent->power.state) {
+ acpi_handle_debug(device->handle,
+ "Cannot transition to %s for parent in %s\n",
+ acpi_power_state_string(state),
+ acpi_power_state_string(parent->power.state));
+ return -ENODEV;
+ }
}
/*
@@ -391,6 +397,19 @@ void acpi_device_fix_up_power_extended(struct acpi_device *adev)
}
EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_extended);
+/**
+ * acpi_device_fix_up_power_children - Force a device's children into D0.
+ * @adev: Parent device object whose children's power state is to be fixed up.
+ *
+ * Call acpi_device_fix_up_power() for @adev's children so long as they
+ * are reported as present and enabled.
+ */
+void acpi_device_fix_up_power_children(struct acpi_device *adev)
+{
+ acpi_dev_for_each_child(adev, fix_up_power_if_applicable, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_children);
+
int acpi_device_update_power(struct acpi_device *device, int *state_p)
{
int state;
@@ -478,6 +497,25 @@ void acpi_dev_power_up_children_with_adr(struct acpi_device *adev)
acpi_dev_for_each_child(adev, acpi_power_up_if_adr_present, NULL);
}
+/**
+ * acpi_dev_power_state_for_wake - Deepest power state for wakeup signaling
+ * @adev: ACPI companion of the target device.
+ *
+ * Evaluate _S0W for @adev and return the value produced by it or return
+ * ACPI_STATE_UNKNOWN on errors (including _S0W not present).
+ */
+u8 acpi_dev_power_state_for_wake(struct acpi_device *adev)
+{
+ unsigned long long state;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(adev->handle, "_S0W", NULL, &state);
+ if (ACPI_FAILURE(status))
+ return ACPI_STATE_UNKNOWN;
+
+ return state;
+}
+
#ifdef CONFIG_PM
static DEFINE_MUTEX(acpi_pm_notifier_lock);
static DEFINE_MUTEX(acpi_pm_notifier_install_lock);
@@ -497,7 +535,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
acpi_handle_debug(handle, "Wake notify\n");
- adev = acpi_bus_get_acpi_device(handle);
+ adev = acpi_get_acpi_dev(handle);
if (!adev)
return;
@@ -515,7 +553,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
mutex_unlock(&acpi_pm_notifier_lock);
- acpi_bus_put_acpi_device(adev);
+ acpi_put_acpi_dev(adev);
}
/**
@@ -681,7 +719,22 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
d_min = ret;
wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid
&& adev->wakeup.sleep_state >= target_state;
+ } else if (device_may_wakeup(dev) && dev->power.wakeirq) {
+ /*
+ * The ACPI subsystem doesn't manage the wake bit for IRQs
+ * defined with ExclusiveAndWake and SharedAndWake. Instead we
+ * expect them to be managed via the PM subsystem. Drivers
+ * should call dev_pm_set_wake_irq to register an IRQ as a wake
+ * source.
+ *
+ * If a device has a wake IRQ attached we need to check the
+ * _S0W method to get the correct wake D-state. Otherwise we
+ * end up putting the device into D3Cold which will more than
+ * likely disable wake functionality.
+ */
+ wakeup = true;
} else {
+ /* ACPI GPE is specified in _PRW. */
wakeup = adev->wakeup.flags.valid;
}
@@ -1066,6 +1119,8 @@ int acpi_subsys_prepare(struct device *dev)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
+ dev_pm_set_strict_midlayer(dev, true);
+
if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) {
int ret = dev->driver->pm->prepare(dev);
@@ -1094,6 +1149,8 @@ void acpi_subsys_complete(struct device *dev)
*/
if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
pm_request_resume(dev);
+
+ dev_pm_set_strict_midlayer(dev, false);
}
EXPORT_SYMBOL_GPL(acpi_subsys_complete);
@@ -1108,7 +1165,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_complete);
*/
int acpi_subsys_suspend(struct device *dev)
{
- if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ if (!dev_pm_smart_suspend(dev) ||
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
pm_runtime_resume(dev);
@@ -1267,7 +1324,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
*/
int acpi_subsys_poweroff(struct device *dev)
{
- if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ if (!dev_pm_smart_suspend(dev) ||
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
pm_runtime_resume(dev);
@@ -1309,6 +1366,8 @@ static int acpi_subsys_poweroff_noirq(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP */
+static void acpi_dev_pm_detach(struct device *dev, bool power_off);
+
static struct dev_pm_domain acpi_general_pm_domain = {
.ops = {
.runtime_suspend = acpi_subsys_runtime_suspend,
@@ -1329,6 +1388,7 @@ static struct dev_pm_domain acpi_general_pm_domain = {
.restore_early = acpi_subsys_restore_early,
#endif
},
+ .detach = acpi_dev_pm_detach,
};
/**
@@ -1412,7 +1472,6 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
acpi_device_wakeup_disable(adev);
}
- dev->pm_domain->detach = acpi_dev_pm_detach;
return 1;
}
EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
@@ -1460,7 +1519,7 @@ EXPORT_SYMBOL_GPL(acpi_storage_d3);
* not valid to ask for the ACPI power state of the device in that time frame.
*
* This function is intended to be used in a driver's probe or remove
- * function. See Documentation/firmware-guide/acpi/low-power-probe.rst for
+ * function. See Documentation/firmware-guide/acpi/non-d0-probe.rst for
* more information.
*/
bool acpi_dev_state_d0(struct device *dev)
diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c
index 120873dad2cc..cd199fbe4dc9 100644
--- a/drivers/acpi/device_sysfs.c
+++ b/drivers/acpi/device_sysfs.c
@@ -78,7 +78,7 @@ static void acpi_data_node_release(struct kobject *kobj)
complete(&dn->kobj_done);
}
-static struct kobj_type acpi_data_node_ktype = {
+static const struct kobj_type acpi_data_node_ktype = {
.sysfs_ops = &acpi_data_node_sysfs_ops,
.default_groups = acpi_data_node_default_groups,
.release = acpi_data_node_release,
@@ -133,7 +133,7 @@ static void acpi_hide_nondev_subnodes(struct acpi_device_data *data)
* -EINVAL: output error
* -ENOMEM: output is truncated
*/
-static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
+static int create_pnp_modalias(const struct acpi_device *acpi_dev, char *modalias,
int size)
{
int len;
@@ -158,8 +158,8 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
return 0;
len = snprintf(modalias, size, "acpi:");
- if (len <= 0)
- return len;
+ if (len >= size)
+ return -ENOMEM;
size -= len;
@@ -168,8 +168,6 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
continue;
count = snprintf(&modalias[len], size, "%s:", id->id);
- if (count < 0)
- return -EINVAL;
if (count >= size)
return -ENOMEM;
@@ -177,7 +175,7 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
len += count;
size -= count;
}
- modalias[len] = '\0';
+
return len;
}
@@ -191,7 +189,7 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
* only be called for devices having ACPI_DT_NAMESPACE_HID in their list of
* ACPI/PNP IDs.
*/
-static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
+static int create_of_modalias(const struct acpi_device *acpi_dev, char *modalias,
int size)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
@@ -212,8 +210,10 @@ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
ACPI_FREE(buf.pointer);
- if (len <= 0)
- return len;
+ if (len >= size)
+ return -ENOMEM;
+
+ size -= len;
of_compatible = acpi_dev->data.of_compatible;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
@@ -226,8 +226,6 @@ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
for (i = 0; i < nval; i++, obj++) {
count = snprintf(&modalias[len], size, "C%s",
obj->string.pointer);
- if (count < 0)
- return -EINVAL;
if (count >= size)
return -ENOMEM;
@@ -235,11 +233,11 @@ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
len += count;
size -= count;
}
- modalias[len] = '\0';
+
return len;
}
-int __acpi_device_uevent_modalias(struct acpi_device *adev,
+int __acpi_device_uevent_modalias(const struct acpi_device *adev,
struct kobj_uevent_env *env)
{
int len;
@@ -277,13 +275,13 @@ int __acpi_device_uevent_modalias(struct acpi_device *adev,
* Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with
* hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001".
*/
-int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
+int acpi_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env)
{
return __acpi_device_uevent_modalias(acpi_companion_match(dev), env);
}
EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
-static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size)
+static int __acpi_device_modalias(const struct acpi_device *adev, char *buf, int size)
{
int len, count;
@@ -410,7 +408,7 @@ static ssize_t uid_show(struct device *dev,
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
- return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id);
+ return sprintf(buf, "%s\n", acpi_device_uid(acpi_dev));
}
static DEVICE_ATTR_RO(uid);
@@ -441,23 +439,33 @@ static ssize_t description_show(struct device *dev,
char *buf)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *str_obj;
+ acpi_status status;
int result;
- if (acpi_dev->pnp.str_obj == NULL)
- return 0;
+ status = acpi_evaluate_object_typed(acpi_dev->handle, "_STR",
+ NULL, &buffer,
+ ACPI_TYPE_BUFFER);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ str_obj = buffer.pointer;
/*
* The _STR object contains a Unicode identifier for a device.
* We need to convert to utf-8 so it can be displayed.
*/
result = utf16s_to_utf8s(
- (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer,
- acpi_dev->pnp.str_obj->buffer.length,
+ (wchar_t *)str_obj->buffer.pointer,
+ str_obj->buffer.length,
UTF16_LITTLE_ENDIAN, buf,
PAGE_SIZE - 1);
buf[result++] = '\n';
+ ACPI_FREE(str_obj);
+
return result;
}
static DEVICE_ATTR_RO(description);
@@ -509,96 +517,97 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(status);
-/**
- * acpi_device_setup_files - Create sysfs attributes of an ACPI device.
- * @dev: ACPI device object.
- */
-int acpi_device_setup_files(struct acpi_device *dev)
-{
- struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
- acpi_status status;
- int result = 0;
+static struct attribute *acpi_attrs[] = {
+ &dev_attr_path.attr,
+ &dev_attr_hid.attr,
+ &dev_attr_modalias.attr,
+ &dev_attr_description.attr,
+ &dev_attr_adr.attr,
+ &dev_attr_uid.attr,
+ &dev_attr_sun.attr,
+ &dev_attr_hrv.attr,
+ &dev_attr_status.attr,
+ &dev_attr_eject.attr,
+ &dev_attr_power_state.attr,
+ &dev_attr_real_power_state.attr,
+ NULL
+};
+static bool acpi_show_attr(struct acpi_device *dev, const struct device_attribute *attr)
+{
/*
* Devices gotten from FADT don't have a "path" attribute
*/
- if (dev->handle) {
- result = device_create_file(&dev->dev, &dev_attr_path);
- if (result)
- goto end;
- }
+ if (attr == &dev_attr_path)
+ return dev->handle;
- if (!list_empty(&dev->pnp.ids)) {
- result = device_create_file(&dev->dev, &dev_attr_hid);
- if (result)
- goto end;
+ if (attr == &dev_attr_hid || attr == &dev_attr_modalias)
+ return !list_empty(&dev->pnp.ids);
- result = device_create_file(&dev->dev, &dev_attr_modalias);
- if (result)
- goto end;
- }
+ if (attr == &dev_attr_description)
+ return acpi_has_method(dev->handle, "_STR");
- /*
- * If device has _STR, 'description' file is created
- */
- if (acpi_has_method(dev->handle, "_STR")) {
- status = acpi_evaluate_object(dev->handle, "_STR",
- NULL, &buffer);
- if (ACPI_FAILURE(status))
- buffer.pointer = NULL;
- dev->pnp.str_obj = buffer.pointer;
- result = device_create_file(&dev->dev, &dev_attr_description);
- if (result)
- goto end;
- }
+ if (attr == &dev_attr_adr)
+ return dev->pnp.type.bus_address;
- if (dev->pnp.type.bus_address)
- result = device_create_file(&dev->dev, &dev_attr_adr);
- if (dev->pnp.unique_id)
- result = device_create_file(&dev->dev, &dev_attr_uid);
+ if (attr == &dev_attr_uid)
+ return acpi_device_uid(dev);
- if (acpi_has_method(dev->handle, "_SUN")) {
- result = device_create_file(&dev->dev, &dev_attr_sun);
- if (result)
- goto end;
- }
+ if (attr == &dev_attr_sun)
+ return acpi_has_method(dev->handle, "_SUN");
- if (acpi_has_method(dev->handle, "_HRV")) {
- result = device_create_file(&dev->dev, &dev_attr_hrv);
- if (result)
- goto end;
- }
+ if (attr == &dev_attr_hrv)
+ return acpi_has_method(dev->handle, "_HRV");
- if (acpi_has_method(dev->handle, "_STA")) {
- result = device_create_file(&dev->dev, &dev_attr_status);
- if (result)
- goto end;
- }
+ if (attr == &dev_attr_status)
+ return acpi_has_method(dev->handle, "_STA");
/*
* If device has _EJ0, 'eject' file is created that is used to trigger
* hot-removal function from userland.
*/
- if (acpi_has_method(dev->handle, "_EJ0")) {
- result = device_create_file(&dev->dev, &dev_attr_eject);
- if (result)
- return result;
- }
+ if (attr == &dev_attr_eject)
+ return acpi_has_method(dev->handle, "_EJ0");
- if (dev->flags.power_manageable) {
- result = device_create_file(&dev->dev, &dev_attr_power_state);
- if (result)
- return result;
+ if (attr == &dev_attr_power_state)
+ return dev->flags.power_manageable;
- if (dev->power.flags.power_resources)
- result = device_create_file(&dev->dev,
- &dev_attr_real_power_state);
- }
+ if (attr == &dev_attr_real_power_state)
+ return dev->flags.power_manageable && dev->power.flags.power_resources;
- acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data);
+ dev_warn_once(&dev->dev, "Unexpected attribute: %s\n", attr->attr.name);
+ return false;
+}
-end:
- return result;
+static umode_t acpi_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr,
+ int attrno)
+{
+ struct acpi_device *dev = to_acpi_device(kobj_to_dev(kobj));
+
+ if (acpi_show_attr(dev, container_of(attr, struct device_attribute, attr)))
+ return attr->mode;
+ else
+ return 0;
+}
+
+static const struct attribute_group acpi_group = {
+ .attrs = acpi_attrs,
+ .is_visible = acpi_attr_is_visible,
+};
+
+const struct attribute_group *acpi_groups[] = {
+ &acpi_group,
+ NULL
+};
+
+/**
+ * acpi_device_setup_files - Create sysfs attributes of an ACPI device.
+ * @dev: ACPI device object.
+ */
+void acpi_device_setup_files(struct acpi_device *dev)
+{
+ acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data);
}
/**
@@ -608,41 +617,4 @@ end:
void acpi_device_remove_files(struct acpi_device *dev)
{
acpi_hide_nondev_subnodes(&dev->data);
-
- if (dev->flags.power_manageable) {
- device_remove_file(&dev->dev, &dev_attr_power_state);
- if (dev->power.flags.power_resources)
- device_remove_file(&dev->dev,
- &dev_attr_real_power_state);
- }
-
- /*
- * If device has _STR, remove 'description' file
- */
- if (acpi_has_method(dev->handle, "_STR")) {
- kfree(dev->pnp.str_obj);
- device_remove_file(&dev->dev, &dev_attr_description);
- }
- /*
- * If device has _EJ0, remove 'eject' file.
- */
- if (acpi_has_method(dev->handle, "_EJ0"))
- device_remove_file(&dev->dev, &dev_attr_eject);
-
- if (acpi_has_method(dev->handle, "_SUN"))
- device_remove_file(&dev->dev, &dev_attr_sun);
-
- if (acpi_has_method(dev->handle, "_HRV"))
- device_remove_file(&dev->dev, &dev_attr_hrv);
-
- if (dev->pnp.unique_id)
- device_remove_file(&dev->dev, &dev_attr_uid);
- if (dev->pnp.type.bus_address)
- device_remove_file(&dev->dev, &dev_attr_adr);
- device_remove_file(&dev->dev, &dev_attr_modalias);
- device_remove_file(&dev->dev, &dev_attr_hid);
- if (acpi_has_method(dev->handle, "_STA"))
- device_remove_file(&dev->dev, &dev_attr_status);
- if (dev->handle)
- device_remove_file(&dev->dev, &dev_attr_path);
}
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index a89bdbe00184..34affbda295e 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -88,43 +88,29 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
enum dock_callback_type cb_type)
{
struct acpi_device *adev = dd->adev;
+ acpi_hp_fixup fixup = NULL;
+ acpi_hp_uevent uevent = NULL;
+ acpi_hp_notify notify = NULL;
acpi_lock_hp_context();
- if (!adev->hp)
- goto out;
-
- if (cb_type == DOCK_CALL_FIXUP) {
- void (*fixup)(struct acpi_device *);
-
- fixup = adev->hp->fixup;
- if (fixup) {
- acpi_unlock_hp_context();
- fixup(adev);
- return;
- }
- } else if (cb_type == DOCK_CALL_UEVENT) {
- void (*uevent)(struct acpi_device *, u32);
-
- uevent = adev->hp->uevent;
- if (uevent) {
- acpi_unlock_hp_context();
- uevent(adev, event);
- return;
- }
- } else {
- int (*notify)(struct acpi_device *, u32);
-
- notify = adev->hp->notify;
- if (notify) {
- acpi_unlock_hp_context();
- notify(adev, event);
- return;
- }
+ if (adev->hp) {
+ if (cb_type == DOCK_CALL_FIXUP)
+ fixup = adev->hp->fixup;
+ else if (cb_type == DOCK_CALL_UEVENT)
+ uevent = adev->hp->uevent;
+ else
+ notify = adev->hp->notify;
}
- out:
acpi_unlock_hp_context();
+
+ if (fixup)
+ fixup(adev);
+ else if (uevent)
+ uevent(adev, event);
+ else if (notify)
+ notify(adev, event);
}
static struct dock_station *find_dock_station(acpi_handle handle)
@@ -380,6 +366,8 @@ static int dock_in_progress(struct dock_station *ds)
/**
* handle_eject_request - handle an undock request checking for error conditions
+ * @ds: The dock station to undock.
+ * @event: The ACPI event number associated with the undock request.
*
* Check to make sure the dock device is still present, then undock and
* hotremove all the devices that may need removing.
diff --git a/drivers/acpi/dptf/Kconfig b/drivers/acpi/dptf/Kconfig
index 1e8c7ce89bf1..4b3fdc03e4ed 100644
--- a/drivers/acpi/dptf/Kconfig
+++ b/drivers/acpi/dptf/Kconfig
@@ -11,9 +11,6 @@ menuconfig ACPI_DPTF
a coordinated approach for different policies to effect the hardware
state of a system.
- For more information see:
- <https://01.org/intel%C2%AE-dynamic-platform-and-thermal-framework-dptf-chromium-os/overview>
-
if ACPI_DPTF
config DPTF_POWER
diff --git a/drivers/acpi/dptf/Makefile b/drivers/acpi/dptf/Makefile
index 297340682f66..e912a3be1d28 100644
--- a/drivers/acpi/dptf/Makefile
+++ b/drivers/acpi/dptf/Makefile
@@ -1,4 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_ACPI) += int340x_thermal.o
obj-$(CONFIG_DPTF_POWER) += dptf_power.o
obj-$(CONFIG_DPTF_PCH_FIVR) += dptf_pch_fivr.o
diff --git a/drivers/acpi/dptf/dptf_pch_fivr.c b/drivers/acpi/dptf/dptf_pch_fivr.c
index 4919e7abe93f..8d7e555929d3 100644
--- a/drivers/acpi/dptf/dptf_pch_fivr.c
+++ b/drivers/acpi/dptf/dptf_pch_fivr.c
@@ -41,7 +41,7 @@ static int pch_fivr_read(acpi_handle handle, char *method, struct pch_fivr_resp
ret = 0;
release_buffer:
- kfree(buffer.pointer);
+ ACPI_FREE(buffer.pointer);
return ret;
}
@@ -141,18 +141,18 @@ static int pch_fivr_add(struct platform_device *pdev)
return 0;
}
-static int pch_fivr_remove(struct platform_device *pdev)
+static void pch_fivr_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &pch_fivr_attribute_group);
-
- return 0;
}
static const struct acpi_device_id pch_fivr_device_ids[] = {
{"INTC1045", 0},
{"INTC1049", 0},
{"INTC1064", 0},
+ {"INTC106B", 0},
{"INTC10A3", 0},
+ {"INTC10D7", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, pch_fivr_device_ids);
diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c
index 86561eda939f..55ccbb8ddbe3 100644
--- a/drivers/acpi/dptf/dptf_power.c
+++ b/drivers/acpi/dptf/dptf_power.c
@@ -209,7 +209,7 @@ static int dptf_power_add(struct platform_device *pdev)
return 0;
}
-static int dptf_power_remove(struct platform_device *pdev)
+static void dptf_power_remove(struct platform_device *pdev)
{
struct acpi_device *acpi_dev = platform_get_drvdata(pdev);
@@ -221,8 +221,6 @@ static int dptf_power_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group);
else
sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group);
-
- return 0;
}
static const struct acpi_device_id int3407_device_ids[] = {
@@ -234,8 +232,16 @@ static const struct acpi_device_id int3407_device_ids[] = {
{"INTC1061", 0},
{"INTC1065", 0},
{"INTC1066", 0},
+ {"INTC106C", 0},
+ {"INTC106D", 0},
{"INTC10A4", 0},
{"INTC10A5", 0},
+ {"INTC10D8", 0},
+ {"INTC10D9", 0},
+ {"INTC1100", 0},
+ {"INTC1101", 0},
+ {"INTC10F7", 0},
+ {"INTC10F8", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3407_device_ids);
diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c
deleted file mode 100644
index b7113fa92fa6..000000000000
--- a/drivers/acpi/dptf/int340x_thermal.c
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ACPI support for int340x thermal drivers
- *
- * Copyright (C) 2014, Intel Corporation
- * Authors: Zhang Rui <rui.zhang@intel.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/module.h>
-
-#include "../internal.h"
-
-#define INT3401_DEVICE 0X01
-static const struct acpi_device_id int340x_thermal_device_ids[] = {
- {"INT3400"},
- {"INT3401", INT3401_DEVICE},
- {"INT3402"},
- {"INT3403"},
- {"INT3404"},
- {"INT3406"},
- {"INT3407"},
- {"INT3408"},
- {"INT3409"},
- {"INT340A"},
- {"INT340B"},
- {"INT3532"},
- {"INTC1040"},
- {"INTC1041"},
- {"INTC1042"},
- {"INTC1043"},
- {"INTC1044"},
- {"INTC1045"},
- {"INTC1046"},
- {"INTC1047"},
- {"INTC1048"},
- {"INTC1049"},
- {"INTC1050"},
- {"INTC1060"},
- {"INTC1061"},
- {"INTC1062"},
- {"INTC1063"},
- {"INTC1064"},
- {"INTC1065"},
- {"INTC1066"},
- {"INTC10A0"},
- {"INTC10A1"},
- {"INTC10A2"},
- {"INTC10A3"},
- {"INTC10A4"},
- {"INTC10A5"},
- {""},
-};
-
-static int int340x_thermal_handler_attach(struct acpi_device *adev,
- const struct acpi_device_id *id)
-{
- if (IS_ENABLED(CONFIG_INT340X_THERMAL))
- acpi_create_platform_device(adev, NULL);
- /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */
- else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) &&
- id->driver_data == INT3401_DEVICE)
- acpi_create_platform_device(adev, NULL);
- return 1;
-}
-
-static struct acpi_scan_handler int340x_thermal_handler = {
- .ids = int340x_thermal_device_ids,
- .attach = int340x_thermal_handler_attach,
-};
-
-void __init acpi_int340x_thermal_init(void)
-{
- acpi_scan_add_handler(&int340x_thermal_handler);
-}
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index c95e535035a0..59b3d50ff01e 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -23,8 +23,10 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/list.h>
+#include <linux/printk.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/suspend.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -94,6 +96,7 @@ enum {
EC_FLAGS_QUERY_ENABLED, /* Query is enabled */
EC_FLAGS_EVENT_HANDLER_INSTALLED, /* Event handler installed */
EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */
+ EC_FLAGS_EC_REG_CALLED, /* OpReg ACPI _REG method called */
EC_FLAGS_QUERY_METHODS_INSTALLED, /* _Qxx handlers installed */
EC_FLAGS_STARTED, /* Driver is started */
EC_FLAGS_STOPPED, /* Driver is stopped */
@@ -661,21 +664,6 @@ static void advance_transaction(struct acpi_ec *ec, bool interrupt)
ec_dbg_stm("%s (%d)", interrupt ? "IRQ" : "TASK", smp_processor_id());
- /*
- * Clear GPE_STS upfront to allow subsequent hardware GPE_STS 0->1
- * changes to always trigger a GPE interrupt.
- *
- * GPE STS is a W1C register, which means:
- *
- * 1. Software can clear it without worrying about clearing the other
- * GPEs' STS bits when the hardware sets them in parallel.
- *
- * 2. As long as software can ensure only clearing it when it is set,
- * hardware won't set it in parallel.
- */
- if (ec->gpe >= 0 && acpi_ec_gpe_status_set(ec))
- acpi_clear_gpe(NULL, ec->gpe);
-
status = acpi_ec_read_status(ec);
/*
@@ -797,6 +785,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
unsigned long tmp;
int ret = 0;
+ if (t->rdata)
+ memset(t->rdata, 0, t->rlen);
+
/* start transaction */
spin_lock_irqsave(&ec->lock, tmp);
/* Enable GPE for command processing (IBF=0/OBF=1) */
@@ -833,8 +824,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata))
return -EINVAL;
- if (t->rdata)
- memset(t->rdata, 0, t->rlen);
mutex_lock(&ec->mutex);
if (ec->global_lock) {
@@ -861,7 +850,7 @@ static int acpi_ec_burst_enable(struct acpi_ec *ec)
.wdata = NULL, .rdata = &d,
.wlen = 0, .rlen = 1};
- return acpi_ec_transaction(ec, &t);
+ return acpi_ec_transaction_unlocked(ec, &t);
}
static int acpi_ec_burst_disable(struct acpi_ec *ec)
@@ -871,7 +860,7 @@ static int acpi_ec_burst_disable(struct acpi_ec *ec)
.wlen = 0, .rlen = 0};
return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ?
- acpi_ec_transaction(ec, &t) : 0;
+ acpi_ec_transaction_unlocked(ec, &t) : 0;
}
static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data)
@@ -887,6 +876,19 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data)
return result;
}
+static int acpi_ec_read_unlocked(struct acpi_ec *ec, u8 address, u8 *data)
+{
+ int result;
+ u8 d;
+ struct transaction t = {.command = ACPI_EC_COMMAND_READ,
+ .wdata = &address, .rdata = &d,
+ .wlen = 1, .rlen = 1};
+
+ result = acpi_ec_transaction_unlocked(ec, &t);
+ *data = d;
+ return result;
+}
+
static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
{
u8 wdata[2] = { address, data };
@@ -897,6 +899,16 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
return acpi_ec_transaction(ec, &t);
}
+static int acpi_ec_write_unlocked(struct acpi_ec *ec, u8 address, u8 data)
+{
+ u8 wdata[2] = { address, data };
+ struct transaction t = {.command = ACPI_EC_COMMAND_WRITE,
+ .wdata = wdata, .rdata = NULL,
+ .wlen = 2, .rlen = 0};
+
+ return acpi_ec_transaction_unlocked(ec, &t);
+}
+
int ec_read(u8 addr, u8 *val)
{
int err;
@@ -917,14 +929,10 @@ EXPORT_SYMBOL(ec_read);
int ec_write(u8 addr, u8 val)
{
- int err;
-
if (!first_ec)
return -ENODEV;
- err = acpi_ec_write(first_ec, addr, val);
-
- return err;
+ return acpi_ec_write(first_ec, addr, val);
}
EXPORT_SYMBOL(ec_write);
@@ -1086,9 +1094,12 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func,
void *data)
{
- struct acpi_ec_query_handler *handler =
- kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL);
+ struct acpi_ec_query_handler *handler;
+
+ if (!handle && !func)
+ return -EINVAL;
+ handler = kzalloc(sizeof(*handler), GFP_KERNEL);
if (!handler)
return -ENOMEM;
@@ -1100,6 +1111,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
kref_init(&handler->kref);
list_add(&handler->node, &ec->list);
mutex_unlock(&ec->mutex);
+
return 0;
}
EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);
@@ -1112,9 +1124,16 @@ static void acpi_ec_remove_query_handlers(struct acpi_ec *ec,
mutex_lock(&ec->mutex);
list_for_each_entry_safe(handler, tmp, &ec->list, node) {
- if (remove_all || query_bit == handler->query_bit) {
+ /*
+ * When remove_all is false, only remove custom query handlers
+ * which have handler->func set. This is done to preserve query
+ * handlers discovered thru ACPI, as they should continue handling
+ * EC queries.
+ */
+ if (remove_all || (handler->func && handler->query_bit == query_bit)) {
list_del_init(&handler->node);
list_add(&handler->node, &free_list);
+
}
}
mutex_unlock(&ec->mutex);
@@ -1125,6 +1144,7 @@ static void acpi_ec_remove_query_handlers(struct acpi_ec *ec,
void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
{
acpi_ec_remove_query_handlers(ec, false, query_bit);
+ flush_workqueue(ec_query_wq);
}
EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
@@ -1273,12 +1293,34 @@ static void acpi_ec_event_handler(struct work_struct *work)
spin_unlock_irq(&ec->lock);
}
+static void clear_gpe_and_advance_transaction(struct acpi_ec *ec, bool interrupt)
+{
+ /*
+ * Clear GPE_STS upfront to allow subsequent hardware GPE_STS 0->1
+ * changes to always trigger a GPE interrupt.
+ *
+ * GPE STS is a W1C register, which means:
+ *
+ * 1. Software can clear it without worrying about clearing the other
+ * GPEs' STS bits when the hardware sets them in parallel.
+ *
+ * 2. As long as software can ensure only clearing it when it is set,
+ * hardware won't set it in parallel.
+ */
+ if (ec->gpe >= 0 && acpi_ec_gpe_status_set(ec))
+ acpi_clear_gpe(NULL, ec->gpe);
+
+ advance_transaction(ec, true);
+}
+
static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
- advance_transaction(ec, true);
+
+ clear_gpe_and_advance_transaction(ec, true);
+
spin_unlock_irqrestore(&ec->lock, flags);
}
@@ -1307,6 +1349,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
struct acpi_ec *ec = handler_context;
int result = 0, i, bytes = bits / 8;
u8 *value = (u8 *)value64;
+ u32 glk;
if ((address > 0xFF) || !value || !handler_context)
return AE_BAD_PARAMETER;
@@ -1314,17 +1357,38 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
if (function != ACPI_READ && function != ACPI_WRITE)
return AE_BAD_PARAMETER;
+ mutex_lock(&ec->mutex);
+
+ if (ec->global_lock) {
+ acpi_status status;
+
+ status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
+ if (ACPI_FAILURE(status)) {
+ result = -ENODEV;
+ goto unlock;
+ }
+ }
+
if (ec->busy_polling || bits > 8)
acpi_ec_burst_enable(ec);
- for (i = 0; i < bytes; ++i, ++address, ++value)
+ for (i = 0; i < bytes; ++i, ++address, ++value) {
result = (function == ACPI_READ) ?
- acpi_ec_read(ec, address, value) :
- acpi_ec_write(ec, address, *value);
+ acpi_ec_read_unlocked(ec, address, value) :
+ acpi_ec_write_unlocked(ec, address, *value);
+ if (result < 0)
+ break;
+ }
if (ec->busy_polling || bits > 8)
acpi_ec_burst_disable(ec);
+ if (ec->global_lock)
+ acpi_release_global_lock(glk);
+
+unlock:
+ mutex_unlock(&ec->mutex);
+
switch (result) {
case -EINVAL:
return AE_BAD_PARAMETER;
@@ -1332,8 +1396,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
return AE_NOT_FOUND;
case -ETIME:
return AE_TIME;
- default:
+ case 0:
return AE_OK;
+ default:
+ return AE_ERROR;
}
}
@@ -1442,14 +1508,15 @@ static bool install_gpe_event_handler(struct acpi_ec *ec)
static bool install_gpio_irq_event_handler(struct acpi_ec *ec)
{
- return request_irq(ec->irq, acpi_ec_irq_handler, IRQF_SHARED,
- "ACPI EC", ec) >= 0;
+ return request_threaded_irq(ec->irq, NULL, acpi_ec_irq_handler,
+ IRQF_SHARED | IRQF_ONESHOT, "ACPI EC", ec) >= 0;
}
/**
* ec_install_handlers - Install service callbacks and register query methods.
* @ec: Target EC.
* @device: ACPI device object corresponding to @ec.
+ * @call_reg: If _REG should be called to notify OpRegion availability
*
* Install a handler for the EC address space type unless it has been installed
* already. If @device is not NULL, also look for EC query methods in the
@@ -1462,18 +1529,21 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec)
* -EPROBE_DEFER if GPIO IRQ acquisition needs to be deferred,
* or 0 (success) otherwise.
*/
-static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device)
+static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device,
+ bool call_reg)
{
acpi_status status;
acpi_ec_start(ec, false);
if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
+ acpi_handle scope_handle = ec == first_ec ? ACPI_ROOT_OBJECT : ec->handle;
+
acpi_ec_enter_noirq(ec);
- status = acpi_install_address_space_handler(ec->handle,
- ACPI_ADR_SPACE_EC,
- &acpi_ec_space_handler,
- NULL, ec);
+ status = acpi_install_address_space_handler_no_reg(scope_handle,
+ ACPI_ADR_SPACE_EC,
+ &acpi_ec_space_handler,
+ NULL, ec);
if (ACPI_FAILURE(status)) {
acpi_ec_stop(ec, false);
return -ENODEV;
@@ -1481,6 +1551,11 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device)
set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
}
+ if (call_reg && !test_bit(EC_FLAGS_EC_REG_CALLED, &ec->flags)) {
+ acpi_execute_reg_methods(ec->handle, ACPI_UINT32_MAX, ACPI_ADR_SPACE_EC);
+ set_bit(EC_FLAGS_EC_REG_CALLED, &ec->flags);
+ }
+
if (!device)
return 0;
@@ -1529,9 +1604,13 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device)
static void ec_remove_handlers(struct acpi_ec *ec)
{
+ acpi_handle scope_handle = ec == first_ec ? ACPI_ROOT_OBJECT : ec->handle;
+
if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
- if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
- ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
+ if (ACPI_FAILURE(acpi_remove_address_space_handler(
+ scope_handle,
+ ACPI_ADR_SPACE_EC,
+ &acpi_ec_space_handler)))
pr_err("failed to remove space handler\n");
clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
}
@@ -1566,18 +1645,22 @@ static void ec_remove_handlers(struct acpi_ec *ec)
}
}
-static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device)
+static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool call_reg)
{
int ret;
- ret = ec_install_handlers(ec, device);
- if (ret)
- return ret;
-
/* First EC capable of handling transactions */
if (!first_ec)
first_ec = ec;
+ ret = ec_install_handlers(ec, device, call_reg);
+ if (ret) {
+ if (ec == first_ec)
+ first_ec = NULL;
+
+ return ret;
+ }
+
pr_info("EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", ec->command_addr,
ec->data_addr);
@@ -1596,8 +1679,8 @@ static int acpi_ec_add(struct acpi_device *device)
struct acpi_ec *ec;
int ret;
- strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_EC_CLASS);
+ strscpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_EC_CLASS);
if (boot_ec && (boot_ec->handle == device->handle ||
!strcmp(acpi_device_hid(device), ACPI_ECDT_HID))) {
@@ -1635,7 +1718,7 @@ static int acpi_ec_add(struct acpi_device *device)
}
}
- ret = acpi_ec_setup(ec, device);
+ ret = acpi_ec_setup(ec, device, true);
if (ret)
goto err;
@@ -1667,12 +1750,12 @@ err:
return ret;
}
-static int acpi_ec_remove(struct acpi_device *device)
+static void acpi_ec_remove(struct acpi_device *device)
{
struct acpi_ec *ec;
if (!device)
- return -EINVAL;
+ return;
ec = acpi_driver_data(device);
release_region(ec->data_addr, 1);
@@ -1682,7 +1765,12 @@ static int acpi_ec_remove(struct acpi_device *device)
ec_remove_handlers(ec);
acpi_ec_free(ec);
}
- return 0;
+}
+
+void acpi_ec_register_opregions(struct acpi_device *adev)
+{
+ if (first_ec && first_ec->handle != adev->handle)
+ acpi_execute_reg_methods(adev->handle, 1, ACPI_ADR_SPACE_EC);
}
static acpi_status
@@ -1755,7 +1843,7 @@ void __init acpi_ec_dsdt_probe(void)
* At this point, the GPE is not fully initialized, so do not to
* handle the events.
*/
- ret = acpi_ec_setup(ec, NULL);
+ ret = acpi_ec_setup(ec, NULL, true);
if (ret) {
acpi_ec_free(ec);
return;
@@ -1881,6 +1969,37 @@ static const struct dmi_system_id ec_dmi_table[] __initconst = {
},
{
/*
+ * HP Pavilion Gaming Laptop 15-cx0041ur
+ */
+ .callback = ec_honor_dsdt_gpe,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP 15-cx0041ur"),
+ },
+ },
+ {
+ /*
+ * HP Pavilion Gaming Laptop 15-dk1xxx
+ * https://github.com/systemd/systemd/issues/28942
+ */
+ .callback = ec_honor_dsdt_gpe,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Gaming Laptop 15-dk1xxx"),
+ },
+ },
+ {
+ /*
+ * HP 250 G7 Notebook PC
+ */
+ .callback = ec_honor_dsdt_gpe,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP 250 G7 Notebook PC"),
+ },
+ },
+ {
+ /*
* Samsung hardware
* https://bugzilla.kernel.org/show_bug.cgi?id=44161
*/
@@ -1914,6 +2033,25 @@ void __init acpi_ec_ecdt_probe(void)
goto out;
}
+ if (!strlen(ecdt_ptr->id)) {
+ /*
+ * The ECDT table on some MSI notebooks contains invalid data, together
+ * with an empty ID string ("").
+ *
+ * Section 5.2.15 of the ACPI specification requires the ID string to be
+ * a "fully qualified reference to the (...) embedded controller device",
+ * so this string always has to start with a backslash.
+ *
+ * However some ThinkBook machines have a ECDT table with a valid EC
+ * description but an invalid ID string ("_SB.PC00.LPCB.EC0").
+ *
+ * Because of this we only check if the ID string is empty in order to
+ * avoid the obvious cases.
+ */
+ pr_err(FW_BUG "Ignoring ECDT due to empty ID string\n");
+ goto out;
+ }
+
ec = acpi_ec_alloc();
if (!ec)
goto out;
@@ -1939,7 +2077,7 @@ void __init acpi_ec_ecdt_probe(void)
* At this point, the namespace is not initialized, so do not find
* the namespace objects, or handle the events.
*/
- ret = acpi_ec_setup(ec, NULL);
+ ret = acpi_ec_setup(ec, NULL, false);
if (ret) {
acpi_ec_free(ec);
goto out;
@@ -2055,7 +2193,7 @@ bool acpi_ec_dispatch_gpe(void)
if (acpi_ec_gpe_status_set(first_ec)) {
pm_pr_dbg("ACPI EC GPE status set\n");
- advance_transaction(first_ec, false);
+ clear_gpe_and_advance_transaction(first_ec, false);
work_in_progress = acpi_ec_work_in_progress(first_ec);
}
@@ -2156,7 +2294,8 @@ static int acpi_ec_init_workqueues(void)
ec_wq = alloc_ordered_workqueue("kec", 0);
if (!ec_query_wq)
- ec_query_wq = alloc_workqueue("kec_query", 0, ec_max_queries);
+ ec_query_wq = alloc_workqueue("kec_query", WQ_PERCPU,
+ ec_max_queries);
if (!ec_wq || !ec_query_wq) {
acpi_ec_destroy_workqueues();
@@ -2184,6 +2323,40 @@ static const struct dmi_system_id acpi_ec_no_wakeup[] = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "103C_5336AN HP ZHAN 66 Pro"),
},
},
+ /*
+ * Lenovo Legion Go S; touchscreen blocks HW sleep when woken up from EC
+ * https://gitlab.freedesktop.org/drm/amd/-/issues/3929
+ */
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83L3"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83N6"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q2"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"),
+ }
+ },
+ {
+ // TUXEDO InfinityBook Pro AMD Gen9
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GXxHRXx"),
+ },
+ },
{ },
};
diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c
index d199a19bb292..96a9aaaaf9f7 100644
--- a/drivers/acpi/event.c
+++ b/drivers/acpi/event.c
@@ -28,8 +28,8 @@ int acpi_notifier_call_chain(struct acpi_device *dev, u32 type, u32 data)
{
struct acpi_bus_event event;
- strcpy(event.device_class, dev->pnp.device_class);
- strcpy(event.bus_id, dev->pnp.bus_id);
+ strscpy(event.device_class, dev->pnp.device_class);
+ strscpy(event.bus_id, dev->pnp.bus_id);
event.type = type;
event.data = data;
return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event)
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c
index fe6b6792c8bb..5c35cbc7f6ff 100644
--- a/drivers/acpi/evged.c
+++ b/drivers/acpi/evged.c
@@ -173,10 +173,9 @@ static void ged_shutdown(struct platform_device *pdev)
}
}
-static int ged_remove(struct platform_device *pdev)
+static void ged_remove(struct platform_device *pdev)
{
ged_shutdown(pdev);
- return 0;
}
static const struct acpi_device_id ged_acpi_ids[] = {
diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h
index e7b4b4e4a55e..97ce3212edf3 100644
--- a/drivers/acpi/fan.h
+++ b/drivers/acpi/fan.h
@@ -10,12 +10,19 @@
#ifndef _ACPI_FAN_H_
#define _ACPI_FAN_H_
+#include <linux/kconfig.h>
+#include <linux/limits.h>
+
#define ACPI_FAN_DEVICE_IDS \
{"INT3404", }, /* Fan */ \
{"INTC1044", }, /* Fan for Tiger Lake generation */ \
{"INTC1048", }, /* Fan for Alder Lake generation */ \
{"INTC1063", }, /* Fan for Meteor Lake generation */ \
+ {"INTC106A", }, /* Fan for Lunar Lake generation */ \
{"INTC10A2", }, /* Fan for Raptor Lake generation */ \
+ {"INTC10D6", }, /* Fan for Panther Lake generation */ \
+ {"INTC10FE", }, /* Fan for Wildcat Lake generation */ \
+ {"INTC10F5", }, /* Fan for Nova Lake generation */ \
{"PNP0C0B", } /* Generic ACPI fan */
#define ACPI_FPS_NAME_LEN 20
@@ -44,16 +51,64 @@ struct acpi_fan_fst {
};
struct acpi_fan {
+ acpi_handle handle;
bool acpi4;
+ bool has_fst;
struct acpi_fan_fif fif;
struct acpi_fan_fps *fps;
int fps_count;
+ /* A value of 0 means that trippoint-related functions are not supported */
+ u32 fan_trip_granularity;
+#if IS_REACHABLE(CONFIG_HWMON)
+ struct device *hdev;
+#endif
struct thermal_cooling_device *cdev;
struct device_attribute fst_speed;
struct device_attribute fine_grain_control;
};
-int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst);
+/**
+ * acpi_fan_speed_valid - Check if fan speed value is valid
+ * @speeed: Speed value returned by the ACPI firmware
+ *
+ * Check if the fan speed value returned by the ACPI firmware is valid. This function is
+ * necessary as ACPI firmware implementations can return 0xFFFFFFFF to signal that the
+ * ACPI fan does not support speed reporting. Additionally, some buggy ACPI firmware
+ * implementations return a value larger than the 32-bit integer value defined by
+ * the ACPI specification when using placeholder values. Such invalid values are also
+ * detected by this function.
+ *
+ * Returns: True if the fan speed value is valid, false otherwise.
+ */
+static inline bool acpi_fan_speed_valid(u64 speed)
+{
+ return speed < U32_MAX;
+}
+
+/**
+ * acpi_fan_power_valid - Check if fan power value is valid
+ * @power: Power value returned by the ACPI firmware
+ *
+ * Check if the fan power value returned by the ACPI firmware is valid.
+ * See acpi_fan_speed_valid() for details.
+ *
+ * Returns: True if the fan power value is valid, false otherwise.
+ */
+static inline bool acpi_fan_power_valid(u64 power)
+{
+ return power < U32_MAX;
+}
+
+int acpi_fan_get_fst(acpi_handle handle, struct acpi_fan_fst *fst);
int acpi_fan_create_attributes(struct acpi_device *device);
void acpi_fan_delete_attributes(struct acpi_device *device);
+
+#if IS_REACHABLE(CONFIG_HWMON)
+int devm_acpi_fan_create_hwmon(struct device *dev);
+void acpi_fan_notify_hwmon(struct device *dev);
+#else
+static inline int devm_acpi_fan_create_hwmon(struct device *dev) { return 0; };
+static inline void acpi_fan_notify_hwmon(struct device *dev) { };
+#endif
+
#endif
diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c
index f15157d40713..9b7fa52f3c2a 100644
--- a/drivers/acpi/fan_attr.c
+++ b/drivers/acpi/fan_attr.c
@@ -22,29 +22,29 @@ static ssize_t show_state(struct device *dev, struct device_attribute *attr, cha
int count;
if (fps->control == 0xFFFFFFFF || fps->control > 100)
- count = scnprintf(buf, PAGE_SIZE, "not-defined:");
+ count = sysfs_emit(buf, "not-defined:");
else
- count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control);
+ count = sysfs_emit(buf, "%lld:", fps->control);
if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
- count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
+ count += sysfs_emit_at(buf, count, "not-defined:");
else
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point);
+ count += sysfs_emit_at(buf, count, "%lld:", fps->trip_point);
if (fps->speed == 0xFFFFFFFF)
- count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
+ count += sysfs_emit_at(buf, count, "not-defined:");
else
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed);
+ count += sysfs_emit_at(buf, count, "%lld:", fps->speed);
if (fps->noise_level == 0xFFFFFFFF)
- count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
+ count += sysfs_emit_at(buf, count, "not-defined:");
else
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100);
+ count += sysfs_emit_at(buf, count, "%lld:", fps->noise_level * 100);
if (fps->power == 0xFFFFFFFF)
- count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n");
+ count += sysfs_emit_at(buf, count, "not-defined\n");
else
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power);
+ count += sysfs_emit_at(buf, count, "%lld\n", fps->power);
return count;
}
@@ -55,11 +55,11 @@ static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr,
struct acpi_fan_fst fst;
int status;
- status = acpi_fan_get_fst(acpi_dev, &fst);
+ status = acpi_fan_get_fst(acpi_dev->handle, &fst);
if (status)
return status;
- return sprintf(buf, "%lld\n", fst.speed);
+ return sysfs_emit(buf, "%lld\n", fst.speed);
}
static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf)
@@ -67,7 +67,7 @@ static ssize_t show_fine_grain_control(struct device *dev, struct device_attribu
struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
struct acpi_fan *fan = acpi_driver_data(acpi_dev);
- return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl);
+ return sysfs_emit(buf, "%d\n", fan->fif.fine_grain_ctrl);
}
int acpi_fan_create_attributes(struct acpi_device *device)
@@ -75,15 +75,6 @@ int acpi_fan_create_attributes(struct acpi_device *device)
struct acpi_fan *fan = acpi_driver_data(device);
int i, status;
- sysfs_attr_init(&fan->fine_grain_control.attr);
- fan->fine_grain_control.show = show_fine_grain_control;
- fan->fine_grain_control.store = NULL;
- fan->fine_grain_control.attr.name = "fine_grain_control";
- fan->fine_grain_control.attr.mode = 0444;
- status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr);
- if (status)
- return status;
-
/* _FST is present if we are here */
sysfs_attr_init(&fan->fst_speed.attr);
fan->fst_speed.show = show_fan_speed;
@@ -92,7 +83,19 @@ int acpi_fan_create_attributes(struct acpi_device *device)
fan->fst_speed.attr.mode = 0444;
status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr);
if (status)
- goto rem_fine_grain_attr;
+ return status;
+
+ if (!fan->acpi4)
+ return 0;
+
+ sysfs_attr_init(&fan->fine_grain_control.attr);
+ fan->fine_grain_control.show = show_fine_grain_control;
+ fan->fine_grain_control.store = NULL;
+ fan->fine_grain_control.attr.name = "fine_grain_control";
+ fan->fine_grain_control.attr.mode = 0444;
+ status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr);
+ if (status)
+ goto rem_fst_attr;
for (i = 0; i < fan->fps_count; ++i) {
struct acpi_fan_fps *fps = &fan->fps[i];
@@ -109,18 +112,18 @@ int acpi_fan_create_attributes(struct acpi_device *device)
for (j = 0; j < i; ++j)
sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
- goto rem_fst_attr;
+ goto rem_fine_grain_attr;
}
}
return 0;
-rem_fst_attr:
- sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
-
rem_fine_grain_attr:
sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
+rem_fst_attr:
+ sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
+
return status;
}
@@ -129,9 +132,13 @@ void acpi_fan_delete_attributes(struct acpi_device *device)
struct acpi_fan *fan = acpi_driver_data(device);
int i;
+ sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
+
+ if (!fan->acpi4)
+ return;
+
for (i = 0; i < fan->fps_count; ++i)
sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
- sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
}
diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c
index b9a9a59ddcc1..fb08b8549ed7 100644
--- a/drivers/acpi/fan_core.c
+++ b/drivers/acpi/fan_core.c
@@ -7,11 +7,16 @@
* Copyright (C) 2022 Intel Corporation. All rights reserved.
*/
+#include <linux/bits.h>
#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/math.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <linux/uuid.h>
#include <linux/thermal.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
@@ -19,12 +24,25 @@
#include "fan.h"
-MODULE_AUTHOR("Paul Diefenbaugh");
-MODULE_DESCRIPTION("ACPI Fan Driver");
-MODULE_LICENSE("GPL");
+#define ACPI_FAN_NOTIFY_STATE_CHANGED 0x80
-static int acpi_fan_probe(struct platform_device *pdev);
-static int acpi_fan_remove(struct platform_device *pdev);
+/*
+ * Defined inside the "Fan Noise Signal" section at
+ * https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide.
+ */
+static const guid_t acpi_fan_microsoft_guid = GUID_INIT(0xA7611840, 0x99FE, 0x41AE, 0xA4, 0x88,
+ 0x35, 0xC7, 0x59, 0x26, 0xC8, 0xEB);
+#define ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY 1
+#define ACPI_FAN_DSM_SET_TRIP_POINTS 2
+#define ACPI_FAN_DSM_GET_OPERATING_RANGES 3
+
+/*
+ * Ensures that fans with a very low trip point granularity
+ * do not send too many notifications.
+ */
+static uint min_trip_distance = 100;
+module_param(min_trip_distance, uint, 0);
+MODULE_PARM_DESC(min_trip_distance, "Minimum distance between fan speed trip points in RPM");
static const struct acpi_device_id fan_device_ids[] = {
ACPI_FAN_DEVICE_IDS,
@@ -32,30 +50,6 @@ static const struct acpi_device_id fan_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, fan_device_ids);
-#ifdef CONFIG_PM_SLEEP
-static int acpi_fan_suspend(struct device *dev);
-static int acpi_fan_resume(struct device *dev);
-static const struct dev_pm_ops acpi_fan_pm = {
- .resume = acpi_fan_resume,
- .freeze = acpi_fan_suspend,
- .thaw = acpi_fan_resume,
- .restore = acpi_fan_resume,
-};
-#define FAN_PM_OPS_PTR (&acpi_fan_pm)
-#else
-#define FAN_PM_OPS_PTR NULL
-#endif
-
-static struct platform_driver acpi_fan_driver = {
- .probe = acpi_fan_probe,
- .remove = acpi_fan_remove,
- .driver = {
- .name = "acpi-fan",
- .acpi_match_table = fan_device_ids,
- .pm = FAN_PM_OPS_PTR,
- },
-};
-
/* thermal cooling device callbacks */
static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
*state)
@@ -75,25 +69,30 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
return 0;
}
-int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
+int acpi_fan_get_fst(acpi_handle handle, struct acpi_fan_fst *fst)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int ret = 0;
- status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
- if (ACPI_FAILURE(status)) {
- dev_err(&device->dev, "Get fan state failed\n");
- return -ENODEV;
- }
+ status = acpi_evaluate_object(handle, "_FST", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -EIO;
obj = buffer.pointer;
- if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
- obj->package.count != 3 ||
- obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
- dev_err(&device->dev, "Invalid _FST data\n");
- ret = -EINVAL;
+ if (!obj)
+ return -ENODATA;
+
+ if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
+ ret = -EPROTO;
+ goto err;
+ }
+
+ if (obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ obj->package.elements[1].type != ACPI_TYPE_INTEGER ||
+ obj->package.elements[2].type != ACPI_TYPE_INTEGER) {
+ ret = -EPROTO;
goto err;
}
@@ -112,7 +111,7 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
struct acpi_fan_fst fst;
int status, i;
- status = acpi_fan_get_fst(device, &fst);
+ status = acpi_fan_get_fst(device->handle, &fst);
if (status)
return status;
@@ -133,7 +132,7 @@ match_fps:
break;
}
if (i == fan->fps_count) {
- dev_dbg(&device->dev, "Invalid control value returned\n");
+ dev_dbg(&device->dev, "No matching fps control value\n");
return -EINVAL;
}
@@ -234,14 +233,6 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = {
* --------------------------------------------------------------------------
*/
-static bool acpi_fan_is_acpi4(struct acpi_device *device)
-{
- return acpi_has_method(device->handle, "_FIF") &&
- acpi_has_method(device->handle, "_FPS") &&
- acpi_has_method(device->handle, "_FSL") &&
- acpi_has_method(device->handle, "_FST");
-}
-
static int acpi_fan_get_fif(struct acpi_device *device)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -267,6 +258,7 @@ static int acpi_fan_get_fif(struct acpi_device *device)
if (ACPI_FAILURE(status)) {
dev_err(&device->dev, "Invalid _FIF element\n");
status = -EINVAL;
+ goto err;
}
fan->fif.revision = fields[0];
@@ -341,6 +333,182 @@ err:
return status;
}
+static int acpi_fan_dsm_init(struct device *dev)
+{
+ union acpi_object dummy = {
+ .package = {
+ .type = ACPI_TYPE_PACKAGE,
+ .count = 0,
+ .elements = NULL,
+ },
+ };
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ union acpi_object *obj;
+ int ret = 0;
+
+ if (!acpi_check_dsm(fan->handle, &acpi_fan_microsoft_guid, 0,
+ BIT(ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY) |
+ BIT(ACPI_FAN_DSM_SET_TRIP_POINTS)))
+ return 0;
+
+ dev_info(dev, "Using Microsoft fan extensions\n");
+
+ obj = acpi_evaluate_dsm_typed(fan->handle, &acpi_fan_microsoft_guid, 0,
+ ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY, &dummy,
+ ACPI_TYPE_INTEGER);
+ if (!obj)
+ return -EIO;
+
+ if (obj->integer.value > U32_MAX)
+ ret = -EOVERFLOW;
+ else
+ fan->fan_trip_granularity = obj->integer.value;
+
+ kfree(obj);
+
+ return ret;
+}
+
+static int acpi_fan_dsm_set_trip_points(struct device *dev, u64 upper, u64 lower)
+{
+ union acpi_object args[2] = {
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = lower,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = upper,
+ },
+ },
+ };
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ union acpi_object in = {
+ .package = {
+ .type = ACPI_TYPE_PACKAGE,
+ .count = ARRAY_SIZE(args),
+ .elements = args,
+ },
+ };
+ union acpi_object *obj;
+
+ obj = acpi_evaluate_dsm(fan->handle, &acpi_fan_microsoft_guid, 0,
+ ACPI_FAN_DSM_SET_TRIP_POINTS, &in);
+ kfree(obj);
+
+ return 0;
+}
+
+static int acpi_fan_dsm_start(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ int ret;
+
+ if (!fan->fan_trip_granularity)
+ return 0;
+
+ /*
+ * Some firmware implementations only update the values returned by the
+ * _FST control method when a notification is received. This usually
+ * works with Microsoft Windows as setting up trip points will keep
+ * triggering said notifications, but will cause issues when using _FST
+ * without the Microsoft-specific trip point extension.
+ *
+ * Because of this, an initial notification needs to be triggered to
+ * start the cycle of trip points updates. This is achieved by setting
+ * the trip points sequencially to two separate ranges. As by the
+ * Microsoft specification the firmware should trigger a notification
+ * immediately if the fan speed is outside the trip point range. This
+ * _should_ result in at least one notification as both ranges do not
+ * overlap, meaning that the current fan speed needs to be outside at
+ * least one range.
+ */
+ ret = acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity, 0);
+ if (ret < 0)
+ return ret;
+
+ return acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity * 3,
+ fan->fan_trip_granularity * 2);
+}
+
+static int acpi_fan_dsm_update_trips_points(struct device *dev, struct acpi_fan_fst *fst)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ u64 upper, lower;
+
+ if (!fan->fan_trip_granularity)
+ return 0;
+
+ if (!acpi_fan_speed_valid(fst->speed))
+ return -EINVAL;
+
+ upper = roundup_u64(fst->speed + min_trip_distance, fan->fan_trip_granularity);
+ if (fst->speed <= min_trip_distance) {
+ lower = 0;
+ } else {
+ /*
+ * Valid fan speed values cannot be larger than 32 bit, so
+ * we can safely assume that no overflow will happen here.
+ */
+ lower = rounddown((u32)fst->speed - min_trip_distance, fan->fan_trip_granularity);
+ }
+
+ return acpi_fan_dsm_set_trip_points(dev, upper, lower);
+}
+
+static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context)
+{
+ struct device *dev = context;
+ struct acpi_fan_fst fst;
+ int ret;
+
+ switch (event) {
+ case ACPI_FAN_NOTIFY_STATE_CHANGED:
+ /*
+ * The ACPI specification says that we must evaluate _FST when we
+ * receive an ACPI event indicating that the fan state has changed.
+ */
+ ret = acpi_fan_get_fst(handle, &fst);
+ if (ret < 0) {
+ dev_err(dev, "Error retrieving current fan status: %d\n", ret);
+ } else {
+ ret = acpi_fan_dsm_update_trips_points(dev, &fst);
+ if (ret < 0)
+ dev_err(dev, "Failed to update trip points: %d\n", ret);
+ }
+
+ acpi_fan_notify_hwmon(dev);
+ acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0);
+ break;
+ default:
+ dev_dbg(dev, "Unsupported ACPI notification 0x%x\n", event);
+ break;
+ }
+}
+
+static void acpi_fan_notify_remove(void *data)
+{
+ struct acpi_fan *fan = data;
+
+ acpi_remove_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, acpi_fan_notify_handler);
+}
+
+static int devm_acpi_fan_notify_init(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ acpi_status status;
+
+ status = acpi_install_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY,
+ acpi_fan_notify_handler, dev);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return devm_add_action_or_reset(dev, acpi_fan_notify_remove, fan);
+}
+
static int acpi_fan_probe(struct platform_device *pdev)
{
int result = 0;
@@ -349,15 +517,27 @@ static int acpi_fan_probe(struct platform_device *pdev)
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
char *name;
+ if (!device)
+ return -ENODEV;
+
fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
if (!fan) {
dev_err(&device->dev, "No memory for fan\n");
return -ENOMEM;
}
+
+ fan->handle = device->handle;
device->driver_data = fan;
platform_set_drvdata(pdev, fan);
- if (acpi_fan_is_acpi4(device)) {
+ if (acpi_has_method(device->handle, "_FST")) {
+ fan->has_fst = true;
+ fan->acpi4 = acpi_has_method(device->handle, "_FIF") &&
+ acpi_has_method(device->handle, "_FPS") &&
+ acpi_has_method(device->handle, "_FSL");
+ }
+
+ if (fan->acpi4) {
result = acpi_fan_get_fif(device);
if (result)
return result;
@@ -365,13 +545,33 @@ static int acpi_fan_probe(struct platform_device *pdev)
result = acpi_fan_get_fps(device);
if (result)
return result;
+ }
+
+ if (fan->has_fst) {
+ result = acpi_fan_dsm_init(&pdev->dev);
+ if (result)
+ return result;
+
+ result = devm_acpi_fan_create_hwmon(&pdev->dev);
+ if (result)
+ return result;
+
+ result = devm_acpi_fan_notify_init(&pdev->dev);
+ if (result)
+ return result;
+
+ result = acpi_fan_dsm_start(&pdev->dev);
+ if (result) {
+ dev_err(&pdev->dev, "Failed to start Microsoft fan extensions\n");
+ return result;
+ }
result = acpi_fan_create_attributes(device);
if (result)
return result;
+ }
- fan->acpi4 = true;
- } else {
+ if (!fan->acpi4) {
result = acpi_device_update_power(device, NULL);
if (result) {
dev_err(&device->dev, "Failed to set initial power state\n");
@@ -397,31 +597,37 @@ static int acpi_fan_probe(struct platform_device *pdev)
result = sysfs_create_link(&pdev->dev.kobj,
&cdev->device.kobj,
"thermal_cooling");
- if (result)
+ if (result) {
dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n");
+ goto err_unregister;
+ }
result = sysfs_create_link(&cdev->device.kobj,
&pdev->dev.kobj,
"device");
if (result) {
dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n");
- goto err_end;
+ goto err_remove_link;
}
return 0;
+err_remove_link:
+ sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
+err_unregister:
+ thermal_cooling_device_unregister(cdev);
err_end:
- if (fan->acpi4)
+ if (fan->has_fst)
acpi_fan_delete_attributes(device);
return result;
}
-static int acpi_fan_remove(struct platform_device *pdev)
+static void acpi_fan_remove(struct platform_device *pdev)
{
struct acpi_fan *fan = platform_get_drvdata(pdev);
- if (fan->acpi4) {
+ if (fan->has_fst) {
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
acpi_fan_delete_attributes(device);
@@ -429,8 +635,6 @@ static int acpi_fan_remove(struct platform_device *pdev)
sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
sysfs_remove_link(&fan->cdev->device.kobj, "device");
thermal_cooling_device_unregister(fan->cdev);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -447,8 +651,14 @@ static int acpi_fan_suspend(struct device *dev)
static int acpi_fan_resume(struct device *dev)
{
- int result;
struct acpi_fan *fan = dev_get_drvdata(dev);
+ int result;
+
+ if (fan->has_fst) {
+ result = acpi_fan_dsm_start(dev);
+ if (result)
+ dev_err(dev, "Failed to start Microsoft fan extensions: %d\n", result);
+ }
if (fan->acpi4)
return 0;
@@ -459,6 +669,33 @@ static int acpi_fan_resume(struct device *dev)
return result;
}
+
+static const struct dev_pm_ops acpi_fan_pm = {
+ .resume = acpi_fan_resume,
+ .freeze = acpi_fan_suspend,
+ .thaw = acpi_fan_resume,
+ .restore = acpi_fan_resume,
+};
+#define FAN_PM_OPS_PTR (&acpi_fan_pm)
+
+#else
+
+#define FAN_PM_OPS_PTR NULL
+
#endif
+static struct platform_driver acpi_fan_driver = {
+ .probe = acpi_fan_probe,
+ .remove = acpi_fan_remove,
+ .driver = {
+ .name = "acpi-fan",
+ .acpi_match_table = fan_device_ids,
+ .pm = FAN_PM_OPS_PTR,
+ },
+};
+
module_platform_driver(acpi_fan_driver);
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION("ACPI Fan Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/fan_hwmon.c b/drivers/acpi/fan_hwmon.c
new file mode 100644
index 000000000000..d3374f8f524b
--- /dev/null
+++ b/drivers/acpi/fan_hwmon.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * hwmon interface for the ACPI Fan driver.
+ *
+ * Copyright (C) 2024 Armin Wolf <W_Armin@gmx.de>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include "fan.h"
+
+static struct acpi_fan_fps *acpi_fan_get_current_fps(struct acpi_fan *fan, u64 control)
+{
+ unsigned int i;
+
+ for (i = 0; i < fan->fps_count; i++) {
+ if (fan->fps[i].control == control)
+ return &fan->fps[i];
+ }
+
+ return NULL;
+}
+
+static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct acpi_fan *fan = drvdata;
+ unsigned int i;
+
+ switch (type) {
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ return 0444;
+ case hwmon_fan_target:
+ /* Only acpi4 fans support fan control. */
+ if (!fan->acpi4)
+ return 0;
+
+ /*
+ * When in fine grain control mode, not every fan control value
+ * has an associated fan performance state.
+ */
+ if (fan->fif.fine_grain_ctrl)
+ return 0;
+
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ /* Only acpi4 fans support fan control. */
+ if (!fan->acpi4)
+ return 0;
+
+ /*
+ * When in fine grain control mode, not every fan control value
+ * has an associated fan performance state.
+ */
+ if (fan->fif.fine_grain_ctrl)
+ return 0;
+
+ /*
+ * When all fan performance states contain no valid power data,
+ * when the associated attribute should not be created.
+ */
+ for (i = 0; i < fan->fps_count; i++) {
+ if (acpi_fan_power_valid(fan->fps[i].power))
+ return 0444;
+ }
+
+ return 0;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long *val)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ struct acpi_fan_fps *fps;
+ struct acpi_fan_fst fst;
+ int ret;
+
+ ret = acpi_fan_get_fst(fan->handle, &fst);
+ if (ret < 0)
+ return ret;
+
+ switch (type) {
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ if (!acpi_fan_speed_valid(fst.speed))
+ return -ENODEV;
+
+ if (fst.speed > LONG_MAX)
+ return -EOVERFLOW;
+
+ *val = fst.speed;
+ return 0;
+ case hwmon_fan_target:
+ fps = acpi_fan_get_current_fps(fan, fst.control);
+ if (!fps)
+ return -EIO;
+
+ if (fps->speed > LONG_MAX)
+ return -EOVERFLOW;
+
+ *val = fps->speed;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ fps = acpi_fan_get_current_fps(fan, fst.control);
+ if (!fps)
+ return -EIO;
+
+ if (!acpi_fan_power_valid(fps->power))
+ return -ENODEV;
+
+ if (fps->power > LONG_MAX / MICROWATT_PER_MILLIWATT)
+ return -EOVERFLOW;
+
+ *val = fps->power * MICROWATT_PER_MILLIWATT;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops acpi_fan_hwmon_ops = {
+ .is_visible = acpi_fan_hwmon_is_visible,
+ .read = acpi_fan_hwmon_read,
+};
+
+static const struct hwmon_channel_info * const acpi_fan_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET),
+ HWMON_CHANNEL_INFO(power, HWMON_P_INPUT),
+ NULL
+};
+
+static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = {
+ .ops = &acpi_fan_hwmon_ops,
+ .info = acpi_fan_hwmon_info,
+};
+
+void acpi_fan_notify_hwmon(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+
+ hwmon_notify_event(fan->hdev, hwmon_fan, hwmon_fan_input, 0);
+}
+
+int devm_acpi_fan_create_hwmon(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+
+ fan->hdev = devm_hwmon_device_register_with_info(dev, "acpi_fan", fan,
+ &acpi_fan_hwmon_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(fan->hdev);
+}
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 204fe94c7e45..a194f30876c5 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -75,7 +75,8 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
}
#define FIND_CHILD_MIN_SCORE 1
-#define FIND_CHILD_MAX_SCORE 2
+#define FIND_CHILD_MID_SCORE 2
+#define FIND_CHILD_MAX_SCORE 3
static int match_any(struct acpi_device *adev, void *not_used)
{
@@ -96,8 +97,17 @@ static int find_child_checks(struct acpi_device *adev, bool check_children)
return -ENODEV;
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
- if (status == AE_NOT_FOUND)
+ if (status == AE_NOT_FOUND) {
+ /*
+ * Special case: backlight device objects without _STA are
+ * preferred to other objects with the same _ADR value, because
+ * it is more likely that they are actually useful.
+ */
+ if (adev->pnp.type.backlight)
+ return FIND_CHILD_MID_SCORE;
+
return FIND_CHILD_MIN_SCORE;
+ }
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
return -ENODEV;
diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c
index 60a2939cde6c..3499f86c411e 100644
--- a/drivers/acpi/hed.c
+++ b/drivers/acpi/hed.c
@@ -42,24 +42,33 @@ EXPORT_SYMBOL_GPL(unregister_acpi_hed_notifier);
* it is used by HEST Generic Hardware Error Source with notify type
* SCI.
*/
-static void acpi_hed_notify(struct acpi_device *device, u32 event)
+static void acpi_hed_notify(acpi_handle handle, u32 event, void *data)
{
blocking_notifier_call_chain(&acpi_hed_notify_list, 0, NULL);
}
static int acpi_hed_add(struct acpi_device *device)
{
+ int err;
+
/* Only one hardware error device */
if (hed_handle)
return -EINVAL;
hed_handle = device->handle;
- return 0;
+
+ err = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_hed_notify, device);
+ if (err)
+ hed_handle = NULL;
+
+ return err;
}
-static int acpi_hed_remove(struct acpi_device *device)
+static void acpi_hed_remove(struct acpi_device *device)
{
+ acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_hed_notify);
hed_handle = NULL;
- return 0;
}
static struct acpi_driver acpi_hed_driver = {
@@ -69,10 +78,14 @@ static struct acpi_driver acpi_hed_driver = {
.ops = {
.add = acpi_hed_add,
.remove = acpi_hed_remove,
- .notify = acpi_hed_notify,
},
};
-module_acpi_driver(acpi_hed_driver);
+
+static int __init acpi_hed_driver_init(void)
+{
+ return acpi_bus_register_driver(&acpi_hed_driver);
+}
+subsys_initcall(acpi_hed_driver_init);
MODULE_AUTHOR("Huang Ying");
MODULE_DESCRIPTION("ACPI Hardware Error Device Driver");
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 628bf8f18130..40f875b265a9 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -11,6 +11,8 @@
#include <linux/idr.h>
+extern struct acpi_device *acpi_root;
+
int early_acpi_osi_init(void);
int acpi_osi_init(void);
acpi_status acpi_os_initialize1(void);
@@ -25,12 +27,6 @@ static inline void acpi_pci_link_init(void) {}
void acpi_processor_init(void);
void acpi_platform_init(void);
void acpi_pnp_init(void);
-void acpi_int340x_thermal_init(void);
-#ifdef CONFIG_ARM_AMBA
-void acpi_amba_init(void);
-#else
-static inline void acpi_amba_init(void) {}
-#endif
int acpi_sysfs_init(void);
void acpi_gpe_apply_masked_gpes(void);
void acpi_container_init(void);
@@ -72,7 +68,8 @@ void acpi_debugfs_init(void);
#else
static inline void acpi_debugfs_init(void) { return; }
#endif
-#ifdef CONFIG_PCI
+
+#if defined(CONFIG_X86) && defined(CONFIG_PCI)
void acpi_lpss_init(void);
#else
static inline void acpi_lpss_init(void) {}
@@ -88,6 +85,20 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context);
void acpi_scan_table_notify(void);
+int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp);
+int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp);
+int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp);
+int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp);
+
+#ifdef CONFIG_ARM64
+int acpi_arch_thermal_cpufreq_pctg(void);
+#else
+static inline int acpi_arch_thermal_cpufreq_pctg(void)
+{
+ return 0;
+}
+#endif
+
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
@@ -102,14 +113,16 @@ struct acpi_device_bus_id {
struct list_head node;
};
-int acpi_device_add(struct acpi_device *device,
- void (*release)(struct device *));
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
- int type);
-int acpi_device_setup_files(struct acpi_device *dev);
+ int type, void (*release)(struct device *));
+int acpi_tie_acpi_dev(struct acpi_device *adev);
+int acpi_device_add(struct acpi_device *device);
+void acpi_device_setup_files(struct acpi_device *dev);
void acpi_device_remove_files(struct acpi_device *dev);
+extern const struct attribute_group *acpi_groups[];
void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
+bool acpi_device_is_enabled(const struct acpi_device *adev);
bool acpi_device_is_present(const struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
@@ -119,14 +132,14 @@ int acpi_bus_register_early_device(int type);
/* --------------------------------------------------------------------------
Device Matching and Notification
-------------------------------------------------------------------------- */
-struct acpi_device *acpi_companion_match(const struct device *dev);
-int __acpi_device_uevent_modalias(struct acpi_device *adev,
+const struct acpi_device *acpi_companion_match(const struct device *dev);
+int __acpi_device_uevent_modalias(const struct acpi_device *adev,
struct kobj_uevent_env *env);
/* --------------------------------------------------------------------------
Power Resource
-------------------------------------------------------------------------- */
-int acpi_power_init(void);
+void acpi_power_resources_init(void);
void acpi_power_resources_list_free(struct list_head *list);
int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
struct list_head *list);
@@ -150,15 +163,22 @@ int acpi_wakeup_device_init(void);
Processor
-------------------------------------------------------------------------- */
#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
+void acpi_early_processor_control_setup(void);
void acpi_early_processor_set_pdc(void);
+#ifdef CONFIG_X86
+void acpi_proc_quirk_mwait_check(void);
+#else
+static inline void acpi_proc_quirk_mwait_check(void) {}
+#endif
+bool processor_physically_present(acpi_handle handle);
#else
-static inline void acpi_early_processor_set_pdc(void) {}
+static inline void acpi_early_processor_control_setup(void) {}
#endif
-#ifdef CONFIG_X86
-void acpi_early_processor_osc(void);
+#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
+void acpi_idle_rescan_dead_smt_siblings(void);
#else
-static inline void acpi_early_processor_osc(void) {}
+static inline void acpi_idle_rescan_dead_smt_siblings(void) {}
#endif
/* --------------------------------------------------------------------------
@@ -201,6 +221,8 @@ extern struct acpi_ec *first_ec;
/* External interfaces use first EC only, so remember */
typedef int (*acpi_ec_query_func) (void *data);
+#ifdef CONFIG_ACPI_EC
+
void acpi_ec_init(void);
void acpi_ec_ecdt_probe(void);
void acpi_ec_dsdt_probe(void);
@@ -210,12 +232,36 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func,
void *data);
void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
+void acpi_ec_register_opregions(struct acpi_device *adev);
#ifdef CONFIG_PM_SLEEP
void acpi_ec_flush_work(void);
bool acpi_ec_dispatch_gpe(void);
#endif
+#else
+
+static inline void acpi_ec_init(void) {}
+static inline void acpi_ec_ecdt_probe(void) {}
+static inline void acpi_ec_dsdt_probe(void) {}
+static inline void acpi_ec_block_transactions(void) {}
+static inline void acpi_ec_unblock_transactions(void) {}
+static inline int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
+ acpi_handle handle, acpi_ec_query_func func,
+ void *data)
+{
+ return -ENXIO;
+}
+static inline void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) {}
+static inline void acpi_ec_register_opregions(struct acpi_device *adev) {}
+
+static inline void acpi_ec_flush_work(void) {}
+static inline bool acpi_ec_dispatch_gpe(void)
+{
+ return false;
+}
+
+#endif
/*--------------------------------------------------------------------------
Suspend/Resume
@@ -281,4 +327,18 @@ void acpi_init_lpit(void);
static inline void acpi_init_lpit(void) { }
#endif
+/*--------------------------------------------------------------------------
+ ACPI _CRS CSI-2 and MIPI DisCo for Imaging
+ -------------------------------------------------------------------------- */
+
+void acpi_mipi_check_crs_csi2(acpi_handle handle);
+void acpi_mipi_scan_crs_csi2(void);
+void acpi_mipi_init_crs_csi2_swnodes(void);
+void acpi_mipi_crs_csi2_cleanup(void);
+#ifdef CONFIG_X86
+bool acpi_graph_ignore_port(acpi_handle handle);
+#else
+static inline bool acpi_graph_ignore_port(acpi_handle handle) { return false; }
+#endif
+
#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c
index a690c7b18623..6677955b4a8e 100644
--- a/drivers/acpi/ioapic.c
+++ b/drivers/acpi/ioapic.c
@@ -24,6 +24,7 @@
#include <linux/acpi.h>
#include <linux/pci.h>
#include <acpi/acpi.h>
+#include "internal.h"
struct acpi_pci_ioapic {
acpi_handle root_handle;
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
index dabe45eba055..d1595156c86a 100644
--- a/drivers/acpi/irq.c
+++ b/drivers/acpi/irq.c
@@ -12,7 +12,7 @@
enum acpi_irq_model_id acpi_irq_model;
-static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);
+static acpi_gsi_domain_disp_fn acpi_get_gsi_domain_id;
static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);
/**
@@ -57,6 +57,7 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
int polarity)
{
struct irq_fwspec fwspec;
+ unsigned int irq;
fwspec.fwnode = acpi_get_gsi_domain_id(gsi);
if (WARN_ON(!fwspec.fwnode)) {
@@ -68,7 +69,11 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
fwspec.param_count = 2;
- return irq_create_fwspec_mapping(&fwspec);
+ irq = irq_create_fwspec_mapping(&fwspec);
+ if (!irq)
+ return -EINVAL;
+
+ return irq;
}
EXPORT_SYMBOL_GPL(acpi_register_gsi);
@@ -94,6 +99,7 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
/**
* acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source.
* @source: acpi_resource_source to use for the lookup.
+ * @gsi: GSI IRQ number
*
* Description:
* Retrieve the fwhandle of the device referenced by the given IRQ resource
@@ -118,12 +124,12 @@ acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,
if (WARN_ON(ACPI_FAILURE(status)))
return NULL;
- device = acpi_bus_get_acpi_device(handle);
+ device = acpi_get_acpi_dev(handle);
if (WARN_ON(!device))
return NULL;
result = &device->fwnode;
- acpi_bus_put_acpi_device(device);
+ acpi_put_acpi_dev(device);
return result;
}
@@ -147,6 +153,7 @@ struct acpi_irq_parse_one_ctx {
* @polarity: polarity attributes of hwirq
* @polarity: polarity attributes of hwirq
* @shareable: shareable attributes of hwirq
+ * @wake_capable: wake capable attribute of hwirq
* @ctx: acpi_irq_parse_one_ctx updated by this function
*
* Description:
@@ -156,12 +163,13 @@ struct acpi_irq_parse_one_ctx {
static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode,
u32 hwirq, u8 triggering,
u8 polarity, u8 shareable,
+ u8 wake_capable,
struct acpi_irq_parse_one_ctx *ctx)
{
if (!fwnode)
return;
ctx->rc = 0;
- *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable);
+ *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable);
ctx->fwspec->fwnode = fwnode;
ctx->fwspec->param[0] = hwirq;
ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);
@@ -204,7 +212,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);
acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
irq->triggering, irq->polarity,
- irq->shareable, ctx);
+ irq->shareable, irq->wake_capable, ctx);
return AE_CTRL_TERMINATE;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
eirq = &ares->data.extended_irq;
@@ -218,7 +226,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
eirq->interrupts[ctx->index]);
acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
eirq->triggering, eirq->polarity,
- eirq->shareable, ctx);
+ eirq->shareable, eirq->wake_capable, ctx);
return AE_CTRL_TERMINATE;
}
@@ -292,19 +300,50 @@ int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
}
EXPORT_SYMBOL_GPL(acpi_irq_get);
+const struct cpumask *acpi_irq_get_affinity(acpi_handle handle,
+ unsigned int index)
+{
+ struct irq_fwspec_info info;
+ struct irq_fwspec fwspec;
+ unsigned long flags;
+
+ if (acpi_irq_parse_one(handle, index, &fwspec, &flags))
+ return NULL;
+
+ if (irq_populate_fwspec_info(&fwspec, &info))
+ return NULL;
+
+ if (!(info.flags & IRQ_FWSPEC_INFO_AFFINITY_VALID))
+ return NULL;
+
+ return info.affinity;
+}
+
/**
* acpi_set_irq_model - Setup the GSI irqdomain information
* @model: the value assigned to acpi_irq_model
- * @fwnode: the irq_domain identifier for mapping and looking up
- * GSI interrupts
+ * @fn: a dispatcher function that will return the domain fwnode
+ * for a given GSI
*/
void __init acpi_set_irq_model(enum acpi_irq_model_id model,
- struct fwnode_handle *(*fn)(u32))
+ acpi_gsi_domain_disp_fn fn)
{
acpi_irq_model = model;
acpi_get_gsi_domain_id = fn;
}
+/*
+ * acpi_get_gsi_dispatcher() - Get the GSI dispatcher function
+ *
+ * Return the dispatcher function that computes the domain fwnode for
+ * a given GSI.
+ */
+acpi_gsi_domain_disp_fn acpi_get_gsi_dispatcher(void)
+{
+ return acpi_get_gsi_domain_id;
+}
+EXPORT_SYMBOL_GPL(acpi_get_gsi_dispatcher);
+
/**
* acpi_set_gsi_to_irq_fallback - Register a GSI transfer
* callback to fallback to arch specified implementation.
diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c
new file mode 100644
index 000000000000..5b85989f96be
--- /dev/null
+++ b/drivers/acpi/mipi-disco-img.c
@@ -0,0 +1,805 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MIPI DisCo for Imaging support.
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in
+ * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource
+ * Descriptor" of ACPI 6.5 and using device properties defined by the MIPI DisCo
+ * for Imaging specification.
+ *
+ * The implementation looks for the information in the ACPI namespace (CSI-2
+ * resource descriptors in _CRS) and constructs software nodes compatible with
+ * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2
+ * connection graph. The software nodes are then populated with the data
+ * extracted from the _CRS CSI-2 resource descriptors and the MIPI DisCo
+ * for Imaging device properties present in _DSD for the ACPI device objects
+ * with CSI-2 connections.
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <media/v4l2-fwnode.h>
+
+#include "internal.h"
+
+static LIST_HEAD(acpi_mipi_crs_csi2_list);
+
+static void acpi_mipi_data_tag(acpi_handle handle, void *context)
+{
+}
+
+/* Connection data extracted from one _CRS CSI-2 resource descriptor. */
+struct crs_csi2_connection {
+ struct list_head entry;
+ struct acpi_resource_csi2_serialbus csi2_data;
+ acpi_handle remote_handle;
+ char remote_name[];
+};
+
+/* Data extracted from _CRS CSI-2 resource descriptors for one device. */
+struct crs_csi2 {
+ struct list_head entry;
+ acpi_handle handle;
+ struct acpi_device_software_nodes *swnodes;
+ struct list_head connections;
+ u32 port_count;
+};
+
+struct csi2_resources_walk_data {
+ acpi_handle handle;
+ struct list_head connections;
+};
+
+static acpi_status parse_csi2_resource(struct acpi_resource *res, void *context)
+{
+ struct csi2_resources_walk_data *crwd = context;
+ struct acpi_resource_csi2_serialbus *csi2_res;
+ struct acpi_resource_source *csi2_res_src;
+ u16 csi2_res_src_length;
+ struct crs_csi2_connection *conn;
+ acpi_handle remote_handle;
+
+ if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+ return AE_OK;
+
+ csi2_res = &res->data.csi2_serial_bus;
+
+ if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2)
+ return AE_OK;
+
+ csi2_res_src = &csi2_res->resource_source;
+ if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr,
+ &remote_handle))) {
+ acpi_handle_debug(crwd->handle,
+ "unable to find resource source\n");
+ return AE_OK;
+ }
+ csi2_res_src_length = csi2_res_src->string_length;
+ if (!csi2_res_src_length) {
+ acpi_handle_debug(crwd->handle,
+ "invalid resource source string length\n");
+ return AE_OK;
+ }
+
+ conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1),
+ GFP_KERNEL);
+ if (!conn)
+ return AE_OK;
+
+ conn->csi2_data = *csi2_res;
+ strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length);
+ conn->csi2_data.resource_source.string_ptr = conn->remote_name;
+ conn->remote_handle = remote_handle;
+
+ list_add(&conn->entry, &crwd->connections);
+
+ return AE_OK;
+}
+
+static struct crs_csi2 *acpi_mipi_add_crs_csi2(acpi_handle handle,
+ struct list_head *list)
+{
+ struct crs_csi2 *csi2;
+
+ csi2 = kzalloc(sizeof(*csi2), GFP_KERNEL);
+ if (!csi2)
+ return NULL;
+
+ csi2->handle = handle;
+ INIT_LIST_HEAD(&csi2->connections);
+ csi2->port_count = 1;
+
+ if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2))) {
+ kfree(csi2);
+ return NULL;
+ }
+
+ list_add(&csi2->entry, list);
+
+ return csi2;
+}
+
+static struct crs_csi2 *acpi_mipi_get_crs_csi2(acpi_handle handle)
+{
+ struct crs_csi2 *csi2;
+
+ if (ACPI_FAILURE(acpi_get_data_full(handle, acpi_mipi_data_tag,
+ (void **)&csi2, NULL)))
+ return NULL;
+
+ return csi2;
+}
+
+static void csi_csr2_release_connections(struct list_head *list)
+{
+ struct crs_csi2_connection *conn, *conn_tmp;
+
+ list_for_each_entry_safe(conn, conn_tmp, list, entry) {
+ list_del(&conn->entry);
+ kfree(conn);
+ }
+}
+
+static void acpi_mipi_del_crs_csi2(struct crs_csi2 *csi2)
+{
+ list_del(&csi2->entry);
+ acpi_detach_data(csi2->handle, acpi_mipi_data_tag);
+ kfree(csi2->swnodes);
+ csi_csr2_release_connections(&csi2->connections);
+ kfree(csi2);
+}
+
+/**
+ * acpi_mipi_check_crs_csi2 - Look for CSI-2 resources in _CRS
+ * @handle: Device object handle to evaluate _CRS for.
+ *
+ * Find all CSI-2 resource descriptors in the given device's _CRS
+ * and collect them into a list.
+ */
+void acpi_mipi_check_crs_csi2(acpi_handle handle)
+{
+ struct csi2_resources_walk_data crwd = {
+ .handle = handle,
+ .connections = LIST_HEAD_INIT(crwd.connections),
+ };
+ struct crs_csi2 *csi2;
+
+ /*
+ * Avoid allocating _CRS CSI-2 objects for devices without any CSI-2
+ * resource descriptions in _CRS to reduce overhead.
+ */
+ acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd);
+ if (list_empty(&crwd.connections))
+ return;
+
+ /*
+ * Create a _CRS CSI-2 entry to store the extracted connection
+ * information and add it to the global list.
+ */
+ csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list);
+ if (!csi2) {
+ csi_csr2_release_connections(&crwd.connections);
+ return; /* Nothing really can be done about this. */
+ }
+
+ list_replace(&crwd.connections, &csi2->connections);
+}
+
+#define NO_CSI2_PORT (UINT_MAX - 1)
+
+static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2)
+{
+ size_t port_count = csi2->port_count;
+ struct acpi_device_software_nodes *swnodes;
+ size_t alloc_size;
+ unsigned int i;
+
+ /*
+ * Allocate memory for ports, node pointers (number of nodes +
+ * 1 (guardian), nodes (root + number of ports * 2 (because for
+ * every port there is an endpoint)).
+ */
+ if (check_mul_overflow(sizeof(*swnodes->ports) +
+ sizeof(*swnodes->nodes) * 2 +
+ sizeof(*swnodes->nodeptrs) * 2,
+ port_count, &alloc_size) ||
+ check_add_overflow(sizeof(*swnodes) +
+ sizeof(*swnodes->nodes) +
+ sizeof(*swnodes->nodeptrs) * 2,
+ alloc_size, &alloc_size)) {
+ acpi_handle_info(csi2->handle,
+ "too many _CRS CSI-2 resource handles (%zu)",
+ port_count);
+ return;
+ }
+
+ swnodes = kmalloc(alloc_size, GFP_KERNEL);
+ if (!swnodes)
+ return;
+
+ swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1);
+ swnodes->nodes = (struct software_node *)(swnodes->ports + port_count);
+ swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 +
+ 2 * port_count);
+ swnodes->num_ports = port_count;
+
+ for (i = 0; i < 2 * port_count + 1; i++)
+ swnodes->nodeptrs[i] = &swnodes->nodes[i];
+
+ swnodes->nodeptrs[i] = NULL;
+
+ for (i = 0; i < port_count; i++)
+ swnodes->ports[i].port_nr = NO_CSI2_PORT;
+
+ csi2->swnodes = swnodes;
+}
+
+#define ACPI_CRS_CSI2_PHY_TYPE_C 0
+#define ACPI_CRS_CSI2_PHY_TYPE_D 1
+
+static unsigned int next_csi2_port_index(struct acpi_device_software_nodes *swnodes,
+ unsigned int port_nr)
+{
+ unsigned int i;
+
+ for (i = 0; i < swnodes->num_ports; i++) {
+ struct acpi_device_software_node_port *port = &swnodes->ports[i];
+
+ if (port->port_nr == port_nr)
+ return i;
+
+ if (port->port_nr == NO_CSI2_PORT) {
+ port->port_nr = port_nr;
+ return i;
+ }
+ }
+
+ return NO_CSI2_PORT;
+}
+
+/* Print graph port name into a buffer, return non-zero on failure. */
+#define GRAPH_PORT_NAME(var, num) \
+ (snprintf((var), sizeof(var), SWNODE_GRAPH_PORT_NAME_FMT, (num)) >= \
+ sizeof(var))
+
+static void extract_crs_csi2_conn_info(acpi_handle local_handle,
+ struct acpi_device_software_nodes *local_swnodes,
+ struct crs_csi2_connection *conn)
+{
+ struct crs_csi2 *remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle);
+ struct acpi_device_software_nodes *remote_swnodes;
+ struct acpi_device_software_node_port *local_port, *remote_port;
+ struct software_node *local_node, *remote_node;
+ unsigned int local_index, remote_index;
+ unsigned int bus_type;
+
+ /*
+ * If the previous steps have failed to make room for a _CRS CSI-2
+ * representation for the remote end of the given connection, skip it.
+ */
+ if (!remote_csi2)
+ return;
+
+ remote_swnodes = remote_csi2->swnodes;
+ if (!remote_swnodes)
+ return;
+
+ switch (conn->csi2_data.phy_type) {
+ case ACPI_CRS_CSI2_PHY_TYPE_C:
+ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY;
+ break;
+
+ case ACPI_CRS_CSI2_PHY_TYPE_D:
+ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY;
+ break;
+
+ default:
+ acpi_handle_info(local_handle, "unknown CSI-2 PHY type %u\n",
+ conn->csi2_data.phy_type);
+ return;
+ }
+
+ local_index = next_csi2_port_index(local_swnodes,
+ conn->csi2_data.local_port_instance);
+ if (WARN_ON_ONCE(local_index >= local_swnodes->num_ports))
+ return;
+
+ remote_index = next_csi2_port_index(remote_swnodes,
+ conn->csi2_data.resource_source.index);
+ if (WARN_ON_ONCE(remote_index >= remote_swnodes->num_ports))
+ return;
+
+ local_port = &local_swnodes->ports[local_index];
+ local_node = &local_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(local_index)];
+ local_port->crs_csi2_local = true;
+
+ remote_port = &remote_swnodes->ports[remote_index];
+ remote_node = &remote_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(remote_index)];
+
+ local_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(remote_node);
+ remote_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(local_node);
+
+ local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] =
+ PROPERTY_ENTRY_REF_ARRAY("remote-endpoint",
+ local_port->remote_ep);
+
+ local_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] =
+ PROPERTY_ENTRY_U32("bus-type", bus_type);
+
+ local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] =
+ PROPERTY_ENTRY_U32("reg", 0);
+
+ local_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] =
+ PROPERTY_ENTRY_U32("reg", conn->csi2_data.local_port_instance);
+
+ if (GRAPH_PORT_NAME(local_port->port_name,
+ conn->csi2_data.local_port_instance))
+ acpi_handle_info(local_handle, "local port %u name too long",
+ conn->csi2_data.local_port_instance);
+
+ remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] =
+ PROPERTY_ENTRY_REF_ARRAY("remote-endpoint",
+ remote_port->remote_ep);
+
+ remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] =
+ PROPERTY_ENTRY_U32("bus-type", bus_type);
+
+ remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] =
+ PROPERTY_ENTRY_U32("reg", 0);
+
+ remote_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] =
+ PROPERTY_ENTRY_U32("reg", conn->csi2_data.resource_source.index);
+
+ if (GRAPH_PORT_NAME(remote_port->port_name,
+ conn->csi2_data.resource_source.index))
+ acpi_handle_info(local_handle, "remote port %u name too long",
+ conn->csi2_data.resource_source.index);
+}
+
+static void prepare_crs_csi2_swnodes(struct crs_csi2 *csi2)
+{
+ struct acpi_device_software_nodes *local_swnodes = csi2->swnodes;
+ acpi_handle local_handle = csi2->handle;
+ struct crs_csi2_connection *conn;
+
+ /* Bail out if the allocation of swnodes has failed. */
+ if (!local_swnodes)
+ return;
+
+ list_for_each_entry(conn, &csi2->connections, entry)
+ extract_crs_csi2_conn_info(local_handle, local_swnodes, conn);
+}
+
+/**
+ * acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes
+ *
+ * Note that this function must be called before any struct acpi_device objects
+ * are bound to any ACPI drivers or scan handlers, so it cannot assume the
+ * existence of struct acpi_device objects for every device present in the ACPI
+ * namespace.
+ *
+ * acpi_scan_lock in scan.c must be held when calling this function.
+ */
+void acpi_mipi_scan_crs_csi2(void)
+{
+ struct crs_csi2 *csi2;
+ LIST_HEAD(aux_list);
+
+ /* Count references to each ACPI handle in the CSI-2 connection graph. */
+ list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) {
+ struct crs_csi2_connection *conn;
+
+ list_for_each_entry(conn, &csi2->connections, entry) {
+ struct crs_csi2 *remote_csi2;
+
+ csi2->port_count++;
+
+ remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle);
+ if (remote_csi2) {
+ remote_csi2->port_count++;
+ continue;
+ }
+ /*
+ * The remote endpoint has no _CRS CSI-2 list entry yet,
+ * so create one for it and add it to the list.
+ */
+ acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list);
+ }
+ }
+ list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
+
+ /*
+ * Allocate software nodes for representing the CSI-2 information.
+ *
+ * This needs to be done for all of the list entries in one go, because
+ * they may point to each other without restrictions and the next step
+ * relies on the availability of swnodes memory for each list entry.
+ */
+ list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
+ alloc_crs_csi2_swnodes(csi2);
+
+ /*
+ * Set up software node properties using data from _CRS CSI-2 resource
+ * descriptors.
+ */
+ list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
+ prepare_crs_csi2_swnodes(csi2);
+}
+
+/*
+ * Get the index of the next property in the property array, with a given
+ * maximum value.
+ */
+#define NEXT_PROPERTY(index, max) \
+ (WARN_ON((index) > ACPI_DEVICE_SWNODE_##max) ? \
+ ACPI_DEVICE_SWNODE_##max : (index)++)
+
+static void init_csi2_port_local(struct acpi_device *adev,
+ struct acpi_device_software_node_port *port,
+ struct fwnode_handle *port_fwnode,
+ unsigned int index)
+{
+ acpi_handle handle = acpi_device_handle(adev);
+ unsigned int num_link_freqs;
+ int ret;
+
+ ret = fwnode_property_count_u64(port_fwnode, "mipi-img-link-frequencies");
+ if (ret <= 0)
+ return;
+
+ num_link_freqs = ret;
+ if (num_link_freqs > ACPI_DEVICE_CSI2_DATA_LANES) {
+ acpi_handle_info(handle, "Too many link frequencies: %u\n",
+ num_link_freqs);
+ num_link_freqs = ACPI_DEVICE_CSI2_DATA_LANES;
+ }
+
+ ret = fwnode_property_read_u64_array(port_fwnode,
+ "mipi-img-link-frequencies",
+ port->link_frequencies,
+ num_link_freqs);
+ if (ret) {
+ acpi_handle_info(handle, "Unable to get link frequencies (%d)\n",
+ ret);
+ return;
+ }
+
+ port->ep_props[NEXT_PROPERTY(index, EP_LINK_FREQUENCIES)] =
+ PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies",
+ port->link_frequencies,
+ num_link_freqs);
+}
+
+static void init_csi2_port(struct acpi_device *adev,
+ struct acpi_device_software_nodes *swnodes,
+ struct acpi_device_software_node_port *port,
+ struct fwnode_handle *port_fwnode,
+ unsigned int port_index)
+{
+ unsigned int ep_prop_index = ACPI_DEVICE_SWNODE_EP_CLOCK_LANES;
+ acpi_handle handle = acpi_device_handle(adev);
+ u8 val[ACPI_DEVICE_CSI2_DATA_LANES];
+ int num_lanes = 0;
+ int ret;
+
+ if (GRAPH_PORT_NAME(port->port_name, port->port_nr))
+ return;
+
+ swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)] =
+ SOFTWARE_NODE(port->port_name, port->port_props,
+ &swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT]);
+
+ ret = fwnode_property_read_u8(port_fwnode, "mipi-img-clock-lane", val);
+ if (!ret)
+ port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_CLOCK_LANES)] =
+ PROPERTY_ENTRY_U32("clock-lanes", val[0]);
+
+ ret = fwnode_property_count_u8(port_fwnode, "mipi-img-data-lanes");
+ if (ret > 0) {
+ num_lanes = ret;
+
+ if (num_lanes > ACPI_DEVICE_CSI2_DATA_LANES) {
+ acpi_handle_info(handle, "Too many data lanes: %u\n",
+ num_lanes);
+ num_lanes = ACPI_DEVICE_CSI2_DATA_LANES;
+ }
+
+ ret = fwnode_property_read_u8_array(port_fwnode,
+ "mipi-img-data-lanes",
+ val, num_lanes);
+ if (!ret) {
+ unsigned int i;
+
+ for (i = 0; i < num_lanes; i++)
+ port->data_lanes[i] = val[i];
+
+ port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_DATA_LANES)] =
+ PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
+ port->data_lanes,
+ num_lanes);
+ }
+ }
+
+ ret = fwnode_property_count_u8(port_fwnode, "mipi-img-lane-polarities");
+ if (ret < 0) {
+ acpi_handle_debug(handle, "Lane polarity bytes missing\n");
+ } else if (ret * BITS_PER_TYPE(u8) < num_lanes + 1) {
+ acpi_handle_info(handle, "Too few lane polarity bits (%zu vs. %d)\n",
+ ret * BITS_PER_TYPE(u8), num_lanes + 1);
+ } else {
+ unsigned long mask = 0;
+ int byte_count = ret;
+ unsigned int i;
+
+ /*
+ * The total number of lanes is ACPI_DEVICE_CSI2_DATA_LANES + 1
+ * (data lanes + clock lane). It is not expected to ever be
+ * greater than the number of bits in an unsigned long
+ * variable, but ensure that this is the case.
+ */
+ BUILD_BUG_ON(BITS_PER_TYPE(unsigned long) <= ACPI_DEVICE_CSI2_DATA_LANES);
+
+ if (byte_count > sizeof(mask)) {
+ acpi_handle_info(handle, "Too many lane polarities: %d\n",
+ byte_count);
+ byte_count = sizeof(mask);
+ }
+ fwnode_property_read_u8_array(port_fwnode, "mipi-img-lane-polarities",
+ val, byte_count);
+
+ for (i = 0; i < byte_count; i++)
+ mask |= (unsigned long)val[i] << BITS_PER_TYPE(u8) * i;
+
+ for (i = 0; i <= num_lanes; i++)
+ port->lane_polarities[i] = test_bit(i, &mask);
+
+ port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_LANE_POLARITIES)] =
+ PROPERTY_ENTRY_U32_ARRAY_LEN("lane-polarities",
+ port->lane_polarities,
+ num_lanes + 1);
+ }
+
+ swnodes->nodes[ACPI_DEVICE_SWNODE_EP(port_index)] =
+ SOFTWARE_NODE("endpoint@0", swnodes->ports[port_index].ep_props,
+ &swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)]);
+
+ if (port->crs_csi2_local)
+ init_csi2_port_local(adev, port, port_fwnode, ep_prop_index);
+}
+
+#define MIPI_IMG_PORT_PREFIX "mipi-img-port-"
+
+static struct fwnode_handle *get_mipi_port_handle(struct fwnode_handle *adev_fwnode,
+ unsigned int port_nr)
+{
+ char port_name[sizeof(MIPI_IMG_PORT_PREFIX) + 2];
+
+ if (snprintf(port_name, sizeof(port_name), "%s%u",
+ MIPI_IMG_PORT_PREFIX, port_nr) >= sizeof(port_name))
+ return NULL;
+
+ return fwnode_get_named_child_node(adev_fwnode, port_name);
+}
+
+static void init_crs_csi2_swnodes(struct crs_csi2 *csi2)
+{
+ struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER };
+ struct acpi_device_software_nodes *swnodes = csi2->swnodes;
+ acpi_handle handle = csi2->handle;
+ unsigned int prop_index = 0;
+ struct fwnode_handle *adev_fwnode;
+ struct acpi_device *adev;
+ acpi_status status;
+ unsigned int i;
+ u32 val;
+ int ret;
+
+ /*
+ * Bail out if the swnodes are not available (either they have not been
+ * allocated or they have been assigned to the device already).
+ */
+ if (!swnodes)
+ return;
+
+ adev = acpi_fetch_acpi_dev(handle);
+ if (!adev)
+ return;
+
+ adev_fwnode = acpi_fwnode_handle(adev);
+
+ /*
+ * If the "rotation" property is not present, but _PLD is there,
+ * evaluate it to get the "rotation" value.
+ */
+ if (!fwnode_property_present(adev_fwnode, "rotation")) {
+ struct acpi_pld_info *pld;
+
+ if (acpi_get_physical_device_location(handle, &pld)) {
+ swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_ROTATION)] =
+ PROPERTY_ENTRY_U32("rotation",
+ pld->rotation * 45U);
+ kfree(pld);
+ }
+ }
+
+ if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-clock-frequency", &val))
+ swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_CLOCK_FREQUENCY)] =
+ PROPERTY_ENTRY_U32("clock-frequency", val);
+
+ if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-led-max-current", &val))
+ swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_LED_MAX_MICROAMP)] =
+ PROPERTY_ENTRY_U32("led-max-microamp", val);
+
+ if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-current", &val))
+ swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_MICROAMP)] =
+ PROPERTY_ENTRY_U32("flash-max-microamp", val);
+
+ if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-timeout-us", &val))
+ swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_TIMEOUT_US)] =
+ PROPERTY_ENTRY_U32("flash-max-timeout-us", val);
+
+ status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_info(handle, "Unable to get the path name\n");
+ return;
+ }
+
+ swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT] =
+ SOFTWARE_NODE(buffer.pointer, swnodes->dev_props, NULL);
+
+ for (i = 0; i < swnodes->num_ports; i++) {
+ struct acpi_device_software_node_port *port = &swnodes->ports[i];
+ struct fwnode_handle *port_fwnode;
+
+ /*
+ * The MIPI DisCo for Imaging specification defines _DSD device
+ * properties for providing CSI-2 port parameters that can be
+ * accessed through the generic device properties framework. To
+ * access them, it is first necessary to find the data node
+ * representing the port under the given ACPI device object.
+ */
+ port_fwnode = get_mipi_port_handle(adev_fwnode, port->port_nr);
+ if (!port_fwnode) {
+ acpi_handle_info(handle,
+ "MIPI port name too long for port %u\n",
+ port->port_nr);
+ continue;
+ }
+
+ init_csi2_port(adev, swnodes, port, port_fwnode, i);
+
+ fwnode_handle_put(port_fwnode);
+ }
+
+ ret = software_node_register_node_group(swnodes->nodeptrs);
+ if (ret < 0) {
+ acpi_handle_info(handle,
+ "Unable to register software nodes (%d)\n", ret);
+ return;
+ }
+
+ adev->swnodes = swnodes;
+ adev_fwnode->secondary = software_node_fwnode(swnodes->nodes);
+
+ /*
+ * Prevents the swnodes from this csi2 entry from being assigned again
+ * or freed prematurely.
+ */
+ csi2->swnodes = NULL;
+}
+
+/**
+ * acpi_mipi_init_crs_csi2_swnodes - Initialize _CRS CSI-2 software nodes
+ *
+ * Use MIPI DisCo for Imaging device properties to finalize the initialization
+ * of CSI-2 software nodes for all ACPI device objects that have been already
+ * enumerated.
+ */
+void acpi_mipi_init_crs_csi2_swnodes(void)
+{
+ struct crs_csi2 *csi2, *csi2_tmp;
+
+ list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
+ init_crs_csi2_swnodes(csi2);
+}
+
+/**
+ * acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data
+ */
+void acpi_mipi_crs_csi2_cleanup(void)
+{
+ struct crs_csi2 *csi2, *csi2_tmp;
+
+ list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
+ acpi_mipi_del_crs_csi2(csi2);
+}
+
+#ifdef CONFIG_X86
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+/* CPU matches for Dell generations with broken ACPI MIPI DISCO info */
+static const struct x86_cpu_id dell_broken_mipi_disco_cpu_gens[] = {
+ X86_MATCH_VFM(INTEL_TIGERLAKE, NULL),
+ X86_MATCH_VFM(INTEL_TIGERLAKE_L, NULL),
+ X86_MATCH_VFM(INTEL_ALDERLAKE, NULL),
+ X86_MATCH_VFM(INTEL_ALDERLAKE_L, NULL),
+ X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL),
+ X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL),
+ X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL),
+ {}
+};
+
+static const char *strnext(const char *s1, const char *s2)
+{
+ s1 = strstr(s1, s2);
+
+ if (!s1)
+ return NULL;
+
+ return s1 + strlen(s2);
+}
+
+/**
+ * acpi_graph_ignore_port - Tell whether a port node should be ignored
+ * @handle: The ACPI handle of the node (which may be a port node)
+ *
+ * Return: true if a port node should be ignored and the data to that should
+ * come from other sources instead (Windows ACPI definitions and
+ * ipu-bridge). This is currently used to ignore bad port nodes related to IPU6
+ * ("IPU?") and camera sensor devices ("LNK?") in certain Dell systems with
+ * Intel VSC.
+ */
+bool acpi_graph_ignore_port(acpi_handle handle)
+{
+ const char *path = NULL, *orig_path;
+ static bool dmi_tested, ignore_port;
+
+ if (!dmi_tested) {
+ if (dmi_name_in_vendors("Dell Inc.") &&
+ x86_match_cpu(dell_broken_mipi_disco_cpu_gens))
+ ignore_port = true;
+
+ dmi_tested = true;
+ }
+
+ if (!ignore_port)
+ return false;
+
+ /* Check if the device is either "IPU" or "LNK" (sensor). */
+ orig_path = acpi_handle_path(handle);
+ if (!orig_path)
+ return false;
+ path = strnext(orig_path, "IPU");
+ if (!path)
+ path = strnext(orig_path, "LNK");
+ if (!path)
+ goto out_free;
+
+ if (!(isdigit(path[0]) && path[1] == '.'))
+ goto out_free;
+
+ /* Check if the node has a "PRT" prefix. */
+ path = strnext(path, "PRT");
+ if (path && isdigit(path[0]) && !path[1]) {
+ acpi_handle_debug(handle, "ignoring data node\n");
+
+ kfree(orig_path);
+ return true;
+ }
+
+out_free:
+ kfree(orig_path);
+ return false;
+}
+#endif
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index ae5f4acf2675..3eb56b77cb6d 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -454,8 +454,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
if (cmd_rc)
*cmd_rc = -EINVAL;
- if (cmd == ND_CMD_CALL)
+ if (cmd == ND_CMD_CALL) {
+ if (!buf || buf_len < sizeof(*call_pkg))
+ return -EINVAL;
+
call_pkg = buf;
+ }
+
func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
if (func < 0)
return func;
@@ -480,7 +485,7 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
cmd_mask = nd_desc->cmd_mask;
if (cmd == ND_CMD_CALL && call_pkg->nd_family) {
family = call_pkg->nd_family;
- if (family > NVDIMM_BUS_FAMILY_MAX ||
+ if (call_pkg->nd_family > NVDIMM_BUS_FAMILY_MAX ||
!test_bit(family, &nd_desc->bus_family_mask))
return -EINVAL;
family = array_index_nospec(family,
@@ -855,7 +860,7 @@ static size_t sizeof_idt(struct acpi_nfit_interleave *idt)
{
if (idt->header.length < sizeof(*idt))
return 0;
- return sizeof(*idt) + sizeof(u32) * (idt->line_count - 1);
+ return sizeof(*idt) + sizeof(u32) * idt->line_count;
}
static bool add_idt(struct acpi_nfit_desc *acpi_desc,
@@ -894,7 +899,7 @@ static size_t sizeof_flush(struct acpi_nfit_flush_address *flush)
{
if (flush->header.length < sizeof(*flush))
return 0;
- return sizeof(*flush) + sizeof(u64) * (flush->hint_count - 1);
+ return struct_size(flush, hint_address, flush->hint_count);
}
static bool add_flush(struct acpi_nfit_desc *acpi_desc,
@@ -1186,7 +1191,7 @@ static ssize_t bus_dsm_mask_show(struct device *dev,
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- return sprintf(buf, "%#lx\n", acpi_desc->bus_dsm_mask);
+ return sysfs_emit(buf, "%#lx\n", acpi_desc->bus_dsm_mask);
}
static struct device_attribute dev_attr_bus_dsm_mask =
__ATTR(dsm_mask, 0444, bus_dsm_mask_show, NULL);
@@ -1198,7 +1203,7 @@ static ssize_t revision_show(struct device *dev,
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- return sprintf(buf, "%d\n", acpi_desc->acpi_header.revision);
+ return sysfs_emit(buf, "%d\n", acpi_desc->acpi_header.revision);
}
static DEVICE_ATTR_RO(revision);
@@ -1209,7 +1214,7 @@ static ssize_t hw_error_scrub_show(struct device *dev,
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- return sprintf(buf, "%d\n", acpi_desc->scrub_mode);
+ return sysfs_emit(buf, "%d\n", acpi_desc->scrub_mode);
}
/*
@@ -1278,7 +1283,7 @@ static ssize_t scrub_show(struct device *dev,
mutex_lock(&acpi_desc->init_mutex);
busy = test_bit(ARS_BUSY, &acpi_desc->scrub_flags)
&& !test_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
- rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, busy ? "+\n" : "\n");
+ rc = sysfs_emit(buf, "%d%s", acpi_desc->scrub_count, busy ? "+\n" : "\n");
/* Allow an admin to poll the busy state at a higher rate */
if (busy && capable(CAP_SYS_RAWIO) && !test_and_set_bit(ARS_POLL,
&acpi_desc->scrub_flags)) {
@@ -1382,7 +1387,7 @@ static ssize_t handle_show(struct device *dev,
{
struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
- return sprintf(buf, "%#x\n", memdev->device_handle);
+ return sysfs_emit(buf, "%#x\n", memdev->device_handle);
}
static DEVICE_ATTR_RO(handle);
@@ -1391,7 +1396,7 @@ static ssize_t phys_id_show(struct device *dev,
{
struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
- return sprintf(buf, "%#x\n", memdev->physical_id);
+ return sysfs_emit(buf, "%#x\n", memdev->physical_id);
}
static DEVICE_ATTR_RO(phys_id);
@@ -1400,7 +1405,7 @@ static ssize_t vendor_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->vendor_id));
+ return sysfs_emit(buf, "0x%04x\n", be16_to_cpu(dcr->vendor_id));
}
static DEVICE_ATTR_RO(vendor);
@@ -1409,7 +1414,7 @@ static ssize_t rev_id_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->revision_id));
+ return sysfs_emit(buf, "0x%04x\n", be16_to_cpu(dcr->revision_id));
}
static DEVICE_ATTR_RO(rev_id);
@@ -1418,7 +1423,7 @@ static ssize_t device_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->device_id));
+ return sysfs_emit(buf, "0x%04x\n", be16_to_cpu(dcr->device_id));
}
static DEVICE_ATTR_RO(device);
@@ -1427,7 +1432,7 @@ static ssize_t subsystem_vendor_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_vendor_id));
+ return sysfs_emit(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_vendor_id));
}
static DEVICE_ATTR_RO(subsystem_vendor);
@@ -1436,7 +1441,7 @@ static ssize_t subsystem_rev_id_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n",
+ return sysfs_emit(buf, "0x%04x\n",
be16_to_cpu(dcr->subsystem_revision_id));
}
static DEVICE_ATTR_RO(subsystem_rev_id);
@@ -1446,7 +1451,7 @@ static ssize_t subsystem_device_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_device_id));
+ return sysfs_emit(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_device_id));
}
static DEVICE_ATTR_RO(subsystem_device);
@@ -1465,7 +1470,7 @@ static ssize_t format_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%04x\n", le16_to_cpu(dcr->code));
+ return sysfs_emit(buf, "0x%04x\n", le16_to_cpu(dcr->code));
}
static DEVICE_ATTR_RO(format);
@@ -1498,7 +1503,7 @@ static ssize_t format1_show(struct device *dev,
continue;
if (nfit_dcr->dcr->code == dcr->code)
continue;
- rc = sprintf(buf, "0x%04x\n",
+ rc = sysfs_emit(buf, "0x%04x\n",
le16_to_cpu(nfit_dcr->dcr->code));
break;
}
@@ -1515,7 +1520,7 @@ static ssize_t formats_show(struct device *dev,
{
struct nvdimm *nvdimm = to_nvdimm(dev);
- return sprintf(buf, "%d\n", num_nvdimm_formats(nvdimm));
+ return sysfs_emit(buf, "%d\n", num_nvdimm_formats(nvdimm));
}
static DEVICE_ATTR_RO(formats);
@@ -1524,7 +1529,7 @@ static ssize_t serial_show(struct device *dev,
{
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
- return sprintf(buf, "0x%08x\n", be32_to_cpu(dcr->serial_number));
+ return sysfs_emit(buf, "0x%08x\n", be32_to_cpu(dcr->serial_number));
}
static DEVICE_ATTR_RO(serial);
@@ -1536,7 +1541,7 @@ static ssize_t family_show(struct device *dev,
if (nfit_mem->family < 0)
return -ENXIO;
- return sprintf(buf, "%d\n", nfit_mem->family);
+ return sysfs_emit(buf, "%d\n", nfit_mem->family);
}
static DEVICE_ATTR_RO(family);
@@ -1548,7 +1553,7 @@ static ssize_t dsm_mask_show(struct device *dev,
if (nfit_mem->family < 0)
return -ENXIO;
- return sprintf(buf, "%#lx\n", nfit_mem->dsm_mask);
+ return sysfs_emit(buf, "%#lx\n", nfit_mem->dsm_mask);
}
static DEVICE_ATTR_RO(dsm_mask);
@@ -1562,7 +1567,7 @@ static ssize_t flags_show(struct device *dev,
if (test_bit(NFIT_MEM_DIRTY, &nfit_mem->flags))
flags |= ACPI_NFIT_MEM_FLUSH_FAILED;
- return sprintf(buf, "%s%s%s%s%s%s%s\n",
+ return sysfs_emit(buf, "%s%s%s%s%s%s%s\n",
flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "",
flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore_fail " : "",
flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush_fail " : "",
@@ -1579,7 +1584,7 @@ static ssize_t id_show(struct device *dev,
struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- return sprintf(buf, "%s\n", nfit_mem->id);
+ return sysfs_emit(buf, "%s\n", nfit_mem->id);
}
static DEVICE_ATTR_RO(id);
@@ -1589,7 +1594,7 @@ static ssize_t dirty_shutdown_show(struct device *dev,
struct nvdimm *nvdimm = to_nvdimm(dev);
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown);
+ return sysfs_emit(buf, "%d\n", nfit_mem->dirty_shutdown);
}
static DEVICE_ATTR_RO(dirty_shutdown);
@@ -1737,9 +1742,8 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
if ((nfit_mem->dsm_mask & (1 << func)) == 0)
return;
- out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
- if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER
- || out_obj->buffer.length < sizeof(smart)) {
+ out_obj = acpi_evaluate_dsm_typed(handle, guid, revid, func, &in_obj, ACPI_TYPE_BUFFER);
+ if (!out_obj || out_obj->buffer.length < sizeof(smart)) {
dev_dbg(dev->parent, "%s: failed to retrieve initial health\n",
dev_name(dev));
ACPI_FREE(out_obj);
@@ -2172,7 +2176,7 @@ static ssize_t range_index_show(struct device *dev,
struct nd_region *nd_region = to_nd_region(dev);
struct nfit_spa *nfit_spa = nd_region_provider_data(nd_region);
- return sprintf(buf, "%d\n", nfit_spa->spa->range_index);
+ return sysfs_emit(buf, "%d\n", nfit_spa->spa->range_index);
}
static DEVICE_ATTR_RO(range_index);
@@ -2257,26 +2261,23 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
struct nd_region_desc *ndr_desc,
struct acpi_nfit_system_address *spa)
{
+ u16 nr = ndr_desc->num_mappings;
+ struct nfit_set_info2 *info2 __free(kfree) =
+ kcalloc(nr, sizeof(*info2), GFP_KERNEL);
+ struct nfit_set_info *info __free(kfree) =
+ kcalloc(nr, sizeof(*info), GFP_KERNEL);
struct device *dev = acpi_desc->dev;
struct nd_interleave_set *nd_set;
- u16 nr = ndr_desc->num_mappings;
- struct nfit_set_info2 *info2;
- struct nfit_set_info *info;
int i;
+ if (!info || !info2)
+ return -ENOMEM;
+
nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL);
if (!nd_set)
return -ENOMEM;
import_guid(&nd_set->type_guid, spa->range_guid);
- info = devm_kcalloc(dev, nr, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info2 = devm_kcalloc(dev, nr, sizeof(*info2), GFP_KERNEL);
- if (!info2)
- return -ENOMEM;
-
for (i = 0; i < nr; i++) {
struct nd_mapping_desc *mapping = &ndr_desc->mapping[i];
struct nvdimm *nvdimm = mapping->nvdimm;
@@ -2337,8 +2338,6 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
}
ndr_desc->nd_set = nd_set;
- devm_kfree(dev, info);
- devm_kfree(dev, info2);
return 0;
}
@@ -2638,7 +2637,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
if (ndr_desc->target_node == NUMA_NO_NODE) {
ndr_desc->target_node = phys_to_target_node(spa->address);
dev_info(acpi_desc->dev, "changing target node from %d to %d for nfit region [%pa-%pa]",
- NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end);
+ NUMA_NO_NODE, ndr_desc->target_node, &res.start, &res.end);
}
/*
@@ -3282,6 +3281,23 @@ static void acpi_nfit_put_table(void *table)
acpi_put_table(table);
}
+static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct acpi_device *adev = data;
+
+ device_lock(&adev->dev);
+ __acpi_nfit_notify(&adev->dev, handle, event);
+ device_unlock(&adev->dev);
+}
+
+static void acpi_nfit_remove_notify_handler(void *data)
+{
+ struct acpi_device *adev = data;
+
+ acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY,
+ acpi_nfit_notify);
+}
+
void acpi_nfit_shutdown(void *data)
{
struct acpi_nfit_desc *acpi_desc = data;
@@ -3297,8 +3313,8 @@ void acpi_nfit_shutdown(void *data)
mutex_lock(&acpi_desc->init_mutex);
set_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
- cancel_delayed_work_sync(&acpi_desc->dwork);
mutex_unlock(&acpi_desc->init_mutex);
+ cancel_delayed_work_sync(&acpi_desc->dwork);
/*
* Bounce the nvdimm bus lock to make sure any in-flight
@@ -3322,6 +3338,16 @@ static int acpi_nfit_add(struct acpi_device *adev)
acpi_size sz;
int rc = 0;
+ rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
+ acpi_nfit_notify, adev);
+ if (rc)
+ return rc;
+
+ rc = devm_add_action_or_reset(dev, acpi_nfit_remove_notify_handler,
+ adev);
+ if (rc)
+ return rc;
+
status = acpi_get_table(ACPI_SIG_NFIT, 0, &tbl);
if (ACPI_FAILURE(status)) {
/* The NVDIMM root device allows OS to trigger enumeration of
@@ -3368,13 +3394,8 @@ static int acpi_nfit_add(struct acpi_device *adev)
if (rc)
return rc;
- return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc);
-}
-static int acpi_nfit_remove(struct acpi_device *adev)
-{
- /* see acpi_nfit_unregister */
- return 0;
+ return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc);
}
static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
@@ -3447,13 +3468,6 @@ void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
}
EXPORT_SYMBOL_GPL(__acpi_nfit_notify);
-static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
-{
- device_lock(&adev->dev);
- __acpi_nfit_notify(&adev->dev, adev->handle, event);
- device_unlock(&adev->dev);
-}
-
static const struct acpi_device_id acpi_nfit_ids[] = {
{ "ACPI0012", 0 },
{ "", 0 },
@@ -3465,8 +3479,6 @@ static struct acpi_driver acpi_nfit_driver = {
.ids = acpi_nfit_ids,
.ops = {
.add = acpi_nfit_add,
- .remove = acpi_nfit_remove,
- .notify = acpi_nfit_notify,
},
};
@@ -3477,8 +3489,8 @@ static __init int nfit_init(void)
BUILD_BUG_ON(sizeof(struct acpi_table_nfit) != 40);
BUILD_BUG_ON(sizeof(struct acpi_nfit_system_address) != 64);
BUILD_BUG_ON(sizeof(struct acpi_nfit_memory_map) != 48);
- BUILD_BUG_ON(sizeof(struct acpi_nfit_interleave) != 20);
- BUILD_BUG_ON(sizeof(struct acpi_nfit_smbios) != 9);
+ BUILD_BUG_ON(sizeof(struct acpi_nfit_interleave) != 16);
+ BUILD_BUG_ON(sizeof(struct acpi_nfit_smbios) != 8);
BUILD_BUG_ON(sizeof(struct acpi_nfit_control_region) != 80);
BUILD_BUG_ON(sizeof(struct acpi_nfit_data_region) != 40);
BUILD_BUG_ON(sizeof(struct acpi_nfit_capabilities) != 16);
@@ -3524,5 +3536,6 @@ static __exit void nfit_exit(void)
module_init(nfit_init);
module_exit(nfit_exit);
+MODULE_DESCRIPTION("ACPI NVDIMM Firmware Interface Table (NFIT) driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Intel Corporation");
diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c
index 8dd792a55730..bce6f6a18426 100644
--- a/drivers/acpi/nfit/intel.c
+++ b/drivers/acpi/nfit/intel.c
@@ -3,6 +3,7 @@
#include <linux/libnvdimm.h>
#include <linux/ndctl.h>
#include <linux/acpi.h>
+#include <linux/memregion.h>
#include <asm/smp.h>
#include "intel.h"
#include "nfit.h"
@@ -54,10 +55,9 @@ static unsigned long intel_security_flags(struct nvdimm *nvdimm,
{
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
unsigned long security_flags = 0;
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_get_security_state cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_GET_SECURITY_STATE,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -119,10 +119,9 @@ static unsigned long intel_security_flags(struct nvdimm *nvdimm,
static int intel_security_freeze(struct nvdimm *nvdimm)
{
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_freeze_lock cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_FREEZE_LOCK,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -152,10 +151,9 @@ static int intel_security_change_key(struct nvdimm *nvdimm,
unsigned int cmd = ptype == NVDIMM_MASTER ?
NVDIMM_INTEL_SET_MASTER_PASSPHRASE :
NVDIMM_INTEL_SET_PASSPHRASE;
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_set_passphrase cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_family = NVDIMM_FAMILY_INTEL,
.nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2,
@@ -190,16 +188,13 @@ static int intel_security_change_key(struct nvdimm *nvdimm,
}
}
-static void nvdimm_invalidate_cache(void);
-
static int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm,
const struct nvdimm_key_data *key_data)
{
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_unlock_unit cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_UNLOCK_UNIT,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -227,9 +222,6 @@ static int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm,
return -EIO;
}
- /* DIMM unlocked, invalidate all CPU caches before we read it */
- nvdimm_invalidate_cache();
-
return 0;
}
@@ -238,10 +230,9 @@ static int intel_security_disable(struct nvdimm *nvdimm,
{
int rc;
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_disable_passphrase cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -281,10 +272,9 @@ static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
unsigned int cmd = ptype == NVDIMM_MASTER ?
NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE;
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_secure_erase cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_family = NVDIMM_FAMILY_INTEL,
.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
@@ -297,8 +287,6 @@ static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,
if (!test_bit(cmd, &nfit_mem->dsm_mask))
return -ENOTTY;
- /* flush all cache before we erase DIMM */
- nvdimm_invalidate_cache();
memcpy(nd_cmd.cmd.passphrase, key->data,
sizeof(nd_cmd.cmd.passphrase));
rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
@@ -317,8 +305,6 @@ static int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,
return -ENXIO;
}
- /* DIMM erased, invalidate all CPU caches before we read it */
- nvdimm_invalidate_cache();
return 0;
}
@@ -326,10 +312,9 @@ static int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm)
{
int rc;
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_query_overwrite cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_QUERY_OVERWRITE,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -354,8 +339,6 @@ static int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm)
return -ENXIO;
}
- /* flush all cache before we make the nvdimms available */
- nvdimm_invalidate_cache();
return 0;
}
@@ -364,10 +347,9 @@ static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,
{
int rc;
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_overwrite cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_OVERWRITE,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -380,8 +362,6 @@ static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,
if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask))
return -ENOTTY;
- /* flush all cache before we erase DIMM */
- nvdimm_invalidate_cache();
memcpy(nd_cmd.cmd.passphrase, nkey->data,
sizeof(nd_cmd.cmd.passphrase));
rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
@@ -401,22 +381,6 @@ static int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,
}
}
-/*
- * TODO: define a cross arch wbinvd equivalent when/if
- * NVDIMM_FAMILY_INTEL command support arrives on another arch.
- */
-#ifdef CONFIG_X86
-static void nvdimm_invalidate_cache(void)
-{
- wbinvd_on_all_cpus();
-}
-#else
-static void nvdimm_invalidate_cache(void)
-{
- WARN_ON_ONCE("cache invalidation required after unlock\n");
-}
-#endif
-
static const struct nvdimm_security_ops __intel_security_ops = {
.get_flags = intel_security_flags,
.freeze = intel_security_freeze,
@@ -435,10 +399,9 @@ const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops;
static int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc,
struct nd_intel_bus_fw_activate_businfo *info)
{
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_bus_fw_activate_businfo cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
.nd_family = NVDIMM_BUS_FAMILY_INTEL,
@@ -546,33 +509,31 @@ static enum nvdimm_fwa_capability intel_bus_fwa_capability(
static int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc)
{
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_bus_fw_activate cmd;
- } nd_cmd = {
- .pkg = {
- .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE,
- .nd_family = NVDIMM_BUS_FAMILY_INTEL,
- .nd_size_in = sizeof(nd_cmd.cmd.iodev_state),
- .nd_size_out =
- sizeof(struct nd_intel_bus_fw_activate),
- .nd_fw_size =
- sizeof(struct nd_intel_bus_fw_activate),
- },
+ ) nd_cmd;
+ int rc;
+
+ nd_cmd.pkg = (struct nd_cmd_pkg) {
+ .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE,
+ .nd_family = NVDIMM_BUS_FAMILY_INTEL,
+ .nd_size_in = sizeof(nd_cmd.cmd.iodev_state),
+ .nd_size_out =
+ sizeof(struct nd_intel_bus_fw_activate),
+ .nd_fw_size =
+ sizeof(struct nd_intel_bus_fw_activate),
+ };
+ nd_cmd.cmd = (struct nd_intel_bus_fw_activate) {
/*
* Even though activate is run from a suspended context,
* for safety, still ask platform firmware to force
* quiesce devices by default. Let a module
* parameter override that policy.
*/
- .cmd = {
- .iodev_state = acpi_desc->fwa_noidle
- ? ND_INTEL_BUS_FWA_IODEV_OS_IDLE
- : ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE,
- },
+ .iodev_state = acpi_desc->fwa_noidle
+ ? ND_INTEL_BUS_FWA_IODEV_OS_IDLE
+ : ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE,
};
- int rc;
-
switch (intel_bus_fwa_state(nd_desc)) {
case NVDIMM_FWA_ARMED:
case NVDIMM_FWA_ARM_OVERFLOW:
@@ -610,10 +571,9 @@ const struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops;
static int intel_fwa_dimminfo(struct nvdimm *nvdimm,
struct nd_intel_fw_activate_dimminfo *info)
{
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_fw_activate_dimminfo cmd;
- } nd_cmd = {
+ ) nd_cmd = {
.pkg = {
.nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO,
.nd_family = NVDIMM_FAMILY_INTEL,
@@ -716,27 +676,24 @@ static int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm)
{
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
- struct {
- struct nd_cmd_pkg pkg;
+ TRAILING_OVERLAP(struct nd_cmd_pkg, pkg, nd_payload,
struct nd_intel_fw_activate_arm cmd;
- } nd_cmd = {
- .pkg = {
- .nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM,
- .nd_family = NVDIMM_FAMILY_INTEL,
- .nd_size_in = sizeof(nd_cmd.cmd.activate_arm),
- .nd_size_out =
- sizeof(struct nd_intel_fw_activate_arm),
- .nd_fw_size =
- sizeof(struct nd_intel_fw_activate_arm),
- },
- .cmd = {
- .activate_arm = arm == NVDIMM_FWA_ARM
- ? ND_INTEL_DIMM_FWA_ARM
- : ND_INTEL_DIMM_FWA_DISARM,
- },
- };
+ ) nd_cmd;
int rc;
+ nd_cmd.pkg = (struct nd_cmd_pkg) {
+ .nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM,
+ .nd_family = NVDIMM_FAMILY_INTEL,
+ .nd_size_in = sizeof(nd_cmd.cmd.activate_arm),
+ .nd_size_out = sizeof(struct nd_intel_fw_activate_arm),
+ .nd_fw_size = sizeof(struct nd_intel_fw_activate_arm),
+ };
+ nd_cmd.cmd = (struct nd_intel_fw_activate_arm) {
+ .activate_arm = arm == NVDIMM_FWA_ARM ?
+ ND_INTEL_DIMM_FWA_ARM :
+ ND_INTEL_DIMM_FWA_DISARM,
+ };
+
switch (intel_fwa_state(nvdimm)) {
case NVDIMM_FWA_INVALID:
return -ENXIO;
diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h
index 6023ad61831a..573bc0de2990 100644
--- a/drivers/acpi/nfit/nfit.h
+++ b/drivers/acpi/nfit/nfit.h
@@ -347,4 +347,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev);
bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus);
extern struct device_attribute dev_attr_firmware_activate_noidle;
+void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem);
+
#endif /* __NFIT_H__ */
diff --git a/drivers/acpi/nhlt.c b/drivers/acpi/nhlt.c
new file mode 100644
index 000000000000..dc1bd0df9228
--- /dev/null
+++ b/drivers/acpi/nhlt.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2023-2024 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: NHLT: " fmt
+
+#include <linux/acpi.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/minmax.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+#include <acpi/nhlt.h>
+
+static struct acpi_table_nhlt *acpi_gbl_nhlt;
+
+static struct acpi_table_nhlt empty_nhlt = {
+ .header = {
+ .signature = ACPI_SIG_NHLT,
+ },
+};
+
+/**
+ * acpi_nhlt_get_gbl_table - Retrieve a pointer to the first NHLT table.
+ *
+ * If there is no NHLT in the system, acpi_gbl_nhlt will instead point to an
+ * empty table.
+ *
+ * Return: ACPI status code of the operation.
+ */
+acpi_status acpi_nhlt_get_gbl_table(void)
+{
+ acpi_status status;
+
+ status = acpi_get_table(ACPI_SIG_NHLT, 0, (struct acpi_table_header **)(&acpi_gbl_nhlt));
+ if (!acpi_gbl_nhlt)
+ acpi_gbl_nhlt = &empty_nhlt;
+ return status;
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_get_gbl_table);
+
+/**
+ * acpi_nhlt_put_gbl_table - Release the global NHLT table.
+ */
+void acpi_nhlt_put_gbl_table(void)
+{
+ acpi_put_table((struct acpi_table_header *)acpi_gbl_nhlt);
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_put_gbl_table);
+
+/**
+ * acpi_nhlt_endpoint_match - Verify if an endpoint matches criteria.
+ * @ep: the endpoint to check.
+ * @link_type: the hardware link type, e.g.: PDM or SSP.
+ * @dev_type: the device type.
+ * @dir: stream direction.
+ * @bus_id: the ID of virtual bus hosting the endpoint.
+ *
+ * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
+ * value to ignore the parameter when matching.
+ *
+ * Return: %true if endpoint matches specified criteria or %false otherwise.
+ */
+bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep,
+ int link_type, int dev_type, int dir, int bus_id)
+{
+ return ep &&
+ (link_type < 0 || ep->link_type == link_type) &&
+ (dev_type < 0 || ep->device_type == dev_type) &&
+ (bus_id < 0 || ep->virtual_bus_id == bus_id) &&
+ (dir < 0 || ep->direction == dir);
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_match);
+
+/**
+ * acpi_nhlt_tb_find_endpoint - Search a NHLT table for an endpoint.
+ * @tb: the table to search.
+ * @link_type: the hardware link type, e.g.: PDM or SSP.
+ * @dev_type: the device type.
+ * @dir: stream direction.
+ * @bus_id: the ID of virtual bus hosting the endpoint.
+ *
+ * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
+ * value to ignore the parameter during the search.
+ *
+ * Return: A pointer to endpoint matching the criteria, %NULL if not found or
+ * an ERR_PTR() otherwise.
+ */
+struct acpi_nhlt_endpoint *
+acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt *tb,
+ int link_type, int dev_type, int dir, int bus_id)
+{
+ struct acpi_nhlt_endpoint *ep;
+
+ for_each_nhlt_endpoint(tb, ep)
+ if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id))
+ return ep;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_endpoint);
+
+/**
+ * acpi_nhlt_find_endpoint - Search all NHLT tables for an endpoint.
+ * @link_type: the hardware link type, e.g.: PDM or SSP.
+ * @dev_type: the device type.
+ * @dir: stream direction.
+ * @bus_id: the ID of virtual bus hosting the endpoint.
+ *
+ * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
+ * value to ignore the parameter during the search.
+ *
+ * Return: A pointer to endpoint matching the criteria, %NULL if not found or
+ * an ERR_PTR() otherwise.
+ */
+struct acpi_nhlt_endpoint *
+acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id)
+{
+ /* TODO: Currently limited to table of index 0. */
+ return acpi_nhlt_tb_find_endpoint(acpi_gbl_nhlt, link_type, dev_type, dir, bus_id);
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_find_endpoint);
+
+/**
+ * acpi_nhlt_endpoint_find_fmtcfg - Search endpoint's formats configuration space
+ * for a specific format.
+ * @ep: the endpoint to search.
+ * @ch: number of channels.
+ * @rate: samples per second.
+ * @vbps: valid bits per sample.
+ * @bps: bits per sample.
+ *
+ * Return: A pointer to format matching the criteria, %NULL if not found or
+ * an ERR_PTR() otherwise.
+ */
+struct acpi_nhlt_format_config *
+acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep,
+ u16 ch, u32 rate, u16 vbps, u16 bps)
+{
+ struct acpi_nhlt_wave_formatext *wav;
+ struct acpi_nhlt_format_config *fmt;
+
+ for_each_nhlt_endpoint_fmtcfg(ep, fmt) {
+ wav = &fmt->format;
+
+ if (wav->valid_bits_per_sample == vbps &&
+ wav->samples_per_sec == rate &&
+ wav->bits_per_sample == bps &&
+ wav->channel_count == ch)
+ return fmt;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_find_fmtcfg);
+
+/**
+ * acpi_nhlt_tb_find_fmtcfg - Search a NHLT table for a specific format.
+ * @tb: the table to search.
+ * @link_type: the hardware link type, e.g.: PDM or SSP.
+ * @dev_type: the device type.
+ * @dir: stream direction.
+ * @bus_id: the ID of virtual bus hosting the endpoint.
+ *
+ * @ch: number of channels.
+ * @rate: samples per second.
+ * @vbps: valid bits per sample.
+ * @bps: bits per sample.
+ *
+ * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
+ * value to ignore the parameter during the search.
+ *
+ * Return: A pointer to format matching the criteria, %NULL if not found or
+ * an ERR_PTR() otherwise.
+ */
+struct acpi_nhlt_format_config *
+acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt *tb,
+ int link_type, int dev_type, int dir, int bus_id,
+ u16 ch, u32 rate, u16 vbps, u16 bps)
+{
+ struct acpi_nhlt_format_config *fmt;
+ struct acpi_nhlt_endpoint *ep;
+
+ for_each_nhlt_endpoint(tb, ep) {
+ if (!acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id))
+ continue;
+
+ fmt = acpi_nhlt_endpoint_find_fmtcfg(ep, ch, rate, vbps, bps);
+ if (fmt)
+ return fmt;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_fmtcfg);
+
+/**
+ * acpi_nhlt_find_fmtcfg - Search all NHLT tables for a specific format.
+ * @link_type: the hardware link type, e.g.: PDM or SSP.
+ * @dev_type: the device type.
+ * @dir: stream direction.
+ * @bus_id: the ID of virtual bus hosting the endpoint.
+ *
+ * @ch: number of channels.
+ * @rate: samples per second.
+ * @vbps: valid bits per sample.
+ * @bps: bits per sample.
+ *
+ * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative
+ * value to ignore the parameter during the search.
+ *
+ * Return: A pointer to format matching the criteria, %NULL if not found or
+ * an ERR_PTR() otherwise.
+ */
+struct acpi_nhlt_format_config *
+acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id,
+ u16 ch, u32 rate, u16 vbps, u16 bps)
+{
+ /* TODO: Currently limited to table of index 0. */
+ return acpi_nhlt_tb_find_fmtcfg(acpi_gbl_nhlt, link_type, dev_type, dir, bus_id,
+ ch, rate, vbps, bps);
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_find_fmtcfg);
+
+static bool acpi_nhlt_config_is_micdevice(struct acpi_nhlt_config *cfg)
+{
+ return cfg->capabilities_size >= sizeof(struct acpi_nhlt_micdevice_config);
+}
+
+static bool acpi_nhlt_config_is_vendor_micdevice(struct acpi_nhlt_config *cfg)
+{
+ struct acpi_nhlt_vendor_micdevice_config *devcfg = __acpi_nhlt_config_caps(cfg);
+
+ return cfg->capabilities_size >= sizeof(*devcfg) &&
+ cfg->capabilities_size == struct_size(devcfg, mics, devcfg->mics_count);
+}
+
+/**
+ * acpi_nhlt_endpoint_mic_count - Retrieve number of digital microphones for a PDM endpoint.
+ * @ep: the endpoint to return microphones count for.
+ *
+ * Return: A number of microphones or an error code if an invalid endpoint is provided.
+ */
+int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt_endpoint *ep)
+{
+ union acpi_nhlt_device_config *devcfg;
+ struct acpi_nhlt_format_config *fmt;
+ struct acpi_nhlt_config *cfg;
+ u16 max_ch = 0;
+
+ if (!ep || ep->link_type != ACPI_NHLT_LINKTYPE_PDM)
+ return -EINVAL;
+
+ /* Find max number of channels based on formats configuration. */
+ for_each_nhlt_endpoint_fmtcfg(ep, fmt)
+ max_ch = max(fmt->format.channel_count, max_ch);
+
+ cfg = __acpi_nhlt_endpoint_config(ep);
+ devcfg = __acpi_nhlt_config_caps(cfg);
+
+ /* If @ep is not a mic array, fallback to channels count. */
+ if (!acpi_nhlt_config_is_micdevice(cfg) ||
+ devcfg->gen.config_type != ACPI_NHLT_CONFIGTYPE_MICARRAY)
+ return max_ch;
+
+ switch (devcfg->mic.array_type) {
+ case ACPI_NHLT_ARRAYTYPE_LINEAR2_SMALL:
+ case ACPI_NHLT_ARRAYTYPE_LINEAR2_BIG:
+ return 2;
+
+ case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO1:
+ case ACPI_NHLT_ARRAYTYPE_PLANAR4_LSHAPED:
+ case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO2:
+ return 4;
+
+ case ACPI_NHLT_ARRAYTYPE_VENDOR:
+ if (!acpi_nhlt_config_is_vendor_micdevice(cfg))
+ return -EINVAL;
+ return devcfg->vendor_mic.mics_count;
+
+ default:
+ pr_warn("undefined mic array type: %#x\n", devcfg->mic.array_type);
+ return max_ch;
+ }
+}
+EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_mic_count);
diff --git a/drivers/acpi/numa/Kconfig b/drivers/acpi/numa/Kconfig
index 39b1f34c21df..f33194d1e43f 100644
--- a/drivers/acpi/numa/Kconfig
+++ b/drivers/acpi/numa/Kconfig
@@ -1,9 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
config ACPI_NUMA
- bool "NUMA support"
- depends on NUMA
- depends on (X86 || IA64 || ARM64 || LOONGARCH)
- default y if IA64 || ARM64
+ def_bool NUMA && !X86
config ACPI_HMAT
bool "ACPI Heterogeneous Memory Attribute Table Support"
diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c
index c3d783aca196..77a81627aaef 100644
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -9,7 +9,6 @@
*/
#define pr_fmt(fmt) "acpi/hmat: " fmt
-#define dev_fmt(fmt) "acpi/hmat: " fmt
#include <linux/acpi.h>
#include <linux/bitops.h>
@@ -25,6 +24,7 @@
#include <linux/node.h>
#include <linux/sysfs.h>
#include <linux/dax.h>
+#include <linux/memory-tiers.h>
static u8 hmat_revision;
static int hmat_disable __initdata;
@@ -58,14 +58,21 @@ struct target_cache {
struct node_cache_attrs cache_attrs;
};
+enum {
+ NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL = ACCESS_COORDINATE_MAX,
+ NODE_ACCESS_CLASS_GENPORT_SINK_CPU,
+ NODE_ACCESS_CLASS_MAX,
+};
+
struct memory_target {
struct list_head node;
unsigned int memory_pxm;
unsigned int processor_pxm;
struct resource memregions;
- struct node_hmem_attrs hmem_attrs[2];
+ struct access_coordinate coord[NODE_ACCESS_CLASS_MAX];
struct list_head caches;
struct node_cache_attrs cache_attrs;
+ u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE];
bool registered;
};
@@ -100,6 +107,90 @@ static struct memory_target *find_mem_target(unsigned int mem_pxm)
return NULL;
}
+/**
+ * hmat_get_extended_linear_cache_size - Retrieve the extended linear cache size
+ * @backing_res: resource from the backing media
+ * @nid: node id for the memory region
+ * @cache_size: (Output) size of extended linear cache.
+ *
+ * Return: 0 on success. Errno on failure.
+ *
+ */
+int hmat_get_extended_linear_cache_size(struct resource *backing_res, int nid,
+ resource_size_t *cache_size)
+{
+ unsigned int pxm = node_to_pxm(nid);
+ struct memory_target *target;
+ struct target_cache *tcache;
+ struct resource *res;
+
+ target = find_mem_target(pxm);
+ if (!target)
+ return -ENOENT;
+
+ list_for_each_entry(tcache, &target->caches, node) {
+ if (tcache->cache_attrs.address_mode !=
+ NODE_CACHE_ADDR_MODE_EXTENDED_LINEAR)
+ continue;
+
+ res = &target->memregions;
+ if (!resource_contains(res, backing_res))
+ continue;
+
+ *cache_size = tcache->cache_attrs.size;
+ return 0;
+ }
+
+ *cache_size = 0;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(hmat_get_extended_linear_cache_size, "CXL");
+
+static struct memory_target *acpi_find_genport_target(u32 uid)
+{
+ struct memory_target *target;
+ u32 target_uid;
+ u8 *uid_ptr;
+
+ list_for_each_entry(target, &targets, node) {
+ uid_ptr = target->gen_port_device_handle + 8;
+ target_uid = *(u32 *)uid_ptr;
+ if (uid == target_uid)
+ return target;
+ }
+
+ return NULL;
+}
+
+/**
+ * acpi_get_genport_coordinates - Retrieve the access coordinates for a generic port
+ * @uid: ACPI unique id
+ * @coord: The access coordinates written back out for the generic port.
+ * Expect 2 levels array.
+ *
+ * Return: 0 on success. Errno on failure.
+ *
+ * Only supports device handles that are ACPI. Assume ACPI0016 HID for CXL.
+ */
+int acpi_get_genport_coordinates(u32 uid,
+ struct access_coordinate *coord)
+{
+ struct memory_target *target;
+
+ guard(mutex)(&target_lock);
+ target = acpi_find_genport_target(uid);
+ if (!target)
+ return -ENOENT;
+
+ coord[ACCESS_COORDINATE_LOCAL] =
+ target->coord[NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL];
+ coord[ACCESS_COORDINATE_CPU] =
+ target->coord[NODE_ACCESS_CLASS_GENPORT_SINK_CPU];
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acpi_get_genport_coordinates, "CXL");
+
static __init void alloc_memory_initiator(unsigned int cpu_pxm)
{
struct memory_initiator *initiator;
@@ -120,8 +211,7 @@ static __init void alloc_memory_initiator(unsigned int cpu_pxm)
list_add_tail(&initiator->node, &initiators);
}
-static __init void alloc_memory_target(unsigned int mem_pxm,
- resource_size_t start, resource_size_t len)
+static __init struct memory_target *alloc_target(unsigned int mem_pxm)
{
struct memory_target *target;
@@ -129,7 +219,7 @@ static __init void alloc_memory_target(unsigned int mem_pxm,
if (!target) {
target = kzalloc(sizeof(*target), GFP_KERNEL);
if (!target)
- return;
+ return NULL;
target->memory_pxm = mem_pxm;
target->processor_pxm = PXM_INVAL;
target->memregions = (struct resource) {
@@ -142,6 +232,19 @@ static __init void alloc_memory_target(unsigned int mem_pxm,
INIT_LIST_HEAD(&target->caches);
}
+ return target;
+}
+
+static __init void alloc_memory_target(unsigned int mem_pxm,
+ resource_size_t start,
+ resource_size_t len)
+{
+ struct memory_target *target;
+
+ target = alloc_target(mem_pxm);
+ if (!target)
+ return;
+
/*
* There are potentially multiple ranges per PXM, so record each
* in the per-target memregions resource tree.
@@ -152,6 +255,18 @@ static __init void alloc_memory_target(unsigned int mem_pxm,
start, start + len, mem_pxm);
}
+static __init void alloc_genport_target(unsigned int mem_pxm, u8 *handle)
+{
+ struct memory_target *target;
+
+ target = alloc_target(mem_pxm);
+ if (!target)
+ return;
+
+ memcpy(target->gen_port_device_handle, handle,
+ ACPI_SRAT_DEVICE_HANDLE_SIZE);
+}
+
static __init const char *hmat_data_type(u8 type)
{
switch (type) {
@@ -228,24 +343,24 @@ static void hmat_update_target_access(struct memory_target *target,
{
switch (type) {
case ACPI_HMAT_ACCESS_LATENCY:
- target->hmem_attrs[access].read_latency = value;
- target->hmem_attrs[access].write_latency = value;
+ target->coord[access].read_latency = value;
+ target->coord[access].write_latency = value;
break;
case ACPI_HMAT_READ_LATENCY:
- target->hmem_attrs[access].read_latency = value;
+ target->coord[access].read_latency = value;
break;
case ACPI_HMAT_WRITE_LATENCY:
- target->hmem_attrs[access].write_latency = value;
+ target->coord[access].write_latency = value;
break;
case ACPI_HMAT_ACCESS_BANDWIDTH:
- target->hmem_attrs[access].read_bandwidth = value;
- target->hmem_attrs[access].write_bandwidth = value;
+ target->coord[access].read_bandwidth = value;
+ target->coord[access].write_bandwidth = value;
break;
case ACPI_HMAT_READ_BANDWIDTH:
- target->hmem_attrs[access].read_bandwidth = value;
+ target->coord[access].read_bandwidth = value;
break;
case ACPI_HMAT_WRITE_BANDWIDTH:
- target->hmem_attrs[access].write_bandwidth = value;
+ target->coord[access].write_bandwidth = value;
break;
default:
break;
@@ -291,18 +406,35 @@ static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
}
}
+static __init void hmat_update_target(unsigned int tgt_pxm, unsigned int init_pxm,
+ u8 mem_hier, u8 type, u32 value)
+{
+ struct memory_target *target = find_mem_target(tgt_pxm);
+
+ if (mem_hier != ACPI_HMAT_MEMORY)
+ return;
+
+ if (target && target->processor_pxm == init_pxm) {
+ hmat_update_target_access(target, type, value,
+ ACCESS_COORDINATE_LOCAL);
+ /* If the node has a CPU, update access ACCESS_COORDINATE_CPU */
+ if (node_state(pxm_to_node(init_pxm), N_CPU))
+ hmat_update_target_access(target, type, value,
+ ACCESS_COORDINATE_CPU);
+ }
+}
+
static __init int hmat_parse_locality(union acpi_subtable_headers *header,
const unsigned long end)
{
struct acpi_hmat_locality *hmat_loc = (void *)header;
- struct memory_target *target;
unsigned int init, targ, total_size, ipds, tpds;
u32 *inits, *targs, value;
u16 *entries;
u8 type, mem_hier;
if (hmat_loc->header.length < sizeof(*hmat_loc)) {
- pr_notice("HMAT: Unexpected locality header length: %u\n",
+ pr_notice("Unexpected locality header length: %u\n",
hmat_loc->header.length);
return -EINVAL;
}
@@ -314,14 +446,14 @@ static __init int hmat_parse_locality(union acpi_subtable_headers *header,
total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
sizeof(*inits) * ipds + sizeof(*targs) * tpds;
if (hmat_loc->header.length < total_size) {
- pr_notice("HMAT: Unexpected locality header length:%u, minimum required:%u\n",
+ pr_notice("Unexpected locality header length:%u, minimum required:%u\n",
hmat_loc->header.length, total_size);
return -EINVAL;
}
- pr_info("HMAT: Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n",
- hmat_loc->flags, hmat_data_type(type), ipds, tpds,
- hmat_loc->entry_base_unit);
+ pr_debug("Locality: Flags:%02x Type:%s Initiator Domains:%u Target Domains:%u Base:%lld\n",
+ hmat_loc->flags, hmat_data_type(type), ipds, tpds,
+ hmat_loc->entry_base_unit);
inits = (u32 *)(hmat_loc + 1);
targs = inits + ipds;
@@ -332,19 +464,12 @@ static __init int hmat_parse_locality(union acpi_subtable_headers *header,
value = hmat_normalize(entries[init * tpds + targ],
hmat_loc->entry_base_unit,
type);
- pr_info(" Initiator-Target[%u-%u]:%u%s\n",
- inits[init], targs[targ], value,
- hmat_data_type_suffix(type));
-
- if (mem_hier == ACPI_HMAT_MEMORY) {
- target = find_mem_target(targs[targ]);
- if (target && target->processor_pxm == inits[init]) {
- hmat_update_target_access(target, type, value, 0);
- /* If the node has a CPU, update access 1 */
- if (node_state(pxm_to_node(inits[init]), N_CPU))
- hmat_update_target_access(target, type, value, 1);
- }
- }
+ pr_debug(" Initiator-Target[%u-%u]:%u%s\n",
+ inits[init], targs[targ], value,
+ hmat_data_type_suffix(type));
+
+ hmat_update_target(targs[targ], inits[init],
+ mem_hier, type, value);
}
}
@@ -363,15 +488,15 @@ static __init int hmat_parse_cache(union acpi_subtable_headers *header,
u32 attrs;
if (cache->header.length < sizeof(*cache)) {
- pr_notice("HMAT: Unexpected cache header length: %u\n",
+ pr_notice("Unexpected cache header length: %u\n",
cache->header.length);
return -EINVAL;
}
attrs = cache->cache_attributes;
- pr_info("HMAT: Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
- cache->memory_PD, cache->cache_size, attrs,
- cache->number_of_SMBIOShandles);
+ pr_debug("Cache: Domain:%u Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
+ cache->memory_PD, cache->cache_size, attrs,
+ cache->number_of_SMBIOShandles);
target = find_mem_target(cache->memory_PD);
if (!target)
@@ -390,6 +515,11 @@ static __init int hmat_parse_cache(union acpi_subtable_headers *header,
switch ((attrs & ACPI_HMAT_CACHE_ASSOCIATIVITY) >> 8) {
case ACPI_HMAT_CA_DIRECT_MAPPED:
tcache->cache_attrs.indexing = NODE_CACHE_DIRECT_MAP;
+ /* Extended Linear mode is only valid if cache is direct mapped */
+ if (cache->address_mode == ACPI_HMAT_CACHE_MODE_EXTENDED_LINEAR) {
+ tcache->cache_attrs.address_mode =
+ NODE_CACHE_ADDR_MODE_EXTENDED_LINEAR;
+ }
break;
case ACPI_HMAT_CA_COMPLEX_CACHE_INDEXING:
tcache->cache_attrs.indexing = NODE_CACHE_INDEXED;
@@ -424,24 +554,24 @@ static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *heade
struct memory_target *target = NULL;
if (p->header.length != sizeof(*p)) {
- pr_notice("HMAT: Unexpected address range header length: %u\n",
+ pr_notice("Unexpected address range header length: %u\n",
p->header.length);
return -EINVAL;
}
if (hmat_revision == 1)
- pr_info("HMAT: Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n",
- p->reserved3, p->reserved4, p->flags, p->processor_PD,
- p->memory_PD);
+ pr_debug("Memory (%#llx length %#llx) Flags:%04x Processor Domain:%u Memory Domain:%u\n",
+ p->reserved3, p->reserved4, p->flags, p->processor_PD,
+ p->memory_PD);
else
- pr_info("HMAT: Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n",
+ pr_info("Memory Flags:%04x Processor Domain:%u Memory Domain:%u\n",
p->flags, p->processor_PD, p->memory_PD);
if ((hmat_revision == 1 && p->flags & ACPI_HMAT_MEMORY_PD_VALID) ||
hmat_revision > 1) {
target = find_mem_target(p->memory_PD);
if (!target) {
- pr_debug("HMAT: Memory Domain missing from SRAT\n");
+ pr_debug("Memory Domain missing from SRAT\n");
return -EINVAL;
}
}
@@ -449,7 +579,7 @@ static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *heade
int p_node = pxm_to_node(p->processor_PD);
if (p_node == NUMA_NO_NODE) {
- pr_debug("HMAT: Invalid Processor Domain\n");
+ pr_debug("Invalid Processor Domain\n");
return -EINVAL;
}
target->processor_pxm = p->processor_PD;
@@ -491,6 +621,27 @@ static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header,
return 0;
}
+static __init int srat_parse_genport_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_srat_generic_affinity *ga = (void *)header;
+
+ if (!ga)
+ return -EINVAL;
+
+ if (!(ga->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED))
+ return 0;
+
+ /* Skip PCI device_handle for now */
+ if (ga->device_handle_type != 0)
+ return 0;
+
+ alloc_genport_target(ga->proximity_domain,
+ (u8 *)ga->device_handle);
+
+ return 0;
+}
+
static u32 hmat_initiator_perf(struct memory_target *target,
struct memory_initiator *initiator,
struct acpi_hmat_locality *hmat_loc)
@@ -563,39 +714,52 @@ static int initiator_cmp(void *priv, const struct list_head *a,
{
struct memory_initiator *ia;
struct memory_initiator *ib;
- unsigned long *p_nodes = priv;
ia = list_entry(a, struct memory_initiator, node);
ib = list_entry(b, struct memory_initiator, node);
- set_bit(ia->processor_pxm, p_nodes);
- set_bit(ib->processor_pxm, p_nodes);
-
return ia->processor_pxm - ib->processor_pxm;
}
-static void hmat_register_target_initiators(struct memory_target *target)
+static int initiators_to_nodemask(unsigned long *p_nodes)
{
- static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
struct memory_initiator *initiator;
- unsigned int mem_nid, cpu_nid;
+
+ if (list_empty(&initiators))
+ return -ENXIO;
+
+ list_for_each_entry(initiator, &initiators, node)
+ set_bit(initiator->processor_pxm, p_nodes);
+
+ return 0;
+}
+
+static void hmat_update_target_attrs(struct memory_target *target,
+ unsigned long *p_nodes, int access)
+{
+ struct memory_initiator *initiator;
+ unsigned int cpu_nid;
struct memory_locality *loc = NULL;
u32 best = 0;
- bool access0done = false;
int i;
- mem_nid = pxm_to_node(target->memory_pxm);
+ /* Don't update for generic port if there's no device handle */
+ if ((access == NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL ||
+ access == NODE_ACCESS_CLASS_GENPORT_SINK_CPU) &&
+ !(*(u16 *)target->gen_port_device_handle))
+ return;
+
+ bitmap_zero(p_nodes, MAX_NUMNODES);
/*
- * If the Address Range Structure provides a local processor pxm, link
+ * If the Address Range Structure provides a local processor pxm, set
* only that one. Otherwise, find the best performance attributes and
- * register all initiators that match.
+ * collect all initiators that match.
*/
if (target->processor_pxm != PXM_INVAL) {
cpu_nid = pxm_to_node(target->processor_pxm);
- register_memory_node_under_compute_node(mem_nid, cpu_nid, 0);
- access0done = true;
- if (node_state(cpu_nid, N_CPU)) {
- register_memory_node_under_compute_node(mem_nid, cpu_nid, 1);
+ if (access == ACCESS_COORDINATE_LOCAL ||
+ node_state(cpu_nid, N_CPU)) {
+ set_bit(target->processor_pxm, p_nodes);
return;
}
}
@@ -609,43 +773,10 @@ static void hmat_register_target_initiators(struct memory_target *target)
* We'll also use the sorting to prime the candidate nodes with known
* initiators.
*/
- bitmap_zero(p_nodes, MAX_NUMNODES);
- list_sort(p_nodes, &initiators, initiator_cmp);
- if (!access0done) {
- for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
- loc = localities_types[i];
- if (!loc)
- continue;
-
- best = 0;
- list_for_each_entry(initiator, &initiators, node) {
- u32 value;
-
- if (!test_bit(initiator->processor_pxm, p_nodes))
- continue;
-
- value = hmat_initiator_perf(target, initiator,
- loc->hmat_loc);
- if (hmat_update_best(loc->hmat_loc->data_type, value, &best))
- bitmap_clear(p_nodes, 0, initiator->processor_pxm);
- if (value != best)
- clear_bit(initiator->processor_pxm, p_nodes);
- }
- if (best)
- hmat_update_target_access(target, loc->hmat_loc->data_type,
- best, 0);
- }
-
- for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
- cpu_nid = pxm_to_node(i);
- register_memory_node_under_compute_node(mem_nid, cpu_nid, 0);
- }
- }
+ list_sort(NULL, &initiators, initiator_cmp);
+ if (initiators_to_nodemask(p_nodes) < 0)
+ return;
- /* Access 1 ignores Generic Initiators */
- bitmap_zero(p_nodes, MAX_NUMNODES);
- list_sort(p_nodes, &initiators, initiator_cmp);
- best = 0;
for (i = WRITE_LATENCY; i <= READ_BANDWIDTH; i++) {
loc = localities_types[i];
if (!loc)
@@ -655,7 +786,9 @@ static void hmat_register_target_initiators(struct memory_target *target)
list_for_each_entry(initiator, &initiators, node) {
u32 value;
- if (!initiator->has_cpu) {
+ if ((access == ACCESS_COORDINATE_CPU ||
+ access == NODE_ACCESS_CLASS_GENPORT_SINK_CPU) &&
+ !initiator->has_cpu) {
clear_bit(initiator->processor_pxm, p_nodes);
continue;
}
@@ -669,14 +802,45 @@ static void hmat_register_target_initiators(struct memory_target *target)
clear_bit(initiator->processor_pxm, p_nodes);
}
if (best)
- hmat_update_target_access(target, loc->hmat_loc->data_type, best, 1);
+ hmat_update_target_access(target, loc->hmat_loc->data_type, best, access);
}
+}
+
+static void __hmat_register_target_initiators(struct memory_target *target,
+ unsigned long *p_nodes,
+ int access)
+{
+ unsigned int mem_nid, cpu_nid;
+ int i;
+
+ mem_nid = pxm_to_node(target->memory_pxm);
+ hmat_update_target_attrs(target, p_nodes, access);
for_each_set_bit(i, p_nodes, MAX_NUMNODES) {
cpu_nid = pxm_to_node(i);
- register_memory_node_under_compute_node(mem_nid, cpu_nid, 1);
+ register_memory_node_under_compute_node(mem_nid, cpu_nid, access);
}
}
+static void hmat_update_generic_target(struct memory_target *target)
+{
+ static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
+
+ hmat_update_target_attrs(target, p_nodes,
+ NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL);
+ hmat_update_target_attrs(target, p_nodes,
+ NODE_ACCESS_CLASS_GENPORT_SINK_CPU);
+}
+
+static void hmat_register_target_initiators(struct memory_target *target)
+{
+ static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
+
+ __hmat_register_target_initiators(target, p_nodes,
+ ACCESS_COORDINATE_LOCAL);
+ __hmat_register_target_initiators(target, p_nodes,
+ ACCESS_COORDINATE_CPU);
+}
+
static void hmat_register_target_cache(struct memory_target *target)
{
unsigned mem_nid = pxm_to_node(target->memory_pxm);
@@ -689,7 +853,7 @@ static void hmat_register_target_cache(struct memory_target *target)
static void hmat_register_target_perf(struct memory_target *target, int access)
{
unsigned mem_nid = pxm_to_node(target->memory_pxm);
- node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access);
+ node_set_perf_attrs(mem_nid, &target->coord[access], access);
}
static void hmat_register_target_devices(struct memory_target *target)
@@ -706,39 +870,55 @@ static void hmat_register_target_devices(struct memory_target *target)
for (res = target->memregions.child; res; res = res->sibling) {
int target_nid = pxm_to_node(target->memory_pxm);
- hmem_register_device(target_nid, res);
+ hmem_register_resource(target_nid, res);
}
}
-static void hmat_register_target(struct memory_target *target)
+static void hmat_hotplug_target(struct memory_target *target)
{
int nid = pxm_to_node(target->memory_pxm);
/*
+ * Skip offline nodes. This can happen when memory marked EFI_MEMORY_SP,
+ * "specific purpose", is applied to all the memory in a proximity
+ * domain leading to * the node being marked offline / unplugged, or if
+ * memory-only "hotplug" node is offline.
+ */
+ if (nid == NUMA_NO_NODE || !node_online(nid))
+ return;
+
+ guard(mutex)(&target_lock);
+ if (target->registered)
+ return;
+
+ hmat_register_target_initiators(target);
+ hmat_register_target_cache(target);
+ hmat_register_target_perf(target, ACCESS_COORDINATE_LOCAL);
+ hmat_register_target_perf(target, ACCESS_COORDINATE_CPU);
+ target->registered = true;
+}
+
+static void hmat_register_target(struct memory_target *target)
+{
+ /*
* Devices may belong to either an offline or online
* node, so unconditionally add them.
*/
hmat_register_target_devices(target);
/*
- * Skip offline nodes. This can happen when memory
- * marked EFI_MEMORY_SP, "specific purpose", is applied
- * to all the memory in a proximity domain leading to
- * the node being marked offline / unplugged, or if
- * memory-only "hotplug" node is offline.
+ * Register generic port perf numbers. The nid may not be
+ * initialized and is still NUMA_NO_NODE.
*/
- if (nid == NUMA_NO_NODE || !node_online(nid))
- return;
-
- mutex_lock(&target_lock);
- if (!target->registered) {
- hmat_register_target_initiators(target);
- hmat_register_target_cache(target);
- hmat_register_target_perf(target, 0);
- hmat_register_target_perf(target, 1);
- target->registered = true;
+ scoped_guard(mutex, &target_lock) {
+ if (*(u16 *)target->gen_port_device_handle) {
+ hmat_update_generic_target(target);
+ target->registered = true;
+ return;
+ }
}
- mutex_unlock(&target_lock);
+
+ hmat_hotplug_target(target);
}
static void hmat_register_targets(void)
@@ -753,10 +933,10 @@ static int hmat_callback(struct notifier_block *self,
unsigned long action, void *arg)
{
struct memory_target *target;
- struct memory_notify *mnb = arg;
- int pxm, nid = mnb->status_change_nid;
+ struct node_notify *nn = arg;
+ int pxm, nid = nn->nid;
- if (nid == NUMA_NO_NODE || action != MEM_ONLINE)
+ if (action != NODE_ADDED_FIRST_MEMORY)
return NOTIFY_OK;
pxm = node_to_pxm(nid);
@@ -764,13 +944,60 @@ static int hmat_callback(struct notifier_block *self,
if (!target)
return NOTIFY_OK;
- hmat_register_target(target);
+ hmat_hotplug_target(target);
return NOTIFY_OK;
}
-static struct notifier_block hmat_callback_nb = {
- .notifier_call = hmat_callback,
- .priority = 2,
+static int __init hmat_set_default_dram_perf(void)
+{
+ int rc;
+ int nid, pxm;
+ struct memory_target *target;
+ struct access_coordinate *attrs;
+
+ for_each_node_mask(nid, default_dram_nodes) {
+ pxm = node_to_pxm(nid);
+ target = find_mem_target(pxm);
+ if (!target)
+ continue;
+ attrs = &target->coord[ACCESS_COORDINATE_CPU];
+ rc = mt_set_default_dram_perf(nid, attrs, "ACPI HMAT");
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int hmat_calculate_adistance(struct notifier_block *self,
+ unsigned long nid, void *data)
+{
+ static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
+ struct memory_target *target;
+ struct access_coordinate *perf;
+ int *adist = data;
+ int pxm;
+
+ pxm = node_to_pxm(nid);
+ target = find_mem_target(pxm);
+ if (!target)
+ return NOTIFY_OK;
+
+ mutex_lock(&target_lock);
+ hmat_update_target_attrs(target, p_nodes, ACCESS_COORDINATE_CPU);
+ mutex_unlock(&target_lock);
+
+ perf = &target->coord[ACCESS_COORDINATE_CPU];
+
+ if (mt_perf_to_adistance(perf, adist))
+ return NOTIFY_OK;
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block hmat_adist_nb __meminitdata = {
+ .notifier_call = hmat_calculate_adistance,
+ .priority = 100,
};
static __init void hmat_free_structures(void)
@@ -828,6 +1055,13 @@ static __init int hmat_init(void)
ACPI_SRAT_TYPE_MEMORY_AFFINITY,
srat_parse_mem_affinity, 0) < 0)
goto out_put;
+
+ if (acpi_table_parse_entries(ACPI_SIG_SRAT,
+ sizeof(struct acpi_table_srat),
+ ACPI_SRAT_TYPE_GENERIC_PORT_AFFINITY,
+ srat_parse_genport_affinity, 0) < 0)
+ goto out_put;
+
acpi_put_table(tbl);
status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
@@ -840,7 +1074,7 @@ static __init int hmat_init(void)
case 2:
break;
default:
- pr_notice("Ignoring HMAT: Unknown revision:%d\n", hmat_revision);
+ pr_notice("Ignoring: Unknown revision:%d\n", hmat_revision);
goto out_put;
}
@@ -848,18 +1082,23 @@ static __init int hmat_init(void)
if (acpi_table_parse_entries(ACPI_SIG_HMAT,
sizeof(struct acpi_table_hmat), i,
hmat_parse_subtable, 0) < 0) {
- pr_notice("Ignoring HMAT: Invalid table");
+ pr_notice("Ignoring: Invalid table");
goto out_put;
}
}
hmat_register_targets();
/* Keep the table and structures if the notifier may use them */
- if (!register_hotmemory_notifier(&hmat_callback_nb))
- return 0;
+ if (hotplug_node_notifier(hmat_callback, HMAT_CALLBACK_PRI))
+ goto out_put;
+
+ if (!hmat_set_default_dram_perf())
+ register_mt_adistance_algorithm(&hmat_adist_nb);
+
+ return 0;
out_put:
hmat_free_structures();
acpi_put_table(tbl);
return 0;
}
-device_initcall(hmat_init);
+subsys_initcall(hmat_init);
diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c
index 3b818ab186be..aa87ee1583a4 100644
--- a/drivers/acpi/numa/srat.c
+++ b/drivers/acpi/numa/srat.c
@@ -14,9 +14,12 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/memblock.h>
+#include <linux/memory.h>
#include <linux/numa.h>
#include <linux/nodemask.h>
#include <linux/topology.h>
+#include <linux/numa_memblks.h>
+#include <linux/string_choices.h>
static nodemask_t nodes_found_map = NODE_MASK_NONE;
@@ -29,6 +32,8 @@ static int node_to_pxm_map[MAX_NUMNODES]
unsigned char acpi_srat_revision __initdata;
static int acpi_numa __initdata;
+static int last_real_pxm;
+
void __init disable_srat(void)
{
acpi_numa = -1;
@@ -48,6 +53,7 @@ int node_to_pxm(int node)
return PXM_INVAL;
return node_to_pxm_map[node];
}
+EXPORT_SYMBOL_GPL(node_to_pxm);
static void __acpi_map_pxm_to_node(int pxm, int node)
{
@@ -67,9 +73,9 @@ int acpi_map_pxm_to_node(int pxm)
node = pxm_to_node_map[pxm];
if (node == NUMA_NO_NODE) {
- if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
- return NUMA_NO_NODE;
node = first_unset_node(nodes_found_map);
+ if (node >= MAX_NUMNODES)
+ return NUMA_NO_NODE;
__acpi_map_pxm_to_node(pxm, node);
node_set(node, nodes_found_map);
}
@@ -78,6 +84,101 @@ int acpi_map_pxm_to_node(int pxm)
}
EXPORT_SYMBOL(acpi_map_pxm_to_node);
+#ifdef CONFIG_NUMA_EMU
+/*
+ * Take max_nid - 1 fake-numa nodes into account in both
+ * pxm_to_node_map()/node_to_pxm_map[] tables.
+ */
+int __init fix_pxm_node_maps(int max_nid)
+{
+ static int pxm_to_node_map_copy[MAX_PXM_DOMAINS] __initdata
+ = { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE };
+ static int node_to_pxm_map_copy[MAX_NUMNODES] __initdata
+ = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
+ int i, j, index = -1, count = 0;
+ nodemask_t nodes_to_enable;
+
+ if (numa_off)
+ return -1;
+
+ /* no or incomplete node/PXM mapping set, nothing to do */
+ if (srat_disabled())
+ return 0;
+
+ /* find fake nodes PXM mapping */
+ for (i = 0; i < MAX_NUMNODES; i++) {
+ if (node_to_pxm_map[i] != PXM_INVAL) {
+ for (j = 0; j <= max_nid; j++) {
+ if ((emu_nid_to_phys[j] == i) &&
+ WARN(node_to_pxm_map_copy[j] != PXM_INVAL,
+ "Node %d is already binded to PXM %d\n",
+ j, node_to_pxm_map_copy[j]))
+ return -1;
+ if (emu_nid_to_phys[j] == i) {
+ node_to_pxm_map_copy[j] =
+ node_to_pxm_map[i];
+ if (j > index)
+ index = j;
+ count++;
+ }
+ }
+ }
+ }
+ if (index == -1) {
+ pr_debug("No node/PXM mapping has been set\n");
+ /* nothing more to be done */
+ return 0;
+ }
+ if (WARN(index != max_nid, "%d max nid when expected %d\n",
+ index, max_nid))
+ return -1;
+
+ nodes_clear(nodes_to_enable);
+
+ /* map phys nodes not used for fake nodes */
+ for (i = 0; i < MAX_NUMNODES; i++) {
+ if (node_to_pxm_map[i] != PXM_INVAL) {
+ for (j = 0; j <= max_nid; j++)
+ if (emu_nid_to_phys[j] == i)
+ break;
+ /* fake nodes PXM mapping has been done */
+ if (j <= max_nid)
+ continue;
+ /* find first hole */
+ for (j = 0;
+ j < MAX_NUMNODES &&
+ node_to_pxm_map_copy[j] != PXM_INVAL;
+ j++)
+ ;
+ if (WARN(j == MAX_NUMNODES,
+ "Number of nodes exceeds MAX_NUMNODES\n"))
+ return -1;
+ node_to_pxm_map_copy[j] = node_to_pxm_map[i];
+ node_set(j, nodes_to_enable);
+ count++;
+ }
+ }
+
+ /* creating reverse mapping in pxm_to_node_map[] */
+ for (i = 0; i < MAX_NUMNODES; i++)
+ if (node_to_pxm_map_copy[i] != PXM_INVAL &&
+ pxm_to_node_map_copy[node_to_pxm_map_copy[i]] == NUMA_NO_NODE)
+ pxm_to_node_map_copy[node_to_pxm_map_copy[i]] = i;
+
+ /* overwrite with new mapping */
+ for (i = 0; i < MAX_NUMNODES; i++) {
+ node_to_pxm_map[i] = node_to_pxm_map_copy[i];
+ pxm_to_node_map[i] = pxm_to_node_map_copy[i];
+ }
+
+ /* enable other nodes found in PXM for hotplug */
+ nodes_or(numa_nodes_parsed, nodes_to_enable, numa_nodes_parsed);
+
+ pr_debug("found %d total number of nodes\n", count);
+ return 0;
+}
+#endif
+
static void __init
acpi_table_print_srat_entry(struct acpi_subtable_header *header)
{
@@ -89,8 +190,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
p->apic_id, p->local_sapic_eid,
p->proximity_domain_lo,
- (p->flags & ACPI_SRAT_CPU_ENABLED) ?
- "enabled" : "disabled");
+ str_enabled_disabled(p->flags & ACPI_SRAT_CPU_ENABLED));
}
break;
@@ -102,8 +202,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
(unsigned long long)p->base_address,
(unsigned long long)p->length,
p->proximity_domain,
- (p->flags & ACPI_SRAT_MEM_ENABLED) ?
- "enabled" : "disabled",
+ str_enabled_disabled(p->flags & ACPI_SRAT_MEM_ENABLED),
(p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ?
" hot-pluggable" : "",
(p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ?
@@ -118,8 +217,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n",
p->apic_id,
p->proximity_domain,
- (p->flags & ACPI_SRAT_CPU_ENABLED) ?
- "enabled" : "disabled");
+ str_enabled_disabled(p->flags & ACPI_SRAT_CPU_ENABLED));
}
break;
@@ -130,8 +228,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n",
p->acpi_processor_uid,
p->proximity_domain,
- (p->flags & ACPI_SRAT_GICC_ENABLED) ?
- "enabled" : "disabled");
+ str_enabled_disabled(p->flags & ACPI_SRAT_GICC_ENABLED));
}
break;
@@ -140,7 +237,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
struct acpi_srat_generic_affinity *p =
(struct acpi_srat_generic_affinity *)header;
- if (p->device_handle_type == 0) {
+ if (p->device_handle_type == 1) {
/*
* For pci devices this may be the only place they
* are assigned a proximity domain
@@ -149,8 +246,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
*(u16 *)(&p->device_handle[0]),
*(u16 *)(&p->device_handle[2]),
p->proximity_domain,
- (p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
- "enabled" : "disabled");
+ str_enabled_disabled(p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED));
} else {
/*
* In this case we can rely on the device having a
@@ -160,11 +256,22 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
(char *)(&p->device_handle[0]),
(char *)(&p->device_handle[8]),
p->proximity_domain,
- (p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
- "enabled" : "disabled");
+ str_enabled_disabled(p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED));
}
}
break;
+
+ case ACPI_SRAT_TYPE_RINTC_AFFINITY:
+ {
+ struct acpi_srat_rintc_affinity *p =
+ (struct acpi_srat_rintc_affinity *)header;
+ pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n",
+ p->acpi_processor_uid,
+ p->proximity_domain,
+ str_enabled_disabled(p->flags & ACPI_SRAT_RINTC_ENABLED));
+ }
+ break;
+
default:
pr_warn("Found unsupported SRAT entry (type = 0x%x)\n",
header->type);
@@ -183,7 +290,7 @@ static int __init slit_valid(struct acpi_table_slit *slit)
int i, j;
int d = slit->locality_count;
for (i = 0; i < d; i++) {
- for (j = 0; j < d; j++) {
+ for (j = 0; j < d; j++) {
u8 val = slit->entry[d*i + j];
if (i == j) {
if (val != LOCAL_DISTANCE)
@@ -206,16 +313,26 @@ int __init srat_disabled(void)
return acpi_numa < 0;
}
-#if defined(CONFIG_X86) || defined(CONFIG_ARM64) || defined(CONFIG_LOONGARCH)
+__weak int __init numa_fill_memblks(u64 start, u64 end)
+{
+ return NUMA_NO_MEMBLK;
+}
+
/*
* Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for
* I/O localities since SRAT does not list them. I/O localities are
* not supported at this point.
*/
-void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
+static int __init acpi_parse_slit(struct acpi_table_header *table)
{
+ struct acpi_table_slit *slit = (struct acpi_table_slit *)table;
int i, j;
+ if (!slit_valid(slit)) {
+ pr_info("SLIT table looks invalid. Not used.\n");
+ return -EINVAL;
+ }
+
for (i = 0; i < slit->locality_count; i++) {
const int from_node = pxm_to_node(i);
@@ -232,28 +349,34 @@ void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
slit->entry[slit->locality_count * i + j]);
}
}
+
+ return 0;
}
-/*
- * Default callback for parsing of the Proximity Domain <-> Memory
- * Area mappings
- */
-int __init
-acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
+static int parsed_numa_memblks __initdata;
+
+static int __init
+acpi_parse_memory_affinity(union acpi_subtable_headers *header,
+ const unsigned long table_end)
{
+ struct acpi_srat_mem_affinity *ma;
u64 start, end;
u32 hotpluggable;
int node, pxm;
+ ma = (struct acpi_srat_mem_affinity *)header;
+
+ acpi_table_print_srat_entry(&header->common);
+
if (srat_disabled())
- goto out_err;
+ return 0;
if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) {
pr_err("SRAT: Unexpected header length: %d\n",
ma->header.length);
goto out_err_bad_srat;
}
if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
- goto out_err;
+ return 0;
hotpluggable = IS_ENABLED(CONFIG_MEMORY_HOTPLUG) &&
(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE);
@@ -291,11 +414,15 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
+ parsed_numa_memblks++;
+
return 0;
+
out_err_bad_srat:
+ /* Just disable SRAT, but do not fail and ignore errors. */
bad_srat();
-out_err:
- return -EINVAL;
+
+ return 0;
}
static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
@@ -303,18 +430,33 @@ static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
{
struct acpi_cedt_cfmws *cfmws;
int *fake_pxm = arg;
- u64 start, end;
+ u64 start, end, align;
int node;
+ int err;
cfmws = (struct acpi_cedt_cfmws *)header;
start = cfmws->base_hpa;
end = cfmws->base_hpa + cfmws->window_size;
- /* Skip if the SRAT already described the NUMA details for this HPA */
- node = phys_to_target_node(start);
- if (node != NUMA_NO_NODE)
+ /* Align memblock size to CFMW regions if possible */
+ align = 1UL << __ffs(start | end);
+ if (align >= SZ_256M) {
+ err = memory_block_advise_max_size(align);
+ if (err)
+ pr_warn("CFMWS: memblock size advise failed (%d)\n", err);
+ } else
+ pr_err("CFMWS: [BIOS BUG] base/size alignment violates spec\n");
+
+ /*
+ * The SRAT may have already described NUMA details for all,
+ * or a portion of, this CFMWS HPA range. Extend the memblks
+ * found for any portion of the window to cover the entire
+ * window.
+ */
+ if (!numa_fill_memblks(start, end))
return 0;
+ /* No SRAT description. Create a new node. */
node = acpi_map_pxm_to_node(*fake_pxm);
if (node == NUMA_NO_NODE) {
@@ -322,36 +464,17 @@ static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
return -EINVAL;
}
- if (numa_add_memblk(node, start, end) < 0) {
+ if (numa_add_reserved_memblk(node, start, end) < 0) {
/* CXL driver must handle the NUMA_NO_NODE case */
pr_warn("ACPI NUMA: Failed to add memblk for CFMWS node %d [mem %#llx-%#llx]\n",
node, start, end);
}
+ node_set(node, numa_nodes_parsed);
/* Set the next available fake_pxm value */
(*fake_pxm)++;
return 0;
}
-#else
-static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
- void *arg, const unsigned long table_end)
-{
- return 0;
-}
-#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
-
-static int __init acpi_parse_slit(struct acpi_table_header *table)
-{
- struct acpi_table_slit *slit = (struct acpi_table_slit *)table;
-
- if (!slit_valid(slit)) {
- pr_info("SLIT table looks invalid. Not used.\n");
- return -EINVAL;
- }
- acpi_numa_slit_init(slit);
-
- return 0;
-}
void __init __weak
acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
@@ -424,7 +547,7 @@ acpi_parse_gi_affinity(union acpi_subtable_headers *header,
return -EINVAL;
node = acpi_map_pxm_to_node(gi_affinity->proximity_domain);
- if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
+ if (node == NUMA_NO_NODE) {
pr_err("SRAT: Too many proximity domains.\n");
return -EINVAL;
}
@@ -442,21 +565,18 @@ acpi_parse_gi_affinity(union acpi_subtable_headers *header,
}
#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
-static int __initdata parsed_numa_memblks;
-
static int __init
-acpi_parse_memory_affinity(union acpi_subtable_headers * header,
- const unsigned long end)
+acpi_parse_rintc_affinity(union acpi_subtable_headers *header,
+ const unsigned long end)
{
- struct acpi_srat_mem_affinity *memory_affinity;
-
- memory_affinity = (struct acpi_srat_mem_affinity *)header;
+ struct acpi_srat_rintc_affinity *rintc_affinity;
+ rintc_affinity = (struct acpi_srat_rintc_affinity *)header;
acpi_table_print_srat_entry(&header->common);
/* let architecture-dependent part to do it */
- if (!acpi_numa_memory_affinity_init(memory_affinity))
- parsed_numa_memblks++;
+ acpi_numa_rintc_affinity_init(rintc_affinity);
+
return 0;
}
@@ -495,7 +615,7 @@ int __init acpi_numa_init(void)
/* SRAT: System Resource Affinity Table */
if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
- struct acpi_subtable_proc srat_proc[4];
+ struct acpi_subtable_proc srat_proc[5];
memset(srat_proc, 0, sizeof(srat_proc));
srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY;
@@ -506,6 +626,8 @@ int __init acpi_numa_init(void)
srat_proc[2].handler = acpi_parse_gicc_affinity;
srat_proc[3].id = ACPI_SRAT_TYPE_GENERIC_AFFINITY;
srat_proc[3].handler = acpi_parse_gi_affinity;
+ srat_proc[4].id = ACPI_SRAT_TYPE_RINTC_AFFINITY;
+ srat_proc[4].handler = acpi_parse_rintc_affinity;
acpi_table_parse_entries_array(ACPI_SIG_SRAT,
sizeof(struct acpi_table_srat),
@@ -526,10 +648,11 @@ int __init acpi_numa_init(void)
*/
/* fake_pxm is the next unused PXM value after SRAT parsing */
- for (i = 0, fake_pxm = -1; i < MAX_NUMNODES - 1; i++) {
+ for (i = 0, fake_pxm = -1; i < MAX_NUMNODES; i++) {
if (node_to_pxm_map[i] > fake_pxm)
fake_pxm = node_to_pxm_map[i];
}
+ last_real_pxm = fake_pxm;
fake_pxm++;
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
&fake_pxm);
@@ -541,6 +664,14 @@ int __init acpi_numa_init(void)
return 0;
}
+bool acpi_node_backed_by_real_pxm(int nid)
+{
+ int pxm = node_to_pxm(nid);
+
+ return pxm <= last_real_pxm;
+}
+EXPORT_SYMBOL_GPL(acpi_node_backed_by_real_pxm);
+
static int acpi_get_pxm(acpi_handle h)
{
unsigned long long pxm;
diff --git a/drivers/acpi/osi.c b/drivers/acpi/osi.c
index 9f6853809138..f2c943b934be 100644
--- a/drivers/acpi/osi.c
+++ b/drivers/acpi/osi.c
@@ -42,32 +42,7 @@ static struct acpi_osi_entry
osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
{"Module Device", true},
{"Processor Device", true},
- {"3.0 _SCP Extensions", true},
{"Processor Aggregator Device", true},
- /*
- * Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
- * cards as RTD3 is not supported by drivers now. Systems with NVidia
- * cards will hang without RTD3 disabled.
- *
- * Once NVidia drivers officially support RTD3, this _OSI strings can
- * be removed if both new and old graphics cards are supported.
- */
- {"Linux-Dell-Video", true},
- /*
- * Linux-Lenovo-NV-HDMI-Audio is used by BIOS to power on NVidia's HDMI
- * audio device which is turned off for power-saving in Windows OS.
- * This power management feature observed on some Lenovo Thinkpad
- * systems which will not be able to output audio via HDMI without
- * a BIOS workaround.
- */
- {"Linux-Lenovo-NV-HDMI-Audio", true},
- /*
- * Linux-HPI-Hybrid-Graphics is used by BIOS to enable dGPU to
- * output video directly to external monitors on HP Inc. mobile
- * workstations as Nvidia and AMD VGA drivers provide limited
- * hybrid graphics supports.
- */
- {"Linux-HPI-Hybrid-Graphics", true},
};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)
@@ -134,7 +109,7 @@ void __init acpi_osi_setup(char *str)
break;
} else if (osi->string[0] == '\0') {
osi->enable = enable;
- strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
+ strscpy(osi->string, str, OSI_STRING_LENGTH_MAX);
break;
}
}
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3269a888fb7a..05393a7315fe 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -149,7 +149,7 @@ void acpi_os_printf(const char *fmt, ...)
}
EXPORT_SYMBOL(acpi_os_printf);
-void acpi_os_vprintf(const char *fmt, va_list args)
+void __printf(1, 0) acpi_os_vprintf(const char *fmt, va_list args)
{
static char buffer[512];
@@ -276,7 +276,7 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
return NULL;
}
-#if defined(CONFIG_IA64) || defined(CONFIG_ARM64)
+#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
/* ioremap will take care of cache attributes */
#define should_use_kmap(pfn) 0
#else
@@ -398,7 +398,7 @@ static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
list_del_rcu(&map->list);
INIT_RCU_WORK(&map->track.rwork, acpi_os_map_remove);
- queue_rcu_work(system_wq, &map->track.rwork);
+ queue_rcu_work(system_percpu_wq, &map->track.rwork);
}
/**
@@ -493,7 +493,7 @@ EXPORT_SYMBOL(acpi_os_unmap_generic_address);
#ifdef ACPI_FUTURE_USAGE
acpi_status
-acpi_os_get_physical_address(void *virt, acpi_physical_address * phys)
+acpi_os_get_physical_address(void *virt, acpi_physical_address *phys)
{
if (!phys || !virt)
return AE_BAD_PARAMETER;
@@ -544,11 +544,7 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
- u32 handled;
-
- handled = (*acpi_irq_handler) (acpi_irq_context);
-
- if (handled) {
+ if ((*acpi_irq_handler)(acpi_irq_context)) {
acpi_irq_handled++;
return IRQ_HANDLED;
} else {
@@ -582,7 +578,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
acpi_irq_handler = handler;
acpi_irq_context = context;
- if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
+ if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED | IRQF_ONESHOT,
+ "acpi", acpi_irq)) {
pr_err("SCI (IRQ%d) allocation failed\n", irq);
acpi_irq_handler = NULL;
return AE_NOT_ACQUIRED;
@@ -610,7 +607,27 @@ acpi_status acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler handler)
void acpi_os_sleep(u64 ms)
{
- msleep(ms);
+ u64 usec = ms * USEC_PER_MSEC, delta_us = 50;
+
+ /*
+ * Use a hrtimer because the timer wheel timers are optimized for
+ * cancelation before they expire and this timer is not going to be
+ * canceled.
+ *
+ * Set the delta between the requested sleep time and the effective
+ * deadline to at least 50 us in case there is an opportunity for timer
+ * coalescing.
+ *
+ * Moreover, longer sleeps can be assumed to need somewhat less timer
+ * precision, so sacrifice some of it for making the timer a more likely
+ * candidate for coalescing by setting the delta to 1% of the sleep time
+ * if it is above 5 ms (this value is chosen so that the delta is a
+ * continuous function of the sleep time).
+ */
+ if (ms > 5)
+ delta_us = (USEC_PER_MSEC / 100) * ms;
+
+ usleep_range(usec, usec + delta_us);
}
void acpi_os_stall(u32 us)
@@ -645,6 +662,15 @@ acpi_status acpi_os_read_port(acpi_io_address port, u32 *value, u32 width)
{
u32 dummy;
+ if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
+ /*
+ * set all-1 result as if reading from non-existing
+ * I/O port
+ */
+ *value = GENMASK(width, 0);
+ return AE_NOT_IMPLEMENTED;
+ }
+
if (value)
*value = 0;
else
@@ -668,6 +694,9 @@ EXPORT_SYMBOL(acpi_os_read_port);
acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
{
+ if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+ return AE_NOT_IMPLEMENTED;
+
if (width <= 8) {
outb(value, port);
} else if (width <= 16) {
@@ -784,7 +813,7 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u64 value, u32 width)
#ifdef CONFIG_PCI
acpi_status
-acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
+acpi_os_read_pci_configuration(struct acpi_pci_id *pci_id, u32 reg,
u64 *value, u32 width)
{
int result, size;
@@ -816,7 +845,7 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
}
acpi_status
-acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
+acpi_os_write_pci_configuration(struct acpi_pci_id *pci_id, u32 reg,
u64 value, u32 width)
{
int result, size;
@@ -1063,10 +1092,9 @@ int __init acpi_debugger_init(void)
acpi_status acpi_os_execute(acpi_execute_type type,
acpi_osd_exec_callback function, void *context)
{
- acpi_status status = AE_OK;
struct acpi_os_dpc *dpc;
- struct workqueue_struct *queue;
int ret;
+
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
@@ -1075,9 +1103,9 @@ acpi_status acpi_os_execute(acpi_execute_type type,
ret = acpi_debugger_create_thread(function, context);
if (ret) {
pr_err("Kernel thread creation failed\n");
- status = AE_ERROR;
+ return AE_ERROR;
}
- goto out_thread;
+ return AE_OK;
}
/*
@@ -1095,43 +1123,41 @@ acpi_status acpi_os_execute(acpi_execute_type type,
dpc->function = function;
dpc->context = context;
+ INIT_WORK(&dpc->work, acpi_os_execute_deferred);
/*
* To prevent lockdep from complaining unnecessarily, make sure that
* there is a different static lockdep key for each workqueue by using
* INIT_WORK() for each of them separately.
*/
- if (type == OSL_NOTIFY_HANDLER) {
- queue = kacpi_notify_wq;
- INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else if (type == OSL_GPE_HANDLER) {
- queue = kacpid_wq;
- INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ switch (type) {
+ case OSL_NOTIFY_HANDLER:
+ ret = queue_work(kacpi_notify_wq, &dpc->work);
+ break;
+ case OSL_GPE_HANDLER:
+ /*
+ * On some machines, a software-initiated SMI causes corruption
+ * unless the SMI runs on CPU 0. An SMI can be initiated by
+ * any AML, but typically it's done in GPE-related methods that
+ * are run via workqueues, so we can avoid the known corruption
+ * cases by always queueing on CPU 0.
+ */
+ ret = queue_work_on(0, kacpid_wq, &dpc->work);
+ break;
+ default:
pr_err("Unsupported os_execute type %d.\n", type);
- status = AE_ERROR;
+ goto err;
}
-
- if (ACPI_FAILURE(status))
- goto err_workqueue;
-
- /*
- * On some machines, a software-initiated SMI causes corruption unless
- * the SMI runs on CPU 0. An SMI can be initiated by any AML, but
- * typically it's done in GPE-related methods that are run via
- * workqueues, so we can avoid the known corruption cases by always
- * queueing on CPU 0.
- */
- ret = queue_work_on(0, queue, &dpc->work);
if (!ret) {
pr_err("Unable to queue work\n");
- status = AE_ERROR;
+ goto err;
}
-err_workqueue:
- if (ACPI_FAILURE(status))
- kfree(dpc);
-out_thread:
- return status;
+
+ return AE_OK;
+
+err:
+ kfree(dpc);
+ return AE_ERROR;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1197,7 +1223,7 @@ bool acpi_queue_hotplug_work(struct work_struct *work)
}
acpi_status
-acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
+acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle *handle)
{
struct semaphore *sem = NULL;
@@ -1521,19 +1547,18 @@ void acpi_os_delete_lock(acpi_spinlock handle)
acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp)
__acquires(lockp)
{
- acpi_cpu_flags flags;
- spin_lock_irqsave(lockp, flags);
- return flags;
+ spin_lock(lockp);
+ return 0;
}
/*
* Release a spinlock. See above.
*/
-void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags)
+void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags not_used)
__releases(lockp)
{
- spin_unlock_irqrestore(lockp, flags);
+ spin_unlock(lockp);
}
#ifndef ACPI_USE_LOCAL_CACHE
@@ -1554,7 +1579,7 @@ void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags)
******************************************************************************/
acpi_status
-acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache)
+acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t **cache)
{
*cache = kmem_cache_create(name, size, 0, 0, NULL);
if (*cache == NULL)
@@ -1575,10 +1600,10 @@ acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache)
*
******************************************************************************/
-acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
+acpi_status acpi_os_purge_cache(acpi_cache_t *cache)
{
kmem_cache_shrink(cache);
- return (AE_OK);
+ return AE_OK;
}
/*******************************************************************************
@@ -1594,10 +1619,10 @@ acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
*
******************************************************************************/
-acpi_status acpi_os_delete_cache(acpi_cache_t * cache)
+acpi_status acpi_os_delete_cache(acpi_cache_t *cache)
{
kmem_cache_destroy(cache);
- return (AE_OK);
+ return AE_OK;
}
/*******************************************************************************
@@ -1614,10 +1639,10 @@ acpi_status acpi_os_delete_cache(acpi_cache_t * cache)
*
******************************************************************************/
-acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
+acpi_status acpi_os_release_object(acpi_cache_t *cache, void *object)
{
kmem_cache_free(cache, object);
- return (AE_OK);
+ return AE_OK;
}
#endif
@@ -1669,8 +1694,8 @@ acpi_status __init acpi_os_initialize(void)
acpi_status __init acpi_os_initialize1(void)
{
- kacpid_wq = alloc_workqueue("kacpid", 0, 1);
- kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
+ kacpid_wq = alloc_workqueue("kacpid", WQ_PERCPU, 1);
+ kacpi_notify_wq = alloc_workqueue("kacpi_notify", WQ_PERCPU, 0);
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
@@ -1708,6 +1733,7 @@ acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control,
u32 pm1b_control)
{
int rc = 0;
+
if (__acpi_os_prepare_sleep)
rc = __acpi_os_prepare_sleep(sleep_state,
pm1a_control, pm1b_control);
@@ -1730,6 +1756,7 @@ acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, u32 val_a,
u32 val_b)
{
int rc = 0;
+
if (__acpi_os_prepare_extended_sleep)
rc = __acpi_os_prepare_extended_sleep(sleep_state,
val_a, val_b);
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 08e15774fb9f..ad81aa03fe2f 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -22,6 +22,7 @@
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/string_choices.h>
struct acpi_prt_entry {
struct acpi_pci_id id;
@@ -288,7 +289,7 @@ static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
}
#endif /* CONFIG_X86_IO_APIC */
-static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
+struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
{
struct acpi_prt_entry *entry = NULL;
struct pci_dev *bridge;
@@ -387,13 +388,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
u8 pin;
int triggering = ACPI_LEVEL_SENSITIVE;
/*
- * On ARM systems with the GIC interrupt model, level interrupts
+ * On ARM systems with the GIC interrupt model, or LoongArch
+ * systems with the LPIC interrupt model, level interrupts
* are always polarity high by specification; PCI legacy
* IRQs lines are inverted before reaching the interrupt
* controller and must therefore be considered active high
* as default.
*/
- int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ?
+ int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ||
+ acpi_irq_model == ACPI_IRQ_MODEL_LPIC ?
ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
char *link = NULL;
char link_desc[16];
@@ -466,7 +469,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n",
pin_name(pin), link_desc, gsi,
(triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
- (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
+ str_low_high(polarity == ACPI_ACTIVE_LOW), dev->irq);
kfree(entry);
return 0;
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index aa1038b8aec4..bed7dc85612e 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -268,7 +268,7 @@ static int acpi_pci_link_get_current(struct acpi_pci_link *link)
link->irq.active = irq;
- acpi_handle_debug(handle, "Link at IRQ %d \n", link->irq.active);
+ acpi_handle_debug(handle, "Link at IRQ %d\n", link->irq.active);
end:
return result;
@@ -714,8 +714,8 @@ static int acpi_pci_link_add(struct acpi_device *device,
return -ENOMEM;
link->device = device;
- strcpy(acpi_device_name(device), ACPI_PCI_LINK_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
+ strscpy(acpi_device_name(device), ACPI_PCI_LINK_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS);
device->driver_data = link;
mutex_lock(&acpi_link_lock);
@@ -748,6 +748,8 @@ static int acpi_pci_link_add(struct acpi_device *device,
if (result)
kfree(link);
+ acpi_dev_clear_dependencies(device);
+
return result < 0 ? result : 1;
}
@@ -759,7 +761,7 @@ static int acpi_pci_link_resume(struct acpi_pci_link *link)
return 0;
}
-static void irqrouter_resume(void)
+static void irqrouter_resume(void *data)
{
struct acpi_pci_link *link;
@@ -886,10 +888,14 @@ static int __init acpi_irq_balance_set(char *str)
__setup("acpi_irq_balance", acpi_irq_balance_set);
-static struct syscore_ops irqrouter_syscore_ops = {
+static const struct syscore_ops irqrouter_syscore_ops = {
.resume = irqrouter_resume,
};
+static struct syscore irqrouter_syscore = {
+ .ops = &irqrouter_syscore_ops,
+};
+
void __init acpi_pci_link_init(void)
{
if (acpi_noirq)
@@ -902,6 +908,6 @@ void __init acpi_pci_link_init(void)
else
acpi_irq_balance = 0;
}
- register_syscore_ops(&irqrouter_syscore_ops);
+ register_syscore(&irqrouter_syscore);
acpi_scan_add_handler(&pci_link_handler);
}
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
index 860014b89b8e..58e10a980114 100644
--- a/drivers/acpi/pci_mcfg.c
+++ b/drivers/acpi/pci_mcfg.c
@@ -181,6 +181,18 @@ static struct mcfg_fixup mcfg_quirks[] = {
LOONGSON_ECAM_MCFG("LOONGSON", 0),
LOONGSON_ECAM_MCFG("\0", 1),
LOONGSON_ECAM_MCFG("LOONGSON", 1),
+ LOONGSON_ECAM_MCFG("\0", 2),
+ LOONGSON_ECAM_MCFG("LOONGSON", 2),
+ LOONGSON_ECAM_MCFG("\0", 3),
+ LOONGSON_ECAM_MCFG("LOONGSON", 3),
+ LOONGSON_ECAM_MCFG("\0", 4),
+ LOONGSON_ECAM_MCFG("LOONGSON", 4),
+ LOONGSON_ECAM_MCFG("\0", 5),
+ LOONGSON_ECAM_MCFG("LOONGSON", 5),
+ LOONGSON_ECAM_MCFG("\0", 6),
+ LOONGSON_ECAM_MCFG("LOONGSON", 6),
+ LOONGSON_ECAM_MCFG("\0", 7),
+ LOONGSON_ECAM_MCFG("LOONGSON", 7),
#endif /* LOONGARCH */
};
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index d57cf8454b93..74ade4160314 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -293,11 +293,6 @@ struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
}
EXPORT_SYMBOL_GPL(acpi_pci_find_root);
-struct acpi_handle_node {
- struct list_head node;
- acpi_handle handle;
-};
-
/**
* acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev
* @handle: the handle in question
@@ -312,76 +307,26 @@ struct acpi_handle_node {
*/
struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
{
- int dev, fn;
- unsigned long long adr;
- acpi_status status;
- acpi_handle phandle;
- struct pci_bus *pbus;
- struct pci_dev *pdev = NULL;
- struct acpi_handle_node *node, *tmp;
- struct acpi_pci_root *root;
- LIST_HEAD(device_list);
-
- /*
- * Walk up the ACPI CA namespace until we reach a PCI root bridge.
- */
- phandle = handle;
- while (!acpi_is_root_bridge(phandle)) {
- node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL);
- if (!node)
- goto out;
-
- INIT_LIST_HEAD(&node->node);
- node->handle = phandle;
- list_add(&node->node, &device_list);
-
- status = acpi_get_parent(phandle, &phandle);
- if (ACPI_FAILURE(status))
- goto out;
- }
-
- root = acpi_pci_find_root(phandle);
- if (!root)
- goto out;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
+ struct acpi_device_physical_node *pn;
+ struct pci_dev *pci_dev = NULL;
- pbus = root->bus;
-
- /*
- * Now, walk back down the PCI device tree until we return to our
- * original handle. Assumes that everything between the PCI root
- * bridge and the device we're looking for must be a P2P bridge.
- */
- list_for_each_entry(node, &device_list, node) {
- acpi_handle hnd = node->handle;
- status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr);
- if (ACPI_FAILURE(status))
- goto out;
- dev = (adr >> 16) & 0xffff;
- fn = adr & 0xffff;
-
- pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn));
- if (!pdev || hnd == handle)
- break;
+ if (!adev)
+ return NULL;
- pbus = pdev->subordinate;
- pci_dev_put(pdev);
+ mutex_lock(&adev->physical_node_lock);
- /*
- * This function may be called for a non-PCI device that has a
- * PCI parent (eg. a disk under a PCI SATA controller). In that
- * case pdev->subordinate will be NULL for the parent.
- */
- if (!pbus) {
- dev_dbg(&pdev->dev, "Not a PCI-to-PCI bridge\n");
- pdev = NULL;
+ list_for_each_entry(pn, &adev->physical_node_list, node) {
+ if (dev_is_pci(pn->dev)) {
+ get_device(pn->dev);
+ pci_dev = to_pci_dev(pn->dev);
break;
}
}
-out:
- list_for_each_entry_safe(node, tmp, &device_list, node)
- kfree(node);
- return pdev;
+ mutex_unlock(&adev->physical_node_lock);
+
+ return pci_dev;
}
EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
@@ -543,6 +488,7 @@ static u32 calculate_cxl_support(void)
u32 support;
support = OSC_CXL_2_0_PORT_DEV_REG_ACCESS_SUPPORT;
+ support |= OSC_CXL_1_1_PORT_REG_ACCESS_SUPPORT;
if (pci_aer_available())
support |= OSC_CXL_PROTOCOL_ERR_REPORTING_SUPPORT;
if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
@@ -743,8 +689,8 @@ static int acpi_pci_root_add(struct acpi_device *device,
root->device = device;
root->segment = segment & 0xFFFF;
- strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
+ strscpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
device->driver_data = root;
if (hotadd && dmar_device_add(handle)) {
@@ -912,7 +858,7 @@ next:
}
}
-static void acpi_pci_root_remap_iospace(struct fwnode_handle *fwnode,
+static void acpi_pci_root_remap_iospace(const struct fwnode_handle *fwnode,
struct resource_entry *entry)
{
#ifdef PCI_IOBASE
@@ -1057,7 +1003,6 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
int node = acpi_get_node(device->handle);
struct pci_bus *bus;
struct pci_host_bridge *host_bridge;
- union acpi_object *obj;
info->root = root;
info->bridge = device;
@@ -1096,16 +1041,8 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
host_bridge->native_dpc = 0;
- /*
- * Evaluate the "PCI Boot Configuration" _DSM Function. If it
- * exists and returns 0, we must preserve any PCI resource
- * assignments made by firmware for this host bridge.
- */
- obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 1,
- DSM_PCI_PRESERVE_BOOT_CONFIG, NULL);
- if (obj && obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 0)
- host_bridge->preserve_config = 1;
- ACPI_FREE(obj);
+ if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
+ host_bridge->native_cxl_error = 0;
acpi_dev_power_up_children_with_adr(device);
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index d6cb2c27a23b..741bcc9d6d6a 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -111,7 +111,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
snprintf(name, sizeof(name), "%llu", sun);
pci_slot = pci_create_slot(pci_bus, device, name, NULL);
if (IS_ERR(pci_slot)) {
- pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
+ pr_err("pci_create_slot returned %pe\n", pci_slot);
kfree(slot);
return AE_OK;
}
diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c
index 9abf350bd7a5..32bdf8cbe8f2 100644
--- a/drivers/acpi/pfr_telemetry.c
+++ b/drivers/acpi/pfr_telemetry.c
@@ -144,7 +144,7 @@ static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info,
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
@@ -180,7 +180,7 @@ static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev)
ret = -EBUSY;
}
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
@@ -218,7 +218,7 @@ static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev)
ret = obj->integer.value;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
@@ -272,9 +272,6 @@ static long pfrt_log_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case PFRT_LOG_IOC_GET_INFO:
info.log_level = get_pfrt_log_level(pfrt_log_dev);
- if (ret < 0)
- return ret;
-
info.log_type = pfrt_log_dev->info.log_type;
info.log_revid = pfrt_log_dev->info.log_revid;
if (copy_to_user(p, &info, sizeof(info)))
@@ -310,7 +307,7 @@ pfrt_log_mmap(struct file *file, struct vm_area_struct *vma)
return -EROFS;
/* changing from read to write with mprotect is not allowed */
- vma->vm_flags &= ~VM_MAYWRITE;
+ vm_flags_clear(vma, VM_MAYWRITE);
pfrt_log_dev = to_pfrt_log_dev(file);
@@ -347,13 +344,11 @@ static const struct file_operations acpi_pfrt_log_fops = {
.llseek = noop_llseek,
};
-static int acpi_pfrt_log_remove(struct platform_device *pdev)
+static void acpi_pfrt_log_remove(struct platform_device *pdev)
{
struct pfrt_log_device *pfrt_log_dev = platform_get_drvdata(pdev);
misc_deregister(&pfrt_log_dev->miscdev);
-
- return 0;
}
static void pfrt_log_put_idx(void *data)
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
index 6bb0b778b5da..11b1c2828005 100644
--- a/drivers/acpi/pfr_update.c
+++ b/drivers/acpi/pfr_update.c
@@ -127,8 +127,11 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
pfru_dev->rev_id,
PFRU_FUNC_QUERY_UPDATE_CAP,
NULL, ACPI_TYPE_PACKAGE);
- if (!out_obj)
+ if (!out_obj) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Query cap failed with no object\n");
return ret;
+ }
if (out_obj->package.count < CAP_NR_IDX ||
out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
@@ -141,13 +144,17 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER ||
out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER ||
- out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER)
+ out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Query cap failed with invalid package count/type\n");
goto free_acpi_buffer;
+ }
cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value;
if (cap_hdr->status != DSM_SUCCEED) {
ret = -EBUSY;
- dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status);
+ dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n",
+ cap_hdr->status);
goto free_acpi_buffer;
}
@@ -178,7 +185,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
@@ -193,24 +200,32 @@ static int query_buffer(struct pfru_com_buf_info *info,
out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF,
NULL, ACPI_TYPE_PACKAGE);
- if (!out_obj)
+ if (!out_obj) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Query buf failed with no object\n");
return ret;
+ }
if (out_obj->package.count < BUF_NR_IDX ||
out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER ||
- out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER)
+ out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Query buf failed with invalid package count/type\n");
goto free_acpi_buffer;
+ }
info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value;
info->ext_status =
out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value;
if (info->status != DSM_SUCCEED) {
ret = -EBUSY;
- dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", info->status);
- dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", info->ext_status);
+ dev_dbg(pfru_dev->parent_dev,
+ "Query buf failed with Error Status:%d\n", info->status);
+ dev_dbg(pfru_dev->parent_dev,
+ "Query buf failed with Error Extended Status:%d\n", info->ext_status);
goto free_acpi_buffer;
}
@@ -224,7 +239,7 @@ static int query_buffer(struct pfru_com_buf_info *info,
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
@@ -295,12 +310,16 @@ static bool applicable_image(const void *data, struct pfru_update_cap_info *cap,
m_img_hdr = data + size;
type = get_image_type(m_img_hdr, pfru_dev);
- if (type < 0)
+ if (type < 0) {
+ dev_dbg(pfru_dev->parent_dev, "Invalid image type\n");
return false;
+ }
size = adjust_efi_size(m_img_hdr, size);
- if (size < 0)
+ if (size < 0) {
+ dev_dbg(pfru_dev->parent_dev, "Invalid image size\n");
return false;
+ }
auth = data + size;
size += sizeof(u64) + auth->auth_info.hdr.len;
@@ -310,7 +329,7 @@ static bool applicable_image(const void *data, struct pfru_update_cap_info *cap,
if (type == PFRU_CODE_INJECT_TYPE)
return payload_hdr->rt_ver >= cap->code_rt_version;
- return payload_hdr->rt_ver >= cap->drv_rt_version;
+ return payload_hdr->svn_ver >= cap->drv_svn;
}
static void print_update_debug_info(struct pfru_updated_result *result,
@@ -346,8 +365,11 @@ static int start_update(int action, struct pfru_device *pfru_dev)
out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
pfru_dev->rev_id, PFRU_FUNC_START,
&in_obj, ACPI_TYPE_PACKAGE);
- if (!out_obj)
+ if (!out_obj) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Update failed to start with no object\n");
return ret;
+ }
if (out_obj->package.count < UPDATE_NR_IDX ||
out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
@@ -355,8 +377,11 @@ static int start_update(int action, struct pfru_device *pfru_dev)
out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER ||
- out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER)
+ out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Update failed with invalid package count/type\n");
goto free_acpi_buffer;
+ }
update_result.status =
out_obj->package.elements[UPDATE_STATUS_IDX].integer.value;
@@ -365,8 +390,10 @@ static int start_update(int action, struct pfru_device *pfru_dev)
if (update_result.status != DSM_SUCCEED) {
ret = -EBUSY;
- dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", update_result.status);
- dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n",
+ dev_dbg(pfru_dev->parent_dev,
+ "Update failed with Error Status:%d\n", update_result.status);
+ dev_dbg(pfru_dev->parent_dev,
+ "Update failed with Error Extended Status:%d\n",
update_result.ext_status);
goto free_acpi_buffer;
@@ -385,7 +412,7 @@ static int start_update(int action, struct pfru_device *pfru_dev)
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
@@ -450,20 +477,26 @@ static ssize_t pfru_write(struct file *file, const char __user *buf,
if (ret)
return ret;
- if (len > buf_info.buf_size)
+ if (len > buf_info.buf_size) {
+ dev_dbg(pfru_dev->parent_dev, "Capsule image size too large\n");
return -EINVAL;
+ }
iov.iov_base = (void __user *)buf;
iov.iov_len = len;
- iov_iter_init(&iter, WRITE, &iov, 1, len);
+ iov_iter_init(&iter, ITER_SOURCE, &iov, 1, len);
/* map the communication buffer */
phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo);
buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB);
- if (!buf_ptr)
+ if (!buf_ptr) {
+ dev_dbg(pfru_dev->parent_dev, "Failed to remap the buffer\n");
return -ENOMEM;
+ }
if (!copy_from_iter_full(buf_ptr, len, &iter)) {
+ dev_dbg(pfru_dev->parent_dev,
+ "Failed to copy the data from the user space buffer\n");
ret = -EINVAL;
goto unmap;
}
@@ -489,13 +522,11 @@ static const struct file_operations acpi_pfru_fops = {
.llseek = noop_llseek,
};
-static int acpi_pfru_remove(struct platform_device *pdev)
+static void acpi_pfru_remove(struct platform_device *pdev)
{
struct pfru_device *pfru_dev = platform_get_drvdata(pdev);
misc_deregister(&pfru_dev->miscdev);
-
- return 0;
}
static void pfru_put_idx(void *data)
diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
index d418462ab791..ea04a8c69215 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/acpi/platform_profile.c
@@ -2,16 +2,34 @@
/* Platform profile sysfs interface */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/acpi.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/platform_profile.h>
#include <linux/sysfs.h>
-static struct platform_profile_handler *cur_profile;
+#define to_pprof_handler(d) (container_of(d, struct platform_profile_handler, dev))
+
static DEFINE_MUTEX(profile_lock);
+struct platform_profile_handler {
+ const char *name;
+ struct device dev;
+ int minor;
+ unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+ unsigned long hidden_choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+ const struct platform_profile_ops *ops;
+};
+
+struct aggregate_choices_data {
+ unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+ int count;
+};
+
static const char * const profile_names[] = {
[PLATFORM_PROFILE_LOW_POWER] = "low-power",
[PLATFORM_PROFILE_COOL] = "cool",
@@ -19,163 +37,682 @@ static const char * const profile_names[] = {
[PLATFORM_PROFILE_BALANCED] = "balanced",
[PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",
[PLATFORM_PROFILE_PERFORMANCE] = "performance",
+ [PLATFORM_PROFILE_MAX_POWER] = "max-power",
+ [PLATFORM_PROFILE_CUSTOM] = "custom",
};
static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
-static ssize_t platform_profile_choices_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int len = 0;
- int err, i;
-
- err = mutex_lock_interruptible(&profile_lock);
- if (err)
- return err;
+static DEFINE_IDA(platform_profile_ida);
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
- }
+/**
+ * _commmon_choices_show - Show the available profile choices
+ * @choices: The available profile choices
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t _commmon_choices_show(unsigned long *choices, char *buf)
+{
+ int i, len = 0;
- for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) {
+ for_each_set_bit(i, choices, PLATFORM_PROFILE_LAST) {
if (len == 0)
len += sysfs_emit_at(buf, len, "%s", profile_names[i]);
else
len += sysfs_emit_at(buf, len, " %s", profile_names[i]);
}
len += sysfs_emit_at(buf, len, "\n");
- mutex_unlock(&profile_lock);
+
return len;
}
-static ssize_t platform_profile_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+/**
+ * _store_class_profile - Set the profile for a class device
+ * @dev: The class device
+ * @data: The profile to set
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _store_class_profile(struct device *dev, void *data)
+{
+ struct platform_profile_handler *handler;
+ int *bit = (int *)data;
+
+ lockdep_assert_held(&profile_lock);
+ handler = to_pprof_handler(dev);
+ if (!test_bit(*bit, handler->choices) && !test_bit(*bit, handler->hidden_choices))
+ return -EOPNOTSUPP;
+
+ return handler->ops->profile_set(dev, *bit);
+}
+
+/**
+ * _notify_class_profile - Notify the class device of a profile change
+ * @dev: The class device
+ * @data: Unused
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _notify_class_profile(struct device *dev, void *data)
+{
+ struct platform_profile_handler *handler = to_pprof_handler(dev);
+
+ lockdep_assert_held(&profile_lock);
+ sysfs_notify(&handler->dev.kobj, NULL, "profile");
+ kobject_uevent(&handler->dev.kobj, KOBJ_CHANGE);
+
+ return 0;
+}
+
+/**
+ * get_class_profile - Show the current profile for a class device
+ * @dev: The class device
+ * @profile: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int get_class_profile(struct device *dev,
+ enum platform_profile_option *profile)
{
- enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
+ struct platform_profile_handler *handler;
+ enum platform_profile_option val;
int err;
- err = mutex_lock_interruptible(&profile_lock);
- if (err)
+ lockdep_assert_held(&profile_lock);
+ handler = to_pprof_handler(dev);
+ err = handler->ops->profile_get(dev, &val);
+ if (err) {
+ pr_err("Failed to get profile for handler %s\n", handler->name);
return err;
+ }
+
+ if (WARN_ON(val >= PLATFORM_PROFILE_LAST))
+ return -EINVAL;
+ *profile = val;
+
+ return 0;
+}
+
+/**
+ * name_show - Show the name of the profile handler
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct platform_profile_handler *handler = to_pprof_handler(dev);
+
+ return sysfs_emit(buf, "%s\n", handler->name);
+}
+static DEVICE_ATTR_RO(name);
+
+/**
+ * choices_show - Show the available profile choices
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t choices_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_profile_handler *handler = to_pprof_handler(dev);
+
+ return _commmon_choices_show(handler->choices, buf);
+}
+static DEVICE_ATTR_RO(choices);
+
+/**
+ * profile_show - Show the current profile for a class device
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t profile_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
+ int err;
+
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = get_class_profile(dev, &profile);
+ if (err)
+ return err;
+ }
+
+ return sysfs_emit(buf, "%s\n", profile_names[profile]);
+}
+
+/**
+ * profile_store - Set the profile for a class device
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to read from
+ * @count: The number of bytes to read
+ *
+ * Return: The number of bytes read
+ */
+static ssize_t profile_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index, ret;
+
+ index = sysfs_match_string(profile_names, buf);
+ if (index < 0)
+ return -EINVAL;
+
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ ret = _store_class_profile(dev, &index);
+ if (ret)
+ return ret;
+ }
+
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ return count;
+}
+static DEVICE_ATTR_RW(profile);
+
+static struct attribute *profile_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_choices.attr,
+ &dev_attr_profile.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(profile);
+
+static void pprof_device_release(struct device *dev)
+{
+ struct platform_profile_handler *pprof = to_pprof_handler(dev);
+
+ kfree(pprof);
+}
+
+static const struct class platform_profile_class = {
+ .name = "platform-profile",
+ .dev_groups = profile_groups,
+ .dev_release = pprof_device_release,
+};
+
+/**
+ * _aggregate_choices - Aggregate the available profile choices
+ * @dev: The device
+ * @arg: struct aggregate_choices_data, with it's aggregate member bitmap
+ * initially filled with ones
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _aggregate_choices(struct device *dev, void *arg)
+{
+ unsigned long tmp[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+ struct aggregate_choices_data *data = arg;
+ struct platform_profile_handler *handler;
+
+ lockdep_assert_held(&profile_lock);
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
+ handler = to_pprof_handler(dev);
+ bitmap_or(tmp, handler->choices, handler->hidden_choices, PLATFORM_PROFILE_LAST);
+ bitmap_and(data->aggregate, tmp, data->aggregate, PLATFORM_PROFILE_LAST);
+ data->count++;
+
+ return 0;
+}
+
+/**
+ * _remove_hidden_choices - Remove hidden choices from aggregate data
+ * @dev: The device
+ * @arg: struct aggregate_choices_data
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _remove_hidden_choices(struct device *dev, void *arg)
+{
+ struct aggregate_choices_data *data = arg;
+ struct platform_profile_handler *handler;
+
+ lockdep_assert_held(&profile_lock);
+ handler = to_pprof_handler(dev);
+ bitmap_andnot(data->aggregate, handler->choices,
+ handler->hidden_choices, PLATFORM_PROFILE_LAST);
+
+ return 0;
+}
+
+/**
+ * platform_profile_choices_show - Show the available profile choices for legacy sysfs interface
+ * @kobj: The kobject
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t platform_profile_choices_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ struct aggregate_choices_data data = {
+ .aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
+ .count = 0,
+ };
+ int err;
+
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &data, _aggregate_choices);
+ if (err)
+ return err;
+ if (data.count == 1) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &data, _remove_hidden_choices);
+ if (err)
+ return err;
+ }
}
- err = cur_profile->profile_get(cur_profile, &profile);
- mutex_unlock(&profile_lock);
+ /* no profile handler registered any more */
+ if (bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
+ return -EINVAL;
+
+ return _commmon_choices_show(data.aggregate, buf);
+}
+
+/**
+ * _aggregate_profiles - Aggregate the profiles for legacy sysfs interface
+ * @dev: The device
+ * @data: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _aggregate_profiles(struct device *dev, void *data)
+{
+ enum platform_profile_option *profile = data;
+ enum platform_profile_option val;
+ int err;
+
+ err = get_class_profile(dev, &val);
if (err)
return err;
- /* Check that profile is valid index */
- if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
- return -EIO;
+ if (*profile != PLATFORM_PROFILE_LAST && *profile != val)
+ *profile = PLATFORM_PROFILE_CUSTOM;
+ else
+ *profile = val;
- return sysfs_emit(buf, "%s\n", profile_names[profile]);
+ return 0;
}
-static ssize_t platform_profile_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+/**
+ * _store_and_notify - Store and notify a class from legacy sysfs interface
+ * @dev: The device
+ * @data: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _store_and_notify(struct device *dev, void *data)
{
- int err, i;
+ enum platform_profile_option *profile = data;
+ int err;
- err = mutex_lock_interruptible(&profile_lock);
+ err = _store_class_profile(dev, profile);
if (err)
return err;
+ return _notify_class_profile(dev, NULL);
+}
+
+/**
+ * platform_profile_show - Show the current profile for legacy sysfs interface
+ * @kobj: The kobject
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t platform_profile_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
+ int err;
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &profile, _aggregate_profiles);
+ if (err)
+ return err;
}
+ /* no profile handler registered any more */
+ if (profile == PLATFORM_PROFILE_LAST)
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%s\n", profile_names[profile]);
+}
+
+/**
+ * platform_profile_store - Set the profile for legacy sysfs interface
+ * @kobj: The kobject
+ * @attr: The attribute
+ * @buf: The buffer to read from
+ * @count: The number of bytes to read
+ *
+ * Return: The number of bytes read
+ */
+static ssize_t platform_profile_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aggregate_choices_data data = {
+ .aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
+ .count = 0,
+ };
+ int ret;
+ int i;
+
/* Scan for a matching profile */
i = sysfs_match_string(profile_names, buf);
- if (i < 0) {
- mutex_unlock(&profile_lock);
+ if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
return -EINVAL;
- }
- /* Check that platform supports this profile choice */
- if (!test_bit(i, cur_profile->choices)) {
- mutex_unlock(&profile_lock);
- return -EOPNOTSUPP;
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ ret = class_for_each_device(&platform_profile_class, NULL,
+ &data, _aggregate_choices);
+ if (ret)
+ return ret;
+ if (!test_bit(i, data.aggregate))
+ return -EOPNOTSUPP;
+
+ ret = class_for_each_device(&platform_profile_class, NULL, &i,
+ _store_and_notify);
+ if (ret)
+ return ret;
}
- err = cur_profile->profile_set(cur_profile, i);
- if (!err)
- sysfs_notify(acpi_kobj, NULL, "platform_profile");
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
- mutex_unlock(&profile_lock);
- if (err)
- return err;
return count;
}
-static DEVICE_ATTR_RO(platform_profile_choices);
-static DEVICE_ATTR_RW(platform_profile);
+static struct kobj_attribute attr_platform_profile_choices = __ATTR_RO(platform_profile_choices);
+static struct kobj_attribute attr_platform_profile = __ATTR_RW(platform_profile);
static struct attribute *platform_profile_attrs[] = {
- &dev_attr_platform_profile_choices.attr,
- &dev_attr_platform_profile.attr,
+ &attr_platform_profile_choices.attr,
+ &attr_platform_profile.attr,
NULL
};
+static int profile_class_registered(struct device *dev, const void *data)
+{
+ return 1;
+}
+
+static umode_t profile_class_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
+{
+ struct device *dev;
+
+ dev = class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered);
+ if (!dev)
+ return 0;
+
+ put_device(dev);
+
+ return attr->mode;
+}
+
static const struct attribute_group platform_profile_group = {
- .attrs = platform_profile_attrs
+ .attrs = platform_profile_attrs,
+ .is_visible = profile_class_is_visible,
};
-void platform_profile_notify(void)
+/**
+ * platform_profile_notify - Notify class device and legacy sysfs interface
+ * @dev: The class device
+ */
+void platform_profile_notify(struct device *dev)
{
- if (!cur_profile)
- return;
+ scoped_cond_guard(mutex_intr, return, &profile_lock) {
+ _notify_class_profile(dev, NULL);
+ }
sysfs_notify(acpi_kobj, NULL, "platform_profile");
}
EXPORT_SYMBOL_GPL(platform_profile_notify);
-int platform_profile_register(struct platform_profile_handler *pprof)
+/**
+ * platform_profile_cycle - Cycles profiles available on all registered class devices
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int platform_profile_cycle(void)
{
+ struct aggregate_choices_data data = {
+ .aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
+ .count = 0,
+ };
+ enum platform_profile_option next = PLATFORM_PROFILE_LAST;
+ enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
int err;
- mutex_lock(&profile_lock);
- /* We can only have one active profile */
- if (cur_profile) {
- mutex_unlock(&profile_lock);
- return -EEXIST;
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &profile, _aggregate_profiles);
+ if (err)
+ return err;
+
+ if (profile == PLATFORM_PROFILE_MAX_POWER ||
+ profile == PLATFORM_PROFILE_CUSTOM ||
+ profile == PLATFORM_PROFILE_LAST)
+ return -EINVAL;
+
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &data, _aggregate_choices);
+ if (err)
+ return err;
+
+ /* never iterate into a custom or max power if all drivers supported it */
+ clear_bit(PLATFORM_PROFILE_MAX_POWER, data.aggregate);
+ clear_bit(PLATFORM_PROFILE_CUSTOM, data.aggregate);
+
+ next = find_next_bit_wrap(data.aggregate,
+ PLATFORM_PROFILE_LAST,
+ profile + 1);
+
+ err = class_for_each_device(&platform_profile_class, NULL, &next,
+ _store_and_notify);
+
+ if (err)
+ return err;
}
- /* Sanity check the profile handler field are set */
- if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
- !pprof->profile_set || !pprof->profile_get) {
- mutex_unlock(&profile_lock);
- return -EINVAL;
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(platform_profile_cycle);
+
+/**
+ * platform_profile_register - Creates and registers a platform profile class device
+ * @dev: Parent device
+ * @name: Name of the class device
+ * @drvdata: Driver data that will be attached to the class device
+ * @ops: Platform profile's mandatory operations
+ *
+ * Return: pointer to the new class device on success, ERR_PTR on failure
+ */
+struct device *platform_profile_register(struct device *dev, const char *name,
+ void *drvdata,
+ const struct platform_profile_ops *ops)
+{
+ struct device *ppdev;
+ int minor;
+ int err;
+
+ /* Sanity check */
+ if (WARN_ON_ONCE(!dev || !name || !ops || !ops->profile_get ||
+ !ops->profile_set || !ops->probe))
+ return ERR_PTR(-EINVAL);
+
+ struct platform_profile_handler *pprof __free(kfree) = kzalloc(
+ sizeof(*pprof), GFP_KERNEL);
+ if (!pprof)
+ return ERR_PTR(-ENOMEM);
+
+ err = ops->probe(drvdata, pprof->choices);
+ if (err) {
+ dev_err(dev, "platform_profile probe failed\n");
+ return ERR_PTR(err);
}
- err = sysfs_create_group(acpi_kobj, &platform_profile_group);
+ if (bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST)) {
+ dev_err(dev, "Failed to register platform_profile class device with empty choices\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (ops->hidden_choices) {
+ err = ops->hidden_choices(drvdata, pprof->hidden_choices);
+ if (err) {
+ dev_err(dev, "platform_profile hidden_choices failed\n");
+ return ERR_PTR(err);
+ }
+ }
+
+ guard(mutex)(&profile_lock);
+
+ /* create class interface for individual handler */
+ minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
+ if (minor < 0)
+ return ERR_PTR(minor);
+
+ pprof->name = name;
+ pprof->ops = ops;
+ pprof->minor = minor;
+ pprof->dev.class = &platform_profile_class;
+ pprof->dev.parent = dev;
+ dev_set_drvdata(&pprof->dev, drvdata);
+ dev_set_name(&pprof->dev, "platform-profile-%d", pprof->minor);
+ /* device_register() takes ownership of pprof/ppdev */
+ ppdev = &no_free_ptr(pprof)->dev;
+ err = device_register(ppdev);
if (err) {
- mutex_unlock(&profile_lock);
- return err;
+ put_device(ppdev);
+ goto cleanup_ida;
}
- cur_profile = pprof;
- mutex_unlock(&profile_lock);
- return 0;
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ err = sysfs_update_group(acpi_kobj, &platform_profile_group);
+ if (err)
+ goto cleanup_cur;
+
+ return ppdev;
+
+cleanup_cur:
+ device_unregister(ppdev);
+
+cleanup_ida:
+ ida_free(&platform_profile_ida, minor);
+
+ return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(platform_profile_register);
-int platform_profile_remove(void)
+/**
+ * platform_profile_remove - Unregisters a platform profile class device
+ * @dev: Class device
+ */
+void platform_profile_remove(struct device *dev)
{
- sysfs_remove_group(acpi_kobj, &platform_profile_group);
+ struct platform_profile_handler *pprof;
- mutex_lock(&profile_lock);
- cur_profile = NULL;
- mutex_unlock(&profile_lock);
- return 0;
+ if (IS_ERR_OR_NULL(dev))
+ return;
+
+ pprof = to_pprof_handler(dev);
+
+ guard(mutex)(&profile_lock);
+
+ ida_free(&platform_profile_ida, pprof->minor);
+ device_unregister(&pprof->dev);
+
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+ sysfs_update_group(acpi_kobj, &platform_profile_group);
}
EXPORT_SYMBOL_GPL(platform_profile_remove);
+static void devm_platform_profile_release(struct device *dev, void *res)
+{
+ struct device **ppdev = res;
+
+ platform_profile_remove(*ppdev);
+}
+
+/**
+ * devm_platform_profile_register - Device managed version of platform_profile_register
+ * @dev: Parent device
+ * @name: Name of the class device
+ * @drvdata: Driver data that will be attached to the class device
+ * @ops: Platform profile's mandatory operations
+ *
+ * Return: pointer to the new class device on success, ERR_PTR on failure
+ */
+struct device *devm_platform_profile_register(struct device *dev, const char *name,
+ void *drvdata,
+ const struct platform_profile_ops *ops)
+{
+ struct device *ppdev;
+ struct device **dr;
+
+ dr = devres_alloc(devm_platform_profile_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ ppdev = platform_profile_register(dev, name, drvdata, ops);
+ if (IS_ERR(ppdev)) {
+ devres_free(dr);
+ return ppdev;
+ }
+
+ *dr = ppdev;
+ devres_add(dev, dr);
+
+ return ppdev;
+}
+EXPORT_SYMBOL_GPL(devm_platform_profile_register);
+
+static int __init platform_profile_init(void)
+{
+ int err;
+
+ if (acpi_disabled)
+ return -EOPNOTSUPP;
+
+ err = class_register(&platform_profile_class);
+ if (err)
+ return err;
+
+ err = sysfs_create_group(acpi_kobj, &platform_profile_group);
+ if (err)
+ class_unregister(&platform_profile_class);
+
+ return err;
+}
+
+static void __exit platform_profile_exit(void)
+{
+ sysfs_remove_group(acpi_kobj, &platform_profile_group);
+ class_unregister(&platform_profile_class);
+}
+module_init(platform_profile_init);
+module_exit(platform_profile_exit);
+
MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
+MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
index f20dbda1a831..134e9ca8eaa2 100644
--- a/drivers/acpi/pmic/intel_pmic.c
+++ b/drivers/acpi/pmic/intel_pmic.c
@@ -31,7 +31,7 @@ struct intel_pmic_opregion {
static struct intel_pmic_opregion *intel_pmic_opregion;
-static int pmic_get_reg_bit(int address, struct pmic_table *table,
+static int pmic_get_reg_bit(int address, const struct pmic_table *table,
int count, int *reg, int *bit)
{
int i;
diff --git a/drivers/acpi/pmic/intel_pmic.h b/drivers/acpi/pmic/intel_pmic.h
index d956b03a6ca0..006f0780ffab 100644
--- a/drivers/acpi/pmic/intel_pmic.h
+++ b/drivers/acpi/pmic/intel_pmic.h
@@ -21,9 +21,9 @@ struct intel_pmic_opregion_data {
u32 reg_address, u32 value, u32 mask);
int (*lpat_raw_to_temp)(struct acpi_lpat_conversion_table *lpat_table,
int raw);
- struct pmic_table *power_table;
+ const struct pmic_table *power_table;
int power_table_count;
- struct pmic_table *thermal_table;
+ const struct pmic_table *thermal_table;
int thermal_table_count;
/* For generic exec_mipi_pmic_seq_element handling */
int pmic_i2c_address;
diff --git a/drivers/acpi/pmic/intel_pmic_bxtwc.c b/drivers/acpi/pmic/intel_pmic_bxtwc.c
index e247615189fa..c332afbf82bd 100644
--- a/drivers/acpi/pmic/intel_pmic_bxtwc.c
+++ b/drivers/acpi/pmic/intel_pmic_bxtwc.c
@@ -24,7 +24,7 @@
#define VSWITCH1_OUTPUT BIT(4)
#define VUSBPHY_CHARGE BIT(1)
-static struct pmic_table power_table[] = {
+static const struct pmic_table power_table[] = {
{
.address = 0x0,
.reg = 0x63,
@@ -177,7 +177,7 @@ static struct pmic_table power_table[] = {
} /* MOFF -> MODEMCTRL Bit 0 */
};
-static struct pmic_table thermal_table[] = {
+static const struct pmic_table thermal_table[] = {
{
.address = 0x00,
.reg = 0x4F39
diff --git a/drivers/acpi/pmic/intel_pmic_bytcrc.c b/drivers/acpi/pmic/intel_pmic_bytcrc.c
index 9ea79f210965..b4c21a75294a 100644
--- a/drivers/acpi/pmic/intel_pmic_bytcrc.c
+++ b/drivers/acpi/pmic/intel_pmic_bytcrc.c
@@ -16,7 +16,7 @@
#define PMIC_A0LOCK_REG 0xc5
-static struct pmic_table power_table[] = {
+static const struct pmic_table power_table[] = {
/* {
.address = 0x00,
.reg = ??,
@@ -134,7 +134,7 @@ static struct pmic_table power_table[] = {
}, /* V105 -> V1P05S, L2 SRAM */
};
-static struct pmic_table thermal_table[] = {
+static const struct pmic_table thermal_table[] = {
{
.address = 0x00,
.reg = 0x75
@@ -283,6 +283,7 @@ static const struct intel_pmic_opregion_data intel_crc_pmic_opregion_data = {
.power_table_count= ARRAY_SIZE(power_table),
.thermal_table = thermal_table,
.thermal_table_count = ARRAY_SIZE(thermal_table),
+ .pmic_i2c_address = 0x6e,
};
static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
diff --git a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
index 418eec523025..ecb36fbc1e7f 100644
--- a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
+++ b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
@@ -8,34 +8,38 @@
*/
#include <linux/acpi.h>
+#include <linux/bits.h>
#include <linux/init.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/platform_device.h>
+#include <asm/byteorder.h>
#include "intel_pmic.h"
/* registers stored in 16bit BE (high:low, total 10bit) */
+#define PMIC_REG_MASK GENMASK(9, 0)
+
#define CHTDC_TI_VBAT 0x54
#define CHTDC_TI_DIETEMP 0x56
#define CHTDC_TI_BPTHERM 0x58
#define CHTDC_TI_GPADC 0x5a
-static struct pmic_table chtdc_ti_power_table[] = {
- { .address = 0x00, .reg = 0x41 },
- { .address = 0x04, .reg = 0x42 },
- { .address = 0x08, .reg = 0x43 },
- { .address = 0x0c, .reg = 0x45 },
- { .address = 0x10, .reg = 0x46 },
- { .address = 0x14, .reg = 0x47 },
- { .address = 0x18, .reg = 0x48 },
- { .address = 0x1c, .reg = 0x49 },
- { .address = 0x20, .reg = 0x4a },
- { .address = 0x24, .reg = 0x4b },
- { .address = 0x28, .reg = 0x4c },
- { .address = 0x2c, .reg = 0x4d },
- { .address = 0x30, .reg = 0x4e },
+static const struct pmic_table chtdc_ti_power_table[] = {
+ { .address = 0x00, .reg = 0x41 }, /* LDO1 */
+ { .address = 0x04, .reg = 0x42 }, /* LDO2 */
+ { .address = 0x08, .reg = 0x43 }, /* LDO3 */
+ { .address = 0x0c, .reg = 0x45 }, /* LDO5 */
+ { .address = 0x10, .reg = 0x46 }, /* LDO6 */
+ { .address = 0x14, .reg = 0x47 }, /* LDO7 */
+ { .address = 0x18, .reg = 0x48 }, /* LDO8 */
+ { .address = 0x1c, .reg = 0x49 }, /* LDO9 */
+ { .address = 0x20, .reg = 0x4a }, /* LD10 */
+ { .address = 0x24, .reg = 0x4b }, /* LD11 */
+ { .address = 0x28, .reg = 0x4c }, /* LD12 */
+ { .address = 0x2c, .reg = 0x4d }, /* LD13 */
+ { .address = 0x30, .reg = 0x4e }, /* LD14 */
};
-static struct pmic_table chtdc_ti_thermal_table[] = {
+static const struct pmic_table chtdc_ti_thermal_table[] = {
{
.address = 0x00,
.reg = CHTDC_TI_GPADC
@@ -73,7 +77,7 @@ static int chtdc_ti_pmic_get_power(struct regmap *regmap, int reg, int bit,
if (regmap_read(regmap, reg, &data))
return -EIO;
- *value = data & 1;
+ *value = data & BIT(0);
return 0;
}
@@ -85,13 +89,12 @@ static int chtdc_ti_pmic_update_power(struct regmap *regmap, int reg, int bit,
static int chtdc_ti_pmic_get_raw_temp(struct regmap *regmap, int reg)
{
- u8 buf[2];
+ __be16 buf;
- if (regmap_bulk_read(regmap, reg, buf, 2))
+ if (regmap_bulk_read(regmap, reg, &buf, sizeof(buf)))
return -EIO;
- /* stored in big-endian */
- return ((buf[0] & 0x03) << 8) | buf[1];
+ return be16_to_cpu(buf) & PMIC_REG_MASK;
}
static const struct intel_pmic_opregion_data chtdc_ti_pmic_opregion_data = {
diff --git a/drivers/acpi/pmic/intel_pmic_chtwc.c b/drivers/acpi/pmic/intel_pmic_chtwc.c
index f2c42f4c79ca..81caede51ca2 100644
--- a/drivers/acpi/pmic/intel_pmic_chtwc.c
+++ b/drivers/acpi/pmic/intel_pmic_chtwc.c
@@ -70,7 +70,7 @@
* "regulator: whiskey_cove: implements Whiskey Cove pmic VRF support"
* https://github.com/intel-aero/meta-intel-aero/blob/master/recipes-kernel/linux/linux-yocto/0019-regulator-whiskey_cove-implements-WhiskeyCove-pmic-V.patch
*/
-static struct pmic_table power_table[] = {
+static const struct pmic_table power_table[] = {
{
.address = 0x0,
.reg = CHT_WC_V1P8A_CTRL,
@@ -236,11 +236,12 @@ static int intel_cht_wc_exec_mipi_pmic_seq_element(struct regmap *regmap,
u32 reg_address,
u32 value, u32 mask)
{
+ struct device *dev = regmap_get_device(regmap);
u32 address;
if (i2c_client_address > 0xff || reg_address > 0xff) {
- pr_warn("%s warning addresses too big client 0x%x reg 0x%x\n",
- __func__, i2c_client_address, reg_address);
+ dev_warn(dev, "warning addresses too big client 0x%x reg 0x%x\n",
+ i2c_client_address, reg_address);
return -ERANGE;
}
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index 61bbe4c24d87..49bda5e0c8aa 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -26,7 +26,7 @@
#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0)
#define AXP288_ADC_TS_CURRENT_ON (3 << 0)
-static struct pmic_table power_table[] = {
+static const struct pmic_table power_table[] = {
{
.address = 0x00,
.reg = 0x13,
@@ -129,7 +129,7 @@ static struct pmic_table power_table[] = {
};
/* TMP0 - TMP5 are the same, all from GPADC */
-static struct pmic_table thermal_table[] = {
+static const struct pmic_table thermal_table[] = {
{
.address = 0x00,
.reg = XPOWER_GPADC_LOW
@@ -255,7 +255,7 @@ static int intel_xpower_pmic_get_raw_temp(struct regmap *regmap, int reg)
if (ret)
return ret;
- ret = regmap_bulk_read(regmap, AXP288_GP_ADC_H, buf, 2);
+ ret = regmap_bulk_read(regmap, AXP288_GP_ADC_H, buf, sizeof(buf));
if (ret == 0)
ret = (buf[0] << 4) + ((buf[1] >> 4) & 0x0f);
@@ -274,11 +274,12 @@ static int intel_xpower_exec_mipi_pmic_seq_element(struct regmap *regmap,
u16 i2c_address, u32 reg_address,
u32 value, u32 mask)
{
+ struct device *dev = regmap_get_device(regmap);
int ret;
if (i2c_address != 0x34) {
- pr_err("%s: Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n",
- __func__, i2c_address, reg_address, value, mask);
+ dev_err(dev, "Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n",
+ i2c_address, reg_address, value, mask);
return -ENXIO;
}
diff --git a/drivers/acpi/pmic/tps68470_pmic.c b/drivers/acpi/pmic/tps68470_pmic.c
index ebd03e472955..0d1a82eeb4b0 100644
--- a/drivers/acpi/pmic/tps68470_pmic.c
+++ b/drivers/acpi/pmic/tps68470_pmic.c
@@ -376,10 +376,8 @@ static int tps68470_pmic_opregion_probe(struct platform_device *pdev)
struct tps68470_pmic_opregion *opregion;
acpi_status status;
- if (!dev || !tps68470_regmap) {
- dev_warn(dev, "dev or regmap is NULL\n");
- return -EINVAL;
- }
+ if (!tps68470_regmap)
+ return dev_err_probe(dev, -EINVAL, "regmap is missing\n");
if (!handle) {
dev_warn(dev, "acpi handle is NULL\n");
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 8c4a73a1351e..361a7721a6a8 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -23,11 +23,14 @@
#define pr_fmt(fmt) "ACPI: PM: " fmt
+#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/pm_runtime.h>
#include <linux/sysfs.h>
#include <linux/acpi.h>
@@ -61,6 +64,9 @@ struct acpi_power_resource_entry {
struct acpi_power_resource *resource;
};
+static bool hp_eb_gp12pxp_quirk;
+static bool unused_power_resources_quirk;
+
static LIST_HEAD(acpi_power_resource_list);
static DEFINE_MUTEX(power_resource_list_lock);
@@ -196,7 +202,7 @@ static int __get_state(acpi_handle handle, u8 *state)
cur_state = sta & ACPI_POWER_RESOURCE_STATE_ON;
acpi_handle_debug(handle, "Power resource is %s\n",
- cur_state ? "on" : "off");
+ str_on_off(cur_state));
*state = cur_state;
return 0;
@@ -239,7 +245,7 @@ static int acpi_power_get_list_state(struct list_head *list, u8 *state)
break;
}
- pr_debug("Power resource list is %s\n", cur_state ? "on" : "off");
+ pr_debug("Power resource list is %s\n", str_on_off(cur_state));
*state = cur_state;
return 0;
@@ -944,13 +950,15 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle)
return NULL;
device = &resource->device;
- acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER);
+ acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER,
+ acpi_release_power_resource);
mutex_init(&resource->resource_lock);
INIT_LIST_HEAD(&resource->list_node);
INIT_LIST_HEAD(&resource->dependents);
- strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
+ strscpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_POWER_CLASS);
device->power.state = ACPI_STATE_UNKNOWN;
+ device->flags.match_driver = true;
/* Evaluate the object to get the system level and resource order. */
status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
@@ -965,10 +973,13 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle)
if (acpi_power_get_state(resource, &state_dummy))
__acpi_power_on(resource);
- pr_info("%s [%s]\n", acpi_device_name(device), acpi_device_bid(device));
+ acpi_handle_info(handle, "New power resource\n");
- device->flags.match_driver = true;
- result = acpi_device_add(device, acpi_release_power_resource);
+ result = acpi_tie_acpi_dev(device);
+ if (result)
+ goto err;
+
+ result = acpi_device_add(device);
if (result)
goto err;
@@ -985,6 +996,38 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle)
}
#ifdef CONFIG_ACPI_SLEEP
+static bool resource_is_gp12pxp(acpi_handle handle)
+{
+ const char *path;
+ bool ret;
+
+ path = acpi_handle_path(handle);
+ ret = path && strcmp(path, "\\_SB_.PCI0.GP12.PXP_") == 0;
+ kfree(path);
+
+ return ret;
+}
+
+static void acpi_resume_on_eb_gp12pxp(struct acpi_power_resource *resource)
+{
+ acpi_handle_notice(resource->device.handle,
+ "HP EB quirk - turning OFF then ON\n");
+
+ __acpi_power_off(resource);
+ __acpi_power_on(resource);
+
+ /*
+ * Use the same delay as DSDT uses in modem _RST method.
+ *
+ * Otherwise we get "Unable to change power state from unknown to D0,
+ * device inaccessible" error for the modem PCI device after thaw.
+ *
+ * This power resource is normally being enabled only during thaw (once)
+ * so this wait is not a performance issue.
+ */
+ msleep(200);
+}
+
void acpi_resume_power_resources(void)
{
struct acpi_power_resource *resource;
@@ -1006,8 +1049,14 @@ void acpi_resume_power_resources(void)
if (state == ACPI_POWER_RESOURCE_STATE_OFF
&& resource->ref_count) {
- acpi_handle_debug(resource->device.handle, "Turning ON\n");
- __acpi_power_on(resource);
+ if (hp_eb_gp12pxp_quirk &&
+ resource_is_gp12pxp(resource->device.handle)) {
+ acpi_resume_on_eb_gp12pxp(resource);
+ } else {
+ acpi_handle_debug(resource->device.handle,
+ "Turning ON\n");
+ __acpi_power_on(resource);
+ }
}
mutex_unlock(&resource->resource_lock);
@@ -1017,6 +1066,56 @@ void acpi_resume_power_resources(void)
}
#endif
+static const struct dmi_system_id dmi_hp_elitebook_gp12pxp_quirk[] = {
+/*
+ * This laptop (and possibly similar models too) has power resource called
+ * "GP12.PXP_" for its WWAN modem.
+ *
+ * For this power resource to turn ON power for the modem it needs certain
+ * internal flag called "ONEN" to be set.
+ * This flag only gets set from this power resource "_OFF" method, while the
+ * actual modem power gets turned off during suspend by "GP12.PTS" method
+ * called from the global "_PTS" (Prepare To Sleep) method.
+ * On the other hand, this power resource "_OFF" method implementation just
+ * sets the aforementioned flag without actually doing anything else (it
+ * doesn't contain any code to actually turn off power).
+ *
+ * The above means that when upon hibernation finish we try to set this
+ * power resource back ON since its "_STA" method returns 0 (while the resource
+ * is still considered in use) its "_ON" method won't do anything since
+ * that "ONEN" flag is not set.
+ * Overall, this means the modem is dead until laptop is rebooted since its
+ * power has been cut by "_PTS" and its PCI configuration was lost and not able
+ * to be restored.
+ *
+ * The easiest way to workaround the issue is to call this power resource
+ * "_OFF" method before calling the "_ON" method to make sure the "ONEN"
+ * flag gets properly set.
+ */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 855 G7 Notebook PC"),
+ },
+ },
+ {}
+};
+
+static const struct dmi_system_id dmi_leave_unused_power_resources_on[] = {
+ {
+ /*
+ * The Toshiba Click Mini has a CPR3 power-resource which must
+ * be on for the touchscreen to work, but which is not in any
+ * _PR? lists. The other 2 affected power-resources are no-ops.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE Click Mini L9W-B"),
+ },
+ },
+ {}
+};
+
/**
* acpi_turn_off_unused_power_resources - Turn off power resources not in use.
*/
@@ -1024,6 +1123,9 @@ void acpi_turn_off_unused_power_resources(void)
{
struct acpi_power_resource *resource;
+ if (unused_power_resources_quirk)
+ return;
+
mutex_lock(&power_resource_list_lock);
list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
@@ -1040,3 +1142,10 @@ void acpi_turn_off_unused_power_resources(void)
mutex_unlock(&power_resource_list_lock);
}
+
+void __init acpi_power_resources_init(void)
+{
+ hp_eb_gp12pxp_quirk = dmi_check_system(dmi_hp_elitebook_gp12pxp_quirk);
+ unused_power_resources_quirk =
+ dmi_check_system(dmi_leave_unused_power_resources_on);
+}
diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c
index c91342dcbcd6..de5f8c018333 100644
--- a/drivers/acpi/pptt.c
+++ b/drivers/acpi/pptt.c
@@ -21,6 +21,25 @@
#include <linux/cacheinfo.h>
#include <acpi/processor.h>
+/*
+ * The acpi_pptt_cache_v1 in actbl2.h, which is imported from acpica,
+ * only contains the cache_id field rather than all the fields of the
+ * Cache Type Structure. Use this alternative structure until it is
+ * resolved in acpica.
+ */
+struct acpi_pptt_cache_v1_full {
+ struct acpi_subtable_header header;
+ u16 reserved;
+ u32 flags;
+ u32 next_level_of_cache;
+ u32 size;
+ u32 number_of_sets;
+ u8 associativity;
+ u8 attributes;
+ u16 line_size;
+ u32 cache_id;
+} __packed;
+
static struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr,
u32 pptt_ref)
{
@@ -56,6 +75,18 @@ static struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_
return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref);
}
+static struct acpi_pptt_cache_v1_full *upgrade_pptt_cache(struct acpi_pptt_cache *cache)
+{
+ if (cache->header.length < sizeof(struct acpi_pptt_cache_v1_full))
+ return NULL;
+
+ /* No use for v1 if the only additional field is invalid */
+ if (!(cache->flags & ACPI_PPTT_CACHE_ID_VALID))
+ return NULL;
+
+ return (struct acpi_pptt_cache_v1_full *)cache;
+}
+
static struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *node,
int resource)
@@ -81,6 +112,7 @@ static inline bool acpi_pptt_match_type(int table_type, int type)
* acpi_pptt_walk_cache() - Attempt to find the requested acpi_pptt_cache
* @table_hdr: Pointer to the head of the PPTT table
* @local_level: passed res reflects this cache level
+ * @split_levels: Number of split cache levels (data/instruction).
* @res: cache resource in the PPTT we want to walk
* @found: returns a pointer to the requested level if found
* @level: the requested cache level
@@ -100,6 +132,7 @@ static inline bool acpi_pptt_match_type(int table_type, int type)
*/
static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr,
unsigned int local_level,
+ unsigned int *split_levels,
struct acpi_subtable_header *res,
struct acpi_pptt_cache **found,
unsigned int level, int type)
@@ -113,8 +146,17 @@ static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr,
while (cache) {
local_level++;
+ if (!(cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)) {
+ cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache);
+ continue;
+ }
+
+ if (split_levels &&
+ (acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_DATA) ||
+ acpi_pptt_match_type(cache->attributes, ACPI_PPTT_CACHE_TYPE_INSTR)))
+ *split_levels = local_level;
+
if (local_level == level &&
- cache->flags & ACPI_PPTT_CACHE_TYPE_VALID &&
acpi_pptt_match_type(cache->attributes, type)) {
if (*found != NULL && cache != *found)
pr_warn("Found duplicate cache level/type unable to determine uniqueness\n");
@@ -135,8 +177,8 @@ static unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr,
static struct acpi_pptt_cache *
acpi_find_cache_level(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *cpu_node,
- unsigned int *starting_level, unsigned int level,
- int type)
+ unsigned int *starting_level, unsigned int *split_levels,
+ unsigned int level, int type)
{
struct acpi_subtable_header *res;
unsigned int number_of_levels = *starting_level;
@@ -149,7 +191,8 @@ acpi_find_cache_level(struct acpi_table_header *table_hdr,
resource++;
local_level = acpi_pptt_walk_cache(table_hdr, *starting_level,
- res, &ret, level, type);
+ split_levels, res, &ret,
+ level, type);
/*
* we are looking for the max depth. Since its potentially
* possible for a given node to have resources with differing
@@ -165,29 +208,33 @@ acpi_find_cache_level(struct acpi_table_header *table_hdr,
}
/**
- * acpi_count_levels() - Given a PPTT table, and a CPU node, count the caches
+ * acpi_count_levels() - Given a PPTT table, and a CPU node, count the
+ * total number of levels and split cache levels (data/instruction).
* @table_hdr: Pointer to the head of the PPTT table
* @cpu_node: processor node we wish to count caches for
+ * @split_levels: Number of split cache levels (data/instruction) if
+ * success. Can by NULL.
*
+ * Return: number of levels.
* Given a processor node containing a processing unit, walk into it and count
* how many levels exist solely for it, and then walk up each level until we hit
* the root node (ignore the package level because it may be possible to have
- * caches that exist across packages). Count the number of cache levels that
- * exist at each level on the way up.
- *
- * Return: Total number of levels found.
+ * caches that exist across packages). Count the number of cache levels and
+ * split cache levels (data/instruction) that exist at each level on the way
+ * up.
*/
static int acpi_count_levels(struct acpi_table_header *table_hdr,
- struct acpi_pptt_processor *cpu_node)
+ struct acpi_pptt_processor *cpu_node,
+ unsigned int *split_levels)
{
- int total_levels = 0;
+ int current_level = 0;
do {
- acpi_find_cache_level(table_hdr, cpu_node, &total_levels, 0, 0);
+ acpi_find_cache_level(table_hdr, cpu_node, &current_level, split_levels, 0, 0);
cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
} while (cpu_node);
- return total_levels;
+ return current_level;
}
/**
@@ -217,18 +264,20 @@ static int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr,
node_entry = ACPI_PTR_DIFF(node, table_hdr);
entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
sizeof(struct acpi_table_pptt));
- proc_sz = sizeof(struct acpi_pptt_processor *);
+ proc_sz = sizeof(struct acpi_pptt_processor);
- while ((unsigned long)entry + proc_sz < table_end) {
+ /* ignore subtable types that are smaller than a processor node */
+ while ((unsigned long)entry + proc_sz <= table_end) {
cpu_node = (struct acpi_pptt_processor *)entry;
+
if (entry->type == ACPI_PPTT_TYPE_PROCESSOR &&
cpu_node->parent == node_entry)
return 0;
if (entry->length == 0)
return 0;
+
entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry,
entry->length);
-
}
return 1;
}
@@ -258,18 +307,21 @@ static struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_he
table_end = (unsigned long)table_hdr + table_hdr->length;
entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
sizeof(struct acpi_table_pptt));
- proc_sz = sizeof(struct acpi_pptt_processor *);
+ proc_sz = sizeof(struct acpi_pptt_processor);
/* find the processor structure associated with this cpuid */
- while ((unsigned long)entry + proc_sz < table_end) {
+ while ((unsigned long)entry + proc_sz <= table_end) {
cpu_node = (struct acpi_pptt_processor *)entry;
if (entry->length == 0) {
pr_warn("Invalid zero length subtable\n");
break;
}
+ /* entry->length may not equal proc_sz, revalidate the processor structure length */
if (entry->type == ACPI_PPTT_TYPE_PROCESSOR &&
acpi_cpu_id == cpu_node->acpi_processor_id &&
+ (unsigned long)entry + entry->length <= table_end &&
+ entry->length == proc_sz + cpu_node->number_of_priv_resources * sizeof(u32) &&
acpi_pptt_leaf_node(table_hdr, cpu_node)) {
return (struct acpi_pptt_processor *)entry;
}
@@ -281,19 +333,6 @@ static struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_he
return NULL;
}
-static int acpi_find_cache_levels(struct acpi_table_header *table_hdr,
- u32 acpi_cpu_id)
-{
- int number_of_levels = 0;
- struct acpi_pptt_processor *cpu;
-
- cpu = acpi_find_processor_node(table_hdr, acpi_cpu_id);
- if (cpu)
- number_of_levels = acpi_count_levels(table_hdr, cpu);
-
- return number_of_levels;
-}
-
static u8 acpi_cache_type(enum cache_type type)
{
switch (type) {
@@ -334,7 +373,7 @@ static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *ta
while (cpu_node && !found) {
found = acpi_find_cache_level(table_hdr, cpu_node,
- &total_levels, level, acpi_type);
+ &total_levels, NULL, level, acpi_type);
*node = cpu_node;
cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
}
@@ -347,7 +386,6 @@ static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *ta
* @this_leaf: Kernel cache info structure being updated
* @found_cache: The PPTT node describing this cache instance
* @cpu_node: A unique reference to describe this cache instance
- * @revision: The revision of the PPTT table
*
* The ACPI spec implies that the fields in the cache structures are used to
* extend and correct the information probed from the hardware. Lets only
@@ -357,10 +395,9 @@ static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *ta
*/
static void update_cache_properties(struct cacheinfo *this_leaf,
struct acpi_pptt_cache *found_cache,
- struct acpi_pptt_processor *cpu_node,
- u8 revision)
+ struct acpi_pptt_processor *cpu_node)
{
- struct acpi_pptt_cache_v1* found_cache_v1;
+ struct acpi_pptt_cache_v1_full *found_cache_v1;
this_leaf->fw_token = cpu_node;
if (found_cache->flags & ACPI_PPTT_SIZE_PROPERTY_VALID)
@@ -410,9 +447,8 @@ static void update_cache_properties(struct cacheinfo *this_leaf,
found_cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)
this_leaf->type = CACHE_TYPE_UNIFIED;
- if (revision >= 3 && (found_cache->flags & ACPI_PPTT_CACHE_ID_VALID)) {
- found_cache_v1 = ACPI_ADD_PTR(struct acpi_pptt_cache_v1,
- found_cache, sizeof(struct acpi_pptt_cache));
+ found_cache_v1 = upgrade_pptt_cache(found_cache);
+ if (found_cache_v1) {
this_leaf->id = found_cache_v1->cache_id;
this_leaf->attributes |= CACHE_ID;
}
@@ -437,8 +473,7 @@ static void cache_setup_acpi_cpu(struct acpi_table_header *table,
pr_debug("found = %p %p\n", found_cache, cpu_node);
if (found_cache)
update_cache_properties(this_leaf, found_cache,
- ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)),
- table->revision);
+ ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)));
index++;
}
@@ -537,16 +572,19 @@ static int topology_get_acpi_cpu_tag(struct acpi_table_header *table,
static struct acpi_table_header *acpi_get_pptt(void)
{
static struct acpi_table_header *pptt;
+ static bool is_pptt_checked;
acpi_status status;
/*
* PPTT will be used at runtime on every CPU hotplug in path, so we
* don't need to call acpi_put_table() to release the table mapping.
*/
- if (!pptt) {
+ if (!pptt && !is_pptt_checked) {
status = acpi_get_table(ACPI_SIG_PPTT, 0, &pptt);
if (ACPI_FAILURE(status))
acpi_pptt_warn_missing();
+
+ is_pptt_checked = true;
}
return pptt;
@@ -602,32 +640,48 @@ static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
}
/**
- * acpi_find_last_cache_level() - Determines the number of cache levels for a PE
+ * acpi_get_cache_info() - Determine the number of cache levels and
+ * split cache levels (data/instruction) and for a PE.
* @cpu: Kernel logical CPU number
+ * @levels: Number of levels if success.
+ * @split_levels: Number of levels being split (i.e. data/instruction)
+ * if success. Can by NULL.
*
* Given a logical CPU number, returns the number of levels of cache represented
* in the PPTT. Errors caused by lack of a PPTT table, or otherwise, return 0
* indicating we didn't find any cache levels.
*
- * Return: Cache levels visible to this core.
+ * Return: -ENOENT if no PPTT table or no PPTT processor struct found.
+ * 0 on success.
*/
-int acpi_find_last_cache_level(unsigned int cpu)
+int acpi_get_cache_info(unsigned int cpu, unsigned int *levels,
+ unsigned int *split_levels)
{
- u32 acpi_cpu_id;
+ struct acpi_pptt_processor *cpu_node;
struct acpi_table_header *table;
- int number_of_levels = 0;
+ u32 acpi_cpu_id;
+
+ *levels = 0;
+ if (split_levels)
+ *split_levels = 0;
table = acpi_get_pptt();
if (!table)
return -ENOENT;
- pr_debug("Cache Setup find last level CPU=%d\n", cpu);
+ pr_debug("Cache Setup: find cache levels for CPU=%d\n", cpu);
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
- number_of_levels = acpi_find_cache_levels(table, acpi_cpu_id);
- pr_debug("Cache Setup find last level level=%d\n", number_of_levels);
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (!cpu_node)
+ return -ENOENT;
+
+ *levels = acpi_count_levels(table, cpu_node, split_levels);
+
+ pr_debug("Cache Setup: last_level=%d split_levels=%d\n",
+ *levels, split_levels ? *split_levels : -1);
- return number_of_levels;
+ return 0;
}
/**
@@ -794,3 +848,218 @@ int find_acpi_cpu_topology_hetero_id(unsigned int cpu)
return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE,
ACPI_PPTT_ACPI_IDENTICAL);
}
+
+/**
+ * acpi_pptt_get_child_cpus() - Find all the CPUs below a PPTT
+ * processor hierarchy node
+ *
+ * @table_hdr: A reference to the PPTT table
+ * @parent_node: A pointer to the processor hierarchy node in the
+ * table_hdr
+ * @cpus: A cpumask to fill with the CPUs below @parent_node
+ *
+ * Walks up the PPTT from every possible CPU to find if the provided
+ * @parent_node is a parent of this CPU.
+ */
+static void acpi_pptt_get_child_cpus(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *parent_node,
+ cpumask_t *cpus)
+{
+ struct acpi_pptt_processor *cpu_node;
+ u32 acpi_id;
+ int cpu;
+
+ cpumask_clear(cpus);
+
+ for_each_possible_cpu(cpu) {
+ acpi_id = get_acpi_id_for_cpu(cpu);
+ cpu_node = acpi_find_processor_node(table_hdr, acpi_id);
+
+ while (cpu_node) {
+ if (cpu_node == parent_node) {
+ cpumask_set_cpu(cpu, cpus);
+ break;
+ }
+ cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
+ }
+ }
+}
+
+/**
+ * acpi_pptt_get_cpus_from_container() - Populate a cpumask with all CPUs in a
+ * processor container
+ * @acpi_cpu_id: The UID of the processor container
+ * @cpus: The resulting CPU mask
+ *
+ * Find the specified Processor Container, and fill @cpus with all the cpus
+ * below it.
+ *
+ * Not all 'Processor Hierarchy' entries in the PPTT are either a CPU
+ * or a Processor Container, they may exist purely to describe a
+ * Private resource. CPUs have to be leaves, so a Processor Container
+ * is a non-leaf that has the 'ACPI Processor ID valid' flag set.
+ */
+void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus)
+{
+ struct acpi_table_header *table_hdr;
+ struct acpi_subtable_header *entry;
+ unsigned long table_end;
+ u32 proc_sz;
+
+ cpumask_clear(cpus);
+
+ table_hdr = acpi_get_pptt();
+ if (!table_hdr)
+ return;
+
+ table_end = (unsigned long)table_hdr + table_hdr->length;
+ entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
+ sizeof(struct acpi_table_pptt));
+ proc_sz = sizeof(struct acpi_pptt_processor);
+ while ((unsigned long)entry + proc_sz <= table_end) {
+ if (entry->type == ACPI_PPTT_TYPE_PROCESSOR) {
+ struct acpi_pptt_processor *cpu_node;
+
+ cpu_node = (struct acpi_pptt_processor *)entry;
+ if (cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID &&
+ !acpi_pptt_leaf_node(table_hdr, cpu_node) &&
+ cpu_node->acpi_processor_id == acpi_cpu_id) {
+ acpi_pptt_get_child_cpus(table_hdr, cpu_node, cpus);
+ break;
+ }
+ }
+ entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry,
+ entry->length);
+ }
+}
+
+/**
+ * find_acpi_cache_level_from_id() - Get the level of the specified cache
+ * @cache_id: The id field of the cache
+ *
+ * Determine the level relative to any CPU for the cache identified by
+ * cache_id. This allows the property to be found even if the CPUs are offline.
+ *
+ * The returned level can be used to group caches that are peers.
+ *
+ * The PPTT table must be rev 3 or later.
+ *
+ * If one CPU's L2 is shared with another CPU as L3, this function will return
+ * an unpredictable value.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, the revision isn't supported or
+ * the cache cannot be found.
+ * Otherwise returns a value which represents the level of the specified cache.
+ */
+int find_acpi_cache_level_from_id(u32 cache_id)
+{
+ int cpu;
+ struct acpi_table_header *table;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ if (table->revision < 3)
+ return -ENOENT;
+
+ for_each_possible_cpu(cpu) {
+ bool empty;
+ int level = 1;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ struct acpi_pptt_cache *cache;
+ struct acpi_pptt_processor *cpu_node;
+
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (!cpu_node)
+ continue;
+
+ do {
+ int cache_type[] = {CACHE_TYPE_INST, CACHE_TYPE_DATA, CACHE_TYPE_UNIFIED};
+
+ empty = true;
+ for (int i = 0; i < ARRAY_SIZE(cache_type); i++) {
+ struct acpi_pptt_cache_v1_full *cache_v1;
+
+ cache = acpi_find_cache_node(table, acpi_cpu_id, cache_type[i],
+ level, &cpu_node);
+ if (!cache)
+ continue;
+
+ empty = false;
+
+ cache_v1 = upgrade_pptt_cache(cache);
+ if (cache_v1 && cache_v1->cache_id == cache_id)
+ return level;
+ }
+ level++;
+ } while (!empty);
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * acpi_pptt_get_cpumask_from_cache_id() - Get the cpus associated with the
+ * specified cache
+ * @cache_id: The id field of the cache
+ * @cpus: Where to build the cpumask
+ *
+ * Determine which CPUs are below this cache in the PPTT. This allows the property
+ * to be found even if the CPUs are offline.
+ *
+ * The PPTT table must be rev 3 or later,
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, or the cache cannot be found.
+ * Otherwise returns 0 and sets the cpus in the provided cpumask.
+ */
+int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, cpumask_t *cpus)
+{
+ int cpu;
+ struct acpi_table_header *table;
+
+ cpumask_clear(cpus);
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ if (table->revision < 3)
+ return -ENOENT;
+
+ for_each_possible_cpu(cpu) {
+ bool empty;
+ int level = 1;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ struct acpi_pptt_cache *cache;
+ struct acpi_pptt_processor *cpu_node;
+
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (!cpu_node)
+ continue;
+
+ do {
+ int cache_type[] = {CACHE_TYPE_INST, CACHE_TYPE_DATA, CACHE_TYPE_UNIFIED};
+
+ empty = true;
+ for (int i = 0; i < ARRAY_SIZE(cache_type); i++) {
+ struct acpi_pptt_cache_v1_full *cache_v1;
+
+ cache = acpi_find_cache_node(table, acpi_cpu_id, cache_type[i],
+ level, &cpu_node);
+
+ if (!cache)
+ continue;
+
+ empty = false;
+
+ cache_v1 = upgrade_pptt_cache(cache);
+ if (cache_v1 && cache_v1->cache_id == cache_id)
+ cpumask_set_cpu(cpu, cpus);
+ }
+ level++;
+ } while (!empty);
+ }
+
+ return 0;
+}
diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
index 998101cf16e4..7b8b5d2015ec 100644
--- a/drivers/acpi/prmt.c
+++ b/drivers/acpi/prmt.c
@@ -52,8 +52,8 @@ struct prm_context_buffer {
static LIST_HEAD(prm_module_list);
struct prm_handler_info {
- guid_t guid;
- void *handler_addr;
+ efi_guid_t guid;
+ efi_status_t (__efiapi *handler_addr)(u64, void *);
u64 static_data_buffer_addr;
u64 acpi_param_buffer_addr;
@@ -69,18 +69,20 @@ struct prm_module_info {
bool updatable;
struct list_head module_list;
- struct prm_handler_info handlers[];
+ struct prm_handler_info handlers[] __counted_by(handler_count);
};
-static u64 efi_pa_va_lookup(u64 pa)
+static u64 efi_pa_va_lookup(efi_guid_t *guid, u64 pa)
{
efi_memory_desc_t *md;
u64 pa_offset = pa & ~PAGE_MASK;
u64 page = pa & PAGE_MASK;
for_each_efi_memory_desc(md) {
- if (md->phys_addr < pa && pa < md->phys_addr + PAGE_SIZE * md->num_pages)
+ if ((md->attribute & EFI_MEMORY_RUNTIME) &&
+ (md->phys_addr < pa && pa < md->phys_addr + PAGE_SIZE * md->num_pages)) {
return pa_offset + md->virt_addr + page - md->phys_addr;
+ }
}
return 0;
@@ -148,9 +150,52 @@ acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
th = &tm->handlers[cur_handler];
guid_copy(&th->guid, (guid_t *)handler_info->handler_guid);
- th->handler_addr = (void *)efi_pa_va_lookup(handler_info->handler_address);
- th->static_data_buffer_addr = efi_pa_va_lookup(handler_info->static_data_buffer_address);
- th->acpi_param_buffer_addr = efi_pa_va_lookup(handler_info->acpi_param_buffer_address);
+
+ /*
+ * Print an error message if handler_address is NULL, the parse of VA also
+ * can be skipped.
+ */
+ if (unlikely(!handler_info->handler_address)) {
+ pr_info("Skipping handler with NULL address for GUID: %pUL",
+ (guid_t *)handler_info->handler_guid);
+ continue;
+ }
+
+ th->handler_addr =
+ (void *)efi_pa_va_lookup(&th->guid, handler_info->handler_address);
+ /*
+ * Print a warning message and skip the parse of VA if handler_addr is zero
+ * which is not expected to ever happen.
+ */
+ if (unlikely(!th->handler_addr)) {
+ pr_warn("Failed to find VA of handler for GUID: %pUL, PA: 0x%llx",
+ &th->guid, handler_info->handler_address);
+ continue;
+ }
+
+ th->static_data_buffer_addr =
+ efi_pa_va_lookup(&th->guid, handler_info->static_data_buffer_address);
+ /*
+ * According to the PRM specification, static_data_buffer_address can be zero,
+ * so avoid printing a warning message in that case. Otherwise, if the
+ * return value of efi_pa_va_lookup() is zero, print the message.
+ */
+ if (unlikely(!th->static_data_buffer_addr && handler_info->static_data_buffer_address))
+ pr_warn("Failed to find VA of static data buffer for GUID: %pUL, PA: 0x%llx",
+ &th->guid, handler_info->static_data_buffer_address);
+
+ th->acpi_param_buffer_addr =
+ efi_pa_va_lookup(&th->guid, handler_info->acpi_param_buffer_address);
+
+ /*
+ * According to the PRM specification, acpi_param_buffer_address can be zero,
+ * so avoid printing a warning message in that case. Otherwise, if the
+ * return value of efi_pa_va_lookup() is zero, print the message.
+ */
+ if (unlikely(!th->acpi_param_buffer_addr && handler_info->acpi_param_buffer_address))
+ pr_warn("Failed to find VA of acpi param buffer for GUID: %pUL, PA: 0x%llx",
+ &th->guid, handler_info->acpi_param_buffer_address);
+
} while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
return 0;
@@ -199,6 +244,12 @@ static struct prm_handler_info *find_prm_handler(const guid_t *guid)
return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
}
+bool acpi_prm_handler_available(const guid_t *guid)
+{
+ return find_prm_handler(guid) && find_prm_module(guid);
+}
+EXPORT_SYMBOL_GPL(acpi_prm_handler_available);
+
/* In-coming PRM commands */
#define PRM_CMD_RUN_SERVICE 0
@@ -214,6 +265,30 @@ static struct prm_handler_info *find_prm_handler(const guid_t *guid)
#define UPDATE_LOCK_ALREADY_HELD 4
#define UPDATE_UNLOCK_WITHOUT_LOCK 5
+int acpi_call_prm_handler(guid_t handler_guid, void *param_buffer)
+{
+ struct prm_handler_info *handler = find_prm_handler(&handler_guid);
+ struct prm_module_info *module = find_prm_module(&handler_guid);
+ struct prm_context_buffer context;
+ efi_status_t status;
+
+ if (!module || !handler)
+ return -ENODEV;
+
+ memset(&context, 0, sizeof(context));
+ ACPI_COPY_NAMESEG(context.signature, "PRMC");
+ context.identifier = handler->guid;
+ context.static_data_buffer = handler->static_data_buffer_addr;
+ context.mmio_ranges = module->mmio_info;
+
+ status = efi_call_acpi_prm_handler(handler->handler_addr,
+ (u64)param_buffer,
+ &context);
+
+ return efi_status_to_err(status);
+}
+EXPORT_SYMBOL_GPL(acpi_call_prm_handler);
+
/*
* This is the PlatformRtMechanism opregion space handler.
* @function: indicates the read/write. In fact as the PlatformRtMechanism
@@ -236,6 +311,11 @@ static acpi_status acpi_platformrt_space_handler(u32 function,
efi_status_t status;
struct prm_context_buffer context;
+ if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+ pr_err_ratelimited("PRM: EFI runtime services no longer available\n");
+ return AE_NO_HANDLER;
+ }
+
/*
* The returned acpi_status will always be AE_OK. Error values will be
* saved in the first byte of the PRM message buffer to be used by ASL.
@@ -248,6 +328,11 @@ static acpi_status acpi_platformrt_space_handler(u32 function,
if (!handler || !module)
goto invalid_guid;
+ if (!handler->handler_addr) {
+ buffer->prm_status = PRM_HANDLER_ERROR;
+ return AE_OK;
+ }
+
ACPI_COPY_NAMESEG(context.signature, "PRMC");
context.revision = 0x0;
context.reserved = 0x0;
@@ -255,9 +340,9 @@ static acpi_status acpi_platformrt_space_handler(u32 function,
context.static_data_buffer = handler->static_data_buffer_addr;
context.mmio_ranges = module->mmio_info;
- status = efi_call_virt_pointer(handler, handler_addr,
- handler->acpi_param_buffer_addr,
- &context);
+ status = efi_call_acpi_prm_handler(handler->handler_addr,
+ handler->acpi_param_buffer_addr,
+ &context);
if (status == EFI_SUCCESS) {
buffer->prm_status = PRM_HANDLER_SUCCESS;
} else {
@@ -325,6 +410,11 @@ void __init init_prmt(void)
pr_info("PRM: found %u modules\n", mc);
+ if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+ pr_err("PRM: EFI runtime services unavailable\n");
+ return;
+ }
+
status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_PLATFORM_RT,
&acpi_platformrt_space_handler,
diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
index 4322f2da6d10..c08ead07252b 100644
--- a/drivers/acpi/proc.c
+++ b/drivers/acpi/proc.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#include <linux/export.h>
+#include <linux/string_choices.h>
#include <linux/suspend.h>
#include <linux/bcd.h>
#include <linux/acpi.h>
@@ -30,17 +30,16 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
if (!dev->wakeup.flags.valid)
continue;
- seq_printf(seq, "%s\t S%d\t",
+ seq_printf(seq, "%s\t S%llu\t",
dev->pnp.bus_id,
- (u32) dev->wakeup.sleep_state);
+ dev->wakeup.sleep_state);
mutex_lock(&dev->physical_node_lock);
if (!dev->physical_node_count) {
seq_printf(seq, "%c%-8s\n",
dev->wakeup.flags.valid ? '*' : ' ',
- device_may_wakeup(&dev->dev) ?
- "enabled" : "disabled");
+ str_enabled_disabled(device_may_wakeup(&dev->dev)));
} else {
struct device *ldev;
list_for_each_entry(entry, &dev->physical_node_list,
@@ -55,9 +54,8 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
seq_printf(seq, "%c%-8s %s:%s\n",
dev->wakeup.flags.valid ? '*' : ' ',
- (device_may_wakeup(&dev->dev) ||
- device_may_wakeup(ldev)) ?
- "enabled" : "disabled",
+ str_enabled_disabled(device_may_wakeup(ldev) ||
+ device_may_wakeup(&dev->dev)),
ldev->bus ? ldev->bus->name :
"no-bus", dev_name(ldev));
put_device(ldev);
@@ -141,6 +139,5 @@ static const struct proc_ops acpi_system_wakeup_device_proc_ops = {
void __init acpi_sleep_proc_init(void)
{
/* 'wakeup device' [R/W] */
- proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
- acpi_root_dir, &acpi_system_wakeup_device_proc_ops);
+ proc_create("wakeup", 0644, acpi_root_dir, &acpi_system_wakeup_device_proc_ops);
}
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 2ac48cda5b20..a4498357bd16 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -54,7 +54,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry,
if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
- if (device_declaration && (apic->uid == acpi_id)) {
+ if (apic->uid == acpi_id && (device_declaration || acpi_id < 255)) {
*apic_id = apic->local_apic_id;
return 0;
}
@@ -90,7 +90,8 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
struct acpi_madt_generic_interrupt *gicc =
container_of(entry, struct acpi_madt_generic_interrupt, header);
- if (!(gicc->flags & ACPI_MADT_ENABLED))
+ if (!(gicc->flags &
+ (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
return -ENODEV;
/* device_declaration means Device object in DSDT, in the
@@ -106,6 +107,56 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
return -EINVAL;
}
+/*
+ * Retrieve the RISC-V hartid for the processor
+ */
+static int map_rintc_hartid(struct acpi_subtable_header *entry,
+ int device_declaration, u32 acpi_id,
+ phys_cpuid_t *hartid)
+{
+ struct acpi_madt_rintc *rintc =
+ container_of(entry, struct acpi_madt_rintc, header);
+
+ if (!(rintc->flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ /* device_declaration means Device object in DSDT, in the
+ * RISC-V, logical processors are required to
+ * have a Processor Device object in the DSDT, so we should
+ * check device_declaration here
+ */
+ if (device_declaration && rintc->uid == acpi_id) {
+ *hartid = rintc->hart_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Retrieve LoongArch CPU physical id
+ */
+static int map_core_pic_id(struct acpi_subtable_header *entry,
+ int device_declaration, u32 acpi_id, phys_cpuid_t *phys_id)
+{
+ struct acpi_madt_core_pic *core_pic =
+ container_of(entry, struct acpi_madt_core_pic, header);
+
+ if (!(core_pic->flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ /* device_declaration means Device object in DSDT, in LoongArch
+ * system, logical processor acpi_id is required in _UID property
+ * of DSDT table, so we should check device_declaration here
+ */
+ if (device_declaration && (core_pic->processor_id == acpi_id)) {
+ *phys_id = core_pic->core_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
int type, u32 acpi_id)
{
@@ -136,6 +187,12 @@ static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
} else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
break;
+ } else if (header->type == ACPI_MADT_TYPE_RINTC) {
+ if (!map_rintc_hartid(header, type, acpi_id, &phys_id))
+ break;
+ } else if (header->type == ACPI_MADT_TYPE_CORE_PIC) {
+ if (!map_core_pic_id(header, type, acpi_id, &phys_id))
+ break;
}
entry += header->length;
}
@@ -159,6 +216,21 @@ phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
return rv;
}
+int __init acpi_get_madt_revision(void)
+{
+ struct acpi_table_header *madt = NULL;
+ int revision;
+
+ if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, &madt)))
+ return -EINVAL;
+
+ revision = madt->revision;
+
+ acpi_put_table(madt);
+
+ return revision;
+}
+
static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -187,6 +259,8 @@ static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
map_x2apic_id(header, type, acpi_id, &phys_id);
else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
map_gicc_mpidr(header, type, acpi_id, &phys_id);
+ else if (header->type == ACPI_MADT_TYPE_CORE_PIC)
+ map_core_pic_id(header, type, acpi_id, &phys_id);
exit:
kfree(buffer.pointer);
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 1278969eec1f..65e779be64ff 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -27,12 +27,12 @@
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82
+#define ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED 0x85
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI Processor Driver");
MODULE_LICENSE("GPL");
-static int acpi_processor_start(struct device *dev);
static int acpi_processor_stop(struct device *dev);
static const struct acpi_device_id processor_device_ids[] = {
@@ -46,7 +46,6 @@ static struct device_driver acpi_processor_driver = {
.name = "processor",
.bus = &cpu_subsys,
.acpi_match_table = processor_device_ids,
- .probe = acpi_processor_start,
.remove = acpi_processor_stop,
};
@@ -83,6 +82,11 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
acpi_bus_generate_netlink_event(device->pnp.device_class,
dev_name(&device->dev), event, 0);
break;
+ case ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED:
+ cpufreq_update_limits(pr->id);
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event, 0);
+ break;
default:
acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event);
break;
@@ -109,12 +113,9 @@ static int acpi_soft_cpu_online(unsigned int cpu)
* CPU got physically hotplugged and onlined for the first time:
* Initialize missing things.
*/
- if (pr->flags.need_hotplug_init) {
+ if (!pr->flags.previously_online) {
int ret;
- pr_info("Will online and init hotplugged CPU: %d\n",
- pr->id);
- pr->flags.need_hotplug_init = 0;
ret = __acpi_processor_start(device);
WARN(ret, "Failed to start CPU: %d\n", pr->id);
} else {
@@ -161,9 +162,6 @@ static int __acpi_processor_start(struct acpi_device *device)
if (!pr)
return -ENODEV;
- if (pr->flags.need_hotplug_init)
- return 0;
-
result = acpi_cppc_processor_probe(pr);
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
dev_dbg(&device->dev, "CPPC data invalid or not present\n");
@@ -179,32 +177,21 @@ static int __acpi_processor_start(struct acpi_device *device)
status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
acpi_processor_notify, device);
- if (ACPI_SUCCESS(status))
- return 0;
+ if (!ACPI_SUCCESS(status)) {
+ result = -ENODEV;
+ goto err_thermal_exit;
+ }
+ pr->flags.previously_online = 1;
- result = -ENODEV;
- acpi_processor_thermal_exit(pr, device);
+ return 0;
+err_thermal_exit:
+ acpi_processor_thermal_exit(pr, device);
err_power_exit:
acpi_processor_power_exit(pr);
return result;
}
-static int acpi_processor_start(struct device *dev)
-{
- struct acpi_device *device = ACPI_COMPANION(dev);
- int ret;
-
- if (!device)
- return -ENODEV;
-
- /* Protect against concurrent CPU hotplug operations */
- cpu_hotplug_disable();
- ret = __acpi_processor_start(device);
- cpu_hotplug_enable();
- return ret;
-}
-
static int acpi_processor_stop(struct device *dev)
{
struct acpi_device *device = ACPI_COMPANION(dev);
@@ -250,6 +237,9 @@ static struct notifier_block acpi_processor_notifier_block = {
.notifier_call = acpi_processor_notifier,
};
+void __weak acpi_processor_init_invariance_cppc(void)
+{ }
+
/*
* We keep the driver loaded even when ACPI is not running.
* This is needed for the powernow-k8 driver, that works even without
@@ -263,26 +253,35 @@ static int __init acpi_processor_driver_init(void)
if (acpi_disabled)
return 0;
+ if (!cpufreq_register_notifier(&acpi_processor_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER)) {
+ acpi_processor_cpufreq_init = true;
+ acpi_processor_ignore_ppc_init();
+ }
+
result = driver_register(&acpi_processor_driver);
if (result < 0)
return result;
- result = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
- "acpi/cpu-drv:online",
- acpi_soft_cpu_online, NULL);
+ result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "acpi/cpu-drv:online",
+ acpi_soft_cpu_online, NULL);
if (result < 0)
goto err;
hp_online = result;
cpuhp_setup_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD, "acpi/cpu-drv:dead",
NULL, acpi_soft_cpu_dead);
- if (!cpufreq_register_notifier(&acpi_processor_notifier_block,
- CPUFREQ_POLICY_NOTIFIER)) {
- acpi_processor_cpufreq_init = true;
- acpi_processor_ignore_ppc_init();
- }
-
acpi_processor_throttling_init();
+
+ /*
+ * Frequency invariance calculations on AMD platforms can't be run until
+ * after acpi_cppc_processor_probe() has been called for all online CPUs
+ */
+ acpi_processor_init_invariance_cppc();
+
+ acpi_idle_rescan_dead_smt_siblings();
+
return 0;
err:
driver_unregister(&acpi_processor_driver);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 16a1663d02d4..89f2f08b2554 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -16,7 +16,6 @@
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/sched.h> /* need_resched() */
-#include <linux/sort.h>
#include <linux/tick.h>
#include <linux/cpuidle.h>
#include <linux/cpu.h>
@@ -25,6 +24,8 @@
#include <acpi/processor.h>
#include <linux/context_tracking.h>
+#include "internal.h"
+
/*
* Include the apic definitions for x86 to have the APIC timer related defines
* available also for UP (on SMP it gets magically included via linux/smp.h).
@@ -56,6 +57,12 @@ struct cpuidle_driver acpi_idle_driver = {
};
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
+void acpi_idle_rescan_dead_smt_siblings(void)
+{
+ if (cpuidle_get_driver() == &acpi_idle_driver)
+ arch_cpu_rescan_dead_smt_siblings();
+}
+
static
DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], acpi_cstate);
@@ -109,8 +116,8 @@ static const struct dmi_system_id processor_power_dmi_table[] = {
static void __cpuidle acpi_safe_halt(void)
{
if (!tif_need_resched()) {
- safe_halt();
- local_irq_disable();
+ raw_safe_halt();
+ raw_local_irq_disable();
}
}
@@ -147,7 +154,7 @@ static void lapic_timer_check_state(int state, struct acpi_processor *pr,
static void __lapic_timer_propagate_broadcast(void *arg)
{
- struct acpi_processor *pr = (struct acpi_processor *) arg;
+ struct acpi_processor *pr = arg;
if (pr->power.timer_broadcast_on_state < INT_MAX)
tick_broadcast_enable();
@@ -269,6 +276,10 @@ static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr)
ACPI_CX_DESC_LEN, "ACPI P_LVL3 IOPORT 0x%x",
pr->power.states[ACPI_STATE_C3].address);
+ if (!pr->power.states[ACPI_STATE_C2].address &&
+ !pr->power.states[ACPI_STATE_C3].address)
+ return -ENODEV;
+
return 0;
}
@@ -324,7 +335,7 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
* the erratum), but this is known to disrupt certain ISA
* devices thus we take the conservative approach.
*/
- else if (errata.piix4.fdma) {
+ if (errata.piix4.fdma) {
acpi_handle_debug(pr->handle,
"C3 not supported on PIIX4 with Type-F DMA\n");
return;
@@ -384,29 +395,26 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
* handle BM_RLD is to set it and leave it set.
*/
acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1);
-
- return;
}
-static int acpi_cst_latency_cmp(const void *a, const void *b)
+static void acpi_cst_latency_sort(struct acpi_processor_cx *states, size_t length)
{
- const struct acpi_processor_cx *x = a, *y = b;
+ int i, j, k;
- if (!(x->valid && y->valid))
- return 0;
- if (x->latency > y->latency)
- return 1;
- if (x->latency < y->latency)
- return -1;
- return 0;
-}
-static void acpi_cst_latency_swap(void *a, void *b, int n)
-{
- struct acpi_processor_cx *x = a, *y = b;
+ for (i = 1; i < length; i++) {
+ if (!states[i].valid)
+ continue;
- if (!(x->valid && y->valid))
- return;
- swap(x->latency, y->latency);
+ for (j = i - 1, k = i; j >= 0; j--) {
+ if (!states[j].valid)
+ continue;
+
+ if (states[j].latency > states[k].latency)
+ swap(states[j].latency, states[k].latency);
+
+ k = j;
+ }
+ }
}
static int acpi_processor_power_verify(struct acpi_processor *pr)
@@ -451,23 +459,18 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
if (buggy_latency) {
pr_notice("FW issue: working around C-state latencies out of order\n");
- sort(&pr->power.states[1], max_cstate,
- sizeof(struct acpi_processor_cx),
- acpi_cst_latency_cmp,
- acpi_cst_latency_swap);
+ acpi_cst_latency_sort(&pr->power.states[1], max_cstate);
}
lapic_timer_propagate_broadcast(pr);
- return (working);
+ return working;
}
static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
{
- unsigned int i;
int result;
-
/* NOTE: the idle thread may not be running while calling
* this function */
@@ -484,17 +487,7 @@ static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
acpi_processor_get_power_info_default(pr);
pr->power.count = acpi_processor_power_verify(pr);
-
- /*
- * if one state of type C2 or C3 is available, mark this
- * CPU as being "idle manageable"
- */
- for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
- if (pr->power.states[i].valid) {
- pr->power.count = i;
- pr->flags.power = 1;
- }
- }
+ pr->flags.power = 1;
return 0;
}
@@ -525,16 +518,36 @@ static int acpi_idle_bm_check(void)
return bm_status;
}
-static void wait_for_freeze(void)
+static __cpuidle void io_idle(unsigned long addr)
{
+ /* IO port based C-state */
+ inb(addr);
+
#ifdef CONFIG_X86
/* No delay is needed if we are in guest */
if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
return;
+ /*
+ * Modern (>=Nehalem) Intel systems use ACPI via intel_idle,
+ * not this code. Assume that any Intel systems using this
+ * are ancient and may need the dummy wait. This also assumes
+ * that the motivating chipset issue was Intel-only.
+ */
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+ return;
#endif
- /* Dummy wait op - must do something useless after P_LVL2 read
- because chipsets cannot guarantee that STPCLK# signal
- gets asserted in time to freeze execution properly. */
+ /*
+ * Dummy wait op - must do something useless after P_LVL2 read
+ * because chipsets cannot guarantee that STPCLK# signal gets
+ * asserted in time to freeze execution properly
+ *
+ * This workaround has been in place since the original ACPI
+ * implementation was merged, circa 2002.
+ *
+ * If a profile is pointing to this instruction, please first
+ * consider moving your system to a more modern idle
+ * mechanism.
+ */
inl(acpi_gbl_FADT.xpm_timer_block.address);
}
@@ -554,9 +567,7 @@ static void __cpuidle acpi_idle_do_entry(struct acpi_processor_cx *cx)
} else if (cx->entry_method == ACPI_CSTATE_HALT) {
acpi_safe_halt();
} else {
- /* IO port based C-state */
- inb(cx->address);
- wait_for_freeze();
+ io_idle(cx->address);
}
perf_lopwr_cb(false);
@@ -567,7 +578,7 @@ static void __cpuidle acpi_idle_do_entry(struct acpi_processor_cx *cx)
* @dev: the target CPU
* @index: the index of suggested state
*/
-static int acpi_idle_play_dead(struct cpuidle_device *dev, int index)
+static void acpi_idle_play_dead(struct cpuidle_device *dev, int index)
{
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
@@ -576,23 +587,17 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index)
while (1) {
if (cx->entry_method == ACPI_CSTATE_HALT)
- safe_halt();
+ raw_safe_halt();
else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) {
- inb(cx->address);
- wait_for_freeze();
+ io_idle(cx->address);
+ } else if (cx->entry_method == ACPI_CSTATE_FFH) {
+ acpi_processor_ffh_play_dead(cx);
} else
- return -ENODEV;
-
-#if defined(CONFIG_X86) && defined(CONFIG_HOTPLUG_CPU)
- cond_wakeup_cpu0();
-#endif
+ return;
}
-
- /* Never reached */
- return 0;
}
-static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr)
+static __always_inline bool acpi_idle_fallback_to_c1(struct acpi_processor *pr)
{
return IS_ENABLED(CONFIG_HOTPLUG_CPU) && !pr->flags.has_cst &&
!(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED);
@@ -627,6 +632,8 @@ static int __cpuidle acpi_idle_enter_bm(struct cpuidle_driver *drv,
*/
bool dis_bm = pr->flags.bm_control;
+ instrumentation_begin();
+
/* If we can skip BM, demote to a safe state. */
if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
dis_bm = false;
@@ -648,11 +655,11 @@ static int __cpuidle acpi_idle_enter_bm(struct cpuidle_driver *drv,
raw_spin_unlock(&c3_lock);
}
- ct_idle_enter();
+ ct_cpuidle_enter();
acpi_idle_do_entry(cx);
- ct_idle_exit();
+ ct_cpuidle_exit();
/* Re-enable bus master arbitration */
if (dis_bm) {
@@ -662,6 +669,8 @@ static int __cpuidle acpi_idle_enter_bm(struct cpuidle_driver *drv,
raw_spin_unlock(&c3_lock);
}
+ instrumentation_end();
+
return index;
}
@@ -723,18 +732,16 @@ static int __cpuidle acpi_idle_enter_s2idle(struct cpuidle_device *dev,
return 0;
}
-static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
- struct cpuidle_device *dev)
+static void acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
+ struct cpuidle_device *dev)
{
int i, count = ACPI_IDLE_STATE_START;
struct acpi_processor_cx *cx;
- struct cpuidle_state *state;
if (max_cstate == 0)
max_cstate = 1;
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
- state = &acpi_idle_driver.states[count];
cx = &pr->power.states[i];
if (!cx->valid)
@@ -742,27 +749,13 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
per_cpu(acpi_cstate[count], dev->cpu) = cx;
- if (lapic_timer_needs_broadcast(pr, cx))
- state->flags |= CPUIDLE_FLAG_TIMER_STOP;
-
- if (cx->type == ACPI_STATE_C3) {
- state->flags |= CPUIDLE_FLAG_TLB_FLUSHED;
- if (pr->flags.bm_check)
- state->flags |= CPUIDLE_FLAG_RCU_IDLE;
- }
-
count++;
if (count == CPUIDLE_STATE_MAX)
break;
}
-
- if (!count)
- return -EINVAL;
-
- return 0;
}
-static int acpi_processor_setup_cstates(struct acpi_processor *pr)
+static void acpi_processor_setup_cstates(struct acpi_processor *pr)
{
int i, count;
struct acpi_processor_cx *cx;
@@ -787,18 +780,18 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr)
state = &drv->states[count];
snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
- strlcpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
+ strscpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
state->exit_latency = cx->latency;
state->target_residency = cx->latency * latency_factor;
state->enter = acpi_idle_enter;
state->flags = 0;
- if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2 ||
- cx->type == ACPI_STATE_C3) {
- state->enter_dead = acpi_idle_play_dead;
- if (cx->type != ACPI_STATE_C3)
- drv->safe_state_index = count;
- }
+
+ state->enter_dead = acpi_idle_play_dead;
+
+ if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2)
+ drv->safe_state_index = count;
+
/*
* Halt-induced C1 is not good for ->enter_s2idle, because it
* re-enables interrupts on exit. Moreover, C1 is generally not
@@ -809,17 +802,21 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr)
if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr))
state->enter_s2idle = acpi_idle_enter_s2idle;
+ if (lapic_timer_needs_broadcast(pr, cx))
+ state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+
+ if (cx->type == ACPI_STATE_C3) {
+ state->flags |= CPUIDLE_FLAG_TLB_FLUSHED;
+ if (pr->flags.bm_check)
+ state->flags |= CPUIDLE_FLAG_RCU_IDLE;
+ }
+
count++;
if (count == CPUIDLE_STATE_MAX)
break;
}
drv->state_count = count;
-
- if (!count)
- return -EINVAL;
-
- return 0;
}
static inline void acpi_processor_cstate_first_run_checks(void)
@@ -956,7 +953,7 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle,
obj = pkg_elem + 9;
if (obj->type == ACPI_TYPE_STRING)
- strlcpy(lpi_state->desc, obj->string.pointer,
+ strscpy(lpi_state->desc, obj->string.pointer,
ACPI_CX_DESC_LEN);
lpi_state->index = state_idx;
@@ -989,11 +986,6 @@ end:
return ret;
}
-/*
- * flat_state_cnt - the number of composite LPI states after the process of flattening
- */
-static int flat_state_cnt;
-
/**
* combine_lpi_states - combine local and parent LPI states to form a composite LPI state
*
@@ -1022,7 +1014,7 @@ static bool combine_lpi_states(struct acpi_lpi_state *local,
result->arch_flags = parent->arch_flags;
result->index = parent->index;
- strlcpy(result->desc, local->desc, ACPI_CX_DESC_LEN);
+ strscpy(result->desc, local->desc, ACPI_CX_DESC_LEN);
strlcat(result->desc, "+", ACPI_CX_DESC_LEN);
strlcat(result->desc, parent->desc, ACPI_CX_DESC_LEN);
return true;
@@ -1036,9 +1028,10 @@ static void stash_composite_state(struct acpi_lpi_states_array *curr_level,
curr_level->composite_states[curr_level->composite_states_size++] = t;
}
-static int flatten_lpi_states(struct acpi_processor *pr,
- struct acpi_lpi_states_array *curr_level,
- struct acpi_lpi_states_array *prev_level)
+static unsigned int flatten_lpi_states(struct acpi_processor *pr,
+ unsigned int flat_state_cnt,
+ struct acpi_lpi_states_array *curr_level,
+ struct acpi_lpi_states_array *prev_level)
{
int i, j, state_count = curr_level->size;
struct acpi_lpi_state *p, *t = curr_level->entries;
@@ -1078,7 +1071,7 @@ static int flatten_lpi_states(struct acpi_processor *pr,
}
kfree(curr_level->entries);
- return 0;
+ return flat_state_cnt;
}
int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu)
@@ -1093,6 +1086,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr)
acpi_handle handle = pr->handle, pr_ahandle;
struct acpi_device *d = NULL;
struct acpi_lpi_states_array info[2], *tmp, *prev, *curr;
+ unsigned int state_count;
/* make sure our architecture has support */
ret = acpi_processor_ffh_lpi_probe(pr->id);
@@ -1105,18 +1099,20 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr)
if (!acpi_has_method(handle, "_LPI"))
return -EINVAL;
- flat_state_cnt = 0;
prev = &info[0];
curr = &info[1];
handle = pr->handle;
ret = acpi_processor_evaluate_lpi(handle, prev);
if (ret)
return ret;
- flatten_lpi_states(pr, prev, NULL);
+ state_count = flatten_lpi_states(pr, 0, prev, NULL);
status = acpi_get_parent(handle, &pr_ahandle);
while (ACPI_SUCCESS(status)) {
d = acpi_fetch_acpi_dev(pr_ahandle);
+ if (!d)
+ break;
+
handle = pr_ahandle;
if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID))
@@ -1131,18 +1127,19 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr)
break;
/* flatten all the LPI states in this level of hierarchy */
- flatten_lpi_states(pr, curr, prev);
+ state_count = flatten_lpi_states(pr, state_count, curr, prev);
tmp = prev, prev = curr, curr = tmp;
status = acpi_get_parent(handle, &pr_ahandle);
}
- pr->power.count = flat_state_cnt;
/* reset the index after flattening */
- for (i = 0; i < pr->power.count; i++)
+ for (i = 0; i < state_count; i++)
pr->power.lpi_states[i].index = i;
+ pr->power.count = state_count;
+
/* Tell driver that _LPI is supported. */
pr->flags.has_lpi = 1;
pr->flags.power = 1;
@@ -1196,11 +1193,12 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
state = &drv->states[i];
snprintf(state->name, CPUIDLE_NAME_LEN, "LPI-%d", i);
- strlcpy(state->desc, lpi->desc, CPUIDLE_DESC_LEN);
+ strscpy(state->desc, lpi->desc, CPUIDLE_DESC_LEN);
state->exit_latency = lpi->wake_latency;
state->target_residency = lpi->min_residency;
- if (lpi->arch_flags)
- state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+ state->flags |= arch_get_idle_state_flags(lpi->arch_flags);
+ if (i != 0 && lpi->entry_method == ACPI_CSTATE_FFH)
+ state->flags |= CPUIDLE_FLAG_RCU_IDLE;
state->enter = acpi_idle_lpi_enter;
drv->safe_state_index = i;
}
@@ -1233,7 +1231,8 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
if (pr->flags.has_lpi)
return acpi_processor_setup_lpi_states(pr);
- return acpi_processor_setup_cstates(pr);
+ acpi_processor_setup_cstates(pr);
+ return 0;
}
/**
@@ -1253,7 +1252,8 @@ static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
if (pr->flags.has_lpi)
return acpi_processor_ffh_lpi_probe(pr->id);
- return acpi_processor_setup_cpuidle_cx(pr, dev);
+ acpi_processor_setup_cpuidle_cx(pr, dev);
+ return 0;
}
static int acpi_processor_get_power_info(struct acpi_processor *pr)
@@ -1392,6 +1392,9 @@ int acpi_processor_power_init(struct acpi_processor *pr)
if (retval) {
if (acpi_processor_registered == 0)
cpuidle_unregister_driver(&acpi_idle_driver);
+
+ per_cpu(acpi_cpuidle_device, pr->id) = NULL;
+ kfree(dev);
return retval;
}
acpi_processor_registered++;
@@ -1411,8 +1414,12 @@ int acpi_processor_power_exit(struct acpi_processor *pr)
acpi_processor_registered--;
if (acpi_processor_registered == 0)
cpuidle_unregister_driver(&acpi_idle_driver);
+
+ kfree(dev);
}
pr->flags.power_setup_done = 0;
return 0;
}
+
+MODULE_IMPORT_NS("ACPI_PROCESSOR_IDLE");
diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c
index 8c3f82c9fff3..994091bd52de 100644
--- a/drivers/acpi/processor_pdc.c
+++ b/drivers/acpi/processor_pdc.c
@@ -9,60 +9,20 @@
#define pr_fmt(fmt) "ACPI: " fmt
-#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#include "internal.h"
-static bool __init processor_physically_present(acpi_handle handle)
-{
- int cpuid, type;
- u32 acpi_id;
- acpi_status status;
- acpi_object_type acpi_type;
- unsigned long long tmp;
- union acpi_object object = { 0 };
- struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
-
- status = acpi_get_type(handle, &acpi_type);
- if (ACPI_FAILURE(status))
- return false;
-
- switch (acpi_type) {
- case ACPI_TYPE_PROCESSOR:
- status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
- if (ACPI_FAILURE(status))
- return false;
- acpi_id = object.processor.proc_id;
- break;
- case ACPI_TYPE_DEVICE:
- status = acpi_evaluate_integer(handle, "_UID", NULL, &tmp);
- if (ACPI_FAILURE(status))
- return false;
- acpi_id = tmp;
- break;
- default:
- return false;
- }
-
- type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0;
- cpuid = acpi_get_cpuid(handle, type, acpi_id);
-
- return !invalid_logical_cpuid(cpuid);
-}
-
static void acpi_set_pdc_bits(u32 *buf)
{
buf[0] = ACPI_PDC_REVISION_ID;
buf[1] = 1;
-
- /* Enable coordination with firmware's _TSD info */
- buf[2] = ACPI_PDC_SMP_T_SWCOORD;
+ buf[2] = 0;
/* Twiddle arch-specific bits needed for _PDC */
- arch_acpi_set_pdc_bits(buf);
+ arch_acpi_set_proc_cap_bits(&buf[2]);
}
static struct acpi_object_list *acpi_processor_alloc_pdc(void)
@@ -112,20 +72,6 @@ acpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in)
{
acpi_status status = AE_OK;
- if (boot_option_idle_override == IDLE_NOMWAIT) {
- /*
- * If mwait is disabled for CPU C-states, the C2C3_FFH access
- * mode will be disabled in the parameter of _PDC object.
- * Of course C1_FFH access mode will also be disabled.
- */
- union acpi_object *obj;
- u32 *buffer = NULL;
-
- obj = pdc_in->pointer;
- buffer = (u32 *)(obj->buffer.pointer);
- buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH);
-
- }
status = acpi_evaluate_object(handle, "_PDC", pdc_in, NULL);
if (ACPI_FAILURE(status))
@@ -163,36 +109,9 @@ early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
-static int __init set_no_mwait(const struct dmi_system_id *id)
-{
- pr_notice("%s detected - disabling mwait for CPU C-states\n",
- id->ident);
- boot_option_idle_override = IDLE_NOMWAIT;
- return 0;
-}
-
-static const struct dmi_system_id processor_idle_dmi_table[] __initconst = {
- {
- set_no_mwait, "Extensa 5220", {
- DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
- DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL},
- {},
-};
-
-static void __init processor_dmi_check(void)
-{
- /*
- * Check whether the system is DMI table. If yes, OSPM
- * should not use mwait for CPU-states.
- */
- dmi_check_system(processor_idle_dmi_table);
-}
-
void __init acpi_early_processor_set_pdc(void)
{
- processor_dmi_check();
+ acpi_proc_quirk_mwait_check();
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 757a98f6d7a2..8972446b7162 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -20,12 +20,11 @@
#include <acpi/processor.h>
#ifdef CONFIG_X86
#include <asm/cpufeature.h>
+#include <asm/msr.h>
#endif
#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
-static DEFINE_MUTEX(performance_mutex);
-
/*
* _PPC support is implemented as a CPUfreq policy notifier:
* This means each time a CPUfreq driver registered also with
@@ -53,6 +52,8 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
{
acpi_status status = 0;
unsigned long long ppc = 0;
+ s32 qos_value;
+ int index;
int ret;
if (!pr)
@@ -72,17 +73,30 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
}
}
+ index = ppc;
+
+ if (pr->performance_platform_limit == index ||
+ ppc >= pr->performance->state_count)
+ return 0;
+
pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
- (int)ppc, ppc ? "" : "not");
+ index, index ? "is" : "is not");
- pr->performance_platform_limit = (int)ppc;
+ pr->performance_platform_limit = index;
- if (ppc >= pr->performance->state_count ||
- unlikely(!freq_qos_request_active(&pr->perflib_req)))
+ if (unlikely(!freq_qos_request_active(&pr->perflib_req)))
return 0;
- ret = freq_qos_update_request(&pr->perflib_req,
- pr->performance->states[ppc].core_frequency * 1000);
+ /*
+ * If _PPC returns 0, it means that all of the available states can be
+ * used ("no limit").
+ */
+ if (index == 0)
+ qos_value = FREQ_QOS_MAX_DEFAULT_VALUE;
+ else
+ qos_value = pr->performance->states[index].core_frequency * 1000;
+
+ ret = freq_qos_update_request(&pr->perflib_req, qos_value);
if (ret < 0) {
pr_warn("Failed to update perflib freq constraint: CPU%d (%d)\n",
pr->id, ret);
@@ -142,6 +156,7 @@ int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
pr = per_cpu(processors, cpu);
if (!pr || !pr->performance || !pr->performance->state_count)
return -ENODEV;
+
*limit = pr->performance->states[pr->performance_platform_limit].
core_frequency * 1000;
return 0;
@@ -158,6 +173,9 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy)
{
unsigned int cpu;
+ if (ignore_ppc == 1)
+ return;
+
for_each_cpu(cpu, policy->related_cpus) {
struct acpi_processor *pr = per_cpu(processors, cpu);
int ret;
@@ -165,12 +183,27 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy)
if (!pr)
continue;
+ /*
+ * Reset performance_platform_limit in case there is a stale
+ * value in it, so as to make it match the "no limit" QoS value
+ * below.
+ */
+ pr->performance_platform_limit = 0;
+
ret = freq_qos_add_request(&policy->constraints,
- &pr->perflib_req,
- FREQ_QOS_MAX, INT_MAX);
+ &pr->perflib_req, FREQ_QOS_MAX,
+ FREQ_QOS_MAX_DEFAULT_VALUE);
if (ret < 0)
pr_err("Failed to add freq constraint for CPU%d (%d)\n",
cpu, ret);
+
+ if (!pr->performance)
+ continue;
+
+ ret = acpi_processor_get_platform_limit(pr);
+ if (ret)
+ pr_err("Failed to update freq constraint for CPU%d (%d)\n",
+ cpu, ret);
}
}
@@ -186,6 +219,10 @@ void acpi_processor_ppc_exit(struct cpufreq_policy *policy)
}
}
+#ifdef CONFIG_X86
+
+static DEFINE_MUTEX(performance_mutex);
+
static int acpi_processor_get_performance_control(struct acpi_processor *pr)
{
int result = 0;
@@ -201,8 +238,7 @@ static int acpi_processor_get_performance_control(struct acpi_processor *pr)
}
pct = (union acpi_object *)buffer.pointer;
- if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
- || (pct->package.count != 2)) {
+ if (!pct || pct->type != ACPI_TYPE_PACKAGE || pct->package.count != 2) {
pr_err("Invalid _PCT data\n");
result = -EFAULT;
goto end;
@@ -214,9 +250,8 @@ static int acpi_processor_get_performance_control(struct acpi_processor *pr)
obj = pct->package.elements[0];
- if ((obj.type != ACPI_TYPE_BUFFER)
- || (obj.buffer.length < sizeof(struct acpi_pct_register))
- || (obj.buffer.pointer == NULL)) {
+ if (!obj.buffer.pointer || obj.type != ACPI_TYPE_BUFFER ||
+ obj.buffer.length < sizeof(struct acpi_pct_register)) {
pr_err("Invalid _PCT data (control_register)\n");
result = -EFAULT;
goto end;
@@ -230,9 +265,8 @@ static int acpi_processor_get_performance_control(struct acpi_processor *pr)
obj = pct->package.elements[1];
- if ((obj.type != ACPI_TYPE_BUFFER)
- || (obj.buffer.length < sizeof(struct acpi_pct_register))
- || (obj.buffer.pointer == NULL)) {
+ if (!obj.buffer.pointer || obj.type != ACPI_TYPE_BUFFER ||
+ obj.buffer.length < sizeof(struct acpi_pct_register)) {
pr_err("Invalid _PCT data (status_register)\n");
result = -EFAULT;
goto end;
@@ -247,7 +281,6 @@ end:
return result;
}
-#ifdef CONFIG_X86
/*
* Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding
* in their ACPI data. Calculate the real values and fix up the _PSS data.
@@ -260,8 +293,8 @@ static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return;
- if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10)
- || boot_cpu_data.x86 == 0x11) {
+ if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) ||
+ boot_cpu_data.x86 == 0x11) {
rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi);
/*
* MSR C001_0064+:
@@ -278,9 +311,6 @@ static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
px->core_frequency = (100 * (fid + 8)) >> did;
}
}
-#else
-static void amd_fixup_frequency(struct acpi_processor_px *px, int i) {};
-#endif
static int acpi_processor_get_performance_states(struct acpi_processor *pr)
{
@@ -300,7 +330,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)
}
pss = buffer.pointer;
- if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
+ if (!pss || pss->type != ACPI_TYPE_PACKAGE) {
pr_err("Invalid _PSS data\n");
result = -EFAULT;
goto end;
@@ -353,8 +383,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)
* Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq
*/
if (!px->core_frequency ||
- ((u32)(px->core_frequency * 1000) !=
- (px->core_frequency * 1000))) {
+ (u32)(px->core_frequency * 1000) != px->core_frequency * 1000) {
pr_err(FW_BUG
"Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n",
pr->id, px->core_frequency);
@@ -421,13 +450,11 @@ int acpi_processor_get_performance_info(struct acpi_processor *pr)
* the BIOS is older than the CPU and does not know its frequencies
*/
update_bios:
-#ifdef CONFIG_X86
if (acpi_has_method(pr->handle, "_PPC")) {
if(boot_cpu_has(X86_FEATURE_EST))
pr_warn(FW_BUG "BIOS needs update for CPU "
"frequency support\n");
}
-#endif
return result;
}
EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info);
@@ -456,7 +483,7 @@ int acpi_processor_pstate_control(void)
int acpi_processor_notify_smm(struct module *calling_module)
{
static int is_done;
- int result;
+ int result = 0;
if (!acpi_processor_cpufreq_init)
return -EBUSY;
@@ -464,42 +491,41 @@ int acpi_processor_notify_smm(struct module *calling_module)
if (!try_module_get(calling_module))
return -EINVAL;
- /* is_done is set to negative if an error occurred,
- * and to postitive if _no_ error occurred, but SMM
- * was already notified. This avoids double notification
- * which might lead to unexpected results...
+ /*
+ * is_done is set to negative if an error occurs and to 1 if no error
+ * occurrs, but SMM has been notified already. This avoids repeated
+ * notification which might lead to unexpected results.
*/
- if (is_done > 0) {
- module_put(calling_module);
- return 0;
- } else if (is_done < 0) {
- module_put(calling_module);
- return is_done;
- }
+ if (is_done != 0) {
+ if (is_done < 0)
+ result = is_done;
- is_done = -EIO;
+ goto out_put;
+ }
result = acpi_processor_pstate_control();
- if (!result) {
- pr_debug("No SMI port or pstate_control\n");
- module_put(calling_module);
- return 0;
- }
- if (result < 0) {
- module_put(calling_module);
- return result;
+ if (result <= 0) {
+ if (result) {
+ is_done = result;
+ } else {
+ pr_debug("No SMI port or pstate_control\n");
+ is_done = 1;
+ }
+ goto out_put;
}
- /* Success. If there's no _PPC, we need to fear nothing, so
- * we can allow the cpufreq driver to be rmmod'ed. */
is_done = 1;
+ /*
+ * Success. If there _PPC, unloading the cpufreq driver would be risky,
+ * so disallow it in that case.
+ */
+ if (acpi_processor_ppc_in_use)
+ return 0;
- if (!acpi_processor_ppc_in_use)
- module_put(calling_module);
-
- return 0;
+out_put:
+ module_put(calling_module);
+ return result;
}
-
EXPORT_SYMBOL(acpi_processor_notify_smm);
int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain)
@@ -517,7 +543,7 @@ int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain)
}
psd = buffer.pointer;
- if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {
+ if (!psd || psd->type != ACPI_TYPE_PACKAGE) {
pr_err("Invalid _PSD data\n");
result = -EFAULT;
goto end;
@@ -532,8 +558,7 @@ int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain)
state.length = sizeof(struct acpi_psd_package);
state.pointer = pdomain;
- status = acpi_extract_package(&(psd->package.elements[0]),
- &format, &state);
+ status = acpi_extract_package(&(psd->package.elements[0]), &format, &state);
if (ACPI_FAILURE(status)) {
pr_err("Invalid _PSD data\n");
result = -EFAULT;
@@ -716,9 +741,8 @@ err_out:
}
EXPORT_SYMBOL(acpi_processor_preregister_performance);
-int
-acpi_processor_register_performance(struct acpi_processor_performance
- *performance, unsigned int cpu)
+int acpi_processor_register_performance(struct acpi_processor_performance
+ *performance, unsigned int cpu)
{
struct acpi_processor *pr;
@@ -751,7 +775,6 @@ acpi_processor_register_performance(struct acpi_processor_performance
mutex_unlock(&performance_mutex);
return 0;
}
-
EXPORT_SYMBOL(acpi_processor_register_performance);
void acpi_processor_unregister_performance(unsigned int cpu)
@@ -761,18 +784,16 @@ void acpi_processor_unregister_performance(unsigned int cpu)
mutex_lock(&performance_mutex);
pr = per_cpu(processors, cpu);
- if (!pr) {
- mutex_unlock(&performance_mutex);
- return;
- }
+ if (!pr)
+ goto unlock;
if (pr->performance)
kfree(pr->performance->states);
+
pr->performance = NULL;
+unlock:
mutex_unlock(&performance_mutex);
-
- return;
}
-
EXPORT_SYMBOL(acpi_processor_unregister_performance);
+#endif
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index e534fd49a67e..c7b1dc5687ec 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -17,6 +17,8 @@
#include <acpi/processor.h>
#include <linux/uaccess.h>
+#include "internal.h"
+
#ifdef CONFIG_CPU_FREQ
/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
@@ -26,12 +28,21 @@
*/
#define CPUFREQ_THERMAL_MIN_STEP 0
-#define CPUFREQ_THERMAL_MAX_STEP 3
-static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
+static int cpufreq_thermal_max_step __read_mostly = 3;
+
+/*
+ * Minimum throttle percentage for processor_thermal cooling device.
+ * The processor_thermal driver uses it to calculate the percentage amount by
+ * which cpu frequency must be reduced for each cooling state. This is also used
+ * to calculate the maximum number of throttling steps or cooling states.
+ */
+static int cpufreq_thermal_reduction_pctg __read_mostly = 20;
+
+static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_step);
-#define reduction_pctg(cpu) \
- per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
+#define reduction_step(cpu) \
+ per_cpu(cpufreq_thermal_reduction_step, phys_package_first_cpu(cpu))
/*
* Emulate "per package data" using per cpu data (which should really be
@@ -51,19 +62,14 @@ static int phys_package_first_cpu(int cpu)
return 0;
}
-static int cpu_has_cpufreq(unsigned int cpu)
+static bool cpu_has_cpufreq(unsigned int cpu)
{
- struct cpufreq_policy *policy;
-
if (!acpi_processor_cpufreq_init)
return 0;
- policy = cpufreq_cpu_get(cpu);
- if (policy) {
- cpufreq_cpu_put(policy);
- return 1;
- }
- return 0;
+ struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpu);
+
+ return policy != NULL;
}
static int cpufreq_get_max_state(unsigned int cpu)
@@ -71,7 +77,7 @@ static int cpufreq_get_max_state(unsigned int cpu)
if (!cpu_has_cpufreq(cpu))
return 0;
- return CPUFREQ_THERMAL_MAX_STEP;
+ return cpufreq_thermal_max_step;
}
static int cpufreq_get_cur_state(unsigned int cpu)
@@ -79,20 +85,39 @@ static int cpufreq_get_cur_state(unsigned int cpu)
if (!cpu_has_cpufreq(cpu))
return 0;
- return reduction_pctg(cpu);
+ return reduction_step(cpu);
+}
+
+static bool cpufreq_update_thermal_limit(unsigned int cpu, struct acpi_processor *pr)
+{
+ unsigned long max_freq;
+ int ret;
+
+ struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpu);
+ if (!policy)
+ return false;
+
+ max_freq = (policy->cpuinfo.max_freq *
+ (100 - reduction_step(cpu) * cpufreq_thermal_reduction_pctg)) / 100;
+
+ ret = freq_qos_update_request(&pr->thermal_req, max_freq);
+ if (ret < 0) {
+ pr_warn("Failed to update thermal freq constraint: CPU%d (%d)\n",
+ pr->id, ret);
+ }
+
+ return true;
}
static int cpufreq_set_cur_state(unsigned int cpu, int state)
{
- struct cpufreq_policy *policy;
struct acpi_processor *pr;
- unsigned long max_freq;
- int i, ret;
+ int i;
if (!cpu_has_cpufreq(cpu))
return 0;
- reduction_pctg(cpu) = state;
+ reduction_step(cpu) = state;
/*
* Update all the CPUs in the same package because they all
@@ -109,27 +134,35 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state)
if (unlikely(!freq_qos_request_active(&pr->thermal_req)))
continue;
- policy = cpufreq_cpu_get(i);
- if (!policy)
+ if (!cpufreq_update_thermal_limit(i, pr))
return -EINVAL;
+ }
+ return 0;
+}
- max_freq = (policy->cpuinfo.max_freq * (100 - reduction_pctg(i) * 20)) / 100;
+static void acpi_thermal_cpufreq_config(void)
+{
+ int cpufreq_pctg = acpi_arch_thermal_cpufreq_pctg();
- cpufreq_cpu_put(policy);
+ if (!cpufreq_pctg)
+ return;
- ret = freq_qos_update_request(&pr->thermal_req, max_freq);
- if (ret < 0) {
- pr_warn("Failed to update thermal freq constraint: CPU%d (%d)\n",
- pr->id, ret);
- }
- }
- return 0;
+ cpufreq_thermal_reduction_pctg = cpufreq_pctg;
+
+ /*
+ * Derive the MAX_STEP from minimum throttle percentage so that the reduction
+ * percentage doesn't end up becoming negative. Also, cap the MAX_STEP so that
+ * the CPU performance doesn't become 0.
+ */
+ cpufreq_thermal_max_step = (100 / cpufreq_pctg) - 2;
}
void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy)
{
unsigned int cpu;
+ acpi_thermal_cpufreq_config();
+
for_each_cpu(cpu, policy->related_cpus) {
struct acpi_processor *pr = per_cpu(processors, cpu);
int ret;
@@ -140,9 +173,13 @@ void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy)
ret = freq_qos_add_request(&policy->constraints,
&pr->thermal_req,
FREQ_QOS_MAX, INT_MAX);
- if (ret < 0)
+ if (ret < 0) {
pr_err("Failed to add freq constraint for CPU%d (%d)\n",
cpu, ret);
+ continue;
+ }
+
+ thermal_cooling_device_update(pr->cdev);
}
}
@@ -153,8 +190,12 @@ void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
for_each_cpu(cpu, policy->related_cpus) {
struct acpi_processor *pr = per_cpu(processors, cpu);
- if (pr)
- freq_qos_remove_request(&pr->thermal_req);
+ if (!pr)
+ continue;
+
+ freq_qos_remove_request(&pr->thermal_req);
+
+ thermal_cooling_device_update(pr->cdev);
}
}
#else /* ! CONFIG_CPU_FREQ */
@@ -182,7 +223,7 @@ static int acpi_processor_max_state(struct acpi_processor *pr)
/*
* There exists four states according to
- * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3
+ * cpufreq_thermal_reduction_step. 0, 1, 2, 3
*/
max_state += cpufreq_get_max_state(pr->id);
if (pr->flags.throttling)
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index a822fe410dda..f9c2bc1d4a3a 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -18,9 +18,12 @@
#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/acpi.h>
+#include <linux/uaccess.h>
#include <acpi/processor.h>
#include <asm/io.h>
-#include <linux/uaccess.h>
+#ifdef CONFIG_X86
+#include <asm/msr.h>
+#endif
/* ignore_tpc:
* 0 -> acpi processor driver doesn't ignore _TPC values
@@ -50,7 +53,7 @@ static int __acpi_processor_set_throttling(struct acpi_processor *pr,
static int acpi_processor_update_tsd_coord(void)
{
- int count, count_target;
+ int count_target;
int retval = 0;
unsigned int i, j;
cpumask_var_t covered_cpus;
@@ -107,7 +110,6 @@ static int acpi_processor_update_tsd_coord(void)
/* Validate the Domain info */
count_target = pdomain->num_processors;
- count = 1;
for_each_possible_cpu(j) {
if (i == j)
@@ -140,7 +142,6 @@ static int acpi_processor_update_tsd_coord(void)
cpumask_set_cpu(j, covered_cpus);
cpumask_set_cpu(j, pthrottling->shared_cpu_map);
- count++;
}
for_each_possible_cpu(j) {
if (i == j)
@@ -234,7 +235,7 @@ static int acpi_processor_throttling_notifier(unsigned long event, void *data)
if (pr->throttling_platform_limit > target_state)
target_state = pr->throttling_platform_limit;
if (target_state >= p_throttling->state_count) {
- pr_warn("Exceed the limit of T-state \n");
+ pr_warn("Exceed the limit of T-state\n");
target_state = p_throttling->state_count - 1;
}
p_tstate->target_state = target_state;
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index d4c168ce428c..18e90067d567 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -2,14 +2,17 @@
/*
* ACPI device specific properties support.
*
- * Copyright (C) 2014, Intel Corporation
+ * Copyright (C) 2014 - 2023, Intel Corporation
* All rights reserved.
*
* Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
* Darren Hart <dvhart@linux.intel.com>
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ * Sakari Ailus <sakari.ailus@linux.intel.com>
*/
+#define pr_fmt(fmt) "ACPI: " fmt
+
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/export.h>
@@ -28,9 +31,14 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data,
* not defined without a warning. For instance if any of the properties
* from different GUID appear in a property list of another, it will be
* accepted by the kernel. Firmware validation tools should catch these.
+ *
+ * References:
+ *
+ * [1] UEFI DSD Guide.
+ * https://github.com/UEFI/DSD-Guide/blob/main/src/dsd-guide.adoc
*/
static const guid_t prp_guids[] = {
- /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
+ /* ACPI _DSD device properties GUID [1]: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01),
/* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
@@ -50,11 +58,12 @@ static const guid_t prp_guids[] = {
0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0),
};
-/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
+/* ACPI _DSD data subnodes GUID [1]: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
static const guid_t ads_guid =
GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
+/* ACPI _DSD data buffer GUID [1]: edb12dd0-363d-4085-a3d2-49522ca160c4 */
static const guid_t buffer_prop_guid =
GUID_INIT(0xedb12dd0, 0x363d, 0x4085,
0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4);
@@ -74,8 +83,12 @@ static bool acpi_nondev_subnode_extract(union acpi_object *desc,
struct fwnode_handle *parent)
{
struct acpi_data_node *dn;
+ acpi_handle scope = NULL;
bool result;
+ if (acpi_graph_ignore_port(handle))
+ return false;
+
dn = kzalloc(sizeof(*dn), GFP_KERNEL);
if (!dn)
return false;
@@ -86,59 +99,45 @@ static bool acpi_nondev_subnode_extract(union acpi_object *desc,
INIT_LIST_HEAD(&dn->data.properties);
INIT_LIST_HEAD(&dn->data.subnodes);
- result = acpi_extract_properties(handle, desc, &dn->data);
-
- if (handle) {
- acpi_handle scope;
- acpi_status status;
+ /*
+ * The scope for the completion of relative pathname segments and
+ * subnode object lookup is the one of the namespace node (device)
+ * containing the object that has returned the package. That is, it's
+ * the scope of that object's parent device.
+ */
+ if (handle)
+ acpi_get_parent(handle, &scope);
- /*
- * The scope for the subnode object lookup is the one of the
- * namespace node (device) containing the object that has
- * returned the package. That is, it's the scope of that
- * object's parent.
- */
- status = acpi_get_parent(handle, &scope);
- if (ACPI_SUCCESS(status)
- && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data,
- &dn->fwnode))
- result = true;
- } else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data,
- &dn->fwnode)) {
+ /*
+ * Extract properties from the _DSD-equivalent package pointed to by
+ * desc and use scope (if not NULL) for the completion of relative
+ * pathname segments.
+ *
+ * The extracted properties will be held in the new data node dn.
+ */
+ result = acpi_extract_properties(scope, desc, &dn->data);
+ /*
+ * Look for subnodes in the _DSD-equivalent package pointed to by desc
+ * and create child nodes of dn if there are any.
+ */
+ if (acpi_enumerate_nondev_subnodes(scope, desc, &dn->data, &dn->fwnode))
result = true;
- }
-
- if (result) {
- dn->handle = handle;
- dn->data.pointer = desc;
- list_add_tail(&dn->sibling, list);
- return true;
- }
- kfree(dn);
- acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
- return false;
-}
-
-static bool acpi_nondev_subnode_data_ok(acpi_handle handle,
- const union acpi_object *link,
- struct list_head *list,
- struct fwnode_handle *parent)
-{
- struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
- acpi_status status;
-
- status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
- ACPI_TYPE_PACKAGE);
- if (ACPI_FAILURE(status))
+ if (!result) {
+ kfree(dn);
+ acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n");
return false;
+ }
- if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
- parent))
- return true;
+ /*
+ * This will be NULL if the desc package is embedded in an outer
+ * _DSD-equivalent package and its scope cannot be determined.
+ */
+ dn->handle = handle;
+ dn->data.pointer = desc;
+ list_add_tail(&dn->sibling, list);
- ACPI_FREE(buf.pointer);
- return false;
+ return true;
}
static bool acpi_nondev_subnode_ok(acpi_handle scope,
@@ -146,9 +145,16 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
struct list_head *list,
struct fwnode_handle *parent)
{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
acpi_handle handle;
acpi_status status;
+ /*
+ * If the scope is unknown, the _DSD-equivalent package being parsed
+ * was embedded in an outer _DSD-equivalent package as a result of
+ * direct evaluation of an object pointed to by a reference. In that
+ * case, using a pathname as the target object pointer is invalid.
+ */
if (!scope)
return false;
@@ -157,7 +163,17 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope,
if (ACPI_FAILURE(status))
return false;
- return acpi_nondev_subnode_data_ok(handle, link, list, parent);
+ status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list,
+ parent))
+ return true;
+
+ ACPI_FREE(buf.pointer);
+ return false;
}
static bool acpi_add_nondev_subnodes(acpi_handle scope,
@@ -168,9 +184,12 @@ static bool acpi_add_nondev_subnodes(acpi_handle scope,
bool ret = false;
int i;
+ /*
+ * Every element in the links package is expected to represent a link
+ * to a non-device node in a tree containing device-specific data.
+ */
for (i = 0; i < links->package.count; i++) {
union acpi_object *link, *desc;
- acpi_handle handle;
bool result;
link = &links->package.elements[i];
@@ -178,26 +197,53 @@ static bool acpi_add_nondev_subnodes(acpi_handle scope,
if (link->package.count != 2)
continue;
- /* The first one must be a string. */
+ /* The first one (the key) must be a string. */
if (link->package.elements[0].type != ACPI_TYPE_STRING)
continue;
- /* The second one may be a string, a reference or a package. */
+ /* The second one (the target) may be a string or a package. */
switch (link->package.elements[1].type) {
case ACPI_TYPE_STRING:
+ /*
+ * The string is expected to be a full pathname or a
+ * pathname segment relative to the given scope. That
+ * pathname is expected to point to an object returning
+ * a package that contains _DSD-equivalent information.
+ */
result = acpi_nondev_subnode_ok(scope, link, list,
parent);
break;
- case ACPI_TYPE_LOCAL_REFERENCE:
- handle = link->package.elements[1].reference.handle;
- result = acpi_nondev_subnode_data_ok(handle, link, list,
- parent);
- break;
case ACPI_TYPE_PACKAGE:
+ /*
+ * This happens when a reference is used in AML to
+ * point to the target. Since the target is expected
+ * to be a named object, a reference to it will cause it
+ * to be avaluated in place and its return package will
+ * be embedded in the links package at the location of
+ * the reference.
+ *
+ * The target package is expected to contain _DSD-
+ * equivalent information, but the scope in which it
+ * is located in the original AML is unknown. Thus
+ * it cannot contain pathname segments represented as
+ * strings because there is no way to build full
+ * pathnames out of them.
+ */
+ acpi_handle_debug(scope, "subnode %s: Unknown scope\n",
+ link->package.elements[0].string.pointer);
desc = &link->package.elements[1];
result = acpi_nondev_subnode_extract(desc, NULL, link,
list, parent);
break;
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ /*
+ * It is not expected to see any local references in
+ * the links package because referencing a named object
+ * should cause it to be evaluated in place.
+ */
+ acpi_handle_info(scope, "subnode %s: Unexpected reference\n",
+ link->package.elements[0].string.pointer);
+ fallthrough;
default:
result = false;
break;
@@ -304,8 +350,10 @@ static void acpi_init_of_compatible(struct acpi_device *adev)
ret = acpi_dev_get_property(adev, "compatible",
ACPI_TYPE_STRING, &of_compatible);
if (ret) {
- if (adev->parent
- && adev->parent->flags.of_compatible_ok)
+ struct acpi_device *parent;
+
+ parent = acpi_dev_parent(adev);
+ if (parent && parent->flags.of_compatible_ok)
goto out;
return;
@@ -355,6 +403,9 @@ static void acpi_untie_nondev_subnodes(struct acpi_device_data *data)
struct acpi_data_node *dn;
list_for_each_entry(dn, &data->subnodes, sibling) {
+ if (!dn->handle)
+ continue;
+
acpi_detach_data(dn->handle, acpi_nondev_subnode_tag);
acpi_untie_nondev_subnodes(&dn->data);
@@ -369,6 +420,9 @@ static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data)
acpi_status status;
bool ret;
+ if (!dn->handle)
+ continue;
+
status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn);
if (ACPI_FAILURE(status) && status != AE_ALREADY_EXISTS) {
acpi_handle_err(dn->handle, "Can't tag data node\n");
@@ -790,35 +844,45 @@ acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
return NULL;
}
+static unsigned int acpi_fwnode_get_args_count(struct fwnode_handle *fwnode,
+ const char *nargs_prop)
+{
+ const struct acpi_device_data *data;
+ const union acpi_object *obj;
+ int ret;
+
+ data = acpi_device_data_of_node(fwnode);
+ if (!data)
+ return 0;
+
+ ret = acpi_data_get_property(data, nargs_prop, ACPI_TYPE_INTEGER, &obj);
+ if (ret)
+ return 0;
+
+ return obj->integer.value;
+}
+
static int acpi_get_ref_args(struct fwnode_reference_args *args,
struct fwnode_handle *ref_fwnode,
+ const char *nargs_prop,
const union acpi_object **element,
const union acpi_object *end, size_t num_args)
{
u32 nargs = 0, i;
- /*
- * Find the referred data extension node under the
- * referred device node.
- */
- for (; *element < end && (*element)->type == ACPI_TYPE_STRING;
- (*element)++) {
- const char *child_name = (*element)->string.pointer;
-
- ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name);
- if (!ref_fwnode)
- return -EINVAL;
- }
+ if (nargs_prop)
+ num_args = acpi_fwnode_get_args_count(ref_fwnode, nargs_prop);
/*
* Assume the following integer elements are all args. Stop counting on
- * the first reference or end of the package arguments. In case of
- * neither reference, nor integer, return an error, we can't parse it.
+ * the first reference (possibly represented as a string) or end of the
+ * package arguments. In case of neither reference, nor integer, return
+ * an error, we can't parse it.
*/
for (i = 0; (*element) + i < end && i < num_args; i++) {
acpi_object_type type = (*element)[i].type;
- if (type == ACPI_TYPE_LOCAL_REFERENCE)
+ if (type == ACPI_TYPE_LOCAL_REFERENCE || type == ACPI_TYPE_STRING)
break;
if (type == ACPI_TYPE_INTEGER)
@@ -842,48 +906,53 @@ static int acpi_get_ref_args(struct fwnode_reference_args *args,
return 0;
}
-/**
- * __acpi_node_get_property_reference - returns handle to the referenced object
- * @fwnode: Firmware node to get the property from
- * @propname: Name of the property
- * @index: Index of the reference to return
- * @num_args: Maximum number of arguments after each reference
- * @args: Location to store the returned reference with optional arguments
- *
- * Find property with @name, verifify that it is a package containing at least
- * one object reference and if so, store the ACPI device object pointer to the
- * target object in @args->adev. If the reference includes arguments, store
- * them in the @args->args[] array.
- *
- * If there's more than one reference in the property value package, @index is
- * used to select the one to return.
- *
- * It is possible to leave holes in the property value set like in the
- * example below:
- *
- * Package () {
- * "cs-gpios",
- * Package () {
- * ^GPIO, 19, 0, 0,
- * ^GPIO, 20, 0, 0,
- * 0,
- * ^GPIO, 21, 0, 0,
- * }
- * }
- *
- * Calling this function with index %2 or index %3 return %-ENOENT. If the
- * property does not contain any more values %-ENOENT is returned. The NULL
- * entry must be single integer and preferably contain value %0.
- *
- * Return: %0 on success, negative error code on failure.
- */
-int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
- const char *propname, size_t index, size_t num_args,
- struct fwnode_reference_args *args)
+static struct fwnode_handle *acpi_parse_string_ref(const struct fwnode_handle *fwnode,
+ const char *refstring)
+{
+ acpi_handle scope, handle;
+ struct acpi_data_node *dn;
+ struct acpi_device *device;
+ acpi_status status;
+
+ if (is_acpi_device_node(fwnode)) {
+ scope = to_acpi_device_node(fwnode)->handle;
+ } else if (is_acpi_data_node(fwnode)) {
+ scope = to_acpi_data_node(fwnode)->handle;
+ } else {
+ pr_debug("Bad node type for node %pfw\n", fwnode);
+ return NULL;
+ }
+
+ status = acpi_get_handle(scope, refstring, &handle);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(scope, "Unable to get an ACPI handle for %s\n",
+ refstring);
+ return NULL;
+ }
+
+ device = acpi_fetch_acpi_dev(handle);
+ if (device)
+ return acpi_fwnode_handle(device);
+
+ status = acpi_get_data_full(handle, acpi_nondev_subnode_tag,
+ (void **)&dn, NULL);
+ if (ACPI_FAILURE(status) || !dn) {
+ acpi_handle_debug(handle, "Subnode not found\n");
+ return NULL;
+ }
+
+ return &dn->fwnode;
+}
+
+static int acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode,
+ const char *propname, const char *nargs_prop,
+ unsigned int args_count, unsigned int index,
+ struct fwnode_reference_args *args)
{
const union acpi_object *element, *end;
const union acpi_object *obj;
const struct acpi_device_data *data;
+ struct fwnode_handle *ref_fwnode;
struct acpi_device *device;
int ret, idx = 0;
@@ -905,18 +974,35 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
if (!device)
return -EINVAL;
+ if (!args)
+ return 0;
+
args->fwnode = acpi_fwnode_handle(device);
args->nargs = 0;
+
+ return 0;
+ case ACPI_TYPE_STRING:
+ if (index)
+ return -ENOENT;
+
+ ref_fwnode = acpi_parse_string_ref(fwnode, obj->string.pointer);
+ if (!ref_fwnode)
+ return -EINVAL;
+
+ args->fwnode = ref_fwnode;
+ args->nargs = 0;
+
return 0;
case ACPI_TYPE_PACKAGE:
/*
* If it is not a single reference, then it is a package of
- * references followed by number of ints as follows:
+ * references, followed by number of ints as follows:
*
* Package () { REF, INT, REF, INT, INT }
*
- * The index argument is then used to determine which reference
- * the caller wants (along with the arguments).
+ * Here, REF may be either a local reference or a string. The
+ * index argument is then used to determine which reference the
+ * caller wants (along with the arguments).
*/
break;
default:
@@ -937,10 +1023,27 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
return -EINVAL;
element++;
-
ret = acpi_get_ref_args(idx == index ? args : NULL,
acpi_fwnode_handle(device),
- &element, end, num_args);
+ nargs_prop, &element, end,
+ args_count);
+ if (ret < 0)
+ return ret;
+
+ if (idx == index)
+ return 0;
+
+ break;
+ case ACPI_TYPE_STRING:
+ ref_fwnode = acpi_parse_string_ref(fwnode,
+ element->string.pointer);
+ if (!ref_fwnode)
+ return -EINVAL;
+
+ element++;
+ ret = acpi_get_ref_args(idx == index ? args : NULL,
+ ref_fwnode, nargs_prop, &element, end,
+ args_count);
if (ret < 0)
return ret;
@@ -962,6 +1065,50 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
return -ENOENT;
}
+
+/**
+ * __acpi_node_get_property_reference - returns handle to the referenced object
+ * @fwnode: Firmware node to get the property from
+ * @propname: Name of the property
+ * @index: Index of the reference to return
+ * @num_args: Maximum number of arguments after each reference
+ * @args: Location to store the returned reference with optional arguments
+ * (may be NULL)
+ *
+ * Find property with @name, verifify that it is a package containing at least
+ * one object reference and if so, store the ACPI device object pointer to the
+ * target object in @args->adev. If the reference includes arguments, store
+ * them in the @args->args[] array.
+ *
+ * If there's more than one reference in the property value package, @index is
+ * used to select the one to return.
+ *
+ * It is possible to leave holes in the property value set like in the
+ * example below:
+ *
+ * Package () {
+ * "cs-gpios",
+ * Package () {
+ * ^GPIO, 19, 0, 0,
+ * ^GPIO, 20, 0, 0,
+ * 0,
+ * ^GPIO, 21, 0, 0,
+ * }
+ * }
+ *
+ * Calling this function with index %2 or index %3 return %-ENOENT. If the
+ * property does not contain any more values %-ENOENT is returned. The NULL
+ * entry must be single integer and preferably contain value %0.
+ *
+ * Return: %0 on success, negative error code on failure.
+ */
+int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
+ const char *propname, size_t index,
+ size_t num_args,
+ struct fwnode_reference_args *args)
+{
+ return acpi_fwnode_get_reference_args(fwnode, propname, NULL, num_args, index, args);
+}
EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
static int acpi_data_prop_read_single(const struct acpi_device_data *data,
@@ -969,60 +1116,48 @@ static int acpi_data_prop_read_single(const struct acpi_device_data *data,
enum dev_prop_type proptype, void *val)
{
const union acpi_object *obj;
- int ret;
+ int ret = 0;
- if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) {
+ if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64)
ret = acpi_data_get_property(data, propname, ACPI_TYPE_INTEGER, &obj);
- if (ret)
- return ret;
-
- switch (proptype) {
- case DEV_PROP_U8:
- if (obj->integer.value > U8_MAX)
- return -EOVERFLOW;
-
- if (val)
- *(u8 *)val = obj->integer.value;
-
- break;
- case DEV_PROP_U16:
- if (obj->integer.value > U16_MAX)
- return -EOVERFLOW;
-
- if (val)
- *(u16 *)val = obj->integer.value;
-
- break;
- case DEV_PROP_U32:
- if (obj->integer.value > U32_MAX)
- return -EOVERFLOW;
-
- if (val)
- *(u32 *)val = obj->integer.value;
-
- break;
- default:
- if (val)
- *(u64 *)val = obj->integer.value;
-
- break;
- }
-
- if (!val)
- return 1;
- } else if (proptype == DEV_PROP_STRING) {
+ else if (proptype == DEV_PROP_STRING)
ret = acpi_data_get_property(data, propname, ACPI_TYPE_STRING, &obj);
- if (ret)
- return ret;
+ if (ret)
+ return ret;
+ switch (proptype) {
+ case DEV_PROP_U8:
+ if (obj->integer.value > U8_MAX)
+ return -EOVERFLOW;
+ if (val)
+ *(u8 *)val = obj->integer.value;
+ break;
+ case DEV_PROP_U16:
+ if (obj->integer.value > U16_MAX)
+ return -EOVERFLOW;
+ if (val)
+ *(u16 *)val = obj->integer.value;
+ break;
+ case DEV_PROP_U32:
+ if (obj->integer.value > U32_MAX)
+ return -EOVERFLOW;
+ if (val)
+ *(u32 *)val = obj->integer.value;
+ break;
+ case DEV_PROP_U64:
+ if (val)
+ *(u64 *)val = obj->integer.value;
+ break;
+ case DEV_PROP_STRING:
if (val)
*(char **)val = obj->string.pointer;
-
return 1;
- } else {
- ret = -EINVAL;
+ default:
+ return -EINVAL;
}
- return ret;
+
+ /* When no storage provided return number of available values */
+ return val ? 0 : 1;
}
#define acpi_copy_property_array_uint(items, val, nval) \
@@ -1112,25 +1247,24 @@ static int acpi_data_prop_read(const struct acpi_device_data *data,
switch (proptype) {
case DEV_PROP_STRING:
break;
- case DEV_PROP_U8 ... DEV_PROP_U64:
+ default:
if (obj->type == ACPI_TYPE_BUFFER) {
if (nval > obj->buffer.length)
return -EOVERFLOW;
- break;
+ } else {
+ if (nval > obj->package.count)
+ return -EOVERFLOW;
}
- fallthrough;
- default:
- if (nval > obj->package.count)
- return -EOVERFLOW;
break;
}
- if (nval == 0)
- return -EINVAL;
- if (obj->type != ACPI_TYPE_BUFFER)
- items = obj->package.elements;
- else
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ if (proptype != DEV_PROP_U8)
+ return -EPROTO;
items = obj;
+ } else {
+ items = obj->package.elements;
+ }
switch (proptype) {
case DEV_PROP_U8:
@@ -1146,9 +1280,11 @@ static int acpi_data_prop_read(const struct acpi_device_data *data,
ret = acpi_copy_property_array_uint(items, (u64 *)val, nval);
break;
case DEV_PROP_STRING:
- ret = acpi_copy_property_array_string(
- items, (char **)val,
- min_t(u32, nval, obj->package.count));
+ nval = min(nval, obj->package.count);
+ if (nval == 0)
+ return -ENODATA;
+
+ ret = acpi_copy_property_array_string(items, (char **)val, nval);
break;
default:
ret = -EINVAL;
@@ -1193,13 +1329,14 @@ static int stop_on_next(struct acpi_device *adev, void *data)
return 0;
}
-/**
+/*
* acpi_get_next_subnode - Return the next child node handle for a fwnode
* @fwnode: Firmware node to find the next child node for.
* @child: Handle to one of the device's child nodes or a null handle.
*/
-struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode,
- struct fwnode_handle *child)
+static struct fwnode_handle *
+acpi_get_next_subnode(const struct fwnode_handle *fwnode,
+ struct fwnode_handle *child)
{
struct acpi_device *adev = to_acpi_device_node(fwnode);
@@ -1252,6 +1389,28 @@ struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode,
return NULL;
}
+/*
+ * acpi_get_next_present_subnode - Return the next present child node handle
+ * @fwnode: Firmware node to find the next child node for.
+ * @child: Handle to one of the device's child nodes or a null handle.
+ *
+ * Like acpi_get_next_subnode(), but the device nodes returned by
+ * acpi_get_next_present_subnode() are guaranteed to be present.
+ *
+ * Returns: The fwnode handle of the next present sub-node.
+ */
+static struct fwnode_handle *
+acpi_get_next_present_subnode(const struct fwnode_handle *fwnode,
+ struct fwnode_handle *child)
+{
+ do {
+ child = acpi_get_next_subnode(fwnode, child);
+ } while (is_acpi_device_node(child) &&
+ !acpi_device_is_present(to_acpi_device_node(child)));
+
+ return child;
+}
+
/**
* acpi_node_get_parent - Return parent fwnode of this fwnode
* @fwnode: Firmware node whose parent to get
@@ -1267,10 +1426,11 @@ acpi_node_get_parent(const struct fwnode_handle *fwnode)
return to_acpi_data_node(fwnode)->parent;
}
if (is_acpi_device_node(fwnode)) {
- struct device *dev = to_acpi_device_node(fwnode)->dev.parent;
+ struct acpi_device *parent;
- if (dev)
- return acpi_fwnode_handle(to_acpi_device(dev));
+ parent = acpi_dev_parent(to_acpi_device_node(fwnode));
+ if (parent)
+ return acpi_fwnode_handle(parent);
}
return NULL;
@@ -1313,7 +1473,7 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint(
if (!prev) {
do {
- port = fwnode_get_next_child_node(fwnode, port);
+ port = acpi_get_next_subnode(fwnode, port);
/*
* The names of the port nodes begin with "port@"
* followed by the number of the port node and they also
@@ -1331,14 +1491,17 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint(
if (!port)
return NULL;
- endpoint = fwnode_get_next_child_node(port, prev);
- while (!endpoint) {
- port = fwnode_get_next_child_node(fwnode, port);
- if (!port)
+ do {
+ endpoint = acpi_get_next_subnode(port, prev);
+ if (endpoint)
break;
- if (is_acpi_graph_node(port, "port"))
- endpoint = fwnode_get_next_child_node(port, NULL);
- }
+
+ prev = NULL;
+
+ do {
+ port = acpi_get_next_subnode(fwnode, port);
+ } while (port && !is_acpi_graph_node(port, "port"));
+ } while (port);
/*
* The names of the endpoint nodes begin with "endpoint@" followed by
@@ -1425,7 +1588,7 @@ acpi_graph_get_remote_endpoint(const struct fwnode_handle *__fwnode)
static bool acpi_fwnode_device_is_available(const struct fwnode_handle *fwnode)
{
if (!is_acpi_device_node(fwnode))
- return false;
+ return true;
return acpi_device_is_present(to_acpi_device_node(fwnode));
}
@@ -1491,16 +1654,6 @@ acpi_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
val, nval);
}
-static int
-acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode,
- const char *prop, const char *nargs_prop,
- unsigned int args_count, unsigned int index,
- struct fwnode_reference_args *args)
-{
- return __acpi_node_get_property_reference(fwnode, prop, index,
- args_count, args);
-}
-
static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode)
{
const struct acpi_device *adev;
@@ -1565,6 +1718,7 @@ static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id))
fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
+ fwnode_handle_put(port_fwnode);
return 0;
}
@@ -1589,12 +1743,13 @@ static int acpi_fwnode_irq_get(const struct fwnode_handle *fwnode,
acpi_fwnode_device_dma_supported, \
.device_get_dma_attr = acpi_fwnode_device_get_dma_attr, \
.property_present = acpi_fwnode_property_present, \
+ .property_read_bool = acpi_fwnode_property_present, \
.property_read_int_array = \
acpi_fwnode_property_read_int_array, \
.property_read_string_array = \
acpi_fwnode_property_read_string_array, \
.get_parent = acpi_node_get_parent, \
- .get_next_child_node = acpi_get_next_subnode, \
+ .get_next_child_node = acpi_get_next_present_subnode, \
.get_named_child_node = acpi_fwnode_get_named_child_node, \
.get_name = acpi_fwnode_get_name, \
.get_name_prefix = acpi_fwnode_get_name_prefix, \
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 510cdec375c4..d16906f46484 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/dmi.h>
+#include <linux/string_choices.h>
#ifdef CONFIG_X86
#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
@@ -250,6 +251,9 @@ static bool acpi_decode_space(struct resource_win *win,
switch (addr->resource_type) {
case ACPI_MEMORY_RANGE:
acpi_dev_memresource_flags(res, len, wp);
+
+ if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
+ res->flags |= IORESOURCE_PREFETCH;
break;
case ACPI_IO_RANGE:
acpi_dev_ioresource_flags(res, len, iodec,
@@ -265,9 +269,6 @@ static bool acpi_decode_space(struct resource_win *win,
if (addr->producer_consumer == ACPI_PRODUCER)
res->flags |= IORESOURCE_WINDOW;
- if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
- res->flags |= IORESOURCE_PREFETCH;
-
return !(res->flags & IORESOURCE_DISABLED);
}
@@ -336,8 +337,9 @@ EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space);
* @triggering: Triggering type as provided by ACPI.
* @polarity: Interrupt polarity as provided by ACPI.
* @shareable: Whether or not the interrupt is shareable.
+ * @wake_capable: Wake capability as provided by ACPI.
*/
-unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable)
+unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable, u8 wake_capable)
{
unsigned long flags;
@@ -351,6 +353,9 @@ unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable)
if (shareable == ACPI_SHARED)
flags |= IORESOURCE_IRQ_SHAREABLE;
+ if (wake_capable == ACPI_WAKE_CAPABLE)
+ flags |= IORESOURCE_IRQ_WAKECAPABLE;
+
return flags | IORESOURCE_IRQ;
}
EXPORT_SYMBOL_GPL(acpi_dev_irq_flags);
@@ -381,21 +386,312 @@ unsigned int acpi_dev_get_irq_type(int triggering, int polarity)
}
EXPORT_SYMBOL_GPL(acpi_dev_get_irq_type);
-static const struct dmi_system_id medion_laptop[] = {
+/*
+ * DMI matches for boards where the DSDT specifies the kbd IRQ as
+ * level active-low and using the override changes this to rising edge,
+ * stopping the keyboard from working.
+ */
+static const struct dmi_system_id irq1_level_low_skip_override[] = {
{
- .ident = "MEDION P15651",
+ /* MEDION P15651 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
DMI_MATCH(DMI_BOARD_NAME, "M15T"),
},
},
{
- .ident = "MEDION S17405",
+ /* MEDION S17405 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
DMI_MATCH(DMI_BOARD_NAME, "M17T"),
},
},
+ {
+ /* MEDION S17413 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_BOARD_NAME, "M1xA"),
+ },
+ },
+ {
+ /* Asus Vivobook K3402ZA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "K3402ZA"),
+ },
+ },
+ {
+ /* Asus Vivobook K3502ZA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "K3502ZA"),
+ },
+ },
+ {
+ /* Asus Vivobook S5402ZA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "S5402ZA"),
+ },
+ },
+ {
+ /* Asus Vivobook S5602ZA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "S5602ZA"),
+ },
+ },
+ {
+ /* Asus Vivobook X1404VAP */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "X1404VAP"),
+ },
+ },
+ {
+ /* Asus Vivobook X1504VAP */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "X1504VAP"),
+ },
+ },
+ {
+ /* Asus Vivobook X1704VAP */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "X1704VAP"),
+ },
+ },
+ {
+ /* Asus ExpertBook B1402C* */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B1402C"),
+ },
+ },
+ {
+ /* Asus ExpertBook B1502C* */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B1502C"),
+ },
+ },
+ {
+ /* Asus ExpertBook B2402 (B2402CBA / B2402FBA / B2402CVA / B2402FVA) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B2402"),
+ },
+ },
+ {
+ /* Asus ExpertBook B2502 (B2502CBA / B2502FBA / B2502CVA / B2502FVA) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B2502"),
+ },
+ },
+ {
+ /* Asus Vivobook Go E1404GA* */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "E1404GA"),
+ },
+ },
+ {
+ /* Asus Vivobook E1504GA* */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "E1504GA"),
+ },
+ },
+ {
+ /* Asus Vivobook Pro N6506M* */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "N6506M"),
+ },
+ },
+ {
+ /* Asus Vivobook Pro N6506CU* */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "N6506CU"),
+ },
+ },
+ {
+ /* LG Electronics 17U70P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+ DMI_MATCH(DMI_BOARD_NAME, "17U70P"),
+ },
+ },
+ {
+ /* LG Electronics 16T90SP */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+ DMI_MATCH(DMI_BOARD_NAME, "16T90SP"),
+ },
+ },
+ { }
+};
+
+/*
+ * DMI matches for AMD Zen boards where the DSDT specifies the kbd IRQ
+ * as falling edge and this must be overridden to rising edge,
+ * to have a working keyboard.
+ */
+static const struct dmi_system_id irq1_edge_low_force_override[] = {
+ {
+ /* MECHREVO Jiaolong17KS Series GM7XG0M */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM7XG0M"),
+ },
+ },
+ {
+ /* XMG APEX 17 (M23) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxBGxx"),
+ },
+ },
+ {
+ /* TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
+ },
+ },
+ {
+ /* TongFang GMxXGxx/TUXEDO Polaris 15 Gen5 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxXGxx"),
+ },
+ },
+ {
+ /* TongFang GMxXGxX/TUXEDO Polaris 15 Gen5 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxXGxX"),
+ },
+ },
+ {
+ /* TongFang GMxXGxx sold as Eluktronics Inc. RP-15 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Eluktronics Inc."),
+ DMI_MATCH(DMI_BOARD_NAME, "RP-15"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Eluktronics Inc."),
+ DMI_MATCH(DMI_BOARD_NAME, "MECH-17"),
+ },
+ },
+ {
+ /* TongFang GM6XGxX/TUXEDO Stellaris 16 Gen5 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM6XGxX"),
+ },
+ },
+ {
+ /* MAINGEAR Vector Pro 2 15 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-15A3070T"),
+ }
+ },
+ {
+ /* MAINGEAR Vector Pro 2 17 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-17A3070T"),
+ },
+ },
+ {
+ /* TongFang GM6BGEQ / PCSpecialist Elimina Pro 16 M, RTX 3050 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM6BGEQ"),
+ },
+ },
+ {
+ /* TongFang GM6BG5Q, RTX 4050 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM6BG5Q"),
+ },
+ },
+ {
+ /* TongFang GM6BG0Q / PCSpecialist Elimina Pro 16 M, RTX 4060 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM6BG0Q"),
+ },
+ },
+ {
+ /* Infinity E15-5A165-BM */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM5RG1E0009COM"),
+ },
+ },
+ {
+ /* Infinity E15-5A305-1M */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GM5RGEE0016COM"),
+ },
+ },
+ {
+ /* Lunnen Ground 15 / AMD Ryzen 5 5500U */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Lunnen"),
+ DMI_MATCH(DMI_BOARD_NAME, "LLL5DAW"),
+ },
+ },
+ {
+ /* Lunnen Ground 16 / AMD Ryzen 7 5800U */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Lunnen"),
+ DMI_MATCH(DMI_BOARD_NAME, "LL6FA"),
+ },
+ },
+ {
+ /* MAIBENBEN X577 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MAIBENBEN"),
+ DMI_MATCH(DMI_BOARD_NAME, "X577"),
+ },
+ },
+ {
+ /* Maibenben X565 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MAIBENBEN"),
+ DMI_MATCH(DMI_BOARD_NAME, "X565"),
+ },
+ },
+ {
+ /* TongFang GXxHRXx/TUXEDO InfinityBook Pro Gen9 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GXxHRXx"),
+ },
+ },
+ {
+ /* TongFang GMxHGxx/TUXEDO Stellaris Slim Gen1 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxHGxx"),
+ },
+ },
+ {
+ /* MACHENIKE L16P/L16P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MACHENIKE"),
+ DMI_MATCH(DMI_BOARD_NAME, "L16P"),
+ },
+ },
+ {
+ /*
+ * TongFang GM5HG0A in case of the SKIKK Vanaheim relabel the
+ * board-name is changed, so check OEM strings instead. Note
+ * OEM string matches are always exact matches.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=219614
+ */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_OEM_STRING, "GM5HG0A"),
+ },
+ },
{ }
};
@@ -405,10 +701,12 @@ struct irq_override_cmp {
unsigned char triggering;
unsigned char polarity;
unsigned char shareable;
+ bool override;
};
-static const struct irq_override_cmp skip_override_table[] = {
- { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0 },
+static const struct irq_override_cmp override_table[] = {
+ { irq1_level_low_skip_override, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
+ { irq1_edge_low_force_override, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true },
};
static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
@@ -416,8 +714,31 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
{
int i;
+ for (i = 0; i < ARRAY_SIZE(override_table); i++) {
+ const struct irq_override_cmp *entry = &override_table[i];
+
+ if (entry->irq == gsi &&
+ entry->triggering == triggering &&
+ entry->polarity == polarity &&
+ entry->shareable == shareable &&
+ dmi_check_system(entry->system))
+ return entry->override;
+ }
+
#ifdef CONFIG_X86
/*
+ * Always use the MADT override info, except for the i8042 PS/2 ctrl
+ * IRQs (1 and 12). For these the DSDT IRQ settings should sometimes
+ * be used otherwise PS/2 keyboards / mice will not work.
+ */
+ if (gsi != 1 && gsi != 12)
+ return true;
+
+ /* If the override comes from an INT_SRC_OVR MADT entry, honor it. */
+ if (acpi_int_src_ovr[gsi])
+ return true;
+
+ /*
* IRQ override isn't needed on modern AMD Zen systems and
* this override breaks active low IRQs on AMD Ryzen 6000 and
* newer systems. Skip it.
@@ -426,23 +747,12 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
return false;
#endif
- for (i = 0; i < ARRAY_SIZE(skip_override_table); i++) {
- const struct irq_override_cmp *entry = &skip_override_table[i];
-
- if (dmi_check_system(entry->system) &&
- entry->irq == gsi &&
- entry->triggering == triggering &&
- entry->polarity == polarity &&
- entry->shareable == shareable)
- return false;
- }
-
return true;
}
static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
u8 triggering, u8 polarity, u8 shareable,
- bool check_override)
+ u8 wake_capable, bool check_override)
{
int irq, p, t;
@@ -468,14 +778,17 @@ static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;
if (triggering != trig || polarity != pol) {
- pr_warn("ACPI: IRQ %d override to %s, %s\n", gsi,
- t ? "level" : "edge", p ? "low" : "high");
+ pr_warn("ACPI: IRQ %d override to %s%s, %s%s\n", gsi,
+ t ? "level" : "edge",
+ trig == triggering ? "" : "(!)",
+ str_low_high(p),
+ pol == polarity ? "" : "(!)");
triggering = trig;
polarity = pol;
}
}
- res->flags = acpi_dev_irq_flags(triggering, polarity, shareable);
+ res->flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable);
irq = acpi_register_gsi(NULL, gsi, triggering, polarity);
if (irq >= 0) {
res->start = irq;
@@ -523,7 +836,8 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
}
acpi_dev_get_irqresource(res, irq->interrupts[index],
irq->triggering, irq->polarity,
- irq->shareable, true);
+ irq->shareable, irq->wake_capable,
+ true);
break;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
ext_irq = &ares->data.extended_irq;
@@ -534,7 +848,8 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
if (is_gsi(ext_irq))
acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
ext_irq->triggering, ext_irq->polarity,
- ext_irq->shareable, false);
+ ext_irq->shareable, ext_irq->wake_capable,
+ false);
else
irqresource_disabled(res, 0);
break;
@@ -690,6 +1005,9 @@ static int is_memory(struct acpi_resource *ares, void *not_used)
memset(&win, 0, sizeof(win));
+ if (acpi_dev_filter_resource_type(ares, IORESOURCE_MEM))
+ return 1;
+
return !(acpi_dev_resource_memory(ares, res)
|| acpi_dev_resource_address_space(ares, &win)
|| acpi_dev_resource_ext_address_space(ares, &win));
@@ -719,6 +1037,23 @@ int acpi_dev_get_dma_resources(struct acpi_device *adev, struct list_head *list)
EXPORT_SYMBOL_GPL(acpi_dev_get_dma_resources);
/**
+ * acpi_dev_get_memory_resources - Get current memory resources of a device.
+ * @adev: ACPI device node to get the resources for.
+ * @list: Head of the resultant list of resources (must be empty).
+ *
+ * This is a helper function that locates all memory type resources of @adev
+ * with acpi_dev_get_resources().
+ *
+ * The number of resources in the output list is returned on success, an error
+ * code reflecting the error condition is returned otherwise.
+ */
+int acpi_dev_get_memory_resources(struct acpi_device *adev, struct list_head *list)
+{
+ return acpi_dev_get_resources(adev, list, is_memory, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_memory_resources);
+
+/**
* acpi_dev_filter_resource_type - Filter ACPI resource according to resource
* types
* @ares: Input ACPI resource object.
diff --git a/drivers/acpi/riscv/Kconfig b/drivers/acpi/riscv/Kconfig
new file mode 100644
index 000000000000..046296a18d00
--- /dev/null
+++ b/drivers/acpi/riscv/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ACPI Configuration for RISC-V
+#
+
+config ACPI_RIMT
+ bool
diff --git a/drivers/acpi/riscv/Makefile b/drivers/acpi/riscv/Makefile
new file mode 100644
index 000000000000..1284a076fa88
--- /dev/null
+++ b/drivers/acpi/riscv/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += rhct.o init.o irq.o
+obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o
+obj-$(CONFIG_ACPI_CPPC_LIB) += cppc.o
+obj-$(CONFIG_ACPI_RIMT) += rimt.o
diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c
new file mode 100644
index 000000000000..42c1a9052470
--- /dev/null
+++ b/drivers/acpi/riscv/cppc.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implement CPPC FFH helper routines for RISC-V.
+ *
+ * Copyright (C) 2024 Ventana Micro Systems Inc.
+ */
+
+#include <acpi/cppc_acpi.h>
+#include <asm/csr.h>
+#include <asm/sbi.h>
+
+#define SBI_EXT_CPPC 0x43505043
+
+/* CPPC interfaces defined in SBI spec */
+#define SBI_CPPC_PROBE 0x0
+#define SBI_CPPC_READ 0x1
+#define SBI_CPPC_READ_HI 0x2
+#define SBI_CPPC_WRITE 0x3
+
+/* RISC-V FFH definitions from RISC-V FFH spec */
+#define FFH_CPPC_TYPE(r) (((r) & GENMASK_ULL(63, 60)) >> 60)
+#define FFH_CPPC_SBI_REG(r) ((r) & GENMASK(31, 0))
+#define FFH_CPPC_CSR_NUM(r) ((r) & GENMASK(11, 0))
+
+#define FFH_CPPC_SBI 0x1
+#define FFH_CPPC_CSR 0x2
+
+struct sbi_cppc_data {
+ u64 val;
+ u32 reg;
+ struct sbiret ret;
+};
+
+static bool cppc_ext_present;
+
+static int __init sbi_cppc_init(void)
+{
+ if (sbi_spec_version >= sbi_mk_version(2, 0) &&
+ sbi_probe_extension(SBI_EXT_CPPC) > 0) {
+ cppc_ext_present = true;
+ } else {
+ cppc_ext_present = false;
+ }
+
+ return 0;
+}
+device_initcall(sbi_cppc_init);
+
+static void sbi_cppc_read(void *read_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
+
+ data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ,
+ data->reg, 0, 0, 0, 0, 0);
+}
+
+static void sbi_cppc_write(void *write_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
+
+ data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE,
+ data->reg, data->val, 0, 0, 0, 0);
+}
+
+static void cppc_ffh_csr_read(void *read_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
+
+ switch (data->reg) {
+ /* Support only TIME CSR for now */
+ case CSR_TIME:
+ data->ret.value = csr_read(CSR_TIME);
+ data->ret.error = 0;
+ break;
+ default:
+ data->ret.error = -EINVAL;
+ break;
+ }
+}
+
+static void cppc_ffh_csr_write(void *write_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
+
+ data->ret.error = -EINVAL;
+}
+
+/*
+ * Refer to drivers/acpi/cppc_acpi.c for the description of the functions
+ * below.
+ */
+bool cpc_ffh_supported(void)
+{
+ return true;
+}
+
+int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
+{
+ struct sbi_cppc_data data;
+
+ if (WARN_ON_ONCE(irqs_disabled()))
+ return -EPERM;
+
+ if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
+ if (!cppc_ext_present)
+ return -EINVAL;
+
+ data.reg = FFH_CPPC_SBI_REG(reg->address);
+
+ smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
+
+ *val = data.ret.value;
+
+ return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
+ } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
+ data.reg = FFH_CPPC_CSR_NUM(reg->address);
+
+ smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
+
+ *val = data.ret.value;
+
+ return data.ret.error;
+ }
+
+ return -EINVAL;
+}
+
+int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
+{
+ struct sbi_cppc_data data;
+
+ if (WARN_ON_ONCE(irqs_disabled()))
+ return -EPERM;
+
+ if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
+ if (!cppc_ext_present)
+ return -EINVAL;
+
+ data.reg = FFH_CPPC_SBI_REG(reg->address);
+ data.val = val;
+
+ smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
+
+ return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
+ } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
+ data.reg = FFH_CPPC_CSR_NUM(reg->address);
+ data.val = val;
+
+ smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
+
+ return data.ret.error;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/acpi/riscv/cpuidle.c b/drivers/acpi/riscv/cpuidle.c
new file mode 100644
index 000000000000..624f9bbdb58c
--- /dev/null
+++ b/drivers/acpi/riscv/cpuidle.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024, Ventana Micro Systems Inc
+ * Author: Sunil V L <sunilvl@ventanamicro.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpuidle.h>
+#include <linux/suspend.h>
+#include <asm/cpuidle.h>
+#include <asm/sbi.h>
+#include <asm/suspend.h>
+
+#define RISCV_FFH_LPI_TYPE_MASK GENMASK_ULL(63, 60)
+#define RISCV_FFH_LPI_RSVD_MASK GENMASK_ULL(59, 32)
+
+#define RISCV_FFH_LPI_TYPE_SBI BIT_ULL(60)
+
+static int acpi_cpu_init_idle(unsigned int cpu)
+{
+ int i;
+ struct acpi_lpi_state *lpi;
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+
+ if (unlikely(!pr || !pr->flags.has_lpi))
+ return -EINVAL;
+
+ if (!riscv_sbi_hsm_is_supported())
+ return -ENODEV;
+
+ if (pr->power.count <= 1)
+ return -ENODEV;
+
+ for (i = 1; i < pr->power.count; i++) {
+ u32 state;
+
+ lpi = &pr->power.lpi_states[i];
+
+ /*
+ * Validate Entry Method as per FFH spec.
+ * bits[63:60] should be 0x1
+ * bits[59:32] should be 0x0
+ * bits[31:0] represent a SBI power_state
+ */
+ if (((lpi->address & RISCV_FFH_LPI_TYPE_MASK) != RISCV_FFH_LPI_TYPE_SBI) ||
+ (lpi->address & RISCV_FFH_LPI_RSVD_MASK)) {
+ pr_warn("Invalid LPI entry method %#llx\n", lpi->address);
+ return -EINVAL;
+ }
+
+ state = lpi->address;
+ if (!riscv_sbi_suspend_state_is_valid(state)) {
+ pr_warn("Invalid SBI power state %#x\n", state);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int acpi_processor_ffh_lpi_probe(unsigned int cpu)
+{
+ return acpi_cpu_init_idle(cpu);
+}
+
+int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
+{
+ u32 state = lpi->address;
+
+ if (state & SBI_HSM_SUSP_NON_RET_BIT)
+ return CPU_PM_CPU_IDLE_ENTER_PARAM(riscv_sbi_hart_suspend,
+ lpi->index,
+ state);
+ else
+ return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(riscv_sbi_hart_suspend,
+ lpi->index,
+ state);
+}
diff --git a/drivers/acpi/riscv/init.c b/drivers/acpi/riscv/init.c
new file mode 100644
index 000000000000..7c00f7995e86
--- /dev/null
+++ b/drivers/acpi/riscv/init.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023-2024, Ventana Micro Systems Inc
+ * Author: Sunil V L <sunilvl@ventanamicro.com>
+ */
+
+#include <linux/acpi.h>
+#include "init.h"
+
+void __init acpi_arch_init(void)
+{
+ riscv_acpi_init_gsi_mapping();
+ if (IS_ENABLED(CONFIG_ACPI_RIMT))
+ riscv_acpi_rimt_init();
+}
diff --git a/drivers/acpi/riscv/init.h b/drivers/acpi/riscv/init.h
new file mode 100644
index 000000000000..1680aa2aaf23
--- /dev/null
+++ b/drivers/acpi/riscv/init.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/init.h>
+
+void __init riscv_acpi_init_gsi_mapping(void);
+void __init riscv_acpi_rimt_init(void);
diff --git a/drivers/acpi/riscv/irq.c b/drivers/acpi/riscv/irq.c
new file mode 100644
index 000000000000..d9a2154d6c6a
--- /dev/null
+++ b/drivers/acpi/riscv/irq.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023-2024, Ventana Micro Systems Inc
+ * Author: Sunil V L <sunilvl@ventanamicro.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/sort.h>
+#include <linux/irq.h>
+
+#include "init.h"
+
+#define RISCV_ACPI_INTC_FLAG_PENDING BIT(0)
+
+struct riscv_ext_intc_list {
+ acpi_handle handle;
+ u32 gsi_base;
+ u32 nr_irqs;
+ u32 nr_idcs;
+ u32 id;
+ u32 type;
+ u32 flag;
+ struct list_head list;
+};
+
+struct acpi_irq_dep_ctx {
+ int rc;
+ unsigned int index;
+ acpi_handle handle;
+};
+
+LIST_HEAD(ext_intc_list);
+
+static int irqchip_cmp_func(const void *in0, const void *in1)
+{
+ struct acpi_probe_entry *elem0 = (struct acpi_probe_entry *)in0;
+ struct acpi_probe_entry *elem1 = (struct acpi_probe_entry *)in1;
+
+ return (elem0->type > elem1->type) - (elem0->type < elem1->type);
+}
+
+/*
+ * On RISC-V, RINTC structures in MADT should be probed before any other
+ * interrupt controller structures and IMSIC before APLIC. The interrupt
+ * controller subtypes in MADT of ACPI spec for RISC-V are defined in
+ * the incremental order like RINTC(24)->IMSIC(25)->APLIC(26)->PLIC(27).
+ * Hence, simply sorting the subtypes in incremental order will
+ * establish the required order.
+ */
+void arch_sort_irqchip_probe(struct acpi_probe_entry *ap_head, int nr)
+{
+ struct acpi_probe_entry *ape = ap_head;
+
+ if (nr == 1 || !ACPI_COMPARE_NAMESEG(ACPI_SIG_MADT, ape->id))
+ return;
+ sort(ape, nr, sizeof(*ape), irqchip_cmp_func, NULL);
+}
+
+static acpi_status riscv_acpi_update_gsi_handle(u32 gsi_base, acpi_handle handle)
+{
+ struct riscv_ext_intc_list *ext_intc_element;
+ struct list_head *i, *tmp;
+
+ list_for_each_safe(i, tmp, &ext_intc_list) {
+ ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
+ if (gsi_base == ext_intc_element->gsi_base) {
+ ext_intc_element->handle = handle;
+ return AE_OK;
+ }
+ }
+
+ return AE_NOT_FOUND;
+}
+
+int riscv_acpi_update_gsi_range(u32 gsi_base, u32 nr_irqs)
+{
+ struct riscv_ext_intc_list *ext_intc_element;
+
+ list_for_each_entry(ext_intc_element, &ext_intc_list, list) {
+ if (gsi_base == ext_intc_element->gsi_base &&
+ (ext_intc_element->flag & RISCV_ACPI_INTC_FLAG_PENDING)) {
+ ext_intc_element->nr_irqs = nr_irqs;
+ ext_intc_element->flag &= ~RISCV_ACPI_INTC_FLAG_PENDING;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
+ u32 *id, u32 *nr_irqs, u32 *nr_idcs)
+{
+ struct riscv_ext_intc_list *ext_intc_element;
+ struct list_head *i;
+
+ list_for_each(i, &ext_intc_list) {
+ ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
+ if (ext_intc_element->handle == ACPI_HANDLE_FWNODE(fwnode)) {
+ *gsi_base = ext_intc_element->gsi_base;
+ *id = ext_intc_element->id;
+ *nr_irqs = ext_intc_element->nr_irqs;
+ if (nr_idcs)
+ *nr_idcs = ext_intc_element->nr_idcs;
+
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+struct fwnode_handle *riscv_acpi_get_gsi_domain_id(u32 gsi)
+{
+ struct riscv_ext_intc_list *ext_intc_element;
+ struct acpi_device *adev;
+ struct list_head *i;
+
+ list_for_each(i, &ext_intc_list) {
+ ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
+ if (gsi >= ext_intc_element->gsi_base &&
+ gsi < (ext_intc_element->gsi_base + ext_intc_element->nr_irqs)) {
+ adev = acpi_fetch_acpi_dev(ext_intc_element->handle);
+ if (!adev)
+ return NULL;
+
+ return acpi_fwnode_handle(adev);
+ }
+ }
+
+ return NULL;
+}
+
+static int __init riscv_acpi_register_ext_intc(u32 gsi_base, u32 nr_irqs, u32 nr_idcs,
+ u32 id, u32 type)
+{
+ struct riscv_ext_intc_list *ext_intc_element, *node, *prev;
+
+ ext_intc_element = kzalloc(sizeof(*ext_intc_element), GFP_KERNEL);
+ if (!ext_intc_element)
+ return -ENOMEM;
+
+ ext_intc_element->gsi_base = gsi_base;
+
+ /* If nr_irqs is zero, indicate it in flag and set to max range possible */
+ if (nr_irqs) {
+ ext_intc_element->nr_irqs = nr_irqs;
+ } else {
+ ext_intc_element->flag |= RISCV_ACPI_INTC_FLAG_PENDING;
+ ext_intc_element->nr_irqs = U32_MAX - ext_intc_element->gsi_base;
+ }
+
+ ext_intc_element->nr_idcs = nr_idcs;
+ ext_intc_element->id = id;
+ list_for_each_entry(node, &ext_intc_list, list) {
+ if (node->gsi_base < ext_intc_element->gsi_base)
+ break;
+ }
+
+ /* Adjust the previous node's GSI range if that has pending registration */
+ prev = list_prev_entry(node, list);
+ if (!list_entry_is_head(prev, &ext_intc_list, list)) {
+ if (prev->flag & RISCV_ACPI_INTC_FLAG_PENDING)
+ prev->nr_irqs = ext_intc_element->gsi_base - prev->gsi_base;
+ }
+
+ list_add_tail(&ext_intc_element->list, &node->list);
+ return 0;
+}
+
+static acpi_status __init riscv_acpi_create_gsi_map_smsi(acpi_handle handle, u32 level,
+ void *context, void **return_value)
+{
+ acpi_status status;
+ u64 gbase;
+
+ if (!acpi_has_method(handle, "_GSB")) {
+ acpi_handle_err(handle, "_GSB method not found\n");
+ return AE_ERROR;
+ }
+
+ status = acpi_evaluate_integer(handle, "_GSB", NULL, &gbase);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "failed to evaluate _GSB method\n");
+ return status;
+ }
+
+ riscv_acpi_register_ext_intc(gbase, 0, 0, 0, ACPI_RISCV_IRQCHIP_SMSI);
+ status = riscv_acpi_update_gsi_handle((u32)gbase, handle);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "failed to find the GSI mapping entry\n");
+ return status;
+ }
+
+ return AE_OK;
+}
+
+static acpi_status __init riscv_acpi_create_gsi_map(acpi_handle handle, u32 level,
+ void *context, void **return_value)
+{
+ acpi_status status;
+ u64 gbase;
+
+ if (!acpi_has_method(handle, "_GSB")) {
+ acpi_handle_err(handle, "_GSB method not found\n");
+ return AE_ERROR;
+ }
+
+ status = acpi_evaluate_integer(handle, "_GSB", NULL, &gbase);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "failed to evaluate _GSB method\n");
+ return status;
+ }
+
+ status = riscv_acpi_update_gsi_handle((u32)gbase, handle);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "failed to find the GSI mapping entry\n");
+ return status;
+ }
+
+ return AE_OK;
+}
+
+static int __init riscv_acpi_aplic_parse_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_aplic *aplic = (struct acpi_madt_aplic *)header;
+
+ return riscv_acpi_register_ext_intc(aplic->gsi_base, aplic->num_sources, aplic->num_idcs,
+ aplic->id, ACPI_RISCV_IRQCHIP_APLIC);
+}
+
+static int __init riscv_acpi_plic_parse_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_plic *plic = (struct acpi_madt_plic *)header;
+
+ return riscv_acpi_register_ext_intc(plic->gsi_base, plic->num_irqs, 0,
+ plic->id, ACPI_RISCV_IRQCHIP_PLIC);
+}
+
+void __init riscv_acpi_init_gsi_mapping(void)
+{
+ /* There can be either PLIC or APLIC */
+ if (acpi_table_parse_madt(ACPI_MADT_TYPE_PLIC, riscv_acpi_plic_parse_madt, 0) > 0) {
+ acpi_get_devices("RSCV0001", riscv_acpi_create_gsi_map, NULL, NULL);
+ return;
+ }
+
+ if (acpi_table_parse_madt(ACPI_MADT_TYPE_APLIC, riscv_acpi_aplic_parse_madt, 0) > 0)
+ acpi_get_devices("RSCV0002", riscv_acpi_create_gsi_map, NULL, NULL);
+
+ /* Unlike PLIC/APLIC, SYSMSI doesn't have MADT */
+ acpi_get_devices("RSCV0006", riscv_acpi_create_gsi_map_smsi, NULL, NULL);
+}
+
+static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi)
+{
+ struct riscv_ext_intc_list *ext_intc_element;
+ struct list_head *i;
+
+ list_for_each(i, &ext_intc_list) {
+ ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
+ if (gsi >= ext_intc_element->gsi_base &&
+ gsi < (ext_intc_element->gsi_base + ext_intc_element->nr_irqs))
+ return ext_intc_element->handle;
+ }
+
+ return NULL;
+}
+
+static acpi_status riscv_acpi_irq_get_parent(struct acpi_resource *ares, void *context)
+{
+ struct acpi_irq_dep_ctx *ctx = context;
+ struct acpi_resource_irq *irq;
+ struct acpi_resource_extended_irq *eirq;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ irq = &ares->data.irq;
+ if (ctx->index >= irq->interrupt_count) {
+ ctx->index -= irq->interrupt_count;
+ return AE_OK;
+ }
+ ctx->handle = riscv_acpi_get_gsi_handle(irq->interrupts[ctx->index]);
+ return AE_CTRL_TERMINATE;
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ eirq = &ares->data.extended_irq;
+ if (eirq->producer_consumer == ACPI_PRODUCER)
+ return AE_OK;
+
+ if (ctx->index >= eirq->interrupt_count) {
+ ctx->index -= eirq->interrupt_count;
+ return AE_OK;
+ }
+
+ /* Support GSIs only */
+ if (eirq->resource_source.string_length)
+ return AE_OK;
+
+ ctx->handle = riscv_acpi_get_gsi_handle(eirq->interrupts[ctx->index]);
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+static int riscv_acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle)
+{
+ struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL};
+
+ if (!gsi_handle)
+ return 0;
+
+ acpi_walk_resources(handle, METHOD_NAME__CRS, riscv_acpi_irq_get_parent, &ctx);
+ *gsi_handle = ctx.handle;
+ if (*gsi_handle)
+ return 1;
+
+ return 0;
+}
+
+static u32 riscv_acpi_add_prt_dep(acpi_handle handle)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_pci_routing_table *entry;
+ struct acpi_handle_list dep_devices;
+ acpi_handle gsi_handle;
+ acpi_handle link_handle;
+ acpi_status status;
+ u32 count = 0;
+
+ status = acpi_get_irq_routing_table(handle, &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_err(handle, "failed to get IRQ routing table\n");
+ kfree(buffer.pointer);
+ return 0;
+ }
+
+ entry = buffer.pointer;
+ while (entry && (entry->length > 0)) {
+ if (entry->source[0]) {
+ acpi_get_handle(handle, entry->source, &link_handle);
+ dep_devices.count = 1;
+ dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
+ if (!dep_devices.handles) {
+ acpi_handle_err(handle, "failed to allocate memory\n");
+ continue;
+ }
+
+ dep_devices.handles[0] = link_handle;
+ count += acpi_scan_add_dep(handle, &dep_devices);
+ } else {
+ gsi_handle = riscv_acpi_get_gsi_handle(entry->source_index);
+ dep_devices.count = 1;
+ dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
+ if (!dep_devices.handles) {
+ acpi_handle_err(handle, "failed to allocate memory\n");
+ continue;
+ }
+
+ dep_devices.handles[0] = gsi_handle;
+ count += acpi_scan_add_dep(handle, &dep_devices);
+ }
+
+ entry = (struct acpi_pci_routing_table *)
+ ((unsigned long)entry + entry->length);
+ }
+
+ kfree(buffer.pointer);
+ return count;
+}
+
+static u32 riscv_acpi_add_irq_dep(acpi_handle handle)
+{
+ struct acpi_handle_list dep_devices;
+ acpi_handle gsi_handle;
+ u32 count = 0;
+ int i;
+
+ for (i = 0;
+ riscv_acpi_irq_get_dep(handle, i, &gsi_handle);
+ i++) {
+ dep_devices.count = 1;
+ dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
+ if (!dep_devices.handles) {
+ acpi_handle_err(handle, "failed to allocate memory\n");
+ continue;
+ }
+
+ dep_devices.handles[0] = gsi_handle;
+ count += acpi_scan_add_dep(handle, &dep_devices);
+ }
+
+ return count;
+}
+
+u32 arch_acpi_add_auto_dep(acpi_handle handle)
+{
+ if (acpi_has_method(handle, "_PRT"))
+ return riscv_acpi_add_prt_dep(handle);
+
+ return riscv_acpi_add_irq_dep(handle);
+}
diff --git a/drivers/acpi/riscv/rhct.c b/drivers/acpi/riscv/rhct.c
new file mode 100644
index 000000000000..caa2c16e1697
--- /dev/null
+++ b/drivers/acpi/riscv/rhct.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2023, Ventana Micro Systems Inc
+ * Author: Sunil V L <sunilvl@ventanamicro.com>
+ *
+ */
+
+#define pr_fmt(fmt) "ACPI: RHCT: " fmt
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+
+static struct acpi_table_rhct *acpi_get_rhct(void)
+{
+ static struct acpi_table_header *rhct;
+ acpi_status status;
+
+ /*
+ * RHCT will be used at runtime on every CPU, so we
+ * don't need to call acpi_put_table() to release the table mapping.
+ */
+ if (!rhct) {
+ status = acpi_get_table(ACPI_SIG_RHCT, 0, &rhct);
+ if (ACPI_FAILURE(status)) {
+ pr_warn_once("No RHCT table found\n");
+ return NULL;
+ }
+ }
+
+ return (struct acpi_table_rhct *)rhct;
+}
+
+/*
+ * During early boot, the caller should call acpi_get_table() and pass its pointer to
+ * these functions(and free up later). At run time, since this table can be used
+ * multiple times, NULL may be passed in order to use the cached table.
+ */
+int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const char **isa)
+{
+ struct acpi_rhct_node_header *node, *ref_node, *end;
+ u32 size_hdr = sizeof(struct acpi_rhct_node_header);
+ u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info);
+ struct acpi_rhct_hart_info *hart_info;
+ struct acpi_rhct_isa_string *isa_node;
+ struct acpi_table_rhct *rhct;
+ u32 *hart_info_node_offset;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+
+ BUG_ON(acpi_disabled);
+
+ if (!table) {
+ rhct = acpi_get_rhct();
+ if (!rhct)
+ return -ENOENT;
+ } else {
+ rhct = (struct acpi_table_rhct *)table;
+ }
+
+ end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length);
+
+ for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset);
+ node < end;
+ node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) {
+ if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) {
+ hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr);
+ hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo);
+ if (acpi_cpu_id != hart_info->uid)
+ continue;
+
+ for (int i = 0; i < hart_info->num_offsets; i++) {
+ ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header,
+ rhct, hart_info_node_offset[i]);
+ if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) {
+ isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string,
+ ref_node, size_hdr);
+ *isa = isa_node->isa;
+ return 0;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static void acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct,
+ struct acpi_rhct_hart_info *hart_info,
+ u32 *cbom_size, u32 *cboz_size, u32 *cbop_size)
+{
+ u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info);
+ u32 size_hdr = sizeof(struct acpi_rhct_node_header);
+ struct acpi_rhct_node_header *ref_node;
+ struct acpi_rhct_cmo_node *cmo_node;
+ u32 *hart_info_node_offset;
+
+ hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo);
+ for (int i = 0; i < hart_info->num_offsets; i++) {
+ ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header,
+ rhct, hart_info_node_offset[i]);
+ if (ref_node->type == ACPI_RHCT_NODE_TYPE_CMO) {
+ cmo_node = ACPI_ADD_PTR(struct acpi_rhct_cmo_node,
+ ref_node, size_hdr);
+ if (cbom_size && cmo_node->cbom_size <= 30) {
+ if (!*cbom_size)
+ *cbom_size = BIT(cmo_node->cbom_size);
+ else if (*cbom_size != BIT(cmo_node->cbom_size))
+ pr_warn("CBOM size is not the same across harts\n");
+ }
+
+ if (cboz_size && cmo_node->cboz_size <= 30) {
+ if (!*cboz_size)
+ *cboz_size = BIT(cmo_node->cboz_size);
+ else if (*cboz_size != BIT(cmo_node->cboz_size))
+ pr_warn("CBOZ size is not the same across harts\n");
+ }
+
+ if (cbop_size && cmo_node->cbop_size <= 30) {
+ if (!*cbop_size)
+ *cbop_size = BIT(cmo_node->cbop_size);
+ else if (*cbop_size != BIT(cmo_node->cbop_size))
+ pr_warn("CBOP size is not the same across harts\n");
+ }
+ }
+ }
+}
+
+/*
+ * During early boot, the caller should call acpi_get_table() and pass its pointer to
+ * these functions (and free up later). At run time, since this table can be used
+ * multiple times, pass NULL so that the table remains in memory.
+ */
+void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
+ u32 *cboz_size, u32 *cbop_size)
+{
+ u32 size_hdr = sizeof(struct acpi_rhct_node_header);
+ struct acpi_rhct_node_header *node, *end;
+ struct acpi_rhct_hart_info *hart_info;
+ struct acpi_table_rhct *rhct;
+
+ if (acpi_disabled)
+ return;
+
+ if (table) {
+ rhct = (struct acpi_table_rhct *)table;
+ } else {
+ rhct = acpi_get_rhct();
+ if (!rhct)
+ return;
+ }
+
+ if (cbom_size)
+ *cbom_size = 0;
+
+ if (cboz_size)
+ *cboz_size = 0;
+
+ if (cbop_size)
+ *cbop_size = 0;
+
+ end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length);
+ for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset);
+ node < end;
+ node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) {
+ if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) {
+ hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr);
+ acpi_parse_hart_info_cmo_node(rhct, hart_info, cbom_size,
+ cboz_size, cbop_size);
+ }
+ }
+}
diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c
new file mode 100644
index 000000000000..7f423405e5ef
--- /dev/null
+++ b/drivers/acpi/riscv/rimt.c
@@ -0,0 +1,520 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025, Ventana Micro Systems Inc
+ * Author: Sunil V L <sunilvl@ventanamicro.com>
+ *
+ */
+
+#define pr_fmt(fmt) "ACPI: RIMT: " fmt
+
+#include <linux/acpi.h>
+#include <linux/acpi_rimt.h>
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include "init.h"
+
+struct rimt_fwnode {
+ struct list_head list;
+ struct acpi_rimt_node *rimt_node;
+ struct fwnode_handle *fwnode;
+};
+
+static LIST_HEAD(rimt_fwnode_list);
+static DEFINE_SPINLOCK(rimt_fwnode_lock);
+
+#define RIMT_TYPE_MASK(type) (1 << (type))
+#define RIMT_IOMMU_TYPE BIT(0)
+
+/* Root pointer to the mapped RIMT table */
+static struct acpi_table_header *rimt_table;
+
+/**
+ * rimt_set_fwnode() - Create rimt_fwnode and use it to register
+ * iommu data in the rimt_fwnode_list
+ *
+ * @rimt_node: RIMT table node associated with the IOMMU
+ * @fwnode: fwnode associated with the RIMT node
+ *
+ * Returns: 0 on success
+ * <0 on failure
+ */
+static int rimt_set_fwnode(struct acpi_rimt_node *rimt_node,
+ struct fwnode_handle *fwnode)
+{
+ struct rimt_fwnode *np;
+
+ np = kzalloc(sizeof(*np), GFP_ATOMIC);
+
+ if (WARN_ON(!np))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&np->list);
+ np->rimt_node = rimt_node;
+ np->fwnode = fwnode;
+
+ spin_lock(&rimt_fwnode_lock);
+ list_add_tail(&np->list, &rimt_fwnode_list);
+ spin_unlock(&rimt_fwnode_lock);
+
+ return 0;
+}
+
+static acpi_status rimt_match_node_callback(struct acpi_rimt_node *node,
+ void *context)
+{
+ acpi_status status = AE_NOT_FOUND;
+ struct device *dev = context;
+
+ if (node->type == ACPI_RIMT_NODE_TYPE_IOMMU) {
+ struct acpi_rimt_iommu *iommu_node = (struct acpi_rimt_iommu *)&node->node_data;
+
+ if (dev_is_pci(dev)) {
+ struct pci_dev *pdev;
+ u16 bdf;
+
+ pdev = to_pci_dev(dev);
+ bdf = PCI_DEVID(pdev->bus->number, pdev->devfn);
+ if ((pci_domain_nr(pdev->bus) == iommu_node->pcie_segment_number) &&
+ bdf == iommu_node->pcie_bdf) {
+ status = AE_OK;
+ } else {
+ status = AE_NOT_FOUND;
+ }
+ } else {
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res && res->start == iommu_node->base_address)
+ status = AE_OK;
+ else
+ status = AE_NOT_FOUND;
+ }
+ } else if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) {
+ struct acpi_rimt_pcie_rc *pci_rc;
+ struct pci_bus *bus;
+
+ bus = to_pci_bus(dev);
+ pci_rc = (struct acpi_rimt_pcie_rc *)node->node_data;
+
+ /*
+ * It is assumed that PCI segment numbers maps one-to-one
+ * with root complexes. Each segment number can represent only
+ * one root complex.
+ */
+ status = pci_rc->pcie_segment_number == pci_domain_nr(bus) ?
+ AE_OK : AE_NOT_FOUND;
+ } else if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE) {
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_rimt_platform_device *ncomp;
+ struct device *plat_dev = dev;
+ struct acpi_device *adev;
+
+ /*
+ * Walk the device tree to find a device with an
+ * ACPI companion; there is no point in scanning
+ * RIMT for a device matching a platform device if
+ * the device does not have an ACPI companion to
+ * start with.
+ */
+ do {
+ adev = ACPI_COMPANION(plat_dev);
+ if (adev)
+ break;
+
+ plat_dev = plat_dev->parent;
+ } while (plat_dev);
+
+ if (!adev)
+ return status;
+
+ status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(plat_dev, "Can't get device full path name\n");
+ return status;
+ }
+
+ ncomp = (struct acpi_rimt_platform_device *)node->node_data;
+ status = !strcmp(ncomp->device_name, buf.pointer) ?
+ AE_OK : AE_NOT_FOUND;
+ acpi_os_free(buf.pointer);
+ }
+
+ return status;
+}
+
+static struct acpi_rimt_node *rimt_scan_node(enum acpi_rimt_node_type type,
+ void *context)
+{
+ struct acpi_rimt_node *rimt_node, *rimt_end;
+ struct acpi_table_rimt *rimt;
+ int i;
+
+ if (!rimt_table)
+ return NULL;
+
+ /* Get the first RIMT node */
+ rimt = (struct acpi_table_rimt *)rimt_table;
+ rimt_node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt,
+ rimt->node_offset);
+ rimt_end = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table,
+ rimt_table->length);
+
+ for (i = 0; i < rimt->num_nodes; i++) {
+ if (WARN_TAINT(rimt_node >= rimt_end, TAINT_FIRMWARE_WORKAROUND,
+ "RIMT node pointer overflows, bad table!\n"))
+ return NULL;
+
+ if (rimt_node->type == type &&
+ ACPI_SUCCESS(rimt_match_node_callback(rimt_node, context)))
+ return rimt_node;
+
+ rimt_node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_node,
+ rimt_node->length);
+ }
+
+ return NULL;
+}
+
+/*
+ * RISC-V supports IOMMU as a PCI device or a platform device.
+ * When it is a platform device, there should be a namespace device as
+ * well along with RIMT. To create the link between RIMT information and
+ * the platform device, the IOMMU driver should register itself with the
+ * RIMT module. This is true for PCI based IOMMU as well.
+ */
+int rimt_iommu_register(struct device *dev)
+{
+ struct fwnode_handle *rimt_fwnode;
+ struct acpi_rimt_node *node;
+
+ node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_IOMMU, dev);
+ if (!node) {
+ pr_err("Could not find IOMMU node in RIMT\n");
+ return -ENODEV;
+ }
+
+ if (dev_is_pci(dev)) {
+ rimt_fwnode = acpi_alloc_fwnode_static();
+ if (!rimt_fwnode)
+ return -ENOMEM;
+
+ rimt_fwnode->dev = dev;
+ if (!dev->fwnode)
+ dev->fwnode = rimt_fwnode;
+
+ rimt_set_fwnode(node, rimt_fwnode);
+ } else {
+ rimt_set_fwnode(node, dev->fwnode);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_IOMMU_API
+
+/**
+ * rimt_get_fwnode() - Retrieve fwnode associated with an RIMT node
+ *
+ * @node: RIMT table node to be looked-up
+ *
+ * Returns: fwnode_handle pointer on success, NULL on failure
+ */
+static struct fwnode_handle *rimt_get_fwnode(struct acpi_rimt_node *node)
+{
+ struct fwnode_handle *fwnode = NULL;
+ struct rimt_fwnode *curr;
+
+ spin_lock(&rimt_fwnode_lock);
+ list_for_each_entry(curr, &rimt_fwnode_list, list) {
+ if (curr->rimt_node == node) {
+ fwnode = curr->fwnode;
+ break;
+ }
+ }
+ spin_unlock(&rimt_fwnode_lock);
+
+ return fwnode;
+}
+
+static bool rimt_pcie_rc_supports_ats(struct acpi_rimt_node *node)
+{
+ struct acpi_rimt_pcie_rc *pci_rc;
+
+ pci_rc = (struct acpi_rimt_pcie_rc *)node->node_data;
+ return pci_rc->flags & ACPI_RIMT_PCIE_ATS_SUPPORTED;
+}
+
+static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 deviceid)
+{
+ struct fwnode_handle *rimt_fwnode;
+
+ if (!node)
+ return -ENODEV;
+
+ rimt_fwnode = rimt_get_fwnode(node);
+
+ /*
+ * The IOMMU drivers may not be probed yet.
+ * Defer the IOMMU configuration
+ */
+ if (!rimt_fwnode)
+ return -EPROBE_DEFER;
+
+ return acpi_iommu_fwspec_init(dev, deviceid, rimt_fwnode);
+}
+
+struct rimt_pci_alias_info {
+ struct device *dev;
+ struct acpi_rimt_node *node;
+ const struct iommu_ops *ops;
+};
+
+static int rimt_id_map(struct acpi_rimt_id_mapping *map, u8 type, u32 rid_in, u32 *rid_out)
+{
+ if (rid_in < map->source_id_base ||
+ (rid_in > map->source_id_base + map->num_ids))
+ return -ENXIO;
+
+ *rid_out = map->dest_id_base + (rid_in - map->source_id_base);
+ return 0;
+}
+
+static struct acpi_rimt_node *rimt_node_get_id(struct acpi_rimt_node *node,
+ u32 *id_out, int index)
+{
+ struct acpi_rimt_platform_device *plat_node;
+ u32 id_mapping_offset, num_id_mapping;
+ struct acpi_rimt_pcie_rc *pci_node;
+ struct acpi_rimt_id_mapping *map;
+ struct acpi_rimt_node *parent;
+
+ if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) {
+ pci_node = (struct acpi_rimt_pcie_rc *)&node->node_data;
+ id_mapping_offset = pci_node->id_mapping_offset;
+ num_id_mapping = pci_node->num_id_mappings;
+ } else if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE) {
+ plat_node = (struct acpi_rimt_platform_device *)&node->node_data;
+ id_mapping_offset = plat_node->id_mapping_offset;
+ num_id_mapping = plat_node->num_id_mappings;
+ } else {
+ return NULL;
+ }
+
+ if (!id_mapping_offset || !num_id_mapping || index >= num_id_mapping)
+ return NULL;
+
+ map = ACPI_ADD_PTR(struct acpi_rimt_id_mapping, node,
+ id_mapping_offset + index * sizeof(*map));
+
+ /* Firmware bug! */
+ if (!map->dest_offset) {
+ pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+ node, node->type);
+ return NULL;
+ }
+
+ parent = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table, map->dest_offset);
+
+ if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE ||
+ node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) {
+ *id_out = map->dest_id_base;
+ return parent;
+ }
+
+ return NULL;
+}
+
+static struct acpi_rimt_node *rimt_node_map_id(struct acpi_rimt_node *node,
+ u32 id_in, u32 *id_out,
+ u8 type_mask)
+{
+ struct acpi_rimt_platform_device *plat_node;
+ u32 id_mapping_offset, num_id_mapping;
+ struct acpi_rimt_pcie_rc *pci_node;
+ u32 id = id_in;
+
+ /* Parse the ID mapping tree to find specified node type */
+ while (node) {
+ struct acpi_rimt_id_mapping *map;
+ int i, rc = 0;
+ u32 map_id = id;
+
+ if (RIMT_TYPE_MASK(node->type) & type_mask) {
+ if (id_out)
+ *id_out = id;
+ return node;
+ }
+
+ if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) {
+ pci_node = (struct acpi_rimt_pcie_rc *)&node->node_data;
+ id_mapping_offset = pci_node->id_mapping_offset;
+ num_id_mapping = pci_node->num_id_mappings;
+ } else if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE) {
+ plat_node = (struct acpi_rimt_platform_device *)&node->node_data;
+ id_mapping_offset = plat_node->id_mapping_offset;
+ num_id_mapping = plat_node->num_id_mappings;
+ } else {
+ goto fail_map;
+ }
+
+ if (!id_mapping_offset || !num_id_mapping)
+ goto fail_map;
+
+ map = ACPI_ADD_PTR(struct acpi_rimt_id_mapping, node,
+ id_mapping_offset);
+
+ /* Firmware bug! */
+ if (!map->dest_offset) {
+ pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+ node, node->type);
+ goto fail_map;
+ }
+
+ /* Do the ID translation */
+ for (i = 0; i < num_id_mapping; i++, map++) {
+ rc = rimt_id_map(map, node->type, map_id, &id);
+ if (!rc)
+ break;
+ }
+
+ if (i == num_id_mapping)
+ goto fail_map;
+
+ node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table,
+ rc ? 0 : map->dest_offset);
+ }
+
+fail_map:
+ /* Map input ID to output ID unchanged on mapping failure */
+ if (id_out)
+ *id_out = id_in;
+
+ return NULL;
+}
+
+static struct acpi_rimt_node *rimt_node_map_platform_id(struct acpi_rimt_node *node, u32 *id_out,
+ u8 type_mask, int index)
+{
+ struct acpi_rimt_node *parent;
+ u32 id;
+
+ parent = rimt_node_get_id(node, &id, index);
+ if (!parent)
+ return NULL;
+
+ if (!(RIMT_TYPE_MASK(parent->type) & type_mask))
+ parent = rimt_node_map_id(parent, id, id_out, type_mask);
+ else
+ if (id_out)
+ *id_out = id;
+
+ return parent;
+}
+
+static int rimt_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
+{
+ struct rimt_pci_alias_info *info = data;
+ struct acpi_rimt_node *parent;
+ u32 deviceid;
+
+ parent = rimt_node_map_id(info->node, alias, &deviceid, RIMT_IOMMU_TYPE);
+ return rimt_iommu_xlate(info->dev, parent, deviceid);
+}
+
+static int rimt_plat_iommu_map(struct device *dev, struct acpi_rimt_node *node)
+{
+ struct acpi_rimt_node *parent;
+ int err = -ENODEV, i = 0;
+ u32 deviceid = 0;
+
+ do {
+ parent = rimt_node_map_platform_id(node, &deviceid,
+ RIMT_IOMMU_TYPE,
+ i++);
+
+ if (parent)
+ err = rimt_iommu_xlate(dev, parent, deviceid);
+ } while (parent && !err);
+
+ return err;
+}
+
+static int rimt_plat_iommu_map_id(struct device *dev,
+ struct acpi_rimt_node *node,
+ const u32 *in_id)
+{
+ struct acpi_rimt_node *parent;
+ u32 deviceid;
+
+ parent = rimt_node_map_id(node, *in_id, &deviceid, RIMT_IOMMU_TYPE);
+ if (parent)
+ return rimt_iommu_xlate(dev, parent, deviceid);
+
+ return -ENODEV;
+}
+
+/**
+ * rimt_iommu_configure_id - Set-up IOMMU configuration for a device.
+ *
+ * @dev: device to configure
+ * @id_in: optional input id const value pointer
+ *
+ * Returns: 0 on success, <0 on failure
+ */
+int rimt_iommu_configure_id(struct device *dev, const u32 *id_in)
+{
+ struct acpi_rimt_node *node;
+ int err = -ENODEV;
+
+ if (dev_is_pci(dev)) {
+ struct iommu_fwspec *fwspec;
+ struct pci_bus *bus = to_pci_dev(dev)->bus;
+ struct rimt_pci_alias_info info = { .dev = dev };
+
+ node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX, &bus->dev);
+ if (!node)
+ return -ENODEV;
+
+ info.node = node;
+ err = pci_for_each_dma_alias(to_pci_dev(dev),
+ rimt_pci_iommu_init, &info);
+
+ fwspec = dev_iommu_fwspec_get(dev);
+ if (fwspec && rimt_pcie_rc_supports_ats(node))
+ fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
+ } else {
+ node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_PLAT_DEVICE, dev);
+ if (!node)
+ return -ENODEV;
+
+ err = id_in ? rimt_plat_iommu_map_id(dev, node, id_in) :
+ rimt_plat_iommu_map(dev, node);
+ }
+
+ return err;
+}
+
+#endif
+
+void __init riscv_acpi_rimt_init(void)
+{
+ acpi_status status;
+
+ /* rimt_table will be used at runtime after the rimt init,
+ * so we don't need to call acpi_put_table() to release
+ * the RIMT table mapping.
+ */
+ status = acpi_get_table(ACPI_SIG_RIMT, 0, &rimt_table);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ const char *msg = acpi_format_exception(status);
+
+ pr_err("Failed to get table, %s\n", msg);
+ }
+
+ return;
+ }
+}
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 4938010fcac7..d3edc3bcbf01 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -77,7 +77,6 @@ struct acpi_battery {
u16 spec;
u8 id;
u8 present:1;
- u8 have_sysfs_alarm:1;
};
#define to_acpi_battery(x) power_supply_get_drvdata(x)
@@ -96,7 +95,7 @@ struct acpi_sbs {
#define to_acpi_sbs(x) power_supply_get_drvdata(x)
-static int acpi_sbs_remove(struct acpi_device *device);
+static void acpi_sbs_remove(struct acpi_device *device);
static int acpi_battery_get_state(struct acpi_battery *battery);
static inline int battery_scale(int log)
@@ -241,11 +240,11 @@ static int acpi_sbs_battery_get_property(struct power_supply *psy,
return 0;
}
-static enum power_supply_property sbs_ac_props[] = {
+static const enum power_supply_property sbs_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
-static enum power_supply_property sbs_charge_battery_props[] = {
+static const enum power_supply_property sbs_charge_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -263,7 +262,7 @@ static enum power_supply_property sbs_charge_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
-static enum power_supply_property sbs_energy_battery_props[] = {
+static const enum power_supply_property sbs_energy_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -462,34 +461,49 @@ static ssize_t acpi_battery_alarm_store(struct device *dev,
return count;
}
-static const struct device_attribute alarm_attr = {
+static struct device_attribute alarm_attr = {
.attr = {.name = "alarm", .mode = 0644},
.show = acpi_battery_alarm_show,
.store = acpi_battery_alarm_store,
};
+static struct attribute *acpi_battery_attrs[] = {
+ &alarm_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(acpi_battery);
+
/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
static int acpi_battery_read(struct acpi_battery *battery)
{
- int result = 0, saved_present = battery->present;
+ int result, saved_present = battery->present;
u16 state;
if (battery->sbs->manager_present) {
result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
ACPI_SBS_MANAGER, 0x01, (u8 *)&state);
- if (!result)
- battery->present = state & (1 << battery->id);
- state &= 0x0fff;
+ if (result)
+ return result;
+
+ battery->present = !!(state & (1 << battery->id));
+ if (!battery->present)
+ return 0;
+
+ /* Masking necessary for Smart Battery Selectors */
+ state = 0x0fff;
state |= 1 << (battery->id + 12);
acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD,
ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2);
- } else if (battery->id == 0)
- battery->present = 1;
-
- if (result || !battery->present)
- return result;
+ } else {
+ if (battery->id == 0) {
+ battery->present = 1;
+ } else {
+ if (!battery->present)
+ return 0;
+ }
+ }
if (saved_present != battery->present) {
battery->update_time = 0;
@@ -509,7 +523,10 @@ static int acpi_battery_read(struct acpi_battery *battery)
static int acpi_battery_add(struct acpi_sbs *sbs, int id)
{
struct acpi_battery *battery = &sbs->battery[id];
- struct power_supply_config psy_cfg = { .drv_data = battery, };
+ struct power_supply_config psy_cfg = {
+ .drv_data = battery,
+ .attr_grp = acpi_battery_groups,
+ };
int result;
battery->id = id;
@@ -539,10 +556,6 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
goto end;
}
- result = device_create_file(&battery->bat->dev, &alarm_attr);
- if (result)
- goto end;
- battery->have_sysfs_alarm = 1;
end:
pr_info("%s [%s]: Battery Slot [%s] (battery %s)\n",
ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
@@ -554,11 +567,8 @@ static void acpi_battery_remove(struct acpi_sbs *sbs, int id)
{
struct acpi_battery *battery = &sbs->battery[id];
- if (battery->bat) {
- if (battery->have_sysfs_alarm)
- device_remove_file(&battery->bat->dev, &alarm_attr);
+ if (battery->bat)
power_supply_unregister(battery->bat);
- }
}
static int acpi_charger_add(struct acpi_sbs *sbs)
@@ -601,7 +611,7 @@ static void acpi_sbs_callback(void *context)
if (sbs->charger_exists) {
acpi_ac_get_present(sbs);
if (sbs->charger_present != saved_charger_state)
- kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE);
+ power_supply_changed(sbs->charger);
}
if (sbs->manager_present) {
@@ -613,7 +623,7 @@ static void acpi_sbs_callback(void *context)
acpi_battery_read(bat);
if (saved_battery_state == bat->present)
continue;
- kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE);
+ power_supply_changed(bat->bat);
}
}
}
@@ -632,10 +642,10 @@ static int acpi_sbs_add(struct acpi_device *device)
mutex_init(&sbs->lock);
- sbs->hc = acpi_driver_data(device->parent);
+ sbs->hc = acpi_driver_data(acpi_dev_parent(device));
sbs->device = device;
- strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
+ strscpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_SBS_CLASS);
device->driver_data = sbs;
result = acpi_charger_add(sbs);
@@ -664,16 +674,16 @@ end:
return result;
}
-static int acpi_sbs_remove(struct acpi_device *device)
+static void acpi_sbs_remove(struct acpi_device *device)
{
struct acpi_sbs *sbs;
int id;
if (!device)
- return -EINVAL;
+ return;
sbs = acpi_driver_data(device);
if (!sbs)
- return -EINVAL;
+ return;
mutex_lock(&sbs->lock);
acpi_smbus_unregister_callback(sbs->hc);
for (id = 0; id < MAX_SBS_BAT; ++id)
@@ -682,7 +692,6 @@ static int acpi_sbs_remove(struct acpi_device *device)
mutex_unlock(&sbs->lock);
mutex_destroy(&sbs->lock);
kfree(sbs);
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c
index 7c62e149a7a1..1a2bf520be23 100644
--- a/drivers/acpi/sbshc.c
+++ b/drivers/acpi/sbshc.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include "sbshc.h"
+#include "internal.h"
#define ACPI_SMB_HC_CLASS "smbus_host_ctl"
#define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC"
@@ -30,7 +31,7 @@ struct acpi_smb_hc {
};
static int acpi_smbus_hc_add(struct acpi_device *device);
-static int acpi_smbus_hc_remove(struct acpi_device *device);
+static void acpi_smbus_hc_remove(struct acpi_device *device);
static const struct acpi_device_id sbs_device_ids[] = {
{"ACPI0001", 0},
@@ -236,12 +237,6 @@ static int smbus_alarm(void *context)
return 0;
}
-typedef int (*acpi_ec_query_func) (void *data);
-
-extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
- acpi_handle handle, acpi_ec_query_func func,
- void *data);
-
static int acpi_smbus_hc_add(struct acpi_device *device)
{
int status;
@@ -257,8 +252,8 @@ static int acpi_smbus_hc_add(struct acpi_device *device)
return -EIO;
}
- strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
+ strscpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
if (!hc)
@@ -266,7 +261,7 @@ static int acpi_smbus_hc_add(struct acpi_device *device)
mutex_init(&hc->lock);
init_waitqueue_head(&hc->wait);
- hc->ec = acpi_driver_data(device->parent);
+ hc->ec = acpi_driver_data(acpi_dev_parent(device));
hc->offset = (val >> 8) & 0xff;
hc->query_bit = val & 0xff;
device->driver_data = hc;
@@ -278,21 +273,18 @@ static int acpi_smbus_hc_add(struct acpi_device *device)
return 0;
}
-extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
-
-static int acpi_smbus_hc_remove(struct acpi_device *device)
+static void acpi_smbus_hc_remove(struct acpi_device *device)
{
struct acpi_smb_hc *hc;
if (!device)
- return -EINVAL;
+ return;
hc = acpi_driver_data(device);
acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
acpi_os_wait_events_complete();
kfree(hc);
device->driver_data = NULL;
- return 0;
}
module_acpi_driver(acpi_smb_hc_driver);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 42cec8120f18..416d87f9bd10 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/acpi_iort.h>
+#include <linux/acpi_rimt.h>
#include <linux/acpi_viot.h>
#include <linux/iommu.h>
#include <linux/signal.h>
@@ -20,18 +21,16 @@
#include <linux/platform_data/x86/apple.h>
#include <linux/pgtable.h>
#include <linux/crc32.h>
+#include <linux/dma-direct.h>
#include "internal.h"
-
-extern struct acpi_device *acpi_root;
+#include "sleep.h"
#define ACPI_BUS_CLASS "system_bus"
#define ACPI_BUS_HID "LNXSYBUS"
#define ACPI_BUS_DEVICE_NAME "System Bus"
-#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
-
-#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
+#define INVALID_ACPI_HANDLE ((acpi_handle)ZERO_PAGE(0))
static const char *dummy_hid = "device";
@@ -75,8 +74,7 @@ void acpi_unlock_hp_context(void)
void acpi_initialize_hp_context(struct acpi_device *adev,
struct acpi_hotplug_context *hp,
- int (*notify)(struct acpi_device *, u32),
- void (*uevent)(struct acpi_device *, u32))
+ acpi_hp_notify notify, acpi_hp_uevent uevent)
{
acpi_lock_hp_context();
hp->notify = notify;
@@ -246,11 +244,85 @@ static int acpi_scan_try_to_offline(struct acpi_device *device)
return 0;
}
+#define ACPI_SCAN_CHECK_FLAG_STATUS BIT(0)
+#define ACPI_SCAN_CHECK_FLAG_EJECT BIT(1)
+
+static int acpi_scan_check_and_detach(struct acpi_device *adev, void *p)
+{
+ struct acpi_scan_handler *handler = adev->handler;
+ uintptr_t flags = (uintptr_t)p;
+
+ acpi_dev_for_each_child_reverse(adev, acpi_scan_check_and_detach, p);
+
+ if (flags & ACPI_SCAN_CHECK_FLAG_STATUS) {
+ acpi_bus_get_status(adev);
+ /*
+ * Skip devices that are still there and take the enabled
+ * flag into account.
+ */
+ if (acpi_device_is_enabled(adev))
+ return 0;
+
+ /* Skip device that have not been enumerated. */
+ if (!acpi_device_enumerated(adev)) {
+ dev_dbg(&adev->dev, "Still not enumerated\n");
+ return 0;
+ }
+ }
+
+ adev->flags.match_driver = false;
+ if (handler) {
+ if (handler->detach)
+ handler->detach(adev);
+ } else {
+ device_release_driver(&adev->dev);
+ }
+ /*
+ * Most likely, the device is going away, so put it into D3cold before
+ * that.
+ */
+ acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
+ adev->flags.initialized = false;
+
+ /* For eject this is deferred to acpi_bus_post_eject() */
+ if (!(flags & ACPI_SCAN_CHECK_FLAG_EJECT)) {
+ adev->handler = NULL;
+ acpi_device_clear_enumerated(adev);
+ }
+ return 0;
+}
+
+static int acpi_bus_post_eject(struct acpi_device *adev, void *not_used)
+{
+ struct acpi_scan_handler *handler = adev->handler;
+
+ acpi_dev_for_each_child_reverse(adev, acpi_bus_post_eject, NULL);
+
+ if (handler) {
+ if (handler->post_eject)
+ handler->post_eject(adev);
+
+ adev->handler = NULL;
+ }
+
+ acpi_device_clear_enumerated(adev);
+
+ return 0;
+}
+
+static void acpi_scan_check_subtree(struct acpi_device *adev)
+{
+ uintptr_t flags = ACPI_SCAN_CHECK_FLAG_STATUS;
+
+ acpi_scan_check_and_detach(adev, (void *)flags);
+}
+
static int acpi_scan_hot_remove(struct acpi_device *device)
{
acpi_handle handle = device->handle;
unsigned long long sta;
acpi_status status;
+ uintptr_t flags = ACPI_SCAN_CHECK_FLAG_EJECT;
if (device->handler && device->handler->hotplug.demand_offline) {
if (!acpi_scan_is_offline(device, true))
@@ -263,7 +335,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
acpi_handle_debug(handle, "Ejecting\n");
- acpi_bus_trim(device);
+ acpi_scan_check_and_detach(device, (void *)flags);
acpi_evaluate_lck(handle, 0);
/*
@@ -286,80 +358,69 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
} else if (sta & ACPI_STA_DEVICE_ENABLED) {
acpi_handle_warn(handle,
"Eject incomplete - status 0x%llx\n", sta);
+ } else {
+ acpi_bus_post_eject(device, NULL);
}
return 0;
}
-static int acpi_scan_device_not_present(struct acpi_device *adev)
+static int acpi_scan_rescan_bus(struct acpi_device *adev)
{
- if (!acpi_device_enumerated(adev)) {
- dev_warn(&adev->dev, "Still not present\n");
- return -EALREADY;
- }
- acpi_bus_trim(adev);
- return 0;
+ struct acpi_scan_handler *handler = adev->handler;
+ int ret;
+
+ if (handler && handler->hotplug.scan_dependent)
+ ret = handler->hotplug.scan_dependent(adev);
+ else
+ ret = acpi_bus_scan(adev->handle);
+
+ if (ret)
+ dev_info(&adev->dev, "Namespace scan failure\n");
+
+ return ret;
}
static int acpi_scan_device_check(struct acpi_device *adev)
{
- int error;
+ struct acpi_device *parent;
- acpi_bus_get_status(adev);
- if (adev->status.present || adev->status.functional) {
- /*
- * This function is only called for device objects for which
- * matching scan handlers exist. The only situation in which
- * the scan handler is not attached to this device object yet
- * is when the device has just appeared (either it wasn't
- * present at all before or it was removed and then added
- * again).
- */
- if (adev->handler) {
- dev_warn(&adev->dev, "Already enumerated\n");
- return -EALREADY;
- }
- error = acpi_bus_scan(adev->handle);
- if (error) {
- dev_warn(&adev->dev, "Namespace scan failure\n");
- return error;
- }
- if (!adev->handler) {
- dev_warn(&adev->dev, "Enumeration failure\n");
- error = -ENODEV;
- }
- } else {
- error = acpi_scan_device_not_present(adev);
- }
- return error;
-}
+ acpi_scan_check_subtree(adev);
-static int acpi_scan_bus_check(struct acpi_device *adev, void *not_used)
-{
- struct acpi_scan_handler *handler = adev->handler;
- int error;
+ if (!acpi_device_is_present(adev))
+ return 0;
- acpi_bus_get_status(adev);
- if (!(adev->status.present || adev->status.functional)) {
- acpi_scan_device_not_present(adev);
+ /*
+ * This function is only called for device objects for which matching
+ * scan handlers exist. The only situation in which the scan handler
+ * is not attached to this device object yet is when the device has
+ * just appeared (either it wasn't present at all before or it was
+ * removed and then added again).
+ */
+ if (adev->handler) {
+ dev_dbg(&adev->dev, "Already enumerated\n");
return 0;
}
- if (handler && handler->hotplug.scan_dependent)
- return handler->hotplug.scan_dependent(adev);
- error = acpi_bus_scan(adev->handle);
- if (error) {
- dev_warn(&adev->dev, "Namespace scan failure\n");
- return error;
- }
- return acpi_dev_for_each_child(adev, acpi_scan_bus_check, NULL);
+ parent = acpi_dev_parent(adev);
+ if (!parent)
+ parent = adev;
+
+ return acpi_scan_rescan_bus(parent);
+}
+
+static int acpi_scan_bus_check(struct acpi_device *adev)
+{
+ acpi_scan_check_subtree(adev);
+
+ return acpi_scan_rescan_bus(adev);
}
static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
{
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
- return acpi_scan_bus_check(adev, NULL);
+ return acpi_scan_bus_check(adev);
case ACPI_NOTIFY_DEVICE_CHECK:
return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST:
@@ -396,7 +457,7 @@ void acpi_device_hotplug(struct acpi_device *adev, u32 src)
} else if (adev->flags.hotplug_notify) {
error = acpi_generic_hotplug_event(adev, src);
} else {
- int (*notify)(struct acpi_device *, u32);
+ acpi_hp_notify notify;
acpi_lock_hp_context();
notify = adev->hp ? adev->hp->notify : NULL;
@@ -429,7 +490,7 @@ void acpi_device_hotplug(struct acpi_device *adev, u32 src)
acpi_evaluate_ost(adev->handle, src, ost_code, NULL);
out:
- acpi_bus_put_acpi_device(adev);
+ acpi_put_acpi_dev(adev);
mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug();
}
@@ -599,11 +660,22 @@ static void get_acpi_device(void *dev)
acpi_dev_get(dev);
}
-struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle)
+/**
+ * acpi_get_acpi_dev - Retrieve ACPI device object and reference count it.
+ * @handle: ACPI handle associated with the requested ACPI device object.
+ *
+ * Return a pointer to the ACPI device object associated with @handle and bump
+ * up that object's reference counter (under the ACPI Namespace lock), if
+ * present, or return NULL otherwise.
+ *
+ * The ACPI device object reference acquired by this function needs to be
+ * dropped via acpi_dev_put().
+ */
+struct acpi_device *acpi_get_acpi_dev(acpi_handle handle)
{
return handle_to_device(handle, get_acpi_device);
}
-EXPORT_SYMBOL_GPL(acpi_bus_get_acpi_device);
+EXPORT_SYMBOL_GPL(acpi_get_acpi_dev);
static struct acpi_device_bus_id *acpi_device_bus_id_match(const char *dev_id)
{
@@ -632,7 +704,7 @@ static int acpi_device_set_name(struct acpi_device *device,
return 0;
}
-static int acpi_tie_acpi_dev(struct acpi_device *adev)
+int acpi_tie_acpi_dev(struct acpi_device *adev)
{
acpi_handle handle = adev->handle;
acpi_status status;
@@ -652,18 +724,15 @@ static int acpi_tie_acpi_dev(struct acpi_device *adev)
static void acpi_store_pld_crc(struct acpi_device *adev)
{
struct acpi_pld_info *pld;
- acpi_status status;
- status = acpi_get_physical_device_location(adev->handle, &pld);
- if (ACPI_FAILURE(status))
+ if (!acpi_get_physical_device_location(adev->handle, &pld))
return;
adev->pld_crc = crc32(~0, pld, sizeof(*pld));
ACPI_FREE(pld);
}
-static int __acpi_device_add(struct acpi_device *device,
- void (*release)(struct device *))
+int acpi_device_add(struct acpi_device *device)
{
struct acpi_device_bus_id *acpi_device_bus_id;
int result;
@@ -719,21 +788,13 @@ static int __acpi_device_add(struct acpi_device *device,
mutex_unlock(&acpi_device_lock);
- if (device->parent)
- device->dev.parent = &device->parent->dev;
-
- device->dev.bus = &acpi_bus_type;
- device->dev.release = release;
result = device_add(&device->dev);
if (result) {
dev_err(&device->dev, "Error registering device\n");
goto err;
}
- result = acpi_device_setup_files(device);
- if (result)
- pr_err("Error creating sysfs interface for device %s\n",
- dev_name(&device->dev));
+ acpi_device_setup_files(device);
return 0;
@@ -750,17 +811,6 @@ err_unlock:
return result;
}
-int acpi_device_add(struct acpi_device *adev, void (*release)(struct device *))
-{
- int ret;
-
- ret = acpi_tie_acpi_dev(adev);
- if (ret)
- return ret;
-
- return __acpi_device_add(adev, release);
-}
-
/* --------------------------------------------------------------------------
Device Enumeration
-------------------------------------------------------------------------- */
@@ -796,19 +846,30 @@ static bool acpi_info_matches_ids(struct acpi_device_info *info,
static const char * const acpi_ignore_dep_ids[] = {
"PNP0D80", /* Windows-compatible System Power Management Controller */
"INT33BD", /* Intel Baytrail Mailbox Device */
+ "INTC10DE", /* Intel CVS LNL */
+ "INTC10E0", /* Intel CVS ARL */
+ "LATT2021", /* Lattice FW Update Client Driver */
NULL
};
/* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */
static const char * const acpi_honor_dep_ids[] = {
"INT3472", /* Camera sensor PMIC / clk and regulator info */
+ "INTC1059", /* IVSC (TGL) driver must be loaded to allow i2c access to camera sensors */
+ "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */
+ "INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */
+ "INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */
+ "RSCV0001", /* RISC-V PLIC */
+ "RSCV0002", /* RISC-V APLIC */
+ "RSCV0005", /* RISC-V SBI MPXY MBOX */
+ "RSCV0006", /* RISC-V RPMI SYSMSI */
+ "PNP0C0F", /* PCI Link Device */
NULL
};
-static struct acpi_device *acpi_bus_get_parent(acpi_handle handle)
+static struct acpi_device *acpi_find_parent_acpi_dev(acpi_handle handle)
{
- struct acpi_device *device;
- acpi_status status;
+ struct acpi_device *adev;
/*
* Fixed hardware devices do not appear in the namespace and do not
@@ -819,13 +880,18 @@ static struct acpi_device *acpi_bus_get_parent(acpi_handle handle)
return acpi_root;
do {
+ acpi_status status;
+
status = acpi_get_parent(handle, &handle);
- if (ACPI_FAILURE(status))
- return status == AE_NULL_ENTRY ? NULL : acpi_root;
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NULL_ENTRY)
+ return acpi_root;
- device = acpi_fetch_acpi_dev(handle);
- } while (!device);
- return device;
+ return NULL;
+ }
+ adev = acpi_fetch_acpi_dev(handle);
+ } while (!adev);
+ return adev;
}
acpi_status
@@ -932,26 +998,29 @@ static int acpi_bus_extract_wakeup_device_power_package(struct acpi_device *dev)
return err;
}
+/* Do not use a button for S5 wakeup */
+#define ACPI_AVOID_WAKE_FROM_S5 BIT(0)
+
static bool acpi_wakeup_gpe_init(struct acpi_device *device)
{
static const struct acpi_device_id button_device_ids[] = {
- {"PNP0C0C", 0}, /* Power button */
- {"PNP0C0D", 0}, /* Lid */
- {"PNP0C0E", 0}, /* Sleep button */
+ {"PNP0C0C", 0}, /* Power button */
+ {"PNP0C0D", ACPI_AVOID_WAKE_FROM_S5}, /* Lid */
+ {"PNP0C0E", ACPI_AVOID_WAKE_FROM_S5}, /* Sleep button */
{"", 0},
};
struct acpi_device_wakeup *wakeup = &device->wakeup;
+ const struct acpi_device_id *match;
acpi_status status;
wakeup->flags.notifier_present = 0;
/* Power button, Lid switch always enable wakeup */
- if (!acpi_match_device_ids(device, button_device_ids)) {
- if (!acpi_match_device_ids(device, &button_device_ids[1])) {
- /* Do not use Lid/sleep button for S5 wakeup */
- if (wakeup->sleep_state == ACPI_STATE_S5)
- wakeup->sleep_state = ACPI_STATE_S4;
- }
+ match = acpi_match_acpi_device(button_device_ids, device);
+ if (match) {
+ if ((match->driver_data & ACPI_AVOID_WAKE_FROM_S5) &&
+ wakeup->sleep_state == ACPI_STATE_S5)
+ wakeup->sleep_state = ACPI_STATE_S4;
acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number);
device_set_wakeup_capable(&device->dev, true);
return true;
@@ -1112,20 +1181,20 @@ static void acpi_device_get_busid(struct acpi_device *device)
* The device's Bus ID is simply the object name.
* TBD: Shouldn't this value be unique (within the ACPI namespace)?
*/
- if (ACPI_IS_ROOT_DEVICE(device)) {
- strcpy(device->pnp.bus_id, "ACPI");
+ if (!acpi_dev_parent(device)) {
+ strscpy(device->pnp.bus_id, "ACPI");
return;
}
switch (device->device_type) {
case ACPI_BUS_TYPE_POWER_BUTTON:
- strcpy(device->pnp.bus_id, "PWRF");
+ strscpy(device->pnp.bus_id, "PWRF");
break;
case ACPI_BUS_TYPE_SLEEP_BUTTON:
- strcpy(device->pnp.bus_id, "SLPF");
+ strscpy(device->pnp.bus_id, "SLPF");
break;
case ACPI_BUS_TYPE_ECDT_EC:
- strcpy(device->pnp.bus_id, "ECDT");
+ strscpy(device->pnp.bus_id, "ECDT");
break;
default:
acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer);
@@ -1136,7 +1205,7 @@ static void acpi_device_get_busid(struct acpi_device *device)
else
break;
}
- strcpy(device->pnp.bus_id, bus_id);
+ strscpy(device->pnp.bus_id, bus_id);
break;
}
}
@@ -1260,10 +1329,10 @@ const char *acpi_device_hid(struct acpi_device *device)
{
struct acpi_hardware_id *hid;
- if (list_empty(&device->pnp.ids))
+ hid = list_first_entry_or_null(&device->pnp.ids, struct acpi_hardware_id, list);
+ if (!hid)
return dummy_hid;
- hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list);
return hid->id;
}
EXPORT_SYMBOL(acpi_device_hid);
@@ -1372,9 +1441,12 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
* Some devices don't reliably have _HIDs & _CIDs, so add
* synthetic HIDs to make sure drivers can find them.
*/
- if (acpi_is_video_device(handle))
+ if (acpi_is_video_device(handle)) {
acpi_add_id(pnp, ACPI_VIDEO_HID);
- else if (acpi_bay_match(handle))
+ pnp->type.backlight = 1;
+ break;
+ }
+ if (acpi_bay_match(handle))
acpi_add_id(pnp, ACPI_BAY_HID);
else if (acpi_dock_match(handle))
acpi_add_id(pnp, ACPI_DOCK_HID);
@@ -1384,8 +1456,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
acpi_object_is_system_bus(handle)) {
/* \_SB, \_TZ, LNXSYBUS */
acpi_add_id(pnp, ACPI_BUS_HID);
- strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME);
- strcpy(pnp->device_class, ACPI_BUS_CLASS);
+ strscpy(pnp->device_name, ACPI_BUS_DEVICE_NAME);
+ strscpy(pnp->device_class, ACPI_BUS_CLASS);
}
break;
@@ -1467,25 +1539,21 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
* acpi_dma_get_range() - Get device DMA parameters.
*
* @dev: device to configure
- * @dma_addr: pointer device DMA address result
- * @offset: pointer to the DMA offset result
- * @size: pointer to DMA range size result
+ * @map: pointer to DMA ranges result
*
- * Evaluate DMA regions and return respectively DMA region start, offset
- * and size in dma_addr, offset and size on parsing success; it does not
- * update the passed in values on failure.
+ * Evaluate DMA regions and return pointer to DMA regions on
+ * parsing success; it does not update the passed in values on failure.
*
* Return 0 on success, < 0 on failure.
*/
-int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset,
- u64 *size)
+int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map)
{
struct acpi_device *adev;
LIST_HEAD(list);
struct resource_entry *rentry;
int ret;
struct device *dma_dev = dev;
- u64 len, dma_start = U64_MAX, dma_end = 0, dma_offset = 0;
+ struct bus_dma_region *r;
/*
* Walk the device tree chasing an ACPI companion with a _DMA
@@ -1510,31 +1578,28 @@ int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset,
ret = acpi_dev_get_dma_resources(adev, &list);
if (ret > 0) {
+ r = kcalloc(ret + 1, sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ *map = r;
+
list_for_each_entry(rentry, &list, node) {
- if (dma_offset && rentry->offset != dma_offset) {
+ if (rentry->res->start >= rentry->res->end) {
+ kfree(*map);
+ *map = NULL;
ret = -EINVAL;
- dev_warn(dma_dev, "Can't handle multiple windows with different offsets\n");
+ dev_dbg(dma_dev, "Invalid DMA regions configuration\n");
goto out;
}
- dma_offset = rentry->offset;
- /* Take lower and upper limits */
- if (rentry->res->start < dma_start)
- dma_start = rentry->res->start;
- if (rentry->res->end > dma_end)
- dma_end = rentry->res->end;
+ r->cpu_start = rentry->res->start;
+ r->dma_start = rentry->res->start - rentry->offset;
+ r->size = resource_size(rentry->res);
+ r++;
}
-
- if (dma_start >= dma_end) {
- ret = -EINVAL;
- dev_dbg(dma_dev, "Invalid DMA regions configuration\n");
- goto out;
- }
-
- *dma_addr = dma_start - dma_offset;
- len = dma_end - dma_start;
- *size = max(len, len + 1);
- *offset = dma_offset;
}
out:
acpi_dev_free_resource_list(&list);
@@ -1544,72 +1609,51 @@ int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset,
#ifdef CONFIG_IOMMU_API
int acpi_iommu_fwspec_init(struct device *dev, u32 id,
- struct fwnode_handle *fwnode,
- const struct iommu_ops *ops)
+ struct fwnode_handle *fwnode)
{
- int ret = iommu_fwspec_init(dev, fwnode, ops);
-
- if (!ret)
- ret = iommu_fwspec_add_ids(dev, &id, 1);
-
- return ret;
-}
+ int ret;
-static inline const struct iommu_ops *acpi_iommu_fwspec_ops(struct device *dev)
-{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ ret = iommu_fwspec_init(dev, fwnode);
+ if (ret)
+ return ret;
- return fwspec ? fwspec->ops : NULL;
+ return iommu_fwspec_add_ids(dev, &id, 1);
}
-static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev,
- const u32 *id_in)
+static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
{
int err;
- const struct iommu_ops *ops;
- /*
- * If we already translated the fwspec there is nothing left to do,
- * return the iommu_ops.
- */
- ops = acpi_iommu_fwspec_ops(dev);
- if (ops)
- return ops;
+ /* Serialise to make dev->iommu stable under our potential fwspec */
+ mutex_lock(&iommu_probe_device_lock);
+ /* If we already translated the fwspec there is nothing left to do */
+ if (dev_iommu_fwspec_get(dev)) {
+ mutex_unlock(&iommu_probe_device_lock);
+ return 0;
+ }
err = iort_iommu_configure_id(dev, id_in);
if (err && err != -EPROBE_DEFER)
+ err = rimt_iommu_configure_id(dev, id_in);
+ if (err && err != -EPROBE_DEFER)
err = viot_iommu_configure(dev);
- /*
- * If we have reason to believe the IOMMU driver missed the initial
- * iommu_probe_device() call for dev, replay it to get things in order.
- */
- if (!err && dev->bus && !device_iommu_mapped(dev))
- err = iommu_probe_device(dev);
+ mutex_unlock(&iommu_probe_device_lock);
- /* Ignore all other errors apart from EPROBE_DEFER */
- if (err == -EPROBE_DEFER) {
- return ERR_PTR(err);
- } else if (err) {
- dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);
- return NULL;
- }
- return acpi_iommu_fwspec_ops(dev);
+ return err;
}
#else /* !CONFIG_IOMMU_API */
int acpi_iommu_fwspec_init(struct device *dev, u32 id,
- struct fwnode_handle *fwnode,
- const struct iommu_ops *ops)
+ struct fwnode_handle *fwnode)
{
return -ENODEV;
}
-static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev,
- const u32 *id_in)
+static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
{
- return NULL;
+ return -ENODEV;
}
#endif /* !CONFIG_IOMMU_API */
@@ -1623,22 +1667,23 @@ static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev,
int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr,
const u32 *input_id)
{
- const struct iommu_ops *iommu;
- u64 dma_addr = 0, size = 0;
+ int ret;
if (attr == DEV_DMA_NOT_SUPPORTED) {
set_dma_ops(dev, &dma_dummy_ops);
return 0;
}
- acpi_arch_dma_setup(dev, &dma_addr, &size);
+ acpi_arch_dma_setup(dev);
- iommu = acpi_iommu_configure_id(dev, input_id);
- if (PTR_ERR(iommu) == -EPROBE_DEFER)
+ /* Ignore all other errors apart from EPROBE_DEFER */
+ ret = acpi_iommu_configure_id(dev, input_id);
+ if (ret == -EPROBE_DEFER)
return -EPROBE_DEFER;
+ if (ret)
+ dev_dbg(dev, "Adding to IOMMU failed: %d\n", ret);
- arch_setup_dma_ops(dev, dma_addr, size,
- iommu, attr == DEV_DMA_COHERENT);
+ arch_setup_dma_ops(dev, attr == DEV_DMA_COHERENT);
return 0;
}
@@ -1648,7 +1693,7 @@ static void acpi_init_coherency(struct acpi_device *adev)
{
unsigned long long cca = 0;
acpi_status status;
- struct acpi_device *parent = adev->parent;
+ struct acpi_device *parent = acpi_dev_parent(adev);
if (parent && parent->flags.cca_seen) {
/*
@@ -1692,7 +1737,7 @@ static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data)
static bool acpi_is_indirect_io_slave(struct acpi_device *device)
{
- struct acpi_device *parent = device->parent;
+ struct acpi_device *parent = acpi_dev_parent(device);
static const struct acpi_device_id indirect_io_hosts[] = {
{"HISI0191", 0},
{}
@@ -1718,8 +1763,12 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
{"BSG1160", },
{"BSG2150", },
{"CSC3551", },
+ {"CSC3554", },
+ {"CSC3556", },
+ {"CSC3557", },
{"INT33FE", },
{"INT3515", },
+ {"TXNW2781", },
/* Non-conforming _HID for Cirrus Logic already released */
{"CLSA0100", },
{"CLSA0101", },
@@ -1727,6 +1776,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
* Some ACPI devs contain SerialBus resources even though they are not
* attached to a serial bus at all.
*/
+ {ACPI_VIDEO_HID, },
{"MSHW0028", },
/*
* HIDs of device with an UartSerialBusV2 resource for which userspace
@@ -1762,12 +1812,17 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
}
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
- int type)
+ int type, void (*release)(struct device *))
{
+ struct acpi_device *parent = acpi_find_parent_acpi_dev(handle);
+
INIT_LIST_HEAD(&device->pnp.ids);
device->device_type = type;
device->handle = handle;
- device->parent = acpi_bus_get_parent(handle);
+ device->dev.parent = parent ? &parent->dev : NULL;
+ device->dev.release = release;
+ device->dev.bus = &acpi_bus_type;
+ device->dev.groups = acpi_groups;
fwnode_init(&device->fwnode, &acpi_device_fwnode_ops);
acpi_set_device_status(device, ACPI_STA_DEFAULT);
acpi_device_get_busid(device);
@@ -1793,7 +1848,8 @@ static void acpi_scan_dep_init(struct acpi_device *adev)
if (dep->honor_dep)
adev->flags.honor_deps = 1;
- adev->dep_unmet++;
+ if (!dep->met)
+ adev->dep_unmet++;
}
}
}
@@ -1821,7 +1877,7 @@ static int acpi_add_single_object(struct acpi_device **child,
if (!device)
return -ENOMEM;
- acpi_init_device_object(device, handle, type);
+ acpi_init_device_object(device, handle, type, acpi_device_release);
/*
* Getting the status is delayed till here so that we can call
* acpi_bus_get_status() and use its quirk handling. Note that
@@ -1851,7 +1907,7 @@ static int acpi_add_single_object(struct acpi_device **child,
mutex_unlock(&acpi_dep_list_lock);
if (!result)
- result = __acpi_device_add(device, acpi_device_release);
+ result = acpi_device_add(device);
if (result) {
acpi_device_release(&device->dev);
@@ -1862,8 +1918,8 @@ static int acpi_add_single_object(struct acpi_device **child,
acpi_device_add_finalize(device);
acpi_handle_debug(handle, "Added as %s, parent %s\n",
- dev_name(&device->dev), device->parent ?
- dev_name(&device->parent->dev) : "(null)");
+ dev_name(&device->dev), device->dev.parent ?
+ dev_name(device->dev.parent) : "(null)");
*child = device;
return 0;
@@ -1909,6 +1965,11 @@ bool acpi_device_is_present(const struct acpi_device *adev)
return adev->status.present || adev->status.functional;
}
+bool acpi_device_is_enabled(const struct acpi_device *adev)
+{
+ return adev->status.enabled;
+}
+
static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
const char *idstr,
const struct acpi_device_id **matchid)
@@ -1953,54 +2014,18 @@ void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val)
mutex_unlock(&acpi_scan_lock);
}
-static void acpi_scan_init_hotplug(struct acpi_device *adev)
-{
- struct acpi_hardware_id *hwid;
-
- if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) {
- acpi_dock_add(adev);
- return;
- }
- list_for_each_entry(hwid, &adev->pnp.ids, list) {
- struct acpi_scan_handler *handler;
-
- handler = acpi_scan_match_handler(hwid->id, NULL);
- if (handler) {
- adev->flags.hotplug_notify = true;
- break;
- }
- }
-}
-
-static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
+int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices)
{
- struct acpi_handle_list dep_devices;
- acpi_status status;
u32 count;
int i;
- /*
- * Check for _HID here to avoid deferring the enumeration of:
- * 1. PCI devices.
- * 2. ACPI nodes describing USB ports.
- * Still, checking for _HID catches more then just these cases ...
- */
- if (!check_dep || !acpi_has_method(handle, "_DEP") ||
- !acpi_has_method(handle, "_HID"))
- return 0;
-
- status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
- if (ACPI_FAILURE(status)) {
- acpi_handle_debug(handle, "Failed to evaluate _DEP.\n");
- return 0;
- }
-
- for (count = 0, i = 0; i < dep_devices.count; i++) {
+ for (count = 0, i = 0; i < dep_devices->count; i++) {
struct acpi_device_info *info;
struct acpi_dep_data *dep;
bool skip, honor_dep;
+ acpi_status status;
- status = acpi_get_object_info(dep_devices.handles[i], &info);
+ status = acpi_get_object_info(dep_devices->handles[i], &info);
if (ACPI_FAILURE(status)) {
acpi_handle_debug(handle, "Error reading _DEP device info\n");
continue;
@@ -2019,21 +2044,79 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
count++;
- dep->supplier = dep_devices.handles[i];
+ dep->supplier = dep_devices->handles[i];
dep->consumer = handle;
dep->honor_dep = honor_dep;
mutex_lock(&acpi_dep_list_lock);
- list_add_tail(&dep->node , &acpi_dep_list);
+ list_add_tail(&dep->node, &acpi_dep_list);
mutex_unlock(&acpi_dep_list_lock);
}
+ acpi_handle_list_free(dep_devices);
+ return count;
+}
+
+static void acpi_scan_init_hotplug(struct acpi_device *adev)
+{
+ struct acpi_hardware_id *hwid;
+
+ if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) {
+ acpi_dock_add(adev);
+ return;
+ }
+ list_for_each_entry(hwid, &adev->pnp.ids, list) {
+ struct acpi_scan_handler *handler;
+
+ handler = acpi_scan_match_handler(hwid->id, NULL);
+ if (handler) {
+ adev->flags.hotplug_notify = true;
+ break;
+ }
+ }
+}
+
+u32 __weak arch_acpi_add_auto_dep(acpi_handle handle) { return 0; }
+
+static u32 acpi_scan_check_dep(acpi_handle handle)
+{
+ struct acpi_handle_list dep_devices;
+ u32 count = 0;
+
+ /*
+ * Some architectures like RISC-V need to add dependencies for
+ * all devices which use GSI to the interrupt controller so that
+ * interrupt controller is probed before any of those devices.
+ * Instead of mandating _DEP on all the devices, detect the
+ * dependency and add automatically.
+ */
+ count += arch_acpi_add_auto_dep(handle);
+
+ /*
+ * Check for _HID here to avoid deferring the enumeration of:
+ * 1. PCI devices.
+ * 2. ACPI nodes describing USB ports.
+ * Still, checking for _HID catches more then just these cases ...
+ */
+ if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID"))
+ return count;
+
+ if (!acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices)) {
+ acpi_handle_debug(handle, "Failed to evaluate _DEP.\n");
+ return count;
+ }
+
+ count += acpi_scan_add_dep(handle, &dep_devices);
return count;
}
-static bool acpi_bus_scan_second_pass;
+static acpi_status acpi_scan_check_crs_csi2_cb(acpi_handle handle, u32 a, void *b, void **c)
+{
+ acpi_mipi_check_crs_csi2(handle);
+ return AE_OK;
+}
-static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
+static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
struct acpi_device **adev_p)
{
struct acpi_device *device = acpi_fetch_acpi_dev(handle);
@@ -2051,10 +2134,24 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
if (acpi_device_should_be_hidden(handle))
return AE_OK;
- /* Bail out if there are dependencies. */
- if (acpi_scan_check_dep(handle, check_dep) > 0) {
- acpi_bus_scan_second_pass = true;
- return AE_CTRL_DEPTH;
+ if (first_pass) {
+ acpi_mipi_check_crs_csi2(handle);
+
+ /* Bail out if there are dependencies. */
+ if (acpi_scan_check_dep(handle) > 0) {
+ /*
+ * The entire CSI-2 connection graph needs to be
+ * extracted before any drivers or scan handlers
+ * are bound to struct device objects, so scan
+ * _CRS CSI-2 resource descriptors for all
+ * devices below the current handle.
+ */
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+ ACPI_UINT32_MAX,
+ acpi_scan_check_crs_csi2_cb,
+ NULL, NULL, NULL);
+ return AE_CTRL_DEPTH;
+ }
}
fallthrough;
@@ -2078,10 +2175,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
}
/*
- * If check_dep is true at this point, the device has no dependencies,
+ * If first_pass is true at this point, the device has no dependencies,
* or the creation of the device object would have been postponed above.
*/
- acpi_add_single_object(&device, handle, type, !check_dep);
+ acpi_add_single_object(&device, handle, type, !first_pass);
if (!device)
return AE_CTRL_DEPTH;
@@ -2196,6 +2293,8 @@ static int acpi_bus_attach(struct acpi_device *device, void *first_pass)
if (device->handler)
goto ok;
+ acpi_ec_register_opregions(device);
+
if (!device->flags.initialized) {
device->flags.power_manageable =
device->power.states[ACPI_STATE_D0].flags.valid;
@@ -2235,11 +2334,24 @@ ok:
return 0;
}
-static int acpi_dev_get_first_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
+static int acpi_dev_get_next_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
{
- struct acpi_device *adev;
+ struct acpi_device **adev_p = data;
+ struct acpi_device *adev = *adev_p;
+
+ /*
+ * If we're passed a 'previous' consumer device then we need to skip
+ * any consumers until we meet the previous one, and then NULL @data
+ * so the next one can be returned.
+ */
+ if (adev) {
+ if (dep->consumer == adev->handle)
+ *adev_p = NULL;
+
+ return 0;
+ }
- adev = acpi_bus_get_acpi_device(dep->consumer);
+ adev = acpi_get_acpi_dev(dep->consumer);
if (adev) {
*(struct acpi_device **)data = adev;
return 1;
@@ -2285,14 +2397,20 @@ static bool acpi_scan_clear_dep_queue(struct acpi_device *adev)
* initial enumeration of devices is complete, put it into the unbound
* workqueue.
*/
- queue_work(system_unbound_wq, &cdw->work);
+ queue_work(system_dfl_wq, &cdw->work);
return true;
}
+static void acpi_scan_delete_dep_data(struct acpi_dep_data *dep)
+{
+ list_del(&dep->node);
+ kfree(dep);
+}
+
static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
{
- struct acpi_device *adev = acpi_bus_get_acpi_device(dep->consumer);
+ struct acpi_device *adev = acpi_get_acpi_dev(dep->consumer);
if (adev) {
adev->dep_unmet--;
@@ -2300,8 +2418,10 @@ static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
acpi_dev_put(adev);
}
- list_del(&dep->node);
- kfree(dep);
+ if (dep->free_when_met)
+ acpi_scan_delete_dep_data(dep);
+ else
+ dep->met = true;
return 0;
}
@@ -2368,25 +2488,88 @@ bool acpi_dev_ready_for_enumeration(const struct acpi_device *device)
EXPORT_SYMBOL_GPL(acpi_dev_ready_for_enumeration);
/**
- * acpi_dev_get_first_consumer_dev - Return ACPI device dependent on @supplier
+ * acpi_dev_get_next_consumer_dev - Return the next adev dependent on @supplier
* @supplier: Pointer to the dependee device
+ * @start: Pointer to the current dependent device
*
- * Returns the first &struct acpi_device which declares itself dependent on
+ * Returns the next &struct acpi_device which declares itself dependent on
* @supplier via the _DEP buffer, parsed from the acpi_dep_list.
*
- * The caller is responsible for putting the reference to adev when it is no
- * longer needed.
+ * If the returned adev is not passed as @start to this function, the caller is
+ * responsible for putting the reference to adev when it is no longer needed.
*/
-struct acpi_device *acpi_dev_get_first_consumer_dev(struct acpi_device *supplier)
+struct acpi_device *acpi_dev_get_next_consumer_dev(struct acpi_device *supplier,
+ struct acpi_device *start)
{
- struct acpi_device *adev = NULL;
+ struct acpi_device *adev = start;
acpi_walk_dep_device_list(supplier->handle,
- acpi_dev_get_first_consumer_dev_cb, &adev);
+ acpi_dev_get_next_consumer_dev_cb, &adev);
+
+ acpi_dev_put(start);
+
+ if (adev == start)
+ return NULL;
return adev;
}
-EXPORT_SYMBOL_GPL(acpi_dev_get_first_consumer_dev);
+EXPORT_SYMBOL_GPL(acpi_dev_get_next_consumer_dev);
+
+static void acpi_scan_postponed_branch(acpi_handle handle)
+{
+ struct acpi_device *adev = NULL;
+
+ if (ACPI_FAILURE(acpi_bus_check_add(handle, false, &adev)))
+ return;
+
+ acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+ acpi_bus_check_add_2, NULL, NULL, (void **)&adev);
+
+ /*
+ * Populate the ACPI _CRS CSI-2 software nodes for the ACPI devices that
+ * have been added above.
+ */
+ acpi_mipi_init_crs_csi2_swnodes();
+
+ acpi_bus_attach(adev, NULL);
+}
+
+static void acpi_scan_postponed(void)
+{
+ struct acpi_dep_data *dep, *tmp;
+
+ mutex_lock(&acpi_dep_list_lock);
+
+ list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) {
+ acpi_handle handle = dep->consumer;
+
+ /*
+ * In case there are multiple acpi_dep_list entries with the
+ * same consumer, skip the current entry if the consumer device
+ * object corresponding to it is present already.
+ */
+ if (!acpi_fetch_acpi_dev(handle)) {
+ /*
+ * Even though the lock is released here, tmp is
+ * guaranteed to be valid, because none of the list
+ * entries following dep is marked as "free when met"
+ * and so they cannot be deleted.
+ */
+ mutex_unlock(&acpi_dep_list_lock);
+
+ acpi_scan_postponed_branch(handle);
+
+ mutex_lock(&acpi_dep_list_lock);
+ }
+
+ if (dep->met)
+ acpi_scan_delete_dep_data(dep);
+ else
+ dep->free_when_met = true;
+ }
+
+ mutex_unlock(&acpi_dep_list_lock);
+}
/**
* acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
@@ -2406,8 +2589,6 @@ int acpi_bus_scan(acpi_handle handle)
{
struct acpi_device *device = NULL;
- acpi_bus_scan_second_pass = false;
-
/* Pass 1: Avoid enumerating devices with missing dependencies. */
if (ACPI_SUCCESS(acpi_bus_check_add(handle, true, &device)))
@@ -2418,52 +2599,26 @@ int acpi_bus_scan(acpi_handle handle)
if (!device)
return -ENODEV;
- acpi_bus_attach(device, (void *)true);
+ /*
+ * Set up ACPI _CRS CSI-2 software nodes using information extracted
+ * from the _CRS CSI-2 resource descriptors during the ACPI namespace
+ * walk above and MIPI DisCo for Imaging device properties.
+ */
+ acpi_mipi_scan_crs_csi2();
+ acpi_mipi_init_crs_csi2_swnodes();
- if (!acpi_bus_scan_second_pass)
- return 0;
+ acpi_bus_attach(device, (void *)true);
/* Pass 2: Enumerate all of the remaining devices. */
- device = NULL;
+ acpi_scan_postponed();
- if (ACPI_SUCCESS(acpi_bus_check_add(handle, false, &device)))
- acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
- acpi_bus_check_add_2, NULL, NULL,
- (void **)&device);
-
- acpi_bus_attach(device, NULL);
+ acpi_mipi_crs_csi2_cleanup();
return 0;
}
EXPORT_SYMBOL(acpi_bus_scan);
-static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
-{
- struct acpi_scan_handler *handler = adev->handler;
-
- acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, NULL);
-
- adev->flags.match_driver = false;
- if (handler) {
- if (handler->detach)
- handler->detach(adev);
-
- adev->handler = NULL;
- } else {
- device_release_driver(&adev->dev);
- }
- /*
- * Most likely, the device is going away, so put it into D3cold before
- * that.
- */
- acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
- adev->flags.initialized = false;
- acpi_device_clear_enumerated(adev);
-
- return 0;
-}
-
/**
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
@@ -2472,7 +2627,9 @@ static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
*/
void acpi_bus_trim(struct acpi_device *adev)
{
- acpi_bus_trim_one(adev, NULL);
+ uintptr_t flags = 0;
+
+ acpi_scan_check_and_detach(adev, (void *)flags);
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);
@@ -2553,8 +2710,7 @@ void __init acpi_scan_init(void)
acpi_memory_hotplug_init();
acpi_watchdog_init();
acpi_pnp_init();
- acpi_int340x_thermal_init();
- acpi_amba_init();
+ acpi_power_resources_init();
acpi_init_lpit();
acpi_scan_add_handler(&generic_device_handler);
@@ -2621,6 +2777,8 @@ static int __init acpi_match_madt(union acpi_subtable_headers *header,
return 0;
}
+void __weak arch_sort_irqchip_probe(struct acpi_probe_entry *ap_head, int nr) { }
+
int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
{
int count = 0;
@@ -2629,6 +2787,7 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
return 0;
mutex_lock(&acpi_probe_mutex);
+ arch_sort_irqchip_probe(ap_head, nr);
for (ape = ap_head; nr; ape++, nr--) {
if (ACPI_COMPARE_NAMESEG(ACPI_SIG_MADT, ape->id)) {
acpi_probe_count = 0;
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index ad4b2987b3d6..66ec81e306d4 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -60,13 +60,17 @@ static struct notifier_block tts_notifier = {
.priority = 0,
};
+#ifndef acpi_skip_set_wakeup_address
+#define acpi_skip_set_wakeup_address() false
+#endif
+
static int acpi_sleep_prepare(u32 acpi_state)
{
#ifdef CONFIG_ACPI_SLEEP
unsigned long acpi_wakeup_address;
/* do we have a wakeup address for S2 and S3? */
- if (acpi_state == ACPI_STATE_S3) {
+ if (acpi_state == ACPI_STATE_S3 && !acpi_skip_set_wakeup_address()) {
acpi_wakeup_address = acpi_get_wakeup_address();
if (!acpi_wakeup_address)
return -EFAULT;
@@ -348,6 +352,20 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
},
},
/*
+ * The ASUS ROG M16 from 2023 has many events which wake it from s2idle
+ * resulting in excessive battery drain and risk of laptop overheating,
+ * these events can be caused by the MMC or y AniMe display if installed.
+ * The match is valid for all of the GU604V<x> range.
+ */
+ {
+ .callback = init_default_s3,
+ .ident = "ASUS ROG Zephyrus M16 (2023)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ROG Zephyrus M16 GU604V"),
+ },
+ },
+ /*
* https://bugzilla.kernel.org/show_bug.cgi?id=189431
* Lenovo G50-45 is a platform later than 2012, but needs nvs memory
* saving during S3.
@@ -381,18 +399,6 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "20GGA00L00"),
},
},
- /*
- * ASUS B1400CEAE hangs on resume from suspend (see
- * https://bugzilla.kernel.org/show_bug.cgi?id=215742).
- */
- {
- .callback = init_default_s3,
- .ident = "ASUS B1400CEAE",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASUS EXPERTBOOK B1400CEAE"),
- },
- },
{},
};
@@ -510,6 +516,7 @@ static void acpi_pm_finish(void)
/**
* acpi_pm_start - Start system PM transition.
+ * @acpi_state: The target ACPI power state to transition to.
*/
static void acpi_pm_start(u32 acpi_state)
{
@@ -548,8 +555,9 @@ static u32 acpi_suspend_states[] = {
};
/**
- * acpi_suspend_begin - Set the target system sleep state to the state
- * associated with given @pm_state, if supported.
+ * acpi_suspend_begin - Set the target system sleep state to the state
+ * associated with given @pm_state, if supported.
+ * @pm_state: The target system power management state.
*/
static int acpi_suspend_begin(suspend_state_t pm_state)
{
@@ -632,11 +640,19 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
}
/*
- * Disable and clear GPE status before interrupt is enabled. Some GPEs
- * (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
- * acpi_leave_sleep_state will reenable specific GPEs later
+ * Disable all GPE and clear their status bits before interrupts are
+ * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can
+ * prevent them from producing spurious interrupts.
+ *
+ * acpi_leave_sleep_state() will reenable specific GPEs later.
+ *
+ * Because this code runs on one CPU with disabled interrupts (all of
+ * the other CPUs are offline at this time), it need not acquire any
+ * sleeping locks which may trigger an implicit preemption point even
+ * if there is no contention, so avoid doing that by using a low-level
+ * library routine here.
*/
- acpi_disable_all_gpes();
+ acpi_hw_disable_all_gpes();
/* Allow EC transactions to happen. */
acpi_ec_unblock_transactions();
@@ -671,10 +687,11 @@ static const struct platform_suspend_ops acpi_suspend_ops = {
};
/**
- * acpi_suspend_begin_old - Set the target system sleep state to the
- * state associated with given @pm_state, if supported, and
- * execute the _PTS control method. This function is used if the
- * pre-ACPI 2.0 suspend ordering has been requested.
+ * acpi_suspend_begin_old - Set the target system sleep state to the
+ * state associated with given @pm_state, if supported, and
+ * execute the _PTS control method. This function is used if the
+ * pre-ACPI 2.0 suspend ordering has been requested.
+ * @pm_state: The target suspend state for the system.
*/
static int acpi_suspend_begin_old(suspend_state_t pm_state)
{
@@ -710,7 +727,13 @@ int acpi_s2idle_begin(void)
int acpi_s2idle_prepare(void)
{
if (acpi_sci_irq_valid()) {
- enable_irq_wake(acpi_sci_irq);
+ int error;
+
+ error = enable_irq_wake(acpi_sci_irq);
+ if (error)
+ pr_warn("Warning: Failed to enable wakeup from IRQ %d: %d\n",
+ acpi_sci_irq, error);
+
acpi_ec_set_gpe_wake_mask(ACPI_GPE_ENABLE);
}
@@ -830,7 +853,7 @@ void __weak acpi_s2idle_setup(void)
s2idle_set_ops(&acpi_s2idle_ops);
}
-static void acpi_sleep_suspend_setup(void)
+static void __init acpi_sleep_suspend_setup(void)
{
bool suspend_ops_needed = false;
int i;
@@ -861,13 +884,13 @@ bool acpi_s2idle_wakeup(void)
#ifdef CONFIG_PM_SLEEP
static u32 saved_bm_rld;
-static int acpi_save_bm_rld(void)
+static int acpi_save_bm_rld(void *data)
{
acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld);
return 0;
}
-static void acpi_restore_bm_rld(void)
+static void acpi_restore_bm_rld(void *data)
{
u32 resumed_bm_rld = 0;
@@ -878,14 +901,18 @@ static void acpi_restore_bm_rld(void)
acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld);
}
-static struct syscore_ops acpi_sleep_syscore_ops = {
+static const struct syscore_ops acpi_sleep_syscore_ops = {
.suspend = acpi_save_bm_rld,
.resume = acpi_restore_bm_rld,
};
+static struct syscore acpi_sleep_syscore = {
+ .ops = &acpi_sleep_syscore_ops,
+};
+
static void acpi_sleep_syscore_init(void)
{
- register_syscore_ops(&acpi_sleep_syscore_ops);
+ register_syscore(&acpi_sleep_syscore);
}
#else
static inline void acpi_sleep_syscore_init(void) {}
@@ -961,10 +988,11 @@ static const struct platform_hibernation_ops acpi_hibernation_ops = {
};
/**
- * acpi_hibernation_begin_old - Set the target system sleep state to
- * ACPI_STATE_S4 and execute the _PTS control method. This
- * function is used if the pre-ACPI 2.0 suspend ordering has been
- * requested.
+ * acpi_hibernation_begin_old - Set the target system sleep state to
+ * ACPI_STATE_S4 and execute the _PTS control method. This
+ * function is used if the pre-ACPI 2.0 suspend ordering has been
+ * requested.
+ * @stage: The power management event message.
*/
static int acpi_hibernation_begin_old(pm_message_t stage)
{
@@ -1088,6 +1116,14 @@ int __init acpi_sleep_init(void)
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_FIRMWARE,
acpi_power_off, NULL);
+
+ /*
+ * Windows uses S5 for reboot, so some BIOSes depend on it to
+ * perform proper reboot.
+ */
+ register_sys_off_handler(SYS_OFF_MODE_RESTART_PREPARE,
+ SYS_OFF_PRIO_FIRMWARE,
+ acpi_power_off_prepare, NULL);
} else {
acpi_no_s5 = true;
}
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index 7fe41ee489d6..9c3cb109c5d2 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -17,9 +17,7 @@ static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)
extern int acpi_s2idle_begin(void);
extern int acpi_s2idle_prepare(void);
-extern int acpi_s2idle_prepare_late(void);
extern bool acpi_s2idle_wake(void);
-extern void acpi_s2idle_restore_early(void);
extern void acpi_s2idle_restore(void);
extern void acpi_s2idle_end(void);
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
index 1eabfcd122ee..73cb933fdc89 100644
--- a/drivers/acpi/spcr.c
+++ b/drivers/acpi/spcr.c
@@ -71,7 +71,6 @@ static bool xgene_8250_erratum_present(struct acpi_table_spcr *tb)
/**
* acpi_parse_spcr() - parse ACPI SPCR table and add preferred console
- *
* @enable_earlycon: set up earlycon for the console specified by the table
* @enable_console: setup the console specified by the table.
*
@@ -82,7 +81,6 @@ static bool xgene_8250_erratum_present(struct acpi_table_spcr *tb)
*
* When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called
* from arch initialization code as soon as the DT/ACPI decision is made.
- *
*/
int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
{
@@ -97,9 +95,7 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
if (acpi_disabled)
return -ENODEV;
- status = acpi_get_table(ACPI_SIG_SPCR, 0,
- (struct acpi_table_header **)&table);
-
+ status = acpi_get_table(ACPI_SIG_SPCR, 0, (struct acpi_table_header **)&table);
if (ACPI_FAILURE(status))
return -ENOENT;
@@ -110,12 +106,12 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
u32 bit_width = table->serial_port.access_width;
if (bit_width > ACPI_ACCESS_BIT_MAX) {
- pr_err("Unacceptable wide SPCR Access Width. Defaulting to byte size\n");
+ pr_err(FW_BUG "Unacceptable wide SPCR Access Width. Defaulting to byte size\n");
bit_width = ACPI_ACCESS_BIT_DEFAULT;
}
switch (ACPI_ACCESS_BIT_WIDTH((bit_width))) {
default:
- pr_err("Unexpected SPCR Access Width. Defaulting to byte size\n");
+ pr_err(FW_BUG "Unexpected SPCR Access Width. Defaulting to byte size\n");
fallthrough;
case 8:
iotype = "mmio";
@@ -145,12 +141,23 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
case ACPI_DBG2_16550_NVIDIA:
uart = "uart";
break;
+ case ACPI_DBG2_RISCV_SBI_CON:
+ uart = "sbi";
+ break;
default:
err = -ENOENT;
goto done;
}
- switch (table->baud_rate) {
+ /*
+ * SPCR 1.09 defines Precise Baud Rate Filed contains a specific
+ * non-zero baud rate which overrides the value of the Configured
+ * Baud Rate field. If this field is zero or not present, Configured
+ * Baud Rate is used.
+ */
+ if (table->header.revision >= 4 && table->precise_baudrate)
+ baud_rate = table->precise_baudrate;
+ else switch (table->baud_rate) {
case 0:
/*
* SPCR 1.04 defines 0 as a preconfigured state of UART.
@@ -202,7 +209,8 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
if (xgene_8250_erratum_present(table)) {
iotype = "mmio32";
- /* for xgene v1 and v2 we don't know the clock rate of the
+ /*
+ * For xgene v1 and v2 we don't know the clock rate of the
* UART so don't attempt to change to the baud rate state
* in the table because driver cannot calculate the dividers
*/
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index cc2fe0618178..e596224302f4 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -9,6 +9,7 @@
#include <linux/bitmap.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/moduleparam.h>
#include "internal.h"
@@ -197,7 +198,7 @@ static int param_set_trace_method_name(const char *val,
static int param_get_trace_method_name(char *buffer, const struct kernel_param *kp)
{
- return scnprintf(buffer, PAGE_SIZE, "%s\n", acpi_gbl_trace_method_name);
+ return sysfs_emit(buffer, "%s\n", acpi_gbl_trace_method_name);
}
static const struct kernel_param_ops param_ops_trace_method = {
@@ -318,7 +319,7 @@ struct acpi_data_attr {
};
static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count)
{
struct acpi_table_attr *table_attr =
@@ -411,7 +412,7 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
}
static ssize_t acpi_data_show(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count)
{
struct acpi_data_attr *data_attr;
@@ -457,11 +458,28 @@ static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr)
return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);
}
+static int acpi_ccel_data_init(void *th, struct acpi_data_attr *data_attr)
+{
+ struct acpi_table_ccel *ccel = th;
+
+ if (ccel->header.length < sizeof(struct acpi_table_ccel) ||
+ !ccel->log_area_start_address || !ccel->log_area_minimum_length) {
+ kfree(data_attr);
+ return -EINVAL;
+ }
+ data_attr->addr = ccel->log_area_start_address;
+ data_attr->attr.size = ccel->log_area_minimum_length;
+ data_attr->attr.attr.name = "CCEL";
+
+ return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);
+}
+
static struct acpi_data_obj {
char *name;
int (*fn)(void *, struct acpi_data_attr *);
} acpi_data_objs[] = {
{ ACPI_SIG_BERT, acpi_bert_data_init },
+ { ACPI_SIG_CCEL, acpi_ccel_data_init },
};
#define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs)
@@ -952,7 +970,7 @@ static struct attribute *hotplug_profile_attrs[] = {
};
ATTRIBUTE_GROUPS(hotplug_profile);
-static struct kobj_type acpi_hotplug_profile_ktype = {
+static const struct kobj_type acpi_hotplug_profile_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_groups = hotplug_profile_groups,
};
@@ -992,7 +1010,7 @@ static ssize_t force_remove_store(struct kobject *kobj,
bool val;
int ret;
- ret = strtobool(buf, &val);
+ ret = kstrtobool(buf, &val);
if (ret < 0)
return ret;
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index 47ec11d4c68e..4286e4af1092 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -37,18 +37,6 @@ static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata;
static int acpi_apic_instance __initdata_or_acpilib;
-enum acpi_subtable_type {
- ACPI_SUBTABLE_COMMON,
- ACPI_SUBTABLE_HMAT,
- ACPI_SUBTABLE_PRMT,
- ACPI_SUBTABLE_CEDT,
-};
-
-struct acpi_subtable_entry {
- union acpi_subtable_headers *hdr;
- enum acpi_subtable_type type;
-};
-
/*
* Disable table checksum verification for the early stage due to the size
* limitation of the current x86 early mapping implementation.
@@ -68,7 +56,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
(struct acpi_madt_local_apic *)header;
pr_debug("LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n",
p->processor_id, p->id,
- (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
+ str_enabled_disabled(p->lapic_flags & ACPI_MADT_ENABLED));
}
break;
@@ -78,7 +66,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
(struct acpi_madt_local_x2apic *)header;
pr_debug("X2APIC (apic_id[0x%02x] uid[0x%02x] %s)\n",
p->local_apic_id, p->uid,
- (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
+ str_enabled_disabled(p->lapic_flags & ACPI_MADT_ENABLED));
}
break;
@@ -172,7 +160,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
(struct acpi_madt_local_sapic *)header;
pr_debug("LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n",
p->processor_id, p->id, p->eid,
- (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
+ str_enabled_disabled(p->lapic_flags & ACPI_MADT_ENABLED));
}
break;
@@ -195,7 +183,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
pr_debug("GICC (acpi_id[0x%04x] address[%llx] MPIDR[0x%llx] %s)\n",
p->uid, p->base_address,
p->arm_mpidr,
- (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
+ str_enabled_disabled(p->flags & ACPI_MADT_ENABLED));
}
break;
@@ -210,172 +198,45 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
}
break;
- default:
- pr_warn("Found unsupported MADT entry (type = 0x%x)\n",
- header->type);
- break;
- }
-}
-
-static unsigned long __init_or_acpilib
-acpi_get_entry_type(struct acpi_subtable_entry *entry)
-{
- switch (entry->type) {
- case ACPI_SUBTABLE_COMMON:
- return entry->hdr->common.type;
- case ACPI_SUBTABLE_HMAT:
- return entry->hdr->hmat.type;
- case ACPI_SUBTABLE_PRMT:
- return 0;
- case ACPI_SUBTABLE_CEDT:
- return entry->hdr->cedt.type;
- }
- return 0;
-}
-
-static unsigned long __init_or_acpilib
-acpi_get_entry_length(struct acpi_subtable_entry *entry)
-{
- switch (entry->type) {
- case ACPI_SUBTABLE_COMMON:
- return entry->hdr->common.length;
- case ACPI_SUBTABLE_HMAT:
- return entry->hdr->hmat.length;
- case ACPI_SUBTABLE_PRMT:
- return entry->hdr->prmt.length;
- case ACPI_SUBTABLE_CEDT:
- return entry->hdr->cedt.length;
- }
- return 0;
-}
-
-static unsigned long __init_or_acpilib
-acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
-{
- switch (entry->type) {
- case ACPI_SUBTABLE_COMMON:
- return sizeof(entry->hdr->common);
- case ACPI_SUBTABLE_HMAT:
- return sizeof(entry->hdr->hmat);
- case ACPI_SUBTABLE_PRMT:
- return sizeof(entry->hdr->prmt);
- case ACPI_SUBTABLE_CEDT:
- return sizeof(entry->hdr->cedt);
- }
- return 0;
-}
-
-static enum acpi_subtable_type __init_or_acpilib
-acpi_get_subtable_type(char *id)
-{
- if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
- return ACPI_SUBTABLE_HMAT;
- if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
- return ACPI_SUBTABLE_PRMT;
- if (strncmp(id, ACPI_SIG_CEDT, 4) == 0)
- return ACPI_SUBTABLE_CEDT;
- return ACPI_SUBTABLE_COMMON;
-}
-
-static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
-{
- return proc->handler || proc->handler_arg;
-}
-
-static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc,
- union acpi_subtable_headers *hdr,
- unsigned long end)
-{
- if (proc->handler)
- return proc->handler(hdr, end);
- if (proc->handler_arg)
- return proc->handler_arg(hdr, proc->arg, end);
- return -EINVAL;
-}
-
-/**
- * acpi_parse_entries_array - for each proc_num find a suitable subtable
- *
- * @id: table id (for debugging purposes)
- * @table_size: size of the root table
- * @table_header: where does the table start?
- * @proc: array of acpi_subtable_proc struct containing entry id
- * and associated handler with it
- * @proc_num: how big proc is?
- * @max_entries: how many entries can we process?
- *
- * For each proc_num find a subtable with proc->id and run proc->handler
- * on it. Assumption is that there's only single handler for particular
- * entry id.
- *
- * The table_size is not the size of the complete ACPI table (the length
- * field in the header struct), but only the size of the root table; i.e.,
- * the offset from the very first byte of the complete ACPI table, to the
- * first byte of the very first subtable.
- *
- * On success returns sum of all matching entries for all proc handlers.
- * Otherwise, -ENODEV or -EINVAL is returned.
- */
-static int __init_or_acpilib acpi_parse_entries_array(
- char *id, unsigned long table_size,
- struct acpi_table_header *table_header, struct acpi_subtable_proc *proc,
- int proc_num, unsigned int max_entries)
-{
- struct acpi_subtable_entry entry;
- unsigned long table_end, subtable_len, entry_len;
- int count = 0;
- int errs = 0;
- int i;
-
- table_end = (unsigned long)table_header + table_header->length;
+ case ACPI_MADT_TYPE_MULTIPROC_WAKEUP:
+ {
+ struct acpi_madt_multiproc_wakeup *p =
+ (struct acpi_madt_multiproc_wakeup *)header;
+ u64 reset_vector = 0;
- /* Parse all entries looking for a match. */
+ if (p->version >= ACPI_MADT_MP_WAKEUP_VERSION_V1)
+ reset_vector = p->reset_vector;
- entry.type = acpi_get_subtable_type(id);
- entry.hdr = (union acpi_subtable_headers *)
- ((unsigned long)table_header + table_size);
- subtable_len = acpi_get_subtable_header_length(&entry);
+ pr_debug("MP Wakeup (version[%d], mailbox[%#llx], reset[%#llx])\n",
+ p->version, p->mailbox_address, reset_vector);
+ }
+ break;
- while (((unsigned long)entry.hdr) + subtable_len < table_end) {
- if (max_entries && count >= max_entries)
- break;
+ case ACPI_MADT_TYPE_CORE_PIC:
+ {
+ struct acpi_madt_core_pic *p = (struct acpi_madt_core_pic *)header;
- for (i = 0; i < proc_num; i++) {
- if (acpi_get_entry_type(&entry) != proc[i].id)
- continue;
- if (!has_handler(&proc[i]) ||
- (!errs &&
- call_handler(&proc[i], entry.hdr, table_end))) {
- errs++;
- continue;
- }
-
- proc[i].count++;
- break;
+ pr_debug("CORE PIC (processor_id[0x%02x] core_id[0x%02x] %s)\n",
+ p->processor_id, p->core_id,
+ str_enabled_disabled(p->flags & ACPI_MADT_ENABLED));
}
- if (i != proc_num)
- count++;
+ break;
- /*
- * If entry->length is 0, break from this loop to avoid
- * infinite loop.
- */
- entry_len = acpi_get_entry_length(&entry);
- if (entry_len == 0) {
- pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id);
- return -EINVAL;
- }
+ case ACPI_MADT_TYPE_RINTC:
+ {
+ struct acpi_madt_rintc *p = (struct acpi_madt_rintc *)header;
- entry.hdr = (union acpi_subtable_headers *)
- ((unsigned long)entry.hdr + entry_len);
- }
+ pr_debug("RISC-V INTC (acpi_uid[0x%04x] hart_id[0x%llx] %s)\n",
+ p->uid, p->hart_id,
+ str_enabled_disabled(p->flags & ACPI_MADT_ENABLED));
+ }
+ break;
- if (max_entries && count > max_entries) {
- pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
- id, proc->id, count);
+ default:
+ pr_warn("Found unsupported MADT entry (type = 0x%x)\n",
+ header->type);
+ break;
}
-
- return errs ? -EINVAL : count;
}
int __init_or_acpilib acpi_table_parse_entries_array(
@@ -404,8 +265,9 @@ int __init_or_acpilib acpi_table_parse_entries_array(
return -ENODEV;
}
- count = acpi_parse_entries_array(id, table_size, table_header,
- proc, proc_num, max_entries);
+ count = acpi_parse_entries_array(id, table_size,
+ (union fw_table_header *)table_header,
+ 0, proc, proc_num, max_entries);
acpi_put_table(table_header);
return count;
@@ -534,7 +396,7 @@ static u8 __init acpi_table_checksum(u8 *buffer, u32 length)
}
/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
-static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = {
+static const char table_sigs[][ACPI_NAMESEG_SIZE] __nonstring_array __initconst = {
ACPI_SIG_BERT, ACPI_SIG_BGRT, ACPI_SIG_CPEP, ACPI_SIG_ECDT,
ACPI_SIG_EINJ, ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT,
ACPI_SIG_MSCT, ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT,
@@ -545,7 +407,8 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = {
ACPI_SIG_WDDT, ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT,
ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT,
ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT,
- ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT, ACPI_SIG_AGDI };
+ ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT, ACPI_SIG_AGDI,
+ ACPI_SIG_NBFT, ACPI_SIG_SWFT, ACPI_SIG_MPAM};
#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
@@ -838,12 +701,11 @@ acpi_status acpi_os_table_override(struct acpi_table_header *existing_table,
/*
* acpi_locate_initial_tables()
*
- * find RSDP, find and checksum SDT/XSDT.
- * checksum all tables, print SDT/XSDT
+ * Get the RSDP, then find and checksum all the ACPI tables.
*
- * result: sdt_entry[] is initialized
+ * result: initial_tables[] is initialized, and points to
+ * a list of ACPI tables.
*/
-
int __init acpi_locate_initial_tables(void)
{
acpi_status status;
@@ -857,8 +719,12 @@ int __init acpi_locate_initial_tables(void)
}
status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0);
- if (ACPI_FAILURE(status))
+ if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+
+ pr_warn("Failed to initialize tables, status=0x%x (%s)", status, msg);
return -EINVAL;
+ }
return 0;
}
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 539660ef93c7..a511f9ea0267 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -31,6 +31,8 @@
#include <linux/uaccess.h>
#include <linux/units.h>
+#include "internal.h"
+
#define ACPI_THERMAL_CLASS "thermal_zone"
#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
@@ -40,12 +42,26 @@
#define ACPI_THERMAL_NOTIFY_HOT 0xF1
#define ACPI_THERMAL_MODE_ACTIVE 0x00
-#define ACPI_THERMAL_MAX_ACTIVE 10
-#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
+#define ACPI_THERMAL_MAX_ACTIVE 10
+#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
-MODULE_AUTHOR("Paul Diefenbaugh");
-MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
-MODULE_LICENSE("GPL");
+#define ACPI_THERMAL_TRIP_PASSIVE (-1)
+
+#define ACPI_THERMAL_MAX_NR_TRIPS (ACPI_THERMAL_MAX_ACTIVE + 3)
+
+/*
+ * This exception is thrown out in two cases:
+ * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
+ * when re-evaluating the AML code.
+ * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
+ * We need to re-bind the cooling devices of a thermal zone when this occurs.
+ */
+#define ACPI_THERMAL_TRIPS_EXCEPTION(tz, str) \
+do { \
+ acpi_handle_info(tz->device->handle, \
+ "ACPI thermal trip point %s changed\n" \
+ "Please report to linux-acpi@vger.kernel.org\n", str); \
+} while (0)
static int act;
module_param(act, int, 0644);
@@ -59,10 +75,6 @@ static int tzp;
module_param(tzp, int, 0444);
MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
-static int nocrt;
-module_param(nocrt, int, 0);
-MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points.");
-
static int off;
module_param(off, int, 0);
MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.");
@@ -73,101 +85,35 @@ MODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
static struct workqueue_struct *acpi_thermal_pm_queue;
-static int acpi_thermal_add(struct acpi_device *device);
-static int acpi_thermal_remove(struct acpi_device *device);
-static void acpi_thermal_notify(struct acpi_device *device, u32 event);
-
-static const struct acpi_device_id thermal_device_ids[] = {
- {ACPI_THERMAL_HID, 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
-
-#ifdef CONFIG_PM_SLEEP
-static int acpi_thermal_suspend(struct device *dev);
-static int acpi_thermal_resume(struct device *dev);
-#else
-#define acpi_thermal_suspend NULL
-#define acpi_thermal_resume NULL
-#endif
-static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
-
-static struct acpi_driver acpi_thermal_driver = {
- .name = "thermal",
- .class = ACPI_THERMAL_CLASS,
- .ids = thermal_device_ids,
- .ops = {
- .add = acpi_thermal_add,
- .remove = acpi_thermal_remove,
- .notify = acpi_thermal_notify,
- },
- .drv.pm = &acpi_thermal_pm,
-};
-
-struct acpi_thermal_state {
- u8 critical:1;
- u8 hot:1;
- u8 passive:1;
- u8 active:1;
- u8 reserved:4;
- int active_index;
-};
-
-struct acpi_thermal_state_flags {
- u8 valid:1;
- u8 enabled:1;
- u8 reserved:6;
-};
-
-struct acpi_thermal_critical {
- struct acpi_thermal_state_flags flags;
- unsigned long temperature;
-};
-
-struct acpi_thermal_hot {
- struct acpi_thermal_state_flags flags;
- unsigned long temperature;
+struct acpi_thermal_trip {
+ unsigned long temp_dk;
+ struct acpi_handle_list devices;
};
struct acpi_thermal_passive {
- struct acpi_thermal_state_flags flags;
- unsigned long temperature;
+ struct acpi_thermal_trip trip;
unsigned long tc1;
unsigned long tc2;
- unsigned long tsp;
- struct acpi_handle_list devices;
+ unsigned long delay;
};
struct acpi_thermal_active {
- struct acpi_thermal_state_flags flags;
- unsigned long temperature;
- struct acpi_handle_list devices;
+ struct acpi_thermal_trip trip;
};
struct acpi_thermal_trips {
- struct acpi_thermal_critical critical;
- struct acpi_thermal_hot hot;
struct acpi_thermal_passive passive;
struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
};
-struct acpi_thermal_flags {
- u8 cooling_mode:1; /* _SCP */
- u8 devices:1; /* _TZD */
- u8 reserved:6;
-};
-
struct acpi_thermal {
- struct acpi_device * device;
+ struct acpi_device *device;
acpi_bus_id name;
- unsigned long temperature;
- unsigned long last_temperature;
+ unsigned long temp_dk;
+ unsigned long last_temp_dk;
unsigned long polling_frequency;
volatile u8 zombie;
- struct acpi_thermal_flags flags;
- struct acpi_thermal_state state;
struct acpi_thermal_trips trips;
- struct acpi_handle_list devices;
struct thermal_zone_device *thermal_zone;
int kelvin_offset; /* in millidegrees */
struct work_struct thermal_check_work;
@@ -187,16 +133,16 @@ static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
if (!tz)
return -EINVAL;
- tz->last_temperature = tz->temperature;
+ tz->last_temp_dk = tz->temp_dk;
status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status))
return -ENODEV;
- tz->temperature = tmp;
+ tz->temp_dk = tmp;
acpi_handle_debug(tz->device->handle, "Temperature is %lu dK\n",
- tz->temperature);
+ tz->temp_dk);
return 0;
}
@@ -220,459 +166,381 @@ static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
return 0;
}
-static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
+static int acpi_thermal_temp(struct acpi_thermal *tz, int temp_deci_k)
{
- if (!tz)
- return -EINVAL;
+ int temp;
- if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle,
- "_SCP", mode)))
- return -ENODEV;
+ if (temp_deci_k == THERMAL_TEMP_INVALID)
+ return THERMAL_TEMP_INVALID;
- return 0;
+ temp = deci_kelvin_to_millicelsius_with_offset(temp_deci_k,
+ tz->kelvin_offset);
+ if (temp <= 0)
+ return THERMAL_TEMP_INVALID;
+
+ return temp;
}
-#define ACPI_TRIPS_CRITICAL 0x01
-#define ACPI_TRIPS_HOT 0x02
-#define ACPI_TRIPS_PASSIVE 0x04
-#define ACPI_TRIPS_ACTIVE 0x08
-#define ACPI_TRIPS_DEVICES 0x10
+static bool acpi_thermal_trip_valid(struct acpi_thermal_trip *acpi_trip)
+{
+ return acpi_trip->temp_dk != THERMAL_TEMP_INVALID;
+}
-#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
-#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
+static int active_trip_index(struct acpi_thermal *tz,
+ struct acpi_thermal_trip *acpi_trip)
+{
+ struct acpi_thermal_active *active;
-#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
- ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
- ACPI_TRIPS_DEVICES)
+ active = container_of(acpi_trip, struct acpi_thermal_active, trip);
+ return active - tz->trips.active;
+}
-/*
- * This exception is thrown out in two cases:
- * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
- * when re-evaluating the AML code.
- * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
- * We need to re-bind the cooling devices of a thermal zone when this occurs.
- */
-#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, tz, str) \
-do { \
- if (flags != ACPI_TRIPS_INIT) \
- acpi_handle_info(tz->device->handle, \
- "ACPI thermal trip point %s changed\n" \
- "Please report to linux-acpi@vger.kernel.org\n", str); \
-} while (0)
+static long get_passive_temp(struct acpi_thermal *tz)
+{
+ int temp;
+
+ if (acpi_passive_trip_temp(tz->device, &temp))
+ return THERMAL_TEMP_INVALID;
-static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
+ return temp;
+}
+
+static long get_active_temp(struct acpi_thermal *tz, int index)
{
- acpi_status status = AE_OK;
- unsigned long long tmp;
- struct acpi_handle_list devices;
- int valid = 0;
- int i;
+ int temp;
- /* Critical Shutdown */
- if (flag & ACPI_TRIPS_CRITICAL) {
- status = acpi_evaluate_integer(tz->device->handle,
- "_CRT", NULL, &tmp);
- tz->trips.critical.temperature = tmp;
- /*
- * Treat freezing temperatures as invalid as well; some
- * BIOSes return really low values and cause reboots at startup.
- * Below zero (Celsius) values clearly aren't right for sure..
- * ... so lets discard those as invalid.
- */
- if (ACPI_FAILURE(status)) {
- tz->trips.critical.flags.valid = 0;
- acpi_handle_debug(tz->device->handle,
- "No critical threshold\n");
- } else if (tmp <= 2732) {
- pr_info(FW_BUG "Invalid critical threshold (%llu)\n",
- tmp);
- tz->trips.critical.flags.valid = 0;
- } else {
- tz->trips.critical.flags.valid = 1;
- acpi_handle_debug(tz->device->handle,
- "Found critical threshold [%lu]\n",
- tz->trips.critical.temperature);
- }
- if (tz->trips.critical.flags.valid == 1) {
- if (crt == -1) {
- tz->trips.critical.flags.valid = 0;
- } else if (crt > 0) {
- unsigned long crt_k = celsius_to_deci_kelvin(crt);
-
- /*
- * Allow override critical threshold
- */
- if (crt_k > tz->trips.critical.temperature)
- pr_info("Critical threshold %d C\n", crt);
-
- tz->trips.critical.temperature = crt_k;
- }
- }
- }
+ if (acpi_active_trip_temp(tz->device, index, &temp))
+ return THERMAL_TEMP_INVALID;
- /* Critical Sleep (optional) */
- if (flag & ACPI_TRIPS_HOT) {
- status = acpi_evaluate_integer(tz->device->handle,
- "_HOT", NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- tz->trips.hot.flags.valid = 0;
- acpi_handle_debug(tz->device->handle,
- "No hot threshold\n");
- } else {
- tz->trips.hot.temperature = tmp;
- tz->trips.hot.flags.valid = 1;
- acpi_handle_debug(tz->device->handle,
- "Found hot threshold [%lu]\n",
- tz->trips.hot.temperature);
- }
- }
+ /*
+ * If an override has been provided, apply it so there are no active
+ * trips with thresholds greater than the override.
+ */
+ if (act > 0) {
+ unsigned long long override = celsius_to_deci_kelvin(act);
- /* Passive (optional) */
- if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) ||
- (flag == ACPI_TRIPS_INIT)) {
- valid = tz->trips.passive.flags.valid;
- if (psv == -1) {
- status = AE_SUPPORT;
- } else if (psv > 0) {
- tmp = celsius_to_deci_kelvin(psv);
- status = AE_OK;
- } else {
- status = acpi_evaluate_integer(tz->device->handle,
- "_PSV", NULL, &tmp);
- }
-
- if (ACPI_FAILURE(status))
- tz->trips.passive.flags.valid = 0;
- else {
- tz->trips.passive.temperature = tmp;
- tz->trips.passive.flags.valid = 1;
- if (flag == ACPI_TRIPS_INIT) {
- status = acpi_evaluate_integer(
- tz->device->handle, "_TC1",
- NULL, &tmp);
- if (ACPI_FAILURE(status))
- tz->trips.passive.flags.valid = 0;
- else
- tz->trips.passive.tc1 = tmp;
- status = acpi_evaluate_integer(
- tz->device->handle, "_TC2",
- NULL, &tmp);
- if (ACPI_FAILURE(status))
- tz->trips.passive.flags.valid = 0;
- else
- tz->trips.passive.tc2 = tmp;
- status = acpi_evaluate_integer(
- tz->device->handle, "_TSP",
- NULL, &tmp);
- if (ACPI_FAILURE(status))
- tz->trips.passive.flags.valid = 0;
- else
- tz->trips.passive.tsp = tmp;
- }
- }
- }
- if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
- memset(&devices, 0, sizeof(struct acpi_handle_list));
- status = acpi_evaluate_reference(tz->device->handle, "_PSL",
- NULL, &devices);
- if (ACPI_FAILURE(status)) {
- acpi_handle_info(tz->device->handle,
- "Invalid passive threshold\n");
- tz->trips.passive.flags.valid = 0;
- }
- else
- tz->trips.passive.flags.valid = 1;
-
- if (memcmp(&tz->trips.passive.devices, &devices,
- sizeof(struct acpi_handle_list))) {
- memcpy(&tz->trips.passive.devices, &devices,
- sizeof(struct acpi_handle_list));
- ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device");
- }
- }
- if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
- if (valid != tz->trips.passive.flags.valid)
- ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state");
+ if (temp > override)
+ return override;
}
+ return temp;
+}
- /* Active (optional) */
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
- char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
- valid = tz->trips.active[i].flags.valid;
+static void acpi_thermal_update_trip(struct acpi_thermal *tz,
+ const struct thermal_trip *trip)
+{
+ struct acpi_thermal_trip *acpi_trip = trip->priv;
- if (act == -1)
- break; /* disable all active trip points */
-
- if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) &&
- tz->trips.active[i].flags.valid)) {
- status = acpi_evaluate_integer(tz->device->handle,
- name, NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- tz->trips.active[i].flags.valid = 0;
- if (i == 0)
- break;
- if (act <= 0)
- break;
- if (i == 1)
- tz->trips.active[0].temperature =
- celsius_to_deci_kelvin(act);
- else
- /*
- * Don't allow override higher than
- * the next higher trip point
- */
- tz->trips.active[i - 1].temperature =
- (tz->trips.active[i - 2].temperature <
- celsius_to_deci_kelvin(act) ?
- tz->trips.active[i - 2].temperature :
- celsius_to_deci_kelvin(act));
- break;
- } else {
- tz->trips.active[i].temperature = tmp;
- tz->trips.active[i].flags.valid = 1;
- }
- }
-
- name[2] = 'L';
- if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
- memset(&devices, 0, sizeof(struct acpi_handle_list));
- status = acpi_evaluate_reference(tz->device->handle,
- name, NULL, &devices);
- if (ACPI_FAILURE(status)) {
- acpi_handle_info(tz->device->handle,
- "Invalid active%d threshold\n", i);
- tz->trips.active[i].flags.valid = 0;
- }
- else
- tz->trips.active[i].flags.valid = 1;
-
- if (memcmp(&tz->trips.active[i].devices, &devices,
- sizeof(struct acpi_handle_list))) {
- memcpy(&tz->trips.active[i].devices, &devices,
- sizeof(struct acpi_handle_list));
- ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device");
- }
- }
- if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
- if (valid != tz->trips.active[i].flags.valid)
- ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state");
-
- if (!tz->trips.active[i].flags.valid)
- break;
- }
+ if (trip->type == THERMAL_TRIP_PASSIVE) {
+ if (psv > 0)
+ return;
+
+ acpi_trip->temp_dk = get_passive_temp(tz);
+ } else {
+ int index = active_trip_index(tz, acpi_trip);
- if (flag & ACPI_TRIPS_DEVICES) {
- memset(&devices, 0, sizeof(devices));
- status = acpi_evaluate_reference(tz->device->handle, "_TZD",
- NULL, &devices);
- if (ACPI_SUCCESS(status)
- && memcmp(&tz->devices, &devices, sizeof(devices))) {
- tz->devices = devices;
- ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device");
- }
+ acpi_trip->temp_dk = get_active_temp(tz, index);
}
- return 0;
+ if (!acpi_thermal_trip_valid(acpi_trip))
+ ACPI_THERMAL_TRIPS_EXCEPTION(tz, "state");
}
-static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
+static bool update_trip_devices(struct acpi_thermal *tz,
+ struct acpi_thermal_trip *acpi_trip,
+ int index, bool compare)
{
- int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
+ struct acpi_handle_list devices = { 0 };
+ char method[] = "_PSL";
- if (ret)
- return ret;
-
- valid = tz->trips.critical.flags.valid |
- tz->trips.hot.flags.valid |
- tz->trips.passive.flags.valid;
+ if (index != ACPI_THERMAL_TRIP_PASSIVE) {
+ method[1] = 'A';
+ method[2] = 'L';
+ method[3] = '0' + index;
+ }
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
- valid |= tz->trips.active[i].flags.valid;
+ if (!acpi_evaluate_reference(tz->device->handle, method, NULL, &devices)) {
+ acpi_handle_info(tz->device->handle, "%s evaluation failure\n", method);
+ return false;
+ }
- if (!valid) {
- pr_warn(FW_BUG "No valid trip found\n");
- return -ENODEV;
+ if (acpi_handle_list_equal(&acpi_trip->devices, &devices)) {
+ acpi_handle_list_free(&devices);
+ return true;
}
- return 0;
+
+ if (compare)
+ ACPI_THERMAL_TRIPS_EXCEPTION(tz, "device");
+
+ acpi_handle_list_replace(&acpi_trip->devices, &devices);
+ return true;
}
-/* sys I/F for generic thermal sysfs support */
+static void acpi_thermal_update_trip_devices(struct acpi_thermal *tz,
+ const struct thermal_trip *trip)
+{
+ struct acpi_thermal_trip *acpi_trip = trip->priv;
+ int index = trip->type == THERMAL_TRIP_PASSIVE ?
+ ACPI_THERMAL_TRIP_PASSIVE : active_trip_index(tz, acpi_trip);
-static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
+ if (update_trip_devices(tz, acpi_trip, index, true))
+ return;
+
+ acpi_trip->temp_dk = THERMAL_TEMP_INVALID;
+ ACPI_THERMAL_TRIPS_EXCEPTION(tz, "state");
+}
+
+struct adjust_trip_data {
+ struct acpi_thermal *tz;
+ u32 event;
+};
+
+static int acpi_thermal_adjust_trip(struct thermal_trip *trip, void *data)
{
- struct acpi_thermal *tz = thermal->devdata;
- int result;
+ struct acpi_thermal_trip *acpi_trip = trip->priv;
+ struct adjust_trip_data *atd = data;
+ struct acpi_thermal *tz = atd->tz;
+ int temp;
- if (!tz)
- return -EINVAL;
+ if (!acpi_trip || !acpi_thermal_trip_valid(acpi_trip))
+ return 0;
- result = acpi_thermal_get_temperature(tz);
- if (result)
- return result;
+ if (atd->event == ACPI_THERMAL_NOTIFY_THRESHOLDS)
+ acpi_thermal_update_trip(tz, trip);
+ else
+ acpi_thermal_update_trip_devices(tz, trip);
+
+ if (acpi_thermal_trip_valid(acpi_trip))
+ temp = acpi_thermal_temp(tz, acpi_trip->temp_dk);
+ else
+ temp = THERMAL_TEMP_INVALID;
+
+ thermal_zone_set_trip_temp(tz->thermal_zone, trip, temp);
- *temp = deci_kelvin_to_millicelsius_with_offset(tz->temperature,
- tz->kelvin_offset);
return 0;
}
-static int thermal_get_trip_type(struct thermal_zone_device *thermal,
- int trip, enum thermal_trip_type *type)
+static void acpi_queue_thermal_check(struct acpi_thermal *tz)
{
- struct acpi_thermal *tz = thermal->devdata;
- int i;
+ if (!work_pending(&tz->thermal_check_work))
+ queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
+}
- if (!tz || trip < 0)
- return -EINVAL;
+static void acpi_thermal_trips_update(struct acpi_thermal *tz, u32 event)
+{
+ struct adjust_trip_data atd = { .tz = tz, .event = event };
+ struct acpi_device *adev = tz->device;
- if (tz->trips.critical.flags.valid) {
- if (!trip) {
- *type = THERMAL_TRIP_CRITICAL;
- return 0;
- }
- trip--;
- }
+ /*
+ * Use thermal_zone_for_each_trip() to carry out the trip points
+ * update, so as to protect thermal_get_trend() from getting stale
+ * trip point temperatures and to prevent thermal_zone_device_update()
+ * invoked from acpi_thermal_check_fn() from producing inconsistent
+ * results.
+ */
+ thermal_zone_for_each_trip(tz->thermal_zone,
+ acpi_thermal_adjust_trip, &atd);
+ acpi_queue_thermal_check(tz);
+ acpi_bus_generate_netlink_event(adev->pnp.device_class,
+ dev_name(&adev->dev), event, 0);
+}
- if (tz->trips.hot.flags.valid) {
- if (!trip) {
- *type = THERMAL_TRIP_HOT;
- return 0;
- }
- trip--;
+static int acpi_thermal_get_critical_trip(struct acpi_thermal *tz)
+{
+ int temp;
+
+ if (crt > 0) {
+ temp = celsius_to_deci_kelvin(crt);
+ goto set;
+ }
+ if (crt == -1) {
+ acpi_handle_debug(tz->device->handle, "Critical threshold disabled\n");
+ return THERMAL_TEMP_INVALID;
}
- if (tz->trips.passive.flags.valid) {
- if (!trip) {
- *type = THERMAL_TRIP_PASSIVE;
- return 0;
- }
- trip--;
+ if (acpi_critical_trip_temp(tz->device, &temp))
+ return THERMAL_TEMP_INVALID;
+
+ if (temp <= 2732) {
+ /*
+ * Below zero (Celsius) values clearly aren't right for sure,
+ * so discard them as invalid.
+ */
+ pr_info(FW_BUG "Invalid critical threshold (%d)\n", temp);
+ return THERMAL_TEMP_INVALID;
}
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
- tz->trips.active[i].flags.valid; i++) {
- if (!trip) {
- *type = THERMAL_TRIP_ACTIVE;
- return 0;
- }
- trip--;
+set:
+ acpi_handle_debug(tz->device->handle, "Critical threshold [%d]\n", temp);
+ return temp;
+}
+
+static int acpi_thermal_get_hot_trip(struct acpi_thermal *tz)
+{
+ int temp;
+
+ if (acpi_hot_trip_temp(tz->device, &temp) || temp == THERMAL_TEMP_INVALID) {
+ acpi_handle_debug(tz->device->handle, "No hot threshold\n");
+ return THERMAL_TEMP_INVALID;
}
- return -EINVAL;
+ acpi_handle_debug(tz->device->handle, "Hot threshold [%d]\n", temp);
+ return temp;
}
-static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
- int trip, int *temp)
+static bool passive_trip_params_init(struct acpi_thermal *tz)
{
- struct acpi_thermal *tz = thermal->devdata;
- int i;
+ unsigned long long tmp;
+ acpi_status status;
- if (!tz || trip < 0)
- return -EINVAL;
+ status = acpi_evaluate_integer(tz->device->handle, "_TC1", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return false;
- if (tz->trips.critical.flags.valid) {
- if (!trip) {
- *temp = deci_kelvin_to_millicelsius_with_offset(
- tz->trips.critical.temperature,
- tz->kelvin_offset);
- return 0;
- }
- trip--;
- }
+ tz->trips.passive.tc1 = tmp;
- if (tz->trips.hot.flags.valid) {
- if (!trip) {
- *temp = deci_kelvin_to_millicelsius_with_offset(
- tz->trips.hot.temperature,
- tz->kelvin_offset);
- return 0;
- }
- trip--;
+ status = acpi_evaluate_integer(tz->device->handle, "_TC2", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ tz->trips.passive.tc2 = tmp;
+
+ status = acpi_evaluate_integer(tz->device->handle, "_TFP", NULL, &tmp);
+ if (ACPI_SUCCESS(status)) {
+ tz->trips.passive.delay = tmp;
+ return true;
}
- if (tz->trips.passive.flags.valid) {
- if (!trip) {
- *temp = deci_kelvin_to_millicelsius_with_offset(
- tz->trips.passive.temperature,
- tz->kelvin_offset);
- return 0;
- }
- trip--;
+ status = acpi_evaluate_integer(tz->device->handle, "_TSP", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ tz->trips.passive.delay = tmp * 100;
+
+ return true;
+}
+
+static bool acpi_thermal_init_trip(struct acpi_thermal *tz, int index)
+{
+ struct acpi_thermal_trip *acpi_trip;
+ long temp;
+
+ if (index == ACPI_THERMAL_TRIP_PASSIVE) {
+ acpi_trip = &tz->trips.passive.trip;
+
+ if (psv == -1)
+ goto fail;
+
+ if (!passive_trip_params_init(tz))
+ goto fail;
+
+ temp = psv > 0 ? celsius_to_deci_kelvin(psv) :
+ get_passive_temp(tz);
+ } else {
+ acpi_trip = &tz->trips.active[index].trip;
+
+ if (act == -1)
+ goto fail;
+
+ temp = get_active_temp(tz, index);
}
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
- tz->trips.active[i].flags.valid; i++) {
- if (!trip) {
- *temp = deci_kelvin_to_millicelsius_with_offset(
- tz->trips.active[i].temperature,
- tz->kelvin_offset);
- return 0;
- }
- trip--;
+ if (temp == THERMAL_TEMP_INVALID)
+ goto fail;
+
+ if (!update_trip_devices(tz, acpi_trip, index, false))
+ goto fail;
+
+ acpi_trip->temp_dk = temp;
+ return true;
+
+fail:
+ acpi_trip->temp_dk = THERMAL_TEMP_INVALID;
+ return false;
+}
+
+static void acpi_thermal_get_trip_points(struct acpi_thermal *tz)
+{
+ int i;
+
+ acpi_thermal_init_trip(tz, ACPI_THERMAL_TRIP_PASSIVE);
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+ if (!acpi_thermal_init_trip(tz, i))
+ break;
}
- return -EINVAL;
+ while (++i < ACPI_THERMAL_MAX_ACTIVE)
+ tz->trips.active[i].trip.temp_dk = THERMAL_TEMP_INVALID;
}
-static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
- int *temperature)
+/* sys I/F for generic thermal sysfs support */
+
+static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
{
- struct acpi_thermal *tz = thermal->devdata;
+ struct acpi_thermal *tz = thermal_zone_device_priv(thermal);
+ int result;
- if (tz->trips.critical.flags.valid) {
- *temperature = deci_kelvin_to_millicelsius_with_offset(
- tz->trips.critical.temperature,
- tz->kelvin_offset);
- return 0;
- } else
+ if (!tz)
return -EINVAL;
+
+ result = acpi_thermal_get_temperature(tz);
+ if (result)
+ return result;
+
+ *temp = deci_kelvin_to_millicelsius_with_offset(tz->temp_dk,
+ tz->kelvin_offset);
+ return 0;
}
static int thermal_get_trend(struct thermal_zone_device *thermal,
- int trip, enum thermal_trend *trend)
+ const struct thermal_trip *trip,
+ enum thermal_trend *trend)
{
- struct acpi_thermal *tz = thermal->devdata;
- enum thermal_trip_type type;
- int i;
+ struct acpi_thermal *tz = thermal_zone_device_priv(thermal);
+ struct acpi_thermal_trip *acpi_trip;
+ int t;
- if (thermal_get_trip_type(thermal, trip, &type))
+ if (!tz || !trip)
return -EINVAL;
- if (type == THERMAL_TRIP_ACTIVE) {
- int trip_temp;
- int temp = deci_kelvin_to_millicelsius_with_offset(
- tz->temperature, tz->kelvin_offset);
- if (thermal_get_trip_temp(thermal, trip, &trip_temp))
- return -EINVAL;
+ acpi_trip = trip->priv;
+ if (!acpi_trip || !acpi_thermal_trip_valid(acpi_trip))
+ return -EINVAL;
- if (temp > trip_temp) {
+ switch (trip->type) {
+ case THERMAL_TRIP_PASSIVE:
+ t = tz->trips.passive.tc1 * (tz->temp_dk -
+ tz->last_temp_dk) +
+ tz->trips.passive.tc2 * (tz->temp_dk -
+ acpi_trip->temp_dk);
+ if (t > 0)
*trend = THERMAL_TREND_RAISING;
- return 0;
- } else {
- /* Fall back on default trend */
- return -EINVAL;
- }
- }
+ else if (t < 0)
+ *trend = THERMAL_TREND_DROPPING;
+ else
+ *trend = THERMAL_TREND_STABLE;
- /*
- * tz->temperature has already been updated by generic thermal layer,
- * before this callback being invoked
- */
- i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature))
- + (tz->trips.passive.tc2
- * (tz->temperature - tz->trips.passive.temperature));
+ return 0;
+
+ case THERMAL_TRIP_ACTIVE:
+ t = acpi_thermal_temp(tz, tz->temp_dk);
+ if (t <= trip->temperature)
+ break;
- if (i > 0)
*trend = THERMAL_TREND_RAISING;
- else if (i < 0)
- *trend = THERMAL_TREND_DROPPING;
- else
- *trend = THERMAL_TREND_STABLE;
- return 0;
+
+ return 0;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
}
static void acpi_thermal_zone_device_hot(struct thermal_zone_device *thermal)
{
- struct acpi_thermal *tz = thermal->devdata;
+ struct acpi_thermal *tz = thermal_zone_device_priv(thermal);
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
dev_name(&tz->device->dev),
@@ -681,7 +549,7 @@ static void acpi_thermal_zone_device_hot(struct thermal_zone_device *thermal)
static void acpi_thermal_zone_device_critical(struct thermal_zone_device *thermal)
{
- struct acpi_thermal *tz = thermal->devdata;
+ struct acpi_thermal *tz = thermal_zone_device_priv(thermal);
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
dev_name(&tz->device->dev),
@@ -690,167 +558,97 @@ static void acpi_thermal_zone_device_critical(struct thermal_zone_device *therma
thermal_zone_device_critical(thermal);
}
-static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
- struct thermal_cooling_device *cdev,
- bool bind)
+static bool acpi_thermal_should_bind_cdev(struct thermal_zone_device *thermal,
+ const struct thermal_trip *trip,
+ struct thermal_cooling_device *cdev,
+ struct cooling_spec *c)
{
- struct acpi_device *device = cdev->devdata;
- struct acpi_thermal *tz = thermal->devdata;
- struct acpi_device *dev;
- acpi_handle handle;
+ struct acpi_thermal_trip *acpi_trip = trip->priv;
+ struct acpi_device *cdev_adev = cdev->devdata;
int i;
- int j;
- int trip = -1;
- int result = 0;
-
- if (tz->trips.critical.flags.valid)
- trip++;
- if (tz->trips.hot.flags.valid)
- trip++;
+ /* Skip critical and hot trips. */
+ if (!acpi_trip)
+ return false;
- if (tz->trips.passive.flags.valid) {
- trip++;
- for (i = 0; i < tz->trips.passive.devices.count;
- i++) {
- handle = tz->trips.passive.devices.handles[i];
- dev = acpi_fetch_acpi_dev(handle);
- if (dev != device)
- continue;
- if (bind)
- result =
- thermal_zone_bind_cooling_device
- (thermal, trip, cdev,
- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
- THERMAL_WEIGHT_DEFAULT);
- else
- result =
- thermal_zone_unbind_cooling_device
- (thermal, trip, cdev);
- if (result)
- goto failed;
- }
- }
+ for (i = 0; i < acpi_trip->devices.count; i++) {
+ acpi_handle handle = acpi_trip->devices.handles[i];
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
- if (!tz->trips.active[i].flags.valid)
- break;
- trip++;
- for (j = 0;
- j < tz->trips.active[i].devices.count;
- j++) {
- handle = tz->trips.active[i].devices.handles[j];
- dev = acpi_fetch_acpi_dev(handle);
- if (dev != device)
- continue;
- if (bind)
- result = thermal_zone_bind_cooling_device
- (thermal, trip, cdev,
- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
- THERMAL_WEIGHT_DEFAULT);
- else
- result = thermal_zone_unbind_cooling_device
- (thermal, trip, cdev);
- if (result)
- goto failed;
- }
+ if (acpi_fetch_acpi_dev(handle) == cdev_adev)
+ return true;
}
-failed:
- return result;
+ return false;
}
-static int
-acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
- struct thermal_cooling_device *cdev)
-{
- return acpi_thermal_cooling_device_cb(thermal, cdev, true);
-}
-
-static int
-acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
- struct thermal_cooling_device *cdev)
-{
- return acpi_thermal_cooling_device_cb(thermal, cdev, false);
-}
-
-static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
- .bind = acpi_thermal_bind_cooling_device,
- .unbind = acpi_thermal_unbind_cooling_device,
+static const struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+ .should_bind = acpi_thermal_should_bind_cdev,
.get_temp = thermal_get_temp,
- .get_trip_type = thermal_get_trip_type,
- .get_trip_temp = thermal_get_trip_temp,
- .get_crit_temp = thermal_get_crit_temp,
.get_trend = thermal_get_trend,
.hot = acpi_thermal_zone_device_hot,
.critical = acpi_thermal_zone_device_critical,
};
-static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
+static int acpi_thermal_zone_sysfs_add(struct acpi_thermal *tz)
{
- int trips = 0;
- int result;
- acpi_status status;
- int i;
+ struct device *tzdev = thermal_zone_device(tz->thermal_zone);
+ int ret;
+
+ ret = sysfs_create_link(&tz->device->dev.kobj,
+ &tzdev->kobj, "thermal_zone");
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_link(&tzdev->kobj,
+ &tz->device->dev.kobj, "device");
+ if (ret)
+ sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
- if (tz->trips.critical.flags.valid)
- trips++;
+ return ret;
+}
- if (tz->trips.hot.flags.valid)
- trips++;
+static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz)
+{
+ struct device *tzdev = thermal_zone_device(tz->thermal_zone);
- if (tz->trips.passive.flags.valid)
- trips++;
+ sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
+ sysfs_remove_link(&tzdev->kobj, "device");
+}
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
- tz->trips.active[i].flags.valid; i++, trips++);
+static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz,
+ const struct thermal_trip *trip_table,
+ unsigned int trip_count,
+ int passive_delay)
+{
+ int result;
- if (tz->trips.passive.flags.valid)
- tz->thermal_zone =
- thermal_zone_device_register("acpitz", trips, 0, tz,
- &acpi_thermal_zone_ops, NULL,
- tz->trips.passive.tsp*100,
- tz->polling_frequency*100);
+ if (trip_count)
+ tz->thermal_zone = thermal_zone_device_register_with_trips(
+ "acpitz", trip_table, trip_count, tz,
+ &acpi_thermal_zone_ops, NULL, passive_delay,
+ tz->polling_frequency * 100);
else
- tz->thermal_zone =
- thermal_zone_device_register("acpitz", trips, 0, tz,
- &acpi_thermal_zone_ops, NULL,
- 0, tz->polling_frequency*100);
+ tz->thermal_zone = thermal_tripless_zone_device_register(
+ "acpitz", tz, &acpi_thermal_zone_ops, NULL);
+
if (IS_ERR(tz->thermal_zone))
- return -ENODEV;
+ return PTR_ERR(tz->thermal_zone);
- result = sysfs_create_link(&tz->device->dev.kobj,
- &tz->thermal_zone->device.kobj, "thermal_zone");
+ result = acpi_thermal_zone_sysfs_add(tz);
if (result)
goto unregister_tzd;
- result = sysfs_create_link(&tz->thermal_zone->device.kobj,
- &tz->device->dev.kobj, "device");
- if (result)
- goto remove_tz_link;
-
- status = acpi_bus_attach_private_data(tz->device->handle,
- tz->thermal_zone);
- if (ACPI_FAILURE(status)) {
- result = -ENODEV;
- goto remove_dev_link;
- }
-
result = thermal_zone_device_enable(tz->thermal_zone);
if (result)
- goto acpi_bus_detach;
+ goto remove_links;
dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
- tz->thermal_zone->id);
+ thermal_zone_device_id(tz->thermal_zone));
return 0;
-acpi_bus_detach:
- acpi_bus_detach_private_data(tz->device->handle);
-remove_dev_link:
- sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
-remove_tz_link:
- sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
+remove_links:
+ acpi_thermal_zone_sysfs_remove(tz);
unregister_tzd:
thermal_zone_device_unregister(tz->thermal_zone);
@@ -859,11 +657,10 @@ unregister_tzd:
static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
{
- sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
- sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
+ thermal_zone_device_disable(tz->thermal_zone);
+ acpi_thermal_zone_sysfs_remove(tz);
thermal_zone_device_unregister(tz->thermal_zone);
tz->thermal_zone = NULL;
- acpi_bus_detach_private_data(tz->device->handle);
}
@@ -871,17 +668,11 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
Driver Interface
-------------------------------------------------------------------------- */
-static void acpi_queue_thermal_check(struct acpi_thermal *tz)
-{
- if (!work_pending(&tz->thermal_check_work))
- queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
-}
-
-static void acpi_thermal_notify(struct acpi_device *device, u32 event)
+static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data)
{
+ struct acpi_device *device = data;
struct acpi_thermal *tz = acpi_driver_data(device);
-
if (!tz)
return;
@@ -890,16 +681,8 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event)
acpi_queue_thermal_check(tz);
break;
case ACPI_THERMAL_NOTIFY_THRESHOLDS:
- acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
- acpi_queue_thermal_check(tz);
- acpi_bus_generate_netlink_event(device->pnp.device_class,
- dev_name(&device->dev), event, 0);
- break;
case ACPI_THERMAL_NOTIFY_DEVICES:
- acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
- acpi_queue_thermal_check(tz);
- acpi_bus_generate_netlink_event(device->pnp.device_class,
- dev_name(&device->dev), event, 0);
+ acpi_thermal_trips_update(tz, event);
break;
default:
acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
@@ -940,40 +723,6 @@ static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz)
acpi_evaluate_integer(handle, "_TMP", NULL, &value);
}
-static int acpi_thermal_get_info(struct acpi_thermal *tz)
-{
- int result = 0;
-
-
- if (!tz)
- return -EINVAL;
-
- acpi_thermal_aml_dependency_fix(tz);
-
- /* Get trip points [_CRT, _PSV, etc.] (required) */
- result = acpi_thermal_get_trip_points(tz);
- if (result)
- return result;
-
- /* Get temperature [_TMP] (required) */
- result = acpi_thermal_get_temperature(tz);
- if (result)
- return result;
-
- /* Set the cooling mode [_SCP] to active cooling (default) */
- result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
- if (!result)
- tz->flags.cooling_mode = 1;
-
- /* Get default polling frequency [_TZP] (optional) */
- if (tzp)
- tz->polling_frequency = tzp;
- else
- acpi_thermal_get_polling_frequency(tz);
-
- return 0;
-}
-
/*
* The exact offset between Kelvin and degree Celsius is 273.15. However ACPI
* handles temperature values with a single decimal place. As a consequence,
@@ -984,10 +733,9 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
* The heuristic below should work for all ACPI thermal zones which have a
* critical trip point with a value being a multiple of 0.5 degree Celsius.
*/
-static void acpi_thermal_guess_offset(struct acpi_thermal *tz)
+static void acpi_thermal_guess_offset(struct acpi_thermal *tz, long crit_temp)
{
- if (tz->trips.critical.flags.valid &&
- (tz->trips.critical.temperature % 5) == 1)
+ if (crit_temp != THERMAL_TEMP_INVALID && crit_temp % 5 == 1)
tz->kelvin_offset = 273100;
else
tz->kelvin_offset = 273200;
@@ -1018,11 +766,27 @@ static void acpi_thermal_check_fn(struct work_struct *work)
mutex_unlock(&tz->thermal_check_lock);
}
-static int acpi_thermal_add(struct acpi_device *device)
+static void acpi_thermal_free_thermal_zone(struct acpi_thermal *tz)
{
- int result = 0;
- struct acpi_thermal *tz = NULL;
+ int i;
+ acpi_handle_list_free(&tz->trips.passive.trip.devices);
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
+ acpi_handle_list_free(&tz->trips.active[i].trip.devices);
+
+ kfree(tz);
+}
+
+static int acpi_thermal_add(struct acpi_device *device)
+{
+ struct thermal_trip trip_table[ACPI_THERMAL_MAX_NR_TRIPS] = { 0 };
+ struct acpi_thermal_trip *acpi_trip;
+ struct thermal_trip *trip;
+ struct acpi_thermal *tz;
+ int crit_temp, hot_temp;
+ int passive_delay = 0;
+ int result;
+ int i;
if (!device)
return -EINVAL;
@@ -1032,18 +796,80 @@ static int acpi_thermal_add(struct acpi_device *device)
return -ENOMEM;
tz->device = device;
- strcpy(tz->name, device->pnp.bus_id);
- strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
+ strscpy(tz->name, device->pnp.bus_id);
+ strscpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
device->driver_data = tz;
- result = acpi_thermal_get_info(tz);
+ acpi_thermal_aml_dependency_fix(tz);
+
+ /*
+ * Set the cooling mode [_SCP] to active cooling. This needs to happen before
+ * we retrieve the trip point values.
+ */
+ acpi_execute_simple_method(tz->device->handle, "_SCP", ACPI_THERMAL_MODE_ACTIVE);
+
+ /* Get trip points [_ACi, _PSV, etc.] (required). */
+ acpi_thermal_get_trip_points(tz);
+
+ crit_temp = acpi_thermal_get_critical_trip(tz);
+ hot_temp = acpi_thermal_get_hot_trip(tz);
+
+ /* Get temperature [_TMP] (required). */
+ result = acpi_thermal_get_temperature(tz);
if (result)
goto free_memory;
- acpi_thermal_guess_offset(tz);
+ /* Determine the default polling frequency [_TZP]. */
+ if (tzp)
+ tz->polling_frequency = tzp;
+ else
+ acpi_thermal_get_polling_frequency(tz);
+
+ acpi_thermal_guess_offset(tz, crit_temp);
+
+ trip = trip_table;
+
+ if (crit_temp != THERMAL_TEMP_INVALID) {
+ trip->type = THERMAL_TRIP_CRITICAL;
+ trip->temperature = acpi_thermal_temp(tz, crit_temp);
+ trip++;
+ }
+
+ if (hot_temp != THERMAL_TEMP_INVALID) {
+ trip->type = THERMAL_TRIP_HOT;
+ trip->temperature = acpi_thermal_temp(tz, hot_temp);
+ trip++;
+ }
+
+ acpi_trip = &tz->trips.passive.trip;
+ if (acpi_thermal_trip_valid(acpi_trip)) {
+ passive_delay = tz->trips.passive.delay;
+
+ trip->type = THERMAL_TRIP_PASSIVE;
+ trip->temperature = acpi_thermal_temp(tz, acpi_trip->temp_dk);
+ trip->priv = acpi_trip;
+ trip++;
+ }
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+ acpi_trip = &tz->trips.active[i].trip;
+
+ if (!acpi_thermal_trip_valid(acpi_trip))
+ break;
+
+ trip->type = THERMAL_TRIP_ACTIVE;
+ trip->temperature = acpi_thermal_temp(tz, acpi_trip->temp_dk);
+ trip->priv = acpi_trip;
+ trip++;
+ }
+
+ if (trip == trip_table)
+ pr_warn(FW_BUG "No valid trip points!\n");
- result = acpi_thermal_register_thermal_zone(tz);
+ result = acpi_thermal_register_thermal_zone(tz, trip_table,
+ trip - trip_table,
+ passive_delay);
if (result)
goto free_memory;
@@ -1052,28 +878,39 @@ static int acpi_thermal_add(struct acpi_device *device)
INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn);
pr_info("%s [%s] (%ld C)\n", acpi_device_name(device),
- acpi_device_bid(device), deci_kelvin_to_celsius(tz->temperature));
- goto end;
+ acpi_device_bid(device), deci_kelvin_to_celsius(tz->temp_dk));
+
+ result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_thermal_notify, device);
+ if (result)
+ goto flush_wq;
+ return 0;
+
+flush_wq:
+ flush_workqueue(acpi_thermal_pm_queue);
+ acpi_thermal_unregister_thermal_zone(tz);
free_memory:
- kfree(tz);
-end:
+ acpi_thermal_free_thermal_zone(tz);
+
return result;
}
-static int acpi_thermal_remove(struct acpi_device *device)
+static void acpi_thermal_remove(struct acpi_device *device)
{
- struct acpi_thermal *tz = NULL;
+ struct acpi_thermal *tz;
if (!device || !acpi_driver_data(device))
- return -EINVAL;
+ return;
- flush_workqueue(acpi_thermal_pm_queue);
tz = acpi_driver_data(device);
+ acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY,
+ acpi_thermal_notify);
+
+ flush_workqueue(acpi_thermal_pm_queue);
acpi_thermal_unregister_thermal_zone(tz);
- kfree(tz);
- return 0;
+ acpi_thermal_free_thermal_zone(tz);
}
#ifdef CONFIG_PM_SLEEP
@@ -1087,7 +924,7 @@ static int acpi_thermal_suspend(struct device *dev)
static int acpi_thermal_resume(struct device *dev)
{
struct acpi_thermal *tz;
- int i, j, power_state, result;
+ int i, j;
if (!dev)
return -EINVAL;
@@ -1097,29 +934,44 @@ static int acpi_thermal_resume(struct device *dev)
return -EINVAL;
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
- if (!tz->trips.active[i].flags.valid)
+ struct acpi_thermal_trip *acpi_trip = &tz->trips.active[i].trip;
+
+ if (!acpi_thermal_trip_valid(acpi_trip))
break;
- tz->trips.active[i].flags.enabled = 1;
- for (j = 0; j < tz->trips.active[i].devices.count; j++) {
- result = acpi_bus_update_power(
- tz->trips.active[i].devices.handles[j],
- &power_state);
- if (result || (power_state != ACPI_STATE_D0)) {
- tz->trips.active[i].flags.enabled = 0;
- break;
- }
- }
- tz->state.active |= tz->trips.active[i].flags.enabled;
+
+ for (j = 0; j < acpi_trip->devices.count; j++)
+ acpi_bus_update_power(acpi_trip->devices.handles[j], NULL);
}
acpi_queue_thermal_check(tz);
return AE_OK;
}
+#else
+#define acpi_thermal_suspend NULL
+#define acpi_thermal_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
-static int thermal_act(const struct dmi_system_id *d) {
+static const struct acpi_device_id thermal_device_ids[] = {
+ {ACPI_THERMAL_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
+static struct acpi_driver acpi_thermal_driver = {
+ .name = "thermal",
+ .class = ACPI_THERMAL_CLASS,
+ .ids = thermal_device_ids,
+ .ops = {
+ .add = acpi_thermal_add,
+ .remove = acpi_thermal_remove,
+ },
+ .drv.pm = &acpi_thermal_pm,
+};
+
+static int thermal_act(const struct dmi_system_id *d)
+{
if (act == 0) {
pr_notice("%s detected: disabling all active thermal trip points\n",
d->ident);
@@ -1127,15 +979,17 @@ static int thermal_act(const struct dmi_system_id *d) {
}
return 0;
}
-static int thermal_nocrt(const struct dmi_system_id *d) {
+static int thermal_nocrt(const struct dmi_system_id *d)
+{
pr_notice("%s detected: disabling all critical thermal trip point actions.\n",
d->ident);
- nocrt = 1;
+ crt = -1;
return 0;
}
-static int thermal_tzp(const struct dmi_system_id *d) {
+static int thermal_tzp(const struct dmi_system_id *d)
+{
if (tzp == 0) {
pr_notice("%s detected: enabling thermal zone polling\n",
d->ident);
@@ -1143,8 +997,9 @@ static int thermal_tzp(const struct dmi_system_id *d) {
}
return 0;
}
-static int thermal_psv(const struct dmi_system_id *d) {
+static int thermal_psv(const struct dmi_system_id *d)
+{
if (psv == 0) {
pr_notice("%s detected: disabling all passive thermal trip points\n",
d->ident);
@@ -1195,7 +1050,7 @@ static const struct dmi_system_id thermal_dmi_table[] __initconst = {
static int __init acpi_thermal_init(void)
{
- int result = 0;
+ int result;
dmi_check_system(thermal_dmi_table);
@@ -1205,7 +1060,8 @@ static int __init acpi_thermal_init(void)
}
acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm",
- WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!acpi_thermal_pm_queue)
return -ENODEV;
@@ -1222,9 +1078,12 @@ static void __exit acpi_thermal_exit(void)
{
acpi_bus_unregister_driver(&acpi_thermal_driver);
destroy_workqueue(acpi_thermal_pm_queue);
-
- return;
}
module_init(acpi_thermal_init);
module_exit(acpi_thermal_exit);
+
+MODULE_IMPORT_NS("ACPI_THERMAL");
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/thermal_lib.c b/drivers/acpi/thermal_lib.c
new file mode 100644
index 000000000000..f81591927e86
--- /dev/null
+++ b/drivers/acpi/thermal_lib.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 Linaro Limited
+ * Copyright 2023 Intel Corporation
+ *
+ * Library routines for retrieving trip point temperature values from the
+ * platform firmware via ACPI.
+ */
+#include <linux/acpi.h>
+#include <linux/units.h>
+#include <linux/thermal.h>
+#include "internal.h"
+
+/*
+ * Minimum temperature for full military grade is 218°K (-55°C) and
+ * max temperature is 448°K (175°C). We can consider those values as
+ * the boundaries for the [trips] temperature returned by the
+ * firmware. Any values out of these boundaries may be considered
+ * bogus and we can assume the firmware has no data to provide.
+ */
+#define TEMP_MIN_DECIK 2180ULL
+#define TEMP_MAX_DECIK 4480ULL
+
+static int acpi_trip_temp(struct acpi_device *adev, char *obj_name,
+ int *ret_temp)
+{
+ unsigned long long temp;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(adev->handle, obj_name, NULL, &temp);
+ if (ACPI_FAILURE(status)) {
+ acpi_handle_debug(adev->handle, "%s evaluation failed\n", obj_name);
+ return -ENODATA;
+ }
+
+ if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) {
+ *ret_temp = temp;
+ } else {
+ acpi_handle_debug(adev->handle, "%s result %llu out of range\n",
+ obj_name, temp);
+ *ret_temp = THERMAL_TEMP_INVALID;
+ }
+
+ return 0;
+}
+
+int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
+{
+ char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'};
+
+ if (id < 0 || id > 9)
+ return -EINVAL;
+
+ return acpi_trip_temp(adev, obj_name, ret_temp);
+}
+EXPORT_SYMBOL_NS_GPL(acpi_active_trip_temp, "ACPI_THERMAL");
+
+int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
+{
+ return acpi_trip_temp(adev, "_PSV", ret_temp);
+}
+EXPORT_SYMBOL_NS_GPL(acpi_passive_trip_temp, "ACPI_THERMAL");
+
+int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
+{
+ return acpi_trip_temp(adev, "_HOT", ret_temp);
+}
+EXPORT_SYMBOL_NS_GPL(acpi_hot_trip_temp, "ACPI_THERMAL");
+
+int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
+{
+ return acpi_trip_temp(adev, "_CRT", ret_temp);
+}
+EXPORT_SYMBOL_NS_GPL(acpi_critical_trip_temp, "ACPI_THERMAL");
+
+static int thermal_temp(int error, int temp_decik, int *ret_temp)
+{
+ if (error)
+ return error;
+
+ if (temp_decik == THERMAL_TEMP_INVALID)
+ *ret_temp = THERMAL_TEMP_INVALID;
+ else
+ *ret_temp = deci_kelvin_to_millicelsius(temp_decik);
+
+ return 0;
+}
+
+/**
+ * thermal_acpi_active_trip_temp - Retrieve active trip point temperature
+ * @adev: Target thermal zone ACPI device object.
+ * @id: Active cooling level (0 - 9).
+ * @ret_temp: Address to store the retrieved temperature value on success.
+ *
+ * Evaluate the _ACx object for the thermal zone represented by @adev to obtain
+ * the temperature of the active cooling trip point corresponding to the active
+ * cooling level given by @id.
+ *
+ * Return 0 on success or a negative error value on failure.
+ */
+int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
+{
+ int temp_decik = 0;
+ int ret = acpi_active_trip_temp(adev, id, &temp_decik);
+
+ return thermal_temp(ret, temp_decik, ret_temp);
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp);
+
+/**
+ * thermal_acpi_passive_trip_temp - Retrieve passive trip point temperature
+ * @adev: Target thermal zone ACPI device object.
+ * @ret_temp: Address to store the retrieved temperature value on success.
+ *
+ * Evaluate the _PSV object for the thermal zone represented by @adev to obtain
+ * the temperature of the passive cooling trip point.
+ *
+ * Return 0 on success or -ENODATA on failure.
+ */
+int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
+{
+ int temp_decik = 0;
+ int ret = acpi_passive_trip_temp(adev, &temp_decik);
+
+ return thermal_temp(ret, temp_decik, ret_temp);
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp);
+
+/**
+ * thermal_acpi_hot_trip_temp - Retrieve hot trip point temperature
+ * @adev: Target thermal zone ACPI device object.
+ * @ret_temp: Address to store the retrieved temperature value on success.
+ *
+ * Evaluate the _HOT object for the thermal zone represented by @adev to obtain
+ * the temperature of the trip point at which the system is expected to be put
+ * into the S4 sleep state.
+ *
+ * Return 0 on success or -ENODATA on failure.
+ */
+int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
+{
+ int temp_decik = 0;
+ int ret = acpi_hot_trip_temp(adev, &temp_decik);
+
+ return thermal_temp(ret, temp_decik, ret_temp);
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp);
+
+/**
+ * thermal_acpi_critical_trip_temp - Retrieve critical trip point temperature
+ * @adev: Target thermal zone ACPI device object.
+ * @ret_temp: Address to store the retrieved temperature value on success.
+ *
+ * Evaluate the _CRT object for the thermal zone represented by @adev to obtain
+ * the temperature of the critical cooling trip point.
+ *
+ * Return 0 on success or -ENODATA on failure.
+ */
+int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
+{
+ int temp_decik = 0;
+ int ret = acpi_critical_trip_temp(adev, &temp_decik);
+
+ return thermal_temp(ret, temp_decik, ret_temp);
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_critical_trip_temp);
diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c
index a19f0e4e69f7..6353be6fec69 100644
--- a/drivers/acpi/tiny-power-button.c
+++ b/drivers/acpi/tiny-power-button.c
@@ -19,14 +19,52 @@ static const struct acpi_device_id tiny_power_button_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids);
-static int acpi_noop_add_remove(struct acpi_device *device)
+static void acpi_tiny_power_button_notify(acpi_handle handle, u32 event, void *data)
{
+ kill_cad_pid(power_signal, 1);
+}
+
+static void acpi_tiny_power_button_notify_run(void *not_used)
+{
+ acpi_tiny_power_button_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, NULL);
+}
+
+static u32 acpi_tiny_power_button_event(void *not_used)
+{
+ acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_tiny_power_button_notify_run, NULL);
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+static int acpi_tiny_power_button_add(struct acpi_device *device)
+{
+ acpi_status status;
+
+ if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
+ status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+ acpi_tiny_power_button_event,
+ NULL);
+ } else {
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_tiny_power_button_notify,
+ NULL);
+ }
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
return 0;
}
-static void acpi_tiny_power_button_notify(struct acpi_device *device, u32 event)
+static void acpi_tiny_power_button_remove(struct acpi_device *device)
{
- kill_cad_pid(power_signal, 1);
+ if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
+ acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+ acpi_tiny_power_button_event);
+ } else {
+ acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_tiny_power_button_notify);
+ }
+ acpi_os_wait_events_complete();
}
static struct acpi_driver acpi_tiny_power_button_driver = {
@@ -34,9 +72,8 @@ static struct acpi_driver acpi_tiny_power_button_driver = {
.class = "tiny-power-button",
.ids = tiny_power_button_device_ids,
.ops = {
- .add = acpi_noop_add_remove,
- .remove = acpi_noop_add_remove,
- .notify = acpi_tiny_power_button_notify,
+ .add = acpi_tiny_power_button_add,
+ .remove = acpi_tiny_power_button_remove,
},
};
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 5a7b8065e77f..526563a0d188 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -277,15 +277,25 @@ acpi_evaluate_integer(acpi_handle handle,
EXPORT_SYMBOL(acpi_evaluate_integer);
-int acpi_get_local_address(acpi_handle handle, u32 *addr)
+int acpi_get_local_u64_address(acpi_handle handle, u64 *addr)
{
- unsigned long long adr;
acpi_status status;
- status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, addr);
if (ACPI_FAILURE(status))
return -ENODATA;
+ return 0;
+}
+EXPORT_SYMBOL(acpi_get_local_u64_address);
+
+int acpi_get_local_address(acpi_handle handle, u32 *addr)
+{
+ u64 adr;
+ int ret;
+ ret = acpi_get_local_u64_address(handle, &adr);
+ if (ret < 0)
+ return ret;
*addr = (u32)adr;
return 0;
}
@@ -329,22 +339,18 @@ const char *acpi_get_subsystem_id(acpi_handle handle)
}
EXPORT_SYMBOL_GPL(acpi_get_subsystem_id);
-acpi_status
-acpi_evaluate_reference(acpi_handle handle,
- acpi_string pathname,
- struct acpi_object_list *arguments,
- struct acpi_handle_list *list)
+bool acpi_evaluate_reference(acpi_handle handle, acpi_string pathname,
+ struct acpi_object_list *arguments,
+ struct acpi_handle_list *list)
{
- acpi_status status = AE_OK;
- union acpi_object *package = NULL;
- union acpi_object *element = NULL;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- u32 i = 0;
-
+ union acpi_object *package;
+ acpi_status status;
+ bool ret = false;
+ u32 i;
- if (!list) {
- return AE_BAD_PARAMETER;
- }
+ if (!list)
+ return false;
/* Evaluate object. */
@@ -354,65 +360,141 @@ acpi_evaluate_reference(acpi_handle handle,
package = buffer.pointer;
- if ((buffer.length == 0) || !package) {
- status = AE_BAD_DATA;
- acpi_util_eval_error(handle, pathname, status);
- goto end;
- }
- if (package->type != ACPI_TYPE_PACKAGE) {
- status = AE_BAD_DATA;
- acpi_util_eval_error(handle, pathname, status);
- goto end;
- }
- if (!package->package.count) {
- status = AE_BAD_DATA;
- acpi_util_eval_error(handle, pathname, status);
- goto end;
- }
+ if (buffer.length == 0 || !package ||
+ package->type != ACPI_TYPE_PACKAGE || !package->package.count)
+ goto err;
- if (package->package.count > ACPI_MAX_HANDLES) {
- kfree(package);
- return AE_NO_MEMORY;
- }
list->count = package->package.count;
+ list->handles = kcalloc(list->count, sizeof(*list->handles), GFP_KERNEL);
+ if (!list->handles)
+ goto err_clear;
/* Extract package data. */
for (i = 0; i < list->count; i++) {
+ union acpi_object *element = &(package->package.elements[i]);
- element = &(package->package.elements[i]);
-
- if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
- status = AE_BAD_DATA;
- acpi_util_eval_error(handle, pathname, status);
- break;
- }
+ if (element->type != ACPI_TYPE_LOCAL_REFERENCE ||
+ !element->reference.handle)
+ goto err_free;
- if (!element->reference.handle) {
- status = AE_NULL_ENTRY;
- acpi_util_eval_error(handle, pathname, status);
- break;
- }
/* Get the acpi_handle. */
list->handles[i] = element->reference.handle;
acpi_handle_debug(list->handles[i], "Found in reference list\n");
}
- end:
- if (ACPI_FAILURE(status)) {
- list->count = 0;
- //kfree(list->handles);
- }
+ ret = true;
+end:
kfree(buffer.pointer);
- return status;
+ return ret;
+
+err_free:
+ kfree(list->handles);
+ list->handles = NULL;
+
+err_clear:
+ list->count = 0;
+
+err:
+ acpi_util_eval_error(handle, pathname, status);
+ goto end;
}
EXPORT_SYMBOL(acpi_evaluate_reference);
-acpi_status
+/**
+ * acpi_handle_list_equal - Check if two ACPI handle lists are the same
+ * @list1: First list to compare.
+ * @list2: Second list to compare.
+ *
+ * Return true if the given ACPI handle lists are of the same size and
+ * contain the same ACPI handles in the same order. Otherwise, return false.
+ */
+bool acpi_handle_list_equal(struct acpi_handle_list *list1,
+ struct acpi_handle_list *list2)
+{
+ return list1->count == list2->count &&
+ !memcmp(list1->handles, list2->handles,
+ list1->count * sizeof(*list1->handles));
+}
+EXPORT_SYMBOL_GPL(acpi_handle_list_equal);
+
+/**
+ * acpi_handle_list_replace - Replace one ACPI handle list with another
+ * @dst: ACPI handle list to replace.
+ * @src: Source ACPI handle list.
+ *
+ * Free the handles table in @dst, move the handles table from @src to @dst,
+ * copy count from @src to @dst and clear @src.
+ */
+void acpi_handle_list_replace(struct acpi_handle_list *dst,
+ struct acpi_handle_list *src)
+{
+ if (dst->count)
+ kfree(dst->handles);
+
+ dst->count = src->count;
+ dst->handles = src->handles;
+
+ src->handles = NULL;
+ src->count = 0;
+}
+EXPORT_SYMBOL_GPL(acpi_handle_list_replace);
+
+/**
+ * acpi_handle_list_free - Free the handles table in an ACPI handle list
+ * @list: ACPI handle list to free.
+ *
+ * Free the handles table in @list and clear its count field.
+ */
+void acpi_handle_list_free(struct acpi_handle_list *list)
+{
+ if (!list->count)
+ return;
+
+ kfree(list->handles);
+ list->count = 0;
+}
+EXPORT_SYMBOL_GPL(acpi_handle_list_free);
+
+/**
+ * acpi_device_dep - Check ACPI device dependency
+ * @target: ACPI handle of the target ACPI device.
+ * @match: ACPI handle to look up in the target's _DEP list.
+ *
+ * Return true if @match is present in the list returned by _DEP for
+ * @target or false otherwise.
+ */
+bool acpi_device_dep(acpi_handle target, acpi_handle match)
+{
+ struct acpi_handle_list dep_devices;
+ bool ret = false;
+ int i;
+
+ if (!acpi_has_method(target, "_DEP"))
+ return false;
+
+ if (!acpi_evaluate_reference(target, "_DEP", NULL, &dep_devices)) {
+ acpi_handle_debug(target, "Failed to evaluate _DEP.\n");
+ return false;
+ }
+
+ for (i = 0; i < dep_devices.count; i++) {
+ if (dep_devices.handles[i] == match) {
+ ret = true;
+ break;
+ }
+ }
+
+ acpi_handle_list_free(&dep_devices);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_device_dep);
+
+bool
acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld)
{
acpi_status status;
@@ -420,9 +502,8 @@ acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld
union acpi_object *output;
status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer);
-
if (ACPI_FAILURE(status))
- return status;
+ return false;
output = buffer.pointer;
@@ -441,7 +522,7 @@ acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld
out:
kfree(buffer.pointer);
- return status;
+ return ACPI_SUCCESS(status);
}
EXPORT_SYMBOL(acpi_get_physical_device_location);
@@ -487,7 +568,7 @@ EXPORT_SYMBOL(acpi_evaluate_ost);
*
* Caller must free the returned buffer
*/
-static char *acpi_handle_path(acpi_handle handle)
+char *acpi_handle_path(acpi_handle handle)
{
struct acpi_buffer buffer = {
.length = ACPI_ALLOCATE_BUFFER,
@@ -523,7 +604,7 @@ acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...)
vaf.va = &args;
path = acpi_handle_path(handle);
- printk("%sACPI: %s: %pV", level, path ? path : "<n/a>" , &vaf);
+ printk("%sACPI: %s: %pV", level, path ? path : "<n/a>", &vaf);
va_end(args);
kfree(path);
@@ -719,7 +800,8 @@ acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 func,
if (ret != AE_NOT_FOUND)
acpi_handle_warn(handle,
- "failed to evaluate _DSM %pUb (0x%x)\n", guid, ret);
+ "failed to evaluate _DSM %pUb rev:%lld func:%lld (0x%x)\n",
+ guid, rev, func, ret);
return NULL;
}
@@ -769,29 +851,28 @@ bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs)
EXPORT_SYMBOL(acpi_check_dsm);
/**
- * acpi_dev_hid_uid_match - Match device by supplied HID and UID
- * @adev: ACPI device to match.
- * @hid2: Hardware ID of the device.
- * @uid2: Unique ID of the device, pass NULL to not check _UID.
+ * acpi_dev_uid_to_integer - treat ACPI device _UID as integer
+ * @adev: ACPI device to get _UID from
+ * @integer: output buffer for integer
+ *
+ * Considers _UID as integer and converts it to @integer.
*
- * Matches HID and UID in @adev with given @hid2 and @uid2.
- * Returns true if matches.
+ * Returns 0 on success, or negative error code otherwise.
*/
-bool acpi_dev_hid_uid_match(struct acpi_device *adev,
- const char *hid2, const char *uid2)
+int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer)
{
- const char *hid1 = acpi_device_hid(adev);
- const char *uid1 = acpi_device_uid(adev);
+ const char *uid;
- if (strcmp(hid1, hid2))
- return false;
+ if (!adev)
+ return -ENODEV;
- if (!uid2)
- return true;
+ uid = acpi_device_uid(adev);
+ if (!uid)
+ return -ENODATA;
- return uid1 && !strcmp(uid1, uid2);
+ return kstrtou64(uid, 0, integer);
}
-EXPORT_SYMBOL(acpi_dev_hid_uid_match);
+EXPORT_SYMBOL(acpi_dev_uid_to_integer);
/**
* acpi_dev_found - Detect presence of a given ACPI device in the namespace.
@@ -839,8 +920,7 @@ static int acpi_dev_match_cb(struct device *dev, const void *data)
if (acpi_match_device_ids(adev, match->hid))
return 0;
- if (match->uid && (!adev->pnp.unique_id ||
- strcmp(adev->pnp.unique_id, match->uid)))
+ if (match->uid && !acpi_dev_uid_match(adev, match->uid))
return 0;
if (match->hrv == -1)
@@ -878,7 +958,7 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
struct acpi_dev_match_info match = {};
struct device *dev;
- strlcpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
+ strscpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
match.uid = uid;
match.hrv = hrv;
@@ -911,7 +991,7 @@ acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const cha
struct acpi_dev_match_info match = {};
struct device *dev;
- strlcpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
+ strscpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
match.uid = uid;
match.hrv = hrv;
@@ -961,7 +1041,7 @@ EXPORT_SYMBOL(acpi_video_backlight_string);
static int __init acpi_backlight(char *str)
{
- strlcpy(acpi_video_backlight_string, str,
+ strscpy(acpi_video_backlight_string, str,
sizeof(acpi_video_backlight_string));
return 1;
}
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 5d7f38016a24..4cf74f173c78 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -17,8 +17,9 @@
* Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop,
* sony_acpi,... can take care about backlight brightness.
*
- * Backlight drivers can use acpi_video_get_backlight_type() to determine
- * which driver should handle the backlight.
+ * Backlight drivers can use acpi_video_get_backlight_type() to determine which
+ * driver should handle the backlight. RAW/GPU-driver backlight drivers must
+ * use the acpi_video_backlight_use_native() helper for this.
*
* If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
* this file will not be compiled and acpi_video_get_backlight_type() will
@@ -27,20 +28,17 @@
#include <linux/export.h>
#include <linux/acpi.h>
+#include <linux/apple-gmux.h>
#include <linux/backlight.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_data/x86/nvidia-wmi-ec-backlight.h>
+#include <linux/pnp.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <acpi/video.h>
-void acpi_video_unregister_backlight(void);
-
-static bool backlight_notifier_registered;
-static struct notifier_block backlight_nb;
-static struct work_struct backlight_notify_work;
-
static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef;
static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef;
@@ -52,6 +50,12 @@ static void acpi_video_parse_cmdline(void)
acpi_backlight_cmdline = acpi_backlight_video;
if (!strcmp("native", acpi_video_backlight_string))
acpi_backlight_cmdline = acpi_backlight_native;
+ if (!strcmp("nvidia_wmi_ec", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_nvidia_wmi_ec;
+ if (!strcmp("apple_gmux", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_apple_gmux;
+ if (!strcmp("dell_uart", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_dell_uart;
if (!strcmp("none", acpi_video_backlight_string))
acpi_backlight_cmdline = acpi_backlight_none;
}
@@ -78,6 +82,36 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
+/* This depends on ACPI_WMI which is X86 only */
+#ifdef CONFIG_X86
+static bool nvidia_wmi_ec_supported(void)
+{
+ struct wmi_brightness_args args = {
+ .mode = WMI_BRIGHTNESS_MODE_GET,
+ .val = 0,
+ .ret = 0,
+ };
+ struct acpi_buffer buf = { (acpi_size)sizeof(args), &args };
+ acpi_status status;
+
+ status = wmi_evaluate_method(WMI_BRIGHTNESS_GUID, 0,
+ WMI_BRIGHTNESS_METHOD_SOURCE, &buf, &buf);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ /*
+ * If brightness is handled by the EC then nvidia-wmi-ec-backlight
+ * should be used, else the GPU driver(s) should be used.
+ */
+ return args.ret == WMI_BRIGHTNESS_SOURCE_EC;
+}
+#else
+static bool nvidia_wmi_ec_supported(void)
+{
+ return false;
+}
+#endif
+
/* Force to use vendor driver when the ACPI device is known to be
* buggy */
static int video_detect_force_vendor(const struct dmi_system_id *d)
@@ -98,66 +132,192 @@ static int video_detect_force_native(const struct dmi_system_id *d)
return 0;
}
-static int video_detect_force_none(const struct dmi_system_id *d)
+static int video_detect_portege_r100(const struct dmi_system_id *d)
{
- acpi_backlight_dmi = acpi_backlight_none;
+ struct pci_dev *dev;
+ /* Search for Trident CyberBlade XP4m32 to confirm Portégé R100 */
+ dev = pci_get_device(PCI_VENDOR_ID_TRIDENT, 0x2100, NULL);
+ if (dev)
+ acpi_backlight_dmi = acpi_backlight_vendor;
return 0;
}
static const struct dmi_system_id video_detect_dmi_table[] = {
- /* On Samsung X360, the BIOS will set a flag (VDRV) if generic
- * ACPI backlight device is used. This flag will definitively break
- * the backlight interface (even the vendor interface) until next
- * reboot. It's why we should prevent video.ko from being used here
- * and we can't rely on a later call to acpi_video_unregister().
+ /*
+ * Models which should use the vendor backlight interface,
+ * because of broken ACPI video backlight control.
*/
{
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1128309 */
.callback = video_detect_force_vendor,
- /* X360 */
+ /* Acer KAV80 */
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
- DMI_MATCH(DMI_BOARD_NAME, "X360"),
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "KAV80"),
},
},
{
- .callback = video_detect_force_vendor,
- /* Asus UL30VT */
- .matches = {
+ .callback = video_detect_force_vendor,
+ /* Asus UL30VT */
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "UL30VT"),
},
},
{
- .callback = video_detect_force_vendor,
- /* Asus UL30A */
- .matches = {
+ .callback = video_detect_force_vendor,
+ /* Asus UL30A */
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "UL30A"),
},
},
{
- .callback = video_detect_force_vendor,
- /* GIGABYTE GB-BXBT-2807 */
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
- DMI_MATCH(DMI_PRODUCT_NAME, "GB-BXBT-2807"),
+ .callback = video_detect_force_vendor,
+ /* Asus X55U */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
},
},
{
- .callback = video_detect_force_vendor,
- /* Sony VPCEH3U1E */
- .matches = {
+ /* https://bugs.launchpad.net/bugs/1000146 */
+ .callback = video_detect_force_vendor,
+ /* Asus X101CH */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Asus X401U */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Asus X501U */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
+ },
+ },
+ {
+ /* https://bugs.launchpad.net/bugs/1000146 */
+ .callback = video_detect_force_vendor,
+ /* Asus 1015CX */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Samsung N150/N210/N220 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Samsung NF110/NF210/NF310 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
+ DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Samsung NC210 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
+ DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
+ },
+ },
+
+ /*
+ * Models which should use the vendor backlight interface,
+ * because of broken native backlight control.
+ */
+ {
+ .callback = video_detect_force_vendor,
+ /* Sony Vaio PCG-FRV35 */
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
- DMI_MATCH(DMI_PRODUCT_NAME, "VPCEH3U1E"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PCG-FRV35"),
},
},
{
- .callback = video_detect_force_vendor,
- /* Xiaomi Mi Pad 2 */
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
+ .callback = video_detect_force_vendor,
+ /* Panasonic Toughbook CF-18 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita Electric Industrial"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
+ },
+ },
+
+ /*
+ * Toshiba models with Transflective display, these need to use
+ * the toshiba_acpi vendor driver for proper Transflective handling.
+ */
+ {
+ .callback = video_detect_force_vendor,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R500"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R600"),
+ },
+ },
+
+ /*
+ * Toshiba Portégé R100 has working both acpi_video and toshiba_acpi
+ * vendor driver. But none of them gets activated as it has a VGA with
+ * no kernel driver (Trident CyberBlade XP4m32).
+ * The DMI strings are generic so check for the VGA chip in callback.
+ */
+ {
+ .callback = video_detect_portege_r100,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"),
+ DMI_MATCH(DMI_BOARD_NAME, "Portable PC")
+ },
+ },
+
+ /*
+ * Models which need acpi_video backlight control where the GPU drivers
+ * do not call acpi_video_register_backlight() because no internal panel
+ * is detected. Typically these are all-in-ones (monitors with builtin
+ * PC) where the panel connection shows up as regular DP instead of eDP.
+ */
+ {
+ .callback = video_detect_force_video,
+ /* Apple iMac14,1 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac14,1"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ /* Apple iMac14,2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac14,2"),
},
},
@@ -293,8 +453,8 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "530U4E/540U4E"),
},
},
- /* https://bugs.launchpad.net/bugs/1894667 */
{
+ /* https://bugs.launchpad.net/bugs/1894667 */
.callback = video_detect_force_video,
/* HP 635 Notebook */
.matches = {
@@ -314,12 +474,21 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
},
{
+ /* https://bugzilla.suse.com/show_bug.cgi?id=1208724 */
+ .callback = video_detect_force_native,
+ /* Lenovo Ideapad Z470 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "IdeaPad Z470"),
+ },
+ },
+ {
/* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
.callback = video_detect_force_native,
/* Lenovo Ideapad Z570 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
},
},
{
@@ -339,6 +508,80 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
},
{
+ .callback = video_detect_force_native,
+ /* Lenovo Slim 7 16ARH7 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UX"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Lenovo ThinkPad X131e (3371 AMD version) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "3371"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Apple iMac11,3 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac11,3"),
+ },
+ },
+ {
+ /* https://gitlab.freedesktop.org/drm/amd/-/issues/1838 */
+ .callback = video_detect_force_native,
+ /* Apple iMac12,1 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac12,1"),
+ },
+ },
+ {
+ /* https://gitlab.freedesktop.org/drm/amd/-/issues/2753 */
+ .callback = video_detect_force_native,
+ /* Apple iMac12,2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac12,2"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Apple MacBook Air 7,2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir7,2"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Apple MacBook Air 9,1 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir9,1"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Apple MacBook Pro 9,2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,2"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Apple MacBook Pro 11,2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro11,2"),
+ },
+ },
+ {
/* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
.callback = video_detect_force_native,
/* Apple MacBook Pro 12,1 */
@@ -349,6 +592,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
{
.callback = video_detect_force_native,
+ /* Apple MacBook Pro 16,2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,2"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
/* Dell Inspiron N4010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
@@ -382,6 +633,30 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
{
.callback = video_detect_force_native,
+ /* Dell Studio 1569 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1569"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Acer Aspire 3830TG */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3830TG"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Acer Aspire 4810T */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 4810T"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
/* Acer Aspire 5738z */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -390,6 +665,41 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
},
{
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1012674 */
+ .callback = video_detect_force_native,
+ /* Acer Aspire 5741 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"),
+ },
+ },
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=42993 */
+ .callback = video_detect_force_native,
+ /* Acer Aspire 5750 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"),
+ },
+ },
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=42833 */
+ .callback = video_detect_force_native,
+ /* Acer Extensa 5235 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5235"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Acer TravelMate 4750 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"),
+ },
+ },
+ {
/* https://bugzilla.kernel.org/show_bug.cgi?id=207835 */
.callback = video_detect_force_native,
/* Acer TravelMate 5735Z */
@@ -400,180 +710,281 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
},
{
- .callback = video_detect_force_native,
- /* ASUSTeK COMPUTER INC. GA401 */
- .matches = {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=36322 */
+ .callback = video_detect_force_native,
+ /* Acer TravelMate 5760 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5760"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* ASUSTeK COMPUTER INC. GA401 */
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "GA401"),
},
},
{
- .callback = video_detect_force_native,
- /* ASUSTeK COMPUTER INC. GA502 */
- .matches = {
+ .callback = video_detect_force_native,
+ /* ASUSTeK COMPUTER INC. GA502 */
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "GA502"),
},
},
{
- .callback = video_detect_force_native,
- /* ASUSTeK COMPUTER INC. GA503 */
- .matches = {
+ .callback = video_detect_force_native,
+ /* ASUSTeK COMPUTER INC. GA503 */
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "GA503"),
},
},
- /*
- * Clevo NL5xRU and NL5xNU/TUXEDO Aura 15 Gen1 and Gen2 have both a
- * working native and video interface. However the default detection
- * mechanism first registers the video interface before unregistering
- * it again and switching to the native interface during boot. This
- * results in a dangling SBIOS request for backlight change for some
- * reason, causing the backlight to switch to ~2% once per boot on the
- * first power cord connect or disconnect event. Setting the native
- * interface explicitly circumvents this buggy behaviour, by avoiding
- * the unregistering process.
- */
{
- .callback = video_detect_force_native,
- .ident = "Clevo NL5xRU",
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
+ .callback = video_detect_force_native,
+ /* Asus U46E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U46E"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Asus UX303UB */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UX303UB"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* HP EliteBook 8460p */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8460p"),
},
},
{
- .callback = video_detect_force_native,
- .ident = "Clevo NL5xRU",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
- DMI_MATCH(DMI_BOARD_NAME, "AURA1501"),
+ .callback = video_detect_force_native,
+ /* HP Pavilion g6-1d80nr / B4U19UA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion g6 Notebook PC"),
+ DMI_MATCH(DMI_PRODUCT_SKU, "B4U19UA"),
},
},
{
- .callback = video_detect_force_native,
- .ident = "Clevo NL5xRU",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
- DMI_MATCH(DMI_BOARD_NAME, "EDUBOOK1502"),
+ .callback = video_detect_force_native,
+ /* Samsung N150P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150P"),
},
},
{
- .callback = video_detect_force_native,
- .ident = "Clevo NL5xNU",
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
+ .callback = video_detect_force_native,
+ /* Samsung N145P/N250P/N260P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
},
},
+ {
+ .callback = video_detect_force_native,
+ /* Samsung N250P */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N250P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N250P"),
+ },
+ },
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=202401 */
+ .callback = video_detect_force_native,
+ /* Sony Vaio VPCEH3U1E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEH3U1E"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* Sony Vaio VPCY11S1E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCY11S1E"),
+ },
+ },
+
/*
- * The TongFang PF5PU1G, PF4NU1F, PF5NU1G, and PF5LUXG/TUXEDO BA15 Gen10,
- * Pulse 14/15 Gen1, and Pulse 15 Gen2 have the same problem as the Clevo
- * NL5xRU and NL5xNU/TUXEDO Aura 15 Gen1 and Gen2. See the description
- * above.
+ * These Toshibas have a broken acpi-video interface for brightness
+ * control. They also have an issue where the panel is off after
+ * suspend until a special firmware call is made to turn it back
+ * on. This is handled by the toshiba_acpi kernel module, so that
+ * module must be enabled for these models to work correctly.
*/
{
- .callback = video_detect_force_native,
- .ident = "TongFang PF5PU1G",
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "PF5PU1G"),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
+ .callback = video_detect_force_native,
+ /* Toshiba Portégé R700 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"),
+ },
+ },
+ {
+ /* Portégé: https://bugs.freedesktop.org/show_bug.cgi?id=82634 */
+ /* Satellite: https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
+ .callback = video_detect_force_native,
+ /* Toshiba Satellite/Portégé R830 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R830"),
},
},
{
- .callback = video_detect_force_native,
- .ident = "TongFang PF4NU1F",
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "PF4NU1F"),
+ .callback = video_detect_force_native,
+ /* Toshiba Satellite/Portégé Z830 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Z830"),
},
},
+
+ /*
+ * Dell AIO (All in Ones) which advertise an UART attached backlight
+ * controller board in their ACPI tables (and may even have one), but
+ * which need native backlight control nevertheless.
+ */
{
- .callback = video_detect_force_native,
- .ident = "TongFang PF4NU1F",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
- DMI_MATCH(DMI_BOARD_NAME, "PULSE1401"),
+ /* https://github.com/zabbly/linux/issues/26 */
+ .callback = video_detect_force_native,
+ /* Dell OptiPlex 5480 AIO */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 5480 AIO"),
},
},
{
- .callback = video_detect_force_native,
- .ident = "TongFang PF5NU1G",
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "PF5NU1G"),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=2303936 */
+ .callback = video_detect_force_native,
+ /* Dell OptiPlex 7760 AIO */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7760 AIO"),
},
},
+
+ /*
+ * Models which have nvidia-ec-wmi support, but should not use it.
+ * Note this indicates a likely firmware bug on these models and should
+ * be revisited if/when Linux gets support for dynamic mux mode.
+ */
{
- .callback = video_detect_force_native,
- .ident = "TongFang PF5NU1G",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
- DMI_MATCH(DMI_BOARD_NAME, "PULSE1501"),
+ .callback = video_detect_force_native,
+ /* Dell G15 5515 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
},
},
{
- .callback = video_detect_force_native,
- .ident = "TongFang PF5LUXG",
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "PF5LUXG"),
+ .callback = video_detect_force_native,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15 3535"),
},
},
+
/*
- * Desktops which falsely report a backlight and which our heuristics
- * for this do not catch.
+ * x86 android tablets which directly control the backlight through
+ * an external backlight controller, typically TI's LP8557.
+ * The backlight is directly controlled by the lp855x driver on these.
+ * This setup means that neither i915's native nor acpi_video backlight
+ * control works. Add a "vendor" quirk to disable both. Note these
+ * devices do not use vendor control in the typical meaning of
+ * vendor specific SMBIOS or ACPI calls being used.
*/
{
- .callback = video_detect_force_none,
- /* Dell OptiPlex 9020M */
+ .callback = video_detect_force_vendor,
+ /* Lenovo Yoga Book X90F / X90L */
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 9020M"),
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),
},
},
{
- .callback = video_detect_force_none,
- /* MSI MS-7721 */
+ .callback = video_detect_force_vendor,
+ /*
+ * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10"
+ * Lenovo Yoga Tablet 2 use the same mainboard)
+ */
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
- DMI_MATCH(DMI_PRODUCT_NAME, "MS-7721"),
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
+ DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
+ /* Partial match on beginning of BIOS version */
+ DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Lenovo Yoga Tab 3 Pro YT3-X90F */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
+ },
+ },
+ {
+ .callback = video_detect_force_vendor,
+ /* Xiaomi Mi Pad 2 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
+ },
+ },
+ /* https://gitlab.freedesktop.org/drm/amd/-/issues/4512 */
+ {
+ .callback = video_detect_force_native,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82K8"),
},
},
{ },
};
-/* This uses a workqueue to avoid various locking ordering issues */
-static void acpi_video_backlight_notify_work(struct work_struct *work)
+static bool google_cros_ec_present(void)
{
- if (acpi_video_get_backlight_type() != acpi_backlight_video)
- acpi_video_unregister_backlight();
+ return acpi_dev_found("GOOG0004") || acpi_dev_found("GOOG000C");
}
-static int acpi_video_backlight_notify(struct notifier_block *nb,
- unsigned long val, void *bd)
+/*
+ * Windows 8 and newer no longer use the ACPI video interface, so it often
+ * does not work. So on win8+ systems prefer native brightness control.
+ * Chromebooks should always prefer native backlight control.
+ */
+static bool prefer_native_over_acpi_video(void)
{
- struct backlight_device *backlight = bd;
-
- /* A raw bl registering may change video -> native */
- if (backlight->props.type == BACKLIGHT_RAW &&
- val == BACKLIGHT_REGISTERED)
- schedule_work(&backlight_notify_work);
-
- return NOTIFY_OK;
+ return acpi_osi_is_win8() || google_cros_ec_present();
}
/*
* Determine which type of backlight interface to use on this system,
* First check cmdline, then dmi quirks, then do autodetect.
- *
- * The autodetect order is:
- * 1) Is the acpi-video backlight interface supported ->
- * no, use a vendor interface
- * 2) Is this a win8 "ready" BIOS and do we have a native interface ->
- * yes, use a native interface
- * 3) Else use the acpi-video interface
- *
- * Arguably the native on win8 check should be done first, but that would
- * be a behavior change, which may causes issues.
*/
-enum acpi_backlight_type acpi_video_get_backlight_type(void)
+enum acpi_backlight_type __acpi_video_get_backlight_type(bool native, bool *auto_detect)
{
static DEFINE_MUTEX(init_mutex);
+ static bool nvidia_wmi_ec_present;
+ static bool apple_gmux_present;
+ static bool dell_uart_present;
+ static bool native_available;
static bool init_done;
static long video_caps;
@@ -585,48 +996,73 @@ enum acpi_backlight_type acpi_video_get_backlight_type(void)
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_video, NULL,
&video_caps, NULL);
- INIT_WORK(&backlight_notify_work,
- acpi_video_backlight_notify_work);
- backlight_nb.notifier_call = acpi_video_backlight_notify;
- backlight_nb.priority = 0;
- if (backlight_register_notifier(&backlight_nb) == 0)
- backlight_notifier_registered = true;
+ nvidia_wmi_ec_present = nvidia_wmi_ec_supported();
+ apple_gmux_present = apple_gmux_detect(NULL, NULL);
+ dell_uart_present = acpi_dev_present("DELL0501", NULL, -1);
init_done = true;
}
+ if (native)
+ native_available = true;
mutex_unlock(&init_mutex);
+ if (auto_detect)
+ *auto_detect = false;
+
+ /*
+ * The below heuristics / detection steps are in order of descending
+ * presedence. The commandline takes presedence over anything else.
+ */
if (acpi_backlight_cmdline != acpi_backlight_undef)
return acpi_backlight_cmdline;
+ /* DMI quirks override any autodetection. */
if (acpi_backlight_dmi != acpi_backlight_undef)
return acpi_backlight_dmi;
- if (!(video_caps & ACPI_VIDEO_BACKLIGHT))
- return acpi_backlight_vendor;
+ if (auto_detect)
+ *auto_detect = true;
- if (acpi_osi_is_win8() && backlight_device_get_by_type(BACKLIGHT_RAW))
- return acpi_backlight_native;
+ /* Special cases such as nvidia_wmi_ec and apple gmux. */
+ if (nvidia_wmi_ec_present)
+ return acpi_backlight_nvidia_wmi_ec;
- return acpi_backlight_video;
-}
-EXPORT_SYMBOL(acpi_video_get_backlight_type);
+ if (apple_gmux_present)
+ return acpi_backlight_apple_gmux;
-/*
- * Set the preferred backlight interface type based on DMI info.
- * This function allows DMI blacklists to be implemented by external
- * platform drivers instead of putting a big blacklist in video_detect.c
- */
-void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type)
-{
- acpi_backlight_dmi = type;
- /* Remove acpi-video backlight interface if it is no longer desired */
- if (acpi_video_get_backlight_type() != acpi_backlight_video)
- acpi_video_unregister_backlight();
-}
-EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type);
+ if (dell_uart_present)
+ return acpi_backlight_dell_uart;
-void __exit acpi_video_detect_exit(void)
-{
- if (backlight_notifier_registered)
- backlight_unregister_notifier(&backlight_nb);
+ /* Use ACPI video if available, except when native should be preferred. */
+ if ((video_caps & ACPI_VIDEO_BACKLIGHT) &&
+ !(native_available && prefer_native_over_acpi_video()))
+ return acpi_backlight_video;
+
+ /* Use native if available */
+ if (native_available)
+ return acpi_backlight_native;
+
+ /*
+ * The vendor specific BIOS interfaces are only necessary for
+ * laptops from before ~2008.
+ *
+ * For laptops from ~2008 till ~2023 this point is never reached
+ * because on those (video_caps & ACPI_VIDEO_BACKLIGHT) above is true.
+ *
+ * Laptops from after ~2023 no longer support ACPI_VIDEO_BACKLIGHT,
+ * if this point is reached on those, this likely means that
+ * the GPU kms driver which sets native_available has not loaded yet.
+ *
+ * Returning acpi_backlight_vendor in this case is known to sometimes
+ * cause a non working vendor specific /sys/class/backlight device to
+ * get registered.
+ *
+ * Return acpi_backlight_none on laptops with ACPI tables written
+ * for Windows 8 (laptops from after ~2012) to avoid this problem.
+ */
+ if (acpi_osi_is_win8())
+ return acpi_backlight_none;
+
+ /* No ACPI video/native (old hw), use vendor specific fw methods. */
+ return acpi_backlight_vendor;
}
+EXPORT_SYMBOL(__acpi_video_get_backlight_type);
diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c
index 6132092dab2a..c13a20365c2c 100644
--- a/drivers/acpi/viot.c
+++ b/drivers/acpi/viot.c
@@ -19,12 +19,11 @@
#define pr_fmt(fmt) "ACPI: VIOT: " fmt
#include <linux/acpi_viot.h>
-#include <linux/dma-iommu.h>
-#include <linux/fwnode.h>
#include <linux/iommu.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
struct viot_iommu {
/* Node offset within the table */
@@ -308,27 +307,21 @@ void __init acpi_viot_init(void)
static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
u32 epid)
{
- const struct iommu_ops *ops;
-
- if (!viommu)
+ if (!viommu || !IS_ENABLED(CONFIG_VIRTIO_IOMMU))
return -ENODEV;
/* We're not translating ourself */
if (device_match_fwnode(dev, viommu->fwnode))
return -EINVAL;
- ops = iommu_ops_from_fwnode(viommu->fwnode);
- if (!ops)
- return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
- -EPROBE_DEFER : -ENODEV;
-
- return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
+ return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode);
}
static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
{
u32 epid;
struct viot_endpoint *ep;
+ struct device *aliased_dev = data;
u32 domain_nr = pci_domain_nr(pdev->bus);
list_for_each_entry(ep, &viot_pci_ranges, list) {
@@ -339,7 +332,7 @@ static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
epid = ((domain_nr - ep->segment_start) << 16) +
dev_id - ep->bdf_start + ep->endpoint_id;
- return viot_dev_iommu_init(&pdev->dev, ep->viommu,
+ return viot_dev_iommu_init(aliased_dev, ep->viommu,
epid);
}
}
@@ -373,7 +366,7 @@ int viot_iommu_configure(struct device *dev)
{
if (dev_is_pci(dev))
return pci_for_each_dma_alias(to_pci_dev(dev),
- viot_pci_dev_iommu_init, NULL);
+ viot_pci_dev_iommu_init, dev);
else if (dev_is_platform(dev))
return viot_mmio_dev_iommu_init(to_platform_device(dev));
return -ENODEV;
diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c
index b02bf770aead..ff6dc957bc11 100644
--- a/drivers/acpi/wakeup.c
+++ b/drivers/acpi/wakeup.c
@@ -42,7 +42,7 @@ void acpi_enable_wakeup_devices(u8 sleep_state)
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
wakeup_list) {
if (!dev->wakeup.flags.valid
- || sleep_state > (u32) dev->wakeup.sleep_state
+ || sleep_state > dev->wakeup.sleep_state
|| !(device_may_wakeup(&dev->dev)
|| dev->wakeup.prepare_count))
continue;
@@ -67,7 +67,7 @@ void acpi_disable_wakeup_devices(u8 sleep_state)
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
wakeup_list) {
if (!dev->wakeup.flags.valid
- || sleep_state > (u32) dev->wakeup.sleep_state
+ || sleep_state > dev->wakeup.sleep_state
|| !(device_may_wakeup(&dev->dev)
|| dev->wakeup.prepare_count))
continue;
diff --git a/drivers/acpi/x86/Makefile b/drivers/acpi/x86/Makefile
new file mode 100644
index 000000000000..63c99509ed9d
--- /dev/null
+++ b/drivers/acpi/x86/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_ACPI) += acpi-x86.o
+acpi-x86-y += apple.o
+acpi-x86-y += cmos_rtc.o
+acpi-x86-$(CONFIG_PCI) += lpss.o
+acpi-x86-y += s2idle.o
+acpi-x86-y += utils.o
+
+obj-$(CONFIG_X86) += blacklist.o
diff --git a/drivers/acpi/x86/apple.c b/drivers/acpi/x86/apple.c
index c285c91a5e9c..45d0f16f374f 100644
--- a/drivers/acpi/x86/apple.c
+++ b/drivers/acpi/x86/apple.c
@@ -8,6 +8,7 @@
#include <linux/bitmap.h>
#include <linux/platform_data/x86/apple.h>
#include <linux/uuid.h>
+#include "../internal.h"
/* Apple _DSM device properties GUID */
static const guid_t apple_prp_guid =
@@ -70,13 +71,16 @@ void acpi_extract_apple_properties(struct acpi_device *adev)
if ( key->type != ACPI_TYPE_STRING ||
(val->type != ACPI_TYPE_INTEGER &&
- val->type != ACPI_TYPE_BUFFER))
+ val->type != ACPI_TYPE_BUFFER &&
+ val->type != ACPI_TYPE_STRING))
continue; /* skip invalid properties */
__set_bit(i, valid);
newsize += key->string.length + 1;
if ( val->type == ACPI_TYPE_BUFFER)
newsize += val->buffer.length;
+ else if (val->type == ACPI_TYPE_STRING)
+ newsize += val->string.length + 1;
}
numvalid = bitmap_weight(valid, numprops);
@@ -118,6 +122,12 @@ void acpi_extract_apple_properties(struct acpi_device *adev)
newprops[v].type = val->type;
if (val->type == ACPI_TYPE_INTEGER) {
newprops[v].integer.value = val->integer.value;
+ } else if (val->type == ACPI_TYPE_STRING) {
+ newprops[v].string.length = val->string.length;
+ newprops[v].string.pointer = free_space;
+ memcpy(free_space, val->string.pointer,
+ val->string.length);
+ free_space += val->string.length + 1;
} else {
newprops[v].buffer.length = val->buffer.length;
newprops[v].buffer.pointer = free_space;
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/x86/blacklist.c
index a558d24fb788..55214d0a12b1 100644
--- a/drivers/acpi/blacklist.c
+++ b/drivers/acpi/x86/blacklist.c
@@ -17,7 +17,7 @@
#include <linux/acpi.h>
#include <linux/dmi.h>
-#include "internal.h"
+#include "../internal.h"
#ifdef CONFIG_DMI
static const struct dmi_system_id acpi_rev_dmi_table[] __initconst;
diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c
new file mode 100644
index 000000000000..51643ff6fe5f
--- /dev/null
+++ b/drivers/acpi/x86/cmos_rtc.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI support for CMOS RTC Address Space access
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Authors: Lan Tianyu <tianyu.lan@intel.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mc146818rtc.h>
+
+#include "../internal.h"
+
+static const struct acpi_device_id acpi_cmos_rtc_ids[] = {
+ { "PNP0B00" },
+ { "PNP0B01" },
+ { "PNP0B02" },
+ {}
+};
+
+static acpi_status
+acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address,
+ u32 bits, u64 *value64,
+ void *handler_context, void *region_context)
+{
+ int i;
+ u8 *value = (u8 *)value64;
+
+ if (address > 0xff || !value64)
+ return AE_BAD_PARAMETER;
+
+ if (function != ACPI_WRITE && function != ACPI_READ)
+ return AE_BAD_PARAMETER;
+
+ spin_lock_irq(&rtc_lock);
+
+ for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value)
+ if (function == ACPI_READ)
+ *value = CMOS_READ(address);
+ else
+ CMOS_WRITE(*value, address);
+
+ spin_unlock_irq(&rtc_lock);
+
+ return AE_OK;
+}
+
+int acpi_install_cmos_rtc_space_handler(acpi_handle handle)
+{
+ acpi_status status;
+
+ status = acpi_install_address_space_handler(handle,
+ ACPI_ADR_SPACE_CMOS,
+ &acpi_cmos_rtc_space_handler,
+ NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Error installing CMOS-RTC region handler\n");
+ return -ENODEV;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler);
+
+void acpi_remove_cmos_rtc_space_handler(acpi_handle handle)
+{
+ if (ACPI_FAILURE(acpi_remove_address_space_handler(handle,
+ ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler)))
+ pr_err("Error removing CMOS-RTC region handler\n");
+}
+EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler);
+
+static int acpi_cmos_rtc_attach_handler(struct acpi_device *adev, const struct acpi_device_id *id)
+{
+ return acpi_install_cmos_rtc_space_handler(adev->handle);
+}
+
+static void acpi_cmos_rtc_detach_handler(struct acpi_device *adev)
+{
+ acpi_remove_cmos_rtc_space_handler(adev->handle);
+}
+
+static struct acpi_scan_handler cmos_rtc_handler = {
+ .ids = acpi_cmos_rtc_ids,
+ .attach = acpi_cmos_rtc_attach_handler,
+ .detach = acpi_cmos_rtc_detach_handler,
+};
+
+void __init acpi_cmos_rtc_init(void)
+{
+ acpi_scan_add_handler(&cmos_rtc_handler);
+}
diff --git a/drivers/acpi/x86/lpss.c b/drivers/acpi/x86/lpss.c
new file mode 100644
index 000000000000..1dcb80ab0d23
--- /dev/null
+++ b/drivers/acpi/x86/lpss.c
@@ -0,0 +1,1352 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI support for Intel Lynxpoint LPSS.
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/x86/clk-lpss.h>
+#include <linux/platform_data/x86/pmc_atom.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/suspend.h>
+#include <linux/delay.h>
+
+#include "../internal.h"
+
+#ifdef CONFIG_X86_INTEL_LPSS
+
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include <asm/iosf_mbi.h>
+
+#define LPSS_ADDR(desc) ((unsigned long)&desc)
+
+#define LPSS_CLK_SIZE 0x04
+#define LPSS_LTR_SIZE 0x18
+
+/* Offsets relative to LPSS_PRIVATE_OFFSET */
+#define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16))
+#define LPSS_RESETS 0x04
+#define LPSS_RESETS_RESET_FUNC BIT(0)
+#define LPSS_RESETS_RESET_APB BIT(1)
+#define LPSS_GENERAL 0x08
+#define LPSS_GENERAL_LTR_MODE_SW BIT(2)
+#define LPSS_GENERAL_UART_RTS_OVRD BIT(3)
+#define LPSS_SW_LTR 0x10
+#define LPSS_AUTO_LTR 0x14
+#define LPSS_LTR_SNOOP_REQ BIT(15)
+#define LPSS_LTR_SNOOP_MASK 0x0000FFFF
+#define LPSS_LTR_SNOOP_LAT_1US 0x800
+#define LPSS_LTR_SNOOP_LAT_32US 0xC00
+#define LPSS_LTR_SNOOP_LAT_SHIFT 5
+#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000
+#define LPSS_LTR_MAX_VAL 0x3FF
+#define LPSS_TX_INT 0x20
+#define LPSS_TX_INT_MASK BIT(1)
+
+#define LPSS_PRV_REG_COUNT 9
+
+/* LPSS Flags */
+#define LPSS_CLK BIT(0)
+#define LPSS_CLK_GATE BIT(1)
+#define LPSS_CLK_DIVIDER BIT(2)
+#define LPSS_LTR BIT(3)
+#define LPSS_SAVE_CTX BIT(4)
+/*
+ * For some devices the DSDT AML code for another device turns off the device
+ * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff)
+ * as ctx register values.
+ * Luckily these devices always use the same ctx register values, so we can
+ * work around this by saving the ctx registers once on activation.
+ */
+#define LPSS_SAVE_CTX_ONCE BIT(5)
+#define LPSS_NO_D3_DELAY BIT(6)
+
+struct lpss_private_data;
+
+struct lpss_device_desc {
+ unsigned int flags;
+ const char *clk_con_id;
+ unsigned int prv_offset;
+ size_t prv_size_override;
+ const struct property_entry *properties;
+ void (*setup)(struct lpss_private_data *pdata);
+ bool resume_from_noirq;
+};
+
+static const struct lpss_device_desc lpss_dma_desc = {
+ .flags = LPSS_CLK,
+};
+
+struct lpss_private_data {
+ struct acpi_device *adev;
+ void __iomem *mmio_base;
+ resource_size_t mmio_size;
+ unsigned int fixed_clk_rate;
+ struct clk *clk;
+ const struct lpss_device_desc *dev_desc;
+ u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
+};
+
+/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */
+static u32 pmc_atom_d3_mask = 0xfe000ffe;
+
+/* LPSS run time quirks */
+static unsigned int lpss_quirks;
+
+/*
+ * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device.
+ *
+ * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover
+ * it can be powered off automatically whenever the last LPSS device goes down.
+ * In case of no power any access to the DMA controller will hang the system.
+ * The behaviour is reproduced on some HP laptops based on Intel BayTrail as
+ * well as on ASuS T100TA transformer.
+ *
+ * This quirk overrides power state of entire LPSS island to keep DMA powered
+ * on whenever we have at least one other device in use.
+ */
+#define LPSS_QUIRK_ALWAYS_POWER_ON BIT(0)
+
+/* UART Component Parameter Register */
+#define LPSS_UART_CPR 0xF4
+#define LPSS_UART_CPR_AFCE BIT(4)
+
+static void lpss_uart_setup(struct lpss_private_data *pdata)
+{
+ unsigned int offset;
+ u32 val;
+
+ offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
+ val = readl(pdata->mmio_base + offset);
+ writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
+
+ val = readl(pdata->mmio_base + LPSS_UART_CPR);
+ if (!(val & LPSS_UART_CPR_AFCE)) {
+ offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
+ val = readl(pdata->mmio_base + offset);
+ val |= LPSS_GENERAL_UART_RTS_OVRD;
+ writel(val, pdata->mmio_base + offset);
+ }
+}
+
+static void lpss_deassert_reset(struct lpss_private_data *pdata)
+{
+ unsigned int offset;
+ u32 val;
+
+ offset = pdata->dev_desc->prv_offset + LPSS_RESETS;
+ val = readl(pdata->mmio_base + offset);
+ val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC;
+ writel(val, pdata->mmio_base + offset);
+}
+
+/*
+ * BYT PWM used for backlight control by the i915 driver on systems without
+ * the Crystal Cove PMIC.
+ */
+static struct pwm_lookup byt_pwm_lookup[] = {
+ PWM_LOOKUP_WITH_MODULE("80860F09:00", 0, "0000:00:02.0",
+ "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
+ "pwm-lpss-platform"),
+};
+
+static void byt_pwm_setup(struct lpss_private_data *pdata)
+{
+ /* Only call pwm_add_table for the first PWM controller */
+ if (acpi_dev_uid_match(pdata->adev, 1))
+ pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup));
+}
+
+#define LPSS_I2C_ENABLE 0x6c
+
+static void byt_i2c_setup(struct lpss_private_data *pdata)
+{
+ acpi_handle handle = pdata->adev->handle;
+ unsigned long long shared_host = 0;
+ acpi_status status;
+ u64 uid;
+
+ /* Expected to always be successful, but better safe then sorry */
+ if (!acpi_dev_uid_to_integer(pdata->adev, &uid) && uid) {
+ /* Detect I2C bus shared with PUNIT and ignore its d3 status */
+ status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
+ if (ACPI_SUCCESS(status) && shared_host)
+ pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1));
+ }
+
+ lpss_deassert_reset(pdata);
+
+ if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
+ pdata->fixed_clk_rate = 133000000;
+
+ writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
+}
+
+/*
+ * BSW PWM1 is used for backlight control by the i915 driver
+ * BSW PWM2 is used for backlight control for fixed (etched into the glass)
+ * touch controls on some models. These touch-controls have specialized
+ * drivers which know they need the "pwm_soc_lpss_2" con-id.
+ */
+static struct pwm_lookup bsw_pwm_lookup[] = {
+ PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0",
+ "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
+ "pwm-lpss-platform"),
+ PWM_LOOKUP_WITH_MODULE("80862289:00", 0, NULL,
+ "pwm_soc_lpss_2", 0, PWM_POLARITY_NORMAL,
+ "pwm-lpss-platform"),
+};
+
+static void bsw_pwm_setup(struct lpss_private_data *pdata)
+{
+ /* Only call pwm_add_table for the first PWM controller */
+ if (acpi_dev_uid_match(pdata->adev, 1))
+ pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup));
+}
+
+static const struct property_entry lpt_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_LPT_SSP),
+ { }
+};
+
+static const struct lpss_device_desc lpt_spi_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR
+ | LPSS_SAVE_CTX,
+ .prv_offset = 0x800,
+ .properties = lpt_spi_properties,
+};
+
+static const struct lpss_device_desc lpt_i2c_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR | LPSS_SAVE_CTX,
+ .prv_offset = 0x800,
+};
+
+static struct property_entry uart_properties[] = {
+ PROPERTY_ENTRY_U32("reg-io-width", 4),
+ PROPERTY_ENTRY_U32("reg-shift", 2),
+ PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
+ { },
+};
+
+static const struct lpss_device_desc lpt_uart_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR
+ | LPSS_SAVE_CTX,
+ .clk_con_id = "baudclk",
+ .prv_offset = 0x800,
+ .setup = lpss_uart_setup,
+ .properties = uart_properties,
+};
+
+static const struct lpss_device_desc lpt_sdio_dev_desc = {
+ .flags = LPSS_LTR,
+ .prv_offset = 0x1000,
+ .prv_size_override = 0x1018,
+};
+
+static const struct lpss_device_desc byt_pwm_dev_desc = {
+ .flags = LPSS_SAVE_CTX,
+ .prv_offset = 0x800,
+ .setup = byt_pwm_setup,
+};
+
+static const struct lpss_device_desc bsw_pwm_dev_desc = {
+ .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY,
+ .prv_offset = 0x800,
+ .setup = bsw_pwm_setup,
+ .resume_from_noirq = true,
+};
+
+static const struct lpss_device_desc bsw_pwm2_dev_desc = {
+ .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY,
+ .prv_offset = 0x800,
+ .resume_from_noirq = true,
+};
+
+static const struct lpss_device_desc byt_uart_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
+ .clk_con_id = "baudclk",
+ .prv_offset = 0x800,
+ .setup = lpss_uart_setup,
+ .properties = uart_properties,
+};
+
+static const struct lpss_device_desc bsw_uart_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
+ | LPSS_NO_D3_DELAY,
+ .clk_con_id = "baudclk",
+ .prv_offset = 0x800,
+ .setup = lpss_uart_setup,
+ .properties = uart_properties,
+};
+
+static const struct property_entry byt_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BYT_SSP),
+ { }
+};
+
+static const struct lpss_device_desc byt_spi_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
+ .prv_offset = 0x400,
+ .properties = byt_spi_properties,
+};
+
+static const struct lpss_device_desc byt_sdio_dev_desc = {
+ .flags = LPSS_CLK,
+};
+
+static const struct lpss_device_desc byt_i2c_dev_desc = {
+ .flags = LPSS_CLK | LPSS_SAVE_CTX,
+ .prv_offset = 0x800,
+ .setup = byt_i2c_setup,
+ .resume_from_noirq = true,
+};
+
+static const struct lpss_device_desc bsw_i2c_dev_desc = {
+ .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
+ .prv_offset = 0x800,
+ .setup = byt_i2c_setup,
+ .resume_from_noirq = true,
+};
+
+static const struct property_entry bsw_spi_properties[] = {
+ PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP),
+ PROPERTY_ENTRY_U32("num-cs", 2),
+ { }
+};
+
+static const struct lpss_device_desc bsw_spi_dev_desc = {
+ .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
+ | LPSS_NO_D3_DELAY,
+ .prv_offset = 0x400,
+ .setup = lpss_deassert_reset,
+ .properties = bsw_spi_properties,
+};
+
+static const struct x86_cpu_id lpss_cpu_ids[] = {
+ X86_MATCH_VFM(INTEL_ATOM_SILVERMONT, NULL),
+ X86_MATCH_VFM(INTEL_ATOM_AIRMONT, NULL),
+ {}
+};
+
+#else
+
+#define LPSS_ADDR(desc) (0UL)
+
+#endif /* CONFIG_X86_INTEL_LPSS */
+
+static const struct acpi_device_id acpi_lpss_device_ids[] = {
+ /* Generic LPSS devices */
+ { "INTL9C60", LPSS_ADDR(lpss_dma_desc) },
+
+ /* Lynxpoint LPSS devices */
+ { "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) },
+ { "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) },
+ { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) },
+ { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) },
+ { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) },
+ { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) },
+ { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) },
+
+ /* BayTrail LPSS devices */
+ { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) },
+ { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) },
+ { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) },
+ { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) },
+ { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) },
+
+ /* Braswell LPSS devices */
+ { "80862286", LPSS_ADDR(lpss_dma_desc) },
+ { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
+ { "80862289", LPSS_ADDR(bsw_pwm2_dev_desc) },
+ { "8086228A", LPSS_ADDR(bsw_uart_dev_desc) },
+ { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) },
+ { "808622C0", LPSS_ADDR(lpss_dma_desc) },
+ { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) },
+
+ /* Broadwell LPSS devices */
+ { "INT3430", LPSS_ADDR(lpt_spi_dev_desc) },
+ { "INT3431", LPSS_ADDR(lpt_spi_dev_desc) },
+ { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) },
+ { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) },
+ { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) },
+ { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) },
+ { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
+
+ { }
+};
+
+#ifdef CONFIG_X86_INTEL_LPSS
+
+/* LPSS main clock device. */
+static struct platform_device *lpss_clk_dev;
+
+static inline void lpt_register_clock_device(void)
+{
+ lpss_clk_dev = platform_device_register_simple("clk-lpss-atom",
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+}
+
+static int register_device_clock(struct acpi_device *adev,
+ struct lpss_private_data *pdata)
+{
+ const struct lpss_device_desc *dev_desc = pdata->dev_desc;
+ const char *devname = dev_name(&adev->dev);
+ struct clk *clk;
+ struct lpss_clk_data *clk_data;
+ const char *parent, *clk_name;
+ void __iomem *prv_base;
+
+ if (!lpss_clk_dev)
+ lpt_register_clock_device();
+
+ if (IS_ERR(lpss_clk_dev))
+ return PTR_ERR(lpss_clk_dev);
+
+ clk_data = platform_get_drvdata(lpss_clk_dev);
+ if (!clk_data)
+ return -ENODEV;
+ clk = clk_data->clk;
+
+ if (!pdata->mmio_base
+ || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
+ return -ENODATA;
+
+ parent = clk_data->name;
+ prv_base = pdata->mmio_base + dev_desc->prv_offset;
+
+ if (pdata->fixed_clk_rate) {
+ clk = clk_register_fixed_rate(NULL, devname, parent, 0,
+ pdata->fixed_clk_rate);
+ goto out;
+ }
+
+ if (dev_desc->flags & LPSS_CLK_GATE) {
+ clk = clk_register_gate(NULL, devname, parent, 0,
+ prv_base, 0, 0, NULL);
+ parent = devname;
+ }
+
+ if (dev_desc->flags & LPSS_CLK_DIVIDER) {
+ /* Prevent division by zero */
+ if (!readl(prv_base))
+ writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
+
+ clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
+ if (!clk_name)
+ return -ENOMEM;
+ clk = clk_register_fractional_divider(NULL, clk_name, parent,
+ 0, prv_base, 1, 15, 16, 15,
+ CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
+ NULL);
+ parent = clk_name;
+
+ clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
+ if (!clk_name) {
+ kfree(parent);
+ return -ENOMEM;
+ }
+ clk = clk_register_gate(NULL, clk_name, parent,
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+ prv_base, 31, 0, NULL);
+ kfree(parent);
+ kfree(clk_name);
+ }
+out:
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ pdata->clk = clk;
+ clk_register_clkdev(clk, dev_desc->clk_con_id, devname);
+ return 0;
+}
+
+struct lpss_device_links {
+ const char *supplier_hid;
+ const char *supplier_uid;
+ const char *consumer_hid;
+ const char *consumer_uid;
+ u32 flags;
+ const struct dmi_system_id *dep_missing_ids;
+};
+
+/* Please keep this list sorted alphabetically by vendor and model */
+static const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"),
+ },
+ },
+ {}
+};
+
+/*
+ * The _DEP method is used to identify dependencies but instead of creating
+ * device links for every handle in _DEP, only links in the following list are
+ * created. That is necessary because, in the general case, _DEP can refer to
+ * devices that might not have drivers, or that are on different buses, or where
+ * the supplier is not enumerated until after the consumer is probed.
+ */
+static const struct lpss_device_links lpss_device_links[] = {
+ /* CHT External sdcard slot controller depends on PMIC I2C ctrl */
+ {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME},
+ /* CHT iGPU depends on PMIC I2C controller */
+ {"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
+ /* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */
+ {"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME,
+ i2c1_dep_missing_dmi_ids},
+ /* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */
+ {"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
+ /* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */
+ {"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
+};
+
+static bool acpi_lpss_is_supplier(struct acpi_device *adev,
+ const struct lpss_device_links *link)
+{
+ return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid);
+}
+
+static bool acpi_lpss_is_consumer(struct acpi_device *adev,
+ const struct lpss_device_links *link)
+{
+ return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid);
+}
+
+struct hid_uid {
+ const char *hid;
+ const char *uid;
+};
+
+static int match_hid_uid(struct device *dev, const void *data)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ const struct hid_uid *id = data;
+
+ if (!adev)
+ return 0;
+
+ return acpi_dev_hid_uid_match(adev, id->hid, id->uid);
+}
+
+static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
+{
+ struct device *dev;
+
+ struct hid_uid data = {
+ .hid = hid,
+ .uid = uid,
+ };
+
+ dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
+ if (dev)
+ return dev;
+
+ return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
+}
+
+static void acpi_lpss_link_consumer(struct device *dev1,
+ const struct lpss_device_links *link)
+{
+ struct device *dev2;
+
+ dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
+ if (!dev2)
+ return;
+
+ if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
+ || acpi_device_dep(ACPI_HANDLE(dev2), ACPI_HANDLE(dev1)))
+ device_link_add(dev2, dev1, link->flags);
+
+ put_device(dev2);
+}
+
+static void acpi_lpss_link_supplier(struct device *dev1,
+ const struct lpss_device_links *link)
+{
+ struct device *dev2;
+
+ dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
+ if (!dev2)
+ return;
+
+ if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
+ || acpi_device_dep(ACPI_HANDLE(dev1), ACPI_HANDLE(dev2)))
+ device_link_add(dev1, dev2, link->flags);
+
+ put_device(dev2);
+}
+
+static void acpi_lpss_create_device_links(struct acpi_device *adev,
+ struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
+ const struct lpss_device_links *link = &lpss_device_links[i];
+
+ if (acpi_lpss_is_supplier(adev, link))
+ acpi_lpss_link_consumer(&pdev->dev, link);
+
+ if (acpi_lpss_is_consumer(adev, link))
+ acpi_lpss_link_supplier(&pdev->dev, link);
+ }
+}
+
+static int acpi_lpss_create_device(struct acpi_device *adev,
+ const struct acpi_device_id *id)
+{
+ const struct lpss_device_desc *dev_desc;
+ struct lpss_private_data *pdata;
+ struct resource_entry *rentry;
+ struct list_head resource_list;
+ struct platform_device *pdev;
+ int ret;
+
+ dev_desc = (const struct lpss_device_desc *)id->driver_data;
+ if (!dev_desc)
+ return -EINVAL;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_memory_resources(adev, &resource_list);
+ if (ret < 0)
+ goto err_out;
+
+ rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node);
+ if (rentry) {
+ if (dev_desc->prv_size_override)
+ pdata->mmio_size = dev_desc->prv_size_override;
+ else
+ pdata->mmio_size = resource_size(rentry->res);
+ pdata->mmio_base = ioremap(rentry->res->start, pdata->mmio_size);
+ }
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ if (!pdata->mmio_base) {
+ /* Avoid acpi_bus_attach() instantiating a pdev for this dev. */
+ adev->pnp.type.platform_id = 0;
+ goto out_free;
+ }
+
+ pdata->adev = adev;
+ pdata->dev_desc = dev_desc;
+
+ if (dev_desc->setup)
+ dev_desc->setup(pdata);
+
+ if (dev_desc->flags & LPSS_CLK) {
+ ret = register_device_clock(adev, pdata);
+ if (ret)
+ goto out_free;
+ }
+
+ /*
+ * This works around a known issue in ACPI tables where LPSS devices
+ * have _PS0 and _PS3 without _PSC (and no power resources), so
+ * acpi_bus_init_power() will assume that the BIOS has put them into D0.
+ */
+ acpi_device_fix_up_power(adev);
+
+ adev->driver_data = pdata;
+ pdev = acpi_create_platform_device(adev, dev_desc->properties);
+ if (IS_ERR_OR_NULL(pdev)) {
+ adev->driver_data = NULL;
+ ret = PTR_ERR(pdev);
+ goto err_out;
+ }
+
+ acpi_lpss_create_device_links(adev, pdev);
+ return 1;
+
+out_free:
+ /* Skip the device, but continue the namespace scan */
+ ret = 0;
+err_out:
+ kfree(pdata);
+ return ret;
+}
+
+static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
+{
+ return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
+}
+
+static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
+ unsigned int reg)
+{
+ writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
+}
+
+static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct lpss_private_data *pdata;
+ unsigned long flags;
+ int ret;
+
+ if (WARN_ON(!adev))
+ return -ENODEV;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+ if (pm_runtime_suspended(dev)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ pdata = acpi_driver_data(adev);
+ if (WARN_ON(!pdata || !pdata->mmio_base)) {
+ ret = -ENODEV;
+ goto out;
+ }
+ *val = __lpss_reg_read(pdata, reg);
+ ret = 0;
+
+ out:
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+ return ret;
+}
+
+static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u32 ltr_value = 0;
+ unsigned int reg;
+ int ret;
+
+ reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR;
+ ret = lpss_reg_read(dev, reg, &ltr_value);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%08x\n", ltr_value);
+}
+
+static ssize_t lpss_ltr_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 ltr_mode = 0;
+ char *outstr;
+ int ret;
+
+ ret = lpss_reg_read(dev, LPSS_GENERAL, &ltr_mode);
+ if (ret)
+ return ret;
+
+ outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto";
+ return sprintf(buf, "%s\n", outstr);
+}
+
+static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL);
+static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL);
+static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL);
+
+static struct attribute *lpss_attrs[] = {
+ &dev_attr_auto_ltr.attr,
+ &dev_attr_sw_ltr.attr,
+ &dev_attr_ltr_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group lpss_attr_group = {
+ .attrs = lpss_attrs,
+ .name = "lpss_ltr",
+};
+
+static void acpi_lpss_set_ltr(struct device *dev, s32 val)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ u32 ltr_mode, ltr_val;
+
+ ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
+ if (val < 0) {
+ if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
+ ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
+ __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
+ }
+ return;
+ }
+ ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
+ if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
+ ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
+ val = LPSS_LTR_MAX_VAL;
+ } else if (val > LPSS_LTR_MAX_VAL) {
+ ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
+ val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
+ } else {
+ ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
+ }
+ ltr_val |= val;
+ __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
+ if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
+ ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
+ __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
+ }
+}
+
+#ifdef CONFIG_PM
+/**
+ * acpi_lpss_save_ctx() - Save the private registers of LPSS device
+ * @dev: LPSS device
+ * @pdata: pointer to the private data of the LPSS device
+ *
+ * Most LPSS devices have private registers which may loose their context when
+ * the device is powered down. acpi_lpss_save_ctx() saves those registers into
+ * prv_reg_ctx array.
+ */
+static void acpi_lpss_save_ctx(struct device *dev,
+ struct lpss_private_data *pdata)
+{
+ unsigned int i;
+
+ for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+ unsigned long offset = i * sizeof(u32);
+
+ pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset);
+ dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n",
+ pdata->prv_reg_ctx[i], offset);
+ }
+}
+
+/**
+ * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device
+ * @dev: LPSS device
+ * @pdata: pointer to the private data of the LPSS device
+ *
+ * Restores the registers that were previously stored with acpi_lpss_save_ctx().
+ */
+static void acpi_lpss_restore_ctx(struct device *dev,
+ struct lpss_private_data *pdata)
+{
+ unsigned int i;
+
+ for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+ unsigned long offset = i * sizeof(u32);
+
+ __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
+ dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
+ pdata->prv_reg_ctx[i], offset);
+ }
+}
+
+static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata)
+{
+ /*
+ * The following delay is needed or the subsequent write operations may
+ * fail. The LPSS devices are actually PCI devices and the PCI spec
+ * expects 10ms delay before the device can be accessed after D3 to D0
+ * transition. However some platforms like BSW does not need this delay.
+ */
+ unsigned int delay = 10; /* default 10ms delay */
+
+ if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY)
+ delay = 0;
+
+ msleep(delay);
+}
+
+static int acpi_lpss_activate(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ ret = acpi_dev_resume(dev);
+ if (ret)
+ return ret;
+
+ acpi_lpss_d3_to_d0_delay(pdata);
+
+ /*
+ * This is called only on ->probe() stage where a device is either in
+ * known state defined by BIOS or most likely powered off. Due to this
+ * we have to deassert reset line to be sure that ->probe() will
+ * recognize the device.
+ */
+ if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
+ lpss_deassert_reset(pdata);
+
+ if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE)
+ acpi_lpss_save_ctx(dev, pdata);
+
+ return 0;
+}
+
+static void acpi_lpss_dismiss(struct device *dev)
+{
+ acpi_dev_suspend(dev, false);
+}
+
+/* IOSF SB for LPSS island */
+#define LPSS_IOSF_UNIT_LPIOEP 0xA0
+#define LPSS_IOSF_UNIT_LPIO1 0xAB
+#define LPSS_IOSF_UNIT_LPIO2 0xAC
+
+#define LPSS_IOSF_PMCSR 0x84
+#define LPSS_PMCSR_D0 0
+#define LPSS_PMCSR_D3hot 3
+#define LPSS_PMCSR_Dx_MASK GENMASK(1, 0)
+
+#define LPSS_IOSF_GPIODEF0 0x154
+#define LPSS_GPIODEF0_DMA1_D3 BIT(2)
+#define LPSS_GPIODEF0_DMA2_D3 BIT(3)
+#define LPSS_GPIODEF0_DMA_D3_MASK GENMASK(3, 2)
+#define LPSS_GPIODEF0_DMA_LLP BIT(13)
+
+static DEFINE_MUTEX(lpss_iosf_mutex);
+static bool lpss_iosf_d3_entered = true;
+
+static void lpss_iosf_enter_d3_state(void)
+{
+ u32 value1 = 0;
+ u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
+ u32 value2 = LPSS_PMCSR_D3hot;
+ u32 mask2 = LPSS_PMCSR_Dx_MASK;
+ /*
+ * PMC provides an information about actual status of the LPSS devices.
+ * Here we read the values related to LPSS power island, i.e. LPSS
+ * devices, excluding both LPSS DMA controllers, along with SCC domain.
+ */
+ u32 func_dis, d3_sts_0, pmc_status;
+ int ret;
+
+ ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
+ if (ret)
+ return;
+
+ mutex_lock(&lpss_iosf_mutex);
+
+ ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0);
+ if (ret)
+ goto exit;
+
+ /*
+ * Get the status of entire LPSS power island per device basis.
+ * Shutdown both LPSS DMA controllers if and only if all other devices
+ * are already in D3hot.
+ */
+ pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask;
+ if (pmc_status)
+ goto exit;
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
+ LPSS_IOSF_GPIODEF0, value1, mask1);
+
+ lpss_iosf_d3_entered = true;
+
+exit:
+ mutex_unlock(&lpss_iosf_mutex);
+}
+
+static void lpss_iosf_exit_d3_state(void)
+{
+ u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 |
+ LPSS_GPIODEF0_DMA_LLP;
+ u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
+ u32 value2 = LPSS_PMCSR_D0;
+ u32 mask2 = LPSS_PMCSR_Dx_MASK;
+
+ mutex_lock(&lpss_iosf_mutex);
+
+ if (!lpss_iosf_d3_entered)
+ goto exit;
+
+ lpss_iosf_d3_entered = false;
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
+ LPSS_IOSF_GPIODEF0, value1, mask1);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+exit:
+ mutex_unlock(&lpss_iosf_mutex);
+}
+
+static int acpi_lpss_suspend(struct device *dev, bool wakeup)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
+ acpi_lpss_save_ctx(dev, pdata);
+
+ ret = acpi_dev_suspend(dev, wakeup);
+
+ /*
+ * This call must be last in the sequence, otherwise PMC will return
+ * wrong status for devices being about to be powered off. See
+ * lpss_iosf_enter_d3_state() for further information.
+ */
+ if (acpi_target_system_state() == ACPI_STATE_S0 &&
+ lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
+ lpss_iosf_enter_d3_state();
+
+ return ret;
+}
+
+static int acpi_lpss_resume(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ /*
+ * This call is kept first to be in symmetry with
+ * acpi_lpss_runtime_suspend() one.
+ */
+ if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
+ lpss_iosf_exit_d3_state();
+
+ ret = acpi_dev_resume(dev);
+ if (ret)
+ return ret;
+
+ acpi_lpss_d3_to_d0_delay(pdata);
+
+ if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
+ acpi_lpss_restore_ctx(dev, pdata);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_lpss_do_suspend_late(struct device *dev)
+{
+ int ret;
+
+ if (dev_pm_skip_suspend(dev))
+ return 0;
+
+ ret = pm_generic_suspend_late(dev);
+ return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
+}
+
+static int acpi_lpss_suspend_late(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_suspend_late(dev);
+}
+
+static int acpi_lpss_suspend_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ if (pdata->dev_desc->resume_from_noirq) {
+ /*
+ * The driver's ->suspend_late callback will be invoked by
+ * acpi_lpss_do_suspend_late(), with the assumption that the
+ * driver really wanted to run that code in ->suspend_noirq, but
+ * it could not run after acpi_dev_suspend() and the driver
+ * expected the latter to be called in the "late" phase.
+ */
+ ret = acpi_lpss_do_suspend_late(dev);
+ if (ret)
+ return ret;
+ }
+
+ return acpi_subsys_suspend_noirq(dev);
+}
+
+static int acpi_lpss_do_resume_early(struct device *dev)
+{
+ int ret = acpi_lpss_resume(dev);
+
+ return ret ? ret : pm_generic_resume_early(dev);
+}
+
+static int acpi_lpss_resume_early(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ if (dev_pm_skip_resume(dev))
+ return 0;
+
+ return acpi_lpss_do_resume_early(dev);
+}
+
+static int acpi_lpss_resume_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ /* Follow acpi_subsys_resume_noirq(). */
+ if (dev_pm_skip_resume(dev))
+ return 0;
+
+ ret = pm_generic_resume_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (!pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ /*
+ * The driver's ->resume_early callback will be invoked by
+ * acpi_lpss_do_resume_early(), with the assumption that the driver
+ * really wanted to run that code in ->resume_noirq, but it could not
+ * run before acpi_dev_resume() and the driver expected the latter to be
+ * called in the "early" phase.
+ */
+ return acpi_lpss_do_resume_early(dev);
+}
+
+static int acpi_lpss_do_restore_early(struct device *dev)
+{
+ int ret = acpi_lpss_resume(dev);
+
+ return ret ? ret : pm_generic_restore_early(dev);
+}
+
+static int acpi_lpss_restore_early(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_restore_early(dev);
+}
+
+static int acpi_lpss_restore_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ ret = pm_generic_restore_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (!pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ /* This is analogous to what happens in acpi_lpss_resume_noirq(). */
+ return acpi_lpss_do_restore_early(dev);
+}
+
+static int acpi_lpss_do_poweroff_late(struct device *dev)
+{
+ int ret = pm_generic_poweroff_late(dev);
+
+ return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
+}
+
+static int acpi_lpss_poweroff_late(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (dev_pm_skip_suspend(dev))
+ return 0;
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_poweroff_late(dev);
+}
+
+static int acpi_lpss_poweroff_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (dev_pm_skip_suspend(dev))
+ return 0;
+
+ if (pdata->dev_desc->resume_from_noirq) {
+ /* This is analogous to the acpi_lpss_suspend_noirq() case. */
+ int ret = acpi_lpss_do_poweroff_late(dev);
+
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_poweroff_noirq(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int acpi_lpss_runtime_suspend(struct device *dev)
+{
+ int ret = pm_generic_runtime_suspend(dev);
+
+ return ret ? ret : acpi_lpss_suspend(dev, true);
+}
+
+static int acpi_lpss_runtime_resume(struct device *dev)
+{
+ int ret = acpi_lpss_resume(dev);
+
+ return ret ? ret : pm_generic_runtime_resume(dev);
+}
+#endif /* CONFIG_PM */
+
+static struct dev_pm_domain acpi_lpss_pm_domain = {
+#ifdef CONFIG_PM
+ .activate = acpi_lpss_activate,
+ .dismiss = acpi_lpss_dismiss,
+#endif
+ .ops = {
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+ .prepare = acpi_subsys_prepare,
+ .complete = acpi_subsys_complete,
+ .suspend = acpi_subsys_suspend,
+ .suspend_late = acpi_lpss_suspend_late,
+ .suspend_noirq = acpi_lpss_suspend_noirq,
+ .resume_noirq = acpi_lpss_resume_noirq,
+ .resume_early = acpi_lpss_resume_early,
+ .freeze = acpi_subsys_freeze,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_lpss_poweroff_late,
+ .poweroff_noirq = acpi_lpss_poweroff_noirq,
+ .restore_noirq = acpi_lpss_restore_noirq,
+ .restore_early = acpi_lpss_restore_early,
+#endif
+ .runtime_suspend = acpi_lpss_runtime_suspend,
+ .runtime_resume = acpi_lpss_runtime_resume,
+#endif
+ },
+};
+
+static int acpi_lpss_platform_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct platform_device *pdev = to_platform_device(data);
+ struct lpss_private_data *pdata;
+ struct acpi_device *adev;
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
+ if (!id || !id->driver_data)
+ return 0;
+
+ adev = ACPI_COMPANION(&pdev->dev);
+ if (!adev)
+ return 0;
+
+ pdata = acpi_driver_data(adev);
+ if (!pdata)
+ return 0;
+
+ if (pdata->mmio_base &&
+ pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
+ dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n");
+ return 0;
+ }
+
+ switch (action) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
+ break;
+ case BUS_NOTIFY_DRIVER_NOT_BOUND:
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ dev_pm_domain_set(&pdev->dev, NULL);
+ break;
+ case BUS_NOTIFY_ADD_DEVICE:
+ dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
+ if (pdata->dev_desc->flags & LPSS_LTR)
+ return sysfs_create_group(&pdev->dev.kobj,
+ &lpss_attr_group);
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (pdata->dev_desc->flags & LPSS_LTR)
+ sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
+ dev_pm_domain_set(&pdev->dev, NULL);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block acpi_lpss_nb = {
+ .notifier_call = acpi_lpss_platform_notify,
+};
+
+static void acpi_lpss_bind(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
+ return;
+
+ if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
+ dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
+ else
+ dev_err(dev, "MMIO size insufficient to access LTR\n");
+}
+
+static void acpi_lpss_unbind(struct device *dev)
+{
+ dev->power.set_latency_tolerance = NULL;
+}
+
+static struct acpi_scan_handler lpss_handler = {
+ .ids = acpi_lpss_device_ids,
+ .attach = acpi_lpss_create_device,
+ .bind = acpi_lpss_bind,
+ .unbind = acpi_lpss_unbind,
+};
+
+void __init acpi_lpss_init(void)
+{
+ const struct x86_cpu_id *id;
+ int ret;
+
+ ret = lpss_atom_clk_init();
+ if (ret)
+ return;
+
+ id = x86_match_cpu(lpss_cpu_ids);
+ if (id)
+ lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON;
+
+ bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
+ acpi_scan_add_handler(&lpss_handler);
+}
+
+#else
+
+static struct acpi_scan_handler lpss_handler = {
+ .ids = acpi_lpss_device_ids,
+};
+
+void __init acpi_lpss_init(void)
+{
+ acpi_scan_add_handler(&lpss_handler);
+}
+
+#endif /* CONFIG_X86_INTEL_LPSS */
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index f9ac12b778e6..6d4d06236f61 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -17,6 +17,7 @@
#include <linux/acpi.h>
#include <linux/device.h>
+#include <linux/dmi.h>
#include <linux/suspend.h>
#include "../sleep.h"
@@ -58,6 +59,7 @@ static int lps0_dsm_func_mask;
static guid_t lps0_dsm_guid_microsoft;
static int lps0_dsm_func_mask_microsoft;
+static int lps0_dsm_state;
/* Device constraint entry structure */
struct lpi_device_info {
@@ -92,6 +94,11 @@ static struct lpi_constraints *lpi_constraints_table;
static int lpi_constraints_table_size;
static int rev_id;
+#define for_each_lpi_constraint(entry) \
+ for (int i = 0; \
+ entry = &lpi_constraints_table[i], i < lpi_constraints_table_size; \
+ i++)
+
static void lpi_device_get_constraints_amd(void)
{
union acpi_object *out_obj;
@@ -111,6 +118,12 @@ static void lpi_device_get_constraints_amd(void)
union acpi_object *package = &out_obj->package.elements[i];
if (package->type == ACPI_TYPE_PACKAGE) {
+ if (lpi_constraints_table) {
+ acpi_handle_err(lps0_device_handle,
+ "Duplicate constraints list\n");
+ goto free_acpi_buffer;
+ }
+
lpi_constraints_table = kcalloc(package->package.count,
sizeof(*lpi_constraints_table),
GFP_KERNEL);
@@ -121,17 +134,16 @@ static void lpi_device_get_constraints_amd(void)
acpi_handle_debug(lps0_device_handle,
"LPI: constraints list begin:\n");
- for (j = 0; j < package->package.count; ++j) {
+ for (j = 0; j < package->package.count; j++) {
union acpi_object *info_obj = &package->package.elements[j];
struct lpi_device_constraint_amd dev_info = {};
struct lpi_constraints *list;
acpi_status status;
- for (k = 0; k < info_obj->package.count; ++k) {
- union acpi_object *obj = &info_obj->package.elements[k];
+ list = &lpi_constraints_table[lpi_constraints_table_size];
- list = &lpi_constraints_table[lpi_constraints_table_size];
- list->min_dstate = -1;
+ for (k = 0; k < info_obj->package.count; k++) {
+ union acpi_object *obj = &info_obj->package.elements[k];
switch (k) {
case 0:
@@ -147,27 +159,25 @@ static void lpi_device_get_constraints_amd(void)
dev_info.min_dstate = obj->integer.value;
break;
}
+ }
- if (!dev_info.enabled || !dev_info.name ||
- !dev_info.min_dstate)
- continue;
+ acpi_handle_debug(lps0_device_handle,
+ "Name:%s, Enabled: %d, States: %d, MinDstate: %d\n",
+ dev_info.name,
+ dev_info.enabled,
+ dev_info.function_states,
+ dev_info.min_dstate);
- status = acpi_get_handle(NULL, dev_info.name,
- &list->handle);
- if (ACPI_FAILURE(status))
- continue;
+ if (!dev_info.enabled || !dev_info.name ||
+ !dev_info.min_dstate)
+ continue;
- acpi_handle_debug(lps0_device_handle,
- "Name:%s\n", dev_info.name);
+ status = acpi_get_handle(NULL, dev_info.name, &list->handle);
+ if (ACPI_FAILURE(status))
+ continue;
- list->min_dstate = dev_info.min_dstate;
+ list->min_dstate = dev_info.min_dstate;
- if (list->min_dstate < 0) {
- acpi_handle_debug(lps0_device_handle,
- "Incomplete constraint defined\n");
- continue;
- }
- }
lpi_constraints_table_size++;
}
}
@@ -212,7 +222,7 @@ static void lpi_device_get_constraints(void)
if (!package)
continue;
- for (j = 0; j < package->package.count; ++j) {
+ for (j = 0; j < package->package.count; j++) {
union acpi_object *element =
&(package->package.elements[j]);
@@ -244,7 +254,7 @@ static void lpi_device_get_constraints(void)
constraint->min_dstate = -1;
- for (j = 0; j < package_count; ++j) {
+ for (j = 0; j < package_count; j++) {
union acpi_object *info_obj = &info.package[j];
union acpi_object *cnstr_pkg;
union acpi_object *obj;
@@ -291,34 +301,74 @@ free_acpi_buffer:
static void lpi_check_constraints(void)
{
- int i;
+ struct lpi_constraints *entry;
- for (i = 0; i < lpi_constraints_table_size; ++i) {
- acpi_handle handle = lpi_constraints_table[i].handle;
- struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
+ if (IS_ERR_OR_NULL(lpi_constraints_table))
+ return;
+
+ for_each_lpi_constraint(entry) {
+ struct acpi_device *adev = acpi_fetch_acpi_dev(entry->handle);
if (!adev)
continue;
- acpi_handle_debug(handle,
+ acpi_handle_debug(entry->handle,
"LPI: required min power state:%s current power state:%s\n",
- acpi_power_state_string(lpi_constraints_table[i].min_dstate),
+ acpi_power_state_string(entry->min_dstate),
acpi_power_state_string(adev->power.state));
if (!adev->flags.power_manageable) {
- acpi_handle_info(handle, "LPI: Device not power manageable\n");
- lpi_constraints_table[i].handle = NULL;
+ acpi_handle_info(entry->handle, "LPI: Device not power manageable\n");
+ entry->handle = NULL;
continue;
}
- if (adev->power.state < lpi_constraints_table[i].min_dstate)
- acpi_handle_info(handle,
+ if (adev->power.state < entry->min_dstate)
+ acpi_handle_info(entry->handle,
"LPI: Constraint not met; min power state:%s current power state:%s\n",
- acpi_power_state_string(lpi_constraints_table[i].min_dstate),
+ acpi_power_state_string(entry->min_dstate),
acpi_power_state_string(adev->power.state));
}
}
+static bool acpi_s2idle_vendor_amd(void)
+{
+ return boot_cpu_data.x86_vendor == X86_VENDOR_AMD;
+}
+
+static const char *acpi_sleep_dsm_state_to_str(unsigned int state)
+{
+ if (lps0_dsm_func_mask_microsoft || !acpi_s2idle_vendor_amd()) {
+ switch (state) {
+ case ACPI_LPS0_SCREEN_OFF:
+ return "screen off";
+ case ACPI_LPS0_SCREEN_ON:
+ return "screen on";
+ case ACPI_LPS0_ENTRY:
+ return "lps0 entry";
+ case ACPI_LPS0_EXIT:
+ return "lps0 exit";
+ case ACPI_LPS0_MS_ENTRY:
+ return "lps0 ms entry";
+ case ACPI_LPS0_MS_EXIT:
+ return "lps0 ms exit";
+ }
+ } else {
+ switch (state) {
+ case ACPI_LPS0_SCREEN_ON_AMD:
+ return "screen on";
+ case ACPI_LPS0_SCREEN_OFF_AMD:
+ return "screen off";
+ case ACPI_LPS0_ENTRY_AMD:
+ return "lps0 entry";
+ case ACPI_LPS0_EXIT_AMD:
+ return "lps0 exit";
+ }
+ }
+
+ return "unknown";
+}
+
static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid)
{
union acpi_object *out_obj;
@@ -330,14 +380,15 @@ static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, g
rev_id, func, NULL);
ACPI_FREE(out_obj);
- acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n",
- func, out_obj ? "successful" : "failed");
+ lps0_dsm_state = func;
+ if (pm_debug_messages_on) {
+ acpi_handle_info(lps0_device_handle,
+ "%s transitioned to state %s\n",
+ out_obj ? "Successfully" : "Failed to",
+ acpi_sleep_dsm_state_to_str(lps0_dsm_state));
+ }
}
-static bool acpi_s2idle_vendor_amd(void)
-{
- return boot_cpu_data.x86_vendor == X86_VENDOR_AMD;
-}
static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid)
{
@@ -345,11 +396,10 @@ static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *d
int ret = -EINVAL;
guid_parse(uuid, dsm_guid);
- obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL);
/* Check if the _DSM is present and as expected. */
- if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 ||
- obj->buffer.length > sizeof(u32)) {
+ obj = acpi_evaluate_dsm_typed(handle, dsm_guid, rev, 0, NULL, ACPI_TYPE_BUFFER);
+ if (!obj || obj->buffer.length == 0 || obj->buffer.length > sizeof(u32)) {
acpi_handle_debug(handle,
"_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev);
goto out;
@@ -363,40 +413,53 @@ out:
return ret;
}
+struct amd_lps0_hid_device_data {
+ const bool check_off_by_one;
+};
+
+static const struct amd_lps0_hid_device_data amd_picasso = {
+ .check_off_by_one = true,
+};
+
+static const struct amd_lps0_hid_device_data amd_cezanne = {
+ .check_off_by_one = false,
+};
+
+static const struct acpi_device_id amd_hid_ids[] = {
+ {"AMD0004", (kernel_ulong_t)&amd_picasso, },
+ {"AMD0005", (kernel_ulong_t)&amd_picasso, },
+ {"AMDI0005", (kernel_ulong_t)&amd_picasso, },
+ {"AMDI0006", (kernel_ulong_t)&amd_cezanne, },
+ {}
+};
+
static int lps0_device_attach(struct acpi_device *adev,
const struct acpi_device_id *not_used)
{
if (lps0_device_handle)
return 0;
+ lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
+ ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
+ &lps0_dsm_guid_microsoft);
if (acpi_s2idle_vendor_amd()) {
- /* AMD0004, AMD0005, AMDI0005:
- * - Should use rev_id 0x0
- * - function mask > 0x3: Should use AMD method, but has off by one bug
- * - function mask = 0x3: Should use Microsoft method
- * AMDI0006:
- * - should use rev_id 0x0
- * - function mask = 0x3: Should use Microsoft method
- * AMDI0007:
- * - Should use rev_id 0x2
- * - Should only use AMD method
- */
- const char *hid = acpi_device_hid(adev);
- rev_id = strcmp(hid, "AMDI0007") ? 0 : 2;
+ static const struct acpi_device_id *dev_id;
+ const struct amd_lps0_hid_device_data *data;
+
+ for (dev_id = &amd_hid_ids[0]; dev_id->id[0]; dev_id++)
+ if (acpi_dev_hid_uid_match(adev, dev_id->id, NULL))
+ break;
+ if (dev_id->id[0])
+ data = (const struct amd_lps0_hid_device_data *) dev_id->driver_data;
+ else
+ data = &amd_cezanne;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
- lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
- ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
- &lps0_dsm_guid_microsoft);
- if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") ||
- !strcmp(hid, "AMD0005") ||
- !strcmp(hid, "AMDI0005"))) {
+ if (lps0_dsm_func_mask > 0x3 && data->check_off_by_one) {
lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
- } else if (lps0_dsm_func_mask_microsoft > 0 &&
- (!strcmp(hid, "AMDI0007") ||
- !strcmp(hid, "AMDI0008"))) {
+ } else if (lps0_dsm_func_mask_microsoft > 0 && rev_id) {
lps0_dsm_func_mask_microsoft = -EINVAL;
acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
}
@@ -404,7 +467,19 @@ static int lps0_device_attach(struct acpi_device *adev,
rev_id = 1;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
- lps0_dsm_func_mask_microsoft = -EINVAL;
+ if (lps0_dsm_func_mask > 0 && lps0_dsm_func_mask_microsoft > 0) {
+ unsigned int func_mask;
+
+ /*
+ * Log a message if the _DSM function sets for two
+ * different UUIDs overlap.
+ */
+ func_mask = lps0_dsm_func_mask & lps0_dsm_func_mask_microsoft;
+ if (func_mask)
+ acpi_handle_info(adev->handle,
+ "Duplicate LPS0 _DSM functions (mask: 0x%x)\n",
+ func_mask);
+ }
}
if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
@@ -412,11 +487,6 @@ static int lps0_device_attach(struct acpi_device *adev,
lps0_device_handle = adev->handle;
- if (acpi_s2idle_vendor_amd())
- lpi_device_get_constraints_amd();
- else
- lpi_device_get_constraints();
-
/*
* Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in
* the FADT and the default suspend mode was not set from the command
@@ -443,7 +513,26 @@ static struct acpi_scan_handler lps0_handler = {
.attach = lps0_device_attach,
};
-int acpi_s2idle_prepare_late(void)
+static int acpi_s2idle_begin_lps0(void)
+{
+ if (pm_debug_messages_on && !lpi_constraints_table) {
+ if (acpi_s2idle_vendor_amd())
+ lpi_device_get_constraints_amd();
+ else
+ lpi_device_get_constraints();
+
+ /*
+ * Try to retrieve the constraints only once because failures
+ * to do so usually are sticky.
+ */
+ if (!lpi_constraints_table)
+ lpi_constraints_table = ERR_PTR(-ENODATA);
+ }
+
+ return acpi_s2idle_begin();
+}
+
+static int acpi_s2idle_prepare_late_lps0(void)
{
struct acpi_s2idle_dev_ops *handler;
@@ -465,19 +554,22 @@ int acpi_s2idle_prepare_late(void)
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
/* LPS0 entry */
- if (lps0_dsm_func_mask > 0)
- acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
- ACPI_LPS0_ENTRY_AMD :
- ACPI_LPS0_ENTRY,
+ if (lps0_dsm_func_mask > 0 && acpi_s2idle_vendor_amd())
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY_AMD,
lps0_dsm_func_mask, lps0_dsm_guid);
+
if (lps0_dsm_func_mask_microsoft > 0) {
- acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
- lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
- /* modern standby entry */
+ /* Modern Standby entry */
acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
+ lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
}
+ if (lps0_dsm_func_mask > 0 && !acpi_s2idle_vendor_amd())
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
+ lps0_dsm_func_mask, lps0_dsm_guid);
+
list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
if (handler->prepare)
handler->prepare();
@@ -486,7 +578,20 @@ int acpi_s2idle_prepare_late(void)
return 0;
}
-void acpi_s2idle_restore_early(void)
+static void acpi_s2idle_check_lps0(void)
+{
+ struct acpi_s2idle_dev_ops *handler;
+
+ if (!lps0_device_handle || sleep_no_lps0)
+ return;
+
+ list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
+ if (handler->check)
+ handler->check();
+ }
+}
+
+static void acpi_s2idle_restore_early_lps0(void)
{
struct acpi_s2idle_dev_ops *handler;
@@ -497,20 +602,20 @@ void acpi_s2idle_restore_early(void)
if (handler->restore)
handler->restore();
- /* Modern standby exit */
- if (lps0_dsm_func_mask_microsoft > 0)
- acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
- lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
-
/* LPS0 exit */
if (lps0_dsm_func_mask > 0)
acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
ACPI_LPS0_EXIT_AMD :
ACPI_LPS0_EXIT,
lps0_dsm_func_mask, lps0_dsm_guid);
- if (lps0_dsm_func_mask_microsoft > 0)
+
+ if (lps0_dsm_func_mask_microsoft > 0) {
acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+ /* Modern Standby exit */
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
+ lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+ }
/* Screen on */
if (lps0_dsm_func_mask_microsoft > 0)
@@ -524,16 +629,17 @@ void acpi_s2idle_restore_early(void)
}
static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
- .begin = acpi_s2idle_begin,
+ .begin = acpi_s2idle_begin_lps0,
.prepare = acpi_s2idle_prepare,
- .prepare_late = acpi_s2idle_prepare_late,
+ .prepare_late = acpi_s2idle_prepare_late_lps0,
+ .check = acpi_s2idle_check_lps0,
.wake = acpi_s2idle_wake,
- .restore_early = acpi_s2idle_restore_early,
+ .restore_early = acpi_s2idle_restore_early_lps0,
.restore = acpi_s2idle_restore,
.end = acpi_s2idle_end,
};
-void acpi_s2idle_setup(void)
+void __init acpi_s2idle_setup(void)
{
acpi_scan_add_handler(&lps0_handler);
s2idle_set_ops(&acpi_s2idle_ops_lps0);
@@ -541,12 +647,14 @@ void acpi_s2idle_setup(void)
int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg)
{
+ unsigned int sleep_flags;
+
if (!lps0_device_handle || sleep_no_lps0)
return -ENODEV;
- lock_system_sleep();
+ sleep_flags = lock_system_sleep();
list_add(&arg->list_node, &lps0_s2idle_devops_head);
- unlock_system_sleep();
+ unlock_system_sleep(sleep_flags);
return 0;
}
@@ -554,12 +662,14 @@ EXPORT_SYMBOL_GPL(acpi_register_lps0_dev);
void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg)
{
+ unsigned int sleep_flags;
+
if (!lps0_device_handle || sleep_no_lps0)
return;
- lock_system_sleep();
+ sleep_flags = lock_system_sleep();
list_del(&arg->list_node);
- unlock_system_sleep();
+ unlock_system_sleep(sleep_flags);
}
EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev);
diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c
index 664070fc8349..4ee30c2897a2 100644
--- a/drivers/acpi/x86/utils.c
+++ b/drivers/acpi/x86/utils.c
@@ -12,6 +12,7 @@
#include <linux/acpi.h>
#include <linux/dmi.h>
+#include <linux/pci.h>
#include <linux/platform_device.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
@@ -45,37 +46,37 @@ struct override_status_id {
unsigned long long status;
};
-#define ENTRY(status, hid, uid, path, cpu_model, dmi...) { \
+#define ENTRY(status, hid, uid, path, cpu_vfm, dmi...) { \
{ { hid, }, {} }, \
- { X86_MATCH_INTEL_FAM6_MODEL(cpu_model, NULL), {} }, \
+ { X86_MATCH_VFM(cpu_vfm, NULL), {} }, \
{ { .matches = dmi }, {} }, \
uid, \
path, \
status, \
}
-#define PRESENT_ENTRY_HID(hid, uid, cpu_model, dmi...) \
- ENTRY(ACPI_STA_DEFAULT, hid, uid, NULL, cpu_model, dmi)
+#define PRESENT_ENTRY_HID(hid, uid, cpu_vfm, dmi...) \
+ ENTRY(ACPI_STA_DEFAULT, hid, uid, NULL, cpu_vfm, dmi)
-#define NOT_PRESENT_ENTRY_HID(hid, uid, cpu_model, dmi...) \
- ENTRY(0, hid, uid, NULL, cpu_model, dmi)
+#define NOT_PRESENT_ENTRY_HID(hid, uid, cpu_vfm, dmi...) \
+ ENTRY(0, hid, uid, NULL, cpu_vfm, dmi)
-#define PRESENT_ENTRY_PATH(path, cpu_model, dmi...) \
- ENTRY(ACPI_STA_DEFAULT, "", NULL, path, cpu_model, dmi)
+#define PRESENT_ENTRY_PATH(path, cpu_vfm, dmi...) \
+ ENTRY(ACPI_STA_DEFAULT, "", NULL, path, cpu_vfm, dmi)
-#define NOT_PRESENT_ENTRY_PATH(path, cpu_model, dmi...) \
- ENTRY(0, "", NULL, path, cpu_model, dmi)
+#define NOT_PRESENT_ENTRY_PATH(path, cpu_vfm, dmi...) \
+ ENTRY(0, "", NULL, path, cpu_vfm, dmi)
static const struct override_status_id override_status_ids[] = {
/*
* Bay / Cherry Trail PWM directly poked by GPU driver in win10,
* but Linux uses a separate PWM driver, harmless if not used.
*/
- PRESENT_ENTRY_HID("80860F09", "1", ATOM_SILVERMONT, {}),
- PRESENT_ENTRY_HID("80862288", "1", ATOM_AIRMONT, {}),
+ PRESENT_ENTRY_HID("80860F09", "1", INTEL_ATOM_SILVERMONT, {}),
+ PRESENT_ENTRY_HID("80862288", "1", INTEL_ATOM_AIRMONT, {}),
/* The Xiaomi Mi Pad 2 uses PWM2 for touchkeys backlight control */
- PRESENT_ENTRY_HID("80862289", "2", ATOM_AIRMONT, {
+ PRESENT_ENTRY_HID("80862289", "2", INTEL_ATOM_AIRMONT, {
DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
}),
@@ -84,23 +85,32 @@ static const struct override_status_id override_status_ids[] = {
* The INT0002 device is necessary to clear wakeup interrupt sources
* on Cherry Trail devices, without it we get nobody cared IRQ msgs.
*/
- PRESENT_ENTRY_HID("INT0002", "1", ATOM_AIRMONT, {}),
+ PRESENT_ENTRY_HID("INT0002", "1", INTEL_ATOM_AIRMONT, {}),
/*
* On the Dell Venue 11 Pro 7130 and 7139, the DSDT hides
* the touchscreen ACPI device until a certain time
* after _SB.PCI0.GFX0.LCD.LCD1._ON gets called has passed
* *and* _STA has been called at least 3 times since.
*/
- PRESENT_ENTRY_HID("SYNA7500", "1", HASWELL_L, {
+ PRESENT_ENTRY_HID("SYNA7500", "1", INTEL_HASWELL_L, {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"),
}),
- PRESENT_ENTRY_HID("SYNA7500", "1", HASWELL_L, {
+ PRESENT_ENTRY_HID("SYNA7500", "1", INTEL_HASWELL_L, {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7139"),
}),
/*
+ * The Dell XPS 15 9550 has a SMO8110 accelerometer /
+ * HDD freefall sensor which is wrongly marked as not present.
+ */
+ PRESENT_ENTRY_HID("SMO8810", "1", INTEL_SKYLAKE, {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 15 9550"),
+ }),
+
+ /*
* The GPD win BIOS dated 20170221 has disabled the accelerometer, the
* drivers sometimes cause crashes under Windows and this is how the
* manufacturer has solved this :| The DMI match may not seem unique,
@@ -112,19 +122,19 @@ static const struct override_status_id override_status_ids[] = {
* was copy-pasted from the GPD win, so it has a disabled KIOX000A
* node which we should not enable, thus we also check the BIOS date.
*/
- PRESENT_ENTRY_HID("KIOX000A", "1", ATOM_AIRMONT, {
+ PRESENT_ENTRY_HID("KIOX000A", "1", INTEL_ATOM_AIRMONT, {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Default string"),
DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
DMI_MATCH(DMI_BIOS_DATE, "02/21/2017")
}),
- PRESENT_ENTRY_HID("KIOX000A", "1", ATOM_AIRMONT, {
+ PRESENT_ENTRY_HID("KIOX000A", "1", INTEL_ATOM_AIRMONT, {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Default string"),
DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
DMI_MATCH(DMI_BIOS_DATE, "03/20/2017")
}),
- PRESENT_ENTRY_HID("KIOX000A", "1", ATOM_AIRMONT, {
+ PRESENT_ENTRY_HID("KIOX000A", "1", INTEL_ATOM_AIRMONT, {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Default string"),
DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
@@ -137,12 +147,22 @@ static const struct override_status_id override_status_ids[] = {
* method sets a GPIO causing the PCI wifi card to turn off.
* See above remark about uniqueness of the DMI match.
*/
- NOT_PRESENT_ENTRY_PATH("\\_SB_.PCI0.SDHB.BRC1", ATOM_AIRMONT, {
+ NOT_PRESENT_ENTRY_PATH("\\_SB_.PCI0.SDHB.BRC1", INTEL_ATOM_AIRMONT, {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
}),
+
+ /*
+ * The LSM303D on the Lenovo Yoga Tablet 2 series is present
+ * as both ACCL0001 and MAGN0001. As we can only ever register an
+ * i2c client for one of them, ignore MAGN0001.
+ */
+ NOT_PRESENT_ENTRY_HID("MAGN0001", "1", INTEL_ATOM_SILVERMONT, {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "YOGATablet2"),
+ }),
};
bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *status)
@@ -174,8 +194,7 @@ bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *s
if (acpi_match_device_ids(adev, override_status_ids[i].hid))
continue;
- if (!adev->pnp.unique_id ||
- strcmp(adev->pnp.unique_id, override_status_ids[i].uid))
+ if (!acpi_dev_uid_match(adev, override_status_ids[i].uid))
continue;
}
@@ -188,28 +207,37 @@ bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *s
}
/*
- * AMD systems from Renoir and Lucienne *require* that the NVME controller
+ * AMD systems from Renoir onwards *require* that the NVME controller
* is put into D3 over a Modern Standby / suspend-to-idle cycle.
*
* This is "typically" accomplished using the `StorageD3Enable`
* property in the _DSD that is checked via the `acpi_storage_d3` function
- * but this property was introduced after many of these systems launched
- * and most OEM systems don't have it in their BIOS.
+ * but some OEM systems still don't have it in their BIOS.
*
* The Microsoft documentation for StorageD3Enable mentioned that Windows has
- * a hardcoded allowlist for D3 support, which was used for these platforms.
+ * a hardcoded allowlist for D3 support as well as a registry key to override
+ * the BIOS, which has been used for these cases.
*
* This allows quirking on Linux in a similar fashion.
+ *
+ * Cezanne systems shouldn't *normally* need this as the BIOS includes
+ * StorageD3Enable. But for two reasons we have added it.
+ * 1) The BIOS on a number of Dell systems have ambiguity
+ * between the same value used for _ADR on ACPI nodes GPP1.DEV0 and GPP1.NVME.
+ * GPP1.NVME is needed to get StorageD3Enable node set properly.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=216440
+ * https://bugzilla.kernel.org/show_bug.cgi?id=216773
+ * https://bugzilla.kernel.org/show_bug.cgi?id=217003
+ * 2) On at least one HP system StorageD3Enable is missing on the second NVME
+ * disk in the system.
+ * 3) On at least one HP Rembrandt system StorageD3Enable is missing on the only
+ * NVME device.
*/
-static const struct x86_cpu_id storage_d3_cpu_ids[] = {
- X86_MATCH_VENDOR_FAM_MODEL(AMD, 23, 96, NULL), /* Renoir */
- X86_MATCH_VENDOR_FAM_MODEL(AMD, 23, 104, NULL), /* Lucienne */
- {}
-};
-
bool force_storage_d3(void)
{
- return x86_match_cpu(storage_d3_cpu_ids);
+ if (!cpu_feature_enabled(X86_FEATURE_ZEN))
+ return false;
+ return acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0;
}
/*
@@ -236,9 +264,12 @@ bool force_storage_d3(void)
* drivers/platform/x86/x86-android-tablets.c kernel module.
*/
#define ACPI_QUIRK_SKIP_I2C_CLIENTS BIT(0)
-#define ACPI_QUIRK_UART1_TTY_UART2_SKIP BIT(1)
-#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY BIT(2)
-#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY BIT(3)
+#define ACPI_QUIRK_UART1_SKIP BIT(1)
+#define ACPI_QUIRK_UART1_TTY_UART2_SKIP BIT(2)
+#define ACPI_QUIRK_PNP_UART1_SKIP BIT(3)
+#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY BIT(4)
+#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY BIT(5)
+#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS BIT(6)
static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
/*
@@ -265,27 +296,66 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
/*
* 2. Devices which also have the skip i2c/serdev quirks and which
* need the x86-android-tablets module to properly work.
+ * Sorted alphabetically.
*/
#if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
{
+ /* Acer Iconia One 7 B1-750 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
+ },
+ {
+ /* Acer Iconia One 8 A1-840 (non FHD version) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+ /* Above strings are too generic also match BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "04/01/2014"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
+ },
+ {
+ /* Asus ME176C tablet */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"),
},
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
ACPI_QUIRK_UART1_TTY_UART2_SKIP |
- ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
},
{
+ /* Asus TF103C transformer 2-in-1 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"),
},
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
- ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
+ },
+ {
+ /* Lenovo Yoga Book X90F/L */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_UART1_SKIP |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
},
{
- /* Lenovo Yoga Tablet 1050F/L */
+ /* Lenovo Yoga Tablet 2 1050F/L */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
@@ -294,18 +364,77 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
},
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_PNP_UART1_SKIP |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ },
+ {
+ /* Lenovo Yoga Tab 3 Pro X90F */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
+ },
+ {
+ /* Medion Lifetab S10346 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Way too generic, also match on BIOS data */
+ DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
},
{
- /* Nextbook Ares 8 */
+ /* Nextbook Ares 8 (BYT version)*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"),
},
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
+ },
+ {
+ /* Nextbook Ares 8A (CHT version)*/
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+ DMI_MATCH(DMI_BIOS_VERSION, "M882"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ },
+ {
+ /* Vexia Edu Atla 10 tablet 5V version */
+ .matches = {
+ /* Having all 3 of these not set is somewhat unique */
+ DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_BOARD_NAME, "To be filled by O.E.M."),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "05/14/2015"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
},
{
+ /* Vexia Edu Atla 10 tablet 9V version */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_UART1_SKIP |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
+ ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
+ },
+ {
/* Whitelabel (sold as various brands) TM800A550L */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
@@ -323,8 +452,11 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
#if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
static const struct acpi_device_id i2c_acpi_known_good_ids[] = {
{ "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
+ { "10EC5651", 0 }, /* RealTek ALC5651 audio codec */
{ "INT33F4", 0 }, /* X-Powers AXP288 PMIC */
+ { "INT33F5", 0 }, /* TI Dollar Cove PMIC */
{ "INT33FD", 0 }, /* Intel Crystal Cove PMIC */
+ { "INT34D3", 0 }, /* Intel Whiskey Cove PMIC */
{ "NPCE69A", 0 }, /* Asus Transformer keyboard dock */
{}
};
@@ -346,35 +478,109 @@ bool acpi_quirk_skip_i2c_client_enumeration(struct acpi_device *adev)
}
EXPORT_SYMBOL_GPL(acpi_quirk_skip_i2c_client_enumeration);
-int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip)
+static int acpi_dmi_skip_serdev_enumeration(struct device *controller_parent, bool *skip)
{
struct acpi_device *adev = ACPI_COMPANION(controller_parent);
const struct dmi_system_id *dmi_id;
long quirks = 0;
+ u64 uid = 0;
- *skip = false;
+ dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
+ if (!dmi_id)
+ return 0;
+
+ quirks = (unsigned long)dmi_id->driver_data;
- /* !dev_is_platform() to not match on PNP enumerated debug UARTs */
- if (!adev || !adev->pnp.unique_id || !dev_is_platform(controller_parent))
+ /* uid is left at 0 on errors and 0 is not a valid UART UID */
+ acpi_dev_uid_to_integer(adev, &uid);
+
+ /* For PCI UARTs without an UID */
+ if (!uid && dev_is_pci(controller_parent)) {
+ struct pci_dev *pdev = to_pci_dev(controller_parent);
+
+ /*
+ * Devfn values for PCI UARTs on Bay Trail SoCs, which are
+ * the only devices where this fallback is necessary.
+ */
+ if (pdev->devfn == PCI_DEVFN(0x1e, 3))
+ uid = 1;
+ else if (pdev->devfn == PCI_DEVFN(0x1e, 4))
+ uid = 2;
+ }
+
+ if (!uid)
return 0;
- dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
- if (dmi_id)
- quirks = (unsigned long)dmi_id->driver_data;
+ if (!dev_is_platform(controller_parent) && !dev_is_pci(controller_parent)) {
+ /* PNP enumerated UARTs */
+ if ((quirks & ACPI_QUIRK_PNP_UART1_SKIP) && uid == 1)
+ *skip = true;
+
+ return 0;
+ }
+
+ if ((quirks & ACPI_QUIRK_UART1_SKIP) && uid == 1)
+ *skip = true;
if (quirks & ACPI_QUIRK_UART1_TTY_UART2_SKIP) {
- if (!strcmp(adev->pnp.unique_id, "1"))
+ if (uid == 1)
return -ENODEV; /* Create tty cdev instead of serdev */
- if (!strcmp(adev->pnp.unique_id, "2"))
+ if (uid == 2)
*skip = true;
}
return 0;
}
-EXPORT_SYMBOL_GPL(acpi_quirk_skip_serdev_enumeration);
+
+bool acpi_quirk_skip_gpio_event_handlers(void)
+{
+ const struct dmi_system_id *dmi_id;
+ long quirks;
+
+ dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
+ if (!dmi_id)
+ return false;
+
+ quirks = (unsigned long)dmi_id->driver_data;
+ return (quirks & ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS);
+}
+EXPORT_SYMBOL_GPL(acpi_quirk_skip_gpio_event_handlers);
+#else
+static int acpi_dmi_skip_serdev_enumeration(struct device *controller_parent, bool *skip)
+{
+ return 0;
+}
#endif
+int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip)
+{
+ struct acpi_device *adev = ACPI_COMPANION(controller_parent);
+
+ *skip = false;
+
+ /*
+ * The DELL0501 ACPI HID represents an UART (CID is set to PNP0501) with
+ * a backlight-controller attached. There is no separate ACPI device with
+ * an UartSerialBusV2() resource to model the backlight-controller.
+ * Set skip to true so that the tty core creates a serdev ctrl device.
+ * The backlight driver will manually create the serdev client device.
+ */
+ if (adev && acpi_dev_hid_match(adev, "DELL0501")) {
+ *skip = true;
+ /*
+ * Create a platform dev for dell-uart-backlight to bind to.
+ * This is a static device, so no need to store the result.
+ */
+ platform_device_register_simple("dell-uart-backlight", PLATFORM_DEVID_NONE,
+ NULL, 0);
+ return 0;
+ }
+
+ return acpi_dmi_skip_serdev_enumeration(controller_parent, skip);
+}
+EXPORT_SYMBOL_GPL(acpi_quirk_skip_serdev_enumeration);
+
/* Lists of PMIC ACPI HIDs with an (often better) native charger driver */
static const struct {
const char *hid;
@@ -412,3 +618,38 @@ bool acpi_quirk_skip_acpi_ac_and_battery(void)
return false;
}
EXPORT_SYMBOL_GPL(acpi_quirk_skip_acpi_ac_and_battery);
+
+/* This section provides a workaround for a specific x86 system
+ * which requires disabling of mwait to work correctly.
+ */
+static int __init acpi_proc_quirk_set_no_mwait(const struct dmi_system_id *id)
+{
+ pr_notice("%s detected - disabling mwait for CPU C-states\n",
+ id->ident);
+ boot_option_idle_override = IDLE_NOMWAIT;
+ return 0;
+}
+
+static const struct dmi_system_id acpi_proc_quirk_mwait_dmi_table[] __initconst = {
+ {
+ .callback = acpi_proc_quirk_set_no_mwait,
+ .ident = "Extensa 5220",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
+ DMI_MATCH(DMI_BOARD_NAME, "Columbia"),
+ },
+ .driver_data = NULL,
+ },
+ {}
+};
+
+void __init acpi_proc_quirk_mwait_check(void)
+{
+ /*
+ * Check whether the system is DMI table. If yes, OSPM
+ * should not use mwait for CPU-states.
+ */
+ dmi_check_system(acpi_proc_quirk_mwait_dmi_table);
+}
diff --git a/drivers/amba/Kconfig b/drivers/amba/Kconfig
index fb6c7e0b4cce..14bb61ff801e 100644
--- a/drivers/amba/Kconfig
+++ b/drivers/amba/Kconfig
@@ -5,7 +5,7 @@ config ARM_AMBA
if ARM_AMBA
config TEGRA_AHB
- bool
+ bool "Enable AHB driver for NVIDIA Tegra SoCs" if COMPILE_TEST
default y if ARCH_TEGRA
help
Adds AHB configuration functionality for NVIDIA Tegra SoCs,
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 110a535648d2..952c45ca6e48 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -18,6 +18,7 @@
#include <linux/limits.h>
#include <linux/clk/clk-conf.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
@@ -25,7 +26,7 @@
#include <linux/iommu.h>
#include <linux/dma-map-ops.h>
-#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
+#define to_amba_driver(d) container_of_const(d, struct amba_driver, drv)
/* called on periphid match and class 0x9 coresight device. */
static int
@@ -137,7 +138,7 @@ static int amba_read_periphid(struct amba_device *dev)
void __iomem *tmp;
int i, ret;
- ret = dev_pm_domain_attach(&dev->dev, true);
+ ret = dev_pm_domain_attach(&dev->dev, PD_FLAG_ATTACH_POWER_ON);
if (ret) {
dev_dbg(&dev->dev, "can't get PM domain: %d\n", ret);
goto err_out;
@@ -204,10 +205,10 @@ err_out:
return ret;
}
-static int amba_match(struct device *dev, struct device_driver *drv)
+static int amba_match(struct device *dev, const struct device_driver *drv)
{
struct amba_device *pcdev = to_amba_device(dev);
- struct amba_driver *pcdrv = to_amba_driver(drv);
+ const struct amba_driver *pcdrv = to_amba_driver(drv);
mutex_lock(&pcdev->periphid_lock);
if (!pcdev->periphid) {
@@ -235,9 +236,9 @@ static int amba_match(struct device *dev, struct device_driver *drv)
return amba_lookup(pcdrv->id_table, pcdev) != NULL;
}
-static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int amba_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct amba_device *pcdev = to_amba_device(dev);
+ const struct amba_device *pcdev = to_amba_device(dev);
int retval = 0;
retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
@@ -290,15 +291,14 @@ static int amba_probe(struct device *dev)
if (ret < 0)
break;
- ret = dev_pm_domain_attach(dev, true);
+ ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON |
+ PD_FLAG_DETACH_POWER_OFF);
if (ret)
break;
ret = amba_get_enable_pclk(pcdev);
- if (ret) {
- dev_pm_domain_detach(dev, true);
+ if (ret)
break;
- }
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
@@ -313,7 +313,6 @@ static int amba_probe(struct device *dev)
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
- dev_pm_domain_detach(dev, true);
} while (0);
return ret;
@@ -335,7 +334,6 @@ static void amba_remove(struct device *dev)
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
- dev_pm_domain_detach(dev, true);
}
static void amba_shutdown(struct device *dev)
@@ -363,7 +361,8 @@ static int amba_dma_configure(struct device *dev)
ret = acpi_dma_configure(dev, attr);
}
- if (!ret && !drv->driver_managed_dma) {
+ /* @drv may not be valid when we're called from the IOMMU layer */
+ if (!ret && dev->driver && !drv->driver_managed_dma) {
ret = iommu_device_use_default_domain(dev);
if (ret)
arch_teardown_dma_ops(dev);
@@ -421,12 +420,6 @@ static int amba_pm_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct dev_pm_ops amba_pm = {
- .suspend = pm_generic_suspend,
- .resume = pm_generic_resume,
- .freeze = pm_generic_freeze,
- .thaw = pm_generic_thaw,
- .poweroff = pm_generic_poweroff,
- .restore = pm_generic_restore,
SET_RUNTIME_PM_OPS(
amba_pm_runtime_suspend,
amba_pm_runtime_resume,
@@ -440,7 +433,7 @@ static const struct dev_pm_ops amba_pm = {
* DMA configuration for platform and AMBA bus is same. So here we reuse
* platform's DMA config routine.
*/
-struct bus_type amba_bustype = {
+const struct bus_type amba_bustype = {
.name = "amba",
.dev_groups = amba_dev_groups,
.match = amba_match,
@@ -454,6 +447,12 @@ struct bus_type amba_bustype = {
};
EXPORT_SYMBOL_GPL(amba_bustype);
+bool dev_is_amba(const struct device *dev)
+{
+ return dev->bus == &amba_bustype;
+}
+EXPORT_SYMBOL_GPL(dev_is_amba);
+
static int __init amba_init(void)
{
return bus_register(&amba_bustype);
@@ -493,28 +492,31 @@ static int __init amba_stub_drv_init(void)
* waiting on amba_match(). So, register a stub driver to make sure
* amba_match() is called even if no amba driver has been registered.
*/
- return amba_driver_register(&amba_proxy_drv);
+ return __amba_driver_register(&amba_proxy_drv, NULL);
}
late_initcall_sync(amba_stub_drv_init);
/**
- * amba_driver_register - register an AMBA device driver
+ * __amba_driver_register - register an AMBA device driver
* @drv: amba device driver structure
+ * @owner: owning module/driver
*
* Register an AMBA device driver with the Linux device model
* core. If devices pre-exist, the drivers probe function will
* be called.
*/
-int amba_driver_register(struct amba_driver *drv)
+int __amba_driver_register(struct amba_driver *drv,
+ struct module *owner)
{
if (!drv->probe)
return -EINVAL;
+ drv->drv.owner = owner;
drv->drv.bus = &amba_bustype;
return driver_register(&drv->drv);
}
-EXPORT_SYMBOL(amba_driver_register);
+EXPORT_SYMBOL(__amba_driver_register);
/**
* amba_driver_unregister - remove an AMBA device driver
@@ -534,6 +536,7 @@ static void amba_device_release(struct device *dev)
{
struct amba_device *d = to_amba_device(dev);
+ fwnode_handle_put(dev_fwnode(&d->dev));
if (d->res.parent)
release_resource(&d->res);
mutex_destroy(&d->periphid_lock);
@@ -553,6 +556,8 @@ int amba_device_add(struct amba_device *dev, struct resource *parent)
{
int ret;
+ fwnode_handle_get(dev_fwnode(&dev->dev));
+
ret = request_resource(parent, &dev->res);
if (ret)
return ret;
diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c
index 0b2c20fddb7c..f23c3ed01810 100644
--- a/drivers/amba/tegra-ahb.c
+++ b/drivers/amba/tegra-ahb.c
@@ -144,6 +144,7 @@ int tegra_ahb_enable_smmu(struct device_node *dn)
if (!dev)
return -EPROBE_DEFER;
ahb = dev_get_drvdata(dev);
+ put_device(dev);
val = gizmo_readl(ahb, AHB_ARBITRATION_XBAR_CTRL);
val |= AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE;
gizmo_writel(ahb, val, AHB_ARBITRATION_XBAR_CTRL);
@@ -285,5 +286,4 @@ module_platform_driver(tegra_ahb_driver);
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
MODULE_DESCRIPTION("Tegra AHB driver");
-MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index 07aa8ae0a058..e2e402c9d175 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -4,6 +4,7 @@ menu "Android"
config ANDROID_BINDER_IPC
bool "Android Binder IPC Driver"
depends on MMU
+ depends on NET
default n
help
Binder is used in Android for both communication between processes,
@@ -13,6 +14,19 @@ config ANDROID_BINDER_IPC
Android process, using Binder to identify, invoke and pass arguments
between said processes.
+config ANDROID_BINDER_IPC_RUST
+ bool "Rust version of Android Binder IPC Driver"
+ depends on RUST && MMU && !ANDROID_BINDER_IPC
+ help
+ This enables the Rust implementation of the Binder driver.
+
+ Binder is used in Android for both communication between processes,
+ and remote method invocation.
+
+ This means one Android process can call a method/routine in another
+ Android process, using Binder to identify, invoke and pass arguments
+ between said processes.
+
config ANDROID_BINDERFS
bool "Android Binderfs filesystem"
depends on ANDROID_BINDER_IPC
@@ -27,7 +41,7 @@ config ANDROID_BINDERFS
config ANDROID_BINDER_DEVICES
string "Android Binder devices"
- depends on ANDROID_BINDER_IPC
+ depends on ANDROID_BINDER_IPC || ANDROID_BINDER_IPC_RUST
default "binder,hwbinder,vndbinder"
help
Default value for the binder.devices parameter.
@@ -37,14 +51,15 @@ config ANDROID_BINDER_DEVICES
created. Each binder device has its own context manager, and is
therefore logically separated from the other devices.
-config ANDROID_BINDER_IPC_SELFTEST
- bool "Android Binder IPC Driver Selftest"
- depends on ANDROID_BINDER_IPC
+config ANDROID_BINDER_ALLOC_KUNIT_TEST
+ tristate "KUnit Tests for Android Binder Alloc" if !KUNIT_ALL_TESTS
+ depends on ANDROID_BINDER_IPC && KUNIT
+ default KUNIT_ALL_TESTS
help
- This feature allows binder selftest to run.
+ This feature builds the binder alloc KUnit tests.
- Binder selftest checks the allocation and free of binder buffers
- exhaustively with combinations of various buffer sizes and
- alignments.
+ Each test case runs using a pared-down binder_alloc struct and
+ test-specific freelist, which allows this KUnit module to be loaded
+ for testing without interfering with a running system.
endmenu
diff --git a/drivers/android/Makefile b/drivers/android/Makefile
index c9d3d0c99c25..e0c650d3898e 100644
--- a/drivers/android/Makefile
+++ b/drivers/android/Makefile
@@ -2,5 +2,6 @@
ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o
-obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o
-obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o
+obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o binder_netlink.o
+obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += tests/
+obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += binder/
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 6428f6be69e3..535fc881c8da 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -66,11 +66,15 @@
#include <linux/syscalls.h>
#include <linux/task_work.h>
#include <linux/sizes.h>
+#include <linux/ktime.h>
+
+#include <kunit/visibility.h>
#include <uapi/linux/android/binder.h>
#include <linux/cacheflush.h>
+#include "binder_netlink.h"
#include "binder_internal.h"
#include "binder_trace.h"
@@ -78,6 +82,8 @@ static HLIST_HEAD(binder_deferred_list);
static DEFINE_MUTEX(binder_deferred_lock);
static HLIST_HEAD(binder_devices);
+static DEFINE_SPINLOCK(binder_devices_lock);
+
static HLIST_HEAD(binder_procs);
static DEFINE_MUTEX(binder_procs_lock);
@@ -276,12 +282,12 @@ _binder_proc_lock(struct binder_proc *proc, int line)
}
/**
- * binder_proc_unlock() - Release spinlock for given binder_proc
- * @proc: struct binder_proc to acquire
+ * binder_proc_unlock() - Release outer lock for given binder_proc
+ * @proc: struct binder_proc to acquire
*
* Release lock acquired via binder_proc_lock()
*/
-#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__)
+#define binder_proc_unlock(proc) _binder_proc_unlock(proc, __LINE__)
static void
_binder_proc_unlock(struct binder_proc *proc, int line)
__releases(&proc->outer_lock)
@@ -378,7 +384,7 @@ _binder_node_inner_lock(struct binder_node *node, int line)
}
/**
- * binder_node_unlock() - Release node and inner locks
+ * binder_node_inner_unlock() - Release node and inner locks
* @node: struct binder_node to acquire
*
* Release lock acquired via binder_node_lock()
@@ -477,6 +483,16 @@ binder_enqueue_thread_work_ilocked(struct binder_thread *thread,
{
WARN_ON(!list_empty(&thread->waiting_thread_node));
binder_enqueue_work_ilocked(work, &thread->todo);
+
+ /* (e)poll-based threads require an explicit wakeup signal when
+ * queuing their own work; they rely on these events to consume
+ * messages without I/O block. Without it, threads risk waiting
+ * indefinitely without handling the work.
+ */
+ if (thread->looper & BINDER_LOOPER_STATE_POLL &&
+ thread->pid == current->pid && !thread->process_todo)
+ wake_up_interruptible_sync(&thread->wait);
+
thread->process_todo = true;
}
@@ -559,9 +575,7 @@ static bool binder_has_work(struct binder_thread *thread, bool do_proc_work)
static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread)
{
return !thread->transaction_stack &&
- binder_worklist_empty_ilocked(&thread->todo) &&
- (thread->looper & (BINDER_LOOPER_STATE_ENTERED |
- BINDER_LOOPER_STATE_REGISTERED));
+ binder_worklist_empty_ilocked(&thread->todo);
}
static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc,
@@ -837,17 +851,8 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong,
} else {
if (!internal)
node->local_weak_refs++;
- if (!node->has_weak_ref && list_empty(&node->work.entry)) {
- if (target_list == NULL) {
- pr_err("invalid inc weak node for %d\n",
- node->debug_id);
- return -EINVAL;
- }
- /*
- * See comment above
- */
+ if (!node->has_weak_ref && target_list && list_empty(&node->work.entry))
binder_enqueue_work_ilocked(&node->work, target_list);
- }
}
return 0;
}
@@ -1034,6 +1039,63 @@ static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc,
return NULL;
}
+/* Find the smallest unused descriptor the "slow way" */
+static u32 slow_desc_lookup_olocked(struct binder_proc *proc, u32 offset)
+{
+ struct binder_ref *ref;
+ struct rb_node *n;
+ u32 desc;
+
+ desc = offset;
+ for (n = rb_first(&proc->refs_by_desc); n; n = rb_next(n)) {
+ ref = rb_entry(n, struct binder_ref, rb_node_desc);
+ if (ref->data.desc > desc)
+ break;
+ desc = ref->data.desc + 1;
+ }
+
+ return desc;
+}
+
+/*
+ * Find an available reference descriptor ID. The proc->outer_lock might
+ * be released in the process, in which case -EAGAIN is returned and the
+ * @desc should be considered invalid.
+ */
+static int get_ref_desc_olocked(struct binder_proc *proc,
+ struct binder_node *node,
+ u32 *desc)
+{
+ struct dbitmap *dmap = &proc->dmap;
+ unsigned int nbits, offset;
+ unsigned long *new, bit;
+
+ /* 0 is reserved for the context manager */
+ offset = (node == proc->context->binder_context_mgr_node) ? 0 : 1;
+
+ if (!dbitmap_enabled(dmap)) {
+ *desc = slow_desc_lookup_olocked(proc, offset);
+ return 0;
+ }
+
+ if (dbitmap_acquire_next_zero_bit(dmap, offset, &bit) == 0) {
+ *desc = bit;
+ return 0;
+ }
+
+ /*
+ * The dbitmap is full and needs to grow. The proc->outer_lock
+ * is briefly released to allocate the new bitmap safely.
+ */
+ nbits = dbitmap_grow_nbits(dmap);
+ binder_proc_unlock(proc);
+ new = bitmap_zalloc(nbits, GFP_KERNEL);
+ binder_proc_lock(proc);
+ dbitmap_grow(dmap, new, nbits);
+
+ return -EAGAIN;
+}
+
/**
* binder_get_ref_for_node_olocked() - get the ref associated with given node
* @proc: binder_proc that owns the ref
@@ -1057,12 +1119,14 @@ static struct binder_ref *binder_get_ref_for_node_olocked(
struct binder_node *node,
struct binder_ref *new_ref)
{
- struct binder_context *context = proc->context;
- struct rb_node **p = &proc->refs_by_node.rb_node;
- struct rb_node *parent = NULL;
struct binder_ref *ref;
- struct rb_node *n;
+ struct rb_node *parent;
+ struct rb_node **p;
+ u32 desc;
+retry:
+ p = &proc->refs_by_node.rb_node;
+ parent = NULL;
while (*p) {
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_node);
@@ -1077,6 +1141,10 @@ static struct binder_ref *binder_get_ref_for_node_olocked(
if (!new_ref)
return NULL;
+ /* might release the proc->outer_lock */
+ if (get_ref_desc_olocked(proc, node, &desc) == -EAGAIN)
+ goto retry;
+
binder_stats_created(BINDER_STAT_REF);
new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
new_ref->proc = proc;
@@ -1084,14 +1152,7 @@ static struct binder_ref *binder_get_ref_for_node_olocked(
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
- new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
- for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
- ref = rb_entry(n, struct binder_ref, rb_node_desc);
- if (ref->data.desc > new_ref->data.desc)
- break;
- new_ref->data.desc = ref->data.desc + 1;
- }
-
+ new_ref->data.desc = desc;
p = &proc->refs_by_desc.rb_node;
while (*p) {
parent = *p;
@@ -1120,6 +1181,7 @@ static struct binder_ref *binder_get_ref_for_node_olocked(
static void binder_cleanup_ref_olocked(struct binder_ref *ref)
{
+ struct dbitmap *dmap = &ref->proc->dmap;
bool delete_node = false;
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
@@ -1127,6 +1189,8 @@ static void binder_cleanup_ref_olocked(struct binder_ref *ref)
ref->proc->pid, ref->data.debug_id, ref->data.desc,
ref->node->debug_id);
+ if (dbitmap_enabled(dmap))
+ dbitmap_clear_bit(dmap, ref->data.desc);
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
@@ -1157,6 +1221,12 @@ static void binder_cleanup_ref_olocked(struct binder_ref *ref)
binder_dequeue_work(ref->proc, &ref->death->work);
binder_stats_deleted(BINDER_STAT_DEATH);
}
+
+ if (ref->freeze) {
+ binder_dequeue_work(ref->proc, &ref->freeze->work);
+ binder_stats_deleted(BINDER_STAT_FREEZE);
+ }
+
binder_stats_deleted(BINDER_STAT_REF);
}
@@ -1194,13 +1264,13 @@ static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
}
/**
- * binder_dec_ref() - dec the ref for given handle
+ * binder_dec_ref_olocked() - dec the ref for given handle
* @ref: ref to be decremented
* @strong: if true, strong decrement, else weak
*
* Decrement the ref.
*
- * Return: true if ref is cleaned up and ready to be freed
+ * Return: %true if ref is cleaned up and ready to be freed.
*/
static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong)
{
@@ -1284,9 +1354,29 @@ static void binder_free_ref(struct binder_ref *ref)
if (ref->node)
binder_free_node(ref->node);
kfree(ref->death);
+ kfree(ref->freeze);
kfree(ref);
}
+/* shrink descriptor bitmap if needed */
+static void try_shrink_dmap(struct binder_proc *proc)
+{
+ unsigned long *new;
+ int nbits;
+
+ binder_proc_lock(proc);
+ nbits = dbitmap_shrink_nbits(&proc->dmap);
+ binder_proc_unlock(proc);
+
+ if (!nbits)
+ return;
+
+ new = bitmap_zalloc(nbits, GFP_KERNEL);
+ binder_proc_lock(proc);
+ dbitmap_shrink(&proc->dmap, new, nbits);
+ binder_proc_unlock(proc);
+}
+
/**
* binder_update_ref_for_handle() - inc/dec the ref for given handle
* @proc: proc containing the ref
@@ -1323,8 +1413,10 @@ static int binder_update_ref_for_handle(struct binder_proc *proc,
*rdata = ref->data;
binder_proc_unlock(proc);
- if (delete_ref)
+ if (delete_ref) {
binder_free_ref(ref);
+ try_shrink_dmap(proc);
+ }
return ret;
err_no_ref:
@@ -1457,7 +1549,7 @@ static void binder_thread_dec_tmpref(struct binder_thread *thread)
* by threads that are being released. When done with the binder_proc,
* this function is called to decrement the counter and free the
* proc if appropriate (proc has been released, all threads have
- * been released and not currenly in-use to process a transaction).
+ * been released and not currently in-use to process a transaction).
*/
static void binder_proc_dec_tmpref(struct binder_proc *proc)
{
@@ -1487,11 +1579,10 @@ static struct binder_thread *binder_get_txn_from(
{
struct binder_thread *from;
- spin_lock(&t->lock);
+ guard(spinlock)(&t->lock);
from = t->from;
if (from)
atomic_inc(&from->tmp_ref);
- spin_unlock(&t->lock);
return from;
}
@@ -1697,8 +1788,10 @@ static size_t binder_get_object(struct binder_proc *proc,
size_t object_size = 0;
read_size = min_t(size_t, sizeof(*object), buffer->data_size - offset);
- if (offset > buffer->data_size || read_size < sizeof(*hdr))
+ if (offset > buffer->data_size || read_size < sizeof(*hdr) ||
+ !IS_ALIGNED(offset, sizeof(u32)))
return 0;
+
if (u) {
if (copy_from_user(object, u + offset, read_size))
return 0;
@@ -1873,7 +1966,7 @@ static bool binder_validate_fixup(struct binder_proc *proc,
* struct binder_task_work_cb - for deferred close
*
* @twork: callback_head for task work
- * @fd: fd to close
+ * @file: file to close
*
* Structure to pass task work to be handled after
* returning from binder_ioctl() via task_work_add().
@@ -1920,7 +2013,7 @@ static void binder_deferred_fd_close(int fd)
if (!twcb)
return;
init_task_work(&twcb->twork, binder_do_fd_close);
- twcb->file = close_fd_get_file(fd);
+ twcb->file = file_close_fd(fd);
if (twcb->file) {
// pin it until binder_do_fd_close(); see comments there
get_file(twcb->file);
@@ -1934,24 +2027,23 @@ static void binder_deferred_fd_close(int fd)
static void binder_transaction_buffer_release(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_buffer *buffer,
- binder_size_t failed_at,
+ binder_size_t off_end_offset,
bool is_failure)
{
int debug_id = buffer->debug_id;
- binder_size_t off_start_offset, buffer_offset, off_end_offset;
+ binder_size_t off_start_offset, buffer_offset;
binder_debug(BINDER_DEBUG_TRANSACTION,
"%d buffer release %d, size %zd-%zd, failed at %llx\n",
proc->pid, buffer->debug_id,
buffer->data_size, buffer->offsets_size,
- (unsigned long long)failed_at);
+ (unsigned long long)off_end_offset);
if (buffer->target_node)
binder_dec_node(buffer->target_node, 1, 0);
off_start_offset = ALIGN(buffer->data_size, sizeof(void *));
- off_end_offset = is_failure && failed_at ? failed_at :
- off_start_offset + buffer->offsets_size;
+
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
struct binder_object_header *hdr;
@@ -2077,9 +2169,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
* Convert the address to an offset relative to
* the base of the transaction buffer.
*/
- fda_offset =
- (parent->buffer - (uintptr_t)buffer->user_data) +
- fda->parent_offset;
+ fda_offset = parent->buffer - buffer->user_data +
+ fda->parent_offset;
for (fd_index = 0; fd_index < fda->num_fds;
fd_index++) {
u32 fd;
@@ -2111,6 +2202,21 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
}
}
+/* Clean up all the objects in the buffer */
+static inline void binder_release_entire_buffer(struct binder_proc *proc,
+ struct binder_thread *thread,
+ struct binder_buffer *buffer,
+ bool is_failure)
+{
+ binder_size_t off_end_offset;
+
+ off_end_offset = ALIGN(buffer->data_size, sizeof(void *));
+ off_end_offset += buffer->offsets_size;
+
+ binder_transaction_buffer_release(proc, thread, buffer,
+ off_end_offset, is_failure);
+}
+
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
@@ -2303,10 +2409,10 @@ err_fd_not_accepted:
/**
* struct binder_ptr_fixup - data to be fixed-up in target buffer
- * @offset offset in target buffer to fixup
- * @skip_size bytes to skip in copy (fixup will be written later)
- * @fixup_data data to write at fixup offset
- * @node list node
+ * @offset: offset in target buffer to fixup
+ * @skip_size: bytes to skip in copy (fixup will be written later)
+ * @fixup_data: data to write at fixup offset
+ * @node: list node
*
* This is used for the pointer fixup list (pf) which is created and consumed
* during binder_transaction() and is only accessed locally. No
@@ -2323,10 +2429,10 @@ struct binder_ptr_fixup {
/**
* struct binder_sg_copy - scatter-gather data to be copied
- * @offset offset in target buffer
- * @sender_uaddr user address in source buffer
- * @length bytes to copy
- * @node list node
+ * @offset: offset in target buffer
+ * @sender_uaddr: user address in source buffer
+ * @length: bytes to copy
+ * @node: list node
*
* This is used for the sg copy list (sgc) which is created and consumed
* during binder_transaction() and is only accessed locally. No
@@ -2582,7 +2688,7 @@ static int binder_translate_fd_array(struct list_head *pf_head,
* Convert the address to an offset relative to
* the base of the transaction buffer.
*/
- fda_offset = (parent->buffer - (uintptr_t)t->buffer->user_data) +
+ fda_offset = parent->buffer - t->buffer->user_data +
fda->parent_offset;
sender_ufda_base = (void __user *)(uintptr_t)sender_uparent->buffer +
fda->parent_offset;
@@ -2657,8 +2763,9 @@ static int binder_fixup_parent(struct list_head *pf_head,
proc->pid, thread->pid);
return -EINVAL;
}
- buffer_offset = bp->parent_offset +
- (uintptr_t)parent->buffer - (uintptr_t)b->user_data;
+
+ buffer_offset = bp->parent_offset + parent->buffer - b->user_data;
+
return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0);
}
@@ -2728,7 +2835,10 @@ binder_find_outdated_transaction_ilocked(struct binder_transaction *t,
*
* Return: 0 if the transaction was successfully queued
* BR_DEAD_REPLY if the target process or thread is dead
- * BR_FROZEN_REPLY if the target process or thread is frozen
+ * BR_FROZEN_REPLY if the target process or thread is frozen and
+ * the sync transaction was rejected
+ * BR_TRANSACTION_PENDING_FROZEN if the target process is frozen
+ * and the async transaction was successfully queued
*/
static int binder_proc_transaction(struct binder_transaction *t,
struct binder_proc *proc,
@@ -2738,6 +2848,7 @@ static int binder_proc_transaction(struct binder_transaction *t,
bool oneway = !!(t->flags & TF_ONE_WAY);
bool pending_async = false;
struct binder_transaction *t_outdated = NULL;
+ bool frozen = false;
BUG_ON(!node);
binder_node_lock(node);
@@ -2751,15 +2862,16 @@ static int binder_proc_transaction(struct binder_transaction *t,
binder_inner_proc_lock(proc);
if (proc->is_frozen) {
+ frozen = true;
proc->sync_recv |= !oneway;
proc->async_recv |= oneway;
}
- if ((proc->is_frozen && !oneway) || proc->is_dead ||
+ if ((frozen && !oneway) || proc->is_dead ||
(thread && thread->is_dead)) {
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
- return proc->is_frozen ? BR_FROZEN_REPLY : BR_DEAD_REPLY;
+ return frozen ? BR_FROZEN_REPLY : BR_DEAD_REPLY;
}
if (!thread && !pending_async)
@@ -2770,7 +2882,7 @@ static int binder_proc_transaction(struct binder_transaction *t,
} else if (!pending_async) {
binder_enqueue_work_ilocked(&t->work, &proc->todo);
} else {
- if ((t->flags & TF_UPDATE_TXN) && proc->is_frozen) {
+ if ((t->flags & TF_UPDATE_TXN) && frozen) {
t_outdated = binder_find_outdated_transaction_ilocked(t,
&node->async_todo);
if (t_outdated) {
@@ -2801,20 +2913,23 @@ static int binder_proc_transaction(struct binder_transaction *t,
t_outdated->buffer = NULL;
buffer->transaction = NULL;
trace_binder_transaction_update_buffer_release(buffer);
- binder_transaction_buffer_release(proc, NULL, buffer, 0, 0);
+ binder_release_entire_buffer(proc, NULL, buffer, false);
binder_alloc_free_buf(&proc->alloc, buffer);
kfree(t_outdated);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
+ if (oneway && frozen)
+ return BR_TRANSACTION_PENDING_FROZEN;
+
return 0;
}
/**
* binder_get_node_refs_for_txn() - Get required refs on node for txn
* @node: struct binder_node for which to get refs
- * @proc: returns @node->proc if valid
- * @error: if no @proc then returns BR_DEAD_REPLY
+ * @procp: returns @node->proc if valid
+ * @error: if no @procp then returns BR_DEAD_REPLY
*
* User-space normally keeps the node alive when creating a transaction
* since it has a reference to the target. The local strong ref keeps it
@@ -2828,8 +2943,8 @@ static int binder_proc_transaction(struct binder_transaction *t,
* constructing the transaction, so we take that here as well.
*
* Return: The target_node with refs taken or NULL if no @node->proc is NULL.
- * Also sets @proc if valid. If the @node->proc is NULL indicating that the
- * target proc has died, @error is set to BR_DEAD_REPLY
+ * Also sets @procp if valid. If the @node->proc is NULL indicating that the
+ * target proc has died, @error is set to BR_DEAD_REPLY.
*/
static struct binder_node *binder_get_node_refs_for_txn(
struct binder_node *node,
@@ -2870,6 +2985,69 @@ static void binder_set_txn_from_error(struct binder_transaction *t, int id,
binder_thread_dec_tmpref(from);
}
+/**
+ * binder_netlink_report() - report a transaction failure via netlink
+ * @proc: the binder proc sending the transaction
+ * @t: the binder transaction that failed
+ * @data_size: the user provided data size for the transaction
+ * @error: enum binder_driver_return_protocol returned to sender
+ */
+static void binder_netlink_report(struct binder_proc *proc,
+ struct binder_transaction *t,
+ u32 data_size,
+ u32 error)
+{
+ const char *context = proc->context->name;
+ struct sk_buff *skb;
+ void *hdr;
+
+ if (!genl_has_listeners(&binder_nl_family, &init_net,
+ BINDER_NLGRP_REPORT))
+ return;
+
+ trace_binder_netlink_report(context, t, data_size, error);
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ hdr = genlmsg_put(skb, 0, 0, &binder_nl_family, 0, BINDER_CMD_REPORT);
+ if (!hdr)
+ goto free_skb;
+
+ if (nla_put_u32(skb, BINDER_A_REPORT_ERROR, error) ||
+ nla_put_string(skb, BINDER_A_REPORT_CONTEXT, context) ||
+ nla_put_u32(skb, BINDER_A_REPORT_FROM_PID, t->from_pid) ||
+ nla_put_u32(skb, BINDER_A_REPORT_FROM_TID, t->from_tid))
+ goto cancel_skb;
+
+ if (t->to_proc &&
+ nla_put_u32(skb, BINDER_A_REPORT_TO_PID, t->to_proc->pid))
+ goto cancel_skb;
+
+ if (t->to_thread &&
+ nla_put_u32(skb, BINDER_A_REPORT_TO_TID, t->to_thread->pid))
+ goto cancel_skb;
+
+ if (t->is_reply && nla_put_flag(skb, BINDER_A_REPORT_IS_REPLY))
+ goto cancel_skb;
+
+ if (nla_put_u32(skb, BINDER_A_REPORT_FLAGS, t->flags) ||
+ nla_put_u32(skb, BINDER_A_REPORT_CODE, t->code) ||
+ nla_put_u32(skb, BINDER_A_REPORT_DATA_SIZE, data_size))
+ goto cancel_skb;
+
+ genlmsg_end(skb, hdr);
+ genlmsg_multicast(&binder_nl_family, skb, 0, BINDER_NLGRP_REPORT,
+ GFP_KERNEL);
+ return;
+
+cancel_skb:
+ genlmsg_cancel(skb, hdr);
+free_skb:
+ nlmsg_free(skb);
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
@@ -2896,8 +3074,8 @@ static void binder_transaction(struct binder_proc *proc,
binder_size_t last_fixup_min_off = 0;
struct binder_context *context = proc->context;
int t_debug_id = atomic_inc_return(&binder_last_id);
- char *secctx = NULL;
- u32 secctx_sz = 0;
+ ktime_t t_start_time = ktime_get();
+ struct lsm_context lsmctx = { };
struct list_head sgc_head;
struct list_head pf_head;
const void __user *user_buffer = (const void __user *)
@@ -2919,6 +3097,32 @@ static void binder_transaction(struct binder_proc *proc,
binder_set_extended_error(&thread->ee, t_debug_id, BR_OK, 0);
binder_inner_proc_unlock(proc);
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t) {
+ binder_txn_error("%d:%d cannot allocate transaction\n",
+ thread->pid, proc->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_param = -ENOMEM;
+ return_error_line = __LINE__;
+ goto err_alloc_t_failed;
+ }
+ INIT_LIST_HEAD(&t->fd_fixups);
+ binder_stats_created(BINDER_STAT_TRANSACTION);
+ spin_lock_init(&t->lock);
+ t->debug_id = t_debug_id;
+ t->start_time = t_start_time;
+ t->from_pid = proc->pid;
+ t->from_tid = thread->pid;
+ t->sender_euid = task_euid(proc->tsk);
+ t->code = tr->code;
+ t->flags = tr->flags;
+ t->priority = task_nice(current);
+ t->work.type = BINDER_WORK_TRANSACTION;
+ t->is_async = !reply && (tr->flags & TF_ONE_WAY);
+ t->is_reply = reply;
+ if (!reply && !(tr->flags & TF_ONE_WAY))
+ t->from = thread;
+
if (reply) {
binder_inner_proc_lock(proc);
in_reply_to = thread->transaction_stack;
@@ -3022,10 +3226,8 @@ static void binder_transaction(struct binder_proc *proc,
}
if (!target_node) {
binder_txn_error("%d:%d cannot find target node\n",
- thread->pid, proc->pid);
- /*
- * return_error is set above
- */
+ proc->pid, thread->pid);
+ /* return_error is set above */
return_error_param = -EINVAL;
return_error_line = __LINE__;
goto err_dead_binder;
@@ -3107,24 +3309,13 @@ static void binder_transaction(struct binder_proc *proc,
}
binder_inner_proc_unlock(proc);
}
+
+ t->to_proc = target_proc;
+ t->to_thread = target_thread;
if (target_thread)
e->to_thread = target_thread->pid;
e->to_proc = target_proc->pid;
- /* TODO: reuse incoming transaction for reply */
- t = kzalloc(sizeof(*t), GFP_KERNEL);
- if (t == NULL) {
- binder_txn_error("%d:%d cannot allocate transaction\n",
- thread->pid, proc->pid);
- return_error = BR_FAILED_REPLY;
- return_error_param = -ENOMEM;
- return_error_line = __LINE__;
- goto err_alloc_t_failed;
- }
- INIT_LIST_HEAD(&t->fd_fixups);
- binder_stats_created(BINDER_STAT_TRANSACTION);
- spin_lock_init(&t->lock);
-
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
binder_txn_error("%d:%d cannot allocate work for transaction\n",
@@ -3136,45 +3327,28 @@ static void binder_transaction(struct binder_proc *proc,
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
- t->debug_id = t_debug_id;
-
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld-%lld\n",
+ "%d:%d BC_REPLY %d -> %d:%d, data size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_thread->pid,
- (u64)tr->data.ptr.buffer,
- (u64)tr->data.ptr.offsets,
(u64)tr->data_size, (u64)tr->offsets_size,
(u64)extra_buffers_size);
else
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld-%lld\n",
+ "%d:%d BC_TRANSACTION %d -> %d - node %d, data size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_node->debug_id,
- (u64)tr->data.ptr.buffer,
- (u64)tr->data.ptr.offsets,
(u64)tr->data_size, (u64)tr->offsets_size,
(u64)extra_buffers_size);
- if (!reply && !(tr->flags & TF_ONE_WAY))
- t->from = thread;
- else
- t->from = NULL;
- t->sender_euid = task_euid(proc->tsk);
- t->to_proc = target_proc;
- t->to_thread = target_thread;
- t->code = tr->code;
- t->flags = tr->flags;
- t->priority = task_nice(current);
-
if (target_node && target_node->txn_security_ctx) {
u32 secid;
size_t added_size;
security_cred_getsecid(proc->cred, &secid);
- ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
- if (ret) {
+ ret = security_secid_to_secctx(secid, &lsmctx);
+ if (ret < 0) {
binder_txn_error("%d:%d failed to get security context\n",
thread->pid, proc->pid);
return_error = BR_FAILED_REPLY;
@@ -3182,7 +3356,7 @@ static void binder_transaction(struct binder_proc *proc,
return_error_line = __LINE__;
goto err_get_secctx_failed;
}
- added_size = ALIGN(secctx_sz, sizeof(u64));
+ added_size = ALIGN(lsmctx.len, sizeof(u64));
extra_buffers_size += added_size;
if (extra_buffers_size < added_size) {
binder_txn_error("%d:%d integer overflow of extra_buffers_size\n",
@@ -3198,7 +3372,7 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
- !reply && (t->flags & TF_ONE_WAY), current->tgid);
+ !reply && (t->flags & TF_ONE_WAY));
if (IS_ERR(t->buffer)) {
char *s;
@@ -3216,23 +3390,23 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer = NULL;
goto err_binder_alloc_buf_failed;
}
- if (secctx) {
+ if (lsmctx.context) {
int err;
size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) +
ALIGN(tr->offsets_size, sizeof(void *)) +
ALIGN(extra_buffers_size, sizeof(void *)) -
- ALIGN(secctx_sz, sizeof(u64));
+ ALIGN(lsmctx.len, sizeof(u64));
- t->security_ctx = (uintptr_t)t->buffer->user_data + buf_offset;
+ t->security_ctx = t->buffer->user_data + buf_offset;
err = binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer, buf_offset,
- secctx, secctx_sz);
+ lsmctx.context, lsmctx.len);
if (err) {
t->security_ctx = 0;
WARN_ON(1);
}
- security_release_secctx(secctx, secctx_sz);
- secctx = NULL;
+ security_release_secctx(&lsmctx);
+ lsmctx.context = NULL;
}
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
@@ -3276,7 +3450,7 @@ static void binder_transaction(struct binder_proc *proc,
off_end_offset = off_start_offset + tr->offsets_size;
sg_buf_offset = ALIGN(off_end_offset, sizeof(void *));
sg_buf_end_offset = sg_buf_offset + extra_buffers_size -
- ALIGN(secctx_sz, sizeof(u64));
+ ALIGN(lsmctx.len, sizeof(u64));
off_min = 0;
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
@@ -3305,6 +3479,7 @@ static void binder_transaction(struct binder_proc *proc,
*/
copy_size = object_offset - user_offset;
if (copy_size && (user_offset > object_offset ||
+ object_offset > tr->data_size ||
binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer, user_offset,
@@ -3500,8 +3675,7 @@ static void binder_transaction(struct binder_proc *proc,
goto err_translate_failed;
}
/* Fixup buffer pointer to target proc address space */
- bp->buffer = (uintptr_t)
- t->buffer->user_data + sg_buf_offset;
+ bp->buffer = t->buffer->user_data + sg_buf_offset;
sg_buf_offset += ALIGN(bp->length, sizeof(u64));
num_valid = (buffer_offset - off_start_offset) /
@@ -3560,11 +3734,13 @@ static void binder_transaction(struct binder_proc *proc,
return_error_line = __LINE__;
goto err_copy_data_failed;
}
- if (t->buffer->oneway_spam_suspect)
+ if (t->buffer->oneway_spam_suspect) {
tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT;
- else
+ binder_netlink_report(proc, t, tr->data_size,
+ BR_ONEWAY_SPAM_SUSPECT);
+ } else {
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
- t->work.type = BINDER_WORK_TRANSACTION;
+ }
if (reply) {
binder_enqueue_thread_work(thread, tcomplete);
@@ -3592,7 +3768,6 @@ static void binder_transaction(struct binder_proc *proc,
* the target replies (or there is an error).
*/
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
- t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
binder_inner_proc_unlock(proc);
@@ -3607,9 +3782,20 @@ static void binder_transaction(struct binder_proc *proc,
} else {
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
- binder_enqueue_thread_work(thread, tcomplete);
return_error = binder_proc_transaction(t, target_proc, NULL);
- if (return_error)
+ /*
+ * Let the caller know when async transaction reaches a frozen
+ * process and is put in a pending queue, waiting for the target
+ * process to be unfrozen.
+ */
+ if (return_error == BR_TRANSACTION_PENDING_FROZEN) {
+ tcomplete->type = BINDER_WORK_TRANSACTION_PENDING;
+ binder_netlink_report(proc, t, tr->data_size,
+ return_error);
+ }
+ binder_enqueue_thread_work(thread, tcomplete);
+ if (return_error &&
+ return_error != BR_TRANSACTION_PENDING_FROZEN)
goto err_dead_proc_or_thread;
}
if (target_thread)
@@ -3647,17 +3833,14 @@ err_copy_data_failed:
binder_alloc_free_buf(&target_proc->alloc, t->buffer);
err_binder_alloc_buf_failed:
err_bad_extra_size:
- if (secctx)
- security_release_secctx(secctx, secctx_sz);
+ if (lsmctx.context)
+ security_release_secctx(&lsmctx);
err_get_secctx_failed:
kfree(tcomplete);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
err_alloc_tcomplete_failed:
if (trace_binder_txn_latency_free_enabled())
binder_txn_latency_free(t);
- kfree(t);
- binder_stats_deleted(BINDER_STAT_TRANSACTION);
-err_alloc_t_failed:
err_bad_todo_list:
err_bad_call_stack:
err_empty_call_stack:
@@ -3668,14 +3851,19 @@ err_invalid_target_handle:
binder_dec_node_tmpref(target_node);
}
+ binder_netlink_report(proc, t, tr->data_size, return_error);
+ kfree(t);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION);
+err_alloc_t_failed:
+
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "%d:%d transaction %s to %d:%d failed %d/%d/%d, size %lld-%lld line %d\n",
+ "%d:%d transaction %s to %d:%d failed %d/%d/%d, code %u size %lld-%lld line %d\n",
proc->pid, thread->pid, reply ? "reply" :
(tr->flags & TF_ONE_WAY ? "async" : "call"),
target_proc ? target_proc->pid : 0,
target_thread ? target_thread->pid : 0,
t_debug_id, return_error, return_error_param,
- (u64)tr->data_size, (u64)tr->offsets_size,
+ tr->code, (u64)tr->data_size, (u64)tr->offsets_size,
return_error_line);
if (target_thread)
@@ -3717,16 +3905,164 @@ err_invalid_target_handle:
}
}
+static int
+binder_request_freeze_notification(struct binder_proc *proc,
+ struct binder_thread *thread,
+ struct binder_handle_cookie *handle_cookie)
+{
+ struct binder_ref_freeze *freeze;
+ struct binder_ref *ref;
+
+ freeze = kzalloc(sizeof(*freeze), GFP_KERNEL);
+ if (!freeze)
+ return -ENOMEM;
+ binder_proc_lock(proc);
+ ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
+ if (!ref) {
+ binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION invalid ref %d\n",
+ proc->pid, thread->pid, handle_cookie->handle);
+ binder_proc_unlock(proc);
+ kfree(freeze);
+ return -EINVAL;
+ }
+
+ binder_node_lock(ref->node);
+ if (ref->freeze) {
+ binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION already set\n",
+ proc->pid, thread->pid);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
+ kfree(freeze);
+ return -EINVAL;
+ }
+
+ binder_stats_created(BINDER_STAT_FREEZE);
+ INIT_LIST_HEAD(&freeze->work.entry);
+ freeze->cookie = handle_cookie->cookie;
+ freeze->work.type = BINDER_WORK_FROZEN_BINDER;
+ ref->freeze = freeze;
+
+ if (ref->node->proc) {
+ binder_inner_proc_lock(ref->node->proc);
+ freeze->is_frozen = ref->node->proc->is_frozen;
+ binder_inner_proc_unlock(ref->node->proc);
+
+ binder_inner_proc_lock(proc);
+ binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
+ binder_inner_proc_unlock(proc);
+ }
+
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
+ return 0;
+}
+
+static int
+binder_clear_freeze_notification(struct binder_proc *proc,
+ struct binder_thread *thread,
+ struct binder_handle_cookie *handle_cookie)
+{
+ struct binder_ref_freeze *freeze;
+ struct binder_ref *ref;
+
+ binder_proc_lock(proc);
+ ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
+ if (!ref) {
+ binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION invalid ref %d\n",
+ proc->pid, thread->pid, handle_cookie->handle);
+ binder_proc_unlock(proc);
+ return -EINVAL;
+ }
+
+ binder_node_lock(ref->node);
+
+ if (!ref->freeze) {
+ binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n",
+ proc->pid, thread->pid);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
+ return -EINVAL;
+ }
+ freeze = ref->freeze;
+ binder_inner_proc_lock(proc);
+ if (freeze->cookie != handle_cookie->cookie) {
+ binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch %016llx != %016llx\n",
+ proc->pid, thread->pid, (u64)freeze->cookie,
+ (u64)handle_cookie->cookie);
+ binder_inner_proc_unlock(proc);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
+ return -EINVAL;
+ }
+ ref->freeze = NULL;
+ /*
+ * Take the existing freeze object and overwrite its work type. There are three cases here:
+ * 1. No pending notification. In this case just add the work to the queue.
+ * 2. A notification was sent and is pending an ack from userspace. Once an ack arrives, we
+ * should resend with the new work type.
+ * 3. A notification is pending to be sent. Since the work is already in the queue, nothing
+ * needs to be done here.
+ */
+ freeze->work.type = BINDER_WORK_CLEAR_FREEZE_NOTIFICATION;
+ if (list_empty(&freeze->work.entry)) {
+ binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
+ } else if (freeze->sent) {
+ freeze->resend = true;
+ }
+ binder_inner_proc_unlock(proc);
+ binder_node_unlock(ref->node);
+ binder_proc_unlock(proc);
+ return 0;
+}
+
+static int
+binder_freeze_notification_done(struct binder_proc *proc,
+ struct binder_thread *thread,
+ binder_uintptr_t cookie)
+{
+ struct binder_ref_freeze *freeze = NULL;
+ struct binder_work *w;
+
+ binder_inner_proc_lock(proc);
+ list_for_each_entry(w, &proc->delivered_freeze, entry) {
+ struct binder_ref_freeze *tmp_freeze =
+ container_of(w, struct binder_ref_freeze, work);
+
+ if (tmp_freeze->cookie == cookie) {
+ freeze = tmp_freeze;
+ break;
+ }
+ }
+ if (!freeze) {
+ binder_user_error("%d:%d BC_FREEZE_NOTIFICATION_DONE %016llx not found\n",
+ proc->pid, thread->pid, (u64)cookie);
+ binder_inner_proc_unlock(proc);
+ return -EINVAL;
+ }
+ binder_dequeue_work_ilocked(&freeze->work);
+ freeze->sent = false;
+ if (freeze->resend) {
+ freeze->resend = false;
+ binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
+ binder_wakeup_proc_ilocked(proc);
+ }
+ binder_inner_proc_unlock(proc);
+ return 0;
+}
+
/**
* binder_free_buf() - free the specified buffer
- * @proc: binder proc that owns buffer
- * @buffer: buffer to be freed
- * @is_failure: failed to send transaction
+ * @proc: binder proc that owns buffer
+ * @thread: binder thread performing the buffer release
+ * @buffer: buffer to be freed
+ * @is_failure: failed to send transaction
*
- * If buffer for an async transaction, enqueue the next async
+ * If the buffer is for an async transaction, enqueue the next async
* transaction from the node.
*
- * Cleanup buffer and free it.
+ * Cleanup the buffer and free it.
*/
static void
binder_free_buf(struct binder_proc *proc,
@@ -3759,7 +4095,7 @@ binder_free_buf(struct binder_proc *proc,
binder_node_inner_unlock(buf_node);
}
trace_binder_transaction_buffer_release(buffer);
- binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure);
+ binder_release_entire_buffer(proc, thread, buffer, is_failure);
binder_alloc_free_buf(&proc->alloc, buffer);
}
@@ -3945,20 +4281,21 @@ static int binder_thread_write(struct binder_proc *proc,
if (IS_ERR_OR_NULL(buffer)) {
if (PTR_ERR(buffer) == -EPERM) {
binder_user_error(
- "%d:%d BC_FREE_BUFFER u%016llx matched unreturned or currently freeing buffer\n",
+ "%d:%d BC_FREE_BUFFER matched unreturned or currently freeing buffer at offset %lx\n",
proc->pid, thread->pid,
- (u64)data_ptr);
+ (unsigned long)data_ptr - proc->alloc.vm_start);
} else {
binder_user_error(
- "%d:%d BC_FREE_BUFFER u%016llx no match\n",
+ "%d:%d BC_FREE_BUFFER no match for buffer at offset %lx\n",
proc->pid, thread->pid,
- (u64)data_ptr);
+ (unsigned long)data_ptr - proc->alloc.vm_start);
}
break;
}
binder_debug(BINDER_DEBUG_FREE_BUFFER,
- "%d:%d BC_FREE_BUFFER u%016llx found buffer %d for %s transaction\n",
- proc->pid, thread->pid, (u64)data_ptr,
+ "%d:%d BC_FREE_BUFFER at offset %lx found buffer %d for %s transaction\n",
+ proc->pid, thread->pid,
+ (unsigned long)data_ptr - proc->alloc.vm_start,
buffer->debug_id,
buffer->transaction ? "active" : "finished");
binder_free_buf(proc, thread, buffer, false);
@@ -4200,6 +4537,44 @@ static int binder_thread_write(struct binder_proc *proc,
binder_inner_proc_unlock(proc);
} break;
+ case BC_REQUEST_FREEZE_NOTIFICATION: {
+ struct binder_handle_cookie handle_cookie;
+ int error;
+
+ if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
+ return -EFAULT;
+ ptr += sizeof(handle_cookie);
+ error = binder_request_freeze_notification(proc, thread,
+ &handle_cookie);
+ if (error)
+ return error;
+ } break;
+
+ case BC_CLEAR_FREEZE_NOTIFICATION: {
+ struct binder_handle_cookie handle_cookie;
+ int error;
+
+ if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
+ return -EFAULT;
+ ptr += sizeof(handle_cookie);
+ error = binder_clear_freeze_notification(proc, thread, &handle_cookie);
+ if (error)
+ return error;
+ } break;
+
+ case BC_FREEZE_NOTIFICATION_DONE: {
+ binder_uintptr_t cookie;
+ int error;
+
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
+ return -EFAULT;
+
+ ptr += sizeof(cookie);
+ error = binder_freeze_notification_done(proc, thread, cookie);
+ if (error)
+ return error;
+ } break;
+
default:
pr_err("%d:%d unknown command %u\n",
proc->pid, thread->pid, cmd);
@@ -4259,10 +4634,9 @@ static int binder_wait_for_work(struct binder_thread *thread,
struct binder_proc *proc = thread->proc;
int ret = 0;
- freezer_do_not_count();
binder_inner_proc_lock(proc);
for (;;) {
- prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE|TASK_FREEZABLE);
if (binder_has_work_ilocked(thread, do_proc_work))
break;
if (do_proc_work)
@@ -4279,7 +4653,6 @@ static int binder_wait_for_work(struct binder_thread *thread,
}
finish_wait(&thread->wait, &wait);
binder_inner_proc_unlock(proc);
- freezer_count();
return ret;
}
@@ -4296,6 +4669,8 @@ static int binder_wait_for_work(struct binder_thread *thread,
*
* If we fail to allocate an fd, skip the install and release
* any fds that have already been allocated.
+ *
+ * Return: 0 on success, a negative errno code on failure.
*/
static int binder_apply_fd_fixups(struct binder_proc *proc,
struct binder_transaction *t)
@@ -4442,10 +4817,13 @@ retry:
binder_stat_br(proc, thread, cmd);
} break;
case BINDER_WORK_TRANSACTION_COMPLETE:
+ case BINDER_WORK_TRANSACTION_PENDING:
case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: {
if (proc->oneway_spam_detection_enabled &&
w->type == BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT)
cmd = BR_ONEWAY_SPAM_SUSPECT;
+ else if (w->type == BINDER_WORK_TRANSACTION_PENDING)
+ cmd = BR_TRANSACTION_PENDING_FROZEN;
else
cmd = BR_TRANSACTION_COMPLETE;
binder_inner_proc_unlock(proc);
@@ -4588,6 +4966,46 @@ retry:
if (cmd == BR_DEAD_BINDER)
goto done; /* DEAD_BINDER notifications can cause transactions */
} break;
+
+ case BINDER_WORK_FROZEN_BINDER: {
+ struct binder_ref_freeze *freeze;
+ struct binder_frozen_state_info info;
+
+ memset(&info, 0, sizeof(info));
+ freeze = container_of(w, struct binder_ref_freeze, work);
+ info.is_frozen = freeze->is_frozen;
+ info.cookie = freeze->cookie;
+ freeze->sent = true;
+ binder_enqueue_work_ilocked(w, &proc->delivered_freeze);
+ binder_inner_proc_unlock(proc);
+
+ if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (copy_to_user(ptr, &info, sizeof(info)))
+ return -EFAULT;
+ ptr += sizeof(info);
+ binder_stat_br(proc, thread, BR_FROZEN_BINDER);
+ goto done; /* BR_FROZEN_BINDER notifications can cause transactions */
+ } break;
+
+ case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: {
+ struct binder_ref_freeze *freeze =
+ container_of(w, struct binder_ref_freeze, work);
+ binder_uintptr_t cookie = freeze->cookie;
+
+ binder_inner_proc_unlock(proc);
+ kfree(freeze);
+ binder_stats_deleted(BINDER_STAT_FREEZE);
+ if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (put_user(cookie, (binder_uintptr_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(binder_uintptr_t);
+ binder_stat_br(proc, thread, BR_CLEAR_FREEZE_NOTIFICATION_DONE);
+ } break;
+
default:
binder_inner_proc_unlock(proc);
pr_err("%d:%d: bad work type %d\n",
@@ -4662,7 +5080,7 @@ retry:
}
trd->data_size = t->buffer->data_size;
trd->offsets_size = t->buffer->offsets_size;
- trd->data.ptr.buffer = (uintptr_t)t->buffer->user_data;
+ trd->data.ptr.buffer = t->buffer->user_data;
trd->data.ptr.offsets = trd->data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
@@ -4696,16 +5114,14 @@ retry:
trace_binder_transaction_received(t);
binder_stat_br(proc, thread, cmd);
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d %s %d %d:%d, cmd %u size %zd-%zd ptr %016llx-%016llx\n",
+ "%d:%d %s %d %d:%d, cmd %u size %zd-%zd\n",
proc->pid, thread->pid,
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
(cmd == BR_TRANSACTION_SEC_CTX) ?
"BR_TRANSACTION_SEC_CTX" : "BR_REPLY",
t->debug_id, t_from ? t_from->proc->pid : 0,
t_from ? t_from->pid : 0, cmd,
- t->buffer->data_size, t->buffer->offsets_size,
- (u64)trd->data.ptr.buffer,
- (u64)trd->data.ptr.offsets);
+ t->buffer->data_size, t->buffer->offsets_size);
if (t_from)
binder_thread_dec_tmpref(t_from);
@@ -4776,6 +5192,8 @@ static void binder_release_work(struct binder_proc *proc,
"undelivered TRANSACTION_ERROR: %u\n",
e->cmd);
} break;
+ case BINDER_WORK_TRANSACTION_PENDING:
+ case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT:
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered TRANSACTION_COMPLETE\n");
@@ -4795,6 +5213,16 @@ static void binder_release_work(struct binder_proc *proc,
} break;
case BINDER_WORK_NODE:
break;
+ case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: {
+ struct binder_ref_freeze *freeze;
+
+ freeze = container_of(w, struct binder_ref_freeze, work);
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "undelivered freeze notification, %016llx\n",
+ (u64)freeze->cookie);
+ kfree(freeze);
+ binder_stats_deleted(BINDER_STAT_FREEZE);
+ } break;
default:
pr_err("unexpected work type, %d, not freed\n",
wtype);
@@ -4875,6 +5303,7 @@ static void binder_free_proc(struct binder_proc *proc)
__func__, proc->outstanding_txns);
device = container_of(proc->context, struct binder_device, context);
if (refcount_dec_and_test(&device->ref)) {
+ binder_remove_device(device);
kfree(proc->context->name);
kfree(device);
}
@@ -4882,6 +5311,7 @@ static void binder_free_proc(struct binder_proc *proc)
put_task_struct(proc->tsk);
put_cred(proc->cred);
binder_stats_deleted(BINDER_STAT_PROC);
+ dbitmap_free(&proc->dmap);
kfree(proc);
}
@@ -4992,7 +5422,7 @@ static __poll_t binder_poll(struct file *filp,
thread = binder_get_thread(proc);
if (!thread)
- return POLLERR;
+ return EPOLLERR;
binder_inner_proc_lock(thread->proc);
thread->looper |= BINDER_LOOPER_STATE_POLL;
@@ -5008,24 +5438,17 @@ static __poll_t binder_poll(struct file *filp,
return 0;
}
-static int binder_ioctl_write_read(struct file *filp,
- unsigned int cmd, unsigned long arg,
+static int binder_ioctl_write_read(struct file *filp, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
- unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
- if (size != sizeof(struct binder_write_read)) {
- ret = -EINVAL;
- goto out;
- }
- if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
- ret = -EFAULT;
- goto out;
- }
+ if (copy_from_user(&bwr, ubuf, sizeof(bwr)))
+ return -EFAULT;
+
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
proc->pid, thread->pid,
@@ -5040,8 +5463,6 @@ static int binder_ioctl_write_read(struct file *filp,
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
- if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
- ret = -EFAULT;
goto out;
}
}
@@ -5055,22 +5476,17 @@ static int binder_ioctl_write_read(struct file *filp,
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
- if (ret < 0) {
- if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
- ret = -EFAULT;
+ if (ret < 0)
goto out;
- }
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
- if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
- ret = -EFAULT;
- goto out;
- }
out:
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
+ ret = -EFAULT;
return ret;
}
@@ -5083,32 +5499,28 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp,
struct binder_node *new_node;
kuid_t curr_euid = current_euid();
- mutex_lock(&context->context_mgr_node_lock);
+ guard(mutex)(&context->context_mgr_node_lock);
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
- ret = -EBUSY;
- goto out;
+ return -EBUSY;
}
ret = security_binder_set_context_mgr(proc->cred);
if (ret < 0)
- goto out;
+ return ret;
if (uid_valid(context->binder_context_mgr_uid)) {
if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
context->binder_context_mgr_uid));
- ret = -EPERM;
- goto out;
+ return -EPERM;
}
} else {
context->binder_context_mgr_uid = curr_euid;
}
new_node = binder_new_node(proc, fbo);
- if (!new_node) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!new_node)
+ return -ENOMEM;
binder_node_lock(new_node);
new_node->local_weak_refs++;
new_node->local_strong_refs++;
@@ -5117,8 +5529,6 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp,
context->binder_context_mgr_node = new_node;
binder_node_unlock(new_node);
binder_put_node(new_node);
-out:
- mutex_unlock(&context->context_mgr_node_lock);
return ret;
}
@@ -5199,6 +5609,57 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc)
return false;
}
+static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen)
+{
+ struct binder_node *prev = NULL;
+ struct rb_node *n;
+ struct binder_ref *ref;
+
+ binder_inner_proc_lock(proc);
+ for (n = rb_first(&proc->nodes); n; n = rb_next(n)) {
+ struct binder_node *node;
+
+ node = rb_entry(n, struct binder_node, rb_node);
+ binder_inc_node_tmpref_ilocked(node);
+ binder_inner_proc_unlock(proc);
+ if (prev)
+ binder_put_node(prev);
+ binder_node_lock(node);
+ hlist_for_each_entry(ref, &node->refs, node_entry) {
+ /*
+ * Need the node lock to synchronize
+ * with new notification requests and the
+ * inner lock to synchronize with queued
+ * freeze notifications.
+ */
+ binder_inner_proc_lock(ref->proc);
+ if (!ref->freeze) {
+ binder_inner_proc_unlock(ref->proc);
+ continue;
+ }
+ ref->freeze->work.type = BINDER_WORK_FROZEN_BINDER;
+ if (list_empty(&ref->freeze->work.entry)) {
+ ref->freeze->is_frozen = is_frozen;
+ binder_enqueue_work_ilocked(&ref->freeze->work, &ref->proc->todo);
+ binder_wakeup_proc_ilocked(ref->proc);
+ } else {
+ if (ref->freeze->sent && ref->freeze->is_frozen != is_frozen)
+ ref->freeze->resend = true;
+ ref->freeze->is_frozen = is_frozen;
+ }
+ binder_inner_proc_unlock(ref->proc);
+ }
+ prev = node;
+ binder_node_unlock(node);
+ binder_inner_proc_lock(proc);
+ if (proc->is_dead)
+ break;
+ }
+ binder_inner_proc_unlock(proc);
+ if (prev)
+ binder_put_node(prev);
+}
+
static int binder_ioctl_freeze(struct binder_freeze_info *info,
struct binder_proc *target_proc)
{
@@ -5210,6 +5671,7 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
target_proc->async_recv = false;
target_proc->is_frozen = false;
binder_inner_proc_unlock(target_proc);
+ binder_add_freeze_work(target_proc, false);
return 0;
}
@@ -5242,6 +5704,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
binder_inner_proc_lock(target_proc);
target_proc->is_frozen = false;
binder_inner_proc_unlock(target_proc);
+ } else {
+ binder_add_freeze_work(target_proc, true);
}
return ret;
@@ -5298,14 +5762,8 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
- unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
- /*pr_info("binder_ioctl: %d:%d %x %lx\n",
- proc->pid, current->pid, cmd, arg);*/
-
- binder_selftest_alloc(&proc->alloc);
-
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
@@ -5320,12 +5778,12 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
switch (cmd) {
case BINDER_WRITE_READ:
- ret = binder_ioctl_write_read(filp, cmd, arg, thread);
+ ret = binder_ioctl_write_read(filp, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: {
- int max_threads;
+ u32 max_threads;
if (copy_from_user(&max_threads, ubuf,
sizeof(max_threads))) {
@@ -5363,10 +5821,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case BINDER_VERSION: {
struct binder_version __user *ver = ubuf;
- if (size != sizeof(struct binder_version)) {
- ret = -EINVAL;
- goto err;
- }
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
@@ -5545,10 +5999,11 @@ static void binder_vma_close(struct vm_area_struct *vma)
binder_alloc_vma_close(&proc->alloc);
}
-static vm_fault_t binder_vm_fault(struct vm_fault *vmf)
+VISIBLE_IF_KUNIT vm_fault_t binder_vm_fault(struct vm_fault *vmf)
{
return VM_FAULT_SIGBUS;
}
+EXPORT_SYMBOL_IF_KUNIT(binder_vm_fault);
static const struct vm_operations_struct binder_vm_ops = {
.open = binder_vma_open,
@@ -5574,8 +6029,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
proc->pid, vma->vm_start, vma->vm_end, "bad vm_flags", -EPERM);
return -EPERM;
}
- vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
- vma->vm_flags &= ~VM_MAYWRITE;
+ vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE);
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
@@ -5597,6 +6051,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
+
+ dbitmap_init(&proc->dmap);
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
@@ -5621,6 +6077,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
binder_stats_created(BINDER_STAT_PROC);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
+ INIT_LIST_HEAD(&proc->delivered_freeze);
INIT_LIST_HEAD(&proc->waiting_threads);
filp->private_data = proc;
@@ -5715,7 +6172,7 @@ static int binder_release(struct inode *nodp, struct file *filp)
debugfs_remove(proc->debugfs_entry);
if (proc->binderfs_entry) {
- binderfs_remove_file(proc->binderfs_entry);
+ simple_recursive_removal(proc->binderfs_entry, NULL);
proc->binderfs_entry = NULL;
}
@@ -5867,6 +6324,7 @@ static void binder_deferred_release(struct binder_proc *proc)
binder_release_work(proc, &proc->todo);
binder_release_work(proc, &proc->delivered_death);
+ binder_release_work(proc, &proc->delivered_freeze);
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n",
@@ -5908,14 +6366,13 @@ static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{
- mutex_lock(&binder_deferred_lock);
+ guard(mutex)(&binder_deferred_lock);
proc->deferred_work |= defer;
if (hlist_unhashed(&proc->deferred_work_node)) {
hlist_add_head(&proc->deferred_work_node,
&binder_deferred_list);
schedule_work(&binder_deferred_work);
}
- mutex_unlock(&binder_deferred_lock);
}
static void print_binder_transaction_ilocked(struct seq_file *m,
@@ -5925,17 +6382,19 @@ static void print_binder_transaction_ilocked(struct seq_file *m,
{
struct binder_proc *to_proc;
struct binder_buffer *buffer = t->buffer;
+ ktime_t current_time = ktime_get();
spin_lock(&t->lock);
to_proc = t->to_proc;
seq_printf(m,
- "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %ld r%d",
+ "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %ld a%d r%d elapsed %lldms",
prefix, t->debug_id, t,
- t->from ? t->from->proc->pid : 0,
- t->from ? t->from->pid : 0,
+ t->from_pid,
+ t->from_tid,
to_proc ? to_proc->pid : 0,
t->to_thread ? t->to_thread->pid : 0,
- t->code, t->flags, t->priority, t->need_reply);
+ t->code, t->flags, t->priority, t->is_async, t->is_reply,
+ ktime_ms_delta(current_time, t->start_time));
spin_unlock(&t->lock);
if (proc != to_proc) {
@@ -5953,16 +6412,16 @@ static void print_binder_transaction_ilocked(struct seq_file *m,
}
if (buffer->target_node)
seq_printf(m, " node %d", buffer->target_node->debug_id);
- seq_printf(m, " size %zd:%zd data %pK\n",
+ seq_printf(m, " size %zd:%zd offset %lx\n",
buffer->data_size, buffer->offsets_size,
- buffer->user_data);
+ buffer->user_data - proc->alloc.vm_start);
}
static void print_binder_work_ilocked(struct seq_file *m,
- struct binder_proc *proc,
- const char *prefix,
- const char *transaction_prefix,
- struct binder_work *w)
+ struct binder_proc *proc,
+ const char *prefix,
+ const char *transaction_prefix,
+ struct binder_work *w, bool hash_ptrs)
{
struct binder_node *node;
struct binder_transaction *t;
@@ -5985,9 +6444,15 @@ static void print_binder_work_ilocked(struct seq_file *m,
break;
case BINDER_WORK_NODE:
node = container_of(w, struct binder_node, work);
- seq_printf(m, "%snode work %d: u%016llx c%016llx\n",
- prefix, node->debug_id,
- (u64)node->ptr, (u64)node->cookie);
+ if (hash_ptrs)
+ seq_printf(m, "%snode work %d: u%p c%p\n",
+ prefix, node->debug_id,
+ (void *)(long)node->ptr,
+ (void *)(long)node->cookie);
+ else
+ seq_printf(m, "%snode work %d: u%016llx c%016llx\n",
+ prefix, node->debug_id,
+ (u64)node->ptr, (u64)node->cookie);
break;
case BINDER_WORK_DEAD_BINDER:
seq_printf(m, "%shas dead binder\n", prefix);
@@ -5998,6 +6463,12 @@ static void print_binder_work_ilocked(struct seq_file *m,
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION:
seq_printf(m, "%shas cleared death notification\n", prefix);
break;
+ case BINDER_WORK_FROZEN_BINDER:
+ seq_printf(m, "%shas frozen binder\n", prefix);
+ break;
+ case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION:
+ seq_printf(m, "%shas cleared freeze notification\n", prefix);
+ break;
default:
seq_printf(m, "%sunknown work: type %d\n", prefix, w->type);
break;
@@ -6006,7 +6477,7 @@ static void print_binder_work_ilocked(struct seq_file *m,
static void print_binder_thread_ilocked(struct seq_file *m,
struct binder_thread *thread,
- int print_always)
+ bool print_always, bool hash_ptrs)
{
struct binder_transaction *t;
struct binder_work *w;
@@ -6036,25 +6507,30 @@ static void print_binder_thread_ilocked(struct seq_file *m,
}
list_for_each_entry(w, &thread->todo, entry) {
print_binder_work_ilocked(m, thread->proc, " ",
- " pending transaction", w);
+ " pending transaction",
+ w, hash_ptrs);
}
if (!print_always && m->count == header_pos)
m->count = start_pos;
}
static void print_binder_node_nilocked(struct seq_file *m,
- struct binder_node *node)
+ struct binder_node *node,
+ bool hash_ptrs)
{
struct binder_ref *ref;
struct binder_work *w;
int count;
- count = 0;
- hlist_for_each_entry(ref, &node->refs, node_entry)
- count++;
+ count = hlist_count_nodes(&node->refs);
- seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d tr %d",
- node->debug_id, (u64)node->ptr, (u64)node->cookie,
+ if (hash_ptrs)
+ seq_printf(m, " node %d: u%p c%p", node->debug_id,
+ (void *)(long)node->ptr, (void *)(long)node->cookie);
+ else
+ seq_printf(m, " node %d: u%016llx c%016llx", node->debug_id,
+ (u64)node->ptr, (u64)node->cookie);
+ seq_printf(m, " hs %d hw %d ls %d lw %d is %d iw %d tr %d",
node->has_strong_ref, node->has_weak_ref,
node->local_strong_refs, node->local_weak_refs,
node->internal_strong_refs, count, node->tmp_refs);
@@ -6067,7 +6543,8 @@ static void print_binder_node_nilocked(struct seq_file *m,
if (node->proc) {
list_for_each_entry(w, &node->async_todo, entry)
print_binder_work_ilocked(m, node->proc, " ",
- " pending async transaction", w);
+ " pending async transaction",
+ w, hash_ptrs);
}
}
@@ -6083,8 +6560,54 @@ static void print_binder_ref_olocked(struct seq_file *m,
binder_node_unlock(ref->node);
}
-static void print_binder_proc(struct seq_file *m,
- struct binder_proc *proc, int print_all)
+/**
+ * print_next_binder_node_ilocked() - Print binder_node from a locked list
+ * @m: struct seq_file for output via seq_printf()
+ * @proc: struct binder_proc we hold the inner_proc_lock to (if any)
+ * @node: struct binder_node to print fields of
+ * @prev_node: struct binder_node we hold a temporary reference to (if any)
+ * @hash_ptrs: whether to hash @node's binder_uintptr_t fields
+ *
+ * Helper function to handle synchronization around printing a struct
+ * binder_node while iterating through @proc->nodes or the dead nodes list.
+ * Caller must hold either @proc->inner_lock (for live nodes) or
+ * binder_dead_nodes_lock. This lock will be released during the body of this
+ * function, but it will be reacquired before returning to the caller.
+ *
+ * Return: pointer to the struct binder_node we hold a tmpref on
+ */
+static struct binder_node *
+print_next_binder_node_ilocked(struct seq_file *m, struct binder_proc *proc,
+ struct binder_node *node,
+ struct binder_node *prev_node, bool hash_ptrs)
+{
+ /*
+ * Take a temporary reference on the node so that isn't freed while
+ * we print it.
+ */
+ binder_inc_node_tmpref_ilocked(node);
+ /*
+ * Live nodes need to drop the inner proc lock and dead nodes need to
+ * drop the binder_dead_nodes_lock before trying to take the node lock.
+ */
+ if (proc)
+ binder_inner_proc_unlock(proc);
+ else
+ spin_unlock(&binder_dead_nodes_lock);
+ if (prev_node)
+ binder_put_node(prev_node);
+ binder_node_inner_lock(node);
+ print_binder_node_nilocked(m, node, hash_ptrs);
+ binder_node_inner_unlock(node);
+ if (proc)
+ binder_inner_proc_lock(proc);
+ else
+ spin_lock(&binder_dead_nodes_lock);
+ return node;
+}
+
+static void print_binder_proc(struct seq_file *m, struct binder_proc *proc,
+ bool print_all, bool hash_ptrs)
{
struct binder_work *w;
struct rb_node *n;
@@ -6097,31 +6620,19 @@ static void print_binder_proc(struct seq_file *m,
header_pos = m->count;
binder_inner_proc_lock(proc);
- for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
+ for (n = rb_first(&proc->threads); n; n = rb_next(n))
print_binder_thread_ilocked(m, rb_entry(n, struct binder_thread,
- rb_node), print_all);
+ rb_node), print_all, hash_ptrs);
- for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
+ for (n = rb_first(&proc->nodes); n; n = rb_next(n)) {
struct binder_node *node = rb_entry(n, struct binder_node,
rb_node);
if (!print_all && !node->has_async_transaction)
continue;
- /*
- * take a temporary reference on the node so it
- * survives and isn't removed from the tree
- * while we print it.
- */
- binder_inc_node_tmpref_ilocked(node);
- /* Need to drop inner lock to take node lock */
- binder_inner_proc_unlock(proc);
- if (last_node)
- binder_put_node(last_node);
- binder_node_inner_lock(node);
- print_binder_node_nilocked(m, node);
- binder_node_inner_unlock(node);
- last_node = node;
- binder_inner_proc_lock(proc);
+ last_node = print_next_binder_node_ilocked(m, proc, node,
+ last_node,
+ hash_ptrs);
}
binder_inner_proc_unlock(proc);
if (last_node)
@@ -6129,23 +6640,26 @@ static void print_binder_proc(struct seq_file *m,
if (print_all) {
binder_proc_lock(proc);
- for (n = rb_first(&proc->refs_by_desc);
- n != NULL;
- n = rb_next(n))
+ for (n = rb_first(&proc->refs_by_desc); n; n = rb_next(n))
print_binder_ref_olocked(m, rb_entry(n,
- struct binder_ref,
- rb_node_desc));
+ struct binder_ref,
+ rb_node_desc));
binder_proc_unlock(proc);
}
binder_alloc_print_allocated(m, &proc->alloc);
binder_inner_proc_lock(proc);
list_for_each_entry(w, &proc->todo, entry)
print_binder_work_ilocked(m, proc, " ",
- " pending transaction", w);
+ " pending transaction", w,
+ hash_ptrs);
list_for_each_entry(w, &proc->delivered_death, entry) {
seq_puts(m, " has delivered dead binder\n");
break;
}
+ list_for_each_entry(w, &proc->delivered_freeze, entry) {
+ seq_puts(m, " has delivered freeze binder\n");
+ break;
+ }
binder_inner_proc_unlock(proc);
if (!print_all && m->count == header_pos)
m->count = start_pos;
@@ -6172,6 +6686,9 @@ static const char * const binder_return_strings[] = {
"BR_FAILED_REPLY",
"BR_FROZEN_REPLY",
"BR_ONEWAY_SPAM_SUSPECT",
+ "BR_TRANSACTION_PENDING_FROZEN",
+ "BR_FROZEN_BINDER",
+ "BR_CLEAR_FREEZE_NOTIFICATION_DONE",
};
static const char * const binder_command_strings[] = {
@@ -6194,6 +6711,9 @@ static const char * const binder_command_strings[] = {
"BC_DEAD_BINDER_DONE",
"BC_TRANSACTION_SG",
"BC_REPLY_SG",
+ "BC_REQUEST_FREEZE_NOTIFICATION",
+ "BC_CLEAR_FREEZE_NOTIFICATION",
+ "BC_FREEZE_NOTIFICATION_DONE",
};
static const char * const binder_objstat_strings[] = {
@@ -6203,7 +6723,8 @@ static const char * const binder_objstat_strings[] = {
"ref",
"death",
"transaction",
- "transaction_complete"
+ "transaction_complete",
+ "freeze",
};
static void print_binder_stats(struct seq_file *m, const char *prefix,
@@ -6263,7 +6784,7 @@ static void print_binder_proc_stats(struct seq_file *m,
count = 0;
ready_threads = 0;
binder_inner_proc_lock(proc);
- for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
+ for (n = rb_first(&proc->threads); n; n = rb_next(n))
count++;
list_for_each_entry(thread, &proc->waiting_threads, waiting_thread_node)
@@ -6277,7 +6798,7 @@ static void print_binder_proc_stats(struct seq_file *m,
ready_threads,
free_async_space);
count = 0;
- for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n))
+ for (n = rb_first(&proc->nodes); n; n = rb_next(n))
count++;
binder_inner_proc_unlock(proc);
seq_printf(m, " nodes: %d\n", count);
@@ -6285,7 +6806,7 @@ static void print_binder_proc_stats(struct seq_file *m,
strong = 0;
weak = 0;
binder_proc_lock(proc);
- for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
+ for (n = rb_first(&proc->refs_by_desc); n; n = rb_next(n)) {
struct binder_ref *ref = rb_entry(n, struct binder_ref,
rb_node_desc);
count++;
@@ -6312,7 +6833,7 @@ static void print_binder_proc_stats(struct seq_file *m,
print_binder_stats(m, " ", &proc->stats);
}
-static int state_show(struct seq_file *m, void *unused)
+static void print_binder_state(struct seq_file *m, bool hash_ptrs)
{
struct binder_proc *proc;
struct binder_node *node;
@@ -6323,31 +6844,40 @@ static int state_show(struct seq_file *m, void *unused)
spin_lock(&binder_dead_nodes_lock);
if (!hlist_empty(&binder_dead_nodes))
seq_puts(m, "dead nodes:\n");
- hlist_for_each_entry(node, &binder_dead_nodes, dead_node) {
- /*
- * take a temporary reference on the node so it
- * survives and isn't removed from the list
- * while we print it.
- */
- node->tmp_refs++;
- spin_unlock(&binder_dead_nodes_lock);
- if (last_node)
- binder_put_node(last_node);
- binder_node_lock(node);
- print_binder_node_nilocked(m, node);
- binder_node_unlock(node);
- last_node = node;
- spin_lock(&binder_dead_nodes_lock);
- }
+ hlist_for_each_entry(node, &binder_dead_nodes, dead_node)
+ last_node = print_next_binder_node_ilocked(m, NULL, node,
+ last_node,
+ hash_ptrs);
spin_unlock(&binder_dead_nodes_lock);
if (last_node)
binder_put_node(last_node);
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(proc, &binder_procs, proc_node)
- print_binder_proc(m, proc, 1);
+ print_binder_proc(m, proc, true, hash_ptrs);
mutex_unlock(&binder_procs_lock);
+}
+
+static void print_binder_transactions(struct seq_file *m, bool hash_ptrs)
+{
+ struct binder_proc *proc;
+ seq_puts(m, "binder transactions:\n");
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(proc, &binder_procs, proc_node)
+ print_binder_proc(m, proc, false, hash_ptrs);
+ mutex_unlock(&binder_procs_lock);
+}
+
+static int state_show(struct seq_file *m, void *unused)
+{
+ print_binder_state(m, false);
+ return 0;
+}
+
+static int state_hashed_show(struct seq_file *m, void *unused)
+{
+ print_binder_state(m, true);
return 0;
}
@@ -6369,14 +6899,13 @@ static int stats_show(struct seq_file *m, void *unused)
static int transactions_show(struct seq_file *m, void *unused)
{
- struct binder_proc *proc;
-
- seq_puts(m, "binder transactions:\n");
- mutex_lock(&binder_procs_lock);
- hlist_for_each_entry(proc, &binder_procs, proc_node)
- print_binder_proc(m, proc, 0);
- mutex_unlock(&binder_procs_lock);
+ print_binder_transactions(m, false);
+ return 0;
+}
+static int transactions_hashed_show(struct seq_file *m, void *unused)
+{
+ print_binder_transactions(m, true);
return 0;
}
@@ -6385,14 +6914,13 @@ static int proc_show(struct seq_file *m, void *unused)
struct binder_proc *itr;
int pid = (unsigned long)m->private;
- mutex_lock(&binder_procs_lock);
+ guard(mutex)(&binder_procs_lock);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
if (itr->pid == pid) {
seq_puts(m, "binder proc state:\n");
- print_binder_proc(m, itr, 1);
+ print_binder_proc(m, itr, true, false);
}
}
- mutex_unlock(&binder_procs_lock);
return 0;
}
@@ -6456,8 +6984,10 @@ const struct file_operations binder_fops = {
};
DEFINE_SHOW_ATTRIBUTE(state);
+DEFINE_SHOW_ATTRIBUTE(state_hashed);
DEFINE_SHOW_ATTRIBUTE(stats);
DEFINE_SHOW_ATTRIBUTE(transactions);
+DEFINE_SHOW_ATTRIBUTE(transactions_hashed);
DEFINE_SHOW_ATTRIBUTE(transaction_log);
const struct binder_debugfs_entry binder_debugfs_entries[] = {
@@ -6468,6 +6998,12 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
.data = NULL,
},
{
+ .name = "state_hashed",
+ .mode = 0444,
+ .fops = &state_hashed_fops,
+ .data = NULL,
+ },
+ {
.name = "stats",
.mode = 0444,
.fops = &stats_fops,
@@ -6480,6 +7016,12 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
.data = NULL,
},
{
+ .name = "transactions_hashed",
+ .mode = 0444,
+ .fops = &transactions_hashed_fops,
+ .data = NULL,
+ },
+ {
.name = "transaction_log",
.mode = 0444,
.fops = &transaction_log_fops,
@@ -6494,6 +7036,18 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
{} /* terminator */
};
+void binder_add_device(struct binder_device *device)
+{
+ guard(spinlock)(&binder_devices_lock);
+ hlist_add_head(&device->hlist, &binder_devices);
+}
+
+void binder_remove_device(struct binder_device *device)
+{
+ guard(spinlock)(&binder_devices_lock);
+ hlist_del_init(&device->hlist);
+}
+
static int __init init_binder_device(const char *name)
{
int ret;
@@ -6518,7 +7072,7 @@ static int __init init_binder_device(const char *name)
return ret;
}
- hlist_add_head(&binder_device->hlist, &binder_devices);
+ binder_add_device(binder_device);
return ret;
}
@@ -6530,6 +7084,7 @@ static int __init binder_init(void)
struct binder_device *device;
struct hlist_node *tmp;
char *device_names = NULL;
+ const struct binder_debugfs_entry *db_entry;
ret = binder_alloc_shrinker_init();
if (ret)
@@ -6539,19 +7094,16 @@ static int __init binder_init(void)
atomic_set(&binder_transaction_log_failed.cur, ~0U);
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
- if (binder_debugfs_dir_entry_root) {
- const struct binder_debugfs_entry *db_entry;
- binder_for_each_debugfs_entry(db_entry)
- debugfs_create_file(db_entry->name,
- db_entry->mode,
- binder_debugfs_dir_entry_root,
- db_entry->data,
- db_entry->fops);
+ binder_for_each_debugfs_entry(db_entry)
+ debugfs_create_file(db_entry->name,
+ db_entry->mode,
+ binder_debugfs_dir_entry_root,
+ db_entry->data,
+ db_entry->fops);
- binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
- binder_debugfs_dir_entry_root);
- }
+ binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
+ binder_debugfs_dir_entry_root);
if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
strcmp(binder_devices_param, "") != 0) {
@@ -6573,16 +7125,23 @@ static int __init binder_init(void)
}
}
- ret = init_binderfs();
+ ret = genl_register_family(&binder_nl_family);
if (ret)
goto err_init_binder_device_failed;
+ ret = init_binderfs();
+ if (ret)
+ goto err_init_binderfs_failed;
+
return ret;
+err_init_binderfs_failed:
+ genl_unregister_family(&binder_nl_family);
+
err_init_binder_device_failed:
hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
misc_deregister(&device->miscdev);
- hlist_del(&device->hlist);
+ binder_remove_device(device);
kfree(device);
}
@@ -6590,6 +7149,7 @@ err_init_binder_device_failed:
err_alloc_device_names_failed:
debugfs_remove_recursive(binder_debugfs_dir_entry_root);
+ binder_alloc_shrinker_exit();
return ret;
}
@@ -6598,5 +7158,3 @@ device_initcall(binder_init);
#define CREATE_TRACE_POINTS
#include "binder_trace.h"
-
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/android/binder/Makefile b/drivers/android/binder/Makefile
new file mode 100644
index 000000000000..09eabb527fa0
--- /dev/null
+++ b/drivers/android/binder/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+ccflags-y += -I$(src) # needed for trace events
+
+obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += rust_binder.o
+rust_binder-y := \
+ rust_binder_main.o \
+ rust_binderfs.o \
+ rust_binder_events.o \
+ page_range_helper.o
diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs
new file mode 100644
index 000000000000..7f65a9c3a0e5
--- /dev/null
+++ b/drivers/android/binder/allocation.rs
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use core::mem::{size_of, size_of_val, MaybeUninit};
+use core::ops::Range;
+
+use kernel::{
+ bindings,
+ fs::file::{File, FileDescriptorReservation},
+ prelude::*,
+ sync::{aref::ARef, Arc},
+ transmute::{AsBytes, FromBytes},
+ uaccess::UserSliceReader,
+ uapi,
+};
+
+use crate::{
+ deferred_close::DeferredFdCloser,
+ defs::*,
+ node::{Node, NodeRef},
+ process::Process,
+ DArc,
+};
+
+#[derive(Default)]
+pub(crate) struct AllocationInfo {
+ /// Range within the allocation where we can find the offsets to the object descriptors.
+ pub(crate) offsets: Option<Range<usize>>,
+ /// The target node of the transaction this allocation is associated to.
+ /// Not set for replies.
+ pub(crate) target_node: Option<NodeRef>,
+ /// When this allocation is dropped, call `pending_oneway_finished` on the node.
+ ///
+ /// This is used to serialize oneway transaction on the same node. Binder guarantees that
+ /// oneway transactions to the same node are delivered sequentially in the order they are sent.
+ pub(crate) oneway_node: Option<DArc<Node>>,
+ /// Zero the data in the buffer on free.
+ pub(crate) clear_on_free: bool,
+ /// List of files embedded in this transaction.
+ file_list: FileList,
+}
+
+/// Represents an allocation that the kernel is currently using.
+///
+/// When allocations are idle, the range allocator holds the data related to them.
+///
+/// # Invariants
+///
+/// This allocation corresponds to an allocation in the range allocator, so the relevant pages are
+/// marked in use in the page range.
+pub(crate) struct Allocation {
+ pub(crate) offset: usize,
+ size: usize,
+ pub(crate) ptr: usize,
+ pub(crate) process: Arc<Process>,
+ allocation_info: Option<AllocationInfo>,
+ free_on_drop: bool,
+ pub(crate) oneway_spam_detected: bool,
+ #[allow(dead_code)]
+ pub(crate) debug_id: usize,
+}
+
+impl Allocation {
+ pub(crate) fn new(
+ process: Arc<Process>,
+ debug_id: usize,
+ offset: usize,
+ size: usize,
+ ptr: usize,
+ oneway_spam_detected: bool,
+ ) -> Self {
+ Self {
+ process,
+ offset,
+ size,
+ ptr,
+ debug_id,
+ oneway_spam_detected,
+ allocation_info: None,
+ free_on_drop: true,
+ }
+ }
+
+ fn size_check(&self, offset: usize, size: usize) -> Result {
+ let overflow_fail = offset.checked_add(size).is_none();
+ let cmp_size_fail = offset.wrapping_add(size) > self.size;
+ if overflow_fail || cmp_size_fail {
+ return Err(EFAULT);
+ }
+ Ok(())
+ }
+
+ pub(crate) fn copy_into(
+ &self,
+ reader: &mut UserSliceReader,
+ offset: usize,
+ size: usize,
+ ) -> Result {
+ self.size_check(offset, size)?;
+
+ // SAFETY: While this object exists, the range allocator will keep the range allocated, and
+ // in turn, the pages will be marked as in use.
+ unsafe {
+ self.process
+ .pages
+ .copy_from_user_slice(reader, self.offset + offset, size)
+ }
+ }
+
+ pub(crate) fn read<T: FromBytes>(&self, offset: usize) -> Result<T> {
+ self.size_check(offset, size_of::<T>())?;
+
+ // SAFETY: While this object exists, the range allocator will keep the range allocated, and
+ // in turn, the pages will be marked as in use.
+ unsafe { self.process.pages.read(self.offset + offset) }
+ }
+
+ pub(crate) fn write<T: ?Sized>(&self, offset: usize, obj: &T) -> Result {
+ self.size_check(offset, size_of_val::<T>(obj))?;
+
+ // SAFETY: While this object exists, the range allocator will keep the range allocated, and
+ // in turn, the pages will be marked as in use.
+ unsafe { self.process.pages.write(self.offset + offset, obj) }
+ }
+
+ pub(crate) fn fill_zero(&self) -> Result {
+ // SAFETY: While this object exists, the range allocator will keep the range allocated, and
+ // in turn, the pages will be marked as in use.
+ unsafe { self.process.pages.fill_zero(self.offset, self.size) }
+ }
+
+ pub(crate) fn keep_alive(mut self) {
+ self.process
+ .buffer_make_freeable(self.offset, self.allocation_info.take());
+ self.free_on_drop = false;
+ }
+
+ pub(crate) fn set_info(&mut self, info: AllocationInfo) {
+ self.allocation_info = Some(info);
+ }
+
+ pub(crate) fn get_or_init_info(&mut self) -> &mut AllocationInfo {
+ self.allocation_info.get_or_insert_with(Default::default)
+ }
+
+ pub(crate) fn set_info_offsets(&mut self, offsets: Range<usize>) {
+ self.get_or_init_info().offsets = Some(offsets);
+ }
+
+ pub(crate) fn set_info_oneway_node(&mut self, oneway_node: DArc<Node>) {
+ self.get_or_init_info().oneway_node = Some(oneway_node);
+ }
+
+ pub(crate) fn set_info_clear_on_drop(&mut self) {
+ self.get_or_init_info().clear_on_free = true;
+ }
+
+ pub(crate) fn set_info_target_node(&mut self, target_node: NodeRef) {
+ self.get_or_init_info().target_node = Some(target_node);
+ }
+
+ /// Reserve enough space to push at least `num_fds` fds.
+ pub(crate) fn info_add_fd_reserve(&mut self, num_fds: usize) -> Result {
+ self.get_or_init_info()
+ .file_list
+ .files_to_translate
+ .reserve(num_fds, GFP_KERNEL)?;
+
+ Ok(())
+ }
+
+ pub(crate) fn info_add_fd(
+ &mut self,
+ file: ARef<File>,
+ buffer_offset: usize,
+ close_on_free: bool,
+ ) -> Result {
+ self.get_or_init_info().file_list.files_to_translate.push(
+ FileEntry {
+ file,
+ buffer_offset,
+ close_on_free,
+ },
+ GFP_KERNEL,
+ )?;
+
+ Ok(())
+ }
+
+ pub(crate) fn set_info_close_on_free(&mut self, cof: FdsCloseOnFree) {
+ self.get_or_init_info().file_list.close_on_free = cof.0;
+ }
+
+ pub(crate) fn translate_fds(&mut self) -> Result<TranslatedFds> {
+ let file_list = match self.allocation_info.as_mut() {
+ Some(info) => &mut info.file_list,
+ None => return Ok(TranslatedFds::new()),
+ };
+
+ let files = core::mem::take(&mut file_list.files_to_translate);
+
+ let num_close_on_free = files.iter().filter(|entry| entry.close_on_free).count();
+ let mut close_on_free = KVec::with_capacity(num_close_on_free, GFP_KERNEL)?;
+
+ let mut reservations = KVec::with_capacity(files.len(), GFP_KERNEL)?;
+ for file_info in files {
+ let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?;
+ let fd = res.reserved_fd();
+ self.write::<u32>(file_info.buffer_offset, &fd)?;
+
+ reservations.push(
+ Reservation {
+ res,
+ file: file_info.file,
+ },
+ GFP_KERNEL,
+ )?;
+ if file_info.close_on_free {
+ close_on_free.push(fd, GFP_KERNEL)?;
+ }
+ }
+
+ Ok(TranslatedFds {
+ reservations,
+ close_on_free: FdsCloseOnFree(close_on_free),
+ })
+ }
+
+ /// Should the looper return to userspace when freeing this allocation?
+ pub(crate) fn looper_need_return_on_free(&self) -> bool {
+ // Closing fds involves pushing task_work for execution when we return to userspace. Hence,
+ // we should return to userspace asap if we are closing fds.
+ match self.allocation_info {
+ Some(ref info) => !info.file_list.close_on_free.is_empty(),
+ None => false,
+ }
+ }
+}
+
+impl Drop for Allocation {
+ fn drop(&mut self) {
+ if !self.free_on_drop {
+ return;
+ }
+
+ if let Some(mut info) = self.allocation_info.take() {
+ if let Some(oneway_node) = info.oneway_node.as_ref() {
+ oneway_node.pending_oneway_finished();
+ }
+
+ info.target_node = None;
+
+ if let Some(offsets) = info.offsets.clone() {
+ let view = AllocationView::new(self, offsets.start);
+ for i in offsets.step_by(size_of::<usize>()) {
+ if view.cleanup_object(i).is_err() {
+ pr_warn!("Error cleaning up object at offset {}\n", i)
+ }
+ }
+ }
+
+ for &fd in &info.file_list.close_on_free {
+ let closer = match DeferredFdCloser::new(GFP_KERNEL) {
+ Ok(closer) => closer,
+ Err(kernel::alloc::AllocError) => {
+ // Ignore allocation failures.
+ break;
+ }
+ };
+
+ // Here, we ignore errors. The operation can fail if the fd is not valid, or if the
+ // method is called from a kthread. However, this is always called from a syscall,
+ // so the latter case cannot happen, and we don't care about the first case.
+ let _ = closer.close_fd(fd);
+ }
+
+ if info.clear_on_free {
+ if let Err(e) = self.fill_zero() {
+ pr_warn!("Failed to clear data on free: {:?}", e);
+ }
+ }
+ }
+
+ self.process.buffer_raw_free(self.ptr);
+ }
+}
+
+/// A wrapper around `Allocation` that is being created.
+///
+/// If the allocation is destroyed while wrapped in this wrapper, then the allocation will be
+/// considered to be part of a failed transaction. Successful transactions avoid that by calling
+/// `success`, which skips the destructor.
+#[repr(transparent)]
+pub(crate) struct NewAllocation(pub(crate) Allocation);
+
+impl NewAllocation {
+ pub(crate) fn success(self) -> Allocation {
+ // This skips the destructor.
+ //
+ // SAFETY: This type is `#[repr(transparent)]`, so the layout matches.
+ unsafe { core::mem::transmute(self) }
+ }
+}
+
+impl core::ops::Deref for NewAllocation {
+ type Target = Allocation;
+ fn deref(&self) -> &Allocation {
+ &self.0
+ }
+}
+
+impl core::ops::DerefMut for NewAllocation {
+ fn deref_mut(&mut self) -> &mut Allocation {
+ &mut self.0
+ }
+}
+
+/// A view into the beginning of an allocation.
+///
+/// All attempts to read or write outside of the view will fail. To intentionally access outside of
+/// this view, use the `alloc` field of this struct directly.
+pub(crate) struct AllocationView<'a> {
+ pub(crate) alloc: &'a mut Allocation,
+ limit: usize,
+}
+
+impl<'a> AllocationView<'a> {
+ pub(crate) fn new(alloc: &'a mut Allocation, limit: usize) -> Self {
+ AllocationView { alloc, limit }
+ }
+
+ pub(crate) fn read<T: FromBytes>(&self, offset: usize) -> Result<T> {
+ if offset.checked_add(size_of::<T>()).ok_or(EINVAL)? > self.limit {
+ return Err(EINVAL);
+ }
+ self.alloc.read(offset)
+ }
+
+ pub(crate) fn write<T: AsBytes>(&self, offset: usize, obj: &T) -> Result {
+ if offset.checked_add(size_of::<T>()).ok_or(EINVAL)? > self.limit {
+ return Err(EINVAL);
+ }
+ self.alloc.write(offset, obj)
+ }
+
+ pub(crate) fn copy_into(
+ &self,
+ reader: &mut UserSliceReader,
+ offset: usize,
+ size: usize,
+ ) -> Result {
+ if offset.checked_add(size).ok_or(EINVAL)? > self.limit {
+ return Err(EINVAL);
+ }
+ self.alloc.copy_into(reader, offset, size)
+ }
+
+ pub(crate) fn transfer_binder_object(
+ &self,
+ offset: usize,
+ obj: &uapi::flat_binder_object,
+ strong: bool,
+ node_ref: NodeRef,
+ ) -> Result {
+ let mut newobj = FlatBinderObject::default();
+ let node = node_ref.node.clone();
+ if Arc::ptr_eq(&node_ref.node.owner, &self.alloc.process) {
+ // The receiving process is the owner of the node, so send it a binder object (instead
+ // of a handle).
+ let (ptr, cookie) = node.get_id();
+ newobj.hdr.type_ = if strong {
+ BINDER_TYPE_BINDER
+ } else {
+ BINDER_TYPE_WEAK_BINDER
+ };
+ newobj.flags = obj.flags;
+ newobj.__bindgen_anon_1.binder = ptr as _;
+ newobj.cookie = cookie as _;
+ self.write(offset, &newobj)?;
+ // Increment the user ref count on the node. It will be decremented as part of the
+ // destruction of the buffer, when we see a binder or weak-binder object.
+ node.update_refcount(true, 1, strong);
+ } else {
+ // The receiving process is different from the owner, so we need to insert a handle to
+ // the binder object.
+ let handle = self
+ .alloc
+ .process
+ .as_arc_borrow()
+ .insert_or_update_handle(node_ref, false)?;
+ newobj.hdr.type_ = if strong {
+ BINDER_TYPE_HANDLE
+ } else {
+ BINDER_TYPE_WEAK_HANDLE
+ };
+ newobj.flags = obj.flags;
+ newobj.__bindgen_anon_1.handle = handle;
+ if self.write(offset, &newobj).is_err() {
+ // Decrement ref count on the handle we just created.
+ let _ = self
+ .alloc
+ .process
+ .as_arc_borrow()
+ .update_ref(handle, false, strong);
+ return Err(EINVAL);
+ }
+ }
+
+ Ok(())
+ }
+
+ fn cleanup_object(&self, index_offset: usize) -> Result {
+ let offset = self.alloc.read(index_offset)?;
+ let header = self.read::<BinderObjectHeader>(offset)?;
+ match header.type_ {
+ BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => {
+ let obj = self.read::<FlatBinderObject>(offset)?;
+ let strong = header.type_ == BINDER_TYPE_BINDER;
+ // SAFETY: The type is `BINDER_TYPE_{WEAK_}BINDER`, so the `binder` field is
+ // populated.
+ let ptr = unsafe { obj.__bindgen_anon_1.binder };
+ let cookie = obj.cookie;
+ self.alloc.process.update_node(ptr, cookie, strong);
+ Ok(())
+ }
+ BINDER_TYPE_WEAK_HANDLE | BINDER_TYPE_HANDLE => {
+ let obj = self.read::<FlatBinderObject>(offset)?;
+ let strong = header.type_ == BINDER_TYPE_HANDLE;
+ // SAFETY: The type is `BINDER_TYPE_{WEAK_}HANDLE`, so the `handle` field is
+ // populated.
+ let handle = unsafe { obj.__bindgen_anon_1.handle };
+ self.alloc
+ .process
+ .as_arc_borrow()
+ .update_ref(handle, false, strong)
+ }
+ _ => Ok(()),
+ }
+ }
+}
+
+/// A binder object as it is serialized.
+///
+/// # Invariants
+///
+/// All bytes must be initialized, and the value of `self.hdr.type_` must be one of the allowed
+/// types.
+#[repr(C)]
+pub(crate) union BinderObject {
+ hdr: uapi::binder_object_header,
+ fbo: uapi::flat_binder_object,
+ fdo: uapi::binder_fd_object,
+ bbo: uapi::binder_buffer_object,
+ fdao: uapi::binder_fd_array_object,
+}
+
+/// A view into a `BinderObject` that can be used in a match statement.
+pub(crate) enum BinderObjectRef<'a> {
+ Binder(&'a mut uapi::flat_binder_object),
+ Handle(&'a mut uapi::flat_binder_object),
+ Fd(&'a mut uapi::binder_fd_object),
+ Ptr(&'a mut uapi::binder_buffer_object),
+ Fda(&'a mut uapi::binder_fd_array_object),
+}
+
+impl BinderObject {
+ pub(crate) fn read_from(reader: &mut UserSliceReader) -> Result<BinderObject> {
+ let object = Self::read_from_inner(|slice| {
+ let read_len = usize::min(slice.len(), reader.len());
+ reader.clone_reader().read_slice(&mut slice[..read_len])?;
+ Ok(())
+ })?;
+
+ // If we used a object type smaller than the largest object size, then we've read more
+ // bytes than we needed to. However, we used `.clone_reader()` to avoid advancing the
+ // original reader. Now, we call `skip` so that the caller's reader is advanced by the
+ // right amount.
+ //
+ // The `skip` call fails if the reader doesn't have `size` bytes available. This could
+ // happen if the type header corresponds to an object type that is larger than the rest of
+ // the reader.
+ //
+ // Any extra bytes beyond the size of the object are inaccessible after this call, so
+ // reading them again from the `reader` later does not result in TOCTOU bugs.
+ reader.skip(object.size())?;
+
+ Ok(object)
+ }
+
+ /// Use the provided reader closure to construct a `BinderObject`.
+ ///
+ /// The closure should write the bytes for the object into the provided slice.
+ pub(crate) fn read_from_inner<R>(reader: R) -> Result<BinderObject>
+ where
+ R: FnOnce(&mut [u8; size_of::<BinderObject>()]) -> Result<()>,
+ {
+ let mut obj = MaybeUninit::<BinderObject>::zeroed();
+
+ // SAFETY: The lengths of `BinderObject` and `[u8; size_of::<BinderObject>()]` are equal,
+ // and the byte array has an alignment requirement of one, so the pointer cast is okay.
+ // Additionally, `obj` was initialized to zeros, so the byte array will not be
+ // uninitialized.
+ (reader)(unsafe { &mut *obj.as_mut_ptr().cast() })?;
+
+ // SAFETY: The entire object is initialized, so accessing this field is safe.
+ let type_ = unsafe { obj.assume_init_ref().hdr.type_ };
+ if Self::type_to_size(type_).is_none() {
+ // The value of `obj.hdr_type_` was invalid.
+ return Err(EINVAL);
+ }
+
+ // SAFETY: All bytes are initialized (since we zeroed them at the start) and we checked
+ // that `self.hdr.type_` is one of the allowed types, so the type invariants are satisfied.
+ unsafe { Ok(obj.assume_init()) }
+ }
+
+ pub(crate) fn as_ref(&mut self) -> BinderObjectRef<'_> {
+ use BinderObjectRef::*;
+ // SAFETY: The constructor ensures that all bytes of `self` are initialized, and all
+ // variants of this union accept all initialized bit patterns.
+ unsafe {
+ match self.hdr.type_ {
+ BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => Binder(&mut self.fbo),
+ BINDER_TYPE_WEAK_HANDLE | BINDER_TYPE_HANDLE => Handle(&mut self.fbo),
+ BINDER_TYPE_FD => Fd(&mut self.fdo),
+ BINDER_TYPE_PTR => Ptr(&mut self.bbo),
+ BINDER_TYPE_FDA => Fda(&mut self.fdao),
+ // SAFETY: By the type invariant, the value of `self.hdr.type_` cannot have any
+ // other value than the ones checked above.
+ _ => core::hint::unreachable_unchecked(),
+ }
+ }
+ }
+
+ pub(crate) fn size(&self) -> usize {
+ // SAFETY: The entire object is initialized, so accessing this field is safe.
+ let type_ = unsafe { self.hdr.type_ };
+
+ // SAFETY: The type invariants guarantee that the type field is correct.
+ unsafe { Self::type_to_size(type_).unwrap_unchecked() }
+ }
+
+ fn type_to_size(type_: u32) -> Option<usize> {
+ match type_ {
+ BINDER_TYPE_WEAK_BINDER => Some(size_of::<uapi::flat_binder_object>()),
+ BINDER_TYPE_BINDER => Some(size_of::<uapi::flat_binder_object>()),
+ BINDER_TYPE_WEAK_HANDLE => Some(size_of::<uapi::flat_binder_object>()),
+ BINDER_TYPE_HANDLE => Some(size_of::<uapi::flat_binder_object>()),
+ BINDER_TYPE_FD => Some(size_of::<uapi::binder_fd_object>()),
+ BINDER_TYPE_PTR => Some(size_of::<uapi::binder_buffer_object>()),
+ BINDER_TYPE_FDA => Some(size_of::<uapi::binder_fd_array_object>()),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Default)]
+struct FileList {
+ files_to_translate: KVec<FileEntry>,
+ close_on_free: KVec<u32>,
+}
+
+struct FileEntry {
+ /// The file for which a descriptor will be created in the recipient process.
+ file: ARef<File>,
+ /// The offset in the buffer where the file descriptor is stored.
+ buffer_offset: usize,
+ /// Whether this fd should be closed when the allocation is freed.
+ close_on_free: bool,
+}
+
+pub(crate) struct TranslatedFds {
+ reservations: KVec<Reservation>,
+ /// If commit is called, then these fds should be closed. (If commit is not called, then they
+ /// shouldn't be closed.)
+ close_on_free: FdsCloseOnFree,
+}
+
+struct Reservation {
+ res: FileDescriptorReservation,
+ file: ARef<File>,
+}
+
+impl TranslatedFds {
+ pub(crate) fn new() -> Self {
+ Self {
+ reservations: KVec::new(),
+ close_on_free: FdsCloseOnFree(KVec::new()),
+ }
+ }
+
+ pub(crate) fn commit(self) -> FdsCloseOnFree {
+ for entry in self.reservations {
+ entry.res.fd_install(entry.file);
+ }
+
+ self.close_on_free
+ }
+}
+
+pub(crate) struct FdsCloseOnFree(KVec<u32>);
diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs
new file mode 100644
index 000000000000..3d135ec03ca7
--- /dev/null
+++ b/drivers/android/binder/context.rs
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{
+ error::Error,
+ list::{List, ListArc, ListLinks},
+ prelude::*,
+ security,
+ str::{CStr, CString},
+ sync::{Arc, Mutex},
+ task::Kuid,
+};
+
+use crate::{error::BinderError, node::NodeRef, process::Process};
+
+kernel::sync::global_lock! {
+ // SAFETY: We call `init` in the module initializer, so it's initialized before first use.
+ pub(crate) unsafe(uninit) static CONTEXTS: Mutex<ContextList> = ContextList {
+ list: List::new(),
+ };
+}
+
+pub(crate) struct ContextList {
+ list: List<Context>,
+}
+
+pub(crate) fn get_all_contexts() -> Result<KVec<Arc<Context>>> {
+ let lock = CONTEXTS.lock();
+
+ let count = lock.list.iter().count();
+
+ let mut ctxs = KVec::with_capacity(count, GFP_KERNEL)?;
+ for ctx in &lock.list {
+ ctxs.push(Arc::from(ctx), GFP_KERNEL)?;
+ }
+ Ok(ctxs)
+}
+
+/// This struct keeps track of the processes using this context, and which process is the context
+/// manager.
+struct Manager {
+ node: Option<NodeRef>,
+ uid: Option<Kuid>,
+ all_procs: List<Process>,
+}
+
+/// There is one context per binder file (/dev/binder, /dev/hwbinder, etc)
+#[pin_data]
+pub(crate) struct Context {
+ #[pin]
+ manager: Mutex<Manager>,
+ pub(crate) name: CString,
+ #[pin]
+ links: ListLinks,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for Context { untracked; }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<0> for Context {
+ using ListLinks { self.links };
+ }
+}
+
+impl Context {
+ pub(crate) fn new(name: &CStr) -> Result<Arc<Self>> {
+ let name = CString::try_from(name)?;
+ let list_ctx = ListArc::pin_init::<Error>(
+ try_pin_init!(Context {
+ name,
+ links <- ListLinks::new(),
+ manager <- kernel::new_mutex!(Manager {
+ all_procs: List::new(),
+ node: None,
+ uid: None,
+ }, "Context::manager"),
+ }),
+ GFP_KERNEL,
+ )?;
+
+ let ctx = list_ctx.clone_arc();
+ CONTEXTS.lock().list.push_back(list_ctx);
+
+ Ok(ctx)
+ }
+
+ /// Called when the file for this context is unlinked.
+ ///
+ /// No-op if called twice.
+ pub(crate) fn deregister(&self) {
+ // SAFETY: We never add the context to any other linked list than this one, so it is either
+ // in this list, or not in any list.
+ unsafe { CONTEXTS.lock().list.remove(self) };
+ }
+
+ pub(crate) fn register_process(self: &Arc<Self>, proc: ListArc<Process>) {
+ if !Arc::ptr_eq(self, &proc.ctx) {
+ pr_err!("Context::register_process called on the wrong context.");
+ return;
+ }
+ self.manager.lock().all_procs.push_back(proc);
+ }
+
+ pub(crate) fn deregister_process(self: &Arc<Self>, proc: &Process) {
+ if !Arc::ptr_eq(self, &proc.ctx) {
+ pr_err!("Context::deregister_process called on the wrong context.");
+ return;
+ }
+ // SAFETY: We just checked that this is the right list.
+ unsafe { self.manager.lock().all_procs.remove(proc) };
+ }
+
+ pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result {
+ let mut manager = self.manager.lock();
+ if manager.node.is_some() {
+ pr_warn!("BINDER_SET_CONTEXT_MGR already set");
+ return Err(EBUSY);
+ }
+ security::binder_set_context_mgr(&node_ref.node.owner.cred)?;
+
+ // If the context manager has been set before, ensure that we use the same euid.
+ let caller_uid = Kuid::current_euid();
+ if let Some(ref uid) = manager.uid {
+ if *uid != caller_uid {
+ return Err(EPERM);
+ }
+ }
+
+ manager.node = Some(node_ref);
+ manager.uid = Some(caller_uid);
+ Ok(())
+ }
+
+ pub(crate) fn unset_manager_node(&self) {
+ let node_ref = self.manager.lock().node.take();
+ drop(node_ref);
+ }
+
+ pub(crate) fn get_manager_node(&self, strong: bool) -> Result<NodeRef, BinderError> {
+ self.manager
+ .lock()
+ .node
+ .as_ref()
+ .ok_or_else(BinderError::new_dead)?
+ .clone(strong)
+ .map_err(BinderError::from)
+ }
+
+ pub(crate) fn for_each_proc<F>(&self, mut func: F)
+ where
+ F: FnMut(&Process),
+ {
+ let lock = self.manager.lock();
+ for proc in &lock.all_procs {
+ func(&proc);
+ }
+ }
+
+ pub(crate) fn get_all_procs(&self) -> Result<KVec<Arc<Process>>> {
+ let lock = self.manager.lock();
+ let count = lock.all_procs.iter().count();
+
+ let mut procs = KVec::with_capacity(count, GFP_KERNEL)?;
+ for proc in &lock.all_procs {
+ procs.push(Arc::from(proc), GFP_KERNEL)?;
+ }
+ Ok(procs)
+ }
+
+ pub(crate) fn get_procs_with_pid(&self, pid: i32) -> Result<KVec<Arc<Process>>> {
+ let orig = self.get_all_procs()?;
+ let mut backing = KVec::with_capacity(orig.len(), GFP_KERNEL)?;
+ for proc in orig.into_iter().filter(|proc| proc.task.pid() == pid) {
+ backing.push(proc, GFP_KERNEL)?;
+ }
+ Ok(backing)
+ }
+}
diff --git a/drivers/android/binder/deferred_close.rs b/drivers/android/binder/deferred_close.rs
new file mode 100644
index 000000000000..ac895c04d0cb
--- /dev/null
+++ b/drivers/android/binder/deferred_close.rs
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Logic for closing files in a deferred manner.
+//!
+//! This file could make sense to have in `kernel::fs`, but it was rejected for being too
+//! Binder-specific.
+
+use core::mem::MaybeUninit;
+use kernel::{
+ alloc::{AllocError, Flags},
+ bindings,
+ prelude::*,
+};
+
+/// Helper used for closing file descriptors in a way that is safe even if the file is currently
+/// held using `fdget`.
+///
+/// Additional motivation can be found in commit 80cd795630d6 ("binder: fix use-after-free due to
+/// ksys_close() during fdget()") and in the comments on `binder_do_fd_close`.
+pub(crate) struct DeferredFdCloser {
+ inner: KBox<DeferredFdCloserInner>,
+}
+
+/// SAFETY: This just holds an allocation with no real content, so there's no safety issue with
+/// moving it across threads.
+unsafe impl Send for DeferredFdCloser {}
+/// SAFETY: This just holds an allocation with no real content, so there's no safety issue with
+/// moving it across threads.
+unsafe impl Sync for DeferredFdCloser {}
+
+/// # Invariants
+///
+/// If the `file` pointer is non-null, then it points at a `struct file` and owns a refcount to
+/// that file.
+#[repr(C)]
+struct DeferredFdCloserInner {
+ twork: MaybeUninit<bindings::callback_head>,
+ file: *mut bindings::file,
+}
+
+impl DeferredFdCloser {
+ /// Create a new [`DeferredFdCloser`].
+ pub(crate) fn new(flags: Flags) -> Result<Self, AllocError> {
+ Ok(Self {
+ // INVARIANT: The `file` pointer is null, so the type invariant does not apply.
+ inner: KBox::new(
+ DeferredFdCloserInner {
+ twork: MaybeUninit::uninit(),
+ file: core::ptr::null_mut(),
+ },
+ flags,
+ )?,
+ })
+ }
+
+ /// Schedule a task work that closes the file descriptor when this task returns to userspace.
+ ///
+ /// Fails if this is called from a context where we cannot run work when returning to
+ /// userspace. (E.g., from a kthread.)
+ pub(crate) fn close_fd(self, fd: u32) -> Result<(), DeferredFdCloseError> {
+ use bindings::task_work_notify_mode_TWA_RESUME as TWA_RESUME;
+
+ // In this method, we schedule the task work before closing the file. This is because
+ // scheduling a task work is fallible, and we need to know whether it will fail before we
+ // attempt to close the file.
+
+ // Task works are not available on kthreads.
+ let current = kernel::current!();
+
+ // Check if this is a kthread.
+ // SAFETY: Reading `flags` from a task is always okay.
+ if unsafe { ((*current.as_ptr()).flags & bindings::PF_KTHREAD) != 0 } {
+ return Err(DeferredFdCloseError::TaskWorkUnavailable);
+ }
+
+ // Transfer ownership of the box's allocation to a raw pointer. This disables the
+ // destructor, so we must manually convert it back to a KBox to drop it.
+ //
+ // Until we convert it back to a `KBox`, there are no aliasing requirements on this
+ // pointer.
+ let inner = KBox::into_raw(self.inner);
+
+ // The `callback_head` field is first in the struct, so this cast correctly gives us a
+ // pointer to the field.
+ let callback_head = inner.cast::<bindings::callback_head>();
+ // SAFETY: This pointer offset operation does not go out-of-bounds.
+ let file_field = unsafe { core::ptr::addr_of_mut!((*inner).file) };
+
+ let current = current.as_ptr();
+
+ // SAFETY: This function currently has exclusive access to the `DeferredFdCloserInner`, so
+ // it is okay for us to perform unsynchronized writes to its `callback_head` field.
+ unsafe { bindings::init_task_work(callback_head, Some(Self::do_close_fd)) };
+
+ // SAFETY: This inserts the `DeferredFdCloserInner` into the task workqueue for the current
+ // task. If this operation is successful, then this transfers exclusive ownership of the
+ // `callback_head` field to the C side until it calls `do_close_fd`, and we don't touch or
+ // invalidate the field during that time.
+ //
+ // When the C side calls `do_close_fd`, the safety requirements of that method are
+ // satisfied because when a task work is executed, the callback is given ownership of the
+ // pointer.
+ //
+ // The file pointer is currently null. If it is changed to be non-null before `do_close_fd`
+ // is called, then that change happens due to the write at the end of this function, and
+ // that write has a safety comment that explains why the refcount can be dropped when
+ // `do_close_fd` runs.
+ let res = unsafe { bindings::task_work_add(current, callback_head, TWA_RESUME) };
+
+ if res != 0 {
+ // SAFETY: Scheduling the task work failed, so we still have ownership of the box, so
+ // we may destroy it.
+ unsafe { drop(KBox::from_raw(inner)) };
+
+ return Err(DeferredFdCloseError::TaskWorkUnavailable);
+ }
+
+ // This removes the fd from the fd table in `current`. The file is not fully closed until
+ // `filp_close` is called. We are given ownership of one refcount to the file.
+ //
+ // SAFETY: This is safe no matter what `fd` is. If the `fd` is valid (that is, if the
+ // pointer is non-null), then we call `filp_close` on the returned pointer as required by
+ // `file_close_fd`.
+ let file = unsafe { bindings::file_close_fd(fd) };
+ if file.is_null() {
+ // We don't clean up the task work since that might be expensive if the task work queue
+ // is long. Just let it execute and let it clean up for itself.
+ return Err(DeferredFdCloseError::BadFd);
+ }
+
+ // Acquire a second refcount to the file.
+ //
+ // SAFETY: The `file` pointer points at a file with a non-zero refcount.
+ unsafe { bindings::get_file(file) };
+
+ // This method closes the fd, consuming one of our two refcounts. There could be active
+ // light refcounts created from that fd, so we must ensure that the file has a positive
+ // refcount for the duration of those active light refcounts. We do that by holding on to
+ // the second refcount until the current task returns to userspace.
+ //
+ // SAFETY: The `file` pointer is valid. Passing `current->files` as the file table to close
+ // it in is correct, since we just got the `fd` from `file_close_fd` which also uses
+ // `current->files`.
+ //
+ // Note: fl_owner_t is currently a void pointer.
+ unsafe { bindings::filp_close(file, (*current).files as bindings::fl_owner_t) };
+
+ // We update the file pointer that the task work is supposed to fput. This transfers
+ // ownership of our last refcount.
+ //
+ // INVARIANT: This changes the `file` field of a `DeferredFdCloserInner` from null to
+ // non-null. This doesn't break the type invariant for `DeferredFdCloserInner` because we
+ // still own a refcount to the file, so we can pass ownership of that refcount to the
+ // `DeferredFdCloserInner`.
+ //
+ // When `do_close_fd` runs, it must be safe for it to `fput` the refcount. However, this is
+ // the case because all light refcounts that are associated with the fd we closed
+ // previously must be dropped when `do_close_fd`, since light refcounts must be dropped
+ // before returning to userspace.
+ //
+ // SAFETY: Task works are executed on the current thread right before we return to
+ // userspace, so this write is guaranteed to happen before `do_close_fd` is called, which
+ // means that a race is not possible here.
+ unsafe { *file_field = file };
+
+ Ok(())
+ }
+
+ /// # Safety
+ ///
+ /// The provided pointer must point at the `twork` field of a `DeferredFdCloserInner` stored in
+ /// a `KBox`, and the caller must pass exclusive ownership of that `KBox`. Furthermore, if the
+ /// file pointer is non-null, then it must be okay to release the refcount by calling `fput`.
+ unsafe extern "C" fn do_close_fd(inner: *mut bindings::callback_head) {
+ // SAFETY: The caller just passed us ownership of this box.
+ let inner = unsafe { KBox::from_raw(inner.cast::<DeferredFdCloserInner>()) };
+ if !inner.file.is_null() {
+ // SAFETY: By the type invariants, we own a refcount to this file, and the caller
+ // guarantees that dropping the refcount now is okay.
+ unsafe { bindings::fput(inner.file) };
+ }
+ // The allocation is freed when `inner` goes out of scope.
+ }
+}
+
+/// Represents a failure to close an fd in a deferred manner.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub(crate) enum DeferredFdCloseError {
+ /// Closing the fd failed because we were unable to schedule a task work.
+ TaskWorkUnavailable,
+ /// Closing the fd failed because the fd does not exist.
+ BadFd,
+}
+
+impl From<DeferredFdCloseError> for Error {
+ fn from(err: DeferredFdCloseError) -> Error {
+ match err {
+ DeferredFdCloseError::TaskWorkUnavailable => ESRCH,
+ DeferredFdCloseError::BadFd => EBADF,
+ }
+ }
+}
diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs
new file mode 100644
index 000000000000..33f51b4139c7
--- /dev/null
+++ b/drivers/android/binder/defs.rs
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use core::mem::MaybeUninit;
+use core::ops::{Deref, DerefMut};
+use kernel::{
+ transmute::{AsBytes, FromBytes},
+ uapi::{self, *},
+};
+
+macro_rules! pub_no_prefix {
+ ($prefix:ident, $($newname:ident),+ $(,)?) => {
+ $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
+ };
+}
+
+pub_no_prefix!(
+ binder_driver_return_protocol_,
+ BR_TRANSACTION,
+ BR_TRANSACTION_SEC_CTX,
+ BR_REPLY,
+ BR_DEAD_REPLY,
+ BR_FAILED_REPLY,
+ BR_FROZEN_REPLY,
+ BR_NOOP,
+ BR_SPAWN_LOOPER,
+ BR_TRANSACTION_COMPLETE,
+ BR_TRANSACTION_PENDING_FROZEN,
+ BR_ONEWAY_SPAM_SUSPECT,
+ BR_OK,
+ BR_ERROR,
+ BR_INCREFS,
+ BR_ACQUIRE,
+ BR_RELEASE,
+ BR_DECREFS,
+ BR_DEAD_BINDER,
+ BR_CLEAR_DEATH_NOTIFICATION_DONE,
+ BR_FROZEN_BINDER,
+ BR_CLEAR_FREEZE_NOTIFICATION_DONE,
+);
+
+pub_no_prefix!(
+ binder_driver_command_protocol_,
+ BC_TRANSACTION,
+ BC_TRANSACTION_SG,
+ BC_REPLY,
+ BC_REPLY_SG,
+ BC_FREE_BUFFER,
+ BC_ENTER_LOOPER,
+ BC_EXIT_LOOPER,
+ BC_REGISTER_LOOPER,
+ BC_INCREFS,
+ BC_ACQUIRE,
+ BC_RELEASE,
+ BC_DECREFS,
+ BC_INCREFS_DONE,
+ BC_ACQUIRE_DONE,
+ BC_REQUEST_DEATH_NOTIFICATION,
+ BC_CLEAR_DEATH_NOTIFICATION,
+ BC_DEAD_BINDER_DONE,
+ BC_REQUEST_FREEZE_NOTIFICATION,
+ BC_CLEAR_FREEZE_NOTIFICATION,
+ BC_FREEZE_NOTIFICATION_DONE,
+);
+
+pub_no_prefix!(
+ flat_binder_object_flags_,
+ FLAT_BINDER_FLAG_ACCEPTS_FDS,
+ FLAT_BINDER_FLAG_TXN_SECURITY_CTX
+);
+
+pub_no_prefix!(
+ transaction_flags_,
+ TF_ONE_WAY,
+ TF_ACCEPT_FDS,
+ TF_CLEAR_BUF,
+ TF_UPDATE_TXN
+);
+
+pub(crate) use uapi::{
+ BINDER_TYPE_BINDER, BINDER_TYPE_FD, BINDER_TYPE_FDA, BINDER_TYPE_HANDLE, BINDER_TYPE_PTR,
+ BINDER_TYPE_WEAK_BINDER, BINDER_TYPE_WEAK_HANDLE,
+};
+
+macro_rules! decl_wrapper {
+ ($newname:ident, $wrapped:ty) => {
+ // Define a wrapper around the C type. Use `MaybeUninit` to enforce that the value of
+ // padding bytes must be preserved.
+ #[derive(Copy, Clone)]
+ #[repr(transparent)]
+ pub(crate) struct $newname(MaybeUninit<$wrapped>);
+
+ // SAFETY: This macro is only used with types where this is ok.
+ unsafe impl FromBytes for $newname {}
+ // SAFETY: This macro is only used with types where this is ok.
+ unsafe impl AsBytes for $newname {}
+
+ impl Deref for $newname {
+ type Target = $wrapped;
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: We use `MaybeUninit` only to preserve padding. The value must still
+ // always be valid.
+ unsafe { self.0.assume_init_ref() }
+ }
+ }
+
+ impl DerefMut for $newname {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ // SAFETY: We use `MaybeUninit` only to preserve padding. The value must still
+ // always be valid.
+ unsafe { self.0.assume_init_mut() }
+ }
+ }
+
+ impl Default for $newname {
+ fn default() -> Self {
+ // Create a new value of this type where all bytes (including padding) are zeroed.
+ Self(MaybeUninit::zeroed())
+ }
+ }
+ };
+}
+
+decl_wrapper!(BinderNodeDebugInfo, uapi::binder_node_debug_info);
+decl_wrapper!(BinderNodeInfoForRef, uapi::binder_node_info_for_ref);
+decl_wrapper!(FlatBinderObject, uapi::flat_binder_object);
+decl_wrapper!(BinderFdObject, uapi::binder_fd_object);
+decl_wrapper!(BinderFdArrayObject, uapi::binder_fd_array_object);
+decl_wrapper!(BinderObjectHeader, uapi::binder_object_header);
+decl_wrapper!(BinderBufferObject, uapi::binder_buffer_object);
+decl_wrapper!(BinderTransactionData, uapi::binder_transaction_data);
+decl_wrapper!(
+ BinderTransactionDataSecctx,
+ uapi::binder_transaction_data_secctx
+);
+decl_wrapper!(BinderTransactionDataSg, uapi::binder_transaction_data_sg);
+decl_wrapper!(BinderWriteRead, uapi::binder_write_read);
+decl_wrapper!(BinderVersion, uapi::binder_version);
+decl_wrapper!(BinderFrozenStatusInfo, uapi::binder_frozen_status_info);
+decl_wrapper!(BinderFreezeInfo, uapi::binder_freeze_info);
+decl_wrapper!(BinderFrozenStateInfo, uapi::binder_frozen_state_info);
+decl_wrapper!(BinderHandleCookie, uapi::binder_handle_cookie);
+decl_wrapper!(ExtendedError, uapi::binder_extended_error);
+
+impl BinderVersion {
+ pub(crate) fn current() -> Self {
+ Self(MaybeUninit::new(uapi::binder_version {
+ protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _,
+ }))
+ }
+}
+
+impl BinderTransactionData {
+ pub(crate) fn with_buffers_size(self, buffers_size: u64) -> BinderTransactionDataSg {
+ BinderTransactionDataSg(MaybeUninit::new(uapi::binder_transaction_data_sg {
+ transaction_data: *self,
+ buffers_size,
+ }))
+ }
+}
+
+impl BinderTransactionDataSecctx {
+ /// View the inner data as wrapped in `BinderTransactionData`.
+ pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData {
+ // SAFETY: Transparent wrapper is safe to transmute.
+ unsafe {
+ &mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data
+ as *mut BinderTransactionData)
+ }
+ }
+}
+
+impl ExtendedError {
+ pub(crate) fn new(id: u32, command: u32, param: i32) -> Self {
+ Self(MaybeUninit::new(uapi::binder_extended_error {
+ id,
+ command,
+ param,
+ }))
+ }
+}
diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs
new file mode 100644
index 000000000000..b24497cfa292
--- /dev/null
+++ b/drivers/android/binder/error.rs
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::fmt;
+use kernel::prelude::*;
+
+use crate::defs::*;
+
+pub(crate) type BinderResult<T = ()> = core::result::Result<T, BinderError>;
+
+/// An error that will be returned to userspace via the `BINDER_WRITE_READ` ioctl rather than via
+/// errno.
+pub(crate) struct BinderError {
+ pub(crate) reply: u32,
+ source: Option<Error>,
+}
+
+impl BinderError {
+ pub(crate) fn new_dead() -> Self {
+ Self {
+ reply: BR_DEAD_REPLY,
+ source: None,
+ }
+ }
+
+ pub(crate) fn new_frozen() -> Self {
+ Self {
+ reply: BR_FROZEN_REPLY,
+ source: None,
+ }
+ }
+
+ pub(crate) fn new_frozen_oneway() -> Self {
+ Self {
+ reply: BR_TRANSACTION_PENDING_FROZEN,
+ source: None,
+ }
+ }
+
+ pub(crate) fn is_dead(&self) -> bool {
+ self.reply == BR_DEAD_REPLY
+ }
+
+ pub(crate) fn as_errno(&self) -> kernel::ffi::c_int {
+ self.source.unwrap_or(EINVAL).to_errno()
+ }
+
+ pub(crate) fn should_pr_warn(&self) -> bool {
+ self.source.is_some()
+ }
+}
+
+/// Convert an errno into a `BinderError` and store the errno used to construct it. The errno
+/// should be stored as the thread's extended error when given to userspace.
+impl From<Error> for BinderError {
+ fn from(source: Error) -> Self {
+ Self {
+ reply: BR_FAILED_REPLY,
+ source: Some(source),
+ }
+ }
+}
+
+impl From<kernel::fs::file::BadFdError> for BinderError {
+ fn from(source: kernel::fs::file::BadFdError) -> Self {
+ BinderError::from(Error::from(source))
+ }
+}
+
+impl From<kernel::alloc::AllocError> for BinderError {
+ fn from(_: kernel::alloc::AllocError) -> Self {
+ Self {
+ reply: BR_FAILED_REPLY,
+ source: Some(ENOMEM),
+ }
+ }
+}
+
+impl fmt::Debug for BinderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.reply {
+ BR_FAILED_REPLY => match self.source.as_ref() {
+ Some(source) => f
+ .debug_struct("BR_FAILED_REPLY")
+ .field("source", source)
+ .finish(),
+ None => f.pad("BR_FAILED_REPLY"),
+ },
+ BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"),
+ BR_FROZEN_REPLY => f.pad("BR_FROZEN_REPLY"),
+ BR_TRANSACTION_PENDING_FROZEN => f.pad("BR_TRANSACTION_PENDING_FROZEN"),
+ BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"),
+ _ => f
+ .debug_struct("BinderError")
+ .field("reply", &self.reply)
+ .finish(),
+ }
+ }
+}
diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
new file mode 100644
index 000000000000..53b60035639a
--- /dev/null
+++ b/drivers/android/binder/freeze.rs
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{
+ alloc::AllocError,
+ list::ListArc,
+ prelude::*,
+ rbtree::{self, RBTreeNodeReservation},
+ seq_file::SeqFile,
+ seq_print,
+ sync::{Arc, UniqueArc},
+ uaccess::UserSliceReader,
+};
+
+use crate::{
+ defs::*, node::Node, process::Process, thread::Thread, BinderReturnWriter, DArc, DLArc,
+ DTRWrap, DeliverToRead,
+};
+
+#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
+pub(crate) struct FreezeCookie(u64);
+
+/// Represents a listener for changes to the frozen state of a process.
+pub(crate) struct FreezeListener {
+ /// The node we are listening for.
+ pub(crate) node: DArc<Node>,
+ /// The cookie of this freeze listener.
+ cookie: FreezeCookie,
+ /// What value of `is_frozen` did we most recently tell userspace about?
+ last_is_frozen: Option<bool>,
+ /// We sent a `BR_FROZEN_BINDER` and we are waiting for `BC_FREEZE_NOTIFICATION_DONE` before
+ /// sending any other commands.
+ is_pending: bool,
+ /// Userspace sent `BC_CLEAR_FREEZE_NOTIFICATION` and we need to reply with
+ /// `BR_CLEAR_FREEZE_NOTIFICATION_DONE` as soon as possible. If `is_pending` is set, then we
+ /// must wait for it to be unset before we can reply.
+ is_clearing: bool,
+ /// Number of cleared duplicates that can't be deleted until userspace sends
+ /// `BC_FREEZE_NOTIFICATION_DONE`.
+ num_pending_duplicates: u64,
+ /// Number of cleared duplicates that can be deleted.
+ num_cleared_duplicates: u64,
+}
+
+impl FreezeListener {
+ /// Is it okay to create a new listener with the same cookie as this one for the provided node?
+ ///
+ /// Under some scenarios, userspace may delete a freeze listener and immediately recreate it
+ /// with the same cookie. This results in duplicate listeners. To avoid issues with ambiguity,
+ /// we allow this only if the new listener is for the same node, and we also require that the
+ /// old listener has already been cleared.
+ fn allow_duplicate(&self, node: &DArc<Node>) -> bool {
+ Arc::ptr_eq(&self.node, node) && self.is_clearing
+ }
+}
+
+type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
+
+/// Represents a notification that the freeze state has changed.
+pub(crate) struct FreezeMessage {
+ cookie: FreezeCookie,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for FreezeMessage {
+ untracked;
+ }
+}
+
+impl FreezeMessage {
+ fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {
+ UniqueArc::new_uninit(flags)
+ }
+
+ fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
+ match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
+ Ok(msg) => ListArc::from(msg),
+ Err(err) => match err {},
+ }
+ }
+}
+
+impl DeliverToRead for FreezeMessage {
+ fn do_work(
+ self: DArc<Self>,
+ thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ let _removed_listener;
+ let mut node_refs = thread.process.node_refs.lock();
+ let Some(mut freeze_entry) = node_refs.freeze_listeners.find_mut(&self.cookie) else {
+ return Ok(true);
+ };
+ let freeze = freeze_entry.get_mut();
+
+ if freeze.num_cleared_duplicates > 0 {
+ freeze.num_cleared_duplicates -= 1;
+ drop(node_refs);
+ writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
+ writer.write_payload(&self.cookie.0)?;
+ return Ok(true);
+ }
+
+ if freeze.is_pending {
+ return Ok(true);
+ }
+ if freeze.is_clearing {
+ kernel::warn_on!(freeze.num_cleared_duplicates != 0);
+ if freeze.num_pending_duplicates > 0 {
+ // The primary freeze listener was deleted, so convert a pending duplicate back
+ // into the primary one.
+ freeze.num_pending_duplicates -= 1;
+ freeze.is_pending = true;
+ freeze.is_clearing = true;
+ } else {
+ _removed_listener = freeze_entry.remove_node();
+ }
+ drop(node_refs);
+ writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
+ writer.write_payload(&self.cookie.0)?;
+ Ok(true)
+ } else {
+ let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
+ if freeze.last_is_frozen == Some(is_frozen) {
+ return Ok(true);
+ }
+
+ let mut state_info = BinderFrozenStateInfo::default();
+ state_info.is_frozen = is_frozen as u32;
+ state_info.cookie = freeze.cookie.0;
+ freeze.is_pending = true;
+ freeze.last_is_frozen = Some(is_frozen);
+ drop(node_refs);
+
+ writer.write_code(BR_FROZEN_BINDER)?;
+ writer.write_payload(&state_info)?;
+ // BR_FROZEN_BINDER notifications can cause transactions
+ Ok(false)
+ }
+ }
+
+ fn cancel(self: DArc<Self>) {}
+
+ fn should_sync_wakeup(&self) -> bool {
+ false
+ }
+
+ #[inline(never)]
+ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
+ seq_print!(m, "{}has frozen binder\n", prefix);
+ Ok(())
+ }
+}
+
+impl FreezeListener {
+ pub(crate) fn on_process_exit(&self, proc: &Arc<Process>) {
+ if !self.is_clearing {
+ self.node.remove_freeze_listener(proc);
+ }
+ }
+}
+
+impl Process {
+ pub(crate) fn request_freeze_notif(
+ self: &Arc<Self>,
+ reader: &mut UserSliceReader,
+ ) -> Result<()> {
+ let hc = reader.read::<BinderHandleCookie>()?;
+ let handle = hc.handle;
+ let cookie = FreezeCookie(hc.cookie);
+
+ let msg = FreezeMessage::new(GFP_KERNEL)?;
+ let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
+
+ let mut node_refs_guard = self.node_refs.lock();
+ let node_refs = &mut *node_refs_guard;
+ let Some(info) = node_refs.by_handle.get_mut(&handle) else {
+ pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
+ return Err(EINVAL);
+ };
+ if info.freeze().is_some() {
+ pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
+ return Err(EINVAL);
+ }
+ let node_ref = info.node_ref();
+ let freeze_entry = node_refs.freeze_listeners.entry(cookie);
+
+ if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
+ if !dupe.get().allow_duplicate(&node_ref.node) {
+ pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
+ return Err(EINVAL);
+ }
+ }
+
+ // All failure paths must come before this call, and all modifications must come after this
+ // call.
+ node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
+
+ match freeze_entry {
+ rbtree::Entry::Vacant(entry) => {
+ entry.insert(
+ FreezeListener {
+ cookie,
+ node: node_ref.node.clone(),
+ last_is_frozen: None,
+ is_pending: false,
+ is_clearing: false,
+ num_pending_duplicates: 0,
+ num_cleared_duplicates: 0,
+ },
+ alloc,
+ );
+ }
+ rbtree::Entry::Occupied(mut dupe) => {
+ let dupe = dupe.get_mut();
+ if dupe.is_pending {
+ dupe.num_pending_duplicates += 1;
+ } else {
+ dupe.num_cleared_duplicates += 1;
+ }
+ dupe.last_is_frozen = None;
+ dupe.is_pending = false;
+ dupe.is_clearing = false;
+ }
+ }
+
+ *info.freeze() = Some(cookie);
+ let msg = FreezeMessage::init(msg, cookie);
+ drop(node_refs_guard);
+ let _ = self.push_work(msg);
+ Ok(())
+ }
+
+ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
+ let cookie = FreezeCookie(reader.read()?);
+ let alloc = FreezeMessage::new(GFP_KERNEL)?;
+ let mut node_refs_guard = self.node_refs.lock();
+ let node_refs = &mut *node_refs_guard;
+ let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
+ pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
+ return Err(EINVAL);
+ };
+ let mut clear_msg = None;
+ if freeze.num_pending_duplicates > 0 {
+ clear_msg = Some(FreezeMessage::init(alloc, cookie));
+ freeze.num_pending_duplicates -= 1;
+ freeze.num_cleared_duplicates += 1;
+ } else {
+ if !freeze.is_pending {
+ pr_warn!(
+ "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
+ cookie.0
+ );
+ return Err(EINVAL);
+ }
+ let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
+ if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
+ // Immediately send another FreezeMessage.
+ clear_msg = Some(FreezeMessage::init(alloc, cookie));
+ }
+ freeze.is_pending = false;
+ }
+ drop(node_refs_guard);
+ if let Some(clear_msg) = clear_msg {
+ let _ = self.push_work(clear_msg);
+ }
+ Ok(())
+ }
+
+ pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
+ let hc = reader.read::<BinderHandleCookie>()?;
+ let handle = hc.handle;
+ let cookie = FreezeCookie(hc.cookie);
+
+ let alloc = FreezeMessage::new(GFP_KERNEL)?;
+ let mut node_refs_guard = self.node_refs.lock();
+ let node_refs = &mut *node_refs_guard;
+ let Some(info) = node_refs.by_handle.get_mut(&handle) else {
+ pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
+ return Err(EINVAL);
+ };
+ let Some(info_cookie) = info.freeze() else {
+ pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
+ return Err(EINVAL);
+ };
+ if *info_cookie != cookie {
+ pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
+ return Err(EINVAL);
+ }
+ let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
+ pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
+ return Err(EINVAL);
+ };
+ listener.is_clearing = true;
+ listener.node.remove_freeze_listener(self);
+ *info.freeze() = None;
+ let mut msg = None;
+ if !listener.is_pending {
+ msg = Some(FreezeMessage::init(alloc, cookie));
+ }
+ drop(node_refs_guard);
+
+ if let Some(msg) = msg {
+ let _ = self.push_work(msg);
+ }
+ Ok(())
+ }
+
+ fn get_freeze_cookie(&self, node: &DArc<Node>) -> Option<FreezeCookie> {
+ let node_refs = &mut *self.node_refs.lock();
+ let handle = node_refs.by_node.get(&node.global_id())?;
+ let node_ref = node_refs.by_handle.get_mut(handle)?;
+ *node_ref.freeze()
+ }
+
+ /// Creates a vector of every freeze listener on this process.
+ ///
+ /// Returns pairs of the remote process listening for notifications and the local node it is
+ /// listening on.
+ #[expect(clippy::type_complexity)]
+ fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, AllocError> {
+ // Defined before `inner` to drop after releasing spinlock if `push_within_capacity` fails.
+ let mut node_proc_pair;
+
+ // We pre-allocate space for up to 8 recipients before we take the spinlock. However, if
+ // the allocation fails, use a vector with a capacity of zero instead of failing. After
+ // all, there might not be any freeze listeners, in which case this operation could still
+ // succeed.
+ let mut recipients =
+ KVVec::with_capacity(8, GFP_KERNEL).unwrap_or_else(|_err| KVVec::new());
+
+ let mut inner = self.lock_with_nodes();
+ let mut curr = inner.nodes.cursor_front_mut();
+ while let Some(cursor) = curr {
+ let (key, node) = cursor.current();
+ let key = *key;
+ let list = node.freeze_list(&inner.inner);
+ let len = list.len();
+
+ if recipients.spare_capacity_mut().len() < len {
+ drop(inner);
+ recipients.reserve(len, GFP_KERNEL)?;
+ inner = self.lock_with_nodes();
+ // Find the node we were looking at and try again. If the set of nodes was changed,
+ // then just proceed to the next node. This is ok because we don't guarantee the
+ // inclusion of nodes that are added or removed in parallel with this operation.
+ curr = inner.nodes.cursor_lower_bound_mut(&key);
+ continue;
+ }
+
+ for proc in list {
+ node_proc_pair = (node.clone(), proc.clone());
+ recipients
+ .push_within_capacity(node_proc_pair)
+ .map_err(|_| {
+ pr_err!(
+ "push_within_capacity failed even though we checked the capacity\n"
+ );
+ AllocError
+ })?;
+ }
+
+ curr = cursor.move_next();
+ }
+ Ok(recipients)
+ }
+
+ /// Prepare allocations for sending freeze messages.
+ pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocError> {
+ let recipients = self.find_freeze_recipients()?;
+ let mut batch = KVVec::with_capacity(recipients.len(), GFP_KERNEL)?;
+ for (node, proc) in recipients {
+ let Some(cookie) = proc.get_freeze_cookie(&node) else {
+ // If the freeze listener was removed in the meantime, just discard the
+ // notification.
+ continue;
+ };
+ let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
+ let msg = FreezeMessage::init(msg_alloc, cookie);
+ batch.push((proc, msg), GFP_KERNEL)?;
+ }
+
+ Ok(FreezeMessages { batch })
+ }
+}
+
+pub(crate) struct FreezeMessages {
+ batch: KVVec<(Arc<Process>, DLArc<FreezeMessage>)>,
+}
+
+impl FreezeMessages {
+ pub(crate) fn send_messages(self) {
+ for (proc, msg) in self.batch {
+ let _ = proc.push_work(msg);
+ }
+ }
+}
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
new file mode 100644
index 000000000000..c26d113ede96
--- /dev/null
+++ b/drivers/android/binder/node.rs
@@ -0,0 +1,1131 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{
+ list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},
+ prelude::*,
+ seq_file::SeqFile,
+ seq_print,
+ sync::lock::{spinlock::SpinLockBackend, Guard},
+ sync::{Arc, LockedBy, SpinLock},
+};
+
+use crate::{
+ defs::*,
+ error::BinderError,
+ process::{NodeRefInfo, Process, ProcessInner},
+ thread::Thread,
+ transaction::Transaction,
+ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
+};
+
+use core::mem;
+
+mod wrapper;
+pub(crate) use self::wrapper::CritIncrWrapper;
+
+#[derive(Debug)]
+pub(crate) struct CouldNotDeliverCriticalIncrement;
+
+/// Keeps track of how this node is scheduled.
+///
+/// There are two ways to schedule a node to a work list. Just schedule the node itself, or
+/// allocate a wrapper that references the node and schedule the wrapper. These wrappers exists to
+/// make it possible to "move" a node from one list to another - when `do_work` is called directly
+/// on the `Node`, then it's a no-op if there's also a pending wrapper.
+///
+/// Wrappers are generally only needed for zero-to-one refcount increments, and there are two cases
+/// of this: weak increments and strong increments. We call such increments "critical" because it
+/// is critical that they are delivered to the thread doing the increment. Some examples:
+///
+/// * One thread makes a zero-to-one strong increment, and another thread makes a zero-to-one weak
+/// increment. Delivering the node to the thread doing the weak increment is wrong, since the
+/// thread doing the strong increment may have ended a long time ago when the command is actually
+/// processed by userspace.
+///
+/// * We have a weak reference and are about to drop it on one thread. But then another thread does
+/// a zero-to-one strong increment. If the strong increment gets sent to the thread that was
+/// about to drop the weak reference, then the strong increment could be processed after the
+/// other thread has already exited, which would be too late.
+///
+/// Note that trying to create a `ListArc` to the node can succeed even if `has_normal_push` is
+/// set. This is because another thread might just have popped the node from a todo list, but not
+/// yet called `do_work`. However, if `has_normal_push` is false, then creating a `ListArc` should
+/// always succeed.
+///
+/// Like the other fields in `NodeInner`, the delivery state is protected by the process lock.
+struct DeliveryState {
+ /// Is the `Node` currently scheduled?
+ has_pushed_node: bool,
+
+ /// Is a wrapper currently scheduled?
+ ///
+ /// The wrapper is used only for strong zero2one increments.
+ has_pushed_wrapper: bool,
+
+ /// Is the currently scheduled `Node` scheduled due to a weak zero2one increment?
+ ///
+ /// Weak zero2one operations are always scheduled using the `Node`.
+ has_weak_zero2one: bool,
+
+ /// Is the currently scheduled wrapper/`Node` scheduled due to a strong zero2one increment?
+ ///
+ /// If `has_pushed_wrapper` is set, then the strong zero2one increment was scheduled using the
+ /// wrapper. Otherwise, `has_pushed_node` must be set and it was scheduled using the `Node`.
+ has_strong_zero2one: bool,
+}
+
+impl DeliveryState {
+ fn should_normal_push(&self) -> bool {
+ !self.has_pushed_node && !self.has_pushed_wrapper
+ }
+
+ fn did_normal_push(&mut self) {
+ assert!(self.should_normal_push());
+ self.has_pushed_node = true;
+ }
+
+ fn should_push_weak_zero2one(&self) -> bool {
+ !self.has_weak_zero2one && !self.has_strong_zero2one
+ }
+
+ fn can_push_weak_zero2one_normally(&self) -> bool {
+ !self.has_pushed_node
+ }
+
+ fn did_push_weak_zero2one(&mut self) {
+ assert!(self.should_push_weak_zero2one());
+ assert!(self.can_push_weak_zero2one_normally());
+ self.has_pushed_node = true;
+ self.has_weak_zero2one = true;
+ }
+
+ fn should_push_strong_zero2one(&self) -> bool {
+ !self.has_strong_zero2one
+ }
+
+ fn can_push_strong_zero2one_normally(&self) -> bool {
+ !self.has_pushed_node
+ }
+
+ fn did_push_strong_zero2one(&mut self) {
+ assert!(self.should_push_strong_zero2one());
+ assert!(self.can_push_strong_zero2one_normally());
+ self.has_pushed_node = true;
+ self.has_strong_zero2one = true;
+ }
+
+ fn did_push_strong_zero2one_wrapper(&mut self) {
+ assert!(self.should_push_strong_zero2one());
+ assert!(!self.can_push_strong_zero2one_normally());
+ self.has_pushed_wrapper = true;
+ self.has_strong_zero2one = true;
+ }
+}
+
+struct CountState {
+ /// The reference count.
+ count: usize,
+ /// Whether the process that owns this node thinks that we hold a refcount on it. (Note that
+ /// even if count is greater than one, we only increment it once in the owning process.)
+ has_count: bool,
+}
+
+impl CountState {
+ fn new() -> Self {
+ Self {
+ count: 0,
+ has_count: false,
+ }
+ }
+}
+
+struct NodeInner {
+ /// Strong refcounts held on this node by `NodeRef` objects.
+ strong: CountState,
+ /// Weak refcounts held on this node by `NodeRef` objects.
+ weak: CountState,
+ delivery_state: DeliveryState,
+ /// The binder driver guarantees that oneway transactions sent to the same node are serialized,
+ /// that is, userspace will not be given the next one until it has finished processing the
+ /// previous oneway transaction. This is done to avoid the case where two oneway transactions
+ /// arrive in opposite order from the order in which they were sent. (E.g., they could be
+ /// delivered to two different threads, which could appear as-if they were sent in opposite
+ /// order.)
+ ///
+ /// To fix that, we store pending oneway transactions in a separate list in the node, and don't
+ /// deliver the next oneway transaction until userspace signals that it has finished processing
+ /// the previous oneway transaction by calling the `BC_FREE_BUFFER` ioctl.
+ oneway_todo: List<DTRWrap<Transaction>>,
+ /// Keeps track of whether this node has a pending oneway transaction.
+ ///
+ /// When this is true, incoming oneway transactions are stored in `oneway_todo`, instead of
+ /// being delivered directly to the process.
+ has_oneway_transaction: bool,
+ /// List of processes to deliver a notification to when this node is destroyed (usually due to
+ /// the process dying).
+ death_list: List<DTRWrap<NodeDeath>, 1>,
+ /// List of processes to deliver freeze notifications to.
+ freeze_list: KVVec<Arc<Process>>,
+ /// The number of active BR_INCREFS or BR_ACQUIRE operations. (should be maximum two)
+ ///
+ /// If this is non-zero, then we postpone any BR_RELEASE or BR_DECREFS notifications until the
+ /// active operations have ended. This avoids the situation an increment and decrement get
+ /// reordered from userspace's perspective.
+ active_inc_refs: u8,
+ /// List of `NodeRefInfo` objects that reference this node.
+ refs: List<NodeRefInfo, { NodeRefInfo::LIST_NODE }>,
+}
+
+#[pin_data]
+pub(crate) struct Node {
+ pub(crate) debug_id: usize,
+ ptr: u64,
+ pub(crate) cookie: u64,
+ pub(crate) flags: u32,
+ pub(crate) owner: Arc<Process>,
+ inner: LockedBy<NodeInner, ProcessInner>,
+ #[pin]
+ links_track: AtomicTracker,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for Node {
+ tracked_by links_track: AtomicTracker;
+ }
+}
+
+// Make `oneway_todo` work.
+kernel::list::impl_list_item! {
+ impl ListItem<0> for DTRWrap<Transaction> {
+ using ListLinks { self.links.inner };
+ }
+}
+
+impl Node {
+ pub(crate) fn new(
+ ptr: u64,
+ cookie: u64,
+ flags: u32,
+ owner: Arc<Process>,
+ ) -> impl PinInit<Self> {
+ pin_init!(Self {
+ inner: LockedBy::new(
+ &owner.inner,
+ NodeInner {
+ strong: CountState::new(),
+ weak: CountState::new(),
+ delivery_state: DeliveryState {
+ has_pushed_node: false,
+ has_pushed_wrapper: false,
+ has_weak_zero2one: false,
+ has_strong_zero2one: false,
+ },
+ death_list: List::new(),
+ oneway_todo: List::new(),
+ freeze_list: KVVec::new(),
+ has_oneway_transaction: false,
+ active_inc_refs: 0,
+ refs: List::new(),
+ },
+ ),
+ debug_id: super::next_debug_id(),
+ ptr,
+ cookie,
+ flags,
+ owner,
+ links_track <- AtomicTracker::new(),
+ })
+ }
+
+ pub(crate) fn has_oneway_transaction(&self, owner_inner: &mut ProcessInner) -> bool {
+ let inner = self.inner.access_mut(owner_inner);
+ inner.has_oneway_transaction
+ }
+
+ #[inline(never)]
+ pub(crate) fn full_debug_print(
+ &self,
+ m: &SeqFile,
+ owner_inner: &mut ProcessInner,
+ ) -> Result<()> {
+ let inner = self.inner.access_mut(owner_inner);
+ seq_print!(
+ m,
+ " node {}: u{:016x} c{:016x} hs {} hw {} cs {} cw {}",
+ self.debug_id,
+ self.ptr,
+ self.cookie,
+ inner.strong.has_count,
+ inner.weak.has_count,
+ inner.strong.count,
+ inner.weak.count,
+ );
+ if !inner.refs.is_empty() {
+ seq_print!(m, " proc");
+ for node_ref in &inner.refs {
+ seq_print!(m, " {}", node_ref.process.task.pid());
+ }
+ }
+ seq_print!(m, "\n");
+ for t in &inner.oneway_todo {
+ t.debug_print_inner(m, " pending async transaction ");
+ }
+ Ok(())
+ }
+
+ /// Insert the `NodeRef` into this `refs` list.
+ ///
+ /// # Safety
+ ///
+ /// It must be the case that `info.node_ref.node` is this node.
+ pub(crate) unsafe fn insert_node_info(
+ &self,
+ info: ListArc<NodeRefInfo, { NodeRefInfo::LIST_NODE }>,
+ ) {
+ self.inner
+ .access_mut(&mut self.owner.inner.lock())
+ .refs
+ .push_front(info);
+ }
+
+ /// Insert the `NodeRef` into this `refs` list.
+ ///
+ /// # Safety
+ ///
+ /// It must be the case that `info.node_ref.node` is this node.
+ pub(crate) unsafe fn remove_node_info(
+ &self,
+ info: &NodeRefInfo,
+ ) -> Option<ListArc<NodeRefInfo, { NodeRefInfo::LIST_NODE }>> {
+ // SAFETY: We always insert `NodeRefInfo` objects into the `refs` list of the node that it
+ // references in `info.node_ref.node`. That is this node, so `info` cannot possibly be in
+ // the `refs` list of another node.
+ unsafe {
+ self.inner
+ .access_mut(&mut self.owner.inner.lock())
+ .refs
+ .remove(info)
+ }
+ }
+
+ /// An id that is unique across all binder nodes on the system. Used as the key in the
+ /// `by_node` map.
+ pub(crate) fn global_id(&self) -> usize {
+ self as *const Node as usize
+ }
+
+ pub(crate) fn get_id(&self) -> (u64, u64) {
+ (self.ptr, self.cookie)
+ }
+
+ pub(crate) fn add_death(
+ &self,
+ death: ListArc<DTRWrap<NodeDeath>, 1>,
+ guard: &mut Guard<'_, ProcessInner, SpinLockBackend>,
+ ) {
+ self.inner.access_mut(guard).death_list.push_back(death);
+ }
+
+ pub(crate) fn inc_ref_done_locked(
+ self: &DArc<Node>,
+ _strong: bool,
+ owner_inner: &mut ProcessInner,
+ ) -> Option<DLArc<Node>> {
+ let inner = self.inner.access_mut(owner_inner);
+ if inner.active_inc_refs == 0 {
+ pr_err!("inc_ref_done called when no active inc_refs");
+ return None;
+ }
+
+ inner.active_inc_refs -= 1;
+ if inner.active_inc_refs == 0 {
+ // Having active inc_refs can inhibit dropping of ref-counts. Calculate whether we
+ // would send a refcount decrement, and if so, tell the caller to schedule us.
+ let strong = inner.strong.count > 0;
+ let has_strong = inner.strong.has_count;
+ let weak = strong || inner.weak.count > 0;
+ let has_weak = inner.weak.has_count;
+
+ let should_drop_weak = !weak && has_weak;
+ let should_drop_strong = !strong && has_strong;
+
+ // If we want to drop the ref-count again, tell the caller to schedule a work node for
+ // that.
+ let need_push = should_drop_weak || should_drop_strong;
+
+ if need_push && inner.delivery_state.should_normal_push() {
+ let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap();
+ inner.delivery_state.did_normal_push();
+ Some(list_arc)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+
+ pub(crate) fn update_refcount_locked(
+ self: &DArc<Node>,
+ inc: bool,
+ strong: bool,
+ count: usize,
+ owner_inner: &mut ProcessInner,
+ ) -> Option<DLArc<Node>> {
+ let is_dead = owner_inner.is_dead;
+ let inner = self.inner.access_mut(owner_inner);
+
+ // Get a reference to the state we'll update.
+ let state = if strong {
+ &mut inner.strong
+ } else {
+ &mut inner.weak
+ };
+
+ // Update the count and determine whether we need to push work.
+ let need_push = if inc {
+ state.count += count;
+ // TODO: This method shouldn't be used for zero-to-one increments.
+ !is_dead && !state.has_count
+ } else {
+ if state.count < count {
+ pr_err!("Failure: refcount underflow!");
+ return None;
+ }
+ state.count -= count;
+ !is_dead && state.count == 0 && state.has_count
+ };
+
+ if need_push && inner.delivery_state.should_normal_push() {
+ let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap();
+ inner.delivery_state.did_normal_push();
+ Some(list_arc)
+ } else {
+ None
+ }
+ }
+
+ pub(crate) fn incr_refcount_allow_zero2one(
+ self: &DArc<Self>,
+ strong: bool,
+ owner_inner: &mut ProcessInner,
+ ) -> Result<Option<DLArc<Node>>, CouldNotDeliverCriticalIncrement> {
+ let is_dead = owner_inner.is_dead;
+ let inner = self.inner.access_mut(owner_inner);
+
+ // Get a reference to the state we'll update.
+ let state = if strong {
+ &mut inner.strong
+ } else {
+ &mut inner.weak
+ };
+
+ // Update the count and determine whether we need to push work.
+ state.count += 1;
+ if is_dead || state.has_count {
+ return Ok(None);
+ }
+
+ // Userspace needs to be notified of this.
+ if !strong && inner.delivery_state.should_push_weak_zero2one() {
+ assert!(inner.delivery_state.can_push_weak_zero2one_normally());
+ let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap();
+ inner.delivery_state.did_push_weak_zero2one();
+ Ok(Some(list_arc))
+ } else if strong && inner.delivery_state.should_push_strong_zero2one() {
+ if inner.delivery_state.can_push_strong_zero2one_normally() {
+ let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap();
+ inner.delivery_state.did_push_strong_zero2one();
+ Ok(Some(list_arc))
+ } else {
+ state.count -= 1;
+ Err(CouldNotDeliverCriticalIncrement)
+ }
+ } else {
+ // Work is already pushed, and we don't need to push again.
+ Ok(None)
+ }
+ }
+
+ pub(crate) fn incr_refcount_allow_zero2one_with_wrapper(
+ self: &DArc<Self>,
+ strong: bool,
+ wrapper: CritIncrWrapper,
+ owner_inner: &mut ProcessInner,
+ ) -> Option<DLArc<dyn DeliverToRead>> {
+ match self.incr_refcount_allow_zero2one(strong, owner_inner) {
+ Ok(Some(node)) => Some(node as _),
+ Ok(None) => None,
+ Err(CouldNotDeliverCriticalIncrement) => {
+ assert!(strong);
+ let inner = self.inner.access_mut(owner_inner);
+ inner.strong.count += 1;
+ inner.delivery_state.did_push_strong_zero2one_wrapper();
+ Some(wrapper.init(self.clone()))
+ }
+ }
+ }
+
+ pub(crate) fn update_refcount(self: &DArc<Self>, inc: bool, count: usize, strong: bool) {
+ self.owner
+ .inner
+ .lock()
+ .update_node_refcount(self, inc, strong, count, None);
+ }
+
+ pub(crate) fn populate_counts(
+ &self,
+ out: &mut BinderNodeInfoForRef,
+ guard: &Guard<'_, ProcessInner, SpinLockBackend>,
+ ) {
+ let inner = self.inner.access(guard);
+ out.strong_count = inner.strong.count as _;
+ out.weak_count = inner.weak.count as _;
+ }
+
+ pub(crate) fn populate_debug_info(
+ &self,
+ out: &mut BinderNodeDebugInfo,
+ guard: &Guard<'_, ProcessInner, SpinLockBackend>,
+ ) {
+ out.ptr = self.ptr as _;
+ out.cookie = self.cookie as _;
+ let inner = self.inner.access(guard);
+ if inner.strong.has_count {
+ out.has_strong_ref = 1;
+ }
+ if inner.weak.has_count {
+ out.has_weak_ref = 1;
+ }
+ }
+
+ pub(crate) fn force_has_count(&self, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>) {
+ let inner = self.inner.access_mut(guard);
+ inner.strong.has_count = true;
+ inner.weak.has_count = true;
+ }
+
+ fn write(&self, writer: &mut BinderReturnWriter<'_>, code: u32) -> Result {
+ writer.write_code(code)?;
+ writer.write_payload(&self.ptr)?;
+ writer.write_payload(&self.cookie)?;
+ Ok(())
+ }
+
+ pub(crate) fn submit_oneway(
+ &self,
+ transaction: DLArc<Transaction>,
+ guard: &mut Guard<'_, ProcessInner, SpinLockBackend>,
+ ) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> {
+ if guard.is_dead {
+ return Err((BinderError::new_dead(), transaction));
+ }
+
+ let inner = self.inner.access_mut(guard);
+ if inner.has_oneway_transaction {
+ inner.oneway_todo.push_back(transaction);
+ } else {
+ inner.has_oneway_transaction = true;
+ guard.push_work(transaction)?;
+ }
+ Ok(())
+ }
+
+ pub(crate) fn release(&self) {
+ let mut guard = self.owner.inner.lock();
+ while let Some(work) = self.inner.access_mut(&mut guard).oneway_todo.pop_front() {
+ drop(guard);
+ work.into_arc().cancel();
+ guard = self.owner.inner.lock();
+ }
+
+ while let Some(death) = self.inner.access_mut(&mut guard).death_list.pop_front() {
+ drop(guard);
+ death.into_arc().set_dead();
+ guard = self.owner.inner.lock();
+ }
+ }
+
+ pub(crate) fn pending_oneway_finished(&self) {
+ let mut guard = self.owner.inner.lock();
+ if guard.is_dead {
+ // Cleanup will happen in `Process::deferred_release`.
+ return;
+ }
+
+ let inner = self.inner.access_mut(&mut guard);
+
+ let transaction = inner.oneway_todo.pop_front();
+ inner.has_oneway_transaction = transaction.is_some();
+ if let Some(transaction) = transaction {
+ match guard.push_work(transaction) {
+ Ok(()) => {}
+ Err((_err, work)) => {
+ // Process is dead.
+ // This shouldn't happen due to the `is_dead` check, but if it does, just drop
+ // the transaction and return.
+ drop(guard);
+ drop(work);
+ }
+ }
+ }
+ }
+
+ /// Finds an outdated transaction that the given transaction can replace.
+ ///
+ /// If one is found, it is removed from the list and returned.
+ pub(crate) fn take_outdated_transaction(
+ &self,
+ new: &Transaction,
+ guard: &mut Guard<'_, ProcessInner, SpinLockBackend>,
+ ) -> Option<DLArc<Transaction>> {
+ let inner = self.inner.access_mut(guard);
+ let mut cursor = inner.oneway_todo.cursor_front();
+ while let Some(next) = cursor.peek_next() {
+ if new.can_replace(&next) {
+ return Some(next.remove());
+ }
+ cursor.move_next();
+ }
+ None
+ }
+
+ /// This is split into a separate function since it's called by both `Node::do_work` and
+ /// `NodeWrapper::do_work`.
+ fn do_work_locked(
+ &self,
+ writer: &mut BinderReturnWriter<'_>,
+ mut guard: Guard<'_, ProcessInner, SpinLockBackend>,
+ ) -> Result<bool> {
+ let inner = self.inner.access_mut(&mut guard);
+ let strong = inner.strong.count > 0;
+ let has_strong = inner.strong.has_count;
+ let weak = strong || inner.weak.count > 0;
+ let has_weak = inner.weak.has_count;
+
+ if weak && !has_weak {
+ inner.weak.has_count = true;
+ inner.active_inc_refs += 1;
+ }
+
+ if strong && !has_strong {
+ inner.strong.has_count = true;
+ inner.active_inc_refs += 1;
+ }
+
+ let no_active_inc_refs = inner.active_inc_refs == 0;
+ let should_drop_weak = no_active_inc_refs && (!weak && has_weak);
+ let should_drop_strong = no_active_inc_refs && (!strong && has_strong);
+ if should_drop_weak {
+ inner.weak.has_count = false;
+ }
+ if should_drop_strong {
+ inner.strong.has_count = false;
+ }
+ if no_active_inc_refs && !weak {
+ // Remove the node if there are no references to it.
+ guard.remove_node(self.ptr);
+ }
+ drop(guard);
+
+ if weak && !has_weak {
+ self.write(writer, BR_INCREFS)?;
+ }
+ if strong && !has_strong {
+ self.write(writer, BR_ACQUIRE)?;
+ }
+ if should_drop_strong {
+ self.write(writer, BR_RELEASE)?;
+ }
+ if should_drop_weak {
+ self.write(writer, BR_DECREFS)?;
+ }
+
+ Ok(true)
+ }
+
+ pub(crate) fn add_freeze_listener(
+ &self,
+ process: &Arc<Process>,
+ flags: kernel::alloc::Flags,
+ ) -> Result {
+ let mut vec_alloc = KVVec::<Arc<Process>>::new();
+ loop {
+ let mut guard = self.owner.inner.lock();
+ // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
+ // listener, no the target.
+ let inner = self.inner.access_mut(&mut guard);
+ let len = inner.freeze_list.len();
+ if len >= inner.freeze_list.capacity() {
+ if len >= vec_alloc.capacity() {
+ drop(guard);
+ vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?;
+ continue;
+ }
+ mem::swap(&mut inner.freeze_list, &mut vec_alloc);
+ for elem in vec_alloc.drain_all() {
+ inner.freeze_list.push_within_capacity(elem)?;
+ }
+ }
+ inner.freeze_list.push_within_capacity(process.clone())?;
+ return Ok(());
+ }
+ }
+
+ pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) {
+ let _unused_capacity;
+ let mut guard = self.owner.inner.lock();
+ let inner = self.inner.access_mut(&mut guard);
+ let len = inner.freeze_list.len();
+ inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p));
+ if len == inner.freeze_list.len() {
+ pr_warn!(
+ "Could not remove freeze listener for {}\n",
+ p.pid_in_current_ns()
+ );
+ }
+ if inner.freeze_list.is_empty() {
+ _unused_capacity = mem::take(&mut inner.freeze_list);
+ }
+ }
+
+ pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc<Process>] {
+ &self.inner.access(guard).freeze_list
+ }
+}
+
+impl DeliverToRead for Node {
+ fn do_work(
+ self: DArc<Self>,
+ _thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ let mut owner_inner = self.owner.inner.lock();
+ let inner = self.inner.access_mut(&mut owner_inner);
+
+ assert!(inner.delivery_state.has_pushed_node);
+ if inner.delivery_state.has_pushed_wrapper {
+ // If the wrapper is scheduled, then we are either a normal push or weak zero2one
+ // increment, and the wrapper is a strong zero2one increment, so the wrapper always
+ // takes precedence over us.
+ assert!(inner.delivery_state.has_strong_zero2one);
+ inner.delivery_state.has_pushed_node = false;
+ inner.delivery_state.has_weak_zero2one = false;
+ return Ok(true);
+ }
+
+ inner.delivery_state.has_pushed_node = false;
+ inner.delivery_state.has_weak_zero2one = false;
+ inner.delivery_state.has_strong_zero2one = false;
+
+ self.do_work_locked(writer, owner_inner)
+ }
+
+ fn cancel(self: DArc<Self>) {}
+
+ fn should_sync_wakeup(&self) -> bool {
+ false
+ }
+
+ #[inline(never)]
+ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
+ seq_print!(
+ m,
+ "{}node work {}: u{:016x} c{:016x}\n",
+ prefix,
+ self.debug_id,
+ self.ptr,
+ self.cookie,
+ );
+ Ok(())
+ }
+}
+
+/// Represents something that holds one or more ref-counts to a `Node`.
+///
+/// Whenever process A holds a refcount to a node owned by a different process B, then process A
+/// will store a `NodeRef` that refers to the `Node` in process B. When process A releases the
+/// refcount, we destroy the NodeRef, which decrements the ref-count in process A.
+///
+/// This type is also used for some other cases. For example, a transaction allocation holds a
+/// refcount on the target node, and this is implemented by storing a `NodeRef` in the allocation
+/// so that the destructor of the allocation will drop a refcount of the `Node`.
+pub(crate) struct NodeRef {
+ pub(crate) node: DArc<Node>,
+ /// How many times does this NodeRef hold a refcount on the Node?
+ strong_node_count: usize,
+ weak_node_count: usize,
+ /// How many times does userspace hold a refcount on this NodeRef?
+ strong_count: usize,
+ weak_count: usize,
+}
+
+impl NodeRef {
+ pub(crate) fn new(node: DArc<Node>, strong_count: usize, weak_count: usize) -> Self {
+ Self {
+ node,
+ strong_node_count: strong_count,
+ weak_node_count: weak_count,
+ strong_count,
+ weak_count,
+ }
+ }
+
+ pub(crate) fn absorb(&mut self, mut other: Self) {
+ assert!(
+ Arc::ptr_eq(&self.node, &other.node),
+ "absorb called with differing nodes"
+ );
+ self.strong_node_count += other.strong_node_count;
+ self.weak_node_count += other.weak_node_count;
+ self.strong_count += other.strong_count;
+ self.weak_count += other.weak_count;
+ other.strong_count = 0;
+ other.weak_count = 0;
+ other.strong_node_count = 0;
+ other.weak_node_count = 0;
+
+ if self.strong_node_count >= 2 || self.weak_node_count >= 2 {
+ let mut guard = self.node.owner.inner.lock();
+ let inner = self.node.inner.access_mut(&mut guard);
+
+ if self.strong_node_count >= 2 {
+ inner.strong.count -= self.strong_node_count - 1;
+ self.strong_node_count = 1;
+ assert_ne!(inner.strong.count, 0);
+ }
+ if self.weak_node_count >= 2 {
+ inner.weak.count -= self.weak_node_count - 1;
+ self.weak_node_count = 1;
+ assert_ne!(inner.weak.count, 0);
+ }
+ }
+ }
+
+ pub(crate) fn get_count(&self) -> (usize, usize) {
+ (self.strong_count, self.weak_count)
+ }
+
+ pub(crate) fn clone(&self, strong: bool) -> Result<NodeRef> {
+ if strong && self.strong_count == 0 {
+ return Err(EINVAL);
+ }
+ Ok(self
+ .node
+ .owner
+ .inner
+ .lock()
+ .new_node_ref(self.node.clone(), strong, None))
+ }
+
+ /// Updates (increments or decrements) the number of references held against the node. If the
+ /// count being updated transitions from 0 to 1 or from 1 to 0, the node is notified by having
+ /// its `update_refcount` function called.
+ ///
+ /// Returns whether `self` should be removed (when both counts are zero).
+ pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool {
+ if strong && self.strong_count == 0 {
+ return false;
+ }
+ let (count, node_count, other_count) = if strong {
+ (
+ &mut self.strong_count,
+ &mut self.strong_node_count,
+ self.weak_count,
+ )
+ } else {
+ (
+ &mut self.weak_count,
+ &mut self.weak_node_count,
+ self.strong_count,
+ )
+ };
+ if inc {
+ if *count == 0 {
+ *node_count = 1;
+ self.node.update_refcount(true, 1, strong);
+ }
+ *count += 1;
+ } else {
+ if *count == 0 {
+ pr_warn!(
+ "pid {} performed invalid decrement on ref\n",
+ kernel::current!().pid()
+ );
+ return false;
+ }
+ *count -= 1;
+ if *count == 0 {
+ self.node.update_refcount(false, *node_count, strong);
+ *node_count = 0;
+ return other_count == 0;
+ }
+ }
+ false
+ }
+}
+
+impl Drop for NodeRef {
+ // This destructor is called conditionally from `Allocation::drop`. That branch is often
+ // mispredicted. Inlining this method call reduces the cost of those branch mispredictions.
+ #[inline(always)]
+ fn drop(&mut self) {
+ if self.strong_node_count > 0 {
+ self.node
+ .update_refcount(false, self.strong_node_count, true);
+ }
+ if self.weak_node_count > 0 {
+ self.node
+ .update_refcount(false, self.weak_node_count, false);
+ }
+ }
+}
+
+struct NodeDeathInner {
+ dead: bool,
+ cleared: bool,
+ notification_done: bool,
+ /// Indicates whether the normal flow was interrupted by removing the handle. In this case, we
+ /// need behave as if the death notification didn't exist (i.e., we don't deliver anything to
+ /// the user.
+ aborted: bool,
+}
+
+/// Used to deliver notifications when a process dies.
+///
+/// A process can request to be notified when a process dies using `BC_REQUEST_DEATH_NOTIFICATION`.
+/// This will make the driver send a `BR_DEAD_BINDER` to userspace when the process dies (or
+/// immediately if it is already dead). Userspace is supposed to respond with `BC_DEAD_BINDER_DONE`
+/// once it has processed the notification.
+///
+/// Userspace can unregister from death notifications using the `BC_CLEAR_DEATH_NOTIFICATION`
+/// command. In this case, the kernel will respond with `BR_CLEAR_DEATH_NOTIFICATION_DONE` once the
+/// notification has been removed. Note that if the remote process dies before the kernel has
+/// responded with `BR_CLEAR_DEATH_NOTIFICATION_DONE`, then the kernel will still send a
+/// `BR_DEAD_BINDER`, which userspace must be able to process. In this case, the kernel will wait
+/// for the `BC_DEAD_BINDER_DONE` command before it sends `BR_CLEAR_DEATH_NOTIFICATION_DONE`.
+///
+/// Note that even if the kernel sends a `BR_DEAD_BINDER`, this does not remove the death
+/// notification. Userspace must still remove it manually using `BC_CLEAR_DEATH_NOTIFICATION`.
+///
+/// If a process uses `BC_RELEASE` to destroy its last refcount on a node that has an active death
+/// registration, then the death registration is immediately deleted (we implement this using the
+/// `aborted` field). However, userspace is not supposed to delete a `NodeRef` without first
+/// deregistering death notifications, so this codepath is not executed under normal circumstances.
+#[pin_data]
+pub(crate) struct NodeDeath {
+ node: DArc<Node>,
+ process: Arc<Process>,
+ pub(crate) cookie: u64,
+ #[pin]
+ links_track: AtomicTracker<0>,
+ /// Used by the owner `Node` to store a list of registered death notifications.
+ ///
+ /// # Invariants
+ ///
+ /// Only ever used with the `death_list` list of `self.node`.
+ #[pin]
+ death_links: ListLinks<1>,
+ /// Used by the process to keep track of the death notifications for which we have sent a
+ /// `BR_DEAD_BINDER` but not yet received a `BC_DEAD_BINDER_DONE`.
+ ///
+ /// # Invariants
+ ///
+ /// Only ever used with the `delivered_deaths` list of `self.process`.
+ #[pin]
+ delivered_links: ListLinks<2>,
+ #[pin]
+ delivered_links_track: AtomicTracker<2>,
+ #[pin]
+ inner: SpinLock<NodeDeathInner>,
+}
+
+impl NodeDeath {
+ /// Constructs a new node death notification object.
+ pub(crate) fn new(
+ node: DArc<Node>,
+ process: Arc<Process>,
+ cookie: u64,
+ ) -> impl PinInit<DTRWrap<Self>> {
+ DTRWrap::new(pin_init!(
+ Self {
+ node,
+ process,
+ cookie,
+ links_track <- AtomicTracker::new(),
+ death_links <- ListLinks::new(),
+ delivered_links <- ListLinks::new(),
+ delivered_links_track <- AtomicTracker::new(),
+ inner <- kernel::new_spinlock!(NodeDeathInner {
+ dead: false,
+ cleared: false,
+ notification_done: false,
+ aborted: false,
+ }, "NodeDeath::inner"),
+ }
+ ))
+ }
+
+ /// Sets the cleared flag to `true`.
+ ///
+ /// It removes `self` from the node's death notification list if needed.
+ ///
+ /// Returns whether it needs to be queued.
+ pub(crate) fn set_cleared(self: &DArc<Self>, abort: bool) -> bool {
+ let (needs_removal, needs_queueing) = {
+ // Update state and determine if we need to queue a work item. We only need to do it
+ // when the node is not dead or if the user already completed the death notification.
+ let mut inner = self.inner.lock();
+ if abort {
+ inner.aborted = true;
+ }
+ if inner.cleared {
+ // Already cleared.
+ return false;
+ }
+ inner.cleared = true;
+ (!inner.dead, !inner.dead || inner.notification_done)
+ };
+
+ // Remove death notification from node.
+ if needs_removal {
+ let mut owner_inner = self.node.owner.inner.lock();
+ let node_inner = self.node.inner.access_mut(&mut owner_inner);
+ // SAFETY: A `NodeDeath` is never inserted into the death list of any node other than
+ // its owner, so it is either in this death list or in no death list.
+ unsafe { node_inner.death_list.remove(self) };
+ }
+ needs_queueing
+ }
+
+ /// Sets the 'notification done' flag to `true`.
+ pub(crate) fn set_notification_done(self: DArc<Self>, thread: &Thread) {
+ let needs_queueing = {
+ let mut inner = self.inner.lock();
+ inner.notification_done = true;
+ inner.cleared
+ };
+ if needs_queueing {
+ if let Some(death) = ListArc::try_from_arc_or_drop(self) {
+ let _ = thread.push_work_if_looper(death);
+ }
+ }
+ }
+
+ /// Sets the 'dead' flag to `true` and queues work item if needed.
+ pub(crate) fn set_dead(self: DArc<Self>) {
+ let needs_queueing = {
+ let mut inner = self.inner.lock();
+ if inner.cleared {
+ false
+ } else {
+ inner.dead = true;
+ true
+ }
+ };
+ if needs_queueing {
+ // Push the death notification to the target process. There is nothing else to do if
+ // it's already dead.
+ if let Some(death) = ListArc::try_from_arc_or_drop(self) {
+ let process = death.process.clone();
+ let _ = process.push_work(death);
+ }
+ }
+ }
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for NodeDeath {
+ tracked_by links_track: AtomicTracker;
+ }
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<1> for DTRWrap<NodeDeath> { untracked; }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<1> for DTRWrap<NodeDeath> {
+ using ListLinks { self.wrapped.death_links };
+ }
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<2> for DTRWrap<NodeDeath> {
+ tracked_by wrapped: NodeDeath;
+ }
+}
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<2> for NodeDeath {
+ tracked_by delivered_links_track: AtomicTracker<2>;
+ }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<2> for DTRWrap<NodeDeath> {
+ using ListLinks { self.wrapped.delivered_links };
+ }
+}
+
+impl DeliverToRead for NodeDeath {
+ fn do_work(
+ self: DArc<Self>,
+ _thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ let done = {
+ let inner = self.inner.lock();
+ if inner.aborted {
+ return Ok(true);
+ }
+ inner.cleared && (!inner.dead || inner.notification_done)
+ };
+
+ let cookie = self.cookie;
+ let cmd = if done {
+ BR_CLEAR_DEATH_NOTIFICATION_DONE
+ } else {
+ let process = self.process.clone();
+ let mut process_inner = process.inner.lock();
+ let inner = self.inner.lock();
+ if inner.aborted {
+ return Ok(true);
+ }
+ // We're still holding the inner lock, so it cannot be aborted while we insert it into
+ // the delivered list.
+ process_inner.death_delivered(self.clone());
+ BR_DEAD_BINDER
+ };
+
+ writer.write_code(cmd)?;
+ writer.write_payload(&cookie)?;
+ // DEAD_BINDER notifications can cause transactions, so stop processing work items when we
+ // get to a death notification.
+ Ok(cmd != BR_DEAD_BINDER)
+ }
+
+ fn cancel(self: DArc<Self>) {}
+
+ fn should_sync_wakeup(&self) -> bool {
+ false
+ }
+
+ #[inline(never)]
+ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
+ let inner = self.inner.lock();
+
+ let dead_binder = inner.dead && !inner.notification_done;
+
+ if dead_binder {
+ if inner.cleared {
+ seq_print!(m, "{}has cleared dead binder\n", prefix);
+ } else {
+ seq_print!(m, "{}has dead binder\n", prefix);
+ }
+ } else {
+ seq_print!(m, "{}has cleared death notification\n", prefix);
+ }
+
+ Ok(())
+ }
+}
diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs
new file mode 100644
index 000000000000..43294c050502
--- /dev/null
+++ b/drivers/android/binder/node/wrapper.rs
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{list::ListArc, prelude::*, seq_file::SeqFile, seq_print, sync::UniqueArc};
+
+use crate::{node::Node, thread::Thread, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead};
+
+use core::mem::MaybeUninit;
+
+pub(crate) struct CritIncrWrapper {
+ inner: UniqueArc<MaybeUninit<DTRWrap<NodeWrapper>>>,
+}
+
+impl CritIncrWrapper {
+ pub(crate) fn new() -> Result<Self> {
+ Ok(CritIncrWrapper {
+ inner: UniqueArc::new_uninit(GFP_KERNEL)?,
+ })
+ }
+
+ pub(super) fn init(self, node: DArc<Node>) -> DLArc<dyn DeliverToRead> {
+ match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) {
+ Ok(initialized) => ListArc::from(initialized) as _,
+ Err(err) => match err {},
+ }
+ }
+}
+
+struct NodeWrapper {
+ node: DArc<Node>,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for NodeWrapper {
+ untracked;
+ }
+}
+
+impl DeliverToRead for NodeWrapper {
+ fn do_work(
+ self: DArc<Self>,
+ _thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ let node = &self.node;
+ let mut owner_inner = node.owner.inner.lock();
+ let inner = node.inner.access_mut(&mut owner_inner);
+
+ let ds = &mut inner.delivery_state;
+
+ assert!(ds.has_pushed_wrapper);
+ assert!(ds.has_strong_zero2one);
+ ds.has_pushed_wrapper = false;
+ ds.has_strong_zero2one = false;
+
+ node.do_work_locked(writer, owner_inner)
+ }
+
+ fn cancel(self: DArc<Self>) {}
+
+ fn should_sync_wakeup(&self) -> bool {
+ false
+ }
+
+ #[inline(never)]
+ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
+ seq_print!(
+ m,
+ "{}node work {}: u{:016x} c{:016x}\n",
+ prefix,
+ self.node.debug_id,
+ self.node.ptr,
+ self.node.cookie,
+ );
+ Ok(())
+ }
+}
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
new file mode 100644
index 000000000000..9379038f61f5
--- /dev/null
+++ b/drivers/android/binder/page_range.rs
@@ -0,0 +1,734 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! This module has utilities for managing a page range where unused pages may be reclaimed by a
+//! vma shrinker.
+
+// To avoid deadlocks, locks are taken in the order:
+//
+// 1. mmap lock
+// 2. spinlock
+// 3. lru spinlock
+//
+// The shrinker will use trylock methods because it locks them in a different order.
+
+use core::{
+ marker::PhantomPinned,
+ mem::{size_of, size_of_val, MaybeUninit},
+ ptr,
+};
+
+use kernel::{
+ bindings,
+ error::Result,
+ ffi::{c_ulong, c_void},
+ mm::{virt, Mm, MmWithUser},
+ new_mutex, new_spinlock,
+ page::{Page, PAGE_SHIFT, PAGE_SIZE},
+ prelude::*,
+ str::CStr,
+ sync::{aref::ARef, Mutex, SpinLock},
+ task::Pid,
+ transmute::FromBytes,
+ types::Opaque,
+ uaccess::UserSliceReader,
+};
+
+/// Represents a shrinker that can be registered with the kernel.
+///
+/// Each shrinker can be used by many `ShrinkablePageRange` objects.
+#[repr(C)]
+pub(crate) struct Shrinker {
+ inner: Opaque<*mut bindings::shrinker>,
+ list_lru: Opaque<bindings::list_lru>,
+}
+
+// SAFETY: The shrinker and list_lru are thread safe.
+unsafe impl Send for Shrinker {}
+// SAFETY: The shrinker and list_lru are thread safe.
+unsafe impl Sync for Shrinker {}
+
+impl Shrinker {
+ /// Create a new shrinker.
+ ///
+ /// # Safety
+ ///
+ /// Before using this shrinker with a `ShrinkablePageRange`, the `register` method must have
+ /// been called exactly once, and it must not have returned an error.
+ pub(crate) const unsafe fn new() -> Self {
+ Self {
+ inner: Opaque::uninit(),
+ list_lru: Opaque::uninit(),
+ }
+ }
+
+ /// Register this shrinker with the kernel.
+ pub(crate) fn register(&'static self, name: &CStr) -> Result<()> {
+ // SAFETY: These fields are not yet used, so it's okay to zero them.
+ unsafe {
+ self.inner.get().write(ptr::null_mut());
+ self.list_lru.get().write_bytes(0, 1);
+ }
+
+ // SAFETY: The field is not yet used, so we can initialize it.
+ let ret = unsafe { bindings::__list_lru_init(self.list_lru.get(), false, ptr::null_mut()) };
+ if ret != 0 {
+ return Err(Error::from_errno(ret));
+ }
+
+ // SAFETY: The `name` points at a valid c string.
+ let shrinker = unsafe { bindings::shrinker_alloc(0, name.as_char_ptr()) };
+ if shrinker.is_null() {
+ // SAFETY: We initialized it, so its okay to destroy it.
+ unsafe { bindings::list_lru_destroy(self.list_lru.get()) };
+ return Err(Error::from_errno(ret));
+ }
+
+ // SAFETY: We're about to register the shrinker, and these are the fields we need to
+ // initialize. (All other fields are already zeroed.)
+ unsafe {
+ (&raw mut (*shrinker).count_objects).write(Some(rust_shrink_count));
+ (&raw mut (*shrinker).scan_objects).write(Some(rust_shrink_scan));
+ (&raw mut (*shrinker).private_data).write(self.list_lru.get().cast());
+ }
+
+ // SAFETY: The new shrinker has been fully initialized, so we can register it.
+ unsafe { bindings::shrinker_register(shrinker) };
+
+ // SAFETY: This initializes the pointer to the shrinker so that we can use it.
+ unsafe { self.inner.get().write(shrinker) };
+
+ Ok(())
+ }
+}
+
+/// A container that manages a page range in a vma.
+///
+/// The pages can be thought of as an array of booleans of whether the pages are usable. The
+/// methods `use_range` and `stop_using_range` set all booleans in a range to true or false
+/// respectively. Initially, no pages are allocated. When a page is not used, it is not freed
+/// immediately. Instead, it is made available to the memory shrinker to free it if the device is
+/// under memory pressure.
+///
+/// It's okay for `use_range` and `stop_using_range` to race with each other, although there's no
+/// way to know whether an index ends up with true or false if a call to `use_range` races with
+/// another call to `stop_using_range` on a given index.
+///
+/// It's also okay for the two methods to race with themselves, e.g. if two threads call
+/// `use_range` on the same index, then that's fine and neither call will return until the page is
+/// allocated and mapped.
+///
+/// The methods that read or write to a range require that the page is marked as in use. So it is
+/// _not_ okay to call `stop_using_range` on a page that is in use by the methods that read or
+/// write to the page.
+#[pin_data(PinnedDrop)]
+pub(crate) struct ShrinkablePageRange {
+ /// Shrinker object registered with the kernel.
+ shrinker: &'static Shrinker,
+ /// Pid using this page range. Only used as debugging information.
+ pid: Pid,
+ /// The mm for the relevant process.
+ mm: ARef<Mm>,
+ /// Used to synchronize calls to `vm_insert_page` and `zap_page_range_single`.
+ #[pin]
+ mm_lock: Mutex<()>,
+ /// Spinlock protecting changes to pages.
+ #[pin]
+ lock: SpinLock<Inner>,
+
+ /// Must not move, since page info has pointers back.
+ #[pin]
+ _pin: PhantomPinned,
+}
+
+struct Inner {
+ /// Array of pages.
+ ///
+ /// Since this is also accessed by the shrinker, we can't use a `Box`, which asserts exclusive
+ /// ownership. To deal with that, we manage it using raw pointers.
+ pages: *mut PageInfo,
+ /// Length of the `pages` array.
+ size: usize,
+ /// The address of the vma to insert the pages into.
+ vma_addr: usize,
+}
+
+// SAFETY: proper locking is in place for `Inner`
+unsafe impl Send for Inner {}
+
+type StableMmGuard =
+ kernel::sync::lock::Guard<'static, (), kernel::sync::lock::mutex::MutexBackend>;
+
+/// An array element that describes the current state of a page.
+///
+/// There are three states:
+///
+/// * Free. The page is None. The `lru` element is not queued.
+/// * Available. The page is Some. The `lru` element is queued to the shrinker's lru.
+/// * Used. The page is Some. The `lru` element is not queued.
+///
+/// When an element is available, the shrinker is able to free the page.
+#[repr(C)]
+struct PageInfo {
+ lru: bindings::list_head,
+ page: Option<Page>,
+ range: *const ShrinkablePageRange,
+}
+
+impl PageInfo {
+ /// # Safety
+ ///
+ /// The caller ensures that writing to `me.page` is ok, and that the page is not currently set.
+ unsafe fn set_page(me: *mut PageInfo, page: Page) {
+ // SAFETY: This pointer offset is in bounds.
+ let ptr = unsafe { &raw mut (*me).page };
+
+ // SAFETY: The pointer is valid for writing, so also valid for reading.
+ if unsafe { (*ptr).is_some() } {
+ pr_err!("set_page called when there is already a page");
+ // SAFETY: We will initialize the page again below.
+ unsafe { ptr::drop_in_place(ptr) };
+ }
+
+ // SAFETY: The pointer is valid for writing.
+ unsafe { ptr::write(ptr, Some(page)) };
+ }
+
+ /// # Safety
+ ///
+ /// The caller ensures that reading from `me.page` is ok for the duration of 'a.
+ unsafe fn get_page<'a>(me: *const PageInfo) -> Option<&'a Page> {
+ // SAFETY: This pointer offset is in bounds.
+ let ptr = unsafe { &raw const (*me).page };
+
+ // SAFETY: The pointer is valid for reading.
+ unsafe { (*ptr).as_ref() }
+ }
+
+ /// # Safety
+ ///
+ /// The caller ensures that writing to `me.page` is ok for the duration of 'a.
+ unsafe fn take_page(me: *mut PageInfo) -> Option<Page> {
+ // SAFETY: This pointer offset is in bounds.
+ let ptr = unsafe { &raw mut (*me).page };
+
+ // SAFETY: The pointer is valid for reading.
+ unsafe { (*ptr).take() }
+ }
+
+ /// Add this page to the lru list, if not already in the list.
+ ///
+ /// # Safety
+ ///
+ /// The pointer must be valid, and it must be the right shrinker and nid.
+ unsafe fn list_lru_add(me: *mut PageInfo, nid: i32, shrinker: &'static Shrinker) {
+ // SAFETY: This pointer offset is in bounds.
+ let lru_ptr = unsafe { &raw mut (*me).lru };
+ // SAFETY: The lru pointer is valid, and we're not using it with any other lru list.
+ unsafe { bindings::list_lru_add(shrinker.list_lru.get(), lru_ptr, nid, ptr::null_mut()) };
+ }
+
+ /// Remove this page from the lru list, if it is in the list.
+ ///
+ /// # Safety
+ ///
+ /// The pointer must be valid, and it must be the right shrinker and nid.
+ unsafe fn list_lru_del(me: *mut PageInfo, nid: i32, shrinker: &'static Shrinker) {
+ // SAFETY: This pointer offset is in bounds.
+ let lru_ptr = unsafe { &raw mut (*me).lru };
+ // SAFETY: The lru pointer is valid, and we're not using it with any other lru list.
+ unsafe { bindings::list_lru_del(shrinker.list_lru.get(), lru_ptr, nid, ptr::null_mut()) };
+ }
+}
+
+impl ShrinkablePageRange {
+ /// Create a new `ShrinkablePageRange` using the given shrinker.
+ pub(crate) fn new(shrinker: &'static Shrinker) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self {
+ shrinker,
+ pid: kernel::current!().pid(),
+ mm: ARef::from(&**kernel::current!().mm().ok_or(ESRCH)?),
+ mm_lock <- new_mutex!((), "ShrinkablePageRange::mm"),
+ lock <- new_spinlock!(Inner {
+ pages: ptr::null_mut(),
+ size: 0,
+ vma_addr: 0,
+ }, "ShrinkablePageRange"),
+ _pin: PhantomPinned,
+ })
+ }
+
+ pub(crate) fn stable_trylock_mm(&self) -> Option<StableMmGuard> {
+ // SAFETY: This extends the duration of the reference. Since this call happens before
+ // `mm_lock` is taken in the destructor of `ShrinkablePageRange`, the destructor will block
+ // until the returned guard is dropped. This ensures that the guard is valid until dropped.
+ let mm_lock = unsafe { &*ptr::from_ref(&self.mm_lock) };
+
+ mm_lock.try_lock()
+ }
+
+ /// Register a vma with this page range. Returns the size of the region.
+ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result<usize> {
+ let num_bytes = usize::min(vma.end() - vma.start(), bindings::SZ_4M as usize);
+ let num_pages = num_bytes >> PAGE_SHIFT;
+
+ if !ptr::eq::<Mm>(&*self.mm, &**vma.mm()) {
+ pr_debug!("Failed to register with vma: invalid vma->vm_mm");
+ return Err(EINVAL);
+ }
+ if num_pages == 0 {
+ pr_debug!("Failed to register with vma: size zero");
+ return Err(EINVAL);
+ }
+
+ let mut pages = KVVec::<PageInfo>::with_capacity(num_pages, GFP_KERNEL)?;
+
+ // SAFETY: This just initializes the pages array.
+ unsafe {
+ let self_ptr = self as *const ShrinkablePageRange;
+ for i in 0..num_pages {
+ let info = pages.as_mut_ptr().add(i);
+ (&raw mut (*info).range).write(self_ptr);
+ (&raw mut (*info).page).write(None);
+ let lru = &raw mut (*info).lru;
+ (&raw mut (*lru).next).write(lru);
+ (&raw mut (*lru).prev).write(lru);
+ }
+ }
+
+ let mut inner = self.lock.lock();
+ if inner.size > 0 {
+ pr_debug!("Failed to register with vma: already registered");
+ drop(inner);
+ return Err(EBUSY);
+ }
+
+ inner.pages = pages.into_raw_parts().0;
+ inner.size = num_pages;
+ inner.vma_addr = vma.start();
+
+ Ok(num_pages)
+ }
+
+ /// Make sure that the given pages are allocated and mapped.
+ ///
+ /// Must not be called from an atomic context.
+ pub(crate) fn use_range(&self, start: usize, end: usize) -> Result<()> {
+ if start >= end {
+ return Ok(());
+ }
+ let mut inner = self.lock.lock();
+ assert!(end <= inner.size);
+
+ for i in start..end {
+ // SAFETY: This pointer offset is in bounds.
+ let page_info = unsafe { inner.pages.add(i) };
+
+ // SAFETY: The pointer is valid, and we hold the lock so reading from the page is okay.
+ if let Some(page) = unsafe { PageInfo::get_page(page_info) } {
+ // Since we're going to use the page, we should remove it from the lru list so that
+ // the shrinker will not free it.
+ //
+ // SAFETY: The pointer is valid, and this is the right shrinker.
+ //
+ // The shrinker can't free the page between the check and this call to
+ // `list_lru_del` because we hold the lock.
+ unsafe { PageInfo::list_lru_del(page_info, page.nid(), self.shrinker) };
+ } else {
+ // We have to allocate a new page. Use the slow path.
+ drop(inner);
+ // SAFETY: `i < end <= inner.size` so `i` is in bounds.
+ match unsafe { self.use_page_slow(i) } {
+ Ok(()) => {}
+ Err(err) => {
+ pr_warn!("Error in use_page_slow: {:?}", err);
+ return Err(err);
+ }
+ }
+ inner = self.lock.lock();
+ }
+ }
+ Ok(())
+ }
+
+ /// Mark the given page as in use, slow path.
+ ///
+ /// Must not be called from an atomic context.
+ ///
+ /// # Safety
+ ///
+ /// Assumes that `i` is in bounds.
+ #[cold]
+ unsafe fn use_page_slow(&self, i: usize) -> Result<()> {
+ let new_page = Page::alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO)?;
+
+ let mm_mutex = self.mm_lock.lock();
+ let inner = self.lock.lock();
+
+ // SAFETY: This pointer offset is in bounds.
+ let page_info = unsafe { inner.pages.add(i) };
+
+ // SAFETY: The pointer is valid, and we hold the lock so reading from the page is okay.
+ if let Some(page) = unsafe { PageInfo::get_page(page_info) } {
+ // The page was already there, or someone else added the page while we didn't hold the
+ // spinlock.
+ //
+ // SAFETY: The pointer is valid, and this is the right shrinker.
+ //
+ // The shrinker can't free the page between the check and this call to
+ // `list_lru_del` because we hold the lock.
+ unsafe { PageInfo::list_lru_del(page_info, page.nid(), self.shrinker) };
+ return Ok(());
+ }
+
+ let vma_addr = inner.vma_addr;
+ // Release the spinlock while we insert the page into the vma.
+ drop(inner);
+
+ // No overflow since we stay in bounds of the vma.
+ let user_page_addr = vma_addr + (i << PAGE_SHIFT);
+
+ // We use `mmput_async` when dropping the `mm` because `use_page_slow` is usually used from
+ // a remote process. If the call to `mmput` races with the process shutting down, then the
+ // caller of `use_page_slow` becomes responsible for cleaning up the `mm`, which doesn't
+ // happen until it returns to userspace. However, the caller might instead go to sleep and
+ // wait for the owner of the `mm` to wake it up, which doesn't happen because it's in the
+ // middle of a shutdown process that won't complete until the `mm` is dropped. This can
+ // amount to a deadlock.
+ //
+ // Using `mmput_async` avoids this, because then the `mm` cleanup is instead queued to a
+ // workqueue.
+ MmWithUser::into_mmput_async(self.mm.mmget_not_zero().ok_or(ESRCH)?)
+ .mmap_read_lock()
+ .vma_lookup(vma_addr)
+ .ok_or(ESRCH)?
+ .as_mixedmap_vma()
+ .ok_or(ESRCH)?
+ .vm_insert_page(user_page_addr, &new_page)
+ .inspect_err(|err| {
+ pr_warn!(
+ "Failed to vm_insert_page({}): vma_addr:{} i:{} err:{:?}",
+ user_page_addr,
+ vma_addr,
+ i,
+ err
+ )
+ })?;
+
+ let inner = self.lock.lock();
+
+ // SAFETY: The `page_info` pointer is valid and currently does not have a page. The page
+ // can be written to since we hold the lock.
+ //
+ // We released and reacquired the spinlock since we checked that the page is null, but we
+ // always hold the mm_lock mutex when setting the page to a non-null value, so it's not
+ // possible for someone else to have changed it since our check.
+ unsafe { PageInfo::set_page(page_info, new_page) };
+
+ drop(inner);
+ drop(mm_mutex);
+
+ Ok(())
+ }
+
+ /// If the given page is in use, then mark it as available so that the shrinker can free it.
+ ///
+ /// May be called from an atomic context.
+ pub(crate) fn stop_using_range(&self, start: usize, end: usize) {
+ if start >= end {
+ return;
+ }
+ let inner = self.lock.lock();
+ assert!(end <= inner.size);
+
+ for i in (start..end).rev() {
+ // SAFETY: The pointer is in bounds.
+ let page_info = unsafe { inner.pages.add(i) };
+
+ // SAFETY: Okay for reading since we have the lock.
+ if let Some(page) = unsafe { PageInfo::get_page(page_info) } {
+ // SAFETY: The pointer is valid, and it's the right shrinker.
+ unsafe { PageInfo::list_lru_add(page_info, page.nid(), self.shrinker) };
+ }
+ }
+ }
+
+ /// Helper for reading or writing to a range of bytes that may overlap with several pages.
+ ///
+ /// # Safety
+ ///
+ /// All pages touched by this operation must be in use for the duration of this call.
+ unsafe fn iterate<T>(&self, mut offset: usize, mut size: usize, mut cb: T) -> Result
+ where
+ T: FnMut(&Page, usize, usize) -> Result,
+ {
+ if size == 0 {
+ return Ok(());
+ }
+
+ let (pages, num_pages) = {
+ let inner = self.lock.lock();
+ (inner.pages, inner.size)
+ };
+ let num_bytes = num_pages << PAGE_SHIFT;
+
+ // Check that the request is within the buffer.
+ if offset.checked_add(size).ok_or(EFAULT)? > num_bytes {
+ return Err(EFAULT);
+ }
+
+ let mut page_index = offset >> PAGE_SHIFT;
+ offset &= PAGE_SIZE - 1;
+ while size > 0 {
+ let available = usize::min(size, PAGE_SIZE - offset);
+ // SAFETY: The pointer is in bounds.
+ let page_info = unsafe { pages.add(page_index) };
+ // SAFETY: The caller guarantees that this page is in the "in use" state for the
+ // duration of this call to `iterate`, so nobody will change the page.
+ let page = unsafe { PageInfo::get_page(page_info) };
+ if page.is_none() {
+ pr_warn!("Page is null!");
+ }
+ let page = page.ok_or(EFAULT)?;
+ cb(page, offset, available)?;
+ size -= available;
+ page_index += 1;
+ offset = 0;
+ }
+ Ok(())
+ }
+
+ /// Copy from userspace into this page range.
+ ///
+ /// # Safety
+ ///
+ /// All pages touched by this operation must be in use for the duration of this call.
+ pub(crate) unsafe fn copy_from_user_slice(
+ &self,
+ reader: &mut UserSliceReader,
+ offset: usize,
+ size: usize,
+ ) -> Result {
+ // SAFETY: `self.iterate` has the same safety requirements as `copy_from_user_slice`.
+ unsafe {
+ self.iterate(offset, size, |page, offset, to_copy| {
+ page.copy_from_user_slice_raw(reader, offset, to_copy)
+ })
+ }
+ }
+
+ /// Copy from this page range into kernel space.
+ ///
+ /// # Safety
+ ///
+ /// All pages touched by this operation must be in use for the duration of this call.
+ pub(crate) unsafe fn read<T: FromBytes>(&self, offset: usize) -> Result<T> {
+ let mut out = MaybeUninit::<T>::uninit();
+ let mut out_offset = 0;
+ // SAFETY: `self.iterate` has the same safety requirements as `read`.
+ unsafe {
+ self.iterate(offset, size_of::<T>(), |page, offset, to_copy| {
+ // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
+ let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset);
+ // SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid.
+ page.read_raw(obj_ptr, offset, to_copy)?;
+ out_offset += to_copy;
+ Ok(())
+ })?;
+ }
+ // SAFETY: We just initialised the data.
+ Ok(unsafe { out.assume_init() })
+ }
+
+ /// Copy from kernel space into this page range.
+ ///
+ /// # Safety
+ ///
+ /// All pages touched by this operation must be in use for the duration of this call.
+ pub(crate) unsafe fn write<T: ?Sized>(&self, offset: usize, obj: &T) -> Result {
+ let mut obj_offset = 0;
+ // SAFETY: `self.iterate` has the same safety requirements as `write`.
+ unsafe {
+ self.iterate(offset, size_of_val(obj), |page, offset, to_copy| {
+ // SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
+ let obj_ptr = (obj as *const T as *const u8).add(obj_offset);
+ // SAFETY: We have a reference to the object, so the pointer is valid.
+ page.write_raw(obj_ptr, offset, to_copy)?;
+ obj_offset += to_copy;
+ Ok(())
+ })
+ }
+ }
+
+ /// Write zeroes to the given range.
+ ///
+ /// # Safety
+ ///
+ /// All pages touched by this operation must be in use for the duration of this call.
+ pub(crate) unsafe fn fill_zero(&self, offset: usize, size: usize) -> Result {
+ // SAFETY: `self.iterate` has the same safety requirements as `copy_into`.
+ unsafe {
+ self.iterate(offset, size, |page, offset, len| {
+ page.fill_zero_raw(offset, len)
+ })
+ }
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for ShrinkablePageRange {
+ fn drop(self: Pin<&mut Self>) {
+ let (pages, size) = {
+ let lock = self.lock.lock();
+ (lock.pages, lock.size)
+ };
+
+ if size == 0 {
+ return;
+ }
+
+ // Note: This call is also necessary for the safety of `stable_trylock_mm`.
+ let mm_lock = self.mm_lock.lock();
+
+ // This is the destructor, so unlike the other methods, we only need to worry about races
+ // with the shrinker here. Since we hold the `mm_lock`, we also can't race with the
+ // shrinker, and after this loop, the shrinker will not access any of our pages since we
+ // removed them from the lru list.
+ for i in 0..size {
+ // SAFETY: Loop is in-bounds of the size.
+ let p_ptr = unsafe { pages.add(i) };
+ // SAFETY: No other readers, so we can read.
+ if let Some(p) = unsafe { PageInfo::get_page(p_ptr) } {
+ // SAFETY: The pointer is valid and it's the right shrinker.
+ unsafe { PageInfo::list_lru_del(p_ptr, p.nid(), self.shrinker) };
+ }
+ }
+
+ drop(mm_lock);
+
+ // SAFETY: `pages` was allocated as an `KVVec<PageInfo>` with capacity `size`. Furthermore,
+ // all `size` elements are initialized. Also, the array is no longer shared with the
+ // shrinker due to the above loop.
+ drop(unsafe { KVVec::from_raw_parts(pages, size, size) });
+ }
+}
+
+/// # Safety
+/// Called by the shrinker.
+#[no_mangle]
+unsafe extern "C" fn rust_shrink_count(
+ shrink: *mut bindings::shrinker,
+ _sc: *mut bindings::shrink_control,
+) -> c_ulong {
+ // SAFETY: We can access our own private data.
+ let list_lru = unsafe { (*shrink).private_data.cast::<bindings::list_lru>() };
+ // SAFETY: Accessing the lru list is okay. Just an FFI call.
+ unsafe { bindings::list_lru_count(list_lru) }
+}
+
+/// # Safety
+/// Called by the shrinker.
+#[no_mangle]
+unsafe extern "C" fn rust_shrink_scan(
+ shrink: *mut bindings::shrinker,
+ sc: *mut bindings::shrink_control,
+) -> c_ulong {
+ // SAFETY: We can access our own private data.
+ let list_lru = unsafe { (*shrink).private_data.cast::<bindings::list_lru>() };
+ // SAFETY: Caller guarantees that it is safe to read this field.
+ let nr_to_scan = unsafe { (*sc).nr_to_scan };
+ // SAFETY: Accessing the lru list is okay. Just an FFI call.
+ unsafe {
+ bindings::list_lru_walk(
+ list_lru,
+ Some(bindings::rust_shrink_free_page_wrap),
+ ptr::null_mut(),
+ nr_to_scan,
+ )
+ }
+}
+
+const LRU_SKIP: bindings::lru_status = bindings::lru_status_LRU_SKIP;
+const LRU_REMOVED_ENTRY: bindings::lru_status = bindings::lru_status_LRU_REMOVED_RETRY;
+
+/// # Safety
+/// Called by the shrinker.
+#[no_mangle]
+unsafe extern "C" fn rust_shrink_free_page(
+ item: *mut bindings::list_head,
+ lru: *mut bindings::list_lru_one,
+ _cb_arg: *mut c_void,
+) -> bindings::lru_status {
+ // Fields that should survive after unlocking the lru lock.
+ let page;
+ let page_index;
+ let mm;
+ let mmap_read;
+ let mm_mutex;
+ let vma_addr;
+
+ {
+ // CAST: The `list_head` field is first in `PageInfo`.
+ let info = item as *mut PageInfo;
+ // SAFETY: The `range` field of `PageInfo` is immutable.
+ let range = unsafe { &*((*info).range) };
+
+ mm = match range.mm.mmget_not_zero() {
+ Some(mm) => MmWithUser::into_mmput_async(mm),
+ None => return LRU_SKIP,
+ };
+
+ mm_mutex = match range.stable_trylock_mm() {
+ Some(guard) => guard,
+ None => return LRU_SKIP,
+ };
+
+ mmap_read = match mm.mmap_read_trylock() {
+ Some(guard) => guard,
+ None => return LRU_SKIP,
+ };
+
+ // We can't lock it normally here, since we hold the lru lock.
+ let inner = match range.lock.try_lock() {
+ Some(inner) => inner,
+ None => return LRU_SKIP,
+ };
+
+ // SAFETY: The item is in this lru list, so it's okay to remove it.
+ unsafe { bindings::list_lru_isolate(lru, item) };
+
+ // SAFETY: Both pointers are in bounds of the same allocation.
+ page_index = unsafe { info.offset_from(inner.pages) } as usize;
+
+ // SAFETY: We hold the spinlock, so we can take the page.
+ //
+ // This sets the page pointer to zero before we unmap it from the vma. However, we call
+ // `zap_page_range` before we release the mmap lock, so `use_page_slow` will not be able to
+ // insert a new page until after our call to `zap_page_range`.
+ page = unsafe { PageInfo::take_page(info) };
+ vma_addr = inner.vma_addr;
+
+ // From this point on, we don't access this PageInfo or ShrinkablePageRange again, because
+ // they can be freed at any point after we unlock `lru_lock`. This is with the exception of
+ // `mm_mutex` which is kept alive by holding the lock.
+ }
+
+ // SAFETY: The lru lock is locked when this method is called.
+ unsafe { bindings::spin_unlock(&raw mut (*lru).lock) };
+
+ if let Some(vma) = mmap_read.vma_lookup(vma_addr) {
+ let user_page_addr = vma_addr + (page_index << PAGE_SHIFT);
+ vma.zap_page_range_single(user_page_addr, PAGE_SIZE);
+ }
+
+ drop(mmap_read);
+ drop(mm_mutex);
+ drop(mm);
+ drop(page);
+
+ // SAFETY: We just unlocked the lru lock, but it should be locked when we return.
+ unsafe { bindings::spin_lock(&raw mut (*lru).lock) };
+
+ LRU_REMOVED_ENTRY
+}
diff --git a/drivers/android/binder/page_range_helper.c b/drivers/android/binder/page_range_helper.c
new file mode 100644
index 000000000000..496887723ee0
--- /dev/null
+++ b/drivers/android/binder/page_range_helper.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* C helper for page_range.rs to work around a CFI violation.
+ *
+ * Bindgen currently pretends that `enum lru_status` is the same as an integer.
+ * This assumption is fine ABI-wise, but once you add CFI to the mix, it
+ * triggers a CFI violation because `enum lru_status` gets a different CFI tag.
+ *
+ * This file contains a workaround until bindgen can be fixed.
+ *
+ * Copyright (C) 2025 Google LLC.
+ */
+#include "page_range_helper.h"
+
+unsigned int rust_shrink_free_page(struct list_head *item,
+ struct list_lru_one *list,
+ void *cb_arg);
+
+enum lru_status
+rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
+ void *cb_arg)
+{
+ return rust_shrink_free_page(item, list, cb_arg);
+}
diff --git a/drivers/android/binder/page_range_helper.h b/drivers/android/binder/page_range_helper.h
new file mode 100644
index 000000000000..18dd2dd117b2
--- /dev/null
+++ b/drivers/android/binder/page_range_helper.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Google, Inc.
+ */
+
+#ifndef _LINUX_PAGE_RANGE_HELPER_H
+#define _LINUX_PAGE_RANGE_HELPER_H
+
+#include <linux/list_lru.h>
+
+enum lru_status
+rust_shrink_free_page_wrap(struct list_head *item, struct list_lru_one *list,
+ void *cb_arg);
+
+#endif /* _LINUX_PAGE_RANGE_HELPER_H */
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
new file mode 100644
index 000000000000..132055b4790f
--- /dev/null
+++ b/drivers/android/binder/process.rs
@@ -0,0 +1,1745 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! This module defines the `Process` type, which represents a process using a particular binder
+//! context.
+//!
+//! The `Process` object keeps track of all of the resources that this process owns in the binder
+//! context.
+//!
+//! There is one `Process` object for each binder fd that a process has opened, so processes using
+//! several binder contexts have several `Process` objects. This ensures that the contexts are
+//! fully separated.
+
+use core::mem::take;
+
+use kernel::{
+ bindings,
+ cred::Credential,
+ error::Error,
+ fs::file::{self, File},
+ id_pool::IdPool,
+ list::{List, ListArc, ListArcField, ListLinks},
+ mm,
+ prelude::*,
+ rbtree::{self, RBTree, RBTreeNode, RBTreeNodeReservation},
+ seq_file::SeqFile,
+ seq_print,
+ sync::poll::PollTable,
+ sync::{
+ lock::{spinlock::SpinLockBackend, Guard},
+ Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
+ },
+ task::Task,
+ types::ARef,
+ uaccess::{UserSlice, UserSliceReader},
+ uapi,
+ workqueue::{self, Work},
+};
+
+use crate::{
+ allocation::{Allocation, AllocationInfo, NewAllocation},
+ context::Context,
+ defs::*,
+ error::{BinderError, BinderResult},
+ node::{CouldNotDeliverCriticalIncrement, CritIncrWrapper, Node, NodeDeath, NodeRef},
+ page_range::ShrinkablePageRange,
+ range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs},
+ stats::BinderStats,
+ thread::{PushWorkRes, Thread},
+ BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead,
+};
+
+#[path = "freeze.rs"]
+mod freeze;
+use self::freeze::{FreezeCookie, FreezeListener};
+
+struct Mapping {
+ address: usize,
+ alloc: RangeAllocator<AllocationInfo>,
+}
+
+impl Mapping {
+ fn new(address: usize, size: usize) -> Self {
+ Self {
+ address,
+ alloc: RangeAllocator::new(size),
+ }
+ }
+}
+
+// bitflags for defer_work.
+const PROC_DEFER_FLUSH: u8 = 1;
+const PROC_DEFER_RELEASE: u8 = 2;
+
+#[derive(Copy, Clone)]
+pub(crate) enum IsFrozen {
+ Yes,
+ No,
+ InProgress,
+}
+
+impl IsFrozen {
+ /// Whether incoming transactions should be rejected due to freeze.
+ pub(crate) fn is_frozen(self) -> bool {
+ match self {
+ IsFrozen::Yes => true,
+ IsFrozen::No => false,
+ IsFrozen::InProgress => true,
+ }
+ }
+
+ /// Whether freeze notifications consider this process frozen.
+ pub(crate) fn is_fully_frozen(self) -> bool {
+ match self {
+ IsFrozen::Yes => true,
+ IsFrozen::No => false,
+ IsFrozen::InProgress => false,
+ }
+ }
+}
+
+/// The fields of `Process` protected by the spinlock.
+pub(crate) struct ProcessInner {
+ is_manager: bool,
+ pub(crate) is_dead: bool,
+ threads: RBTree<i32, Arc<Thread>>,
+ /// INVARIANT: Threads pushed to this list must be owned by this process.
+ ready_threads: List<Thread>,
+ nodes: RBTree<u64, DArc<Node>>,
+ mapping: Option<Mapping>,
+ work: List<DTRWrap<dyn DeliverToRead>>,
+ delivered_deaths: List<DTRWrap<NodeDeath>, 2>,
+
+ /// The number of requested threads that haven't registered yet.
+ requested_thread_count: u32,
+ /// The maximum number of threads used by the process thread pool.
+ max_threads: u32,
+ /// The number of threads the started and registered with the thread pool.
+ started_thread_count: u32,
+
+ /// Bitmap of deferred work to do.
+ defer_work: u8,
+
+ /// Number of transactions to be transmitted before processes in freeze_wait
+ /// are woken up.
+ outstanding_txns: u32,
+ /// Process is frozen and unable to service binder transactions.
+ pub(crate) is_frozen: IsFrozen,
+ /// Process received sync transactions since last frozen.
+ pub(crate) sync_recv: bool,
+ /// Process received async transactions since last frozen.
+ pub(crate) async_recv: bool,
+ pub(crate) binderfs_file: Option<BinderfsProcFile>,
+ /// Check for oneway spam
+ oneway_spam_detection_enabled: bool,
+}
+
+impl ProcessInner {
+ fn new() -> Self {
+ Self {
+ is_manager: false,
+ is_dead: false,
+ threads: RBTree::new(),
+ ready_threads: List::new(),
+ mapping: None,
+ nodes: RBTree::new(),
+ work: List::new(),
+ delivered_deaths: List::new(),
+ requested_thread_count: 0,
+ max_threads: 0,
+ started_thread_count: 0,
+ defer_work: 0,
+ outstanding_txns: 0,
+ is_frozen: IsFrozen::No,
+ sync_recv: false,
+ async_recv: false,
+ binderfs_file: None,
+ oneway_spam_detection_enabled: false,
+ }
+ }
+
+ /// Schedule the work item for execution on this process.
+ ///
+ /// If any threads are ready for work, then the work item is given directly to that thread and
+ /// it is woken up. Otherwise, it is pushed to the process work list.
+ ///
+ /// This call can fail only if the process is dead. In this case, the work item is returned to
+ /// the caller so that the caller can drop it after releasing the inner process lock. This is
+ /// necessary since the destructor of `Transaction` will take locks that can't necessarily be
+ /// taken while holding the inner process lock.
+ pub(crate) fn push_work(
+ &mut self,
+ work: DLArc<dyn DeliverToRead>,
+ ) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> {
+ // Try to find a ready thread to which to push the work.
+ if let Some(thread) = self.ready_threads.pop_front() {
+ // Push to thread while holding state lock. This prevents the thread from giving up
+ // (for example, because of a signal) when we're about to deliver work.
+ match thread.push_work(work) {
+ PushWorkRes::Ok => Ok(()),
+ PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)),
+ }
+ } else if self.is_dead {
+ Err((BinderError::new_dead(), work))
+ } else {
+ let sync = work.should_sync_wakeup();
+
+ // Didn't find a thread waiting for proc work; this can happen
+ // in two scenarios:
+ // 1. All threads are busy handling transactions
+ // In that case, one of those threads should call back into
+ // the kernel driver soon and pick up this work.
+ // 2. Threads are using the (e)poll interface, in which case
+ // they may be blocked on the waitqueue without having been
+ // added to waiting_threads. For this case, we just iterate
+ // over all threads not handling transaction work, and
+ // wake them all up. We wake all because we don't know whether
+ // a thread that called into (e)poll is handling non-binder
+ // work currently.
+ self.work.push_back(work);
+
+ // Wake up polling threads, if any.
+ for thread in self.threads.values() {
+ thread.notify_if_poll_ready(sync);
+ }
+
+ Ok(())
+ }
+ }
+
+ pub(crate) fn remove_node(&mut self, ptr: u64) {
+ self.nodes.remove(&ptr);
+ }
+
+ /// Updates the reference count on the given node.
+ pub(crate) fn update_node_refcount(
+ &mut self,
+ node: &DArc<Node>,
+ inc: bool,
+ strong: bool,
+ count: usize,
+ othread: Option<&Thread>,
+ ) {
+ let push = node.update_refcount_locked(inc, strong, count, self);
+
+ // If we decided that we need to push work, push either to the process or to a thread if
+ // one is specified.
+ if let Some(node) = push {
+ if let Some(thread) = othread {
+ thread.push_work_deferred(node);
+ } else {
+ let _ = self.push_work(node);
+ // Nothing to do: `push_work` may fail if the process is dead, but that's ok as in
+ // that case, it doesn't care about the notification.
+ }
+ }
+ }
+
+ pub(crate) fn new_node_ref(
+ &mut self,
+ node: DArc<Node>,
+ strong: bool,
+ thread: Option<&Thread>,
+ ) -> NodeRef {
+ self.update_node_refcount(&node, true, strong, 1, thread);
+ let strong_count = if strong { 1 } else { 0 };
+ NodeRef::new(node, strong_count, 1 - strong_count)
+ }
+
+ pub(crate) fn new_node_ref_with_thread(
+ &mut self,
+ node: DArc<Node>,
+ strong: bool,
+ thread: &Thread,
+ wrapper: Option<CritIncrWrapper>,
+ ) -> Result<NodeRef, CouldNotDeliverCriticalIncrement> {
+ let push = match wrapper {
+ None => node
+ .incr_refcount_allow_zero2one(strong, self)?
+ .map(|node| node as _),
+ Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self),
+ };
+ if let Some(node) = push {
+ thread.push_work_deferred(node);
+ }
+ let strong_count = if strong { 1 } else { 0 };
+ Ok(NodeRef::new(node, strong_count, 1 - strong_count))
+ }
+
+ /// Returns an existing node with the given pointer and cookie, if one exists.
+ ///
+ /// Returns an error if a node with the given pointer but a different cookie exists.
+ fn get_existing_node(&self, ptr: u64, cookie: u64) -> Result<Option<DArc<Node>>> {
+ match self.nodes.get(&ptr) {
+ None => Ok(None),
+ Some(node) => {
+ let (_, node_cookie) = node.get_id();
+ if node_cookie == cookie {
+ Ok(Some(node.clone()))
+ } else {
+ Err(EINVAL)
+ }
+ }
+ }
+ }
+
+ fn register_thread(&mut self) -> bool {
+ if self.requested_thread_count == 0 {
+ return false;
+ }
+
+ self.requested_thread_count -= 1;
+ self.started_thread_count += 1;
+ true
+ }
+
+ /// Finds a delivered death notification with the given cookie, removes it from the thread's
+ /// delivered list, and returns it.
+ fn pull_delivered_death(&mut self, cookie: u64) -> Option<DArc<NodeDeath>> {
+ let mut cursor = self.delivered_deaths.cursor_front();
+ while let Some(next) = cursor.peek_next() {
+ if next.cookie == cookie {
+ return Some(next.remove().into_arc());
+ }
+ cursor.move_next();
+ }
+ None
+ }
+
+ pub(crate) fn death_delivered(&mut self, death: DArc<NodeDeath>) {
+ if let Some(death) = ListArc::try_from_arc_or_drop(death) {
+ self.delivered_deaths.push_back(death);
+ } else {
+ pr_warn!("Notification added to `delivered_deaths` twice.");
+ }
+ }
+
+ pub(crate) fn add_outstanding_txn(&mut self) {
+ self.outstanding_txns += 1;
+ }
+
+ fn txns_pending_locked(&self) -> bool {
+ if self.outstanding_txns > 0 {
+ return true;
+ }
+ for thread in self.threads.values() {
+ if thread.has_current_transaction() {
+ return true;
+ }
+ }
+ false
+ }
+}
+
+/// Used to keep track of a node that this process has a handle to.
+#[pin_data]
+pub(crate) struct NodeRefInfo {
+ debug_id: usize,
+ /// The refcount that this process owns to the node.
+ node_ref: ListArcField<NodeRef, { Self::LIST_PROC }>,
+ death: ListArcField<Option<DArc<NodeDeath>>, { Self::LIST_PROC }>,
+ /// Cookie of the active freeze listener for this node.
+ freeze: ListArcField<Option<FreezeCookie>, { Self::LIST_PROC }>,
+ /// Used to store this `NodeRefInfo` in the node's `refs` list.
+ #[pin]
+ links: ListLinks<{ Self::LIST_NODE }>,
+ /// The handle for this `NodeRefInfo`.
+ handle: u32,
+ /// The process that has a handle to the node.
+ pub(crate) process: Arc<Process>,
+}
+
+impl NodeRefInfo {
+ /// The id used for the `Node::refs` list.
+ pub(crate) const LIST_NODE: u64 = 0x2da16350fb724a10;
+ /// The id used for the `ListArc` in `ProcessNodeRefs`.
+ const LIST_PROC: u64 = 0xd703a5263dcc8650;
+
+ fn new(node_ref: NodeRef, handle: u32, process: Arc<Process>) -> impl PinInit<Self> {
+ pin_init!(Self {
+ debug_id: super::next_debug_id(),
+ node_ref: ListArcField::new(node_ref),
+ death: ListArcField::new(None),
+ freeze: ListArcField::new(None),
+ links <- ListLinks::new(),
+ handle,
+ process,
+ })
+ }
+
+ kernel::list::define_list_arc_field_getter! {
+ pub(crate) fn death(&mut self<{Self::LIST_PROC}>) -> &mut Option<DArc<NodeDeath>> { death }
+ pub(crate) fn freeze(&mut self<{Self::LIST_PROC}>) -> &mut Option<FreezeCookie> { freeze }
+ pub(crate) fn node_ref(&mut self<{Self::LIST_PROC}>) -> &mut NodeRef { node_ref }
+ pub(crate) fn node_ref2(&self<{Self::LIST_PROC}>) -> &NodeRef { node_ref }
+ }
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<{Self::LIST_NODE}> for NodeRefInfo { untracked; }
+ impl ListArcSafe<{Self::LIST_PROC}> for NodeRefInfo { untracked; }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<{Self::LIST_NODE}> for NodeRefInfo {
+ using ListLinks { self.links };
+ }
+}
+
+/// Keeps track of references this process has to nodes owned by other processes.
+///
+/// TODO: Currently, the rbtree requires two allocations per node reference, and two tree
+/// traversals to look up a node by `Node::global_id`. Once the rbtree is more powerful, these
+/// extra costs should be eliminated.
+struct ProcessNodeRefs {
+ /// Used to look up nodes using the 32-bit id that this process knows it by.
+ by_handle: RBTree<u32, ListArc<NodeRefInfo, { NodeRefInfo::LIST_PROC }>>,
+ /// Used to quickly find unused ids in `by_handle`.
+ handle_is_present: IdPool,
+ /// Used to look up nodes without knowing their local 32-bit id. The usize is the address of
+ /// the underlying `Node` struct as returned by `Node::global_id`.
+ by_node: RBTree<usize, u32>,
+ /// Used to look up a `FreezeListener` by cookie.
+ ///
+ /// There might be multiple freeze listeners for the same node, but at most one of them is
+ /// active.
+ freeze_listeners: RBTree<FreezeCookie, FreezeListener>,
+}
+
+impl ProcessNodeRefs {
+ fn new() -> Self {
+ Self {
+ by_handle: RBTree::new(),
+ handle_is_present: IdPool::new(),
+ by_node: RBTree::new(),
+ freeze_listeners: RBTree::new(),
+ }
+ }
+}
+
+/// A process using binder.
+///
+/// Strictly speaking, there can be multiple of these per process. There is one for each binder fd
+/// that a process has opened, so processes using several binder contexts have several `Process`
+/// objects. This ensures that the contexts are fully separated.
+#[pin_data]
+pub(crate) struct Process {
+ pub(crate) ctx: Arc<Context>,
+
+ // The task leader (process).
+ pub(crate) task: ARef<Task>,
+
+ // Credential associated with file when `Process` is created.
+ pub(crate) cred: ARef<Credential>,
+
+ #[pin]
+ pub(crate) inner: SpinLock<ProcessInner>,
+
+ #[pin]
+ pub(crate) pages: ShrinkablePageRange,
+
+ // Waitqueue of processes waiting for all outstanding transactions to be
+ // processed.
+ #[pin]
+ freeze_wait: CondVar,
+
+ // Node references are in a different lock to avoid recursive acquisition when
+ // incrementing/decrementing a node in another process.
+ #[pin]
+ node_refs: Mutex<ProcessNodeRefs>,
+
+ // Work node for deferred work item.
+ #[pin]
+ defer_work: Work<Process>,
+
+ // Links for process list in Context.
+ #[pin]
+ links: ListLinks,
+
+ pub(crate) stats: BinderStats,
+}
+
+kernel::impl_has_work! {
+ impl HasWork<Process> for Process { self.defer_work }
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for Process { untracked; }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<0> for Process {
+ using ListLinks { self.links };
+ }
+}
+
+impl workqueue::WorkItem for Process {
+ type Pointer = Arc<Process>;
+
+ fn run(me: Arc<Self>) {
+ let defer;
+ {
+ let mut inner = me.inner.lock();
+ defer = inner.defer_work;
+ inner.defer_work = 0;
+ }
+
+ if defer & PROC_DEFER_FLUSH != 0 {
+ me.deferred_flush();
+ }
+ if defer & PROC_DEFER_RELEASE != 0 {
+ me.deferred_release();
+ }
+ }
+}
+
+impl Process {
+ fn new(ctx: Arc<Context>, cred: ARef<Credential>) -> Result<Arc<Self>> {
+ let current = kernel::current!();
+ let list_process = ListArc::pin_init::<Error>(
+ try_pin_init!(Process {
+ ctx,
+ cred,
+ inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"),
+ pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER),
+ node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"),
+ freeze_wait <- kernel::new_condvar!("Process::freeze_wait"),
+ task: current.group_leader().into(),
+ defer_work <- kernel::new_work!("Process::defer_work"),
+ links <- ListLinks::new(),
+ stats: BinderStats::new(),
+ }),
+ GFP_KERNEL,
+ )?;
+
+ let process = list_process.clone_arc();
+ process.ctx.register_process(list_process);
+
+ Ok(process)
+ }
+
+ pub(crate) fn pid_in_current_ns(&self) -> kernel::task::Pid {
+ self.task.tgid_nr_ns(None)
+ }
+
+ #[inline(never)]
+ pub(crate) fn debug_print_stats(&self, m: &SeqFile, ctx: &Context) -> Result<()> {
+ seq_print!(m, "proc {}\n", self.pid_in_current_ns());
+ seq_print!(m, "context {}\n", &*ctx.name);
+
+ let inner = self.inner.lock();
+ seq_print!(m, " threads: {}\n", inner.threads.iter().count());
+ seq_print!(
+ m,
+ " requested threads: {}+{}/{}\n",
+ inner.requested_thread_count,
+ inner.started_thread_count,
+ inner.max_threads,
+ );
+ if let Some(mapping) = &inner.mapping {
+ seq_print!(
+ m,
+ " free oneway space: {}\n",
+ mapping.alloc.free_oneway_space()
+ );
+ seq_print!(m, " buffers: {}\n", mapping.alloc.count_buffers());
+ }
+ seq_print!(
+ m,
+ " outstanding transactions: {}\n",
+ inner.outstanding_txns
+ );
+ seq_print!(m, " nodes: {}\n", inner.nodes.iter().count());
+ drop(inner);
+
+ {
+ let mut refs = self.node_refs.lock();
+ let (mut count, mut weak, mut strong) = (0, 0, 0);
+ for r in refs.by_handle.values_mut() {
+ let node_ref = r.node_ref();
+ let (nstrong, nweak) = node_ref.get_count();
+ count += 1;
+ weak += nweak;
+ strong += nstrong;
+ }
+ seq_print!(m, " refs: {count} s {strong} w {weak}\n");
+ }
+
+ self.stats.debug_print(" ", m);
+
+ Ok(())
+ }
+
+ #[inline(never)]
+ pub(crate) fn debug_print(&self, m: &SeqFile, ctx: &Context, print_all: bool) -> Result<()> {
+ seq_print!(m, "proc {}\n", self.pid_in_current_ns());
+ seq_print!(m, "context {}\n", &*ctx.name);
+
+ let mut all_threads = KVec::new();
+ let mut all_nodes = KVec::new();
+ loop {
+ let inner = self.inner.lock();
+ let num_threads = inner.threads.iter().count();
+ let num_nodes = inner.nodes.iter().count();
+
+ if all_threads.capacity() < num_threads || all_nodes.capacity() < num_nodes {
+ drop(inner);
+ all_threads.reserve(num_threads, GFP_KERNEL)?;
+ all_nodes.reserve(num_nodes, GFP_KERNEL)?;
+ continue;
+ }
+
+ for thread in inner.threads.values() {
+ assert!(all_threads.len() < all_threads.capacity());
+ let _ = all_threads.push(thread.clone(), GFP_ATOMIC);
+ }
+
+ for node in inner.nodes.values() {
+ assert!(all_nodes.len() < all_nodes.capacity());
+ let _ = all_nodes.push(node.clone(), GFP_ATOMIC);
+ }
+
+ break;
+ }
+
+ for thread in all_threads {
+ thread.debug_print(m, print_all)?;
+ }
+
+ let mut inner = self.inner.lock();
+ for node in all_nodes {
+ if print_all || node.has_oneway_transaction(&mut inner) {
+ node.full_debug_print(m, &mut inner)?;
+ }
+ }
+ drop(inner);
+
+ if print_all {
+ let mut refs = self.node_refs.lock();
+ for r in refs.by_handle.values_mut() {
+ let node_ref = r.node_ref();
+ let dead = node_ref.node.owner.inner.lock().is_dead;
+ let (strong, weak) = node_ref.get_count();
+ let debug_id = node_ref.node.debug_id;
+
+ seq_print!(
+ m,
+ " ref {}: desc {} {}node {debug_id} s {strong} w {weak}",
+ r.debug_id,
+ r.handle,
+ if dead { "dead " } else { "" }
+ );
+ }
+ }
+
+ let inner = self.inner.lock();
+ for work in &inner.work {
+ work.debug_print(m, " ", " pending transaction ")?;
+ }
+ for _death in &inner.delivered_deaths {
+ seq_print!(m, " has delivered dead binder\n");
+ }
+ if let Some(mapping) = &inner.mapping {
+ mapping.alloc.debug_print(m)?;
+ }
+ drop(inner);
+
+ Ok(())
+ }
+
+ /// Attempts to fetch a work item from the process queue.
+ pub(crate) fn get_work(&self) -> Option<DLArc<dyn DeliverToRead>> {
+ self.inner.lock().work.pop_front()
+ }
+
+ /// Attempts to fetch a work item from the process queue. If none is available, it registers the
+ /// given thread as ready to receive work directly.
+ ///
+ /// This must only be called when the thread is not participating in a transaction chain; when
+ /// it is, work will always be delivered directly to the thread (and not through the process
+ /// queue).
+ pub(crate) fn get_work_or_register<'a>(
+ &'a self,
+ thread: &'a Arc<Thread>,
+ ) -> GetWorkOrRegister<'a> {
+ let mut inner = self.inner.lock();
+ // Try to get work from the process queue.
+ if let Some(work) = inner.work.pop_front() {
+ return GetWorkOrRegister::Work(work);
+ }
+
+ // Register the thread as ready.
+ GetWorkOrRegister::Register(Registration::new(thread, &mut inner))
+ }
+
+ fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result<Arc<Thread>> {
+ let id = {
+ let current = kernel::current!();
+ if !core::ptr::eq(current.group_leader(), &*self.task) {
+ pr_err!("get_current_thread was called from the wrong process.");
+ return Err(EINVAL);
+ }
+ current.pid()
+ };
+
+ {
+ let inner = self.inner.lock();
+ if let Some(thread) = inner.threads.get(&id) {
+ return Ok(thread.clone());
+ }
+ }
+
+ // Allocate a new `Thread` without holding any locks.
+ let reservation = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let ta: Arc<Thread> = Thread::new(id, self.into())?;
+
+ let mut inner = self.inner.lock();
+ match inner.threads.entry(id) {
+ rbtree::Entry::Vacant(entry) => {
+ entry.insert(ta.clone(), reservation);
+ Ok(ta)
+ }
+ rbtree::Entry::Occupied(_entry) => {
+ pr_err!("Cannot create two threads with the same id.");
+ Err(EINVAL)
+ }
+ }
+ }
+
+ pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
+ // If push_work fails, drop the work item outside the lock.
+ let res = self.inner.lock().push_work(work);
+ match res {
+ Ok(()) => Ok(()),
+ Err((err, work)) => {
+ drop(work);
+ Err(err)
+ }
+ }
+ }
+
+ fn set_as_manager(
+ self: ArcBorrow<'_, Self>,
+ info: Option<FlatBinderObject>,
+ thread: &Thread,
+ ) -> Result {
+ let (ptr, cookie, flags) = if let Some(obj) = info {
+ (
+ // SAFETY: The object type for this ioctl is implicitly `BINDER_TYPE_BINDER`, so it
+ // is safe to access the `binder` field.
+ unsafe { obj.__bindgen_anon_1.binder },
+ obj.cookie,
+ obj.flags,
+ )
+ } else {
+ (0, 0, 0)
+ };
+ let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?;
+ let node = node_ref.node.clone();
+ self.ctx.set_manager_node(node_ref)?;
+ self.inner.lock().is_manager = true;
+
+ // Force the state of the node to prevent the delivery of acquire/increfs.
+ let mut owner_inner = node.owner.inner.lock();
+ node.force_has_count(&mut owner_inner);
+ Ok(())
+ }
+
+ fn get_node_inner(
+ self: ArcBorrow<'_, Self>,
+ ptr: u64,
+ cookie: u64,
+ flags: u32,
+ strong: bool,
+ thread: &Thread,
+ wrapper: Option<CritIncrWrapper>,
+ ) -> Result<Result<NodeRef, CouldNotDeliverCriticalIncrement>> {
+ // Try to find an existing node.
+ {
+ let mut inner = self.inner.lock();
+ if let Some(node) = inner.get_existing_node(ptr, cookie)? {
+ return Ok(inner.new_node_ref_with_thread(node, strong, thread, wrapper));
+ }
+ }
+
+ // Allocate the node before reacquiring the lock.
+ let node = DTRWrap::arc_pin_init(Node::new(ptr, cookie, flags, self.into()))?.into_arc();
+ let rbnode = RBTreeNode::new(ptr, node.clone(), GFP_KERNEL)?;
+ let mut inner = self.inner.lock();
+ if let Some(node) = inner.get_existing_node(ptr, cookie)? {
+ return Ok(inner.new_node_ref_with_thread(node, strong, thread, wrapper));
+ }
+
+ inner.nodes.insert(rbnode);
+ // This can only fail if someone has already pushed the node to a list, but we just created
+ // it and still hold the lock, so it can't fail right now.
+ let node_ref = inner
+ .new_node_ref_with_thread(node, strong, thread, wrapper)
+ .unwrap();
+
+ Ok(Ok(node_ref))
+ }
+
+ pub(crate) fn get_node(
+ self: ArcBorrow<'_, Self>,
+ ptr: u64,
+ cookie: u64,
+ flags: u32,
+ strong: bool,
+ thread: &Thread,
+ ) -> Result<NodeRef> {
+ let mut wrapper = None;
+ for _ in 0..2 {
+ match self.get_node_inner(ptr, cookie, flags, strong, thread, wrapper) {
+ Err(err) => return Err(err),
+ Ok(Ok(node_ref)) => return Ok(node_ref),
+ Ok(Err(CouldNotDeliverCriticalIncrement)) => {
+ wrapper = Some(CritIncrWrapper::new()?);
+ }
+ }
+ }
+ // We only get a `CouldNotDeliverCriticalIncrement` error if `wrapper` is `None`, so the
+ // loop should run at most twice.
+ unreachable!()
+ }
+
+ pub(crate) fn insert_or_update_handle(
+ self: ArcBorrow<'_, Process>,
+ node_ref: NodeRef,
+ is_manager: bool,
+ ) -> Result<u32> {
+ {
+ let mut refs = self.node_refs.lock();
+
+ // Do a lookup before inserting.
+ if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) {
+ let handle = *handle_ref;
+ let info = refs.by_handle.get_mut(&handle).unwrap();
+ info.node_ref().absorb(node_ref);
+ return Ok(handle);
+ }
+ }
+
+ // Reserve memory for tree nodes.
+ let reserve1 = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let reserve2 = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let info = UniqueArc::new_uninit(GFP_KERNEL)?;
+
+ let mut refs_lock = self.node_refs.lock();
+ let mut refs = &mut *refs_lock;
+
+ let (unused_id, by_handle_slot) = loop {
+ // ID 0 may only be used by the manager.
+ let start = if is_manager { 0 } else { 1 };
+
+ if let Some(res) = refs.handle_is_present.find_unused_id(start) {
+ match refs.by_handle.entry(res.as_u32()) {
+ rbtree::Entry::Vacant(entry) => break (res, entry),
+ rbtree::Entry::Occupied(_) => {
+ pr_err!("Detected mismatch between handle_is_present and by_handle");
+ res.acquire();
+ kernel::warn_on!(true);
+ return Err(EINVAL);
+ }
+ }
+ }
+
+ let grow_request = refs.handle_is_present.grow_request().ok_or(ENOMEM)?;
+ drop(refs_lock);
+ let resizer = grow_request.realloc(GFP_KERNEL)?;
+ refs_lock = self.node_refs.lock();
+ refs = &mut *refs_lock;
+ refs.handle_is_present.grow(resizer);
+ };
+ let handle = unused_id.as_u32();
+
+ // Do a lookup again as node may have been inserted before the lock was reacquired.
+ if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) {
+ let handle = *handle_ref;
+ let info = refs.by_handle.get_mut(&handle).unwrap();
+ info.node_ref().absorb(node_ref);
+ return Ok(handle);
+ }
+
+ let gid = node_ref.node.global_id();
+ let (info_proc, info_node) = {
+ let info_init = NodeRefInfo::new(node_ref, handle, self.into());
+ match info.pin_init_with(info_init) {
+ Ok(info) => ListArc::pair_from_pin_unique(info),
+ // error is infallible
+ Err(err) => match err {},
+ }
+ };
+
+ // Ensure the process is still alive while we insert a new reference.
+ //
+ // This releases the lock before inserting the nodes, but since `is_dead` is set as the
+ // first thing in `deferred_release`, process cleanup will not miss the items inserted into
+ // `refs` below.
+ if self.inner.lock().is_dead {
+ return Err(ESRCH);
+ }
+
+ // SAFETY: `info_proc` and `info_node` reference the same node, so we are inserting
+ // `info_node` into the right node's `refs` list.
+ unsafe { info_proc.node_ref2().node.insert_node_info(info_node) };
+
+ refs.by_node.insert(reserve1.into_node(gid, handle));
+ by_handle_slot.insert(info_proc, reserve2);
+ unused_id.acquire();
+ Ok(handle)
+ }
+
+ pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult<NodeRef> {
+ // When handle is zero, try to get the context manager.
+ if handle == 0 {
+ Ok(self.ctx.get_manager_node(true)?)
+ } else {
+ Ok(self.get_node_from_handle(handle, true)?)
+ }
+ }
+
+ pub(crate) fn get_node_from_handle(&self, handle: u32, strong: bool) -> Result<NodeRef> {
+ self.node_refs
+ .lock()
+ .by_handle
+ .get_mut(&handle)
+ .ok_or(ENOENT)?
+ .node_ref()
+ .clone(strong)
+ }
+
+ pub(crate) fn remove_from_delivered_deaths(&self, death: &DArc<NodeDeath>) {
+ let mut inner = self.inner.lock();
+ // SAFETY: By the invariant on the `delivered_links` field, this is the right linked list.
+ let removed = unsafe { inner.delivered_deaths.remove(death) };
+ drop(inner);
+ drop(removed);
+ }
+
+ pub(crate) fn update_ref(
+ self: ArcBorrow<'_, Process>,
+ handle: u32,
+ inc: bool,
+ strong: bool,
+ ) -> Result {
+ if inc && handle == 0 {
+ if let Ok(node_ref) = self.ctx.get_manager_node(strong) {
+ if core::ptr::eq(&*self, &*node_ref.node.owner) {
+ return Err(EINVAL);
+ }
+ let _ = self.insert_or_update_handle(node_ref, true);
+ return Ok(());
+ }
+ }
+
+ // To preserve original binder behaviour, we only fail requests where the manager tries to
+ // increment references on itself.
+ let mut refs = self.node_refs.lock();
+ if let Some(info) = refs.by_handle.get_mut(&handle) {
+ if info.node_ref().update(inc, strong) {
+ // Clean up death if there is one attached to this node reference.
+ if let Some(death) = info.death().take() {
+ death.set_cleared(true);
+ self.remove_from_delivered_deaths(&death);
+ }
+
+ // Remove reference from process tables, and from the node's `refs` list.
+
+ // SAFETY: We are removing the `NodeRefInfo` from the right node.
+ unsafe { info.node_ref2().node.remove_node_info(info) };
+
+ let id = info.node_ref().node.global_id();
+ refs.by_handle.remove(&handle);
+ refs.by_node.remove(&id);
+ refs.handle_is_present.release_id(handle as usize);
+
+ if let Some(shrink) = refs.handle_is_present.shrink_request() {
+ drop(refs);
+ // This intentionally ignores allocation failures.
+ if let Ok(new_bitmap) = shrink.realloc(GFP_KERNEL) {
+ refs = self.node_refs.lock();
+ refs.handle_is_present.shrink(new_bitmap);
+ }
+ }
+ }
+ } else {
+ // All refs are cleared in process exit, so this warning is expected in that case.
+ if !self.inner.lock().is_dead {
+ pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns());
+ }
+ }
+ Ok(())
+ }
+
+ /// Decrements the refcount of the given node, if one exists.
+ pub(crate) fn update_node(&self, ptr: u64, cookie: u64, strong: bool) {
+ let mut inner = self.inner.lock();
+ if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) {
+ inner.update_node_refcount(&node, false, strong, 1, None);
+ }
+ }
+
+ pub(crate) fn inc_ref_done(&self, reader: &mut UserSliceReader, strong: bool) -> Result {
+ let ptr = reader.read::<u64>()?;
+ let cookie = reader.read::<u64>()?;
+ let mut inner = self.inner.lock();
+ if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) {
+ if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) {
+ // This only fails if the process is dead.
+ let _ = inner.push_work(node);
+ }
+ }
+ Ok(())
+ }
+
+ pub(crate) fn buffer_alloc(
+ self: &Arc<Self>,
+ debug_id: usize,
+ size: usize,
+ is_oneway: bool,
+ from_pid: i32,
+ ) -> BinderResult<NewAllocation> {
+ use kernel::page::PAGE_SIZE;
+
+ let mut reserve_new_args = ReserveNewArgs {
+ debug_id,
+ size,
+ is_oneway,
+ pid: from_pid,
+ ..ReserveNewArgs::default()
+ };
+
+ let (new_alloc, addr) = loop {
+ let mut inner = self.inner.lock();
+ let mapping = inner.mapping.as_mut().ok_or_else(BinderError::new_dead)?;
+ let alloc_request = match mapping.alloc.reserve_new(reserve_new_args)? {
+ ReserveNew::Success(new_alloc) => break (new_alloc, mapping.address),
+ ReserveNew::NeedAlloc(request) => request,
+ };
+ drop(inner);
+ // We need to allocate memory and then call `reserve_new` again.
+ reserve_new_args = alloc_request.make_alloc()?;
+ };
+
+ let res = Allocation::new(
+ self.clone(),
+ debug_id,
+ new_alloc.offset,
+ size,
+ addr + new_alloc.offset,
+ new_alloc.oneway_spam_detected,
+ );
+
+ // This allocation will be marked as in use until the `Allocation` is used to free it.
+ //
+ // This method can't be called while holding a lock, so we release the lock first. It's
+ // okay for several threads to use the method on the same index at the same time. In that
+ // case, one of the calls will allocate the given page (if missing), and the other call
+ // will wait for the other call to finish allocating the page.
+ //
+ // We will not call `stop_using_range` in parallel with this on the same page, because the
+ // allocation can only be removed via the destructor of the `Allocation` object that we
+ // currently own.
+ match self.pages.use_range(
+ new_alloc.offset / PAGE_SIZE,
+ (new_alloc.offset + size).div_ceil(PAGE_SIZE),
+ ) {
+ Ok(()) => {}
+ Err(err) => {
+ pr_warn!("use_range failure {:?}", err);
+ return Err(err.into());
+ }
+ }
+
+ Ok(NewAllocation(res))
+ }
+
+ pub(crate) fn buffer_get(self: &Arc<Self>, ptr: usize) -> Option<Allocation> {
+ let mut inner = self.inner.lock();
+ let mapping = inner.mapping.as_mut()?;
+ let offset = ptr.checked_sub(mapping.address)?;
+ let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?;
+ let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
+ if let Some(data) = odata {
+ alloc.set_info(data);
+ }
+ Some(alloc)
+ }
+
+ pub(crate) fn buffer_raw_free(&self, ptr: usize) {
+ let mut inner = self.inner.lock();
+ if let Some(ref mut mapping) = &mut inner.mapping {
+ let offset = match ptr.checked_sub(mapping.address) {
+ Some(offset) => offset,
+ None => return,
+ };
+
+ let freed_range = match mapping.alloc.reservation_abort(offset) {
+ Ok(freed_range) => freed_range,
+ Err(_) => {
+ pr_warn!(
+ "Pointer {:x} failed to free, base = {:x}\n",
+ ptr,
+ mapping.address
+ );
+ return;
+ }
+ };
+
+ // No more allocations in this range. Mark them as not in use.
+ //
+ // Must be done before we release the lock so that `use_range` is not used on these
+ // indices until `stop_using_range` returns.
+ self.pages
+ .stop_using_range(freed_range.start_page_idx, freed_range.end_page_idx);
+ }
+ }
+
+ pub(crate) fn buffer_make_freeable(&self, offset: usize, mut data: Option<AllocationInfo>) {
+ let mut inner = self.inner.lock();
+ if let Some(ref mut mapping) = &mut inner.mapping {
+ if mapping.alloc.reservation_commit(offset, &mut data).is_err() {
+ pr_warn!("Offset {} failed to be marked freeable\n", offset);
+ }
+ }
+ }
+
+ fn create_mapping(&self, vma: &mm::virt::VmaNew) -> Result {
+ use kernel::page::PAGE_SIZE;
+ let size = usize::min(vma.end() - vma.start(), bindings::SZ_4M as usize);
+ let mapping = Mapping::new(vma.start(), size);
+ let page_count = self.pages.register_with_vma(vma)?;
+ if page_count * PAGE_SIZE != size {
+ return Err(EINVAL);
+ }
+
+ // Save range allocator for later.
+ self.inner.lock().mapping = Some(mapping);
+
+ Ok(())
+ }
+
+ fn version(&self, data: UserSlice) -> Result {
+ data.writer().write(&BinderVersion::current())
+ }
+
+ pub(crate) fn register_thread(&self) -> bool {
+ self.inner.lock().register_thread()
+ }
+
+ fn remove_thread(&self, thread: Arc<Thread>) {
+ self.inner.lock().threads.remove(&thread.id);
+ thread.release();
+ }
+
+ fn set_max_threads(&self, max: u32) {
+ self.inner.lock().max_threads = max;
+ }
+
+ fn set_oneway_spam_detection_enabled(&self, enabled: u32) {
+ self.inner.lock().oneway_spam_detection_enabled = enabled != 0;
+ }
+
+ pub(crate) fn is_oneway_spam_detection_enabled(&self) -> bool {
+ self.inner.lock().oneway_spam_detection_enabled
+ }
+
+ fn get_node_debug_info(&self, data: UserSlice) -> Result {
+ let (mut reader, mut writer) = data.reader_writer();
+
+ // Read the starting point.
+ let ptr = reader.read::<BinderNodeDebugInfo>()?.ptr;
+ let mut out = BinderNodeDebugInfo::default();
+
+ {
+ let inner = self.inner.lock();
+ for (node_ptr, node) in &inner.nodes {
+ if *node_ptr > ptr {
+ node.populate_debug_info(&mut out, &inner);
+ break;
+ }
+ }
+ }
+
+ writer.write(&out)
+ }
+
+ fn get_node_info_from_ref(&self, data: UserSlice) -> Result {
+ let (mut reader, mut writer) = data.reader_writer();
+ let mut out = reader.read::<BinderNodeInfoForRef>()?;
+
+ if out.strong_count != 0
+ || out.weak_count != 0
+ || out.reserved1 != 0
+ || out.reserved2 != 0
+ || out.reserved3 != 0
+ {
+ return Err(EINVAL);
+ }
+
+ // Only the context manager is allowed to use this ioctl.
+ if !self.inner.lock().is_manager {
+ return Err(EPERM);
+ }
+
+ {
+ let mut node_refs = self.node_refs.lock();
+ let node_info = node_refs.by_handle.get_mut(&out.handle).ok_or(ENOENT)?;
+ let node_ref = node_info.node_ref();
+ let owner_inner = node_ref.node.owner.inner.lock();
+ node_ref.node.populate_counts(&mut out, &owner_inner);
+ }
+
+ // Write the result back.
+ writer.write(&out)
+ }
+
+ pub(crate) fn needs_thread(&self) -> bool {
+ let mut inner = self.inner.lock();
+ let ret = inner.requested_thread_count == 0
+ && inner.ready_threads.is_empty()
+ && inner.started_thread_count < inner.max_threads;
+ if ret {
+ inner.requested_thread_count += 1
+ }
+ ret
+ }
+
+ pub(crate) fn request_death(
+ self: &Arc<Self>,
+ reader: &mut UserSliceReader,
+ thread: &Thread,
+ ) -> Result {
+ let handle: u32 = reader.read()?;
+ let cookie: u64 = reader.read()?;
+
+ // Queue BR_ERROR if we can't allocate memory for the death notification.
+ let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| {
+ thread.push_return_work(BR_ERROR);
+ })?;
+ let mut refs = self.node_refs.lock();
+ let Some(info) = refs.by_handle.get_mut(&handle) else {
+ pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n");
+ return Ok(());
+ };
+
+ // Nothing to do if there is already a death notification request for this handle.
+ if info.death().is_some() {
+ pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n");
+ return Ok(());
+ }
+
+ let death = {
+ let death_init = NodeDeath::new(info.node_ref().node.clone(), self.clone(), cookie);
+ match death.pin_init_with(death_init) {
+ Ok(death) => death,
+ // error is infallible
+ Err(err) => match err {},
+ }
+ };
+
+ // Register the death notification.
+ {
+ let owner = info.node_ref2().node.owner.clone();
+ let mut owner_inner = owner.inner.lock();
+ if owner_inner.is_dead {
+ let death = Arc::from(death);
+ *info.death() = Some(death.clone());
+ drop(owner_inner);
+ death.set_dead();
+ } else {
+ let death = ListArc::from(death);
+ *info.death() = Some(death.clone_arc());
+ info.node_ref().node.add_death(death, &mut owner_inner);
+ }
+ }
+ Ok(())
+ }
+
+ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread) -> Result {
+ let handle: u32 = reader.read()?;
+ let cookie: u64 = reader.read()?;
+
+ let mut refs = self.node_refs.lock();
+ let Some(info) = refs.by_handle.get_mut(&handle) else {
+ pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n");
+ return Ok(());
+ };
+
+ let Some(death) = info.death().take() else {
+ pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n");
+ return Ok(());
+ };
+ if death.cookie != cookie {
+ *info.death() = Some(death);
+ pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n");
+ return Ok(());
+ }
+
+ // Update state and determine if we need to queue a work item. We only need to do it when
+ // the node is not dead or if the user already completed the death notification.
+ if death.set_cleared(false) {
+ if let Some(death) = ListArc::try_from_arc_or_drop(death) {
+ let _ = thread.push_work_if_looper(death);
+ }
+ }
+
+ Ok(())
+ }
+
+ pub(crate) fn dead_binder_done(&self, cookie: u64, thread: &Thread) {
+ if let Some(death) = self.inner.lock().pull_delivered_death(cookie) {
+ death.set_notification_done(thread);
+ }
+ }
+
+ /// Locks the spinlock and move the `nodes` rbtree out.
+ ///
+ /// This allows you to iterate through `nodes` while also allowing you to give other parts of
+ /// the codebase exclusive access to `ProcessInner`.
+ pub(crate) fn lock_with_nodes(&self) -> WithNodes<'_> {
+ let mut inner = self.inner.lock();
+ WithNodes {
+ nodes: take(&mut inner.nodes),
+ inner,
+ }
+ }
+
+ fn deferred_flush(&self) {
+ let inner = self.inner.lock();
+ for thread in inner.threads.values() {
+ thread.exit_looper();
+ }
+ }
+
+ fn deferred_release(self: Arc<Self>) {
+ let is_manager = {
+ let mut inner = self.inner.lock();
+ inner.is_dead = true;
+ inner.is_frozen = IsFrozen::No;
+ inner.sync_recv = false;
+ inner.async_recv = false;
+ inner.is_manager
+ };
+
+ if is_manager {
+ self.ctx.unset_manager_node();
+ }
+
+ self.ctx.deregister_process(&self);
+
+ let binderfs_file = self.inner.lock().binderfs_file.take();
+ drop(binderfs_file);
+
+ // Release threads.
+ let threads = {
+ let mut inner = self.inner.lock();
+ let threads = take(&mut inner.threads);
+ let ready = take(&mut inner.ready_threads);
+ drop(inner);
+ drop(ready);
+
+ for thread in threads.values() {
+ thread.release();
+ }
+ threads
+ };
+
+ // Release nodes.
+ {
+ while let Some(node) = {
+ let mut lock = self.inner.lock();
+ lock.nodes.cursor_front_mut().map(|c| c.remove_current().1)
+ } {
+ node.to_key_value().1.release();
+ }
+ }
+
+ // Clean up death listeners and remove nodes from external node info lists.
+ for info in self.node_refs.lock().by_handle.values_mut() {
+ // SAFETY: We are removing the `NodeRefInfo` from the right node.
+ unsafe { info.node_ref2().node.remove_node_info(info) };
+
+ // Remove all death notifications from the nodes (that belong to a different process).
+ let death = if let Some(existing) = info.death().take() {
+ existing
+ } else {
+ continue;
+ };
+ death.set_cleared(false);
+ }
+
+ // Clean up freeze listeners.
+ let freeze_listeners = take(&mut self.node_refs.lock().freeze_listeners);
+ for listener in freeze_listeners.values() {
+ listener.on_process_exit(&self);
+ }
+ drop(freeze_listeners);
+
+ // Release refs on foreign nodes.
+ {
+ let mut refs = self.node_refs.lock();
+ let by_handle = take(&mut refs.by_handle);
+ let by_node = take(&mut refs.by_node);
+ drop(refs);
+ drop(by_node);
+ drop(by_handle);
+ }
+
+ // Cancel all pending work items.
+ while let Some(work) = self.get_work() {
+ work.into_arc().cancel();
+ }
+
+ // Clear delivered_deaths list.
+ //
+ // Scope ensures that MutexGuard is dropped while executing the body.
+ while let Some(delivered_death) = { self.inner.lock().delivered_deaths.pop_front() } {
+ drop(delivered_death);
+ }
+
+ // Free any resources kept alive by allocated buffers.
+ let omapping = self.inner.lock().mapping.take();
+ if let Some(mut mapping) = omapping {
+ let address = mapping.address;
+ mapping
+ .alloc
+ .take_for_each(|offset, size, debug_id, odata| {
+ let ptr = offset + address;
+ let mut alloc =
+ Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
+ if let Some(data) = odata {
+ alloc.set_info(data);
+ }
+ drop(alloc)
+ });
+ }
+
+ // calls to synchronize_rcu() in thread drop will happen here
+ drop(threads);
+ }
+
+ pub(crate) fn drop_outstanding_txn(&self) {
+ let wake = {
+ let mut inner = self.inner.lock();
+ if inner.outstanding_txns == 0 {
+ pr_err!("outstanding_txns underflow");
+ return;
+ }
+ inner.outstanding_txns -= 1;
+ inner.is_frozen.is_frozen() && inner.outstanding_txns == 0
+ };
+
+ if wake {
+ self.freeze_wait.notify_all();
+ }
+ }
+
+ pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result {
+ if info.enable == 0 {
+ let msgs = self.prepare_freeze_messages()?;
+ let mut inner = self.inner.lock();
+ inner.sync_recv = false;
+ inner.async_recv = false;
+ inner.is_frozen = IsFrozen::No;
+ drop(inner);
+ msgs.send_messages();
+ return Ok(());
+ }
+
+ let mut inner = self.inner.lock();
+ inner.sync_recv = false;
+ inner.async_recv = false;
+ inner.is_frozen = IsFrozen::InProgress;
+
+ if info.timeout_ms > 0 {
+ let mut jiffies = kernel::time::msecs_to_jiffies(info.timeout_ms);
+ while jiffies > 0 {
+ if inner.outstanding_txns == 0 {
+ break;
+ }
+
+ match self
+ .freeze_wait
+ .wait_interruptible_timeout(&mut inner, jiffies)
+ {
+ CondVarTimeoutResult::Signal { .. } => {
+ inner.is_frozen = IsFrozen::No;
+ return Err(ERESTARTSYS);
+ }
+ CondVarTimeoutResult::Woken { jiffies: remaining } => {
+ jiffies = remaining;
+ }
+ CondVarTimeoutResult::Timeout => {
+ jiffies = 0;
+ }
+ }
+ }
+ }
+
+ if inner.txns_pending_locked() {
+ inner.is_frozen = IsFrozen::No;
+ Err(EAGAIN)
+ } else {
+ drop(inner);
+ match self.prepare_freeze_messages() {
+ Ok(batch) => {
+ self.inner.lock().is_frozen = IsFrozen::Yes;
+ batch.send_messages();
+ Ok(())
+ }
+ Err(kernel::alloc::AllocError) => {
+ self.inner.lock().is_frozen = IsFrozen::No;
+ Err(ENOMEM)
+ }
+ }
+ }
+ }
+}
+
+fn get_frozen_status(data: UserSlice) -> Result {
+ let (mut reader, mut writer) = data.reader_writer();
+
+ let mut info = reader.read::<BinderFrozenStatusInfo>()?;
+ info.sync_recv = 0;
+ info.async_recv = 0;
+ let mut found = false;
+
+ for ctx in crate::context::get_all_contexts()? {
+ ctx.for_each_proc(|proc| {
+ if proc.task.pid() == info.pid as _ {
+ found = true;
+ let inner = proc.inner.lock();
+ let txns_pending = inner.txns_pending_locked();
+ info.async_recv |= inner.async_recv as u32;
+ info.sync_recv |= inner.sync_recv as u32;
+ info.sync_recv |= (txns_pending as u32) << 1;
+ }
+ });
+ }
+
+ if found {
+ writer.write(&info)?;
+ Ok(())
+ } else {
+ Err(EINVAL)
+ }
+}
+
+fn ioctl_freeze(reader: &mut UserSliceReader) -> Result {
+ let info = reader.read::<BinderFreezeInfo>()?;
+
+ // Very unlikely for there to be more than 3, since a process normally uses at most binder and
+ // hwbinder.
+ let mut procs = KVec::with_capacity(3, GFP_KERNEL)?;
+
+ let ctxs = crate::context::get_all_contexts()?;
+ for ctx in ctxs {
+ for proc in ctx.get_procs_with_pid(info.pid as i32)? {
+ procs.push(proc, GFP_KERNEL)?;
+ }
+ }
+
+ for proc in procs {
+ proc.ioctl_freeze(&info)?;
+ }
+ Ok(())
+}
+
+/// The ioctl handler.
+impl Process {
+ /// Ioctls that are write-only from the perspective of userspace.
+ ///
+ /// The kernel will only read from the pointer that userspace provided to us.
+ fn ioctl_write_only(
+ this: ArcBorrow<'_, Process>,
+ _file: &File,
+ cmd: u32,
+ reader: &mut UserSliceReader,
+ ) -> Result {
+ let thread = this.get_current_thread()?;
+ match cmd {
+ uapi::BINDER_SET_MAX_THREADS => this.set_max_threads(reader.read()?),
+ uapi::BINDER_THREAD_EXIT => this.remove_thread(thread),
+ uapi::BINDER_SET_CONTEXT_MGR => this.set_as_manager(None, &thread)?,
+ uapi::BINDER_SET_CONTEXT_MGR_EXT => {
+ this.set_as_manager(Some(reader.read()?), &thread)?
+ }
+ uapi::BINDER_ENABLE_ONEWAY_SPAM_DETECTION => {
+ this.set_oneway_spam_detection_enabled(reader.read()?)
+ }
+ uapi::BINDER_FREEZE => ioctl_freeze(reader)?,
+ _ => return Err(EINVAL),
+ }
+ Ok(())
+ }
+
+ /// Ioctls that are read/write from the perspective of userspace.
+ ///
+ /// The kernel will both read from and write to the pointer that userspace provided to us.
+ fn ioctl_write_read(
+ this: ArcBorrow<'_, Process>,
+ file: &File,
+ cmd: u32,
+ data: UserSlice,
+ ) -> Result {
+ let thread = this.get_current_thread()?;
+ let blocking = (file.flags() & file::flags::O_NONBLOCK) == 0;
+ match cmd {
+ uapi::BINDER_WRITE_READ => thread.write_read(data, blocking)?,
+ uapi::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?,
+ uapi::BINDER_GET_NODE_INFO_FOR_REF => this.get_node_info_from_ref(data)?,
+ uapi::BINDER_VERSION => this.version(data)?,
+ uapi::BINDER_GET_FROZEN_INFO => get_frozen_status(data)?,
+ uapi::BINDER_GET_EXTENDED_ERROR => thread.get_extended_error(data)?,
+ _ => return Err(EINVAL),
+ }
+ Ok(())
+ }
+}
+
+/// The file operations supported by `Process`.
+impl Process {
+ pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result<Arc<Process>> {
+ Self::new(ctx.into(), ARef::from(file.cred()))
+ }
+
+ pub(crate) fn release(this: Arc<Process>, _file: &File) {
+ let binderfs_file;
+ let should_schedule;
+ {
+ let mut inner = this.inner.lock();
+ should_schedule = inner.defer_work == 0;
+ inner.defer_work |= PROC_DEFER_RELEASE;
+ binderfs_file = inner.binderfs_file.take();
+ }
+
+ if should_schedule {
+ // Ignore failures to schedule to the workqueue. Those just mean that we're already
+ // scheduled for execution.
+ let _ = workqueue::system().enqueue(this);
+ }
+
+ drop(binderfs_file);
+ }
+
+ pub(crate) fn flush(this: ArcBorrow<'_, Process>) -> Result {
+ let should_schedule;
+ {
+ let mut inner = this.inner.lock();
+ should_schedule = inner.defer_work == 0;
+ inner.defer_work |= PROC_DEFER_FLUSH;
+ }
+
+ if should_schedule {
+ // Ignore failures to schedule to the workqueue. Those just mean that we're already
+ // scheduled for execution.
+ let _ = workqueue::system().enqueue(Arc::from(this));
+ }
+ Ok(())
+ }
+
+ pub(crate) fn ioctl(this: ArcBorrow<'_, Process>, file: &File, cmd: u32, arg: usize) -> Result {
+ use kernel::ioctl::{_IOC_DIR, _IOC_SIZE};
+ use kernel::uapi::{_IOC_READ, _IOC_WRITE};
+
+ crate::trace::trace_ioctl(cmd, arg);
+
+ let user_slice = UserSlice::new(UserPtr::from_addr(arg), _IOC_SIZE(cmd));
+
+ const _IOC_READ_WRITE: u32 = _IOC_READ | _IOC_WRITE;
+
+ match _IOC_DIR(cmd) {
+ _IOC_WRITE => Self::ioctl_write_only(this, file, cmd, &mut user_slice.reader()),
+ _IOC_READ_WRITE => Self::ioctl_write_read(this, file, cmd, user_slice),
+ _ => Err(EINVAL),
+ }
+ }
+
+ pub(crate) fn mmap(
+ this: ArcBorrow<'_, Process>,
+ _file: &File,
+ vma: &mm::virt::VmaNew,
+ ) -> Result {
+ // We don't allow mmap to be used in a different process.
+ if !core::ptr::eq(kernel::current!().group_leader(), &*this.task) {
+ return Err(EINVAL);
+ }
+ if vma.start() == 0 {
+ return Err(EINVAL);
+ }
+
+ vma.try_clear_maywrite().map_err(|_| EPERM)?;
+ vma.set_dontcopy();
+ vma.set_mixedmap();
+
+ // TODO: Set ops. We need to learn when the user unmaps so that we can stop using it.
+ this.create_mapping(vma)
+ }
+
+ pub(crate) fn poll(
+ this: ArcBorrow<'_, Process>,
+ file: &File,
+ table: PollTable<'_>,
+ ) -> Result<u32> {
+ let thread = this.get_current_thread()?;
+ let (from_proc, mut mask) = thread.poll(file, table);
+ if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
+ mask |= bindings::POLLIN;
+ }
+ Ok(mask)
+ }
+}
+
+/// Represents that a thread has registered with the `ready_threads` list of its process.
+///
+/// The destructor of this type will unregister the thread from the list of ready threads.
+pub(crate) struct Registration<'a> {
+ thread: &'a Arc<Thread>,
+}
+
+impl<'a> Registration<'a> {
+ fn new(thread: &'a Arc<Thread>, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>) -> Self {
+ assert!(core::ptr::eq(&thread.process.inner, guard.lock_ref()));
+ // INVARIANT: We are pushing this thread to the right `ready_threads` list.
+ if let Ok(list_arc) = ListArc::try_from_arc(thread.clone()) {
+ guard.ready_threads.push_front(list_arc);
+ } else {
+ // It is an error to hit this branch, and it should not be reachable. We try to do
+ // something reasonable when the failure path happens. Most likely, the thread in
+ // question will sleep forever.
+ pr_err!("Same thread registered with `ready_threads` twice.");
+ }
+ Self { thread }
+ }
+}
+
+impl Drop for Registration<'_> {
+ fn drop(&mut self) {
+ let mut inner = self.thread.process.inner.lock();
+ // SAFETY: The thread has the invariant that we never push it to any other linked list than
+ // the `ready_threads` list of its parent process. Therefore, the thread is either in that
+ // list, or in no list.
+ unsafe { inner.ready_threads.remove(self.thread) };
+ }
+}
+
+pub(crate) struct WithNodes<'a> {
+ pub(crate) inner: Guard<'a, ProcessInner, SpinLockBackend>,
+ pub(crate) nodes: RBTree<u64, DArc<Node>>,
+}
+
+impl Drop for WithNodes<'_> {
+ fn drop(&mut self) {
+ core::mem::swap(&mut self.nodes, &mut self.inner.nodes);
+ if self.nodes.iter().next().is_some() {
+ pr_err!("nodes array was modified while using lock_with_nodes\n");
+ }
+ }
+}
+
+pub(crate) enum GetWorkOrRegister<'a> {
+ Work(DLArc<dyn DeliverToRead>),
+ Register(Registration<'a>),
+}
diff --git a/drivers/android/binder/range_alloc/array.rs b/drivers/android/binder/range_alloc/array.rs
new file mode 100644
index 000000000000..07e1dec2ce63
--- /dev/null
+++ b/drivers/android/binder/range_alloc/array.rs
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{
+ page::{PAGE_MASK, PAGE_SIZE},
+ prelude::*,
+ seq_file::SeqFile,
+ seq_print,
+ task::Pid,
+};
+
+use crate::range_alloc::{DescriptorState, FreedRange, Range};
+
+/// Keeps track of allocations in a process' mmap.
+///
+/// Each process has an mmap where the data for incoming transactions will be placed. This struct
+/// keeps track of allocations made in the mmap. For each allocation, we store a descriptor that
+/// has metadata related to the allocation. We also keep track of available free space.
+pub(super) struct ArrayRangeAllocator<T> {
+ /// This stores all ranges that are allocated. Unlike the tree based allocator, we do *not*
+ /// store the free ranges.
+ ///
+ /// Sorted by offset.
+ pub(super) ranges: KVec<Range<T>>,
+ size: usize,
+ free_oneway_space: usize,
+}
+
+struct FindEmptyRes {
+ /// Which index in `ranges` should we insert the new range at?
+ ///
+ /// Inserting the new range at this index keeps `ranges` sorted.
+ insert_at_idx: usize,
+ /// Which offset should we insert the new range at?
+ insert_at_offset: usize,
+}
+
+impl<T> ArrayRangeAllocator<T> {
+ pub(crate) fn new(size: usize, alloc: EmptyArrayAlloc<T>) -> Self {
+ Self {
+ ranges: alloc.ranges,
+ size,
+ free_oneway_space: size / 2,
+ }
+ }
+
+ pub(crate) fn free_oneway_space(&self) -> usize {
+ self.free_oneway_space
+ }
+
+ pub(crate) fn count_buffers(&self) -> usize {
+ self.ranges.len()
+ }
+
+ pub(crate) fn total_size(&self) -> usize {
+ self.size
+ }
+
+ pub(crate) fn is_full(&self) -> bool {
+ self.ranges.len() == self.ranges.capacity()
+ }
+
+ pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> {
+ for range in &self.ranges {
+ seq_print!(
+ m,
+ " buffer {}: {} size {} pid {} oneway {}",
+ 0,
+ range.offset,
+ range.size,
+ range.state.pid(),
+ range.state.is_oneway(),
+ );
+ if let DescriptorState::Reserved(_) = range.state {
+ seq_print!(m, " reserved\n");
+ } else {
+ seq_print!(m, " allocated\n");
+ }
+ }
+ Ok(())
+ }
+
+ /// Find somewhere to put a new range.
+ ///
+ /// Unlike the tree implementation, we do not bother to find the smallest gap. The idea is that
+ /// fragmentation isn't a big issue when we don't have many ranges.
+ ///
+ /// Returns the index that the new range should have in `self.ranges` after insertion.
+ fn find_empty_range(&self, size: usize) -> Option<FindEmptyRes> {
+ let after_last_range = self.ranges.last().map(Range::endpoint).unwrap_or(0);
+
+ if size <= self.total_size() - after_last_range {
+ // We can put the range at the end, so just do that.
+ Some(FindEmptyRes {
+ insert_at_idx: self.ranges.len(),
+ insert_at_offset: after_last_range,
+ })
+ } else {
+ let mut end_of_prev = 0;
+ for (i, range) in self.ranges.iter().enumerate() {
+ // Does it fit before the i'th range?
+ if size <= range.offset - end_of_prev {
+ return Some(FindEmptyRes {
+ insert_at_idx: i,
+ insert_at_offset: end_of_prev,
+ });
+ }
+ end_of_prev = range.endpoint();
+ }
+ None
+ }
+ }
+
+ pub(crate) fn reserve_new(
+ &mut self,
+ debug_id: usize,
+ size: usize,
+ is_oneway: bool,
+ pid: Pid,
+ ) -> Result<usize> {
+ // Compute new value of free_oneway_space, which is set only on success.
+ let new_oneway_space = if is_oneway {
+ match self.free_oneway_space.checked_sub(size) {
+ Some(new_oneway_space) => new_oneway_space,
+ None => return Err(ENOSPC),
+ }
+ } else {
+ self.free_oneway_space
+ };
+
+ let FindEmptyRes {
+ insert_at_idx,
+ insert_at_offset,
+ } = self.find_empty_range(size).ok_or(ENOSPC)?;
+ self.free_oneway_space = new_oneway_space;
+
+ let new_range = Range {
+ offset: insert_at_offset,
+ size,
+ state: DescriptorState::new(is_oneway, debug_id, pid),
+ };
+ // Insert the value at the given index to keep the array sorted.
+ self.ranges
+ .insert_within_capacity(insert_at_idx, new_range)
+ .ok()
+ .unwrap();
+
+ Ok(insert_at_offset)
+ }
+
+ pub(crate) fn reservation_abort(&mut self, offset: usize) -> Result<FreedRange> {
+ // This could use a binary search, but linear scans are usually faster for small arrays.
+ let i = self
+ .ranges
+ .iter()
+ .position(|range| range.offset == offset)
+ .ok_or(EINVAL)?;
+ let range = &self.ranges[i];
+
+ if let DescriptorState::Allocated(_) = range.state {
+ return Err(EPERM);
+ }
+
+ let size = range.size;
+ let offset = range.offset;
+
+ if range.state.is_oneway() {
+ self.free_oneway_space += size;
+ }
+
+ // This computes the range of pages that are no longer used by *any* allocated range. The
+ // caller will mark them as unused, which means that they can be freed if the system comes
+ // under memory pressure.
+ let mut freed_range = FreedRange::interior_pages(offset, size);
+ #[expect(clippy::collapsible_if)] // reads better like this
+ if offset % PAGE_SIZE != 0 {
+ if i == 0 || self.ranges[i - 1].endpoint() <= (offset & PAGE_MASK) {
+ freed_range.start_page_idx -= 1;
+ }
+ }
+ if range.endpoint() % PAGE_SIZE != 0 {
+ let page_after = (range.endpoint() & PAGE_MASK) + PAGE_SIZE;
+ if i + 1 == self.ranges.len() || page_after <= self.ranges[i + 1].offset {
+ freed_range.end_page_idx += 1;
+ }
+ }
+
+ self.ranges.remove(i)?;
+ Ok(freed_range)
+ }
+
+ pub(crate) fn reservation_commit(&mut self, offset: usize, data: &mut Option<T>) -> Result {
+ // This could use a binary search, but linear scans are usually faster for small arrays.
+ let range = self
+ .ranges
+ .iter_mut()
+ .find(|range| range.offset == offset)
+ .ok_or(ENOENT)?;
+
+ let DescriptorState::Reserved(reservation) = &range.state else {
+ return Err(ENOENT);
+ };
+
+ range.state = DescriptorState::Allocated(reservation.clone().allocate(data.take()));
+ Ok(())
+ }
+
+ pub(crate) fn reserve_existing(&mut self, offset: usize) -> Result<(usize, usize, Option<T>)> {
+ // This could use a binary search, but linear scans are usually faster for small arrays.
+ let range = self
+ .ranges
+ .iter_mut()
+ .find(|range| range.offset == offset)
+ .ok_or(ENOENT)?;
+
+ let DescriptorState::Allocated(allocation) = &mut range.state else {
+ return Err(ENOENT);
+ };
+
+ let data = allocation.take();
+ let debug_id = allocation.reservation.debug_id;
+ range.state = DescriptorState::Reserved(allocation.reservation.clone());
+ Ok((range.size, debug_id, data))
+ }
+
+ pub(crate) fn take_for_each<F: Fn(usize, usize, usize, Option<T>)>(&mut self, callback: F) {
+ for range in self.ranges.iter_mut() {
+ if let DescriptorState::Allocated(allocation) = &mut range.state {
+ callback(
+ range.offset,
+ range.size,
+ allocation.reservation.debug_id,
+ allocation.data.take(),
+ );
+ }
+ }
+ }
+}
+
+pub(crate) struct EmptyArrayAlloc<T> {
+ ranges: KVec<Range<T>>,
+}
+
+impl<T> EmptyArrayAlloc<T> {
+ pub(crate) fn try_new(capacity: usize) -> Result<Self> {
+ Ok(Self {
+ ranges: KVec::with_capacity(capacity, GFP_KERNEL)?,
+ })
+ }
+}
diff --git a/drivers/android/binder/range_alloc/mod.rs b/drivers/android/binder/range_alloc/mod.rs
new file mode 100644
index 000000000000..2301e2bc1a1f
--- /dev/null
+++ b/drivers/android/binder/range_alloc/mod.rs
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{page::PAGE_SIZE, prelude::*, seq_file::SeqFile, task::Pid};
+
+mod tree;
+use self::tree::{FromArrayAllocs, ReserveNewTreeAlloc, TreeRangeAllocator};
+
+mod array;
+use self::array::{ArrayRangeAllocator, EmptyArrayAlloc};
+
+enum DescriptorState<T> {
+ Reserved(Reservation),
+ Allocated(Allocation<T>),
+}
+
+impl<T> DescriptorState<T> {
+ fn new(is_oneway: bool, debug_id: usize, pid: Pid) -> Self {
+ DescriptorState::Reserved(Reservation {
+ debug_id,
+ is_oneway,
+ pid,
+ })
+ }
+
+ fn pid(&self) -> Pid {
+ match self {
+ DescriptorState::Reserved(inner) => inner.pid,
+ DescriptorState::Allocated(inner) => inner.reservation.pid,
+ }
+ }
+
+ fn is_oneway(&self) -> bool {
+ match self {
+ DescriptorState::Reserved(inner) => inner.is_oneway,
+ DescriptorState::Allocated(inner) => inner.reservation.is_oneway,
+ }
+ }
+}
+
+#[derive(Clone)]
+struct Reservation {
+ debug_id: usize,
+ is_oneway: bool,
+ pid: Pid,
+}
+
+impl Reservation {
+ fn allocate<T>(self, data: Option<T>) -> Allocation<T> {
+ Allocation {
+ data,
+ reservation: self,
+ }
+ }
+}
+
+struct Allocation<T> {
+ reservation: Reservation,
+ data: Option<T>,
+}
+
+impl<T> Allocation<T> {
+ fn deallocate(self) -> (Reservation, Option<T>) {
+ (self.reservation, self.data)
+ }
+
+ fn debug_id(&self) -> usize {
+ self.reservation.debug_id
+ }
+
+ fn take(&mut self) -> Option<T> {
+ self.data.take()
+ }
+}
+
+/// The array implementation must switch to the tree if it wants to go beyond this number of
+/// ranges.
+const TREE_THRESHOLD: usize = 8;
+
+/// Represents a range of pages that have just become completely free.
+#[derive(Copy, Clone)]
+pub(crate) struct FreedRange {
+ pub(crate) start_page_idx: usize,
+ pub(crate) end_page_idx: usize,
+}
+
+impl FreedRange {
+ fn interior_pages(offset: usize, size: usize) -> FreedRange {
+ FreedRange {
+ // Divide round up
+ start_page_idx: offset.div_ceil(PAGE_SIZE),
+ // Divide round down
+ end_page_idx: (offset + size) / PAGE_SIZE,
+ }
+ }
+}
+
+struct Range<T> {
+ offset: usize,
+ size: usize,
+ state: DescriptorState<T>,
+}
+
+impl<T> Range<T> {
+ fn endpoint(&self) -> usize {
+ self.offset + self.size
+ }
+}
+
+pub(crate) struct RangeAllocator<T> {
+ inner: Impl<T>,
+}
+
+enum Impl<T> {
+ Empty(usize),
+ Array(ArrayRangeAllocator<T>),
+ Tree(TreeRangeAllocator<T>),
+}
+
+impl<T> RangeAllocator<T> {
+ pub(crate) fn new(size: usize) -> Self {
+ Self {
+ inner: Impl::Empty(size),
+ }
+ }
+
+ pub(crate) fn free_oneway_space(&self) -> usize {
+ match &self.inner {
+ Impl::Empty(size) => size / 2,
+ Impl::Array(array) => array.free_oneway_space(),
+ Impl::Tree(tree) => tree.free_oneway_space(),
+ }
+ }
+
+ pub(crate) fn count_buffers(&self) -> usize {
+ match &self.inner {
+ Impl::Empty(_size) => 0,
+ Impl::Array(array) => array.count_buffers(),
+ Impl::Tree(tree) => tree.count_buffers(),
+ }
+ }
+
+ pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> {
+ match &self.inner {
+ Impl::Empty(_size) => Ok(()),
+ Impl::Array(array) => array.debug_print(m),
+ Impl::Tree(tree) => tree.debug_print(m),
+ }
+ }
+
+ /// Try to reserve a new buffer, using the provided allocation if necessary.
+ pub(crate) fn reserve_new(&mut self, mut args: ReserveNewArgs<T>) -> Result<ReserveNew<T>> {
+ match &mut self.inner {
+ Impl::Empty(size) => {
+ let empty_array = match args.empty_array_alloc.take() {
+ Some(empty_array) => ArrayRangeAllocator::new(*size, empty_array),
+ None => {
+ return Ok(ReserveNew::NeedAlloc(ReserveNewNeedAlloc {
+ args,
+ need_empty_array_alloc: true,
+ need_new_tree_alloc: false,
+ need_tree_alloc: false,
+ }))
+ }
+ };
+
+ self.inner = Impl::Array(empty_array);
+ self.reserve_new(args)
+ }
+ Impl::Array(array) if array.is_full() => {
+ let allocs = match args.new_tree_alloc {
+ Some(ref mut allocs) => allocs,
+ None => {
+ return Ok(ReserveNew::NeedAlloc(ReserveNewNeedAlloc {
+ args,
+ need_empty_array_alloc: false,
+ need_new_tree_alloc: true,
+ need_tree_alloc: true,
+ }))
+ }
+ };
+
+ let new_tree =
+ TreeRangeAllocator::from_array(array.total_size(), &mut array.ranges, allocs);
+
+ self.inner = Impl::Tree(new_tree);
+ self.reserve_new(args)
+ }
+ Impl::Array(array) => {
+ let offset =
+ array.reserve_new(args.debug_id, args.size, args.is_oneway, args.pid)?;
+ Ok(ReserveNew::Success(ReserveNewSuccess {
+ offset,
+ oneway_spam_detected: false,
+ _empty_array_alloc: args.empty_array_alloc,
+ _new_tree_alloc: args.new_tree_alloc,
+ _tree_alloc: args.tree_alloc,
+ }))
+ }
+ Impl::Tree(tree) => {
+ let alloc = match args.tree_alloc {
+ Some(alloc) => alloc,
+ None => {
+ return Ok(ReserveNew::NeedAlloc(ReserveNewNeedAlloc {
+ args,
+ need_empty_array_alloc: false,
+ need_new_tree_alloc: false,
+ need_tree_alloc: true,
+ }));
+ }
+ };
+ let (offset, oneway_spam_detected) =
+ tree.reserve_new(args.debug_id, args.size, args.is_oneway, args.pid, alloc)?;
+ Ok(ReserveNew::Success(ReserveNewSuccess {
+ offset,
+ oneway_spam_detected,
+ _empty_array_alloc: args.empty_array_alloc,
+ _new_tree_alloc: args.new_tree_alloc,
+ _tree_alloc: None,
+ }))
+ }
+ }
+ }
+
+ /// Deletes the allocations at `offset`.
+ pub(crate) fn reservation_abort(&mut self, offset: usize) -> Result<FreedRange> {
+ match &mut self.inner {
+ Impl::Empty(_size) => Err(EINVAL),
+ Impl::Array(array) => array.reservation_abort(offset),
+ Impl::Tree(tree) => {
+ let freed_range = tree.reservation_abort(offset)?;
+ if tree.is_empty() {
+ self.inner = Impl::Empty(tree.total_size());
+ }
+ Ok(freed_range)
+ }
+ }
+ }
+
+ /// Called when an allocation is no longer in use by the kernel.
+ ///
+ /// The value in `data` will be stored, if any. A mutable reference is used to avoid dropping
+ /// the `T` when an error is returned.
+ pub(crate) fn reservation_commit(&mut self, offset: usize, data: &mut Option<T>) -> Result {
+ match &mut self.inner {
+ Impl::Empty(_size) => Err(EINVAL),
+ Impl::Array(array) => array.reservation_commit(offset, data),
+ Impl::Tree(tree) => tree.reservation_commit(offset, data),
+ }
+ }
+
+ /// Called when the kernel starts using an allocation.
+ ///
+ /// Returns the size of the existing entry and the data associated with it.
+ pub(crate) fn reserve_existing(&mut self, offset: usize) -> Result<(usize, usize, Option<T>)> {
+ match &mut self.inner {
+ Impl::Empty(_size) => Err(EINVAL),
+ Impl::Array(array) => array.reserve_existing(offset),
+ Impl::Tree(tree) => tree.reserve_existing(offset),
+ }
+ }
+
+ /// Call the provided callback at every allocated region.
+ ///
+ /// This destroys the range allocator. Used only during shutdown.
+ pub(crate) fn take_for_each<F: Fn(usize, usize, usize, Option<T>)>(&mut self, callback: F) {
+ match &mut self.inner {
+ Impl::Empty(_size) => {}
+ Impl::Array(array) => array.take_for_each(callback),
+ Impl::Tree(tree) => tree.take_for_each(callback),
+ }
+ }
+}
+
+/// The arguments for `reserve_new`.
+#[derive(Default)]
+pub(crate) struct ReserveNewArgs<T> {
+ pub(crate) size: usize,
+ pub(crate) is_oneway: bool,
+ pub(crate) debug_id: usize,
+ pub(crate) pid: Pid,
+ pub(crate) empty_array_alloc: Option<EmptyArrayAlloc<T>>,
+ pub(crate) new_tree_alloc: Option<FromArrayAllocs<T>>,
+ pub(crate) tree_alloc: Option<ReserveNewTreeAlloc<T>>,
+}
+
+/// The return type of `ReserveNew`.
+pub(crate) enum ReserveNew<T> {
+ Success(ReserveNewSuccess<T>),
+ NeedAlloc(ReserveNewNeedAlloc<T>),
+}
+
+/// Returned by `reserve_new` when the reservation was successul.
+pub(crate) struct ReserveNewSuccess<T> {
+ pub(crate) offset: usize,
+ pub(crate) oneway_spam_detected: bool,
+
+ // If the user supplied an allocation that we did not end up using, then we return it here.
+ // The caller will kfree it outside of the lock.
+ _empty_array_alloc: Option<EmptyArrayAlloc<T>>,
+ _new_tree_alloc: Option<FromArrayAllocs<T>>,
+ _tree_alloc: Option<ReserveNewTreeAlloc<T>>,
+}
+
+/// Returned by `reserve_new` to request the caller to make an allocation before calling the method
+/// again.
+pub(crate) struct ReserveNewNeedAlloc<T> {
+ args: ReserveNewArgs<T>,
+ need_empty_array_alloc: bool,
+ need_new_tree_alloc: bool,
+ need_tree_alloc: bool,
+}
+
+impl<T> ReserveNewNeedAlloc<T> {
+ /// Make the necessary allocations for another call to `reserve_new`.
+ pub(crate) fn make_alloc(mut self) -> Result<ReserveNewArgs<T>> {
+ if self.need_empty_array_alloc && self.args.empty_array_alloc.is_none() {
+ self.args.empty_array_alloc = Some(EmptyArrayAlloc::try_new(TREE_THRESHOLD)?);
+ }
+ if self.need_new_tree_alloc && self.args.new_tree_alloc.is_none() {
+ self.args.new_tree_alloc = Some(FromArrayAllocs::try_new(TREE_THRESHOLD)?);
+ }
+ if self.need_tree_alloc && self.args.tree_alloc.is_none() {
+ self.args.tree_alloc = Some(ReserveNewTreeAlloc::try_new()?);
+ }
+ Ok(self.args)
+ }
+}
diff --git a/drivers/android/binder/range_alloc/tree.rs b/drivers/android/binder/range_alloc/tree.rs
new file mode 100644
index 000000000000..838fdd2b47ea
--- /dev/null
+++ b/drivers/android/binder/range_alloc/tree.rs
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::{
+ page::PAGE_SIZE,
+ prelude::*,
+ rbtree::{RBTree, RBTreeNode, RBTreeNodeReservation},
+ seq_file::SeqFile,
+ seq_print,
+ task::Pid,
+};
+
+use crate::range_alloc::{DescriptorState, FreedRange, Range};
+
+/// Keeps track of allocations in a process' mmap.
+///
+/// Each process has an mmap where the data for incoming transactions will be placed. This struct
+/// keeps track of allocations made in the mmap. For each allocation, we store a descriptor that
+/// has metadata related to the allocation. We also keep track of available free space.
+pub(super) struct TreeRangeAllocator<T> {
+ /// This collection contains descriptors for *both* ranges containing an allocation, *and* free
+ /// ranges between allocations. The free ranges get merged, so there are never two free ranges
+ /// next to each other.
+ tree: RBTree<usize, Descriptor<T>>,
+ /// Contains an entry for every free range in `self.tree`. This tree sorts the ranges by size,
+ /// letting us look up the smallest range whose size is at least some lower bound.
+ free_tree: RBTree<FreeKey, ()>,
+ size: usize,
+ free_oneway_space: usize,
+}
+
+impl<T> TreeRangeAllocator<T> {
+ pub(crate) fn from_array(
+ size: usize,
+ ranges: &mut KVec<Range<T>>,
+ alloc: &mut FromArrayAllocs<T>,
+ ) -> Self {
+ let mut tree = TreeRangeAllocator {
+ tree: RBTree::new(),
+ free_tree: RBTree::new(),
+ size,
+ free_oneway_space: size / 2,
+ };
+
+ let mut free_offset = 0;
+ for range in ranges.drain_all() {
+ let free_size = range.offset - free_offset;
+ if free_size > 0 {
+ let free_node = alloc.free_tree.pop().unwrap();
+ tree.free_tree
+ .insert(free_node.into_node((free_size, free_offset), ()));
+ let tree_node = alloc.tree.pop().unwrap();
+ tree.tree.insert(
+ tree_node.into_node(free_offset, Descriptor::new(free_offset, free_size)),
+ );
+ }
+ free_offset = range.endpoint();
+
+ if range.state.is_oneway() {
+ tree.free_oneway_space = tree.free_oneway_space.saturating_sub(range.size);
+ }
+
+ let free_res = alloc.free_tree.pop().unwrap();
+ let tree_node = alloc.tree.pop().unwrap();
+ let mut desc = Descriptor::new(range.offset, range.size);
+ desc.state = Some((range.state, free_res));
+ tree.tree.insert(tree_node.into_node(range.offset, desc));
+ }
+
+ // After the last range, we may need a free range.
+ if free_offset < size {
+ let free_size = size - free_offset;
+ let free_node = alloc.free_tree.pop().unwrap();
+ tree.free_tree
+ .insert(free_node.into_node((free_size, free_offset), ()));
+ let tree_node = alloc.tree.pop().unwrap();
+ tree.tree
+ .insert(tree_node.into_node(free_offset, Descriptor::new(free_offset, free_size)));
+ }
+
+ tree
+ }
+
+ pub(crate) fn is_empty(&self) -> bool {
+ let mut tree_iter = self.tree.values();
+ // There's always at least one range, because index zero is either the start of a free or
+ // allocated range.
+ let first_value = tree_iter.next().unwrap();
+ if tree_iter.next().is_some() {
+ // There are never two free ranges next to each other, so if there is more than one
+ // descriptor, then at least one of them must hold an allocated range.
+ return false;
+ }
+ // There is only one descriptor. Return true if it is for a free range.
+ first_value.state.is_none()
+ }
+
+ pub(crate) fn total_size(&self) -> usize {
+ self.size
+ }
+
+ pub(crate) fn free_oneway_space(&self) -> usize {
+ self.free_oneway_space
+ }
+
+ pub(crate) fn count_buffers(&self) -> usize {
+ self.tree
+ .values()
+ .filter(|desc| desc.state.is_some())
+ .count()
+ }
+
+ pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> {
+ for desc in self.tree.values() {
+ let state = match &desc.state {
+ Some(state) => &state.0,
+ None => continue,
+ };
+ seq_print!(
+ m,
+ " buffer: {} size {} pid {}",
+ desc.offset,
+ desc.size,
+ state.pid(),
+ );
+ if state.is_oneway() {
+ seq_print!(m, " oneway");
+ }
+ match state {
+ DescriptorState::Reserved(_res) => {
+ seq_print!(m, " reserved\n");
+ }
+ DescriptorState::Allocated(_alloc) => {
+ seq_print!(m, " allocated\n");
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn find_best_match(&mut self, size: usize) -> Option<&mut Descriptor<T>> {
+ let free_cursor = self.free_tree.cursor_lower_bound(&(size, 0))?;
+ let ((_, offset), ()) = free_cursor.current();
+ self.tree.get_mut(offset)
+ }
+
+ /// Try to reserve a new buffer, using the provided allocation if necessary.
+ pub(crate) fn reserve_new(
+ &mut self,
+ debug_id: usize,
+ size: usize,
+ is_oneway: bool,
+ pid: Pid,
+ alloc: ReserveNewTreeAlloc<T>,
+ ) -> Result<(usize, bool)> {
+ // Compute new value of free_oneway_space, which is set only on success.
+ let new_oneway_space = if is_oneway {
+ match self.free_oneway_space.checked_sub(size) {
+ Some(new_oneway_space) => new_oneway_space,
+ None => return Err(ENOSPC),
+ }
+ } else {
+ self.free_oneway_space
+ };
+
+ // Start detecting spammers once we have less than 20%
+ // of async space left (which is less than 10% of total
+ // buffer size).
+ //
+ // (This will short-circut, so `low_oneway_space` is
+ // only called when necessary.)
+ let oneway_spam_detected =
+ is_oneway && new_oneway_space < self.size / 10 && self.low_oneway_space(pid);
+
+ let (found_size, found_off, tree_node, free_tree_node) = match self.find_best_match(size) {
+ None => {
+ pr_warn!("ENOSPC from range_alloc.reserve_new - size: {}", size);
+ return Err(ENOSPC);
+ }
+ Some(desc) => {
+ let found_size = desc.size;
+ let found_offset = desc.offset;
+
+ // In case we need to break up the descriptor
+ let new_desc = Descriptor::new(found_offset + size, found_size - size);
+ let (tree_node, free_tree_node, desc_node_res) = alloc.initialize(new_desc);
+
+ desc.state = Some((
+ DescriptorState::new(is_oneway, debug_id, pid),
+ desc_node_res,
+ ));
+ desc.size = size;
+
+ (found_size, found_offset, tree_node, free_tree_node)
+ }
+ };
+ self.free_oneway_space = new_oneway_space;
+ self.free_tree.remove(&(found_size, found_off));
+
+ if found_size != size {
+ self.tree.insert(tree_node);
+ self.free_tree.insert(free_tree_node);
+ }
+
+ Ok((found_off, oneway_spam_detected))
+ }
+
+ pub(crate) fn reservation_abort(&mut self, offset: usize) -> Result<FreedRange> {
+ let mut cursor = self.tree.cursor_lower_bound_mut(&offset).ok_or_else(|| {
+ pr_warn!(
+ "EINVAL from range_alloc.reservation_abort - offset: {}",
+ offset
+ );
+ EINVAL
+ })?;
+
+ let (_, desc) = cursor.current_mut();
+
+ if desc.offset != offset {
+ pr_warn!(
+ "EINVAL from range_alloc.reservation_abort - offset: {}",
+ offset
+ );
+ return Err(EINVAL);
+ }
+
+ let (reservation, free_node_res) = desc.try_change_state(|state| match state {
+ Some((DescriptorState::Reserved(reservation), free_node_res)) => {
+ (None, Ok((reservation, free_node_res)))
+ }
+ None => {
+ pr_warn!(
+ "EINVAL from range_alloc.reservation_abort - offset: {}",
+ offset
+ );
+ (None, Err(EINVAL))
+ }
+ allocated => {
+ pr_warn!(
+ "EPERM from range_alloc.reservation_abort - offset: {}",
+ offset
+ );
+ (allocated, Err(EPERM))
+ }
+ })?;
+
+ let mut size = desc.size;
+ let mut offset = desc.offset;
+ let free_oneway_space_add = if reservation.is_oneway { size } else { 0 };
+
+ self.free_oneway_space += free_oneway_space_add;
+
+ let mut freed_range = FreedRange::interior_pages(offset, size);
+ // Compute how large the next free region needs to be to include one more page in
+ // the newly freed range.
+ let add_next_page_needed = match (offset + size) % PAGE_SIZE {
+ 0 => usize::MAX,
+ unalign => PAGE_SIZE - unalign,
+ };
+ // Compute how large the previous free region needs to be to include one more page
+ // in the newly freed range.
+ let add_prev_page_needed = match offset % PAGE_SIZE {
+ 0 => usize::MAX,
+ unalign => unalign,
+ };
+
+ // Merge next into current if next is free
+ let remove_next = match cursor.peek_next() {
+ Some((_, next)) if next.state.is_none() => {
+ if next.size >= add_next_page_needed {
+ freed_range.end_page_idx += 1;
+ }
+ self.free_tree.remove(&(next.size, next.offset));
+ size += next.size;
+ true
+ }
+ _ => false,
+ };
+
+ if remove_next {
+ let (_, desc) = cursor.current_mut();
+ desc.size = size;
+ cursor.remove_next();
+ }
+
+ // Merge current into prev if prev is free
+ match cursor.peek_prev_mut() {
+ Some((_, prev)) if prev.state.is_none() => {
+ if prev.size >= add_prev_page_needed {
+ freed_range.start_page_idx -= 1;
+ }
+ // merge previous with current, remove current
+ self.free_tree.remove(&(prev.size, prev.offset));
+ offset = prev.offset;
+ size += prev.size;
+ prev.size = size;
+ cursor.remove_current();
+ }
+ _ => {}
+ };
+
+ self.free_tree
+ .insert(free_node_res.into_node((size, offset), ()));
+
+ Ok(freed_range)
+ }
+
+ pub(crate) fn reservation_commit(&mut self, offset: usize, data: &mut Option<T>) -> Result {
+ let desc = self.tree.get_mut(&offset).ok_or(ENOENT)?;
+
+ desc.try_change_state(|state| match state {
+ Some((DescriptorState::Reserved(reservation), free_node_res)) => (
+ Some((
+ DescriptorState::Allocated(reservation.allocate(data.take())),
+ free_node_res,
+ )),
+ Ok(()),
+ ),
+ other => (other, Err(ENOENT)),
+ })
+ }
+
+ /// Takes an entry at the given offset from [`DescriptorState::Allocated`] to
+ /// [`DescriptorState::Reserved`].
+ ///
+ /// Returns the size of the existing entry and the data associated with it.
+ pub(crate) fn reserve_existing(&mut self, offset: usize) -> Result<(usize, usize, Option<T>)> {
+ let desc = self.tree.get_mut(&offset).ok_or_else(|| {
+ pr_warn!(
+ "ENOENT from range_alloc.reserve_existing - offset: {}",
+ offset
+ );
+ ENOENT
+ })?;
+
+ let (debug_id, data) = desc.try_change_state(|state| match state {
+ Some((DescriptorState::Allocated(allocation), free_node_res)) => {
+ let (reservation, data) = allocation.deallocate();
+ let debug_id = reservation.debug_id;
+ (
+ Some((DescriptorState::Reserved(reservation), free_node_res)),
+ Ok((debug_id, data)),
+ )
+ }
+ other => {
+ pr_warn!(
+ "ENOENT from range_alloc.reserve_existing - offset: {}",
+ offset
+ );
+ (other, Err(ENOENT))
+ }
+ })?;
+
+ Ok((desc.size, debug_id, data))
+ }
+
+ /// Call the provided callback at every allocated region.
+ ///
+ /// This destroys the range allocator. Used only during shutdown.
+ pub(crate) fn take_for_each<F: Fn(usize, usize, usize, Option<T>)>(&mut self, callback: F) {
+ for (_, desc) in self.tree.iter_mut() {
+ if let Some((DescriptorState::Allocated(allocation), _)) = &mut desc.state {
+ callback(
+ desc.offset,
+ desc.size,
+ allocation.debug_id(),
+ allocation.take(),
+ );
+ }
+ }
+ }
+
+ /// Find the amount and size of buffers allocated by the current caller.
+ ///
+ /// The idea is that once we cross the threshold, whoever is responsible
+ /// for the low async space is likely to try to send another async transaction,
+ /// and at some point we'll catch them in the act. This is more efficient
+ /// than keeping a map per pid.
+ fn low_oneway_space(&self, calling_pid: Pid) -> bool {
+ let mut total_alloc_size = 0;
+ let mut num_buffers = 0;
+ for (_, desc) in self.tree.iter() {
+ if let Some((state, _)) = &desc.state {
+ if state.is_oneway() && state.pid() == calling_pid {
+ total_alloc_size += desc.size;
+ num_buffers += 1;
+ }
+ }
+ }
+
+ // Warn if this pid has more than 50 transactions, or more than 50% of
+ // async space (which is 25% of total buffer size). Oneway spam is only
+ // detected when the threshold is exceeded.
+ num_buffers > 50 || total_alloc_size > self.size / 4
+ }
+}
+
+type TreeDescriptorState<T> = (DescriptorState<T>, FreeNodeRes);
+struct Descriptor<T> {
+ size: usize,
+ offset: usize,
+ state: Option<TreeDescriptorState<T>>,
+}
+
+impl<T> Descriptor<T> {
+ fn new(offset: usize, size: usize) -> Self {
+ Self {
+ size,
+ offset,
+ state: None,
+ }
+ }
+
+ fn try_change_state<F, Data>(&mut self, f: F) -> Result<Data>
+ where
+ F: FnOnce(Option<TreeDescriptorState<T>>) -> (Option<TreeDescriptorState<T>>, Result<Data>),
+ {
+ let (new_state, result) = f(self.state.take());
+ self.state = new_state;
+ result
+ }
+}
+
+// (Descriptor.size, Descriptor.offset)
+type FreeKey = (usize, usize);
+type FreeNodeRes = RBTreeNodeReservation<FreeKey, ()>;
+
+/// An allocation for use by `reserve_new`.
+pub(crate) struct ReserveNewTreeAlloc<T> {
+ tree_node_res: RBTreeNodeReservation<usize, Descriptor<T>>,
+ free_tree_node_res: FreeNodeRes,
+ desc_node_res: FreeNodeRes,
+}
+
+impl<T> ReserveNewTreeAlloc<T> {
+ pub(crate) fn try_new() -> Result<Self> {
+ let tree_node_res = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let free_tree_node_res = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let desc_node_res = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ Ok(Self {
+ tree_node_res,
+ free_tree_node_res,
+ desc_node_res,
+ })
+ }
+
+ fn initialize(
+ self,
+ desc: Descriptor<T>,
+ ) -> (
+ RBTreeNode<usize, Descriptor<T>>,
+ RBTreeNode<FreeKey, ()>,
+ FreeNodeRes,
+ ) {
+ let size = desc.size;
+ let offset = desc.offset;
+ (
+ self.tree_node_res.into_node(offset, desc),
+ self.free_tree_node_res.into_node((size, offset), ()),
+ self.desc_node_res,
+ )
+ }
+}
+
+/// An allocation for creating a tree from an `ArrayRangeAllocator`.
+pub(crate) struct FromArrayAllocs<T> {
+ tree: KVec<RBTreeNodeReservation<usize, Descriptor<T>>>,
+ free_tree: KVec<RBTreeNodeReservation<FreeKey, ()>>,
+}
+
+impl<T> FromArrayAllocs<T> {
+ pub(crate) fn try_new(len: usize) -> Result<Self> {
+ let num_descriptors = 2 * len + 1;
+
+ let mut tree = KVec::with_capacity(num_descriptors, GFP_KERNEL)?;
+ for _ in 0..num_descriptors {
+ tree.push(RBTreeNodeReservation::new(GFP_KERNEL)?, GFP_KERNEL)?;
+ }
+
+ let mut free_tree = KVec::with_capacity(num_descriptors, GFP_KERNEL)?;
+ for _ in 0..num_descriptors {
+ free_tree.push(RBTreeNodeReservation::new(GFP_KERNEL)?, GFP_KERNEL)?;
+ }
+
+ Ok(Self { tree, free_tree })
+ }
+}
diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h
new file mode 100644
index 000000000000..31806890ed1a
--- /dev/null
+++ b/drivers/android/binder/rust_binder.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Google, Inc.
+ */
+
+#ifndef _LINUX_RUST_BINDER_H
+#define _LINUX_RUST_BINDER_H
+
+#include <uapi/linux/android/binder.h>
+#include <uapi/linux/android/binderfs.h>
+
+/*
+ * These symbols are exposed by `rust_binderfs.c` and exist here so that Rust
+ * Binder can call them.
+ */
+int init_rust_binderfs(void);
+
+struct dentry;
+struct inode;
+struct dentry *rust_binderfs_create_proc_file(struct inode *nodp, int pid);
+void rust_binderfs_remove_file(struct dentry *dentry);
+
+#endif
diff --git a/drivers/android/binder/rust_binder_events.c b/drivers/android/binder/rust_binder_events.c
new file mode 100644
index 000000000000..488b1470060c
--- /dev/null
+++ b/drivers/android/binder/rust_binder_events.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* rust_binder_events.c
+ *
+ * Rust Binder tracepoints.
+ *
+ * Copyright 2025 Google LLC
+ */
+
+#include "rust_binder.h"
+
+const char * const binder_command_strings[] = {
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
+};
+
+const char * const binder_return_strings[] = {
+ "BR_ERROR",
+ "BR_OK",
+ "BR_TRANSACTION",
+ "BR_REPLY",
+ "BR_ACQUIRE_RESULT",
+ "BR_DEAD_REPLY",
+ "BR_TRANSACTION_COMPLETE",
+ "BR_INCREFS",
+ "BR_ACQUIRE",
+ "BR_RELEASE",
+ "BR_DECREFS",
+ "BR_ATTEMPT_ACQUIRE",
+ "BR_NOOP",
+ "BR_SPAWN_LOOPER",
+ "BR_FINISHED",
+ "BR_DEAD_BINDER",
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+ "BR_FAILED_REPLY",
+ "BR_FROZEN_REPLY",
+ "BR_ONEWAY_SPAM_SUSPECT",
+ "BR_TRANSACTION_PENDING_FROZEN"
+};
+
+#define CREATE_TRACE_POINTS
+#define CREATE_RUST_TRACE_POINTS
+#include "rust_binder_events.h"
diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h
new file mode 100644
index 000000000000..2f3efbf9dba6
--- /dev/null
+++ b/drivers/android/binder/rust_binder_events.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Google, Inc.
+ */
+
+#undef TRACE_SYSTEM
+#undef TRACE_INCLUDE_FILE
+#undef TRACE_INCLUDE_PATH
+#define TRACE_SYSTEM rust_binder
+#define TRACE_INCLUDE_FILE rust_binder_events
+#define TRACE_INCLUDE_PATH ../drivers/android/binder
+
+#if !defined(_RUST_BINDER_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _RUST_BINDER_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(rust_binder_ioctl,
+ TP_PROTO(unsigned int cmd, unsigned long arg),
+ TP_ARGS(cmd, arg),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned long, arg)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->arg = arg;
+ ),
+ TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
+);
+
+#endif /* _RUST_BINDER_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/android/binder/rust_binder_internal.h b/drivers/android/binder/rust_binder_internal.h
new file mode 100644
index 000000000000..78288fe7964d
--- /dev/null
+++ b/drivers/android/binder/rust_binder_internal.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* rust_binder_internal.h
+ *
+ * This file contains internal data structures used by Rust Binder. Mostly,
+ * these are type definitions used only by binderfs or things that Rust Binder
+ * define and export to binderfs.
+ *
+ * It does not include things exported by binderfs to Rust Binder since this
+ * file is not included as input to bindgen.
+ *
+ * Copyright (C) 2025 Google LLC.
+ */
+
+#ifndef _LINUX_RUST_BINDER_INTERNAL_H
+#define _LINUX_RUST_BINDER_INTERNAL_H
+
+#define RUST_BINDERFS_SUPER_MAGIC 0x6c6f6f71
+
+#include <linux/seq_file.h>
+#include <uapi/linux/android/binder.h>
+#include <uapi/linux/android/binderfs.h>
+
+/*
+ * The internal data types in the Rust Binder driver are opaque to C, so we use
+ * void pointer typedefs for these types.
+ */
+typedef void *rust_binder_context;
+
+/**
+ * struct binder_device - information about a binder device node
+ * @minor: the minor number used by this device
+ * @ctx: the Rust Context used by this device, or null for binder-control
+ *
+ * This is used as the private data for files directly in binderfs, but not
+ * files in the binder_logs subdirectory. This struct owns a refcount on `ctx`
+ * and the entry for `minor` in `binderfs_minors`. For binder-control `ctx` is
+ * null.
+ */
+struct binder_device {
+ int minor;
+ rust_binder_context ctx;
+};
+
+int rust_binder_stats_show(struct seq_file *m, void *unused);
+int rust_binder_state_show(struct seq_file *m, void *unused);
+int rust_binder_transactions_show(struct seq_file *m, void *unused);
+int rust_binder_proc_show(struct seq_file *m, void *pid);
+
+extern const struct file_operations rust_binder_fops;
+rust_binder_context rust_binder_new_context(char *name);
+void rust_binder_remove_context(rust_binder_context device);
+
+/**
+ * binderfs_mount_opts - mount options for binderfs
+ * @max: maximum number of allocatable binderfs binder devices
+ * @stats_mode: enable binder stats in binderfs.
+ */
+struct binderfs_mount_opts {
+ int max;
+ int stats_mode;
+};
+
+/**
+ * binderfs_info - information about a binderfs mount
+ * @ipc_ns: The ipc namespace the binderfs mount belongs to.
+ * @control_dentry: This records the dentry of this binderfs mount
+ * binder-control device.
+ * @root_uid: uid that needs to be used when a new binder device is
+ * created.
+ * @root_gid: gid that needs to be used when a new binder device is
+ * created.
+ * @mount_opts: The mount options in use.
+ * @device_count: The current number of allocated binder devices.
+ * @proc_log_dir: Pointer to the directory dentry containing process-specific
+ * logs.
+ */
+struct binderfs_info {
+ struct ipc_namespace *ipc_ns;
+ struct dentry *control_dentry;
+ kuid_t root_uid;
+ kgid_t root_gid;
+ struct binderfs_mount_opts mount_opts;
+ int device_count;
+ struct dentry *proc_log_dir;
+};
+
+#endif /* _LINUX_RUST_BINDER_INTERNAL_H */
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
new file mode 100644
index 000000000000..c79a9e742240
--- /dev/null
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Binder -- the Android IPC mechanism.
+#![recursion_limit = "256"]
+#![allow(
+ clippy::as_underscore,
+ clippy::ref_as_ptr,
+ clippy::ptr_as_ptr,
+ clippy::cast_lossless
+)]
+
+use kernel::{
+ bindings::{self, seq_file},
+ fs::File,
+ list::{ListArc, ListArcSafe, ListLinksSelfPtr, TryNewListArc},
+ prelude::*,
+ seq_file::SeqFile,
+ seq_print,
+ sync::poll::PollTable,
+ sync::Arc,
+ task::Pid,
+ transmute::AsBytes,
+ types::ForeignOwnable,
+ uaccess::UserSliceWriter,
+};
+
+use crate::{context::Context, page_range::Shrinker, process::Process, thread::Thread};
+
+use core::{
+ ptr::NonNull,
+ sync::atomic::{AtomicBool, AtomicUsize, Ordering},
+};
+
+mod allocation;
+mod context;
+mod deferred_close;
+mod defs;
+mod error;
+mod node;
+mod page_range;
+mod process;
+mod range_alloc;
+mod stats;
+mod thread;
+mod trace;
+mod transaction;
+
+#[allow(warnings)] // generated bindgen code
+mod binderfs {
+ use kernel::bindings::{dentry, inode};
+
+ extern "C" {
+ pub fn init_rust_binderfs() -> kernel::ffi::c_int;
+ }
+ extern "C" {
+ pub fn rust_binderfs_create_proc_file(
+ nodp: *mut inode,
+ pid: kernel::ffi::c_int,
+ ) -> *mut dentry;
+ }
+ extern "C" {
+ pub fn rust_binderfs_remove_file(dentry: *mut dentry);
+ }
+ pub type rust_binder_context = *mut kernel::ffi::c_void;
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct binder_device {
+ pub minor: kernel::ffi::c_int,
+ pub ctx: rust_binder_context,
+ }
+ impl Default for binder_device {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+ }
+}
+
+module! {
+ type: BinderModule,
+ name: "rust_binder",
+ authors: ["Wedson Almeida Filho", "Alice Ryhl"],
+ description: "Android Binder",
+ license: "GPL",
+}
+
+fn next_debug_id() -> usize {
+ static NEXT_DEBUG_ID: AtomicUsize = AtomicUsize::new(0);
+
+ NEXT_DEBUG_ID.fetch_add(1, Ordering::Relaxed)
+}
+
+/// Provides a single place to write Binder return values via the
+/// supplied `UserSliceWriter`.
+pub(crate) struct BinderReturnWriter<'a> {
+ writer: UserSliceWriter,
+ thread: &'a Thread,
+}
+
+impl<'a> BinderReturnWriter<'a> {
+ fn new(writer: UserSliceWriter, thread: &'a Thread) -> Self {
+ BinderReturnWriter { writer, thread }
+ }
+
+ /// Write a return code back to user space.
+ /// Should be a `BR_` constant from [`defs`] e.g. [`defs::BR_TRANSACTION_COMPLETE`].
+ fn write_code(&mut self, code: u32) -> Result {
+ stats::GLOBAL_STATS.inc_br(code);
+ self.thread.process.stats.inc_br(code);
+ self.writer.write(&code)
+ }
+
+ /// Write something *other than* a return code to user space.
+ fn write_payload<T: AsBytes>(&mut self, payload: &T) -> Result {
+ self.writer.write(payload)
+ }
+
+ fn len(&self) -> usize {
+ self.writer.len()
+ }
+}
+
+/// Specifies how a type should be delivered to the read part of a BINDER_WRITE_READ ioctl.
+///
+/// When a value is pushed to the todo list for a process or thread, it is stored as a trait object
+/// with the type `Arc<dyn DeliverToRead>`. Trait objects are a Rust feature that lets you
+/// implement dynamic dispatch over many different types. This lets us store many different types
+/// in the todo list.
+trait DeliverToRead: ListArcSafe + Send + Sync {
+ /// Performs work. Returns true if remaining work items in the queue should be processed
+ /// immediately, or false if it should return to caller before processing additional work
+ /// items.
+ fn do_work(
+ self: DArc<Self>,
+ thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool>;
+
+ /// Cancels the given work item. This is called instead of [`DeliverToRead::do_work`] when work
+ /// won't be delivered.
+ fn cancel(self: DArc<Self>);
+
+ /// Should we use `wake_up_interruptible_sync` or `wake_up_interruptible` when scheduling this
+ /// work item?
+ ///
+ /// Generally only set to true for non-oneway transactions.
+ fn should_sync_wakeup(&self) -> bool;
+
+ fn debug_print(&self, m: &SeqFile, prefix: &str, transaction_prefix: &str) -> Result<()>;
+}
+
+// Wrapper around a `DeliverToRead` with linked list links.
+#[pin_data]
+struct DTRWrap<T: ?Sized> {
+ #[pin]
+ links: ListLinksSelfPtr<DTRWrap<dyn DeliverToRead>>,
+ #[pin]
+ wrapped: T,
+}
+kernel::list::impl_list_arc_safe! {
+ impl{T: ListArcSafe + ?Sized} ListArcSafe<0> for DTRWrap<T> {
+ tracked_by wrapped: T;
+ }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<0> for DTRWrap<dyn DeliverToRead> {
+ using ListLinksSelfPtr { self.links };
+ }
+}
+
+impl<T: ?Sized> core::ops::Deref for DTRWrap<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.wrapped
+ }
+}
+
+type DArc<T> = kernel::sync::Arc<DTRWrap<T>>;
+type DLArc<T> = kernel::list::ListArc<DTRWrap<T>>;
+
+impl<T: ListArcSafe> DTRWrap<T> {
+ fn new(val: impl PinInit<T>) -> impl PinInit<Self> {
+ pin_init!(Self {
+ links <- ListLinksSelfPtr::new(),
+ wrapped <- val,
+ })
+ }
+
+ fn arc_try_new(val: T) -> Result<DLArc<T>, kernel::alloc::AllocError> {
+ ListArc::pin_init(
+ try_pin_init!(Self {
+ links <- ListLinksSelfPtr::new(),
+ wrapped: val,
+ }),
+ GFP_KERNEL,
+ )
+ .map_err(|_| kernel::alloc::AllocError)
+ }
+
+ fn arc_pin_init(init: impl PinInit<T>) -> Result<DLArc<T>, kernel::error::Error> {
+ ListArc::pin_init(
+ try_pin_init!(Self {
+ links <- ListLinksSelfPtr::new(),
+ wrapped <- init,
+ }),
+ GFP_KERNEL,
+ )
+ }
+}
+
+struct DeliverCode {
+ code: u32,
+ skip: AtomicBool,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for DeliverCode { untracked; }
+}
+
+impl DeliverCode {
+ fn new(code: u32) -> Self {
+ Self {
+ code,
+ skip: AtomicBool::new(false),
+ }
+ }
+
+ /// Disable this DeliverCode and make it do nothing.
+ ///
+ /// This is used instead of removing it from the work list, since `LinkedList::remove` is
+ /// unsafe, whereas this method is not.
+ fn skip(&self) {
+ self.skip.store(true, Ordering::Relaxed);
+ }
+}
+
+impl DeliverToRead for DeliverCode {
+ fn do_work(
+ self: DArc<Self>,
+ _thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ if !self.skip.load(Ordering::Relaxed) {
+ writer.write_code(self.code)?;
+ }
+ Ok(true)
+ }
+
+ fn cancel(self: DArc<Self>) {}
+
+ fn should_sync_wakeup(&self) -> bool {
+ false
+ }
+
+ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
+ seq_print!(m, "{}", prefix);
+ if self.skip.load(Ordering::Relaxed) {
+ seq_print!(m, "(skipped) ");
+ }
+ if self.code == defs::BR_TRANSACTION_COMPLETE {
+ seq_print!(m, "transaction complete\n");
+ } else {
+ seq_print!(m, "transaction error: {}\n", self.code);
+ }
+ Ok(())
+ }
+}
+
+fn ptr_align(value: usize) -> Option<usize> {
+ let size = core::mem::size_of::<usize>() - 1;
+ Some(value.checked_add(size)? & !size)
+}
+
+// SAFETY: We call register in `init`.
+static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() };
+
+struct BinderModule {}
+
+impl kernel::Module for BinderModule {
+ fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
+ // SAFETY: The module initializer never runs twice, so we only call this once.
+ unsafe { crate::context::CONTEXTS.init() };
+
+ pr_warn!("Loaded Rust Binder.");
+
+ BINDER_SHRINKER.register(kernel::c_str!("android-binder"))?;
+
+ // SAFETY: The module is being loaded, so we can initialize binderfs.
+ unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? };
+
+ Ok(Self {})
+ }
+}
+
+/// Makes the inner type Sync.
+#[repr(transparent)]
+pub struct AssertSync<T>(T);
+// SAFETY: Used only to insert `file_operations` into a global, which is safe.
+unsafe impl<T> Sync for AssertSync<T> {}
+
+/// File operations that rust_binderfs.c can use.
+#[no_mangle]
+#[used]
+pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
+ // SAFETY: All zeroes is safe for the `file_operations` type.
+ let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+ let ops = kernel::bindings::file_operations {
+ owner: THIS_MODULE.as_ptr(),
+ poll: Some(rust_binder_poll),
+ unlocked_ioctl: Some(rust_binder_ioctl),
+ compat_ioctl: Some(bindings::compat_ptr_ioctl),
+ mmap: Some(rust_binder_mmap),
+ open: Some(rust_binder_open),
+ release: Some(rust_binder_release),
+ flush: Some(rust_binder_flush),
+ ..zeroed_ops
+ };
+ AssertSync(ops)
+};
+
+/// # Safety
+/// Only called by binderfs.
+#[no_mangle]
+unsafe extern "C" fn rust_binder_new_context(
+ name: *const kernel::ffi::c_char,
+) -> *mut kernel::ffi::c_void {
+ // SAFETY: The caller will always provide a valid c string here.
+ let name = unsafe { kernel::str::CStr::from_char_ptr(name) };
+ match Context::new(name) {
+ Ok(ctx) => Arc::into_foreign(ctx),
+ Err(_err) => core::ptr::null_mut(),
+ }
+}
+
+/// # Safety
+/// Only called by binderfs.
+#[no_mangle]
+unsafe extern "C" fn rust_binder_remove_context(device: *mut kernel::ffi::c_void) {
+ if !device.is_null() {
+ // SAFETY: The caller ensures that the `device` pointer came from a previous call to
+ // `rust_binder_new_device`.
+ let ctx = unsafe { Arc::<Context>::from_foreign(device) };
+ ctx.deregister();
+ drop(ctx);
+ }
+}
+
+/// # Safety
+/// Only called by binderfs.
+unsafe extern "C" fn rust_binder_open(
+ inode: *mut bindings::inode,
+ file_ptr: *mut bindings::file,
+) -> kernel::ffi::c_int {
+ // SAFETY: The `rust_binderfs.c` file ensures that `i_private` is set to a
+ // `struct binder_device`.
+ let device = unsafe { (*inode).i_private } as *const binderfs::binder_device;
+
+ assert!(!device.is_null());
+
+ // SAFETY: The `rust_binderfs.c` file ensures that `device->ctx` holds a binder context when
+ // using the rust binder fops.
+ let ctx = unsafe { Arc::<Context>::borrow((*device).ctx) };
+
+ // SAFETY: The caller provides a valid file pointer to a new `struct file`.
+ let file = unsafe { File::from_raw_file(file_ptr) };
+ let process = match Process::open(ctx, file) {
+ Ok(process) => process,
+ Err(err) => return err.to_errno(),
+ };
+
+ // SAFETY: This is an `inode` for a newly created binder file.
+ match unsafe { BinderfsProcFile::new(inode, process.task.pid()) } {
+ Ok(Some(file)) => process.inner.lock().binderfs_file = Some(file),
+ Ok(None) => { /* pid already exists */ }
+ Err(err) => return err.to_errno(),
+ }
+
+ // SAFETY: This file is associated with Rust binder, so we own the `private_data` field.
+ unsafe { (*file_ptr).private_data = process.into_foreign() };
+ 0
+}
+
+/// # Safety
+/// Only called by binderfs.
+unsafe extern "C" fn rust_binder_release(
+ _inode: *mut bindings::inode,
+ file: *mut bindings::file,
+) -> kernel::ffi::c_int {
+ // SAFETY: We previously set `private_data` in `rust_binder_open`.
+ let process = unsafe { Arc::<Process>::from_foreign((*file).private_data) };
+ // SAFETY: The caller ensures that the file is valid.
+ let file = unsafe { File::from_raw_file(file) };
+ Process::release(process, file);
+ 0
+}
+
+/// # Safety
+/// Only called by binderfs.
+unsafe extern "C" fn rust_binder_ioctl(
+ file: *mut bindings::file,
+ cmd: kernel::ffi::c_uint,
+ arg: kernel::ffi::c_ulong,
+) -> kernel::ffi::c_long {
+ // SAFETY: We previously set `private_data` in `rust_binder_open`.
+ let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
+ // SAFETY: The caller ensures that the file is valid.
+ match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) {
+ Ok(()) => 0,
+ Err(err) => err.to_errno() as isize,
+ }
+}
+
+/// # Safety
+/// Only called by binderfs.
+unsafe extern "C" fn rust_binder_mmap(
+ file: *mut bindings::file,
+ vma: *mut bindings::vm_area_struct,
+) -> kernel::ffi::c_int {
+ // SAFETY: We previously set `private_data` in `rust_binder_open`.
+ let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
+ // SAFETY: The caller ensures that the vma is valid.
+ let area = unsafe { kernel::mm::virt::VmaNew::from_raw(vma) };
+ // SAFETY: The caller ensures that the file is valid.
+ match Process::mmap(f, unsafe { File::from_raw_file(file) }, area) {
+ Ok(()) => 0,
+ Err(err) => err.to_errno(),
+ }
+}
+
+/// # Safety
+/// Only called by binderfs.
+unsafe extern "C" fn rust_binder_poll(
+ file: *mut bindings::file,
+ wait: *mut bindings::poll_table_struct,
+) -> bindings::__poll_t {
+ // SAFETY: We previously set `private_data` in `rust_binder_open`.
+ let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
+ // SAFETY: The caller ensures that the file is valid.
+ let fileref = unsafe { File::from_raw_file(file) };
+ // SAFETY: The caller ensures that the `PollTable` is valid.
+ match Process::poll(f, fileref, unsafe { PollTable::from_raw(wait) }) {
+ Ok(v) => v,
+ Err(_) => bindings::POLLERR,
+ }
+}
+
+/// # Safety
+/// Only called by binderfs.
+unsafe extern "C" fn rust_binder_flush(
+ file: *mut bindings::file,
+ _id: bindings::fl_owner_t,
+) -> kernel::ffi::c_int {
+ // SAFETY: We previously set `private_data` in `rust_binder_open`.
+ let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
+ match Process::flush(f) {
+ Ok(()) => 0,
+ Err(err) => err.to_errno(),
+ }
+}
+
+/// # Safety
+/// Only called by binderfs.
+#[no_mangle]
+unsafe extern "C" fn rust_binder_stats_show(
+ ptr: *mut seq_file,
+ _: *mut kernel::ffi::c_void,
+) -> kernel::ffi::c_int {
+ // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
+ // this method is called.
+ let m = unsafe { SeqFile::from_raw(ptr) };
+ if let Err(err) = rust_binder_stats_show_impl(m) {
+ seq_print!(m, "failed to generate state: {:?}\n", err);
+ }
+ 0
+}
+
+/// # Safety
+/// Only called by binderfs.
+#[no_mangle]
+unsafe extern "C" fn rust_binder_state_show(
+ ptr: *mut seq_file,
+ _: *mut kernel::ffi::c_void,
+) -> kernel::ffi::c_int {
+ // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
+ // this method is called.
+ let m = unsafe { SeqFile::from_raw(ptr) };
+ if let Err(err) = rust_binder_state_show_impl(m) {
+ seq_print!(m, "failed to generate state: {:?}\n", err);
+ }
+ 0
+}
+
+/// # Safety
+/// Only called by binderfs.
+#[no_mangle]
+unsafe extern "C" fn rust_binder_proc_show(
+ ptr: *mut seq_file,
+ _: *mut kernel::ffi::c_void,
+) -> kernel::ffi::c_int {
+ // SAFETY: Accessing the private field of `seq_file` is okay.
+ let pid = (unsafe { (*ptr).private }) as usize as Pid;
+ // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
+ // this method is called.
+ let m = unsafe { SeqFile::from_raw(ptr) };
+ if let Err(err) = rust_binder_proc_show_impl(m, pid) {
+ seq_print!(m, "failed to generate state: {:?}\n", err);
+ }
+ 0
+}
+
+/// # Safety
+/// Only called by binderfs.
+#[no_mangle]
+unsafe extern "C" fn rust_binder_transactions_show(
+ ptr: *mut seq_file,
+ _: *mut kernel::ffi::c_void,
+) -> kernel::ffi::c_int {
+ // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
+ // this method is called.
+ let m = unsafe { SeqFile::from_raw(ptr) };
+ if let Err(err) = rust_binder_transactions_show_impl(m) {
+ seq_print!(m, "failed to generate state: {:?}\n", err);
+ }
+ 0
+}
+
+fn rust_binder_transactions_show_impl(m: &SeqFile) -> Result<()> {
+ seq_print!(m, "binder transactions:\n");
+ let contexts = context::get_all_contexts()?;
+ for ctx in contexts {
+ let procs = ctx.get_all_procs()?;
+ for proc in procs {
+ proc.debug_print(m, &ctx, false)?;
+ seq_print!(m, "\n");
+ }
+ }
+ Ok(())
+}
+
+fn rust_binder_stats_show_impl(m: &SeqFile) -> Result<()> {
+ seq_print!(m, "binder stats:\n");
+ stats::GLOBAL_STATS.debug_print("", m);
+ let contexts = context::get_all_contexts()?;
+ for ctx in contexts {
+ let procs = ctx.get_all_procs()?;
+ for proc in procs {
+ proc.debug_print_stats(m, &ctx)?;
+ seq_print!(m, "\n");
+ }
+ }
+ Ok(())
+}
+
+fn rust_binder_state_show_impl(m: &SeqFile) -> Result<()> {
+ seq_print!(m, "binder state:\n");
+ let contexts = context::get_all_contexts()?;
+ for ctx in contexts {
+ let procs = ctx.get_all_procs()?;
+ for proc in procs {
+ proc.debug_print(m, &ctx, true)?;
+ seq_print!(m, "\n");
+ }
+ }
+ Ok(())
+}
+
+fn rust_binder_proc_show_impl(m: &SeqFile, pid: Pid) -> Result<()> {
+ seq_print!(m, "binder proc state:\n");
+ let contexts = context::get_all_contexts()?;
+ for ctx in contexts {
+ let procs = ctx.get_procs_with_pid(pid)?;
+ for proc in procs {
+ proc.debug_print(m, &ctx, true)?;
+ seq_print!(m, "\n");
+ }
+ }
+ Ok(())
+}
+
+struct BinderfsProcFile(NonNull<bindings::dentry>);
+
+// SAFETY: Safe to drop any thread.
+unsafe impl Send for BinderfsProcFile {}
+
+impl BinderfsProcFile {
+ /// # Safety
+ ///
+ /// Takes an inode from a newly created binder file.
+ unsafe fn new(nodp: *mut bindings::inode, pid: i32) -> Result<Option<Self>> {
+ // SAFETY: The caller passes an `inode` for a newly created binder file.
+ let dentry = unsafe { binderfs::rust_binderfs_create_proc_file(nodp, pid) };
+ match kernel::error::from_err_ptr(dentry) {
+ Ok(dentry) => Ok(NonNull::new(dentry).map(Self)),
+ Err(err) if err == EEXIST => Ok(None),
+ Err(err) => Err(err),
+ }
+ }
+}
+
+impl Drop for BinderfsProcFile {
+ fn drop(&mut self) {
+ // SAFETY: This is a dentry from `rust_binderfs_remove_file` that has not been deleted yet.
+ unsafe { binderfs::rust_binderfs_remove_file(self.0.as_ptr()) };
+ }
+}
diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c
new file mode 100644
index 000000000000..c69026df775c
--- /dev/null
+++ b/drivers/android/binder/rust_binderfs.c
@@ -0,0 +1,795 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/compiler_types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fsnotify.h>
+#include <linux/gfp.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/ipc_namespace.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/namei.h>
+#include <linux/magic.h>
+#include <linux/major.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/mount.h>
+#include <linux/fs_parser.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock_types.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/user_namespace.h>
+#include <linux/xarray.h>
+#include <uapi/asm-generic/errno-base.h>
+#include <uapi/linux/android/binder.h>
+#include <uapi/linux/android/binderfs.h>
+
+#include "rust_binder.h"
+#include "rust_binder_internal.h"
+
+#define FIRST_INODE 1
+#define SECOND_INODE 2
+#define INODE_OFFSET 3
+#define BINDERFS_MAX_MINOR (1U << MINORBITS)
+/* Ensure that the initial ipc namespace always has devices available. */
+#define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4)
+
+DEFINE_SHOW_ATTRIBUTE(rust_binder_stats);
+DEFINE_SHOW_ATTRIBUTE(rust_binder_state);
+DEFINE_SHOW_ATTRIBUTE(rust_binder_transactions);
+DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);
+
+char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
+module_param_named(rust_devices, rust_binder_devices_param, charp, 0444);
+
+static dev_t binderfs_dev;
+static DEFINE_MUTEX(binderfs_minors_mutex);
+static DEFINE_IDA(binderfs_minors);
+
+enum binderfs_param {
+ Opt_max,
+ Opt_stats_mode,
+};
+
+enum binderfs_stats_mode {
+ binderfs_stats_mode_unset,
+ binderfs_stats_mode_global,
+};
+
+struct binder_features {
+ bool oneway_spam_detection;
+ bool extended_error;
+ bool freeze_notification;
+};
+
+static const struct constant_table binderfs_param_stats[] = {
+ { "global", binderfs_stats_mode_global },
+ {}
+};
+
+static const struct fs_parameter_spec binderfs_fs_parameters[] = {
+ fsparam_u32("max", Opt_max),
+ fsparam_enum("stats", Opt_stats_mode, binderfs_param_stats),
+ {}
+};
+
+static struct binder_features binder_features = {
+ .oneway_spam_detection = true,
+ .extended_error = true,
+ .freeze_notification = true,
+};
+
+static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+/**
+ * binderfs_binder_device_create - allocate inode from super block of a
+ * binderfs mount
+ * @ref_inode: inode from wich the super block will be taken
+ * @userp: buffer to copy information about new device for userspace to
+ * @req: struct binderfs_device as copied from userspace
+ *
+ * This function allocates a new binder_device and reserves a new minor
+ * number for it.
+ * Minor numbers are limited and tracked globally in binderfs_minors. The
+ * function will stash a struct binder_device for the specific binder
+ * device in i_private of the inode.
+ * It will go on to allocate a new inode from the super block of the
+ * filesystem mount, stash a struct binder_device in its i_private field
+ * and attach a dentry to that inode.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int binderfs_binder_device_create(struct inode *ref_inode,
+ struct binderfs_device __user *userp,
+ struct binderfs_device *req)
+{
+ int minor, ret;
+ struct dentry *dentry, *root;
+ struct binder_device *device = NULL;
+ rust_binder_context ctx = NULL;
+ struct inode *inode = NULL;
+ struct super_block *sb = ref_inode->i_sb;
+ struct binderfs_info *info = sb->s_fs_info;
+#if defined(CONFIG_IPC_NS)
+ bool use_reserve = (info->ipc_ns == &init_ipc_ns);
+#else
+ bool use_reserve = true;
+#endif
+
+ /* Reserve new minor number for the new device. */
+ mutex_lock(&binderfs_minors_mutex);
+ if (++info->device_count <= info->mount_opts.max)
+ minor = ida_alloc_max(&binderfs_minors,
+ use_reserve ? BINDERFS_MAX_MINOR :
+ BINDERFS_MAX_MINOR_CAPPED,
+ GFP_KERNEL);
+ else
+ minor = -ENOSPC;
+ if (minor < 0) {
+ --info->device_count;
+ mutex_unlock(&binderfs_minors_mutex);
+ return minor;
+ }
+ mutex_unlock(&binderfs_minors_mutex);
+
+ ret = -ENOMEM;
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ goto err;
+
+ req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */
+
+ ctx = rust_binder_new_context(req->name);
+ if (!ctx)
+ goto err;
+
+ inode = new_inode(sb);
+ if (!inode)
+ goto err;
+
+ inode->i_ino = minor + INODE_OFFSET;
+ simple_inode_init_ts(inode);
+ init_special_inode(inode, S_IFCHR | 0600,
+ MKDEV(MAJOR(binderfs_dev), minor));
+ inode->i_fop = &rust_binder_fops;
+ inode->i_uid = info->root_uid;
+ inode->i_gid = info->root_gid;
+
+ req->major = MAJOR(binderfs_dev);
+ req->minor = minor;
+ device->ctx = ctx;
+ device->minor = minor;
+
+ if (userp && copy_to_user(userp, req, sizeof(*req))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ root = sb->s_root;
+ dentry = simple_start_creating(root, req->name);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto err;
+ }
+
+ inode->i_private = device;
+ d_make_persistent(dentry, inode);
+
+ fsnotify_create(root->d_inode, dentry);
+ simple_done_creating(dentry);
+
+ return 0;
+
+err:
+ kfree(device);
+ rust_binder_remove_context(ctx);
+ mutex_lock(&binderfs_minors_mutex);
+ --info->device_count;
+ ida_free(&binderfs_minors, minor);
+ mutex_unlock(&binderfs_minors_mutex);
+ iput(inode);
+
+ return ret;
+}
+
+/**
+ * binder_ctl_ioctl - handle binder device node allocation requests
+ *
+ * The request handler for the binder-control device. All requests operate on
+ * the binderfs mount the binder-control device resides in:
+ * - BINDER_CTL_ADD
+ * Allocate a new binder device.
+ *
+ * Return: %0 on success, negative errno on failure.
+ */
+static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -EINVAL;
+ struct inode *inode = file_inode(file);
+ struct binderfs_device __user *device = (struct binderfs_device __user *)arg;
+ struct binderfs_device device_req;
+
+ switch (cmd) {
+ case BINDER_CTL_ADD:
+ ret = copy_from_user(&device_req, device, sizeof(device_req));
+ if (ret) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = binderfs_binder_device_create(inode, device, &device_req);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void binderfs_evict_inode(struct inode *inode)
+{
+ struct binder_device *device = inode->i_private;
+ struct binderfs_info *info = BINDERFS_SB(inode->i_sb);
+
+ clear_inode(inode);
+
+ if (!S_ISCHR(inode->i_mode) || !device)
+ return;
+
+ mutex_lock(&binderfs_minors_mutex);
+ --info->device_count;
+ ida_free(&binderfs_minors, device->minor);
+ mutex_unlock(&binderfs_minors_mutex);
+
+ /* ctx is null for binder-control, but this function ignores null pointers */
+ rust_binder_remove_context(device->ctx);
+
+ kfree(device);
+}
+
+static int binderfs_fs_context_parse_param(struct fs_context *fc,
+ struct fs_parameter *param)
+{
+ int opt;
+ struct binderfs_mount_opts *ctx = fc->fs_private;
+ struct fs_parse_result result;
+
+ opt = fs_parse(fc, binderfs_fs_parameters, param, &result);
+ if (opt < 0)
+ return opt;
+
+ switch (opt) {
+ case Opt_max:
+ if (result.uint_32 > BINDERFS_MAX_MINOR)
+ return invalfc(fc, "Bad value for '%s'", param->key);
+
+ ctx->max = result.uint_32;
+ break;
+ case Opt_stats_mode:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ctx->stats_mode = result.uint_32;
+ break;
+ default:
+ return invalfc(fc, "Unsupported parameter '%s'", param->key);
+ }
+
+ return 0;
+}
+
+static int binderfs_fs_context_reconfigure(struct fs_context *fc)
+{
+ struct binderfs_mount_opts *ctx = fc->fs_private;
+ struct binderfs_info *info = BINDERFS_SB(fc->root->d_sb);
+
+ if (info->mount_opts.stats_mode != ctx->stats_mode)
+ return invalfc(fc, "Binderfs stats mode cannot be changed during a remount");
+
+ info->mount_opts.stats_mode = ctx->stats_mode;
+ info->mount_opts.max = ctx->max;
+ return 0;
+}
+
+static int binderfs_show_options(struct seq_file *seq, struct dentry *root)
+{
+ struct binderfs_info *info = BINDERFS_SB(root->d_sb);
+
+ if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
+ seq_printf(seq, ",max=%d", info->mount_opts.max);
+
+ switch (info->mount_opts.stats_mode) {
+ case binderfs_stats_mode_unset:
+ break;
+ case binderfs_stats_mode_global:
+ seq_puts(seq, ",stats=global");
+ break;
+ }
+
+ return 0;
+}
+
+static const struct super_operations binderfs_super_ops = {
+ .evict_inode = binderfs_evict_inode,
+ .show_options = binderfs_show_options,
+ .statfs = simple_statfs,
+};
+
+static inline bool is_binderfs_control_device(const struct dentry *dentry)
+{
+ struct binderfs_info *info = dentry->d_sb->s_fs_info;
+
+ return info->control_dentry == dentry;
+}
+
+static int binderfs_rename(struct mnt_idmap *idmap,
+ struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
+{
+ if (is_binderfs_control_device(old_dentry) ||
+ is_binderfs_control_device(new_dentry))
+ return -EPERM;
+
+ return simple_rename(idmap, old_dir, old_dentry, new_dir,
+ new_dentry, flags);
+}
+
+static int binderfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ if (is_binderfs_control_device(dentry))
+ return -EPERM;
+
+ return simple_unlink(dir, dentry);
+}
+
+static const struct file_operations binder_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = nonseekable_open,
+ .unlocked_ioctl = binder_ctl_ioctl,
+ .compat_ioctl = binder_ctl_ioctl,
+ .llseek = noop_llseek,
+};
+
+/**
+ * binderfs_binder_ctl_create - create a new binder-control device
+ * @sb: super block of the binderfs mount
+ *
+ * This function creates a new binder-control device node in the binderfs mount
+ * referred to by @sb.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int binderfs_binder_ctl_create(struct super_block *sb)
+{
+ int minor, ret;
+ struct dentry *dentry;
+ struct binder_device *device;
+ struct inode *inode = NULL;
+ struct dentry *root = sb->s_root;
+ struct binderfs_info *info = sb->s_fs_info;
+#if defined(CONFIG_IPC_NS)
+ bool use_reserve = (info->ipc_ns == &init_ipc_ns);
+#else
+ bool use_reserve = true;
+#endif
+
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ /* If we have already created a binder-control node, return. */
+ if (info->control_dentry) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = -ENOMEM;
+ inode = new_inode(sb);
+ if (!inode)
+ goto out;
+
+ /* Reserve a new minor number for the new device. */
+ mutex_lock(&binderfs_minors_mutex);
+ minor = ida_alloc_max(&binderfs_minors,
+ use_reserve ? BINDERFS_MAX_MINOR :
+ BINDERFS_MAX_MINOR_CAPPED,
+ GFP_KERNEL);
+ mutex_unlock(&binderfs_minors_mutex);
+ if (minor < 0) {
+ ret = minor;
+ goto out;
+ }
+
+ inode->i_ino = SECOND_INODE;
+ simple_inode_init_ts(inode);
+ init_special_inode(inode, S_IFCHR | 0600,
+ MKDEV(MAJOR(binderfs_dev), minor));
+ inode->i_fop = &binder_ctl_fops;
+ inode->i_uid = info->root_uid;
+ inode->i_gid = info->root_gid;
+
+ device->minor = minor;
+ device->ctx = NULL;
+
+ dentry = d_alloc_name(root, "binder-control");
+ if (!dentry)
+ goto out;
+
+ inode->i_private = device;
+ info->control_dentry = dentry;
+ d_add(dentry, inode);
+
+ return 0;
+
+out:
+ kfree(device);
+ iput(inode);
+
+ return ret;
+}
+
+static const struct inode_operations binderfs_dir_inode_operations = {
+ .lookup = simple_lookup,
+ .rename = binderfs_rename,
+ .unlink = binderfs_unlink,
+};
+
+static struct inode *binderfs_make_inode(struct super_block *sb, int mode)
+{
+ struct inode *ret;
+
+ ret = new_inode(sb);
+ if (ret) {
+ ret->i_ino = iunique(sb, BINDERFS_MAX_MINOR + INODE_OFFSET);
+ ret->i_mode = mode;
+ simple_inode_init_ts(ret);
+ }
+ return ret;
+}
+
+void rust_binderfs_remove_file(struct dentry *dentry)
+{
+ simple_recursive_removal(dentry, NULL);
+}
+
+static struct dentry *rust_binderfs_create_file(struct dentry *parent, const char *name,
+ const struct file_operations *fops,
+ void *data)
+{
+ struct dentry *dentry;
+ struct inode *new_inode;
+
+ new_inode = binderfs_make_inode(parent->d_sb, S_IFREG | 0444);
+ if (!new_inode)
+ return ERR_PTR(-ENOMEM);
+ new_inode->i_fop = fops;
+ new_inode->i_private = data;
+
+ dentry = simple_start_creating(parent, name);
+ if (IS_ERR(dentry)) {
+ iput(new_inode);
+ return dentry;
+ }
+
+ d_make_persistent(dentry, new_inode);
+ fsnotify_create(parent->d_inode, dentry);
+ simple_done_creating(dentry);
+ return dentry;
+}
+
+struct dentry *rust_binderfs_create_proc_file(struct inode *nodp, int pid)
+{
+ struct binderfs_info *info = nodp->i_sb->s_fs_info;
+ struct dentry *dir = info->proc_log_dir;
+ char strbuf[20 + 1];
+ void *data = (void *)(unsigned long) pid;
+
+ if (!dir)
+ return NULL;
+
+ snprintf(strbuf, sizeof(strbuf), "%u", pid);
+ return rust_binderfs_create_file(dir, strbuf, &rust_binder_proc_fops, data);
+}
+
+static struct dentry *binderfs_create_dir(struct dentry *parent,
+ const char *name)
+{
+ struct dentry *dentry;
+ struct inode *new_inode;
+
+ new_inode = binderfs_make_inode(parent->d_sb, S_IFDIR | 0755);
+ if (!new_inode)
+ return ERR_PTR(-ENOMEM);
+
+ new_inode->i_fop = &simple_dir_operations;
+ new_inode->i_op = &simple_dir_inode_operations;
+
+ dentry = simple_start_creating(parent, name);
+ if (IS_ERR(dentry)) {
+ iput(new_inode);
+ return dentry;
+ }
+
+ inc_nlink(parent->d_inode);
+ set_nlink(new_inode, 2);
+ d_make_persistent(dentry, new_inode);
+ fsnotify_mkdir(parent->d_inode, dentry);
+ simple_done_creating(dentry);
+ return dentry;
+}
+
+static int binder_features_show(struct seq_file *m, void *unused)
+{
+ bool *feature = m->private;
+
+ seq_printf(m, "%d\n", *feature);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(binder_features);
+
+static int init_binder_features(struct super_block *sb)
+{
+ struct dentry *dentry, *dir;
+
+ dir = binderfs_create_dir(sb->s_root, "features");
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ dentry = rust_binderfs_create_file(dir, "oneway_spam_detection",
+ &binder_features_fops,
+ &binder_features.oneway_spam_detection);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ dentry = rust_binderfs_create_file(dir, "extended_error",
+ &binder_features_fops,
+ &binder_features.extended_error);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ dentry = rust_binderfs_create_file(dir, "freeze_notification",
+ &binder_features_fops,
+ &binder_features.freeze_notification);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ return 0;
+}
+
+static int init_binder_logs(struct super_block *sb)
+{
+ struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir;
+ struct binderfs_info *info;
+ int ret = 0;
+
+ binder_logs_root_dir = binderfs_create_dir(sb->s_root,
+ "binder_logs");
+ if (IS_ERR(binder_logs_root_dir)) {
+ ret = PTR_ERR(binder_logs_root_dir);
+ goto out;
+ }
+
+ dentry = rust_binderfs_create_file(binder_logs_root_dir, "stats",
+ &rust_binder_stats_fops, NULL);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out;
+ }
+
+ dentry = rust_binderfs_create_file(binder_logs_root_dir, "state",
+ &rust_binder_state_fops, NULL);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out;
+ }
+
+ dentry = rust_binderfs_create_file(binder_logs_root_dir, "transactions",
+ &rust_binder_transactions_fops, NULL);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out;
+ }
+
+ proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc");
+ if (IS_ERR(proc_log_dir)) {
+ ret = PTR_ERR(proc_log_dir);
+ goto out;
+ }
+ info = sb->s_fs_info;
+ info->proc_log_dir = proc_log_dir;
+
+out:
+ return ret;
+}
+
+static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+ int ret;
+ struct binderfs_info *info;
+ struct binderfs_mount_opts *ctx = fc->fs_private;
+ struct inode *inode = NULL;
+ struct binderfs_device device_info = {};
+ const char *name;
+ size_t len;
+
+ sb->s_blocksize = PAGE_SIZE;
+ sb->s_blocksize_bits = PAGE_SHIFT;
+
+ /*
+ * The binderfs filesystem can be mounted by userns root in a
+ * non-initial userns. By default such mounts have the SB_I_NODEV flag
+ * set in s_iflags to prevent security issues where userns root can
+ * just create random device nodes via mknod() since it owns the
+ * filesystem mount. But binderfs does not allow to create any files
+ * including devices nodes. The only way to create binder devices nodes
+ * is through the binder-control device which userns root is explicitly
+ * allowed to do. So removing the SB_I_NODEV flag from s_iflags is both
+ * necessary and safe.
+ */
+ sb->s_iflags &= ~SB_I_NODEV;
+ sb->s_iflags |= SB_I_NOEXEC;
+ sb->s_magic = RUST_BINDERFS_SUPER_MAGIC;
+ sb->s_op = &binderfs_super_ops;
+ sb->s_time_gran = 1;
+
+ sb->s_fs_info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);
+ if (!sb->s_fs_info)
+ return -ENOMEM;
+ info = sb->s_fs_info;
+
+ info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns);
+
+ info->root_gid = make_kgid(sb->s_user_ns, 0);
+ if (!gid_valid(info->root_gid))
+ info->root_gid = GLOBAL_ROOT_GID;
+ info->root_uid = make_kuid(sb->s_user_ns, 0);
+ if (!uid_valid(info->root_uid))
+ info->root_uid = GLOBAL_ROOT_UID;
+ info->mount_opts.max = ctx->max;
+ info->mount_opts.stats_mode = ctx->stats_mode;
+
+ inode = new_inode(sb);
+ if (!inode)
+ return -ENOMEM;
+
+ inode->i_ino = FIRST_INODE;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_mode = S_IFDIR | 0755;
+ simple_inode_init_ts(inode);
+ inode->i_op = &binderfs_dir_inode_operations;
+ set_nlink(inode, 2);
+
+ sb->s_root = d_make_root(inode);
+ if (!sb->s_root)
+ return -ENOMEM;
+
+ ret = binderfs_binder_ctl_create(sb);
+ if (ret)
+ return ret;
+
+ name = rust_binder_devices_param;
+ for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
+ strscpy(device_info.name, name, len + 1);
+ ret = binderfs_binder_device_create(inode, NULL, &device_info);
+ if (ret)
+ return ret;
+ name += len;
+ if (*name == ',')
+ name++;
+ }
+
+ ret = init_binder_features(sb);
+ if (ret)
+ return ret;
+
+ if (info->mount_opts.stats_mode == binderfs_stats_mode_global)
+ return init_binder_logs(sb);
+
+ return 0;
+}
+
+static int binderfs_fs_context_get_tree(struct fs_context *fc)
+{
+ return get_tree_nodev(fc, binderfs_fill_super);
+}
+
+static void binderfs_fs_context_free(struct fs_context *fc)
+{
+ struct binderfs_mount_opts *ctx = fc->fs_private;
+
+ kfree(ctx);
+}
+
+static const struct fs_context_operations binderfs_fs_context_ops = {
+ .free = binderfs_fs_context_free,
+ .get_tree = binderfs_fs_context_get_tree,
+ .parse_param = binderfs_fs_context_parse_param,
+ .reconfigure = binderfs_fs_context_reconfigure,
+};
+
+static int binderfs_init_fs_context(struct fs_context *fc)
+{
+ struct binderfs_mount_opts *ctx;
+
+ ctx = kzalloc(sizeof(struct binderfs_mount_opts), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->max = BINDERFS_MAX_MINOR;
+ ctx->stats_mode = binderfs_stats_mode_unset;
+
+ fc->fs_private = ctx;
+ fc->ops = &binderfs_fs_context_ops;
+
+ return 0;
+}
+
+static void binderfs_kill_super(struct super_block *sb)
+{
+ struct binderfs_info *info = sb->s_fs_info;
+
+ /*
+ * During inode eviction struct binderfs_info is needed.
+ * So first wipe the super_block then free struct binderfs_info.
+ */
+ kill_anon_super(sb);
+
+ if (info && info->ipc_ns)
+ put_ipc_ns(info->ipc_ns);
+
+ kfree(info);
+}
+
+static struct file_system_type binder_fs_type = {
+ .name = "binder",
+ .init_fs_context = binderfs_init_fs_context,
+ .parameters = binderfs_fs_parameters,
+ .kill_sb = binderfs_kill_super,
+ .fs_flags = FS_USERNS_MOUNT,
+};
+
+int init_rust_binderfs(void)
+{
+ int ret;
+ const char *name;
+ size_t len;
+
+ /* Verify that the default binderfs device names are valid. */
+ name = rust_binder_devices_param;
+ for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
+ if (len > BINDERFS_MAX_NAME)
+ return -E2BIG;
+ name += len;
+ if (*name == ',')
+ name++;
+ }
+
+ /* Allocate new major number for binderfs. */
+ ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR,
+ "rust_binder");
+ if (ret)
+ return ret;
+
+ ret = register_filesystem(&binder_fs_type);
+ if (ret) {
+ unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR);
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/drivers/android/binder/stats.rs b/drivers/android/binder/stats.rs
new file mode 100644
index 000000000000..037002651941
--- /dev/null
+++ b/drivers/android/binder/stats.rs
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Keep track of statistics for binder_logs.
+
+use crate::defs::*;
+use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
+use kernel::{ioctl::_IOC_NR, seq_file::SeqFile, seq_print};
+
+const BC_COUNT: usize = _IOC_NR(BC_REPLY_SG) as usize + 1;
+const BR_COUNT: usize = _IOC_NR(BR_TRANSACTION_PENDING_FROZEN) as usize + 1;
+
+pub(crate) static GLOBAL_STATS: BinderStats = BinderStats::new();
+
+pub(crate) struct BinderStats {
+ bc: [AtomicU32; BC_COUNT],
+ br: [AtomicU32; BR_COUNT],
+}
+
+impl BinderStats {
+ pub(crate) const fn new() -> Self {
+ #[expect(clippy::declare_interior_mutable_const)]
+ const ZERO: AtomicU32 = AtomicU32::new(0);
+
+ Self {
+ bc: [ZERO; BC_COUNT],
+ br: [ZERO; BR_COUNT],
+ }
+ }
+
+ pub(crate) fn inc_bc(&self, bc: u32) {
+ let idx = _IOC_NR(bc) as usize;
+ if let Some(bc_ref) = self.bc.get(idx) {
+ bc_ref.fetch_add(1, Relaxed);
+ }
+ }
+
+ pub(crate) fn inc_br(&self, br: u32) {
+ let idx = _IOC_NR(br) as usize;
+ if let Some(br_ref) = self.br.get(idx) {
+ br_ref.fetch_add(1, Relaxed);
+ }
+ }
+
+ pub(crate) fn debug_print(&self, prefix: &str, m: &SeqFile) {
+ for (i, cnt) in self.bc.iter().enumerate() {
+ let cnt = cnt.load(Relaxed);
+ if cnt > 0 {
+ seq_print!(m, "{}{}: {}\n", prefix, command_string(i), cnt);
+ }
+ }
+ for (i, cnt) in self.br.iter().enumerate() {
+ let cnt = cnt.load(Relaxed);
+ if cnt > 0 {
+ seq_print!(m, "{}{}: {}\n", prefix, return_string(i), cnt);
+ }
+ }
+ }
+}
+
+mod strings {
+ use core::str::from_utf8_unchecked;
+ use kernel::str::{CStr, CStrExt as _};
+
+ extern "C" {
+ static binder_command_strings: [*const u8; super::BC_COUNT];
+ static binder_return_strings: [*const u8; super::BR_COUNT];
+ }
+
+ pub(super) fn command_string(i: usize) -> &'static str {
+ // SAFETY: Accessing `binder_command_strings` is always safe.
+ let c_str_ptr = unsafe { binder_command_strings[i] };
+ // SAFETY: The `binder_command_strings` array only contains nul-terminated strings.
+ let bytes = unsafe { CStr::from_char_ptr(c_str_ptr) }.to_bytes();
+ // SAFETY: The `binder_command_strings` array only contains strings with ascii-chars.
+ unsafe { from_utf8_unchecked(bytes) }
+ }
+
+ pub(super) fn return_string(i: usize) -> &'static str {
+ // SAFETY: Accessing `binder_return_strings` is always safe.
+ let c_str_ptr = unsafe { binder_return_strings[i] };
+ // SAFETY: The `binder_command_strings` array only contains nul-terminated strings.
+ let bytes = unsafe { CStr::from_char_ptr(c_str_ptr) }.to_bytes();
+ // SAFETY: The `binder_command_strings` array only contains strings with ascii-chars.
+ unsafe { from_utf8_unchecked(bytes) }
+ }
+}
+use strings::{command_string, return_string};
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
new file mode 100644
index 000000000000..1a8e6fdc0dc4
--- /dev/null
+++ b/drivers/android/binder/thread.rs
@@ -0,0 +1,1596 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! This module defines the `Thread` type, which represents a userspace thread that is using
+//! binder.
+//!
+//! The `Process` object stores all of the threads in an rb tree.
+
+use kernel::{
+ bindings,
+ fs::{File, LocalFile},
+ list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},
+ prelude::*,
+ security,
+ seq_file::SeqFile,
+ seq_print,
+ sync::poll::{PollCondVar, PollTable},
+ sync::{Arc, SpinLock},
+ task::Task,
+ types::ARef,
+ uaccess::UserSlice,
+ uapi,
+};
+
+use crate::{
+ allocation::{Allocation, AllocationView, BinderObject, BinderObjectRef, NewAllocation},
+ defs::*,
+ error::BinderResult,
+ process::{GetWorkOrRegister, Process},
+ ptr_align,
+ stats::GLOBAL_STATS,
+ transaction::Transaction,
+ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
+};
+
+use core::{
+ mem::size_of,
+ sync::atomic::{AtomicU32, Ordering},
+};
+
+/// Stores the layout of the scatter-gather entries. This is used during the `translate_objects`
+/// call and is discarded when it returns.
+struct ScatterGatherState {
+ /// A struct that tracks the amount of unused buffer space.
+ unused_buffer_space: UnusedBufferSpace,
+ /// Scatter-gather entries to copy.
+ sg_entries: KVec<ScatterGatherEntry>,
+ /// Indexes into `sg_entries` corresponding to the last binder_buffer_object that
+ /// was processed and all of its ancestors. The array is in sorted order.
+ ancestors: KVec<usize>,
+}
+
+/// This entry specifies an additional buffer that should be copied using the scatter-gather
+/// mechanism.
+struct ScatterGatherEntry {
+ /// The index in the offset array of the BINDER_TYPE_PTR that this entry originates from.
+ obj_index: usize,
+ /// Offset in target buffer.
+ offset: usize,
+ /// User address in source buffer.
+ sender_uaddr: usize,
+ /// Number of bytes to copy.
+ length: usize,
+ /// The minimum offset of the next fixup in this buffer.
+ fixup_min_offset: usize,
+ /// The offsets within this buffer that contain pointers which should be translated.
+ pointer_fixups: KVec<PointerFixupEntry>,
+}
+
+/// This entry specifies that a fixup should happen at `target_offset` of the
+/// buffer. If `skip` is nonzero, then the fixup is a `binder_fd_array_object`
+/// and is applied later. Otherwise if `skip` is zero, then the size of the
+/// fixup is `sizeof::<u64>()` and `pointer_value` is written to the buffer.
+struct PointerFixupEntry {
+ /// The number of bytes to skip, or zero for a `binder_buffer_object` fixup.
+ skip: usize,
+ /// The translated pointer to write when `skip` is zero.
+ pointer_value: u64,
+ /// The offset at which the value should be written. The offset is relative
+ /// to the original buffer.
+ target_offset: usize,
+}
+
+/// Return type of `apply_and_validate_fixup_in_parent`.
+struct ParentFixupInfo {
+ /// The index of the parent buffer in `sg_entries`.
+ parent_sg_index: usize,
+ /// The number of ancestors of the buffer.
+ ///
+ /// The buffer is considered an ancestor of itself, so this is always at
+ /// least one.
+ num_ancestors: usize,
+ /// New value of `fixup_min_offset` if this fixup is applied.
+ new_min_offset: usize,
+ /// The offset of the fixup in the target buffer.
+ target_offset: usize,
+}
+
+impl ScatterGatherState {
+ /// Called when a `binder_buffer_object` or `binder_fd_array_object` tries
+ /// to access a region in its parent buffer. These accesses have various
+ /// restrictions, which this method verifies.
+ ///
+ /// The `parent_offset` and `length` arguments describe the offset and
+ /// length of the access in the parent buffer.
+ ///
+ /// # Detailed restrictions
+ ///
+ /// Obviously the fixup must be in-bounds for the parent buffer.
+ ///
+ /// For safety reasons, we only allow fixups inside a buffer to happen
+ /// at increasing offsets; additionally, we only allow fixup on the last
+ /// buffer object that was verified, or one of its parents.
+ ///
+ /// Example of what is allowed:
+ ///
+ /// A
+ /// B (parent = A, offset = 0)
+ /// C (parent = A, offset = 16)
+ /// D (parent = C, offset = 0)
+ /// E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset)
+ ///
+ /// Examples of what is not allowed:
+ ///
+ /// Decreasing offsets within the same parent:
+ /// A
+ /// C (parent = A, offset = 16)
+ /// B (parent = A, offset = 0) // decreasing offset within A
+ ///
+ /// Arcerring to a parent that wasn't the last object or any of its parents:
+ /// A
+ /// B (parent = A, offset = 0)
+ /// C (parent = A, offset = 0)
+ /// C (parent = A, offset = 16)
+ /// D (parent = B, offset = 0) // B is not A or any of A's parents
+ fn validate_parent_fixup(
+ &self,
+ parent: usize,
+ parent_offset: usize,
+ length: usize,
+ ) -> Result<ParentFixupInfo> {
+ // Using `position` would also be correct, but `rposition` avoids
+ // quadratic running times.
+ let ancestors_i = self
+ .ancestors
+ .iter()
+ .copied()
+ .rposition(|sg_idx| self.sg_entries[sg_idx].obj_index == parent)
+ .ok_or(EINVAL)?;
+ let sg_idx = self.ancestors[ancestors_i];
+ let sg_entry = match self.sg_entries.get(sg_idx) {
+ Some(sg_entry) => sg_entry,
+ None => {
+ pr_err!(
+ "self.ancestors[{}] is {}, but self.sg_entries.len() is {}",
+ ancestors_i,
+ sg_idx,
+ self.sg_entries.len()
+ );
+ return Err(EINVAL);
+ }
+ };
+ if sg_entry.fixup_min_offset > parent_offset {
+ pr_warn!(
+ "validate_parent_fixup: fixup_min_offset={}, parent_offset={}",
+ sg_entry.fixup_min_offset,
+ parent_offset
+ );
+ return Err(EINVAL);
+ }
+ let new_min_offset = parent_offset.checked_add(length).ok_or(EINVAL)?;
+ if new_min_offset > sg_entry.length {
+ pr_warn!(
+ "validate_parent_fixup: new_min_offset={}, sg_entry.length={}",
+ new_min_offset,
+ sg_entry.length
+ );
+ return Err(EINVAL);
+ }
+ let target_offset = sg_entry.offset.checked_add(parent_offset).ok_or(EINVAL)?;
+ // The `ancestors_i + 1` operation can't overflow since the output of the addition is at
+ // most `self.ancestors.len()`, which also fits in a usize.
+ Ok(ParentFixupInfo {
+ parent_sg_index: sg_idx,
+ num_ancestors: ancestors_i + 1,
+ new_min_offset,
+ target_offset,
+ })
+ }
+}
+
+/// Keeps track of how much unused buffer space is left. The initial amount is the number of bytes
+/// requested by the user using the `buffers_size` field of `binder_transaction_data_sg`. Each time
+/// we translate an object of type `BINDER_TYPE_PTR`, some of the unused buffer space is consumed.
+struct UnusedBufferSpace {
+ /// The start of the remaining space.
+ offset: usize,
+ /// The end of the remaining space.
+ limit: usize,
+}
+impl UnusedBufferSpace {
+ /// Claim the next `size` bytes from the unused buffer space. The offset for the claimed chunk
+ /// into the buffer is returned.
+ fn claim_next(&mut self, size: usize) -> Result<usize> {
+ // We require every chunk to be aligned.
+ let size = ptr_align(size).ok_or(EINVAL)?;
+ let new_offset = self.offset.checked_add(size).ok_or(EINVAL)?;
+
+ if new_offset <= self.limit {
+ let offset = self.offset;
+ self.offset = new_offset;
+ Ok(offset)
+ } else {
+ Err(EINVAL)
+ }
+ }
+}
+
+pub(crate) enum PushWorkRes {
+ Ok,
+ FailedDead(DLArc<dyn DeliverToRead>),
+}
+
+impl PushWorkRes {
+ fn is_ok(&self) -> bool {
+ match self {
+ PushWorkRes::Ok => true,
+ PushWorkRes::FailedDead(_) => false,
+ }
+ }
+}
+
+/// The fields of `Thread` protected by the spinlock.
+struct InnerThread {
+ /// Determines the looper state of the thread. It is a bit-wise combination of the constants
+ /// prefixed with `LOOPER_`.
+ looper_flags: u32,
+
+ /// Determines whether the looper should return.
+ looper_need_return: bool,
+
+ /// Determines if thread is dead.
+ is_dead: bool,
+
+ /// Work item used to deliver error codes to the thread that started a transaction. Stored here
+ /// so that it can be reused.
+ reply_work: DArc<ThreadError>,
+
+ /// Work item used to deliver error codes to the current thread. Stored here so that it can be
+ /// reused.
+ return_work: DArc<ThreadError>,
+
+ /// Determines whether the work list below should be processed. When set to false, `work_list`
+ /// is treated as if it were empty.
+ process_work_list: bool,
+ /// List of work items to deliver to userspace.
+ work_list: List<DTRWrap<dyn DeliverToRead>>,
+ current_transaction: Option<DArc<Transaction>>,
+
+ /// Extended error information for this thread.
+ extended_error: ExtendedError,
+}
+
+const LOOPER_REGISTERED: u32 = 0x01;
+const LOOPER_ENTERED: u32 = 0x02;
+const LOOPER_EXITED: u32 = 0x04;
+const LOOPER_INVALID: u32 = 0x08;
+const LOOPER_WAITING: u32 = 0x10;
+const LOOPER_WAITING_PROC: u32 = 0x20;
+const LOOPER_POLL: u32 = 0x40;
+
+impl InnerThread {
+ fn new() -> Result<Self> {
+ fn next_err_id() -> u32 {
+ static EE_ID: AtomicU32 = AtomicU32::new(0);
+ EE_ID.fetch_add(1, Ordering::Relaxed)
+ }
+
+ Ok(Self {
+ looper_flags: 0,
+ looper_need_return: false,
+ is_dead: false,
+ process_work_list: false,
+ reply_work: ThreadError::try_new()?,
+ return_work: ThreadError::try_new()?,
+ work_list: List::new(),
+ current_transaction: None,
+ extended_error: ExtendedError::new(next_err_id(), BR_OK, 0),
+ })
+ }
+
+ fn pop_work(&mut self) -> Option<DLArc<dyn DeliverToRead>> {
+ if !self.process_work_list {
+ return None;
+ }
+
+ let ret = self.work_list.pop_front();
+ self.process_work_list = !self.work_list.is_empty();
+ ret
+ }
+
+ fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
+ if self.is_dead {
+ PushWorkRes::FailedDead(work)
+ } else {
+ self.work_list.push_back(work);
+ self.process_work_list = true;
+ PushWorkRes::Ok
+ }
+ }
+
+ fn push_reply_work(&mut self, code: u32) {
+ if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) {
+ work.set_error_code(code);
+ self.push_work(work);
+ } else {
+ pr_warn!("Thread reply work is already in use.");
+ }
+ }
+
+ fn push_return_work(&mut self, reply: u32) {
+ if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) {
+ work.set_error_code(reply);
+ self.push_work(work);
+ } else {
+ pr_warn!("Thread return work is already in use.");
+ }
+ }
+
+ /// Used to push work items that do not need to be processed immediately and can wait until the
+ /// thread gets another work item.
+ fn push_work_deferred(&mut self, work: DLArc<dyn DeliverToRead>) {
+ self.work_list.push_back(work);
+ }
+
+ /// Fetches the transaction this thread can reply to. If the thread has a pending transaction
+ /// (that it could respond to) but it has also issued a transaction, it must first wait for the
+ /// previously-issued transaction to complete.
+ ///
+ /// The `thread` parameter should be the thread containing this `ThreadInner`.
+ fn pop_transaction_to_reply(&mut self, thread: &Thread) -> Result<DArc<Transaction>> {
+ let transaction = self.current_transaction.take().ok_or(EINVAL)?;
+ if core::ptr::eq(thread, transaction.from.as_ref()) {
+ self.current_transaction = Some(transaction);
+ return Err(EINVAL);
+ }
+ // Find a new current transaction for this thread.
+ self.current_transaction = transaction.find_from(thread).cloned();
+ Ok(transaction)
+ }
+
+ fn pop_transaction_replied(&mut self, transaction: &DArc<Transaction>) -> bool {
+ match self.current_transaction.take() {
+ None => false,
+ Some(old) => {
+ if !Arc::ptr_eq(transaction, &old) {
+ self.current_transaction = Some(old);
+ return false;
+ }
+ self.current_transaction = old.clone_next();
+ true
+ }
+ }
+ }
+
+ fn looper_enter(&mut self) {
+ self.looper_flags |= LOOPER_ENTERED;
+ if self.looper_flags & LOOPER_REGISTERED != 0 {
+ self.looper_flags |= LOOPER_INVALID;
+ }
+ }
+
+ fn looper_register(&mut self, valid: bool) {
+ self.looper_flags |= LOOPER_REGISTERED;
+ if !valid || self.looper_flags & LOOPER_ENTERED != 0 {
+ self.looper_flags |= LOOPER_INVALID;
+ }
+ }
+
+ fn looper_exit(&mut self) {
+ self.looper_flags |= LOOPER_EXITED;
+ }
+
+ /// Determines whether the thread is part of a pool, i.e., if it is a looper.
+ fn is_looper(&self) -> bool {
+ self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0
+ }
+
+ /// Determines whether the thread should attempt to fetch work items from the process queue.
+ /// This is generally case when the thread is registered as a looper and not part of a
+ /// transaction stack. But if there is local work, we want to return to userspace before we
+ /// deliver any remote work.
+ fn should_use_process_work_queue(&self) -> bool {
+ self.current_transaction.is_none() && !self.process_work_list && self.is_looper()
+ }
+
+ fn poll(&mut self) -> u32 {
+ self.looper_flags |= LOOPER_POLL;
+ if self.process_work_list || self.looper_need_return {
+ bindings::POLLIN
+ } else {
+ 0
+ }
+ }
+}
+
+/// This represents a thread that's used with binder.
+#[pin_data]
+pub(crate) struct Thread {
+ pub(crate) id: i32,
+ pub(crate) process: Arc<Process>,
+ pub(crate) task: ARef<Task>,
+ #[pin]
+ inner: SpinLock<InnerThread>,
+ #[pin]
+ work_condvar: PollCondVar,
+ /// Used to insert this thread into the process' `ready_threads` list.
+ ///
+ /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.
+ #[pin]
+ links: ListLinks,
+ #[pin]
+ links_track: AtomicTracker,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for Thread {
+ tracked_by links_track: AtomicTracker;
+ }
+}
+kernel::list::impl_list_item! {
+ impl ListItem<0> for Thread {
+ using ListLinks { self.links };
+ }
+}
+
+impl Thread {
+ pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
+ let inner = InnerThread::new()?;
+
+ Arc::pin_init(
+ try_pin_init!(Thread {
+ id,
+ process,
+ task: ARef::from(&**kernel::current!()),
+ inner <- kernel::new_spinlock!(inner, "Thread::inner"),
+ work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),
+ links <- ListLinks::new(),
+ links_track <- AtomicTracker::new(),
+ }),
+ GFP_KERNEL,
+ )
+ }
+
+ #[inline(never)]
+ pub(crate) fn debug_print(self: &Arc<Self>, m: &SeqFile, print_all: bool) -> Result<()> {
+ let inner = self.inner.lock();
+
+ if print_all || inner.current_transaction.is_some() || !inner.work_list.is_empty() {
+ seq_print!(
+ m,
+ " thread {}: l {:02x} need_return {}\n",
+ self.id,
+ inner.looper_flags,
+ inner.looper_need_return,
+ );
+ }
+
+ let mut t_opt = inner.current_transaction.as_ref();
+ while let Some(t) = t_opt {
+ if Arc::ptr_eq(&t.from, self) {
+ t.debug_print_inner(m, " outgoing transaction ");
+ t_opt = t.from_parent.as_ref();
+ } else if Arc::ptr_eq(&t.to, &self.process) {
+ t.debug_print_inner(m, " incoming transaction ");
+ t_opt = t.find_from(self);
+ } else {
+ t.debug_print_inner(m, " bad transaction ");
+ t_opt = None;
+ }
+ }
+
+ for work in &inner.work_list {
+ work.debug_print(m, " ", " pending transaction ")?;
+ }
+ Ok(())
+ }
+
+ pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result {
+ let mut writer = data.writer();
+ let ee = self.inner.lock().extended_error;
+ writer.write(&ee)?;
+ Ok(())
+ }
+
+ pub(crate) fn set_current_transaction(&self, transaction: DArc<Transaction>) {
+ self.inner.lock().current_transaction = Some(transaction);
+ }
+
+ pub(crate) fn has_current_transaction(&self) -> bool {
+ self.inner.lock().current_transaction.is_some()
+ }
+
+ /// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is
+ /// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a
+ /// signal); otherwise it returns indicating that none is available.
+ fn get_work_local(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> {
+ {
+ let mut inner = self.inner.lock();
+ if inner.looper_need_return {
+ return Ok(inner.pop_work());
+ }
+ }
+
+ // Try once if the caller does not want to wait.
+ if !wait {
+ return self.inner.lock().pop_work().ok_or(EAGAIN).map(Some);
+ }
+
+ // Loop waiting only on the local queue (i.e., not registering with the process queue).
+ let mut inner = self.inner.lock();
+ loop {
+ if let Some(work) = inner.pop_work() {
+ return Ok(Some(work));
+ }
+
+ inner.looper_flags |= LOOPER_WAITING;
+ let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
+ inner.looper_flags &= !LOOPER_WAITING;
+
+ if signal_pending {
+ return Err(EINTR);
+ }
+ if inner.looper_need_return {
+ return Ok(None);
+ }
+ }
+ }
+
+ /// Attempts to fetch a work item from the thread-local queue, falling back to the process-wide
+ /// queue if none is available locally.
+ ///
+ /// This must only be called when the thread is not participating in a transaction chain. If it
+ /// is, the local version (`get_work_local`) should be used instead.
+ fn get_work(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> {
+ // Try to get work from the thread's work queue, using only a local lock.
+ {
+ let mut inner = self.inner.lock();
+ if let Some(work) = inner.pop_work() {
+ return Ok(Some(work));
+ }
+ if inner.looper_need_return {
+ drop(inner);
+ return Ok(self.process.get_work());
+ }
+ }
+
+ // If the caller doesn't want to wait, try to grab work from the process queue.
+ //
+ // We know nothing will have been queued directly to the thread queue because it is not in
+ // a transaction and it is not in the process' ready list.
+ if !wait {
+ return self.process.get_work().ok_or(EAGAIN).map(Some);
+ }
+
+ // Get work from the process queue. If none is available, atomically register as ready.
+ let reg = match self.process.get_work_or_register(self) {
+ GetWorkOrRegister::Work(work) => return Ok(Some(work)),
+ GetWorkOrRegister::Register(reg) => reg,
+ };
+
+ let mut inner = self.inner.lock();
+ loop {
+ if let Some(work) = inner.pop_work() {
+ return Ok(Some(work));
+ }
+
+ inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC;
+ let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
+ inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC);
+
+ if signal_pending || inner.looper_need_return {
+ // We need to return now. We need to pull the thread off the list of ready threads
+ // (by dropping `reg`), then check the state again after it's off the list to
+ // ensure that something was not queued in the meantime. If something has been
+ // queued, we just return it (instead of the error).
+ drop(inner);
+ drop(reg);
+
+ let res = match self.inner.lock().pop_work() {
+ Some(work) => Ok(Some(work)),
+ None if signal_pending => Err(EINTR),
+ None => Ok(None),
+ };
+ return res;
+ }
+ }
+ }
+
+ /// Push the provided work item to be delivered to user space via this thread.
+ ///
+ /// Returns whether the item was successfully pushed. This can only fail if the thread is dead.
+ pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
+ let sync = work.should_sync_wakeup();
+
+ let res = self.inner.lock().push_work(work);
+
+ if res.is_ok() {
+ if sync {
+ self.work_condvar.notify_sync();
+ } else {
+ self.work_condvar.notify_one();
+ }
+ }
+
+ res
+ }
+
+ /// Attempts to push to given work item to the thread if it's a looper thread (i.e., if it's
+ /// part of a thread pool) and is alive. Otherwise, push the work item to the process instead.
+ pub(crate) fn push_work_if_looper(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
+ let mut inner = self.inner.lock();
+ if inner.is_looper() && !inner.is_dead {
+ inner.push_work(work);
+ Ok(())
+ } else {
+ drop(inner);
+ self.process.push_work(work)
+ }
+ }
+
+ pub(crate) fn push_work_deferred(&self, work: DLArc<dyn DeliverToRead>) {
+ self.inner.lock().push_work_deferred(work);
+ }
+
+ pub(crate) fn push_return_work(&self, reply: u32) {
+ self.inner.lock().push_return_work(reply);
+ }
+
+ fn translate_object(
+ &self,
+ obj_index: usize,
+ offset: usize,
+ object: BinderObjectRef<'_>,
+ view: &mut AllocationView<'_>,
+ allow_fds: bool,
+ sg_state: &mut ScatterGatherState,
+ ) -> BinderResult {
+ match object {
+ BinderObjectRef::Binder(obj) => {
+ let strong = obj.hdr.type_ == BINDER_TYPE_BINDER;
+ // SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid
+ // representation.
+ let ptr = unsafe { obj.__bindgen_anon_1.binder } as _;
+ let cookie = obj.cookie as _;
+ let flags = obj.flags as _;
+ let node = self
+ .process
+ .as_arc_borrow()
+ .get_node(ptr, cookie, flags, strong, self)?;
+ security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?;
+ view.transfer_binder_object(offset, obj, strong, node)?;
+ }
+ BinderObjectRef::Handle(obj) => {
+ let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE;
+ // SAFETY: `handle` is a `u32`; any bit pattern is a valid representation.
+ let handle = unsafe { obj.__bindgen_anon_1.handle } as _;
+ let node = self.process.get_node_from_handle(handle, strong)?;
+ security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?;
+ view.transfer_binder_object(offset, obj, strong, node)?;
+ }
+ BinderObjectRef::Fd(obj) => {
+ if !allow_fds {
+ return Err(EPERM.into());
+ }
+
+ // SAFETY: `fd` is a `u32`; any bit pattern is a valid representation.
+ let fd = unsafe { obj.__bindgen_anon_1.fd };
+ let file = LocalFile::fget(fd)?;
+ // SAFETY: The binder driver never calls `fdget_pos` and this code runs from an
+ // ioctl, so there are no active calls to `fdget_pos` on this thread.
+ let file = unsafe { LocalFile::assume_no_fdget_pos(file) };
+ security::binder_transfer_file(
+ &self.process.cred,
+ &view.alloc.process.cred,
+ &file,
+ )?;
+
+ let mut obj_write = BinderFdObject::default();
+ obj_write.hdr.type_ = BINDER_TYPE_FD;
+ // This will be overwritten with the actual fd when the transaction is received.
+ obj_write.__bindgen_anon_1.fd = u32::MAX;
+ obj_write.cookie = obj.cookie;
+ view.write::<BinderFdObject>(offset, &obj_write)?;
+
+ const FD_FIELD_OFFSET: usize =
+ core::mem::offset_of!(uapi::binder_fd_object, __bindgen_anon_1.fd);
+
+ let field_offset = offset + FD_FIELD_OFFSET;
+
+ view.alloc.info_add_fd(file, field_offset, false)?;
+ }
+ BinderObjectRef::Ptr(obj) => {
+ let obj_length = obj.length.try_into().map_err(|_| EINVAL)?;
+ let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) {
+ Ok(alloc_offset) => alloc_offset,
+ Err(err) => {
+ pr_warn!(
+ "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})",
+ sg_state.unused_buffer_space.offset,
+ sg_state.unused_buffer_space.limit,
+ obj_length,
+ );
+ return Err(err.into());
+ }
+ };
+
+ let sg_state_idx = sg_state.sg_entries.len();
+ sg_state.sg_entries.push(
+ ScatterGatherEntry {
+ obj_index,
+ offset: alloc_offset,
+ sender_uaddr: obj.buffer as _,
+ length: obj_length,
+ pointer_fixups: KVec::new(),
+ fixup_min_offset: 0,
+ },
+ GFP_KERNEL,
+ )?;
+
+ let buffer_ptr_in_user_space = (view.alloc.ptr + alloc_offset) as u64;
+
+ if obj.flags & uapi::BINDER_BUFFER_FLAG_HAS_PARENT == 0 {
+ sg_state.ancestors.clear();
+ sg_state.ancestors.push(sg_state_idx, GFP_KERNEL)?;
+ } else {
+ // Another buffer also has a pointer to this buffer, and we need to fixup that
+ // pointer too.
+
+ let parent_index = usize::try_from(obj.parent).map_err(|_| EINVAL)?;
+ let parent_offset = usize::try_from(obj.parent_offset).map_err(|_| EINVAL)?;
+
+ let info = sg_state.validate_parent_fixup(
+ parent_index,
+ parent_offset,
+ size_of::<u64>(),
+ )?;
+
+ sg_state.ancestors.truncate(info.num_ancestors);
+ sg_state.ancestors.push(sg_state_idx, GFP_KERNEL)?;
+
+ let parent_entry = match sg_state.sg_entries.get_mut(info.parent_sg_index) {
+ Some(parent_entry) => parent_entry,
+ None => {
+ pr_err!(
+ "validate_parent_fixup returned index out of bounds for sg.entries"
+ );
+ return Err(EINVAL.into());
+ }
+ };
+
+ parent_entry.fixup_min_offset = info.new_min_offset;
+ parent_entry.pointer_fixups.push(
+ PointerFixupEntry {
+ skip: 0,
+ pointer_value: buffer_ptr_in_user_space,
+ target_offset: info.target_offset,
+ },
+ GFP_KERNEL,
+ )?;
+ }
+
+ let mut obj_write = BinderBufferObject::default();
+ obj_write.hdr.type_ = BINDER_TYPE_PTR;
+ obj_write.flags = obj.flags;
+ obj_write.buffer = buffer_ptr_in_user_space;
+ obj_write.length = obj.length;
+ obj_write.parent = obj.parent;
+ obj_write.parent_offset = obj.parent_offset;
+ view.write::<BinderBufferObject>(offset, &obj_write)?;
+ }
+ BinderObjectRef::Fda(obj) => {
+ if !allow_fds {
+ return Err(EPERM.into());
+ }
+ let parent_index = usize::try_from(obj.parent).map_err(|_| EINVAL)?;
+ let parent_offset = usize::try_from(obj.parent_offset).map_err(|_| EINVAL)?;
+ let num_fds = usize::try_from(obj.num_fds).map_err(|_| EINVAL)?;
+ let fds_len = num_fds.checked_mul(size_of::<u32>()).ok_or(EINVAL)?;
+
+ let info = sg_state.validate_parent_fixup(parent_index, parent_offset, fds_len)?;
+ view.alloc.info_add_fd_reserve(num_fds)?;
+
+ sg_state.ancestors.truncate(info.num_ancestors);
+ let parent_entry = match sg_state.sg_entries.get_mut(info.parent_sg_index) {
+ Some(parent_entry) => parent_entry,
+ None => {
+ pr_err!(
+ "validate_parent_fixup returned index out of bounds for sg.entries"
+ );
+ return Err(EINVAL.into());
+ }
+ };
+
+ parent_entry.fixup_min_offset = info.new_min_offset;
+ parent_entry
+ .pointer_fixups
+ .push(
+ PointerFixupEntry {
+ skip: fds_len,
+ pointer_value: 0,
+ target_offset: info.target_offset,
+ },
+ GFP_KERNEL,
+ )
+ .map_err(|_| ENOMEM)?;
+
+ let fda_uaddr = parent_entry
+ .sender_uaddr
+ .checked_add(parent_offset)
+ .ok_or(EINVAL)?;
+ let mut fda_bytes = KVec::new();
+ UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len)
+ .read_all(&mut fda_bytes, GFP_KERNEL)?;
+
+ if fds_len != fda_bytes.len() {
+ pr_err!("UserSlice::read_all returned wrong length in BINDER_TYPE_FDA");
+ return Err(EINVAL.into());
+ }
+
+ for i in (0..fds_len).step_by(size_of::<u32>()) {
+ let fd = {
+ let mut fd_bytes = [0u8; size_of::<u32>()];
+ fd_bytes.copy_from_slice(&fda_bytes[i..i + size_of::<u32>()]);
+ u32::from_ne_bytes(fd_bytes)
+ };
+
+ let file = LocalFile::fget(fd)?;
+ // SAFETY: The binder driver never calls `fdget_pos` and this code runs from an
+ // ioctl, so there are no active calls to `fdget_pos` on this thread.
+ let file = unsafe { LocalFile::assume_no_fdget_pos(file) };
+ security::binder_transfer_file(
+ &self.process.cred,
+ &view.alloc.process.cred,
+ &file,
+ )?;
+
+ // The `validate_parent_fixup` call ensuers that this addition will not
+ // overflow.
+ view.alloc.info_add_fd(file, info.target_offset + i, true)?;
+ }
+ drop(fda_bytes);
+
+ let mut obj_write = BinderFdArrayObject::default();
+ obj_write.hdr.type_ = BINDER_TYPE_FDA;
+ obj_write.num_fds = obj.num_fds;
+ obj_write.parent = obj.parent;
+ obj_write.parent_offset = obj.parent_offset;
+ view.write::<BinderFdArrayObject>(offset, &obj_write)?;
+ }
+ }
+ Ok(())
+ }
+
+ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) -> BinderResult {
+ for sg_entry in &mut sg_state.sg_entries {
+ let mut end_of_previous_fixup = sg_entry.offset;
+ let offset_end = sg_entry.offset.checked_add(sg_entry.length).ok_or(EINVAL)?;
+
+ let mut reader =
+ UserSlice::new(UserPtr::from_addr(sg_entry.sender_uaddr), sg_entry.length).reader();
+ for fixup in &mut sg_entry.pointer_fixups {
+ let fixup_len = if fixup.skip == 0 {
+ size_of::<u64>()
+ } else {
+ fixup.skip
+ };
+
+ let target_offset_end = fixup.target_offset.checked_add(fixup_len).ok_or(EINVAL)?;
+ if fixup.target_offset < end_of_previous_fixup || offset_end < target_offset_end {
+ pr_warn!(
+ "Fixups oob {} {} {} {}",
+ fixup.target_offset,
+ end_of_previous_fixup,
+ offset_end,
+ target_offset_end
+ );
+ return Err(EINVAL.into());
+ }
+
+ let copy_off = end_of_previous_fixup;
+ let copy_len = fixup.target_offset - end_of_previous_fixup;
+ if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
+ pr_warn!("Failed copying into alloc: {:?}", err);
+ return Err(err.into());
+ }
+ if fixup.skip == 0 {
+ let res = alloc.write::<u64>(fixup.target_offset, &fixup.pointer_value);
+ if let Err(err) = res {
+ pr_warn!("Failed copying ptr into alloc: {:?}", err);
+ return Err(err.into());
+ }
+ }
+ if let Err(err) = reader.skip(fixup_len) {
+ pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err);
+ return Err(err.into());
+ }
+ end_of_previous_fixup = target_offset_end;
+ }
+ let copy_off = end_of_previous_fixup;
+ let copy_len = offset_end - end_of_previous_fixup;
+ if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
+ pr_warn!("Failed copying remainder into alloc: {:?}", err);
+ return Err(err.into());
+ }
+ }
+ Ok(())
+ }
+
+ /// This method copies the payload of a transaction into the target process.
+ ///
+ /// The resulting payload will have several different components, which will be stored next to
+ /// each other in the allocation. Furthermore, various objects can be embedded in the payload,
+ /// and those objects have to be translated so that they make sense to the target transaction.
+ pub(crate) fn copy_transaction_data(
+ &self,
+ to_process: Arc<Process>,
+ tr: &BinderTransactionDataSg,
+ debug_id: usize,
+ allow_fds: bool,
+ txn_security_ctx_offset: Option<&mut usize>,
+ ) -> BinderResult<NewAllocation> {
+ let trd = &tr.transaction_data;
+ let is_oneway = trd.flags & TF_ONE_WAY != 0;
+ let mut secctx = if let Some(offset) = txn_security_ctx_offset {
+ let secid = self.process.cred.get_secid();
+ let ctx = match security::SecurityCtx::from_secid(secid) {
+ Ok(ctx) => ctx,
+ Err(err) => {
+ pr_warn!("Failed to get security ctx for id {}: {:?}", secid, err);
+ return Err(err.into());
+ }
+ };
+ Some((offset, ctx))
+ } else {
+ None
+ };
+
+ let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?;
+ let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?;
+ let offsets_size = trd.offsets_size.try_into().map_err(|_| EINVAL)?;
+ let aligned_offsets_size = ptr_align(offsets_size).ok_or(EINVAL)?;
+ let buffers_size = tr.buffers_size.try_into().map_err(|_| EINVAL)?;
+ let aligned_buffers_size = ptr_align(buffers_size).ok_or(EINVAL)?;
+ let aligned_secctx_size = match secctx.as_ref() {
+ Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?,
+ None => 0,
+ };
+
+ // This guarantees that at least `sizeof(usize)` bytes will be allocated.
+ let len = usize::max(
+ aligned_data_size
+ .checked_add(aligned_offsets_size)
+ .and_then(|sum| sum.checked_add(aligned_buffers_size))
+ .and_then(|sum| sum.checked_add(aligned_secctx_size))
+ .ok_or(ENOMEM)?,
+ size_of::<usize>(),
+ );
+ let secctx_off = aligned_data_size + aligned_offsets_size + aligned_buffers_size;
+ let mut alloc =
+ match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) {
+ Ok(alloc) => alloc,
+ Err(err) => {
+ pr_warn!(
+ "Failed to allocate buffer. len:{}, is_oneway:{}",
+ len,
+ is_oneway
+ );
+ return Err(err);
+ }
+ };
+
+ // SAFETY: This accesses a union field, but it's okay because the field's type is valid for
+ // all bit-patterns.
+ let trd_data_ptr = unsafe { &trd.data.ptr };
+ let mut buffer_reader =
+ UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader();
+ let mut end_of_previous_object = 0;
+ let mut sg_state = None;
+
+ // Copy offsets if there are any.
+ if offsets_size > 0 {
+ {
+ let mut reader =
+ UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size)
+ .reader();
+ alloc.copy_into(&mut reader, aligned_data_size, offsets_size)?;
+ }
+
+ let offsets_start = aligned_data_size;
+ let offsets_end = aligned_data_size + aligned_offsets_size;
+
+ // This state is used for BINDER_TYPE_PTR objects.
+ let sg_state = sg_state.insert(ScatterGatherState {
+ unused_buffer_space: UnusedBufferSpace {
+ offset: offsets_end,
+ limit: len,
+ },
+ sg_entries: KVec::new(),
+ ancestors: KVec::new(),
+ });
+
+ // Traverse the objects specified.
+ let mut view = AllocationView::new(&mut alloc, data_size);
+ for (index, index_offset) in (offsets_start..offsets_end)
+ .step_by(size_of::<usize>())
+ .enumerate()
+ {
+ let offset = view.alloc.read(index_offset)?;
+
+ if offset < end_of_previous_object {
+ pr_warn!("Got transaction with invalid offset.");
+ return Err(EINVAL.into());
+ }
+
+ // Copy data between two objects.
+ if end_of_previous_object < offset {
+ view.copy_into(
+ &mut buffer_reader,
+ end_of_previous_object,
+ offset - end_of_previous_object,
+ )?;
+ }
+
+ let mut object = BinderObject::read_from(&mut buffer_reader)?;
+
+ match self.translate_object(
+ index,
+ offset,
+ object.as_ref(),
+ &mut view,
+ allow_fds,
+ sg_state,
+ ) {
+ Ok(()) => end_of_previous_object = offset + object.size(),
+ Err(err) => {
+ pr_warn!("Error while translating object.");
+ return Err(err);
+ }
+ }
+
+ // Update the indexes containing objects to clean up.
+ let offset_after_object = index_offset + size_of::<usize>();
+ view.alloc
+ .set_info_offsets(offsets_start..offset_after_object);
+ }
+ }
+
+ // Copy remaining raw data.
+ alloc.copy_into(
+ &mut buffer_reader,
+ end_of_previous_object,
+ data_size - end_of_previous_object,
+ )?;
+
+ if let Some(sg_state) = sg_state.as_mut() {
+ if let Err(err) = self.apply_sg(&mut alloc, sg_state) {
+ pr_warn!("Failure in apply_sg: {:?}", err);
+ return Err(err);
+ }
+ }
+
+ if let Some((off_out, secctx)) = secctx.as_mut() {
+ if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) {
+ pr_warn!("Failed to write security context: {:?}", err);
+ return Err(err.into());
+ }
+ **off_out = secctx_off;
+ }
+ Ok(alloc)
+ }
+
+ fn unwind_transaction_stack(self: &Arc<Self>) {
+ let mut thread = self.clone();
+ while let Ok(transaction) = {
+ let mut inner = thread.inner.lock();
+ inner.pop_transaction_to_reply(thread.as_ref())
+ } {
+ let reply = Err(BR_DEAD_REPLY);
+ if !transaction.from.deliver_single_reply(reply, &transaction) {
+ break;
+ }
+
+ thread = transaction.from.clone();
+ }
+ }
+
+ pub(crate) fn deliver_reply(
+ &self,
+ reply: Result<DLArc<Transaction>, u32>,
+ transaction: &DArc<Transaction>,
+ ) {
+ if self.deliver_single_reply(reply, transaction) {
+ transaction.from.unwind_transaction_stack();
+ }
+ }
+
+ /// Delivers a reply to the thread that started a transaction. The reply can either be a
+ /// reply-transaction or an error code to be delivered instead.
+ ///
+ /// Returns whether the thread is dead. If it is, the caller is expected to unwind the
+ /// transaction stack by completing transactions for threads that are dead.
+ fn deliver_single_reply(
+ &self,
+ reply: Result<DLArc<Transaction>, u32>,
+ transaction: &DArc<Transaction>,
+ ) -> bool {
+ if let Ok(transaction) = &reply {
+ transaction.set_outstanding(&mut self.process.inner.lock());
+ }
+
+ {
+ let mut inner = self.inner.lock();
+ if !inner.pop_transaction_replied(transaction) {
+ return false;
+ }
+
+ if inner.is_dead {
+ return true;
+ }
+
+ match reply {
+ Ok(work) => {
+ inner.push_work(work);
+ }
+ Err(code) => inner.push_reply_work(code),
+ }
+ }
+
+ // Notify the thread now that we've released the inner lock.
+ self.work_condvar.notify_sync();
+ false
+ }
+
+ /// Determines if the given transaction is the current transaction for this thread.
+ fn is_current_transaction(&self, transaction: &DArc<Transaction>) -> bool {
+ let inner = self.inner.lock();
+ match &inner.current_transaction {
+ None => false,
+ Some(current) => Arc::ptr_eq(current, transaction),
+ }
+ }
+
+ /// Determines the current top of the transaction stack. It fails if the top is in another
+ /// thread (i.e., this thread belongs to a stack but it has called another thread). The top is
+ /// [`None`] if the thread is not currently participating in a transaction stack.
+ fn top_of_transaction_stack(&self) -> Result<Option<DArc<Transaction>>> {
+ let inner = self.inner.lock();
+ if let Some(cur) = &inner.current_transaction {
+ if core::ptr::eq(self, cur.from.as_ref()) {
+ pr_warn!("got new transaction with bad transaction stack");
+ return Err(EINVAL);
+ }
+ Ok(Some(cur.clone()))
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn transaction<T>(self: &Arc<Self>, tr: &BinderTransactionDataSg, inner: T)
+ where
+ T: FnOnce(&Arc<Self>, &BinderTransactionDataSg) -> BinderResult,
+ {
+ if let Err(err) = inner(self, tr) {
+ if err.should_pr_warn() {
+ let mut ee = self.inner.lock().extended_error;
+ ee.command = err.reply;
+ ee.param = err.as_errno();
+ pr_warn!(
+ "Transaction failed: {:?} my_pid:{}",
+ err,
+ self.process.pid_in_current_ns()
+ );
+ }
+
+ self.push_return_work(err.reply);
+ }
+ }
+
+ fn transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+ // SAFETY: Handle's type has no invalid bit patterns.
+ let handle = unsafe { tr.transaction_data.target.handle };
+ let node_ref = self.process.get_transaction_node(handle)?;
+ security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
+ // TODO: We need to ensure that there isn't a pending transaction in the work queue. How
+ // could this happen?
+ let top = self.top_of_transaction_stack()?;
+ let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
+ let completion = list_completion.clone_arc();
+ let transaction = Transaction::new(node_ref, top, self, tr)?;
+
+ // Check that the transaction stack hasn't changed while the lock was released, then update
+ // it with the new transaction.
+ {
+ let mut inner = self.inner.lock();
+ if !transaction.is_stacked_on(&inner.current_transaction) {
+ pr_warn!("Transaction stack changed during transaction!");
+ return Err(EINVAL.into());
+ }
+ inner.current_transaction = Some(transaction.clone_arc());
+ // We push the completion as a deferred work so that we wait for the reply before
+ // returning to userland.
+ inner.push_work_deferred(list_completion);
+ }
+
+ if let Err(e) = transaction.submit() {
+ completion.skip();
+ // Define `transaction` first to drop it after `inner`.
+ let transaction;
+ let mut inner = self.inner.lock();
+ transaction = inner.current_transaction.take().unwrap();
+ inner.current_transaction = transaction.clone_next();
+ Err(e)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn reply_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+ let orig = self.inner.lock().pop_transaction_to_reply(self)?;
+ if !orig.from.is_current_transaction(&orig) {
+ return Err(EINVAL.into());
+ }
+
+ // We need to complete the transaction even if we cannot complete building the reply.
+ let out = (|| -> BinderResult<_> {
+ let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
+ let process = orig.from.process.clone();
+ let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
+ let reply = Transaction::new_reply(self, process, tr, allow_fds)?;
+ self.inner.lock().push_work(completion);
+ orig.from.deliver_reply(Ok(reply), &orig);
+ Ok(())
+ })()
+ .map_err(|mut err| {
+ // At this point we only return `BR_TRANSACTION_COMPLETE` to the caller, and we must let
+ // the sender know that the transaction has completed (with an error in this case).
+ pr_warn!(
+ "Failure {:?} during reply - delivering BR_FAILED_REPLY to sender.",
+ err
+ );
+ let reply = Err(BR_FAILED_REPLY);
+ orig.from.deliver_reply(reply, &orig);
+ err.reply = BR_TRANSACTION_COMPLETE;
+ err
+ });
+
+ out
+ }
+
+ fn oneway_transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+ // SAFETY: The `handle` field is valid for all possible byte values, so reading from the
+ // union is okay.
+ let handle = unsafe { tr.transaction_data.target.handle };
+ let node_ref = self.process.get_transaction_node(handle)?;
+ security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
+ let transaction = Transaction::new(node_ref, None, self, tr)?;
+ let code = if self.process.is_oneway_spam_detection_enabled()
+ && transaction.oneway_spam_detected
+ {
+ BR_ONEWAY_SPAM_SUSPECT
+ } else {
+ BR_TRANSACTION_COMPLETE
+ };
+ let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
+ let completion = list_completion.clone_arc();
+ self.inner.lock().push_work(list_completion);
+ match transaction.submit() {
+ Ok(()) => Ok(()),
+ Err(err) => {
+ completion.skip();
+ Err(err)
+ }
+ }
+ }
+
+ fn write(self: &Arc<Self>, req: &mut BinderWriteRead) -> Result {
+ let write_start = req.write_buffer.wrapping_add(req.write_consumed);
+ let write_len = req.write_size.saturating_sub(req.write_consumed);
+ let mut reader =
+ UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader();
+
+ while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() {
+ let before = reader.len();
+ let cmd = reader.read::<u32>()?;
+ GLOBAL_STATS.inc_bc(cmd);
+ self.process.stats.inc_bc(cmd);
+ match cmd {
+ BC_TRANSACTION => {
+ let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
+ if tr.transaction_data.flags & TF_ONE_WAY != 0 {
+ self.transaction(&tr, Self::oneway_transaction_inner);
+ } else {
+ self.transaction(&tr, Self::transaction_inner);
+ }
+ }
+ BC_TRANSACTION_SG => {
+ let tr = reader.read::<BinderTransactionDataSg>()?;
+ if tr.transaction_data.flags & TF_ONE_WAY != 0 {
+ self.transaction(&tr, Self::oneway_transaction_inner);
+ } else {
+ self.transaction(&tr, Self::transaction_inner);
+ }
+ }
+ BC_REPLY => {
+ let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
+ self.transaction(&tr, Self::reply_inner)
+ }
+ BC_REPLY_SG => {
+ let tr = reader.read::<BinderTransactionDataSg>()?;
+ self.transaction(&tr, Self::reply_inner)
+ }
+ BC_FREE_BUFFER => {
+ let buffer = self.process.buffer_get(reader.read()?);
+ if let Some(buffer) = buffer {
+ if buffer.looper_need_return_on_free() {
+ self.inner.lock().looper_need_return = true;
+ }
+ drop(buffer);
+ }
+ }
+ BC_INCREFS => {
+ self.process
+ .as_arc_borrow()
+ .update_ref(reader.read()?, true, false)?
+ }
+ BC_ACQUIRE => {
+ self.process
+ .as_arc_borrow()
+ .update_ref(reader.read()?, true, true)?
+ }
+ BC_RELEASE => {
+ self.process
+ .as_arc_borrow()
+ .update_ref(reader.read()?, false, true)?
+ }
+ BC_DECREFS => {
+ self.process
+ .as_arc_borrow()
+ .update_ref(reader.read()?, false, false)?
+ }
+ BC_INCREFS_DONE => self.process.inc_ref_done(&mut reader, false)?,
+ BC_ACQUIRE_DONE => self.process.inc_ref_done(&mut reader, true)?,
+ BC_REQUEST_DEATH_NOTIFICATION => self.process.request_death(&mut reader, self)?,
+ BC_CLEAR_DEATH_NOTIFICATION => self.process.clear_death(&mut reader, self)?,
+ BC_DEAD_BINDER_DONE => self.process.dead_binder_done(reader.read()?, self),
+ BC_REGISTER_LOOPER => {
+ let valid = self.process.register_thread();
+ self.inner.lock().looper_register(valid);
+ }
+ BC_ENTER_LOOPER => self.inner.lock().looper_enter(),
+ BC_EXIT_LOOPER => self.inner.lock().looper_exit(),
+ BC_REQUEST_FREEZE_NOTIFICATION => self.process.request_freeze_notif(&mut reader)?,
+ BC_CLEAR_FREEZE_NOTIFICATION => self.process.clear_freeze_notif(&mut reader)?,
+ BC_FREEZE_NOTIFICATION_DONE => self.process.freeze_notif_done(&mut reader)?,
+
+ // Fail if given an unknown error code.
+ // BC_ATTEMPT_ACQUIRE and BC_ACQUIRE_RESULT are no longer supported.
+ _ => return Err(EINVAL),
+ }
+ // Update the number of write bytes consumed.
+ req.write_consumed += (before - reader.len()) as u64;
+ }
+
+ Ok(())
+ }
+
+ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
+ let read_start = req.read_buffer.wrapping_add(req.read_consumed);
+ let read_len = req.read_size.saturating_sub(req.read_consumed);
+ let mut writer = BinderReturnWriter::new(
+ UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(),
+ self,
+ );
+ let (in_pool, use_proc_queue) = {
+ let inner = self.inner.lock();
+ (inner.is_looper(), inner.should_use_process_work_queue())
+ };
+
+ let getter = if use_proc_queue {
+ Self::get_work
+ } else {
+ Self::get_work_local
+ };
+
+ // Reserve some room at the beginning of the read buffer so that we can send a
+ // BR_SPAWN_LOOPER if we need to.
+ let mut has_noop_placeholder = false;
+ if req.read_consumed == 0 {
+ if let Err(err) = writer.write_code(BR_NOOP) {
+ pr_warn!("Failure when writing BR_NOOP at beginning of buffer.");
+ return Err(err);
+ }
+ has_noop_placeholder = true;
+ }
+
+ // Loop doing work while there is room in the buffer.
+ let initial_len = writer.len();
+ while writer.len() >= size_of::<uapi::binder_transaction_data_secctx>() + 4 {
+ match getter(self, wait && initial_len == writer.len()) {
+ Ok(Some(work)) => match work.into_arc().do_work(self, &mut writer) {
+ Ok(true) => {}
+ Ok(false) => break,
+ Err(err) => {
+ return Err(err);
+ }
+ },
+ Ok(None) => {
+ break;
+ }
+ Err(err) => {
+ // Propagate the error if we haven't written anything else.
+ if err != EINTR && err != EAGAIN {
+ pr_warn!("Failure in work getter: {:?}", err);
+ }
+ if initial_len == writer.len() {
+ return Err(err);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ req.read_consumed += read_len - writer.len() as u64;
+
+ // Write BR_SPAWN_LOOPER if the process needs more threads for its pool.
+ if has_noop_placeholder && in_pool && self.process.needs_thread() {
+ let mut writer =
+ UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _)
+ .writer();
+ writer.write(&BR_SPAWN_LOOPER)?;
+ }
+ Ok(())
+ }
+
+ pub(crate) fn write_read(self: &Arc<Self>, data: UserSlice, wait: bool) -> Result {
+ let (mut reader, mut writer) = data.reader_writer();
+ let mut req = reader.read::<BinderWriteRead>()?;
+
+ // Go through the write buffer.
+ let mut ret = Ok(());
+ if req.write_size > 0 {
+ ret = self.write(&mut req);
+ if let Err(err) = ret {
+ pr_warn!(
+ "Write failure {:?} in pid:{}",
+ err,
+ self.process.pid_in_current_ns()
+ );
+ req.read_consumed = 0;
+ writer.write(&req)?;
+ self.inner.lock().looper_need_return = false;
+ return ret;
+ }
+ }
+
+ // Go through the work queue.
+ if req.read_size > 0 {
+ ret = self.read(&mut req, wait);
+ if ret.is_err() && ret != Err(EINTR) {
+ pr_warn!(
+ "Read failure {:?} in pid:{}",
+ ret,
+ self.process.pid_in_current_ns()
+ );
+ }
+ }
+
+ // Write the request back so that the consumed fields are visible to the caller.
+ writer.write(&req)?;
+
+ self.inner.lock().looper_need_return = false;
+
+ ret
+ }
+
+ pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) {
+ table.register_wait(file, &self.work_condvar);
+ let mut inner = self.inner.lock();
+ (inner.should_use_process_work_queue(), inner.poll())
+ }
+
+ /// Make the call to `get_work` or `get_work_local` return immediately, if any.
+ pub(crate) fn exit_looper(&self) {
+ let mut inner = self.inner.lock();
+ let should_notify = inner.looper_flags & LOOPER_WAITING != 0;
+ if should_notify {
+ inner.looper_need_return = true;
+ }
+ drop(inner);
+
+ if should_notify {
+ self.work_condvar.notify_one();
+ }
+ }
+
+ pub(crate) fn notify_if_poll_ready(&self, sync: bool) {
+ // Determine if we need to notify. This requires the lock.
+ let inner = self.inner.lock();
+ let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue();
+ drop(inner);
+
+ // Now that the lock is no longer held, notify the waiters if we have to.
+ if notify {
+ if sync {
+ self.work_condvar.notify_sync();
+ } else {
+ self.work_condvar.notify_one();
+ }
+ }
+ }
+
+ pub(crate) fn release(self: &Arc<Self>) {
+ self.inner.lock().is_dead = true;
+
+ //self.work_condvar.clear();
+ self.unwind_transaction_stack();
+
+ // Cancel all pending work items.
+ while let Ok(Some(work)) = self.get_work_local(false) {
+ work.into_arc().cancel();
+ }
+ }
+}
+
+#[pin_data]
+struct ThreadError {
+ error_code: AtomicU32,
+ #[pin]
+ links_track: AtomicTracker,
+}
+
+impl ThreadError {
+ fn try_new() -> Result<DArc<Self>> {
+ DTRWrap::arc_pin_init(pin_init!(Self {
+ error_code: AtomicU32::new(BR_OK),
+ links_track <- AtomicTracker::new(),
+ }))
+ .map(ListArc::into_arc)
+ }
+
+ fn set_error_code(&self, code: u32) {
+ self.error_code.store(code, Ordering::Relaxed);
+ }
+
+ fn is_unused(&self) -> bool {
+ self.error_code.load(Ordering::Relaxed) == BR_OK
+ }
+}
+
+impl DeliverToRead for ThreadError {
+ fn do_work(
+ self: DArc<Self>,
+ _thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ let code = self.error_code.load(Ordering::Relaxed);
+ self.error_code.store(BR_OK, Ordering::Relaxed);
+ writer.write_code(code)?;
+ Ok(true)
+ }
+
+ fn cancel(self: DArc<Self>) {}
+
+ fn should_sync_wakeup(&self) -> bool {
+ false
+ }
+
+ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
+ seq_print!(
+ m,
+ "{}transaction error: {}\n",
+ prefix,
+ self.error_code.load(Ordering::Relaxed)
+ );
+ Ok(())
+ }
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for ThreadError {
+ tracked_by links_track: AtomicTracker;
+ }
+}
diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs
new file mode 100644
index 000000000000..af0e4392805e
--- /dev/null
+++ b/drivers/android/binder/trace.rs
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use kernel::ffi::{c_uint, c_ulong};
+use kernel::tracepoint::declare_trace;
+
+declare_trace! {
+ unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong);
+}
+
+#[inline]
+pub(crate) fn trace_ioctl(cmd: u32, arg: usize) {
+ // SAFETY: Always safe to call.
+ unsafe { rust_binder_ioctl(cmd, arg as c_ulong) }
+}
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
new file mode 100644
index 000000000000..4bd3c0e417eb
--- /dev/null
+++ b/drivers/android/binder/transaction.rs
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use core::sync::atomic::{AtomicBool, Ordering};
+use kernel::{
+ prelude::*,
+ seq_file::SeqFile,
+ seq_print,
+ sync::{Arc, SpinLock},
+ task::Kuid,
+ time::{Instant, Monotonic},
+ types::ScopeGuard,
+};
+
+use crate::{
+ allocation::{Allocation, TranslatedFds},
+ defs::*,
+ error::{BinderError, BinderResult},
+ node::{Node, NodeRef},
+ process::{Process, ProcessInner},
+ ptr_align,
+ thread::{PushWorkRes, Thread},
+ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
+};
+
+#[pin_data(PinnedDrop)]
+pub(crate) struct Transaction {
+ pub(crate) debug_id: usize,
+ target_node: Option<DArc<Node>>,
+ pub(crate) from_parent: Option<DArc<Transaction>>,
+ pub(crate) from: Arc<Thread>,
+ pub(crate) to: Arc<Process>,
+ #[pin]
+ allocation: SpinLock<Option<Allocation>>,
+ is_outstanding: AtomicBool,
+ code: u32,
+ pub(crate) flags: u32,
+ data_size: usize,
+ offsets_size: usize,
+ data_address: usize,
+ sender_euid: Kuid,
+ txn_security_ctx_off: Option<usize>,
+ pub(crate) oneway_spam_detected: bool,
+ start_time: Instant<Monotonic>,
+}
+
+kernel::list::impl_list_arc_safe! {
+ impl ListArcSafe<0> for Transaction { untracked; }
+}
+
+impl Transaction {
+ pub(crate) fn new(
+ node_ref: NodeRef,
+ from_parent: Option<DArc<Transaction>>,
+ from: &Arc<Thread>,
+ tr: &BinderTransactionDataSg,
+ ) -> BinderResult<DLArc<Self>> {
+ let debug_id = super::next_debug_id();
+ let trd = &tr.transaction_data;
+ let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0;
+ let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0;
+ let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None };
+ let to = node_ref.node.owner.clone();
+ let mut alloc = match from.copy_transaction_data(
+ to.clone(),
+ tr,
+ debug_id,
+ allow_fds,
+ txn_security_ctx_off.as_mut(),
+ ) {
+ Ok(alloc) => alloc,
+ Err(err) => {
+ if !err.is_dead() {
+ pr_warn!("Failure in copy_transaction_data: {:?}", err);
+ }
+ return Err(err);
+ }
+ };
+ let oneway_spam_detected = alloc.oneway_spam_detected;
+ if trd.flags & TF_ONE_WAY != 0 {
+ if from_parent.is_some() {
+ pr_warn!("Oneway transaction should not be in a transaction stack.");
+ return Err(EINVAL.into());
+ }
+ alloc.set_info_oneway_node(node_ref.node.clone());
+ }
+ if trd.flags & TF_CLEAR_BUF != 0 {
+ alloc.set_info_clear_on_drop();
+ }
+ let target_node = node_ref.node.clone();
+ alloc.set_info_target_node(node_ref);
+ let data_address = alloc.ptr;
+
+ Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
+ debug_id,
+ target_node: Some(target_node),
+ from_parent,
+ sender_euid: from.process.task.euid(),
+ from: from.clone(),
+ to,
+ code: trd.code,
+ flags: trd.flags,
+ data_size: trd.data_size as _,
+ offsets_size: trd.offsets_size as _,
+ data_address,
+ allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
+ is_outstanding: AtomicBool::new(false),
+ txn_security_ctx_off,
+ oneway_spam_detected,
+ start_time: Instant::now(),
+ }))?)
+ }
+
+ pub(crate) fn new_reply(
+ from: &Arc<Thread>,
+ to: Arc<Process>,
+ tr: &BinderTransactionDataSg,
+ allow_fds: bool,
+ ) -> BinderResult<DLArc<Self>> {
+ let debug_id = super::next_debug_id();
+ let trd = &tr.transaction_data;
+ let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None)
+ {
+ Ok(alloc) => alloc,
+ Err(err) => {
+ pr_warn!("Failure in copy_transaction_data: {:?}", err);
+ return Err(err);
+ }
+ };
+ let oneway_spam_detected = alloc.oneway_spam_detected;
+ if trd.flags & TF_CLEAR_BUF != 0 {
+ alloc.set_info_clear_on_drop();
+ }
+ Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
+ debug_id,
+ target_node: None,
+ from_parent: None,
+ sender_euid: from.process.task.euid(),
+ from: from.clone(),
+ to,
+ code: trd.code,
+ flags: trd.flags,
+ data_size: trd.data_size as _,
+ offsets_size: trd.offsets_size as _,
+ data_address: alloc.ptr,
+ allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
+ is_outstanding: AtomicBool::new(false),
+ txn_security_ctx_off: None,
+ oneway_spam_detected,
+ start_time: Instant::now(),
+ }))?)
+ }
+
+ #[inline(never)]
+ pub(crate) fn debug_print_inner(&self, m: &SeqFile, prefix: &str) {
+ seq_print!(
+ m,
+ "{}{}: from {}:{} to {} code {:x} flags {:x} elapsed {}ms",
+ prefix,
+ self.debug_id,
+ self.from.process.task.pid(),
+ self.from.id,
+ self.to.task.pid(),
+ self.code,
+ self.flags,
+ self.start_time.elapsed().as_millis(),
+ );
+ if let Some(target_node) = &self.target_node {
+ seq_print!(m, " node {}", target_node.debug_id);
+ }
+ seq_print!(m, " size {}:{}\n", self.data_size, self.offsets_size);
+ }
+
+ /// Determines if the transaction is stacked on top of the given transaction.
+ pub(crate) fn is_stacked_on(&self, onext: &Option<DArc<Self>>) -> bool {
+ match (&self.from_parent, onext) {
+ (None, None) => true,
+ (Some(from_parent), Some(next)) => Arc::ptr_eq(from_parent, next),
+ _ => false,
+ }
+ }
+
+ /// Returns a pointer to the next transaction on the transaction stack, if there is one.
+ pub(crate) fn clone_next(&self) -> Option<DArc<Self>> {
+ Some(self.from_parent.as_ref()?.clone())
+ }
+
+ /// Searches in the transaction stack for a thread that belongs to the target process. This is
+ /// useful when finding a target for a new transaction: if the node belongs to a process that
+ /// is already part of the transaction stack, we reuse the thread.
+ fn find_target_thread(&self) -> Option<Arc<Thread>> {
+ let mut it = &self.from_parent;
+ while let Some(transaction) = it {
+ if Arc::ptr_eq(&transaction.from.process, &self.to) {
+ return Some(transaction.from.clone());
+ }
+ it = &transaction.from_parent;
+ }
+ None
+ }
+
+ /// Searches in the transaction stack for a transaction originating at the given thread.
+ pub(crate) fn find_from(&self, thread: &Thread) -> Option<&DArc<Transaction>> {
+ let mut it = &self.from_parent;
+ while let Some(transaction) = it {
+ if core::ptr::eq(thread, transaction.from.as_ref()) {
+ return Some(transaction);
+ }
+
+ it = &transaction.from_parent;
+ }
+ None
+ }
+
+ pub(crate) fn set_outstanding(&self, to_process: &mut ProcessInner) {
+ // No race because this method is only called once.
+ if !self.is_outstanding.load(Ordering::Relaxed) {
+ self.is_outstanding.store(true, Ordering::Relaxed);
+ to_process.add_outstanding_txn();
+ }
+ }
+
+ /// Decrement `outstanding_txns` in `to` if it hasn't already been decremented.
+ fn drop_outstanding_txn(&self) {
+ // No race because this is called at most twice, and one of the calls are in the
+ // destructor, which is guaranteed to not race with any other operations on the
+ // transaction. It also cannot race with `set_outstanding`, since submission happens
+ // before delivery.
+ if self.is_outstanding.load(Ordering::Relaxed) {
+ self.is_outstanding.store(false, Ordering::Relaxed);
+ self.to.drop_outstanding_txn();
+ }
+ }
+
+ /// Submits the transaction to a work queue. Uses a thread if there is one in the transaction
+ /// stack, otherwise uses the destination process.
+ ///
+ /// Not used for replies.
+ pub(crate) fn submit(self: DLArc<Self>) -> BinderResult {
+ // Defined before `process_inner` so that the destructor runs after releasing the lock.
+ let mut _t_outdated;
+
+ let oneway = self.flags & TF_ONE_WAY != 0;
+ let process = self.to.clone();
+ let mut process_inner = process.inner.lock();
+
+ self.set_outstanding(&mut process_inner);
+
+ if oneway {
+ if let Some(target_node) = self.target_node.clone() {
+ if process_inner.is_frozen.is_frozen() {
+ process_inner.async_recv = true;
+ if self.flags & TF_UPDATE_TXN != 0 {
+ if let Some(t_outdated) =
+ target_node.take_outdated_transaction(&self, &mut process_inner)
+ {
+ // Save the transaction to be dropped after locks are released.
+ _t_outdated = t_outdated;
+ }
+ }
+ }
+ match target_node.submit_oneway(self, &mut process_inner) {
+ Ok(()) => {}
+ Err((err, work)) => {
+ drop(process_inner);
+ // Drop work after releasing process lock.
+ drop(work);
+ return Err(err);
+ }
+ }
+
+ if process_inner.is_frozen.is_frozen() {
+ return Err(BinderError::new_frozen_oneway());
+ } else {
+ return Ok(());
+ }
+ } else {
+ pr_err!("Failed to submit oneway transaction to node.");
+ }
+ }
+
+ if process_inner.is_frozen.is_frozen() {
+ process_inner.sync_recv = true;
+ return Err(BinderError::new_frozen());
+ }
+
+ let res = if let Some(thread) = self.find_target_thread() {
+ match thread.push_work(self) {
+ PushWorkRes::Ok => Ok(()),
+ PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
+ }
+ } else {
+ process_inner.push_work(self)
+ };
+ drop(process_inner);
+
+ match res {
+ Ok(()) => Ok(()),
+ Err((err, work)) => {
+ // Drop work after releasing process lock.
+ drop(work);
+ Err(err)
+ }
+ }
+ }
+
+ /// Check whether one oneway transaction can supersede another.
+ pub(crate) fn can_replace(&self, old: &Transaction) -> bool {
+ if self.from.process.task.pid() != old.from.process.task.pid() {
+ return false;
+ }
+
+ if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) {
+ return false;
+ }
+
+ let target_node_match = match (self.target_node.as_ref(), old.target_node.as_ref()) {
+ (None, None) => true,
+ (Some(tn1), Some(tn2)) => Arc::ptr_eq(tn1, tn2),
+ _ => false,
+ };
+
+ self.code == old.code && self.flags == old.flags && target_node_match
+ }
+
+ fn prepare_file_list(&self) -> Result<TranslatedFds> {
+ let mut alloc = self.allocation.lock().take().ok_or(ESRCH)?;
+
+ match alloc.translate_fds() {
+ Ok(translated) => {
+ *self.allocation.lock() = Some(alloc);
+ Ok(translated)
+ }
+ Err(err) => {
+ // Free the allocation eagerly.
+ drop(alloc);
+ Err(err)
+ }
+ }
+ }
+}
+
+impl DeliverToRead for Transaction {
+ fn do_work(
+ self: DArc<Self>,
+ thread: &Thread,
+ writer: &mut BinderReturnWriter<'_>,
+ ) -> Result<bool> {
+ let send_failed_reply = ScopeGuard::new(|| {
+ if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ let reply = Err(BR_FAILED_REPLY);
+ self.from.deliver_reply(reply, &self);
+ }
+ self.drop_outstanding_txn();
+ });
+
+ let files = if let Ok(list) = self.prepare_file_list() {
+ list
+ } else {
+ // On failure to process the list, we send a reply back to the sender and ignore the
+ // transaction on the recipient.
+ return Ok(true);
+ };
+
+ let mut tr_sec = BinderTransactionDataSecctx::default();
+ let tr = tr_sec.tr_data();
+ if let Some(target_node) = &self.target_node {
+ let (ptr, cookie) = target_node.get_id();
+ tr.target.ptr = ptr as _;
+ tr.cookie = cookie as _;
+ };
+ tr.code = self.code;
+ tr.flags = self.flags;
+ tr.data_size = self.data_size as _;
+ tr.data.ptr.buffer = self.data_address as _;
+ tr.offsets_size = self.offsets_size as _;
+ if tr.offsets_size > 0 {
+ tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _;
+ }
+ tr.sender_euid = self.sender_euid.into_uid_in_current_ns();
+ tr.sender_pid = 0;
+ if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ // Not a reply and not one-way.
+ tr.sender_pid = self.from.process.pid_in_current_ns();
+ }
+ let code = if self.target_node.is_none() {
+ BR_REPLY
+ } else if self.txn_security_ctx_off.is_some() {
+ BR_TRANSACTION_SEC_CTX
+ } else {
+ BR_TRANSACTION
+ };
+
+ // Write the transaction code and data to the user buffer.
+ writer.write_code(code)?;
+ if let Some(off) = self.txn_security_ctx_off {
+ tr_sec.secctx = (self.data_address + off) as u64;
+ writer.write_payload(&tr_sec)?;
+ } else {
+ writer.write_payload(&*tr)?;
+ }
+
+ let mut alloc = self.allocation.lock().take().ok_or(ESRCH)?;
+
+ // Dismiss the completion of transaction with a failure. No failure paths are allowed from
+ // here on out.
+ send_failed_reply.dismiss();
+
+ // Commit files, and set FDs in FDA to be closed on buffer free.
+ let close_on_free = files.commit();
+ alloc.set_info_close_on_free(close_on_free);
+
+ // It is now the user's responsibility to clear the allocation.
+ alloc.keep_alive();
+
+ self.drop_outstanding_txn();
+
+ // When this is not a reply and not a oneway transaction, update `current_transaction`. If
+ // it's a reply, `current_transaction` has already been updated appropriately.
+ if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 {
+ thread.set_current_transaction(self);
+ }
+
+ Ok(false)
+ }
+
+ fn cancel(self: DArc<Self>) {
+ let allocation = self.allocation.lock().take();
+ drop(allocation);
+
+ // If this is not a reply or oneway transaction, then send a dead reply.
+ if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ let reply = Err(BR_DEAD_REPLY);
+ self.from.deliver_reply(reply, &self);
+ }
+
+ self.drop_outstanding_txn();
+ }
+
+ fn should_sync_wakeup(&self) -> bool {
+ self.flags & TF_ONE_WAY == 0
+ }
+
+ fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> {
+ self.debug_print_inner(m, tprefix);
+ Ok(())
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for Transaction {
+ fn drop(self: Pin<&mut Self>) {
+ self.drop_outstanding_txn();
+ }
+}
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 9b1778c00610..979c96b74cad 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -23,10 +23,11 @@
#include <linux/uaccess.h>
#include <linux/highmem.h>
#include <linux/sizes.h>
+#include <kunit/visibility.h>
#include "binder_alloc.h"
#include "binder_trace.h"
-struct list_lru binder_alloc_lru;
+static struct list_lru binder_freelist;
static DEFINE_MUTEX(binder_alloc_mmap_lock);
@@ -57,13 +58,14 @@ static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer)
return list_entry(buffer->entry.prev, struct binder_buffer, entry);
}
-static size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
- struct binder_buffer *buffer)
+VISIBLE_IF_KUNIT size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
{
if (list_is_last(&buffer->entry, &alloc->buffers))
- return alloc->buffer + alloc->buffer_size - buffer->user_data;
+ return alloc->vm_start + alloc->buffer_size - buffer->user_data;
return binder_buffer_next(buffer)->user_data - buffer->user_data;
}
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_buffer_size);
static void binder_insert_free_buffer(struct binder_alloc *alloc,
struct binder_buffer *new_buffer)
@@ -125,23 +127,20 @@ static void binder_insert_allocated_buffer_locked(
static struct binder_buffer *binder_alloc_prepare_to_free_locked(
struct binder_alloc *alloc,
- uintptr_t user_ptr)
+ unsigned long user_ptr)
{
struct rb_node *n = alloc->allocated_buffers.rb_node;
struct binder_buffer *buffer;
- void __user *uptr;
-
- uptr = (void __user *)user_ptr;
while (n) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
BUG_ON(buffer->free);
- if (uptr < buffer->user_data)
+ if (user_ptr < buffer->user_data) {
n = n->rb_left;
- else if (uptr > buffer->user_data)
+ } else if (user_ptr > buffer->user_data) {
n = n->rb_right;
- else {
+ } else {
/*
* Guard against user threads attempting to
* free the buffer when in use by kernel or
@@ -168,180 +167,299 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked(
* Return: Pointer to buffer or NULL
*/
struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
- uintptr_t user_ptr)
+ unsigned long user_ptr)
{
- struct binder_buffer *buffer;
+ guard(mutex)(&alloc->mutex);
+ return binder_alloc_prepare_to_free_locked(alloc, user_ptr);
+}
- mutex_lock(&alloc->mutex);
- buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);
- mutex_unlock(&alloc->mutex);
- return buffer;
+static inline void
+binder_set_installed_page(struct binder_alloc *alloc,
+ unsigned long index,
+ struct page *page)
+{
+ /* Pairs with acquire in binder_get_installed_page() */
+ smp_store_release(&alloc->pages[index], page);
}
-static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
- void __user *start, void __user *end)
+static inline struct page *
+binder_get_installed_page(struct binder_alloc *alloc, unsigned long index)
{
- void __user *page_addr;
- unsigned long user_page_addr;
- struct binder_lru_page *page;
- struct vm_area_struct *vma = NULL;
- struct mm_struct *mm = NULL;
- bool need_mm = false;
+ /* Pairs with release in binder_set_installed_page() */
+ return smp_load_acquire(&alloc->pages[index]);
+}
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: %s pages %pK-%pK\n", alloc->pid,
- allocate ? "allocate" : "free", start, end);
+static void binder_lru_freelist_add(struct binder_alloc *alloc,
+ unsigned long start, unsigned long end)
+{
+ unsigned long page_addr;
+ struct page *page;
- if (end <= start)
- return 0;
+ trace_binder_update_page_range(alloc, false, start, end);
+
+ for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
+ size_t index;
+ int ret;
- trace_binder_update_page_range(alloc, allocate, start, end);
+ index = (page_addr - alloc->vm_start) / PAGE_SIZE;
+ page = binder_get_installed_page(alloc, index);
+ if (!page)
+ continue;
- if (allocate == 0)
- goto free_range;
+ trace_binder_free_lru_start(alloc, index);
- for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
- page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
- if (!page->page_ptr) {
- need_mm = true;
- break;
- }
+ ret = list_lru_add(alloc->freelist,
+ page_to_lru(page),
+ page_to_nid(page),
+ NULL);
+ WARN_ON(!ret);
+
+ trace_binder_free_lru_end(alloc, index);
}
+}
+
+static inline
+void binder_alloc_set_mapped(struct binder_alloc *alloc, bool state)
+{
+ /* pairs with smp_load_acquire in binder_alloc_is_mapped() */
+ smp_store_release(&alloc->mapped, state);
+}
+
+static inline bool binder_alloc_is_mapped(struct binder_alloc *alloc)
+{
+ /* pairs with smp_store_release in binder_alloc_set_mapped() */
+ return smp_load_acquire(&alloc->mapped);
+}
+
+static struct page *binder_page_lookup(struct binder_alloc *alloc,
+ unsigned long addr)
+{
+ struct mm_struct *mm = alloc->mm;
+ struct page *page;
+ long npages = 0;
+
+ /*
+ * Find an existing page in the remote mm. If missing,
+ * don't attempt to fault-in just propagate an error.
+ */
+ mmap_read_lock(mm);
+ if (binder_alloc_is_mapped(alloc))
+ npages = get_user_pages_remote(mm, addr, 1, FOLL_NOFAULT,
+ &page, NULL);
+ mmap_read_unlock(mm);
+
+ return npages > 0 ? page : NULL;
+}
- if (need_mm && mmget_not_zero(alloc->vma_vm_mm))
- mm = alloc->vma_vm_mm;
+static int binder_page_insert(struct binder_alloc *alloc,
+ unsigned long addr,
+ struct page *page)
+{
+ struct mm_struct *mm = alloc->mm;
+ struct vm_area_struct *vma;
+ int ret = -ESRCH;
- if (mm) {
- mmap_read_lock(mm);
- vma = vma_lookup(mm, alloc->vma_addr);
+ /* attempt per-vma lock first */
+ vma = lock_vma_under_rcu(mm, addr);
+ if (vma) {
+ if (binder_alloc_is_mapped(alloc))
+ ret = vm_insert_page(vma, addr, page);
+ vma_end_read(vma);
+ return ret;
}
- if (!vma && need_mm) {
- binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
- "%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
- alloc->pid);
- goto err_no_vma;
+ /* fall back to mmap_lock */
+ mmap_read_lock(mm);
+ vma = vma_lookup(mm, addr);
+ if (vma && binder_alloc_is_mapped(alloc))
+ ret = vm_insert_page(vma, addr, page);
+ mmap_read_unlock(mm);
+
+ return ret;
+}
+
+static struct page *binder_page_alloc(struct binder_alloc *alloc,
+ unsigned long index)
+{
+ struct binder_shrinker_mdata *mdata;
+ struct page *page;
+
+ page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+ if (!page)
+ return NULL;
+
+ /* allocate and install shrinker metadata under page->private */
+ mdata = kzalloc(sizeof(*mdata), GFP_KERNEL);
+ if (!mdata) {
+ __free_page(page);
+ return NULL;
}
- for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
- int ret;
- bool on_lru;
- size_t index;
+ mdata->alloc = alloc;
+ mdata->page_index = index;
+ INIT_LIST_HEAD(&mdata->lru);
+ set_page_private(page, (unsigned long)mdata);
- index = (page_addr - alloc->buffer) / PAGE_SIZE;
- page = &alloc->pages[index];
+ return page;
+}
- if (page->page_ptr) {
- trace_binder_alloc_lru_start(alloc, index);
+static void binder_free_page(struct page *page)
+{
+ kfree((struct binder_shrinker_mdata *)page_private(page));
+ __free_page(page);
+}
- on_lru = list_lru_del(&binder_alloc_lru, &page->lru);
- WARN_ON(!on_lru);
+static int binder_install_single_page(struct binder_alloc *alloc,
+ unsigned long index,
+ unsigned long addr)
+{
+ struct page *page;
+ int ret;
- trace_binder_alloc_lru_end(alloc, index);
- continue;
+ if (!mmget_not_zero(alloc->mm))
+ return -ESRCH;
+
+ page = binder_page_alloc(alloc, index);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = binder_page_insert(alloc, addr, page);
+ switch (ret) {
+ case -EBUSY:
+ /*
+ * EBUSY is ok. Someone installed the pte first but the
+ * alloc->pages[index] has not been updated yet. Discard
+ * our page and look up the one already installed.
+ */
+ ret = 0;
+ binder_free_page(page);
+ page = binder_page_lookup(alloc, addr);
+ if (!page) {
+ pr_err("%d: failed to find page at offset %lx\n",
+ alloc->pid, addr - alloc->vm_start);
+ ret = -ESRCH;
+ break;
}
+ fallthrough;
+ case 0:
+ /* Mark page installation complete and safe to use */
+ binder_set_installed_page(alloc, index, page);
+ break;
+ default:
+ binder_free_page(page);
+ pr_err("%d: %s failed to insert page at offset %lx with %d\n",
+ alloc->pid, __func__, addr - alloc->vm_start, ret);
+ break;
+ }
+out:
+ mmput_async(alloc->mm);
+ return ret;
+}
- if (WARN_ON(!vma))
- goto err_page_ptr_cleared;
+static int binder_install_buffer_pages(struct binder_alloc *alloc,
+ struct binder_buffer *buffer,
+ size_t size)
+{
+ unsigned long start, final;
+ unsigned long page_addr;
+
+ start = buffer->user_data & PAGE_MASK;
+ final = PAGE_ALIGN(buffer->user_data + size);
+
+ for (page_addr = start; page_addr < final; page_addr += PAGE_SIZE) {
+ unsigned long index;
+ int ret;
+
+ index = (page_addr - alloc->vm_start) / PAGE_SIZE;
+ if (binder_get_installed_page(alloc, index))
+ continue;
trace_binder_alloc_page_start(alloc, index);
- page->page_ptr = alloc_page(GFP_KERNEL |
- __GFP_HIGHMEM |
- __GFP_ZERO);
- if (!page->page_ptr) {
- pr_err("%d: binder_alloc_buf failed for page at %pK\n",
- alloc->pid, page_addr);
- goto err_alloc_page_failed;
- }
- page->alloc = alloc;
- INIT_LIST_HEAD(&page->lru);
-
- user_page_addr = (uintptr_t)page_addr;
- ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr);
- if (ret) {
- pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
- alloc->pid, user_page_addr);
- goto err_vm_insert_page_failed;
- }
- if (index + 1 > alloc->pages_high)
- alloc->pages_high = index + 1;
+ ret = binder_install_single_page(alloc, index, page_addr);
+ if (ret)
+ return ret;
trace_binder_alloc_page_end(alloc, index);
}
- if (mm) {
- mmap_read_unlock(mm);
- mmput(mm);
- }
+
return 0;
+}
-free_range:
- for (page_addr = end - PAGE_SIZE; 1; page_addr -= PAGE_SIZE) {
- bool ret;
- size_t index;
+/* The range of pages should exclude those shared with other buffers */
+static void binder_lru_freelist_del(struct binder_alloc *alloc,
+ unsigned long start, unsigned long end)
+{
+ unsigned long page_addr;
+ struct page *page;
- index = (page_addr - alloc->buffer) / PAGE_SIZE;
- page = &alloc->pages[index];
+ trace_binder_update_page_range(alloc, true, start, end);
- trace_binder_free_lru_start(alloc, index);
+ for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
+ unsigned long index;
+ bool on_lru;
- ret = list_lru_add(&binder_alloc_lru, &page->lru);
- WARN_ON(!ret);
+ index = (page_addr - alloc->vm_start) / PAGE_SIZE;
+ page = binder_get_installed_page(alloc, index);
- trace_binder_free_lru_end(alloc, index);
- if (page_addr == start)
- break;
- continue;
-
-err_vm_insert_page_failed:
- __free_page(page->page_ptr);
- page->page_ptr = NULL;
-err_alloc_page_failed:
-err_page_ptr_cleared:
- if (page_addr == start)
- break;
- }
-err_no_vma:
- if (mm) {
- mmap_read_unlock(mm);
- mmput(mm);
- }
- return vma ? -ENOMEM : -ESRCH;
-}
+ if (page) {
+ trace_binder_alloc_lru_start(alloc, index);
+ on_lru = list_lru_del(alloc->freelist,
+ page_to_lru(page),
+ page_to_nid(page),
+ NULL);
+ WARN_ON(!on_lru);
-static inline void binder_alloc_set_vma(struct binder_alloc *alloc,
- struct vm_area_struct *vma)
-{
- unsigned long vm_start = 0;
+ trace_binder_alloc_lru_end(alloc, index);
+ continue;
+ }
- /*
- * Allow clearing the vma with holding just the read lock to allow
- * munmapping downgrade of the write lock before freeing and closing the
- * file using binder_alloc_vma_close().
- */
- if (vma) {
- vm_start = vma->vm_start;
- mmap_assert_write_locked(alloc->vma_vm_mm);
- } else {
- mmap_assert_locked(alloc->vma_vm_mm);
+ if (index + 1 > alloc->pages_high)
+ alloc->pages_high = index + 1;
}
-
- alloc->vma_addr = vm_start;
}
-static inline struct vm_area_struct *binder_alloc_get_vma(
- struct binder_alloc *alloc)
+static void debug_no_space_locked(struct binder_alloc *alloc)
{
- struct vm_area_struct *vma = NULL;
+ size_t largest_alloc_size = 0;
+ struct binder_buffer *buffer;
+ size_t allocated_buffers = 0;
+ size_t largest_free_size = 0;
+ size_t total_alloc_size = 0;
+ size_t total_free_size = 0;
+ size_t free_buffers = 0;
+ size_t buffer_size;
+ struct rb_node *n;
+
+ for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+ allocated_buffers++;
+ total_alloc_size += buffer_size;
+ if (buffer_size > largest_alloc_size)
+ largest_alloc_size = buffer_size;
+ }
- if (alloc->vma_addr)
- vma = vma_lookup(alloc->vma_vm_mm, alloc->vma_addr);
+ for (n = rb_first(&alloc->free_buffers); n; n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ buffer_size = binder_alloc_buffer_size(alloc, buffer);
+ free_buffers++;
+ total_free_size += buffer_size;
+ if (buffer_size > largest_free_size)
+ largest_free_size = buffer_size;
+ }
- return vma;
+ binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
+ "allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n",
+ total_alloc_size, allocated_buffers,
+ largest_alloc_size, total_free_size,
+ free_buffers, largest_free_size);
}
-static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
+static bool debug_low_async_space_locked(struct binder_alloc *alloc)
{
/*
* Find the amount and size of buffers allocated by the current caller;
@@ -350,10 +468,20 @@ static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
* and at some point we'll catch them in the act. This is more efficient
* than keeping a map per pid.
*/
- struct rb_node *n;
struct binder_buffer *buffer;
size_t total_alloc_size = 0;
+ int pid = current->tgid;
size_t num_buffers = 0;
+ struct rb_node *n;
+
+ /*
+ * Only start detecting spammers once we have less than 20% of async
+ * space left (which is less than 10% of total buffer size).
+ */
+ if (alloc->free_async_space >= alloc->buffer_size / 10) {
+ alloc->oneway_spam_detected = false;
+ return false;
+ }
for (n = rb_first(&alloc->allocated_buffers); n != NULL;
n = rb_next(n)) {
@@ -362,8 +490,7 @@ static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
continue;
if (!buffer->async_transaction)
continue;
- total_alloc_size += binder_alloc_buffer_size(alloc, buffer)
- + sizeof(struct binder_buffer);
+ total_alloc_size += binder_alloc_buffer_size(alloc, buffer);
num_buffers++;
}
@@ -384,60 +511,28 @@ static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
return false;
}
+/* Callers preallocate @new_buffer, it is freed by this function if unused */
static struct binder_buffer *binder_alloc_new_buf_locked(
struct binder_alloc *alloc,
- size_t data_size,
- size_t offsets_size,
- size_t extra_buffers_size,
- int is_async,
- int pid)
+ struct binder_buffer *new_buffer,
+ size_t size,
+ int is_async)
{
struct rb_node *n = alloc->free_buffers.rb_node;
+ struct rb_node *best_fit = NULL;
struct binder_buffer *buffer;
+ unsigned long next_used_page;
+ unsigned long curr_last_page;
size_t buffer_size;
- struct rb_node *best_fit = NULL;
- void __user *has_page_addr;
- void __user *end_page_addr;
- size_t size, data_offsets_size;
- int ret;
- mmap_read_lock(alloc->vma_vm_mm);
- if (!binder_alloc_get_vma(alloc)) {
- mmap_read_unlock(alloc->vma_vm_mm);
- binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
- "%d: binder_alloc_buf, no vma\n",
- alloc->pid);
- return ERR_PTR(-ESRCH);
- }
- mmap_read_unlock(alloc->vma_vm_mm);
-
- data_offsets_size = ALIGN(data_size, sizeof(void *)) +
- ALIGN(offsets_size, sizeof(void *));
-
- if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: got transaction with invalid size %zd-%zd\n",
- alloc->pid, data_size, offsets_size);
- return ERR_PTR(-EINVAL);
- }
- size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
- if (size < data_offsets_size || size < extra_buffers_size) {
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: got transaction with invalid extra_buffers_size %zd\n",
- alloc->pid, extra_buffers_size);
- return ERR_PTR(-EINVAL);
- }
- if (is_async &&
- alloc->free_async_space < size + sizeof(struct binder_buffer)) {
+ if (is_async && alloc->free_async_space < size) {
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd failed, no async space left\n",
alloc->pid, size);
- return ERR_PTR(-ENOSPC);
+ buffer = ERR_PTR(-ENOSPC);
+ goto out;
}
- /* Pad 0-size buffers so they get assigned unique addresses */
- size = max(size, sizeof(void *));
-
while (n) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
BUG_ON(!buffer->free);
@@ -446,121 +541,92 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
if (size < buffer_size) {
best_fit = n;
n = n->rb_left;
- } else if (size > buffer_size)
+ } else if (size > buffer_size) {
n = n->rb_right;
- else {
+ } else {
best_fit = n;
break;
}
}
- if (best_fit == NULL) {
- size_t allocated_buffers = 0;
- size_t largest_alloc_size = 0;
- size_t total_alloc_size = 0;
- size_t free_buffers = 0;
- size_t largest_free_size = 0;
- size_t total_free_size = 0;
-
- for (n = rb_first(&alloc->allocated_buffers); n != NULL;
- n = rb_next(n)) {
- buffer = rb_entry(n, struct binder_buffer, rb_node);
- buffer_size = binder_alloc_buffer_size(alloc, buffer);
- allocated_buffers++;
- total_alloc_size += buffer_size;
- if (buffer_size > largest_alloc_size)
- largest_alloc_size = buffer_size;
- }
- for (n = rb_first(&alloc->free_buffers); n != NULL;
- n = rb_next(n)) {
- buffer = rb_entry(n, struct binder_buffer, rb_node);
- buffer_size = binder_alloc_buffer_size(alloc, buffer);
- free_buffers++;
- total_free_size += buffer_size;
- if (buffer_size > largest_free_size)
- largest_free_size = buffer_size;
- }
+
+ if (unlikely(!best_fit)) {
binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
"%d: binder_alloc_buf size %zd failed, no address space\n",
alloc->pid, size);
- binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
- "allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n",
- total_alloc_size, allocated_buffers,
- largest_alloc_size, total_free_size,
- free_buffers, largest_free_size);
- return ERR_PTR(-ENOSPC);
+ debug_no_space_locked(alloc);
+ buffer = ERR_PTR(-ENOSPC);
+ goto out;
}
- if (n == NULL) {
+
+ if (buffer_size != size) {
+ /* Found an oversized buffer and needs to be split */
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
buffer_size = binder_alloc_buffer_size(alloc, buffer);
+
+ WARN_ON(n || buffer_size == size);
+ new_buffer->user_data = buffer->user_data + size;
+ list_add(&new_buffer->entry, &buffer->entry);
+ new_buffer->free = 1;
+ binder_insert_free_buffer(alloc, new_buffer);
+ new_buffer = NULL;
}
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",
alloc->pid, size, buffer, buffer_size);
- has_page_addr = (void __user *)
- (((uintptr_t)buffer->user_data + buffer_size) & PAGE_MASK);
- WARN_ON(n && buffer_size != size);
- end_page_addr =
- (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data + size);
- if (end_page_addr > has_page_addr)
- end_page_addr = has_page_addr;
- ret = binder_update_page_range(alloc, 1, (void __user *)
- PAGE_ALIGN((uintptr_t)buffer->user_data), end_page_addr);
- if (ret)
- return ERR_PTR(ret);
-
- if (buffer_size != size) {
- struct binder_buffer *new_buffer;
-
- new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (!new_buffer) {
- pr_err("%s: %d failed to alloc new buffer struct\n",
- __func__, alloc->pid);
- goto err_alloc_buf_struct_failed;
- }
- new_buffer->user_data = (u8 __user *)buffer->user_data + size;
- list_add(&new_buffer->entry, &buffer->entry);
- new_buffer->free = 1;
- binder_insert_free_buffer(alloc, new_buffer);
- }
+ /*
+ * Now we remove the pages from the freelist. A clever calculation
+ * with buffer_size determines if the last page is shared with an
+ * adjacent in-use buffer. In such case, the page has been already
+ * removed from the freelist so we trim our range short.
+ */
+ next_used_page = (buffer->user_data + buffer_size) & PAGE_MASK;
+ curr_last_page = PAGE_ALIGN(buffer->user_data + size);
+ binder_lru_freelist_del(alloc, PAGE_ALIGN(buffer->user_data),
+ min(next_used_page, curr_last_page));
- rb_erase(best_fit, &alloc->free_buffers);
+ rb_erase(&buffer->rb_node, &alloc->free_buffers);
buffer->free = 0;
buffer->allow_user_free = 0;
binder_insert_allocated_buffer_locked(alloc, buffer);
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd got %pK\n",
- alloc->pid, size, buffer);
- buffer->data_size = data_size;
- buffer->offsets_size = offsets_size;
buffer->async_transaction = is_async;
- buffer->extra_buffers_size = extra_buffers_size;
- buffer->pid = pid;
buffer->oneway_spam_suspect = false;
if (is_async) {
- alloc->free_async_space -= size + sizeof(struct binder_buffer);
+ alloc->free_async_space -= size;
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
"%d: binder_alloc_buf size %zd async free %zd\n",
alloc->pid, size, alloc->free_async_space);
- if (alloc->free_async_space < alloc->buffer_size / 10) {
- /*
- * Start detecting spammers once we have less than 20%
- * of async space left (which is less than 10% of total
- * buffer size).
- */
- buffer->oneway_spam_suspect = debug_low_async_space_locked(alloc, pid);
- } else {
- alloc->oneway_spam_detected = false;
- }
+ if (debug_low_async_space_locked(alloc))
+ buffer->oneway_spam_suspect = true;
}
+
+out:
+ /* Discard possibly unused new_buffer */
+ kfree(new_buffer);
return buffer;
+}
-err_alloc_buf_struct_failed:
- binder_update_page_range(alloc, 0, (void __user *)
- PAGE_ALIGN((uintptr_t)buffer->user_data),
- end_page_addr);
- return ERR_PTR(-ENOMEM);
+/* Calculate the sanitized total size, returns 0 for invalid request */
+static inline size_t sanitized_size(size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size)
+{
+ size_t total, tmp;
+
+ /* Align to pointer size and check for overflows */
+ tmp = ALIGN(data_size, sizeof(void *)) +
+ ALIGN(offsets_size, sizeof(void *));
+ if (tmp < data_size || tmp < offsets_size)
+ return 0;
+ total = tmp + ALIGN(extra_buffers_size, sizeof(void *));
+ if (total < tmp || total < extra_buffers_size)
+ return 0;
+
+ /* Pad 0-sized buffers so they get a unique address */
+ total = max(total, sizeof(void *));
+
+ return total;
}
/**
@@ -570,87 +636,102 @@ err_alloc_buf_struct_failed:
* @offsets_size: user specified buffer offset
* @extra_buffers_size: size of extra space for meta-data (eg, security context)
* @is_async: buffer for async transaction
- * @pid: pid to attribute allocation to (used for debugging)
*
* Allocate a new buffer given the requested sizes. Returns
* the kernel version of the buffer pointer. The size allocated
* is the sum of the three given sizes (each rounded up to
* pointer-sized boundary)
*
- * Return: The allocated buffer or %NULL if error
+ * Return: The allocated buffer or %ERR_PTR(-errno) if error
*/
struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
size_t data_size,
size_t offsets_size,
size_t extra_buffers_size,
- int is_async,
- int pid)
+ int is_async)
{
- struct binder_buffer *buffer;
+ struct binder_buffer *buffer, *next;
+ size_t size;
+ int ret;
+
+ /* Check binder_alloc is fully initialized */
+ if (!binder_alloc_is_mapped(alloc)) {
+ binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
+ "%d: binder_alloc_buf, no vma\n",
+ alloc->pid);
+ return ERR_PTR(-ESRCH);
+ }
+
+ size = sanitized_size(data_size, offsets_size, extra_buffers_size);
+ if (unlikely(!size)) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: got transaction with invalid size %zd-%zd-%zd\n",
+ alloc->pid, data_size, offsets_size,
+ extra_buffers_size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Preallocate the next buffer */
+ next = kzalloc(sizeof(*next), GFP_KERNEL);
+ if (!next)
+ return ERR_PTR(-ENOMEM);
mutex_lock(&alloc->mutex);
- buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size,
- extra_buffers_size, is_async, pid);
+ buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async);
+ if (IS_ERR(buffer)) {
+ mutex_unlock(&alloc->mutex);
+ goto out;
+ }
+
+ buffer->data_size = data_size;
+ buffer->offsets_size = offsets_size;
+ buffer->extra_buffers_size = extra_buffers_size;
+ buffer->pid = current->tgid;
mutex_unlock(&alloc->mutex);
+
+ ret = binder_install_buffer_pages(alloc, buffer, size);
+ if (ret) {
+ binder_alloc_free_buf(alloc, buffer);
+ buffer = ERR_PTR(ret);
+ }
+out:
return buffer;
}
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_new_buf);
-static void __user *buffer_start_page(struct binder_buffer *buffer)
+static unsigned long buffer_start_page(struct binder_buffer *buffer)
{
- return (void __user *)((uintptr_t)buffer->user_data & PAGE_MASK);
+ return buffer->user_data & PAGE_MASK;
}
-static void __user *prev_buffer_end_page(struct binder_buffer *buffer)
+static unsigned long prev_buffer_end_page(struct binder_buffer *buffer)
{
- return (void __user *)
- (((uintptr_t)(buffer->user_data) - 1) & PAGE_MASK);
+ return (buffer->user_data - 1) & PAGE_MASK;
}
static void binder_delete_free_buffer(struct binder_alloc *alloc,
struct binder_buffer *buffer)
{
- struct binder_buffer *prev, *next = NULL;
- bool to_free = true;
+ struct binder_buffer *prev, *next;
+
+ if (PAGE_ALIGNED(buffer->user_data))
+ goto skip_freelist;
BUG_ON(alloc->buffers.next == &buffer->entry);
prev = binder_buffer_prev(buffer);
BUG_ON(!prev->free);
- if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) {
- to_free = false;
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %pK share page with %pK\n",
- alloc->pid, buffer->user_data,
- prev->user_data);
- }
+ if (prev_buffer_end_page(prev) == buffer_start_page(buffer))
+ goto skip_freelist;
if (!list_is_last(&buffer->entry, &alloc->buffers)) {
next = binder_buffer_next(buffer);
- if (buffer_start_page(next) == buffer_start_page(buffer)) {
- to_free = false;
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %pK share page with %pK\n",
- alloc->pid,
- buffer->user_data,
- next->user_data);
- }
+ if (buffer_start_page(next) == buffer_start_page(buffer))
+ goto skip_freelist;
}
- if (PAGE_ALIGNED(buffer->user_data)) {
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer start %pK is page aligned\n",
- alloc->pid, buffer->user_data);
- to_free = false;
- }
-
- if (to_free) {
- binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %pK do not share page with %pK or %pK\n",
- alloc->pid, buffer->user_data,
- prev->user_data,
- next ? next->user_data : NULL);
- binder_update_page_range(alloc, 0, buffer_start_page(buffer),
- buffer_start_page(buffer) + PAGE_SIZE);
- }
+ binder_lru_freelist_add(alloc, buffer_start_page(buffer),
+ buffer_start_page(buffer) + PAGE_SIZE);
+skip_freelist:
list_del(&buffer->entry);
kfree(buffer);
}
@@ -673,21 +754,18 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,
BUG_ON(buffer->free);
BUG_ON(size > buffer_size);
BUG_ON(buffer->transaction != NULL);
- BUG_ON(buffer->user_data < alloc->buffer);
- BUG_ON(buffer->user_data > alloc->buffer + alloc->buffer_size);
+ BUG_ON(buffer->user_data < alloc->vm_start);
+ BUG_ON(buffer->user_data > alloc->vm_start + alloc->buffer_size);
if (buffer->async_transaction) {
- alloc->free_async_space += buffer_size + sizeof(struct binder_buffer);
-
+ alloc->free_async_space += buffer_size;
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
"%d: binder_free_buf size %zd async free %zd\n",
alloc->pid, size, alloc->free_async_space);
}
- binder_update_page_range(alloc, 0,
- (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data),
- (void __user *)(((uintptr_t)
- buffer->user_data + buffer_size) & PAGE_MASK));
+ binder_lru_freelist_add(alloc, PAGE_ALIGN(buffer->user_data),
+ (buffer->user_data + buffer_size) & PAGE_MASK);
rb_erase(&buffer->rb_node, &alloc->allocated_buffers);
buffer->free = 1;
@@ -711,8 +789,67 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,
binder_insert_free_buffer(alloc, buffer);
}
+/**
+ * binder_alloc_get_page() - get kernel pointer for given buffer offset
+ * @alloc: binder_alloc for this proc
+ * @buffer: binder buffer to be accessed
+ * @buffer_offset: offset into @buffer data
+ * @pgoffp: address to copy final page offset to
+ *
+ * Lookup the struct page corresponding to the address
+ * at @buffer_offset into @buffer->user_data. If @pgoffp is not
+ * NULL, the byte-offset into the page is written there.
+ *
+ * The caller is responsible to ensure that the offset points
+ * to a valid address within the @buffer and that @buffer is
+ * not freeable by the user. Since it can't be freed, we are
+ * guaranteed that the corresponding elements of @alloc->pages[]
+ * cannot change.
+ *
+ * Return: struct page
+ */
+static struct page *binder_alloc_get_page(struct binder_alloc *alloc,
+ struct binder_buffer *buffer,
+ binder_size_t buffer_offset,
+ pgoff_t *pgoffp)
+{
+ binder_size_t buffer_space_offset = buffer_offset +
+ (buffer->user_data - alloc->vm_start);
+ pgoff_t pgoff = buffer_space_offset & ~PAGE_MASK;
+ size_t index = buffer_space_offset >> PAGE_SHIFT;
+
+ *pgoffp = pgoff;
+
+ return alloc->pages[index];
+}
+
+/**
+ * binder_alloc_clear_buf() - zero out buffer
+ * @alloc: binder_alloc for this proc
+ * @buffer: binder buffer to be cleared
+ *
+ * memset the given buffer to 0
+ */
static void binder_alloc_clear_buf(struct binder_alloc *alloc,
- struct binder_buffer *buffer);
+ struct binder_buffer *buffer)
+{
+ size_t bytes = binder_alloc_buffer_size(alloc, buffer);
+ binder_size_t buffer_offset = 0;
+
+ while (bytes) {
+ unsigned long size;
+ struct page *page;
+ pgoff_t pgoff;
+
+ page = binder_alloc_get_page(alloc, buffer,
+ buffer_offset, &pgoff);
+ size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
+ memset_page(page, pgoff, 0, size);
+ bytes -= size;
+ buffer_offset += size;
+ }
+}
+
/**
* binder_alloc_free_buf() - free a binder buffer
* @alloc: binder_alloc for this proc
@@ -726,7 +863,7 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
/*
* We could eliminate the call to binder_alloc_clear_buf()
* from binder_alloc_deferred_release() by moving this to
- * binder_alloc_free_buf_locked(). However, that could
+ * binder_free_buf_locked(). However, that could
* increase contention for the alloc mutex if clear_on_free
* is used frequently for large buffers. The mutex is not
* needed for correctness here.
@@ -739,6 +876,7 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
binder_free_buf_locked(alloc, buffer);
mutex_unlock(&alloc->mutex);
}
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_buf);
/**
* binder_alloc_mmap_handler() - map virtual address space for proc
@@ -756,9 +894,15 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
- int ret;
- const char *failure_string;
struct binder_buffer *buffer;
+ const char *failure_string;
+ int ret;
+
+ if (unlikely(vma->vm_mm != alloc->mm)) {
+ ret = -EINVAL;
+ failure_string = "invalid vma->vm_mm";
+ goto err_invalid_mm;
+ }
mutex_lock(&binder_alloc_mmap_lock);
if (alloc->buffer_size) {
@@ -770,12 +914,12 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
SZ_4M);
mutex_unlock(&binder_alloc_mmap_lock);
- alloc->buffer = (void __user *)vma->vm_start;
+ alloc->vm_start = vma->vm_start;
- alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE,
- sizeof(alloc->pages[0]),
- GFP_KERNEL);
- if (alloc->pages == NULL) {
+ alloc->pages = kvcalloc(alloc->buffer_size / PAGE_SIZE,
+ sizeof(alloc->pages[0]),
+ GFP_KERNEL);
+ if (!alloc->pages) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
@@ -788,31 +932,34 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
goto err_alloc_buf_struct_failed;
}
- buffer->user_data = alloc->buffer;
+ buffer->user_data = alloc->vm_start;
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
alloc->free_async_space = alloc->buffer_size / 2;
- binder_alloc_set_vma(alloc, vma);
+
+ /* Signal binder_alloc is fully initialized */
+ binder_alloc_set_mapped(alloc, true);
return 0;
err_alloc_buf_struct_failed:
- kfree(alloc->pages);
+ kvfree(alloc->pages);
alloc->pages = NULL;
err_alloc_pages_failed:
- alloc->buffer = NULL;
+ alloc->vm_start = 0;
mutex_lock(&binder_alloc_mmap_lock);
alloc->buffer_size = 0;
err_already_mapped:
mutex_unlock(&binder_alloc_mmap_lock);
+err_invalid_mm:
binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
"%s: %d %lx-%lx %s failed %d\n", __func__,
alloc->pid, vma->vm_start, vma->vm_end,
failure_string, ret);
return ret;
}
-
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_mmap_handler);
void binder_alloc_deferred_release(struct binder_alloc *alloc)
{
@@ -822,8 +969,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
buffers = 0;
mutex_lock(&alloc->mutex);
- BUG_ON(alloc->vma_addr &&
- vma_lookup(alloc->vma_vm_mm, alloc->vma_addr));
+ BUG_ON(alloc->mapped);
while ((n = rb_first(&alloc->allocated_buffers))) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
@@ -854,42 +1000,35 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
int i;
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
- void __user *page_addr;
+ struct page *page;
bool on_lru;
- if (!alloc->pages[i].page_ptr)
+ page = binder_get_installed_page(alloc, i);
+ if (!page)
continue;
- on_lru = list_lru_del(&binder_alloc_lru,
- &alloc->pages[i].lru);
- page_addr = alloc->buffer + i * PAGE_SIZE;
+ on_lru = list_lru_del(alloc->freelist,
+ page_to_lru(page),
+ page_to_nid(page),
+ NULL);
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%s: %d: page %d at %pK %s\n",
- __func__, alloc->pid, i, page_addr,
+ "%s: %d: page %d %s\n",
+ __func__, alloc->pid, i,
on_lru ? "on lru" : "active");
- __free_page(alloc->pages[i].page_ptr);
+ binder_free_page(page);
page_count++;
}
- kfree(alloc->pages);
}
mutex_unlock(&alloc->mutex);
- if (alloc->vma_vm_mm)
- mmdrop(alloc->vma_vm_mm);
+ kvfree(alloc->pages);
+ if (alloc->mm)
+ mmdrop(alloc->mm);
binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,
"%s: %d buffers %d, pages %d\n",
__func__, alloc->pid, buffers, page_count);
}
-
-static void print_binder_buffer(struct seq_file *m, const char *prefix,
- struct binder_buffer *buffer)
-{
- seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n",
- prefix, buffer->debug_id, buffer->user_data,
- buffer->data_size, buffer->offsets_size,
- buffer->extra_buffers_size,
- buffer->transaction ? "active" : "delivered");
-}
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_deferred_release);
/**
* binder_alloc_print_allocated() - print buffer info
@@ -902,13 +1041,19 @@ static void print_binder_buffer(struct seq_file *m, const char *prefix,
void binder_alloc_print_allocated(struct seq_file *m,
struct binder_alloc *alloc)
{
+ struct binder_buffer *buffer;
struct rb_node *n;
- mutex_lock(&alloc->mutex);
- for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
- print_binder_buffer(m, " buffer",
- rb_entry(n, struct binder_buffer, rb_node));
- mutex_unlock(&alloc->mutex);
+ guard(mutex)(&alloc->mutex);
+ for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n",
+ buffer->debug_id,
+ buffer->user_data - alloc->vm_start,
+ buffer->data_size, buffer->offsets_size,
+ buffer->extra_buffers_size,
+ buffer->transaction ? "active" : "delivered");
+ }
}
/**
@@ -919,7 +1064,7 @@ void binder_alloc_print_allocated(struct seq_file *m,
void binder_alloc_print_pages(struct seq_file *m,
struct binder_alloc *alloc)
{
- struct binder_lru_page *page;
+ struct page *page;
int i;
int active = 0;
int lru = 0;
@@ -930,25 +1075,17 @@ void binder_alloc_print_pages(struct seq_file *m,
* Make sure the binder_alloc is fully initialized, otherwise we might
* read inconsistent state.
*/
-
- mmap_read_lock(alloc->vma_vm_mm);
- if (binder_alloc_get_vma(alloc) == NULL) {
- mmap_read_unlock(alloc->vma_vm_mm);
- goto uninitialized;
- }
-
- mmap_read_unlock(alloc->vma_vm_mm);
- for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
- page = &alloc->pages[i];
- if (!page->page_ptr)
- free++;
- else if (list_empty(&page->lru))
- active++;
- else
- lru++;
+ if (binder_alloc_is_mapped(alloc)) {
+ for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
+ page = binder_get_installed_page(alloc, i);
+ if (!page)
+ free++;
+ else if (list_empty(page_to_lru(page)))
+ active++;
+ else
+ lru++;
+ }
}
-
-uninitialized:
mutex_unlock(&alloc->mutex);
seq_printf(m, " pages: %d:%d:%d\n", active, lru, free);
seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high);
@@ -965,10 +1102,9 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
struct rb_node *n;
int count = 0;
- mutex_lock(&alloc->mutex);
+ guard(mutex)(&alloc->mutex);
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
count++;
- mutex_unlock(&alloc->mutex);
return count;
}
@@ -978,18 +1114,19 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
* @alloc: binder_alloc for this proc
*
* Called from binder_vma_close() when releasing address space.
- * Clears alloc->vma to prevent new incoming transactions from
+ * Clears alloc->mapped to prevent new incoming transactions from
* allocating more buffers.
*/
void binder_alloc_vma_close(struct binder_alloc *alloc)
{
- binder_alloc_set_vma(alloc, NULL);
+ binder_alloc_set_mapped(alloc, false);
}
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_vma_close);
/**
* binder_alloc_free_page() - shrinker callback to free pages
* @item: item to free
- * @lock: lock protecting the item
+ * @lru: list_lru instance of the item
* @cb_arg: callback argument
*
* Called from list_lru_walk() in binder_shrink_scan() to free
@@ -997,87 +1134,113 @@ void binder_alloc_vma_close(struct binder_alloc *alloc)
*/
enum lru_status binder_alloc_free_page(struct list_head *item,
struct list_lru_one *lru,
- spinlock_t *lock,
void *cb_arg)
- __must_hold(lock)
+ __must_hold(&lru->lock)
{
- struct mm_struct *mm = NULL;
- struct binder_lru_page *page = container_of(item,
- struct binder_lru_page,
- lru);
- struct binder_alloc *alloc;
- uintptr_t page_addr;
- size_t index;
+ struct binder_shrinker_mdata *mdata = container_of(item, typeof(*mdata), lru);
+ struct binder_alloc *alloc = mdata->alloc;
+ struct mm_struct *mm = alloc->mm;
struct vm_area_struct *vma;
+ struct page *page_to_free;
+ unsigned long page_addr;
+ int mm_locked = 0;
+ size_t index;
+
+ if (!mmget_not_zero(mm))
+ goto err_mmget;
+
+ index = mdata->page_index;
+ page_addr = alloc->vm_start + index * PAGE_SIZE;
+
+ /* attempt per-vma lock first */
+ vma = lock_vma_under_rcu(mm, page_addr);
+ if (!vma) {
+ /* fall back to mmap_lock */
+ if (!mmap_read_trylock(mm))
+ goto err_mmap_read_lock_failed;
+ mm_locked = 1;
+ vma = vma_lookup(mm, page_addr);
+ }
- alloc = page->alloc;
if (!mutex_trylock(&alloc->mutex))
goto err_get_alloc_mutex_failed;
- if (!page->page_ptr)
- goto err_page_already_freed;
+ /*
+ * Since a binder_alloc can only be mapped once, we ensure
+ * the vma corresponds to this mapping by checking whether
+ * the binder_alloc is still mapped.
+ */
+ if (vma && !binder_alloc_is_mapped(alloc))
+ goto err_invalid_vma;
- index = page - alloc->pages;
- page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE;
+ trace_binder_unmap_kernel_start(alloc, index);
- mm = alloc->vma_vm_mm;
- if (!mmget_not_zero(mm))
- goto err_mmget;
- if (!mmap_read_trylock(mm))
- goto err_mmap_read_lock_failed;
- vma = binder_alloc_get_vma(alloc);
+ page_to_free = alloc->pages[index];
+ binder_set_installed_page(alloc, index, NULL);
+
+ trace_binder_unmap_kernel_end(alloc, index);
list_lru_isolate(lru, item);
- spin_unlock(lock);
+ spin_unlock(&lru->lock);
if (vma) {
trace_binder_unmap_user_start(alloc, index);
- zap_page_range(vma, page_addr, PAGE_SIZE);
+ zap_page_range_single(vma, page_addr, PAGE_SIZE, NULL);
trace_binder_unmap_user_end(alloc, index);
}
- mmap_read_unlock(mm);
- mmput_async(mm);
-
- trace_binder_unmap_kernel_start(alloc, index);
-
- __free_page(page->page_ptr);
- page->page_ptr = NULL;
-
- trace_binder_unmap_kernel_end(alloc, index);
- spin_lock(lock);
mutex_unlock(&alloc->mutex);
+ if (mm_locked)
+ mmap_read_unlock(mm);
+ else
+ vma_end_read(vma);
+ mmput_async(mm);
+ binder_free_page(page_to_free);
+
return LRU_REMOVED_RETRY;
+err_invalid_vma:
+ mutex_unlock(&alloc->mutex);
+err_get_alloc_mutex_failed:
+ if (mm_locked)
+ mmap_read_unlock(mm);
+ else
+ vma_end_read(vma);
err_mmap_read_lock_failed:
mmput_async(mm);
err_mmget:
-err_page_already_freed:
- mutex_unlock(&alloc->mutex);
-err_get_alloc_mutex_failed:
return LRU_SKIP;
}
+EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_page);
static unsigned long
binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
{
- return list_lru_count(&binder_alloc_lru);
+ return list_lru_count(&binder_freelist);
}
static unsigned long
binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
{
- return list_lru_walk(&binder_alloc_lru, binder_alloc_free_page,
+ return list_lru_walk(&binder_freelist, binder_alloc_free_page,
NULL, sc->nr_to_scan);
}
-static struct shrinker binder_shrinker = {
- .count_objects = binder_shrink_count,
- .scan_objects = binder_shrink_scan,
- .seeks = DEFAULT_SEEKS,
-};
+static struct shrinker *binder_shrinker;
+
+VISIBLE_IF_KUNIT void __binder_alloc_init(struct binder_alloc *alloc,
+ struct list_lru *freelist)
+{
+ alloc->pid = current->group_leader->pid;
+ alloc->mm = current->mm;
+ mmgrab(alloc->mm);
+ mutex_init(&alloc->mutex);
+ INIT_LIST_HEAD(&alloc->buffers);
+ alloc->freelist = freelist;
+}
+EXPORT_SYMBOL_IF_KUNIT(__binder_alloc_init);
/**
* binder_alloc_init() - called by binder_open() for per-proc initialization
@@ -1088,23 +1251,35 @@ static struct shrinker binder_shrinker = {
*/
void binder_alloc_init(struct binder_alloc *alloc)
{
- alloc->pid = current->group_leader->pid;
- alloc->vma_vm_mm = current->mm;
- mmgrab(alloc->vma_vm_mm);
- mutex_init(&alloc->mutex);
- INIT_LIST_HEAD(&alloc->buffers);
+ __binder_alloc_init(alloc, &binder_freelist);
}
int binder_alloc_shrinker_init(void)
{
- int ret = list_lru_init(&binder_alloc_lru);
+ int ret;
- if (ret == 0) {
- ret = register_shrinker(&binder_shrinker, "android-binder");
- if (ret)
- list_lru_destroy(&binder_alloc_lru);
+ ret = list_lru_init(&binder_freelist);
+ if (ret)
+ return ret;
+
+ binder_shrinker = shrinker_alloc(0, "android-binder");
+ if (!binder_shrinker) {
+ list_lru_destroy(&binder_freelist);
+ return -ENOMEM;
}
- return ret;
+
+ binder_shrinker->count_objects = binder_shrink_count;
+ binder_shrinker->scan_objects = binder_shrink_scan;
+
+ shrinker_register(binder_shrinker);
+
+ return 0;
+}
+
+void binder_alloc_shrinker_exit(void)
+{
+ shrinker_free(binder_shrinker);
+ list_lru_destroy(&binder_freelist);
}
/**
@@ -1140,68 +1315,6 @@ static inline bool check_buffer(struct binder_alloc *alloc,
}
/**
- * binder_alloc_get_page() - get kernel pointer for given buffer offset
- * @alloc: binder_alloc for this proc
- * @buffer: binder buffer to be accessed
- * @buffer_offset: offset into @buffer data
- * @pgoffp: address to copy final page offset to
- *
- * Lookup the struct page corresponding to the address
- * at @buffer_offset into @buffer->user_data. If @pgoffp is not
- * NULL, the byte-offset into the page is written there.
- *
- * The caller is responsible to ensure that the offset points
- * to a valid address within the @buffer and that @buffer is
- * not freeable by the user. Since it can't be freed, we are
- * guaranteed that the corresponding elements of @alloc->pages[]
- * cannot change.
- *
- * Return: struct page
- */
-static struct page *binder_alloc_get_page(struct binder_alloc *alloc,
- struct binder_buffer *buffer,
- binder_size_t buffer_offset,
- pgoff_t *pgoffp)
-{
- binder_size_t buffer_space_offset = buffer_offset +
- (buffer->user_data - alloc->buffer);
- pgoff_t pgoff = buffer_space_offset & ~PAGE_MASK;
- size_t index = buffer_space_offset >> PAGE_SHIFT;
- struct binder_lru_page *lru_page;
-
- lru_page = &alloc->pages[index];
- *pgoffp = pgoff;
- return lru_page->page_ptr;
-}
-
-/**
- * binder_alloc_clear_buf() - zero out buffer
- * @alloc: binder_alloc for this proc
- * @buffer: binder buffer to be cleared
- *
- * memset the given buffer to 0
- */
-static void binder_alloc_clear_buf(struct binder_alloc *alloc,
- struct binder_buffer *buffer)
-{
- size_t bytes = binder_alloc_buffer_size(alloc, buffer);
- binder_size_t buffer_offset = 0;
-
- while (bytes) {
- unsigned long size;
- struct page *page;
- pgoff_t pgoff;
-
- page = binder_alloc_get_page(alloc, buffer,
- buffer_offset, &pgoff);
- size = min_t(size_t, bytes, PAGE_SIZE - pgoff);
- memset_page(page, pgoff, 0, size);
- bytes -= size;
- buffer_offset += size;
- }
-}
-
-/**
* binder_alloc_copy_user_to_buffer() - copy src user to tgt user
* @alloc: binder_alloc for this proc
* @buffer: binder buffer to be accessed
@@ -1295,4 +1408,3 @@ int binder_alloc_copy_from_buffer(struct binder_alloc *alloc,
return binder_alloc_do_buffer_copy(alloc, false, buffer, buffer_offset,
dest, bytes);
}
-
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
index 1e4fd37af5e0..d6f1f6f2d00e 100644
--- a/drivers/android/binder_alloc.h
+++ b/drivers/android/binder_alloc.h
@@ -15,7 +15,6 @@
#include <linux/list_lru.h>
#include <uapi/linux/android/binder.h>
-extern struct list_lru binder_alloc_lru;
struct binder_transaction;
/**
@@ -49,47 +48,54 @@ struct binder_buffer {
unsigned async_transaction:1;
unsigned oneway_spam_suspect:1;
unsigned debug_id:27;
-
struct binder_transaction *transaction;
-
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
size_t extra_buffers_size;
- void __user *user_data;
- int pid;
+ unsigned long user_data;
+ int pid;
};
/**
- * struct binder_lru_page - page object used for binder shrinker
- * @page_ptr: pointer to physical page in mmap'd space
- * @lru: entry in binder_alloc_lru
- * @alloc: binder_alloc for a proc
+ * struct binder_shrinker_mdata - binder metadata used to reclaim pages
+ * @lru: LRU entry in binder_freelist
+ * @alloc: binder_alloc owning the page to reclaim
+ * @page_index: offset in @alloc->pages[] into the page to reclaim
*/
-struct binder_lru_page {
+struct binder_shrinker_mdata {
struct list_head lru;
- struct page *page_ptr;
struct binder_alloc *alloc;
+ unsigned long page_index;
};
+static inline struct list_head *page_to_lru(struct page *p)
+{
+ struct binder_shrinker_mdata *mdata;
+
+ mdata = (struct binder_shrinker_mdata *)page_private(p);
+
+ return &mdata->lru;
+}
+
/**
* struct binder_alloc - per-binder proc state for binder allocator
- * @vma: vm_area_struct passed to mmap_handler
- * (invarient after mmap)
- * @tsk: tid for task that called init for this proc
- * (invariant after init)
- * @vma_vm_mm: copy of vma->vm_mm (invarient after mmap)
- * @buffer: base of per-proc address space mapped via mmap
+ * @mutex: protects binder_alloc fields
+ * @mm: copy of task->mm (invariant after open)
+ * @vm_start: base of per-proc address space mapped via mmap
* @buffers: list of all buffers for this proc
* @free_buffers: rb tree of buffers available for allocation
* sorted by size
* @allocated_buffers: rb tree of allocated buffers sorted by address
* @free_async_space: VA space available for async buffers. This is
* initialized at mmap time to 1/2 the full VA space
- * @pages: array of binder_lru_page
+ * @pages: array of struct page *
+ * @freelist: lru list to use for free pages (invariant after init)
* @buffer_size: size of address space specified via mmap
* @pid: pid for associated binder_proc (invariant after init)
* @pages_high: high watermark of offset in @pages
+ * @mapped: whether the vm area is mapped, each binder instance is
+ * allowed a single mapping throughout its lifetime
* @oneway_spam_detected: %true if oneway spam detection fired, clear that
* flag once the async buffer has returned to a healthy state
*
@@ -100,49 +106,44 @@ struct binder_lru_page {
*/
struct binder_alloc {
struct mutex mutex;
- unsigned long vma_addr;
- struct mm_struct *vma_vm_mm;
- void __user *buffer;
+ struct mm_struct *mm;
+ unsigned long vm_start;
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
- struct binder_lru_page *pages;
+ struct page **pages;
+ struct list_lru *freelist;
size_t buffer_size;
- uint32_t buffer_free;
int pid;
size_t pages_high;
+ bool mapped;
bool oneway_spam_detected;
};
-#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST
-void binder_selftest_alloc(struct binder_alloc *alloc);
-#else
-static inline void binder_selftest_alloc(struct binder_alloc *alloc) {}
-#endif
enum lru_status binder_alloc_free_page(struct list_head *item,
struct list_lru_one *lru,
- spinlock_t *lock, void *cb_arg);
-extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
- size_t data_size,
- size_t offsets_size,
- size_t extra_buffers_size,
- int is_async,
- int pid);
-extern void binder_alloc_init(struct binder_alloc *alloc);
-extern int binder_alloc_shrinker_init(void);
-extern void binder_alloc_vma_close(struct binder_alloc *alloc);
-extern struct binder_buffer *
+ void *cb_arg);
+struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
+ size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async);
+void binder_alloc_init(struct binder_alloc *alloc);
+int binder_alloc_shrinker_init(void);
+void binder_alloc_shrinker_exit(void);
+void binder_alloc_vma_close(struct binder_alloc *alloc);
+struct binder_buffer *
binder_alloc_prepare_to_free(struct binder_alloc *alloc,
- uintptr_t user_ptr);
-extern void binder_alloc_free_buf(struct binder_alloc *alloc,
- struct binder_buffer *buffer);
-extern int binder_alloc_mmap_handler(struct binder_alloc *alloc,
- struct vm_area_struct *vma);
-extern void binder_alloc_deferred_release(struct binder_alloc *alloc);
-extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc);
-extern void binder_alloc_print_allocated(struct seq_file *m,
- struct binder_alloc *alloc);
+ unsigned long user_ptr);
+void binder_alloc_free_buf(struct binder_alloc *alloc,
+ struct binder_buffer *buffer);
+int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+ struct vm_area_struct *vma);
+void binder_alloc_deferred_release(struct binder_alloc *alloc);
+int binder_alloc_get_allocated_count(struct binder_alloc *alloc);
+void binder_alloc_print_allocated(struct seq_file *m,
+ struct binder_alloc *alloc);
void binder_alloc_print_pages(struct seq_file *m,
struct binder_alloc *alloc);
@@ -155,12 +156,8 @@ void binder_alloc_print_pages(struct seq_file *m,
static inline size_t
binder_alloc_get_free_async_space(struct binder_alloc *alloc)
{
- size_t free_async_space;
-
- mutex_lock(&alloc->mutex);
- free_async_space = alloc->free_async_space;
- mutex_unlock(&alloc->mutex);
- return free_async_space;
+ guard(mutex)(&alloc->mutex);
+ return alloc->free_async_space;
}
unsigned long
@@ -182,5 +179,11 @@ int binder_alloc_copy_from_buffer(struct binder_alloc *alloc,
binder_size_t buffer_offset,
size_t bytes);
+#if IS_ENABLED(CONFIG_KUNIT)
+void __binder_alloc_init(struct binder_alloc *alloc, struct list_lru *freelist);
+size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
+ struct binder_buffer *buffer);
+#endif
+
#endif /* _LINUX_BINDER_ALLOC_H */
diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c
deleted file mode 100644
index 43a881073a42..000000000000
--- a/drivers/android/binder_alloc_selftest.c
+++ /dev/null
@@ -1,302 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* binder_alloc_selftest.c
- *
- * Android IPC Subsystem
- *
- * Copyright (C) 2017 Google, Inc.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/mm_types.h>
-#include <linux/err.h>
-#include "binder_alloc.h"
-
-#define BUFFER_NUM 5
-#define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
-
-static bool binder_selftest_run = true;
-static int binder_selftest_failures;
-static DEFINE_MUTEX(binder_selftest_lock);
-
-/**
- * enum buf_end_align_type - Page alignment of a buffer
- * end with regard to the end of the previous buffer.
- *
- * In the pictures below, buf2 refers to the buffer we
- * are aligning. buf1 refers to previous buffer by addr.
- * Symbol [ means the start of a buffer, ] means the end
- * of a buffer, and | means page boundaries.
- */
-enum buf_end_align_type {
- /**
- * @SAME_PAGE_UNALIGNED: The end of this buffer is on
- * the same page as the end of the previous buffer and
- * is not page aligned. Examples:
- * buf1 ][ buf2 ][ ...
- * buf1 ]|[ buf2 ][ ...
- */
- SAME_PAGE_UNALIGNED = 0,
- /**
- * @SAME_PAGE_ALIGNED: When the end of the previous buffer
- * is not page aligned, the end of this buffer is on the
- * same page as the end of the previous buffer and is page
- * aligned. When the previous buffer is page aligned, the
- * end of this buffer is aligned to the next page boundary.
- * Examples:
- * buf1 ][ buf2 ]| ...
- * buf1 ]|[ buf2 ]| ...
- */
- SAME_PAGE_ALIGNED,
- /**
- * @NEXT_PAGE_UNALIGNED: The end of this buffer is on
- * the page next to the end of the previous buffer and
- * is not page aligned. Examples:
- * buf1 ][ buf2 | buf2 ][ ...
- * buf1 ]|[ buf2 | buf2 ][ ...
- */
- NEXT_PAGE_UNALIGNED,
- /**
- * @NEXT_PAGE_ALIGNED: The end of this buffer is on
- * the page next to the end of the previous buffer and
- * is page aligned. Examples:
- * buf1 ][ buf2 | buf2 ]| ...
- * buf1 ]|[ buf2 | buf2 ]| ...
- */
- NEXT_PAGE_ALIGNED,
- /**
- * @NEXT_NEXT_UNALIGNED: The end of this buffer is on
- * the page that follows the page after the end of the
- * previous buffer and is not page aligned. Examples:
- * buf1 ][ buf2 | buf2 | buf2 ][ ...
- * buf1 ]|[ buf2 | buf2 | buf2 ][ ...
- */
- NEXT_NEXT_UNALIGNED,
- LOOP_END,
-};
-
-static void pr_err_size_seq(size_t *sizes, int *seq)
-{
- int i;
-
- pr_err("alloc sizes: ");
- for (i = 0; i < BUFFER_NUM; i++)
- pr_cont("[%zu]", sizes[i]);
- pr_cont("\n");
- pr_err("free seq: ");
- for (i = 0; i < BUFFER_NUM; i++)
- pr_cont("[%d]", seq[i]);
- pr_cont("\n");
-}
-
-static bool check_buffer_pages_allocated(struct binder_alloc *alloc,
- struct binder_buffer *buffer,
- size_t size)
-{
- void __user *page_addr;
- void __user *end;
- int page_index;
-
- end = (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data + size);
- page_addr = buffer->user_data;
- for (; page_addr < end; page_addr += PAGE_SIZE) {
- page_index = (page_addr - alloc->buffer) / PAGE_SIZE;
- if (!alloc->pages[page_index].page_ptr ||
- !list_empty(&alloc->pages[page_index].lru)) {
- pr_err("expect alloc but is %s at page index %d\n",
- alloc->pages[page_index].page_ptr ?
- "lru" : "free", page_index);
- return false;
- }
- }
- return true;
-}
-
-static void binder_selftest_alloc_buf(struct binder_alloc *alloc,
- struct binder_buffer *buffers[],
- size_t *sizes, int *seq)
-{
- int i;
-
- for (i = 0; i < BUFFER_NUM; i++) {
- buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0, 0);
- if (IS_ERR(buffers[i]) ||
- !check_buffer_pages_allocated(alloc, buffers[i],
- sizes[i])) {
- pr_err_size_seq(sizes, seq);
- binder_selftest_failures++;
- }
- }
-}
-
-static void binder_selftest_free_buf(struct binder_alloc *alloc,
- struct binder_buffer *buffers[],
- size_t *sizes, int *seq, size_t end)
-{
- int i;
-
- for (i = 0; i < BUFFER_NUM; i++)
- binder_alloc_free_buf(alloc, buffers[seq[i]]);
-
- for (i = 0; i < end / PAGE_SIZE; i++) {
- /**
- * Error message on a free page can be false positive
- * if binder shrinker ran during binder_alloc_free_buf
- * calls above.
- */
- if (list_empty(&alloc->pages[i].lru)) {
- pr_err_size_seq(sizes, seq);
- pr_err("expect lru but is %s at page index %d\n",
- alloc->pages[i].page_ptr ? "alloc" : "free", i);
- binder_selftest_failures++;
- }
- }
-}
-
-static void binder_selftest_free_page(struct binder_alloc *alloc)
-{
- int i;
- unsigned long count;
-
- while ((count = list_lru_count(&binder_alloc_lru))) {
- list_lru_walk(&binder_alloc_lru, binder_alloc_free_page,
- NULL, count);
- }
-
- for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
- if (alloc->pages[i].page_ptr) {
- pr_err("expect free but is %s at page index %d\n",
- list_empty(&alloc->pages[i].lru) ?
- "alloc" : "lru", i);
- binder_selftest_failures++;
- }
- }
-}
-
-static void binder_selftest_alloc_free(struct binder_alloc *alloc,
- size_t *sizes, int *seq, size_t end)
-{
- struct binder_buffer *buffers[BUFFER_NUM];
-
- binder_selftest_alloc_buf(alloc, buffers, sizes, seq);
- binder_selftest_free_buf(alloc, buffers, sizes, seq, end);
-
- /* Allocate from lru. */
- binder_selftest_alloc_buf(alloc, buffers, sizes, seq);
- if (list_lru_count(&binder_alloc_lru))
- pr_err("lru list should be empty but is not\n");
-
- binder_selftest_free_buf(alloc, buffers, sizes, seq, end);
- binder_selftest_free_page(alloc);
-}
-
-static bool is_dup(int *seq, int index, int val)
-{
- int i;
-
- for (i = 0; i < index; i++) {
- if (seq[i] == val)
- return true;
- }
- return false;
-}
-
-/* Generate BUFFER_NUM factorial free orders. */
-static void binder_selftest_free_seq(struct binder_alloc *alloc,
- size_t *sizes, int *seq,
- int index, size_t end)
-{
- int i;
-
- if (index == BUFFER_NUM) {
- binder_selftest_alloc_free(alloc, sizes, seq, end);
- return;
- }
- for (i = 0; i < BUFFER_NUM; i++) {
- if (is_dup(seq, index, i))
- continue;
- seq[index] = i;
- binder_selftest_free_seq(alloc, sizes, seq, index + 1, end);
- }
-}
-
-static void binder_selftest_alloc_size(struct binder_alloc *alloc,
- size_t *end_offset)
-{
- int i;
- int seq[BUFFER_NUM] = {0};
- size_t front_sizes[BUFFER_NUM];
- size_t back_sizes[BUFFER_NUM];
- size_t last_offset, offset = 0;
-
- for (i = 0; i < BUFFER_NUM; i++) {
- last_offset = offset;
- offset = end_offset[i];
- front_sizes[i] = offset - last_offset;
- back_sizes[BUFFER_NUM - i - 1] = front_sizes[i];
- }
- /*
- * Buffers share the first or last few pages.
- * Only BUFFER_NUM - 1 buffer sizes are adjustable since
- * we need one giant buffer before getting to the last page.
- */
- back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1];
- binder_selftest_free_seq(alloc, front_sizes, seq, 0,
- end_offset[BUFFER_NUM - 1]);
- binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size);
-}
-
-static void binder_selftest_alloc_offset(struct binder_alloc *alloc,
- size_t *end_offset, int index)
-{
- int align;
- size_t end, prev;
-
- if (index == BUFFER_NUM) {
- binder_selftest_alloc_size(alloc, end_offset);
- return;
- }
- prev = index == 0 ? 0 : end_offset[index - 1];
- end = prev;
-
- BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE);
-
- for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) {
- if (align % 2)
- end = ALIGN(end, PAGE_SIZE);
- else
- end += BUFFER_MIN_SIZE;
- end_offset[index] = end;
- binder_selftest_alloc_offset(alloc, end_offset, index + 1);
- }
-}
-
-/**
- * binder_selftest_alloc() - Test alloc and free of buffer pages.
- * @alloc: Pointer to alloc struct.
- *
- * Allocate BUFFER_NUM buffers to cover all page alignment cases,
- * then free them in all orders possible. Check that pages are
- * correctly allocated, put onto lru when buffers are freed, and
- * are freed when binder_alloc_free_page is called.
- */
-void binder_selftest_alloc(struct binder_alloc *alloc)
-{
- size_t end_offset[BUFFER_NUM];
-
- if (!binder_selftest_run)
- return;
- mutex_lock(&binder_selftest_lock);
- if (!binder_selftest_run || !alloc->vma_addr)
- goto done;
- pr_info("STARTED\n");
- binder_selftest_alloc_offset(alloc, end_offset, 0);
- binder_selftest_run = false;
- if (binder_selftest_failures > 0)
- pr_info("%d tests FAILED\n", binder_selftest_failures);
- else
- pr_info("PASSED\n");
-
-done:
- mutex_unlock(&binder_selftest_lock);
-}
diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
index abe19d88c6ec..342574bfd28a 100644
--- a/drivers/android/binder_internal.h
+++ b/drivers/android/binder_internal.h
@@ -3,7 +3,6 @@
#ifndef _LINUX_BINDER_INTERNAL_H
#define _LINUX_BINDER_INTERNAL_H
-#include <linux/export.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
@@ -14,6 +13,7 @@
#include <linux/uidgid.h>
#include <uapi/linux/android/binderfs.h>
#include "binder_alloc.h"
+#include "dbitmap.h"
struct binder_context {
struct binder_node *binder_context_mgr_node;
@@ -24,8 +24,7 @@ struct binder_context {
/**
* struct binder_device - information about a binder device node
- * @hlist: list of binder devices (only used for devices requested via
- * CONFIG_ANDROID_BINDER_DEVICES)
+ * @hlist: list of binder devices
* @miscdev: information about a binder character device node
* @context: binder context information
* @binderfs_inode: This is the inode of the root dentry of the super block
@@ -82,7 +81,6 @@ extern bool is_binderfs_device(const struct inode *inode);
extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
const struct file_operations *fops,
void *data);
-extern void binderfs_remove_file(struct dentry *dentry);
#else
static inline bool is_binderfs_device(const struct inode *inode)
{
@@ -95,7 +93,6 @@ static inline struct dentry *binderfs_create_file(struct dentry *dir,
{
return NULL;
}
-static inline void binderfs_remove_file(struct dentry *dentry) {}
#endif
#ifdef CONFIG_ANDROID_BINDERFS
@@ -129,12 +126,13 @@ enum binder_stat_types {
BINDER_STAT_DEATH,
BINDER_STAT_TRANSACTION,
BINDER_STAT_TRANSACTION_COMPLETE,
+ BINDER_STAT_FREEZE,
BINDER_STAT_COUNT
};
struct binder_stats {
- atomic_t br[_IOC_NR(BR_ONEWAY_SPAM_SUSPECT) + 1];
- atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1];
+ atomic_t br[_IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) + 1];
+ atomic_t bc[_IOC_NR(BC_FREEZE_NOTIFICATION_DONE) + 1];
atomic_t obj_created[BINDER_STAT_COUNT];
atomic_t obj_deleted[BINDER_STAT_COUNT];
};
@@ -152,12 +150,15 @@ struct binder_work {
enum binder_work_type {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
+ BINDER_WORK_TRANSACTION_PENDING,
BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT,
BINDER_WORK_RETURN_ERROR,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
+ BINDER_WORK_FROZEN_BINDER,
+ BINDER_WORK_CLEAR_FREEZE_NOTIFICATION,
} type;
};
@@ -274,6 +275,14 @@ struct binder_ref_death {
binder_uintptr_t cookie;
};
+struct binder_ref_freeze {
+ struct binder_work work;
+ binder_uintptr_t cookie;
+ bool is_frozen:1;
+ bool sent:1;
+ bool resend:1;
+};
+
/**
* struct binder_ref_data - binder_ref counts and id
* @debug_id: unique ID for the ref
@@ -306,6 +315,8 @@ struct binder_ref_data {
* @node indicates the node must be freed
* @death: pointer to death notification (ref_death) if requested
* (protected by @node->lock)
+ * @freeze: pointer to freeze notification (ref_freeze) if requested
+ * (protected by @node->lock)
*
* Structure to track references from procA to target node (on procB). This
* structure is unsafe to access without holding @proc->outer_lock.
@@ -322,6 +333,7 @@ struct binder_ref {
struct binder_proc *proc;
struct binder_node *node;
struct binder_ref_death *death;
+ struct binder_ref_freeze *freeze;
};
/**
@@ -367,12 +379,16 @@ struct binder_ref {
* @freeze_wait: waitqueue of processes waiting for all outstanding
* transactions to be processed
* (protected by @inner_lock)
+ * @dmap dbitmap to manage available reference descriptors
+ * (protected by @outer_lock)
* @todo: list of work for this process
* (protected by @inner_lock)
* @stats: per-process binder statistics
* (atomics, no lock needed)
* @delivered_death: list of delivered death notification
* (protected by @inner_lock)
+ * @delivered_freeze: list of delivered freeze notification
+ * (protected by @inner_lock)
* @max_threads: cap on number of binder threads
* (protected by @inner_lock)
* @requested_threads: number of binder threads requested but not
@@ -416,11 +432,12 @@ struct binder_proc {
bool sync_recv;
bool async_recv;
wait_queue_head_t freeze_wait;
-
+ struct dbitmap dmap;
struct list_head todo;
struct binder_stats stats;
struct list_head delivered_death;
- int max_threads;
+ struct list_head delivered_freeze;
+ u32 max_threads;
int requested_threads;
int requested_threads_started;
int tmp_ref;
@@ -514,12 +531,14 @@ struct binder_transaction {
int debug_id;
struct binder_work work;
struct binder_thread *from;
+ pid_t from_pid;
+ pid_t from_tid;
struct binder_transaction *from_parent;
struct binder_proc *to_proc;
struct binder_thread *to_thread;
struct binder_transaction *to_parent;
- unsigned need_reply:1;
- /* unsigned is_dead:1; */ /* not used at the moment */
+ unsigned is_async:1;
+ unsigned is_reply:1;
struct binder_buffer *buffer;
unsigned int code;
@@ -527,6 +546,7 @@ struct binder_transaction {
long priority;
long saved_priority;
kuid_t sender_euid;
+ ktime_t start_time;
struct list_head fd_fixups;
binder_uintptr_t security_ctx;
/**
@@ -558,4 +578,20 @@ struct binder_object {
};
};
+/**
+ * Add a binder device to binder_devices
+ * @device: the new binder device to add to the global list
+ */
+void binder_add_device(struct binder_device *device);
+
+/**
+ * Remove a binder device to binder_devices
+ * @device: the binder device to remove from the global list
+ */
+void binder_remove_device(struct binder_device *device);
+
+#if IS_ENABLED(CONFIG_KUNIT)
+vm_fault_t binder_vm_fault(struct vm_fault *vmf);
+#endif
+
#endif /* _LINUX_BINDER_INTERNAL_H */
diff --git a/drivers/android/binder_netlink.c b/drivers/android/binder_netlink.c
new file mode 100644
index 000000000000..81e8432b5904
--- /dev/null
+++ b/drivers/android/binder_netlink.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/binder.yaml */
+/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "binder_netlink.h"
+
+#include <uapi/linux/android/binder_netlink.h>
+
+/* Ops table for binder */
+static const struct genl_split_ops binder_nl_ops[] = {
+};
+
+static const struct genl_multicast_group binder_nl_mcgrps[] = {
+ [BINDER_NLGRP_REPORT] = { "report", },
+};
+
+struct genl_family binder_nl_family __ro_after_init = {
+ .name = BINDER_FAMILY_NAME,
+ .version = BINDER_FAMILY_VERSION,
+ .netnsok = true,
+ .parallel_ops = true,
+ .module = THIS_MODULE,
+ .split_ops = binder_nl_ops,
+ .n_split_ops = ARRAY_SIZE(binder_nl_ops),
+ .mcgrps = binder_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(binder_nl_mcgrps),
+};
diff --git a/drivers/android/binder_netlink.h b/drivers/android/binder_netlink.h
new file mode 100644
index 000000000000..57399942a5e3
--- /dev/null
+++ b/drivers/android/binder_netlink.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/binder.yaml */
+/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _LINUX_BINDER_GEN_H
+#define _LINUX_BINDER_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/android/binder_netlink.h>
+
+enum {
+ BINDER_NLGRP_REPORT,
+};
+
+extern struct genl_family binder_nl_family;
+
+#endif /* _LINUX_BINDER_GEN_H */
diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h
index 8cc07e6a4273..fa5eb61cf580 100644
--- a/drivers/android/binder_trace.h
+++ b/drivers/android/binder_trace.h
@@ -34,27 +34,6 @@ TRACE_EVENT(binder_ioctl,
TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
);
-DECLARE_EVENT_CLASS(binder_lock_class,
- TP_PROTO(const char *tag),
- TP_ARGS(tag),
- TP_STRUCT__entry(
- __field(const char *, tag)
- ),
- TP_fast_assign(
- __entry->tag = tag;
- ),
- TP_printk("tag=%s", __entry->tag)
-);
-
-#define DEFINE_BINDER_LOCK_EVENT(name) \
-DEFINE_EVENT(binder_lock_class, name, \
- TP_PROTO(const char *func), \
- TP_ARGS(func))
-
-DEFINE_BINDER_LOCK_EVENT(binder_lock);
-DEFINE_BINDER_LOCK_EVENT(binder_locked);
-DEFINE_BINDER_LOCK_EVENT(binder_unlock);
-
DECLARE_EVENT_CLASS(binder_function_return_class,
TP_PROTO(int ret),
TP_ARGS(ret),
@@ -317,7 +296,7 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release,
TRACE_EVENT(binder_update_page_range,
TP_PROTO(struct binder_alloc *alloc, bool allocate,
- void __user *start, void __user *end),
+ unsigned long start, unsigned long end),
TP_ARGS(alloc, allocate, start, end),
TP_STRUCT__entry(
__field(int, proc)
@@ -328,7 +307,7 @@ TRACE_EVENT(binder_update_page_range,
TP_fast_assign(
__entry->proc = alloc->pid;
__entry->allocate = allocate;
- __entry->offset = start - alloc->buffer;
+ __entry->offset = start - alloc->vm_start;
__entry->size = end - start;
),
TP_printk("proc=%d allocate=%d offset=%zu size=%zu",
@@ -423,6 +402,43 @@ TRACE_EVENT(binder_return,
"unknown")
);
+TRACE_EVENT(binder_netlink_report,
+ TP_PROTO(const char *context,
+ struct binder_transaction *t,
+ u32 data_size,
+ u32 error),
+ TP_ARGS(context, t, data_size, error),
+ TP_STRUCT__entry(
+ __field(const char *, context)
+ __field(u32, error)
+ __field(int, from_pid)
+ __field(int, from_tid)
+ __field(int, to_pid)
+ __field(int, to_tid)
+ __field(bool, is_reply)
+ __field(unsigned int, flags)
+ __field(unsigned int, code)
+ __field(size_t, data_size)
+ ),
+ TP_fast_assign(
+ __entry->context = context;
+ __entry->error = error;
+ __entry->from_pid = t->from_pid;
+ __entry->from_tid = t->from_tid;
+ __entry->to_pid = t->to_proc ? t->to_proc->pid : 0;
+ __entry->to_tid = t->to_thread ? t->to_thread->pid : 0;
+ __entry->is_reply = t->is_reply;
+ __entry->flags = t->flags;
+ __entry->code = t->code;
+ __entry->data_size = data_size;
+ ),
+ TP_printk("from %d:%d to %d:%d context=%s error=%d is_reply=%d flags=0x%x code=0x%x size=%zu",
+ __entry->from_pid, __entry->from_tid,
+ __entry->to_pid, __entry->to_tid,
+ __entry->context, __entry->error, __entry->is_reply,
+ __entry->flags, __entry->code, __entry->data_size)
+);
+
#endif /* _BINDER_TRACE_H */
#undef TRACE_INCLUDE_PATH
diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
index 588d753a7a19..b46bcb91072d 100644
--- a/drivers/android/binderfs.c
+++ b/drivers/android/binderfs.c
@@ -19,7 +19,6 @@
#include <linux/mutex.h>
#include <linux/mount.h>
#include <linux/fs_parser.h>
-#include <linux/radix-tree.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -30,7 +29,6 @@
#include <linux/uaccess.h>
#include <linux/user_namespace.h>
#include <linux/xarray.h>
-#include <uapi/asm-generic/errno-base.h>
#include <uapi/linux/android/binder.h>
#include <uapi/linux/android/binderfs.h>
@@ -39,7 +37,6 @@
#define FIRST_INODE 1
#define SECOND_INODE 2
#define INODE_OFFSET 3
-#define INTSTRLEN 21
#define BINDERFS_MAX_MINOR (1U << MINORBITS)
/* Ensure that the initial ipc namespace always has devices available. */
#define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4)
@@ -61,6 +58,8 @@ enum binderfs_stats_mode {
struct binder_features {
bool oneway_spam_detection;
bool extended_error;
+ bool freeze_notification;
+ bool transaction_report;
};
static const struct constant_table binderfs_param_stats[] = {
@@ -77,6 +76,8 @@ static const struct fs_parameter_spec binderfs_fs_parameters[] = {
static struct binder_features binder_features = {
.oneway_spam_detection = true,
.extended_error = true,
+ .freeze_notification = true,
+ .transaction_report = true,
};
static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
@@ -95,7 +96,7 @@ bool is_binderfs_device(const struct inode *inode)
/**
* binderfs_binder_device_create - allocate inode from super block of a
* binderfs mount
- * @ref_inode: inode from wich the super block will be taken
+ * @ref_inode: inode from which the super block will be taken
* @userp: buffer to copy information about new device for userspace to
* @req: struct binderfs_device as copied from userspace
*
@@ -118,7 +119,6 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
struct dentry *dentry, *root;
struct binder_device *device;
char *name = NULL;
- size_t name_len;
struct inode *inode = NULL;
struct super_block *sb = ref_inode->i_sb;
struct binderfs_info *info = sb->s_fs_info;
@@ -154,7 +154,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
goto err;
inode->i_ino = minor + INODE_OFFSET;
- inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
init_special_inode(inode, S_IFCHR | 0600,
MKDEV(MAJOR(binderfs_dev), minor));
inode->i_fop = &binder_fops;
@@ -162,9 +162,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
inode->i_gid = info->root_gid;
req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */
- name_len = strlen(req->name);
- /* Make sure to include terminating NUL byte */
- name = kmemdup(req->name, name_len + 1, GFP_KERNEL);
+ name = kstrdup(req->name, GFP_KERNEL);
if (!name)
goto err;
@@ -185,28 +183,17 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
}
root = sb->s_root;
- inode_lock(d_inode(root));
-
- /* look it up */
- dentry = lookup_one_len(name, root, name_len);
+ dentry = simple_start_creating(root, name);
if (IS_ERR(dentry)) {
- inode_unlock(d_inode(root));
ret = PTR_ERR(dentry);
goto err;
}
-
- if (d_really_is_positive(dentry)) {
- /* already exists */
- dput(dentry);
- inode_unlock(d_inode(root));
- ret = -EEXIST;
- goto err;
- }
-
inode->i_private = device;
- d_instantiate(dentry, inode);
+ d_make_persistent(dentry, inode);
fsnotify_create(root->d_inode, dentry);
- inode_unlock(d_inode(root));
+ simple_done_creating(dentry);
+
+ binder_add_device(device);
return 0;
@@ -223,14 +210,17 @@ err:
}
/**
- * binderfs_ctl_ioctl - handle binder device node allocation requests
+ * binder_ctl_ioctl - handle binder device node allocation requests
+ * @file: The file pointer for the binder-control device node.
+ * @cmd: The ioctl command.
+ * @arg: The ioctl argument.
*
* The request handler for the binder-control device. All requests operate on
* the binderfs mount the binder-control device resides in:
* - BINDER_CTL_ADD
* Allocate a new binder device.
*
- * Return: 0 on success, negative errno on failure
+ * Return: %0 on success, negative errno on failure.
*/
static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
@@ -273,6 +263,7 @@ static void binderfs_evict_inode(struct inode *inode)
mutex_unlock(&binderfs_minors_mutex);
if (refcount_dec_and_test(&device->ref)) {
+ binder_remove_device(device);
kfree(device->context.name);
kfree(device);
}
@@ -340,22 +331,10 @@ static int binderfs_show_options(struct seq_file *seq, struct dentry *root)
return 0;
}
-static void binderfs_put_super(struct super_block *sb)
-{
- struct binderfs_info *info = sb->s_fs_info;
-
- if (info && info->ipc_ns)
- put_ipc_ns(info->ipc_ns);
-
- kfree(info);
- sb->s_fs_info = NULL;
-}
-
static const struct super_operations binderfs_super_ops = {
.evict_inode = binderfs_evict_inode,
.show_options = binderfs_show_options,
.statfs = simple_statfs,
- .put_super = binderfs_put_super,
};
static inline bool is_binderfs_control_device(const struct dentry *dentry)
@@ -365,7 +344,7 @@ static inline bool is_binderfs_control_device(const struct dentry *dentry)
return info->control_dentry == dentry;
}
-static int binderfs_rename(struct user_namespace *mnt_userns,
+static int binderfs_rename(struct mnt_idmap *idmap,
struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
@@ -374,7 +353,7 @@ static int binderfs_rename(struct user_namespace *mnt_userns,
is_binderfs_control_device(new_dentry))
return -EPERM;
- return simple_rename(&init_user_ns, old_dir, old_dentry, new_dir,
+ return simple_rename(idmap, old_dir, old_dentry, new_dir,
new_dentry, flags);
}
@@ -421,12 +400,6 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
if (!device)
return -ENOMEM;
- /* If we have already created a binder-control node, return. */
- if (info->control_dentry) {
- ret = 0;
- goto out;
- }
-
ret = -ENOMEM;
inode = new_inode(sb);
if (!inode)
@@ -445,7 +418,7 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
}
inode->i_ino = SECOND_INODE;
- inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
init_special_inode(inode, S_IFCHR | 0600,
MKDEV(MAJOR(binderfs_dev), minor));
inode->i_fop = &binder_ctl_fops;
@@ -462,7 +435,8 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
inode->i_private = device;
info->control_dentry = dentry;
- d_add(dentry, inode);
+ d_make_persistent(dentry, inode);
+ dput(dentry);
return 0;
@@ -487,44 +461,11 @@ static struct inode *binderfs_make_inode(struct super_block *sb, int mode)
if (ret) {
ret->i_ino = iunique(sb, BINDERFS_MAX_MINOR + INODE_OFFSET);
ret->i_mode = mode;
- ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
+ simple_inode_init_ts(ret);
}
return ret;
}
-static struct dentry *binderfs_create_dentry(struct dentry *parent,
- const char *name)
-{
- struct dentry *dentry;
-
- dentry = lookup_one_len(name, parent, strlen(name));
- if (IS_ERR(dentry))
- return dentry;
-
- /* Return error if the file/dir already exists. */
- if (d_really_is_positive(dentry)) {
- dput(dentry);
- return ERR_PTR(-EEXIST);
- }
-
- return dentry;
-}
-
-void binderfs_remove_file(struct dentry *dentry)
-{
- struct inode *parent_inode;
-
- parent_inode = d_inode(dentry->d_parent);
- inode_lock(parent_inode);
- if (simple_positive(dentry)) {
- dget(dentry);
- simple_unlink(parent_inode, dentry);
- d_delete(dentry);
- dput(dentry);
- }
- inode_unlock(parent_inode);
-}
-
struct dentry *binderfs_create_file(struct dentry *parent, const char *name,
const struct file_operations *fops,
void *data)
@@ -534,28 +475,24 @@ struct dentry *binderfs_create_file(struct dentry *parent, const char *name,
struct super_block *sb;
parent_inode = d_inode(parent);
- inode_lock(parent_inode);
- dentry = binderfs_create_dentry(parent, name);
+ dentry = simple_start_creating(parent, name);
if (IS_ERR(dentry))
- goto out;
+ return dentry;
sb = parent_inode->i_sb;
new_inode = binderfs_make_inode(sb, S_IFREG | 0444);
if (!new_inode) {
- dput(dentry);
- dentry = ERR_PTR(-ENOMEM);
- goto out;
+ simple_done_creating(dentry);
+ return ERR_PTR(-ENOMEM);
}
new_inode->i_fop = fops;
new_inode->i_private = data;
- d_instantiate(dentry, new_inode);
+ d_make_persistent(dentry, new_inode);
fsnotify_create(parent_inode, dentry);
-
-out:
- inode_unlock(parent_inode);
- return dentry;
+ simple_done_creating(dentry);
+ return dentry; // borrowed
}
static struct dentry *binderfs_create_dir(struct dentry *parent,
@@ -566,30 +503,26 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,
struct super_block *sb;
parent_inode = d_inode(parent);
- inode_lock(parent_inode);
- dentry = binderfs_create_dentry(parent, name);
+ dentry = simple_start_creating(parent, name);
if (IS_ERR(dentry))
- goto out;
+ return dentry;
sb = parent_inode->i_sb;
new_inode = binderfs_make_inode(sb, S_IFDIR | 0755);
if (!new_inode) {
- dput(dentry);
- dentry = ERR_PTR(-ENOMEM);
- goto out;
+ simple_done_creating(dentry);
+ return ERR_PTR(-ENOMEM);
}
new_inode->i_fop = &simple_dir_operations;
new_inode->i_op = &simple_dir_inode_operations;
set_nlink(new_inode, 2);
- d_instantiate(dentry, new_inode);
+ d_make_persistent(dentry, new_inode);
inc_nlink(parent_inode);
fsnotify_mkdir(parent_inode, dentry);
-
-out:
- inode_unlock(parent_inode);
+ simple_done_creating(dentry);
return dentry;
}
@@ -623,6 +556,18 @@ static int init_binder_features(struct super_block *sb)
if (IS_ERR(dentry))
return PTR_ERR(dentry);
+ dentry = binderfs_create_file(dir, "freeze_notification",
+ &binder_features_fops,
+ &binder_features.freeze_notification);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ dentry = binderfs_create_file(dir, "transaction_report",
+ &binder_features_fops,
+ &binder_features.transaction_report);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
return 0;
}
@@ -716,7 +661,7 @@ static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc)
inode->i_ino = FIRST_INODE;
inode->i_fop = &simple_dir_operations;
inode->i_mode = S_IFDIR | 0755;
- inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
inode->i_op = &binderfs_dir_inode_operations;
set_nlink(inode, 2);
@@ -785,11 +730,27 @@ static int binderfs_init_fs_context(struct fs_context *fc)
return 0;
}
+static void binderfs_kill_super(struct super_block *sb)
+{
+ struct binderfs_info *info = sb->s_fs_info;
+
+ /*
+ * During inode eviction struct binderfs_info is needed.
+ * So first wipe the super_block then free struct binderfs_info.
+ */
+ kill_anon_super(sb);
+
+ if (info && info->ipc_ns)
+ put_ipc_ns(info->ipc_ns);
+
+ kfree(info);
+}
+
static struct file_system_type binder_fs_type = {
.name = "binder",
.init_fs_context = binderfs_init_fs_context,
.parameters = binderfs_fs_parameters,
- .kill_sb = kill_litter_super,
+ .kill_sb = binderfs_kill_super,
.fs_flags = FS_USERNS_MOUNT,
};
diff --git a/drivers/android/dbitmap.h b/drivers/android/dbitmap.h
new file mode 100644
index 000000000000..c7299ce8b374
--- /dev/null
+++ b/drivers/android/dbitmap.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2024 Google LLC
+ *
+ * dbitmap - dynamically sized bitmap library.
+ *
+ * Used by the binder driver to optimize the allocation of the smallest
+ * available descriptor ID. Each bit in the bitmap represents the state
+ * of an ID.
+ *
+ * A dbitmap can grow or shrink as needed. This part has been designed
+ * considering that users might need to briefly release their locks in
+ * order to allocate memory for the new bitmap. These operations then,
+ * are verified to determine if the grow or shrink is sill valid.
+ *
+ * This library does not provide protection against concurrent access
+ * by itself. Binder uses the proc->outer_lock for this purpose.
+ */
+
+#ifndef _LINUX_DBITMAP_H
+#define _LINUX_DBITMAP_H
+#include <linux/bitmap.h>
+
+#define NBITS_MIN BITS_PER_TYPE(unsigned long)
+
+struct dbitmap {
+ unsigned int nbits;
+ unsigned long *map;
+};
+
+static inline int dbitmap_enabled(struct dbitmap *dmap)
+{
+ return !!dmap->nbits;
+}
+
+static inline void dbitmap_free(struct dbitmap *dmap)
+{
+ dmap->nbits = 0;
+ kfree(dmap->map);
+ dmap->map = NULL;
+}
+
+/* Returns the nbits that a dbitmap can shrink to, 0 if not possible. */
+static inline unsigned int dbitmap_shrink_nbits(struct dbitmap *dmap)
+{
+ unsigned int bit;
+
+ if (dmap->nbits <= NBITS_MIN)
+ return 0;
+
+ /*
+ * Determine if the bitmap can shrink based on the position of
+ * its last set bit. If the bit is within the first quarter of
+ * the bitmap then shrinking is possible. In this case, the
+ * bitmap should shrink to half its current size.
+ */
+ bit = find_last_bit(dmap->map, dmap->nbits);
+ if (bit < (dmap->nbits >> 2))
+ return dmap->nbits >> 1;
+
+ /* find_last_bit() returns dmap->nbits when no bits are set. */
+ if (bit == dmap->nbits)
+ return NBITS_MIN;
+
+ return 0;
+}
+
+/* Replace the internal bitmap with a new one of different size */
+static inline void
+dbitmap_replace(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)
+{
+ bitmap_copy(new, dmap->map, min(dmap->nbits, nbits));
+ kfree(dmap->map);
+ dmap->map = new;
+ dmap->nbits = nbits;
+}
+
+static inline void
+dbitmap_shrink(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)
+{
+ if (!new)
+ return;
+
+ /*
+ * Verify that shrinking to @nbits is still possible. The @new
+ * bitmap might have been allocated without locks, so this call
+ * could now be outdated. In this case, free @new and move on.
+ */
+ if (!dbitmap_enabled(dmap) || dbitmap_shrink_nbits(dmap) != nbits) {
+ kfree(new);
+ return;
+ }
+
+ dbitmap_replace(dmap, new, nbits);
+}
+
+/* Returns the nbits that a dbitmap can grow to. */
+static inline unsigned int dbitmap_grow_nbits(struct dbitmap *dmap)
+{
+ return dmap->nbits << 1;
+}
+
+static inline void
+dbitmap_grow(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)
+{
+ /*
+ * Verify that growing to @nbits is still possible. The @new
+ * bitmap might have been allocated without locks, so this call
+ * could now be outdated. In this case, free @new and move on.
+ */
+ if (!dbitmap_enabled(dmap) || nbits <= dmap->nbits) {
+ kfree(new);
+ return;
+ }
+
+ /*
+ * Check for ENOMEM after confirming the grow operation is still
+ * required. This ensures we only disable the dbitmap when it's
+ * necessary. Once the dbitmap is disabled, binder will fallback
+ * to slow_desc_lookup_olocked().
+ */
+ if (!new) {
+ dbitmap_free(dmap);
+ return;
+ }
+
+ dbitmap_replace(dmap, new, nbits);
+}
+
+/*
+ * Finds and sets the next zero bit in the bitmap. Upon success @bit
+ * is populated with the index and 0 is returned. Otherwise, -ENOSPC
+ * is returned to indicate that a dbitmap_grow() is needed.
+ */
+static inline int
+dbitmap_acquire_next_zero_bit(struct dbitmap *dmap, unsigned long offset,
+ unsigned long *bit)
+{
+ unsigned long n;
+
+ n = find_next_zero_bit(dmap->map, dmap->nbits, offset);
+ if (n == dmap->nbits)
+ return -ENOSPC;
+
+ *bit = n;
+ set_bit(n, dmap->map);
+
+ return 0;
+}
+
+static inline void
+dbitmap_clear_bit(struct dbitmap *dmap, unsigned long bit)
+{
+ clear_bit(bit, dmap->map);
+}
+
+static inline int dbitmap_init(struct dbitmap *dmap)
+{
+ dmap->map = bitmap_zalloc(NBITS_MIN, GFP_KERNEL);
+ if (!dmap->map) {
+ dmap->nbits = 0;
+ return -ENOMEM;
+ }
+
+ dmap->nbits = NBITS_MIN;
+
+ return 0;
+}
+#endif
diff --git a/drivers/android/tests/.kunitconfig b/drivers/android/tests/.kunitconfig
new file mode 100644
index 000000000000..39b76bab9d9a
--- /dev/null
+++ b/drivers/android/tests/.kunitconfig
@@ -0,0 +1,7 @@
+#
+# Copyright 2025 Google LLC.
+#
+
+CONFIG_KUNIT=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST=y
diff --git a/drivers/android/tests/Makefile b/drivers/android/tests/Makefile
new file mode 100644
index 000000000000..27268418eb03
--- /dev/null
+++ b/drivers/android/tests/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright 2025 Google LLC.
+#
+
+obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += binder_alloc_kunit.o
diff --git a/drivers/android/tests/binder_alloc_kunit.c b/drivers/android/tests/binder_alloc_kunit.c
new file mode 100644
index 000000000000..7f9cc003bbe3
--- /dev/null
+++ b/drivers/android/tests/binder_alloc_kunit.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test cases for binder allocator code.
+ *
+ * Copyright 2025 Google LLC.
+ * Author: Tiffany Yang <ynaffit@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <kunit/test.h>
+#include <linux/anon_inodes.h>
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/seq_buf.h>
+#include <linux/sizes.h>
+
+#include "../binder_alloc.h"
+#include "../binder_internal.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+#define BINDER_MMAP_SIZE SZ_128K
+
+#define BUFFER_NUM 5
+#define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
+
+#define FREESEQ_BUFLEN ((3 * BUFFER_NUM) + 1)
+
+#define ALIGN_TYPE_STRLEN (12)
+
+#define ALIGNMENTS_BUFLEN (((ALIGN_TYPE_STRLEN + 6) * BUFFER_NUM) + 1)
+
+#define PRINT_ALL_CASES (0)
+
+/* 5^5 alignment combinations * 2 places to share pages * 5! free sequences */
+#define TOTAL_EXHAUSTIVE_CASES (3125 * 2 * 120)
+
+/**
+ * enum buf_end_align_type - Page alignment of a buffer
+ * end with regard to the end of the previous buffer.
+ *
+ * In the pictures below, buf2 refers to the buffer we
+ * are aligning. buf1 refers to previous buffer by addr.
+ * Symbol [ means the start of a buffer, ] means the end
+ * of a buffer, and | means page boundaries.
+ */
+enum buf_end_align_type {
+ /**
+ * @SAME_PAGE_UNALIGNED: The end of this buffer is on
+ * the same page as the end of the previous buffer and
+ * is not page aligned. Examples:
+ * buf1 ][ buf2 ][ ...
+ * buf1 ]|[ buf2 ][ ...
+ */
+ SAME_PAGE_UNALIGNED = 0,
+ /**
+ * @SAME_PAGE_ALIGNED: When the end of the previous buffer
+ * is not page aligned, the end of this buffer is on the
+ * same page as the end of the previous buffer and is page
+ * aligned. When the previous buffer is page aligned, the
+ * end of this buffer is aligned to the next page boundary.
+ * Examples:
+ * buf1 ][ buf2 ]| ...
+ * buf1 ]|[ buf2 ]| ...
+ */
+ SAME_PAGE_ALIGNED,
+ /**
+ * @NEXT_PAGE_UNALIGNED: The end of this buffer is on
+ * the page next to the end of the previous buffer and
+ * is not page aligned. Examples:
+ * buf1 ][ buf2 | buf2 ][ ...
+ * buf1 ]|[ buf2 | buf2 ][ ...
+ */
+ NEXT_PAGE_UNALIGNED,
+ /**
+ * @NEXT_PAGE_ALIGNED: The end of this buffer is on
+ * the page next to the end of the previous buffer and
+ * is page aligned. Examples:
+ * buf1 ][ buf2 | buf2 ]| ...
+ * buf1 ]|[ buf2 | buf2 ]| ...
+ */
+ NEXT_PAGE_ALIGNED,
+ /**
+ * @NEXT_NEXT_UNALIGNED: The end of this buffer is on
+ * the page that follows the page after the end of the
+ * previous buffer and is not page aligned. Examples:
+ * buf1 ][ buf2 | buf2 | buf2 ][ ...
+ * buf1 ]|[ buf2 | buf2 | buf2 ][ ...
+ */
+ NEXT_NEXT_UNALIGNED,
+ /**
+ * @LOOP_END: The number of enum values in &buf_end_align_type.
+ * It is used for controlling loop termination.
+ */
+ LOOP_END,
+};
+
+static const char *const buf_end_align_type_strs[LOOP_END] = {
+ [SAME_PAGE_UNALIGNED] = "SP_UNALIGNED",
+ [SAME_PAGE_ALIGNED] = " SP_ALIGNED ",
+ [NEXT_PAGE_UNALIGNED] = "NP_UNALIGNED",
+ [NEXT_PAGE_ALIGNED] = " NP_ALIGNED ",
+ [NEXT_NEXT_UNALIGNED] = "NN_UNALIGNED",
+};
+
+struct binder_alloc_test_case_info {
+ char alignments[ALIGNMENTS_BUFLEN];
+ struct seq_buf alignments_sb;
+ size_t *buffer_sizes;
+ int *free_sequence;
+ bool front_pages;
+};
+
+static void stringify_free_seq(struct kunit *test, int *seq, struct seq_buf *sb)
+{
+ int i;
+
+ for (i = 0; i < BUFFER_NUM; i++)
+ seq_buf_printf(sb, "[%d]", seq[i]);
+
+ KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb));
+}
+
+static void stringify_alignments(struct kunit *test, int *alignments,
+ struct seq_buf *sb)
+{
+ int i;
+
+ for (i = 0; i < BUFFER_NUM; i++)
+ seq_buf_printf(sb, "[ %d:%s ]", i,
+ buf_end_align_type_strs[alignments[i]]);
+
+ KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb));
+}
+
+static bool check_buffer_pages_allocated(struct kunit *test,
+ struct binder_alloc *alloc,
+ struct binder_buffer *buffer,
+ size_t size)
+{
+ unsigned long page_addr;
+ unsigned long end;
+ int page_index;
+
+ end = PAGE_ALIGN(buffer->user_data + size);
+ page_addr = buffer->user_data;
+ for (; page_addr < end; page_addr += PAGE_SIZE) {
+ page_index = (page_addr - alloc->vm_start) / PAGE_SIZE;
+ if (!alloc->pages[page_index] ||
+ !list_empty(page_to_lru(alloc->pages[page_index]))) {
+ kunit_err(test, "expect alloc but is %s at page index %d\n",
+ alloc->pages[page_index] ?
+ "lru" : "free", page_index);
+ return false;
+ }
+ }
+ return true;
+}
+
+static unsigned long binder_alloc_test_alloc_buf(struct kunit *test,
+ struct binder_alloc *alloc,
+ struct binder_buffer *buffers[],
+ size_t *sizes, int *seq)
+{
+ unsigned long failures = 0;
+ int i;
+
+ for (i = 0; i < BUFFER_NUM; i++) {
+ buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0);
+ if (IS_ERR(buffers[i]) ||
+ !check_buffer_pages_allocated(test, alloc, buffers[i], sizes[i]))
+ failures++;
+ }
+
+ return failures;
+}
+
+static unsigned long binder_alloc_test_free_buf(struct kunit *test,
+ struct binder_alloc *alloc,
+ struct binder_buffer *buffers[],
+ size_t *sizes, int *seq, size_t end)
+{
+ unsigned long failures = 0;
+ int i;
+
+ for (i = 0; i < BUFFER_NUM; i++)
+ binder_alloc_free_buf(alloc, buffers[seq[i]]);
+
+ for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) {
+ if (list_empty(page_to_lru(alloc->pages[i]))) {
+ kunit_err(test, "expect lru but is %s at page index %d\n",
+ alloc->pages[i] ? "alloc" : "free", i);
+ failures++;
+ }
+ }
+
+ return failures;
+}
+
+static unsigned long binder_alloc_test_free_page(struct kunit *test,
+ struct binder_alloc *alloc)
+{
+ unsigned long failures = 0;
+ unsigned long count;
+ int i;
+
+ while ((count = list_lru_count(alloc->freelist))) {
+ list_lru_walk(alloc->freelist, binder_alloc_free_page,
+ NULL, count);
+ }
+
+ for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
+ if (alloc->pages[i]) {
+ kunit_err(test, "expect free but is %s at page index %d\n",
+ list_empty(page_to_lru(alloc->pages[i])) ?
+ "alloc" : "lru", i);
+ failures++;
+ }
+ }
+
+ return failures;
+}
+
+/* Executes one full test run for the given test case. */
+static bool binder_alloc_test_alloc_free(struct kunit *test,
+ struct binder_alloc *alloc,
+ struct binder_alloc_test_case_info *tc,
+ size_t end)
+{
+ unsigned long pages = PAGE_ALIGN(end) / PAGE_SIZE;
+ struct binder_buffer *buffers[BUFFER_NUM];
+ unsigned long failures;
+ bool failed = false;
+
+ failures = binder_alloc_test_alloc_buf(test, alloc, buffers,
+ tc->buffer_sizes,
+ tc->free_sequence);
+ failed = failed || failures;
+ KUNIT_EXPECT_EQ_MSG(test, failures, 0,
+ "Initial allocation failed: %lu/%u buffers with errors",
+ failures, BUFFER_NUM);
+
+ failures = binder_alloc_test_free_buf(test, alloc, buffers,
+ tc->buffer_sizes,
+ tc->free_sequence, end);
+ failed = failed || failures;
+ KUNIT_EXPECT_EQ_MSG(test, failures, 0,
+ "Initial buffers not freed correctly: %lu/%lu pages not on lru list",
+ failures, pages);
+
+ /* Allocate from lru. */
+ failures = binder_alloc_test_alloc_buf(test, alloc, buffers,
+ tc->buffer_sizes,
+ tc->free_sequence);
+ failed = failed || failures;
+ KUNIT_EXPECT_EQ_MSG(test, failures, 0,
+ "Reallocation failed: %lu/%u buffers with errors",
+ failures, BUFFER_NUM);
+
+ failures = list_lru_count(alloc->freelist);
+ failed = failed || failures;
+ KUNIT_EXPECT_EQ_MSG(test, failures, 0,
+ "lru list should be empty after reallocation but still has %lu pages",
+ failures);
+
+ failures = binder_alloc_test_free_buf(test, alloc, buffers,
+ tc->buffer_sizes,
+ tc->free_sequence, end);
+ failed = failed || failures;
+ KUNIT_EXPECT_EQ_MSG(test, failures, 0,
+ "Reallocated buffers not freed correctly: %lu/%lu pages not on lru list",
+ failures, pages);
+
+ failures = binder_alloc_test_free_page(test, alloc);
+ failed = failed || failures;
+ KUNIT_EXPECT_EQ_MSG(test, failures, 0,
+ "Failed to clean up allocated pages: %lu/%lu pages still installed",
+ failures, (alloc->buffer_size / PAGE_SIZE));
+
+ return failed;
+}
+
+static bool is_dup(int *seq, int index, int val)
+{
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (seq[i] == val)
+ return true;
+ }
+ return false;
+}
+
+/* Generate BUFFER_NUM factorial free orders. */
+static void permute_frees(struct kunit *test, struct binder_alloc *alloc,
+ struct binder_alloc_test_case_info *tc,
+ unsigned long *runs, unsigned long *failures,
+ int index, size_t end)
+{
+ bool case_failed;
+ int i;
+
+ if (index == BUFFER_NUM) {
+ DECLARE_SEQ_BUF(freeseq_sb, FREESEQ_BUFLEN);
+
+ case_failed = binder_alloc_test_alloc_free(test, alloc, tc, end);
+ *runs += 1;
+ *failures += case_failed;
+
+ if (case_failed || PRINT_ALL_CASES) {
+ stringify_free_seq(test, tc->free_sequence,
+ &freeseq_sb);
+ kunit_err(test, "case %lu: [%s] | %s - %s - %s", *runs,
+ case_failed ? "FAILED" : "PASSED",
+ tc->front_pages ? "front" : "back ",
+ seq_buf_str(&tc->alignments_sb),
+ seq_buf_str(&freeseq_sb));
+ }
+
+ return;
+ }
+ for (i = 0; i < BUFFER_NUM; i++) {
+ if (is_dup(tc->free_sequence, index, i))
+ continue;
+ tc->free_sequence[index] = i;
+ permute_frees(test, alloc, tc, runs, failures, index + 1, end);
+ }
+}
+
+static void gen_buf_sizes(struct kunit *test,
+ struct binder_alloc *alloc,
+ struct binder_alloc_test_case_info *tc,
+ size_t *end_offset, unsigned long *runs,
+ unsigned long *failures)
+{
+ size_t last_offset, offset = 0;
+ size_t front_sizes[BUFFER_NUM];
+ size_t back_sizes[BUFFER_NUM];
+ int seq[BUFFER_NUM] = {0};
+ int i;
+
+ tc->free_sequence = seq;
+ for (i = 0; i < BUFFER_NUM; i++) {
+ last_offset = offset;
+ offset = end_offset[i];
+ front_sizes[i] = offset - last_offset;
+ back_sizes[BUFFER_NUM - i - 1] = front_sizes[i];
+ }
+ back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1];
+
+ /*
+ * Buffers share the first or last few pages.
+ * Only BUFFER_NUM - 1 buffer sizes are adjustable since
+ * we need one giant buffer before getting to the last page.
+ */
+ tc->front_pages = true;
+ tc->buffer_sizes = front_sizes;
+ permute_frees(test, alloc, tc, runs, failures, 0,
+ end_offset[BUFFER_NUM - 1]);
+
+ tc->front_pages = false;
+ tc->buffer_sizes = back_sizes;
+ permute_frees(test, alloc, tc, runs, failures, 0, alloc->buffer_size);
+}
+
+static void gen_buf_offsets(struct kunit *test, struct binder_alloc *alloc,
+ size_t *end_offset, int *alignments,
+ unsigned long *runs, unsigned long *failures,
+ int index)
+{
+ size_t end, prev;
+ int align;
+
+ if (index == BUFFER_NUM) {
+ struct binder_alloc_test_case_info tc = {0};
+
+ seq_buf_init(&tc.alignments_sb, tc.alignments,
+ ALIGNMENTS_BUFLEN);
+ stringify_alignments(test, alignments, &tc.alignments_sb);
+
+ gen_buf_sizes(test, alloc, &tc, end_offset, runs, failures);
+ return;
+ }
+ prev = index == 0 ? 0 : end_offset[index - 1];
+ end = prev;
+
+ BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE);
+
+ for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) {
+ if (align % 2)
+ end = ALIGN(end, PAGE_SIZE);
+ else
+ end += BUFFER_MIN_SIZE;
+ end_offset[index] = end;
+ alignments[index] = align;
+ gen_buf_offsets(test, alloc, end_offset, alignments, runs,
+ failures, index + 1);
+ }
+}
+
+struct binder_alloc_test {
+ struct binder_alloc alloc;
+ struct list_lru binder_test_freelist;
+ struct file *filp;
+ unsigned long mmap_uaddr;
+};
+
+static void binder_alloc_test_init_freelist(struct kunit *test)
+{
+ struct binder_alloc_test *priv = test->priv;
+
+ KUNIT_EXPECT_PTR_EQ(test, priv->alloc.freelist,
+ &priv->binder_test_freelist);
+}
+
+static void binder_alloc_test_mmap(struct kunit *test)
+{
+ struct binder_alloc_test *priv = test->priv;
+ struct binder_alloc *alloc = &priv->alloc;
+ struct binder_buffer *buf;
+ struct rb_node *n;
+
+ KUNIT_EXPECT_EQ(test, alloc->mapped, true);
+ KUNIT_EXPECT_EQ(test, alloc->buffer_size, BINDER_MMAP_SIZE);
+
+ n = rb_first(&alloc->allocated_buffers);
+ KUNIT_EXPECT_PTR_EQ(test, n, NULL);
+
+ n = rb_first(&alloc->free_buffers);
+ buf = rb_entry(n, struct binder_buffer, rb_node);
+ KUNIT_EXPECT_EQ(test, binder_alloc_buffer_size(alloc, buf),
+ BINDER_MMAP_SIZE);
+ KUNIT_EXPECT_TRUE(test, list_is_last(&buf->entry, &alloc->buffers));
+}
+
+/**
+ * binder_alloc_exhaustive_test() - Exhaustively test alloc and free of buffer pages.
+ * @test: The test context object.
+ *
+ * Allocate BUFFER_NUM buffers to cover all page alignment cases,
+ * then free them in all orders possible. Check that pages are
+ * correctly allocated, put onto lru when buffers are freed, and
+ * are freed when binder_alloc_free_page() is called.
+ */
+static void binder_alloc_exhaustive_test(struct kunit *test)
+{
+ struct binder_alloc_test *priv = test->priv;
+ size_t end_offset[BUFFER_NUM];
+ int alignments[BUFFER_NUM];
+ unsigned long failures = 0;
+ unsigned long runs = 0;
+
+ gen_buf_offsets(test, &priv->alloc, end_offset, alignments, &runs,
+ &failures, 0);
+
+ KUNIT_EXPECT_EQ(test, runs, TOTAL_EXHAUSTIVE_CASES);
+ KUNIT_EXPECT_EQ(test, failures, 0);
+}
+
+/* ===== End test cases ===== */
+
+static void binder_alloc_test_vma_close(struct vm_area_struct *vma)
+{
+ struct binder_alloc *alloc = vma->vm_private_data;
+
+ binder_alloc_vma_close(alloc);
+}
+
+static const struct vm_operations_struct binder_alloc_test_vm_ops = {
+ .close = binder_alloc_test_vma_close,
+ .fault = binder_vm_fault,
+};
+
+static int binder_alloc_test_mmap_handler(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct binder_alloc *alloc = filp->private_data;
+
+ vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE);
+
+ vma->vm_ops = &binder_alloc_test_vm_ops;
+ vma->vm_private_data = alloc;
+
+ return binder_alloc_mmap_handler(alloc, vma);
+}
+
+static const struct file_operations binder_alloc_test_fops = {
+ .mmap = binder_alloc_test_mmap_handler,
+};
+
+static int binder_alloc_test_init(struct kunit *test)
+{
+ struct binder_alloc_test *priv;
+ int ret;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ test->priv = priv;
+
+ ret = list_lru_init(&priv->binder_test_freelist);
+ if (ret) {
+ kunit_err(test, "Failed to initialize test freelist\n");
+ return ret;
+ }
+
+ /* __binder_alloc_init requires mm to be attached */
+ ret = kunit_attach_mm();
+ if (ret) {
+ kunit_err(test, "Failed to attach mm\n");
+ return ret;
+ }
+ __binder_alloc_init(&priv->alloc, &priv->binder_test_freelist);
+
+ priv->filp = anon_inode_getfile("binder_alloc_kunit",
+ &binder_alloc_test_fops, &priv->alloc,
+ O_RDWR | O_CLOEXEC);
+ if (IS_ERR_OR_NULL(priv->filp)) {
+ kunit_err(test, "Failed to open binder alloc test driver file\n");
+ return priv->filp ? PTR_ERR(priv->filp) : -ENOMEM;
+ }
+
+ priv->mmap_uaddr = kunit_vm_mmap(test, priv->filp, 0, BINDER_MMAP_SIZE,
+ PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
+ 0);
+ if (!priv->mmap_uaddr) {
+ kunit_err(test, "Could not map the test's transaction memory\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void binder_alloc_test_exit(struct kunit *test)
+{
+ struct binder_alloc_test *priv = test->priv;
+
+ /* Close the backing file to make sure binder_alloc_vma_close runs */
+ if (!IS_ERR_OR_NULL(priv->filp))
+ fput(priv->filp);
+
+ if (priv->alloc.mm)
+ binder_alloc_deferred_release(&priv->alloc);
+
+ /* Make sure freelist is empty */
+ KUNIT_EXPECT_EQ(test, list_lru_count(&priv->binder_test_freelist), 0);
+ list_lru_destroy(&priv->binder_test_freelist);
+}
+
+static struct kunit_case binder_alloc_test_cases[] = {
+ KUNIT_CASE(binder_alloc_test_init_freelist),
+ KUNIT_CASE(binder_alloc_test_mmap),
+ KUNIT_CASE_SLOW(binder_alloc_exhaustive_test),
+ {}
+};
+
+static struct kunit_suite binder_alloc_test_suite = {
+ .name = "binder_alloc",
+ .test_cases = binder_alloc_test_cases,
+ .init = binder_alloc_test_init,
+ .exit = binder_alloc_test_exit,
+};
+
+kunit_test_suite(binder_alloc_test_suite);
+
+MODULE_AUTHOR("Tiffany Yang <ynaffit@google.com>");
+MODULE_DESCRIPTION("Binder Alloc KUnit tests");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 1c9f4fb2595d..120a2b7067fc 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -116,25 +116,40 @@ config SATA_AHCI
If unsure, say N.
config SATA_MOBILE_LPM_POLICY
- int "Default SATA Link Power Management policy for low power chipsets"
- range 0 4
- default 0
+ int "Default SATA Link Power Management policy"
+ range 0 5
+ default 3
depends on SATA_AHCI
help
Select the Default SATA Link Power Management (LPM) policy to use
for chipsets / "South Bridges" supporting low-power modes. Such
- chipsets are typically found on most laptops but desktops and
- servers now also widely use chipsets supporting low power modes.
-
- The value set has the following meanings:
+ chipsets are ubiquitous across laptops, desktops and servers.
+ Each policy combines power saving states and features:
+ - Partial: The Phy logic is powered but is in a reduced power
+ state. The exit latency from this state is no longer than
+ 10us).
+ - Slumber: The Phy logic is powered but is in an even lower power
+ state. The exit latency from this state is potentially
+ longer, but no longer than 10ms.
+ - DevSleep: The Phy logic may be powered down. The exit latency from
+ this state is no longer than 20 ms, unless otherwise
+ specified by DETO in the device Identify Device Data log.
+ - HIPM: Host Initiated Power Management (host automatically
+ transitions to partial and slumber).
+ - DIPM: Device Initiated Power Management (device automatically
+ transitions to partial and slumber).
+
+ The possible values for the default SATA link power management
+ policies are:
0 => Keep firmware settings
- 1 => Maximum performance
- 2 => Medium power
- 3 => Medium power with Device Initiated PM enabled
- 4 => Minimum power
+ 1 => No power savings (maximum performance)
+ 2 => HIPM (Partial)
+ 3 => HIPM (Partial) and DIPM (Partial and Slumber)
+ 4 => HIPM (Partial and DevSleep) and DIPM (Partial and Slumber)
+ 5 => HIPM (Slumber and DevSleep) and DIPM (Partial and Slumber)
- Note "Minimum power" is known to cause issues, including disk
- corruption, with some disks and should not be used.
+ Excluding the value 0, higher values represent policies with higher
+ power savings.
config SATA_AHCI_PLATFORM
tristate "Platform AHCI SATA support"
@@ -176,9 +191,19 @@ config AHCI_DM816
If unsure, say N.
+config AHCI_DWC
+ tristate "Synopsys DWC AHCI SATA support"
+ select SATA_HOST
+ select MFD_SYSCON if (MIPS_BAIKAL_T1 || COMPILE_TEST)
+ help
+ This option enables support for the Synopsys DWC AHCI SATA
+ controller implementation.
+
+ If unsure, say N.
+
config AHCI_ST
tristate "ST AHCI SATA support"
- depends on ARCH_STI
+ depends on ARCH_STI || COMPILE_TEST
select SATA_HOST
help
This option enables support for ST AHCI SATA controller.
@@ -266,6 +291,7 @@ config AHCI_XGENE
config AHCI_QORIQ
tristate "Freescale QorIQ AHCI SATA support"
depends on OF
+ depends on SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
select SATA_HOST
help
This option enables support for the Freescale QorIQ AHCI SoC's
@@ -285,7 +311,7 @@ config SATA_FSL
config SATA_GEMINI
tristate "Gemini SATA bridge support"
- depends on ARCH_GEMINI || (OF && COMPILE_TEST)
+ depends on ARCH_GEMINI || COMPILE_TEST
select SATA_HOST
default ARCH_GEMINI
help
@@ -546,7 +572,7 @@ comment "PATA SFF controllers with BMDMA"
config PATA_ALI
tristate "ALi PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select PATA_TIMINGS
help
This option enables support for the ALi ATA interfaces
@@ -556,7 +582,7 @@ config PATA_ALI
config PATA_AMD
tristate "AMD/NVidia PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select PATA_TIMINGS
help
This option enables support for the AMD and NVidia PATA
@@ -574,7 +600,7 @@ config PATA_ARASAN_CF
config PATA_ARTOP
tristate "ARTOP 6210/6260 PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for ARTOP PATA controllers.
@@ -599,19 +625,9 @@ config PATA_ATP867X
If unsure, say N.
-config PATA_BK3710
- tristate "Palmchip BK3710 PATA support"
- depends on ARCH_DAVINCI || COMPILE_TEST
- select PATA_TIMINGS
- help
- This option enables support for the integrated IDE controller on
- the TI DaVinci SoC.
-
- If unsure, say N.
-
config PATA_CMD64X
tristate "CMD64x PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select PATA_TIMINGS
help
This option enables support for the CMD64x series chips
@@ -640,6 +656,7 @@ config PATA_CS5530
config PATA_CS5535
tristate "CS5535 PATA support (Experimental)"
depends on PCI && (X86_32 || (X86_64 && COMPILE_TEST))
+ depends on !UML
help
This option enables support for the NatSemi/AMD CS5535
companion chip used with the Geode processor family.
@@ -657,7 +674,7 @@ config PATA_CS5536
config PATA_CYPRESS
tristate "Cypress CY82C693 PATA support (Very Experimental)"
- depends on PCI
+ depends on PCI && HAS_IOPORT
select PATA_TIMINGS
help
This option enables support for the Cypress/Contaq CY82C693
@@ -686,7 +703,6 @@ config PATA_EP93XX
config PATA_FTIDE010
tristate "Faraday Technology FTIDE010 PATA support"
- depends on OF
depends on ARM || COMPILE_TEST
depends on SATA_GEMINI
help
@@ -706,7 +722,7 @@ config PATA_HPT366
config PATA_HPT37X
tristate "HPT 370/370A/371/372/374/302 PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for the majority of the later HPT
PATA controllers via the new ATA layer.
@@ -715,7 +731,7 @@ config PATA_HPT37X
config PATA_HPT3X2N
tristate "HPT 371N/372N/302N PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for the N variant HPT PATA
controllers via the new ATA layer.
@@ -818,7 +834,7 @@ config PATA_MPC52xx
config PATA_NETCELL
tristate "NETCELL Revolution RAID support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for the Netcell Revolution RAID
PATA controller.
@@ -854,7 +870,7 @@ config PATA_OLDPIIX
config PATA_OPTIDMA
tristate "OPTI FireStar PATA support (Very Experimental)"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables DMA/PIO support for the later OPTi
controllers found on some old motherboards and in some
@@ -864,7 +880,7 @@ config PATA_OPTIDMA
config PATA_PDC2027X
tristate "Promise PATA 2027x support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for Promise PATA pdc20268 to pdc20277 host adapters.
@@ -872,7 +888,7 @@ config PATA_PDC2027X
config PATA_PDC_OLD
tristate "Older Promise PATA controller support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for the Promise 20246, 20262, 20263,
20265 and 20267 adapters.
@@ -900,7 +916,7 @@ config PATA_RDC
config PATA_SC1200
tristate "SC1200 PATA support"
- depends on PCI && (X86_32 || COMPILE_TEST)
+ depends on PCI && (X86_32 || COMPILE_TEST) && HAS_IOPORT
help
This option enables support for the NatSemi/AMD SC1200 SoC
companion chip used with the Geode processor family.
@@ -918,7 +934,7 @@ config PATA_SCH
config PATA_SERVERWORKS
tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support"
- depends on PCI
+ depends on PCI && HAS_IOPORT
help
This option enables support for the Serverworks OSB4/CSB5/CSB6 and
HT1000 PATA controllers, via the new ATA layer.
@@ -1083,15 +1099,6 @@ config PATA_OPTI
If unsure, say N.
-config PATA_PALMLD
- tristate "Palm LifeDrive PATA support"
- depends on MACH_PALMLD
- help
- This option enables support for Palm LifeDrive's internal ATA
- port via the new ATA layer.
-
- If unsure, say N.
-
config PATA_PCMCIA
tristate "PCMCIA PATA support"
depends on PCMCIA
@@ -1102,8 +1109,7 @@ config PATA_PCMCIA
If unsure, say N.
config PATA_PLATFORM
- tristate "Generic platform device PATA support"
- depends on EXPERT || PPC || HAVE_PATA_PLATFORM
+ tristate "Generic platform device PATA support" if HAVE_PATA_PLATFORM
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems.
@@ -1112,7 +1118,8 @@ config PATA_PLATFORM
config PATA_OF_PLATFORM
tristate "OpenFirmware platform device PATA support"
- depends on PATA_PLATFORM && OF
+ depends on OF
+ select PATA_PLATFORM
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems with OpenFirmware
@@ -1145,16 +1152,6 @@ config PATA_RZ1000
If unsure, say N.
-config PATA_SAMSUNG_CF
- tristate "Samsung SoC PATA support"
- depends on SAMSUNG_DEV_IDE || COMPILE_TEST
- select PATA_TIMINGS
- help
- This option enables basic support for Samsung's S3C/S5P board
- PATA controllers via the new ATA layer
-
- If unsure, say N.
-
config PATA_WINBOND_VLB
tristate "Winbond W83759A VLB PATA support (Experimental)"
depends on ISA
@@ -1163,6 +1160,20 @@ config PATA_WINBOND_VLB
Support for the Winbond W83759A controller on Vesa Local Bus
systems.
+config PATA_PARPORT
+ tristate "Parallel port IDE device support"
+ depends on PARPORT_PC
+ help
+ There are many external CD-ROM and disk devices that connect through
+ your computer's parallel port. Most of them are actually IDE devices
+ using a parallel port IDE adapter. This option enables the
+ PATA_PARPORT subsystem which contains drivers for many of these
+ external drives.
+ Read <file:Documentation/admin-guide/blockdev/paride.rst> for more
+ information.
+
+source "drivers/ata/pata_parport/Kconfig"
+
comment "Generic fallback / legacy drivers"
config PATA_ACPI
@@ -1187,7 +1198,7 @@ config ATA_GENERIC
config PATA_LEGACY
tristate "Legacy ISA PATA support (Experimental)"
- depends on (ISA || PCI)
+ depends on (ISA || PCI) && HAS_IOPORT
select PATA_TIMINGS
help
This option enables support for ISA/VLB/PCI bus legacy PATA
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index b8aebfb14e82..20e6645ab737 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_AHCI_BRCM) += ahci_brcm.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_CEVA) += ahci_ceva.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_DM816) += ahci_dm816.o libahci.o libahci_platform.o
+obj-$(CONFIG_AHCI_DWC) += ahci_dwc.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_MTK) += ahci_mtk.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o
@@ -53,7 +54,6 @@ obj-$(CONFIG_PATA_AMD) += pata_amd.o
obj-$(CONFIG_PATA_ARTOP) += pata_artop.o
obj-$(CONFIG_PATA_ATIIXP) += pata_atiixp.o
obj-$(CONFIG_PATA_ATP867X) += pata_atp867x.o
-obj-$(CONFIG_PATA_BK3710) += pata_bk3710.o
obj-$(CONFIG_PATA_CMD64X) += pata_cmd64x.o
obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o
obj-$(CONFIG_PATA_CS5530) += pata_cs5530.o
@@ -105,15 +105,15 @@ obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o
obj-$(CONFIG_PATA_NS87410) += pata_ns87410.o
obj-$(CONFIG_PATA_OPTI) += pata_opti.o
obj-$(CONFIG_PATA_PCMCIA) += pata_pcmcia.o
-obj-$(CONFIG_PATA_PALMLD) += pata_palmld.o
obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o
obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
obj-$(CONFIG_PATA_RB532) += pata_rb532_cf.o
obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
-obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
obj-$(CONFIG_PATA_PXA) += pata_pxa.o
+obj-$(CONFIG_PATA_PARPORT) += pata_parport/
+
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
# Should be last but one libata driver
diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c
index 7654a40c12b4..3999305b5356 100644
--- a/drivers/ata/acard-ahci.c
+++ b/drivers/ata/acard-ahci.c
@@ -57,7 +57,7 @@ struct acard_sg {
};
static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc);
-static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
+static void acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
static int acard_ahci_port_start(struct ata_port *ap);
static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
@@ -66,7 +66,7 @@ static int acard_ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg
static int acard_ahci_pci_device_resume(struct pci_dev *pdev);
#endif
-static struct scsi_host_template acard_ahci_sht = {
+static const struct scsi_host_template acard_ahci_sht = {
AHCI_SHT("acard-ahci"),
};
@@ -248,7 +248,7 @@ static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc)
return AC_ERR_OK;
}
-static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
+static void acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct ahci_port_priv *pp = qc->ap->private_data;
u8 *rx_fis = pp->rx_fis;
@@ -263,13 +263,11 @@ static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
* Setup FIS.
*/
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
- !(qc->flags & ATA_QCFLAG_FAILED)) {
+ !(qc->flags & ATA_QCFLAG_EH)) {
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
} else
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
-
- return true;
}
static int acard_ahci_port_start(struct ata_port *ap)
@@ -372,7 +370,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
/* AHCI controllers often implement SFF compatible interface.
* Grab all PCI BARs just in case.
*/
- rc = pcim_iomap_regions_request_all(pdev, 1 << AHCI_PCI_BAR, DRV_NAME);
+ rc = pcim_request_all_regions(pdev, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
if (rc)
@@ -388,7 +386,9 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
if (!(hpriv->flags & AHCI_HFLAG_NO_MSI))
pci_enable_msi(pdev);
- hpriv->mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR];
+ hpriv->mmio = pcim_iomap(pdev, AHCI_PCI_BAR, 0);
+ if (!hpriv->mmio)
+ return -ENOMEM;
/* save initial config */
ahci_save_initial_config(&pdev->dev, hpriv);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index c1eca72b4575..7a7f88b3fa2b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -26,7 +26,6 @@
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gfp.h>
-#include <linux/msi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
@@ -49,13 +48,22 @@ enum {
enum board_ids {
/* board IDs by feature in alphabetical order */
board_ahci,
+ board_ahci_43bit_dma,
board_ahci_ign_iferr,
- board_ahci_low_power,
board_ahci_no_debounce_delay,
- board_ahci_nomsi,
- board_ahci_noncq,
- board_ahci_nosntf,
+ board_ahci_no_msi,
+ /*
+ * board_ahci_pcs_quirk is for legacy Intel platforms.
+ * Modern Intel platforms should use board_ahci instead.
+ * (Some modern Intel platforms might have been added with
+ * board_ahci_pcs_quirk, however, we cannot change them to board_ahci
+ * without testing that the platform actually works without the quirk.)
+ */
+ board_ahci_pcs_quirk,
+ board_ahci_pcs_quirk_no_devslp,
+ board_ahci_pcs_quirk_no_sntf,
board_ahci_yes_fbs,
+ board_ahci_yes_fbs_atapi_dma,
/* board IDs for specific chipsets in alphabetical order */
board_ahci_al,
@@ -68,12 +76,6 @@ enum board_ids {
board_ahci_sb700, /* for SB700 and SB800 */
board_ahci_vt8251,
- /*
- * board IDs for Intel chipsets that support more than 6 ports
- * *and* end up needing the PCS quirk.
- */
- board_ahci_pcs7,
-
/* aliases */
board_ahci_mcp_linux = board_ahci_mcp65,
board_ahci_mcp67 = board_ahci_mcp65,
@@ -84,6 +86,7 @@ enum board_ids {
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
static void ahci_remove_one(struct pci_dev *dev);
static void ahci_shutdown_one(struct pci_dev *dev);
+static void ahci_intel_pcs_quirk(struct pci_dev *pdev, struct ahci_host_priv *hpriv);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
@@ -101,23 +104,23 @@ static int ahci_pci_device_resume(struct device *dev);
#endif
#endif /* CONFIG_PM */
-static struct scsi_host_template ahci_sht = {
+static const struct scsi_host_template ahci_sht = {
AHCI_SHT("ahci"),
};
static struct ata_port_operations ahci_vt8251_ops = {
.inherits = &ahci_ops,
- .hardreset = ahci_vt8251_hardreset,
+ .reset.hardreset = ahci_vt8251_hardreset,
};
static struct ata_port_operations ahci_p5wdh_ops = {
.inherits = &ahci_ops,
- .hardreset = ahci_p5wdh_hardreset,
+ .reset.hardreset = ahci_p5wdh_hardreset,
};
static struct ata_port_operations ahci_avn_ops = {
.inherits = &ahci_ops,
- .hardreset = ahci_avn_hardreset,
+ .reset.hardreset = ahci_avn_hardreset,
};
static const struct ata_port_info ahci_port_info[] = {
@@ -128,15 +131,15 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- [board_ahci_ign_iferr] = {
- AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR),
+ [board_ahci_43bit_dma] = {
+ AHCI_HFLAGS (AHCI_HFLAG_43BIT_ONLY),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- [board_ahci_low_power] = {
- AHCI_HFLAGS (AHCI_HFLAG_USE_LPM_POLICY),
+ [board_ahci_ign_iferr] = {
+ AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
@@ -149,22 +152,31 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- [board_ahci_nomsi] = {
+ [board_ahci_no_msi] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_MSI),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- [board_ahci_noncq] = {
- AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ),
+ [board_ahci_pcs_quirk] = {
+ AHCI_HFLAGS (AHCI_HFLAG_INTEL_PCS_QUIRK),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- [board_ahci_nosntf] = {
- AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF),
+ [board_ahci_pcs_quirk_no_devslp] = {
+ AHCI_HFLAGS (AHCI_HFLAG_INTEL_PCS_QUIRK |
+ AHCI_HFLAG_NO_DEVSLP),
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
+ [board_ahci_pcs_quirk_no_sntf] = {
+ AHCI_HFLAGS (AHCI_HFLAG_INTEL_PCS_QUIRK |
+ AHCI_HFLAG_NO_SNTF),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
@@ -177,6 +189,14 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
+ [board_ahci_yes_fbs_atapi_dma] = {
+ AHCI_HFLAGS (AHCI_HFLAG_YES_FBS |
+ AHCI_HFLAG_ATAPI_DMA_QUIRK),
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
/* by chipsets */
[board_ahci_al] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_MSI),
@@ -186,6 +206,7 @@ static const struct ata_port_info ahci_port_info[] = {
.port_ops = &ahci_ops,
},
[board_ahci_avn] = {
+ AHCI_HFLAGS (AHCI_HFLAG_INTEL_PCS_QUIRK),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
@@ -244,119 +265,113 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_vt8251_ops,
},
- [board_ahci_pcs7] = {
- .flags = AHCI_FLAG_COMMON,
- .pio_mask = ATA_PIO4,
- .udma_mask = ATA_UDMA6,
- .port_ops = &ahci_ops,
- },
};
static const struct pci_device_id ahci_pci_tbl[] = {
/* Intel */
- { PCI_VDEVICE(INTEL, 0x06d6), board_ahci }, /* Comet Lake PCH-H RAID */
- { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */
- { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */
- { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */
- { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */
- { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */
+ { PCI_VDEVICE(INTEL, 0x06d6), board_ahci_pcs_quirk }, /* Comet Lake PCH-H RAID */
+ { PCI_VDEVICE(INTEL, 0x2652), board_ahci_pcs_quirk }, /* ICH6 */
+ { PCI_VDEVICE(INTEL, 0x2653), board_ahci_pcs_quirk }, /* ICH6M */
+ { PCI_VDEVICE(INTEL, 0x27c1), board_ahci_pcs_quirk }, /* ICH7 */
+ { PCI_VDEVICE(INTEL, 0x27c5), board_ahci_pcs_quirk }, /* ICH7M */
+ { PCI_VDEVICE(INTEL, 0x27c3), board_ahci_pcs_quirk }, /* ICH7R */
{ PCI_VDEVICE(AL, 0x5288), board_ahci_ign_iferr }, /* ULi M5288 */
- { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */
- { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
- { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
- { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
- { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
- { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8/Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
- { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
- { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
- { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
- { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
- { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
- { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
- { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
- { PCI_VDEVICE(INTEL, 0x2929), board_ahci_low_power }, /* ICH9M */
- { PCI_VDEVICE(INTEL, 0x292a), board_ahci_low_power }, /* ICH9M */
- { PCI_VDEVICE(INTEL, 0x292b), board_ahci_low_power }, /* ICH9M */
- { PCI_VDEVICE(INTEL, 0x292c), board_ahci_low_power }, /* ICH9M */
- { PCI_VDEVICE(INTEL, 0x292f), board_ahci_low_power }, /* ICH9M */
- { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
- { PCI_VDEVICE(INTEL, 0x294e), board_ahci_low_power }, /* ICH9M */
- { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
- { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
- { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
- { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
- { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
- { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
- { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
- { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
- { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
- { PCI_VDEVICE(INTEL, 0x3b29), board_ahci_low_power }, /* PCH M AHCI */
- { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
- { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci_low_power }, /* PCH M RAID */
- { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
- { PCI_VDEVICE(INTEL, 0x19b0), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b1), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b2), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b3), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b4), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b5), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b6), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19b7), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19bE), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19bF), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c0), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c1), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c2), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c3), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c4), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c5), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c6), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19c7), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19cE), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x19cF), board_ahci_pcs7 }, /* DNV AHCI */
- { PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */
- { PCI_VDEVICE(INTEL, 0x1c03), board_ahci_low_power }, /* CPT M AHCI */
- { PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */
- { PCI_VDEVICE(INTEL, 0x1c05), board_ahci_low_power }, /* CPT M RAID */
- { PCI_VDEVICE(INTEL, 0x1c06), board_ahci }, /* CPT RAID */
- { PCI_VDEVICE(INTEL, 0x1c07), board_ahci }, /* CPT RAID */
- { PCI_VDEVICE(INTEL, 0x1d02), board_ahci }, /* PBG AHCI */
- { PCI_VDEVICE(INTEL, 0x1d04), board_ahci }, /* PBG RAID */
- { PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */
- { PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */
- { PCI_VDEVICE(INTEL, 0x1e02), board_ahci }, /* Panther Point AHCI */
- { PCI_VDEVICE(INTEL, 0x1e03), board_ahci_low_power }, /* Panther M AHCI */
- { PCI_VDEVICE(INTEL, 0x1e04), board_ahci }, /* Panther Point RAID */
- { PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */
- { PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
- { PCI_VDEVICE(INTEL, 0x1e07), board_ahci_low_power }, /* Panther M RAID */
- { PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */
- { PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */
- { PCI_VDEVICE(INTEL, 0x8c03), board_ahci_low_power }, /* Lynx M AHCI */
- { PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */
- { PCI_VDEVICE(INTEL, 0x8c05), board_ahci_low_power }, /* Lynx M RAID */
- { PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */
- { PCI_VDEVICE(INTEL, 0x8c07), board_ahci_low_power }, /* Lynx M RAID */
- { PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */
- { PCI_VDEVICE(INTEL, 0x8c0f), board_ahci_low_power }, /* Lynx M RAID */
- { PCI_VDEVICE(INTEL, 0x9c02), board_ahci_low_power }, /* Lynx LP AHCI */
- { PCI_VDEVICE(INTEL, 0x9c03), board_ahci_low_power }, /* Lynx LP AHCI */
- { PCI_VDEVICE(INTEL, 0x9c04), board_ahci_low_power }, /* Lynx LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c05), board_ahci_low_power }, /* Lynx LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c06), board_ahci_low_power }, /* Lynx LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c07), board_ahci_low_power }, /* Lynx LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c0e), board_ahci_low_power }, /* Lynx LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c0f), board_ahci_low_power }, /* Lynx LP RAID */
- { PCI_VDEVICE(INTEL, 0x9dd3), board_ahci_low_power }, /* Cannon Lake PCH-LP AHCI */
- { PCI_VDEVICE(INTEL, 0x1f22), board_ahci }, /* Avoton AHCI */
- { PCI_VDEVICE(INTEL, 0x1f23), board_ahci }, /* Avoton AHCI */
- { PCI_VDEVICE(INTEL, 0x1f24), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f25), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f26), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x2681), board_ahci_pcs_quirk }, /* ESB2 */
+ { PCI_VDEVICE(INTEL, 0x2682), board_ahci_pcs_quirk }, /* ESB2 */
+ { PCI_VDEVICE(INTEL, 0x2683), board_ahci_pcs_quirk }, /* ESB2 */
+ { PCI_VDEVICE(INTEL, 0x27c6), board_ahci_pcs_quirk }, /* ICH7-M DH */
+ { PCI_VDEVICE(INTEL, 0x2821), board_ahci_pcs_quirk }, /* ICH8 */
+ { PCI_VDEVICE(INTEL, 0x2822), board_ahci_pcs_quirk_no_sntf }, /* ICH8/Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0x2824), board_ahci_pcs_quirk }, /* ICH8 */
+ { PCI_VDEVICE(INTEL, 0x2829), board_ahci_pcs_quirk }, /* ICH8M */
+ { PCI_VDEVICE(INTEL, 0x282a), board_ahci_pcs_quirk }, /* ICH8M */
+ { PCI_VDEVICE(INTEL, 0x2922), board_ahci_pcs_quirk }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2923), board_ahci_pcs_quirk }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2924), board_ahci_pcs_quirk }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2925), board_ahci_pcs_quirk }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2927), board_ahci_pcs_quirk }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x2929), board_ahci_pcs_quirk }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292a), board_ahci_pcs_quirk }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292b), board_ahci_pcs_quirk }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292c), board_ahci_pcs_quirk }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x292f), board_ahci_pcs_quirk }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x294d), board_ahci_pcs_quirk }, /* ICH9 */
+ { PCI_VDEVICE(INTEL, 0x294e), board_ahci_pcs_quirk }, /* ICH9M */
+ { PCI_VDEVICE(INTEL, 0x502a), board_ahci_pcs_quirk }, /* Tolapai */
+ { PCI_VDEVICE(INTEL, 0x502b), board_ahci_pcs_quirk }, /* Tolapai */
+ { PCI_VDEVICE(INTEL, 0x3a05), board_ahci_pcs_quirk }, /* ICH10 */
+ { PCI_VDEVICE(INTEL, 0x3a22), board_ahci_pcs_quirk }, /* ICH10 */
+ { PCI_VDEVICE(INTEL, 0x3a25), board_ahci_pcs_quirk }, /* ICH10 */
+ { PCI_VDEVICE(INTEL, 0x3b22), board_ahci_pcs_quirk }, /* PCH AHCI */
+ { PCI_VDEVICE(INTEL, 0x3b23), board_ahci_pcs_quirk }, /* PCH AHCI */
+ { PCI_VDEVICE(INTEL, 0x3b24), board_ahci_pcs_quirk }, /* PCH RAID */
+ { PCI_VDEVICE(INTEL, 0x3b25), board_ahci_pcs_quirk }, /* PCH RAID */
+ { PCI_VDEVICE(INTEL, 0x3b29), board_ahci_pcs_quirk }, /* PCH M AHCI */
+ { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci_pcs_quirk }, /* PCH RAID */
+ { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci_pcs_quirk }, /* PCH M RAID */
+ { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci_pcs_quirk }, /* PCH AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b0), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b1), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b2), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b3), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b4), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b5), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b6), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19b7), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19bE), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19bF), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c0), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c1), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c2), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c3), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c4), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c5), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c6), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19c7), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19cE), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x19cF), board_ahci }, /* DNV AHCI */
+ { PCI_VDEVICE(INTEL, 0x1c02), board_ahci_pcs_quirk }, /* CPT AHCI */
+ { PCI_VDEVICE(INTEL, 0x1c03), board_ahci_pcs_quirk }, /* CPT M AHCI */
+ { PCI_VDEVICE(INTEL, 0x1c04), board_ahci_pcs_quirk }, /* CPT RAID */
+ { PCI_VDEVICE(INTEL, 0x1c05), board_ahci_pcs_quirk }, /* CPT M RAID */
+ { PCI_VDEVICE(INTEL, 0x1c06), board_ahci_pcs_quirk }, /* CPT RAID */
+ { PCI_VDEVICE(INTEL, 0x1c07), board_ahci_pcs_quirk }, /* CPT RAID */
+ { PCI_VDEVICE(INTEL, 0x1d02), board_ahci_pcs_quirk }, /* PBG AHCI */
+ { PCI_VDEVICE(INTEL, 0x1d04), board_ahci_pcs_quirk }, /* PBG RAID */
+ { PCI_VDEVICE(INTEL, 0x1d06), board_ahci_pcs_quirk }, /* PBG RAID */
+ { PCI_VDEVICE(INTEL, 0x2323), board_ahci_pcs_quirk }, /* DH89xxCC AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e02), board_ahci_pcs_quirk }, /* Panther Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e03), board_ahci_pcs_quirk }, /* Panther M AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e04), board_ahci_pcs_quirk }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e05), board_ahci_pcs_quirk }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e06), board_ahci_pcs_quirk }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e07), board_ahci_pcs_quirk }, /* Panther M RAID */
+ { PCI_VDEVICE(INTEL, 0x1e0e), board_ahci_pcs_quirk }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c02), board_ahci_pcs_quirk }, /* Lynx Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x8c03), board_ahci_pcs_quirk }, /* Lynx M AHCI */
+ { PCI_VDEVICE(INTEL, 0x8c04), board_ahci_pcs_quirk }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c05), board_ahci_pcs_quirk }, /* Lynx M RAID */
+ { PCI_VDEVICE(INTEL, 0x8c06), board_ahci_pcs_quirk }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c07), board_ahci_pcs_quirk }, /* Lynx M RAID */
+ { PCI_VDEVICE(INTEL, 0x8c0e), board_ahci_pcs_quirk }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c0f), board_ahci_pcs_quirk }, /* Lynx M RAID */
+ { PCI_VDEVICE(INTEL, 0x9c02), board_ahci_pcs_quirk }, /* Lynx LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9c03), board_ahci_pcs_quirk }, /* Lynx LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9c04), board_ahci_pcs_quirk }, /* Lynx LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c05), board_ahci_pcs_quirk }, /* Lynx LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c06), board_ahci_pcs_quirk }, /* Lynx LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c07), board_ahci_pcs_quirk }, /* Lynx LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c0e), board_ahci_pcs_quirk }, /* Lynx LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c0f), board_ahci_pcs_quirk }, /* Lynx LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9dd3), board_ahci_pcs_quirk }, /* Cannon Lake PCH-LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x1f22), board_ahci_pcs_quirk }, /* Avoton AHCI */
+ { PCI_VDEVICE(INTEL, 0x1f23), board_ahci_pcs_quirk }, /* Avoton AHCI */
+ { PCI_VDEVICE(INTEL, 0x1f24), board_ahci_pcs_quirk }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f25), board_ahci_pcs_quirk }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f26), board_ahci_pcs_quirk }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f27), board_ahci_pcs_quirk }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f2e), board_ahci_pcs_quirk }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f2f), board_ahci_pcs_quirk }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */
@@ -365,62 +380,64 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg/Lewisburg AHCI*/
- { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* *burg SATA0 'RAID' */
- { PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* *burg SATA1 'RAID' */
- { PCI_VDEVICE(INTEL, 0x282f), board_ahci }, /* *burg SATA2 'RAID' */
- { PCI_VDEVICE(INTEL, 0x43d4), board_ahci }, /* Rocket Lake PCH-H RAID */
- { PCI_VDEVICE(INTEL, 0x43d5), board_ahci }, /* Rocket Lake PCH-H RAID */
- { PCI_VDEVICE(INTEL, 0x43d6), board_ahci }, /* Rocket Lake PCH-H RAID */
- { PCI_VDEVICE(INTEL, 0x43d7), board_ahci }, /* Rocket Lake PCH-H RAID */
- { PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */
- { PCI_VDEVICE(INTEL, 0x8d04), board_ahci }, /* Wellsburg RAID */
- { PCI_VDEVICE(INTEL, 0x8d06), board_ahci }, /* Wellsburg RAID */
- { PCI_VDEVICE(INTEL, 0x8d0e), board_ahci }, /* Wellsburg RAID */
- { PCI_VDEVICE(INTEL, 0x8d62), board_ahci }, /* Wellsburg AHCI */
- { PCI_VDEVICE(INTEL, 0x8d64), board_ahci }, /* Wellsburg RAID */
- { PCI_VDEVICE(INTEL, 0x8d66), board_ahci }, /* Wellsburg RAID */
- { PCI_VDEVICE(INTEL, 0x8d6e), board_ahci }, /* Wellsburg RAID */
- { PCI_VDEVICE(INTEL, 0x23a3), board_ahci }, /* Coleto Creek AHCI */
- { PCI_VDEVICE(INTEL, 0x9c83), board_ahci_low_power }, /* Wildcat LP AHCI */
- { PCI_VDEVICE(INTEL, 0x9c85), board_ahci_low_power }, /* Wildcat LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c87), board_ahci_low_power }, /* Wildcat LP RAID */
- { PCI_VDEVICE(INTEL, 0x9c8f), board_ahci_low_power }, /* Wildcat LP RAID */
- { PCI_VDEVICE(INTEL, 0x8c82), board_ahci }, /* 9 Series AHCI */
- { PCI_VDEVICE(INTEL, 0x8c83), board_ahci_low_power }, /* 9 Series M AHCI */
- { PCI_VDEVICE(INTEL, 0x8c84), board_ahci }, /* 9 Series RAID */
- { PCI_VDEVICE(INTEL, 0x8c85), board_ahci_low_power }, /* 9 Series M RAID */
- { PCI_VDEVICE(INTEL, 0x8c86), board_ahci }, /* 9 Series RAID */
- { PCI_VDEVICE(INTEL, 0x8c87), board_ahci_low_power }, /* 9 Series M RAID */
- { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
- { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci_low_power }, /* 9 Series M RAID */
- { PCI_VDEVICE(INTEL, 0x9d03), board_ahci_low_power }, /* Sunrise LP AHCI */
- { PCI_VDEVICE(INTEL, 0x9d05), board_ahci_low_power }, /* Sunrise LP RAID */
- { PCI_VDEVICE(INTEL, 0x9d07), board_ahci_low_power }, /* Sunrise LP RAID */
- { PCI_VDEVICE(INTEL, 0xa102), board_ahci }, /* Sunrise Point-H AHCI */
- { PCI_VDEVICE(INTEL, 0xa103), board_ahci_low_power }, /* Sunrise M AHCI */
- { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
- { PCI_VDEVICE(INTEL, 0xa106), board_ahci }, /* Sunrise Point-H RAID */
- { PCI_VDEVICE(INTEL, 0xa107), board_ahci_low_power }, /* Sunrise M RAID */
- { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
- { PCI_VDEVICE(INTEL, 0xa182), board_ahci }, /* Lewisburg AHCI*/
- { PCI_VDEVICE(INTEL, 0xa186), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa1d2), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa1d6), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa202), board_ahci }, /* Lewisburg AHCI*/
- { PCI_VDEVICE(INTEL, 0xa206), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa252), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa256), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa356), board_ahci }, /* Cannon Lake PCH-H RAID */
- { PCI_VDEVICE(INTEL, 0x06d7), board_ahci }, /* Comet Lake-H RAID */
- { PCI_VDEVICE(INTEL, 0xa386), board_ahci }, /* Comet Lake PCH-V RAID */
- { PCI_VDEVICE(INTEL, 0x0f22), board_ahci_low_power }, /* Bay Trail AHCI */
- { PCI_VDEVICE(INTEL, 0x0f23), board_ahci_low_power }, /* Bay Trail AHCI */
- { PCI_VDEVICE(INTEL, 0x22a3), board_ahci_low_power }, /* Cherry Tr. AHCI */
- { PCI_VDEVICE(INTEL, 0x5ae3), board_ahci_low_power }, /* ApolloLake AHCI */
- { PCI_VDEVICE(INTEL, 0x34d3), board_ahci_low_power }, /* Ice Lake LP AHCI */
- { PCI_VDEVICE(INTEL, 0x02d3), board_ahci_low_power }, /* Comet Lake PCH-U AHCI */
- { PCI_VDEVICE(INTEL, 0x02d7), board_ahci_low_power }, /* Comet Lake PCH RAID */
+ { PCI_VDEVICE(INTEL, 0x2823), board_ahci_pcs_quirk }, /* Wellsburg/Lewisburg AHCI*/
+ { PCI_VDEVICE(INTEL, 0x2826), board_ahci_pcs_quirk }, /* *burg SATA0 'RAID' */
+ { PCI_VDEVICE(INTEL, 0x2827), board_ahci_pcs_quirk }, /* *burg SATA1 'RAID' */
+ { PCI_VDEVICE(INTEL, 0x282f), board_ahci_pcs_quirk }, /* *burg SATA2 'RAID' */
+ { PCI_VDEVICE(INTEL, 0x43d4), board_ahci_pcs_quirk }, /* Rocket Lake PCH-H RAID */
+ { PCI_VDEVICE(INTEL, 0x43d5), board_ahci_pcs_quirk }, /* Rocket Lake PCH-H RAID */
+ { PCI_VDEVICE(INTEL, 0x43d6), board_ahci_pcs_quirk }, /* Rocket Lake PCH-H RAID */
+ { PCI_VDEVICE(INTEL, 0x43d7), board_ahci_pcs_quirk }, /* Rocket Lake PCH-H RAID */
+ { PCI_VDEVICE(INTEL, 0x8d02), board_ahci_pcs_quirk }, /* Wellsburg AHCI */
+ { PCI_VDEVICE(INTEL, 0x8d04), board_ahci_pcs_quirk }, /* Wellsburg RAID */
+ { PCI_VDEVICE(INTEL, 0x8d06), board_ahci_pcs_quirk }, /* Wellsburg RAID */
+ { PCI_VDEVICE(INTEL, 0x8d0e), board_ahci_pcs_quirk }, /* Wellsburg RAID */
+ { PCI_VDEVICE(INTEL, 0x8d62), board_ahci_pcs_quirk }, /* Wellsburg AHCI */
+ { PCI_VDEVICE(INTEL, 0x8d64), board_ahci_pcs_quirk }, /* Wellsburg RAID */
+ { PCI_VDEVICE(INTEL, 0x8d66), board_ahci_pcs_quirk }, /* Wellsburg RAID */
+ { PCI_VDEVICE(INTEL, 0x8d6e), board_ahci_pcs_quirk }, /* Wellsburg RAID */
+ { PCI_VDEVICE(INTEL, 0x23a3), board_ahci_pcs_quirk }, /* Coleto Creek AHCI */
+ { PCI_VDEVICE(INTEL, 0x9c83), board_ahci_pcs_quirk }, /* Wildcat LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9c85), board_ahci_pcs_quirk }, /* Wildcat LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c87), board_ahci_pcs_quirk }, /* Wildcat LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c8f), board_ahci_pcs_quirk }, /* Wildcat LP RAID */
+ { PCI_VDEVICE(INTEL, 0x8c82), board_ahci_pcs_quirk }, /* 9 Series AHCI */
+ { PCI_VDEVICE(INTEL, 0x8c83), board_ahci_pcs_quirk }, /* 9 Series M AHCI */
+ { PCI_VDEVICE(INTEL, 0x8c84), board_ahci_pcs_quirk }, /* 9 Series RAID */
+ { PCI_VDEVICE(INTEL, 0x8c85), board_ahci_pcs_quirk }, /* 9 Series M RAID */
+ { PCI_VDEVICE(INTEL, 0x8c86), board_ahci_pcs_quirk }, /* 9 Series RAID */
+ { PCI_VDEVICE(INTEL, 0x8c87), board_ahci_pcs_quirk }, /* 9 Series M RAID */
+ { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci_pcs_quirk }, /* 9 Series RAID */
+ { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci_pcs_quirk }, /* 9 Series M RAID */
+ { PCI_VDEVICE(INTEL, 0x9d03), board_ahci_pcs_quirk }, /* Sunrise LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9d05), board_ahci_pcs_quirk }, /* Sunrise LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9d07), board_ahci_pcs_quirk }, /* Sunrise LP RAID */
+ { PCI_VDEVICE(INTEL, 0xa102), board_ahci_pcs_quirk }, /* Sunrise Point-H AHCI */
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci_pcs_quirk }, /* Sunrise M AHCI */
+ { PCI_VDEVICE(INTEL, 0xa105), board_ahci_pcs_quirk }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa106), board_ahci_pcs_quirk }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa107), board_ahci_pcs_quirk }, /* Sunrise M RAID */
+ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci_pcs_quirk }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa182), board_ahci_pcs_quirk }, /* Lewisburg AHCI*/
+ { PCI_VDEVICE(INTEL, 0xa186), board_ahci_pcs_quirk }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa1d2), board_ahci_pcs_quirk }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa1d6), board_ahci_pcs_quirk }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa202), board_ahci_pcs_quirk }, /* Lewisburg AHCI*/
+ { PCI_VDEVICE(INTEL, 0xa206), board_ahci_pcs_quirk }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa252), board_ahci_pcs_quirk }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa256), board_ahci_pcs_quirk }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa356), board_ahci_pcs_quirk }, /* Cannon Lake PCH-H RAID */
+ { PCI_VDEVICE(INTEL, 0x06d7), board_ahci_pcs_quirk }, /* Comet Lake-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa386), board_ahci_pcs_quirk }, /* Comet Lake PCH-V RAID */
+ { PCI_VDEVICE(INTEL, 0x0f22), board_ahci_pcs_quirk }, /* Bay Trail AHCI */
+ { PCI_VDEVICE(INTEL, 0x0f23), board_ahci_pcs_quirk_no_devslp }, /* Bay Trail AHCI */
+ { PCI_VDEVICE(INTEL, 0x22a3), board_ahci_pcs_quirk }, /* Cherry Tr. AHCI */
+ { PCI_VDEVICE(INTEL, 0x5ae3), board_ahci_pcs_quirk }, /* ApolloLake AHCI */
+ { PCI_VDEVICE(INTEL, 0x34d3), board_ahci_pcs_quirk }, /* Ice Lake LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x02d3), board_ahci_pcs_quirk }, /* Comet Lake PCH-U AHCI */
+ { PCI_VDEVICE(INTEL, 0x02d7), board_ahci_pcs_quirk }, /* Comet Lake PCH RAID */
+ /* Elkhart Lake IDs 0x4b60 & 0x4b62 https://sata-io.org/product/8803 not tested yet */
+ { PCI_VDEVICE(INTEL, 0x4b63), board_ahci_pcs_quirk }, /* Elkhart Lake AHCI */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -448,14 +465,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */
{ PCI_VDEVICE(AMD, 0x7801), board_ahci_no_debounce_delay }, /* AMD Hudson-2 (AHCI mode) */
{ PCI_VDEVICE(AMD, 0x7900), board_ahci }, /* AMD CZ */
- { PCI_VDEVICE(AMD, 0x7901), board_ahci_low_power }, /* AMD Green Sardine */
+ { PCI_VDEVICE(AMD, 0x7901), board_ahci }, /* AMD Green Sardine */
/* AMD is using RAID class only for ahci controllers */
{ PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci },
/* Dell S140/S150 */
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_SUBVENDOR_ID_DELL, PCI_ANY_ID,
- PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci },
+ PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci_pcs_quirk },
/* VIA */
{ PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
@@ -581,6 +598,8 @@ static const struct pci_device_id ahci_pci_tbl[] = {
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3),
.driver_data = board_ahci_yes_fbs },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9215),
+ .driver_data = board_ahci_yes_fbs_atapi_dma },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9235),
@@ -594,21 +613,26 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */
{ PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */
- /* Asmedia */
- { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */
- { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */
- { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */
- { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
- { PCI_VDEVICE(ASMEDIA, 0x0621), board_ahci }, /* ASM1061R */
- { PCI_VDEVICE(ASMEDIA, 0x0622), board_ahci }, /* ASM1062R */
- { PCI_VDEVICE(ASMEDIA, 0x0624), board_ahci }, /* ASM1062+JMB575 */
+ /* ASMedia */
+ { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci_43bit_dma }, /* ASM1060 */
+ { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci_43bit_dma }, /* ASM1060 */
+ { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci_43bit_dma }, /* ASM1061 */
+ { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci_43bit_dma }, /* ASM1061/1062 */
+ { PCI_VDEVICE(ASMEDIA, 0x0621), board_ahci_43bit_dma }, /* ASM1061R */
+ { PCI_VDEVICE(ASMEDIA, 0x0622), board_ahci_43bit_dma }, /* ASM1062R */
+ { PCI_VDEVICE(ASMEDIA, 0x0624), board_ahci_43bit_dma }, /* ASM1062+JMB575 */
+ { PCI_VDEVICE(ASMEDIA, 0x1062), board_ahci }, /* ASM1062A */
+ { PCI_VDEVICE(ASMEDIA, 0x1064), board_ahci }, /* ASM1064 */
+ { PCI_VDEVICE(ASMEDIA, 0x1164), board_ahci }, /* ASM1164 */
+ { PCI_VDEVICE(ASMEDIA, 0x1165), board_ahci }, /* ASM1165 */
+ { PCI_VDEVICE(ASMEDIA, 0x1166), board_ahci }, /* ASM1166 */
/*
* Samsung SSDs found on some macbooks. NCQ times out if MSI is
* enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
*/
- { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
- { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi },
+ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_no_msi },
+ { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_no_msi },
/* Enmotus */
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
@@ -650,14 +674,109 @@ MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)");
static int mobile_lpm_policy = -1;
module_param(mobile_lpm_policy, int, 0644);
-MODULE_PARM_DESC(mobile_lpm_policy, "Default LPM policy for mobile chipsets");
+MODULE_PARM_DESC(mobile_lpm_policy,
+ "Default LPM policy. Despite its name, this parameter applies "
+ "to all chipsets, including desktop and server chipsets");
+
+static char *ahci_mask_port_map;
+module_param_named(mask_port_map, ahci_mask_port_map, charp, 0444);
+MODULE_PARM_DESC(mask_port_map,
+ "32-bits port map masks to ignore controllers ports. "
+ "Valid values are: "
+ "\"<mask>\" to apply the same mask to all AHCI controller "
+ "devices, and \"<pci_dev>=<mask>,<pci_dev>=<mask>,...\" to "
+ "specify different masks for the controllers specified, "
+ "where <pci_dev> is the PCI ID of an AHCI controller in the "
+ "form \"domain:bus:dev.func\"");
+
+static char *ahci_mask_port_ext;
+module_param_named(mask_port_ext, ahci_mask_port_ext, charp, 0444);
+MODULE_PARM_DESC(mask_port_ext,
+ "32-bits mask to ignore the external/hotplug capability of ports. "
+ "Valid values are: "
+ "\"<mask>\" to apply the same mask to all AHCI controller "
+ "devices, and \"<pci_dev>=<mask>,<pci_dev>=<mask>,...\" to "
+ "specify different masks for the controllers specified, "
+ "where <pci_dev> is the PCI ID of an AHCI controller in the "
+ "form \"domain:bus:dev.func\"");
+
+static u32 ahci_port_mask(struct device *dev, char *mask_s)
+{
+ unsigned int mask;
+
+ if (kstrtouint(mask_s, 0, &mask)) {
+ dev_err(dev, "Invalid port map mask\n");
+ return 0;
+ }
+
+ return mask;
+}
+
+static u32 ahci_get_port_mask(struct device *dev, char *mask_p)
+{
+ char *param, *end, *str, *mask_s;
+ char *name;
+ u32 mask = 0;
+
+ if (!mask_p || !strlen(mask_p))
+ return 0;
+
+ str = kstrdup(mask_p, GFP_KERNEL);
+ if (!str)
+ return 0;
+
+ /* Handle single mask case */
+ if (!strchr(str, '=')) {
+ mask = ahci_port_mask(dev, str);
+ goto free;
+ }
+
+ /*
+ * Mask list case: parse the parameter to get the mask only if
+ * the device name matches.
+ */
+ param = str;
+ end = param + strlen(param);
+ while (param && param < end && *param) {
+ name = param;
+ param = strchr(name, '=');
+ if (!param)
+ break;
+
+ *param = '\0';
+ param++;
+ if (param >= end)
+ break;
+
+ if (strcmp(dev_name(dev), name) != 0) {
+ param = strchr(param, ',');
+ if (param)
+ param++;
+ continue;
+ }
+
+ mask_s = param;
+ param = strchr(mask_s, ',');
+ if (param) {
+ *param = '\0';
+ param++;
+ }
+
+ mask = ahci_port_mask(dev, mask_s);
+ }
+
+free:
+ kfree(str);
+
+ return mask;
+}
static void ahci_pci_save_initial_config(struct pci_dev *pdev,
struct ahci_host_priv *hpriv)
{
if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) {
dev_info(&pdev->dev, "JMB361 has only one port\n");
- hpriv->force_port_map = 1;
+ hpriv->saved_port_map = 1;
}
/*
@@ -674,9 +793,34 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev,
"Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n");
}
+ /* Handle port map masks passed as module parameter. */
+ hpriv->mask_port_map =
+ ahci_get_port_mask(&pdev->dev, ahci_mask_port_map);
+ hpriv->mask_port_ext =
+ ahci_get_port_mask(&pdev->dev, ahci_mask_port_ext);
+
ahci_save_initial_config(&pdev->dev, hpriv);
}
+static int ahci_pci_reset_controller(struct ata_host *host)
+{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+ rc = ahci_reset_controller(host);
+ if (rc)
+ return rc;
+
+ /*
+ * If platform firmware failed to enable ports, try to enable
+ * them here.
+ */
+ ahci_intel_pcs_quirk(pdev, hpriv);
+
+ return 0;
+}
+
static void ahci_pci_init_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
@@ -690,7 +834,7 @@ static void ahci_pci_init_controller(struct ata_host *host)
mv = 2;
else
mv = 4;
- port_mmio = __ahci_port_base(host, mv);
+ port_mmio = __ahci_port_base(hpriv, mv);
writel(0, port_mmio + PORT_IRQ_MASK);
@@ -788,7 +932,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
@@ -871,7 +1015,7 @@ static int ahci_pci_device_runtime_resume(struct device *dev)
struct ata_host *host = pci_get_drvdata(pdev);
int rc;
- rc = ahci_reset_controller(host);
+ rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
ahci_pci_init_controller(host);
@@ -907,7 +1051,7 @@ static int ahci_pci_device_resume(struct device *dev)
ahci_mcp89_apple_enable(pdev);
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
- rc = ahci_reset_controller(host);
+ rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
@@ -922,11 +1066,20 @@ static int ahci_pci_device_resume(struct device *dev)
#endif /* CONFIG_PM */
-static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
+static int ahci_configure_dma_masks(struct pci_dev *pdev,
+ struct ahci_host_priv *hpriv)
{
- const int dma_bits = using_dac ? 64 : 32;
+ int dma_bits;
int rc;
+ if (hpriv->cap & HOST_CAP_64) {
+ dma_bits = 64;
+ if (hpriv->flags & AHCI_HFLAG_43BIT_ONLY)
+ dma_bits = 43;
+ } else {
+ dma_bits = 32;
+ }
+
/*
* If the device fixup already set the dma_mask to some non-standard
* value, don't extend it here. This happens on STA2X11, for example.
@@ -1244,7 +1397,7 @@ static bool ahci_broken_suspend(struct pci_dev *pdev)
* V1.03 is known to be broken. V3.04 is known to
* work. Between, there are V1.06, V2.06 and V3.03
* that we don't have much idea about. For now,
- * blacklist anything older than V3.04.
+ * assume that anything older than V3.04 is broken.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=15104
*/
@@ -1273,8 +1426,15 @@ static bool ahci_broken_suspend(struct pci_dev *pdev)
static bool ahci_broken_lpm(struct pci_dev *pdev)
{
+ /*
+ * Platforms with LPM problems.
+ * If driver_data is NULL, there is no existing BIOS version with
+ * functioning LPM.
+ * If driver_data is non-NULL, then driver_data contains the DMI BIOS
+ * build date of the first BIOS version with functioning LPM (i.e. older
+ * BIOS versions have broken LPM).
+ */
static const struct dmi_system_id sysids[] = {
- /* Various Lenovo 50 series have LPM issues with older BIOSen */
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -1301,13 +1461,30 @@ static bool ahci_broken_lpm(struct pci_dev *pdev)
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W541"),
},
+ .driver_data = "20180409", /* 2.35 */
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASUSPRO D840MB_M840SA"),
+ },
+ /* 320 is broken, there is no known good version. */
+ },
+ {
/*
- * Note date based on release notes, 2.35 has been
- * reported to be good, but I've been unable to get
- * a hold of the reporter to get the DMI BIOS date.
- * TODO: fix this.
+ * AMD 500 Series Chipset SATA Controller [1022:43eb]
+ * on this motherboard timeouts on ports 5 and 6 when
+ * LPM is enabled, at least with WDC WD20EFAX-68FB5N0
+ * hard drives. LPM with the same drive works fine on
+ * all other ports on the same controller.
*/
- .driver_data = "20180310", /* 2.35 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR,
+ "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME,
+ "ROG STRIX B550-F GAMING (WI-FI)"),
+ },
+ /* 3621 is broken, there is no known good version. */
},
{ } /* terminate list */
};
@@ -1318,6 +1495,9 @@ static bool ahci_broken_lpm(struct pci_dev *pdev)
if (!dmi)
return false;
+ if (!dmi->driver_data)
+ return true;
+
dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
@@ -1374,17 +1554,6 @@ static bool ahci_broken_online(struct pci_dev *pdev)
return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);
}
-static bool ahci_broken_devslp(struct pci_dev *pdev)
-{
- /* device with broken DEVSLP but still showing SDS capability */
- static const struct pci_device_id ids[] = {
- { PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */
- {}
- };
-
- return pci_match_id(ids, pdev);
-}
-
#ifdef CONFIG_ATA_ACPI
static void ahci_gtf_filter_workaround(struct ata_host *host)
{
@@ -1550,18 +1719,20 @@ static int ahci_get_irq_vector(struct ata_host *host, int port)
return pci_irq_vector(to_pci_dev(host->dev), port);
}
-static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
+static void ahci_init_irq(struct pci_dev *pdev, unsigned int n_ports,
struct ahci_host_priv *hpriv)
{
int nvec;
- if (hpriv->flags & AHCI_HFLAG_NO_MSI)
- return -ENODEV;
+ if (hpriv->flags & AHCI_HFLAG_NO_MSI) {
+ pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX);
+ return;
+ }
/*
* If number of MSIs is less than number of ports then Sharing Last
* Message mode could be enforced. In this case assume that advantage
- * of multipe MSIs is negated and use single MSI mode instead.
+ * of multiple MSIs is negated and use single MSI mode instead.
*/
if (n_ports > 1) {
nvec = pci_alloc_irq_vectors(pdev, n_ports, INT_MAX,
@@ -1570,7 +1741,7 @@ static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
if (!(readl(hpriv->mmio + HOST_CTL) & HOST_MRSM)) {
hpriv->get_irq_vector = ahci_get_irq_vector;
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
- return nvec;
+ return;
}
/*
@@ -1585,23 +1756,71 @@ static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
/*
* If the host is not capable of supporting per-port vectors, fall
- * back to single MSI before finally attempting single MSI-X.
+ * back to single MSI before finally attempting single MSI-X or
+ * a legacy INTx.
*/
nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
if (nvec == 1)
- return nvec;
- return pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
+ return;
+ pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX | PCI_IRQ_INTX);
+}
+
+static void ahci_mark_external_port(struct ata_port *ap)
+{
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
+ u32 tmp;
+
+ /*
+ * Mark external ports (hotplug-capable, eSATA), unless we were asked to
+ * ignore this feature.
+ */
+ tmp = readl(port_mmio + PORT_CMD);
+ if (((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS)) ||
+ (tmp & PORT_CMD_HPCP)) {
+ if (hpriv->mask_port_ext & (1U << ap->port_no)) {
+ ata_port_info(ap,
+ "Ignoring external/hotplug capability\n");
+ return;
+ }
+ ap->pflags |= ATA_PFLAG_EXTERNAL;
+ }
}
-static void ahci_update_initial_lpm_policy(struct ata_port *ap,
- struct ahci_host_priv *hpriv)
+static void ahci_update_initial_lpm_policy(struct ata_port *ap)
{
+ struct ahci_host_priv *hpriv = ap->host->private_data;
int policy = CONFIG_SATA_MOBILE_LPM_POLICY;
+ /*
+ * AHCI contains a known incompatibility between LPM and hot-plug
+ * removal events, see 7.3.1 Hot Plug Removal Detection and Power
+ * Management Interaction in AHCI 1.3.1. Therefore, do not enable
+ * LPM if the port advertises itself as an external port.
+ */
+ if (ap->pflags & ATA_PFLAG_EXTERNAL) {
+ ap->flags |= ATA_FLAG_NO_LPM;
+ ap->target_lpm_policy = ATA_LPM_MAX_POWER;
+ return;
+ }
- /* Ignore processing for chipsets that don't use policy */
- if (!(hpriv->flags & AHCI_HFLAG_USE_LPM_POLICY))
+ /* If no Partial or no Slumber, we cannot support DIPM. */
+ if ((ap->host->flags & ATA_HOST_NO_PART) ||
+ (ap->host->flags & ATA_HOST_NO_SSC)) {
+ ata_port_dbg(ap, "Host does not support DIPM\n");
+ ap->flags |= ATA_FLAG_NO_DIPM;
+ }
+
+ /* If no LPM states are supported by the HBA, do not bother with LPM */
+ if ((ap->host->flags & ATA_HOST_NO_PART) &&
+ (ap->host->flags & ATA_HOST_NO_SSC) &&
+ (ap->host->flags & ATA_HOST_NO_DEVSLP)) {
+ ata_port_dbg(ap,
+ "No LPM states supported, forcing LPM max_power\n");
+ ap->flags |= ATA_FLAG_NO_LPM;
+ ap->target_lpm_policy = ATA_LPM_MAX_POWER;
return;
+ }
/* user modified policy via module param */
if (mobile_lpm_policy != -1) {
@@ -1609,15 +1828,12 @@ static void ahci_update_initial_lpm_policy(struct ata_port *ap,
goto update_policy;
}
-#ifdef CONFIG_ACPI
- if (policy > ATA_LPM_MED_POWER &&
- (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) {
+ if (policy > ATA_LPM_MED_POWER && pm_suspend_default_s2idle()) {
if (hpriv->cap & HOST_CAP_PART)
policy = ATA_LPM_MIN_POWER_WITH_PARTIAL;
else if (hpriv->cap & HOST_CAP_SSC)
policy = ATA_LPM_MIN_POWER;
}
-#endif
update_policy:
if (policy >= ATA_LPM_UNKNOWN && policy <= ATA_LPM_MIN_POWER)
@@ -1626,17 +1842,9 @@ update_policy:
static void ahci_intel_pcs_quirk(struct pci_dev *pdev, struct ahci_host_priv *hpriv)
{
- const struct pci_device_id *id = pci_match_id(ahci_pci_tbl, pdev);
u16 tmp16;
- /*
- * Only apply the 6-port PCS quirk for known legacy platforms.
- */
- if (!id || id->vendor != PCI_VENDOR_ID_INTEL)
- return;
-
- /* Skip applying the quirk on Denverton and beyond */
- if (((enum board_ids) id->driver_data) >= board_ahci_pcs7)
+ if (!(hpriv->flags & AHCI_HFLAG_INTEL_PCS_QUIRK))
return;
/*
@@ -1738,7 +1946,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* AHCI controllers often implement SFF compatible interface.
* Grab all PCI BARs just in case.
*/
- rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME);
+ rc = pcim_request_all_regions(pdev, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
if (rc)
@@ -1762,7 +1970,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ahci_sb600_enable_64bit(pdev))
hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
- hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
+ hpriv->mmio = pcim_iomap(pdev, ahci_pci_bar, 0);
+ if (!hpriv->mmio)
+ return -ENOMEM;
/* detect remapped nvme devices */
ahci_remap_check(pdev, ahci_pci_bar, hpriv);
@@ -1771,10 +1981,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
&dev_attr_remapped_nvme.attr,
NULL);
- /* must set flag prior to save config in order to take effect */
- if (ahci_broken_devslp(pdev))
- hpriv->flags |= AHCI_HFLAG_NO_DEVSLP;
-
#ifdef CONFIG_ARM64
if (pdev->vendor == PCI_VENDOR_ID_HUAWEI &&
pdev->device == 0xa235 &&
@@ -1788,12 +1994,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* save initial config */
ahci_pci_save_initial_config(pdev, hpriv);
- /*
- * If platform firmware failed to enable ports, try to enable
- * them here.
- */
- ahci_intel_pcs_quirk(pdev, hpriv);
-
/* prepare host */
if (hpriv->cap & HOST_CAP_NCQ) {
pi.flags |= ATA_FLAG_NCQ;
@@ -1856,14 +2056,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
- if (!host)
- return -ENOMEM;
+ if (!host) {
+ rc = -ENOMEM;
+ goto err_rm_sysfs_file;
+ }
host->private_data = hpriv;
- if (ahci_init_msi(pdev, n_ports, hpriv) < 0) {
- /* legacy intx interrupts */
- pci_intx(pdev, 1);
- }
+ ahci_init_irq(pdev, n_ports, hpriv);
+
hpriv->irq = pci_irq_vector(pdev, 0);
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
@@ -1871,6 +2071,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
else
dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");
+ if (!(hpriv->cap & HOST_CAP_PART))
+ host->flags |= ATA_HOST_NO_PART;
+
+ if (!(hpriv->cap & HOST_CAP_SSC))
+ host->flags |= ATA_HOST_NO_SSC;
+
+ if (!(hpriv->cap2 & HOST_CAP2_SDS))
+ host->flags |= ATA_HOST_NO_DEVSLP;
+
if (pi.flags & ATA_FLAG_EM)
ahci_reset_em(host);
@@ -1885,7 +2094,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
- ahci_update_initial_lpm_policy(ap, hpriv);
+ ahci_mark_external_port(ap);
+
+ ahci_update_initial_lpm_policy(ap);
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
@@ -1899,13 +2110,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
ahci_gtf_filter_workaround(host);
/* initialize adapter */
- rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64);
+ rc = ahci_configure_dma_masks(pdev, hpriv);
if (rc)
- return rc;
+ goto err_rm_sysfs_file;
- rc = ahci_reset_controller(host);
+ rc = ahci_pci_reset_controller(host);
if (rc)
- return rc;
+ goto err_rm_sysfs_file;
ahci_pci_init_controller(host);
ahci_pci_print_info(host);
@@ -1914,10 +2125,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rc = ahci_host_activate(host, &ahci_sht);
if (rc)
- return rc;
+ goto err_rm_sysfs_file;
pm_runtime_put_noidle(&pdev->dev);
return 0;
+
+err_rm_sysfs_file:
+ sysfs_remove_file_from_group(&pdev->dev.kobj,
+ &dev_attr_remapped_nvme.attr, NULL);
+ return rc;
}
static void ahci_shutdown_one(struct pci_dev *pdev)
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index ad11a4c52fbe..293b7fb216b5 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -24,6 +24,7 @@
#include <linux/libata.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
+#include <linux/bits.h>
/* Enclosure Management Control */
#define EM_CTRL_MSG_TYPE 0x000f0000
@@ -38,7 +39,6 @@
enum {
AHCI_MAX_PORTS = 32,
- AHCI_MAX_CLKS = 5,
AHCI_MAX_SG = 168, /* hardware max is 64K */
AHCI_DMA_BOUNDARY = 0xffffffff,
AHCI_MAX_CMDS = 32,
@@ -54,12 +54,12 @@ enum {
AHCI_PORT_PRIV_FBS_DMA_SZ = AHCI_CMD_SLOT_SZ +
AHCI_CMD_TBL_AR_SZ +
(AHCI_RX_FIS_SZ * 16),
- AHCI_IRQ_ON_SG = (1 << 31),
- AHCI_CMD_ATAPI = (1 << 5),
- AHCI_CMD_WRITE = (1 << 6),
- AHCI_CMD_PREFETCH = (1 << 7),
- AHCI_CMD_RESET = (1 << 8),
- AHCI_CMD_CLR_BUSY = (1 << 10),
+ AHCI_IRQ_ON_SG = BIT(31),
+ AHCI_CMD_ATAPI = BIT(5),
+ AHCI_CMD_WRITE = BIT(6),
+ AHCI_CMD_PREFETCH = BIT(7),
+ AHCI_CMD_RESET = BIT(8),
+ AHCI_CMD_CLR_BUSY = BIT(10),
RX_FIS_PIO_SETUP = 0x20, /* offset of PIO Setup FIS data */
RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
@@ -77,37 +77,37 @@ enum {
HOST_CAP2 = 0x24, /* host capabilities, extended */
/* HOST_CTL bits */
- HOST_RESET = (1 << 0), /* reset controller; self-clear */
- HOST_IRQ_EN = (1 << 1), /* global IRQ enable */
- HOST_MRSM = (1 << 2), /* MSI Revert to Single Message */
- HOST_AHCI_EN = (1 << 31), /* AHCI enabled */
+ HOST_RESET = BIT(0), /* reset controller; self-clear */
+ HOST_IRQ_EN = BIT(1), /* global IRQ enable */
+ HOST_MRSM = BIT(2), /* MSI Revert to Single Message */
+ HOST_AHCI_EN = BIT(31), /* AHCI enabled */
/* HOST_CAP bits */
- HOST_CAP_SXS = (1 << 5), /* Supports External SATA */
- HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */
- HOST_CAP_CCC = (1 << 7), /* Command Completion Coalescing */
- HOST_CAP_PART = (1 << 13), /* Partial state capable */
- HOST_CAP_SSC = (1 << 14), /* Slumber state capable */
- HOST_CAP_PIO_MULTI = (1 << 15), /* PIO multiple DRQ support */
- HOST_CAP_FBS = (1 << 16), /* FIS-based switching support */
- HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */
- HOST_CAP_ONLY = (1 << 18), /* Supports AHCI mode only */
- HOST_CAP_CLO = (1 << 24), /* Command List Override support */
- HOST_CAP_LED = (1 << 25), /* Supports activity LED */
- HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */
- HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */
- HOST_CAP_MPS = (1 << 28), /* Mechanical presence switch */
- HOST_CAP_SNTF = (1 << 29), /* SNotification register */
- HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */
- HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */
+ HOST_CAP_SXS = BIT(5), /* Supports External SATA */
+ HOST_CAP_EMS = BIT(6), /* Enclosure Management support */
+ HOST_CAP_CCC = BIT(7), /* Command Completion Coalescing */
+ HOST_CAP_PART = BIT(13), /* Partial state capable */
+ HOST_CAP_SSC = BIT(14), /* Slumber state capable */
+ HOST_CAP_PIO_MULTI = BIT(15), /* PIO multiple DRQ support */
+ HOST_CAP_FBS = BIT(16), /* FIS-based switching support */
+ HOST_CAP_PMP = BIT(17), /* Port Multiplier support */
+ HOST_CAP_ONLY = BIT(18), /* Supports AHCI mode only */
+ HOST_CAP_CLO = BIT(24), /* Command List Override support */
+ HOST_CAP_LED = BIT(25), /* Supports activity LED */
+ HOST_CAP_ALPM = BIT(26), /* Aggressive Link PM support */
+ HOST_CAP_SSS = BIT(27), /* Staggered Spin-up */
+ HOST_CAP_MPS = BIT(28), /* Mechanical presence switch */
+ HOST_CAP_SNTF = BIT(29), /* SNotification register */
+ HOST_CAP_NCQ = BIT(30), /* Native Command Queueing */
+ HOST_CAP_64 = BIT(31), /* PCI DAC (64-bit DMA) support */
/* HOST_CAP2 bits */
- HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */
- HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */
- HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */
- HOST_CAP2_SDS = (1 << 3), /* Support device sleep */
- HOST_CAP2_SADM = (1 << 4), /* Support aggressive DevSlp */
- HOST_CAP2_DESO = (1 << 5), /* DevSlp from slumber only */
+ HOST_CAP2_BOH = BIT(0), /* BIOS/OS handoff supported */
+ HOST_CAP2_NVMHCI = BIT(1), /* NVMHCI supported */
+ HOST_CAP2_APST = BIT(2), /* Automatic partial to slumber */
+ HOST_CAP2_SDS = BIT(3), /* Support device sleep */
+ HOST_CAP2_SADM = BIT(4), /* Support aggressive DevSlp */
+ HOST_CAP2_DESO = BIT(5), /* DevSlp from slumber only */
/* registers for each SATA port */
PORT_LST_ADDR = 0x00, /* command list DMA addr */
@@ -129,24 +129,24 @@ enum {
PORT_DEVSLP = 0x44, /* device sleep */
/* PORT_IRQ_{STAT,MASK} bits */
- PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */
- PORT_IRQ_TF_ERR = (1 << 30), /* task file error */
- PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */
- PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */
- PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */
- PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */
- PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */
- PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */
-
- PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */
- PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */
- PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */
- PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */
- PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */
- PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */
- PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */
- PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */
- PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */
+ PORT_IRQ_COLD_PRES = BIT(31), /* cold presence detect */
+ PORT_IRQ_TF_ERR = BIT(30), /* task file error */
+ PORT_IRQ_HBUS_ERR = BIT(29), /* host bus fatal error */
+ PORT_IRQ_HBUS_DATA_ERR = BIT(28), /* host bus data error */
+ PORT_IRQ_IF_ERR = BIT(27), /* interface fatal error */
+ PORT_IRQ_IF_NONFATAL = BIT(26), /* interface non-fatal error */
+ PORT_IRQ_OVERFLOW = BIT(24), /* xfer exhausted available S/G */
+ PORT_IRQ_BAD_PMP = BIT(23), /* incorrect port multiplier */
+
+ PORT_IRQ_PHYRDY = BIT(22), /* PhyRdy changed */
+ PORT_IRQ_DMPS = BIT(7), /* mechanical presence status */
+ PORT_IRQ_CONNECT = BIT(6), /* port connect change status */
+ PORT_IRQ_SG_DONE = BIT(5), /* descriptor processed */
+ PORT_IRQ_UNK_FIS = BIT(4), /* unknown FIS rx'd */
+ PORT_IRQ_SDB_FIS = BIT(3), /* Set Device Bits FIS rx'd */
+ PORT_IRQ_DMAS_FIS = BIT(2), /* DMA Setup FIS rx'd */
+ PORT_IRQ_PIOS_FIS = BIT(1), /* PIO Setup FIS rx'd */
+ PORT_IRQ_D2H_REG_FIS = BIT(0), /* D2H Register FIS rx'd */
PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR |
PORT_IRQ_IF_ERR |
@@ -162,34 +162,40 @@ enum {
PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS,
/* PORT_CMD bits */
- PORT_CMD_ASP = (1 << 27), /* Aggressive Slumber/Partial */
- PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */
- PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */
- PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */
- PORT_CMD_ESP = (1 << 21), /* External Sata Port */
- PORT_CMD_HPCP = (1 << 18), /* HotPlug Capable Port */
- PORT_CMD_PMP = (1 << 17), /* PMP attached */
- PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */
- PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */
- PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */
- PORT_CMD_CLO = (1 << 3), /* Command list override */
- PORT_CMD_POWER_ON = (1 << 2), /* Power up device */
- PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */
- PORT_CMD_START = (1 << 0), /* Enable port DMA engine */
-
- PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */
- PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */
- PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */
- PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */
+ PORT_CMD_ASP = BIT(27), /* Aggressive Slumber/Partial */
+ PORT_CMD_ALPE = BIT(26), /* Aggressive Link PM enable */
+ PORT_CMD_ATAPI = BIT(24), /* Device is ATAPI */
+ PORT_CMD_FBSCP = BIT(22), /* FBS Capable Port */
+ PORT_CMD_ESP = BIT(21), /* External Sata Port */
+ PORT_CMD_CPD = BIT(20), /* Cold Presence Detection */
+ PORT_CMD_MPSP = BIT(19), /* Mechanical Presence Switch */
+ PORT_CMD_HPCP = BIT(18), /* HotPlug Capable Port */
+ PORT_CMD_PMP = BIT(17), /* PMP attached */
+ PORT_CMD_LIST_ON = BIT(15), /* cmd list DMA engine running */
+ PORT_CMD_FIS_ON = BIT(14), /* FIS DMA engine running */
+ PORT_CMD_FIS_RX = BIT(4), /* Enable FIS receive DMA engine */
+ PORT_CMD_CLO = BIT(3), /* Command list override */
+ PORT_CMD_POWER_ON = BIT(2), /* Power up device */
+ PORT_CMD_SPIN_UP = BIT(1), /* Spin up device */
+ PORT_CMD_START = BIT(0), /* Enable port DMA engine */
+
+ PORT_CMD_ICC_MASK = (0xfu << 28), /* i/f ICC state mask */
+ PORT_CMD_ICC_ACTIVE = (0x1u << 28), /* Put i/f in active state */
+ PORT_CMD_ICC_PARTIAL = (0x2u << 28), /* Put i/f in partial state */
+ PORT_CMD_ICC_SLUMBER = (0x6u << 28), /* Put i/f in slumber state */
+
+ /* PORT_CMD capabilities mask */
+ PORT_CMD_CAP = PORT_CMD_HPCP | PORT_CMD_MPSP |
+ PORT_CMD_CPD | PORT_CMD_ESP | PORT_CMD_FBSCP,
/* PORT_FBS bits */
PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */
PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */
PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */
PORT_FBS_DEV_MASK = (0xf << PORT_FBS_DEV_OFFSET), /* FBS.DEV */
- PORT_FBS_SDE = (1 << 2), /* FBS single device error */
- PORT_FBS_DEC = (1 << 1), /* FBS device error clear */
- PORT_FBS_EN = (1 << 0), /* Enable FBS */
+ PORT_FBS_SDE = BIT(2), /* FBS single device error */
+ PORT_FBS_DEC = BIT(1), /* FBS device error clear */
+ PORT_FBS_EN = BIT(0), /* Enable FBS */
/* PORT_DEVSLP bits */
PORT_DEVSLP_DM_OFFSET = 25, /* DITO multiplier offset */
@@ -197,50 +203,50 @@ enum {
PORT_DEVSLP_DITO_OFFSET = 15, /* DITO offset */
PORT_DEVSLP_MDAT_OFFSET = 10, /* Minimum assertion time */
PORT_DEVSLP_DETO_OFFSET = 2, /* DevSlp exit timeout */
- PORT_DEVSLP_DSP = (1 << 1), /* DevSlp present */
- PORT_DEVSLP_ADSE = (1 << 0), /* Aggressive DevSlp enable */
+ PORT_DEVSLP_DSP = BIT(1), /* DevSlp present */
+ PORT_DEVSLP_ADSE = BIT(0), /* Aggressive DevSlp enable */
/* hpriv->flags bits */
#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
- AHCI_HFLAG_NO_NCQ = (1 << 0),
- AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */
- AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */
- AHCI_HFLAG_32BIT_ONLY = (1 << 3), /* force 32bit */
- AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */
- AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */
- AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */
- AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
- AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */
- AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */
- AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as
- link offline */
- AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */
- AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */
- AHCI_HFLAG_YES_FBS = (1 << 14), /* force FBS cap on */
- AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
- port start (wait until
- error-handling stage) */
- AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
- AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
+ AHCI_HFLAG_NO_NCQ = BIT(0),
+ AHCI_HFLAG_IGN_IRQ_IF_ERR = BIT(1), /* ignore IRQ_IF_ERR */
+ AHCI_HFLAG_IGN_SERR_INTERNAL = BIT(2), /* ignore SERR_INTERNAL */
+ AHCI_HFLAG_32BIT_ONLY = BIT(3), /* force 32bit */
+ AHCI_HFLAG_MV_PATA = BIT(4), /* PATA port */
+ AHCI_HFLAG_NO_MSI = BIT(5), /* no PCI MSI */
+ AHCI_HFLAG_NO_PMP = BIT(6), /* no PMP */
+ AHCI_HFLAG_SECT255 = BIT(8), /* max 255 sectors */
+ AHCI_HFLAG_YES_NCQ = BIT(9), /* force NCQ cap on */
+ AHCI_HFLAG_NO_SUSPEND = BIT(10), /* don't suspend */
+ AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = BIT(11), /* treat SRST timeout as
+ link offline */
+ AHCI_HFLAG_NO_SNTF = BIT(12), /* no sntf */
+ AHCI_HFLAG_NO_FPDMA_AA = BIT(13), /* no FPDMA AA */
+ AHCI_HFLAG_YES_FBS = BIT(14), /* force FBS cap on */
+ AHCI_HFLAG_DELAY_ENGINE = BIT(15), /* do not start engine on
+ port start (wait until
+ error-handling stage) */
+ AHCI_HFLAG_NO_DEVSLP = BIT(17), /* no device sleep */
+ AHCI_HFLAG_NO_FBS = BIT(18), /* no FBS */
#ifdef CONFIG_PCI_MSI
- AHCI_HFLAG_MULTI_MSI = (1 << 20), /* per-port MSI(-X) */
+ AHCI_HFLAG_MULTI_MSI = BIT(20), /* per-port MSI(-X) */
#else
/* compile out MSI infrastructure */
AHCI_HFLAG_MULTI_MSI = 0,
#endif
- AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */
- AHCI_HFLAG_YES_ALPM = (1 << 23), /* force ALPM cap on */
- AHCI_HFLAG_NO_WRITE_TO_RO = (1 << 24), /* don't write to read
- only registers */
- AHCI_HFLAG_USE_LPM_POLICY = (1 << 25), /* chipset that should use
- SATA_MOBILE_LPM_POLICY
- as default lpm_policy */
- AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during
- suspend/resume */
- AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */
+ AHCI_HFLAG_WAKE_BEFORE_STOP = BIT(22), /* wake before DMA stop */
+ AHCI_HFLAG_YES_ALPM = BIT(23), /* force ALPM cap on */
+ AHCI_HFLAG_NO_WRITE_TO_RO = BIT(24), /* don't write to read
+ only registers */
+ AHCI_HFLAG_SUSPEND_PHYS = BIT(25), /* handle PHYs during
+ suspend/resume */
+ AHCI_HFLAG_NO_SXS = BIT(26), /* SXS not supported */
+ AHCI_HFLAG_43BIT_ONLY = BIT(27), /* 43bit DMA addr limit */
+ AHCI_HFLAG_INTEL_PCS_QUIRK = BIT(28), /* apply Intel PCS quirk */
+ AHCI_HFLAG_ATAPI_DMA_QUIRK = BIT(29), /* force ATAPI to use DMA */
/* ap->flags bits */
@@ -252,26 +258,26 @@ enum {
PCS_7 = 0x94, /* 7+ port PCS (Denverton) */
/* em constants */
- EM_MAX_SLOTS = 8,
+ EM_MAX_SLOTS = SATA_PMP_MAX_PORTS,
EM_MAX_RETRY = 5,
/* em_ctl bits */
- EM_CTL_RST = (1 << 9), /* Reset */
- EM_CTL_TM = (1 << 8), /* Transmit Message */
- EM_CTL_MR = (1 << 0), /* Message Received */
- EM_CTL_ALHD = (1 << 26), /* Activity LED */
- EM_CTL_XMT = (1 << 25), /* Transmit Only */
- EM_CTL_SMB = (1 << 24), /* Single Message Buffer */
- EM_CTL_SGPIO = (1 << 19), /* SGPIO messages supported */
- EM_CTL_SES = (1 << 18), /* SES-2 messages supported */
- EM_CTL_SAFTE = (1 << 17), /* SAF-TE messages supported */
- EM_CTL_LED = (1 << 16), /* LED messages supported */
+ EM_CTL_RST = BIT(9), /* Reset */
+ EM_CTL_TM = BIT(8), /* Transmit Message */
+ EM_CTL_MR = BIT(0), /* Message Received */
+ EM_CTL_ALHD = BIT(26), /* Activity LED */
+ EM_CTL_XMT = BIT(25), /* Transmit Only */
+ EM_CTL_SMB = BIT(24), /* Single Message Buffer */
+ EM_CTL_SGPIO = BIT(19), /* SGPIO messages supported */
+ EM_CTL_SES = BIT(18), /* SES-2 messages supported */
+ EM_CTL_SAFTE = BIT(17), /* SAF-TE messages supported */
+ EM_CTL_LED = BIT(16), /* LED messages supported */
/* em message type */
- EM_MSG_TYPE_LED = (1 << 0), /* LED */
- EM_MSG_TYPE_SAFTE = (1 << 1), /* SAF-TE */
- EM_MSG_TYPE_SES2 = (1 << 2), /* SES-2 */
- EM_MSG_TYPE_SGPIO = (1 << 3), /* SGPIO */
+ EM_MSG_TYPE_LED = BIT(0), /* LED */
+ EM_MSG_TYPE_SAFTE = BIT(1), /* SAF-TE */
+ EM_MSG_TYPE_SES2 = BIT(2), /* SES-2 */
+ EM_MSG_TYPE_SGPIO = BIT(3), /* SGPIO */
};
struct ahci_cmd_hdr {
@@ -323,8 +329,8 @@ struct ahci_port_priv {
struct ahci_host_priv {
/* Input fields */
unsigned int flags; /* AHCI_HFLAG_* */
- u32 force_port_map; /* force port map */
- u32 mask_port_map; /* mask out particular bits */
+ u32 mask_port_map; /* Mask of valid ports */
+ u32 mask_port_ext; /* Mask of ports ext capability */
void __iomem * mmio; /* bus-independent mem map */
u32 cap; /* cap to use */
@@ -334,12 +340,15 @@ struct ahci_host_priv {
u32 saved_cap; /* saved initial cap */
u32 saved_cap2; /* saved initial cap2 */
u32 saved_port_map; /* saved initial port_map */
+ u32 saved_port_cap[AHCI_MAX_PORTS]; /* saved port_cap */
u32 em_loc; /* enclosure management location */
u32 em_buf_sz; /* EM buffer size in byte */
u32 em_msg_type; /* EM message type */
u32 remapped_nvme; /* NVMe remapped device count */
bool got_runtime_pm; /* Did we do pm_runtime_get? */
- struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
+ unsigned int n_clks;
+ struct clk_bulk_data *clks; /* Optional */
+ unsigned int f_rsts;
struct reset_control *rsts; /* Optional */
struct regulator **target_pwrs; /* Optional */
struct regulator *ahci_regulator;/* Optional */
@@ -372,6 +381,21 @@ struct ahci_host_priv {
int port);
};
+/*
+ * Return true if a port should be ignored because it is excluded from
+ * the host port map.
+ */
+static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv,
+ unsigned int portid)
+{
+ if (portid >= hpriv->nports)
+ return true;
+ /* mask_port_map not set means that all ports are available */
+ if (!hpriv->mask_port_map)
+ return false;
+ return !(hpriv->mask_port_map & (1 << portid));
+}
+
extern int ahci_ignore_sss;
extern const struct attribute_group *ahci_shost_groups[];
@@ -389,8 +413,8 @@ extern const struct attribute_group *ahci_sdev_groups[];
.shost_groups = ahci_shost_groups, \
.sdev_groups = ahci_sdev_groups, \
.change_queue_depth = ata_scsi_change_queue_depth, \
- .tag_alloc_policy = BLK_TAG_ALLOC_RR, \
- .slave_configure = ata_scsi_slave_config
+ .tag_alloc_policy_rr = true, \
+ .sdev_configure = ata_scsi_sdev_configure
extern struct ata_port_operations ahci_ops;
extern struct ata_port_operations ahci_platform_ops;
@@ -422,14 +446,13 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
struct ata_port_info *pi);
int ahci_reset_em(struct ata_host *host);
void ahci_print_info(struct ata_host *host, const char *scc_s);
-int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht);
+int ahci_host_activate(struct ata_host *host, const struct scsi_host_template *sht);
void ahci_error_handler(struct ata_port *ap);
u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked);
-static inline void __iomem *__ahci_port_base(struct ata_host *host,
+static inline void __iomem *__ahci_port_base(struct ahci_host_priv *hpriv,
unsigned int port_no)
{
- struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
return mmio + 0x100 + (port_no * 0x80);
@@ -437,7 +460,9 @@ static inline void __iomem *__ahci_port_base(struct ata_host *host,
static inline void __iomem *ahci_port_base(struct ata_port *ap)
{
- return __ahci_port_base(ap->host, ap->port_no);
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+
+ return __ahci_port_base(hpriv, ap->port_no);
}
static inline int ahci_nr_ports(u32 cap)
diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
index f61795c546cf..29be74fedcf0 100644
--- a/drivers/ata/ahci_brcm.c
+++ b/drivers/ata/ahci_brcm.c
@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,
/* Re-initialize and calibrate the PHY */
for (i = 0; i < hpriv->nports; i++) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
rc = phy_init(hpriv->phys[i]);
if (rc)
goto disable_phys;
@@ -417,7 +420,7 @@ out_disable_clks:
return ret;
}
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -437,7 +440,6 @@ static int brcm_ahci_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct brcm_ahci_priv *priv;
struct ahci_host_priv *hpriv;
- struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -448,11 +450,10 @@ static int brcm_ahci_probe(struct platform_device *pdev)
if (!of_id)
return -ENODEV;
- priv->version = (enum brcm_ahci_version)of_id->data;
+ priv->version = (unsigned long)of_id->data;
priv->dev = dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "top-ctrl");
- priv->top_ctrl = devm_ioremap_resource(dev, res);
+ priv->top_ctrl = devm_platform_ioremap_resource_byname(pdev, "top-ctrl");
if (IS_ERR(priv->top_ctrl))
return PTR_ERR(priv->top_ctrl);
@@ -544,7 +545,7 @@ out_reset:
return ret;
}
-static int brcm_ahci_remove(struct platform_device *pdev)
+static void brcm_ahci_remove(struct platform_device *pdev)
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
struct ahci_host_priv *hpriv = host->private_data;
@@ -552,7 +553,7 @@ static int brcm_ahci_remove(struct platform_device *pdev)
brcm_sata_phys_disable(priv);
- return ata_platform_remove_one(pdev);
+ ata_platform_remove_one(pdev);
}
static void brcm_ahci_shutdown(struct platform_device *pdev)
diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
index cb24ecf36faf..2d6a08c23d6a 100644
--- a/drivers/ata/ahci_ceva.c
+++ b/drivers/ata/ahci_ceva.c
@@ -10,7 +10,7 @@
#include <linux/kernel.h>
#include <linux/libata.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include "ahci.h"
@@ -88,7 +88,6 @@ struct ceva_ahci_priv {
u32 axicc;
bool is_cci_enabled;
int flags;
- struct reset_control *rst;
};
static unsigned int ceva_ahci_read_id(struct ata_device *dev,
@@ -185,10 +184,70 @@ static void ahci_ceva_setup(struct ahci_host_priv *hpriv)
}
}
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
+static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+{
+ int rc, i;
+
+ rc = ahci_platform_enable_regulators(hpriv);
+ if (rc)
+ return rc;
+
+ rc = ahci_platform_enable_clks(hpriv);
+ if (rc)
+ goto disable_regulator;
+
+ /* Assert the controller reset */
+ rc = ahci_platform_assert_rsts(hpriv);
+ if (rc)
+ goto disable_clks;
+
+ for (i = 0; i < hpriv->nports; i++) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
+ rc = phy_init(hpriv->phys[i]);
+ if (rc)
+ goto disable_rsts;
+ }
+
+ /* De-assert the controller reset */
+ ahci_platform_deassert_rsts(hpriv);
+
+ for (i = 0; i < hpriv->nports; i++) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
+ rc = phy_power_on(hpriv->phys[i]);
+ if (rc) {
+ phy_exit(hpriv->phys[i]);
+ goto disable_phys;
+ }
+ }
+
+ return 0;
+
+disable_rsts:
+ ahci_platform_deassert_rsts(hpriv);
+
+disable_phys:
+ while (--i >= 0) {
+ phy_power_off(hpriv->phys[i]);
+ phy_exit(hpriv->phys[i]);
+ }
+
+disable_clks:
+ ahci_platform_disable_clks(hpriv);
+
+disable_regulator:
+ ahci_platform_disable_regulators(hpriv);
+
+ return rc;
+}
+
static int ceva_ahci_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -203,47 +262,19 @@ static int ceva_ahci_probe(struct platform_device *pdev)
return -ENOMEM;
cevapriv->ahci_pdev = pdev;
-
- cevapriv->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
- NULL);
- if (IS_ERR(cevapriv->rst))
- dev_err_probe(&pdev->dev, PTR_ERR(cevapriv->rst),
- "failed to get reset\n");
-
hpriv = ahci_platform_get_resources(pdev, 0);
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
- if (!cevapriv->rst) {
- rc = ahci_platform_enable_resources(hpriv);
- if (rc)
- return rc;
- } else {
- int i;
-
- rc = ahci_platform_enable_clks(hpriv);
- if (rc)
- return rc;
- /* Assert the controller reset */
- reset_control_assert(cevapriv->rst);
-
- for (i = 0; i < hpriv->nports; i++) {
- rc = phy_init(hpriv->phys[i]);
- if (rc)
- return rc;
- }
-
- /* De-assert the controller reset */
- reset_control_deassert(cevapriv->rst);
+ hpriv->rsts = devm_reset_control_get_optional_exclusive(&pdev->dev,
+ NULL);
+ if (IS_ERR(hpriv->rsts))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hpriv->rsts),
+ "failed to get reset\n");
- for (i = 0; i < hpriv->nports; i++) {
- rc = phy_power_on(hpriv->phys[i]);
- if (rc) {
- phy_exit(hpriv->phys[i]);
- return rc;
- }
- }
- }
+ rc = ceva_ahci_platform_enable_resources(hpriv);
+ if (rc)
+ return rc;
if (of_property_read_bool(np, "ceva,broken-gen2"))
cevapriv->flags = CEVA_FLAG_BROKEN_GEN2;
@@ -252,52 +283,60 @@ static int ceva_ahci_probe(struct platform_device *pdev)
if (of_property_read_u8_array(np, "ceva,p0-cominit-params",
(u8 *)&cevapriv->pp2c[0], 4) < 0) {
dev_warn(dev, "ceva,p0-cominit-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
if (of_property_read_u8_array(np, "ceva,p1-cominit-params",
(u8 *)&cevapriv->pp2c[1], 4) < 0) {
dev_warn(dev, "ceva,p1-cominit-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
/* Read OOB timing value for COMWAKE from device-tree*/
if (of_property_read_u8_array(np, "ceva,p0-comwake-params",
(u8 *)&cevapriv->pp3c[0], 4) < 0) {
dev_warn(dev, "ceva,p0-comwake-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
if (of_property_read_u8_array(np, "ceva,p1-comwake-params",
(u8 *)&cevapriv->pp3c[1], 4) < 0) {
dev_warn(dev, "ceva,p1-comwake-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
/* Read phy BURST timing value from device-tree */
if (of_property_read_u8_array(np, "ceva,p0-burst-params",
(u8 *)&cevapriv->pp4c[0], 4) < 0) {
dev_warn(dev, "ceva,p0-burst-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
if (of_property_read_u8_array(np, "ceva,p1-burst-params",
(u8 *)&cevapriv->pp4c[1], 4) < 0) {
dev_warn(dev, "ceva,p1-burst-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
/* Read phy RETRY interval timing value from device-tree */
if (of_property_read_u16_array(np, "ceva,p0-retry-params",
(u16 *)&cevapriv->pp5c[0], 2) < 0) {
dev_warn(dev, "ceva,p0-retry-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
if (of_property_read_u16_array(np, "ceva,p1-retry-params",
(u16 *)&cevapriv->pp5c[1], 2) < 0) {
dev_warn(dev, "ceva,p1-retry-params property not defined\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto disable_resources;
}
/*
@@ -335,7 +374,7 @@ static int __maybe_unused ceva_ahci_resume(struct device *dev)
struct ahci_host_priv *hpriv = host->private_data;
int rc;
- rc = ahci_platform_enable_resources(hpriv);
+ rc = ceva_ahci_platform_enable_resources(hpriv);
if (rc)
return rc;
diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c
index 052c28e250aa..f97566c420f8 100644
--- a/drivers/ata/ahci_da850.c
+++ b/drivers/ata/ahci_da850.c
@@ -137,13 +137,13 @@ static int ahci_da850_hardreset(struct ata_link *link,
static struct ata_port_operations ahci_da850_port_ops = {
.inherits = &ahci_platform_ops,
- .softreset = ahci_da850_softreset,
+ .reset.softreset = ahci_da850_softreset,
/*
* No need to override .pmp_softreset - it's only used for actual
* PMP-enabled ports.
*/
- .hardreset = ahci_da850_hardreset,
- .pmp_hardreset = ahci_da850_hardreset,
+ .reset.hardreset = ahci_da850_hardreset,
+ .pmp_reset.hardreset = ahci_da850_hardreset,
};
static const struct ata_port_info ahci_da850_port_info = {
@@ -153,7 +153,7 @@ static const struct ata_port_info ahci_da850_port_info = {
.port_ops = &ahci_da850_port_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -163,7 +163,6 @@ static int ahci_da850_probe(struct platform_device *pdev)
struct ahci_host_priv *hpriv;
void __iomem *pwrdn_reg;
struct resource *res;
- struct clk *clk;
u32 mpy;
int rc;
@@ -172,36 +171,28 @@ static int ahci_da850_probe(struct platform_device *pdev)
return PTR_ERR(hpriv);
/*
- * Internally ahci_platform_get_resources() calls clk_get(dev, NULL)
- * when trying to obtain the functional clock. This SATA controller
- * uses two clocks for which we specify two connection ids. If we don't
- * have the functional clock at this point - call clk_get() again with
- * con_id = "fck".
+ * Internally ahci_platform_get_resources() calls the bulk clocks
+ * get method or falls back to using a single clk_get_optional().
+ * This AHCI SATA controller uses two clocks: functional clock
+ * with "fck" connection id and external reference clock with
+ * "refclk" id. If we haven't got all of them re-try the clocks
+ * getting procedure with the explicitly specified ids.
*/
- if (!hpriv->clks[0]) {
- clk = clk_get(dev, "fck");
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- hpriv->clks[0] = clk;
- }
-
- /*
- * The second clock used by ahci-da850 is the external REFCLK. If we
- * didn't get it from ahci_platform_get_resources(), let's try to
- * specify the con_id in clk_get().
- */
- if (!hpriv->clks[1]) {
- clk = clk_get(dev, "refclk");
- if (IS_ERR(clk)) {
- dev_err(dev, "unable to obtain the reference clock");
- return -ENODEV;
- }
-
- hpriv->clks[1] = clk;
+ if (hpriv->n_clks < 2) {
+ hpriv->clks = devm_kcalloc(dev, 2, sizeof(*hpriv->clks), GFP_KERNEL);
+ if (!hpriv->clks)
+ return -ENOMEM;
+
+ hpriv->clks[0].id = "fck";
+ hpriv->clks[1].id = "refclk";
+ hpriv->n_clks = 2;
+
+ rc = devm_clk_bulk_get(dev, hpriv->n_clks, hpriv->clks);
+ if (rc)
+ return rc;
}
- mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1]));
+ mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1].clk));
if (mpy == 0) {
dev_err(dev, "invalid REFCLK multiplier value: 0x%x", mpy);
return -EINVAL;
diff --git a/drivers/ata/ahci_dm816.c b/drivers/ata/ahci_dm816.c
index 8a92112dcd59..93faed2cfeb6 100644
--- a/drivers/ata/ahci_dm816.c
+++ b/drivers/ata/ahci_dm816.c
@@ -69,12 +69,12 @@ static int ahci_dm816_phy_init(struct ahci_host_priv *hpriv, struct device *dev)
* keep-alive clock and the external reference clock. We need the
* rate of the latter to calculate the correct value of MPY bits.
*/
- if (!hpriv->clks[1]) {
+ if (hpriv->n_clks < 2) {
dev_err(dev, "reference clock not supplied\n");
return -EINVAL;
}
- refclk_rate = clk_get_rate(hpriv->clks[1]);
+ refclk_rate = clk_get_rate(hpriv->clks[1].clk);
if ((refclk_rate % 100) != 0) {
dev_err(dev, "reference clock rate must be divisible by 100\n");
return -EINVAL;
@@ -124,7 +124,7 @@ static int ahci_dm816_softreset(struct ata_link *link,
static struct ata_port_operations ahci_dm816_port_ops = {
.inherits = &ahci_platform_ops,
- .softreset = ahci_dm816_softreset,
+ .reset.softreset = ahci_dm816_softreset,
};
static const struct ata_port_info ahci_dm816_port_info = {
@@ -134,7 +134,7 @@ static const struct ata_port_info ahci_dm816_port_info = {
.port_ops = &ahci_dm816_port_ops,
};
-static struct scsi_host_template ahci_dm816_platform_sht = {
+static const struct scsi_host_template ahci_dm816_platform_sht = {
AHCI_SHT(AHCI_DM816_DRV_NAME),
};
diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c
new file mode 100644
index 000000000000..aec6d793f51a
--- /dev/null
+++ b/drivers/ata/ahci_dwc.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DWC AHCI SATA Platform driver
+ *
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ */
+
+#include <linux/ahci_platform.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/log2.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#include "ahci.h"
+
+#define DRV_NAME "ahci-dwc"
+
+#define AHCI_DWC_FBS_PMPN_MAX 15
+
+/* DWC AHCI SATA controller specific registers */
+#define AHCI_DWC_HOST_OOBR 0xbc
+#define AHCI_DWC_HOST_OOB_WE BIT(31)
+#define AHCI_DWC_HOST_CWMIN_MASK GENMASK(30, 24)
+#define AHCI_DWC_HOST_CWMAX_MASK GENMASK(23, 16)
+#define AHCI_DWC_HOST_CIMIN_MASK GENMASK(15, 8)
+#define AHCI_DWC_HOST_CIMAX_MASK GENMASK(7, 0)
+
+#define AHCI_DWC_HOST_GPCR 0xd0
+#define AHCI_DWC_HOST_GPSR 0xd4
+
+#define AHCI_DWC_HOST_TIMER1MS 0xe0
+#define AHCI_DWC_HOST_TIMV_MASK GENMASK(19, 0)
+
+#define AHCI_DWC_HOST_GPARAM1R 0xe8
+#define AHCI_DWC_HOST_ALIGN_M BIT(31)
+#define AHCI_DWC_HOST_RX_BUFFER BIT(30)
+#define AHCI_DWC_HOST_PHY_DATA_MASK GENMASK(29, 28)
+#define AHCI_DWC_HOST_PHY_RST BIT(27)
+#define AHCI_DWC_HOST_PHY_CTRL_MASK GENMASK(26, 21)
+#define AHCI_DWC_HOST_PHY_STAT_MASK GENMASK(20, 15)
+#define AHCI_DWC_HOST_LATCH_M BIT(14)
+#define AHCI_DWC_HOST_PHY_TYPE_MASK GENMASK(13, 11)
+#define AHCI_DWC_HOST_RET_ERR BIT(10)
+#define AHCI_DWC_HOST_AHB_ENDIAN_MASK GENMASK(9, 8)
+#define AHCI_DWC_HOST_S_HADDR BIT(7)
+#define AHCI_DWC_HOST_M_HADDR BIT(6)
+#define AHCI_DWC_HOST_S_HDATA_MASK GENMASK(5, 3)
+#define AHCI_DWC_HOST_M_HDATA_MASK GENMASK(2, 0)
+
+#define AHCI_DWC_HOST_GPARAM2R 0xec
+#define AHCI_DWC_HOST_FBS_MEM_S BIT(19)
+#define AHCI_DWC_HOST_FBS_PMPN_MASK GENMASK(17, 16)
+#define AHCI_DWC_HOST_FBS_SUP BIT(15)
+#define AHCI_DWC_HOST_DEV_CP BIT(14)
+#define AHCI_DWC_HOST_DEV_MP BIT(13)
+#define AHCI_DWC_HOST_ENCODE_M BIT(12)
+#define AHCI_DWC_HOST_RXOOB_CLK_M BIT(11)
+#define AHCI_DWC_HOST_RXOOB_M BIT(10)
+#define AHCI_DWC_HOST_TXOOB_M BIT(9)
+#define AHCI_DWC_HOST_RXOOB_M BIT(10)
+#define AHCI_DWC_HOST_RXOOB_CLK_MASK GENMASK(8, 0)
+
+#define AHCI_DWC_HOST_PPARAMR 0xf0
+#define AHCI_DWC_HOST_TX_MEM_M BIT(11)
+#define AHCI_DWC_HOST_TX_MEM_S BIT(10)
+#define AHCI_DWC_HOST_RX_MEM_M BIT(9)
+#define AHCI_DWC_HOST_RX_MEM_S BIT(8)
+#define AHCI_DWC_HOST_TXFIFO_DEPTH GENMASK(7, 4)
+#define AHCI_DWC_HOST_RXFIFO_DEPTH GENMASK(3, 0)
+
+#define AHCI_DWC_HOST_TESTR 0xf4
+#define AHCI_DWC_HOST_PSEL_MASK GENMASK(18, 16)
+#define AHCI_DWC_HOST_TEST_IF BIT(0)
+
+#define AHCI_DWC_HOST_VERSIONR 0xf8
+#define AHCI_DWC_HOST_IDR 0xfc
+
+#define AHCI_DWC_PORT_DMACR 0x70
+#define AHCI_DWC_PORT_RXABL_MASK GENMASK(15, 12)
+#define AHCI_DWC_PORT_TXABL_MASK GENMASK(11, 8)
+#define AHCI_DWC_PORT_RXTS_MASK GENMASK(7, 4)
+#define AHCI_DWC_PORT_TXTS_MASK GENMASK(3, 0)
+#define AHCI_DWC_PORT_PHYCR 0x74
+#define AHCI_DWC_PORT_PHYSR 0x78
+
+/* Baikal-T1 AHCI SATA specific registers */
+#define AHCI_BT1_HOST_PHYCR AHCI_DWC_HOST_GPCR
+#define AHCI_BT1_HOST_MPLM_MASK GENMASK(29, 23)
+#define AHCI_BT1_HOST_LOSDT_MASK GENMASK(22, 20)
+#define AHCI_BT1_HOST_CRR BIT(19)
+#define AHCI_BT1_HOST_CRW BIT(18)
+#define AHCI_BT1_HOST_CRCD BIT(17)
+#define AHCI_BT1_HOST_CRCA BIT(16)
+#define AHCI_BT1_HOST_CRDI_MASK GENMASK(15, 0)
+
+#define AHCI_BT1_HOST_PHYSR AHCI_DWC_HOST_GPSR
+#define AHCI_BT1_HOST_CRA BIT(16)
+#define AHCI_BT1_HOST_CRDO_MASK GENMASK(15, 0)
+
+struct ahci_dwc_plat_data {
+ unsigned int pflags;
+ unsigned int hflags;
+ int (*init)(struct ahci_host_priv *hpriv);
+ int (*reinit)(struct ahci_host_priv *hpriv);
+ void (*clear)(struct ahci_host_priv *hpriv);
+};
+
+struct ahci_dwc_host_priv {
+ const struct ahci_dwc_plat_data *pdata;
+ struct platform_device *pdev;
+
+ u32 timv;
+ u32 dmacr[AHCI_MAX_PORTS];
+};
+
+static int ahci_bt1_init(struct ahci_host_priv *hpriv)
+{
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+ int ret;
+
+ /* APB, application and reference clocks are required */
+ if (!ahci_platform_find_clk(hpriv, "pclk") ||
+ !ahci_platform_find_clk(hpriv, "aclk") ||
+ !ahci_platform_find_clk(hpriv, "ref")) {
+ dev_err(&dpriv->pdev->dev, "No system clocks specified\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Fully reset the SATA AXI and ref clocks domain to ensure the state
+ * machine is working from scratch especially if the reference clocks
+ * source has been changed.
+ */
+ ret = ahci_platform_assert_rsts(hpriv);
+ if (ret) {
+ dev_err(&dpriv->pdev->dev, "Couldn't assert the resets\n");
+ return ret;
+ }
+
+ ret = ahci_platform_deassert_rsts(hpriv);
+ if (ret) {
+ dev_err(&dpriv->pdev->dev, "Couldn't de-assert the resets\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct ahci_host_priv *ahci_dwc_get_resources(struct platform_device *pdev)
+{
+ struct ahci_dwc_host_priv *dpriv;
+ struct ahci_host_priv *hpriv;
+
+ dpriv = devm_kzalloc(&pdev->dev, sizeof(*dpriv), GFP_KERNEL);
+ if (!dpriv)
+ return ERR_PTR(-ENOMEM);
+
+ dpriv->pdev = pdev;
+ dpriv->pdata = device_get_match_data(&pdev->dev);
+ if (!dpriv->pdata)
+ return ERR_PTR(-EINVAL);
+
+ hpriv = ahci_platform_get_resources(pdev, dpriv->pdata->pflags);
+ if (IS_ERR(hpriv))
+ return hpriv;
+
+ hpriv->flags |= dpriv->pdata->hflags;
+ hpriv->plat_data = (void *)dpriv;
+
+ return hpriv;
+}
+
+static void ahci_dwc_check_cap(struct ahci_host_priv *hpriv)
+{
+ unsigned long port_map = hpriv->saved_port_map | hpriv->mask_port_map;
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+ bool dev_mp, dev_cp, fbs_sup;
+ unsigned int fbs_pmp;
+ u32 param;
+ int i;
+
+ param = readl(hpriv->mmio + AHCI_DWC_HOST_GPARAM2R);
+ dev_mp = !!(param & AHCI_DWC_HOST_DEV_MP);
+ dev_cp = !!(param & AHCI_DWC_HOST_DEV_CP);
+ fbs_sup = !!(param & AHCI_DWC_HOST_FBS_SUP);
+ fbs_pmp = 5 * FIELD_GET(AHCI_DWC_HOST_FBS_PMPN_MASK, param);
+
+ if (!dev_mp && hpriv->saved_cap & HOST_CAP_MPS) {
+ dev_warn(&dpriv->pdev->dev, "MPS is unsupported\n");
+ hpriv->saved_cap &= ~HOST_CAP_MPS;
+ }
+
+
+ if (fbs_sup && fbs_pmp < AHCI_DWC_FBS_PMPN_MAX) {
+ dev_warn(&dpriv->pdev->dev, "PMPn is limited up to %u ports\n",
+ fbs_pmp);
+ }
+
+ for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
+ if (!dev_mp && hpriv->saved_port_cap[i] & PORT_CMD_MPSP) {
+ dev_warn(&dpriv->pdev->dev, "MPS incapable port %d\n", i);
+ hpriv->saved_port_cap[i] &= ~PORT_CMD_MPSP;
+ }
+
+ if (!dev_cp && hpriv->saved_port_cap[i] & PORT_CMD_CPD) {
+ dev_warn(&dpriv->pdev->dev, "CPD incapable port %d\n", i);
+ hpriv->saved_port_cap[i] &= ~PORT_CMD_CPD;
+ }
+
+ if (!fbs_sup && hpriv->saved_port_cap[i] & PORT_CMD_FBSCP) {
+ dev_warn(&dpriv->pdev->dev, "FBS incapable port %d\n", i);
+ hpriv->saved_port_cap[i] &= ~PORT_CMD_FBSCP;
+ }
+ }
+}
+
+static void ahci_dwc_init_timer(struct ahci_host_priv *hpriv)
+{
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+ unsigned long rate;
+ struct clk *aclk;
+ u32 cap, cap2;
+
+ /* 1ms tick is generated only for the CCC or DevSleep features */
+ cap = readl(hpriv->mmio + HOST_CAP);
+ cap2 = readl(hpriv->mmio + HOST_CAP2);
+ if (!(cap & HOST_CAP_CCC) && !(cap2 & HOST_CAP2_SDS))
+ return;
+
+ /*
+ * Tick is generated based on the AXI/AHB application clocks signal
+ * so we need to be sure in the clock we are going to use.
+ */
+ aclk = ahci_platform_find_clk(hpriv, "aclk");
+ if (!aclk)
+ return;
+
+ /* 1ms timer interval is set as TIMV = AMBA_FREQ[MHZ] * 1000 */
+ dpriv->timv = readl(hpriv->mmio + AHCI_DWC_HOST_TIMER1MS);
+ dpriv->timv = FIELD_GET(AHCI_DWC_HOST_TIMV_MASK, dpriv->timv);
+ rate = clk_get_rate(aclk) / 1000UL;
+ if (rate == dpriv->timv)
+ return;
+
+ dev_info(&dpriv->pdev->dev, "Update CCC/DevSlp timer for Fapp %lu MHz\n",
+ rate / 1000UL);
+ dpriv->timv = FIELD_PREP(AHCI_DWC_HOST_TIMV_MASK, rate);
+ writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS);
+}
+
+static int ahci_dwc_init_dmacr(struct ahci_host_priv *hpriv)
+{
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+ struct device_node *child;
+ void __iomem *port_mmio;
+ u32 port, dmacr, ts;
+
+ /*
+ * Update the DMA Tx/Rx transaction sizes in accordance with the
+ * platform setup. Note values exceeding maximal or minimal limits will
+ * be automatically clamped. Also note the register isn't affected by
+ * the HBA global reset so we can freely initialize it once until the
+ * next system reset.
+ */
+ for_each_child_of_node(dpriv->pdev->dev.of_node, child) {
+ if (!of_device_is_available(child))
+ continue;
+
+ if (of_property_read_u32(child, "reg", &port)) {
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ port_mmio = __ahci_port_base(hpriv, port);
+ dmacr = readl(port_mmio + AHCI_DWC_PORT_DMACR);
+
+ if (!of_property_read_u32(child, "snps,tx-ts-max", &ts)) {
+ ts = ilog2(ts);
+ dmacr &= ~AHCI_DWC_PORT_TXTS_MASK;
+ dmacr |= FIELD_PREP(AHCI_DWC_PORT_TXTS_MASK, ts);
+ }
+
+ if (!of_property_read_u32(child, "snps,rx-ts-max", &ts)) {
+ ts = ilog2(ts);
+ dmacr &= ~AHCI_DWC_PORT_RXTS_MASK;
+ dmacr |= FIELD_PREP(AHCI_DWC_PORT_RXTS_MASK, ts);
+ }
+
+ writel(dmacr, port_mmio + AHCI_DWC_PORT_DMACR);
+ dpriv->dmacr[port] = dmacr;
+ }
+
+ return 0;
+}
+
+static int ahci_dwc_init_host(struct ahci_host_priv *hpriv)
+{
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+ int rc;
+
+ rc = ahci_platform_enable_resources(hpriv);
+ if (rc)
+ return rc;
+
+ if (dpriv->pdata->init) {
+ rc = dpriv->pdata->init(hpriv);
+ if (rc)
+ goto err_disable_resources;
+ }
+
+ ahci_dwc_check_cap(hpriv);
+
+ ahci_dwc_init_timer(hpriv);
+
+ rc = ahci_dwc_init_dmacr(hpriv);
+ if (rc)
+ goto err_clear_platform;
+
+ return 0;
+
+err_clear_platform:
+ if (dpriv->pdata->clear)
+ dpriv->pdata->clear(hpriv);
+
+err_disable_resources:
+ ahci_platform_disable_resources(hpriv);
+
+ return rc;
+}
+
+static int ahci_dwc_reinit_host(struct ahci_host_priv *hpriv)
+{
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+ unsigned long port_map = hpriv->port_map;
+ void __iomem *port_mmio;
+ int i, rc;
+
+ rc = ahci_platform_enable_resources(hpriv);
+ if (rc)
+ return rc;
+
+ if (dpriv->pdata->reinit) {
+ rc = dpriv->pdata->reinit(hpriv);
+ if (rc)
+ goto err_disable_resources;
+ }
+
+ writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS);
+
+ for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
+ port_mmio = __ahci_port_base(hpriv, i);
+ writel(dpriv->dmacr[i], port_mmio + AHCI_DWC_PORT_DMACR);
+ }
+
+ return 0;
+
+err_disable_resources:
+ ahci_platform_disable_resources(hpriv);
+
+ return rc;
+}
+
+static void ahci_dwc_clear_host(struct ahci_host_priv *hpriv)
+{
+ struct ahci_dwc_host_priv *dpriv = hpriv->plat_data;
+
+ if (dpriv->pdata->clear)
+ dpriv->pdata->clear(hpriv);
+
+ ahci_platform_disable_resources(hpriv);
+}
+
+static void ahci_dwc_stop_host(struct ata_host *host)
+{
+ struct ahci_host_priv *hpriv = host->private_data;
+
+ ahci_dwc_clear_host(hpriv);
+}
+
+static struct ata_port_operations ahci_dwc_port_ops = {
+ .inherits = &ahci_platform_ops,
+ .host_stop = ahci_dwc_stop_host,
+};
+
+static const struct ata_port_info ahci_dwc_port_info = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_dwc_port_ops,
+};
+
+static const struct scsi_host_template ahci_dwc_scsi_info = {
+ AHCI_SHT(DRV_NAME),
+};
+
+static int ahci_dwc_probe(struct platform_device *pdev)
+{
+ struct ahci_host_priv *hpriv;
+ int rc;
+
+ hpriv = ahci_dwc_get_resources(pdev);
+ if (IS_ERR(hpriv))
+ return PTR_ERR(hpriv);
+
+ rc = ahci_dwc_init_host(hpriv);
+ if (rc)
+ return rc;
+
+ rc = ahci_platform_init_host(pdev, hpriv, &ahci_dwc_port_info,
+ &ahci_dwc_scsi_info);
+ if (rc)
+ goto err_clear_host;
+
+ return 0;
+
+err_clear_host:
+ ahci_dwc_clear_host(hpriv);
+
+ return rc;
+}
+
+static int ahci_dwc_suspend(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+ rc = ahci_platform_suspend_host(dev);
+ if (rc)
+ return rc;
+
+ ahci_dwc_clear_host(hpriv);
+
+ return 0;
+}
+
+static int ahci_dwc_resume(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+ rc = ahci_dwc_reinit_host(hpriv);
+ if (rc)
+ return rc;
+
+ return ahci_platform_resume_host(dev);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ahci_dwc_pm_ops, ahci_dwc_suspend,
+ ahci_dwc_resume);
+
+static struct ahci_dwc_plat_data ahci_dwc_plat = {
+ .pflags = AHCI_PLATFORM_GET_RESETS,
+};
+
+static struct ahci_dwc_plat_data ahci_bt1_plat = {
+ .pflags = AHCI_PLATFORM_GET_RESETS | AHCI_PLATFORM_RST_TRIGGER,
+ .init = ahci_bt1_init,
+};
+
+static const struct of_device_id ahci_dwc_of_match[] = {
+ { .compatible = "snps,dwc-ahci", &ahci_dwc_plat },
+ { .compatible = "snps,spear-ahci", &ahci_dwc_plat },
+ { .compatible = "baikal,bt1-ahci", &ahci_bt1_plat },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ahci_dwc_of_match);
+
+static struct platform_driver ahci_dwc_driver = {
+ .probe = ahci_dwc_probe,
+ .remove = ata_platform_remove_one,
+ .shutdown = ahci_platform_shutdown,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = ahci_dwc_of_match,
+ .pm = &ahci_dwc_pm_ops,
+ },
+};
+module_platform_driver(ahci_dwc_driver);
+
+MODULE_DESCRIPTION("DWC AHCI SATA platform driver");
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c
index 79aa9f285312..86aedd5923ac 100644
--- a/drivers/ata/ahci_imx.c
+++ b/drivers/ata/ahci_imx.c
@@ -9,15 +9,17 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/ahci_platform.h>
#include <linux/gpio/consumer.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/libata.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
+#include <linux/phy/phy.h>
#include <linux/thermal.h>
#include "ahci.h"
@@ -43,42 +45,10 @@ enum {
/* Clock Reset Register */
IMX_CLOCK_RESET = 0x7f3f,
IMX_CLOCK_RESET_RESET = 1 << 0,
- /* IMX8QM HSIO AHCI definitions */
- IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET = 0x03,
- IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET = 0x09,
- IMX8QM_SATA_PHY_IMPED_RATIO_85OHM = 0x6c,
- IMX8QM_LPCG_PHYX2_OFFSET = 0x00000,
- IMX8QM_CSR_PHYX2_OFFSET = 0x90000,
- IMX8QM_CSR_PHYX1_OFFSET = 0xa0000,
- IMX8QM_CSR_PHYX_STTS0_OFFSET = 0x4,
- IMX8QM_CSR_PCIEA_OFFSET = 0xb0000,
- IMX8QM_CSR_PCIEB_OFFSET = 0xc0000,
- IMX8QM_CSR_SATA_OFFSET = 0xd0000,
- IMX8QM_CSR_PCIE_CTRL2_OFFSET = 0x8,
- IMX8QM_CSR_MISC_OFFSET = 0xe0000,
-
- IMX8QM_LPCG_PHYX2_PCLK0_MASK = (0x3 << 16),
- IMX8QM_LPCG_PHYX2_PCLK1_MASK = (0x3 << 20),
- IMX8QM_PHY_APB_RSTN_0 = BIT(0),
- IMX8QM_PHY_MODE_SATA = BIT(19),
- IMX8QM_PHY_MODE_MASK = (0xf << 17),
- IMX8QM_PHY_PIPE_RSTN_0 = BIT(24),
- IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0 = BIT(25),
- IMX8QM_PHY_PIPE_RSTN_1 = BIT(26),
- IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1 = BIT(27),
- IMX8QM_STTS0_LANE0_TX_PLL_LOCK = BIT(4),
- IMX8QM_MISC_IOB_RXENA = BIT(0),
- IMX8QM_MISC_IOB_TXENA = BIT(1),
- IMX8QM_MISC_PHYX1_EPCS_SEL = BIT(12),
- IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 = BIT(24),
- IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 = BIT(25),
- IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 = BIT(28),
- IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0 = BIT(29),
- IMX8QM_SATA_CTRL_RESET_N = BIT(12),
- IMX8QM_SATA_CTRL_EPCS_PHYRESET_N = BIT(7),
- IMX8QM_CTRL_BUTTON_RST_N = BIT(21),
- IMX8QM_CTRL_POWER_UP_RST_N = BIT(23),
- IMX8QM_CTRL_LTSSM_ENABLE = BIT(4),
+ /* IMX8QM SATA specific control registers */
+ IMX8QM_SATA_AHCI_PTC = 0xc8,
+ IMX8QM_SATA_AHCI_PTC_RXWM_MASK = GENMASK(6, 0),
+ IMX8QM_SATA_AHCI_PTC_RXWM = 0x29,
};
enum ahci_imx_type {
@@ -94,14 +64,10 @@ struct imx_ahci_priv {
struct clk *sata_clk;
struct clk *sata_ref_clk;
struct clk *ahb_clk;
- struct clk *epcs_tx_clk;
- struct clk *epcs_rx_clk;
- struct clk *phy_apbclk;
- struct clk *phy_pclk0;
- struct clk *phy_pclk1;
- void __iomem *phy_base;
- struct gpio_desc *clkreq_gpiod;
struct regmap *gpr;
+ struct phy *sata_phy;
+ struct phy *cali_phy0;
+ struct phy *cali_phy1;
bool no_device;
bool first_time;
u32 phy_params;
@@ -327,7 +293,7 @@ static int read_adc_sum(void *dev, u16 rtune_ctl_reg, void __iomem * mmio)
}
/* SATA AHCI temperature monitor */
-static int sata_ahci_read_temperature(void *dev, int *temp)
+static int __sata_ahci_read_temperature(void *dev, int *temp)
{
u16 mpll_test_reg, rtune_ctl_reg, dac_ctl_reg, read_sum;
u32 str1, str2, str3, str4;
@@ -416,6 +382,11 @@ static int sata_ahci_read_temperature(void *dev, int *temp)
return 0;
}
+static int sata_ahci_read_temperature(struct thermal_zone_device *tz, int *temp)
+{
+ return __sata_ahci_read_temperature(thermal_zone_device_priv(tz), temp);
+}
+
static ssize_t sata_ahci_show_temp(struct device *dev,
struct device_attribute *da,
char *buf)
@@ -423,14 +394,14 @@ static ssize_t sata_ahci_show_temp(struct device *dev,
unsigned int temp = 0;
int err;
- err = sata_ahci_read_temperature(dev, &temp);
+ err = __sata_ahci_read_temperature(dev, &temp);
if (err < 0)
return err;
return sprintf(buf, "%u\n", temp);
}
-static const struct thermal_zone_of_device_ops fsl_sata_ahci_of_thermal_ops = {
+static const struct thermal_zone_device_ops fsl_sata_ahci_of_thermal_ops = {
.get_temp = sata_ahci_read_temperature,
};
@@ -444,201 +415,79 @@ ATTRIBUTE_GROUPS(fsl_sata_ahci);
static int imx8_sata_enable(struct ahci_host_priv *hpriv)
{
- u32 val, reg;
- int i, ret;
+ u32 val;
+ int ret;
struct imx_ahci_priv *imxpriv = hpriv->plat_data;
struct device *dev = &imxpriv->ahci_pdev->dev;
- /* configure the hsio for sata */
- ret = clk_prepare_enable(imxpriv->phy_pclk0);
- if (ret < 0) {
- dev_err(dev, "can't enable phy_pclk0.\n");
+ /*
+ * Since "REXT" pin is only present for first lane of i.MX8QM
+ * PHY, its calibration results will be stored, passed through
+ * to the second lane PHY, and shared with all three lane PHYs.
+ *
+ * Initialize the first two lane PHYs here, although only the
+ * third lane PHY is used by SATA.
+ */
+ ret = phy_init(imxpriv->cali_phy0);
+ if (ret) {
+ dev_err(dev, "cali PHY init failed\n");
return ret;
}
- ret = clk_prepare_enable(imxpriv->phy_pclk1);
- if (ret < 0) {
- dev_err(dev, "can't enable phy_pclk1.\n");
- goto disable_phy_pclk0;
+ ret = phy_power_on(imxpriv->cali_phy0);
+ if (ret) {
+ dev_err(dev, "cali PHY power on failed\n");
+ goto err_cali_phy0_exit;
}
- ret = clk_prepare_enable(imxpriv->epcs_tx_clk);
- if (ret < 0) {
- dev_err(dev, "can't enable epcs_tx_clk.\n");
- goto disable_phy_pclk1;
+ ret = phy_init(imxpriv->cali_phy1);
+ if (ret) {
+ dev_err(dev, "cali PHY1 init failed\n");
+ goto err_cali_phy0_off;
}
- ret = clk_prepare_enable(imxpriv->epcs_rx_clk);
- if (ret < 0) {
- dev_err(dev, "can't enable epcs_rx_clk.\n");
- goto disable_epcs_tx_clk;
+ ret = phy_power_on(imxpriv->cali_phy1);
+ if (ret) {
+ dev_err(dev, "cali PHY1 power on failed\n");
+ goto err_cali_phy1_exit;
}
- ret = clk_prepare_enable(imxpriv->phy_apbclk);
- if (ret < 0) {
- dev_err(dev, "can't enable phy_apbclk.\n");
- goto disable_epcs_rx_clk;
+ ret = phy_init(imxpriv->sata_phy);
+ if (ret) {
+ dev_err(dev, "sata PHY init failed\n");
+ goto err_cali_phy1_off;
}
- /* Configure PHYx2 PIPE_RSTN */
- regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEA_OFFSET +
- IMX8QM_CSR_PCIE_CTRL2_OFFSET, &val);
- if ((val & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
- /* The link of the PCIEA of HSIO is down */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_PHYX2_OFFSET,
- IMX8QM_PHY_PIPE_RSTN_0 |
- IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0,
- IMX8QM_PHY_PIPE_RSTN_0 |
- IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0);
+ ret = phy_set_mode(imxpriv->sata_phy, PHY_MODE_SATA);
+ if (ret) {
+ dev_err(dev, "unable to set SATA PHY mode\n");
+ goto err_sata_phy_exit;
}
- regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEB_OFFSET +
- IMX8QM_CSR_PCIE_CTRL2_OFFSET, &reg);
- if ((reg & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
- /* The link of the PCIEB of HSIO is down */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_PHYX2_OFFSET,
- IMX8QM_PHY_PIPE_RSTN_1 |
- IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1,
- IMX8QM_PHY_PIPE_RSTN_1 |
- IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1);
- }
- if (((reg | val) & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
- /* The links of both PCIA and PCIEB of HSIO are down */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_LPCG_PHYX2_OFFSET,
- IMX8QM_LPCG_PHYX2_PCLK0_MASK |
- IMX8QM_LPCG_PHYX2_PCLK1_MASK,
- 0);
+ ret = phy_power_on(imxpriv->sata_phy);
+ if (ret) {
+ dev_err(dev, "sata PHY power up failed\n");
+ goto err_sata_phy_exit;
}
- /* set PWR_RST and BT_RST of csr_pciea */
- val = IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET;
- regmap_update_bits(imxpriv->gpr,
- val,
- IMX8QM_CTRL_BUTTON_RST_N,
- IMX8QM_CTRL_BUTTON_RST_N);
- regmap_update_bits(imxpriv->gpr,
- val,
- IMX8QM_CTRL_POWER_UP_RST_N,
- IMX8QM_CTRL_POWER_UP_RST_N);
-
- /* PHYX1_MODE to SATA */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_PHYX1_OFFSET,
- IMX8QM_PHY_MODE_MASK,
- IMX8QM_PHY_MODE_SATA);
+ /* The cali_phy# can be turned off after SATA PHY is initialized. */
+ phy_power_off(imxpriv->cali_phy1);
+ phy_exit(imxpriv->cali_phy1);
+ phy_power_off(imxpriv->cali_phy0);
+ phy_exit(imxpriv->cali_phy0);
- /*
- * BIT0 RXENA 1, BIT1 TXENA 0
- * BIT12 PHY_X1_EPCS_SEL 1.
- */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_MISC_OFFSET,
- IMX8QM_MISC_IOB_RXENA,
- IMX8QM_MISC_IOB_RXENA);
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_MISC_OFFSET,
- IMX8QM_MISC_IOB_TXENA,
- 0);
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_MISC_OFFSET,
- IMX8QM_MISC_PHYX1_EPCS_SEL,
- IMX8QM_MISC_PHYX1_EPCS_SEL);
- /*
- * It is possible, for PCIe and SATA are sharing
- * the same clock source, HPLL or external oscillator.
- * When PCIe is in low power modes (L1.X or L2 etc),
- * the clock source can be turned off. In this case,
- * if this clock source is required to be toggling by
- * SATA, then SATA functions will be abnormal.
- * Set the override here to avoid it.
- */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_MISC_OFFSET,
- IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 |
- IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 |
- IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 |
- IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0,
- IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 |
- IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 |
- IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 |
- IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0);
-
- /* clear PHY RST, then set it */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_SATA_OFFSET,
- IMX8QM_SATA_CTRL_EPCS_PHYRESET_N,
- 0);
-
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_SATA_OFFSET,
- IMX8QM_SATA_CTRL_EPCS_PHYRESET_N,
- IMX8QM_SATA_CTRL_EPCS_PHYRESET_N);
-
- /* CTRL RST: SET -> delay 1 us -> CLEAR -> SET */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_SATA_OFFSET,
- IMX8QM_SATA_CTRL_RESET_N,
- IMX8QM_SATA_CTRL_RESET_N);
- udelay(1);
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_SATA_OFFSET,
- IMX8QM_SATA_CTRL_RESET_N,
- 0);
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_SATA_OFFSET,
- IMX8QM_SATA_CTRL_RESET_N,
- IMX8QM_SATA_CTRL_RESET_N);
-
- /* APB reset */
- regmap_update_bits(imxpriv->gpr,
- IMX8QM_CSR_PHYX1_OFFSET,
- IMX8QM_PHY_APB_RSTN_0,
- IMX8QM_PHY_APB_RSTN_0);
-
- for (i = 0; i < 100; i++) {
- reg = IMX8QM_CSR_PHYX1_OFFSET +
- IMX8QM_CSR_PHYX_STTS0_OFFSET;
- regmap_read(imxpriv->gpr, reg, &val);
- val &= IMX8QM_STTS0_LANE0_TX_PLL_LOCK;
- if (val == IMX8QM_STTS0_LANE0_TX_PLL_LOCK)
- break;
- udelay(1);
- }
-
- if (val != IMX8QM_STTS0_LANE0_TX_PLL_LOCK) {
- dev_err(dev, "TX PLL of the PHY is not locked\n");
- ret = -ENODEV;
- } else {
- writeb(imxpriv->imped_ratio, imxpriv->phy_base +
- IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET);
- writeb(imxpriv->imped_ratio, imxpriv->phy_base +
- IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET);
- reg = readb(imxpriv->phy_base +
- IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET);
- if (unlikely(reg != imxpriv->imped_ratio))
- dev_info(dev, "Can't set PHY RX impedance ratio.\n");
- reg = readb(imxpriv->phy_base +
- IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET);
- if (unlikely(reg != imxpriv->imped_ratio))
- dev_info(dev, "Can't set PHY TX impedance ratio.\n");
- usleep_range(50, 100);
+ /* RxWaterMark setting */
+ val = readl(hpriv->mmio + IMX8QM_SATA_AHCI_PTC);
+ val &= ~IMX8QM_SATA_AHCI_PTC_RXWM_MASK;
+ val |= IMX8QM_SATA_AHCI_PTC_RXWM;
+ writel(val, hpriv->mmio + IMX8QM_SATA_AHCI_PTC);
- /*
- * To reduce the power consumption, gate off
- * the PHY clks
- */
- clk_disable_unprepare(imxpriv->phy_apbclk);
- clk_disable_unprepare(imxpriv->phy_pclk1);
- clk_disable_unprepare(imxpriv->phy_pclk0);
- return ret;
- }
+ return 0;
- clk_disable_unprepare(imxpriv->phy_apbclk);
-disable_epcs_rx_clk:
- clk_disable_unprepare(imxpriv->epcs_rx_clk);
-disable_epcs_tx_clk:
- clk_disable_unprepare(imxpriv->epcs_tx_clk);
-disable_phy_pclk1:
- clk_disable_unprepare(imxpriv->phy_pclk1);
-disable_phy_pclk0:
- clk_disable_unprepare(imxpriv->phy_pclk0);
+err_sata_phy_exit:
+ phy_exit(imxpriv->sata_phy);
+err_cali_phy1_off:
+ phy_power_off(imxpriv->cali_phy1);
+err_cali_phy1_exit:
+ phy_exit(imxpriv->cali_phy1);
+err_cali_phy0_off:
+ phy_power_off(imxpriv->cali_phy0);
+err_cali_phy0_exit:
+ phy_exit(imxpriv->cali_phy0);
return ret;
}
@@ -662,7 +511,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) {
/*
- * set PHY Paremeters, two steps to configure the GPR13,
+ * set PHY Parameters, two steps to configure the GPR13,
* one write for rest of parameters, mask of first write
* is 0x07ffffff, and the other one write for setting
* the mpll_clk_en.
@@ -692,6 +541,9 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
}
} else if (imxpriv->type == AHCI_IMX8QM) {
ret = imx8_sata_enable(hpriv);
+ if (ret)
+ goto disable_clk;
+
}
usleep_range(1000, 2000);
@@ -730,8 +582,10 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv)
break;
case AHCI_IMX8QM:
- clk_disable_unprepare(imxpriv->epcs_rx_clk);
- clk_disable_unprepare(imxpriv->epcs_tx_clk);
+ if (imxpriv->sata_phy) {
+ phy_power_off(imxpriv->sata_phy);
+ phy_exit(imxpriv->sata_phy);
+ }
break;
default:
@@ -754,6 +608,9 @@ static void ahci_imx_error_handler(struct ata_port *ap)
ahci_error_handler(ap);
+ if (imxpriv->type == AHCI_IMX8QM)
+ return;
+
if (!(imxpriv->first_time) || ahci_imx_hotplug)
return;
@@ -785,18 +642,19 @@ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class,
int ret;
if (imxpriv->type == AHCI_IMX53)
- ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline);
+ ret = ahci_pmp_retry_srst_ops.reset.softreset(link, class,
+ deadline);
else
- ret = ahci_ops.softreset(link, class, deadline);
+ ret = ahci_ops.reset.softreset(link, class, deadline);
return ret;
}
static struct ata_port_operations ahci_imx_ops = {
- .inherits = &ahci_ops,
- .host_stop = ahci_imx_host_stop,
- .error_handler = ahci_imx_error_handler,
- .softreset = ahci_imx_softreset,
+ .inherits = &ahci_ops,
+ .host_stop = ahci_imx_host_stop,
+ .error_handler = ahci_imx_error_handler,
+ .reset.softreset = ahci_imx_softreset,
};
static const struct ata_port_info ahci_imx_port_info = {
@@ -974,87 +832,36 @@ static u32 imx_ahci_parse_props(struct device *dev,
return reg_value;
}
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv)
{
- struct resource *phy_res;
- struct platform_device *pdev = imxpriv->ahci_pdev;
- struct device_node *np = dev->of_node;
-
- if (of_property_read_u32(np, "fsl,phy-imp", &imxpriv->imped_ratio))
- imxpriv->imped_ratio = IMX8QM_SATA_PHY_IMPED_RATIO_85OHM;
- phy_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
- if (phy_res) {
- imxpriv->phy_base = devm_ioremap(dev, phy_res->start,
- resource_size(phy_res));
- if (!imxpriv->phy_base) {
- dev_err(dev, "error with ioremap\n");
- return -ENOMEM;
- }
- } else {
- dev_err(dev, "missing *phy* reg region.\n");
- return -ENOMEM;
- }
- imxpriv->gpr =
- syscon_regmap_lookup_by_phandle(np, "hsio");
- if (IS_ERR(imxpriv->gpr)) {
- dev_err(dev, "unable to find gpr registers\n");
- return PTR_ERR(imxpriv->gpr);
- }
-
- imxpriv->epcs_tx_clk = devm_clk_get(dev, "epcs_tx");
- if (IS_ERR(imxpriv->epcs_tx_clk)) {
- dev_err(dev, "can't get epcs_tx_clk clock.\n");
- return PTR_ERR(imxpriv->epcs_tx_clk);
- }
- imxpriv->epcs_rx_clk = devm_clk_get(dev, "epcs_rx");
- if (IS_ERR(imxpriv->epcs_rx_clk)) {
- dev_err(dev, "can't get epcs_rx_clk clock.\n");
- return PTR_ERR(imxpriv->epcs_rx_clk);
- }
- imxpriv->phy_pclk0 = devm_clk_get(dev, "phy_pclk0");
- if (IS_ERR(imxpriv->phy_pclk0)) {
- dev_err(dev, "can't get phy_pclk0 clock.\n");
- return PTR_ERR(imxpriv->phy_pclk0);
- }
- imxpriv->phy_pclk1 = devm_clk_get(dev, "phy_pclk1");
- if (IS_ERR(imxpriv->phy_pclk1)) {
- dev_err(dev, "can't get phy_pclk1 clock.\n");
- return PTR_ERR(imxpriv->phy_pclk1);
- }
- imxpriv->phy_apbclk = devm_clk_get(dev, "phy_apbclk");
- if (IS_ERR(imxpriv->phy_apbclk)) {
- dev_err(dev, "can't get phy_apbclk clock.\n");
- return PTR_ERR(imxpriv->phy_apbclk);
- }
-
- /* Fetch GPIO, then enable the external OSC */
- imxpriv->clkreq_gpiod = devm_gpiod_get_optional(dev, "clkreq",
- GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
- if (IS_ERR(imxpriv->clkreq_gpiod))
- return PTR_ERR(imxpriv->clkreq_gpiod);
- if (imxpriv->clkreq_gpiod)
- gpiod_set_consumer_name(imxpriv->clkreq_gpiod, "SATA CLKREQ");
-
+ imxpriv->sata_phy = devm_phy_get(dev, "sata-phy");
+ if (IS_ERR(imxpriv->sata_phy))
+ return dev_err_probe(dev, PTR_ERR(imxpriv->sata_phy),
+ "Failed to get sata_phy\n");
+
+ imxpriv->cali_phy0 = devm_phy_get(dev, "cali-phy0");
+ if (IS_ERR(imxpriv->cali_phy0))
+ return dev_err_probe(dev, PTR_ERR(imxpriv->cali_phy0),
+ "Failed to get cali_phy0\n");
+ imxpriv->cali_phy1 = devm_phy_get(dev, "cali-phy1");
+ if (IS_ERR(imxpriv->cali_phy1))
+ return dev_err_probe(dev, PTR_ERR(imxpriv->cali_phy1),
+ "Failed to get cali_phy1\n");
return 0;
}
static int imx_ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *of_id;
struct ahci_host_priv *hpriv;
struct imx_ahci_priv *imxpriv;
unsigned int reg_val;
int ret;
- of_id = of_match_device(imx_ahci_of_match, dev);
- if (!of_id)
- return -EINVAL;
-
imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL);
if (!imxpriv)
return -ENOMEM;
@@ -1062,7 +869,7 @@ static int imx_ahci_probe(struct platform_device *pdev)
imxpriv->ahci_pdev = pdev;
imxpriv->no_device = false;
imxpriv->first_time = true;
- imxpriv->type = (enum ahci_imx_type)of_id->data;
+ imxpriv->type = (enum ahci_imx_type)device_get_match_data(dev);
imxpriv->sata_clk = devm_clk_get(dev, "sata");
if (IS_ERR(imxpriv->sata_clk)) {
@@ -1076,12 +883,6 @@ static int imx_ahci_probe(struct platform_device *pdev)
return PTR_ERR(imxpriv->sata_ref_clk);
}
- imxpriv->ahb_clk = devm_clk_get(dev, "ahb");
- if (IS_ERR(imxpriv->ahb_clk)) {
- dev_err(dev, "can't get ahb clock.\n");
- return PTR_ERR(imxpriv->ahb_clk);
- }
-
if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) {
u32 reg_value;
@@ -1131,8 +932,8 @@ static int imx_ahci_probe(struct platform_device *pdev)
ret = PTR_ERR(hwmon_dev);
goto disable_clk;
}
- devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
- &fsl_sata_ahci_of_thermal_ops);
+ devm_thermal_of_zone_register(hwmon_dev, 0, hwmon_dev,
+ &fsl_sata_ahci_of_thermal_ops);
dev_info(dev, "%s: sensor 'sata_ahci'\n", dev_name(hwmon_dev));
}
@@ -1141,11 +942,8 @@ static int imx_ahci_probe(struct platform_device *pdev)
goto disable_clk;
/*
- * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL,
- * and IP vendor specific register IMX_TIMER1MS.
- * Configure CAP_SSS (support stagered spin up).
- * Implement the port0.
- * Get the ahb clock rate, and configure the TIMER1MS register.
+ * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL.
+ * Set CAP_SSS (support stagered spin up) and Implement the port0.
*/
reg_val = readl(hpriv->mmio + HOST_CAP);
if (!(reg_val & HOST_CAP_SSS)) {
@@ -1158,8 +956,20 @@ static int imx_ahci_probe(struct platform_device *pdev)
writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL);
}
- reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000;
- writel(reg_val, hpriv->mmio + IMX_TIMER1MS);
+ if (imxpriv->type != AHCI_IMX8QM) {
+ /*
+ * Get AHB clock rate and configure the vendor specified
+ * TIMER1MS register on i.MX53, i.MX6Q and i.MX6QP only.
+ */
+ imxpriv->ahb_clk = devm_clk_get(dev, "ahb");
+ if (IS_ERR(imxpriv->ahb_clk)) {
+ dev_err(dev, "Failed to get ahb clock\n");
+ ret = PTR_ERR(imxpriv->ahb_clk);
+ goto disable_sata;
+ }
+ reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000;
+ writel(reg_val, hpriv->mmio + IMX_TIMER1MS);
+ }
ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info,
&ahci_platform_sht);
@@ -1228,6 +1038,6 @@ static struct platform_driver imx_ahci_driver = {
module_platform_driver(imx_ahci_driver);
MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver");
-MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>");
+MODULE_AUTHOR("Richard Zhu <hongxing.zhu@nxp.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("ahci:imx");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/ahci_mtk.c b/drivers/ata/ahci_mtk.c
index 1f6c85fde983..7295b9066ae2 100644
--- a/drivers/ata/ahci_mtk.c
+++ b/drivers/ata/ahci_mtk.c
@@ -11,6 +11,7 @@
#include <linux/libata.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/regmap.h>
@@ -37,7 +38,7 @@ static const struct ata_port_info ahci_port_info = {
.port_ops = &ahci_platform_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -106,7 +107,7 @@ static int mtk_ahci_parse_property(struct ahci_host_priv *hpriv,
struct device_node *np = dev->of_node;
/* enable SATA function if needed */
- if (of_find_property(np, "mediatek,phy-mode", NULL)) {
+ if (of_property_present(np, "mediatek,phy-mode")) {
plat->mode = syscon_regmap_lookup_by_phandle(
np, "mediatek,phy-mode");
if (IS_ERR(plat->mode)) {
@@ -118,8 +119,6 @@ static int mtk_ahci_parse_property(struct ahci_host_priv *hpriv,
SYS_CFG_SATA_EN);
}
- of_property_read_u32(np, "ports-implemented", &hpriv->force_port_map);
-
return 0;
}
diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c
index 22ecc4f3ae79..8744dae41612 100644
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -15,7 +15,7 @@
#include <linux/kernel.h>
#include <linux/mbus.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include "ahci.h"
@@ -178,7 +178,7 @@ static const struct ata_port_info ahci_mvebu_port_info = {
.port_ops = &ahci_platform_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
diff --git a/drivers/ata/ahci_octeon.c b/drivers/ata/ahci_octeon.c
index b9460b91288f..9accf8923891 100644
--- a/drivers/ata/ahci_octeon.c
+++ b/drivers/ata/ahci_octeon.c
@@ -16,7 +16,6 @@
#include <linux/of_platform.h>
#include <asm/octeon/octeon.h>
-#include <asm/bitfield.h>
#define CVMX_SATA_UCTL_SHIM_CFG 0xE8
@@ -32,13 +31,11 @@ static int ahci_octeon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
- struct resource *res;
void __iomem *base;
u64 cfg;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -73,11 +70,6 @@ static int ahci_octeon_probe(struct platform_device *pdev)
return 0;
}
-static int ahci_octeon_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static const struct of_device_id octeon_ahci_match[] = {
{ .compatible = "cavium,octeon-7130-sata-uctl", },
{ /* sentinel */ }
@@ -86,7 +78,6 @@ MODULE_DEVICE_TABLE(of, octeon_ahci_match);
static struct platform_driver ahci_octeon_driver = {
.probe = ahci_octeon_probe,
- .remove = ahci_octeon_remove,
.driver = {
.name = "octeon-ahci",
.of_match_table = octeon_ahci_match,
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 28a8de5b48b9..c18054333f7c 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -9,14 +9,14 @@
*/
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/device.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
-#include <linux/acpi.h>
#include <linux/pci_ids.h>
#include "ahci.h"
@@ -36,7 +36,7 @@ static const struct ata_port_info ahci_port_info_nolpm = {
.port_ops = &ahci_platform_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -56,13 +56,10 @@ static int ahci_probe(struct platform_device *pdev)
if (rc)
return rc;
- of_property_read_u32(dev->of_node,
- "ports-implemented", &hpriv->force_port_map);
-
- if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci"))
+ if (device_is_compatible(dev, "hisilicon,hisi-ahci"))
hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ;
- port = acpi_device_get_match_data(dev);
+ port = device_get_match_data(dev);
if (!port)
port = &ahci_port_info;
@@ -83,9 +80,7 @@ static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
static const struct of_device_id ahci_of_match[] = {
{ .compatible = "generic-ahci", },
/* Keep the following compatibles for device tree compatibility */
- { .compatible = "snps,spear-ahci", },
{ .compatible = "ibm,476gtr-ahci", },
- { .compatible = "snps,dwc-ahci", },
{ .compatible = "hisilicon,hisi-ahci", },
{ .compatible = "cavium,octeon-7130-ahci", },
{ /* sentinel */ }
diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c
index 6cd61842ad48..0dec1a17e5b1 100644
--- a/drivers/ata/ahci_qoriq.c
+++ b/drivers/ata/ahci_qoriq.c
@@ -12,9 +12,7 @@
#include <linux/pm.h>
#include <linux/ahci_platform.h>
#include <linux/device.h>
-#include <linux/of_address.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/libata.h>
#include "ahci.h"
@@ -90,7 +88,7 @@ MODULE_DEVICE_TABLE(acpi, ahci_qoriq_acpi_match);
static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
void __iomem *port_mmio = ahci_port_base(link->ap);
u32 px_cmd, px_is, px_val;
struct ata_port *ap = link->ap;
@@ -148,8 +146,8 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
}
static struct ata_port_operations ahci_qoriq_ops = {
- .inherits = &ahci_ops,
- .hardreset = ahci_qoriq_hardreset,
+ .inherits = &ahci_ops,
+ .reset.hardreset = ahci_qoriq_hardreset,
};
static const struct ata_port_info ahci_qoriq_port_info = {
@@ -159,7 +157,7 @@ static const struct ata_port_info ahci_qoriq_port_info = {
.port_ops = &ahci_qoriq_ops,
};
-static struct scsi_host_template ahci_qoriq_sht = {
+static const struct scsi_host_template ahci_qoriq_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -280,7 +278,7 @@ static int ahci_qoriq_probe(struct platform_device *pdev)
return -ENOMEM;
if (of_id)
- qoriq_priv->type = (enum ahci_qoriq_type)of_id->data;
+ qoriq_priv->type = (unsigned long)of_id->data;
else
qoriq_priv->type = (enum ahci_qoriq_type)acpi_id->driver_data;
diff --git a/drivers/ata/ahci_seattle.c b/drivers/ata/ahci_seattle.c
index ced12705ed9d..3f16c1678402 100644
--- a/drivers/ata/ahci_seattle.c
+++ b/drivers/ata/ahci_seattle.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/device.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
@@ -72,7 +71,7 @@ static const struct ata_port_info ahci_port_seattle_info = {
.port_ops = &ahci_seattle_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -132,8 +131,7 @@ static const struct ata_port_info *ahci_seattle_get_port_info(
if (!plat_data)
return &ahci_port_info;
- plat_data->sgpio_ctrl = devm_ioremap_resource(dev,
- platform_get_resource(pdev, IORESOURCE_MEM, 1));
+ plat_data->sgpio_ctrl = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(plat_data->sgpio_ctrl))
return &ahci_port_info;
diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c
index 7526653c843b..4336c8a6e208 100644
--- a/drivers/ata/ahci_st.c
+++ b/drivers/ata/ahci_st.c
@@ -30,7 +30,6 @@
#define ST_AHCI_OOBR_CIMAX_SHIFT 0
struct st_ahci_drv_data {
- struct platform_device *ahci;
struct reset_control *pwr;
struct reset_control *sw_rst;
struct reset_control *pwr_rst;
@@ -138,13 +137,12 @@ static const struct ata_port_info st_ahci_port_info = {
.port_ops = &st_ahci_port_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
static int st_ahci_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
struct st_ahci_drv_data *drv_data;
struct ahci_host_priv *hpriv;
int err;
@@ -168,9 +166,6 @@ static int st_ahci_probe(struct platform_device *pdev)
st_ahci_configure_oob(hpriv->mmio);
- of_property_read_u32(dev->of_node,
- "ports-implemented", &hpriv->force_port_map);
-
err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
&ahci_platform_sht);
if (err) {
@@ -181,7 +176,6 @@ static int st_ahci_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int st_ahci_suspend(struct device *dev)
{
struct ata_host *host = dev_get_drvdata(dev);
@@ -226,9 +220,8 @@ static int st_ahci_resume(struct device *dev)
return ahci_platform_resume_host(dev);
}
-#endif
-static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume);
static const struct of_device_id st_ahci_match[] = {
{ .compatible = "st,ahci", },
@@ -239,8 +232,8 @@ MODULE_DEVICE_TABLE(of, st_ahci_match);
static struct platform_driver st_ahci_driver = {
.driver = {
.name = DRV_NAME,
- .pm = &st_ahci_pm_ops,
- .of_match_table = of_match_ptr(st_ahci_match),
+ .pm = pm_sleep_ptr(&st_ahci_pm_ops),
+ .of_match_table = st_ahci_match,
},
.probe = st_ahci_probe,
.remove = ata_platform_remove_one,
diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c
index c7273c1cb0c7..5d4584570ae0 100644
--- a/drivers/ata/ahci_sunxi.c
+++ b/drivers/ata/ahci_sunxi.c
@@ -13,8 +13,8 @@
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include "ahci.h"
@@ -206,7 +206,7 @@ static const struct ata_port_info ahci_sunxi_port_info = {
.port_ops = &ahci_platform_ops,
};
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c
index 4fb94db1217d..44584eed6374 100644
--- a/drivers/ata/ahci_tegra.c
+++ b/drivers/ata/ahci_tegra.c
@@ -12,7 +12,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -506,7 +506,7 @@ static const struct of_device_id tegra_ahci_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_ahci_of_match);
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -530,8 +530,7 @@ static int tegra_ahci_probe(struct platform_device *pdev)
tegra->pdev = pdev;
tegra->soc = of_device_get_match_data(&pdev->dev);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- tegra->sata_regs = devm_ioremap_resource(&pdev->dev, res);
+ tegra->sata_regs = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(tegra->sata_regs))
return PTR_ERR(tegra->sata_regs);
diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
index 7bb5db17f864..6b8844646fcd 100644
--- a/drivers/ata/ahci_xgene.c
+++ b/drivers/ata/ahci_xgene.c
@@ -13,9 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ahci_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
#include <linux/phy/phy.h>
#include "ahci.h"
@@ -110,9 +108,8 @@ static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx)
* @timeout : timeout for achieving the value.
*/
static int xgene_ahci_poll_reg_val(struct ata_port *ap,
- void __iomem *reg, unsigned
- int val, unsigned long interval,
- unsigned long timeout)
+ void __iomem *reg, unsigned int val,
+ unsigned int interval, unsigned int timeout)
{
unsigned long deadline;
unsigned int tmp;
@@ -350,7 +347,7 @@ static void xgene_ahci_set_phy_cfg(struct xgene_ahci_context *ctx, int channel)
static int xgene_ahci_do_hardreset(struct ata_link *link,
unsigned long deadline, bool *online)
{
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct xgene_ahci_context *ctx = hpriv->plat_data;
@@ -453,7 +450,6 @@ static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class,
{
int pmp = sata_srst_pmp(link);
struct ata_port *ap = link->ap;
- u32 rc;
void __iomem *port_mmio = ahci_port_base(ap);
u32 port_fbs;
@@ -466,9 +462,7 @@ static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class,
port_fbs |= pmp << PORT_FBS_DEV_OFFSET;
writel(port_fbs, port_mmio + PORT_FBS);
- rc = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
-
- return rc;
+ return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
}
/**
@@ -503,7 +497,7 @@ static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class,
u32 port_fbs;
u32 port_fbs_save;
u32 retry = 1;
- u32 rc;
+ int rc;
port_fbs_save = readl(port_mmio + PORT_FBS);
@@ -537,7 +531,7 @@ softreset_retry:
/**
* xgene_ahci_handle_broken_edge_irq - Handle the broken irq.
- * @host: Host that recieved the irq
+ * @host: Host that received the irq
* @irq_masked: HOST_IRQ_STAT value
*
* For hardware with broken edge trigger latch
@@ -616,11 +610,11 @@ static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance)
static struct ata_port_operations xgene_ahci_v1_ops = {
.inherits = &ahci_ops,
.host_stop = xgene_ahci_host_stop,
- .hardreset = xgene_ahci_hardreset,
+ .reset.hardreset = xgene_ahci_hardreset,
+ .reset.softreset = xgene_ahci_softreset,
+ .pmp_reset.softreset = xgene_ahci_pmp_softreset,
.read_id = xgene_ahci_read_id,
.qc_issue = xgene_ahci_qc_issue,
- .softreset = xgene_ahci_softreset,
- .pmp_softreset = xgene_ahci_pmp_softreset
};
static const struct ata_port_info xgene_ahci_v1_port_info = {
@@ -633,7 +627,7 @@ static const struct ata_port_info xgene_ahci_v1_port_info = {
static struct ata_port_operations xgene_ahci_v2_ops = {
.inherits = &ahci_ops,
.host_stop = xgene_ahci_host_stop,
- .hardreset = xgene_ahci_hardreset,
+ .reset.hardreset = xgene_ahci_hardreset,
.read_id = xgene_ahci_read_id,
};
@@ -710,7 +704,7 @@ static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx)
return val & CFG_SATA_ENET_SELECT_MASK ? -1 : 0;
}
-static struct scsi_host_template ahci_platform_sht = {
+static const struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
@@ -736,7 +730,6 @@ static int xgene_ahci_probe(struct platform_device *pdev)
struct ahci_host_priv *hpriv;
struct xgene_ahci_context *ctx;
struct resource *res;
- const struct of_device_id *of_devid;
enum xgene_ahci_version version = XGENE_AHCI_V1;
const struct ata_port_info *ppi[] = { &xgene_ahci_v1_port_info,
&xgene_ahci_v2_port_info };
@@ -755,20 +748,17 @@ static int xgene_ahci_probe(struct platform_device *pdev)
ctx->dev = dev;
/* Retrieve the IP core resource */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- ctx->csr_core = devm_ioremap_resource(dev, res);
+ ctx->csr_core = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(ctx->csr_core))
return PTR_ERR(ctx->csr_core);
/* Retrieve the IP diagnostic resource */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- ctx->csr_diag = devm_ioremap_resource(dev, res);
+ ctx->csr_diag = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(ctx->csr_diag))
return PTR_ERR(ctx->csr_diag);
/* Retrieve the IP AXI resource */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
- ctx->csr_axi = devm_ioremap_resource(dev, res);
+ ctx->csr_axi = devm_platform_ioremap_resource(pdev, 3);
if (IS_ERR(ctx->csr_axi))
return PTR_ERR(ctx->csr_axi);
@@ -782,10 +772,8 @@ static int xgene_ahci_probe(struct platform_device *pdev)
ctx->csr_mux = csr;
}
- of_devid = of_match_device(xgene_ahci_of_match, dev);
- if (of_devid) {
- if (of_devid->data)
- version = (enum xgene_ahci_version) of_devid->data;
+ if (dev->of_node) {
+ version = (enum xgene_ahci_version)of_device_get_match_data(dev);
}
#ifdef CONFIG_ACPI
else {
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 20a32e4d501d..e70b6c089cf1 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -95,7 +95,7 @@ static int generic_set_mode(struct ata_link *link, struct ata_device **unused)
return 0;
}
-static struct scsi_host_template generic_sht = {
+static const struct scsi_host_template generic_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -209,7 +209,7 @@ static int ata_generic_init_one(struct pci_dev *dev, const struct pci_device_id
return ata_pci_bmdma_init_one(dev, ppi, &generic_sht, (void *)id, 0);
}
-static struct pci_device_id ata_generic[] = {
+static const struct pci_device_id ata_generic[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE), },
{ PCI_DEVICE(PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), },
{ PCI_DEVICE(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F), },
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index ade5e894563b..495fa096dd65 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -1059,7 +1059,7 @@ static u8 piix_vmw_bmdma_status(struct ata_port *ap)
return ata_bmdma_status(ap) & ~ATA_DMA_ERR;
}
-static struct scsi_host_template piix_sht = {
+static const struct scsi_host_template piix_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -1074,7 +1074,7 @@ static struct ata_port_operations piix_pata_ops = {
.cable_detect = ata_cable_40wire,
.set_piomode = piix_set_piomode,
.set_dmamode = piix_set_dmamode,
- .prereset = piix_pata_prereset,
+ .reset.prereset = piix_pata_prereset,
};
static struct ata_port_operations piix_vmw_ops = {
@@ -1089,20 +1089,21 @@ static struct ata_port_operations ich_pata_ops = {
};
static struct attribute *piix_sidpr_shost_attrs[] = {
+ &dev_attr_link_power_management_supported.attr,
&dev_attr_link_power_management_policy.attr,
NULL
};
ATTRIBUTE_GROUPS(piix_sidpr_shost);
-static struct scsi_host_template piix_sidpr_sht = {
+static const struct scsi_host_template piix_sidpr_sht = {
ATA_BMDMA_SHT(DRV_NAME),
.shost_groups = piix_sidpr_shost_groups,
};
static struct ata_port_operations piix_sidpr_sata_ops = {
.inherits = &piix_sata_ops,
- .hardreset = sata_std_hardreset,
+ .reset.hardreset = sata_std_hardreset,
.scr_read = piix_sidpr_scr_read,
.scr_write = piix_sidpr_scr_write,
.set_lpm = piix_sidpr_set_lpm,
@@ -1446,7 +1447,6 @@ static int piix_init_sidpr(struct ata_host *host)
if (hpriv->map[i] == IDE)
return 0;
- /* is it blacklisted? */
if (piix_no_sidpr(host))
return 0;
@@ -1645,7 +1645,7 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct device *dev = &pdev->dev;
struct ata_port_info port_info[2];
const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] };
- struct scsi_host_template *sht = &piix_sht;
+ const struct scsi_host_template *sht = &piix_sht;
unsigned long port_flags;
struct ata_host *host;
struct piix_host_priv *hpriv;
@@ -1726,7 +1726,7 @@ static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
* message-signalled interrupts currently).
*/
if (port_flags & PIIX_FLAG_CHECKINTR)
- pci_intx(pdev, 1);
+ pcim_intx(pdev, 1);
if (piix_check_450nx_errata(pdev)) {
/* This writes into the master table but it does not
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index cf8c7fd59ada..c79abdfcd7a9 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -16,6 +16,7 @@
* http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
@@ -54,7 +55,8 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
-static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
+static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
+static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask);
static int ahci_port_start(struct ata_port *ap);
static void ahci_port_stop(struct ata_port *ap);
static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc);
@@ -109,6 +111,7 @@ static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);
static struct attribute *ahci_shost_attrs[] = {
+ &dev_attr_link_power_management_supported.attr,
&dev_attr_link_power_management_policy.attr,
&dev_attr_em_message_type.attr,
&dev_attr_em_message.attr,
@@ -156,13 +159,14 @@ struct ata_port_operations ahci_ops = {
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
.qc_fill_rtf = ahci_qc_fill_rtf,
+ .qc_ncq_fill_rtf = ahci_qc_ncq_fill_rtf,
.freeze = ahci_freeze,
.thaw = ahci_thaw,
- .softreset = ahci_softreset,
- .hardreset = ahci_hardreset,
- .postreset = ahci_postreset,
- .pmp_softreset = ahci_softreset,
+ .reset.softreset = ahci_softreset,
+ .reset.hardreset = ahci_hardreset,
+ .reset.postreset = ahci_postreset,
+ .pmp_reset.softreset = ahci_softreset,
.error_handler = ahci_error_handler,
.post_internal_cmd = ahci_post_internal_cmd,
.dev_config = ahci_dev_config,
@@ -189,7 +193,7 @@ EXPORT_SYMBOL_GPL(ahci_ops);
struct ata_port_operations ahci_pmp_retry_srst_ops = {
.inherits = &ahci_ops,
- .softreset = ahci_pmp_retry_softreset,
+ .reset.softreset = ahci_pmp_retry_softreset,
};
EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops);
@@ -443,17 +447,28 @@ static ssize_t ahci_show_em_supported(struct device *dev,
void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
{
void __iomem *mmio = hpriv->mmio;
- u32 cap, cap2, vers, port_map;
+ void __iomem *port_mmio;
+ unsigned long port_map;
+ u32 cap, cap2, vers;
int i;
/* make sure AHCI mode is enabled before accessing CAP */
ahci_enable_ahci(mmio);
- /* Values prefixed with saved_ are written back to host after
- * reset. Values without are used for driver operation.
+ /*
+ * Values prefixed with saved_ are written back to the HBA and ports
+ * registers after reset. Values without are used for driver operation.
+ */
+
+ /*
+ * Override HW-init HBA capability fields with the platform-specific
+ * values. The rest of the HBA capabilities are defined as Read-only
+ * and can't be modified in CSR anyway.
*/
- hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
- hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
+ cap = readl(mmio + HOST_CAP);
+ if (hpriv->saved_cap)
+ cap = (cap & ~(HOST_CAP_SSS | HOST_CAP_MPS)) | hpriv->saved_cap;
+ hpriv->saved_cap = cap;
/* CAP2 register is only defined for AHCI 1.2 and later */
vers = readl(mmio + HOST_VERSION);
@@ -517,15 +532,19 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
cap &= ~HOST_CAP_SXS;
}
- if (hpriv->force_port_map && port_map != hpriv->force_port_map) {
- dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
- port_map, hpriv->force_port_map);
- port_map = hpriv->force_port_map;
+ /* Override the HBA ports mapping if the platform needs it */
+ port_map = readl(mmio + HOST_PORTS_IMPL);
+ if (hpriv->saved_port_map && port_map != hpriv->saved_port_map) {
+ dev_info(dev, "forcing port_map 0x%lx -> 0x%x\n",
+ port_map, hpriv->saved_port_map);
+ port_map = hpriv->saved_port_map;
+ } else {
hpriv->saved_port_map = port_map;
}
+ /* mask_port_map not set means that all ports are available */
if (hpriv->mask_port_map) {
- dev_warn(dev, "masking port_map 0x%x -> 0x%x\n",
+ dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n",
port_map,
port_map & hpriv->mask_port_map);
port_map &= hpriv->mask_port_map;
@@ -544,7 +563,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
*/
if (map_ports > ahci_nr_ports(cap)) {
dev_warn(dev,
- "implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n",
+ "implemented port map (0x%lx) contains more ports than nr_ports (%u), using nr_ports\n",
port_map, ahci_nr_ports(cap));
port_map = 0;
}
@@ -553,16 +572,30 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
/* fabricate port_map from cap.nr_ports for < AHCI 1.3 */
if (!port_map && vers < 0x10300) {
port_map = (1 << ahci_nr_ports(cap)) - 1;
- dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
+ dev_warn(dev, "forcing PORTS_IMPL to 0x%lx\n", port_map);
/* write the fixed up value to the PI register */
hpriv->saved_port_map = port_map;
}
+ /*
+ * Preserve the ports capabilities defined by the platform. Note there
+ * is no need in storing the rest of the P#.CMD fields since they are
+ * volatile.
+ */
+ for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
+ if (hpriv->saved_port_cap[i])
+ continue;
+
+ port_mmio = __ahci_port_base(hpriv, i);
+ hpriv->saved_port_cap[i] =
+ readl(port_mmio + PORT_CMD) & PORT_CMD_CAP;
+ }
+
/* record values to use during operation */
hpriv->cap = cap;
hpriv->cap2 = cap2;
- hpriv->version = readl(mmio + HOST_VERSION);
+ hpriv->version = vers;
hpriv->port_map = port_map;
if (!hpriv->start_engine)
@@ -588,13 +621,21 @@ EXPORT_SYMBOL_GPL(ahci_save_initial_config);
static void ahci_restore_initial_config(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
+ unsigned long port_map = hpriv->port_map;
void __iomem *mmio = hpriv->mmio;
+ void __iomem *port_mmio;
+ int i;
writel(hpriv->saved_cap, mmio + HOST_CAP);
if (hpriv->saved_cap2)
writel(hpriv->saved_cap2, mmio + HOST_CAP2);
writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
(void) readl(mmio + HOST_PORTS_IMPL); /* flush */
+
+ for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
+ port_mmio = __ahci_port_base(hpriv, i);
+ writel(hpriv->saved_port_cap[i], port_mmio + PORT_CMD);
+ }
}
static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg)
@@ -936,44 +977,43 @@ int ahci_reset_controller(struct ata_host *host)
void __iomem *mmio = hpriv->mmio;
u32 tmp;
- /* we must be in AHCI mode, before using anything
- * AHCI-specific, such as HOST_RESET.
+ /*
+ * We must be in AHCI mode, before using anything AHCI-specific, such
+ * as HOST_RESET.
*/
ahci_enable_ahci(mmio);
- /* global controller reset */
- if (!ahci_skip_host_reset) {
- tmp = readl(mmio + HOST_CTL);
- if ((tmp & HOST_RESET) == 0) {
- writel(tmp | HOST_RESET, mmio + HOST_CTL);
- readl(mmio + HOST_CTL); /* flush */
- }
+ /* Global controller reset */
+ if (ahci_skip_host_reset) {
+ dev_info(host->dev, "Skipping global host reset\n");
+ return 0;
+ }
- /*
- * to perform host reset, OS should set HOST_RESET
- * and poll until this bit is read to be "0".
- * reset must complete within 1 second, or
- * the hardware should be considered fried.
- */
- tmp = ata_wait_register(NULL, mmio + HOST_CTL, HOST_RESET,
- HOST_RESET, 10, 1000);
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_RESET)) {
+ writel(tmp | HOST_RESET, mmio + HOST_CTL);
+ readl(mmio + HOST_CTL); /* flush */
+ }
- if (tmp & HOST_RESET) {
- dev_err(host->dev, "controller reset failed (0x%x)\n",
- tmp);
- return -EIO;
- }
+ /*
+ * To perform host reset, OS should set HOST_RESET and poll until this
+ * bit is read to be "0". Reset must complete within 1 second, or the
+ * hardware should be considered fried.
+ */
+ tmp = ata_wait_register(NULL, mmio + HOST_CTL, HOST_RESET,
+ HOST_RESET, 10, 1000);
+ if (tmp & HOST_RESET) {
+ dev_err(host->dev, "Controller reset failed (0x%x)\n",
+ tmp);
+ return -EIO;
+ }
- /* turn on AHCI mode */
- ahci_enable_ahci(mmio);
+ /* Turn on AHCI mode */
+ ahci_enable_ahci(mmio);
- /* Some registers might be cleared on reset. Restore
- * initial values.
- */
- if (!(hpriv->flags & AHCI_HFLAG_NO_WRITE_TO_RO))
- ahci_restore_initial_config(host);
- } else
- dev_info(host->dev, "skipping global host reset\n");
+ /* Some registers might be cleared on reset. Restore initial values. */
+ if (!(hpriv->flags & AHCI_HFLAG_NO_WRITE_TO_RO))
+ ahci_restore_initial_config(host);
return 0;
}
@@ -995,7 +1035,7 @@ static void ahci_sw_activity(struct ata_link *link)
static void ahci_sw_activity_blink(struct timer_list *t)
{
- struct ahci_em_priv *emp = from_timer(emp, t, timer);
+ struct ahci_em_priv *emp = timer_container_of(emp, t, timer);
struct ata_link *link = emp->link;
struct ata_port *ap = link->ap;
@@ -1218,37 +1258,39 @@ static ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
return sprintf(buf, "%d\n", emp->blink_policy);
}
-static void ahci_port_init(struct device *dev, struct ata_port *ap,
- int port_no, void __iomem *mmio,
- void __iomem *port_mmio)
+static void ahci_port_clear_pending_irq(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
- const char *emsg = NULL;
- int rc;
+ void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
- /* make sure port is not active */
- rc = ahci_deinit_port(ap, &emsg);
- if (rc)
- dev_warn(dev, "%s (%d)\n", emsg, rc);
-
/* clear SError */
tmp = readl(port_mmio + PORT_SCR_ERR);
- dev_dbg(dev, "PORT_SCR_ERR 0x%x\n", tmp);
+ dev_dbg(ap->host->dev, "PORT_SCR_ERR 0x%x\n", tmp);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* clear port IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
- dev_dbg(dev, "PORT_IRQ_STAT 0x%x\n", tmp);
+ dev_dbg(ap->host->dev, "PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
- writel(1 << port_no, mmio + HOST_IRQ_STAT);
+ writel(1 << ap->port_no, hpriv->mmio + HOST_IRQ_STAT);
+}
- /* mark esata ports */
- tmp = readl(port_mmio + PORT_CMD);
- if ((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS))
- ap->pflags |= ATA_PFLAG_EXTERNAL;
+static void ahci_port_init(struct device *dev, struct ata_port *ap,
+ int port_no, void __iomem *mmio,
+ void __iomem *port_mmio)
+{
+ const char *emsg = NULL;
+ int rc;
+
+ /* make sure port is not active */
+ rc = ahci_deinit_port(ap, &emsg);
+ if (rc)
+ dev_warn(dev, "%s (%d)\n", emsg, rc);
+
+ ahci_port_clear_pending_irq(ap);
}
void ahci_init_controller(struct ata_host *host)
@@ -1281,6 +1323,10 @@ static void ahci_dev_config(struct ata_device *dev)
{
struct ahci_host_priv *hpriv = dev->link->ap->host->private_data;
+ if ((dev->class == ATA_DEV_ATAPI) &&
+ (hpriv->flags & AHCI_HFLAG_ATAPI_DMA_QUIRK))
+ dev->quirks |= ATA_QUIRK_ATAPI_MOD16_DMA;
+
if (hpriv->flags & AHCI_HFLAG_SECT255) {
dev->max_sectors = 255;
ata_dev_info(dev,
@@ -1365,7 +1411,7 @@ EXPORT_SYMBOL_GPL(ahci_kick_engine);
static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
struct ata_taskfile *tf, int is_cmd, u16 flags,
- unsigned long timeout_msec)
+ unsigned int timeout_msec)
{
const u32 cmd_fis_len = 5; /* five dwords */
struct ahci_port_priv *pp = ap->private_data;
@@ -1410,7 +1456,8 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class,
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
const char *reason = NULL;
- unsigned long now, msecs;
+ unsigned long now;
+ unsigned int msecs;
struct ata_taskfile tf;
bool fbs_disabled = false;
int rc;
@@ -1549,7 +1596,7 @@ static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
int ahci_do_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline, bool *online)
{
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
@@ -1564,6 +1611,8 @@ int ahci_do_hardreset(struct ata_link *link, unsigned int *class,
tf.status = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
+ ahci_port_clear_pending_irq(ap);
+
rc = sata_link_hardreset(link, timing, deadline, online,
ahci_check_ready);
@@ -1810,18 +1859,47 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
ata_port_abort(ap);
}
-static void ahci_handle_port_interrupt(struct ata_port *ap,
- void __iomem *port_mmio, u32 status)
+static void ahci_qc_complete(struct ata_port *ap, void __iomem *port_mmio)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
struct ahci_port_priv *pp = ap->private_data;
- struct ahci_host_priv *hpriv = ap->host->private_data;
- int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
u32 qc_active = 0;
int rc;
+ /*
+ * pp->active_link is not reliable once FBS is enabled, both
+ * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
+ * NCQ and non-NCQ commands may be in flight at the same time.
+ */
+ if (pp->fbs_enabled) {
+ if (ap->qc_active) {
+ qc_active = readl(port_mmio + PORT_SCR_ACT);
+ qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
+ }
+ } else {
+ /* pp->active_link is valid iff any command is in flight */
+ if (ap->qc_active && pp->active_link->sactive)
+ qc_active = readl(port_mmio + PORT_SCR_ACT);
+ else
+ qc_active = readl(port_mmio + PORT_CMD_ISSUE);
+ }
+
+ rc = ata_qc_complete_multiple(ap, qc_active);
+ if (unlikely(rc < 0 && !(ap->pflags & ATA_PFLAG_RESETTING))) {
+ ehi->err_mask |= AC_ERR_HSM;
+ ehi->action |= ATA_EH_RESET;
+ ata_port_freeze(ap);
+ }
+}
+
+static void ahci_handle_port_interrupt(struct ata_port *ap,
+ void __iomem *port_mmio, u32 status)
+{
+ struct ahci_port_priv *pp = ap->private_data;
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+
/* ignore BAD_PMP while resetting */
- if (unlikely(resetting))
+ if (unlikely(ap->pflags & ATA_PFLAG_RESETTING))
status &= ~PORT_IRQ_BAD_PMP;
if (sata_lpm_ignore_phy_events(&ap->link)) {
@@ -1830,6 +1908,12 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
}
if (unlikely(status & PORT_IRQ_ERROR)) {
+ /*
+ * Before getting the error notification, we may have
+ * received SDB FISes notifying successful completions.
+ * Handle these first and then handle the error.
+ */
+ ahci_qc_complete(ap, port_mmio);
ahci_error_intr(ap, status);
return;
}
@@ -1866,32 +1950,8 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
}
}
- /* pp->active_link is not reliable once FBS is enabled, both
- * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
- * NCQ and non-NCQ commands may be in flight at the same time.
- */
- if (pp->fbs_enabled) {
- if (ap->qc_active) {
- qc_active = readl(port_mmio + PORT_SCR_ACT);
- qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
- }
- } else {
- /* pp->active_link is valid iff any command is in flight */
- if (ap->qc_active && pp->active_link->sactive)
- qc_active = readl(port_mmio + PORT_SCR_ACT);
- else
- qc_active = readl(port_mmio + PORT_CMD_ISSUE);
- }
-
-
- rc = ata_qc_complete_multiple(ap, qc_active);
-
- /* while resetting, invalid completions are expected */
- if (unlikely(rc < 0 && !resetting)) {
- ehi->err_mask |= AC_ERR_HSM;
- ehi->action |= ATA_EH_RESET;
- ata_port_freeze(ap);
- }
+ /* Handle completed commands */
+ ahci_qc_complete(ap, port_mmio);
}
static void ahci_port_intr(struct ata_port *ap)
@@ -2016,7 +2076,7 @@ unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
}
EXPORT_SYMBOL_GPL(ahci_qc_issue);
-static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
+static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct ahci_port_priv *pp = qc->ap->private_data;
u8 *rx_fis = pp->rx_fis;
@@ -2031,13 +2091,99 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
* Setup FIS.
*/
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
- !(qc->flags & ATA_QCFLAG_FAILED)) {
+ !(qc->flags & ATA_QCFLAG_EH)) {
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
- } else
- ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
+ return;
+ }
+
+ /*
+ * For NCQ commands, we never get a D2H FIS, so reading the D2H Register
+ * FIS area of the Received FIS Structure (which contains a copy of the
+ * last D2H FIS received) will contain an outdated status code.
+ * For NCQ commands, we instead get a SDB FIS, so read the SDB FIS area
+ * instead. However, the SDB FIS does not contain the LBA, so we can't
+ * use the ata_tf_from_fis() helper.
+ */
+ if (ata_is_ncq(qc->tf.protocol)) {
+ const u8 *fis = rx_fis + RX_FIS_SDB;
+
+ /*
+ * Successful NCQ commands have been filled already.
+ * A failed NCQ command will read the status here.
+ * (Note that a failed NCQ command will get a more specific
+ * error when reading the NCQ Command Error log.)
+ */
+ qc->result_tf.status = fis[2];
+ qc->result_tf.error = fis[3];
+ return;
+ }
- return true;
+ ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
+}
+
+static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask)
+{
+ struct ahci_port_priv *pp = ap->private_data;
+ const u8 *fis;
+
+ /* No outstanding commands. */
+ if (!ap->qc_active)
+ return;
+
+ /*
+ * FBS not enabled, so read status and error once, since they are shared
+ * for all QCs.
+ */
+ if (!pp->fbs_enabled) {
+ u8 status, error;
+
+ /* No outstanding NCQ commands. */
+ if (!pp->active_link->sactive)
+ return;
+
+ fis = pp->rx_fis + RX_FIS_SDB;
+ status = fis[2];
+ error = fis[3];
+
+ while (done_mask) {
+ struct ata_queued_cmd *qc;
+ unsigned int tag = __ffs64(done_mask);
+
+ qc = ata_qc_from_tag(ap, tag);
+ if (qc && ata_is_ncq(qc->tf.protocol)) {
+ qc->result_tf.status = status;
+ qc->result_tf.error = error;
+ qc->result_tf.flags = qc->tf.flags;
+ qc->flags |= ATA_QCFLAG_RTF_FILLED;
+ }
+ done_mask &= ~(1ULL << tag);
+ }
+
+ return;
+ }
+
+ /*
+ * FBS enabled, so read the status and error for each QC, since the QCs
+ * can belong to different PMP links. (Each PMP link has its own FIS
+ * Receive Area.)
+ */
+ while (done_mask) {
+ struct ata_queued_cmd *qc;
+ unsigned int tag = __ffs64(done_mask);
+
+ qc = ata_qc_from_tag(ap, tag);
+ if (qc && ata_is_ncq(qc->tf.protocol)) {
+ fis = pp->rx_fis;
+ fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
+ fis += RX_FIS_SDB;
+ qc->result_tf.status = fis[2];
+ qc->result_tf.error = fis[3];
+ qc->result_tf.flags = qc->tf.flags;
+ qc->flags |= ATA_QCFLAG_RTF_FILLED;
+ }
+ done_mask &= ~(1ULL << tag);
+ }
}
static void ahci_freeze(struct ata_port *ap)
@@ -2069,7 +2215,7 @@ void ahci_error_handler(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
- if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
+ if (!ata_port_is_frozen(ap)) {
/* restart engine */
hpriv->stop_engine(ap);
hpriv->start_engine(ap);
@@ -2087,7 +2233,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
/* make DMA engine forget about the failed command */
- if (qc->flags & ATA_QCFLAG_FAILED)
+ if (qc->flags & ATA_QCFLAG_EH)
ahci_kick_engine(ap);
}
@@ -2260,7 +2406,7 @@ static void ahci_pmp_attach(struct ata_port *ap)
* Note that during initialization, the port is marked as
* frozen since the irq handler is not yet registered.
*/
- if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ if (!ata_port_is_frozen(ap))
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
@@ -2279,7 +2425,7 @@ static void ahci_pmp_detach(struct ata_port *ap)
pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
/* see comment above in ahci_pmp_attach() */
- if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ if (!ata_port_is_frozen(ap))
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
@@ -2472,8 +2618,8 @@ void ahci_print_info(struct ata_host *host, const char *scc_s)
speed_s = "?";
dev_info(host->dev,
- "AHCI %02x%02x.%02x%02x "
- "%u slots %u ports %s Gbps 0x%x impl %s mode\n"
+ "AHCI vers %02x%02x.%02x%02x, "
+ "%u command slots, %s Gbps, %s mode\n"
,
(vers >> 24) & 0xff,
@@ -2482,12 +2628,18 @@ void ahci_print_info(struct ata_host *host, const char *scc_s)
vers & 0xff,
((cap >> 8) & 0x1f) + 1,
- (cap & 0x1f) + 1,
speed_s,
- impl,
scc_s);
dev_info(host->dev,
+ "%u/%u ports implemented (port mask 0x%x)\n"
+ ,
+
+ hweight32(impl),
+ (cap & 0x1f) + 1,
+ impl);
+
+ dev_info(host->dev,
"flags: "
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
@@ -2548,7 +2700,7 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
EXPORT_SYMBOL_GPL(ahci_set_em_messages);
static int ahci_host_activate_multi_irqs(struct ata_host *host,
- struct scsi_host_template *sht)
+ const struct scsi_host_template *sht)
{
struct ahci_host_priv *hpriv = host->private_data;
int i, rc;
@@ -2575,7 +2727,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
if (rc)
return rc;
- ata_port_desc(host->ports[i], "irq %d", irq);
+ ata_port_desc_misc(host->ports[i], irq);
}
return ata_host_register(host, sht);
@@ -2592,7 +2744,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
* RETURNS:
* 0 on success, -errno otherwise.
*/
-int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
+int ahci_host_activate(struct ata_host *host, const struct scsi_host_template *sht)
{
struct ahci_host_priv *hpriv = host->private_data;
int irq = hpriv->irq;
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index 32495ae96567..91d44302eac9 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -20,6 +20,7 @@
#include <linux/ahci_platform.h>
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/reset.h>
#include "ahci.h"
@@ -48,6 +49,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
int rc, i;
for (i = 0; i < hpriv->nports; i++) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
rc = phy_init(hpriv->phys[i]);
if (rc)
goto disable_phys;
@@ -69,6 +73,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)
disable_phys:
while (--i >= 0) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
phy_power_off(hpriv->phys[i]);
phy_exit(hpriv->phys[i]);
}
@@ -87,6 +94,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
int i;
for (i = 0; i < hpriv->nports; i++) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
phy_power_off(hpriv->phys[i]);
phy_exit(hpriv->phys[i]);
}
@@ -94,31 +104,41 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)
EXPORT_SYMBOL_GPL(ahci_platform_disable_phys);
/**
- * ahci_platform_enable_clks - Enable platform clocks
+ * ahci_platform_find_clk - Find platform clock
* @hpriv: host private area to store config values
+ * @con_id: clock connection ID
*
- * This function enables all the clks found in hpriv->clks, starting at
- * index 0. If any clk fails to enable it disables all the clks already
- * enabled in reverse order, and then returns an error.
+ * This function returns a pointer to the clock descriptor of the clock with
+ * the passed ID.
*
* RETURNS:
- * 0 on success otherwise a negative error code
+ * Pointer to the clock descriptor on success otherwise NULL
*/
-int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
+struct clk *ahci_platform_find_clk(struct ahci_host_priv *hpriv, const char *con_id)
{
- int c, rc;
+ int i;
- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
- rc = clk_prepare_enable(hpriv->clks[c]);
- if (rc)
- goto disable_unprepare_clk;
+ for (i = 0; i < hpriv->n_clks; i++) {
+ if (hpriv->clks[i].id && !strcmp(hpriv->clks[i].id, con_id))
+ return hpriv->clks[i].clk;
}
- return 0;
-disable_unprepare_clk:
- while (--c >= 0)
- clk_disable_unprepare(hpriv->clks[c]);
- return rc;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(ahci_platform_find_clk);
+
+/**
+ * ahci_platform_enable_clks - Enable platform clocks
+ * @hpriv: host private area to store config values
+ *
+ * This function enables all the clks found for the AHCI device.
+ *
+ * RETURNS:
+ * 0 on success otherwise a negative error code
+ */
+int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
+{
+ return clk_bulk_prepare_enable(hpriv->n_clks, hpriv->clks);
}
EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
@@ -126,20 +146,55 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
* ahci_platform_disable_clks - Disable platform clocks
* @hpriv: host private area to store config values
*
- * This function disables all the clks found in hpriv->clks, in reverse
- * order of ahci_platform_enable_clks (starting at the end of the array).
+ * This function disables all the clocks enabled before
+ * (bulk-clocks-disable function is supposed to do that in reverse
+ * from the enabling procedure order).
*/
void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
{
- int c;
-
- for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
- if (hpriv->clks[c])
- clk_disable_unprepare(hpriv->clks[c]);
+ clk_bulk_disable_unprepare(hpriv->n_clks, hpriv->clks);
}
EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
/**
+ * ahci_platform_deassert_rsts - Deassert/trigger platform resets
+ * @hpriv: host private area to store config values
+ *
+ * This function deasserts or triggers all the reset lines found for
+ * the AHCI device.
+ *
+ * RETURNS:
+ * 0 on success otherwise a negative error code
+ */
+int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv)
+{
+ if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER)
+ return reset_control_reset(hpriv->rsts);
+
+ return reset_control_deassert(hpriv->rsts);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_deassert_rsts);
+
+/**
+ * ahci_platform_assert_rsts - Assert/rearm platform resets
+ * @hpriv: host private area to store config values
+ *
+ * This function asserts or rearms (for self-deasserting resets) all
+ * the reset controls found for the AHCI device.
+ *
+ * RETURNS:
+ * 0 on success otherwise a negative error code
+ */
+int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv)
+{
+ if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER)
+ return reset_control_rearm(hpriv->rsts);
+
+ return reset_control_assert(hpriv->rsts);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_assert_rsts);
+
+/**
* ahci_platform_enable_regulators - Enable regulators
* @hpriv: host private area to store config values
*
@@ -236,18 +291,18 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
if (rc)
goto disable_regulator;
- rc = reset_control_deassert(hpriv->rsts);
+ rc = ahci_platform_deassert_rsts(hpriv);
if (rc)
goto disable_clks;
rc = ahci_platform_enable_phys(hpriv);
if (rc)
- goto disable_resets;
+ goto disable_rsts;
return 0;
-disable_resets:
- reset_control_assert(hpriv->rsts);
+disable_rsts:
+ ahci_platform_assert_rsts(hpriv);
disable_clks:
ahci_platform_disable_clks(hpriv);
@@ -274,7 +329,7 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
{
ahci_platform_disable_phys(hpriv);
- reset_control_assert(hpriv->rsts);
+ ahci_platform_assert_rsts(hpriv);
ahci_platform_disable_clks(hpriv);
@@ -292,8 +347,6 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
pm_runtime_disable(dev);
}
- for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
- clk_put(hpriv->clks[c]);
/*
* The regulators are tied to child node device and not to the
* SATA device itself. So we can't use devm for automatically
@@ -320,7 +373,7 @@ static int ahci_platform_get_phy(struct ahci_host_priv *hpriv, u32 port,
switch (rc) {
case -ENOSYS:
/* No PHY support. Check if PHY is required. */
- if (of_find_property(node, "phys", NULL)) {
+ if (of_property_present(node, "phys")) {
dev_err(dev,
"couldn't get PHY in node %pOFn: ENOSYS\n",
node);
@@ -363,6 +416,45 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port,
return rc;
}
+static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv,
+ struct device *dev)
+{
+ u32 port;
+
+ if (!of_property_read_u32(dev->of_node, "hba-cap", &hpriv->saved_cap))
+ hpriv->saved_cap &= (HOST_CAP_SSS | HOST_CAP_MPS);
+
+ of_property_read_u32(dev->of_node,
+ "ports-implemented", &hpriv->saved_port_map);
+
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ if (!of_device_is_available(child))
+ continue;
+
+ if (of_property_read_u32(child, "reg", &port))
+ return -EINVAL;
+
+ if (!of_property_read_u32(child, "hba-port-cap", &hpriv->saved_port_cap[port]))
+ hpriv->saved_port_cap[port] &= PORT_CMD_CAP;
+ }
+
+ return 0;
+}
+
+static u32 ahci_platform_find_max_port_id(struct device *dev)
+{
+ u32 max_port = 0;
+
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ u32 port;
+
+ if (!of_property_read_u32(child, "reg", &port))
+ max_port = max(max_port, port);
+ }
+
+ return max_port;
+}
+
/**
* ahci_platform_get_resources - Get platform resources
* @pdev: platform device to get resources for
@@ -374,8 +466,8 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port,
* 1) mmio registers (IORESOURCE_MEM 0, mandatory)
* 2) regulator for controlling the targets power (optional)
* regulator for controlling the AHCI controller (optional)
- * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
- * or for non devicetree enabled platforms a single clock
+ * 3) all clocks specified in the devicetree node, or a single
+ * clock for non-OF platforms (optional)
* 4) resets, if flags has AHCI_PLATFORM_GET_RESETS (optional)
* 5) phys (optional)
*
@@ -385,12 +477,11 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port,
struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
unsigned int flags)
{
+ int child_nodes, rc = -ENOMEM, enabled_ports = 0;
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
- struct clk *clk;
- struct device_node *child;
- int i, enabled_ports = 0, rc = -ENOMEM, child_nodes;
u32 mask_port_map = 0;
+ u32 max_port;
if (!devres_open_group(dev, NULL, GFP_KERNEL))
return ERR_PTR(-ENOMEM);
@@ -402,32 +493,51 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
devres_add(dev, hpriv);
- hpriv->mmio = devm_ioremap_resource(dev,
- platform_get_resource(pdev, IORESOURCE_MEM, 0));
+ /*
+ * If the DT provided an "ahci" named resource, use it. Otherwise,
+ * fallback to using the default first resource for the device node.
+ */
+ if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci"))
+ hpriv->mmio = devm_platform_ioremap_resource_byname(pdev, "ahci");
+ else
+ hpriv->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hpriv->mmio)) {
rc = PTR_ERR(hpriv->mmio);
goto err_out;
}
- for (i = 0; i < AHCI_MAX_CLKS; i++) {
+ /*
+ * Bulk clocks getting procedure can fail to find any clock due to
+ * running on a non-OF platform or due to the clocks being defined in
+ * bypass of the DT firmware (like da850, spear13xx). In that case we
+ * fallback to getting a single clock source right from the dev clocks
+ * list.
+ */
+ rc = devm_clk_bulk_get_all(dev, &hpriv->clks);
+ if (rc < 0)
+ goto err_out;
+
+ if (rc > 0) {
+ /* Got clocks in bulk */
+ hpriv->n_clks = rc;
+ } else {
/*
- * For now we must use clk_get(dev, NULL) for the first clock,
- * because some platforms (da850, spear13xx) are not yet
- * converted to use devicetree for clocks. For new platforms
- * this is equivalent to of_clk_get(dev->of_node, 0).
+ * No clock bulk found: fallback to manually getting
+ * the optional clock.
*/
- if (i == 0)
- clk = clk_get(dev, NULL);
- else
- clk = of_clk_get(dev->of_node, i);
-
- if (IS_ERR(clk)) {
- rc = PTR_ERR(clk);
- if (rc == -EPROBE_DEFER)
- goto err_out;
- break;
+ hpriv->clks = devm_kzalloc(dev, sizeof(*hpriv->clks), GFP_KERNEL);
+ if (!hpriv->clks) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ hpriv->clks->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(hpriv->clks->clk)) {
+ rc = PTR_ERR(hpriv->clks->clk);
+ goto err_out;
+ } else if (hpriv->clks->clk) {
+ hpriv->clks->id = "ahci";
+ hpriv->n_clks = 1;
}
- hpriv->clks[i] = clk;
}
hpriv->ahci_regulator = devm_regulator_get(dev, "ahci");
@@ -449,17 +559,31 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
rc = PTR_ERR(hpriv->rsts);
goto err_out;
}
+
+ hpriv->f_rsts = flags & AHCI_PLATFORM_RST_TRIGGER;
}
- hpriv->nports = child_nodes = of_get_child_count(dev->of_node);
+ /*
+ * Too many sub-nodes most likely means having something wrong with
+ * the firmware.
+ */
+ child_nodes = of_get_child_count(dev->of_node);
+ if (child_nodes > AHCI_MAX_PORTS) {
+ rc = -EINVAL;
+ goto err_out;
+ }
+ /* find maximum port id for allocating structures */
+ max_port = ahci_platform_find_max_port_id(dev);
/*
- * If no sub-node was found, we still need to set nports to
- * one in order to be able to use the
+ * Set nports according to maximum port id. Clamp at
+ * AHCI_MAX_PORTS, warning message for invalid port id
+ * is generated later.
+ * When DT has no sub-nodes max_port is 0, nports is 1,
+ * in order to be able to use the
* ahci_platform_[en|dis]able_[phys|regulators] functions.
*/
- if (!child_nodes)
- hpriv->nports = 1;
+ hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1);
hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL);
if (!hpriv->phys) {
@@ -477,7 +601,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
}
if (child_nodes) {
- for_each_child_of_node(dev->of_node, child) {
+ for_each_child_of_node_scoped(dev->of_node, child) {
u32 port;
struct platform_device *port_dev __maybe_unused;
@@ -486,7 +610,6 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
if (of_property_read_u32(child, "reg", &port)) {
rc = -EINVAL;
- of_node_put(child);
goto err_out;
}
@@ -504,18 +627,14 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
if (port_dev) {
rc = ahci_platform_get_regulator(hpriv, port,
&port_dev->dev);
- if (rc == -EPROBE_DEFER) {
- of_node_put(child);
+ if (rc == -EPROBE_DEFER)
goto err_out;
- }
}
#endif
rc = ahci_platform_get_phy(hpriv, port, dev, child);
- if (rc) {
- of_node_put(child);
+ if (rc)
goto err_out;
- }
enabled_ports++;
}
@@ -540,6 +659,15 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,
if (rc == -EPROBE_DEFER)
goto err_out;
}
+
+ /*
+ * Retrieve firmware-specific flags which then will be used to set
+ * the HW-init fields of HBA and its ports
+ */
+ rc = ahci_platform_get_firmware(hpriv, dev);
+ if (rc)
+ goto err_out;
+
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
hpriv->got_runtime_pm = true;
@@ -570,7 +698,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
int ahci_platform_init_host(struct platform_device *pdev,
struct ahci_host_priv *hpriv,
const struct ata_port_info *pi_template,
- struct scsi_host_template *sht)
+ const struct scsi_host_template *sht)
{
struct device *dev = &pdev->dev;
struct ata_port_info pi = *pi_template;
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 61b4ccf88bf1..15e18d50dcc6 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -86,7 +86,7 @@ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
* @dev: ATA device ACPI event occurred (can be NULL)
* @event: ACPI event which occurred
*
- * All ACPI bay / device realted events end up in this function. If
+ * All ACPI bay / device related events end up in this function. If
* the event is port-wide @dev is NULL. If the event is specific to a
* device, @dev points to it.
*
@@ -246,6 +246,73 @@ void ata_acpi_bind_dev(struct ata_device *dev)
}
/**
+ * ata_acpi_dev_manage_restart - if the disk should be stopped (spun down) on
+ * system restart.
+ * @dev: target ATA device
+ *
+ * RETURNS:
+ * true if the disk should be stopped, otherwise false.
+ */
+bool ata_acpi_dev_manage_restart(struct ata_device *dev)
+{
+ struct device *tdev;
+
+ /*
+ * If ATA_FLAG_ACPI_SATA is set, the acpi fwnode is attached to the
+ * ata_device instead of the ata_port.
+ */
+ if (dev->link->ap->flags & ATA_FLAG_ACPI_SATA)
+ tdev = &dev->tdev;
+ else
+ tdev = &dev->link->ap->tdev;
+
+ if (!is_acpi_device_node(tdev->fwnode))
+ return false;
+ return acpi_bus_power_manageable(ACPI_HANDLE(tdev));
+}
+
+/**
+ * ata_acpi_port_power_on - set the power state of the ata port to D0
+ * @ap: target ATA port
+ *
+ * This function is called at the beginning of ata_port_probe().
+ */
+void ata_acpi_port_power_on(struct ata_port *ap)
+{
+ acpi_handle handle;
+ int i;
+
+ /*
+ * If ATA_FLAG_ACPI_SATA is set, the acpi fwnode is attached to the
+ * ata_device instead of the ata_port.
+ */
+ if (ap->flags & ATA_FLAG_ACPI_SATA) {
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->link.device[i];
+
+ if (!is_acpi_device_node(dev->tdev.fwnode))
+ continue;
+ handle = ACPI_HANDLE(&dev->tdev);
+ if (!acpi_bus_power_manageable(handle))
+ continue;
+ if (acpi_bus_set_power(handle, ACPI_STATE_D0))
+ ata_dev_err(dev,
+ "acpi: failed to set power state to D0\n");
+ }
+ return;
+ }
+
+ if (!is_acpi_device_node(ap->tdev.fwnode))
+ return;
+ handle = ACPI_HANDLE(&ap->tdev);
+ if (!acpi_bus_power_manageable(handle))
+ return;
+
+ if (acpi_bus_set_power(handle, ACPI_STATE_D0))
+ ata_port_err(ap, "acpi: failed to set power state to D0\n");
+}
+
+/**
* ata_acpi_dissociate - dissociate ATA host from ACPI objects
* @host: target ATA host
*
@@ -514,15 +581,19 @@ unsigned int ata_acpi_gtm_xfermask(struct ata_device *dev,
EXPORT_SYMBOL_GPL(ata_acpi_gtm_xfermask);
/**
- * ata_acpi_cbl_80wire - Check for 80 wire cable
+ * ata_acpi_cbl_pata_type - Return PATA cable type
* @ap: Port to check
- * @gtm: GTM data to use
*
- * Return 1 if the @gtm indicates the BIOS selected an 80wire mode.
+ * Return ATA_CBL_PATA* according to the transfer mode selected by BIOS
*/
-int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm)
+int ata_acpi_cbl_pata_type(struct ata_port *ap)
{
struct ata_device *dev;
+ int ret = ATA_CBL_PATA_UNK;
+ const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap);
+
+ if (!gtm)
+ return ATA_CBL_PATA40;
ata_for_each_dev(dev, &ap->link, ENABLED) {
unsigned int xfer_mask, udma_mask;
@@ -530,13 +601,17 @@ int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm)
xfer_mask = ata_acpi_gtm_xfermask(dev, gtm);
ata_unpack_xfermask(xfer_mask, NULL, NULL, &udma_mask);
- if (udma_mask & ~ATA_UDMA_MASK_40C)
- return 1;
+ ret = ATA_CBL_PATA40;
+
+ if (udma_mask & ~ATA_UDMA_MASK_40C) {
+ ret = ATA_CBL_PATA80;
+ break;
+ }
}
- return 0;
+ return ret;
}
-EXPORT_SYMBOL_GPL(ata_acpi_cbl_80wire);
+EXPORT_SYMBOL_GPL(ata_acpi_cbl_pata_type);
static void ata_acpi_gtf_to_tf(struct ata_device *dev,
const struct ata_acpi_gtf *gtf,
@@ -832,7 +907,7 @@ void ata_acpi_on_resume(struct ata_port *ap)
dev->flags |= ATA_DFLAG_ACPI_PENDING;
}
} else {
- /* SATA _GTF needs to be evaulated after _SDD and
+ /* SATA _GTF needs to be evaluated after _SDD and
* there's no reason to evaluate IDE _GTF early
* without _STM. Clear cache and schedule _GTF.
*/
@@ -992,7 +1067,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev)
acpi_err:
/* ignore evaluation failure if we can continue safely */
- if (rc == -EINVAL && !nr_executed && !(ap->pflags & ATA_PFLAG_FROZEN))
+ if (rc == -EINVAL && !nr_executed && !ata_port_is_frozen(ap))
return 0;
/* fail and let EH retry once more for unknown IO errors */
@@ -1007,7 +1082,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev)
/* We can safely continue if no _GTF command has been executed
* and port is not frozen.
*/
- if (!nr_executed && !(ap->pflags & ATA_PFLAG_FROZEN))
+ if (!nr_executed && !ata_port_is_frozen(ap))
return 0;
return rc;
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 826d41f341e4..0b24bd169d61 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -50,7 +50,7 @@
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/cdrom.h>
#include <linux/ratelimit.h>
#include <linux/leds.h>
@@ -65,28 +65,20 @@
#include "libata-transport.h"
const struct ata_port_operations ata_base_port_ops = {
- .prereset = ata_std_prereset,
- .postreset = ata_std_postreset,
+ .reset.prereset = ata_std_prereset,
+ .reset.postreset = ata_std_postreset,
.error_handler = ata_std_error_handler,
.sched_eh = ata_std_sched_eh,
.end_eh = ata_std_end_eh,
};
-const struct ata_port_operations sata_port_ops = {
- .inherits = &ata_base_port_ops,
-
- .qc_defer = ata_std_qc_defer,
- .hardreset = sata_std_hardreset,
-};
-EXPORT_SYMBOL_GPL(sata_port_ops);
-
static unsigned int ata_dev_init_params(struct ata_device *dev,
u16 heads, u16 sectors);
static unsigned int ata_dev_set_xfermode(struct ata_device *dev);
static void ata_dev_xfermask(struct ata_device *dev);
-static unsigned long ata_dev_blacklisted(const struct ata_device *dev);
+static unsigned int ata_dev_quirks(const struct ata_device *dev);
-atomic_t ata_print_id = ATOMIC_INIT(0);
+static DEFINE_IDA(ata_ida);
#ifdef CONFIG_ATA_FORCE
struct ata_force_param {
@@ -94,8 +86,9 @@ struct ata_force_param {
u8 cbl;
u8 spd_limit;
unsigned int xfer_mask;
- unsigned int horkage_on;
- unsigned int horkage_off;
+ unsigned int quirk_on;
+ unsigned int quirk_off;
+ unsigned int pflags_on;
u16 lflags_on;
u16 lflags_off;
};
@@ -160,18 +153,13 @@ MODULE_DESCRIPTION("Library module for ATA devices");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-static inline bool ata_dev_print_info(struct ata_device *dev)
+static inline bool ata_dev_print_info(const struct ata_device *dev)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
return ehc->i.flags & ATA_EHI_PRINTINFO;
}
-static bool ata_sstatus_online(u32 sstatus)
-{
- return (sstatus & 0xf) == 0x3;
-}
-
/**
* ata_link_next - link iteration helper
* @link: the previous link, NULL to start
@@ -345,6 +333,35 @@ void ata_force_cbl(struct ata_port *ap)
}
/**
+ * ata_force_pflags - force port flags according to libata.force
+ * @ap: ATA port of interest
+ *
+ * Force port flags according to libata.force and whine about it.
+ *
+ * LOCKING:
+ * EH context.
+ */
+static void ata_force_pflags(struct ata_port *ap)
+{
+ int i;
+
+ for (i = ata_force_tbl_size - 1; i >= 0; i--) {
+ const struct ata_force_ent *fe = &ata_force_tbl[i];
+
+ if (fe->port != -1 && fe->port != ap->print_id)
+ continue;
+
+ /* let pflags stack */
+ if (fe->param.pflags_on) {
+ ap->pflags |= fe->param.pflags_on;
+ ata_port_notice(ap,
+ "FORCE: port flag 0x%x forced -> 0x%x\n",
+ fe->param.pflags_on, ap->pflags);
+ }
+ }
+}
+
+/**
* ata_force_link_limits - force link limits according to libata.force
* @link: ATA link of interest
*
@@ -457,17 +474,17 @@ static void ata_force_xfermask(struct ata_device *dev)
}
/**
- * ata_force_horkage - force horkage according to libata.force
+ * ata_force_quirks - force quirks according to libata.force
* @dev: ATA device of interest
*
- * Force horkage according to libata.force and whine about it.
+ * Force quirks according to libata.force and whine about it.
* For consistency with link selection, device number 15 selects
* the first device connected to the host link.
*
* LOCKING:
* EH context.
*/
-static void ata_force_horkage(struct ata_device *dev)
+static void ata_force_quirks(struct ata_device *dev)
{
int devno = dev->link->pmp + dev->devno;
int alt_devno = devno;
@@ -487,21 +504,22 @@ static void ata_force_horkage(struct ata_device *dev)
fe->device != alt_devno)
continue;
- if (!(~dev->horkage & fe->param.horkage_on) &&
- !(dev->horkage & fe->param.horkage_off))
+ if (!(~dev->quirks & fe->param.quirk_on) &&
+ !(dev->quirks & fe->param.quirk_off))
continue;
- dev->horkage |= fe->param.horkage_on;
- dev->horkage &= ~fe->param.horkage_off;
+ dev->quirks |= fe->param.quirk_on;
+ dev->quirks &= ~fe->param.quirk_off;
- ata_dev_notice(dev, "FORCE: horkage modified (%s)\n",
+ ata_dev_notice(dev, "FORCE: modified (%s)\n",
fe->param.name);
}
}
#else
+static inline void ata_force_pflags(struct ata_port *ap) { }
static inline void ata_force_link_limits(struct ata_link *link) { }
static inline void ata_force_xfermask(struct ata_device *dev) { }
-static inline void ata_force_horkage(struct ata_device *dev) { }
+static inline void ata_force_quirks(struct ata_device *dev) { }
#endif
/**
@@ -552,7 +570,7 @@ static const u8 ata_rw_cmds[] = {
0,
0,
0,
- ATA_CMD_WRITE_MULTI_FUA_EXT,
+ 0,
/* pio */
ATA_CMD_PIO_READ,
ATA_CMD_PIO_WRITE,
@@ -574,17 +592,18 @@ static const u8 ata_rw_cmds[] = {
};
/**
- * ata_rwcmd_protocol - set taskfile r/w commands and protocol
- * @tf: command to examine and configure
- * @dev: device tf belongs to
+ * ata_set_rwcmd_protocol - set taskfile r/w command and protocol
+ * @dev: target device for the taskfile
+ * @tf: taskfile to examine and configure
*
- * Examine the device configuration and tf->flags to calculate
- * the proper read/write commands and protocol to use.
+ * Examine the device configuration and tf->flags to determine
+ * the proper read/write command and protocol to use for @tf.
*
* LOCKING:
* caller.
*/
-static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
+static bool ata_set_rwcmd_protocol(struct ata_device *dev,
+ struct ata_taskfile *tf)
{
u8 cmd;
@@ -607,11 +626,12 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
}
cmd = ata_rw_cmds[index + fua + lba48 + write];
- if (cmd) {
- tf->command = cmd;
- return 0;
- }
- return -1;
+ if (!cmd)
+ return false;
+
+ tf->command = cmd;
+
+ return true;
}
/**
@@ -663,35 +683,56 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
return block;
}
+/*
+ * Set a taskfile command duration limit index.
+ */
+static inline void ata_set_tf_cdl(struct ata_queued_cmd *qc, int cdl)
+{
+ struct ata_taskfile *tf = &qc->tf;
+
+ if (tf->protocol == ATA_PROT_NCQ)
+ tf->auxiliary |= cdl;
+ else
+ tf->feature |= cdl;
+
+ /*
+ * Mark this command as having a CDL and request the result
+ * task file so that we can inspect the sense data available
+ * bit on completion.
+ */
+ qc->flags |= ATA_QCFLAG_HAS_CDL | ATA_QCFLAG_RESULT_TF;
+}
+
/**
* ata_build_rw_tf - Build ATA taskfile for given read/write request
- * @tf: Target ATA taskfile
- * @dev: ATA device @tf belongs to
+ * @qc: Metadata associated with the taskfile to build
* @block: Block address
* @n_block: Number of blocks
* @tf_flags: RW/FUA etc...
- * @tag: tag
+ * @cdl: Command duration limit index
* @class: IO priority class
*
* LOCKING:
* None.
*
- * Build ATA taskfile @tf for read/write request described by
- * @block, @n_block, @tf_flags and @tag on @dev.
+ * Build ATA taskfile for the command @qc for read/write request described
+ * by @block, @n_block, @tf_flags and @class.
*
* RETURNS:
*
* 0 on success, -ERANGE if the request is too large for @dev,
* -EINVAL if the request is invalid.
*/
-int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
- u64 block, u32 n_block, unsigned int tf_flags,
- unsigned int tag, int class)
+int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block,
+ unsigned int tf_flags, int cdl, int class)
{
+ struct ata_taskfile *tf = &qc->tf;
+ struct ata_device *dev = qc->dev;
+
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf->flags |= tf_flags;
- if (ata_ncq_enabled(dev) && !ata_tag_internal(tag)) {
+ if (ata_ncq_enabled(dev)) {
/* yay, NCQ */
if (!lba_48_ok(block, n_block))
return -ERANGE;
@@ -704,7 +745,7 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
else
tf->command = ATA_CMD_FPDMA_READ;
- tf->nsect = tag << 3;
+ tf->nsect = qc->hw_tag << 3;
tf->hob_feature = (n_block >> 8) & 0xff;
tf->feature = n_block & 0xff;
@@ -719,13 +760,23 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
if (tf->flags & ATA_TFLAG_FUA)
tf->device |= 1 << 7;
- if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE &&
+ if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED &&
class == IOPRIO_CLASS_RT)
tf->hob_nsect |= ATA_PRIO_HIGH << ATA_SHIFT_PRIO;
+
+ if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl)
+ ata_set_tf_cdl(qc, cdl);
+
} else if (dev->flags & ATA_DFLAG_LBA) {
tf->flags |= ATA_TFLAG_LBA;
- if (lba_28_ok(block, n_block)) {
+ if ((dev->flags & ATA_DFLAG_CDL_ENABLED) && cdl)
+ ata_set_tf_cdl(qc, cdl);
+
+ /* Both FUA writes and a CDL index require 48-bit commands */
+ if (!(tf->flags & ATA_TFLAG_FUA) &&
+ !(qc->flags & ATA_QCFLAG_HAS_CDL) &&
+ lba_28_ok(block, n_block)) {
/* use LBA28 */
tf->device |= (block >> 24) & 0xf;
} else if (lba_48_ok(block, n_block)) {
@@ -740,11 +791,12 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
- } else
+ } else {
/* request too large even for LBA48 */
return -ERANGE;
+ }
- if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
+ if (unlikely(!ata_set_rwcmd_protocol(dev, tf)))
return -EINVAL;
tf->nsect = n_block & 0xff;
@@ -762,7 +814,7 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
if (!lba_28_ok(block, n_block))
return -ERANGE;
- if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
+ if (unlikely(!ata_set_rwcmd_protocol(dev, tf)))
return -EINVAL;
/* Convert LBA to CHS */
@@ -1187,7 +1239,7 @@ static int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors)
*max_sectors = ata_tf_to_lba48(&tf) + 1;
else
*max_sectors = ata_tf_to_lba(&tf) + 1;
- if (dev->horkage & ATA_HORKAGE_HPA_SIZE)
+ if (dev->quirks & ATA_QUIRK_HPA_SIZE)
(*max_sectors)--;
return 0;
}
@@ -1272,7 +1324,7 @@ static int ata_hpa_resize(struct ata_device *dev)
/* do we need to do it? */
if ((dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) ||
!ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) ||
- (dev->horkage & ATA_HORKAGE_BROKEN_HPA))
+ (dev->quirks & ATA_QUIRK_BROKEN_HPA))
return 0;
/* read native max address */
@@ -1284,7 +1336,7 @@ static int ata_hpa_resize(struct ata_device *dev)
if (rc == -EACCES || !unlock_hpa) {
ata_dev_warn(dev,
"HPA support seems broken, skipping HPA handling\n");
- dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
+ dev->quirks |= ATA_QUIRK_BROKEN_HPA;
/* we can continue if device aborted the command */
if (rc == -EACCES)
@@ -1321,7 +1373,7 @@ static int ata_hpa_resize(struct ata_device *dev)
"device aborted resize (%llu -> %llu), skipping HPA handling\n",
(unsigned long long)sectors,
(unsigned long long)native_sectors);
- dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
+ dev->quirks |= ATA_QUIRK_BROKEN_HPA;
return 0;
} else if (rc)
return rc;
@@ -1446,19 +1498,19 @@ static void ata_qc_complete_internal(struct ata_queued_cmd *qc)
}
/**
- * ata_exec_internal_sg - execute libata internal command
+ * ata_exec_internal - execute libata internal command
* @dev: Device to which the command is sent
* @tf: Taskfile registers for the command and the result
* @cdb: CDB for packet command
* @dma_dir: Data transfer direction of the command
- * @sgl: sg list for the data buffer of the command
- * @n_elem: Number of sg entries
+ * @buf: Data buffer of the command
+ * @buflen: Length of data buffer
* @timeout: Timeout in msecs (0 for default)
*
- * Executes libata internal command with timeout. @tf contains
- * command on entry and result on return. Timeout and error
- * conditions are reported via return value. No recovery action
- * is taken after a command times out. It's caller's duty to
+ * Executes libata internal command with timeout. @tf contains
+ * the command on entry and the result on return. Timeout and error
+ * conditions are reported via the return value. No recovery action
+ * is taken after a command times out. It is the caller's duty to
* clean up after timeout.
*
* LOCKING:
@@ -1467,34 +1519,38 @@ static void ata_qc_complete_internal(struct ata_queued_cmd *qc)
* RETURNS:
* Zero on success, AC_ERR_* mask on failure
*/
-static unsigned ata_exec_internal_sg(struct ata_device *dev,
- struct ata_taskfile *tf, const u8 *cdb,
- int dma_dir, struct scatterlist *sgl,
- unsigned int n_elem, unsigned int timeout)
+unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf,
+ const u8 *cdb, enum dma_data_direction dma_dir,
+ void *buf, unsigned int buflen,
+ unsigned int timeout)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
u8 command = tf->command;
- int auto_timeout = 0;
struct ata_queued_cmd *qc;
+ struct scatterlist sgl;
unsigned int preempted_tag;
u32 preempted_sactive;
u64 preempted_qc_active;
int preempted_nr_active_links;
+ bool auto_timeout = false;
DECLARE_COMPLETION_ONSTACK(wait);
unsigned long flags;
unsigned int err_mask;
int rc;
+ if (WARN_ON(dma_dir != DMA_NONE && !buf))
+ return AC_ERR_INVALID;
+
spin_lock_irqsave(ap->lock, flags);
- /* no internal command while frozen */
- if (ap->pflags & ATA_PFLAG_FROZEN) {
+ /* No internal command while frozen */
+ if (ata_port_is_frozen(ap)) {
spin_unlock_irqrestore(ap->lock, flags);
return AC_ERR_SYSTEM;
}
- /* initialize internal qc */
+ /* Initialize internal qc */
qc = __ata_qc_from_tag(ap, ATA_TAG_INTERNAL);
qc->tag = ATA_TAG_INTERNAL;
@@ -1513,12 +1569,12 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
ap->qc_active = 0;
ap->nr_active_links = 0;
- /* prepare & issue qc */
+ /* Prepare and issue qc */
qc->tf = *tf;
if (cdb)
memcpy(qc->cdb, cdb, ATAPI_CDB_LEN);
- /* some SATA bridges need us to indicate data xfer direction */
+ /* Some SATA bridges need us to indicate data xfer direction */
if (tf->protocol == ATAPI_PROT_DMA && (dev->flags & ATA_DFLAG_DMADIR) &&
dma_dir == DMA_FROM_DEVICE)
qc->tf.feature |= ATAPI_DMADIR;
@@ -1526,13 +1582,8 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
qc->flags |= ATA_QCFLAG_RESULT_TF;
qc->dma_dir = dma_dir;
if (dma_dir != DMA_NONE) {
- unsigned int i, buflen = 0;
- struct scatterlist *sg;
-
- for_each_sg(sgl, sg, n_elem, i)
- buflen += sg->length;
-
- ata_sg_init(qc, sgl, n_elem);
+ sg_init_one(&sgl, buf, buflen);
+ ata_sg_init(qc, &sgl, 1);
qc->nbytes = buflen;
}
@@ -1544,53 +1595,43 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
spin_unlock_irqrestore(ap->lock, flags);
if (!timeout) {
- if (ata_probe_timeout)
+ if (ata_probe_timeout) {
timeout = ata_probe_timeout * 1000;
- else {
+ } else {
timeout = ata_internal_cmd_timeout(dev, command);
- auto_timeout = 1;
+ auto_timeout = true;
}
}
- if (ap->ops->error_handler)
- ata_eh_release(ap);
+ ata_eh_release(ap);
rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout));
- if (ap->ops->error_handler)
- ata_eh_acquire(ap);
+ ata_eh_acquire(ap);
ata_sff_flush_pio_task(ap);
if (!rc) {
- spin_lock_irqsave(ap->lock, flags);
-
- /* We're racing with irq here. If we lose, the
- * following test prevents us from completing the qc
- * twice. If we win, the port is frozen and will be
- * cleaned up by ->post_internal_cmd().
+ /*
+ * We are racing with irq here. If we lose, the following test
+ * prevents us from completing the qc twice. If we win, the port
+ * is frozen and will be cleaned up by ->post_internal_cmd().
*/
+ spin_lock_irqsave(ap->lock, flags);
if (qc->flags & ATA_QCFLAG_ACTIVE) {
qc->err_mask |= AC_ERR_TIMEOUT;
-
- if (ap->ops->error_handler)
- ata_port_freeze(ap);
- else
- ata_qc_complete(qc);
-
- ata_dev_warn(dev, "qc timeout (cmd 0x%x)\n",
- command);
+ ata_port_freeze(ap);
+ ata_dev_warn(dev, "qc timeout after %u msecs (cmd 0x%x)\n",
+ timeout, command);
}
-
spin_unlock_irqrestore(ap->lock, flags);
}
- /* do post_internal_cmd */
if (ap->ops->post_internal_cmd)
ap->ops->post_internal_cmd(qc);
- /* perform minimal error analysis */
- if (qc->flags & ATA_QCFLAG_FAILED) {
+ /* Perform minimal error analysis */
+ if (qc->flags & ATA_QCFLAG_EH) {
if (qc->result_tf.status & (ATA_ERR | ATA_DF))
qc->err_mask |= AC_ERR_DEV;
@@ -1603,7 +1644,7 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
qc->result_tf.status |= ATA_SENSE;
}
- /* finish up */
+ /* Finish up */
spin_lock_irqsave(ap->lock, flags);
*tf = qc->result_tf;
@@ -1624,44 +1665,6 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
}
/**
- * ata_exec_internal - execute libata internal command
- * @dev: Device to which the command is sent
- * @tf: Taskfile registers for the command and the result
- * @cdb: CDB for packet command
- * @dma_dir: Data transfer direction of the command
- * @buf: Data buffer of the command
- * @buflen: Length of data buffer
- * @timeout: Timeout in msecs (0 for default)
- *
- * Wrapper around ata_exec_internal_sg() which takes simple
- * buffer instead of sg list.
- *
- * LOCKING:
- * None. Should be called with kernel context, might sleep.
- *
- * RETURNS:
- * Zero on success, AC_ERR_* mask on failure
- */
-unsigned ata_exec_internal(struct ata_device *dev,
- struct ata_taskfile *tf, const u8 *cdb,
- int dma_dir, void *buf, unsigned int buflen,
- unsigned int timeout)
-{
- struct scatterlist *psg = NULL, sg;
- unsigned int n_elem = 0;
-
- if (dma_dir != DMA_NONE) {
- WARN_ON(!buf);
- sg_init_one(&sg, buf, buflen);
- psg = &sg;
- n_elem++;
- }
-
- return ata_exec_internal_sg(dev, tf, cdb, dma_dir, psg, n_elem,
- timeout);
-}
-
-/**
* ata_pio_need_iordy - check if iordy needed
* @adev: ATA device
*
@@ -1850,7 +1853,7 @@ retry:
goto err_out;
}
- if (dev->horkage & ATA_HORKAGE_DUMP_ID) {
+ if (dev->quirks & ATA_QUIRK_DUMP_ID) {
ata_dev_info(dev, "dumping IDENTIFY data, "
"class=%d may_fallback=%d tried_spinup=%d\n",
class, may_fallback, tried_spinup);
@@ -1943,6 +1946,146 @@ retry:
return rc;
}
+bool ata_dev_power_init_tf(struct ata_device *dev, struct ata_taskfile *tf,
+ bool set_active)
+{
+ /* Only applies to ATA and ZAC devices */
+ if (dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC)
+ return false;
+
+ ata_tf_init(dev, tf);
+ tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf->protocol = ATA_PROT_NODATA;
+
+ if (set_active) {
+ /* VERIFY for 1 sector at lba=0 */
+ tf->command = ATA_CMD_VERIFY;
+ tf->nsect = 1;
+ if (dev->flags & ATA_DFLAG_LBA) {
+ tf->flags |= ATA_TFLAG_LBA;
+ tf->device |= ATA_LBA;
+ } else {
+ /* CHS */
+ tf->lbal = 0x1; /* sect */
+ }
+ } else {
+ tf->command = ATA_CMD_STANDBYNOW1;
+ }
+
+ return true;
+}
+
+static bool ata_dev_power_is_active(struct ata_device *dev)
+{
+ struct ata_taskfile tf;
+ unsigned int err_mask;
+
+ ata_tf_init(dev, &tf);
+ tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf.protocol = ATA_PROT_NODATA;
+ tf.command = ATA_CMD_CHK_POWER;
+
+ err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+ if (err_mask) {
+ ata_dev_err(dev, "Check power mode failed (err_mask=0x%x)\n",
+ err_mask);
+ /*
+ * Assume we are in standby mode so that we always force a
+ * spinup in ata_dev_power_set_active().
+ */
+ return false;
+ }
+
+ ata_dev_dbg(dev, "Power mode: 0x%02x\n", tf.nsect);
+
+ /* Active or idle */
+ return tf.nsect == 0xff;
+}
+
+/**
+ * ata_dev_power_set_standby - Set a device power mode to standby
+ * @dev: target device
+ *
+ * Issue a STANDBY IMMEDIATE command to set a device power mode to standby.
+ * For an HDD device, this spins down the disks.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_dev_power_set_standby(struct ata_device *dev)
+{
+ unsigned long ap_flags = dev->link->ap->flags;
+ struct ata_taskfile tf;
+ unsigned int err_mask;
+
+ /* If the device is already sleeping or in standby, do nothing. */
+ if ((dev->flags & ATA_DFLAG_SLEEPING) ||
+ !ata_dev_power_is_active(dev))
+ return;
+
+ /*
+ * Some odd clown BIOSes issue spindown on power off (ACPI S4 or S5)
+ * causing some drives to spin up and down again. For these, do nothing
+ * if we are being called on shutdown.
+ */
+ if ((ap_flags & ATA_FLAG_NO_POWEROFF_SPINDOWN) &&
+ system_state == SYSTEM_POWER_OFF)
+ return;
+
+ if ((ap_flags & ATA_FLAG_NO_HIBERNATE_SPINDOWN) &&
+ system_entering_hibernation())
+ return;
+
+ /* Issue STANDBY IMMEDIATE command only if supported by the device */
+ if (!ata_dev_power_init_tf(dev, &tf, false))
+ return;
+
+ ata_dev_notice(dev, "Entering standby power mode\n");
+
+ err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+ if (err_mask)
+ ata_dev_err(dev, "STANDBY IMMEDIATE failed (err_mask=0x%x)\n",
+ err_mask);
+}
+
+/**
+ * ata_dev_power_set_active - Set a device power mode to active
+ * @dev: target device
+ *
+ * Issue a VERIFY command to enter to ensure that the device is in the
+ * active power mode. For a spun-down HDD (standby or idle power mode),
+ * the VERIFY command will complete after the disk spins up.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_dev_power_set_active(struct ata_device *dev)
+{
+ struct ata_taskfile tf;
+ unsigned int err_mask;
+
+ /*
+ * Issue READ VERIFY SECTORS command for 1 sector at lba=0 only
+ * if supported by the device.
+ */
+ if (!ata_dev_power_init_tf(dev, &tf, true))
+ return;
+
+ /*
+ * Check the device power state & condition and force a spinup with
+ * VERIFY command only if the drive is not already ACTIVE or IDLE.
+ */
+ if (ata_dev_power_is_active(dev))
+ return;
+
+ ata_dev_notice(dev, "Entering active power mode\n");
+
+ err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+ if (err_mask)
+ ata_dev_err(dev, "VERIFY failed (err_mask=0x%x)\n",
+ err_mask);
+}
+
/**
* ata_read_log_page - read a specific log page
* @dev: target device
@@ -1979,7 +2122,7 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
retry:
ata_tf_init(dev, &tf);
if (ata_dma_enabled(dev) && ata_id_has_read_log_dma_ext(dev->id) &&
- !(dev->horkage & ATA_HORKAGE_NO_DMA_LOG)) {
+ !(dev->quirks & ATA_QUIRK_NO_DMA_LOG)) {
tf.command = ATA_CMD_READ_LOG_DMA_EXT;
tf.protocol = ATA_PROT_DMA;
dma = true;
@@ -1999,8 +2142,9 @@ retry:
if (err_mask) {
if (dma) {
- dev->horkage |= ATA_HORKAGE_NO_DMA_LOG;
- goto retry;
+ dev->quirks |= ATA_QUIRK_NO_DMA_LOG;
+ if (!ata_port_is_frozen(dev->link->ap))
+ goto retry;
}
ata_dev_err(dev,
"Read log 0x%02x page 0x%02x failed, Emask 0x%x\n",
@@ -2010,24 +2154,50 @@ retry:
return err_mask;
}
-static int ata_log_supported(struct ata_device *dev, u8 log)
+static inline void ata_clear_log_directory(struct ata_device *dev)
{
- struct ata_port *ap = dev->link->ap;
+ memset(dev->gp_log_dir, 0, ATA_SECT_SIZE);
+}
+
+static int ata_read_log_directory(struct ata_device *dev)
+{
+ u16 version;
+
+ /* If the log page is already cached, do nothing. */
+ version = get_unaligned_le16(&dev->gp_log_dir[0]);
+ if (version == 0x0001)
+ return 0;
+
+ if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->gp_log_dir, 1)) {
+ ata_clear_log_directory(dev);
+ return -EIO;
+ }
- if (dev->horkage & ATA_HORKAGE_NO_LOG_DIR)
+ version = get_unaligned_le16(&dev->gp_log_dir[0]);
+ if (version != 0x0001)
+ ata_dev_warn_once(dev,
+ "Invalid log directory version 0x%04x\n",
+ version);
+
+ return 0;
+}
+
+static int ata_log_supported(struct ata_device *dev, u8 log)
+{
+ if (dev->quirks & ATA_QUIRK_NO_LOG_DIR)
return 0;
- if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, ap->sector_buf, 1))
+ if (ata_read_log_directory(dev))
return 0;
- return get_unaligned_le16(&ap->sector_buf[log * 2]);
+
+ return get_unaligned_le16(&dev->gp_log_dir[log * 2]);
}
static bool ata_identify_page_supported(struct ata_device *dev, u8 page)
{
- struct ata_port *ap = dev->link->ap;
unsigned int err, i;
- if (dev->horkage & ATA_HORKAGE_NO_ID_DEV_LOG)
+ if (dev->quirks & ATA_QUIRK_NO_ID_DEV_LOG)
return false;
if (!ata_log_supported(dev, ATA_LOG_IDENTIFY_DEVICE)) {
@@ -2039,7 +2209,7 @@ static bool ata_identify_page_supported(struct ata_device *dev, u8 page)
if (ata_id_major_version(dev->id) >= 10)
ata_dev_warn(dev,
"ATA Identify Device Log not supported\n");
- dev->horkage |= ATA_HORKAGE_NO_ID_DEV_LOG;
+ dev->quirks |= ATA_QUIRK_NO_ID_DEV_LOG;
return false;
}
@@ -2047,20 +2217,20 @@ static bool ata_identify_page_supported(struct ata_device *dev, u8 page)
* Read IDENTIFY DEVICE data log, page 0, to figure out if the page is
* supported.
*/
- err = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE, 0, ap->sector_buf,
- 1);
+ err = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE, 0,
+ dev->sector_buf, 1);
if (err)
return false;
- for (i = 0; i < ap->sector_buf[8]; i++) {
- if (ap->sector_buf[9 + i] == page)
+ for (i = 0; i < dev->sector_buf[8]; i++) {
+ if (dev->sector_buf[9 + i] == page)
return true;
}
return false;
}
-static int ata_do_link_spd_horkage(struct ata_device *dev)
+static int ata_do_link_spd_quirk(struct ata_device *dev)
{
struct ata_link *plink = ata_dev_phys_link(dev);
u32 target, target_limit;
@@ -2068,7 +2238,7 @@ static int ata_do_link_spd_horkage(struct ata_device *dev)
if (!sata_scr_valid(plink))
return 0;
- if (dev->horkage & ATA_HORKAGE_1_5_GBPS)
+ if (dev->quirks & ATA_QUIRK_1_5_GBPS)
target = 1;
else
return 0;
@@ -2086,26 +2256,25 @@ static int ata_do_link_spd_horkage(struct ata_device *dev)
* guaranteed by setting sata_spd_limit to target_limit above.
*/
if (plink->sata_spd > target) {
- ata_dev_info(dev, "applying link speed limit horkage to %s\n",
+ ata_dev_info(dev, "applying link speed limit quirk to %s\n",
sata_spd_string(target));
return -EAGAIN;
}
return 0;
}
-static inline u8 ata_dev_knobble(struct ata_device *dev)
+static inline bool ata_dev_knobble(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
- if (ata_dev_blacklisted(dev) & ATA_HORKAGE_BRIDGE_OK)
- return 0;
+ if (ata_dev_quirks(dev) & ATA_QUIRK_BRIDGE_OK)
+ return false;
return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id)));
}
static void ata_dev_config_ncq_send_recv(struct ata_device *dev)
{
- struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
if (!ata_log_supported(dev, ATA_LOG_NCQ_SEND_RECV)) {
@@ -2113,14 +2282,14 @@ static void ata_dev_config_ncq_send_recv(struct ata_device *dev)
return;
}
err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV,
- 0, ap->sector_buf, 1);
+ 0, dev->sector_buf, 1);
if (!err_mask) {
u8 *cmds = dev->ncq_send_recv_cmds;
dev->flags |= ATA_DFLAG_NCQ_SEND_RECV;
- memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE);
+ memcpy(cmds, dev->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE);
- if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) {
+ if (dev->quirks & ATA_QUIRK_NO_NCQ_TRIM) {
ata_dev_dbg(dev, "disabling queued TRIM support\n");
cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &=
~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM;
@@ -2130,26 +2299,22 @@ static void ata_dev_config_ncq_send_recv(struct ata_device *dev)
static void ata_dev_config_ncq_non_data(struct ata_device *dev)
{
- struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
if (!ata_log_supported(dev, ATA_LOG_NCQ_NON_DATA)) {
ata_dev_warn(dev,
- "NCQ Send/Recv Log not supported\n");
+ "NCQ Non-Data Log not supported\n");
return;
}
err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_NON_DATA,
- 0, ap->sector_buf, 1);
- if (!err_mask) {
- u8 *cmds = dev->ncq_non_data_cmds;
-
- memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_NON_DATA_SIZE);
- }
+ 0, dev->sector_buf, 1);
+ if (!err_mask)
+ memcpy(dev->ncq_non_data_cmds, dev->sector_buf,
+ ATA_LOG_NCQ_NON_DATA_SIZE);
}
static void ata_dev_config_ncq_prio(struct ata_device *dev)
{
- struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
if (!ata_identify_page_supported(dev, ATA_LOG_SATA_SETTINGS))
@@ -2158,12 +2323,11 @@ static void ata_dev_config_ncq_prio(struct ata_device *dev)
err_mask = ata_read_log_page(dev,
ATA_LOG_IDENTIFY_DEVICE,
ATA_LOG_SATA_SETTINGS,
- ap->sector_buf,
- 1);
+ dev->sector_buf, 1);
if (err_mask)
goto not_supported;
- if (!(ap->sector_buf[ATA_LOG_NCQ_PRIO_OFFSET] & BIT(3)))
+ if (!(dev->sector_buf[ATA_LOG_NCQ_PRIO_OFFSET] & BIT(3)))
goto not_supported;
dev->flags |= ATA_DFLAG_NCQ_PRIO;
@@ -2171,7 +2335,7 @@ static void ata_dev_config_ncq_prio(struct ata_device *dev)
return;
not_supported:
- dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE;
+ dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLED;
dev->flags &= ~ATA_DFLAG_NCQ_PRIO;
}
@@ -2208,12 +2372,12 @@ static int ata_dev_config_ncq(struct ata_device *dev,
}
if (!IS_ENABLED(CONFIG_SATA_HOST))
return 0;
- if (dev->horkage & ATA_HORKAGE_NONCQ) {
+ if (dev->quirks & ATA_QUIRK_NONCQ) {
snprintf(desc, desc_sz, "NCQ (not used)");
return 0;
}
- if (dev->horkage & ATA_HORKAGE_NO_NCQ_ON_ATI &&
+ if (dev->quirks & ATA_QUIRK_NO_NCQ_ON_ATI &&
ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI)) {
snprintf(desc, desc_sz, "NCQ (not used)");
return 0;
@@ -2224,7 +2388,7 @@ static int ata_dev_config_ncq(struct ata_device *dev,
dev->flags |= ATA_DFLAG_NCQ;
}
- if (!(dev->horkage & ATA_HORKAGE_BROKEN_FPDMA_AA) &&
+ if (!(dev->quirks & ATA_QUIRK_BROKEN_FPDMA_AA) &&
(ap->flags & ATA_FLAG_FPDMA_AA) &&
ata_id_has_fpdma_aa(dev->id)) {
err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE,
@@ -2234,7 +2398,7 @@ static int ata_dev_config_ncq(struct ata_device *dev,
"failed to enable AA (error_mask=0x%x)\n",
err_mask);
if (err_mask != AC_ERR_DEV) {
- dev->horkage |= ATA_HORKAGE_BROKEN_FPDMA_AA;
+ dev->quirks |= ATA_QUIRK_BROKEN_FPDMA_AA;
return -EIO;
}
} else
@@ -2279,26 +2443,14 @@ static void ata_dev_config_sense_reporting(struct ata_device *dev)
static void ata_dev_config_zac(struct ata_device *dev)
{
- struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
- u8 *identify_buf = ap->sector_buf;
+ u8 *identify_buf = dev->sector_buf;
dev->zac_zones_optimal_open = U32_MAX;
dev->zac_zones_optimal_nonseq = U32_MAX;
dev->zac_zones_max_open = U32_MAX;
- /*
- * Always set the 'ZAC' flag for Host-managed devices.
- */
- if (dev->class == ATA_DEV_ZAC)
- dev->flags |= ATA_DFLAG_ZAC;
- else if (ata_id_zoned_cap(dev->id) == 0x01)
- /*
- * Check for host-aware devices.
- */
- dev->flags |= ATA_DFLAG_ZAC;
-
- if (!(dev->flags & ATA_DFLAG_ZAC))
+ if (!ata_dev_is_zac(dev))
return;
if (!ata_identify_page_supported(dev, ATA_LOG_ZONED_INFORMATION)) {
@@ -2333,7 +2485,6 @@ static void ata_dev_config_zac(struct ata_device *dev)
static void ata_dev_config_trusted(struct ata_device *dev)
{
- struct ata_port *ap = dev->link->ap;
u64 trusted_cap;
unsigned int err;
@@ -2347,11 +2498,11 @@ static void ata_dev_config_trusted(struct ata_device *dev)
}
err = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE, ATA_LOG_SECURITY,
- ap->sector_buf, 1);
+ dev->sector_buf, 1);
if (err)
return;
- trusted_cap = get_unaligned_le64(&ap->sector_buf[40]);
+ trusted_cap = get_unaligned_le64(&dev->sector_buf[40]);
if (!(trusted_cap & (1ULL << 63))) {
ata_dev_dbg(dev,
"Trusted Computing capability qword not valid!\n");
@@ -2362,11 +2513,156 @@ static void ata_dev_config_trusted(struct ata_device *dev)
dev->flags |= ATA_DFLAG_TRUSTED;
}
+static void ata_dev_cleanup_cdl_resources(struct ata_device *dev)
+{
+ kfree(dev->cdl);
+ dev->cdl = NULL;
+}
+
+static int ata_dev_init_cdl_resources(struct ata_device *dev)
+{
+ struct ata_cdl *cdl = dev->cdl;
+ unsigned int err_mask;
+
+ if (!cdl) {
+ cdl = kzalloc(sizeof(*cdl), GFP_KERNEL);
+ if (!cdl)
+ return -ENOMEM;
+ dev->cdl = cdl;
+ }
+
+ err_mask = ata_read_log_page(dev, ATA_LOG_CDL, 0, cdl->desc_log_buf,
+ ATA_LOG_CDL_SIZE / ATA_SECT_SIZE);
+ if (err_mask) {
+ ata_dev_warn(dev, "Read Command Duration Limits log failed\n");
+ ata_dev_cleanup_cdl_resources(dev);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ata_dev_config_cdl(struct ata_device *dev)
+{
+ unsigned int err_mask;
+ bool cdl_enabled;
+ u64 val;
+ int ret;
+
+ if (ata_id_major_version(dev->id) < 11)
+ goto not_supported;
+
+ if (!ata_log_supported(dev, ATA_LOG_IDENTIFY_DEVICE) ||
+ !ata_identify_page_supported(dev, ATA_LOG_SUPPORTED_CAPABILITIES) ||
+ !ata_identify_page_supported(dev, ATA_LOG_CURRENT_SETTINGS))
+ goto not_supported;
+
+ err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
+ ATA_LOG_SUPPORTED_CAPABILITIES,
+ dev->sector_buf, 1);
+ if (err_mask)
+ goto not_supported;
+
+ /* Check Command Duration Limit Supported bits */
+ val = get_unaligned_le64(&dev->sector_buf[168]);
+ if (!(val & BIT_ULL(63)) || !(val & BIT_ULL(0)))
+ goto not_supported;
+
+ /* Warn the user if command duration guideline is not supported */
+ if (!(val & BIT_ULL(1)))
+ ata_dev_warn(dev,
+ "Command duration guideline is not supported\n");
+
+ /*
+ * We must have support for the sense data for successful NCQ commands
+ * log indicated by the successful NCQ command sense data supported bit.
+ */
+ val = get_unaligned_le64(&dev->sector_buf[8]);
+ if (!(val & BIT_ULL(63)) || !(val & BIT_ULL(47))) {
+ ata_dev_warn(dev,
+ "CDL supported but Successful NCQ Command Sense Data is not supported\n");
+ goto not_supported;
+ }
+
+ /* Without NCQ autosense, the successful NCQ commands log is useless. */
+ if (!ata_id_has_ncq_autosense(dev->id)) {
+ ata_dev_warn(dev,
+ "CDL supported but NCQ autosense is not supported\n");
+ goto not_supported;
+ }
+
+ /*
+ * If CDL is marked as enabled, make sure the feature is enabled too.
+ * Conversely, if CDL is disabled, make sure the feature is turned off.
+ */
+ err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
+ ATA_LOG_CURRENT_SETTINGS,
+ dev->sector_buf, 1);
+ if (err_mask)
+ goto not_supported;
+
+ val = get_unaligned_le64(&dev->sector_buf[8]);
+ cdl_enabled = val & BIT_ULL(63) && val & BIT_ULL(21);
+ if (dev->flags & ATA_DFLAG_CDL_ENABLED) {
+ if (!cdl_enabled) {
+ /* Enable CDL on the device */
+ err_mask = ata_dev_set_feature(dev, SETFEATURES_CDL, 1);
+ if (err_mask) {
+ ata_dev_err(dev,
+ "Enable CDL feature failed\n");
+ goto not_supported;
+ }
+ }
+ } else {
+ if (cdl_enabled) {
+ /* Disable CDL on the device */
+ err_mask = ata_dev_set_feature(dev, SETFEATURES_CDL, 0);
+ if (err_mask) {
+ ata_dev_err(dev,
+ "Disable CDL feature failed\n");
+ goto not_supported;
+ }
+ }
+ }
+
+ /*
+ * While CDL itself has to be enabled using sysfs, CDL requires that
+ * sense data for successful NCQ commands is enabled to work properly.
+ * Just like ata_dev_config_sense_reporting(), enable it unconditionally
+ * if supported.
+ */
+ if (!(val & BIT_ULL(63)) || !(val & BIT_ULL(18))) {
+ err_mask = ata_dev_set_feature(dev,
+ SETFEATURE_SENSE_DATA_SUCC_NCQ, 0x1);
+ if (err_mask) {
+ ata_dev_warn(dev,
+ "failed to enable Sense Data for successful NCQ commands, Emask 0x%x\n",
+ err_mask);
+ goto not_supported;
+ }
+ }
+
+ /* CDL is supported: allocate and initialize needed resources. */
+ ret = ata_dev_init_cdl_resources(dev);
+ if (ret) {
+ ata_dev_warn(dev, "Initialize CDL resources failed\n");
+ goto not_supported;
+ }
+
+ dev->flags |= ATA_DFLAG_CDL;
+
+ return;
+
+not_supported:
+ dev->flags &= ~(ATA_DFLAG_CDL | ATA_DFLAG_CDL_ENABLED);
+ ata_dev_cleanup_cdl_resources(dev);
+}
+
static int ata_dev_config_lba(struct ata_device *dev)
{
const u16 *id = dev->id;
const char *lba_desc;
- char ncq_desc[24];
+ char ncq_desc[32];
int ret;
dev->flags |= ATA_DFLAG_LBA;
@@ -2419,9 +2715,31 @@ static void ata_dev_config_chs(struct ata_device *dev)
dev->heads, dev->sectors);
}
+static void ata_dev_config_fua(struct ata_device *dev)
+{
+ /* Ignore FUA support if its use is disabled globally */
+ if (!libata_fua)
+ goto nofua;
+
+ /* Ignore devices without support for WRITE DMA FUA EXT */
+ if (!(dev->flags & ATA_DFLAG_LBA48) || !ata_id_has_fua(dev->id))
+ goto nofua;
+
+ /* Ignore known bad devices and devices that lack NCQ support */
+ if (!ata_ncq_supported(dev) || (dev->quirks & ATA_QUIRK_NO_FUA))
+ goto nofua;
+
+ dev->flags |= ATA_DFLAG_FUA;
+
+ return;
+
+nofua:
+ dev->flags &= ~ATA_DFLAG_FUA;
+}
+
static void ata_dev_config_devslp(struct ata_device *dev)
{
- u8 *sata_setting = dev->link->ap->sector_buf;
+ u8 *sata_setting = dev->sector_buf;
unsigned int err_mask;
int i, j;
@@ -2501,18 +2819,73 @@ out:
kfree(buf);
}
+/*
+ * Configure features related to link power management.
+ */
+static void ata_dev_config_lpm(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->link->ap;
+ unsigned int err_mask;
+
+ if (ap->flags & ATA_FLAG_NO_LPM) {
+ /*
+ * When the port does not support LPM, we cannot support it on
+ * the device either.
+ */
+ dev->quirks |= ATA_QUIRK_NOLPM;
+ } else {
+ /*
+ * Some WD SATA-1 drives have issues with LPM, turn on NOLPM for
+ * them.
+ */
+ if ((dev->quirks & ATA_QUIRK_WD_BROKEN_LPM) &&
+ (dev->id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
+ dev->quirks |= ATA_QUIRK_NOLPM;
+
+ /* ATI specific quirk */
+ if ((dev->quirks & ATA_QUIRK_NO_LPM_ON_ATI) &&
+ ata_dev_check_adapter(dev, PCI_VENDOR_ID_ATI))
+ dev->quirks |= ATA_QUIRK_NOLPM;
+ }
+
+ if (dev->quirks & ATA_QUIRK_NOLPM &&
+ ap->target_lpm_policy != ATA_LPM_MAX_POWER) {
+ ata_dev_warn(dev, "LPM support broken, forcing max_power\n");
+ ap->target_lpm_policy = ATA_LPM_MAX_POWER;
+ }
+
+ /*
+ * Device Initiated Power Management (DIPM) is normally disabled by
+ * default on a device. However, DIPM may have been enabled and that
+ * setting kept even after COMRESET because of the Software Settings
+ * Preservation feature. So if the port does not support DIPM and the
+ * device does, disable DIPM on the device.
+ */
+ if (ap->flags & ATA_FLAG_NO_DIPM && ata_id_has_dipm(dev->id)) {
+ err_mask = ata_dev_set_feature(dev,
+ SETFEATURES_SATA_DISABLE, SATA_DIPM);
+ if (err_mask && err_mask != AC_ERR_DEV)
+ ata_dev_err(dev, "Disable DIPM failed, Emask 0x%x\n",
+ err_mask);
+ }
+}
+
static void ata_dev_print_features(struct ata_device *dev)
{
if (!(dev->flags & ATA_DFLAG_FEATURES_MASK))
return;
ata_dev_info(dev,
- "Features:%s%s%s%s%s%s\n",
+ "Features:%s%s%s%s%s%s%s%s%s%s\n",
+ dev->flags & ATA_DFLAG_FUA ? " FUA" : "",
dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "",
dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "",
dev->flags & ATA_DFLAG_DEVSLP ? " Dev-Sleep" : "",
+ ata_id_has_hipm(dev->id) ? " HIPM" : "",
+ ata_id_has_dipm(dev->id) ? " DIPM" : "",
dev->flags & ATA_DFLAG_NCQ_SEND_RECV ? " NCQ-sndrcv" : "",
dev->flags & ATA_DFLAG_NCQ_PRIO ? " NCQ-prio" : "",
+ dev->flags & ATA_DFLAG_CDL ? " CDL" : "",
dev->cpr_log ? " CPR" : "");
}
@@ -2546,11 +2919,14 @@ int ata_dev_configure(struct ata_device *dev)
return 0;
}
- /* set horkage */
- dev->horkage |= ata_dev_blacklisted(dev);
- ata_force_horkage(dev);
+ /* Clear the general purpose log directory cache. */
+ ata_clear_log_directory(dev);
+
+ /* Set quirks */
+ dev->quirks |= ata_dev_quirks(dev);
+ ata_force_quirks(dev);
- if (dev->horkage & ATA_HORKAGE_DISABLE) {
+ if (dev->quirks & ATA_QUIRK_DISABLE) {
ata_dev_info(dev, "unsupported device, disabling\n");
ata_dev_disable(dev);
return 0;
@@ -2565,23 +2941,10 @@ int ata_dev_configure(struct ata_device *dev)
return 0;
}
- rc = ata_do_link_spd_horkage(dev);
+ rc = ata_do_link_spd_quirk(dev);
if (rc)
return rc;
- /* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */
- if ((dev->horkage & ATA_HORKAGE_WD_BROKEN_LPM) &&
- (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
- dev->horkage |= ATA_HORKAGE_NOLPM;
-
- if (ap->flags & ATA_FLAG_NO_LPM)
- dev->horkage |= ATA_HORKAGE_NOLPM;
-
- if (dev->horkage & ATA_HORKAGE_NOLPM) {
- ata_dev_warn(dev, "LPM support broken, forcing max_power\n");
- dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER;
- }
-
/* let ACPI work its magic */
rc = ata_acpi_on_devcfg(dev);
if (rc)
@@ -2643,6 +3006,16 @@ int ata_dev_configure(struct ata_device *dev)
}
dev->n_sectors = ata_id_n_sectors(id);
+ if (ata_id_is_locked(id)) {
+ /*
+ * If Security locked, set capacity to zero to prevent
+ * any I/O, e.g. partition scanning, as any I/O to a
+ * locked drive will result in user visible errors.
+ */
+ ata_dev_info(dev,
+ "Security locked, setting capacity to zero\n");
+ dev->n_sectors = 0;
+ }
/* get current R/W Multiple count setting */
if ((dev->id[47] >> 8) == 0x80 && (dev->id[59] & 0x100)) {
@@ -2668,11 +3041,14 @@ int ata_dev_configure(struct ata_device *dev)
ata_dev_config_chs(dev);
}
+ ata_dev_config_lpm(dev);
+ ata_dev_config_fua(dev);
ata_dev_config_devslp(dev);
ata_dev_config_sense_reporting(dev);
ata_dev_config_zac(dev);
ata_dev_config_trusted(dev);
ata_dev_config_cpr(dev);
+ ata_dev_config_cdl(dev);
dev->cdb_len = 32;
if (print_info)
@@ -2721,7 +3097,8 @@ int ata_dev_configure(struct ata_device *dev)
cdb_intr_string = ", CDB intr";
}
- if (atapi_dmadir || (dev->horkage & ATA_HORKAGE_ATAPI_DMADIR) || atapi_id_dmadir(dev->id)) {
+ if (atapi_dmadir || (dev->quirks & ATA_QUIRK_ATAPI_DMADIR) ||
+ atapi_id_dmadir(dev->id)) {
dev->flags |= ATA_DFLAG_DMADIR;
dma_dir_string = ", DMADIR";
}
@@ -2758,24 +3135,28 @@ int ata_dev_configure(struct ata_device *dev)
if ((dev->class == ATA_DEV_ATAPI) &&
(atapi_command_packet_set(id) == TYPE_TAPE)) {
dev->max_sectors = ATA_MAX_SECTORS_TAPE;
- dev->horkage |= ATA_HORKAGE_STUCK_ERR;
+ dev->quirks |= ATA_QUIRK_STUCK_ERR;
}
- if (dev->horkage & ATA_HORKAGE_MAX_SEC_128)
+ if (dev->quirks & ATA_QUIRK_MAX_SEC_128)
dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128,
dev->max_sectors);
- if (dev->horkage & ATA_HORKAGE_MAX_SEC_1024)
+ if (dev->quirks & ATA_QUIRK_MAX_SEC_1024)
dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_1024,
dev->max_sectors);
- if (dev->horkage & ATA_HORKAGE_MAX_SEC_LBA48)
+ if (dev->quirks & ATA_QUIRK_MAX_SEC_8191)
+ dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_8191,
+ dev->max_sectors);
+
+ if (dev->quirks & ATA_QUIRK_MAX_SEC_LBA48)
dev->max_sectors = ATA_MAX_SECTORS_LBA48;
if (ap->ops->dev_config)
ap->ops->dev_config(dev);
- if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) {
+ if (dev->quirks & ATA_QUIRK_DIAGNOSTIC) {
/* Let the user know. We don't want to disallow opens for
rescue purposes, or in case the vendor is just a blithering
idiot. Do this after the dev_config call as some controllers
@@ -2790,7 +3171,7 @@ int ata_dev_configure(struct ata_device *dev)
}
}
- if ((dev->horkage & ATA_HORKAGE_FIRMWARE_WARN) && print_info) {
+ if ((dev->quirks & ATA_QUIRK_FIRMWARE_WARN) && print_info) {
ata_dev_warn(dev, "WARNING: device requires firmware update to be fully functional\n");
ata_dev_warn(dev, " contact the vendor or visit http://ata.wiki.kernel.org\n");
}
@@ -2869,144 +3250,6 @@ int ata_cable_sata(struct ata_port *ap)
EXPORT_SYMBOL_GPL(ata_cable_sata);
/**
- * ata_bus_probe - Reset and probe ATA bus
- * @ap: Bus to probe
- *
- * Master ATA bus probing function. Initiates a hardware-dependent
- * bus reset, then attempts to identify any devices found on
- * the bus.
- *
- * LOCKING:
- * PCI/etc. bus probe sem.
- *
- * RETURNS:
- * Zero on success, negative errno otherwise.
- */
-
-int ata_bus_probe(struct ata_port *ap)
-{
- unsigned int classes[ATA_MAX_DEVICES];
- int tries[ATA_MAX_DEVICES];
- int rc;
- struct ata_device *dev;
-
- ata_for_each_dev(dev, &ap->link, ALL)
- tries[dev->devno] = ATA_PROBE_MAX_TRIES;
-
- retry:
- ata_for_each_dev(dev, &ap->link, ALL) {
- /* If we issue an SRST then an ATA drive (not ATAPI)
- * may change configuration and be in PIO0 timing. If
- * we do a hard reset (or are coming from power on)
- * this is true for ATA or ATAPI. Until we've set a
- * suitable controller mode we should not touch the
- * bus as we may be talking too fast.
- */
- dev->pio_mode = XFER_PIO_0;
- dev->dma_mode = 0xff;
-
- /* If the controller has a pio mode setup function
- * then use it to set the chipset to rights. Don't
- * touch the DMA setup as that will be dealt with when
- * configuring devices.
- */
- if (ap->ops->set_piomode)
- ap->ops->set_piomode(ap, dev);
- }
-
- /* reset and determine device classes */
- ap->ops->phy_reset(ap);
-
- ata_for_each_dev(dev, &ap->link, ALL) {
- if (dev->class != ATA_DEV_UNKNOWN)
- classes[dev->devno] = dev->class;
- else
- classes[dev->devno] = ATA_DEV_NONE;
-
- dev->class = ATA_DEV_UNKNOWN;
- }
-
- /* read IDENTIFY page and configure devices. We have to do the identify
- specific sequence bass-ackwards so that PDIAG- is released by
- the slave device */
-
- ata_for_each_dev(dev, &ap->link, ALL_REVERSE) {
- if (tries[dev->devno])
- dev->class = classes[dev->devno];
-
- if (!ata_dev_enabled(dev))
- continue;
-
- rc = ata_dev_read_id(dev, &dev->class, ATA_READID_POSTRESET,
- dev->id);
- if (rc)
- goto fail;
- }
-
- /* Now ask for the cable type as PDIAG- should have been released */
- if (ap->ops->cable_detect)
- ap->cbl = ap->ops->cable_detect(ap);
-
- /* We may have SATA bridge glue hiding here irrespective of
- * the reported cable types and sensed types. When SATA
- * drives indicate we have a bridge, we don't know which end
- * of the link the bridge is which is a problem.
- */
- ata_for_each_dev(dev, &ap->link, ENABLED)
- if (ata_id_is_sata(dev->id))
- ap->cbl = ATA_CBL_SATA;
-
- /* After the identify sequence we can now set up the devices. We do
- this in the normal order so that the user doesn't get confused */
-
- ata_for_each_dev(dev, &ap->link, ENABLED) {
- ap->link.eh_context.i.flags |= ATA_EHI_PRINTINFO;
- rc = ata_dev_configure(dev);
- ap->link.eh_context.i.flags &= ~ATA_EHI_PRINTINFO;
- if (rc)
- goto fail;
- }
-
- /* configure transfer mode */
- rc = ata_set_mode(&ap->link, &dev);
- if (rc)
- goto fail;
-
- ata_for_each_dev(dev, &ap->link, ENABLED)
- return 0;
-
- return -ENODEV;
-
- fail:
- tries[dev->devno]--;
-
- switch (rc) {
- case -EINVAL:
- /* eeek, something went very wrong, give up */
- tries[dev->devno] = 0;
- break;
-
- case -ENODEV:
- /* give it just one more chance */
- tries[dev->devno] = min(tries[dev->devno], 1);
- fallthrough;
- case -EIO:
- if (tries[dev->devno] == 1) {
- /* This is the last chance, better to slow
- * down than lose it.
- */
- sata_down_spd_limit(&ap->link, 0);
- ata_down_xfermask_limit(dev, ATA_DNXFER_PIO);
- }
- }
-
- if (!tries[dev->devno])
- ata_dev_disable(dev);
-
- goto retry;
-}
-
-/**
* sata_print_link_status - Print SATA link status
* @link: SATA link to printk link status about
*
@@ -3021,7 +3264,8 @@ static void sata_print_link_status(struct ata_link *link)
if (sata_scr_read(link, SCR_STATUS, &sstatus))
return;
- sata_scr_read(link, SCR_CONTROL, &scontrol);
+ if (sata_scr_read(link, SCR_CONTROL, &scontrol))
+ return;
if (ata_phys_link_online(link)) {
tmp = (sstatus >> 4) & 0xf;
@@ -3051,86 +3295,6 @@ struct ata_device *ata_dev_pair(struct ata_device *adev)
}
EXPORT_SYMBOL_GPL(ata_dev_pair);
-/**
- * sata_down_spd_limit - adjust SATA spd limit downward
- * @link: Link to adjust SATA spd limit for
- * @spd_limit: Additional limit
- *
- * Adjust SATA spd limit of @link downward. Note that this
- * function only adjusts the limit. The change must be applied
- * using sata_set_spd().
- *
- * If @spd_limit is non-zero, the speed is limited to equal to or
- * lower than @spd_limit if such speed is supported. If
- * @spd_limit is slower than any supported speed, only the lowest
- * supported speed is allowed.
- *
- * LOCKING:
- * Inherited from caller.
- *
- * RETURNS:
- * 0 on success, negative errno on failure
- */
-int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
-{
- u32 sstatus, spd, mask;
- int rc, bit;
-
- if (!sata_scr_valid(link))
- return -EOPNOTSUPP;
-
- /* If SCR can be read, use it to determine the current SPD.
- * If not, use cached value in link->sata_spd.
- */
- rc = sata_scr_read(link, SCR_STATUS, &sstatus);
- if (rc == 0 && ata_sstatus_online(sstatus))
- spd = (sstatus >> 4) & 0xf;
- else
- spd = link->sata_spd;
-
- mask = link->sata_spd_limit;
- if (mask <= 1)
- return -EINVAL;
-
- /* unconditionally mask off the highest bit */
- bit = fls(mask) - 1;
- mask &= ~(1 << bit);
-
- /*
- * Mask off all speeds higher than or equal to the current one. At
- * this point, if current SPD is not available and we previously
- * recorded the link speed from SStatus, the driver has already
- * masked off the highest bit so mask should already be 1 or 0.
- * Otherwise, we should not force 1.5Gbps on a link where we have
- * not previously recorded speed from SStatus. Just return in this
- * case.
- */
- if (spd > 1)
- mask &= (1 << (spd - 1)) - 1;
- else
- return -EINVAL;
-
- /* were we already at the bottom? */
- if (!mask)
- return -EINVAL;
-
- if (spd_limit) {
- if (mask & ((1 << spd_limit) - 1))
- mask &= (1 << spd_limit) - 1;
- else {
- bit = ffs(mask) - 1;
- mask = 1 << bit;
- }
- }
-
- link->sata_spd_limit = mask;
-
- ata_link_warn(link, "limiting SATA link speed to %s\n",
- sata_spd_string(fls(mask)));
-
- return 0;
-}
-
#ifdef CONFIG_ATA_ACPI
/**
* ata_timing_cycle2mode - find xfer mode for the specified cycle duration
@@ -3277,7 +3441,7 @@ static int ata_dev_set_mode(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
struct ata_eh_context *ehc = &dev->link->eh_context;
- const bool nosetxfer = dev->horkage & ATA_HORKAGE_NOSETXFER;
+ const bool nosetxfer = dev->quirks & ATA_QUIRK_NOSETXFER;
const char *dev_err_whine = "";
int ign_dev_err = 0;
unsigned int err_mask = 0;
@@ -3357,7 +3521,7 @@ static int ata_dev_set_mode(struct ata_device *dev)
}
/**
- * ata_do_set_mode - Program timings and issue SET FEATURES - XFER
+ * ata_set_mode - Program timings and issue SET FEATURES - XFER
* @link: link on which timings will be programmed
* @r_failed_dev: out parameter for failed device
*
@@ -3373,7 +3537,7 @@ static int ata_dev_set_mode(struct ata_device *dev)
* 0 on success, negative errno otherwise
*/
-int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
+int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_device *dev;
@@ -3454,7 +3618,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
*r_failed_dev = dev;
return rc;
}
-EXPORT_SYMBOL_GPL(ata_do_set_mode);
+EXPORT_SYMBOL_GPL(ata_set_mode);
/**
* ata_wait_ready - wait for link to become ready
@@ -3587,7 +3751,7 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
- const unsigned long *timing = sata_ehc_deb_timing(ehc);
+ const unsigned int *timing = sata_ehc_deb_timing(ehc);
int rc;
/* if we're about to do hardreset, nothing more to do */
@@ -3613,33 +3777,6 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline)
EXPORT_SYMBOL_GPL(ata_std_prereset);
/**
- * sata_std_hardreset - COMRESET w/o waiting or classification
- * @link: link to reset
- * @class: resulting class of attached device
- * @deadline: deadline jiffies for the operation
- *
- * Standard SATA COMRESET w/o waiting or classification.
- *
- * LOCKING:
- * Kernel thread context (may sleep)
- *
- * RETURNS:
- * 0 if link offline, -EAGAIN if link online, -errno on errors.
- */
-int sata_std_hardreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
-{
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
- bool online;
- int rc;
-
- /* do hardreset */
- rc = sata_link_hardreset(link, timing, deadline, &online, NULL);
- return online ? -EAGAIN : rc;
-}
-EXPORT_SYMBOL_GPL(sata_std_hardreset);
-
-/**
* ata_std_postreset - standard postreset callback
* @link: the target ata_link
* @classes: classes of attached devices
@@ -3730,7 +3867,7 @@ static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class,
int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags)
{
unsigned int class = dev->class;
- u16 *id = (void *)dev->link->ap->sector_buf;
+ u16 *id = (void *)dev->sector_buf;
int rc;
/* read ID data */
@@ -3772,11 +3909,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
return -ENODEV;
/* fail early if !ATA && !ATAPI to avoid issuing [P]IDENTIFY to PMP */
- if (ata_class_enabled(new_class) &&
- new_class != ATA_DEV_ATA &&
- new_class != ATA_DEV_ATAPI &&
- new_class != ATA_DEV_ZAC &&
- new_class != ATA_DEV_SEMB) {
+ if (ata_class_enabled(new_class) && new_class == ATA_DEV_PMP) {
ata_dev_info(dev, "class mismatch %u != %u\n",
dev->class, new_class);
rc = -ENODEV;
@@ -3825,7 +3958,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
*/
if (dev->n_native_sectors == n_native_sectors &&
dev->n_sectors < n_sectors && n_sectors == n_native_sectors &&
- !(dev->horkage & ATA_HORKAGE_BROKEN_HPA)) {
+ !(dev->quirks & ATA_QUIRK_BROKEN_HPA)) {
ata_dev_warn(dev,
"old n_sectors matches native, probably "
"late HPA lock, will try to unlock HPA\n");
@@ -3843,208 +3976,307 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
return rc;
}
-struct ata_blacklist_entry {
+static const char * const ata_quirk_names[] = {
+ [__ATA_QUIRK_DIAGNOSTIC] = "diagnostic",
+ [__ATA_QUIRK_NODMA] = "nodma",
+ [__ATA_QUIRK_NONCQ] = "noncq",
+ [__ATA_QUIRK_MAX_SEC_128] = "maxsec128",
+ [__ATA_QUIRK_BROKEN_HPA] = "brokenhpa",
+ [__ATA_QUIRK_DISABLE] = "disable",
+ [__ATA_QUIRK_HPA_SIZE] = "hpasize",
+ [__ATA_QUIRK_IVB] = "ivb",
+ [__ATA_QUIRK_STUCK_ERR] = "stuckerr",
+ [__ATA_QUIRK_BRIDGE_OK] = "bridgeok",
+ [__ATA_QUIRK_ATAPI_MOD16_DMA] = "atapimod16dma",
+ [__ATA_QUIRK_FIRMWARE_WARN] = "firmwarewarn",
+ [__ATA_QUIRK_1_5_GBPS] = "1.5gbps",
+ [__ATA_QUIRK_NOSETXFER] = "nosetxfer",
+ [__ATA_QUIRK_BROKEN_FPDMA_AA] = "brokenfpdmaaa",
+ [__ATA_QUIRK_DUMP_ID] = "dumpid",
+ [__ATA_QUIRK_MAX_SEC_LBA48] = "maxseclba48",
+ [__ATA_QUIRK_ATAPI_DMADIR] = "atapidmadir",
+ [__ATA_QUIRK_NO_NCQ_TRIM] = "noncqtrim",
+ [__ATA_QUIRK_NOLPM] = "nolpm",
+ [__ATA_QUIRK_WD_BROKEN_LPM] = "wdbrokenlpm",
+ [__ATA_QUIRK_ZERO_AFTER_TRIM] = "zeroaftertrim",
+ [__ATA_QUIRK_NO_DMA_LOG] = "nodmalog",
+ [__ATA_QUIRK_NOTRIM] = "notrim",
+ [__ATA_QUIRK_MAX_SEC_1024] = "maxsec1024",
+ [__ATA_QUIRK_MAX_SEC_8191] = "maxsec8191",
+ [__ATA_QUIRK_MAX_TRIM_128M] = "maxtrim128m",
+ [__ATA_QUIRK_NO_NCQ_ON_ATI] = "noncqonati",
+ [__ATA_QUIRK_NO_LPM_ON_ATI] = "nolpmonati",
+ [__ATA_QUIRK_NO_ID_DEV_LOG] = "noiddevlog",
+ [__ATA_QUIRK_NO_LOG_DIR] = "nologdir",
+ [__ATA_QUIRK_NO_FUA] = "nofua",
+};
+
+static void ata_dev_print_quirks(const struct ata_device *dev,
+ const char *model, const char *rev,
+ unsigned int quirks)
+{
+ struct ata_eh_context *ehc = &dev->link->eh_context;
+ int n = 0, i;
+ size_t sz;
+ char *str;
+
+ if (!ata_dev_print_info(dev) || ehc->i.flags & ATA_EHI_DID_PRINT_QUIRKS)
+ return;
+
+ ehc->i.flags |= ATA_EHI_DID_PRINT_QUIRKS;
+
+ if (!quirks)
+ return;
+
+ sz = 64 + ARRAY_SIZE(ata_quirk_names) * 16;
+ str = kmalloc(sz, GFP_KERNEL);
+ if (!str)
+ return;
+
+ n = snprintf(str, sz, "Model '%s', rev '%s', applying quirks:",
+ model, rev);
+
+ for (i = 0; i < ARRAY_SIZE(ata_quirk_names); i++) {
+ if (quirks & (1U << i))
+ n += snprintf(str + n, sz - n,
+ " %s", ata_quirk_names[i]);
+ }
+
+ ata_dev_warn(dev, "%s\n", str);
+
+ kfree(str);
+}
+
+struct ata_dev_quirks_entry {
const char *model_num;
const char *model_rev;
- unsigned long horkage;
+ unsigned int quirks;
};
-static const struct ata_blacklist_entry ata_device_blacklist [] = {
+static const struct ata_dev_quirks_entry __ata_dev_quirks[] = {
/* Devices with DMA related problems under Linux */
- { "WDC AC11000H", NULL, ATA_HORKAGE_NODMA },
- { "WDC AC22100H", NULL, ATA_HORKAGE_NODMA },
- { "WDC AC32500H", NULL, ATA_HORKAGE_NODMA },
- { "WDC AC33100H", NULL, ATA_HORKAGE_NODMA },
- { "WDC AC31600H", NULL, ATA_HORKAGE_NODMA },
- { "WDC AC32100H", "24.09P07", ATA_HORKAGE_NODMA },
- { "WDC AC23200L", "21.10N21", ATA_HORKAGE_NODMA },
- { "Compaq CRD-8241B", NULL, ATA_HORKAGE_NODMA },
- { "CRD-8400B", NULL, ATA_HORKAGE_NODMA },
- { "CRD-848[02]B", NULL, ATA_HORKAGE_NODMA },
- { "CRD-84", NULL, ATA_HORKAGE_NODMA },
- { "SanDisk SDP3B", NULL, ATA_HORKAGE_NODMA },
- { "SanDisk SDP3B-64", NULL, ATA_HORKAGE_NODMA },
- { "SANYO CD-ROM CRD", NULL, ATA_HORKAGE_NODMA },
- { "HITACHI CDR-8", NULL, ATA_HORKAGE_NODMA },
- { "HITACHI CDR-8[34]35",NULL, ATA_HORKAGE_NODMA },
- { "Toshiba CD-ROM XM-6202B", NULL, ATA_HORKAGE_NODMA },
- { "TOSHIBA CD-ROM XM-1702BC", NULL, ATA_HORKAGE_NODMA },
- { "CD-532E-A", NULL, ATA_HORKAGE_NODMA },
- { "E-IDE CD-ROM CR-840",NULL, ATA_HORKAGE_NODMA },
- { "CD-ROM Drive/F5A", NULL, ATA_HORKAGE_NODMA },
- { "WPI CDD-820", NULL, ATA_HORKAGE_NODMA },
- { "SAMSUNG CD-ROM SC-148C", NULL, ATA_HORKAGE_NODMA },
- { "SAMSUNG CD-ROM SC", NULL, ATA_HORKAGE_NODMA },
- { "ATAPI CD-ROM DRIVE 40X MAXIMUM",NULL,ATA_HORKAGE_NODMA },
- { "_NEC DV5800A", NULL, ATA_HORKAGE_NODMA },
- { "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA },
- { "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA },
- { " 2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA },
- { "VRFDFC22048UCHC-TE*", NULL, ATA_HORKAGE_NODMA },
+ { "WDC AC11000H", NULL, ATA_QUIRK_NODMA },
+ { "WDC AC22100H", NULL, ATA_QUIRK_NODMA },
+ { "WDC AC32500H", NULL, ATA_QUIRK_NODMA },
+ { "WDC AC33100H", NULL, ATA_QUIRK_NODMA },
+ { "WDC AC31600H", NULL, ATA_QUIRK_NODMA },
+ { "WDC AC32100H", "24.09P07", ATA_QUIRK_NODMA },
+ { "WDC AC23200L", "21.10N21", ATA_QUIRK_NODMA },
+ { "Compaq CRD-8241B", NULL, ATA_QUIRK_NODMA },
+ { "CRD-8400B", NULL, ATA_QUIRK_NODMA },
+ { "CRD-848[02]B", NULL, ATA_QUIRK_NODMA },
+ { "CRD-84", NULL, ATA_QUIRK_NODMA },
+ { "SanDisk SDP3B", NULL, ATA_QUIRK_NODMA },
+ { "SanDisk SDP3B-64", NULL, ATA_QUIRK_NODMA },
+ { "SANYO CD-ROM CRD", NULL, ATA_QUIRK_NODMA },
+ { "HITACHI CDR-8", NULL, ATA_QUIRK_NODMA },
+ { "HITACHI CDR-8[34]35", NULL, ATA_QUIRK_NODMA },
+ { "Toshiba CD-ROM XM-6202B", NULL, ATA_QUIRK_NODMA },
+ { "TOSHIBA CD-ROM XM-1702BC", NULL, ATA_QUIRK_NODMA },
+ { "CD-532E-A", NULL, ATA_QUIRK_NODMA },
+ { "E-IDE CD-ROM CR-840", NULL, ATA_QUIRK_NODMA },
+ { "CD-ROM Drive/F5A", NULL, ATA_QUIRK_NODMA },
+ { "WPI CDD-820", NULL, ATA_QUIRK_NODMA },
+ { "SAMSUNG CD-ROM SC-148C", NULL, ATA_QUIRK_NODMA },
+ { "SAMSUNG CD-ROM SC", NULL, ATA_QUIRK_NODMA },
+ { "ATAPI CD-ROM DRIVE 40X MAXIMUM", NULL, ATA_QUIRK_NODMA },
+ { "_NEC DV5800A", NULL, ATA_QUIRK_NODMA },
+ { "SAMSUNG CD-ROM SN-124", "N001", ATA_QUIRK_NODMA },
+ { "Seagate STT20000A", NULL, ATA_QUIRK_NODMA },
+ { " 2GB ATA Flash Disk", "ADMA428M", ATA_QUIRK_NODMA },
+ { "VRFDFC22048UCHC-TE*", NULL, ATA_QUIRK_NODMA },
/* Odd clown on sil3726/4726 PMPs */
- { "Config Disk", NULL, ATA_HORKAGE_DISABLE },
+ { "Config Disk", NULL, ATA_QUIRK_DISABLE },
/* Similar story with ASMedia 1092 */
- { "ASMT109x- Config", NULL, ATA_HORKAGE_DISABLE },
+ { "ASMT109x- Config", NULL, ATA_QUIRK_DISABLE },
/* Weird ATAPI devices */
- { "TORiSAN DVD-ROM DRD-N216", NULL, ATA_HORKAGE_MAX_SEC_128 },
- { "QUANTUM DAT DAT72-000", NULL, ATA_HORKAGE_ATAPI_MOD16_DMA },
- { "Slimtype DVD A DS8A8SH", NULL, ATA_HORKAGE_MAX_SEC_LBA48 },
- { "Slimtype DVD A DS8A9SH", NULL, ATA_HORKAGE_MAX_SEC_LBA48 },
+ { "TORiSAN DVD-ROM DRD-N216", NULL, ATA_QUIRK_MAX_SEC_128 },
+ { "QUANTUM DAT DAT72-000", NULL, ATA_QUIRK_ATAPI_MOD16_DMA },
+ { "Slimtype DVD A DS8A8SH", NULL, ATA_QUIRK_MAX_SEC_LBA48 },
+ { "Slimtype DVD A DS8A9SH", NULL, ATA_QUIRK_MAX_SEC_LBA48 },
/*
* Causes silent data corruption with higher max sects.
* http://lkml.kernel.org/g/x49wpy40ysk.fsf@segfault.boston.devel.redhat.com
*/
- { "ST380013AS", "3.20", ATA_HORKAGE_MAX_SEC_1024 },
+ { "ST380013AS", "3.20", ATA_QUIRK_MAX_SEC_1024 },
/*
* These devices time out with higher max sects.
* https://bugzilla.kernel.org/show_bug.cgi?id=121671
*/
- { "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
- { "LITEON EP1-*", NULL, ATA_HORKAGE_MAX_SEC_1024 },
+ { "LITEON CX1-JB*-HP", NULL, ATA_QUIRK_MAX_SEC_1024 },
+ { "LITEON EP1-*", NULL, ATA_QUIRK_MAX_SEC_1024 },
+
+ /*
+ * These devices time out with higher max sects.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=220693
+ */
+ { "DELLBOSS VD", "MV.R00-0", ATA_QUIRK_MAX_SEC_8191 },
/* Devices we expect to fail diagnostics */
/* Devices where NCQ should be avoided */
/* NCQ is slow */
- { "WDC WD740ADFD-00", NULL, ATA_HORKAGE_NONCQ },
- { "WDC WD740ADFD-00NLR1", NULL, ATA_HORKAGE_NONCQ },
+ { "WDC WD740ADFD-00", NULL, ATA_QUIRK_NONCQ },
+ { "WDC WD740ADFD-00NLR1", NULL, ATA_QUIRK_NONCQ },
/* http://thread.gmane.org/gmane.linux.ide/14907 */
- { "FUJITSU MHT2060BH", NULL, ATA_HORKAGE_NONCQ },
+ { "FUJITSU MHT2060BH", NULL, ATA_QUIRK_NONCQ },
/* NCQ is broken */
- { "Maxtor *", "BANC*", ATA_HORKAGE_NONCQ },
- { "Maxtor 7V300F0", "VA111630", ATA_HORKAGE_NONCQ },
- { "ST380817AS", "3.42", ATA_HORKAGE_NONCQ },
- { "ST3160023AS", "3.42", ATA_HORKAGE_NONCQ },
- { "OCZ CORE_SSD", "02.10104", ATA_HORKAGE_NONCQ },
+ { "Maxtor *", "BANC*", ATA_QUIRK_NONCQ },
+ { "Maxtor 7V300F0", "VA111630", ATA_QUIRK_NONCQ },
+ { "ST380817AS", "3.42", ATA_QUIRK_NONCQ },
+ { "ST3160023AS", "3.42", ATA_QUIRK_NONCQ },
+ { "OCZ CORE_SSD", "02.10104", ATA_QUIRK_NONCQ },
/* Seagate NCQ + FLUSH CACHE firmware bug */
- { "ST31500341AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
- ATA_HORKAGE_FIRMWARE_WARN },
+ { "ST31500341AS", "SD1[5-9]", ATA_QUIRK_NONCQ |
+ ATA_QUIRK_FIRMWARE_WARN },
- { "ST31000333AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
- ATA_HORKAGE_FIRMWARE_WARN },
+ { "ST31000333AS", "SD1[5-9]", ATA_QUIRK_NONCQ |
+ ATA_QUIRK_FIRMWARE_WARN },
- { "ST3640[36]23AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
- ATA_HORKAGE_FIRMWARE_WARN },
+ { "ST3640[36]23AS", "SD1[5-9]", ATA_QUIRK_NONCQ |
+ ATA_QUIRK_FIRMWARE_WARN },
- { "ST3320[68]13AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
- ATA_HORKAGE_FIRMWARE_WARN },
+ { "ST3320[68]13AS", "SD1[5-9]", ATA_QUIRK_NONCQ |
+ ATA_QUIRK_FIRMWARE_WARN },
/* drives which fail FPDMA_AA activation (some may freeze afterwards)
the ST disks also have LPM issues */
- { "ST1000LM024 HN-M101MBB", NULL, ATA_HORKAGE_BROKEN_FPDMA_AA |
- ATA_HORKAGE_NOLPM },
- { "VB0250EAVER", "HPG7", ATA_HORKAGE_BROKEN_FPDMA_AA },
+ { "ST1000LM024 HN-M101MBB", NULL, ATA_QUIRK_BROKEN_FPDMA_AA |
+ ATA_QUIRK_NOLPM },
+ { "VB0250EAVER", "HPG7", ATA_QUIRK_BROKEN_FPDMA_AA },
/* Blacklist entries taken from Silicon Image 3124/3132
Windows driver .inf file - also several Linux problem reports */
- { "HTS541060G9SA00", "MB3OC60D", ATA_HORKAGE_NONCQ },
- { "HTS541080G9SA00", "MB4OC60D", ATA_HORKAGE_NONCQ },
- { "HTS541010G9SA00", "MBZOC60D", ATA_HORKAGE_NONCQ },
+ { "HTS541060G9SA00", "MB3OC60D", ATA_QUIRK_NONCQ },
+ { "HTS541080G9SA00", "MB4OC60D", ATA_QUIRK_NONCQ },
+ { "HTS541010G9SA00", "MBZOC60D", ATA_QUIRK_NONCQ },
/* https://bugzilla.kernel.org/show_bug.cgi?id=15573 */
- { "C300-CTFDDAC128MAG", "0001", ATA_HORKAGE_NONCQ },
+ { "C300-CTFDDAC128MAG", "0001", ATA_QUIRK_NONCQ },
/* Sandisk SD7/8/9s lock up hard on large trims */
- { "SanDisk SD[789]*", NULL, ATA_HORKAGE_MAX_TRIM_128M },
+ { "SanDisk SD[789]*", NULL, ATA_QUIRK_MAX_TRIM_128M },
/* devices which puke on READ_NATIVE_MAX */
- { "HDS724040KLSA80", "KFAOA20N", ATA_HORKAGE_BROKEN_HPA },
- { "WDC WD3200JD-00KLB0", "WD-WCAMR1130137", ATA_HORKAGE_BROKEN_HPA },
- { "WDC WD2500JD-00HBB0", "WD-WMAL71490727", ATA_HORKAGE_BROKEN_HPA },
- { "MAXTOR 6L080L4", "A93.0500", ATA_HORKAGE_BROKEN_HPA },
+ { "HDS724040KLSA80", "KFAOA20N", ATA_QUIRK_BROKEN_HPA },
+ { "WDC WD3200JD-00KLB0", "WD-WCAMR1130137", ATA_QUIRK_BROKEN_HPA },
+ { "WDC WD2500JD-00HBB0", "WD-WMAL71490727", ATA_QUIRK_BROKEN_HPA },
+ { "MAXTOR 6L080L4", "A93.0500", ATA_QUIRK_BROKEN_HPA },
/* this one allows HPA unlocking but fails IOs on the area */
- { "OCZ-VERTEX", "1.30", ATA_HORKAGE_BROKEN_HPA },
+ { "OCZ-VERTEX", "1.30", ATA_QUIRK_BROKEN_HPA },
/* Devices which report 1 sector over size HPA */
- { "ST340823A", NULL, ATA_HORKAGE_HPA_SIZE },
- { "ST320413A", NULL, ATA_HORKAGE_HPA_SIZE },
- { "ST310211A", NULL, ATA_HORKAGE_HPA_SIZE },
+ { "ST340823A", NULL, ATA_QUIRK_HPA_SIZE },
+ { "ST320413A", NULL, ATA_QUIRK_HPA_SIZE },
+ { "ST310211A", NULL, ATA_QUIRK_HPA_SIZE },
/* Devices which get the IVB wrong */
- { "QUANTUM FIREBALLlct10 05", "A03.0900", ATA_HORKAGE_IVB },
- /* Maybe we should just blacklist TSSTcorp... */
- { "TSSTcorp CDDVDW SH-S202[HJN]", "SB0[01]", ATA_HORKAGE_IVB },
+ { "QUANTUM FIREBALLlct10 05", "A03.0900", ATA_QUIRK_IVB },
+ /* Maybe we should just add all TSSTcorp devices... */
+ { "TSSTcorp CDDVDW SH-S202[HJN]", "SB0[01]", ATA_QUIRK_IVB },
/* Devices that do not need bridging limits applied */
- { "MTRON MSP-SATA*", NULL, ATA_HORKAGE_BRIDGE_OK },
- { "BUFFALO HD-QSU2/R5", NULL, ATA_HORKAGE_BRIDGE_OK },
+ { "MTRON MSP-SATA*", NULL, ATA_QUIRK_BRIDGE_OK },
+ { "BUFFALO HD-QSU2/R5", NULL, ATA_QUIRK_BRIDGE_OK },
/* Devices which aren't very happy with higher link speeds */
- { "WD My Book", NULL, ATA_HORKAGE_1_5_GBPS },
- { "Seagate FreeAgent GoFlex", NULL, ATA_HORKAGE_1_5_GBPS },
+ { "WD My Book", NULL, ATA_QUIRK_1_5_GBPS },
+ { "Seagate FreeAgent GoFlex", NULL, ATA_QUIRK_1_5_GBPS },
/*
* Devices which choke on SETXFER. Applies only if both the
* device and controller are SATA.
*/
- { "PIONEER DVD-RW DVRTD08", NULL, ATA_HORKAGE_NOSETXFER },
- { "PIONEER DVD-RW DVRTD08A", NULL, ATA_HORKAGE_NOSETXFER },
- { "PIONEER DVD-RW DVR-215", NULL, ATA_HORKAGE_NOSETXFER },
- { "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER },
- { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
+ { "PIONEER DVD-RW DVRTD08", NULL, ATA_QUIRK_NOSETXFER },
+ { "PIONEER DVD-RW DVRTD08A", NULL, ATA_QUIRK_NOSETXFER },
+ { "PIONEER DVD-RW DVR-215", NULL, ATA_QUIRK_NOSETXFER },
+ { "PIONEER DVD-RW DVR-212D", NULL, ATA_QUIRK_NOSETXFER },
+ { "PIONEER DVD-RW DVR-216D", NULL, ATA_QUIRK_NOSETXFER },
+
+ /* These specific Pioneer models have LPM issues */
+ { "PIONEER BD-RW BDR-207M", NULL, ATA_QUIRK_NOLPM },
+ { "PIONEER BD-RW BDR-205", NULL, ATA_QUIRK_NOLPM },
- /* Crucial BX100 SSD 500GB has broken LPM support */
- { "CT500BX100SSD1", NULL, ATA_HORKAGE_NOLPM },
+ /* Crucial devices with broken LPM support */
+ { "CT*0BX*00SSD1", NULL, ATA_QUIRK_NOLPM },
/* 512GB MX100 with MU01 firmware has both queued TRIM and LPM issues */
- { "Crucial_CT512MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM |
- ATA_HORKAGE_NOLPM },
+ { "Crucial_CT512MX100*", "MU01", ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NOLPM },
/* 512GB MX100 with newer firmware has only LPM issues */
- { "Crucial_CT512MX100*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM |
- ATA_HORKAGE_NOLPM },
+ { "Crucial_CT512MX100*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NOLPM },
/* 480GB+ M500 SSDs have both queued TRIM and LPM issues */
- { "Crucial_CT480M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM |
- ATA_HORKAGE_NOLPM },
- { "Crucial_CT960M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM |
- ATA_HORKAGE_NOLPM },
+ { "Crucial_CT480M500*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NOLPM },
+ { "Crucial_CT960M500*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NOLPM },
+
+ /* AMD Radeon devices with broken LPM support */
+ { "R3SL240G", NULL, ATA_QUIRK_NOLPM },
+
+ /* Apacer models with LPM issues */
+ { "Apacer AS340*", NULL, ATA_QUIRK_NOLPM },
+
+ /* Silicon Motion models with LPM issues */
+ { "MD619HXCLDE3TC", "TCVAID", ATA_QUIRK_NOLPM },
+ { "MD619GXCLDE3TC", "TCV35D", ATA_QUIRK_NOLPM },
/* These specific Samsung models/firmware-revs do not handle LPM well */
- { "SAMSUNG MZMPC128HBFU-000MV", "CXM14M1Q", ATA_HORKAGE_NOLPM },
- { "SAMSUNG SSD PM830 mSATA *", "CXM13D1Q", ATA_HORKAGE_NOLPM },
- { "SAMSUNG MZ7TD256HAFV-000L9", NULL, ATA_HORKAGE_NOLPM },
- { "SAMSUNG MZ7TE512HMHP-000L1", "EXT06L0Q", ATA_HORKAGE_NOLPM },
+ { "SAMSUNG MZMPC128HBFU-000MV", "CXM14M1Q", ATA_QUIRK_NOLPM },
+ { "SAMSUNG SSD PM830 mSATA *", "CXM13D1Q", ATA_QUIRK_NOLPM },
+ { "SAMSUNG MZ7TD256HAFV-000L9", NULL, ATA_QUIRK_NOLPM },
+ { "SAMSUNG MZ7TE512HMHP-000L1", "EXT06L0Q", ATA_QUIRK_NOLPM },
/* devices that don't properly handle queued TRIM commands */
- { "Micron_M500IT_*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Micron_M5[15]0_*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Samsung SSD 840 EVO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_NO_DMA_LOG |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Samsung SSD 840*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Samsung SSD 850*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Samsung SSD 860*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM |
- ATA_HORKAGE_NO_NCQ_ON_ATI },
- { "Samsung SSD 870*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM |
- ATA_HORKAGE_NO_NCQ_ON_ATI },
- { "FCCT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
- ATA_HORKAGE_ZERO_AFTER_TRIM },
+ { "Micron_M500IT_*", "MU01", ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Micron_M500_*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Micron_M5[15]0_*", "MU01", ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Micron_1100_*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*M500*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Crucial_CT*M550*", "MU01", ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Crucial_CT*MX100*", "MU01", ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Samsung SSD 840 EVO*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_NO_DMA_LOG |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Samsung SSD 840*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Samsung SSD 850*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Samsung SSD 860*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NO_NCQ_ON_ATI |
+ ATA_QUIRK_NO_LPM_ON_ATI },
+ { "Samsung SSD 870*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NO_NCQ_ON_ATI |
+ ATA_QUIRK_NO_LPM_ON_ATI },
+ { "SAMSUNG*MZ7LH*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM |
+ ATA_QUIRK_NO_NCQ_ON_ATI |
+ ATA_QUIRK_NO_LPM_ON_ATI },
+ { "FCCT*M500*", NULL, ATA_QUIRK_NO_NCQ_TRIM |
+ ATA_QUIRK_ZERO_AFTER_TRIM },
/* devices that don't properly handle TRIM commands */
- { "SuperSSpeed S238*", NULL, ATA_HORKAGE_NOTRIM },
- { "M88V29*", NULL, ATA_HORKAGE_NOTRIM },
+ { "SuperSSpeed S238*", NULL, ATA_QUIRK_NOTRIM },
+ { "M88V29*", NULL, ATA_QUIRK_NOTRIM },
/*
* As defined, the DRAT (Deterministic Read After Trim) and RZAT
@@ -4064,14 +4296,14 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
*/
{ "INTEL*SSDSC2MH*", NULL, 0 },
- { "Micron*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Crucial*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "SAMSUNG*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "SAMSUNG*MZ7KM*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
- { "ST[1248][0248]0[FH]*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM },
+ { "Micron*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Crucial*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "INTEL*SSD*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "SSD*INTEL*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "Samsung*SSD*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "SAMSUNG*SSD*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "SAMSUNG*MZ7KM*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
+ { "ST[1248][0248]0[FH]*", NULL, ATA_QUIRK_ZERO_AFTER_TRIM },
/*
* Some WD SATA-I drives spin up and down erratically when the link
@@ -4082,56 +4314,66 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
*
* https://bugzilla.kernel.org/show_bug.cgi?id=57211
*/
- { "WDC WD800JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
- { "WDC WD1200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
- { "WDC WD1600JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
- { "WDC WD2000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
- { "WDC WD2500JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
- { "WDC WD3000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
- { "WDC WD3200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
+ { "WDC WD800JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
+ { "WDC WD1200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
+ { "WDC WD1600JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
+ { "WDC WD2000JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
+ { "WDC WD2500JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
+ { "WDC WD3000JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
+ { "WDC WD3200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
/*
* This sata dom device goes on a walkabout when the ATA_LOG_DIRECTORY
* log page is accessed. Ensure we never ask for this log page with
* these devices.
*/
- { "SATADOM-ML 3ME", NULL, ATA_HORKAGE_NO_LOG_DIR },
+ { "SATADOM-ML 3ME", NULL, ATA_QUIRK_NO_LOG_DIR },
+
+ /* Buggy FUA */
+ { "Maxtor", "BANC1G10", ATA_QUIRK_NO_FUA },
+ { "WDC*WD2500J*", NULL, ATA_QUIRK_NO_FUA },
+ { "OCZ-VERTEX*", NULL, ATA_QUIRK_NO_FUA },
+ { "INTEL*SSDSC2CT*", NULL, ATA_QUIRK_NO_FUA },
/* End Marker */
{ }
};
-static unsigned long ata_dev_blacklisted(const struct ata_device *dev)
+static unsigned int ata_dev_quirks(const struct ata_device *dev)
{
unsigned char model_num[ATA_ID_PROD_LEN + 1];
unsigned char model_rev[ATA_ID_FW_REV_LEN + 1];
- const struct ata_blacklist_entry *ad = ata_device_blacklist;
+ const struct ata_dev_quirks_entry *ad = __ata_dev_quirks;
+
+ /* dev->quirks is an unsigned int. */
+ BUILD_BUG_ON(__ATA_QUIRK_MAX > 32);
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
ata_id_c_string(dev->id, model_rev, ATA_ID_FW_REV, sizeof(model_rev));
while (ad->model_num) {
- if (glob_match(ad->model_num, model_num)) {
- if (ad->model_rev == NULL)
- return ad->horkage;
- if (glob_match(ad->model_rev, model_rev))
- return ad->horkage;
+ if (glob_match(ad->model_num, model_num) &&
+ (!ad->model_rev || glob_match(ad->model_rev, model_rev))) {
+ ata_dev_print_quirks(dev, model_num, model_rev,
+ ad->quirks);
+ return ad->quirks;
}
ad++;
}
return 0;
}
-static int ata_dma_blacklisted(const struct ata_device *dev)
+static bool ata_dev_nodma(const struct ata_device *dev)
{
- /* We don't support polling DMA.
- * DMA blacklist those ATAPI devices with CDB-intr (and use PIO)
- * if the LLDD handles only interrupts in the HSM_ST_LAST state.
+ /*
+ * We do not support polling DMA. Deny DMA for those ATAPI devices
+ * with CDB-intr (and use PIO) if the LLDD handles only interrupts in
+ * the HSM_ST_LAST state.
*/
if ((dev->link->ap->flags & ATA_FLAG_PIO_POLLING) &&
(dev->flags & ATA_DFLAG_CDB_INTR))
- return 1;
- return (dev->horkage & ATA_HORKAGE_NODMA) ? 1 : 0;
+ return true;
+ return dev->quirks & ATA_QUIRK_NODMA;
}
/**
@@ -4144,7 +4386,7 @@ static int ata_dma_blacklisted(const struct ata_device *dev)
static int ata_is_40wire(struct ata_device *dev)
{
- if (dev->horkage & ATA_HORKAGE_IVB)
+ if (dev->quirks & ATA_QUIRK_IVB)
return ata_drive_40wire_relaxed(dev->id);
return ata_drive_40wire(dev->id);
}
@@ -4206,8 +4448,7 @@ static int cable_is_40wire(struct ata_port *ap)
*
* Compute supported xfermask of @dev and store it in
* dev->*_mask. This function is responsible for applying all
- * known limits including host controller limits, device
- * blacklist, etc...
+ * known limits including host controller limits, device quirks, etc...
*
* LOCKING:
* None.
@@ -4239,10 +4480,10 @@ static void ata_dev_xfermask(struct ata_device *dev)
xfer_mask &= ~(0x03 << (ATA_SHIFT_MWDMA + 3));
}
- if (ata_dma_blacklisted(dev)) {
+ if (ata_dev_nodma(dev)) {
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
ata_dev_warn(dev,
- "device is on DMA blacklist, disabling DMA\n");
+ "device does not support DMA, disabling DMA\n");
}
if ((host->flags & ATA_HOST_SIMPLEX) &&
@@ -4295,7 +4536,6 @@ static void ata_dev_xfermask(struct ata_device *dev)
static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
{
struct ata_taskfile tf;
- unsigned int err_mask;
/* set up set-features taskfile */
ata_dev_dbg(dev, "set features - xfer mode\n");
@@ -4317,20 +4557,20 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
else /* In the ancient relic department - skip all of this */
return 0;
- /* On some disks, this command causes spin-up, so we need longer timeout */
- err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 15000);
-
- return err_mask;
+ /*
+ * On some disks, this command causes spin-up, so we need longer
+ * timeout.
+ */
+ return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 15000);
}
/**
- * ata_dev_set_feature - Issue SET FEATURES - SATA FEATURES
+ * ata_dev_set_feature - Issue SET FEATURES
* @dev: Device to which command will be sent
- * @enable: Whether to enable or disable the feature
- * @feature: The sector count represents the feature to set
+ * @subcmd: The SET FEATURES subcommand to be sent
+ * @action: The sector count represents a subcommand specific action
*
- * Issue SET FEATURES - SATA FEATURES command to device @dev
- * on port @ap with sector count
+ * Issue SET FEATURES command to device @dev on port @ap with sector count
*
* LOCKING:
* PCI/etc. bus probe sem.
@@ -4338,28 +4578,26 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
* RETURNS:
* 0 on success, AC_ERR_* mask otherwise.
*/
-unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature)
+unsigned int ata_dev_set_feature(struct ata_device *dev, u8 subcmd, u8 action)
{
struct ata_taskfile tf;
- unsigned int err_mask;
unsigned int timeout = 0;
/* set up set-features taskfile */
- ata_dev_dbg(dev, "set features - SATA features\n");
+ ata_dev_dbg(dev, "set features\n");
ata_tf_init(dev, &tf);
tf.command = ATA_CMD_SET_FEATURES;
- tf.feature = enable;
+ tf.feature = subcmd;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.protocol = ATA_PROT_NODATA;
- tf.nsect = feature;
+ tf.nsect = action;
- if (enable == SETFEATURES_SPINUP)
+ if (subcmd == SETFEATURES_SPINUP)
timeout = ata_probe_timeout ?
ata_probe_timeout * 1000 : SETFEATURES_SPINUP_TIMEOUT;
- err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout);
- return err_mask;
+ return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout);
}
EXPORT_SYMBOL_GPL(ata_dev_set_feature);
@@ -4386,7 +4624,7 @@ static unsigned int ata_dev_init_params(struct ata_device *dev,
return AC_ERR_INVALID;
/* set up init dev params taskfile */
- ata_dev_dbg(dev, "init dev params \n");
+ ata_dev_dbg(dev, "init dev params\n");
ata_tf_init(dev, &tf);
tf.command = ATA_CMD_INIT_DEV_PARAMS;
@@ -4426,9 +4664,9 @@ int atapi_check_dma(struct ata_queued_cmd *qc)
/* Don't allow DMA if it isn't multiple of 16 bytes. Quite a
* few ATAPI devices choke on such DMA requests.
*/
- if (!(qc->dev->horkage & ATA_HORKAGE_ATAPI_MOD16_DMA) &&
+ if (!(qc->dev->quirks & ATA_QUIRK_ATAPI_MOD16_DMA) &&
unlikely(qc->nbytes & 15))
- return 1;
+ return -EOPNOTSUPP;
if (ap->ops->check_atapi_dma)
return ap->ops->check_atapi_dma(qc);
@@ -4467,12 +4705,6 @@ int ata_std_qc_defer(struct ata_queued_cmd *qc)
}
EXPORT_SYMBOL_GPL(ata_std_qc_defer);
-enum ata_completion_errors ata_noop_qc_prep(struct ata_queued_cmd *qc)
-{
- return AC_ERR_OK;
-}
-EXPORT_SYMBOL_GPL(ata_noop_qc_prep);
-
/**
* ata_sg_init - Associate command with scatter-gather table.
* @qc: Command to be associated
@@ -4600,8 +4832,9 @@ void __ata_qc_complete(struct ata_queued_cmd *qc)
struct ata_port *ap;
struct ata_link *link;
- WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */
- WARN_ON_ONCE(!(qc->flags & ATA_QCFLAG_ACTIVE));
+ if (WARN_ON_ONCE(!(qc->flags & ATA_QCFLAG_ACTIVE)))
+ return;
+
ap = qc->ap;
link = qc->dev->link;
@@ -4623,9 +4856,10 @@ void __ata_qc_complete(struct ata_queued_cmd *qc)
ap->excl_link == link))
ap->excl_link = NULL;
- /* atapi: mark qc as inactive to prevent the interrupt handler
- * from completing the command twice later, before the error handler
- * is called. (when rc != 0 and atapi request sense is needed)
+ /*
+ * Mark qc as inactive to prevent the port interrupt handler from
+ * completing the command twice later, before the error handler is
+ * called.
*/
qc->flags &= ~ATA_QCFLAG_ACTIVE;
ap->qc_active &= ~(1ULL << qc->tag);
@@ -4638,8 +4872,16 @@ static void fill_result_tf(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
+ /*
+ * rtf may already be filled (e.g. for successful NCQ commands).
+ * If that is the case, we have nothing to do.
+ */
+ if (qc->flags & ATA_QCFLAG_RTF_FILLED)
+ return;
+
qc->result_tf.flags = qc->tf.flags;
ap->ops->qc_fill_rtf(qc);
+ qc->flags |= ATA_QCFLAG_RTF_FILLED;
}
static void ata_verify_xfer(struct ata_queued_cmd *qc)
@@ -4673,96 +4915,100 @@ static void ata_verify_xfer(struct ata_queued_cmd *qc)
void ata_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
+ struct ata_device *dev = qc->dev;
+ struct ata_eh_info *ehi = &dev->link->eh_info;
/* Trigger the LED (if available) */
ledtrig_disk_activity(!!(qc->tf.flags & ATA_TFLAG_WRITE));
- /* XXX: New EH and old EH use different mechanisms to
- * synchronize EH with regular execution path.
- *
- * In new EH, a failed qc is marked with ATA_QCFLAG_FAILED.
- * Normal execution path is responsible for not accessing a
- * failed qc. libata core enforces the rule by returning NULL
- * from ata_qc_from_tag() for failed qcs.
+ /*
+ * In order to synchronize EH with the regular execution path, a qc that
+ * is owned by EH is marked with ATA_QCFLAG_EH.
*
- * Old EH depends on ata_qc_complete() nullifying completion
- * requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does
- * not synchronize with interrupt handler. Only PIO task is
- * taken care of.
+ * The normal execution path is responsible for not accessing a qc owned
+ * by EH. libata core enforces the rule by returning NULL from
+ * ata_qc_from_tag() for qcs owned by EH.
*/
- if (ap->ops->error_handler) {
- struct ata_device *dev = qc->dev;
- struct ata_eh_info *ehi = &dev->link->eh_info;
+ if (unlikely(qc->err_mask))
+ qc->flags |= ATA_QCFLAG_EH;
- if (unlikely(qc->err_mask))
- qc->flags |= ATA_QCFLAG_FAILED;
+ /*
+ * Finish internal commands without any further processing and always
+ * with the result TF filled.
+ */
+ if (unlikely(ata_tag_internal(qc->tag))) {
+ fill_result_tf(qc);
+ trace_ata_qc_complete_internal(qc);
+ __ata_qc_complete(qc);
+ return;
+ }
- /*
- * Finish internal commands without any further processing
- * and always with the result TF filled.
- */
- if (unlikely(ata_tag_internal(qc->tag))) {
- fill_result_tf(qc);
- trace_ata_qc_complete_internal(qc);
- __ata_qc_complete(qc);
- return;
- }
+ /* Non-internal qc has failed. Fill the result TF and summon EH. */
+ if (unlikely(qc->flags & ATA_QCFLAG_EH)) {
+ fill_result_tf(qc);
+ trace_ata_qc_complete_failed(qc);
+ ata_qc_schedule_eh(qc);
+ return;
+ }
- /*
- * Non-internal qc has failed. Fill the result TF and
- * summon EH.
- */
- if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) {
- fill_result_tf(qc);
- trace_ata_qc_complete_failed(qc);
- ata_qc_schedule_eh(qc);
- return;
- }
+ WARN_ON_ONCE(ata_port_is_frozen(ap));
- WARN_ON_ONCE(ap->pflags & ATA_PFLAG_FROZEN);
+ /* read result TF if requested */
+ if (qc->flags & ATA_QCFLAG_RESULT_TF)
+ fill_result_tf(qc);
- /* read result TF if requested */
- if (qc->flags & ATA_QCFLAG_RESULT_TF)
- fill_result_tf(qc);
+ trace_ata_qc_complete_done(qc);
- trace_ata_qc_complete_done(qc);
- /* Some commands need post-processing after successful
- * completion.
+ /*
+ * For CDL commands that completed without an error, check if we have
+ * sense data (ATA_SENSE is set). If we do, then the command may have
+ * been aborted by the device due to a limit timeout using the policy
+ * 0xD. For these commands, invoke EH to get the command sense data.
+ */
+ if (qc->flags & ATA_QCFLAG_HAS_CDL &&
+ qc->result_tf.status & ATA_SENSE) {
+ /*
+ * Tell SCSI EH to not overwrite scmd->result even if this
+ * command is finished with result SAM_STAT_GOOD.
*/
- switch (qc->tf.command) {
- case ATA_CMD_SET_FEATURES:
- if (qc->tf.feature != SETFEATURES_WC_ON &&
- qc->tf.feature != SETFEATURES_WC_OFF &&
- qc->tf.feature != SETFEATURES_RA_ON &&
- qc->tf.feature != SETFEATURES_RA_OFF)
- break;
- fallthrough;
- case ATA_CMD_INIT_DEV_PARAMS: /* CHS translation changed */
- case ATA_CMD_SET_MULTI: /* multi_count changed */
- /* revalidate device */
- ehi->dev_action[dev->devno] |= ATA_EH_REVALIDATE;
- ata_port_schedule_eh(ap);
- break;
+ qc->scsicmd->flags |= SCMD_FORCE_EH_SUCCESS;
+ qc->flags |= ATA_QCFLAG_EH_SUCCESS_CMD;
+ ehi->dev_action[dev->devno] |= ATA_EH_GET_SUCCESS_SENSE;
- case ATA_CMD_SLEEP:
- dev->flags |= ATA_DFLAG_SLEEPING;
- break;
- }
+ /*
+ * set pending so that ata_qc_schedule_eh() does not trigger
+ * fast drain, and freeze the port.
+ */
+ ap->pflags |= ATA_PFLAG_EH_PENDING;
+ ata_qc_schedule_eh(qc);
+ return;
+ }
- if (unlikely(dev->flags & ATA_DFLAG_DUBIOUS_XFER))
- ata_verify_xfer(qc);
+ /* Some commands need post-processing after successful completion. */
+ switch (qc->tf.command) {
+ case ATA_CMD_SET_FEATURES:
+ if (qc->tf.feature != SETFEATURES_WC_ON &&
+ qc->tf.feature != SETFEATURES_WC_OFF &&
+ qc->tf.feature != SETFEATURES_RA_ON &&
+ qc->tf.feature != SETFEATURES_RA_OFF)
+ break;
+ fallthrough;
+ case ATA_CMD_INIT_DEV_PARAMS: /* CHS translation changed */
+ case ATA_CMD_SET_MULTI: /* multi_count changed */
+ /* revalidate device */
+ ehi->dev_action[dev->devno] |= ATA_EH_REVALIDATE;
+ ata_port_schedule_eh(ap);
+ break;
- __ata_qc_complete(qc);
- } else {
- if (qc->flags & ATA_QCFLAG_EH_SCHEDULED)
- return;
+ case ATA_CMD_SLEEP:
+ dev->flags |= ATA_DFLAG_SLEEPING;
+ break;
+ }
- /* read result TF if failed or requested */
- if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF)
- fill_result_tf(qc);
+ if (unlikely(dev->flags & ATA_DFLAG_DUBIOUS_XFER))
+ ata_verify_xfer(qc);
- __ata_qc_complete(qc);
- }
+ __ata_qc_complete(qc);
}
EXPORT_SYMBOL_GPL(ata_qc_complete);
@@ -4808,11 +5054,8 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
struct ata_link *link = qc->dev->link;
u8 prot = qc->tf.protocol;
- /* Make sure only one non-NCQ command is outstanding. The
- * check is skipped for old EH because it reuses active qc to
- * request ATAPI sense.
- */
- WARN_ON_ONCE(ap->ops->error_handler && ata_tag_valid(link->active_tag));
+ /* Make sure only one non-NCQ command is outstanding. */
+ WARN_ON_ONCE(ata_tag_valid(link->active_tag));
if (ata_is_ncq(prot)) {
WARN_ON_ONCE(link->sactive & (1 << qc->hw_tag));
@@ -4850,10 +5093,13 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
return;
}
- trace_ata_qc_prep(qc);
- qc->err_mask |= ap->ops->qc_prep(qc);
- if (unlikely(qc->err_mask))
- goto err;
+ if (ap->ops->qc_prep) {
+ trace_ata_qc_prep(qc);
+ qc->err_mask |= ap->ops->qc_prep(qc);
+ if (unlikely(qc->err_mask))
+ goto err;
+ }
+
trace_ata_qc_issue(qc);
qc->err_mask |= ap->ops->qc_issue(qc);
if (unlikely(qc->err_mask))
@@ -4976,17 +5222,19 @@ static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
struct ata_link *link;
unsigned long flags;
- /* Previous resume operation might still be in
- * progress. Wait for PM_PENDING to clear.
+ spin_lock_irqsave(ap->lock, flags);
+
+ /*
+ * A previous PM operation might still be in progress. Wait for
+ * ATA_PFLAG_PM_PENDING to clear.
*/
if (ap->pflags & ATA_PFLAG_PM_PENDING) {
+ spin_unlock_irqrestore(ap->lock, flags);
ata_port_wait_eh(ap);
- WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+ spin_lock_irqsave(ap->lock, flags);
}
- /* request PM ops to EH */
- spin_lock_irqsave(ap->lock, flags);
-
+ /* Request PM operation to EH */
ap->pm_mesg = mesg;
ap->pflags |= ATA_PFLAG_PM_PENDING;
ata_for_each_link(link, ap, HOST_FIRST) {
@@ -4998,31 +5246,33 @@ static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
spin_unlock_irqrestore(ap->lock, flags);
- if (!async) {
+ if (!async)
ata_port_wait_eh(ap);
- WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
- }
}
-/*
- * On some hardware, device fails to respond after spun down for suspend. As
- * the device won't be used before being resumed, we don't need to touch the
- * device. Ask EH to skip the usual stuff and proceed directly to suspend.
- *
- * http://thread.gmane.org/gmane.linux.ide/46764
- */
-static const unsigned int ata_port_suspend_ehi = ATA_EHI_QUIET
- | ATA_EHI_NO_AUTOPSY
- | ATA_EHI_NO_RECOVERY;
-
-static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg)
+static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg,
+ bool async)
{
- ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, false);
-}
+ /*
+ * We are about to suspend the port, so we do not care about
+ * scsi_rescan_device() calls scheduled by previous resume operations.
+ * The next resume will schedule the rescan again. So cancel any rescan
+ * that is not done yet.
+ */
+ cancel_delayed_work_sync(&ap->scsi_rescan_task);
-static void ata_port_suspend_async(struct ata_port *ap, pm_message_t mesg)
-{
- ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, true);
+ /*
+ * On some hardware, device fails to respond after spun down for
+ * suspend. As the device will not be used until being resumed, we
+ * do not need to touch the device. Ask EH to skip the usual stuff
+ * and proceed directly to suspend.
+ *
+ * http://thread.gmane.org/gmane.linux.ide/46764
+ */
+ ata_port_request_pm(ap, mesg, 0,
+ ATA_EHI_QUIET | ATA_EHI_NO_AUTOPSY |
+ ATA_EHI_NO_RECOVERY,
+ async);
}
static int ata_port_pm_suspend(struct device *dev)
@@ -5032,7 +5282,7 @@ static int ata_port_pm_suspend(struct device *dev)
if (pm_runtime_suspended(dev))
return 0;
- ata_port_suspend(ap, PMSG_SUSPEND);
+ ata_port_suspend(ap, PMSG_SUSPEND, false);
return 0;
}
@@ -5043,35 +5293,29 @@ static int ata_port_pm_freeze(struct device *dev)
if (pm_runtime_suspended(dev))
return 0;
- ata_port_suspend(ap, PMSG_FREEZE);
+ ata_port_suspend(ap, PMSG_FREEZE, false);
return 0;
}
static int ata_port_pm_poweroff(struct device *dev)
{
- ata_port_suspend(to_ata_port(dev), PMSG_HIBERNATE);
+ if (!pm_runtime_suspended(dev))
+ ata_port_suspend(to_ata_port(dev), PMSG_HIBERNATE, false);
return 0;
}
-static const unsigned int ata_port_resume_ehi = ATA_EHI_NO_AUTOPSY
- | ATA_EHI_QUIET;
-
-static void ata_port_resume(struct ata_port *ap, pm_message_t mesg)
-{
- ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, false);
-}
-
-static void ata_port_resume_async(struct ata_port *ap, pm_message_t mesg)
+static void ata_port_resume(struct ata_port *ap, pm_message_t mesg,
+ bool async)
{
- ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, true);
+ ata_port_request_pm(ap, mesg, ATA_EH_RESET,
+ ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET,
+ async);
}
static int ata_port_pm_resume(struct device *dev)
{
- ata_port_resume_async(to_ata_port(dev), PMSG_RESUME);
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ if (!pm_runtime_suspended(dev))
+ ata_port_resume(to_ata_port(dev), PMSG_RESUME, true);
return 0;
}
@@ -5101,13 +5345,13 @@ static int ata_port_runtime_idle(struct device *dev)
static int ata_port_runtime_suspend(struct device *dev)
{
- ata_port_suspend(to_ata_port(dev), PMSG_AUTO_SUSPEND);
+ ata_port_suspend(to_ata_port(dev), PMSG_AUTO_SUSPEND, false);
return 0;
}
static int ata_port_runtime_resume(struct device *dev)
{
- ata_port_resume(to_ata_port(dev), PMSG_AUTO_RESUME);
+ ata_port_resume(to_ata_port(dev), PMSG_AUTO_RESUME, false);
return 0;
}
@@ -5131,13 +5375,13 @@ static const struct dev_pm_ops ata_port_pm_ops = {
*/
void ata_sas_port_suspend(struct ata_port *ap)
{
- ata_port_suspend_async(ap, PMSG_SUSPEND);
+ ata_port_suspend(ap, PMSG_SUSPEND, true);
}
EXPORT_SYMBOL_GPL(ata_sas_port_suspend);
void ata_sas_port_resume(struct ata_port *ap)
{
- ata_port_resume_async(ap, PMSG_RESUME);
+ ata_port_resume(ap, PMSG_RESUME, true);
}
EXPORT_SYMBOL_GPL(ata_sas_port_resume);
@@ -5168,7 +5412,7 @@ EXPORT_SYMBOL_GPL(ata_host_resume);
#endif
const struct device_type ata_port_type = {
- .name = "ata_port",
+ .name = ATA_PORT_TYPE_NAME,
#ifdef CONFIG_PM
.pm = &ata_port_pm_ops,
#endif
@@ -5199,7 +5443,7 @@ void ata_dev_init(struct ata_device *dev)
*/
spin_lock_irqsave(ap->lock, flags);
dev->flags &= ~ATA_DFLAG_INIT_MASK;
- dev->horkage = 0;
+ dev->quirks = 0;
spin_unlock_irqrestore(ap->lock, flags);
memset((void *)dev + ATA_DEVICE_CLEAR_BEGIN, 0,
@@ -5294,6 +5538,7 @@ int sata_link_init_spd(struct ata_link *link)
struct ata_port *ata_port_alloc(struct ata_host *host)
{
struct ata_port *ap;
+ int id;
ap = kzalloc(sizeof(*ap), GFP_KERNEL);
if (!ap)
@@ -5301,14 +5546,18 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN;
ap->lock = &host->lock;
- ap->print_id = -1;
- ap->local_port_no = -1;
+ id = ida_alloc_min(&ata_ida, 1, GFP_KERNEL);
+ if (id < 0) {
+ kfree(ap);
+ return NULL;
+ }
+ ap->print_id = id;
ap->host = host;
ap->dev = host->dev;
mutex_init(&ap->scsi_scan_mutex);
INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
- INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
+ INIT_DELAYED_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
init_completion(&ap->park_req_pending);
@@ -5325,8 +5574,23 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
#endif
ata_sff_port_init(ap);
+ ata_force_pflags(ap);
+
return ap;
}
+EXPORT_SYMBOL_GPL(ata_port_alloc);
+
+void ata_port_free(struct ata_port *ap)
+{
+ if (!ap)
+ return;
+
+ kfree(ap->pmp_link);
+ kfree(ap->slave_link);
+ ida_free(&ata_ida, ap->print_id);
+ kfree(ap);
+}
+EXPORT_SYMBOL_GPL(ata_port_free);
static void ata_devres_release(struct device *gendev, void *res)
{
@@ -5354,11 +5618,7 @@ static void ata_host_release(struct kref *kref)
int i;
for (i = 0; i < host->n_ports; i++) {
- struct ata_port *ap = host->ports[i];
-
- kfree(ap->pmp_link);
- kfree(ap->slave_link);
- kfree(ap);
+ ata_port_free(host->ports[i]);
host->ports[i] = NULL;
}
kfree(host);
@@ -5378,24 +5638,19 @@ EXPORT_SYMBOL_GPL(ata_host_put);
/**
* ata_host_alloc - allocate and init basic ATA host resources
* @dev: generic device this host is associated with
- * @max_ports: maximum number of ATA ports associated with this host
+ * @n_ports: the number of ATA ports associated with this host
*
* Allocate and initialize basic ATA host resources. LLD calls
* this function to allocate a host, initializes it fully and
* attaches it using ata_host_register().
*
- * @max_ports ports are allocated and host->n_ports is
- * initialized to @max_ports. The caller is allowed to decrease
- * host->n_ports before calling ata_host_register(). The unused
- * ports will be automatically freed on registration.
- *
* RETURNS:
* Allocate ATA host on success, NULL on failure.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*/
-struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
+struct ata_host *ata_host_alloc(struct device *dev, int n_ports)
{
struct ata_host *host;
size_t sz;
@@ -5403,17 +5658,21 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
void *dr;
/* alloc a container for our list of ATA ports (buses) */
- sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *);
+ sz = sizeof(struct ata_host) + n_ports * sizeof(void *);
host = kzalloc(sz, GFP_KERNEL);
if (!host)
return NULL;
- if (!devres_open_group(dev, NULL, GFP_KERNEL))
- goto err_free;
+ if (!devres_open_group(dev, NULL, GFP_KERNEL)) {
+ kfree(host);
+ return NULL;
+ }
dr = devres_alloc(ata_devres_release, 0, GFP_KERNEL);
- if (!dr)
+ if (!dr) {
+ kfree(host);
goto err_out;
+ }
devres_add(dev, dr);
dev_set_drvdata(dev, host);
@@ -5421,11 +5680,11 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
spin_lock_init(&host->lock);
mutex_init(&host->eh_mutex);
host->dev = dev;
- host->n_ports = max_ports;
+ host->n_ports = n_ports;
kref_init(&host->kref);
/* allocate ports bound to this host */
- for (i = 0; i < max_ports; i++) {
+ for (i = 0; i < n_ports; i++) {
struct ata_port *ap;
ap = ata_port_alloc(host);
@@ -5441,8 +5700,6 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
err_out:
devres_release_group(dev, NULL);
- err_free:
- kfree(host);
return NULL;
}
EXPORT_SYMBOL_GPL(ata_host_alloc);
@@ -5664,11 +5921,13 @@ void ata_host_init(struct ata_host *host, struct device *dev,
}
EXPORT_SYMBOL_GPL(ata_host_init);
-void __ata_port_probe(struct ata_port *ap)
+void ata_port_probe(struct ata_port *ap)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
unsigned long flags;
+ ata_acpi_port_power_on(ap);
+
/* kick EH for boot probing */
spin_lock_irqsave(ap->lock, flags);
@@ -5682,20 +5941,7 @@ void __ata_port_probe(struct ata_port *ap)
spin_unlock_irqrestore(ap->lock, flags);
}
-
-int ata_port_probe(struct ata_port *ap)
-{
- int rc = 0;
-
- if (ap->ops->error_handler) {
- __ata_port_probe(ap);
- ata_port_wait_eh(ap);
- } else {
- rc = ata_bus_probe(ap);
- }
- return rc;
-}
-
+EXPORT_SYMBOL_GPL(ata_port_probe);
static void async_port_probe(void *data, async_cookie_t cookie)
{
@@ -5711,7 +5957,8 @@ static void async_port_probe(void *data, async_cookie_t cookie)
if (!(ap->host->flags & ATA_HOST_PARALLEL_SCAN) && ap->port_no != 0)
async_synchronize_cookie(cookie);
- (void)ata_port_probe(ap);
+ ata_port_probe(ap);
+ ata_port_wait_eh(ap);
/* in order to keep device order, we need to synchronize at this point */
async_synchronize_cookie(cookie);
@@ -5735,7 +5982,7 @@ static void async_port_probe(void *data, async_cookie_t cookie)
* RETURNS:
* 0 on success, -errno otherwise.
*/
-int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
+int ata_host_register(struct ata_host *host, const struct scsi_host_template *sht)
{
int i, rc;
@@ -5748,19 +5995,6 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
return -EINVAL;
}
- /* Blow away unused ports. This happens when LLD can't
- * determine the exact number of ports to allocate at
- * allocation time.
- */
- for (i = host->n_ports; host->ports[i]; i++)
- kfree(host->ports[i]);
-
- /* give ports names and add SCSI hosts */
- for (i = 0; i < host->n_ports; i++) {
- host->ports[i]->print_id = atomic_inc_return(&ata_print_id);
- host->ports[i]->local_port_no = i + 1;
- }
-
/* Create associated sysfs transport objects */
for (i = 0; i < host->n_ports; i++) {
rc = ata_tport_add(host->dev,host->ports[i]);
@@ -5843,7 +6077,7 @@ EXPORT_SYMBOL_GPL(ata_host_register);
*/
int ata_host_activate(struct ata_host *host, int irq,
irq_handler_t irq_handler, unsigned long irq_flags,
- struct scsi_host_template *sht)
+ const struct scsi_host_template *sht)
{
int i, rc;
char *irq_desc;
@@ -5870,7 +6104,7 @@ int ata_host_activate(struct ata_host *host, int irq,
return rc;
for (i = 0; i < host->n_ports; i++)
- ata_port_desc(host->ports[i], "irq %d", irq);
+ ata_port_desc_misc(host->ports[i], irq);
rc = ata_host_register(host, sht);
/* if failed, just free the IRQ and leave ports alone */
@@ -5882,6 +6116,23 @@ int ata_host_activate(struct ata_host *host, int irq,
EXPORT_SYMBOL_GPL(ata_host_activate);
/**
+ * ata_dev_free_resources - Free a device resources
+ * @dev: Target ATA device
+ *
+ * Free resources allocated to support a device features.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_dev_free_resources(struct ata_device *dev)
+{
+ if (zpodd_dev_enabled(dev))
+ zpodd_exit(dev);
+
+ ata_dev_cleanup_cdl_resources(dev);
+}
+
+/**
* ata_port_detach - Detach ATA port in preparation of device removal
* @ap: ATA port to be detached
*
@@ -5898,14 +6149,33 @@ static void ata_port_detach(struct ata_port *ap)
struct ata_link *link;
struct ata_device *dev;
- if (!ap->ops->error_handler)
- goto skip_eh;
+ /* Ensure ata_port probe has completed */
+ async_synchronize_cookie(ap->cookie + 1);
+
+ /* Wait for any ongoing EH */
+ ata_port_wait_eh(ap);
- /* tell EH we're leaving & flush EH */
+ mutex_lock(&ap->scsi_scan_mutex);
spin_lock_irqsave(ap->lock, flags);
+
+ /* Remove scsi devices */
+ ata_for_each_link(link, ap, HOST_FIRST) {
+ ata_for_each_dev(dev, link, ALL) {
+ if (dev->sdev) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ scsi_remove_device(dev->sdev);
+ spin_lock_irqsave(ap->lock, flags);
+ dev->sdev = NULL;
+ }
+ }
+ }
+
+ /* Tell EH to disable all devices */
ap->pflags |= ATA_PFLAG_UNLOADING;
ata_port_schedule_eh(ap);
+
spin_unlock_irqrestore(ap->lock, flags);
+ mutex_unlock(&ap->scsi_scan_mutex);
/* wait till EH commits suicide */
ata_port_wait_eh(ap);
@@ -5914,21 +6184,17 @@ static void ata_port_detach(struct ata_port *ap)
WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));
cancel_delayed_work_sync(&ap->hotplug_task);
+ cancel_delayed_work_sync(&ap->scsi_rescan_task);
- skip_eh:
- /* clean up zpodd on port removal */
- ata_for_each_link(link, ap, HOST_FIRST) {
- ata_for_each_dev(dev, link, ALL) {
- if (zpodd_dev_enabled(dev))
- zpodd_exit(dev);
- }
- }
+ /* Delete port multiplier link transport devices */
if (ap->pmp_link) {
int i;
+
for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
ata_tlink_delete(&ap->pmp_link[i]);
}
- /* remove the associated SCSI host */
+
+ /* Remove the associated SCSI host */
scsi_remove_host(ap->scsi_host);
ata_tport_delete(ap);
}
@@ -5946,11 +6212,8 @@ void ata_host_detach(struct ata_host *host)
{
int i;
- for (i = 0; i < host->n_ports; i++) {
- /* Ensure ata_port probe has completed */
- async_synchronize_cookie(host->ports[i]->cookie + 1);
+ for (i = 0; i < host->n_ports; i++)
ata_port_detach(host->ports[i]);
- }
/* the host is dead now, dissociate ACPI */
ata_acpi_dissociate(host);
@@ -6101,13 +6364,11 @@ EXPORT_SYMBOL_GPL(ata_pci_device_resume);
* LOCKING:
* Inherited from platform layer (may sleep).
*/
-int ata_platform_remove_one(struct platform_device *pdev)
+void ata_platform_remove_one(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
ata_host_detach(host);
-
- return 0;
}
EXPORT_SYMBOL_GPL(ata_platform_remove_one);
@@ -6129,12 +6390,15 @@ EXPORT_SYMBOL_GPL(ata_platform_remove_one);
{ "no" #name, .lflags_on = (flags) }, \
{ #name, .lflags_off = (flags) }
-#define force_horkage_on(name, flag) \
- { #name, .horkage_on = (flag) }
+#define force_pflag_on(name, flags) \
+ { #name, .pflags_on = (flags) }
+
+#define force_quirk_on(name, flag) \
+ { #name, .quirk_on = (flag) }
-#define force_horkage_onoff(name, flag) \
- { "no" #name, .horkage_on = (flag) }, \
- { #name, .horkage_off = (flag) }
+#define force_quirk_onoff(name, flag) \
+ { "no" #name, .quirk_on = (flag) }, \
+ { #name, .quirk_off = (flag) }
static const struct ata_force_param force_tbl[] __initconst = {
force_cbl(40c, ATA_CBL_PATA40),
@@ -6188,31 +6452,34 @@ static const struct ata_force_param force_tbl[] __initconst = {
force_lflag_on(rstonce, ATA_LFLAG_RST_ONCE),
force_lflag_onoff(dbdelay, ATA_LFLAG_NO_DEBOUNCE_DELAY),
- force_horkage_onoff(ncq, ATA_HORKAGE_NONCQ),
- force_horkage_onoff(ncqtrim, ATA_HORKAGE_NO_NCQ_TRIM),
- force_horkage_onoff(ncqati, ATA_HORKAGE_NO_NCQ_ON_ATI),
+ force_pflag_on(external, ATA_PFLAG_EXTERNAL),
- force_horkage_onoff(trim, ATA_HORKAGE_NOTRIM),
- force_horkage_on(trim_zero, ATA_HORKAGE_ZERO_AFTER_TRIM),
- force_horkage_on(max_trim_128m, ATA_HORKAGE_MAX_TRIM_128M),
+ force_quirk_onoff(ncq, ATA_QUIRK_NONCQ),
+ force_quirk_onoff(ncqtrim, ATA_QUIRK_NO_NCQ_TRIM),
+ force_quirk_onoff(ncqati, ATA_QUIRK_NO_NCQ_ON_ATI),
- force_horkage_onoff(dma, ATA_HORKAGE_NODMA),
- force_horkage_on(atapi_dmadir, ATA_HORKAGE_ATAPI_DMADIR),
- force_horkage_on(atapi_mod16_dma, ATA_HORKAGE_ATAPI_MOD16_DMA),
+ force_quirk_onoff(trim, ATA_QUIRK_NOTRIM),
+ force_quirk_on(trim_zero, ATA_QUIRK_ZERO_AFTER_TRIM),
+ force_quirk_on(max_trim_128m, ATA_QUIRK_MAX_TRIM_128M),
- force_horkage_onoff(dmalog, ATA_HORKAGE_NO_DMA_LOG),
- force_horkage_onoff(iddevlog, ATA_HORKAGE_NO_ID_DEV_LOG),
- force_horkage_onoff(logdir, ATA_HORKAGE_NO_LOG_DIR),
+ force_quirk_onoff(dma, ATA_QUIRK_NODMA),
+ force_quirk_on(atapi_dmadir, ATA_QUIRK_ATAPI_DMADIR),
+ force_quirk_on(atapi_mod16_dma, ATA_QUIRK_ATAPI_MOD16_DMA),
- force_horkage_on(max_sec_128, ATA_HORKAGE_MAX_SEC_128),
- force_horkage_on(max_sec_1024, ATA_HORKAGE_MAX_SEC_1024),
- force_horkage_on(max_sec_lba48, ATA_HORKAGE_MAX_SEC_LBA48),
+ force_quirk_onoff(dmalog, ATA_QUIRK_NO_DMA_LOG),
+ force_quirk_onoff(iddevlog, ATA_QUIRK_NO_ID_DEV_LOG),
+ force_quirk_onoff(logdir, ATA_QUIRK_NO_LOG_DIR),
- force_horkage_onoff(lpm, ATA_HORKAGE_NOLPM),
- force_horkage_onoff(setxfer, ATA_HORKAGE_NOSETXFER),
- force_horkage_on(dump_id, ATA_HORKAGE_DUMP_ID),
+ force_quirk_on(max_sec_128, ATA_QUIRK_MAX_SEC_128),
+ force_quirk_on(max_sec_1024, ATA_QUIRK_MAX_SEC_1024),
+ force_quirk_on(max_sec_lba48, ATA_QUIRK_MAX_SEC_LBA48),
- force_horkage_on(disable, ATA_HORKAGE_DISABLE),
+ force_quirk_onoff(lpm, ATA_QUIRK_NOLPM),
+ force_quirk_onoff(setxfer, ATA_QUIRK_NOSETXFER),
+ force_quirk_on(dump_id, ATA_QUIRK_DUMP_ID),
+ force_quirk_onoff(fua, ATA_QUIRK_NO_FUA),
+
+ force_quirk_on(disable, ATA_QUIRK_DISABLE),
};
static int __init ata_parse_force_one(char **cur,
@@ -6452,7 +6719,7 @@ EXPORT_SYMBOL_GPL(ata_msleep);
* The final register value.
*/
u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val,
- unsigned long interval, unsigned long timeout)
+ unsigned int interval, unsigned int timeout)
{
unsigned long deadline;
u32 tmp;
@@ -6488,7 +6755,6 @@ static void ata_dummy_error_handler(struct ata_port *ap)
}
struct ata_port_operations ata_dummy_port_ops = {
- .qc_prep = ata_noop_qc_prep,
.qc_issue = ata_dummy_qc_issue,
.error_handler = ata_dummy_error_handler,
.sched_eh = ata_std_sched_eh,
@@ -6501,12 +6767,6 @@ const struct ata_port_info ata_dummy_port_info = {
};
EXPORT_SYMBOL_GPL(ata_dummy_port_info);
-void ata_print_version(const struct device *dev, const char *version)
-{
- dev_printk(KERN_DEBUG, dev, "version %s\n", version);
-}
-EXPORT_SYMBOL(ata_print_version);
-
EXPORT_TRACEPOINT_SYMBOL_GPL(ata_tf_load);
EXPORT_TRACEPOINT_SYMBOL_GPL(ata_exec_command);
EXPORT_TRACEPOINT_SYMBOL_GPL(ata_bmdma_setup);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 7c128c89b454..2586e77ebf45 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -78,12 +78,12 @@ enum {
* are mostly for error handling, hotplug and those outlier devices that
* take an exceptionally long time to recover from reset.
*/
-static const unsigned long ata_eh_reset_timeouts[] = {
+static const unsigned int ata_eh_reset_timeouts[] = {
10000, /* most drives spin up by 10sec */
10000, /* > 99% working drives spin up before 20sec */
35000, /* give > 30 secs of idleness for outlier devices */
5000, /* and sweet one last chance */
- ULONG_MAX, /* > 1 min has elapsed, give up */
+ UINT_MAX, /* > 1 min has elapsed, give up */
};
static const unsigned int ata_eh_identify_timeouts[] = {
@@ -147,6 +147,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = {
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT),
.timeouts = ata_eh_flush_timeouts },
+ { .commands = CMDS(ATA_CMD_VERIFY),
+ .timeouts = ata_eh_reset_timeouts },
};
#undef CMDS
@@ -490,19 +492,46 @@ void ata_eh_release(struct ata_port *ap)
mutex_unlock(&ap->host->eh_mutex);
}
+static void ata_eh_dev_disable(struct ata_device *dev)
+{
+ ata_acpi_on_disable(dev);
+ ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
+ dev->class++;
+
+ /*
+ * From now till the next successful probe, ering is used to
+ * track probe failures. Clear accumulated device error info.
+ */
+ ata_ering_clear(&dev->ering);
+
+ ata_dev_free_resources(dev);
+}
+
static void ata_eh_unload(struct ata_port *ap)
{
struct ata_link *link;
struct ata_device *dev;
unsigned long flags;
- /* Restore SControl IPM and SPD for the next driver and
+ /*
+ * Unless we are restarting, transition all enabled devices to
+ * standby power mode.
+ */
+ if (system_state != SYSTEM_RESTART) {
+ ata_for_each_link(link, ap, PMP_FIRST) {
+ ata_for_each_dev(dev, link, ENABLED)
+ ata_dev_power_set_standby(dev);
+ }
+ }
+
+ /*
+ * Restore SControl IPM and SPD for the next driver and
* disable attached devices.
*/
ata_for_each_link(link, ap, PMP_FIRST) {
sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
- ata_for_each_dev(dev, link, ALL)
- ata_dev_disable(dev);
+ ata_for_each_dev(dev, link, ENABLED)
+ ata_eh_dev_disable(dev);
}
/* freeze and set UNLOADED */
@@ -563,17 +592,20 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
{
int i;
unsigned long flags;
+ struct scsi_cmnd *scmd, *tmp;
+ int nr_timedout = 0;
/* make sure sff pio task is not running */
ata_sff_flush_pio_task(ap);
/* synchronize with host lock and sort out timeouts */
- /* For new EH, all qcs are finished in one of three ways -
+ /*
+ * For EH, all qcs are finished in one of three ways -
* normal completion, error completion, and SCSI timeout.
* Both completions can race against SCSI timeout. When normal
* completion wins, the qc never reaches EH. When error
- * completion wins, the qc has ATA_QCFLAG_FAILED set.
+ * completion wins, the qc has ATA_QCFLAG_EH set.
*
* When SCSI timeout wins, things are a bit more complex.
* Normal or error completion can occur after the timeout but
@@ -582,64 +614,70 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
* timed out iff its associated qc is active and not failed.
*/
spin_lock_irqsave(ap->lock, flags);
- if (ap->ops->error_handler) {
- struct scsi_cmnd *scmd, *tmp;
- int nr_timedout = 0;
-
- /* This must occur under the ap->lock as we don't want
- a polled recovery to race the real interrupt handler
- The lost_interrupt handler checks for any completed but
- non-notified command and completes much like an IRQ handler.
-
- We then fall into the error recovery code which will treat
- this as if normal completion won the race */
+ /*
+ * This must occur under the ap->lock as we don't want
+ * a polled recovery to race the real interrupt handler
+ *
+ * The lost_interrupt handler checks for any completed but
+ * non-notified command and completes much like an IRQ handler.
+ *
+ * We then fall into the error recovery code which will treat
+ * this as if normal completion won the race
+ */
+ if (ap->ops->lost_interrupt)
+ ap->ops->lost_interrupt(ap);
- if (ap->ops->lost_interrupt)
- ap->ops->lost_interrupt(ap);
+ list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) {
+ struct ata_queued_cmd *qc;
- list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) {
- struct ata_queued_cmd *qc;
+ /*
+ * If the scmd was added to EH, via ata_qc_schedule_eh() ->
+ * scsi_timeout() -> scsi_eh_scmd_add(), scsi_timeout() will
+ * have set DID_TIME_OUT (since libata does not have an abort
+ * handler). Thus, to clear DID_TIME_OUT, clear the host byte.
+ */
+ set_host_byte(scmd, DID_OK);
- ata_qc_for_each_raw(ap, qc, i) {
- if (qc->flags & ATA_QCFLAG_ACTIVE &&
- qc->scsicmd == scmd)
- break;
- }
+ ata_qc_for_each_raw(ap, qc, i) {
+ if (qc->flags & ATA_QCFLAG_ACTIVE &&
+ qc->scsicmd == scmd)
+ break;
+ }
- if (i < ATA_MAX_QUEUE) {
- /* the scmd has an associated qc */
- if (!(qc->flags & ATA_QCFLAG_FAILED)) {
- /* which hasn't failed yet, timeout */
- qc->err_mask |= AC_ERR_TIMEOUT;
- qc->flags |= ATA_QCFLAG_FAILED;
- nr_timedout++;
- }
- } else {
- /* Normal completion occurred after
- * SCSI timeout but before this point.
- * Successfully complete it.
- */
- scmd->retries = scmd->allowed;
- scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
+ if (i < ATA_MAX_QUEUE) {
+ /* the scmd has an associated qc */
+ if (!(qc->flags & ATA_QCFLAG_EH)) {
+ /* which hasn't failed yet, timeout */
+ set_host_byte(scmd, DID_TIME_OUT);
+ qc->err_mask |= AC_ERR_TIMEOUT;
+ qc->flags |= ATA_QCFLAG_EH;
+ nr_timedout++;
}
+ } else {
+ /* Normal completion occurred after
+ * SCSI timeout but before this point.
+ * Successfully complete it.
+ */
+ scmd->retries = scmd->allowed;
+ scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
}
+ }
- /* If we have timed out qcs. They belong to EH from
- * this point but the state of the controller is
- * unknown. Freeze the port to make sure the IRQ
- * handler doesn't diddle with those qcs. This must
- * be done atomically w.r.t. setting QCFLAG_FAILED.
- */
- if (nr_timedout)
- __ata_port_freeze(ap);
+ /*
+ * If we have timed out qcs. They belong to EH from
+ * this point but the state of the controller is
+ * unknown. Freeze the port to make sure the IRQ
+ * handler doesn't diddle with those qcs. This must
+ * be done atomically w.r.t. setting ATA_QCFLAG_EH.
+ */
+ if (nr_timedout)
+ __ata_port_freeze(ap);
+ /* initialize eh_tries */
+ ap->eh_tries = ATA_EH_MAX_TRIES;
- /* initialize eh_tries */
- ap->eh_tries = ATA_EH_MAX_TRIES;
- }
spin_unlock_irqrestore(ap->lock, flags);
-
}
EXPORT_SYMBOL(ata_scsi_cmd_error_handler);
@@ -654,100 +692,101 @@ EXPORT_SYMBOL(ata_scsi_cmd_error_handler);
void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)
{
unsigned long flags;
+ struct ata_link *link;
- /* invoke error handler */
- if (ap->ops->error_handler) {
- struct ata_link *link;
-
- /* acquire EH ownership */
- ata_eh_acquire(ap);
+ /* acquire EH ownership */
+ ata_eh_acquire(ap);
repeat:
- /* kill fast drain timer */
- del_timer_sync(&ap->fastdrain_timer);
+ /* kill fast drain timer */
+ timer_delete_sync(&ap->fastdrain_timer);
- /* process port resume request */
- ata_eh_handle_port_resume(ap);
+ /* process port resume request */
+ ata_eh_handle_port_resume(ap);
- /* fetch & clear EH info */
- spin_lock_irqsave(ap->lock, flags);
+ /* fetch & clear EH info */
+ spin_lock_irqsave(ap->lock, flags);
- ata_for_each_link(link, ap, HOST_FIRST) {
- struct ata_eh_context *ehc = &link->eh_context;
- struct ata_device *dev;
+ ata_for_each_link(link, ap, HOST_FIRST) {
+ struct ata_eh_context *ehc = &link->eh_context;
+ struct ata_device *dev;
+
+ memset(&link->eh_context, 0, sizeof(link->eh_context));
+ link->eh_context.i = link->eh_info;
+ memset(&link->eh_info, 0, sizeof(link->eh_info));
- memset(&link->eh_context, 0, sizeof(link->eh_context));
- link->eh_context.i = link->eh_info;
- memset(&link->eh_info, 0, sizeof(link->eh_info));
+ ata_for_each_dev(dev, link, ENABLED) {
+ int devno = dev->devno;
- ata_for_each_dev(dev, link, ENABLED) {
- int devno = dev->devno;
+ ehc->saved_xfer_mode[devno] = dev->xfer_mode;
+ if (ata_ncq_enabled(dev))
+ ehc->saved_ncq_enabled |= 1 << devno;
- ehc->saved_xfer_mode[devno] = dev->xfer_mode;
- if (ata_ncq_enabled(dev))
- ehc->saved_ncq_enabled |= 1 << devno;
+ /* If we are resuming, wake up the device */
+ if (ap->pflags & ATA_PFLAG_RESUMING) {
+ dev->flags |= ATA_DFLAG_RESUMING;
+ ehc->i.dev_action[devno] |= ATA_EH_SET_ACTIVE;
}
}
+ }
- ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
- ap->pflags &= ~ATA_PFLAG_EH_PENDING;
- ap->excl_link = NULL; /* don't maintain exclusion over EH */
+ ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
+ ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+ ap->excl_link = NULL; /* don't maintain exclusion over EH */
- spin_unlock_irqrestore(ap->lock, flags);
+ spin_unlock_irqrestore(ap->lock, flags);
- /* invoke EH, skip if unloading or suspended */
- if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
- ap->ops->error_handler(ap);
- else {
- /* if unloading, commence suicide */
- if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
- !(ap->pflags & ATA_PFLAG_UNLOADED))
- ata_eh_unload(ap);
- ata_eh_finish(ap);
- }
+ /* invoke EH, skip if unloading or suspended */
+ if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
+ ap->ops->error_handler(ap);
+ else {
+ /* if unloading, commence suicide */
+ if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
+ !(ap->pflags & ATA_PFLAG_UNLOADED))
+ ata_eh_unload(ap);
+ ata_eh_finish(ap);
+ }
- /* process port suspend request */
- ata_eh_handle_port_suspend(ap);
+ /* process port suspend request */
+ ata_eh_handle_port_suspend(ap);
- /* Exception might have happened after ->error_handler
- * recovered the port but before this point. Repeat
- * EH in such case.
- */
- spin_lock_irqsave(ap->lock, flags);
+ /*
+ * Exception might have happened after ->error_handler recovered the
+ * port but before this point. Repeat EH in such case.
+ */
+ spin_lock_irqsave(ap->lock, flags);
- if (ap->pflags & ATA_PFLAG_EH_PENDING) {
- if (--ap->eh_tries) {
- spin_unlock_irqrestore(ap->lock, flags);
- goto repeat;
- }
- ata_port_err(ap,
- "EH pending after %d tries, giving up\n",
- ATA_EH_MAX_TRIES);
- ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+ if (ap->pflags & ATA_PFLAG_EH_PENDING) {
+ if (--ap->eh_tries) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ goto repeat;
}
+ ata_port_err(ap,
+ "EH pending after %d tries, giving up\n",
+ ATA_EH_MAX_TRIES);
+ ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+ }
- /* this run is complete, make sure EH info is clear */
- ata_for_each_link(link, ap, HOST_FIRST)
- memset(&link->eh_info, 0, sizeof(link->eh_info));
+ /* this run is complete, make sure EH info is clear */
+ ata_for_each_link(link, ap, HOST_FIRST)
+ memset(&link->eh_info, 0, sizeof(link->eh_info));
- /* end eh (clear host_eh_scheduled) while holding
- * ap->lock such that if exception occurs after this
- * point but before EH completion, SCSI midlayer will
- * re-initiate EH.
- */
- ap->ops->end_eh(ap);
+ /*
+ * end eh (clear host_eh_scheduled) while holding ap->lock such that if
+ * exception occurs after this point but before EH completion, SCSI
+ * midlayer will re-initiate EH.
+ */
+ ap->ops->end_eh(ap);
- spin_unlock_irqrestore(ap->lock, flags);
- ata_eh_release(ap);
- } else {
- WARN_ON(ata_qc_from_tag(ap, ap->link.active_tag) == NULL);
- ap->ops->eng_timeout(ap);
- }
+ spin_unlock_irqrestore(ap->lock, flags);
+ ata_eh_release(ap);
scsi_eh_flush_done_q(&ap->eh_done_q);
/* clean up */
spin_lock_irqsave(ap->lock, flags);
+ ap->pflags &= ~ATA_PFLAG_RESUMING;
+
if (ap->pflags & ATA_PFLAG_LOADING)
ap->pflags &= ~ATA_PFLAG_LOADING;
else if ((ap->pflags & ATA_PFLAG_SCSI_HOTPLUG) &&
@@ -784,7 +823,7 @@ void ata_port_wait_eh(struct ata_port *ap)
retry:
spin_lock_irqsave(ap->lock, flags);
- while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) {
+ while (ata_port_eh_scheduled(ap)) {
prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE);
spin_unlock_irqrestore(ap->lock, flags);
schedule();
@@ -819,7 +858,7 @@ static unsigned int ata_eh_nr_in_flight(struct ata_port *ap)
void ata_eh_fastdrain_timerfn(struct timer_list *t)
{
- struct ata_port *ap = from_timer(ap, t, fastdrain_timer);
+ struct ata_port *ap = timer_container_of(ap, t, fastdrain_timer);
unsigned long flags;
unsigned int cnt;
@@ -868,7 +907,7 @@ void ata_eh_fastdrain_timerfn(struct timer_list *t)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static void ata_eh_set_pending(struct ata_port *ap, int fastdrain)
+static void ata_eh_set_pending(struct ata_port *ap, bool fastdrain)
{
unsigned int cnt;
@@ -907,14 +946,12 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- WARN_ON(!ap->ops->error_handler);
-
- qc->flags |= ATA_QCFLAG_FAILED;
- ata_eh_set_pending(ap, 1);
+ qc->flags |= ATA_QCFLAG_EH;
+ ata_eh_set_pending(ap, true);
/* The following will fail if timeout has already expired.
* ata_scsi_error() takes care of such scmds on EH entry.
- * Note that ATA_QCFLAG_FAILED is unconditionally set after
+ * Note that ATA_QCFLAG_EH is unconditionally set after
* this function completes.
*/
blk_abort_request(scsi_cmd_to_rq(qc->scsicmd));
@@ -929,12 +966,10 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
*/
void ata_std_sched_eh(struct ata_port *ap)
{
- WARN_ON(!ap->ops->error_handler);
-
if (ap->pflags & ATA_PFLAG_INITIALIZING)
return;
- ata_eh_set_pending(ap, 1);
+ ata_eh_set_pending(ap, true);
scsi_schedule_eh(ap->scsi_host);
trace_ata_std_sched_eh(ap);
@@ -984,15 +1019,13 @@ static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link)
struct ata_queued_cmd *qc;
int tag, nr_aborted = 0;
- WARN_ON(!ap->ops->error_handler);
-
/* we're gonna abort all commands, no need for fast drain */
- ata_eh_set_pending(ap, 0);
+ ata_eh_set_pending(ap, false);
/* include internal tag in iteration */
ata_qc_for_each_with_internal(ap, qc, tag) {
if (qc && (!link || qc->dev->link == link)) {
- qc->flags |= ATA_QCFLAG_FAILED;
+ qc->flags |= ATA_QCFLAG_EH;
ata_qc_complete(qc);
nr_aborted++;
}
@@ -1060,8 +1093,6 @@ EXPORT_SYMBOL_GPL(ata_port_abort);
*/
static void __ata_port_freeze(struct ata_port *ap)
{
- WARN_ON(!ap->ops->error_handler);
-
if (ap->ops->freeze)
ap->ops->freeze(ap);
@@ -1086,14 +1117,9 @@ static void __ata_port_freeze(struct ata_port *ap)
*/
int ata_port_freeze(struct ata_port *ap)
{
- int nr_aborted;
-
- WARN_ON(!ap->ops->error_handler);
-
__ata_port_freeze(ap);
- nr_aborted = ata_port_abort(ap);
- return nr_aborted;
+ return ata_port_abort(ap);
}
EXPORT_SYMBOL_GPL(ata_port_freeze);
@@ -1110,9 +1136,6 @@ void ata_eh_freeze_port(struct ata_port *ap)
{
unsigned long flags;
- if (!ap->ops->error_handler)
- return;
-
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -1132,9 +1155,6 @@ void ata_eh_thaw_port(struct ata_port *ap)
{
unsigned long flags;
- if (!ap->ops->error_handler)
- return;
-
spin_lock_irqsave(ap->lock, flags);
ap->pflags &= ~ATA_PFLAG_FROZEN;
@@ -1215,14 +1235,8 @@ void ata_dev_disable(struct ata_device *dev)
return;
ata_dev_warn(dev, "disable device\n");
- ata_acpi_on_disable(dev);
- ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
- dev->class++;
- /* From now till the next successful probe, ering is used to
- * track probe failures. Clear accumulated device error info.
- */
- ata_ering_clear(&dev->ering);
+ ata_eh_dev_disable(dev);
}
EXPORT_SYMBOL_GPL(ata_dev_disable);
@@ -1242,7 +1256,14 @@ void ata_eh_detach_dev(struct ata_device *dev)
struct ata_eh_context *ehc = &link->eh_context;
unsigned long flags;
- ata_dev_disable(dev);
+ /*
+ * If the device is still enabled, transition it to standby power mode
+ * (i.e. spin down HDDs) and disable it.
+ */
+ if (ata_dev_enabled(dev)) {
+ ata_dev_power_set_standby(dev);
+ ata_eh_dev_disable(dev);
+ }
spin_lock_irqsave(ap->lock, flags);
@@ -1391,34 +1412,70 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)
}
/**
+ * ata_eh_decide_disposition - Disposition a qc based on sense data
+ * @qc: qc to examine
+ *
+ * For a regular SCSI command, the SCSI completion callback (scsi_done())
+ * will call scsi_complete(), which will call scsi_decide_disposition(),
+ * which will call scsi_check_sense(). scsi_complete() finally calls
+ * scsi_finish_command(). This is fine for SCSI, since any eventual sense
+ * data is usually returned in the completion itself (without invoking SCSI
+ * EH). However, for a QC, we always need to fetch the sense data
+ * explicitly using SCSI EH.
+ *
+ * A command that is completed via SCSI EH will instead be completed using
+ * scsi_eh_flush_done_q(), which will call scsi_finish_command() directly
+ * (without ever calling scsi_check_sense()).
+ *
+ * For a command that went through SCSI EH, it is the responsibility of the
+ * SCSI EH strategy handler to call scsi_decide_disposition(), see e.g. how
+ * scsi_eh_get_sense() calls scsi_decide_disposition() for SCSI LLDDs that
+ * do not get the sense data as part of the completion.
+ *
+ * Thus, for QC commands that went via SCSI EH, we need to call
+ * scsi_check_sense() ourselves, similar to how scsi_eh_get_sense() calls
+ * scsi_decide_disposition(), which calls scsi_check_sense(), in order to
+ * set the correct SCSI ML byte (if any).
+ *
+ * LOCKING:
+ * EH context.
+ *
+ * RETURNS:
+ * SUCCESS or FAILED or NEEDS_RETRY or ADD_TO_MLQUEUE
+ */
+enum scsi_disposition ata_eh_decide_disposition(struct ata_queued_cmd *qc)
+{
+ return scsi_check_sense(qc->scsicmd);
+}
+
+/**
* ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT
* @qc: qc to perform REQUEST_SENSE_SENSE_DATA_EXT to
- * @cmd: scsi command for which the sense code should be set
*
* Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK
* SENSE. This function is an EH helper.
*
* LOCKING:
* Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * true if sense data could be fetched, false otherwise.
*/
-static void ata_eh_request_sense(struct ata_queued_cmd *qc,
- struct scsi_cmnd *cmd)
+static bool ata_eh_request_sense(struct ata_queued_cmd *qc)
{
+ struct scsi_cmnd *cmd = qc->scsicmd;
struct ata_device *dev = qc->dev;
struct ata_taskfile tf;
unsigned int err_mask;
- if (qc->ap->pflags & ATA_PFLAG_FROZEN) {
+ if (ata_port_is_frozen(qc->ap)) {
ata_dev_warn(dev, "sense data available but port frozen\n");
- return;
+ return false;
}
- if (!cmd || qc->flags & ATA_QCFLAG_SENSE_VALID)
- return;
-
if (!ata_id_sense_reporting_enabled(dev->id)) {
ata_dev_warn(qc->dev, "sense data reporting disabled\n");
- return;
+ return false;
}
ata_tf_init(dev, &tf);
@@ -1430,12 +1487,20 @@ static void ata_eh_request_sense(struct ata_queued_cmd *qc,
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
/* Ignore err_mask; ATA_ERR might be set */
if (tf.status & ATA_SENSE) {
- ata_scsi_set_sense(dev, cmd, tf.lbah, tf.lbam, tf.lbal);
- qc->flags |= ATA_QCFLAG_SENSE_VALID;
+ if (ata_scsi_sense_is_valid(tf.lbah, tf.lbam, tf.lbal)) {
+ /* Set sense without also setting scsicmd->result */
+ scsi_build_sense_buffer(dev->flags & ATA_DFLAG_D_SENSE,
+ cmd->sense_buffer, tf.lbah,
+ tf.lbam, tf.lbal);
+ qc->flags |= ATA_QCFLAG_SENSE_VALID;
+ return true;
+ }
} else {
ata_dev_warn(dev, "request sense failed stat %02x emask %x\n",
tf.status, err_mask);
}
+
+ return false;
}
/**
@@ -1475,8 +1540,15 @@ unsigned int atapi_eh_request_sense(struct ata_device *dev,
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.command = ATA_CMD_PACKET;
- /* is it pointless to prefer PIO for "safety reasons"? */
- if (ap->flags & ATA_FLAG_PIO_DMA) {
+ /*
+ * Do not use DMA if the connected device only supports PIO, even if the
+ * port prefers PIO commands via DMA.
+ *
+ * Ideally, we should call atapi_check_dma() to check if it is safe for
+ * the LLD to use DMA for REQUEST_SENSE, but we don't have a qc.
+ * Since we can't check the command, perhaps we should only use pio?
+ */
+ if ((ap->flags & ATA_FLAG_PIO_DMA) && !(dev->flags & ATA_DFLAG_PIO)) {
tf.protocol = ATAPI_PROT_DMA;
tf.feature |= ATAPI_PKT_DMA;
} else {
@@ -1541,7 +1613,6 @@ static void ata_eh_analyze_serror(struct ata_link *link)
/**
* ata_eh_analyze_tf - analyze taskfile of a failed qc
* @qc: qc to analyze
- * @tf: Taskfile registers to analyze
*
* Analyze taskfile of @qc and further determine cause of
* failure. This function also requests ATAPI sense data if
@@ -1553,9 +1624,9 @@ static void ata_eh_analyze_serror(struct ata_link *link)
* RETURNS:
* Determined recovery action
*/
-static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
- const struct ata_taskfile *tf)
+static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc)
{
+ const struct ata_taskfile *tf = &qc->result_tf;
unsigned int tmp, action = 0;
u8 stat = tf->status, err = tf->error;
@@ -1577,11 +1648,18 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
}
switch (qc->dev->class) {
- case ATA_DEV_ZAC:
- if (stat & ATA_SENSE)
- ata_eh_request_sense(qc, qc->scsicmd);
- fallthrough;
case ATA_DEV_ATA:
+ case ATA_DEV_ZAC:
+ /*
+ * Fetch the sense data explicitly if:
+ * -It was a non-NCQ command that failed, or
+ * -It was a NCQ command that failed, but the sense data
+ * was not included in the NCQ command error log
+ * (i.e. NCQ autosense is not supported by the device).
+ */
+ if (!(qc->flags & ATA_QCFLAG_SENSE_VALID) &&
+ (stat & ATA_SENSE) && ata_eh_request_sense(qc))
+ set_status_byte(qc->scsicmd, SAM_STAT_CHECK_CONDITION);
if (err & ATA_ICRC)
qc->err_mask |= AC_ERR_ATA_BUS;
if (err & (ATA_UNC | ATA_AMNF))
@@ -1591,7 +1669,7 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
break;
case ATA_DEV_ATAPI:
- if (!(qc->ap->pflags & ATA_PFLAG_FROZEN)) {
+ if (!ata_port_is_frozen(qc->ap)) {
tmp = atapi_eh_request_sense(qc->dev,
qc->scsicmd->sense_buffer,
qc->result_tf.error >> 4);
@@ -1603,7 +1681,8 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
}
if (qc->flags & ATA_QCFLAG_SENSE_VALID) {
- enum scsi_disposition ret = scsi_check_sense(qc->scsicmd);
+ enum scsi_disposition ret = ata_eh_decide_disposition(qc);
+
/*
* SUCCESS here means that the sense code could be
* evaluated and should be passed to the upper layers
@@ -1809,9 +1888,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev,
verdict = ata_eh_speed_down_verdict(dev);
/* turn off NCQ? */
- if ((verdict & ATA_EH_SPDN_NCQ_OFF) &&
- (dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ |
- ATA_DFLAG_NCQ_OFF)) == ATA_DFLAG_NCQ) {
+ if ((verdict & ATA_EH_SPDN_NCQ_OFF) && ata_ncq_enabled(dev)) {
dev->flags |= ATA_DFLAG_NCQ_OFF;
ata_dev_warn(dev, "NCQ disabled due to excessive errors\n");
goto done;
@@ -1902,6 +1979,280 @@ static inline bool ata_eh_quiet(struct ata_queued_cmd *qc)
return qc->flags & ATA_QCFLAG_QUIET;
}
+static int ata_eh_get_non_ncq_success_sense(struct ata_link *link)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_queued_cmd *qc;
+
+ qc = __ata_qc_from_tag(ap, link->active_tag);
+ if (!qc)
+ return -EIO;
+
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
+ !(qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD) ||
+ qc->err_mask)
+ return -EIO;
+
+ if (!ata_eh_request_sense(qc))
+ return -EIO;
+
+ /*
+ * No point in checking the return value, since the command has already
+ * completed successfully.
+ */
+ ata_eh_decide_disposition(qc);
+
+ return 0;
+}
+
+static void ata_eh_get_success_sense(struct ata_link *link)
+{
+ struct ata_eh_context *ehc = &link->eh_context;
+ struct ata_device *dev = link->device;
+ struct ata_port *ap = link->ap;
+ struct ata_queued_cmd *qc;
+ int tag, ret = 0;
+
+ if (!(ehc->i.dev_action[dev->devno] & ATA_EH_GET_SUCCESS_SENSE))
+ return;
+
+ /* if frozen, we can't do much */
+ if (ata_port_is_frozen(ap)) {
+ ata_dev_warn(dev,
+ "successful sense data available but port frozen\n");
+ goto out;
+ }
+
+ /*
+ * If the link has sactive set, then we have outstanding NCQ commands
+ * and have to read the Successful NCQ Commands log to get the sense
+ * data. Otherwise, we are dealing with a non-NCQ command and use
+ * request sense ext command to retrieve the sense data.
+ */
+ if (link->sactive)
+ ret = ata_eh_get_ncq_success_sense(link);
+ else
+ ret = ata_eh_get_non_ncq_success_sense(link);
+ if (ret)
+ goto out;
+
+ ata_eh_done(link, dev, ATA_EH_GET_SUCCESS_SENSE);
+ return;
+
+out:
+ /*
+ * If we failed to get sense data for a successful command that ought to
+ * have sense data, we cannot simply return BLK_STS_OK to user space.
+ * This is because we can't know if the sense data that we couldn't get
+ * was actually "DATA CURRENTLY UNAVAILABLE". Reporting such a command
+ * as success to user space would result in a silent data corruption.
+ * Thus, add a bogus ABORTED_COMMAND sense data to such commands, such
+ * that SCSI will report these commands as BLK_STS_IOERR to user space.
+ */
+ ata_qc_for_each_raw(ap, qc, tag) {
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
+ !(qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD) ||
+ qc->err_mask ||
+ ata_dev_phys_link(qc->dev) != link)
+ continue;
+
+ /* We managed to get sense for this success command, skip. */
+ if (qc->flags & ATA_QCFLAG_SENSE_VALID)
+ continue;
+
+ /* This success command did not have any sense data, skip. */
+ if (!(qc->result_tf.status & ATA_SENSE))
+ continue;
+
+ /* This success command had sense data, but we failed to get. */
+ ata_scsi_set_sense(dev, qc->scsicmd, ABORTED_COMMAND, 0, 0);
+ qc->flags |= ATA_QCFLAG_SENSE_VALID;
+ }
+ ata_eh_done(link, dev, ATA_EH_GET_SUCCESS_SENSE);
+}
+
+/*
+ * Check if a link is established. This is a relaxed version of
+ * ata_phys_link_online() which accounts for the fact that this is potentially
+ * called after changing the link power management policy, which may not be
+ * reflected immediately in the SStatus register (e.g., we may still be seeing
+ * the PHY in partial, slumber or devsleep Partial power management state.
+ * So check that:
+ * - A device is still present, that is, DET is 1h (Device presence detected
+ * but Phy communication not established) or 3h (Device presence detected and
+ * Phy communication established)
+ * - Communication is established, that is, IPM is not 0h, indicating that PHY
+ * is online or in a low power state.
+ */
+static bool ata_eh_link_established(struct ata_link *link)
+{
+ u32 sstatus;
+ u8 det, ipm;
+
+ /*
+ * For old IDE/PATA adapters that do not have a valid scr_read method,
+ * or if reading the SStatus register fails, assume that the device is
+ * present. Device probe will determine if that is really the case.
+ */
+ if (sata_scr_read(link, SCR_STATUS, &sstatus))
+ return true;
+
+ det = sstatus & 0x0f;
+ ipm = (sstatus >> 8) & 0x0f;
+
+ return (det & 0x01) && ipm;
+}
+
+/**
+ * ata_eh_link_set_lpm - configure SATA interface power management
+ * @link: link to configure
+ * @policy: the link power management policy
+ * @r_failed_dev: out parameter for failed device
+ *
+ * Enable SATA Interface power management. This will enable
+ * Device Interface Power Management (DIPM) for min_power and
+ * medium_power_with_dipm policies, and then call driver specific
+ * callbacks for enabling Host Initiated Power management.
+ *
+ * LOCKING:
+ * EH context.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int ata_eh_link_set_lpm(struct ata_link *link,
+ enum ata_lpm_policy policy,
+ struct ata_device **r_failed_dev)
+{
+ struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
+ struct ata_eh_context *ehc = &link->eh_context;
+ struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
+ enum ata_lpm_policy old_policy = link->lpm_policy;
+ bool host_has_dipm = !(link->ap->flags & ATA_FLAG_NO_DIPM);
+ unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
+ unsigned int err_mask;
+ int rc;
+
+ /* if the link or host doesn't do LPM, noop */
+ if (!IS_ENABLED(CONFIG_SATA_HOST) ||
+ (link->flags & ATA_LFLAG_NO_LPM) || (ap && !ap->ops->set_lpm))
+ return 0;
+
+ /*
+ * This function currently assumes that it will never be supplied policy
+ * ATA_LPM_UNKNOWN.
+ */
+ if (WARN_ON_ONCE(policy == ATA_LPM_UNKNOWN))
+ return 0;
+
+ ata_link_dbg(link, "Set LPM policy: %d -> %d\n", old_policy, policy);
+
+ /*
+ * DIPM is enabled only for ATA_LPM_MIN_POWER,
+ * ATA_LPM_MIN_POWER_WITH_PARTIAL, and ATA_LPM_MED_POWER_WITH_DIPM, as
+ * some devices misbehave when the host NACKs transition to SLUMBER.
+ */
+ ata_for_each_dev(dev, link, ENABLED) {
+ bool dev_has_hipm = ata_id_has_hipm(dev->id);
+ bool dev_has_dipm = ata_id_has_dipm(dev->id);
+
+ /* find the first enabled and LPM enabled devices */
+ if (!link_dev)
+ link_dev = dev;
+
+ if (!lpm_dev &&
+ (dev_has_hipm || (dev_has_dipm && host_has_dipm)))
+ lpm_dev = dev;
+
+ hints &= ~ATA_LPM_EMPTY;
+ if (!dev_has_hipm)
+ hints &= ~ATA_LPM_HIPM;
+
+ /* disable DIPM before changing link config */
+ if (dev_has_dipm) {
+ err_mask = ata_dev_set_feature(dev,
+ SETFEATURES_SATA_DISABLE, SATA_DIPM);
+ if (err_mask && err_mask != AC_ERR_DEV) {
+ ata_dev_warn(dev,
+ "failed to disable DIPM, Emask 0x%x\n",
+ err_mask);
+ rc = -EIO;
+ goto fail;
+ }
+ }
+ }
+
+ if (ap) {
+ rc = ap->ops->set_lpm(link, policy, hints);
+ if (!rc && ap->slave_link)
+ rc = ap->ops->set_lpm(ap->slave_link, policy, hints);
+ } else
+ rc = sata_pmp_set_lpm(link, policy, hints);
+
+ /*
+ * Attribute link config failure to the first (LPM) enabled
+ * device on the link.
+ */
+ if (rc) {
+ if (rc == -EOPNOTSUPP) {
+ link->flags |= ATA_LFLAG_NO_LPM;
+ return 0;
+ }
+ dev = lpm_dev ? lpm_dev : link_dev;
+ goto fail;
+ }
+
+ /*
+ * Low level driver acked the transition. Issue DIPM command
+ * with the new policy set.
+ */
+ link->lpm_policy = policy;
+ if (ap && ap->slave_link)
+ ap->slave_link->lpm_policy = policy;
+
+ /*
+ * Host config updated, enable DIPM if transitioning to
+ * ATA_LPM_MIN_POWER, ATA_LPM_MIN_POWER_WITH_PARTIAL, or
+ * ATA_LPM_MED_POWER_WITH_DIPM.
+ */
+ ata_for_each_dev(dev, link, ENABLED) {
+ bool dev_has_dipm = ata_id_has_dipm(dev->id);
+
+ if (policy >= ATA_LPM_MED_POWER_WITH_DIPM && host_has_dipm &&
+ dev_has_dipm) {
+ err_mask = ata_dev_set_feature(dev,
+ SETFEATURES_SATA_ENABLE, SATA_DIPM);
+ if (err_mask && err_mask != AC_ERR_DEV) {
+ ata_dev_warn(dev,
+ "failed to enable DIPM, Emask 0x%x\n",
+ err_mask);
+ rc = -EIO;
+ goto fail;
+ }
+ }
+ }
+
+ link->last_lpm_change = jiffies;
+ link->flags |= ATA_LFLAG_CHANGED;
+
+ return 0;
+
+fail:
+ /* restore the old policy */
+ link->lpm_policy = old_policy;
+ if (ap && ap->slave_link)
+ ap->slave_link->lpm_policy = old_policy;
+
+ /* if no device or only one more chance is left, disable LPM */
+ if (!dev || ehc->tries[dev->devno] <= 2) {
+ ata_link_warn(link, "disabling LPM on the link\n");
+ link->flags |= ATA_LFLAG_NO_LPM;
+ }
+ if (r_failed_dev)
+ *r_failed_dev = dev;
+ return rc;
+}
+
/**
* ata_eh_link_autopsy - analyze error and determine recovery action
* @link: host link to perform autopsy on
@@ -1942,6 +2293,14 @@ static void ata_eh_link_autopsy(struct ata_link *link)
/* analyze NCQ failure */
ata_eh_analyze_ncq_error(link);
+ /*
+ * Check if this was a successful command that simply needs sense data.
+ * Since the sense data is not part of the completion, we need to fetch
+ * it using an additional command. Since this can't be done from irq
+ * context, the sense data for successful commands are fetched by EH.
+ */
+ ata_eh_get_success_sense(link);
+
/* any real error trumps AC_ERR_OTHER */
if (ehc->i.err_mask & ~AC_ERR_OTHER)
ehc->i.err_mask &= ~AC_ERR_OTHER;
@@ -1949,7 +2308,9 @@ static void ata_eh_link_autopsy(struct ata_link *link)
all_err_mask |= ehc->i.err_mask;
ata_qc_for_each_raw(ap, qc, tag) {
- if (!(qc->flags & ATA_QCFLAG_FAILED) ||
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
+ qc->flags & ATA_QCFLAG_RETRY ||
+ qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD ||
ata_dev_phys_link(qc->dev) != link)
continue;
@@ -1957,7 +2318,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
qc->err_mask |= ehc->i.err_mask;
/* analyze TF */
- ehc->i.action |= ata_eh_analyze_tf(qc, &qc->result_tf);
+ ehc->i.action |= ata_eh_analyze_tf(qc);
/* DEV errors are probably spurious in case of ATA_BUS error */
if (qc->err_mask & AC_ERR_ATA_BUS)
@@ -1998,7 +2359,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
ehc->i.flags |= ATA_EHI_QUIET;
/* enforce default EH actions */
- if (ap->pflags & ATA_PFLAG_FROZEN ||
+ if (ata_port_is_frozen(ap) ||
all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT))
ehc->i.action |= ATA_EH_RESET;
else if (((eflags & ATA_EFLAG_IS_IO) && all_err_mask) ||
@@ -2215,7 +2576,7 @@ static void ata_eh_link_report(struct ata_link *link)
struct ata_eh_context *ehc = &link->eh_context;
struct ata_queued_cmd *qc;
const char *frozen, *desc;
- char tries_buf[6] = "";
+ char tries_buf[16] = "";
int tag, nr_failed = 0;
if (ehc->i.flags & ATA_EHI_QUIET)
@@ -2226,7 +2587,7 @@ static void ata_eh_link_report(struct ata_link *link)
desc = ehc->i.desc;
ata_qc_for_each_raw(ap, qc, tag) {
- if (!(qc->flags & ATA_QCFLAG_FAILED) ||
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
ata_dev_phys_link(qc->dev) != link ||
((qc->flags & ATA_QCFLAG_QUIET) &&
qc->err_mask == AC_ERR_DEV))
@@ -2241,7 +2602,7 @@ static void ata_eh_link_report(struct ata_link *link)
return;
frozen = "";
- if (ap->pflags & ATA_PFLAG_FROZEN)
+ if (ata_port_is_frozen(ap))
frozen = " frozen";
if (ap->eh_tries < ATA_EH_MAX_TRIES)
@@ -2292,7 +2653,7 @@ static void ata_eh_link_report(struct ata_link *link)
char data_buf[20] = "";
char cdb_buf[70] = "";
- if (!(qc->flags & ATA_QCFLAG_FAILED) ||
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
ata_dev_phys_link(qc->dev) != link || !qc->err_mask)
continue;
@@ -2425,25 +2786,28 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
return reset(link, classes, deadline);
}
-static int ata_eh_followup_srst_needed(struct ata_link *link, int rc)
+static bool ata_eh_followup_srst_needed(struct ata_link *link, int rc)
{
if ((link->flags & ATA_LFLAG_NO_SRST) || ata_link_offline(link))
- return 0;
+ return false;
if (rc == -EAGAIN)
- return 1;
+ return true;
if (sata_pmp_supported(link->ap) && ata_is_host_link(link))
- return 1;
- return 0;
+ return true;
+ return false;
}
int ata_eh_reset(struct ata_link *link, int classify,
- ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
- ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+ struct ata_reset_operations *reset_ops)
{
struct ata_port *ap = link->ap;
struct ata_link *slave = ap->slave_link;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_eh_context *sehc = slave ? &slave->eh_context : NULL;
+ ata_reset_fn_t hardreset = reset_ops->hardreset;
+ ata_reset_fn_t softreset = reset_ops->softreset;
+ ata_prereset_fn_t prereset = reset_ops->prereset;
+ ata_postreset_fn_t postreset = reset_ops->postreset;
unsigned int *classes = ehc->classes;
unsigned int lflags = link->flags;
int verbose = !(ehc->i.flags & ATA_EHI_QUIET);
@@ -2459,7 +2823,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
/*
* Prepare to reset
*/
- while (ata_eh_reset_timeouts[max_tries] != ULONG_MAX)
+ while (ata_eh_reset_timeouts[max_tries] != UINT_MAX)
max_tries++;
if (link->flags & ATA_LFLAG_RST_ONCE)
max_tries = 1;
@@ -2562,8 +2926,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
if (reset && !(ehc->i.action & ATA_EH_RESET)) {
ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
- if ((ap->pflags & ATA_PFLAG_FROZEN) &&
- ata_is_host_link(link))
+ if (ata_port_is_frozen(ap) && ata_is_host_link(link))
ata_eh_thaw_port(ap);
rc = 0;
goto out;
@@ -2707,23 +3070,13 @@ int ata_eh_reset(struct ata_link *link, int classify,
}
}
- /*
- * Some controllers can't be frozen very well and may set spurious
- * error conditions during reset. Clear accumulated error
- * information and re-thaw the port if frozen. As reset is the
- * final recovery action and we cross check link onlineness against
- * device classification later, no hotplug event is lost by this.
- */
+ /* clear cached SError */
spin_lock_irqsave(link->ap->lock, flags);
- memset(&link->eh_info, 0, sizeof(link->eh_info));
+ link->eh_info.serror = 0;
if (slave)
- memset(&slave->eh_info, 0, sizeof(link->eh_info));
- ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+ slave->eh_info.serror = 0;
spin_unlock_irqrestore(link->ap->lock, flags);
- if (ap->pflags & ATA_PFLAG_FROZEN)
- ata_eh_thaw_port(ap);
-
/*
* Make sure onlineness and classification result correspond.
* Hotplug could have happened during reset and some
@@ -2803,6 +3156,8 @@ int ata_eh_reset(struct ata_link *link, int classify,
*/
if (ata_is_host_link(link))
ata_eh_thaw_port(ap);
+ ata_link_warn(link, "%s failed\n",
+ reset == hardreset ? "hardreset" : "softreset");
goto out;
}
@@ -2940,7 +3295,24 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) {
WARN_ON(dev->class == ATA_DEV_PMP);
- if (ata_phys_link_offline(ata_dev_phys_link(dev))) {
+ /*
+ * The link may be in a deep sleep, wake it up.
+ *
+ * If the link is in deep sleep, ata_phys_link_offline()
+ * will return true, causing the revalidation to fail,
+ * which leads to a (potentially) needless hard reset.
+ *
+ * ata_eh_recover() will later restore the link policy
+ * to ap->target_lpm_policy after revalidation is done.
+ */
+ if (link->lpm_policy > ATA_LPM_MAX_POWER) {
+ rc = ata_eh_link_set_lpm(link, ATA_LPM_MAX_POWER,
+ r_failed_dev);
+ if (rc)
+ goto err;
+ }
+
+ if (!ata_eh_link_established(ata_dev_phys_link(dev))) {
rc = -EIO;
goto err;
}
@@ -2959,7 +3331,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
ehc->i.flags |= ATA_EHI_SETMODE;
/* schedule the scsi_rescan_device() here */
- schedule_work(&(ap->scsi_rescan_task));
+ schedule_delayed_work(&ap->scsi_rescan_task, 0);
} else if (dev->class == ATA_DEV_UNKNOWN &&
ehc->tries[dev->devno] &&
ata_class_enabled(ehc->classes[dev->devno])) {
@@ -3038,17 +3410,18 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
return 0;
err:
+ dev->flags &= ~ATA_DFLAG_RESUMING;
*r_failed_dev = dev;
return rc;
}
/**
- * ata_set_mode - Program timings and issue SET FEATURES - XFER
+ * ata_eh_set_mode - Program timings and issue SET FEATURES - XFER
* @link: link on which timings will be programmed
* @r_failed_dev: out parameter for failed device
*
* Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
- * ata_set_mode() fails, pointer to the failing device is
+ * ata_eh_set_mode() fails, pointer to the failing device is
* returned in @r_failed_dev.
*
* LOCKING:
@@ -3057,7 +3430,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
* RETURNS:
* 0 on success, negative errno otherwise
*/
-int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
+static int ata_eh_set_mode(struct ata_link *link,
+ struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_device *dev;
@@ -3078,7 +3452,7 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
if (ap->ops->set_mode)
rc = ap->ops->set_mode(link, r_failed_dev);
else
- rc = ata_do_set_mode(link, r_failed_dev);
+ rc = ata_set_mode(link, r_failed_dev);
/* if transfer mode has changed, set DUBIOUS_XFER on device */
ata_for_each_dev(dev, link, ENABLED) {
@@ -3113,7 +3487,7 @@ static int atapi_eh_clear_ua(struct ata_device *dev)
int i;
for (i = 0; i < ATA_EH_UA_TRIES; i++) {
- u8 *sense_buffer = dev->link->ap->sector_buf;
+ u8 *sense_buffer = dev->sector_buf;
u8 sense_key = 0;
unsigned int err_mask;
@@ -3211,147 +3585,13 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev)
if (err_mask & AC_ERR_DEV) {
qc->err_mask |= AC_ERR_DEV;
qc->result_tf = tf;
- if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ if (!ata_port_is_frozen(ap))
rc = 0;
}
}
return rc;
}
-/**
- * ata_eh_set_lpm - configure SATA interface power management
- * @link: link to configure power management
- * @policy: the link power management policy
- * @r_failed_dev: out parameter for failed device
- *
- * Enable SATA Interface power management. This will enable
- * Device Interface Power Management (DIPM) for min_power and
- * medium_power_with_dipm policies, and then call driver specific
- * callbacks for enabling Host Initiated Power management.
- *
- * LOCKING:
- * EH context.
- *
- * RETURNS:
- * 0 on success, -errno on failure.
- */
-static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- struct ata_device **r_failed_dev)
-{
- struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
- struct ata_eh_context *ehc = &link->eh_context;
- struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
- enum ata_lpm_policy old_policy = link->lpm_policy;
- bool no_dipm = link->ap->flags & ATA_FLAG_NO_DIPM;
- unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
- unsigned int err_mask;
- int rc;
-
- /* if the link or host doesn't do LPM, noop */
- if (!IS_ENABLED(CONFIG_SATA_HOST) ||
- (link->flags & ATA_LFLAG_NO_LPM) || (ap && !ap->ops->set_lpm))
- return 0;
-
- /*
- * DIPM is enabled only for MIN_POWER as some devices
- * misbehave when the host NACKs transition to SLUMBER. Order
- * device and link configurations such that the host always
- * allows DIPM requests.
- */
- ata_for_each_dev(dev, link, ENABLED) {
- bool hipm = ata_id_has_hipm(dev->id);
- bool dipm = ata_id_has_dipm(dev->id) && !no_dipm;
-
- /* find the first enabled and LPM enabled devices */
- if (!link_dev)
- link_dev = dev;
-
- if (!lpm_dev && (hipm || dipm))
- lpm_dev = dev;
-
- hints &= ~ATA_LPM_EMPTY;
- if (!hipm)
- hints &= ~ATA_LPM_HIPM;
-
- /* disable DIPM before changing link config */
- if (policy < ATA_LPM_MED_POWER_WITH_DIPM && dipm) {
- err_mask = ata_dev_set_feature(dev,
- SETFEATURES_SATA_DISABLE, SATA_DIPM);
- if (err_mask && err_mask != AC_ERR_DEV) {
- ata_dev_warn(dev,
- "failed to disable DIPM, Emask 0x%x\n",
- err_mask);
- rc = -EIO;
- goto fail;
- }
- }
- }
-
- if (ap) {
- rc = ap->ops->set_lpm(link, policy, hints);
- if (!rc && ap->slave_link)
- rc = ap->ops->set_lpm(ap->slave_link, policy, hints);
- } else
- rc = sata_pmp_set_lpm(link, policy, hints);
-
- /*
- * Attribute link config failure to the first (LPM) enabled
- * device on the link.
- */
- if (rc) {
- if (rc == -EOPNOTSUPP) {
- link->flags |= ATA_LFLAG_NO_LPM;
- return 0;
- }
- dev = lpm_dev ? lpm_dev : link_dev;
- goto fail;
- }
-
- /*
- * Low level driver acked the transition. Issue DIPM command
- * with the new policy set.
- */
- link->lpm_policy = policy;
- if (ap && ap->slave_link)
- ap->slave_link->lpm_policy = policy;
-
- /* host config updated, enable DIPM if transitioning to MIN_POWER */
- ata_for_each_dev(dev, link, ENABLED) {
- if (policy >= ATA_LPM_MED_POWER_WITH_DIPM && !no_dipm &&
- ata_id_has_dipm(dev->id)) {
- err_mask = ata_dev_set_feature(dev,
- SETFEATURES_SATA_ENABLE, SATA_DIPM);
- if (err_mask && err_mask != AC_ERR_DEV) {
- ata_dev_warn(dev,
- "failed to enable DIPM, Emask 0x%x\n",
- err_mask);
- rc = -EIO;
- goto fail;
- }
- }
- }
-
- link->last_lpm_change = jiffies;
- link->flags |= ATA_LFLAG_CHANGED;
-
- return 0;
-
-fail:
- /* restore the old policy */
- link->lpm_policy = old_policy;
- if (ap && ap->slave_link)
- ap->slave_link->lpm_policy = old_policy;
-
- /* if no device or only one more chance is left, disable LPM */
- if (!dev || ehc->tries[dev->devno] <= 2) {
- ata_link_warn(link, "disabling LPM on the link\n");
- link->flags |= ATA_LFLAG_NO_LPM;
- }
- if (r_failed_dev)
- *r_failed_dev = dev;
- return rc;
-}
-
int ata_link_nr_enabled(struct ata_link *link)
{
struct ata_device *dev;
@@ -3388,7 +3628,7 @@ static int ata_eh_skip_recovery(struct ata_link *link)
return 1;
/* thaw frozen port and recover failed devices */
- if ((ap->pflags & ATA_PFLAG_FROZEN) || ata_link_nr_enabled(link))
+ if (ata_port_is_frozen(ap) || ata_link_nr_enabled(link))
return 0;
/* reset at least once if reset is requested */
@@ -3524,10 +3764,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
/**
* ata_eh_recover - recover host port after error
* @ap: host port to recover
- * @prereset: prereset method (can be NULL)
- * @softreset: softreset method (can be NULL)
- * @hardreset: hardreset method (can be NULL)
- * @postreset: postreset method (can be NULL)
+ * @reset_ops: The set of reset operations to use
* @r_failed_link: out parameter for failed link
*
* This is the alpha and omega, eum and yang, heart and soul of
@@ -3543,9 +3780,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
* RETURNS:
* 0 on success, -errno on failure.
*/
-int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
- ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
- ata_postreset_fn_t postreset,
+int ata_eh_recover(struct ata_port *ap, struct ata_reset_operations *reset_ops,
struct ata_link **r_failed_link)
{
struct ata_link *link;
@@ -3613,8 +3848,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
if (!(ehc->i.action & ATA_EH_RESET))
continue;
- rc = ata_eh_reset(link, ata_link_nr_vacant(link),
- prereset, softreset, hardreset, postreset);
+ rc = ata_eh_reset(link, ata_link_nr_vacant(link), reset_ops);
if (rc) {
ata_link_err(link, "reset failed, giving up\n");
goto out;
@@ -3695,7 +3929,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
/* configure transfer mode if necessary */
if (ehc->i.flags & ATA_EHI_SETMODE) {
- rc = ata_set_mode(link, &dev);
+ rc = ata_eh_set_mode(link, &dev);
if (rc)
goto rest_fail;
ehc->i.flags &= ~ATA_EHI_SETMODE;
@@ -3716,6 +3950,17 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
}
}
+ /*
+ * Make sure to transition devices to the active power mode
+ * if needed (e.g. if we were scheduled on system resume).
+ */
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (ehc->i.dev_action[dev->devno] & ATA_EH_SET_ACTIVE) {
+ ata_dev_power_set_active(dev);
+ ata_eh_done(link, dev, ATA_EH_SET_ACTIVE);
+ }
+ }
+
/* retry flush if necessary */
ata_for_each_dev(dev, link, ALL) {
if (dev->class != ATA_DEV_ATA &&
@@ -3729,7 +3974,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
config_lpm:
/* configure link power saving */
if (link->lpm_policy != ap->target_lpm_policy) {
- rc = ata_eh_set_lpm(link, ap->target_lpm_policy, &dev);
+ rc = ata_eh_link_set_lpm(link, ap->target_lpm_policy,
+ &dev);
if (rc)
goto rest_fail;
}
@@ -3743,7 +3989,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
if (dev)
ata_eh_handle_dev_fail(dev, rc);
- if (ap->pflags & ATA_PFLAG_FROZEN) {
+ if (ata_port_is_frozen(ap)) {
/* PMP reset requires working host port.
* Can't retry if it's frozen.
*/
@@ -3780,7 +4026,7 @@ void ata_eh_finish(struct ata_port *ap)
/* retry or finish qcs */
ata_qc_for_each_raw(ap, qc, tag) {
- if (!(qc->flags & ATA_QCFLAG_FAILED))
+ if (!(qc->flags & ATA_QCFLAG_EH))
continue;
if (qc->err_mask) {
@@ -3788,16 +4034,30 @@ void ata_eh_finish(struct ata_port *ap)
* generate sense data in this function,
* considering both err_mask and tf.
*/
- if (qc->flags & ATA_QCFLAG_RETRY)
+ if (qc->flags & ATA_QCFLAG_RETRY) {
+ /*
+ * Since qc->err_mask is set, ata_eh_qc_retry()
+ * will not increment scmd->allowed, so upper
+ * layer will only retry the command if it has
+ * not already been retried too many times.
+ */
ata_eh_qc_retry(qc);
- else
+ } else {
ata_eh_qc_complete(qc);
+ }
} else {
- if (qc->flags & ATA_QCFLAG_SENSE_VALID) {
+ if (qc->flags & ATA_QCFLAG_SENSE_VALID ||
+ qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD) {
ata_eh_qc_complete(qc);
} else {
/* feed zero TF to sense generation */
memset(&qc->result_tf, 0, sizeof(qc->result_tf));
+ /*
+ * Since qc->err_mask is not set,
+ * ata_eh_qc_retry() will increment
+ * scmd->allowed, so upper layer is guaranteed
+ * to retry the command.
+ */
ata_eh_qc_retry(qc);
}
}
@@ -3809,59 +4069,39 @@ void ata_eh_finish(struct ata_port *ap)
}
/**
- * ata_do_eh - do standard error handling
+ * ata_std_error_handler - standard error handler
* @ap: host port to handle error for
*
- * @prereset: prereset method (can be NULL)
- * @softreset: softreset method (can be NULL)
- * @hardreset: hardreset method (can be NULL)
- * @postreset: postreset method (can be NULL)
- *
* Perform standard error handling sequence.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
-void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
- ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
- ata_postreset_fn_t postreset)
+void ata_std_error_handler(struct ata_port *ap)
{
- struct ata_device *dev;
+ struct ata_reset_operations *reset_ops = &ap->ops->reset;
+ struct ata_link *link = &ap->link;
int rc;
+ /* Ignore built-in hardresets if SCR access is not available */
+ if ((reset_ops->hardreset == sata_std_hardreset ||
+ reset_ops->hardreset == sata_sff_hardreset) &&
+ !sata_scr_valid(link))
+ link->flags |= ATA_LFLAG_NO_HRST;
+
ata_eh_autopsy(ap);
ata_eh_report(ap);
- rc = ata_eh_recover(ap, prereset, softreset, hardreset, postreset,
- NULL);
+ rc = ata_eh_recover(ap, reset_ops, NULL);
if (rc) {
- ata_for_each_dev(dev, &ap->link, ALL)
+ struct ata_device *dev;
+
+ ata_for_each_dev(dev, link, ALL)
ata_dev_disable(dev);
}
ata_eh_finish(ap);
}
-
-/**
- * ata_std_error_handler - standard error handler
- * @ap: host port to handle error for
- *
- * Standard error handler
- *
- * LOCKING:
- * Kernel thread context (may sleep).
- */
-void ata_std_error_handler(struct ata_port *ap)
-{
- struct ata_port_operations *ops = ap->ops;
- ata_reset_fn_t hardreset = ops->hardreset;
-
- /* ignore built-in hardreset if SCR access is not available */
- if (hardreset == sata_std_hardreset && !sata_scr_valid(&ap->link))
- hardreset = NULL;
-
- ata_do_eh(ap, ops->prereset, ops->softreset, hardreset, ops->postreset);
-}
EXPORT_SYMBOL_GPL(ata_std_error_handler);
#ifdef CONFIG_PM
@@ -3879,6 +4119,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)
unsigned long flags;
int rc = 0;
struct ata_device *dev;
+ struct ata_link *link;
/* are we suspending? */
spin_lock_irqsave(ap->lock, flags);
@@ -3892,6 +4133,22 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)
WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED);
/*
+ * We will reach this point for all of the PM events:
+ * PM_EVENT_SUSPEND (if runtime pm, PM_EVENT_AUTO will also be set)
+ * PM_EVENT_FREEZE, and PM_EVENT_HIBERNATE.
+ *
+ * We do not want to perform disk spin down for PM_EVENT_FREEZE.
+ * (Spin down will be performed by the subsequent PM_EVENT_HIBERNATE.)
+ */
+ if (!(ap->pm_mesg.event & PM_EVENT_FREEZE)) {
+ /* Set all devices attached to the port in standby mode */
+ ata_for_each_link(link, ap, HOST_FIRST) {
+ ata_for_each_dev(dev, link, ENABLED)
+ ata_dev_power_set_standby(dev);
+ }
+ }
+
+ /*
* If we have a ZPODD attached, check its zero
* power ready status before the port is frozen.
* Only needed for runtime suspend.
@@ -3917,7 +4174,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)
ap->pflags &= ~ATA_PFLAG_PM_PENDING;
if (rc == 0)
ap->pflags |= ATA_PFLAG_SUSPENDED;
- else if (ap->pflags & ATA_PFLAG_FROZEN)
+ else if (ata_port_is_frozen(ap))
ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -3973,6 +4230,7 @@ static void ata_eh_handle_port_resume(struct ata_port *ap)
/* update the flags */
spin_lock_irqsave(ap->lock, flags);
ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
+ ap->pflags |= ATA_PFLAG_RESUMING;
spin_unlock_irqrestore(ap->lock, flags);
}
#endif /* CONFIG_PM */
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index e2e9cbd405fa..57023324a56f 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -15,9 +15,9 @@
const struct ata_port_operations sata_pmp_port_ops = {
.inherits = &sata_port_ops,
- .pmp_prereset = ata_std_prereset,
- .pmp_hardreset = sata_std_hardreset,
- .pmp_postreset = ata_std_postreset,
+ .pmp_reset.prereset = ata_std_prereset,
+ .pmp_reset.hardreset = sata_std_hardreset,
+ .pmp_reset.postreset = ata_std_postreset,
.error_handler = sata_pmp_error_handler,
};
@@ -648,8 +648,7 @@ static int sata_pmp_same_pmp(struct ata_device *dev, const u32 *new_gscr)
static int sata_pmp_revalidate(struct ata_device *dev, unsigned int new_class)
{
struct ata_link *link = dev->link;
- struct ata_port *ap = link->ap;
- u32 *gscr = (void *)ap->sector_buf;
+ u32 *gscr = (void *)dev->sector_buf;
int rc;
ata_eh_about_to_do(link, NULL, ATA_EH_REVALIDATE);
@@ -728,10 +727,7 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev)
/**
* sata_pmp_eh_recover_pmp - recover PMP
* @ap: ATA port PMP is attached to
- * @prereset: prereset method (can be NULL)
- * @softreset: softreset method
- * @hardreset: hardreset method
- * @postreset: postreset method (can be NULL)
+ * @reset_ops: The set of reset operations to use
*
* Recover PMP attached to @ap. Recovery procedure is somewhat
* similar to that of ata_eh_recover() except that reset should
@@ -745,8 +741,7 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev)
* 0 on success, -errno on failure.
*/
static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
- ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
- ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+ struct ata_reset_operations *reset_ops)
{
struct ata_link *link = &ap->link;
struct ata_eh_context *ehc = &link->eh_context;
@@ -768,8 +763,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
struct ata_link *tlink;
/* reset */
- rc = ata_eh_reset(link, 0, prereset, softreset, hardreset,
- postreset);
+ rc = ata_eh_reset(link, 0, reset_ops);
if (rc) {
ata_link_err(link, "failed to reset PMP, giving up\n");
goto fail;
@@ -933,8 +927,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
retry:
/* PMP attached? */
if (!sata_pmp_attached(ap)) {
- rc = ata_eh_recover(ap, ops->prereset, ops->softreset,
- ops->hardreset, ops->postreset, NULL);
+ rc = ata_eh_recover(ap, &ops->reset, NULL);
if (rc) {
ata_for_each_dev(dev, &ap->link, ALL)
ata_dev_disable(dev);
@@ -952,8 +945,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
}
/* recover pmp */
- rc = sata_pmp_eh_recover_pmp(ap, ops->prereset, ops->softreset,
- ops->hardreset, ops->postreset);
+ rc = sata_pmp_eh_recover_pmp(ap, &ops->reset);
if (rc)
goto pmp_fail;
@@ -979,8 +971,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
goto pmp_fail;
/* recover links */
- rc = ata_eh_recover(ap, ops->pmp_prereset, ops->pmp_softreset,
- ops->pmp_hardreset, ops->pmp_postreset, &link);
+ rc = ata_eh_recover(ap, &ops->pmp_reset, &link);
if (rc)
goto link_fail;
diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c
index 7a5fe41aa5ae..b2817a2995d6 100644
--- a/drivers/ata/libata-sata.c
+++ b/drivers/ata/libata-sata.c
@@ -11,17 +11,19 @@
#include <linux/module.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <linux/libata.h>
+#include <linux/unaligned.h>
#include "libata.h"
#include "libata-transport.h"
/* debounce timing parameters in msecs { interval, duration, timeout } */
-const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };
+const unsigned int sata_deb_timing_normal[] = { 5, 100, 2000 };
EXPORT_SYMBOL_GPL(sata_deb_timing_normal);
-const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 };
+const unsigned int sata_deb_timing_hotplug[] = { 25, 500, 2000 };
EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
-const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 };
+const unsigned int sata_deb_timing_long[] = { 100, 2000, 5000 };
EXPORT_SYMBOL_GPL(sata_deb_timing_long);
/**
@@ -230,11 +232,11 @@ EXPORT_SYMBOL_GPL(ata_tf_from_fis);
* RETURNS:
* 0 on success, -errno on failure.
*/
-int sata_link_debounce(struct ata_link *link, const unsigned long *params,
+int sata_link_debounce(struct ata_link *link, const unsigned int *params,
unsigned long deadline)
{
- unsigned long interval = params[0];
- unsigned long duration = params[1];
+ unsigned int interval = params[0];
+ unsigned int duration = params[1];
unsigned long last_jiffies, t;
u32 last, cur;
int rc;
@@ -293,7 +295,7 @@ EXPORT_SYMBOL_GPL(sata_link_debounce);
* RETURNS:
* 0 on success, -errno on failure.
*/
-int sata_link_resume(struct ata_link *link, const unsigned long *params,
+int sata_link_resume(struct ata_link *link, const unsigned int *params,
unsigned long deadline)
{
int tries = ATA_LINK_RESUME_TRIES;
@@ -394,10 +396,23 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
case ATA_LPM_MED_POWER_WITH_DIPM:
case ATA_LPM_MIN_POWER_WITH_PARTIAL:
case ATA_LPM_MIN_POWER:
- if (ata_link_nr_enabled(link) > 0)
- /* no restrictions on LPM transitions */
+ if (ata_link_nr_enabled(link) > 0) {
+ /* assume no restrictions on LPM transitions */
scontrol &= ~(0x7 << 8);
- else {
+
+ /*
+ * If the controller does not support partial, slumber,
+ * or devsleep, then disallow these transitions.
+ */
+ if (link->ap->host->flags & ATA_HOST_NO_PART)
+ scontrol |= (0x1 << 8);
+
+ if (link->ap->host->flags & ATA_HOST_NO_SSC)
+ scontrol |= (0x2 << 8);
+
+ if (link->ap->host->flags & ATA_HOST_NO_DEVSLP)
+ scontrol |= (0x4 << 8);
+ } else {
/* empty port, power off */
scontrol &= ~0xf;
scontrol |= (0x1 << 2);
@@ -503,6 +518,86 @@ int sata_set_spd(struct ata_link *link)
EXPORT_SYMBOL_GPL(sata_set_spd);
/**
+ * sata_down_spd_limit - adjust SATA spd limit downward
+ * @link: Link to adjust SATA spd limit for
+ * @spd_limit: Additional limit
+ *
+ * Adjust SATA spd limit of @link downward. Note that this
+ * function only adjusts the limit. The change must be applied
+ * using sata_set_spd().
+ *
+ * If @spd_limit is non-zero, the speed is limited to equal to or
+ * lower than @spd_limit if such speed is supported. If
+ * @spd_limit is slower than any supported speed, only the lowest
+ * supported speed is allowed.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ *
+ * RETURNS:
+ * 0 on success, negative errno on failure
+ */
+int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
+{
+ u32 sstatus, spd, mask;
+ int rc, bit;
+
+ if (!sata_scr_valid(link))
+ return -EOPNOTSUPP;
+
+ /* If SCR can be read, use it to determine the current SPD.
+ * If not, use cached value in link->sata_spd.
+ */
+ rc = sata_scr_read(link, SCR_STATUS, &sstatus);
+ if (rc == 0 && ata_sstatus_online(sstatus))
+ spd = (sstatus >> 4) & 0xf;
+ else
+ spd = link->sata_spd;
+
+ mask = link->sata_spd_limit;
+ if (mask <= 1)
+ return -EINVAL;
+
+ /* unconditionally mask off the highest bit */
+ bit = fls(mask) - 1;
+ mask &= ~(1 << bit);
+
+ /*
+ * Mask off all speeds higher than or equal to the current one. At
+ * this point, if current SPD is not available and we previously
+ * recorded the link speed from SStatus, the driver has already
+ * masked off the highest bit so mask should already be 1 or 0.
+ * Otherwise, we should not force 1.5Gbps on a link where we have
+ * not previously recorded speed from SStatus. Just return in this
+ * case.
+ */
+ if (spd > 1)
+ mask &= (1 << (spd - 1)) - 1;
+ else if (link->sata_spd)
+ return -EINVAL;
+
+ /* were we already at the bottom? */
+ if (!mask)
+ return -EINVAL;
+
+ if (spd_limit) {
+ if (mask & ((1 << spd_limit) - 1))
+ mask &= (1 << spd_limit) - 1;
+ else {
+ bit = ffs(mask) - 1;
+ mask = 1 << bit;
+ }
+ }
+
+ link->sata_spd_limit = mask;
+
+ ata_link_warn(link, "limiting SATA link speed to %s\n",
+ sata_spd_string(fls(mask)));
+
+ return 0;
+}
+
+/**
* sata_link_hardreset - reset link via SATA phy reset
* @link: link to reset
* @timing: timing parameters { interval, duration, timeout } in msec
@@ -526,7 +621,7 @@ EXPORT_SYMBOL_GPL(sata_set_spd);
* RETURNS:
* 0 on success, -errno otherwise.
*/
-int sata_link_hardreset(struct ata_link *link, const unsigned long *timing,
+int sata_link_hardreset(struct ata_link *link, const unsigned int *timing,
unsigned long deadline,
bool *online, int (*check_ready)(struct ata_link *))
{
@@ -606,13 +701,40 @@ int sata_link_hardreset(struct ata_link *link, const unsigned long *timing,
/* online is set iff link is online && reset succeeded */
if (online)
*online = false;
- ata_link_err(link, "COMRESET failed (errno=%d)\n", rc);
}
return rc;
}
EXPORT_SYMBOL_GPL(sata_link_hardreset);
/**
+ * sata_std_hardreset - COMRESET w/o waiting or classification
+ * @link: link to reset
+ * @class: resulting class of attached device
+ * @deadline: deadline jiffies for the operation
+ *
+ * Standard SATA COMRESET w/o waiting or classification.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep)
+ *
+ * RETURNS:
+ * 0 if link offline, -EAGAIN if link online, -errno on errors.
+ */
+int sata_std_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
+ bool online;
+ int rc;
+
+ rc = sata_link_hardreset(link, timing, deadline, &online, NULL);
+ if (online)
+ return -EAGAIN;
+ return rc;
+}
+EXPORT_SYMBOL_GPL(sata_std_hardreset);
+
+/**
* ata_qc_complete_multiple - Complete multiple qcs successfully
* @ap: port in question
* @qc_active: new qc_active mask
@@ -655,6 +777,9 @@ int ata_qc_complete_multiple(struct ata_port *ap, u64 qc_active)
return -EINVAL;
}
+ if (ap->ops->qc_ncq_fill_rtf)
+ ap->ops->qc_ncq_fill_rtf(ap, done_mask);
+
while (done_mask) {
struct ata_queued_cmd *qc;
unsigned int tag = __ffs64(done_mask);
@@ -767,7 +892,7 @@ bool sata_lpm_ignore_phy_events(struct ata_link *link)
EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events);
static const char *ata_lpm_policy_names[] = {
- [ATA_LPM_UNKNOWN] = "max_performance",
+ [ATA_LPM_UNKNOWN] = "keep_firmware_settings",
[ATA_LPM_MAX_POWER] = "max_performance",
[ATA_LPM_MED_POWER] = "medium_power",
[ATA_LPM_MED_POWER_WITH_DIPM] = "med_power_with_dipm",
@@ -775,14 +900,52 @@ static const char *ata_lpm_policy_names[] = {
[ATA_LPM_MIN_POWER] = "min_power",
};
+/*
+ * Check if a port supports link power management.
+ * Must be called with the port locked.
+ */
+static bool ata_scsi_lpm_supported(struct ata_port *ap)
+{
+ struct ata_link *link;
+ struct ata_device *dev;
+
+ if (ap->flags & ATA_FLAG_NO_LPM)
+ return false;
+
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
+ if (dev->quirks & ATA_QUIRK_NOLPM)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static ssize_t ata_scsi_lpm_supported_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ata_port *ap = ata_shost_to_port(shost);
+ unsigned long flags;
+ bool supported;
+
+ spin_lock_irqsave(ap->lock, flags);
+ supported = ata_scsi_lpm_supported(ap);
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ return sysfs_emit(buf, "%d\n", supported);
+}
+DEVICE_ATTR(link_power_management_supported, S_IRUGO,
+ ata_scsi_lpm_supported_show, NULL);
+EXPORT_SYMBOL_GPL(dev_attr_link_power_management_supported);
+
static ssize_t ata_scsi_lpm_store(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(device);
struct ata_port *ap = ata_shost_to_port(shost);
- struct ata_link *link;
- struct ata_device *dev;
enum ata_lpm_policy policy;
unsigned long flags;
@@ -799,13 +962,9 @@ static ssize_t ata_scsi_lpm_store(struct device *device,
spin_lock_irqsave(ap->lock, flags);
- ata_for_each_link(link, ap, EDGE) {
- ata_for_each_dev(dev, &ap->link, ENABLED) {
- if (dev->horkage & ATA_HORKAGE_NOLPM) {
- count = -EOPNOTSUPP;
- goto out_unlock;
- }
- }
+ if (!ata_scsi_lpm_supported(ap)) {
+ count = -EOPNOTSUPP;
+ goto out_unlock;
}
ap->target_lpm_policy = policy;
@@ -831,88 +990,179 @@ DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR,
ata_scsi_lpm_show, ata_scsi_lpm_store);
EXPORT_SYMBOL_GPL(dev_attr_link_power_management_policy);
-static ssize_t ata_ncq_prio_supported_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
+/**
+ * ata_ncq_prio_supported - Check if device supports NCQ Priority
+ * @ap: ATA port of the target device
+ * @sdev: SCSI device
+ * @supported: Address of a boolean to store the result
+ *
+ * Helper to check if device supports NCQ Priority feature.
+ *
+ * Context: Any context. Takes and releases @ap->lock.
+ *
+ * Return:
+ * * %0 - OK. Status is stored into @supported
+ * * %-ENODEV - Failed to find the ATA device
+ */
+int ata_ncq_prio_supported(struct ata_port *ap, struct scsi_device *sdev,
+ bool *supported)
{
- struct scsi_device *sdev = to_scsi_device(device);
- struct ata_port *ap = ata_shost_to_port(sdev->host);
struct ata_device *dev;
- bool ncq_prio_supported;
+ unsigned long flags;
int rc = 0;
- spin_lock_irq(ap->lock);
+ spin_lock_irqsave(ap->lock, flags);
dev = ata_scsi_find_dev(ap, sdev);
if (!dev)
rc = -ENODEV;
else
- ncq_prio_supported = dev->flags & ATA_DFLAG_NCQ_PRIO;
- spin_unlock_irq(ap->lock);
+ *supported = dev->flags & ATA_DFLAG_NCQ_PRIO;
+ spin_unlock_irqrestore(ap->lock, flags);
- return rc ? rc : sysfs_emit(buf, "%u\n", ncq_prio_supported);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ata_ncq_prio_supported);
+
+static ssize_t ata_ncq_prio_supported_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(device);
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+ bool supported;
+ int rc;
+
+ rc = ata_ncq_prio_supported(ap, sdev, &supported);
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%d\n", supported);
}
DEVICE_ATTR(ncq_prio_supported, S_IRUGO, ata_ncq_prio_supported_show, NULL);
EXPORT_SYMBOL_GPL(dev_attr_ncq_prio_supported);
-static ssize_t ata_ncq_prio_enable_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
+/**
+ * ata_ncq_prio_enabled - Check if NCQ Priority is enabled
+ * @ap: ATA port of the target device
+ * @sdev: SCSI device
+ * @enabled: Address of a boolean to store the result
+ *
+ * Helper to check if NCQ Priority feature is enabled.
+ *
+ * Context: Any context. Takes and releases @ap->lock.
+ *
+ * Return:
+ * * %0 - OK. Status is stored into @enabled
+ * * %-ENODEV - Failed to find the ATA device
+ */
+int ata_ncq_prio_enabled(struct ata_port *ap, struct scsi_device *sdev,
+ bool *enabled)
{
- struct scsi_device *sdev = to_scsi_device(device);
- struct ata_port *ap = ata_shost_to_port(sdev->host);
struct ata_device *dev;
- bool ncq_prio_enable;
+ unsigned long flags;
int rc = 0;
- spin_lock_irq(ap->lock);
+ spin_lock_irqsave(ap->lock, flags);
dev = ata_scsi_find_dev(ap, sdev);
if (!dev)
rc = -ENODEV;
else
- ncq_prio_enable = dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE;
- spin_unlock_irq(ap->lock);
+ *enabled = dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED;
+ spin_unlock_irqrestore(ap->lock, flags);
- return rc ? rc : sysfs_emit(buf, "%u\n", ncq_prio_enable);
+ return rc;
}
+EXPORT_SYMBOL_GPL(ata_ncq_prio_enabled);
-static ssize_t ata_ncq_prio_enable_store(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t len)
+static ssize_t ata_ncq_prio_enable_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
{
struct scsi_device *sdev = to_scsi_device(device);
- struct ata_port *ap;
- struct ata_device *dev;
- long int input;
- int rc = 0;
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+ bool enabled;
+ int rc;
- rc = kstrtol(buf, 10, &input);
+ rc = ata_ncq_prio_enabled(ap, sdev, &enabled);
if (rc)
return rc;
- if ((input < 0) || (input > 1))
- return -EINVAL;
- ap = ata_shost_to_port(sdev->host);
- dev = ata_scsi_find_dev(ap, sdev);
- if (unlikely(!dev))
- return -ENODEV;
+ return sysfs_emit(buf, "%d\n", enabled);
+}
- spin_lock_irq(ap->lock);
+/**
+ * ata_ncq_prio_enable - Enable/disable NCQ Priority
+ * @ap: ATA port of the target device
+ * @sdev: SCSI device
+ * @enable: true - enable NCQ Priority, false - disable NCQ Priority
+ *
+ * Helper to enable/disable NCQ Priority feature.
+ *
+ * Context: Any context. Takes and releases @ap->lock.
+ *
+ * Return:
+ * * %0 - OK. Status is stored into @enabled
+ * * %-ENODEV - Failed to find the ATA device
+ * * %-EINVAL - NCQ Priority is not supported or CDL is enabled
+ */
+int ata_ncq_prio_enable(struct ata_port *ap, struct scsi_device *sdev,
+ bool enable)
+{
+ struct ata_device *dev;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(ap->lock, flags);
+
+ dev = ata_scsi_find_dev(ap, sdev);
+ if (!dev) {
+ rc = -ENODEV;
+ goto unlock;
+ }
if (!(dev->flags & ATA_DFLAG_NCQ_PRIO)) {
rc = -EINVAL;
goto unlock;
}
- if (input)
- dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLE;
- else
- dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE;
+ if (enable) {
+ if (dev->flags & ATA_DFLAG_CDL_ENABLED) {
+ ata_dev_err(dev,
+ "CDL must be disabled to enable NCQ priority\n");
+ rc = -EINVAL;
+ goto unlock;
+ }
+ dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLED;
+ } else {
+ dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLED;
+ }
unlock:
- spin_unlock_irq(ap->lock);
+ spin_unlock_irqrestore(ap->lock, flags);
- return rc ? rc : len;
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ata_ncq_prio_enable);
+
+static ssize_t ata_ncq_prio_enable_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct scsi_device *sdev = to_scsi_device(device);
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+ bool enable;
+ int rc;
+
+ rc = kstrtobool(buf, &enable);
+ if (rc)
+ return rc;
+
+ rc = ata_ncq_prio_enable(ap, sdev, enable);
+ if (rc)
+ return rc;
+
+ return len;
}
DEVICE_ATTR(ncq_prio_enable, S_IRUGO | S_IWUSR,
@@ -1018,48 +1268,60 @@ DEVICE_ATTR(sw_activity, S_IWUSR | S_IRUGO, ata_scsi_activity_show,
EXPORT_SYMBOL_GPL(dev_attr_sw_activity);
/**
- * __ata_change_queue_depth - helper for ata_scsi_change_queue_depth
- * @ap: ATA port to which the device change the queue depth
+ * ata_change_queue_depth - Set a device maximum queue depth
+ * @ap: ATA port of the target device
* @sdev: SCSI device to configure queue depth for
* @queue_depth: new queue depth
*
- * libsas and libata have different approaches for associating a sdev to
- * its ata_port.
+ * Helper to set a device maximum queue depth, usable with both libsas
+ * and libata.
*
*/
-int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev,
- int queue_depth)
+int ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev,
+ int queue_depth)
{
struct ata_device *dev;
unsigned long flags;
+ int max_queue_depth;
- if (queue_depth < 1 || queue_depth == sdev->queue_depth)
- return sdev->queue_depth;
+ spin_lock_irqsave(ap->lock, flags);
dev = ata_scsi_find_dev(ap, sdev);
- if (!dev || !ata_dev_enabled(dev))
+ if (!dev || queue_depth < 1 || queue_depth == sdev->queue_depth) {
+ spin_unlock_irqrestore(ap->lock, flags);
return sdev->queue_depth;
+ }
- /* NCQ enabled? */
- spin_lock_irqsave(ap->lock, flags);
- dev->flags &= ~ATA_DFLAG_NCQ_OFF;
- if (queue_depth == 1 || !ata_ncq_enabled(dev)) {
+ /*
+ * Make sure that the queue depth requested does not exceed the device
+ * capabilities.
+ */
+ max_queue_depth = min(ATA_MAX_QUEUE, sdev->host->can_queue);
+ max_queue_depth = min(max_queue_depth, ata_id_queue_depth(dev->id));
+ if (queue_depth > max_queue_depth) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ return -EINVAL;
+ }
+
+ /*
+ * If NCQ is not supported by the device or if the target queue depth
+ * is 1 (to disable drive side command queueing), turn off NCQ.
+ */
+ if (queue_depth == 1 || !ata_ncq_supported(dev)) {
dev->flags |= ATA_DFLAG_NCQ_OFF;
queue_depth = 1;
+ } else {
+ dev->flags &= ~ATA_DFLAG_NCQ_OFF;
}
- spin_unlock_irqrestore(ap->lock, flags);
- /* limit and apply queue depth */
- queue_depth = min(queue_depth, sdev->host->can_queue);
- queue_depth = min(queue_depth, ata_id_queue_depth(dev->id));
- queue_depth = min(queue_depth, ATA_MAX_QUEUE);
+ spin_unlock_irqrestore(ap->lock, flags);
- if (sdev->queue_depth == queue_depth)
- return -EINVAL;
+ if (queue_depth == sdev->queue_depth)
+ return sdev->queue_depth;
return scsi_change_queue_depth(sdev, queue_depth);
}
-EXPORT_SYMBOL_GPL(__ata_change_queue_depth);
+EXPORT_SYMBOL_GPL(ata_change_queue_depth);
/**
* ata_scsi_change_queue_depth - SCSI callback for queue depth config
@@ -1080,169 +1342,29 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
- return __ata_change_queue_depth(ap, sdev, queue_depth);
+ return ata_change_queue_depth(ap, sdev, queue_depth);
}
EXPORT_SYMBOL_GPL(ata_scsi_change_queue_depth);
/**
- * ata_sas_port_alloc - Allocate port for a SAS attached SATA device
- * @host: ATA host container for all SAS ports
- * @port_info: Information from low-level host driver
- * @shost: SCSI host that the scsi device is attached to
- *
- * LOCKING:
- * PCI/etc. bus probe sem.
- *
- * RETURNS:
- * ata_port pointer on success / NULL on failure.
- */
-
-struct ata_port *ata_sas_port_alloc(struct ata_host *host,
- struct ata_port_info *port_info,
- struct Scsi_Host *shost)
-{
- struct ata_port *ap;
-
- ap = ata_port_alloc(host);
- if (!ap)
- return NULL;
-
- ap->port_no = 0;
- ap->lock = &host->lock;
- ap->pio_mask = port_info->pio_mask;
- ap->mwdma_mask = port_info->mwdma_mask;
- ap->udma_mask = port_info->udma_mask;
- ap->flags |= port_info->flags;
- ap->ops = port_info->port_ops;
- ap->cbl = ATA_CBL_SATA;
-
- return ap;
-}
-EXPORT_SYMBOL_GPL(ata_sas_port_alloc);
-
-/**
- * ata_sas_port_start - Set port up for dma.
- * @ap: Port to initialize
- *
- * Called just after data structures for each port are
- * initialized.
- *
- * May be used as the port_start() entry in ata_port_operations.
- *
- * LOCKING:
- * Inherited from caller.
- */
-int ata_sas_port_start(struct ata_port *ap)
-{
- /*
- * the port is marked as frozen at allocation time, but if we don't
- * have new eh, we won't thaw it
- */
- if (!ap->ops->error_handler)
- ap->pflags &= ~ATA_PFLAG_FROZEN;
- return 0;
-}
-EXPORT_SYMBOL_GPL(ata_sas_port_start);
-
-/**
- * ata_sas_port_stop - Undo ata_sas_port_start()
- * @ap: Port to shut down
- *
- * May be used as the port_stop() entry in ata_port_operations.
- *
- * LOCKING:
- * Inherited from caller.
- */
-
-void ata_sas_port_stop(struct ata_port *ap)
-{
-}
-EXPORT_SYMBOL_GPL(ata_sas_port_stop);
-
-/**
- * ata_sas_async_probe - simply schedule probing and return
- * @ap: Port to probe
- *
- * For batch scheduling of probe for sas attached ata devices, assumes
- * the port has already been through ata_sas_port_init()
- */
-void ata_sas_async_probe(struct ata_port *ap)
-{
- __ata_port_probe(ap);
-}
-EXPORT_SYMBOL_GPL(ata_sas_async_probe);
-
-int ata_sas_sync_probe(struct ata_port *ap)
-{
- return ata_port_probe(ap);
-}
-EXPORT_SYMBOL_GPL(ata_sas_sync_probe);
-
-
-/**
- * ata_sas_port_init - Initialize a SATA device
- * @ap: SATA port to initialize
- *
- * LOCKING:
- * PCI/etc. bus probe sem.
- *
- * RETURNS:
- * Zero on success, non-zero on error.
- */
-
-int ata_sas_port_init(struct ata_port *ap)
-{
- int rc = ap->ops->port_start(ap);
-
- if (rc)
- return rc;
- ap->print_id = atomic_inc_return(&ata_print_id);
- return 0;
-}
-EXPORT_SYMBOL_GPL(ata_sas_port_init);
-
-int ata_sas_tport_add(struct device *parent, struct ata_port *ap)
-{
- return ata_tport_add(parent, ap);
-}
-EXPORT_SYMBOL_GPL(ata_sas_tport_add);
-
-void ata_sas_tport_delete(struct ata_port *ap)
-{
- ata_tport_delete(ap);
-}
-EXPORT_SYMBOL_GPL(ata_sas_tport_delete);
-
-/**
- * ata_sas_port_destroy - Destroy a SATA port allocated by ata_sas_port_alloc
- * @ap: SATA port to destroy
- *
- */
-
-void ata_sas_port_destroy(struct ata_port *ap)
-{
- if (ap->ops->port_stop)
- ap->ops->port_stop(ap);
- kfree(ap);
-}
-EXPORT_SYMBOL_GPL(ata_sas_port_destroy);
-
-/**
- * ata_sas_slave_configure - Default slave_config routine for libata devices
+ * ata_sas_sdev_configure - Default sdev_configure routine for libata
+ * devices
* @sdev: SCSI device to configure
+ * @lim: queue limits
* @ap: ATA port to which SCSI device is attached
*
* RETURNS:
* Zero.
*/
-int ata_sas_slave_configure(struct scsi_device *sdev, struct ata_port *ap)
+int ata_sas_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim,
+ struct ata_port *ap)
{
ata_scsi_sdev_config(sdev);
- ata_scsi_dev_config(sdev, ap->link.device);
- return 0;
+
+ return ata_scsi_dev_config(sdev, lim, ap->link.device);
}
-EXPORT_SYMBOL_GPL(ata_sas_slave_configure);
+EXPORT_SYMBOL_GPL(ata_sas_sdev_configure);
/**
* ata_sas_queuecmd - Issue SCSI cdb to libata-managed device
@@ -1360,7 +1482,7 @@ EXPORT_SYMBOL_GPL(sata_async_notification);
static int ata_eh_read_log_10h(struct ata_device *dev,
int *tag, struct ata_taskfile *tf)
{
- u8 *buf = dev->link->ap->sector_buf;
+ u8 *buf = dev->sector_buf;
unsigned int err_mask;
u8 csum;
int i;
@@ -1392,13 +1514,114 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
tf->hob_lbah = buf[10];
tf->nsect = buf[12];
tf->hob_nsect = buf[13];
- if (dev->class == ATA_DEV_ZAC && ata_id_has_ncq_autosense(dev->id))
+ if (ata_id_has_ncq_autosense(dev->id) && (tf->status & ATA_SENSE))
tf->auxiliary = buf[14] << 16 | buf[15] << 8 | buf[16];
return 0;
}
/**
+ * ata_eh_get_ncq_success_sense - Read and process the sense data for
+ * successful NCQ commands log page
+ * @link: ATA link to get sense data for
+ *
+ * Read the sense data for successful NCQ commands log page to obtain
+ * sense data for all NCQ commands that completed successfully with
+ * the sense data available bit set.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_eh_get_ncq_success_sense(struct ata_link *link)
+{
+ struct ata_device *dev = link->device;
+ struct ata_port *ap = dev->link->ap;
+ u8 *buf = dev->cdl->ncq_sense_log_buf;
+ struct ata_queued_cmd *qc;
+ unsigned int err_mask, tag;
+ u8 *sense, sk = 0, asc = 0, ascq = 0;
+ u16 extended_sense;
+ bool aux_icc_valid;
+ u32 sense_valid;
+ u64 val;
+ int ret = 0;
+
+ err_mask = ata_read_log_page(dev, ATA_LOG_SENSE_NCQ, 0, buf, 2);
+ if (err_mask) {
+ ata_dev_err(dev,
+ "Failed to read Sense Data for Successful NCQ Commands log\n");
+ return -EIO;
+ }
+
+ /* Check the log header */
+ val = get_unaligned_le64(&buf[0]);
+ if ((val & 0xffff) != 1 || ((val >> 16) & 0xff) != 0x0f) {
+ ata_dev_err(dev,
+ "Invalid Sense Data for Successful NCQ Commands log\n");
+ return -EIO;
+ }
+
+ sense_valid = get_unaligned_le32(&buf[8]);
+ extended_sense = get_unaligned_le16(&buf[14]);
+ aux_icc_valid = extended_sense & BIT(15);
+
+ ata_qc_for_each_raw(ap, qc, tag) {
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
+ !(qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD) ||
+ qc->err_mask ||
+ ata_dev_phys_link(qc->dev) != link)
+ continue;
+
+ /*
+ * If the command does not have any sense data, clear ATA_SENSE.
+ * Keep ATA_QCFLAG_EH_SUCCESS_CMD so that command is finished.
+ */
+ if (!(sense_valid & BIT(tag))) {
+ qc->result_tf.status &= ~ATA_SENSE;
+ continue;
+ }
+
+ sense = &buf[32 + 24 * tag];
+ sk = sense[0];
+ asc = sense[1];
+ ascq = sense[2];
+
+ if (!ata_scsi_sense_is_valid(sk, asc, ascq)) {
+ ret = -EIO;
+ continue;
+ }
+
+ qc->result_tf.nsect = sense[6];
+ qc->result_tf.hob_nsect = sense[7];
+ qc->result_tf.lbal = sense[8];
+ qc->result_tf.lbam = sense[9];
+ qc->result_tf.lbah = sense[10];
+ qc->result_tf.hob_lbal = sense[11];
+ qc->result_tf.hob_lbam = sense[12];
+ qc->result_tf.hob_lbah = sense[13];
+ if (aux_icc_valid)
+ qc->result_tf.auxiliary = get_unaligned_le32(&sense[16]);
+
+ /* Set sense without also setting scsicmd->result */
+ scsi_build_sense_buffer(dev->flags & ATA_DFLAG_D_SENSE,
+ qc->scsicmd->sense_buffer, sk,
+ asc, ascq);
+ qc->flags |= ATA_QCFLAG_SENSE_VALID;
+
+ /*
+ * No point in checking the return value, since the command has
+ * already completed successfully.
+ */
+ ata_eh_decide_disposition(qc);
+ }
+
+ return ret;
+}
+
+/**
* ata_eh_analyze_ncq_error - analyze NCQ error
* @link: ATA link to analyze NCQ error for
*
@@ -1420,7 +1643,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
int tag, rc;
/* if frozen, we can't do much */
- if (ap->pflags & ATA_PFLAG_FROZEN)
+ if (ata_port_is_frozen(ap))
return;
/* is it NCQ device error? */
@@ -1429,7 +1652,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
/* has LLDD analyzed already? */
ata_qc_for_each_raw(ap, qc, tag) {
- if (!(qc->flags & ATA_QCFLAG_FAILED))
+ if (!(qc->flags & ATA_QCFLAG_EH))
continue;
if (qc->err_mask)
@@ -1445,7 +1668,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
return;
}
- if (!(link->sactive & (1 << tag))) {
+ if (!(link->sactive & BIT(tag))) {
ata_link_err(link, "log page 10h reported inactive tag %d\n",
tag);
return;
@@ -1456,19 +1679,60 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
memcpy(&qc->result_tf, &tf, sizeof(tf));
qc->result_tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ;
- if (dev->class == ATA_DEV_ZAC &&
- ((qc->result_tf.status & ATA_SENSE) || qc->result_tf.auxiliary)) {
+
+ /*
+ * If the device supports NCQ autosense, ata_eh_read_log_10h() will have
+ * stored the sense data in qc->result_tf.auxiliary.
+ */
+ if (qc->result_tf.auxiliary) {
char sense_key, asc, ascq;
sense_key = (qc->result_tf.auxiliary >> 16) & 0xff;
asc = (qc->result_tf.auxiliary >> 8) & 0xff;
ascq = qc->result_tf.auxiliary & 0xff;
- ata_scsi_set_sense(dev, qc->scsicmd, sense_key, asc, ascq);
- ata_scsi_set_sense_information(dev, qc->scsicmd,
- &qc->result_tf);
- qc->flags |= ATA_QCFLAG_SENSE_VALID;
+ if (ata_scsi_sense_is_valid(sense_key, asc, ascq)) {
+ ata_scsi_set_sense(dev, qc->scsicmd, sense_key, asc,
+ ascq);
+ qc->flags |= ATA_QCFLAG_SENSE_VALID;
+ }
+ }
+
+ ata_qc_for_each_raw(ap, qc, tag) {
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
+ qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD ||
+ ata_dev_phys_link(qc->dev) != link)
+ continue;
+
+ /* Skip the single QC which caused the NCQ error. */
+ if (qc->err_mask)
+ continue;
+
+ /*
+ * For SATA, the STATUS and ERROR fields are shared for all NCQ
+ * commands that were completed with the same SDB FIS.
+ * Therefore, we have to clear the ATA_ERR bit for all QCs
+ * except the one that caused the NCQ error.
+ */
+ qc->result_tf.status &= ~ATA_ERR;
+ qc->result_tf.error = 0;
+
+ /*
+ * If we get a NCQ error, that means that a single command was
+ * aborted. All other failed commands for our link should be
+ * retried and has no business of going though further scrutiny
+ * by ata_eh_link_autopsy().
+ */
+ qc->flags |= ATA_QCFLAG_RETRY;
}
ehc->i.err_mask &= ~AC_ERR_DEV;
}
EXPORT_SYMBOL_GPL(ata_eh_analyze_ncq_error);
+
+const struct ata_port_operations sata_port_ops = {
+ .inherits = &ata_base_port_ops,
+
+ .qc_defer = ata_std_qc_defer,
+ .reset.hardreset = sata_std_hardreset,
+};
+EXPORT_SYMBOL_GPL(sata_port_ops);
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 29e2f55c6faa..721d3f270c8e 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -30,14 +30,14 @@
#include <linux/hdreg.h>
#include <linux/uaccess.h>
#include <linux/suspend.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/ioprio.h>
#include <linux/of.h>
#include "libata.h"
#include "libata-transport.h"
-#define ATA_SCSI_RBUF_SIZE 576
+#define ATA_SCSI_RBUF_SIZE 2048
static DEFINE_SPINLOCK(ata_scsi_rbuf_lock);
static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE];
@@ -47,15 +47,19 @@ typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc);
static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev);
-#define RW_RECOVERY_MPAGE 0x1
-#define RW_RECOVERY_MPAGE_LEN 12
-#define CACHE_MPAGE 0x8
-#define CACHE_MPAGE_LEN 20
-#define CONTROL_MPAGE 0xa
-#define CONTROL_MPAGE_LEN 12
-#define ALL_MPAGES 0x3f
-#define ALL_SUB_MPAGES 0xff
-
+#define RW_RECOVERY_MPAGE 0x1
+#define RW_RECOVERY_MPAGE_LEN 12
+#define CACHE_MPAGE 0x8
+#define CACHE_MPAGE_LEN 20
+#define CONTROL_MPAGE 0xa
+#define CONTROL_MPAGE_LEN 12
+#define ALL_MPAGES 0x3f
+#define ALL_SUB_MPAGES 0xff
+#define CDL_T2A_SUB_MPAGE 0x07
+#define CDL_T2B_SUB_MPAGE 0x08
+#define CDL_T2_SUB_MPAGE_LEN 232
+#define ATA_FEATURE_SUB_MPAGE 0xf2
+#define ATA_FEATURE_SUB_MPAGE_LEN 16
static const u8 def_rw_recovery_mpage[RW_RECOVERY_MPAGE_LEN] = {
RW_RECOVERY_MPAGE,
@@ -131,11 +135,11 @@ static ssize_t ata_scsi_park_store(struct device *device,
struct scsi_device *sdev = to_scsi_device(device);
struct ata_port *ap;
struct ata_device *dev;
- long int input;
+ int input;
unsigned long flags;
int rc;
- rc = kstrtol(buf, 10, &input);
+ rc = kstrtoint(buf, 10, &input);
if (rc)
return rc;
if (input < -2)
@@ -188,34 +192,129 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR,
ata_scsi_park_show, ata_scsi_park_store);
EXPORT_SYMBOL_GPL(dev_attr_unload_heads);
+bool ata_scsi_sense_is_valid(u8 sk, u8 asc, u8 ascq)
+{
+ /*
+ * If sk == NO_SENSE, and asc + ascq == NO ADDITIONAL SENSE INFORMATION,
+ * then there is no sense data to add.
+ */
+ if (sk == 0 && asc == 0 && ascq == 0)
+ return false;
+
+ /* If sk > COMPLETED, sense data is bogus. */
+ if (sk > COMPLETED)
+ return false;
+
+ return true;
+}
+
void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd,
u8 sk, u8 asc, u8 ascq)
{
bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE);
- if (!cmd)
- return;
-
scsi_build_sense(cmd, d_sense, sk, asc, ascq);
}
-void ata_scsi_set_sense_information(struct ata_device *dev,
- struct scsi_cmnd *cmd,
- const struct ata_taskfile *tf)
+static void ata_scsi_set_sense_information(struct ata_queued_cmd *qc)
{
u64 information;
- if (!cmd)
+ if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) {
+ ata_dev_dbg(qc->dev,
+ "missing result TF: can't set INFORMATION sense field\n");
return;
+ }
- information = ata_tf_read_block(tf, dev);
+ information = ata_tf_read_block(&qc->result_tf, qc->dev);
if (information == U64_MAX)
return;
- scsi_set_sense_information(cmd->sense_buffer,
+ scsi_set_sense_information(qc->scsicmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE, information);
}
+/**
+ * ata_scsi_set_passthru_sense_fields - Set ATA fields in sense buffer
+ * @qc: ATA PASS-THROUGH command.
+ *
+ * Populates "ATA Status Return sense data descriptor" / "Fixed format
+ * sense data" with ATA taskfile fields.
+ *
+ * LOCKING:
+ * None.
+ */
+static void ata_scsi_set_passthru_sense_fields(struct ata_queued_cmd *qc)
+{
+ struct ata_device *dev = qc->dev;
+ struct scsi_cmnd *cmd = qc->scsicmd;
+ struct ata_taskfile *tf = &qc->result_tf;
+ unsigned char *sb = cmd->sense_buffer;
+
+ if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) {
+ ata_dev_dbg(dev,
+ "missing result TF: can't set ATA PT sense fields\n");
+ return;
+ }
+
+ if ((sb[0] & 0x7f) >= 0x72) {
+ unsigned char *desc;
+ u8 len;
+
+ /* descriptor format */
+ len = sb[7];
+ desc = (char *)scsi_sense_desc_find(sb, len + 8, 9);
+ if (!desc) {
+ if (SCSI_SENSE_BUFFERSIZE < len + 14)
+ return;
+ sb[7] = len + 14;
+ desc = sb + 8 + len;
+ }
+ desc[0] = 9;
+ desc[1] = 12;
+ /*
+ * Copy registers into sense buffer.
+ */
+ desc[2] = 0x00;
+ desc[3] = tf->error;
+ desc[5] = tf->nsect;
+ desc[7] = tf->lbal;
+ desc[9] = tf->lbam;
+ desc[11] = tf->lbah;
+ desc[12] = tf->device;
+ desc[13] = tf->status;
+
+ /*
+ * Fill in Extend bit, and the high order bytes
+ * if applicable.
+ */
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ desc[2] |= 0x01;
+ desc[4] = tf->hob_nsect;
+ desc[6] = tf->hob_lbal;
+ desc[8] = tf->hob_lbam;
+ desc[10] = tf->hob_lbah;
+ }
+ } else {
+ /* Fixed sense format */
+ sb[0] |= 0x80;
+ sb[3] = tf->error;
+ sb[4] = tf->status;
+ sb[5] = tf->device;
+ sb[6] = tf->nsect;
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ sb[8] |= 0x80;
+ if (tf->hob_nsect)
+ sb[8] |= 0x40;
+ if (tf->hob_lbal || tf->hob_lbam || tf->hob_lbah)
+ sb[8] |= 0x20;
+ }
+ sb[9] = tf->lbal;
+ sb[10] = tf->lbam;
+ sb[11] = tf->lbah;
+ }
+}
+
static void ata_scsi_set_invalid_field(struct ata_device *dev,
struct scsi_cmnd *cmd, u16 field, u8 bit)
{
@@ -252,7 +351,7 @@ EXPORT_SYMBOL_GPL(ata_common_sdev_groups);
/**
* ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd.
* @sdev: SCSI device for which BIOS geometry is to be determined
- * @bdev: block device associated with @sdev
+ * @unused: gendisk associated with @sdev
* @capacity: capacity of SCSI device
* @geom: location to which geometry will be output
*
@@ -267,7 +366,7 @@ EXPORT_SYMBOL_GPL(ata_common_sdev_groups);
* RETURNS:
* Zero.
*/
-int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+int ata_std_bios_param(struct scsi_device *sdev, struct gendisk *unused,
sector_t capacity, int geom[])
{
geom[0] = 255;
@@ -367,8 +466,12 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
u8 scsi_cmd[MAX_COMMAND_SIZE];
u8 args[4], *argbuf = NULL;
int argsize = 0;
- enum dma_data_direction data_dir;
struct scsi_sense_hdr sshdr;
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ .sense = sensebuf,
+ .sense_len = sizeof(sensebuf),
+ };
int cmd_result;
if (arg == NULL)
@@ -391,11 +494,9 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
scsi_cmd[1] = (4 << 1); /* PIO Data-in */
scsi_cmd[2] = 0x0e; /* no off.line or cc, read from dev,
block count in sector count field */
- data_dir = DMA_FROM_DEVICE;
} else {
scsi_cmd[1] = (3 << 1); /* Non-data */
scsi_cmd[2] = 0x20; /* cc but no off.line or data xfer */
- data_dir = DMA_NONE;
}
scsi_cmd[0] = ATA_16;
@@ -413,9 +514,8 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
- cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize,
- sensebuf, &sshdr, (10*HZ), 5, 0, 0, NULL);
-
+ cmd_result = scsi_execute_cmd(scsidev, scsi_cmd, REQ_OP_DRV_IN, argbuf,
+ argsize, 10 * HZ, 5, &exec_args);
if (cmd_result < 0) {
rc = cmd_result;
goto error;
@@ -475,6 +575,11 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
u8 args[7];
struct scsi_sense_hdr sshdr;
int cmd_result;
+ const struct scsi_exec_args exec_args = {
+ .sshdr = &sshdr,
+ .sense = sensebuf,
+ .sense_len = sizeof(sensebuf),
+ };
if (arg == NULL)
return -EINVAL;
@@ -497,9 +602,8 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
- cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0,
- sensebuf, &sshdr, (10*HZ), 5, 0, 0, NULL);
-
+ cmd_result = scsi_execute_cmd(scsidev, scsi_cmd, REQ_OP_DRV_IN, NULL,
+ 0, 10 * HZ, 5, &exec_args);
if (cmd_result < 0) {
rc = cmd_result;
goto error;
@@ -642,7 +746,7 @@ static struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev,
struct ata_queued_cmd *qc;
int tag;
- if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
+ if (unlikely(ata_port_is_frozen(ap)))
goto fail;
if (ap->flags & ATA_FLAG_SAS_HOST) {
@@ -691,55 +795,12 @@ static void ata_qc_set_pc_nbytes(struct ata_queued_cmd *qc)
}
/**
- * ata_dump_status - user friendly display of error info
- * @ap: the port in question
- * @tf: ptr to filled out taskfile
- *
- * Decode and dump the ATA error/status registers for the user so
- * that they have some idea what really happened at the non
- * make-believe layer.
- *
- * LOCKING:
- * inherited from caller
- */
-static void ata_dump_status(struct ata_port *ap, struct ata_taskfile *tf)
-{
- u8 stat = tf->status, err = tf->error;
-
- if (stat & ATA_BUSY) {
- ata_port_warn(ap, "status=0x%02x {Busy} ", stat);
- } else {
- ata_port_warn(ap, "status=0x%02x { %s%s%s%s%s%s%s} ", stat,
- stat & ATA_DRDY ? "DriveReady " : "",
- stat & ATA_DF ? "DeviceFault " : "",
- stat & ATA_DSC ? "SeekComplete " : "",
- stat & ATA_DRQ ? "DataRequest " : "",
- stat & ATA_CORR ? "CorrectedError " : "",
- stat & ATA_SENSE ? "Sense " : "",
- stat & ATA_ERR ? "Error " : "");
- if (err)
- ata_port_warn(ap, "error=0x%02x {%s%s%s%s%s%s", err,
- err & ATA_ABORTED ?
- "DriveStatusError " : "",
- err & ATA_ICRC ?
- (err & ATA_ABORTED ?
- "BadCRC " : "Sector ") : "",
- err & ATA_UNC ? "UncorrectableError " : "",
- err & ATA_IDNF ? "SectorIdNotFound " : "",
- err & ATA_TRK0NF ? "TrackZeroNotFound " : "",
- err & ATA_AMNF ? "AddrMarkNotFound " : "");
- }
-}
-
-/**
* ata_to_sense_error - convert ATA error to SCSI error
- * @id: ATA device number
* @drv_stat: value contained in ATA status register
* @drv_err: value contained in ATA error register
* @sk: the sense key we'll fill out
* @asc: the additional sense code we'll fill out
* @ascq: the additional sense code qualifier we'll fill out
- * @verbose: be verbose
*
* Converts an ATA error into a SCSI error. Fill out pointers to
* SK, ASC, and ASCQ bytes for later use in fixed or descriptor
@@ -748,8 +809,8 @@ static void ata_dump_status(struct ata_port *ap, struct ata_taskfile *tf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk,
- u8 *asc, u8 *ascq, int verbose)
+static void ata_to_sense_error(u8 drv_stat, u8 drv_err, u8 *sk, u8 *asc,
+ u8 *ascq)
{
int i;
@@ -798,18 +859,14 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk,
{0xFF, 0xFF, 0xFF, 0xFF}, // END mark
};
static const unsigned char stat_table[][4] = {
- /* Must be first because BUSY means no other bits valid */
- {0x80, ABORTED_COMMAND, 0x47, 0x00},
- // Busy, fake parity for now
- {0x40, ILLEGAL_REQUEST, 0x21, 0x04},
- // Device ready, unaligned write command
- {0x20, HARDWARE_ERROR, 0x44, 0x00},
- // Device fault, internal target failure
- {0x08, ABORTED_COMMAND, 0x47, 0x00},
- // Timed out in xfer, fake parity for now
- {0x04, RECOVERED_ERROR, 0x11, 0x00},
- // Recovered ECC error Medium error, recovered
- {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ /* Busy: must be first because BUSY means no other bits valid */
+ { ATA_BUSY, ABORTED_COMMAND, 0x00, 0x00 },
+ /* Device fault: INTERNAL TARGET FAILURE */
+ { ATA_DF, HARDWARE_ERROR, 0x44, 0x00 },
+ /* Corrected data error */
+ { ATA_CORR, RECOVERED_ERROR, 0x00, 0x00 },
+
+ { 0xFF, 0xFF, 0xFF, 0xFF }, /* END mark */
};
/*
@@ -828,7 +885,7 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk,
*sk = sense_table[i][1];
*asc = sense_table[i][2];
*ascq = sense_table[i][3];
- goto translate_done;
+ return;
}
}
}
@@ -843,7 +900,7 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk,
*sk = stat_table[i][1];
*asc = stat_table[i][2];
*ascq = stat_table[i][3];
- goto translate_done;
+ return;
}
}
@@ -854,22 +911,14 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk,
*sk = ABORTED_COMMAND;
*asc = 0x00;
*ascq = 0x00;
-
- translate_done:
- if (verbose)
- pr_err("ata%u: translated ATA stat/err 0x%02x/%02x to SCSI SK/ASC/ASCQ 0x%x/%02x/%02x\n",
- id, drv_stat, drv_err, *sk, *asc, *ascq);
- return;
}
/*
* ata_gen_passthru_sense - Generate check condition sense block.
* @qc: Command that completed.
*
- * This function is specific to the ATA descriptor format sense
- * block specified for the ATA pass through commands. Regardless
- * of whether the command errored or not, return a sense
- * block. Copy all controller registers into the sense
+ * This function is specific to the ATA pass through commands.
+ * Regardless of whether the command errored or not, return a sense
* block. If there was no error, we get the request from an ATA
* passthrough command, so we use the following sense data:
* sk = RECOVERED ERROR
@@ -881,14 +930,18 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk,
*/
static void ata_gen_passthru_sense(struct ata_queued_cmd *qc)
{
+ struct ata_device *dev = qc->dev;
struct scsi_cmnd *cmd = qc->scsicmd;
struct ata_taskfile *tf = &qc->result_tf;
- unsigned char *sb = cmd->sense_buffer;
- unsigned char *desc = sb + 8;
- int verbose = qc->ap->ops->error_handler == NULL;
u8 sense_key, asc, ascq;
- memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
+ if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) {
+ ata_dev_dbg(dev,
+ "missing result TF: can't generate ATA PT sense data\n");
+ if (qc->err_mask)
+ ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0);
+ return;
+ }
/*
* Use ata_to_sense_error() to map status register bits
@@ -896,80 +949,31 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc)
*/
if (qc->err_mask ||
tf->status & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) {
- ata_to_sense_error(qc->ap->print_id, tf->status, tf->error,
- &sense_key, &asc, &ascq, verbose);
+ ata_to_sense_error(tf->status, tf->error,
+ &sense_key, &asc, &ascq);
ata_scsi_set_sense(qc->dev, cmd, sense_key, asc, ascq);
} else {
/*
* ATA PASS-THROUGH INFORMATION AVAILABLE
- * Always in descriptor format sense.
+ *
+ * Note: we are supposed to call ata_scsi_set_sense(), which
+ * respects the D_SENSE bit, instead of unconditionally
+ * generating the sense data in descriptor format. However,
+ * because hdparm, hddtemp, and udisks incorrectly assume sense
+ * data in descriptor format, without even looking at the
+ * RESPONSE CODE field in the returned sense data (to see which
+ * format the returned sense data is in), we are stuck with
+ * being bug compatible with older kernels.
*/
scsi_build_sense(cmd, 1, RECOVERED_ERROR, 0, 0x1D);
}
-
- if ((cmd->sense_buffer[0] & 0x7f) >= 0x72) {
- u8 len;
-
- /* descriptor format */
- len = sb[7];
- desc = (char *)scsi_sense_desc_find(sb, len + 8, 9);
- if (!desc) {
- if (SCSI_SENSE_BUFFERSIZE < len + 14)
- return;
- sb[7] = len + 14;
- desc = sb + 8 + len;
- }
- desc[0] = 9;
- desc[1] = 12;
- /*
- * Copy registers into sense buffer.
- */
- desc[2] = 0x00;
- desc[3] = tf->error;
- desc[5] = tf->nsect;
- desc[7] = tf->lbal;
- desc[9] = tf->lbam;
- desc[11] = tf->lbah;
- desc[12] = tf->device;
- desc[13] = tf->status;
-
- /*
- * Fill in Extend bit, and the high order bytes
- * if applicable.
- */
- if (tf->flags & ATA_TFLAG_LBA48) {
- desc[2] |= 0x01;
- desc[4] = tf->hob_nsect;
- desc[6] = tf->hob_lbal;
- desc[8] = tf->hob_lbam;
- desc[10] = tf->hob_lbah;
- }
- } else {
- /* Fixed sense format */
- desc[0] = tf->error;
- desc[1] = tf->status;
- desc[2] = tf->device;
- desc[3] = tf->nsect;
- desc[7] = 0;
- if (tf->flags & ATA_TFLAG_LBA48) {
- desc[8] |= 0x80;
- if (tf->hob_nsect)
- desc[8] |= 0x40;
- if (tf->hob_lbal || tf->hob_lbam || tf->hob_lbah)
- desc[8] |= 0x20;
- }
- desc[9] = tf->lbal;
- desc[10] = tf->lbam;
- desc[11] = tf->lbah;
- }
}
/**
* ata_gen_ata_sense - generate a SCSI fixed sense block
* @qc: Command that we are erroring out
*
- * Generate sense block for a failed ATA command @qc. Descriptor
- * format is used to accommodate LBA48 block address.
+ * Generate sense block for a failed ATA command @qc.
*
* LOCKING:
* None.
@@ -979,40 +983,45 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc)
struct ata_device *dev = qc->dev;
struct scsi_cmnd *cmd = qc->scsicmd;
struct ata_taskfile *tf = &qc->result_tf;
- unsigned char *sb = cmd->sense_buffer;
- int verbose = qc->ap->ops->error_handler == NULL;
- u64 block;
u8 sense_key, asc, ascq;
- memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
-
if (ata_dev_disabled(dev)) {
/* Device disabled after error recovery */
/* LOGICAL UNIT NOT READY, HARD RESET REQUIRED */
ata_scsi_set_sense(dev, cmd, NOT_READY, 0x04, 0x21);
return;
}
+
+ if (ata_id_is_locked(dev->id)) {
+ /* Security locked */
+ /* LOGICAL UNIT ACCESS NOT AUTHORIZED */
+ ata_scsi_set_sense(dev, cmd, DATA_PROTECT, 0x74, 0x71);
+ return;
+ }
+
+ if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) {
+ ata_dev_dbg(dev,
+ "Missing result TF: reporting aborted command\n");
+ goto aborted;
+ }
+
/* Use ata_to_sense_error() to map status register bits
* onto sense key, asc & ascq.
*/
if (qc->err_mask ||
tf->status & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) {
- ata_to_sense_error(qc->ap->print_id, tf->status, tf->error,
- &sense_key, &asc, &ascq, verbose);
+ ata_to_sense_error(tf->status, tf->error,
+ &sense_key, &asc, &ascq);
ata_scsi_set_sense(dev, cmd, sense_key, asc, ascq);
- } else {
- /* Could not decode error */
- ata_dev_warn(dev, "could not decode error status 0x%x err_mask 0x%x\n",
- tf->status, qc->err_mask);
- ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0);
return;
}
- block = ata_tf_read_block(&qc->result_tf, dev);
- if (block == U64_MAX)
- return;
-
- scsi_set_sense_information(sb, SCSI_SENSE_BUFFERSIZE, block);
+ /* Could not decode error */
+ ata_dev_warn(dev,
+ "Could not decode error 0x%x, status 0x%x (err_mask=0x%x)\n",
+ tf->error, tf->status, qc->err_mask);
+aborted:
+ ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0);
}
void ata_scsi_sdev_config(struct scsi_device *sdev)
@@ -1052,25 +1061,26 @@ bool ata_scsi_dma_need_drain(struct request *rq)
}
EXPORT_SYMBOL_GPL(ata_scsi_dma_need_drain);
-int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
+int ata_scsi_dev_config(struct scsi_device *sdev, struct queue_limits *lim,
+ struct ata_device *dev)
{
- struct request_queue *q = sdev->request_queue;
+ int depth = 1;
if (!ata_id_has_unload(dev->id))
dev->flags |= ATA_DFLAG_NO_UNLOAD;
/* configure max sectors */
dev->max_sectors = min(dev->max_sectors, sdev->host->max_sectors);
- blk_queue_max_hw_sectors(q, dev->max_sectors);
+ lim->max_hw_sectors = dev->max_sectors;
if (dev->class == ATA_DEV_ATAPI) {
sdev->sector_size = ATA_SECT_SIZE;
/* set DMA padding */
- blk_queue_update_dma_pad(q, ATA_DMA_PAD_SZ - 1);
+ lim->dma_pad_mask = ATA_DMA_PAD_SZ - 1;
/* make room for appending the drain */
- blk_queue_max_segments(q, queue_max_segments(q) - 1);
+ lim->max_segments--;
sdev->dma_drain_len = ATAPI_MAX_DRAIN;
sdev->dma_drain_buf = kmalloc(sdev->dma_drain_len, GFP_NOIO);
@@ -1080,7 +1090,20 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
}
} else {
sdev->sector_size = ata_id_logical_sector_size(dev->id);
- sdev->manage_start_stop = 1;
+
+ /*
+ * Ask the sd driver to issue START STOP UNIT on runtime suspend
+ * and resume and shutdown only. For system level suspend/resume,
+ * devices power state is handled directly by libata EH.
+ * Given that disks are always spun up on system resume, also
+ * make sure that the sd driver forces runtime suspended disks
+ * to be resumed to correctly reflect the power state of the
+ * device.
+ */
+ sdev->manage_runtime_start_stop = 1;
+ sdev->manage_shutdown = 1;
+ sdev->manage_restart = ata_acpi_dev_manage_restart(dev);
+ sdev->force_runtime_start_on_system_start = 1;
}
/*
@@ -1095,18 +1118,15 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
"sector_size=%u > PAGE_SIZE, PIO may malfunction\n",
sdev->sector_size);
- blk_queue_update_dma_alignment(q, sdev->sector_size - 1);
+ lim->dma_alignment = sdev->sector_size - 1;
if (dev->flags & ATA_DFLAG_AN)
set_bit(SDEV_EVT_MEDIA_CHANGE, sdev->supported_events);
- if (dev->flags & ATA_DFLAG_NCQ) {
- int depth;
-
+ if (ata_ncq_supported(dev))
depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id));
- depth = min(ATA_MAX_QUEUE, depth);
- scsi_change_queue_depth(sdev, depth);
- }
+ depth = min(ATA_MAX_QUEUE, depth);
+ scsi_change_queue_depth(sdev, depth);
if (dev->flags & ATA_DFLAG_TRUSTED)
sdev->security_supported = 1;
@@ -1116,9 +1136,46 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
}
/**
- * ata_scsi_slave_config - Set SCSI device attributes
+ * ata_scsi_sdev_init - Early setup of SCSI device
* @sdev: SCSI device to examine
*
+ * This is called from scsi_alloc_sdev() when the scsi device
+ * associated with an ATA device is scanned on a port.
+ *
+ * LOCKING:
+ * Defined by SCSI layer. We don't really care.
+ */
+
+int ata_scsi_sdev_init(struct scsi_device *sdev)
+{
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+ struct device_link *link;
+
+ ata_scsi_sdev_config(sdev);
+
+ /*
+ * Create a link from the ata_port device to the scsi device to ensure
+ * that PM does suspend/resume in the correct order: the scsi device is
+ * consumer (child) and the ata port the supplier (parent).
+ */
+ link = device_link_add(&sdev->sdev_gendev, &ap->tdev,
+ DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+ if (!link) {
+ ata_port_err(ap, "Failed to create link to scsi device %s\n",
+ dev_name(&sdev->sdev_gendev));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ata_scsi_sdev_init);
+
+/**
+ * ata_scsi_sdev_configure - Set SCSI device attributes
+ * @sdev: SCSI device to examine
+ * @lim: queue limits
+ *
* This is called before we actually start reading
* and writing to the device, to configure certain
* SCSI mid-layer behaviors.
@@ -1127,23 +1184,20 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
* Defined by SCSI layer. We don't really care.
*/
-int ata_scsi_slave_config(struct scsi_device *sdev)
+int ata_scsi_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
- int rc = 0;
-
- ata_scsi_sdev_config(sdev);
if (dev)
- rc = ata_scsi_dev_config(sdev, dev);
+ return ata_scsi_dev_config(sdev, lim, dev);
- return rc;
+ return 0;
}
-EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
+EXPORT_SYMBOL_GPL(ata_scsi_sdev_configure);
/**
- * ata_scsi_slave_destroy - SCSI device is about to be destroyed
+ * ata_scsi_sdev_destroy - SCSI device is about to be destroyed
* @sdev: SCSI device to be destroyed
*
* @sdev is about to be destroyed for hot/warm unplugging. If
@@ -1156,14 +1210,13 @@ EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
* LOCKING:
* Defined by SCSI layer. We don't really care.
*/
-void ata_scsi_slave_destroy(struct scsi_device *sdev)
+void ata_scsi_sdev_destroy(struct scsi_device *sdev)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
unsigned long flags;
struct ata_device *dev;
- if (!ap->ops->error_handler)
- return;
+ device_link_remove(&sdev->sdev_gendev, &ap->tdev);
spin_lock_irqsave(ap->lock, flags);
dev = __ata_scsi_find_dev(ap, sdev);
@@ -1177,7 +1230,7 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
kfree(sdev->dma_drain_buf);
}
-EXPORT_SYMBOL_GPL(ata_scsi_slave_destroy);
+EXPORT_SYMBOL_GPL(ata_scsi_sdev_destroy);
/**
* ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command
@@ -1197,7 +1250,6 @@ EXPORT_SYMBOL_GPL(ata_scsi_slave_destroy);
static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
{
struct scsi_cmnd *scmd = qc->scsicmd;
- struct ata_taskfile *tf = &qc->tf;
const u8 *cdb = scmd->cmnd;
u16 fp;
u8 bp = 0xff;
@@ -1207,54 +1259,24 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
goto invalid_fld;
}
- tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
- tf->protocol = ATA_PROT_NODATA;
- if (cdb[1] & 0x1) {
- ; /* ignore IMMED bit, violates sat-r05 */
- }
+ /* LOEJ bit set not supported */
if (cdb[4] & 0x2) {
fp = 4;
bp = 1;
- goto invalid_fld; /* LOEJ bit set not supported */
+ goto invalid_fld;
}
+
+ /* Power conditions not supported */
if (((cdb[4] >> 4) & 0xf) != 0) {
fp = 4;
bp = 3;
- goto invalid_fld; /* power conditions not supported */
+ goto invalid_fld;
}
- if (cdb[4] & 0x1) {
- tf->nsect = 1; /* 1 sector, lba=0 */
-
- if (qc->dev->flags & ATA_DFLAG_LBA) {
- tf->flags |= ATA_TFLAG_LBA;
-
- tf->lbah = 0x0;
- tf->lbam = 0x0;
- tf->lbal = 0x0;
- tf->device |= ATA_LBA;
- } else {
- /* CHS */
- tf->lbal = 0x1; /* sect */
- tf->lbam = 0x0; /* cyl low */
- tf->lbah = 0x0; /* cyl high */
- }
-
- tf->command = ATA_CMD_VERIFY; /* READ VERIFY */
- } else {
- /* Some odd clown BIOSen issue spindown on power off (ACPI S4
- * or S5) causing some drives to spin up and down again.
- */
- if ((qc->ap->flags & ATA_FLAG_NO_POWEROFF_SPINDOWN) &&
- system_state == SYSTEM_POWER_OFF)
- goto skip;
-
- if ((qc->ap->flags & ATA_FLAG_NO_HIBERNATE_SPINDOWN) &&
- system_entering_hibernation())
- goto skip;
-
- /* Issue ATA STANDBY IMMEDIATE command */
- tf->command = ATA_CMD_STANDBYNOW1;
+ /* Ignore IMMED bit (cdb[1] & 0x1), violates sat-r05 */
+ if (!ata_dev_power_init_tf(qc->dev, &qc->tf, cdb[4] & 0x1)) {
+ ata_scsi_set_sense(qc->dev, scmd, ABORTED_COMMAND, 0, 0);
+ return 1;
}
/*
@@ -1269,12 +1291,8 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
invalid_fld:
ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp);
return 1;
- skip:
- scmd->result = SAM_STAT_GOOD;
- return 1;
}
-
/**
* ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command
* @qc: Storage for translated ATA taskfile
@@ -1318,17 +1336,8 @@ static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc)
*/
static void scsi_6_lba_len(const u8 *cdb, u64 *plba, u32 *plen)
{
- u64 lba = 0;
- u32 len;
-
- lba |= ((u64)(cdb[1] & 0x1f)) << 16;
- lba |= ((u64)cdb[2]) << 8;
- lba |= ((u64)cdb[3]);
-
- len = cdb[4];
-
- *plba = lba;
- *plen = len;
+ *plba = get_unaligned_be24(&cdb[1]) & 0x1fffff;
+ *plen = cdb[4];
}
/**
@@ -1364,6 +1373,18 @@ static inline void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen)
}
/**
+ * scsi_dld - Get duration limit descriptor index
+ * @cdb: SCSI command to translate
+ *
+ * Returns the dld bits indicating the index of a command duration limit
+ * descriptor.
+ */
+static inline int scsi_dld(const u8 *cdb)
+{
+ return ((cdb[1] & 0x01) << 2) | ((cdb[14] >> 6) & 0x03);
+}
+
+/**
* ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one
* @qc: Storage for translated ATA taskfile
*
@@ -1531,6 +1552,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
struct request *rq = scsi_cmd_to_rq(scmd);
int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
unsigned int tf_flags = 0;
+ int dld = 0;
u64 block;
u32 n_block;
int rc;
@@ -1581,6 +1603,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
goto invalid_fld;
}
scsi_16_lba_len(cdb, &block, &n_block);
+ dld = scsi_dld(cdb);
if (cdb[1] & (1 << 3))
tf_flags |= ATA_TFLAG_FUA;
if (!ata_check_nblocks(scmd, n_block))
@@ -1605,9 +1628,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
qc->flags |= ATA_QCFLAG_IO;
qc->nbytes = n_block * scmd->device->sector_size;
- rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags,
- qc->hw_tag, class);
-
+ rc = ata_build_rw_tf(qc, block, n_block, tf_flags, dld, class);
if (likely(rc == 0))
return 0;
@@ -1639,32 +1660,33 @@ static void ata_qc_done(struct ata_queued_cmd *qc)
static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
{
- struct ata_port *ap = qc->ap;
struct scsi_cmnd *cmd = qc->scsicmd;
u8 *cdb = cmd->cmnd;
- int need_sense = (qc->err_mask != 0);
+ bool have_sense = qc->flags & ATA_QCFLAG_SENSE_VALID;
+ bool is_ata_passthru = cdb[0] == ATA_16 || cdb[0] == ATA_12;
+ bool is_ck_cond_request = cdb[2] & 0x20;
+ bool is_error = qc->err_mask != 0;
/* For ATA pass thru (SAT) commands, generate a sense block if
* user mandated it or if there's an error. Note that if we
- * generate because the user forced us to [CK_COND =1], a check
+ * generate because the user forced us to [CK_COND=1], a check
* condition is generated and the ATA register values are returned
* whether the command completed successfully or not. If there
- * was no error, we use the following sense data:
+ * was no error, and CK_COND=1, we use the following sense data:
* sk = RECOVERED ERROR
* asc,ascq = ATA PASS-THROUGH INFORMATION AVAILABLE
*/
- if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
- ((cdb[2] & 0x20) || need_sense))
- ata_gen_passthru_sense(qc);
- else if (qc->flags & ATA_QCFLAG_SENSE_VALID)
- cmd->result = SAM_STAT_CHECK_CONDITION;
- else if (need_sense)
- ata_gen_ata_sense(qc);
- else
- cmd->result = SAM_STAT_GOOD;
-
- if (need_sense && !ap->ops->error_handler)
- ata_dump_status(ap, &qc->result_tf);
+ if (is_ata_passthru && (is_ck_cond_request || is_error || have_sense)) {
+ if (!have_sense)
+ ata_gen_passthru_sense(qc);
+ ata_scsi_set_passthru_sense_fields(qc);
+ if (is_ck_cond_request)
+ set_status_byte(qc->scsicmd, SAM_STAT_CHECK_CONDITION);
+ } else if (is_error) {
+ if (!have_sense)
+ ata_gen_ata_sense(qc);
+ ata_scsi_set_sense_information(qc);
+ }
ata_qc_done(qc);
}
@@ -1754,15 +1776,10 @@ defer:
return SCSI_MLQUEUE_HOST_BUSY;
}
-struct ata_scsi_args {
- struct ata_device *dev;
- u16 *id;
- struct scsi_cmnd *cmd;
-};
-
/**
* ata_scsi_rbuf_fill - wrapper for SCSI command simulators
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @actor: Callback hook for desired SCSI command simulator
*
* Takes care of the hard work of simulating a SCSI command...
@@ -1775,30 +1792,32 @@ struct ata_scsi_args {
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
- unsigned int (*actor)(struct ata_scsi_args *args, u8 *rbuf))
+static void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd,
+ unsigned int (*actor)(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf))
{
- unsigned int rc;
- struct scsi_cmnd *cmd = args->cmd;
unsigned long flags;
+ unsigned int len;
spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
memset(ata_scsi_rbuf, 0, ATA_SCSI_RBUF_SIZE);
- rc = actor(args, ata_scsi_rbuf);
- if (rc == 0)
+ len = actor(dev, cmd, ata_scsi_rbuf);
+ if (len) {
sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd),
ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE);
+ cmd->result = SAM_STAT_GOOD;
+ if (scsi_bufflen(cmd) > len)
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - len);
+ }
spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
-
- if (rc == 0)
- cmd->result = SAM_STAT_GOOD;
}
/**
- * ata_scsiop_inq_std - Simulate INQUIRY command
- * @args: device IDENTIFY data / SCSI command of interest.
+ * ata_scsiop_inq_std - Simulate standard INQUIRY command
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns standard device identification data associated
@@ -1807,7 +1826,8 @@ static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_std(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
static const u8 versions[] = {
0x00,
@@ -1844,41 +1864,49 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
2
};
- /* set scsi removable (RMB) bit per ata bit, or if the
- * AHCI port says it's external (Hotplug-capable, eSATA).
+ /*
+ * Set the SCSI Removable Media Bit (RMB) if the ATA removable media
+ * device bit (obsolete since ATA-8 ACS) is set.
*/
- if (ata_id_removable(args->id) ||
- (args->dev->link->ap->pflags & ATA_PFLAG_EXTERNAL))
+ if (ata_id_removable(dev->id))
hdr[1] |= (1 << 7);
- if (args->dev->class == ATA_DEV_ZAC) {
+ if (dev->class == ATA_DEV_ZAC) {
hdr[0] = TYPE_ZBC;
hdr[2] = 0x7; /* claim SPC-5 version compatibility */
}
+ if (dev->flags & ATA_DFLAG_CDL)
+ hdr[2] = 0xd; /* claim SPC-6 version compatibility */
+
memcpy(rbuf, hdr, sizeof(hdr));
memcpy(&rbuf[8], "ATA ", 8);
- ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16);
+ ata_id_string(dev->id, &rbuf[16], ATA_ID_PROD, 16);
/* From SAT, use last 2 words from fw rev unless they are spaces */
- ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV + 2, 4);
+ ata_id_string(dev->id, &rbuf[32], ATA_ID_FW_REV + 2, 4);
if (strncmp(&rbuf[32], " ", 4) == 0)
- ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4);
+ ata_id_string(dev->id, &rbuf[32], ATA_ID_FW_REV, 4);
if (rbuf[32] == 0 || rbuf[32] == ' ')
memcpy(&rbuf[32], "n/a ", 4);
- if (ata_id_zoned_cap(args->id) || args->dev->class == ATA_DEV_ZAC)
+ if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC)
memcpy(rbuf + 58, versions_zbc, sizeof(versions_zbc));
else
memcpy(rbuf + 58, versions, sizeof(versions));
- return 0;
+ /*
+ * Include all 8 possible version descriptors, even if not all of
+ * them are popoulated.
+ */
+ return 96;
}
/**
* ata_scsiop_inq_00 - Simulate INQUIRY VPD page 0, list of pages
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns list of inquiry VPD pages available.
@@ -1886,7 +1914,8 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_00(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
int i, num_pages = 0;
static const u8 pages[] = {
@@ -1902,19 +1931,20 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
};
for (i = 0; i < sizeof(pages); i++) {
- if (pages[i] == 0xb6 &&
- !(args->dev->flags & ATA_DFLAG_ZAC))
+ if (pages[i] == 0xb6 && !ata_dev_is_zac(dev))
continue;
rbuf[num_pages + 4] = pages[i];
num_pages++;
}
rbuf[3] = num_pages; /* number of supported VPD pages */
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
* ata_scsiop_inq_80 - Simulate INQUIRY VPD page 80, device serial number
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns ATA device serial number.
@@ -1922,7 +1952,8 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_80(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
static const u8 hdr[] = {
0,
@@ -1932,14 +1963,16 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
};
memcpy(rbuf, hdr, sizeof(hdr));
- ata_id_string(args->id, (unsigned char *) &rbuf[4],
+ ata_id_string(dev->id, (unsigned char *) &rbuf[4],
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
* ata_scsiop_inq_83 - Simulate INQUIRY VPD page 83, device identity
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Yields two logical unit device identification designators:
@@ -1950,7 +1983,8 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_83(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
const int sat_model_serial_desc_len = 68;
int num;
@@ -1962,7 +1996,7 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
rbuf[num + 0] = 2;
rbuf[num + 3] = ATA_ID_SERNO_LEN;
num += 4;
- ata_id_string(args->id, (unsigned char *) rbuf + num,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num,
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
num += ATA_ID_SERNO_LEN;
@@ -1974,31 +2008,33 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
num += 4;
memcpy(rbuf + num, "ATA ", 8);
num += 8;
- ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_PROD,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num, ATA_ID_PROD,
ATA_ID_PROD_LEN);
num += ATA_ID_PROD_LEN;
- ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_SERNO,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num, ATA_ID_SERNO,
ATA_ID_SERNO_LEN);
num += ATA_ID_SERNO_LEN;
- if (ata_id_has_wwn(args->id)) {
+ if (ata_id_has_wwn(dev->id)) {
/* SAT defined lu world wide name */
/* piv=0, assoc=lu, code_set=binary, designator=NAA */
rbuf[num + 0] = 1;
rbuf[num + 1] = 3;
rbuf[num + 3] = ATA_ID_WWN_LEN;
num += 4;
- ata_id_string(args->id, (unsigned char *) rbuf + num,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num,
ATA_ID_WWN, ATA_ID_WWN_LEN);
num += ATA_ID_WWN_LEN;
}
rbuf[3] = num - 4; /* page len (assume less than 256 bytes) */
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
* ata_scsiop_inq_89 - Simulate INQUIRY VPD page 89, ATA info
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Yields SAT-specified ATA VPD page.
@@ -2006,7 +2042,8 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_89(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
rbuf[1] = 0x89; /* our page code */
rbuf[2] = (0x238 >> 8); /* page size fixed at 238h */
@@ -2027,13 +2064,25 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
rbuf[56] = ATA_CMD_ID_ATA;
- memcpy(&rbuf[60], &args->id[0], 512);
- return 0;
+ memcpy(&rbuf[60], &dev->id[0], 512);
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
-static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
+/**
+ * ata_scsiop_inq_b0 - Simulate INQUIRY VPD page B0, Block Limits
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B0h (Block Limits).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inq_b0(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
u16 min_io_sectors;
rbuf[1] = 0xb0;
@@ -2046,7 +2095,7 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
* logical than physical sector size we need to figure out what the
* latter is.
*/
- min_io_sectors = 1 << ata_id_log2_per_physical_sector(args->id);
+ min_io_sectors = 1 << ata_id_log2_per_physical_sector(dev->id);
put_unaligned_be16(min_io_sectors, &rbuf[6]);
/*
@@ -2058,24 +2107,37 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
* that we support some form of unmap - in thise case via WRITE SAME
* with the unmap bit set.
*/
- if (ata_id_has_trim(args->id)) {
+ if (ata_id_has_trim(dev->id)) {
u64 max_blocks = 65535 * ATA_MAX_TRIM_RNUM;
- if (dev->horkage & ATA_HORKAGE_MAX_TRIM_128M)
+ if (dev->quirks & ATA_QUIRK_MAX_TRIM_128M)
max_blocks = 128 << (20 - SECTOR_SHIFT);
put_unaligned_be64(max_blocks, &rbuf[36]);
put_unaligned_be32(1, &rbuf[28]);
}
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
-static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
+/**
+ * ata_scsiop_inq_b1 - Simulate INQUIRY VPD page B1, Block Device
+ * Characteristics
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B1h (Block Device Characteristics).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inq_b1(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- int form_factor = ata_id_form_factor(args->id);
- int media_rotation_rate = ata_id_rotation_rate(args->id);
- u8 zoned = ata_id_zoned_cap(args->id);
+ int form_factor = ata_id_form_factor(dev->id);
+ int media_rotation_rate = ata_id_rotation_rate(dev->id);
+ u8 zoned = ata_id_zoned_cap(dev->id);
rbuf[1] = 0xb1;
rbuf[3] = 0x3c;
@@ -2085,21 +2147,52 @@ static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
if (zoned)
rbuf[8] = (zoned << 4);
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
-static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
+/**
+ * ata_scsiop_inq_b2 - Simulate INQUIRY VPD page B2, Logical Block
+ * Provisioning
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B2h (Logical Block Provisioning).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inq_b2(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
/* SCSI Thin Provisioning VPD page: SBC-3 rev 22 or later */
rbuf[1] = 0xb2;
rbuf[3] = 0x4;
rbuf[5] = 1 << 6; /* TPWS */
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
-static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
+/**
+ * ata_scsiop_inq_b6 - Simulate INQUIRY VPD page B6, Zoned Block Device
+ * Characteristics
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B2h (Zoned Block Device Characteristics).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inq_b6(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
+ if (!ata_dev_is_zac(dev)) {
+ ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
+ return 0;
+ }
+
/*
* zbc-r05 SCSI Zoned Block device characteristics VPD page
*/
@@ -2109,21 +2202,39 @@ static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
/*
* URSWRZ bit is only meaningful for host-managed ZAC drives
*/
- if (args->dev->zac_zoned_cap & 1)
+ if (dev->zac_zoned_cap & 1)
rbuf[4] |= 1;
- put_unaligned_be32(args->dev->zac_zones_optimal_open, &rbuf[8]);
- put_unaligned_be32(args->dev->zac_zones_optimal_nonseq, &rbuf[12]);
- put_unaligned_be32(args->dev->zac_zones_max_open, &rbuf[16]);
+ put_unaligned_be32(dev->zac_zones_optimal_open, &rbuf[8]);
+ put_unaligned_be32(dev->zac_zones_optimal_nonseq, &rbuf[12]);
+ put_unaligned_be32(dev->zac_zones_max_open, &rbuf[16]);
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
-static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
+/**
+ * ata_scsiop_inq_b9 - Simulate INQUIRY VPD page B9, Concurrent Positioning
+ * Ranges
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B9h (Concurrent Positioning Ranges).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inq_b9(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_cpr_log *cpr_log = args->dev->cpr_log;
+ struct ata_cpr_log *cpr_log = dev->cpr_log;
u8 *desc = &rbuf[64];
int i;
+ if (!cpr_log) {
+ ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
+ return 0;
+ }
+
/* SCSI Concurrent Positioning Ranges VPD page: SBC-5 rev 1 or later */
rbuf[1] = 0xb9;
put_unaligned_be16(64 + (int)cpr_log->nr_cpr * 32 - 4, &rbuf[2]);
@@ -2135,7 +2246,58 @@ static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
put_unaligned_be64(cpr_log->cpr[i].num_lbas, &desc[16]);
}
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
+}
+
+/**
+ * ata_scsiop_inquiry - Simulate INQUIRY command
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Returns data associated with an INQUIRY command output.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inquiry(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
+{
+ const u8 *scsicmd = cmd->cmnd;
+
+ /* is CmdDt set? */
+ if (scsicmd[1] & 2) {
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ return 0;
+ }
+
+ /* Is EVPD clear? */
+ if ((scsicmd[1] & 1) == 0)
+ return ata_scsiop_inq_std(dev, cmd, rbuf);
+
+ switch (scsicmd[2]) {
+ case 0x00:
+ return ata_scsiop_inq_00(dev, cmd, rbuf);
+ case 0x80:
+ return ata_scsiop_inq_80(dev, cmd, rbuf);
+ case 0x83:
+ return ata_scsiop_inq_83(dev, cmd, rbuf);
+ case 0x89:
+ return ata_scsiop_inq_89(dev, cmd, rbuf);
+ case 0xb0:
+ return ata_scsiop_inq_b0(dev, cmd, rbuf);
+ case 0xb1:
+ return ata_scsiop_inq_b1(dev, cmd, rbuf);
+ case 0xb2:
+ return ata_scsiop_inq_b2(dev, cmd, rbuf);
+ case 0xb6:
+ return ata_scsiop_inq_b6(dev, cmd, rbuf);
+ case 0xb9:
+ return ata_scsiop_inq_b9(dev, cmd, rbuf);
+ default:
+ ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
+ return 0;
+ }
}
/**
@@ -2186,10 +2348,128 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)
return sizeof(def_cache_mpage);
}
+/*
+ * Simulate MODE SENSE control mode page, sub-page 0.
+ */
+static unsigned int ata_msense_control_spg0(struct ata_device *dev, u8 *buf,
+ bool changeable)
+{
+ modecpy(buf, def_control_mpage,
+ sizeof(def_control_mpage), changeable);
+ if (changeable) {
+ /* ata_mselect_control() */
+ buf[2] |= (1 << 2);
+ } else {
+ bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE);
+
+ /* descriptor format sense data */
+ buf[2] |= (d_sense << 2);
+ }
+
+ return sizeof(def_control_mpage);
+}
+
+/*
+ * Translate an ATA duration limit in microseconds to a SCSI duration limit
+ * using the t2cdlunits 0xa (10ms). Since the SCSI duration limits are 2-bytes
+ * only, take care of overflows.
+ */
+static inline u16 ata_xlat_cdl_limit(u8 *buf)
+{
+ u32 limit = get_unaligned_le32(buf);
+
+ return min_t(u32, limit / 10000, 65535);
+}
+
+/*
+ * Simulate MODE SENSE control mode page, sub-pages 07h and 08h
+ * (command duration limits T2A and T2B mode pages).
+ */
+static unsigned int ata_msense_control_spgt2(struct ata_device *dev, u8 *buf,
+ u8 spg)
+{
+ u8 *b, *cdl, *desc;
+ u32 policy;
+ int i;
+
+ if (!(dev->flags & ATA_DFLAG_CDL) || !dev->cdl)
+ return 0;
+
+ cdl = dev->cdl->desc_log_buf;
+
+ /*
+ * Fill the subpage. The first four bytes of the T2A/T2B mode pages
+ * are a header. The PAGE LENGTH field is the size of the page
+ * excluding the header.
+ */
+ buf[0] = CONTROL_MPAGE;
+ buf[1] = spg;
+ put_unaligned_be16(CDL_T2_SUB_MPAGE_LEN - 4, &buf[2]);
+ if (spg == CDL_T2A_SUB_MPAGE) {
+ /*
+ * Read descriptors map to the T2A page:
+ * set perf_vs_duration_guidleine.
+ */
+ buf[7] = (cdl[0] & 0x03) << 4;
+ desc = cdl + 64;
+ } else {
+ /* Write descriptors map to the T2B page */
+ desc = cdl + 288;
+ }
+
+ /* Fill the T2 page descriptors */
+ b = &buf[8];
+ policy = get_unaligned_le32(&cdl[0]);
+ for (i = 0; i < 7; i++, b += 32, desc += 32) {
+ /* t2cdlunits: fixed to 10ms */
+ b[0] = 0x0a;
+
+ /* Max inactive time and its policy */
+ put_unaligned_be16(ata_xlat_cdl_limit(&desc[8]), &b[2]);
+ b[6] = ((policy >> 8) & 0x0f) << 4;
+
+ /* Max active time and its policy */
+ put_unaligned_be16(ata_xlat_cdl_limit(&desc[4]), &b[4]);
+ b[6] |= (policy >> 4) & 0x0f;
+
+ /* Command duration guideline and its policy */
+ put_unaligned_be16(ata_xlat_cdl_limit(&desc[16]), &b[10]);
+ b[14] = policy & 0x0f;
+ }
+
+ return CDL_T2_SUB_MPAGE_LEN;
+}
+
+/*
+ * Simulate MODE SENSE control mode page, sub-page f2h
+ * (ATA feature control mode page).
+ */
+static unsigned int ata_msense_control_ata_feature(struct ata_device *dev,
+ u8 *buf)
+{
+ /* PS=0, SPF=1 */
+ buf[0] = CONTROL_MPAGE | (1 << 6);
+ buf[1] = ATA_FEATURE_SUB_MPAGE;
+
+ /*
+ * The first four bytes of ATA Feature Control mode page are a header.
+ * The PAGE LENGTH field is the size of the page excluding the header.
+ */
+ put_unaligned_be16(ATA_FEATURE_SUB_MPAGE_LEN - 4, &buf[2]);
+
+ if (dev->flags & ATA_DFLAG_CDL_ENABLED)
+ buf[4] = 0x02; /* T2A and T2B pages enabled */
+ else
+ buf[4] = 0;
+
+ return ATA_FEATURE_SUB_MPAGE_LEN;
+}
+
/**
* ata_msense_control - Simulate MODE SENSE control mode page
* @dev: ATA device of interest
* @buf: output buffer
+ * @spg: sub-page code
* @changeable: whether changeable parameters are requested
*
* Generate a generic MODE SENSE control mode page.
@@ -2198,17 +2478,27 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)
* None.
*/
static unsigned int ata_msense_control(struct ata_device *dev, u8 *buf,
- bool changeable)
+ u8 spg, bool changeable)
{
- modecpy(buf, def_control_mpage, sizeof(def_control_mpage), changeable);
- if (changeable) {
- buf[2] |= (1 << 2); /* ata_mselect_control() */
- } else {
- bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE);
-
- buf[2] |= (d_sense << 2); /* descriptor format sense data */
+ unsigned int n;
+
+ switch (spg) {
+ case 0:
+ return ata_msense_control_spg0(dev, buf, changeable);
+ case CDL_T2A_SUB_MPAGE:
+ case CDL_T2B_SUB_MPAGE:
+ return ata_msense_control_spgt2(dev, buf, spg);
+ case ATA_FEATURE_SUB_MPAGE:
+ return ata_msense_control_ata_feature(dev, buf);
+ case ALL_SUB_MPAGES:
+ n = ata_msense_control_spg0(dev, buf, changeable);
+ n += ata_msense_control_spgt2(dev, buf + n, CDL_T2A_SUB_MPAGE);
+ n += ata_msense_control_spgt2(dev, buf + n, CDL_T2B_SUB_MPAGE);
+ n += ata_msense_control_ata_feature(dev, buf + n);
+ return n;
+ default:
+ return 0;
}
- return sizeof(def_control_mpage);
}
/**
@@ -2228,33 +2518,10 @@ static unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable)
return sizeof(def_rw_recovery_mpage);
}
-/*
- * We can turn this into a real blacklist if it's needed, for now just
- * blacklist any Maxtor BANC1G10 revision firmware
- */
-static int ata_dev_supports_fua(u16 *id)
-{
- unsigned char model[ATA_ID_PROD_LEN + 1], fw[ATA_ID_FW_REV_LEN + 1];
-
- if (!libata_fua)
- return 0;
- if (!ata_id_has_fua(id))
- return 0;
-
- ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model));
- ata_id_c_string(id, fw, ATA_ID_FW_REV, sizeof(fw));
-
- if (strcmp(model, "Maxtor"))
- return 1;
- if (strcmp(fw, "BANC1G10"))
- return 1;
-
- return 0; /* blacklisted */
-}
-
/**
* ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Simulate MODE SENSE commands. Assume this is invoked for direct
@@ -2264,10 +2531,10 @@ static int ata_dev_supports_fua(u16 *id)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_mode_sense(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
- u8 *scsicmd = args->cmd->cmnd, *p = rbuf;
+ u8 *scsicmd = cmd->cmnd, *p = rbuf;
static const u8 sat_blk_desc[] = {
0, 0, 0, 0, /* number of blocks: sat unspecified */
0,
@@ -2275,7 +2542,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
};
u8 pg, spg;
unsigned int ebd, page_control, six_byte;
- u8 dpofua, bp = 0xff;
+ u8 dpofua = 0, bp = 0xff;
u16 fp;
six_byte = (scsicmd[0] == MODE_SENSE);
@@ -2305,13 +2572,25 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
pg = scsicmd[2] & 0x3f;
spg = scsicmd[3];
+
/*
- * No mode subpages supported (yet) but asking for _all_
- * subpages may be valid
+ * Supported subpages: all subpages and sub-pages 07h, 08h and f2h of
+ * the control page.
*/
- if (spg && (spg != ALL_SUB_MPAGES)) {
- fp = 3;
- goto invalid_fld;
+ if (spg) {
+ switch (spg) {
+ case ALL_SUB_MPAGES:
+ break;
+ case CDL_T2A_SUB_MPAGE:
+ case CDL_T2B_SUB_MPAGE:
+ case ATA_FEATURE_SUB_MPAGE:
+ if (dev->flags & ATA_DFLAG_CDL && pg == CONTROL_MPAGE)
+ break;
+ fallthrough;
+ default:
+ fp = 3;
+ goto invalid_fld;
+ }
}
switch(pg) {
@@ -2320,17 +2599,17 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
break;
case CACHE_MPAGE:
- p += ata_msense_caching(args->id, p, page_control == 1);
+ p += ata_msense_caching(dev->id, p, page_control == 1);
break;
case CONTROL_MPAGE:
- p += ata_msense_control(args->dev, p, page_control == 1);
+ p += ata_msense_control(dev, p, spg, page_control == 1);
break;
case ALL_MPAGES:
p += ata_msense_rw_recovery(p, page_control == 1);
- p += ata_msense_caching(args->id, p, page_control == 1);
- p += ata_msense_control(args->dev, p, page_control == 1);
+ p += ata_msense_caching(dev->id, p, page_control == 1);
+ p += ata_msense_control(dev, p, spg, page_control == 1);
break;
default: /* invalid page code */
@@ -2338,9 +2617,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
goto invalid_fld;
}
- dpofua = 0;
- if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) &&
- (!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count))
+ if (dev->flags & ATA_DFLAG_FUA)
dpofua = 1 << 4;
if (six_byte) {
@@ -2350,32 +2627,33 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
rbuf[3] = sizeof(sat_blk_desc);
memcpy(rbuf + 4, sat_blk_desc, sizeof(sat_blk_desc));
}
- } else {
- unsigned int output_len = p - rbuf - 2;
- rbuf[0] = output_len >> 8;
- rbuf[1] = output_len;
- rbuf[3] |= dpofua;
- if (ebd) {
- rbuf[7] = sizeof(sat_blk_desc);
- memcpy(rbuf + 8, sat_blk_desc, sizeof(sat_blk_desc));
- }
+ return rbuf[0] + 1;
+ }
+
+ put_unaligned_be16(p - rbuf - 2, &rbuf[0]);
+ rbuf[3] |= dpofua;
+ if (ebd) {
+ rbuf[7] = sizeof(sat_blk_desc);
+ memcpy(rbuf + 8, sat_blk_desc, sizeof(sat_blk_desc));
}
- return 0;
+
+ return get_unaligned_be16(&rbuf[0]) + 2;
invalid_fld:
- ata_scsi_set_invalid_field(dev, args->cmd, fp, bp);
- return 1;
+ ata_scsi_set_invalid_field(dev, cmd, fp, bp);
+ return 0;
saving_not_supp:
- ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x39, 0x0);
+ ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x39, 0x0);
/* "Saving parameters not supported" */
- return 1;
+ return 0;
}
/**
* ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Simulate READ CAPACITY commands.
@@ -2383,9 +2661,10 @@ saving_not_supp:
* LOCKING:
* None.
*/
-static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_read_cap(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
+ u8 *scsicmd = cmd->cmnd;
u64 last_lba = dev->n_sectors - 1; /* LBA of the last block */
u32 sector_size; /* physical sector size in bytes */
u8 log2_per_phys;
@@ -2395,7 +2674,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
log2_per_phys = ata_id_log2_per_physical_sector(dev->id);
lowest_aligned = ata_id_logical_sector_offset(dev->id, log2_per_phys);
- if (args->cmd->cmnd[0] == READ_CAPACITY) {
+ if (scsicmd[0] == READ_CAPACITY) {
if (last_lba >= 0xffffffffULL)
last_lba = 0xffffffff;
@@ -2410,48 +2689,59 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
rbuf[5] = sector_size >> (8 * 2);
rbuf[6] = sector_size >> (8 * 1);
rbuf[7] = sector_size;
- } else {
- /* sector count, 64-bit */
- rbuf[0] = last_lba >> (8 * 7);
- rbuf[1] = last_lba >> (8 * 6);
- rbuf[2] = last_lba >> (8 * 5);
- rbuf[3] = last_lba >> (8 * 4);
- rbuf[4] = last_lba >> (8 * 3);
- rbuf[5] = last_lba >> (8 * 2);
- rbuf[6] = last_lba >> (8 * 1);
- rbuf[7] = last_lba;
- /* sector size */
- rbuf[ 8] = sector_size >> (8 * 3);
- rbuf[ 9] = sector_size >> (8 * 2);
- rbuf[10] = sector_size >> (8 * 1);
- rbuf[11] = sector_size;
-
- rbuf[12] = 0;
- rbuf[13] = log2_per_phys;
- rbuf[14] = (lowest_aligned >> 8) & 0x3f;
- rbuf[15] = lowest_aligned;
-
- if (ata_id_has_trim(args->id) &&
- !(dev->horkage & ATA_HORKAGE_NOTRIM)) {
- rbuf[14] |= 0x80; /* LBPME */
-
- if (ata_id_has_zero_after_trim(args->id) &&
- dev->horkage & ATA_HORKAGE_ZERO_AFTER_TRIM) {
- ata_dev_info(dev, "Enabling discard_zeroes_data\n");
- rbuf[14] |= 0x40; /* LBPRZ */
- }
+ return 8;
+ }
+
+ /*
+ * READ CAPACITY 16 command is defined as a service action
+ * (SERVICE_ACTION_IN_16 command).
+ */
+ if (scsicmd[0] != SERVICE_ACTION_IN_16 ||
+ (scsicmd[1] & 0x1f) != SAI_READ_CAPACITY_16) {
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ return 0;
+ }
+
+ /* sector count, 64-bit */
+ rbuf[0] = last_lba >> (8 * 7);
+ rbuf[1] = last_lba >> (8 * 6);
+ rbuf[2] = last_lba >> (8 * 5);
+ rbuf[3] = last_lba >> (8 * 4);
+ rbuf[4] = last_lba >> (8 * 3);
+ rbuf[5] = last_lba >> (8 * 2);
+ rbuf[6] = last_lba >> (8 * 1);
+ rbuf[7] = last_lba;
+
+ /* sector size */
+ rbuf[ 8] = sector_size >> (8 * 3);
+ rbuf[ 9] = sector_size >> (8 * 2);
+ rbuf[10] = sector_size >> (8 * 1);
+ rbuf[11] = sector_size;
+
+ if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC)
+ rbuf[12] = (1 << 4); /* RC_BASIS */
+ rbuf[13] = log2_per_phys;
+ rbuf[14] = (lowest_aligned >> 8) & 0x3f;
+ rbuf[15] = lowest_aligned;
+
+ if (ata_id_has_trim(dev->id) && !(dev->quirks & ATA_QUIRK_NOTRIM)) {
+ rbuf[14] |= 0x80; /* LBPME */
+
+ if (ata_id_has_zero_after_trim(dev->id) &&
+ dev->quirks & ATA_QUIRK_ZERO_AFTER_TRIM) {
+ ata_dev_info(dev, "Enabling discard_zeroes_data\n");
+ rbuf[14] |= 0x40; /* LBPRZ */
}
- if (ata_id_zoned_cap(args->id) ||
- args->dev->class == ATA_DEV_ZAC)
- rbuf[12] = (1 << 4); /* RC_BASIS */
}
- return 0;
+
+ return 16;
}
/**
* ata_scsiop_report_luns - Simulate REPORT LUNS command
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Simulate REPORT LUNS command.
@@ -2459,76 +2749,12 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_report_luns(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */
- return 0;
-}
-
-static void atapi_sense_complete(struct ata_queued_cmd *qc)
-{
- if (qc->err_mask && ((qc->err_mask & AC_ERR_DEV) == 0)) {
- /* FIXME: not quite right; we don't want the
- * translation of taskfile registers into
- * a sense descriptors, since that's only
- * correct for ATA, not ATAPI
- */
- ata_gen_passthru_sense(qc);
- }
-
- ata_qc_done(qc);
-}
-
-/* is it pointless to prefer PIO for "safety reasons"? */
-static inline int ata_pio_use_silly(struct ata_port *ap)
-{
- return (ap->flags & ATA_FLAG_PIO_DMA);
-}
-
-static void atapi_request_sense(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- struct scsi_cmnd *cmd = qc->scsicmd;
-
- memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-
-#ifdef CONFIG_ATA_SFF
- if (ap->ops->sff_tf_read)
- ap->ops->sff_tf_read(ap, &qc->tf);
-#endif
-
- /* fill these in, for the case where they are -not- overwritten */
- cmd->sense_buffer[0] = 0x70;
- cmd->sense_buffer[2] = qc->tf.error >> 4;
-
- ata_qc_reinit(qc);
-
- /* setup sg table and init transfer direction */
- sg_init_one(&qc->sgent, cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
- ata_sg_init(qc, &qc->sgent, 1);
- qc->dma_dir = DMA_FROM_DEVICE;
-
- memset(&qc->cdb, 0, qc->dev->cdb_len);
- qc->cdb[0] = REQUEST_SENSE;
- qc->cdb[4] = SCSI_SENSE_BUFFERSIZE;
-
- qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
- qc->tf.command = ATA_CMD_PACKET;
-
- if (ata_pio_use_silly(ap)) {
- qc->tf.protocol = ATAPI_PROT_DMA;
- qc->tf.feature |= ATAPI_PKT_DMA;
- } else {
- qc->tf.protocol = ATAPI_PROT_PIO;
- qc->tf.lbam = SCSI_SENSE_BUFFERSIZE;
- qc->tf.lbah = 0;
- }
- qc->nbytes = SCSI_SENSE_BUFFERSIZE;
-
- qc->complete_fn = atapi_sense_complete;
-
- ata_qc_issue(qc);
+ return 16;
}
/*
@@ -2556,18 +2782,11 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
struct scsi_cmnd *cmd = qc->scsicmd;
unsigned int err_mask = qc->err_mask;
- /* handle completion from new EH */
- if (unlikely(qc->ap->ops->error_handler &&
- (err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID))) {
+ /* handle completion from EH */
+ if (unlikely(err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID)) {
- if (!(qc->flags & ATA_QCFLAG_SENSE_VALID)) {
- /* FIXME: not quite right; we don't want the
- * translation of taskfile registers into a
- * sense descriptors, since that's only
- * correct for ATA, not ATAPI
- */
+ if (!(qc->flags & ATA_QCFLAG_SENSE_VALID))
ata_gen_passthru_sense(qc);
- }
/* SCSI EH automatically locks door if sdev->locked is
* set. Sometimes door lock request continues to
@@ -2590,23 +2809,10 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
return;
}
- /* successful completion or old EH failure path */
- if (unlikely(err_mask & AC_ERR_DEV)) {
- cmd->result = SAM_STAT_CHECK_CONDITION;
- atapi_request_sense(qc);
- return;
- } else if (unlikely(err_mask)) {
- /* FIXME: not quite right; we don't want the
- * translation of taskfile registers into
- * a sense descriptors, since that's only
- * correct for ATA, not ATAPI
- */
- ata_gen_passthru_sense(qc);
- } else {
- if (cmd->cmnd[0] == INQUIRY && (cmd->cmnd[1] & 0x03) == 0)
- atapi_fixup_inquiry(cmd);
- cmd->result = SAM_STAT_GOOD;
- }
+ /* successful completion path */
+ if (cmd->cmnd[0] == INQUIRY && (cmd->cmnd[1] & 0x03) == 0)
+ atapi_fixup_inquiry(cmd);
+ cmd->result = SAM_STAT_GOOD;
ata_qc_done(qc);
}
@@ -2703,18 +2909,36 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
return 0;
}
-static struct ata_device *ata_find_dev(struct ata_port *ap, int devno)
+static struct ata_device *ata_find_dev(struct ata_port *ap, unsigned int devno)
{
- if (!sata_pmp_attached(ap)) {
- if (likely(devno >= 0 &&
- devno < ata_link_max_devices(&ap->link)))
+ /*
+ * For the non-PMP case, ata_link_max_devices() returns 1 (SATA case),
+ * or 2 (IDE master + slave case). However, the former case includes
+ * libsas hosted devices which are numbered per scsi host, leading
+ * to devno potentially being larger than 0 but with each struct
+ * ata_device having its own struct ata_port and struct ata_link.
+ * To accommodate these, ignore devno and always use device number 0.
+ */
+ if (likely(!sata_pmp_attached(ap))) {
+ int link_max_devices = ata_link_max_devices(&ap->link);
+
+ if (link_max_devices == 1)
+ return &ap->link.device[0];
+
+ if (devno < link_max_devices)
return &ap->link.device[devno];
- } else {
- if (likely(devno >= 0 &&
- devno < ap->nr_pmp_links))
- return &ap->pmp_link[devno].device[0];
+
+ return NULL;
}
+ /*
+ * For PMP-attached devices, the device number corresponds to C
+ * (channel) of SCSI [H:C:I:L], indicating the port pmp link
+ * for the device.
+ */
+ if (devno < ap->nr_pmp_links)
+ return &ap->pmp_link[devno].device[0];
+
return NULL;
}
@@ -3168,8 +3392,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
}
scsi_16_lba_len(cdb, &block, &n_block);
- if (!unmap ||
- (dev->horkage & ATA_HORKAGE_NOTRIM) ||
+ if (!unmap || (dev->quirks & ATA_QUIRK_NOTRIM) ||
!ata_id_has_trim(dev->id)) {
fp = 1;
bp = 3;
@@ -3239,7 +3462,8 @@ invalid_opcode:
/**
* ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
- * @args: device MAINTENANCE_IN data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Yields a subset to satisfy scsi_report_opcode()
@@ -3247,18 +3471,23 @@ invalid_opcode:
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
- u8 *cdb = args->cmd->cmnd;
- u8 supported = 0;
- unsigned int err = 0;
+ u8 *cdb = cmd->cmnd;
+ u8 supported = 0, cdlp = 0, rwcdlp = 0;
+
+ if ((cdb[1] & 0x1f) != MI_REPORT_SUPPORTED_OPERATION_CODES) {
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ return 0;
+ }
- if (cdb[2] != 1) {
+ if (cdb[2] != 1 && cdb[2] != 3) {
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
- err = 2;
- goto out;
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ return 0;
}
+
switch (cdb[3]) {
case INQUIRY:
case MODE_SENSE:
@@ -3268,6 +3497,7 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
case REPORT_LUNS:
case REQUEST_SENSE:
case SYNCHRONIZE_CACHE:
+ case SYNCHRONIZE_CACHE_16:
case REZERO_UNIT:
case SEEK_6:
case SEEK_10:
@@ -3276,10 +3506,8 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
case MAINTENANCE_IN:
case READ_6:
case READ_10:
- case READ_16:
case WRITE_6:
case WRITE_10:
- case WRITE_16:
case ATA_12:
case ATA_16:
case VERIFY:
@@ -3289,6 +3517,28 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
case START_STOP:
supported = 3;
break;
+ case READ_16:
+ supported = 3;
+ if (dev->flags & ATA_DFLAG_CDL) {
+ /*
+ * CDL read descriptors map to the T2A page, that is,
+ * rwcdlp = 0x01 and cdlp = 0x01
+ */
+ rwcdlp = 0x01;
+ cdlp = 0x01 << 3;
+ }
+ break;
+ case WRITE_16:
+ supported = 3;
+ if (dev->flags & ATA_DFLAG_CDL) {
+ /*
+ * CDL write descriptors map to the T2B page, that is,
+ * rwcdlp = 0x01 and cdlp = 0x02
+ */
+ rwcdlp = 0x01;
+ cdlp = 0x02 << 3;
+ }
+ break;
case ZBC_IN:
case ZBC_OUT:
if (ata_id_zoned_cap(dev->id) ||
@@ -3303,9 +3553,12 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
default:
break;
}
-out:
- rbuf[1] = supported; /* supported */
- return err;
+
+ /* One command format */
+ rbuf[0] = rwcdlp;
+ rbuf[1] = cdlp | supported;
+
+ return 4;
}
/**
@@ -3594,20 +3847,11 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc,
return 0;
}
-/**
- * ata_mselect_control - Simulate MODE SELECT for control page
- * @qc: Storage for translated ATA taskfile
- * @buf: input buffer
- * @len: number of valid bytes in the input buffer
- * @fp: out parameter for the failed field on error
- *
- * Prepare a taskfile to modify caching information for the device.
- *
- * LOCKING:
- * None.
+/*
+ * Simulate MODE SELECT control mode page, sub-page 0.
*/
-static int ata_mselect_control(struct ata_queued_cmd *qc,
- const u8 *buf, int len, u16 *fp)
+static int ata_mselect_control_spg0(struct ata_queued_cmd *qc,
+ const u8 *buf, int len, u16 *fp)
{
struct ata_device *dev = qc->dev;
u8 mpage[CONTROL_MPAGE_LEN];
@@ -3629,7 +3873,7 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
/*
* Check that read-only bits are not modified.
*/
- ata_msense_control(dev, mpage, false);
+ ata_msense_control_spg0(dev, mpage, false);
for (i = 0; i < CONTROL_MPAGE_LEN - 2; i++) {
if (i == 0)
continue;
@@ -3645,6 +3889,88 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
return 0;
}
+/*
+ * Translate MODE SELECT control mode page, sub-page f2h (ATA feature mode
+ * page) into a SET FEATURES command.
+ */
+static int ata_mselect_control_ata_feature(struct ata_queued_cmd *qc,
+ const u8 *buf, int len, u16 *fp)
+{
+ struct ata_device *dev = qc->dev;
+ struct ata_taskfile *tf = &qc->tf;
+ u8 cdl_action;
+
+ /*
+ * The first four bytes of ATA Feature Control mode page are a header,
+ * so offsets in mpage are off by 4 compared to buf. Same for len.
+ */
+ if (len != ATA_FEATURE_SUB_MPAGE_LEN - 4) {
+ *fp = min(len, ATA_FEATURE_SUB_MPAGE_LEN - 4);
+ return -EINVAL;
+ }
+
+ /* Check cdl_ctrl */
+ switch (buf[0] & 0x03) {
+ case 0:
+ /* Disable CDL */
+ ata_dev_dbg(dev, "Disabling CDL\n");
+ cdl_action = 0;
+ dev->flags &= ~ATA_DFLAG_CDL_ENABLED;
+ break;
+ case 0x02:
+ /*
+ * Enable CDL. Since CDL is mutually exclusive with NCQ
+ * priority, allow this only if NCQ priority is disabled.
+ */
+ if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED) {
+ ata_dev_err(dev,
+ "NCQ priority must be disabled to enable CDL\n");
+ return -EINVAL;
+ }
+ ata_dev_dbg(dev, "Enabling CDL\n");
+ cdl_action = 1;
+ dev->flags |= ATA_DFLAG_CDL_ENABLED;
+ break;
+ default:
+ *fp = 0;
+ return -EINVAL;
+ }
+
+ tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+ tf->protocol = ATA_PROT_NODATA;
+ tf->command = ATA_CMD_SET_FEATURES;
+ tf->feature = SETFEATURES_CDL;
+ tf->nsect = cdl_action;
+
+ return 1;
+}
+
+/**
+ * ata_mselect_control - Simulate MODE SELECT for control page
+ * @qc: Storage for translated ATA taskfile
+ * @spg: target sub-page of the control page
+ * @buf: input buffer
+ * @len: number of valid bytes in the input buffer
+ * @fp: out parameter for the failed field on error
+ *
+ * Prepare a taskfile to modify caching information for the device.
+ *
+ * LOCKING:
+ * None.
+ */
+static int ata_mselect_control(struct ata_queued_cmd *qc, u8 spg,
+ const u8 *buf, int len, u16 *fp)
+{
+ switch (spg) {
+ case 0:
+ return ata_mselect_control_spg0(qc, buf, len, fp);
+ case ATA_FEATURE_SUB_MPAGE:
+ return ata_mselect_control_ata_feature(qc, buf, len, fp);
+ default:
+ return -EINVAL;
+ }
+}
+
/**
* ata_scsi_mode_select_xlat - Simulate MODE SELECT 6, 10 commands
* @qc: Storage for translated ATA taskfile
@@ -3662,7 +3988,7 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
const u8 *cdb = scmd->cmnd;
u8 pg, spg;
unsigned six_byte, pg_len, hdr_len, bd_len;
- int len;
+ int len, ret;
u16 fp = (u16)-1;
u8 bp = 0xff;
u8 buffer[64];
@@ -3747,13 +4073,29 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
}
/*
- * No mode subpages supported (yet) but asking for _all_
- * subpages may be valid
+ * Supported subpages: all subpages and ATA feature sub-page f2h of
+ * the control page.
*/
- if (spg && (spg != ALL_SUB_MPAGES)) {
- fp = (p[0] & 0x40) ? 1 : 0;
- fp += hdr_len + bd_len;
- goto invalid_param;
+ if (spg) {
+ switch (spg) {
+ case ALL_SUB_MPAGES:
+ /* All subpages is not supported for the control page */
+ if (pg == CONTROL_MPAGE) {
+ fp = (p[0] & 0x40) ? 1 : 0;
+ fp += hdr_len + bd_len;
+ goto invalid_param;
+ }
+ break;
+ case ATA_FEATURE_SUB_MPAGE:
+ if (qc->dev->flags & ATA_DFLAG_CDL &&
+ pg == CONTROL_MPAGE)
+ break;
+ fallthrough;
+ default:
+ fp = (p[0] & 0x40) ? 1 : 0;
+ fp += hdr_len + bd_len;
+ goto invalid_param;
+ }
}
if (pg_len > len)
goto invalid_param_len;
@@ -3766,14 +4108,16 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
}
break;
case CONTROL_MPAGE:
- if (ata_mselect_control(qc, p, pg_len, &fp) < 0) {
+ ret = ata_mselect_control(qc, spg, p, pg_len, &fp);
+ if (ret < 0) {
fp += hdr_len + bd_len;
goto invalid_param;
- } else {
- goto skip; /* No ATA command to send */
}
+ if (!ret)
+ goto skip; /* No ATA command to send */
break;
- default: /* invalid page code */
+ default:
+ /* Invalid page code */
fp = bd_len + hdr_len;
goto invalid_param;
}
@@ -3926,6 +4270,7 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
return ata_scsi_write_same_xlat;
case SYNCHRONIZE_CACHE:
+ case SYNCHRONIZE_CACHE_16:
if (ata_try_flush_cache(dev))
return ata_scsi_flush_xlat;
break;
@@ -3966,9 +4311,20 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev)
{
+ struct ata_port *ap = dev->link->ap;
u8 scsi_op = scmd->cmnd[0];
ata_xlat_func_t xlat_func;
+ /*
+ * scsi_queue_rq() will defer commands if scsi_host_in_recovery().
+ * However, this check is done without holding the ap->lock (a libata
+ * specific lock), so we can have received an error irq since then,
+ * therefore we must check if EH is pending or running, while holding
+ * ap->lock.
+ */
+ if (ata_port_eh_scheduled(ap))
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
if (unlikely(!scmd->cmd_len))
goto bad_cdb_len;
@@ -4067,78 +4423,26 @@ EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
{
- struct ata_scsi_args args;
const u8 *scsicmd = cmd->cmnd;
u8 tmp8;
- args.dev = dev;
- args.id = dev->id;
- args.cmd = cmd;
-
switch(scsicmd[0]) {
case INQUIRY:
- if (scsicmd[1] & 2) /* is CmdDt set? */
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
- else switch (scsicmd[2]) {
- case 0x00:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00);
- break;
- case 0x80:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80);
- break;
- case 0x83:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83);
- break;
- case 0x89:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_89);
- break;
- case 0xb0:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b0);
- break;
- case 0xb1:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b1);
- break;
- case 0xb2:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2);
- break;
- case 0xb6:
- if (dev->flags & ATA_DFLAG_ZAC)
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b6);
- else
- ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- break;
- case 0xb9:
- if (dev->cpr_log)
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b9);
- else
- ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- break;
- default:
- ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- break;
- }
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_inquiry);
break;
case MODE_SENSE:
case MODE_SENSE_10:
- ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_mode_sense);
break;
case READ_CAPACITY:
- ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
- break;
-
case SERVICE_ACTION_IN_16:
- if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
- ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
- else
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_read_cap);
break;
case REPORT_LUNS:
- ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_report_luns);
break;
case REQUEST_SENSE:
@@ -4149,6 +4453,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
* turning this into a no-op.
*/
case SYNCHRONIZE_CACHE:
+ case SYNCHRONIZE_CACHE_16:
fallthrough;
/* no-op's, complete with success */
@@ -4165,10 +4470,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
break;
case MAINTENANCE_IN:
- if (scsicmd[1] == MI_REPORT_SUPPORTED_OPERATION_CODES)
- ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
- else
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_maint_in);
break;
/* all other commands */
@@ -4181,7 +4483,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
scsi_done(cmd);
}
-int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
+int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *sht)
{
int i, rc;
@@ -4335,24 +4637,23 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
* ata_scsi_offline_dev - offline attached SCSI device
* @dev: ATA device to offline attached SCSI device for
*
- * This function is called from ata_eh_hotplug() and responsible
- * for taking the SCSI device attached to @dev offline. This
- * function is called with host lock which protects dev->sdev
- * against clearing.
+ * This function is called from ata_eh_detach_dev() and is responsible for
+ * taking the SCSI device attached to @dev offline. This function is
+ * called with host lock which protects dev->sdev against clearing.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
- * 1 if attached SCSI device exists, 0 otherwise.
+ * true if attached SCSI device exists, false otherwise.
*/
-int ata_scsi_offline_dev(struct ata_device *dev)
+bool ata_scsi_offline_dev(struct ata_device *dev)
{
if (dev->sdev) {
scsi_device_set_state(dev->sdev, SDEV_OFFLINE);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/**
@@ -4422,16 +4723,15 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
ata_for_each_dev(dev, link, ALL) {
unsigned long flags;
- if (!(dev->flags & ATA_DFLAG_DETACHED))
+ spin_lock_irqsave(ap->lock, flags);
+ if (!(dev->flags & ATA_DFLAG_DETACHED)) {
+ spin_unlock_irqrestore(ap->lock, flags);
continue;
+ }
- spin_lock_irqsave(ap->lock, flags);
dev->flags &= ~ATA_DFLAG_DETACHED;
spin_unlock_irqrestore(ap->lock, flags);
- if (zpodd_dev_enabled(dev))
- zpodd_exit(dev);
-
ata_scsi_remove_dev(dev);
}
}
@@ -4514,9 +4814,6 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned long flags;
int devno, rc = 0;
- if (!ap->ops->error_handler)
- return -EOPNOTSUPP;
-
if (lun != SCAN_WILD_CARD && lun)
return -EINVAL;
@@ -4574,10 +4871,12 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
void ata_scsi_dev_rescan(struct work_struct *work)
{
struct ata_port *ap =
- container_of(work, struct ata_port, scsi_rescan_task);
+ container_of(work, struct ata_port, scsi_rescan_task.work);
struct ata_link *link;
struct ata_device *dev;
unsigned long flags;
+ bool do_resume;
+ int ret = 0;
mutex_lock(&ap->scsi_scan_mutex);
spin_lock_irqsave(ap->lock, flags);
@@ -4586,18 +4885,45 @@ void ata_scsi_dev_rescan(struct work_struct *work)
ata_for_each_dev(dev, link, ENABLED) {
struct scsi_device *sdev = dev->sdev;
+ /*
+ * If the port was suspended before this was scheduled,
+ * bail out.
+ */
+ if (ap->pflags & ATA_PFLAG_SUSPENDED)
+ goto unlock_ap;
+
if (!sdev)
continue;
if (scsi_device_get(sdev))
continue;
+ do_resume = dev->flags & ATA_DFLAG_RESUMING;
+
spin_unlock_irqrestore(ap->lock, flags);
- scsi_rescan_device(&(sdev->sdev_gendev));
+ if (do_resume) {
+ ret = scsi_resume_device(sdev);
+ if (ret == -EWOULDBLOCK) {
+ scsi_device_put(sdev);
+ goto unlock_scan;
+ }
+ dev->flags &= ~ATA_DFLAG_RESUMING;
+ }
+ ret = scsi_rescan_device(sdev);
scsi_device_put(sdev);
spin_lock_irqsave(ap->lock, flags);
+
+ if (ret)
+ goto unlock_ap;
}
}
+unlock_ap:
spin_unlock_irqrestore(ap->lock, flags);
+unlock_scan:
mutex_unlock(&ap->scsi_scan_mutex);
+
+ /* Reschedule with a delay if scsi_rescan_device() returned an error */
+ if (ret)
+ schedule_delayed_work(&ap->scsi_rescan_task,
+ msecs_to_jiffies(5));
}
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index b1666adc1c3a..785b6e371abf 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -26,16 +26,15 @@ static struct workqueue_struct *ata_sff_wq;
const struct ata_port_operations ata_sff_port_ops = {
.inherits = &ata_base_port_ops,
- .qc_prep = ata_noop_qc_prep,
.qc_issue = ata_sff_qc_issue,
.qc_fill_rtf = ata_sff_qc_fill_rtf,
.freeze = ata_sff_freeze,
.thaw = ata_sff_thaw,
- .prereset = ata_sff_prereset,
- .softreset = ata_sff_softreset,
- .hardreset = sata_sff_hardreset,
- .postreset = ata_sff_postreset,
+ .reset.prereset = ata_sff_prereset,
+ .reset.softreset = ata_sff_softreset,
+ .reset.hardreset = sata_sff_hardreset,
+ .reset.postreset = ata_sff_postreset,
.error_handler = ata_sff_error_handler,
.sff_dev_select = ata_sff_dev_select,
@@ -184,62 +183,6 @@ void ata_sff_dma_pause(struct ata_port *ap)
}
EXPORT_SYMBOL_GPL(ata_sff_dma_pause);
-/**
- * ata_sff_busy_sleep - sleep until BSY clears, or timeout
- * @ap: port containing status register to be polled
- * @tmout_pat: impatience timeout in msecs
- * @tmout: overall timeout in msecs
- *
- * Sleep until ATA Status register bit BSY clears,
- * or a timeout occurs.
- *
- * LOCKING:
- * Kernel thread context (may sleep).
- *
- * RETURNS:
- * 0 on success, -errno otherwise.
- */
-int ata_sff_busy_sleep(struct ata_port *ap,
- unsigned long tmout_pat, unsigned long tmout)
-{
- unsigned long timer_start, timeout;
- u8 status;
-
- status = ata_sff_busy_wait(ap, ATA_BUSY, 300);
- timer_start = jiffies;
- timeout = ata_deadline(timer_start, tmout_pat);
- while (status != 0xff && (status & ATA_BUSY) &&
- time_before(jiffies, timeout)) {
- ata_msleep(ap, 50);
- status = ata_sff_busy_wait(ap, ATA_BUSY, 3);
- }
-
- if (status != 0xff && (status & ATA_BUSY))
- ata_port_warn(ap,
- "port is slow to respond, please be patient (Status 0x%x)\n",
- status);
-
- timeout = ata_deadline(timer_start, tmout);
- while (status != 0xff && (status & ATA_BUSY) &&
- time_before(jiffies, timeout)) {
- ata_msleep(ap, 50);
- status = ap->ops->sff_check_status(ap);
- }
-
- if (status == 0xff)
- return -ENODEV;
-
- if (status & ATA_BUSY) {
- ata_port_err(ap,
- "port failed to respond (%lu secs, Status 0x%x)\n",
- DIV_ROUND_UP(tmout, 1000), status);
- return -EBUSY;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(ata_sff_busy_sleep);
-
static int ata_sff_check_ready(struct ata_link *link)
{
u8 status = link->ap->ops->sff_check_status(link->ap);
@@ -658,7 +601,7 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct page *page;
- unsigned int offset;
+ unsigned int offset, count;
if (!qc->cursg) {
qc->curbytes = qc->nbytes;
@@ -671,28 +614,30 @@ static void ata_pio_sector(struct ata_queued_cmd *qc)
offset = qc->cursg->offset + qc->cursg_ofs;
/* get the current page and offset */
- page = nth_page(page, (offset >> PAGE_SHIFT));
+ page += offset >> PAGE_SHIFT;
offset %= PAGE_SIZE;
- trace_ata_sff_pio_transfer_data(qc, offset, qc->sect_size);
+ /* don't overrun current sg */
+ count = min(qc->cursg->length - qc->cursg_ofs, qc->sect_size);
+
+ trace_ata_sff_pio_transfer_data(qc, offset, count);
/*
* Split the transfer when it splits a page boundary. Note that the
* split still has to be dword aligned like all ATA data transfers.
*/
WARN_ON_ONCE(offset % 4);
- if (offset + qc->sect_size > PAGE_SIZE) {
+ if (offset + count > PAGE_SIZE) {
unsigned int split_len = PAGE_SIZE - offset;
ata_pio_xfer(qc, page, offset, split_len);
- ata_pio_xfer(qc, nth_page(page, 1), 0,
- qc->sect_size - split_len);
+ ata_pio_xfer(qc, page + 1, 0, count - split_len);
} else {
- ata_pio_xfer(qc, page, offset, qc->sect_size);
+ ata_pio_xfer(qc, page, offset, count);
}
- qc->curbytes += qc->sect_size;
- qc->cursg_ofs += qc->sect_size;
+ qc->curbytes += count;
+ qc->cursg_ofs += count;
if (qc->cursg_ofs == qc->cursg->length) {
qc->cursg = sg_next(qc->cursg);
@@ -776,7 +721,7 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
* @qc: Command on going
* @bytes: number of bytes
*
- * Transfer Transfer data from/to the ATAPI device.
+ * Transfer data from/to the ATAPI device.
*
* LOCKING:
* Inherited from caller.
@@ -806,7 +751,7 @@ next_sg:
offset = sg->offset + qc->cursg_ofs;
/* get the current page and offset */
- page = nth_page(page, (offset >> PAGE_SHIFT));
+ page += offset >> PAGE_SHIFT;
offset %= PAGE_SIZE;
/* don't overrun current sg */
@@ -939,31 +884,21 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq)
{
struct ata_port *ap = qc->ap;
- if (ap->ops->error_handler) {
- if (in_wq) {
- /* EH might have kicked in while host lock is
- * released.
- */
- qc = ata_qc_from_tag(ap, qc->tag);
- if (qc) {
- if (likely(!(qc->err_mask & AC_ERR_HSM))) {
- ata_sff_irq_on(ap);
- ata_qc_complete(qc);
- } else
- ata_port_freeze(ap);
- }
- } else {
- if (likely(!(qc->err_mask & AC_ERR_HSM)))
+ if (in_wq) {
+ /* EH might have kicked in while host lock is released. */
+ qc = ata_qc_from_tag(ap, qc->tag);
+ if (qc) {
+ if (likely(!(qc->err_mask & AC_ERR_HSM))) {
+ ata_sff_irq_on(ap);
ata_qc_complete(qc);
- else
+ } else
ata_port_freeze(ap);
}
} else {
- if (in_wq) {
- ata_sff_irq_on(ap);
- ata_qc_complete(qc);
- } else
+ if (likely(!(qc->err_mask & AC_ERR_HSM)))
ata_qc_complete(qc);
+ else
+ ata_port_freeze(ap);
}
}
@@ -1036,7 +971,7 @@ fsm_start:
* We ignore ERR here to workaround and proceed sending
* the CDB.
*/
- if (!(qc->dev->horkage & ATA_HORKAGE_STUCK_ERR)) {
+ if (!(qc->dev->quirks & ATA_QUIRK_STUCK_ERR)) {
ata_ehi_push_desc(ehi, "ST_FIRST: "
"DRQ=1 with device error, "
"dev_stat 0x%X", status);
@@ -1111,8 +1046,8 @@ fsm_start:
* IDENTIFY, it's likely a phantom
* device. Mark hint.
*/
- if (qc->dev->horkage &
- ATA_HORKAGE_DIAGNOSTIC)
+ if (qc->dev->quirks &
+ ATA_QUIRK_DIAGNOSTIC)
qc->err_mask |=
AC_ERR_NODEV_HINT;
} else {
@@ -1433,14 +1368,10 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_issue);
*
* LOCKING:
* spin_lock_irqsave(host lock)
- *
- * RETURNS:
- * true indicating that result TF is successfully filled.
*/
-bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc)
+void ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc)
{
qc->ap->ops->sff_tf_read(qc->ap, &qc->result_tf);
- return true;
}
EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
@@ -1832,7 +1763,7 @@ unsigned int ata_sff_dev_classify(struct ata_device *dev, int present,
/* see if device passed diags: continue and warn later */
if (err == 0)
/* diagnostic fail : do nothing _YET_ */
- dev->horkage |= ATA_HORKAGE_DIAGNOSTIC;
+ dev->quirks |= ATA_QUIRK_DIAGNOSTIC;
else if (err == 1)
/* do nothing */ ;
else if ((dev->devno == 0) && (err == 0x81))
@@ -1851,7 +1782,7 @@ unsigned int ata_sff_dev_classify(struct ata_device *dev, int present,
* device signature is invalid with diagnostic
* failure.
*/
- if (present && (dev->horkage & ATA_HORKAGE_DIAGNOSTIC))
+ if (present && (dev->quirks & ATA_QUIRK_DIAGNOSTIC))
class = ATA_DEV_ATA;
else
class = ATA_DEV_NONE;
@@ -2031,7 +1962,7 @@ int sata_sff_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_eh_context *ehc = &link->eh_context;
- const unsigned long *timing = sata_ehc_deb_timing(ehc);
+ const unsigned int *timing = sata_ehc_deb_timing(ehc);
bool online;
int rc;
@@ -2123,13 +2054,11 @@ EXPORT_SYMBOL_GPL(ata_sff_drain_fifo);
*/
void ata_sff_error_handler(struct ata_port *ap)
{
- ata_reset_fn_t softreset = ap->ops->softreset;
- ata_reset_fn_t hardreset = ap->ops->hardreset;
struct ata_queued_cmd *qc;
unsigned long flags;
qc = __ata_qc_from_tag(ap, ap->link.active_tag);
- if (qc && !(qc->flags & ATA_QCFLAG_FAILED))
+ if (qc && !(qc->flags & ATA_QCFLAG_EH))
qc = NULL;
spin_lock_irqsave(ap->lock, flags);
@@ -2146,13 +2075,7 @@ void ata_sff_error_handler(struct ata_port *ap)
spin_unlock_irqrestore(ap->lock, flags);
- /* ignore built-in hardresets if SCR access is not available */
- if ((hardreset == sata_std_hardreset ||
- hardreset == sata_sff_hardreset) && !sata_scr_valid(&ap->link))
- hardreset = NULL;
-
- ata_do_eh(ap, ap->ops->prereset, softreset, hardreset,
- ap->ops->postreset);
+ ata_std_error_handler(ap);
}
EXPORT_SYMBOL_GPL(ata_sff_error_handler);
@@ -2341,7 +2264,7 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_prepare_host);
*/
int ata_pci_sff_activate_host(struct ata_host *host,
irq_handler_t irq_handler,
- struct scsi_host_template *sht)
+ const struct scsi_host_template *sht)
{
struct device *dev = host->dev;
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2386,7 +2309,7 @@ int ata_pci_sff_activate_host(struct ata_host *host,
for (i = 0; i < 2; i++) {
if (ata_port_is_dummy(host->ports[i]))
continue;
- ata_port_desc(host->ports[i], "irq %d", pdev->irq);
+ ata_port_desc_misc(host->ports[i], pdev->irq);
}
} else if (legacy_mode) {
if (!ata_port_is_dummy(host->ports[0])) {
@@ -2396,8 +2319,8 @@ int ata_pci_sff_activate_host(struct ata_host *host,
if (rc)
goto out;
- ata_port_desc(host->ports[0], "irq %d",
- ATA_PRIMARY_IRQ(pdev));
+ ata_port_desc_misc(host->ports[0],
+ ATA_PRIMARY_IRQ(pdev));
}
if (!ata_port_is_dummy(host->ports[1])) {
@@ -2407,8 +2330,8 @@ int ata_pci_sff_activate_host(struct ata_host *host,
if (rc)
goto out;
- ata_port_desc(host->ports[1], "irq %d",
- ATA_SECONDARY_IRQ(pdev));
+ ata_port_desc_misc(host->ports[1],
+ ATA_SECONDARY_IRQ(pdev));
}
}
@@ -2438,7 +2361,7 @@ static const struct ata_port_info *ata_sff_find_valid_pi(
static int ata_pci_init_one(struct pci_dev *pdev,
const struct ata_port_info * const *ppi,
- struct scsi_host_template *sht, void *host_priv,
+ const struct scsi_host_template *sht, void *host_priv,
int hflags, bool bmdma)
{
struct device *dev = &pdev->dev;
@@ -2512,7 +2435,7 @@ out:
*/
int ata_pci_sff_init_one(struct pci_dev *pdev,
const struct ata_port_info * const *ppi,
- struct scsi_host_template *sht, void *host_priv, int hflag)
+ const struct scsi_host_template *sht, void *host_priv, int hflag)
{
return ata_pci_init_one(pdev, ppi, sht, host_priv, hflag, 0);
}
@@ -2852,7 +2775,7 @@ void ata_bmdma_error_handler(struct ata_port *ap)
bool thaw = false;
qc = __ata_qc_from_tag(ap, ap->link.active_tag);
- if (qc && !(qc->flags & ATA_QCFLAG_FAILED))
+ if (qc && !(qc->flags & ATA_QCFLAG_EH))
qc = NULL;
/* reset PIO HSM and stop DMA engine */
@@ -3102,6 +3025,7 @@ EXPORT_SYMBOL_GPL(ata_bmdma_port_start32);
*/
int ata_pci_bmdma_clear_simplex(struct pci_dev *pdev)
{
+#ifdef CONFIG_HAS_IOPORT
unsigned long bmdma = pci_resource_start(pdev, 4);
u8 simplex;
@@ -3114,6 +3038,9 @@ int ata_pci_bmdma_clear_simplex(struct pci_dev *pdev)
if (simplex & 0x80)
return -EOPNOTSUPP;
return 0;
+#else
+ return -ENOENT;
+#endif /* CONFIG_HAS_IOPORT */
}
EXPORT_SYMBOL_GPL(ata_pci_bmdma_clear_simplex);
@@ -3235,7 +3162,7 @@ EXPORT_SYMBOL_GPL(ata_pci_bmdma_prepare_host);
*/
int ata_pci_bmdma_init_one(struct pci_dev *pdev,
const struct ata_port_info * const * ppi,
- struct scsi_host_template *sht, void *host_priv,
+ const struct scsi_host_template *sht, void *host_priv,
int hflags)
{
return ata_pci_init_one(pdev, ppi, sht, host_priv, hflags, 1);
@@ -3264,7 +3191,8 @@ void ata_sff_port_init(struct ata_port *ap)
int __init ata_sff_init(void)
{
- ata_sff_wq = alloc_workqueue("ata_sff", WQ_MEM_RECLAIM, WQ_MAX_ACTIVE);
+ ata_sff_wq = alloc_workqueue("ata_sff", WQ_MEM_RECLAIM | WQ_PERCPU,
+ WQ_MAX_ACTIVE);
if (!ata_sff_wq)
return -ENOMEM;
diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c
index e0e4d0d5a100..9b5363fd0ab0 100644
--- a/drivers/ata/libata-trace.c
+++ b/drivers/ata/libata-trace.c
@@ -142,7 +142,7 @@ libata_trace_parse_qc_flags(struct trace_seq *p, unsigned int qc_flags)
trace_seq_printf(p, "QUIET ");
if (qc_flags & ATA_QCFLAG_RETRY)
trace_seq_printf(p, "RETRY ");
- if (qc_flags & ATA_QCFLAG_FAILED)
+ if (qc_flags & ATA_QCFLAG_EH)
trace_seq_printf(p, "FAILED ");
if (qc_flags & ATA_QCFLAG_SENSE_VALID)
trace_seq_printf(p, "SENSE_VALID ");
diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c
index a7e9a75410a3..62415fe67a11 100644
--- a/drivers/ata/libata-transport.c
+++ b/drivers/ata/libata-transport.c
@@ -80,12 +80,6 @@ struct ata_internal {
#define transport_class_to_port(dev) \
tdev_to_port((dev)->parent)
-
-/* Device objects are always created whit link objects */
-static int ata_tdev_add(struct ata_device *dev);
-static void ata_tdev_delete(struct ata_device *dev);
-
-
/*
* Hack to allow attributes of the same name in different objects.
*/
@@ -208,7 +202,7 @@ show_ata_port_##name(struct device *dev, \
{ \
struct ata_port *ap = transport_class_to_port(dev); \
\
- return scnprintf(buf, 20, format_string, cast ap->field); \
+ return sysfs_emit(buf, format_string, cast ap->field); \
}
#define ata_port_simple_attr(field, name, format_string, type) \
@@ -217,7 +211,8 @@ static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL)
ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int);
ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long);
-ata_port_simple_attr(local_port_no, port_no, "%u\n", unsigned int);
+/* We want the port_no sysfs attibute to start at 1 (ap->port_no starts at 0) */
+ata_port_simple_attr(port_no + 1, port_no, "%u\n", unsigned int);
static DECLARE_TRANSPORT_CLASS(ata_port_class,
"ata_port", NULL, NULL, NULL);
@@ -265,6 +260,11 @@ void ata_tport_delete(struct ata_port *ap)
transport_destroy_device(dev);
put_device(dev);
}
+EXPORT_SYMBOL_GPL(ata_tport_delete);
+
+static const struct device_type ata_port_sas_type = {
+ .name = ATA_PORT_TYPE_NAME,
+};
/** ata_tport_add - initialize a transport ATA port structure
*
@@ -283,7 +283,10 @@ int ata_tport_add(struct device *parent,
struct device *dev = &ap->tdev;
device_initialize(dev);
- dev->type = &ata_port_type;
+ if (ap->flags & ATA_FLAG_SAS_HOST)
+ dev->type = &ata_port_sas_type;
+ else
+ dev->type = &ata_port_type;
dev->parent = parent;
ata_host_get(ap->host);
@@ -301,7 +304,9 @@ int ata_tport_add(struct device *parent,
pm_runtime_enable(dev);
pm_runtime_forbid(dev);
- transport_add_device(dev);
+ error = transport_add_device(dev);
+ if (error)
+ goto tport_transport_add_err;
transport_configure_device(dev);
error = ata_tlink_add(&ap->link);
@@ -312,14 +317,15 @@ int ata_tport_add(struct device *parent,
tport_link_err:
transport_remove_device(dev);
+ tport_transport_add_err:
device_del(dev);
tport_err:
transport_destroy_device(dev);
put_device(dev);
- ata_host_put(ap->host);
return error;
}
+EXPORT_SYMBOL_GPL(ata_tport_add);
/**
* ata_port_classify - determine device type based on ATA-spec signature
@@ -353,132 +359,6 @@ unsigned int ata_port_classify(struct ata_port *ap,
EXPORT_SYMBOL_GPL(ata_port_classify);
/*
- * ATA link attributes
- */
-static int noop(int x) { return x; }
-
-#define ata_link_show_linkspeed(field, format) \
-static ssize_t \
-show_ata_link_##field(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct ata_link *link = transport_class_to_link(dev); \
- \
- return sprintf(buf, "%s\n", sata_spd_string(format(link->field))); \
-}
-
-#define ata_link_linkspeed_attr(field, format) \
- ata_link_show_linkspeed(field, format) \
-static DEVICE_ATTR(field, S_IRUGO, show_ata_link_##field, NULL)
-
-ata_link_linkspeed_attr(hw_sata_spd_limit, fls);
-ata_link_linkspeed_attr(sata_spd_limit, fls);
-ata_link_linkspeed_attr(sata_spd, noop);
-
-
-static DECLARE_TRANSPORT_CLASS(ata_link_class,
- "ata_link", NULL, NULL, NULL);
-
-static void ata_tlink_release(struct device *dev)
-{
-}
-
-/**
- * ata_is_link -- check if a struct device represents a ATA link
- * @dev: device to check
- *
- * Returns:
- * %1 if the device represents a ATA link, %0 else
- */
-static int ata_is_link(const struct device *dev)
-{
- return dev->release == ata_tlink_release;
-}
-
-static int ata_tlink_match(struct attribute_container *cont,
- struct device *dev)
-{
- struct ata_internal* i = to_ata_internal(ata_scsi_transport_template);
- if (!ata_is_link(dev))
- return 0;
- return &i->link_attr_cont.ac == cont;
-}
-
-/**
- * ata_tlink_delete -- remove ATA LINK
- * @link: ATA LINK to remove
- *
- * Removes the specified ATA LINK. remove associated ATA device(s) as well.
- */
-void ata_tlink_delete(struct ata_link *link)
-{
- struct device *dev = &link->tdev;
- struct ata_device *ata_dev;
-
- ata_for_each_dev(ata_dev, link, ALL) {
- ata_tdev_delete(ata_dev);
- }
-
- transport_remove_device(dev);
- device_del(dev);
- transport_destroy_device(dev);
- put_device(dev);
-}
-
-/**
- * ata_tlink_add -- initialize a transport ATA link structure
- * @link: allocated ata_link structure.
- *
- * Initialize an ATA LINK structure for sysfs. It will be added in the
- * device tree below the ATA PORT it belongs to.
- *
- * Returns %0 on success
- */
-int ata_tlink_add(struct ata_link *link)
-{
- struct device *dev = &link->tdev;
- struct ata_port *ap = link->ap;
- struct ata_device *ata_dev;
- int error;
-
- device_initialize(dev);
- dev->parent = &ap->tdev;
- dev->release = ata_tlink_release;
- if (ata_is_host_link(link))
- dev_set_name(dev, "link%d", ap->print_id);
- else
- dev_set_name(dev, "link%d.%d", ap->print_id, link->pmp);
-
- transport_setup_device(dev);
-
- error = device_add(dev);
- if (error) {
- goto tlink_err;
- }
-
- transport_add_device(dev);
- transport_configure_device(dev);
-
- ata_for_each_dev(ata_dev, link, ALL) {
- error = ata_tdev_add(ata_dev);
- if (error) {
- goto tlink_dev_err;
- }
- }
- return 0;
- tlink_dev_err:
- while (--ata_dev >= link->device) {
- ata_tdev_delete(ata_dev);
- }
- transport_remove_device(dev);
- device_del(dev);
- tlink_err:
- transport_destroy_device(dev);
- put_device(dev);
- return error;
-}
-
-/*
* ATA device attributes
*/
@@ -509,7 +389,7 @@ show_ata_dev_##field(struct device *dev, \
{ \
struct ata_device *ata_dev = transport_class_to_dev(dev); \
\
- return scnprintf(buf, 20, format_string, cast ata_dev->field); \
+ return sysfs_emit(buf, format_string, cast ata_dev->field); \
}
#define ata_dev_simple_attr(field, format_string, type) \
@@ -602,10 +482,10 @@ show_ata_dev_trim(struct device *dev,
if (!ata_id_has_trim(ata_dev->id))
mode = "unsupported";
- else if (ata_dev->horkage & ATA_HORKAGE_NOTRIM)
+ else if (ata_dev->quirks & ATA_QUIRK_NOTRIM)
mode = "forced_unsupported";
- else if (ata_dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM)
- mode = "forced_unqueued";
+ else if (ata_dev->quirks & ATA_QUIRK_NO_NCQ_TRIM)
+ mode = "forced_unqueued";
else if (ata_fpdma_dsm_supported(ata_dev))
mode = "queued";
else
@@ -628,9 +508,9 @@ static void ata_tdev_release(struct device *dev)
* @dev: device to check
*
* Returns:
- * %1 if the device represents a ATA device, %0 else
+ * true if the device represents a ATA device, false otherwise
*/
-static int ata_is_ata_dev(const struct device *dev)
+static bool ata_is_ata_dev(const struct device *dev)
{
return dev->release == ata_tdev_release;
}
@@ -638,21 +518,22 @@ static int ata_is_ata_dev(const struct device *dev)
static int ata_tdev_match(struct attribute_container *cont,
struct device *dev)
{
- struct ata_internal* i = to_ata_internal(ata_scsi_transport_template);
+ struct ata_internal *i = to_ata_internal(ata_scsi_transport_template);
+
if (!ata_is_ata_dev(dev))
return 0;
return &i->dev_attr_cont.ac == cont;
}
/**
- * ata_tdev_free -- free a ATA LINK
- * @dev: ATA PHY to free
+ * ata_tdev_free -- free an ATA transport device
+ * @dev: struct ata_device owning the transport device to free
*
- * Frees the specified ATA PHY.
+ * Free the ATA transport device for the specified ATA device.
*
* Note:
- * This function must only be called on a PHY that has not
- * successfully been added using ata_tdev_add().
+ * This function must only be called for a ATA transport device that has not
+ * yet successfully been added using ata_tdev_add().
*/
static void ata_tdev_free(struct ata_device *dev)
{
@@ -661,10 +542,10 @@ static void ata_tdev_free(struct ata_device *dev)
}
/**
- * ata_tdev_delete -- remove ATA device
- * @ata_dev: ATA device to remove
+ * ata_tdev_delete -- remove an ATA transport device
+ * @ata_dev: struct ata_device owning the transport device to delete
*
- * Removes the specified ATA device.
+ * Removes the ATA transport device for the specified ATA device.
*/
static void ata_tdev_delete(struct ata_device *ata_dev)
{
@@ -675,15 +556,14 @@ static void ata_tdev_delete(struct ata_device *ata_dev)
ata_tdev_free(ata_dev);
}
-
/**
- * ata_tdev_add -- initialize a transport ATA device structure.
- * @ata_dev: ata_dev structure.
+ * ata_tdev_add -- initialize an ATA transport device
+ * @ata_dev: struct ata_device owning the transport device to add
*
- * Initialize an ATA device structure for sysfs. It will be added in the
- * device tree below the ATA LINK device it belongs to.
+ * Initialize an ATA transport device for sysfs. It will be added in the
+ * device tree below the ATA link device it belongs to.
*
- * Returns %0 on success
+ * Returns %0 on success and a negative error code on error.
*/
static int ata_tdev_add(struct ata_device *ata_dev)
{
@@ -708,11 +588,147 @@ static int ata_tdev_add(struct ata_device *ata_dev)
return error;
}
- transport_add_device(dev);
+ error = transport_add_device(dev);
+ if (error) {
+ device_del(dev);
+ ata_tdev_free(ata_dev);
+ return error;
+ }
+
transport_configure_device(dev);
return 0;
}
+/*
+ * ATA link attributes
+ */
+static int noop(int x)
+{
+ return x;
+}
+
+#define ata_link_show_linkspeed(field, format) \
+static ssize_t \
+show_ata_link_##field(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct ata_link *link = transport_class_to_link(dev); \
+ \
+ return sprintf(buf, "%s\n", \
+ sata_spd_string(format(link->field))); \
+}
+
+#define ata_link_linkspeed_attr(field, format) \
+ ata_link_show_linkspeed(field, format) \
+static DEVICE_ATTR(field, 0444, show_ata_link_##field, NULL)
+
+ata_link_linkspeed_attr(hw_sata_spd_limit, fls);
+ata_link_linkspeed_attr(sata_spd_limit, fls);
+ata_link_linkspeed_attr(sata_spd, noop);
+
+static DECLARE_TRANSPORT_CLASS(ata_link_class,
+ "ata_link", NULL, NULL, NULL);
+
+static void ata_tlink_release(struct device *dev)
+{
+}
+
+/**
+ * ata_is_link -- check if a struct device represents a ATA link
+ * @dev: device to check
+ *
+ * Returns:
+ * true if the device represents a ATA link, false otherwise
+ */
+static bool ata_is_link(const struct device *dev)
+{
+ return dev->release == ata_tlink_release;
+}
+
+static int ata_tlink_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct ata_internal *i = to_ata_internal(ata_scsi_transport_template);
+
+ if (!ata_is_link(dev))
+ return 0;
+ return &i->link_attr_cont.ac == cont;
+}
+
+/**
+ * ata_tlink_delete -- remove an ATA link transport device
+ * @link: struct ata_link owning the link transport device to remove
+ *
+ * Removes the link transport device of the specified ATA link. This also
+ * removes the ATA device(s) associated with the link as well.
+ */
+void ata_tlink_delete(struct ata_link *link)
+{
+ struct device *dev = &link->tdev;
+ struct ata_device *ata_dev;
+
+ ata_for_each_dev(ata_dev, link, ALL) {
+ ata_tdev_delete(ata_dev);
+ }
+
+ transport_remove_device(dev);
+ device_del(dev);
+ transport_destroy_device(dev);
+ put_device(dev);
+}
+
+/**
+ * ata_tlink_add -- initialize an ATA link transport device
+ * @link: struct ata_link owning the link transport device to initialize
+ *
+ * Initialize an ATA link transport device for sysfs. It will be added in the
+ * device tree below the ATA port it belongs to.
+ *
+ * Returns %0 on success and a negative error code on error.
+ */
+int ata_tlink_add(struct ata_link *link)
+{
+ struct device *dev = &link->tdev;
+ struct ata_port *ap = link->ap;
+ struct ata_device *ata_dev;
+ int error;
+
+ device_initialize(dev);
+ dev->parent = &ap->tdev;
+ dev->release = ata_tlink_release;
+ if (ata_is_host_link(link))
+ dev_set_name(dev, "link%d", ap->print_id);
+ else
+ dev_set_name(dev, "link%d.%d", ap->print_id, link->pmp);
+
+ transport_setup_device(dev);
+
+ error = device_add(dev);
+ if (error)
+ goto tlink_err;
+
+ error = transport_add_device(dev);
+ if (error)
+ goto tlink_transport_err;
+ transport_configure_device(dev);
+
+ ata_for_each_dev(ata_dev, link, ALL) {
+ error = ata_tdev_add(ata_dev);
+ if (error)
+ goto tlink_dev_err;
+ }
+ return 0;
+ tlink_dev_err:
+ while (--ata_dev >= link->device)
+ ata_tdev_delete(ata_dev);
+ transport_remove_device(dev);
+ tlink_transport_err:
+ device_del(dev);
+ tlink_err:
+ transport_destroy_device(dev);
+ put_device(dev);
+ return error;
+}
/*
* Setup / Teardown code
diff --git a/drivers/ata/libata-transport.h b/drivers/ata/libata-transport.h
index 08a57fb9dc61..50cd2cbe8eea 100644
--- a/drivers/ata/libata-transport.h
+++ b/drivers/ata/libata-transport.h
@@ -8,9 +8,6 @@ extern struct scsi_transport_template *ata_scsi_transport_template;
int ata_tlink_add(struct ata_link *link);
void ata_tlink_delete(struct ata_link *link);
-int ata_tport_add(struct device *parent, struct ata_port *ap);
-void ata_tport_delete(struct ata_port *ap);
-
struct scsi_transport_template *ata_attach_transport(void);
void ata_release_transport(struct scsi_transport_template *t);
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index eefda51f97d3..799531218ea2 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -112,7 +112,7 @@ static bool zpready(struct ata_device *dev)
if (!ret || sense_key != NOT_READY)
return false;
- sense_buf = dev->link->ap->sector_buf;
+ sense_buf = dev->sector_buf;
ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
if (ret)
return false;
@@ -160,8 +160,7 @@ void zpodd_on_suspend(struct ata_device *dev)
return;
}
- expires = zpodd->last_ready +
- msecs_to_jiffies(zpodd_poweroff_delay * 1000);
+ expires = zpodd->last_ready + secs_to_jiffies(zpodd_poweroff_delay);
if (time_before(jiffies, expires))
return;
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 98bc8649c63f..0e7ecac73680 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -30,13 +30,32 @@ enum {
ATA_DNXFER_QUIET = (1 << 31),
};
-extern atomic_t ata_print_id;
+#define ATA_PORT_TYPE_NAME "ata_port"
+
extern int atapi_passthru16;
extern int libata_fua;
extern int libata_noacpi;
extern int libata_allow_tpm;
extern const struct device_type ata_port_type;
extern struct ata_link *ata_dev_phys_link(struct ata_device *dev);
+
+static inline bool ata_sstatus_online(u32 sstatus)
+{
+ return (sstatus & 0xf) == 0x3;
+}
+
+static inline bool ata_dev_is_zac(struct ata_device *dev)
+{
+ /* Host managed device or host aware device */
+ return dev->class == ATA_DEV_ZAC ||
+ ata_id_zoned_cap(dev->id) == 0x01;
+}
+
+static inline bool ata_port_eh_scheduled(struct ata_port *ap)
+{
+ return ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS);
+}
+
#ifdef CONFIG_ATA_FORCE
extern void ata_force_cbl(struct ata_port *ap);
#else
@@ -44,15 +63,14 @@ static inline void ata_force_cbl(struct ata_port *ap) { }
#endif
extern u64 ata_tf_to_lba(const struct ata_taskfile *tf);
extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf);
-extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
- u64 block, u32 n_block, unsigned int tf_flags,
- unsigned int tag, int class);
+extern int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block,
+ unsigned int tf_flags, int dld, int class);
extern u64 ata_tf_read_block(const struct ata_taskfile *tf,
struct ata_device *dev);
-extern unsigned ata_exec_internal(struct ata_device *dev,
- struct ata_taskfile *tf, const u8 *cdb,
- int dma_dir, void *buf, unsigned int buflen,
- unsigned int timeout);
+unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf,
+ const u8 *cdb, enum dma_data_direction dma_dir,
+ void *buf, unsigned int buflen,
+ unsigned int timeout);
extern int ata_wait_ready(struct ata_link *link, unsigned long deadline,
int (*check_ready)(struct ata_link *link));
extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
@@ -61,10 +79,14 @@ extern int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags);
extern int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
unsigned int readid_flags);
extern int ata_dev_configure(struct ata_device *dev);
-extern int sata_down_spd_limit(struct ata_link *link, u32 spd_limit);
+extern bool ata_dev_power_init_tf(struct ata_device *dev,
+ struct ata_taskfile *tf, bool set_active);
+extern void ata_dev_power_set_standby(struct ata_device *dev);
+extern void ata_dev_power_set_active(struct ata_device *dev);
+void ata_dev_free_resources(struct ata_device *dev);
extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel);
extern unsigned int ata_dev_set_feature(struct ata_device *dev,
- u8 enable, u8 feature);
+ u8 subcmd, u8 action);
extern void ata_qc_free(struct ata_queued_cmd *qc);
extern void ata_qc_issue(struct ata_queued_cmd *qc);
extern void __ata_qc_complete(struct ata_queued_cmd *qc);
@@ -77,15 +99,27 @@ extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp);
extern int sata_link_init_spd(struct ata_link *link);
extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
-extern struct ata_port *ata_port_alloc(struct ata_host *host);
extern const char *sata_spd_string(unsigned int spd);
-extern int ata_port_probe(struct ata_port *ap);
-extern void __ata_port_probe(struct ata_port *ap);
extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
u8 page, void *buf, unsigned int sectors);
#define to_ata_port(d) container_of(d, struct ata_port, tdev)
+/* libata-sata.c */
+#ifdef CONFIG_SATA_HOST
+int sata_down_spd_limit(struct ata_link *link, u32 spd_limit);
+int ata_eh_get_ncq_success_sense(struct ata_link *link);
+#else
+static inline int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
+{
+ return -EOPNOTSUPP;
+}
+static inline int ata_eh_get_ncq_success_sense(struct ata_link *link)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/* libata-acpi.c */
#ifdef CONFIG_ATA_ACPI
extern unsigned int ata_acpi_gtf_filter;
@@ -96,6 +130,8 @@ extern void ata_acpi_on_disable(struct ata_device *dev);
extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state);
extern void ata_acpi_bind_port(struct ata_port *ap);
extern void ata_acpi_bind_dev(struct ata_device *dev);
+extern void ata_acpi_port_power_on(struct ata_port *ap);
+extern bool ata_acpi_dev_manage_restart(struct ata_device *dev);
extern acpi_handle ata_dev_acpi_handle(struct ata_device *dev);
#else
static inline void ata_acpi_dissociate(struct ata_host *host) { }
@@ -106,29 +142,28 @@ static inline void ata_acpi_set_state(struct ata_port *ap,
pm_message_t state) { }
static inline void ata_acpi_bind_port(struct ata_port *ap) {}
static inline void ata_acpi_bind_dev(struct ata_device *dev) {}
+static inline void ata_acpi_port_power_on(struct ata_port *ap) {}
+static inline bool ata_acpi_dev_manage_restart(struct ata_device *dev) { return 0; }
#endif
/* libata-scsi.c */
extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev);
extern int ata_scsi_add_hosts(struct ata_host *host,
- struct scsi_host_template *sht);
+ const struct scsi_host_template *sht);
extern void ata_scsi_scan_host(struct ata_port *ap, int sync);
-extern int ata_scsi_offline_dev(struct ata_device *dev);
+extern bool ata_scsi_offline_dev(struct ata_device *dev);
+extern bool ata_scsi_sense_is_valid(u8 sk, u8 asc, u8 ascq);
extern void ata_scsi_set_sense(struct ata_device *dev,
struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq);
-extern void ata_scsi_set_sense_information(struct ata_device *dev,
- struct scsi_cmnd *cmd,
- const struct ata_taskfile *tf);
extern void ata_scsi_media_change_notify(struct ata_device *dev);
extern void ata_scsi_hotplug(struct work_struct *work);
-extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);
extern void ata_scsi_dev_rescan(struct work_struct *work);
-extern int ata_bus_probe(struct ata_port *ap);
extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, u64 lun);
void ata_scsi_sdev_config(struct scsi_device *sdev);
-int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev);
+int ata_scsi_dev_config(struct scsi_device *sdev, struct queue_limits *lim,
+ struct ata_device *dev);
int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev);
/* libata-eh.c */
@@ -149,17 +184,15 @@ extern void ata_eh_autopsy(struct ata_port *ap);
const char *ata_get_cmd_name(u8 command);
extern void ata_eh_report(struct ata_port *ap);
extern int ata_eh_reset(struct ata_link *link, int classify,
- ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
- ata_reset_fn_t hardreset, ata_postreset_fn_t postreset);
-extern int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev);
-extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
- ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
- ata_postreset_fn_t postreset,
+ struct ata_reset_operations *reset_ops);
+extern int ata_eh_recover(struct ata_port *ap,
+ struct ata_reset_operations *reset_ops,
struct ata_link **r_failed_disk);
extern void ata_eh_finish(struct ata_port *ap);
extern int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg);
+enum scsi_disposition ata_eh_decide_disposition(struct ata_queued_cmd *qc);
extern unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key);
extern unsigned int atapi_eh_request_sense(struct ata_device *dev,
u8 *sense_buf, u8 dfl_sense_key);
diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c
index f8706ee427d2..23fff10af2ac 100644
--- a/drivers/ata/pata_acpi.c
+++ b/drivers/ata/pata_acpi.c
@@ -205,7 +205,7 @@ static int pacpi_port_start(struct ata_port *ap)
return ata_bmdma_port_start(ap);
}
-static struct scsi_host_template pacpi_sht = {
+static const struct scsi_host_template pacpi_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -216,7 +216,7 @@ static struct ata_port_operations pacpi_ops = {
.mode_filter = pacpi_mode_filter,
.set_piomode = pacpi_set_piomode,
.set_dmamode = pacpi_set_dmamode,
- .prereset = pacpi_pre_reset,
+ .reset.prereset = pacpi_pre_reset,
.port_start = pacpi_port_start,
};
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 76ad0e73fe2a..9d5cb9c34c52 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -355,7 +355,7 @@ static void ali_c2_c3_postreset(struct ata_link *link, unsigned int *classes)
ata_sff_postreset(link, classes);
}
-static struct scsi_host_template ali_sht = {
+static const struct scsi_host_template ali_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -392,11 +392,11 @@ static struct ata_port_operations ali_20_port_ops = {
* Port operations for DMA capable ALi with cable detect
*/
static struct ata_port_operations ali_c2_port_ops = {
- .inherits = &ali_dma_base_ops,
- .check_atapi_dma = ali_check_atapi_dma,
- .cable_detect = ali_c2_cable_detect,
- .dev_config = ali_lock_sectors,
- .postreset = ali_c2_c3_postreset,
+ .inherits = &ali_dma_base_ops,
+ .check_atapi_dma = ali_check_atapi_dma,
+ .cable_detect = ali_c2_cable_detect,
+ .dev_config = ali_lock_sectors,
+ .reset.postreset = ali_c2_c3_postreset,
};
/*
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index f216f9d7b9ec..a2fecadc927d 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -388,13 +388,13 @@ static void nv_host_stop(struct ata_host *host)
pci_write_config_dword(to_pci_dev(host->dev), 0x60, udma);
}
-static struct scsi_host_template amd_sht = {
+static const struct scsi_host_template amd_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
static const struct ata_port_operations amd_base_port_ops = {
.inherits = &ata_bmdma32_port_ops,
- .prereset = amd_pre_reset,
+ .reset.prereset = amd_pre_reset,
};
static struct ata_port_operations amd33_port_ops = {
@@ -429,7 +429,7 @@ static const struct ata_port_operations nv_base_port_ops = {
.inherits = &ata_bmdma_port_ops,
.cable_detect = ata_cable_ignore,
.mode_filter = nv_mode_filter,
- .prereset = nv_pre_reset,
+ .reset.prereset = nv_pre_reset,
.host_stop = nv_host_stop,
};
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index e89617ed9175..514d549286b5 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -218,7 +218,7 @@ struct arasan_cf_dev {
struct ata_queued_cmd *qc;
};
-static struct scsi_host_template arasan_cf_sht = {
+static const struct scsi_host_template arasan_cf_sht = {
ATA_BASE_SHT(DRIVER_NAME),
.dma_boundary = 0xFFFFFFFFUL,
};
@@ -529,7 +529,8 @@ static void data_xfer(struct work_struct *work)
/* dma_request_channel may sleep, so calling from process context */
acdev->dma_chan = dma_request_chan(acdev->host->dev, "data");
if (IS_ERR(acdev->dma_chan)) {
- dev_err(acdev->host->dev, "Unable to get dma_chan\n");
+ dev_err_probe(acdev->host->dev, PTR_ERR(acdev->dma_chan),
+ "Unable to get dma_chan\n");
acdev->dma_chan = NULL;
goto chan_request_fail;
}
@@ -916,15 +917,13 @@ static int arasan_cf_probe(struct platform_device *pdev)
return ret;
}
-static int arasan_cf_remove(struct platform_device *pdev)
+static void arasan_cf_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct arasan_cf_dev *acdev = host->ports[0]->private_data;
ata_host_detach(host);
cf_exit(acdev);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
index 20a8f31a3f57..6160414172a3 100644
--- a/drivers/ata/pata_artop.c
+++ b/drivers/ata/pata_artop.c
@@ -292,7 +292,7 @@ static int artop6210_qc_defer(struct ata_queued_cmd *qc)
return 0;
}
-static struct scsi_host_template artop_sht = {
+static const struct scsi_host_template artop_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -301,7 +301,7 @@ static struct ata_port_operations artop6210_ops = {
.cable_detect = ata_cable_40wire,
.set_piomode = artop6210_set_piomode,
.set_dmamode = artop6210_set_dmamode,
- .prereset = artop62x0_pre_reset,
+ .reset.prereset = artop62x0_pre_reset,
.qc_defer = artop6210_qc_defer,
};
@@ -310,7 +310,7 @@ static struct ata_port_operations artop6260_ops = {
.cable_detect = artop6260_cable_detect,
.set_piomode = artop6260_set_piomode,
.set_dmamode = artop6260_set_dmamode,
- .prereset = artop62x0_pre_reset,
+ .reset.prereset = artop62x0_pre_reset,
};
static void atp8xx_fixup(struct pci_dev *pdev)
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index efdb94cff68b..4c612f9543f6 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -251,7 +251,7 @@ static void atiixp_bmdma_stop(struct ata_queued_cmd *qc)
ata_bmdma_stop(qc);
}
-static struct scsi_host_template atiixp_sht = {
+static const struct scsi_host_template atiixp_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
@@ -264,7 +264,7 @@ static struct ata_port_operations atiixp_port_ops = {
.bmdma_start = atiixp_bmdma_start,
.bmdma_stop = atiixp_bmdma_stop,
- .prereset = atiixp_prereset,
+ .reset.prereset = atiixp_prereset,
.cable_detect = atiixp_cable_detect,
.set_piomode = atiixp_set_piomode,
.set_dmamode = atiixp_set_dmamode,
diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c
index 779d660415c8..308f86f9e2f0 100644
--- a/drivers/ata/pata_atp867x.c
+++ b/drivers/ata/pata_atp867x.c
@@ -259,7 +259,7 @@ static int atp867x_cable_detect(struct ata_port *ap)
return ATA_CBL_PATA_UNK;
}
-static struct scsi_host_template atp867x_sht = {
+static const struct scsi_host_template atp867x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -525,7 +525,7 @@ static int atp867x_reinit_one(struct pci_dev *pdev)
}
#endif
-static struct pci_device_id atp867x_pci_tbl[] = {
+static const struct pci_device_id atp867x_pci_tbl[] = {
{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP867A), 0 },
{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP867B), 0 },
{ },
diff --git a/drivers/ata/pata_bk3710.c b/drivers/ata/pata_bk3710.c
deleted file mode 100644
index fad95cfecced..000000000000
--- a/drivers/ata/pata_bk3710.c
+++ /dev/null
@@ -1,380 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Palmchip BK3710 PATA controller driver
- *
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Based on palm_bk3710.c:
- *
- * Copyright (C) 2006 Texas Instruments.
- * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- */
-
-#include <linux/ata.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/libata.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-
-#define DRV_NAME "pata_bk3710"
-
-#define BK3710_TF_OFFSET 0x1F0
-#define BK3710_CTL_OFFSET 0x3F6
-
-#define BK3710_BMISP 0x02
-#define BK3710_IDETIMP 0x40
-#define BK3710_UDMACTL 0x48
-#define BK3710_MISCCTL 0x50
-#define BK3710_REGSTB 0x54
-#define BK3710_REGRCVR 0x58
-#define BK3710_DATSTB 0x5C
-#define BK3710_DATRCVR 0x60
-#define BK3710_DMASTB 0x64
-#define BK3710_DMARCVR 0x68
-#define BK3710_UDMASTB 0x6C
-#define BK3710_UDMATRP 0x70
-#define BK3710_UDMAENV 0x74
-#define BK3710_IORDYTMP 0x78
-
-static struct scsi_host_template pata_bk3710_sht = {
- ATA_BMDMA_SHT(DRV_NAME),
-};
-
-static unsigned int ideclk_period; /* in nanoseconds */
-
-struct pata_bk3710_udmatiming {
- unsigned int rptime; /* tRP -- Ready to pause time (nsec) */
- unsigned int cycletime; /* tCYCTYP2/2 -- avg Cycle Time (nsec) */
- /* tENV is always a minimum of 20 nsec */
-};
-
-static const struct pata_bk3710_udmatiming pata_bk3710_udmatimings[6] = {
- { 160, 240 / 2 }, /* UDMA Mode 0 */
- { 125, 160 / 2 }, /* UDMA Mode 1 */
- { 100, 120 / 2 }, /* UDMA Mode 2 */
- { 100, 90 / 2 }, /* UDMA Mode 3 */
- { 100, 60 / 2 }, /* UDMA Mode 4 */
- { 85, 40 / 2 }, /* UDMA Mode 5 */
-};
-
-static void pata_bk3710_setudmamode(void __iomem *base, unsigned int dev,
- unsigned int mode)
-{
- u32 val32;
- u16 val16;
- u8 tenv, trp, t0;
-
- /* DMA Data Setup */
- t0 = DIV_ROUND_UP(pata_bk3710_udmatimings[mode].cycletime,
- ideclk_period) - 1;
- tenv = DIV_ROUND_UP(20, ideclk_period) - 1;
- trp = DIV_ROUND_UP(pata_bk3710_udmatimings[mode].rptime,
- ideclk_period) - 1;
-
- /* udmastb Ultra DMA Access Strobe Width */
- val32 = ioread32(base + BK3710_UDMASTB) & (0xFF << (dev ? 0 : 8));
- val32 |= t0 << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_UDMASTB);
-
- /* udmatrp Ultra DMA Ready to Pause Time */
- val32 = ioread32(base + BK3710_UDMATRP) & (0xFF << (dev ? 0 : 8));
- val32 |= trp << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_UDMATRP);
-
- /* udmaenv Ultra DMA envelop Time */
- val32 = ioread32(base + BK3710_UDMAENV) & (0xFF << (dev ? 0 : 8));
- val32 |= tenv << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_UDMAENV);
-
- /* Enable UDMA for Device */
- val16 = ioread16(base + BK3710_UDMACTL) | (1 << dev);
- iowrite16(val16, base + BK3710_UDMACTL);
-}
-
-static void pata_bk3710_setmwdmamode(void __iomem *base, unsigned int dev,
- unsigned short min_cycle,
- unsigned int mode)
-{
- const struct ata_timing *t;
- int cycletime;
- u32 val32;
- u16 val16;
- u8 td, tkw, t0;
-
- t = ata_timing_find_mode(mode);
- cycletime = max_t(int, t->cycle, min_cycle);
-
- /* DMA Data Setup */
- t0 = DIV_ROUND_UP(cycletime, ideclk_period);
- td = DIV_ROUND_UP(t->active, ideclk_period);
- tkw = t0 - td - 1;
- td--;
-
- val32 = ioread32(base + BK3710_DMASTB) & (0xFF << (dev ? 0 : 8));
- val32 |= td << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_DMASTB);
-
- val32 = ioread32(base + BK3710_DMARCVR) & (0xFF << (dev ? 0 : 8));
- val32 |= tkw << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_DMARCVR);
-
- /* Disable UDMA for Device */
- val16 = ioread16(base + BK3710_UDMACTL) & ~(1 << dev);
- iowrite16(val16, base + BK3710_UDMACTL);
-}
-
-static void pata_bk3710_set_dmamode(struct ata_port *ap,
- struct ata_device *adev)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.bmdma_addr;
- int is_slave = adev->devno;
- const u8 xferspeed = adev->dma_mode;
-
- if (xferspeed >= XFER_UDMA_0)
- pata_bk3710_setudmamode(base, is_slave,
- xferspeed - XFER_UDMA_0);
- else
- pata_bk3710_setmwdmamode(base, is_slave,
- adev->id[ATA_ID_EIDE_DMA_MIN],
- xferspeed);
-}
-
-static void pata_bk3710_setpiomode(void __iomem *base, struct ata_device *pair,
- unsigned int dev, unsigned int cycletime,
- unsigned int mode)
-{
- const struct ata_timing *t;
- u32 val32;
- u8 t2, t2i, t0;
-
- t = ata_timing_find_mode(XFER_PIO_0 + mode);
-
- /* PIO Data Setup */
- t0 = DIV_ROUND_UP(cycletime, ideclk_period);
- t2 = DIV_ROUND_UP(t->active, ideclk_period);
-
- t2i = t0 - t2 - 1;
- t2--;
-
- val32 = ioread32(base + BK3710_DATSTB) & (0xFF << (dev ? 0 : 8));
- val32 |= t2 << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_DATSTB);
-
- val32 = ioread32(base + BK3710_DATRCVR) & (0xFF << (dev ? 0 : 8));
- val32 |= t2i << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_DATRCVR);
-
- /* FIXME: this is broken also in the old driver */
- if (pair) {
- u8 mode2 = pair->pio_mode - XFER_PIO_0;
-
- if (mode2 < mode)
- mode = mode2;
- }
-
- /* TASKFILE Setup */
- t0 = DIV_ROUND_UP(t->cyc8b, ideclk_period);
- t2 = DIV_ROUND_UP(t->act8b, ideclk_period);
-
- t2i = t0 - t2 - 1;
- t2--;
-
- val32 = ioread32(base + BK3710_REGSTB) & (0xFF << (dev ? 0 : 8));
- val32 |= t2 << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_REGSTB);
-
- val32 = ioread32(base + BK3710_REGRCVR) & (0xFF << (dev ? 0 : 8));
- val32 |= t2i << (dev ? 8 : 0);
- iowrite32(val32, base + BK3710_REGRCVR);
-}
-
-static void pata_bk3710_set_piomode(struct ata_port *ap,
- struct ata_device *adev)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.bmdma_addr;
- struct ata_device *pair = ata_dev_pair(adev);
- const struct ata_timing *t = ata_timing_find_mode(adev->pio_mode);
- const u16 *id = adev->id;
- unsigned int cycle_time = 0;
- int is_slave = adev->devno;
- const u8 pio = adev->pio_mode - XFER_PIO_0;
-
- if (id[ATA_ID_FIELD_VALID] & 2) {
- if (ata_id_has_iordy(id))
- cycle_time = id[ATA_ID_EIDE_PIO_IORDY];
- else
- cycle_time = id[ATA_ID_EIDE_PIO];
-
- /* conservative "downgrade" for all pre-ATA2 drives */
- if (pio < 3 && cycle_time < t->cycle)
- cycle_time = 0; /* use standard timing */
- }
-
- if (!cycle_time)
- cycle_time = t->cycle;
-
- pata_bk3710_setpiomode(base, pair, is_slave, cycle_time, pio);
-}
-
-static void pata_bk3710_chipinit(void __iomem *base)
-{
- /*
- * REVISIT: the ATA reset signal needs to be managed through a
- * GPIO, which means it should come from platform_data. Until
- * we get and use such information, we have to trust that things
- * have been reset before we get here.
- */
-
- /*
- * Program the IDETIMP Register Value based on the following assumptions
- *
- * (ATA_IDETIMP_IDEEN , ENABLE ) |
- * (ATA_IDETIMP_PREPOST1 , DISABLE) |
- * (ATA_IDETIMP_PREPOST0 , DISABLE) |
- *
- * DM6446 silicon rev 2.1 and earlier have no observed net benefit
- * from enabling prefetch/postwrite.
- */
- iowrite16(BIT(15), base + BK3710_IDETIMP);
-
- /*
- * UDMACTL Ultra-ATA DMA Control
- * (ATA_UDMACTL_UDMAP1 , 0 ) |
- * (ATA_UDMACTL_UDMAP0 , 0 )
- *
- */
- iowrite16(0, base + BK3710_UDMACTL);
-
- /*
- * MISCCTL Miscellaneous Conrol Register
- * (ATA_MISCCTL_HWNHLD1P , 1 cycle)
- * (ATA_MISCCTL_HWNHLD0P , 1 cycle)
- * (ATA_MISCCTL_TIMORIDE , 1)
- */
- iowrite32(0x001, base + BK3710_MISCCTL);
-
- /*
- * IORDYTMP IORDY Timer for Primary Register
- * (ATA_IORDYTMP_IORDYTMP , DISABLE)
- */
- iowrite32(0, base + BK3710_IORDYTMP);
-
- /*
- * Configure BMISP Register
- * (ATA_BMISP_DMAEN1 , DISABLE ) |
- * (ATA_BMISP_DMAEN0 , DISABLE ) |
- * (ATA_BMISP_IORDYINT , CLEAR) |
- * (ATA_BMISP_INTRSTAT , CLEAR) |
- * (ATA_BMISP_DMAERROR , CLEAR)
- */
- iowrite16(0xE, base + BK3710_BMISP);
-
- pata_bk3710_setpiomode(base, NULL, 0, 600, 0);
- pata_bk3710_setpiomode(base, NULL, 1, 600, 0);
-}
-
-static struct ata_port_operations pata_bk3710_ports_ops = {
- .inherits = &ata_bmdma_port_ops,
- .cable_detect = ata_cable_80wire,
-
- .set_piomode = pata_bk3710_set_piomode,
- .set_dmamode = pata_bk3710_set_dmamode,
-};
-
-static int __init pata_bk3710_probe(struct platform_device *pdev)
-{
- struct clk *clk;
- struct resource *mem;
- struct ata_host *host;
- struct ata_port *ap;
- void __iomem *base;
- unsigned long rate;
- int irq;
-
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk))
- return -ENODEV;
-
- clk_enable(clk);
- rate = clk_get_rate(clk);
- if (!rate)
- return -EINVAL;
-
- /* NOTE: round *down* to meet minimum timings; we count in clocks */
- ideclk_period = 1000000000UL / rate;
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- pr_err(DRV_NAME ": failed to get IRQ resource\n");
- return irq;
- }
-
- base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- /* configure the Palmchip controller */
- pata_bk3710_chipinit(base);
-
- /* allocate host */
- host = ata_host_alloc(&pdev->dev, 1);
- if (!host)
- return -ENOMEM;
- ap = host->ports[0];
-
- ap->ops = &pata_bk3710_ports_ops;
- ap->pio_mask = ATA_PIO4;
- ap->mwdma_mask = ATA_MWDMA2;
- ap->udma_mask = rate < 100000000 ? ATA_UDMA4 : ATA_UDMA5;
- ap->flags |= ATA_FLAG_SLAVE_POSS;
-
- ap->ioaddr.data_addr = base + BK3710_TF_OFFSET;
- ap->ioaddr.error_addr = base + BK3710_TF_OFFSET + 1;
- ap->ioaddr.feature_addr = base + BK3710_TF_OFFSET + 1;
- ap->ioaddr.nsect_addr = base + BK3710_TF_OFFSET + 2;
- ap->ioaddr.lbal_addr = base + BK3710_TF_OFFSET + 3;
- ap->ioaddr.lbam_addr = base + BK3710_TF_OFFSET + 4;
- ap->ioaddr.lbah_addr = base + BK3710_TF_OFFSET + 5;
- ap->ioaddr.device_addr = base + BK3710_TF_OFFSET + 6;
- ap->ioaddr.status_addr = base + BK3710_TF_OFFSET + 7;
- ap->ioaddr.command_addr = base + BK3710_TF_OFFSET + 7;
-
- ap->ioaddr.altstatus_addr = base + BK3710_CTL_OFFSET;
- ap->ioaddr.ctl_addr = base + BK3710_CTL_OFFSET;
-
- ap->ioaddr.bmdma_addr = base;
-
- ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx",
- (unsigned long)base + BK3710_TF_OFFSET,
- (unsigned long)base + BK3710_CTL_OFFSET);
-
- /* activate */
- return ata_host_activate(host, irq, ata_sff_interrupt, 0,
- &pata_bk3710_sht);
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:palm_bk3710");
-
-static struct platform_driver pata_bk3710_driver = {
- .driver = {
- .name = "palm_bk3710",
- },
-};
-
-static int __init pata_bk3710_init(void)
-{
- return platform_driver_probe(&pata_bk3710_driver, pata_bk3710_probe);
-}
-
-module_init(pata_bk3710_init);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/ata/pata_buddha.c b/drivers/ata/pata_buddha.c
index 27d4c417fc60..c36ee991d5e5 100644
--- a/drivers/ata/pata_buddha.c
+++ b/drivers/ata/pata_buddha.c
@@ -27,7 +27,6 @@
#include <asm/amigahw.h>
#include <asm/amigaints.h>
-#include <asm/ide.h>
#include <asm/setup.h>
#define DRV_NAME "pata_buddha"
@@ -57,7 +56,7 @@ static unsigned int xsurf_bases[2] = {
XSURF_BASE1, XSURF_BASE2
};
-static struct scsi_host_template pata_buddha_sht = {
+static const struct scsi_host_template pata_buddha_sht = {
ATA_PIO_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c
index 1a3372a72213..45a7217b136e 100644
--- a/drivers/ata/pata_cmd640.c
+++ b/drivers/ata/pata_cmd640.c
@@ -172,7 +172,7 @@ static bool cmd640_sff_irq_check(struct ata_port *ap)
return irq_stat & irq_mask;
}
-static struct scsi_host_template cmd640_sht = {
+static const struct scsi_host_template cmd640_sht = {
ATA_PIO_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index 5baa4a7819c1..fafea2b79145 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -319,7 +319,7 @@ static void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc)
ata_bmdma_stop(qc);
}
-static struct scsi_host_template cmd64x_sht = {
+static const struct scsi_host_template cmd64x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index f4289a532f87..3163c8d9cef5 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -8,9 +8,9 @@
* PIO mode and smarter silicon.
*
* The practical upshot of this is that we must always tune the
- * drive for the right PIO mode. We must also ignore all the blacklists
- * and the drive bus mastering DMA information. Also to confuse matters
- * further we can do DMA on PIO only drives.
+ * drive for the right PIO mode and ignore the drive bus mastering DMA
+ * information. Also to confuse matters further we can do DMA on PIO only
+ * drives.
*
* DMA on the 5510 also requires we disable_hlt() during DMA on early
* revisions.
@@ -94,7 +94,7 @@ static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev)
cs5520_set_timings(ap, adev, adev->pio_mode);
}
-static struct scsi_host_template cs5520_sht = {
+static const struct scsi_host_template cs5520_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
@@ -151,12 +151,6 @@ static int cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (!host)
return -ENOMEM;
- /* Perform set up for DMA */
- if (pci_enable_device_io(pdev)) {
- dev_err(&pdev->dev, "unable to configure BAR2.\n");
- return -ENODEV;
- }
-
if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) {
dev_err(&pdev->dev, "unable to configure DMA mask.\n");
return -ENODEV;
@@ -212,7 +206,7 @@ static int cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
return rc;
- ata_port_desc(ap, "irq %d", irq[i]);
+ ata_port_desc_misc(ap, irq[i]);
}
return ata_host_register(host, &cs5520_sht);
diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c
index d5b7ac14e78f..1e67b0f8db43 100644
--- a/drivers/ata/pata_cs5530.c
+++ b/drivers/ata/pata_cs5530.c
@@ -146,7 +146,7 @@ static unsigned int cs5530_qc_issue(struct ata_queued_cmd *qc)
return ata_bmdma_qc_issue(qc);
}
-static struct scsi_host_template cs5530_sht = {
+static const struct scsi_host_template cs5530_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index c2c3238ff84b..d793fc441b46 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -141,7 +141,7 @@ static void cs5535_set_dmamode(struct ata_port *ap, struct ata_device *adev)
wrmsr(ATAC_CH0D0_DMA + 2 * adev->devno, reg, 0);
}
-static struct scsi_host_template cs5535_sht = {
+static const struct scsi_host_template cs5535_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
index ab47aeb5587f..73e81e160c91 100644
--- a/drivers/ata/pata_cs5536.c
+++ b/drivers/ata/pata_cs5536.c
@@ -27,7 +27,7 @@
#include <scsi/scsi_host.h>
#include <linux/dmi.h>
-#ifdef CONFIG_X86_32
+#if defined(CONFIG_X86) && defined(CONFIG_X86_32)
#include <asm/msr.h>
static int use_msr;
module_param_named(msr, use_msr, int, 0644);
@@ -217,7 +217,7 @@ static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev)
cs5536_write(pdev, ETC, etc);
}
-static struct scsi_host_template cs5536_sht = {
+static const struct scsi_host_template cs5536_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c
index 3be5d52a777b..ae347b5c2871 100644
--- a/drivers/ata/pata_cypress.c
+++ b/drivers/ata/pata_cypress.c
@@ -115,7 +115,7 @@ static void cy82c693_set_dmamode(struct ata_port *ap, struct ata_device *adev)
outb(0x50, 0x23);
}
-static struct scsi_host_template cy82c693_sht = {
+static const struct scsi_host_template cy82c693_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index 21da59f35b41..6fe49b303fee 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -234,7 +234,7 @@ static void efar_set_dmamode (struct ata_port *ap, struct ata_device *adev)
spin_unlock_irqrestore(&efar_lock, flags);
}
-static struct scsi_host_template efar_sht = {
+static const struct scsi_host_template efar_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -243,7 +243,7 @@ static struct ata_port_operations efar_ops = {
.cable_detect = efar_cable_detect,
.set_piomode = efar_set_piomode,
.set_dmamode = efar_set_dmamode,
- .prereset = efar_pre_reset,
+ .reset.prereset = efar_pre_reset,
};
diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
index 6c75a22db12b..b2b9e0058333 100644
--- a/drivers/ata/pata_ep93xx.c
+++ b/drivers/ata/pata_ep93xx.c
@@ -40,11 +40,12 @@
#include <linux/ata.h>
#include <linux/libata.h>
#include <linux/platform_device.h>
+#include <linux/sys_soc.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/ktime.h>
+#include <linux/mod_devicetable.h>
-#include <linux/platform_data/dma-ep93xx.h>
#include <linux/soc/cirrus/ep93xx.h>
#define DRV_NAME "ep93xx-ide"
@@ -125,7 +126,7 @@ enum {
};
struct ep93xx_pata_data {
- const struct platform_device *pdev;
+ struct platform_device *pdev;
void __iomem *ide_base;
struct ata_timing t;
bool iordy;
@@ -134,9 +135,7 @@ struct ep93xx_pata_data {
unsigned long udma_out_phys;
struct dma_chan *dma_rx_channel;
- struct ep93xx_dma_data dma_rx_data;
struct dma_chan *dma_tx_channel;
- struct ep93xx_dma_data dma_tx_data;
};
static void ep93xx_pata_clear_regs(void __iomem *base)
@@ -636,20 +635,13 @@ static void ep93xx_pata_release_dma(struct ep93xx_pata_data *drv_data)
}
}
-static bool ep93xx_pata_dma_filter(struct dma_chan *chan, void *filter_param)
+static int ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
{
- if (ep93xx_dma_chan_is_m2p(chan))
- return false;
-
- chan->private = filter_param;
- return true;
-}
-
-static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
-{
- const struct platform_device *pdev = drv_data->pdev;
+ struct platform_device *pdev = drv_data->pdev;
+ struct device *dev = &pdev->dev;
dma_cap_mask_t mask;
struct dma_slave_config conf;
+ int ret;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -659,22 +651,16 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
* to request only one channel, and reprogram it's direction at
* start of new transfer.
*/
- drv_data->dma_rx_data.port = EP93XX_DMA_IDE;
- drv_data->dma_rx_data.direction = DMA_DEV_TO_MEM;
- drv_data->dma_rx_data.name = "ep93xx-pata-rx";
- drv_data->dma_rx_channel = dma_request_channel(mask,
- ep93xx_pata_dma_filter, &drv_data->dma_rx_data);
- if (!drv_data->dma_rx_channel)
- return;
-
- drv_data->dma_tx_data.port = EP93XX_DMA_IDE;
- drv_data->dma_tx_data.direction = DMA_MEM_TO_DEV;
- drv_data->dma_tx_data.name = "ep93xx-pata-tx";
- drv_data->dma_tx_channel = dma_request_channel(mask,
- ep93xx_pata_dma_filter, &drv_data->dma_tx_data);
- if (!drv_data->dma_tx_channel) {
- dma_release_channel(drv_data->dma_rx_channel);
- return;
+ drv_data->dma_rx_channel = dma_request_chan(dev, "rx");
+ if (IS_ERR(drv_data->dma_rx_channel))
+ return dev_err_probe(dev, PTR_ERR(drv_data->dma_rx_channel),
+ "rx DMA setup failed\n");
+
+ drv_data->dma_tx_channel = dma_request_chan(&pdev->dev, "tx");
+ if (IS_ERR(drv_data->dma_tx_channel)) {
+ ret = dev_err_probe(dev, PTR_ERR(drv_data->dma_tx_channel),
+ "tx DMA setup failed\n");
+ goto fail_release_rx;
}
/* Configure receive channel direction and source address */
@@ -682,10 +668,10 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
conf.direction = DMA_DEV_TO_MEM;
conf.src_addr = drv_data->udma_in_phys;
conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- if (dmaengine_slave_config(drv_data->dma_rx_channel, &conf)) {
- dev_err(&pdev->dev, "failed to configure rx dma channel\n");
- ep93xx_pata_release_dma(drv_data);
- return;
+ ret = dmaengine_slave_config(drv_data->dma_rx_channel, &conf);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to configure rx dma channel");
+ goto fail_release_dma;
}
/* Configure transmit channel direction and destination address */
@@ -693,10 +679,20 @@ static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
conf.direction = DMA_MEM_TO_DEV;
conf.dst_addr = drv_data->udma_out_phys;
conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- if (dmaengine_slave_config(drv_data->dma_tx_channel, &conf)) {
- dev_err(&pdev->dev, "failed to configure tx dma channel\n");
- ep93xx_pata_release_dma(drv_data);
+ ret = dmaengine_slave_config(drv_data->dma_tx_channel, &conf);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to configure tx dma channel");
+ goto fail_release_dma;
}
+
+ return 0;
+
+fail_release_rx:
+ dma_release_channel(drv_data->dma_rx_channel);
+fail_release_dma:
+ ep93xx_pata_release_dma(drv_data);
+
+ return ret;
}
static void ep93xx_pata_dma_start(struct ata_queued_cmd *qc)
@@ -872,7 +868,7 @@ static int ep93xx_pata_port_start(struct ata_port *ap)
return 0;
}
-static struct scsi_host_template ep93xx_pata_sht = {
+static const struct scsi_host_template ep93xx_pata_sht = {
ATA_BASE_SHT(DRV_NAME),
/* ep93xx dma implementation limit */
.sg_tablesize = 32,
@@ -883,10 +879,8 @@ static struct scsi_host_template ep93xx_pata_sht = {
static struct ata_port_operations ep93xx_pata_port_ops = {
.inherits = &ata_bmdma_port_ops,
- .qc_prep = ata_noop_qc_prep,
-
- .softreset = ep93xx_pata_softreset,
- .hardreset = ATA_OP_NULL,
+ .reset.softreset = ep93xx_pata_softreset,
+ .reset.hardreset = ATA_OP_NULL,
.sff_dev_select = ep93xx_pata_dev_select,
.sff_set_devctl = ep93xx_pata_set_devctl,
@@ -910,6 +904,12 @@ static struct ata_port_operations ep93xx_pata_port_ops = {
.port_start = ep93xx_pata_port_start,
};
+static const struct soc_device_attribute ep93xx_soc_table[] = {
+ { .revision = "E1", .data = (void *)ATA_UDMA3 },
+ { .revision = "E2", .data = (void *)ATA_UDMA4 },
+ { /* sentinel */ }
+};
+
static int ep93xx_pata_probe(struct platform_device *pdev)
{
struct ep93xx_pata_data *drv_data;
@@ -920,40 +920,31 @@ static int ep93xx_pata_probe(struct platform_device *pdev)
void __iomem *ide_base;
int err;
- err = ep93xx_ide_acquire_gpio(pdev);
- if (err)
- return err;
-
/* INT[3] (IRQ_EP93XX_EXT3) line connected as pull down */
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = irq;
- goto err_rel_gpio;
- }
+ if (irq < 0)
+ return irq;
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ide_base = devm_ioremap_resource(&pdev->dev, mem_res);
- if (IS_ERR(ide_base)) {
- err = PTR_ERR(ide_base);
- goto err_rel_gpio;
- }
+ ide_base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
+ if (IS_ERR(ide_base))
+ return PTR_ERR(ide_base);
drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
- if (!drv_data) {
- err = -ENXIO;
- goto err_rel_gpio;
- }
+ if (!drv_data)
+ return -ENOMEM;
drv_data->pdev = pdev;
drv_data->ide_base = ide_base;
drv_data->udma_in_phys = mem_res->start + IDEUDMADATAIN;
drv_data->udma_out_phys = mem_res->start + IDEUDMADATAOUT;
- ep93xx_pata_dma_init(drv_data);
+ err = ep93xx_pata_dma_init(drv_data);
+ if (err)
+ return err;
/* allocate host */
host = ata_host_alloc(&pdev->dev, 1);
if (!host) {
- err = -ENXIO;
+ err = -ENOMEM;
goto err_rel_dma;
}
@@ -977,12 +968,11 @@ static int ep93xx_pata_probe(struct platform_device *pdev)
* so this driver supports only UDMA modes.
*/
if (drv_data->dma_rx_channel && drv_data->dma_tx_channel) {
- int chip_rev = ep93xx_chip_revision();
+ const struct soc_device_attribute *match;
- if (chip_rev == EP93XX_CHIP_REV_E1)
- ap->udma_mask = ATA_UDMA3;
- else if (chip_rev == EP93XX_CHIP_REV_E2)
- ap->udma_mask = ATA_UDMA4;
+ match = soc_device_match(ep93xx_soc_table);
+ if (match)
+ ap->udma_mask = (unsigned int) match->data;
else
ap->udma_mask = ATA_UDMA2;
}
@@ -1000,12 +990,10 @@ static int ep93xx_pata_probe(struct platform_device *pdev)
err_rel_dma:
ep93xx_pata_release_dma(drv_data);
-err_rel_gpio:
- ep93xx_ide_release_gpio(pdev);
return err;
}
-static int ep93xx_pata_remove(struct platform_device *pdev)
+static void ep93xx_pata_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct ep93xx_pata_data *drv_data = host->private_data;
@@ -1013,13 +1001,18 @@ static int ep93xx_pata_remove(struct platform_device *pdev)
ata_host_detach(host);
ep93xx_pata_release_dma(drv_data);
ep93xx_pata_clear_regs(drv_data->ide_base);
- ep93xx_ide_release_gpio(pdev);
- return 0;
}
+static const struct of_device_id ep93xx_pata_of_ids[] = {
+ { .compatible = "cirrus,ep9312-pata" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ep93xx_pata_of_ids);
+
static struct platform_driver ep93xx_pata_platform_driver = {
.driver = {
.name = DRV_NAME,
+ .of_match_table = ep93xx_pata_of_ids,
},
.probe = ep93xx_pata_probe,
.remove = ep93xx_pata_remove,
diff --git a/drivers/ata/pata_falcon.c b/drivers/ata/pata_falcon.c
index 823c88622e34..334c4eea41ec 100644
--- a/drivers/ata/pata_falcon.c
+++ b/drivers/ata/pata_falcon.c
@@ -28,12 +28,16 @@
#include <asm/atarihw.h>
#include <asm/atariints.h>
#include <asm/atari_stdma.h>
-#include <asm/ide.h>
#define DRV_NAME "pata_falcon"
#define DRV_VERSION "0.1.0"
-static struct scsi_host_template pata_falcon_sht = {
+static int pata_falcon_swap_mask;
+
+module_param_named(data_swab, pata_falcon_swap_mask, int, 0444);
+MODULE_PARM_DESC(data_swab, "Data byte swap enable/disable bitmap (0x1==drive1, 0x2==drive2, 0x4==drive3, 0x8==drive4, default==0)");
+
+static const struct scsi_host_template pata_falcon_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -50,7 +54,7 @@ static unsigned int pata_falcon_data_xfer(struct ata_queued_cmd *qc,
if (dev->class == ATA_DEV_ATA && cmd &&
!blk_rq_is_passthrough(scsi_cmd_to_rq(cmd)))
- swap = 0;
+ swap = (uintptr_t)ap->private_data & BIT(dev->devno);
/* Transfer multiple of 2 bytes */
if (rw == READ) {
@@ -117,14 +121,15 @@ static struct ata_port_operations pata_falcon_ops = {
.set_mode = pata_falcon_set_mode,
};
-static int __init pata_falcon_init_one(struct platform_device *pdev)
+static int pata_falcon_init_one(struct platform_device *pdev)
{
struct resource *base_mem_res, *ctl_mem_res;
struct resource *base_res, *ctl_res, *irq_res;
struct ata_host *host;
struct ata_port *ap;
- void __iomem *base;
- int irq = 0;
+ void __iomem *base, *ctl_base;
+ int mask_shift = 0; /* Q40 & Falcon default */
+ int irq = 0, io_offset = 1, reg_shift = 2; /* Falcon defaults */
dev_info(&pdev->dev, "Atari Falcon and Q40/Q60 PATA controller\n");
@@ -165,26 +170,38 @@ static int __init pata_falcon_init_one(struct platform_device *pdev)
ap->pio_mask = ATA_PIO4;
ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
- base = (void __iomem *)base_mem_res->start;
/* N.B. this assumes data_addr will be used for word-sized I/O only */
- ap->ioaddr.data_addr = base + 0 + 0 * 4;
- ap->ioaddr.error_addr = base + 1 + 1 * 4;
- ap->ioaddr.feature_addr = base + 1 + 1 * 4;
- ap->ioaddr.nsect_addr = base + 1 + 2 * 4;
- ap->ioaddr.lbal_addr = base + 1 + 3 * 4;
- ap->ioaddr.lbam_addr = base + 1 + 4 * 4;
- ap->ioaddr.lbah_addr = base + 1 + 5 * 4;
- ap->ioaddr.device_addr = base + 1 + 6 * 4;
- ap->ioaddr.status_addr = base + 1 + 7 * 4;
- ap->ioaddr.command_addr = base + 1 + 7 * 4;
-
- base = (void __iomem *)ctl_mem_res->start;
- ap->ioaddr.altstatus_addr = base + 1;
- ap->ioaddr.ctl_addr = base + 1;
-
- ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx",
- (unsigned long)base_mem_res->start,
- (unsigned long)ctl_mem_res->start);
+ ap->ioaddr.data_addr = (void __iomem *)base_mem_res->start;
+
+ if (base_res) { /* only Q40 has IO resources */
+ io_offset = 0x10000;
+ reg_shift = 0;
+ base = (void __iomem *)base_res->start;
+ ctl_base = (void __iomem *)ctl_res->start;
+ } else {
+ base = (void __iomem *)base_mem_res->start;
+ ctl_base = (void __iomem *)ctl_mem_res->start;
+ }
+
+ ap->ioaddr.error_addr = base + io_offset + (1 << reg_shift);
+ ap->ioaddr.feature_addr = base + io_offset + (1 << reg_shift);
+ ap->ioaddr.nsect_addr = base + io_offset + (2 << reg_shift);
+ ap->ioaddr.lbal_addr = base + io_offset + (3 << reg_shift);
+ ap->ioaddr.lbam_addr = base + io_offset + (4 << reg_shift);
+ ap->ioaddr.lbah_addr = base + io_offset + (5 << reg_shift);
+ ap->ioaddr.device_addr = base + io_offset + (6 << reg_shift);
+ ap->ioaddr.status_addr = base + io_offset + (7 << reg_shift);
+ ap->ioaddr.command_addr = base + io_offset + (7 << reg_shift);
+
+ ap->ioaddr.altstatus_addr = ctl_base + io_offset;
+ ap->ioaddr.ctl_addr = ctl_base + io_offset;
+
+ ata_port_desc(ap, "cmd %px ctl %px data %px",
+ base, ctl_base, ap->ioaddr.data_addr);
+
+ if (pdev->id > 0)
+ mask_shift = 2;
+ ap->private_data = (void *)(uintptr_t)(pata_falcon_swap_mask >> mask_shift);
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq_res && irq_res->start > 0) {
@@ -199,23 +216,22 @@ static int __init pata_falcon_init_one(struct platform_device *pdev)
IRQF_SHARED, &pata_falcon_sht);
}
-static int __exit pata_falcon_remove_one(struct platform_device *pdev)
+static void pata_falcon_remove_one(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
ata_host_detach(host);
-
- return 0;
}
static struct platform_driver pata_falcon_driver = {
- .remove = __exit_p(pata_falcon_remove_one),
- .driver = {
+ .probe = pata_falcon_init_one,
+ .remove = pata_falcon_remove_one,
+ .driver = {
.name = "atari-falcon-ide",
},
};
-module_platform_driver_probe(pata_falcon_driver, pata_falcon_init_one);
+module_platform_driver(pata_falcon_driver);
MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
MODULE_DESCRIPTION("low-level driver for Atari Falcon PATA");
diff --git a/drivers/ata/pata_ftide010.c b/drivers/ata/pata_ftide010.c
index 0117df0fe3c5..c3a8384c3e04 100644
--- a/drivers/ata/pata_ftide010.c
+++ b/drivers/ata/pata_ftide010.c
@@ -14,8 +14,7 @@
#include <linux/module.h>
#include <linux/libata.h>
#include <linux/bitops.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/clk.h>
#include "sata_gemini.h"
@@ -84,7 +83,7 @@ struct ftide010 {
#define FTIDE010_CLK_MOD_DEV0_UDMA_EN BIT(4)
#define FTIDE010_CLK_MOD_DEV1_UDMA_EN BIT(5)
-static struct scsi_host_template pata_ftide010_sht = {
+static const struct scsi_host_template pata_ftide010_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -470,11 +469,7 @@ static int pata_ftide010_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- ftide->base = devm_ioremap_resource(dev, res);
+ ftide->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(ftide->base))
return PTR_ERR(ftide->base);
@@ -541,32 +536,32 @@ err_dis_clk:
return ret;
}
-static int pata_ftide010_remove(struct platform_device *pdev)
+static void pata_ftide010_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct ftide010 *ftide = host->private_data;
ata_host_detach(ftide->host);
clk_disable_unprepare(ftide->pclk);
-
- return 0;
}
static const struct of_device_id pata_ftide010_of_match[] = {
{ .compatible = "faraday,ftide010", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, pata_ftide010_of_match);
static struct platform_driver pata_ftide010_driver = {
.driver = {
.name = DRV_NAME,
- .of_match_table = of_match_ptr(pata_ftide010_of_match),
+ .of_match_table = pata_ftide010_of_match,
},
.probe = pata_ftide010_probe,
.remove = pata_ftide010_remove,
};
module_platform_driver(pata_ftide010_driver);
+MODULE_DESCRIPTION("low level driver for Faraday Technology FTIDE010");
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/pata_gayle.c b/drivers/ata/pata_gayle.c
index 65bc9f3042ce..8602c3889948 100644
--- a/drivers/ata/pata_gayle.c
+++ b/drivers/ata/pata_gayle.c
@@ -27,7 +27,6 @@
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include <asm/amigayle.h>
-#include <asm/ide.h>
#include <asm/setup.h>
#define DRV_NAME "pata_gayle"
@@ -35,7 +34,7 @@
#define GAYLE_CONTROL 0x101a
-static struct scsi_host_template pata_gayle_sht = {
+static const struct scsi_host_template pata_gayle_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -125,7 +124,7 @@ static struct ata_port_operations pata_gayle_a4000_ops = {
.set_mode = pata_gayle_set_mode,
};
-static int __init pata_gayle_init_one(struct platform_device *pdev)
+static int pata_gayle_init_one(struct platform_device *pdev)
{
struct resource *res;
struct gayle_ide_platform_data *pdata;
@@ -194,23 +193,22 @@ static int __init pata_gayle_init_one(struct platform_device *pdev)
return 0;
}
-static int __exit pata_gayle_remove_one(struct platform_device *pdev)
+static void pata_gayle_remove_one(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
ata_host_detach(host);
-
- return 0;
}
static struct platform_driver pata_gayle_driver = {
- .remove = __exit_p(pata_gayle_remove_one),
- .driver = {
- .name = "amiga-gayle-ide",
+ .probe = pata_gayle_init_one,
+ .remove = pata_gayle_remove_one,
+ .driver = {
+ .name = "amiga-gayle-ide",
},
};
-module_platform_driver_probe(pata_gayle_driver, pata_gayle_init_one);
+module_platform_driver(pata_gayle_driver);
MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
MODULE_DESCRIPTION("low-level driver for Amiga Gayle PATA");
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index 7e441fb304d3..b96e8bd2a3f8 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -170,8 +170,8 @@ static const char * const bad_ata66_3[] = {
NULL
};
-static int hpt_dma_blacklisted(const struct ata_device *dev, char *modestr,
- const char * const list[])
+static int hpt_dma_broken(const struct ata_device *dev, char *modestr,
+ const char * const list[])
{
unsigned char model_num[ATA_ID_PROD_LEN + 1];
int i;
@@ -197,11 +197,11 @@ static int hpt_dma_blacklisted(const struct ata_device *dev, char *modestr,
static unsigned int hpt366_filter(struct ata_device *adev, unsigned int mask)
{
if (adev->class == ATA_DEV_ATA) {
- if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33))
+ if (hpt_dma_broken(adev, "UDMA", bad_ata33))
mask &= ~ATA_MASK_UDMA;
- if (hpt_dma_blacklisted(adev, "UDMA3", bad_ata66_3))
+ if (hpt_dma_broken(adev, "UDMA3", bad_ata66_3))
mask &= ~(0xF8 << ATA_SHIFT_UDMA);
- if (hpt_dma_blacklisted(adev, "UDMA4", bad_ata66_4))
+ if (hpt_dma_broken(adev, "UDMA4", bad_ata66_4))
mask &= ~(0xF0 << ATA_SHIFT_UDMA);
} else if (adev->class == ATA_DEV_ATAPI)
mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
@@ -312,7 +312,7 @@ static int hpt366_prereset(struct ata_link *link, unsigned long deadline)
return ata_sff_prereset(link, deadline);
}
-static struct scsi_host_template hpt36x_sht = {
+static const struct scsi_host_template hpt36x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -322,7 +322,7 @@ static struct scsi_host_template hpt36x_sht = {
static struct ata_port_operations hpt366_port_ops = {
.inherits = &ata_bmdma_port_ops,
- .prereset = hpt366_prereset,
+ .reset.prereset = hpt366_prereset,
.cable_detect = hpt36x_cable_detect,
.mode_filter = hpt366_filter,
.set_piomode = hpt366_set_piomode,
diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
index ce3c5eaa7e76..07e3a984cbb1 100644
--- a/drivers/ata/pata_hpt37x.c
+++ b/drivers/ata/pata_hpt37x.c
@@ -218,8 +218,8 @@ static u32 hpt37x_find_mode(struct ata_port *ap, int speed)
return 0xffffffffU; /* silence compiler warning */
}
-static int hpt_dma_blacklisted(const struct ata_device *dev, char *modestr,
- const char * const list[])
+static int hpt_dma_broken(const struct ata_device *dev, char *modestr,
+ const char * const list[])
{
unsigned char model_num[ATA_ID_PROD_LEN + 1];
int i;
@@ -281,9 +281,9 @@ static const char * const bad_ata100_5[] = {
static unsigned int hpt370_filter(struct ata_device *adev, unsigned int mask)
{
if (adev->class == ATA_DEV_ATA) {
- if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33))
+ if (hpt_dma_broken(adev, "UDMA", bad_ata33))
mask &= ~ATA_MASK_UDMA;
- if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5))
+ if (hpt_dma_broken(adev, "UDMA100", bad_ata100_5))
mask &= ~(0xE0 << ATA_SHIFT_UDMA);
}
return mask;
@@ -300,7 +300,7 @@ static unsigned int hpt370_filter(struct ata_device *adev, unsigned int mask)
static unsigned int hpt370a_filter(struct ata_device *adev, unsigned int mask)
{
if (adev->class == ATA_DEV_ATA) {
- if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5))
+ if (hpt_dma_broken(adev, "UDMA100", bad_ata100_5))
mask &= ~(0xE0 << ATA_SHIFT_UDMA);
}
return mask;
@@ -526,7 +526,7 @@ static void hpt37x_bmdma_stop(struct ata_queued_cmd *qc)
}
-static struct scsi_host_template hpt37x_sht = {
+static const struct scsi_host_template hpt37x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -543,7 +543,7 @@ static struct ata_port_operations hpt370_port_ops = {
.cable_detect = hpt37x_cable_detect,
.set_piomode = hpt37x_set_piomode,
.set_dmamode = hpt37x_set_dmamode,
- .prereset = hpt37x_pre_reset,
+ .reset.prereset = hpt37x_pre_reset,
};
/*
@@ -567,7 +567,7 @@ static struct ata_port_operations hpt302_port_ops = {
.cable_detect = hpt37x_cable_detect,
.set_piomode = hpt37x_set_piomode,
.set_dmamode = hpt37x_set_dmamode,
- .prereset = hpt37x_pre_reset,
+ .reset.prereset = hpt37x_pre_reset,
};
/*
diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
index 617c95522f43..2cc57fcf2c46 100644
--- a/drivers/ata/pata_hpt3x2n.c
+++ b/drivers/ata/pata_hpt3x2n.c
@@ -337,7 +337,7 @@ static unsigned int hpt3x2n_qc_issue(struct ata_queued_cmd *qc)
return ata_bmdma_qc_issue(qc);
}
-static struct scsi_host_template hpt3x2n_sht = {
+static const struct scsi_host_template hpt3x2n_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -356,7 +356,7 @@ static struct ata_port_operations hpt3xxn_port_ops = {
.cable_detect = hpt3x2n_cable_detect,
.set_piomode = hpt3x2n_set_piomode,
.set_dmamode = hpt3x2n_set_dmamode,
- .prereset = hpt3x2n_pre_reset,
+ .reset.prereset = hpt3x2n_pre_reset,
};
/*
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 83974d5eb387..d65c586b5ad0 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -136,7 +136,7 @@ static int hpt3x3_atapi_dma(struct ata_queued_cmd *qc)
#endif /* CONFIG_PATA_HPT3X3_DMA */
-static struct scsi_host_template hpt3x3_sht = {
+static const struct scsi_host_template hpt3x3_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c
index 498383cb6e29..70f056e47e6b 100644
--- a/drivers/ata/pata_icside.c
+++ b/drivers/ata/pata_icside.c
@@ -298,7 +298,7 @@ static int icside_dma_init(struct pata_icside_info *info)
}
-static struct scsi_host_template pata_icside_sht = {
+static const struct scsi_host_template pata_icside_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = SG_MAX_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
@@ -328,8 +328,6 @@ static void pata_icside_postreset(struct ata_link *link, unsigned int *classes)
static struct ata_port_operations pata_icside_port_ops = {
.inherits = &ata_bmdma_port_ops,
- /* no need to build any PRD tables for DMA */
- .qc_prep = ata_noop_qc_prep,
.sff_data_xfer = ata_sff_data_xfer32,
.bmdma_setup = pata_icside_bmdma_setup,
.bmdma_start = pata_icside_bmdma_start,
@@ -338,7 +336,7 @@ static struct ata_port_operations pata_icside_port_ops = {
.cable_detect = ata_cable_40wire,
.set_dmamode = pata_icside_set_dmamode,
- .postreset = pata_icside_postreset,
+ .reset.postreset = pata_icside_postreset,
.port_start = ATA_OP_NULL, /* don't need PRD table */
};
diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c
index 150939275b1b..b37682b0578f 100644
--- a/drivers/ata/pata_imx.c
+++ b/drivers/ata/pata_imx.c
@@ -97,7 +97,7 @@ static void pata_imx_set_piomode(struct ata_port *ap, struct ata_device *adev)
__raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
}
-static struct scsi_host_template pata_imx_sht = {
+static const struct scsi_host_template pata_imx_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -141,21 +141,15 @@ static int pata_imx_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->clk = devm_clk_get(&pdev->dev, NULL);
+ priv->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "Failed to get clock\n");
+ dev_err(&pdev->dev, "Failed to get and enable clock\n");
return PTR_ERR(priv->clk);
}
- ret = clk_prepare_enable(priv->clk);
- if (ret)
- return ret;
-
host = ata_host_alloc(&pdev->dev, 1);
- if (!host) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!host)
+ return -ENOMEM;
host->private_data = priv;
ap = host->ports[0];
@@ -164,12 +158,9 @@ static int pata_imx_probe(struct platform_device *pdev)
ap->pio_mask = ATA_PIO4;
ap->flags |= ATA_FLAG_SLAVE_POSS;
- io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->host_regs = devm_ioremap_resource(&pdev->dev, io_res);
- if (IS_ERR(priv->host_regs)) {
- ret = PTR_ERR(priv->host_regs);
- goto err;
- }
+ priv->host_regs = devm_platform_get_and_ioremap_resource(pdev, 0, &io_res);
+ if (IS_ERR(priv->host_regs))
+ return PTR_ERR(priv->host_regs);
ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA;
ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL;
@@ -195,16 +186,12 @@ static int pata_imx_probe(struct platform_device *pdev)
&pata_imx_sht);
if (ret)
- goto err;
+ return ret;
return 0;
-err:
- clk_disable_unprepare(priv->clk);
-
- return ret;
}
-static int pata_imx_remove(struct platform_device *pdev)
+static void pata_imx_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct pata_imx_priv *priv = host->private_data;
@@ -212,10 +199,6 @@ static int pata_imx_remove(struct platform_device *pdev)
ata_host_detach(host);
__raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
-
- clk_disable_unprepare(priv->clk);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c
index 43bb224430d3..0f77e0424066 100644
--- a/drivers/ata/pata_isapnp.c
+++ b/drivers/ata/pata_isapnp.c
@@ -20,7 +20,7 @@
#define DRV_NAME "pata_isapnp"
#define DRV_VERSION "0.2.5"
-static struct scsi_host_template isapnp_sht = {
+static const struct scsi_host_template isapnp_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -82,6 +82,9 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev
if (pnp_port_valid(idev, 1)) {
ctl_addr = devm_ioport_map(&idev->dev,
pnp_port_start(idev, 1), 1);
+ if (!ctl_addr)
+ return -ENOMEM;
+
ap->ioaddr.altstatus_addr = ctl_addr;
ap->ioaddr.ctl_addr = ctl_addr;
ap->ops = &isapnp_port_ops;
diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c
index 8a3e8778163c..a6f2cfc1602e 100644
--- a/drivers/ata/pata_it8213.c
+++ b/drivers/ata/pata_it8213.c
@@ -81,7 +81,7 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev)
int control = 0;
/*
- * See Intel Document 298600-004 for the timing programing rules
+ * See Intel Document 298600-004 for the timing programming rules
* for PIIX/ICH. The 8213 is a clone so very similar
*/
@@ -228,7 +228,7 @@ static void it8213_set_dmamode (struct ata_port *ap, struct ata_device *adev)
pci_write_config_byte(dev, 0x48, udma_enable);
}
-static struct scsi_host_template it8213_sht = {
+static const struct scsi_host_template it8213_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -238,7 +238,7 @@ static struct ata_port_operations it8213_ops = {
.cable_detect = it8213_cable_detect,
.set_piomode = it8213_set_piomode,
.set_dmamode = it8213_set_dmamode,
- .prereset = it8213_pre_reset,
+ .reset.prereset = it8213_pre_reset,
};
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 8a5b4e0079ab..fc762dcc61bf 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -75,6 +75,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
@@ -519,9 +520,9 @@ static void it821x_dev_config(struct ata_device *adev)
}
/* This is a controller firmware triggered funny, don't
report the drive faulty! */
- adev->horkage &= ~ATA_HORKAGE_DIAGNOSTIC;
+ adev->quirks &= ~ATA_QUIRK_DIAGNOSTIC;
/* No HPA in 'smart' mode */
- adev->horkage |= ATA_HORKAGE_BROKEN_HPA;
+ adev->quirks |= ATA_QUIRK_BROKEN_HPA;
}
/**
@@ -632,9 +633,9 @@ static void it821x_display_disk(struct ata_port *ap, int n, u8 *buf)
cbl = "";
if (mode)
- snprintf(mbuf, 8, "%5s%d", mtype, mode - 1);
+ snprintf(mbuf, sizeof(mbuf), "%5s%d", mtype, mode - 1);
else
- strcpy(mbuf, "PIO");
+ strscpy(mbuf, "PIO");
if (buf[52] == 4)
ata_port_info(ap, "%d: %-6s %-8s %s %s\n",
n, mbuf, types[buf[52]], id, cbl);
@@ -800,7 +801,7 @@ static int it821x_rdc_cable(struct ata_port *ap)
return ATA_CBL_PATA80;
}
-static struct scsi_host_template it821x_sht = {
+static const struct scsi_host_template it821x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index e225913a619d..80f6a91acf6f 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/libata.h>
#include <linux/irq.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <scsi/scsi_host.h>
@@ -173,7 +174,7 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
return words << 1;
}
-static struct scsi_host_template ixp4xx_sht = {
+static const struct scsi_host_template ixp4xx_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -241,12 +242,6 @@ static int ixp4xx_pata_probe(struct platform_device *pdev)
int ret;
int irq;
- cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
- if (!cmd || !ctl)
- return -EINVAL;
-
ixpp = devm_kzalloc(dev, sizeof(*ixpp), GFP_KERNEL);
if (!ixpp)
return -ENOMEM;
@@ -270,18 +265,18 @@ static int ixp4xx_pata_probe(struct platform_device *pdev)
if (ret)
return ret;
- ixpp->cmd = devm_ioremap_resource(dev, cmd);
- ixpp->ctl = devm_ioremap_resource(dev, ctl);
- if (IS_ERR(ixpp->cmd) || IS_ERR(ixpp->ctl))
- return -ENOMEM;
+ ixpp->cmd = devm_platform_get_and_ioremap_resource(pdev, 0, &cmd);
+ if (IS_ERR(ixpp->cmd))
+ return PTR_ERR(ixpp->cmd);
+
+ ixpp->ctl = devm_platform_get_and_ioremap_resource(pdev, 1, &ctl);
+ if (IS_ERR(ixpp->ctl))
+ return PTR_ERR(ixpp->ctl);
irq = platform_get_irq(pdev, 0);
- if (irq > 0)
- irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
- else if (irq < 0)
+ if (irq < 0)
return irq;
- else
- return -EINVAL;
+ irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
/* Just one port to set up */
ixp4xx_setup_port(ixpp->host->ports[0], ixpp, cmd->start, ctl->start);
@@ -295,6 +290,7 @@ static const struct of_device_id ixp4xx_pata_of_match[] = {
{ .compatible = "intel,ixp4xx-compact-flash", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, ixp4xx_pata_of_match);
static struct platform_driver ixp4xx_pata_platform_driver = {
.driver = {
diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c
index d1b3ce8958dd..b885f33e8980 100644
--- a/drivers/ata/pata_jmicron.c
+++ b/drivers/ata/pata_jmicron.c
@@ -107,13 +107,13 @@ static int jmicron_pre_reset(struct ata_link *link, unsigned long deadline)
/* No PIO or DMA methods needed for this device */
-static struct scsi_host_template jmicron_sht = {
+static const struct scsi_host_template jmicron_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
static struct ata_port_operations jmicron_ops = {
.inherits = &ata_bmdma_port_ops,
- .prereset = jmicron_pre_reset,
+ .reset.prereset = jmicron_pre_reset,
};
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 0a8bf09a5c19..e7ac142c2423 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -173,8 +173,6 @@ static int legacy_port[NR_HOST] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
static struct legacy_probe probe_list[NR_HOST];
static struct legacy_data legacy_data[NR_HOST];
static struct ata_host *legacy_host[NR_HOST];
-static int nr_legacy_host;
-
/**
* legacy_probe_add - Add interface to probe list
@@ -250,7 +248,7 @@ static int legacy_set_mode(struct ata_link *link, struct ata_device **unused)
return 0;
}
-static struct scsi_host_template legacy_sht = {
+static const struct scsi_host_template legacy_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -315,9 +313,10 @@ static void pdc20230_set_piomode(struct ata_port *ap, struct ata_device *adev)
outb(inb(0x1F4) & 0x07, 0x1F4);
rt = inb(0x1F3);
- rt &= 0x07 << (3 * adev->devno);
+ rt &= ~(0x07 << (3 * !adev->devno));
if (pio)
- rt |= (1 + 3 * pio) << (3 * adev->devno);
+ rt |= (1 + 3 * pio) << (3 * !adev->devno);
+ outb(rt, 0x1F3);
udelay(100);
outb(inb(0x1F2) | 0x01, 0x1F2);
@@ -1275,9 +1274,11 @@ static __exit void legacy_exit(void)
{
int i;
- for (i = 0; i < nr_legacy_host; i++) {
+ for (i = 0; i < NR_HOST; i++) {
struct legacy_data *ld = &legacy_data[i];
- ata_host_detach(legacy_host[i]);
+
+ if (legacy_host[i])
+ ata_host_detach(legacy_host[i]);
platform_device_unregister(ld->platform_dev);
}
}
diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c
index bfea2be2959a..9eefdc5df5df 100644
--- a/drivers/ata/pata_macio.c
+++ b/drivers/ata/pata_macio.c
@@ -21,6 +21,7 @@
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/scatterlist.h>
+#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/gfp.h>
#include <linux/pci.h>
@@ -207,6 +208,19 @@ static const char* macio_ata_names[] = {
/* Don't let a DMA segment go all the way to 64K */
#define MAX_DBDMA_SEG 0xff00
+#ifdef CONFIG_PAGE_SIZE_64KB
+/*
+ * The SCSI core requires the segment size to cover at least a page, so
+ * for 64K page size kernels it must be at least 64K. However the
+ * hardware can't handle 64K, so pata_macio_qc_prep() will split large
+ * requests. To handle the split requests the tablesize must be halved.
+ */
+#define PATA_MACIO_MAX_SEGMENT_SIZE SZ_64K
+#define PATA_MACIO_SG_TABLESIZE (MAX_DCMDS / 2)
+#else
+#define PATA_MACIO_MAX_SEGMENT_SIZE MAX_DBDMA_SEG
+#define PATA_MACIO_SG_TABLESIZE MAX_DCMDS
+#endif
/*
* Wait 1s for disk to answer on IDE bus after a hard reset
@@ -540,7 +554,8 @@ static enum ata_completion_errors pata_macio_qc_prep(struct ata_queued_cmd *qc)
while (sg_len) {
/* table overflow should never happen */
- BUG_ON (pi++ >= MAX_DCMDS);
+ if (WARN_ON_ONCE(pi >= MAX_DCMDS))
+ return AC_ERR_SYSTEM;
len = (sg_len < MAX_DBDMA_SEG) ? sg_len : MAX_DBDMA_SEG;
table->command = cpu_to_le16(write ? OUTPUT_MORE: INPUT_MORE);
@@ -552,11 +567,13 @@ static enum ata_completion_errors pata_macio_qc_prep(struct ata_queued_cmd *qc)
addr += len;
sg_len -= len;
++table;
+ ++pi;
}
}
/* Should never happen according to Tejun */
- BUG_ON(!pi);
+ if (WARN_ON_ONCE(!pi))
+ return AC_ERR_SYSTEM;
/* Convert the last command to an input/output */
table--;
@@ -666,8 +683,7 @@ static u8 pata_macio_bmdma_status(struct ata_port *ap)
* a multi-block transfer.
*
* - The dbdma fifo hasn't yet finished flushing to
- * to system memory when the disk interrupt occurs.
- *
+ * system memory when the disk interrupt occurs.
*/
/* First check for errors */
@@ -742,7 +758,7 @@ static void pata_macio_irq_clear(struct ata_port *ap)
static void pata_macio_reset_hw(struct pata_macio_priv *priv, int resume)
{
- dev_dbg(priv->dev, "Enabling & resetting... \n");
+ dev_dbg(priv->dev, "Enabling & resetting...\n");
if (priv->mediabay)
return;
@@ -796,7 +812,8 @@ static void pata_macio_reset_hw(struct pata_macio_priv *priv, int resume)
/* Hook the standard slave config to fixup some HW related alignment
* restrictions
*/
-static int pata_macio_slave_config(struct scsi_device *sdev)
+static int pata_macio_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
struct pata_macio_priv *priv = ap->private_data;
@@ -805,7 +822,7 @@ static int pata_macio_slave_config(struct scsi_device *sdev)
int rc;
/* First call original */
- rc = ata_scsi_slave_config(sdev);
+ rc = ata_scsi_sdev_configure(sdev, lim);
if (rc)
return rc;
@@ -814,8 +831,8 @@ static int pata_macio_slave_config(struct scsi_device *sdev)
/* OHare has issues with non cache aligned DMA on some chipsets */
if (priv->kind == controller_ohare) {
- blk_queue_update_dma_alignment(sdev->request_queue, 31);
- blk_queue_update_dma_pad(sdev->request_queue, 31);
+ lim->dma_alignment = 31;
+ lim->dma_pad_mask = 31;
/* Tell the world about it */
ata_dev_info(dev, "OHare alignment limits applied\n");
@@ -829,8 +846,8 @@ static int pata_macio_slave_config(struct scsi_device *sdev)
/* Shasta and K2 seem to have "issues" with reads ... */
if (priv->kind == controller_sh_ata6 || priv->kind == controller_k2_ata6) {
/* Allright these are bad, apply restrictions */
- blk_queue_update_dma_alignment(sdev->request_queue, 15);
- blk_queue_update_dma_pad(sdev->request_queue, 15);
+ lim->dma_alignment = 15;
+ lim->dma_pad_mask = 15;
/* We enable MWI and hack cache line size directly here, this
* is specific to this chipset and not normal values, we happen
@@ -909,19 +926,16 @@ static int pata_macio_do_resume(struct pata_macio_priv *priv)
}
#endif /* CONFIG_PM_SLEEP */
-static struct scsi_host_template pata_macio_sht = {
+static const struct scsi_host_template pata_macio_sht = {
__ATA_BASE_SHT(DRV_NAME),
- .sg_tablesize = MAX_DCMDS,
+ .sg_tablesize = PATA_MACIO_SG_TABLESIZE,
/* We may not need that strict one */
.dma_boundary = ATA_DMA_BOUNDARY,
- /* Not sure what the real max is but we know it's less than 64K, let's
- * use 64K minus 256
- */
- .max_segment_size = MAX_DBDMA_SEG,
- .slave_configure = pata_macio_slave_config,
+ .max_segment_size = PATA_MACIO_MAX_SEGMENT_SIZE,
+ .sdev_configure = pata_macio_sdev_configure,
.sdev_groups = ata_common_sdev_groups,
.can_queue = ATA_DEF_QUEUE,
- .tag_alloc_policy = BLK_TAG_ALLOC_RR,
+ .tag_alloc_policy_rr = true,
};
static struct ata_port_operations pata_macio_ops = {
@@ -1188,7 +1202,7 @@ static int pata_macio_attach(struct macio_dev *mdev,
return rc;
}
-static int pata_macio_detach(struct macio_dev *mdev)
+static void pata_macio_detach(struct macio_dev *mdev)
{
struct ata_host *host = macio_get_drvdata(mdev);
struct pata_macio_priv *priv = host->private_data;
@@ -1203,8 +1217,6 @@ static int pata_macio_detach(struct macio_dev *mdev)
ata_host_detach(host);
unlock_media_bay(priv->mdev->media_bay);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -1286,7 +1298,7 @@ static int pata_macio_pci_attach(struct pci_dev *pdev,
priv->dev = &pdev->dev;
/* Get MMIO regions */
- if (pci_request_regions(pdev, "pata-macio")) {
+ if (pcim_request_all_regions(pdev, "pata-macio")) {
dev_err(&pdev->dev,
"Cannot obtain PCI resources\n");
return -EBUSY;
@@ -1373,9 +1385,6 @@ static struct pci_driver pata_macio_pci_driver = {
.suspend = pata_macio_pci_suspend,
.resume = pata_macio_pci_resume,
#endif
- .driver = {
- .owner = THIS_MODULE,
- },
};
MODULE_DEVICE_TABLE(pci, pata_macio_pci_match);
diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c
index 014ccb0f45dc..deab67328388 100644
--- a/drivers/ata/pata_marvell.c
+++ b/drivers/ata/pata_marvell.c
@@ -92,14 +92,14 @@ static int marvell_cable_detect(struct ata_port *ap)
/* No PIO or DMA methods needed for this device */
-static struct scsi_host_template marvell_sht = {
+static const struct scsi_host_template marvell_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
static struct ata_port_operations marvell_ops = {
.inherits = &ata_bmdma_port_ops,
.cable_detect = marvell_cable_detect,
- .prereset = marvell_pre_reset,
+ .reset.prereset = marvell_pre_reset,
};
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index 6559b606736d..210a63283f62 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -19,9 +19,10 @@
#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/libata.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/types.h>
#include <asm/cacheflush.h>
@@ -606,7 +607,7 @@ mpc52xx_ata_task_irq(int irq, void *vpriv)
return IRQ_HANDLED;
}
-static struct scsi_host_template mpc52xx_ata_sht = {
+static const struct scsi_host_template mpc52xx_ata_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -619,7 +620,6 @@ static struct ata_port_operations mpc52xx_ata_port_ops = {
.bmdma_start = mpc52xx_bmdma_start,
.bmdma_stop = mpc52xx_bmdma_stop,
.bmdma_status = mpc52xx_bmdma_status,
- .qc_prep = ata_noop_qc_prep,
};
static int mpc52xx_ata_init_one(struct device *dev,
@@ -731,7 +731,7 @@ static int mpc52xx_ata_probe(struct platform_device *op)
udma_mask = ATA_UDMA2 & ((1 << (*prop + 1)) - 1);
ata_irq = irq_of_parse_and_map(op->dev.of_node, 0);
- if (ata_irq == NO_IRQ) {
+ if (!ata_irq) {
dev_err(&op->dev, "error mapping irq\n");
return -EINVAL;
}
@@ -800,8 +800,7 @@ static int mpc52xx_ata_probe(struct platform_device *op)
return rv;
}
-static int
-mpc52xx_ata_remove(struct platform_device *op)
+static void mpc52xx_ata_remove(struct platform_device *op)
{
struct ata_host *host = platform_get_drvdata(op);
struct mpc52xx_ata_priv *priv = host->private_data;
@@ -815,8 +814,6 @@ mpc52xx_ata_remove(struct platform_device *op)
irq_dispose_mapping(task_irq);
bcom_ata_release(priv->dmatsk);
irq_dispose_mapping(priv->ata_irq);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
index 8fda0e32c1ab..ce310ae7c93a 100644
--- a/drivers/ata/pata_mpiix.c
+++ b/drivers/ata/pata_mpiix.c
@@ -136,7 +136,7 @@ static unsigned int mpiix_qc_issue(struct ata_queued_cmd *qc)
return ata_sff_qc_issue(qc);
}
-static struct scsi_host_template mpiix_sht = {
+static const struct scsi_host_template mpiix_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -145,7 +145,7 @@ static struct ata_port_operations mpiix_port_ops = {
.qc_issue = mpiix_qc_issue,
.cable_detect = ata_cable_40wire,
.set_piomode = mpiix_set_piomode,
- .prereset = mpiix_pre_reset,
+ .reset.prereset = mpiix_pre_reset,
.sff_data_xfer = ata_sff_data_xfer32,
};
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index 06929e77c491..c0b2897fcf40 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -31,7 +31,7 @@ static unsigned int netcell_read_id(struct ata_device *adev,
return err_mask;
}
-static struct scsi_host_template netcell_sht = {
+static const struct scsi_host_template netcell_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_ninja32.c b/drivers/ata/pata_ninja32.c
index f9255d6fd194..76a91013d27d 100644
--- a/drivers/ata/pata_ninja32.c
+++ b/drivers/ata/pata_ninja32.c
@@ -77,7 +77,7 @@ static void ninja32_dev_select(struct ata_port *ap, unsigned int device)
}
}
-static struct scsi_host_template ninja32_sht = {
+static const struct scsi_host_template ninja32_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
index ca3ab2736fef..bdb55c1a3280 100644
--- a/drivers/ata/pata_ns87410.c
+++ b/drivers/ata/pata_ns87410.c
@@ -114,7 +114,7 @@ static unsigned int ns87410_qc_issue(struct ata_queued_cmd *qc)
return ata_sff_qc_issue(qc);
}
-static struct scsi_host_template ns87410_sht = {
+static const struct scsi_host_template ns87410_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -123,7 +123,7 @@ static struct ata_port_operations ns87410_port_ops = {
.qc_issue = ns87410_qc_issue,
.cable_detect = ata_cable_40wire,
.set_piomode = ns87410_set_piomode,
- .prereset = ns87410_pre_reset,
+ .reset.prereset = ns87410_pre_reset,
};
static int ns87410_init_one(struct pci_dev *dev, const struct pci_device_id *id)
diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c
index 9dd6bffefb48..c697219a61a2 100644
--- a/drivers/ata/pata_ns87415.c
+++ b/drivers/ata/pata_ns87415.c
@@ -260,7 +260,7 @@ static u8 ns87560_check_status(struct ata_port *ap)
* LOCKING:
* Inherited from caller.
*/
-void ns87560_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+static void ns87560_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
{
struct ata_ioports *ioaddr = &ap->ioaddr;
@@ -320,7 +320,7 @@ static struct ata_port_operations ns87560_pata_ops = {
};
#endif
-static struct scsi_host_template ns87415_sht = {
+static const struct scsi_host_template ns87415_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c
index 35608a0cf552..df42ebe98db7 100644
--- a/drivers/ata/pata_octeon_cf.c
+++ b/drivers/ata/pata_octeon_cf.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <scsi/scsi_host.h>
@@ -58,7 +59,7 @@ struct octeon_cf_port {
u64 dma_base;
};
-static struct scsi_host_template octeon_cf_sht = {
+static const struct scsi_host_template octeon_cf_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -67,7 +68,7 @@ module_param(enable_dma, int, 0444);
MODULE_PARM_DESC(enable_dma,
"Enable use of DMA on interfaces that support it (0=no dma [default], 1=use dma)");
-/**
+/*
* Convert nanosecond based time to setting used in the
* boot bus timing register, based on timing multiple
*/
@@ -114,7 +115,7 @@ static void octeon_cf_set_boot_reg_cfg(int cs, unsigned int multiplier)
cvmx_write_csr(CVMX_MIO_BOOT_REG_CFGX(cs), reg_cfg.u64);
}
-/**
+/*
* Called after libata determines the needed PIO mode. This
* function programs the Octeon bootbus regions to support the
* timing requirements of the PIO mode.
@@ -182,7 +183,7 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
reg_tim.s.ale = 0;
/* Not used */
reg_tim.s.page = 0;
- /* Time after IORDY to coninue to assert the data */
+ /* Time after IORDY to continue to assert the data */
reg_tim.s.wait = 0;
/* Time to wait to complete the cycle. */
reg_tim.s.pause = pause;
@@ -278,7 +279,7 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev)
cvmx_write_csr(cf_port->dma_base + DMA_TIM, dma_tim.u64);
}
-/**
+/*
* Handle an 8 bit I/O request.
*
* @qc: Queued command
@@ -317,7 +318,7 @@ static unsigned int octeon_cf_data_xfer8(struct ata_queued_cmd *qc,
return buflen;
}
-/**
+/*
* Handle a 16 bit I/O request.
*
* @qc: Queued command
@@ -372,7 +373,7 @@ static unsigned int octeon_cf_data_xfer16(struct ata_queued_cmd *qc,
return buflen;
}
-/**
+/*
* Read the taskfile for 16bit non-True IDE only.
*/
static void octeon_cf_tf_read16(struct ata_port *ap, struct ata_taskfile *tf)
@@ -453,7 +454,7 @@ static int octeon_cf_softreset16(struct ata_link *link, unsigned int *classes,
return 0;
}
-/**
+/*
* Load the taskfile for 16bit non-True IDE only. The device_addr is
* not loaded, we do this as part of octeon_cf_exec_command16.
*/
@@ -525,7 +526,7 @@ static void octeon_cf_dma_setup(struct ata_queued_cmd *qc)
ap->ops->sff_exec_command(ap, &qc->tf);
}
-/**
+/*
* Start a DMA transfer that was already setup
*
* @qc: Information about the DMA
@@ -580,7 +581,7 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc)
cvmx_write_csr(cf_port->dma_base + DMA_CFG, mio_boot_dma_cfg.u64);
}
-/**
+/*
*
* LOCKING:
* spin_lock_irqsave(host lock)
@@ -788,7 +789,6 @@ static unsigned int octeon_cf_qc_issue(struct ata_queued_cmd *qc)
static struct ata_port_operations octeon_cf_ops = {
.inherits = &ata_sff_port_ops,
.check_atapi_dma = octeon_cf_check_atapi_dma,
- .qc_prep = ata_noop_qc_prep,
.qc_issue = octeon_cf_qc_issue,
.sff_dev_select = octeon_cf_dev_select,
.sff_irq_on = octeon_cf_ata_port_noaction,
@@ -804,9 +804,7 @@ static int octeon_cf_probe(struct platform_device *pdev)
struct resource *res_cs0, *res_cs1;
bool is_16bit;
- const __be32 *cs_num;
- struct property *reg_prop;
- int n_addr, n_size, reg_len;
+ u64 reg;
struct device_node *node;
void __iomem *cs0;
void __iomem *cs1 = NULL;
@@ -816,8 +814,8 @@ static int octeon_cf_probe(struct platform_device *pdev)
irq_handler_t irq_handler = NULL;
void __iomem *base;
struct octeon_cf_port *cf_port;
- int rv = -ENOMEM;
u32 bus_width;
+ int rv;
node = pdev->dev.of_node;
if (node == NULL)
@@ -834,15 +832,10 @@ static int octeon_cf_probe(struct platform_device *pdev)
else
is_16bit = false;
- n_addr = of_n_addr_cells(node);
- n_size = of_n_size_cells(node);
-
- reg_prop = of_find_property(node, "reg", &reg_len);
- if (!reg_prop || reg_len < sizeof(__be32))
- return -EINVAL;
-
- cs_num = reg_prop->value;
- cf_port->cs0 = be32_to_cpup(cs_num);
+ rv = of_property_read_reg(node, 0, &reg, NULL);
+ if (rv < 0)
+ return rv;
+ cf_port->cs0 = upper_32_bits(reg);
if (cf_port->is_true_ide) {
struct device_node *dma_node;
@@ -884,13 +877,12 @@ static int octeon_cf_probe(struct platform_device *pdev)
cs1 = devm_ioremap(&pdev->dev, res_cs1->start,
resource_size(res_cs1));
if (!cs1)
- return rv;
-
- if (reg_len < (n_addr + n_size + 1) * sizeof(__be32))
return -EINVAL;
- cs_num += n_addr + n_size;
- cf_port->cs1 = be32_to_cpup(cs_num);
+ rv = of_property_read_reg(node, 1, &reg, NULL);
+ if (rv < 0)
+ return rv;
+ cf_port->cs1 = upper_32_bits(reg);
}
res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -900,12 +892,12 @@ static int octeon_cf_probe(struct platform_device *pdev)
cs0 = devm_ioremap(&pdev->dev, res_cs0->start,
resource_size(res_cs0));
if (!cs0)
- return rv;
+ return -ENOMEM;
/* allocate host */
host = ata_host_alloc(&pdev->dev, 1);
if (!host)
- return rv;
+ return -ENOMEM;
ap = host->ports[0];
ap->private_data = cf_port;
@@ -943,14 +935,13 @@ static int octeon_cf_probe(struct platform_device *pdev)
ap->mwdma_mask = enable_dma ? ATA_MWDMA4 : 0;
/* True IDE mode needs a timer to poll for not-busy. */
- hrtimer_init(&cf_port->delayed_finish, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- cf_port->delayed_finish.function = octeon_cf_delayed_finish;
+ hrtimer_setup(&cf_port->delayed_finish, octeon_cf_delayed_finish, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
} else {
/* 16 bit but not True IDE */
base = cs0 + 0x800;
octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16;
- octeon_cf_ops.softreset = octeon_cf_softreset16;
+ octeon_cf_ops.reset.softreset = octeon_cf_softreset16;
octeon_cf_ops.sff_check_status = octeon_cf_check_status16;
octeon_cf_ops.sff_tf_read = octeon_cf_tf_read16;
octeon_cf_ops.sff_tf_load = octeon_cf_tf_load16;
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index ac5a633c00a5..178b28eff170 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -15,7 +15,7 @@
#define DRV_NAME "pata_of_platform"
-static struct scsi_host_template pata_platform_sht = {
+static const struct scsi_host_template pata_platform_sht = {
ATA_PIO_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index 22a020374410..81a7f3eb5654 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -70,7 +70,7 @@ static void oldpiix_set_piomode (struct ata_port *ap, struct ata_device *adev)
int control = 0;
/*
- * See Intel Document 298600-004 for the timing programing rules
+ * See Intel Document 298600-004 for the timing programming rules
* for PIIX/ICH. Note that the early PIIX does not have the slave
* timing port at 0x44.
*/
@@ -204,7 +204,7 @@ static unsigned int oldpiix_qc_issue(struct ata_queued_cmd *qc)
}
-static struct scsi_host_template oldpiix_sht = {
+static const struct scsi_host_template oldpiix_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -214,7 +214,7 @@ static struct ata_port_operations oldpiix_pata_ops = {
.cable_detect = ata_cable_40wire,
.set_piomode = oldpiix_set_piomode,
.set_dmamode = oldpiix_set_dmamode,
- .prereset = oldpiix_pre_reset,
+ .reset.prereset = oldpiix_pre_reset,
};
diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
index 01976c4e4033..3db1b95d1404 100644
--- a/drivers/ata/pata_opti.c
+++ b/drivers/ata/pata_opti.c
@@ -148,7 +148,7 @@ static void opti_set_piomode(struct ata_port *ap, struct ata_device *adev)
opti_write_reg(ap, 0x85, CNTRL_REG);
}
-static struct scsi_host_template opti_sht = {
+static const struct scsi_host_template opti_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -156,7 +156,7 @@ static struct ata_port_operations opti_port_ops = {
.inherits = &ata_sff_port_ops,
.cable_detect = ata_cable_40wire,
.set_piomode = opti_set_piomode,
- .prereset = opti_pre_reset,
+ .reset.prereset = opti_pre_reset,
};
static int opti_init_one(struct pci_dev *dev, const struct pci_device_id *id)
diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
index ad1090b90e52..b42dba5f4e05 100644
--- a/drivers/ata/pata_optidma.c
+++ b/drivers/ata/pata_optidma.c
@@ -322,7 +322,9 @@ static int optidma_set_mode(struct ata_link *link, struct ata_device **r_failed)
u8 r;
int nybble = 4 * ap->port_no;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- int rc = ata_do_set_mode(link, r_failed);
+ int rc;
+
+ rc = ata_set_mode(link, r_failed);
if (rc == 0) {
pci_read_config_byte(pdev, 0x43, &r);
@@ -334,7 +336,7 @@ static int optidma_set_mode(struct ata_link *link, struct ata_device **r_failed)
return rc;
}
-static struct scsi_host_template optidma_sht = {
+static const struct scsi_host_template optidma_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -344,7 +346,7 @@ static struct ata_port_operations optidma_port_ops = {
.set_piomode = optidma_set_pio_mode,
.set_dmamode = optidma_set_dma_mode,
.set_mode = optidma_set_mode,
- .prereset = optidma_pre_reset,
+ .reset.prereset = optidma_pre_reset,
};
static struct ata_port_operations optiplus_port_ops = {
diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c
deleted file mode 100644
index 400e65190904..000000000000
--- a/drivers/ata/pata_palmld.c
+++ /dev/null
@@ -1,137 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * drivers/ata/pata_palmld.c
- *
- * Driver for IDE channel in Palm LifeDrive
- *
- * Based on research of:
- * Alex Osborne <ato@meshy.org>
- *
- * Rewrite for mainline:
- * Marek Vasut <marek.vasut@gmail.com>
- *
- * Rewritten version based on pata_ixp4xx_cf.c:
- * ixp4xx PATA/Compact Flash driver
- * Copyright (C) 2006-07 Tower Technologies
- * Author: Alessandro Zummo <a.zummo@towertech.it>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/libata.h>
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-
-#include <scsi/scsi_host.h>
-
-#define DRV_NAME "pata_palmld"
-
-struct palmld_pata {
- struct ata_host *host;
- struct gpio_desc *power;
- struct gpio_desc *reset;
-};
-
-static struct scsi_host_template palmld_sht = {
- ATA_PIO_SHT(DRV_NAME),
-};
-
-static struct ata_port_operations palmld_port_ops = {
- .inherits = &ata_sff_port_ops,
- .sff_data_xfer = ata_sff_data_xfer32,
- .cable_detect = ata_cable_40wire,
-};
-
-static int palmld_pata_probe(struct platform_device *pdev)
-{
- struct palmld_pata *lda;
- struct ata_port *ap;
- void __iomem *mem;
- struct device *dev = &pdev->dev;
- int ret;
-
- lda = devm_kzalloc(dev, sizeof(*lda), GFP_KERNEL);
- if (!lda)
- return -ENOMEM;
-
- /* allocate host */
- lda->host = ata_host_alloc(dev, 1);
- if (!lda->host)
- return -ENOMEM;
-
- /* remap drive's physical memory address */
- mem = devm_platform_ioremap_resource(pdev, 0);
- if (!mem)
- return -ENOMEM;
-
- /* request and activate power and reset GPIOs */
- lda->power = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH);
- if (IS_ERR(lda->power))
- return PTR_ERR(lda->power);
- lda->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(lda->reset)) {
- gpiod_set_value(lda->power, 0);
- return PTR_ERR(lda->reset);
- }
-
- /* Assert reset to reset the drive */
- gpiod_set_value(lda->reset, 1);
- msleep(30);
- gpiod_set_value(lda->reset, 0);
- msleep(30);
-
- /* setup the ata port */
- ap = lda->host->ports[0];
- ap->ops = &palmld_port_ops;
- ap->pio_mask = ATA_PIO4;
- ap->flags |= ATA_FLAG_PIO_POLLING;
-
- /* memory mapping voodoo */
- ap->ioaddr.cmd_addr = mem + 0x10;
- ap->ioaddr.altstatus_addr = mem + 0xe;
- ap->ioaddr.ctl_addr = mem + 0xe;
-
- /* start the port */
- ata_sff_std_ports(&ap->ioaddr);
-
- /* activate host */
- ret = ata_host_activate(lda->host, 0, NULL, IRQF_TRIGGER_RISING,
- &palmld_sht);
- /* power down on failure */
- if (ret) {
- gpiod_set_value(lda->power, 0);
- return ret;
- }
-
- platform_set_drvdata(pdev, lda);
- return 0;
-}
-
-static int palmld_pata_remove(struct platform_device *pdev)
-{
- struct palmld_pata *lda = platform_get_drvdata(pdev);
-
- ata_platform_remove_one(pdev);
-
- /* power down the HDD */
- gpiod_set_value(lda->power, 0);
-
- return 0;
-}
-
-static struct platform_driver palmld_pata_platform_driver = {
- .driver = {
- .name = DRV_NAME,
- },
- .probe = palmld_pata_probe,
- .remove = palmld_pata_remove,
-};
-
-module_platform_driver(palmld_pata_platform_driver);
-
-MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
-MODULE_DESCRIPTION("PalmLD PATA driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
new file mode 100644
index 000000000000..2c953f5d1396
--- /dev/null
+++ b/drivers/ata/pata_parport/Kconfig
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: GPL-2.0
+
+comment "Parallel IDE protocol modules"
+ depends on PATA_PARPORT
+
+config PATA_PARPORT_ATEN
+ tristate "ATEN EH-100 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the ATEN EH-100 parallel port IDE
+ protocol. This protocol is used in some inexpensive low performance
+ parallel port kits made in Hong Kong.
+
+config PATA_PARPORT_BPCK
+ tristate "MicroSolutions backpack (Series 5) protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Micro Solutions BACKPACK
+ parallel port Series 5 IDE protocol. (Most BACKPACK drives made
+ before 1999 were Series 5) Series 5 drives will NOT always have the
+ Series noted on the bottom of the drive. Series 6 drivers will.
+
+ In other words, if your BACKPACK drive doesn't say "Series 6" on the
+ bottom, enable this option.
+
+config PATA_PARPORT_BPCK6
+ tristate "MicroSolutions backpack (Series 6) protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Micro Solutions BACKPACK
+ parallel port Series 6 IDE protocol. (Most BACKPACK drives made
+ after 1999 were Series 6) Series 6 drives will have the Series noted
+ on the bottom of the drive. Series 5 drivers don't always have it
+ noted.
+
+ In other words, if your BACKPACK drive says "Series 6" on the
+ bottom, enable this option.
+
+config PATA_PARPORT_COMM
+ tristate "DataStor Commuter protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Commuter parallel port IDE
+ protocol from DataStor.
+
+config PATA_PARPORT_DSTR
+ tristate "DataStor EP-2000 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the EP-2000 parallel port IDE
+ protocol from DataStor
+
+config PATA_PARPORT_FIT2
+ tristate "FIT TD-2000 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the TD-2000 parallel port IDE
+ protocol from Fidelity International Technology. This is a simple
+ (low speed) adapter that is used in some portable hard drives.
+
+config PATA_PARPORT_FIT3
+ tristate "FIT TD-3000 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the TD-3000 parallel port IDE
+ protocol from Fidelity International Technology. This protocol is
+ used in newer models of their portable disk, CD-ROM and PD/CD
+ devices.
+
+config PATA_PARPORT_EPAT
+ tristate "Shuttle EPAT/EPEZ protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the EPAT parallel port IDE protocol.
+ EPAT is a parallel port IDE adapter manufactured by Shuttle
+ Technology and widely used in devices from major vendors such as
+ Hewlett-Packard, SyQuest, Imation and Avatar.
+
+config PATA_PARPORT_EPATC8
+ bool "Support c7/c8 chips"
+ depends on PATA_PARPORT_EPAT
+ help
+ This option enables support for the newer Shuttle EP1284 (aka c7 and
+ c8) chip. You need this if you are using any recent Imation SuperDisk
+ (LS-120) drive.
+
+config PATA_PARPORT_EPIA
+ tristate "Shuttle EPIA protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the (obsolete) EPIA parallel port
+ IDE protocol from Shuttle Technology. This adapter can still be
+ found in some no-name kits.
+
+config PATA_PARPORT_FRIQ
+ tristate "Freecom IQ ASIC-2 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for version 2 of the Freecom IQ parallel
+ port IDE adapter. This adapter is used by the Maxell Superdisk
+ drive.
+
+config PATA_PARPORT_FRPW
+ tristate "FreeCom power protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Freecom power parallel port IDE
+ protocol.
+
+config PATA_PARPORT_KBIC
+ tristate "KingByte KBIC-951A/971A protocols"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the KBIC-951A and KBIC-971A parallel
+ port IDE protocols from KingByte Information Corp. KingByte's
+ adapters appear in many no-name portable disk and CD-ROM products,
+ especially in Europe.
+
+config PATA_PARPORT_KTTI
+ tristate "KT PHd protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the "PHd" parallel port IDE protocol
+ from KT Technology. This is a simple (low speed) adapter that is
+ used in some 2.5" portable hard drives.
+
+config PATA_PARPORT_ON20
+ tristate "OnSpec 90c20 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the (obsolete) 90c20 parallel port
+ IDE protocol from OnSpec (often marketed under the ValuStore brand
+ name).
+
+config PATA_PARPORT_ON26
+ tristate "OnSpec 90c26 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the 90c26 parallel port IDE protocol
+ from OnSpec Electronics (often marketed under the ValuStore brand
+ name).
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
new file mode 100644
index 000000000000..0932c8d55b91
--- /dev/null
+++ b/drivers/ata/pata_parport/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_PATA_PARPORT) += pata_parport.o
+
+obj-$(CONFIG_PATA_PARPORT_ATEN) += aten.o
+obj-$(CONFIG_PATA_PARPORT_BPCK) += bpck.o
+obj-$(CONFIG_PATA_PARPORT_COMM) += comm.o
+obj-$(CONFIG_PATA_PARPORT_DSTR) += dstr.o
+obj-$(CONFIG_PATA_PARPORT_KBIC) += kbic.o
+obj-$(CONFIG_PATA_PARPORT_EPAT) += epat.o
+obj-$(CONFIG_PATA_PARPORT_EPIA) += epia.o
+obj-$(CONFIG_PATA_PARPORT_FRPW) += frpw.o
+obj-$(CONFIG_PATA_PARPORT_FRIQ) += friq.o
+obj-$(CONFIG_PATA_PARPORT_FIT2) += fit2.o
+obj-$(CONFIG_PATA_PARPORT_FIT3) += fit3.o
+obj-$(CONFIG_PATA_PARPORT_ON20) += on20.o
+obj-$(CONFIG_PATA_PARPORT_ON26) += on26.o
+obj-$(CONFIG_PATA_PARPORT_KTTI) += ktti.o
+obj-$(CONFIG_PATA_PARPORT_BPCK6) += bpck6.o
diff --git a/drivers/ata/pata_parport/aten.c b/drivers/ata/pata_parport/aten.c
new file mode 100644
index 000000000000..620ce6c8da5c
--- /dev/null
+++ b/drivers/ata/pata_parport/aten.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-8 Grant R. Guenther <grant@torque.net>
+ *
+ * aten.c is a low-level protocol driver for the ATEN EH-100
+ * parallel port adapter. The EH-100 supports 4-bit and 8-bit
+ * modes only. There is also an EH-132 which supports EPP mode
+ * transfers. The EH-132 is not yet supported.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x20 };
+
+static void aten_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont] + 0x80;
+
+ w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
+}
+
+static int aten_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont] + 0x40;
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(r); w2(0xe); w2(6);
+ w2(7); w2(6); w2(0);
+ a = r1(); w0(0x10); b = r1(); w2(0xc);
+ return j44(a,b);
+
+ case 1:
+ r |= 0x10;
+ w0(r); w2(0xe); w2(6); w0(0xff);
+ w2(0x27); w2(0x26); w2(0x20);
+ a = r0();
+ w2(0x26); w2(0xc);
+ return a;
+ }
+ return -1;
+}
+
+static void aten_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b, c, d;
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(0x48); w2(0xe); w2(6);
+ for (k = 0; k < count / 2; k++) {
+ w2(7); w2(6); w2(2);
+ a = r1(); w0(0x58); b = r1();
+ w2(0); d = r1(); w0(0x48); c = r1();
+ buf[2 * k] = j44(c, d);
+ buf[2 * k + 1] = j44(a, b);
+ }
+ w2(0xc);
+ break;
+
+ case 1:
+ w0(0x58); w2(0xe); w2(6);
+ for (k = 0; k < count / 2; k++) {
+ w2(0x27); w2(0x26); w2(0x22);
+ a = r0(); w2(0x20); b = r0();
+ buf[2 * k] = b;
+ buf[2 * k + 1] = a;
+ }
+ w2(0x26); w2(0xc);
+ break;
+ }
+}
+
+static void aten_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ w0(0x88); w2(0xe); w2(6);
+ for (k = 0; k < count / 2; k++) {
+ w0(buf[2 * k + 1]); w2(0xe); w2(6);
+ w0(buf[2 * k]); w2(7); w2(6);
+ }
+ w2(0xc);
+}
+
+static void aten_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xc);
+}
+
+static void aten_disconnect(struct pi_adapter *pi)
+{
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void aten_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[2] = { "4-bit", "8-bit" };
+
+ dev_info(&pi->dev,
+ "ATEN EH-100 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol aten = {
+ .owner = THIS_MODULE,
+ .name = "aten",
+ .max_mode = 2,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = aten_write_regr,
+ .read_regr = aten_read_regr,
+ .write_block = aten_write_block,
+ .read_block = aten_read_block,
+ .connect = aten_connect,
+ .disconnect = aten_disconnect,
+ .log_adapter = aten_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("ATEN EH-100 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(aten);
diff --git a/drivers/ata/pata_parport/bpck.c b/drivers/ata/pata_parport/bpck.c
new file mode 100644
index 000000000000..bba1eda65f36
--- /dev/null
+++ b/drivers/ata/pata_parport/bpck.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * bpck.c is a low-level protocol driver for the MicroSolutions
+ * "backpack" parallel port IDE adapter.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#undef r2
+#undef w2
+#undef PC
+
+#define PC pi->private
+#define r2() (PC=(in_p(2) & 0xff))
+#define w2(byte) {out_p(2,byte); PC = byte;}
+#define t2(pat) {PC ^= pat; out_p(2,PC);}
+#define e2() {PC &= 0xfe; out_p(2,PC);}
+#define o2() {PC |= 1; out_p(2,PC);}
+
+#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ * cont = 2 - use internal bpck register addressing
+ */
+static int cont_map[3] = { 0x40, 0x48, 0 };
+
+static int bpck_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int r, l, h;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ w0(r & 0xf); w0(r); t2(2); t2(4);
+ l = r1();
+ t2(4);
+ h = r1();
+ return j44(l, h);
+ case 1:
+ w0(r & 0xf); w0(r); t2(2);
+ e2(); t2(0x20);
+ t2(4); h = r0();
+ t2(1); t2(0x20);
+ return h;
+ case 2:
+ case 3:
+ case 4:
+ w0(r); w2(9); w2(0); w2(0x20);
+ h = r4();
+ w2(0);
+ return h;
+
+ }
+ return -1;
+}
+
+static void bpck_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1: w0(r);
+ t2(2);
+ w0(val);
+ o2(); t2(4); t2(1);
+ break;
+ case 2:
+ case 3:
+ case 4: w0(r); w2(9); w2(0);
+ w0(val); w2(1); w2(3); w2(0);
+ break;
+
+ }
+}
+
+/* These macros access the bpck registers in native addressing */
+
+#define WR(r,v) bpck_write_regr(pi,2,r,v)
+#define RR(r) (bpck_read_regr(pi,2,r))
+
+static void bpck_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int i;
+
+ switch (pi->mode) {
+
+ case 0:
+ WR(4, 0x40);
+ w0(0x40); t2(2); t2(1);
+ for (i = 0; i < count; i++) {
+ w0(buf[i]);
+ t2(4);
+ }
+ WR(4, 0);
+ break;
+
+ case 1:
+ WR(4, 0x50);
+ w0(0x40); t2(2); t2(1);
+ for (i = 0; i < count; i++) {
+ w0(buf[i]);
+ t2(4);
+ }
+ WR(4, 0x10);
+ break;
+
+ case 2:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i = 0; i < count; i++)
+ w4(buf[i]);
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 3:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i = 0; i < count / 2; i++)
+ w4w(((u16 *)buf)[i]);
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 4:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i = 0; i < count / 4; i++)
+ w4l(((u32 *)buf)[i]);
+ w2(0);
+ WR(4, 8);
+ break;
+ }
+}
+
+static void bpck_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int i, l, h;
+
+ switch (pi->mode) {
+
+ case 0:
+ WR(4, 0x40);
+ w0(0x40); t2(2);
+ for (i = 0; i < count; i++) {
+ t2(4); l = r1();
+ t2(4); h = r1();
+ buf[i] = j44(l, h);
+ }
+ WR(4, 0);
+ break;
+
+ case 1:
+ WR(4, 0x50);
+ w0(0x40); t2(2); t2(0x20);
+ for (i = 0; i < count; i++) {
+ t2(4);
+ buf[i] = r0();
+ }
+ t2(1); t2(0x20);
+ WR(4, 0x10);
+ break;
+
+ case 2:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i = 0; i < count; i++)
+ buf[i] = r4();
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 3:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i = 0; i < count / 2; i++)
+ ((u16 *)buf)[i] = r4w();
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 4:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i = 0; i < count / 4; i++)
+ ((u32 *)buf)[i] = r4l();
+ w2(0);
+ WR(4, 8);
+ break;
+
+ }
+}
+
+static int bpck_probe_unit(struct pi_adapter *pi)
+{
+ int o1, o0, f7, id;
+ int t, s;
+
+ id = pi->unit;
+ s = 0;
+ w2(4); w2(0xe); r2(); t2(2);
+ o1 = r1()&0xf8;
+ o0 = r0();
+ w0(255-id); w2(4); w0(id);
+ t2(8); t2(8); t2(8);
+ t2(2); t = r1()&0xf8;
+ f7 = ((id % 8) == 7);
+ if ((f7) || (t != o1)) {
+ t2(2);
+ s = r1() & 0xf8;
+ }
+ if ((t == o1) && ((!f7) || (s == o1))) {
+ w2(0x4c); w0(o0);
+ return 0;
+ }
+ t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
+ return 1;
+}
+
+static void bpck_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ w0(0xff-pi->unit); w2(4); w0(pi->unit);
+ t2(8); t2(8); t2(8);
+ t2(2); t2(2);
+
+ switch (pi->mode) {
+ case 0:
+ t2(8); WR(4, 0);
+ break;
+ case 1:
+ t2(8); WR(4, 0x10);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w2(0); WR(4, 8);
+ break;
+ }
+
+ WR(5,8);
+
+ /*
+ * Possibly wrong, purpose unknown (fiddle with ESS logic ???)
+ * if (pi->devtype == PI_PCD) {
+ */
+ WR(0x46, 0x10);
+ WR(0x4c, 0x38);
+ WR(0x4d, 0x88);
+ WR(0x46, 0xa0);
+ WR(0x41, 0);
+ WR(0x4e, 8);
+ /* } */
+}
+
+static void bpck_disconnect(struct pi_adapter *pi)
+{
+ w0(0);
+ if (pi->mode >= 2) {
+ w2(9); w2(0);
+ } else {
+ t2(2);
+ }
+ w2(0x4c); w0(pi->saved_r0);
+}
+
+static void bpck_force_spp(struct pi_adapter *pi)
+{
+ /* This fakes the EPP protocol to turn off EPP ... */
+ pi->saved_r0 = r0();
+ w0(0xff-pi->unit); w2(4); w0(pi->unit);
+ t2(8); t2(8); t2(8);
+ t2(2); t2(2);
+
+ w2(0);
+ w0(4); w2(9); w2(0);
+ w0(0); w2(1); w2(3); w2(0);
+ w0(0); w2(9); w2(0);
+ w2(0x4c); w0(pi->saved_r0);
+}
+
+#define TEST_LEN 16
+
+static int bpck_test_proto(struct pi_adapter *pi)
+{
+ int i, e, l, h, om;
+ char buf[TEST_LEN];
+
+ bpck_force_spp(pi);
+
+ switch (pi->mode) {
+
+ case 0:
+ bpck_connect(pi);
+ WR(0x13, 0x7f);
+ w0(0x13); t2(2);
+ for (i = 0; i < TEST_LEN; i++) {
+ t2(4); l = r1();
+ t2(4); h = r1();
+ buf[i] = j44(l, h);
+ }
+ bpck_disconnect(pi);
+ break;
+
+ case 1:
+ bpck_connect(pi);
+ WR(0x13, 0x7f);
+ w0(0x13); t2(2); t2(0x20);
+ for (i = 0; i < TEST_LEN; i++) {
+ t2(4);
+ buf[i] = r0();
+ }
+ t2(1); t2(0x20);
+ bpck_disconnect(pi);
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ om = pi->mode;
+ pi->mode = 0;
+ bpck_connect(pi);
+ WR(7, 3);
+ WR(4, 8);
+ bpck_disconnect(pi);
+
+ pi->mode = om;
+ bpck_connect(pi);
+ w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
+
+ switch (pi->mode) {
+ case 2:
+ for (i = 0; i < TEST_LEN; i++)
+ buf[i] = r4();
+ break;
+ case 3:
+ for (i = 0; i < TEST_LEN / 2; i++)
+ ((u16 *)buf)[i] = r4w();
+ break;
+ case 4:
+ for (i = 0; i < TEST_LEN / 4; i++)
+ ((u32 *)buf)[i] = r4l();
+ break;
+ }
+
+ w2(0);
+ WR(7, 0);
+ bpck_disconnect(pi);
+ break;
+
+ }
+
+ dev_dbg(&pi->dev, "bpck: 0x%x unit %d mode %d: ",
+ pi->port, pi->unit, pi->mode);
+ print_hex_dump_debug("bpck: ", DUMP_PREFIX_NONE, TEST_LEN, 1, buf,
+ TEST_LEN, false);
+
+ e = 0;
+ for (i = 0; i < TEST_LEN; i++) {
+ if (buf[i] != i + 1)
+ e++;
+ }
+
+ return e;
+}
+
+static void bpck_read_eeprom(struct pi_adapter *pi, char *buf)
+{
+ int i, j, k, p, v, f, om, od;
+
+ bpck_force_spp(pi);
+
+ om = pi->mode; od = pi->delay;
+ pi->mode = 0; pi->delay = 6;
+
+ bpck_connect(pi);
+
+ WR(4, 0);
+ for (i = 0; i < 64; i++) {
+ WR(6, 8);
+ WR(6, 0xc);
+ p = 0x100;
+ for (k = 0; k < 9; k++) {
+ f = (((i + 0x180) & p) != 0) * 2;
+ WR(6, f + 0xc);
+ WR(6, f + 0xd);
+ WR(6, f + 0xc);
+ p = (p >> 1);
+ }
+ for (j = 0; j < 2; j++) {
+ v = 0;
+ for (k = 0; k < 8; k++) {
+ WR(6, 0xc);
+ WR(6, 0xd);
+ WR(6, 0xc);
+ f = RR(0);
+ v = 2 * v + (f == 0x84);
+ }
+ buf[2 * i + 1 - j] = v;
+ }
+ }
+ WR(6, 8);
+ WR(6, 0);
+ WR(5, 8);
+
+ bpck_disconnect(pi);
+
+ if (om >= 2) {
+ bpck_connect(pi);
+ WR(7, 3);
+ WR(4, 8);
+ bpck_disconnect(pi);
+ }
+
+ pi->mode = om; pi->delay = od;
+}
+
+static int bpck_test_port(struct pi_adapter *pi)
+{
+ int i, r, m;
+
+ /* Check for 8-bit port */
+ w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
+ m = -1;
+ if (r == i)
+ m = 2;
+ if (r == (255-i))
+ m = 0;
+
+ w2(0xc);
+ i = r0();
+ w0(255-i);
+ r = r0();
+ w0(i);
+ if (r != (255-i))
+ m = -1;
+
+ if (m == 0) {
+ w2(6);
+ w2(0xc);
+ r = r0();
+ w0(0xaa);
+ w0(r);
+ w0(0xaa);
+ }
+ if (m == 2) {
+ w2(0x26);
+ w2(0xc);
+ }
+
+ if (m == -1)
+ return 0;
+
+ return 5;
+}
+
+static void bpck_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_str[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+ char scratch[128];
+
+ bpck_read_eeprom(pi,scratch);
+ print_hex_dump_bytes("bpck EEPROM: ", DUMP_PREFIX_NONE, scratch, 128);
+ dev_info(&pi->dev,
+ "backpack %8.8s unit %d at 0x%x, mode %d (%s), delay %d\n",
+ &scratch[110], pi->unit, pi->port, pi->mode,
+ mode_str[pi->mode], pi->delay);
+}
+
+static struct pi_protocol bpck = {
+ .owner = THIS_MODULE,
+ .name = "bpck",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 4,
+ .max_units = 255,
+ .write_regr = bpck_write_regr,
+ .read_regr = bpck_read_regr,
+ .write_block = bpck_write_block,
+ .read_block = bpck_read_block,
+ .connect = bpck_connect,
+ .disconnect = bpck_disconnect,
+ .test_port = bpck_test_port,
+ .probe_unit = bpck_probe_unit,
+ .test_proto = bpck_test_proto,
+ .log_adapter = bpck_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("MicroSolutions BACKPACK parallel port IDE adapter protocol driver");
+module_pata_parport_driver(bpck);
diff --git a/drivers/ata/pata_parport/bpck6.c b/drivers/ata/pata_parport/bpck6.c
new file mode 100644
index 000000000000..62c2b53325e1
--- /dev/null
+++ b/drivers/ata/pata_parport/bpck6.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 2001 Micro Solutions Inc.
+ *
+ * backpack.c is a low-level protocol driver for the Micro Solutions
+ * "BACKPACK" parallel port IDE adapter (works on Series 6 drives).
+ *
+ * Written by: Ken Hahn (linux-dev@micro-solutions.com)
+ * Clive Turvey (linux-dev@micro-solutions.com)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/parport.h>
+#include "pata_parport.h"
+
+/* 60772 Commands */
+#define ACCESS_REG 0x00
+#define ACCESS_PORT 0x40
+
+#define ACCESS_READ 0x00
+#define ACCESS_WRITE 0x20
+
+/* 60772 Command Prefix */
+#define CMD_PREFIX_SET 0xe0 // Special command that modifies next command's operation
+#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits
+ #define PREFIX_IO16 0x01 // perform 16-bit wide I/O
+ #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write
+ #define PREFIX_BLK 0x08 // enable block transfer mode
+
+/* 60772 Registers */
+#define REG_STATUS 0x00 // status register
+ #define STATUS_IRQA 0x01 // Peripheral IRQA line
+ #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit
+#define REG_VERSION 0x01 // PPC version register (read)
+#define REG_HWCFG 0x02 // Hardware Config register
+#define REG_RAMSIZE 0x03 // Size of RAM Buffer
+ #define RAMSIZE_128K 0x02
+#define REG_EEPROM 0x06 // EEPROM control register
+ #define EEPROM_SK 0x01 // eeprom SK bit
+ #define EEPROM_DI 0x02 // eeprom DI bit
+ #define EEPROM_CS 0x04 // eeprom CS bit
+ #define EEPROM_EN 0x08 // eeprom output enable
+#define REG_BLKSIZE 0x08 // Block transfer len (24 bit)
+
+/* flags */
+#define fifo_wait 0x10
+
+/* DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES */
+#define PPCMODE_UNI_SW 0
+#define PPCMODE_UNI_FW 1
+#define PPCMODE_BI_SW 2
+#define PPCMODE_BI_FW 3
+#define PPCMODE_EPP_BYTE 4
+#define PPCMODE_EPP_WORD 5
+#define PPCMODE_EPP_DWORD 6
+
+static int mode_map[] = { PPCMODE_UNI_FW, PPCMODE_BI_FW, PPCMODE_EPP_BYTE,
+ PPCMODE_EPP_WORD, PPCMODE_EPP_DWORD };
+
+static void bpck6_send_cmd(struct pi_adapter *pi, u8 cmd)
+{
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_write_data(pi->pardev->port, cmd);
+ parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_AUTOFD);
+ break;
+ case PPCMODE_EPP_BYTE:
+ case PPCMODE_EPP_WORD:
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_write_addr(pi->pardev->port, &cmd, 1, 0);
+ break;
+ }
+}
+
+static u8 bpck6_rd_data_byte(struct pi_adapter *pi)
+{
+ u8 data = 0;
+
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_INIT);
+ data = parport_read_status(pi->pardev->port);
+ data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ data |= parport_read_status(pi->pardev->port) & 0xB8;
+ break;
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_data_reverse(pi->pardev->port);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);
+ data = parport_read_data(pi->pardev->port);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0);
+ parport_data_forward(pi->pardev->port);
+ break;
+ case PPCMODE_EPP_BYTE:
+ case PPCMODE_EPP_WORD:
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, &data, 1, 0);
+ break;
+ }
+
+ return data;
+}
+
+static void bpck6_wr_data_byte(struct pi_adapter *pi, u8 data)
+{
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_write_data(pi->pardev->port, data);
+ parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT);
+ break;
+ case PPCMODE_EPP_BYTE:
+ case PPCMODE_EPP_WORD:
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, &data, 1, 0);
+ break;
+ }
+}
+
+static int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg)
+{
+ u8 port = cont ? reg | 8 : reg;
+
+ bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_READ);
+ return bpck6_rd_data_byte(pi);
+}
+
+static void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val)
+{
+ u8 port = cont ? reg | 8 : reg;
+
+ bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, val);
+}
+
+static void bpck6_wait_for_fifo(struct pi_adapter *pi)
+{
+ int i;
+
+ if (pi->private & fifo_wait) {
+ for (i = 0; i < 20; i++)
+ parport_read_status(pi->pardev->port);
+ }
+}
+
+static void bpck6_write_block(struct pi_adapter *pi, char *buf, int len)
+{
+ u8 this, last;
+
+ bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, (u8)len);
+ bpck6_wr_data_byte(pi, (u8)(len >> 8));
+ bpck6_wr_data_byte(pi, 0);
+
+ bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
+ bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_WRITE);
+
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_BI_SW:
+ while (len--) {
+ parport_write_data(pi->pardev->port, *buf++);
+ parport_frob_control(pi->pardev->port, 0,
+ PARPORT_CONTROL_INIT);
+ }
+ break;
+ case PPCMODE_UNI_FW:
+ case PPCMODE_BI_FW:
+ bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_FASTWR);
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+
+ last = *buf;
+
+ parport_write_data(pi->pardev->port, last);
+
+ while (len) {
+ this = *buf++;
+ len--;
+
+ if (this == last) {
+ parport_frob_control(pi->pardev->port, 0,
+ PARPORT_CONTROL_INIT);
+ } else {
+ parport_write_data(pi->pardev->port, this);
+ last = this;
+ }
+ }
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ 0);
+ bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_FASTWR);
+ break;
+ case PPCMODE_EPP_BYTE:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
+ len, PARPORT_EPP_FAST_8);
+ bpck6_wait_for_fifo(pi);
+ break;
+ case PPCMODE_EPP_WORD:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
+ len, PARPORT_EPP_FAST_16);
+ bpck6_wait_for_fifo(pi);
+ break;
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
+ len, PARPORT_EPP_FAST_32);
+ bpck6_wait_for_fifo(pi);
+ break;
+ }
+
+ bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
+}
+
+static void bpck6_read_block(struct pi_adapter *pi, char *buf, int len)
+{
+ bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, (u8)len);
+ bpck6_wr_data_byte(pi, (u8)(len >> 8));
+ bpck6_wr_data_byte(pi, 0);
+
+ bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
+ bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_READ);
+
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ while (len) {
+ u8 d;
+
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_INIT); /* DATA STROBE */
+ d = parport_read_status(pi->pardev->port);
+ d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ d |= parport_read_status(pi->pardev->port) & 0xB8;
+ *buf++ = d;
+ len--;
+ }
+ break;
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_data_reverse(pi->pardev->port);
+ while (len) {
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);
+ *buf++ = parport_read_data(pi->pardev->port);
+ len--;
+ }
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ 0);
+ parport_data_forward(pi->pardev->port);
+ break;
+ case PPCMODE_EPP_BYTE:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
+ PARPORT_EPP_FAST_8);
+ break;
+ case PPCMODE_EPP_WORD:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
+ PARPORT_EPP_FAST_16);
+ break;
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
+ PARPORT_EPP_FAST_32);
+ break;
+ }
+
+ bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
+}
+
+static int bpck6_open(struct pi_adapter *pi)
+{
+ u8 i, j, k;
+
+ pi->saved_r0 = parport_read_data(pi->pardev->port);
+ pi->saved_r2 = parport_read_control(pi->pardev->port) & 0x5F;
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ if (pi->saved_r0 == 'b')
+ parport_write_data(pi->pardev->port, 'x');
+ parport_write_data(pi->pardev->port, 'b');
+ parport_write_data(pi->pardev->port, 'p');
+ parport_write_data(pi->pardev->port, pi->unit);
+ parport_write_data(pi->pardev->port, ~pi->unit);
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);
+ parport_write_control(pi->pardev->port, PARPORT_CONTROL_INIT);
+
+ i = mode_map[pi->mode] & 0x0C;
+ if (i == 0)
+ i = (mode_map[pi->mode] & 2) | 1;
+ parport_write_data(pi->pardev->port, i);
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
+ k = parport_read_status(pi->pardev->port) & 0xB8;
+ if (j != k)
+ goto fail;
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0);
+ k = (parport_read_status(pi->pardev->port) & 0xB8) ^ 0xB8;
+ if (j != k)
+ goto fail;
+
+ if (i & 4) {
+ /* EPP */
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, 0);
+ } else {
+ /* PPC/ECP */
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);
+ }
+
+ pi->private = 0;
+
+ bpck6_send_cmd(pi, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE);
+ bpck6_wr_data_byte(pi, RAMSIZE_128K);
+
+ bpck6_send_cmd(pi, ACCESS_REG | ACCESS_READ | REG_VERSION);
+ if ((bpck6_rd_data_byte(pi) & 0x3F) == 0x0C)
+ pi->private |= fifo_wait;
+
+ return 1;
+
+fail:
+ parport_write_control(pi->pardev->port, pi->saved_r2);
+ parport_write_data(pi->pardev->port, pi->saved_r0);
+
+ return 0;
+}
+
+static void bpck6_deselect(struct pi_adapter *pi)
+{
+ if (mode_map[pi->mode] & 4) {
+ /* EPP */
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+ } else {
+ /* PPC/ECP */
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ }
+
+ parport_write_data(pi->pardev->port, pi->saved_r0);
+ parport_write_control(pi->pardev->port,
+ pi->saved_r2 | PARPORT_CONTROL_SELECT);
+ parport_write_control(pi->pardev->port, pi->saved_r2);
+}
+
+static void bpck6_wr_extout(struct pi_adapter *pi, u8 regdata)
+{
+ bpck6_send_cmd(pi, REG_VERSION | ACCESS_REG | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, (u8)((regdata & 0x03) << 6));
+}
+
+static void bpck6_connect(struct pi_adapter *pi)
+{
+ dev_dbg(&pi->dev, "connect\n");
+
+ bpck6_open(pi);
+ bpck6_wr_extout(pi, 0x3);
+}
+
+static void bpck6_disconnect(struct pi_adapter *pi)
+{
+ dev_dbg(&pi->dev, "disconnect\n");
+ bpck6_wr_extout(pi, 0x0);
+ bpck6_deselect(pi);
+}
+
+/* check for 8-bit port */
+static int bpck6_test_port(struct pi_adapter *pi)
+{
+ dev_dbg(&pi->dev, "PARPORT indicates modes=%x for lp=0x%lx\n",
+ pi->pardev->port->modes, pi->pardev->port->base);
+
+ /* look at the parport device to see what modes we can use */
+ if (pi->pardev->port->modes & PARPORT_MODE_EPP)
+ return 5; /* Can do EPP */
+ if (pi->pardev->port->modes & PARPORT_MODE_TRISTATE)
+ return 2;
+ return 1; /* Just flat SPP */
+}
+
+static int bpck6_probe_unit(struct pi_adapter *pi)
+{
+ int out, saved_mode;
+
+ dev_dbg(&pi->dev, "PROBE UNIT %x on port:%x\n", pi->unit, pi->port);
+
+ saved_mode = pi->mode;
+ /*LOWER DOWN TO UNIDIRECTIONAL*/
+ pi->mode = 0;
+
+ out = bpck6_open(pi);
+
+ dev_dbg(&pi->dev, "ppc_open returned %2x\n", out);
+
+ if (out) {
+ bpck6_deselect(pi);
+ dev_dbg(&pi->dev, "leaving probe\n");
+ pi->mode = saved_mode;
+ return 1;
+ }
+
+ dev_dbg(&pi->dev, "Failed open\n");
+ pi->mode = saved_mode;
+
+ return 0;
+}
+
+static void bpck6_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "Micro Solutions BACKPACK Drive unit %d at 0x%x, mode:%d (%s), delay %d\n",
+ pi->unit, pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol bpck6 = {
+ .owner = THIS_MODULE,
+ .name = "bpck6",
+ .max_mode = 5,
+ .epp_first = 2, /* 2-5 use epp (need 8 ports) */
+ .max_units = 255,
+ .write_regr = bpck6_write_regr,
+ .read_regr = bpck6_read_regr,
+ .write_block = bpck6_write_block,
+ .read_block = bpck6_read_block,
+ .connect = bpck6_connect,
+ .disconnect = bpck6_disconnect,
+ .test_port = bpck6_test_port,
+ .probe_unit = bpck6_probe_unit,
+ .log_adapter = bpck6_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Micro Solutions Inc.");
+MODULE_DESCRIPTION("Micro Solutions BACKPACK parallel port IDE adapter "
+ "(version 6 drives) protocol driver");
+module_pata_parport_driver(bpck6);
diff --git a/drivers/ata/pata_parport/comm.c b/drivers/ata/pata_parport/comm.c
new file mode 100644
index 000000000000..94b8d352102e
--- /dev/null
+++ b/drivers/ata/pata_parport/comm.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * comm.c is a low-level protocol driver for some older models of the DataStor
+ * "Commuter" parallel to IDE adapter. Some of the parallel port devices
+ * marketed by Arista currently use this adapter.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads, 8-bit writes
+ * 1 8-bit reads and writes
+ * 2 8-bit EPP mode
+ */
+
+#define j44(a, b) (((a >> 3) & 0x0f) | ((b << 1) & 0xf0))
+
+#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(7);w2(5);w2(4);
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int comm_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int l, h, r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ w0(r); P1; w0(0);
+ w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
+ return j44(l, h);
+
+ case 1:
+ w0(r+0x20); P1;
+ w0(0); w2(0x26); h = r0(); w2(4);
+ return h;
+
+ case 2:
+ case 3:
+ case 4:
+ w3(r+0x20); (void)r1();
+ w2(0x24); h = r4(); w2(4);
+ return h;
+ }
+
+ return -1;
+}
+
+static void comm_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(r); P1; w0(val); P2;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w3(r); (void)r1(); w4(val);
+ break;
+ }
+}
+
+static void comm_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4); w0(0xff); w2(6);
+ w2(4); w0(0xaa); w2(6);
+ w2(4); w0(0x00); w2(6);
+ w2(4); w0(0x87); w2(6);
+ w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
+}
+
+static void comm_disconnect(struct pi_adapter *pi)
+{
+ w2(0); w2(0); w2(0); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void comm_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int i, l, h;
+
+ switch (pi->mode) {
+ case 0:
+ w0(0x48); P1;
+ for (i = 0; i < count; i++) {
+ w0(0); w2(6); l = r1();
+ w0(0x80); h = r1(); w2(4);
+ buf[i] = j44(l, h);
+ }
+ break;
+ case 1:
+ w0(0x68); P1; w0(0);
+ for (i = 0; i < count; i++) {
+ w2(0x26);
+ buf[i] = r0();
+ w2(0x24);
+ }
+ w2(4);
+ break;
+ case 2:
+ w3(0x68); (void)r1(); w2(0x24);
+ for (i = 0; i < count; i++)
+ buf[i] = r4();
+ w2(4);
+ break;
+ case 3:
+ w3(0x68); (void)r1(); w2(0x24);
+ for (i = 0; i < count / 2; i++)
+ ((u16 *)buf)[i] = r4w();
+ w2(4);
+ break;
+ case 4:
+ w3(0x68); (void)r1(); w2(0x24);
+ for (i = 0; i < count / 4; i++)
+ ((u32 *)buf)[i] = r4l();
+ w2(4);
+ break;
+ }
+}
+
+/* NB: Watch out for the byte swapped writes ! */
+static void comm_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(0x68); P1;
+ for (k = 0; k < count; k++) {
+ w2(5);
+ w0(buf[k ^ 1]);
+ w2(7);
+ }
+ w2(5); w2(4);
+ break;
+ case 2:
+ w3(0x48); (void)r1();
+ for (k = 0; k < count; k++)
+ w4(buf[k ^ 1]);
+ break;
+ case 3:
+ w3(0x48); (void)r1();
+ for (k = 0; k < count / 2; k++)
+ w4w(swab16(((u16 *)buf)[k]));
+ break;
+ case 4:
+ w3(0x48); (void)r1();
+ for (k = 0; k < count / 4; k++)
+ w4l(swab16(((u16 *)buf)[2 * k]) |
+ swab16(((u16 *)buf)[2 * k + 1]) << 16);
+ break;
+ }
+}
+
+static void comm_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "DataStor Commuter at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol comm = {
+ .owner = THIS_MODULE,
+ .name = "comm",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = comm_write_regr,
+ .read_regr = comm_read_regr,
+ .write_block = comm_write_block,
+ .read_block = comm_read_block,
+ .connect = comm_connect,
+ .disconnect = comm_disconnect,
+ .log_adapter = comm_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("DataStor Commuter parallel port IDE adapter protocol driver");
+module_pata_parport_driver(comm);
diff --git a/drivers/ata/pata_parport/dstr.c b/drivers/ata/pata_parport/dstr.c
new file mode 100644
index 000000000000..88930bb1f07e
--- /dev/null
+++ b/drivers/ata/pata_parport/dstr.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * dstr.c is a low-level protocol driver for the DataStor EP2000 parallel
+ * to IDE adapter chip.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads, 8-bit writes
+ * 1 8-bit reads and writes
+ * 2 8-bit EPP mode
+ * 3 EPP-16
+ * 4 EPP-32
+ */
+
+#define j44(a, b) (((a >> 3) & 0x07) | ((~a >> 4) & 0x08) | \
+ ((b << 1) & 0x70) | ((~b) & 0x80))
+
+#define P1 w2(5);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(5);w2(4);
+#define P3 w2(6);w2(4);w2(6);w2(4);
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x20, 0x40 };
+
+static int dstr_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ w0(0x81); P1;
+ if (pi->mode)
+ w0(0x11);
+ else
+ w0(1);
+ P2; w0(r); P1;
+
+ switch (pi->mode) {
+ case 0:
+ w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
+ return j44(a, b);
+ case 1:
+ w0(0); w2(0x26); a = r0(); w2(4);
+ return a;
+ case 2:
+ case 3:
+ case 4:
+ w2(0x24); a = r4(); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void dstr_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ w0(0x81); P1;
+ if (pi->mode >= 2)
+ w0(0x11);
+ else
+ w0(1);
+ P2; w0(r); P1;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(val); w2(5); w2(7); w2(5); w2(4);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w4(val);
+ break;
+ }
+}
+
+#define CCP(x) \
+ do { \
+ w0(0xff); w2(0xc); w2(4); \
+ w0(0xaa); w0(0x55); w0(0); w0(0xff); \
+ w0(0x87); w0(0x78); \
+ w0(x); w2(5); w2(4); \
+ } while (0)
+
+static void dstr_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4); CCP(0xe0); w0(0xff);
+}
+
+static void dstr_disconnect(struct pi_adapter *pi)
+{
+ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void dstr_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ w0(0x81); P1;
+ if (pi->mode)
+ w0(0x19);
+ else
+ w0(9);
+ P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+ switch (pi->mode) {
+ case 0:
+ for (k = 0; k < count; k++) {
+ w2(6); a = r1(); w2(4);
+ w2(6); b = r1(); w2(4);
+ buf[k] = j44(a, b);
+ }
+ break;
+ case 1:
+ w0(0);
+ for (k = 0; k < count; k++) {
+ w2(0x26);
+ buf[k] = r0();
+ w2(0x24);
+ }
+ w2(4);
+ break;
+ case 2:
+ w2(0x24);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4);
+ break;
+ case 3:
+ w2(0x24);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4);
+ break;
+ case 4:
+ w2(0x24);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4);
+ break;
+ }
+}
+
+static void dstr_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ w0(0x81); P1;
+ if (pi->mode)
+ w0(0x19);
+ else
+ w0(9);
+ P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ for (k = 0; k < count; k++) {
+ w2(5);
+ w0(buf[k]);
+ w2(7);
+ }
+ w2(5); w2(4);
+ break;
+ case 2:
+ w2(0xc5);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(0xc4);
+ break;
+ case 3:
+ w2(0xc5);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(0xc4);
+ break;
+ case 4:
+ w2(0xc5);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(0xc4);
+ break;
+ }
+}
+
+static void dstr_log_adapter(struct pi_adapter *pi)
+
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "DataStor EP2000 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol dstr = {
+ .owner = THIS_MODULE,
+ .name = "dstr",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = dstr_write_regr,
+ .read_regr = dstr_read_regr,
+ .write_block = dstr_write_block,
+ .read_block = dstr_read_block,
+ .connect = dstr_connect,
+ .disconnect = dstr_disconnect,
+ .log_adapter = dstr_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("DataStor EP2000 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(dstr);
diff --git a/drivers/ata/pata_parport/epat.c b/drivers/ata/pata_parport/epat.c
new file mode 100644
index 000000000000..3cb54fcbf0d0
--- /dev/null
+++ b/drivers/ata/pata_parport/epat.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * This is the low level protocol driver for the EPAT parallel
+ * to IDE adapter from Shuttle Technologies. This adapter is
+ * used in many popular parallel port disk products such as the
+ * SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 4) & 0x0f) + (b & 0xf0))
+#define j53(a, b) (((a >> 3) & 0x1f) + ((b << 4) & 0xe0))
+
+static int epatc8;
+
+module_param(epatc8, int, 0);
+MODULE_PARM_DESC(epatc8,
+ "support for the Shuttle EP1284 chip, "
+ "used in any recent Imation SuperDisk (LS-120) drive.");
+
+/*
+ * cont = 0 IDE register file
+ * cont = 1 IDE control registers
+ * cont = 2 internal EPAT registers
+ */
+static int cont_map[3] = { 0x18, 0x10, 0 };
+
+static void epat_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0x60+r); w2(1); w0(val); w2(4);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ w3(0x40+r); w4(val);
+ break;
+ }
+}
+
+static int epat_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(r); w2(1); w2(3);
+ a = r1(); w2(4); b = r1();
+ return j44(a, b);
+ case 1:
+ w0(0x40+r); w2(1); w2(4);
+ a = r1(); b = r2(); w0(0xff);
+ return j53(a, b);
+ case 2:
+ w0(0x20+r); w2(1); w2(0x25);
+ a = r0(); w2(4);
+ return a;
+ case 3:
+ case 4:
+ case 5:
+ w3(r); w2(0x24); a = r4(); w2(4);
+ return a;
+ }
+
+ return -1; /* never gets here */
+}
+
+static void epat_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, ph, a, b;
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(7); w2(1); w2(3); w0(0xff);
+ ph = 0;
+ for (k = 0; k < count; k++) {
+ if (k == count-1)
+ w0(0xfd);
+ w2(6 + ph); a = r1();
+ if (a & 8) {
+ b = a;
+ } else {
+ w2(4+ph); b = r1();
+ }
+ buf[k] = j44(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 1:
+ w0(0x47); w2(1); w2(5); w0(0xff);
+ ph = 0;
+ for (k = 0; k < count; k++) {
+ if (k == count - 1)
+ w0(0xfd);
+ w2(4 + ph);
+ a = r1(); b = r2();
+ buf[k] = j53(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 2:
+ w0(0x27); w2(1); w2(0x25); w0(0);
+ ph = 0;
+ for (k = 0; k < count - 1; k++) {
+ w2(0x24 + ph);
+ buf[k] = r0();
+ ph = 1 - ph;
+ }
+ w2(0x26); w2(0x27);
+ buf[count - 1] = r0();
+ w2(0x25); w2(4);
+ break;
+
+ case 3:
+ w3(0x80); w2(0x24);
+ for (k = 0; k < count - 1; k++)
+ buf[k] = r4();
+ w2(4); w3(0xa0); w2(0x24);
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 4:
+ w3(0x80); w2(0x24);
+ for (k = 0; k < count / 2 - 1; k++)
+ ((u16 *)buf)[k] = r4w();
+ buf[count - 2] = r4();
+ w2(4); w3(0xa0); w2(0x24);
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 5:
+ w3(0x80); w2(0x24);
+ for (k = 0; k < count / 4 - 1; k++)
+ ((u32 *)buf)[k] = r4l();
+ for (k = count - 4; k < count - 1; k++)
+ buf[k] = r4();
+ w2(4); w3(0xa0); w2(0x24);
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ }
+}
+
+static void epat_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int ph, k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0x67); w2(1); w2(5);
+ ph = 0;
+ for (k = 0; k < count; k++) {
+ w0(buf[k]);
+ w2(4 + ph);
+ ph = 1 - ph;
+ }
+ w2(7); w2(4);
+ break;
+ case 3:
+ w3(0xc0);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(4);
+ break;
+ case 4:
+ w3(0xc0);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+ case 5:
+ w3(0xc0);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+ }
+}
+
+/* these macros access the EPAT registers in native addressing */
+
+#define WR(r, v) epat_write_regr(pi, 2, r, v)
+#define RR(r) epat_read_regr(pi, 2, r)
+
+/* and these access the IDE task file */
+
+#define WRi(r, v) epat_write_regr(pi, 0, r, v)
+#define RRi(r) epat_read_regr(pi, 0, r)
+
+/* FIXME: the CPP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CPP(x) \
+ do { \
+ w2(4); w0(0x22); w0(0xaa); \
+ w0(0x55); w0(0); w0(0xff); \
+ w0(0x87); w0(0x78); w0(x); \
+ w2(4); w2(5); w2(4); w0(0xff); \
+ } while (0)
+
+static void epat_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ /* Initialize the chip */
+ CPP(0);
+
+ if (epatc8) {
+ CPP(0x40); CPP(0xe0);
+ w0(0); w2(1); w2(4);
+ WR(0x8, 0x12);
+ WR(0xc, 0x14);
+ WR(0x12, 0x10);
+ WR(0xe, 0xf);
+ WR(0xf, 4);
+ /* WR(0xe,0xa);WR(0xf,4); */
+ WR(0xe, 0xd);
+ WR(0xf, 0);
+ /* CPP(0x30); */
+ }
+
+ /* Connect to the chip */
+ CPP(0xe0);
+ w0(0); w2(1); w2(4); /* Idle into SPP */
+ if (pi->mode >= 3) {
+ w0(0); w2(1); w2(4); w2(0xc);
+ /* Request EPP */
+ w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+ }
+
+ if (!epatc8) {
+ WR(8, 0x10);
+ WR(0xc, 0x14);
+ WR(0xa, 0x38);
+ WR(0x12, 0x10);
+ }
+}
+
+static void epat_disconnect(struct pi_adapter *pi)
+{
+ CPP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static int epat_test_proto(struct pi_adapter *pi)
+{
+ int k, j, f, cc;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ epat_connect(pi);
+ cc = RR(0xd);
+ epat_disconnect(pi);
+
+ epat_connect(pi);
+ for (j=0;j<2;j++) {
+ WRi(6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ WRi(2, k ^ 0xaa);
+ WRi(3, k ^ 0x55);
+ if (RRi(2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ epat_disconnect(pi);
+
+ f = 0;
+ epat_connect(pi);
+ WR(0x13, 1); WR(0x13, 0); WR(0xa, 0x11);
+ epat_read_block(pi, scratch, 512);
+
+ for (k = 0; k < 256; k++) {
+ if ((scratch[2 * k] & 0xff) != k)
+ f++;
+ if ((scratch[2 * k + 1] & 0xff) != 0xff - k)
+ f++;
+ }
+ epat_disconnect(pi);
+
+ dev_dbg(&pi->dev,
+ "epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
+ pi->port, pi->mode, cc, e[0], e[1], f);
+
+ return (e[0] && e[1]) || f;
+}
+
+static void epat_log_adapter(struct pi_adapter *pi)
+{
+ int ver;
+ char *mode_string[6] =
+ { "4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ epat_connect(pi);
+ WR(0xa, 0x38); /* read the version code */
+ ver = RR(0xb);
+ epat_disconnect(pi);
+
+ dev_info(&pi->dev,
+ "Shuttle EPAT chip %x at 0x%x, mode %d (%s), delay %d\n",
+ ver, pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol epat = {
+ .owner = THIS_MODULE,
+ .name = "epat",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = epat_write_regr,
+ .read_regr = epat_read_regr,
+ .write_block = epat_write_block,
+ .read_block = epat_read_block,
+ .connect = epat_connect,
+ .disconnect = epat_disconnect,
+ .test_proto = epat_test_proto,
+ .log_adapter = epat_log_adapter,
+};
+
+static int __init epat_init(void)
+{
+#ifdef CONFIG_PATA_PARPORT_EPATC8
+ epatc8 = 1;
+#endif
+ return pata_parport_register_driver(&epat);
+}
+
+static void __exit epat_exit(void)
+{
+ pata_parport_unregister_driver(&epat);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Shuttle Technologies EPAT parallel port IDE adapter "
+ "protocol driver");
+module_init(epat_init)
+module_exit(epat_exit)
diff --git a/drivers/ata/pata_parport/epia.c b/drivers/ata/pata_parport/epia.c
new file mode 100644
index 000000000000..7aaba474c671
--- /dev/null
+++ b/drivers/ata/pata_parport/epia.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * epia.c is a low-level protocol driver for Shuttle Technologies
+ * EPIA parallel to IDE adapter chip. This device is now obsolete
+ * and has been replaced with the EPAT chip, which is supported
+ * by epat.c, however, some devices based on EPIA are still
+ * available.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads on port 1, 8-bit writes
+ * 1 5/3 reads on ports 1 & 2, 8-bit writes
+ * 2 8-bit reads and writes
+ * 3 8-bit EPP mode
+ * 4 16-bit EPP
+ * 5 32-bit EPP
+ */
+
+#define j44(a, b) (((a >> 4) & 0x0f) + (b & 0xf0))
+#define j53(a, b) (((a >> 3) & 0x1f) + ((b << 4) & 0xe0))
+
+/*
+ * cont = 0 IDE register file
+ * cont = 1 IDE control registers
+ */
+static int cont_map[2] = { 0, 0x80 };
+
+static int epia_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ regr += cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ r = regr ^ 0x39;
+ w0(r); w2(1); w2(3); w0(r);
+ a = r1(); w2(1); b = r1(); w2(4);
+ return j44(a, b);
+ case 1:
+ r = regr ^ 0x31;
+ w0(r); w2(1); w0(r & 0x37);
+ w2(3); w2(5); w0(r | 0xf0);
+ a = r1(); b = r2(); w2(4);
+ return j53(a, b);
+ case 2:
+ r = regr^0x29;
+ w0(r); w2(1); w2(0X21); w2(0x23);
+ a = r0(); w2(4);
+ return a;
+ case 3:
+ case 4:
+ case 5:
+ w3(regr); w2(0x24); a = r4(); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void epia_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r;
+
+ regr += cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ r = regr ^ 0x19;
+ w0(r); w2(1); w0(val); w2(3); w2(4);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ r = regr ^ 0x40;
+ w3(r); w4(val); w2(4);
+ break;
+ }
+}
+
+#define WR(r, v) epia_write_regr(pi, 0, r, v)
+#define RR(r) epia_read_regr(pi, 0, r)
+
+/*
+ * The use of register 0x84 is entirely unclear - it seems to control
+ * some EPP counters ... currently we know about 3 different block
+ * sizes: the standard 512 byte reads and writes, 12 byte writes and
+ * 2048 byte reads (the last two being used in the CDrom drivers.
+ */
+static void epia_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
+ w2(1); w2(4);
+ if (pi->mode >= 3) {
+ w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
+ w2(0x24); w2(0x26); w2(4);
+ }
+ WR(0x86, 8);
+}
+
+static void epia_disconnect(struct pi_adapter *pi)
+{
+ /* WR(0x84,0x10); */
+ w0(pi->saved_r0);
+ w2(1); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void epia_read_block(struct pi_adapter *pi, char *buf, int count)
+
+{
+ int k, ph, a, b;
+
+ switch (pi->mode) {
+ case 0:
+ w0(0x81); w2(1); w2(3); w0(0xc1);
+ ph = 1;
+ for (k = 0; k < count; k++) {
+ w2(2+ph); a = r1();
+ w2(4+ph); b = r1();
+ buf[k] = j44(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+ case 1:
+ w0(0x91); w2(1); w0(0x10); w2(3);
+ w0(0x51); w2(5); w0(0xd1);
+ ph = 1;
+ for (k = 0; k < count; k++) {
+ w2(4 + ph);
+ a = r1(); b = r2();
+ buf[k] = j53(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+ case 2:
+ w0(0x89); w2(1); w2(0x23); w2(0x21);
+ ph = 1;
+ for (k = 0; k < count; k++) {
+ w2(0x24 + ph);
+ buf[k] = r0();
+ ph = 1 - ph;
+ }
+ w2(6); w2(4);
+ break;
+ case 3:
+ if (count > 512)
+ WR(0x84, 3);
+ w3(0); w2(0x24);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4); WR(0x84, 0);
+ break;
+ case 4:
+ if (count > 512)
+ WR(0x84, 3);
+ w3(0); w2(0x24);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4); WR(0x84, 0);
+ break;
+ case 5:
+ if (count > 512)
+ WR(0x84, 3);
+ w3(0); w2(0x24);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4); WR(0x84, 0);
+ break;
+ }
+}
+
+static void epia_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int ph, k, last, d;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0xa1); w2(1); w2(3); w2(1); w2(5);
+ ph = 0; last = 0x8000;
+ for (k = 0; k < count; k++) {
+ d = buf[k];
+ if (d != last) {
+ last = d;
+ w0(d);
+ }
+ w2(4 + ph);
+ ph = 1 - ph;
+ }
+ w2(7); w2(4);
+ break;
+ case 3:
+ if (count < 512)
+ WR(0x84, 1);
+ w3(0x40);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ if (count < 512)
+ WR(0x84, 0);
+ break;
+ case 4:
+ if (count < 512)
+ WR(0x84, 1);
+ w3(0x40);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ if (count < 512)
+ WR(0x84, 0);
+ break;
+ case 5:
+ if (count < 512)
+ WR(0x84, 1);
+ w3(0x40);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ if (count < 512)
+ WR(0x84, 0);
+ break;
+ }
+}
+
+static int epia_test_proto(struct pi_adapter *pi)
+{
+ int j, k, f;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ epia_connect(pi);
+ for (j = 0; j < 2; j++) {
+ WR(6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ WR(2, k ^ 0xaa);
+ WR(3, k ^ 0x55);
+ if (RR(2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ WR(2, 1); WR(3, 1);
+ }
+ epia_disconnect(pi);
+
+ f = 0;
+ epia_connect(pi);
+ WR(0x84, 8);
+ epia_read_block(pi, scratch, 512);
+ for (k = 0; k < 256; k++) {
+ if ((scratch[2 * k] & 0xff) != ((k + 1) & 0xff))
+ f++;
+ if ((scratch[2 * k + 1] & 0xff) != ((-2 - k) & 0xff))
+ f++;
+ }
+ WR(0x84, 0);
+ epia_disconnect(pi);
+
+ dev_dbg(&pi->dev, "epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+ pi->port, pi->mode, e[0], e[1], f);
+
+ return (e[0] && e[1]) || f;
+}
+
+
+static void epia_log_adapter(struct pi_adapter *pi)
+{
+ char *mode[6] = { "4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32"};
+
+ dev_info(&pi->dev,
+ "Shuttle EPIA at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode[pi->mode], pi->delay);
+}
+
+static struct pi_protocol epia = {
+ .owner = THIS_MODULE,
+ .name = "epia",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = epia_write_regr,
+ .read_regr = epia_read_regr,
+ .write_block = epia_write_block,
+ .read_block = epia_read_block,
+ .connect = epia_connect,
+ .disconnect = epia_disconnect,
+ .test_proto = epia_test_proto,
+ .log_adapter = epia_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Shuttle Technologies EPIA parallel port IDE adapter "
+ "protocol driver");
+module_pata_parport_driver(epia);
diff --git a/drivers/ata/pata_parport/fit2.c b/drivers/ata/pata_parport/fit2.c
new file mode 100644
index 000000000000..de79cf91ad5f
--- /dev/null
+++ b/drivers/ata/pata_parport/fit2.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * fit2.c is a low-level protocol driver for the older version
+ * of the Fidelity International Technology parallel port adapter.
+ * This adapter is used in their TransDisk 2000 and older TransDisk
+ * 3000 portable hard-drives. As far as I can tell, this device
+ * supports 4-bit mode _only_.
+ *
+ * Newer models of the FIT products use an enhanced protocol.
+ * The "fit3" protocol module should support current drives.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ *
+ * NB: The FIT adapter does not appear to use the control registers.
+ * So, we map ALT_STATUS to STATUS and NO-OP writes to the device
+ * control register - this means that IDE reset will not work on these
+ * devices.
+ */
+
+static void fit2_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ if (cont == 1)
+ return;
+ w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4);
+}
+
+static int fit2_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ if (cont) {
+ if (regr != 6)
+ return 0xff;
+ r = 7;
+ } else {
+ r = regr + 0x10;
+ }
+
+ w2(0xc); w0(r); w2(4); w2(5);
+ w0(0); a = r1();
+ w0(1); b = r1();
+ w2(4);
+
+ return j44(a, b);
+}
+
+static void fit2_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b, c, d;
+
+ w2(0xc); w0(0x10);
+
+ for (k = 0; k < count / 4; k++) {
+ w2(4); w2(5);
+ w0(0); a = r1(); w0(1); b = r1();
+ w0(3); c = r1(); w0(2); d = r1();
+ buf[4 * k + 0] = j44(a, b);
+ buf[4 * k + 1] = j44(d, c);
+
+ w2(4); w2(5);
+ a = r1(); w0(3); b = r1();
+ w0(1); c = r1(); w0(0); d = r1();
+ buf[4 * k + 2] = j44(d, c);
+ buf[4 * k + 3] = j44(a, b);
+ }
+
+ w2(4);
+}
+
+static void fit2_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ w2(0xc); w0(0);
+ for (k = 0; k < count / 2; k++) {
+ w2(4); w0(buf[2 * k]);
+ w2(5); w0(buf[2 * k + 1]);
+ }
+ w2(4);
+}
+
+static void fit2_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xcc);
+}
+
+static void fit2_disconnect(struct pi_adapter *pi)
+{
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void fit2_log_adapter(struct pi_adapter *pi)
+{
+ dev_info(&pi->dev, "FIT 2000 adapter at 0x%x, delay %d\n",
+ pi->port, pi->delay);
+
+}
+
+static struct pi_protocol fit2 = {
+ .owner = THIS_MODULE,
+ .name = "fit2",
+ .max_mode = 1,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = fit2_write_regr,
+ .read_regr = fit2_read_regr,
+ .write_block = fit2_write_block,
+ .read_block = fit2_read_block,
+ .connect = fit2_connect,
+ .disconnect = fit2_disconnect,
+ .log_adapter = fit2_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Fidelity International Technology parallel port IDE adapter"
+ "(older models) protocol driver");
+module_pata_parport_driver(fit2);
diff --git a/drivers/ata/pata_parport/fit3.c b/drivers/ata/pata_parport/fit3.c
new file mode 100644
index 000000000000..d2b81cf2e16d
--- /dev/null
+++ b/drivers/ata/pata_parport/fit3.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * fit3.c is a low-level protocol driver for newer models
+ * of the Fidelity International Technology parallel port adapter.
+ * This adapter is used in their TransDisk 3000 portable
+ * hard-drives, as well as CD-ROM, PD-CD and other devices.
+ *
+ * The TD-2000 and certain older devices use a different protocol.
+ * Try the fit2 protocol module with them.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 3) & 0x0f) | ((b << 1) & 0xf0))
+
+#define w7(byte) out_p(7, byte)
+#define r7() (in_p(7) & 0xff)
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+
+static void fit3_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ regr += cont << 3;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w2(0xc); w0(regr); w2(0x8); w2(0xc);
+ w0(val); w2(0xd);
+ w0(0); w2(0xc);
+ break;
+ case 2:
+ w2(0xc); w0(regr); w2(0x8); w2(0xc);
+ w4(val); w4(0);
+ w2(0xc);
+ break;
+ }
+}
+
+static int fit3_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b;
+
+ regr += cont << 3;
+
+ switch (pi->mode) {
+ case 0:
+ w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc);
+ w2(0xd); a = r1();
+ w2(0xf); b = r1();
+ w2(0xc);
+ return j44(a, b);
+ case 1:
+ w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
+ w2(0xec); w2(0xee); w2(0xef); a = r0();
+ w2(0xc);
+ return a;
+ case 2:
+ w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
+ w2(0xec);
+ a = r4(); b = r4();
+ w2(0xc);
+ return a;
+ }
+
+ return -1;
+}
+
+static void fit3_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b, c, d;
+
+ switch (pi->mode) {
+ case 0:
+ w2(0xc); w0(0x10); w2(0x8); w2(0xc);
+ for (k = 0; k < count / 2; k++) {
+ w2(0xd); a = r1();
+ w2(0xf); b = r1();
+ w2(0xc); c = r1();
+ w2(0xe); d = r1();
+ buf[2 * k] = j44(a, b);
+ buf[2 * k + 1] = j44(c, d);
+ }
+ w2(0xc);
+ break;
+ case 1:
+ w2(0xc); w0(0x90); w2(0x8); w2(0xc);
+ w2(0xec); w2(0xee);
+ for (k = 0; k < count / 2; k++) {
+ w2(0xef); a = r0();
+ w2(0xee); b = r0();
+ buf[2 * k] = a;
+ buf[2 * k + 1] = b;
+ }
+ w2(0xec);
+ w2(0xc);
+ break;
+ case 2:
+ w2(0xc); w0(0x90); w2(0x8); w2(0xc);
+ w2(0xec);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(0xc);
+ break;
+ }
+}
+
+static void fit3_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w2(0xc); w0(0); w2(0x8); w2(0xc);
+ for (k = 0; k < count / 2; k++) {
+ w0(buf[2 * k]); w2(0xd);
+ w0(buf[2 * k + 1]); w2(0xc);
+ }
+ break;
+ case 2:
+ w2(0xc); w0(0); w2(0x8); w2(0xc);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(0xc);
+ break;
+ }
+}
+
+static void fit3_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xc); w0(0); w2(0xa);
+ if (pi->mode == 2) {
+ w2(0xc); w0(0x9);
+ w2(0x8); w2(0xc);
+ }
+}
+
+static void fit3_disconnect(struct pi_adapter *pi)
+{
+ w2(0xc); w0(0xa); w2(0x8); w2(0xc);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void fit3_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[3] = { "4-bit", "8-bit", "EPP"};
+
+ dev_info(&pi->dev,
+ "FIT 3000 adapter at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol fit3 = {
+ .owner = THIS_MODULE,
+ .name = "fit3",
+ .max_mode = 3,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = fit3_write_regr,
+ .read_regr = fit3_read_regr,
+ .write_block = fit3_write_block,
+ .read_block = fit3_read_block,
+ .connect = fit3_connect,
+ .disconnect = fit3_disconnect,
+ .log_adapter = fit3_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Fidelity International Technology parallel port IDE adapter"
+ "(newer models) protocol driver");
+module_pata_parport_driver(fit3);
diff --git a/drivers/ata/pata_parport/friq.c b/drivers/ata/pata_parport/friq.c
new file mode 100644
index 000000000000..7abe2ff40685
--- /dev/null
+++ b/drivers/ata/pata_parport/friq.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * friq.c is a low-level protocol driver for the Freecom "IQ"
+ * parallel port IDE adapter. Early versions of this adapter
+ * use the 'frpw' protocol.
+ *
+ * Freecom uses this adapter in a battery powered external
+ * CD-ROM drive. It is also used in LS-120 drives by
+ * Maxell and Panasonic, and other devices.
+ *
+ * The battery powered drive requires software support to
+ * control the power to the drive. This module enables the
+ * drive power when the high level driver (pcd) is loaded
+ * and disables it when the module is unloaded. Note, if
+ * the friq module is built in to the kernel, the power
+ * will never be switched off, so other means should be
+ * used to conserve battery power.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define CMD(x) \
+ do { \
+ w2(4); w0(0xff); w0(0xff); w0(0x73); w0(0x73); \
+ w0(0xc9); w0(0xc9); w0(0x26); \
+ w0(0x26); w0(x); w0(x); \
+ } while (0)
+
+#define j44(l, h) (((l >> 4) & 0x0f) | (h & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int friq_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int h, l, r;
+
+ r = regr + cont_map[cont];
+
+ CMD(r);
+ w2(6); l = r1();
+ w2(4); h = r1();
+ w2(4);
+
+ return j44(l, h);
+}
+
+static void friq_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ CMD(r);
+ w0(val);
+ w2(5); w2(7); w2(5); w2(4);
+}
+
+static void friq_read_block_int(struct pi_adapter *pi, char *buf, int count, int regr)
+{
+ int h, l, k, ph;
+
+ switch (pi->mode) {
+ case 0:
+ CMD(regr);
+ for (k = 0; k < count; k++) {
+ w2(6); l = r1();
+ w2(4); h = r1();
+ buf[k] = j44(l, h);
+ }
+ w2(4);
+ break;
+ case 1:
+ ph = 2;
+ CMD(regr + 0xc0);
+ w0(0xff);
+ for (k = 0; k < count; k++) {
+ w2(0xa4 + ph);
+ buf[k] = r0();
+ ph = 2 - ph;
+ }
+ w2(0xac); w2(0xa4); w2(4);
+ break;
+ case 2:
+ CMD(regr + 0x80);
+ for (k = 0; k < count - 2; k++)
+ buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ case 3:
+ CMD(regr + 0x80);
+ for (k = 0; k < count / 2 - 1; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ case 4:
+ CMD(regr + 0x80);
+ for (k = 0; k < count / 4 - 1; k++)
+ ((u32 *)buf)[k] = r4l();
+ buf[count - 4] = r4();
+ buf[count - 3] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ }
+}
+
+static void friq_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ friq_read_block_int(pi, buf, count, 0x08);
+}
+
+static void friq_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ CMD(8); w2(5);
+ for (k = 0; k < count; k++) {
+ w0(buf[k]);
+ w2(7); w2(5);
+ }
+ w2(4);
+ break;
+ case 2:
+ CMD(0xc8); w2(5);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(4);
+ break;
+ case 3:
+ CMD(0xc8); w2(5);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+ case 4:
+ CMD(0xc8); w2(5);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+}
+}
+
+static void friq_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void friq_disconnect(struct pi_adapter *pi)
+{
+ CMD(0x20);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static int friq_test_proto(struct pi_adapter *pi)
+{
+ int j, k, r;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ pi->saved_r0 = r0();
+ w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
+ udelay(500);
+ w0(pi->saved_r0);
+
+ friq_connect(pi);
+ for (j = 0; j < 2; j++) {
+ friq_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ friq_write_regr(pi, 0, 2, k ^ 0xaa);
+ friq_write_regr(pi, 0, 3, k ^ 0x55);
+ if (friq_read_regr(pi, 0, 2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ friq_disconnect(pi);
+
+ friq_connect(pi);
+ friq_read_block_int(pi, scratch, 512, 0x10);
+ r = 0;
+ for (k = 0; k < 128; k++) {
+ if (scratch[k] != k)
+ r++;
+ }
+ friq_disconnect(pi);
+
+ dev_dbg(&pi->dev,
+ "friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+ pi->port, pi->mode, e[0], e[1], r);
+
+ return r || (e[0] && e[1]);
+}
+
+static void friq_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[6] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32"};
+
+ dev_info(&pi->dev,
+ "Freecom IQ ASIC-2 adapter at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+
+ pi->private = 1;
+ friq_connect(pi);
+ CMD(0x9e); /* disable sleep timer */
+ friq_disconnect(pi);
+}
+
+static void friq_release_proto(struct pi_adapter *pi)
+{
+ if (pi->private) { /* turn off the power */
+ friq_connect(pi);
+ CMD(0x1d); CMD(0x1e);
+ friq_disconnect(pi);
+ pi->private = 0;
+ }
+}
+
+static struct pi_protocol friq = {
+ .owner = THIS_MODULE,
+ .name = "friq",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = friq_write_regr,
+ .read_regr = friq_read_regr,
+ .write_block = friq_write_block,
+ .read_block = friq_read_block,
+ .connect = friq_connect,
+ .disconnect = friq_disconnect,
+ .test_proto = friq_test_proto,
+ .log_adapter = friq_log_adapter,
+ .release_proto = friq_release_proto,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Freecom IQ parallel port IDE adapter protocol driver");
+module_pata_parport_driver(friq);
diff --git a/drivers/ata/pata_parport/frpw.c b/drivers/ata/pata_parport/frpw.c
new file mode 100644
index 000000000000..7fa9b9857321
--- /dev/null
+++ b/drivers/ata/pata_parport/frpw.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * frpw.c is a low-level protocol driver for the Freecom "Power" parallel port
+ * IDE adapter.
+ *
+ * Some applications of this adapter may require a "printer" reset prior to
+ * loading the driver. This can be done by loading and unloading the "lp"
+ * driver, or it can be done by this driver if you define FRPW_HARD_RESET.
+ * The latter is not recommended as it may upset devices on other ports.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
+#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int frpw_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int h, l, r;
+
+ r = regr + cont_map[cont];
+
+ w2(4);
+ w0(r); cec4;
+ w2(6); l = r1();
+ w2(4); h = r1();
+ w2(4);
+
+ return j44(l, h);
+}
+
+static void frpw_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ w2(4); w0(r); cec4;
+ w0(val);
+ w2(5); w2(7); w2(5); w2(4);
+}
+
+static void frpw_read_block_int(struct pi_adapter *pi, char *buf, int count,
+ int regr)
+{
+ int h, l, k, ph;
+
+ switch (pi->mode) {
+ case 0:
+ w2(4); w0(regr); cec4;
+ for (k = 0; k < count; k++) {
+ w2(6); l = r1();
+ w2(4); h = r1();
+ buf[k] = j44(l, h);
+ }
+ w2(4);
+ break;
+
+ case 1:
+ ph = 2;
+ w2(4); w0(regr + 0xc0); cec4;
+ w0(0xff);
+ for (k = 0; k < count; k++) {
+ w2(0xa4 + ph);
+ buf[k] = r0();
+ ph = 2 - ph;
+ }
+ w2(0xac); w2(0xa4); w2(4);
+ break;
+
+ case 2:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ w2(4);
+ break;
+
+ case 3:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count - 2; k++)
+ buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 4:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count / 2 - 1; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 5:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count / 4 - 1; k++)
+ ((u32 *)buf)[k] = r4l();
+ buf[count - 4] = r4();
+ buf[count - 3] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ }
+}
+
+static void frpw_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ frpw_read_block_int(pi, buf, count, 0x08);
+}
+
+static void frpw_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w2(4); w0(8); cec4; w2(5);
+ for (k = 0; k < count; k++) {
+ w0(buf[k]);
+ w2(7); w2(5);
+ }
+ w2(4);
+ break;
+
+ case 3:
+ w2(4); w0(0xc8); cec4; w2(5);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(4);
+ break;
+
+ case 4:
+ w2(4); w0(0xc8); cec4; w2(5);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+
+ case 5:
+ w2(4); w0(0xc8); cec4; w2(5);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+ }
+}
+
+static void frpw_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void frpw_disconnect(struct pi_adapter *pi)
+{
+ w2(4); w0(0x20); cec4;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+/*
+ * Stub logic to see if PNP string is available - used to distinguish
+ * between the Xilinx and ASIC implementations of the Freecom adapter.
+ * returns chip_type: 0 = Xilinx, 1 = ASIC
+ */
+static int frpw_test_pnp(struct pi_adapter *pi)
+{
+ int olddelay, a, b;
+
+#ifdef FRPW_HARD_RESET
+ w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */
+ mdelay(1500);
+#endif
+
+ olddelay = pi->delay;
+ pi->delay = 10;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(4); w2(6); w2(7);
+ a = r1() & 0xff; w2(4); b = r1() & 0xff;
+ w2(0xc); w2(0xe); w2(4);
+
+ pi->delay = olddelay;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+
+ return ((~a & 0x40) && (b & 0x40));
+}
+
+/*
+ * We use the pi->private to remember the result of the PNP test.
+ * To make this work, private = port*2 + chip. Yes, I know it's a hack :-(
+ */
+static int frpw_test_proto(struct pi_adapter *pi)
+{
+ int j, k, r;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ if ((pi->private >> 1) != pi->port)
+ pi->private = frpw_test_pnp(pi) + 2*pi->port;
+
+ if (((pi->private & 0x1) == 0) && (pi->mode > 2)) {
+ dev_dbg(&pi->dev,
+ "frpw: Xilinx does not support mode %d\n", pi->mode);
+ return 1;
+ }
+
+ if (((pi->private & 0x1) == 1) && (pi->mode == 2)) {
+ dev_dbg(&pi->dev, "frpw: ASIC does not support mode 2\n");
+ return 1;
+ }
+
+ frpw_connect(pi);
+ for (j = 0; j < 2; j++) {
+ frpw_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ frpw_write_regr(pi, 0, 2, k ^ 0xaa);
+ frpw_write_regr(pi, 0, 3, k ^ 0x55);
+ if (frpw_read_regr(pi, 0, 2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ frpw_disconnect(pi);
+
+ frpw_connect(pi);
+ frpw_read_block_int(pi, scratch, 512, 0x10);
+ r = 0;
+ for (k = 0; k < 128; k++) {
+ if (scratch[k] != k)
+ r++;
+ }
+ frpw_disconnect(pi);
+
+ dev_dbg(&pi->dev,
+ "frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n",
+ pi->port, (pi->private%2), pi->mode, e[0], e[1], r);
+
+ return r || (e[0] && e[1]);
+}
+
+static void frpw_log_adapter(struct pi_adapter *pi)
+
+{
+ char *mode[6] = { "4-bit", "8-bit", "EPP", "EPP-8", "EPP-16", "EPP-32"};
+
+ dev_info(&pi->dev,
+ "Freecom (%s) adapter at 0x%x, mode %d (%s), delay %d\n",
+ ((pi->private & 0x1) == 0) ? "Xilinx" : "ASIC",
+ pi->port, pi->mode, mode[pi->mode], pi->delay);
+}
+
+static struct pi_protocol frpw = {
+ .owner = THIS_MODULE,
+ .name = "frpw",
+ .max_mode = 6,
+ .epp_first = 2,
+ .default_delay = 2,
+ .max_units = 1,
+ .write_regr = frpw_write_regr,
+ .read_regr = frpw_read_regr,
+ .write_block = frpw_write_block,
+ .read_block = frpw_read_block,
+ .connect = frpw_connect,
+ .disconnect = frpw_disconnect,
+ .test_proto = frpw_test_proto,
+ .log_adapter = frpw_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Freecom Power parallel port IDE adapter protocol driver");
+module_pata_parport_driver(frpw);
diff --git a/drivers/ata/pata_parport/kbic.c b/drivers/ata/pata_parport/kbic.c
new file mode 100644
index 000000000000..fca322627b82
--- /dev/null
+++ b/drivers/ata/pata_parport/kbic.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * This is a low-level driver for the KBIC-951A and KBIC-971A
+ * parallel to IDE adapter chips from KingByte Information Systems.
+ *
+ * The chips are almost identical, however, the wakeup code
+ * required for the 971A interferes with the correct operation of
+ * the 951A, so this driver registers itself twice, once for
+ * each chip.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define r12w() (delay_p, inw(pi->port + 1) & 0xffff)
+
+#define j44(a, b) ((((a >> 4) & 0x0f) | (b & 0xf0)) ^ 0x88)
+#define j53(w) (((w >> 3) & 0x1f) | ((w >> 4) & 0xe0))
+
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x80, 0x40 };
+
+static int kbic_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, s;
+
+ s = cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ w0(regr | 0x18 | s); w2(4); w2(6); w2(4); w2(1); w0(8);
+ a = r1(); w0(0x28); b = r1(); w2(4);
+ return j44(a, b);
+ case 1:
+ w0(regr|0x38 | s); w2(4); w2(6); w2(4); w2(5); w0(8);
+ a = r12w(); w2(4);
+ return j53(a);
+ case 2:
+ w0(regr | 0x08 | s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
+ a = r0(); w2(4);
+ return a;
+ case 3:
+ case 4:
+ case 5:
+ w0(0x20 | s); w2(4); w2(6); w2(4); w3(regr);
+ a = r4(); b = r4(); w2(4); w2(0); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void kbic_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int s = cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(regr | 0x10 | s); w2(4); w2(6); w2(4);
+ w0(val); w2(5); w2(4);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ w0(0x20 | s); w2(4); w2(6); w2(4); w3(regr);
+ w4(val); w4(val);
+ w2(4); w2(0); w2(4);
+ break;
+ }
+}
+
+static void k951_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void k951_disconnect(struct pi_adapter *pi)
+{
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+#define CCP(x) \
+ do { \
+ w2(0xc4); w0(0xaa); w0(0x55); \
+ w0(0); w0(0xff); w0(0x87); \
+ w0(0x78); w0(x); w2(0xc5); \
+ w2(0xc4); w0(0xff); \
+ } while (0)
+
+static void k971_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ CCP(0x20);
+ w2(4);
+}
+
+static void k971_disconnect(struct pi_adapter *pi)
+{
+ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+/*
+ * count must be congruent to 0 MOD 4, but all known applications
+ *have this property.
+ */
+static void kbic_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ switch (pi->mode) {
+ case 0:
+ w0(0x98); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 2; k++) {
+ w2(1); w0(8);
+ a = r1();
+ w0(0x28);
+ b = r1();
+ buf[2 * k] = j44(a, b);
+ w2(5);
+ b = r1();
+ w0(8);
+ a = r1();
+ buf[2 * k + 1] = j44(a, b);
+ w2(4);
+ }
+ break;
+ case 1:
+ w0(0xb8); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 4; k++) {
+ w0(0xb8);
+ w2(4); w2(5);
+ w0(8);
+ buf[4 * k] = j53(r12w());
+ w0(0xb8);
+ buf[4 * k + 1] = j53(r12w());
+ w2(4); w2(5);
+ buf[4 * k + 3] = j53(r12w());
+ w0(8);
+ buf[4 * k + 2] = j53(r12w());
+ }
+ w2(4);
+ break;
+ case 2:
+ w0(0x88); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 2; k++) {
+ w2(0xa0); w2(0xa1);
+ buf[2 * k] = r0();
+ w2(0xa5);
+ buf[2 * k + 1] = r0();
+ }
+ w2(4);
+ break;
+ case 3:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4); w2(0); w2(4);
+ break;
+ case 4:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4); w2(0); w2(4);
+ break;
+ case 5:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4); w2(0); w2(4);
+ break;
+ }
+}
+
+static void kbic_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0x90); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 2; k++) {
+ w0(buf[2 * k + 1]);
+ w2(0); w2(4);
+ w0(buf[2 * k]);
+ w2(5); w2(4);
+ }
+ break;
+ case 3:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 2; k++) {
+ w4(buf[2 * k + 1]);
+ w4(buf[2 * k]);
+ }
+ w2(4); w2(0); w2(4);
+ break;
+ case 4:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 2; k++)
+ w4w(swab16(((u16 *)buf)[k]));
+ w2(4); w2(0); w2(4);
+ break;
+ case 5:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 4; k++)
+ w4l(swab16(((u16 *)buf)[2 * k]) |
+ swab16(((u16 *)buf)[2 * k + 1]) << 16);
+ w2(4); w2(0); w2(4);
+ break;
+ }
+}
+
+static void kbic_log_adapter(struct pi_adapter *pi, char *chip)
+{
+ char *mode[6] = { "4-bit", "5/3", "8-bit", "EPP-8", "EPP_16", "EPP-32"};
+
+ dev_info(&pi->dev, "KingByte %s at 0x%x, mode %d (%s), delay %d\n",
+ chip, pi->port, pi->mode, mode[pi->mode], pi->delay);
+}
+
+static void k951_log_adapter(struct pi_adapter *pi)
+{
+ kbic_log_adapter(pi, "KBIC-951A");
+}
+
+static void k971_log_adapter(struct pi_adapter *pi)
+{
+ kbic_log_adapter(pi, "KBIC-971A");
+}
+
+static struct pi_protocol k951 = {
+ .owner = THIS_MODULE,
+ .name = "k951",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = kbic_write_regr,
+ .read_regr = kbic_read_regr,
+ .write_block = kbic_write_block,
+ .read_block = kbic_read_block,
+ .connect = k951_connect,
+ .disconnect = k951_disconnect,
+ .log_adapter = k951_log_adapter,
+};
+
+static struct pi_protocol k971 = {
+ .owner = THIS_MODULE,
+ .name = "k971",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = kbic_write_regr,
+ .read_regr = kbic_read_regr,
+ .write_block = kbic_write_block,
+ .read_block = kbic_read_block,
+ .connect = k971_connect,
+ .disconnect = k971_disconnect,
+ .log_adapter = k971_log_adapter,
+};
+
+static int __init kbic_init(void)
+{
+ int rv;
+
+ rv = pata_parport_register_driver(&k951);
+ if (rv < 0)
+ return rv;
+ rv = pata_parport_register_driver(&k971);
+ if (rv < 0)
+ pata_parport_unregister_driver(&k951);
+ return rv;
+}
+
+static void __exit kbic_exit(void)
+{
+ pata_parport_unregister_driver(&k951);
+ pata_parport_unregister_driver(&k971);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("KingByte Information Systems KBIC-951A and KBIC-971A "
+ "parallel port IDE adapter protocol driver");
+module_init(kbic_init)
+module_exit(kbic_exit)
diff --git a/drivers/ata/pata_parport/ktti.c b/drivers/ata/pata_parport/ktti.c
new file mode 100644
index 000000000000..c078d1934862
--- /dev/null
+++ b/drivers/ata/pata_parport/ktti.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * ktti.c is a low-level protocol driver for the KT Technology
+ * parallel port adapter. This adapter is used in the "PHd"
+ * portable hard-drives. As far as I can tell, this device
+ * supports 4-bit mode _only_.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x10, 0x08 };
+
+static void ktti_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ w0(r); w2(0xb); w2(0xa); w2(3); w2(6);
+ w0(val); w2(3); w0(0); w2(6); w2(0xb);
+}
+
+static int ktti_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
+ a = r1(); w2(0xc); b = r1(); w2(9); w2(0xc); w2(9);
+ return j44(a, b);
+}
+
+static void ktti_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ for (k = 0; k < count / 2; k++) {
+ w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
+ a = r1(); w2(0xc); b = r1(); w2(9);
+ buf[2*k] = j44(a, b);
+ a = r1(); w2(0xc); b = r1(); w2(9);
+ buf[2*k+1] = j44(a, b);
+ }
+}
+
+static void ktti_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ for (k = 0; k < count / 2; k++) {
+ w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6);
+ w0(buf[2 * k]); w2(3);
+ w0(buf[2 * k + 1]); w2(6);
+ w2(0xb);
+ }
+}
+
+static void ktti_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xb); w2(0xa); w0(0); w2(3); w2(6);
+}
+
+static void ktti_disconnect(struct pi_adapter *pi)
+{
+ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void ktti_log_adapter(struct pi_adapter *pi)
+{
+ dev_info(&pi->dev, "KT adapter at 0x%x, delay %d\n",
+ pi->port, pi->delay);
+}
+
+static struct pi_protocol ktti = {
+ .owner = THIS_MODULE,
+ .name = "ktti",
+ .max_mode = 1,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = ktti_write_regr,
+ .read_regr = ktti_read_regr,
+ .write_block = ktti_write_block,
+ .read_block = ktti_read_block,
+ .connect = ktti_connect,
+ .disconnect = ktti_disconnect,
+ .log_adapter = ktti_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("KT Technology parallel port IDE adapter protocol driver");
+module_pata_parport_driver(ktti);
diff --git a/drivers/ata/pata_parport/on20.c b/drivers/ata/pata_parport/on20.c
new file mode 100644
index 000000000000..7c70e5b13a2a
--- /dev/null
+++ b/drivers/ata/pata_parport/on20.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * on20.c is a low-level protocol driver for the
+ * Onspec 90c20 parallel to IDE adapter.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define op(f) \
+ do { \
+ w2(4); w0(f); w2(5); w2(0xd); \
+ w2(5); w2(0xd); w2(5); w2(4); \
+ } while (0)
+
+#define vl(v) \
+ do { \
+ w2(4); w0(v); w2(5); \
+ w2(7); w2(5); w2(4); \
+ } while (0)
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+
+static int on20_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int h, l, r;
+
+ r = (regr << 2) + 1 + cont;
+
+ op(1); vl(r); op(0);
+
+ switch (pi->mode) {
+ case 0:
+ w2(4); w2(6); l = r1();
+ w2(4); w2(6); h = r1();
+ w2(4); w2(6); w2(4); w2(6); w2(4);
+ return j44(l, h);
+ case 1:
+ w2(4); w2(0x26); r = r0();
+ w2(4); w2(0x26); w2(4);
+ return r;
+ }
+
+ return -1;
+}
+
+static void on20_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = (regr << 2) + 1 + cont;
+
+ op(1); vl(r);
+ op(0); vl(val);
+ op(0); vl(val);
+}
+
+static void on20_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(0); w2(0xc); w2(4); w2(6); w2(4); w2(6); w2(4);
+ if (pi->mode) {
+ op(2); vl(8); op(2); vl(9);
+ } else {
+ op(2); vl(0); op(2); vl(8);
+ }
+}
+
+static void on20_disconnect(struct pi_adapter *pi)
+{
+ w2(4); w0(7); w2(4); w2(0xc); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void on20_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, l, h;
+
+ op(1); vl(1); op(0);
+
+ for (k = 0; k < count; k++) {
+ if (pi->mode) {
+ w2(4); w2(0x26); buf[k] = r0();
+ } else {
+ w2(6); l = r1(); w2(4);
+ w2(6); h = r1(); w2(4);
+ buf[k] = j44(l, h);
+ }
+ }
+ w2(4);
+}
+
+static void on20_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ op(1); vl(1); op(0);
+
+ for (k = 0; k < count; k++) {
+ w2(5); w0(buf[k]); w2(7);
+ }
+ w2(4);
+}
+
+static void on20_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[2] = { "4-bit", "8-bit" };
+
+ dev_info(&pi->dev,
+ "OnSpec 90c20 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol on20 = {
+ .owner = THIS_MODULE,
+ .name = "on20",
+ .max_mode = 2,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = on20_write_regr,
+ .read_regr = on20_read_regr,
+ .write_block = on20_write_block,
+ .read_block = on20_read_block,
+ .connect = on20_connect,
+ .disconnect = on20_disconnect,
+ .log_adapter = on20_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Onspec 90c20 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(on20);
diff --git a/drivers/ata/pata_parport/on26.c b/drivers/ata/pata_parport/on26.c
new file mode 100644
index 000000000000..c88e5d6f203e
--- /dev/null
+++ b/drivers/ata/pata_parport/on26.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * on26.c is a low-level protocol driver for the
+ * OnSpec 90c26 parallel to IDE adapter chip.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads, 8-bit writes
+ * 1 8-bit reads and writes
+ * 2 8-bit EPP mode
+ * 3 EPP-16
+ * 4 EPP-32
+ */
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+#define P1 \
+ do { \
+ w2(5); w2(0xd); w2(5); w2(0xd); w2(5); w2(4); \
+ } while (0)
+
+#define P2 \
+ do { \
+ w2(5); w2(7); w2(5); w2(4); \
+ } while (0)
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+
+static int on26_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = (regr << 2) + 1 + cont;
+
+ switch (pi->mode) {
+ case 0:
+ w0(1); P1; w0(r); P2; w0(0); P1;
+ w2(6); a = r1(); w2(4);
+ w2(6); b = r1(); w2(4);
+ w2(6); w2(4); w2(6); w2(4);
+ return j44(a, b);
+ case 1:
+ w0(1); P1; w0(r); P2; w0(0); P1;
+ w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
+ return a;
+ case 2:
+ case 3:
+ case 4:
+ w3(1); w3(1); w2(5); w4(r); w2(4);
+ w3(0); w3(0); w2(0x24); a = r4(); w2(4);
+ w2(0x24); (void)r4(); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void on26_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = (regr << 2) + 1 + cont;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(1); P1; w0(r); P2; w0(0); P1;
+ w0(val); P2; w0(val); P2;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w3(1); w3(1); w2(5); w4(r); w2(4);
+ w3(0); w3(0);
+ w2(5); w4(val); w2(4);
+ w2(5); w4(val); w2(4);
+ break;
+ }
+}
+
+#define CCP(x) \
+ do { \
+ w0(0xfe); w0(0xaa); w0(0x55); w0(0); \
+ w0(0xff); w0(0x87); w0(0x78); w0(x); \
+ w2(4); w2(5); w2(4); w0(0xff); \
+ } while (0)
+
+static void on26_connect(struct pi_adapter *pi)
+{
+ int x;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ CCP(0x20);
+ if (pi->mode)
+ x = 9;
+ else
+ x = 8;
+
+ w0(2); P1; w0(8); P2;
+ w0(2); P1; w0(x); P2;
+}
+
+static void on26_disconnect(struct pi_adapter *pi)
+{
+ if (pi->mode >= 2) {
+ w3(4); w3(4); w3(4); w3(4);
+ } else {
+ w0(4); P1; w0(4); P1;
+ }
+ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+#define RESET_WAIT 200
+
+/* hard reset */
+static int on26_test_port(struct pi_adapter *pi)
+{
+ int i, m, d, x = 0, y = 0;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ d = pi->delay;
+ m = pi->mode;
+ pi->delay = 5;
+ pi->mode = 0;
+
+ w2(0xc);
+
+ CCP(0x30); CCP(0);
+
+ w0(0xfe); w0(0xaa); w0(0x55); w0(0); w0(0xff);
+ i = ((r1() & 0xf0) << 4); w0(0x87);
+ i |= (r1() & 0xf0); w0(0x78);
+ w0(0x20); w2(4); w2(5);
+ i |= ((r1() & 0xf0) >> 4);
+ w2(4); w0(0xff);
+
+ if (i == 0xb5f) {
+ w0(2); P1; w0(0); P2;
+ w0(3); P1; w0(0); P2;
+ w0(2); P1; w0(8); P2; udelay(100);
+ w0(2); P1; w0(0xa); P2; udelay(100);
+ w0(2); P1; w0(8); P2; udelay(1000);
+
+ on26_write_regr(pi, 0, 6, 0xa0);
+
+ for (i = 0; i < RESET_WAIT; i++) {
+ on26_write_regr(pi, 0, 6, 0xa0);
+ x = on26_read_regr(pi, 0, 7);
+ on26_write_regr(pi, 0, 6, 0xb0);
+ y = on26_read_regr(pi, 0, 7);
+ if (!((x & 0x80) || (y & 0x80)))
+ break;
+ mdelay(100);
+ }
+
+ if (i == RESET_WAIT)
+ dev_err(&pi->dev,
+ "on26: Device reset failed (%x,%x)\n", x, y);
+
+ w0(4); P1; w0(4); P1;
+ }
+
+ CCP(0x30);
+
+ pi->delay = d;
+ pi->mode = m;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+
+ return 5;
+}
+
+static void on26_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ switch (pi->mode) {
+ case 0:
+ w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
+ udelay(10);
+ for (k = 0; k < count; k++) {
+ w2(6); a = r1();
+ w2(4); b = r1();
+ buf[k] = j44(a, b);
+ }
+ w0(2); P1; w0(8); P2;
+ break;
+ case 1:
+ w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
+ udelay(10);
+ for (k = 0; k < count / 2; k++) {
+ w2(0x26); buf[2 * k] = r0();
+ w2(0x24); buf[2 * k + 1] = r0();
+ }
+ w0(2); P1; w0(9); P2;
+ break;
+ case 2:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4);
+ break;
+ case 3:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4);
+ break;
+ case 4:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4);
+ break;
+ }
+}
+
+static void on26_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(1); P1; w0(1); P2;
+ w0(2); P1; w0(0x18 + pi->mode); P2; w0(0); P1;
+ udelay(10);
+ for (k = 0; k < count / 2; k++) {
+ w2(5); w0(buf[2 * k]);
+ w2(7); w0(buf[2 * k + 1]);
+ }
+ w2(5); w2(4);
+ w0(2); P1; w0(8 + pi->mode); P2;
+ break;
+ case 2:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(0xc4);
+ break;
+ case 3:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(0xc4);
+ break;
+ case 4:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(0xc4);
+ break;
+ }
+}
+
+static void on26_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "OnSpec 90c26 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol on26 = {
+ .owner = THIS_MODULE,
+ .name = "on26",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = on26_write_regr,
+ .read_regr = on26_read_regr,
+ .write_block = on26_write_block,
+ .read_block = on26_read_block,
+ .connect = on26_connect,
+ .disconnect = on26_disconnect,
+ .test_port = on26_test_port,
+ .log_adapter = on26_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Onspec 90c26 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(on26);
diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c
new file mode 100644
index 000000000000..22bd3ff6b7ae
--- /dev/null
+++ b/drivers/ata/pata_parport/pata_parport.c
@@ -0,0 +1,835 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2023 Ondrej Zary
+ * based on paride.c by Grant R. Guenther <grant@torque.net>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include "pata_parport.h"
+
+#define DRV_NAME "pata_parport"
+
+static DEFINE_IDR(parport_list);
+static DEFINE_IDR(protocols);
+static DEFINE_IDA(pata_parport_bus_dev_ids);
+static DEFINE_MUTEX(pi_mutex);
+
+static bool probe = true;
+module_param(probe, bool, 0644);
+MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
+
+/*
+ * libata drivers cannot sleep so this driver claims parport before activating
+ * the ata host and keeps it claimed (and protocol connected) until the ata
+ * host is removed. Unfortunately, this means that you cannot use any chained
+ * devices (neither other pata_parport devices nor a printer).
+ */
+static void pi_connect(struct pi_adapter *pi)
+{
+ parport_claim_or_block(pi->pardev);
+ pi->proto->connect(pi);
+}
+
+static void pi_disconnect(struct pi_adapter *pi)
+{
+ pi->proto->disconnect(pi);
+ parport_release(pi->pardev);
+}
+
+static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+ u8 tmp;
+
+ if (device == 0)
+ tmp = ATA_DEVICE_OBS;
+ else
+ tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+ pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
+ ata_sff_pause(ap);
+}
+
+static void pata_parport_set_devctl(struct ata_port *ap, u8 ctl)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ pi->proto->write_regr(pi, 1, 6, ctl);
+}
+
+static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+ u8 nsect, lbal;
+
+ pata_parport_dev_select(ap, device);
+
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
+
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55);
+
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
+
+ nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+
+ return (nsect == 0x55) && (lbal == 0xaa);
+}
+
+static int pata_parport_wait_after_reset(struct ata_link *link,
+ unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ struct pi_adapter *pi = ap->host->private_data;
+ unsigned int dev0 = devmask & (1 << 0);
+ unsigned int dev1 = devmask & (1 << 1);
+ int rc, ret = 0;
+
+ ata_msleep(ap, ATA_WAIT_AFTER_RESET);
+
+ /* always check readiness of the master device */
+ rc = ata_sff_wait_ready(link, deadline);
+ if (rc) {
+ /*
+ * some adapters return bogus values if master device is not
+ * present, so don't abort now if a slave device is present
+ */
+ if (!dev1)
+ return rc;
+ ret = -ENODEV;
+ }
+
+ /*
+ * if device 1 was found in ata_devchk, wait for register
+ * access briefly, then wait for BSY to clear.
+ */
+ if (dev1) {
+ int i;
+
+ pata_parport_dev_select(ap, 1);
+
+ /*
+ * Wait for register access. Some ATAPI devices fail
+ * to set nsect/lbal after reset, so don't waste too
+ * much time on it. We're gonna wait for !BSY anyway.
+ */
+ for (i = 0; i < 2; i++) {
+ u8 nsect, lbal;
+
+ nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+ if (nsect == 1 && lbal == 1)
+ break;
+ /* give drive a breather */
+ ata_msleep(ap, 50);
+ }
+
+ rc = ata_sff_wait_ready(link, deadline);
+ if (rc) {
+ if (rc != -ENODEV)
+ return rc;
+ ret = rc;
+ }
+ }
+
+ pata_parport_dev_select(ap, 0);
+ if (dev1)
+ pata_parport_dev_select(ap, 1);
+ if (dev0)
+ pata_parport_dev_select(ap, 0);
+
+ return ret;
+}
+
+static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
+ unsigned long deadline)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ /* software reset. causes dev0 to be selected */
+ pi->proto->write_regr(pi, 1, 6, ap->ctl);
+ udelay(20);
+ pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
+ udelay(20);
+ pi->proto->write_regr(pi, 1, 6, ap->ctl);
+ ap->last_ctl = ap->ctl;
+
+ /* wait the port to become ready */
+ return pata_parport_wait_after_reset(&ap->link, devmask, deadline);
+}
+
+static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ unsigned int devmask = 0;
+ int rc;
+ u8 err;
+
+ /* determine if device 0/1 are present */
+ if (pata_parport_devchk(ap, 0))
+ devmask |= (1 << 0);
+ if (pata_parport_devchk(ap, 1))
+ devmask |= (1 << 1);
+
+ /* select device 0 again */
+ pata_parport_dev_select(ap, 0);
+
+ /* issue bus reset */
+ rc = pata_parport_bus_softreset(ap, devmask, deadline);
+ if (rc && rc != -ENODEV) {
+ ata_link_err(link, "SRST failed (errno=%d)\n", rc);
+ return rc;
+ }
+
+ /* determine by signature whether we have ATA or ATAPI devices */
+ classes[0] = ata_sff_dev_classify(&link->device[0],
+ devmask & (1 << 0), &err);
+ if (err != 0x81)
+ classes[1] = ata_sff_dev_classify(&link->device[1],
+ devmask & (1 << 1), &err);
+
+ return 0;
+}
+
+static u8 pata_parport_check_status(struct ata_port *ap)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ return pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+}
+
+static u8 pata_parport_check_altstatus(struct ata_port *ap)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ return pi->proto->read_regr(pi, 1, 6);
+}
+
+static void pata_parport_tf_load(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ if (tf->ctl != ap->last_ctl) {
+ pi->proto->write_regr(pi, 1, 6, tf->ctl);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (tf->flags & ATA_TFLAG_ISADDR) {
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ pi->proto->write_regr(pi, 0, ATA_REG_FEATURE,
+ tf->hob_feature);
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT,
+ tf->hob_nsect);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL,
+ tf->hob_lbal);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAM,
+ tf->hob_lbam);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAH,
+ tf->hob_lbah);
+ }
+ pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature);
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE)
+ pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device);
+
+ ata_wait_idle(ap);
+}
+
+static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+ tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
+ tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+ tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
+ tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
+ tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB);
+ tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
+ tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+ tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
+ tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
+ pi->proto->write_regr(pi, 1, 6, tf->ctl);
+ ap->last_ctl = tf->ctl;
+ }
+}
+
+static void pata_parport_exec_command(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
+ ata_sff_pause(ap);
+}
+
+static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
+ unsigned char *buf, unsigned int buflen, int rw)
+{
+ struct ata_port *ap = qc->dev->link->ap;
+ struct pi_adapter *pi = ap->host->private_data;
+
+ if (rw == READ)
+ pi->proto->read_block(pi, buf, buflen);
+ else
+ pi->proto->write_block(pi, buf, buflen);
+
+ return buflen;
+}
+
+static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
+{
+ int count;
+ struct ata_port *ap;
+ struct pi_adapter *pi;
+ char junk[2];
+
+ /* We only need to flush incoming data when a command was running */
+ if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
+ return;
+
+ ap = qc->ap;
+ pi = ap->host->private_data;
+ /* Drain up to 64K of data before we give up this recovery method */
+ for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
+ && count < 65536; count += 2) {
+ pi->proto->read_block(pi, junk, 2);
+ }
+
+ if (count)
+ ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
+}
+
+static struct ata_port_operations pata_parport_port_ops = {
+ .inherits = &ata_sff_port_ops,
+
+ .reset.softreset = pata_parport_softreset,
+ .reset.hardreset = NULL,
+
+ .sff_dev_select = pata_parport_dev_select,
+ .sff_set_devctl = pata_parport_set_devctl,
+ .sff_check_status = pata_parport_check_status,
+ .sff_check_altstatus = pata_parport_check_altstatus,
+ .sff_tf_load = pata_parport_tf_load,
+ .sff_tf_read = pata_parport_tf_read,
+ .sff_exec_command = pata_parport_exec_command,
+ .sff_data_xfer = pata_parport_data_xfer,
+ .sff_drain_fifo = pata_parport_drain_fifo,
+};
+
+static const struct ata_port_info pata_parport_port_info = {
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
+ .pio_mask = ATA_PIO0,
+ /* No DMA */
+ .port_ops = &pata_parport_port_ops,
+};
+
+static void pi_release(struct pi_adapter *pi)
+{
+ parport_unregister_device(pi->pardev);
+ if (pi->proto->release_proto)
+ pi->proto->release_proto(pi);
+ module_put(pi->proto->owner);
+}
+
+static int default_test_proto(struct pi_adapter *pi)
+{
+ int j, k;
+ int e[2] = { 0, 0 };
+
+ pi->proto->connect(pi);
+
+ for (j = 0; j < 2; j++) {
+ pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
+ pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
+ if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ pi->proto->disconnect(pi);
+
+ dev_dbg(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n",
+ pi->proto->name, pi->port, pi->mode, e[0], e[1]);
+
+ return e[0] && e[1]; /* not here if both > 0 */
+}
+
+static int pi_test_proto(struct pi_adapter *pi)
+{
+ int res;
+
+ parport_claim_or_block(pi->pardev);
+ if (pi->proto->test_proto)
+ res = pi->proto->test_proto(pi);
+ else
+ res = default_test_proto(pi);
+ parport_release(pi->pardev);
+
+ return res;
+}
+
+static bool pi_probe_mode(struct pi_adapter *pi, int max)
+{
+ int best, range;
+
+ if (pi->mode != -1) {
+ if (pi->mode >= max)
+ return false;
+ range = 3;
+ if (pi->mode >= pi->proto->epp_first)
+ range = 8;
+ if (range == 8 && pi->port % 8)
+ return false;
+ return !pi_test_proto(pi);
+ }
+ best = -1;
+ for (pi->mode = 0; pi->mode < max; pi->mode++) {
+ range = 3;
+ if (pi->mode >= pi->proto->epp_first)
+ range = 8;
+ if (range == 8 && pi->port % 8)
+ break;
+ if (!pi_test_proto(pi))
+ best = pi->mode;
+ }
+ pi->mode = best;
+ return best > -1;
+}
+
+static bool pi_probe_unit(struct pi_adapter *pi, int unit)
+{
+ int max, s, e;
+
+ s = unit;
+ e = s + 1;
+
+ if (s == -1) {
+ s = 0;
+ e = pi->proto->max_units;
+ }
+
+ if (pi->proto->test_port) {
+ parport_claim_or_block(pi->pardev);
+ max = pi->proto->test_port(pi);
+ parport_release(pi->pardev);
+ } else {
+ max = pi->proto->max_mode;
+ }
+
+ if (pi->proto->probe_unit) {
+ parport_claim_or_block(pi->pardev);
+ for (pi->unit = s; pi->unit < e; pi->unit++) {
+ if (pi->proto->probe_unit(pi)) {
+ parport_release(pi->pardev);
+ return pi_probe_mode(pi, max);
+ }
+ }
+ parport_release(pi->pardev);
+ return false;
+ }
+
+ return pi_probe_mode(pi, max);
+}
+
+static void pata_parport_dev_release(struct device *dev)
+{
+ struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+
+ ida_free(&pata_parport_bus_dev_ids, dev->id);
+ kfree(pi);
+}
+
+static void pata_parport_bus_release(struct device *dev)
+{
+ /* nothing to do here but required to avoid warning on device removal */
+}
+
+static const struct bus_type pata_parport_bus_type = {
+ .name = DRV_NAME,
+};
+
+static struct device pata_parport_bus = {
+ .init_name = DRV_NAME,
+ .release = pata_parport_bus_release,
+};
+
+static const struct scsi_host_template pata_parport_sht = {
+ PATA_PARPORT_SHT("pata_parport")
+};
+
+struct pi_device_match {
+ struct parport *parport;
+ struct pi_protocol *proto;
+};
+
+static int pi_find_dev(struct device *dev, void *data)
+{
+ struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+ struct pi_device_match *match = data;
+
+ return pi->pardev->port == match->parport && pi->proto == match->proto;
+}
+
+static struct pi_adapter *pi_init_one(struct parport *parport,
+ struct pi_protocol *pr, int mode, int unit, int delay)
+{
+ struct pardev_cb par_cb = { };
+ const struct ata_port_info *ppi[] = { &pata_parport_port_info };
+ struct ata_host *host;
+ struct pi_adapter *pi;
+ struct pi_device_match match = { .parport = parport, .proto = pr };
+ int id;
+
+ /*
+ * Abort if there's a device already registered on the same parport
+ * using the same protocol.
+ */
+ if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev))
+ return NULL;
+
+ id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
+ if (id < 0)
+ return NULL;
+
+ pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
+ if (!pi) {
+ ida_free(&pata_parport_bus_dev_ids, id);
+ return NULL;
+ }
+
+ /* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
+ pi->dev.parent = &pata_parport_bus;
+ pi->dev.bus = &pata_parport_bus_type;
+ pi->dev.driver = &pr->driver;
+ pi->dev.release = pata_parport_dev_release;
+ pi->dev.id = id;
+ dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
+ if (device_register(&pi->dev)) {
+ put_device(&pi->dev);
+ /* pata_parport_dev_release will do ida_free(dev->id) and kfree(pi) */
+ return NULL;
+ }
+
+ pi->proto = pr;
+
+ if (!try_module_get(pi->proto->owner))
+ goto out_unreg_dev;
+ if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
+ goto out_module_put;
+
+ pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
+ pi->mode = mode;
+ pi->port = parport->base;
+
+ par_cb.private = pi;
+ pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb, id);
+ if (!pi->pardev)
+ goto out_module_put;
+
+ if (!pi_probe_unit(pi, unit)) {
+ dev_info(&pi->dev, "Adapter not found\n");
+ goto out_unreg_parport;
+ }
+
+ pi->proto->log_adapter(pi);
+
+ host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1);
+ if (!host)
+ goto out_unreg_parport;
+ dev_set_drvdata(&pi->dev, host);
+ host->private_data = pi;
+
+ ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
+ ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);
+
+ pi_connect(pi);
+ if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht))
+ goto out_disconnect;
+
+ return pi;
+
+out_disconnect:
+ pi_disconnect(pi);
+out_unreg_parport:
+ parport_unregister_device(pi->pardev);
+ if (pi->proto->release_proto)
+ pi->proto->release_proto(pi);
+out_module_put:
+ module_put(pi->proto->owner);
+out_unreg_dev:
+ device_unregister(&pi->dev);
+ /* pata_parport_dev_release will do ida_free(dev->id) and kfree(pi) */
+ return NULL;
+}
+
+int pata_parport_register_driver(struct pi_protocol *pr)
+{
+ int error;
+ struct parport *parport;
+ int port_num;
+
+ pr->driver.bus = &pata_parport_bus_type;
+ pr->driver.name = pr->name;
+ error = driver_register(&pr->driver);
+ if (error)
+ return error;
+
+ mutex_lock(&pi_mutex);
+ error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL);
+ if (error < 0) {
+ driver_unregister(&pr->driver);
+ mutex_unlock(&pi_mutex);
+ return error;
+ }
+
+ pr_info("pata_parport: protocol %s registered\n", pr->name);
+
+ if (probe) {
+ /* probe all parports using this protocol */
+ idr_for_each_entry(&parport_list, parport, port_num)
+ pi_init_one(parport, pr, -1, -1, -1);
+ }
+ mutex_unlock(&pi_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pata_parport_register_driver);
+
+void pata_parport_unregister_driver(struct pi_protocol *pr)
+{
+ struct pi_protocol *pr_iter;
+ int id = -1;
+
+ mutex_lock(&pi_mutex);
+ idr_for_each_entry(&protocols, pr_iter, id) {
+ if (pr_iter == pr)
+ break;
+ }
+ idr_remove(&protocols, id);
+ mutex_unlock(&pi_mutex);
+ driver_unregister(&pr->driver);
+}
+EXPORT_SYMBOL_GPL(pata_parport_unregister_driver);
+
+static ssize_t new_device_store(const struct bus_type *bus, const char *buf, size_t count)
+{
+ char port[12] = "auto";
+ char protocol[8] = "auto";
+ int mode = -1, unit = -1, delay = -1;
+ struct pi_protocol *pr, *pr_wanted;
+ struct device_driver *drv;
+ struct parport *parport;
+ int port_num, port_wanted, pr_num;
+ bool ok = false;
+
+ if (sscanf(buf, "%11s %7s %d %d %d",
+ port, protocol, &mode, &unit, &delay) < 1)
+ return -EINVAL;
+
+ if (sscanf(port, "parport%u", &port_wanted) < 1) {
+ if (strcmp(port, "auto")) {
+ pr_err("invalid port name %s\n", port);
+ return -EINVAL;
+ }
+ port_wanted = -1;
+ }
+
+ drv = driver_find(protocol, &pata_parport_bus_type);
+ if (!drv) {
+ if (strcmp(protocol, "auto")) {
+ pr_err("protocol %s not found\n", protocol);
+ return -EINVAL;
+ }
+ pr_wanted = NULL;
+ } else {
+ pr_wanted = container_of(drv, struct pi_protocol, driver);
+ }
+
+ mutex_lock(&pi_mutex);
+ /* walk all parports */
+ idr_for_each_entry(&parport_list, parport, port_num) {
+ if (port_num == port_wanted || port_wanted == -1) {
+ parport = parport_find_number(port_num);
+ if (!parport) {
+ pr_err("no such port %s\n", port);
+ mutex_unlock(&pi_mutex);
+ return -ENODEV;
+ }
+ /* walk all protocols */
+ idr_for_each_entry(&protocols, pr, pr_num) {
+ if (pr == pr_wanted || !pr_wanted)
+ if (pi_init_one(parport, pr, mode, unit,
+ delay))
+ ok = true;
+ }
+ parport_put_port(parport);
+ }
+ }
+ mutex_unlock(&pi_mutex);
+ if (!ok)
+ return -ENODEV;
+
+ return count;
+}
+static BUS_ATTR_WO(new_device);
+
+static void pi_remove_one(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pi_adapter *pi = host->private_data;
+
+ ata_host_detach(host);
+ pi_disconnect(pi);
+ pi_release(pi);
+ device_unregister(dev);
+ /* pata_parport_dev_release will do ida_free(dev->id) and kfree(pi) */
+}
+
+static ssize_t delete_device_store(const struct bus_type *bus, const char *buf, size_t count)
+{
+ struct device *dev;
+
+ mutex_lock(&pi_mutex);
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (!dev) {
+ mutex_unlock(&pi_mutex);
+ return -ENODEV;
+ }
+
+ pi_remove_one(dev);
+ put_device(dev);
+ mutex_unlock(&pi_mutex);
+
+ return count;
+}
+static BUS_ATTR_WO(delete_device);
+
+static void pata_parport_attach(struct parport *port)
+{
+ struct pi_protocol *pr;
+ int pr_num, id;
+
+ mutex_lock(&pi_mutex);
+ id = idr_alloc(&parport_list, port, port->number, port->number,
+ GFP_KERNEL);
+ if (id < 0) {
+ mutex_unlock(&pi_mutex);
+ return;
+ }
+
+ if (probe) {
+ /* probe this port using all protocols */
+ idr_for_each_entry(&protocols, pr, pr_num)
+ pi_init_one(port, pr, -1, -1, -1);
+ }
+ mutex_unlock(&pi_mutex);
+}
+
+static int pi_remove_port(struct device *dev, void *p)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pi_adapter *pi = host->private_data;
+
+ if (pi->pardev->port == p)
+ pi_remove_one(dev);
+
+ return 0;
+}
+
+static void pata_parport_detach(struct parport *port)
+{
+ mutex_lock(&pi_mutex);
+ bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port);
+ idr_remove(&parport_list, port->number);
+ mutex_unlock(&pi_mutex);
+}
+
+static struct parport_driver pata_parport_driver = {
+ .name = DRV_NAME,
+ .match_port = pata_parport_attach,
+ .detach = pata_parport_detach,
+};
+
+static __init int pata_parport_init(void)
+{
+ int error;
+
+ error = bus_register(&pata_parport_bus_type);
+ if (error) {
+ pr_err("failed to register pata_parport bus, error: %d\n", error);
+ return error;
+ }
+
+ error = device_register(&pata_parport_bus);
+ if (error) {
+ pr_err("failed to register pata_parport bus, error: %d\n", error);
+ goto out_unregister_bus;
+ }
+
+ error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device);
+ if (error) {
+ pr_err("unable to create sysfs file, error: %d\n", error);
+ goto out_unregister_dev;
+ }
+
+ error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device);
+ if (error) {
+ pr_err("unable to create sysfs file, error: %d\n", error);
+ goto out_remove_new;
+ }
+
+ error = parport_register_driver(&pata_parport_driver);
+ if (error) {
+ pr_err("unable to register parport driver, error: %d\n", error);
+ goto out_remove_del;
+ }
+
+ return 0;
+
+out_remove_del:
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
+out_remove_new:
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
+out_unregister_dev:
+ device_unregister(&pata_parport_bus);
+out_unregister_bus:
+ bus_unregister(&pata_parport_bus_type);
+ return error;
+}
+
+static __exit void pata_parport_exit(void)
+{
+ parport_unregister_driver(&pata_parport_driver);
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
+ device_unregister(&pata_parport_bus);
+ bus_unregister(&pata_parport_bus_type);
+}
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("driver for parallel port ATA adapters");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("paride");
+
+module_init(pata_parport_init);
+module_exit(pata_parport_exit);
diff --git a/drivers/ata/pata_parport/pata_parport.h b/drivers/ata/pata_parport/pata_parport.h
new file mode 100644
index 000000000000..bbfa4e63ee85
--- /dev/null
+++ b/drivers/ata/pata_parport/pata_parport.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * pata_parport.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
+ * Under the terms of the GPL.
+ *
+ * This file defines the interface for parallel port IDE adapter chip drivers.
+ */
+
+#ifndef LINUX_PATA_PARPORT_H
+#define LINUX_PATA_PARPORT_H
+
+#include <linux/libata.h>
+
+struct pi_adapter {
+ struct device dev;
+ struct pi_protocol *proto; /* adapter protocol */
+ int port; /* base address of parallel port */
+ int mode; /* transfer mode in use */
+ int delay; /* adapter delay setting */
+ int unit; /* unit number for chained adapters */
+ int saved_r0; /* saved port state */
+ int saved_r2; /* saved port state */
+ unsigned long private; /* for protocol module */
+ struct pardevice *pardev; /* pointer to pardevice */
+};
+
+/* registers are addressed as (cont,regr)
+ * cont: 0 for command register file, 1 for control register(s)
+ * regr: 0-7 for register number.
+ */
+
+/* macros and functions exported to the protocol modules */
+#define delay_p (pi->delay ? udelay(pi->delay) : (void)0)
+#define out_p(offs, byte) do { outb(byte, pi->port + offs); delay_p; } while (0)
+#define in_p(offs) (delay_p, inb(pi->port + offs))
+
+#define w0(byte) out_p(0, byte)
+#define r0() in_p(0)
+#define w1(byte) out_p(1, byte)
+#define r1() in_p(1)
+#define w2(byte) out_p(2, byte)
+#define r2() in_p(2)
+#define w3(byte) out_p(3, byte)
+#define w4(byte) out_p(4, byte)
+#define r4() in_p(4)
+#define w4w(data) do { outw(data, pi->port + 4); delay_p; } while (0)
+#define w4l(data) do { outl(data, pi->port + 4); delay_p; } while (0)
+#define r4w() (delay_p, inw(pi->port + 4))
+#define r4l() (delay_p, inl(pi->port + 4))
+
+struct pi_protocol {
+ char name[8];
+
+ int max_mode;
+ int epp_first; /* modes >= this use 8 ports */
+
+ int default_delay;
+ int max_units; /* max chained units probed for */
+
+ void (*write_regr)(struct pi_adapter *pi, int cont, int regr, int val);
+ int (*read_regr)(struct pi_adapter *pi, int cont, int regr);
+ void (*write_block)(struct pi_adapter *pi, char *buf, int count);
+ void (*read_block)(struct pi_adapter *pi, char *buf, int count);
+
+ void (*connect)(struct pi_adapter *pi);
+ void (*disconnect)(struct pi_adapter *pi);
+
+ int (*test_port)(struct pi_adapter *pi);
+ int (*probe_unit)(struct pi_adapter *pi);
+ int (*test_proto)(struct pi_adapter *pi);
+ void (*log_adapter)(struct pi_adapter *pi);
+
+ int (*init_proto)(struct pi_adapter *pi);
+ void (*release_proto)(struct pi_adapter *pi);
+ struct module *owner;
+ struct device_driver driver;
+ struct scsi_host_template sht;
+};
+
+#define PATA_PARPORT_SHT ATA_PIO_SHT
+
+int pata_parport_register_driver(struct pi_protocol *pr);
+void pata_parport_unregister_driver(struct pi_protocol *pr);
+
+/**
+ * module_pata_parport_driver() - Helper macro for registering a pata_parport driver
+ * @__pi_protocol: pi_protocol struct
+ *
+ * Helper macro for pata_parport drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_pata_parport_driver(__pi_protocol) \
+ module_driver(__pi_protocol, pata_parport_register_driver, pata_parport_unregister_driver)
+
+#endif /* LINUX_PATA_PARPORT_H */
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 8eb066abbd9c..caefcd8c4b3c 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -46,7 +46,7 @@ static int pcmcia_set_mode(struct ata_link *link, struct ata_device **r_failed_d
struct ata_device *slave = &link->device[1];
if (!ata_dev_enabled(master) || !ata_dev_enabled(slave))
- return ata_do_set_mode(link, r_failed_dev);
+ return ata_set_mode(link, r_failed_dev);
if (memcmp(master->id + ATA_ID_FW_REV, slave->id + ATA_ID_FW_REV,
ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0) {
@@ -58,7 +58,7 @@ static int pcmcia_set_mode(struct ata_link *link, struct ata_device **r_failed_d
ata_dev_disable(slave);
}
}
- return ata_do_set_mode(link, r_failed_dev);
+ return ata_set_mode(link, r_failed_dev);
}
/**
@@ -132,7 +132,7 @@ static void pcmcia_8bit_drain_fifo(struct ata_queued_cmd *qc)
}
-static struct scsi_host_template pcmcia_sht = {
+static const struct scsi_host_template pcmcia_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -344,6 +344,7 @@ static const struct pcmcia_device_id pcmcia_devices[] = {
PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79),
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
+ PCMCIA_DEVICE_PROD_ID2("PCMCIA ATA/ATAPI Adapter", 0x888d7b73),
PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 4191aa61c8e4..ae914dcb0c83 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -122,7 +122,7 @@ static struct pci_driver pdc2027x_pci_driver = {
#endif
};
-static struct scsi_host_template pdc2027x_sht = {
+static const struct scsi_host_template pdc2027x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -130,7 +130,7 @@ static struct ata_port_operations pdc2027x_pata100_ops = {
.inherits = &ata_bmdma_port_ops,
.check_atapi_dma = pdc2027x_check_atapi_dma,
.cable_detect = pdc2027x_cable_detect,
- .prereset = pdc2027x_prereset,
+ .reset.prereset = pdc2027x_prereset,
};
static struct ata_port_operations pdc2027x_pata133_ops = {
@@ -295,7 +295,7 @@ static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev)
}
/* Set the PIO timing registers using value table for 133MHz */
- ata_port_dbg(ap, "Set pio regs... \n");
+ ata_port_dbg(ap, "Set PIO regs...\n");
ctcr0 = ioread32(dev_mmio(ap, adev, PDC_CTCR0));
ctcr0 &= 0xffff0000;
@@ -308,7 +308,7 @@ static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev)
ctcr1 |= (pdc2027x_pio_timing_tbl[pio].value2 << 24);
iowrite32(ctcr1, dev_mmio(ap, adev, PDC_CTCR1));
- ata_port_dbg(ap, "Set to pio mode[%u] \n", pio);
+ ata_port_dbg(ap, "Set to PIO mode[%u]\n", pio);
}
/**
@@ -341,7 +341,7 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev)
iowrite32(ctcr1 & ~(1 << 7), dev_mmio(ap, adev, PDC_CTCR1));
}
- ata_port_dbg(ap, "Set udma regs... \n");
+ ata_port_dbg(ap, "Set UDMA regs...\n");
ctcr1 = ioread32(dev_mmio(ap, adev, PDC_CTCR1));
ctcr1 &= 0xff000000;
@@ -350,14 +350,14 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev)
(pdc2027x_udma_timing_tbl[udma_mode].value2 << 16);
iowrite32(ctcr1, dev_mmio(ap, adev, PDC_CTCR1));
- ata_port_dbg(ap, "Set to udma mode[%u] \n", udma_mode);
+ ata_port_dbg(ap, "Set to UDMA mode[%u]\n", udma_mode);
} else if ((dma_mode >= XFER_MW_DMA_0) &&
(dma_mode <= XFER_MW_DMA_2)) {
/* Set the MDMA timing registers with value table for 133MHz */
unsigned int mdma_mode = dma_mode & 0x07;
- ata_port_dbg(ap, "Set mdma regs... \n");
+ ata_port_dbg(ap, "Set MDMA regs...\n");
ctcr0 = ioread32(dev_mmio(ap, adev, PDC_CTCR0));
ctcr0 &= 0x0000ffff;
@@ -366,7 +366,7 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev)
iowrite32(ctcr0, dev_mmio(ap, adev, PDC_CTCR0));
- ata_port_dbg(ap, "Set to mdma mode[%u] \n", mdma_mode);
+ ata_port_dbg(ap, "Set to MDMA mode[%u]\n", mdma_mode);
} else {
ata_port_err(ap, "Unknown dma mode [%u] ignored\n", dma_mode);
}
@@ -387,7 +387,7 @@ static int pdc2027x_set_mode(struct ata_link *link, struct ata_device **r_failed
struct ata_device *dev;
int rc;
- rc = ata_do_set_mode(link, r_failed);
+ rc = ata_set_mode(link, r_failed);
if (rc < 0)
return rc;
diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c
index f894ff2de0a9..a32723e46357 100644
--- a/drivers/ata/pata_pdc202xx_old.c
+++ b/drivers/ata/pata_pdc202xx_old.c
@@ -289,7 +289,7 @@ static int pdc2026x_check_atapi_dma(struct ata_queued_cmd *qc)
return 1;
}
-static struct scsi_host_template pdc202xx_sht = {
+static const struct scsi_host_template pdc202xx_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c
index 389b63b13c70..beb53bd990be 100644
--- a/drivers/ata/pata_piccolo.c
+++ b/drivers/ata/pata_piccolo.c
@@ -62,7 +62,7 @@ static void tosh_set_dmamode(struct ata_port *ap, struct ata_device *adev)
}
-static struct scsi_host_template tosh_sht = {
+static const struct scsi_host_template tosh_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -97,7 +97,7 @@ static int ata_tosh_init_one(struct pci_dev *dev, const struct pci_device_id *id
return ata_pci_bmdma_init_one(dev, ppi, &tosh_sht, NULL, 0);
}
-static struct pci_device_id ata_tosh[] = {
+static const struct pci_device_id ata_tosh[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), },
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), },
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), },
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 21fb059859bd..87479bc893b2 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -45,7 +45,7 @@ static int pata_platform_set_mode(struct ata_link *link, struct ata_device **unu
return 0;
}
-static struct scsi_host_template pata_platform_sht = {
+static const struct scsi_host_template pata_platform_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -97,7 +97,7 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr,
int __pata_platform_probe(struct device *dev, struct resource *io_res,
struct resource *ctl_res, struct resource *irq_res,
unsigned int ioport_shift, int __pio_mask,
- struct scsi_host_template *sht, bool use16bit)
+ const struct scsi_host_template *sht, bool use16bit)
{
struct ata_host *host;
struct ata_port *ap;
diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c
index 985f42c4fd70..03dbaf4a13a7 100644
--- a/drivers/ata/pata_pxa.c
+++ b/drivers/ata/pata_pxa.c
@@ -136,7 +136,7 @@ static int pxa_check_atapi_dma(struct ata_queued_cmd *qc)
return -EOPNOTSUPP;
}
-static struct scsi_host_template pxa_ata_sht = {
+static const struct scsi_host_template pxa_ata_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -223,10 +223,16 @@ static int pxa_ata_probe(struct platform_device *pdev)
ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start,
resource_size(cmd_res));
+ if (!ap->ioaddr.cmd_addr)
+ return -ENOMEM;
ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
resource_size(ctl_res));
+ if (!ap->ioaddr.ctl_addr)
+ return -ENOMEM;
ap->ioaddr.bmdma_addr = devm_ioremap(&pdev->dev, dma_res->start,
resource_size(dma_res));
+ if (!ap->ioaddr.bmdma_addr)
+ return -ENOMEM;
/*
* Adjust register offsets
@@ -274,10 +280,9 @@ static int pxa_ata_probe(struct platform_device *pdev)
/*
* Request the DMA channel
*/
- data->dma_chan =
- dma_request_slave_channel(&pdev->dev, "data");
- if (!data->dma_chan)
- return -EBUSY;
+ data->dma_chan = dma_request_chan(&pdev->dev, "data");
+ if (IS_ERR(data->dma_chan))
+ return PTR_ERR(data->dma_chan);
ret = dmaengine_slave_config(data->dma_chan, &config);
if (ret < 0) {
dev_err(&pdev->dev, "dma configuration failed: %d\n", ret);
@@ -295,7 +300,7 @@ static int pxa_ata_probe(struct platform_device *pdev)
return ret;
}
-static int pxa_ata_remove(struct platform_device *pdev)
+static void pxa_ata_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct pata_pxa_data *data = host->ports[0]->private_data;
@@ -303,8 +308,6 @@ static int pxa_ata_remove(struct platform_device *pdev)
dma_release_channel(data->dma_chan);
ata_host_detach(host);
-
- return 0;
}
static struct platform_driver pxa_ata_driver = {
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 3aca8fe3fdb6..40ef8072c159 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -45,7 +45,7 @@ static void radisys_set_piomode (struct ata_port *ap, struct ata_device *adev)
int control = 0;
/*
- * See Intel Document 298600-004 for the timing programing rules
+ * See Intel Document 298600-004 for the timing programming rules
* for PIIX/ICH. Note that the early PIIX does not have the slave
* timing port at 0x44. The Radisys is a relative of the PIIX
* but not the same so be careful.
@@ -183,7 +183,7 @@ static unsigned int radisys_qc_issue(struct ata_queued_cmd *qc)
}
-static struct scsi_host_template radisys_sht = {
+static const struct scsi_host_template radisys_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c
index 2e110aefe59b..fd81e75c9402 100644
--- a/drivers/ata/pata_rb532_cf.c
+++ b/drivers/ata/pata_rb532_cf.c
@@ -73,7 +73,7 @@ static struct ata_port_operations rb532_pata_port_ops = {
/* ------------------------------------------------------------------------ */
-static struct scsi_host_template rb532_pata_sht = {
+static const struct scsi_host_template rb532_pata_sht = {
ATA_PIO_SHT(DRV_NAME),
};
@@ -155,13 +155,11 @@ static int rb532_pata_driver_probe(struct platform_device *pdev)
return 0;
}
-static int rb532_pata_driver_remove(struct platform_device *pdev)
+static void rb532_pata_driver_remove(struct platform_device *pdev)
{
struct ata_host *ah = platform_get_drvdata(pdev);
ata_host_detach(ah);
-
- return 0;
}
static struct platform_driver rb532_pata_platform_driver = {
diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c
index ecb229c2c1a2..6ff4c11e937d 100644
--- a/drivers/ata/pata_rdc.c
+++ b/drivers/ata/pata_rdc.c
@@ -276,7 +276,7 @@ static struct ata_port_operations rdc_pata_ops = {
.cable_detect = rdc_pata_cable_detect,
.set_piomode = rdc_set_piomode,
.set_dmamode = rdc_set_dmamode,
- .prereset = rdc_pata_prereset,
+ .reset.prereset = rdc_pata_prereset,
};
static const struct ata_port_info rdc_port_info = {
@@ -288,7 +288,7 @@ static const struct ata_port_info rdc_port_info = {
.port_ops = &rdc_pata_ops,
};
-static struct scsi_host_template rdc_sht = {
+static const struct scsi_host_template rdc_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -340,7 +340,7 @@ static int rdc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return rc;
host->private_data = hpriv;
- pci_intx(pdev, 1);
+ pcim_intx(pdev, 1);
host->flags |= ATA_HOST_PARALLEL_SCAN;
@@ -359,8 +359,8 @@ static void rdc_remove_one(struct pci_dev *pdev)
}
static const struct pci_device_id rdc_pci_tbl[] = {
- { PCI_DEVICE(0x17F3, 0x1011), },
- { PCI_DEVICE(0x17F3, 0x1012), },
+ { PCI_VDEVICE(RDC, 0x1011) },
+ { PCI_VDEVICE(RDC, 0x1012) },
{ } /* terminate list */
};
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index fb00c3e5fd19..8e2606793091 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -50,7 +50,7 @@ static int rz1000_set_mode(struct ata_link *link, struct ata_device **unused)
}
-static struct scsi_host_template rz1000_sht = {
+static const struct scsi_host_template rz1000_sht = {
ATA_PIO_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c
deleted file mode 100644
index aba1536ddd44..000000000000
--- a/drivers/ata/pata_samsung_cf.c
+++ /dev/null
@@ -1,662 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * PATA driver for Samsung SoCs.
- * Supports CF Interface in True IDE mode. Currently only PIO mode has been
- * implemented; UDMA support has to be added.
- *
- * Based on:
- * PATA driver for AT91SAM9260 Static Memory Controller
- * PATA driver for Toshiba SCC controller
-*/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-#include <linux/libata.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <linux/platform_data/ata-samsung_cf.h>
-
-#define DRV_NAME "pata_samsung_cf"
-#define DRV_VERSION "0.1"
-
-#define S3C_CFATA_REG(x) (x)
-#define S3C_CFATA_MUX S3C_CFATA_REG(0x0)
-#define S3C_ATA_CTRL S3C_CFATA_REG(0x0)
-#define S3C_ATA_CMD S3C_CFATA_REG(0x8)
-#define S3C_ATA_IRQ S3C_CFATA_REG(0x10)
-#define S3C_ATA_IRQ_MSK S3C_CFATA_REG(0x14)
-#define S3C_ATA_CFG S3C_CFATA_REG(0x18)
-
-#define S3C_ATA_PIO_TIME S3C_CFATA_REG(0x2c)
-#define S3C_ATA_PIO_DTR S3C_CFATA_REG(0x54)
-#define S3C_ATA_PIO_FED S3C_CFATA_REG(0x58)
-#define S3C_ATA_PIO_SCR S3C_CFATA_REG(0x5c)
-#define S3C_ATA_PIO_LLR S3C_CFATA_REG(0x60)
-#define S3C_ATA_PIO_LMR S3C_CFATA_REG(0x64)
-#define S3C_ATA_PIO_LHR S3C_CFATA_REG(0x68)
-#define S3C_ATA_PIO_DVR S3C_CFATA_REG(0x6c)
-#define S3C_ATA_PIO_CSD S3C_CFATA_REG(0x70)
-#define S3C_ATA_PIO_DAD S3C_CFATA_REG(0x74)
-#define S3C_ATA_PIO_RDATA S3C_CFATA_REG(0x7c)
-
-#define S3C_CFATA_MUX_TRUEIDE 0x01
-#define S3C_ATA_CFG_SWAP 0x40
-#define S3C_ATA_CFG_IORDYEN 0x02
-
-enum s3c_cpu_type {
- TYPE_S3C64XX,
- TYPE_S5PV210,
-};
-
-/*
- * struct s3c_ide_info - S3C PATA instance.
- * @clk: The clock resource for this controller.
- * @ide_addr: The area mapped for the hardware registers.
- * @sfr_addr: The area mapped for the special function registers.
- * @irq: The IRQ number we are using.
- * @cpu_type: The exact type of this controller.
- * @fifo_status_reg: The ATA_FIFO_STATUS register offset.
- */
-struct s3c_ide_info {
- struct clk *clk;
- void __iomem *ide_addr;
- void __iomem *sfr_addr;
- int irq;
- enum s3c_cpu_type cpu_type;
- unsigned int fifo_status_reg;
-};
-
-static void pata_s3c_set_endian(void __iomem *s3c_ide_regbase, u8 mode)
-{
- u32 reg = readl(s3c_ide_regbase + S3C_ATA_CFG);
- reg = mode ? (reg & ~S3C_ATA_CFG_SWAP) : (reg | S3C_ATA_CFG_SWAP);
- writel(reg, s3c_ide_regbase + S3C_ATA_CFG);
-}
-
-static void pata_s3c_cfg_mode(void __iomem *s3c_ide_sfrbase)
-{
- /* Select true-ide as the internal operating mode */
- writel(readl(s3c_ide_sfrbase + S3C_CFATA_MUX) | S3C_CFATA_MUX_TRUEIDE,
- s3c_ide_sfrbase + S3C_CFATA_MUX);
-}
-
-static unsigned long
-pata_s3c_setup_timing(struct s3c_ide_info *info, const struct ata_timing *ata)
-{
- int t1 = ata->setup;
- int t2 = ata->act8b;
- int t2i = ata->rec8b;
- ulong piotime;
-
- piotime = ((t2i & 0xff) << 12) | ((t2 & 0xff) << 4) | (t1 & 0xf);
-
- return piotime;
-}
-
-static void pata_s3c_set_piomode(struct ata_port *ap, struct ata_device *adev)
-{
- struct s3c_ide_info *info = ap->host->private_data;
- struct ata_timing timing;
- int cycle_time;
- ulong ata_cfg = readl(info->ide_addr + S3C_ATA_CFG);
- ulong piotime;
-
- /* Enables IORDY if mode requires it */
- if (ata_pio_need_iordy(adev))
- ata_cfg |= S3C_ATA_CFG_IORDYEN;
- else
- ata_cfg &= ~S3C_ATA_CFG_IORDYEN;
-
- cycle_time = (int)(1000000000UL / clk_get_rate(info->clk));
-
- ata_timing_compute(adev, adev->pio_mode, &timing,
- cycle_time * 1000, 0);
-
- piotime = pata_s3c_setup_timing(info, &timing);
-
- writel(ata_cfg, info->ide_addr + S3C_ATA_CFG);
- writel(piotime, info->ide_addr + S3C_ATA_PIO_TIME);
-}
-
-/*
- * Waits until the IDE controller is able to perform next read/write
- * operation to the disk. Needed for 64XX series boards only.
- */
-static int wait_for_host_ready(struct s3c_ide_info *info)
-{
- ulong timeout;
- void __iomem *fifo_reg = info->ide_addr + info->fifo_status_reg;
-
- /* wait for maximum of 20 msec */
- timeout = jiffies + msecs_to_jiffies(20);
- while (time_before(jiffies, timeout)) {
- if ((readl(fifo_reg) >> 28) == 0)
- return 0;
- }
- return -EBUSY;
-}
-
-/*
- * Writes to one of the task file registers.
- */
-static void ata_outb(struct ata_host *host, u8 addr, void __iomem *reg)
-{
- struct s3c_ide_info *info = host->private_data;
-
- wait_for_host_ready(info);
- writeb(addr, reg);
-}
-
-/*
- * Reads from one of the task file registers.
- */
-static u8 ata_inb(struct ata_host *host, void __iomem *reg)
-{
- struct s3c_ide_info *info = host->private_data;
- u8 temp;
-
- wait_for_host_ready(info);
- (void) readb(reg);
- wait_for_host_ready(info);
- temp = readb(info->ide_addr + S3C_ATA_PIO_RDATA);
- return temp;
-}
-
-/*
- * pata_s3c_tf_load - send taskfile registers to host controller
- */
-static void pata_s3c_tf_load(struct ata_port *ap,
- const struct ata_taskfile *tf)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
- unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
-
- if (tf->ctl != ap->last_ctl) {
- ata_outb(ap->host, tf->ctl, ioaddr->ctl_addr);
- ap->last_ctl = tf->ctl;
- ata_wait_idle(ap);
- }
-
- if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
- ata_outb(ap->host, tf->hob_feature, ioaddr->feature_addr);
- ata_outb(ap->host, tf->hob_nsect, ioaddr->nsect_addr);
- ata_outb(ap->host, tf->hob_lbal, ioaddr->lbal_addr);
- ata_outb(ap->host, tf->hob_lbam, ioaddr->lbam_addr);
- ata_outb(ap->host, tf->hob_lbah, ioaddr->lbah_addr);
- }
-
- if (is_addr) {
- ata_outb(ap->host, tf->feature, ioaddr->feature_addr);
- ata_outb(ap->host, tf->nsect, ioaddr->nsect_addr);
- ata_outb(ap->host, tf->lbal, ioaddr->lbal_addr);
- ata_outb(ap->host, tf->lbam, ioaddr->lbam_addr);
- ata_outb(ap->host, tf->lbah, ioaddr->lbah_addr);
- }
-
- if (tf->flags & ATA_TFLAG_DEVICE)
- ata_outb(ap->host, tf->device, ioaddr->device_addr);
-
- ata_wait_idle(ap);
-}
-
-/*
- * pata_s3c_tf_read - input device's ATA taskfile shadow registers
- */
-static void pata_s3c_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
-
- tf->error = ata_inb(ap->host, ioaddr->error_addr);
- tf->nsect = ata_inb(ap->host, ioaddr->nsect_addr);
- tf->lbal = ata_inb(ap->host, ioaddr->lbal_addr);
- tf->lbam = ata_inb(ap->host, ioaddr->lbam_addr);
- tf->lbah = ata_inb(ap->host, ioaddr->lbah_addr);
- tf->device = ata_inb(ap->host, ioaddr->device_addr);
-
- if (tf->flags & ATA_TFLAG_LBA48) {
- ata_outb(ap->host, tf->ctl | ATA_HOB, ioaddr->ctl_addr);
- tf->hob_feature = ata_inb(ap->host, ioaddr->error_addr);
- tf->hob_nsect = ata_inb(ap->host, ioaddr->nsect_addr);
- tf->hob_lbal = ata_inb(ap->host, ioaddr->lbal_addr);
- tf->hob_lbam = ata_inb(ap->host, ioaddr->lbam_addr);
- tf->hob_lbah = ata_inb(ap->host, ioaddr->lbah_addr);
- ata_outb(ap->host, tf->ctl, ioaddr->ctl_addr);
- ap->last_ctl = tf->ctl;
- }
-}
-
-/*
- * pata_s3c_exec_command - issue ATA command to host controller
- */
-static void pata_s3c_exec_command(struct ata_port *ap,
- const struct ata_taskfile *tf)
-{
- ata_outb(ap->host, tf->command, ap->ioaddr.command_addr);
- ata_sff_pause(ap);
-}
-
-/*
- * pata_s3c_check_status - Read device status register
- */
-static u8 pata_s3c_check_status(struct ata_port *ap)
-{
- return ata_inb(ap->host, ap->ioaddr.status_addr);
-}
-
-/*
- * pata_s3c_check_altstatus - Read alternate device status register
- */
-static u8 pata_s3c_check_altstatus(struct ata_port *ap)
-{
- return ata_inb(ap->host, ap->ioaddr.altstatus_addr);
-}
-
-/*
- * pata_s3c_data_xfer - Transfer data by PIO
- */
-static unsigned int pata_s3c_data_xfer(struct ata_queued_cmd *qc,
- unsigned char *buf, unsigned int buflen, int rw)
-{
- struct ata_port *ap = qc->dev->link->ap;
- struct s3c_ide_info *info = ap->host->private_data;
- void __iomem *data_addr = ap->ioaddr.data_addr;
- unsigned int words = buflen >> 1, i;
- u16 *data_ptr = (u16 *)buf;
-
- /* Requires wait same as in ata_inb/ata_outb */
- if (rw == READ)
- for (i = 0; i < words; i++, data_ptr++) {
- wait_for_host_ready(info);
- (void) readw(data_addr);
- wait_for_host_ready(info);
- *data_ptr = readw(info->ide_addr
- + S3C_ATA_PIO_RDATA);
- }
- else
- for (i = 0; i < words; i++, data_ptr++) {
- wait_for_host_ready(info);
- writew(*data_ptr, data_addr);
- }
-
- if (buflen & 0x01)
- dev_err(ap->dev, "unexpected trailing data\n");
-
- return words << 1;
-}
-
-/*
- * pata_s3c_dev_select - Select device on ATA bus
- */
-static void pata_s3c_dev_select(struct ata_port *ap, unsigned int device)
-{
- u8 tmp = ATA_DEVICE_OBS;
-
- if (device != 0)
- tmp |= ATA_DEV1;
-
- ata_outb(ap->host, tmp, ap->ioaddr.device_addr);
- ata_sff_pause(ap);
-}
-
-/*
- * pata_s3c_devchk - PATA device presence detection
- */
-static bool pata_s3c_devchk(struct ata_port *ap, unsigned int device)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
- u8 nsect, lbal;
-
- pata_s3c_dev_select(ap, device);
-
- ata_outb(ap->host, 0x55, ioaddr->nsect_addr);
- ata_outb(ap->host, 0xaa, ioaddr->lbal_addr);
-
- ata_outb(ap->host, 0xaa, ioaddr->nsect_addr);
- ata_outb(ap->host, 0x55, ioaddr->lbal_addr);
-
- ata_outb(ap->host, 0x55, ioaddr->nsect_addr);
- ata_outb(ap->host, 0xaa, ioaddr->lbal_addr);
-
- nsect = ata_inb(ap->host, ioaddr->nsect_addr);
- lbal = ata_inb(ap->host, ioaddr->lbal_addr);
-
- if ((nsect == 0x55) && (lbal == 0xaa))
- return true; /* we found a device */
-
- return false; /* nothing found */
-}
-
-/*
- * pata_s3c_wait_after_reset - wait for devices to become ready after reset
- */
-static int pata_s3c_wait_after_reset(struct ata_link *link,
- unsigned long deadline)
-{
- int rc;
-
- ata_msleep(link->ap, ATA_WAIT_AFTER_RESET);
-
- /* always check readiness of the master device */
- rc = ata_sff_wait_ready(link, deadline);
- /* -ENODEV means the odd clown forgot the D7 pulldown resistor
- * and TF status is 0xff, bail out on it too.
- */
- if (rc)
- return rc;
-
- return 0;
-}
-
-/*
- * pata_s3c_bus_softreset - PATA device software reset
- */
-static int pata_s3c_bus_softreset(struct ata_port *ap,
- unsigned long deadline)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
-
- /* software reset. causes dev0 to be selected */
- ata_outb(ap->host, ap->ctl, ioaddr->ctl_addr);
- udelay(20);
- ata_outb(ap->host, ap->ctl | ATA_SRST, ioaddr->ctl_addr);
- udelay(20);
- ata_outb(ap->host, ap->ctl, ioaddr->ctl_addr);
- ap->last_ctl = ap->ctl;
-
- return pata_s3c_wait_after_reset(&ap->link, deadline);
-}
-
-/*
- * pata_s3c_softreset - reset host port via ATA SRST
- */
-static int pata_s3c_softreset(struct ata_link *link, unsigned int *classes,
- unsigned long deadline)
-{
- struct ata_port *ap = link->ap;
- unsigned int devmask = 0;
- int rc;
- u8 err;
-
- /* determine if device 0 is present */
- if (pata_s3c_devchk(ap, 0))
- devmask |= (1 << 0);
-
- /* select device 0 again */
- pata_s3c_dev_select(ap, 0);
-
- /* issue bus reset */
- rc = pata_s3c_bus_softreset(ap, deadline);
- /* if link is occupied, -ENODEV too is an error */
- if (rc && rc != -ENODEV) {
- ata_link_err(link, "SRST failed (errno=%d)\n", rc);
- return rc;
- }
-
- /* determine by signature whether we have ATA or ATAPI devices */
- classes[0] = ata_sff_dev_classify(&ap->link.device[0],
- devmask & (1 << 0), &err);
-
- return 0;
-}
-
-/*
- * pata_s3c_set_devctl - Write device control register
- */
-static void pata_s3c_set_devctl(struct ata_port *ap, u8 ctl)
-{
- ata_outb(ap->host, ctl, ap->ioaddr.ctl_addr);
-}
-
-static struct scsi_host_template pata_s3c_sht = {
- ATA_PIO_SHT(DRV_NAME),
-};
-
-static struct ata_port_operations pata_s3c_port_ops = {
- .inherits = &ata_sff_port_ops,
- .sff_check_status = pata_s3c_check_status,
- .sff_check_altstatus = pata_s3c_check_altstatus,
- .sff_tf_load = pata_s3c_tf_load,
- .sff_tf_read = pata_s3c_tf_read,
- .sff_data_xfer = pata_s3c_data_xfer,
- .sff_exec_command = pata_s3c_exec_command,
- .sff_dev_select = pata_s3c_dev_select,
- .sff_set_devctl = pata_s3c_set_devctl,
- .softreset = pata_s3c_softreset,
- .set_piomode = pata_s3c_set_piomode,
-};
-
-static struct ata_port_operations pata_s5p_port_ops = {
- .inherits = &ata_sff_port_ops,
- .set_piomode = pata_s3c_set_piomode,
-};
-
-static void pata_s3c_enable(void __iomem *s3c_ide_regbase, bool state)
-{
- u32 temp = readl(s3c_ide_regbase + S3C_ATA_CTRL);
- temp = state ? (temp | 1) : (temp & ~1);
- writel(temp, s3c_ide_regbase + S3C_ATA_CTRL);
-}
-
-static irqreturn_t pata_s3c_irq(int irq, void *dev_instance)
-{
- struct ata_host *host = dev_instance;
- struct s3c_ide_info *info = host->private_data;
- u32 reg;
-
- reg = readl(info->ide_addr + S3C_ATA_IRQ);
- writel(reg, info->ide_addr + S3C_ATA_IRQ);
-
- return ata_sff_interrupt(irq, dev_instance);
-}
-
-static void pata_s3c_hwinit(struct s3c_ide_info *info,
- struct s3c_ide_platdata *pdata)
-{
- switch (info->cpu_type) {
- case TYPE_S3C64XX:
- /* Configure as big endian */
- pata_s3c_cfg_mode(info->sfr_addr);
- pata_s3c_set_endian(info->ide_addr, 1);
- pata_s3c_enable(info->ide_addr, true);
- msleep(100);
-
- /* Remove IRQ Status */
- writel(0x1f, info->ide_addr + S3C_ATA_IRQ);
- writel(0x1b, info->ide_addr + S3C_ATA_IRQ_MSK);
- break;
-
- case TYPE_S5PV210:
- /* Configure as little endian */
- pata_s3c_set_endian(info->ide_addr, 0);
- pata_s3c_enable(info->ide_addr, true);
- msleep(100);
-
- /* Remove IRQ Status */
- writel(0x3f, info->ide_addr + S3C_ATA_IRQ);
- writel(0x3f, info->ide_addr + S3C_ATA_IRQ_MSK);
- break;
-
- default:
- BUG();
- }
-}
-
-static int __init pata_s3c_probe(struct platform_device *pdev)
-{
- struct s3c_ide_platdata *pdata = dev_get_platdata(&pdev->dev);
- struct device *dev = &pdev->dev;
- struct s3c_ide_info *info;
- struct resource *res;
- struct ata_port *ap;
- struct ata_host *host;
- enum s3c_cpu_type cpu_type;
- int ret;
-
- cpu_type = platform_get_device_id(pdev)->driver_data;
-
- info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->irq = platform_get_irq(pdev, 0);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- info->ide_addr = devm_ioremap_resource(dev, res);
- if (IS_ERR(info->ide_addr))
- return PTR_ERR(info->ide_addr);
-
- info->clk = devm_clk_get(&pdev->dev, "cfcon");
- if (IS_ERR(info->clk)) {
- dev_err(dev, "failed to get access to cf controller clock\n");
- ret = PTR_ERR(info->clk);
- info->clk = NULL;
- return ret;
- }
-
- clk_enable(info->clk);
-
- /* init ata host */
- host = ata_host_alloc(dev, 1);
- if (!host) {
- dev_err(dev, "failed to allocate ide host\n");
- ret = -ENOMEM;
- goto stop_clk;
- }
-
- ap = host->ports[0];
- ap->pio_mask = ATA_PIO4;
-
- if (cpu_type == TYPE_S3C64XX) {
- ap->ops = &pata_s3c_port_ops;
- info->sfr_addr = info->ide_addr + 0x1800;
- info->ide_addr += 0x1900;
- info->fifo_status_reg = 0x94;
- } else {
- ap->ops = &pata_s5p_port_ops;
- info->fifo_status_reg = 0x84;
- }
-
- info->cpu_type = cpu_type;
-
- if (info->irq <= 0) {
- ap->flags |= ATA_FLAG_PIO_POLLING;
- info->irq = 0;
- ata_port_desc(ap, "no IRQ, using PIO polling\n");
- }
-
- ap->ioaddr.cmd_addr = info->ide_addr + S3C_ATA_CMD;
- ap->ioaddr.data_addr = info->ide_addr + S3C_ATA_PIO_DTR;
- ap->ioaddr.error_addr = info->ide_addr + S3C_ATA_PIO_FED;
- ap->ioaddr.feature_addr = info->ide_addr + S3C_ATA_PIO_FED;
- ap->ioaddr.nsect_addr = info->ide_addr + S3C_ATA_PIO_SCR;
- ap->ioaddr.lbal_addr = info->ide_addr + S3C_ATA_PIO_LLR;
- ap->ioaddr.lbam_addr = info->ide_addr + S3C_ATA_PIO_LMR;
- ap->ioaddr.lbah_addr = info->ide_addr + S3C_ATA_PIO_LHR;
- ap->ioaddr.device_addr = info->ide_addr + S3C_ATA_PIO_DVR;
- ap->ioaddr.status_addr = info->ide_addr + S3C_ATA_PIO_CSD;
- ap->ioaddr.command_addr = info->ide_addr + S3C_ATA_PIO_CSD;
- ap->ioaddr.altstatus_addr = info->ide_addr + S3C_ATA_PIO_DAD;
- ap->ioaddr.ctl_addr = info->ide_addr + S3C_ATA_PIO_DAD;
-
- ata_port_desc(ap, "mmio cmd 0x%llx ",
- (unsigned long long)res->start);
-
- host->private_data = info;
-
- if (pdata && pdata->setup_gpio)
- pdata->setup_gpio();
-
- /* Set endianness and enable the interface */
- pata_s3c_hwinit(info, pdata);
-
- ret = ata_host_activate(host, info->irq,
- info->irq ? pata_s3c_irq : NULL,
- 0, &pata_s3c_sht);
- if (ret)
- goto stop_clk;
-
- return 0;
-
-stop_clk:
- clk_disable(info->clk);
- return ret;
-}
-
-static int __exit pata_s3c_remove(struct platform_device *pdev)
-{
- struct ata_host *host = platform_get_drvdata(pdev);
- struct s3c_ide_info *info = host->private_data;
-
- ata_host_detach(host);
-
- clk_disable(info->clk);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int pata_s3c_suspend(struct device *dev)
-{
- struct ata_host *host = dev_get_drvdata(dev);
-
- ata_host_suspend(host, PMSG_SUSPEND);
- return 0;
-}
-
-static int pata_s3c_resume(struct device *dev)
-{
- struct ata_host *host = dev_get_drvdata(dev);
- struct s3c_ide_platdata *pdata = dev_get_platdata(dev);
- struct s3c_ide_info *info = host->private_data;
-
- pata_s3c_hwinit(info, pdata);
- ata_host_resume(host);
-
- return 0;
-}
-
-static const struct dev_pm_ops pata_s3c_pm_ops = {
- .suspend = pata_s3c_suspend,
- .resume = pata_s3c_resume,
-};
-#endif
-
-/* driver device registration */
-static const struct platform_device_id pata_s3c_driver_ids[] = {
- {
- .name = "s3c64xx-pata",
- .driver_data = TYPE_S3C64XX,
- }, {
- .name = "s5pv210-pata",
- .driver_data = TYPE_S5PV210,
- },
- { }
-};
-
-MODULE_DEVICE_TABLE(platform, pata_s3c_driver_ids);
-
-static struct platform_driver pata_s3c_driver = {
- .remove = __exit_p(pata_s3c_remove),
- .id_table = pata_s3c_driver_ids,
- .driver = {
- .name = DRV_NAME,
-#ifdef CONFIG_PM_SLEEP
- .pm = &pata_s3c_pm_ops,
-#endif
- },
-};
-
-module_platform_driver_probe(pata_s3c_driver, pata_s3c_probe);
-
-MODULE_AUTHOR("Abhilash Kesavan, <a.kesavan@samsung.com>");
-MODULE_DESCRIPTION("low-level driver for Samsung PATA controller");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
index f28daf62a37d..a388dfb97ad8 100644
--- a/drivers/ata/pata_sc1200.c
+++ b/drivers/ata/pata_sc1200.c
@@ -192,7 +192,7 @@ static int sc1200_qc_defer(struct ata_queued_cmd *qc)
return 0;
}
-static struct scsi_host_template sc1200_sht = {
+static const struct scsi_host_template sc1200_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c
index 4f9c2aefd807..8356f1f2a025 100644
--- a/drivers/ata/pata_sch.c
+++ b/drivers/ata/pata_sch.c
@@ -57,7 +57,7 @@ static struct pci_driver sch_pci_driver = {
#endif
};
-static struct scsi_host_template sch_sht = {
+static const struct scsi_host_template sch_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index c0bc4af0d196..4edddf6bcc15 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -46,10 +46,11 @@
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
-/* Seagate Barracuda ATA IV Family drives in UDMA mode 5
- * can overrun their FIFOs when used with the CSB5 */
-
-static const char *csb_bad_ata100[] = {
+/*
+ * Seagate Barracuda ATA IV Family drives in UDMA mode 5
+ * can overrun their FIFOs when used with the CSB5.
+ */
+static const char * const csb_bad_ata100[] = {
"ST320011A",
"ST340016A",
"ST360021A",
@@ -163,10 +164,11 @@ static unsigned int serverworks_osb4_filter(struct ata_device *adev, unsigned in
* @adev: ATA device
* @mask: Mask of proposed modes
*
- * Check the blacklist and disable UDMA5 if matched
+ * Check the list of devices with broken UDMA5 and
+ * disable UDMA5 if matched.
*/
-
-static unsigned int serverworks_csb_filter(struct ata_device *adev, unsigned int mask)
+static unsigned int serverworks_csb_filter(struct ata_device *adev,
+ unsigned int mask)
{
const char *p;
char model_num[ATA_ID_PROD_LEN + 1];
@@ -252,13 +254,13 @@ static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev
pci_write_config_byte(pdev, 0x54, ultra_cfg);
}
-static struct scsi_host_template serverworks_osb4_sht = {
+static const struct scsi_host_template serverworks_osb4_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
};
-static struct scsi_host_template serverworks_csb_sht = {
+static const struct scsi_host_template serverworks_csb_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -413,7 +415,7 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
}
};
const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL };
- struct scsi_host_template *sht = &serverworks_csb_sht;
+ const struct scsi_host_template *sht = &serverworks_csb_sht;
int rc;
rc = pcim_enable_device(pdev);
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index 67ef2e26d7df..abe64b5f83cf 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -223,7 +223,7 @@ static bool sil680_sff_irq_check(struct ata_port *ap)
return val & 0x08;
}
-static struct scsi_host_template sil680_sht = {
+static const struct scsi_host_template sil680_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index 92e4cf05de2c..2b751e393771 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -539,7 +539,7 @@ static unsigned int sis_133_mode_filter(struct ata_device *adev, unsigned int ma
return mask;
}
-static struct scsi_host_template sis_sht = {
+static const struct scsi_host_template sis_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -552,7 +552,7 @@ static struct ata_port_operations sis_133_for_sata_ops = {
static struct ata_port_operations sis_base_ops = {
.inherits = &ata_bmdma_port_ops,
- .prereset = sis_pre_reset,
+ .reset.prereset = sis_pre_reset,
};
static struct ata_port_operations sis_133_ops = {
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index 8487470e2e01..2d24c6b3e9d9 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -180,8 +180,7 @@ static void sl82c105_bmdma_start(struct ata_queued_cmd *qc)
* document.
*
* This function is also called to turn off DMA when a timeout occurs
- * during DMA operation. In both cases we need to reset the engine,
- * so no actual eng_timeout handler is required.
+ * during DMA operation. In both cases we need to reset the engine.
*
* We assume bmdma_stop is always called if bmdma_start as called. If
* not then we may need to wrap qc_issue.
@@ -238,7 +237,7 @@ static bool sl82c105_sff_irq_check(struct ata_port *ap)
return val & mask;
}
-static struct scsi_host_template sl82c105_sht = {
+static const struct scsi_host_template sl82c105_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -249,7 +248,7 @@ static struct ata_port_operations sl82c105_port_ops = {
.bmdma_stop = sl82c105_bmdma_stop,
.cable_detect = ata_cable_40wire,
.set_piomode = sl82c105_set_piomode,
- .prereset = sl82c105_pre_reset,
+ .reset.prereset = sl82c105_pre_reset,
.sff_irq_check = sl82c105_sff_irq_check,
};
diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
index 782162d2f3f8..596e86a031b3 100644
--- a/drivers/ata/pata_triflex.c
+++ b/drivers/ata/pata_triflex.c
@@ -160,7 +160,7 @@ static void triflex_bmdma_stop(struct ata_queued_cmd *qc)
triflex_load_timing(qc->ap, qc->dev, qc->dev->pio_mode);
}
-static struct scsi_host_template triflex_sht = {
+static const struct scsi_host_template triflex_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -170,7 +170,7 @@ static struct ata_port_operations triflex_port_ops = {
.bmdma_stop = triflex_bmdma_stop,
.cable_detect = ata_cable_40wire,
.set_piomode = triflex_set_piomode,
- .prereset = triflex_prereset,
+ .reset.prereset = triflex_prereset,
};
static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id)
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 34f00f389932..a8c9cf685b4b 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -201,11 +201,9 @@ static int via_cable_detect(struct ata_port *ap) {
two drives */
if (ata66 & (0x10100000 >> (16 * ap->port_no)))
return ATA_CBL_PATA80;
+
/* Check with ACPI so we can spot BIOS reported SATA bridges */
- if (ata_acpi_init_gtm(ap) &&
- ata_acpi_cbl_80wire(ap, ata_acpi_init_gtm(ap)))
- return ATA_CBL_PATA80;
- return ATA_CBL_PATA40;
+ return ata_acpi_cbl_pata_type(ap);
}
static int via_pre_reset(struct ata_link *link, unsigned long deadline)
@@ -368,7 +366,8 @@ static unsigned int via_mode_filter(struct ata_device *dev, unsigned int mask)
}
if (dev->class == ATA_DEV_ATAPI &&
- dmi_check_system(no_atapi_dma_dmi_table)) {
+ (dmi_check_system(no_atapi_dma_dmi_table) ||
+ config->id == PCI_DEVICE_ID_VIA_6415)) {
ata_dev_warn(dev, "controller locks up on ATAPI DMA, forcing PIO\n");
mask &= ATA_MASK_PIO;
}
@@ -443,7 +442,7 @@ static int via_port_start(struct ata_port *ap)
return 0;
}
-static struct scsi_host_template via_sht = {
+static const struct scsi_host_template via_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -452,7 +451,7 @@ static struct ata_port_operations via_port_ops = {
.cable_detect = via_cable_detect,
.set_piomode = via_set_piomode,
.set_dmamode = via_set_dmamode,
- .prereset = via_pre_reset,
+ .reset.prereset = via_pre_reset,
.sff_tf_load = via_tf_load,
.port_start = via_port_start,
.mode_filter = via_mode_filter,
diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c
index 35b823ac20c9..17a5a59861c3 100644
--- a/drivers/ata/pdc_adma.c
+++ b/drivers/ata/pdc_adma.c
@@ -123,7 +123,7 @@ static void adma_freeze(struct ata_port *ap);
static void adma_thaw(struct ata_port *ap);
static int adma_prereset(struct ata_link *link, unsigned long deadline);
-static struct scsi_host_template adma_ata_sht = {
+static const struct scsi_host_template adma_ata_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_MAX_PRD,
.dma_boundary = ADMA_DMA_BOUNDARY,
@@ -140,7 +140,7 @@ static struct ata_port_operations adma_ata_ops = {
.freeze = adma_freeze,
.thaw = adma_thaw,
- .prereset = adma_prereset,
+ .reset.prereset = adma_prereset,
.port_start = adma_port_start,
.port_stop = adma_port_stop,
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index e3263e961045..7a4f59202156 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -18,9 +18,8 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
-#include <linux/of_address.h>
+#include <linux/of.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/libata.h>
@@ -42,10 +41,6 @@
#define sata_dwc_writel(a, v) writel_relaxed(v, a)
#define sata_dwc_readl(a) readl_relaxed(a)
-#ifndef NO_IRQ
-#define NO_IRQ 0
-#endif
-
#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length */
enum {
@@ -242,7 +237,7 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev,
/* Get SATA DMA interrupt number */
hsdev->dma->irq = irq_of_parse_and_map(np, 1);
- if (hsdev->dma->irq == NO_IRQ) {
+ if (!hsdev->dma->irq) {
dev_err(dev, "no SATA DMA irq\n");
return -ENODEV;
}
@@ -472,7 +467,7 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
struct ata_queued_cmd *qc;
unsigned long flags;
u8 status, tag;
- int handled, num_processed, port = 0;
+ int handled, port = 0;
uint intpr, sactive, sactive2, tag_mask;
struct sata_dwc_device_port *hsdevp;
hsdev->sactive_issued = 0;
@@ -618,9 +613,7 @@ DRVSTILLBUSY:
dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);
tag = 0;
- num_processed = 0;
while (tag_mask) {
- num_processed++;
while (!(tag_mask & 0x00000001)) {
tag++;
tag_mask <<= 1;
@@ -816,7 +809,7 @@ static int sata_dwc_dma_get_channel(struct sata_dwc_device_port *hsdevp)
struct device *dev = hsdev->dev;
#ifdef CONFIG_SATA_DWC_OLD_DMA
- if (!of_find_property(dev->of_node, "dmas", NULL))
+ if (!of_property_present(dev->of_node, "dmas"))
return sata_dwc_dma_get_channel_old(hsdevp);
#endif
@@ -1082,7 +1075,7 @@ static void sata_dwc_dev_select(struct ata_port *ap, unsigned int device)
/*
* scsi mid-layer and libata interface structures
*/
-static struct scsi_host_template sata_dwc_sht = {
+static const struct scsi_host_template sata_dwc_sht = {
ATA_NCQ_SHT(DRV_NAME),
/*
* test-only: Currently this driver doesn't handle NCQ
@@ -1104,7 +1097,7 @@ static struct ata_port_operations sata_dwc_ops = {
.inherits = &ata_sff_port_ops,
.error_handler = sata_dwc_error_handler,
- .hardreset = sata_dwc_hardreset,
+ .reset.hardreset = sata_dwc_hardreset,
.qc_issue = sata_dwc_qc_issue,
@@ -1180,13 +1173,13 @@ static int sata_dwc_probe(struct platform_device *ofdev)
/* Get SATA interrupt number */
irq = irq_of_parse_and_map(np, 0);
- if (irq == NO_IRQ) {
+ if (!irq) {
dev_err(dev, "no SATA DMA irq\n");
return -ENODEV;
}
#ifdef CONFIG_SATA_DWC_OLD_DMA
- if (!of_find_property(np, "dmas", NULL)) {
+ if (!of_property_present(np, "dmas")) {
err = sata_dwc_dma_init_old(ofdev, hsdev);
if (err)
return err;
@@ -1217,7 +1210,7 @@ error_out:
return err;
}
-static int sata_dwc_remove(struct platform_device *ofdev)
+static void sata_dwc_remove(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct ata_host *host = dev_get_drvdata(dev);
@@ -1233,7 +1226,6 @@ static int sata_dwc_remove(struct platform_device *ofdev)
#endif
dev_dbg(dev, "done\n");
- return 0;
}
static const struct of_device_id sata_dwc_match[] = {
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index b9a4f68b371d..84da8d6ef28e 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -12,6 +12,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -19,9 +22,6 @@
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include <asm/io.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
static unsigned int intr_coalescing_count;
module_param(intr_coalescing_count, int, S_IRUGO);
@@ -566,7 +566,7 @@ static unsigned int sata_fsl_qc_issue(struct ata_queued_cmd *qc)
return 0;
}
-static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc)
+static void sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct sata_fsl_port_priv *pp = qc->ap->private_data;
struct sata_fsl_host_priv *host_priv = qc->ap->host->private_data;
@@ -577,7 +577,6 @@ static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc)
cd = pp->cmdentry + tag;
ata_tf_from_fis(cd->sfis, &qc->result_tf);
- return true;
}
static int sata_fsl_scr_write(struct ata_link *link,
@@ -1042,7 +1041,7 @@ static void sata_fsl_error_handler(struct ata_port *ap)
static void sata_fsl_post_internal_cmd(struct ata_queued_cmd *qc)
{
- if (qc->flags & ATA_QCFLAG_FAILED)
+ if (qc->flags & ATA_QCFLAG_EH)
qc->err_mask |= AC_ERR_OTHER;
if (qc->err_mask) {
@@ -1377,7 +1376,7 @@ static void sata_fsl_host_stop(struct ata_host *host)
/*
* scsi mid-layer and libata interface structures
*/
-static struct scsi_host_template sata_fsl_sht = {
+static const struct scsi_host_template sata_fsl_sht = {
ATA_NCQ_SHT_QD("sata_fsl", SATA_FSL_QUEUE_DEPTH),
.sg_tablesize = SATA_FSL_MAX_PRD_USABLE,
.dma_boundary = ATA_DMA_BOUNDARY,
@@ -1396,9 +1395,9 @@ static struct ata_port_operations sata_fsl_ops = {
.freeze = sata_fsl_freeze,
.thaw = sata_fsl_thaw,
- .softreset = sata_fsl_softreset,
- .hardreset = sata_fsl_hardreset,
- .pmp_softreset = sata_fsl_softreset,
+ .reset.softreset = sata_fsl_softreset,
+ .reset.hardreset = sata_fsl_hardreset,
+ .pmp_reset.softreset = sata_fsl_softreset,
.error_handler = sata_fsl_error_handler,
.post_internal_cmd = sata_fsl_post_internal_cmd,
@@ -1527,7 +1526,7 @@ error_exit_with_cleanup:
return retval;
}
-static int sata_fsl_remove(struct platform_device *ofdev)
+static void sata_fsl_remove(struct platform_device *ofdev)
{
struct ata_host *host = platform_get_drvdata(ofdev);
struct sata_fsl_host_priv *host_priv = host->private_data;
@@ -1536,8 +1535,6 @@ static int sata_fsl_remove(struct platform_device *ofdev)
device_remove_file(&ofdev->dev, &host_priv->rx_watermark);
ata_host_detach(host);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ata/sata_gemini.c b/drivers/ata/sata_gemini.c
index b729e9919bb0..530ee26b3012 100644
--- a/drivers/ata/sata_gemini.c
+++ b/drivers/ata/sata_gemini.c
@@ -11,9 +11,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
-#include <linux/reset.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/pinctrl/consumer.h>
@@ -28,8 +26,6 @@
* @muxmode: the current muxing mode
* @ide_pins: if the device is using the plain IDE interface pins
* @sata_bridge: if the device enables the SATA bridge
- * @sata0_reset: SATA0 reset handler
- * @sata1_reset: SATA1 reset handler
* @sata0_pclk: SATA0 PCLK handler
* @sata1_pclk: SATA1 PCLK handler
*/
@@ -39,8 +35,6 @@ struct sata_gemini {
enum gemini_muxmode muxmode;
bool ide_pins;
bool sata_bridge;
- struct reset_control *sata0_reset;
- struct reset_control *sata1_reset;
struct clk *sata0_pclk;
struct clk *sata1_pclk;
};
@@ -201,7 +195,10 @@ int gemini_sata_start_bridge(struct sata_gemini *sg, unsigned int bridge)
pclk = sg->sata0_pclk;
else
pclk = sg->sata1_pclk;
- clk_enable(pclk);
+ ret = clk_enable(pclk);
+ if (ret)
+ return ret;
+
msleep(10);
/* Do not keep clocking a bridge that is not online */
@@ -222,18 +219,6 @@ void gemini_sata_stop_bridge(struct sata_gemini *sg, unsigned int bridge)
}
EXPORT_SYMBOL(gemini_sata_stop_bridge);
-int gemini_sata_reset_bridge(struct sata_gemini *sg,
- unsigned int bridge)
-{
- if (bridge == 0)
- reset_control_reset(sg->sata0_reset);
- else
- reset_control_reset(sg->sata1_reset);
- msleep(10);
- return gemini_sata_setup_bridge(sg, bridge);
-}
-EXPORT_SYMBOL(gemini_sata_reset_bridge);
-
static int gemini_sata_bridge_init(struct sata_gemini *sg)
{
struct device *dev = sg->dev;
@@ -263,21 +248,6 @@ static int gemini_sata_bridge_init(struct sata_gemini *sg)
return ret;
}
- sg->sata0_reset = devm_reset_control_get_exclusive(dev, "sata0");
- if (IS_ERR(sg->sata0_reset)) {
- dev_err(dev, "no SATA0 reset controller\n");
- clk_disable_unprepare(sg->sata1_pclk);
- clk_disable_unprepare(sg->sata0_pclk);
- return PTR_ERR(sg->sata0_reset);
- }
- sg->sata1_reset = devm_reset_control_get_exclusive(dev, "sata1");
- if (IS_ERR(sg->sata1_reset)) {
- dev_err(dev, "no SATA1 reset controller\n");
- clk_disable_unprepare(sg->sata1_pclk);
- clk_disable_unprepare(sg->sata0_pclk);
- return PTR_ERR(sg->sata1_reset);
- }
-
sata_id = readl(sg->base + GEMINI_SATA_ID);
sata_phy_id = readl(sg->base + GEMINI_SATA_PHY_ID);
sg->sata_bridge = true;
@@ -400,7 +370,7 @@ out_unprep_clk:
return ret;
}
-static int gemini_sata_remove(struct platform_device *pdev)
+static void gemini_sata_remove(struct platform_device *pdev)
{
struct sata_gemini *sg = platform_get_drvdata(pdev);
@@ -409,25 +379,25 @@ static int gemini_sata_remove(struct platform_device *pdev)
clk_unprepare(sg->sata0_pclk);
}
sg_singleton = NULL;
-
- return 0;
}
static const struct of_device_id gemini_sata_of_match[] = {
{ .compatible = "cortina,gemini-sata-bridge", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, gemini_sata_of_match);
static struct platform_driver gemini_sata_driver = {
.driver = {
.name = DRV_NAME,
- .of_match_table = of_match_ptr(gemini_sata_of_match),
+ .of_match_table = gemini_sata_of_match,
},
.probe = gemini_sata_probe,
.remove = gemini_sata_remove,
};
module_platform_driver(gemini_sata_driver);
+MODULE_DESCRIPTION("low level driver for Cortina Systems Gemini SATA bridge");
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/sata_gemini.h b/drivers/ata/sata_gemini.h
index 6f6e691d6007..b6e4a5c86e01 100644
--- a/drivers/ata/sata_gemini.h
+++ b/drivers/ata/sata_gemini.h
@@ -17,6 +17,5 @@ bool gemini_sata_bridge_enabled(struct sata_gemini *sg, bool is_ata1);
enum gemini_muxmode gemini_sata_get_muxmode(struct sata_gemini *sg);
int gemini_sata_start_bridge(struct sata_gemini *sg, unsigned int bridge);
void gemini_sata_stop_bridge(struct sata_gemini *sg, unsigned int bridge);
-int gemini_sata_reset_bridge(struct sata_gemini *sg, unsigned int bridge);
#endif
diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
index dfbf9493e451..3421039f4bae 100644
--- a/drivers/ata/sata_highbank.c
+++ b/drivers/ata/sata_highbank.c
@@ -13,7 +13,7 @@
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/device.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/libata.h>
@@ -348,6 +348,7 @@ static int highbank_initialize_phys(struct device *dev, void __iomem *addr)
phy_nodes[phy] = phy_data.np;
cphy_base[phy] = of_iomap(phy_nodes[phy], 0);
if (cphy_base[phy] == NULL) {
+ of_node_put(phy_data.np);
return 0;
}
phy_count += 1;
@@ -385,7 +386,7 @@ static int highbank_initialize_phys(struct device *dev, void __iomem *addr)
static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
- static const unsigned long timing[] = { 5, 100, 500};
+ static const unsigned int timing[] = { 5, 100, 500};
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
@@ -427,7 +428,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
static struct ata_port_operations ahci_highbank_ops = {
.inherits = &ahci_ops,
- .hardreset = ahci_highbank_hardreset,
+ .reset.hardreset = ahci_highbank_hardreset,
.transmit_led_message = ecx_transmit_led_message,
};
@@ -438,7 +439,7 @@ static const struct ata_port_info ahci_highbank_port_info = {
.port_ops = &ahci_highbank_ops,
};
-static struct scsi_host_template ahci_highbank_platform_sht = {
+static const struct scsi_host_template ahci_highbank_platform_sht = {
AHCI_SHT("sata_highbank"),
};
@@ -615,11 +616,11 @@ static SIMPLE_DEV_PM_OPS(ahci_highbank_pm_ops,
static struct platform_driver ahci_highbank_driver = {
.remove = ata_platform_remove_one,
- .driver = {
- .name = "highbank-ahci",
- .of_match_table = ahci_of_match,
- .pm = &ahci_highbank_pm_ops,
- },
+ .driver = {
+ .name = "highbank-ahci",
+ .of_match_table = ahci_of_match,
+ .pm = &ahci_highbank_pm_ops,
+ },
.probe = ahci_highbank_probe,
};
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c
index 11e518f0111c..46a8c20daf18 100644
--- a/drivers/ata/sata_inic162x.c
+++ b/drivers/ata/sata_inic162x.c
@@ -242,7 +242,7 @@ struct inic_port_priv {
dma_addr_t cpb_tbl_dma;
};
-static struct scsi_host_template inic_sht = {
+static const struct scsi_host_template inic_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_MAX_PRD, /* maybe it can be larger? */
@@ -566,7 +566,7 @@ static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
tf->status = readb(port_base + PORT_TF_COMMAND);
}
-static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc)
+static void inic_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct ata_taskfile *rtf = &qc->result_tf;
struct ata_taskfile tf;
@@ -580,12 +580,10 @@ static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc)
*/
inic_tf_read(qc->ap, &tf);
- if (!(tf.status & ATA_ERR))
- return false;
-
- rtf->status = tf.status;
- rtf->error = tf.error;
- return true;
+ if (tf.status & ATA_ERR) {
+ rtf->status = tf.status;
+ rtf->error = tf.error;
+ }
}
static void inic_freeze(struct ata_port *ap)
@@ -621,7 +619,7 @@ static int inic_hardreset(struct ata_link *link, unsigned int *class,
struct ata_port *ap = link->ap;
void __iomem *port_base = inic_port_base(ap);
void __iomem *idma_ctl = port_base + PORT_IDMA_CTL;
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);
int rc;
/* hammer it into sane state */
@@ -672,7 +670,7 @@ static void inic_error_handler(struct ata_port *ap)
static void inic_post_internal_cmd(struct ata_queued_cmd *qc)
{
/* make DMA engine forget about the failed command */
- if (qc->flags & ATA_QCFLAG_FAILED)
+ if (qc->flags & ATA_QCFLAG_EH)
inic_reset_port(inic_port_base(qc->ap));
}
@@ -732,7 +730,7 @@ static struct ata_port_operations inic_port_ops = {
.freeze = inic_freeze,
.thaw = inic_thaw,
- .hardreset = inic_hardreset,
+ .reset.hardreset = inic_hardreset,
.error_handler = inic_error_handler,
.post_internal_cmd = inic_post_internal_cmd,
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index e3cff01201b8..ffb396f61731 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -659,21 +659,21 @@ static u8 mv_sff_check_status(struct ata_port *ap);
* PRDs for 64K boundaries in mv_fill_sg().
*/
#ifdef CONFIG_PCI
-static struct scsi_host_template mv5_sht = {
+static const struct scsi_host_template mv5_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = MV_MAX_SG_CT / 2,
.dma_boundary = MV_DMA_BOUNDARY,
};
#endif
-static struct scsi_host_template mv6_sht = {
+static const struct scsi_host_template mv6_sht = {
__ATA_BASE_SHT(DRV_NAME),
.can_queue = MV_MAX_Q_DEPTH - 1,
.sg_tablesize = MV_MAX_SG_CT / 2,
.dma_boundary = MV_DMA_BOUNDARY,
.sdev_groups = ata_ncq_sdev_groups,
.change_queue_depth = ata_scsi_change_queue_depth,
- .tag_alloc_policy = BLK_TAG_ALLOC_RR,
- .slave_configure = ata_scsi_slave_config
+ .tag_alloc_policy_rr = true,
+ .sdev_configure = ata_scsi_sdev_configure
};
static struct ata_port_operations mv5_ops = {
@@ -687,7 +687,7 @@ static struct ata_port_operations mv5_ops = {
.freeze = mv_eh_freeze,
.thaw = mv_eh_thaw,
- .hardreset = mv_hardreset,
+ .reset.hardreset = mv_hardreset,
.scr_read = mv5_scr_read,
.scr_write = mv5_scr_write,
@@ -709,10 +709,10 @@ static struct ata_port_operations mv6_ops = {
.freeze = mv_eh_freeze,
.thaw = mv_eh_thaw,
- .hardreset = mv_hardreset,
- .softreset = mv_softreset,
- .pmp_hardreset = mv_pmp_hardreset,
- .pmp_softreset = mv_softreset,
+ .reset.hardreset = mv_hardreset,
+ .reset.softreset = mv_softreset,
+ .pmp_reset.hardreset = mv_pmp_hardreset,
+ .pmp_reset.softreset = mv_softreset,
.error_handler = mv_pmp_error_handler,
.scr_read = mv_scr_read,
@@ -787,37 +787,6 @@ static const struct ata_port_info mv_port_info[] = {
},
};
-static const struct pci_device_id mv_pci_tbl[] = {
- { PCI_VDEVICE(MARVELL, 0x5040), chip_504x },
- { PCI_VDEVICE(MARVELL, 0x5041), chip_504x },
- { PCI_VDEVICE(MARVELL, 0x5080), chip_5080 },
- { PCI_VDEVICE(MARVELL, 0x5081), chip_508x },
- /* RocketRAID 1720/174x have different identifiers */
- { PCI_VDEVICE(TTI, 0x1720), chip_6042 },
- { PCI_VDEVICE(TTI, 0x1740), chip_6042 },
- { PCI_VDEVICE(TTI, 0x1742), chip_6042 },
-
- { PCI_VDEVICE(MARVELL, 0x6040), chip_604x },
- { PCI_VDEVICE(MARVELL, 0x6041), chip_604x },
- { PCI_VDEVICE(MARVELL, 0x6042), chip_6042 },
- { PCI_VDEVICE(MARVELL, 0x6080), chip_608x },
- { PCI_VDEVICE(MARVELL, 0x6081), chip_608x },
-
- { PCI_VDEVICE(ADAPTEC2, 0x0241), chip_604x },
-
- /* Adaptec 1430SA */
- { PCI_VDEVICE(ADAPTEC2, 0x0243), chip_7042 },
-
- /* Marvell 7042 support */
- { PCI_VDEVICE(MARVELL, 0x7042), chip_7042 },
-
- /* Highpoint RocketRAID PCIe series */
- { PCI_VDEVICE(TTI, 0x2300), chip_7042 },
- { PCI_VDEVICE(TTI, 0x2310), chip_7042 },
-
- { } /* terminate list */
-};
-
static const struct mv_hw_ops mv5xxx_ops = {
.phy_errata = mv5_phy_errata,
.enable_leds = mv5_enable_leds,
@@ -1255,8 +1224,8 @@ static void mv_dump_mem(struct device *dev, void __iomem *start, unsigned bytes)
for (b = 0; b < bytes; ) {
for (w = 0, o = 0; b < bytes && w < 4; w++) {
- o += snprintf(linebuf + o, sizeof(linebuf) - o,
- "%08x ", readl(start + b));
+ o += scnprintf(linebuf + o, sizeof(linebuf) - o,
+ "%08x ", readl(start + b));
b += sizeof(u32);
}
dev_dbg(dev, "%s: %p: %s\n",
@@ -3633,7 +3602,7 @@ static int mv_hardreset(struct ata_link *link, unsigned int *class,
/* Workaround for errata FEr SATA#10 (part 2) */
do {
- const unsigned long *timing =
+ const unsigned int *timing =
sata_ehc_deb_timing(&link->eh_context);
rc = sata_link_hardreset(link, timing, deadline + extra,
@@ -4123,10 +4092,13 @@ static int mv_platform_probe(struct platform_device *pdev)
hpriv->base -= SATAHC0_REG_BASE;
hpriv->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(hpriv->clk))
+ if (IS_ERR(hpriv->clk)) {
dev_notice(&pdev->dev, "cannot get optional clkdev\n");
- else
- clk_prepare_enable(hpriv->clk);
+ } else {
+ rc = clk_prepare_enable(hpriv->clk);
+ if (rc)
+ goto err;
+ }
for (port = 0; port < n_ports; port++) {
char port_number[16];
@@ -4210,7 +4182,7 @@ err:
* A platform bus SATA device has been unplugged. Perform the needed
* cleanup. Also called on module unload for any active devices.
*/
-static int mv_platform_remove(struct platform_device *pdev)
+static void mv_platform_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct mv_host_priv *hpriv = host->private_data;
@@ -4228,7 +4200,6 @@ static int mv_platform_remove(struct platform_device *pdev)
}
phy_power_off(hpriv->port_phys[port]);
}
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -4301,6 +4272,36 @@ static int mv_pci_init_one(struct pci_dev *pdev,
static int mv_pci_device_resume(struct pci_dev *pdev);
#endif
+static const struct pci_device_id mv_pci_tbl[] = {
+ { PCI_VDEVICE(MARVELL, 0x5040), chip_504x },
+ { PCI_VDEVICE(MARVELL, 0x5041), chip_504x },
+ { PCI_VDEVICE(MARVELL, 0x5080), chip_5080 },
+ { PCI_VDEVICE(MARVELL, 0x5081), chip_508x },
+ /* RocketRAID 1720/174x have different identifiers */
+ { PCI_VDEVICE(TTI, 0x1720), chip_6042 },
+ { PCI_VDEVICE(TTI, 0x1740), chip_6042 },
+ { PCI_VDEVICE(TTI, 0x1742), chip_6042 },
+
+ { PCI_VDEVICE(MARVELL, 0x6040), chip_604x },
+ { PCI_VDEVICE(MARVELL, 0x6041), chip_604x },
+ { PCI_VDEVICE(MARVELL, 0x6042), chip_6042 },
+ { PCI_VDEVICE(MARVELL, 0x6080), chip_608x },
+ { PCI_VDEVICE(MARVELL, 0x6081), chip_608x },
+
+ { PCI_VDEVICE(ADAPTEC2, 0x0241), chip_604x },
+
+ /* Adaptec 1430SA */
+ { PCI_VDEVICE(ADAPTEC2, 0x0243), chip_7042 },
+
+ /* Marvell 7042 support */
+ { PCI_VDEVICE(MARVELL, 0x7042), chip_7042 },
+
+ /* Highpoint RocketRAID PCIe series */
+ { PCI_VDEVICE(TTI, 0x2300), chip_7042 },
+ { PCI_VDEVICE(TTI, 0x2310), chip_7042 },
+
+ { } /* terminate list */
+};
static struct pci_driver mv_pci_driver = {
.name = DRV_NAME,
@@ -4313,6 +4314,7 @@ static struct pci_driver mv_pci_driver = {
#endif
};
+MODULE_DEVICE_TABLE(pci, mv_pci_tbl);
/**
* mv_print_info - Dump key info to kernel log for perusal.
@@ -4485,7 +4487,6 @@ static void __exit mv_exit(void)
MODULE_AUTHOR("Brett Russ");
MODULE_DESCRIPTION("SCSI low-level driver for Marvell SATA controllers");
MODULE_LICENSE("GPL v2");
-MODULE_DEVICE_TABLE(pci, mv_pci_tbl);
MODULE_VERSION(DRV_VERSION);
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 7f14d0d31057..841e7de2bba6 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -296,7 +296,8 @@ static void nv_nf2_freeze(struct ata_port *ap);
static void nv_nf2_thaw(struct ata_port *ap);
static void nv_ck804_freeze(struct ata_port *ap);
static void nv_ck804_thaw(struct ata_port *ap);
-static int nv_adma_slave_config(struct scsi_device *sdev);
+static int nv_adma_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim);
static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc);
static enum ata_completion_errors nv_adma_qc_prep(struct ata_queued_cmd *qc);
static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc);
@@ -318,7 +319,8 @@ static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
static void nv_mcp55_thaw(struct ata_port *ap);
static void nv_mcp55_freeze(struct ata_port *ap);
static void nv_swncq_error_handler(struct ata_port *ap);
-static int nv_swncq_slave_config(struct scsi_device *sdev);
+static int nv_swncq_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim);
static int nv_swncq_port_start(struct ata_port *ap);
static enum ata_completion_errors nv_swncq_qc_prep(struct ata_queued_cmd *qc);
static void nv_swncq_fill_sg(struct ata_queued_cmd *qc);
@@ -371,30 +373,30 @@ static struct pci_driver nv_pci_driver = {
.remove = ata_pci_remove_one,
};
-static struct scsi_host_template nv_sht = {
+static const struct scsi_host_template nv_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
-static struct scsi_host_template nv_adma_sht = {
+static const struct scsi_host_template nv_adma_sht = {
__ATA_BASE_SHT(DRV_NAME),
.can_queue = NV_ADMA_MAX_CPBS,
.sg_tablesize = NV_ADMA_SGTBL_TOTAL_LEN,
.dma_boundary = NV_ADMA_DMA_BOUNDARY,
- .slave_configure = nv_adma_slave_config,
+ .sdev_configure = nv_adma_sdev_configure,
.sdev_groups = ata_ncq_sdev_groups,
.change_queue_depth = ata_scsi_change_queue_depth,
- .tag_alloc_policy = BLK_TAG_ALLOC_RR,
+ .tag_alloc_policy_rr = true,
};
-static struct scsi_host_template nv_swncq_sht = {
+static const struct scsi_host_template nv_swncq_sht = {
__ATA_BASE_SHT(DRV_NAME),
.can_queue = ATA_MAX_QUEUE - 1,
.sg_tablesize = LIBATA_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
- .slave_configure = nv_swncq_slave_config,
+ .sdev_configure = nv_swncq_sdev_configure,
.sdev_groups = ata_ncq_sdev_groups,
.change_queue_depth = ata_scsi_change_queue_depth,
- .tag_alloc_policy = BLK_TAG_ALLOC_RR,
+ .tag_alloc_policy_rr = true,
};
/*
@@ -460,7 +462,7 @@ static struct ata_port_operations nv_generic_ops = {
.lost_interrupt = ATA_OP_NULL,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
- .hardreset = nv_hardreset,
+ .reset.hardreset = nv_hardreset,
};
static struct ata_port_operations nv_nf2_ops = {
@@ -520,7 +522,7 @@ static struct ata_port_operations nv_swncq_ops = {
struct nv_pi_priv {
irq_handler_t irq_handler;
- struct scsi_host_template *sht;
+ const struct scsi_host_template *sht;
};
#define NV_PI_PRIV(_irq_handler, _sht) \
@@ -661,7 +663,8 @@ static void nv_adma_mode(struct ata_port *ap)
pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
}
-static int nv_adma_slave_config(struct scsi_device *sdev)
+static int nv_adma_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
struct nv_adma_port_priv *pp = ap->private_data;
@@ -673,7 +676,7 @@ static int nv_adma_slave_config(struct scsi_device *sdev)
int adma_enable;
u32 current_reg, new_reg, config_mask;
- rc = ata_scsi_slave_config(sdev);
+ rc = ata_scsi_sdev_configure(sdev, lim);
if (sdev->id >= ATA_MAX_DEVICES || sdev->channel || sdev->lun)
/* Not a proper libata device, ignore */
@@ -740,8 +743,8 @@ static int nv_adma_slave_config(struct scsi_device *sdev)
rc = dma_set_mask(&pdev->dev, pp->adma_dma_mask);
}
- blk_queue_segment_boundary(sdev->request_queue, segment_boundary);
- blk_queue_max_segments(sdev->request_queue, sg_tablesize);
+ lim->seg_boundary_mask = segment_boundary;
+ lim->max_segments = sg_tablesize;
ata_port_info(ap,
"DMA mask 0x%llX, segment boundary 0x%lX, hw segs %hu\n",
(unsigned long long)*ap->host->dev->dma_mask,
@@ -1529,7 +1532,7 @@ static int nv_hardreset(struct ata_link *link, unsigned int *class,
sata_link_hardreset(link, sata_deb_timing_hotplug, deadline,
NULL, NULL);
else {
- const unsigned long *timing = sata_ehc_deb_timing(ehc);
+ const unsigned int *timing = sata_ehc_deb_timing(ehc);
int rc;
if (!(ehc->i.flags & ATA_EHI_QUIET))
@@ -1868,7 +1871,8 @@ static void nv_swncq_host_init(struct ata_host *host)
writel(~0x0, mmio + NV_INT_STATUS_MCP55);
}
-static int nv_swncq_slave_config(struct scsi_device *sdev)
+static int nv_swncq_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
@@ -1878,7 +1882,7 @@ static int nv_swncq_slave_config(struct scsi_device *sdev)
u8 check_maxtor = 0;
unsigned char model_num[ATA_ID_PROD_LEN + 1];
- rc = ata_scsi_slave_config(sdev);
+ rc = ata_scsi_sdev_configure(sdev, lim);
if (sdev->id >= ATA_MAX_DEVICES || sdev->channel || sdev->lun)
/* Not a proper libata device, ignore */
return rc;
@@ -2185,7 +2189,7 @@ static void nv_swncq_host_interrupt(struct ata_port *ap, u16 fis)
if (!fis)
return;
- if (ap->pflags & ATA_PFLAG_FROZEN)
+ if (ata_port_is_frozen(ap))
return;
if (fis & NV_SWNCQ_IRQ_HOTPLUG) {
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
index b8465fef2ed2..2a005aede123 100644
--- a/drivers/ata/sata_promise.c
+++ b/drivers/ata/sata_promise.c
@@ -158,7 +158,7 @@ static void pdc_error_handler(struct ata_port *ap);
static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
static int pdc_pata_cable_detect(struct ata_port *ap);
-static struct scsi_host_template pdc_ata_sht = {
+static const struct scsi_host_template pdc_ata_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = PDC_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
@@ -188,7 +188,7 @@ static struct ata_port_operations pdc_sata_ops = {
.scr_read = pdc_sata_scr_read,
.scr_write = pdc_sata_scr_write,
.port_start = pdc_sata_port_start,
- .hardreset = pdc_sata_hardreset,
+ .reset.hardreset = pdc_sata_hardreset,
};
/* First-generation chips need a more restrictive ->check_atapi_dma op,
@@ -206,7 +206,7 @@ static struct ata_port_operations pdc_pata_ops = {
.freeze = pdc_freeze,
.thaw = pdc_thaw,
.port_start = pdc_common_port_start,
- .softreset = pdc_pata_softreset,
+ .reset.softreset = pdc_pata_softreset,
};
static const struct ata_port_info pdc_port_info[] = {
@@ -817,7 +817,7 @@ static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class,
static void pdc_error_handler(struct ata_port *ap)
{
- if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ if (!ata_port_is_frozen(ap))
pdc_reset_port(ap);
ata_sff_error_handler(ap);
@@ -828,7 +828,7 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
/* make DMA engine forget about the failed command */
- if (qc->flags & ATA_QCFLAG_FAILED)
+ if (qc->flags & ATA_QCFLAG_EH)
pdc_reset_port(ap);
}
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
index 8ca0810aad26..cfb9b5b61cd7 100644
--- a/drivers/ata/sata_qstor.c
+++ b/drivers/ata/sata_qstor.c
@@ -108,7 +108,7 @@ static void qs_thaw(struct ata_port *ap);
static int qs_prereset(struct ata_link *link, unsigned long deadline);
static void qs_error_handler(struct ata_port *ap);
-static struct scsi_host_template qs_ata_sht = {
+static const struct scsi_host_template qs_ata_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = QS_MAX_PRD,
.dma_boundary = QS_DMA_BOUNDARY,
@@ -123,8 +123,8 @@ static struct ata_port_operations qs_ata_ops = {
.freeze = qs_freeze,
.thaw = qs_thaw,
- .prereset = qs_prereset,
- .softreset = ATA_OP_NULL,
+ .reset.prereset = qs_prereset,
+ .reset.softreset = ATA_OP_NULL,
.error_handler = qs_error_handler,
.lost_interrupt = ATA_OP_NULL,
diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c
index 590ebea99601..487eadd4073f 100644
--- a/drivers/ata/sata_rcar.c
+++ b/drivers/ata/sata_rcar.c
@@ -11,7 +11,7 @@
#include <linux/module.h>
#include <linux/ata.h>
#include <linux/libata.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
@@ -608,7 +608,7 @@ static u8 sata_rcar_bmdma_status(struct ata_port *ap)
return host_stat;
}
-static struct scsi_host_template sata_rcar_sht = {
+static const struct scsi_host_template sata_rcar_sht = {
ATA_BASE_SHT(DRV_NAME),
/*
* This controller allows transfer chunks up to 512MB which cross 64KB
@@ -624,7 +624,7 @@ static struct ata_port_operations sata_rcar_port_ops = {
.freeze = sata_rcar_freeze,
.thaw = sata_rcar_thaw,
- .softreset = sata_rcar_softreset,
+ .reset.softreset = sata_rcar_softreset,
.scr_read = sata_rcar_scr_read,
.scr_write = sata_rcar_scr_write,
@@ -861,21 +861,17 @@ static int sata_rcar_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct ata_host *host;
struct sata_rcar_priv *priv;
- struct resource *mem;
- int irq;
- int ret = 0;
+ int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- if (!irq)
- return -EINVAL;
priv = devm_kzalloc(dev, sizeof(struct sata_rcar_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- priv->type = (enum sata_rcar_type)of_device_get_match_data(dev);
+ priv->type = (unsigned long)of_device_get_match_data(dev);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
@@ -890,8 +886,7 @@ static int sata_rcar_probe(struct platform_device *pdev)
host->private_data = priv;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(dev, mem);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base)) {
ret = PTR_ERR(priv->base);
goto err_pm_put;
@@ -914,7 +909,7 @@ err_pm_put:
return ret;
}
-static int sata_rcar_remove(struct platform_device *pdev)
+static void sata_rcar_remove(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
struct sata_rcar_priv *priv = host->private_data;
@@ -930,8 +925,6 @@ static int sata_rcar_remove(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 3b989a52879d..1b6dc950a42a 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -128,7 +128,7 @@ static const struct pci_device_id sil_pci_tbl[] = {
static const struct sil_drivelist {
const char *product;
unsigned int quirk;
-} sil_blacklist [] = {
+} sil_quirks[] = {
{ "ST320012AS", SIL_QUIRK_MOD15WRITE },
{ "ST330013AS", SIL_QUIRK_MOD15WRITE },
{ "ST340017AS", SIL_QUIRK_MOD15WRITE },
@@ -156,7 +156,7 @@ static struct pci_driver sil_pci_driver = {
#endif
};
-static struct scsi_host_template sil_sht = {
+static const struct scsi_host_template sil_sht = {
ATA_BASE_SHT(DRV_NAME),
/** These controllers support Large Block Transfer which allows
transfer chunks up to 2GB and which cross 64KB boundaries,
@@ -351,7 +351,7 @@ static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed)
u32 tmp, dev_mode[2] = { };
int rc;
- rc = ata_do_set_mode(link, r_failed);
+ rc = ata_set_mode(link, r_failed);
if (rc)
return rc;
@@ -600,8 +600,8 @@ static void sil_thaw(struct ata_port *ap)
* list, and apply the fixups to only the specific
* devices/hosts/firmwares that need it.
*
- * 20040111 - Seagate drives affected by the Mod15Write bug are blacklisted
- * The Maxtor quirk is in the blacklist, but I'm keeping the original
+ * 20040111 - Seagate drives affected by the Mod15Write bug are quirked
+ * The Maxtor quirk is in sil_quirks, but I'm keeping the original
* pessimistic fix for the following reasons...
* - There seems to be less info on it, only one device gleaned off the
* Windows driver, maybe only one is affected. More info would be greatly
@@ -616,13 +616,13 @@ static void sil_dev_config(struct ata_device *dev)
unsigned char model_num[ATA_ID_PROD_LEN + 1];
/* This controller doesn't support trim */
- dev->horkage |= ATA_HORKAGE_NOTRIM;
+ dev->quirks |= ATA_QUIRK_NOTRIM;
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
- for (n = 0; sil_blacklist[n].product; n++)
- if (!strcmp(sil_blacklist[n].product, model_num)) {
- quirks = sil_blacklist[n].quirk;
+ for (n = 0; sil_quirks[n].product; n++)
+ if (!strcmp(sil_quirks[n].product, model_num)) {
+ quirks = sil_quirks[n].quirk;
break;
}
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 2fef6ce93f07..d642ece9f07a 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -328,7 +328,7 @@ static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val);
static int sil24_qc_defer(struct ata_queued_cmd *qc);
static enum ata_completion_errors sil24_qc_prep(struct ata_queued_cmd *qc);
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
-static bool sil24_qc_fill_rtf(struct ata_queued_cmd *qc);
+static void sil24_qc_fill_rtf(struct ata_queued_cmd *qc);
static void sil24_pmp_attach(struct ata_port *ap);
static void sil24_pmp_detach(struct ata_port *ap);
static void sil24_freeze(struct ata_port *ap);
@@ -373,15 +373,14 @@ static struct pci_driver sil24_pci_driver = {
#endif
};
-static struct scsi_host_template sil24_sht = {
+static const struct scsi_host_template sil24_sht = {
__ATA_BASE_SHT(DRV_NAME),
.can_queue = SIL24_MAX_CMDS,
.sg_tablesize = SIL24_MAX_SGE,
.dma_boundary = ATA_DMA_BOUNDARY,
- .tag_alloc_policy = BLK_TAG_ALLOC_FIFO,
.sdev_groups = ata_ncq_sdev_groups,
.change_queue_depth = ata_scsi_change_queue_depth,
- .slave_configure = ata_scsi_slave_config
+ .sdev_configure = ata_scsi_sdev_configure
};
static struct ata_port_operations sil24_ops = {
@@ -394,10 +393,10 @@ static struct ata_port_operations sil24_ops = {
.freeze = sil24_freeze,
.thaw = sil24_thaw,
- .softreset = sil24_softreset,
- .hardreset = sil24_hardreset,
- .pmp_softreset = sil24_softreset,
- .pmp_hardreset = sil24_pmp_hardreset,
+ .reset.softreset = sil24_softreset,
+ .reset.hardreset = sil24_hardreset,
+ .pmp_reset.softreset = sil24_softreset,
+ .pmp_reset.hardreset = sil24_pmp_hardreset,
.error_handler = sil24_error_handler,
.post_internal_cmd = sil24_post_internal_cmd,
.dev_config = sil24_dev_config,
@@ -597,7 +596,7 @@ static int sil24_init_port(struct ata_port *ap)
static int sil24_exec_polled_cmd(struct ata_port *ap, int pmp,
const struct ata_taskfile *tf,
int is_cmd, u32 ctrl,
- unsigned long timeout_msec)
+ unsigned int timeout_msec)
{
void __iomem *port = sil24_port_base(ap);
struct sil24_port_priv *pp = ap->private_data;
@@ -651,7 +650,7 @@ static int sil24_softreset(struct ata_link *link, unsigned int *class,
{
struct ata_port *ap = link->ap;
int pmp = sata_srst_pmp(link);
- unsigned long timeout_msec = 0;
+ unsigned int timeout_msec = 0;
struct ata_taskfile tf;
const char *reason;
int rc;
@@ -901,10 +900,9 @@ static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc)
return 0;
}
-static bool sil24_qc_fill_rtf(struct ata_queued_cmd *qc)
+static void sil24_qc_fill_rtf(struct ata_queued_cmd *qc)
{
sil24_read_tf(qc->ap, qc->hw_tag, &qc->result_tf);
- return true;
}
static void sil24_pmp_attach(struct ata_port *ap)
@@ -1185,7 +1183,7 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
/* make DMA engine forget about the failed command */
- if ((qc->flags & ATA_QCFLAG_FAILED) && sil24_init_port(ap))
+ if ((qc->flags & ATA_QCFLAG_EH) && sil24_init_port(ap))
ata_eh_freeze_port(ap);
}
@@ -1318,7 +1316,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (sata_sil24_msi && !pci_enable_msi(pdev)) {
dev_info(&pdev->dev, "Using MSI\n");
- pci_intx(pdev, 0);
+ pcim_intx(pdev, 0);
}
pci_set_master(pdev);
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index 316237362aa9..b8b6d9eff3b8 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -72,7 +72,7 @@ static struct pci_driver sis_pci_driver = {
#endif
};
-static struct scsi_host_template sis_sht = {
+static const struct scsi_host_template sis_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -290,7 +290,7 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
pci_set_master(pdev);
- pci_intx(pdev, 1);
+ pcim_intx(pdev, 1);
return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
IRQF_SHARED, &sis_sht);
}
diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c
index 2e3418a82b44..c5d6aa36c9c3 100644
--- a/drivers/ata/sata_svw.c
+++ b/drivers/ata/sata_svw.c
@@ -32,6 +32,7 @@
#include <scsi/scsi.h>
#include <linux/libata.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#define DRV_NAME "sata_svw"
#define DRV_VERSION "2.3"
@@ -319,10 +320,11 @@ static int k2_sata_show_info(struct seq_file *m, struct Scsi_Host *shost)
/* Match it to a port node */
index = (ap == ap->host->ports[0]) ? 0 : 1;
for (np = np->child; np != NULL; np = np->sibling) {
- const u32 *reg = of_get_property(np, "reg", NULL);
- if (!reg)
+ u64 reg;
+
+ if (of_property_read_reg(np, 0, &reg, NULL))
continue;
- if (index == *reg) {
+ if (index == reg) {
seq_printf(m, "devspec: %pOF\n", np);
break;
}
@@ -330,7 +332,7 @@ static int k2_sata_show_info(struct seq_file *m, struct Scsi_Host *shost)
return 0;
}
-static struct scsi_host_template k2_sata_sht = {
+static const struct scsi_host_template k2_sata_sht = {
ATA_BMDMA_SHT(DRV_NAME),
.show_info = k2_sata_show_info,
};
@@ -338,8 +340,8 @@ static struct scsi_host_template k2_sata_sht = {
static struct ata_port_operations k2_sata_ops = {
.inherits = &ata_bmdma_port_ops,
- .softreset = k2_sata_softreset,
- .hardreset = k2_sata_hardreset,
+ .reset.softreset = k2_sata_softreset,
+ .reset.hardreset = k2_sata_hardreset,
.sff_tf_load = k2_sata_tf_load,
.sff_tf_read = k2_sata_tf_read,
.sff_check_status = k2_stat_check_status,
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index 6ceec59cb291..0986ebd1eb4e 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -226,13 +226,12 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
-static struct scsi_host_template pdc_sata_sht = {
+static const struct scsi_host_template pdc_sata_sht = {
ATA_BASE_SHT(DRV_NAME),
.sg_tablesize = LIBATA_MAX_PRD,
.dma_boundary = ATA_DMA_BOUNDARY,
};
-/* TODO: inherit from base port_ops after converting to new EH */
static struct ata_port_operations pdc_20621_ops = {
.inherits = &ata_sff_port_ops,
@@ -242,7 +241,7 @@ static struct ata_port_operations pdc_20621_ops = {
.freeze = pdc_freeze,
.thaw = pdc_thaw,
- .softreset = pdc_softreset,
+ .reset.softreset = pdc_softreset,
.error_handler = pdc_error_handler,
.lost_interrupt = ATA_OP_NULL,
.post_internal_cmd = pdc_post_internal_cmd,
@@ -855,7 +854,7 @@ static int pdc_softreset(struct ata_link *link, unsigned int *class,
static void pdc_error_handler(struct ata_port *ap)
{
- if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ if (!ata_port_is_frozen(ap))
pdc_reset_port(ap);
ata_sff_error_handler(ap);
@@ -866,7 +865,7 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
/* make DMA engine forget about the failed command */
- if (qc->flags & ATA_QCFLAG_FAILED)
+ if (qc->flags & ATA_QCFLAG_EH)
pdc_reset_port(ap);
}
@@ -958,8 +957,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource,
offset -= (idx * window_size);
idx++;
- dist = ((long) (window_size - (offset + size))) >= 0 ? size :
- (long) (window_size - offset);
+ dist = min(size, window_size - offset);
memcpy_fromio(psource, dimm_mmio + offset / 4, dist);
psource += dist;
@@ -1006,8 +1004,7 @@ static void pdc20621_put_to_dimm(struct ata_host *host, void *psource,
readl(mmio + PDC_DIMM_WINDOW_CTLR);
offset -= (idx * window_size);
idx++;
- dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size :
- (long) (window_size - offset);
+ dist = min(size, window_size - offset);
memcpy_toio(dimm_mmio + offset / 4, psource, dist);
writel(0x01, mmio + PDC_GENERAL_CTLR);
readl(mmio + PDC_GENERAL_CTLR);
@@ -1120,9 +1117,14 @@ static int pdc20621_prog_dimm0(struct ata_host *host)
mmio += PDC_CHIP0_OFS;
for (i = 0; i < ARRAY_SIZE(pdc_i2c_read_data); i++)
- pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
- pdc_i2c_read_data[i].reg,
- &spd0[pdc_i2c_read_data[i].ofs]);
+ if (!pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
+ pdc_i2c_read_data[i].reg,
+ &spd0[pdc_i2c_read_data[i].ofs])) {
+ dev_err(host->dev,
+ "Failed in i2c read at index %d: device=%#x, reg=%#x\n",
+ i, PDC_DIMM0_SPD_DEV_ADDRESS, pdc_i2c_read_data[i].reg);
+ return -EIO;
+ }
data |= (spd0[4] - 8) | ((spd0[21] != 0) << 3) | ((spd0[3]-11) << 4);
data |= ((spd0[17] / 4) << 6) | ((spd0[5] / 2) << 7) |
@@ -1287,6 +1289,8 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host)
/* Programming DIMM0 Module Control Register (index_CID0:80h) */
size = pdc20621_prog_dimm0(host);
+ if (size < 0)
+ return size;
dev_dbg(host->dev, "Local DIMM Size = %dMB\n", size);
/* Programming DIMM Module Global Control Register (index_CID0:88h) */
@@ -1297,32 +1301,32 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host)
}
if (dimm_test) {
- u8 test_parttern1[40] =
+ u8 test_pattern1[40] =
{0x55,0xAA,'P','r','o','m','i','s','e',' ',
'N','o','t',' ','Y','e','t',' ',
'D','e','f','i','n','e','d',' ',
'1','.','1','0',
'9','8','0','3','1','6','1','2',0,0};
- u8 test_parttern2[40] = {0};
+ u8 test_pattern2[40] = {0};
- pdc20621_put_to_dimm(host, test_parttern2, 0x10040, 40);
- pdc20621_put_to_dimm(host, test_parttern2, 0x40, 40);
+ pdc20621_put_to_dimm(host, test_pattern2, 0x10040, 40);
+ pdc20621_put_to_dimm(host, test_pattern2, 0x40, 40);
- pdc20621_put_to_dimm(host, test_parttern1, 0x10040, 40);
- pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40);
- dev_info(host->dev, "DIMM test pattern 1: %x, %x, %s\n", test_parttern2[0],
- test_parttern2[1], &(test_parttern2[2]));
- pdc20621_get_from_dimm(host, test_parttern2, 0x10040,
+ pdc20621_put_to_dimm(host, test_pattern1, 0x10040, 40);
+ pdc20621_get_from_dimm(host, test_pattern2, 0x40, 40);
+ dev_info(host->dev, "DIMM test pattern 1: %x, %x, %s\n", test_pattern2[0],
+ test_pattern2[1], &(test_pattern2[2]));
+ pdc20621_get_from_dimm(host, test_pattern2, 0x10040,
40);
dev_info(host->dev, "DIMM test pattern 2: %x, %x, %s\n",
- test_parttern2[0],
- test_parttern2[1], &(test_parttern2[2]));
+ test_pattern2[0],
+ test_pattern2[1], &(test_pattern2[2]));
- pdc20621_put_to_dimm(host, test_parttern1, 0x40, 40);
- pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40);
+ pdc20621_put_to_dimm(host, test_pattern1, 0x40, 40);
+ pdc20621_get_from_dimm(host, test_pattern2, 0x40, 40);
dev_info(host->dev, "DIMM test pattern 3: %x, %x, %s\n",
- test_parttern2[0],
- test_parttern2[1], &(test_parttern2[2]));
+ test_pattern2[0],
+ test_pattern2[1], &(test_pattern2[2]));
}
/* ECC initiliazation. */
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index 815e6af75310..44985796cc47 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -59,7 +59,7 @@ static struct pci_driver uli_pci_driver = {
.remove = ata_pci_remove_one,
};
-static struct scsi_host_template uli_sht = {
+static const struct scsi_host_template uli_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -67,7 +67,7 @@ static struct ata_port_operations uli_ops = {
.inherits = &ata_bmdma_port_ops,
.scr_read = uli_scr_read,
.scr_write = uli_scr_write,
- .hardreset = ATA_OP_NULL,
+ .reset.hardreset = ATA_OP_NULL,
};
static const struct ata_port_info uli_port_info = {
@@ -221,7 +221,7 @@ static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
pci_set_master(pdev);
- pci_intx(pdev, 1);
+ pcim_intx(pdev, 1);
return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
IRQF_SHARED, &uli_sht);
}
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index c7891cc84ea0..68e9003ec2d4 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -25,6 +25,7 @@
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
+#include <linux/string_choices.h>
#define DRV_NAME "sata_via"
#define DRV_VERSION "2.6"
@@ -107,7 +108,7 @@ static struct pci_driver svia_pci_driver = {
.remove = ata_pci_remove_one,
};
-static struct scsi_host_template svia_sht = {
+static const struct scsi_host_template svia_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -119,7 +120,7 @@ static struct ata_port_operations svia_base_ops = {
static struct ata_port_operations vt6420_sata_ops = {
.inherits = &svia_base_ops,
.freeze = svia_noop_freeze,
- .prereset = vt6420_prereset,
+ .reset.prereset = vt6420_prereset,
.bmdma_start = vt6420_bmdma_start,
};
@@ -139,7 +140,7 @@ static struct ata_port_operations vt6421_sata_ops = {
static struct ata_port_operations vt8251_ops = {
.inherits = &svia_base_ops,
- .hardreset = sata_std_hardreset,
+ .reset.hardreset = sata_std_hardreset,
.scr_read = vt8251_scr_read,
.scr_write = vt8251_scr_write,
};
@@ -359,7 +360,7 @@ static int vt6420_prereset(struct ata_link *link, unsigned long deadline)
ata_port_info(ap,
"SATA link %s 1.5 Gbps (SStatus %X SControl %X)\n",
- online ? "up" : "down", sstatus, scontrol);
+ str_up_down(online), sstatus, scontrol);
/* SStatus is read one more time */
svia_scr_read(link, SCR_STATUS, &sstatus);
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
index 87e4ed66b306..a53a2dfc1e17 100644
--- a/drivers/ata/sata_vsc.c
+++ b/drivers/ata/sata_vsc.c
@@ -277,7 +277,7 @@ out:
}
-static struct scsi_host_template vsc_sata_sht = {
+static const struct scsi_host_template vsc_sata_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -384,7 +384,7 @@ static int vsc_sata_init_one(struct pci_dev *pdev,
pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80);
if (pci_enable_msi(pdev) == 0)
- pci_intx(pdev, 0);
+ pcim_intx(pdev, 0);
/*
* Config offset 0x98 is "Extended Control and Status Register 0"
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
index 96bea1ab1ecc..fa3c76a2b49d 100644
--- a/drivers/atm/atmtcp.c
+++ b/drivers/atm/atmtcp.c
@@ -279,6 +279,19 @@ static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci)
return NULL;
}
+static int atmtcp_c_pre_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct atmtcp_hdr *hdr;
+
+ if (skb->len < sizeof(struct atmtcp_hdr))
+ return -EINVAL;
+
+ hdr = (struct atmtcp_hdr *)skb->data;
+ if (hdr->length == ATMTCP_HDR_MAGIC)
+ return -EINVAL;
+
+ return 0;
+}
static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
{
@@ -288,7 +301,6 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
struct sk_buff *new_skb;
int result = 0;
- if (!skb->len) return 0;
dev = vcc->dev_data;
hdr = (struct atmtcp_hdr *) skb->data;
if (hdr->length == ATMTCP_HDR_MAGIC) {
@@ -345,6 +357,7 @@ static const struct atmdev_ops atmtcp_v_dev_ops = {
static const struct atmdev_ops atmtcp_c_dev_ops = {
.close = atmtcp_c_close,
+ .pre_send = atmtcp_c_pre_send,
.send = atmtcp_c_send
};
@@ -494,6 +507,7 @@ static void __exit atmtcp_exit(void)
deregister_atm_ioctl(&atmtcp_ioctl_ops);
}
+MODULE_DESCRIPTION("ATM over TCP");
MODULE_LICENSE("GPL");
module_init(atmtcp_init);
module_exit(atmtcp_exit);
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index a31ffe16e626..3011cf1a84a9 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -2318,4 +2318,5 @@ static int __init eni_init(void)
module_init(eni_init);
/* @@@ since exit routine not defined, this module can not be unloaded */
+MODULE_DESCRIPTION("Efficient Networks ENI155P ATM NIC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index fb2be3574c26..f62e38571440 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -36,7 +36,7 @@
#ifdef CONFIG_SBUS
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <asm/idprom.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
@@ -94,9 +94,6 @@
static const struct atmdev_ops fore200e_ops;
-static LIST_HEAD(fore200e_boards);
-
-
MODULE_AUTHOR("Christophe Lizzi - credits to Uwe Dannowski and Heikki Vatiainen");
MODULE_DESCRIPTION("FORE Systems 200E-series ATM driver - version " FORE200E_VERSION);
@@ -1377,7 +1374,9 @@ fore200e_open(struct atm_vcc *vcc)
vcc->dev_data = NULL;
+ mutex_lock(&fore200e->rate_mtx);
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
+ mutex_unlock(&fore200e->rate_mtx);
kfree(fore200e_vcc);
return -EINVAL;
@@ -2520,18 +2519,12 @@ static int fore200e_init(struct fore200e *fore200e, struct device *parent)
}
#ifdef CONFIG_SBUS
-static const struct of_device_id fore200e_sba_match[];
static int fore200e_sba_probe(struct platform_device *op)
{
- const struct of_device_id *match;
struct fore200e *fore200e;
static int index = 0;
int err;
- match = of_match_device(fore200e_sba_match, &op->dev);
- if (!match)
- return -EINVAL;
-
fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL);
if (!fore200e)
return -ENOMEM;
@@ -2556,14 +2549,12 @@ static int fore200e_sba_probe(struct platform_device *op)
return 0;
}
-static int fore200e_sba_remove(struct platform_device *op)
+static void fore200e_sba_remove(struct platform_device *op)
{
struct fore200e *fore200e = dev_get_drvdata(&op->dev);
fore200e_shutdown(fore200e);
kfree(fore200e);
-
- return 0;
}
static const struct of_device_id fore200e_sba_match[] = {
diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h
index caf0ea6a328a..5d95fe9fd836 100644
--- a/drivers/atm/fore200e.h
+++ b/drivers/atm/fore200e.h
@@ -830,7 +830,6 @@ typedef struct fore200e_vc_map {
/* per-device data */
typedef struct fore200e {
- struct list_head entry; /* next device */
const struct fore200e_bus* bus; /* bus-dependent code and data */
union fore200e_regs regs; /* bus-dependent registers */
struct atm_dev* atm_dev; /* ATM device */
diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c
index bfca7b8a6f31..e6a300203e6c 100644
--- a/drivers/atm/idt77105.c
+++ b/drivers/atm/idt77105.c
@@ -366,10 +366,11 @@ EXPORT_SYMBOL(idt77105_init);
static void __exit idt77105_exit(void)
{
/* turn off timers */
- del_timer_sync(&stats_timer);
- del_timer_sync(&restart_timer);
+ timer_delete_sync(&stats_timer);
+ timer_delete_sync(&restart_timer);
}
module_exit(idt77105_exit);
+MODULE_DESCRIPTION("IDT77105 PHY driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
index 681cb3786794..f2e91b7d79f0 100644
--- a/drivers/atm/idt77252.c
+++ b/drivers/atm/idt77252.c
@@ -852,6 +852,8 @@ queue_skb(struct idt77252_dev *card, struct vc_map *vc,
IDT77252_PRV_PADDR(skb) = dma_map_single(&card->pcidev->dev, skb->data,
skb->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&card->pcidev->dev, IDT77252_PRV_PADDR(skb)))
+ return -ENOMEM;
error = -EINVAL;
@@ -1118,8 +1120,8 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe)
rpp->len += skb->len;
if (stat & SAR_RSQE_EPDU) {
+ unsigned int len, truesize;
unsigned char *l1l2;
- unsigned int len;
l1l2 = (unsigned char *) ((unsigned long) skb->data + skb->len - 6);
@@ -1189,14 +1191,15 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe)
ATM_SKB(skb)->vcc = vcc;
__net_timestamp(skb);
+ truesize = skb->truesize;
vcc->push(vcc, skb);
atomic_inc(&vcc->stats->rx);
- if (skb->truesize > SAR_FB_SIZE_3)
+ if (truesize > SAR_FB_SIZE_3)
add_rx_skb(card, 3, SAR_FB_SIZE_3, 1);
- else if (skb->truesize > SAR_FB_SIZE_2)
+ else if (truesize > SAR_FB_SIZE_2)
add_rx_skb(card, 2, SAR_FB_SIZE_2, 1);
- else if (skb->truesize > SAR_FB_SIZE_1)
+ else if (truesize > SAR_FB_SIZE_1)
add_rx_skb(card, 1, SAR_FB_SIZE_1, 1);
else
add_rx_skb(card, 0, SAR_FB_SIZE_0, 1);
@@ -1530,7 +1533,7 @@ idt77252_tx(struct idt77252_dev *card)
static void
tst_timer(struct timer_list *t)
{
- struct idt77252_dev *card = from_timer(card, t, tst_timer);
+ struct idt77252_dev *card = timer_container_of(card, t, tst_timer);
unsigned long base, idle, jump;
unsigned long flags;
u32 pc;
@@ -1856,6 +1859,8 @@ add_rx_skb(struct idt77252_dev *card, int queue,
paddr = dma_map_single(&card->pcidev->dev, skb->data,
skb_end_pointer(skb) - skb->data,
DMA_FROM_DEVICE);
+ if (dma_mapping_error(&card->pcidev->dev, paddr))
+ goto outpoolrm;
IDT77252_PRV_PADDR(skb) = paddr;
if (push_rx_skb(card, skb, queue)) {
@@ -1870,6 +1875,7 @@ outunmap:
dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
skb_end_pointer(skb) - skb->data, DMA_FROM_DEVICE);
+outpoolrm:
handle = IDT77252_PRV_POOL(skb);
card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL;
@@ -2069,7 +2075,7 @@ idt77252_rate_logindex(struct idt77252_dev *card, int pcr)
static void
idt77252_est_timer(struct timer_list *t)
{
- struct rate_estimator *est = from_timer(est, t, timer);
+ struct rate_estimator *est = timer_container_of(est, t, timer);
struct vc_map *vc = est->vc;
struct idt77252_dev *card = vc->card;
unsigned long flags;
@@ -2213,7 +2219,7 @@ idt77252_init_ubr(struct idt77252_dev *card, struct vc_map *vc,
}
spin_unlock_irqrestore(&vc->lock, flags);
if (est) {
- del_timer_sync(&est->timer);
+ timer_shutdown_sync(&est->timer);
kfree(est);
}
@@ -2530,7 +2536,7 @@ done:
vc->tx_vcc = NULL;
if (vc->estimator) {
- del_timer(&vc->estimator->timer);
+ timer_shutdown(&vc->estimator->timer);
kfree(vc->estimator);
vc->estimator = NULL;
}
@@ -2909,6 +2915,7 @@ close_card_oam(struct idt77252_dev *card)
recycle_rx_pool_skb(card, &vc->rcv.rx_pool);
}
+ kfree(vc);
}
}
}
@@ -2929,6 +2936,8 @@ open_card_ubr0(struct idt77252_dev *card)
vc->scq = alloc_scq(card, vc->class);
if (!vc->scq) {
printk("%s: can't get SCQ.\n", card->name);
+ kfree(card->vcs[0]);
+ card->vcs[0] = NULL;
return -ENOMEM;
}
@@ -2952,6 +2961,15 @@ open_card_ubr0(struct idt77252_dev *card)
return 0;
}
+static void
+close_card_ubr0(struct idt77252_dev *card)
+{
+ struct vc_map *vc = card->vcs[0];
+
+ free_scq(card, vc->scq);
+ kfree(vc);
+}
+
static int
idt77252_dev_open(struct idt77252_dev *card)
{
@@ -3001,6 +3019,7 @@ static void idt77252_dev_close(struct atm_dev *dev)
struct idt77252_dev *card = dev->dev_data;
u32 conf;
+ close_card_ubr0(card);
close_card_oam(card);
conf = SAR_CFG_RXPTH | /* enable receive path */
@@ -3752,7 +3771,7 @@ static void __exit idt77252_exit(void)
card = idt77252_chain;
dev = card->atmdev;
idt77252_chain = card->next;
- del_timer_sync(&card->tst_timer);
+ timer_shutdown_sync(&card->tst_timer);
if (dev->phy->stop)
dev->phy->stop(dev);
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 324148686953..301e697e22ad 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -90,6 +90,7 @@ module_param(IA_RX_BUF, int, 0);
module_param(IA_RX_BUF_SZ, int, 0);
module_param(IADebugFlag, uint, 0644);
+MODULE_DESCRIPTION("Driver for Interphase ATM PCI NICs");
MODULE_LICENSE("GPL");
/**************************** IA_LIB **********************************/
@@ -2291,19 +2292,21 @@ static int get_esi(struct atm_dev *dev)
static int reset_sar(struct atm_dev *dev)
{
IADEV *iadev;
- int i, error = 1;
+ int i, error;
unsigned int pci[64];
iadev = INPH_IA_DEV(dev);
- for(i=0; i<64; i++)
- if ((error = pci_read_config_dword(iadev->pci,
- i*4, &pci[i])) != PCIBIOS_SUCCESSFUL)
- return error;
+ for (i = 0; i < 64; i++) {
+ error = pci_read_config_dword(iadev->pci, i * 4, &pci[i]);
+ if (error != PCIBIOS_SUCCESSFUL)
+ return error;
+ }
writel(0, iadev->reg+IPHASE5575_EXT_RESET);
- for(i=0; i<64; i++)
- if ((error = pci_write_config_dword(iadev->pci,
- i*4, pci[i])) != PCIBIOS_SUCCESSFUL)
- return error;
+ for (i = 0; i < 64; i++) {
+ error = pci_write_config_dword(iadev->pci, i * 4, pci[i]);
+ if (error != PCIBIOS_SUCCESSFUL)
+ return error;
+ }
udelay(5);
return 0;
}
@@ -3280,7 +3283,7 @@ static void __exit ia_module_exit(void)
{
pci_unregister_driver(&ia_driver);
- del_timer_sync(&ia_timer);
+ timer_delete_sync(&ia_timer);
}
module_init(ia_module_init);
diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c
index 32d7aa141d96..0dfa2cdc897c 100644
--- a/drivers/atm/lanai.c
+++ b/drivers/atm/lanai.c
@@ -755,7 +755,7 @@ static void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc)
/* Shutdown transmitting on card.
* Unfortunately the lanai needs us to wait until all the data
* drains out of the buffer before we can dealloc it, so this
- * can take awhile -- up to 370ms for a full 128KB buffer
+ * can take a while -- up to 370ms for a full 128KB buffer
* assuming everone else is quiet. In theory the time is
* boundless if there's a CBR VCC holding things up.
*/
@@ -1758,7 +1758,7 @@ static void iter_dequeue(struct lanai_dev *lanai, vci_t vci)
static void lanai_timed_poll(struct timer_list *t)
{
- struct lanai_dev *lanai = from_timer(lanai, t, timer);
+ struct lanai_dev *lanai = timer_container_of(lanai, t, timer);
#ifndef DEBUG_RW
unsigned long flags;
#ifdef USE_POWERDOWN
@@ -1792,7 +1792,7 @@ static inline void lanai_timed_poll_start(struct lanai_dev *lanai)
static inline void lanai_timed_poll_stop(struct lanai_dev *lanai)
{
- del_timer_sync(&lanai->timer);
+ timer_delete_sync(&lanai->timer);
}
/* -------------------- INTERRUPT SERVICE: */
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index 1a50de39f5b5..45952cfea06b 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -171,6 +171,7 @@ static const struct atmdev_ops atm_ops = {
static struct timer_list ns_timer;
static char *mac[NS_MAX_CARDS];
module_param_array(mac, charp, NULL, 0);
+MODULE_DESCRIPTION("ATM NIC driver for IDT 77201/77211 \"NICStAR\" and Fore ForeRunnerLE.");
MODULE_LICENSE("GPL");
/* Functions */
@@ -299,7 +300,7 @@ static void __exit nicstar_cleanup(void)
{
XPRINTK("nicstar: nicstar_cleanup() called.\n");
- del_timer_sync(&ns_timer);
+ timer_delete_sync(&ns_timer);
pci_unregister_driver(&nicstar_driver);
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index 94fbc3abe60e..d3c30a28c410 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -449,9 +449,9 @@ static ssize_t console_show(struct device *dev, struct device_attribute *attr,
struct sk_buff *skb;
unsigned int len;
- spin_lock(&card->cli_queue_lock);
+ spin_lock_bh(&card->cli_queue_lock);
skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]);
- spin_unlock(&card->cli_queue_lock);
+ spin_unlock_bh(&card->cli_queue_lock);
if(skb == NULL)
return sprintf(buf, "No data.\n");
@@ -956,14 +956,14 @@ static void pclose(struct atm_vcc *vcc)
struct pkt_hdr *header;
/* Remove any yet-to-be-transmitted packets from the pending queue */
- spin_lock(&card->tx_queue_lock);
+ spin_lock_bh(&card->tx_queue_lock);
skb_queue_walk_safe(&card->tx_queue[port], skb, tmpskb) {
if (SKB_CB(skb)->vcc == vcc) {
skb_unlink(skb, &card->tx_queue[port]);
solos_pop(vcc, skb);
}
}
- spin_unlock(&card->tx_queue_lock);
+ spin_unlock_bh(&card->tx_queue_lock);
skb = alloc_skb(sizeof(*header), GFP_KERNEL);
if (!skb) {
diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c
index 21e5acc766b8..7d0fa729c2fe 100644
--- a/drivers/atm/suni.c
+++ b/drivers/atm/suni.c
@@ -347,7 +347,7 @@ static int suni_stop(struct atm_dev *dev)
for (walk = &sunis; *walk != PRIV(dev);
walk = &PRIV((*walk)->dev)->next);
*walk = PRIV((*walk)->dev)->next;
- if (!sunis) del_timer_sync(&poll_timer);
+ if (!sunis) timer_delete_sync(&poll_timer);
spin_unlock_irqrestore(&sunis_lock,flags);
kfree(PRIV(dev));
@@ -387,4 +387,5 @@ int suni_init(struct atm_dev *dev)
EXPORT_SYMBOL(suni_init);
+MODULE_DESCRIPTION("S/UNI PHY driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 64012cda4d12..bedc6133f970 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -16,6 +16,9 @@ menuconfig AUXDISPLAY
if AUXDISPLAY
+#
+# Character LCD section
+#
config CHARLCD
tristate "Character LCD core support" if COMPILE_TEST
help
@@ -25,12 +28,6 @@ config CHARLCD
This is some character LCD core interface that multiple drivers can
use.
-config LINEDISP
- tristate "Character line display core support" if COMPILE_TEST
- help
- This is the core support for single-line character displays, to be
- selected by drivers that use it.
-
config HD44780_COMMON
tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST
select CHARLCD
@@ -52,137 +49,6 @@ config HD44780
kernel and started at boot.
If you don't understand what all this is about, say N.
-config KS0108
- tristate "KS0108 LCD Controller"
- depends on PARPORT_PC
- default n
- help
- If you have a LCD controlled by one or more KS0108
- controllers, say Y. You will need also another more specific
- driver for your LCD.
-
- Depends on Parallel Port support. If you say Y at
- parport, you will be able to compile this as a module (M)
- and built-in as well (Y).
-
- To compile this as a module, choose M here:
- the module will be called ks0108.
-
- If unsure, say N.
-
-config KS0108_PORT
- hex "Parallel port where the LCD is connected"
- depends on KS0108
- default 0x378
- help
- The address of the parallel port where the LCD is connected.
-
- The first standard parallel port address is 0x378.
- The second standard parallel port address is 0x278.
- The third standard parallel port address is 0x3BC.
-
- You can specify a different address if you need.
-
- If you don't know what I'm talking about, load the parport module,
- and execute "dmesg" or "cat /proc/ioports". You can see there how
- many parallel ports are present and which address each one has.
-
- Usually you only need to use 0x378.
-
- If you compile this as a module, you can still override this
- using the module parameters.
-
-config KS0108_DELAY
- int "Delay between each control writing (microseconds)"
- depends on KS0108
- default "2"
- help
- Amount of time the ks0108 should wait between each control write
- to the parallel port.
-
- If your LCD seems to miss random writings, increment this.
-
- If you don't know what I'm talking about, ignore it.
-
- If you compile this as a module, you can still override this
- value using the module parameters.
-
-config CFAG12864B
- tristate "CFAG12864B LCD"
- depends on X86
- depends on FB
- depends on KS0108
- select FB_SYS_FILLRECT
- select FB_SYS_COPYAREA
- select FB_SYS_IMAGEBLIT
- select FB_SYS_FOPS
- default n
- help
- If you have a Crystalfontz 128x64 2-color LCD, cfag12864b Series,
- say Y. You also need the ks0108 LCD Controller driver.
-
- For help about how to wire your LCD to the parallel port,
- check Documentation/admin-guide/auxdisplay/cfag12864b.rst
-
- Depends on the x86 arch and the framebuffer support.
-
- The LCD framebuffer driver can be attached to a console.
- It will work fine. However, you can't attach it to the fbdev driver
- of the xorg server.
-
- To compile this as a module, choose M here:
- the modules will be called cfag12864b and cfag12864bfb.
-
- If unsure, say N.
-
-config CFAG12864B_RATE
- int "Refresh rate (hertz)"
- depends on CFAG12864B
- default "20"
- help
- Refresh rate of the LCD.
-
- As the LCD is not memory mapped, the driver has to make the work by
- software. This means you should be careful setting this value higher.
- If your CPUs are really slow or you feel the system is slowed down,
- decrease the value.
-
- Be careful modifying this value to a very high value:
- You can freeze the computer, or the LCD maybe can't draw as fast as you
- are requesting.
-
- If you don't know what I'm talking about, ignore it.
-
- If you compile this as a module, you can still override this
- value using the module parameters.
-
-config IMG_ASCII_LCD
- tristate "Imagination Technologies ASCII LCD Display"
- depends on HAS_IOMEM
- default y if MIPS_MALTA
- select MFD_SYSCON
- select LINEDISP
- help
- Enable this to support the simple ASCII LCD displays found on
- development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3
- from Imagination Technologies.
-
-config HT16K33
- tristate "Holtek Ht16K33 LED controller with keyscan"
- depends on FB && I2C && INPUT
- select FB_SYS_FOPS
- select FB_SYS_FILLRECT
- select FB_SYS_COPYAREA
- select FB_SYS_IMAGEBLIT
- select INPUT_MATRIXKMAP
- select FB_BACKLIGHT
- select NEW_LEDS
- select LEDS_CLASS
- select LINEDISP
- help
- Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
- LED controller driver with keyscan.
-
config LCD2S
tristate "lcd2s 20x4 character display over I2C console"
depends on I2C
@@ -193,16 +59,6 @@ config LCD2S
is a simple single color character display. You have to connect it
to an I2C bus.
-config ARM_CHARLCD
- bool "ARM Ltd. Character LCD Driver"
- depends on PLAT_VERSATILE
- help
- This is a driver for the character LCD found on the ARM Ltd.
- Versatile and RealView Platform Baseboards. It doesn't do
- very much more than display the text "ARM Linux" on the first
- line and the Linux version on the second line, but that's
- still useful.
-
menuconfig PARPORT_PANEL
tristate "Parallel port LCD/Keypad Panel support"
depends on PARPORT
@@ -460,8 +316,7 @@ endif # PARPORT_PANEL
config PANEL_CHANGE_MESSAGE
bool "Change LCD initialization message ?"
- depends on CHARLCD
- default "n"
+ depends on CHARLCD || LINEDISP
help
This allows you to replace the boot message indicating the kernel version
and the driver version with a custom message. This is useful on appliances
@@ -510,8 +365,185 @@ choice
endchoice
+#
+# Samsung KS0108 LCD controller section
+#
+config KS0108
+ tristate "KS0108 LCD Controller"
+ depends on PARPORT_PC
+ help
+ If you have a LCD controlled by one or more KS0108
+ controllers, say Y. You will need also another more specific
+ driver for your LCD.
+
+ Depends on Parallel Port support. If you say Y at
+ parport, you will be able to compile this as a module (M)
+ and built-in as well (Y).
+
+ To compile this as a module, choose M here:
+ the module will be called ks0108.
+
+ If unsure, say N.
+
+config KS0108_PORT
+ hex "Parallel port where the LCD is connected"
+ depends on KS0108
+ default 0x378
+ help
+ The address of the parallel port where the LCD is connected.
+
+ The first standard parallel port address is 0x378.
+ The second standard parallel port address is 0x278.
+ The third standard parallel port address is 0x3BC.
+
+ You can specify a different address if you need.
+
+ If you don't know what I'm talking about, load the parport module,
+ and execute "dmesg" or "cat /proc/ioports". You can see there how
+ many parallel ports are present and which address each one has.
+
+ Usually you only need to use 0x378.
+
+ If you compile this as a module, you can still override this
+ using the module parameters.
+
+config KS0108_DELAY
+ int "Delay between each control writing (microseconds)"
+ depends on KS0108
+ default "2"
+ help
+ Amount of time the ks0108 should wait between each control write
+ to the parallel port.
+
+ If your LCD seems to miss random writings, increment this.
+
+ If you don't know what I'm talking about, ignore it.
+
+ If you compile this as a module, you can still override this
+ value using the module parameters.
+
+config CFAG12864B
+ tristate "CFAG12864B LCD"
+ depends on X86
+ depends on FB
+ depends on KS0108
+ select FB_SYSMEM_HELPERS
+ help
+ If you have a Crystalfontz 128x64 2-color LCD, cfag12864b Series,
+ say Y. You also need the ks0108 LCD Controller driver.
+
+ For help about how to wire your LCD to the parallel port,
+ check Documentation/admin-guide/auxdisplay/cfag12864b.rst
+
+ Depends on the x86 arch and the framebuffer support.
+
+ The LCD framebuffer driver can be attached to a console.
+ It will work fine. However, you can't attach it to the fbdev driver
+ of the xorg server.
+
+ To compile this as a module, choose M here:
+ the modules will be called cfag12864b and cfag12864bfb.
+
+ If unsure, say N.
+
+config CFAG12864B_RATE
+ int "Refresh rate (hertz)"
+ depends on CFAG12864B
+ default "20"
+ help
+ Refresh rate of the LCD.
+
+ As the LCD is not memory mapped, the driver has to make the work by
+ software. This means you should be careful setting this value higher.
+ If your CPUs are really slow or you feel the system is slowed down,
+ decrease the value.
+
+ Be careful modifying this value to a very high value:
+ You can freeze the computer, or the LCD maybe can't draw as fast as you
+ are requesting.
+
+ If you don't know what I'm talking about, ignore it.
+
+ If you compile this as a module, you can still override this
+ value using the module parameters.
+
+#
+# Single character line display section
+#
+config LINEDISP
+ tristate "Character line display core support" if COMPILE_TEST
+ help
+ This is the core support for single-line character displays, to be
+ selected by drivers that use it.
+
+config IMG_ASCII_LCD
+ tristate "Imagination Technologies ASCII LCD Display"
+ depends on HAS_IOMEM
+ default y if MIPS_MALTA
+ select MFD_SYSCON
+ select LINEDISP
+ help
+ Enable this to support the simple ASCII LCD displays found on
+ development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3
+ from Imagination Technologies.
+
+config HT16K33
+ tristate "Holtek Ht16K33 LED controller with keyscan"
+ depends on FB && I2C && INPUT && BACKLIGHT_CLASS_DEVICE
+ select FB_SYSMEM_HELPERS
+ select INPUT_MATRIXKMAP
+ select FB_BACKLIGHT
+ select NEW_LEDS
+ select LEDS_CLASS
+ select LINEDISP
+ help
+ Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
+ LED controller driver with keyscan.
+
+config MAX6959
+ tristate "Maxim MAX6958/6959 7-segment LED controller"
+ depends on I2C
+ select BITREVERSE
+ select REGMAP_I2C
+ select LINEDISP
+ help
+ If you say yes here you get support for the following Maxim chips
+ (I2C 7-segment LED display controller):
+ - MAX6958
+ - MAX6959 (input support)
+
+ This driver can also be built as a module. If so, the module
+ will be called max6959.
+
+config SEG_LED_GPIO
+ tristate "Generic 7-segment LED display"
+ depends on GPIOLIB || COMPILE_TEST
+ select LINEDISP
+ help
+ This driver supports a generic 7-segment LED display made up
+ of GPIO pins connected to the individual segments.
+
+ This driver can also be built as a module. If so, the module
+ will be called seg-led-gpio.
+
+#
+# Character LCD with non-conforming interface section
+#
+config ARM_CHARLCD
+ bool "ARM Ltd. Character LCD Driver"
+ depends on PLAT_VERSATILE
+ help
+ This is a driver for the character LCD found on the ARM Ltd.
+ Versatile and RealView Platform Baseboards. It doesn't do
+ very much more than display the text "ARM Linux" on the first
+ line and the Linux version on the second line, but that's
+ still useful.
+
endif # AUXDISPLAY
+#
+# Deprecated options
+#
config PANEL
tristate "Parallel port LCD/Keypad Panel support (OLD OPTION)"
depends on PARPORT
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index 6968ed4d3f0a..f5c13ed1cd4f 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -3,14 +3,16 @@
# Makefile for the kernel auxiliary displays device drivers.
#
-obj-$(CONFIG_CHARLCD) += charlcd.o
-obj-$(CONFIG_HD44780_COMMON) += hd44780_common.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
-obj-$(CONFIG_KS0108) += ks0108.o
obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
-obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
+obj-$(CONFIG_CHARLCD) += charlcd.o
+obj-$(CONFIG_HD44780_COMMON) += hd44780_common.o
obj-$(CONFIG_HD44780) += hd44780.o
obj-$(CONFIG_HT16K33) += ht16k33.o
-obj-$(CONFIG_PARPORT_PANEL) += panel.o
+obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
+obj-$(CONFIG_KS0108) += ks0108.o
obj-$(CONFIG_LCD2S) += lcd2s.o
obj-$(CONFIG_LINEDISP) += line-display.o
+obj-$(CONFIG_MAX6959) += max6959.o
+obj-$(CONFIG_PARPORT_PANEL) += panel.o
+obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o
diff --git a/drivers/auxdisplay/arm-charlcd.c b/drivers/auxdisplay/arm-charlcd.c
index 0b1c99cca733..a7eae99a48f7 100644
--- a/drivers/auxdisplay/arm-charlcd.c
+++ b/drivers/auxdisplay/arm-charlcd.c
@@ -270,7 +270,7 @@ static int __init charlcd_probe(struct platform_device *pdev)
struct charlcd *lcd;
struct resource *res;
- lcd = kzalloc(sizeof(struct charlcd), GFP_KERNEL);
+ lcd = kzalloc(sizeof(*lcd), GFP_KERNEL);
if (!lcd)
return -ENOMEM;
diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c
index 6526aa51fb1d..e1a94ae3eb0c 100644
--- a/drivers/auxdisplay/cfag12864b.c
+++ b/drivers/auxdisplay/cfag12864b.c
@@ -37,11 +37,6 @@ module_param(cfag12864b_rate, uint, 0444);
MODULE_PARM_DESC(cfag12864b_rate,
"Refresh rate (hertz)");
-unsigned int cfag12864b_getrate(void)
-{
- return cfag12864b_rate;
-}
-
/*
* cfag12864b Commands
*
@@ -249,11 +244,6 @@ void cfag12864b_disable(void)
mutex_unlock(&cfag12864b_mutex);
}
-unsigned char cfag12864b_isenabled(void)
-{
- return cfag12864b_updating;
-}
-
static void cfag12864b_update(struct work_struct *work)
{
unsigned char c;
@@ -293,10 +283,8 @@ static void cfag12864b_update(struct work_struct *work)
*/
EXPORT_SYMBOL_GPL(cfag12864b_buffer);
-EXPORT_SYMBOL_GPL(cfag12864b_getrate);
EXPORT_SYMBOL_GPL(cfag12864b_enable);
EXPORT_SYMBOL_GPL(cfag12864b_disable);
-EXPORT_SYMBOL_GPL(cfag12864b_isenabled);
/*
* Is the module inited?
diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c
index 0df474506fb9..24baf6b2c587 100644
--- a/drivers/auxdisplay/cfag12864bfb.c
+++ b/drivers/auxdisplay/cfag12864bfb.c
@@ -51,16 +51,15 @@ static int cfag12864bfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
struct page *pages = virt_to_page(cfag12864b_buffer);
+ vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+
return vm_map_pages_zero(vma, &pages, 1);
}
static const struct fb_ops cfag12864bfb_ops = {
.owner = THIS_MODULE,
- .fb_read = fb_sys_read,
- .fb_write = fb_sys_write,
- .fb_fillrect = sys_fillrect,
- .fb_copyarea = sys_copyarea,
- .fb_imageblit = sys_imageblit,
+ __FB_DEFAULT_SYSMEM_OPS_RDWR,
+ __FB_DEFAULT_SYSMEM_OPS_DRAW,
.fb_mmap = cfag12864bfb_mmap,
};
@@ -72,14 +71,14 @@ static int cfag12864bfb_probe(struct platform_device *device)
if (!info)
goto none;
- info->screen_base = (char __iomem *) cfag12864b_buffer;
+ info->flags = FBINFO_VIRTFB;
+ info->screen_buffer = cfag12864b_buffer;
info->screen_size = CFAG12864B_SIZE;
info->fbops = &cfag12864bfb_ops;
info->fix = cfag12864bfb_fix;
info->var = cfag12864bfb_var;
info->pseudo_palette = NULL;
info->par = NULL;
- info->flags = FBINFO_FLAG_DEFAULT;
if (register_framebuffer(info) < 0)
goto fballoced;
@@ -97,7 +96,7 @@ none:
return ret;
}
-static int cfag12864bfb_remove(struct platform_device *device)
+static void cfag12864bfb_remove(struct platform_device *device)
{
struct fb_info *info = platform_get_drvdata(device);
@@ -105,8 +104,6 @@ static int cfag12864bfb_remove(struct platform_device *device)
unregister_framebuffer(info);
framebuffer_release(info);
}
-
- return 0;
}
static struct platform_driver cfag12864bfb_driver = {
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 6d309e4971b6..09020bb8ad15 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -17,7 +17,9 @@
#include <linux/uaccess.h>
#include <linux/workqueue.h>
+#ifndef CONFIG_PANEL_BOOT_MESSAGE
#include <generated/utsrelease.h>
+#endif
#include "charlcd.h"
@@ -524,7 +526,6 @@ static const struct file_operations charlcd_fops = {
.write = charlcd_write,
.open = charlcd_open,
.release = charlcd_release,
- .llseek = no_llseek,
};
static struct miscdevice charlcd_dev = {
@@ -594,18 +595,19 @@ static int charlcd_init(struct charlcd *lcd)
return 0;
}
-struct charlcd *charlcd_alloc(void)
+struct charlcd *charlcd_alloc(unsigned int drvdata_size)
{
struct charlcd_priv *priv;
struct charlcd *lcd;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
if (!priv)
return NULL;
priv->esc_seq.len = -1;
lcd = &priv->lcd;
+ lcd->drvdata = priv->drvdata;
return lcd;
}
@@ -678,4 +680,5 @@ int charlcd_unregister(struct charlcd *lcd)
}
EXPORT_SYMBOL_GPL(charlcd_unregister);
+MODULE_DESCRIPTION("Character LCD core support");
MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index eed80063a6d2..d10b89740bca 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -36,6 +36,8 @@ enum charlcd_lines {
CHARLCD_LINES_2,
};
+struct charlcd_ops;
+
struct charlcd {
const struct charlcd_ops *ops;
const unsigned char *char_conv; /* Optional */
@@ -49,7 +51,7 @@ struct charlcd {
unsigned long y;
} addr;
- void *drvdata;
+ void *drvdata; /* Set by charlcd_alloc() */
};
/**
@@ -93,7 +95,8 @@ struct charlcd_ops {
};
void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
-struct charlcd *charlcd_alloc(void);
+
+struct charlcd *charlcd_alloc(unsigned int drvdata_size);
void charlcd_free(struct charlcd *lcd);
int charlcd_register(struct charlcd *lcd);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 8b2a0eb3f32a..cef42656c4b0 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -222,20 +222,17 @@ static int hd44780_probe(struct platform_device *pdev)
return -EINVAL;
}
- hdc = hd44780_common_alloc();
- if (!hdc)
- return -ENOMEM;
-
- lcd = charlcd_alloc();
+ lcd = hd44780_common_alloc();
if (!lcd)
- goto fail1;
+ return -ENOMEM;
- hd = kzalloc(sizeof(struct hd44780), GFP_KERNEL);
+ hd = kzalloc(sizeof(*hd), GFP_KERNEL);
if (!hd)
goto fail2;
+ hdc = lcd->drvdata;
hdc->hd44780 = hd;
- lcd->drvdata = hdc;
+
for (i = 0; i < ifwidth; i++) {
hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,
GPIOD_OUT_LOW);
@@ -313,21 +310,18 @@ static int hd44780_probe(struct platform_device *pdev)
fail3:
kfree(hd);
fail2:
- kfree(lcd);
-fail1:
- kfree(hdc);
+ hd44780_common_free(lcd);
return ret;
}
-static int hd44780_remove(struct platform_device *pdev)
+static void hd44780_remove(struct platform_device *pdev)
{
struct charlcd *lcd = platform_get_drvdata(pdev);
+ struct hd44780_common *hdc = lcd->drvdata;
charlcd_unregister(lcd);
- kfree(lcd->drvdata);
-
- kfree(lcd);
- return 0;
+ kfree(hdc->hd44780);
+ hd44780_common_free(lcd);
}
static const struct of_device_id hd44780_of_match[] = {
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 3934c2eebf33..1792fe2a4460 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -82,7 +82,15 @@ int hd44780_common_clear_display(struct charlcd *lcd)
hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
/* datasheet says to wait 1,64 milliseconds */
long_sleep(2);
- return 0;
+
+ /*
+ * The Hitachi HD44780 controller (and compatible ones) reset the DDRAM
+ * address when executing the DISPLAY_CLEAR command, thus the
+ * following call is not required. However, other controllers do not
+ * (e.g. NewHaven NHD-0220DZW-AG5), thus move the cursor to home
+ * unconditionally to support both.
+ */
+ return hd44780_common_home(lcd);
}
EXPORT_SYMBOL_GPL(hd44780_common_clear_display);
@@ -343,19 +351,28 @@ int hd44780_common_redefine_char(struct charlcd *lcd, char *esc)
}
EXPORT_SYMBOL_GPL(hd44780_common_redefine_char);
-struct hd44780_common *hd44780_common_alloc(void)
+struct charlcd *hd44780_common_alloc(void)
{
- struct hd44780_common *hd;
+ struct hd44780_common *hdc;
+ struct charlcd *lcd;
- hd = kzalloc(sizeof(*hd), GFP_KERNEL);
- if (!hd)
+ lcd = charlcd_alloc(sizeof(*hdc));
+ if (!lcd)
return NULL;
- hd->ifwidth = 8;
- hd->bwidth = DEFAULT_LCD_BWIDTH;
- hd->hwidth = DEFAULT_LCD_HWIDTH;
- return hd;
+ hdc = lcd->drvdata;
+ hdc->ifwidth = 8;
+ hdc->bwidth = DEFAULT_LCD_BWIDTH;
+ hdc->hwidth = DEFAULT_LCD_HWIDTH;
+ return lcd;
}
EXPORT_SYMBOL_GPL(hd44780_common_alloc);
+void hd44780_common_free(struct charlcd *lcd)
+{
+ charlcd_free(lcd);
+}
+EXPORT_SYMBOL_GPL(hd44780_common_free);
+
+MODULE_DESCRIPTION("Common functions for HD44780 (and compatibles) LCD displays");
MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index a16aa8c29c99..4c87f55722b6 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -30,4 +30,6 @@ int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size);
int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines);
int hd44780_common_redefine_char(struct charlcd *lcd, char *esc);
-struct hd44780_common *hd44780_common_alloc(void);
+
+struct charlcd *hd44780_common_alloc(void);
+void hd44780_common_free(struct charlcd *lcd);
diff --git a/drivers/auxdisplay/ht16k33.c b/drivers/auxdisplay/ht16k33.c
index 4fab3b2c7023..0b8ba754b343 100644
--- a/drivers/auxdisplay/ht16k33.c
+++ b/drivers/auxdisplay/ht16k33.c
@@ -15,6 +15,7 @@
#include <linux/property.h>
#include <linux/fb.h>
#include <linux/backlight.h>
+#include <linux/container_of.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/leds.h>
@@ -24,7 +25,7 @@
#include <linux/map_to_7segment.h>
#include <linux/map_to_14segment.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "line-display.h"
@@ -85,16 +86,6 @@ struct ht16k33_fbdev {
uint8_t *cache;
};
-struct ht16k33_seg {
- struct linedisp linedisp;
- union {
- struct seg7_conversion_map seg7;
- struct seg14_conversion_map seg14;
- } map;
- unsigned int map_size;
- char curr[4];
-};
-
struct ht16k33_priv {
struct i2c_client *client;
struct delayed_work work;
@@ -102,12 +93,21 @@ struct ht16k33_priv {
struct ht16k33_keypad keypad;
union {
struct ht16k33_fbdev fbdev;
- struct ht16k33_seg seg;
+ struct linedisp linedisp;
};
enum display_type type;
uint8_t blink;
};
+#define ht16k33_work_to_priv(p) \
+ container_of(p, struct ht16k33_priv, work.work)
+
+#define ht16k33_led_to_priv(p) \
+ container_of(p, struct ht16k33_priv, led)
+
+#define ht16k33_linedisp_to_priv(p) \
+ container_of(p, struct ht16k33_priv, linedisp)
+
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
.id = DRIVER_NAME,
.type = FB_TYPE_PACKED_PIXELS,
@@ -135,33 +135,6 @@ static const struct fb_var_screeninfo ht16k33_fb_var = {
.vmode = FB_VMODE_NONINTERLACED,
};
-static const SEG7_DEFAULT_MAP(initial_map_seg7);
-static const SEG14_DEFAULT_MAP(initial_map_seg14);
-
-static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct ht16k33_priv *priv = dev_get_drvdata(dev);
-
- memcpy(buf, &priv->seg.map, priv->seg.map_size);
- return priv->seg.map_size;
-}
-
-static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t cnt)
-{
- struct ht16k33_priv *priv = dev_get_drvdata(dev);
-
- if (cnt != priv->seg.map_size)
- return -EINVAL;
-
- memcpy(&priv->seg.map, buf, cnt);
- return cnt;
-}
-
-static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store);
-static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
-
static int ht16k33_display_on(struct ht16k33_priv *priv)
{
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON | priv->blink;
@@ -195,8 +168,7 @@ static int ht16k33_brightness_set(struct ht16k33_priv *priv,
static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
- struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv,
- led);
+ struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev);
return ht16k33_brightness_set(priv, brightness);
}
@@ -204,8 +176,7 @@ static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev,
static int ht16k33_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
- struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv,
- led);
+ struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev);
unsigned int delay;
uint8_t blink;
int err;
@@ -247,8 +218,7 @@ static void ht16k33_fb_queue(struct ht16k33_priv *priv)
*/
static void ht16k33_fb_update(struct work_struct *work)
{
- struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
- work.work);
+ struct ht16k33_priv *priv = ht16k33_work_to_priv(work);
struct ht16k33_fbdev *fbdev = &priv->fbdev;
uint8_t *p1, *p2;
@@ -314,27 +284,14 @@ static int ht16k33_initialize(struct ht16k33_priv *priv)
static int ht16k33_bl_update_status(struct backlight_device *bl)
{
- int brightness = bl->props.brightness;
+ const int brightness = backlight_get_brightness(bl);
struct ht16k33_priv *priv = bl_get_data(bl);
- if (bl->props.power != FB_BLANK_UNBLANK ||
- bl->props.fb_blank != FB_BLANK_UNBLANK ||
- bl->props.state & BL_CORE_FBBLANK)
- brightness = 0;
-
return ht16k33_brightness_set(priv, brightness);
}
-static int ht16k33_bl_check_fb(struct backlight_device *bl, struct fb_info *fi)
-{
- struct ht16k33_priv *priv = bl_get_data(bl);
-
- return (fi == NULL) || (fi->par == priv);
-}
-
static const struct backlight_ops ht16k33_bl_ops = {
.update_status = ht16k33_bl_update_status,
- .check_fb = ht16k33_bl_check_fb,
};
/*
@@ -351,17 +308,16 @@ static int ht16k33_mmap(struct fb_info *info, struct vm_area_struct *vma)
struct ht16k33_priv *priv = info->par;
struct page *pages = virt_to_page(priv->fbdev.buffer);
+ vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+
return vm_map_pages_zero(vma, &pages, 1);
}
static const struct fb_ops ht16k33_fb_ops = {
.owner = THIS_MODULE,
- .fb_read = fb_sys_read,
- .fb_write = fb_sys_write,
+ __FB_DEFAULT_SYSMEM_OPS_RDWR,
.fb_blank = ht16k33_blank,
- .fb_fillrect = sys_fillrect,
- .fb_copyarea = sys_copyarea,
- .fb_imageblit = sys_imageblit,
+ __FB_DEFAULT_SYSMEM_OPS_DRAW,
.fb_mmap = ht16k33_mmap,
};
@@ -441,51 +397,71 @@ static void ht16k33_keypad_stop(struct input_dev *dev)
disable_irq(keypad->client->irq);
}
-static void ht16k33_linedisp_update(struct linedisp *linedisp)
-{
- struct ht16k33_priv *priv = container_of(linedisp, struct ht16k33_priv,
- seg.linedisp);
-
- schedule_delayed_work(&priv->work, 0);
-}
-
static void ht16k33_seg7_update(struct work_struct *work)
{
- struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
- work.work);
- struct ht16k33_seg *seg = &priv->seg;
- char *s = seg->curr;
+ struct ht16k33_priv *priv = ht16k33_work_to_priv(work);
+ struct linedisp_map *map = priv->linedisp.map;
+ char *s = priv->linedisp.buf;
uint8_t buf[9];
- buf[0] = map_to_seg7(&seg->map.seg7, *s++);
+ buf[0] = map_to_seg7(&map->map.seg7, *s++);
buf[1] = 0;
- buf[2] = map_to_seg7(&seg->map.seg7, *s++);
+ buf[2] = map_to_seg7(&map->map.seg7, *s++);
buf[3] = 0;
buf[4] = 0;
buf[5] = 0;
- buf[6] = map_to_seg7(&seg->map.seg7, *s++);
+ buf[6] = map_to_seg7(&map->map.seg7, *s++);
buf[7] = 0;
- buf[8] = map_to_seg7(&seg->map.seg7, *s++);
+ buf[8] = map_to_seg7(&map->map.seg7, *s++);
i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
}
static void ht16k33_seg14_update(struct work_struct *work)
{
- struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
- work.work);
- struct ht16k33_seg *seg = &priv->seg;
- char *s = seg->curr;
+ struct ht16k33_priv *priv = ht16k33_work_to_priv(work);
+ struct linedisp_map *map = priv->linedisp.map;
+ char *s = priv->linedisp.buf;
uint8_t buf[8];
- put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf);
- put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 2);
- put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 4);
- put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 6);
+ put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 0);
+ put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 2);
+ put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 4);
+ put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 6);
i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
}
+static int ht16k33_linedisp_get_map_type(struct linedisp *linedisp)
+{
+ struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp);
+
+ switch (priv->type) {
+ case DISP_QUAD_7SEG:
+ INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update);
+ return LINEDISP_MAP_SEG7;
+
+ case DISP_QUAD_14SEG:
+ INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update);
+ return LINEDISP_MAP_SEG14;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void ht16k33_linedisp_update(struct linedisp *linedisp)
+{
+ struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp);
+
+ schedule_delayed_work(&priv->work, 0);
+}
+
+static const struct linedisp_ops ht16k33_linedisp_ops = {
+ .get_map_type = ht16k33_linedisp_get_map_type,
+ .update = ht16k33_linedisp_update,
+};
+
static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,
unsigned int brightness)
{
@@ -507,6 +483,7 @@ static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,
led->max_brightness = MAX_BRIGHTNESS;
err = devm_led_classdev_register_ext(dev, led, &init_data);
+ fwnode_handle_put(init_data.fwnode);
if (err)
dev_err(dev, "Failed to register LED\n");
@@ -640,13 +617,13 @@ static int ht16k33_fbdev_probe(struct device *dev, struct ht16k33_priv *priv,
INIT_DELAYED_WORK(&priv->work, ht16k33_fb_update);
fbdev->info->fbops = &ht16k33_fb_ops;
- fbdev->info->screen_base = (char __iomem *) fbdev->buffer;
+ fbdev->info->flags |= FBINFO_VIRTFB;
+ fbdev->info->screen_buffer = fbdev->buffer;
fbdev->info->screen_size = HT16K33_FB_SIZE;
fbdev->info->fix = ht16k33_fb_fix;
fbdev->info->var = ht16k33_fb_var;
fbdev->info->bl_dev = bl;
fbdev->info->pseudo_palette = NULL;
- fbdev->info->flags = FBINFO_FLAG_DEFAULT;
fbdev->info->par = priv;
err = register_framebuffer(fbdev->info);
@@ -667,53 +644,19 @@ err_fbdev_buffer:
static int ht16k33_seg_probe(struct device *dev, struct ht16k33_priv *priv,
uint32_t brightness)
{
- struct ht16k33_seg *seg = &priv->seg;
+ struct linedisp *linedisp = &priv->linedisp;
int err;
err = ht16k33_brightness_set(priv, brightness);
if (err)
return err;
- switch (priv->type) {
- case DISP_MATRIX:
- /* not handled here */
- err = -EINVAL;
- break;
-
- case DISP_QUAD_7SEG:
- INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update);
- seg->map.seg7 = initial_map_seg7;
- seg->map_size = sizeof(seg->map.seg7);
- err = device_create_file(dev, &dev_attr_map_seg7);
- break;
-
- case DISP_QUAD_14SEG:
- INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update);
- seg->map.seg14 = initial_map_seg14;
- seg->map_size = sizeof(seg->map.seg14);
- err = device_create_file(dev, &dev_attr_map_seg14);
- break;
- }
- if (err)
- return err;
-
- err = linedisp_register(&seg->linedisp, dev, 4, seg->curr,
- ht16k33_linedisp_update);
- if (err)
- goto err_remove_map_file;
-
- return 0;
-
-err_remove_map_file:
- device_remove_file(dev, &dev_attr_map_seg7);
- device_remove_file(dev, &dev_attr_map_seg14);
- return err;
+ return linedisp_register(linedisp, dev, 4, &ht16k33_linedisp_ops);
}
static int ht16k33_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
- const struct of_device_id *id;
struct ht16k33_priv *priv;
uint32_t dft_brightness;
int err;
@@ -728,9 +671,8 @@ static int ht16k33_probe(struct i2c_client *client)
return -ENOMEM;
priv->client = client;
- id = i2c_of_match_device(dev->driver->of_match_table, client);
- if (id)
- priv->type = (uintptr_t)id->data;
+ priv->type = (uintptr_t)i2c_get_match_data(client);
+
i2c_set_clientdata(client, priv);
err = ht16k33_initialize(priv);
@@ -771,11 +713,14 @@ static int ht16k33_probe(struct i2c_client *client)
/* Segment Display */
err = ht16k33_seg_probe(dev, priv, dft_brightness);
break;
+
+ default:
+ return -EINVAL;
}
return err;
}
-static int ht16k33_remove(struct i2c_client *client)
+static void ht16k33_remove(struct i2c_client *client)
{
struct ht16k33_priv *priv = i2c_get_clientdata(client);
struct ht16k33_fbdev *fbdev = &priv->fbdev;
@@ -791,17 +736,18 @@ static int ht16k33_remove(struct i2c_client *client)
case DISP_QUAD_7SEG:
case DISP_QUAD_14SEG:
- linedisp_unregister(&priv->seg.linedisp);
- device_remove_file(&client->dev, &dev_attr_map_seg7);
- device_remove_file(&client->dev, &dev_attr_map_seg14);
+ linedisp_unregister(&priv->linedisp);
break;
- }
- return 0;
+ default:
+ break;
+ }
}
static const struct i2c_device_id ht16k33_i2c_match[] = {
- { "ht16k33", 0 },
+ { "3108", DISP_QUAD_7SEG },
+ { "3130", DISP_QUAD_14SEG },
+ { "ht16k33", DISP_MATRIX },
{ }
};
MODULE_DEVICE_TABLE(i2c, ht16k33_i2c_match);
@@ -822,7 +768,7 @@ static const struct of_device_id ht16k33_of_match[] = {
MODULE_DEVICE_TABLE(of, ht16k33_of_match);
static struct i2c_driver ht16k33_driver = {
- .probe_new = ht16k33_probe,
+ .probe = ht16k33_probe,
.remove = ht16k33_remove,
.driver = {
.name = DRIVER_NAME,
@@ -834,4 +780,5 @@ module_i2c_driver(ht16k33_driver);
MODULE_DESCRIPTION("Holtek HT16K33 driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("LINEDISP");
MODULE_AUTHOR("Robin van der Gracht <robin@protonic.nl>");
diff --git a/drivers/auxdisplay/img-ascii-lcd.c b/drivers/auxdisplay/img-ascii-lcd.c
index fa23e415f260..32e1863ef4b2 100644
--- a/drivers/auxdisplay/img-ascii-lcd.c
+++ b/drivers/auxdisplay/img-ascii-lcd.c
@@ -8,9 +8,9 @@
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -22,32 +22,28 @@ struct img_ascii_lcd_ctx;
* struct img_ascii_lcd_config - Configuration information about an LCD model
* @num_chars: the number of characters the LCD can display
* @external_regmap: true if registers are in a system controller, else false
- * @update: function called to update the LCD
+ * @ops: character line display operations
*/
struct img_ascii_lcd_config {
unsigned int num_chars;
bool external_regmap;
- void (*update)(struct linedisp *linedisp);
+ const struct linedisp_ops ops;
};
/**
* struct img_ascii_lcd_ctx - Private data structure
+ * @linedisp: line display structure
* @base: the base address of the LCD registers
* @regmap: the regmap through which LCD registers are accessed
* @offset: the offset within regmap to the start of the LCD registers
- * @cfg: pointer to the LCD model configuration
- * @linedisp: line display structure
- * @curr: the string currently displayed on the LCD
*/
struct img_ascii_lcd_ctx {
+ struct linedisp linedisp;
union {
void __iomem *base;
struct regmap *regmap;
};
u32 offset;
- const struct img_ascii_lcd_config *cfg;
- struct linedisp linedisp;
- char curr[] __aligned(8);
};
/*
@@ -61,21 +57,23 @@ static void boston_update(struct linedisp *linedisp)
ulong val;
#if BITS_PER_LONG == 64
- val = *((u64 *)&ctx->curr[0]);
+ val = *((u64 *)&linedisp->buf[0]);
__raw_writeq(val, ctx->base);
#elif BITS_PER_LONG == 32
- val = *((u32 *)&ctx->curr[0]);
+ val = *((u32 *)&linedisp->buf[0]);
__raw_writel(val, ctx->base);
- val = *((u32 *)&ctx->curr[4]);
+ val = *((u32 *)&linedisp->buf[4]);
__raw_writel(val, ctx->base + 4);
#else
# error Not 32 or 64 bit
#endif
}
-static struct img_ascii_lcd_config boston_config = {
+static const struct img_ascii_lcd_config boston_config = {
.num_chars = 8,
- .update = boston_update,
+ .ops = {
+ .update = boston_update,
+ },
};
/*
@@ -91,7 +89,7 @@ static void malta_update(struct linedisp *linedisp)
for (i = 0; i < linedisp->num_chars; i++) {
err = regmap_write(ctx->regmap,
- ctx->offset + (i * 8), ctx->curr[i]);
+ ctx->offset + (i * 8), linedisp->buf[i]);
if (err)
break;
}
@@ -100,10 +98,12 @@ static void malta_update(struct linedisp *linedisp)
pr_err_ratelimited("Failed to update LCD display: %d\n", err);
}
-static struct img_ascii_lcd_config malta_config = {
+static const struct img_ascii_lcd_config malta_config = {
.num_chars = 8,
.external_regmap = true,
- .update = malta_update,
+ .ops = {
+ .update = malta_update,
+ },
};
/*
@@ -191,7 +191,7 @@ static void sead3_update(struct linedisp *linedisp)
err = regmap_write(ctx->regmap,
ctx->offset + SEAD3_REG_LCD_DATA,
- ctx->curr[i]);
+ linedisp->buf[i]);
if (err)
break;
}
@@ -200,10 +200,12 @@ static void sead3_update(struct linedisp *linedisp)
pr_err_ratelimited("Failed to update LCD display: %d\n", err);
}
-static struct img_ascii_lcd_config sead3_config = {
+static const struct img_ascii_lcd_config sead3_config = {
.num_chars = 16,
.external_regmap = true,
- .update = sead3_update,
+ .ops = {
+ .update = sead3_update,
+ },
};
static const struct of_device_id img_ascii_lcd_matches[] = {
@@ -225,18 +227,12 @@ MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
*/
static int img_ascii_lcd_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
- const struct img_ascii_lcd_config *cfg;
struct device *dev = &pdev->dev;
+ const struct img_ascii_lcd_config *cfg = device_get_match_data(dev);
struct img_ascii_lcd_ctx *ctx;
int err;
- match = of_match_device(img_ascii_lcd_matches, dev);
- if (!match)
- return -ENODEV;
-
- cfg = match->data;
- ctx = devm_kzalloc(dev, sizeof(*ctx) + cfg->num_chars, GFP_KERNEL);
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
@@ -253,8 +249,7 @@ static int img_ascii_lcd_probe(struct platform_device *pdev)
return PTR_ERR(ctx->base);
}
- err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr,
- cfg->update);
+ err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, &cfg->ops);
if (err)
return err;
@@ -279,16 +274,13 @@ err_unregister:
*
* Remove an LCD display device, freeing private resources & ensuring that the
* driver stops using the LCD display registers.
- *
- * Return: 0
*/
-static int img_ascii_lcd_remove(struct platform_device *pdev)
+static void img_ascii_lcd_remove(struct platform_device *pdev)
{
struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
sysfs_remove_link(&pdev->dev.kobj, "message");
linedisp_unregister(&ctx->linedisp);
- return 0;
}
static struct platform_driver img_ascii_lcd_driver = {
@@ -297,10 +289,11 @@ static struct platform_driver img_ascii_lcd_driver = {
.of_match_table = img_ascii_lcd_matches,
},
.probe = img_ascii_lcd_probe,
- .remove = img_ascii_lcd_remove,
+ .remove = img_ascii_lcd_remove,
};
module_platform_driver(img_ascii_lcd_driver);
MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("LINEDISP");
diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c
index 234f9dbe6e30..51587f0fdaae 100644
--- a/drivers/auxdisplay/ks0108.c
+++ b/drivers/auxdisplay/ks0108.c
@@ -162,7 +162,6 @@ static struct parport_driver ks0108_parport_driver = {
.name = "ks0108",
.match_port = ks0108_parport_attach,
.detach = ks0108_parport_detach,
- .devmodel = true,
};
module_parport_driver(ks0108_parport_driver);
diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c
index e465108d9998..045dbef49dee 100644
--- a/drivers/auxdisplay/lcd2s.c
+++ b/drivers/auxdisplay/lcd2s.c
@@ -298,20 +298,18 @@ static int lcd2s_i2c_probe(struct i2c_client *i2c)
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
return -EIO;
- lcd2s = devm_kzalloc(&i2c->dev, sizeof(*lcd2s), GFP_KERNEL);
- if (!lcd2s)
- return -ENOMEM;
-
/* Test, if the display is responding */
err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF);
if (err < 0)
return err;
- lcd = charlcd_alloc();
+ lcd = charlcd_alloc(sizeof(*lcd2s));
if (!lcd)
return -ENOMEM;
- lcd->drvdata = lcd2s;
+ lcd->ops = &lcd2s_ops;
+
+ lcd2s = lcd->drvdata;
lcd2s->i2c = i2c;
lcd2s->charlcd = lcd;
@@ -326,8 +324,6 @@ static int lcd2s_i2c_probe(struct i2c_client *i2c)
if (err)
goto fail1;
- lcd->ops = &lcd2s_ops;
-
err = charlcd_register(lcd2s->charlcd);
if (err)
goto fail1;
@@ -340,17 +336,16 @@ fail1:
return err;
}
-static int lcd2s_i2c_remove(struct i2c_client *i2c)
+static void lcd2s_i2c_remove(struct i2c_client *i2c)
{
struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c);
charlcd_unregister(lcd2s->charlcd);
charlcd_free(lcd2s->charlcd);
- return 0;
}
static const struct i2c_device_id lcd2s_i2c_id[] = {
- { "lcd2s", 0 },
+ { "lcd2s" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id);
@@ -366,7 +361,7 @@ static struct i2c_driver lcd2s_i2c_driver = {
.name = "lcd2s",
.of_match_table = lcd2s_of_table,
},
- .probe_new = lcd2s_i2c_probe,
+ .probe = lcd2s_i2c_probe,
.remove = lcd2s_i2c_remove,
.id_table = lcd2s_i2c_id,
};
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index 03e7f104aa1a..4e22373fcc1a 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -6,22 +6,116 @@
* Author: Paul Burton <paul.burton@mips.com>
*
* Copyright (C) 2021 Glider bv
+ * Copyright (C) 2025 Jean-François Lessard
*/
+#ifndef CONFIG_PANEL_BOOT_MESSAGE
#include <generated/utsrelease.h>
+#endif
+#include <linux/cleanup.h>
#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/jiffies.h>
+#include <linux/kstrtox.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/timer.h>
+#include <linux/map_to_7segment.h>
+#include <linux/map_to_14segment.h>
+
#include "line-display.h"
#define DEFAULT_SCROLL_RATE (HZ / 2)
/**
+ * struct linedisp_attachment - Holds the device to linedisp mapping
+ * @list: List entry for the linedisp_attachments list
+ * @device: Pointer to the device where linedisp attributes are added
+ * @linedisp: Pointer to the linedisp mapped to the device
+ * @direct: true for directly attached device using linedisp_attach(),
+ * false for child registered device using linedisp_register()
+ */
+struct linedisp_attachment {
+ struct list_head list;
+ struct device *device;
+ struct linedisp *linedisp;
+ bool direct;
+};
+
+static LIST_HEAD(linedisp_attachments);
+static DEFINE_SPINLOCK(linedisp_attachments_lock);
+
+static int create_attachment(struct device *dev, struct linedisp *linedisp, bool direct)
+{
+ struct linedisp_attachment *attachment;
+
+ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL);
+ if (!attachment)
+ return -ENOMEM;
+
+ attachment->device = dev;
+ attachment->linedisp = linedisp;
+ attachment->direct = direct;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+ list_add(&attachment->list, &linedisp_attachments);
+
+ return 0;
+}
+
+static struct linedisp *delete_attachment(struct device *dev, bool direct)
+{
+ struct linedisp_attachment *attachment;
+ struct linedisp *linedisp;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
+ if (attachment->device == dev &&
+ attachment->direct == direct)
+ break;
+ }
+
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
+ return NULL;
+
+ linedisp = attachment->linedisp;
+ list_del(&attachment->list);
+ kfree(attachment);
+
+ return linedisp;
+}
+
+static struct linedisp *to_linedisp(struct device *dev)
+{
+ struct linedisp_attachment *attachment;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
+ if (attachment->device == dev)
+ break;
+ }
+
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
+ return NULL;
+
+ return attachment->linedisp;
+}
+
+static inline bool should_scroll(struct linedisp *linedisp)
+{
+ return linedisp->message_len > linedisp->num_chars && linedisp->scroll_rate;
+}
+
+/**
* linedisp_scroll() - scroll the display by a character
* @t: really a pointer to the private data structure
*
@@ -30,7 +124,7 @@
*/
static void linedisp_scroll(struct timer_list *t)
{
- struct linedisp *linedisp = from_timer(linedisp, t, timer);
+ struct linedisp *linedisp = timer_container_of(linedisp, t, timer);
unsigned int i, ch = linedisp->scroll_pos;
unsigned int num_chars = linedisp->num_chars;
@@ -45,15 +139,14 @@ static void linedisp_scroll(struct timer_list *t)
}
/* update the display */
- linedisp->update(linedisp);
+ linedisp->ops->update(linedisp);
/* move on to the next character */
linedisp->scroll_pos++;
linedisp->scroll_pos %= linedisp->message_len;
/* rearm the timer */
- if (linedisp->message_len > num_chars && linedisp->scroll_rate)
- mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
+ mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
}
/**
@@ -74,7 +167,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
char *new_msg;
/* stop the scroll timer */
- del_timer_sync(&linedisp->timer);
+ timer_delete_sync(&linedisp->timer);
if (count == -1)
count = strlen(msg);
@@ -89,7 +182,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
linedisp->message = NULL;
linedisp->message_len = 0;
memset(linedisp->buf, ' ', linedisp->num_chars);
- linedisp->update(linedisp);
+ linedisp->ops->update(linedisp);
return 0;
}
@@ -103,8 +196,16 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
linedisp->message_len = count;
linedisp->scroll_pos = 0;
- /* update the display */
- linedisp_scroll(&linedisp->timer);
+ if (should_scroll(linedisp)) {
+ /* display scrolling message */
+ linedisp_scroll(&linedisp->timer);
+ } else {
+ /* display static message */
+ memset(linedisp->buf, ' ', linedisp->num_chars);
+ memcpy(linedisp->buf, linedisp->message,
+ umin(linedisp->num_chars, linedisp->message_len));
+ linedisp->ops->update(linedisp);
+ }
return 0;
}
@@ -123,7 +224,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
static ssize_t message_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
return sysfs_emit(buf, "%s\n", linedisp->message);
}
@@ -142,7 +243,7 @@ static ssize_t message_show(struct device *dev, struct device_attribute *attr,
static ssize_t message_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
int err;
err = linedisp_display(linedisp, buf, count);
@@ -151,10 +252,20 @@ static ssize_t message_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(message);
+static ssize_t num_chars_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct linedisp *linedisp = to_linedisp(dev);
+
+ return sysfs_emit(buf, "%u\n", linedisp->num_chars);
+}
+
+static DEVICE_ATTR_RO(num_chars);
+
static ssize_t scroll_step_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
}
@@ -163,86 +274,309 @@ static ssize_t scroll_step_ms_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
unsigned int ms;
+ int err;
- if (kstrtouint(buf, 10, &ms) != 0)
- return -EINVAL;
+ err = kstrtouint(buf, 10, &ms);
+ if (err)
+ return err;
+
+ timer_delete_sync(&linedisp->timer);
linedisp->scroll_rate = msecs_to_jiffies(ms);
- if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
- del_timer_sync(&linedisp->timer);
- if (linedisp->scroll_rate)
- linedisp_scroll(&linedisp->timer);
- }
+
+ if (should_scroll(linedisp))
+ linedisp_scroll(&linedisp->timer);
return count;
}
static DEVICE_ATTR_RW(scroll_step_ms);
+static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct linedisp *linedisp = to_linedisp(dev);
+ struct linedisp_map *map = linedisp->map;
+
+ memcpy(buf, &map->map, map->size);
+ return map->size;
+}
+
+static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct linedisp *linedisp = to_linedisp(dev);
+ struct linedisp_map *map = linedisp->map;
+
+ if (count != map->size)
+ return -EINVAL;
+
+ memcpy(&map->map, buf, count);
+ return count;
+}
+
+static const SEG7_DEFAULT_MAP(initial_map_seg7);
+static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store);
+
+static const SEG14_DEFAULT_MAP(initial_map_seg14);
+static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
+
static struct attribute *linedisp_attrs[] = {
&dev_attr_message.attr,
+ &dev_attr_num_chars.attr,
&dev_attr_scroll_step_ms.attr,
- NULL,
+ &dev_attr_map_seg7.attr,
+ &dev_attr_map_seg14.attr,
+ NULL
+};
+
+static umode_t linedisp_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct linedisp *linedisp = to_linedisp(dev);
+ struct linedisp_map *map = linedisp->map;
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_map_seg7.attr) {
+ if (!map)
+ return 0;
+ if (map->type != LINEDISP_MAP_SEG7)
+ return 0;
+ }
+
+ if (attr == &dev_attr_map_seg14.attr) {
+ if (!map)
+ return 0;
+ if (map->type != LINEDISP_MAP_SEG14)
+ return 0;
+ }
+
+ return mode;
};
-ATTRIBUTE_GROUPS(linedisp);
+
+static const struct attribute_group linedisp_group = {
+ .is_visible = linedisp_attr_is_visible,
+ .attrs = linedisp_attrs,
+};
+__ATTRIBUTE_GROUPS(linedisp);
+
+static DEFINE_IDA(linedisp_id);
+
+static void linedisp_release(struct device *dev)
+{
+ struct linedisp *linedisp = to_linedisp(dev);
+
+ kfree(linedisp->map);
+ kfree(linedisp->message);
+ kfree(linedisp->buf);
+ ida_free(&linedisp_id, linedisp->id);
+}
static const struct device_type linedisp_type = {
.groups = linedisp_groups,
+ .release = linedisp_release,
};
+static int linedisp_init_map(struct linedisp *linedisp)
+{
+ struct linedisp_map *map;
+ int err;
+
+ if (!linedisp->ops->get_map_type)
+ return 0;
+
+ err = linedisp->ops->get_map_type(linedisp);
+ if (err < 0)
+ return err;
+
+ map = kmalloc(sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ map->type = err;
+
+ /* assign initial mapping */
+ switch (map->type) {
+ case LINEDISP_MAP_SEG7:
+ map->map.seg7 = initial_map_seg7;
+ map->size = sizeof(map->map.seg7);
+ break;
+ case LINEDISP_MAP_SEG14:
+ map->map.seg14 = initial_map_seg14;
+ map->size = sizeof(map->map.seg14);
+ break;
+ default:
+ kfree(map);
+ return -EINVAL;
+ }
+
+ linedisp->map = map;
+
+ return 0;
+}
+
+#ifdef CONFIG_PANEL_BOOT_MESSAGE
+#define LINEDISP_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE
+#else
+#define LINEDISP_INIT_TEXT "Linux " UTS_RELEASE " "
+#endif
+
+/**
+ * linedisp_attach - attach a character line display
+ * @linedisp: pointer to character line display structure
+ * @dev: pointer of the device to attach to
+ * @num_chars: the number of characters that can be displayed
+ * @ops: character line display operations
+ *
+ * Directly attach the line-display sysfs attributes to the passed device.
+ * The caller is responsible for calling linedisp_detach() to release resources
+ * after use.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
+ unsigned int num_chars, const struct linedisp_ops *ops)
+{
+ int err;
+
+ memset(linedisp, 0, sizeof(*linedisp));
+ linedisp->ops = ops;
+ linedisp->num_chars = num_chars;
+ linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
+
+ linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
+ if (!linedisp->buf)
+ return -ENOMEM;
+
+ /* initialise a character mapping, if required */
+ err = linedisp_init_map(linedisp);
+ if (err)
+ goto out_free_buf;
+
+ /* initialise a timer for scrolling the message */
+ timer_setup(&linedisp->timer, linedisp_scroll, 0);
+
+ err = create_attachment(dev, linedisp, true);
+ if (err)
+ goto out_del_timer;
+
+ /* display a default message */
+ err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
+ if (err)
+ goto out_del_attach;
+
+ /* add attribute groups to target device */
+ err = device_add_groups(dev, linedisp_groups);
+ if (err)
+ goto out_del_attach;
+
+ return 0;
+
+out_del_attach:
+ delete_attachment(dev, true);
+out_del_timer:
+ timer_delete_sync(&linedisp->timer);
+out_free_buf:
+ kfree(linedisp->buf);
+ return err;
+}
+EXPORT_SYMBOL_NS_GPL(linedisp_attach, "LINEDISP");
+
+/**
+ * linedisp_detach - detach a character line display
+ * @dev: pointer of the device to detach from, that was previously
+ * attached with linedisp_attach()
+ */
+void linedisp_detach(struct device *dev)
+{
+ struct linedisp *linedisp;
+
+ linedisp = delete_attachment(dev, true);
+ if (!linedisp)
+ return;
+
+ timer_delete_sync(&linedisp->timer);
+
+ device_remove_groups(dev, linedisp_groups);
+
+ kfree(linedisp->map);
+ kfree(linedisp->message);
+ kfree(linedisp->buf);
+}
+EXPORT_SYMBOL_NS_GPL(linedisp_detach, "LINEDISP");
+
/**
* linedisp_register - register a character line display
* @linedisp: pointer to character line display structure
* @parent: parent device
* @num_chars: the number of characters that can be displayed
- * @buf: pointer to a buffer that can hold @num_chars characters
- * @update: Function called to update the display. This must not sleep!
+ * @ops: character line display operations
+ *
+ * Register the line-display sysfs attributes to a new device named
+ * "linedisp.N" added to the passed parent device.
+ * The caller is responsible for calling linedisp_unregister() to release
+ * resources after use.
*
* Return: zero on success, else a negative error code.
*/
int linedisp_register(struct linedisp *linedisp, struct device *parent,
- unsigned int num_chars, char *buf,
- void (*update)(struct linedisp *linedisp))
+ unsigned int num_chars, const struct linedisp_ops *ops)
{
- static atomic_t linedisp_id = ATOMIC_INIT(-1);
int err;
memset(linedisp, 0, sizeof(*linedisp));
linedisp->dev.parent = parent;
linedisp->dev.type = &linedisp_type;
- linedisp->update = update;
- linedisp->buf = buf;
+ linedisp->ops = ops;
linedisp->num_chars = num_chars;
linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
+ err = ida_alloc(&linedisp_id, GFP_KERNEL);
+ if (err < 0)
+ return err;
+ linedisp->id = err;
+
device_initialize(&linedisp->dev);
- dev_set_name(&linedisp->dev, "linedisp.%lu",
- (unsigned long)atomic_inc_return(&linedisp_id));
+ dev_set_name(&linedisp->dev, "linedisp.%u", linedisp->id);
+
+ err = -ENOMEM;
+ linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
+ if (!linedisp->buf)
+ goto out_put_device;
+
+ /* initialise a character mapping, if required */
+ err = linedisp_init_map(linedisp);
+ if (err)
+ goto out_put_device;
/* initialise a timer for scrolling the message */
timer_setup(&linedisp->timer, linedisp_scroll, 0);
- err = device_add(&linedisp->dev);
+ err = create_attachment(&linedisp->dev, linedisp, false);
if (err)
goto out_del_timer;
/* display a default message */
- err = linedisp_display(linedisp, "Linux " UTS_RELEASE " ", -1);
+ err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
if (err)
- goto out_del_dev;
+ goto out_del_attach;
+
+ err = device_add(&linedisp->dev);
+ if (err)
+ goto out_del_attach;
return 0;
-out_del_dev:
- device_del(&linedisp->dev);
+out_del_attach:
+ delete_attachment(&linedisp->dev, false);
out_del_timer:
- del_timer_sync(&linedisp->timer);
+ timer_delete_sync(&linedisp->timer);
+out_put_device:
put_device(&linedisp->dev);
return err;
}
-EXPORT_SYMBOL_GPL(linedisp_register);
+EXPORT_SYMBOL_NS_GPL(linedisp_register, "LINEDISP");
/**
* linedisp_unregister - unregister a character line display
@@ -252,10 +586,11 @@ EXPORT_SYMBOL_GPL(linedisp_register);
void linedisp_unregister(struct linedisp *linedisp)
{
device_del(&linedisp->dev);
- del_timer_sync(&linedisp->timer);
- kfree(linedisp->message);
+ delete_attachment(&linedisp->dev, false);
+ timer_delete_sync(&linedisp->timer);
put_device(&linedisp->dev);
}
-EXPORT_SYMBOL_GPL(linedisp_unregister);
+EXPORT_SYMBOL_NS_GPL(linedisp_unregister, "LINEDISP");
+MODULE_DESCRIPTION("Character line display core support");
MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/line-display.h b/drivers/auxdisplay/line-display.h
index 0f5891d34c48..36853b639711 100644
--- a/drivers/auxdisplay/line-display.h
+++ b/drivers/auxdisplay/line-display.h
@@ -6,38 +6,87 @@
* Author: Paul Burton <paul.burton@mips.com>
*
* Copyright (C) 2021 Glider bv
+ * Copyright (C) 2025 Jean-François Lessard
*/
#ifndef _LINEDISP_H
#define _LINEDISP_H
+#include <linux/device.h>
+#include <linux/timer_types.h>
+
+#include <linux/map_to_7segment.h>
+#include <linux/map_to_14segment.h>
+
+struct linedisp;
+
+/**
+ * enum linedisp_map_type - type of the character mapping
+ * @LINEDISP_MAP_SEG7: Map characters to 7 segment display
+ * @LINEDISP_MAP_SEG14: Map characters to 14 segment display
+ */
+enum linedisp_map_type {
+ LINEDISP_MAP_SEG7,
+ LINEDISP_MAP_SEG14,
+};
+
+/**
+ * struct linedisp_map - character mapping
+ * @type: type of the character mapping
+ * @map: conversion character mapping
+ * @size: size of the @map
+ */
+struct linedisp_map {
+ enum linedisp_map_type type;
+ union {
+ struct seg7_conversion_map seg7;
+ struct seg14_conversion_map seg14;
+ } map;
+ unsigned int size;
+};
+
+/**
+ * struct linedisp_ops - character line display operations
+ * @get_map_type: Function called to get the character mapping, if required
+ * @update: Function called to update the display. This must not sleep!
+ */
+struct linedisp_ops {
+ int (*get_map_type)(struct linedisp *linedisp);
+ void (*update)(struct linedisp *linedisp);
+};
+
/**
* struct linedisp - character line display private data structure
* @dev: the line display device
* @timer: timer used to implement scrolling
- * @update: function called to update the display
+ * @ops: character line display operations
* @buf: pointer to the buffer for the string currently displayed
* @message: the full message to display or scroll on the display
* @num_chars: the number of characters that can be displayed
* @message_len: the length of the @message string
* @scroll_pos: index of the first character of @message currently displayed
* @scroll_rate: scroll interval in jiffies
+ * @id: instance id of this display
*/
struct linedisp {
struct device dev;
struct timer_list timer;
- void (*update)(struct linedisp *linedisp);
+ const struct linedisp_ops *ops;
+ struct linedisp_map *map;
char *buf;
char *message;
unsigned int num_chars;
unsigned int message_len;
unsigned int scroll_pos;
unsigned int scroll_rate;
+ unsigned int id;
};
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
+ unsigned int num_chars, const struct linedisp_ops *ops);
+void linedisp_detach(struct device *dev);
int linedisp_register(struct linedisp *linedisp, struct device *parent,
- unsigned int num_chars, char *buf,
- void (*update)(struct linedisp *linedisp));
+ unsigned int num_chars, const struct linedisp_ops *ops);
void linedisp_unregister(struct linedisp *linedisp);
#endif /* LINEDISP_H */
diff --git a/drivers/auxdisplay/max6959.c b/drivers/auxdisplay/max6959.c
new file mode 100644
index 000000000000..962488197b9e
--- /dev/null
+++ b/drivers/auxdisplay/max6959.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MAX6958/6959 7-segment LED display controller
+ * Datasheet:
+ * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6958-MAX6959.pdf
+ *
+ * Copyright (c) 2024, Intel Corporation.
+ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+ */
+#include <linux/array_size.h>
+#include <linux/bitrev.h>
+#include <linux/bits.h>
+#include <linux/container_of.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <linux/map_to_7segment.h>
+
+#include "line-display.h"
+
+/* Registers */
+#define REG_DECODE_MODE 0x01
+#define REG_INTENSITY 0x02
+#define REG_SCAN_LIMIT 0x03
+#define REG_CONFIGURATION 0x04
+#define REG_CONFIGURATION_S_BIT BIT(0)
+
+#define REG_DIGIT(x) (0x20 + (x))
+#define REG_DIGIT0 0x20
+#define REG_DIGIT1 0x21
+#define REG_DIGIT2 0x22
+#define REG_DIGIT3 0x23
+
+#define REG_SEGMENTS 0x24
+#define REG_MAX REG_SEGMENTS
+
+struct max6959_priv {
+ struct linedisp linedisp;
+ struct delayed_work work;
+ struct regmap *regmap;
+};
+
+static void max6959_disp_update(struct work_struct *work)
+{
+ struct max6959_priv *priv = container_of(work, struct max6959_priv, work.work);
+ struct linedisp *linedisp = &priv->linedisp;
+ struct linedisp_map *map = linedisp->map;
+ char *s = linedisp->buf;
+ u8 buf[4];
+
+ /* Map segments according to datasheet */
+ buf[0] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
+ buf[1] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
+ buf[2] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
+ buf[3] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
+
+ regmap_bulk_write(priv->regmap, REG_DIGIT(0), buf, ARRAY_SIZE(buf));
+}
+
+static int max6959_linedisp_get_map_type(struct linedisp *linedisp)
+{
+ struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp);
+
+ INIT_DELAYED_WORK(&priv->work, max6959_disp_update);
+ return LINEDISP_MAP_SEG7;
+}
+
+static void max6959_linedisp_update(struct linedisp *linedisp)
+{
+ struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp);
+
+ schedule_delayed_work(&priv->work, 0);
+}
+
+static const struct linedisp_ops max6959_linedisp_ops = {
+ .get_map_type = max6959_linedisp_get_map_type,
+ .update = max6959_linedisp_update,
+};
+
+static int max6959_enable(struct max6959_priv *priv, bool enable)
+{
+ u8 mask = REG_CONFIGURATION_S_BIT;
+ u8 value = enable ? mask : 0;
+
+ return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value);
+}
+
+static void max6959_power_off(void *priv)
+{
+ max6959_enable(priv, false);
+}
+
+static int max6959_power_on(struct max6959_priv *priv)
+{
+ struct device *dev = regmap_get_device(priv->regmap);
+ int ret;
+
+ ret = max6959_enable(priv, true);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, max6959_power_off, priv);
+}
+
+static const struct regmap_config max6959_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int max6959_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct max6959_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(client, &max6959_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ ret = max6959_power_on(priv);
+ if (ret)
+ return ret;
+
+ ret = linedisp_register(&priv->linedisp, dev, 4, &max6959_linedisp_ops);
+ if (ret)
+ return ret;
+
+ i2c_set_clientdata(client, priv);
+
+ return 0;
+}
+
+static void max6959_i2c_remove(struct i2c_client *client)
+{
+ struct max6959_priv *priv = i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&priv->work);
+ linedisp_unregister(&priv->linedisp);
+}
+
+static int max6959_suspend(struct device *dev)
+{
+ return max6959_enable(dev_get_drvdata(dev), false);
+}
+
+static int max6959_resume(struct device *dev)
+{
+ return max6959_enable(dev_get_drvdata(dev), true);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(max6959_pm_ops, max6959_suspend, max6959_resume);
+
+static const struct i2c_device_id max6959_i2c_id[] = {
+ { "max6959" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6959_i2c_id);
+
+static const struct of_device_id max6959_of_table[] = {
+ { .compatible = "maxim,max6959" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max6959_of_table);
+
+static struct i2c_driver max6959_i2c_driver = {
+ .driver = {
+ .name = "max6959",
+ .pm = pm_sleep_ptr(&max6959_pm_ops),
+ .of_match_table = max6959_of_table,
+ },
+ .probe = max6959_i2c_probe,
+ .remove = max6959_i2c_remove,
+ .id_table = max6959_i2c_id,
+};
+module_i2c_driver(max6959_i2c_driver);
+
+MODULE_DESCRIPTION("MAX6958/6959 7-segment LED controller");
+MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("LINEDISP");
diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index eba04c0de7eb..958c0e31e84a 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -831,18 +831,12 @@ static void lcd_init(void)
struct charlcd *charlcd;
struct hd44780_common *hdc;
- hdc = hd44780_common_alloc();
- if (!hdc)
+ charlcd = hd44780_common_alloc();
+ if (!charlcd)
return;
- charlcd = charlcd_alloc();
- if (!charlcd) {
- kfree(hdc);
- return;
- }
-
+ hdc = charlcd->drvdata;
hdc->hd44780 = &lcd;
- charlcd->drvdata = hdc;
/*
* Init lcd struct with load-time values to preserve exact
@@ -1449,10 +1443,9 @@ static struct logical_input *panel_bind_key(const char *name, const char *press,
key->rise_time = 1;
key->fall_time = 1;
- strncpy(key->u.kbd.press_str, press, sizeof(key->u.kbd.press_str));
- strncpy(key->u.kbd.repeat_str, repeat, sizeof(key->u.kbd.repeat_str));
- strncpy(key->u.kbd.release_str, release,
- sizeof(key->u.kbd.release_str));
+ strtomem_pad(key->u.kbd.press_str, press, '\0');
+ strtomem_pad(key->u.kbd.repeat_str, repeat, '\0');
+ strtomem_pad(key->u.kbd.release_str, release, '\0');
list_add(&key->list, &logical_inputs);
return key;
}
@@ -1520,106 +1513,9 @@ static void keypad_init(void)
static void panel_attach(struct parport *port)
{
+ int selected_keypad_type = NOT_SET;
struct pardev_cb panel_cb;
- if (port->number != parport)
- return;
-
- if (pprt) {
- pr_err("%s: port->number=%d parport=%d, already registered!\n",
- __func__, port->number, parport);
- return;
- }
-
- memset(&panel_cb, 0, sizeof(panel_cb));
- panel_cb.private = &pprt;
- /* panel_cb.flags = 0 should be PARPORT_DEV_EXCL? */
-
- pprt = parport_register_dev_model(port, "panel", &panel_cb, 0);
- if (!pprt) {
- pr_err("%s: port->number=%d parport=%d, parport_register_device() failed\n",
- __func__, port->number, parport);
- return;
- }
-
- if (parport_claim(pprt)) {
- pr_err("could not claim access to parport%d. Aborting.\n",
- parport);
- goto err_unreg_device;
- }
-
- /* must init LCD first, just in case an IRQ from the keypad is
- * generated at keypad init
- */
- if (lcd.enabled) {
- lcd_init();
- if (!lcd.charlcd || charlcd_register(lcd.charlcd))
- goto err_unreg_device;
- }
-
- if (keypad.enabled) {
- keypad_init();
- if (misc_register(&keypad_dev))
- goto err_lcd_unreg;
- }
- return;
-
-err_lcd_unreg:
- if (scan_timer.function)
- del_timer_sync(&scan_timer);
- if (lcd.enabled)
- charlcd_unregister(lcd.charlcd);
-err_unreg_device:
- kfree(lcd.charlcd);
- lcd.charlcd = NULL;
- parport_unregister_device(pprt);
- pprt = NULL;
-}
-
-static void panel_detach(struct parport *port)
-{
- if (port->number != parport)
- return;
-
- if (!pprt) {
- pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n",
- __func__, port->number, parport);
- return;
- }
- if (scan_timer.function)
- del_timer_sync(&scan_timer);
-
- if (keypad.enabled) {
- misc_deregister(&keypad_dev);
- keypad_initialized = 0;
- }
-
- if (lcd.enabled) {
- charlcd_unregister(lcd.charlcd);
- lcd.initialized = false;
- kfree(lcd.charlcd->drvdata);
- kfree(lcd.charlcd);
- lcd.charlcd = NULL;
- }
-
- /* TODO: free all input signals */
- parport_release(pprt);
- parport_unregister_device(pprt);
- pprt = NULL;
-}
-
-static struct parport_driver panel_driver = {
- .name = "panel",
- .match_port = panel_attach,
- .detach = panel_detach,
- .devmodel = true,
-};
-
-/* init function */
-static int __init panel_init_module(void)
-{
- int selected_keypad_type = NOT_SET, err;
-
/* take care of an eventual profile */
switch (profile) {
case PANEL_PROFILE_CUSTOM:
@@ -1711,29 +1607,100 @@ static int __init panel_init_module(void)
if (!lcd.enabled && !keypad.enabled) {
/* no device enabled, let's exit */
pr_err("panel driver disabled.\n");
- return -ENODEV;
+ return;
}
- err = parport_register_driver(&panel_driver);
- if (err) {
- pr_err("could not register with parport. Aborting.\n");
- return err;
+ if (port->number != parport)
+ return;
+
+ if (pprt) {
+ pr_err("%s: port->number=%d parport=%d, already registered!\n",
+ __func__, port->number, parport);
+ return;
}
- if (pprt)
- pr_info("panel driver registered on parport%d (io=0x%lx).\n",
- parport, pprt->port->base);
- else
- pr_info("panel driver not yet registered\n");
- return 0;
+ memset(&panel_cb, 0, sizeof(panel_cb));
+ panel_cb.private = &pprt;
+ /* panel_cb.flags = 0 should be PARPORT_DEV_EXCL? */
+
+ pprt = parport_register_dev_model(port, "panel", &panel_cb, 0);
+ if (!pprt) {
+ pr_err("%s: port->number=%d parport=%d, parport_register_device() failed\n",
+ __func__, port->number, parport);
+ return;
+ }
+
+ if (parport_claim(pprt)) {
+ pr_err("could not claim access to parport%d. Aborting.\n",
+ parport);
+ goto err_unreg_device;
+ }
+
+ /* must init LCD first, just in case an IRQ from the keypad is
+ * generated at keypad init
+ */
+ if (lcd.enabled) {
+ lcd_init();
+ if (!lcd.charlcd || charlcd_register(lcd.charlcd))
+ goto err_unreg_device;
+ }
+
+ if (keypad.enabled) {
+ keypad_init();
+ if (misc_register(&keypad_dev))
+ goto err_lcd_unreg;
+ }
+ return;
+
+err_lcd_unreg:
+ if (scan_timer.function)
+ timer_delete_sync(&scan_timer);
+ if (lcd.enabled)
+ charlcd_unregister(lcd.charlcd);
+err_unreg_device:
+ hd44780_common_free(lcd.charlcd);
+ lcd.charlcd = NULL;
+ parport_unregister_device(pprt);
+ pprt = NULL;
}
-static void __exit panel_cleanup_module(void)
+static void panel_detach(struct parport *port)
{
- parport_unregister_driver(&panel_driver);
+ if (port->number != parport)
+ return;
+
+ if (!pprt) {
+ pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n",
+ __func__, port->number, parport);
+ return;
+ }
+ if (scan_timer.function)
+ timer_delete_sync(&scan_timer);
+
+ if (keypad.enabled) {
+ misc_deregister(&keypad_dev);
+ keypad_initialized = 0;
+ }
+
+ if (lcd.enabled) {
+ charlcd_unregister(lcd.charlcd);
+ lcd.initialized = false;
+ hd44780_common_free(lcd.charlcd);
+ lcd.charlcd = NULL;
+ }
+
+ /* TODO: free all input signals */
+ parport_release(pprt);
+ parport_unregister_device(pprt);
+ pprt = NULL;
}
-module_init(panel_init_module);
-module_exit(panel_cleanup_module);
+static struct parport_driver panel_driver = {
+ .name = "panel",
+ .match_port = panel_attach,
+ .detach = panel_detach,
+};
+module_parport_driver(panel_driver);
+
MODULE_AUTHOR("Willy Tarreau");
MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/seg-led-gpio.c b/drivers/auxdisplay/seg-led-gpio.c
new file mode 100644
index 000000000000..dfb62e9ce9b4
--- /dev/null
+++ b/drivers/auxdisplay/seg-led-gpio.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for a 7-segment LED display
+ *
+ * The decimal point LED present on some devices is currently not
+ * supported.
+ *
+ * Copyright (C) Allied Telesis Labs
+ */
+
+#include <linux/bitmap.h>
+#include <linux/container_of.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/map_to_7segment.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "line-display.h"
+
+struct seg_led_priv {
+ struct linedisp linedisp;
+ struct delayed_work work;
+ struct gpio_descs *segment_gpios;
+};
+
+static void seg_led_update(struct work_struct *work)
+{
+ struct seg_led_priv *priv = container_of(work, struct seg_led_priv, work.work);
+ struct linedisp *linedisp = &priv->linedisp;
+ struct linedisp_map *map = linedisp->map;
+ DECLARE_BITMAP(values, 8) = { };
+
+ bitmap_set_value8(values, map_to_seg7(&map->map.seg7, linedisp->buf[0]), 0);
+
+ gpiod_multi_set_value_cansleep(priv->segment_gpios, values);
+}
+
+static int seg_led_linedisp_get_map_type(struct linedisp *linedisp)
+{
+ struct seg_led_priv *priv = container_of(linedisp, struct seg_led_priv, linedisp);
+
+ INIT_DELAYED_WORK(&priv->work, seg_led_update);
+ return LINEDISP_MAP_SEG7;
+}
+
+static void seg_led_linedisp_update(struct linedisp *linedisp)
+{
+ struct seg_led_priv *priv = container_of(linedisp, struct seg_led_priv, linedisp);
+
+ schedule_delayed_work(&priv->work, 0);
+}
+
+static const struct linedisp_ops seg_led_linedisp_ops = {
+ .get_map_type = seg_led_linedisp_get_map_type,
+ .update = seg_led_linedisp_update,
+};
+
+static int seg_led_probe(struct platform_device *pdev)
+{
+ struct seg_led_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->segment_gpios = devm_gpiod_get_array(dev, "segment", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->segment_gpios))
+ return PTR_ERR(priv->segment_gpios);
+
+ if (priv->segment_gpios->ndescs < 7 || priv->segment_gpios->ndescs > 8)
+ return -EINVAL;
+
+ return linedisp_register(&priv->linedisp, dev, 1, &seg_led_linedisp_ops);
+}
+
+static void seg_led_remove(struct platform_device *pdev)
+{
+ struct seg_led_priv *priv = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&priv->work);
+ linedisp_unregister(&priv->linedisp);
+}
+
+static const struct of_device_id seg_led_of_match[] = {
+ { .compatible = "gpio-7-segment"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, seg_led_of_match);
+
+static struct platform_driver seg_led_driver = {
+ .probe = seg_led_probe,
+ .remove = seg_led_remove,
+ .driver = {
+ .name = "seg-led-gpio",
+ .of_match_table = seg_led_of_match,
+ },
+};
+module_platform_driver(seg_led_driver);
+
+MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>");
+MODULE_DESCRIPTION("7 segment LED driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("LINEDISP");
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 6f04b831a5c0..1786d87b29e2 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -167,6 +167,12 @@ config PM_QOS_KUNIT_TEST
depends on KUNIT=y
default KUNIT_ALL_TESTS
+config PM_RUNTIME_KUNIT_TEST
+ tristate "KUnit Tests for runtime PM" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ depends on PM
+ default KUNIT_ALL_TESTS
+
config HMEM_REPORTING
bool
default n
@@ -226,8 +232,21 @@ config GENERIC_ARCH_TOPOLOGY
config GENERIC_ARCH_NUMA
bool
+ select NUMA_MEMBLKS
help
Enable support for generic NUMA implementation. Currently, RISC-V
and ARM64 use it.
+config FW_DEVLINK_SYNC_STATE_TIMEOUT
+ bool "sync_state() behavior defaults to timeout instead of strict"
+ help
+ This is build time equivalent of adding kernel command line parameter
+ "fw_devlink.sync_state=timeout". Give up waiting on consumers and
+ call sync_state() on any devices that haven't yet received their
+ sync_state() calls after deferred_probe_timeout has expired or by
+ late_initcall() if !CONFIG_MODULES. You should almost always want to
+ select N here unless you have already successfully tested with the
+ command line option on every system/board your kernel is expected to
+ work on.
+
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 83217d243c25..8074a10183dc 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -6,7 +6,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
cpu.o firmware.o init.o map.o devres.o \
attribute_container.o transport_class.o \
topology.o container.o property.o cacheinfo.o \
- swnode.o
+ swnode.o faux.o
obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-y += power/
@@ -16,13 +16,14 @@ obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
ifeq ($(CONFIG_SYSFS),y)
obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_AUXILIARY_BUS) += auxiliary_sysfs.o
endif
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
obj-$(CONFIG_SOC_BUS) += soc.o
obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
-obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o
+obj-$(CONFIG_GENERIC_MSI_IRQ) += platform-msi.o
obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o
obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o
obj-$(CONFIG_ACPI) += physical_location.o
diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c
index eaa31e567d1e..c99f2ab105e5 100644
--- a/drivers/base/arch_numa.c
+++ b/drivers/base/arch_numa.c
@@ -12,16 +12,12 @@
#include <linux/memblock.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/numa_memblks.h>
#include <asm/sections.h>
-struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
-EXPORT_SYMBOL(node_data);
-nodemask_t numa_nodes_parsed __initdata;
static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
-static int numa_distance_cnt;
-static u8 *numa_distance;
bool numa_off;
static __init int numa_parse_early_param(char *opt)
@@ -30,6 +26,8 @@ static __init int numa_parse_early_param(char *opt)
return -EINVAL;
if (str_has_prefix(opt, "off"))
numa_off = true;
+ if (!strncmp(opt, "fake=", 5))
+ return numa_emu_cmdline(opt + 5);
return 0;
}
@@ -61,6 +59,7 @@ EXPORT_SYMBOL(cpumask_of_node);
#endif
+#ifndef CONFIG_NUMA_EMU
static void numa_update_cpu(unsigned int cpu, bool remove)
{
int nid = cpu_to_node(cpu);
@@ -83,6 +82,7 @@ void numa_remove_cpu(unsigned int cpu)
{
numa_update_cpu(cpu, true);
}
+#endif
void numa_clear_node(unsigned int cpu)
{
@@ -144,7 +144,7 @@ void __init early_map_cpu_to_node(unsigned int cpu, int nid)
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
EXPORT_SYMBOL(__per_cpu_offset);
-static int __init early_cpu_to_node(int cpu)
+int early_cpu_to_node(int cpu)
{
return cpu_to_node_map[cpu];
}
@@ -189,174 +189,28 @@ void __init setup_per_cpu_areas(void)
}
#endif
-/**
- * numa_add_memblk() - Set node id to memblk
- * @nid: NUMA node ID of the new memblk
- * @start: Start address of the new memblk
- * @end: End address of the new memblk
- *
- * RETURNS:
- * 0 on success, -errno on failure.
- */
-int __init numa_add_memblk(int nid, u64 start, u64 end)
-{
- int ret;
-
- ret = memblock_set_node(start, (end - start), &memblock.memory, nid);
- if (ret < 0) {
- pr_err("memblock [0x%llx - 0x%llx] failed to add on node %d\n",
- start, (end - 1), nid);
- return ret;
- }
-
- node_set(nid, numa_nodes_parsed);
- return ret;
-}
-
/*
* Initialize NODE_DATA for a node on the local memory
*/
static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
{
- const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES);
- u64 nd_pa;
- void *nd;
- int tnid;
-
if (start_pfn >= end_pfn)
pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
- nd_pa = memblock_phys_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
- if (!nd_pa)
- panic("Cannot allocate %zu bytes for node %d data\n",
- nd_size, nid);
+ alloc_node_data(nid);
- nd = __va(nd_pa);
-
- /* report and initialize */
- pr_info("NODE_DATA [mem %#010Lx-%#010Lx]\n",
- nd_pa, nd_pa + nd_size - 1);
- tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
- if (tnid != nid)
- pr_info("NODE_DATA(%d) on node %d\n", nid, tnid);
-
- node_data[nid] = nd;
- memset(NODE_DATA(nid), 0, sizeof(pg_data_t));
NODE_DATA(nid)->node_id = nid;
NODE_DATA(nid)->node_start_pfn = start_pfn;
NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
}
-/*
- * numa_free_distance
- *
- * The current table is freed.
- */
-void __init numa_free_distance(void)
-{
- size_t size;
-
- if (!numa_distance)
- return;
-
- size = numa_distance_cnt * numa_distance_cnt *
- sizeof(numa_distance[0]);
-
- memblock_free(numa_distance, size);
- numa_distance_cnt = 0;
- numa_distance = NULL;
-}
-
-/*
- * Create a new NUMA distance table.
- */
-static int __init numa_alloc_distance(void)
-{
- size_t size;
- int i, j;
-
- size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]);
- numa_distance = memblock_alloc(size, PAGE_SIZE);
- if (WARN_ON(!numa_distance))
- return -ENOMEM;
-
- numa_distance_cnt = nr_node_ids;
-
- /* fill with the default distances */
- for (i = 0; i < numa_distance_cnt; i++)
- for (j = 0; j < numa_distance_cnt; j++)
- numa_distance[i * numa_distance_cnt + j] = i == j ?
- LOCAL_DISTANCE : REMOTE_DISTANCE;
-
- pr_debug("Initialized distance table, cnt=%d\n", numa_distance_cnt);
-
- return 0;
-}
-
-/**
- * numa_set_distance() - Set inter node NUMA distance from node to node.
- * @from: the 'from' node to set distance
- * @to: the 'to' node to set distance
- * @distance: NUMA distance
- *
- * Set the distance from node @from to @to to @distance.
- * If distance table doesn't exist, a warning is printed.
- *
- * If @from or @to is higher than the highest known node or lower than zero
- * or @distance doesn't make sense, the call is ignored.
- */
-void __init numa_set_distance(int from, int to, int distance)
-{
- if (!numa_distance) {
- pr_warn_once("Warning: distance table not allocated yet\n");
- return;
- }
-
- if (from >= numa_distance_cnt || to >= numa_distance_cnt ||
- from < 0 || to < 0) {
- pr_warn_once("Warning: node ids are out of bound, from=%d to=%d distance=%d\n",
- from, to, distance);
- return;
- }
-
- if ((u8)distance != distance ||
- (from == to && distance != LOCAL_DISTANCE)) {
- pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
- from, to, distance);
- return;
- }
-
- numa_distance[from * numa_distance_cnt + to] = distance;
-}
-
-/*
- * Return NUMA distance @from to @to
- */
-int __node_distance(int from, int to)
-{
- if (from >= numa_distance_cnt || to >= numa_distance_cnt)
- return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE;
- return numa_distance[from * numa_distance_cnt + to];
-}
-EXPORT_SYMBOL(__node_distance);
-
static int __init numa_register_nodes(void)
{
int nid;
- struct memblock_region *mblk;
-
- /* Check that valid nid is set to memblks */
- for_each_mem_region(mblk) {
- int mblk_nid = memblock_get_region_node(mblk);
- phys_addr_t start = mblk->base;
- phys_addr_t end = mblk->base + mblk->size - 1;
-
- if (mblk_nid == NUMA_NO_NODE || mblk_nid >= MAX_NUMNODES) {
- pr_warn("Warning: invalid memblk node %d [mem %pap-%pap]\n",
- mblk_nid, &start, &end);
- return -EINVAL;
- }
- }
+
+ /* Check the validity of the memblock/node mapping */
+ if (!memblock_validate_numa_coverage(0))
+ return -EINVAL;
/* Finally register nodes. */
for_each_node_mask(nid, numa_nodes_parsed) {
@@ -381,11 +235,7 @@ static int __init numa_init(int (*init_func)(void))
nodes_clear(node_possible_map);
nodes_clear(node_online_map);
- ret = numa_alloc_distance();
- if (ret < 0)
- return ret;
-
- ret = init_func();
+ ret = numa_memblks_init(init_func, /* memblock_force_top_down */ false);
if (ret < 0)
goto out_free_distance;
@@ -403,7 +253,7 @@ static int __init numa_init(int (*init_func)(void))
return 0;
out_free_distance:
- numa_free_distance();
+ numa_reset_distance();
return ret;
}
@@ -433,6 +283,7 @@ static int __init dummy_numa_init(void)
pr_err("NUMA init failed\n");
return ret;
}
+ node_set(0, numa_nodes_parsed);
numa_off = true;
return 0;
@@ -445,7 +296,7 @@ static int __init arch_acpi_numa_init(void)
ret = acpi_numa_init();
if (ret) {
- pr_info("Failed to initialise from firmware\n");
+ pr_debug("Failed to initialise from firmware\n");
return ret;
}
@@ -475,3 +326,54 @@ void __init arch_numa_init(void)
numa_init(dummy_numa_init);
}
+
+#ifdef CONFIG_NUMA_EMU
+void __init numa_emu_update_cpu_to_node(int *emu_nid_to_phys,
+ unsigned int nr_emu_nids)
+{
+ int i, j;
+
+ /*
+ * Transform cpu_to_node_map table to use emulated nids by
+ * reverse-mapping phys_nid. The maps should always exist but fall
+ * back to zero just in case.
+ */
+ for (i = 0; i < ARRAY_SIZE(cpu_to_node_map); i++) {
+ if (cpu_to_node_map[i] == NUMA_NO_NODE)
+ continue;
+ for (j = 0; j < nr_emu_nids; j++)
+ if (cpu_to_node_map[i] == emu_nid_to_phys[j])
+ break;
+ cpu_to_node_map[i] = j < nr_emu_nids ? j : 0;
+ }
+}
+
+u64 __init numa_emu_dma_end(void)
+{
+ return memblock_start_of_DRAM() + SZ_4G;
+}
+
+void debug_cpumask_set_cpu(unsigned int cpu, int node, bool enable)
+{
+ struct cpumask *mask;
+
+ if (node == NUMA_NO_NODE)
+ return;
+
+ mask = node_to_cpumask_map[node];
+ if (!cpumask_available(mask)) {
+ pr_err("node_to_cpumask_map[%i] NULL\n", node);
+ dump_stack();
+ return;
+ }
+
+ if (enable)
+ cpumask_set_cpu(cpu, mask);
+ else
+ cpumask_clear_cpu(cpu, mask);
+
+ pr_debug("%s cpu %d node %d: mask now %*pbl\n",
+ enable ? "numa_add_cpu" : "numa_remove_cpu",
+ cpu, node, cpumask_pr_args(mask));
+}
+#endif /* CONFIG_NUMA_EMU */
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 46cbe4471e78..84ec92bff642 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -8,8 +8,10 @@
#include <linux/acpi.h>
#include <linux/cacheinfo.h>
+#include <linux/cleanup.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/cpu_smt.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>
@@ -19,14 +21,16 @@
#include <linux/init.h>
#include <linux/rcupdate.h>
#include <linux/sched.h>
+#include <linux/units.h>
#define CREATE_TRACE_POINTS
-#include <trace/events/thermal_pressure.h>
+#include <trace/events/hw_pressure.h>
static DEFINE_PER_CPU(struct scale_freq_data __rcu *, sft_data);
static struct cpumask scale_freq_counters_mask;
static bool scale_freq_invariant;
-static DEFINE_PER_CPU(u32, freq_factor) = 1;
+DEFINE_PER_CPU(unsigned long, capacity_freq_ref) = 0;
+EXPORT_PER_CPU_SYMBOL_GPL(capacity_freq_ref);
static bool supports_scale_freq_counters(const struct cpumask *cpus)
{
@@ -150,95 +154,54 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq,
per_cpu(arch_freq_scale, i) = scale;
}
-DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
-EXPORT_PER_CPU_SYMBOL_GPL(cpu_scale);
-
-void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity)
-{
- per_cpu(cpu_scale, cpu) = capacity;
-}
-
-DEFINE_PER_CPU(unsigned long, thermal_pressure);
+DEFINE_PER_CPU(unsigned long, hw_pressure);
/**
- * topology_update_thermal_pressure() - Update thermal pressure for CPUs
+ * topology_update_hw_pressure() - Update HW pressure for CPUs
* @cpus : The related CPUs for which capacity has been reduced
* @capped_freq : The maximum allowed frequency that CPUs can run at
*
- * Update the value of thermal pressure for all @cpus in the mask. The
+ * Update the value of HW pressure for all @cpus in the mask. The
* cpumask should include all (online+offline) affected CPUs, to avoid
* operating on stale data when hot-plug is used for some CPUs. The
* @capped_freq reflects the currently allowed max CPUs frequency due to
- * thermal capping. It might be also a boost frequency value, which is bigger
- * than the internal 'freq_factor' max frequency. In such case the pressure
- * value should simply be removed, since this is an indication that there is
- * no thermal throttling. The @capped_freq must be provided in kHz.
+ * HW capping. It might be also a boost frequency value, which is bigger
+ * than the internal 'capacity_freq_ref' max frequency. In such case the
+ * pressure value should simply be removed, since this is an indication that
+ * there is no HW throttling. The @capped_freq must be provided in kHz.
*/
-void topology_update_thermal_pressure(const struct cpumask *cpus,
+void topology_update_hw_pressure(const struct cpumask *cpus,
unsigned long capped_freq)
{
- unsigned long max_capacity, capacity, th_pressure;
+ unsigned long max_capacity, capacity, pressure;
u32 max_freq;
int cpu;
cpu = cpumask_first(cpus);
max_capacity = arch_scale_cpu_capacity(cpu);
- max_freq = per_cpu(freq_factor, cpu);
-
- /* Convert to MHz scale which is used in 'freq_factor' */
- capped_freq /= 1000;
+ max_freq = arch_scale_freq_ref(cpu);
/*
* Handle properly the boost frequencies, which should simply clean
- * the thermal pressure value.
+ * the HW pressure value.
*/
if (max_freq <= capped_freq)
capacity = max_capacity;
else
capacity = mult_frac(max_capacity, capped_freq, max_freq);
- th_pressure = max_capacity - capacity;
+ pressure = max_capacity - capacity;
- trace_thermal_pressure_update(cpu, th_pressure);
+ trace_hw_pressure_update(cpu, pressure);
for_each_cpu(cpu, cpus)
- WRITE_ONCE(per_cpu(thermal_pressure, cpu), th_pressure);
-}
-EXPORT_SYMBOL_GPL(topology_update_thermal_pressure);
-
-static ssize_t cpu_capacity_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct cpu *cpu = container_of(dev, struct cpu, dev);
-
- return sysfs_emit(buf, "%lu\n", topology_get_cpu_scale(cpu->dev.id));
+ WRITE_ONCE(per_cpu(hw_pressure, cpu), pressure);
}
+EXPORT_SYMBOL_GPL(topology_update_hw_pressure);
static void update_topology_flags_workfn(struct work_struct *work);
static DECLARE_WORK(update_topology_flags_work, update_topology_flags_workfn);
-static DEVICE_ATTR_RO(cpu_capacity);
-
-static int register_cpu_capacity_sysctl(void)
-{
- int i;
- struct device *cpu;
-
- for_each_possible_cpu(i) {
- cpu = get_cpu_device(i);
- if (!cpu) {
- pr_err("%s: too early to get CPU%d device!\n",
- __func__, i);
- continue;
- }
- device_create_file(cpu, &dev_attr_cpu_capacity);
- }
-
- return 0;
-}
-subsys_initcall(register_cpu_capacity_sysctl);
-
static int update_topology;
int topology_update_cpu_topology(void)
@@ -279,13 +242,15 @@ void topology_normalize_cpu_scale(void)
capacity_scale = 1;
for_each_possible_cpu(cpu) {
- capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
+ capacity = raw_capacity[cpu] *
+ (per_cpu(capacity_freq_ref, cpu) ?: 1);
capacity_scale = max(capacity, capacity_scale);
}
pr_debug("cpu_capacity: capacity_scale=%llu\n", capacity_scale);
for_each_possible_cpu(cpu) {
- capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
+ capacity = raw_capacity[cpu] *
+ (per_cpu(capacity_freq_ref, cpu) ?: 1);
capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT,
capacity_scale);
topology_set_cpu_scale(cpu, capacity);
@@ -321,15 +286,15 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
cpu_node, raw_capacity[cpu]);
/*
- * Update freq_factor for calculating early boot cpu capacities.
+ * Update capacity_freq_ref for calculating early boot CPU capacities.
* For non-clk CPU DVFS mechanism, there's no way to get the
* frequency value now, assuming they are running at the same
- * frequency (by keeping the initial freq_factor value).
+ * frequency (by keeping the initial capacity_freq_ref value).
*/
cpu_clk = of_clk_get(cpu_node, 0);
- if (!PTR_ERR_OR_ZERO(cpu_clk)) {
- per_cpu(freq_factor, cpu) =
- clk_get_rate(cpu_clk) / 1000;
+ if (!IS_ERR_OR_NULL(cpu_clk)) {
+ per_cpu(capacity_freq_ref, cpu) =
+ clk_get_rate(cpu_clk) / HZ_PER_KHZ;
clk_put(cpu_clk);
}
} else {
@@ -345,15 +310,20 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
return !ret;
}
+void __weak freq_inv_set_max_ratio(int cpu, u64 max_rate)
+{
+}
+
#ifdef CONFIG_ACPI_CPPC_LIB
#include <acpi/cppc_acpi.h>
-void topology_init_cpu_capacity_cppc(void)
+static inline void topology_init_cpu_capacity_cppc(void)
{
+ u64 capacity, capacity_scale = 0;
struct cppc_perf_caps perf_caps;
int cpu;
- if (likely(acpi_disabled || !acpi_cpc_valid()))
+ if (likely(!acpi_cpc_valid()))
return;
raw_capacity = kcalloc(num_possible_cpus(), sizeof(*raw_capacity),
@@ -366,6 +336,10 @@ void topology_init_cpu_capacity_cppc(void)
(perf_caps.highest_perf >= perf_caps.nominal_perf) &&
(perf_caps.highest_perf >= perf_caps.lowest_perf)) {
raw_capacity[cpu] = perf_caps.highest_perf;
+ capacity_scale = max_t(u64, capacity_scale, raw_capacity[cpu]);
+
+ per_cpu(capacity_freq_ref, cpu) = cppc_perf_to_khz(&perf_caps, raw_capacity[cpu]);
+
pr_debug("cpu_capacity: CPU%d cpu_capacity=%u (raw).\n",
cpu, raw_capacity[cpu]);
continue;
@@ -376,13 +350,28 @@ void topology_init_cpu_capacity_cppc(void)
goto exit;
}
- topology_normalize_cpu_scale();
+ for_each_possible_cpu(cpu) {
+ freq_inv_set_max_ratio(cpu,
+ per_cpu(capacity_freq_ref, cpu) * HZ_PER_KHZ);
+
+ capacity = raw_capacity[cpu];
+ capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT,
+ capacity_scale);
+ topology_set_cpu_scale(cpu, capacity);
+ pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
+ cpu, topology_get_cpu_scale(cpu));
+ }
+
schedule_work(&update_topology_flags_work);
pr_debug("cpu_capacity: cpu_capacity initialization done\n");
exit:
free_raw_capacity();
}
+void acpi_processor_init_invariance_cppc(void)
+{
+ topology_init_cpu_capacity_cppc();
+}
#endif
#ifdef CONFIG_CPU_FREQ
@@ -398,9 +387,6 @@ init_cpu_capacity_callback(struct notifier_block *nb,
struct cpufreq_policy *policy = data;
int cpu;
- if (!raw_capacity)
- return 0;
-
if (val != CPUFREQ_CREATE_POLICY)
return 0;
@@ -410,13 +396,18 @@ init_cpu_capacity_callback(struct notifier_block *nb,
cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus);
- for_each_cpu(cpu, policy->related_cpus)
- per_cpu(freq_factor, cpu) = policy->cpuinfo.max_freq / 1000;
+ for_each_cpu(cpu, policy->related_cpus) {
+ per_cpu(capacity_freq_ref, cpu) = policy->cpuinfo.max_freq;
+ freq_inv_set_max_ratio(cpu,
+ per_cpu(capacity_freq_ref, cpu) * HZ_PER_KHZ);
+ }
if (cpumask_empty(cpus_to_visit)) {
- topology_normalize_cpu_scale();
- schedule_work(&update_topology_flags_work);
- free_raw_capacity();
+ if (raw_capacity) {
+ topology_normalize_cpu_scale();
+ schedule_work(&update_topology_flags_work);
+ free_raw_capacity();
+ }
pr_debug("cpu_capacity: parsing done\n");
schedule_work(&parsing_done_work);
}
@@ -436,7 +427,7 @@ static int __init register_cpufreq_notifier(void)
* On ACPI-based systems skip registering cpufreq notifier as cpufreq
* information is not needed for cpu capacity initialization.
*/
- if (!acpi_disabled || !raw_capacity)
+ if (!acpi_disabled)
return -EINVAL;
if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL))
@@ -466,6 +457,10 @@ core_initcall(free_raw_capacity);
#endif
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
+
+/* Used to enable the SMT control */
+static unsigned int max_smt_thread_num = 1;
+
/*
* This function returns the logic cpu number of the node.
* There are basically three kinds of return values:
@@ -478,10 +473,10 @@ core_initcall(free_raw_capacity);
*/
static int __init get_cpu_for_node(struct device_node *node)
{
- struct device_node *cpu_node;
int cpu;
+ struct device_node *cpu_node __free(device_node) =
+ of_parse_phandle(node, "cpu", 0);
- cpu_node = of_parse_phandle(node, "cpu", 0);
if (!cpu_node)
return -1;
@@ -492,7 +487,6 @@ static int __init get_cpu_for_node(struct device_node *node)
pr_info("CPU node for %pOF exist but the possible cpu range is :%*pbl\n",
cpu_node, cpumask_pr_args(cpu_possible_mask));
- of_node_put(cpu_node);
return cpu;
}
@@ -503,28 +497,30 @@ static int __init parse_core(struct device_node *core, int package_id,
bool leaf = true;
int i = 0;
int cpu;
- struct device_node *t;
do {
snprintf(name, sizeof(name), "thread%d", i);
- t = of_get_child_by_name(core, name);
- if (t) {
- leaf = false;
- cpu = get_cpu_for_node(t);
- if (cpu >= 0) {
- cpu_topology[cpu].package_id = package_id;
- cpu_topology[cpu].cluster_id = cluster_id;
- cpu_topology[cpu].core_id = core_id;
- cpu_topology[cpu].thread_id = i;
- } else if (cpu != -ENODEV) {
- pr_err("%pOF: Can't get CPU for thread\n", t);
- of_node_put(t);
- return -EINVAL;
- }
- of_node_put(t);
+ struct device_node *t __free(device_node) =
+ of_get_child_by_name(core, name);
+
+ if (!t)
+ break;
+
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].package_id = package_id;
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else if (cpu != -ENODEV) {
+ pr_err("%pOF: Can't get CPU for thread\n", t);
+ return -EINVAL;
}
i++;
- } while (t);
+ } while (1);
+
+ max_smt_thread_num = max_t(unsigned int, max_smt_thread_num, i);
cpu = get_cpu_for_node(core);
if (cpu >= 0) {
@@ -551,7 +547,6 @@ static int __init parse_cluster(struct device_node *cluster, int package_id,
char name[20];
bool leaf = true;
bool has_cores = false;
- struct device_node *c;
int core_id = 0;
int i, ret;
@@ -563,49 +558,50 @@ static int __init parse_cluster(struct device_node *cluster, int package_id,
i = 0;
do {
snprintf(name, sizeof(name), "cluster%d", i);
- c = of_get_child_by_name(cluster, name);
- if (c) {
- leaf = false;
- ret = parse_cluster(c, package_id, i, depth + 1);
- if (depth > 0)
- pr_warn("Topology for clusters of clusters not yet supported\n");
- of_node_put(c);
- if (ret != 0)
- return ret;
- }
+ struct device_node *c __free(device_node) =
+ of_get_child_by_name(cluster, name);
+
+ if (!c)
+ break;
+
+ leaf = false;
+ ret = parse_cluster(c, package_id, i, depth + 1);
+ if (depth > 0)
+ pr_warn("Topology for clusters of clusters not yet supported\n");
+ if (ret != 0)
+ return ret;
i++;
- } while (c);
+ } while (1);
/* Now check for cores */
i = 0;
do {
snprintf(name, sizeof(name), "core%d", i);
- c = of_get_child_by_name(cluster, name);
- if (c) {
- has_cores = true;
-
- if (depth == 0) {
- pr_err("%pOF: cpu-map children should be clusters\n",
- c);
- of_node_put(c);
- return -EINVAL;
- }
+ struct device_node *c __free(device_node) =
+ of_get_child_by_name(cluster, name);
- if (leaf) {
- ret = parse_core(c, package_id, cluster_id,
- core_id++);
- } else {
- pr_err("%pOF: Non-leaf cluster with core %s\n",
- cluster, name);
- ret = -EINVAL;
- }
+ if (!c)
+ break;
- of_node_put(c);
+ has_cores = true;
+
+ if (depth == 0) {
+ pr_err("%pOF: cpu-map children should be clusters\n", c);
+ return -EINVAL;
+ }
+
+ if (leaf) {
+ ret = parse_core(c, package_id, cluster_id, core_id++);
if (ret != 0)
return ret;
+ } else {
+ pr_err("%pOF: Non-leaf cluster with core %s\n",
+ cluster, name);
+ return -EINVAL;
}
+
i++;
- } while (c);
+ } while (1);
if (leaf && !has_cores)
pr_warn("%pOF: empty cluster\n", cluster);
@@ -616,36 +612,49 @@ static int __init parse_cluster(struct device_node *cluster, int package_id,
static int __init parse_socket(struct device_node *socket)
{
char name[20];
- struct device_node *c;
bool has_socket = false;
int package_id = 0, ret;
do {
snprintf(name, sizeof(name), "socket%d", package_id);
- c = of_get_child_by_name(socket, name);
- if (c) {
- has_socket = true;
- ret = parse_cluster(c, package_id, -1, 0);
- of_node_put(c);
- if (ret != 0)
- return ret;
- }
+ struct device_node *c __free(device_node) =
+ of_get_child_by_name(socket, name);
+
+ if (!c)
+ break;
+
+ has_socket = true;
+ ret = parse_cluster(c, package_id, -1, 0);
+ if (ret != 0)
+ return ret;
+
package_id++;
- } while (c);
+ } while (1);
if (!has_socket)
ret = parse_cluster(socket, 0, -1, 0);
+ /*
+ * Reset the max_smt_thread_num to 1 on failure. Since on failure
+ * we need to notify the framework the SMT is not supported, but
+ * max_smt_thread_num can be initialized to the SMT thread number
+ * of the cores which are successfully parsed.
+ */
+ if (ret)
+ max_smt_thread_num = 1;
+
+ cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num);
+
return ret;
}
static int __init parse_dt_topology(void)
{
- struct device_node *cn, *map;
int ret = 0;
int cpu;
+ struct device_node *cn __free(device_node) =
+ of_find_node_by_path("/cpus");
- cn = of_find_node_by_path("/cpus");
if (!cn) {
pr_err("No CPU information found in DT\n");
return 0;
@@ -655,13 +664,15 @@ static int __init parse_dt_topology(void)
* When topology is provided cpu-map is essentially a root
* cluster with restricted subnodes.
*/
- map = of_get_child_by_name(cn, "cpu-map");
+ struct device_node *map __free(device_node) =
+ of_get_child_by_name(cn, "cpu-map");
+
if (!map)
- goto out;
+ return ret;
ret = parse_socket(map);
if (ret != 0)
- goto out_map;
+ return ret;
topology_normalize_cpu_scale();
@@ -671,14 +682,9 @@ static int __init parse_dt_topology(void)
*/
for_each_possible_cpu(cpu)
if (cpu_topology[cpu].package_id < 0) {
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
-out_map:
- of_node_put(map);
-out:
- of_node_put(cn);
return ret;
}
#endif
@@ -736,7 +742,7 @@ void update_siblings_masks(unsigned int cpuid)
ret = detect_cache_attributes(cpuid);
if (ret && ret != -ENOENT)
- pr_info("Early cacheinfo failed, ret = %d\n", ret);
+ pr_info("Early cacheinfo allocation failed, ret = %d\n", ret);
/* update core and thread sibling masks */
for_each_online_cpu(cpu) {
@@ -817,15 +823,109 @@ void remove_cpu_topology(unsigned int cpu)
clear_cpu_topology(cpu);
}
+#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
+struct cpu_smt_info {
+ unsigned int thread_num;
+ int core_id;
+};
+
+static bool __init acpi_cpu_is_threaded(int cpu)
+{
+ int is_threaded = acpi_pptt_cpu_is_thread(cpu);
+
+ /*
+ * if the PPTT doesn't have thread information, check for architecture
+ * specific fallback if available
+ */
+ if (is_threaded < 0)
+ is_threaded = arch_cpu_is_threaded();
+
+ return !!is_threaded;
+}
+
+/*
+ * Propagate the topology information of the processor_topology_node tree to the
+ * cpu_topology array.
+ */
__weak int __init parse_acpi_topology(void)
{
+ unsigned int max_smt_thread_num = 1;
+ struct cpu_smt_info *entry;
+ struct xarray hetero_cpu;
+ unsigned long hetero_id;
+ int cpu, topology_id;
+
+ if (acpi_disabled)
+ return 0;
+
+ xa_init(&hetero_cpu);
+
+ for_each_possible_cpu(cpu) {
+ topology_id = find_acpi_cpu_topology(cpu, 0);
+ if (topology_id < 0)
+ return topology_id;
+
+ if (acpi_cpu_is_threaded(cpu)) {
+ cpu_topology[cpu].thread_id = topology_id;
+ topology_id = find_acpi_cpu_topology(cpu, 1);
+ cpu_topology[cpu].core_id = topology_id;
+
+ /*
+ * In the PPTT, CPUs below a node with the 'identical
+ * implementation' flag have the same number of threads.
+ * Count the number of threads for only one CPU (i.e.
+ * one core_id) among those with the same hetero_id.
+ * See the comment of find_acpi_cpu_topology_hetero_id()
+ * for more details.
+ *
+ * One entry is created for each node having:
+ * - the 'identical implementation' flag
+ * - its parent not having the flag
+ */
+ hetero_id = find_acpi_cpu_topology_hetero_id(cpu);
+ entry = xa_load(&hetero_cpu, hetero_id);
+ if (!entry) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ WARN_ON_ONCE(!entry);
+
+ if (entry) {
+ entry->core_id = topology_id;
+ entry->thread_num = 1;
+ xa_store(&hetero_cpu, hetero_id,
+ entry, GFP_KERNEL);
+ }
+ } else if (entry->core_id == topology_id) {
+ entry->thread_num++;
+ }
+ } else {
+ cpu_topology[cpu].thread_id = -1;
+ cpu_topology[cpu].core_id = topology_id;
+ }
+ topology_id = find_acpi_cpu_topology_cluster(cpu);
+ cpu_topology[cpu].cluster_id = topology_id;
+ topology_id = find_acpi_cpu_topology_package(cpu);
+ cpu_topology[cpu].package_id = topology_id;
+ }
+
+ /*
+ * This is a short loop since the number of XArray elements is the
+ * number of heterogeneous CPU clusters. On a homogeneous system
+ * there's only one entry in the XArray.
+ */
+ xa_for_each(&hetero_cpu, hetero_id, entry) {
+ max_smt_thread_num = max(max_smt_thread_num, entry->thread_num);
+ xa_erase(&hetero_cpu, hetero_id);
+ kfree(entry);
+ }
+
+ cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num);
+ xa_destroy(&hetero_cpu);
return 0;
}
-#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
void __init init_cpu_topology(void)
{
- int ret;
+ int cpu, ret;
reset_cpu_topology();
ret = parse_acpi_topology();
@@ -835,10 +935,38 @@ void __init init_cpu_topology(void)
if (ret) {
/*
* Discard anything that was parsed if we hit an error so we
- * don't use partial information.
+ * don't use partial information. But do not return yet to give
+ * arch-specific early cache level detection a chance to run.
*/
reset_cpu_topology();
+ }
+
+ for_each_possible_cpu(cpu) {
+ ret = fetch_cache_info(cpu);
+ if (!ret)
+ continue;
+ else if (ret != -ENOENT)
+ pr_err("Early cacheinfo failed, ret = %d\n", ret);
return;
}
}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
+
+ if (cpuid_topo->package_id != -1)
+ goto topology_populated;
+
+ cpuid_topo->thread_id = -1;
+ cpuid_topo->core_id = cpuid;
+ cpuid_topo->package_id = cpu_to_node(cpuid);
+
+ pr_debug("CPU%u: package %d core %d thread %d\n",
+ cpuid, cpuid_topo->package_id, cpuid_topo->core_id,
+ cpuid_topo->thread_id);
+
+topology_populated:
+ update_siblings_masks(cpuid);
+}
#endif
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 01ef796c2055..b6f941a6ab69 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -346,8 +346,7 @@ attribute_container_device_trigger_safe(struct device *dev,
* @fn: the function to execute for each classdev.
*
* This function is for executing a trigger when you need to know both
- * the container and the classdev. If you only care about the
- * container, then use attribute_container_trigger() instead.
+ * the container and the classdev.
*/
void
attribute_container_device_trigger(struct device *dev,
@@ -379,33 +378,6 @@ attribute_container_device_trigger(struct device *dev,
}
/**
- * attribute_container_trigger - trigger a function for each matching container
- *
- * @dev: The generic device to activate the trigger for
- * @fn: the function to trigger
- *
- * This routine triggers a function that only needs to know the
- * matching containers (not the classdev) associated with a device.
- * It is more lightweight than attribute_container_device_trigger, so
- * should be used in preference unless the triggering function
- * actually needs to know the classdev.
- */
-void
-attribute_container_trigger(struct device *dev,
- int (*fn)(struct attribute_container *,
- struct device *))
-{
- struct attribute_container *cont;
-
- mutex_lock(&attribute_container_mutex);
- list_for_each_entry(cont, &attribute_container_list, node) {
- if (cont->match(cont, dev))
- fn(cont, dev);
- }
- mutex_unlock(&attribute_container_mutex);
-}
-
-/**
* attribute_container_add_attrs - add attributes
*
* @classdev: The class device
@@ -459,24 +431,6 @@ attribute_container_add_class_device(struct device *classdev)
}
/**
- * attribute_container_add_class_device_adapter - simple adapter for triggers
- *
- * @cont: the container to register.
- * @dev: the generic device to activate the trigger for
- * @classdev: the class device to add
- *
- * This function is identical to attribute_container_add_class_device except
- * that it is designed to be called from the triggers
- */
-int
-attribute_container_add_class_device_adapter(struct attribute_container *cont,
- struct device *dev,
- struct device *classdev)
-{
- return attribute_container_add_class_device(classdev);
-}
-
-/**
* attribute_container_remove_attrs - remove any attribute files
*
* @classdev: The class device to remove the files from
diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c
index 8c5e65930617..04bdbff4dbe5 100644
--- a/drivers/base/auxiliary.c
+++ b/drivers/base/auxiliary.c
@@ -92,7 +92,7 @@
* Auxiliary devices are created and registered by a subsystem-level core
* device that needs to break up its functionality into smaller fragments. One
* way to extend the scope of an auxiliary_device is to encapsulate it within a
- * domain- pecific structure defined by the parent device. This structure
+ * domain-specific structure defined by the parent device. This structure
* contains the auxiliary_device and any associated shared data/callbacks
* needed to establish the connection with the parent.
*
@@ -156,36 +156,47 @@
* },
* .ops = my_custom_ops,
* };
+ *
+ * Please note that such custom ops approach is valid, but it is hard to implement
+ * it right without global locks per-device to protect from auxiliary_drv removal
+ * during call to that ops. In addition, this implementation lacks proper module
+ * dependency, which causes to load/unload races between auxiliary parent and devices
+ * modules.
+ *
+ * The most easiest way to provide these ops reliably without needing to
+ * have a lock is to EXPORT_SYMBOL*() them and rely on already existing
+ * modules infrastructure for validity and correct dependencies chains.
*/
static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
const struct auxiliary_device *auxdev)
{
- for (; id->name[0]; id++) {
- const char *p = strrchr(dev_name(&auxdev->dev), '.');
- int match_size;
+ const char *auxdev_name = dev_name(&auxdev->dev);
+ const char *p = strrchr(auxdev_name, '.');
+ int match_size;
- if (!p)
- continue;
- match_size = p - dev_name(&auxdev->dev);
+ if (!p)
+ return NULL;
+ match_size = p - auxdev_name;
+ for (; id->name[0]; id++) {
/* use dev_name(&auxdev->dev) prefix before last '.' char to match to */
if (strlen(id->name) == match_size &&
- !strncmp(dev_name(&auxdev->dev), id->name, match_size))
+ !strncmp(auxdev_name, id->name, match_size))
return id;
}
return NULL;
}
-static int auxiliary_match(struct device *dev, struct device_driver *drv)
+static int auxiliary_match(struct device *dev, const struct device_driver *drv)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
- struct auxiliary_driver *auxdrv = to_auxiliary_drv(drv);
+ const struct auxiliary_driver *auxdrv = to_auxiliary_drv(drv);
return !!auxiliary_match_id(auxdrv->id_table, auxdev);
}
-static int auxiliary_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int auxiliary_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const char *name, *p;
@@ -203,36 +214,32 @@ static const struct dev_pm_ops auxiliary_dev_pm_ops = {
static int auxiliary_bus_probe(struct device *dev)
{
- struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
+ const struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
int ret;
- ret = dev_pm_domain_attach(dev, true);
+ ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON |
+ PD_FLAG_DETACH_POWER_OFF);
if (ret) {
dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret);
return ret;
}
- ret = auxdrv->probe(auxdev, auxiliary_match_id(auxdrv->id_table, auxdev));
- if (ret)
- dev_pm_domain_detach(dev, true);
-
- return ret;
+ return auxdrv->probe(auxdev, auxiliary_match_id(auxdrv->id_table, auxdev));
}
static void auxiliary_bus_remove(struct device *dev)
{
- struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
+ const struct auxiliary_driver *auxdrv = to_auxiliary_drv(dev->driver);
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
if (auxdrv->remove)
auxdrv->remove(auxdev);
- dev_pm_domain_detach(dev, true);
}
static void auxiliary_bus_shutdown(struct device *dev)
{
- struct auxiliary_driver *auxdrv = NULL;
+ const struct auxiliary_driver *auxdrv = NULL;
struct auxiliary_device *auxdev;
if (dev->driver) {
@@ -244,7 +251,7 @@ static void auxiliary_bus_shutdown(struct device *dev)
auxdrv->shutdown(auxdev);
}
-static struct bus_type auxiliary_bus_type = {
+static const struct bus_type auxiliary_bus_type = {
.name = "auxiliary",
.probe = auxiliary_bus_probe,
.remove = auxiliary_bus_remove,
@@ -287,6 +294,7 @@ int auxiliary_device_init(struct auxiliary_device *auxdev)
dev->bus = &auxiliary_bus_type;
device_initialize(&auxdev->dev);
+ mutex_init(&auxdev->sysfs.lock);
return 0;
}
EXPORT_SYMBOL_GPL(auxiliary_device_init);
@@ -335,35 +343,6 @@ int __auxiliary_device_add(struct auxiliary_device *auxdev, const char *modname)
EXPORT_SYMBOL_GPL(__auxiliary_device_add);
/**
- * auxiliary_find_device - auxiliary device iterator for locating a particular device.
- * @start: Device to begin with
- * @data: Data to pass to match function
- * @match: Callback function to check device
- *
- * This function returns a reference to a device that is 'found'
- * for later use, as determined by the @match callback.
- *
- * The reference returned should be released with put_device().
- *
- * The callback should return 0 if the device doesn't match and non-zero
- * if it does. If the callback returns non-zero, this function will
- * return to the caller and not iterate over any more devices.
- */
-struct auxiliary_device *auxiliary_find_device(struct device *start,
- const void *data,
- int (*match)(struct device *dev, const void *data))
-{
- struct device *dev;
-
- dev = bus_find_device(&auxiliary_bus_type, start, data, match);
- if (!dev)
- return NULL;
-
- return to_auxiliary_dev(dev);
-}
-EXPORT_SYMBOL_GPL(auxiliary_find_device);
-
-/**
* __auxiliary_driver_register - register a driver for auxiliary bus devices
* @auxdrv: auxiliary_driver structure
* @owner: owning module/driver
@@ -413,6 +392,116 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
}
EXPORT_SYMBOL_GPL(auxiliary_driver_unregister);
+static void auxiliary_device_release(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+
+ of_node_put(dev->of_node);
+ kfree(auxdev);
+}
+
+/**
+ * auxiliary_device_create - create a device on the auxiliary bus
+ * @dev: parent device
+ * @modname: module name used to create the auxiliary driver name.
+ * @devname: auxiliary bus device name
+ * @platform_data: auxiliary bus device platform data
+ * @id: auxiliary bus device id
+ *
+ * Helper to create an auxiliary bus device.
+ * The device created matches driver 'modname.devname' on the auxiliary bus.
+ */
+struct auxiliary_device *auxiliary_device_create(struct device *dev,
+ const char *modname,
+ const char *devname,
+ void *platform_data,
+ int id)
+{
+ struct auxiliary_device *auxdev;
+ int ret;
+
+ auxdev = kzalloc(sizeof(*auxdev), GFP_KERNEL);
+ if (!auxdev)
+ return NULL;
+
+ auxdev->id = id;
+ auxdev->name = devname;
+ auxdev->dev.parent = dev;
+ auxdev->dev.platform_data = platform_data;
+ auxdev->dev.release = auxiliary_device_release;
+ device_set_of_node_from_dev(&auxdev->dev, dev);
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret) {
+ of_node_put(auxdev->dev.of_node);
+ kfree(auxdev);
+ return NULL;
+ }
+
+ ret = __auxiliary_device_add(auxdev, modname);
+ if (ret) {
+ /*
+ * It may look odd but auxdev should not be freed here.
+ * auxiliary_device_uninit() calls device_put() which call
+ * the device release function, freeing auxdev.
+ */
+ auxiliary_device_uninit(auxdev);
+ return NULL;
+ }
+
+ return auxdev;
+}
+EXPORT_SYMBOL_GPL(auxiliary_device_create);
+
+/**
+ * auxiliary_device_destroy - remove an auxiliary device
+ * @auxdev: pointer to the auxdev to be removed
+ *
+ * Helper to remove an auxiliary device created with
+ * auxiliary_device_create()
+ */
+void auxiliary_device_destroy(void *auxdev)
+{
+ struct auxiliary_device *_auxdev = auxdev;
+
+ auxiliary_device_delete(_auxdev);
+ auxiliary_device_uninit(_auxdev);
+}
+EXPORT_SYMBOL_GPL(auxiliary_device_destroy);
+
+/**
+ * __devm_auxiliary_device_create - create a managed device on the auxiliary bus
+ * @dev: parent device
+ * @modname: module name used to create the auxiliary driver name.
+ * @devname: auxiliary bus device name
+ * @platform_data: auxiliary bus device platform data
+ * @id: auxiliary bus device id
+ *
+ * Device managed helper to create an auxiliary bus device.
+ * The device created matches driver 'modname.devname' on the auxiliary bus.
+ */
+struct auxiliary_device *__devm_auxiliary_device_create(struct device *dev,
+ const char *modname,
+ const char *devname,
+ void *platform_data,
+ int id)
+{
+ struct auxiliary_device *auxdev;
+ int ret;
+
+ auxdev = auxiliary_device_create(dev, modname, devname, platform_data, id);
+ if (!auxdev)
+ return NULL;
+
+ ret = devm_add_action_or_reset(dev, auxiliary_device_destroy,
+ auxdev);
+ if (ret)
+ return NULL;
+
+ return auxdev;
+}
+EXPORT_SYMBOL_GPL(__devm_auxiliary_device_create);
+
void __init auxiliary_bus_init(void)
{
WARN_ON(bus_register(&auxiliary_bus_type));
diff --git a/drivers/base/auxiliary_sysfs.c b/drivers/base/auxiliary_sysfs.c
new file mode 100644
index 000000000000..754f21730afd
--- /dev/null
+++ b/drivers/base/auxiliary_sysfs.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/slab.h>
+
+#define AUXILIARY_MAX_IRQ_NAME 11
+
+struct auxiliary_irq_info {
+ struct device_attribute sysfs_attr;
+ char name[AUXILIARY_MAX_IRQ_NAME];
+};
+
+static struct attribute *auxiliary_irq_attrs[] = {
+ NULL
+};
+
+static const struct attribute_group auxiliary_irqs_group = {
+ .name = "irqs",
+ .attrs = auxiliary_irq_attrs,
+};
+
+static int auxiliary_irq_dir_prepare(struct auxiliary_device *auxdev)
+{
+ int ret = 0;
+
+ guard(mutex)(&auxdev->sysfs.lock);
+ if (auxdev->sysfs.irq_dir_exists)
+ return 0;
+
+ ret = devm_device_add_group(&auxdev->dev, &auxiliary_irqs_group);
+ if (ret)
+ return ret;
+
+ auxdev->sysfs.irq_dir_exists = true;
+ xa_init(&auxdev->sysfs.irqs);
+ return 0;
+}
+
+/**
+ * auxiliary_device_sysfs_irq_add - add a sysfs entry for the given IRQ
+ * @auxdev: auxiliary bus device to add the sysfs entry.
+ * @irq: The associated interrupt number.
+ *
+ * This function should be called after auxiliary device have successfully
+ * received the irq.
+ * The driver is responsible to add a unique irq for the auxiliary device. The
+ * driver can invoke this function from multiple thread context safely for
+ * unique irqs of the auxiliary devices. The driver must not invoke this API
+ * multiple times if the irq is already added previously.
+ *
+ * Return: zero on success or an error code on failure.
+ */
+int auxiliary_device_sysfs_irq_add(struct auxiliary_device *auxdev, int irq)
+{
+ struct auxiliary_irq_info *info __free(kfree) = NULL;
+ struct device *dev = &auxdev->dev;
+ int ret;
+
+ ret = auxiliary_irq_dir_prepare(auxdev);
+ if (ret)
+ return ret;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ sysfs_attr_init(&info->sysfs_attr.attr);
+ snprintf(info->name, AUXILIARY_MAX_IRQ_NAME, "%d", irq);
+
+ ret = xa_insert(&auxdev->sysfs.irqs, irq, info, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ info->sysfs_attr.attr.name = info->name;
+ ret = sysfs_add_file_to_group(&dev->kobj, &info->sysfs_attr.attr,
+ auxiliary_irqs_group.name);
+ if (ret)
+ goto sysfs_add_err;
+
+ xa_store(&auxdev->sysfs.irqs, irq, no_free_ptr(info), GFP_KERNEL);
+ return 0;
+
+sysfs_add_err:
+ xa_erase(&auxdev->sysfs.irqs, irq);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_add);
+
+/**
+ * auxiliary_device_sysfs_irq_remove - remove a sysfs entry for the given IRQ
+ * @auxdev: auxiliary bus device to add the sysfs entry.
+ * @irq: the IRQ to remove.
+ *
+ * This function should be called to remove an IRQ sysfs entry.
+ * The driver must invoke this API when IRQ is released by the device.
+ */
+void auxiliary_device_sysfs_irq_remove(struct auxiliary_device *auxdev, int irq)
+{
+ struct auxiliary_irq_info *info __free(kfree) = xa_load(&auxdev->sysfs.irqs, irq);
+ struct device *dev = &auxdev->dev;
+
+ if (!info) {
+ dev_err(&auxdev->dev, "IRQ %d doesn't exist\n", irq);
+ return;
+ }
+ sysfs_remove_file_from_group(&dev->kobj, &info->sysfs_attr.attr,
+ auxiliary_irqs_group.name);
+ xa_erase(&auxdev->sysfs.irqs, irq);
+}
+EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_remove);
diff --git a/drivers/base/base.h b/drivers/base/base.h
index b3a43a164dcd..430cbefbc97f 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -27,11 +27,13 @@
* on this bus.
* @bus - pointer back to the struct bus_type that this structure is associated
* with.
+ * @dev_root: Default device to use as the parent.
*
* @glue_dirs - "glue" directory to put in-between the parent device to
* avoid namespace conflicts
* @class - pointer back to the struct class that this structure is associated
* with.
+ * @lock_key: Lock class key for use by the lock validator
*
* This structure is the one that is the actual kobject allowing struct
* bus_type/class to be statically allocated safely. Nothing outside of the
@@ -48,12 +50,31 @@ struct subsys_private {
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
- struct bus_type *bus;
+ const struct bus_type *bus;
+ struct device *dev_root;
struct kset glue_dirs;
- struct class *class;
+ const struct class *class;
+
+ struct lock_class_key lock_key;
};
-#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
+#define to_subsys_private(obj) container_of_const(obj, struct subsys_private, subsys.kobj)
+
+static inline struct subsys_private *subsys_get(struct subsys_private *sp)
+{
+ if (sp)
+ kset_get(&sp->subsys);
+ return sp;
+}
+
+static inline void subsys_put(struct subsys_private *sp)
+{
+ if (sp)
+ kset_put(&sp->subsys);
+}
+
+struct subsys_private *bus_to_subsys(const struct bus_type *bus);
+struct subsys_private *class_to_subsys(const struct class *class);
struct driver_private {
struct kobject kobj;
@@ -64,6 +85,18 @@ struct driver_private {
};
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+#ifdef CONFIG_RUST
+/**
+ * struct driver_type - Representation of a Rust driver type.
+ */
+struct driver_type {
+ /**
+ * @id: Representation of core::any::TypeId.
+ */
+ u8 id[16];
+} __packed;
+#endif
+
/**
* struct device_private - structure to hold the private to the driver core portions of the device structure.
*
@@ -79,6 +112,7 @@ struct driver_private {
* @async_driver - pointer to device driver awaiting probe via async_probe
* @device - pointer back to the struct device that this structure is
* associated with.
+ * @driver_type - The type of the bound Rust driver.
* @dead - This device is currently either in the process of or has been
* removed from the system. Any asynchronous events scheduled for this
* device should exit without taking any action.
@@ -92,9 +126,12 @@ struct device_private {
struct klist_node knode_bus;
struct klist_node knode_class;
struct list_head deferred_probe;
- struct device_driver *async_driver;
+ const struct device_driver *async_driver;
char *deferred_probe_reason;
struct device *device;
+#ifdef CONFIG_RUST
+ struct driver_type driver_type;
+#endif
u8 dead:1;
};
#define to_device_private_parent(obj) \
@@ -107,92 +144,137 @@ struct device_private {
container_of(obj, struct device_private, knode_class)
/* initialisation functions */
-extern int devices_init(void);
-extern int buses_init(void);
-extern int classes_init(void);
-extern int firmware_init(void);
+int devices_init(void);
+int buses_init(void);
+int classes_init(void);
+int firmware_init(void);
#ifdef CONFIG_SYS_HYPERVISOR
-extern int hypervisor_init(void);
+int hypervisor_init(void);
#else
static inline int hypervisor_init(void) { return 0; }
#endif
-extern int platform_bus_init(void);
-extern void cpu_dev_init(void);
-extern void container_dev_init(void);
+int platform_bus_init(void);
+int faux_bus_init(void);
+void cpu_dev_init(void);
+void container_dev_init(void);
#ifdef CONFIG_AUXILIARY_BUS
-extern void auxiliary_bus_init(void);
+void auxiliary_bus_init(void);
#else
static inline void auxiliary_bus_init(void) { }
#endif
-struct kobject *virtual_device_parent(struct device *dev);
+struct kobject *virtual_device_parent(void);
-extern int bus_add_device(struct device *dev);
-extern void bus_probe_device(struct device *dev);
-extern void bus_remove_device(struct device *dev);
+int bus_add_device(struct device *dev);
+void bus_probe_device(struct device *dev);
+void bus_remove_device(struct device *dev);
+void bus_notify(struct device *dev, enum bus_notifier_event value);
+bool bus_is_registered(const struct bus_type *bus);
-extern int bus_add_driver(struct device_driver *drv);
-extern void bus_remove_driver(struct device_driver *drv);
-extern void device_release_driver_internal(struct device *dev,
- struct device_driver *drv,
- struct device *parent);
+int bus_add_driver(struct device_driver *drv);
+void bus_remove_driver(struct device_driver *drv);
+void device_release_driver_internal(struct device *dev, const struct device_driver *drv,
+ struct device *parent);
-extern void driver_detach(struct device_driver *drv);
-extern void driver_deferred_probe_del(struct device *dev);
-extern void device_set_deferred_probe_reason(const struct device *dev,
- struct va_format *vaf);
-static inline int driver_match_device(struct device_driver *drv,
+void driver_detach(const struct device_driver *drv);
+void driver_deferred_probe_del(struct device *dev);
+void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf);
+static inline int driver_match_device(const struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
-extern bool driver_allows_async_probing(struct device_driver *drv);
-extern int driver_add_groups(struct device_driver *drv,
- const struct attribute_group **groups);
-extern void driver_remove_groups(struct device_driver *drv,
- const struct attribute_group **groups);
+static inline void dev_sync_state(struct device *dev)
+{
+ if (dev->bus->sync_state)
+ dev->bus->sync_state(dev);
+ else if (dev->driver && dev->driver->sync_state)
+ dev->driver->sync_state(dev);
+}
+
+int driver_add_groups(const struct device_driver *drv, const struct attribute_group **groups);
+void driver_remove_groups(const struct device_driver *drv, const struct attribute_group **groups);
void device_driver_detach(struct device *dev);
-extern char *make_class_name(const char *name, struct kobject *kobj);
+static inline void device_set_driver(struct device *dev, const struct device_driver *drv)
+{
+ /*
+ * Majority (all?) read accesses to dev->driver happens either
+ * while holding device lock or in bus/driver code that is only
+ * invoked when the device is bound to a driver and there is no
+ * concern of the pointer being changed while it is being read.
+ * However when reading device's uevent file we read driver pointer
+ * without taking device lock (so we do not block there for
+ * arbitrary amount of time). We use WRITE_ONCE() here to prevent
+ * tearing so that READ_ONCE() can safely be used in uevent code.
+ */
+ // FIXME - this cast should not be needed "soon"
+ WRITE_ONCE(dev->driver, (struct device_driver *)drv);
+}
-extern int devres_release_all(struct device *dev);
-extern void device_block_probing(void);
-extern void device_unblock_probing(void);
-extern void deferred_probe_extend_timeout(void);
-extern void driver_deferred_probe_trigger(void);
+int devres_release_all(struct device *dev);
+void device_block_probing(void);
+void device_unblock_probing(void);
+void deferred_probe_extend_timeout(void);
+void driver_deferred_probe_trigger(void);
+const char *device_get_devnode(const struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid, const char **tmp);
/* /sys/devices directory */
extern struct kset *devices_kset;
-extern void devices_kset_move_last(struct device *dev);
+void devices_kset_move_last(struct device *dev);
#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
-extern void module_add_driver(struct module *mod, struct device_driver *drv);
-extern void module_remove_driver(struct device_driver *drv);
+int module_add_driver(struct module *mod, const struct device_driver *drv);
+void module_remove_driver(const struct device_driver *drv);
#else
-static inline void module_add_driver(struct module *mod,
- struct device_driver *drv) { }
+static inline int module_add_driver(struct module *mod,
+ struct device_driver *drv)
+{
+ return 0;
+}
static inline void module_remove_driver(struct device_driver *drv) { }
#endif
#ifdef CONFIG_DEVTMPFS
-extern int devtmpfs_init(void);
+int devtmpfs_init(void);
#else
static inline int devtmpfs_init(void) { return 0; }
#endif
+#ifdef CONFIG_BLOCK
+extern const struct class block_class;
+static inline bool is_blockdev(struct device *dev)
+{
+ return dev->class == &block_class;
+}
+#else
+static inline bool is_blockdev(struct device *dev) { return false; }
+#endif
+
/* Device links support */
-extern int device_links_read_lock(void);
-extern void device_links_read_unlock(int idx);
-extern int device_links_read_lock_held(void);
-extern int device_links_check_suppliers(struct device *dev);
-extern void device_links_force_bind(struct device *dev);
-extern void device_links_driver_bound(struct device *dev);
-extern void device_links_driver_cleanup(struct device *dev);
-extern void device_links_no_driver(struct device *dev);
-extern bool device_links_busy(struct device *dev);
-extern void device_links_unbind_consumers(struct device *dev);
-extern void fw_devlink_drivers_done(void);
+int device_links_read_lock(void);
+void device_links_read_unlock(int idx);
+int device_links_read_lock_held(void);
+int device_links_check_suppliers(struct device *dev);
+void device_links_force_bind(struct device *dev);
+void device_links_driver_bound(struct device *dev);
+void device_links_driver_cleanup(struct device *dev);
+void device_links_no_driver(struct device *dev);
+bool device_links_busy(struct device *dev);
+void device_links_unbind_consumers(struct device *dev);
+bool device_link_flag_is_sync_state_only(u32 flags);
+void fw_devlink_drivers_done(void);
+void fw_devlink_probing_done(void);
+
+#define dev_for_each_link_to_supplier(__link, __dev) \
+ list_for_each_entry_srcu(__link, &(__dev)->links.suppliers, c_node, \
+ device_links_read_lock_held())
+
+#define dev_for_each_link_to_consumer(__link, __dev) \
+ list_for_each_entry_srcu(__link, &(__dev)->links.consumers, s_node, \
+ device_links_read_lock_held())
/* device pm support */
void device_pm_move_to_tail(struct device *dev);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 7ca47e5b3c1f..9eb7771706f0 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -6,6 +6,7 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2007 Novell Inc.
+ * Copyright (c) 2023 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
*/
#include <linux/async.h>
@@ -24,6 +25,9 @@
/* /sys/devices/system */
static struct kset *system_kset;
+/* /sys/bus */
+static struct kset *bus_kset;
+
#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
/*
@@ -39,19 +43,63 @@ static struct kset *system_kset;
static int __must_check bus_rescan_devices_helper(struct device *dev,
void *data);
-static struct bus_type *bus_get(struct bus_type *bus)
+/**
+ * bus_to_subsys - Turn a struct bus_type into a struct subsys_private
+ *
+ * @bus: pointer to the struct bus_type to look up
+ *
+ * The driver core internals needs to work on the subsys_private structure, not
+ * the external struct bus_type pointer. This function walks the list of
+ * registered busses in the system and finds the matching one and returns the
+ * internal struct subsys_private that relates to that bus.
+ *
+ * Note, the reference count of the return value is INCREMENTED if it is not
+ * NULL. A call to subsys_put() must be done when finished with the pointer in
+ * order for it to be properly freed.
+ */
+struct subsys_private *bus_to_subsys(const struct bus_type *bus)
{
- if (bus) {
- kset_get(&bus->p->subsys);
- return bus;
+ struct subsys_private *sp = NULL;
+ struct kobject *kobj;
+
+ if (!bus || !bus_kset)
+ return NULL;
+
+ spin_lock(&bus_kset->list_lock);
+
+ if (list_empty(&bus_kset->list))
+ goto done;
+
+ list_for_each_entry(kobj, &bus_kset->list, entry) {
+ struct kset *kset = container_of(kobj, struct kset, kobj);
+
+ sp = container_of_const(kset, struct subsys_private, subsys);
+ if (sp->bus == bus)
+ goto done;
}
+ sp = NULL;
+done:
+ sp = subsys_get(sp);
+ spin_unlock(&bus_kset->list_lock);
+ return sp;
+}
+
+static const struct bus_type *bus_get(const struct bus_type *bus)
+{
+ struct subsys_private *sp = bus_to_subsys(bus);
+
+ if (sp)
+ return bus;
return NULL;
}
-static void bus_put(struct bus_type *bus)
+static void bus_put(const struct bus_type *bus)
{
- if (bus)
- kset_put(&bus->p->subsys);
+ struct subsys_private *sp = bus_to_subsys(bus);
+
+ /* two puts are required as the call to bus_to_subsys incremented it again */
+ subsys_put(sp);
+ subsys_put(sp);
}
static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
@@ -91,7 +139,7 @@ static void driver_release(struct kobject *kobj)
kfree(drv_priv);
}
-static struct kobj_type driver_ktype = {
+static const struct kobj_type driver_ktype = {
.sysfs_ops = &driver_sysfs_ops,
.release = driver_release,
};
@@ -104,7 +152,8 @@ static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct subsys_private *subsys_priv = to_subsys_private(kobj);
- ssize_t ret = 0;
+ /* return -EIO for reading a bus attribute without show() */
+ ssize_t ret = -EIO;
if (bus_attr->show)
ret = bus_attr->show(subsys_priv->bus, buf);
@@ -116,7 +165,8 @@ static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct subsys_private *subsys_priv = to_subsys_private(kobj);
- ssize_t ret = 0;
+ /* return -EIO for writing a bus attribute without store() */
+ ssize_t ret = -EIO;
if (bus_attr->store)
ret = bus_attr->store(subsys_priv->bus, buf, count);
@@ -128,42 +178,47 @@ static const struct sysfs_ops bus_sysfs_ops = {
.store = bus_attr_store,
};
-int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
+int bus_create_file(const struct bus_type *bus, struct bus_attribute *attr)
{
+ struct subsys_private *sp = bus_to_subsys(bus);
int error;
- if (bus_get(bus)) {
- error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
- bus_put(bus);
- } else
- error = -EINVAL;
+
+ if (!sp)
+ return -EINVAL;
+
+ error = sysfs_create_file(&sp->subsys.kobj, &attr->attr);
+
+ subsys_put(sp);
return error;
}
EXPORT_SYMBOL_GPL(bus_create_file);
-void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
+void bus_remove_file(const struct bus_type *bus, struct bus_attribute *attr)
{
- if (bus_get(bus)) {
- sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
- bus_put(bus);
- }
+ struct subsys_private *sp = bus_to_subsys(bus);
+
+ if (!sp)
+ return;
+
+ sysfs_remove_file(&sp->subsys.kobj, &attr->attr);
+ subsys_put(sp);
}
EXPORT_SYMBOL_GPL(bus_remove_file);
static void bus_release(struct kobject *kobj)
{
struct subsys_private *priv = to_subsys_private(kobj);
- struct bus_type *bus = priv->bus;
+ lockdep_unregister_key(&priv->lock_key);
kfree(priv);
- bus->p = NULL;
}
-static struct kobj_type bus_ktype = {
+static const struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
.release = bus_release,
};
-static int bus_uevent_filter(struct kobject *kobj)
+static int bus_uevent_filter(const struct kobject *kobj)
{
const struct kobj_type *ktype = get_ktype(kobj);
@@ -176,13 +231,11 @@ static const struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
-static struct kset *bus_kset;
-
/* Manually detach a device from its associated driver. */
static ssize_t unbind_store(struct device_driver *drv, const char *buf,
size_t count)
{
- struct bus_type *bus = bus_get(drv->bus);
+ const struct bus_type *bus = bus_get(drv->bus);
struct device *dev;
int err = -ENODEV;
@@ -205,7 +258,7 @@ static DRIVER_ATTR_IGNORE_LOCKDEP(unbind, 0200, NULL, unbind_store);
static ssize_t bind_store(struct device_driver *drv, const char *buf,
size_t count)
{
- struct bus_type *bus = bus_get(drv->bus);
+ const struct bus_type *bus = bus_get(drv->bus);
struct device *dev;
int err = -ENODEV;
@@ -223,22 +276,37 @@ static ssize_t bind_store(struct device_driver *drv, const char *buf,
}
static DRIVER_ATTR_IGNORE_LOCKDEP(bind, 0200, NULL, bind_store);
-static ssize_t drivers_autoprobe_show(struct bus_type *bus, char *buf)
+static ssize_t drivers_autoprobe_show(const struct bus_type *bus, char *buf)
{
- return sysfs_emit(buf, "%d\n", bus->p->drivers_autoprobe);
+ struct subsys_private *sp = bus_to_subsys(bus);
+ int ret;
+
+ if (!sp)
+ return -EINVAL;
+
+ ret = sysfs_emit(buf, "%d\n", sp->drivers_autoprobe);
+ subsys_put(sp);
+ return ret;
}
-static ssize_t drivers_autoprobe_store(struct bus_type *bus,
+static ssize_t drivers_autoprobe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
+ struct subsys_private *sp = bus_to_subsys(bus);
+
+ if (!sp)
+ return -EINVAL;
+
if (buf[0] == '0')
- bus->p->drivers_autoprobe = 0;
+ sp->drivers_autoprobe = 0;
else
- bus->p->drivers_autoprobe = 1;
+ sp->drivers_autoprobe = 1;
+
+ subsys_put(sp);
return count;
}
-static ssize_t drivers_probe_store(struct bus_type *bus,
+static ssize_t drivers_probe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
@@ -266,6 +334,19 @@ static struct device *next_device(struct klist_iter *i)
return dev;
}
+static struct device *prev_device(struct klist_iter *i)
+{
+ struct klist_node *n = klist_prev(i);
+ struct device *dev = NULL;
+ struct device_private *dev_prv;
+
+ if (n) {
+ dev_prv = to_device_private_bus(n);
+ dev = dev_prv->device;
+ }
+ return dev;
+}
+
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
@@ -285,21 +366,23 @@ static struct device *next_device(struct klist_iter *i)
* to retain this data, it should do so, and increment the reference
* count in the supplied callback.
*/
-int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
+int bus_for_each_dev(const struct bus_type *bus, struct device *start,
+ void *data, device_iter_t fn)
{
+ struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
struct device *dev;
int error = 0;
- if (!bus || !bus->p)
+ if (!sp)
return -EINVAL;
- klist_iter_init_node(&bus->p->klist_devices, &i,
+ klist_iter_init_node(&sp->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
+ subsys_put(sp);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
@@ -319,66 +402,55 @@ EXPORT_SYMBOL_GPL(bus_for_each_dev);
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
-struct device *bus_find_device(struct bus_type *bus,
+struct device *bus_find_device(const struct bus_type *bus,
struct device *start, const void *data,
- int (*match)(struct device *dev, const void *data))
+ device_match_t match)
{
+ struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
struct device *dev;
- if (!bus || !bus->p)
+ if (!sp)
return NULL;
- klist_iter_init_node(&bus->p->klist_devices, &i,
+ klist_iter_init_node(&sp->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)))
- if (match(dev, data) && get_device(dev))
+ while ((dev = next_device(&i))) {
+ if (match(dev, data)) {
+ get_device(dev);
break;
+ }
+ }
klist_iter_exit(&i);
+ subsys_put(sp);
return dev;
}
EXPORT_SYMBOL_GPL(bus_find_device);
-/**
- * subsys_find_device_by_id - find a device with a specific enumeration number
- * @subsys: subsystem
- * @id: index 'id' in struct device
- * @hint: device to check first
- *
- * Check the hint's next object and if it is a match return it directly,
- * otherwise, fall back to a full list search. Either way a reference for
- * the returned object is taken.
- */
-struct device *subsys_find_device_by_id(struct bus_type *subsys, unsigned int id,
- struct device *hint)
+struct device *bus_find_device_reverse(const struct bus_type *bus,
+ struct device *start, const void *data,
+ device_match_t match)
{
+ struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
struct device *dev;
- if (!subsys)
+ if (!sp)
return NULL;
- if (hint) {
- klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus);
- dev = next_device(&i);
- if (dev && dev->id == id && get_device(dev)) {
- klist_iter_exit(&i);
- return dev;
- }
- klist_iter_exit(&i);
- }
-
- klist_iter_init_node(&subsys->p->klist_devices, &i, NULL);
- while ((dev = next_device(&i))) {
- if (dev->id == id && get_device(dev)) {
- klist_iter_exit(&i);
- return dev;
+ klist_iter_init_node(&sp->klist_devices, &i,
+ (start ? &start->p->knode_bus : NULL));
+ while ((dev = prev_device(&i))) {
+ if (match(dev, data)) {
+ get_device(dev);
+ break;
}
}
klist_iter_exit(&i);
- return NULL;
+ subsys_put(sp);
+ return dev;
}
-EXPORT_SYMBOL_GPL(subsys_find_device_by_id);
+EXPORT_SYMBOL_GPL(bus_find_device_reverse);
static struct device_driver *next_driver(struct klist_iter *i)
{
@@ -411,21 +483,23 @@ static struct device_driver *next_driver(struct klist_iter *i)
* in the callback. It must also be sure to increment the refcount
* so it doesn't disappear before returning to the caller.
*/
-int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
+int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
+ struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
struct device_driver *drv;
int error = 0;
- if (!bus)
+ if (!sp)
return -EINVAL;
- klist_iter_init_node(&bus->p->klist_drivers, &i,
+ klist_iter_init_node(&sp->klist_drivers, &i,
start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
+ subsys_put(sp);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_drv);
@@ -440,32 +514,46 @@ EXPORT_SYMBOL_GPL(bus_for_each_drv);
*/
int bus_add_device(struct device *dev)
{
- struct bus_type *bus = bus_get(dev->bus);
- int error = 0;
+ struct subsys_private *sp = bus_to_subsys(dev->bus);
+ int error;
- if (bus) {
- pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
- error = device_add_groups(dev, bus->dev_groups);
- if (error)
- goto out_put;
- error = sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_groups;
- error = sysfs_create_link(&dev->kobj,
- &dev->bus->p->subsys.kobj, "subsystem");
- if (error)
- goto out_subsys;
- klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
+ if (!sp) {
+ /*
+ * This is a normal operation for many devices that do not
+ * have a bus assigned to them, just say that all went
+ * well.
+ */
+ return 0;
}
+
+ /*
+ * Reference in sp is now incremented and will be dropped when
+ * the device is removed from the bus
+ */
+
+ pr_debug("bus: '%s': add device %s\n", sp->bus->name, dev_name(dev));
+
+ error = device_add_groups(dev, sp->bus->dev_groups);
+ if (error)
+ goto out_put;
+
+ error = sysfs_create_link(&sp->devices_kset->kobj, &dev->kobj, dev_name(dev));
+ if (error)
+ goto out_groups;
+
+ error = sysfs_create_link(&dev->kobj, &sp->subsys.kobj, "subsystem");
+ if (error)
+ goto out_subsys;
+
+ klist_add_tail(&dev->p->knode_bus, &sp->klist_devices);
return 0;
out_subsys:
- sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
+ sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev));
out_groups:
- device_remove_groups(dev, bus->dev_groups);
+ device_remove_groups(dev, sp->bus->dev_groups);
out_put:
- bus_put(dev->bus);
+ subsys_put(sp);
return error;
}
@@ -477,20 +565,20 @@ out_put:
*/
void bus_probe_device(struct device *dev)
{
- struct bus_type *bus = dev->bus;
+ struct subsys_private *sp = bus_to_subsys(dev->bus);
struct subsys_interface *sif;
- if (!bus)
+ if (!sp)
return;
- if (bus->p->drivers_autoprobe)
- device_initial_probe(dev);
+ device_initial_probe(dev);
- mutex_lock(&bus->p->mutex);
- list_for_each_entry(sif, &bus->p->interfaces, node)
+ mutex_lock(&sp->mutex);
+ list_for_each_entry(sif, &sp->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
- mutex_unlock(&bus->p->mutex);
+ mutex_unlock(&sp->mutex);
+ subsys_put(sp);
}
/**
@@ -505,21 +593,20 @@ void bus_probe_device(struct device *dev)
*/
void bus_remove_device(struct device *dev)
{
- struct bus_type *bus = dev->bus;
+ struct subsys_private *sp = bus_to_subsys(dev->bus);
struct subsys_interface *sif;
- if (!bus)
+ if (!sp)
return;
- mutex_lock(&bus->p->mutex);
- list_for_each_entry(sif, &bus->p->interfaces, node)
+ mutex_lock(&sp->mutex);
+ list_for_each_entry(sif, &sp->interfaces, node)
if (sif->remove_dev)
sif->remove_dev(dev, sif);
- mutex_unlock(&bus->p->mutex);
+ mutex_unlock(&sp->mutex);
sysfs_remove_link(&dev->kobj, "subsystem");
- sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
- dev_name(dev));
+ sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev));
device_remove_groups(dev, dev->bus->dev_groups);
if (klist_node_attached(&dev->p->knode_bus))
klist_del(&dev->p->knode_bus);
@@ -527,7 +614,14 @@ void bus_remove_device(struct device *dev)
pr_debug("bus: '%s': remove device %s\n",
dev->bus->name, dev_name(dev));
device_release_driver(dev);
- bus_put(dev->bus);
+
+ /*
+ * Decrement the reference count twice, once for the bus_to_subsys()
+ * call in the start of this function, and the second one from the
+ * reference increment in bus_add_device()
+ */
+ subsys_put(sp);
+ subsys_put(sp);
}
static int __must_check add_bind_files(struct device_driver *drv)
@@ -552,7 +646,7 @@ static void remove_bind_files(struct device_driver *drv)
static BUS_ATTR_WO(drivers_probe);
static BUS_ATTR_RW(drivers_autoprobe);
-static int add_probe_files(struct bus_type *bus)
+static int add_probe_files(const struct bus_type *bus)
{
int retval;
@@ -567,7 +661,7 @@ out:
return retval;
}
-static void remove_probe_files(struct bus_type *bus)
+static void remove_probe_files(const struct bus_type *bus)
{
bus_remove_file(bus, &bus_attr_drivers_autoprobe);
bus_remove_file(bus, &bus_attr_drivers_probe);
@@ -589,15 +683,18 @@ static DRIVER_ATTR_WO(uevent);
*/
int bus_add_driver(struct device_driver *drv)
{
- struct bus_type *bus;
+ struct subsys_private *sp = bus_to_subsys(drv->bus);
struct driver_private *priv;
int error = 0;
- bus = bus_get(drv->bus);
- if (!bus)
+ if (!sp)
return -EINVAL;
- pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
+ /*
+ * Reference in sp is now incremented and will be dropped when
+ * the driver is removed from the bus
+ */
+ pr_debug("bus: '%s': add driver %s\n", sp->bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
@@ -607,26 +704,31 @@ int bus_add_driver(struct device_driver *drv)
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
- priv->kobj.kset = bus->p->drivers_kset;
+ priv->kobj.kset = sp->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- if (drv->bus->p->drivers_autoprobe) {
+ klist_add_tail(&priv->knode_bus, &sp->klist_drivers);
+ if (sp->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_del_list;
}
- module_add_driver(drv->owner, drv);
+ error = module_add_driver(drv->owner, drv);
+ if (error) {
+ printk(KERN_ERR "%s: failed to create module links for %s\n",
+ __func__, drv->name);
+ goto out_detach;
+ }
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
- error = driver_add_groups(drv, bus->drv_groups);
+ error = driver_add_groups(drv, sp->bus->drv_groups);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_groups(%s) failed\n",
@@ -644,6 +746,8 @@ int bus_add_driver(struct device_driver *drv)
return 0;
+out_detach:
+ driver_detach(drv);
out_del_list:
klist_del(&priv->knode_bus);
out_unregister:
@@ -651,7 +755,7 @@ out_unregister:
/* drv->p is freed in driver_release() */
drv->p = NULL;
out_put_bus:
- bus_put(bus);
+ subsys_put(sp);
return error;
}
@@ -665,19 +769,29 @@ out_put_bus:
*/
void bus_remove_driver(struct device_driver *drv)
{
- if (!drv->bus)
+ struct subsys_private *sp = bus_to_subsys(drv->bus);
+
+ if (!sp)
return;
+ pr_debug("bus: '%s': remove driver %s\n", sp->bus->name, drv->name);
+
if (!drv->suppress_bind_attrs)
remove_bind_files(drv);
- driver_remove_groups(drv, drv->bus->drv_groups);
+ driver_remove_groups(drv, sp->bus->drv_groups);
driver_remove_file(drv, &driver_attr_uevent);
klist_remove(&drv->p->knode_bus);
- pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name);
driver_detach(drv);
module_remove_driver(drv);
kobject_put(&drv->p->kobj);
- bus_put(drv->bus);
+
+ /*
+ * Decrement the reference count twice, once for the bus_to_subsys()
+ * call in the start of this function, and the second one from the
+ * reference increment in bus_add_driver()
+ */
+ subsys_put(sp);
+ subsys_put(sp);
}
/* Helper for bus_rescan_devices's iter */
@@ -704,7 +818,7 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,
* attached and rescan it against existing drivers to see if it matches
* any by calling device_attach() for the unbound devices.
*/
-int bus_rescan_devices(struct bus_type *bus)
+int bus_rescan_devices(const struct bus_type *bus)
{
return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
}
@@ -727,18 +841,6 @@ int device_reprobe(struct device *dev)
}
EXPORT_SYMBOL_GPL(device_reprobe);
-static int bus_add_groups(struct bus_type *bus,
- const struct attribute_group **groups)
-{
- return sysfs_create_groups(&bus->p->subsys.kobj, groups);
-}
-
-static void bus_remove_groups(struct bus_type *bus,
- const struct attribute_group **groups)
-{
- sysfs_remove_groups(&bus->p->subsys.kobj, groups);
-}
-
static void klist_devices_get(struct klist_node *n)
{
struct device_private *dev_prv = to_device_private_bus(n);
@@ -755,13 +857,21 @@ static void klist_devices_put(struct klist_node *n)
put_device(dev);
}
-static ssize_t bus_uevent_store(struct bus_type *bus,
+static ssize_t bus_uevent_store(const struct bus_type *bus,
const char *buf, size_t count)
{
- int rc;
+ struct subsys_private *sp = bus_to_subsys(bus);
+ int ret;
- rc = kobject_synth_uevent(&bus->p->subsys.kobj, buf, count);
- return rc ? rc : count;
+ if (!sp)
+ return -EINVAL;
+
+ ret = kobject_synth_uevent(&sp->subsys.kobj, buf, count);
+ subsys_put(sp);
+
+ if (ret)
+ return ret;
+ return count;
}
/*
* "open code" the old BUS_ATTR() macro here. We want to use BUS_ATTR_WO()
@@ -780,27 +890,28 @@ static struct bus_attribute bus_attr_uevent = __ATTR(uevent, 0200, NULL,
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
-int bus_register(struct bus_type *bus)
+int bus_register(const struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
- struct lock_class_key *key = &bus->lock_key;
+ struct kobject *bus_kobj;
+ struct lock_class_key *key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
- bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
- retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
+ bus_kobj = &priv->subsys.kobj;
+ retval = kobject_set_name(bus_kobj, "%s", bus->name);
if (retval)
goto out;
- priv->subsys.kobj.kset = bus_kset;
- priv->subsys.kobj.ktype = &bus_ktype;
+ bus_kobj->kset = bus_kset;
+ bus_kobj->ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
@@ -811,21 +922,21 @@ int bus_register(struct bus_type *bus)
if (retval)
goto bus_uevent_fail;
- priv->devices_kset = kset_create_and_add("devices", NULL,
- &priv->subsys.kobj);
+ priv->devices_kset = kset_create_and_add("devices", NULL, bus_kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
- priv->drivers_kset = kset_create_and_add("drivers", NULL,
- &priv->subsys.kobj);
+ priv->drivers_kset = kset_create_and_add("drivers", NULL, bus_kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
+ key = &priv->lock_key;
+ lockdep_register_key(key);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
@@ -834,7 +945,7 @@ int bus_register(struct bus_type *bus)
if (retval)
goto bus_probe_files_fail;
- retval = bus_add_groups(bus, bus->bus_groups);
+ retval = sysfs_create_groups(bus_kobj, bus->bus_groups);
if (retval)
goto bus_groups_fail;
@@ -844,16 +955,17 @@ int bus_register(struct bus_type *bus)
bus_groups_fail:
remove_probe_files(bus);
bus_probe_files_fail:
- kset_unregister(bus->p->drivers_kset);
+ kset_unregister(priv->drivers_kset);
bus_drivers_fail:
- kset_unregister(bus->p->devices_kset);
+ kset_unregister(priv->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
- kset_unregister(&bus->p->subsys);
+ kset_unregister(&priv->subsys);
+ /* Above kset_unregister() will kfree @priv */
+ priv = NULL;
out:
- kfree(bus->p);
- bus->p = NULL;
+ kfree(priv);
return retval;
}
EXPORT_SYMBOL_GPL(bus_register);
@@ -865,43 +977,82 @@ EXPORT_SYMBOL_GPL(bus_register);
* Unregister the child subsystems and the bus itself.
* Finally, we call bus_put() to release the refcount
*/
-void bus_unregister(struct bus_type *bus)
+void bus_unregister(const struct bus_type *bus)
{
+ struct subsys_private *sp = bus_to_subsys(bus);
+ struct kobject *bus_kobj;
+
+ if (!sp)
+ return;
+
pr_debug("bus: '%s': unregistering\n", bus->name);
- if (bus->dev_root)
- device_unregister(bus->dev_root);
- bus_remove_groups(bus, bus->bus_groups);
+ if (sp->dev_root)
+ device_unregister(sp->dev_root);
+
+ bus_kobj = &sp->subsys.kobj;
+ sysfs_remove_groups(bus_kobj, bus->bus_groups);
remove_probe_files(bus);
- kset_unregister(bus->p->drivers_kset);
- kset_unregister(bus->p->devices_kset);
bus_remove_file(bus, &bus_attr_uevent);
- kset_unregister(&bus->p->subsys);
+
+ kset_unregister(sp->drivers_kset);
+ kset_unregister(sp->devices_kset);
+ kset_unregister(&sp->subsys);
+ subsys_put(sp);
}
EXPORT_SYMBOL_GPL(bus_unregister);
-int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
+int bus_register_notifier(const struct bus_type *bus, struct notifier_block *nb)
{
- return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
+ struct subsys_private *sp = bus_to_subsys(bus);
+ int retval;
+
+ if (!sp)
+ return -EINVAL;
+
+ retval = blocking_notifier_chain_register(&sp->bus_notifier, nb);
+ subsys_put(sp);
+ return retval;
}
EXPORT_SYMBOL_GPL(bus_register_notifier);
-int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
+int bus_unregister_notifier(const struct bus_type *bus, struct notifier_block *nb)
{
- return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb);
+ struct subsys_private *sp = bus_to_subsys(bus);
+ int retval;
+
+ if (!sp)
+ return -EINVAL;
+ retval = blocking_notifier_chain_unregister(&sp->bus_notifier, nb);
+ subsys_put(sp);
+ return retval;
}
EXPORT_SYMBOL_GPL(bus_unregister_notifier);
-struct kset *bus_get_kset(struct bus_type *bus)
+void bus_notify(struct device *dev, enum bus_notifier_event value)
{
- return &bus->p->subsys;
+ struct subsys_private *sp = bus_to_subsys(dev->bus);
+
+ if (!sp)
+ return;
+
+ blocking_notifier_call_chain(&sp->bus_notifier, value, dev);
+ subsys_put(sp);
}
-EXPORT_SYMBOL_GPL(bus_get_kset);
-struct klist *bus_get_device_klist(struct bus_type *bus)
+struct kset *bus_get_kset(const struct bus_type *bus)
{
- return &bus->p->klist_devices;
+ struct subsys_private *sp = bus_to_subsys(bus);
+ struct kset *kset;
+
+ if (!sp)
+ return NULL;
+
+ kset = &sp->subsys;
+ subsys_put(sp);
+
+ return kset;
}
-EXPORT_SYMBOL_GPL(bus_get_device_klist);
+EXPORT_SYMBOL_GPL(bus_get_kset);
/*
* Yes, this forcibly breaks the klist abstraction temporarily. It
@@ -930,17 +1081,20 @@ static void device_insertion_sort_klist(struct device *a, struct list_head *list
list_move_tail(&a->p->knode_bus.n_node, list);
}
-void bus_sort_breadthfirst(struct bus_type *bus,
+void bus_sort_breadthfirst(const struct bus_type *bus,
int (*compare)(const struct device *a,
const struct device *b))
{
+ struct subsys_private *sp = bus_to_subsys(bus);
LIST_HEAD(sorted_devices);
struct klist_node *n, *tmp;
struct device_private *dev_prv;
struct device *dev;
struct klist *device_klist;
- device_klist = bus_get_device_klist(bus);
+ if (!sp)
+ return;
+ device_klist = &sp->klist_devices;
spin_lock(&device_klist->k_lock);
list_for_each_entry_safe(n, tmp, &device_klist->k_list, n_node) {
@@ -950,13 +1104,19 @@ void bus_sort_breadthfirst(struct bus_type *bus,
}
list_splice(&sorted_devices, &device_klist->k_list);
spin_unlock(&device_klist->k_lock);
+ subsys_put(sp);
}
EXPORT_SYMBOL_GPL(bus_sort_breadthfirst);
+struct subsys_dev_iter {
+ struct klist_iter ki;
+ const struct device_type *type;
+};
+
/**
* subsys_dev_iter_init - initialize subsys device iterator
* @iter: subsys iterator to initialize
- * @subsys: the subsys we wanna iterate over
+ * @sp: the subsys private (i.e. bus) we wanna iterate over
* @start: the device to start iterating from, if any
* @type: device_type of the devices to iterate over, NULL for all
*
@@ -965,17 +1125,16 @@ EXPORT_SYMBOL_GPL(bus_sort_breadthfirst);
* otherwise if it is NULL, the iteration starts at the beginning of
* the list.
*/
-void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys,
- struct device *start, const struct device_type *type)
+static void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct subsys_private *sp,
+ struct device *start, const struct device_type *type)
{
struct klist_node *start_knode = NULL;
if (start)
start_knode = &start->p->knode_bus;
- klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode);
+ klist_iter_init_node(&sp->klist_devices, &iter->ki, start_knode);
iter->type = type;
}
-EXPORT_SYMBOL_GPL(subsys_dev_iter_init);
/**
* subsys_dev_iter_next - iterate to the next device
@@ -989,7 +1148,7 @@ EXPORT_SYMBOL_GPL(subsys_dev_iter_init);
* free to do whatever it wants to do with the device including
* calling back into subsys code.
*/
-struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter)
+static struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter)
{
struct klist_node *knode;
struct device *dev;
@@ -1003,7 +1162,6 @@ struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter)
return dev;
}
}
-EXPORT_SYMBOL_GPL(subsys_dev_iter_next);
/**
* subsys_dev_iter_exit - finish iteration
@@ -1012,34 +1170,38 @@ EXPORT_SYMBOL_GPL(subsys_dev_iter_next);
* Finish an iteration. Always call this function after iteration is
* complete whether the iteration ran till the end or not.
*/
-void subsys_dev_iter_exit(struct subsys_dev_iter *iter)
+static void subsys_dev_iter_exit(struct subsys_dev_iter *iter)
{
klist_iter_exit(&iter->ki);
}
-EXPORT_SYMBOL_GPL(subsys_dev_iter_exit);
int subsys_interface_register(struct subsys_interface *sif)
{
- struct bus_type *subsys;
+ struct subsys_private *sp;
struct subsys_dev_iter iter;
struct device *dev;
if (!sif || !sif->subsys)
return -ENODEV;
- subsys = bus_get(sif->subsys);
- if (!subsys)
+ sp = bus_to_subsys(sif->subsys);
+ if (!sp)
return -EINVAL;
- mutex_lock(&subsys->p->mutex);
- list_add_tail(&sif->node, &subsys->p->interfaces);
+ /*
+ * Reference in sp is now incremented and will be dropped when
+ * the interface is removed from the bus
+ */
+
+ mutex_lock(&sp->mutex);
+ list_add_tail(&sif->node, &sp->interfaces);
if (sif->add_dev) {
- subsys_dev_iter_init(&iter, subsys, NULL, NULL);
+ subsys_dev_iter_init(&iter, sp, NULL, NULL);
while ((dev = subsys_dev_iter_next(&iter)))
sif->add_dev(dev, sif);
subsys_dev_iter_exit(&iter);
}
- mutex_unlock(&subsys->p->mutex);
+ mutex_unlock(&sp->mutex);
return 0;
}
@@ -1047,26 +1209,34 @@ EXPORT_SYMBOL_GPL(subsys_interface_register);
void subsys_interface_unregister(struct subsys_interface *sif)
{
- struct bus_type *subsys;
+ struct subsys_private *sp;
struct subsys_dev_iter iter;
struct device *dev;
if (!sif || !sif->subsys)
return;
- subsys = sif->subsys;
+ sp = bus_to_subsys(sif->subsys);
+ if (!sp)
+ return;
- mutex_lock(&subsys->p->mutex);
+ mutex_lock(&sp->mutex);
list_del_init(&sif->node);
if (sif->remove_dev) {
- subsys_dev_iter_init(&iter, subsys, NULL, NULL);
+ subsys_dev_iter_init(&iter, sp, NULL, NULL);
while ((dev = subsys_dev_iter_next(&iter)))
sif->remove_dev(dev, sif);
subsys_dev_iter_exit(&iter);
}
- mutex_unlock(&subsys->p->mutex);
-
- bus_put(subsys);
+ mutex_unlock(&sp->mutex);
+
+ /*
+ * Decrement the reference count twice, once for the bus_to_subsys()
+ * call in the start of this function, and the second one from the
+ * reference increment in subsys_interface_register()
+ */
+ subsys_put(sp);
+ subsys_put(sp);
}
EXPORT_SYMBOL_GPL(subsys_interface_unregister);
@@ -1075,10 +1245,11 @@ static void system_root_device_release(struct device *dev)
kfree(dev);
}
-static int subsys_register(struct bus_type *subsys,
+static int subsys_register(const struct bus_type *subsys,
const struct attribute_group **groups,
struct kobject *parent_of_root)
{
+ struct subsys_private *sp;
struct device *dev;
int err;
@@ -1086,6 +1257,12 @@ static int subsys_register(struct bus_type *subsys,
if (err < 0)
return err;
+ sp = bus_to_subsys(subsys);
+ if (!sp) {
+ err = -EINVAL;
+ goto err_sp;
+ }
+
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!dev) {
err = -ENOMEM;
@@ -1104,7 +1281,8 @@ static int subsys_register(struct bus_type *subsys,
if (err < 0)
goto err_dev_reg;
- subsys->dev_root = dev;
+ sp->dev_root = dev;
+ subsys_put(sp);
return 0;
err_dev_reg:
@@ -1113,6 +1291,8 @@ err_dev_reg:
err_name:
kfree(dev);
err_dev:
+ subsys_put(sp);
+err_sp:
bus_unregister(subsys);
return err;
}
@@ -1135,7 +1315,7 @@ err_dev:
* directory itself and not some create fake root-device placed in
* /sys/devices/system/<name>.
*/
-int subsys_system_register(struct bus_type *subsys,
+int subsys_system_register(const struct bus_type *subsys,
const struct attribute_group **groups)
{
return subsys_register(subsys, groups, &system_kset->kobj);
@@ -1148,17 +1328,17 @@ EXPORT_SYMBOL_GPL(subsys_system_register);
* @groups: default attributes for the root device
*
* All 'virtual' subsystems have a /sys/devices/system/<name> root device
- * with the name of the subystem. The root device can carry subsystem-wide
+ * with the name of the subsystem. The root device can carry subsystem-wide
* attributes. All registered devices are below this single root device.
* There's no restriction on device naming. This is for kernel software
* constructs which need sysfs interface.
*/
-int subsys_virtual_register(struct bus_type *subsys,
+int subsys_virtual_register(const struct bus_type *subsys,
const struct attribute_group **groups)
{
struct kobject *virtual_dir;
- virtual_dir = virtual_device_parent(NULL);
+ virtual_dir = virtual_device_parent();
if (!virtual_dir)
return -ENOMEM;
@@ -1166,6 +1346,82 @@ int subsys_virtual_register(struct bus_type *subsys,
}
EXPORT_SYMBOL_GPL(subsys_virtual_register);
+/**
+ * driver_find - locate driver on a bus by its name.
+ * @name: name of the driver.
+ * @bus: bus to scan for the driver.
+ *
+ * Call kset_find_obj() to iterate over list of drivers on
+ * a bus to find driver by name. Return driver if found.
+ *
+ * This routine provides no locking to prevent the driver it returns
+ * from being unregistered or unloaded while the caller is using it.
+ * The caller is responsible for preventing this.
+ */
+struct device_driver *driver_find(const char *name, const struct bus_type *bus)
+{
+ struct subsys_private *sp = bus_to_subsys(bus);
+ struct kobject *k;
+ struct driver_private *priv;
+
+ if (!sp)
+ return NULL;
+
+ k = kset_find_obj(sp->drivers_kset, name);
+ subsys_put(sp);
+ if (!k)
+ return NULL;
+
+ priv = to_driver(k);
+
+ /* Drop reference added by kset_find_obj() */
+ kobject_put(k);
+ return priv->driver;
+}
+EXPORT_SYMBOL_GPL(driver_find);
+
+/*
+ * Warning, the value could go to "removed" instantly after calling this function, so be very
+ * careful when calling it...
+ */
+bool bus_is_registered(const struct bus_type *bus)
+{
+ struct subsys_private *sp = bus_to_subsys(bus);
+ bool is_initialized = false;
+
+ if (sp) {
+ is_initialized = true;
+ subsys_put(sp);
+ }
+ return is_initialized;
+}
+
+/**
+ * bus_get_dev_root - return a pointer to the "device root" of a bus
+ * @bus: bus to return the device root of.
+ *
+ * If a bus has a "device root" structure, return it, WITH THE REFERENCE
+ * COUNT INCREMENTED.
+ *
+ * Note, when finished with the device, a call to put_device() is required.
+ *
+ * If the device root is not present (or bus is not a valid pointer), NULL
+ * will be returned.
+ */
+struct device *bus_get_dev_root(const struct bus_type *bus)
+{
+ struct subsys_private *sp = bus_to_subsys(bus);
+ struct device *dev_root;
+
+ if (!sp)
+ return NULL;
+
+ dev_root = get_device(sp->dev_root);
+ subsys_put(sp);
+ return dev_root;
+}
+EXPORT_SYMBOL_GPL(bus_get_dev_root);
+
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
@@ -1173,8 +1429,13 @@ int __init buses_init(void)
return -ENOMEM;
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
- if (!system_kset)
+ if (!system_kset) {
+ /* Do error handling here as devices_init() do */
+ kset_unregister(bus_kset);
+ bus_kset = NULL;
+ pr_err("%s: failed to create and add kset 'bus'\n", __func__);
return -ENOMEM;
+ }
return 0;
}
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index 4b5cd08c5a65..613410705a47 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -8,13 +8,14 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cacheinfo.h>
#include <linux/compiler.h>
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/init.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp.h>
@@ -28,6 +29,9 @@ static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo);
#define per_cpu_cacheinfo_idx(cpu, idx) \
(per_cpu_cacheinfo(cpu) + (idx))
+/* Set if no cache information is found in DT/ACPI. */
+static bool use_arch_info;
+
struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu)
{
return ci_cacheinfo(cpu);
@@ -38,11 +42,11 @@ static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
{
/*
* For non DT/ACPI systems, assume unique level 1 caches,
- * system-wide shared caches for all other levels. This will be used
- * only if arch specific code has not populated shared_cpu_map
+ * system-wide shared caches for all other levels.
*/
- if (!(IS_ENABLED(CONFIG_OF) || IS_ENABLED(CONFIG_ACPI)))
- return !(this_leaf->level == 1);
+ if (!(IS_ENABLED(CONFIG_OF) || IS_ENABLED(CONFIG_ACPI)) ||
+ use_arch_info)
+ return (this_leaf->level != 1) && (sib_leaf->level != 1);
if ((sib_leaf->attributes & CACHE_ID) &&
(this_leaf->attributes & CACHE_ID))
@@ -55,7 +59,7 @@ bool last_level_cache_is_valid(unsigned int cpu)
{
struct cacheinfo *llc;
- if (!cache_leaves(cpu))
+ if (!cache_leaves(cpu) || !per_cpu_cacheinfo(cpu))
return false;
llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1);
@@ -79,6 +83,9 @@ bool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y)
}
#ifdef CONFIG_OF
+
+static bool of_check_cache_nodes(struct device_node *np);
+
/* OF properties to query for a given cache type */
struct cache_type_info {
const char *size_prop;
@@ -177,6 +184,54 @@ static bool cache_node_is_unified(struct cacheinfo *this_leaf,
return of_property_read_bool(np, "cache-unified");
}
+static bool match_cache_node(struct device_node *cpu,
+ const struct device_node *cache_node)
+{
+ struct device_node *prev, *cache = of_find_next_cache_node(cpu);
+
+ while (cache) {
+ if (cache == cache_node) {
+ of_node_put(cache);
+ return true;
+ }
+
+ prev = cache;
+ cache = of_find_next_cache_node(cache);
+ of_node_put(prev);
+ }
+
+ return false;
+}
+
+#ifndef arch_compact_of_hwid
+#define arch_compact_of_hwid(_x) (_x)
+#endif
+
+static void cache_of_set_id(struct cacheinfo *this_leaf,
+ struct device_node *cache_node)
+{
+ struct device_node *cpu;
+ u32 min_id = ~0;
+
+ for_each_of_cpu_node(cpu) {
+ u64 id = of_get_cpu_hwid(cpu, 0);
+
+ id = arch_compact_of_hwid(id);
+ if (FIELD_GET(GENMASK_ULL(63, 32), id)) {
+ of_node_put(cpu);
+ return;
+ }
+
+ if (match_cache_node(cpu, cache_node))
+ min_id = min(min_id, id);
+ }
+
+ if (min_id != ~0) {
+ this_leaf->id = min_id;
+ this_leaf->attributes |= CACHE_ID;
+ }
+}
+
static void cache_of_set_props(struct cacheinfo *this_leaf,
struct device_node *np)
{
@@ -192,28 +247,32 @@ static void cache_of_set_props(struct cacheinfo *this_leaf,
cache_get_line_size(this_leaf, np);
cache_nr_sets(this_leaf, np);
cache_associativity(this_leaf);
+ cache_of_set_id(this_leaf, np);
}
static int cache_setup_of_node(unsigned int cpu)
{
- struct device_node *np;
struct cacheinfo *this_leaf;
unsigned int index = 0;
- np = of_cpu_device_node_get(cpu);
+ struct device_node *np __free(device_node) = of_cpu_device_node_get(cpu);
if (!np) {
pr_err("Failed to find cpu%d device node\n", cpu);
return -ENOENT;
}
+ if (!of_check_cache_nodes(np)) {
+ return -ENOENT;
+ }
+
while (index < cache_leaves(cpu)) {
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
- if (this_leaf->level != 1)
+ if (this_leaf->level != 1) {
+ struct device_node *prev __free(device_node) = np;
np = of_find_next_cache_node(np);
- else
- np = of_node_get(np);/* cpu node itself */
- if (!np)
- break;
+ if (!np)
+ break;
+ }
cache_of_set_props(this_leaf, np);
this_leaf->fw_token = np;
index++;
@@ -224,8 +283,87 @@ static int cache_setup_of_node(unsigned int cpu)
return 0;
}
+
+static bool of_check_cache_nodes(struct device_node *np)
+{
+ if (of_property_present(np, "cache-size") ||
+ of_property_present(np, "i-cache-size") ||
+ of_property_present(np, "d-cache-size") ||
+ of_property_present(np, "cache-unified"))
+ return true;
+
+ struct device_node *next __free(device_node) = of_find_next_cache_node(np);
+ if (next) {
+ return true;
+ }
+
+ return false;
+}
+
+static int of_count_cache_leaves(struct device_node *np)
+{
+ unsigned int leaves = 0;
+
+ if (of_property_present(np, "cache-size"))
+ ++leaves;
+ if (of_property_present(np, "i-cache-size"))
+ ++leaves;
+ if (of_property_present(np, "d-cache-size"))
+ ++leaves;
+
+ if (!leaves) {
+ /* The '[i-|d-|]cache-size' property is required, but
+ * if absent, fallback on the 'cache-unified' property.
+ */
+ if (of_property_read_bool(np, "cache-unified"))
+ return 1;
+ else
+ return 2;
+ }
+
+ return leaves;
+}
+
+int init_of_cache_level(unsigned int cpu)
+{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct device_node *np __free(device_node) = of_cpu_device_node_get(cpu);
+ unsigned int levels = 0, leaves, level;
+
+ if (!of_check_cache_nodes(np)) {
+ return -ENOENT;
+ }
+
+ leaves = of_count_cache_leaves(np);
+ if (leaves > 0)
+ levels = 1;
+
+ while (1) {
+ struct device_node *prev __free(device_node) = np;
+ np = of_find_next_cache_node(np);
+ if (!np)
+ break;
+
+ if (!of_device_is_compatible(np, "cache"))
+ return -EINVAL;
+ if (of_property_read_u32(np, "cache-level", &level))
+ return -EINVAL;
+ if (level <= levels)
+ return -EINVAL;
+
+ leaves += of_count_cache_leaves(np);
+ levels = level;
+ }
+
+ this_cpu_ci->num_levels = levels;
+ this_cpu_ci->num_leaves = leaves;
+
+ return 0;
+}
+
#else
static inline int cache_setup_of_node(unsigned int cpu) { return 0; }
+int init_of_cache_level(unsigned int cpu) { return 0; }
#endif
int __weak cache_setup_acpi(unsigned int cpu)
@@ -244,6 +382,10 @@ static int cache_setup_properties(unsigned int cpu)
else if (!acpi_disabled)
ret = cache_setup_acpi(cpu);
+ // Assume there is no cache information available in DT/ACPI from now.
+ if (ret && use_arch_cache_info())
+ use_arch_info = true;
+
return ret;
}
@@ -251,7 +393,7 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
{
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
struct cacheinfo *this_leaf, *sib_leaf;
- unsigned int index;
+ unsigned int index, sib_index;
int ret = 0;
if (this_cpu_ci->cpu_map_populated)
@@ -262,7 +404,7 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
* to update the shared cpu_map if the cache attributes were
* populated early before all the cpus are brought online
*/
- if (!last_level_cache_is_valid(cpu)) {
+ if (!last_level_cache_is_valid(cpu) && !use_arch_info) {
ret = cache_setup_properties(cpu);
if (ret)
return ret;
@@ -275,15 +417,25 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
for_each_online_cpu(i) {
- struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);
-
- if (i == cpu || !sib_cpu_ci->info_list)
+ if (i == cpu || !per_cpu_cacheinfo(i))
continue;/* skip if itself or no cacheinfo */
-
- sib_leaf = per_cpu_cacheinfo_idx(i, index);
- if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
- cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
- cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
+ for (sib_index = 0; sib_index < cache_leaves(i); sib_index++) {
+ sib_leaf = per_cpu_cacheinfo_idx(i, sib_index);
+
+ /*
+ * Comparing cache IDs only makes sense if the leaves
+ * belong to the same cache level of same type. Skip
+ * the check if level and type do not match.
+ */
+ if (sib_leaf->level != this_leaf->level ||
+ sib_leaf->type != this_leaf->type)
+ continue;
+
+ if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
+ cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
+ cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
+ break;
+ }
}
}
/* record the maximum cache line size */
@@ -291,30 +443,46 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
coherency_max_size = this_leaf->coherency_line_size;
}
+ /* shared_cpu_map is now populated for the cpu */
+ this_cpu_ci->cpu_map_populated = true;
return 0;
}
static void cache_shared_cpu_map_remove(unsigned int cpu)
{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
struct cacheinfo *this_leaf, *sib_leaf;
- unsigned int sibling, index;
+ unsigned int sibling, index, sib_index;
for (index = 0; index < cache_leaves(cpu); index++) {
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
for_each_cpu(sibling, &this_leaf->shared_cpu_map) {
- struct cpu_cacheinfo *sib_cpu_ci =
- get_cpu_cacheinfo(sibling);
-
- if (sibling == cpu || !sib_cpu_ci->info_list)
+ if (sibling == cpu || !per_cpu_cacheinfo(sibling))
continue;/* skip if itself or no cacheinfo */
- sib_leaf = per_cpu_cacheinfo_idx(sibling, index);
- cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
- cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
+ for (sib_index = 0; sib_index < cache_leaves(sibling); sib_index++) {
+ sib_leaf = per_cpu_cacheinfo_idx(sibling, sib_index);
+
+ /*
+ * Comparing cache IDs only makes sense if the leaves
+ * belong to the same cache level of same type. Skip
+ * the check if level and type do not match.
+ */
+ if (sib_leaf->level != this_leaf->level ||
+ sib_leaf->type != this_leaf->type)
+ continue;
+
+ if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
+ cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
+ cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
+ break;
+ }
+ }
}
- if (of_have_populated_dt())
- of_node_put(this_leaf->fw_token);
}
+
+ /* cpu is no longer populated in the shared map */
+ this_cpu_ci->cpu_map_populated = false;
}
static void free_cache_attributes(unsigned int cpu)
@@ -323,10 +491,11 @@ static void free_cache_attributes(unsigned int cpu)
return;
cache_shared_cpu_map_remove(cpu);
+}
- kfree(per_cpu_cacheinfo(cpu));
- per_cpu_cacheinfo(cpu) = NULL;
- cache_leaves(cpu) = 0;
+int __weak early_cache_level(unsigned int cpu)
+{
+ return -ENOENT;
}
int __weak init_cache_level(unsigned int cpu)
@@ -339,38 +508,113 @@ int __weak populate_cache_leaves(unsigned int cpu)
return -ENOENT;
}
-int detect_cache_attributes(unsigned int cpu)
+static inline int allocate_cache_info(int cpu)
+{
+ per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), sizeof(struct cacheinfo), GFP_ATOMIC);
+ if (!per_cpu_cacheinfo(cpu)) {
+ cache_leaves(cpu) = 0;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int fetch_cache_info(unsigned int cpu)
{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ unsigned int levels = 0, split_levels = 0;
int ret;
- /* Since early detection of the cacheinfo is allowed via this
- * function and this also gets called as CPU hotplug callbacks via
- * cacheinfo_cpu_online, the initialisation can be skipped and only
- * CPU maps can be updated as the CPU online status would be update
- * if called via cacheinfo_cpu_online path.
+ if (acpi_disabled) {
+ ret = init_of_cache_level(cpu);
+ } else {
+ ret = acpi_get_cache_info(cpu, &levels, &split_levels);
+ if (!ret) {
+ this_cpu_ci->num_levels = levels;
+ /*
+ * This assumes that:
+ * - there cannot be any split caches (data/instruction)
+ * above a unified cache
+ * - data/instruction caches come by pair
+ */
+ this_cpu_ci->num_leaves = levels + split_levels;
+ }
+ }
+
+ if (ret || !cache_leaves(cpu)) {
+ ret = early_cache_level(cpu);
+ if (ret)
+ return ret;
+
+ if (!cache_leaves(cpu))
+ return -ENOENT;
+
+ this_cpu_ci->early_ci_levels = true;
+ }
+
+ return allocate_cache_info(cpu);
+}
+
+static inline int init_level_allocate_ci(unsigned int cpu)
+{
+ unsigned int early_leaves = cache_leaves(cpu);
+
+ /* Since early initialization/allocation of the cacheinfo is allowed
+ * via fetch_cache_info() and this also gets called as CPU hotplug
+ * callbacks via cacheinfo_cpu_online, the init/alloc can be skipped
+ * as it will happen only once (the cacheinfo memory is never freed).
+ * Just populate the cacheinfo. However, if the cacheinfo has been
+ * allocated early through the arch-specific early_cache_level() call,
+ * there is a chance the info is wrong (this can happen on arm64). In
+ * that case, call init_cache_level() anyway to give the arch-specific
+ * code a chance to make things right.
*/
- if (per_cpu_cacheinfo(cpu))
- goto update_cpu_map;
+ if (per_cpu_cacheinfo(cpu) && !ci_cacheinfo(cpu)->early_ci_levels)
+ return 0;
if (init_cache_level(cpu) || !cache_leaves(cpu))
return -ENOENT;
- per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu),
- sizeof(struct cacheinfo), GFP_ATOMIC);
- if (per_cpu_cacheinfo(cpu) == NULL) {
- cache_leaves(cpu) = 0;
- return -ENOMEM;
- }
+ /*
+ * Now that we have properly initialized the cache level info, make
+ * sure we don't try to do that again the next time we are called
+ * (e.g. as CPU hotplug callbacks).
+ */
+ ci_cacheinfo(cpu)->early_ci_levels = false;
/*
- * populate_cache_leaves() may completely setup the cache leaves and
- * shared_cpu_map or it may leave it partially setup.
+ * Some architectures (e.g., x86) do not use early initialization.
+ * Allocate memory now in such case.
*/
- ret = populate_cache_leaves(cpu);
+ if (cache_leaves(cpu) <= early_leaves && per_cpu_cacheinfo(cpu))
+ return 0;
+
+ kfree(per_cpu_cacheinfo(cpu));
+ return allocate_cache_info(cpu);
+}
+
+int detect_cache_attributes(unsigned int cpu)
+{
+ int ret;
+
+ ret = init_level_allocate_ci(cpu);
if (ret)
- goto free_ci;
+ return ret;
+
+ /*
+ * If LLC is valid the cache leaves were already populated so just go to
+ * update the cpu map.
+ */
+ if (!last_level_cache_is_valid(cpu)) {
+ /*
+ * populate_cache_leaves() may completely setup the cache leaves and
+ * shared_cpu_map or it may leave it partially setup.
+ */
+ ret = populate_cache_leaves(cpu);
+ if (ret)
+ goto free_ci;
+ }
-update_cpu_map:
/*
* For systems using DT for cache hierarchy, fw_token
* and shared_cpu_map will be set up here only if they are
@@ -686,24 +930,111 @@ err:
return rc;
}
+static unsigned int cpu_map_shared_cache(bool online, unsigned int cpu,
+ cpumask_t **map)
+{
+ struct cacheinfo *llc, *sib_llc;
+ unsigned int sibling;
+
+ if (!last_level_cache_is_valid(cpu))
+ return 0;
+
+ llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1);
+
+ if (llc->type != CACHE_TYPE_DATA && llc->type != CACHE_TYPE_UNIFIED)
+ return 0;
+
+ if (online) {
+ *map = &llc->shared_cpu_map;
+ return cpumask_weight(*map);
+ }
+
+ /* shared_cpu_map of offlined CPU will be cleared, so use sibling map */
+ for_each_cpu(sibling, &llc->shared_cpu_map) {
+ if (sibling == cpu || !last_level_cache_is_valid(sibling))
+ continue;
+ sib_llc = per_cpu_cacheinfo_idx(sibling, cache_leaves(sibling) - 1);
+ *map = &sib_llc->shared_cpu_map;
+ return cpumask_weight(*map);
+ }
+
+ return 0;
+}
+
+/*
+ * Calculate the size of the per-CPU data cache slice. This can be
+ * used to estimate the size of the data cache slice that can be used
+ * by one CPU under ideal circumstances. UNIFIED caches are counted
+ * in addition to DATA caches. So, please consider code cache usage
+ * when use the result.
+ *
+ * Because the cache inclusive/non-inclusive information isn't
+ * available, we just use the size of the per-CPU slice of LLC to make
+ * the result more predictable across architectures.
+ */
+static void update_per_cpu_data_slice_size_cpu(unsigned int cpu)
+{
+ struct cpu_cacheinfo *ci;
+ struct cacheinfo *llc;
+ unsigned int nr_shared;
+
+ if (!last_level_cache_is_valid(cpu))
+ return;
+
+ ci = ci_cacheinfo(cpu);
+ llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1);
+
+ if (llc->type != CACHE_TYPE_DATA && llc->type != CACHE_TYPE_UNIFIED)
+ return;
+
+ nr_shared = cpumask_weight(&llc->shared_cpu_map);
+ if (nr_shared)
+ ci->per_cpu_data_slice_size = llc->size / nr_shared;
+}
+
+static void update_per_cpu_data_slice_size(bool cpu_online, unsigned int cpu,
+ cpumask_t *cpu_map)
+{
+ unsigned int icpu;
+
+ for_each_cpu(icpu, cpu_map) {
+ if (!cpu_online && icpu == cpu)
+ continue;
+ update_per_cpu_data_slice_size_cpu(icpu);
+ setup_pcp_cacheinfo(icpu);
+ }
+}
+
static int cacheinfo_cpu_online(unsigned int cpu)
{
int rc = detect_cache_attributes(cpu);
+ cpumask_t *cpu_map;
if (rc)
return rc;
rc = cache_add_dev(cpu);
if (rc)
- free_cache_attributes(cpu);
+ goto err;
+ if (cpu_map_shared_cache(true, cpu, &cpu_map))
+ update_per_cpu_data_slice_size(true, cpu, cpu_map);
+ return 0;
+err:
+ free_cache_attributes(cpu);
return rc;
}
static int cacheinfo_cpu_pre_down(unsigned int cpu)
{
+ cpumask_t *cpu_map;
+ unsigned int nr_shared;
+
+ nr_shared = cpu_map_shared_cache(false, cpu, &cpu_map);
if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map))
cpu_cache_sysfs_exit(cpu);
free_cache_attributes(cpu);
+ if (nr_shared > 1)
+ update_per_cpu_data_slice_size(false, cpu, cpu_map);
return 0;
}
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 8feb85e186e3..2526c57d924e 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -20,8 +20,52 @@
#include <linux/mutex.h>
#include "base.h"
+/* /sys/class */
+static struct kset *class_kset;
+
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
+/**
+ * class_to_subsys - Turn a struct class into a struct subsys_private
+ *
+ * @class: pointer to the struct bus_type to look up
+ *
+ * The driver core internals need to work on the subsys_private structure, not
+ * the external struct class pointer. This function walks the list of
+ * registered classes in the system and finds the matching one and returns the
+ * internal struct subsys_private that relates to that class.
+ *
+ * Note, the reference count of the return value is INCREMENTED if it is not
+ * NULL. A call to subsys_put() must be done when finished with the pointer in
+ * order for it to be properly freed.
+ */
+struct subsys_private *class_to_subsys(const struct class *class)
+{
+ struct subsys_private *sp = NULL;
+ struct kobject *kobj;
+
+ if (!class || !class_kset)
+ return NULL;
+
+ spin_lock(&class_kset->list_lock);
+
+ if (list_empty(&class_kset->list))
+ goto done;
+
+ list_for_each_entry(kobj, &class_kset->list, entry) {
+ struct kset *kset = container_of(kobj, struct kset, kobj);
+
+ sp = container_of_const(kset, struct subsys_private, subsys);
+ if (sp->class == class)
+ goto done;
+ }
+ sp = NULL;
+done:
+ sp = subsys_get(sp);
+ spin_unlock(&class_kset->list_lock);
+ return sp;
+}
+
static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
@@ -49,7 +93,7 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
static void class_release(struct kobject *kobj)
{
struct subsys_private *cp = to_subsys_private(kobj);
- struct class *class = cp->class;
+ const struct class *class = cp->class;
pr_debug("class '%s': release.\n", class->name);
@@ -59,13 +103,14 @@ static void class_release(struct kobject *kobj)
pr_debug("class '%s' does not have a release() function, "
"be careful\n", class->name);
+ lockdep_unregister_key(&cp->lock_key);
kfree(cp);
}
-static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj)
+static const struct kobj_ns_type_operations *class_child_ns_type(const struct kobject *kobj)
{
- struct subsys_private *cp = to_subsys_private(kobj);
- struct class *class = cp->class;
+ const struct subsys_private *cp = to_subsys_private(kobj);
+ const struct class *class = cp->class;
return class->ns_type;
}
@@ -75,48 +120,40 @@ static const struct sysfs_ops class_sysfs_ops = {
.store = class_attr_store,
};
-static struct kobj_type class_ktype = {
+static const struct kobj_type class_ktype = {
.sysfs_ops = &class_sysfs_ops,
.release = class_release,
.child_ns_type = class_child_ns_type,
};
-/* Hotplug events for classes go to the class subsys */
-static struct kset *class_kset;
-
-
-int class_create_file_ns(struct class *cls, const struct class_attribute *attr,
+int class_create_file_ns(const struct class *cls, const struct class_attribute *attr,
const void *ns)
{
+ struct subsys_private *sp = class_to_subsys(cls);
int error;
- if (cls)
- error = sysfs_create_file_ns(&cls->p->subsys.kobj,
- &attr->attr, ns);
- else
- error = -EINVAL;
+ if (!sp)
+ return -EINVAL;
+
+ error = sysfs_create_file_ns(&sp->subsys.kobj, &attr->attr, ns);
+ subsys_put(sp);
+
return error;
}
+EXPORT_SYMBOL_GPL(class_create_file_ns);
-void class_remove_file_ns(struct class *cls, const struct class_attribute *attr,
+void class_remove_file_ns(const struct class *cls, const struct class_attribute *attr,
const void *ns)
{
- if (cls)
- sysfs_remove_file_ns(&cls->p->subsys.kobj, &attr->attr, ns);
-}
+ struct subsys_private *sp = class_to_subsys(cls);
-static struct class *class_get(struct class *cls)
-{
- if (cls)
- kset_get(&cls->p->subsys);
- return cls;
-}
+ if (!sp)
+ return;
-static void class_put(struct class *cls)
-{
- if (cls)
- kset_put(&cls->p->subsys);
+ sysfs_remove_file_ns(&sp->subsys.kobj, &attr->attr, ns);
+ subsys_put(sp);
}
+EXPORT_SYMBOL_GPL(class_remove_file_ns);
static struct device *klist_class_to_dev(struct klist_node *n)
{
@@ -138,82 +175,85 @@ static void klist_class_dev_put(struct klist_node *n)
put_device(dev);
}
-static int class_add_groups(struct class *cls,
- const struct attribute_group **groups)
-{
- return sysfs_create_groups(&cls->p->subsys.kobj, groups);
-}
-
-static void class_remove_groups(struct class *cls,
- const struct attribute_group **groups)
-{
- return sysfs_remove_groups(&cls->p->subsys.kobj, groups);
-}
-
-int __class_register(struct class *cls, struct lock_class_key *key)
+int class_register(const struct class *cls)
{
struct subsys_private *cp;
+ struct lock_class_key *key;
int error;
pr_debug("device class '%s': registering\n", cls->name);
+ if (cls->ns_type && !cls->namespace) {
+ pr_err("%s: class '%s' does not have namespace\n",
+ __func__, cls->name);
+ return -EINVAL;
+ }
+ if (!cls->ns_type && cls->namespace) {
+ pr_err("%s: class '%s' does not have ns_type\n",
+ __func__, cls->name);
+ return -EINVAL;
+ }
+
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->interfaces);
kset_init(&cp->glue_dirs);
+ key = &cp->lock_key;
+ lockdep_register_key(key);
__mutex_init(&cp->mutex, "subsys mutex", key);
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
- if (error) {
- kfree(cp);
- return error;
- }
-
- /* set the default /sys/dev directory for devices of this class */
- if (!cls->dev_kobj)
- cls->dev_kobj = sysfs_dev_char_kobj;
+ if (error)
+ goto err_out;
-#if defined(CONFIG_BLOCK)
- /* let the block class directory show up in the root of sysfs */
- if (!sysfs_deprecated || cls != &block_class)
- cp->subsys.kobj.kset = class_kset;
-#else
cp->subsys.kobj.kset = class_kset;
-#endif
cp->subsys.kobj.ktype = &class_ktype;
cp->class = cls;
- cls->p = cp;
error = kset_register(&cp->subsys);
+ if (error)
+ goto err_out;
+
+ error = sysfs_create_groups(&cp->subsys.kobj, cls->class_groups);
if (error) {
- kfree(cp);
- return error;
+ kobject_del(&cp->subsys.kobj);
+ kfree_const(cp->subsys.kobj.name);
+ goto err_out;
}
- error = class_add_groups(class_get(cls), cls->class_groups);
- class_put(cls);
+ return 0;
+
+err_out:
+ lockdep_unregister_key(key);
+ kfree(cp);
return error;
}
-EXPORT_SYMBOL_GPL(__class_register);
+EXPORT_SYMBOL_GPL(class_register);
-void class_unregister(struct class *cls)
+void class_unregister(const struct class *cls)
{
+ struct subsys_private *sp = class_to_subsys(cls);
+
+ if (!sp)
+ return;
+
pr_debug("device class '%s': unregistering\n", cls->name);
- class_remove_groups(cls, cls->class_groups);
- kset_unregister(&cls->p->subsys);
+
+ sysfs_remove_groups(&sp->subsys.kobj, cls->class_groups);
+ kset_unregister(&sp->subsys);
+ subsys_put(sp);
}
+EXPORT_SYMBOL_GPL(class_unregister);
-static void class_create_release(struct class *cls)
+static void class_create_release(const struct class *cls)
{
pr_debug("%s called for %s\n", __func__, cls->name);
kfree(cls);
}
/**
- * __class_create - create a struct class structure
- * @owner: pointer to the module that is to "own" this struct class
+ * class_create - create a struct class structure
* @name: pointer to a string for the name of this class.
- * @key: the lock_class_key for this class; used by mutex lock debugging
*
* This is used to create a struct class pointer that can then be used
* in calls to device_create().
@@ -223,8 +263,7 @@ static void class_create_release(struct class *cls)
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
-struct class *__class_create(struct module *owner, const char *name,
- struct lock_class_key *key)
+struct class *class_create(const char *name)
{
struct class *cls;
int retval;
@@ -236,10 +275,9 @@ struct class *__class_create(struct module *owner, const char *name,
}
cls->name = name;
- cls->owner = owner;
cls->class_release = class_create_release;
- retval = __class_register(cls, key);
+ retval = class_register(cls);
if (retval)
goto error;
@@ -249,7 +287,7 @@ error:
kfree(cls);
return ERR_PTR(retval);
}
-EXPORT_SYMBOL_GPL(__class_create);
+EXPORT_SYMBOL_GPL(class_create);
/**
* class_destroy - destroys a struct class structure
@@ -258,13 +296,14 @@ EXPORT_SYMBOL_GPL(__class_create);
* Note, the pointer to be destroyed must have been created with a call
* to class_create().
*/
-void class_destroy(struct class *cls)
+void class_destroy(const struct class *cls)
{
- if ((cls == NULL) || (IS_ERR(cls)))
+ if (IS_ERR_OR_NULL(cls))
return;
class_unregister(cls);
}
+EXPORT_SYMBOL_GPL(class_destroy);
/**
* class_dev_iter_init - initialize class device iterator
@@ -278,15 +317,24 @@ void class_destroy(struct class *cls)
* otherwise if it is NULL, the iteration starts at the beginning of
* the list.
*/
-void class_dev_iter_init(struct class_dev_iter *iter, struct class *class,
- struct device *start, const struct device_type *type)
+void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class,
+ const struct device *start, const struct device_type *type)
{
+ struct subsys_private *sp = class_to_subsys(class);
struct klist_node *start_knode = NULL;
+ memset(iter, 0, sizeof(*iter));
+ if (!sp) {
+ pr_crit("%s: class %p was not registered yet\n",
+ __func__, class);
+ return;
+ }
+
if (start)
start_knode = &start->p->knode_class;
- klist_iter_init_node(&class->p->klist_devices, &iter->ki, start_knode);
+ klist_iter_init_node(&sp->klist_devices, &iter->ki, start_knode);
iter->type = type;
+ iter->sp = sp;
}
EXPORT_SYMBOL_GPL(class_dev_iter_init);
@@ -307,6 +355,9 @@ struct device *class_dev_iter_next(struct class_dev_iter *iter)
struct klist_node *knode;
struct device *dev;
+ if (!iter->sp)
+ return NULL;
+
while (1) {
knode = klist_next(&iter->ki);
if (!knode)
@@ -328,6 +379,7 @@ EXPORT_SYMBOL_GPL(class_dev_iter_next);
void class_dev_iter_exit(struct class_dev_iter *iter)
{
klist_iter_exit(&iter->ki);
+ subsys_put(iter->sp);
}
EXPORT_SYMBOL_GPL(class_dev_iter_exit);
@@ -349,17 +401,18 @@ EXPORT_SYMBOL_GPL(class_dev_iter_exit);
* @fn is allowed to do anything including calling back into class
* code. There's no locking restriction.
*/
-int class_for_each_device(struct class *class, struct device *start,
- void *data, int (*fn)(struct device *, void *))
+int class_for_each_device(const struct class *class, const struct device *start,
+ void *data, device_iter_t fn)
{
+ struct subsys_private *sp = class_to_subsys(class);
struct class_dev_iter iter;
struct device *dev;
int error = 0;
if (!class)
return -EINVAL;
- if (!class->p) {
- WARN(1, "%s called for class '%s' before it was initialized",
+ if (!sp) {
+ WARN(1, "%s called for class '%s' before it was registered",
__func__, class->name);
return -EINVAL;
}
@@ -371,6 +424,7 @@ int class_for_each_device(struct class *class, struct device *start,
break;
}
class_dev_iter_exit(&iter);
+ subsys_put(sp);
return error;
}
@@ -396,17 +450,17 @@ EXPORT_SYMBOL_GPL(class_for_each_device);
* @match is allowed to do anything including calling back into class
* code. There's no locking restriction.
*/
-struct device *class_find_device(struct class *class, struct device *start,
- const void *data,
- int (*match)(struct device *, const void *))
+struct device *class_find_device(const struct class *class, const struct device *start,
+ const void *data, device_match_t match)
{
+ struct subsys_private *sp = class_to_subsys(class);
struct class_dev_iter iter;
struct device *dev;
if (!class)
return NULL;
- if (!class->p) {
- WARN(1, "%s called for class '%s' before it was initialized",
+ if (!sp) {
+ WARN(1, "%s called for class '%s' before it was registered",
__func__, class->name);
return NULL;
}
@@ -419,6 +473,7 @@ struct device *class_find_device(struct class *class, struct device *start,
}
}
class_dev_iter_exit(&iter);
+ subsys_put(sp);
return dev;
}
@@ -426,54 +481,74 @@ EXPORT_SYMBOL_GPL(class_find_device);
int class_interface_register(struct class_interface *class_intf)
{
- struct class *parent;
+ struct subsys_private *sp;
+ const struct class *parent;
struct class_dev_iter iter;
struct device *dev;
if (!class_intf || !class_intf->class)
return -ENODEV;
- parent = class_get(class_intf->class);
- if (!parent)
+ parent = class_intf->class;
+ sp = class_to_subsys(parent);
+ if (!sp)
return -EINVAL;
- mutex_lock(&parent->p->mutex);
- list_add_tail(&class_intf->node, &parent->p->interfaces);
+ /*
+ * Reference in sp is now incremented and will be dropped when
+ * the interface is removed in the call to class_interface_unregister()
+ */
+
+ mutex_lock(&sp->mutex);
+ list_add_tail(&class_intf->node, &sp->interfaces);
if (class_intf->add_dev) {
class_dev_iter_init(&iter, parent, NULL, NULL);
while ((dev = class_dev_iter_next(&iter)))
- class_intf->add_dev(dev, class_intf);
+ class_intf->add_dev(dev);
class_dev_iter_exit(&iter);
}
- mutex_unlock(&parent->p->mutex);
+ mutex_unlock(&sp->mutex);
return 0;
}
+EXPORT_SYMBOL_GPL(class_interface_register);
void class_interface_unregister(struct class_interface *class_intf)
{
- struct class *parent = class_intf->class;
+ struct subsys_private *sp;
+ const struct class *parent = class_intf->class;
struct class_dev_iter iter;
struct device *dev;
if (!parent)
return;
- mutex_lock(&parent->p->mutex);
+ sp = class_to_subsys(parent);
+ if (!sp)
+ return;
+
+ mutex_lock(&sp->mutex);
list_del_init(&class_intf->node);
if (class_intf->remove_dev) {
class_dev_iter_init(&iter, parent, NULL, NULL);
while ((dev = class_dev_iter_next(&iter)))
- class_intf->remove_dev(dev, class_intf);
+ class_intf->remove_dev(dev);
class_dev_iter_exit(&iter);
}
- mutex_unlock(&parent->p->mutex);
+ mutex_unlock(&sp->mutex);
- class_put(parent);
+ /*
+ * Decrement the reference count twice, once for the class_to_subsys()
+ * call in the start of this function, and the second one from the
+ * reference increment in class_interface_register()
+ */
+ subsys_put(sp);
+ subsys_put(sp);
}
+EXPORT_SYMBOL_GPL(class_interface_unregister);
-ssize_t show_class_attr_string(struct class *class,
- struct class_attribute *attr, char *buf)
+ssize_t show_class_attr_string(const struct class *class,
+ const struct class_attribute *attr, char *buf)
{
struct class_attribute_string *cs;
@@ -526,30 +601,10 @@ EXPORT_SYMBOL_GPL(class_compat_unregister);
* a bus device
* @cls: the compatibility class
* @dev: the target bus device
- * @device_link: an optional device to which a "device" link should be created
*/
-int class_compat_create_link(struct class_compat *cls, struct device *dev,
- struct device *device_link)
+int class_compat_create_link(struct class_compat *cls, struct device *dev)
{
- int error;
-
- error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
- if (error)
- return error;
-
- /*
- * Optionally add a "device" link (typically to the parent), as a
- * class device would have one and we want to provide as much
- * backwards compatibility as possible.
- */
- if (device_link) {
- error = sysfs_create_link(&dev->kobj, &device_link->kobj,
- "device");
- if (error)
- sysfs_remove_link(cls->kobj, dev_name(dev));
- }
-
- return error;
+ return sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
}
EXPORT_SYMBOL_GPL(class_compat_create_link);
@@ -558,18 +613,38 @@ EXPORT_SYMBOL_GPL(class_compat_create_link);
* a bus device
* @cls: the compatibility class
* @dev: the target bus device
- * @device_link: an optional device to which a "device" link was previously
- * created
*/
-void class_compat_remove_link(struct class_compat *cls, struct device *dev,
- struct device *device_link)
+void class_compat_remove_link(struct class_compat *cls, struct device *dev)
{
- if (device_link)
- sysfs_remove_link(&dev->kobj, "device");
sysfs_remove_link(cls->kobj, dev_name(dev));
}
EXPORT_SYMBOL_GPL(class_compat_remove_link);
+/**
+ * class_is_registered - determine if at this moment in time, a class is
+ * registered in the driver core or not.
+ * @class: the class to check
+ *
+ * Returns a boolean to state if the class is registered in the driver core
+ * or not. Note that the value could switch right after this call is made,
+ * so only use this in places where you "know" it is safe to do so (usually
+ * to determine if the specific class has been registered yet or not).
+ *
+ * Be careful in using this.
+ */
+bool class_is_registered(const struct class *class)
+{
+ struct subsys_private *sp = class_to_subsys(class);
+ bool is_initialized = false;
+
+ if (sp) {
+ is_initialized = true;
+ subsys_put(sp);
+ }
+ return is_initialized;
+}
+EXPORT_SYMBOL_GPL(class_is_registered);
+
int __init classes_init(void)
{
class_kset = kset_create_and_add("class", NULL, NULL);
@@ -577,11 +652,3 @@ int __init classes_init(void)
return -ENOMEM;
return 0;
}
-
-EXPORT_SYMBOL_GPL(class_create_file_ns);
-EXPORT_SYMBOL_GPL(class_remove_file_ns);
-EXPORT_SYMBOL_GPL(class_unregister);
-EXPORT_SYMBOL_GPL(class_destroy);
-
-EXPORT_SYMBOL_GPL(class_interface_register);
-EXPORT_SYMBOL_GPL(class_interface_unregister);
diff --git a/drivers/base/component.c b/drivers/base/component.c
index 5eadeac6c532..024ad9471b8a 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -87,17 +87,17 @@ static int component_devices_show(struct seq_file *s, void *data)
size_t i;
mutex_lock(&component_mutex);
- seq_printf(s, "%-40s %20s\n", "aggregate_device name", "status");
- seq_puts(s, "-------------------------------------------------------------\n");
- seq_printf(s, "%-40s %20s\n\n",
+ seq_printf(s, "%-50s %20s\n", "aggregate_device name", "status");
+ seq_puts(s, "-----------------------------------------------------------------------\n");
+ seq_printf(s, "%-50s %20s\n\n",
dev_name(m->parent), m->bound ? "bound" : "not bound");
- seq_printf(s, "%-40s %20s\n", "device name", "status");
- seq_puts(s, "-------------------------------------------------------------\n");
+ seq_printf(s, "%-50s %20s\n", "device name", "status");
+ seq_puts(s, "-----------------------------------------------------------------------\n");
for (i = 0; i < match->num; i++) {
struct component *component = match->compare[i].component;
- seq_printf(s, "%-40s %20s\n",
+ seq_printf(s, "%-50s %20s\n",
component ? dev_name(component->dev) : "(unknown)",
component ? (component->bound ? "bound" : "not bound") : "not registered");
}
@@ -125,7 +125,7 @@ static void component_debugfs_add(struct aggregate_device *m)
static void component_debugfs_del(struct aggregate_device *m)
{
- debugfs_remove(debugfs_lookup(dev_name(m->parent), component_debugfs_dir));
+ debugfs_lookup_and_remove(dev_name(m->parent), component_debugfs_dir);
}
#else
@@ -569,10 +569,28 @@ void component_master_del(struct device *parent,
}
EXPORT_SYMBOL_GPL(component_master_del);
+bool component_master_is_bound(struct device *parent,
+ const struct component_master_ops *ops)
+{
+ struct aggregate_device *adev;
+
+ guard(mutex)(&component_mutex);
+ adev = __aggregate_find(parent, ops);
+ if (!adev)
+ return 0;
+
+ return adev->bound;
+}
+EXPORT_SYMBOL_GPL(component_master_is_bound);
+
static void component_unbind(struct component *component,
struct aggregate_device *adev, void *data)
{
- WARN_ON(!component->bound);
+ if (WARN_ON(!component->bound))
+ return;
+
+ dev_dbg(adev->parent, "unbinding %s component %p (ops %ps)\n",
+ dev_name(component->dev), component, component->ops);
if (component->ops && component->ops->unbind)
component->ops->unbind(component->dev, adev->parent, data);
@@ -751,7 +769,7 @@ static int __component_add(struct device *dev, const struct component_ops *ops,
* component_bind_all(). See also &struct component_ops.
*
* @subcomponent must be nonzero and is used to differentiate between multiple
- * components registerd on the same device @dev. These components are match
+ * components registered on the same device @dev. These components are match
* using component_match_add_typed().
*
* The component needs to be unregistered at driver unload/disconnect by
@@ -781,7 +799,7 @@ EXPORT_SYMBOL_GPL(component_add_typed);
* The component needs to be unregistered at driver unload/disconnect by
* calling component_del().
*
- * See also component_add_typed() for a variant that allows multipled different
+ * See also component_add_typed() for a variant that allows multiple different
* components on the same device.
*/
int component_add(struct device *dev, const struct component_ops *ops)
diff --git a/drivers/base/container.c b/drivers/base/container.c
index 1ba42d2d3532..f40588ebc3f5 100644
--- a/drivers/base/container.c
+++ b/drivers/base/container.c
@@ -24,7 +24,7 @@ static int container_offline(struct device *dev)
return cdev->offline ? cdev->offline(cdev) : 0;
}
-struct bus_type container_subsys = {
+const struct bus_type container_subsys = {
.name = CONTAINER_BUS_NAME,
.dev_name = CONTAINER_BUS_NAME,
.online = trivial_online,
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 753e7cca0f40..40de2f51a1b1 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -9,57 +9,49 @@
*/
#include <linux/acpi.h>
+#include <linux/blkdev.h>
+#include <linux/cleanup.h>
#include <linux/cpufreq.h>
#include <linux/device.h>
+#include <linux/dma-map-ops.h> /* for dma_default_coherent */
#include <linux/err.h>
#include <linux/fwnode.h>
#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/kdev_t.h>
+#include <linux/kstrtox.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/blkdev.h>
-#include <linux/mutex.h>
#include <linux/pm_runtime.h>
-#include <linux/netdevice.h>
-#include <linux/sched/signal.h>
#include <linux/sched/mm.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
#include <linux/swiotlb.h>
#include <linux/sysfs.h>
-#include <linux/dma-map-ops.h> /* for dma_default_coherent */
#include "base.h"
#include "physical_location.h"
#include "power/power.h"
-#ifdef CONFIG_SYSFS_DEPRECATED
-#ifdef CONFIG_SYSFS_DEPRECATED_V2
-long sysfs_deprecated = 1;
-#else
-long sysfs_deprecated = 0;
-#endif
-static int __init sysfs_deprecated_setup(char *arg)
-{
- return kstrtol(arg, 10, &sysfs_deprecated);
-}
-early_param("sysfs.deprecated", sysfs_deprecated_setup);
-#endif
-
/* Device links support. */
static LIST_HEAD(deferred_sync);
static unsigned int defer_sync_state_count = 1;
static DEFINE_MUTEX(fwnode_link_lock);
static bool fw_devlink_is_permissive(void);
+static void __fw_devlink_link_to_consumers(struct device *dev);
static bool fw_devlink_drv_reg_done;
static bool fw_devlink_best_effort;
+static struct workqueue_struct *device_link_wq;
/**
- * fwnode_link_add - Create a link between two fwnode_handles.
+ * __fwnode_link_add - Create a link between two fwnode_handles.
* @con: Consumer end of the link.
* @sup: Supplier end of the link.
+ * @flags: Link flags.
*
* Create a fwnode link between fwnode handles @con and @sup. The fwnode link
* represents the detail that the firmware lists @sup fwnode as supplying a
@@ -73,36 +65,41 @@ static bool fw_devlink_best_effort;
* Attempts to create duplicate links between the same pair of fwnode handles
* are ignored and there is no reference counting.
*/
-int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup)
+static int __fwnode_link_add(struct fwnode_handle *con,
+ struct fwnode_handle *sup, u8 flags)
{
struct fwnode_link *link;
- int ret = 0;
-
- mutex_lock(&fwnode_link_lock);
list_for_each_entry(link, &sup->consumers, s_hook)
- if (link->consumer == con)
- goto out;
+ if (link->consumer == con) {
+ link->flags |= flags;
+ return 0;
+ }
link = kzalloc(sizeof(*link), GFP_KERNEL);
- if (!link) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!link)
+ return -ENOMEM;
link->supplier = sup;
INIT_LIST_HEAD(&link->s_hook);
link->consumer = con;
INIT_LIST_HEAD(&link->c_hook);
+ link->flags = flags;
list_add(&link->s_hook, &sup->consumers);
list_add(&link->c_hook, &con->suppliers);
- pr_debug("%pfwP Linked as a fwnode consumer to %pfwP\n",
+ pr_debug("%pfwf Linked as a fwnode consumer to %pfwf\n",
con, sup);
-out:
- mutex_unlock(&fwnode_link_lock);
- return ret;
+ return 0;
+}
+
+int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup,
+ u8 flags)
+{
+ guard(mutex)(&fwnode_link_lock);
+
+ return __fwnode_link_add(con, sup, flags);
}
/**
@@ -113,7 +110,7 @@ out:
*/
static void __fwnode_link_del(struct fwnode_link *link)
{
- pr_debug("%pfwP Dropping the fwnode link to %pfwP\n",
+ pr_debug("%pfwf Dropping the fwnode link to %pfwf\n",
link->consumer, link->supplier);
list_del(&link->s_hook);
list_del(&link->c_hook);
@@ -121,6 +118,19 @@ static void __fwnode_link_del(struct fwnode_link *link)
}
/**
+ * __fwnode_link_cycle - Mark a fwnode link as being part of a cycle.
+ * @link: the fwnode_link to be marked
+ *
+ * The fwnode_link_lock needs to be held when this function is called.
+ */
+static void __fwnode_link_cycle(struct fwnode_link *link)
+{
+ pr_debug("%pfwf: cycle: depends on %pfwf\n",
+ link->consumer, link->supplier);
+ link->flags |= FWLINK_FLAG_CYCLE;
+}
+
+/**
* fwnode_links_purge_suppliers - Delete all supplier links of fwnode_handle.
* @fwnode: fwnode whose supplier links need to be deleted
*
@@ -130,10 +140,10 @@ static void fwnode_links_purge_suppliers(struct fwnode_handle *fwnode)
{
struct fwnode_link *link, *tmp;
- mutex_lock(&fwnode_link_lock);
+ guard(mutex)(&fwnode_link_lock);
+
list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook)
__fwnode_link_del(link);
- mutex_unlock(&fwnode_link_lock);
}
/**
@@ -146,10 +156,10 @@ static void fwnode_links_purge_consumers(struct fwnode_handle *fwnode)
{
struct fwnode_link *link, *tmp;
- mutex_lock(&fwnode_link_lock);
+ guard(mutex)(&fwnode_link_lock);
+
list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook)
__fwnode_link_del(link);
- mutex_unlock(&fwnode_link_lock);
}
/**
@@ -180,7 +190,51 @@ void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL_GPL(fw_devlink_purge_absent_suppliers);
-#ifdef CONFIG_SRCU
+/**
+ * __fwnode_links_move_consumers - Move consumer from @from to @to fwnode_handle
+ * @from: move consumers away from this fwnode
+ * @to: move consumers to this fwnode
+ *
+ * Move all consumer links from @from fwnode to @to fwnode.
+ */
+static void __fwnode_links_move_consumers(struct fwnode_handle *from,
+ struct fwnode_handle *to)
+{
+ struct fwnode_link *link, *tmp;
+
+ list_for_each_entry_safe(link, tmp, &from->consumers, s_hook) {
+ __fwnode_link_add(link->consumer, to, link->flags);
+ __fwnode_link_del(link);
+ }
+}
+
+/**
+ * __fw_devlink_pickup_dangling_consumers - Pick up dangling consumers
+ * @fwnode: fwnode from which to pick up dangling consumers
+ * @new_sup: fwnode of new supplier
+ *
+ * If the @fwnode has a corresponding struct device and the device supports
+ * probing (that is, added to a bus), then we want to let fw_devlink create
+ * MANAGED device links to this device, so leave @fwnode and its descendant's
+ * fwnode links alone.
+ *
+ * Otherwise, move its consumers to the new supplier @new_sup.
+ */
+static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode,
+ struct fwnode_handle *new_sup)
+{
+ struct fwnode_handle *child;
+
+ if (fwnode->dev && fwnode->dev->bus)
+ return;
+
+ fwnode->flags |= FWNODE_FLAG_NOT_DEVICE;
+ __fwnode_links_move_consumers(fwnode, new_sup);
+
+ fwnode_for_each_available_child_node(fwnode, child)
+ __fw_devlink_pickup_dangling_consumers(child, new_sup);
+}
+
static DEFINE_MUTEX(device_links_lock);
DEFINE_STATIC_SRCU(device_links_srcu);
@@ -219,47 +273,6 @@ static void device_link_remove_from_lists(struct device_link *link)
list_del_rcu(&link->s_node);
list_del_rcu(&link->c_node);
}
-#else /* !CONFIG_SRCU */
-static DECLARE_RWSEM(device_links_lock);
-
-static inline void device_links_write_lock(void)
-{
- down_write(&device_links_lock);
-}
-
-static inline void device_links_write_unlock(void)
-{
- up_write(&device_links_lock);
-}
-
-int device_links_read_lock(void)
-{
- down_read(&device_links_lock);
- return 0;
-}
-
-void device_links_read_unlock(int not_used)
-{
- up_read(&device_links_lock);
-}
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-int device_links_read_lock_held(void)
-{
- return lockdep_is_held(&device_links_lock);
-}
-#endif
-
-static inline void device_link_synchronize_removal(void)
-{
-}
-
-static void device_link_remove_from_lists(struct device_link *link)
-{
- list_del(&link->s_node);
- list_del(&link->c_node);
-}
-#endif /* !CONFIG_SRCU */
static bool device_is_ancestor(struct device *dev, struct device *target)
{
@@ -271,6 +284,14 @@ static bool device_is_ancestor(struct device *dev, struct device *target)
return false;
}
+#define DL_MARKER_FLAGS (DL_FLAG_INFERRED | \
+ DL_FLAG_CYCLE | \
+ DL_FLAG_MANAGED)
+bool device_link_flag_is_sync_state_only(u32 flags)
+{
+ return (flags & ~DL_MARKER_FLAGS) == DL_FLAG_SYNC_STATE_ONLY;
+}
+
/**
* device_is_dependent - Check if one device depends on another one
* @dev: Device to check dependencies for.
@@ -279,7 +300,7 @@ static bool device_is_ancestor(struct device *dev, struct device *target)
* Check if @target depends on @dev or any device dependent on it (its child or
* its consumer etc). Return 1 if that is the case or 0 otherwise.
*/
-int device_is_dependent(struct device *dev, void *target)
+static int device_is_dependent(struct device *dev, void *target)
{
struct device_link *link;
int ret;
@@ -297,8 +318,7 @@ int device_is_dependent(struct device *dev, void *target)
return ret;
list_for_each_entry(link, &dev->links.consumers, s_node) {
- if ((link->flags & ~DL_FLAG_INFERRED) ==
- (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
+ if (device_link_flag_is_sync_state_only(link->flags))
continue;
if (link->consumer == target)
@@ -371,8 +391,7 @@ static int device_reorder_to_tail(struct device *dev, void *not_used)
device_for_each_child(dev, NULL, device_reorder_to_tail);
list_for_each_entry(link, &dev->links.consumers, s_node) {
- if ((link->flags & ~DL_FLAG_INFERRED) ==
- (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
+ if (device_link_flag_is_sync_state_only(link->flags))
continue;
device_reorder_to_tail(link->consumer, NULL);
}
@@ -441,9 +460,9 @@ static ssize_t auto_remove_on_show(struct device *dev,
struct device_link *link = to_devlink(dev);
const char *output;
- if (link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
+ if (device_link_test(link, DL_FLAG_AUTOREMOVE_SUPPLIER))
output = "supplier unbind";
- else if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER)
+ else if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER))
output = "consumer unbind";
else
output = "never";
@@ -457,7 +476,7 @@ static ssize_t runtime_pm_show(struct device *dev,
{
struct device_link *link = to_devlink(dev);
- return sysfs_emit(buf, "%d\n", !!(link->flags & DL_FLAG_PM_RUNTIME));
+ return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_PM_RUNTIME));
}
static DEVICE_ATTR_RO(runtime_pm);
@@ -466,8 +485,7 @@ static ssize_t sync_state_only_show(struct device *dev,
{
struct device_link *link = to_devlink(dev);
- return sysfs_emit(buf, "%d\n",
- !!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
+ return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_SYNC_STATE_ONLY));
}
static DEVICE_ATTR_RO(sync_state_only);
@@ -513,36 +531,39 @@ static void devlink_dev_release(struct device *dev)
/*
* It may take a while to complete this work because of the SRCU
* synchronization in device_link_release_fn() and if the consumer or
- * supplier devices get deleted when it runs, so put it into the "long"
- * workqueue.
+ * supplier devices get deleted when it runs, so put it into the
+ * dedicated workqueue.
+ */
+ queue_work(device_link_wq, &link->rm_work);
+}
+
+/**
+ * device_link_wait_removal - Wait for ongoing devlink removal jobs to terminate
+ */
+void device_link_wait_removal(void)
+{
+ /*
+ * devlink removal jobs are queued in the dedicated work queue.
+ * To be sure that all removal jobs are terminated, ensure that any
+ * scheduled work has run to completion.
*/
- queue_work(system_long_wq, &link->rm_work);
+ flush_workqueue(device_link_wq);
}
+EXPORT_SYMBOL_GPL(device_link_wait_removal);
-static struct class devlink_class = {
+static const struct class devlink_class = {
.name = "devlink",
- .owner = THIS_MODULE,
.dev_groups = devlink_groups,
.dev_release = devlink_dev_release,
};
-static int devlink_add_symlinks(struct device *dev,
- struct class_interface *class_intf)
+static int devlink_add_symlinks(struct device *dev)
{
+ char *buf_con __free(kfree) = NULL, *buf_sup __free(kfree) = NULL;
int ret;
- size_t len;
struct device_link *link = to_devlink(dev);
struct device *sup = link->supplier;
struct device *con = link->consumer;
- char *buf;
-
- len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)),
- strlen(dev_bus_name(con)) + strlen(dev_name(con)));
- len += strlen(":");
- len += strlen("supplier:") + 1;
- buf = kzalloc(len, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
ret = sysfs_create_link(&link->link_dev.kobj, &sup->kobj, "supplier");
if (ret)
@@ -552,59 +573,64 @@ static int devlink_add_symlinks(struct device *dev,
if (ret)
goto err_con;
- snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
- ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf);
+ buf_con = kasprintf(GFP_KERNEL, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
+ if (!buf_con) {
+ ret = -ENOMEM;
+ goto err_con_dev;
+ }
+
+ ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf_con);
if (ret)
goto err_con_dev;
- snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
- ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf);
+ buf_sup = kasprintf(GFP_KERNEL, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
+ if (!buf_sup) {
+ ret = -ENOMEM;
+ goto err_sup_dev;
+ }
+
+ ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf_sup);
if (ret)
goto err_sup_dev;
goto out;
err_sup_dev:
- snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
- sysfs_remove_link(&sup->kobj, buf);
+ sysfs_remove_link(&sup->kobj, buf_con);
err_con_dev:
sysfs_remove_link(&link->link_dev.kobj, "consumer");
err_con:
sysfs_remove_link(&link->link_dev.kobj, "supplier");
out:
- kfree(buf);
return ret;
}
-static void devlink_remove_symlinks(struct device *dev,
- struct class_interface *class_intf)
+static void devlink_remove_symlinks(struct device *dev)
{
+ char *buf_con __free(kfree) = NULL, *buf_sup __free(kfree) = NULL;
struct device_link *link = to_devlink(dev);
- size_t len;
struct device *sup = link->supplier;
struct device *con = link->consumer;
- char *buf;
sysfs_remove_link(&link->link_dev.kobj, "consumer");
sysfs_remove_link(&link->link_dev.kobj, "supplier");
- len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)),
- strlen(dev_bus_name(con)) + strlen(dev_name(con)));
- len += strlen(":");
- len += strlen("supplier:") + 1;
- buf = kzalloc(len, GFP_KERNEL);
- if (!buf) {
- WARN(1, "Unable to properly free device link symlinks!\n");
- return;
- }
-
if (device_is_registered(con)) {
- snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
- sysfs_remove_link(&con->kobj, buf);
+ buf_sup = kasprintf(GFP_KERNEL, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
+ if (!buf_sup)
+ goto out;
+ sysfs_remove_link(&con->kobj, buf_sup);
}
- snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
- sysfs_remove_link(&sup->kobj, buf);
- kfree(buf);
+
+ buf_con = kasprintf(GFP_KERNEL, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
+ if (!buf_con)
+ goto out;
+ sysfs_remove_link(&sup->kobj, buf_con);
+
+ return;
+
+out:
+ WARN(1, "Unable to properly free device link symlinks!\n");
}
static struct class_interface devlink_class_intf = {
@@ -633,7 +659,8 @@ postcore_initcall(devlink_class_init);
DL_FLAG_AUTOREMOVE_SUPPLIER | \
DL_FLAG_AUTOPROBE_CONSUMER | \
DL_FLAG_SYNC_STATE_ONLY | \
- DL_FLAG_INFERRED)
+ DL_FLAG_INFERRED | \
+ DL_FLAG_CYCLE)
#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \
DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)
@@ -644,6 +671,9 @@ postcore_initcall(devlink_class_init);
* @supplier: Supplier end of the link.
* @flags: Link flags.
*
+ * Return: On success, a device_link struct will be returned.
+ * On error or invalid flag settings, NULL will be returned.
+ *
* The caller is responsible for the proper synchronization of the link creation
* with runtime PM. First, setting the DL_FLAG_PM_RUNTIME flag will cause the
* runtime PM framework to take the link into account. Second, if the
@@ -702,8 +732,6 @@ struct device_link *device_link_add(struct device *consumer,
if (!consumer || !supplier || consumer == supplier ||
flags & ~DL_ADD_VALID_FLAGS ||
(flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
- (flags & DL_FLAG_SYNC_STATE_ONLY &&
- (flags & ~DL_FLAG_INFERRED) != DL_FLAG_SYNC_STATE_ONLY) ||
(flags & DL_FLAG_AUTOPROBE_CONSUMER &&
flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
DL_FLAG_AUTOREMOVE_SUPPLIER)))
@@ -719,6 +747,10 @@ struct device_link *device_link_add(struct device *consumer,
if (!(flags & DL_FLAG_STATELESS))
flags |= DL_FLAG_MANAGED;
+ if (flags & DL_FLAG_SYNC_STATE_ONLY &&
+ !device_link_flag_is_sync_state_only(flags))
+ return NULL;
+
device_links_write_lock();
device_pm_lock();
@@ -759,12 +791,12 @@ struct device_link *device_link_add(struct device *consumer,
if (link->consumer != consumer)
continue;
- if (link->flags & DL_FLAG_INFERRED &&
+ if (device_link_test(link, DL_FLAG_INFERRED) &&
!(flags & DL_FLAG_INFERRED))
link->flags &= ~DL_FLAG_INFERRED;
if (flags & DL_FLAG_PM_RUNTIME) {
- if (!(link->flags & DL_FLAG_PM_RUNTIME)) {
+ if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) {
pm_runtime_new_link(consumer);
link->flags |= DL_FLAG_PM_RUNTIME;
}
@@ -774,8 +806,8 @@ struct device_link *device_link_add(struct device *consumer,
if (flags & DL_FLAG_STATELESS) {
kref_get(&link->kref);
- if (link->flags & DL_FLAG_SYNC_STATE_ONLY &&
- !(link->flags & DL_FLAG_STATELESS)) {
+ if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) &&
+ !device_link_test(link, DL_FLAG_STATELESS)) {
link->flags |= DL_FLAG_STATELESS;
goto reorder;
} else {
@@ -790,7 +822,7 @@ struct device_link *device_link_add(struct device *consumer,
* update the existing link to stay around longer.
*/
if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) {
- if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) {
+ if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) {
link->flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER;
link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER;
}
@@ -798,12 +830,12 @@ struct device_link *device_link_add(struct device *consumer,
link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER |
DL_FLAG_AUTOREMOVE_SUPPLIER);
}
- if (!(link->flags & DL_FLAG_MANAGED)) {
+ if (!device_link_test(link, DL_FLAG_MANAGED)) {
kref_get(&link->kref);
link->flags |= DL_FLAG_MANAGED;
device_link_init_status(link, consumer, supplier);
}
- if (link->flags & DL_FLAG_SYNC_STATE_ONLY &&
+ if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) &&
!(flags & DL_FLAG_SYNC_STATE_ONLY)) {
link->flags &= ~DL_FLAG_SYNC_STATE_ONLY;
goto reorder;
@@ -907,7 +939,7 @@ static void __device_link_del(struct kref *kref)
static void device_link_put_kref(struct device_link *link)
{
- if (link->flags & DL_FLAG_STATELESS)
+ if (device_link_test(link, DL_FLAG_STATELESS))
kref_put(&link->kref, __device_link_del);
else if (!device_is_registered(link->consumer))
__device_link_del(&link->kref);
@@ -971,7 +1003,7 @@ static void device_links_missing_supplier(struct device *dev)
if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) {
WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
} else {
- WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
+ WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY));
WRITE_ONCE(link->status, DL_STATE_DORMANT);
}
}
@@ -983,6 +1015,22 @@ static bool dev_is_best_effort(struct device *dev)
(dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT));
}
+static struct fwnode_handle *fwnode_links_check_suppliers(
+ struct fwnode_handle *fwnode)
+{
+ struct fwnode_link *link;
+
+ if (!fwnode || fw_devlink_is_permissive())
+ return NULL;
+
+ list_for_each_entry(link, &fwnode->suppliers, c_hook)
+ if (!(link->flags &
+ (FWLINK_FLAG_CYCLE | FWLINK_FLAG_IGNORE)))
+ return link->supplier;
+
+ return NULL;
+}
+
/**
* device_links_check_suppliers - Check presence of supplier drivers.
* @dev: Consumer device.
@@ -1009,45 +1057,36 @@ int device_links_check_suppliers(struct device *dev)
* Device waiting for supplier to become available is not allowed to
* probe.
*/
- mutex_lock(&fwnode_link_lock);
- if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) &&
- !fw_devlink_is_permissive()) {
- sup_fw = list_first_entry(&dev->fwnode->suppliers,
- struct fwnode_link,
- c_hook)->supplier;
- if (!dev_is_best_effort(dev)) {
- fwnode_ret = -EPROBE_DEFER;
- dev_err_probe(dev, -EPROBE_DEFER,
- "wait for supplier %pfwP\n", sup_fw);
- } else {
- fwnode_ret = -EAGAIN;
+ scoped_guard(mutex, &fwnode_link_lock) {
+ sup_fw = fwnode_links_check_suppliers(dev->fwnode);
+ if (sup_fw) {
+ if (dev_is_best_effort(dev))
+ fwnode_ret = -EAGAIN;
+ else
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "wait for supplier %pfwf\n", sup_fw);
}
}
- mutex_unlock(&fwnode_link_lock);
- if (fwnode_ret == -EPROBE_DEFER)
- return fwnode_ret;
device_links_write_lock();
list_for_each_entry(link, &dev->links.suppliers, c_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
if (link->status != DL_STATE_AVAILABLE &&
- !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) {
+ !device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) {
if (dev_is_best_effort(dev) &&
- link->flags & DL_FLAG_INFERRED &&
+ device_link_test(link, DL_FLAG_INFERRED) &&
!link->supplier->can_match) {
ret = -EAGAIN;
continue;
}
device_links_missing_supplier(dev);
- dev_err_probe(dev, -EPROBE_DEFER,
- "supplier %s not ready\n",
- dev_name(link->supplier));
- ret = -EPROBE_DEFER;
+ ret = dev_err_probe(dev, -EPROBE_DEFER,
+ "supplier %s not ready\n", dev_name(link->supplier));
break;
}
WRITE_ONCE(link->status, DL_STATE_CONSUMER_PROBE);
@@ -1088,7 +1127,7 @@ static void __device_links_queue_sync_state(struct device *dev,
return;
list_for_each_entry(link, &dev->links.consumers, s_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
if (link->status != DL_STATE_ACTIVE)
return;
@@ -1129,10 +1168,7 @@ static void device_links_flush_sync_list(struct list_head *list,
if (dev != dont_lock_dev)
device_lock(dev);
- if (dev->bus->sync_state)
- dev->bus->sync_state(dev);
- else if (dev->driver && dev->driver->sync_state)
- dev->driver->sync_state(dev);
+ dev_sync_state(dev);
if (dev != dont_lock_dev)
device_unlock(dev);
@@ -1203,7 +1239,8 @@ static ssize_t waiting_for_supplier_show(struct device *dev,
bool val;
device_lock(dev);
- val = !list_empty(&dev->fwnode->suppliers);
+ scoped_guard(mutex, &fwnode_link_lock)
+ val = !!fwnode_links_check_suppliers(dev->fwnode);
device_unlock(dev);
return sysfs_emit(buf, "%u\n", val);
}
@@ -1230,7 +1267,7 @@ void device_links_force_bind(struct device *dev)
device_links_write_lock();
list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
if (link->status != DL_STATE_AVAILABLE) {
@@ -1266,23 +1303,32 @@ void device_links_driver_bound(struct device *dev)
* them. So, fw_devlink no longer needs to create device links to any
* of the device's suppliers.
*
- * Also, if a child firmware node of this bound device is not added as
- * a device by now, assume it is never going to be added and make sure
- * other devices don't defer probe indefinitely by waiting for such a
- * child device.
+ * Also, if a child firmware node of this bound device is not added as a
+ * device by now, assume it is never going to be added. Make this bound
+ * device the fallback supplier to the dangling consumers of the child
+ * firmware node because this bound device is probably implementing the
+ * child firmware node functionality and we don't want the dangling
+ * consumers to defer probe indefinitely waiting for a device for the
+ * child firmware node.
*/
if (dev->fwnode && dev->fwnode->dev == dev) {
struct fwnode_handle *child;
+
fwnode_links_purge_suppliers(dev->fwnode);
+
+ guard(mutex)(&fwnode_link_lock);
+
fwnode_for_each_available_child_node(dev->fwnode, child)
- fw_devlink_purge_absent_suppliers(child);
+ __fw_devlink_pickup_dangling_consumers(child,
+ dev->fwnode);
+ __fw_devlink_link_to_consumers(dev);
}
device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_links_write_lock();
list_for_each_entry(link, &dev->links.consumers, s_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
/*
@@ -1298,7 +1344,7 @@ void device_links_driver_bound(struct device *dev)
WARN_ON(link->status != DL_STATE_DORMANT);
WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
- if (link->flags & DL_FLAG_AUTOPROBE_CONSUMER)
+ if (device_link_test(link, DL_FLAG_AUTOPROBE_CONSUMER))
driver_deferred_probe_add(link->consumer);
}
@@ -1310,11 +1356,11 @@ void device_links_driver_bound(struct device *dev)
list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) {
struct device *supplier;
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
supplier = link->supplier;
- if (link->flags & DL_FLAG_SYNC_STATE_ONLY) {
+ if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) {
/*
* When DL_FLAG_SYNC_STATE_ONLY is set, it means no
* other DL_MANAGED_LINK_FLAGS have been set. So, it's
@@ -1322,7 +1368,7 @@ void device_links_driver_bound(struct device *dev)
*/
device_link_drop_managed(link);
} else if (dev_is_best_effort(dev) &&
- link->flags & DL_FLAG_INFERRED &&
+ device_link_test(link, DL_FLAG_INFERRED) &&
link->status != DL_STATE_CONSUMER_PROBE &&
!link->supplier->can_match) {
/*
@@ -1374,10 +1420,10 @@ static void __device_links_no_driver(struct device *dev)
struct device_link *link, *ln;
list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
- if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) {
+ if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) {
device_link_drop_managed(link);
continue;
}
@@ -1389,7 +1435,7 @@ static void __device_links_no_driver(struct device *dev)
if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) {
WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
} else {
- WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
+ WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY));
WRITE_ONCE(link->status, DL_STATE_DORMANT);
}
}
@@ -1414,7 +1460,7 @@ void device_links_no_driver(struct device *dev)
device_links_write_lock();
list_for_each_entry(link, &dev->links.consumers, s_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
/*
@@ -1451,10 +1497,10 @@ void device_links_driver_cleanup(struct device *dev)
device_links_write_lock();
list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
- WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER);
+ WARN_ON(device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER));
WARN_ON(link->status != DL_STATE_SUPPLIER_UNBIND);
/*
@@ -1463,7 +1509,7 @@ void device_links_driver_cleanup(struct device *dev)
* has moved to DL_STATE_SUPPLIER_UNBIND.
*/
if (link->status == DL_STATE_SUPPLIER_UNBIND &&
- link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
+ device_link_test(link, DL_FLAG_AUTOREMOVE_SUPPLIER))
device_link_drop_managed(link);
WRITE_ONCE(link->status, DL_STATE_DORMANT);
@@ -1497,7 +1543,7 @@ bool device_links_busy(struct device *dev)
device_links_write_lock();
list_for_each_entry(link, &dev->links.consumers, s_node) {
- if (!(link->flags & DL_FLAG_MANAGED))
+ if (!device_link_test(link, DL_FLAG_MANAGED))
continue;
if (link->status == DL_STATE_CONSUMER_PROBE
@@ -1539,8 +1585,8 @@ void device_links_unbind_consumers(struct device *dev)
list_for_each_entry(link, &dev->links.consumers, s_node) {
enum device_link_state status;
- if (!(link->flags & DL_FLAG_MANAGED) ||
- link->flags & DL_FLAG_SYNC_STATE_ONLY)
+ if (!device_link_test(link, DL_FLAG_MANAGED) ||
+ device_link_test(link, DL_FLAG_SYNC_STATE_ONLY))
continue;
status = link->status;
@@ -1606,7 +1652,7 @@ static void device_links_purge(struct device *dev)
#define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \
DL_FLAG_PM_RUNTIME)
-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
+static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_RPM;
static int __init fw_devlink_setup(char *arg)
{
if (!arg)
@@ -1625,15 +1671,43 @@ static int __init fw_devlink_setup(char *arg)
}
early_param("fw_devlink", fw_devlink_setup);
-static bool fw_devlink_strict = true;
+static bool fw_devlink_strict;
static int __init fw_devlink_strict_setup(char *arg)
{
- return strtobool(arg, &fw_devlink_strict);
+ return kstrtobool(arg, &fw_devlink_strict);
}
early_param("fw_devlink.strict", fw_devlink_strict_setup);
-u32 fw_devlink_get_flags(void)
+#define FW_DEVLINK_SYNC_STATE_STRICT 0
+#define FW_DEVLINK_SYNC_STATE_TIMEOUT 1
+
+#ifndef CONFIG_FW_DEVLINK_SYNC_STATE_TIMEOUT
+static int fw_devlink_sync_state;
+#else
+static int fw_devlink_sync_state = FW_DEVLINK_SYNC_STATE_TIMEOUT;
+#endif
+
+static int __init fw_devlink_sync_state_setup(char *arg)
{
+ if (!arg)
+ return -EINVAL;
+
+ if (strcmp(arg, "strict") == 0) {
+ fw_devlink_sync_state = FW_DEVLINK_SYNC_STATE_STRICT;
+ return 0;
+ } else if (strcmp(arg, "timeout") == 0) {
+ fw_devlink_sync_state = FW_DEVLINK_SYNC_STATE_TIMEOUT;
+ return 0;
+ }
+ return -EINVAL;
+}
+early_param("fw_devlink.sync_state", fw_devlink_sync_state_setup);
+
+static inline u32 fw_devlink_get_flags(u8 fwlink_flags)
+{
+ if (fwlink_flags & FWLINK_FLAG_CYCLE)
+ return FW_DEVLINK_FLAGS_PERMISSIVE | DL_FLAG_CYCLE;
+
return fw_devlink_flags;
}
@@ -1668,10 +1742,10 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode)
static void fw_devlink_relax_link(struct device_link *link)
{
- if (!(link->flags & DL_FLAG_INFERRED))
+ if (!device_link_test(link, DL_FLAG_INFERRED))
return;
- if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE))
+ if (device_link_flag_is_sync_state_only(link->flags))
return;
pm_runtime_drop_link(link);
@@ -1699,6 +1773,44 @@ void fw_devlink_drivers_done(void)
device_links_write_unlock();
}
+static int fw_devlink_dev_sync_state(struct device *dev, void *data)
+{
+ struct device_link *link = to_devlink(dev);
+ struct device *sup = link->supplier;
+
+ if (!device_link_test(link, DL_FLAG_MANAGED) ||
+ link->status == DL_STATE_ACTIVE || sup->state_synced ||
+ !dev_has_sync_state(sup))
+ return 0;
+
+ if (fw_devlink_sync_state == FW_DEVLINK_SYNC_STATE_STRICT) {
+ dev_info(sup, "sync_state() pending due to %s\n",
+ dev_name(link->consumer));
+ return 0;
+ }
+
+ if (!list_empty(&sup->links.defer_sync))
+ return 0;
+
+ dev_warn(sup, "Timed out. Forcing sync_state()\n");
+ sup->state_synced = true;
+ get_device(sup);
+ list_add_tail(&sup->links.defer_sync, data);
+
+ return 0;
+}
+
+void fw_devlink_probing_done(void)
+{
+ LIST_HEAD(sync_list);
+
+ device_links_write_lock();
+ class_for_each_device(&devlink_class, NULL, &sync_list,
+ fw_devlink_dev_sync_state);
+ device_links_write_unlock();
+ device_links_flush_sync_list(&sync_list, NULL);
+}
+
/**
* wait_for_init_devices_probe - Try to probe any device needed for init
*
@@ -1768,44 +1880,204 @@ static void fw_devlink_unblock_consumers(struct device *dev)
device_links_write_unlock();
}
+static bool fwnode_init_without_drv(struct fwnode_handle *fwnode)
+{
+ struct device *dev;
+ bool ret;
+
+ if (!(fwnode->flags & FWNODE_FLAG_INITIALIZED))
+ return false;
+
+ dev = get_dev_from_fwnode(fwnode);
+ ret = !dev || dev->links.status == DL_DEV_NO_DRIVER;
+ put_device(dev);
+
+ return ret;
+}
+
+static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent;
+
+ fwnode_for_each_parent_node(fwnode, parent) {
+ if (fwnode_init_without_drv(parent)) {
+ fwnode_handle_put(parent);
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
- * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links
- * @con: Device to check dependencies for.
- * @sup: Device to check against.
- *
- * Check if @sup depends on @con or any device dependent on it (its child or
- * its consumer etc). When such a cyclic dependency is found, convert all
- * device links created solely by fw_devlink into SYNC_STATE_ONLY device links.
- * This is the equivalent of doing fw_devlink=permissive just between the
- * devices in the cycle. We need to do this because, at this point, fw_devlink
- * can't tell which of these dependencies is not a real dependency.
- *
- * Return 1 if a cycle is found. Otherwise, return 0.
+ * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child
+ * @ancestor: Firmware which is tested for being an ancestor
+ * @child: Firmware which is tested for being the child
+ *
+ * A node is considered an ancestor of itself too.
+ *
+ * Return: true if @ancestor is an ancestor of @child. Otherwise, returns false.
*/
-static int fw_devlink_relax_cycle(struct device *con, void *sup)
+static bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor,
+ const struct fwnode_handle *child)
{
- struct device_link *link;
- int ret;
+ struct fwnode_handle *parent;
- if (con == sup)
- return 1;
+ if (IS_ERR_OR_NULL(ancestor))
+ return false;
- ret = device_for_each_child(con, sup, fw_devlink_relax_cycle);
- if (ret)
- return ret;
+ if (child == ancestor)
+ return true;
- list_for_each_entry(link, &con->links.consumers, s_node) {
- if ((link->flags & ~DL_FLAG_INFERRED) ==
- (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
- continue;
+ fwnode_for_each_parent_node(child, parent) {
+ if (parent == ancestor) {
+ fwnode_handle_put(parent);
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode
+ * @fwnode: firmware node
+ *
+ * Given a firmware node (@fwnode), this function finds its closest ancestor
+ * firmware node that has a corresponding struct device and returns that struct
+ * device.
+ *
+ * The caller is responsible for calling put_device() on the returned device
+ * pointer.
+ *
+ * Return: a pointer to the device of the @fwnode's closest ancestor.
+ */
+static struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent;
+ struct device *dev;
+
+ fwnode_for_each_parent_node(fwnode, parent) {
+ dev = get_dev_from_fwnode(parent);
+ if (dev) {
+ fwnode_handle_put(parent);
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * __fw_devlink_relax_cycles - Relax and mark dependency cycles.
+ * @con_handle: Potential consumer device fwnode.
+ * @sup_handle: Potential supplier's fwnode.
+ *
+ * Needs to be called with fwnode_lock and device link lock held.
+ *
+ * Check if @sup_handle or any of its ancestors or suppliers direct/indirectly
+ * depend on @con. This function can detect multiple cyles between @sup_handle
+ * and @con. When such dependency cycles are found, convert all device links
+ * created solely by fw_devlink into SYNC_STATE_ONLY device links. Also, mark
+ * all fwnode links in the cycle with FWLINK_FLAG_CYCLE so that when they are
+ * converted into a device link in the future, they are created as
+ * SYNC_STATE_ONLY device links. This is the equivalent of doing
+ * fw_devlink=permissive just between the devices in the cycle. We need to do
+ * this because, at this point, fw_devlink can't tell which of these
+ * dependencies is not a real dependency.
+ *
+ * Return true if one or more cycles were found. Otherwise, return false.
+ */
+static bool __fw_devlink_relax_cycles(struct fwnode_handle *con_handle,
+ struct fwnode_handle *sup_handle)
+{
+ struct device *sup_dev = NULL, *par_dev = NULL, *con_dev = NULL;
+ struct fwnode_link *link;
+ struct device_link *dev_link;
+ bool ret = false;
+
+ if (!sup_handle)
+ return false;
+
+ /*
+ * We aren't trying to find all cycles. Just a cycle between con and
+ * sup_handle.
+ */
+ if (sup_handle->flags & FWNODE_FLAG_VISITED)
+ return false;
+
+ sup_handle->flags |= FWNODE_FLAG_VISITED;
+
+ /* Termination condition. */
+ if (sup_handle == con_handle) {
+ pr_debug("----- cycle: start -----\n");
+ ret = true;
+ goto out;
+ }
+
+ sup_dev = get_dev_from_fwnode(sup_handle);
+ con_dev = get_dev_from_fwnode(con_handle);
+ /*
+ * If sup_dev is bound to a driver and @con hasn't started binding to a
+ * driver, sup_dev can't be a consumer of @con. So, no need to check
+ * further.
+ */
+ if (sup_dev && sup_dev->links.status == DL_DEV_DRIVER_BOUND &&
+ con_dev && con_dev->links.status == DL_DEV_NO_DRIVER) {
+ ret = false;
+ goto out;
+ }
- if (!fw_devlink_relax_cycle(link->consumer, sup))
+ list_for_each_entry(link, &sup_handle->suppliers, c_hook) {
+ if (link->flags & FWLINK_FLAG_IGNORE)
continue;
- ret = 1;
+ if (__fw_devlink_relax_cycles(con_handle, link->supplier)) {
+ __fwnode_link_cycle(link);
+ ret = true;
+ }
+ }
- fw_devlink_relax_link(link);
+ /*
+ * Give priority to device parent over fwnode parent to account for any
+ * quirks in how fwnodes are converted to devices.
+ */
+ if (sup_dev)
+ par_dev = get_device(sup_dev->parent);
+ else
+ par_dev = fwnode_get_next_parent_dev(sup_handle);
+
+ if (par_dev && __fw_devlink_relax_cycles(con_handle, par_dev->fwnode)) {
+ pr_debug("%pfwf: cycle: child of %pfwf\n", sup_handle,
+ par_dev->fwnode);
+ ret = true;
+ }
+
+ if (!sup_dev)
+ goto out;
+
+ list_for_each_entry(dev_link, &sup_dev->links.suppliers, c_node) {
+ /*
+ * Ignore a SYNC_STATE_ONLY flag only if it wasn't marked as
+ * such due to a cycle.
+ */
+ if (device_link_flag_is_sync_state_only(dev_link->flags) &&
+ !device_link_test(dev_link, DL_FLAG_CYCLE))
+ continue;
+
+ if (__fw_devlink_relax_cycles(con_handle,
+ dev_link->supplier->fwnode)) {
+ pr_debug("%pfwf: cycle: depends on %pfwf\n", sup_handle,
+ dev_link->supplier->fwnode);
+ fw_devlink_relax_link(dev_link);
+ dev_link->flags |= DL_FLAG_CYCLE;
+ ret = true;
+ }
}
+
+out:
+ sup_handle->flags &= ~FWNODE_FLAG_VISITED;
+ put_device(sup_dev);
+ put_device(con_dev);
+ put_device(par_dev);
return ret;
}
@@ -1813,7 +2085,7 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup)
* fw_devlink_create_devlink - Create a device link from a consumer to fwnode
* @con: consumer device for the device link
* @sup_handle: fwnode handle of supplier
- * @flags: devlink flags
+ * @link: fwnode link that's being converted to a device link
*
* This function will try to create a device link between the consumer device
* @con and the supplier device represented by @sup_handle.
@@ -1830,10 +2102,15 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup)
* possible to do that in the future
*/
static int fw_devlink_create_devlink(struct device *con,
- struct fwnode_handle *sup_handle, u32 flags)
+ struct fwnode_handle *sup_handle,
+ struct fwnode_link *link)
{
struct device *sup_dev;
int ret = 0;
+ u32 flags;
+
+ if (link->flags & FWLINK_FLAG_IGNORE)
+ return 0;
/*
* In some cases, a device P might also be a supplier to its child node
@@ -1854,7 +2131,30 @@ static int fw_devlink_create_devlink(struct device *con,
fwnode_is_ancestor_of(sup_handle, con->fwnode))
return -EINVAL;
- sup_dev = get_dev_from_fwnode(sup_handle);
+ /*
+ * Don't try to optimize by not calling the cycle detection logic under
+ * certain conditions. There's always some corner case that won't get
+ * detected.
+ */
+ device_links_write_lock();
+ if (__fw_devlink_relax_cycles(link->consumer, sup_handle)) {
+ __fwnode_link_cycle(link);
+ pr_debug("----- cycle: end -----\n");
+ pr_info("%pfwf: Fixed dependency cycle(s) with %pfwf\n",
+ link->consumer, sup_handle);
+ }
+ device_links_write_unlock();
+
+ if (con->fwnode == link->consumer)
+ flags = fw_devlink_get_flags(link->flags);
+ else
+ flags = FW_DEVLINK_FLAGS_PERMISSIVE;
+
+ if (sup_handle->flags & FWNODE_FLAG_NOT_DEVICE)
+ sup_dev = fwnode_get_next_parent_dev(sup_handle);
+ else
+ sup_dev = get_dev_from_fwnode(sup_handle);
+
if (sup_dev) {
/*
* If it's one of those drivers that don't actually bind to
@@ -1863,71 +2163,34 @@ static int fw_devlink_create_devlink(struct device *con,
*/
if (sup_dev->links.status == DL_DEV_NO_DRIVER &&
sup_handle->flags & FWNODE_FLAG_INITIALIZED) {
+ dev_dbg(con,
+ "Not linking %pfwf - dev might never probe\n",
+ sup_handle);
ret = -EINVAL;
goto out;
}
- /*
- * If this fails, it is due to cycles in device links. Just
- * give up on this link and treat it as invalid.
- */
- if (!device_link_add(con, sup_dev, flags) &&
- !(flags & DL_FLAG_SYNC_STATE_ONLY)) {
- dev_info(con, "Fixing up cyclic dependency with %s\n",
- dev_name(sup_dev));
- device_links_write_lock();
- fw_devlink_relax_cycle(con, sup_dev);
- device_links_write_unlock();
- device_link_add(con, sup_dev,
- FW_DEVLINK_FLAGS_PERMISSIVE);
+ if (con != sup_dev && !device_link_add(con, sup_dev, flags)) {
+ dev_err(con, "Failed to create device link (0x%x) with supplier %s for %pfwf\n",
+ flags, dev_name(sup_dev), link->consumer);
ret = -EINVAL;
}
goto out;
}
- /* Supplier that's already initialized without a struct device. */
- if (sup_handle->flags & FWNODE_FLAG_INITIALIZED)
- return -EINVAL;
-
- /*
- * DL_FLAG_SYNC_STATE_ONLY doesn't block probing and supports
- * cycles. So cycle detection isn't necessary and shouldn't be
- * done.
- */
- if (flags & DL_FLAG_SYNC_STATE_ONLY)
- return -EAGAIN;
-
/*
- * If we can't find the supplier device from its fwnode, it might be
- * due to a cyclic dependency between fwnodes. Some of these cycles can
- * be broken by applying logic. Check for these types of cycles and
- * break them so that devices in the cycle probe properly.
- *
- * If the supplier's parent is dependent on the consumer, then the
- * consumer and supplier have a cyclic dependency. Since fw_devlink
- * can't tell which of the inferred dependencies are incorrect, don't
- * enforce probe ordering between any of the devices in this cyclic
- * dependency. Do this by relaxing all the fw_devlink device links in
- * this cycle and by treating the fwnode link between the consumer and
- * the supplier as an invalid dependency.
+ * Supplier or supplier's ancestor already initialized without a struct
+ * device or being probed by a driver.
*/
- sup_dev = fwnode_get_next_parent_dev(sup_handle);
- if (sup_dev && device_is_dependent(con, sup_dev)) {
- dev_info(con, "Fixing up cyclic dependency with %pfwP (%s)\n",
- sup_handle, dev_name(sup_dev));
- device_links_write_lock();
- fw_devlink_relax_cycle(con, sup_dev);
- device_links_write_unlock();
- ret = -EINVAL;
- } else {
- /*
- * Can't check for cycles or no cycles. So let's try
- * again later.
- */
- ret = -EAGAIN;
+ if (fwnode_init_without_drv(sup_handle) ||
+ fwnode_ancestor_init_without_drv(sup_handle)) {
+ dev_dbg(con, "Not linking %pfwf - might never become dev\n",
+ sup_handle);
+ return -EINVAL;
}
+ ret = -EAGAIN;
out:
put_device(sup_dev);
return ret;
@@ -1955,7 +2218,6 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
struct fwnode_link *link, *tmp;
list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) {
- u32 dl_flags = fw_devlink_get_flags();
struct device *con_dev;
bool own_link = true;
int ret;
@@ -1985,14 +2247,13 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
con_dev = NULL;
} else {
own_link = false;
- dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
}
}
if (!con_dev)
continue;
- ret = fw_devlink_create_devlink(con_dev, fwnode, dl_flags);
+ ret = fw_devlink_create_devlink(con_dev, fwnode, link);
put_device(con_dev);
if (!own_link || ret == -EAGAIN)
continue;
@@ -2012,10 +2273,7 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
*
* The function creates normal (non-SYNC_STATE_ONLY) device links between @dev
* and the real suppliers of @dev. Once these device links are created, the
- * fwnode links are deleted. When such device links are successfully created,
- * this function is called recursively on those supplier devices. This is
- * needed to detect and break some invalid cycles in fwnode links. See
- * fw_devlink_create_devlink() for more details.
+ * fwnode links are deleted.
*
* In addition, it also looks at all the suppliers of the entire fwnode tree
* because some of the child devices of @dev that have not been added yet
@@ -2033,44 +2291,16 @@ static void __fw_devlink_link_to_suppliers(struct device *dev,
bool own_link = (dev->fwnode == fwnode);
struct fwnode_link *link, *tmp;
struct fwnode_handle *child = NULL;
- u32 dl_flags;
-
- if (own_link)
- dl_flags = fw_devlink_get_flags();
- else
- dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) {
int ret;
- struct device *sup_dev;
struct fwnode_handle *sup = link->supplier;
- ret = fw_devlink_create_devlink(dev, sup, dl_flags);
+ ret = fw_devlink_create_devlink(dev, sup, link);
if (!own_link || ret == -EAGAIN)
continue;
__fwnode_link_del(link);
-
- /* If no device link was created, nothing more to do. */
- if (ret)
- continue;
-
- /*
- * If a device link was successfully created to a supplier, we
- * now need to try and link the supplier to all its suppliers.
- *
- * This is needed to detect and delete false dependencies in
- * fwnode links that haven't been converted to a device link
- * yet. See comments in fw_devlink_create_devlink() for more
- * details on the false dependency.
- *
- * Without deleting these false dependencies, some devices will
- * never probe because they'll keep waiting for their false
- * dependency fwnode links to be converted to device links.
- */
- sup_dev = get_dev_from_fwnode(sup);
- __fw_devlink_link_to_suppliers(sup_dev, sup_dev->fwnode);
- put_device(sup_dev);
}
/*
@@ -2092,19 +2322,21 @@ static void fw_devlink_link_device(struct device *dev)
fw_devlink_parse_fwtree(fwnode);
- mutex_lock(&fwnode_link_lock);
+ guard(mutex)(&fwnode_link_lock);
+
__fw_devlink_link_to_consumers(dev);
__fw_devlink_link_to_suppliers(dev, fwnode);
- mutex_unlock(&fwnode_link_lock);
}
/* Device links support end. */
-int (*platform_notify)(struct device *dev) = NULL;
-int (*platform_notify_remove)(struct device *dev) = NULL;
static struct kobject *dev_kobj;
-struct kobject *sysfs_dev_char_kobj;
-struct kobject *sysfs_dev_block_kobj;
+
+/* /sys/dev/char */
+static struct kobject *sysfs_dev_char_kobj;
+
+/* /sys/dev/block */
+static struct kobject *sysfs_dev_block_kobj;
static DEFINE_MUTEX(device_hotplug_lock);
@@ -2145,19 +2377,13 @@ static void device_platform_notify(struct device *dev)
acpi_device_notify(dev);
software_node_notify(dev);
-
- if (platform_notify)
- platform_notify(dev);
}
static void device_platform_notify_remove(struct device *dev)
{
- acpi_device_notify_remove(dev);
-
software_node_notify_remove(dev);
- if (platform_notify_remove)
- platform_notify_remove(dev);
+ acpi_device_notify_remove(dev);
}
/**
@@ -2280,7 +2506,7 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
{
struct dev_ext_attribute *ea = to_ext_attr(attr);
- if (strtobool(buf, ea->var) < 0)
+ if (kstrtobool(buf, ea->var) < 0)
return -EINVAL;
return size;
@@ -2296,6 +2522,15 @@ ssize_t device_show_bool(struct device *dev, struct device_attribute *attr,
}
EXPORT_SYMBOL_GPL(device_show_bool);
+ssize_t device_show_string(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *ea = to_ext_attr(attr);
+
+ return sysfs_emit(buf, "%s\n", (char *)ea->var);
+}
+EXPORT_SYMBOL_GPL(device_show_string);
+
/**
* device_release - free device structure.
* @kobj: device's kobject.
@@ -2334,26 +2569,26 @@ static void device_release(struct kobject *kobj)
kfree(p);
}
-static const void *device_namespace(struct kobject *kobj)
+static const void *device_namespace(const struct kobject *kobj)
{
- struct device *dev = kobj_to_dev(kobj);
+ const struct device *dev = kobj_to_dev(kobj);
const void *ns = NULL;
- if (dev->class && dev->class->ns_type)
+ if (dev->class && dev->class->namespace)
ns = dev->class->namespace(dev);
return ns;
}
-static void device_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
+static void device_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid)
{
- struct device *dev = kobj_to_dev(kobj);
+ const struct device *dev = kobj_to_dev(kobj);
if (dev->class && dev->class->get_ownership)
dev->class->get_ownership(dev, uid, gid);
}
-static struct kobj_type device_ktype = {
+static const struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.namespace = device_namespace,
@@ -2361,12 +2596,12 @@ static struct kobj_type device_ktype = {
};
-static int dev_uevent_filter(struct kobject *kobj)
+static int dev_uevent_filter(const struct kobject *kobj)
{
const struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &device_ktype) {
- struct device *dev = kobj_to_dev(kobj);
+ const struct device *dev = kobj_to_dev(kobj);
if (dev->bus)
return 1;
if (dev->class)
@@ -2375,9 +2610,9 @@ static int dev_uevent_filter(struct kobject *kobj)
return 0;
}
-static const char *dev_uevent_name(struct kobject *kobj)
+static const char *dev_uevent_name(const struct kobject *kobj)
{
- struct device *dev = kobj_to_dev(kobj);
+ const struct device *dev = kobj_to_dev(kobj);
if (dev->bus)
return dev->bus->name;
@@ -2386,9 +2621,38 @@ static const char *dev_uevent_name(struct kobject *kobj)
return NULL;
}
-static int dev_uevent(struct kobject *kobj, struct kobj_uevent_env *env)
+/*
+ * Try filling "DRIVER=<name>" uevent variable for a device. Because this
+ * function may race with binding and unbinding the device from a driver,
+ * we need to be careful. Binding is generally safe, at worst we miss the
+ * fact that the device is already bound to a driver (but the driver
+ * information that is delivered through uevents is best-effort, it may
+ * become obsolete as soon as it is generated anyways). Unbinding is more
+ * risky as driver pointer is transitioning to NULL, so READ_ONCE() should
+ * be used to make sure we are dealing with the same pointer, and to
+ * ensure that driver structure is not going to disappear from under us
+ * we take bus' drivers klist lock. The assumption that only registered
+ * driver can be bound to a device, and to unregister a driver bus code
+ * will take the same lock.
+ */
+static void dev_driver_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+ struct subsys_private *sp = bus_to_subsys(dev->bus);
+
+ if (sp) {
+ scoped_guard(spinlock, &sp->klist_drivers.k_lock) {
+ struct device_driver *drv = READ_ONCE(dev->driver);
+ if (drv)
+ add_uevent_var(env, "DRIVER=%s", drv->name);
+ }
+
+ subsys_put(sp);
+ }
+}
+
+static int dev_uevent(const struct kobject *kobj, struct kobj_uevent_env *env)
{
- struct device *dev = kobj_to_dev(kobj);
+ const struct device *dev = kobj_to_dev(kobj);
int retval = 0;
/* add device node properties if present */
@@ -2417,8 +2681,8 @@ static int dev_uevent(struct kobject *kobj, struct kobj_uevent_env *env)
if (dev->type && dev->type->name)
add_uevent_var(env, "DEVTYPE=%s", dev->type->name);
- if (dev->driver)
- add_uevent_var(env, "DRIVER=%s", dev->driver->name);
+ /* Add "DRIVER=%s" variable if the device is bound to a driver */
+ dev_driver_uevent(dev, env);
/* Add common DT information about the device */
of_device_uevent(dev, env);
@@ -2509,7 +2773,7 @@ static ssize_t uevent_store(struct device *dev, struct device_attribute *attr,
rc = kobject_synth_uevent(&dev->kobj, buf, count);
if (rc) {
- dev_err(dev, "uevent: failed to send synthetic uevent\n");
+ dev_err(dev, "uevent: failed to send synthetic uevent: %d\n", rc);
return rc;
}
@@ -2534,7 +2798,7 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr,
bool val;
int ret;
- ret = strtobool(buf, &val);
+ ret = kstrtobool(buf, &val);
if (ret < 0)
return ret;
@@ -2585,11 +2849,6 @@ union device_attr_group_devres {
const struct attribute_group **groups;
};
-static int devm_attr_group_match(struct device *dev, void *res, void *data)
-{
- return ((union device_attr_group_devres *)res)->group == data;
-}
-
static void devm_attr_group_remove(struct device *dev, void *res)
{
union device_attr_group_devres *devres = res;
@@ -2599,15 +2858,6 @@ static void devm_attr_group_remove(struct device *dev, void *res)
sysfs_remove_group(&dev->kobj, group);
}
-static void devm_attr_groups_remove(struct device *dev, void *res)
-{
- union device_attr_group_devres *devres = res;
- const struct attribute_group **groups = devres->groups;
-
- dev_dbg(dev, "%s: removing groups %p\n", __func__, groups);
- sysfs_remove_groups(&dev->kobj, groups);
-}
-
/**
* devm_device_add_group - given a device, create a managed attribute group
* @dev: The device to create the group for
@@ -2640,79 +2890,9 @@ int devm_device_add_group(struct device *dev, const struct attribute_group *grp)
}
EXPORT_SYMBOL_GPL(devm_device_add_group);
-/**
- * devm_device_remove_group: remove a managed group from a device
- * @dev: device to remove the group from
- * @grp: group to remove
- *
- * This function removes a group of attributes from a device. The attributes
- * previously have to have been created for this group, otherwise it will fail.
- */
-void devm_device_remove_group(struct device *dev,
- const struct attribute_group *grp)
-{
- WARN_ON(devres_release(dev, devm_attr_group_remove,
- devm_attr_group_match,
- /* cast away const */ (void *)grp));
-}
-EXPORT_SYMBOL_GPL(devm_device_remove_group);
-
-/**
- * devm_device_add_groups - create a bunch of managed attribute groups
- * @dev: The device to create the group for
- * @groups: The attribute groups to create, NULL terminated
- *
- * This function creates a bunch of managed attribute groups. If an error
- * occurs when creating a group, all previously created groups will be
- * removed, unwinding everything back to the original state when this
- * function was called. It will explicitly warn and error if any of the
- * attribute files being created already exist.
- *
- * Returns 0 on success or error code from sysfs_create_group on failure.
- */
-int devm_device_add_groups(struct device *dev,
- const struct attribute_group **groups)
-{
- union device_attr_group_devres *devres;
- int error;
-
- devres = devres_alloc(devm_attr_groups_remove,
- sizeof(*devres), GFP_KERNEL);
- if (!devres)
- return -ENOMEM;
-
- error = sysfs_create_groups(&dev->kobj, groups);
- if (error) {
- devres_free(devres);
- return error;
- }
-
- devres->groups = groups;
- devres_add(dev, devres);
- return 0;
-}
-EXPORT_SYMBOL_GPL(devm_device_add_groups);
-
-/**
- * devm_device_remove_groups - remove a list of managed groups
- *
- * @dev: The device for the groups to be removed from
- * @groups: NULL terminated list of groups to be removed
- *
- * If groups is not NULL, remove the specified groups from the device.
- */
-void devm_device_remove_groups(struct device *dev,
- const struct attribute_group **groups)
-{
- WARN_ON(devres_release(dev, devm_attr_groups_remove,
- devm_attr_group_match,
- /* cast away const */ (void *)groups));
-}
-EXPORT_SYMBOL_GPL(devm_device_remove_groups);
-
static int device_add_attrs(struct device *dev)
{
- struct class *class = dev->class;
+ const struct class *class = dev->class;
const struct device_type *type = dev->type;
int error;
@@ -2779,7 +2959,7 @@ static int device_add_attrs(struct device *dev)
static void device_remove_attrs(struct device *dev)
{
- struct class *class = dev->class;
+ const struct class *class = dev->class;
const struct device_type *type = dev->type;
if (dev->physical_location) {
@@ -2993,13 +3173,11 @@ void device_initialize(struct device *dev)
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
dev->dma_coherent = dma_default_coherent;
#endif
-#ifdef CONFIG_SWIOTLB
- dev->dma_io_tlb_mem = &io_tlb_default_mem;
-#endif
+ swiotlb_dev_init(dev);
}
EXPORT_SYMBOL_GPL(device_initialize);
-struct kobject *virtual_device_parent(struct device *dev)
+struct kobject *virtual_device_parent(void)
{
static struct kobject *virtual_dir = NULL;
@@ -3012,7 +3190,7 @@ struct kobject *virtual_device_parent(struct device *dev)
struct class_dir {
struct kobject kobj;
- struct class *class;
+ const struct class *class;
};
#define to_class_dir(obj) container_of(obj, struct class_dir, kobj)
@@ -3024,20 +3202,20 @@ static void class_dir_release(struct kobject *kobj)
}
static const
-struct kobj_ns_type_operations *class_dir_child_ns_type(struct kobject *kobj)
+struct kobj_ns_type_operations *class_dir_child_ns_type(const struct kobject *kobj)
{
- struct class_dir *dir = to_class_dir(kobj);
+ const struct class_dir *dir = to_class_dir(kobj);
return dir->class->ns_type;
}
-static struct kobj_type class_dir_ktype = {
+static const struct kobj_type class_dir_ktype = {
.release = class_dir_release,
.sysfs_ops = &kobj_sysfs_ops,
.child_ns_type = class_dir_child_ns_type
};
-static struct kobject *
-class_dir_create_and_add(struct class *class, struct kobject *parent_kobj)
+static struct kobject *class_dir_create_and_add(struct subsys_private *sp,
+ struct kobject *parent_kobj)
{
struct class_dir *dir;
int retval;
@@ -3046,12 +3224,12 @@ class_dir_create_and_add(struct class *class, struct kobject *parent_kobj)
if (!dir)
return ERR_PTR(-ENOMEM);
- dir->class = class;
+ dir->class = sp->class;
kobject_init(&dir->kobj, &class_dir_ktype);
- dir->kobj.kset = &class->p->glue_dirs;
+ dir->kobj.kset = &sp->glue_dirs;
- retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name);
+ retval = kobject_add(&dir->kobj, parent_kobj, "%s", sp->class->name);
if (retval < 0) {
kobject_put(&dir->kobj);
return ERR_PTR(retval);
@@ -3064,57 +3242,61 @@ static DEFINE_MUTEX(gdp_mutex);
static struct kobject *get_device_parent(struct device *dev,
struct device *parent)
{
- if (dev->class) {
- struct kobject *kobj = NULL;
+ struct subsys_private *sp = class_to_subsys(dev->class);
+ struct kobject *kobj = NULL;
+
+ if (sp) {
struct kobject *parent_kobj;
struct kobject *k;
-#ifdef CONFIG_BLOCK
- /* block disks show up in /sys/block */
- if (sysfs_deprecated && dev->class == &block_class) {
- if (parent && parent->class == &block_class)
- return &parent->kobj;
- return &block_class.p->subsys.kobj;
- }
-#endif
-
/*
* If we have no parent, we live in "virtual".
* Class-devices with a non class-device as parent, live
* in a "glue" directory to prevent namespace collisions.
*/
if (parent == NULL)
- parent_kobj = virtual_device_parent(dev);
- else if (parent->class && !dev->class->ns_type)
+ parent_kobj = virtual_device_parent();
+ else if (parent->class && !dev->class->ns_type) {
+ subsys_put(sp);
return &parent->kobj;
- else
+ } else {
parent_kobj = &parent->kobj;
+ }
mutex_lock(&gdp_mutex);
/* find our class-directory at the parent and reference it */
- spin_lock(&dev->class->p->glue_dirs.list_lock);
- list_for_each_entry(k, &dev->class->p->glue_dirs.list, entry)
+ spin_lock(&sp->glue_dirs.list_lock);
+ list_for_each_entry(k, &sp->glue_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
- spin_unlock(&dev->class->p->glue_dirs.list_lock);
+ spin_unlock(&sp->glue_dirs.list_lock);
if (kobj) {
mutex_unlock(&gdp_mutex);
+ subsys_put(sp);
return kobj;
}
/* or create a new class-directory at the parent device */
- k = class_dir_create_and_add(dev->class, parent_kobj);
+ k = class_dir_create_and_add(sp, parent_kobj);
/* do not emit an uevent for this simple "glue" directory */
mutex_unlock(&gdp_mutex);
+ subsys_put(sp);
return k;
}
/* subsystems can specify a default root directory for their devices */
- if (!parent && dev->bus && dev->bus->dev_root)
- return &dev->bus->dev_root->kobj;
+ if (!parent && dev->bus) {
+ struct device *dev_root = bus_get_dev_root(dev->bus);
+
+ if (dev_root) {
+ kobj = &dev_root->kobj;
+ put_device(dev_root);
+ return kobj;
+ }
+ }
if (parent)
return &parent->kobj;
@@ -3124,10 +3306,23 @@ static struct kobject *get_device_parent(struct device *dev,
static inline bool live_in_glue_dir(struct kobject *kobj,
struct device *dev)
{
- if (!kobj || !dev->class ||
- kobj->kset != &dev->class->p->glue_dirs)
+ struct subsys_private *sp;
+ bool retval;
+
+ if (!kobj || !dev->class)
return false;
- return true;
+
+ sp = class_to_subsys(dev->class);
+ if (!sp)
+ return false;
+
+ if (kobj->kset == &sp->glue_dirs)
+ retval = true;
+ else
+ retval = false;
+
+ subsys_put(sp);
+ return retval;
}
static inline struct kobject *get_glue_dir(struct device *dev)
@@ -3224,6 +3419,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
static int device_add_class_symlinks(struct device *dev)
{
struct device_node *of_node = dev_of_node(dev);
+ struct subsys_private *sp;
int error;
if (of_node) {
@@ -3233,12 +3429,11 @@ static int device_add_class_symlinks(struct device *dev)
/* An error here doesn't warrant bringing down the device */
}
- if (!dev->class)
+ sp = class_to_subsys(dev->class);
+ if (!sp)
return 0;
- error = sysfs_create_link(&dev->kobj,
- &dev->class->p->subsys.kobj,
- "subsystem");
+ error = sysfs_create_link(&dev->kobj, &sp->subsys.kobj, "subsystem");
if (error)
goto out_devnode;
@@ -3249,46 +3444,38 @@ static int device_add_class_symlinks(struct device *dev)
goto out_subsys;
}
-#ifdef CONFIG_BLOCK
- /* /sys/block has directories and does not need symlinks */
- if (sysfs_deprecated && dev->class == &block_class)
- return 0;
-#endif
-
/* link in the class directory pointing to the device */
- error = sysfs_create_link(&dev->class->p->subsys.kobj,
- &dev->kobj, dev_name(dev));
+ error = sysfs_create_link(&sp->subsys.kobj, &dev->kobj, dev_name(dev));
if (error)
goto out_device;
-
- return 0;
+ goto exit;
out_device:
sysfs_remove_link(&dev->kobj, "device");
-
out_subsys:
sysfs_remove_link(&dev->kobj, "subsystem");
out_devnode:
sysfs_remove_link(&dev->kobj, "of_node");
+exit:
+ subsys_put(sp);
return error;
}
static void device_remove_class_symlinks(struct device *dev)
{
+ struct subsys_private *sp = class_to_subsys(dev->class);
+
if (dev_of_node(dev))
sysfs_remove_link(&dev->kobj, "of_node");
- if (!dev->class)
+ if (!sp)
return;
if (dev->parent && device_is_not_partition(dev))
sysfs_remove_link(&dev->kobj, "device");
sysfs_remove_link(&dev->kobj, "subsystem");
-#ifdef CONFIG_BLOCK
- if (sysfs_deprecated && dev->class == &block_class)
- return;
-#endif
- sysfs_delete_link(&dev->class->p->subsys.kobj, &dev->kobj, dev_name(dev));
+ sysfs_delete_link(&sp->subsys.kobj, &dev->kobj, dev_name(dev));
+ subsys_put(sp);
}
/**
@@ -3308,27 +3495,13 @@ int dev_set_name(struct device *dev, const char *fmt, ...)
}
EXPORT_SYMBOL_GPL(dev_set_name);
-/**
- * device_to_dev_kobj - select a /sys/dev/ directory for the device
- * @dev: device
- *
- * By default we select char/ for new entries. Setting class->dev_obj
- * to NULL prevents an entry from being created. class->dev_kobj must
- * be set (or cleared) before any devices are registered to the class
- * otherwise device_create_sys_dev_entry() and
- * device_remove_sys_dev_entry() will disagree about the presence of
- * the link.
- */
+/* select a /sys/dev/ directory for the device */
static struct kobject *device_to_dev_kobj(struct device *dev)
{
- struct kobject *kobj;
-
- if (dev->class)
- kobj = dev->class->dev_kobj;
+ if (is_blockdev(dev))
+ return sysfs_dev_block_kobj;
else
- kobj = sysfs_dev_char_kobj;
-
- return kobj;
+ return sysfs_dev_char_kobj;
}
static int device_create_sys_dev_entry(struct device *dev)
@@ -3397,6 +3570,7 @@ static int device_private_init(struct device *dev)
*/
int device_add(struct device *dev)
{
+ struct subsys_private *sp;
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
@@ -3419,18 +3593,19 @@ int device_add(struct device *dev)
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
- dev_set_name(dev, "%s", dev->init_name);
+ error = dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
+ if (dev_name(dev))
+ error = 0;
/* subsystems can specify simple device enumeration */
- if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
- dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
-
- if (!dev_name(dev)) {
+ else if (dev->bus && dev->bus->dev_name)
+ error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
+ else
error = -EINVAL;
+ if (error)
goto name_error;
- }
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
@@ -3451,7 +3626,7 @@ int device_add(struct device *dev)
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error) {
- glue_dir = get_glue_dir(dev);
+ glue_dir = kobj;
goto Error;
}
@@ -3491,10 +3666,7 @@ int device_add(struct device *dev)
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_ADD_DEVICE, dev);
-
+ bus_notify(dev, BUS_NOTIFY_ADD_DEVICE);
kobject_uevent(&dev->kobj, KOBJ_ADD);
/*
@@ -3528,18 +3700,18 @@ int device_add(struct device *dev)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
- if (dev->class) {
- mutex_lock(&dev->class->p->mutex);
+ sp = class_to_subsys(dev->class);
+ if (sp) {
+ mutex_lock(&sp->mutex);
/* tie the class to the device */
- klist_add_tail(&dev->p->knode_class,
- &dev->class->p->klist_devices);
+ klist_add_tail(&dev->p->knode_class, &sp->klist_devices);
/* notify any interfaces that the device is here */
- list_for_each_entry(class_intf,
- &dev->class->p->interfaces, node)
+ list_for_each_entry(class_intf, &sp->interfaces, node)
if (class_intf->add_dev)
- class_intf->add_dev(dev, class_intf);
- mutex_unlock(&dev->class->p->mutex);
+ class_intf->add_dev(dev);
+ mutex_unlock(&sp->mutex);
+ subsys_put(sp);
}
done:
put_device(dev);
@@ -3551,6 +3723,7 @@ done:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
DPMError:
+ device_set_driver(dev, NULL);
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
@@ -3658,6 +3831,7 @@ EXPORT_SYMBOL_GPL(kill_device);
*/
void device_del(struct device *dev)
{
+ struct subsys_private *sp;
struct device *parent = dev->parent;
struct kobject *glue_dir = NULL;
struct class_interface *class_intf;
@@ -3674,9 +3848,7 @@ void device_del(struct device *dev)
* before dpm_sysfs_remove().
*/
noio_flag = memalloc_noio_save();
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_DEL_DEVICE, dev);
+ bus_notify(dev, BUS_NOTIFY_DEL_DEVICE);
dpm_sysfs_remove(dev);
if (parent)
@@ -3686,18 +3858,20 @@ void device_del(struct device *dev)
device_remove_sys_dev_entry(dev);
device_remove_file(dev, &dev_attr_dev);
}
- if (dev->class) {
+
+ sp = class_to_subsys(dev->class);
+ if (sp) {
device_remove_class_symlinks(dev);
- mutex_lock(&dev->class->p->mutex);
+ mutex_lock(&sp->mutex);
/* notify any interfaces that the device is now gone */
- list_for_each_entry(class_intf,
- &dev->class->p->interfaces, node)
+ list_for_each_entry(class_intf, &sp->interfaces, node)
if (class_intf->remove_dev)
- class_intf->remove_dev(dev, class_intf);
+ class_intf->remove_dev(dev);
/* remove the device from the class list */
klist_del(&dev->p->knode_class);
- mutex_unlock(&dev->class->p->mutex);
+ mutex_unlock(&sp->mutex);
+ subsys_put(sp);
}
device_remove_file(dev, &dev_attr_uevent);
device_remove_attrs(dev);
@@ -3707,9 +3881,18 @@ void device_del(struct device *dev)
device_platform_notify_remove(dev);
device_links_purge(dev);
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_REMOVED_DEVICE, dev);
+ /*
+ * If a device does not have a driver attached, we need to clean
+ * up any managed resources. We do this in device_release(), but
+ * it's never called (and we leak the device) if a managed
+ * resource holds a reference to the device. So release all
+ * managed resources here, like we do in driver_detach(). We
+ * still need to do so again in device_release() in case someone
+ * adds a new resource after this point, though.
+ */
+ devres_release_all(dev);
+
+ bus_notify(dev, BUS_NOTIFY_REMOVED_DEVICE);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
@@ -3777,7 +3960,7 @@ static struct device *next_device(struct klist_iter *i)
* a name. This memory is returned in tmp and needs to be
* freed by the caller.
*/
-const char *device_get_devnode(struct device *dev,
+const char *device_get_devnode(const struct device *dev,
umode_t *mode, kuid_t *uid, kgid_t *gid,
const char **tmp)
{
@@ -3802,18 +3985,17 @@ const char *device_get_devnode(struct device *dev,
return dev_name(dev);
/* replace '!' in the name with '/' */
- s = kstrdup(dev_name(dev), GFP_KERNEL);
+ s = kstrdup_and_replace(dev_name(dev), '!', '/', GFP_KERNEL);
if (!s)
return NULL;
- strreplace(s, '!', '/');
return *tmp = s;
}
/**
* device_for_each_child - device child iterator.
* @parent: parent struct device.
- * @fn: function to be called for each device.
* @data: data for the callback.
+ * @fn: function to be called for each device.
*
* Iterate over @parent's child devices, and call @fn for each,
* passing it @data.
@@ -3822,13 +4004,13 @@ const char *device_get_devnode(struct device *dev,
* other than 0, we break out and return that value.
*/
int device_for_each_child(struct device *parent, void *data,
- int (*fn)(struct device *dev, void *data))
+ device_iter_t fn)
{
struct klist_iter i;
struct device *child;
int error = 0;
- if (!parent->p)
+ if (!parent || !parent->p)
return 0;
klist_iter_init(&parent->p->klist_children, &i);
@@ -3842,8 +4024,8 @@ EXPORT_SYMBOL_GPL(device_for_each_child);
/**
* device_for_each_child_reverse - device child iterator in reversed order.
* @parent: parent struct device.
- * @fn: function to be called for each device.
* @data: data for the callback.
+ * @fn: function to be called for each device.
*
* Iterate over @parent's child devices, and call @fn for each,
* passing it @data.
@@ -3852,13 +4034,13 @@ EXPORT_SYMBOL_GPL(device_for_each_child);
* other than 0, we break out and return that value.
*/
int device_for_each_child_reverse(struct device *parent, void *data,
- int (*fn)(struct device *dev, void *data))
+ device_iter_t fn)
{
struct klist_iter i;
struct device *child;
int error = 0;
- if (!parent->p)
+ if (!parent || !parent->p)
return 0;
klist_iter_init(&parent->p->klist_children, &i);
@@ -3870,87 +4052,77 @@ int device_for_each_child_reverse(struct device *parent, void *data,
EXPORT_SYMBOL_GPL(device_for_each_child_reverse);
/**
- * device_find_child - device iterator for locating a particular device.
- * @parent: parent struct device
- * @match: Callback function to check device
- * @data: Data to pass to match function
- *
- * This is similar to the device_for_each_child() function above, but it
- * returns a reference to a device that is 'found' for later use, as
- * determined by the @match callback.
+ * device_for_each_child_reverse_from - device child iterator in reversed order.
+ * @parent: parent struct device.
+ * @from: optional starting point in child list
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
*
- * The callback should return 0 if the device doesn't match and non-zero
- * if it does. If the callback returns non-zero and a reference to the
- * current device can be obtained, this function will return to the caller
- * and not iterate over any more devices.
+ * Iterate over @parent's child devices, starting at @from, and call @fn
+ * for each, passing it @data. This helper is identical to
+ * device_for_each_child_reverse() when @from is NULL.
*
- * NOTE: you will need to drop the reference with put_device() after use.
+ * @fn is checked each iteration. If it returns anything other than 0,
+ * iteration stop and that value is returned to the caller of
+ * device_for_each_child_reverse_from();
*/
-struct device *device_find_child(struct device *parent, void *data,
- int (*match)(struct device *dev, void *data))
+int device_for_each_child_reverse_from(struct device *parent,
+ struct device *from, void *data,
+ device_iter_t fn)
{
struct klist_iter i;
struct device *child;
+ int error = 0;
- if (!parent)
- return NULL;
+ if (!parent || !parent->p)
+ return 0;
- klist_iter_init(&parent->p->klist_children, &i);
- while ((child = next_device(&i)))
- if (match(child, data) && get_device(child))
- break;
+ klist_iter_init_node(&parent->p->klist_children, &i,
+ (from ? &from->p->knode_parent : NULL));
+ while ((child = prev_device(&i)) && !error)
+ error = fn(child, data);
klist_iter_exit(&i);
- return child;
+ return error;
}
-EXPORT_SYMBOL_GPL(device_find_child);
+EXPORT_SYMBOL_GPL(device_for_each_child_reverse_from);
/**
- * device_find_child_by_name - device iterator for locating a child device.
+ * device_find_child - device iterator for locating a particular device.
* @parent: parent struct device
- * @name: name of the child device
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
*
- * This is similar to the device_find_child() function above, but it
- * returns a reference to a device that has the name @name.
+ * This is similar to the device_for_each_child() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero and a reference to the
+ * current device can be obtained, this function will return to the caller
+ * and not iterate over any more devices.
*
* NOTE: you will need to drop the reference with put_device() after use.
*/
-struct device *device_find_child_by_name(struct device *parent,
- const char *name)
+struct device *device_find_child(struct device *parent, const void *data,
+ device_match_t match)
{
struct klist_iter i;
struct device *child;
- if (!parent)
+ if (!parent || !parent->p)
return NULL;
klist_iter_init(&parent->p->klist_children, &i);
- while ((child = next_device(&i)))
- if (sysfs_streq(dev_name(child), name) && get_device(child))
+ while ((child = next_device(&i))) {
+ if (match(child, data)) {
+ get_device(child);
break;
+ }
+ }
klist_iter_exit(&i);
return child;
}
-EXPORT_SYMBOL_GPL(device_find_child_by_name);
-
-static int match_any(struct device *dev, void *unused)
-{
- return 1;
-}
-
-/**
- * device_find_any_child - device iterator for locating a child device, if any.
- * @parent: parent struct device
- *
- * This is similar to the device_find_child() function above, but it
- * returns a reference to a child device, if any.
- *
- * NOTE: you will need to drop the reference with put_device() after use.
- */
-struct device *device_find_any_child(struct device *parent)
-{
- return device_find_child(parent, NULL, match_any);
-}
-EXPORT_SYMBOL_GPL(device_find_any_child);
+EXPORT_SYMBOL_GPL(device_find_child);
int __init devices_init(void)
{
@@ -3966,9 +4138,14 @@ int __init devices_init(void)
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
if (!sysfs_dev_char_kobj)
goto char_kobj_err;
+ device_link_wq = alloc_workqueue("device_link_wq", WQ_PERCPU, 0);
+ if (!device_link_wq)
+ goto wq_err;
return 0;
+ wq_err:
+ kobject_put(sysfs_dev_char_kobj);
char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
@@ -4162,7 +4339,7 @@ static void device_create_release(struct device *dev)
}
static __printf(6, 0) struct device *
-device_create_groups_vargs(struct class *class, struct device *parent,
+device_create_groups_vargs(const struct class *class, struct device *parent,
dev_t devt, void *drvdata,
const struct attribute_group **groups,
const char *fmt, va_list args)
@@ -4170,7 +4347,7 @@ device_create_groups_vargs(struct class *class, struct device *parent,
struct device *dev = NULL;
int retval = -ENODEV;
- if (class == NULL || IS_ERR(class))
+ if (IS_ERR_OR_NULL(class))
goto error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -4222,11 +4399,8 @@ error:
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
- *
- * Note: the struct class passed to this function must have previously
- * been created with a call to class_create().
*/
-struct device *device_create(struct class *class, struct device *parent,
+struct device *device_create(const struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
@@ -4263,11 +4437,8 @@ EXPORT_SYMBOL_GPL(device_create);
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
- *
- * Note: the struct class passed to this function must have previously
- * been created with a call to class_create().
*/
-struct device *device_create_with_groups(struct class *class,
+struct device *device_create_with_groups(const struct class *class,
struct device *parent, dev_t devt,
void *drvdata,
const struct attribute_group **groups,
@@ -4292,7 +4463,7 @@ EXPORT_SYMBOL_GPL(device_create_with_groups);
* This call unregisters and cleans up a device that was created with a
* call to device_create().
*/
-void device_destroy(struct class *class, dev_t devt)
+void device_destroy(const struct class *class, dev_t devt)
{
struct device *dev;
@@ -4314,9 +4485,12 @@ EXPORT_SYMBOL_GPL(device_destroy);
* on the same device to ensure that new_name is valid and
* won't conflict with other devices.
*
- * Note: Don't call this function. Currently, the networking layer calls this
- * function, but that will change. The following text from Kay Sievers offers
- * some insight:
+ * Note: given that some subsystems (networking and infiniband) use this
+ * function, with no immediate plans for this to change, we cannot assume or
+ * require that this function not be called at all.
+ *
+ * However, if you're writing new code, do not call this function. The following
+ * text from Kay Sievers offers some insight:
*
* Renaming devices is racy at many levels, symlinks and other stuff are not
* replaced atomically, and you get a "move" uevent, but it's not easy to
@@ -4330,13 +4504,6 @@ EXPORT_SYMBOL_GPL(device_destroy);
* kernel device renaming. Besides that, it's not even implemented now for
* other things than (driver-core wise very simple) network devices.
*
- * We are currently about to change network renaming in udev to completely
- * disallow renaming of devices in the same namespace as the kernel uses,
- * because we can't solve the problems properly, that arise with swapping names
- * of multiple interfaces without races. Means, renaming of eth[0-9]* will only
- * be allowed to some other name than eth[0-9]*, for the aforementioned
- * reasons.
- *
* Make up a "real" name in the driver before you register anything, or add
* some other attributes for userspace to find the device, or use udev to add
* symlinks -- but never rename kernel devices later, it's a complete mess. We
@@ -4345,9 +4512,11 @@ EXPORT_SYMBOL_GPL(device_destroy);
*/
int device_rename(struct device *dev, const char *new_name)
{
+ struct subsys_private *sp = NULL;
struct kobject *kobj = &dev->kobj;
char *old_device_name = NULL;
int error;
+ bool is_link_renamed = false;
dev = get_device(dev);
if (!dev)
@@ -4362,18 +4531,28 @@ int device_rename(struct device *dev, const char *new_name)
}
if (dev->class) {
- error = sysfs_rename_link_ns(&dev->class->p->subsys.kobj,
- kobj, old_device_name,
+ sp = class_to_subsys(dev->class);
+
+ if (!sp) {
+ error = -EINVAL;
+ goto out;
+ }
+
+ error = sysfs_rename_link_ns(&sp->subsys.kobj, kobj, old_device_name,
new_name, kobject_namespace(kobj));
if (error)
goto out;
+
+ is_link_renamed = true;
}
error = kobject_rename(kobj, new_name);
- if (error)
- goto out;
-
out:
+ if (error && is_link_renamed)
+ sysfs_rename_link_ns(&sp->subsys.kobj, kobj, new_name,
+ old_device_name, kobject_namespace(kobj));
+ subsys_put(sp);
+
put_device(dev);
kfree(old_device_name);
@@ -4489,7 +4668,7 @@ static int device_attrs_change_owner(struct device *dev, kuid_t kuid,
kgid_t kgid)
{
struct kobject *kobj = &dev->kobj;
- struct class *class = dev->class;
+ const struct class *class = dev->class;
const struct device_type *type = dev->type;
int error;
@@ -4547,6 +4726,7 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
{
int error;
struct kobject *kobj = &dev->kobj;
+ struct subsys_private *sp;
dev = get_device(dev);
if (!dev)
@@ -4583,21 +4763,19 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
if (error)
goto out;
-#ifdef CONFIG_BLOCK
- if (sysfs_deprecated && dev->class == &block_class)
- goto out;
-#endif
-
/*
* Change the owner of the symlink located in the class directory of
* the device class associated with @dev which points to the actual
* directory entry for @dev to @kuid/@kgid. This ensures that the
* symlink shows the same permissions as its target.
*/
- error = sysfs_link_change_owner(&dev->class->p->subsys.kobj, &dev->kobj,
- dev_name(dev), kuid, kgid);
- if (error)
+ sp = class_to_subsys(dev->class);
+ if (!sp) {
+ error = -EINVAL;
goto out;
+ }
+ error = sysfs_link_change_owner(&sp->subsys.kobj, &dev->kobj, dev_name(dev), kuid, kgid);
+ subsys_put(sp);
out:
put_device(dev);
@@ -4696,7 +4874,7 @@ set_dev_info(const struct device *dev, struct dev_printk_info *dev_info)
else
return;
- strscpy(dev_info->subsystem, subsys, sizeof(dev_info->subsystem));
+ strscpy(dev_info->subsystem, subsys);
/*
* Add device identifier DEVICE=:
@@ -4806,6 +4984,49 @@ define_dev_printk_level(_dev_info, KERN_INFO);
#endif
+static void __dev_probe_failed(const struct device *dev, int err, bool fatal,
+ const char *fmt, va_list vargsp)
+{
+ struct va_format vaf;
+ va_list vargs;
+
+ /*
+ * On x86_64 and possibly on other architectures, va_list is actually a
+ * size-1 array containing a structure. As a result, function parameter
+ * vargsp decays from T[1] to T*, and &vargsp has type T** rather than
+ * T(*)[1], which is expected by its assignment to vaf.va below.
+ *
+ * One standard way to solve this mess is by creating a copy in a local
+ * variable of type va_list and then using a pointer to that local copy
+ * instead, which is the approach employed here.
+ */
+ va_copy(vargs, vargsp);
+
+ vaf.fmt = fmt;
+ vaf.va = &vargs;
+
+ switch (err) {
+ case -EPROBE_DEFER:
+ device_set_deferred_probe_reason(dev, &vaf);
+ dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
+ break;
+
+ case -ENOMEM:
+ /* Don't print anything on -ENOMEM, there's already enough output */
+ break;
+
+ default:
+ /* Log fatal final failures as errors, otherwise produce warnings */
+ if (fatal)
+ dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
+ else
+ dev_warn(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
+ break;
+ }
+
+ va_end(vargs);
+}
+
/**
* dev_err_probe - probe error check and log helper
* @dev: the pointer to the struct device
@@ -4818,7 +5039,7 @@ define_dev_printk_level(_dev_info, KERN_INFO);
* -EPROBE_DEFER and propagate error upwards.
* In case of -EPROBE_DEFER it sets also defer probe reason, which can be
* checked later by reading devices_deferred debugfs attribute.
- * It replaces code sequence::
+ * It replaces the following code sequence::
*
* if (err != -EPROBE_DEFER)
* dev_err(dev, ...);
@@ -4830,36 +5051,78 @@ define_dev_printk_level(_dev_info, KERN_INFO);
*
* return dev_err_probe(dev, err, ...);
*
- * Note that it is deemed acceptable to use this function for error
- * prints during probe even if the @err is known to never be -EPROBE_DEFER.
+ * Using this helper in your probe function is totally fine even if @err
+ * is known to never be -EPROBE_DEFER.
* The benefit compared to a normal dev_err() is the standardized format
- * of the error code and the fact that the error code is returned.
+ * of the error code, which is emitted symbolically (i.e. you get "EAGAIN"
+ * instead of "-35"), and having the error code returned allows more
+ * compact error paths.
*
* Returns @err.
- *
*/
int dev_err_probe(const struct device *dev, int err, const char *fmt, ...)
{
- struct va_format vaf;
- va_list args;
+ va_list vargs;
- va_start(args, fmt);
- vaf.fmt = fmt;
- vaf.va = &args;
+ va_start(vargs, fmt);
- if (err != -EPROBE_DEFER) {
- dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
- } else {
- device_set_deferred_probe_reason(dev, &vaf);
- dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
- }
+ /* Use dev_err() for logging when err doesn't equal -EPROBE_DEFER */
+ __dev_probe_failed(dev, err, true, fmt, vargs);
- va_end(args);
+ va_end(vargs);
return err;
}
EXPORT_SYMBOL_GPL(dev_err_probe);
+/**
+ * dev_warn_probe - probe error check and log helper
+ * @dev: the pointer to the struct device
+ * @err: error value to test
+ * @fmt: printf-style format string
+ * @...: arguments as specified in the format string
+ *
+ * This helper implements common pattern present in probe functions for error
+ * checking: print debug or warning message depending if the error value is
+ * -EPROBE_DEFER and propagate error upwards.
+ * In case of -EPROBE_DEFER it sets also defer probe reason, which can be
+ * checked later by reading devices_deferred debugfs attribute.
+ * It replaces the following code sequence::
+ *
+ * if (err != -EPROBE_DEFER)
+ * dev_warn(dev, ...);
+ * else
+ * dev_dbg(dev, ...);
+ * return err;
+ *
+ * with::
+ *
+ * return dev_warn_probe(dev, err, ...);
+ *
+ * Using this helper in your probe function is totally fine even if @err
+ * is known to never be -EPROBE_DEFER.
+ * The benefit compared to a normal dev_warn() is the standardized format
+ * of the error code, which is emitted symbolically (i.e. you get "EAGAIN"
+ * instead of "-35"), and having the error code returned allows more
+ * compact error paths.
+ *
+ * Returns @err.
+ */
+int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...)
+{
+ va_list vargs;
+
+ va_start(vargs, fmt);
+
+ /* Use dev_warn() for logging when err doesn't equal -EPROBE_DEFER */
+ __dev_probe_failed(dev, err, false, fmt, vargs);
+
+ va_end(vargs);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(dev_warn_probe);
+
static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
{
return fwnode && !IS_ERR(fwnode->secondary);
@@ -4896,9 +5159,13 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
} else {
if (fwnode_is_primary(fn)) {
dev->fwnode = fn->secondary;
+
+ /* Skip nullifying fn->secondary if the primary is shared */
+ if (parent && fn == parent->fwnode)
+ return;
+
/* Set fn->secondary = NULL, so fn remains the primary fwnode */
- if (!(parent && fn == parent->fwnode))
- fn->secondary = NULL;
+ fn->secondary = NULL;
} else {
dev->fwnode = NULL;
}
@@ -4928,6 +5195,67 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(set_secondary_fwnode);
/**
+ * device_remove_of_node - Remove an of_node from a device
+ * @dev: device whose device tree node is being removed
+ */
+void device_remove_of_node(struct device *dev)
+{
+ dev = get_device(dev);
+ if (!dev)
+ return;
+
+ if (!dev->of_node)
+ goto end;
+
+ if (dev->fwnode == of_fwnode_handle(dev->of_node))
+ dev->fwnode = NULL;
+
+ of_node_put(dev->of_node);
+ dev->of_node = NULL;
+
+end:
+ put_device(dev);
+}
+EXPORT_SYMBOL_GPL(device_remove_of_node);
+
+/**
+ * device_add_of_node - Add an of_node to an existing device
+ * @dev: device whose device tree node is being added
+ * @of_node: of_node to add
+ *
+ * Return: 0 on success or error code on failure.
+ */
+int device_add_of_node(struct device *dev, struct device_node *of_node)
+{
+ int ret;
+
+ if (!of_node)
+ return -EINVAL;
+
+ dev = get_device(dev);
+ if (!dev)
+ return -EINVAL;
+
+ if (dev->of_node) {
+ dev_err(dev, "Cannot replace node %pOF with %pOF\n",
+ dev->of_node, of_node);
+ ret = -EBUSY;
+ goto end;
+ }
+
+ dev->of_node = of_node_get(of_node);
+
+ if (!dev->fwnode)
+ dev->fwnode = of_fwnode_handle(of_node);
+
+ ret = 0;
+end:
+ put_device(dev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(device_add_of_node);
+
+/**
* device_set_of_node_from_dev - reuse device-tree node of another device
* @dev: device whose device-tree node is being set
* @dev2: device whose device-tree node is being reused
@@ -4950,21 +5278,52 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL_GPL(device_set_node);
+/**
+ * get_dev_from_fwnode - Obtain a reference count of the struct device the
+ * struct fwnode_handle is associated with.
+ * @fwnode: The pointer to the struct fwnode_handle to obtain the struct device
+ * reference count of.
+ *
+ * This function obtains a reference count of the device the device pointer
+ * embedded in the struct fwnode_handle points to.
+ *
+ * Note that the struct device pointer embedded in struct fwnode_handle does
+ * *not* have a reference count of the struct device itself.
+ *
+ * Hence, it is a UAF (and thus a bug) to call this function if the caller can't
+ * guarantee that the last reference count of the corresponding struct device is
+ * not dropped concurrently.
+ *
+ * This is possible since struct fwnode_handle has its own reference count and
+ * hence can out-live the struct device it is associated with.
+ */
+struct device *get_dev_from_fwnode(struct fwnode_handle *fwnode)
+{
+ return get_device((fwnode)->dev);
+}
+EXPORT_SYMBOL_GPL(get_dev_from_fwnode);
+
int device_match_name(struct device *dev, const void *name)
{
return sysfs_streq(dev_name(dev), name);
}
EXPORT_SYMBOL_GPL(device_match_name);
+int device_match_type(struct device *dev, const void *type)
+{
+ return dev->type == type;
+}
+EXPORT_SYMBOL_GPL(device_match_type);
+
int device_match_of_node(struct device *dev, const void *np)
{
- return dev->of_node == np;
+ return np && dev->of_node == np;
}
EXPORT_SYMBOL_GPL(device_match_of_node);
int device_match_fwnode(struct device *dev, const void *fwnode)
{
- return dev_fwnode(dev) == fwnode;
+ return fwnode && dev_fwnode(dev) == fwnode;
}
EXPORT_SYMBOL_GPL(device_match_fwnode);
@@ -4976,13 +5335,13 @@ EXPORT_SYMBOL_GPL(device_match_devt);
int device_match_acpi_dev(struct device *dev, const void *adev)
{
- return ACPI_COMPANION(dev) == adev;
+ return adev && ACPI_COMPANION(dev) == adev;
}
EXPORT_SYMBOL(device_match_acpi_dev);
int device_match_acpi_handle(struct device *dev, const void *handle)
{
- return ACPI_HANDLE(dev) == handle;
+ return handle && ACPI_HANDLE(dev) == handle;
}
EXPORT_SYMBOL(device_match_acpi_handle);
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 4c98849577d4..c6c57b6f61c6 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -19,13 +19,14 @@
#include <linux/cpufeature.h>
#include <linux/tick.h>
#include <linux/pm_qos.h>
+#include <linux/delay.h>
#include <linux/sched/isolation.h>
#include "base.h"
static DEFINE_PER_CPU(struct device *, cpu_sys_devices);
-static int cpu_subsys_match(struct device *dev, struct device_driver *drv)
+static int cpu_subsys_match(struct device *dev, const struct device_driver *drv)
{
/* ACPI style match is the only one that may succeed. */
if (acpi_driver_match_device(dev, drv))
@@ -50,12 +51,30 @@ static int cpu_subsys_online(struct device *dev)
int cpuid = dev->id;
int from_nid, to_nid;
int ret;
+ int retries = 0;
from_nid = cpu_to_node(cpuid);
if (from_nid == NUMA_NO_NODE)
return -ENODEV;
+retry:
ret = cpu_device_up(dev);
+
+ /*
+ * If -EBUSY is returned, it is likely that hotplug is temporarily
+ * disabled when cpu_hotplug_disable() was called. This condition is
+ * transient. So we retry after waiting for an exponentially
+ * increasing delay up to a total of at least 620ms as some PCI
+ * device initialization can take quite a while.
+ */
+ if (ret == -EBUSY) {
+ retries++;
+ if (retries > 5)
+ return ret;
+ msleep(10 * (1 << retries));
+ goto retry;
+ }
+
/*
* When hot adding memory to memoryless node and enabling a cpu
* on the node, node number of the cpu may internally change.
@@ -76,6 +95,7 @@ void unregister_cpu(struct cpu *cpu)
{
int logical_cpu = cpu->dev.id;
+ set_cpu_enabled(logical_cpu, false);
unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
device_unregister(&cpu->dev);
@@ -125,18 +145,7 @@ static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store);
#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
#endif /* CONFIG_HOTPLUG_CPU */
-struct bus_type cpu_subsys = {
- .name = "cpu",
- .dev_name = "cpu",
- .match = cpu_subsys_match,
-#ifdef CONFIG_HOTPLUG_CPU
- .online = cpu_subsys_online,
- .offline = cpu_subsys_offline,
-#endif
-};
-EXPORT_SYMBOL_GPL(cpu_subsys);
-
-#ifdef CONFIG_KEXEC
+#ifdef CONFIG_CRASH_DUMP
#include <linux/kexec.h>
static ssize_t crash_notes_show(struct device *dev,
@@ -181,14 +190,14 @@ static const struct attribute_group crash_note_cpu_attr_group = {
#endif
static const struct attribute_group *common_cpu_attr_groups[] = {
-#ifdef CONFIG_KEXEC
+#ifdef CONFIG_CRASH_DUMP
&crash_note_cpu_attr_group,
#endif
NULL
};
static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
-#ifdef CONFIG_KEXEC
+#ifdef CONFIG_CRASH_DUMP
&crash_note_cpu_attr_group,
#endif
NULL
@@ -265,6 +274,13 @@ static ssize_t print_cpus_offline(struct device *dev,
}
static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
+static ssize_t print_cpus_enabled(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_enabled_mask));
+}
+static DEVICE_ATTR(enabled, 0444, print_cpus_enabled, NULL);
+
static ssize_t print_cpus_isolated(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -284,13 +300,40 @@ static ssize_t print_cpus_isolated(struct device *dev,
}
static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL);
+static ssize_t housekeeping_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct cpumask *hk_mask;
+
+ hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE);
+
+ if (housekeeping_enabled(HK_TYPE_KERNEL_NOISE))
+ return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(hk_mask));
+ return sysfs_emit(buf, "\n");
+}
+static DEVICE_ATTR_RO(housekeeping);
+
#ifdef CONFIG_NO_HZ_FULL
-static ssize_t print_cpus_nohz_full(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t nohz_full_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
+ if (cpumask_available(tick_nohz_full_mask))
+ return sysfs_emit(buf, "%*pbl\n",
+ cpumask_pr_args(tick_nohz_full_mask));
+ return sysfs_emit(buf, "\n");
}
-static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
+static DEVICE_ATTR_RO(nohz_full);
+#endif
+
+#ifdef CONFIG_CRASH_HOTPLUG
+static ssize_t crash_hotplug_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%d\n", crash_check_hotplug_support());
+}
+static DEVICE_ATTR_RO(crash_hotplug);
#endif
static void cpu_device_release(struct device *dev)
@@ -299,7 +342,7 @@ static void cpu_device_release(struct device *dev)
* This is an empty function to prevent the driver core from spitting a
* warning at us. Yes, I know this is directly opposite of what the
* documentation for the driver core and kobjects say, and the author
- * of this code has already been publically ridiculed for doing
+ * of this code has already been publicly ridiculed for doing
* something as foolish as this. However, at this point in time, it is
* the only way to handle the issue of statically allocated cpu
* devices. The different architectures will have their cpu device
@@ -336,7 +379,7 @@ static ssize_t print_cpu_modalias(struct device *dev,
return len;
}
-static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (buf) {
@@ -348,6 +391,20 @@ static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
}
#endif
+const struct bus_type cpu_subsys = {
+ .name = "cpu",
+ .dev_name = "cpu",
+ .match = cpu_subsys_match,
+#ifdef CONFIG_HOTPLUG_CPU
+ .online = cpu_subsys_online,
+ .offline = cpu_subsys_offline,
+#endif
+#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
+ .uevent = cpu_uevent,
+#endif
+};
+EXPORT_SYMBOL_GPL(cpu_subsys);
+
/*
* register_cpu - Setup a sysfs device for a CPU.
* @cpu - cpu->hotpluggable field set to 1 will generate a control file in
@@ -368,9 +425,6 @@ int register_cpu(struct cpu *cpu, int num)
cpu->dev.offline_disabled = !cpu->hotpluggable;
cpu->dev.offline = !cpu_online(num);
cpu->dev.of_node = of_get_cpu_node(num, NULL);
-#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
- cpu->dev.bus->uevent = cpu_uevent;
-#endif
cpu->dev.groups = common_cpu_attr_groups;
if (cpu->hotpluggable)
cpu->dev.groups = hotplugable_cpu_attr_groups;
@@ -384,6 +438,7 @@ int register_cpu(struct cpu *cpu, int num)
register_cpu_under_node(num, cpu_to_node(num));
dev_pm_qos_expose_latency_limit(&cpu->dev,
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
+ set_cpu_enabled(num, true);
return 0;
}
@@ -465,10 +520,15 @@ static struct attribute *cpu_root_attrs[] = {
&cpu_attrs[2].attr.attr,
&dev_attr_kernel_max.attr,
&dev_attr_offline.attr,
+ &dev_attr_enabled.attr,
&dev_attr_isolated.attr,
+ &dev_attr_housekeeping.attr,
#ifdef CONFIG_NO_HZ_FULL
&dev_attr_nohz_full.attr,
#endif
+#ifdef CONFIG_CRASH_HOTPLUG
+ &dev_attr_crash_hotplug.attr,
+#endif
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
&dev_attr_modalias.attr,
#endif
@@ -487,94 +547,81 @@ static const struct attribute_group *cpu_root_attr_groups[] = {
bool cpu_is_hotpluggable(unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
- return dev && container_of(dev, struct cpu, dev)->hotpluggable;
+ return dev && container_of(dev, struct cpu, dev)->hotpluggable
+ && tick_nohz_cpu_hotpluggable(cpu);
}
EXPORT_SYMBOL_GPL(cpu_is_hotpluggable);
#ifdef CONFIG_GENERIC_CPU_DEVICES
-static DEFINE_PER_CPU(struct cpu, cpu_devices);
-#endif
-
-static void __init cpu_dev_register_generic(void)
-{
-#ifdef CONFIG_GENERIC_CPU_DEVICES
- int i;
-
- for_each_possible_cpu(i) {
- if (register_cpu(&per_cpu(cpu_devices, i), i))
- panic("Failed to register CPU device");
- }
-#endif
-}
+DEFINE_PER_CPU(struct cpu, cpu_devices);
-#ifdef CONFIG_GENERIC_CPU_VULNERABILITIES
-
-ssize_t __weak cpu_show_meltdown(struct device *dev,
- struct device_attribute *attr, char *buf)
+bool __weak arch_cpu_is_hotpluggable(int cpu)
{
- return sysfs_emit(buf, "Not affected\n");
+ return false;
}
-ssize_t __weak cpu_show_spectre_v1(struct device *dev,
- struct device_attribute *attr, char *buf)
+int __weak arch_register_cpu(int cpu)
{
- return sysfs_emit(buf, "Not affected\n");
-}
+ struct cpu *c = &per_cpu(cpu_devices, cpu);
-ssize_t __weak cpu_show_spectre_v2(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sysfs_emit(buf, "Not affected\n");
-}
+ c->hotpluggable = arch_cpu_is_hotpluggable(cpu);
-ssize_t __weak cpu_show_spec_store_bypass(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sysfs_emit(buf, "Not affected\n");
+ return register_cpu(c, cpu);
}
-ssize_t __weak cpu_show_l1tf(struct device *dev,
- struct device_attribute *attr, char *buf)
+#ifdef CONFIG_HOTPLUG_CPU
+void __weak arch_unregister_cpu(int num)
{
- return sysfs_emit(buf, "Not affected\n");
+ unregister_cpu(&per_cpu(cpu_devices, num));
}
+#endif /* CONFIG_HOTPLUG_CPU */
+#endif /* CONFIG_GENERIC_CPU_DEVICES */
-ssize_t __weak cpu_show_mds(struct device *dev,
- struct device_attribute *attr, char *buf)
+static void __init cpu_dev_register_generic(void)
{
- return sysfs_emit(buf, "Not affected\n");
-}
+ int i, ret;
-ssize_t __weak cpu_show_tsx_async_abort(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "Not affected\n");
-}
+ if (!IS_ENABLED(CONFIG_GENERIC_CPU_DEVICES))
+ return;
-ssize_t __weak cpu_show_itlb_multihit(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sysfs_emit(buf, "Not affected\n");
+ for_each_present_cpu(i) {
+ ret = arch_register_cpu(i);
+ if (ret && ret != -EPROBE_DEFER)
+ pr_warn("register_cpu %d failed (%d)\n", i, ret);
+ }
}
-ssize_t __weak cpu_show_srbds(struct device *dev,
+#ifdef CONFIG_GENERIC_CPU_VULNERABILITIES
+static ssize_t cpu_show_not_affected(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "Not affected\n");
}
-ssize_t __weak cpu_show_mmio_stale_data(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sysfs_emit(buf, "Not affected\n");
-}
-
-ssize_t __weak cpu_show_retbleed(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sysfs_emit(buf, "Not affected\n");
-}
+#define CPU_SHOW_VULN_FALLBACK(func) \
+ ssize_t cpu_show_##func(struct device *, \
+ struct device_attribute *, char *) \
+ __attribute__((weak, alias("cpu_show_not_affected")))
+
+CPU_SHOW_VULN_FALLBACK(meltdown);
+CPU_SHOW_VULN_FALLBACK(spectre_v1);
+CPU_SHOW_VULN_FALLBACK(spectre_v2);
+CPU_SHOW_VULN_FALLBACK(spec_store_bypass);
+CPU_SHOW_VULN_FALLBACK(l1tf);
+CPU_SHOW_VULN_FALLBACK(mds);
+CPU_SHOW_VULN_FALLBACK(tsx_async_abort);
+CPU_SHOW_VULN_FALLBACK(itlb_multihit);
+CPU_SHOW_VULN_FALLBACK(srbds);
+CPU_SHOW_VULN_FALLBACK(mmio_stale_data);
+CPU_SHOW_VULN_FALLBACK(retbleed);
+CPU_SHOW_VULN_FALLBACK(spec_rstack_overflow);
+CPU_SHOW_VULN_FALLBACK(gds);
+CPU_SHOW_VULN_FALLBACK(reg_file_data_sampling);
+CPU_SHOW_VULN_FALLBACK(ghostwrite);
+CPU_SHOW_VULN_FALLBACK(old_microcode);
+CPU_SHOW_VULN_FALLBACK(indirect_target_selection);
+CPU_SHOW_VULN_FALLBACK(tsa);
+CPU_SHOW_VULN_FALLBACK(vmscape);
static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
@@ -587,6 +634,14 @@ static DEVICE_ATTR(itlb_multihit, 0444, cpu_show_itlb_multihit, NULL);
static DEVICE_ATTR(srbds, 0444, cpu_show_srbds, NULL);
static DEVICE_ATTR(mmio_stale_data, 0444, cpu_show_mmio_stale_data, NULL);
static DEVICE_ATTR(retbleed, 0444, cpu_show_retbleed, NULL);
+static DEVICE_ATTR(spec_rstack_overflow, 0444, cpu_show_spec_rstack_overflow, NULL);
+static DEVICE_ATTR(gather_data_sampling, 0444, cpu_show_gds, NULL);
+static DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling, NULL);
+static DEVICE_ATTR(ghostwrite, 0444, cpu_show_ghostwrite, NULL);
+static DEVICE_ATTR(old_microcode, 0444, cpu_show_old_microcode, NULL);
+static DEVICE_ATTR(indirect_target_selection, 0444, cpu_show_indirect_target_selection, NULL);
+static DEVICE_ATTR(tsa, 0444, cpu_show_tsa, NULL);
+static DEVICE_ATTR(vmscape, 0444, cpu_show_vmscape, NULL);
static struct attribute *cpu_root_vulnerabilities_attrs[] = {
&dev_attr_meltdown.attr,
@@ -600,6 +655,14 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = {
&dev_attr_srbds.attr,
&dev_attr_mmio_stale_data.attr,
&dev_attr_retbleed.attr,
+ &dev_attr_spec_rstack_overflow.attr,
+ &dev_attr_gather_data_sampling.attr,
+ &dev_attr_reg_file_data_sampling.attr,
+ &dev_attr_ghostwrite.attr,
+ &dev_attr_old_microcode.attr,
+ &dev_attr_indirect_target_selection.attr,
+ &dev_attr_tsa.attr,
+ &dev_attr_vmscape.attr,
NULL
};
@@ -610,9 +673,13 @@ static const struct attribute_group cpu_root_vulnerabilities_group = {
static void __init cpu_register_vulnerabilities(void)
{
- if (sysfs_create_group(&cpu_subsys.dev_root->kobj,
- &cpu_root_vulnerabilities_group))
- pr_err("Unable to register CPU vulnerabilities\n");
+ struct device *dev = bus_get_dev_root(&cpu_subsys);
+
+ if (dev) {
+ if (sysfs_create_group(&dev->kobj, &cpu_root_vulnerabilities_group))
+ pr_err("Unable to register CPU vulnerabilities\n");
+ put_device(dev);
+ }
}
#else
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index ec69b43f926a..349f31bedfa1 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -25,6 +25,7 @@
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/async.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/slab.h>
@@ -192,7 +193,7 @@ void driver_deferred_probe_trigger(void)
* Kick the re-probe thread. It may already be scheduled, but it is
* safe to kick it again.
*/
- queue_work(system_unbound_wq, &deferred_probe_work);
+ queue_work(system_dfl_wq, &deferred_probe_work);
}
/**
@@ -248,7 +249,7 @@ static int deferred_devs_show(struct seq_file *s, void *data)
list_for_each_entry(curr, &deferred_probe_pending_list, deferred_probe)
seq_printf(s, "%s\t%s", dev_name(curr->device),
- curr->device->p->deferred_probe_reason ?: "\n");
+ curr->deferred_probe_reason ?: "\n");
mutex_unlock(&deferred_probe_mutex);
@@ -257,13 +258,11 @@ static int deferred_devs_show(struct seq_file *s, void *data)
DEFINE_SHOW_ATTRIBUTE(deferred_devs);
#ifdef CONFIG_MODULES
-int driver_deferred_probe_timeout = 10;
+static int driver_deferred_probe_timeout = 10;
#else
-int driver_deferred_probe_timeout;
+static int driver_deferred_probe_timeout;
#endif
-EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout);
-
static int __init deferred_probe_timeout_setup(char *str)
{
int timeout;
@@ -315,8 +314,10 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
mutex_lock(&deferred_probe_mutex);
list_for_each_entry(p, &deferred_probe_pending_list, deferred_probe)
- dev_info(p->device, "deferred probe pending\n");
+ dev_warn(p->device, "deferred probe pending: %s", p->deferred_probe_reason ?: "(reason unknown)\n");
mutex_unlock(&deferred_probe_mutex);
+
+ fw_devlink_probing_done();
}
static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func);
@@ -366,13 +367,17 @@ static int deferred_probe_initcall(void)
schedule_delayed_work(&deferred_probe_timeout_work,
driver_deferred_probe_timeout * HZ);
}
+
+ if (!IS_ENABLED(CONFIG_MODULES))
+ fw_devlink_probing_done();
+
return 0;
}
late_initcall(deferred_probe_initcall);
static void __exit deferred_probe_exit(void)
{
- debugfs_remove_recursive(debugfs_lookup("devices_deferred", NULL));
+ debugfs_lookup_and_remove("devices_deferred", NULL);
}
__exitcall(deferred_probe_exit);
@@ -389,17 +394,17 @@ bool device_is_bound(struct device *dev)
{
return dev->p && klist_node_attached(&dev->p->knode_driver);
}
+EXPORT_SYMBOL_GPL(device_is_bound);
static void driver_bound(struct device *dev)
{
if (device_is_bound(dev)) {
- pr_warn("%s: device %s already bound\n",
- __func__, kobject_name(&dev->kobj));
+ dev_warn(dev, "%s: device already bound\n", __func__);
return;
}
- pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name,
- __func__, dev_name(dev));
+ dev_dbg(dev, "driver: '%s': %s: bound to device\n", dev->driver->name,
+ __func__);
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
device_links_driver_bound(dev);
@@ -413,10 +418,7 @@ static void driver_bound(struct device *dev)
driver_deferred_probe_del(dev);
driver_deferred_probe_trigger();
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_BOUND_DRIVER, dev);
-
+ bus_notify(dev, BUS_NOTIFY_BOUND_DRIVER);
kobject_uevent(&dev->kobj, KOBJ_BIND);
}
@@ -435,9 +437,7 @@ static int driver_sysfs_add(struct device *dev)
{
int ret;
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_BIND_DRIVER, dev);
+ bus_notify(dev, BUS_NOTIFY_BIND_DRIVER);
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));
@@ -502,9 +502,8 @@ int device_bind_driver(struct device *dev)
device_links_force_bind(dev);
driver_bound(dev);
}
- else if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
+ else
+ bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
return ret;
}
EXPORT_SYMBOL_GPL(device_bind_driver);
@@ -512,6 +511,27 @@ EXPORT_SYMBOL_GPL(device_bind_driver);
static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
+static ssize_t state_synced_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = 0;
+
+ if (strcmp("1", buf))
+ return -EINVAL;
+
+ device_lock(dev);
+ if (!dev->state_synced) {
+ dev->state_synced = true;
+ dev_sync_state(dev);
+ } else {
+ ret = -EINVAL;
+ }
+ device_unlock(dev);
+
+ return ret ? ret : count;
+}
+
static ssize_t state_synced_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -523,7 +543,7 @@ static ssize_t state_synced_show(struct device *dev,
return sysfs_emit(buf, "%u\n", val);
}
-static DEVICE_ATTR_RO(state_synced);
+static DEVICE_ATTR_RW(state_synced);
static void device_unbind_cleanup(struct device *dev)
{
@@ -531,8 +551,9 @@ static void device_unbind_cleanup(struct device *dev)
arch_teardown_dma_ops(dev);
kfree(dev->dma_range_map);
dev->dma_range_map = NULL;
- dev->driver = NULL;
+ device_set_driver(dev, NULL);
dev_set_drvdata(dev, NULL);
+ dev_pm_domain_detach(dev, dev->power.detach_power_off);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
@@ -550,7 +571,7 @@ static void device_remove(struct device *dev)
dev->driver->remove(dev);
}
-static int call_driver_probe(struct device *dev, struct device_driver *drv)
+static int call_driver_probe(struct device *dev, const struct device_driver *drv)
{
int ret = 0;
@@ -568,20 +589,20 @@ static int call_driver_probe(struct device *dev, struct device_driver *drv)
break;
case -ENODEV:
case -ENXIO:
- pr_debug("%s: probe of %s rejects match %d\n",
- drv->name, dev_name(dev), ret);
+ dev_dbg(dev, "probe with driver %s rejects match %d\n",
+ drv->name, ret);
break;
default:
/* driver matched but the probe failed */
- pr_warn("%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
+ dev_err(dev, "probe with driver %s failed with error %d\n",
+ drv->name, ret);
break;
}
return ret;
}
-static int really_probe(struct device *dev, struct device_driver *drv)
+static int really_probe(struct device *dev, const struct device_driver *drv)
{
bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
!drv->suppress_bind_attrs;
@@ -601,8 +622,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
if (link_ret == -EPROBE_DEFER)
return link_ret;
- pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
+ dev_dbg(dev, "bus: '%s': %s: probing driver %s with device\n",
+ drv->bus->name, __func__, drv->name);
if (!list_empty(&dev->devres_head)) {
dev_crit(dev, "Resources present before probing\n");
ret = -EBUSY;
@@ -610,7 +631,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
}
re_probe:
- dev->driver = drv;
+ device_set_driver(dev, drv);
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
@@ -625,8 +646,7 @@ re_probe:
ret = driver_sysfs_add(dev);
if (ret) {
- pr_err("%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
+ dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
goto sysfs_failed;
}
@@ -674,6 +694,8 @@ re_probe:
device_remove(dev);
driver_sysfs_remove(dev);
+ if (dev->bus && dev->bus->dma_cleanup)
+ dev->bus->dma_cleanup(dev);
device_unbind_cleanup(dev);
goto re_probe;
@@ -685,8 +707,8 @@ re_probe:
dev->pm_domain->sync(dev);
driver_bound(dev);
- pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
+ dev_dbg(dev, "bus: '%s': %s: bound device to driver %s\n",
+ drv->bus->name, __func__, drv->name);
goto done;
dev_sysfs_state_synced_failed:
@@ -695,9 +717,7 @@ dev_groups_failed:
probe_failed:
driver_sysfs_remove(dev);
sysfs_failed:
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
+ bus_notify(dev, BUS_NOTIFY_DRIVER_NOT_BOUND);
if (dev->bus && dev->bus->dma_cleanup)
dev->bus->dma_cleanup(dev);
pinctrl_bind_failed:
@@ -710,7 +730,7 @@ done:
/*
* For initcall_debug, show the driver probe time.
*/
-static int really_probe_debug(struct device *dev, struct device_driver *drv)
+static int really_probe_debug(struct device *dev, const struct device_driver *drv)
{
ktime_t calltime, rettime;
int ret;
@@ -718,7 +738,12 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv)
calltime = ktime_get();
ret = really_probe(dev, drv);
rettime = ktime_get();
- pr_debug("probe of %s returned %d after %lld usecs\n",
+ /*
+ * Don't change this to pr_debug() because that requires
+ * CONFIG_DYNAMIC_DEBUG and we want a simple 'initcall_debug' on the
+ * kernel commandline to print this all the time at the debug level.
+ */
+ printk(KERN_DEBUG "probe of %s returned %d after %lld usecs\n",
dev_name(dev), ret, ktime_us_delta(rettime, calltime));
return ret;
}
@@ -729,14 +754,12 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv)
*
* Should somehow figure out how to use a semaphore, not an atomic variable...
*/
-int driver_probe_done(void)
+bool __init driver_probe_done(void)
{
int local_probe_count = atomic_read(&probe_count);
pr_debug("%s: probe_count = %d\n", __func__, local_probe_count);
- if (local_probe_count)
- return -EBUSY;
- return 0;
+ return !local_probe_count;
}
/**
@@ -754,7 +777,7 @@ void wait_for_device_probe(void)
}
EXPORT_SYMBOL_GPL(wait_for_device_probe);
-static int __driver_probe_device(struct device_driver *drv, struct device *dev)
+static int __driver_probe_device(const struct device_driver *drv, struct device *dev)
{
int ret = 0;
@@ -764,8 +787,8 @@ static int __driver_probe_device(struct device_driver *drv, struct device *dev)
return -EBUSY;
dev->can_match = true;
- pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
+ dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n",
+ drv->bus->name, __func__, drv->name);
pm_runtime_get_suppliers(dev);
if (dev->parent)
@@ -799,7 +822,7 @@ static int __driver_probe_device(struct device_driver *drv, struct device *dev)
*
* If the device has a parent, runtime-resume the parent before driver probing.
*/
-static int driver_probe_device(struct device_driver *drv, struct device *dev)
+static int driver_probe_device(const struct device_driver *drv, struct device *dev)
{
int trigger_count = atomic_read(&deferred_trigger_count);
int ret;
@@ -836,14 +859,14 @@ static int __init save_async_options(char *buf)
if (strlen(buf) >= ASYNC_DRV_NAMES_MAX_LEN)
pr_warn("Too long list of driver names for 'driver_async_probe'!\n");
- strlcpy(async_probe_drv_names, buf, ASYNC_DRV_NAMES_MAX_LEN);
+ strscpy(async_probe_drv_names, buf, ASYNC_DRV_NAMES_MAX_LEN);
async_probe_default = parse_option_str(async_probe_drv_names, "*");
return 1;
}
__setup("driver_async_probe=", save_async_options);
-bool driver_allows_async_probing(struct device_driver *drv)
+static bool driver_allows_async_probing(const struct device_driver *drv)
{
switch (drv->probe_type) {
case PROBE_PREFER_ASYNCHRONOUS:
@@ -992,7 +1015,7 @@ static int __device_attach(struct device *dev, bool allow_async)
if (ret == 0)
ret = 1;
else {
- dev->driver = NULL;
+ device_set_driver(dev, NULL);
ret = 0;
}
} else {
@@ -1054,7 +1077,15 @@ EXPORT_SYMBOL_GPL(device_attach);
void device_initial_probe(struct device *dev)
{
- __device_attach(dev, true);
+ struct subsys_private *sp = bus_to_subsys(dev->bus);
+
+ if (!sp)
+ return;
+
+ if (sp->drivers_autoprobe)
+ __device_attach(dev, true);
+
+ subsys_put(sp);
}
/*
@@ -1097,7 +1128,7 @@ static void __device_driver_unlock(struct device *dev, struct device *parent)
* Manually attach driver to a device. Will acquire both @dev lock and
* @dev->parent lock if needed. Returns 0 on success, -ERR on failure.
*/
-int device_driver_attach(struct device_driver *drv, struct device *dev)
+int device_driver_attach(const struct device_driver *drv, struct device *dev)
{
int ret;
@@ -1117,7 +1148,7 @@ EXPORT_SYMBOL_GPL(device_driver_attach);
static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
{
struct device *dev = _dev;
- struct device_driver *drv;
+ const struct device_driver *drv;
int ret;
__device_driver_lock(dev, dev->parent);
@@ -1133,7 +1164,7 @@ static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
static int __driver_attach(struct device *dev, void *data)
{
- struct device_driver *drv = data;
+ const struct device_driver *drv = data;
bool async = false;
int ret;
@@ -1162,7 +1193,11 @@ static int __driver_attach(struct device *dev, void *data)
return 0;
} else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d\n", ret);
- return ret;
+ /*
+ * Driver could not match with device, but may match with
+ * another device on the bus.
+ */
+ return 0;
} /* ret > 0 means positive match */
if (driver_allows_async_probing(drv)) {
@@ -1202,9 +1237,10 @@ static int __driver_attach(struct device *dev, void *data)
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/
-int driver_attach(struct device_driver *drv)
+int driver_attach(const struct device_driver *drv)
{
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
+ /* The (void *) will be put back to const * in __driver_attach() */
+ return bus_for_each_dev(drv->bus, NULL, (void *)drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
@@ -1239,10 +1275,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
driver_sysfs_remove(dev);
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_UNBIND_DRIVER,
- dev);
+ bus_notify(dev, BUS_NOTIFY_UNBIND_DRIVER);
pm_runtime_put_sync(dev);
@@ -1251,22 +1284,19 @@ static void __device_release_driver(struct device *dev, struct device *parent)
if (dev->bus && dev->bus->dma_cleanup)
dev->bus->dma_cleanup(dev);
- device_links_driver_cleanup(dev);
device_unbind_cleanup(dev);
+ device_links_driver_cleanup(dev);
klist_remove(&dev->p->knode_driver);
device_pm_check_callbacks(dev);
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_UNBOUND_DRIVER,
- dev);
+ bus_notify(dev, BUS_NOTIFY_UNBOUND_DRIVER);
kobject_uevent(&dev->kobj, KOBJ_UNBIND);
}
}
void device_release_driver_internal(struct device *dev,
- struct device_driver *drv,
+ const struct device_driver *drv,
struct device *parent)
{
__device_driver_lock(dev, parent);
@@ -1315,7 +1345,7 @@ void device_driver_detach(struct device *dev)
* driver_detach - detach driver from all devices it controls.
* @drv: driver.
*/
-void driver_detach(struct device_driver *drv)
+void driver_detach(const struct device_driver *drv)
{
struct device_private *dev_prv;
struct device *dev;
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c
index f4d794d6bb85..55bdc7f5e59d 100644
--- a/drivers/base/devcoredump.c
+++ b/drivers/base/devcoredump.c
@@ -18,17 +18,51 @@ static struct class devcd_class;
/* global disable flag, for security purposes */
static bool devcd_disabled;
-/* if data isn't read by userspace after 5 minutes then delete it */
-#define DEVCD_TIMEOUT (HZ * 60 * 5)
-
struct devcd_entry {
struct device devcd_dev;
void *data;
size_t datalen;
+ /*
+ * There are 2 races for which mutex is required.
+ *
+ * The first race is between device creation and userspace writing to
+ * schedule immediately destruction.
+ *
+ * This race is handled by arming the timer before device creation, but
+ * when device creation fails the timer still exists.
+ *
+ * To solve this, hold the mutex during device_add(), and set
+ * init_completed on success before releasing the mutex.
+ *
+ * That way the timer will never fire until device_add() is called,
+ * it will do nothing if init_completed is not set. The timer is also
+ * cancelled in that case.
+ *
+ * The second race involves multiple parallel invocations of devcd_free(),
+ * add a deleted flag so only 1 can call the destructor.
+ */
+ struct mutex mutex;
+ bool init_completed, deleted;
struct module *owner;
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
void *data, size_t datalen);
void (*free)(void *data);
+ /*
+ * If nothing interferes and device_add() was returns success,
+ * del_wk will destroy the device after the timer fires.
+ *
+ * Multiple userspace processes can interfere in the working of the timer:
+ * - Writing to the coredump will reschedule the timer to run immediately,
+ * if still armed.
+ *
+ * This is handled by using "if (cancel_delayed_work()) {
+ * schedule_delayed_work() }", to prevent re-arming after having
+ * been previously fired.
+ * - Writing to /sys/class/devcoredump/disabled will destroy the
+ * coredump synchronously.
+ * This is handled by using disable_delayed_work_sync(), and then
+ * checking if deleted flag is set with &devcd->mutex held.
+ */
struct delayed_work del_wk;
struct device *failing_dev;
};
@@ -57,18 +91,31 @@ static void devcd_dev_release(struct device *dev)
kfree(devcd);
}
+static void __devcd_del(struct devcd_entry *devcd)
+{
+ devcd->deleted = true;
+ device_del(&devcd->devcd_dev);
+ put_device(&devcd->devcd_dev);
+}
+
static void devcd_del(struct work_struct *wk)
{
struct devcd_entry *devcd;
+ bool init_completed;
devcd = container_of(wk, struct devcd_entry, del_wk.work);
- device_del(&devcd->devcd_dev);
- put_device(&devcd->devcd_dev);
+ /* devcd->mutex serializes against dev_coredumpm_timeout */
+ mutex_lock(&devcd->mutex);
+ init_completed = devcd->init_completed;
+ mutex_unlock(&devcd->mutex);
+
+ if (init_completed)
+ __devcd_del(devcd);
}
static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -78,25 +125,26 @@ static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
}
static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct devcd_entry *devcd = dev_to_devcd(dev);
- mod_delayed_work(system_wq, &devcd->del_wk, 0);
+ /*
+ * Although it's tempting to use mod_delayed work here,
+ * that will cause a reschedule if the timer already fired.
+ */
+ if (cancel_delayed_work(&devcd->del_wk))
+ schedule_delayed_work(&devcd->del_wk, 0);
return count;
}
-static struct bin_attribute devcd_attr_data = {
- .attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, },
- .size = 0,
- .read = devcd_data_read,
- .write = devcd_data_write,
-};
+static const struct bin_attribute devcd_attr_data =
+ __BIN_ATTR(data, 0600, devcd_data_read, devcd_data_write, 0);
-static struct bin_attribute *devcd_dev_bin_attrs[] = {
+static const struct bin_attribute *const devcd_dev_bin_attrs[] = {
&devcd_attr_data, NULL,
};
@@ -112,17 +160,54 @@ static int devcd_free(struct device *dev, void *data)
{
struct devcd_entry *devcd = dev_to_devcd(dev);
- flush_delayed_work(&devcd->del_wk);
+ /*
+ * To prevent a race with devcd_data_write(), disable work and
+ * complete manually instead.
+ *
+ * We cannot rely on the return value of
+ * disable_delayed_work_sync() here, because it might be in the
+ * middle of a cancel_delayed_work + schedule_delayed_work pair.
+ *
+ * devcd->mutex here guards against multiple parallel invocations
+ * of devcd_free().
+ */
+ disable_delayed_work_sync(&devcd->del_wk);
+ mutex_lock(&devcd->mutex);
+ if (!devcd->deleted)
+ __devcd_del(devcd);
+ mutex_unlock(&devcd->mutex);
return 0;
}
-static ssize_t disabled_show(struct class *class, struct class_attribute *attr,
+static ssize_t disabled_show(const struct class *class, const struct class_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", devcd_disabled);
}
-static ssize_t disabled_store(struct class *class, struct class_attribute *attr,
+/*
+ *
+ * disabled_store() worker()
+ * class_for_each_device(&devcd_class,
+ * NULL, NULL, devcd_free)
+ * ...
+ * ...
+ * while ((dev = class_dev_iter_next(&iter))
+ * devcd_del()
+ * device_del()
+ * put_device() <- last reference
+ * error = fn(dev, data) devcd_dev_release()
+ * devcd_free(dev, data) kfree(devcd)
+ *
+ *
+ * In the above diagram, it looks like disabled_store() would be racing with parallelly
+ * running devcd_del() and result in memory abort after dropping its last reference with
+ * put_device(). However, this will not happens as fn(dev, data) runs
+ * with its own reference to device via klist_node so it is not its last reference.
+ * so, above situation would not occur.
+ */
+
+static ssize_t disabled_store(const struct class *class, const struct class_attribute *attr,
const char *buf, size_t count)
{
long tmp = simple_strtol(buf, NULL, 10);
@@ -151,7 +236,6 @@ ATTRIBUTE_GROUPS(devcd_class);
static struct class devcd_class = {
.name = "devcoredump",
- .owner = THIS_MODULE,
.dev_release = devcd_dev_release,
.dev_groups = devcd_dev_groups,
.class_groups = devcd_class_groups,
@@ -214,6 +298,8 @@ static void devcd_free_sgtable(void *data)
* @offset: start copy from @offset@ bytes from the head of the data
* in the given scatterlist
* @data_len: the length of the data in the sg_table
+ *
+ * Returns: the number of bytes copied
*/
static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
size_t buf_len, void *data,
@@ -231,7 +317,31 @@ static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
}
/**
- * dev_coredumpm - create device coredump with read/free methods
+ * dev_coredump_put - remove device coredump
+ * @dev: the struct device for the crashed device
+ *
+ * dev_coredump_put() removes coredump, if exists, for a given device from
+ * the file system and free its associated data otherwise, does nothing.
+ *
+ * It is useful for modules that do not want to keep coredump
+ * available after its unload.
+ */
+void dev_coredump_put(struct device *dev)
+{
+ struct device *existing;
+
+ existing = class_find_device(&devcd_class, NULL, dev,
+ devcd_match_failing);
+ if (existing) {
+ devcd_free(existing, NULL);
+ put_device(existing);
+ }
+}
+EXPORT_SYMBOL_GPL(dev_coredump_put);
+
+/**
+ * dev_coredumpm_timeout - create device coredump with read/free methods with a
+ * custom timeout.
* @dev: the struct device for the crashed device
* @owner: the module that contains the read/free functions, use %THIS_MODULE
* @data: data cookie for the @read/@free functions
@@ -239,17 +349,20 @@ static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
* @gfp: allocation flags
* @read: function to read from the given buffer
* @free: function to free the given buffer
+ * @timeout: time in jiffies to remove coredump
*
* Creates a new device coredump for the given device. If a previous one hasn't
* been read yet, the new coredump is discarded. The data lifetime is determined
* by the device coredump framework and when it is no longer needed the @free
* function will be called to free the data.
*/
-void dev_coredumpm(struct device *dev, struct module *owner,
- void *data, size_t datalen, gfp_t gfp,
- ssize_t (*read)(char *buffer, loff_t offset, size_t count,
- void *data, size_t datalen),
- void (*free)(void *data))
+void dev_coredumpm_timeout(struct device *dev, struct module *owner,
+ void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset,
+ size_t count, void *data,
+ size_t datalen),
+ void (*free)(void *data),
+ unsigned long timeout)
{
static atomic_t devcd_count = ATOMIC_INIT(0);
struct devcd_entry *devcd;
@@ -278,13 +391,23 @@ void dev_coredumpm(struct device *dev, struct module *owner,
devcd->read = read;
devcd->free = free;
devcd->failing_dev = get_device(dev);
+ devcd->deleted = false;
+ mutex_init(&devcd->mutex);
device_initialize(&devcd->devcd_dev);
dev_set_name(&devcd->devcd_dev, "devcd%d",
atomic_inc_return(&devcd_count));
devcd->devcd_dev.class = &devcd_class;
+ dev_set_uevent_suppress(&devcd->devcd_dev, true);
+
+ /* devcd->mutex prevents devcd_del() completing until init finishes */
+ mutex_lock(&devcd->mutex);
+ devcd->init_completed = false;
+ INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
+ schedule_delayed_work(&devcd->del_wk, timeout);
+
if (device_add(&devcd->devcd_dev))
goto put_device;
@@ -299,18 +422,28 @@ void dev_coredumpm(struct device *dev, struct module *owner,
"devcoredump"))
dev_warn(dev, "devcoredump create_link failed\n");
- INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
- schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
+ dev_set_uevent_suppress(&devcd->devcd_dev, false);
+ kobject_uevent(&devcd->devcd_dev.kobj, KOBJ_ADD);
+ /*
+ * Safe to run devcd_del() now that we are done with devcd_dev.
+ * Alternatively we could have taken a ref on devcd_dev before
+ * dropping the lock.
+ */
+ devcd->init_completed = true;
+ mutex_unlock(&devcd->mutex);
return;
put_device:
+ mutex_unlock(&devcd->mutex);
+ cancel_delayed_work_sync(&devcd->del_wk);
put_device(&devcd->devcd_dev);
+
put_module:
module_put(owner);
free:
free(data);
}
-EXPORT_SYMBOL_GPL(dev_coredumpm);
+EXPORT_SYMBOL_GPL(dev_coredumpm_timeout);
/**
* dev_coredumpsg - create device coredump that uses scatterlist as data
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 864d0b3f566e..f54db6d138ab 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -29,10 +29,10 @@ struct devres {
* Some archs want to perform DMA into kmalloc caches
* and need a guaranteed alignment larger than
* the alignment of a 64-bit integer.
- * Thus we use ARCH_KMALLOC_MINALIGN here and get exactly the same
- * buffer alignment as if it was allocated by plain kmalloc().
+ * Thus we use ARCH_DMA_MINALIGN for data[] which will force the same
+ * alignment for struct devres when allocated by kmalloc().
*/
- u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
+ u8 __aligned(ARCH_DMA_MINALIGN) data[];
};
struct devres_group {
@@ -85,7 +85,7 @@ static void group_close_release(struct device *dev, void *res)
/* noop */
}
-static struct devres_group * node_to_group(struct devres_node *node)
+static struct devres_group *node_to_group(struct devres_node *node)
{
if (node->release == &group_open_release)
return container_of(node, struct devres_group, node[0]);
@@ -101,11 +101,14 @@ static bool check_dr_size(size_t size, size_t *tot_size)
size, tot_size)))
return false;
+ /* Actually allocate the full kmalloc bucket size. */
+ *tot_size = kmalloc_size_roundup(*tot_size);
+
return true;
}
-static __always_inline struct devres * alloc_dr(dr_release_t release,
- size_t size, gfp_t gfp, int nid)
+static __always_inline struct devres *alloc_dr(dr_release_t release,
+ size_t size, gfp_t gfp, int nid)
{
size_t tot_size;
struct devres *dr;
@@ -117,7 +120,9 @@ static __always_inline struct devres * alloc_dr(dr_release_t release,
if (unlikely(!dr))
return NULL;
- memset(dr, 0, offsetof(struct devres, data));
+ /* No need to clear memory twice */
+ if (!(gfp & __GFP_ZERO))
+ memset(dr, 0, offsetof(struct devres, data));
INIT_LIST_HEAD(&dr->node.entry);
dr->node.release = release;
@@ -278,8 +283,8 @@ static struct devres *find_dr(struct device *dev, dr_release_t release,
* RETURNS:
* Pointer to found devres, NULL if not found.
*/
-void * devres_find(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data)
+void *devres_find(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
{
struct devres *dr;
unsigned long flags;
@@ -308,8 +313,8 @@ EXPORT_SYMBOL_GPL(devres_find);
* RETURNS:
* Pointer to found or added devres.
*/
-void * devres_get(struct device *dev, void *new_res,
- dr_match_t match, void *match_data)
+void *devres_get(struct device *dev, void *new_res,
+ dr_match_t match, void *match_data)
{
struct devres *new_dr = container_of(new_res, struct devres, data);
struct devres *dr;
@@ -344,8 +349,8 @@ EXPORT_SYMBOL_GPL(devres_get);
* RETURNS:
* Pointer to removed devres on success, NULL if not found.
*/
-void * devres_remove(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data)
+void *devres_remove(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
{
struct devres *dr;
unsigned long flags;
@@ -544,7 +549,7 @@ int devres_release_all(struct device *dev)
* RETURNS:
* ID of the new group, NULL on failure.
*/
-void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
+void *devres_open_group(struct device *dev, void *id, gfp_t gfp)
{
struct devres_group *grp;
unsigned long flags;
@@ -562,6 +567,7 @@ void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
grp->id = grp;
if (id)
grp->id = id;
+ grp->color = 0;
spin_lock_irqsave(&dev->devres_lock, flags);
add_dr(dev, &grp->node[0]);
@@ -570,8 +576,11 @@ void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(devres_open_group);
-/* Find devres group with ID @id. If @id is NULL, look for the latest. */
-static struct devres_group * find_group(struct device *dev, void *id)
+/*
+ * Find devres group with ID @id. If @id is NULL, look for the latest open
+ * group.
+ */
+static struct devres_group *find_group(struct device *dev, void *id)
{
struct devres_node *node;
@@ -681,6 +690,13 @@ int devres_release_group(struct device *dev, void *id)
spin_unlock_irqrestore(&dev->devres_lock, flags);
release_nodes(dev, &todo);
+ } else if (list_empty(&dev->devres_head)) {
+ /*
+ * dev is probably dying via devres_release_all(): groups
+ * have already been removed and are on the process of
+ * being released - don't touch and don't warn.
+ */
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
} else {
WARN_ON(1);
spin_unlock_irqrestore(&dev->devres_lock, flags);
@@ -717,20 +733,21 @@ static void devm_action_release(struct device *dev, void *res)
}
/**
- * devm_add_action() - add a custom action to list of managed resources
+ * __devm_add_action() - add a custom action to list of managed resources
* @dev: Device that owns the action
* @action: Function that should be called
* @data: Pointer to data passed to @action implementation
+ * @name: Name of the resource (for debugging purposes)
*
* This adds a custom action to the list of managed resources so that
* it gets executed as part of standard resource unwinding.
*/
-int devm_add_action(struct device *dev, void (*action)(void *), void *data)
+int __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name)
{
struct action_devres *devres;
- devres = devres_alloc(devm_action_release,
- sizeof(struct action_devres), GFP_KERNEL);
+ devres = __devres_alloc_node(devm_action_release, sizeof(struct action_devres),
+ GFP_KERNEL, NUMA_NO_NODE, name);
if (!devres)
return -ENOMEM;
@@ -740,28 +757,52 @@ int devm_add_action(struct device *dev, void (*action)(void *), void *data)
devres_add(dev, devres);
return 0;
}
-EXPORT_SYMBOL_GPL(devm_add_action);
+EXPORT_SYMBOL_GPL(__devm_add_action);
+
+bool devm_is_action_added(struct device *dev, void (*action)(void *), void *data)
+{
+ struct action_devres devres = {
+ .data = data,
+ .action = action,
+ };
+
+ return devres_find(dev, devm_action_release, devm_action_match, &devres);
+}
+EXPORT_SYMBOL_GPL(devm_is_action_added);
/**
- * devm_remove_action() - removes previously added custom action
+ * devm_remove_action_nowarn() - removes previously added custom action
* @dev: Device that owns the action
* @action: Function implementing the action
* @data: Pointer to data passed to @action implementation
*
* Removes instance of @action previously added by devm_add_action().
* Both action and data should match one of the existing entries.
+ *
+ * In contrast to devm_remove_action(), this function does not WARN() if no
+ * entry could have been found.
+ *
+ * This should only be used if the action is contained in an object with
+ * independent lifetime management, e.g. the Devres rust abstraction.
+ *
+ * Causing the warning from regular driver code most likely indicates an abuse
+ * of the devres API.
+ *
+ * Returns: 0 on success, -ENOENT if no entry could have been found.
*/
-void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
+int devm_remove_action_nowarn(struct device *dev,
+ void (*action)(void *),
+ void *data)
{
struct action_devres devres = {
.data = data,
.action = action,
};
- WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
- &devres));
+ return devres_destroy(dev, devm_action_release, devm_action_match,
+ &devres);
}
-EXPORT_SYMBOL_GPL(devm_remove_action);
+EXPORT_SYMBOL_GPL(devm_remove_action_nowarn);
/**
* devm_release_action() - release previously added custom action
@@ -890,9 +931,12 @@ void *devm_krealloc(struct device *dev, void *ptr, size_t new_size, gfp_t gfp)
/*
* Otherwise: allocate new, larger chunk. We need to allocate before
* taking the lock as most probably the caller uses GFP_KERNEL.
+ * alloc_dr() will call check_dr_size() to reserve extra memory
+ * for struct devres automatically, so size @new_size user request
+ * is delivered to it directly as devm_kmalloc() does.
*/
new_dr = alloc_dr(devm_kmalloc_release,
- total_new_size, gfp, dev_to_node(dev));
+ new_size, gfp, dev_to_node(dev));
if (!new_dr)
return NULL;
@@ -943,17 +987,10 @@ EXPORT_SYMBOL_GPL(devm_krealloc);
*/
char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp)
{
- size_t size;
- char *buf;
-
if (!s)
return NULL;
- size = strlen(s) + 1;
- buf = devm_kmalloc(dev, size, gfp);
- if (buf)
- memcpy(buf, s, size);
- return buf;
+ return devm_kmemdup(dev, s, strlen(s) + 1, gfp);
}
EXPORT_SYMBOL_GPL(devm_kstrdup);
@@ -1080,6 +1117,27 @@ void *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(devm_kmemdup);
+/**
+ * devm_kmemdup_const - conditionally duplicate and manage a region of memory
+ *
+ * @dev: Device this memory belongs to
+ * @src: memory region to duplicate
+ * @len: memory region length,
+ * @gfp: GFP mask to use
+ *
+ * Return: source address if it is in .rodata or the return value of kmemdup()
+ * to which the function falls back otherwise.
+ */
+const void *
+devm_kmemdup_const(struct device *dev, const void *src, size_t len, gfp_t gfp)
+{
+ if (is_kernel_rodata((unsigned long)src))
+ return src;
+
+ return devm_kmemdup(dev, src, len, gfp);
+}
+EXPORT_SYMBOL_GPL(devm_kmemdup_const);
+
struct pages_devres {
unsigned long addr;
unsigned int order;
@@ -1164,13 +1222,6 @@ static void devm_percpu_release(struct device *dev, void *pdata)
free_percpu(p);
}
-static int devm_percpu_match(struct device *dev, void *data, void *p)
-{
- struct devres *devr = container_of(data, struct devres, data);
-
- return *(void **)devr->data == p;
-}
-
/**
* __devm_alloc_percpu - Resource-managed alloc_percpu
* @dev: Device to allocate per-cpu memory for
@@ -1206,17 +1257,3 @@ void __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
return pcpu;
}
EXPORT_SYMBOL_GPL(__devm_alloc_percpu);
-
-/**
- * devm_free_percpu - Resource-managed free_percpu
- * @dev: Device this memory belongs to
- * @pdata: Per-cpu memory to free
- *
- * Free memory allocated with devm_alloc_percpu().
- */
-void devm_free_percpu(struct device *dev, void __percpu *pdata)
-{
- WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match,
- (__force void *)pdata));
-}
-EXPORT_SYMBOL_GPL(devm_free_percpu);
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index e4bffeabf344..194b44075ac7 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -13,6 +13,8 @@
* overwrite the default setting if needed.
*/
+#define pr_fmt(fmt) "devtmpfs: " fmt
+
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/mount.h>
@@ -61,22 +63,6 @@ __setup("devtmpfs.mount=", mount_param);
static struct vfsmount *mnt;
-static struct dentry *public_dev_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data)
-{
- struct super_block *s = mnt->mnt_sb;
- int err;
-
- atomic_inc(&s->s_active);
- down_write(&s->s_umount);
- err = reconfigure_single(s, flags, data);
- if (err < 0) {
- deactivate_locked_super(s);
- return ERR_PTR(err);
- }
- return dget(s->s_root);
-}
-
static struct file_system_type internal_fs_type = {
.name = "devtmpfs",
#ifdef CONFIG_TMPFS
@@ -84,22 +70,44 @@ static struct file_system_type internal_fs_type = {
#else
.init_fs_context = ramfs_init_fs_context,
#endif
- .kill_sb = kill_litter_super,
+ .kill_sb = kill_anon_super,
};
-static struct file_system_type dev_fs_type = {
- .name = "devtmpfs",
- .mount = public_dev_mount,
-};
-
-#ifdef CONFIG_BLOCK
-static inline int is_blockdev(struct device *dev)
+/* Simply take a ref on the existing mount */
+static int devtmpfs_get_tree(struct fs_context *fc)
{
- return dev->class == &block_class;
+ struct super_block *sb = mnt->mnt_sb;
+
+ atomic_inc(&sb->s_active);
+ down_write(&sb->s_umount);
+ fc->root = dget(sb->s_root);
+ return 0;
}
+
+/* Ops are filled in during init depending on underlying shmem or ramfs type */
+struct fs_context_operations devtmpfs_context_ops = {};
+
+/* Call the underlying initialization and set to our ops */
+static int devtmpfs_init_fs_context(struct fs_context *fc)
+{
+ int ret;
+#ifdef CONFIG_TMPFS
+ ret = shmem_init_fs_context(fc);
#else
-static inline int is_blockdev(struct device *dev) { return 0; }
+ ret = ramfs_init_fs_context(fc);
#endif
+ if (ret < 0)
+ return ret;
+
+ fc->ops = &devtmpfs_context_ops;
+
+ return 0;
+}
+
+static struct file_system_type dev_fs_type = {
+ .name = "devtmpfs",
+ .init_fs_context = devtmpfs_init_fs_context,
+};
static int devtmpfs_submit_req(struct req *req, const char *tmp)
{
@@ -167,18 +175,17 @@ static int dev_mkdir(const char *name, umode_t mode)
{
struct dentry *dentry;
struct path path;
- int err;
- dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY);
+ dentry = start_creating_path(AT_FDCWD, name, &path, LOOKUP_DIRECTORY);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- err = vfs_mkdir(&init_user_ns, d_inode(path.dentry), dentry, mode);
- if (!err)
+ dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
+ if (!IS_ERR(dentry))
/* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread;
- done_path_create(&path, dentry);
- return err;
+ end_creating_path(&path, dentry);
+ return PTR_ERR_OR_ZERO(dentry);
}
static int create_path(const char *nodepath)
@@ -215,16 +222,16 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
struct path path;
int err;
- dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
+ dentry = start_creating_path(AT_FDCWD, nodename, &path, 0);
if (dentry == ERR_PTR(-ENOENT)) {
create_path(nodename);
- dentry = kern_path_create(AT_FDCWD, nodename, &path, 0);
+ dentry = start_creating_path(AT_FDCWD, nodename, &path, 0);
}
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- err = vfs_mknod(&init_user_ns, d_inode(path.dentry), dentry, mode,
- dev->devt);
+ err = vfs_mknod(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode,
+ dev->devt, NULL);
if (!err) {
struct iattr newattrs;
@@ -233,13 +240,13 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
newattrs.ia_gid = gid;
newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
inode_lock(d_inode(dentry));
- notify_change(&init_user_ns, dentry, &newattrs, NULL);
+ notify_change(&nop_mnt_idmap, dentry, &newattrs, NULL);
inode_unlock(d_inode(dentry));
/* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread;
}
- done_path_create(&path, dentry);
+ end_creating_path(&path, dentry);
return err;
}
@@ -249,21 +256,16 @@ static int dev_rmdir(const char *name)
struct dentry *dentry;
int err;
- dentry = kern_path_locked(name, &parent);
+ dentry = start_removing_path(name, &parent);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- if (d_really_is_positive(dentry)) {
- if (d_inode(dentry)->i_private == &thread)
- err = vfs_rmdir(&init_user_ns, d_inode(parent.dentry),
- dentry);
- else
- err = -EPERM;
- } else {
- err = -ENOENT;
- }
- dput(dentry);
- inode_unlock(d_inode(parent.dentry));
- path_put(&parent);
+ if (d_inode(dentry)->i_private == &thread)
+ err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry),
+ dentry, NULL);
+ else
+ err = -EPERM;
+
+ end_removing_path(&parent, dentry);
return err;
}
@@ -292,7 +294,7 @@ static int delete_path(const char *nodepath)
return err;
}
-static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
+static int dev_mynode(struct device *dev, struct inode *inode)
{
/* did we create it */
if (inode->i_private != &thread)
@@ -300,13 +302,13 @@ static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *sta
/* does the dev_t match */
if (is_blockdev(dev)) {
- if (!S_ISBLK(stat->mode))
+ if (!S_ISBLK(inode->i_mode))
return 0;
} else {
- if (!S_ISCHR(stat->mode))
+ if (!S_ISCHR(inode->i_mode))
return 0;
}
- if (stat->rdev != dev->devt)
+ if (inode->i_rdev != dev->devt)
return 0;
/* ours */
@@ -317,44 +319,36 @@ static int handle_remove(const char *nodename, struct device *dev)
{
struct path parent;
struct dentry *dentry;
+ struct inode *inode;
int deleted = 0;
- int err;
+ int err = 0;
- dentry = kern_path_locked(nodename, &parent);
+ dentry = start_removing_path(nodename, &parent);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- if (d_really_is_positive(dentry)) {
- struct kstat stat;
- struct path p = {.mnt = parent.mnt, .dentry = dentry};
- err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE,
- AT_STATX_SYNC_AS_STAT);
- if (!err && dev_mynode(dev, d_inode(dentry), &stat)) {
- struct iattr newattrs;
- /*
- * before unlinking this node, reset permissions
- * of possible references like hardlinks
- */
- newattrs.ia_uid = GLOBAL_ROOT_UID;
- newattrs.ia_gid = GLOBAL_ROOT_GID;
- newattrs.ia_mode = stat.mode & ~0777;
- newattrs.ia_valid =
- ATTR_UID|ATTR_GID|ATTR_MODE;
- inode_lock(d_inode(dentry));
- notify_change(&init_user_ns, dentry, &newattrs, NULL);
- inode_unlock(d_inode(dentry));
- err = vfs_unlink(&init_user_ns, d_inode(parent.dentry),
- dentry, NULL);
- if (!err || err == -ENOENT)
- deleted = 1;
- }
- } else {
- err = -ENOENT;
+ inode = d_inode(dentry);
+ if (dev_mynode(dev, inode)) {
+ struct iattr newattrs;
+ /*
+ * before unlinking this node, reset permissions
+ * of possible references like hardlinks
+ */
+ newattrs.ia_uid = GLOBAL_ROOT_UID;
+ newattrs.ia_gid = GLOBAL_ROOT_GID;
+ newattrs.ia_mode = inode->i_mode & ~0777;
+ newattrs.ia_valid =
+ ATTR_UID|ATTR_GID|ATTR_MODE;
+ inode_lock(d_inode(dentry));
+ notify_change(&nop_mnt_idmap, dentry, &newattrs, NULL);
+ inode_unlock(d_inode(dentry));
+ err = vfs_unlink(&nop_mnt_idmap, d_inode(parent.dentry),
+ dentry, NULL);
+ if (!err || err == -ENOENT)
+ deleted = 1;
}
- dput(dentry);
- inode_unlock(d_inode(parent.dentry));
+ end_removing_path(&parent, dentry);
- path_put(&parent);
if (deleted && strchr(nodename, '/'))
delete_path(nodename);
return err;
@@ -376,9 +370,9 @@ int __init devtmpfs_mount(void)
err = init_mount("devtmpfs", "dev", "devtmpfs", DEVTMPFS_MFLAGS, NULL);
if (err)
- printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
+ pr_info("error mounting %d\n", err);
else
- printk(KERN_INFO "devtmpfs: mounted\n");
+ pr_info("mounted\n");
return err;
}
@@ -450,6 +444,31 @@ static int __ref devtmpfsd(void *p)
}
/*
+ * Get the underlying (shmem/ramfs) context ops to build ours
+ */
+static int devtmpfs_configure_context(void)
+{
+ struct fs_context *fc;
+
+ fc = fs_context_for_reconfigure(mnt->mnt_root, mnt->mnt_sb->s_flags,
+ MS_RMT_MASK);
+ if (IS_ERR(fc))
+ return PTR_ERR(fc);
+
+ /* Set up devtmpfs_context_ops based on underlying type */
+ devtmpfs_context_ops.free = fc->ops->free;
+ devtmpfs_context_ops.dup = fc->ops->dup;
+ devtmpfs_context_ops.parse_param = fc->ops->parse_param;
+ devtmpfs_context_ops.parse_monolithic = fc->ops->parse_monolithic;
+ devtmpfs_context_ops.get_tree = &devtmpfs_get_tree;
+ devtmpfs_context_ops.reconfigure = fc->ops->reconfigure;
+
+ put_fs_context(fc);
+
+ return 0;
+}
+
+/*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
*/
@@ -460,14 +479,19 @@ int __init devtmpfs_init(void)
mnt = vfs_kern_mount(&internal_fs_type, 0, "devtmpfs", opts);
if (IS_ERR(mnt)) {
- printk(KERN_ERR "devtmpfs: unable to create devtmpfs %ld\n",
- PTR_ERR(mnt));
+ pr_err("unable to create devtmpfs %ld\n", PTR_ERR(mnt));
return PTR_ERR(mnt);
}
+
+ err = devtmpfs_configure_context();
+ if (err) {
+ pr_err("unable to configure devtmpfs type %d\n", err);
+ return err;
+ }
+
err = register_filesystem(&dev_fs_type);
if (err) {
- printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
- "type %i\n", err);
+ pr_err("unable to register devtmpfs type %d\n", err);
return err;
}
@@ -480,12 +504,12 @@ int __init devtmpfs_init(void)
}
if (err) {
- printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
+ pr_err("unable to create devtmpfs %d\n", err);
unregister_filesystem(&dev_fs_type);
thread = NULL;
return err;
}
- printk(KERN_INFO "devtmpfs: initialized\n");
+ pr_info("initialized\n");
return 0;
}
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 676b6275d5b5..8ab010ddf709 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -115,7 +115,7 @@ EXPORT_SYMBOL_GPL(driver_set_override);
* Iterate over the @drv's list of devices calling @fn for each one.
*/
int driver_for_each_device(struct device_driver *drv, struct device *start,
- void *data, int (*fn)(struct device *, void *))
+ void *data, device_iter_t fn)
{
struct klist_iter i;
struct device *dev;
@@ -148,9 +148,9 @@ EXPORT_SYMBOL_GPL(driver_for_each_device);
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
-struct device *driver_find_device(struct device_driver *drv,
+struct device *driver_find_device(const struct device_driver *drv,
struct device *start, const void *data,
- int (*match)(struct device *dev, const void *data))
+ device_match_t match)
{
struct klist_iter i;
struct device *dev;
@@ -160,9 +160,12 @@ struct device *driver_find_device(struct device_driver *drv,
klist_iter_init_node(&drv->p->klist_devices, &i,
(start ? &start->p->knode_driver : NULL));
- while ((dev = next_device(&i)))
- if (match(dev, data) && get_device(dev))
+ while ((dev = next_device(&i))) {
+ if (match(dev, data)) {
+ get_device(dev);
break;
+ }
+ }
klist_iter_exit(&i);
return dev;
}
@@ -173,7 +176,7 @@ EXPORT_SYMBOL_GPL(driver_find_device);
* @drv: driver.
* @attr: driver attribute descriptor.
*/
-int driver_create_file(struct device_driver *drv,
+int driver_create_file(const struct device_driver *drv,
const struct driver_attribute *attr)
{
int error;
@@ -191,7 +194,7 @@ EXPORT_SYMBOL_GPL(driver_create_file);
* @drv: driver.
* @attr: driver attribute descriptor.
*/
-void driver_remove_file(struct device_driver *drv,
+void driver_remove_file(const struct device_driver *drv,
const struct driver_attribute *attr)
{
if (drv)
@@ -199,13 +202,13 @@ void driver_remove_file(struct device_driver *drv,
}
EXPORT_SYMBOL_GPL(driver_remove_file);
-int driver_add_groups(struct device_driver *drv,
+int driver_add_groups(const struct device_driver *drv,
const struct attribute_group **groups)
{
return sysfs_create_groups(&drv->p->kobj, groups);
}
-void driver_remove_groups(struct device_driver *drv,
+void driver_remove_groups(const struct device_driver *drv,
const struct attribute_group **groups)
{
sysfs_remove_groups(&drv->p->kobj, groups);
@@ -224,7 +227,7 @@ int driver_register(struct device_driver *drv)
int ret;
struct device_driver *other;
- if (!drv->bus->p) {
+ if (!bus_is_registered(drv->bus)) {
pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
drv->name, drv->bus->name);
return -EINVAL;
@@ -274,30 +277,3 @@ void driver_unregister(struct device_driver *drv)
bus_remove_driver(drv);
}
EXPORT_SYMBOL_GPL(driver_unregister);
-
-/**
- * driver_find - locate driver on a bus by its name.
- * @name: name of the driver.
- * @bus: bus to scan for the driver.
- *
- * Call kset_find_obj() to iterate over list of drivers on
- * a bus to find driver by name. Return driver if found.
- *
- * This routine provides no locking to prevent the driver it returns
- * from being unregistered or unloaded while the caller is using it.
- * The caller is responsible for preventing this.
- */
-struct device_driver *driver_find(const char *name, struct bus_type *bus)
-{
- struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
- struct driver_private *priv;
-
- if (k) {
- /* Drop reference added by kset_find_obj() */
- kobject_put(k);
- priv = to_driver(k);
- return priv->driver;
- }
- return NULL;
-}
-EXPORT_SYMBOL_GPL(driver_find);
diff --git a/drivers/base/faux.c b/drivers/base/faux.c
new file mode 100644
index 000000000000..21dd02124231
--- /dev/null
+++ b/drivers/base/faux.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (c) 2025 The Linux Foundation
+ *
+ * A "simple" faux bus that allows devices to be created and added
+ * automatically to it. This is to be used whenever you need to create a
+ * device that is not associated with any "real" system resources, and do
+ * not want to have to deal with a bus/driver binding logic. It is
+ * intended to be very simple, with only a create and a destroy function
+ * available.
+ */
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/container_of.h>
+#include <linux/device/faux.h>
+#include "base.h"
+
+/*
+ * Internal wrapper structure so we can hold a pointer to the
+ * faux_device_ops for this device.
+ */
+struct faux_object {
+ struct faux_device faux_dev;
+ const struct faux_device_ops *faux_ops;
+ const struct attribute_group **groups;
+};
+#define to_faux_object(dev) container_of_const(dev, struct faux_object, faux_dev.dev)
+
+static struct device faux_bus_root = {
+ .init_name = "faux",
+};
+
+static int faux_match(struct device *dev, const struct device_driver *drv)
+{
+ /* Match always succeeds, we only have one driver */
+ return 1;
+}
+
+static int faux_probe(struct device *dev)
+{
+ struct faux_object *faux_obj = to_faux_object(dev);
+ struct faux_device *faux_dev = &faux_obj->faux_dev;
+ const struct faux_device_ops *faux_ops = faux_obj->faux_ops;
+ int ret;
+
+ if (faux_ops && faux_ops->probe) {
+ ret = faux_ops->probe(faux_dev);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Add groups after the probe succeeds to ensure resources are
+ * initialized correctly
+ */
+ ret = device_add_groups(dev, faux_obj->groups);
+ if (ret && faux_ops && faux_ops->remove)
+ faux_ops->remove(faux_dev);
+
+ return ret;
+}
+
+static void faux_remove(struct device *dev)
+{
+ struct faux_object *faux_obj = to_faux_object(dev);
+ struct faux_device *faux_dev = &faux_obj->faux_dev;
+ const struct faux_device_ops *faux_ops = faux_obj->faux_ops;
+
+ device_remove_groups(dev, faux_obj->groups);
+
+ if (faux_ops && faux_ops->remove)
+ faux_ops->remove(faux_dev);
+}
+
+static const struct bus_type faux_bus_type = {
+ .name = "faux",
+ .match = faux_match,
+ .probe = faux_probe,
+ .remove = faux_remove,
+};
+
+static struct device_driver faux_driver = {
+ .name = "faux_driver",
+ .bus = &faux_bus_type,
+ .probe_type = PROBE_FORCE_SYNCHRONOUS,
+ .suppress_bind_attrs = true,
+};
+
+static void faux_device_release(struct device *dev)
+{
+ struct faux_object *faux_obj = to_faux_object(dev);
+
+ kfree(faux_obj);
+}
+
+/**
+ * faux_device_create_with_groups - Create and register with the driver
+ * core a faux device and populate the device with an initial
+ * set of sysfs attributes.
+ * @name: The name of the device we are adding, must be unique for
+ * all faux devices.
+ * @parent: Pointer to a potential parent struct device. If set to
+ * NULL, the device will be created in the "root" of the faux
+ * device tree in sysfs.
+ * @faux_ops: struct faux_device_ops that the new device will call back
+ * into, can be NULL.
+ * @groups: The set of sysfs attributes that will be created for this
+ * device when it is registered with the driver core.
+ *
+ * Create a new faux device and register it in the driver core properly.
+ * If present, callbacks in @faux_ops will be called with the device that
+ * for the caller to do something with at the proper time given the
+ * device's lifecycle.
+ *
+ * Note, when this function is called, the functions specified in struct
+ * faux_ops can be called before the function returns, so be prepared for
+ * everything to be properly initialized before that point in time. If the
+ * probe callback (if one is present) does NOT succeed, the creation of the
+ * device will fail and NULL will be returned.
+ *
+ * Return:
+ * * NULL if an error happened with creating the device
+ * * pointer to a valid struct faux_device that is registered with sysfs
+ */
+struct faux_device *faux_device_create_with_groups(const char *name,
+ struct device *parent,
+ const struct faux_device_ops *faux_ops,
+ const struct attribute_group **groups)
+{
+ struct faux_object *faux_obj;
+ struct faux_device *faux_dev;
+ struct device *dev;
+ int ret;
+
+ faux_obj = kzalloc(sizeof(*faux_obj), GFP_KERNEL);
+ if (!faux_obj)
+ return NULL;
+
+ /* Save off the callbacks and groups so we can use them in the future */
+ faux_obj->faux_ops = faux_ops;
+ faux_obj->groups = groups;
+
+ /* Initialize the device portion and register it with the driver core */
+ faux_dev = &faux_obj->faux_dev;
+ dev = &faux_dev->dev;
+
+ device_initialize(dev);
+ dev->release = faux_device_release;
+ if (parent)
+ dev->parent = parent;
+ else
+ dev->parent = &faux_bus_root;
+ dev->bus = &faux_bus_type;
+ dev_set_name(dev, "%s", name);
+ device_set_pm_not_required(dev);
+
+ ret = device_add(dev);
+ if (ret) {
+ pr_err("%s: device_add for faux device '%s' failed with %d\n",
+ __func__, name, ret);
+ put_device(dev);
+ return NULL;
+ }
+
+ /*
+ * Verify that we did bind the driver to the device (i.e. probe worked),
+ * if not, let's fail the creation as trying to guess if probe was
+ * successful is almost impossible to determine by the caller.
+ */
+ if (!dev->driver) {
+ dev_dbg(dev, "probe did not succeed, tearing down the device\n");
+ faux_device_destroy(faux_dev);
+ faux_dev = NULL;
+ }
+
+ return faux_dev;
+}
+EXPORT_SYMBOL_GPL(faux_device_create_with_groups);
+
+/**
+ * faux_device_create - create and register with the driver core a faux device
+ * @name: The name of the device we are adding, must be unique for all
+ * faux devices.
+ * @parent: Pointer to a potential parent struct device. If set to
+ * NULL, the device will be created in the "root" of the faux
+ * device tree in sysfs.
+ * @faux_ops: struct faux_device_ops that the new device will call back
+ * into, can be NULL.
+ *
+ * Create a new faux device and register it in the driver core properly.
+ * If present, callbacks in @faux_ops will be called with the device that
+ * for the caller to do something with at the proper time given the
+ * device's lifecycle.
+ *
+ * Note, when this function is called, the functions specified in struct
+ * faux_ops can be called before the function returns, so be prepared for
+ * everything to be properly initialized before that point in time.
+ *
+ * Return:
+ * * NULL if an error happened with creating the device
+ * * pointer to a valid struct faux_device that is registered with sysfs
+ */
+struct faux_device *faux_device_create(const char *name,
+ struct device *parent,
+ const struct faux_device_ops *faux_ops)
+{
+ return faux_device_create_with_groups(name, parent, faux_ops, NULL);
+}
+EXPORT_SYMBOL_GPL(faux_device_create);
+
+/**
+ * faux_device_destroy - destroy a faux device
+ * @faux_dev: faux device to destroy
+ *
+ * Unregisters and cleans up a device that was created with a call to
+ * faux_device_create()
+ */
+void faux_device_destroy(struct faux_device *faux_dev)
+{
+ struct device *dev = &faux_dev->dev;
+
+ if (!faux_dev)
+ return;
+
+ device_del(dev);
+
+ /* The final put_device() will clean up the memory we allocated for this device. */
+ put_device(dev);
+}
+EXPORT_SYMBOL_GPL(faux_device_destroy);
+
+int __init faux_bus_init(void)
+{
+ int ret;
+
+ ret = device_register(&faux_bus_root);
+ if (ret) {
+ put_device(&faux_bus_root);
+ return ret;
+ }
+
+ ret = bus_register(&faux_bus_type);
+ if (ret)
+ goto error_bus;
+
+ ret = driver_register(&faux_driver);
+ if (ret)
+ goto error_driver;
+
+ return ret;
+
+error_driver:
+ bus_unregister(&faux_bus_type);
+
+error_bus:
+ device_unregister(&faux_bus_root);
+ return ret;
+}
diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
index 5166b323a0f8..15eff8a4b505 100644
--- a/drivers/base/firmware_loader/Kconfig
+++ b/drivers/base/firmware_loader/Kconfig
@@ -3,6 +3,7 @@ menu "Firmware loader"
config FW_LOADER
tristate "Firmware loading facility" if EXPERT
+ select CRYPTO_LIB_SHA256 if FW_LOADER_DEBUG
default y
help
This enables the firmware loading facility in the kernel. The kernel
@@ -24,6 +25,23 @@ config FW_LOADER
You also want to be sure to enable this built-in if you are going to
enable built-in firmware (CONFIG_EXTRA_FIRMWARE).
+config FW_LOADER_DEBUG
+ bool "Log filenames and checksums for loaded firmware"
+ depends on DYNAMIC_DEBUG
+ depends on FW_LOADER
+ default FW_LOADER
+ help
+ Select this option to use dynamic debug to log firmware filenames and
+ SHA256 checksums to the kernel log for each firmware file that is
+ loaded.
+
+config RUST_FW_LOADER_ABSTRACTIONS
+ bool "Rust Firmware Loader abstractions"
+ depends on RUST
+ select FW_LOADER
+ help
+ This enables the Rust abstractions for the firmware loader API.
+
if FW_LOADER
config FW_LOADER_PAGED_BUF
diff --git a/drivers/base/firmware_loader/builtin/main.c b/drivers/base/firmware_loader/builtin/main.c
index a065c3150897..d36befebb1b9 100644
--- a/drivers/base/firmware_loader/builtin/main.c
+++ b/drivers/base/firmware_loader/builtin/main.c
@@ -61,7 +61,7 @@ bool firmware_request_builtin(struct firmware *fw, const char *name)
return false;
}
-EXPORT_SYMBOL_NS_GPL(firmware_request_builtin, TEST_FIRMWARE);
+EXPORT_SYMBOL_NS_GPL(firmware_request_builtin, "TEST_FIRMWARE");
/**
* firmware_request_builtin_buf() - load builtin firmware into optional buffer
diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
index bf68e3947814..3ef0b312ae71 100644
--- a/drivers/base/firmware_loader/fallback.c
+++ b/drivers/base/firmware_loader/fallback.c
@@ -46,7 +46,7 @@ static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv, long timeout)
static LIST_HEAD(pending_fw_head);
-void kill_pending_fw_fallback_reqs(bool only_kill_custom)
+void kill_pending_fw_fallback_reqs(bool kill_all)
{
struct fw_priv *fw_priv;
struct fw_priv *next;
@@ -54,9 +54,13 @@ void kill_pending_fw_fallback_reqs(bool only_kill_custom)
mutex_lock(&fw_lock);
list_for_each_entry_safe(fw_priv, next, &pending_fw_head,
pending_list) {
- if (!fw_priv->need_uevent || !only_kill_custom)
+ if (kill_all || !fw_priv->need_uevent)
__fw_load_abort(fw_priv);
}
+
+ if (kill_all)
+ fw_load_abort_all = true;
+
mutex_unlock(&fw_lock);
}
@@ -86,7 +90,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
}
mutex_lock(&fw_lock);
- if (fw_state_is_aborted(fw_priv)) {
+ if (fw_load_abort_all || fw_state_is_aborted(fw_priv)) {
mutex_unlock(&fw_lock);
retval = -EINTR;
goto out;
diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h
index 144148595660..ccf912bef6ca 100644
--- a/drivers/base/firmware_loader/fallback.h
+++ b/drivers/base/firmware_loader/fallback.h
@@ -13,7 +13,7 @@ int firmware_fallback_sysfs(struct firmware *fw, const char *name,
struct device *device,
u32 opt_flags,
int ret);
-void kill_pending_fw_fallback_reqs(bool only_kill_custom);
+void kill_pending_fw_fallback_reqs(bool kill_all);
void fw_fallback_set_cache_timeout(void);
void fw_fallback_set_default_timeout(void);
@@ -28,7 +28,7 @@ static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name,
return ret;
}
-static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
+static inline void kill_pending_fw_fallback_reqs(bool kill_all) { }
static inline void fw_fallback_set_cache_timeout(void) { }
static inline void fw_fallback_set_default_timeout(void) { }
#endif /* CONFIG_FW_LOADER_USER_HELPER */
diff --git a/drivers/base/firmware_loader/fallback_table.c b/drivers/base/firmware_loader/fallback_table.c
index e5ac098d0742..c8afc501a8a4 100644
--- a/drivers/base/firmware_loader/fallback_table.c
+++ b/drivers/base/firmware_loader/fallback_table.c
@@ -22,10 +22,10 @@ struct firmware_fallback_config fw_fallback_config = {
.loading_timeout = 60,
.old_timeout = 60,
};
-EXPORT_SYMBOL_NS_GPL(fw_fallback_config, FIRMWARE_LOADER_PRIVATE);
+EXPORT_SYMBOL_NS_GPL(fw_fallback_config, "FIRMWARE_LOADER_PRIVATE");
#ifdef CONFIG_SYSCTL
-static struct ctl_table firmware_config_table[] = {
+static const struct ctl_table firmware_config_table[] = {
{
.procname = "force_sysfs_fallback",
.data = &fw_fallback_config.force_sysfs_fallback,
@@ -44,7 +44,6 @@ static struct ctl_table firmware_config_table[] = {
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
- { }
};
static struct ctl_table_header *firmware_config_sysct_table_header;
@@ -57,13 +56,13 @@ int register_firmware_config_sysctl(void)
return -ENOMEM;
return 0;
}
-EXPORT_SYMBOL_NS_GPL(register_firmware_config_sysctl, FIRMWARE_LOADER_PRIVATE);
+EXPORT_SYMBOL_NS_GPL(register_firmware_config_sysctl, "FIRMWARE_LOADER_PRIVATE");
void unregister_firmware_config_sysctl(void)
{
unregister_sysctl_table(firmware_config_sysct_table_header);
firmware_config_sysct_table_header = NULL;
}
-EXPORT_SYMBOL_NS_GPL(unregister_firmware_config_sysctl, FIRMWARE_LOADER_PRIVATE);
+EXPORT_SYMBOL_NS_GPL(unregister_firmware_config_sysctl, "FIRMWARE_LOADER_PRIVATE");
#endif /* CONFIG_SYSCTL */
diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h
index fe77e91c38a2..e891742ba264 100644
--- a/drivers/base/firmware_loader/firmware.h
+++ b/drivers/base/firmware_loader/firmware.h
@@ -9,8 +9,6 @@
#include <linux/list.h>
#include <linux/completion.h>
-#include <generated/utsrelease.h>
-
/**
* enum fw_opt - options to control firmware loading behaviour
*
@@ -88,6 +86,7 @@ struct fw_priv {
extern struct mutex fw_lock;
extern struct firmware_cache fw_cache;
+extern bool fw_load_abort_all;
static inline bool __fw_state_check(struct fw_priv *fw_priv,
enum fw_status status)
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index 7c3590fd97c2..4ebdca9e4da4 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -93,6 +93,7 @@ static inline struct fw_priv *to_fw_priv(struct kref *ref)
DEFINE_MUTEX(fw_lock);
struct firmware_cache fw_cache;
+bool fw_load_abort_all;
void fw_state_init(struct fw_priv *fw_priv)
{
@@ -493,9 +494,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
const void *in_buffer))
{
size_t size;
- int i, len;
+ int i, len, maxlen = 0;
int rc = -ENOENT;
- char *path;
+ char *path, *nt = NULL;
size_t msize = INT_MAX;
void *buffer = NULL;
@@ -518,8 +519,17 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
if (!fw_path[i][0])
continue;
- len = snprintf(path, PATH_MAX, "%s/%s%s",
- fw_path[i], fw_priv->fw_name, suffix);
+ /* strip off \n from customized path */
+ maxlen = strlen(fw_path[i]);
+ if (i == 0) {
+ nt = strchr(fw_path[i], '\n');
+ if (nt)
+ maxlen = nt - fw_path[i];
+ }
+
+ len = snprintf(path, PATH_MAX, "%.*s/%s%s",
+ maxlen, fw_path[i],
+ fw_priv->fw_name, suffix);
if (len >= PATH_MAX) {
rc = -ENAMETOOLONG;
break;
@@ -541,12 +551,16 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
file_size_ptr,
READING_FIRMWARE);
if (rc < 0) {
- if (rc != -ENOENT)
- dev_warn(device, "loading %s failed with error %d\n",
- path, rc);
- else
- dev_dbg(device, "loading %s failed for no such file or directory.\n",
- path);
+ if (!(fw_priv->opt_flags & FW_OPT_NO_WARN)) {
+ if (rc != -ENOENT)
+ dev_warn(device,
+ "loading %s failed with error %d\n",
+ path, rc);
+ else
+ dev_dbg(device,
+ "loading %s failed for no such file or directory.\n",
+ path);
+ }
continue;
}
size = rc;
@@ -791,6 +805,23 @@ static void fw_abort_batch_reqs(struct firmware *fw)
mutex_unlock(&fw_lock);
}
+#if defined(CONFIG_FW_LOADER_DEBUG)
+#include <crypto/sha2.h>
+
+static void fw_log_firmware_info(const struct firmware *fw, const char *name, struct device *device)
+{
+ u8 digest[SHA256_DIGEST_SIZE];
+
+ sha256(fw->data, fw->size, digest);
+ dev_dbg(device, "Loaded FW: %s, sha256: %*phN\n",
+ name, SHA256_DIGEST_SIZE, digest);
+}
+#else
+static void fw_log_firmware_info(const struct firmware *fw, const char *name,
+ struct device *device)
+{}
+#endif
+
/* called from request_firmware() and request_firmware_work_func() */
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
@@ -798,8 +829,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
size_t offset, u32 opt_flags)
{
struct firmware *fw = NULL;
- struct cred *kern_cred = NULL;
- const struct cred *old_cred;
bool nondirect = false;
int ret;
@@ -811,6 +840,25 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
goto out;
}
+
+ /*
+ * Reject firmware file names with ".." path components.
+ * There are drivers that construct firmware file names from
+ * device-supplied strings, and we don't want some device to be
+ * able to tell us "I would like to be sent my firmware from
+ * ../../../etc/shadow, please".
+ *
+ * This intentionally only looks at the firmware name, not at
+ * the firmware base directory or at symlink contents.
+ */
+ if (name_contains_dotdot(name)) {
+ dev_warn(device,
+ "Firmware load for '%s' refused, path contains '..' component\n",
+ name);
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = _request_firmware_prepare(&fw, name, device, buf, size,
offset, opt_flags);
if (ret <= 0) /* error or already assigned */
@@ -821,51 +869,46 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
* called by a driver when serving an unrelated request from userland, we use
* the kernel credentials to read the file.
*/
- kern_cred = prepare_kernel_cred(NULL);
- if (!kern_cred) {
- ret = -ENOMEM;
- goto out;
- }
- old_cred = override_creds(kern_cred);
+ scoped_with_kernel_creds() {
+ ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
- ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
-
- /* Only full reads can support decompression, platform, and sysfs. */
- if (!(opt_flags & FW_OPT_PARTIAL))
- nondirect = true;
+ /* Only full reads can support decompression, platform, and sysfs. */
+ if (!(opt_flags & FW_OPT_PARTIAL))
+ nondirect = true;
#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
- if (ret == -ENOENT && nondirect)
- ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
- fw_decompress_zstd);
+ if (ret == -ENOENT && nondirect)
+ ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
+ fw_decompress_zstd);
#endif
#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
- if (ret == -ENOENT && nondirect)
- ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
- fw_decompress_xz);
+ if (ret == -ENOENT && nondirect)
+ ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
+ fw_decompress_xz);
#endif
- if (ret == -ENOENT && nondirect)
- ret = firmware_fallback_platform(fw->priv);
+ if (ret == -ENOENT && nondirect)
+ ret = firmware_fallback_platform(fw->priv);
- if (ret) {
- if (!(opt_flags & FW_OPT_NO_WARN))
- dev_warn(device,
- "Direct firmware load for %s failed with error %d\n",
- name, ret);
- if (nondirect)
- ret = firmware_fallback_sysfs(fw, name, device,
- opt_flags, ret);
- } else
- ret = assign_fw(fw, device);
-
- revert_creds(old_cred);
- put_cred(kern_cred);
+ if (ret) {
+ if (!(opt_flags & FW_OPT_NO_WARN))
+ dev_warn(device,
+ "Direct firmware load for %s failed with error %d\n",
+ name, ret);
+ if (nondirect)
+ ret = firmware_fallback_sysfs(fw, name, device,
+ opt_flags, ret);
+ } else {
+ ret = assign_fw(fw, device);
+ }
+ }
- out:
+out:
if (ret < 0) {
fw_abort_batch_reqs(fw);
release_firmware(fw);
fw = NULL;
+ } else {
+ fw_log_firmware_info(fw, name, device);
}
*firmware_p = fw;
@@ -886,6 +929,8 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
* @name will be used as $FIRMWARE in the uevent environment and
* should be distinctive enough not to be confused with any other
* firmware image for this or any other device.
+ * It must not contain any ".." path components - "foo/bar..bin" is
+ * allowed, but "foo/../bar.bin" is not.
*
* Caller must hold the reference count of @device.
*
@@ -985,8 +1030,8 @@ EXPORT_SYMBOL_GPL(firmware_request_platform);
/**
* firmware_request_cache() - cache firmware for suspend so resume can use it
- * @name: name of firmware file
* @device: device for which firmware should be cached for
+ * @name: name of firmware file
*
* There are some devices with an optimization that enables the device to not
* require loading firmware on system reboot. This optimization may still
@@ -1112,34 +1157,11 @@ static void request_firmware_work_func(struct work_struct *work)
kfree(fw_work);
}
-/**
- * request_firmware_nowait() - asynchronous version of request_firmware
- * @module: module requesting the firmware
- * @uevent: sends uevent to copy the firmware image if this flag
- * is non-zero else the firmware copy must be done manually.
- * @name: name of firmware file
- * @device: device for which firmware is being loaded
- * @gfp: allocation flags
- * @context: will be passed over to @cont, and
- * @fw may be %NULL if firmware request fails.
- * @cont: function will be called asynchronously when the firmware
- * request is over.
- *
- * Caller must hold the reference count of @device.
- *
- * Asynchronous variant of request_firmware() for user contexts:
- * - sleep for as small periods as possible since it may
- * increase kernel boot time of built-in device drivers
- * requesting firmware in their ->probe() methods, if
- * @gfp is GFP_KERNEL.
- *
- * - can't sleep at all if @gfp is GFP_ATOMIC.
- **/
-int
-request_firmware_nowait(
+
+static int _request_firmware_nowait(
struct module *module, bool uevent,
const char *name, struct device *device, gfp_t gfp, void *context,
- void (*cont)(const struct firmware *fw, void *context))
+ void (*cont)(const struct firmware *fw, void *context), bool nowarn)
{
struct firmware_work *fw_work;
@@ -1157,7 +1179,8 @@ request_firmware_nowait(
fw_work->context = context;
fw_work->cont = cont;
fw_work->opt_flags = FW_OPT_NOWAIT |
- (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
+ (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER) |
+ (nowarn ? FW_OPT_NO_WARN : 0);
if (!uevent && fw_cache_is_setup(device, name)) {
kfree_const(fw_work->name);
@@ -1176,8 +1199,66 @@ request_firmware_nowait(
schedule_work(&fw_work->work);
return 0;
}
+
+/**
+ * request_firmware_nowait() - asynchronous version of request_firmware
+ * @module: module requesting the firmware
+ * @uevent: sends uevent to copy the firmware image if this flag
+ * is non-zero else the firmware copy must be done manually.
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ * @gfp: allocation flags
+ * @context: will be passed over to @cont, and
+ * @fw may be %NULL if firmware request fails.
+ * @cont: function will be called asynchronously when the firmware
+ * request is over.
+ *
+ * Caller must hold the reference count of @device.
+ *
+ * Asynchronous variant of request_firmware() for user contexts:
+ * - sleep for as small periods as possible since it may
+ * increase kernel boot time of built-in device drivers
+ * requesting firmware in their ->probe() methods, if
+ * @gfp is GFP_KERNEL.
+ *
+ * - can't sleep at all if @gfp is GFP_ATOMIC.
+ **/
+int request_firmware_nowait(
+ struct module *module, bool uevent,
+ const char *name, struct device *device, gfp_t gfp, void *context,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ return _request_firmware_nowait(module, uevent, name, device, gfp,
+ context, cont, false);
+
+}
EXPORT_SYMBOL(request_firmware_nowait);
+/**
+ * firmware_request_nowait_nowarn() - async version of request_firmware_nowarn
+ * @module: module requesting the firmware
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ * @gfp: allocation flags
+ * @context: will be passed over to @cont, and
+ * @fw may be %NULL if firmware request fails.
+ * @cont: function will be called asynchronously when the firmware
+ * request is over.
+ *
+ * Similar in function to request_firmware_nowait(), but doesn't print a warning
+ * when the firmware file could not be found and always sends a uevent to copy
+ * the firmware image.
+ */
+int firmware_request_nowait_nowarn(
+ struct module *module, const char *name,
+ struct device *device, gfp_t gfp, void *context,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ return _request_firmware_nowait(module, FW_ACTION_UEVENT, name, device,
+ gfp, context, cont, true);
+}
+EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn);
+
#ifdef CONFIG_FW_CACHE
static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
@@ -1469,10 +1550,10 @@ static int fw_pm_notify(struct notifier_block *notify_block,
case PM_SUSPEND_PREPARE:
case PM_RESTORE_PREPARE:
/*
- * kill pending fallback requests with a custom fallback
- * to avoid stalling suspend.
+ * Here, kill pending fallback requests will only kill
+ * non-uevent firmware request to avoid stalling suspend.
*/
- kill_pending_fw_fallback_reqs(true);
+ kill_pending_fw_fallback_reqs(false);
device_cache_fw_images();
break;
@@ -1495,16 +1576,20 @@ static int fw_pm_notify(struct notifier_block *notify_block,
}
/* stop caching firmware once syscore_suspend is reached */
-static int fw_suspend(void)
+static int fw_suspend(void *data)
{
fw_cache.state = FW_LOADER_NO_CACHE;
return 0;
}
-static struct syscore_ops fw_syscore_ops = {
+static const struct syscore_ops fw_syscore_ops = {
.suspend = fw_suspend,
};
+static struct syscore fw_syscore = {
+ .ops = &fw_syscore_ops,
+};
+
static int __init register_fw_pm_ops(void)
{
int ret;
@@ -1520,14 +1605,14 @@ static int __init register_fw_pm_ops(void)
if (ret)
return ret;
- register_syscore_ops(&fw_syscore_ops);
+ register_syscore(&fw_syscore);
return ret;
}
static inline void unregister_fw_pm_ops(void)
{
- unregister_syscore_ops(&fw_syscore_ops);
+ unregister_syscore(&fw_syscore);
unregister_pm_notifier(&fw_cache.pm_notify);
}
#else
@@ -1557,7 +1642,7 @@ static int fw_shutdown_notify(struct notifier_block *unused1,
* Kill all pending fallback requests to avoid both stalling shutdown,
* and avoid a deadlock with the usermode_lock.
*/
- kill_pending_fw_fallback_reqs(false);
+ kill_pending_fw_fallback_reqs(true);
return NOTIFY_DONE;
}
diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c
index 5b66b3d1fa16..92e91050f96a 100644
--- a/drivers/base/firmware_loader/sysfs.c
+++ b/drivers/base/firmware_loader/sysfs.c
@@ -25,7 +25,7 @@ void __fw_load_abort(struct fw_priv *fw_priv)
}
#ifdef CONFIG_FW_LOADER_USER_HELPER
-static ssize_t timeout_show(struct class *class, struct class_attribute *attr,
+static ssize_t timeout_show(const struct class *class, const struct class_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", __firmware_loading_timeout());
@@ -44,10 +44,13 @@ static ssize_t timeout_show(struct class *class, struct class_attribute *attr,
*
* Note: zero means 'wait forever'.
**/
-static ssize_t timeout_store(struct class *class, struct class_attribute *attr,
+static ssize_t timeout_store(const struct class *class, const struct class_attribute *attr,
const char *buf, size_t count)
{
- int tmp_loading_timeout = simple_strtol(buf, NULL, 10);
+ int tmp_loading_timeout;
+
+ if (kstrtoint(buf, 10, &tmp_loading_timeout))
+ return -EINVAL;
if (tmp_loading_timeout < 0)
tmp_loading_timeout = 0;
@@ -64,7 +67,7 @@ static struct attribute *firmware_class_attrs[] = {
};
ATTRIBUTE_GROUPS(firmware_class);
-static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
+static int do_firmware_uevent(const struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
{
if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
return -ENOMEM;
@@ -76,9 +79,9 @@ static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env
return 0;
}
-static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int firmware_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ const struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
int err = 0;
mutex_lock(&fw_lock);
@@ -157,7 +160,10 @@ static ssize_t firmware_loading_store(struct device *dev,
struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
struct fw_priv *fw_priv;
ssize_t written = count;
- int loading = simple_strtol(buf, NULL, 10);
+ int loading;
+
+ if (kstrtoint(buf, 10, &loading))
+ return -EINVAL;
mutex_lock(&fw_lock);
fw_priv = fw_sysfs->fw_priv;
@@ -259,7 +265,7 @@ static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
}
static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -316,7 +322,7 @@ static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
* the driver as a firmware image.
**/
static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -356,7 +362,7 @@ out:
return retval;
}
-static struct bin_attribute firmware_attr_data = {
+static const struct bin_attribute firmware_attr_data = {
.attr = { .name = "data", .mode = 0644 },
.size = 0,
.read = firmware_data_read,
@@ -374,7 +380,7 @@ static struct attribute *fw_dev_attrs[] = {
NULL
};
-static struct bin_attribute *fw_dev_bin_attrs[] = {
+static const struct bin_attribute *const fw_dev_bin_attrs[] = {
&firmware_attr_data,
NULL
};
diff --git a/drivers/base/firmware_loader/sysfs.h b/drivers/base/firmware_loader/sysfs.h
index df1d5add698f..1cabea544a40 100644
--- a/drivers/base/firmware_loader/sysfs.h
+++ b/drivers/base/firmware_loader/sysfs.h
@@ -6,7 +6,7 @@
#include "firmware.h"
-MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE);
+MODULE_IMPORT_NS("FIRMWARE_LOADER_PRIVATE");
extern struct firmware_fallback_config fw_fallback_config;
extern struct device_attribute dev_attr_loading;
@@ -80,11 +80,7 @@ struct fw_sysfs {
struct firmware *fw;
void *fw_upload_priv;
};
-
-static inline struct fw_sysfs *to_fw_sysfs(struct device *dev)
-{
- return container_of(dev, struct fw_sysfs, dev);
-}
+#define to_fw_sysfs(__dev) container_of_const(__dev, struct fw_sysfs, dev)
void __fw_load_abort(struct fw_priv *fw_priv);
diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c
index a0af8f5f13d8..c3797b93c5f5 100644
--- a/drivers/base/firmware_loader/sysfs_upload.c
+++ b/drivers/base/firmware_loader/sysfs_upload.c
@@ -27,6 +27,7 @@ static const char * const fw_upload_err_str[] = {
[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
[FW_UPLOAD_ERR_RW_ERROR] = "read-write-error",
[FW_UPLOAD_ERR_WEAROUT] = "flash-wearout",
+ [FW_UPLOAD_ERR_FW_INVALID] = "firmware-invalid",
};
static const char *fw_upload_progress(struct device *dev,
@@ -99,8 +100,10 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
mutex_lock(&fwlp->lock);
- if (fwlp->progress == FW_UPLOAD_PROG_IDLE)
- ret = -ENODEV;
+ if (fwlp->progress == FW_UPLOAD_PROG_IDLE) {
+ mutex_unlock(&fwlp->lock);
+ return -ENODEV;
+ }
fwlp->ops->cancel(fwlp->fw_upload);
mutex_unlock(&fwlp->lock);
diff --git a/drivers/base/init.c b/drivers/base/init.c
index 397eb9880cec..9d2b06d65dfc 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -32,11 +32,12 @@ void __init driver_init(void)
/* These are also core pieces, but must come after the
* core core pieces.
*/
+ faux_bus_init();
of_core_init();
platform_bus_init();
auxiliary_bus_init();
- cpu_dev_init();
memory_dev_init();
node_dev_init();
+ cpu_dev_init();
container_dev_init();
}
diff --git a/drivers/base/isa.c b/drivers/base/isa.c
index 55e3ee2da98f..bfd9215c9070 100644
--- a/drivers/base/isa.c
+++ b/drivers/base/isa.c
@@ -23,7 +23,7 @@ struct isa_dev {
#define to_isa_dev(x) container_of((x), struct isa_dev, dev)
-static int isa_bus_match(struct device *dev, struct device_driver *driver)
+static int isa_bus_match(struct device *dev, const struct device_driver *driver)
{
struct isa_driver *isa_driver = to_isa_driver(driver);
@@ -82,7 +82,7 @@ static int isa_bus_resume(struct device *dev)
return 0;
}
-static struct bus_type isa_bus_type = {
+static const struct bus_type isa_bus_type = {
.name = "isa",
.match = isa_bus_match,
.probe = isa_bus_probe,
@@ -149,11 +149,8 @@ int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev)
break;
}
- if (isa_dev->dev.platform_data) {
- isa_dev->next = isa_driver->devices;
- isa_driver->devices = &isa_dev->dev;
- } else
- device_unregister(&isa_dev->dev);
+ isa_dev->next = isa_driver->devices;
+ isa_driver->devices = &isa_dev->dev;
}
if (!error && !isa_driver->devices)
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index bc60c9cd3230..751f248ca4a8 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -22,6 +22,7 @@
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/xarray.h>
+#include <linux/export.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
@@ -48,27 +49,13 @@ int mhp_online_type_from_str(const char *str)
#define to_memory_block(dev) container_of(dev, struct memory_block, dev)
-static int sections_per_block;
-
-static inline unsigned long memory_block_id(unsigned long section_nr)
-{
- return section_nr / sections_per_block;
-}
-
-static inline unsigned long pfn_to_block_id(unsigned long pfn)
-{
- return memory_block_id(pfn_to_section_nr(pfn));
-}
-
-static inline unsigned long phys_to_block_id(unsigned long phys)
-{
- return pfn_to_block_id(PFN_DOWN(phys));
-}
+int sections_per_block;
+EXPORT_SYMBOL(sections_per_block);
static int memory_subsys_online(struct device *dev);
static int memory_subsys_offline(struct device *dev);
-static struct bus_type memory_subsys = {
+static const struct bus_type memory_subsys = {
.name = MEMORY_CLASS_NAME,
.dev_name = MEMORY_CLASS_NAME,
.online = memory_subsys_online,
@@ -105,28 +92,75 @@ EXPORT_SYMBOL(unregister_memory_notifier);
static void memory_block_release(struct device *dev)
{
struct memory_block *mem = to_memory_block(dev);
-
+ /* Verify that the altmap is freed */
+ WARN_ON(mem->altmap);
kfree(mem);
}
+
+/* Max block size to be set by memory_block_advise_max_size */
+static unsigned long memory_block_advised_size;
+static bool memory_block_advised_size_queried;
+
+/**
+ * memory_block_advise_max_size() - advise memory hotplug on the max suggested
+ * block size, usually for alignment.
+ * @size: suggestion for maximum block size. must be aligned on power of 2.
+ *
+ * Early boot software (pre-allocator init) may advise archs on the max block
+ * size. This value can only decrease after initialization, as the intent is
+ * to identify the largest supported alignment for all sources.
+ *
+ * Use of this value is arch-defined, as is min/max block size.
+ *
+ * Return: 0 on success
+ * -EINVAL if size is 0 or not pow2 aligned
+ * -EBUSY if value has already been probed
+ */
+int __init memory_block_advise_max_size(unsigned long size)
+{
+ if (!size || !is_power_of_2(size))
+ return -EINVAL;
+
+ if (memory_block_advised_size_queried)
+ return -EBUSY;
+
+ if (memory_block_advised_size)
+ memory_block_advised_size = min(memory_block_advised_size, size);
+ else
+ memory_block_advised_size = size;
+
+ return 0;
+}
+
+/**
+ * memory_block_advised_max_size() - query advised max hotplug block size.
+ *
+ * After the first call, the value can never change. Callers looking for the
+ * actual block size should use memory_block_size_bytes. This interface is
+ * intended for use by arch-init when initializing the hotplug block size.
+ *
+ * Return: advised size in bytes, or 0 if never set.
+ */
+unsigned long memory_block_advised_max_size(void)
+{
+ memory_block_advised_size_queried = true;
+ return memory_block_advised_size;
+}
+
unsigned long __weak memory_block_size_bytes(void)
{
return MIN_MEMORY_BLOCK_SIZE;
}
EXPORT_SYMBOL_GPL(memory_block_size_bytes);
-/*
- * Show the first physical section index (number) of this memory block.
- */
+/* Show the memory block ID, relative to the memory block size */
static ssize_t phys_index_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct memory_block *mem = to_memory_block(dev);
- unsigned long phys_index;
- phys_index = mem->start_section_nr / sections_per_block;
-
- return sysfs_emit(buf, "%08lx\n", phys_index);
+ return sysfs_emit(buf, "%08lx\n", memory_block_id(mem->start_section_nr));
}
/*
@@ -164,25 +198,40 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
break;
default:
WARN_ON(1);
- return sysfs_emit(buf, "ERROR-UNKNOWN-%ld\n", mem->state);
+ return sysfs_emit(buf, "ERROR-UNKNOWN-%d\n", mem->state);
}
return sysfs_emit(buf, "%s\n", output);
}
-int memory_notify(unsigned long val, void *v)
+int memory_notify(enum memory_block_state state, void *v)
{
- return blocking_notifier_call_chain(&memory_chain, val, v);
+ return blocking_notifier_call_chain(&memory_chain, state, v);
}
+#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG)
+static unsigned long memblk_nr_poison(struct memory_block *mem);
+#else
+static inline unsigned long memblk_nr_poison(struct memory_block *mem)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Must acquire mem_hotplug_lock in write mode.
+ */
static int memory_block_online(struct memory_block *mem)
{
unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
- unsigned long nr_vmemmap_pages = mem->nr_vmemmap_pages;
+ unsigned long nr_vmemmap_pages = 0;
struct zone *zone;
int ret;
+ if (memblk_nr_poison(mem))
+ return -EHWPOISON;
+
zone = zone_for_pfn_range(mem->online_type, mem->nid, mem->group,
start_pfn, nr_pages);
@@ -193,10 +242,14 @@ static int memory_block_online(struct memory_block *mem)
* stage helps to keep accounting easier to follow - e.g vmemmaps
* belong to the same zone as the memory they backed.
*/
+ if (mem->altmap)
+ nr_vmemmap_pages = mem->altmap->free;
+
+ mem_hotplug_begin();
if (nr_vmemmap_pages) {
ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone);
if (ret)
- return ret;
+ goto out;
}
ret = online_pages(start_pfn + nr_vmemmap_pages,
@@ -204,7 +257,7 @@ static int memory_block_online(struct memory_block *mem)
if (ret) {
if (nr_vmemmap_pages)
mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages);
- return ret;
+ goto out;
}
/*
@@ -216,14 +269,19 @@ static int memory_block_online(struct memory_block *mem)
nr_vmemmap_pages);
mem->zone = zone;
+out:
+ mem_hotplug_done();
return ret;
}
+/*
+ * Must acquire mem_hotplug_lock in write mode.
+ */
static int memory_block_offline(struct memory_block *mem)
{
unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
- unsigned long nr_vmemmap_pages = mem->nr_vmemmap_pages;
+ unsigned long nr_vmemmap_pages = 0;
int ret;
if (!mem->zone)
@@ -233,6 +291,10 @@ static int memory_block_offline(struct memory_block *mem)
* Unaccount before offlining, such that unpopulated zone and kthreads
* can properly be torn down in offline_pages().
*/
+ if (mem->altmap)
+ nr_vmemmap_pages = mem->altmap->free;
+
+ mem_hotplug_begin();
if (nr_vmemmap_pages)
adjust_present_page_count(pfn_to_page(start_pfn), mem->group,
-nr_vmemmap_pages);
@@ -244,13 +306,15 @@ static int memory_block_offline(struct memory_block *mem)
if (nr_vmemmap_pages)
adjust_present_page_count(pfn_to_page(start_pfn),
mem->group, nr_vmemmap_pages);
- return ret;
+ goto out;
}
if (nr_vmemmap_pages)
mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages);
mem->zone = NULL;
+out:
+ mem_hotplug_done();
return ret;
}
@@ -408,7 +472,7 @@ static ssize_t valid_zones_show(struct device *dev,
struct memory_group *group = mem->group;
struct zone *default_zone;
int nid = mem->nid;
- int len = 0;
+ int len;
/*
* Check the existing zone. Make sure that we do that only on the
@@ -419,22 +483,18 @@ static ssize_t valid_zones_show(struct device *dev,
* If !mem->zone, the memory block spans multiple zones and
* cannot get offlined.
*/
- default_zone = mem->zone;
- if (!default_zone)
- return sysfs_emit(buf, "%s\n", "none");
- len += sysfs_emit_at(buf, len, "%s", default_zone->name);
- goto out;
+ return sysfs_emit(buf, "%s\n",
+ mem->zone ? mem->zone->name : "none");
}
default_zone = zone_for_pfn_range(MMOP_ONLINE, nid, group,
start_pfn, nr_pages);
- len += sysfs_emit_at(buf, len, "%s", default_zone->name);
+ len = sysfs_emit(buf, "%s", default_zone->name);
len += print_allowed_zone(buf, len, nid, group, start_pfn, nr_pages,
MMOP_ONLINE_KERNEL, default_zone);
len += print_allowed_zone(buf, len, nid, group, start_pfn, nr_pages,
MMOP_ONLINE_MOVABLE, default_zone);
-out:
len += sysfs_emit_at(buf, len, "\n");
return len;
}
@@ -465,7 +525,7 @@ static ssize_t auto_online_blocks_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%s\n",
- online_type_to_str[mhp_default_online_type]);
+ online_type_to_str[mhp_get_default_online_type()]);
}
static ssize_t auto_online_blocks_store(struct device *dev,
@@ -477,12 +537,22 @@ static ssize_t auto_online_blocks_store(struct device *dev,
if (online_type < 0)
return -EINVAL;
- mhp_default_online_type = online_type;
+ mhp_set_default_online_type(online_type);
return count;
}
static DEVICE_ATTR_RW(auto_online_blocks);
+#ifdef CONFIG_CRASH_HOTPLUG
+#include <linux/kexec.h>
+static ssize_t crash_hotplug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", crash_check_hotplug_support());
+}
+static DEVICE_ATTR_RO(crash_hotplug);
+#endif
+
/*
* Some architectures will have custom drivers to do this, and
* will not need to do it from userspace. The fake hot-add code
@@ -579,7 +649,7 @@ int __weak arch_get_memory_phys_device(unsigned long start_pfn)
*
* Called under device_hotplug_lock.
*/
-static struct memory_block *find_memory_block_by_id(unsigned long block_id)
+struct memory_block *find_memory_block_by_id(unsigned long block_id)
{
struct memory_block *mem;
@@ -678,21 +748,22 @@ static struct zone *early_node_zone_for_memory_block(struct memory_block *mem,
#ifdef CONFIG_NUMA
/**
- * memory_block_add_nid() - Indicate that system RAM falling into this memory
- * block device (partially) belongs to the given node.
+ * memory_block_add_nid_early() - Indicate that early system RAM falling into
+ * this memory block device (partially) belongs
+ * to the given node.
* @mem: The memory block device.
* @nid: The node id.
- * @context: The memory initialization context.
*
- * Indicate that system RAM falling into this memory block (partially) belongs
- * to the given node. If the context indicates ("early") that we are adding the
- * node during node device subsystem initialization, this will also properly
- * set/adjust mem->zone based on the zone ranges of the given node.
+ * Indicate that early system RAM falling into this memory block (partially)
+ * belongs to the given node. This will also properly set/adjust mem->zone based
+ * on the zone ranges of the given node.
+ *
+ * Memory hotplug handles this on memory block creation, where we can only have
+ * a single nid span a memory block.
*/
-void memory_block_add_nid(struct memory_block *mem, int nid,
- enum meminit_context context)
+void memory_block_add_nid_early(struct memory_block *mem, int nid)
{
- if (context == MEMINIT_EARLY && mem->nid != nid) {
+ if (mem->nid != nid) {
/*
* For early memory we have to determine the zone when setting
* the node id and handle multiple nodes spanning a single
@@ -706,20 +777,19 @@ void memory_block_add_nid(struct memory_block *mem, int nid,
mem->zone = early_node_zone_for_memory_block(mem, nid);
else
mem->zone = NULL;
+ /*
+ * If this memory block spans multiple nodes, we only indicate
+ * the last processed node. If we span multiple nodes (not applicable
+ * to hotplugged memory), zone == NULL will prohibit memory offlining
+ * and consequently unplug.
+ */
+ mem->nid = nid;
}
-
- /*
- * If this memory block spans multiple nodes, we only indicate
- * the last processed node. If we span multiple nodes (not applicable
- * to hotplugged memory), zone == NULL will prohibit memory offlining
- * and consequently unplug.
- */
- mem->nid = nid;
}
#endif
-static int add_memory_block(unsigned long block_id, unsigned long state,
- unsigned long nr_vmemmap_pages,
+static int add_memory_block(unsigned long block_id, int nid, unsigned long state,
+ struct vmem_altmap *altmap,
struct memory_group *group)
{
struct memory_block *mem;
@@ -736,8 +806,8 @@ static int add_memory_block(unsigned long block_id, unsigned long state,
mem->start_section_nr = block_id * sections_per_block;
mem->state = state;
- mem->nid = NUMA_NO_NODE;
- mem->nr_vmemmap_pages = nr_vmemmap_pages;
+ mem->nid = nid;
+ mem->altmap = altmap;
INIT_LIST_HEAD(&mem->group_next);
#ifndef CONFIG_NUMA
@@ -763,29 +833,6 @@ static int add_memory_block(unsigned long block_id, unsigned long state,
return 0;
}
-static int __init add_boot_memory_block(unsigned long base_section_nr)
-{
- int section_count = 0;
- unsigned long nr;
-
- for (nr = base_section_nr; nr < base_section_nr + sections_per_block;
- nr++)
- if (present_section_nr(nr))
- section_count++;
-
- if (section_count == 0)
- return 0;
- return add_memory_block(memory_block_id(base_section_nr),
- MEM_ONLINE, 0, NULL);
-}
-
-static int add_hotplug_memory_block(unsigned long block_id,
- unsigned long nr_vmemmap_pages,
- struct memory_group *group)
-{
- return add_memory_block(block_id, MEM_OFFLINE, nr_vmemmap_pages, group);
-}
-
static void remove_memory_block(struct memory_block *memory)
{
if (WARN_ON_ONCE(memory->dev.bus != &memory_subsys))
@@ -811,7 +858,7 @@ static void remove_memory_block(struct memory_block *memory)
* Called under device_hotplug_lock.
*/
int create_memory_block_devices(unsigned long start, unsigned long size,
- unsigned long vmemmap_pages,
+ int nid, struct vmem_altmap *altmap,
struct memory_group *group)
{
const unsigned long start_block_id = pfn_to_block_id(PFN_DOWN(start));
@@ -825,7 +872,7 @@ int create_memory_block_devices(unsigned long start, unsigned long size,
return -EINVAL;
for (block_id = start_block_id; block_id != end_block_id; block_id++) {
- ret = add_hotplug_memory_block(block_id, vmemmap_pages, group);
+ ret = add_memory_block(block_id, nid, MEM_OFFLINE, altmap, group);
if (ret)
break;
}
@@ -864,17 +911,12 @@ void remove_memory_block_devices(unsigned long start, unsigned long size)
mem = find_memory_block_by_id(block_id);
if (WARN_ON_ONCE(!mem))
continue;
+ num_poisoned_pages_sub(-1UL, memblk_nr_poison(mem));
unregister_memory_block_under_nodes(mem);
remove_memory_block(mem);
}
}
-/* return true if the memory block is offlined, otherwise, return false */
-bool is_memblock_offlined(struct memory_block *mem)
-{
- return mem->state == MEM_OFFLINE;
-}
-
static struct attribute *memory_root_attrs[] = {
#ifdef CONFIG_ARCH_MEMORY_PROBE
&dev_attr_probe.attr,
@@ -887,6 +929,9 @@ static struct attribute *memory_root_attrs[] = {
&dev_attr_block_size_bytes.attr,
&dev_attr_auto_online_blocks.attr,
+#ifdef CONFIG_CRASH_HOTPLUG
+ &dev_attr_crash_hotplug.attr,
+#endif
NULL
};
@@ -907,7 +952,7 @@ static const struct attribute_group *memory_root_attr_groups[] = {
void __init memory_dev_init(void)
{
int ret;
- unsigned long block_sz, nr;
+ unsigned long block_sz, block_id, nr;
/* Validate the configured memory block size */
block_sz = memory_block_size_bytes();
@@ -920,15 +965,23 @@ void __init memory_dev_init(void)
panic("%s() failed to register subsystem: %d\n", __func__, ret);
/*
- * Create entries for memory sections that were found
- * during boot and have been initialized
+ * Create entries for memory sections that were found during boot
+ * and have been initialized. Use @block_id to track the last
+ * handled block and initialize it to an invalid value (ULONG_MAX)
+ * to bypass the block ID matching check for the first present
+ * block so that it can be covered.
*/
- for (nr = 0; nr <= __highest_present_section_nr;
- nr += sections_per_block) {
- ret = add_boot_memory_block(nr);
- if (ret)
- panic("%s() failed to add memory block: %d\n", __func__,
- ret);
+ block_id = ULONG_MAX;
+ for_each_present_section_nr(0, nr) {
+ if (block_id != ULONG_MAX && memory_block_id(nr) == block_id)
+ continue;
+
+ block_id = memory_block_id(nr);
+ ret = add_memory_block(block_id, NUMA_NO_NODE, MEM_ONLINE, NULL, NULL);
+ if (ret) {
+ panic("%s() failed to add memory block: %d\n",
+ __func__, ret);
+ }
}
}
@@ -1170,3 +1223,28 @@ int walk_dynamic_memory_groups(int nid, walk_memory_groups_func_t func,
}
return ret;
}
+
+#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG)
+void memblk_nr_poison_inc(unsigned long pfn)
+{
+ const unsigned long block_id = pfn_to_block_id(pfn);
+ struct memory_block *mem = find_memory_block_by_id(block_id);
+
+ if (mem)
+ atomic_long_inc(&mem->nr_hwpoison);
+}
+
+void memblk_nr_poison_sub(unsigned long pfn, long i)
+{
+ const unsigned long block_id = pfn_to_block_id(pfn);
+ struct memory_block *mem = find_memory_block_by_id(block_id);
+
+ if (mem)
+ atomic_long_sub(i, &mem->nr_hwpoison);
+}
+
+static unsigned long memblk_nr_poison(struct memory_block *mem)
+{
+ return atomic_long_read(&mem->nr_hwpoison);
+}
+#endif
diff --git a/drivers/base/module.c b/drivers/base/module.c
index 46ad4d636731..218aaa096455 100644
--- a/drivers/base/module.c
+++ b/drivers/base/module.c
@@ -9,7 +9,7 @@
#include <linux/string.h>
#include "base.h"
-static char *make_driver_name(struct device_driver *drv)
+static char *make_driver_name(const struct device_driver *drv)
{
char *driver_name;
@@ -30,46 +30,67 @@ static void module_create_drivers_dir(struct module_kobject *mk)
mutex_unlock(&drivers_dir_mutex);
}
-void module_add_driver(struct module *mod, struct device_driver *drv)
+int module_add_driver(struct module *mod, const struct device_driver *drv)
{
char *driver_name;
- int no_warn;
struct module_kobject *mk = NULL;
+ int ret;
if (!drv)
- return;
+ return 0;
if (mod)
mk = &mod->mkobj;
else if (drv->mod_name) {
- struct kobject *mkobj;
-
- /* Lookup built-in module entry in /sys/modules */
- mkobj = kset_find_obj(module_kset, drv->mod_name);
- if (mkobj) {
- mk = container_of(mkobj, struct module_kobject, kobj);
+ /* Lookup or create built-in module entry in /sys/modules */
+ mk = lookup_or_create_module_kobject(drv->mod_name);
+ if (mk) {
/* remember our module structure */
drv->p->mkobj = mk;
- /* kset_find_obj took a reference */
- kobject_put(mkobj);
+ /* lookup_or_create_module_kobject took a reference */
+ kobject_put(&mk->kobj);
}
}
if (!mk)
- return;
+ return 0;
+
+ ret = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
+ if (ret)
+ return ret;
- /* Don't check return codes; these calls are idempotent */
- no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
driver_name = make_driver_name(drv);
- if (driver_name) {
- module_create_drivers_dir(mk);
- no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
- driver_name);
- kfree(driver_name);
+ if (!driver_name) {
+ ret = -ENOMEM;
+ goto out_remove_kobj;
}
+
+ module_create_drivers_dir(mk);
+ if (!mk->drivers_dir) {
+ ret = -EINVAL;
+ goto out_free_driver_name;
+ }
+
+ ret = sysfs_create_link(mk->drivers_dir, &drv->p->kobj, driver_name);
+ if (ret)
+ goto out_remove_drivers_dir;
+
+ kfree(driver_name);
+
+ return 0;
+
+out_remove_drivers_dir:
+ sysfs_remove_link(mk->drivers_dir, driver_name);
+
+out_free_driver_name:
+ kfree(driver_name);
+
+out_remove_kobj:
+ sysfs_remove_link(&drv->p->kobj, "module");
+ return ret;
}
-void module_remove_driver(struct device_driver *drv)
+void module_remove_driver(const struct device_driver *drv)
{
struct module_kobject *mk = NULL;
char *driver_name;
diff --git a/drivers/base/node.c b/drivers/base/node.c
index eb0f43784c2b..00cf4532f121 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -7,6 +7,7 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/memory.h>
+#include <linux/mempolicy.h>
#include <linux/vmstat.h>
#include <linux/notifier.h>
#include <linux/node.h>
@@ -20,14 +21,15 @@
#include <linux/pm_runtime.h>
#include <linux/swap.h>
#include <linux/slab.h>
+#include <linux/memblock.h>
-static struct bus_type node_subsys = {
+static const struct bus_type node_subsys = {
.name = "node",
.dev_name = "node",
};
static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
+ const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -45,10 +47,10 @@ static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj,
return n;
}
-static BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES);
static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
+ const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -66,7 +68,7 @@ static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
return n;
}
-static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES);
/**
* struct node_access_nodes - Access class device to hold user visible
@@ -74,14 +76,14 @@ static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES);
* @dev: Device for this memory access class
* @list_node: List element in the node's access list
* @access: The access class rank
- * @hmem_attrs: Heterogeneous memory performance attributes
+ * @coord: Heterogeneous memory performance coordinates
*/
struct node_access_nodes {
struct device dev;
struct list_head list_node;
unsigned int access;
#ifdef CONFIG_HMEM_REPORTING
- struct node_hmem_attrs hmem_attrs;
+ struct access_coordinate coord;
#endif
};
#define to_access_nodes(dev) container_of(dev, struct node_access_nodes, dev)
@@ -110,6 +112,27 @@ static const struct attribute_group *node_access_node_groups[] = {
NULL,
};
+#ifdef CONFIG_MEMORY_HOTPLUG
+static BLOCKING_NOTIFIER_HEAD(node_chain);
+
+int register_node_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&node_chain, nb);
+}
+EXPORT_SYMBOL(register_node_notifier);
+
+void unregister_node_notifier(struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&node_chain, nb);
+}
+EXPORT_SYMBOL(unregister_node_notifier);
+
+int node_notify(unsigned long val, void *v)
+{
+ return blocking_notifier_call_chain(&node_chain, val, v);
+}
+#endif
+
static void node_remove_accesses(struct node *node)
{
struct node_access_nodes *c, *cnext;
@@ -126,7 +149,7 @@ static void node_access_release(struct device *dev)
}
static struct node_access_nodes *node_init_node_access(struct node *node,
- unsigned int access)
+ enum access_coordinate_class access)
{
struct node_access_nodes *access_node;
struct device *dev;
@@ -161,15 +184,15 @@ free:
}
#ifdef CONFIG_HMEM_REPORTING
-#define ACCESS_ATTR(name) \
-static ssize_t name##_show(struct device *dev, \
+#define ACCESS_ATTR(property) \
+static ssize_t property##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return sysfs_emit(buf, "%u\n", \
- to_access_nodes(dev)->hmem_attrs.name); \
+ to_access_nodes(dev)->coord.property); \
} \
-static DEVICE_ATTR_RO(name)
+static DEVICE_ATTR_RO(property)
ACCESS_ATTR(read_bandwidth);
ACCESS_ATTR(read_latency);
@@ -187,11 +210,11 @@ static struct attribute *access_attrs[] = {
/**
* node_set_perf_attrs - Set the performance values for given access class
* @nid: Node identifier to be set
- * @hmem_attrs: Heterogeneous memory performance attributes
+ * @coord: Heterogeneous memory performance coordinates
* @access: The access class the for the given attributes
*/
-void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,
- unsigned int access)
+void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord,
+ enum access_coordinate_class access)
{
struct node_access_nodes *c;
struct node *node;
@@ -205,7 +228,7 @@ void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,
if (!c)
return;
- c->hmem_attrs = *hmem_attrs;
+ c->coord = *coord;
for (i = 0; access_attrs[i] != NULL; i++) {
if (sysfs_add_file_to_group(&c->dev.kobj, access_attrs[i],
"initiators")) {
@@ -214,7 +237,54 @@ void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,
break;
}
}
+
+ /* When setting CPU access coordinates, update mempolicy */
+ if (access == ACCESS_COORDINATE_CPU) {
+ if (mempolicy_set_node_perf(nid, coord)) {
+ pr_info("failed to set mempolicy attrs for node %d\n",
+ nid);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(node_set_perf_attrs);
+
+/**
+ * node_update_perf_attrs - Update the performance values for given access class
+ * @nid: Node identifier to be updated
+ * @coord: Heterogeneous memory performance coordinates
+ * @access: The access class for the given attributes
+ */
+void node_update_perf_attrs(unsigned int nid, struct access_coordinate *coord,
+ enum access_coordinate_class access)
+{
+ struct node_access_nodes *access_node;
+ struct node *node;
+ int i;
+
+ if (WARN_ON_ONCE(!node_online(nid)))
+ return;
+
+ node = node_devices[nid];
+ list_for_each_entry(access_node, &node->access_list, list_node) {
+ if (access_node->access != access)
+ continue;
+
+ access_node->coord = *coord;
+ for (i = 0; access_attrs[i]; i++) {
+ sysfs_notify(&access_node->dev.kobj,
+ NULL, access_attrs[i]->name);
+ }
+ break;
+ }
+
+ /* When setting CPU access coordinates, update mempolicy */
+ if (access != ACCESS_COORDINATE_CPU)
+ return;
+
+ if (mempolicy_set_node_perf(nid, coord))
+ pr_info("failed to set mempolicy attrs for node %d\n", nid);
}
+EXPORT_SYMBOL_GPL(node_update_perf_attrs);
/**
* struct node_cache_info - Internal tracking for memory node caches
@@ -243,12 +313,14 @@ CACHE_ATTR(size, "%llu")
CACHE_ATTR(line_size, "%u")
CACHE_ATTR(indexing, "%u")
CACHE_ATTR(write_policy, "%u")
+CACHE_ATTR(address_mode, "%#x")
static struct attribute *cache_attrs[] = {
&dev_attr_indexing.attr,
&dev_attr_size.attr,
&dev_attr_line_size.attr,
&dev_attr_write_policy.attr,
+ &dev_attr_address_mode.attr,
NULL,
};
ATTRIBUTE_GROUPS(cache);
@@ -433,6 +505,7 @@ static ssize_t node_read_meminfo(struct device *dev,
"Node %d ShadowCallStack:%8lu kB\n"
#endif
"Node %d PageTables: %8lu kB\n"
+ "Node %d SecPageTables: %8lu kB\n"
"Node %d NFS_Unstable: %8lu kB\n"
"Node %d Bounce: %8lu kB\n"
"Node %d WritebackTmp: %8lu kB\n"
@@ -444,8 +517,11 @@ static ssize_t node_read_meminfo(struct device *dev,
"Node %d AnonHugePages: %8lu kB\n"
"Node %d ShmemHugePages: %8lu kB\n"
"Node %d ShmemPmdMapped: %8lu kB\n"
- "Node %d FileHugePages: %8lu kB\n"
- "Node %d FilePmdMapped: %8lu kB\n"
+ "Node %d FileHugePages: %8lu kB\n"
+ "Node %d FilePmdMapped: %8lu kB\n"
+#endif
+#ifdef CONFIG_UNACCEPTED_MEMORY
+ "Node %d Unaccepted: %8lu kB\n"
#endif
,
nid, K(node_page_state(pgdat, NR_FILE_DIRTY)),
@@ -459,9 +535,10 @@ static ssize_t node_read_meminfo(struct device *dev,
nid, node_page_state(pgdat, NR_KERNEL_SCS_KB),
#endif
nid, K(node_page_state(pgdat, NR_PAGETABLE)),
+ nid, K(node_page_state(pgdat, NR_SECONDARY_PAGETABLE)),
+ nid, 0UL,
+ nid, 0UL,
nid, 0UL,
- nid, K(sum_zone_node_page_state(nid, NR_BOUNCE)),
- nid, K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
nid, K(sreclaimable +
node_page_state(pgdat, NR_KERNEL_MISC_RECLAIMABLE)),
nid, K(sreclaimable + sunreclaimable),
@@ -475,6 +552,10 @@ static ssize_t node_read_meminfo(struct device *dev,
nid, K(node_page_state(pgdat, NR_FILE_THPS)),
nid, K(node_page_state(pgdat, NR_FILE_PMDMAPPED))
#endif
+#ifdef CONFIG_UNACCEPTED_MEMORY
+ ,
+ nid, K(sum_zone_node_page_state(nid, NR_UNACCEPTED))
+#endif
);
len += hugetlb_report_node_meminfo(buf, len, nid);
return len;
@@ -568,7 +649,7 @@ static struct attribute *node_dev_attrs[] = {
NULL
};
-static struct bin_attribute *node_dev_bin_attrs[] = {
+static const struct bin_attribute *node_dev_bin_attrs[] = {
&bin_attr_cpumap,
&bin_attr_cpulist,
NULL
@@ -576,7 +657,7 @@ static struct bin_attribute *node_dev_bin_attrs[] = {
static const struct attribute_group node_dev_group = {
.attrs = node_dev_attrs,
- .bin_attrs = node_dev_bin_attrs
+ .bin_attrs = node_dev_bin_attrs,
};
static const struct attribute_group *node_dev_groups[] = {
@@ -584,109 +665,15 @@ static const struct attribute_group *node_dev_groups[] = {
#ifdef CONFIG_HAVE_ARCH_NODE_DEV_GROUP
&arch_node_dev_group,
#endif
+#ifdef CONFIG_MEMORY_FAILURE
+ &memory_failure_attr_group,
+#endif
NULL
};
-#ifdef CONFIG_HUGETLBFS
-/*
- * hugetlbfs per node attributes registration interface:
- * When/if hugetlb[fs] subsystem initializes [sometime after this module],
- * it will register its per node attributes for all online nodes with
- * memory. It will also call register_hugetlbfs_with_node(), below, to
- * register its attribute registration functions with this node driver.
- * Once these hooks have been initialized, the node driver will call into
- * the hugetlb module to [un]register attributes for hot-plugged nodes.
- */
-static node_registration_func_t __hugetlb_register_node;
-static node_registration_func_t __hugetlb_unregister_node;
-
-static inline bool hugetlb_register_node(struct node *node)
-{
- if (__hugetlb_register_node &&
- node_state(node->dev.id, N_MEMORY)) {
- __hugetlb_register_node(node);
- return true;
- }
- return false;
-}
-
-static inline void hugetlb_unregister_node(struct node *node)
-{
- if (__hugetlb_unregister_node)
- __hugetlb_unregister_node(node);
-}
-
-void register_hugetlbfs_with_node(node_registration_func_t doregister,
- node_registration_func_t unregister)
-{
- __hugetlb_register_node = doregister;
- __hugetlb_unregister_node = unregister;
-}
-#else
-static inline void hugetlb_register_node(struct node *node) {}
-
-static inline void hugetlb_unregister_node(struct node *node) {}
-#endif
-
static void node_device_release(struct device *dev)
{
- struct node *node = to_node(dev);
-
-#if defined(CONFIG_MEMORY_HOTPLUG) && defined(CONFIG_HUGETLBFS)
- /*
- * We schedule the work only when a memory section is
- * onlined/offlined on this node. When we come here,
- * all the memory on this node has been offlined,
- * so we won't enqueue new work to this work.
- *
- * The work is using node->node_work, so we should
- * flush work before freeing the memory.
- */
- flush_work(&node->node_work);
-#endif
- kfree(node);
-}
-
-/*
- * register_node - Setup a sysfs device for a node.
- * @num - Node number to use when creating the device.
- *
- * Initialize and register the node device.
- */
-static int register_node(struct node *node, int num)
-{
- int error;
-
- node->dev.id = num;
- node->dev.bus = &node_subsys;
- node->dev.release = node_device_release;
- node->dev.groups = node_dev_groups;
- error = device_register(&node->dev);
-
- if (error)
- put_device(&node->dev);
- else {
- hugetlb_register_node(node);
-
- compaction_register_node(node);
- }
- return error;
-}
-
-/**
- * unregister_node - unregister a node device
- * @node: node going away
- *
- * Unregisters a node device @node. All the devices on the node must be
- * unregistered before calling this function.
- */
-void unregister_node(struct node *node)
-{
- compaction_unregister_node(node);
- hugetlb_unregister_node(node); /* no-op, if memoryless node */
- node_remove_accesses(node);
- node_remove_caches(node);
- device_unregister(&node->dev);
+ kfree(to_node(dev));
}
struct node *node_devices[MAX_NUMNODES];
@@ -732,7 +719,7 @@ int register_cpu_under_node(unsigned int cpu, unsigned int nid)
*/
int register_memory_node_under_compute_node(unsigned int mem_nid,
unsigned int cpu_nid,
- unsigned int access)
+ enum access_coordinate_class access)
{
struct node *init_node, *targ_node;
struct node_access_nodes *initiator, *target;
@@ -787,23 +774,11 @@ int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
}
#ifdef CONFIG_MEMORY_HOTPLUG
-static int __ref get_nid_for_pfn(unsigned long pfn)
-{
-#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
- if (system_state < SYSTEM_RUNNING)
- return early_pfn_to_nid(pfn);
-#endif
- return pfn_to_nid(pfn);
-}
-
static void do_register_memory_block_under_node(int nid,
- struct memory_block *mem_blk,
- enum meminit_context context)
+ struct memory_block *mem_blk)
{
int ret;
- memory_block_add_nid(mem_blk, nid, context);
-
ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj,
&mem_blk->dev.kobj,
kobject_name(&mem_blk->dev.kobj));
@@ -822,46 +797,6 @@ static void do_register_memory_block_under_node(int nid,
ret);
}
-/* register memory section under specified node if it spans that node */
-static int register_mem_block_under_node_early(struct memory_block *mem_blk,
- void *arg)
-{
- unsigned long memory_block_pfns = memory_block_size_bytes() / PAGE_SIZE;
- unsigned long start_pfn = section_nr_to_pfn(mem_blk->start_section_nr);
- unsigned long end_pfn = start_pfn + memory_block_pfns - 1;
- int nid = *(int *)arg;
- unsigned long pfn;
-
- for (pfn = start_pfn; pfn <= end_pfn; pfn++) {
- int page_nid;
-
- /*
- * memory block could have several absent sections from start.
- * skip pfn range from absent section
- */
- if (!pfn_in_present_section(pfn)) {
- pfn = round_down(pfn + PAGES_PER_SECTION,
- PAGES_PER_SECTION) - 1;
- continue;
- }
-
- /*
- * We need to check if page belongs to nid only at the boot
- * case because node's ranges can be interleaved.
- */
- page_nid = get_nid_for_pfn(pfn);
- if (page_nid < 0)
- continue;
- if (page_nid != nid)
- continue;
-
- do_register_memory_block_under_node(nid, mem_blk, MEMINIT_EARLY);
- return 0;
- }
- /* mem section does not span the specified node */
- return 0;
-}
-
/*
* During hotplug we know that all pages in the memory block belong to the same
* node.
@@ -871,7 +806,7 @@ static int register_mem_block_under_node_hotplug(struct memory_block *mem_blk,
{
int nid = *(int *)arg;
- do_register_memory_block_under_node(nid, mem_blk, MEMINIT_HOTPLUG);
+ do_register_memory_block_under_node(nid, mem_blk);
return 0;
}
@@ -890,99 +825,77 @@ void unregister_memory_block_under_nodes(struct memory_block *mem_blk)
kobject_name(&node_devices[mem_blk->nid]->dev.kobj));
}
-void register_memory_blocks_under_node(int nid, unsigned long start_pfn,
- unsigned long end_pfn,
- enum meminit_context context)
+/* register all memory blocks under the corresponding nodes */
+static void register_memory_blocks_under_nodes(void)
{
- walk_memory_blocks_func_t func;
+ struct memblock_region *r;
- if (context == MEMINIT_HOTPLUG)
- func = register_mem_block_under_node_hotplug;
- else
- func = register_mem_block_under_node_early;
+ for_each_mem_region(r) {
+ const unsigned long start_block_id = phys_to_block_id(r->base);
+ const unsigned long end_block_id = phys_to_block_id(r->base + r->size - 1);
+ const int nid = memblock_get_region_node(r);
+ unsigned long block_id;
- walk_memory_blocks(PFN_PHYS(start_pfn), PFN_PHYS(end_pfn - start_pfn),
- (void *)&nid, func);
- return;
-}
-
-#ifdef CONFIG_HUGETLBFS
-/*
- * Handle per node hstate attribute [un]registration on transistions
- * to/from memoryless state.
- */
-static void node_hugetlb_work(struct work_struct *work)
-{
- struct node *node = container_of(work, struct node, node_work);
+ if (!node_online(nid))
+ continue;
- /*
- * We only get here when a node transitions to/from memoryless state.
- * We can detect which transition occurred by examining whether the
- * node has memory now. hugetlb_register_node() already check this
- * so we try to register the attributes. If that fails, then the
- * node has transitioned to memoryless, try to unregister the
- * attributes.
- */
- if (!hugetlb_register_node(node))
- hugetlb_unregister_node(node);
-}
+ for (block_id = start_block_id; block_id <= end_block_id; block_id++) {
+ struct memory_block *mem;
-static void init_node_hugetlb_work(int nid)
-{
- INIT_WORK(&node_devices[nid]->node_work, node_hugetlb_work);
-}
+ mem = find_memory_block_by_id(block_id);
+ if (!mem)
+ continue;
-static int node_memory_callback(struct notifier_block *self,
- unsigned long action, void *arg)
-{
- struct memory_notify *mnb = arg;
- int nid = mnb->status_change_nid;
-
- switch (action) {
- case MEM_ONLINE:
- case MEM_OFFLINE:
- /*
- * offload per node hstate [un]registration to a work thread
- * when transitioning to/from memoryless state.
- */
- if (nid != NUMA_NO_NODE)
- schedule_work(&node_devices[nid]->node_work);
- break;
+ memory_block_add_nid_early(mem, nid);
+ do_register_memory_block_under_node(nid, mem);
+ put_device(&mem->dev);
+ }
- case MEM_GOING_ONLINE:
- case MEM_GOING_OFFLINE:
- case MEM_CANCEL_ONLINE:
- case MEM_CANCEL_OFFLINE:
- default:
- break;
}
-
- return NOTIFY_OK;
}
-#endif /* CONFIG_HUGETLBFS */
-#endif /* CONFIG_MEMORY_HOTPLUG */
-#if !defined(CONFIG_MEMORY_HOTPLUG) || !defined(CONFIG_HUGETLBFS)
-static inline int node_memory_callback(struct notifier_block *self,
- unsigned long action, void *arg)
+void register_memory_blocks_under_node_hotplug(int nid, unsigned long start_pfn,
+ unsigned long end_pfn)
{
- return NOTIFY_OK;
+ walk_memory_blocks(PFN_PHYS(start_pfn), PFN_PHYS(end_pfn - start_pfn),
+ (void *)&nid, register_mem_block_under_node_hotplug);
+ return;
}
+#endif /* CONFIG_MEMORY_HOTPLUG */
-static void init_node_hugetlb_work(int nid) { }
-
-#endif
-
-int __register_one_node(int nid)
+/**
+ * register_node - Initialize and register the node device.
+ * @nid: Node number to use when creating the device.
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+int register_node(int nid)
{
int error;
int cpu;
+ struct node *node;
- node_devices[nid] = kzalloc(sizeof(struct node), GFP_KERNEL);
- if (!node_devices[nid])
+ node = kzalloc(sizeof(struct node), GFP_KERNEL);
+ if (!node)
return -ENOMEM;
- error = register_node(node_devices[nid], nid);
+ INIT_LIST_HEAD(&node->access_list);
+
+ node->dev.id = nid;
+ node->dev.bus = &node_subsys;
+ node->dev.release = node_device_release;
+ node->dev.groups = node_dev_groups;
+
+ error = device_register(&node->dev);
+ if (error) {
+ put_device(&node->dev);
+ return error;
+ }
+
+ node_devices[nid] = node;
+ hugetlb_register_node(node);
+ compaction_register_node(node);
+ reclaim_register_node(node);
/* link cpu under this node */
for_each_present_cpu(cpu) {
@@ -990,20 +903,30 @@ int __register_one_node(int nid)
register_cpu_under_node(cpu, nid);
}
- INIT_LIST_HEAD(&node_devices[nid]->access_list);
- /* initialize work queue for memory hot plug */
- init_node_hugetlb_work(nid);
node_init_caches(nid);
return error;
}
-
-void unregister_one_node(int nid)
+/**
+ * unregister_node - unregister a node device
+ * @nid: nid of the node going away
+ *
+ * Unregisters the node device at node id @nid. All the devices on the
+ * node must be unregistered before calling this function.
+ */
+void unregister_node(int nid)
{
- if (!node_devices[nid])
+ struct node *node = node_devices[nid];
+
+ if (!node)
return;
- unregister_node(node_devices[nid]);
+ hugetlb_unregister_node(node);
+ compaction_unregister_node(node);
+ reclaim_unregister_node(node);
+ node_remove_accesses(node);
+ node_remove_caches(node);
+ device_unregister(&node->dev);
node_devices[nid] = NULL;
}
@@ -1063,13 +986,8 @@ static const struct attribute_group *cpu_root_attr_groups[] = {
NULL,
};
-#define NODE_CALLBACK_PRI 2 /* lower than SLAB */
void __init node_dev_init(void)
{
- static struct notifier_block node_memory_callback_nb = {
- .notifier_call = node_memory_callback,
- .priority = NODE_CALLBACK_PRI,
- };
int ret, i;
BUILD_BUG_ON(ARRAY_SIZE(node_state_attr) != NR_NODE_STATES);
@@ -1079,15 +997,15 @@ void __init node_dev_init(void)
if (ret)
panic("%s() failed to register subsystem: %d\n", __func__, ret);
- register_hotmemory_notifier(&node_memory_callback_nb);
-
/*
* Create all node devices, which will properly link the node
- * to applicable memory block devices and already created cpu devices.
+ * to already created cpu devices.
*/
for_each_online_node(i) {
- ret = register_one_node(i);
+ ret = register_node(i);
if (ret)
panic("%s() failed to add node: %d\n", __func__, ret);
}
+
+ register_memory_blocks_under_nodes();
}
diff --git a/drivers/base/physical_location.c b/drivers/base/physical_location.c
index 87af641cfe1a..a5539e294d4d 100644
--- a/drivers/base/physical_location.c
+++ b/drivers/base/physical_location.c
@@ -7,25 +7,27 @@
#include <linux/acpi.h>
#include <linux/sysfs.h>
+#include <linux/string_choices.h>
#include "physical_location.h"
bool dev_add_physical_location(struct device *dev)
{
struct acpi_pld_info *pld;
- acpi_status status;
if (!has_acpi_companion(dev))
return false;
- status = acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld);
- if (ACPI_FAILURE(status))
+ if (!acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld))
return false;
dev->physical_location =
kzalloc(sizeof(*dev->physical_location), GFP_KERNEL);
- if (!dev->physical_location)
+ if (!dev->physical_location) {
+ ACPI_FREE(pld);
return false;
+ }
+
dev->physical_location->panel = pld->panel;
dev->physical_location->vertical_position = pld->vertical_position;
dev->physical_location->horizontal_position = pld->horizontal_position;
@@ -115,7 +117,7 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n",
- dev->physical_location->dock ? "yes" : "no");
+ str_yes_no(dev->physical_location->dock));
}
static DEVICE_ATTR_RO(dock);
@@ -123,7 +125,7 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n",
- dev->physical_location->lid ? "yes" : "no");
+ str_yes_no(dev->physical_location->lid));
}
static DEVICE_ATTR_RO(lid);
diff --git a/drivers/base/physical_location.h b/drivers/base/physical_location.h
index 82cde9f1b161..3f3f61307998 100644
--- a/drivers/base/physical_location.h
+++ b/drivers/base/physical_location.h
@@ -8,7 +8,7 @@
#include <linux/device.h>
#ifdef CONFIG_ACPI
-extern bool dev_add_physical_location(struct device *dev);
+bool dev_add_physical_location(struct device *dev);
extern const struct attribute_group dev_attr_physical_location_group;
#else
static inline bool dev_add_physical_location(struct device *dev) { return false; };
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 296ea673d661..70db08f3ac6f 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -4,347 +4,97 @@
*
* Copyright (C) 2015 ARM Limited, All Rights Reserved.
* Author: Marc Zyngier <marc.zyngier@arm.com>
+ * Copyright (C) 2022 Linutronix GmbH
*/
#include <linux/device.h>
-#include <linux/idr.h>
-#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
-#include <linux/slab.h>
-#define DEV_ID_SHIFT 21
-#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
-
-/*
- * Internal data structure containing a (made up, but unique) devid
- * and the callback to write the MSI message.
- */
-struct platform_msi_priv_data {
- struct device *dev;
- void *host_data;
- msi_alloc_info_t arg;
- irq_write_msi_msg_t write_msg;
- int devid;
-};
-
-/* The devid allocator */
-static DEFINE_IDA(platform_msi_devid_ida);
-
-#ifdef GENERIC_MSI_DOMAIN_OPS
/*
- * Convert an msi_desc to a globaly unique identifier (per-device
- * devid + msi_desc position in the msi_list).
+ * This indirection can go when platform_device_msi_init_and_alloc_irqs()
+ * is switched to a proper irq_chip::irq_write_msi_msg() callback. Keep it
+ * simple for now.
*/
-static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
+static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
- u32 devid = desc->dev->msi.data->platform_data->devid;
+ irq_write_msi_msg_t cb = d->chip_data;
- return (devid << (32 - DEV_ID_SHIFT)) | desc->msi_index;
+ cb(irq_data_get_msi_desc(d), msg);
}
static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->desc = desc;
- arg->hwirq = platform_msi_calc_hwirq(desc);
-}
-
-static int platform_msi_init(struct irq_domain *domain,
- struct msi_domain_info *info,
- unsigned int virq, irq_hw_number_t hwirq,
- msi_alloc_info_t *arg)
-{
- return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
- info->chip, info->chip_data);
-}
-
-static void platform_msi_set_proxy_dev(msi_alloc_info_t *arg)
-{
- arg->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
-}
-#else
-#define platform_msi_set_desc NULL
-#define platform_msi_init NULL
-#define platform_msi_set_proxy_dev(x) do {} while(0)
-#endif
-
-static void platform_msi_update_dom_ops(struct msi_domain_info *info)
-{
- struct msi_domain_ops *ops = info->ops;
-
- BUG_ON(!ops);
-
- if (ops->msi_init == NULL)
- ops->msi_init = platform_msi_init;
- if (ops->set_desc == NULL)
- ops->set_desc = platform_msi_set_desc;
-}
-
-static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
-{
- struct msi_desc *desc = irq_data_get_msi_desc(data);
-
- desc->dev->msi.data->platform_data->write_msg(desc, msg);
-}
-
-static void platform_msi_update_chip_ops(struct msi_domain_info *info)
-{
- struct irq_chip *chip = info->chip;
-
- BUG_ON(!chip);
- if (!chip->irq_mask)
- chip->irq_mask = irq_chip_mask_parent;
- if (!chip->irq_unmask)
- chip->irq_unmask = irq_chip_unmask_parent;
- if (!chip->irq_eoi)
- chip->irq_eoi = irq_chip_eoi_parent;
- if (!chip->irq_set_affinity)
- chip->irq_set_affinity = msi_domain_set_affinity;
- if (!chip->irq_write_msi_msg)
- chip->irq_write_msi_msg = platform_msi_write_msg;
- if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) &&
- !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)))
- info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
+ arg->hwirq = desc->msi_index;
}
-/**
- * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
- * @fwnode: Optional fwnode of the interrupt controller
- * @info: MSI domain info
- * @parent: Parent irq domain
- *
- * Updates the domain and chip ops and creates a platform MSI
- * interrupt domain.
- *
- * Returns:
- * A domain pointer or NULL in case of failure.
- */
-struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
- struct msi_domain_info *info,
- struct irq_domain *parent)
-{
- struct irq_domain *domain;
-
- if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
- platform_msi_update_dom_ops(info);
- if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
- platform_msi_update_chip_ops(info);
- info->flags |= MSI_FLAG_DEV_SYSFS | MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS |
- MSI_FLAG_FREE_MSI_DESCS;
-
- domain = msi_create_irq_domain(fwnode, info, parent);
- if (domain)
- irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI);
-
- return domain;
-}
-
-static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
- irq_write_msi_msg_t write_msi_msg)
-{
- struct platform_msi_priv_data *datap;
- int err;
-
- /*
- * Limit the number of interrupts to 2048 per device. Should we
- * need to bump this up, DEV_ID_SHIFT should be adjusted
- * accordingly (which would impact the max number of MSI
- * capable devices).
- */
- if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
- return -EINVAL;
-
- if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
- dev_err(dev, "Incompatible msi_domain, giving up\n");
- return -EINVAL;
- }
-
- err = msi_setup_device_data(dev);
- if (err)
- return err;
-
- /* Already initialized? */
- if (dev->msi.data->platform_data)
- return -EBUSY;
-
- datap = kzalloc(sizeof(*datap), GFP_KERNEL);
- if (!datap)
- return -ENOMEM;
+static const struct msi_domain_template platform_msi_template = {
+ .chip = {
+ .name = "pMSI",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_write_msi_msg = platform_msi_write_msi_msg,
+ /* The rest is filled in by the platform MSI parent */
+ },
- datap->devid = ida_simple_get(&platform_msi_devid_ida,
- 0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
- if (datap->devid < 0) {
- err = datap->devid;
- kfree(datap);
- return err;
- }
+ .ops = {
+ .set_desc = platform_msi_set_desc,
+ },
- datap->write_msg = write_msi_msg;
- datap->dev = dev;
- dev->msi.data->platform_data = datap;
- return 0;
-}
-
-static void platform_msi_free_priv_data(struct device *dev)
-{
- struct platform_msi_priv_data *data = dev->msi.data->platform_data;
-
- dev->msi.data->platform_data = NULL;
- ida_simple_remove(&platform_msi_devid_ida, data->devid);
- kfree(data);
-}
+ .info = {
+ .bus_token = DOMAIN_BUS_DEVICE_MSI,
+ },
+};
/**
- * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
+ * platform_device_msi_init_and_alloc_irqs - Initialize platform device MSI
+ * and allocate interrupts for @dev
* @dev: The device for which to allocate interrupts
* @nvec: The number of interrupts to allocate
* @write_msi_msg: Callback to write an interrupt message for @dev
*
* Returns:
* Zero for success, or an error code in case of failure
- */
-int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
- irq_write_msi_msg_t write_msi_msg)
-{
- int err;
-
- err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
- if (err)
- return err;
-
- err = msi_domain_alloc_irqs(dev->msi.domain, dev, nvec);
- if (err)
- platform_msi_free_priv_data(dev);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);
-
-/**
- * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
- * @dev: The device for which to free interrupts
- */
-void platform_msi_domain_free_irqs(struct device *dev)
-{
- msi_domain_free_irqs(dev->msi.domain, dev);
- platform_msi_free_priv_data(dev);
-}
-EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);
-
-/**
- * platform_msi_get_host_data - Query the private data associated with
- * a platform-msi domain
- * @domain: The platform-msi domain
*
- * Return: The private data provided when calling
- * platform_msi_create_device_domain().
- */
-void *platform_msi_get_host_data(struct irq_domain *domain)
-{
- struct platform_msi_priv_data *data = domain->host_data;
-
- return data->host_data;
-}
-
-static struct lock_class_key platform_device_msi_lock_class;
-
-/**
- * __platform_msi_create_device_domain - Create a platform-msi device domain
- *
- * @dev: The device generating the MSIs
- * @nvec: The number of MSIs that need to be allocated
- * @is_tree: flag to indicate tree hierarchy
- * @write_msi_msg: Callback to write an interrupt message for @dev
- * @ops: The hierarchy domain operations to use
- * @host_data: Private data associated to this domain
+ * This creates a MSI domain on @dev which has @dev->msi.domain as
+ * parent. The parent domain sets up the new domain. The domain has
+ * a fixed size of @nvec. The domain is managed by devres and will
+ * be removed when the device is removed.
*
- * Return: An irqdomain for @nvec interrupts on success, NULL in case of error.
- *
- * This is for interrupt domains which stack on a platform-msi domain
- * created by platform_msi_create_irq_domain(). @dev->msi.domain points to
- * that platform-msi domain which is the parent for the new domain.
+ * Note: For migration purposes this falls back to the original platform_msi code
+ * up to the point where all platforms have been converted to the MSI
+ * parent model.
*/
-struct irq_domain *
-__platform_msi_create_device_domain(struct device *dev,
- unsigned int nvec,
- bool is_tree,
- irq_write_msi_msg_t write_msi_msg,
- const struct irq_domain_ops *ops,
- void *host_data)
+int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
+ irq_write_msi_msg_t write_msi_msg)
{
- struct platform_msi_priv_data *data;
- struct irq_domain *domain;
- int err;
+ struct irq_domain *domain = dev->msi.domain;
- err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
- if (err)
- return NULL;
+ if (!domain || !write_msi_msg)
+ return -EINVAL;
/*
- * Use a separate lock class for the MSI descriptor mutex on
- * platform MSI device domains because the descriptor mutex nests
- * into the domain mutex. See alloc/free below.
+ * @write_msi_msg is stored in the resulting msi_domain_info::data.
+ * The underlying domain creation mechanism will assign that
+ * callback to the resulting irq chip.
*/
- lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class);
-
- data = dev->msi.data->platform_data;
- data->host_data = host_data;
- domain = irq_domain_create_hierarchy(dev->msi.domain, 0,
- is_tree ? 0 : nvec,
- dev->fwnode, ops, data);
- if (!domain)
- goto free_priv;
-
- platform_msi_set_proxy_dev(&data->arg);
- err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
- if (err)
- goto free_domain;
-
- return domain;
+ if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
+ &platform_msi_template,
+ nvec, NULL, write_msi_msg))
+ return -ENODEV;
-free_domain:
- irq_domain_remove(domain);
-free_priv:
- platform_msi_free_priv_data(dev);
- return NULL;
+ return msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1);
}
+EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);
/**
- * platform_msi_device_domain_free - Free interrupts associated with a platform-msi
- * device domain
- *
- * @domain: The platform-msi device domain
- * @virq: The base irq from which to perform the free operation
- * @nr_irqs: How many interrupts to free from @virq
- */
-void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
-{
- struct platform_msi_priv_data *data = domain->host_data;
-
- msi_lock_descs(data->dev);
- irq_domain_free_irqs_common(domain, virq, nr_irqs);
- msi_free_msi_descs_range(data->dev, MSI_DESC_ALL, virq, virq + nr_irqs - 1);
- msi_unlock_descs(data->dev);
-}
-
-/**
- * platform_msi_device_domain_alloc - Allocate interrupts associated with
- * a platform-msi device domain
- *
- * @domain: The platform-msi device domain
- * @virq: The base irq from which to perform the allocate operation
- * @nr_irqs: How many interrupts to allocate from @virq
- *
- * Return 0 on success, or an error code on failure. Must be called
- * with irq_domain_mutex held (which can only be done as part of a
- * top-level interrupt allocation).
+ * platform_device_msi_free_irqs_all - Free all interrupts for @dev
+ * @dev: The device for which to free interrupts
*/
-int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
+void platform_device_msi_free_irqs_all(struct device *dev)
{
- struct platform_msi_priv_data *data = domain->host_data;
- struct device *dev = data->dev;
-
- return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
+ msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
+ msi_remove_device_irq_domain(dev, MSI_DEFAULT_DOMAIN);
}
+EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 51bb2289865c..b45d41b018ca 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -150,25 +150,37 @@ devm_platform_ioremap_resource_byname(struct platform_device *pdev,
EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname);
#endif /* CONFIG_HAS_IOMEM */
+static const struct cpumask *get_irq_affinity(struct platform_device *dev,
+ unsigned int num)
+{
+ const struct cpumask *mask = NULL;
+#ifndef CONFIG_SPARC
+ struct fwnode_handle *fwnode = dev_fwnode(&dev->dev);
+
+ if (is_of_node(fwnode))
+ mask = of_irq_get_affinity(to_of_node(fwnode), num);
+ else if (is_acpi_device_node(fwnode))
+ mask = acpi_irq_get_affinity(ACPI_HANDLE_FWNODE(fwnode), num);
+#endif
+
+ return mask ?: cpu_possible_mask;
+}
+
/**
- * platform_get_irq_optional - get an optional IRQ for a device
- * @dev: platform device
- * @num: IRQ number index
+ * platform_get_irq_affinity - get an optional IRQ and its affinity for a device
+ * @dev: platform device
+ * @num: interrupt number index
+ * @affinity: optional cpumask pointer to get the affinity of a per-cpu interrupt
*
- * Gets an IRQ for a platform device. Device drivers should check the return
- * value for errors so as to not pass a negative integer value to the
- * request_irq() APIs. This is the same as platform_get_irq(), except that it
- * does not print an error message if an IRQ can not be obtained.
+ * Gets an interupt for a platform device. Device drivers should check the
+ * return value for errors so as to not pass a negative integer value to
+ * the request_irq() APIs. Optional affinity information is provided in the
+ * affinity pointer if available, and NULL otherwise.
*
- * For example::
- *
- * int irq = platform_get_irq_optional(pdev, 0);
- * if (irq < 0)
- * return irq;
- *
- * Return: non-zero IRQ number on success, negative error number on failure.
+ * Return: non-zero interrupt number on success, negative error number on failure.
*/
-int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
+int platform_get_irq_affinity(struct platform_device *dev, unsigned int num,
+ const struct cpumask **affinity)
{
int ret;
#ifdef CONFIG_SPARC
@@ -178,18 +190,19 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
ret = dev->archdata.irqs[num];
goto out;
#else
+ struct fwnode_handle *fwnode = dev_fwnode(&dev->dev);
struct resource *r;
- if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
- ret = of_irq_get(dev->dev.of_node, num);
+ if (is_of_node(fwnode)) {
+ ret = of_irq_get(to_of_node(fwnode), num);
if (ret > 0 || ret == -EPROBE_DEFER)
goto out;
}
r = platform_get_resource(dev, IORESOURCE_IRQ, num);
- if (has_acpi_companion(&dev->dev)) {
+ if (is_acpi_device_node(fwnode)) {
if (r && r->flags & IORESOURCE_DISABLED) {
- ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);
+ ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), num, r);
if (ret)
goto out;
}
@@ -222,8 +235,8 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
* the device will only expose one IRQ, and this fallback
* allows a common code path across either kind of resource.
*/
- if (num == 0 && has_acpi_companion(&dev->dev)) {
- ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num);
+ if (num == 0 && is_acpi_device_node(fwnode)) {
+ ret = acpi_dev_gpio_irq_get(to_acpi_device_node(fwnode), num);
/* Our callers expect -ENXIO for missing IRQs. */
if (ret >= 0 || ret == -EPROBE_DEFER)
goto out;
@@ -235,8 +248,37 @@ out_not_found:
out:
if (WARN(!ret, "0 is an invalid IRQ number\n"))
return -EINVAL;
+
+ if (ret > 0 && affinity)
+ *affinity = get_irq_affinity(dev, num);
+
return ret;
}
+EXPORT_SYMBOL_GPL(platform_get_irq_affinity);
+
+/**
+ * platform_get_irq_optional - get an optional interrupt for a device
+ * @dev: platform device
+ * @num: interrupt number index
+ *
+ * Gets an interrupt for a platform device. Device drivers should check the
+ * return value for errors so as to not pass a negative integer value to
+ * the request_irq() APIs. This is the same as platform_get_irq(), except
+ * that it does not print an error message if an interrupt can not be
+ * obtained.
+ *
+ * For example::
+ *
+ * int irq = platform_get_irq_optional(pdev, 0);
+ * if (irq < 0)
+ * return irq;
+ *
+ * Return: non-zero interrupt number on success, negative error number on failure.
+ */
+int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
+{
+ return platform_get_irq_affinity(dev, num, NULL);
+}
EXPORT_SYMBOL_GPL(platform_get_irq_optional);
/**
@@ -291,7 +333,7 @@ EXPORT_SYMBOL_GPL(platform_irq_count);
struct irq_affinity_devres {
unsigned int count;
- unsigned int irq[];
+ unsigned int irq[] __counted_by(count);
};
static void platform_disable_acpi_irq(struct platform_device *pdev, int index)
@@ -312,7 +354,7 @@ static void devm_platform_get_irqs_affinity_release(struct device *dev,
for (i = 0; i < ptr->count; i++) {
irq_dispose_mapping(ptr->irq[i]);
- if (has_acpi_companion(dev))
+ if (is_acpi_device_node(dev_fwnode(dev)))
platform_disable_acpi_irq(to_platform_device(dev), i);
}
}
@@ -441,11 +483,9 @@ static int __platform_get_irq_byname(struct platform_device *dev,
struct resource *r;
int ret;
- if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
- ret = of_irq_get_byname(dev->dev.of_node, name);
- if (ret > 0 || ret == -EPROBE_DEFER)
- return ret;
- }
+ ret = fwnode_irq_get_byname(dev_fwnode(&dev->dev), name);
+ if (ret > 0 || ret == -EPROBE_DEFER)
+ return ret;
r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
if (r) {
@@ -499,6 +539,8 @@ EXPORT_SYMBOL_GPL(platform_get_irq_byname_optional);
* platform_add_devices - add a numbers of platform devices
* @devs: array of platform devices to add
* @num: number of platform devices in array
+ *
+ * Return: 0 on success, negative error number on failure.
*/
int platform_add_devices(struct platform_device **devs, int num)
{
@@ -607,7 +649,7 @@ int platform_device_add_resources(struct platform_device *pdev,
struct resource *r = NULL;
if (res) {
- r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL);
+ r = kmemdup_array(res, num, sizeof(*r), GFP_KERNEL);
if (!r)
return -ENOMEM;
}
@@ -655,23 +697,21 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
*/
int platform_device_add(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
u32 i;
int ret;
- if (!pdev)
- return -EINVAL;
+ if (!dev->parent)
+ dev->parent = &platform_bus;
- if (!pdev->dev.parent)
- pdev->dev.parent = &platform_bus;
-
- pdev->dev.bus = &platform_bus_type;
+ dev->bus = &platform_bus_type;
switch (pdev->id) {
default:
- dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
+ dev_set_name(dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE:
- dev_set_name(&pdev->dev, "%s", pdev->name);
+ dev_set_name(dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO:
/*
@@ -681,10 +721,10 @@ int platform_device_add(struct platform_device *pdev)
*/
ret = ida_alloc(&platform_devid_ida, GFP_KERNEL);
if (ret < 0)
- goto err_out;
+ return ret;
pdev->id = ret;
pdev->id_auto = true;
- dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
+ dev_set_name(dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
@@ -692,7 +732,7 @@ int platform_device_add(struct platform_device *pdev)
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
- r->name = dev_name(&pdev->dev);
+ r->name = dev_name(dev);
p = r->parent;
if (!p) {
@@ -705,18 +745,20 @@ int platform_device_add(struct platform_device *pdev)
if (p) {
ret = insert_resource(p, r);
if (ret) {
- dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
+ dev_err(dev, "failed to claim resource %d: %pR\n", i, r);
goto failed;
}
}
}
- pr_debug("Registering platform device '%s'. Parent at %s\n",
- dev_name(&pdev->dev), dev_name(pdev->dev.parent));
+ pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(dev),
+ dev_name(dev->parent));
- ret = device_add(&pdev->dev);
- if (ret == 0)
- return ret;
+ ret = device_add(dev);
+ if (ret)
+ goto failed;
+
+ return 0;
failed:
if (pdev->id_auto) {
@@ -730,7 +772,6 @@ int platform_device_add(struct platform_device *pdev)
release_resource(r);
}
- err_out:
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
@@ -883,6 +924,13 @@ static int platform_probe_fail(struct platform_device *pdev)
return -ENXIO;
}
+static int is_bound_to_driver(struct device *dev, void *driver)
+{
+ if (dev->driver == driver)
+ return 1;
+ return 0;
+}
+
/**
* __platform_driver_probe - register driver for non-hotpluggable device
* @drv: platform driver structure
@@ -906,7 +954,7 @@ static int platform_probe_fail(struct platform_device *pdev)
int __init_or_module __platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *), struct module *module)
{
- int retval, code;
+ int retval;
if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) {
pr_err("%s: drivers registered with %s can not be probed asynchronously\n",
@@ -932,24 +980,21 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv,
/* temporary section violation during probe() */
drv->probe = probe;
- retval = code = __platform_driver_register(drv, module);
+ retval = __platform_driver_register(drv, module);
if (retval)
return retval;
- /*
- * Fixup that section violation, being paranoid about code scanning
- * the list of drivers in order to probe new devices. Check to see
- * if the probe was successful, and make sure any forced probes of
- * new devices fail.
- */
- spin_lock(&drv->driver.bus->p->klist_drivers.k_lock);
+ /* Force all new probes of this driver to fail */
drv->probe = platform_probe_fail;
- if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
- retval = -ENODEV;
- spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock);
- if (code != retval)
+ /* Walk all platform devices and see if any actually bound to this driver.
+ * If not, return an error as the device should have done so by now.
+ */
+ if (!bus_for_each_dev(&platform_bus_type, NULL, &drv->driver, is_bound_to_driver)) {
+ retval = -ENODEV;
platform_driver_unregister(drv);
+ }
+
return retval;
}
EXPORT_SYMBOL_GPL(__platform_driver_probe);
@@ -978,7 +1023,7 @@ struct platform_device * __init_or_module __platform_create_bundle(
struct platform_device *pdev;
int error;
- pdev = platform_device_alloc(driver->driver.name, -1);
+ pdev = platform_device_alloc(driver->driver.name, PLATFORM_DEVID_NONE);
if (!pdev) {
error = -ENOMEM;
goto err_out;
@@ -1118,7 +1163,7 @@ static int platform_legacy_resume(struct device *dev)
int platform_pm_suspend(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ const struct device_driver *drv = dev->driver;
int ret = 0;
if (!drv)
@@ -1136,7 +1181,7 @@ int platform_pm_suspend(struct device *dev)
int platform_pm_resume(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ const struct device_driver *drv = dev->driver;
int ret = 0;
if (!drv)
@@ -1158,7 +1203,7 @@ int platform_pm_resume(struct device *dev)
int platform_pm_freeze(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ const struct device_driver *drv = dev->driver;
int ret = 0;
if (!drv)
@@ -1176,7 +1221,7 @@ int platform_pm_freeze(struct device *dev)
int platform_pm_thaw(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ const struct device_driver *drv = dev->driver;
int ret = 0;
if (!drv)
@@ -1194,7 +1239,7 @@ int platform_pm_thaw(struct device *dev)
int platform_pm_poweroff(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ const struct device_driver *drv = dev->driver;
int ret = 0;
if (!drv)
@@ -1212,7 +1257,7 @@ int platform_pm_poweroff(struct device *dev)
int platform_pm_restore(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ const struct device_driver *drv = dev->driver;
int ret = 0;
if (!drv)
@@ -1328,7 +1373,7 @@ __ATTRIBUTE_GROUPS(platform_dev);
* and compare it against the name of the driver. Return whether they match
* or not.
*/
-static int platform_match(struct device *dev, struct device_driver *drv)
+static int platform_match(struct device *dev, const struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
@@ -1353,9 +1398,9 @@ static int platform_match(struct device *dev, struct device_driver *drv)
return (strcmp(pdev->name, drv->name) == 0);
}
-static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int platform_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct platform_device *pdev = to_platform_device(dev);
+ const struct platform_device *pdev = to_platform_device(dev);
int rc;
/* Some devices have extra OF data and an OF-style MODALIAS */
@@ -1392,15 +1437,13 @@ static int platform_probe(struct device *_dev)
if (ret < 0)
return ret;
- ret = dev_pm_domain_attach(_dev, true);
+ ret = dev_pm_domain_attach(_dev, PD_FLAG_ATTACH_POWER_ON |
+ PD_FLAG_DETACH_POWER_OFF);
if (ret)
goto out;
- if (drv->probe) {
+ if (drv->probe)
ret = drv->probe(dev);
- if (ret)
- dev_pm_domain_detach(_dev, true);
- }
out:
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
@@ -1416,13 +1459,8 @@ static void platform_remove(struct device *_dev)
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
- if (drv->remove) {
- int ret = drv->remove(dev);
-
- if (ret)
- dev_warn(_dev, "remove callback returned a non-zero value. This will be ignored.\n");
- }
- dev_pm_domain_detach(_dev, true);
+ if (drv->remove)
+ drv->remove(dev);
}
static void platform_shutdown(struct device *_dev)
@@ -1440,22 +1478,24 @@ static void platform_shutdown(struct device *_dev)
static int platform_dma_configure(struct device *dev)
{
- struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct device_driver *drv = READ_ONCE(dev->driver);
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
enum dev_dma_attr attr;
int ret = 0;
- if (dev->of_node) {
- ret = of_dma_configure(dev, dev->of_node, true);
- } else if (has_acpi_companion(dev)) {
- attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode));
+ if (is_of_node(fwnode)) {
+ ret = of_dma_configure(dev, to_of_node(fwnode), true);
+ } else if (is_acpi_device_node(fwnode)) {
+ attr = acpi_get_dma_attr(to_acpi_device_node(fwnode));
ret = acpi_dma_configure(dev, attr);
}
+ /* @dev->driver may not be valid when we're called from the IOMMU layer */
+ if (ret || !drv || to_platform_driver(drv)->driver_managed_dma)
+ return ret;
- if (!ret && !drv->driver_managed_dma) {
- ret = iommu_device_use_default_domain(dev);
- if (ret)
- arch_teardown_dma_ops(dev);
- }
+ ret = iommu_device_use_default_domain(dev);
+ if (ret)
+ arch_teardown_dma_ops(dev);
return ret;
}
@@ -1473,7 +1513,7 @@ static const struct dev_pm_ops platform_dev_pm_ops = {
USE_PLATFORM_PM_SLEEP_OPS
};
-struct bus_type platform_bus_type = {
+const struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
@@ -1522,6 +1562,6 @@ int __init platform_bus_init(void)
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
- of_platform_register_reconfig_notifier();
+
return error;
}
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 8fdd0073eeeb..2989e42d0161 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -2,8 +2,8 @@
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o wakeup_stats.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
-obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
obj-$(CONFIG_PM_QOS_KUNIT_TEST) += qos-test.o
+obj-$(CONFIG_PM_RUNTIME_KUNIT_TEST) += runtime-test.o
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 4110c19c08dc..b69bcb37c830 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -259,39 +259,6 @@ int pm_clk_add_clk(struct device *dev, struct clk *clk)
}
EXPORT_SYMBOL_GPL(pm_clk_add_clk);
-
-/**
- * of_pm_clk_add_clk - Start using a device clock for power management.
- * @dev: Device whose clock is going to be used for power management.
- * @name: Name of clock that is going to be used for power management.
- *
- * Add the clock described in the 'clocks' device-tree node that matches
- * with the 'name' provided, to the list of clocks used for the power
- * management of @dev. On success, returns 0. Returns a negative error
- * code if the clock is not found or cannot be added.
- */
-int of_pm_clk_add_clk(struct device *dev, const char *name)
-{
- struct clk *clk;
- int ret;
-
- if (!dev || !dev->of_node || !name)
- return -EINVAL;
-
- clk = of_clk_get_by_name(dev->of_node, name);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- ret = pm_clk_add_clk(dev, clk);
- if (ret) {
- clk_put(clk);
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_pm_clk_add_clk);
-
/**
* of_pm_clk_add_clks - Start using device clock(s) for power management.
* @dev: Device whose clock(s) is going to be used for power management.
@@ -377,46 +344,6 @@ static void __pm_clk_remove(struct pm_clock_entry *ce)
}
/**
- * pm_clk_remove - Stop using a device clock for power management.
- * @dev: Device whose clock should not be used for PM any more.
- * @con_id: Connection ID of the clock.
- *
- * Remove the clock represented by @con_id from the list of clocks used for
- * the power management of @dev.
- */
-void pm_clk_remove(struct device *dev, const char *con_id)
-{
- struct pm_subsys_data *psd = dev_to_psd(dev);
- struct pm_clock_entry *ce;
-
- if (!psd)
- return;
-
- pm_clk_list_lock(psd);
-
- list_for_each_entry(ce, &psd->clock_list, node) {
- if (!con_id && !ce->con_id)
- goto remove;
- else if (!con_id || !ce->con_id)
- continue;
- else if (!strcmp(con_id, ce->con_id))
- goto remove;
- }
-
- pm_clk_list_unlock(psd);
- return;
-
- remove:
- list_del(&ce->node);
- if (ce->enabled_when_prepared)
- psd->clock_op_might_sleep--;
- pm_clk_list_unlock(psd);
-
- __pm_clk_remove(ce);
-}
-EXPORT_SYMBOL_GPL(pm_clk_remove);
-
-/**
* pm_clk_remove_clk - Stop using a device clock for power management.
* @dev: Device whose clock should not be used for PM any more.
* @clk: Clock pointer
@@ -793,7 +720,7 @@ static int pm_clk_notify(struct notifier_block *nb,
* the remaining members of @clknb should be populated prior to calling this
* routine.
*/
-void pm_clk_add_notifier(struct bus_type *bus,
+void pm_clk_add_notifier(const struct bus_type *bus,
struct pm_clk_notifier_block *clknb)
{
if (!bus || !clknb)
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 72115917e0bd..6ecf9ce4a4e6 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -11,6 +11,7 @@
#include <linux/pm_clock.h>
#include <linux/acpi.h>
#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
#include "power.h"
@@ -82,7 +83,7 @@ EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
/**
* dev_pm_domain_attach - Attach a device to its PM domain.
* @dev: Device to attach.
- * @power_on: Used to indicate whether we should power on the device.
+ * @flags: indicate whether we should power on/off the device on attach/detach
*
* The @dev may only be attached to a single PM domain. By iterating through
* the available alternatives we try to find a valid PM domain for the device.
@@ -99,17 +100,20 @@ EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
* Returns 0 on successfully attached PM domain, or when it is found that the
* device doesn't need a PM domain, else a negative error code.
*/
-int dev_pm_domain_attach(struct device *dev, bool power_on)
+int dev_pm_domain_attach(struct device *dev, u32 flags)
{
int ret;
if (dev->pm_domain)
return 0;
- ret = acpi_dev_pm_attach(dev, power_on);
+ ret = acpi_dev_pm_attach(dev, !!(flags & PD_FLAG_ATTACH_POWER_ON));
if (!ret)
ret = genpd_dev_pm_attach(dev);
+ if (dev->pm_domain)
+ dev->power.detach_power_off = !!(flags & PD_FLAG_DETACH_POWER_OFF);
+
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
@@ -168,6 +172,179 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev,
EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_name);
/**
+ * dev_pm_domain_attach_list - Associate a device with its PM domains.
+ * @dev: The device used to lookup the PM domains for.
+ * @data: The data used for attaching to the PM domains.
+ * @list: An out-parameter with an allocated list of attached PM domains.
+ *
+ * This function helps to attach a device to its multiple PM domains. The
+ * caller, which is typically a driver's probe function, may provide a list of
+ * names for the PM domains that we should try to attach the device to, but it
+ * may also provide an empty list, in case the attach should be done for all of
+ * the available PM domains.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns the number of attached PM domains or a negative error code in case of
+ * a failure. Note that, to detach the list of PM domains, the driver shall call
+ * dev_pm_domain_detach_list(), typically during the remove phase.
+ */
+int dev_pm_domain_attach_list(struct device *dev,
+ const struct dev_pm_domain_attach_data *data,
+ struct dev_pm_domain_list **list)
+{
+ struct device_node *np = dev->of_node;
+ struct dev_pm_domain_list *pds;
+ struct device *pd_dev = NULL;
+ int ret, i, num_pds = 0;
+ bool by_id = true;
+ size_t size;
+ u32 pd_flags = data ? data->pd_flags : 0;
+ u32 link_flags = pd_flags & PD_FLAG_NO_DEV_LINK ? 0 :
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME;
+
+ if (dev->pm_domain)
+ return -EEXIST;
+
+ /* For now this is limited to OF based platforms. */
+ if (!np)
+ return 0;
+
+ if (data && data->pd_names) {
+ num_pds = data->num_pd_names;
+ by_id = false;
+ } else {
+ num_pds = of_count_phandle_with_args(np, "power-domains",
+ "#power-domain-cells");
+ }
+
+ if (num_pds <= 0)
+ return 0;
+
+ pds = kzalloc(sizeof(*pds), GFP_KERNEL);
+ if (!pds)
+ return -ENOMEM;
+
+ size = sizeof(*pds->pd_devs) + sizeof(*pds->pd_links) +
+ sizeof(*pds->opp_tokens);
+ pds->pd_devs = kcalloc(num_pds, size, GFP_KERNEL);
+ if (!pds->pd_devs) {
+ ret = -ENOMEM;
+ goto free_pds;
+ }
+ pds->pd_links = (void *)(pds->pd_devs + num_pds);
+ pds->opp_tokens = (void *)(pds->pd_links + num_pds);
+
+ if (link_flags && pd_flags & PD_FLAG_DEV_LINK_ON)
+ link_flags |= DL_FLAG_RPM_ACTIVE;
+
+ for (i = 0; i < num_pds; i++) {
+ if (by_id)
+ pd_dev = dev_pm_domain_attach_by_id(dev, i);
+ else
+ pd_dev = dev_pm_domain_attach_by_name(dev,
+ data->pd_names[i]);
+ if (IS_ERR_OR_NULL(pd_dev)) {
+ ret = pd_dev ? PTR_ERR(pd_dev) : -ENODEV;
+ goto err_attach;
+ }
+
+ if (pd_flags & PD_FLAG_REQUIRED_OPP) {
+ struct dev_pm_opp_config config = {
+ .required_dev = pd_dev,
+ .required_dev_index = i,
+ };
+
+ ret = dev_pm_opp_set_config(dev, &config);
+ if (ret < 0)
+ goto err_link;
+
+ pds->opp_tokens[i] = ret;
+ }
+
+ if (link_flags) {
+ struct device_link *link;
+
+ link = device_link_add(dev, pd_dev, link_flags);
+ if (!link) {
+ ret = -ENODEV;
+ goto err_link;
+ }
+
+ pds->pd_links[i] = link;
+ }
+
+ pds->pd_devs[i] = pd_dev;
+ }
+
+ pds->num_pds = num_pds;
+ *list = pds;
+ return num_pds;
+
+err_link:
+ dev_pm_opp_clear_config(pds->opp_tokens[i]);
+ dev_pm_domain_detach(pd_dev, true);
+err_attach:
+ while (--i >= 0) {
+ dev_pm_opp_clear_config(pds->opp_tokens[i]);
+ if (pds->pd_links[i])
+ device_link_del(pds->pd_links[i]);
+ dev_pm_domain_detach(pds->pd_devs[i], true);
+ }
+ kfree(pds->pd_devs);
+free_pds:
+ kfree(pds);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_attach_list);
+
+/**
+ * devm_pm_domain_detach_list - devres-enabled version of dev_pm_domain_detach_list.
+ * @_list: The list of PM domains to detach.
+ *
+ * This function reverse the actions from devm_pm_domain_attach_list().
+ * it will be invoked during the remove phase from drivers implicitly if driver
+ * uses devm_pm_domain_attach_list() to attach the PM domains.
+ */
+static void devm_pm_domain_detach_list(void *_list)
+{
+ struct dev_pm_domain_list *list = _list;
+
+ dev_pm_domain_detach_list(list);
+}
+
+/**
+ * devm_pm_domain_attach_list - devres-enabled version of dev_pm_domain_attach_list
+ * @dev: The device used to lookup the PM domains for.
+ * @data: The data used for attaching to the PM domains.
+ * @list: An out-parameter with an allocated list of attached PM domains.
+ *
+ * NOTE: this will also handle calling devm_pm_domain_detach_list() for
+ * you during remove phase.
+ *
+ * Returns the number of attached PM domains or a negative error code in case of
+ * a failure.
+ */
+int devm_pm_domain_attach_list(struct device *dev,
+ const struct dev_pm_domain_attach_data *data,
+ struct dev_pm_domain_list **list)
+{
+ int ret, num_pds;
+
+ num_pds = dev_pm_domain_attach_list(dev, data, list);
+ if (num_pds <= 0)
+ return num_pds;
+
+ ret = devm_add_action_or_reset(dev, devm_pm_domain_detach_list, *list);
+ if (ret)
+ return ret;
+
+ return num_pds;
+}
+EXPORT_SYMBOL_GPL(devm_pm_domain_attach_list);
+
+/**
* dev_pm_domain_detach - Detach a device from its PM domain.
* @dev: Device to detach.
* @power_off: Used to indicate whether we should power off the device.
@@ -188,6 +365,35 @@ void dev_pm_domain_detach(struct device *dev, bool power_off)
EXPORT_SYMBOL_GPL(dev_pm_domain_detach);
/**
+ * dev_pm_domain_detach_list - Detach a list of PM domains.
+ * @list: The list of PM domains to detach.
+ *
+ * This function reverse the actions from dev_pm_domain_attach_list().
+ * Typically it should be invoked during the remove phase from drivers.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ */
+void dev_pm_domain_detach_list(struct dev_pm_domain_list *list)
+{
+ int i;
+
+ if (!list)
+ return;
+
+ for (i = 0; i < list->num_pds; i++) {
+ dev_pm_opp_clear_config(list->opp_tokens[i]);
+ if (list->pd_links[i])
+ device_link_del(list->pd_links[i]);
+ dev_pm_domain_detach(list->pd_devs[i], true);
+ }
+
+ kfree(list->pd_devs);
+ kfree(list);
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_detach_list);
+
+/**
* dev_pm_domain_start - Start the device through its PM domain.
* @dev: Device to start.
*
@@ -228,3 +434,24 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd)
device_pm_check_callbacks(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_domain_set);
+
+/**
+ * dev_pm_domain_set_performance_state - Request a new performance state.
+ * @dev: The device to make the request for.
+ * @state: Target performance state for the device.
+ *
+ * This function should be called when a new performance state needs to be
+ * requested for a device that is attached to a PM domain. Note that, the
+ * support for performance scaling for PM domains is optional.
+ *
+ * Returns 0 on success and when performance scaling isn't supported, negative
+ * error code on failure.
+ */
+int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state)
+{
+ if (dev->pm_domain && dev->pm_domain->set_performance_state)
+ return dev->pm_domain->set_performance_state(dev, state);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_set_performance_state);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
deleted file mode 100644
index 55a10e6d4e2a..000000000000
--- a/drivers/base/power/domain.c
+++ /dev/null
@@ -1,3401 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/base/power/domain.c - Common code related to device power domains.
- *
- * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
- */
-#define pr_fmt(fmt) "PM: " fmt
-
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/pm_opp.h>
-#include <linux/pm_runtime.h>
-#include <linux/pm_domain.h>
-#include <linux/pm_qos.h>
-#include <linux/pm_clock.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/sched.h>
-#include <linux/suspend.h>
-#include <linux/export.h>
-#include <linux/cpu.h>
-#include <linux/debugfs.h>
-
-#include "power.h"
-
-#define GENPD_RETRY_MAX_MS 250 /* Approximate */
-
-#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
-({ \
- type (*__routine)(struct device *__d); \
- type __ret = (type)0; \
- \
- __routine = genpd->dev_ops.callback; \
- if (__routine) { \
- __ret = __routine(dev); \
- } \
- __ret; \
-})
-
-static LIST_HEAD(gpd_list);
-static DEFINE_MUTEX(gpd_list_lock);
-
-struct genpd_lock_ops {
- void (*lock)(struct generic_pm_domain *genpd);
- void (*lock_nested)(struct generic_pm_domain *genpd, int depth);
- int (*lock_interruptible)(struct generic_pm_domain *genpd);
- void (*unlock)(struct generic_pm_domain *genpd);
-};
-
-static void genpd_lock_mtx(struct generic_pm_domain *genpd)
-{
- mutex_lock(&genpd->mlock);
-}
-
-static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd,
- int depth)
-{
- mutex_lock_nested(&genpd->mlock, depth);
-}
-
-static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd)
-{
- return mutex_lock_interruptible(&genpd->mlock);
-}
-
-static void genpd_unlock_mtx(struct generic_pm_domain *genpd)
-{
- return mutex_unlock(&genpd->mlock);
-}
-
-static const struct genpd_lock_ops genpd_mtx_ops = {
- .lock = genpd_lock_mtx,
- .lock_nested = genpd_lock_nested_mtx,
- .lock_interruptible = genpd_lock_interruptible_mtx,
- .unlock = genpd_unlock_mtx,
-};
-
-static void genpd_lock_spin(struct generic_pm_domain *genpd)
- __acquires(&genpd->slock)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&genpd->slock, flags);
- genpd->lock_flags = flags;
-}
-
-static void genpd_lock_nested_spin(struct generic_pm_domain *genpd,
- int depth)
- __acquires(&genpd->slock)
-{
- unsigned long flags;
-
- spin_lock_irqsave_nested(&genpd->slock, flags, depth);
- genpd->lock_flags = flags;
-}
-
-static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd)
- __acquires(&genpd->slock)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&genpd->slock, flags);
- genpd->lock_flags = flags;
- return 0;
-}
-
-static void genpd_unlock_spin(struct generic_pm_domain *genpd)
- __releases(&genpd->slock)
-{
- spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags);
-}
-
-static const struct genpd_lock_ops genpd_spin_ops = {
- .lock = genpd_lock_spin,
- .lock_nested = genpd_lock_nested_spin,
- .lock_interruptible = genpd_lock_interruptible_spin,
- .unlock = genpd_unlock_spin,
-};
-
-#define genpd_lock(p) p->lock_ops->lock(p)
-#define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d)
-#define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p)
-#define genpd_unlock(p) p->lock_ops->unlock(p)
-
-#define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON)
-#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
-#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)
-#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
-#define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN)
-#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
-
-static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
- const struct generic_pm_domain *genpd)
-{
- bool ret;
-
- ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd);
-
- /*
- * Warn once if an IRQ safe device is attached to a domain, which
- * callbacks are allowed to sleep. This indicates a suboptimal
- * configuration for PM, but it doesn't matter for an always on domain.
- */
- if (genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd))
- return ret;
-
- if (ret)
- dev_warn_once(dev, "PM domain %s will not be powered off\n",
- genpd->name);
-
- return ret;
-}
-
-static int genpd_runtime_suspend(struct device *dev);
-
-/*
- * Get the generic PM domain for a particular struct device.
- * This validates the struct device pointer, the PM domain pointer,
- * and checks that the PM domain pointer is a real generic PM domain.
- * Any failure results in NULL being returned.
- */
-static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev)
-{
- if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain))
- return NULL;
-
- /* A genpd's always have its ->runtime_suspend() callback assigned. */
- if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend)
- return pd_to_genpd(dev->pm_domain);
-
- return NULL;
-}
-
-/*
- * This should only be used where we are certain that the pm_domain
- * attached to the device is a genpd domain.
- */
-static struct generic_pm_domain *dev_to_genpd(struct device *dev)
-{
- if (IS_ERR_OR_NULL(dev->pm_domain))
- return ERR_PTR(-EINVAL);
-
- return pd_to_genpd(dev->pm_domain);
-}
-
-static int genpd_stop_dev(const struct generic_pm_domain *genpd,
- struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, stop, dev);
-}
-
-static int genpd_start_dev(const struct generic_pm_domain *genpd,
- struct device *dev)
-{
- return GENPD_DEV_CALLBACK(genpd, int, start, dev);
-}
-
-static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
-{
- bool ret = false;
-
- if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
- ret = !!atomic_dec_and_test(&genpd->sd_count);
-
- return ret;
-}
-
-static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
-{
- atomic_inc(&genpd->sd_count);
- smp_mb__after_atomic();
-}
-
-#ifdef CONFIG_DEBUG_FS
-static struct dentry *genpd_debugfs_dir;
-
-static void genpd_debug_add(struct generic_pm_domain *genpd);
-
-static void genpd_debug_remove(struct generic_pm_domain *genpd)
-{
- struct dentry *d;
-
- if (!genpd_debugfs_dir)
- return;
-
- d = debugfs_lookup(genpd->name, genpd_debugfs_dir);
- debugfs_remove(d);
-}
-
-static void genpd_update_accounting(struct generic_pm_domain *genpd)
-{
- u64 delta, now;
-
- now = ktime_get_mono_fast_ns();
- if (now <= genpd->accounting_time)
- return;
-
- delta = now - genpd->accounting_time;
-
- /*
- * If genpd->status is active, it means we are just
- * out of off and so update the idle time and vice
- * versa.
- */
- if (genpd->status == GENPD_STATE_ON)
- genpd->states[genpd->state_idx].idle_time += delta;
- else
- genpd->on_time += delta;
-
- genpd->accounting_time = now;
-}
-#else
-static inline void genpd_debug_add(struct generic_pm_domain *genpd) {}
-static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}
-static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
-#endif
-
-static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
- unsigned int state)
-{
- struct generic_pm_domain_data *pd_data;
- struct pm_domain_data *pdd;
- struct gpd_link *link;
-
- /* New requested state is same as Max requested state */
- if (state == genpd->performance_state)
- return state;
-
- /* New requested state is higher than Max requested state */
- if (state > genpd->performance_state)
- return state;
-
- /* Traverse all devices within the domain */
- list_for_each_entry(pdd, &genpd->dev_list, list_node) {
- pd_data = to_gpd_data(pdd);
-
- if (pd_data->performance_state > state)
- state = pd_data->performance_state;
- }
-
- /*
- * Traverse all sub-domains within the domain. This can be
- * done without any additional locking as the link->performance_state
- * field is protected by the parent genpd->lock, which is already taken.
- *
- * Also note that link->performance_state (subdomain's performance state
- * requirement to parent domain) is different from
- * link->child->performance_state (current performance state requirement
- * of the devices/sub-domains of the subdomain) and so can have a
- * different value.
- *
- * Note that we also take vote from powered-off sub-domains into account
- * as the same is done for devices right now.
- */
- list_for_each_entry(link, &genpd->parent_links, parent_node) {
- if (link->performance_state > state)
- state = link->performance_state;
- }
-
- return state;
-}
-
-static int genpd_xlate_performance_state(struct generic_pm_domain *genpd,
- struct generic_pm_domain *parent,
- unsigned int pstate)
-{
- if (!parent->set_performance_state)
- return pstate;
-
- return dev_pm_opp_xlate_performance_state(genpd->opp_table,
- parent->opp_table,
- pstate);
-}
-
-static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
- unsigned int state, int depth)
-{
- struct generic_pm_domain *parent;
- struct gpd_link *link;
- int parent_state, ret;
-
- if (state == genpd->performance_state)
- return 0;
-
- /* Propagate to parents of genpd */
- list_for_each_entry(link, &genpd->child_links, child_node) {
- parent = link->parent;
-
- /* Find parent's performance state */
- ret = genpd_xlate_performance_state(genpd, parent, state);
- if (unlikely(ret < 0))
- goto err;
-
- parent_state = ret;
-
- genpd_lock_nested(parent, depth + 1);
-
- link->prev_performance_state = link->performance_state;
- link->performance_state = parent_state;
- parent_state = _genpd_reeval_performance_state(parent,
- parent_state);
- ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
- if (ret)
- link->performance_state = link->prev_performance_state;
-
- genpd_unlock(parent);
-
- if (ret)
- goto err;
- }
-
- if (genpd->set_performance_state) {
- ret = genpd->set_performance_state(genpd, state);
- if (ret)
- goto err;
- }
-
- genpd->performance_state = state;
- return 0;
-
-err:
- /* Encountered an error, lets rollback */
- list_for_each_entry_continue_reverse(link, &genpd->child_links,
- child_node) {
- parent = link->parent;
-
- genpd_lock_nested(parent, depth + 1);
-
- parent_state = link->prev_performance_state;
- link->performance_state = parent_state;
-
- parent_state = _genpd_reeval_performance_state(parent,
- parent_state);
- if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
- pr_err("%s: Failed to roll back to %d performance state\n",
- parent->name, parent_state);
- }
-
- genpd_unlock(parent);
- }
-
- return ret;
-}
-
-static int genpd_set_performance_state(struct device *dev, unsigned int state)
-{
- struct generic_pm_domain *genpd = dev_to_genpd(dev);
- struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
- unsigned int prev_state;
- int ret;
-
- prev_state = gpd_data->performance_state;
- if (prev_state == state)
- return 0;
-
- gpd_data->performance_state = state;
- state = _genpd_reeval_performance_state(genpd, state);
-
- ret = _genpd_set_performance_state(genpd, state, 0);
- if (ret)
- gpd_data->performance_state = prev_state;
-
- return ret;
-}
-
-static int genpd_drop_performance_state(struct device *dev)
-{
- unsigned int prev_state = dev_gpd_data(dev)->performance_state;
-
- if (!genpd_set_performance_state(dev, 0))
- return prev_state;
-
- return 0;
-}
-
-static void genpd_restore_performance_state(struct device *dev,
- unsigned int state)
-{
- if (state)
- genpd_set_performance_state(dev, state);
-}
-
-/**
- * dev_pm_genpd_set_performance_state- Set performance state of device's power
- * domain.
- *
- * @dev: Device for which the performance-state needs to be set.
- * @state: Target performance state of the device. This can be set as 0 when the
- * device doesn't have any performance state constraints left (And so
- * the device wouldn't participate anymore to find the target
- * performance state of the genpd).
- *
- * It is assumed that the users guarantee that the genpd wouldn't be detached
- * while this routine is getting called.
- *
- * Returns 0 on success and negative error values on failures.
- */
-int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
-{
- struct generic_pm_domain *genpd;
- int ret = 0;
-
- genpd = dev_to_genpd_safe(dev);
- if (!genpd)
- return -ENODEV;
-
- if (WARN_ON(!dev->power.subsys_data ||
- !dev->power.subsys_data->domain_data))
- return -EINVAL;
-
- genpd_lock(genpd);
- if (pm_runtime_suspended(dev)) {
- dev_gpd_data(dev)->rpm_pstate = state;
- } else {
- ret = genpd_set_performance_state(dev, state);
- if (!ret)
- dev_gpd_data(dev)->rpm_pstate = 0;
- }
- genpd_unlock(genpd);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state);
-
-/**
- * dev_pm_genpd_set_next_wakeup - Notify PM framework of an impending wakeup.
- *
- * @dev: Device to handle
- * @next: impending interrupt/wakeup for the device
- *
- *
- * Allow devices to inform of the next wakeup. It's assumed that the users
- * guarantee that the genpd wouldn't be detached while this routine is getting
- * called. Additionally, it's also assumed that @dev isn't runtime suspended
- * (RPM_SUSPENDED)."
- * Although devices are expected to update the next_wakeup after the end of
- * their usecase as well, it is possible the devices themselves may not know
- * about that, so stale @next will be ignored when powering off the domain.
- */
-void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next)
-{
- struct generic_pm_domain *genpd;
- struct gpd_timing_data *td;
-
- genpd = dev_to_genpd_safe(dev);
- if (!genpd)
- return;
-
- td = to_gpd_data(dev->power.subsys_data->domain_data)->td;
- if (td)
- td->next_wakeup = next;
-}
-EXPORT_SYMBOL_GPL(dev_pm_genpd_set_next_wakeup);
-
-static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
-{
- unsigned int state_idx = genpd->state_idx;
- ktime_t time_start;
- s64 elapsed_ns;
- int ret;
-
- /* Notify consumers that we are about to power on. */
- ret = raw_notifier_call_chain_robust(&genpd->power_notifiers,
- GENPD_NOTIFY_PRE_ON,
- GENPD_NOTIFY_OFF, NULL);
- ret = notifier_to_errno(ret);
- if (ret)
- return ret;
-
- if (!genpd->power_on)
- goto out;
-
- timed = timed && genpd->gd && !genpd->states[state_idx].fwnode;
- if (!timed) {
- ret = genpd->power_on(genpd);
- if (ret)
- goto err;
-
- goto out;
- }
-
- time_start = ktime_get();
- ret = genpd->power_on(genpd);
- if (ret)
- goto err;
-
- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
- goto out;
-
- genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
- genpd->gd->max_off_time_changed = true;
- pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
- genpd->name, "on", elapsed_ns);
-
-out:
- raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
- return 0;
-err:
- raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
- NULL);
- return ret;
-}
-
-static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
-{
- unsigned int state_idx = genpd->state_idx;
- ktime_t time_start;
- s64 elapsed_ns;
- int ret;
-
- /* Notify consumers that we are about to power off. */
- ret = raw_notifier_call_chain_robust(&genpd->power_notifiers,
- GENPD_NOTIFY_PRE_OFF,
- GENPD_NOTIFY_ON, NULL);
- ret = notifier_to_errno(ret);
- if (ret)
- return ret;
-
- if (!genpd->power_off)
- goto out;
-
- timed = timed && genpd->gd && !genpd->states[state_idx].fwnode;
- if (!timed) {
- ret = genpd->power_off(genpd);
- if (ret)
- goto busy;
-
- goto out;
- }
-
- time_start = ktime_get();
- ret = genpd->power_off(genpd);
- if (ret)
- goto busy;
-
- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
- goto out;
-
- genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
- genpd->gd->max_off_time_changed = true;
- pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
- genpd->name, "off", elapsed_ns);
-
-out:
- raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
- NULL);
- return 0;
-busy:
- raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
- return ret;
-}
-
-/**
- * genpd_queue_power_off_work - Queue up the execution of genpd_power_off().
- * @genpd: PM domain to power off.
- *
- * Queue up the execution of genpd_power_off() unless it's already been done
- * before.
- */
-static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
-{
- queue_work(pm_wq, &genpd->power_off_work);
-}
-
-/**
- * genpd_power_off - Remove power from a given PM domain.
- * @genpd: PM domain to power down.
- * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the
- * RPM status of the releated device is in an intermediate state, not yet turned
- * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not
- * be RPM_SUSPENDED, while it tries to power off the PM domain.
- * @depth: nesting count for lockdep.
- *
- * If all of the @genpd's devices have been suspended and all of its subdomains
- * have been powered down, remove power from @genpd.
- */
-static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
- unsigned int depth)
-{
- struct pm_domain_data *pdd;
- struct gpd_link *link;
- unsigned int not_suspended = 0;
- int ret;
-
- /*
- * Do not try to power off the domain in the following situations:
- * (1) The domain is already in the "power off" state.
- * (2) System suspend is in progress.
- */
- if (!genpd_status_on(genpd) || genpd->prepared_count > 0)
- return 0;
-
- /*
- * Abort power off for the PM domain in the following situations:
- * (1) The domain is configured as always on.
- * (2) When the domain has a subdomain being powered on.
- */
- if (genpd_is_always_on(genpd) ||
- genpd_is_rpm_always_on(genpd) ||
- atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
-
- /*
- * The children must be in their deepest (powered-off) states to allow
- * the parent to be powered off. Note that, there's no need for
- * additional locking, as powering on a child, requires the parent's
- * lock to be acquired first.
- */
- list_for_each_entry(link, &genpd->parent_links, parent_node) {
- struct generic_pm_domain *child = link->child;
- if (child->state_idx < child->state_count - 1)
- return -EBUSY;
- }
-
- list_for_each_entry(pdd, &genpd->dev_list, list_node) {
- /*
- * Do not allow PM domain to be powered off, when an IRQ safe
- * device is part of a non-IRQ safe domain.
- */
- if (!pm_runtime_suspended(pdd->dev) ||
- irq_safe_dev_in_sleep_domain(pdd->dev, genpd))
- not_suspended++;
- }
-
- if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
- return -EBUSY;
-
- if (genpd->gov && genpd->gov->power_down_ok) {
- if (!genpd->gov->power_down_ok(&genpd->domain))
- return -EAGAIN;
- }
-
- /* Default to shallowest state. */
- if (!genpd->gov)
- genpd->state_idx = 0;
-
- /* Don't power off, if a child domain is waiting to power on. */
- if (atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
-
- ret = _genpd_power_off(genpd, true);
- if (ret) {
- genpd->states[genpd->state_idx].rejected++;
- return ret;
- }
-
- genpd->status = GENPD_STATE_OFF;
- genpd_update_accounting(genpd);
- genpd->states[genpd->state_idx].usage++;
-
- list_for_each_entry(link, &genpd->child_links, child_node) {
- genpd_sd_counter_dec(link->parent);
- genpd_lock_nested(link->parent, depth + 1);
- genpd_power_off(link->parent, false, depth + 1);
- genpd_unlock(link->parent);
- }
-
- return 0;
-}
-
-/**
- * genpd_power_on - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- * @depth: nesting count for lockdep.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
-{
- struct gpd_link *link;
- int ret = 0;
-
- if (genpd_status_on(genpd))
- return 0;
-
- /*
- * The list is guaranteed not to change while the loop below is being
- * executed, unless one of the parents' .power_on() callbacks fiddles
- * with it.
- */
- list_for_each_entry(link, &genpd->child_links, child_node) {
- struct generic_pm_domain *parent = link->parent;
-
- genpd_sd_counter_inc(parent);
-
- genpd_lock_nested(parent, depth + 1);
- ret = genpd_power_on(parent, depth + 1);
- genpd_unlock(parent);
-
- if (ret) {
- genpd_sd_counter_dec(parent);
- goto err;
- }
- }
-
- ret = _genpd_power_on(genpd, true);
- if (ret)
- goto err;
-
- genpd->status = GENPD_STATE_ON;
- genpd_update_accounting(genpd);
-
- return 0;
-
- err:
- list_for_each_entry_continue_reverse(link,
- &genpd->child_links,
- child_node) {
- genpd_sd_counter_dec(link->parent);
- genpd_lock_nested(link->parent, depth + 1);
- genpd_power_off(link->parent, false, depth + 1);
- genpd_unlock(link->parent);
- }
-
- return ret;
-}
-
-static int genpd_dev_pm_start(struct device *dev)
-{
- struct generic_pm_domain *genpd = dev_to_genpd(dev);
-
- return genpd_start_dev(genpd, dev);
-}
-
-static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
- unsigned long val, void *ptr)
-{
- struct generic_pm_domain_data *gpd_data;
- struct device *dev;
-
- gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
- dev = gpd_data->base.dev;
-
- for (;;) {
- struct generic_pm_domain *genpd = ERR_PTR(-ENODATA);
- struct pm_domain_data *pdd;
- struct gpd_timing_data *td;
-
- spin_lock_irq(&dev->power.lock);
-
- pdd = dev->power.subsys_data ?
- dev->power.subsys_data->domain_data : NULL;
- if (pdd) {
- td = to_gpd_data(pdd)->td;
- if (td) {
- td->constraint_changed = true;
- genpd = dev_to_genpd(dev);
- }
- }
-
- spin_unlock_irq(&dev->power.lock);
-
- if (!IS_ERR(genpd)) {
- genpd_lock(genpd);
- genpd->gd->max_off_time_changed = true;
- genpd_unlock(genpd);
- }
-
- dev = dev->parent;
- if (!dev || dev->power.ignore_children)
- break;
- }
-
- return NOTIFY_DONE;
-}
-
-/**
- * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
- * @work: Work structure used for scheduling the execution of this function.
- */
-static void genpd_power_off_work_fn(struct work_struct *work)
-{
- struct generic_pm_domain *genpd;
-
- genpd = container_of(work, struct generic_pm_domain, power_off_work);
-
- genpd_lock(genpd);
- genpd_power_off(genpd, false, 0);
- genpd_unlock(genpd);
-}
-
-/**
- * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks
- * @dev: Device to handle.
- */
-static int __genpd_runtime_suspend(struct device *dev)
-{
- int (*cb)(struct device *__dev);
-
- if (dev->type && dev->type->pm)
- cb = dev->type->pm->runtime_suspend;
- else if (dev->class && dev->class->pm)
- cb = dev->class->pm->runtime_suspend;
- else if (dev->bus && dev->bus->pm)
- cb = dev->bus->pm->runtime_suspend;
- else
- cb = NULL;
-
- if (!cb && dev->driver && dev->driver->pm)
- cb = dev->driver->pm->runtime_suspend;
-
- return cb ? cb(dev) : 0;
-}
-
-/**
- * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks
- * @dev: Device to handle.
- */
-static int __genpd_runtime_resume(struct device *dev)
-{
- int (*cb)(struct device *__dev);
-
- if (dev->type && dev->type->pm)
- cb = dev->type->pm->runtime_resume;
- else if (dev->class && dev->class->pm)
- cb = dev->class->pm->runtime_resume;
- else if (dev->bus && dev->bus->pm)
- cb = dev->bus->pm->runtime_resume;
- else
- cb = NULL;
-
- if (!cb && dev->driver && dev->driver->pm)
- cb = dev->driver->pm->runtime_resume;
-
- return cb ? cb(dev) : 0;
-}
-
-/**
- * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
- * @dev: Device to suspend.
- *
- * Carry out a runtime suspend of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a PM domain consisting of I/O devices.
- */
-static int genpd_runtime_suspend(struct device *dev)
-{
- struct generic_pm_domain *genpd;
- bool (*suspend_ok)(struct device *__dev);
- struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
- struct gpd_timing_data *td = gpd_data->td;
- bool runtime_pm = pm_runtime_enabled(dev);
- ktime_t time_start = 0;
- s64 elapsed_ns;
- int ret;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- /*
- * A runtime PM centric subsystem/driver may re-use the runtime PM
- * callbacks for other purposes than runtime PM. In those scenarios
- * runtime PM is disabled. Under these circumstances, we shall skip
- * validating/measuring the PM QoS latency.
- */
- suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL;
- if (runtime_pm && suspend_ok && !suspend_ok(dev))
- return -EBUSY;
-
- /* Measure suspend latency. */
- if (td && runtime_pm)
- time_start = ktime_get();
-
- ret = __genpd_runtime_suspend(dev);
- if (ret)
- return ret;
-
- ret = genpd_stop_dev(genpd, dev);
- if (ret) {
- __genpd_runtime_resume(dev);
- return ret;
- }
-
- /* Update suspend latency value if the measured time exceeds it. */
- if (td && runtime_pm) {
- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns > td->suspend_latency_ns) {
- td->suspend_latency_ns = elapsed_ns;
- dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
- elapsed_ns);
- genpd->gd->max_off_time_changed = true;
- td->constraint_changed = true;
- }
- }
-
- /*
- * If power.irq_safe is set, this routine may be run with
- * IRQs disabled, so suspend only if the PM domain also is irq_safe.
- */
- if (irq_safe_dev_in_sleep_domain(dev, genpd))
- return 0;
-
- genpd_lock(genpd);
- gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
- genpd_power_off(genpd, true, 0);
- genpd_unlock(genpd);
-
- return 0;
-}
-
-/**
- * genpd_runtime_resume - Resume a device belonging to I/O PM domain.
- * @dev: Device to resume.
- *
- * Carry out a runtime resume of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a PM domain consisting of I/O devices.
- */
-static int genpd_runtime_resume(struct device *dev)
-{
- struct generic_pm_domain *genpd;
- struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
- struct gpd_timing_data *td = gpd_data->td;
- bool timed = td && pm_runtime_enabled(dev);
- ktime_t time_start = 0;
- s64 elapsed_ns;
- int ret;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- /*
- * As we don't power off a non IRQ safe domain, which holds
- * an IRQ safe device, we don't need to restore power to it.
- */
- if (irq_safe_dev_in_sleep_domain(dev, genpd))
- goto out;
-
- genpd_lock(genpd);
- ret = genpd_power_on(genpd, 0);
- if (!ret)
- genpd_restore_performance_state(dev, gpd_data->rpm_pstate);
- genpd_unlock(genpd);
-
- if (ret)
- return ret;
-
- out:
- /* Measure resume latency. */
- if (timed)
- time_start = ktime_get();
-
- ret = genpd_start_dev(genpd, dev);
- if (ret)
- goto err_poweroff;
-
- ret = __genpd_runtime_resume(dev);
- if (ret)
- goto err_stop;
-
- /* Update resume latency value if the measured time exceeds it. */
- if (timed) {
- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns > td->resume_latency_ns) {
- td->resume_latency_ns = elapsed_ns;
- dev_dbg(dev, "resume latency exceeded, %lld ns\n",
- elapsed_ns);
- genpd->gd->max_off_time_changed = true;
- td->constraint_changed = true;
- }
- }
-
- return 0;
-
-err_stop:
- genpd_stop_dev(genpd, dev);
-err_poweroff:
- if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) {
- genpd_lock(genpd);
- gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
- genpd_power_off(genpd, true, 0);
- genpd_unlock(genpd);
- }
-
- return ret;
-}
-
-static bool pd_ignore_unused;
-static int __init pd_ignore_unused_setup(char *__unused)
-{
- pd_ignore_unused = true;
- return 1;
-}
-__setup("pd_ignore_unused", pd_ignore_unused_setup);
-
-/**
- * genpd_power_off_unused - Power off all PM domains with no devices in use.
- */
-static int __init genpd_power_off_unused(void)
-{
- struct generic_pm_domain *genpd;
-
- if (pd_ignore_unused) {
- pr_warn("genpd: Not disabling unused power domains\n");
- return 0;
- }
-
- mutex_lock(&gpd_list_lock);
-
- list_for_each_entry(genpd, &gpd_list, gpd_list_node)
- genpd_queue_power_off_work(genpd);
-
- mutex_unlock(&gpd_list_lock);
-
- return 0;
-}
-late_initcall(genpd_power_off_unused);
-
-#ifdef CONFIG_PM_SLEEP
-
-/**
- * genpd_sync_power_off - Synchronously power off a PM domain and its parents.
- * @genpd: PM domain to power off, if possible.
- * @use_lock: use the lock.
- * @depth: nesting count for lockdep.
- *
- * Check if the given PM domain can be powered off (during system suspend or
- * hibernation) and do that if so. Also, in that case propagate to its parents.
- *
- * This function is only called in "noirq" and "syscore" stages of system power
- * transitions. The "noirq" callbacks may be executed asynchronously, thus in
- * these cases the lock must be held.
- */
-static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
- unsigned int depth)
-{
- struct gpd_link *link;
-
- if (!genpd_status_on(genpd) || genpd_is_always_on(genpd))
- return;
-
- if (genpd->suspended_count != genpd->device_count
- || atomic_read(&genpd->sd_count) > 0)
- return;
-
- /* Check that the children are in their deepest (powered-off) state. */
- list_for_each_entry(link, &genpd->parent_links, parent_node) {
- struct generic_pm_domain *child = link->child;
- if (child->state_idx < child->state_count - 1)
- return;
- }
-
- /* Choose the deepest state when suspending */
- genpd->state_idx = genpd->state_count - 1;
- if (_genpd_power_off(genpd, false))
- return;
-
- genpd->status = GENPD_STATE_OFF;
-
- list_for_each_entry(link, &genpd->child_links, child_node) {
- genpd_sd_counter_dec(link->parent);
-
- if (use_lock)
- genpd_lock_nested(link->parent, depth + 1);
-
- genpd_sync_power_off(link->parent, use_lock, depth + 1);
-
- if (use_lock)
- genpd_unlock(link->parent);
- }
-}
-
-/**
- * genpd_sync_power_on - Synchronously power on a PM domain and its parents.
- * @genpd: PM domain to power on.
- * @use_lock: use the lock.
- * @depth: nesting count for lockdep.
- *
- * This function is only called in "noirq" and "syscore" stages of system power
- * transitions. The "noirq" callbacks may be executed asynchronously, thus in
- * these cases the lock must be held.
- */
-static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
- unsigned int depth)
-{
- struct gpd_link *link;
-
- if (genpd_status_on(genpd))
- return;
-
- list_for_each_entry(link, &genpd->child_links, child_node) {
- genpd_sd_counter_inc(link->parent);
-
- if (use_lock)
- genpd_lock_nested(link->parent, depth + 1);
-
- genpd_sync_power_on(link->parent, use_lock, depth + 1);
-
- if (use_lock)
- genpd_unlock(link->parent);
- }
-
- _genpd_power_on(genpd, false);
- genpd->status = GENPD_STATE_ON;
-}
-
-/**
- * genpd_prepare - Start power transition of a device in a PM domain.
- * @dev: Device to start the transition of.
- *
- * Start a power transition of a device (during a system-wide power transition)
- * under the assumption that its pm_domain field points to the domain member of
- * an object of type struct generic_pm_domain representing a PM domain
- * consisting of I/O devices.
- */
-static int genpd_prepare(struct device *dev)
-{
- struct generic_pm_domain *genpd;
- int ret;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- genpd_lock(genpd);
-
- if (genpd->prepared_count++ == 0)
- genpd->suspended_count = 0;
-
- genpd_unlock(genpd);
-
- ret = pm_generic_prepare(dev);
- if (ret < 0) {
- genpd_lock(genpd);
-
- genpd->prepared_count--;
-
- genpd_unlock(genpd);
- }
-
- /* Never return 1, as genpd don't cope with the direct_complete path. */
- return ret >= 0 ? 0 : ret;
-}
-
-/**
- * genpd_finish_suspend - Completion of suspend or hibernation of device in an
- * I/O pm domain.
- * @dev: Device to suspend.
- * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback.
- *
- * Stop the device and remove power from the domain if all devices in it have
- * been stopped.
- */
-static int genpd_finish_suspend(struct device *dev, bool poweroff)
-{
- struct generic_pm_domain *genpd;
- int ret = 0;
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- if (poweroff)
- ret = pm_generic_poweroff_noirq(dev);
- else
- ret = pm_generic_suspend_noirq(dev);
- if (ret)
- return ret;
-
- if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
- return 0;
-
- if (genpd->dev_ops.stop && genpd->dev_ops.start &&
- !pm_runtime_status_suspended(dev)) {
- ret = genpd_stop_dev(genpd, dev);
- if (ret) {
- if (poweroff)
- pm_generic_restore_noirq(dev);
- else
- pm_generic_resume_noirq(dev);
- return ret;
- }
- }
-
- genpd_lock(genpd);
- genpd->suspended_count++;
- genpd_sync_power_off(genpd, true, 0);
- genpd_unlock(genpd);
-
- return 0;
-}
-
-/**
- * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
- * @dev: Device to suspend.
- *
- * Stop the device and remove power from the domain if all devices in it have
- * been stopped.
- */
-static int genpd_suspend_noirq(struct device *dev)
-{
- dev_dbg(dev, "%s()\n", __func__);
-
- return genpd_finish_suspend(dev, false);
-}
-
-/**
- * genpd_resume_noirq - Start of resume of device in an I/O PM domain.
- * @dev: Device to resume.
- *
- * Restore power to the device's PM domain, if necessary, and start the device.
- */
-static int genpd_resume_noirq(struct device *dev)
-{
- struct generic_pm_domain *genpd;
- int ret;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
- return pm_generic_resume_noirq(dev);
-
- genpd_lock(genpd);
- genpd_sync_power_on(genpd, true, 0);
- genpd->suspended_count--;
- genpd_unlock(genpd);
-
- if (genpd->dev_ops.stop && genpd->dev_ops.start &&
- !pm_runtime_status_suspended(dev)) {
- ret = genpd_start_dev(genpd, dev);
- if (ret)
- return ret;
- }
-
- return pm_generic_resume_noirq(dev);
-}
-
-/**
- * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
- * @dev: Device to freeze.
- *
- * Carry out a late freeze of a device under the assumption that its
- * pm_domain field points to the domain member of an object of type
- * struct generic_pm_domain representing a power domain consisting of I/O
- * devices.
- */
-static int genpd_freeze_noirq(struct device *dev)
-{
- const struct generic_pm_domain *genpd;
- int ret = 0;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- ret = pm_generic_freeze_noirq(dev);
- if (ret)
- return ret;
-
- if (genpd->dev_ops.stop && genpd->dev_ops.start &&
- !pm_runtime_status_suspended(dev))
- ret = genpd_stop_dev(genpd, dev);
-
- return ret;
-}
-
-/**
- * genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
- * @dev: Device to thaw.
- *
- * Start the device, unless power has been removed from the domain already
- * before the system transition.
- */
-static int genpd_thaw_noirq(struct device *dev)
-{
- const struct generic_pm_domain *genpd;
- int ret = 0;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- if (genpd->dev_ops.stop && genpd->dev_ops.start &&
- !pm_runtime_status_suspended(dev)) {
- ret = genpd_start_dev(genpd, dev);
- if (ret)
- return ret;
- }
-
- return pm_generic_thaw_noirq(dev);
-}
-
-/**
- * genpd_poweroff_noirq - Completion of hibernation of device in an
- * I/O PM domain.
- * @dev: Device to poweroff.
- *
- * Stop the device and remove power from the domain if all devices in it have
- * been stopped.
- */
-static int genpd_poweroff_noirq(struct device *dev)
-{
- dev_dbg(dev, "%s()\n", __func__);
-
- return genpd_finish_suspend(dev, true);
-}
-
-/**
- * genpd_restore_noirq - Start of restore of device in an I/O PM domain.
- * @dev: Device to resume.
- *
- * Make sure the domain will be in the same power state as before the
- * hibernation the system is resuming from and start the device if necessary.
- */
-static int genpd_restore_noirq(struct device *dev)
-{
- struct generic_pm_domain *genpd;
- int ret = 0;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return -EINVAL;
-
- /*
- * At this point suspended_count == 0 means we are being run for the
- * first time for the given domain in the present cycle.
- */
- genpd_lock(genpd);
- if (genpd->suspended_count++ == 0) {
- /*
- * The boot kernel might put the domain into arbitrary state,
- * so make it appear as powered off to genpd_sync_power_on(),
- * so that it tries to power it on in case it was really off.
- */
- genpd->status = GENPD_STATE_OFF;
- }
-
- genpd_sync_power_on(genpd, true, 0);
- genpd_unlock(genpd);
-
- if (genpd->dev_ops.stop && genpd->dev_ops.start &&
- !pm_runtime_status_suspended(dev)) {
- ret = genpd_start_dev(genpd, dev);
- if (ret)
- return ret;
- }
-
- return pm_generic_restore_noirq(dev);
-}
-
-/**
- * genpd_complete - Complete power transition of a device in a power domain.
- * @dev: Device to complete the transition of.
- *
- * Complete a power transition of a device (during a system-wide power
- * transition) under the assumption that its pm_domain field points to the
- * domain member of an object of type struct generic_pm_domain representing
- * a power domain consisting of I/O devices.
- */
-static void genpd_complete(struct device *dev)
-{
- struct generic_pm_domain *genpd;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- genpd = dev_to_genpd(dev);
- if (IS_ERR(genpd))
- return;
-
- pm_generic_complete(dev);
-
- genpd_lock(genpd);
-
- genpd->prepared_count--;
- if (!genpd->prepared_count)
- genpd_queue_power_off_work(genpd);
-
- genpd_unlock(genpd);
-}
-
-static void genpd_switch_state(struct device *dev, bool suspend)
-{
- struct generic_pm_domain *genpd;
- bool use_lock;
-
- genpd = dev_to_genpd_safe(dev);
- if (!genpd)
- return;
-
- use_lock = genpd_is_irq_safe(genpd);
-
- if (use_lock)
- genpd_lock(genpd);
-
- if (suspend) {
- genpd->suspended_count++;
- genpd_sync_power_off(genpd, use_lock, 0);
- } else {
- genpd_sync_power_on(genpd, use_lock, 0);
- genpd->suspended_count--;
- }
-
- if (use_lock)
- genpd_unlock(genpd);
-}
-
-/**
- * dev_pm_genpd_suspend - Synchronously try to suspend the genpd for @dev
- * @dev: The device that is attached to the genpd, that can be suspended.
- *
- * This routine should typically be called for a device that needs to be
- * suspended during the syscore suspend phase. It may also be called during
- * suspend-to-idle to suspend a corresponding CPU device that is attached to a
- * genpd.
- */
-void dev_pm_genpd_suspend(struct device *dev)
-{
- genpd_switch_state(dev, true);
-}
-EXPORT_SYMBOL_GPL(dev_pm_genpd_suspend);
-
-/**
- * dev_pm_genpd_resume - Synchronously try to resume the genpd for @dev
- * @dev: The device that is attached to the genpd, which needs to be resumed.
- *
- * This routine should typically be called for a device that needs to be resumed
- * during the syscore resume phase. It may also be called during suspend-to-idle
- * to resume a corresponding CPU device that is attached to a genpd.
- */
-void dev_pm_genpd_resume(struct device *dev)
-{
- genpd_switch_state(dev, false);
-}
-EXPORT_SYMBOL_GPL(dev_pm_genpd_resume);
-
-#else /* !CONFIG_PM_SLEEP */
-
-#define genpd_prepare NULL
-#define genpd_suspend_noirq NULL
-#define genpd_resume_noirq NULL
-#define genpd_freeze_noirq NULL
-#define genpd_thaw_noirq NULL
-#define genpd_poweroff_noirq NULL
-#define genpd_restore_noirq NULL
-#define genpd_complete NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
-static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
- bool has_governor)
-{
- struct generic_pm_domain_data *gpd_data;
- struct gpd_timing_data *td;
- int ret;
-
- ret = dev_pm_get_subsys_data(dev);
- if (ret)
- return ERR_PTR(ret);
-
- gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
- if (!gpd_data) {
- ret = -ENOMEM;
- goto err_put;
- }
-
- gpd_data->base.dev = dev;
- gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
-
- /* Allocate data used by a governor. */
- if (has_governor) {
- td = kzalloc(sizeof(*td), GFP_KERNEL);
- if (!td) {
- ret = -ENOMEM;
- goto err_free;
- }
-
- td->constraint_changed = true;
- td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
- td->next_wakeup = KTIME_MAX;
- gpd_data->td = td;
- }
-
- spin_lock_irq(&dev->power.lock);
-
- if (dev->power.subsys_data->domain_data)
- ret = -EINVAL;
- else
- dev->power.subsys_data->domain_data = &gpd_data->base;
-
- spin_unlock_irq(&dev->power.lock);
-
- if (ret)
- goto err_free;
-
- return gpd_data;
-
- err_free:
- kfree(gpd_data->td);
- kfree(gpd_data);
- err_put:
- dev_pm_put_subsys_data(dev);
- return ERR_PTR(ret);
-}
-
-static void genpd_free_dev_data(struct device *dev,
- struct generic_pm_domain_data *gpd_data)
-{
- spin_lock_irq(&dev->power.lock);
-
- dev->power.subsys_data->domain_data = NULL;
-
- spin_unlock_irq(&dev->power.lock);
-
- kfree(gpd_data->td);
- kfree(gpd_data);
- dev_pm_put_subsys_data(dev);
-}
-
-static void genpd_update_cpumask(struct generic_pm_domain *genpd,
- int cpu, bool set, unsigned int depth)
-{
- struct gpd_link *link;
-
- if (!genpd_is_cpu_domain(genpd))
- return;
-
- list_for_each_entry(link, &genpd->child_links, child_node) {
- struct generic_pm_domain *parent = link->parent;
-
- genpd_lock_nested(parent, depth + 1);
- genpd_update_cpumask(parent, cpu, set, depth + 1);
- genpd_unlock(parent);
- }
-
- if (set)
- cpumask_set_cpu(cpu, genpd->cpus);
- else
- cpumask_clear_cpu(cpu, genpd->cpus);
-}
-
-static void genpd_set_cpumask(struct generic_pm_domain *genpd, int cpu)
-{
- if (cpu >= 0)
- genpd_update_cpumask(genpd, cpu, true, 0);
-}
-
-static void genpd_clear_cpumask(struct generic_pm_domain *genpd, int cpu)
-{
- if (cpu >= 0)
- genpd_update_cpumask(genpd, cpu, false, 0);
-}
-
-static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev)
-{
- int cpu;
-
- if (!genpd_is_cpu_domain(genpd))
- return -1;
-
- for_each_possible_cpu(cpu) {
- if (get_cpu_device(cpu) == dev)
- return cpu;
- }
-
- return -1;
-}
-
-static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
- struct device *base_dev)
-{
- struct genpd_governor_data *gd = genpd->gd;
- struct generic_pm_domain_data *gpd_data;
- int ret;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
- return -EINVAL;
-
- gpd_data = genpd_alloc_dev_data(dev, gd);
- if (IS_ERR(gpd_data))
- return PTR_ERR(gpd_data);
-
- gpd_data->cpu = genpd_get_cpu(genpd, base_dev);
-
- ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0;
- if (ret)
- goto out;
-
- genpd_lock(genpd);
-
- genpd_set_cpumask(genpd, gpd_data->cpu);
- dev_pm_domain_set(dev, &genpd->domain);
-
- genpd->device_count++;
- if (gd)
- gd->max_off_time_changed = true;
-
- list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
-
- genpd_unlock(genpd);
- out:
- if (ret)
- genpd_free_dev_data(dev, gpd_data);
- else
- dev_pm_qos_add_notifier(dev, &gpd_data->nb,
- DEV_PM_QOS_RESUME_LATENCY);
-
- return ret;
-}
-
-/**
- * pm_genpd_add_device - Add a device to an I/O PM domain.
- * @genpd: PM domain to add the device to.
- * @dev: Device to be added.
- */
-int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
-{
- int ret;
-
- mutex_lock(&gpd_list_lock);
- ret = genpd_add_device(genpd, dev, dev);
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_add_device);
-
-static int genpd_remove_device(struct generic_pm_domain *genpd,
- struct device *dev)
-{
- struct generic_pm_domain_data *gpd_data;
- struct pm_domain_data *pdd;
- int ret = 0;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- pdd = dev->power.subsys_data->domain_data;
- gpd_data = to_gpd_data(pdd);
- dev_pm_qos_remove_notifier(dev, &gpd_data->nb,
- DEV_PM_QOS_RESUME_LATENCY);
-
- genpd_lock(genpd);
-
- if (genpd->prepared_count > 0) {
- ret = -EAGAIN;
- goto out;
- }
-
- genpd->device_count--;
- if (genpd->gd)
- genpd->gd->max_off_time_changed = true;
-
- genpd_clear_cpumask(genpd, gpd_data->cpu);
- dev_pm_domain_set(dev, NULL);
-
- list_del_init(&pdd->list_node);
-
- genpd_unlock(genpd);
-
- if (genpd->detach_dev)
- genpd->detach_dev(genpd, dev);
-
- genpd_free_dev_data(dev, gpd_data);
-
- return 0;
-
- out:
- genpd_unlock(genpd);
- dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY);
-
- return ret;
-}
-
-/**
- * pm_genpd_remove_device - Remove a device from an I/O PM domain.
- * @dev: Device to be removed.
- */
-int pm_genpd_remove_device(struct device *dev)
-{
- struct generic_pm_domain *genpd = dev_to_genpd_safe(dev);
-
- if (!genpd)
- return -EINVAL;
-
- return genpd_remove_device(genpd, dev);
-}
-EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
-
-/**
- * dev_pm_genpd_add_notifier - Add a genpd power on/off notifier for @dev
- *
- * @dev: Device that should be associated with the notifier
- * @nb: The notifier block to register
- *
- * Users may call this function to add a genpd power on/off notifier for an
- * attached @dev. Only one notifier per device is allowed. The notifier is
- * sent when genpd is powering on/off the PM domain.
- *
- * It is assumed that the user guarantee that the genpd wouldn't be detached
- * while this routine is getting called.
- *
- * Returns 0 on success and negative error values on failures.
- */
-int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb)
-{
- struct generic_pm_domain *genpd;
- struct generic_pm_domain_data *gpd_data;
- int ret;
-
- genpd = dev_to_genpd_safe(dev);
- if (!genpd)
- return -ENODEV;
-
- if (WARN_ON(!dev->power.subsys_data ||
- !dev->power.subsys_data->domain_data))
- return -EINVAL;
-
- gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
- if (gpd_data->power_nb)
- return -EEXIST;
-
- genpd_lock(genpd);
- ret = raw_notifier_chain_register(&genpd->power_notifiers, nb);
- genpd_unlock(genpd);
-
- if (ret) {
- dev_warn(dev, "failed to add notifier for PM domain %s\n",
- genpd->name);
- return ret;
- }
-
- gpd_data->power_nb = nb;
- return 0;
-}
-EXPORT_SYMBOL_GPL(dev_pm_genpd_add_notifier);
-
-/**
- * dev_pm_genpd_remove_notifier - Remove a genpd power on/off notifier for @dev
- *
- * @dev: Device that is associated with the notifier
- *
- * Users may call this function to remove a genpd power on/off notifier for an
- * attached @dev.
- *
- * It is assumed that the user guarantee that the genpd wouldn't be detached
- * while this routine is getting called.
- *
- * Returns 0 on success and negative error values on failures.
- */
-int dev_pm_genpd_remove_notifier(struct device *dev)
-{
- struct generic_pm_domain *genpd;
- struct generic_pm_domain_data *gpd_data;
- int ret;
-
- genpd = dev_to_genpd_safe(dev);
- if (!genpd)
- return -ENODEV;
-
- if (WARN_ON(!dev->power.subsys_data ||
- !dev->power.subsys_data->domain_data))
- return -EINVAL;
-
- gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
- if (!gpd_data->power_nb)
- return -ENODEV;
-
- genpd_lock(genpd);
- ret = raw_notifier_chain_unregister(&genpd->power_notifiers,
- gpd_data->power_nb);
- genpd_unlock(genpd);
-
- if (ret) {
- dev_warn(dev, "failed to remove notifier for PM domain %s\n",
- genpd->name);
- return ret;
- }
-
- gpd_data->power_nb = NULL;
- return 0;
-}
-EXPORT_SYMBOL_GPL(dev_pm_genpd_remove_notifier);
-
-static int genpd_add_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *subdomain)
-{
- struct gpd_link *link, *itr;
- int ret = 0;
-
- if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)
- || genpd == subdomain)
- return -EINVAL;
-
- /*
- * If the domain can be powered on/off in an IRQ safe
- * context, ensure that the subdomain can also be
- * powered on/off in that context.
- */
- if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) {
- WARN(1, "Parent %s of subdomain %s must be IRQ safe\n",
- genpd->name, subdomain->name);
- return -EINVAL;
- }
-
- link = kzalloc(sizeof(*link), GFP_KERNEL);
- if (!link)
- return -ENOMEM;
-
- genpd_lock(subdomain);
- genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
-
- if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) {
- ret = -EINVAL;
- goto out;
- }
-
- list_for_each_entry(itr, &genpd->parent_links, parent_node) {
- if (itr->child == subdomain && itr->parent == genpd) {
- ret = -EINVAL;
- goto out;
- }
- }
-
- link->parent = genpd;
- list_add_tail(&link->parent_node, &genpd->parent_links);
- link->child = subdomain;
- list_add_tail(&link->child_node, &subdomain->child_links);
- if (genpd_status_on(subdomain))
- genpd_sd_counter_inc(genpd);
-
- out:
- genpd_unlock(genpd);
- genpd_unlock(subdomain);
- if (ret)
- kfree(link);
- return ret;
-}
-
-/**
- * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
- * @genpd: Leader PM domain to add the subdomain to.
- * @subdomain: Subdomain to be added.
- */
-int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *subdomain)
-{
- int ret;
-
- mutex_lock(&gpd_list_lock);
- ret = genpd_add_subdomain(genpd, subdomain);
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
-
-/**
- * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
- * @genpd: Leader PM domain to remove the subdomain from.
- * @subdomain: Subdomain to be removed.
- */
-int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *subdomain)
-{
- struct gpd_link *l, *link;
- int ret = -EINVAL;
-
- if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
- return -EINVAL;
-
- genpd_lock(subdomain);
- genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
-
- if (!list_empty(&subdomain->parent_links) || subdomain->device_count) {
- pr_warn("%s: unable to remove subdomain %s\n",
- genpd->name, subdomain->name);
- ret = -EBUSY;
- goto out;
- }
-
- list_for_each_entry_safe(link, l, &genpd->parent_links, parent_node) {
- if (link->child != subdomain)
- continue;
-
- list_del(&link->parent_node);
- list_del(&link->child_node);
- kfree(link);
- if (genpd_status_on(subdomain))
- genpd_sd_counter_dec(genpd);
-
- ret = 0;
- break;
- }
-
-out:
- genpd_unlock(genpd);
- genpd_unlock(subdomain);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
-
-static void genpd_free_default_power_state(struct genpd_power_state *states,
- unsigned int state_count)
-{
- kfree(states);
-}
-
-static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
-{
- struct genpd_power_state *state;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- genpd->states = state;
- genpd->state_count = 1;
- genpd->free_states = genpd_free_default_power_state;
-
- return 0;
-}
-
-static int genpd_alloc_data(struct generic_pm_domain *genpd)
-{
- struct genpd_governor_data *gd = NULL;
- int ret;
-
- if (genpd_is_cpu_domain(genpd) &&
- !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL))
- return -ENOMEM;
-
- if (genpd->gov) {
- gd = kzalloc(sizeof(*gd), GFP_KERNEL);
- if (!gd) {
- ret = -ENOMEM;
- goto free;
- }
-
- gd->max_off_time_ns = -1;
- gd->max_off_time_changed = true;
- gd->next_wakeup = KTIME_MAX;
- }
-
- /* Use only one "off" state if there were no states declared */
- if (genpd->state_count == 0) {
- ret = genpd_set_default_power_state(genpd);
- if (ret)
- goto free;
- }
-
- genpd->gd = gd;
- return 0;
-
-free:
- if (genpd_is_cpu_domain(genpd))
- free_cpumask_var(genpd->cpus);
- kfree(gd);
- return ret;
-}
-
-static void genpd_free_data(struct generic_pm_domain *genpd)
-{
- if (genpd_is_cpu_domain(genpd))
- free_cpumask_var(genpd->cpus);
- if (genpd->free_states)
- genpd->free_states(genpd->states, genpd->state_count);
- kfree(genpd->gd);
-}
-
-static void genpd_lock_init(struct generic_pm_domain *genpd)
-{
- if (genpd->flags & GENPD_FLAG_IRQ_SAFE) {
- spin_lock_init(&genpd->slock);
- genpd->lock_ops = &genpd_spin_ops;
- } else {
- mutex_init(&genpd->mlock);
- genpd->lock_ops = &genpd_mtx_ops;
- }
-}
-
-/**
- * pm_genpd_init - Initialize a generic I/O PM domain object.
- * @genpd: PM domain object to initialize.
- * @gov: PM domain governor to associate with the domain (may be NULL).
- * @is_off: Initial value of the domain's power_is_off field.
- *
- * Returns 0 on successful initialization, else a negative error code.
- */
-int pm_genpd_init(struct generic_pm_domain *genpd,
- struct dev_power_governor *gov, bool is_off)
-{
- int ret;
-
- if (IS_ERR_OR_NULL(genpd))
- return -EINVAL;
-
- INIT_LIST_HEAD(&genpd->parent_links);
- INIT_LIST_HEAD(&genpd->child_links);
- INIT_LIST_HEAD(&genpd->dev_list);
- RAW_INIT_NOTIFIER_HEAD(&genpd->power_notifiers);
- genpd_lock_init(genpd);
- genpd->gov = gov;
- INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
- atomic_set(&genpd->sd_count, 0);
- genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
- genpd->device_count = 0;
- genpd->provider = NULL;
- genpd->has_provider = false;
- genpd->accounting_time = ktime_get_mono_fast_ns();
- genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
- genpd->domain.ops.runtime_resume = genpd_runtime_resume;
- genpd->domain.ops.prepare = genpd_prepare;
- genpd->domain.ops.suspend_noirq = genpd_suspend_noirq;
- genpd->domain.ops.resume_noirq = genpd_resume_noirq;
- genpd->domain.ops.freeze_noirq = genpd_freeze_noirq;
- genpd->domain.ops.thaw_noirq = genpd_thaw_noirq;
- genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq;
- genpd->domain.ops.restore_noirq = genpd_restore_noirq;
- genpd->domain.ops.complete = genpd_complete;
- genpd->domain.start = genpd_dev_pm_start;
-
- if (genpd->flags & GENPD_FLAG_PM_CLK) {
- genpd->dev_ops.stop = pm_clk_suspend;
- genpd->dev_ops.start = pm_clk_resume;
- }
-
- /* The always-on governor works better with the corresponding flag. */
- if (gov == &pm_domain_always_on_gov)
- genpd->flags |= GENPD_FLAG_RPM_ALWAYS_ON;
-
- /* Always-on domains must be powered on at initialization. */
- if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) &&
- !genpd_status_on(genpd))
- return -EINVAL;
-
- /* Multiple states but no governor doesn't make sense. */
- if (!gov && genpd->state_count > 1)
- pr_warn("%s: no governor for states\n", genpd->name);
-
- ret = genpd_alloc_data(genpd);
- if (ret)
- return ret;
-
- device_initialize(&genpd->dev);
- dev_set_name(&genpd->dev, "%s", genpd->name);
-
- mutex_lock(&gpd_list_lock);
- list_add(&genpd->gpd_list_node, &gpd_list);
- mutex_unlock(&gpd_list_lock);
- genpd_debug_add(genpd);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_init);
-
-static int genpd_remove(struct generic_pm_domain *genpd)
-{
- struct gpd_link *l, *link;
-
- if (IS_ERR_OR_NULL(genpd))
- return -EINVAL;
-
- genpd_lock(genpd);
-
- if (genpd->has_provider) {
- genpd_unlock(genpd);
- pr_err("Provider present, unable to remove %s\n", genpd->name);
- return -EBUSY;
- }
-
- if (!list_empty(&genpd->parent_links) || genpd->device_count) {
- genpd_unlock(genpd);
- pr_err("%s: unable to remove %s\n", __func__, genpd->name);
- return -EBUSY;
- }
-
- list_for_each_entry_safe(link, l, &genpd->child_links, child_node) {
- list_del(&link->parent_node);
- list_del(&link->child_node);
- kfree(link);
- }
-
- list_del(&genpd->gpd_list_node);
- genpd_unlock(genpd);
- genpd_debug_remove(genpd);
- cancel_work_sync(&genpd->power_off_work);
- genpd_free_data(genpd);
-
- pr_debug("%s: removed %s\n", __func__, genpd->name);
-
- return 0;
-}
-
-/**
- * pm_genpd_remove - Remove a generic I/O PM domain
- * @genpd: Pointer to PM domain that is to be removed.
- *
- * To remove the PM domain, this function:
- * - Removes the PM domain as a subdomain to any parent domains,
- * if it was added.
- * - Removes the PM domain from the list of registered PM domains.
- *
- * The PM domain will only be removed, if the associated provider has
- * been removed, it is not a parent to any other PM domain and has no
- * devices associated with it.
- */
-int pm_genpd_remove(struct generic_pm_domain *genpd)
-{
- int ret;
-
- mutex_lock(&gpd_list_lock);
- ret = genpd_remove(genpd);
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_remove);
-
-#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
-
-/*
- * Device Tree based PM domain providers.
- *
- * The code below implements generic device tree based PM domain providers that
- * bind device tree nodes with generic PM domains registered in the system.
- *
- * Any driver that registers generic PM domains and needs to support binding of
- * devices to these domains is supposed to register a PM domain provider, which
- * maps a PM domain specifier retrieved from the device tree to a PM domain.
- *
- * Two simple mapping functions have been provided for convenience:
- * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
- * - genpd_xlate_onecell() for mapping of multiple PM domains per node by
- * index.
- */
-
-/**
- * struct of_genpd_provider - PM domain provider registration structure
- * @link: Entry in global list of PM domain providers
- * @node: Pointer to device tree node of PM domain provider
- * @xlate: Provider-specific xlate callback mapping a set of specifier cells
- * into a PM domain.
- * @data: context pointer to be passed into @xlate callback
- */
-struct of_genpd_provider {
- struct list_head link;
- struct device_node *node;
- genpd_xlate_t xlate;
- void *data;
-};
-
-/* List of registered PM domain providers. */
-static LIST_HEAD(of_genpd_providers);
-/* Mutex to protect the list above. */
-static DEFINE_MUTEX(of_genpd_mutex);
-
-/**
- * genpd_xlate_simple() - Xlate function for direct node-domain mapping
- * @genpdspec: OF phandle args to map into a PM domain
- * @data: xlate function private data - pointer to struct generic_pm_domain
- *
- * This is a generic xlate function that can be used to model PM domains that
- * have their own device tree nodes. The private data of xlate function needs
- * to be a valid pointer to struct generic_pm_domain.
- */
-static struct generic_pm_domain *genpd_xlate_simple(
- struct of_phandle_args *genpdspec,
- void *data)
-{
- return data;
-}
-
-/**
- * genpd_xlate_onecell() - Xlate function using a single index.
- * @genpdspec: OF phandle args to map into a PM domain
- * @data: xlate function private data - pointer to struct genpd_onecell_data
- *
- * This is a generic xlate function that can be used to model simple PM domain
- * controllers that have one device tree node and provide multiple PM domains.
- * A single cell is used as an index into an array of PM domains specified in
- * the genpd_onecell_data struct when registering the provider.
- */
-static struct generic_pm_domain *genpd_xlate_onecell(
- struct of_phandle_args *genpdspec,
- void *data)
-{
- struct genpd_onecell_data *genpd_data = data;
- unsigned int idx = genpdspec->args[0];
-
- if (genpdspec->args_count != 1)
- return ERR_PTR(-EINVAL);
-
- if (idx >= genpd_data->num_domains) {
- pr_err("%s: invalid domain index %u\n", __func__, idx);
- return ERR_PTR(-EINVAL);
- }
-
- if (!genpd_data->domains[idx])
- return ERR_PTR(-ENOENT);
-
- return genpd_data->domains[idx];
-}
-
-/**
- * genpd_add_provider() - Register a PM domain provider for a node
- * @np: Device node pointer associated with the PM domain provider.
- * @xlate: Callback for decoding PM domain from phandle arguments.
- * @data: Context pointer for @xlate callback.
- */
-static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
- void *data)
-{
- struct of_genpd_provider *cp;
-
- cp = kzalloc(sizeof(*cp), GFP_KERNEL);
- if (!cp)
- return -ENOMEM;
-
- cp->node = of_node_get(np);
- cp->data = data;
- cp->xlate = xlate;
- fwnode_dev_initialized(&np->fwnode, true);
-
- mutex_lock(&of_genpd_mutex);
- list_add(&cp->link, &of_genpd_providers);
- mutex_unlock(&of_genpd_mutex);
- pr_debug("Added domain provider from %pOF\n", np);
-
- return 0;
-}
-
-static bool genpd_present(const struct generic_pm_domain *genpd)
-{
- bool ret = false;
- const struct generic_pm_domain *gpd;
-
- mutex_lock(&gpd_list_lock);
- list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
- if (gpd == genpd) {
- ret = true;
- break;
- }
- }
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-
-/**
- * of_genpd_add_provider_simple() - Register a simple PM domain provider
- * @np: Device node pointer associated with the PM domain provider.
- * @genpd: Pointer to PM domain associated with the PM domain provider.
- */
-int of_genpd_add_provider_simple(struct device_node *np,
- struct generic_pm_domain *genpd)
-{
- int ret;
-
- if (!np || !genpd)
- return -EINVAL;
-
- if (!genpd_present(genpd))
- return -EINVAL;
-
- genpd->dev.of_node = np;
-
- /* Parse genpd OPP table */
- if (genpd->set_performance_state) {
- ret = dev_pm_opp_of_add_table(&genpd->dev);
- if (ret)
- return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
-
- /*
- * Save table for faster processing while setting performance
- * state.
- */
- genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
- WARN_ON(IS_ERR(genpd->opp_table));
- }
-
- ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
- if (ret) {
- if (genpd->set_performance_state) {
- dev_pm_opp_put_opp_table(genpd->opp_table);
- dev_pm_opp_of_remove_table(&genpd->dev);
- }
-
- return ret;
- }
-
- genpd->provider = &np->fwnode;
- genpd->has_provider = true;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
-
-/**
- * of_genpd_add_provider_onecell() - Register a onecell PM domain provider
- * @np: Device node pointer associated with the PM domain provider.
- * @data: Pointer to the data associated with the PM domain provider.
- */
-int of_genpd_add_provider_onecell(struct device_node *np,
- struct genpd_onecell_data *data)
-{
- struct generic_pm_domain *genpd;
- unsigned int i;
- int ret = -EINVAL;
-
- if (!np || !data)
- return -EINVAL;
-
- if (!data->xlate)
- data->xlate = genpd_xlate_onecell;
-
- for (i = 0; i < data->num_domains; i++) {
- genpd = data->domains[i];
-
- if (!genpd)
- continue;
- if (!genpd_present(genpd))
- goto error;
-
- genpd->dev.of_node = np;
-
- /* Parse genpd OPP table */
- if (genpd->set_performance_state) {
- ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
- if (ret) {
- dev_err_probe(&genpd->dev, ret,
- "Failed to add OPP table for index %d\n", i);
- goto error;
- }
-
- /*
- * Save table for faster processing while setting
- * performance state.
- */
- genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
- WARN_ON(IS_ERR(genpd->opp_table));
- }
-
- genpd->provider = &np->fwnode;
- genpd->has_provider = true;
- }
-
- ret = genpd_add_provider(np, data->xlate, data);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- while (i--) {
- genpd = data->domains[i];
-
- if (!genpd)
- continue;
-
- genpd->provider = NULL;
- genpd->has_provider = false;
-
- if (genpd->set_performance_state) {
- dev_pm_opp_put_opp_table(genpd->opp_table);
- dev_pm_opp_of_remove_table(&genpd->dev);
- }
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell);
-
-/**
- * of_genpd_del_provider() - Remove a previously registered PM domain provider
- * @np: Device node pointer associated with the PM domain provider
- */
-void of_genpd_del_provider(struct device_node *np)
-{
- struct of_genpd_provider *cp, *tmp;
- struct generic_pm_domain *gpd;
-
- mutex_lock(&gpd_list_lock);
- mutex_lock(&of_genpd_mutex);
- list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) {
- if (cp->node == np) {
- /*
- * For each PM domain associated with the
- * provider, set the 'has_provider' to false
- * so that the PM domain can be safely removed.
- */
- list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
- if (gpd->provider == &np->fwnode) {
- gpd->has_provider = false;
-
- if (!gpd->set_performance_state)
- continue;
-
- dev_pm_opp_put_opp_table(gpd->opp_table);
- dev_pm_opp_of_remove_table(&gpd->dev);
- }
- }
-
- fwnode_dev_initialized(&cp->node->fwnode, false);
- list_del(&cp->link);
- of_node_put(cp->node);
- kfree(cp);
- break;
- }
- }
- mutex_unlock(&of_genpd_mutex);
- mutex_unlock(&gpd_list_lock);
-}
-EXPORT_SYMBOL_GPL(of_genpd_del_provider);
-
-/**
- * genpd_get_from_provider() - Look-up PM domain
- * @genpdspec: OF phandle args to use for look-up
- *
- * Looks for a PM domain provider under the node specified by @genpdspec and if
- * found, uses xlate function of the provider to map phandle args to a PM
- * domain.
- *
- * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
- * on failure.
- */
-static struct generic_pm_domain *genpd_get_from_provider(
- struct of_phandle_args *genpdspec)
-{
- struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
- struct of_genpd_provider *provider;
-
- if (!genpdspec)
- return ERR_PTR(-EINVAL);
-
- mutex_lock(&of_genpd_mutex);
-
- /* Check if we have such a provider in our array */
- list_for_each_entry(provider, &of_genpd_providers, link) {
- if (provider->node == genpdspec->np)
- genpd = provider->xlate(genpdspec, provider->data);
- if (!IS_ERR(genpd))
- break;
- }
-
- mutex_unlock(&of_genpd_mutex);
-
- return genpd;
-}
-
-/**
- * of_genpd_add_device() - Add a device to an I/O PM domain
- * @genpdspec: OF phandle args to use for look-up PM domain
- * @dev: Device to be added.
- *
- * Looks-up an I/O PM domain based upon phandle args provided and adds
- * the device to the PM domain. Returns a negative error code on failure.
- */
-int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
-{
- struct generic_pm_domain *genpd;
- int ret;
-
- mutex_lock(&gpd_list_lock);
-
- genpd = genpd_get_from_provider(genpdspec);
- if (IS_ERR(genpd)) {
- ret = PTR_ERR(genpd);
- goto out;
- }
-
- ret = genpd_add_device(genpd, dev, dev);
-
-out:
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(of_genpd_add_device);
-
-/**
- * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
- * @parent_spec: OF phandle args to use for parent PM domain look-up
- * @subdomain_spec: OF phandle args to use for subdomain look-up
- *
- * Looks-up a parent PM domain and subdomain based upon phandle args
- * provided and adds the subdomain to the parent PM domain. Returns a
- * negative error code on failure.
- */
-int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
- struct of_phandle_args *subdomain_spec)
-{
- struct generic_pm_domain *parent, *subdomain;
- int ret;
-
- mutex_lock(&gpd_list_lock);
-
- parent = genpd_get_from_provider(parent_spec);
- if (IS_ERR(parent)) {
- ret = PTR_ERR(parent);
- goto out;
- }
-
- subdomain = genpd_get_from_provider(subdomain_spec);
- if (IS_ERR(subdomain)) {
- ret = PTR_ERR(subdomain);
- goto out;
- }
-
- ret = genpd_add_subdomain(parent, subdomain);
-
-out:
- mutex_unlock(&gpd_list_lock);
-
- return ret == -ENOENT ? -EPROBE_DEFER : ret;
-}
-EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
-
-/**
- * of_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
- * @parent_spec: OF phandle args to use for parent PM domain look-up
- * @subdomain_spec: OF phandle args to use for subdomain look-up
- *
- * Looks-up a parent PM domain and subdomain based upon phandle args
- * provided and removes the subdomain from the parent PM domain. Returns a
- * negative error code on failure.
- */
-int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
- struct of_phandle_args *subdomain_spec)
-{
- struct generic_pm_domain *parent, *subdomain;
- int ret;
-
- mutex_lock(&gpd_list_lock);
-
- parent = genpd_get_from_provider(parent_spec);
- if (IS_ERR(parent)) {
- ret = PTR_ERR(parent);
- goto out;
- }
-
- subdomain = genpd_get_from_provider(subdomain_spec);
- if (IS_ERR(subdomain)) {
- ret = PTR_ERR(subdomain);
- goto out;
- }
-
- ret = pm_genpd_remove_subdomain(parent, subdomain);
-
-out:
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain);
-
-/**
- * of_genpd_remove_last - Remove the last PM domain registered for a provider
- * @np: Pointer to device node associated with provider
- *
- * Find the last PM domain that was added by a particular provider and
- * remove this PM domain from the list of PM domains. The provider is
- * identified by the 'provider' device structure that is passed. The PM
- * domain will only be removed, if the provider associated with domain
- * has been removed.
- *
- * Returns a valid pointer to struct generic_pm_domain on success or
- * ERR_PTR() on failure.
- */
-struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
-{
- struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT);
- int ret;
-
- if (IS_ERR_OR_NULL(np))
- return ERR_PTR(-EINVAL);
-
- mutex_lock(&gpd_list_lock);
- list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) {
- if (gpd->provider == &np->fwnode) {
- ret = genpd_remove(gpd);
- genpd = ret ? ERR_PTR(ret) : gpd;
- break;
- }
- }
- mutex_unlock(&gpd_list_lock);
-
- return genpd;
-}
-EXPORT_SYMBOL_GPL(of_genpd_remove_last);
-
-static void genpd_release_dev(struct device *dev)
-{
- of_node_put(dev->of_node);
- kfree(dev);
-}
-
-static struct bus_type genpd_bus_type = {
- .name = "genpd",
-};
-
-/**
- * genpd_dev_pm_detach - Detach a device from its PM domain.
- * @dev: Device to detach.
- * @power_off: Currently not used
- *
- * Try to locate a corresponding generic PM domain, which the device was
- * attached to previously. If such is found, the device is detached from it.
- */
-static void genpd_dev_pm_detach(struct device *dev, bool power_off)
-{
- struct generic_pm_domain *pd;
- unsigned int i;
- int ret = 0;
-
- pd = dev_to_genpd(dev);
- if (IS_ERR(pd))
- return;
-
- dev_dbg(dev, "removing from PM domain %s\n", pd->name);
-
- /* Drop the default performance state */
- if (dev_gpd_data(dev)->default_pstate) {
- dev_pm_genpd_set_performance_state(dev, 0);
- dev_gpd_data(dev)->default_pstate = 0;
- }
-
- for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
- ret = genpd_remove_device(pd, dev);
- if (ret != -EAGAIN)
- break;
-
- mdelay(i);
- cond_resched();
- }
-
- if (ret < 0) {
- dev_err(dev, "failed to remove from PM domain %s: %d",
- pd->name, ret);
- return;
- }
-
- /* Check if PM domain can be powered off after removing this device. */
- genpd_queue_power_off_work(pd);
-
- /* Unregister the device if it was created by genpd. */
- if (dev->bus == &genpd_bus_type)
- device_unregister(dev);
-}
-
-static void genpd_dev_pm_sync(struct device *dev)
-{
- struct generic_pm_domain *pd;
-
- pd = dev_to_genpd(dev);
- if (IS_ERR(pd))
- return;
-
- genpd_queue_power_off_work(pd);
-}
-
-static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
- unsigned int index, bool power_on)
-{
- struct of_phandle_args pd_args;
- struct generic_pm_domain *pd;
- int pstate;
- int ret;
-
- ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
- "#power-domain-cells", index, &pd_args);
- if (ret < 0)
- return ret;
-
- mutex_lock(&gpd_list_lock);
- pd = genpd_get_from_provider(&pd_args);
- of_node_put(pd_args.np);
- if (IS_ERR(pd)) {
- mutex_unlock(&gpd_list_lock);
- dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
- __func__, PTR_ERR(pd));
- return driver_deferred_probe_check_state(base_dev);
- }
-
- dev_dbg(dev, "adding to PM domain %s\n", pd->name);
-
- ret = genpd_add_device(pd, dev, base_dev);
- mutex_unlock(&gpd_list_lock);
-
- if (ret < 0)
- return dev_err_probe(dev, ret, "failed to add to PM domain %s\n", pd->name);
-
- dev->pm_domain->detach = genpd_dev_pm_detach;
- dev->pm_domain->sync = genpd_dev_pm_sync;
-
- if (power_on) {
- genpd_lock(pd);
- ret = genpd_power_on(pd, 0);
- genpd_unlock(pd);
- }
-
- if (ret) {
- genpd_remove_device(pd, dev);
- return -EPROBE_DEFER;
- }
-
- /* Set the default performance state */
- pstate = of_get_required_opp_performance_state(dev->of_node, index);
- if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) {
- ret = pstate;
- goto err;
- } else if (pstate > 0) {
- ret = dev_pm_genpd_set_performance_state(dev, pstate);
- if (ret)
- goto err;
- dev_gpd_data(dev)->default_pstate = pstate;
- }
- return 1;
-
-err:
- dev_err(dev, "failed to set required performance state for power-domain %s: %d\n",
- pd->name, ret);
- genpd_remove_device(pd, dev);
- return ret;
-}
-
-/**
- * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
- * @dev: Device to attach.
- *
- * Parse device's OF node to find a PM domain specifier. If such is found,
- * attaches the device to retrieved pm_domain ops.
- *
- * Returns 1 on successfully attached PM domain, 0 when the device don't need a
- * PM domain or when multiple power-domains exists for it, else a negative error
- * code. Note that if a power-domain exists for the device, but it cannot be
- * found or turned on, then return -EPROBE_DEFER to ensure that the device is
- * not probed and to re-try again later.
- */
-int genpd_dev_pm_attach(struct device *dev)
-{
- if (!dev->of_node)
- return 0;
-
- /*
- * Devices with multiple PM domains must be attached separately, as we
- * can only attach one PM domain per device.
- */
- if (of_count_phandle_with_args(dev->of_node, "power-domains",
- "#power-domain-cells") != 1)
- return 0;
-
- return __genpd_dev_pm_attach(dev, dev, 0, true);
-}
-EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
-
-/**
- * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains.
- * @dev: The device used to lookup the PM domain.
- * @index: The index of the PM domain.
- *
- * Parse device's OF node to find a PM domain specifier at the provided @index.
- * If such is found, creates a virtual device and attaches it to the retrieved
- * pm_domain ops. To deal with detaching of the virtual device, the ->detach()
- * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach().
- *
- * Returns the created virtual device if successfully attached PM domain, NULL
- * when the device don't need a PM domain, else an ERR_PTR() in case of
- * failures. If a power-domain exists for the device, but cannot be found or
- * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device
- * is not probed and to re-try again later.
- */
-struct device *genpd_dev_pm_attach_by_id(struct device *dev,
- unsigned int index)
-{
- struct device *virt_dev;
- int num_domains;
- int ret;
-
- if (!dev->of_node)
- return NULL;
-
- /* Verify that the index is within a valid range. */
- num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
- "#power-domain-cells");
- if (index >= num_domains)
- return NULL;
-
- /* Allocate and register device on the genpd bus. */
- virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
- if (!virt_dev)
- return ERR_PTR(-ENOMEM);
-
- dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev));
- virt_dev->bus = &genpd_bus_type;
- virt_dev->release = genpd_release_dev;
- virt_dev->of_node = of_node_get(dev->of_node);
-
- ret = device_register(virt_dev);
- if (ret) {
- put_device(virt_dev);
- return ERR_PTR(ret);
- }
-
- /* Try to attach the device to the PM domain at the specified index. */
- ret = __genpd_dev_pm_attach(virt_dev, dev, index, false);
- if (ret < 1) {
- device_unregister(virt_dev);
- return ret ? ERR_PTR(ret) : NULL;
- }
-
- pm_runtime_enable(virt_dev);
- genpd_queue_power_off_work(dev_to_genpd(virt_dev));
-
- return virt_dev;
-}
-EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
-
-/**
- * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains.
- * @dev: The device used to lookup the PM domain.
- * @name: The name of the PM domain.
- *
- * Parse device's OF node to find a PM domain specifier using the
- * power-domain-names DT property. For further description see
- * genpd_dev_pm_attach_by_id().
- */
-struct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name)
-{
- int index;
-
- if (!dev->of_node)
- return NULL;
-
- index = of_property_match_string(dev->of_node, "power-domain-names",
- name);
- if (index < 0)
- return NULL;
-
- return genpd_dev_pm_attach_by_id(dev, index);
-}
-
-static const struct of_device_id idle_state_match[] = {
- { .compatible = "domain-idle-state", },
- { }
-};
-
-static int genpd_parse_state(struct genpd_power_state *genpd_state,
- struct device_node *state_node)
-{
- int err;
- u32 residency;
- u32 entry_latency, exit_latency;
-
- err = of_property_read_u32(state_node, "entry-latency-us",
- &entry_latency);
- if (err) {
- pr_debug(" * %pOF missing entry-latency-us property\n",
- state_node);
- return -EINVAL;
- }
-
- err = of_property_read_u32(state_node, "exit-latency-us",
- &exit_latency);
- if (err) {
- pr_debug(" * %pOF missing exit-latency-us property\n",
- state_node);
- return -EINVAL;
- }
-
- err = of_property_read_u32(state_node, "min-residency-us", &residency);
- if (!err)
- genpd_state->residency_ns = 1000 * residency;
-
- genpd_state->power_on_latency_ns = 1000 * exit_latency;
- genpd_state->power_off_latency_ns = 1000 * entry_latency;
- genpd_state->fwnode = &state_node->fwnode;
-
- return 0;
-}
-
-static int genpd_iterate_idle_states(struct device_node *dn,
- struct genpd_power_state *states)
-{
- int ret;
- struct of_phandle_iterator it;
- struct device_node *np;
- int i = 0;
-
- ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL);
- if (ret <= 0)
- return ret == -ENOENT ? 0 : ret;
-
- /* Loop over the phandles until all the requested entry is found */
- of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) {
- np = it.node;
- if (!of_match_node(idle_state_match, np))
- continue;
- if (states) {
- ret = genpd_parse_state(&states[i], np);
- if (ret) {
- pr_err("Parsing idle state node %pOF failed with err %d\n",
- np, ret);
- of_node_put(np);
- return ret;
- }
- }
- i++;
- }
-
- return i;
-}
-
-/**
- * of_genpd_parse_idle_states: Return array of idle states for the genpd.
- *
- * @dn: The genpd device node
- * @states: The pointer to which the state array will be saved.
- * @n: The count of elements in the array returned from this function.
- *
- * Returns the device states parsed from the OF node. The memory for the states
- * is allocated by this function and is the responsibility of the caller to
- * free the memory after use. If any or zero compatible domain idle states is
- * found it returns 0 and in case of errors, a negative error code is returned.
- */
-int of_genpd_parse_idle_states(struct device_node *dn,
- struct genpd_power_state **states, int *n)
-{
- struct genpd_power_state *st;
- int ret;
-
- ret = genpd_iterate_idle_states(dn, NULL);
- if (ret < 0)
- return ret;
-
- if (!ret) {
- *states = NULL;
- *n = 0;
- return 0;
- }
-
- st = kcalloc(ret, sizeof(*st), GFP_KERNEL);
- if (!st)
- return -ENOMEM;
-
- ret = genpd_iterate_idle_states(dn, st);
- if (ret <= 0) {
- kfree(st);
- return ret < 0 ? ret : -EINVAL;
- }
-
- *states = st;
- *n = ret;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
-
-/**
- * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node.
- *
- * @genpd_dev: Genpd's device for which the performance-state needs to be found.
- * @opp: struct dev_pm_opp of the OPP for which we need to find performance
- * state.
- *
- * Returns performance state encoded in the OPP of the genpd. This calls
- * platform specific genpd->opp_to_performance_state() callback to translate
- * power domain OPP to performance state.
- *
- * Returns performance state on success and 0 on failure.
- */
-unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev,
- struct dev_pm_opp *opp)
-{
- struct generic_pm_domain *genpd = NULL;
- int state;
-
- genpd = container_of(genpd_dev, struct generic_pm_domain, dev);
-
- if (unlikely(!genpd->opp_to_performance_state))
- return 0;
-
- genpd_lock(genpd);
- state = genpd->opp_to_performance_state(genpd, opp);
- genpd_unlock(genpd);
-
- return state;
-}
-EXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state);
-
-static int __init genpd_bus_init(void)
-{
- return bus_register(&genpd_bus_type);
-}
-core_initcall(genpd_bus_init);
-
-#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
-
-
-/*** debugfs support ***/
-
-#ifdef CONFIG_DEBUG_FS
-/*
- * TODO: This function is a slightly modified version of rtpm_status_show
- * from sysfs.c, so generalize it.
- */
-static void rtpm_status_str(struct seq_file *s, struct device *dev)
-{
- static const char * const status_lookup[] = {
- [RPM_ACTIVE] = "active",
- [RPM_RESUMING] = "resuming",
- [RPM_SUSPENDED] = "suspended",
- [RPM_SUSPENDING] = "suspending"
- };
- const char *p = "";
-
- if (dev->power.runtime_error)
- p = "error";
- else if (dev->power.disable_depth)
- p = "unsupported";
- else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup))
- p = status_lookup[dev->power.runtime_status];
- else
- WARN_ON(1);
-
- seq_printf(s, "%-25s ", p);
-}
-
-static void perf_status_str(struct seq_file *s, struct device *dev)
-{
- struct generic_pm_domain_data *gpd_data;
-
- gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
- seq_put_decimal_ull(s, "", gpd_data->performance_state);
-}
-
-static int genpd_summary_one(struct seq_file *s,
- struct generic_pm_domain *genpd)
-{
- static const char * const status_lookup[] = {
- [GENPD_STATE_ON] = "on",
- [GENPD_STATE_OFF] = "off"
- };
- struct pm_domain_data *pm_data;
- const char *kobj_path;
- struct gpd_link *link;
- char state[16];
- int ret;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup)))
- goto exit;
- if (!genpd_status_on(genpd))
- snprintf(state, sizeof(state), "%s-%u",
- status_lookup[genpd->status], genpd->state_idx);
- else
- snprintf(state, sizeof(state), "%s",
- status_lookup[genpd->status]);
- seq_printf(s, "%-30s %-50s %u", genpd->name, state, genpd->performance_state);
-
- /*
- * Modifications on the list require holding locks on both
- * parent and child, so we are safe.
- * Also genpd->name is immutable.
- */
- list_for_each_entry(link, &genpd->parent_links, parent_node) {
- if (list_is_first(&link->parent_node, &genpd->parent_links))
- seq_printf(s, "\n%48s", " ");
- seq_printf(s, "%s", link->child->name);
- if (!list_is_last(&link->parent_node, &genpd->parent_links))
- seq_puts(s, ", ");
- }
-
- list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
- kobj_path = kobject_get_path(&pm_data->dev->kobj,
- genpd_is_irq_safe(genpd) ?
- GFP_ATOMIC : GFP_KERNEL);
- if (kobj_path == NULL)
- continue;
-
- seq_printf(s, "\n %-50s ", kobj_path);
- rtpm_status_str(s, pm_data->dev);
- perf_status_str(s, pm_data->dev);
- kfree(kobj_path);
- }
-
- seq_puts(s, "\n");
-exit:
- genpd_unlock(genpd);
-
- return 0;
-}
-
-static int summary_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd;
- int ret = 0;
-
- seq_puts(s, "domain status children performance\n");
- seq_puts(s, " /device runtime status\n");
- seq_puts(s, "----------------------------------------------------------------------------------------------\n");
-
- ret = mutex_lock_interruptible(&gpd_list_lock);
- if (ret)
- return -ERESTARTSYS;
-
- list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
- ret = genpd_summary_one(s, genpd);
- if (ret)
- break;
- }
- mutex_unlock(&gpd_list_lock);
-
- return ret;
-}
-
-static int status_show(struct seq_file *s, void *data)
-{
- static const char * const status_lookup[] = {
- [GENPD_STATE_ON] = "on",
- [GENPD_STATE_OFF] = "off"
- };
-
- struct generic_pm_domain *genpd = s->private;
- int ret = 0;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup)))
- goto exit;
-
- if (genpd->status == GENPD_STATE_OFF)
- seq_printf(s, "%s-%u\n", status_lookup[genpd->status],
- genpd->state_idx);
- else
- seq_printf(s, "%s\n", status_lookup[genpd->status]);
-exit:
- genpd_unlock(genpd);
- return ret;
-}
-
-static int sub_domains_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd = s->private;
- struct gpd_link *link;
- int ret = 0;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- list_for_each_entry(link, &genpd->parent_links, parent_node)
- seq_printf(s, "%s\n", link->child->name);
-
- genpd_unlock(genpd);
- return ret;
-}
-
-static int idle_states_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd = s->private;
- u64 now, delta, idle_time = 0;
- unsigned int i;
- int ret = 0;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- seq_puts(s, "State Time Spent(ms) Usage Rejected\n");
-
- for (i = 0; i < genpd->state_count; i++) {
- idle_time += genpd->states[i].idle_time;
-
- if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) {
- now = ktime_get_mono_fast_ns();
- if (now > genpd->accounting_time) {
- delta = now - genpd->accounting_time;
- idle_time += delta;
- }
- }
-
- do_div(idle_time, NSEC_PER_MSEC);
- seq_printf(s, "S%-13i %-14llu %-14llu %llu\n", i, idle_time,
- genpd->states[i].usage, genpd->states[i].rejected);
- }
-
- genpd_unlock(genpd);
- return ret;
-}
-
-static int active_time_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd = s->private;
- u64 now, on_time, delta = 0;
- int ret = 0;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- if (genpd->status == GENPD_STATE_ON) {
- now = ktime_get_mono_fast_ns();
- if (now > genpd->accounting_time)
- delta = now - genpd->accounting_time;
- }
-
- on_time = genpd->on_time + delta;
- do_div(on_time, NSEC_PER_MSEC);
- seq_printf(s, "%llu ms\n", on_time);
-
- genpd_unlock(genpd);
- return ret;
-}
-
-static int total_idle_time_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd = s->private;
- u64 now, delta, total = 0;
- unsigned int i;
- int ret = 0;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- for (i = 0; i < genpd->state_count; i++) {
- total += genpd->states[i].idle_time;
-
- if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) {
- now = ktime_get_mono_fast_ns();
- if (now > genpd->accounting_time) {
- delta = now - genpd->accounting_time;
- total += delta;
- }
- }
- }
-
- do_div(total, NSEC_PER_MSEC);
- seq_printf(s, "%llu ms\n", total);
-
- genpd_unlock(genpd);
- return ret;
-}
-
-
-static int devices_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd = s->private;
- struct pm_domain_data *pm_data;
- const char *kobj_path;
- int ret = 0;
-
- ret = genpd_lock_interruptible(genpd);
- if (ret)
- return -ERESTARTSYS;
-
- list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
- kobj_path = kobject_get_path(&pm_data->dev->kobj,
- genpd_is_irq_safe(genpd) ?
- GFP_ATOMIC : GFP_KERNEL);
- if (kobj_path == NULL)
- continue;
-
- seq_printf(s, "%s\n", kobj_path);
- kfree(kobj_path);
- }
-
- genpd_unlock(genpd);
- return ret;
-}
-
-static int perf_state_show(struct seq_file *s, void *data)
-{
- struct generic_pm_domain *genpd = s->private;
-
- if (genpd_lock_interruptible(genpd))
- return -ERESTARTSYS;
-
- seq_printf(s, "%u\n", genpd->performance_state);
-
- genpd_unlock(genpd);
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(summary);
-DEFINE_SHOW_ATTRIBUTE(status);
-DEFINE_SHOW_ATTRIBUTE(sub_domains);
-DEFINE_SHOW_ATTRIBUTE(idle_states);
-DEFINE_SHOW_ATTRIBUTE(active_time);
-DEFINE_SHOW_ATTRIBUTE(total_idle_time);
-DEFINE_SHOW_ATTRIBUTE(devices);
-DEFINE_SHOW_ATTRIBUTE(perf_state);
-
-static void genpd_debug_add(struct generic_pm_domain *genpd)
-{
- struct dentry *d;
-
- if (!genpd_debugfs_dir)
- return;
-
- d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
-
- debugfs_create_file("current_state", 0444,
- d, genpd, &status_fops);
- debugfs_create_file("sub_domains", 0444,
- d, genpd, &sub_domains_fops);
- debugfs_create_file("idle_states", 0444,
- d, genpd, &idle_states_fops);
- debugfs_create_file("active_time", 0444,
- d, genpd, &active_time_fops);
- debugfs_create_file("total_idle_time", 0444,
- d, genpd, &total_idle_time_fops);
- debugfs_create_file("devices", 0444,
- d, genpd, &devices_fops);
- if (genpd->set_performance_state)
- debugfs_create_file("perf_state", 0444,
- d, genpd, &perf_state_fops);
-}
-
-static int __init genpd_debug_init(void)
-{
- struct generic_pm_domain *genpd;
-
- genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
-
- debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir,
- NULL, &summary_fops);
-
- list_for_each_entry(genpd, &gpd_list, gpd_list_node)
- genpd_debug_add(genpd);
-
- return 0;
-}
-late_initcall(genpd_debug_init);
-
-static void __exit genpd_debug_exit(void)
-{
- debugfs_remove_recursive(genpd_debugfs_dir);
-}
-__exitcall(genpd_debug_exit);
-#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
deleted file mode 100644
index 282a3a135827..000000000000
--- a/drivers/base/power/domain_governor.c
+++ /dev/null
@@ -1,411 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/base/power/domain_governor.c - Governors for device PM domains.
- *
- * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
- */
-#include <linux/kernel.h>
-#include <linux/pm_domain.h>
-#include <linux/pm_qos.h>
-#include <linux/hrtimer.h>
-#include <linux/cpuidle.h>
-#include <linux/cpumask.h>
-#include <linux/ktime.h>
-
-static int dev_update_qos_constraint(struct device *dev, void *data)
-{
- s64 *constraint_ns_p = data;
- s64 constraint_ns;
-
- if (dev->power.subsys_data && dev->power.subsys_data->domain_data) {
- struct gpd_timing_data *td = dev_gpd_data(dev)->td;
-
- /*
- * Only take suspend-time QoS constraints of devices into
- * account, because constraints updated after the device has
- * been suspended are not guaranteed to be taken into account
- * anyway. In order for them to take effect, the device has to
- * be resumed and suspended again.
- */
- constraint_ns = td ? td->effective_constraint_ns :
- PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
- } else {
- /*
- * The child is not in a domain and there's no info on its
- * suspend/resume latencies, so assume them to be negligible and
- * take its current PM QoS constraint (that's the only thing
- * known at this point anyway).
- */
- constraint_ns = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY);
- constraint_ns *= NSEC_PER_USEC;
- }
-
- if (constraint_ns < *constraint_ns_p)
- *constraint_ns_p = constraint_ns;
-
- return 0;
-}
-
-/**
- * default_suspend_ok - Default PM domain governor routine to suspend devices.
- * @dev: Device to check.
- */
-static bool default_suspend_ok(struct device *dev)
-{
- struct gpd_timing_data *td = dev_gpd_data(dev)->td;
- unsigned long flags;
- s64 constraint_ns;
-
- dev_dbg(dev, "%s()\n", __func__);
-
- spin_lock_irqsave(&dev->power.lock, flags);
-
- if (!td->constraint_changed) {
- bool ret = td->cached_suspend_ok;
-
- spin_unlock_irqrestore(&dev->power.lock, flags);
- return ret;
- }
- td->constraint_changed = false;
- td->cached_suspend_ok = false;
- td->effective_constraint_ns = 0;
- constraint_ns = __dev_pm_qos_resume_latency(dev);
-
- spin_unlock_irqrestore(&dev->power.lock, flags);
-
- if (constraint_ns == 0)
- return false;
-
- constraint_ns *= NSEC_PER_USEC;
- /*
- * We can walk the children without any additional locking, because
- * they all have been suspended at this point and their
- * effective_constraint_ns fields won't be modified in parallel with us.
- */
- if (!dev->power.ignore_children)
- device_for_each_child(dev, &constraint_ns,
- dev_update_qos_constraint);
-
- if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS) {
- /* "No restriction", so the device is allowed to suspend. */
- td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
- td->cached_suspend_ok = true;
- } else if (constraint_ns == 0) {
- /*
- * This triggers if one of the children that don't belong to a
- * domain has a zero PM QoS constraint and it's better not to
- * suspend then. effective_constraint_ns is zero already and
- * cached_suspend_ok is false, so bail out.
- */
- return false;
- } else {
- constraint_ns -= td->suspend_latency_ns +
- td->resume_latency_ns;
- /*
- * effective_constraint_ns is zero already and cached_suspend_ok
- * is false, so if the computed value is not positive, return
- * right away.
- */
- if (constraint_ns <= 0)
- return false;
-
- td->effective_constraint_ns = constraint_ns;
- td->cached_suspend_ok = true;
- }
-
- /*
- * The children have been suspended already, so we don't need to take
- * their suspend latencies into account here.
- */
- return td->cached_suspend_ok;
-}
-
-static void update_domain_next_wakeup(struct generic_pm_domain *genpd, ktime_t now)
-{
- ktime_t domain_wakeup = KTIME_MAX;
- ktime_t next_wakeup;
- struct pm_domain_data *pdd;
- struct gpd_link *link;
-
- if (!(genpd->flags & GENPD_FLAG_MIN_RESIDENCY))
- return;
-
- /*
- * Devices that have a predictable wakeup pattern, may specify
- * their next wakeup. Let's find the next wakeup from all the
- * devices attached to this domain and from all the sub-domains.
- * It is possible that component's a next wakeup may have become
- * stale when we read that here. We will ignore to ensure the domain
- * is able to enter its optimal idle state.
- */
- list_for_each_entry(pdd, &genpd->dev_list, list_node) {
- next_wakeup = to_gpd_data(pdd)->td->next_wakeup;
- if (next_wakeup != KTIME_MAX && !ktime_before(next_wakeup, now))
- if (ktime_before(next_wakeup, domain_wakeup))
- domain_wakeup = next_wakeup;
- }
-
- list_for_each_entry(link, &genpd->parent_links, parent_node) {
- struct genpd_governor_data *cgd = link->child->gd;
-
- next_wakeup = cgd ? cgd->next_wakeup : KTIME_MAX;
- if (next_wakeup != KTIME_MAX && !ktime_before(next_wakeup, now))
- if (ktime_before(next_wakeup, domain_wakeup))
- domain_wakeup = next_wakeup;
- }
-
- genpd->gd->next_wakeup = domain_wakeup;
-}
-
-static bool next_wakeup_allows_state(struct generic_pm_domain *genpd,
- unsigned int state, ktime_t now)
-{
- ktime_t domain_wakeup = genpd->gd->next_wakeup;
- s64 idle_time_ns, min_sleep_ns;
-
- min_sleep_ns = genpd->states[state].power_off_latency_ns +
- genpd->states[state].residency_ns;
-
- idle_time_ns = ktime_to_ns(ktime_sub(domain_wakeup, now));
-
- return idle_time_ns >= min_sleep_ns;
-}
-
-static bool __default_power_down_ok(struct dev_pm_domain *pd,
- unsigned int state)
-{
- struct generic_pm_domain *genpd = pd_to_genpd(pd);
- struct gpd_link *link;
- struct pm_domain_data *pdd;
- s64 min_off_time_ns;
- s64 off_on_time_ns;
-
- off_on_time_ns = genpd->states[state].power_off_latency_ns +
- genpd->states[state].power_on_latency_ns;
-
- min_off_time_ns = -1;
- /*
- * Check if subdomains can be off for enough time.
- *
- * All subdomains have been powered off already at this point.
- */
- list_for_each_entry(link, &genpd->parent_links, parent_node) {
- struct genpd_governor_data *cgd = link->child->gd;
-
- s64 sd_max_off_ns = cgd ? cgd->max_off_time_ns : -1;
-
- if (sd_max_off_ns < 0)
- continue;
-
- /*
- * Check if the subdomain is allowed to be off long enough for
- * the current domain to turn off and on (that's how much time
- * it will have to wait worst case).
- */
- if (sd_max_off_ns <= off_on_time_ns)
- return false;
-
- if (min_off_time_ns > sd_max_off_ns || min_off_time_ns < 0)
- min_off_time_ns = sd_max_off_ns;
- }
-
- /*
- * Check if the devices in the domain can be off enough time.
- */
- list_for_each_entry(pdd, &genpd->dev_list, list_node) {
- struct gpd_timing_data *td;
- s64 constraint_ns;
-
- /*
- * Check if the device is allowed to be off long enough for the
- * domain to turn off and on (that's how much time it will
- * have to wait worst case).
- */
- td = to_gpd_data(pdd)->td;
- constraint_ns = td->effective_constraint_ns;
- /*
- * Zero means "no suspend at all" and this runs only when all
- * devices in the domain are suspended, so it must be positive.
- */
- if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS)
- continue;
-
- if (constraint_ns <= off_on_time_ns)
- return false;
-
- if (min_off_time_ns > constraint_ns || min_off_time_ns < 0)
- min_off_time_ns = constraint_ns;
- }
-
- /*
- * If the computed minimum device off time is negative, there are no
- * latency constraints, so the domain can spend arbitrary time in the
- * "off" state.
- */
- if (min_off_time_ns < 0)
- return true;
-
- /*
- * The difference between the computed minimum subdomain or device off
- * time and the time needed to turn the domain on is the maximum
- * theoretical time this domain can spend in the "off" state.
- */
- genpd->gd->max_off_time_ns = min_off_time_ns -
- genpd->states[state].power_on_latency_ns;
- return true;
-}
-
-/**
- * _default_power_down_ok - Default generic PM domain power off governor routine.
- * @pd: PM domain to check.
- * @now: current ktime.
- *
- * This routine must be executed under the PM domain's lock.
- */
-static bool _default_power_down_ok(struct dev_pm_domain *pd, ktime_t now)
-{
- struct generic_pm_domain *genpd = pd_to_genpd(pd);
- struct genpd_governor_data *gd = genpd->gd;
- int state_idx = genpd->state_count - 1;
- struct gpd_link *link;
-
- /*
- * Find the next wakeup from devices that can determine their own wakeup
- * to find when the domain would wakeup and do it for every device down
- * the hierarchy. It is not worth while to sleep if the state's residency
- * cannot be met.
- */
- update_domain_next_wakeup(genpd, now);
- if ((genpd->flags & GENPD_FLAG_MIN_RESIDENCY) && (gd->next_wakeup != KTIME_MAX)) {
- /* Let's find out the deepest domain idle state, the devices prefer */
- while (state_idx >= 0) {
- if (next_wakeup_allows_state(genpd, state_idx, now)) {
- gd->max_off_time_changed = true;
- break;
- }
- state_idx--;
- }
-
- if (state_idx < 0) {
- state_idx = 0;
- gd->cached_power_down_ok = false;
- goto done;
- }
- }
-
- if (!gd->max_off_time_changed) {
- genpd->state_idx = gd->cached_power_down_state_idx;
- return gd->cached_power_down_ok;
- }
-
- /*
- * We have to invalidate the cached results for the parents, so
- * use the observation that default_power_down_ok() is not
- * going to be called for any parent until this instance
- * returns.
- */
- list_for_each_entry(link, &genpd->child_links, child_node) {
- struct genpd_governor_data *pgd = link->parent->gd;
-
- if (pgd)
- pgd->max_off_time_changed = true;
- }
-
- gd->max_off_time_ns = -1;
- gd->max_off_time_changed = false;
- gd->cached_power_down_ok = true;
-
- /*
- * Find a state to power down to, starting from the state
- * determined by the next wakeup.
- */
- while (!__default_power_down_ok(pd, state_idx)) {
- if (state_idx == 0) {
- gd->cached_power_down_ok = false;
- break;
- }
- state_idx--;
- }
-
-done:
- genpd->state_idx = state_idx;
- gd->cached_power_down_state_idx = genpd->state_idx;
- return gd->cached_power_down_ok;
-}
-
-static bool default_power_down_ok(struct dev_pm_domain *pd)
-{
- return _default_power_down_ok(pd, ktime_get());
-}
-
-#ifdef CONFIG_CPU_IDLE
-static bool cpu_power_down_ok(struct dev_pm_domain *pd)
-{
- struct generic_pm_domain *genpd = pd_to_genpd(pd);
- struct cpuidle_device *dev;
- ktime_t domain_wakeup, next_hrtimer;
- ktime_t now = ktime_get();
- s64 idle_duration_ns;
- int cpu, i;
-
- /* Validate dev PM QoS constraints. */
- if (!_default_power_down_ok(pd, now))
- return false;
-
- if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN))
- return true;
-
- /*
- * Find the next wakeup for any of the online CPUs within the PM domain
- * and its subdomains. Note, we only need the genpd->cpus, as it already
- * contains a mask of all CPUs from subdomains.
- */
- domain_wakeup = ktime_set(KTIME_SEC_MAX, 0);
- for_each_cpu_and(cpu, genpd->cpus, cpu_online_mask) {
- dev = per_cpu(cpuidle_devices, cpu);
- if (dev) {
- next_hrtimer = READ_ONCE(dev->next_hrtimer);
- if (ktime_before(next_hrtimer, domain_wakeup))
- domain_wakeup = next_hrtimer;
- }
- }
-
- /* The minimum idle duration is from now - until the next wakeup. */
- idle_duration_ns = ktime_to_ns(ktime_sub(domain_wakeup, now));
- if (idle_duration_ns <= 0)
- return false;
-
- /*
- * Find the deepest idle state that has its residency value satisfied
- * and by also taking into account the power off latency for the state.
- * Start at the state picked by the dev PM QoS constraint validation.
- */
- i = genpd->state_idx;
- do {
- if (idle_duration_ns >= (genpd->states[i].residency_ns +
- genpd->states[i].power_off_latency_ns)) {
- genpd->state_idx = i;
- return true;
- }
- } while (--i >= 0);
-
- return false;
-}
-
-struct dev_power_governor pm_domain_cpu_gov = {
- .suspend_ok = default_suspend_ok,
- .power_down_ok = cpu_power_down_ok,
-};
-#endif
-
-struct dev_power_governor simple_qos_governor = {
- .suspend_ok = default_suspend_ok,
- .power_down_ok = default_power_down_ok,
-};
-
-/**
- * pm_genpd_gov_always_on - A governor implementing an always-on policy
- */
-struct dev_power_governor pm_domain_always_on_gov = {
- .suspend_ok = default_suspend_ok,
-};
diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c
index 4fa525668cb7..af99bbcf281c 100644
--- a/drivers/base/power/generic_ops.c
+++ b/drivers/base/power/generic_ops.c
@@ -8,6 +8,13 @@
#include <linux/pm_runtime.h>
#include <linux/export.h>
+#define CALL_PM_OP(dev, op) \
+({ \
+ struct device *_dev = (dev); \
+ const struct dev_pm_ops *pm = _dev->driver ? _dev->driver->pm : NULL; \
+ pm && pm->op ? pm->op(_dev) : 0; \
+})
+
#ifdef CONFIG_PM
/**
* pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
@@ -19,12 +26,7 @@
*/
int pm_generic_runtime_suspend(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- int ret;
-
- ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
-
- return ret;
+ return CALL_PM_OP(dev, runtime_suspend);
}
EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
@@ -38,12 +40,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
*/
int pm_generic_runtime_resume(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- int ret;
-
- ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
-
- return ret;
+ return CALL_PM_OP(dev, runtime_resume);
}
EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
#endif /* CONFIG_PM */
@@ -72,9 +69,7 @@ int pm_generic_prepare(struct device *dev)
*/
int pm_generic_suspend_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0;
+ return CALL_PM_OP(dev, suspend_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
@@ -84,9 +79,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
*/
int pm_generic_suspend_late(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->suspend_late ? pm->suspend_late(dev) : 0;
+ return CALL_PM_OP(dev, suspend_late);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
@@ -96,9 +89,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
*/
int pm_generic_suspend(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->suspend ? pm->suspend(dev) : 0;
+ return CALL_PM_OP(dev, suspend);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend);
@@ -108,33 +99,17 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend);
*/
int pm_generic_freeze_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0;
+ return CALL_PM_OP(dev, freeze_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
/**
- * pm_generic_freeze_late - Generic freeze_late callback for subsystems.
- * @dev: Device to freeze.
- */
-int pm_generic_freeze_late(struct device *dev)
-{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->freeze_late ? pm->freeze_late(dev) : 0;
-}
-EXPORT_SYMBOL_GPL(pm_generic_freeze_late);
-
-/**
* pm_generic_freeze - Generic freeze callback for subsystems.
* @dev: Device to freeze.
*/
int pm_generic_freeze(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->freeze ? pm->freeze(dev) : 0;
+ return CALL_PM_OP(dev, freeze);
}
EXPORT_SYMBOL_GPL(pm_generic_freeze);
@@ -144,9 +119,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze);
*/
int pm_generic_poweroff_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0;
+ return CALL_PM_OP(dev, poweroff_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
@@ -156,9 +129,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
*/
int pm_generic_poweroff_late(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0;
+ return CALL_PM_OP(dev, poweroff_late);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
@@ -168,9 +139,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
*/
int pm_generic_poweroff(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->poweroff ? pm->poweroff(dev) : 0;
+ return CALL_PM_OP(dev, poweroff);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff);
@@ -180,33 +149,17 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff);
*/
int pm_generic_thaw_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0;
+ return CALL_PM_OP(dev, thaw_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
/**
- * pm_generic_thaw_early - Generic thaw_early callback for subsystems.
- * @dev: Device to thaw.
- */
-int pm_generic_thaw_early(struct device *dev)
-{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->thaw_early ? pm->thaw_early(dev) : 0;
-}
-EXPORT_SYMBOL_GPL(pm_generic_thaw_early);
-
-/**
* pm_generic_thaw - Generic thaw callback for subsystems.
* @dev: Device to thaw.
*/
int pm_generic_thaw(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->thaw ? pm->thaw(dev) : 0;
+ return CALL_PM_OP(dev, thaw);
}
EXPORT_SYMBOL_GPL(pm_generic_thaw);
@@ -216,9 +169,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
*/
int pm_generic_resume_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0;
+ return CALL_PM_OP(dev, resume_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
@@ -228,9 +179,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
*/
int pm_generic_resume_early(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->resume_early ? pm->resume_early(dev) : 0;
+ return CALL_PM_OP(dev, resume_early);
}
EXPORT_SYMBOL_GPL(pm_generic_resume_early);
@@ -240,9 +189,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_early);
*/
int pm_generic_resume(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->resume ? pm->resume(dev) : 0;
+ return CALL_PM_OP(dev, resume);
}
EXPORT_SYMBOL_GPL(pm_generic_resume);
@@ -252,9 +199,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume);
*/
int pm_generic_restore_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0;
+ return CALL_PM_OP(dev, restore_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
@@ -264,9 +209,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
*/
int pm_generic_restore_early(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->restore_early ? pm->restore_early(dev) : 0;
+ return CALL_PM_OP(dev, restore_early);
}
EXPORT_SYMBOL_GPL(pm_generic_restore_early);
@@ -276,9 +219,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_early);
*/
int pm_generic_restore(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->restore ? pm->restore(dev) : 0;
+ return CALL_PM_OP(dev, restore);
}
EXPORT_SYMBOL_GPL(pm_generic_restore);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index c50139207794..97a8b4fcf471 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -34,16 +34,13 @@
#include <linux/cpufreq.h>
#include <linux/devfreq.h>
#include <linux/timer.h>
+#include <linux/nmi.h>
#include "../base.h"
#include "power.h"
typedef int (*pm_callback_t)(struct device *);
-#define list_for_each_entry_rcu_locked(pos, head, member) \
- list_for_each_entry_rcu(pos, head, member, \
- device_links_read_lock_held())
-
/*
* The entries in the dpm_list list are in a depth first order, simply
* because children are guaranteed to be discovered after parents, and
@@ -60,12 +57,26 @@ static LIST_HEAD(dpm_suspended_list);
static LIST_HEAD(dpm_late_early_list);
static LIST_HEAD(dpm_noirq_list);
-struct suspend_stats suspend_stats;
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
+static DEFINE_MUTEX(async_wip_mtx);
static int async_error;
+/**
+ * pm_hibernate_is_recovering - if recovering from hibernate due to error.
+ *
+ * Used to query if dev_pm_ops.thaw() is called for normal hibernation case or
+ * recovering from some error.
+ *
+ * Return: true for error case, false for normal case.
+ */
+bool pm_hibernate_is_recovering(void)
+{
+ return pm_transition.event == PM_EVENT_RECOVER;
+}
+EXPORT_SYMBOL_GPL(pm_hibernate_is_recovering);
+
static const char *pm_verb(int event)
{
switch (event) {
@@ -85,6 +96,8 @@ static const char *pm_verb(int event)
return "restore";
case PM_EVENT_RECOVER:
return "recover";
+ case PM_EVENT_POWEROFF:
+ return "poweroff";
default:
return "(unknown PM event)";
}
@@ -209,7 +222,7 @@ static ktime_t initcall_debug_start(struct device *dev, void *cb)
if (!pm_print_times_enabled)
return 0;
- dev_info(dev, "calling %pS @ %i, parent: %s\n", cb,
+ dev_info(dev, "calling %ps @ %i, parent: %s\n", cb,
task_pid_nr(current),
dev->parent ? dev_name(dev->parent) : "none");
return ktime_get();
@@ -224,7 +237,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
return;
rettime = ktime_get();
- dev_info(dev, "%pS returned %d after %Ld usecs\n", cb, error,
+ dev_info(dev, "%ps returned %d after %Ld usecs\n", cb, error,
(unsigned long long)ktime_us_delta(rettime, calltime));
}
@@ -250,7 +263,7 @@ static int dpm_wait_fn(struct device *dev, void *async_ptr)
static void dpm_wait_for_children(struct device *dev, bool async)
{
- device_for_each_child(dev, &async, dpm_wait_fn);
+ device_for_each_child(dev, &async, dpm_wait_fn);
}
static void dpm_wait_for_suppliers(struct device *dev, bool async)
@@ -267,8 +280,9 @@ static void dpm_wait_for_suppliers(struct device *dev, bool async)
* callbacks freeing the link objects for the links in the list we're
* walking.
*/
- list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node)
- if (READ_ONCE(link->status) != DL_STATE_DORMANT)
+ dev_for_each_link_to_supplier(link, dev)
+ if (READ_ONCE(link->status) != DL_STATE_DORMANT &&
+ !device_link_flag_is_sync_state_only(link->flags))
dpm_wait(link->supplier, async);
device_links_read_unlock(idx);
@@ -324,8 +338,9 @@ static void dpm_wait_for_consumers(struct device *dev, bool async)
* continue instead of trying to continue in parallel with its
* unregistration).
*/
- list_for_each_entry_rcu_locked(link, &dev->links.consumers, s_node)
- if (READ_ONCE(link->status) != DL_STATE_DORMANT)
+ dev_for_each_link_to_consumer(link, dev)
+ if (READ_ONCE(link->status) != DL_STATE_DORMANT &&
+ !device_link_flag_is_sync_state_only(link->flags))
dpm_wait(link->consumer, async);
device_links_read_unlock(idx);
@@ -355,6 +370,7 @@ static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze;
+ case PM_EVENT_POWEROFF:
case PM_EVENT_HIBERNATE:
return ops->poweroff;
case PM_EVENT_THAW:
@@ -389,6 +405,7 @@ static pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops,
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze_late;
+ case PM_EVENT_POWEROFF:
case PM_EVENT_HIBERNATE:
return ops->poweroff_late;
case PM_EVENT_THAW:
@@ -423,6 +440,7 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze_noirq;
+ case PM_EVENT_POWEROFF:
case PM_EVENT_HIBERNATE:
return ops->poweroff_noirq;
case PM_EVENT_THAW:
@@ -497,11 +515,17 @@ struct dpm_watchdog {
struct device *dev;
struct task_struct *tsk;
struct timer_list timer;
+ bool fatal;
};
#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
struct dpm_watchdog wd
+static bool __read_mostly dpm_watchdog_all_cpu_backtrace;
+module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644);
+MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace,
+ "Backtrace all CPUs on DPM watchdog timeout");
+
/**
* dpm_watchdog_handler - Driver suspend / resume watchdog handler.
* @t: The timer that PM watchdog depends on.
@@ -512,12 +536,28 @@ struct dpm_watchdog {
*/
static void dpm_watchdog_handler(struct timer_list *t)
{
- struct dpm_watchdog *wd = from_timer(wd, t, timer);
+ struct dpm_watchdog *wd = timer_container_of(wd, t, timer);
+ struct timer_list *timer = &wd->timer;
+ unsigned int time_left;
+
+ if (wd->fatal) {
+ unsigned int this_cpu = smp_processor_id();
- dev_emerg(wd->dev, "**** DPM device timeout ****\n");
- show_stack(wd->tsk, NULL, KERN_EMERG);
- panic("%s %s: unrecoverable failure\n",
- dev_driver_string(wd->dev), dev_name(wd->dev));
+ dev_emerg(wd->dev, "**** DPM device timeout ****\n");
+ show_stack(wd->tsk, NULL, KERN_EMERG);
+ if (dpm_watchdog_all_cpu_backtrace)
+ trigger_allbutcpu_cpu_backtrace(this_cpu);
+ panic("%s %s: unrecoverable failure\n",
+ dev_driver_string(wd->dev), dev_name(wd->dev));
+ }
+
+ time_left = CONFIG_DPM_WATCHDOG_TIMEOUT - CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
+ dev_warn(wd->dev, "**** DPM device timeout after %u seconds; %u seconds until panic ****\n",
+ CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT, time_left);
+ show_stack(wd->tsk, NULL, KERN_WARNING);
+
+ wd->fatal = true;
+ mod_timer(timer, jiffies + HZ * time_left);
}
/**
@@ -531,10 +571,11 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
wd->dev = dev;
wd->tsk = current;
+ wd->fatal = CONFIG_DPM_WATCHDOG_TIMEOUT == CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
timer_setup_on_stack(timer, dpm_watchdog_handler, 0);
/* use same timeout value for both suspend and resume */
- timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT;
+ timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
add_timer(timer);
}
@@ -546,8 +587,8 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
{
struct timer_list *timer = &wd->timer;
- del_timer_sync(timer);
- destroy_timer_on_stack(timer);
+ timer_delete_sync(timer);
+ timer_destroy_on_stack(timer);
}
#else
#define DECLARE_DPM_WATCHDOG_ON_STACK(wd)
@@ -578,6 +619,102 @@ bool dev_pm_skip_resume(struct device *dev)
return !dev->power.must_resume;
}
+static bool is_async(struct device *dev)
+{
+ return dev->power.async_suspend && pm_async_enabled
+ && !pm_trace_is_enabled();
+}
+
+static bool __dpm_async(struct device *dev, async_func_t func)
+{
+ if (dev->power.work_in_progress)
+ return true;
+
+ if (!is_async(dev))
+ return false;
+
+ dev->power.work_in_progress = true;
+
+ get_device(dev);
+
+ if (async_schedule_dev_nocall(func, dev))
+ return true;
+
+ put_device(dev);
+
+ return false;
+}
+
+static bool dpm_async_fn(struct device *dev, async_func_t func)
+{
+ guard(mutex)(&async_wip_mtx);
+
+ return __dpm_async(dev, func);
+}
+
+static int dpm_async_with_cleanup(struct device *dev, void *fn)
+{
+ guard(mutex)(&async_wip_mtx);
+
+ if (!__dpm_async(dev, fn))
+ dev->power.work_in_progress = false;
+
+ return 0;
+}
+
+static void dpm_async_resume_children(struct device *dev, async_func_t func)
+{
+ /*
+ * Prevent racing with dpm_clear_async_state() during initial list
+ * walks in dpm_noirq_resume_devices(), dpm_resume_early(), and
+ * dpm_resume().
+ */
+ guard(mutex)(&dpm_list_mtx);
+
+ /*
+ * Start processing "async" children of the device unless it's been
+ * started already for them.
+ */
+ device_for_each_child(dev, func, dpm_async_with_cleanup);
+}
+
+static void dpm_async_resume_subordinate(struct device *dev, async_func_t func)
+{
+ struct device_link *link;
+ int idx;
+
+ dpm_async_resume_children(dev, func);
+
+ idx = device_links_read_lock();
+
+ /* Start processing the device's "async" consumers. */
+ dev_for_each_link_to_consumer(link, dev)
+ if (READ_ONCE(link->status) != DL_STATE_DORMANT)
+ dpm_async_with_cleanup(link->consumer, func);
+
+ device_links_read_unlock(idx);
+}
+
+static void dpm_clear_async_state(struct device *dev)
+{
+ reinit_completion(&dev->power.completion);
+ dev->power.work_in_progress = false;
+}
+
+static bool dpm_root_device(struct device *dev)
+{
+ lockdep_assert_held(&dpm_list_mtx);
+
+ /*
+ * Since this function is required to run under dpm_list_mtx, the
+ * list_empty() below will only return true if the device's list of
+ * consumers is actually empty before calling it.
+ */
+ return !dev->parent && list_empty(&dev->links.suppliers);
+}
+
+static void async_resume_noirq(void *data, async_cookie_t cookie);
+
/**
* device_resume_noirq - Execute a "noirq resume" callback for given device.
* @dev: Device to handle.
@@ -587,7 +724,7 @@ bool dev_pm_skip_resume(struct device *dev)
* The driver of @dev will not receive interrupts while this function is being
* executed.
*/
-static int device_resume_noirq(struct device *dev, pm_message_t state, bool async)
+static void device_resume_noirq(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -600,8 +737,20 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
if (dev->power.syscore || dev->power.direct_complete)
goto Out;
- if (!dev->power.is_noirq_suspended)
+ if (!dev->power.is_noirq_suspended) {
+ /*
+ * This means that system suspend has been aborted in the noirq
+ * phase before invoking the noirq suspend callback for the
+ * device, so if device_suspend_late() has left it in suspend,
+ * device_resume_early() should leave it in suspend either in
+ * case the early resume of it depends on the noirq resume that
+ * has not run.
+ */
+ if (dev_pm_skip_suspend(dev))
+ dev->power.must_resume = false;
+
goto Out;
+ }
if (!dpm_wait_for_superior(dev, async))
goto Out;
@@ -614,12 +763,12 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
* so change its status accordingly.
*
* Otherwise, the device is going to be resumed, so set its PM-runtime
- * status to "active", but do that only if DPM_FLAG_SMART_SUSPEND is set
- * to avoid confusing drivers that don't use it.
+ * status to "active" unless its power.smart_suspend flag is clear, in
+ * which case it is not necessary to update its PM-runtime status.
*/
if (skip_resume)
pm_runtime_set_suspended(dev);
- else if (dev_pm_skip_suspend(dev))
+ else if (dev_pm_smart_suspend(dev))
pm_runtime_set_active(dev);
if (dev->pm_domain) {
@@ -655,37 +804,21 @@ Skip:
Out:
complete_all(&dev->power.completion);
TRACE_RESUME(error);
- return error;
-}
-
-static bool is_async(struct device *dev)
-{
- return dev->power.async_suspend && pm_async_enabled
- && !pm_trace_is_enabled();
-}
-
-static bool dpm_async_fn(struct device *dev, async_func_t func)
-{
- reinit_completion(&dev->power.completion);
- if (is_async(dev)) {
- get_device(dev);
- async_schedule_dev(func, dev);
- return true;
+ if (error) {
+ WRITE_ONCE(async_error, error);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
}
- return false;
+ dpm_async_resume_subordinate(dev, async_resume_noirq);
}
static void async_resume_noirq(void *data, async_cookie_t cookie)
{
- struct device *dev = (struct device *)data;
- int error;
-
- error = device_resume_noirq(dev, pm_transition, true);
- if (error)
- pm_dev_err(dev, pm_transition, " async", error);
+ struct device *dev = data;
+ device_resume_noirq(dev, pm_transition, true);
put_device(dev);
}
@@ -695,43 +828,44 @@ static void dpm_noirq_resume_devices(pm_message_t state)
ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true);
- mutex_lock(&dpm_list_mtx);
+
+ async_error = 0;
pm_transition = state;
+ mutex_lock(&dpm_list_mtx);
+
/*
- * Advanced the async threads upfront,
- * in case the starting of async threads is
- * delayed by non-async resuming devices.
+ * Start processing "async" root devices upfront so they don't wait for
+ * the "sync" devices they don't depend on.
*/
- list_for_each_entry(dev, &dpm_noirq_list, power.entry)
- dpm_async_fn(dev, async_resume_noirq);
+ list_for_each_entry(dev, &dpm_noirq_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_root_device(dev))
+ dpm_async_with_cleanup(dev, async_resume_noirq);
+ }
while (!list_empty(&dpm_noirq_list)) {
dev = to_device(dpm_noirq_list.next);
- get_device(dev);
list_move_tail(&dev->power.entry, &dpm_late_early_list);
- mutex_unlock(&dpm_list_mtx);
+ if (!dpm_async_fn(dev, async_resume_noirq)) {
+ get_device(dev);
- if (!is_async(dev)) {
- int error;
+ mutex_unlock(&dpm_list_mtx);
- error = device_resume_noirq(dev, state, false);
- if (error) {
- suspend_stats.failed_resume_noirq++;
- dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
- dpm_save_failed_dev(dev_name(dev));
- pm_dev_err(dev, state, " noirq", error);
- }
- }
+ device_resume_noirq(dev, state, false);
- put_device(dev);
+ put_device(dev);
- mutex_lock(&dpm_list_mtx);
+ mutex_lock(&dpm_list_mtx);
+ }
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, 0, "noirq");
+ if (READ_ONCE(async_error))
+ dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
+
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
}
@@ -750,6 +884,8 @@ void dpm_resume_noirq(pm_message_t state)
device_wakeup_disarm_wake_irqs();
}
+static void async_resume_early(void *data, async_cookie_t cookie);
+
/**
* device_resume_early - Execute an "early resume" callback for given device.
* @dev: Device to handle.
@@ -758,7 +894,7 @@ void dpm_resume_noirq(pm_message_t state)
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
-static int device_resume_early(struct device *dev, pm_message_t state, bool async)
+static void device_resume_early(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -767,12 +903,15 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn
TRACE_DEVICE(dev);
TRACE_RESUME(0);
- if (dev->power.syscore || dev->power.direct_complete)
+ if (dev->power.direct_complete)
goto Out;
if (!dev->power.is_late_suspended)
goto Out;
+ if (dev->power.syscore)
+ goto Skip;
+
if (!dpm_wait_for_superior(dev, async))
goto Out;
@@ -805,24 +944,27 @@ Run:
Skip:
dev->power.is_late_suspended = false;
+ pm_runtime_enable(dev);
Out:
TRACE_RESUME(error);
- pm_runtime_enable(dev);
complete_all(&dev->power.completion);
- return error;
+
+ if (error) {
+ WRITE_ONCE(async_error, error);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, async ? " async early" : " early", error);
+ }
+
+ dpm_async_resume_subordinate(dev, async_resume_early);
}
static void async_resume_early(void *data, async_cookie_t cookie)
{
- struct device *dev = (struct device *)data;
- int error;
-
- error = device_resume_early(dev, pm_transition, true);
- if (error)
- pm_dev_err(dev, pm_transition, " async", error);
+ struct device *dev = data;
+ device_resume_early(dev, pm_transition, true);
put_device(dev);
}
@@ -836,43 +978,44 @@ void dpm_resume_early(pm_message_t state)
ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume_early"), state.event, true);
- mutex_lock(&dpm_list_mtx);
+
+ async_error = 0;
pm_transition = state;
+ mutex_lock(&dpm_list_mtx);
+
/*
- * Advanced the async threads upfront,
- * in case the starting of async threads is
- * delayed by non-async resuming devices.
+ * Start processing "async" root devices upfront so they don't wait for
+ * the "sync" devices they don't depend on.
*/
- list_for_each_entry(dev, &dpm_late_early_list, power.entry)
- dpm_async_fn(dev, async_resume_early);
+ list_for_each_entry(dev, &dpm_late_early_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_root_device(dev))
+ dpm_async_with_cleanup(dev, async_resume_early);
+ }
while (!list_empty(&dpm_late_early_list)) {
dev = to_device(dpm_late_early_list.next);
- get_device(dev);
list_move_tail(&dev->power.entry, &dpm_suspended_list);
- mutex_unlock(&dpm_list_mtx);
+ if (!dpm_async_fn(dev, async_resume_early)) {
+ get_device(dev);
- if (!is_async(dev)) {
- int error;
+ mutex_unlock(&dpm_list_mtx);
- error = device_resume_early(dev, state, false);
- if (error) {
- suspend_stats.failed_resume_early++;
- dpm_save_failed_step(SUSPEND_RESUME_EARLY);
- dpm_save_failed_dev(dev_name(dev));
- pm_dev_err(dev, state, " early", error);
- }
- }
+ device_resume_early(dev, state, false);
- put_device(dev);
+ put_device(dev);
- mutex_lock(&dpm_list_mtx);
+ mutex_lock(&dpm_list_mtx);
+ }
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, 0, "early");
+ if (READ_ONCE(async_error))
+ dpm_save_failed_step(SUSPEND_RESUME_EARLY);
+
trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
}
@@ -887,13 +1030,15 @@ void dpm_resume_start(pm_message_t state)
}
EXPORT_SYMBOL_GPL(dpm_resume_start);
+static void async_resume(void *data, async_cookie_t cookie);
+
/**
* device_resume - Execute "resume" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*/
-static int device_resume(struct device *dev, pm_message_t state, bool async)
+static void device_resume(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -906,8 +1051,20 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
if (dev->power.syscore)
goto Complete;
+ if (!dev->power.is_suspended)
+ goto Complete;
+
+ dev->power.is_suspended = false;
+
if (dev->power.direct_complete) {
- /* Match the pm_runtime_disable() in __device_suspend(). */
+ /*
+ * Allow new children to be added under the device after this
+ * point if it has no PM callbacks.
+ */
+ if (dev->power.no_pm_callbacks)
+ dev->power.is_prepared = false;
+
+ /* Match the pm_runtime_disable() in device_suspend(). */
pm_runtime_enable(dev);
goto Complete;
}
@@ -924,9 +1081,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
*/
dev->power.is_prepared = false;
- if (!dev->power.is_suspended)
- goto Unlock;
-
if (dev->pm_domain) {
info = "power domain ";
callback = pm_op(&dev->pm_domain->ops, state);
@@ -964,9 +1118,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
End:
error = dpm_run_callback(callback, dev, state, info);
- dev->power.is_suspended = false;
- Unlock:
device_unlock(dev);
dpm_watchdog_clear(&wd);
@@ -975,17 +1127,20 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
TRACE_RESUME(error);
- return error;
+ if (error) {
+ WRITE_ONCE(async_error, error);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, async ? " async" : "", error);
+ }
+
+ dpm_async_resume_subordinate(dev, async_resume);
}
static void async_resume(void *data, async_cookie_t cookie)
{
- struct device *dev = (struct device *)data;
- int error;
+ struct device *dev = data;
- error = device_resume(dev, pm_transition, true);
- if (error)
- pm_dev_err(dev, pm_transition, " async", error);
+ device_resume(dev, pm_transition, true);
put_device(dev);
}
@@ -1002,45 +1157,43 @@ void dpm_resume(pm_message_t state)
ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume"), state.event, true);
- might_sleep();
- mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
- list_for_each_entry(dev, &dpm_suspended_list, power.entry)
- dpm_async_fn(dev, async_resume);
+ mutex_lock(&dpm_list_mtx);
+
+ /*
+ * Start processing "async" root devices upfront so they don't wait for
+ * the "sync" devices they don't depend on.
+ */
+ list_for_each_entry(dev, &dpm_suspended_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_root_device(dev))
+ dpm_async_with_cleanup(dev, async_resume);
+ }
while (!list_empty(&dpm_suspended_list)) {
dev = to_device(dpm_suspended_list.next);
- get_device(dev);
- if (!is_async(dev)) {
- int error;
+ list_move_tail(&dev->power.entry, &dpm_prepared_list);
+
+ if (!dpm_async_fn(dev, async_resume)) {
+ get_device(dev);
mutex_unlock(&dpm_list_mtx);
- error = device_resume(dev, state, false);
- if (error) {
- suspend_stats.failed_resume++;
- dpm_save_failed_step(SUSPEND_RESUME);
- dpm_save_failed_dev(dev_name(dev));
- pm_dev_err(dev, state, "", error);
- }
+ device_resume(dev, state, false);
+
+ put_device(dev);
mutex_lock(&dpm_list_mtx);
}
- if (!list_empty(&dev->power.entry))
- list_move_tail(&dev->power.entry, &dpm_prepared_list);
-
- mutex_unlock(&dpm_list_mtx);
-
- put_device(dev);
-
- mutex_lock(&dpm_list_mtx);
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, 0, NULL);
+ if (READ_ONCE(async_error))
+ dpm_save_failed_step(SUSPEND_RESUME);
cpufreq_resume();
devfreq_resume();
@@ -1089,6 +1242,8 @@ static void device_complete(struct device *dev, pm_message_t state)
device_unlock(dev);
out:
+ /* If enabling runtime PM for the device is blocked, unblock it. */
+ pm_runtime_unblock(dev);
pm_runtime_put(dev);
}
@@ -1104,7 +1259,6 @@ void dpm_complete(pm_message_t state)
struct list_head list;
trace_suspend_resume(TPS("dpm_complete"), state.event, true);
- might_sleep();
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
@@ -1143,6 +1297,7 @@ void dpm_complete(pm_message_t state)
void dpm_resume_end(pm_message_t state)
{
dpm_resume(state);
+ pm_restore_gfp_mask();
dpm_complete(state);
}
EXPORT_SYMBOL_GPL(dpm_resume_end);
@@ -1150,6 +1305,82 @@ EXPORT_SYMBOL_GPL(dpm_resume_end);
/*------------------------- Suspend routines -------------------------*/
+static bool dpm_leaf_device(struct device *dev)
+{
+ struct device *child;
+
+ lockdep_assert_held(&dpm_list_mtx);
+
+ child = device_find_any_child(dev);
+ if (child) {
+ put_device(child);
+
+ return false;
+ }
+
+ /*
+ * Since this function is required to run under dpm_list_mtx, the
+ * list_empty() below will only return true if the device's list of
+ * consumers is actually empty before calling it.
+ */
+ return list_empty(&dev->links.consumers);
+}
+
+static bool dpm_async_suspend_parent(struct device *dev, async_func_t func)
+{
+ guard(mutex)(&dpm_list_mtx);
+
+ /*
+ * If the device is suspended asynchronously and the parent's callback
+ * deletes both the device and the parent itself, the parent object may
+ * be freed while this function is running, so avoid that by checking
+ * if the device has been deleted already as the parent cannot be
+ * deleted before it.
+ */
+ if (!device_pm_initialized(dev))
+ return false;
+
+ /* Start processing the device's parent if it is "async". */
+ if (dev->parent)
+ dpm_async_with_cleanup(dev->parent, func);
+
+ return true;
+}
+
+static void dpm_async_suspend_superior(struct device *dev, async_func_t func)
+{
+ struct device_link *link;
+ int idx;
+
+ if (!dpm_async_suspend_parent(dev, func))
+ return;
+
+ idx = device_links_read_lock();
+
+ /* Start processing the device's "async" suppliers. */
+ dev_for_each_link_to_supplier(link, dev)
+ if (READ_ONCE(link->status) != DL_STATE_DORMANT)
+ dpm_async_with_cleanup(link->supplier, func);
+
+ device_links_read_unlock(idx);
+}
+
+static void dpm_async_suspend_complete_all(struct list_head *device_list)
+{
+ struct device *dev;
+
+ guard(mutex)(&async_wip_mtx);
+
+ list_for_each_entry_reverse(dev, device_list, power.entry) {
+ /*
+ * In case the device is being waited for and async processing
+ * has not started for it yet, let the waiters make progress.
+ */
+ if (!dev->power.work_in_progress)
+ complete_all(&dev->power.completion);
+ }
+}
+
/**
* resume_event - Return a "resume" message for given "suspend" sleep state.
* @sleep_state: PM message representing a sleep state.
@@ -1181,14 +1412,16 @@ static void dpm_superior_set_must_resume(struct device *dev)
idx = device_links_read_lock();
- list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node)
+ dev_for_each_link_to_supplier(link, dev)
link->supplier->power.must_resume = true;
device_links_read_unlock(idx);
}
+static void async_suspend_noirq(void *data, async_cookie_t cookie);
+
/**
- * __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
+ * device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
@@ -1196,7 +1429,7 @@ static void dpm_superior_set_must_resume(struct device *dev)
* The driver of @dev will not receive interrupts while this function is being
* executed.
*/
-static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
+static void device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -1207,7 +1440,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
dpm_wait_for_subordinate(dev, async);
- if (async_error)
+ if (READ_ONCE(async_error))
goto Complete;
if (dev->power.syscore || dev->power.direct_complete)
@@ -1240,7 +1473,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
Run:
error = dpm_run_callback(callback, dev, state, info);
if (error) {
- async_error = error;
+ WRITE_ONCE(async_error, error);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
goto Complete;
}
@@ -1248,14 +1483,13 @@ Skip:
dev->power.is_noirq_suspended = true;
/*
- * Skipping the resume of devices that were in use right before the
- * system suspend (as indicated by their PM-runtime usage counters)
- * would be suboptimal. Also resume them if doing that is not allowed
- * to be skipped.
+ * Devices must be resumed unless they are explicitly allowed to be left
+ * in suspend, but even in that case skipping the resume of devices that
+ * were in use right before the system suspend (as indicated by their
+ * runtime PM usage counters and child counters) would be suboptimal.
*/
- if (atomic_read(&dev->power.usage_count) > 1 ||
- !(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) &&
- dev->power.may_skip_resume))
+ if (!(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) &&
+ dev->power.may_skip_resume) || !pm_runtime_need_not_resume(dev))
dev->power.must_resume = true;
if (dev->power.must_resume)
@@ -1264,76 +1498,81 @@ Skip:
Complete:
complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
- return error;
-}
-static void async_suspend_noirq(void *data, async_cookie_t cookie)
-{
- struct device *dev = (struct device *)data;
- int error;
-
- error = __device_suspend_noirq(dev, pm_transition, true);
- if (error) {
- dpm_save_failed_dev(dev_name(dev));
- pm_dev_err(dev, pm_transition, " async", error);
- }
+ if (error || READ_ONCE(async_error))
+ return;
- put_device(dev);
+ dpm_async_suspend_superior(dev, async_suspend_noirq);
}
-static int device_suspend_noirq(struct device *dev)
+static void async_suspend_noirq(void *data, async_cookie_t cookie)
{
- if (dpm_async_fn(dev, async_suspend_noirq))
- return 0;
+ struct device *dev = data;
- return __device_suspend_noirq(dev, pm_transition, false);
+ device_suspend_noirq(dev, pm_transition, true);
+ put_device(dev);
}
static int dpm_noirq_suspend_devices(pm_message_t state)
{
ktime_t starttime = ktime_get();
- int error = 0;
+ struct device *dev;
+ int error;
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
- mutex_lock(&dpm_list_mtx);
+
pm_transition = state;
async_error = 0;
- while (!list_empty(&dpm_late_early_list)) {
- struct device *dev = to_device(dpm_late_early_list.prev);
+ mutex_lock(&dpm_list_mtx);
- get_device(dev);
- mutex_unlock(&dpm_list_mtx);
+ /*
+ * Start processing "async" leaf devices upfront so they don't need to
+ * wait for the "sync" devices they don't depend on.
+ */
+ list_for_each_entry_reverse(dev, &dpm_late_early_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_leaf_device(dev))
+ dpm_async_with_cleanup(dev, async_suspend_noirq);
+ }
- error = device_suspend_noirq(dev);
+ while (!list_empty(&dpm_late_early_list)) {
+ dev = to_device(dpm_late_early_list.prev);
- mutex_lock(&dpm_list_mtx);
+ list_move(&dev->power.entry, &dpm_noirq_list);
- if (error) {
- pm_dev_err(dev, state, " noirq", error);
- dpm_save_failed_dev(dev_name(dev));
- } else if (!list_empty(&dev->power.entry)) {
- list_move(&dev->power.entry, &dpm_noirq_list);
- }
+ if (dpm_async_fn(dev, async_suspend_noirq))
+ continue;
+
+ get_device(dev);
mutex_unlock(&dpm_list_mtx);
+ device_suspend_noirq(dev, state, false);
+
put_device(dev);
mutex_lock(&dpm_list_mtx);
- if (error || async_error)
+ if (READ_ONCE(async_error)) {
+ dpm_async_suspend_complete_all(&dpm_late_early_list);
+ /*
+ * Move all devices to the target list to resume them
+ * properly.
+ */
+ list_splice_init(&dpm_late_early_list, &dpm_noirq_list);
break;
+ }
}
+
mutex_unlock(&dpm_list_mtx);
+
async_synchronize_full();
- if (!error)
- error = async_error;
- if (error) {
- suspend_stats.failed_suspend_noirq++;
+ error = READ_ONCE(async_error);
+ if (error)
dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
- }
+
dpm_show_time(starttime, state, error, "noirq");
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);
return error;
@@ -1375,15 +1614,17 @@ static void dpm_propagate_wakeup_to_parent(struct device *dev)
spin_unlock_irq(&parent->power.lock);
}
+static void async_suspend_late(void *data, async_cookie_t cookie);
+
/**
- * __device_suspend_late - Execute a "late suspend" callback for given device.
+ * device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
-static int __device_suspend_late(struct device *dev, pm_message_t state, bool async)
+static void device_suspend_late(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -1392,21 +1633,28 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
- __pm_runtime_disable(dev, false);
-
dpm_wait_for_subordinate(dev, async);
- if (async_error)
+ if (READ_ONCE(async_error))
goto Complete;
if (pm_wakeup_pending()) {
- async_error = -EBUSY;
+ WRITE_ONCE(async_error, -EBUSY);
goto Complete;
}
- if (dev->power.syscore || dev->power.direct_complete)
+ if (dev->power.direct_complete)
goto Complete;
+ /*
+ * Disable runtime PM for the device without checking if there is a
+ * pending resume request for it.
+ */
+ __pm_runtime_disable(dev, false);
+
+ if (dev->power.syscore)
+ goto Skip;
+
if (dev->pm_domain) {
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
@@ -1434,7 +1682,10 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
Run:
error = dpm_run_callback(callback, dev, state, info);
if (error) {
- async_error = error;
+ WRITE_ONCE(async_error, error);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, async ? " async late" : " late", error);
+ pm_runtime_enable(dev);
goto Complete;
}
dpm_propagate_wakeup_to_parent(dev);
@@ -1445,28 +1696,19 @@ Skip:
Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion);
- return error;
-}
-static void async_suspend_late(void *data, async_cookie_t cookie)
-{
- struct device *dev = (struct device *)data;
- int error;
+ if (error || READ_ONCE(async_error))
+ return;
- error = __device_suspend_late(dev, pm_transition, true);
- if (error) {
- dpm_save_failed_dev(dev_name(dev));
- pm_dev_err(dev, pm_transition, " async", error);
- }
- put_device(dev);
+ dpm_async_suspend_superior(dev, async_suspend_late);
}
-static int device_suspend_late(struct device *dev)
+static void async_suspend_late(void *data, async_cookie_t cookie)
{
- if (dpm_async_fn(dev, async_suspend_late))
- return 0;
+ struct device *dev = data;
- return __device_suspend_late(dev, pm_transition, false);
+ device_suspend_late(dev, pm_transition, true);
+ put_device(dev);
}
/**
@@ -1476,48 +1718,63 @@ static int device_suspend_late(struct device *dev)
int dpm_suspend_late(pm_message_t state)
{
ktime_t starttime = ktime_get();
- int error = 0;
+ struct device *dev;
+ int error;
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
- wake_up_all_idle_cpus();
- mutex_lock(&dpm_list_mtx);
+
pm_transition = state;
async_error = 0;
- while (!list_empty(&dpm_suspended_list)) {
- struct device *dev = to_device(dpm_suspended_list.prev);
+ wake_up_all_idle_cpus();
- get_device(dev);
+ mutex_lock(&dpm_list_mtx);
- mutex_unlock(&dpm_list_mtx);
+ /*
+ * Start processing "async" leaf devices upfront so they don't need to
+ * wait for the "sync" devices they don't depend on.
+ */
+ list_for_each_entry_reverse(dev, &dpm_suspended_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_leaf_device(dev))
+ dpm_async_with_cleanup(dev, async_suspend_late);
+ }
- error = device_suspend_late(dev);
+ while (!list_empty(&dpm_suspended_list)) {
+ dev = to_device(dpm_suspended_list.prev);
- mutex_lock(&dpm_list_mtx);
+ list_move(&dev->power.entry, &dpm_late_early_list);
- if (!list_empty(&dev->power.entry))
- list_move(&dev->power.entry, &dpm_late_early_list);
+ if (dpm_async_fn(dev, async_suspend_late))
+ continue;
- if (error) {
- pm_dev_err(dev, state, " late", error);
- dpm_save_failed_dev(dev_name(dev));
- }
+ get_device(dev);
mutex_unlock(&dpm_list_mtx);
+ device_suspend_late(dev, state, false);
+
put_device(dev);
mutex_lock(&dpm_list_mtx);
- if (error || async_error)
+ if (READ_ONCE(async_error)) {
+ dpm_async_suspend_complete_all(&dpm_suspended_list);
+ /*
+ * Move all devices to the target list to resume them
+ * properly.
+ */
+ list_splice_init(&dpm_suspended_list, &dpm_late_early_list);
break;
+ }
}
+
mutex_unlock(&dpm_list_mtx);
+
async_synchronize_full();
- if (!error)
- error = async_error;
+
+ error = READ_ONCE(async_error);
if (error) {
- suspend_stats.failed_suspend_late++;
dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
dpm_resume_early(resume_event(state));
}
@@ -1588,7 +1845,7 @@ static void dpm_clear_superiors_direct_complete(struct device *dev)
idx = device_links_read_lock();
- list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) {
+ dev_for_each_link_to_supplier(link, dev) {
spin_lock_irq(&link->supplier->power.lock);
link->supplier->power.direct_complete = false;
spin_unlock_irq(&link->supplier->power.lock);
@@ -1597,13 +1854,15 @@ static void dpm_clear_superiors_direct_complete(struct device *dev)
device_links_read_unlock(idx);
}
+static void async_suspend(void *data, async_cookie_t cookie);
+
/**
- * __device_suspend - Execute "suspend" callbacks for given device.
+ * device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
*/
-static int __device_suspend(struct device *dev, pm_message_t state, bool async)
+static void device_suspend(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -1615,7 +1874,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
dpm_wait_for_subordinate(dev, async);
- if (async_error) {
+ if (READ_ONCE(async_error)) {
dev->power.direct_complete = false;
goto Complete;
}
@@ -1635,7 +1894,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (pm_wakeup_pending()) {
dev->power.direct_complete = false;
- async_error = -EBUSY;
+ WRITE_ONCE(async_error, -EBUSY);
goto Complete;
}
@@ -1651,6 +1910,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
pm_runtime_disable(dev);
if (pm_runtime_status_suspended(dev)) {
pm_dev_dbg(dev, state, "direct-complete ");
+ dev->power.is_suspended = true;
goto Complete;
}
@@ -1717,34 +1977,27 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
dpm_watchdog_clear(&wd);
Complete:
- if (error)
- async_error = error;
+ if (error) {
+ WRITE_ONCE(async_error, error);
+ dpm_save_failed_dev(dev_name(dev));
+ pm_dev_err(dev, state, async ? " async" : "", error);
+ }
complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
- return error;
-}
-static void async_suspend(void *data, async_cookie_t cookie)
-{
- struct device *dev = (struct device *)data;
- int error;
-
- error = __device_suspend(dev, pm_transition, true);
- if (error) {
- dpm_save_failed_dev(dev_name(dev));
- pm_dev_err(dev, pm_transition, " async", error);
- }
+ if (error || READ_ONCE(async_error))
+ return;
- put_device(dev);
+ dpm_async_suspend_superior(dev, async_suspend);
}
-static int device_suspend(struct device *dev)
+static void async_suspend(void *data, async_cookie_t cookie)
{
- if (dpm_async_fn(dev, async_suspend))
- return 0;
+ struct device *dev = data;
- return __device_suspend(dev, pm_transition, false);
+ device_suspend(dev, pm_transition, true);
+ put_device(dev);
}
/**
@@ -1754,7 +2007,8 @@ static int device_suspend(struct device *dev)
int dpm_suspend(pm_message_t state)
{
ktime_t starttime = ktime_get();
- int error = 0;
+ struct device *dev;
+ int error;
trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
might_sleep();
@@ -1762,49 +2016,103 @@ int dpm_suspend(pm_message_t state)
devfreq_suspend();
cpufreq_suspend();
- mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
- while (!list_empty(&dpm_prepared_list)) {
- struct device *dev = to_device(dpm_prepared_list.prev);
- get_device(dev);
+ mutex_lock(&dpm_list_mtx);
- mutex_unlock(&dpm_list_mtx);
+ /*
+ * Start processing "async" leaf devices upfront so they don't need to
+ * wait for the "sync" devices they don't depend on.
+ */
+ list_for_each_entry_reverse(dev, &dpm_prepared_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_leaf_device(dev))
+ dpm_async_with_cleanup(dev, async_suspend);
+ }
+
+ while (!list_empty(&dpm_prepared_list)) {
+ dev = to_device(dpm_prepared_list.prev);
- error = device_suspend(dev);
+ list_move(&dev->power.entry, &dpm_suspended_list);
- mutex_lock(&dpm_list_mtx);
+ if (dpm_async_fn(dev, async_suspend))
+ continue;
- if (error) {
- pm_dev_err(dev, state, "", error);
- dpm_save_failed_dev(dev_name(dev));
- } else if (!list_empty(&dev->power.entry)) {
- list_move(&dev->power.entry, &dpm_suspended_list);
- }
+ get_device(dev);
mutex_unlock(&dpm_list_mtx);
+ device_suspend(dev, state, false);
+
put_device(dev);
mutex_lock(&dpm_list_mtx);
- if (error || async_error)
+ if (READ_ONCE(async_error)) {
+ dpm_async_suspend_complete_all(&dpm_prepared_list);
+ /*
+ * Move all devices to the target list to resume them
+ * properly.
+ */
+ list_splice_init(&dpm_prepared_list, &dpm_suspended_list);
break;
+ }
}
+
mutex_unlock(&dpm_list_mtx);
+
async_synchronize_full();
- if (!error)
- error = async_error;
- if (error) {
- suspend_stats.failed_suspend++;
+
+ error = READ_ONCE(async_error);
+ if (error)
dpm_save_failed_step(SUSPEND_SUSPEND);
- }
+
dpm_show_time(starttime, state, error, NULL);
trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
return error;
}
+static bool device_prepare_smart_suspend(struct device *dev)
+{
+ struct device_link *link;
+ bool ret = true;
+ int idx;
+
+ /*
+ * The "smart suspend" feature is enabled for devices whose drivers ask
+ * for it and for devices without PM callbacks.
+ *
+ * However, if "smart suspend" is not enabled for the device's parent
+ * or any of its suppliers that take runtime PM into account, it cannot
+ * be enabled for the device either.
+ */
+ if (!dev->power.no_pm_callbacks &&
+ !dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
+ return false;
+
+ if (dev->parent && !dev_pm_smart_suspend(dev->parent) &&
+ !dev->parent->power.ignore_children && !pm_runtime_blocked(dev->parent))
+ return false;
+
+ idx = device_links_read_lock();
+
+ dev_for_each_link_to_supplier(link, dev) {
+ if (!device_link_test(link, DL_FLAG_PM_RUNTIME))
+ continue;
+
+ if (!dev_pm_smart_suspend(link->supplier) &&
+ !pm_runtime_blocked(link->supplier)) {
+ ret = false;
+ break;
+ }
+ }
+
+ device_links_read_unlock(idx);
+
+ return ret;
+}
+
/**
* device_prepare - Prepare a device for system power transition.
* @dev: Device to handle.
@@ -1816,6 +2124,7 @@ int dpm_suspend(pm_message_t state)
static int device_prepare(struct device *dev, pm_message_t state)
{
int (*callback)(struct device *) = NULL;
+ bool smart_suspend;
int ret = 0;
/*
@@ -1825,6 +2134,13 @@ static int device_prepare(struct device *dev, pm_message_t state)
* it again during the complete phase.
*/
pm_runtime_get_noresume(dev);
+ /*
+ * If runtime PM is disabled for the device at this point and it has
+ * never been enabled so far, it should not be enabled until this system
+ * suspend-resume cycle is complete, so prepare to trigger a warning on
+ * subsequent attempts to enable it.
+ */
+ smart_suspend = !pm_runtime_block_if_disabled(dev);
if (dev->power.syscore)
return 0;
@@ -1832,6 +2148,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
device_lock(dev);
dev->power.wakeup_path = false;
+ dev->power.out_band_wakeup = false;
if (dev->power.no_pm_callbacks)
goto unlock;
@@ -1859,6 +2176,13 @@ unlock:
pm_runtime_put(dev);
return ret;
}
+ /* Do not enable "smart suspend" for devices with disabled runtime PM. */
+ if (smart_suspend)
+ smart_suspend = device_prepare_smart_suspend(dev);
+
+ spin_lock_irq(&dev->power.lock);
+
+ dev->power.smart_suspend = smart_suspend;
/*
* A positive return value from ->prepare() means "this device appears
* to be runtime-suspended and its state is fine, so if it really is
@@ -1866,11 +2190,12 @@ unlock:
* will do the same thing with all of its descendants". This only
* applies to suspend transitions, however.
*/
- spin_lock_irq(&dev->power.lock);
dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
(ret > 0 || dev->power.no_pm_callbacks) &&
!dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
+
spin_unlock_irq(&dev->power.lock);
+
return 0;
}
@@ -1885,7 +2210,6 @@ int dpm_prepare(pm_message_t state)
int error = 0;
trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
- might_sleep();
/*
* Give a chance for the known devices to complete their probes, before
@@ -1950,11 +2274,13 @@ int dpm_suspend_start(pm_message_t state)
int error;
error = dpm_prepare(state);
- if (error) {
- suspend_stats.failed_prepare++;
+ if (error)
dpm_save_failed_step(SUSPEND_PREPARE);
- } else
+ else {
+ pm_restrict_gfp_mask();
error = dpm_suspend(state);
+ }
+
dpm_show_time(starttime, state, error, "start");
return error;
}
@@ -1963,7 +2289,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_start);
void __suspend_report_result(const char *function, struct device *dev, void *fn, int ret)
{
if (ret)
- dev_err(dev, "%s(): %pS returns %d\n", function, fn, ret);
+ dev_err(dev, "%s(): %ps returns %d\n", function, fn, ret);
}
EXPORT_SYMBOL_GPL(__suspend_report_result);
@@ -2034,6 +2360,5 @@ void device_pm_check_callbacks(struct device *dev)
bool dev_pm_skip_suspend(struct device *dev)
{
- return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
- pm_runtime_status_suspended(dev);
+ return dev_pm_smart_suspend(dev) && pm_runtime_status_suspended(dev);
}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 0eb7f02b3ad5..922ed457db19 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -29,6 +29,7 @@ extern u64 pm_runtime_active_time(struct device *dev);
#define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \
WAKE_IRQ_DEDICATED_MANAGED | \
WAKE_IRQ_DEDICATED_REVERSE)
+#define WAKE_IRQ_DEDICATED_ENABLED BIT(3)
struct wake_irq {
struct device *dev;
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 8e93167f1783..ff393cba7649 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -137,6 +137,7 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type)
return ret;
}
+EXPORT_SYMBOL_GPL(dev_pm_qos_read_value);
/**
* apply_constraint - Add/modify/remove device PM QoS request.
@@ -201,7 +202,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
if (!qos)
return -ENOMEM;
- n = kzalloc(3 * sizeof(*n), GFP_KERNEL);
+ n = kcalloc(3, sizeof(*n), GFP_KERNEL);
if (!n) {
kfree(qos);
return -ENOMEM;
diff --git a/drivers/base/power/runtime-test.c b/drivers/base/power/runtime-test.c
new file mode 100644
index 000000000000..1535ad2b0264
--- /dev/null
+++ b/drivers/base/power/runtime-test.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Google, Inc.
+ */
+
+#include <linux/cleanup.h>
+#include <linux/pm_runtime.h>
+#include <kunit/device.h>
+#include <kunit/test.h>
+
+#define DEVICE_NAME "pm_runtime_test_device"
+
+static void pm_runtime_depth_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ pm_runtime_enable(dev);
+
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_get_sync(dev)); /* "already active" */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+}
+
+/* Test pm_runtime_put() and friends when already suspended. */
+static void pm_runtime_already_suspended_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ pm_runtime_enable(dev);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+
+ pm_runtime_get_noresume(dev);
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync(dev));
+
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_suspend(dev));
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_autosuspend(dev));
+ KUNIT_EXPECT_EQ(test, 1, pm_request_autosuspend(dev));
+
+ pm_runtime_get_noresume(dev);
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync_autosuspend(dev));
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ /* Grab 2 refcounts */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_get_noresume(dev);
+ /* The first put() sees usage_count 1 */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync_autosuspend(dev));
+ /* The second put() sees usage_count 0 but tells us "already suspended". */
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync_autosuspend(dev));
+
+ /* Should have remained suspended the whole time. */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+}
+
+static void pm_runtime_idle_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ pm_runtime_enable(dev);
+
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ pm_runtime_put_noidle(dev);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_idle(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_idle(dev));
+}
+
+static void pm_runtime_disabled_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ /* Never called pm_runtime_enable() */
+ KUNIT_EXPECT_FALSE(test, pm_runtime_enabled(dev));
+
+ /* "disabled" is treated as "active" */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ KUNIT_EXPECT_FALSE(test, pm_runtime_suspended(dev));
+
+ /*
+ * Note: these "fail", but they still acquire/release refcounts, so
+ * keep them balanced.
+ */
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get(dev));
+ pm_runtime_put(dev);
+
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get_sync(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_put_sync(dev));
+
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get(dev));
+ pm_runtime_put_autosuspend(dev);
+
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_resume_and_get(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_idle(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_request_idle(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_request_resume(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_request_autosuspend(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_suspend(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_resume(dev));
+ KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_autosuspend(dev));
+
+ /* Still disabled */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+ KUNIT_EXPECT_FALSE(test, pm_runtime_enabled(dev));
+}
+
+static void pm_runtime_error_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ pm_runtime_enable(dev);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+
+ /* Fake a .runtime_resume() error */
+ dev->power.runtime_error = -EIO;
+
+ /*
+ * Note: these "fail", but they still acquire/release refcounts, so
+ * keep them balanced.
+ */
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev));
+ pm_runtime_put(dev);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get_sync(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_put_sync(dev));
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev));
+ pm_runtime_put_autosuspend(dev);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_put_sync_autosuspend(dev));
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_resume_and_get(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_idle(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_idle(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_resume(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_autosuspend(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_suspend(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_resume(dev));
+ KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_autosuspend(dev));
+
+ /* Error is still pending */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+ KUNIT_EXPECT_EQ(test, -EIO, dev->power.runtime_error);
+ /* Clear error */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_set_suspended(dev));
+ KUNIT_EXPECT_EQ(test, 0, dev->power.runtime_error);
+ /* Still suspended */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get(dev));
+ pm_runtime_barrier(dev);
+ pm_runtime_put(dev);
+ pm_runtime_suspend(dev); /* flush the put(), to suspend */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
+
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
+ pm_runtime_put_autosuspend(dev);
+
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_resume_and_get(dev));
+
+ /*
+ * The following should all return -EAGAIN (usage is non-zero) or 1
+ * (already resumed).
+ */
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_idle(dev));
+ KUNIT_EXPECT_EQ(test, 1, pm_request_resume(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_autosuspend(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_suspend(dev));
+ KUNIT_EXPECT_EQ(test, 1, pm_runtime_resume(dev));
+ KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_autosuspend(dev));
+
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
+
+ /* Suspended again */
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+}
+
+/*
+ * Explore a typical probe() sequence in which a device marks itself powered,
+ * but doesn't hold any runtime PM reference, so it suspends as soon as it goes
+ * idle.
+ */
+static void pm_runtime_probe_active_test(struct kunit *test)
+{
+ struct device *dev = kunit_device_register(test, DEVICE_NAME);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ KUNIT_EXPECT_TRUE(test, pm_runtime_status_suspended(dev));
+
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_set_active(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+
+ pm_runtime_enable(dev);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+
+ /* Nothing to flush. We stay active. */
+ pm_runtime_barrier(dev);
+ KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
+
+ /* Ask for idle? Now we suspend. */
+ KUNIT_EXPECT_EQ(test, 0, pm_runtime_idle(dev));
+ KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
+}
+
+static struct kunit_case pm_runtime_test_cases[] = {
+ KUNIT_CASE(pm_runtime_depth_test),
+ KUNIT_CASE(pm_runtime_already_suspended_test),
+ KUNIT_CASE(pm_runtime_idle_test),
+ KUNIT_CASE(pm_runtime_disabled_test),
+ KUNIT_CASE(pm_runtime_error_test),
+ KUNIT_CASE(pm_runtime_probe_active_test),
+ {}
+};
+
+static struct kunit_suite pm_runtime_test_suite = {
+ .name = "pm_runtime_test_cases",
+ .test_cases = pm_runtime_test_cases,
+};
+
+kunit_test_suite(pm_runtime_test_suite);
+MODULE_DESCRIPTION("Runtime power management unit test suite");
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 997be3ac20a7..84676cc24221 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -11,6 +11,7 @@
#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
+#include <linux/rculist.h>
#include <trace/events/rpm.h>
#include "../base.h"
@@ -18,10 +19,24 @@
typedef int (*pm_callback_t)(struct device *);
+static inline pm_callback_t get_callback_ptr(const void *start, size_t offset)
+{
+ return *(pm_callback_t *)(start + offset);
+}
+
+static pm_callback_t __rpm_get_driver_callback(struct device *dev,
+ size_t cb_offset)
+{
+ if (dev->driver && dev->driver->pm)
+ return get_callback_ptr(dev->driver->pm, cb_offset);
+
+ return NULL;
+}
+
static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset)
{
- pm_callback_t cb;
const struct dev_pm_ops *ops;
+ pm_callback_t cb = NULL;
if (dev->pm_domain)
ops = &dev->pm_domain->ops;
@@ -35,12 +50,10 @@ static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset)
ops = NULL;
if (ops)
- cb = *(pm_callback_t *)((void *)ops + cb_offset);
- else
- cb = NULL;
+ cb = get_callback_ptr(ops, cb_offset);
- if (!cb && dev->driver && dev->driver->pm)
- cb = *(pm_callback_t *)((void *)dev->driver->pm + cb_offset);
+ if (!cb)
+ cb = __rpm_get_driver_callback(dev, cb_offset);
return cb;
}
@@ -77,7 +90,7 @@ static void update_pm_runtime_accounting(struct device *dev)
/*
* Because ktime_get_mono_fast_ns() is not monotonic during
* timekeeping updates, ensure that 'now' is after the last saved
- * timesptamp.
+ * timestamp.
*/
if (now < last)
return;
@@ -93,6 +106,7 @@ static void update_pm_runtime_accounting(struct device *dev)
static void __update_runtime_status(struct device *dev, enum rpm_status status)
{
update_pm_runtime_accounting(dev);
+ trace_rpm_status(dev, status);
dev->power.runtime_status = status;
}
@@ -203,7 +217,7 @@ static int dev_memalloc_noio(struct device *dev, void *data)
* resume/suspend callback of any one of its ancestors(or the
* block device itself), the deadlock may be triggered inside the
* memory allocation since it might not complete until the block
- * device becomes active and the involed page I/O finishes. The
+ * device becomes active and the involved page I/O finishes. The
* situation is pointed out first by Alan Stern. Network device
* are involved in iSCSI kind of situation.
*
@@ -243,8 +257,7 @@ void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
* flag was set by any one of the descendants.
*/
if (!dev || (!enable &&
- device_for_each_child(dev, NULL,
- dev_memalloc_noio)))
+ device_for_each_child(dev, NULL, dev_memalloc_noio)))
break;
}
mutex_unlock(&dev_hotplug_mutex);
@@ -265,15 +278,13 @@ static int rpm_check_suspend_allowed(struct device *dev)
retval = -EACCES;
else if (atomic_read(&dev->power.usage_count))
retval = -EAGAIN;
- else if (!dev->power.ignore_children &&
- atomic_read(&dev->power.child_count))
+ else if (!dev->power.ignore_children && atomic_read(&dev->power.child_count))
retval = -EBUSY;
/* Pending resume requests take precedence over suspends. */
- else if ((dev->power.deferred_resume
- && dev->power.runtime_status == RPM_SUSPENDING)
- || (dev->power.request_pending
- && dev->power.request == RPM_REQ_RESUME))
+ else if ((dev->power.deferred_resume &&
+ dev->power.runtime_status == RPM_SUSPENDING) ||
+ (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN;
else if (__dev_pm_qos_resume_latency(dev) == 0)
retval = -EPERM;
@@ -291,7 +302,7 @@ static int rpm_get_suppliers(struct device *dev)
device_links_read_lock_held()) {
int retval;
- if (!(link->flags & DL_FLAG_PM_RUNTIME))
+ if (!device_link_test(link, DL_FLAG_PM_RUNTIME))
continue;
retval = pm_runtime_get_sync(link->supplier);
@@ -404,9 +415,9 @@ static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
*
* Do that if resume fails too.
*/
- if (use_links
- && ((dev->power.runtime_status == RPM_SUSPENDING && !retval)
- || (dev->power.runtime_status == RPM_RESUMING && retval))) {
+ if (use_links &&
+ ((dev->power.runtime_status == RPM_SUSPENDING && !retval) ||
+ (dev->power.runtime_status == RPM_RESUMING && retval))) {
idx = device_links_read_lock();
__rpm_put_suppliers(dev, false);
@@ -422,6 +433,49 @@ fail:
}
/**
+ * rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int rpm_callback(int (*cb)(struct device *), struct device *dev)
+{
+ int retval;
+
+ if (dev->power.memalloc_noio) {
+ unsigned int noio_flag;
+
+ /*
+ * Deadlock might be caused if memory allocation with
+ * GFP_KERNEL happens inside runtime_suspend and
+ * runtime_resume callbacks of one block device's
+ * ancestor or the block device itself. Network
+ * device might be thought as part of iSCSI block
+ * device, so network device and its ancestor should
+ * be marked as memalloc_noio too.
+ */
+ noio_flag = memalloc_noio_save();
+ retval = __rpm_callback(cb, dev);
+ memalloc_noio_restore(noio_flag);
+ } else {
+ retval = __rpm_callback(cb, dev);
+ }
+
+ /*
+ * Since -EACCES means that runtime PM is disabled for the given device,
+ * it should not be returned by runtime PM callbacks. If it is returned
+ * nevertheless, assume it to be a transient error and convert it to
+ * -EAGAIN.
+ */
+ if (retval == -EACCES)
+ retval = -EAGAIN;
+
+ if (retval != -EAGAIN && retval != -EBUSY)
+ dev->power.runtime_error = retval;
+
+ return retval;
+}
+
+/**
* rpm_idle - Notify device bus type if the device can be suspended.
* @dev: Device to notify the bus type about.
* @rpmflags: Flag bits.
@@ -439,11 +493,14 @@ static int rpm_idle(struct device *dev, int rpmflags)
int (*callback)(struct device *);
int retval;
- trace_rpm_idle_rcuidle(dev, rpmflags);
+ trace_rpm_idle(dev, rpmflags);
retval = rpm_check_suspend_allowed(dev);
if (retval < 0)
; /* Conditions are wrong. */
+ else if ((rpmflags & RPM_GET_PUT) && retval == 1)
+ ; /* put() is allowed in RPM_SUSPENDED */
+
/* Idle notifications are allowed only in the RPM_ACTIVE state. */
else if (dev->power.runtime_status != RPM_ACTIVE)
retval = -EAGAIN;
@@ -459,6 +516,7 @@ static int rpm_idle(struct device *dev, int rpmflags)
/* Act as though RPM_NOWAIT is always set. */
else if (dev->power.idle_notification)
retval = -EINPROGRESS;
+
if (retval)
goto out;
@@ -478,55 +536,33 @@ static int rpm_idle(struct device *dev, int rpmflags)
dev->power.request_pending = true;
queue_work(pm_wq, &dev->power.work);
}
- trace_rpm_return_int_rcuidle(dev, _THIS_IP_, 0);
+ trace_rpm_return_int(dev, _THIS_IP_, 0);
return 0;
}
dev->power.idle_notification = true;
- retval = __rpm_callback(callback, dev);
+ if (dev->power.irq_safe)
+ spin_unlock(&dev->power.lock);
+ else
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = callback(dev);
+
+ if (dev->power.irq_safe)
+ spin_lock(&dev->power.lock);
+ else
+ spin_lock_irq(&dev->power.lock);
dev->power.idle_notification = false;
wake_up_all(&dev->power.wait_queue);
out:
- trace_rpm_return_int_rcuidle(dev, _THIS_IP_, retval);
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval ? retval : rpm_suspend(dev, rpmflags | RPM_AUTO);
}
/**
- * rpm_callback - Run a given runtime PM callback for a given device.
- * @cb: Runtime PM callback to run.
- * @dev: Device to run the callback for.
- */
-static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-{
- int retval;
-
- if (dev->power.memalloc_noio) {
- unsigned int noio_flag;
-
- /*
- * Deadlock might be caused if memory allocation with
- * GFP_KERNEL happens inside runtime_suspend and
- * runtime_resume callbacks of one block device's
- * ancestor or the block device itself. Network
- * device might be thought as part of iSCSI block
- * device, so network device and its ancestor should
- * be marked as memalloc_noio too.
- */
- noio_flag = memalloc_noio_save();
- retval = __rpm_callback(cb, dev);
- memalloc_noio_restore(noio_flag);
- } else {
- retval = __rpm_callback(cb, dev);
- }
-
- dev->power.runtime_error = retval;
- return retval != -EACCES ? retval : -EIO;
-}
-
-/**
* rpm_suspend - Carry out runtime suspend of given device.
* @dev: Device to suspend.
* @rpmflags: Flag bits.
@@ -554,7 +590,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
struct device *parent = NULL;
int retval;
- trace_rpm_suspend_rcuidle(dev, rpmflags);
+ trace_rpm_suspend(dev, rpmflags);
repeat:
retval = rpm_check_suspend_allowed(dev);
@@ -564,12 +600,12 @@ static int rpm_suspend(struct device *dev, int rpmflags)
/* Synchronous suspends are not allowed in the RPM_RESUMING state. */
if (dev->power.runtime_status == RPM_RESUMING && !(rpmflags & RPM_ASYNC))
retval = -EAGAIN;
+
if (retval)
goto out;
/* If the autosuspend_delay time hasn't expired yet, reschedule. */
- if ((rpmflags & RPM_AUTO)
- && dev->power.runtime_status != RPM_SUSPENDING) {
+ if ((rpmflags & RPM_AUTO) && dev->power.runtime_status != RPM_SUSPENDING) {
u64 expires = pm_runtime_autosuspend_expiration(dev);
if (expires != 0) {
@@ -584,7 +620,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
* rest.
*/
if (!(dev->power.timer_expires &&
- dev->power.timer_expires <= expires)) {
+ dev->power.timer_expires <= expires)) {
/*
* We add a slack of 25% to gather wakeups
* without sacrificing the granularity.
@@ -594,9 +630,9 @@ static int rpm_suspend(struct device *dev, int rpmflags)
dev->power.timer_expires = expires;
hrtimer_start_range_ns(&dev->power.suspend_timer,
- ns_to_ktime(expires),
- slack,
- HRTIMER_MODE_ABS);
+ ns_to_ktime(expires),
+ slack,
+ HRTIMER_MODE_ABS);
}
dev->power.timer_autosuspends = 1;
goto out;
@@ -705,7 +741,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
}
out:
- trace_rpm_return_int_rcuidle(dev, _THIS_IP_, retval);
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
@@ -715,21 +751,18 @@ static int rpm_suspend(struct device *dev, int rpmflags)
dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue);
- if (retval == -EAGAIN || retval == -EBUSY) {
- dev->power.runtime_error = 0;
+ /*
+ * On transient errors, if the callback routine failed an autosuspend,
+ * and if the last_busy time has been updated so that there is a new
+ * autosuspend expiration time, automatically reschedule another
+ * autosuspend.
+ */
+ if (!dev->power.runtime_error && (rpmflags & RPM_AUTO) &&
+ pm_runtime_autosuspend_expiration(dev) != 0)
+ goto repeat;
+
+ pm_runtime_cancel_pending(dev);
- /*
- * If the callback routine failed an autosuspend, and
- * if the last_busy time has been updated so that there
- * is a new autosuspend expiration time, automatically
- * reschedule another autosuspend.
- */
- if ((rpmflags & RPM_AUTO) &&
- pm_runtime_autosuspend_expiration(dev) != 0)
- goto repeat;
- } else {
- pm_runtime_cancel_pending(dev);
- }
goto out;
}
@@ -757,7 +790,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
struct device *parent = NULL;
int retval = 0;
- trace_rpm_resume_rcuidle(dev, rpmflags);
+ trace_rpm_resume(dev, rpmflags);
repeat:
if (dev->power.runtime_error) {
@@ -766,6 +799,8 @@ static int rpm_resume(struct device *dev, int rpmflags)
if (dev->power.runtime_status == RPM_ACTIVE &&
dev->power.last_status == RPM_ACTIVE)
retval = 1;
+ else if (rpmflags & RPM_TRANSPARENT)
+ goto out;
else
retval = -EACCES;
}
@@ -787,15 +822,18 @@ static int rpm_resume(struct device *dev, int rpmflags)
goto out;
}
- if (dev->power.runtime_status == RPM_RESUMING
- || dev->power.runtime_status == RPM_SUSPENDING) {
+ if (dev->power.runtime_status == RPM_RESUMING ||
+ dev->power.runtime_status == RPM_SUSPENDING) {
DEFINE_WAIT(wait);
if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {
- if (dev->power.runtime_status == RPM_SUSPENDING)
+ if (dev->power.runtime_status == RPM_SUSPENDING) {
dev->power.deferred_resume = true;
- else
+ if (rpmflags & RPM_NOWAIT)
+ retval = -EINPROGRESS;
+ } else {
retval = -EINPROGRESS;
+ }
goto out;
}
@@ -812,8 +850,8 @@ static int rpm_resume(struct device *dev, int rpmflags)
for (;;) {
prepare_to_wait(&dev->power.wait_queue, &wait,
TASK_UNINTERRUPTIBLE);
- if (dev->power.runtime_status != RPM_RESUMING
- && dev->power.runtime_status != RPM_SUSPENDING)
+ if (dev->power.runtime_status != RPM_RESUMING &&
+ dev->power.runtime_status != RPM_SUSPENDING)
break;
spin_unlock_irq(&dev->power.lock);
@@ -833,9 +871,9 @@ static int rpm_resume(struct device *dev, int rpmflags)
*/
if (dev->power.no_callbacks && !parent && dev->parent) {
spin_lock_nested(&dev->parent->power.lock, SINGLE_DEPTH_NESTING);
- if (dev->parent->power.disable_depth > 0
- || dev->parent->power.ignore_children
- || dev->parent->power.runtime_status == RPM_ACTIVE) {
+ if (dev->parent->power.disable_depth > 0 ||
+ dev->parent->power.ignore_children ||
+ dev->parent->power.runtime_status == RPM_ACTIVE) {
atomic_inc(&dev->parent->power.child_count);
spin_unlock(&dev->parent->power.lock);
retval = 1;
@@ -864,6 +902,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
parent = dev->parent;
if (dev->power.irq_safe)
goto skip_parent;
+
spin_unlock(&dev->power.lock);
pm_runtime_get_noresume(parent);
@@ -873,8 +912,8 @@ static int rpm_resume(struct device *dev, int rpmflags)
* Resume the parent if it has runtime PM enabled and not been
* set to ignore its children.
*/
- if (!parent->power.disable_depth
- && !parent->power.ignore_children) {
+ if (!parent->power.disable_depth &&
+ !parent->power.ignore_children) {
rpm_resume(parent, 0);
if (parent->power.runtime_status != RPM_ACTIVE)
retval = -EBUSY;
@@ -884,6 +923,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
spin_lock(&dev->power.lock);
if (retval)
goto out;
+
goto repeat;
}
skip_parent:
@@ -922,7 +962,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
spin_lock_irq(&dev->power.lock);
}
- trace_rpm_return_int_rcuidle(dev, _THIS_IP_, retval);
+ trace_rpm_return_int(dev, _THIS_IP_, retval);
return retval;
}
@@ -988,7 +1028,7 @@ static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer)
* If 'expires' is after the current time, we've been called
* too early.
*/
- if (expires > 0 && expires < ktime_get_mono_fast_ns()) {
+ if (expires > 0 && expires <= ktime_get_mono_fast_ns()) {
dev->power.timer_expires = 0;
rpm_suspend(dev, dev->power.timer_autosuspends ?
(RPM_ASYNC | RPM_AUTO) : RPM_ASYNC);
@@ -1078,7 +1118,7 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
if (retval < 0) {
return retval;
} else if (retval > 0) {
- trace_rpm_usage_rcuidle(dev, rpmflags);
+ trace_rpm_usage(dev, rpmflags);
return 0;
}
}
@@ -1116,7 +1156,7 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
if (retval < 0) {
return retval;
} else if (retval > 0) {
- trace_rpm_usage_rcuidle(dev, rpmflags);
+ trace_rpm_usage(dev, rpmflags);
return 0;
}
}
@@ -1162,16 +1202,18 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_resume);
/**
- * pm_runtime_get_if_active - Conditionally bump up device usage counter.
+ * pm_runtime_get_conditional - Conditionally bump up device usage counter.
* @dev: Device to handle.
* @ign_usage_count: Whether or not to look at the current usage counter value.
*
* Return -EINVAL if runtime PM is disabled for @dev.
*
- * Otherwise, if the runtime PM status of @dev is %RPM_ACTIVE and either
- * @ign_usage_count is %true or the runtime PM usage counter of @dev is not
- * zero, increment the usage counter of @dev and return 1. Otherwise, return 0
- * without changing the usage counter.
+ * Otherwise, if its runtime PM status is %RPM_ACTIVE and (1) @ign_usage_count
+ * is set, or (2) @dev is not ignoring children and its active child count is
+ * nonzero, or (3) the runtime PM usage counter of @dev is not zero, increment
+ * the usage counter of @dev and return 1.
+ *
+ * Otherwise, return 0 without changing the usage counter.
*
* If @ign_usage_count is %true, this function can be used to prevent suspending
* the device when its runtime PM status is %RPM_ACTIVE.
@@ -1183,7 +1225,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_resume);
* The caller is responsible for decrementing the runtime PM usage counter of
* @dev after this function has returned a positive value for it.
*/
-int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count)
+static int pm_runtime_get_conditional(struct device *dev, bool ign_usage_count)
{
unsigned long flags;
int retval;
@@ -1193,20 +1235,58 @@ int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count)
retval = -EINVAL;
} else if (dev->power.runtime_status != RPM_ACTIVE) {
retval = 0;
- } else if (ign_usage_count) {
+ } else if (ign_usage_count || (!dev->power.ignore_children &&
+ atomic_read(&dev->power.child_count) > 0)) {
retval = 1;
atomic_inc(&dev->power.usage_count);
} else {
retval = atomic_inc_not_zero(&dev->power.usage_count);
}
- trace_rpm_usage_rcuidle(dev, 0);
+ trace_rpm_usage(dev, 0);
spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
}
+
+/**
+ * pm_runtime_get_if_active - Bump up runtime PM usage counter if the device is
+ * in active state
+ * @dev: Target device.
+ *
+ * Increment the runtime PM usage counter of @dev if its runtime PM status is
+ * %RPM_ACTIVE, in which case it returns 1. If the device is in a different
+ * state, 0 is returned. -EINVAL is returned if runtime PM is disabled for the
+ * device, in which case also the usage_count will remain unmodified.
+ */
+int pm_runtime_get_if_active(struct device *dev)
+{
+ return pm_runtime_get_conditional(dev, true);
+}
EXPORT_SYMBOL_GPL(pm_runtime_get_if_active);
/**
+ * pm_runtime_get_if_in_use - Conditionally bump up runtime PM usage counter.
+ * @dev: Target device.
+ *
+ * Increment the runtime PM usage counter of @dev if its runtime PM status is
+ * %RPM_ACTIVE and its runtime PM usage counter is greater than 0 or it is not
+ * ignoring children and its active child count is nonzero. 1 is returned in
+ * this case.
+ *
+ * If @dev is in a different state or it is not in use (that is, its usage
+ * counter is 0, or it is ignoring children, or its active child count is 0),
+ * 0 is returned.
+ *
+ * -EINVAL is returned if runtime PM is disabled for the device, in which case
+ * also the usage counter of @dev is not updated.
+ */
+int pm_runtime_get_if_in_use(struct device *dev)
+{
+ return pm_runtime_get_conditional(dev, false);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use);
+
+/**
* __pm_runtime_set_status - Set runtime PM status of a device.
* @dev: Device to handle.
* @status: New runtime PM status of the device.
@@ -1288,9 +1368,9 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
* not active, has runtime PM enabled and the
* 'power.ignore_children' flag unset.
*/
- if (!parent->power.disable_depth
- && !parent->power.ignore_children
- && parent->power.runtime_status != RPM_ACTIVE) {
+ if (!parent->power.disable_depth &&
+ !parent->power.ignore_children &&
+ parent->power.runtime_status != RPM_ACTIVE) {
dev_err(dev, "runtime PM trying to activate child device %s but parent (%s) is not active\n",
dev_name(dev),
dev_name(parent));
@@ -1355,9 +1435,9 @@ static void __pm_runtime_barrier(struct device *dev)
dev->power.request_pending = false;
}
- if (dev->power.runtime_status == RPM_SUSPENDING
- || dev->power.runtime_status == RPM_RESUMING
- || dev->power.idle_notification) {
+ if (dev->power.runtime_status == RPM_SUSPENDING ||
+ dev->power.runtime_status == RPM_RESUMING ||
+ dev->power.idle_notification) {
DEFINE_WAIT(wait);
/* Suspend, wake-up or idle notification in progress. */
@@ -1387,47 +1467,48 @@ static void __pm_runtime_barrier(struct device *dev)
* Next, make sure that all pending requests for the device have been flushed
* from pm_wq and wait for all runtime PM operations involving the device in
* progress to complete.
- *
- * Return value:
- * 1, if there was a resume request pending and the device had to be woken up,
- * 0, otherwise
*/
-int pm_runtime_barrier(struct device *dev)
+void pm_runtime_barrier(struct device *dev)
{
- int retval = 0;
-
pm_runtime_get_noresume(dev);
spin_lock_irq(&dev->power.lock);
if (dev->power.request_pending
- && dev->power.request == RPM_REQ_RESUME) {
+ && dev->power.request == RPM_REQ_RESUME)
rpm_resume(dev, 0);
- retval = 1;
- }
__pm_runtime_barrier(dev);
spin_unlock_irq(&dev->power.lock);
pm_runtime_put_noidle(dev);
-
- return retval;
}
EXPORT_SYMBOL_GPL(pm_runtime_barrier);
-/**
- * __pm_runtime_disable - Disable runtime PM of a device.
- * @dev: Device to handle.
- * @check_resume: If set, check if there's a resume request for the device.
- *
- * Increment power.disable_depth for the device and if it was zero previously,
- * cancel all pending runtime PM requests for the device and wait for all
- * operations in progress to complete. The device can be either active or
- * suspended after its runtime PM has been disabled.
- *
- * If @check_resume is set and there's a resume request pending when
- * __pm_runtime_disable() is called and power.disable_depth is zero, the
- * function will wake up the device before disabling its runtime PM.
- */
+bool pm_runtime_block_if_disabled(struct device *dev)
+{
+ bool ret;
+
+ spin_lock_irq(&dev->power.lock);
+
+ ret = !pm_runtime_enabled(dev);
+ if (ret && dev->power.last_status == RPM_INVALID)
+ dev->power.last_status = RPM_BLOCKED;
+
+ spin_unlock_irq(&dev->power.lock);
+
+ return ret;
+}
+
+void pm_runtime_unblock(struct device *dev)
+{
+ spin_lock_irq(&dev->power.lock);
+
+ if (dev->power.last_status == RPM_BLOCKED)
+ dev->power.last_status = RPM_INVALID;
+
+ spin_unlock_irq(&dev->power.lock);
+}
+
void __pm_runtime_disable(struct device *dev, bool check_resume)
{
spin_lock_irq(&dev->power.lock);
@@ -1442,8 +1523,8 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
* means there probably is some I/O to process and disabling runtime PM
* shouldn't prevent the device from processing the I/O.
*/
- if (check_resume && dev->power.request_pending
- && dev->power.request == RPM_REQ_RESUME) {
+ if (check_resume && dev->power.request_pending &&
+ dev->power.request == RPM_REQ_RESUME) {
/*
* Prevent suspends and idle notifications from being carried
* out after we have woken up the device.
@@ -1486,6 +1567,10 @@ void pm_runtime_enable(struct device *dev)
if (--dev->power.disable_depth > 0)
goto out;
+ if (dev->power.last_status == RPM_BLOCKED) {
+ dev_warn(dev, "Attempt to enable runtime PM when it is blocked\n");
+ dump_stack();
+ }
dev->power.last_status = RPM_INVALID;
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();
@@ -1499,6 +1584,32 @@ out:
}
EXPORT_SYMBOL_GPL(pm_runtime_enable);
+static void pm_runtime_set_suspended_action(void *data)
+{
+ pm_runtime_set_suspended(data);
+}
+
+/**
+ * devm_pm_runtime_set_active_enabled - set_active version of devm_pm_runtime_enable.
+ *
+ * @dev: Device to handle.
+ */
+int devm_pm_runtime_set_active_enabled(struct device *dev)
+{
+ int err;
+
+ err = pm_runtime_set_active(dev);
+ if (err)
+ return err;
+
+ err = devm_add_action_or_reset(dev, pm_runtime_set_suspended_action, dev);
+ if (err)
+ return err;
+
+ return devm_pm_runtime_enable(dev);
+}
+EXPORT_SYMBOL_GPL(devm_pm_runtime_set_active_enabled);
+
static void pm_runtime_disable_action(void *data)
{
pm_runtime_dont_use_autosuspend(data);
@@ -1521,13 +1632,34 @@ int devm_pm_runtime_enable(struct device *dev)
}
EXPORT_SYMBOL_GPL(devm_pm_runtime_enable);
+static void pm_runtime_put_noidle_action(void *data)
+{
+ pm_runtime_put_noidle(data);
+}
+
+/**
+ * devm_pm_runtime_get_noresume - devres-enabled version of pm_runtime_get_noresume.
+ *
+ * @dev: Device to handle.
+ */
+int devm_pm_runtime_get_noresume(struct device *dev)
+{
+ pm_runtime_get_noresume(dev);
+
+ return devm_add_action_or_reset(dev, pm_runtime_put_noidle_action, dev);
+}
+EXPORT_SYMBOL_GPL(devm_pm_runtime_get_noresume);
+
/**
* pm_runtime_forbid - Block runtime PM of a device.
* @dev: Device to handle.
*
- * Increase the device's usage count and clear its power.runtime_auto flag,
- * so that it cannot be suspended at run time until pm_runtime_allow() is called
- * for it.
+ * Resume @dev if already suspended and block runtime suspend of @dev in such
+ * a way that it can be unblocked via the /sys/devices/.../power/control
+ * interface, or otherwise by calling pm_runtime_allow().
+ *
+ * Calling this function many times in a row has the same effect as calling it
+ * once.
*/
void pm_runtime_forbid(struct device *dev)
{
@@ -1548,7 +1680,13 @@ EXPORT_SYMBOL_GPL(pm_runtime_forbid);
* pm_runtime_allow - Unblock runtime PM of a device.
* @dev: Device to handle.
*
- * Decrease the device's usage count and set its power.runtime_auto flag.
+ * Unblock runtime suspend of @dev after it has been blocked by
+ * pm_runtime_forbid() (for instance, if it has been blocked via the
+ * /sys/devices/.../power/control interface), check if @dev can be
+ * suspended and suspend it in that case.
+ *
+ * Calling this function many times in a row has the same effect as calling it
+ * once.
*/
void pm_runtime_allow(struct device *dev)
{
@@ -1563,7 +1701,7 @@ void pm_runtime_allow(struct device *dev)
if (ret == 0)
rpm_idle(dev, RPM_AUTO | RPM_ASYNC);
else if (ret > 0)
- trace_rpm_usage_rcuidle(dev, RPM_AUTO | RPM_ASYNC);
+ trace_rpm_usage(dev, RPM_AUTO | RPM_ASYNC);
out:
spin_unlock_irq(&dev->power.lock);
@@ -1603,6 +1741,7 @@ void pm_runtime_irq_safe(struct device *dev)
{
if (dev->parent)
pm_runtime_get_sync(dev->parent);
+
spin_lock_irq(&dev->power.lock);
dev->power.irq_safe = 1;
spin_unlock_irq(&dev->power.lock);
@@ -1632,7 +1771,7 @@ static void update_autosuspend(struct device *dev, int old_delay, int old_use)
atomic_inc(&dev->power.usage_count);
rpm_resume(dev, 0);
} else {
- trace_rpm_usage_rcuidle(dev, 0);
+ trace_rpm_usage(dev, 0);
}
}
@@ -1713,12 +1852,12 @@ void pm_runtime_init(struct device *dev)
dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE;
dev->power.deferred_resume = false;
- dev->power.needs_force_resume = 0;
+ dev->power.needs_force_resume = false;
INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0;
- hrtimer_init(&dev->power.suspend_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- dev->power.suspend_timer.function = pm_suspend_timer_fn;
+ hrtimer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS);
init_waitqueue_head(&dev->power.wait_queue);
}
@@ -1740,6 +1879,11 @@ void pm_runtime_reinit(struct device *dev)
pm_runtime_put(dev->parent);
}
}
+ /*
+ * Clear power.needs_force_resume in case it has been set by
+ * pm_runtime_force_suspend() invoked from a driver remove callback.
+ */
+ dev->power.needs_force_resume = false;
}
/**
@@ -1763,9 +1907,8 @@ void pm_runtime_get_suppliers(struct device *dev)
idx = device_links_read_lock();
- list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
- device_links_read_lock_held())
- if (link->flags & DL_FLAG_PM_RUNTIME) {
+ dev_for_each_link_to_supplier(link, dev)
+ if (device_link_test(link, DL_FLAG_PM_RUNTIME)) {
link->supplier_preactivated = true;
pm_runtime_get_sync(link->supplier);
}
@@ -1819,7 +1962,7 @@ static void pm_runtime_drop_link_count(struct device *dev)
*/
void pm_runtime_drop_link(struct device_link *link)
{
- if (!(link->flags & DL_FLAG_PM_RUNTIME))
+ if (!device_link_test(link, DL_FLAG_PM_RUNTIME))
return;
pm_runtime_drop_link_count(link->consumer);
@@ -1827,13 +1970,23 @@ void pm_runtime_drop_link(struct device_link *link)
pm_request_idle(link->supplier);
}
-static bool pm_runtime_need_not_resume(struct device *dev)
+static pm_callback_t get_callback(struct device *dev, size_t cb_offset)
{
- return atomic_read(&dev->power.usage_count) <= 1 &&
- (atomic_read(&dev->power.child_count) == 0 ||
- dev->power.ignore_children);
+ /*
+ * Setting power.strict_midlayer means that the middle layer
+ * code does not want its runtime PM callbacks to be invoked via
+ * pm_runtime_force_suspend() and pm_runtime_force_resume(), so
+ * return a direct pointer to the driver callback in that case.
+ */
+ if (dev_pm_strict_midlayer_is_set(dev))
+ return __rpm_get_driver_callback(dev, cb_offset);
+
+ return __rpm_get_callback(dev, cb_offset);
}
+#define GET_CALLBACK(dev, callback) \
+ get_callback(dev, offsetof(struct dev_pm_ops, callback))
+
/**
* pm_runtime_force_suspend - Force a device into suspend state if needed.
* @dev: Device to suspend.
@@ -1857,10 +2010,10 @@ int pm_runtime_force_suspend(struct device *dev)
int ret;
pm_runtime_disable(dev);
- if (pm_runtime_status_suspended(dev))
+ if (pm_runtime_status_suspended(dev) || dev->power.needs_force_resume)
return 0;
- callback = RPM_GET_CALLBACK(dev, runtime_suspend);
+ callback = GET_CALLBACK(dev, runtime_suspend);
dev_pm_enable_wake_irq_check(dev, true);
ret = callback ? callback(dev) : 0;
@@ -1872,15 +2025,16 @@ int pm_runtime_force_suspend(struct device *dev)
/*
* If the device can stay in suspend after the system-wide transition
* to the working state that will follow, drop the children counter of
- * its parent, but set its status to RPM_SUSPENDED anyway in case this
- * function will be called again for it in the meantime.
+ * its parent and the usage counters of its suppliers. Otherwise, set
+ * power.needs_force_resume to let pm_runtime_force_resume() know that
+ * the device needs to be taken care of and to prevent this function
+ * from handling the device again in case the device is passed to it
+ * once more subsequently.
*/
- if (pm_runtime_need_not_resume(dev)) {
+ if (pm_runtime_need_not_resume(dev))
pm_runtime_set_suspended(dev);
- } else {
- __update_runtime_status(dev, RPM_SUSPENDED);
- dev->power.needs_force_resume = 1;
- }
+ else
+ dev->power.needs_force_resume = true;
return 0;
@@ -1891,33 +2045,37 @@ err:
}
EXPORT_SYMBOL_GPL(pm_runtime_force_suspend);
+#ifdef CONFIG_PM_SLEEP
+
/**
* pm_runtime_force_resume - Force a device into resume state if needed.
* @dev: Device to resume.
*
- * Prior invoking this function we expect the user to have brought the device
- * into low power state by a call to pm_runtime_force_suspend(). Here we reverse
- * those actions and bring the device into full power, if it is expected to be
- * used on system resume. In the other case, we defer the resume to be managed
- * via runtime PM.
+ * This function expects that either pm_runtime_force_suspend() has put the
+ * device into a low-power state prior to calling it, or the device had been
+ * runtime-suspended before the preceding system-wide suspend transition and it
+ * was left in suspend during that transition.
+ *
+ * The actions carried out by pm_runtime_force_suspend(), or by a runtime
+ * suspend in general, are reversed and the device is brought back into full
+ * power if it is expected to be used on system resume, which is the case when
+ * its needs_force_resume flag is set or when its smart_suspend flag is set and
+ * its runtime PM status is "active".
*
- * Typically this function may be invoked from a system resume callback.
+ * In other cases, the resume is deferred to be managed via runtime PM.
+ *
+ * Typically, this function may be invoked from a system resume callback.
*/
int pm_runtime_force_resume(struct device *dev)
{
int (*callback)(struct device *);
int ret = 0;
- if (!pm_runtime_status_suspended(dev) || !dev->power.needs_force_resume)
+ if (!dev->power.needs_force_resume && (!dev_pm_smart_suspend(dev) ||
+ pm_runtime_status_suspended(dev)))
goto out;
- /*
- * The value of the parent's children counter is correct already, so
- * just update the status of the device.
- */
- __update_runtime_status(dev, RPM_ACTIVE);
-
- callback = RPM_GET_CALLBACK(dev, runtime_resume);
+ callback = GET_CALLBACK(dev, runtime_resume);
dev_pm_disable_wake_irq_check(dev, false);
ret = callback ? callback(dev) : 0;
@@ -1928,9 +2086,30 @@ int pm_runtime_force_resume(struct device *dev)
}
pm_runtime_mark_last_busy(dev);
+
out:
- dev->power.needs_force_resume = 0;
+ /*
+ * The smart_suspend flag can be cleared here because it is not going
+ * to be necessary until the next system-wide suspend transition that
+ * will update it again.
+ */
+ dev->power.smart_suspend = false;
+ /*
+ * Also clear needs_force_resume to make this function skip devices that
+ * have been seen by it once.
+ */
+ dev->power.needs_force_resume = false;
+
pm_runtime_enable(dev);
return ret;
}
EXPORT_SYMBOL_GPL(pm_runtime_force_resume);
+
+bool pm_runtime_need_not_resume(struct device *dev)
+{
+ return atomic_read(&dev->power.usage_count) <= 1 &&
+ (atomic_read(&dev->power.child_count) == 0 ||
+ dev->power.ignore_children);
+}
+
+#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index a1474fb67db9..13b31a3adc77 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -6,7 +6,6 @@
#include <linux/export.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
-#include <linux/pm_wakeup.h>
#include <linux/atomic.h>
#include <linux/jiffies.h>
#include "power.h"
@@ -509,14 +508,6 @@ static ssize_t wakeup_last_time_ms_show(struct device *dev,
return sysfs_emit(buf, "%lld\n", msec);
}
-static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid,
- kgid_t kgid)
-{
- if (dev->power.wakeup && dev->power.wakeup->dev)
- return device_change_owner(dev->power.wakeup->dev, kuid, kgid);
- return 0;
-}
-
static DEVICE_ATTR_RO(wakeup_last_time_ms);
#ifdef CONFIG_PM_AUTOSLEEP
@@ -541,6 +532,15 @@ static ssize_t wakeup_prevent_sleep_time_ms_show(struct device *dev,
static DEVICE_ATTR_RO(wakeup_prevent_sleep_time_ms);
#endif /* CONFIG_PM_AUTOSLEEP */
+
+static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid,
+ kgid_t kgid)
+{
+ if (dev->power.wakeup && dev->power.wakeup->dev)
+ return device_change_owner(dev->power.wakeup->dev, kuid, kgid);
+ return 0;
+}
+
#else /* CONFIG_PM_SLEEP */
static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid,
kgid_t kgid)
@@ -611,15 +611,9 @@ static DEVICE_ATTR_RW(async);
#endif /* CONFIG_PM_ADVANCED_DEBUG */
static struct attribute *power_attrs[] = {
-#ifdef CONFIG_PM_ADVANCED_DEBUG
-#ifdef CONFIG_PM_SLEEP
+#if defined(CONFIG_PM_ADVANCED_DEBUG) && defined(CONFIG_PM_SLEEP)
&dev_attr_async.attr,
#endif
- &dev_attr_runtime_status.attr,
- &dev_attr_runtime_usage.attr,
- &dev_attr_runtime_active_kids.attr,
- &dev_attr_runtime_enabled.attr,
-#endif /* CONFIG_PM_ADVANCED_DEBUG */
NULL,
};
static const struct attribute_group pm_attr_group = {
@@ -650,13 +644,16 @@ static const struct attribute_group pm_wakeup_attr_group = {
};
static struct attribute *runtime_attrs[] = {
-#ifndef CONFIG_PM_ADVANCED_DEBUG
&dev_attr_runtime_status.attr,
-#endif
&dev_attr_control.attr,
&dev_attr_runtime_suspended_time.attr,
&dev_attr_runtime_active_time.attr,
&dev_attr_autosuspend_delay_ms.attr,
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ &dev_attr_runtime_usage.attr,
+ &dev_attr_runtime_active_kids.attr,
+ &dev_attr_runtime_enabled.attr,
+#endif
NULL,
};
static const struct attribute_group pm_runtime_attr_group = {
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index 72b7a92337b1..d8da7195bb00 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -120,7 +120,7 @@ static unsigned int read_magic_time(void)
struct rtc_time time;
unsigned int val;
- if (mc146818_get_time(&time) < 0) {
+ if (mc146818_get_time(&time, 1000) < 0) {
pr_err("Unable to read current time from RTC\n");
return 0;
}
@@ -238,10 +238,8 @@ int show_trace_dev_match(char *buf, size_t size)
unsigned int hash = hash_string(DEVSEED, dev_name(dev),
DEVHASH);
if (hash == value) {
- int len = snprintf(buf, size, "%s\n",
+ int len = scnprintf(buf, size, "%s\n",
dev_driver_string(dev));
- if (len > size)
- len = size;
buf += len;
ret += len;
size -= len;
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index d487a6bac630..8aa28c08b289 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -103,6 +103,32 @@ void dev_pm_clear_wake_irq(struct device *dev)
}
EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+static void devm_pm_clear_wake_irq(void *dev)
+{
+ dev_pm_clear_wake_irq(dev);
+}
+
+/**
+ * devm_pm_set_wake_irq - device-managed variant of dev_pm_set_wake_irq
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ *
+ * Attach a device IO interrupt as a wake IRQ, same with dev_pm_set_wake_irq,
+ * but the device will be auto clear wake capability on driver detach.
+ */
+int devm_pm_set_wake_irq(struct device *dev, int irq)
+{
+ int ret;
+
+ ret = dev_pm_set_wake_irq(dev, irq);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, devm_pm_clear_wake_irq, dev);
+}
+EXPORT_SYMBOL_GPL(devm_pm_set_wake_irq);
+
/**
* handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
* @irq: Device specific dedicated wake-up interrupt
@@ -194,7 +220,6 @@ err_free:
return err;
}
-
/**
* dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
* @dev: Device entry
@@ -206,11 +231,6 @@ err_free:
* Sets up a threaded interrupt handler for a device that has
* a dedicated wake-up interrupt in addition to the device IO
* interrupt.
- *
- * The interrupt starts disabled, and needs to be managed for
- * the device by the bus code or the device driver using
- * dev_pm_enable_wake_irq*() and dev_pm_disable_wake_irq*()
- * functions.
*/
int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
{
@@ -232,11 +252,6 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
* the status of WAKE_IRQ_DEDICATED_REVERSE to tell rpm_suspend()
* to enable dedicated wake-up interrupt after running the runtime suspend
* callback for @dev.
- *
- * The interrupt starts disabled, and needs to be managed for
- * the device by the bus code or the device driver using
- * dev_pm_enable_wake_irq*() and dev_pm_disable_wake_irq*()
- * functions.
*/
int dev_pm_set_dedicated_wake_irq_reverse(struct device *dev, int irq)
{
@@ -245,44 +260,6 @@ int dev_pm_set_dedicated_wake_irq_reverse(struct device *dev, int irq)
EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq_reverse);
/**
- * dev_pm_enable_wake_irq - Enable device wake-up interrupt
- * @dev: Device
- *
- * Optionally called from the bus code or the device driver for
- * runtime_resume() to override the PM runtime core managed wake-up
- * interrupt handling to enable the wake-up interrupt.
- *
- * Note that for runtime_suspend()) the wake-up interrupts
- * should be unconditionally enabled unlike for suspend()
- * that is conditional.
- */
-void dev_pm_enable_wake_irq(struct device *dev)
-{
- struct wake_irq *wirq = dev->power.wakeirq;
-
- if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
- enable_irq(wirq->irq);
-}
-EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
-
-/**
- * dev_pm_disable_wake_irq - Disable device wake-up interrupt
- * @dev: Device
- *
- * Optionally called from the bus code or the device driver for
- * runtime_suspend() to override the PM runtime core managed wake-up
- * interrupt handling to disable the wake-up interrupt.
- */
-void dev_pm_disable_wake_irq(struct device *dev)
-{
- struct wake_irq *wirq = dev->power.wakeirq;
-
- if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
- disable_irq_nosync(wirq->irq);
-}
-EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
-
-/**
* dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt
* @dev: Device
* @can_change_status: Can change wake-up interrupt status
@@ -314,8 +291,10 @@ void dev_pm_enable_wake_irq_check(struct device *dev,
return;
enable:
- if (!can_change_status || !(wirq->status & WAKE_IRQ_DEDICATED_REVERSE))
+ if (!can_change_status || !(wirq->status & WAKE_IRQ_DEDICATED_REVERSE)) {
enable_irq(wirq->irq);
+ wirq->status |= WAKE_IRQ_DEDICATED_ENABLED;
+ }
}
/**
@@ -336,8 +315,10 @@ void dev_pm_disable_wake_irq_check(struct device *dev, bool cond_disable)
if (cond_disable && (wirq->status & WAKE_IRQ_DEDICATED_REVERSE))
return;
- if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED)
+ if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) {
+ wirq->status &= ~WAKE_IRQ_DEDICATED_ENABLED;
disable_irq_nosync(wirq->irq);
+ }
}
/**
@@ -358,8 +339,10 @@ void dev_pm_enable_wake_irq_complete(struct device *dev)
return;
if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED &&
- wirq->status & WAKE_IRQ_DEDICATED_REVERSE)
+ wirq->status & WAKE_IRQ_DEDICATED_REVERSE) {
enable_irq(wirq->irq);
+ wirq->status |= WAKE_IRQ_DEDICATED_ENABLED;
+ }
}
/**
@@ -376,7 +359,7 @@ void dev_pm_arm_wake_irq(struct wake_irq *wirq)
if (device_may_wakeup(wirq->dev)) {
if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED &&
- !pm_runtime_status_suspended(wirq->dev))
+ !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED))
enable_irq(wirq->irq);
enable_irq_wake(wirq->irq);
@@ -399,7 +382,7 @@ void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
disable_irq_wake(wirq->irq);
if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED &&
- !pm_runtime_status_suspended(wirq->dev))
+ !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED))
disable_irq_nosync(wirq->irq);
}
}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index e3befa2c1b66..1e1a0e7eeac5 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -19,11 +19,6 @@
#include "power.h"
-#ifndef CONFIG_SUSPEND
-suspend_state_t pm_suspend_target_state;
-#define pm_suspend_target_state (PM_SUSPEND_ON)
-#endif
-
#define list_for_each_entry_rcu_locked(pos, head, member) \
list_for_each_entry_rcu(pos, head, member, \
srcu_read_lock_held(&wakeup_srcu))
@@ -82,7 +77,7 @@ static DEFINE_IDA(wakeup_ida);
* wakeup_source_create - Create a struct wakeup_source object.
* @name: Name of the new wakeup source.
*/
-struct wakeup_source *wakeup_source_create(const char *name)
+static struct wakeup_source *wakeup_source_create(const char *name)
{
struct wakeup_source *ws;
const char *ws_name;
@@ -111,7 +106,6 @@ err_name:
err_ws:
return NULL;
}
-EXPORT_SYMBOL_GPL(wakeup_source_create);
/*
* Record wakeup_source statistics being deleted into a dummy wakeup_source.
@@ -154,7 +148,7 @@ static void wakeup_source_free(struct wakeup_source *ws)
*
* Use only for wakeup source objects created with wakeup_source_create().
*/
-void wakeup_source_destroy(struct wakeup_source *ws)
+static void wakeup_source_destroy(struct wakeup_source *ws)
{
if (!ws)
return;
@@ -163,13 +157,12 @@ void wakeup_source_destroy(struct wakeup_source *ws)
wakeup_source_record(ws);
wakeup_source_free(ws);
}
-EXPORT_SYMBOL_GPL(wakeup_source_destroy);
/**
* wakeup_source_add - Add given object to the list of wakeup sources.
* @ws: Wakeup source object to add to the list.
*/
-void wakeup_source_add(struct wakeup_source *ws)
+static void wakeup_source_add(struct wakeup_source *ws)
{
unsigned long flags;
@@ -184,32 +177,29 @@ void wakeup_source_add(struct wakeup_source *ws)
list_add_rcu(&ws->entry, &wakeup_sources);
raw_spin_unlock_irqrestore(&events_lock, flags);
}
-EXPORT_SYMBOL_GPL(wakeup_source_add);
/**
* wakeup_source_remove - Remove given object from the wakeup sources list.
* @ws: Wakeup source object to remove from the list.
*/
-void wakeup_source_remove(struct wakeup_source *ws)
+static void wakeup_source_remove(struct wakeup_source *ws)
{
unsigned long flags;
if (WARN_ON(!ws))
return;
+ /*
+ * After shutting down the timer, wakeup_source_activate() will warn if
+ * the given wakeup source is passed to it.
+ */
+ timer_shutdown_sync(&ws->timer);
+
raw_spin_lock_irqsave(&events_lock, flags);
list_del_rcu(&ws->entry);
raw_spin_unlock_irqrestore(&events_lock, flags);
synchronize_srcu(&wakeup_srcu);
-
- del_timer_sync(&ws->timer);
- /*
- * Clear timer.function to make wakeup_source_not_registered() treat
- * this wakeup source as not registered.
- */
- ws->timer.function = NULL;
}
-EXPORT_SYMBOL_GPL(wakeup_source_remove);
/**
* wakeup_source_register - Create wakeup source and add it to the list.
@@ -342,7 +332,7 @@ int device_wakeup_enable(struct device *dev)
if (!dev || !dev->power.can_wakeup)
return -EINVAL;
- if (pm_suspend_target_state != PM_SUSPEND_ON)
+ if (pm_sleep_transition_in_progress())
dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);
ws = wakeup_source_register(dev, dev_name(dev));
@@ -456,16 +446,15 @@ static struct wakeup_source *device_wakeup_detach(struct device *dev)
* Detach the @dev's wakeup source object from it, unregister this wakeup source
* object and destroy it.
*/
-int device_wakeup_disable(struct device *dev)
+void device_wakeup_disable(struct device *dev)
{
struct wakeup_source *ws;
if (!dev || !dev->power.can_wakeup)
- return -EINVAL;
+ return;
ws = device_wakeup_detach(dev);
wakeup_source_unregister(ws);
- return 0;
}
EXPORT_SYMBOL_GPL(device_wakeup_disable);
@@ -507,19 +496,23 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
*/
int device_set_wakeup_enable(struct device *dev, bool enable)
{
- return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev);
+ if (enable)
+ return device_wakeup_enable(dev);
+
+ device_wakeup_disable(dev);
+ return 0;
}
EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
/**
- * wakeup_source_not_registered - validate the given wakeup source.
+ * wakeup_source_not_usable - validate the given wakeup source.
* @ws: Wakeup source to be validated.
*/
-static bool wakeup_source_not_registered(struct wakeup_source *ws)
+static bool wakeup_source_not_usable(struct wakeup_source *ws)
{
/*
- * Use timer struct to check if the given source is initialized
- * by wakeup_source_add.
+ * Use the timer struct to check if the given wakeup source has been
+ * initialized by wakeup_source_add() and it is not going away.
*/
return ws->timer.function != pm_wakeup_timer_fn;
}
@@ -564,8 +557,7 @@ static void wakeup_source_activate(struct wakeup_source *ws)
{
unsigned int cec;
- if (WARN_ONCE(wakeup_source_not_registered(ws),
- "unregistered wakeup source\n"))
+ if (WARN_ONCE(wakeup_source_not_usable(ws), "unusable wakeup source\n"))
return;
ws->active = true;
@@ -615,7 +607,7 @@ void __pm_stay_awake(struct wakeup_source *ws)
spin_lock_irqsave(&ws->lock, flags);
wakeup_source_report_event(ws, false);
- del_timer(&ws->timer);
+ timer_delete(&ws->timer);
ws->timer_expires = 0;
spin_unlock_irqrestore(&ws->lock, flags);
@@ -695,7 +687,7 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
ws->max_time = duration;
ws->last_time = now;
- del_timer(&ws->timer);
+ timer_delete(&ws->timer);
ws->timer_expires = 0;
if (ws->autosleep_enabled)
@@ -765,7 +757,7 @@ EXPORT_SYMBOL_GPL(pm_relax);
*/
static void pm_wakeup_timer_fn(struct timer_list *t)
{
- struct wakeup_source *ws = from_timer(ws, t, timer);
+ struct wakeup_source *ws = timer_container_of(ws, t, timer);
unsigned long flags;
spin_lock_irqsave(&ws->lock, flags);
@@ -944,6 +936,8 @@ void pm_system_irq_wakeup(unsigned int irq_number)
else
irq_number = 0;
+ pm_pr_dbg("Triggering wakeup from IRQ %d\n", irq_number);
+
raw_spin_unlock_irqrestore(&wakeup_irq_lock, flags);
if (irq_number)
diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c
index 924fac493c4f..3ffd427248e8 100644
--- a/drivers/base/power/wakeup_stats.c
+++ b/drivers/base/power/wakeup_stats.c
@@ -34,6 +34,7 @@ wakeup_attr(active_count);
wakeup_attr(event_count);
wakeup_attr(wakeup_count);
wakeup_attr(expire_count);
+wakeup_attr(relax_count);
static ssize_t active_time_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -119,6 +120,7 @@ static struct attribute *wakeup_source_attrs[] = {
&dev_attr_event_count.attr,
&dev_attr_wakeup_count.attr,
&dev_attr_expire_count.attr,
+ &dev_attr_relax_count.attr,
&dev_attr_active_time_ms.attr,
&dev_attr_total_time_ms.attr,
&dev_attr_max_time_ms.attr,
@@ -210,7 +212,7 @@ void wakeup_source_sysfs_remove(struct wakeup_source *ws)
static int __init wakeup_sources_sysfs_init(void)
{
- wakeup_class = class_create(THIS_MODULE, "wakeup");
+ wakeup_class = class_create("wakeup");
return PTR_ERR_OR_ZERO(wakeup_class);
}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index ed6f449f8e5c..6a63860579dd 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -7,22 +7,30 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
-#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
#include <linux/export.h>
-#include <linux/kernel.h>
+#include <linux/kconfig.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_graph.h>
-#include <linux/of_irq.h>
#include <linux/property.h>
#include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
-struct fwnode_handle *dev_fwnode(struct device *dev)
+struct fwnode_handle *__dev_fwnode(struct device *dev)
{
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
of_fwnode_handle(dev->of_node) : dev->fwnode;
}
-EXPORT_SYMBOL_GPL(dev_fwnode);
+EXPORT_SYMBOL_GPL(__dev_fwnode);
+
+const struct fwnode_handle *__dev_fwnode_const(const struct device *dev)
+{
+ return IS_ENABLED(CONFIG_OF) && dev->of_node ?
+ of_fwnode_handle(dev->of_node) : dev->fwnode;
+}
+EXPORT_SYMBOL_GPL(__dev_fwnode_const);
/**
* device_property_present - check if a property of a device is present
@@ -30,8 +38,10 @@ EXPORT_SYMBOL_GPL(dev_fwnode);
* @propname: Name of the property
*
* Check if property @propname is present in the device firmware description.
+ *
+ * Return: true if property @propname is present. Otherwise, returns false.
*/
-bool device_property_present(struct device *dev, const char *propname)
+bool device_property_present(const struct device *dev, const char *propname)
{
return fwnode_property_present(dev_fwnode(dev), propname);
}
@@ -41,6 +51,8 @@ EXPORT_SYMBOL_GPL(device_property_present);
* fwnode_property_present - check if a property of a firmware node is present
* @fwnode: Firmware node whose property to check
* @propname: Name of the property
+ *
+ * Return: true if property @propname is present. Otherwise, returns false.
*/
bool fwnode_property_present(const struct fwnode_handle *fwnode,
const char *propname)
@@ -59,6 +71,44 @@ bool fwnode_property_present(const struct fwnode_handle *fwnode,
EXPORT_SYMBOL_GPL(fwnode_property_present);
/**
+ * device_property_read_bool - Return the value for a boolean property of a device
+ * @dev: Device whose property is being checked
+ * @propname: Name of the property
+ *
+ * Return if property @propname is true or false in the device firmware description.
+ *
+ * Return: true if property @propname is present. Otherwise, returns false.
+ */
+bool device_property_read_bool(const struct device *dev, const char *propname)
+{
+ return fwnode_property_read_bool(dev_fwnode(dev), propname);
+}
+EXPORT_SYMBOL_GPL(device_property_read_bool);
+
+/**
+ * fwnode_property_read_bool - Return the value for a boolean property of a firmware node
+ * @fwnode: Firmware node whose property to check
+ * @propname: Name of the property
+ *
+ * Return if property @propname is true or false in the firmware description.
+ */
+bool fwnode_property_read_bool(const struct fwnode_handle *fwnode,
+ const char *propname)
+{
+ bool ret;
+
+ if (IS_ERR_OR_NULL(fwnode))
+ return false;
+
+ ret = fwnode_call_bool_op(fwnode, property_read_bool, propname);
+ if (ret)
+ return ret;
+
+ return fwnode_call_bool_op(fwnode->secondary, property_read_bool, propname);
+}
+EXPORT_SYMBOL_GPL(fwnode_property_read_bool);
+
+/**
* device_property_read_u8_array - return a u8 array property of a device
* @dev: Device to get the property of
* @propname: Name of the property
@@ -79,7 +129,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_present);
* %-EOVERFLOW if the size of the property is not as expected.
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_read_u8_array(struct device *dev, const char *propname,
+int device_property_read_u8_array(const struct device *dev, const char *propname,
u8 *val, size_t nval)
{
return fwnode_property_read_u8_array(dev_fwnode(dev), propname, val, nval);
@@ -107,7 +157,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array);
* %-EOVERFLOW if the size of the property is not as expected.
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_read_u16_array(struct device *dev, const char *propname,
+int device_property_read_u16_array(const struct device *dev, const char *propname,
u16 *val, size_t nval)
{
return fwnode_property_read_u16_array(dev_fwnode(dev), propname, val, nval);
@@ -135,7 +185,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array);
* %-EOVERFLOW if the size of the property is not as expected.
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_read_u32_array(struct device *dev, const char *propname,
+int device_property_read_u32_array(const struct device *dev, const char *propname,
u32 *val, size_t nval)
{
return fwnode_property_read_u32_array(dev_fwnode(dev), propname, val, nval);
@@ -163,7 +213,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array);
* %-EOVERFLOW if the size of the property is not as expected.
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_read_u64_array(struct device *dev, const char *propname,
+int device_property_read_u64_array(const struct device *dev, const char *propname,
u64 *val, size_t nval)
{
return fwnode_property_read_u64_array(dev_fwnode(dev), propname, val, nval);
@@ -191,7 +241,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
* %-EOVERFLOW if the size of the property is not as expected.
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_read_string_array(struct device *dev, const char *propname,
+int device_property_read_string_array(const struct device *dev, const char *propname,
const char **val, size_t nval)
{
return fwnode_property_read_string_array(dev_fwnode(dev), propname, val, nval);
@@ -213,7 +263,7 @@ EXPORT_SYMBOL_GPL(device_property_read_string_array);
* %-EPROTO or %-EILSEQ if the property type is not a string.
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_read_string(struct device *dev, const char *propname,
+int device_property_read_string(const struct device *dev, const char *propname,
const char **val)
{
return fwnode_property_read_string(dev_fwnode(dev), propname, val);
@@ -229,13 +279,13 @@ EXPORT_SYMBOL_GPL(device_property_read_string);
* Find a given string in a string array and if it is found return the
* index back.
*
- * Return: %0 if the property was found (success),
+ * Return: index, starting from %0, if the property was found (success),
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of strings,
* %-ENXIO if no suitable firmware interface is present.
*/
-int device_property_match_string(struct device *dev, const char *propname,
+int device_property_match_string(const struct device *dev, const char *propname,
const char *string)
{
return fwnode_property_match_string(dev_fwnode(dev), propname, string);
@@ -450,7 +500,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string);
* Find a given string in a string array and if it is found return the
* index back.
*
- * Return: %0 if the property was found (success),
+ * Return: index, starting from %0, if the property was found (success),
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of strings,
@@ -462,7 +512,7 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
const char **values;
int nval, ret;
- nval = fwnode_property_read_string_array(fwnode, propname, NULL, 0);
+ nval = fwnode_property_string_array_count(fwnode, propname);
if (nval < 0)
return nval;
@@ -475,35 +525,72 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
ret = fwnode_property_read_string_array(fwnode, propname, values, nval);
if (ret < 0)
- goto out;
+ goto out_free;
ret = match_string(values, nval, string);
if (ret < 0)
ret = -ENODATA;
-out:
+
+out_free:
kfree(values);
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_match_string);
/**
+ * fwnode_property_match_property_string - find a property string value in an array and return index
+ * @fwnode: Firmware node to get the property of
+ * @propname: Name of the property holding the string value
+ * @array: String array to search in
+ * @n: Size of the @array
+ *
+ * Find a property string value in a given @array and if it is found return
+ * the index back.
+ *
+ * Return: index, starting from %0, if the string value was found in the @array (success),
+ * %-ENOENT when the string value was not found in the @array,
+ * %-EINVAL if given arguments are not valid,
+ * %-ENODATA if the property does not have a value,
+ * %-EPROTO or %-EILSEQ if the property is not a string,
+ * %-ENXIO if no suitable firmware interface is present.
+ */
+int fwnode_property_match_property_string(const struct fwnode_handle *fwnode,
+ const char *propname, const char * const *array, size_t n)
+{
+ const char *string;
+ int ret;
+
+ ret = fwnode_property_read_string(fwnode, propname, &string);
+ if (ret)
+ return ret;
+
+ ret = match_string(array, n, string);
+ if (ret < 0)
+ ret = -ENOENT;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fwnode_property_match_property_string);
+
+/**
* fwnode_property_get_reference_args() - Find a reference with arguments
* @fwnode: Firmware node where to look for the reference
* @prop: The name of the property
* @nargs_prop: The name of the property telling the number of
* arguments in the referred node. NULL if @nargs is known,
- * otherwise @nargs is ignored. Only relevant on OF.
+ * otherwise @nargs is ignored.
* @nargs: Number of arguments. Ignored if @nargs_prop is non-NULL.
* @index: Index of the reference, from zero onwards.
* @args: Result structure with reference and integer arguments.
+ * May be NULL.
*
* Obtain a reference based on a named property in an fwnode, with
* integer arguments.
*
- * Caller is responsible to call fwnode_handle_put() on the returned
- * args->fwnode pointer.
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * @args->fwnode pointer.
*
- * Returns: %0 on success
+ * Return: %0 on success
* %-ENOENT when the index is out of bounds, the index has an empty
* reference or the property was not found
* %-EINVAL on parse error
@@ -539,8 +626,11 @@ EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args);
*
* @index can be used when the named reference holds a table of references.
*
- * Returns pointer to the reference fwnode, or ERR_PTR. Caller is responsible to
- * call fwnode_handle_put() on the returned fwnode pointer.
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
+ *
+ * Return: a pointer to the reference fwnode, when found. Otherwise,
+ * returns an error pointer.
*/
struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode,
const char *name,
@@ -559,7 +649,7 @@ EXPORT_SYMBOL_GPL(fwnode_find_reference);
* fwnode_get_name - Return the name of a node
* @fwnode: The firmware node
*
- * Returns a pointer to the node name.
+ * Return: a pointer to the node name, or %NULL.
*/
const char *fwnode_get_name(const struct fwnode_handle *fwnode)
{
@@ -571,7 +661,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_name);
* fwnode_get_name_prefix - Return the prefix of node for printing purposes
* @fwnode: The firmware node
*
- * Returns the prefix of a node, intended to be printed right before the node.
+ * Return: the prefix of a node, intended to be printed right before the node.
* The prefix works also as a separator between the nodes.
*/
const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode)
@@ -580,10 +670,41 @@ const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode)
}
/**
+ * fwnode_name_eq - Return true if node name is equal
+ * @fwnode: The firmware node
+ * @name: The name to which to compare the node name
+ *
+ * Compare the name provided as an argument to the name of the node, stopping
+ * the comparison at either NUL or '@' character, whichever comes first. This
+ * function is generally used for comparing node names while ignoring the
+ * possible unit address of the node.
+ *
+ * Return: true if the node name matches with the name provided in the @name
+ * argument, false otherwise.
+ */
+bool fwnode_name_eq(const struct fwnode_handle *fwnode, const char *name)
+{
+ const char *node_name;
+ ptrdiff_t len;
+
+ node_name = fwnode_get_name(fwnode);
+ if (!node_name)
+ return false;
+
+ len = strchrnul(node_name, '@') - node_name;
+
+ return str_has_prefix(node_name, name) == len;
+}
+EXPORT_SYMBOL_GPL(fwnode_name_eq);
+
+/**
* fwnode_get_parent - Return parent firwmare node
* @fwnode: Firmware whose parent is retrieved
*
- * Return parent firmware node of the given node if possible or %NULL if no
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
+ *
+ * Return: parent firmware node of the given node if possible or %NULL if no
* parent was available.
*/
struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode)
@@ -600,8 +721,12 @@ EXPORT_SYMBOL_GPL(fwnode_get_parent);
* on the passed node, making it suitable for iterating through a
* node's parents.
*
- * Returns a node pointer with refcount incremented, use
- * fwnode_handle_node() on it when done.
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer. Note that this function also puts a reference to @fwnode
+ * unconditionally.
+ *
+ * Return: parent firmware node of the given node if possible or %NULL if no
+ * parent was available.
*/
struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode)
{
@@ -614,36 +739,10 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(fwnode_get_next_parent);
/**
- * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode
- * @fwnode: firmware node
- *
- * Given a firmware node (@fwnode), this function finds its closest ancestor
- * firmware node that has a corresponding struct device and returns that struct
- * device.
- *
- * The caller of this function is expected to call put_device() on the returned
- * device when they are done.
- */
-struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode)
-{
- struct fwnode_handle *parent;
- struct device *dev;
-
- fwnode_for_each_parent_node(fwnode, parent) {
- dev = get_dev_from_fwnode(parent);
- if (dev) {
- fwnode_handle_put(parent);
- return dev;
- }
- }
- return NULL;
-}
-
-/**
* fwnode_count_parents - Return the number of parents a node has
* @fwnode: The node the parents of which are to be counted
*
- * Returns the number of parents a node has.
+ * Return: the number of parents a node has.
*/
unsigned int fwnode_count_parents(const struct fwnode_handle *fwnode)
{
@@ -662,12 +761,12 @@ EXPORT_SYMBOL_GPL(fwnode_count_parents);
* @fwnode: The node the parent of which is requested
* @depth: Distance of the parent from the node
*
- * Returns the nth parent of a node. If there is no parent at the requested
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
+ *
+ * Return: the nth parent of a node. If there is no parent at the requested
* @depth, %NULL is returned. If @depth is 0, the functionality is equivalent to
* fwnode_handle_get(). For @depth == 1, it is fwnode_get_parent() and so on.
- *
- * The caller is responsible for calling fwnode_handle_put() for the returned
- * node.
*/
struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode,
unsigned int depth)
@@ -686,37 +785,13 @@ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode,
EXPORT_SYMBOL_GPL(fwnode_get_nth_parent);
/**
- * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child
- * @ancestor: Firmware which is tested for being an ancestor
- * @child: Firmware which is tested for being the child
- *
- * A node is considered an ancestor of itself too.
- *
- * Returns true if @ancestor is an ancestor of @child. Otherwise, returns false.
- */
-bool fwnode_is_ancestor_of(struct fwnode_handle *ancestor, struct fwnode_handle *child)
-{
- struct fwnode_handle *parent;
-
- if (IS_ERR_OR_NULL(ancestor))
- return false;
-
- if (child == ancestor)
- return true;
-
- fwnode_for_each_parent_node(child, parent) {
- if (parent == ancestor) {
- fwnode_handle_put(parent);
- return true;
- }
- }
- return false;
-}
-
-/**
* fwnode_get_next_child_node - Return the next child node handle for a node
* @fwnode: Firmware node to find the next child node for.
* @child: Handle to one of the node's child nodes or a %NULL handle.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer. Note that this function also puts a reference to @child
+ * unconditionally.
*/
struct fwnode_handle *
fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
@@ -727,10 +802,13 @@ fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
/**
- * fwnode_get_next_available_child_node - Return the next
- * available child node handle for a node
+ * fwnode_get_next_available_child_node - Return the next available child node handle for a node
* @fwnode: Firmware node to find the next child node for.
* @child: Handle to one of the node's child nodes or a %NULL handle.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer. Note that this function also puts a reference to @child
+ * unconditionally.
*/
struct fwnode_handle *
fwnode_get_next_available_child_node(const struct fwnode_handle *fwnode,
@@ -754,9 +832,13 @@ EXPORT_SYMBOL_GPL(fwnode_get_next_available_child_node);
/**
* device_get_next_child_node - Return the next child node handle for a device
* @dev: Device to find the next child node for.
- * @child: Handle to one of the device's child nodes or a null handle.
+ * @child: Handle to one of the device's child nodes or a %NULL handle.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer. Note that this function also puts a reference to @child
+ * unconditionally.
*/
-struct fwnode_handle *device_get_next_child_node(struct device *dev,
+struct fwnode_handle *device_get_next_child_node(const struct device *dev,
struct fwnode_handle *child)
{
const struct fwnode_handle *fwnode = dev_fwnode(dev);
@@ -779,6 +861,9 @@ EXPORT_SYMBOL_GPL(device_get_next_child_node);
* fwnode_get_named_child_node - Return first matching named child node handle
* @fwnode: Firmware node to find the named child node for.
* @childname: String to match child node name against.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
*/
struct fwnode_handle *
fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
@@ -792,8 +877,11 @@ EXPORT_SYMBOL_GPL(fwnode_get_named_child_node);
* device_get_named_child_node - Return first matching named child node handle
* @dev: Device to find the named child node for.
* @childname: String to match child node name against.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
*/
-struct fwnode_handle *device_get_named_child_node(struct device *dev,
+struct fwnode_handle *device_get_named_child_node(const struct device *dev,
const char *childname)
{
return fwnode_get_named_child_node(dev_fwnode(dev), childname);
@@ -804,7 +892,10 @@ EXPORT_SYMBOL_GPL(device_get_named_child_node);
* fwnode_handle_get - Obtain a reference to a device node
* @fwnode: Pointer to the device node to obtain the reference to.
*
- * Returns the fwnode handle.
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
+ *
+ * Return: the fwnode handle.
*/
struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
{
@@ -816,23 +907,11 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(fwnode_handle_get);
/**
- * fwnode_handle_put - Drop reference to a device node
- * @fwnode: Pointer to the device node to drop the reference to.
- *
- * This has to be used when terminating device_for_each_child_node() iteration
- * with break or return to prevent stale device node references from being left
- * behind.
- */
-void fwnode_handle_put(struct fwnode_handle *fwnode)
-{
- fwnode_call_void_op(fwnode, put);
-}
-EXPORT_SYMBOL_GPL(fwnode_handle_put);
-
-/**
* fwnode_device_is_available - check if a device is available for use
* @fwnode: Pointer to the fwnode of the device.
*
+ * Return: true if device is available for use. Otherwise, returns false.
+ *
* For fwnode node types that don't implement the .device_is_available()
* operation, this function returns true.
*/
@@ -849,28 +928,57 @@ bool fwnode_device_is_available(const struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(fwnode_device_is_available);
/**
- * device_get_child_node_count - return the number of child nodes for device
- * @dev: Device to cound the child nodes for
+ * fwnode_get_child_node_count - return the number of child nodes for a given firmware node
+ * @fwnode: Pointer to the parent firmware node
+ *
+ * Return: the number of child nodes for a given firmware node.
+ */
+unsigned int fwnode_get_child_node_count(const struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *child;
+ unsigned int count = 0;
+
+ fwnode_for_each_child_node(fwnode, child)
+ count++;
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(fwnode_get_child_node_count);
+
+/**
+ * fwnode_get_named_child_node_count - number of child nodes with given name
+ * @fwnode: Node which child nodes are counted.
+ * @name: String to match child node name against.
+ *
+ * Scan child nodes and count all the nodes with a specific name. Potential
+ * 'number' -ending after the 'at sign' for scanned names is ignored.
+ * E.g.::
+ * fwnode_get_named_child_node_count(fwnode, "channel");
+ * would match all the nodes::
+ * channel { }, channel@0 {}, channel@0xabba {}...
+ *
+ * Return: the number of child nodes with a matching name for a given device.
*/
-unsigned int device_get_child_node_count(struct device *dev)
+unsigned int fwnode_get_named_child_node_count(const struct fwnode_handle *fwnode,
+ const char *name)
{
struct fwnode_handle *child;
unsigned int count = 0;
- device_for_each_child_node(dev, child)
+ fwnode_for_each_named_child_node(fwnode, child, name)
count++;
return count;
}
-EXPORT_SYMBOL_GPL(device_get_child_node_count);
+EXPORT_SYMBOL_GPL(fwnode_get_named_child_node_count);
-bool device_dma_supported(struct device *dev)
+bool device_dma_supported(const struct device *dev)
{
return fwnode_call_bool_op(dev_fwnode(dev), device_dma_supported);
}
EXPORT_SYMBOL_GPL(device_dma_supported);
-enum dev_dma_attr device_get_dma_attr(struct device *dev)
+enum dev_dma_attr device_get_dma_attr(const struct device *dev)
{
if (!fwnode_has_op(dev_fwnode(dev), device_get_dma_attr))
return DEV_DMA_NOT_SUPPORTED;
@@ -887,7 +995,7 @@ EXPORT_SYMBOL_GPL(device_get_dma_attr);
* 'phy-connection-type', and return its index in phy_modes table, or errno in
* error case.
*/
-int fwnode_get_phy_mode(struct fwnode_handle *fwnode)
+int fwnode_get_phy_mode(const struct fwnode_handle *fwnode)
{
const char *pm;
int err, i;
@@ -926,7 +1034,7 @@ EXPORT_SYMBOL_GPL(device_get_phy_mode);
* @fwnode: Pointer to the firmware node
* @index: Index of the IO range
*
- * Returns a pointer to the mapped memory.
+ * Return: a pointer to the mapped memory.
*/
void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index)
{
@@ -939,12 +1047,18 @@ EXPORT_SYMBOL(fwnode_iomap);
* @fwnode: Pointer to the firmware node
* @index: Zero-based index of the IRQ
*
- * Returns Linux IRQ number on success. Other values are determined
- * accordingly to acpi_/of_ irq_get() operation.
+ * Return: Linux IRQ number on success. Negative errno on failure.
*/
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index)
{
- return fwnode_call_int_op(fwnode, irq_get, index);
+ int ret;
+
+ ret = fwnode_call_int_op(fwnode, irq_get, index);
+ /* We treat mapping errors as invalid case */
+ if (ret == 0)
+ return -EINVAL;
+
+ return ret;
}
EXPORT_SYMBOL(fwnode_irq_get);
@@ -959,8 +1073,7 @@ EXPORT_SYMBOL(fwnode_irq_get);
* number of the IRQ resource corresponding to the index of the matched
* string.
*
- * Return:
- * Linux IRQ number on success, or negative errno otherwise.
+ * Return: Linux IRQ number on success, or negative errno otherwise.
*/
int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name)
{
@@ -982,33 +1095,43 @@ EXPORT_SYMBOL(fwnode_irq_get_byname);
* @fwnode: Pointer to the parent firmware node
* @prev: Previous endpoint node or %NULL to get the first
*
- * Returns an endpoint firmware node pointer or %NULL if no more endpoints
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer. Note that this function also puts a reference to @prev
+ * unconditionally.
+ *
+ * Return: an endpoint firmware node pointer or %NULL if no more endpoints
* are available.
*/
struct fwnode_handle *
fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
+ struct fwnode_handle *ep, *port_parent = NULL;
const struct fwnode_handle *parent;
- struct fwnode_handle *ep;
/*
* If this function is in a loop and the previous iteration returned
* an endpoint from fwnode->secondary, then we need to use the secondary
* as parent rather than @fwnode.
*/
- if (prev)
- parent = fwnode_graph_get_port_parent(prev);
- else
+ if (prev) {
+ port_parent = fwnode_graph_get_port_parent(prev);
+ parent = port_parent;
+ } else {
parent = fwnode;
+ }
if (IS_ERR_OR_NULL(parent))
return NULL;
ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev);
if (ep)
- return ep;
+ goto out_put_port_parent;
+
+ ep = fwnode_graph_get_next_endpoint(parent->secondary, NULL);
- return fwnode_graph_get_next_endpoint(parent->secondary, NULL);
+out_put_port_parent:
+ fwnode_handle_put(port_parent);
+ return ep;
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
@@ -1016,6 +1139,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
* fwnode_graph_get_port_parent - Return the device fwnode of a port endpoint
* @endpoint: Endpoint firmware node of the port
*
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
+ *
* Return: the firmware node of the device the @endpoint belongs to.
*/
struct fwnode_handle *
@@ -1037,6 +1163,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_port_parent);
* @fwnode: Endpoint firmware node pointing to the remote endpoint
*
* Extracts firmware node of a remote device the @fwnode points to.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
*/
struct fwnode_handle *
fwnode_graph_get_remote_port_parent(const struct fwnode_handle *fwnode)
@@ -1057,6 +1186,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port_parent);
* @fwnode: Endpoint firmware node pointing to the remote endpoint
*
* Extracts firmware node of a remote port the @fwnode points to.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
*/
struct fwnode_handle *
fwnode_graph_get_remote_port(const struct fwnode_handle *fwnode)
@@ -1070,6 +1202,9 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_port);
* @fwnode: Endpoint firmware node pointing to the remote endpoint
*
* Extracts firmware node of a remote endpoint the @fwnode points to.
+ *
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
*/
struct fwnode_handle *
fwnode_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
@@ -1097,8 +1232,11 @@ static bool fwnode_graph_remote_available(struct fwnode_handle *ep)
* @endpoint: identifier of the endpoint node under the port node
* @flags: fwnode lookup flags
*
- * Return the fwnode handle of the local endpoint corresponding the port and
- * endpoint IDs or NULL if not found.
+ * The caller is responsible for calling fwnode_handle_put() on the returned
+ * fwnode pointer.
+ *
+ * Return: the fwnode handle of the local endpoint corresponding the port and
+ * endpoint IDs or %NULL if not found.
*
* If FWNODE_GRAPH_ENDPOINT_NEXT is passed in @flags and the specified endpoint
* has not been found, look for the closest endpoint ID greater than the
@@ -1106,9 +1244,6 @@ static bool fwnode_graph_remote_available(struct fwnode_handle *ep)
*
* Does not return endpoints that belong to disabled devices or endpoints that
* are unconnected, unless FWNODE_GRAPH_DEVICE_DISABLED is passed in @flags.
- *
- * The returned endpoint needs to be released by calling fwnode_handle_put() on
- * it when it is not needed any more.
*/
struct fwnode_handle *
fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode,
@@ -1166,7 +1301,7 @@ EXPORT_SYMBOL_GPL(fwnode_graph_get_endpoint_by_id);
* If FWNODE_GRAPH_DEVICE_DISABLED flag is specified, also unconnected endpoints
* and endpoints connected to disabled devices are counted.
*/
-unsigned int fwnode_graph_get_endpoint_count(struct fwnode_handle *fwnode,
+unsigned int fwnode_graph_get_endpoint_count(const struct fwnode_handle *fwnode,
unsigned long flags)
{
struct fwnode_handle *ep;
@@ -1200,13 +1335,13 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
}
EXPORT_SYMBOL(fwnode_graph_parse_endpoint);
-const void *device_get_match_data(struct device *dev)
+const void *device_get_match_data(const struct device *dev)
{
return fwnode_call_ptr_op(dev_fwnode(dev), device_get_match_data, dev);
}
EXPORT_SYMBOL_GPL(device_get_match_data);
-static unsigned int fwnode_graph_devcon_matches(struct fwnode_handle *fwnode,
+static unsigned int fwnode_graph_devcon_matches(const struct fwnode_handle *fwnode,
const char *con_id, void *data,
devcon_match_fn_t match,
void **matches,
@@ -1240,7 +1375,7 @@ static unsigned int fwnode_graph_devcon_matches(struct fwnode_handle *fwnode,
return count;
}
-static unsigned int fwnode_devcon_matches(struct fwnode_handle *fwnode,
+static unsigned int fwnode_devcon_matches(const struct fwnode_handle *fwnode,
const char *con_id, void *data,
devcon_match_fn_t match,
void **matches,
@@ -1282,7 +1417,7 @@ static unsigned int fwnode_devcon_matches(struct fwnode_handle *fwnode,
* device node. @match will be used to convert the connection description to
* data the caller is expecting to be returned.
*/
-void *fwnode_connection_find_match(struct fwnode_handle *fwnode,
+void *fwnode_connection_find_match(const struct fwnode_handle *fwnode,
const char *con_id, void *data,
devcon_match_fn_t match)
{
@@ -1314,12 +1449,13 @@ EXPORT_SYMBOL_GPL(fwnode_connection_find_match);
* @fwnode and other device nodes. @match will be used to convert the
* connection description to data the caller is expecting to be returned
* through the @matches array.
- * If @matches is NULL @matches_len is ignored and the total number of resolved
+ *
+ * If @matches is %NULL @matches_len is ignored and the total number of resolved
* matches is returned.
*
* Return: Number of matches resolved, or negative errno.
*/
-int fwnode_connection_find_matches(struct fwnode_handle *fwnode,
+int fwnode_connection_find_matches(const struct fwnode_handle *fwnode,
const char *con_id, void *data,
devcon_match_fn_t match,
void **matches, unsigned int matches_len)
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 159bac6c5046..ffb2ef488298 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -4,15 +4,32 @@
# subsystems should select the appropriate symbols.
config REGMAP
- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO)
- select IRQ_DOMAIN if REGMAP_IRQ
- select MDIO_BUS if REGMAP_MDIO
- bool
-
-config REGCACHE_COMPRESSED
- select LZO_COMPRESS
- select LZO_DECOMPRESS
bool
+ default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI)
+ help
+ Enable support for the Register Map (regmap) access API.
+
+ Usually, this option is automatically selected when needed.
+ However, you may want to enable it manually for running the regmap
+ KUnit tests.
+
+ If unsure, say N.
+
+config REGMAP_KUNIT
+ tristate "KUnit tests for regmap"
+ depends on KUNIT && REGMAP
+ default KUNIT_ALL_TESTS
+ select REGMAP_RAM
+
+config REGMAP_BUILD
+ bool "Enable regmap build"
+ depends on KUNIT
+ select REGMAP
+ help
+ This option exists purely to allow the regmap KUnit tests to
+ be enabled without having to enable some driver that uses
+ regmap due to unfortunate issues with how KUnit tests are
+ normally enabled.
config REGMAP_AC97
tristate
@@ -39,12 +56,17 @@ config REGMAP_W1
config REGMAP_MDIO
tristate
+ select MDIO_BUS
config REGMAP_MMIO
tristate
config REGMAP_IRQ
bool
+ select IRQ_DOMAIN
+
+config REGMAP_RAM
+ tristate
config REGMAP_SOUNDWIRE
tristate
@@ -65,3 +87,7 @@ config REGMAP_I3C
config REGMAP_SPI_AVMM
tristate
depends on SPI
+
+config REGMAP_FSI
+ tristate
+ depends on FSI
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 11facb32a027..5fdd0845b45e 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -3,11 +3,12 @@
CFLAGS_regmap.o := -I$(src)
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
-obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
+obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o regcache-maple.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_KUNIT) += regmap-kunit.o
obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_RAM) += regmap-ram.o regmap-raw-ram.o
obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
@@ -20,3 +21,4 @@ obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o
obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o
obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o
obj-$(CONFIG_REGMAP_MDIO) += regmap-mdio.o
+obj-$(CONFIG_REGMAP_FSI) += regmap-fsi.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index da8996e7a1f1..1477329410ec 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -31,8 +31,8 @@ struct regmap_format {
size_t buf_size;
size_t reg_bytes;
size_t pad_bytes;
- size_t reg_downshift;
size_t val_bytes;
+ s8 reg_shift;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
@@ -59,6 +59,7 @@ struct regmap {
unsigned long raw_spinlock_flags;
};
};
+ struct lock_class_key *lock_key;
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */
@@ -72,12 +73,12 @@ struct regmap {
void *bus_context;
const char *name;
- bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;
+ bool async;
#ifdef CONFIG_DEBUG_FS
bool debugfs_disable;
@@ -93,6 +94,7 @@ struct regmap {
#endif
unsigned int max_register;
+ bool max_register_is_set;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
@@ -115,8 +117,6 @@ struct regmap {
void *val_buf, size_t val_size);
int (*write)(void *context, const void *data, size_t count);
- bool defer_caching;
-
unsigned long read_flag_mask;
unsigned long write_flag_mask;
@@ -125,6 +125,11 @@ struct regmap {
int reg_stride;
int reg_stride_order;
+ bool defer_caching;
+
+ /* If set, will always write field to HW. */
+ bool force_write_field;
+
/* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type;
@@ -156,6 +161,9 @@ struct regmap {
struct reg_sequence *patch;
int patch_regs;
+ /* if set, the regmap core can sleep */
+ bool can_sleep;
+
/* if set, converts bulk read to single read */
bool use_single_read;
/* if set, converts bulk write to single write */
@@ -171,9 +179,6 @@ struct regmap {
void *selector_work_buf; /* Scratch buffer used for selector */
struct hwspinlock *hwlock;
-
- /* if set, the regmap core can sleep */
- bool can_sleep;
};
struct regcache_ops {
@@ -181,6 +186,7 @@ struct regcache_ops {
enum regcache_type type;
int (*init)(struct regmap *map);
int (*exit)(struct regmap *map);
+ int (*populate)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
void (*debugfs_init)(struct regmap *map);
#endif
@@ -257,6 +263,8 @@ int regcache_sync_block(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end);
+bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
+ unsigned int val);
static inline const void *regcache_get_val_addr(struct regmap *map,
const void *base,
@@ -267,9 +275,10 @@ static inline const void *regcache_get_val_addr(struct regmap *map,
unsigned int regcache_get_val(struct regmap *map, const void *base,
unsigned int idx);
-bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
+void regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val);
int regcache_lookup_reg(struct regmap *map, unsigned int reg);
+int regcache_sync_val(struct regmap *map, unsigned int reg, unsigned int val);
int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc);
@@ -280,8 +289,9 @@ enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config);
+extern struct regcache_ops regcache_flat_sparse_ops;
extern struct regcache_ops regcache_rbtree_ops;
-extern struct regcache_ops regcache_lzo_ops;
+extern struct regcache_ops regcache_maple_ops;
extern struct regcache_ops regcache_flat_ops;
static inline const char *regmap_name(const struct regmap *map)
@@ -307,4 +317,34 @@ static inline unsigned int regcache_get_index_by_order(const struct regmap *map,
return reg >> map->reg_stride_order;
}
+struct regmap_ram_data {
+ unsigned int *vals; /* Allocatd by caller */
+ bool *read;
+ bool *written;
+ enum regmap_endian reg_endian;
+ bool (*noinc_reg)(struct regmap_ram_data *data, unsigned int reg);
+};
+
+/*
+ * Create a test register map with data stored in RAM, not intended
+ * for practical use.
+ */
+struct regmap *__regmap_init_ram(struct device *dev,
+ const struct regmap_config *config,
+ struct regmap_ram_data *data,
+ struct lock_class_key *lock_key,
+ const char *lock_name);
+
+#define regmap_init_ram(dev, config, data) \
+ __regmap_lockdep_wrapper(__regmap_init_ram, #dev, dev, config, data)
+
+struct regmap *__regmap_init_raw_ram(struct device *dev,
+ const struct regmap_config *config,
+ struct regmap_ram_data *data,
+ struct lock_class_key *lock_key,
+ const char *lock_name);
+
+#define regmap_init_raw_ram(dev, config, data) \
+ __regmap_lockdep_wrapper(__regmap_init_raw_ram, #dev, dev, config, data)
+
#endif
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index b7e4b2464102..53cc59c84e2f 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -6,7 +6,11 @@
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
#include <linux/device.h>
+#include <linux/limits.h>
+#include <linux/overflow.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -18,46 +22,92 @@ static inline unsigned int regcache_flat_get_index(const struct regmap *map,
return regcache_get_index_by_order(map, reg);
}
+struct regcache_flat_data {
+ unsigned long *valid;
+ unsigned int data[];
+};
+
static int regcache_flat_init(struct regmap *map)
{
- int i;
- unsigned int *cache;
+ unsigned int cache_size;
+ struct regcache_flat_data *cache;
- if (!map || map->reg_stride_order < 0 || !map->max_register)
+ if (!map || map->reg_stride_order < 0 || !map->max_register_is_set)
return -EINVAL;
- map->cache = kcalloc(regcache_flat_get_index(map, map->max_register)
- + 1, sizeof(unsigned int), GFP_KERNEL);
- if (!map->cache)
+ cache_size = regcache_flat_get_index(map, map->max_register) + 1;
+ cache = kzalloc(struct_size(cache, data, cache_size), map->alloc_flags);
+ if (!cache)
return -ENOMEM;
- cache = map->cache;
+ cache->valid = bitmap_zalloc(cache_size, map->alloc_flags);
+ if (!cache->valid)
+ goto err_free;
+
+ map->cache = cache;
+
+ return 0;
+
+err_free:
+ kfree(cache);
+ return -ENOMEM;
+}
+
+static int regcache_flat_exit(struct regmap *map)
+{
+ struct regcache_flat_data *cache = map->cache;
+
+ if (cache)
+ bitmap_free(cache->valid);
+
+ kfree(cache);
+ map->cache = NULL;
+
+ return 0;
+}
+
+static int regcache_flat_populate(struct regmap *map)
+{
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int i;
for (i = 0; i < map->num_reg_defaults; i++) {
unsigned int reg = map->reg_defaults[i].reg;
unsigned int index = regcache_flat_get_index(map, reg);
- cache[index] = map->reg_defaults[i].def;
+ cache->data[index] = map->reg_defaults[i].def;
+ __set_bit(index, cache->valid);
}
return 0;
}
-static int regcache_flat_exit(struct regmap *map)
+static int regcache_flat_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
{
- kfree(map->cache);
- map->cache = NULL;
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int index = regcache_flat_get_index(map, reg);
+
+ /* legacy behavior: ignore validity, but warn the user */
+ if (unlikely(!test_bit(index, cache->valid)))
+ dev_warn_once(map->dev,
+ "using zero-initialized flat cache, this may cause unexpected behavior");
+
+ *value = cache->data[index];
return 0;
}
-static int regcache_flat_read(struct regmap *map,
- unsigned int reg, unsigned int *value)
+static int regcache_flat_sparse_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
{
- unsigned int *cache = map->cache;
+ struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
- *value = cache[index];
+ if (unlikely(!test_bit(index, cache->valid)))
+ return -ENOENT;
+
+ *value = cache->data[index];
return 0;
}
@@ -65,10 +115,23 @@ static int regcache_flat_read(struct regmap *map,
static int regcache_flat_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
- unsigned int *cache = map->cache;
+ struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
- cache[index] = value;
+ cache->data[index] = value;
+ __set_bit(index, cache->valid);
+
+ return 0;
+}
+
+static int regcache_flat_drop(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int bitmap_min = regcache_flat_get_index(map, min);
+ unsigned int bitmap_max = regcache_flat_get_index(map, max);
+
+ bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min);
return 0;
}
@@ -78,6 +141,18 @@ struct regcache_ops regcache_flat_ops = {
.name = "flat",
.init = regcache_flat_init,
.exit = regcache_flat_exit,
+ .populate = regcache_flat_populate,
.read = regcache_flat_read,
.write = regcache_flat_write,
};
+
+struct regcache_ops regcache_flat_sparse_ops = {
+ .type = REGCACHE_FLAT_S,
+ .name = "flat-sparse",
+ .init = regcache_flat_init,
+ .exit = regcache_flat_exit,
+ .populate = regcache_flat_populate,
+ .read = regcache_flat_sparse_read,
+ .write = regcache_flat_write,
+ .drop = regcache_flat_drop,
+};
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
deleted file mode 100644
index 7886303eb026..000000000000
--- a/drivers/base/regmap/regcache-lzo.c
+++ /dev/null
@@ -1,368 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-//
-// Register cache access API - LZO caching support
-//
-// Copyright 2011 Wolfson Microelectronics plc
-//
-// Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
-
-#include <linux/device.h>
-#include <linux/lzo.h>
-#include <linux/slab.h>
-
-#include "internal.h"
-
-static int regcache_lzo_exit(struct regmap *map);
-
-struct regcache_lzo_ctx {
- void *wmem;
- void *dst;
- const void *src;
- size_t src_len;
- size_t dst_len;
- size_t decompressed_size;
- unsigned long *sync_bmp;
- int sync_bmp_nbits;
-};
-
-#define LZO_BLOCK_NUM 8
-static int regcache_lzo_block_count(struct regmap *map)
-{
- return LZO_BLOCK_NUM;
-}
-
-static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx)
-{
- lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
- if (!lzo_ctx->wmem)
- return -ENOMEM;
- return 0;
-}
-
-static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx)
-{
- size_t compress_size;
- int ret;
-
- ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
- lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
- if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
- return -EINVAL;
- lzo_ctx->dst_len = compress_size;
- return 0;
-}
-
-static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx)
-{
- size_t dst_len;
- int ret;
-
- dst_len = lzo_ctx->dst_len;
- ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
- lzo_ctx->dst, &dst_len);
- if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
- return -EINVAL;
- return 0;
-}
-
-static int regcache_lzo_compress_cache_block(struct regmap *map,
- struct regcache_lzo_ctx *lzo_ctx)
-{
- int ret;
-
- lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
- lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
- if (!lzo_ctx->dst) {
- lzo_ctx->dst_len = 0;
- return -ENOMEM;
- }
-
- ret = regcache_lzo_compress(lzo_ctx);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static int regcache_lzo_decompress_cache_block(struct regmap *map,
- struct regcache_lzo_ctx *lzo_ctx)
-{
- int ret;
-
- lzo_ctx->dst_len = lzo_ctx->decompressed_size;
- lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
- if (!lzo_ctx->dst) {
- lzo_ctx->dst_len = 0;
- return -ENOMEM;
- }
-
- ret = regcache_lzo_decompress(lzo_ctx);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static inline int regcache_lzo_get_blkindex(struct regmap *map,
- unsigned int reg)
-{
- return ((reg / map->reg_stride) * map->cache_word_size) /
- DIV_ROUND_UP(map->cache_size_raw,
- regcache_lzo_block_count(map));
-}
-
-static inline int regcache_lzo_get_blkpos(struct regmap *map,
- unsigned int reg)
-{
- return (reg / map->reg_stride) %
- (DIV_ROUND_UP(map->cache_size_raw,
- regcache_lzo_block_count(map)) /
- map->cache_word_size);
-}
-
-static inline int regcache_lzo_get_blksize(struct regmap *map)
-{
- return DIV_ROUND_UP(map->cache_size_raw,
- regcache_lzo_block_count(map));
-}
-
-static int regcache_lzo_init(struct regmap *map)
-{
- struct regcache_lzo_ctx **lzo_blocks;
- size_t bmp_size;
- int ret, i, blksize, blkcount;
- const char *p, *end;
- unsigned long *sync_bmp;
-
- ret = 0;
-
- blkcount = regcache_lzo_block_count(map);
- map->cache = kcalloc(blkcount, sizeof(*lzo_blocks),
- GFP_KERNEL);
- if (!map->cache)
- return -ENOMEM;
- lzo_blocks = map->cache;
-
- /*
- * allocate a bitmap to be used when syncing the cache with
- * the hardware. Each time a register is modified, the corresponding
- * bit is set in the bitmap, so we know that we have to sync
- * that register.
- */
- bmp_size = map->num_reg_defaults_raw;
- sync_bmp = bitmap_zalloc(bmp_size, GFP_KERNEL);
- if (!sync_bmp) {
- ret = -ENOMEM;
- goto err;
- }
-
- /* allocate the lzo blocks and initialize them */
- for (i = 0; i < blkcount; i++) {
- lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
- GFP_KERNEL);
- if (!lzo_blocks[i]) {
- bitmap_free(sync_bmp);
- ret = -ENOMEM;
- goto err;
- }
- lzo_blocks[i]->sync_bmp = sync_bmp;
- lzo_blocks[i]->sync_bmp_nbits = bmp_size;
- /* alloc the working space for the compressed block */
- ret = regcache_lzo_prepare(lzo_blocks[i]);
- if (ret < 0)
- goto err;
- }
-
- blksize = regcache_lzo_get_blksize(map);
- p = map->reg_defaults_raw;
- end = map->reg_defaults_raw + map->cache_size_raw;
- /* compress the register map and fill the lzo blocks */
- for (i = 0; i < blkcount; i++, p += blksize) {
- lzo_blocks[i]->src = p;
- if (p + blksize > end)
- lzo_blocks[i]->src_len = end - p;
- else
- lzo_blocks[i]->src_len = blksize;
- ret = regcache_lzo_compress_cache_block(map,
- lzo_blocks[i]);
- if (ret < 0)
- goto err;
- lzo_blocks[i]->decompressed_size =
- lzo_blocks[i]->src_len;
- }
-
- return 0;
-err:
- regcache_lzo_exit(map);
- return ret;
-}
-
-static int regcache_lzo_exit(struct regmap *map)
-{
- struct regcache_lzo_ctx **lzo_blocks;
- int i, blkcount;
-
- lzo_blocks = map->cache;
- if (!lzo_blocks)
- return 0;
-
- blkcount = regcache_lzo_block_count(map);
- /*
- * the pointer to the bitmap used for syncing the cache
- * is shared amongst all lzo_blocks. Ensure it is freed
- * only once.
- */
- if (lzo_blocks[0])
- bitmap_free(lzo_blocks[0]->sync_bmp);
- for (i = 0; i < blkcount; i++) {
- if (lzo_blocks[i]) {
- kfree(lzo_blocks[i]->wmem);
- kfree(lzo_blocks[i]->dst);
- }
- /* each lzo_block is a pointer returned by kmalloc or NULL */
- kfree(lzo_blocks[i]);
- }
- kfree(lzo_blocks);
- map->cache = NULL;
- return 0;
-}
-
-static int regcache_lzo_read(struct regmap *map,
- unsigned int reg, unsigned int *value)
-{
- struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
- int ret, blkindex, blkpos;
- size_t tmp_dst_len;
- void *tmp_dst;
-
- /* index of the compressed lzo block */
- blkindex = regcache_lzo_get_blkindex(map, reg);
- /* register index within the decompressed block */
- blkpos = regcache_lzo_get_blkpos(map, reg);
- lzo_blocks = map->cache;
- lzo_block = lzo_blocks[blkindex];
-
- /* save the pointer and length of the compressed block */
- tmp_dst = lzo_block->dst;
- tmp_dst_len = lzo_block->dst_len;
-
- /* prepare the source to be the compressed block */
- lzo_block->src = lzo_block->dst;
- lzo_block->src_len = lzo_block->dst_len;
-
- /* decompress the block */
- ret = regcache_lzo_decompress_cache_block(map, lzo_block);
- if (ret >= 0)
- /* fetch the value from the cache */
- *value = regcache_get_val(map, lzo_block->dst, blkpos);
-
- kfree(lzo_block->dst);
- /* restore the pointer and length of the compressed block */
- lzo_block->dst = tmp_dst;
- lzo_block->dst_len = tmp_dst_len;
-
- return ret;
-}
-
-static int regcache_lzo_write(struct regmap *map,
- unsigned int reg, unsigned int value)
-{
- struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
- int ret, blkindex, blkpos;
- size_t tmp_dst_len;
- void *tmp_dst;
-
- /* index of the compressed lzo block */
- blkindex = regcache_lzo_get_blkindex(map, reg);
- /* register index within the decompressed block */
- blkpos = regcache_lzo_get_blkpos(map, reg);
- lzo_blocks = map->cache;
- lzo_block = lzo_blocks[blkindex];
-
- /* save the pointer and length of the compressed block */
- tmp_dst = lzo_block->dst;
- tmp_dst_len = lzo_block->dst_len;
-
- /* prepare the source to be the compressed block */
- lzo_block->src = lzo_block->dst;
- lzo_block->src_len = lzo_block->dst_len;
-
- /* decompress the block */
- ret = regcache_lzo_decompress_cache_block(map, lzo_block);
- if (ret < 0) {
- kfree(lzo_block->dst);
- goto out;
- }
-
- /* write the new value to the cache */
- if (regcache_set_val(map, lzo_block->dst, blkpos, value)) {
- kfree(lzo_block->dst);
- goto out;
- }
-
- /* prepare the source to be the decompressed block */
- lzo_block->src = lzo_block->dst;
- lzo_block->src_len = lzo_block->dst_len;
-
- /* compress the block */
- ret = regcache_lzo_compress_cache_block(map, lzo_block);
- if (ret < 0) {
- kfree(lzo_block->dst);
- kfree(lzo_block->src);
- goto out;
- }
-
- /* set the bit so we know we have to sync this register */
- set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
- kfree(tmp_dst);
- kfree(lzo_block->src);
- return 0;
-out:
- lzo_block->dst = tmp_dst;
- lzo_block->dst_len = tmp_dst_len;
- return ret;
-}
-
-static int regcache_lzo_sync(struct regmap *map, unsigned int min,
- unsigned int max)
-{
- struct regcache_lzo_ctx **lzo_blocks;
- unsigned int val;
- int i;
- int ret;
-
- lzo_blocks = map->cache;
- i = min;
- for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp,
- lzo_blocks[0]->sync_bmp_nbits) {
- if (i > max)
- continue;
-
- ret = regcache_read(map, i, &val);
- if (ret)
- return ret;
-
- /* Is this the hardware default? If so skip. */
- ret = regcache_lookup_reg(map, i);
- if (ret > 0 && val == map->reg_defaults[ret].def)
- continue;
-
- map->cache_bypass = true;
- ret = _regmap_write(map, i, val);
- map->cache_bypass = false;
- if (ret)
- return ret;
- dev_dbg(map->dev, "Synced register %#x, value %#x\n",
- i, val);
- }
-
- return 0;
-}
-
-struct regcache_ops regcache_lzo_ops = {
- .type = REGCACHE_COMPRESSED,
- .name = "lzo",
- .init = regcache_lzo_init,
- .exit = regcache_lzo_exit,
- .read = regcache_lzo_read,
- .write = regcache_lzo_write,
- .sync = regcache_lzo_sync
-};
diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c
new file mode 100644
index 000000000000..ca1c72b68f31
--- /dev/null
+++ b/drivers/base/regmap/regcache-maple.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register cache access API - maple tree based cache
+//
+// Copyright 2023 Arm, Ltd
+//
+// Author: Mark Brown <broonie@kernel.org>
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/maple_tree.h>
+#include <linux/slab.h>
+
+#include "internal.h"
+
+static int regcache_maple_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
+{
+ struct maple_tree *mt = map->cache;
+ MA_STATE(mas, mt, reg, reg);
+ unsigned long *entry;
+
+ rcu_read_lock();
+
+ entry = mas_walk(&mas);
+ if (!entry) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+
+ *value = entry[reg - mas.index];
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static int regcache_maple_write(struct regmap *map, unsigned int reg,
+ unsigned int val)
+{
+ struct maple_tree *mt = map->cache;
+ MA_STATE(mas, mt, reg, reg);
+ unsigned long *entry, *upper, *lower;
+ unsigned long index, last;
+ size_t lower_sz, upper_sz;
+ int ret;
+
+ rcu_read_lock();
+
+ entry = mas_walk(&mas);
+ if (entry) {
+ entry[reg - mas.index] = val;
+ rcu_read_unlock();
+ return 0;
+ }
+
+ /* Any adjacent entries to extend/merge? */
+ mas_set_range(&mas, reg - 1, reg + 1);
+ index = reg;
+ last = reg;
+
+ lower = mas_find(&mas, reg - 1);
+ if (lower) {
+ index = mas.index;
+ lower_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
+ }
+
+ upper = mas_find(&mas, reg + 1);
+ if (upper) {
+ last = mas.last;
+ upper_sz = (mas.last - mas.index + 1) * sizeof(unsigned long);
+ }
+
+ rcu_read_unlock();
+
+ entry = kmalloc_array(last - index + 1, sizeof(*entry), map->alloc_flags);
+ if (!entry)
+ return -ENOMEM;
+
+ if (lower)
+ memcpy(entry, lower, lower_sz);
+ entry[reg - index] = val;
+ if (upper)
+ memcpy(&entry[reg - index + 1], upper, upper_sz);
+
+ /*
+ * This is safe because the regmap lock means the Maple lock
+ * is redundant, but we need to take it due to lockdep asserts
+ * in the maple tree code.
+ */
+ mas_lock(&mas);
+
+ mas_set_range(&mas, index, last);
+ ret = mas_store_gfp(&mas, entry, map->alloc_flags);
+
+ mas_unlock(&mas);
+
+ if (ret == 0) {
+ kfree(lower);
+ kfree(upper);
+ }
+
+ return ret;
+}
+
+static int regcache_maple_drop(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct maple_tree *mt = map->cache;
+ MA_STATE(mas, mt, min, max);
+ unsigned long *entry, *lower, *upper;
+ /* initialized to work around false-positive -Wuninitialized warning */
+ unsigned long lower_index = 0, lower_last = 0;
+ unsigned long upper_index, upper_last;
+ int ret = 0;
+
+ lower = NULL;
+ upper = NULL;
+
+ mas_lock(&mas);
+
+ mas_for_each(&mas, entry, max) {
+ /*
+ * This is safe because the regmap lock means the
+ * Maple lock is redundant, but we need to take it due
+ * to lockdep asserts in the maple tree code.
+ */
+ mas_unlock(&mas);
+
+ /* Do we need to save any of this entry? */
+ if (mas.index < min) {
+ lower_index = mas.index;
+ lower_last = min -1;
+
+ lower = kmemdup_array(entry,
+ min - mas.index, sizeof(*lower),
+ map->alloc_flags);
+ if (!lower) {
+ ret = -ENOMEM;
+ goto out_unlocked;
+ }
+ }
+
+ if (mas.last > max) {
+ upper_index = max + 1;
+ upper_last = mas.last;
+
+ upper = kmemdup_array(&entry[max - mas.index + 1],
+ mas.last - max, sizeof(*upper),
+ map->alloc_flags);
+ if (!upper) {
+ ret = -ENOMEM;
+ goto out_unlocked;
+ }
+ }
+
+ kfree(entry);
+ mas_lock(&mas);
+ mas_erase(&mas);
+
+ /* Insert new nodes with the saved data */
+ if (lower) {
+ mas_set_range(&mas, lower_index, lower_last);
+ ret = mas_store_gfp(&mas, lower, map->alloc_flags);
+ if (ret != 0)
+ goto out;
+ lower = NULL;
+ }
+
+ if (upper) {
+ mas_set_range(&mas, upper_index, upper_last);
+ ret = mas_store_gfp(&mas, upper, map->alloc_flags);
+ if (ret != 0)
+ goto out;
+ upper = NULL;
+ }
+ }
+
+out:
+ mas_unlock(&mas);
+out_unlocked:
+ kfree(lower);
+ kfree(upper);
+
+ return ret;
+}
+
+static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
+ struct ma_state *mas,
+ unsigned int min, unsigned int max)
+{
+ void *buf;
+ unsigned long r;
+ size_t val_bytes = map->format.val_bytes;
+ int ret = 0;
+
+ mas_pause(mas);
+ rcu_read_unlock();
+
+ /*
+ * Use a raw write if writing more than one register to a
+ * device that supports raw writes to reduce transaction
+ * overheads.
+ */
+ if (max - min > 1 && regmap_can_raw_write(map)) {
+ buf = kmalloc_array(max - min, val_bytes, map->alloc_flags);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Render the data for a raw write */
+ for (r = min; r < max; r++) {
+ regcache_set_val(map, buf, r - min,
+ entry[r - mas->index]);
+ }
+
+ ret = _regmap_raw_write(map, min, buf, (max - min) * val_bytes,
+ false);
+
+ kfree(buf);
+ } else {
+ for (r = min; r < max; r++) {
+ ret = _regmap_write(map, r,
+ entry[r - mas->index]);
+ if (ret != 0)
+ goto out;
+ }
+ }
+
+out:
+ rcu_read_lock();
+
+ return ret;
+}
+
+static int regcache_maple_sync(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct maple_tree *mt = map->cache;
+ unsigned long *entry;
+ MA_STATE(mas, mt, min, max);
+ unsigned long lmin = min;
+ unsigned long lmax = max;
+ unsigned int r, v, sync_start;
+ int ret = 0;
+ bool sync_needed = false;
+
+ map->cache_bypass = true;
+
+ rcu_read_lock();
+
+ mas_for_each(&mas, entry, max) {
+ for (r = max(mas.index, lmin); r <= min(mas.last, lmax); r++) {
+ v = entry[r - mas.index];
+
+ if (regcache_reg_needs_sync(map, r, v)) {
+ if (!sync_needed) {
+ sync_start = r;
+ sync_needed = true;
+ }
+ continue;
+ }
+
+ if (!sync_needed)
+ continue;
+
+ ret = regcache_maple_sync_block(map, entry, &mas,
+ sync_start, r);
+ if (ret != 0)
+ goto out;
+ sync_needed = false;
+ }
+
+ if (sync_needed) {
+ ret = regcache_maple_sync_block(map, entry, &mas,
+ sync_start, r);
+ if (ret != 0)
+ goto out;
+ sync_needed = false;
+ }
+ }
+
+out:
+ rcu_read_unlock();
+
+ map->cache_bypass = false;
+
+ return ret;
+}
+
+static int regcache_maple_init(struct regmap *map)
+{
+ struct maple_tree *mt;
+
+ mt = kmalloc(sizeof(*mt), map->alloc_flags);
+ if (!mt)
+ return -ENOMEM;
+ map->cache = mt;
+
+ mt_init(mt);
+
+ if (!mt_external_lock(mt) && map->lock_key)
+ lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1);
+
+ return 0;
+}
+
+static int regcache_maple_exit(struct regmap *map)
+{
+ struct maple_tree *mt = map->cache;
+ MA_STATE(mas, mt, 0, UINT_MAX);
+ unsigned int *entry;
+
+ /* if we've already been called then just return */
+ if (!mt)
+ return 0;
+
+ mas_lock(&mas);
+ mas_for_each(&mas, entry, UINT_MAX)
+ kfree(entry);
+ __mt_destroy(mt);
+ mas_unlock(&mas);
+
+ kfree(mt);
+ map->cache = NULL;
+
+ return 0;
+}
+
+static int regcache_maple_insert_block(struct regmap *map, int first,
+ int last)
+{
+ struct maple_tree *mt = map->cache;
+ MA_STATE(mas, mt, first, last);
+ unsigned long *entry;
+ int i, ret;
+
+ entry = kmalloc_array(last - first + 1, sizeof(*entry), map->alloc_flags);
+ if (!entry)
+ return -ENOMEM;
+
+ for (i = 0; i < last - first + 1; i++)
+ entry[i] = map->reg_defaults[first + i].def;
+
+ mas_lock(&mas);
+
+ mas_set_range(&mas, map->reg_defaults[first].reg,
+ map->reg_defaults[last].reg);
+ ret = mas_store_gfp(&mas, entry, map->alloc_flags);
+
+ mas_unlock(&mas);
+
+ if (ret)
+ kfree(entry);
+
+ return ret;
+}
+
+static int regcache_maple_populate(struct regmap *map)
+{
+ int i;
+ int ret;
+ int range_start;
+
+ range_start = 0;
+
+ /* Scan for ranges of contiguous registers */
+ for (i = 1; i < map->num_reg_defaults; i++) {
+ if (map->reg_defaults[i].reg !=
+ map->reg_defaults[i - 1].reg + 1) {
+ ret = regcache_maple_insert_block(map, range_start,
+ i - 1);
+ if (ret != 0)
+ return ret;
+
+ range_start = i;
+ }
+ }
+
+ /* Add the last block */
+ return regcache_maple_insert_block(map, range_start, map->num_reg_defaults - 1);
+}
+
+struct regcache_ops regcache_maple_ops = {
+ .type = REGCACHE_MAPLE,
+ .name = "maple",
+ .init = regcache_maple_init,
+ .exit = regcache_maple_exit,
+ .populate = regcache_maple_populate,
+ .read = regcache_maple_read,
+ .write = regcache_maple_write,
+ .drop = regcache_maple_drop,
+ .sync = regcache_maple_sync,
+};
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index fabf87058d80..3344b82c3799 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -22,7 +22,7 @@ struct regcache_rbtree_node {
/* block of adjacent registers */
void *block;
/* Which registers are present */
- long *cache_present;
+ unsigned long *cache_present;
/* base register handled by this block */
unsigned int base_reg;
/* number of registers available in the block */
@@ -184,10 +184,8 @@ static void rbtree_debugfs_init(struct regmap *map)
static int regcache_rbtree_init(struct regmap *map)
{
struct regcache_rbtree_ctx *rbtree_ctx;
- int i;
- int ret;
- map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
+ map->cache = kmalloc(sizeof *rbtree_ctx, map->alloc_flags);
if (!map->cache)
return -ENOMEM;
@@ -195,19 +193,7 @@ static int regcache_rbtree_init(struct regmap *map)
rbtree_ctx->root = RB_ROOT;
rbtree_ctx->cached_rbnode = NULL;
- for (i = 0; i < map->num_reg_defaults; i++) {
- ret = regcache_rbtree_write(map,
- map->reg_defaults[i].reg,
- map->reg_defaults[i].def);
- if (ret)
- goto err;
- }
-
return 0;
-
-err:
- regcache_rbtree_exit(map);
- return ret;
}
static int regcache_rbtree_exit(struct regmap *map)
@@ -239,6 +225,22 @@ static int regcache_rbtree_exit(struct regmap *map)
return 0;
}
+static int regcache_rbtree_populate(struct regmap *map)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < map->num_reg_defaults; i++) {
+ ret = regcache_rbtree_write(map,
+ map->reg_defaults[i].reg,
+ map->reg_defaults[i].def);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int regcache_rbtree_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
@@ -275,18 +277,16 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
pos = (reg - base_reg) / map->reg_stride;
offset = (rbnode->base_reg - base_reg) / map->reg_stride;
- blk = krealloc(rbnode->block,
- blklen * map->cache_word_size,
- GFP_KERNEL);
+ blk = krealloc_array(rbnode->block, blklen, map->cache_word_size, map->alloc_flags);
if (!blk)
return -ENOMEM;
rbnode->block = blk;
if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) {
- present = krealloc(rbnode->cache_present,
- BITS_TO_LONGS(blklen) * sizeof(*present),
- GFP_KERNEL);
+ present = krealloc_array(rbnode->cache_present,
+ BITS_TO_LONGS(blklen), sizeof(*present),
+ map->alloc_flags);
if (!present)
return -ENOMEM;
@@ -320,7 +320,7 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
const struct regmap_range *range;
int i;
- rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL);
+ rbnode = kzalloc(sizeof(*rbnode), map->alloc_flags);
if (!rbnode)
return NULL;
@@ -346,13 +346,13 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
}
rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size,
- GFP_KERNEL);
+ map->alloc_flags);
if (!rbnode->block)
goto err_free;
rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen),
sizeof(*rbnode->cache_present),
- GFP_KERNEL);
+ map->alloc_flags);
if (!rbnode->cache_present)
goto err_free_block;
@@ -453,7 +453,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
if (!rbnode)
return -ENOMEM;
regcache_rbtree_set_register(map, rbnode,
- reg - rbnode->base_reg, value);
+ (reg - rbnode->base_reg) / map->reg_stride,
+ value);
regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
rbtree_ctx->cached_rbnode = rbnode;
}
@@ -471,6 +472,8 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
unsigned int start, end;
int ret;
+ map->async = true;
+
rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
@@ -499,6 +502,8 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
return ret;
}
+ map->async = false;
+
return regmap_async_complete(map);
}
@@ -543,6 +548,7 @@ struct regcache_ops regcache_rbtree_ops = {
.name = "rbtree",
.init = regcache_rbtree_init,
.exit = regcache_rbtree_exit,
+ .populate = regcache_rbtree_populate,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = rbtree_debugfs_init,
#endif
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 362e043e26d8..319c342bf5a0 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -16,13 +16,32 @@
#include "internal.h"
static const struct regcache_ops *cache_types[] = {
+ &regcache_flat_sparse_ops,
&regcache_rbtree_ops,
-#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED)
- &regcache_lzo_ops,
-#endif
+ &regcache_maple_ops,
&regcache_flat_ops,
};
+static int regcache_defaults_cmp(const void *a, const void *b)
+{
+ const struct reg_default *x = a;
+ const struct reg_default *y = b;
+
+ if (x->reg > y->reg)
+ return 1;
+ else if (x->reg < y->reg)
+ return -1;
+ else
+ return 0;
+}
+
+void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults)
+{
+ sort(defaults, ndefaults, sizeof(*defaults),
+ regcache_defaults_cmp, NULL);
+}
+EXPORT_SYMBOL_GPL(regcache_sort_defaults);
+
static int regcache_hw_init(struct regmap *map)
{
int i, j;
@@ -148,7 +167,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
break;
if (i == ARRAY_SIZE(cache_types)) {
- dev_err(map->dev, "Could not match compress type: %d\n",
+ dev_err(map->dev, "Could not match cache type: %d\n",
map->cache_type);
return -EINVAL;
}
@@ -156,7 +175,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
map->num_reg_defaults = config->num_reg_defaults;
map->num_reg_defaults_raw = config->num_reg_defaults_raw;
map->reg_defaults_raw = config->reg_defaults_raw;
- map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
+ map->cache_word_size = BITS_TO_BYTES(config->val_bits);
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
map->cache = NULL;
@@ -172,8 +191,8 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* a copy of it.
*/
if (config->reg_defaults) {
- tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
- sizeof(struct reg_default), GFP_KERNEL);
+ tmp_buf = kmemdup_array(config->reg_defaults, map->num_reg_defaults,
+ sizeof(*map->reg_defaults), GFP_KERNEL);
if (!tmp_buf)
return -ENOMEM;
map->reg_defaults = tmp_buf;
@@ -189,18 +208,38 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
return 0;
}
- if (!map->max_register && map->num_reg_defaults_raw)
+ if (!map->max_register_is_set && map->num_reg_defaults_raw) {
map->max_register = (map->num_reg_defaults_raw - 1) * map->reg_stride;
+ map->max_register_is_set = true;
+ }
if (map->cache_ops->init) {
dev_dbg(map->dev, "Initializing %s cache\n",
map->cache_ops->name);
+ map->lock(map->lock_arg);
ret = map->cache_ops->init(map);
+ map->unlock(map->lock_arg);
if (ret)
goto err_free;
}
+
+ if (map->num_reg_defaults && map->cache_ops->populate) {
+ dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
+ map->lock(map->lock_arg);
+ ret = map->cache_ops->populate(map);
+ map->unlock(map->lock_arg);
+ if (ret)
+ goto err_exit;
+ }
return 0;
+err_exit:
+ if (map->cache_ops->exit) {
+ dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
+ map->lock(map->lock_arg);
+ ret = map->cache_ops->exit(map);
+ map->unlock(map->lock_arg);
+ }
err_free:
kfree(map->reg_defaults);
if (map->cache_free)
@@ -223,7 +262,9 @@ void regcache_exit(struct regmap *map)
if (map->cache_ops->exit) {
dev_dbg(map->dev, "Destroying %s cache\n",
map->cache_ops->name);
+ map->lock(map->lock_arg);
map->cache_ops->exit(map);
+ map->unlock(map->lock_arg);
}
}
@@ -242,7 +283,7 @@ int regcache_read(struct regmap *map,
int ret;
if (map->cache_type == REGCACHE_NONE)
- return -ENOSYS;
+ return -EINVAL;
BUG_ON(!map->cache_ops);
@@ -281,11 +322,14 @@ int regcache_write(struct regmap *map,
return 0;
}
-static bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
- unsigned int val)
+bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
+ unsigned int val)
{
int ret;
+ if (!regmap_writeable(map, reg))
+ return false;
+
/* If we don't know the chip just got reset, then sync everything. */
if (!map->no_sync_defaults)
return true;
@@ -311,6 +355,8 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
continue;
ret = regcache_read(map, reg, &val);
+ if (ret == -ENOENT)
+ continue;
if (ret)
return ret;
@@ -331,6 +377,11 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
return 0;
}
+static int rbtree_all(const void *key, const struct rb_node *node)
+{
+ return 0;
+}
+
/**
* regcache_sync - Sync the register cache with the hardware.
*
@@ -348,6 +399,10 @@ int regcache_sync(struct regmap *map)
unsigned int i;
const char *name;
bool bypass;
+ struct rb_node *node;
+
+ if (WARN_ON(map->cache_type == REGCACHE_NONE))
+ return -EINVAL;
BUG_ON(!map->cache_ops);
@@ -362,8 +417,6 @@ int regcache_sync(struct regmap *map)
if (!map->cache_dirty)
goto out;
- map->async = true;
-
/* Apply any patch first */
map->cache_bypass = true;
for (i = 0; i < map->patch_regs; i++) {
@@ -386,9 +439,31 @@ int regcache_sync(struct regmap *map)
out:
/* Restore the bypass state */
- map->async = false;
map->cache_bypass = bypass;
map->no_sync_defaults = false;
+
+ /*
+ * If we did any paging with cache bypassed and a cached
+ * paging register then the register and cache state might
+ * have gone out of sync, force writes of all the paging
+ * registers.
+ */
+ rb_for_each(node, NULL, &map->range_tree, rbtree_all) {
+ struct regmap_range_node *this =
+ rb_entry(node, struct regmap_range_node, node);
+
+ /* If there's nothing in the cache there's nothing to sync */
+ if (regcache_read(map, this->selector_reg, &i) != 0)
+ continue;
+
+ ret = _regmap_write(map, this->selector_reg, i);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to write %x = %x: %d\n",
+ this->selector_reg, i, ret);
+ break;
+ }
+ }
+
map->unlock(map->lock_arg);
regmap_async_complete(map);
@@ -418,6 +493,9 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
const char *name;
bool bypass;
+ if (WARN_ON(map->cache_type == REGCACHE_NONE))
+ return -EINVAL;
+
BUG_ON(!map->cache_ops);
map->lock(map->lock_arg);
@@ -552,17 +630,37 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
-bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
- unsigned int val)
+/**
+ * regcache_reg_cached - Check if a register is cached
+ *
+ * @map: map to check
+ * @reg: register to check
+ *
+ * Reports if a register is cached.
+ */
+bool regcache_reg_cached(struct regmap *map, unsigned int reg)
{
- if (regcache_get_val(map, base, idx) == val)
- return true;
+ unsigned int val;
+ int ret;
+
+ map->lock(map->lock_arg);
+
+ ret = regcache_read(map, reg, &val);
+
+ map->unlock(map->lock_arg);
+
+ return ret == 0;
+}
+EXPORT_SYMBOL_GPL(regcache_reg_cached);
+void regcache_set_val(struct regmap *map, void *base, unsigned int idx,
+ unsigned int val)
+{
/* Use device native format if possible */
if (map->format.format_val) {
map->format.format_val(base + (map->cache_word_size * idx),
val, 0);
- return false;
+ return;
}
switch (map->cache_word_size) {
@@ -584,18 +682,9 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
cache[idx] = val;
break;
}
-#ifdef CONFIG_64BIT
- case 8: {
- u64 *cache = base;
-
- cache[idx] = val;
- break;
- }
-#endif
default:
BUG();
}
- return false;
}
unsigned int regcache_get_val(struct regmap *map, const void *base,
@@ -625,13 +714,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
return cache[idx];
}
-#ifdef CONFIG_64BIT
- case 8: {
- const u64 *cache = base;
-
- return cache[idx];
- }
-#endif
default:
BUG();
}
@@ -672,6 +754,30 @@ static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
return test_bit(idx, cache_present);
}
+int regcache_sync_val(struct regmap *map, unsigned int reg, unsigned int val)
+{
+ int ret;
+
+ if (!regcache_reg_needs_sync(map, reg, val))
+ return 0;
+
+ map->cache_bypass = true;
+
+ ret = _regmap_write(map, reg, val);
+
+ map->cache_bypass = false;
+
+ if (ret != 0) {
+ dev_err(map->dev, "Unable to sync register %#x. %d\n",
+ reg, ret);
+ return ret;
+ }
+ dev_dbg(map->dev, "Synced register %#x, value %#x\n",
+ reg, val);
+
+ return 0;
+}
+
static int regcache_sync_block_single(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base,
@@ -688,21 +794,9 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
continue;
val = regcache_get_val(map, block, i);
- if (!regcache_reg_needs_sync(map, regtmp, val))
- continue;
-
- map->cache_bypass = true;
-
- ret = _regmap_write(map, regtmp, val);
-
- map->cache_bypass = false;
- if (ret != 0) {
- dev_err(map->dev, "Unable to sync register %#x. %d\n",
- regtmp, ret);
+ ret = regcache_sync_val(map, regtmp, val);
+ if (ret != 0)
return ret;
- }
- dev_dbg(map->dev, "Synced register %#x, value %#x\n",
- regtmp, val);
}
return 0;
diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c
index b9f76bdf74a9..a561971c459c 100644
--- a/drivers/base/regmap/regmap-ac97.c
+++ b/drivers/base/regmap/regmap-ac97.c
@@ -86,4 +86,5 @@ struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_ac97);
+MODULE_DESCRIPTION("Register map access API - AC'97 support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 817eda2075aa..c9b4c04b1cf6 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -48,7 +48,7 @@ static ssize_t regmap_name_read_file(struct file *file,
name = map->dev->driver->name;
ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
- if (ret < 0) {
+ if (ret >= PAGE_SIZE) {
kfree(buf);
return ret;
}
@@ -226,8 +226,8 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
if (*ppos < 0 || !count)
return -EINVAL;
- if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
- count = PAGE_SIZE << (MAX_ORDER - 1);
+ if (count > (PAGE_SIZE << MAX_PAGE_ORDER))
+ count = PAGE_SIZE << MAX_PAGE_ORDER;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
@@ -373,8 +373,8 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
if (*ppos < 0 || !count)
return -EINVAL;
- if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
- count = PAGE_SIZE << (MAX_ORDER - 1);
+ if (count > (PAGE_SIZE << MAX_PAGE_ORDER))
+ count = PAGE_SIZE << MAX_PAGE_ORDER;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
@@ -470,10 +470,6 @@ static ssize_t regmap_cache_only_write_file(struct file *file,
if (err)
return count;
- err = debugfs_file_get(file->f_path.dentry);
- if (err)
- return err;
-
map->lock(map->lock_arg);
if (new_val && !map->cache_only) {
@@ -486,7 +482,6 @@ static ssize_t regmap_cache_only_write_file(struct file *file,
map->cache_only = new_val;
map->unlock(map->lock_arg);
- debugfs_file_put(file->f_path.dentry);
if (require_sync) {
err = regcache_sync(map);
@@ -517,10 +512,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file,
if (err)
return count;
- err = debugfs_file_get(file->f_path.dentry);
- if (err)
- return err;
-
map->lock(map->lock_arg);
if (new_val && !map->cache_bypass) {
@@ -532,7 +523,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file,
map->cache_bypass = new_val;
map->unlock(map->lock_arg);
- debugfs_file_put(file->f_path.dentry);
return count;
}
@@ -636,6 +626,17 @@ void regmap_debugfs_init(struct regmap *map)
&regmap_cache_bypass_fops);
}
+ /*
+ * This could interfere with driver operation. Therefore, don't provide
+ * any real compile time configuration option for this feature. One will
+ * have to modify the source code directly in order to use it.
+ */
+#undef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
+#ifdef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
+ debugfs_create_bool("force_write_field", 0600, map->debugfs,
+ &map->force_write_field);
+#endif
+
next = rb_first(&map->range_tree);
while (next) {
range_node = rb_entry(next, struct regmap_range_node, node);
diff --git a/drivers/base/regmap/regmap-fsi.c b/drivers/base/regmap/regmap-fsi.c
new file mode 100644
index 000000000000..3d2f3cb31d5e
--- /dev/null
+++ b/drivers/base/regmap/regmap-fsi.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - FSI support
+//
+// Copyright 2022 IBM Corp
+//
+// Author: Eddie James <eajames@linux.ibm.com>
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "internal.h"
+
+static int regmap_fsi32_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ u32 v;
+ int ret;
+
+ ret = fsi_slave_read(context, reg, &v, sizeof(v));
+ if (ret)
+ return ret;
+
+ *val = v;
+ return 0;
+}
+
+static int regmap_fsi32_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ u32 v = val;
+
+ return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi32 = {
+ .reg_write = regmap_fsi32_reg_write,
+ .reg_read = regmap_fsi32_reg_read,
+};
+
+static int regmap_fsi32le_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ __be32 v;
+ int ret;
+
+ ret = fsi_slave_read(context, reg, &v, sizeof(v));
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(v);
+ return 0;
+}
+
+static int regmap_fsi32le_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ __be32 v = cpu_to_be32(val);
+
+ return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi32le = {
+ .reg_write = regmap_fsi32le_reg_write,
+ .reg_read = regmap_fsi32le_reg_read,
+};
+
+static int regmap_fsi16_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ u16 v;
+ int ret;
+
+ ret = fsi_slave_read(context, reg, &v, sizeof(v));
+ if (ret)
+ return ret;
+
+ *val = v;
+ return 0;
+}
+
+static int regmap_fsi16_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ u16 v;
+
+ if (val > 0xffff)
+ return -EINVAL;
+
+ v = val;
+ return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi16 = {
+ .reg_write = regmap_fsi16_reg_write,
+ .reg_read = regmap_fsi16_reg_read,
+};
+
+static int regmap_fsi16le_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ __be16 v;
+ int ret;
+
+ ret = fsi_slave_read(context, reg, &v, sizeof(v));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(v);
+ return 0;
+}
+
+static int regmap_fsi16le_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ __be16 v;
+
+ if (val > 0xffff)
+ return -EINVAL;
+
+ v = cpu_to_be16(val);
+ return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi16le = {
+ .reg_write = regmap_fsi16le_reg_write,
+ .reg_read = regmap_fsi16le_reg_read,
+};
+
+static int regmap_fsi8_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ u8 v;
+ int ret;
+
+ ret = fsi_slave_read(context, reg, &v, sizeof(v));
+ if (ret)
+ return ret;
+
+ *val = v;
+ return 0;
+}
+
+static int regmap_fsi8_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ u8 v;
+
+ if (val > 0xff)
+ return -EINVAL;
+
+ v = val;
+ return fsi_slave_write(context, reg, &v, sizeof(v));
+}
+
+static const struct regmap_bus regmap_fsi8 = {
+ .reg_write = regmap_fsi8_reg_write,
+ .reg_read = regmap_fsi8_reg_read,
+};
+
+static const struct regmap_bus *regmap_get_fsi_bus(struct fsi_device *fsi_dev,
+ const struct regmap_config *config)
+{
+ const struct regmap_bus *bus = NULL;
+
+ if (config->reg_bits == 8 || config->reg_bits == 16 || config->reg_bits == 32) {
+ switch (config->val_bits) {
+ case 8:
+ bus = &regmap_fsi8;
+ break;
+ case 16:
+ switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
+ case REGMAP_ENDIAN_LITTLE:
+#ifdef __LITTLE_ENDIAN
+ case REGMAP_ENDIAN_NATIVE:
+#endif
+ bus = &regmap_fsi16le;
+ break;
+ case REGMAP_ENDIAN_DEFAULT:
+ case REGMAP_ENDIAN_BIG:
+#ifdef __BIG_ENDIAN
+ case REGMAP_ENDIAN_NATIVE:
+#endif
+ bus = &regmap_fsi16;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 32:
+ switch (regmap_get_val_endian(&fsi_dev->dev, NULL, config)) {
+ case REGMAP_ENDIAN_LITTLE:
+#ifdef __LITTLE_ENDIAN
+ case REGMAP_ENDIAN_NATIVE:
+#endif
+ bus = &regmap_fsi32le;
+ break;
+ case REGMAP_ENDIAN_DEFAULT:
+ case REGMAP_ENDIAN_BIG:
+#ifdef __BIG_ENDIAN
+ case REGMAP_ENDIAN_NATIVE:
+#endif
+ bus = &regmap_fsi32;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return bus ?: ERR_PTR(-EOPNOTSUPP);
+}
+
+struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config,
+ struct lock_class_key *lock_key, const char *lock_name)
+{
+ const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
+
+ if (IS_ERR(bus))
+ return ERR_CAST(bus);
+
+ return __regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_fsi);
+
+struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key, const char *lock_name)
+{
+ const struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);
+
+ if (IS_ERR(bus))
+ return ERR_CAST(bus);
+
+ return __devm_regmap_init(&fsi_dev->dev, bus, fsi_dev->slave, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_fsi);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 980e5ce6a3a3..c9b39a02278e 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -242,8 +242,8 @@ static int regmap_i2c_smbus_i2c_read(void *context, const void *reg,
static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
.write = regmap_i2c_smbus_i2c_write,
.read = regmap_i2c_smbus_i2c_read,
- .max_raw_read = I2C_SMBUS_BLOCK_MAX,
- .max_raw_write = I2C_SMBUS_BLOCK_MAX,
+ .max_raw_read = I2C_SMBUS_BLOCK_MAX - 1,
+ .max_raw_write = I2C_SMBUS_BLOCK_MAX - 1,
};
static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
@@ -299,8 +299,8 @@ static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
.write = regmap_i2c_smbus_i2c_write_reg16,
.read = regmap_i2c_smbus_i2c_read_reg16,
- .max_raw_read = I2C_SMBUS_BLOCK_MAX,
- .max_raw_write = I2C_SMBUS_BLOCK_MAX,
+ .max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
+ .max_raw_write = I2C_SMBUS_BLOCK_MAX - 2,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
@@ -350,7 +350,8 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
if (quirks->max_write_len &&
(bus->max_raw_write == 0 || bus->max_raw_write > quirks->max_write_len))
- max_write = quirks->max_write_len;
+ max_write = quirks->max_write_len -
+ (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
if (max_read || max_write) {
ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL);
@@ -396,4 +397,5 @@ struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c);
+MODULE_DESCRIPTION("Register map access API - I2C support");
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c
index 0328b0b34284..863b348704dc 100644
--- a/drivers/base/regmap/regmap-i3c.c
+++ b/drivers/base/regmap/regmap-i3c.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+#include <linux/array_size.h>
#include <linux/regmap.h>
#include <linux/i3c/device.h>
#include <linux/i3c/master.h>
@@ -10,7 +11,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i3c_device *i3c = dev_to_i3cdev(dev);
- struct i3c_priv_xfer xfers[] = {
+ struct i3c_xfer xfers[] = {
{
.rnw = false,
.len = count,
@@ -18,7 +19,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count)
},
};
- return i3c_device_do_priv_xfers(i3c, xfers, 1);
+ return i3c_device_do_xfers(i3c, xfers, ARRAY_SIZE(xfers), I3C_SDR);
}
static int regmap_i3c_read(void *context,
@@ -27,7 +28,7 @@ static int regmap_i3c_read(void *context,
{
struct device *dev = context;
struct i3c_device *i3c = dev_to_i3cdev(dev);
- struct i3c_priv_xfer xfers[2];
+ struct i3c_xfer xfers[2];
xfers[0].rnw = false;
xfers[0].len = reg_size;
@@ -37,7 +38,7 @@ static int regmap_i3c_read(void *context,
xfers[1].len = val_size;
xfers[1].data.in = val;
- return i3c_device_do_priv_xfers(i3c, xfers, 2);
+ return i3c_device_do_xfers(i3c, xfers, ARRAY_SIZE(xfers), I3C_SDR);
}
static const struct regmap_bus regmap_i3c = {
@@ -56,5 +57,5 @@ struct regmap *__devm_regmap_init_i3c(struct i3c_device *i3c,
EXPORT_SYMBOL_GPL(__devm_regmap_init_i3c);
MODULE_AUTHOR("Vitor Soares <vitor.soares@synopsys.com>");
-MODULE_DESCRIPTION("Regmap I3C Module");
+MODULE_DESCRIPTION("regmap I3C Module");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 4ef9488d05cd..6112d942499b 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -6,11 +6,13 @@
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+#include <linux/array_size.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include <linux/overflow.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -19,6 +21,7 @@
struct regmap_irq_chip_data {
struct mutex lock;
+ struct lock_class_key lock_key;
struct irq_chip irq_chip;
struct regmap *map;
@@ -30,18 +33,15 @@ struct regmap_irq_chip_data {
int irq;
int wake_count;
- unsigned int mask_base;
- unsigned int unmask_base;
-
void *status_reg_buf;
unsigned int *main_status_buf;
unsigned int *status_buf;
+ unsigned int *prev_status_buf;
unsigned int *mask_buf;
unsigned int *mask_buf_def;
unsigned int *wake_buf;
unsigned int *type_buf;
unsigned int *type_buf_def;
- unsigned int **virt_buf;
unsigned int **config_buf;
unsigned int irq_reg_stride;
@@ -114,17 +114,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
- if (d->mask_base) {
- reg = d->get_irq_reg(d, d->mask_base, i);
+ if (d->chip->handle_mask_sync)
+ d->chip->handle_mask_sync(i, d->mask_buf_def[i],
+ d->mask_buf[i],
+ d->chip->irq_drv_data);
+
+ if (d->chip->mask_base && !d->chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, d->chip->mask_base, i);
ret = regmap_update_bits(d->map, reg,
- d->mask_buf_def[i], d->mask_buf[i]);
+ d->mask_buf_def[i],
+ d->mask_buf[i]);
if (ret)
- dev_err(d->map->dev, "Failed to sync masks in %x\n",
- reg);
+ dev_err(d->map->dev, "Failed to sync masks in %x\n", reg);
}
- if (d->unmask_base) {
- reg = d->get_irq_reg(d, d->unmask_base, i);
+ if (d->chip->unmask_base && !d->chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, d->chip->unmask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret)
@@ -175,38 +180,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
- /* Don't update the type bits if we're using mask bits for irq type. */
- if (!d->chip->type_in_mask) {
- for (i = 0; i < d->chip->num_type_reg; i++) {
- if (!d->type_buf_def[i])
- continue;
- reg = d->get_irq_reg(d, d->chip->type_base, i);
- if (d->chip->type_invert)
- ret = regmap_update_bits(d->map, reg,
- d->type_buf_def[i], ~d->type_buf[i]);
- else
- ret = regmap_update_bits(d->map, reg,
- d->type_buf_def[i], d->type_buf[i]);
- if (ret != 0)
- dev_err(d->map->dev, "Failed to sync type in %x\n",
- reg);
- }
- }
-
- if (d->chip->num_virt_regs) {
- for (i = 0; i < d->chip->num_virt_regs; i++) {
- for (j = 0; j < d->chip->num_regs; j++) {
- reg = d->get_irq_reg(d, d->chip->virt_reg_base[i],
- j);
- ret = regmap_write(map, reg, d->virt_buf[i][j]);
- if (ret != 0)
- dev_err(d->map->dev,
- "Failed to write virt 0x%x: %d\n",
- reg, ret);
- }
- }
- }
-
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
reg = d->get_irq_reg(d, d->chip->config_base[i], j);
@@ -224,10 +197,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
/* If we've changed our wakeup count propagate it to the parent */
if (d->wake_count < 0)
for (i = d->wake_count; i < 0; i++)
- irq_set_irq_wake(d->irq, 0);
+ disable_irq_wake(d->irq);
else if (d->wake_count > 0)
for (i = 0; i < d->wake_count; i++)
- irq_set_irq_wake(d->irq, 1);
+ enable_irq_wake(d->irq);
d->wake_count = 0;
@@ -285,48 +258,16 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
reg = t->type_reg_offset / map->reg_stride;
- if (t->type_reg_mask)
- d->type_buf[reg] &= ~t->type_reg_mask;
- else
- d->type_buf[reg] &= ~(t->type_falling_val |
- t->type_rising_val |
- t->type_level_low_val |
- t->type_level_high_val);
- switch (type) {
- case IRQ_TYPE_EDGE_FALLING:
- d->type_buf[reg] |= t->type_falling_val;
- break;
-
- case IRQ_TYPE_EDGE_RISING:
- d->type_buf[reg] |= t->type_rising_val;
- break;
-
- case IRQ_TYPE_EDGE_BOTH:
- d->type_buf[reg] |= (t->type_falling_val |
- t->type_rising_val);
- break;
-
- case IRQ_TYPE_LEVEL_HIGH:
- d->type_buf[reg] |= t->type_level_high_val;
- break;
-
- case IRQ_TYPE_LEVEL_LOW:
- d->type_buf[reg] |= t->type_level_low_val;
- break;
- default:
- return -EINVAL;
- }
-
- if (d->chip->set_type_virt) {
- ret = d->chip->set_type_virt(d->virt_buf, type, data->hwirq,
- reg);
+ if (d->chip->type_in_mask) {
+ ret = regmap_irq_set_type_config_simple(&d->type_buf, type,
+ irq_data, reg, d->chip->irq_drv_data);
if (ret)
return ret;
}
if (d->chip->set_type_config) {
- ret = d->chip->set_type_config(d->config_buf, type,
- irq_data, reg);
+ ret = d->chip->set_type_config(d->config_buf, type, irq_data,
+ reg, d->chip->irq_drv_data);
if (ret)
return ret;
}
@@ -368,8 +309,8 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
unsigned int b)
{
const struct regmap_irq_chip *chip = data->chip;
+ const struct regmap_irq_sub_irq_map *subreg;
struct regmap *map = data->map;
- struct regmap_irq_sub_irq_map *subreg;
unsigned int reg;
int i, ret = 0;
@@ -386,15 +327,8 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
unsigned int offset = subreg->offset[i];
unsigned int index = offset / map->reg_stride;
- if (chip->not_fixed_stride)
- ret = regmap_read(map,
- chip->status_base + offset,
- &data->status_buf[b]);
- else
- ret = regmap_read(map,
- chip->status_base + offset,
- &data->status_buf[index]);
-
+ ret = regmap_read(map, chip->status_base + offset,
+ &data->status_buf[index]);
if (ret)
break;
}
@@ -402,43 +336,29 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
return ret;
}
-static irqreturn_t regmap_irq_thread(int irq, void *d)
+static int read_irq_data(struct regmap_irq_chip_data *data)
{
- struct regmap_irq_chip_data *data = d;
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
int ret, i;
- bool handled = false;
u32 reg;
- if (chip->handle_pre_irq)
- chip->handle_pre_irq(chip->irq_drv_data);
-
- if (chip->runtime_pm) {
- ret = pm_runtime_get_sync(map->dev);
- if (ret < 0) {
- dev_err(map->dev, "IRQ thread failed to resume: %d\n",
- ret);
- goto exit;
- }
- }
-
/*
* Read only registers with active IRQs if the chip has 'main status
* register'. Else read in the statuses, using a single bulk read if
* possible in order to reduce the I/O overheads.
*/
- if (chip->num_main_regs) {
+ if (chip->no_status) {
+ /* no status register so default to all active */
+ memset32(data->status_buf, GENMASK(31, 0), chip->num_regs);
+ } else if (chip->num_main_regs) {
unsigned int max_main_bits;
- unsigned long size;
-
- size = chip->num_regs * sizeof(unsigned int);
max_main_bits = (chip->num_main_status_bits) ?
chip->num_main_status_bits : chip->num_regs;
/* Clear the status buf as we don't read all status regs */
- memset(data->status_buf, 0, size);
+ memset32(data->status_buf, 0, chip->num_regs);
/* We could support bulk read for main status registers
* but I don't expect to see devices with really many main
@@ -446,23 +366,11 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* sake of simplicity. and add bulk reads only if needed
*/
for (i = 0; i < chip->num_main_regs; i++) {
- /*
- * For not_fixed_stride, don't use ->get_irq_reg().
- * It would produce an incorrect result.
- */
- if (data->chip->not_fixed_stride)
- reg = chip->main_status +
- i * map->reg_stride * data->irq_reg_stride;
- else
- reg = data->get_irq_reg(data,
- chip->main_status, i);
-
+ reg = data->get_irq_reg(data, chip->main_status, i);
ret = regmap_read(map, reg, &data->main_status_buf[i]);
if (ret) {
- dev_err(map->dev,
- "Failed to read IRQ status %d\n",
- ret);
- goto exit;
+ dev_err(map->dev, "Failed to read IRQ status %d\n", ret);
+ return ret;
}
}
@@ -478,10 +386,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
ret = read_sub_irq_data(data, b);
if (ret != 0) {
- dev_err(map->dev,
- "Failed to read IRQ status %d\n",
- ret);
- goto exit;
+ dev_err(map->dev, "Failed to read IRQ status %d\n", ret);
+ return ret;
}
}
@@ -498,9 +404,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
data->status_reg_buf,
chip->num_regs);
if (ret != 0) {
- dev_err(map->dev, "Failed to read IRQ status: %d\n",
- ret);
- goto exit;
+ dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
+ return ret;
}
for (i = 0; i < data->chip->num_regs; i++) {
@@ -516,7 +421,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
break;
default:
BUG();
- goto exit;
+ return -EIO;
}
}
@@ -527,10 +432,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
ret = regmap_read(map, reg, &data->status_buf[i]);
if (ret != 0) {
- dev_err(map->dev,
- "Failed to read IRQ status: %d\n",
- ret);
- goto exit;
+ dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
+ return ret;
}
}
}
@@ -539,6 +442,42 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
for (i = 0; i < data->chip->num_regs; i++)
data->status_buf[i] = ~data->status_buf[i];
+ return 0;
+}
+
+static irqreturn_t regmap_irq_thread(int irq, void *d)
+{
+ struct regmap_irq_chip_data *data = d;
+ const struct regmap_irq_chip *chip = data->chip;
+ struct regmap *map = data->map;
+ int ret, i;
+ bool handled = false;
+ u32 reg;
+
+ if (chip->handle_pre_irq)
+ chip->handle_pre_irq(chip->irq_drv_data);
+
+ if (chip->runtime_pm) {
+ ret = pm_runtime_get_sync(map->dev);
+ if (ret < 0) {
+ dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret);
+ goto exit;
+ }
+ }
+
+ ret = read_irq_data(data);
+ if (ret < 0)
+ goto exit;
+
+ if (chip->status_is_level) {
+ for (i = 0; i < data->chip->num_regs; i++) {
+ unsigned int val = data->status_buf[i];
+
+ data->status_buf[i] ^= data->prev_status_buf[i];
+ data->prev_status_buf[i] = val;
+ }
+ }
+
/*
* Ignore masked IRQs and ack if we need to; we ack early so
* there is no race between handling and acknowledging the
@@ -579,24 +518,28 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
exit:
- if (chip->runtime_pm)
- pm_runtime_put(map->dev);
-
if (chip->handle_post_irq)
chip->handle_post_irq(chip->irq_drv_data);
+ if (chip->runtime_pm)
+ pm_runtime_put(map->dev);
+
if (handled)
return IRQ_HANDLED;
else
return IRQ_NONE;
}
+static struct lock_class_key regmap_irq_lock_class;
+static struct lock_class_key regmap_irq_request_class;
+
static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct regmap_irq_chip_data *data = h->host_data;
irq_set_chip_data(virq, data);
+ irq_set_lockdep_class(virq, &regmap_irq_lock_class, &regmap_irq_request_class);
irq_set_chip(virq, &data->irq_chip);
irq_set_nested_thread(virq, 1);
irq_set_parent(virq, data->irq);
@@ -622,20 +565,8 @@ static const struct irq_domain_ops regmap_domain_ops = {
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index)
{
- const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
- /*
- * FIXME: This is for backward compatibility and should be removed
- * when not_fixed_stride is dropped (it's only used by qcom-pm8008).
- */
- if (chip->not_fixed_stride && chip->sub_reg_offsets) {
- struct regmap_irq_sub_irq_map *subreg;
-
- subreg = &chip->sub_reg_offsets[0];
- return base + subreg->offset[0];
- }
-
return base + index * map->reg_stride * data->irq_reg_stride;
}
EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
@@ -647,13 +578,15 @@ EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
* @type: The requested IRQ type.
* @irq_data: The IRQ being configured.
* @idx: Index of the irq's config registers within each array `buf[i]`
+ * @irq_drv_data: Driver specific IRQ data
*
* This is a &struct regmap_irq_chip->set_type_config callback suitable for
* chips with one config register. Register values are updated according to
* the &struct regmap_irq_type data associated with an IRQ.
*/
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
- const struct regmap_irq *irq_data, int idx)
+ const struct regmap_irq *irq_data,
+ int idx, void *irq_drv_data)
{
const struct regmap_irq_type *t = &irq_data->type;
@@ -695,6 +628,30 @@ int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
}
EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple);
+static int regmap_irq_create_domain(struct fwnode_handle *fwnode, int irq_base,
+ const struct regmap_irq_chip *chip,
+ struct regmap_irq_chip_data *d)
+{
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = chip->num_irqs,
+ .hwirq_max = chip->num_irqs,
+ .virq_base = irq_base,
+ .ops = &regmap_domain_ops,
+ .host_data = d,
+ .name_suffix = chip->domain_suffix,
+ };
+
+ d->domain = irq_domain_instantiate(&info);
+ if (IS_ERR(d->domain)) {
+ dev_err(d->map->dev, "Failed to create IRQ domain\n");
+ return PTR_ERR(d->domain);
+ }
+
+ return 0;
+}
+
+
/**
* regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling
*
@@ -721,7 +678,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
struct regmap_irq_chip_data *d;
int i;
int ret = -ENOMEM;
- int num_type_reg;
u32 reg;
if (chip->num_regs <= 0)
@@ -730,6 +686,9 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack))
return -EINVAL;
+ if (chip->mask_base && chip->unmask_base && !chip->mask_unmask_non_inverted)
+ return -EINVAL;
+
for (i = 0; i < chip->num_irqs; i++) {
if (chip->irqs[i].reg_offset % map->reg_stride)
return -EINVAL;
@@ -738,20 +697,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
return -EINVAL;
}
- if (chip->not_fixed_stride) {
- dev_warn(map->dev, "not_fixed_stride is deprecated; use ->get_irq_reg() instead");
-
- for (i = 0; i < chip->num_regs; i++)
- if (chip->sub_reg_offsets[i].num_regs != 1)
- return -EINVAL;
- }
-
- if (chip->num_type_reg)
- dev_warn(map->dev, "type registers are deprecated; use config registers instead");
-
- if (chip->num_virt_regs || chip->virt_reg_base || chip->set_type_virt)
- dev_warn(map->dev, "virtual registers are deprecated; use config registers instead");
-
if (irq_base) {
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
@@ -779,6 +724,13 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (!d->status_buf)
goto err_alloc;
+ if (chip->status_is_level) {
+ d->prev_status_buf = kcalloc(chip->num_regs, sizeof(*d->prev_status_buf),
+ GFP_KERNEL);
+ if (!d->prev_status_buf)
+ goto err_alloc;
+ }
+
d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf),
GFP_KERNEL);
if (!d->mask_buf)
@@ -796,37 +748,17 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
goto err_alloc;
}
- num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg;
- if (num_type_reg) {
- d->type_buf_def = kcalloc(num_type_reg,
+ if (chip->type_in_mask) {
+ d->type_buf_def = kcalloc(chip->num_regs,
sizeof(*d->type_buf_def), GFP_KERNEL);
if (!d->type_buf_def)
goto err_alloc;
- d->type_buf = kcalloc(num_type_reg, sizeof(*d->type_buf),
- GFP_KERNEL);
+ d->type_buf = kcalloc(chip->num_regs, sizeof(*d->type_buf), GFP_KERNEL);
if (!d->type_buf)
goto err_alloc;
}
- if (chip->num_virt_regs) {
- /*
- * Create virt_buf[chip->num_extra_config_regs][chip->num_regs]
- */
- d->virt_buf = kcalloc(chip->num_virt_regs, sizeof(*d->virt_buf),
- GFP_KERNEL);
- if (!d->virt_buf)
- goto err_alloc;
-
- for (i = 0; i < chip->num_virt_regs; i++) {
- d->virt_buf[i] = kcalloc(chip->num_regs,
- sizeof(**d->virt_buf),
- GFP_KERNEL);
- if (!d->virt_buf[i])
- goto err_alloc;
- }
- }
-
if (chip->num_config_bases && chip->num_config_regs) {
/*
* Create config_buf[num_config_bases][num_config_regs]
@@ -836,7 +768,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (!d->config_buf)
goto err_alloc;
- for (i = 0; i < chip->num_config_regs; i++) {
+ for (i = 0; i < chip->num_config_bases; i++) {
d->config_buf[i] = kcalloc(chip->num_config_regs,
sizeof(**d->config_buf),
GFP_KERNEL);
@@ -852,42 +784,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->chip = chip;
d->irq_base = irq_base;
- if (chip->mask_base && chip->unmask_base &&
- !chip->mask_unmask_non_inverted) {
- /*
- * Chips that specify both mask_base and unmask_base used to
- * get inverted mask behavior by default, with no way to ask
- * for the normal, non-inverted behavior. This "inverted by
- * default" behavior is deprecated, but we have to support it
- * until existing drivers have been fixed.
- *
- * Existing drivers should be updated by swapping mask_base
- * and unmask_base and setting mask_unmask_non_inverted=true.
- * New drivers should always set the flag.
- */
- dev_warn(map->dev, "mask_base and unmask_base are inverted, please fix it");
-
- /* Might as well warn about mask_invert while we're at it... */
- if (chip->mask_invert)
- dev_warn(map->dev, "mask_invert=true ignored");
-
- d->mask_base = chip->unmask_base;
- d->unmask_base = chip->mask_base;
- } else if (chip->mask_invert) {
- /*
- * Swap the roles of mask_base and unmask_base if the bits are
- * inverted. This is deprecated, drivers should use unmask_base
- * directly.
- */
- dev_warn(map->dev, "mask_invert=true is deprecated; please switch to unmask_base");
-
- d->mask_base = chip->unmask_base;
- d->unmask_base = chip->mask_base;
- } else {
- d->mask_base = chip->mask_base;
- d->unmask_base = chip->unmask_base;
- }
-
if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride;
else
@@ -906,7 +802,13 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
goto err_alloc;
}
- mutex_init(&d->lock);
+ /*
+ * If one regmap-irq is the parent of another then we'll try
+ * to lock the child with the parent locked, use an explicit
+ * lock_key so lockdep can figure out what's going on.
+ */
+ lockdep_register_key(&d->lock_key);
+ mutex_init_with_key(&d->lock, &d->lock_key);
for (i = 0; i < chip->num_irqs; i++)
d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride]
@@ -916,25 +818,34 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
- if (d->mask_base) {
- reg = d->get_irq_reg(d, d->mask_base, i);
+ if (chip->handle_mask_sync) {
+ ret = chip->handle_mask_sync(i, d->mask_buf_def[i],
+ d->mask_buf[i],
+ chip->irq_drv_data);
+ if (ret)
+ goto err_mutex;
+ }
+
+ if (chip->mask_base && !chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, chip->mask_base, i);
ret = regmap_update_bits(d->map, reg,
- d->mask_buf_def[i], d->mask_buf[i]);
+ d->mask_buf_def[i],
+ d->mask_buf[i]);
if (ret) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
- goto err_alloc;
+ goto err_mutex;
}
}
- if (d->unmask_base) {
- reg = d->get_irq_reg(d, d->unmask_base, i);
+ if (chip->unmask_base && !chip->handle_mask_sync) {
+ reg = d->get_irq_reg(d, chip->unmask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
- goto err_alloc;
+ goto err_mutex;
}
}
@@ -942,12 +853,17 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
continue;
/* Ack masked but set interrupts */
- reg = d->get_irq_reg(d, d->chip->status_base, i);
- ret = regmap_read(map, reg, &d->status_buf[i]);
- if (ret != 0) {
- dev_err(map->dev, "Failed to read IRQ status: %d\n",
- ret);
- goto err_alloc;
+ if (d->chip->no_status) {
+ /* no status register so default to all active */
+ d->status_buf[i] = UINT_MAX;
+ } else {
+ reg = d->get_irq_reg(d, d->chip->status_base, i);
+ ret = regmap_read(map, reg, &d->status_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to read IRQ status: %d\n",
+ ret);
+ goto err_mutex;
+ }
}
if (chip->status_invert)
@@ -970,7 +886,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (ret != 0) {
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
- goto err_alloc;
+ goto err_mutex;
}
}
}
@@ -992,40 +908,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
- goto err_alloc;
+ goto err_mutex;
}
}
}
- if (chip->num_type_reg && !chip->type_in_mask) {
- for (i = 0; i < chip->num_type_reg; ++i) {
- reg = d->get_irq_reg(d, d->chip->type_base, i);
-
- ret = regmap_read(map, reg, &d->type_buf_def[i]);
-
- if (d->chip->type_invert)
- d->type_buf_def[i] = ~d->type_buf_def[i];
+ /* Store current levels */
+ if (chip->status_is_level) {
+ ret = read_irq_data(d);
+ if (ret < 0)
+ goto err_mutex;
- if (ret) {
- dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n",
- reg, ret);
- goto err_alloc;
- }
- }
+ memcpy(d->prev_status_buf, d->status_buf,
+ array_size(d->chip->num_regs, sizeof(d->prev_status_buf[0])));
}
- if (irq_base)
- d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs,
- irq_base, 0,
- &regmap_domain_ops, d);
- else
- d->domain = irq_domain_create_linear(fwnode, chip->num_irqs,
- &regmap_domain_ops, d);
- if (!d->domain) {
- dev_err(map->dev, "Failed to create IRQ domain\n");
- ret = -ENOMEM;
- goto err_alloc;
- }
+ ret = regmap_irq_create_domain(fwnode, irq_base, chip, d);
+ if (ret)
+ goto err_mutex;
ret = request_threaded_irq(irq, NULL, regmap_irq_thread,
irq_flags | IRQF_ONESHOT,
@@ -1042,19 +942,19 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
err_domain:
/* Should really dispose of the domain but... */
+err_mutex:
+ mutex_destroy(&d->lock);
+ lockdep_unregister_key(&d->lock_key);
err_alloc:
kfree(d->type_buf);
kfree(d->type_buf_def);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
+ kfree(d->main_status_buf);
kfree(d->status_buf);
+ kfree(d->prev_status_buf);
kfree(d->status_reg_buf);
- if (d->virt_buf) {
- for (i = 0; i < chip->num_virt_regs; i++)
- kfree(d->virt_buf[i]);
- kfree(d->virt_buf);
- }
if (d->config_buf) {
for (i = 0; i < chip->num_config_bases; i++)
kfree(d->config_buf[i]);
@@ -1128,13 +1028,17 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
+ kfree(d->main_status_buf);
kfree(d->status_reg_buf);
kfree(d->status_buf);
+ kfree(d->prev_status_buf);
if (d->config_buf) {
for (i = 0; i < d->chip->num_config_bases; i++)
kfree(d->config_buf[i]);
kfree(d->config_buf);
}
+ mutex_destroy(&d->lock);
+ lockdep_unregister_key(&d->lock_key);
kfree(d);
}
EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
new file mode 100644
index 000000000000..f6fc5ed016da
--- /dev/null
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -0,0 +1,2131 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// regmap KUnit tests
+//
+// Copyright 2023 Arm Ltd
+
+#include <kunit/device.h>
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include "internal.h"
+
+#define BLOCK_TEST_SIZE 12
+
+KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_action, regmap_exit, struct regmap *);
+
+struct regmap_test_priv {
+ struct device *dev;
+};
+
+struct regmap_test_param {
+ enum regcache_type cache;
+ enum regmap_endian val_endian;
+
+ unsigned int from_reg;
+ bool fast_io;
+};
+
+static void get_changed_bytes(void *orig, void *new, size_t size)
+{
+ char *o = orig;
+ char *n = new;
+ int i;
+
+ get_random_bytes(new, size);
+
+ /*
+ * This could be nicer and more efficient but we shouldn't
+ * super care.
+ */
+ for (i = 0; i < size; i++)
+ while (n[i] == o[i])
+ get_random_bytes(&n[i], 1);
+}
+
+static const struct regmap_config test_regmap_config = {
+ .reg_stride = 1,
+ .val_bits = sizeof(unsigned int) * 8,
+};
+
+static const char *regcache_type_name(enum regcache_type type)
+{
+ switch (type) {
+ case REGCACHE_NONE:
+ return "none";
+ case REGCACHE_FLAT:
+ return "flat";
+ case REGCACHE_FLAT_S:
+ return "flat-sparse";
+ case REGCACHE_RBTREE:
+ return "rbtree";
+ case REGCACHE_MAPLE:
+ return "maple";
+ default:
+ return NULL;
+ }
+}
+
+static const char *regmap_endian_name(enum regmap_endian endian)
+{
+ switch (endian) {
+ case REGMAP_ENDIAN_BIG:
+ return "big";
+ case REGMAP_ENDIAN_LITTLE:
+ return "little";
+ case REGMAP_ENDIAN_DEFAULT:
+ return "default";
+ case REGMAP_ENDIAN_NATIVE:
+ return "native";
+ default:
+ return NULL;
+ }
+}
+
+static void param_to_desc(const struct regmap_test_param *param, char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s-%s%s @%#x",
+ regcache_type_name(param->cache),
+ regmap_endian_name(param->val_endian),
+ param->fast_io ? " fast I/O" : "",
+ param->from_reg);
+}
+
+static const struct regmap_test_param regcache_types_list[] = {
+ { .cache = REGCACHE_NONE },
+ { .cache = REGCACHE_NONE, .fast_io = true },
+ { .cache = REGCACHE_FLAT },
+ { .cache = REGCACHE_FLAT, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S },
+ { .cache = REGCACHE_FLAT_S, .fast_io = true },
+ { .cache = REGCACHE_RBTREE },
+ { .cache = REGCACHE_RBTREE, .fast_io = true },
+ { .cache = REGCACHE_MAPLE },
+ { .cache = REGCACHE_MAPLE, .fast_io = true },
+};
+
+KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc);
+
+static const struct regmap_test_param real_cache_types_only_list[] = {
+ { .cache = REGCACHE_FLAT },
+ { .cache = REGCACHE_FLAT, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S },
+ { .cache = REGCACHE_FLAT_S, .fast_io = true },
+ { .cache = REGCACHE_RBTREE },
+ { .cache = REGCACHE_RBTREE, .fast_io = true },
+ { .cache = REGCACHE_MAPLE },
+ { .cache = REGCACHE_MAPLE, .fast_io = true },
+};
+
+KUNIT_ARRAY_PARAM(real_cache_types_only, real_cache_types_only_list, param_to_desc);
+
+static const struct regmap_test_param real_cache_types_list[] = {
+ { .cache = REGCACHE_FLAT, .from_reg = 0 },
+ { .cache = REGCACHE_FLAT, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_FLAT, .from_reg = 0x2001 },
+ { .cache = REGCACHE_FLAT, .from_reg = 0x2002 },
+ { .cache = REGCACHE_FLAT, .from_reg = 0x2003 },
+ { .cache = REGCACHE_FLAT, .from_reg = 0x2004 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 },
+};
+
+KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc);
+
+static const struct regmap_test_param sparse_cache_types_list[] = {
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2002 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2003 },
+ { .cache = REGCACHE_RBTREE, .from_reg = 0x2004 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2001 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2002 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2003 },
+ { .cache = REGCACHE_MAPLE, .from_reg = 0x2004 },
+};
+
+KUNIT_ARRAY_PARAM(sparse_cache_types, sparse_cache_types_list, param_to_desc);
+
+static struct regmap *gen_regmap(struct kunit *test,
+ struct regmap_config *config,
+ struct regmap_ram_data **data)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap_test_priv *priv = test->priv;
+ unsigned int *buf;
+ struct regmap *ret = ERR_PTR(-ENOMEM);
+ size_t size;
+ int i, error;
+ struct reg_default *defaults;
+
+ config->cache_type = param->cache;
+ config->fast_io = param->fast_io;
+
+ if (config->max_register == 0) {
+ config->max_register = param->from_reg;
+ if (config->num_reg_defaults)
+ config->max_register += (config->num_reg_defaults - 1) *
+ config->reg_stride;
+ else
+ config->max_register += (BLOCK_TEST_SIZE * config->reg_stride);
+ }
+
+ size = array_size(config->max_register + 1, sizeof(*buf));
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ get_random_bytes(buf, size);
+
+ *data = kzalloc(sizeof(**data), GFP_KERNEL);
+ if (!(*data))
+ goto out_free;
+ (*data)->vals = buf;
+
+ if (config->num_reg_defaults) {
+ defaults = kunit_kcalloc(test,
+ config->num_reg_defaults,
+ sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!defaults)
+ goto out_free;
+
+ config->reg_defaults = defaults;
+
+ for (i = 0; i < config->num_reg_defaults; i++) {
+ defaults[i].reg = param->from_reg + (i * config->reg_stride);
+ defaults[i].def = buf[param->from_reg + (i * config->reg_stride)];
+ }
+ }
+
+ ret = regmap_init_ram(priv->dev, config, *data);
+ if (IS_ERR(ret))
+ goto out_free;
+
+ /* This calls regmap_exit() on failure, which frees buf and *data */
+ error = kunit_add_action_or_reset(test, regmap_exit_action, ret);
+ if (error)
+ ret = ERR_PTR(error);
+
+ return ret;
+
+out_free:
+ kfree(buf);
+ kfree(*data);
+
+ return ret;
+}
+
+static bool reg_5_false(struct device *dev, unsigned int reg)
+{
+ struct kunit *test = dev_get_drvdata(dev);
+ const struct regmap_test_param *param = test->param_value;
+
+ return reg != (param->from_reg + 5);
+}
+
+static void basic_read_write(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val, rval;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* If we write a value to a register we can read it back */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val));
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval));
+ KUNIT_EXPECT_EQ(test, val, rval);
+
+ /* If using a cache the cache satisfied the read */
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[0]);
+}
+
+static void bulk_write(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /*
+ * Data written via the bulk API can be read back with single
+ * reads.
+ */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, 0, val,
+ BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval[i]));
+
+ KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
+
+ /* If using a cache the cache satisfied the read */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+}
+
+static void bulk_read(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Data written as single writes can be read via the bulk API */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i]));
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
+
+ /* If using a cache the cache satisfied the read */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+}
+
+static void multi_write(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ struct reg_sequence sequence[BLOCK_TEST_SIZE];
+ unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /*
+ * Data written via the multi API can be read back with single
+ * reads.
+ */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ sequence[i].reg = i;
+ sequence[i].def = val[i];
+ sequence[i].delay_us = 0;
+ }
+ KUNIT_EXPECT_EQ(test, 0,
+ regmap_multi_reg_write(map, sequence, BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval[i]));
+
+ KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
+
+ /* If using a cache the cache satisfied the read */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+}
+
+static void multi_read(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int regs[BLOCK_TEST_SIZE];
+ unsigned int val[BLOCK_TEST_SIZE], rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Data written as single writes can be read via the multi API */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ regs[i] = i;
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, val[i]));
+ }
+ KUNIT_EXPECT_EQ(test, 0,
+ regmap_multi_reg_read(map, regs, rval, BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, val, rval, sizeof(val));
+
+ /* If using a cache the cache satisfied the read */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+}
+
+static void read_bypassed(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[BLOCK_TEST_SIZE], rval;
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ KUNIT_EXPECT_FALSE(test, map->cache_bypass);
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Write some test values */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, ARRAY_SIZE(val)));
+
+ regcache_cache_only(map, true);
+
+ /*
+ * While in cache-only regmap_read_bypassed() should return the register
+ * value and leave the map in cache-only.
+ */
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ /* Put inverted bits in rval to prove we really read the value */
+ rval = ~val[i];
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+
+ rval = ~val[i];
+ KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+ KUNIT_EXPECT_TRUE(test, map->cache_only);
+ KUNIT_EXPECT_FALSE(test, map->cache_bypass);
+ }
+
+ /*
+ * Change the underlying register values to prove it is returning
+ * real values not cached values.
+ */
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ val[i] = ~val[i];
+ data->vals[param->from_reg + i] = val[i];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ rval = ~val[i];
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &rval));
+ KUNIT_EXPECT_NE(test, val[i], rval);
+
+ rval = ~val[i];
+ KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+ KUNIT_EXPECT_TRUE(test, map->cache_only);
+ KUNIT_EXPECT_FALSE(test, map->cache_bypass);
+ }
+}
+
+static void read_bypassed_volatile(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[BLOCK_TEST_SIZE], rval;
+ int i;
+
+ config = test_regmap_config;
+ /* All registers except #5 volatile */
+ config.volatile_reg = reg_5_false;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ KUNIT_EXPECT_FALSE(test, map->cache_bypass);
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Write some test values */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val, ARRAY_SIZE(val)));
+
+ regcache_cache_only(map, true);
+
+ /*
+ * While in cache-only regmap_read_bypassed() should return the register
+ * value and leave the map in cache-only.
+ */
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ /* Register #5 is non-volatile so should read from cache */
+ KUNIT_EXPECT_EQ(test, (i == 5) ? 0 : -EBUSY,
+ regmap_read(map, param->from_reg + i, &rval));
+
+ /* Put inverted bits in rval to prove we really read the value */
+ rval = ~val[i];
+ KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+ KUNIT_EXPECT_TRUE(test, map->cache_only);
+ KUNIT_EXPECT_FALSE(test, map->cache_bypass);
+ }
+
+ /*
+ * Change the underlying register values to prove it is returning
+ * real values not cached values.
+ */
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ val[i] = ~val[i];
+ data->vals[param->from_reg + i] = val[i];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ if (i == 5)
+ continue;
+
+ rval = ~val[i];
+ KUNIT_EXPECT_EQ(test, 0, regmap_read_bypassed(map, param->from_reg + i, &rval));
+ KUNIT_EXPECT_EQ(test, val[i], rval);
+ KUNIT_EXPECT_TRUE(test, map->cache_only);
+ KUNIT_EXPECT_FALSE(test, map->cache_bypass);
+ }
+}
+
+static void write_readonly(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+ config.writeable_reg = reg_5_false;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[i] = false;
+
+ /* Change the value of all registers, readonly should fail */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, regmap_write(map, i, val) == 0);
+
+ /* Did that match what we see on the device? */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, data->written[i]);
+}
+
+static void read_writeonly(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.readable_reg = reg_5_false;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[i] = false;
+
+ /*
+ * Try to read all the registers, the writeonly one should
+ * fail if we aren't using the flat cache.
+ */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ if (config.cache_type != REGCACHE_FLAT) {
+ KUNIT_EXPECT_EQ(test, i != 5,
+ regmap_read(map, i, &val) == 0);
+ } else {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val));
+ }
+ }
+
+ /* Did we trigger a hardware access? */
+ KUNIT_EXPECT_FALSE(test, data->read[5]);
+}
+
+static void reg_defaults(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Read back the expected default data */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval));
+
+ /* The data should have been read from cache if there was one */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+}
+
+static void reg_defaults_read_dev(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults_raw = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* We should have read the cache defaults back from the map */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_EQ(test, config.cache_type != REGCACHE_NONE, data->read[i]);
+ data->read[i] = false;
+ }
+
+ /* Read back the expected default data */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, data->vals, rval, sizeof(rval));
+
+ /* The data should have been read from cache if there was one */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+}
+
+static void register_patch(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ struct reg_sequence patch[2];
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ /* We need defaults so readback works */
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Stash the original values */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, 0, rval,
+ BLOCK_TEST_SIZE));
+
+ /* Patch a couple of values */
+ patch[0].reg = 2;
+ patch[0].def = rval[2] + 1;
+ patch[0].delay_us = 0;
+ patch[1].reg = 5;
+ patch[1].def = rval[5] + 1;
+ patch[1].delay_us = 0;
+ KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch,
+ ARRAY_SIZE(patch)));
+
+ /* Only the patched registers are written */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ switch (i) {
+ case 2:
+ case 5:
+ KUNIT_EXPECT_TRUE(test, data->written[i]);
+ KUNIT_EXPECT_EQ(test, data->vals[i], rval[i] + 1);
+ break;
+ default:
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+ KUNIT_EXPECT_EQ(test, data->vals[i], rval[i]);
+ break;
+ }
+ }
+}
+
+static void stride(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval;
+ int i;
+
+ config = test_regmap_config;
+ config.reg_stride = 2;
+ config.num_reg_defaults = BLOCK_TEST_SIZE / 2;
+
+ /*
+ * Allow one extra register so that the read/written arrays
+ * are sized big enough to include an entry for the odd
+ * address past the final reg_default register.
+ */
+ config.max_register = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Only even addresses can be accessed, try both read and write */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ data->read[i] = false;
+ data->written[i] = false;
+
+ if (i % 2) {
+ KUNIT_EXPECT_NE(test, 0, regmap_read(map, i, &rval));
+ KUNIT_EXPECT_NE(test, 0, regmap_write(map, i, rval));
+ KUNIT_EXPECT_FALSE(test, data->read[i]);
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+ } else {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+ KUNIT_EXPECT_EQ(test, data->vals[i], rval);
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE,
+ data->read[i]);
+
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, rval));
+ KUNIT_EXPECT_TRUE(test, data->written[i]);
+ }
+ }
+}
+
+static const struct regmap_range_cfg test_range = {
+ .selector_reg = 1,
+ .selector_mask = 0xff,
+
+ .window_start = 4,
+ .window_len = 10,
+
+ .range_min = 20,
+ .range_max = 40,
+};
+
+static bool test_range_window_volatile(struct device *dev, unsigned int reg)
+{
+ if (reg >= test_range.window_start &&
+ reg <= test_range.window_start + test_range.window_len)
+ return true;
+
+ return false;
+}
+
+static bool test_range_all_volatile(struct device *dev, unsigned int reg)
+{
+ if (test_range_window_volatile(dev, reg))
+ return true;
+
+ if (reg >= test_range.range_min && reg <= test_range.range_max)
+ return true;
+
+ return false;
+}
+
+static void basic_ranges(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.volatile_reg = test_range_all_volatile;
+ config.ranges = &test_range;
+ config.num_ranges = 1;
+ config.max_register = test_range.range_max;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (i = test_range.range_min; i < test_range.range_max; i++) {
+ data->read[i] = false;
+ data->written[i] = false;
+ }
+
+ /* Reset the page to a non-zero value to trigger a change */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.selector_reg,
+ test_range.range_max));
+
+ /* Check we set the page and use the window for writes */
+ data->written[test_range.selector_reg] = false;
+ data->written[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.range_min, 0));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]);
+
+ data->written[test_range.selector_reg] = false;
+ data->written[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map,
+ test_range.range_min +
+ test_range.window_len,
+ 0));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]);
+
+ /* Same for reads */
+ data->written[test_range.selector_reg] = false;
+ data->read[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, test_range.range_min, &val));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]);
+
+ data->written[test_range.selector_reg] = false;
+ data->read[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map,
+ test_range.range_min +
+ test_range.window_len,
+ &val));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]);
+
+ /* No physical access triggered in the virtual range */
+ for (i = test_range.range_min; i < test_range.range_max; i++) {
+ KUNIT_EXPECT_FALSE(test, data->read[i]);
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+ }
+}
+
+/* Try to stress dynamic creation of cache data structures */
+static void stress_insert(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval, *vals;
+ size_t buf_sz;
+ int i;
+
+ config = test_regmap_config;
+ config.max_register = 300;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ buf_sz = array_size(sizeof(*vals), config.max_register);
+ vals = kunit_kmalloc(test, buf_sz, GFP_KERNEL);
+ KUNIT_ASSERT_FALSE(test, vals == NULL);
+
+ get_random_bytes(vals, buf_sz);
+
+ /* Write data into the map/cache in ever decreasing strides */
+ for (i = 0; i < config.max_register; i += 100)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i += 50)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i += 25)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i += 10)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i += 5)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i += 3)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i += 2)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+ for (i = 0; i < config.max_register; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, i, vals[i]));
+
+ /* Do reads from the cache (if there is one) match? */
+ for (i = 0; i < config.max_register; i ++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+ KUNIT_EXPECT_EQ(test, rval, vals[i]);
+ KUNIT_EXPECT_EQ(test, config.cache_type == REGCACHE_NONE, data->read[i]);
+ }
+}
+
+static void cache_bypass(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val, rval;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Ensure the cache has a value in it */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg, val));
+
+ /* Bypass then write a different value */
+ regcache_cache_bypass(map, true);
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg, val + 1));
+
+ /* Read the bypassed value */
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg, &rval));
+ KUNIT_EXPECT_EQ(test, val + 1, rval);
+ KUNIT_EXPECT_EQ(test, data->vals[param->from_reg], rval);
+
+ /* Disable bypass, the cache should still return the original value */
+ regcache_cache_bypass(map, false);
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg, &rval));
+ KUNIT_EXPECT_EQ(test, val, rval);
+}
+
+static void cache_sync_marked_dirty(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Put some data into the cache */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val,
+ BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+
+ /* Trash the data on the device itself then resync */
+ regcache_mark_dirty(map);
+ memset(data->vals, 0, sizeof(val));
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Did we just write the correct data out? */
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], val, sizeof(val));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, true, data->written[param->from_reg + i]);
+}
+
+static void cache_sync_after_cache_only(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[BLOCK_TEST_SIZE];
+ unsigned int val_mask;
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ val_mask = GENMASK(config.val_bits - 1, 0);
+ get_random_bytes(&val, sizeof(val));
+
+ /* Put some data into the cache */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val,
+ BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+
+ /* Set cache-only and change the values */
+ regcache_cache_only(map, true);
+ for (i = 0; i < ARRAY_SIZE(val); ++i)
+ val[i] = ~val[i] & val_mask;
+
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, param->from_reg, val,
+ BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[param->from_reg + i]);
+
+ KUNIT_EXPECT_MEMNEQ(test, &data->vals[param->from_reg], val, sizeof(val));
+
+ /* Exit cache-only and sync the cache without marking hardware registers dirty */
+ regcache_cache_only(map, false);
+
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Did we just write the correct data out? */
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], val, sizeof(val));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_TRUE(test, data->written[param->from_reg + i]);
+}
+
+static void cache_sync_defaults_marked_dirty(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Change the value of one register */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + 2, val));
+
+ /* Resync */
+ regcache_mark_dirty(map);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Did we just sync the one register we touched? */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i == 2, data->written[param->from_reg + i]);
+
+ /* Rewrite registers back to their defaults */
+ for (i = 0; i < config.num_reg_defaults; ++i)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, config.reg_defaults[i].reg,
+ config.reg_defaults[i].def));
+
+ /*
+ * Resync after regcache_mark_dirty() should not write out registers
+ * that are at default value
+ */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+ regcache_mark_dirty(map);
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[param->from_reg + i]);
+}
+
+static void cache_sync_default_after_cache_only(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int orig_val;
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + 2, &orig_val));
+
+ /* Enter cache-only and change the value of one register */
+ regcache_cache_only(map, true);
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + 2, orig_val + 1));
+
+ /* Exit cache-only and resync, should write out the changed register */
+ regcache_cache_only(map, false);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Was the register written out? */
+ KUNIT_EXPECT_TRUE(test, data->written[param->from_reg + 2]);
+ KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + 2], orig_val + 1);
+
+ /* Enter cache-only and write register back to its default value */
+ regcache_cache_only(map, true);
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + 2, orig_val));
+
+ /* Resync should write out the new value */
+ regcache_cache_only(map, false);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+ KUNIT_EXPECT_TRUE(test, data->written[param->from_reg + 2]);
+ KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + 2], orig_val);
+}
+
+static void cache_sync_readonly(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.writeable_reg = reg_5_false;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Read all registers to fill the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &val));
+
+ /* Change the value of all registers, readonly should fail */
+ get_random_bytes(&val, sizeof(val));
+ regcache_cache_only(map, true);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, regmap_write(map, param->from_reg + i, val) == 0);
+ regcache_cache_only(map, false);
+
+ /* Resync */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Did that match what we see on the device? */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, i != 5, data->written[param->from_reg + i]);
+}
+
+static void cache_sync_patch(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ struct reg_sequence patch[2];
+ unsigned int rval[BLOCK_TEST_SIZE], val;
+ int i;
+
+ /* We need defaults so readback works */
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Stash the original values */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval,
+ BLOCK_TEST_SIZE));
+
+ /* Patch a couple of values */
+ patch[0].reg = param->from_reg + 2;
+ patch[0].def = rval[2] + 1;
+ patch[0].delay_us = 0;
+ patch[1].reg = param->from_reg + 5;
+ patch[1].def = rval[5] + 1;
+ patch[1].delay_us = 0;
+ KUNIT_EXPECT_EQ(test, 0, regmap_register_patch(map, patch,
+ ARRAY_SIZE(patch)));
+
+ /* Sync the cache */
+ regcache_mark_dirty(map);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* The patch should be on the device but not in the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &val));
+ KUNIT_EXPECT_EQ(test, val, rval[i]);
+
+ switch (i) {
+ case 2:
+ case 5:
+ KUNIT_EXPECT_EQ(test, true, data->written[param->from_reg + i]);
+ KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + i], rval[i] + 1);
+ break;
+ default:
+ KUNIT_EXPECT_EQ(test, false, data->written[param->from_reg + i]);
+ KUNIT_EXPECT_EQ(test, data->vals[param->from_reg + i], rval[i]);
+ break;
+ }
+ }
+}
+
+static void cache_drop(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Ensure the data is read from the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval,
+ BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++) {
+ KUNIT_EXPECT_FALSE(test, data->read[param->from_reg + i]);
+ data->read[param->from_reg + i] = false;
+ }
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval));
+
+ /* Drop some registers */
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, param->from_reg + 3,
+ param->from_reg + 5));
+
+ /* Reread and check only the dropped registers hit the device. */
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval,
+ BLOCK_TEST_SIZE));
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, data->read[param->from_reg + i], i >= 3 && i <= 5);
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval));
+}
+
+static void cache_drop_with_non_contiguous_ranges(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val[4][BLOCK_TEST_SIZE];
+ unsigned int reg;
+ const int num_ranges = ARRAY_SIZE(val) * 2;
+ int rangeidx, i;
+
+ static_assert(ARRAY_SIZE(val) == 4);
+
+ config = test_regmap_config;
+ config.max_register = param->from_reg + (num_ranges * BLOCK_TEST_SIZE);
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (i = 0; i < config.max_register + 1; i++)
+ data->written[i] = false;
+
+ /* Create non-contiguous cache blocks by writing every other range */
+ get_random_bytes(&val, sizeof(val));
+ for (rangeidx = 0; rangeidx < num_ranges; rangeidx += 2) {
+ reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE);
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_write(map, reg,
+ &val[rangeidx / 2],
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[reg],
+ &val[rangeidx / 2], sizeof(val[rangeidx / 2]));
+ }
+
+ /* Check that odd ranges weren't written */
+ for (rangeidx = 1; rangeidx < num_ranges; rangeidx += 2) {
+ reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[reg + i]);
+ }
+
+ /* Drop range 2 */
+ reg = param->from_reg + (2 * BLOCK_TEST_SIZE);
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, reg, reg + BLOCK_TEST_SIZE - 1));
+
+ /* Drop part of range 4 */
+ reg = param->from_reg + (4 * BLOCK_TEST_SIZE);
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, reg + 3, reg + 5));
+
+ /* Mark dirty and reset mock registers to 0 */
+ regcache_mark_dirty(map);
+ for (i = 0; i < config.max_register + 1; i++) {
+ data->vals[i] = 0;
+ data->written[i] = false;
+ }
+
+ /* The registers that were dropped from range 4 should now remain at 0 */
+ val[4 / 2][3] = 0;
+ val[4 / 2][4] = 0;
+ val[4 / 2][5] = 0;
+
+ /* Sync and check that the expected register ranges were written */
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* Check that odd ranges weren't written */
+ for (rangeidx = 1; rangeidx < num_ranges; rangeidx += 2) {
+ reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[reg + i]);
+ }
+
+ /* Check that even ranges (except 2 and 4) were written */
+ for (rangeidx = 0; rangeidx < num_ranges; rangeidx += 2) {
+ if ((rangeidx == 2) || (rangeidx == 4))
+ continue;
+
+ reg = param->from_reg + (rangeidx * BLOCK_TEST_SIZE);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_TRUE(test, data->written[reg + i]);
+
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[reg],
+ &val[rangeidx / 2], sizeof(val[rangeidx / 2]));
+ }
+
+ /* Check that range 2 wasn't written */
+ reg = param->from_reg + (2 * BLOCK_TEST_SIZE);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[reg + i]);
+
+ /* Check that range 4 was partially written */
+ reg = param->from_reg + (4 * BLOCK_TEST_SIZE);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, data->written[reg + i], i < 3 || i > 5);
+
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[reg], &val[4 / 2], sizeof(val[4 / 2]));
+
+ /* Nothing before param->from_reg should have been written */
+ for (i = 0; i < param->from_reg; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+}
+
+static void cache_drop_all_and_sync_marked_dirty(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Ensure the data is read from the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval,
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval));
+
+ /* Change all values in cache from defaults */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, rval[i] + 1));
+
+ /* Drop all registers */
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 0, config.max_register));
+
+ /* Mark dirty and cache sync should not write anything. */
+ regcache_mark_dirty(map);
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+ for (i = 0; i <= config.max_register; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+}
+
+static void cache_drop_all_and_sync_no_defaults(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Ensure the data is read from the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval,
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval));
+
+ /* Change all values in cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, rval[i] + 1));
+
+ /* Drop all registers */
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 0, config.max_register));
+
+ /*
+ * Sync cache without marking it dirty. All registers were dropped
+ * so the cache should not have any entries to write out.
+ */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+ for (i = 0; i <= config.max_register; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+}
+
+static void cache_drop_all_and_sync_has_defaults(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval[BLOCK_TEST_SIZE];
+ int i;
+
+ config = test_regmap_config;
+ config.num_reg_defaults = BLOCK_TEST_SIZE;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Ensure the data is read from the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[param->from_reg + i] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_bulk_read(map, param->from_reg, rval,
+ BLOCK_TEST_SIZE));
+ KUNIT_EXPECT_MEMEQ(test, &data->vals[param->from_reg], rval, sizeof(rval));
+
+ /* Change all values in cache from defaults */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, param->from_reg + i, rval[i] + 1));
+
+ /* Drop all registers */
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 0, config.max_register));
+
+ /*
+ * Sync cache without marking it dirty. All registers were dropped
+ * so the cache should not have any entries to write out.
+ */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->written[param->from_reg + i] = false;
+
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+ for (i = 0; i <= config.max_register; i++)
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+}
+
+static void cache_present(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[param->from_reg + i] = false;
+
+ /* No defaults so no registers cached. */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, param->from_reg + i));
+
+ /* We didn't trigger any reads */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_ASSERT_FALSE(test, data->read[param->from_reg + i]);
+
+ /* Fill the cache */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, param->from_reg + i, &val));
+
+ /* Now everything should be cached */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, param->from_reg + i));
+}
+
+static void cache_write_zero(struct kunit *test)
+{
+ const struct regmap_test_param *param = test->param_value;
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ data->read[param->from_reg + i] = false;
+
+ /* No defaults so no registers cached. */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, param->from_reg + i));
+
+ /* We didn't trigger any reads */
+ for (i = 0; i < BLOCK_TEST_SIZE; i++)
+ KUNIT_ASSERT_FALSE(test, data->read[param->from_reg + i]);
+
+ /* Write a zero value */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 1, 0));
+
+ /* Read that zero value back */
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 1, &val));
+ KUNIT_EXPECT_EQ(test, 0, val);
+
+ /* From the cache? */
+ KUNIT_ASSERT_TRUE(test, regcache_reg_cached(map, 1));
+
+ /* Try to throw it away */
+ KUNIT_EXPECT_EQ(test, 0, regcache_drop_region(map, 1, 1));
+ KUNIT_ASSERT_FALSE(test, regcache_reg_cached(map, 1));
+}
+
+/* Check that caching the window register works with sync */
+static void cache_range_window_reg(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = test_regmap_config;
+ config.volatile_reg = test_range_window_volatile;
+ config.ranges = &test_range;
+ config.num_ranges = 1;
+ config.max_register = test_range.range_max;
+
+ map = gen_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Write new values to the entire range */
+ for (i = test_range.range_min; i <= test_range.range_max; i++)
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(map, i, 0));
+
+ val = data->vals[test_range.selector_reg] & test_range.selector_mask;
+ KUNIT_ASSERT_EQ(test, val, 2);
+
+ /* Write to the first register in the range to reset the page */
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(map, test_range.range_min, 0));
+ val = data->vals[test_range.selector_reg] & test_range.selector_mask;
+ KUNIT_ASSERT_EQ(test, val, 0);
+
+ /* Trigger a cache sync */
+ regcache_mark_dirty(map);
+ KUNIT_ASSERT_EQ(test, 0, regcache_sync(map));
+
+ /* Write to the first register again, the page should be reset */
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(map, test_range.range_min, 0));
+ val = data->vals[test_range.selector_reg] & test_range.selector_mask;
+ KUNIT_ASSERT_EQ(test, val, 0);
+
+ /* Trigger another cache sync */
+ regcache_mark_dirty(map);
+ KUNIT_ASSERT_EQ(test, 0, regcache_sync(map));
+
+ /* Write to the last register again, the page should be reset */
+ KUNIT_ASSERT_EQ(test, 0, regmap_write(map, test_range.range_max, 0));
+ val = data->vals[test_range.selector_reg] & test_range.selector_mask;
+ KUNIT_ASSERT_EQ(test, val, 2);
+}
+
+static const struct regmap_test_param raw_types_list[] = {
+ { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_BIG },
+};
+
+KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, param_to_desc);
+
+static const struct regmap_test_param raw_cache_types_list[] = {
+ { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_BIG },
+};
+
+KUNIT_ARRAY_PARAM(raw_test_cache_types, raw_cache_types_list, param_to_desc);
+
+static const struct regmap_config raw_regmap_config = {
+ .max_register = BLOCK_TEST_SIZE,
+
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .reg_bits = 16,
+ .val_bits = 16,
+};
+
+static struct regmap *gen_raw_regmap(struct kunit *test,
+ struct regmap_config *config,
+ struct regmap_ram_data **data)
+{
+ struct regmap_test_priv *priv = test->priv;
+ const struct regmap_test_param *param = test->param_value;
+ u16 *buf;
+ struct regmap *ret = ERR_PTR(-ENOMEM);
+ int i, error;
+ struct reg_default *defaults;
+ size_t size;
+
+ config->cache_type = param->cache;
+ config->val_format_endian = param->val_endian;
+ config->disable_locking = config->cache_type == REGCACHE_RBTREE ||
+ config->cache_type == REGCACHE_MAPLE;
+
+ size = array_size(config->max_register + 1, BITS_TO_BYTES(config->reg_bits));
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ get_random_bytes(buf, size);
+
+ *data = kzalloc(sizeof(**data), GFP_KERNEL);
+ if (!(*data))
+ goto out_free;
+ (*data)->vals = (void *)buf;
+
+ config->num_reg_defaults = config->max_register + 1;
+ defaults = kunit_kcalloc(test,
+ config->num_reg_defaults,
+ sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!defaults)
+ goto out_free;
+ config->reg_defaults = defaults;
+
+ for (i = 0; i < config->num_reg_defaults; i++) {
+ defaults[i].reg = i;
+ switch (param->val_endian) {
+ case REGMAP_ENDIAN_LITTLE:
+ defaults[i].def = le16_to_cpu(buf[i]);
+ break;
+ case REGMAP_ENDIAN_BIG:
+ defaults[i].def = be16_to_cpu(buf[i]);
+ break;
+ default:
+ ret = ERR_PTR(-EINVAL);
+ goto out_free;
+ }
+ }
+
+ /*
+ * We use the defaults in the tests but they don't make sense
+ * to the core if there's no cache.
+ */
+ if (config->cache_type == REGCACHE_NONE)
+ config->num_reg_defaults = 0;
+
+ ret = regmap_init_raw_ram(priv->dev, config, *data);
+ if (IS_ERR(ret))
+ goto out_free;
+
+ /* This calls regmap_exit() on failure, which frees buf and *data */
+ error = kunit_add_action_or_reset(test, regmap_exit_action, ret);
+ if (error)
+ ret = ERR_PTR(error);
+
+ return ret;
+
+out_free:
+ kfree(buf);
+ kfree(*data);
+
+ return ret;
+}
+
+static void raw_read_defaults_single(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int rval;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Check that we can read the defaults via the API */
+ for (i = 0; i < config.max_register + 1; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+ KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval);
+ }
+}
+
+static void raw_read_defaults(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 *rval;
+ u16 def;
+ size_t val_len;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ val_len = array_size(sizeof(*rval), config.max_register + 1);
+ rval = kunit_kmalloc(test, val_len, GFP_KERNEL);
+ KUNIT_ASSERT_TRUE(test, rval != NULL);
+ if (!rval)
+ return;
+
+ /* Check that we can read the defaults via the API */
+ KUNIT_EXPECT_EQ(test, 0, regmap_raw_read(map, 0, rval, val_len));
+ for (i = 0; i < config.max_register + 1; i++) {
+ def = config.reg_defaults[i].def;
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ KUNIT_EXPECT_EQ(test, def, be16_to_cpu((__force __be16)rval[i]));
+ } else {
+ KUNIT_EXPECT_EQ(test, def, le16_to_cpu((__force __le16)rval[i]));
+ }
+ }
+}
+
+static void raw_write_read_single(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 val;
+ unsigned int rval;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* If we write a value to a register we can read it back */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 0, val));
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &rval));
+ KUNIT_EXPECT_EQ(test, val, rval);
+}
+
+static void raw_write(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 *hw_buf;
+ u16 val[2];
+ unsigned int rval;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ hw_buf = (u16 *)data->vals;
+
+ get_random_bytes(&val, sizeof(val));
+
+ /* Do a raw write */
+ KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val, sizeof(val)));
+
+ /* We should read back the new values, and defaults for the rest */
+ for (i = 0; i < config.max_register + 1; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+
+ switch (i) {
+ case 2:
+ case 3:
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ KUNIT_EXPECT_EQ(test, rval,
+ be16_to_cpu((__force __be16)val[i % 2]));
+ } else {
+ KUNIT_EXPECT_EQ(test, rval,
+ le16_to_cpu((__force __le16)val[i % 2]));
+ }
+ break;
+ default:
+ KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval);
+ break;
+ }
+ }
+
+ /* The values should appear in the "hardware" */
+ KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], val, sizeof(val));
+}
+
+static bool reg_zero(struct device *dev, unsigned int reg)
+{
+ return reg == 0;
+}
+
+static bool ram_reg_zero(struct regmap_ram_data *data, unsigned int reg)
+{
+ return reg == 0;
+}
+
+static void raw_noinc_write(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ u16 val_test, val_last;
+ u16 val_array[BLOCK_TEST_SIZE];
+
+ config = raw_regmap_config;
+ config.volatile_reg = reg_zero;
+ config.writeable_noinc_reg = reg_zero;
+ config.readable_noinc_reg = reg_zero;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ data->noinc_reg = ram_reg_zero;
+
+ get_random_bytes(&val_array, sizeof(val_array));
+
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ val_test = be16_to_cpu(val_array[1]) + 100;
+ val_last = be16_to_cpu(val_array[BLOCK_TEST_SIZE - 1]);
+ } else {
+ val_test = le16_to_cpu(val_array[1]) + 100;
+ val_last = le16_to_cpu(val_array[BLOCK_TEST_SIZE - 1]);
+ }
+
+ /* Put some data into the register following the noinc register */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 1, val_test));
+
+ /* Write some data to the noinc register */
+ KUNIT_EXPECT_EQ(test, 0, regmap_noinc_write(map, 0, val_array,
+ sizeof(val_array)));
+
+ /* We should read back the last value written */
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 0, &val));
+ KUNIT_ASSERT_EQ(test, val_last, val);
+
+ /* Make sure we didn't touch the register after the noinc register */
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, 1, &val));
+ KUNIT_ASSERT_EQ(test, val_test, val);
+}
+
+static void raw_sync(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ u16 val[3];
+ u16 *hw_buf;
+ unsigned int rval;
+ int i;
+
+ config = raw_regmap_config;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ hw_buf = (u16 *)data->vals;
+
+ get_changed_bytes(&hw_buf[2], &val[0], sizeof(val));
+
+ /* Do a regular write and a raw write in cache only mode */
+ regcache_cache_only(map, true);
+ KUNIT_EXPECT_EQ(test, 0, regmap_raw_write(map, 2, val,
+ sizeof(u16) * 2));
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, 4, val[2]));
+
+ /* We should read back the new values, and defaults for the rest */
+ for (i = 0; i < config.max_register + 1; i++) {
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &rval));
+
+ switch (i) {
+ case 2:
+ case 3:
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG) {
+ KUNIT_EXPECT_EQ(test, rval,
+ be16_to_cpu((__force __be16)val[i - 2]));
+ } else {
+ KUNIT_EXPECT_EQ(test, rval,
+ le16_to_cpu((__force __le16)val[i - 2]));
+ }
+ break;
+ case 4:
+ KUNIT_EXPECT_EQ(test, rval, val[i - 2]);
+ break;
+ default:
+ KUNIT_EXPECT_EQ(test, config.reg_defaults[i].def, rval);
+ break;
+ }
+ }
+
+ /*
+ * The value written via _write() was translated by the core,
+ * translate the original copy for comparison purposes.
+ */
+ if (config.val_format_endian == REGMAP_ENDIAN_BIG)
+ val[2] = cpu_to_be16(val[2]);
+ else
+ val[2] = cpu_to_le16(val[2]);
+
+ /* The values should not appear in the "hardware" */
+ KUNIT_EXPECT_MEMNEQ(test, &hw_buf[2], &val[0], sizeof(val));
+
+ for (i = 0; i < config.max_register + 1; i++)
+ data->written[i] = false;
+
+ /* Do the sync */
+ regcache_cache_only(map, false);
+ regcache_mark_dirty(map);
+ KUNIT_EXPECT_EQ(test, 0, regcache_sync(map));
+
+ /* The values should now appear in the "hardware" */
+ KUNIT_EXPECT_MEMEQ(test, &hw_buf[2], &val[0], sizeof(val));
+}
+
+static void raw_ranges(struct kunit *test)
+{
+ struct regmap *map;
+ struct regmap_config config;
+ struct regmap_ram_data *data;
+ unsigned int val;
+ int i;
+
+ config = raw_regmap_config;
+ config.volatile_reg = test_range_all_volatile;
+ config.ranges = &test_range;
+ config.num_ranges = 1;
+ config.max_register = test_range.range_max;
+
+ map = gen_raw_regmap(test, &config, &data);
+ KUNIT_ASSERT_FALSE(test, IS_ERR(map));
+ if (IS_ERR(map))
+ return;
+
+ /* Reset the page to a non-zero value to trigger a change */
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.selector_reg,
+ test_range.range_max));
+
+ /* Check we set the page and use the window for writes */
+ data->written[test_range.selector_reg] = false;
+ data->written[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map, test_range.range_min, 0));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]);
+
+ data->written[test_range.selector_reg] = false;
+ data->written[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_write(map,
+ test_range.range_min +
+ test_range.window_len,
+ 0));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.window_start]);
+
+ /* Same for reads */
+ data->written[test_range.selector_reg] = false;
+ data->read[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map, test_range.range_min, &val));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]);
+
+ data->written[test_range.selector_reg] = false;
+ data->read[test_range.window_start] = false;
+ KUNIT_EXPECT_EQ(test, 0, regmap_read(map,
+ test_range.range_min +
+ test_range.window_len,
+ &val));
+ KUNIT_EXPECT_TRUE(test, data->written[test_range.selector_reg]);
+ KUNIT_EXPECT_TRUE(test, data->read[test_range.window_start]);
+
+ /* No physical access triggered in the virtual range */
+ for (i = test_range.range_min; i < test_range.range_max; i++) {
+ KUNIT_EXPECT_FALSE(test, data->read[i]);
+ KUNIT_EXPECT_FALSE(test, data->written[i]);
+ }
+}
+
+static struct kunit_case regmap_test_cases[] = {
+ KUNIT_CASE_PARAM(basic_read_write, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(read_bypassed, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(read_bypassed_volatile, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(bulk_write, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(bulk_read, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(multi_write, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(multi_read, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(write_readonly, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(read_writeonly, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(reg_defaults, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(reg_defaults_read_dev, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(register_patch, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(stride, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(basic_ranges, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(stress_insert, regcache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_bypass, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_marked_dirty, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_after_cache_only, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_defaults_marked_dirty, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_default_after_cache_only, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_readonly, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_sync_patch, real_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_drop, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_drop_with_non_contiguous_ranges, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_drop_all_and_sync_marked_dirty, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_drop_all_and_sync_no_defaults, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_drop_all_and_sync_has_defaults, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_present, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_write_zero, sparse_cache_types_gen_params),
+ KUNIT_CASE_PARAM(cache_range_window_reg, real_cache_types_only_gen_params),
+
+ KUNIT_CASE_PARAM(raw_read_defaults_single, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_read_defaults, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_write_read_single, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_write, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_noinc_write, raw_test_types_gen_params),
+ KUNIT_CASE_PARAM(raw_sync, raw_test_cache_types_gen_params),
+ KUNIT_CASE_PARAM(raw_ranges, raw_test_cache_types_gen_params),
+ {}
+};
+
+static int regmap_test_init(struct kunit *test)
+{
+ struct regmap_test_priv *priv;
+ struct device *dev;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+
+ dev = kunit_device_register(test, "regmap_test");
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ priv->dev = get_device(dev);
+ dev_set_drvdata(dev, test);
+
+ return 0;
+}
+
+static void regmap_test_exit(struct kunit *test)
+{
+ struct regmap_test_priv *priv = test->priv;
+
+ /* Destroy the dummy struct device */
+ if (priv && priv->dev)
+ put_device(priv->dev);
+}
+
+static struct kunit_suite regmap_test_suite = {
+ .name = "regmap",
+ .init = regmap_test_init,
+ .exit = regmap_test_exit,
+ .test_cases = regmap_test_cases,
+};
+kunit_test_suite(regmap_test_suite);
+
+MODULE_DESCRIPTION("Regmap KUnit tests");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-mdio.c b/drivers/base/regmap/regmap-mdio.c
index f7293040a2b1..9573bf3b52f4 100644
--- a/drivers/base/regmap/regmap-mdio.c
+++ b/drivers/base/regmap/regmap-mdio.c
@@ -10,31 +10,21 @@
/* Clause-45 mask includes the device type (5 bit) and actual register number (16 bit) */
#define REGNUM_C45_MASK GENMASK(20, 0)
-static int regmap_mdio_read(struct mdio_device *mdio_dev, u32 reg, unsigned int *val)
+static int regmap_mdio_c22_read(void *context, unsigned int reg, unsigned int *val)
{
+ struct mdio_device *mdio_dev = context;
int ret;
+ if (unlikely(reg & ~REGNUM_C22_MASK))
+ return -ENXIO;
+
ret = mdiodev_read(mdio_dev, reg);
if (ret < 0)
return ret;
*val = ret & REGVAL_MASK;
- return 0;
-}
-
-static int regmap_mdio_write(struct mdio_device *mdio_dev, u32 reg, unsigned int val)
-{
- return mdiodev_write(mdio_dev, reg, val);
-}
-
-static int regmap_mdio_c22_read(void *context, unsigned int reg, unsigned int *val)
-{
- struct mdio_device *mdio_dev = context;
-
- if (unlikely(reg & ~REGNUM_C22_MASK))
- return -ENXIO;
- return regmap_mdio_read(mdio_dev, reg, val);
+ return 0;
}
static int regmap_mdio_c22_write(void *context, unsigned int reg, unsigned int val)
@@ -55,21 +45,36 @@ static const struct regmap_bus regmap_mdio_c22_bus = {
static int regmap_mdio_c45_read(void *context, unsigned int reg, unsigned int *val)
{
struct mdio_device *mdio_dev = context;
+ unsigned int devad;
+ int ret;
if (unlikely(reg & ~REGNUM_C45_MASK))
return -ENXIO;
- return regmap_mdio_read(mdio_dev, MII_ADDR_C45 | reg, val);
+ devad = reg >> REGMAP_MDIO_C45_DEVAD_SHIFT;
+ reg = reg & REGMAP_MDIO_C45_REGNUM_MASK;
+
+ ret = mdiodev_c45_read(mdio_dev, devad, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret & REGVAL_MASK;
+
+ return 0;
}
static int regmap_mdio_c45_write(void *context, unsigned int reg, unsigned int val)
{
struct mdio_device *mdio_dev = context;
+ unsigned int devad;
if (unlikely(reg & ~REGNUM_C45_MASK))
return -ENXIO;
- return regmap_mdio_write(mdio_dev, MII_ADDR_C45 | reg, val);
+ devad = reg >> REGMAP_MDIO_C45_DEVAD_SHIFT;
+ reg = reg & REGMAP_MDIO_C45_REGNUM_MASK;
+
+ return mdiodev_c45_write(mdio_dev, devad, reg, val);
}
static const struct regmap_bus regmap_mdio_c45_bus = {
@@ -112,5 +117,5 @@ struct regmap *__devm_regmap_init_mdio(struct mdio_device *mdio_dev,
EXPORT_SYMBOL_GPL(__devm_regmap_init_mdio);
MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
-MODULE_DESCRIPTION("Regmap MDIO Module");
+MODULE_DESCRIPTION("regmap MDIO Module");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
index 71f16be7e717..29e5f3175301 100644
--- a/drivers/base/regmap/regmap-mmio.c
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -10,13 +10,14 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/swab.h>
#include "internal.h"
struct regmap_mmio_context {
void __iomem *regs;
unsigned int val_bytes;
- bool relaxed_mmio;
+ bool big_endian;
bool attached_clk;
struct clk *clk;
@@ -33,9 +34,6 @@ static int regmap_mmio_regbits_check(size_t reg_bits)
case 8:
case 16:
case 32:
-#ifdef CONFIG_64BIT
- case 64:
-#endif
return 0;
default:
return -EINVAL;
@@ -50,18 +48,13 @@ static int regmap_mmio_get_min_stride(size_t val_bits)
case 8:
/* The core treats 0 as 1 */
min_stride = 0;
- return 0;
+ break;
case 16:
min_stride = 2;
break;
case 32:
min_stride = 4;
break;
-#ifdef CONFIG_64BIT
- case 64:
- min_stride = 8;
- break;
-#endif
default:
return -EINVAL;
}
@@ -83,6 +76,12 @@ static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx,
writeb_relaxed(val, ctx->regs + reg);
}
+static void regmap_mmio_iowrite8(struct regmap_mmio_context *ctx,
+ unsigned int reg, unsigned int val)
+{
+ iowrite8(val, ctx->regs + reg);
+}
+
static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
@@ -97,10 +96,22 @@ static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx,
writew_relaxed(val, ctx->regs + reg);
}
+static void regmap_mmio_iowrite16le(struct regmap_mmio_context *ctx,
+ unsigned int reg, unsigned int val)
+{
+ iowrite16(val, ctx->regs + reg);
+}
+
static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
+ writew(swab16(val), ctx->regs + reg);
+}
+
+static void regmap_mmio_iowrite16be(struct regmap_mmio_context *ctx,
+ unsigned int reg, unsigned int val)
+{
iowrite16be(val, ctx->regs + reg);
}
@@ -118,28 +129,24 @@ static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx,
writel_relaxed(val, ctx->regs + reg);
}
-static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
- unsigned int reg,
- unsigned int val)
+static void regmap_mmio_iowrite32le(struct regmap_mmio_context *ctx,
+ unsigned int reg, unsigned int val)
{
- iowrite32be(val, ctx->regs + reg);
+ iowrite32(val, ctx->regs + reg);
}
-#ifdef CONFIG_64BIT
-static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
+static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
unsigned int reg,
unsigned int val)
{
- writeq(val, ctx->regs + reg);
+ writel(swab32(val), ctx->regs + reg);
}
-static void regmap_mmio_write64le_relaxed(struct regmap_mmio_context *ctx,
- unsigned int reg,
- unsigned int val)
+static void regmap_mmio_iowrite32be(struct regmap_mmio_context *ctx,
+ unsigned int reg, unsigned int val)
{
- writeq_relaxed(val, ctx->regs + reg);
+ iowrite32be(val, ctx->regs + reg);
}
-#endif
static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
{
@@ -160,6 +167,69 @@ static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
return 0;
}
+static int regmap_mmio_noinc_write(void *context, unsigned int reg,
+ const void *val, size_t val_count)
+{
+ struct regmap_mmio_context *ctx = context;
+ int ret = 0;
+ int i;
+
+ if (!IS_ERR(ctx->clk)) {
+ ret = clk_enable(ctx->clk);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * There are no native, assembly-optimized write single register
+ * operations for big endian, so fall back to emulation if this
+ * is needed. (Single bytes are fine, they are not affected by
+ * endianness.)
+ */
+ if (ctx->big_endian && (ctx->val_bytes > 1)) {
+ switch (ctx->val_bytes) {
+ case 2:
+ {
+ const u16 *valp = (const u16 *)val;
+ for (i = 0; i < val_count; i++)
+ writew(swab16(valp[i]), ctx->regs + reg);
+ goto out_clk;
+ }
+ case 4:
+ {
+ const u32 *valp = (const u32 *)val;
+ for (i = 0; i < val_count; i++)
+ writel(swab32(valp[i]), ctx->regs + reg);
+ goto out_clk;
+ }
+ default:
+ ret = -EINVAL;
+ goto out_clk;
+ }
+ }
+
+ switch (ctx->val_bytes) {
+ case 1:
+ writesb(ctx->regs + reg, (const u8 *)val, val_count);
+ break;
+ case 2:
+ writesw(ctx->regs + reg, (const u16 *)val, val_count);
+ break;
+ case 4:
+ writesl(ctx->regs + reg, (const u32 *)val, val_count);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+out_clk:
+ if (!IS_ERR(ctx->clk))
+ clk_disable(ctx->clk);
+
+ return ret;
+}
+
static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
unsigned int reg)
{
@@ -172,6 +242,12 @@ static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
return readb_relaxed(ctx->regs + reg);
}
+static unsigned int regmap_mmio_ioread8(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return ioread8(ctx->regs + reg);
+}
+
static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
unsigned int reg)
{
@@ -184,9 +260,21 @@ static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx
return readw_relaxed(ctx->regs + reg);
}
+static unsigned int regmap_mmio_ioread16le(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return ioread16(ctx->regs + reg);
+}
+
static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
unsigned int reg)
{
+ return swab16(readw(ctx->regs + reg));
+}
+
+static unsigned int regmap_mmio_ioread16be(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
return ioread16be(ctx->regs + reg);
}
@@ -202,25 +290,23 @@ static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx
return readl_relaxed(ctx->regs + reg);
}
-static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
- unsigned int reg)
+static unsigned int regmap_mmio_ioread32le(struct regmap_mmio_context *ctx,
+ unsigned int reg)
{
- return ioread32be(ctx->regs + reg);
+ return ioread32(ctx->regs + reg);
}
-#ifdef CONFIG_64BIT
-static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
+static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
unsigned int reg)
{
- return readq(ctx->regs + reg);
+ return swab32(readl(ctx->regs + reg));
}
-static unsigned int regmap_mmio_read64le_relaxed(struct regmap_mmio_context *ctx,
- unsigned int reg)
+static unsigned int regmap_mmio_ioread32be(struct regmap_mmio_context *ctx,
+ unsigned int reg)
{
- return readq_relaxed(ctx->regs + reg);
+ return ioread32be(ctx->regs + reg);
}
-#endif
static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
{
@@ -241,6 +327,61 @@ static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
return 0;
}
+static int regmap_mmio_noinc_read(void *context, unsigned int reg,
+ void *val, size_t val_count)
+{
+ struct regmap_mmio_context *ctx = context;
+ int ret = 0;
+
+ if (!IS_ERR(ctx->clk)) {
+ ret = clk_enable(ctx->clk);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (ctx->val_bytes) {
+ case 1:
+ readsb(ctx->regs + reg, (u8 *)val, val_count);
+ break;
+ case 2:
+ readsw(ctx->regs + reg, (u16 *)val, val_count);
+ break;
+ case 4:
+ readsl(ctx->regs + reg, (u32 *)val, val_count);
+ break;
+ default:
+ ret = -EINVAL;
+ goto out_clk;
+ }
+
+ /*
+ * There are no native, assembly-optimized write single register
+ * operations for big endian, so fall back to emulation if this
+ * is needed. (Single bytes are fine, they are not affected by
+ * endianness.)
+ */
+ if (ctx->big_endian && (ctx->val_bytes > 1)) {
+ switch (ctx->val_bytes) {
+ case 2:
+ swab16_array(val, val_count);
+ break;
+ case 4:
+ swab32_array(val, val_count);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+out_clk:
+ if (!IS_ERR(ctx->clk))
+ clk_disable(ctx->clk);
+
+ return ret;
+}
+
+
static void regmap_mmio_free_context(void *context)
{
struct regmap_mmio_context *ctx = context;
@@ -257,6 +398,8 @@ static const struct regmap_bus regmap_mmio = {
.fast_io = true,
.reg_write = regmap_mmio_write,
.reg_read = regmap_mmio_read,
+ .reg_noinc_write = regmap_mmio_noinc_write,
+ .reg_noinc_read = regmap_mmio_noinc_read,
.free_context = regmap_mmio_free_context,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
@@ -281,7 +424,10 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
if (min_stride < 0)
return ERR_PTR(min_stride);
- if (config->reg_stride < min_stride)
+ if (config->reg_stride && config->reg_stride < min_stride)
+ return ERR_PTR(-EINVAL);
+
+ if (config->use_relaxed_mmio && config->io_port)
return ERR_PTR(-EINVAL);
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -290,7 +436,6 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
ctx->regs = regs;
ctx->val_bytes = config->val_bits / 8;
- ctx->relaxed_mmio = config->use_relaxed_mmio;
ctx->clk = ERR_PTR(-ENODEV);
switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
@@ -301,7 +446,10 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
#endif
switch (config->val_bits) {
case 8:
- if (ctx->relaxed_mmio) {
+ if (config->io_port) {
+ ctx->reg_read = regmap_mmio_ioread8;
+ ctx->reg_write = regmap_mmio_iowrite8;
+ } else if (config->use_relaxed_mmio) {
ctx->reg_read = regmap_mmio_read8_relaxed;
ctx->reg_write = regmap_mmio_write8_relaxed;
} else {
@@ -310,7 +458,10 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
}
break;
case 16:
- if (ctx->relaxed_mmio) {
+ if (config->io_port) {
+ ctx->reg_read = regmap_mmio_ioread16le;
+ ctx->reg_write = regmap_mmio_iowrite16le;
+ } else if (config->use_relaxed_mmio) {
ctx->reg_read = regmap_mmio_read16le_relaxed;
ctx->reg_write = regmap_mmio_write16le_relaxed;
} else {
@@ -319,7 +470,10 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
}
break;
case 32:
- if (ctx->relaxed_mmio) {
+ if (config->io_port) {
+ ctx->reg_read = regmap_mmio_ioread32le;
+ ctx->reg_write = regmap_mmio_iowrite32le;
+ } else if (config->use_relaxed_mmio) {
ctx->reg_read = regmap_mmio_read32le_relaxed;
ctx->reg_write = regmap_mmio_write32le_relaxed;
} else {
@@ -327,17 +481,6 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
ctx->reg_write = regmap_mmio_write32le;
}
break;
-#ifdef CONFIG_64BIT
- case 64:
- if (ctx->relaxed_mmio) {
- ctx->reg_read = regmap_mmio_read64le_relaxed;
- ctx->reg_write = regmap_mmio_write64le_relaxed;
- } else {
- ctx->reg_read = regmap_mmio_read64le;
- ctx->reg_write = regmap_mmio_write64le;
- }
- break;
-#endif
default:
ret = -EINVAL;
goto err_free;
@@ -347,18 +490,34 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
#ifdef __BIG_ENDIAN
case REGMAP_ENDIAN_NATIVE:
#endif
+ ctx->big_endian = true;
switch (config->val_bits) {
case 8:
- ctx->reg_read = regmap_mmio_read8;
- ctx->reg_write = regmap_mmio_write8;
+ if (config->io_port) {
+ ctx->reg_read = regmap_mmio_ioread8;
+ ctx->reg_write = regmap_mmio_iowrite8;
+ } else {
+ ctx->reg_read = regmap_mmio_read8;
+ ctx->reg_write = regmap_mmio_write8;
+ }
break;
case 16:
- ctx->reg_read = regmap_mmio_read16be;
- ctx->reg_write = regmap_mmio_write16be;
+ if (config->io_port) {
+ ctx->reg_read = regmap_mmio_ioread16be;
+ ctx->reg_write = regmap_mmio_iowrite16be;
+ } else {
+ ctx->reg_read = regmap_mmio_read16be;
+ ctx->reg_write = regmap_mmio_write16be;
+ }
break;
case 32:
- ctx->reg_read = regmap_mmio_read32be;
- ctx->reg_write = regmap_mmio_write32be;
+ if (config->io_port) {
+ ctx->reg_read = regmap_mmio_ioread32be;
+ ctx->reg_write = regmap_mmio_iowrite32be;
+ } else {
+ ctx->reg_read = regmap_mmio_read32be;
+ ctx->reg_write = regmap_mmio_write32be;
+ }
break;
default:
ret = -EINVAL;
@@ -450,4 +609,5 @@ void regmap_mmio_detach_clk(struct regmap *map)
}
EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk);
+MODULE_DESCRIPTION("regmap MMIO Module");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-ram.c
new file mode 100644
index 000000000000..4e5b4518ce4d
--- /dev/null
+++ b/drivers/base/regmap/regmap-ram.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - Memory region
+//
+// This is intended for testing only
+//
+// Copyright (c) 2023, Arm Ltd
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
+
+#include "internal.h"
+
+static int regmap_ram_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct regmap_ram_data *data = context;
+
+ data->vals[reg] = val;
+ data->written[reg] = true;
+
+ return 0;
+}
+
+static int regmap_ram_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct regmap_ram_data *data = context;
+
+ *val = data->vals[reg];
+ data->read[reg] = true;
+
+ return 0;
+}
+
+static void regmap_ram_free_context(void *context)
+{
+ struct regmap_ram_data *data = context;
+
+ kfree(data->vals);
+ kfree(data->read);
+ kfree(data->written);
+ kfree(data);
+}
+
+static const struct regmap_bus regmap_ram = {
+ .fast_io = true,
+ .reg_write = regmap_ram_write,
+ .reg_read = regmap_ram_read,
+ .free_context = regmap_ram_free_context,
+};
+
+struct regmap *__regmap_init_ram(struct device *dev,
+ const struct regmap_config *config,
+ struct regmap_ram_data *data,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ struct regmap *map;
+
+ if (!config->max_register) {
+ pr_crit("No max_register specified for RAM regmap\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ data->read = kcalloc(config->max_register + 1, sizeof(bool),
+ GFP_KERNEL);
+ if (!data->read)
+ return ERR_PTR(-ENOMEM);
+
+ data->written = kcalloc(config->max_register + 1, sizeof(bool),
+ GFP_KERNEL);
+ if (!data->written)
+ return ERR_PTR(-ENOMEM);
+
+ map = __regmap_init(dev, &regmap_ram, data, config,
+ lock_key, lock_name);
+
+ return map;
+}
+EXPORT_SYMBOL_GPL(__regmap_init_ram);
+
+MODULE_DESCRIPTION("Register map access API - Memory region");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-raw-ram.c b/drivers/base/regmap/regmap-raw-ram.c
new file mode 100644
index 000000000000..76c98814fb8a
--- /dev/null
+++ b/drivers/base/regmap/regmap-raw-ram.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - Memory region with raw access
+//
+// This is intended for testing only
+//
+// Copyright (c) 2023, Arm Ltd
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
+
+#include "internal.h"
+
+static unsigned int decode_reg(enum regmap_endian endian, const void *reg)
+{
+ const u16 *r = reg;
+
+ if (endian == REGMAP_ENDIAN_BIG)
+ return be16_to_cpu(*r);
+ else
+ return le16_to_cpu(*r);
+}
+
+static int regmap_raw_ram_gather_write(void *context,
+ const void *reg, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ struct regmap_ram_data *data = context;
+ unsigned int r;
+ u16 *our_buf = (u16 *)data->vals;
+ int i;
+
+ if (reg_len != 2)
+ return -EINVAL;
+ if (val_len % 2)
+ return -EINVAL;
+
+ r = decode_reg(data->reg_endian, reg);
+ if (data->noinc_reg && data->noinc_reg(data, r)) {
+ memcpy(&our_buf[r], val + val_len - 2, 2);
+ data->written[r] = true;
+ } else {
+ memcpy(&our_buf[r], val, val_len);
+
+ for (i = 0; i < val_len / 2; i++)
+ data->written[r + i] = true;
+ }
+
+ return 0;
+}
+
+static int regmap_raw_ram_write(void *context, const void *data, size_t count)
+{
+ return regmap_raw_ram_gather_write(context, data, 2,
+ data + 2, count - 2);
+}
+
+static int regmap_raw_ram_read(void *context,
+ const void *reg, size_t reg_len,
+ void *val, size_t val_len)
+{
+ struct regmap_ram_data *data = context;
+ unsigned int r;
+ u16 *our_buf = (u16 *)data->vals;
+ int i;
+
+ if (reg_len != 2)
+ return -EINVAL;
+ if (val_len % 2)
+ return -EINVAL;
+
+ r = decode_reg(data->reg_endian, reg);
+ if (data->noinc_reg && data->noinc_reg(data, r)) {
+ for (i = 0; i < val_len; i += 2)
+ memcpy(val + i, &our_buf[r], 2);
+ data->read[r] = true;
+ } else {
+ memcpy(val, &our_buf[r], val_len);
+
+ for (i = 0; i < val_len / 2; i++)
+ data->read[r + i] = true;
+ }
+
+ return 0;
+}
+
+static void regmap_raw_ram_free_context(void *context)
+{
+ struct regmap_ram_data *data = context;
+
+ kfree(data->vals);
+ kfree(data->read);
+ kfree(data->written);
+ kfree(data);
+}
+
+static const struct regmap_bus regmap_raw_ram = {
+ .fast_io = true,
+ .write = regmap_raw_ram_write,
+ .gather_write = regmap_raw_ram_gather_write,
+ .read = regmap_raw_ram_read,
+ .free_context = regmap_raw_ram_free_context,
+};
+
+struct regmap *__regmap_init_raw_ram(struct device *dev,
+ const struct regmap_config *config,
+ struct regmap_ram_data *data,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ struct regmap *map;
+
+ if (config->reg_bits != 16)
+ return ERR_PTR(-EINVAL);
+
+ if (!config->max_register) {
+ pr_crit("No max_register specified for RAM regmap\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ data->read = kcalloc(config->max_register + 1, sizeof(bool),
+ GFP_KERNEL);
+ if (!data->read)
+ return ERR_PTR(-ENOMEM);
+
+ data->written = kcalloc(config->max_register + 1, sizeof(bool),
+ GFP_KERNEL);
+ if (!data->written)
+ return ERR_PTR(-ENOMEM);
+
+ data->reg_endian = config->reg_format_endian;
+
+ map = __regmap_init(dev, &regmap_raw_ram, data, config,
+ lock_key, lock_name);
+
+ return map;
+}
+EXPORT_SYMBOL_GPL(__regmap_init_raw_ram);
+
+MODULE_DESCRIPTION("Register map access API - Memory region with raw access");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-sccb.c b/drivers/base/regmap/regmap-sccb.c
index 986af26d88c2..12bbbb03e5f2 100644
--- a/drivers/base/regmap/regmap-sccb.c
+++ b/drivers/base/regmap/regmap-sccb.c
@@ -125,4 +125,5 @@ struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sccb);
+MODULE_DESCRIPTION("Register map access API - SCCB support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c
index 388c3a087bd9..6a61629f5f89 100644
--- a/drivers/base/regmap/regmap-sdw-mbq.c
+++ b/drivers/base/regmap/regmap-sdw-mbq.c
@@ -1,47 +1,189 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2020 Intel Corporation.
+#include <linux/bits.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
+#include <sound/sdca_function.h>
#include "internal.h"
+struct regmap_mbq_context {
+ struct device *dev;
+ struct sdw_slave *sdw;
+
+ bool (*readable_reg)(struct device *dev, unsigned int reg);
+
+ struct regmap_sdw_mbq_cfg cfg;
+
+ int val_size;
+};
+
+static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
+{
+ int size = ctx->val_size;
+
+ if (ctx->cfg.mbq_size) {
+ size = ctx->cfg.mbq_size(ctx->dev, reg);
+ if (!size || size > ctx->val_size)
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned int reg)
+{
+ if (ctx->cfg.deferrable)
+ return ctx->cfg.deferrable(ctx->dev, reg);
+
+ return false;
+}
+
+static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
+ struct regmap_mbq_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ int val, ret = 0;
+
+ dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);
+
+ reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0,
+ SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
+
+ if (ctx->readable_reg(dev, reg)) {
+ ret = read_poll_timeout(sdw_read_no_pm, val,
+ val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY),
+ ctx->cfg.timeout_us, ctx->cfg.retry_us,
+ false, slave, reg);
+ if (val < 0)
+ return val;
+ if (ret)
+ dev_err(dev, "Function busy timed out 0x%x: %d\n", reg, val);
+ } else {
+ fsleep(ctx->cfg.timeout_us);
+ }
+
+ return ret;
+}
+
+static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
+ unsigned int reg, unsigned int val,
+ int mbq_size, bool deferrable)
+{
+ int shift = mbq_size * BITS_PER_BYTE;
+ int ret;
+
+ while (--mbq_size > 0) {
+ shift -= BITS_PER_BYTE;
+
+ ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg),
+ (val >> shift) & 0xff);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sdw_write_no_pm(slave, reg, val & 0xff);
+ if (deferrable && ret == -ENODATA)
+ return -EAGAIN;
+
+ return ret;
+}
+
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
- struct device *dev = context;
- struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct regmap_mbq_context *ctx = context;
+ struct sdw_slave *slave = ctx->sdw;
+ bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
+ int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
- ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg), (val >> 8) & 0xff);
- if (ret < 0)
- return ret;
+ if (mbq_size < 0)
+ return mbq_size;
+
+ /*
+ * Technically the spec does allow a device to set itself to busy for
+ * internal reasons, but since it doesn't provide any information on
+ * how to handle timeouts in that case, for now the code will only
+ * process a single wait/timeout on function busy and a single retry
+ * of the transaction.
+ */
+ ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, deferrable);
+ if (ret == -EAGAIN) {
+ ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
+ if (ret)
+ return ret;
+
+ ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, false);
+ }
- return sdw_write_no_pm(slave, reg, val & 0xff);
+ return ret;
}
-static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
+static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
+ unsigned int reg, unsigned int *val,
+ int mbq_size, bool deferrable)
{
- struct device *dev = context;
- struct sdw_slave *slave = dev_to_sdw_dev(dev);
- int read0;
- int read1;
+ int shift = BITS_PER_BYTE;
+ int read;
+
+ read = sdw_read_no_pm(slave, reg);
+ if (read < 0) {
+ if (deferrable && read == -ENODATA)
+ return -EAGAIN;
+
+ return read;
+ }
- read0 = sdw_read_no_pm(slave, reg);
- if (read0 < 0)
- return read0;
+ *val = read;
- read1 = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
- if (read1 < 0)
- return read1;
+ while (--mbq_size > 0) {
+ read = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
+ if (read < 0)
+ return read;
- *val = (read1 << 8) | read0;
+ *val |= read << shift;
+ shift += BITS_PER_BYTE;
+ }
return 0;
}
+static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct regmap_mbq_context *ctx = context;
+ struct sdw_slave *slave = ctx->sdw;
+ bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
+ int mbq_size = regmap_sdw_mbq_size(ctx, reg);
+ int ret;
+
+ if (mbq_size < 0)
+ return mbq_size;
+
+ /*
+ * Technically the spec does allow a device to set itself to busy for
+ * internal reasons, but since it doesn't provide any information on
+ * how to handle timeouts in that case, for now the code will only
+ * process a single wait/timeout on function busy and a single retry
+ * of the transaction.
+ */
+ ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, deferrable);
+ if (ret == -EAGAIN) {
+ ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
+ if (ret)
+ return ret;
+
+ ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, false);
+ }
+
+ return ret;
+}
+
static const struct regmap_bus regmap_sdw_mbq = {
.reg_read = regmap_sdw_mbq_read,
.reg_write = regmap_sdw_mbq_write,
@@ -51,8 +193,7 @@ static const struct regmap_bus regmap_sdw_mbq = {
static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
{
- /* MBQ-based controls are only 16-bits for now */
- if (config->val_bits != 16)
+ if (config->val_bits > (sizeof(unsigned int) * BITS_PER_BYTE))
return -ENOTSUPP;
/* Registers are 32 bits wide */
@@ -65,37 +206,73 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
return 0;
}
-struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
+static struct regmap_mbq_context *
+regmap_sdw_mbq_gen_context(struct device *dev,
+ struct sdw_slave *sdw,
+ const struct regmap_config *config,
+ const struct regmap_sdw_mbq_cfg *mbq_config)
+{
+ struct regmap_mbq_context *ctx;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->dev = dev;
+ ctx->sdw = sdw;
+
+ if (mbq_config)
+ ctx->cfg = *mbq_config;
+
+ ctx->val_size = config->val_bits / BITS_PER_BYTE;
+ ctx->readable_reg = config->readable_reg;
+
+ return ctx;
+}
+
+struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
+ const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name)
{
+ struct regmap_mbq_context *ctx;
int ret;
ret = regmap_sdw_mbq_config_check(config);
if (ret)
return ERR_PTR(ret);
- return __regmap_init(&sdw->dev, &regmap_sdw_mbq,
- &sdw->dev, config, lock_key, lock_name);
+ ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
+ if (IS_ERR(ctx))
+ return ERR_CAST(ctx);
+
+ return __regmap_init(dev, &regmap_sdw_mbq, ctx,
+ config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);
-struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
+struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
+ const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name)
{
+ struct regmap_mbq_context *ctx;
int ret;
ret = regmap_sdw_mbq_config_check(config);
if (ret)
return ERR_PTR(ret);
- return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq,
- &sdw->dev, config, lock_key, lock_name);
+ ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
+ if (IS_ERR(ctx))
+ return ERR_CAST(ctx);
+
+ return __devm_regmap_init(dev, &regmap_sdw_mbq, ctx,
+ config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);
-MODULE_DESCRIPTION("Regmap SoundWire MBQ Module");
+MODULE_DESCRIPTION("regmap SoundWire MBQ Module");
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-sdw.c b/drivers/base/regmap/regmap-sdw.c
index 81b0327f719d..ea631ac7c7ec 100644
--- a/drivers/base/regmap/regmap-sdw.c
+++ b/drivers/base/regmap/regmap-sdw.c
@@ -6,50 +6,63 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
#include "internal.h"
-static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val)
+static int regmap_sdw_write(void *context, const void *val_buf, size_t val_size)
{
struct device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ /* First word of buffer contains the destination address */
+ u32 addr = le32_to_cpu(*(const __le32 *)val_buf);
+ const u8 *val = val_buf;
- return sdw_write_no_pm(slave, reg, val);
+ return sdw_nwrite_no_pm(slave, addr, val_size - sizeof(addr), val + sizeof(addr));
}
-static int regmap_sdw_read(void *context, unsigned int reg, unsigned int *val)
+static int regmap_sdw_gather_write(void *context,
+ const void *reg_buf, size_t reg_size,
+ const void *val_buf, size_t val_size)
{
struct device *dev = context;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
- int read;
+ u32 addr = le32_to_cpu(*(const __le32 *)reg_buf);
- read = sdw_read_no_pm(slave, reg);
- if (read < 0)
- return read;
+ return sdw_nwrite_no_pm(slave, addr, val_size, val_buf);
+}
- *val = read;
- return 0;
+static int regmap_sdw_read(void *context,
+ const void *reg_buf, size_t reg_size,
+ void *val_buf, size_t val_size)
+{
+ struct device *dev = context;
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ u32 addr = le32_to_cpu(*(const __le32 *)reg_buf);
+
+ return sdw_nread_no_pm(slave, addr, val_size, val_buf);
}
static const struct regmap_bus regmap_sdw = {
- .reg_read = regmap_sdw_read,
- .reg_write = regmap_sdw_write,
+ .write = regmap_sdw_write,
+ .gather_write = regmap_sdw_gather_write,
+ .read = regmap_sdw_read,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
static int regmap_sdw_config_check(const struct regmap_config *config)
{
- /* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */
- if (config->val_bits != 8)
- return -ENOTSUPP;
-
- /* Registers are 32 bits wide */
+ /* Register addresses are 32 bits wide */
if (config->reg_bits != 32)
return -ENOTSUPP;
if (config->pad_bits != 0)
return -ENOTSUPP;
+ /* Only bulk writes are supported not multi-register writes */
+ if (config->can_multi_write)
+ return -ENOTSUPP;
+
return 0;
}
@@ -85,5 +98,5 @@ struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw);
-MODULE_DESCRIPTION("Regmap SoundWire Module");
+MODULE_DESCRIPTION("regmap SoundWire Module");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c
index 8075db788b39..e523fae73004 100644
--- a/drivers/base/regmap/regmap-slimbus.c
+++ b/drivers/base/regmap/regmap-slimbus.c
@@ -48,8 +48,7 @@ struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,
if (IS_ERR(bus))
return ERR_CAST(bus);
- return __regmap_init(&slimbus->dev, bus, &slimbus->dev, config,
- lock_key, lock_name);
+ return __regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_slimbus);
@@ -63,9 +62,9 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
if (IS_ERR(bus))
return ERR_CAST(bus);
- return __devm_regmap_init(&slimbus->dev, bus, &slimbus, config,
- lock_key, lock_name);
+ return __devm_regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus);
+MODULE_DESCRIPTION("Register map access API - SLIMbus support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-spi-avmm.c b/drivers/base/regmap/regmap-spi-avmm.c
index ad1da83e849f..d86a06cadcdb 100644
--- a/drivers/base/regmap/regmap-spi-avmm.c
+++ b/drivers/base/regmap/regmap-spi-avmm.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
+#include <linux/swab.h>
/*
* This driver implements the regmap operations for a generic SPI
@@ -162,19 +163,12 @@ struct spi_avmm_bridge {
/* bridge buffer used in translation between protocol layers */
char trans_buf[TRANS_BUF_SIZE];
char phy_buf[PHY_BUF_SIZE];
- void (*swap_words)(char *buf, unsigned int len);
+ void (*swap_words)(void *buf, unsigned int len);
};
-static void br_swap_words_32(char *buf, unsigned int len)
+static void br_swap_words_32(void *buf, unsigned int len)
{
- u32 *p = (u32 *)buf;
- unsigned int count;
-
- count = len / 4;
- while (count--) {
- *p = swab32p(p);
- p++;
- }
+ swab32_array(buf, len / 4);
}
/*
@@ -716,4 +710,5 @@ struct regmap *__devm_regmap_init_spi_avmm(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spi_avmm);
+MODULE_DESCRIPTION("Register map access API - SPI AVMM support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
index 37ab23a9d034..14b1d88997cb 100644
--- a/drivers/base/regmap/regmap-spi.c
+++ b/drivers/base/regmap/regmap-spi.c
@@ -122,8 +122,7 @@ static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
return ERR_PTR(-ENOMEM);
max_msg_size = spi_max_message_size(spi);
- reg_reserve_size = config->reg_bits / BITS_PER_BYTE
- + config->pad_bits / BITS_PER_BYTE;
+ reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
if (max_size + reg_reserve_size > max_msg_size)
max_size -= reg_reserve_size;
@@ -165,4 +164,5 @@ struct regmap *__devm_regmap_init_spi(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spi);
+MODULE_DESCRIPTION("regmap SPI Module");
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c
index cdf12d2aa3a1..347bfe9544ce 100644
--- a/drivers/base/regmap/regmap-spmi.c
+++ b/drivers/base/regmap/regmap-spmi.c
@@ -222,4 +222,5 @@ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext);
+MODULE_DESCRIPTION("Register map access API - SPMI support");
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-w1.c b/drivers/base/regmap/regmap-w1.c
index 3a8b402db852..29fd24f9c7ed 100644
--- a/drivers/base/regmap/regmap-w1.c
+++ b/drivers/base/regmap/regmap-w1.c
@@ -234,4 +234,5 @@ struct regmap *__devm_regmap_init_w1(struct device *w1_dev,
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_w1);
+MODULE_DESCRIPTION("Register map access API - W1 (1-Wire) support");
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index fee221c5008c..ce9be3989a21 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -17,7 +17,7 @@
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/hwspinlock.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -89,7 +89,7 @@ EXPORT_SYMBOL_GPL(regmap_check_range_table);
bool regmap_writeable(struct regmap *map, unsigned int reg)
{
- if (map->max_register && reg > map->max_register)
+ if (map->max_register_is_set && reg > map->max_register)
return false;
if (map->writeable_reg)
@@ -112,7 +112,7 @@ bool regmap_cached(struct regmap *map, unsigned int reg)
if (!map->cache_ops)
return false;
- if (map->max_register && reg > map->max_register)
+ if (map->max_register_is_set && reg > map->max_register)
return false;
map->lock(map->lock_arg);
@@ -129,7 +129,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg)
if (!map->reg_read)
return false;
- if (map->max_register && reg > map->max_register)
+ if (map->max_register_is_set && reg > map->max_register)
return false;
if (map->format.format_write)
@@ -288,15 +288,9 @@ static void regmap_format_16_native(void *buf, unsigned int val,
memcpy(buf, &v, sizeof(v));
}
-static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
+static void regmap_format_24_be(void *buf, unsigned int val, unsigned int shift)
{
- u8 *b = buf;
-
- val <<= shift;
-
- b[0] = val >> 16;
- b[1] = val >> 8;
- b[2] = val;
+ put_unaligned_be24(val << shift, buf);
}
static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
@@ -317,26 +311,6 @@ static void regmap_format_32_native(void *buf, unsigned int val,
memcpy(buf, &v, sizeof(v));
}
-#ifdef CONFIG_64BIT
-static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift)
-{
- put_unaligned_be64((u64) val << shift, buf);
-}
-
-static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift)
-{
- put_unaligned_le64((u64) val << shift, buf);
-}
-
-static void regmap_format_64_native(void *buf, unsigned int val,
- unsigned int shift)
-{
- u64 v = (u64) val << shift;
-
- memcpy(buf, &v, sizeof(v));
-}
-#endif
-
static void regmap_parse_inplace_noop(void *buf)
{
}
@@ -380,14 +354,9 @@ static unsigned int regmap_parse_16_native(const void *buf)
return v;
}
-static unsigned int regmap_parse_24(const void *buf)
+static unsigned int regmap_parse_24_be(const void *buf)
{
- const u8 *b = buf;
- unsigned int ret = b[2];
- ret |= ((unsigned int)b[1]) << 8;
- ret |= ((unsigned int)b[0]) << 16;
-
- return ret;
+ return get_unaligned_be24(buf);
}
static unsigned int regmap_parse_32_be(const void *buf)
@@ -422,40 +391,6 @@ static unsigned int regmap_parse_32_native(const void *buf)
return v;
}
-#ifdef CONFIG_64BIT
-static unsigned int regmap_parse_64_be(const void *buf)
-{
- return get_unaligned_be64(buf);
-}
-
-static unsigned int regmap_parse_64_le(const void *buf)
-{
- return get_unaligned_le64(buf);
-}
-
-static void regmap_parse_64_be_inplace(void *buf)
-{
- u64 v = get_unaligned_be64(buf);
-
- memcpy(buf, &v, sizeof(v));
-}
-
-static void regmap_parse_64_le_inplace(void *buf)
-{
- u64 v = get_unaligned_le64(buf);
-
- memcpy(buf, &v, sizeof(v));
-}
-
-static unsigned int regmap_parse_64_native(const void *buf)
-{
- u64 v;
-
- memcpy(&v, buf, sizeof(v));
- return v;
-}
-#endif
-
static void regmap_lock_hwlock(void *__map)
{
struct regmap *map = __map;
@@ -663,6 +598,17 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
}
EXPORT_SYMBOL_GPL(regmap_attach_dev);
+static int dev_get_regmap_match(struct device *dev, void *res, void *data);
+
+static int regmap_detach_dev(struct device *dev, struct regmap *map)
+{
+ if (!dev)
+ return 0;
+
+ return devres_release(dev, dev_get_regmap_release,
+ dev_get_regmap_match, (void *)map->name);
+}
+
static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
const struct regmap_config *config)
{
@@ -810,6 +756,7 @@ struct regmap *__regmap_init(struct device *dev,
lock_key, lock_name);
}
map->lock_arg = map;
+ map->lock_key = lock_key;
}
/*
@@ -822,14 +769,13 @@ struct regmap *__regmap_init(struct device *dev,
map->alloc_flags = GFP_KERNEL;
map->reg_base = config->reg_base;
+ map->reg_shift = config->pad_bits % 8;
- map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8;
- map->format.reg_downshift = config->reg_downshift;
- map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
- map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
- config->val_bits + config->pad_bits, 8);
- map->reg_shift = config->pad_bits % 8;
+ map->format.reg_shift = config->reg_shift;
+ map->format.reg_bytes = BITS_TO_BYTES(config->reg_bits);
+ map->format.val_bytes = BITS_TO_BYTES(config->val_bits);
+ map->format.buf_size = BITS_TO_BYTES(config->reg_bits + config->val_bits + config->pad_bits);
if (config->reg_stride)
map->reg_stride = config->reg_stride;
else
@@ -852,6 +798,7 @@ struct regmap *__regmap_init(struct device *dev,
map->bus = bus;
map->bus_context = bus_context;
map->max_register = config->max_register;
+ map->max_register_is_set = map->max_register ?: config->max_register_is_0;
map->wr_table = config->wr_table;
map->rd_table = config->rd_table;
map->volatile_table = config->volatile_table;
@@ -880,7 +827,7 @@ struct regmap *__regmap_init(struct device *dev,
map->read_flag_mask = bus->read_flag_mask;
}
- if (config && config->read && config->write) {
+ if (config->read && config->write) {
map->reg_read = _regmap_bus_read;
if (config->reg_update_bits)
map->reg_update_bits = config->reg_update_bits;
@@ -991,44 +938,30 @@ struct regmap *__regmap_init(struct device *dev,
break;
case 24:
- if (reg_endian != REGMAP_ENDIAN_BIG)
- goto err_hwlock;
- map->format.format_reg = regmap_format_24;
- break;
-
- case 32:
switch (reg_endian) {
case REGMAP_ENDIAN_BIG:
- map->format.format_reg = regmap_format_32_be;
- break;
- case REGMAP_ENDIAN_LITTLE:
- map->format.format_reg = regmap_format_32_le;
- break;
- case REGMAP_ENDIAN_NATIVE:
- map->format.format_reg = regmap_format_32_native;
+ map->format.format_reg = regmap_format_24_be;
break;
default:
goto err_hwlock;
}
break;
-#ifdef CONFIG_64BIT
- case 64:
+ case 32:
switch (reg_endian) {
case REGMAP_ENDIAN_BIG:
- map->format.format_reg = regmap_format_64_be;
+ map->format.format_reg = regmap_format_32_be;
break;
case REGMAP_ENDIAN_LITTLE:
- map->format.format_reg = regmap_format_64_le;
+ map->format.format_reg = regmap_format_32_le;
break;
case REGMAP_ENDIAN_NATIVE:
- map->format.format_reg = regmap_format_64_native;
+ map->format.format_reg = regmap_format_32_native;
break;
default:
goto err_hwlock;
}
break;
-#endif
default:
goto err_hwlock;
@@ -1064,10 +997,14 @@ struct regmap *__regmap_init(struct device *dev,
}
break;
case 24:
- if (val_endian != REGMAP_ENDIAN_BIG)
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_24_be;
+ map->format.parse_val = regmap_parse_24_be;
+ break;
+ default:
goto err_hwlock;
- map->format.format_val = regmap_format_24;
- map->format.parse_val = regmap_parse_24;
+ }
break;
case 32:
switch (val_endian) {
@@ -1089,28 +1026,6 @@ struct regmap *__regmap_init(struct device *dev,
goto err_hwlock;
}
break;
-#ifdef CONFIG_64BIT
- case 64:
- switch (val_endian) {
- case REGMAP_ENDIAN_BIG:
- map->format.format_val = regmap_format_64_be;
- map->format.parse_val = regmap_parse_64_be;
- map->format.parse_inplace = regmap_parse_64_be_inplace;
- break;
- case REGMAP_ENDIAN_LITTLE:
- map->format.format_val = regmap_format_64_le;
- map->format.parse_val = regmap_parse_64_le;
- map->format.parse_inplace = regmap_parse_64_le_inplace;
- break;
- case REGMAP_ENDIAN_NATIVE:
- map->format.format_val = regmap_format_64_native;
- map->format.parse_val = regmap_parse_64_native;
- break;
- default:
- goto err_hwlock;
- }
- break;
-#endif
}
if (map->format.format_write) {
@@ -1147,13 +1062,13 @@ skip_format_initialization:
/* Sanity check */
if (range_cfg->range_max < range_cfg->range_min) {
- dev_err(map->dev, "Invalid range %d: %d < %d\n", i,
+ dev_err(map->dev, "Invalid range %d: %u < %u\n", i,
range_cfg->range_max, range_cfg->range_min);
goto err_range;
}
if (range_cfg->range_max > map->max_register) {
- dev_err(map->dev, "Invalid range %d: %d > %d\n", i,
+ dev_err(map->dev, "Invalid range %d: %u > %u\n", i,
range_cfg->range_max, map->max_register);
goto err_range;
}
@@ -1258,6 +1173,8 @@ err_name:
err_map:
kfree(map);
err:
+ if (bus && bus->free_on_exit)
+ kfree(bus);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(__regmap_init);
@@ -1509,6 +1426,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
regmap_debugfs_exit(map);
map->max_register = config->max_register;
+ map->max_register_is_set = map->max_register ?: config->max_register_is_0;
map->writeable_reg = config->writeable_reg;
map->readable_reg = config->readable_reg;
map->volatile_reg = config->volatile_reg;
@@ -1539,7 +1457,9 @@ void regmap_exit(struct regmap *map)
{
struct regmap_async *async;
+ regmap_detach_dev(map->dev, map);
regcache_exit(map);
+
regmap_debugfs_exit(map);
regmap_range_exit(map);
if (map->bus && map->bus->free_context)
@@ -1575,7 +1495,7 @@ static int dev_get_regmap_match(struct device *dev, void *res, void *data)
/* If the user didn't specify a name match any */
if (data)
- return !strcmp((*r)->name, data);
+ return (*r)->name && !strcmp((*r)->name, data);
else
return 1;
}
@@ -1679,6 +1599,18 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
buf[i] |= (mask >> (8 * i)) & 0xff;
}
+static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg)
+{
+ reg += map->reg_base;
+
+ if (map->format.reg_shift > 0)
+ reg >>= map->format.reg_shift;
+ else if (map->format.reg_shift < 0)
+ reg <<= -(map->format.reg_shift);
+
+ return reg;
+}
+
static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc)
{
@@ -1705,17 +1637,19 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
}
if (!map->cache_bypass && map->format.parse_val) {
- unsigned int ival;
+ unsigned int ival, offset;
int val_bytes = map->format.val_bytes;
- for (i = 0; i < val_len / val_bytes; i++) {
- ival = map->format.parse_val(val + (i * val_bytes));
- ret = regcache_write(map,
- reg + regmap_get_offset(map, i),
- ival);
+
+ /* Cache the last written value for noinc writes */
+ i = noinc ? val_len - val_bytes : 0;
+ for (; i < val_len; i += val_bytes) {
+ ival = map->format.parse_val(val + i);
+ offset = noinc ? 0 : regmap_get_offset(map, i / val_bytes);
+ ret = regcache_write(map, reg + offset, ival);
if (ret) {
dev_err(map->dev,
"Error in caching of register: %x ret: %d\n",
- reg + regmap_get_offset(map, i), ret);
+ reg + offset, ret);
return ret;
}
}
@@ -1756,8 +1690,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
return ret;
}
- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->write_flag_mask);
@@ -1927,8 +1860,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
return ret;
}
- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_write(map, reg, val);
trace_regmap_hw_write_start(map, reg, 1);
@@ -1944,7 +1876,17 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct regmap *map = context;
+ struct regmap_range_node *range;
+ int ret;
+
+ range = _regmap_range_lookup(map, reg);
+ if (range) {
+ ret = _regmap_select_page(map, &reg, range, 1);
+ if (ret != 0)
+ return ret;
+ }
+ reg = regmap_reg_addr(map, reg);
return map->bus->reg_write(map->bus_context, reg, val);
}
@@ -2132,8 +2074,86 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
}
EXPORT_SYMBOL_GPL(regmap_raw_write);
+static int regmap_noinc_readwrite(struct regmap *map, unsigned int reg,
+ void *val, unsigned int val_len, bool write)
+{
+ size_t val_bytes = map->format.val_bytes;
+ size_t val_count = val_len / val_bytes;
+ unsigned int lastval;
+ u8 *u8p;
+ u16 *u16p;
+ u32 *u32p;
+ int ret;
+ int i;
+
+ switch (val_bytes) {
+ case 1:
+ u8p = val;
+ if (write)
+ lastval = (unsigned int)u8p[val_count - 1];
+ break;
+ case 2:
+ u16p = val;
+ if (write)
+ lastval = (unsigned int)u16p[val_count - 1];
+ break;
+ case 4:
+ u32p = val;
+ if (write)
+ lastval = (unsigned int)u32p[val_count - 1];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Update the cache with the last value we write, the rest is just
+ * gone down in the hardware FIFO. We can't cache FIFOs. This makes
+ * sure a single read from the cache will work.
+ */
+ if (write) {
+ if (!map->cache_bypass && !map->defer_caching) {
+ ret = regcache_write(map, reg, lastval);
+ if (ret != 0)
+ return ret;
+ if (map->cache_only) {
+ map->cache_dirty = true;
+ return 0;
+ }
+ }
+ ret = map->bus->reg_noinc_write(map->bus_context, reg, val, val_count);
+ } else {
+ ret = map->bus->reg_noinc_read(map->bus_context, reg, val, val_count);
+ }
+
+ if (!ret && regmap_should_log(map)) {
+ dev_info(map->dev, "%x %s [", reg, write ? "<=" : "=>");
+ for (i = 0; i < val_count; i++) {
+ switch (val_bytes) {
+ case 1:
+ pr_cont("%x", u8p[i]);
+ break;
+ case 2:
+ pr_cont("%x", u16p[i]);
+ break;
+ case 4:
+ pr_cont("%x", u32p[i]);
+ break;
+ default:
+ break;
+ }
+ if (i == (val_count - 1))
+ pr_cont("]\n");
+ else
+ pr_cont(",");
+ }
+ }
+
+ return 0;
+}
+
/**
- * regmap_noinc_write(): Write data from a register without incrementing the
+ * regmap_noinc_write(): Write data to a register without incrementing the
* register number
*
* @map: Register map to write to
@@ -2159,9 +2179,8 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
size_t write_len;
int ret;
- if (!map->write)
- return -ENOTSUPP;
-
+ if (!map->write && !(map->bus && map->bus->reg_noinc_write))
+ return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
if (!IS_ALIGNED(reg, map->reg_stride))
@@ -2176,6 +2195,15 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
goto out_unlock;
}
+ /*
+ * Use the accelerated operation if we can. The val drops the const
+ * typing in order to facilitate code reuse in regmap_noinc_readwrite().
+ */
+ if (map->bus->reg_noinc_write) {
+ ret = regmap_noinc_readwrite(map, reg, (void *)val, val_len, true);
+ goto out_unlock;
+ }
+
while (val_len) {
if (map->max_raw_write && map->max_raw_write < val_len)
write_len = map->max_raw_write;
@@ -2230,12 +2258,14 @@ EXPORT_SYMBOL_GPL(regmap_field_update_bits_base);
* @field: Register field to operate on
* @bits: Bits to test
*
- * Returns -1 if the underlying regmap_field_read() fails, 0 if at least one of the
- * tested bits is not set and 1 if all tested bits are set.
+ * Returns negative errno if the underlying regmap_field_read() fails,
+ * 0 if at least one of the tested bits is not set and 1 if all tested
+ * bits are set.
*/
int regmap_field_test_bits(struct regmap_field *field, unsigned int bits)
{
- unsigned int val, ret;
+ unsigned int val;
+ int ret;
ret = regmap_field_read(field, &val);
if (ret)
@@ -2318,11 +2348,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
case 4:
ival = *(u32 *)(val + (i * val_bytes));
break;
-#ifdef CONFIG_64BIT
- case 8:
- ival = *(u64 *)(val + (i * val_bytes));
- break;
-#endif
default:
ret = -EINVAL;
goto out;
@@ -2339,7 +2364,7 @@ out:
} else {
void *wval;
- wval = kmemdup(val, val_count * val_bytes, map->alloc_flags);
+ wval = kmemdup_array(val, val_count, val_bytes, map->alloc_flags);
if (!wval)
return -ENOMEM;
@@ -2350,6 +2375,10 @@ out:
kfree(wval);
}
+
+ if (!ret)
+ trace_regmap_bulk_write(map, reg, val, val_bytes * val_count);
+
return ret;
}
EXPORT_SYMBOL_GPL(regmap_bulk_write);
@@ -2390,8 +2419,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map,
unsigned int reg = regs[i].reg;
unsigned int val = regs[i].def;
trace_regmap_hw_write_start(map, reg, 1);
- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_reg(u8, reg, map->reg_shift);
u8 += reg_bytes + pad_bytes;
map->format.format_val(u8, val, 0);
@@ -2717,8 +2745,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return ret;
}
- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->read_flag_mask);
@@ -2737,7 +2764,17 @@ static int _regmap_bus_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct regmap *map = context;
+ struct regmap_range_node *range;
+ int ret;
+
+ range = _regmap_range_lookup(map, reg);
+ if (range) {
+ ret = _regmap_select_page(map, &reg, range, 1);
+ if (ret != 0)
+ return ret;
+ }
+ reg = regmap_reg_addr(map, reg);
return map->bus->reg_read(map->bus_context, reg, val);
}
@@ -2819,6 +2856,43 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
EXPORT_SYMBOL_GPL(regmap_read);
/**
+ * regmap_read_bypassed() - Read a value from a single register direct
+ * from the device, bypassing the cache
+ *
+ * @map: Register map to read from
+ * @reg: Register to be read from
+ * @val: Pointer to store read value
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)
+{
+ int ret;
+ bool bypass, cache_only;
+
+ if (!IS_ALIGNED(reg, map->reg_stride))
+ return -EINVAL;
+
+ map->lock(map->lock_arg);
+
+ bypass = map->cache_bypass;
+ cache_only = map->cache_only;
+ map->cache_bypass = true;
+ map->cache_only = false;
+
+ ret = _regmap_read(map, reg, val);
+
+ map->cache_bypass = bypass;
+ map->cache_only = cache_only;
+
+ map->unlock(map->lock_arg);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_read_bypassed);
+
+/**
* regmap_raw_read() - Read raw data from the device
*
* @map: Register map to read from
@@ -2851,6 +2925,11 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
size_t chunk_count, chunk_bytes;
size_t chunk_regs = val_count;
+ if (!map->cache_bypass && map->cache_only) {
+ ret = -EBUSY;
+ goto out;
+ }
+
if (!map->read) {
ret = -ENOTSUPP;
goto out;
@@ -2946,6 +3025,23 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
goto out_unlock;
}
+ /*
+ * We have not defined the FIFO semantics for cache, as the
+ * cache is just one value deep. Should we return the last
+ * written value? Just avoid this by always reading the FIFO
+ * even when using cache. Cache only will not work.
+ */
+ if (!map->cache_bypass && map->cache_only) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* Use the accelerated operation if we can */
+ if (map->bus->reg_noinc_read) {
+ ret = regmap_noinc_readwrite(map, reg, val, val_len, false);
+ goto out_unlock;
+ }
+
while (val_len) {
if (map->max_raw_read && map->max_raw_read < val_len)
read_len = map->max_raw_read;
@@ -3022,8 +3118,53 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id,
}
EXPORT_SYMBOL_GPL(regmap_fields_read);
+static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
+ const unsigned int *regs, void *val, size_t val_count)
+{
+ u32 *u32 = val;
+ u16 *u16 = val;
+ u8 *u8 = val;
+ int ret, i;
+
+ map->lock(map->lock_arg);
+
+ for (i = 0; i < val_count; i++) {
+ unsigned int ival;
+
+ if (regs) {
+ if (!IS_ALIGNED(regs[i], map->reg_stride)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = _regmap_read(map, regs[i], &ival);
+ } else {
+ ret = _regmap_read(map, reg + regmap_get_offset(map, i), &ival);
+ }
+ if (ret != 0)
+ goto out;
+
+ switch (map->format.val_bytes) {
+ case 4:
+ u32[i] = ival;
+ break;
+ case 2:
+ u16[i] = ival;
+ break;
+ case 1:
+ u8[i] = ival;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+out:
+ map->unlock(map->lock_arg);
+ return ret;
+}
+
/**
- * regmap_bulk_read() - Read multiple registers from the device
+ * regmap_bulk_read() - Read multiple sequential registers from the device
*
* @map: Register map to read from
* @reg: First register to be read from
@@ -3053,52 +3194,35 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
for (i = 0; i < val_count * val_bytes; i += val_bytes)
map->format.parse_inplace(val + i);
} else {
-#ifdef CONFIG_64BIT
- u64 *u64 = val;
-#endif
- u32 *u32 = val;
- u16 *u16 = val;
- u8 *u8 = val;
-
- map->lock(map->lock_arg);
-
- for (i = 0; i < val_count; i++) {
- unsigned int ival;
-
- ret = _regmap_read(map, reg + regmap_get_offset(map, i),
- &ival);
- if (ret != 0)
- goto out;
-
- switch (map->format.val_bytes) {
-#ifdef CONFIG_64BIT
- case 8:
- u64[i] = ival;
- break;
-#endif
- case 4:
- u32[i] = ival;
- break;
- case 2:
- u16[i] = ival;
- break;
- case 1:
- u8[i] = ival;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
- }
-
-out:
- map->unlock(map->lock_arg);
+ ret = _regmap_bulk_read(map, reg, NULL, val, val_count);
}
-
+ if (!ret)
+ trace_regmap_bulk_read(map, reg, val, val_bytes * val_count);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_bulk_read);
+/**
+ * regmap_multi_reg_read() - Read multiple non-sequential registers from the device
+ *
+ * @map: Register map to read from
+ * @regs: Array of registers to read from
+ * @val: Pointer to store read value, in native register size for device
+ * @val_count: Number of registers to read
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_multi_reg_read(struct regmap *map, const unsigned int *regs, void *val,
+ size_t val_count)
+{
+ if (val_count == 0)
+ return -EINVAL;
+
+ return _regmap_bulk_read(map, 0, regs, val, val_count);
+}
+EXPORT_SYMBOL_GPL(regmap_multi_reg_read);
+
static int _regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool force_write)
@@ -3110,6 +3234,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
*change = false;
if (regmap_volatile(map, reg) && map->reg_update_bits) {
+ reg = regmap_reg_addr(map, reg);
ret = map->reg_update_bits(map->bus_context, reg, mask, val);
if (ret == 0 && change)
*change = true;
@@ -3121,7 +3246,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
tmp = orig & ~mask;
tmp |= val & mask;
- if (force_write || (tmp != orig)) {
+ if (force_write || (tmp != orig) || map->force_write_field) {
ret = _regmap_write(map, reg, tmp);
if (ret == 0 && change)
*change = true;
@@ -3186,7 +3311,8 @@ EXPORT_SYMBOL_GPL(regmap_update_bits_base);
*/
int regmap_test_bits(struct regmap *map, unsigned int reg, unsigned int bits)
{
- unsigned int val, ret;
+ unsigned int val;
+ int ret;
ret = regmap_read(map, reg, &val);
if (ret)
@@ -3347,7 +3473,7 @@ EXPORT_SYMBOL_GPL(regmap_get_val_bytes);
*/
int regmap_get_max_register(struct regmap *map)
{
- return map->max_register ? map->max_register : -EINVAL;
+ return map->max_register_is_set ? map->max_register : -EINVAL;
}
EXPORT_SYMBOL_GPL(regmap_get_max_register);
@@ -3365,6 +3491,19 @@ int regmap_get_reg_stride(struct regmap *map)
}
EXPORT_SYMBOL_GPL(regmap_get_reg_stride);
+/**
+ * regmap_might_sleep() - Returns whether a regmap access might sleep.
+ *
+ * @map: Register map to operate on.
+ *
+ * Returns true if an access to the register might sleep, else false.
+ */
+bool regmap_might_sleep(struct regmap *map)
+{
+ return map->can_sleep;
+}
+EXPORT_SYMBOL_GPL(regmap_might_sleep);
+
int regmap_parse_val(struct regmap *map, const void *buf,
unsigned int *val)
{
diff --git a/drivers/base/regmap/trace.h b/drivers/base/regmap/trace.h
index 9abee14df9ee..bcc5a8b226a6 100644
--- a/drivers/base/regmap/trace.h
+++ b/drivers/base/regmap/trace.h
@@ -27,14 +27,12 @@ DECLARE_EVENT_CLASS(regmap_reg,
),
TP_fast_assign(
- __assign_str(name, regmap_name(map));
+ __assign_str(name);
__entry->reg = reg;
__entry->val = val;
),
- TP_printk("%s reg=%x val=%x", __get_str(name),
- (unsigned int)__entry->reg,
- (unsigned int)__entry->val)
+ TP_printk("%s reg=%x val=%x", __get_str(name), __entry->reg, __entry->val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_write,
@@ -43,7 +41,6 @@ DEFINE_EVENT(regmap_reg, regmap_reg_write,
unsigned int val),
TP_ARGS(map, reg, val)
-
);
DEFINE_EVENT(regmap_reg, regmap_reg_read,
@@ -52,7 +49,6 @@ DEFINE_EVENT(regmap_reg, regmap_reg_read,
unsigned int val),
TP_ARGS(map, reg, val)
-
);
DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
@@ -61,7 +57,47 @@ DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
unsigned int val),
TP_ARGS(map, reg, val)
+);
+
+DECLARE_EVENT_CLASS(regmap_bulk,
+ TP_PROTO(struct regmap *map, unsigned int reg,
+ const void *val, int val_len),
+
+ TP_ARGS(map, reg, val, val_len),
+
+ TP_STRUCT__entry(
+ __string(name, regmap_name(map))
+ __field(unsigned int, reg)
+ __dynamic_array(char, buf, val_len)
+ __field(int, val_len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->reg = reg;
+ __entry->val_len = val_len;
+ memcpy(__get_dynamic_array(buf), val, val_len);
+ ),
+
+ TP_printk("%s reg=%x val=%s", __get_str(name), __entry->reg,
+ __print_hex(__get_dynamic_array(buf), __entry->val_len))
+);
+
+DEFINE_EVENT(regmap_bulk, regmap_bulk_write,
+
+ TP_PROTO(struct regmap *map, unsigned int reg,
+ const void *val, int val_len),
+
+ TP_ARGS(map, reg, val, val_len)
+);
+
+DEFINE_EVENT(regmap_bulk, regmap_bulk_read,
+
+ TP_PROTO(struct regmap *map, unsigned int reg,
+ const void *val, int val_len),
+
+ TP_ARGS(map, reg, val, val_len)
);
DECLARE_EVENT_CLASS(regmap_block,
@@ -77,14 +113,12 @@ DECLARE_EVENT_CLASS(regmap_block,
),
TP_fast_assign(
- __assign_str(name, regmap_name(map));
+ __assign_str(name);
__entry->reg = reg;
__entry->count = count;
),
- TP_printk("%s reg=%x count=%d", __get_str(name),
- (unsigned int)__entry->reg,
- (int)__entry->count)
+ TP_printk("%s reg=%x count=%d", __get_str(name), __entry->reg, __entry->count)
);
DEFINE_EVENT(regmap_block, regmap_hw_read_start,
@@ -129,9 +163,9 @@ TRACE_EVENT(regcache_sync,
),
TP_fast_assign(
- __assign_str(name, regmap_name(map));
- __assign_str(status, status);
- __assign_str(type, type);
+ __assign_str(name);
+ __assign_str(status);
+ __assign_str(type);
),
TP_printk("%s type=%s status=%s", __get_str(name),
@@ -150,12 +184,11 @@ DECLARE_EVENT_CLASS(regmap_bool,
),
TP_fast_assign(
- __assign_str(name, regmap_name(map));
+ __assign_str(name);
__entry->flag = flag;
),
- TP_printk("%s flag=%d", __get_str(name),
- (int)__entry->flag)
+ TP_printk("%s flag=%d", __get_str(name), __entry->flag)
);
DEFINE_EVENT(regmap_bool, regmap_cache_only,
@@ -163,7 +196,6 @@ DEFINE_EVENT(regmap_bool, regmap_cache_only,
TP_PROTO(struct regmap *map, bool flag),
TP_ARGS(map, flag)
-
);
DEFINE_EVENT(regmap_bool, regmap_cache_bypass,
@@ -171,7 +203,6 @@ DEFINE_EVENT(regmap_bool, regmap_cache_bypass,
TP_PROTO(struct regmap *map, bool flag),
TP_ARGS(map, flag)
-
);
DECLARE_EVENT_CLASS(regmap_async,
@@ -185,7 +216,7 @@ DECLARE_EVENT_CLASS(regmap_async,
),
TP_fast_assign(
- __assign_str(name, regmap_name(map));
+ __assign_str(name);
),
TP_printk("%s", __get_str(name))
@@ -203,7 +234,6 @@ DEFINE_EVENT(regmap_async, regmap_async_io_complete,
TP_PROTO(struct regmap *map),
TP_ARGS(map)
-
);
DEFINE_EVENT(regmap_async, regmap_async_complete_start,
@@ -211,7 +241,6 @@ DEFINE_EVENT(regmap_async, regmap_async_complete_start,
TP_PROTO(struct regmap *map),
TP_ARGS(map)
-
);
DEFINE_EVENT(regmap_async, regmap_async_complete_done,
@@ -219,7 +248,6 @@ DEFINE_EVENT(regmap_async, regmap_async_complete_done,
TP_PROTO(struct regmap *map),
TP_ARGS(map)
-
);
TRACE_EVENT(regcache_drop_region,
@@ -236,13 +264,12 @@ TRACE_EVENT(regcache_drop_region,
),
TP_fast_assign(
- __assign_str(name, regmap_name(map));
+ __assign_str(name);
__entry->from = from;
__entry->to = to;
),
- TP_printk("%s %u-%u", __get_str(name), (unsigned int)__entry->from,
- (unsigned int)__entry->to)
+ TP_printk("%s %u-%u", __get_str(name), __entry->from, __entry->to)
);
#endif /* _TRACE_REGMAP_H */
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 22130b5f789d..282c38aece0d 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -7,6 +7,7 @@
#include <linux/sysfs.h>
#include <linux/init.h>
+#include <linux/of.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/idr.h>
@@ -27,9 +28,10 @@ struct soc_device {
int soc_dev_num;
};
-static struct bus_type soc_bus_type = {
+static const struct bus_type soc_bus_type = {
.name = "soc",
};
+static bool soc_bus_registered;
static DEVICE_ATTR(machine, 0444, soc_info_show, NULL);
static DEVICE_ATTR(family, 0444, soc_info_show, NULL);
@@ -104,11 +106,23 @@ static void soc_release(struct device *dev)
{
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
- ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
+ ida_free(&soc_ida, soc_dev->soc_dev_num);
kfree(soc_dev->dev.groups);
kfree(soc_dev);
}
+static void soc_device_get_machine(struct soc_device_attribute *soc_dev_attr)
+{
+ struct device_node *np;
+
+ if (soc_dev_attr->machine)
+ return;
+
+ np = of_find_node_by_path("/");
+ of_property_read_string(np, "model", &soc_dev_attr->machine);
+ of_node_put(np);
+}
+
static struct soc_device_attribute *early_soc_dev_attr;
struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
@@ -117,7 +131,9 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
const struct attribute_group **soc_attr_groups;
int ret;
- if (!soc_bus_type.p) {
+ soc_device_get_machine(soc_dev_attr);
+
+ if (!soc_bus_registered) {
if (early_soc_dev_attr)
return ERR_PTR(-EBUSY);
early_soc_dev_attr = soc_dev_attr;
@@ -139,7 +155,7 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
soc_attr_groups[1] = soc_dev_attr->custom_attr_group;
/* Fetch a unique (reclaimable) SOC ID. */
- ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL);
+ ret = ida_alloc(&soc_ida, GFP_KERNEL);
if (ret < 0)
goto out3;
soc_dev->soc_dev_num = ret;
@@ -183,6 +199,7 @@ static int __init soc_bus_register(void)
ret = bus_register(&soc_bus_type);
if (ret)
return ret;
+ soc_bus_registered = true;
if (early_soc_dev_attr)
return PTR_ERR(soc_device_register(early_soc_dev_attr));
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 0a482212c7e8..16a8301c25d6 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -6,10 +6,21 @@
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/
+#include <linux/container_of.h>
#include <linux/device.h>
-#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/kstrtox.h>
+#include <linux/list.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
#include "base.h"
@@ -518,20 +529,35 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
if (prop->is_inline)
return -EINVAL;
- if (index * sizeof(*ref) >= prop->length)
+ if ((index + 1) * sizeof(*ref) > prop->length)
return -ENOENT;
ref_array = prop->pointer;
ref = &ref_array[index];
- refnode = software_node_fwnode(ref->node);
+ /*
+ * A software node can reference other software nodes or firmware
+ * nodes (which are the abstraction layer sitting on top of them).
+ * This is done to ensure we can create references to static software
+ * nodes before they're registered with the firmware node framework.
+ * At the time the reference is being resolved, we expect the swnodes
+ * in question to already have been registered and to be backed by
+ * a firmware node. This is why we use the fwnode API below to read the
+ * relevant properties and bump the reference count.
+ */
+
+ if (ref->swnode)
+ refnode = software_node_fwnode(ref->swnode);
+ else if (ref->fwnode)
+ refnode = ref->fwnode;
+ else
+ return -EINVAL;
+
if (!refnode)
return -ENOENT;
if (nargs_prop) {
- error = property_entry_read_int_array(ref->node->properties,
- nargs_prop, sizeof(u32),
- &nargs_prop_val, 1);
+ error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val);
if (error)
return error;
@@ -541,7 +567,10 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
if (nargs > NR_FWNODE_REFERENCE_ARGS)
return -EINVAL;
- args->fwnode = software_node_get(refnode);
+ if (!args)
+ return 0;
+
+ args->fwnode = fwnode_handle_get(refnode);
args->nargs = nargs;
for (i = 0; i < nargs; i++)
@@ -621,7 +650,10 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
ref = prop->pointer;
- return software_node_get(software_node_fwnode(ref[0].node));
+ if (!ref->swnode)
+ return NULL;
+
+ return software_node_get(software_node_fwnode(ref->swnode));
}
static struct fwnode_handle *
@@ -663,6 +695,7 @@ static const struct fwnode_operations software_node_ops = {
.get = software_node_get,
.put = software_node_put,
.property_present = software_node_property_present,
+ .property_read_bool = software_node_property_present,
.property_read_int_array = software_node_read_int_array,
.property_read_string_array = software_node_read_string_array,
.get_name = software_node_get_name,
@@ -747,10 +780,10 @@ static void software_node_release(struct kobject *kobj)
struct swnode *swnode = kobj_to_swnode(kobj);
if (swnode->parent) {
- ida_simple_remove(&swnode->parent->child_ids, swnode->id);
+ ida_free(&swnode->parent->child_ids, swnode->id);
list_del(&swnode->entry);
} else {
- ida_simple_remove(&swnode_root_ids, swnode->id);
+ ida_free(&swnode_root_ids, swnode->id);
}
if (swnode->allocated)
@@ -760,7 +793,7 @@ static void software_node_release(struct kobject *kobj)
kfree(swnode);
}
-static struct kobj_type software_node_type = {
+static const struct kobj_type software_node_type = {
.release = software_node_release,
.sysfs_ops = &kobj_sysfs_ops,
};
@@ -776,8 +809,8 @@ swnode_register(const struct software_node *node, struct swnode *parent,
if (!swnode)
return ERR_PTR(-ENOMEM);
- ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
- 0, 0, GFP_KERNEL);
+ ret = ida_alloc(parent ? &parent->child_ids : &swnode_root_ids,
+ GFP_KERNEL);
if (ret < 0) {
kfree(swnode);
return ERR_PTR(ret);
@@ -820,67 +853,6 @@ swnode_register(const struct software_node *node, struct swnode *parent,
}
/**
- * software_node_register_nodes - Register an array of software nodes
- * @nodes: Zero terminated array of software nodes to be registered
- *
- * Register multiple software nodes at once. If any node in the array
- * has its .parent pointer set (which can only be to another software_node),
- * then its parent **must** have been registered before it is; either outside
- * of this function or by ordering the array such that parent comes before
- * child.
- */
-int software_node_register_nodes(const struct software_node *nodes)
-{
- int ret;
- int i;
-
- for (i = 0; nodes[i].name; i++) {
- const struct software_node *parent = nodes[i].parent;
-
- if (parent && !software_node_to_swnode(parent)) {
- ret = -EINVAL;
- goto err_unregister_nodes;
- }
-
- ret = software_node_register(&nodes[i]);
- if (ret)
- goto err_unregister_nodes;
- }
-
- return 0;
-
-err_unregister_nodes:
- software_node_unregister_nodes(nodes);
- return ret;
-}
-EXPORT_SYMBOL_GPL(software_node_register_nodes);
-
-/**
- * software_node_unregister_nodes - Unregister an array of software nodes
- * @nodes: Zero terminated array of software nodes to be unregistered
- *
- * Unregister multiple software nodes at once. If parent pointers are set up
- * in any of the software nodes then the array **must** be ordered such that
- * parents come before their children.
- *
- * NOTE: If you are uncertain whether the array is ordered such that
- * parents will be unregistered before their children, it is wiser to
- * remove the nodes individually, in the correct order (child before
- * parent).
- */
-void software_node_unregister_nodes(const struct software_node *nodes)
-{
- unsigned int i = 0;
-
- while (nodes[i].name)
- i++;
-
- while (i--)
- software_node_unregister(&nodes[i]);
-}
-EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
-
-/**
* software_node_register_node_group - Register a group of software nodes
* @node_group: NULL terminated array of software node pointers to be registered
*
@@ -890,7 +862,7 @@ EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
* of this function or by ordering the array such that parent comes before
* child.
*/
-int software_node_register_node_group(const struct software_node **node_group)
+int software_node_register_node_group(const struct software_node * const *node_group)
{
unsigned int i;
int ret;
@@ -923,8 +895,7 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group);
* remove the nodes individually, in the correct order (child before
* parent).
*/
-void software_node_unregister_node_group(
- const struct software_node **node_group)
+void software_node_unregister_node_group(const struct software_node * const *node_group)
{
unsigned int i = 0;
@@ -1126,6 +1097,7 @@ void software_node_notify(struct device *dev)
if (!swnode)
return;
+ kobject_get(&swnode->kobj);
ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node");
if (ret)
return;
@@ -1135,8 +1107,6 @@ void software_node_notify(struct device *dev)
sysfs_remove_link(&dev->kobj, "software_node");
return;
}
-
- kobject_get(&swnode->kobj);
}
void software_node_notify_remove(struct device *dev)
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index 13db1f78d2ce..483adb796654 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -11,32 +11,32 @@
#include <linux/suspend.h>
#include <trace/events/power.h>
-static LIST_HEAD(syscore_ops_list);
-static DEFINE_MUTEX(syscore_ops_lock);
+static LIST_HEAD(syscore_list);
+static DEFINE_MUTEX(syscore_lock);
/**
- * register_syscore_ops - Register a set of system core operations.
- * @ops: System core operations to register.
+ * register_syscore - Register a set of system core operations.
+ * @syscore: System core operations to register.
*/
-void register_syscore_ops(struct syscore_ops *ops)
+void register_syscore(struct syscore *syscore)
{
- mutex_lock(&syscore_ops_lock);
- list_add_tail(&ops->node, &syscore_ops_list);
- mutex_unlock(&syscore_ops_lock);
+ mutex_lock(&syscore_lock);
+ list_add_tail(&syscore->node, &syscore_list);
+ mutex_unlock(&syscore_lock);
}
-EXPORT_SYMBOL_GPL(register_syscore_ops);
+EXPORT_SYMBOL_GPL(register_syscore);
/**
- * unregister_syscore_ops - Unregister a set of system core operations.
- * @ops: System core operations to unregister.
+ * unregister_syscore - Unregister a set of system core operations.
+ * @syscore: System core operations to unregister.
*/
-void unregister_syscore_ops(struct syscore_ops *ops)
+void unregister_syscore(struct syscore *syscore)
{
- mutex_lock(&syscore_ops_lock);
- list_del(&ops->node);
- mutex_unlock(&syscore_ops_lock);
+ mutex_lock(&syscore_lock);
+ list_del(&syscore->node);
+ mutex_unlock(&syscore_lock);
}
-EXPORT_SYMBOL_GPL(unregister_syscore_ops);
+EXPORT_SYMBOL_GPL(unregister_syscore);
#ifdef CONFIG_PM_SLEEP
/**
@@ -46,7 +46,7 @@ EXPORT_SYMBOL_GPL(unregister_syscore_ops);
*/
int syscore_suspend(void)
{
- struct syscore_ops *ops;
+ struct syscore *syscore;
int ret = 0;
trace_suspend_resume(TPS("syscore_suspend"), 0, true);
@@ -59,25 +59,27 @@ int syscore_suspend(void)
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled before system core suspend.\n");
- list_for_each_entry_reverse(ops, &syscore_ops_list, node)
- if (ops->suspend) {
- pm_pr_dbg("Calling %pS\n", ops->suspend);
- ret = ops->suspend();
+ list_for_each_entry_reverse(syscore, &syscore_list, node)
+ if (syscore->ops->suspend) {
+ pm_pr_dbg("Calling %pS\n", syscore->ops->suspend);
+ ret = syscore->ops->suspend(syscore->data);
if (ret)
goto err_out;
WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled after %pS\n", ops->suspend);
+ "Interrupts enabled after %pS\n",
+ syscore->ops->suspend);
}
trace_suspend_resume(TPS("syscore_suspend"), 0, false);
return 0;
err_out:
- pr_err("PM: System core suspend callback %pS failed.\n", ops->suspend);
+ pr_err("PM: System core suspend callback %pS failed.\n",
+ syscore->ops->suspend);
- list_for_each_entry_continue(ops, &syscore_ops_list, node)
- if (ops->resume)
- ops->resume();
+ list_for_each_entry_continue(syscore, &syscore_list, node)
+ if (syscore->ops->resume)
+ syscore->ops->resume(syscore->data);
return ret;
}
@@ -90,18 +92,19 @@ EXPORT_SYMBOL_GPL(syscore_suspend);
*/
void syscore_resume(void)
{
- struct syscore_ops *ops;
+ struct syscore *syscore;
trace_suspend_resume(TPS("syscore_resume"), 0, true);
WARN_ONCE(!irqs_disabled(),
"Interrupts enabled before system core resume.\n");
- list_for_each_entry(ops, &syscore_ops_list, node)
- if (ops->resume) {
- pm_pr_dbg("Calling %pS\n", ops->resume);
- ops->resume();
+ list_for_each_entry(syscore, &syscore_list, node)
+ if (syscore->ops->resume) {
+ pm_pr_dbg("Calling %pS\n", syscore->ops->resume);
+ syscore->ops->resume(syscore->data);
WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled after %pS\n", ops->resume);
+ "Interrupts enabled after %pS\n",
+ syscore->ops->resume);
}
trace_suspend_resume(TPS("syscore_resume"), 0, false);
}
@@ -113,16 +116,17 @@ EXPORT_SYMBOL_GPL(syscore_resume);
*/
void syscore_shutdown(void)
{
- struct syscore_ops *ops;
+ struct syscore *syscore;
- mutex_lock(&syscore_ops_lock);
+ mutex_lock(&syscore_lock);
- list_for_each_entry_reverse(ops, &syscore_ops_list, node)
- if (ops->shutdown) {
+ list_for_each_entry_reverse(syscore, &syscore_list, node)
+ if (syscore->ops->shutdown) {
if (initcall_debug)
- pr_info("PM: Calling %pS\n", ops->shutdown);
- ops->shutdown();
+ pr_info("PM: Calling %pS\n",
+ syscore->ops->shutdown);
+ syscore->ops->shutdown(syscore->data);
}
- mutex_unlock(&syscore_ops_lock);
+ mutex_unlock(&syscore_lock);
}
diff --git a/drivers/base/test/.kunitconfig b/drivers/base/test/.kunitconfig
new file mode 100644
index 000000000000..473923f0998b
--- /dev/null
+++ b/drivers/base/test/.kunitconfig
@@ -0,0 +1,2 @@
+CONFIG_KUNIT=y
+CONFIG_DM_KUNIT_TEST=y
diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig
index 2f3fa31a948e..2756870615cc 100644
--- a/drivers/base/test/Kconfig
+++ b/drivers/base/test/Kconfig
@@ -8,7 +8,13 @@ config TEST_ASYNC_DRIVER_PROBE
The module name will be test_async_driver_probe.ko
If unsure say N.
+
+config DM_KUNIT_TEST
+ tristate "KUnit Tests for the device model" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+
config DRIVER_PE_KUNIT_TEST
- bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
- depends on KUNIT=y
+ tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
+ depends on KUNIT
default KUNIT_ALL_TESTS
diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile
index 7f76fee6f989..e321dfc7e922 100644
--- a/drivers/base/test/Makefile
+++ b/drivers/base/test/Makefile
@@ -1,5 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o
+obj-$(CONFIG_DM_KUNIT_TEST) += root-device-test.o
+obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
+
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/base/test/platform-device-test.c b/drivers/base/test/platform-device-test.c
new file mode 100644
index 000000000000..6355a2231b74
--- /dev/null
+++ b/drivers/base/test/platform-device-test.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <kunit/platform_device.h>
+#include <kunit/resource.h>
+
+#include <linux/device.h>
+#include <linux/device/bus.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#define DEVICE_NAME "test"
+
+struct test_priv {
+ bool probe_done;
+ bool release_done;
+ wait_queue_head_t probe_wq;
+ wait_queue_head_t release_wq;
+ struct device *dev;
+};
+
+static int platform_device_devm_init(struct kunit *test)
+{
+ struct test_priv *priv;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+ init_waitqueue_head(&priv->probe_wq);
+ init_waitqueue_head(&priv->release_wq);
+
+ test->priv = priv;
+
+ return 0;
+}
+
+static void devm_device_action(void *ptr)
+{
+ struct test_priv *priv = ptr;
+
+ priv->release_done = true;
+ wake_up_interruptible(&priv->release_wq);
+}
+
+static void devm_put_device_action(void *ptr)
+{
+ struct test_priv *priv = ptr;
+
+ put_device(priv->dev);
+ priv->release_done = true;
+ wake_up_interruptible(&priv->release_wq);
+}
+
+#define RELEASE_TIMEOUT_MS 100
+
+/*
+ * Tests that a platform bus, non-probed device will run its
+ * device-managed actions when unregistered.
+ */
+static void platform_device_devm_register_unregister_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct test_priv *priv = test->priv;
+ int ret;
+
+ pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ ret = platform_device_add(pdev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ priv->dev = &pdev->dev;
+
+ ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ platform_device_unregister(pdev);
+
+ ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_EXPECT_GT(test, ret, 0);
+}
+
+/*
+ * Tests that a platform bus, non-probed device will run its
+ * device-managed actions when unregistered, even if someone still holds
+ * a reference to it.
+ */
+static void platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct test_priv *priv = test->priv;
+ int ret;
+
+ pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ ret = platform_device_add(pdev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ priv->dev = &pdev->dev;
+
+ get_device(priv->dev);
+
+ ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ platform_device_unregister(pdev);
+
+ ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_EXPECT_GT(test, ret, 0);
+}
+
+static int fake_probe(struct platform_device *pdev)
+{
+ struct test_priv *priv = platform_get_drvdata(pdev);
+
+ priv->probe_done = true;
+ wake_up_interruptible(&priv->probe_wq);
+
+ return 0;
+}
+
+static struct platform_driver fake_driver = {
+ .probe = fake_probe,
+ .driver = {
+ .name = DEVICE_NAME,
+ },
+};
+
+/*
+ * Tests that a platform bus, probed device will run its device-managed
+ * actions when unregistered.
+ */
+static void probed_platform_device_devm_register_unregister_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct test_priv *priv = test->priv;
+ int ret;
+
+ ret = platform_driver_register(&fake_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ priv->dev = &pdev->dev;
+ platform_set_drvdata(pdev, priv);
+
+ ret = platform_device_add(pdev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ platform_device_unregister(pdev);
+
+ ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_EXPECT_GT(test, ret, 0);
+
+ platform_driver_unregister(&fake_driver);
+}
+
+/*
+ * Tests that a platform bus, probed device will run its device-managed
+ * actions when unregistered, even if someone still holds a reference to
+ * it.
+ */
+static void probed_platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct test_priv *priv = test->priv;
+ int ret;
+
+ ret = platform_driver_register(&fake_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ priv->dev = &pdev->dev;
+ platform_set_drvdata(pdev, priv);
+
+ ret = platform_device_add(pdev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ get_device(priv->dev);
+
+ ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ platform_device_unregister(pdev);
+
+ ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_EXPECT_GT(test, ret, 0);
+
+ platform_driver_unregister(&fake_driver);
+}
+
+static struct kunit_case platform_device_devm_tests[] = {
+ KUNIT_CASE(platform_device_devm_register_unregister_test),
+ KUNIT_CASE(platform_device_devm_register_get_unregister_with_devm_test),
+ KUNIT_CASE(probed_platform_device_devm_register_unregister_test),
+ KUNIT_CASE(probed_platform_device_devm_register_get_unregister_with_devm_test),
+ {}
+};
+
+static struct kunit_suite platform_device_devm_test_suite = {
+ .name = "platform-device-devm",
+ .init = platform_device_devm_init,
+ .test_cases = platform_device_devm_tests,
+};
+
+static void platform_device_find_by_null_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = kunit_platform_device_alloc(test, DEVICE_NAME, PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ ret = kunit_platform_device_add(test, pdev);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_PTR_EQ(test, of_find_device_by_node(NULL), NULL);
+
+ KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_of_node(&platform_bus_type, NULL), NULL);
+ KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_fwnode(&platform_bus_type, NULL), NULL);
+ KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_acpi_dev(&platform_bus_type, NULL), NULL);
+
+ KUNIT_EXPECT_FALSE(test, device_match_of_node(&pdev->dev, NULL));
+ KUNIT_EXPECT_FALSE(test, device_match_fwnode(&pdev->dev, NULL));
+ KUNIT_EXPECT_FALSE(test, device_match_acpi_dev(&pdev->dev, NULL));
+ KUNIT_EXPECT_FALSE(test, device_match_acpi_handle(&pdev->dev, NULL));
+}
+
+static struct kunit_case platform_device_match_tests[] = {
+ KUNIT_CASE(platform_device_find_by_null_test),
+ {}
+};
+
+static struct kunit_suite platform_device_match_test_suite = {
+ .name = "platform-device-match",
+ .test_cases = platform_device_match_tests,
+};
+
+kunit_test_suites(
+ &platform_device_devm_test_suite,
+ &platform_device_match_test_suite,
+);
+
+MODULE_DESCRIPTION("Test module for platform devices");
+MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c
index 6071d5bc128c..a8657eb06f94 100644
--- a/drivers/base/test/property-entry-test.c
+++ b/drivers/base/test/property-entry-test.c
@@ -405,20 +405,18 @@ static void pe_test_move_inline_str(struct kunit *test)
/* Handling of reference properties */
static void pe_test_reference(struct kunit *test)
{
- static const struct software_node nodes[] = {
- { .name = "1", },
- { .name = "2", },
- { }
- };
+ static const struct software_node node1 = { .name = "1" };
+ static const struct software_node node2 = { .name = "2" };
+ static const struct software_node *group[] = { &node1, &node2, NULL };
static const struct software_node_ref_args refs[] = {
- SOFTWARE_NODE_REFERENCE(&nodes[0]),
- SOFTWARE_NODE_REFERENCE(&nodes[1], 3, 4),
+ SOFTWARE_NODE_REFERENCE(&node1),
+ SOFTWARE_NODE_REFERENCE(&node2, 3, 4),
};
const struct property_entry entries[] = {
- PROPERTY_ENTRY_REF("ref-1", &nodes[0]),
- PROPERTY_ENTRY_REF("ref-2", &nodes[1], 1, 2),
+ PROPERTY_ENTRY_REF("ref-1", &node1),
+ PROPERTY_ENTRY_REF("ref-2", &node2, 1, 2),
PROPERTY_ENTRY_REF_ARRAY("ref-3", refs),
{ }
};
@@ -427,7 +425,7 @@ static void pe_test_reference(struct kunit *test)
struct fwnode_reference_args ref;
int error;
- error = software_node_register_nodes(nodes);
+ error = software_node_register_node_group(group);
KUNIT_ASSERT_EQ(test, error, 0);
node = fwnode_create_software_node(entries, NULL);
@@ -436,7 +434,7 @@ static void pe_test_reference(struct kunit *test)
error = fwnode_property_get_reference_args(node, "ref-1", NULL,
0, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
- KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[0]);
+ KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node1);
KUNIT_EXPECT_EQ(test, ref.nargs, 0U);
/* wrong index */
@@ -447,7 +445,7 @@ static void pe_test_reference(struct kunit *test)
error = fwnode_property_get_reference_args(node, "ref-2", NULL,
1, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
- KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]);
+ KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2);
KUNIT_EXPECT_EQ(test, ref.nargs, 1U);
KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU);
@@ -455,7 +453,7 @@ static void pe_test_reference(struct kunit *test)
error = fwnode_property_get_reference_args(node, "ref-2", NULL,
3, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
- KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]);
+ KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2);
KUNIT_EXPECT_EQ(test, ref.nargs, 3U);
KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU);
KUNIT_EXPECT_EQ(test, ref.args[1], 2LLU);
@@ -470,14 +468,14 @@ static void pe_test_reference(struct kunit *test)
error = fwnode_property_get_reference_args(node, "ref-3", NULL,
0, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
- KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[0]);
+ KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node1);
KUNIT_EXPECT_EQ(test, ref.nargs, 0U);
/* second reference in the array */
error = fwnode_property_get_reference_args(node, "ref-3", NULL,
2, 1, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
- KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &nodes[1]);
+ KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2);
KUNIT_EXPECT_EQ(test, ref.nargs, 2U);
KUNIT_EXPECT_EQ(test, ref.args[0], 3LLU);
KUNIT_EXPECT_EQ(test, ref.args[1], 4LLU);
@@ -488,7 +486,7 @@ static void pe_test_reference(struct kunit *test)
KUNIT_EXPECT_NE(test, error, 0);
fwnode_remove_software_node(node);
- software_node_unregister_nodes(nodes);
+ software_node_unregister_node_group(group);
}
static struct kunit_case property_entry_test_cases[] = {
@@ -508,3 +506,7 @@ static struct kunit_suite property_entry_test_suite = {
};
kunit_test_suite(property_entry_test_suite);
+
+MODULE_DESCRIPTION("Test module for the property entry API");
+MODULE_AUTHOR("Dmitry Torokhov <dtor@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/test/root-device-test.c b/drivers/base/test/root-device-test.c
new file mode 100644
index 000000000000..9aea23c9123e
--- /dev/null
+++ b/drivers/base/test/root-device-test.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2023 Maxime Ripard <mripard@kernel.org>
+
+#include <kunit/resource.h>
+
+#include <linux/device.h>
+
+#define DEVICE_NAME "test"
+
+struct test_priv {
+ bool probe_done;
+ bool release_done;
+ wait_queue_head_t release_wq;
+ struct device *dev;
+};
+
+static int root_device_devm_init(struct kunit *test)
+{
+ struct test_priv *priv;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+ init_waitqueue_head(&priv->release_wq);
+
+ test->priv = priv;
+
+ return 0;
+}
+
+static void devm_device_action(void *ptr)
+{
+ struct test_priv *priv = ptr;
+
+ priv->release_done = true;
+ wake_up_interruptible(&priv->release_wq);
+}
+
+#define RELEASE_TIMEOUT_MS 100
+
+/*
+ * Tests that a bus-less, non-probed device will run its device-managed
+ * actions when unregistered.
+ */
+static void root_device_devm_register_unregister_test(struct kunit *test)
+{
+ struct test_priv *priv = test->priv;
+ int ret;
+
+ priv->dev = root_device_register(DEVICE_NAME);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
+
+ ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ root_device_unregister(priv->dev);
+
+ ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_EXPECT_GT(test, ret, 0);
+}
+
+static void devm_put_device_action(void *ptr)
+{
+ struct test_priv *priv = ptr;
+
+ put_device(priv->dev);
+ priv->release_done = true;
+ wake_up_interruptible(&priv->release_wq);
+}
+
+/*
+ * Tests that a bus-less, non-probed device will run its device-managed
+ * actions when unregistered, even if someone still holds a reference to
+ * it.
+ */
+static void root_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
+{
+ struct test_priv *priv = test->priv;
+ int ret;
+
+ priv->dev = root_device_register(DEVICE_NAME);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
+
+ get_device(priv->dev);
+
+ ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ root_device_unregister(priv->dev);
+
+ ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
+ msecs_to_jiffies(RELEASE_TIMEOUT_MS));
+ KUNIT_EXPECT_GT(test, ret, 0);
+}
+
+static struct kunit_case root_device_devm_tests[] = {
+ KUNIT_CASE(root_device_devm_register_unregister_test),
+ KUNIT_CASE(root_device_devm_register_get_unregister_with_devm_test),
+ {}
+};
+
+static struct kunit_suite root_device_devm_test_suite = {
+ .name = "root-device-devm",
+ .init = root_device_devm_init,
+ .test_cases = root_device_devm_tests,
+};
+
+kunit_test_suite(root_device_devm_test_suite);
+
+MODULE_DESCRIPTION("Test module for root devices");
+MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/test/test_async_driver_probe.c b/drivers/base/test/test_async_driver_probe.c
index 4d1976ca5072..3465800baa6c 100644
--- a/drivers/base/test/test_async_driver_probe.c
+++ b/drivers/base/test/test_async_driver_probe.c
@@ -84,7 +84,7 @@ test_platform_device_register_node(char *name, int id, int nid)
pdev = platform_device_alloc(name, id);
if (!pdev)
- return NULL;
+ return ERR_PTR(-ENOMEM);
if (nid != NUMA_NO_NODE)
set_dev_node(&pdev->dev, nid);
@@ -145,7 +145,7 @@ static int __init test_async_probe_init(void)
calltime = ktime_get();
for_each_online_cpu(cpu) {
nid = cpu_to_node(cpu);
- pdev = &sync_dev[sync_id];
+ pdev = &async_dev[async_id];
*pdev = test_platform_device_register_node("test_async_driver",
async_id,
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index 89f98be5c5b9..c890e2a5b428 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -23,23 +23,39 @@ static ssize_t name##_show(struct device *dev, \
#define define_siblings_read_func(name, mask) \
static ssize_t name##_read(struct file *file, struct kobject *kobj, \
- struct bin_attribute *attr, char *buf, \
+ const struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
{ \
struct device *dev = kobj_to_dev(kobj); \
+ cpumask_var_t mask; \
+ ssize_t n; \
\
- return cpumap_print_bitmask_to_buf(buf, topology_##mask(dev->id), \
- off, count); \
+ if (!alloc_cpumask_var(&mask, GFP_KERNEL)) \
+ return -ENOMEM; \
+ \
+ cpumask_copy(mask, topology_##mask(dev->id)); \
+ n = cpumap_print_bitmask_to_buf(buf, mask, off, count); \
+ free_cpumask_var(mask); \
+ \
+ return n; \
} \
\
static ssize_t name##_list_read(struct file *file, struct kobject *kobj, \
- struct bin_attribute *attr, char *buf, \
+ const struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
{ \
struct device *dev = kobj_to_dev(kobj); \
+ cpumask_var_t mask; \
+ ssize_t n; \
+ \
+ if (!alloc_cpumask_var(&mask, GFP_KERNEL)) \
+ return -ENOMEM; \
+ \
+ cpumask_copy(mask, topology_##mask(dev->id)); \
+ n = cpumap_print_list_to_buf(buf, mask, off, count); \
+ free_cpumask_var(mask); \
\
- return cpumap_print_list_to_buf(buf, topology_##mask(dev->id), \
- off, count); \
+ return n; \
}
define_id_show_func(physical_package_id, "%d");
@@ -62,50 +78,50 @@ define_id_show_func(ppin, "0x%llx");
static DEVICE_ATTR_ADMIN_RO(ppin);
define_siblings_read_func(thread_siblings, sibling_cpumask);
-static BIN_ATTR_RO(thread_siblings, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(thread_siblings_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(thread_siblings, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(thread_siblings_list, CPULIST_FILE_MAX_BYTES);
define_siblings_read_func(core_cpus, sibling_cpumask);
-static BIN_ATTR_RO(core_cpus, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(core_cpus_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(core_cpus, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(core_cpus_list, CPULIST_FILE_MAX_BYTES);
define_siblings_read_func(core_siblings, core_cpumask);
-static BIN_ATTR_RO(core_siblings, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(core_siblings_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(core_siblings, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(core_siblings_list, CPULIST_FILE_MAX_BYTES);
#ifdef TOPOLOGY_CLUSTER_SYSFS
define_siblings_read_func(cluster_cpus, cluster_cpumask);
-static BIN_ATTR_RO(cluster_cpus, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(cluster_cpus_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(cluster_cpus, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(cluster_cpus_list, CPULIST_FILE_MAX_BYTES);
#endif
#ifdef TOPOLOGY_DIE_SYSFS
define_siblings_read_func(die_cpus, die_cpumask);
-static BIN_ATTR_RO(die_cpus, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(die_cpus_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(die_cpus, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(die_cpus_list, CPULIST_FILE_MAX_BYTES);
#endif
define_siblings_read_func(package_cpus, core_cpumask);
-static BIN_ATTR_RO(package_cpus, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(package_cpus_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(package_cpus, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(package_cpus_list, CPULIST_FILE_MAX_BYTES);
#ifdef TOPOLOGY_BOOK_SYSFS
define_id_show_func(book_id, "%d");
static DEVICE_ATTR_RO(book_id);
define_siblings_read_func(book_siblings, book_cpumask);
-static BIN_ATTR_RO(book_siblings, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(book_siblings_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(book_siblings, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(book_siblings_list, CPULIST_FILE_MAX_BYTES);
#endif
#ifdef TOPOLOGY_DRAWER_SYSFS
define_id_show_func(drawer_id, "%d");
static DEVICE_ATTR_RO(drawer_id);
define_siblings_read_func(drawer_siblings, drawer_cpumask);
-static BIN_ATTR_RO(drawer_siblings, CPUMAP_FILE_MAX_BYTES);
-static BIN_ATTR_RO(drawer_siblings_list, CPULIST_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(drawer_siblings, CPUMAP_FILE_MAX_BYTES);
+static const BIN_ATTR_RO(drawer_siblings_list, CPULIST_FILE_MAX_BYTES);
#endif
-static struct bin_attribute *bin_attrs[] = {
+static const struct bin_attribute *const bin_attrs[] = {
&bin_attr_core_cpus,
&bin_attr_core_cpus_list,
&bin_attr_thread_siblings,
@@ -192,3 +208,55 @@ static int __init topology_sysfs_init(void)
}
device_initcall(topology_sysfs_init);
+
+DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
+EXPORT_PER_CPU_SYMBOL_GPL(cpu_scale);
+
+void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity)
+{
+ per_cpu(cpu_scale, cpu) = capacity;
+}
+
+static ssize_t cpu_capacity_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+
+ return sysfs_emit(buf, "%lu\n", topology_get_cpu_scale(cpu->dev.id));
+}
+
+static DEVICE_ATTR_RO(cpu_capacity);
+
+static int cpu_capacity_sysctl_add(unsigned int cpu)
+{
+ struct device *cpu_dev = get_cpu_device(cpu);
+
+ if (!cpu_dev)
+ return -ENOENT;
+
+ device_create_file(cpu_dev, &dev_attr_cpu_capacity);
+
+ return 0;
+}
+
+static int cpu_capacity_sysctl_remove(unsigned int cpu)
+{
+ struct device *cpu_dev = get_cpu_device(cpu);
+
+ if (!cpu_dev)
+ return -ENOENT;
+
+ device_remove_file(cpu_dev, &dev_attr_cpu_capacity);
+
+ return 0;
+}
+
+static int register_cpu_capacity_sysctl(void)
+{
+ cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "topology/cpu-capacity",
+ cpu_capacity_sysctl_add, cpu_capacity_sysctl_remove);
+
+ return 0;
+}
+subsys_initcall(register_cpu_capacity_sysctl);
diff --git a/drivers/base/trace.h b/drivers/base/trace.h
index 3192e18f877e..3b83b13a57ff 100644
--- a/drivers/base/trace.h
+++ b/drivers/base/trace.h
@@ -24,18 +24,18 @@ DECLARE_EVENT_CLASS(devres,
__field(struct device *, dev)
__field(const char *, op)
__field(void *, node)
- __field(const char *, name)
+ __string(name, name)
__field(size_t, size)
),
TP_fast_assign(
- __assign_str(devname, dev_name(dev));
+ __assign_str(devname);
__entry->op = op;
__entry->node = node;
- __entry->name = name;
+ __assign_str(name);
__entry->size = size;
),
TP_printk("%s %3s %p %s (%zu bytes)", __get_str(devname),
- __entry->op, __entry->node, __entry->name, __entry->size)
+ __entry->op, __entry->node, __get_str(name), __entry->size)
);
DEFINE_EVENT(devres, devres_log,
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
index ccc86206e508..09ee2a1e35bb 100644
--- a/drivers/base/transport_class.c
+++ b/drivers/base/transport_class.c
@@ -155,12 +155,27 @@ static int transport_add_class_device(struct attribute_container *cont,
struct device *dev,
struct device *classdev)
{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
int error = attribute_container_add_class_device(classdev);
struct transport_container *tcont =
attribute_container_to_transport_container(cont);
- if (!error && tcont->statistics)
+ if (error)
+ goto err_remove;
+
+ if (tcont->statistics) {
error = sysfs_create_group(&classdev->kobj, tcont->statistics);
+ if (error)
+ goto err_del;
+ }
+
+ return 0;
+
+err_del:
+ attribute_container_class_device_del(classdev);
+err_remove:
+ if (tclass->remove)
+ tclass->remove(tcont, dev, classdev);
return error;
}
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
index fac8ff983aec..658c7e2ac8bf 100644
--- a/drivers/bcma/driver_gpio.c
+++ b/drivers/bcma/driver_gpio.c
@@ -26,12 +26,14 @@ static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
return !!bcma_chipco_gpio_in(cc, 1 << gpio);
}
-static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
- int value)
+static int bcma_gpio_set_value(struct gpio_chip *chip, unsigned int gpio,
+ int value)
{
struct bcma_drv_cc *cc = gpiochip_get_data(chip);
bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);
+
+ return 0;
}
static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
@@ -82,6 +84,7 @@ static void bcma_gpio_irq_unmask(struct irq_data *d)
int gpio = irqd_to_hwirq(d);
u32 val = bcma_chipco_gpio_in(cc, BIT(gpio));
+ gpiochip_enable_irq(gc, gpio);
bcma_chipco_gpio_polarity(cc, BIT(gpio), val);
bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio));
}
@@ -93,12 +96,15 @@ static void bcma_gpio_irq_mask(struct irq_data *d)
int gpio = irqd_to_hwirq(d);
bcma_chipco_gpio_intmask(cc, BIT(gpio), 0);
+ gpiochip_disable_irq(gc, gpio);
}
-static struct irq_chip bcma_gpio_irq_chip = {
+static const struct irq_chip bcma_gpio_irq_chip = {
.name = "BCMA-GPIO",
.irq_mask = bcma_gpio_irq_mask,
.irq_unmask = bcma_gpio_irq_unmask,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id)
@@ -115,7 +121,7 @@ static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
for_each_set_bit(gpio, &irqs, gc->ngpio)
- generic_handle_irq(irq_find_mapping(gc->irq.domain, gpio));
+ generic_handle_domain_irq_safe(gc->irq.domain, gpio);
bcma_chipco_gpio_polarity(cc, irqs, val & irqs);
return IRQ_HANDLED;
@@ -139,7 +145,7 @@ static int bcma_gpio_irq_init(struct bcma_drv_cc *cc)
bcma_chipco_gpio_intmask(cc, ~0, 0);
bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO);
- girq->chip = &bcma_gpio_irq_chip;
+ gpio_irq_chip_set_chip(girq, &bcma_gpio_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index 12aca34e8db0..9be0806eb033 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -30,7 +30,7 @@ enum bcma_boot_dev {
BCMA_BOOT_DEV_NAND,
};
-/* The 47162a0 hangs when reading MIPS DMP registers registers */
+/* The 47162a0 hangs when reading MIPS DMP registers */
static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
{
return dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM47162 &&
@@ -46,12 +46,6 @@ static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
dev->id.id == BCMA_CORE_USB20_HOST;
}
-static inline u32 mips_read32(struct bcma_drv_mips *mcore,
- u16 offset)
-{
- return bcma_read32(mcore->core, offset);
-}
-
static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
{
u32 flag;
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
index aa0581cda718..8540052d37c5 100644
--- a/drivers/bcma/driver_pci_host.c
+++ b/drivers/bcma/driver_pci_host.c
@@ -280,7 +280,7 @@ static u8 bcma_find_pci_capability(struct bcma_drv_pci *pc, unsigned int dev,
/* check for Header type 0 */
bcma_extpci_read_config(pc, dev, func, PCI_HEADER_TYPE, &byte_val,
sizeof(u8));
- if ((byte_val & 0x7F) != PCI_HEADER_TYPE_NORMAL)
+ if ((byte_val & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL)
return cap_ptr;
/* check if the capability pointer field exists */
@@ -334,7 +334,7 @@ static u8 bcma_find_pci_capability(struct bcma_drv_pci *pc, unsigned int dev,
}
/* If the root port is capable of returning Config Request
- * Retry Status (CRS) Completion Status to software then
+ * Retry Status (RRS) Completion Status to software then
* enable the feature.
*/
static void bcma_core_pci_enable_crs(struct bcma_drv_pci *pc)
@@ -348,10 +348,10 @@ static void bcma_core_pci_enable_crs(struct bcma_drv_pci *pc)
NULL);
root_cap = cap_ptr + PCI_EXP_RTCAP;
bcma_extpci_read_config(pc, 0, 0, root_cap, &val16, sizeof(u16));
- if (val16 & BCMA_CORE_PCI_RC_CRS_VISIBILITY) {
- /* Enable CRS software visibility */
+ if (val16 & BCMA_CORE_PCI_RC_RRS_VISIBILITY) {
+ /* Enable Configuration RRS Software Visibility */
root_ctrl = cap_ptr + PCI_EXP_RTCTL;
- val16 = PCI_EXP_RTCTL_CRSSVE;
+ val16 = PCI_EXP_RTCTL_RRS_SVE;
bcma_extpci_read_config(pc, 0, 0, root_ctrl, &val16,
sizeof(u16));
@@ -360,7 +360,7 @@ static void bcma_core_pci_enable_crs(struct bcma_drv_pci *pc)
* 100 ms wait time from the end of Reset. If the device is
* not done with its internal initialization, it must at
* least return a completion TLP, with a completion status
- * of "Configuration Request Retry Status (CRS)". The root
+ * of "Configuration Request Retry Status (RRS)". The root
* complex must complete the request to the host by returning
* a read-data value of 0001h for the Vendor ID field and
* all 1s for any additional bytes included in the request.
diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c
index 90d5bdc12e03..20b1816c570b 100644
--- a/drivers/bcma/host_soc.c
+++ b/drivers/bcma/host_soc.c
@@ -240,15 +240,13 @@ err_unmap_mmio:
return err;
}
-static int bcma_host_soc_remove(struct platform_device *pdev)
+static void bcma_host_soc_remove(struct platform_device *pdev)
{
struct bcma_bus *bus = platform_get_drvdata(pdev);
bcma_bus_unregister(bus);
iounmap(bus->mmio);
platform_set_drvdata(pdev, NULL);
-
- return 0;
}
static const struct of_device_id bcma_host_soc_of_match[] = {
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 44392b624b20..72f045e6ed51 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/of_device.h>
#include <linux/of_platform.h>
MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
@@ -25,10 +26,10 @@ static unsigned int bcma_bus_next_num;
/* bcma_buses_mutex locks the bcma_bus_next_num */
static DEFINE_MUTEX(bcma_buses_mutex);
-static int bcma_bus_match(struct device *dev, struct device_driver *drv);
+static int bcma_bus_match(struct device *dev, const struct device_driver *drv);
static int bcma_device_probe(struct device *dev);
static void bcma_device_remove(struct device *dev);
-static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env);
+static int bcma_device_uevent(const struct device *dev, struct kobj_uevent_env *env);
static ssize_t manuf_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -67,7 +68,7 @@ static struct attribute *bcma_device_attrs[] = {
};
ATTRIBUTE_GROUPS(bcma_device);
-static struct bus_type bcma_bus_type = {
+static const struct bus_type bcma_bus_type = {
.name = "bcma",
.match = bcma_bus_match,
.probe = bcma_device_probe,
@@ -140,17 +141,17 @@ static struct device_node *bcma_of_find_child_device(struct device *parent,
struct bcma_device *core)
{
struct device_node *node;
- u64 size;
- const __be32 *reg;
+ int ret;
if (!parent->of_node)
return NULL;
for_each_child_of_node(parent->of_node, node) {
- reg = of_get_address(node, 0, &size, NULL);
- if (!reg)
+ struct resource res;
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret)
continue;
- if (of_translate_address(node, reg) == core->addr)
+ if (res.start == core->addr)
return node;
}
return NULL;
@@ -293,6 +294,8 @@ static int bcma_register_devices(struct bcma_bus *bus)
int err;
list_for_each_entry(core, &bus->cores, list) {
+ struct device_node *np;
+
/* We support that core ourselves */
switch (core->id.id) {
case BCMA_CORE_4706_CHIPCOMMON:
@@ -310,6 +313,10 @@ static int bcma_register_devices(struct bcma_bus *bus)
if (bcma_is_core_needed_early(core->id.id))
continue;
+ np = core->dev.of_node;
+ if (np && !of_device_is_available(np))
+ continue;
+
/* Only first GMAC core on BCM4706 is connected and working */
if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
core->core_unit > 0)
@@ -344,8 +351,10 @@ static int bcma_register_devices(struct bcma_bus *bus)
err = bcma_gpio_init(&bus->drv_cc);
if (err == -ENOTSUPP)
bcma_debug(bus, "GPIO driver not activated\n");
- else if (err)
+ else if (err) {
bcma_err(bus, "Error registering GPIO driver: %i\n", err);
+ return err;
+ }
if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
err = bcma_chipco_watchdog_register(&bus->drv_cc);
@@ -581,10 +590,10 @@ void bcma_driver_unregister(struct bcma_driver *drv)
}
EXPORT_SYMBOL_GPL(bcma_driver_unregister);
-static int bcma_bus_match(struct device *dev, struct device_driver *drv)
+static int bcma_bus_match(struct device *dev, const struct device_driver *drv)
{
struct bcma_device *core = container_of(dev, struct bcma_device, dev);
- struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv);
+ const struct bcma_driver *adrv = container_of_const(drv, struct bcma_driver, drv);
const struct bcma_device_id *cid = &core->id;
const struct bcma_device_id *did;
@@ -625,9 +634,9 @@ static void bcma_device_remove(struct device *dev)
put_device(dev);
}
-static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int bcma_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+ const struct bcma_device *core = container_of_const(dev, struct bcma_device, dev);
return add_uevent_var(env,
"MODALIAS=bcma:m%04Xid%04Xrev%02Xcl%02X",
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 3da01f173c63..e668ad7963fc 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -165,7 +165,7 @@ static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
return err;
revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
- if (revision != 8 && revision != 9 && revision != 10) {
+ if (revision < 8 || revision > 11) {
pr_err("Unsupported SPROM revision: %d\n", revision);
return -ENOENT;
}
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index db1b4b202646..77d694448990 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -17,6 +17,7 @@ menuconfig BLK_DEV
if BLK_DEV
source "drivers/block/null_blk/Kconfig"
+source "drivers/block/rnull/Kconfig"
config BLK_DEV_FD
tristate "Normal floppy disk support"
@@ -103,35 +104,6 @@ config GDROM
Most users will want to say "Y" here.
You can also build this as a module which will be called gdrom.
-config PARIDE
- tristate "Parallel port IDE device support"
- depends on PARPORT_PC
- help
- There are many external CD-ROM and disk devices that connect through
- your computer's parallel port. Most of them are actually IDE devices
- using a parallel port IDE adapter. This option enables the PARIDE
- subsystem which contains drivers for many of these external drives.
- Read <file:Documentation/admin-guide/blockdev/paride.rst> for more information.
-
- If you have said Y to the "Parallel-port support" configuration
- option, you may share a single port between your printer and other
- parallel port devices. Answer Y to build PARIDE support into your
- kernel, or M if you would like to build it as a loadable module. If
- your parallel port support is in a loadable module, you must build
- PARIDE as a module. If you built PARIDE support into your kernel,
- you may still build the individual protocol modules and high-level
- drivers as loadable modules. If you build this support as a module,
- it will be called paride.
-
- To use the PARIDE support, you must say Y or M here and also to at
- least one high-level driver (e.g. "Parallel port IDE disks",
- "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI disks" etc.) and
- to at least one protocol driver (e.g. "ATEN EH-100 protocol",
- "MicroSolutions backpack protocol", "DataStor Commuter protocol"
- etc.).
-
-source "drivers/block/paride/Kconfig"
-
source "drivers/block/mtip32xx/Kconfig"
source "drivers/block/zram/Kconfig"
@@ -159,7 +131,7 @@ config BLK_DEV_UBD_SYNC
kernel command line option. Alternatively, you can say Y here to
turn on synchronous operation by default for all block devices.
- If you're running a journalling file system (like reiserfs, for
+ If you're running a journalling file system (like xfs, for
example) in your virtual machine, you will want to say Y here. If
you care for the safety of the data in your virtual machine, Y is a
wise choice too. In all other cases (for example, if you're just
@@ -285,49 +257,6 @@ config BLK_DEV_RAM_SIZE
The default value is 4096 kilobytes. Only change this if you know
what you are doing.
-config CDROM_PKTCDVD
- tristate "Packet writing on CD/DVD media (DEPRECATED)"
- depends on !UML
- depends on SCSI
- select CDROM
- help
- Note: This driver is deprecated and will be removed from the
- kernel in the near future!
-
- If you have a CDROM/DVD drive that supports packet writing, say
- Y to include support. It should work with any MMC/Mt Fuji
- compliant ATAPI or SCSI drive, which is just about any newer
- DVD/CD writer.
-
- Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs
- is possible.
- DVD-RW disks must be in restricted overwrite mode.
-
- See the file <file:Documentation/cdrom/packet-writing.rst>
- for further information on the use of this driver.
-
- To compile this driver as a module, choose M here: the
- module will be called pktcdvd.
-
-config CDROM_PKTCDVD_BUFFERS
- int "Free buffers for data gathering"
- depends on CDROM_PKTCDVD
- default "8"
- help
- This controls the maximum number of active concurrent packets. More
- concurrent packets can increase write performance, but also require
- more memory. Each concurrent packet will require approximately 64Kb
- of non-swappable kernel memory, memory which will be allocated when
- a disc is opened for writing.
-
-config CDROM_PKTCDVD_WCACHE
- bool "Enable write caching"
- depends on CDROM_PKTCDVD
- help
- If enabled, write caching will be set for the CD-R/W device. For now
- this option is dangerous unless the CD-RW media is known good, as we
- don't do deferred write error handling yet.
-
config ATA_OVER_ETH
tristate "ATA over Ethernet support"
depends on NET
@@ -387,7 +316,7 @@ config BLK_DEV_RBD
tristate "Rados block device (RBD)"
depends on INET && BLOCK
select CEPH_LIB
- select LIBCRC32C
+ select CRC32
select CRYPTO_AES
select CRYPTO
help
@@ -408,6 +337,42 @@ config BLK_DEV_UBLK
definition isn't finalized yet, and might change according to future
requirement, so mark is as experimental now.
+config BLKDEV_UBLK_LEGACY_OPCODES
+ bool "Support legacy command opcode"
+ depends on BLK_DEV_UBLK
+ default y
+ help
+ ublk driver started to take plain command encoding, which turns out
+ one bad way. The traditional ioctl command opcode encodes more
+ info and basically defines each code uniquely, so opcode conflict
+ is avoided, and driver can handle wrong command easily, meantime it
+ may help security subsystem to audit io_uring command.
+
+ Say Y if your application still uses legacy command opcode.
+
+ Say N if you don't want to support legacy command opcode. It is
+ suggested to enable N if your application(ublk server) switches to
+ ioctl command encoding.
+
source "drivers/block/rnbd/Kconfig"
+config BLK_DEV_ZONED_LOOP
+ tristate "Zoned loopback device support"
+ depends on BLK_DEV_ZONED
+ help
+ Saying Y here will allow you to use create a zoned block device using
+ regular files for zones (one file per zones). This is useful to test
+ file systems, device mapper and applications that support zoned block
+ devices. To create a zoned loop device, no user utility is needed, a
+ zoned loop device can be created (or re-started) using a command
+ like:
+
+ echo "add id=0,zone_size_mb=256,capacity_mb=16384,conv_zones=11" > \
+ /dev/zloop-control
+
+ See Documentation/admin-guide/blockdev/zoned_loop.rst for usage
+ details.
+
+ If unsure, say N.
+
endif # BLK_DEV
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 101612cba303..2d8096eb8cdf 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -20,7 +20,6 @@ obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
obj-$(CONFIG_N64CART) += n64cart.o
obj-$(CONFIG_BLK_DEV_RAM) += brd.o
obj-$(CONFIG_BLK_DEV_LOOP) += loop.o
-obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o
obj-$(CONFIG_SUNVDC) += sunvdc.o
obj-$(CONFIG_BLK_DEV_NBD) += nbd.o
@@ -36,7 +35,9 @@ obj-$(CONFIG_ZRAM) += zram/
obj-$(CONFIG_BLK_DEV_RNBD) += rnbd/
obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_blk/
+obj-$(CONFIG_BLK_DEV_RUST_NULL) += rnull/
obj-$(CONFIG_BLK_DEV_UBLK) += ublk_drv.o
+obj-$(CONFIG_BLK_DEV_ZONED_LOOP) += zloop.o
swim_mod-y := swim.o swim_asm.o
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 4c8b2ba579ee..2932b6653b6f 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -232,6 +232,7 @@ static DEFINE_MUTEX(amiflop_mutex);
static unsigned long int fd_def_df0 = FD_DD_3; /* default for df0 if it doesn't identify */
module_param(fd_def_df0, ulong, 0);
+MODULE_DESCRIPTION("Amiga floppy driver");
MODULE_LICENSE("GPL");
/*
@@ -456,7 +457,7 @@ static int fd_motor_on(int nr)
{
nr &= 3;
- del_timer(motor_off_timer + nr);
+ timer_delete(motor_off_timer + nr);
if (!unit[nr].motor) {
unit[nr].motor = 1;
@@ -1392,7 +1393,7 @@ static int non_int_flush_track (unsigned long nr)
nr&=3;
writefromint = 0;
- del_timer(&post_write_timer);
+ timer_delete(&post_write_timer);
get_fdc(nr);
if (!fd_motor_on(nr)) {
writepending = 0;
@@ -1434,7 +1435,7 @@ static int get_track(int drive, int track)
}
if (unit[drive].dirty == 1) {
- del_timer (flush_track_timer + drive);
+ timer_delete(flush_track_timer + drive);
non_int_flush_track (drive);
}
errcnt = 0;
@@ -1522,17 +1523,17 @@ static blk_status_t amiflop_queue_rq(struct blk_mq_hw_ctx *hctx,
return BLK_STS_OK;
}
-static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+static int fd_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- int drive = MINOR(bdev->bd_dev) & 3;
+ struct amiga_floppy_struct *p = disk->private_data;
- geo->heads = unit[drive].type->heads;
- geo->sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult;
- geo->cylinders = unit[drive].type->tracks;
+ geo->heads = p->type->heads;
+ geo->sectors = p->dtype->sects * p->type->sect_mult;
+ geo->cylinders = p->type->tracks;
return 0;
}
-static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
struct amiga_floppy_struct *p = bdev->bd_disk->private_data;
@@ -1547,7 +1548,6 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
rel_fdc();
return -EBUSY;
}
- fsync_bdev(bdev);
if (fd_motor_on(drive) == 0) {
rel_fdc();
return -ENODEV;
@@ -1591,7 +1591,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
case FDDEFPRM:
return -EINVAL;
case FDFLUSH: /* unconditionally, even if not needed */
- del_timer (flush_track_timer + drive);
+ timer_delete(flush_track_timer + drive);
non_int_flush_track(drive);
break;
#ifdef RAW_IOCTL
@@ -1607,7 +1607,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
return 0;
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
int ret;
@@ -1654,10 +1654,10 @@ static void fd_probe(int dev)
* /dev/PS0 etc), and disallows simultaneous access to the same
* drive with different device numbers.
*/
-static int floppy_open(struct block_device *bdev, fmode_t mode)
+static int floppy_open(struct gendisk *disk, blk_mode_t mode)
{
- int drive = MINOR(bdev->bd_dev) & 3;
- int system = (MINOR(bdev->bd_dev) & 4) >> 2;
+ int drive = disk->first_minor & 3;
+ int system = (disk->first_minor & 4) >> 2;
int old_dev;
unsigned long flags;
@@ -1673,10 +1673,9 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
mutex_unlock(&amiflop_mutex);
return -ENXIO;
}
-
- if (mode & (FMODE_READ|FMODE_WRITE)) {
- bdev_check_media_change(bdev);
- if (mode & FMODE_WRITE) {
+ if (mode & (BLK_OPEN_READ | BLK_OPEN_WRITE)) {
+ disk_check_media_change(disk);
+ if (mode & BLK_OPEN_WRITE) {
int wrprot;
get_fdc(drive);
@@ -1691,7 +1690,6 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
}
}
}
-
local_irq_save(flags);
fd_ref[drive]++;
fd_device[drive] = system;
@@ -1709,14 +1707,14 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static void floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk)
{
struct amiga_floppy_struct *p = disk->private_data;
int drive = p - unit;
mutex_lock(&amiflop_mutex);
if (unit[drive].dirty == 1) {
- del_timer (flush_track_timer + drive);
+ timer_delete(flush_track_timer + drive);
non_int_flush_track (drive);
}
@@ -1779,10 +1777,13 @@ static const struct blk_mq_ops amiflop_mq_ops = {
static int fd_alloc_disk(int drive, int system)
{
+ struct queue_limits lim = {
+ .features = BLK_FEAT_ROTATIONAL,
+ };
struct gendisk *disk;
int err;
- disk = blk_mq_alloc_disk(&unit[drive].tag_set, NULL);
+ disk = blk_mq_alloc_disk(&unit[drive].tag_set, &lim, NULL);
if (IS_ERR(disk))
return PTR_ERR(disk);
@@ -1818,7 +1819,6 @@ static int fd_alloc_drive(int drive)
unit[drive].tag_set.nr_maps = 1;
unit[drive].tag_set.queue_depth = 2;
unit[drive].tag_set.numa_node = NUMA_NO_NODE;
- unit[drive].tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
if (blk_mq_alloc_tag_set(&unit[drive].tag_set))
goto out_cleanup_trackbuf;
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
index 749ae1246f4c..d35caa3c69e1 100644
--- a/drivers/block/aoe/aoe.h
+++ b/drivers/block/aoe/aoe.h
@@ -80,6 +80,7 @@ enum {
DEVFL_NEWSIZE = (1<<6), /* need to update dev size in block layer */
DEVFL_FREEING = (1<<7), /* set when device is being cleaned up */
DEVFL_FREED = (1<<8), /* device has been cleaned up */
+ DEVFL_DEAD = (1<<9), /* device has timed out of aoe_deadsecs */
};
enum {
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 12b3ca8f6f4a..34ead75e7e02 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -24,8 +24,8 @@ static DEFINE_MUTEX(aoeblk_mutex);
static struct kmem_cache *buf_pool_cache;
static struct dentry *aoe_debugfs_dir;
-/* GPFS needs a larger value than the default. */
-static int aoe_maxsectors;
+/* random default picked from the historic block max_sectors cap */
+static int aoe_maxsectors = 2560;
module_param(aoe_maxsectors, int, 0644);
MODULE_PARM_DESC(aoe_maxsectors,
"When nonzero, set the maximum number of sectors per I/O request");
@@ -108,7 +108,7 @@ static ssize_t aoedisk_show_payload(struct device *dev,
return sysfs_emit(page, "%lu\n", d->maxbcnt);
}
-static int aoedisk_debugfs_show(struct seq_file *s, void *ignored)
+static int aoe_debugfs_show(struct seq_file *s, void *ignored)
{
struct aoedev *d;
struct aoetgt **t, **te;
@@ -151,11 +151,7 @@ static int aoedisk_debugfs_show(struct seq_file *s, void *ignored)
return 0;
}
-
-static int aoe_debugfs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, aoedisk_debugfs_show, inode->i_private);
-}
+DEFINE_SHOW_ATTRIBUTE(aoe_debugfs);
static DEVICE_ATTR(state, 0444, aoedisk_show_state, NULL);
static DEVICE_ATTR(mac, 0444, aoedisk_show_mac, NULL);
@@ -184,13 +180,6 @@ static const struct attribute_group *aoe_attr_groups[] = {
NULL,
};
-static const struct file_operations aoe_debugfs_fops = {
- .open = aoe_debugfs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static void
aoedisk_add_debugfs(struct aoedev *d)
{
@@ -215,9 +204,9 @@ aoedisk_rm_debugfs(struct aoedev *d)
}
static int
-aoeblk_open(struct block_device *bdev, fmode_t mode)
+aoeblk_open(struct gendisk *disk, blk_mode_t mode)
{
- struct aoedev *d = bdev->bd_disk->private_data;
+ struct aoedev *d = disk->private_data;
ulong flags;
if (!virt_addr_valid(d)) {
@@ -243,7 +232,7 @@ aoeblk_open(struct block_device *bdev, fmode_t mode)
}
static void
-aoeblk_release(struct gendisk *disk, fmode_t mode)
+aoeblk_release(struct gendisk *disk)
{
struct aoedev *d = disk->private_data;
ulong flags;
@@ -280,9 +269,9 @@ static blk_status_t aoeblk_queue_rq(struct blk_mq_hw_ctx *hctx,
}
static int
-aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+aoeblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- struct aoedev *d = bdev->bd_disk->private_data;
+ struct aoedev *d = disk->private_data;
if ((d->flags & DEVFL_UP) == 0) {
printk(KERN_ERR "aoe: disk not up\n");
@@ -296,7 +285,7 @@ aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
}
static int
-aoeblk_ioctl(struct block_device *bdev, fmode_t mode, uint cmd, ulong arg)
+aoeblk_ioctl(struct block_device *bdev, blk_mode_t mode, uint cmd, ulong arg)
{
struct aoedev *d;
@@ -344,6 +333,12 @@ aoeblk_gdalloc(void *vp)
struct gendisk *gd;
mempool_t *mp;
struct blk_mq_tag_set *set;
+ sector_t ssize;
+ struct queue_limits lim = {
+ .max_hw_sectors = aoe_maxsectors,
+ .io_opt = SZ_2M,
+ .features = BLK_FEAT_ROTATIONAL,
+ };
ulong flags;
int late = 0;
int err;
@@ -373,7 +368,6 @@ aoeblk_gdalloc(void *vp)
set->nr_hw_queues = 1;
set->queue_depth = 128;
set->numa_node = NUMA_NO_NODE;
- set->flags = BLK_MQ_F_SHOULD_MERGE;
err = blk_mq_alloc_tag_set(set);
if (err) {
pr_err("aoe: cannot allocate tag set for %ld.%d\n",
@@ -381,7 +375,7 @@ aoeblk_gdalloc(void *vp)
goto err_mempool;
}
- gd = blk_mq_alloc_disk(set, d);
+ gd = blk_mq_alloc_disk(set, &lim, d);
if (IS_ERR(gd)) {
pr_err("aoe: cannot allocate block queue for %ld.%d\n",
d->aoemajor, d->aoeminor);
@@ -394,19 +388,15 @@ aoeblk_gdalloc(void *vp)
WARN_ON(d->flags & DEVFL_TKILL);
WARN_ON(d->gd);
WARN_ON(d->flags & DEVFL_UP);
- blk_queue_max_hw_sectors(gd->queue, BLK_DEF_MAX_SECTORS);
- blk_queue_io_opt(gd->queue, SZ_2M);
d->bufpool = mp;
d->blkq = gd->queue;
d->gd = gd;
- if (aoe_maxsectors)
- blk_queue_max_hw_sectors(gd->queue, aoe_maxsectors);
gd->major = AOE_MAJOR;
gd->first_minor = d->sysminor;
gd->minors = AOE_PARTITIONS;
gd->fops = &aoe_bdops;
gd->private_data = d;
- set_capacity(gd, d->ssize);
+ ssize = d->ssize;
snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d",
d->aoemajor, d->aoeminor);
@@ -415,6 +405,8 @@ aoeblk_gdalloc(void *vp)
spin_unlock_irqrestore(&d->lock, flags);
+ set_capacity(gd, ssize);
+
err = device_add_disk(NULL, gd, aoe_attr_groups);
if (err)
goto out_disk_cleanup;
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c
index 8eea2529da20..a42c4bcc85ba 100644
--- a/drivers/block/aoe/aoechr.c
+++ b/drivers/block/aoe/aoechr.c
@@ -49,7 +49,7 @@ static int emsgs_head_idx, emsgs_tail_idx;
static struct completion emsgs_comp;
static spinlock_t emsgs_lock;
static int nblocked_emsgs_readers;
-static struct class *aoe_class;
+
static struct aoe_chardev chardevs[] = {
{ MINOR_ERR, "err" },
{ MINOR_DISCOVER, "discover" },
@@ -58,6 +58,16 @@ static struct aoe_chardev chardevs[] = {
{ MINOR_FLUSH, "flush" },
};
+static char *aoe_devnode(const struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
+}
+
+static const struct class aoe_class = {
+ .name = "aoe",
+ .devnode = aoe_devnode,
+};
+
static int
discover(void)
{
@@ -273,11 +283,6 @@ static const struct file_operations aoe_fops = {
.llseek = noop_llseek,
};
-static char *aoe_devnode(struct device *dev, umode_t *mode)
-{
- return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
-}
-
int __init
aoechr_init(void)
{
@@ -290,15 +295,14 @@ aoechr_init(void)
}
init_completion(&emsgs_comp);
spin_lock_init(&emsgs_lock);
- aoe_class = class_create(THIS_MODULE, "aoe");
- if (IS_ERR(aoe_class)) {
+ n = class_register(&aoe_class);
+ if (n) {
unregister_chrdev(AOE_MAJOR, "aoechr");
- return PTR_ERR(aoe_class);
+ return n;
}
- aoe_class->devnode = aoe_devnode;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
- device_create(aoe_class, NULL,
+ device_create(&aoe_class, NULL,
MKDEV(AOE_MAJOR, chardevs[i].minor), NULL,
chardevs[i].name);
@@ -311,8 +315,8 @@ aoechr_exit(void)
int i;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
- device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
- class_destroy(aoe_class);
+ device_destroy(&aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
+ class_unregister(&aoe_class);
unregister_chrdev(AOE_MAJOR, "aoechr");
}
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index d7317425be51..a9affb7c264d 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -14,7 +14,7 @@
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <net/net_namespace.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/uio.h>
#include "aoe.h"
@@ -361,6 +361,7 @@ ata_rw_frameinit(struct frame *f)
}
ah->cmdstat = ATA_CMD_PIO_READ | writebit | extbit;
+ dev_hold(t->ifp->nd);
skb->dev = t->ifp->nd;
}
@@ -401,6 +402,8 @@ aoecmd_ata_rw(struct aoedev *d)
__skb_queue_head_init(&queue);
__skb_queue_tail(&queue, skb);
aoenet_xmit(&queue);
+ } else {
+ dev_put(f->t->ifp->nd);
}
return 1;
}
@@ -419,13 +422,16 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff_head *qu
rcu_read_lock();
for_each_netdev_rcu(&init_net, ifp) {
dev_hold(ifp);
- if (!is_aoe_netif(ifp))
- goto cont;
+ if (!is_aoe_netif(ifp)) {
+ dev_put(ifp);
+ continue;
+ }
skb = new_skb(sizeof *h + sizeof *ch);
if (skb == NULL) {
printk(KERN_INFO "aoe: skb alloc failure\n");
- goto cont;
+ dev_put(ifp);
+ continue;
}
skb_put(skb, sizeof *h + sizeof *ch);
skb->dev = ifp;
@@ -440,9 +446,6 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff_head *qu
h->major = cpu_to_be16(aoemajor);
h->minor = aoeminor;
h->cmd = AOECMD_CFG;
-
-cont:
- dev_put(ifp);
}
rcu_read_unlock();
}
@@ -483,10 +486,13 @@ resend(struct aoedev *d, struct frame *f)
memcpy(h->dst, t->addr, sizeof h->dst);
memcpy(h->src, t->ifp->nd->dev_addr, sizeof h->src);
+ dev_hold(t->ifp->nd);
skb->dev = t->ifp->nd;
skb = skb_clone(skb, GFP_ATOMIC);
- if (skb == NULL)
+ if (skb == NULL) {
+ dev_put(t->ifp->nd);
return;
+ }
f->sent = ktime_get();
__skb_queue_head_init(&queue);
__skb_queue_tail(&queue, skb);
@@ -617,6 +623,8 @@ probe(struct aoetgt *t)
__skb_queue_head_init(&queue);
__skb_queue_tail(&queue, skb);
aoenet_xmit(&queue);
+ } else {
+ dev_put(f->t->ifp->nd);
}
}
@@ -737,7 +745,7 @@ rexmit_timer(struct timer_list *timer)
int utgts; /* number of aoetgt descriptors (not slots) */
int since;
- d = from_timer(d, timer, timer);
+ d = timer_container_of(d, timer, timer);
spin_lock_irqsave(&d->lock, flags);
@@ -746,7 +754,7 @@ rexmit_timer(struct timer_list *timer)
utgts = count_targets(d, NULL);
- if (d->flags & DEVFL_TKILL) {
+ if (d->flags & (DEVFL_TKILL | DEVFL_DEAD)) {
spin_unlock_irqrestore(&d->lock, flags);
return;
}
@@ -778,7 +786,8 @@ rexmit_timer(struct timer_list *timer)
* to clean up.
*/
list_splice(&flist, &d->factive[0]);
- aoedev_downdev(d);
+ d->flags |= DEVFL_DEAD;
+ queue_work(aoe_wq, &d->work);
goto out;
}
@@ -890,6 +899,9 @@ aoecmd_sleepwork(struct work_struct *work)
{
struct aoedev *d = container_of(work, struct aoedev, work);
+ if (d->flags & DEVFL_DEAD)
+ aoedev_downdev(d);
+
if (d->flags & DEVFL_GDALLOC)
aoeblk_gdalloc(d);
@@ -1395,6 +1407,7 @@ aoecmd_ata_id(struct aoedev *d)
ah->cmdstat = ATA_CMD_ID_ATA;
ah->lba3 = 0xa0;
+ dev_hold(t->ifp->nd);
skb->dev = t->ifp->nd;
d->rttavg = RTTAVG_INIT;
@@ -1404,6 +1417,8 @@ aoecmd_ata_id(struct aoedev *d)
skb = skb_clone(skb, GFP_ATOMIC);
if (skb)
f->sent = ktime_get();
+ else
+ dev_put(t->ifp->nd);
return skb;
}
@@ -1746,6 +1761,6 @@ aoecmd_exit(void)
kfree(kts);
kfree(ktiowq);
- free_page((unsigned long) page_address(empty_page));
+ __free_page(empty_page);
empty_page = NULL;
}
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c
index 3523dd82d7a0..3a240755045b 100644
--- a/drivers/block/aoe/aoedev.c
+++ b/drivers/block/aoe/aoedev.c
@@ -149,7 +149,7 @@ dummy_timer(struct timer_list *t)
{
struct aoedev *d;
- d = from_timer(d, t, timer);
+ d = timer_container_of(d, t, timer);
if (d->flags & DEVFL_TKILL)
return;
d->timer.expires = jiffies + HZ;
@@ -198,9 +198,13 @@ aoedev_downdev(struct aoedev *d)
{
struct aoetgt *t, **tt, **te;
struct list_head *head, *pos, *nx;
+ struct request *rq, *rqnext;
int i;
+ unsigned long flags;
- d->flags &= ~DEVFL_UP;
+ spin_lock_irqsave(&d->lock, flags);
+ d->flags &= ~(DEVFL_UP | DEVFL_DEAD);
+ spin_unlock_irqrestore(&d->lock, flags);
/* clean out active and to-be-retransmitted buffers */
for (i = 0; i < NFACTIVE; i++) {
@@ -223,13 +227,21 @@ aoedev_downdev(struct aoedev *d)
/* clean out the in-process request (if any) */
aoe_failip(d);
+ /* clean out any queued block requests */
+ list_for_each_entry_safe(rq, rqnext, &d->rq_list, queuelist) {
+ list_del_init(&rq->queuelist);
+ blk_mq_start_request(rq);
+ blk_mq_end_request(rq, BLK_STS_IOERR);
+ }
+
/* fast fail all pending I/O */
if (d->blkq) {
/* UP is cleared, freeze+quiesce to insure all are errored */
- blk_mq_freeze_queue(d->blkq);
+ unsigned int memflags = blk_mq_freeze_queue(d->blkq);
+
blk_mq_quiesce_queue(d->blkq);
blk_mq_unquiesce_queue(d->blkq);
- blk_mq_unfreeze_queue(d->blkq);
+ blk_mq_unfreeze_queue(d->blkq, memflags);
}
if (d->gd)
@@ -273,7 +285,7 @@ freedev(struct aoedev *d)
if (!freeing)
return;
- del_timer_sync(&d->timer);
+ timer_delete_sync(&d->timer);
if (d->gd) {
aoedisk_rm_debugfs(d);
del_gendisk(d->gd);
diff --git a/drivers/block/aoe/aoemain.c b/drivers/block/aoe/aoemain.c
index 6238c4c87cfc..3b21750038ee 100644
--- a/drivers/block/aoe/aoemain.c
+++ b/drivers/block/aoe/aoemain.c
@@ -28,7 +28,7 @@ static void discover_timer(struct timer_list *t)
static void __exit
aoe_exit(void)
{
- del_timer_sync(&timer);
+ timer_delete_sync(&timer);
aoenet_exit();
unregister_blkdev(AOE_MAJOR, DEVICE_NAME);
@@ -44,7 +44,7 @@ aoe_init(void)
{
int ret;
- aoe_wq = alloc_workqueue("aoe_wq", 0, 0);
+ aoe_wq = alloc_workqueue("aoe_wq", WQ_PERCPU, 0);
if (!aoe_wq)
return -ENOMEM;
diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c
index 63773a90581d..66e617664c14 100644
--- a/drivers/block/aoe/aoenet.c
+++ b/drivers/block/aoe/aoenet.c
@@ -10,7 +10,7 @@
#include <linux/netdevice.h>
#include <linux/moduleparam.h>
#include <net/net_namespace.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "aoe.h"
#define NECODES 5
@@ -39,8 +39,7 @@ static struct ktstate kts;
#ifndef MODULE
static int __init aoe_iflist_setup(char *str)
{
- strncpy(aoe_iflist, str, IFLISTSZ);
- aoe_iflist[IFLISTSZ - 1] = '\0';
+ strscpy(aoe_iflist, str, IFLISTSZ);
return 1;
}
@@ -64,6 +63,7 @@ tx(int id) __must_hold(&txlock)
pr_warn("aoe: packet could not be sent on %s. %s\n",
ifp ? ifp->name : "netif",
"consider increasing tx_queue_len");
+ dev_put(ifp);
spin_lock_irq(&txlock);
}
return 0;
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 9deb4df6bdb8..7fe14266c12c 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -442,13 +442,13 @@ static void fd_times_out(struct timer_list *unused);
static void finish_fdc( void );
static void finish_fdc_done( int dummy );
static void setup_req_params( int drive );
-static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
- cmd, unsigned long param);
+static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
+ unsigned int cmd, unsigned long param);
static void fd_probe( int drive );
static int fd_test_drive_present( int drive );
static void config_types( void );
-static int floppy_open(struct block_device *bdev, fmode_t mode);
-static void floppy_release(struct gendisk *disk, fmode_t mode);
+static int floppy_open(struct gendisk *disk, blk_mode_t mode);
+static void floppy_release(struct gendisk *disk);
/************************* End of Prototypes **************************/
@@ -494,7 +494,7 @@ static inline void start_timeout(void)
static inline void stop_timeout(void)
{
- del_timer(&timeout_timer);
+ timer_delete(&timeout_timer);
}
/* Select the side to use. */
@@ -746,6 +746,7 @@ static int do_format(int drive, int type, struct atari_format_descr *desc)
unsigned char *p;
int sect, nsect;
unsigned long flags;
+ unsigned int memflags;
int ret;
if (type) {
@@ -758,7 +759,7 @@ static int do_format(int drive, int type, struct atari_format_descr *desc)
}
q = unit[drive].disk[type]->queue;
- blk_mq_freeze_queue(q);
+ memflags = blk_mq_freeze_queue(q);
blk_mq_quiesce_queue(q);
local_irq_save(flags);
@@ -783,7 +784,7 @@ static int do_format(int drive, int type, struct atari_format_descr *desc)
contents become invalid! */
BufferDrive = -1;
/* stop deselect timer */
- del_timer( &motor_off_timer );
+ timer_delete(&motor_off_timer);
FILL( 60 * (nsect / 9), 0x4e );
for( sect = 0; sect < nsect; ++sect ) {
@@ -817,7 +818,7 @@ static int do_format(int drive, int type, struct atari_format_descr *desc)
ret = FormatError ? -EIO : 0;
out:
blk_mq_unquiesce_queue(q);
- blk_mq_unfreeze_queue(q);
+ blk_mq_unfreeze_queue(q, memflags);
return ret;
}
@@ -1137,7 +1138,7 @@ static void fd_rwsec_done( int status )
DPRINT(("fd_rwsec_done()\n"));
if (read_track) {
- del_timer(&readtrack_timer);
+ timer_delete(&readtrack_timer);
if (!MultReadInProgress)
return;
MultReadInProgress = 0;
@@ -1355,7 +1356,7 @@ static void fd_times_out(struct timer_list *unused)
/* If the timeout occurred while the readtrack_check timer was
* active, we need to cancel it, else bad things will happen */
if (UseTrackbuffer)
- del_timer( &readtrack_timer );
+ timer_delete(&readtrack_timer);
FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
udelay( 25 );
@@ -1565,7 +1566,7 @@ static blk_status_t ataflop_queue_rq(struct blk_mq_hw_ctx *hctx,
}
/* stop deselect timer */
- del_timer( &motor_off_timer );
+ timer_delete(&motor_off_timer);
ReqCnt = 0;
ReqCmd = rq_data_dir(fd_request);
@@ -1581,7 +1582,7 @@ out:
return BLK_STS_OK;
}
-static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
struct gendisk *disk = bdev->bd_disk;
@@ -1760,15 +1761,17 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
/* invalidate the buffer track to force a reread */
BufferDrive = -1;
set_bit(drive, &fake_change);
- if (bdev_check_media_change(bdev))
- floppy_revalidate(bdev->bd_disk);
+ if (disk_check_media_change(disk)) {
+ bdev_mark_dead(disk->part0, true);
+ floppy_revalidate(disk);
+ }
return 0;
default:
return -EINVAL;
}
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg)
{
int ret;
@@ -1915,32 +1918,31 @@ static void __init config_types( void )
* drive with different device numbers.
*/
-static int floppy_open(struct block_device *bdev, fmode_t mode)
+static int floppy_open(struct gendisk *disk, blk_mode_t mode)
{
- struct atari_floppy_struct *p = bdev->bd_disk->private_data;
- int type = MINOR(bdev->bd_dev) >> 2;
+ struct atari_floppy_struct *p = disk->private_data;
+ int type = disk->first_minor >> 2;
DPRINT(("fd_open: type=%d\n",type));
if (p->ref && p->type != type)
return -EBUSY;
- if (p->ref == -1 || (p->ref && mode & FMODE_EXCL))
+ if (p->ref == -1 || (p->ref && mode & BLK_OPEN_EXCL))
return -EBUSY;
-
- if (mode & FMODE_EXCL)
+ if (mode & BLK_OPEN_EXCL)
p->ref = -1;
else
p->ref++;
p->type = type;
- if (mode & FMODE_NDELAY)
+ if (mode & BLK_OPEN_NDELAY)
return 0;
- if (mode & (FMODE_READ|FMODE_WRITE)) {
- if (bdev_check_media_change(bdev))
- floppy_revalidate(bdev->bd_disk);
- if (mode & FMODE_WRITE) {
+ if (mode & (BLK_OPEN_READ | BLK_OPEN_WRITE)) {
+ if (disk_check_media_change(disk))
+ floppy_revalidate(disk);
+ if (mode & BLK_OPEN_WRITE) {
if (p->wpstat) {
if (p->ref < 0)
p->ref = 0;
@@ -1953,18 +1955,18 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+static int floppy_unlocked_open(struct gendisk *disk, blk_mode_t mode)
{
int ret;
mutex_lock(&ataflop_mutex);
- ret = floppy_open(bdev, mode);
+ ret = floppy_open(disk, mode);
mutex_unlock(&ataflop_mutex);
return ret;
}
-static void floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk)
{
struct atari_floppy_struct *p = disk->private_data;
mutex_lock(&ataflop_mutex);
@@ -1991,9 +1993,12 @@ static const struct blk_mq_ops ataflop_mq_ops = {
static int ataflop_alloc_disk(unsigned int drive, unsigned int type)
{
+ struct queue_limits lim = {
+ .features = BLK_FEAT_ROTATIONAL,
+ };
struct gendisk *disk;
- disk = blk_mq_alloc_disk(&unit[drive].tag_set, NULL);
+ disk = blk_mq_alloc_disk(&unit[drive].tag_set, &lim, NULL);
if (IS_ERR(disk))
return PTR_ERR(disk);
@@ -2050,7 +2055,7 @@ static void atari_floppy_cleanup(void)
blk_mq_free_tag_set(&unit[i].tag_set);
}
- del_timer_sync(&fd_timer);
+ timer_delete_sync(&fd_timer);
atari_stram_free(DMABuffer);
}
@@ -2084,7 +2089,6 @@ static int __init atari_floppy_init (void)
unit[i].tag_set.nr_maps = 1;
unit[i].tag_set.queue_depth = 2;
unit[i].tag_set.numa_node = NUMA_NO_NODE;
- unit[i].tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
ret = blk_mq_alloc_tag_set(&unit[i].tag_set);
if (ret)
goto err;
@@ -2196,4 +2200,5 @@ static void __exit atari_floppy_exit(void)
module_init(atari_floppy_init)
module_exit(atari_floppy_exit)
+MODULE_DESCRIPTION("Atari floppy driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 859499cd1ff8..9778259b30d4 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -19,7 +19,7 @@
#include <linux/highmem.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
-#include <linux/radix-tree.h>
+#include <linux/xarray.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/backing-dev.h>
@@ -28,11 +28,8 @@
#include <linux/uaccess.h>
/*
- * Each block ramdisk device has a radix_tree brd_pages of pages that stores
- * the pages containing the block device's contents. A brd page's ->index is
- * its offset in PAGE_SIZE units. This is similar to, but in no way connected
- * with, the kernel's pagecache or buffer cache (which sit above our block
- * device).
+ * Each block ramdisk device has a xarray brd_pages of pages that stores
+ * the pages containing the block device's contents.
*/
struct brd_device {
int brd_number;
@@ -40,292 +37,190 @@ struct brd_device {
struct list_head brd_list;
/*
- * Backing store of pages and lock to protect it. This is the contents
- * of the block device.
+ * Backing store of pages. This is the contents of the block device.
*/
- spinlock_t brd_lock;
- struct radix_tree_root brd_pages;
+ struct xarray brd_pages;
u64 brd_nr_pages;
};
/*
- * Look up and return a brd's page for a given sector.
+ * Look up and return a brd's page with reference grabbed for a given sector.
*/
static struct page *brd_lookup_page(struct brd_device *brd, sector_t sector)
{
- pgoff_t idx;
struct page *page;
+ XA_STATE(xas, &brd->brd_pages, sector >> PAGE_SECTORS_SHIFT);
- /*
- * The page lifetime is protected by the fact that we have opened the
- * device node -- brd pages will never be deleted under us, so we
- * don't need any further locking or refcounting.
- *
- * This is strictly true for the radix-tree nodes as well (ie. we
- * don't actually need the rcu_read_lock()), however that is not a
- * documented feature of the radix-tree API so it is better to be
- * safe here (we don't have total exclusion from radix tree updates
- * here, only deletes).
- */
rcu_read_lock();
- idx = sector >> PAGE_SECTORS_SHIFT; /* sector to page index */
- page = radix_tree_lookup(&brd->brd_pages, idx);
- rcu_read_unlock();
+repeat:
+ page = xas_load(&xas);
+ if (xas_retry(&xas, page)) {
+ xas_reset(&xas);
+ goto repeat;
+ }
- BUG_ON(page && page->index != idx);
+ if (!page)
+ goto out;
+
+ if (!get_page_unless_zero(page)) {
+ xas_reset(&xas);
+ goto repeat;
+ }
+
+ if (unlikely(page != xas_reload(&xas))) {
+ put_page(page);
+ xas_reset(&xas);
+ goto repeat;
+ }
+out:
+ rcu_read_unlock();
return page;
}
/*
- * Look up and return a brd's page for a given sector.
- * If one does not exist, allocate an empty page, and insert that. Then
- * return it.
+ * Insert a new page for a given sector, if one does not already exist.
+ * The returned page will grab reference.
*/
-static struct page *brd_insert_page(struct brd_device *brd, sector_t sector)
+static struct page *brd_insert_page(struct brd_device *brd, sector_t sector,
+ blk_opf_t opf)
{
- pgoff_t idx;
- struct page *page;
- gfp_t gfp_flags;
+ gfp_t gfp = (opf & REQ_NOWAIT) ? GFP_NOWAIT : GFP_NOIO;
+ struct page *page, *ret;
- page = brd_lookup_page(brd, sector);
- if (page)
- return page;
-
- /*
- * Must use NOIO because we don't want to recurse back into the
- * block or filesystem layers from page reclaim.
- */
- gfp_flags = GFP_NOIO | __GFP_ZERO | __GFP_HIGHMEM;
- page = alloc_page(gfp_flags);
+ page = alloc_page(gfp | __GFP_ZERO | __GFP_HIGHMEM);
if (!page)
- return NULL;
-
- if (radix_tree_preload(GFP_NOIO)) {
- __free_page(page);
- return NULL;
- }
+ return ERR_PTR(-ENOMEM);
- spin_lock(&brd->brd_lock);
- idx = sector >> PAGE_SECTORS_SHIFT;
- page->index = idx;
- if (radix_tree_insert(&brd->brd_pages, idx, page)) {
- __free_page(page);
- page = radix_tree_lookup(&brd->brd_pages, idx);
- BUG_ON(!page);
- BUG_ON(page->index != idx);
- } else {
+ xa_lock(&brd->brd_pages);
+ ret = __xa_cmpxchg(&brd->brd_pages, sector >> PAGE_SECTORS_SHIFT, NULL,
+ page, gfp);
+ if (!ret) {
brd->brd_nr_pages++;
+ get_page(page);
+ xa_unlock(&brd->brd_pages);
+ return page;
}
- spin_unlock(&brd->brd_lock);
- radix_tree_preload_end();
+ if (!xa_is_err(ret)) {
+ get_page(ret);
+ xa_unlock(&brd->brd_pages);
+ put_page(page);
+ return ret;
+ }
- return page;
+ xa_unlock(&brd->brd_pages);
+ put_page(page);
+ return ERR_PTR(xa_err(ret));
}
/*
- * Free all backing store pages and radix tree. This must only be called when
+ * Free all backing store pages and xarray. This must only be called when
* there are no other users of the device.
*/
-#define FREE_BATCH 16
static void brd_free_pages(struct brd_device *brd)
{
- unsigned long pos = 0;
- struct page *pages[FREE_BATCH];
- int nr_pages;
-
- do {
- int i;
-
- nr_pages = radix_tree_gang_lookup(&brd->brd_pages,
- (void **)pages, pos, FREE_BATCH);
-
- for (i = 0; i < nr_pages; i++) {
- void *ret;
-
- BUG_ON(pages[i]->index < pos);
- pos = pages[i]->index;
- ret = radix_tree_delete(&brd->brd_pages, pos);
- BUG_ON(!ret || ret != pages[i]);
- __free_page(pages[i]);
- }
-
- pos++;
+ struct page *page;
+ pgoff_t idx;
- /*
- * It takes 3.4 seconds to remove 80GiB ramdisk.
- * So, we need cond_resched to avoid stalling the CPU.
- */
+ xa_for_each(&brd->brd_pages, idx, page) {
+ put_page(page);
cond_resched();
-
- /*
- * This assumes radix_tree_gang_lookup always returns as
- * many pages as possible. If the radix-tree code changes,
- * so will this have to.
- */
- } while (nr_pages == FREE_BATCH);
-}
-
-/*
- * copy_to_brd_setup must be called before copy_to_brd. It may sleep.
- */
-static int copy_to_brd_setup(struct brd_device *brd, sector_t sector, size_t n)
-{
- unsigned int offset = (sector & (PAGE_SECTORS-1)) << SECTOR_SHIFT;
- size_t copy;
-
- copy = min_t(size_t, n, PAGE_SIZE - offset);
- if (!brd_insert_page(brd, sector))
- return -ENOSPC;
- if (copy < n) {
- sector += copy >> SECTOR_SHIFT;
- if (!brd_insert_page(brd, sector))
- return -ENOSPC;
}
- return 0;
+
+ xa_destroy(&brd->brd_pages);
}
/*
- * Copy n bytes from src to the brd starting at sector. Does not sleep.
+ * Process a single segment. The segment is capped to not cross page boundaries
+ * in both the bio and the brd backing memory.
*/
-static void copy_to_brd(struct brd_device *brd, const void *src,
- sector_t sector, size_t n)
+static bool brd_rw_bvec(struct brd_device *brd, struct bio *bio)
{
+ struct bio_vec bv = bio_iter_iovec(bio, bio->bi_iter);
+ sector_t sector = bio->bi_iter.bi_sector;
+ u32 offset = (sector & (PAGE_SECTORS - 1)) << SECTOR_SHIFT;
+ blk_opf_t opf = bio->bi_opf;
struct page *page;
- void *dst;
- unsigned int offset = (sector & (PAGE_SECTORS-1)) << SECTOR_SHIFT;
- size_t copy;
+ void *kaddr;
+
+ bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset);
- copy = min_t(size_t, n, PAGE_SIZE - offset);
page = brd_lookup_page(brd, sector);
- BUG_ON(!page);
-
- dst = kmap_atomic(page);
- memcpy(dst + offset, src, copy);
- kunmap_atomic(dst);
-
- if (copy < n) {
- src += copy;
- sector += copy >> SECTOR_SHIFT;
- copy = n - copy;
- page = brd_lookup_page(brd, sector);
- BUG_ON(!page);
-
- dst = kmap_atomic(page);
- memcpy(dst, src, copy);
- kunmap_atomic(dst);
+ if (!page && op_is_write(opf)) {
+ page = brd_insert_page(brd, sector, opf);
+ if (IS_ERR(page))
+ goto out_error;
}
-}
-/*
- * Copy n bytes to dst from the brd starting at sector. Does not sleep.
- */
-static void copy_from_brd(void *dst, struct brd_device *brd,
- sector_t sector, size_t n)
-{
- struct page *page;
- void *src;
- unsigned int offset = (sector & (PAGE_SECTORS-1)) << SECTOR_SHIFT;
- size_t copy;
-
- copy = min_t(size_t, n, PAGE_SIZE - offset);
- page = brd_lookup_page(brd, sector);
- if (page) {
- src = kmap_atomic(page);
- memcpy(dst, src + offset, copy);
- kunmap_atomic(src);
- } else
- memset(dst, 0, copy);
-
- if (copy < n) {
- dst += copy;
- sector += copy >> SECTOR_SHIFT;
- copy = n - copy;
- page = brd_lookup_page(brd, sector);
- if (page) {
- src = kmap_atomic(page);
- memcpy(dst, src, copy);
- kunmap_atomic(src);
- } else
- memset(dst, 0, copy);
+ kaddr = bvec_kmap_local(&bv);
+ if (op_is_write(opf)) {
+ memcpy_to_page(page, offset, kaddr, bv.bv_len);
+ } else {
+ if (page)
+ memcpy_from_page(kaddr, page, offset, bv.bv_len);
+ else
+ memset(kaddr, 0, bv.bv_len);
}
+ kunmap_local(kaddr);
+
+ bio_advance_iter_single(bio, &bio->bi_iter, bv.bv_len);
+ if (page)
+ put_page(page);
+ return true;
+
+out_error:
+ if (PTR_ERR(page) == -ENOMEM && (opf & REQ_NOWAIT))
+ bio_wouldblock_error(bio);
+ else
+ bio_io_error(bio);
+ return false;
}
-/*
- * Process a single bvec of a bio.
- */
-static int brd_do_bvec(struct brd_device *brd, struct page *page,
- unsigned int len, unsigned int off, enum req_op op,
- sector_t sector)
+static void brd_do_discard(struct brd_device *brd, sector_t sector, u32 size)
{
- void *mem;
- int err = 0;
+ sector_t aligned_sector = round_up(sector, PAGE_SECTORS);
+ sector_t aligned_end = round_down(
+ sector + (size >> SECTOR_SHIFT), PAGE_SECTORS);
+ struct page *page;
- if (op_is_write(op)) {
- err = copy_to_brd_setup(brd, sector, len);
- if (err)
- goto out;
- }
+ if (aligned_end <= aligned_sector)
+ return;
- mem = kmap_atomic(page);
- if (!op_is_write(op)) {
- copy_from_brd(mem + off, brd, sector, len);
- flush_dcache_page(page);
- } else {
- flush_dcache_page(page);
- copy_to_brd(brd, mem + off, sector, len);
+ xa_lock(&brd->brd_pages);
+ while (aligned_sector < aligned_end && aligned_sector < rd_size * 2) {
+ page = __xa_erase(&brd->brd_pages, aligned_sector >> PAGE_SECTORS_SHIFT);
+ if (page) {
+ put_page(page);
+ brd->brd_nr_pages--;
+ }
+ aligned_sector += PAGE_SECTORS;
}
- kunmap_atomic(mem);
-
-out:
- return err;
+ xa_unlock(&brd->brd_pages);
}
static void brd_submit_bio(struct bio *bio)
{
struct brd_device *brd = bio->bi_bdev->bd_disk->private_data;
- sector_t sector = bio->bi_iter.bi_sector;
- struct bio_vec bvec;
- struct bvec_iter iter;
- bio_for_each_segment(bvec, bio, iter) {
- unsigned int len = bvec.bv_len;
- int err;
-
- /* Don't support un-aligned buffer */
- WARN_ON_ONCE((bvec.bv_offset & (SECTOR_SIZE - 1)) ||
- (len & (SECTOR_SIZE - 1)));
+ if (unlikely(op_is_discard(bio->bi_opf))) {
+ brd_do_discard(brd, bio->bi_iter.bi_sector,
+ bio->bi_iter.bi_size);
+ bio_endio(bio);
+ return;
+ }
- err = brd_do_bvec(brd, bvec.bv_page, len, bvec.bv_offset,
- bio_op(bio), sector);
- if (err) {
- bio_io_error(bio);
+ do {
+ if (!brd_rw_bvec(brd, bio))
return;
- }
- sector += len >> SECTOR_SHIFT;
- }
+ } while (bio->bi_iter.bi_size);
bio_endio(bio);
}
-static int brd_rw_page(struct block_device *bdev, sector_t sector,
- struct page *page, enum req_op op)
-{
- struct brd_device *brd = bdev->bd_disk->private_data;
- int err;
-
- if (PageTransHuge(page))
- return -ENOTSUPP;
- err = brd_do_bvec(brd, page, PAGE_SIZE, 0, op, sector);
- page_endio(page, op_is_write(op), err);
- return err;
-}
-
static const struct block_device_operations brd_fops = {
.owner = THIS_MODULE,
.submit_bio = brd_submit_bio,
- .rw_page = brd_rw_page,
};
/*
@@ -343,6 +238,7 @@ static int max_part = 1;
module_param(max_part, int, 0444);
MODULE_PARM_DESC(max_part, "Num Minors to reserve between devices");
+MODULE_DESCRIPTION("Ram backed block device driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR);
MODULE_ALIAS("rd");
@@ -362,56 +258,86 @@ __setup("ramdisk_size=", ramdisk_size);
* (should share code eventually).
*/
static LIST_HEAD(brd_devices);
+static DEFINE_MUTEX(brd_devices_mutex);
static struct dentry *brd_debugfs_dir;
+static struct brd_device *brd_find_or_alloc_device(int i)
+{
+ struct brd_device *brd;
+
+ mutex_lock(&brd_devices_mutex);
+ list_for_each_entry(brd, &brd_devices, brd_list) {
+ if (brd->brd_number == i) {
+ mutex_unlock(&brd_devices_mutex);
+ return ERR_PTR(-EEXIST);
+ }
+ }
+
+ brd = kzalloc(sizeof(*brd), GFP_KERNEL);
+ if (!brd) {
+ mutex_unlock(&brd_devices_mutex);
+ return ERR_PTR(-ENOMEM);
+ }
+ brd->brd_number = i;
+ list_add_tail(&brd->brd_list, &brd_devices);
+ mutex_unlock(&brd_devices_mutex);
+ return brd;
+}
+
+static void brd_free_device(struct brd_device *brd)
+{
+ mutex_lock(&brd_devices_mutex);
+ list_del(&brd->brd_list);
+ mutex_unlock(&brd_devices_mutex);
+ kfree(brd);
+}
+
static int brd_alloc(int i)
{
struct brd_device *brd;
struct gendisk *disk;
char buf[DISK_NAME_LEN];
int err = -ENOMEM;
+ struct queue_limits lim = {
+ /*
+ * This is so fdisk will align partitions on 4k, because of
+ * direct_access API needing 4k alignment, returning a PFN
+ * (This is only a problem on very small devices <= 4M,
+ * otherwise fdisk will align on 1M. Regardless this call
+ * is harmless)
+ */
+ .physical_block_size = PAGE_SIZE,
+ .max_hw_discard_sectors = UINT_MAX,
+ .max_discard_segments = 1,
+ .discard_granularity = PAGE_SIZE,
+ .features = BLK_FEAT_SYNCHRONOUS |
+ BLK_FEAT_NOWAIT,
+ };
- list_for_each_entry(brd, &brd_devices, brd_list)
- if (brd->brd_number == i)
- return -EEXIST;
- brd = kzalloc(sizeof(*brd), GFP_KERNEL);
- if (!brd)
- return -ENOMEM;
- brd->brd_number = i;
- list_add_tail(&brd->brd_list, &brd_devices);
+ brd = brd_find_or_alloc_device(i);
+ if (IS_ERR(brd))
+ return PTR_ERR(brd);
- spin_lock_init(&brd->brd_lock);
- INIT_RADIX_TREE(&brd->brd_pages, GFP_ATOMIC);
+ xa_init(&brd->brd_pages);
snprintf(buf, DISK_NAME_LEN, "ram%d", i);
if (!IS_ERR_OR_NULL(brd_debugfs_dir))
debugfs_create_u64(buf, 0444, brd_debugfs_dir,
&brd->brd_nr_pages);
- disk = brd->brd_disk = blk_alloc_disk(NUMA_NO_NODE);
- if (!disk)
+ disk = brd->brd_disk = blk_alloc_disk(&lim, NUMA_NO_NODE);
+ if (IS_ERR(disk)) {
+ err = PTR_ERR(disk);
goto out_free_dev;
-
+ }
disk->major = RAMDISK_MAJOR;
disk->first_minor = i * max_part;
disk->minors = max_part;
disk->fops = &brd_fops;
disk->private_data = brd;
- strlcpy(disk->disk_name, buf, DISK_NAME_LEN);
+ strscpy(disk->disk_name, buf, DISK_NAME_LEN);
set_capacity(disk, rd_size * 2);
- /*
- * This is so fdisk will align partitions on 4k, because of
- * direct_access API needing 4k alignment, returning a PFN
- * (This is only a problem on very small devices <= 4M,
- * otherwise fdisk will align on 1M. Regardless this call
- * is harmless)
- */
- blk_queue_physical_block_size(disk->queue, PAGE_SIZE);
-
- /* Tell the block layer that this is not a rotational device */
- blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue);
- blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, disk->queue);
err = add_disk(disk);
if (err)
goto out_cleanup_disk;
@@ -421,8 +347,7 @@ static int brd_alloc(int i)
out_cleanup_disk:
put_disk(disk);
out_free_dev:
- list_del(&brd->brd_list);
- kfree(brd);
+ brd_free_device(brd);
return err;
}
@@ -441,8 +366,7 @@ static void brd_cleanup(void)
del_gendisk(brd->brd_disk);
put_disk(brd->brd_disk);
brd_free_pages(brd);
- list_del(&brd->brd_list);
- kfree(brd);
+ brd_free_device(brd);
}
}
@@ -469,16 +393,6 @@ static int __init brd_init(void)
{
int err, i;
- brd_check_and_reset_par();
-
- brd_debugfs_dir = debugfs_create_dir("ramdisk_pages", NULL);
-
- for (i = 0; i < rd_nr; i++) {
- err = brd_alloc(i);
- if (err)
- goto out_free;
- }
-
/*
* brd module now has a feature to instantiate underlying device
* structure on-demand, provided that there is an access dev node.
@@ -494,11 +408,18 @@ static int __init brd_init(void)
* dynamically.
*/
+ brd_check_and_reset_par();
+
+ brd_debugfs_dir = debugfs_create_dir("ramdisk_pages", NULL);
+
if (__register_blkdev(RAMDISK_MAJOR, "ramdisk", brd_probe)) {
err = -EIO;
goto out_free;
}
+ for (i = 0; i < rd_nr; i++)
+ brd_alloc(i);
+
pr_info("brd: module loaded\n");
return 0;
diff --git a/drivers/block/drbd/Kconfig b/drivers/block/drbd/Kconfig
index cbacddc55a1d..495a72da04c6 100644
--- a/drivers/block/drbd/Kconfig
+++ b/drivers/block/drbd/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
#
# DRBD device driver configuration
#
@@ -10,7 +10,7 @@ config BLK_DEV_DRBD
tristate "DRBD Distributed Replicated Block Device support"
depends on PROC_FS && INET
select LRU_CACHE
- select LIBCRC32C
+ select CRC32
help
NOTE: In order to authenticate connections you have to select
diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile
index 8bd534697d1b..67a8b352a1d5 100644
--- a/drivers/block/drbd/Makefile
+++ b/drivers/block/drbd/Makefile
@@ -1,5 +1,5 @@
-# SPDX-License-Identifier: GPL-2.0
-drbd-y := drbd_bitmap.o drbd_proc.o
+# SPDX-License-Identifier: GPL-2.0-only
+drbd-y := drbd_buildtag.o drbd_bitmap.o drbd_proc.o
drbd-y += drbd_worker.o drbd_receiver.o drbd_req.o drbd_actlog.o
drbd-y += drbd_main.o drbd_strings.o drbd_nl.o
drbd-y += drbd_interval.o drbd_state.o
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index e27478ae579c..742b2908ff68 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_actlog.c
@@ -735,8 +735,9 @@ static bool update_rs_extent(struct drbd_device *device,
return false;
}
-void drbd_advance_rs_marks(struct drbd_device *device, unsigned long still_to_go)
+void drbd_advance_rs_marks(struct drbd_peer_device *peer_device, unsigned long still_to_go)
{
+ struct drbd_device *device = peer_device->device;
unsigned long now = jiffies;
unsigned long last = device->rs_mark_time[device->rs_last_mark];
int next = (device->rs_last_mark + 1) % DRBD_SYNC_MARKS;
@@ -819,7 +820,7 @@ static int update_sync_bits(struct drbd_device *device,
if (mode == SET_IN_SYNC) {
unsigned long still_to_go = drbd_bm_total_weight(device);
bool rs_is_done = (still_to_go <= device->rs_failed);
- drbd_advance_rs_marks(device, still_to_go);
+ drbd_advance_rs_marks(first_peer_device(device), still_to_go);
if (cleared || rs_is_done)
maybe_schedule_on_disk_bitmap_update(device, rs_is_done);
} else if (mode == RECORD_RS_FAILED)
@@ -837,16 +838,17 @@ static bool plausible_request_size(int size)
}
/* clear the bit corresponding to the piece of storage in question:
- * size byte of data starting from sector. Only clear a bits of the affected
- * one ore more _aligned_ BM_BLOCK_SIZE blocks.
+ * size byte of data starting from sector. Only clear bits of the affected
+ * one or more _aligned_ BM_BLOCK_SIZE blocks.
*
* called by worker on C_SYNC_TARGET and receiver on SyncSource.
*
*/
-int __drbd_change_sync(struct drbd_device *device, sector_t sector, int size,
+int __drbd_change_sync(struct drbd_peer_device *peer_device, sector_t sector, int size,
enum update_sync_bits_mode mode)
{
/* Is called from worker and receiver context _only_ */
+ struct drbd_device *device = peer_device->device;
unsigned long sbnr, ebnr, lbnr;
unsigned long count = 0;
sector_t esector, nr_sectors;
@@ -868,9 +870,9 @@ int __drbd_change_sync(struct drbd_device *device, sector_t sector, int size,
nr_sectors = get_capacity(device->vdisk);
esector = sector + (size >> 9) - 1;
- if (!expect(sector < nr_sectors))
+ if (!expect(device, sector < nr_sectors))
goto out;
- if (!expect(esector < nr_sectors))
+ if (!expect(device, esector < nr_sectors))
esector = nr_sectors - 1;
lbnr = BM_SECT_TO_BIT(nr_sectors-1);
@@ -955,7 +957,9 @@ static int _is_in_al(struct drbd_device *device, unsigned int enr)
* @device: DRBD device.
* @sector: The sector number.
*
- * This functions sleeps on al_wait. Returns 0 on success, -EINTR if interrupted.
+ * This functions sleeps on al_wait.
+ *
+ * Returns: %0 on success, -EINTR if interrupted.
*/
int drbd_rs_begin_io(struct drbd_device *device, sector_t sector)
{
@@ -1002,21 +1006,24 @@ retry:
/**
* drbd_try_rs_begin_io() - Gets an extent in the resync LRU cache, does not sleep
- * @device: DRBD device.
+ * @peer_device: DRBD device.
* @sector: The sector number.
*
* Gets an extent in the resync LRU cache, sets it to BME_NO_WRITES, then
- * tries to set it to BME_LOCKED. Returns 0 upon success, and -EAGAIN
+ * tries to set it to BME_LOCKED.
+ *
+ * Returns: %0 upon success, and -EAGAIN
* if there is still application IO going on in this area.
*/
-int drbd_try_rs_begin_io(struct drbd_device *device, sector_t sector)
+int drbd_try_rs_begin_io(struct drbd_peer_device *peer_device, sector_t sector)
{
+ struct drbd_device *device = peer_device->device;
unsigned int enr = BM_SECT_TO_EXT(sector);
const unsigned int al_enr = enr*AL_EXT_PER_BM_SECT;
struct lc_element *e;
struct bm_extent *bm_ext;
int i;
- bool throttle = drbd_rs_should_slow_down(device, sector, true);
+ bool throttle = drbd_rs_should_slow_down(peer_device, sector, true);
/* If we need to throttle, a half-locked (only marked BME_NO_WRITES,
* not yet BME_LOCKED) extent needs to be kicked out explicitly if we
@@ -1143,7 +1150,7 @@ void drbd_rs_complete_io(struct drbd_device *device, sector_t sector)
bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
if (!bm_ext) {
spin_unlock_irqrestore(&device->al_lock, flags);
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "drbd_rs_complete_io() called, but extent not found\n");
return;
}
@@ -1187,7 +1194,7 @@ void drbd_rs_cancel_all(struct drbd_device *device)
* drbd_rs_del_all() - Gracefully remove all extents from the resync LRU
* @device: DRBD device.
*
- * Returns 0 upon success, -EAGAIN if at least one reference count was
+ * Returns: %0 upon success, -EAGAIN if at least one reference count was
* not zero.
*/
int drbd_rs_del_all(struct drbd_device *device)
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index 7d9db33363de..d90fa3e7f4cf 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_bitmap.c
@@ -113,7 +113,7 @@ struct drbd_bitmap {
static void __bm_print_lock_info(struct drbd_device *device, const char *func)
{
struct drbd_bitmap *b = device->bitmap;
- if (!__ratelimit(&drbd_ratelimit_state))
+ if (!drbd_ratelimit())
return;
drbd_err(device, "FIXME %s[%d] in %s, bitmap locked for '%s' by %s[%d]\n",
current->comm, task_pid_nr(current),
@@ -448,7 +448,7 @@ int drbd_bm_init(struct drbd_device *device)
sector_t drbd_bm_capacity(struct drbd_device *device)
{
- if (!expect(device->bitmap))
+ if (!expect(device, device->bitmap))
return 0;
return device->bitmap->bm_dev_capacity;
}
@@ -457,7 +457,7 @@ sector_t drbd_bm_capacity(struct drbd_device *device)
*/
void drbd_bm_cleanup(struct drbd_device *device)
{
- if (!expect(device->bitmap))
+ if (!expect(device, device->bitmap))
return;
bm_free_pages(device->bitmap->bm_pages, device->bitmap->bm_number_of_pages);
bm_vk_free(device->bitmap->bm_pages);
@@ -636,7 +636,7 @@ int drbd_bm_resize(struct drbd_device *device, sector_t capacity, int set_new_bi
int err = 0;
bool growing;
- if (!expect(b))
+ if (!expect(device, b))
return -ENOMEM;
drbd_bm_lock(device, "resize", BM_LOCKED_MASK);
@@ -757,9 +757,9 @@ unsigned long _drbd_bm_total_weight(struct drbd_device *device)
unsigned long s;
unsigned long flags;
- if (!expect(b))
+ if (!expect(device, b))
return 0;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return 0;
spin_lock_irqsave(&b->bm_lock, flags);
@@ -783,9 +783,9 @@ unsigned long drbd_bm_total_weight(struct drbd_device *device)
size_t drbd_bm_words(struct drbd_device *device)
{
struct drbd_bitmap *b = device->bitmap;
- if (!expect(b))
+ if (!expect(device, b))
return 0;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return 0;
return b->bm_words;
@@ -794,7 +794,7 @@ size_t drbd_bm_words(struct drbd_device *device)
unsigned long drbd_bm_bits(struct drbd_device *device)
{
struct drbd_bitmap *b = device->bitmap;
- if (!expect(b))
+ if (!expect(device, b))
return 0;
return b->bm_bits;
@@ -816,9 +816,9 @@ void drbd_bm_merge_lel(struct drbd_device *device, size_t offset, size_t number,
end = offset + number;
- if (!expect(b))
+ if (!expect(device, b))
return;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return;
if (number == 0)
return;
@@ -863,9 +863,9 @@ void drbd_bm_get_lel(struct drbd_device *device, size_t offset, size_t number,
end = offset + number;
- if (!expect(b))
+ if (!expect(device, b))
return;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return;
spin_lock_irq(&b->bm_lock);
@@ -894,9 +894,9 @@ void drbd_bm_get_lel(struct drbd_device *device, size_t offset, size_t number,
void drbd_bm_set_all(struct drbd_device *device)
{
struct drbd_bitmap *b = device->bitmap;
- if (!expect(b))
+ if (!expect(device, b))
return;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return;
spin_lock_irq(&b->bm_lock);
@@ -910,9 +910,9 @@ void drbd_bm_set_all(struct drbd_device *device)
void drbd_bm_clear_all(struct drbd_device *device)
{
struct drbd_bitmap *b = device->bitmap;
- if (!expect(b))
+ if (!expect(device, b))
return;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return;
spin_lock_irq(&b->bm_lock);
@@ -952,7 +952,7 @@ static void drbd_bm_endio(struct bio *bio)
bm_set_page_io_err(b->bm_pages[idx]);
/* Not identical to on disk version of it.
* Is BM_PAGE_IO_ERROR enough? */
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "IO ERROR %d on bitmap page idx %u\n",
bio->bi_status, idx);
} else {
@@ -1013,7 +1013,7 @@ static void bm_page_io_async(struct drbd_bm_aio_ctx *ctx, int page_nr) __must_ho
else
len = PAGE_SIZE;
} else {
- if (__ratelimit(&drbd_ratelimit_state)) {
+ if (drbd_ratelimit()) {
drbd_err(device, "Invalid offset during on-disk bitmap access: "
"page idx %u, sector %llu\n", page_nr, on_disk_sector);
}
@@ -1043,9 +1043,7 @@ static void bm_page_io_async(struct drbd_bm_aio_ctx *ctx, int page_nr) __must_ho
bio = bio_alloc_bioset(device->ldev->md_bdev, 1, op, GFP_NOIO,
&drbd_md_io_bio_set);
bio->bi_iter.bi_sector = on_disk_sector;
- /* bio_add_page of a single page to an empty bio will always succeed,
- * according to api. Do we want to assert that? */
- bio_add_page(bio, page, len, 0);
+ __bio_add_page(bio, page, len, 0);
bio->bi_private = ctx;
bio->bi_end_io = drbd_bm_endio;
@@ -1212,33 +1210,37 @@ static int bm_rw(struct drbd_device *device, const unsigned int flags, unsigned
return err;
}
-/**
+/*
* drbd_bm_read() - Read the whole bitmap from its on disk location.
* @device: DRBD device.
*/
-int drbd_bm_read(struct drbd_device *device) __must_hold(local)
+int drbd_bm_read(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
+
{
return bm_rw(device, BM_AIO_READ, 0);
}
-/**
+/*
* drbd_bm_write() - Write the whole bitmap to its on disk location.
* @device: DRBD device.
*
* Will only write pages that have changed since last IO.
*/
-int drbd_bm_write(struct drbd_device *device) __must_hold(local)
+int drbd_bm_write(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
{
return bm_rw(device, 0, 0);
}
-/**
+/*
* drbd_bm_write_all() - Write the whole bitmap to its on disk location.
* @device: DRBD device.
*
* Will write all pages.
*/
-int drbd_bm_write_all(struct drbd_device *device) __must_hold(local)
+int drbd_bm_write_all(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
{
return bm_rw(device, BM_AIO_WRITE_ALL_PAGES, 0);
}
@@ -1253,7 +1255,7 @@ int drbd_bm_write_lazy(struct drbd_device *device, unsigned upper_idx) __must_ho
return bm_rw(device, BM_AIO_COPY_PAGES, upper_idx);
}
-/**
+/*
* drbd_bm_write_copy_pages() - Write the whole bitmap to its on disk location.
* @device: DRBD device.
*
@@ -1264,12 +1266,13 @@ int drbd_bm_write_lazy(struct drbd_device *device, unsigned upper_idx) __must_ho
* verify is aborted due to a failed peer disk, while local IO continues, or
* pending resync acks are still being processed.
*/
-int drbd_bm_write_copy_pages(struct drbd_device *device) __must_hold(local)
+int drbd_bm_write_copy_pages(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
{
return bm_rw(device, BM_AIO_COPY_PAGES, 0);
}
-/**
+/*
* drbd_bm_write_hinted() - Write bitmap pages with "hint" marks, if they have changed.
* @device: DRBD device.
*/
@@ -1332,9 +1335,9 @@ static unsigned long bm_find_next(struct drbd_device *device,
struct drbd_bitmap *b = device->bitmap;
unsigned long i = DRBD_END_OF_BITMAP;
- if (!expect(b))
+ if (!expect(device, b))
return i;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return i;
spin_lock_irq(&b->bm_lock);
@@ -1436,9 +1439,9 @@ static int bm_change_bits_to(struct drbd_device *device, const unsigned long s,
struct drbd_bitmap *b = device->bitmap;
int c = 0;
- if (!expect(b))
+ if (!expect(device, b))
return 1;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return 0;
spin_lock_irqsave(&b->bm_lock, flags);
@@ -1582,9 +1585,9 @@ int drbd_bm_test_bit(struct drbd_device *device, const unsigned long bitnr)
unsigned long *p_addr;
int i;
- if (!expect(b))
+ if (!expect(device, b))
return 0;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return 0;
spin_lock_irqsave(&b->bm_lock, flags);
@@ -1619,9 +1622,9 @@ int drbd_bm_count_bits(struct drbd_device *device, const unsigned long s, const
* robust in case we screwed up elsewhere, in that case pretend there
* was one dirty bit in the requested area, so we won't try to do a
* local read there (no bitmap probably implies no disk) */
- if (!expect(b))
+ if (!expect(device, b))
return 1;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return 1;
spin_lock_irqsave(&b->bm_lock, flags);
@@ -1635,7 +1638,7 @@ int drbd_bm_count_bits(struct drbd_device *device, const unsigned long s, const
bm_unmap(p_addr);
p_addr = bm_map_pidx(b, idx);
}
- if (expect(bitnr < b->bm_bits))
+ if (expect(device, bitnr < b->bm_bits))
c += (0 != test_bit_le(bitnr - (page_nr << (PAGE_SHIFT+3)), p_addr));
else
drbd_err(device, "bitnr=%lu bm_bits=%lu\n", bitnr, b->bm_bits);
@@ -1668,9 +1671,9 @@ int drbd_bm_e_weight(struct drbd_device *device, unsigned long enr)
unsigned long flags;
unsigned long *p_addr, *bm;
- if (!expect(b))
+ if (!expect(device, b))
return 0;
- if (!expect(b->bm_pages))
+ if (!expect(device, b->bm_pages))
return 0;
spin_lock_irqsave(&b->bm_lock, flags);
diff --git a/drivers/block/drbd/drbd_buildtag.c b/drivers/block/drbd/drbd_buildtag.c
new file mode 100644
index 000000000000..cb1aa66d7d5d
--- /dev/null
+++ b/drivers/block/drbd/drbd_buildtag.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/drbd_config.h>
+#include <linux/module.h>
+
+const char *drbd_buildtag(void)
+{
+ /* DRBD built from external sources has here a reference to the
+ * git hash of the source code.
+ */
+
+ static char buildtag[38] = "\0uilt-in";
+
+ if (buildtag[0] == 0) {
+#ifdef MODULE
+ sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
+#else
+ buildtag[0] = 'b';
+#endif
+ }
+
+ return buildtag;
+}
diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c
index b3b9cd5628fd..12460b584bcb 100644
--- a/drivers/block/drbd/drbd_debugfs.c
+++ b/drivers/block/drbd/drbd_debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "drbd debugfs: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
@@ -844,7 +844,7 @@ static int drbd_version_show(struct seq_file *m, void *ignored)
{
seq_printf(m, "# %s\n", drbd_buildtag());
seq_printf(m, "VERSION=%s\n", REL_VERSION);
- seq_printf(m, "API_VERSION=%u\n", API_VERSION);
+ seq_printf(m, "API_VERSION=%u\n", GENL_MAGIC_VERSION);
seq_printf(m, "PRO_VERSION_MIN=%u\n", PRO_VERSION_MIN);
seq_printf(m, "PRO_VERSION_MAX=%u\n", PRO_VERSION_MAX);
return 0;
diff --git a/drivers/block/drbd/drbd_debugfs.h b/drivers/block/drbd/drbd_debugfs.h
index 58e31cef0844..ee3d66eb40c6 100644
--- a/drivers/block/drbd/drbd_debugfs.h
+++ b/drivers/block/drbd/drbd_debugfs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/debugfs.h>
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index f15f2f041596..f6d6276974ee 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
drbd_int.h
@@ -34,19 +34,11 @@
#include <linux/prefetch.h>
#include <linux/drbd_genl_api.h>
#include <linux/drbd.h>
+#include <linux/drbd_config.h>
#include "drbd_strings.h"
#include "drbd_state.h"
#include "drbd_protocol.h"
-
-#ifdef __CHECKER__
-# define __protected_by(x) __attribute__((require_context(x,1,999,"rdwr")))
-# define __protected_read_by(x) __attribute__((require_context(x,1,999,"read")))
-# define __protected_write_by(x) __attribute__((require_context(x,1,999,"write")))
-#else
-# define __protected_by(x)
-# define __protected_read_by(x)
-# define __protected_write_by(x)
-#endif
+#include "drbd_polymorph_printk.h"
/* shared module parameters, defined in drbd_main.c */
#ifdef CONFIG_DRBD_FAULT_INJECTION
@@ -74,71 +66,7 @@ extern int drbd_proc_details;
struct drbd_device;
struct drbd_connection;
-
-#define __drbd_printk_device(level, device, fmt, args...) \
- dev_printk(level, disk_to_dev((device)->vdisk), fmt, ## args)
-#define __drbd_printk_peer_device(level, peer_device, fmt, args...) \
- dev_printk(level, disk_to_dev((peer_device)->device->vdisk), fmt, ## args)
-#define __drbd_printk_resource(level, resource, fmt, args...) \
- printk(level "drbd %s: " fmt, (resource)->name, ## args)
-#define __drbd_printk_connection(level, connection, fmt, args...) \
- printk(level "drbd %s: " fmt, (connection)->resource->name, ## args)
-
-void drbd_printk_with_wrong_object_type(void);
-
-#define __drbd_printk_if_same_type(obj, type, func, level, fmt, args...) \
- (__builtin_types_compatible_p(typeof(obj), type) || \
- __builtin_types_compatible_p(typeof(obj), const type)), \
- func(level, (const type)(obj), fmt, ## args)
-
-#define drbd_printk(level, obj, fmt, args...) \
- __builtin_choose_expr( \
- __drbd_printk_if_same_type(obj, struct drbd_device *, \
- __drbd_printk_device, level, fmt, ## args), \
- __builtin_choose_expr( \
- __drbd_printk_if_same_type(obj, struct drbd_resource *, \
- __drbd_printk_resource, level, fmt, ## args), \
- __builtin_choose_expr( \
- __drbd_printk_if_same_type(obj, struct drbd_connection *, \
- __drbd_printk_connection, level, fmt, ## args), \
- __builtin_choose_expr( \
- __drbd_printk_if_same_type(obj, struct drbd_peer_device *, \
- __drbd_printk_peer_device, level, fmt, ## args), \
- drbd_printk_with_wrong_object_type()))))
-
-#define drbd_dbg(obj, fmt, args...) \
- drbd_printk(KERN_DEBUG, obj, fmt, ## args)
-#define drbd_alert(obj, fmt, args...) \
- drbd_printk(KERN_ALERT, obj, fmt, ## args)
-#define drbd_err(obj, fmt, args...) \
- drbd_printk(KERN_ERR, obj, fmt, ## args)
-#define drbd_warn(obj, fmt, args...) \
- drbd_printk(KERN_WARNING, obj, fmt, ## args)
-#define drbd_info(obj, fmt, args...) \
- drbd_printk(KERN_INFO, obj, fmt, ## args)
-#define drbd_emerg(obj, fmt, args...) \
- drbd_printk(KERN_EMERG, obj, fmt, ## args)
-
-#define dynamic_drbd_dbg(device, fmt, args...) \
- dynamic_dev_dbg(disk_to_dev(device->vdisk), fmt, ## args)
-
-#define D_ASSERT(device, exp) do { \
- if (!(exp)) \
- drbd_err(device, "ASSERT( " #exp " ) in %s:%d\n", __FILE__, __LINE__); \
- } while (0)
-
-/**
- * expect - Make an assertion
- *
- * Unlike the assert macro, this macro returns a boolean result.
- */
-#define expect(exp) ({ \
- bool _bool = (exp); \
- if (!_bool) \
- drbd_err(device, "ASSERTION %s FAILED in %s\n", \
- #exp, __func__); \
- _bool; \
- })
+struct drbd_peer_device;
/* Defines to control fault insertion */
enum {
@@ -199,8 +127,8 @@ struct bm_xfer_ctx {
unsigned bytes[2];
};
-extern void INFO_bm_xfer_stats(struct drbd_device *device,
- const char *direction, struct bm_xfer_ctx *c);
+extern void INFO_bm_xfer_stats(struct drbd_peer_device *peer_device,
+ const char *direction, struct bm_xfer_ctx *c);
static inline void bm_xfer_ctx_bit_to_word_offset(struct bm_xfer_ctx *c)
{
@@ -369,10 +297,6 @@ struct drbd_epoch {
unsigned long flags;
};
-/* Prototype declaration of function defined in drbd_receiver.c */
-int drbdd_init(struct drbd_thread *);
-int drbd_asender(struct drbd_thread *);
-
/* drbd_epoch flag bits */
enum {
DE_HAVE_BARRIER_NUMBER,
@@ -395,6 +319,7 @@ struct drbd_peer_request {
struct drbd_peer_device *peer_device;
struct drbd_epoch *epoch; /* for writes */
struct page *pages;
+ blk_opf_t opf;
atomic_t pending_bios;
struct drbd_interval i;
/* see comments on ee flag bits below */
@@ -406,6 +331,10 @@ struct drbd_peer_request {
};
};
+/* Equivalent to bio_op and req_op. */
+#define peer_req_op(peer_req) \
+ ((peer_req)->opf & REQ_OP_MASK)
+
/* ee flag bits.
* While corresponding bios are in flight, the only modification will be
* set_bit WAS_ERROR, which has to be atomic.
@@ -451,6 +380,9 @@ enum {
/* this is/was a write request */
__EE_WRITE,
+ /* hand back using mempool_free(e, drbd_buffer_page_pool) */
+ __EE_RELEASE_TO_MEMPOOL,
+
/* this is/was a write same request */
__EE_WRITE_SAME,
@@ -473,6 +405,7 @@ enum {
#define EE_IN_INTERVAL_TREE (1<<__EE_IN_INTERVAL_TREE)
#define EE_SUBMITTED (1<<__EE_SUBMITTED)
#define EE_WRITE (1<<__EE_WRITE)
+#define EE_RELEASE_TO_MEMPOOL (1<<__EE_RELEASE_TO_MEMPOOL)
#define EE_WRITE_SAME (1<<__EE_WRITE_SAME)
#define EE_APPLICATION (1<<__EE_APPLICATION)
#define EE_RS_THIN_REQ (1<<__EE_RS_THIN_REQ)
@@ -591,7 +524,9 @@ struct drbd_md {
struct drbd_backing_dev {
struct block_device *backing_bdev;
+ struct file *backing_bdev_file;
struct block_device *md_bdev;
+ struct file *f_md_bdev;
struct drbd_md md;
struct disk_conf *disk_conf; /* RCU, for updates: resource->conf_update */
sector_t known_size; /* last known size of that backing device */
@@ -609,9 +544,10 @@ struct drbd_md_io {
struct bm_io_work {
struct drbd_work w;
+ struct drbd_peer_device *peer_device;
char *why;
enum bm_flag flags;
- int (*io_fn)(struct drbd_device *device);
+ int (*io_fn)(struct drbd_device *device, struct drbd_peer_device *peer_device);
void (*done)(struct drbd_device *device, int rv);
};
@@ -619,7 +555,7 @@ struct fifo_buffer {
unsigned int head_index;
unsigned int size;
int total; /* sum of all values */
- int values[];
+ int values[] __counted_by(size);
};
extern struct fifo_buffer *fifo_alloc(unsigned int fifo_size);
@@ -833,7 +769,7 @@ struct drbd_device {
unsigned long flags;
/* configured by drbdsetup */
- struct drbd_backing_dev *ldev __protected_by(local);
+ struct drbd_backing_dev *ldev;
sector_t p_size; /* partner's disk size */
struct request_queue *rq_queue;
@@ -926,9 +862,7 @@ struct drbd_device {
struct list_head sync_ee; /* IO in progress (P_RS_DATA_REPLY gets written to disk) */
struct list_head done_ee; /* need to send P_WRITE_ACK */
struct list_head read_ee; /* [RS]P_DATA_REQUEST being read */
- struct list_head net_ee; /* zero-copy network send in progress */
- int next_barrier_nr;
struct list_head resync_reads;
atomic_t pp_in_use; /* allocated from page pool */
atomic_t pp_in_use_by_net; /* sendpage()d, still referenced by tcp */
@@ -1109,7 +1043,7 @@ extern int drbd_send_drequest_csum(struct drbd_peer_device *, sector_t sector,
enum drbd_packet cmd);
extern int drbd_send_ov_request(struct drbd_peer_device *, sector_t sector, int size);
-extern int drbd_send_bitmap(struct drbd_device *device);
+extern int drbd_send_bitmap(struct drbd_device *device, struct drbd_peer_device *peer_device);
extern void drbd_send_sr_reply(struct drbd_peer_device *, enum drbd_state_rv retcode);
extern void conn_send_sr_reply(struct drbd_connection *connection, enum drbd_state_rv retcode);
extern int drbd_send_rs_deallocated(struct drbd_peer_device *, struct drbd_peer_request *);
@@ -1133,17 +1067,22 @@ extern void drbd_md_clear_flag(struct drbd_device *device, int flags)__must_hold
extern int drbd_md_test_flag(struct drbd_backing_dev *, int);
extern void drbd_md_mark_dirty(struct drbd_device *device);
extern void drbd_queue_bitmap_io(struct drbd_device *device,
- int (*io_fn)(struct drbd_device *),
+ int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
void (*done)(struct drbd_device *, int),
- char *why, enum bm_flag flags);
+ char *why, enum bm_flag flags,
+ struct drbd_peer_device *peer_device);
extern int drbd_bitmap_io(struct drbd_device *device,
- int (*io_fn)(struct drbd_device *),
- char *why, enum bm_flag flags);
+ int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
+ char *why, enum bm_flag flags,
+ struct drbd_peer_device *peer_device);
extern int drbd_bitmap_io_from_worker(struct drbd_device *device,
- int (*io_fn)(struct drbd_device *),
- char *why, enum bm_flag flags);
-extern int drbd_bmio_set_n_write(struct drbd_device *device) __must_hold(local);
-extern int drbd_bmio_clear_n_write(struct drbd_device *device) __must_hold(local);
+ int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
+ char *why, enum bm_flag flags,
+ struct drbd_peer_device *peer_device);
+extern int drbd_bmio_set_n_write(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local);
+extern int drbd_bmio_clear_n_write(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local);
/* Meta data layout
*
@@ -1352,14 +1291,18 @@ extern void _drbd_bm_set_bits(struct drbd_device *device,
const unsigned long s, const unsigned long e);
extern int drbd_bm_test_bit(struct drbd_device *device, unsigned long bitnr);
extern int drbd_bm_e_weight(struct drbd_device *device, unsigned long enr);
-extern int drbd_bm_read(struct drbd_device *device) __must_hold(local);
+extern int drbd_bm_read(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local);
extern void drbd_bm_mark_for_writeout(struct drbd_device *device, int page_nr);
-extern int drbd_bm_write(struct drbd_device *device) __must_hold(local);
+extern int drbd_bm_write(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local);
extern void drbd_bm_reset_al_hints(struct drbd_device *device) __must_hold(local);
extern int drbd_bm_write_hinted(struct drbd_device *device) __must_hold(local);
extern int drbd_bm_write_lazy(struct drbd_device *device, unsigned upper_idx) __must_hold(local);
-extern int drbd_bm_write_all(struct drbd_device *device) __must_hold(local);
-extern int drbd_bm_write_copy_pages(struct drbd_device *device) __must_hold(local);
+extern int drbd_bm_write_all(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local);
+extern int drbd_bm_write_copy_pages(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local);
extern size_t drbd_bm_words(struct drbd_device *device);
extern unsigned long drbd_bm_bits(struct drbd_device *device);
extern sector_t drbd_bm_capacity(struct drbd_device *device);
@@ -1389,24 +1332,6 @@ extern struct kmem_cache *drbd_al_ext_cache; /* activity log extents */
extern mempool_t drbd_request_mempool;
extern mempool_t drbd_ee_mempool;
-/* drbd's page pool, used to buffer data received from the peer,
- * or data requested by the peer.
- *
- * This does not have an emergency reserve.
- *
- * When allocating from this pool, it first takes pages from the pool.
- * Only if the pool is depleted will try to allocate from the system.
- *
- * The assumption is that pages taken from this pool will be processed,
- * and given back, "quickly", and then can be recycled, so we can avoid
- * frequent calls to alloc_page(), and still will be able to make progress even
- * under memory pressure.
- */
-extern struct page *drbd_pp_pool;
-extern spinlock_t drbd_pp_lock;
-extern int drbd_pp_vacant;
-extern wait_queue_head_t drbd_pp_wait;
-
/* We also need a standard (emergency-reserve backed) page pool
* for meta data IO (activity log, bitmap).
* We can keep it global, as long as it is used as "N pages at a time".
@@ -1414,6 +1339,7 @@ extern wait_queue_head_t drbd_pp_wait;
*/
#define DRBD_MIN_POOL_PAGES 128
extern mempool_t drbd_md_io_page_pool;
+extern mempool_t drbd_buffer_page_pool;
/* We also need to make sure we get a bio
* when we need it for housekeeping purposes */
@@ -1424,7 +1350,6 @@ extern struct bio_set drbd_io_bio_set;
extern struct mutex resources_mutex;
-extern int conn_lowest_minor(struct drbd_connection *connection);
extern enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsigned int minor);
extern void drbd_destroy_device(struct kref *kref);
extern void drbd_delete_device(struct drbd_device *device);
@@ -1445,9 +1370,6 @@ extern void conn_free_crypto(struct drbd_connection *connection);
extern void do_submit(struct work_struct *ws);
extern void __drbd_make_request(struct drbd_device *, struct bio *);
void drbd_submit_bio(struct bio *bio);
-extern int drbd_read_remote(struct drbd_device *device, struct drbd_request *req);
-extern int is_valid_ar_handle(struct drbd_request *, sector_t);
-
/* drbd_nl.c */
@@ -1490,21 +1412,24 @@ void drbd_resync_after_changed(struct drbd_device *device);
extern void drbd_start_resync(struct drbd_device *device, enum drbd_conns side);
extern void resume_next_sg(struct drbd_device *device);
extern void suspend_other_sg(struct drbd_device *device);
-extern int drbd_resync_finished(struct drbd_device *device);
+extern int drbd_resync_finished(struct drbd_peer_device *peer_device);
/* maybe rather drbd_main.c ? */
extern void *drbd_md_get_buffer(struct drbd_device *device, const char *intent);
extern void drbd_md_put_buffer(struct drbd_device *device);
extern int drbd_md_sync_page_io(struct drbd_device *device,
struct drbd_backing_dev *bdev, sector_t sector, enum req_op op);
-extern void drbd_ov_out_of_sync_found(struct drbd_device *, sector_t, int);
+extern void drbd_ov_out_of_sync_found(struct drbd_peer_device *peer_device,
+ sector_t sector, int size);
extern void wait_until_done_or_force_detached(struct drbd_device *device,
struct drbd_backing_dev *bdev, unsigned int *done);
-extern void drbd_rs_controller_reset(struct drbd_device *device);
+extern void drbd_rs_controller_reset(struct drbd_peer_device *peer_device);
-static inline void ov_out_of_sync_print(struct drbd_device *device)
+static inline void ov_out_of_sync_print(struct drbd_peer_device *peer_device)
{
+ struct drbd_device *device = peer_device->device;
+
if (device->ov_last_oos_size) {
- drbd_err(device, "Out of sync: start=%llu, size=%lu (sectors)\n",
+ drbd_err(peer_device, "Out of sync: start=%llu, size=%lu (sectors)\n",
(unsigned long long)device->ov_last_oos_start,
(unsigned long)device->ov_last_oos_size);
}
@@ -1526,10 +1451,8 @@ extern int w_resync_timer(struct drbd_work *, int);
extern int w_send_write_hint(struct drbd_work *, int);
extern int w_send_dblock(struct drbd_work *, int);
extern int w_send_read_req(struct drbd_work *, int);
-extern int w_e_reissue(struct drbd_work *, int);
extern int w_restart_disk_io(struct drbd_work *, int);
extern int w_send_out_of_sync(struct drbd_work *, int);
-extern int w_start_resync(struct drbd_work *, int);
extern void resync_timer_fn(struct timer_list *t);
extern void start_resync_timer_fn(struct timer_list *t);
@@ -1541,24 +1464,18 @@ extern int drbd_issue_discard_or_zero_out(struct drbd_device *device,
sector_t start, unsigned int nr_sectors, int flags);
extern int drbd_receiver(struct drbd_thread *thi);
extern int drbd_ack_receiver(struct drbd_thread *thi);
-extern void drbd_send_ping_wf(struct work_struct *ws);
extern void drbd_send_acks_wf(struct work_struct *ws);
extern bool drbd_rs_c_min_rate_throttle(struct drbd_device *device);
-extern bool drbd_rs_should_slow_down(struct drbd_device *device, sector_t sector,
+extern bool drbd_rs_should_slow_down(struct drbd_peer_device *peer_device, sector_t sector,
bool throttle_if_app_is_waiting);
-extern int drbd_submit_peer_request(struct drbd_device *,
- struct drbd_peer_request *, blk_opf_t, int);
+extern int drbd_submit_peer_request(struct drbd_peer_request *peer_req);
extern int drbd_free_peer_reqs(struct drbd_device *, struct list_head *);
extern struct drbd_peer_request *drbd_alloc_peer_req(struct drbd_peer_device *, u64,
sector_t, unsigned int,
unsigned int,
gfp_t) __must_hold(local);
-extern void __drbd_free_peer_req(struct drbd_device *, struct drbd_peer_request *,
- int);
-#define drbd_free_peer_req(m,e) __drbd_free_peer_req(m, e, 0)
-#define drbd_free_net_peer_req(m,e) __drbd_free_peer_req(m, e, 1)
+extern void drbd_free_peer_req(struct drbd_device *device, struct drbd_peer_request *req);
extern struct page *drbd_alloc_pages(struct drbd_peer_device *, unsigned int, bool);
-extern void drbd_set_recv_tcq(struct drbd_device *device, int tcq_enabled);
extern void _drbd_clear_done_ee(struct drbd_device *device, struct list_head *to_be_freed);
extern int drbd_connected(struct drbd_peer_device *);
@@ -1601,22 +1518,22 @@ extern void drbd_al_begin_io(struct drbd_device *device, struct drbd_interval *i
extern void drbd_al_complete_io(struct drbd_device *device, struct drbd_interval *i);
extern void drbd_rs_complete_io(struct drbd_device *device, sector_t sector);
extern int drbd_rs_begin_io(struct drbd_device *device, sector_t sector);
-extern int drbd_try_rs_begin_io(struct drbd_device *device, sector_t sector);
+extern int drbd_try_rs_begin_io(struct drbd_peer_device *peer_device, sector_t sector);
extern void drbd_rs_cancel_all(struct drbd_device *device);
extern int drbd_rs_del_all(struct drbd_device *device);
-extern void drbd_rs_failed_io(struct drbd_device *device,
+extern void drbd_rs_failed_io(struct drbd_peer_device *peer_device,
sector_t sector, int size);
-extern void drbd_advance_rs_marks(struct drbd_device *device, unsigned long still_to_go);
+extern void drbd_advance_rs_marks(struct drbd_peer_device *peer_device, unsigned long still_to_go);
enum update_sync_bits_mode { RECORD_RS_FAILED, SET_OUT_OF_SYNC, SET_IN_SYNC };
-extern int __drbd_change_sync(struct drbd_device *device, sector_t sector, int size,
+extern int __drbd_change_sync(struct drbd_peer_device *peer_device, sector_t sector, int size,
enum update_sync_bits_mode mode);
-#define drbd_set_in_sync(device, sector, size) \
- __drbd_change_sync(device, sector, size, SET_IN_SYNC)
-#define drbd_set_out_of_sync(device, sector, size) \
- __drbd_change_sync(device, sector, size, SET_OUT_OF_SYNC)
-#define drbd_rs_failed_io(device, sector, size) \
- __drbd_change_sync(device, sector, size, RECORD_RS_FAILED)
+#define drbd_set_in_sync(peer_device, sector, size) \
+ __drbd_change_sync(peer_device, sector, size, SET_IN_SYNC)
+#define drbd_set_out_of_sync(peer_device, sector, size) \
+ __drbd_change_sync(peer_device, sector, size, SET_OUT_OF_SYNC)
+#define drbd_rs_failed_io(peer_device, sector, size) \
+ __drbd_change_sync(peer_device, sector, size, RECORD_RS_FAILED)
extern void drbd_al_shrink(struct drbd_device *device);
extern int drbd_al_initialize(struct drbd_device *, void *);
@@ -1676,16 +1593,6 @@ static inline struct page *page_chain_next(struct page *page)
for (; page && ({ n = page_chain_next(page); 1; }); page = n)
-static inline int drbd_peer_req_has_active_page(struct drbd_peer_request *peer_req)
-{
- struct page *page = peer_req->pages;
- page_chain_for_each(page) {
- if (page_count(page) > 1)
- return 1;
- }
- return 0;
-}
-
static inline union drbd_state drbd_read_state(struct drbd_device *device)
{
struct drbd_resource *resource = device->resource;
@@ -1719,7 +1626,7 @@ static inline void __drbd_chk_io_error_(struct drbd_device *device,
switch (ep) {
case EP_PASS_ON: /* FIXME would this be better named "Ignore"? */
if (df == DRBD_READ_ERROR || df == DRBD_WRITE_ERROR) {
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "Local IO failed in %s.\n", where);
if (device->state.disk > D_INCONSISTENT)
_drbd_set_state(_NS(device, disk, D_INCONSISTENT), CS_HARD, NULL);
@@ -1988,18 +1895,14 @@ static inline void inc_ap_pending(struct drbd_device *device)
atomic_inc(&device->ap_pending_cnt);
}
-#define ERR_IF_CNT_IS_NEGATIVE(which, func, line) \
- if (atomic_read(&device->which) < 0) \
- drbd_err(device, "in %s:%d: " #which " = %d < 0 !\n", \
- func, line, \
- atomic_read(&device->which))
-
-#define dec_ap_pending(device) _dec_ap_pending(device, __func__, __LINE__)
-static inline void _dec_ap_pending(struct drbd_device *device, const char *func, int line)
+#define dec_ap_pending(device) ((void)expect((device), __dec_ap_pending(device) >= 0))
+static inline int __dec_ap_pending(struct drbd_device *device)
{
- if (atomic_dec_and_test(&device->ap_pending_cnt))
+ int ap_pending_cnt = atomic_dec_return(&device->ap_pending_cnt);
+
+ if (ap_pending_cnt == 0)
wake_up(&device->misc_wait);
- ERR_IF_CNT_IS_NEGATIVE(ap_pending_cnt, func, line);
+ return ap_pending_cnt;
}
/* counts how many resync-related answers we still expect from the peer
@@ -2008,16 +1911,16 @@ static inline void _dec_ap_pending(struct drbd_device *device, const char *func,
* C_SYNC_SOURCE sends P_RS_DATA_REPLY (and expects P_WRITE_ACK with ID_SYNCER)
* (or P_NEG_ACK with ID_SYNCER)
*/
-static inline void inc_rs_pending(struct drbd_device *device)
+static inline void inc_rs_pending(struct drbd_peer_device *peer_device)
{
- atomic_inc(&device->rs_pending_cnt);
+ atomic_inc(&peer_device->device->rs_pending_cnt);
}
-#define dec_rs_pending(device) _dec_rs_pending(device, __func__, __LINE__)
-static inline void _dec_rs_pending(struct drbd_device *device, const char *func, int line)
+#define dec_rs_pending(peer_device) \
+ ((void)expect((peer_device), __dec_rs_pending(peer_device) >= 0))
+static inline int __dec_rs_pending(struct drbd_peer_device *peer_device)
{
- atomic_dec(&device->rs_pending_cnt);
- ERR_IF_CNT_IS_NEGATIVE(rs_pending_cnt, func, line);
+ return atomic_dec_return(&peer_device->device->rs_pending_cnt);
}
/* counts how many answers we still need to send to the peer.
@@ -2034,18 +1937,16 @@ static inline void inc_unacked(struct drbd_device *device)
atomic_inc(&device->unacked_cnt);
}
-#define dec_unacked(device) _dec_unacked(device, __func__, __LINE__)
-static inline void _dec_unacked(struct drbd_device *device, const char *func, int line)
+#define dec_unacked(device) ((void)expect(device, __dec_unacked(device) >= 0))
+static inline int __dec_unacked(struct drbd_device *device)
{
- atomic_dec(&device->unacked_cnt);
- ERR_IF_CNT_IS_NEGATIVE(unacked_cnt, func, line);
+ return atomic_dec_return(&device->unacked_cnt);
}
-#define sub_unacked(device, n) _sub_unacked(device, n, __func__, __LINE__)
-static inline void _sub_unacked(struct drbd_device *device, int n, const char *func, int line)
+#define sub_unacked(device, n) ((void)expect(device, __sub_unacked(device) >= 0))
+static inline int __sub_unacked(struct drbd_device *device, int n)
{
- atomic_sub(n, &device->unacked_cnt);
- ERR_IF_CNT_IS_NEGATIVE(unacked_cnt, func, line);
+ return atomic_sub_return(n, &device->unacked_cnt);
}
static inline bool is_sync_target_state(enum drbd_conns connection_state)
diff --git a/drivers/block/drbd/drbd_interval.c b/drivers/block/drbd/drbd_interval.c
index f07b4378388b..873beda6de24 100644
--- a/drivers/block/drbd/drbd_interval.c
+++ b/drivers/block/drbd/drbd_interval.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
#include <asm/bug.h>
#include <linux/rbtree_augmented.h>
#include "drbd_interval.h"
@@ -58,7 +58,7 @@ drbd_insert_interval(struct rb_root *root, struct drbd_interval *this)
* drbd_contains_interval - check if a tree contains a given interval
* @root: red black tree root
* @sector: start sector of @interval
- * @interval: may not be a valid pointer
+ * @interval: may be an invalid pointer
*
* Returns if the tree contains the node @interval with start sector @start.
* Does not dereference @interval until @interval is known to be a valid object
@@ -95,6 +95,10 @@ drbd_contains_interval(struct rb_root *root, sector_t sector,
void
drbd_remove_interval(struct rb_root *root, struct drbd_interval *this)
{
+ /* avoid endless loop */
+ if (drbd_interval_empty(this))
+ return;
+
rb_erase_augmented(&this->rb, root, &augment_callbacks);
}
diff --git a/drivers/block/drbd/drbd_interval.h b/drivers/block/drbd/drbd_interval.h
index b8c2dee5edc8..366489b72fe9 100644
--- a/drivers/block/drbd/drbd_interval.h
+++ b/drivers/block/drbd/drbd_interval.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __DRBD_INTERVAL_H
#define __DRBD_INTERVAL_H
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index f3e4db16fd07..c73376886e7a 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd.c
@@ -37,7 +37,6 @@
#include <linux/notifier.h>
#include <linux/kthread.h>
#include <linux/workqueue.h>
-#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <linux/vmalloc.h>
#include <linux/sched/signal.h>
@@ -50,8 +49,8 @@
#include "drbd_debugfs.h"
static DEFINE_MUTEX(drbd_main_mutex);
-static int drbd_open(struct block_device *bdev, fmode_t mode);
-static void drbd_release(struct gendisk *gd, fmode_t mode);
+static int drbd_open(struct gendisk *disk, blk_mode_t mode);
+static void drbd_release(struct gendisk *gd);
static void md_sync_timer_fn(struct timer_list *t);
static int w_bitmap_io(struct drbd_work *w, int unused);
@@ -115,20 +114,10 @@ struct kmem_cache *drbd_al_ext_cache; /* activity log extents */
mempool_t drbd_request_mempool;
mempool_t drbd_ee_mempool;
mempool_t drbd_md_io_page_pool;
+mempool_t drbd_buffer_page_pool;
struct bio_set drbd_md_io_bio_set;
struct bio_set drbd_io_bio_set;
-/* I do not use a standard mempool, because:
- 1) I want to hand out the pre-allocated objects first.
- 2) I want to be able to interrupt sleeping allocation with a signal.
- Note: This is a single linked list, the next pointer is the private
- member of struct page.
- */
-struct page *drbd_pp_pool;
-DEFINE_SPINLOCK(drbd_pp_lock);
-int drbd_pp_vacant;
-wait_queue_head_t drbd_pp_wait;
-
DEFINE_RATELIMIT_STATE(drbd_ratelimit_state, 5 * HZ, 5);
static const struct block_device_operations drbd_ops = {
@@ -231,9 +220,11 @@ void tl_release(struct drbd_connection *connection, unsigned int barrier_nr,
}
req = list_prepare_entry(tmp, &connection->transfer_log, tl_requests);
list_for_each_entry_safe_from(req, r, &connection->transfer_log, tl_requests) {
+ struct drbd_peer_device *peer_device;
if (req->epoch != expect_epoch)
break;
- _req_mod(req, BARRIER_ACKED);
+ peer_device = conn_peer_device(connection, req->device->vnr);
+ _req_mod(req, BARRIER_ACKED, peer_device);
}
spin_unlock_irq(&connection->resource->req_lock);
@@ -256,10 +247,13 @@ bail:
/* must hold resource->req_lock */
void _tl_restart(struct drbd_connection *connection, enum drbd_req_event what)
{
+ struct drbd_peer_device *peer_device;
struct drbd_request *req, *r;
- list_for_each_entry_safe(req, r, &connection->transfer_log, tl_requests)
- _req_mod(req, what);
+ list_for_each_entry_safe(req, r, &connection->transfer_log, tl_requests) {
+ peer_device = conn_peer_device(connection, req->device->vnr);
+ _req_mod(req, what, peer_device);
+ }
}
void tl_restart(struct drbd_connection *connection, enum drbd_req_event what)
@@ -297,7 +291,7 @@ void tl_abort_disk_io(struct drbd_device *device)
continue;
if (req->device != device)
continue;
- _req_mod(req, ABORT_DISK_IO);
+ _req_mod(req, ABORT_DISK_IO, NULL);
}
spin_unlock_irq(&connection->resource->req_lock);
}
@@ -467,20 +461,6 @@ void _drbd_thread_stop(struct drbd_thread *thi, int restart, int wait)
wait_for_completion(&thi->stop);
}
-int conn_lowest_minor(struct drbd_connection *connection)
-{
- struct drbd_peer_device *peer_device;
- int vnr = 0, minor = -1;
-
- rcu_read_lock();
- peer_device = idr_get_next(&connection->peer_devices, &vnr);
- if (peer_device)
- minor = device_to_minor(peer_device->device);
- rcu_read_unlock();
-
- return minor;
-}
-
#ifdef CONFIG_SMP
/*
* drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs
@@ -1198,10 +1178,11 @@ static int fill_bitmap_rle_bits(struct drbd_device *device,
* code upon failure.
*/
static int
-send_bitmap_rle_or_plain(struct drbd_device *device, struct bm_xfer_ctx *c)
+send_bitmap_rle_or_plain(struct drbd_peer_device *peer_device, struct bm_xfer_ctx *c)
{
- struct drbd_socket *sock = &first_peer_device(device)->connection->data;
- unsigned int header_size = drbd_header_size(first_peer_device(device)->connection);
+ struct drbd_device *device = peer_device->device;
+ struct drbd_socket *sock = &peer_device->connection->data;
+ unsigned int header_size = drbd_header_size(peer_device->connection);
struct p_compressed_bm *p = sock->sbuf + header_size;
int len, err;
@@ -1212,7 +1193,7 @@ send_bitmap_rle_or_plain(struct drbd_device *device, struct bm_xfer_ctx *c)
if (len) {
dcbp_set_code(p, RLE_VLI_Bits);
- err = __send_command(first_peer_device(device)->connection, device->vnr, sock,
+ err = __send_command(peer_device->connection, device->vnr, sock,
P_COMPRESSED_BITMAP, sizeof(*p) + len,
NULL, 0);
c->packets[0]++;
@@ -1233,7 +1214,8 @@ send_bitmap_rle_or_plain(struct drbd_device *device, struct bm_xfer_ctx *c)
len = num_words * sizeof(*p);
if (len)
drbd_bm_get_lel(device, c->word_offset, num_words, p);
- err = __send_command(first_peer_device(device)->connection, device->vnr, sock, P_BITMAP, len, NULL, 0);
+ err = __send_command(peer_device->connection, device->vnr, sock, P_BITMAP,
+ len, NULL, 0);
c->word_offset += num_words;
c->bit_offset = c->word_offset * BITS_PER_LONG;
@@ -1245,7 +1227,7 @@ send_bitmap_rle_or_plain(struct drbd_device *device, struct bm_xfer_ctx *c)
}
if (!err) {
if (len == 0) {
- INFO_bm_xfer_stats(device, "send", c);
+ INFO_bm_xfer_stats(peer_device, "send", c);
return 0;
} else
return 1;
@@ -1254,19 +1236,20 @@ send_bitmap_rle_or_plain(struct drbd_device *device, struct bm_xfer_ctx *c)
}
/* See the comment at receive_bitmap() */
-static int _drbd_send_bitmap(struct drbd_device *device)
+static int _drbd_send_bitmap(struct drbd_device *device,
+ struct drbd_peer_device *peer_device)
{
struct bm_xfer_ctx c;
int err;
- if (!expect(device->bitmap))
+ if (!expect(device, device->bitmap))
return false;
if (get_ldev(device)) {
if (drbd_md_test_flag(device->ldev, MDF_FULL_SYNC)) {
drbd_info(device, "Writing the whole bitmap, MDF_FullSync was set.\n");
drbd_bm_set_all(device);
- if (drbd_bm_write(device)) {
+ if (drbd_bm_write(device, peer_device)) {
/* write_bm did fail! Leave full sync flag set in Meta P_DATA
* but otherwise process as per normal - need to tell other
* side that a full resync is required! */
@@ -1285,20 +1268,20 @@ static int _drbd_send_bitmap(struct drbd_device *device)
};
do {
- err = send_bitmap_rle_or_plain(device, &c);
+ err = send_bitmap_rle_or_plain(peer_device, &c);
} while (err > 0);
return err == 0;
}
-int drbd_send_bitmap(struct drbd_device *device)
+int drbd_send_bitmap(struct drbd_device *device, struct drbd_peer_device *peer_device)
{
- struct drbd_socket *sock = &first_peer_device(device)->connection->data;
+ struct drbd_socket *sock = &peer_device->connection->data;
int err = -1;
mutex_lock(&sock->mutex);
if (sock->socket)
- err = !_drbd_send_bitmap(device);
+ err = !_drbd_send_bitmap(device, peer_device);
mutex_unlock(&sock->mutex);
return err;
}
@@ -1532,6 +1515,8 @@ static int _drbd_send_page(struct drbd_peer_device *peer_device, struct page *pa
int offset, size_t size, unsigned msg_flags)
{
struct socket *socket = peer_device->connection->data.socket;
+ struct msghdr msg = { .msg_flags = msg_flags, };
+ struct bio_vec bvec;
int len = size;
int err = -EIO;
@@ -1541,15 +1526,17 @@ static int _drbd_send_page(struct drbd_peer_device *peer_device, struct page *pa
* put_page(); and would cause either a VM_BUG directly, or
* __page_cache_release a page that would actually still be referenced
* by someone, leading to some obscure delayed Oops somewhere else. */
- if (drbd_disable_sendpage || !sendpage_ok(page))
- return _drbd_no_send_page(peer_device, page, offset, size, msg_flags);
+ if (!drbd_disable_sendpage && sendpages_ok(page, len, offset))
+ msg.msg_flags |= MSG_NOSIGNAL | MSG_SPLICE_PAGES;
- msg_flags |= MSG_NOSIGNAL;
drbd_update_congested(peer_device->connection);
do {
int sent;
- sent = socket->ops->sendpage(socket, page, offset, len, msg_flags);
+ bvec_set_page(&bvec, page, len, offset);
+ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bvec, 1, len);
+
+ sent = sock_sendmsg(socket, &msg);
if (sent <= 0) {
if (sent == -EAGAIN) {
if (we_should_drop_the_connection(peer_device->connection, socket))
@@ -1614,6 +1601,7 @@ static int _drbd_send_zc_bio(struct drbd_peer_device *peer_device, struct bio *b
static int _drbd_send_zc_ee(struct drbd_peer_device *peer_device,
struct drbd_peer_request *peer_req)
{
+ bool use_sendpage = !(peer_req->flags & EE_RELEASE_TO_MEMPOOL);
struct page *page = peer_req->pages;
unsigned len = peer_req->i.size;
int err;
@@ -1622,8 +1610,13 @@ static int _drbd_send_zc_ee(struct drbd_peer_device *peer_device,
page_chain_for_each(page) {
unsigned l = min_t(unsigned, len, PAGE_SIZE);
- err = _drbd_send_page(peer_device, page, 0, l,
- page_chain_next(page) ? MSG_MORE : 0);
+ if (likely(use_sendpage))
+ err = _drbd_send_page(peer_device, page, 0, l,
+ page_chain_next(page) ? MSG_MORE : 0);
+ else
+ err = _drbd_no_send_page(peer_device, page, 0, l,
+ page_chain_next(page) ? MSG_MORE : 0);
+
if (err)
return err;
len -= l;
@@ -1816,7 +1809,7 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock,
/* THINK if (signal_pending) return ... ? */
- iov_iter_kvec(&msg.msg_iter, WRITE, &iov, 1, size);
+ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iov, 1, size);
if (sock == connection->data.socket) {
rcu_read_lock();
@@ -1875,9 +1868,9 @@ int drbd_send_all(struct drbd_connection *connection, struct socket *sock, void
return 0;
}
-static int drbd_open(struct block_device *bdev, fmode_t mode)
+static int drbd_open(struct gendisk *disk, blk_mode_t mode)
{
- struct drbd_device *device = bdev->bd_disk->private_data;
+ struct drbd_device *device = disk->private_data;
unsigned long flags;
int rv = 0;
@@ -1887,7 +1880,7 @@ static int drbd_open(struct block_device *bdev, fmode_t mode)
* and no race with updating open_cnt */
if (device->state.role != R_PRIMARY) {
- if (mode & FMODE_WRITE)
+ if (mode & BLK_OPEN_WRITE)
rv = -EROFS;
else if (!drbd_allow_oos)
rv = -EMEDIUMTYPE;
@@ -1901,9 +1894,10 @@ static int drbd_open(struct block_device *bdev, fmode_t mode)
return rv;
}
-static void drbd_release(struct gendisk *gd, fmode_t mode)
+static void drbd_release(struct gendisk *gd)
{
struct drbd_device *device = gd->private_data;
+
mutex_lock(&drbd_main_mutex);
device->open_cnt--;
mutex_unlock(&drbd_main_mutex);
@@ -1964,7 +1958,6 @@ void drbd_init_set_defaults(struct drbd_device *device)
INIT_LIST_HEAD(&device->sync_ee);
INIT_LIST_HEAD(&device->done_ee);
INIT_LIST_HEAD(&device->read_ee);
- INIT_LIST_HEAD(&device->net_ee);
INIT_LIST_HEAD(&device->resync_reads);
INIT_LIST_HEAD(&device->resync_work.list);
INIT_LIST_HEAD(&device->unplug_work.list);
@@ -2045,7 +2038,6 @@ void drbd_device_cleanup(struct drbd_device *device)
D_ASSERT(device, list_empty(&device->sync_ee));
D_ASSERT(device, list_empty(&device->done_ee));
D_ASSERT(device, list_empty(&device->read_ee));
- D_ASSERT(device, list_empty(&device->net_ee));
D_ASSERT(device, list_empty(&device->resync_reads));
D_ASSERT(device, list_empty(&first_peer_device(device)->connection->sender_work.q));
D_ASSERT(device, list_empty(&device->resync_work.list));
@@ -2057,19 +2049,11 @@ void drbd_device_cleanup(struct drbd_device *device)
static void drbd_destroy_mempools(void)
{
- struct page *page;
-
- while (drbd_pp_pool) {
- page = drbd_pp_pool;
- drbd_pp_pool = (struct page *)page_private(page);
- __free_page(page);
- drbd_pp_vacant--;
- }
-
/* D_ASSERT(device, atomic_read(&drbd_pp_vacant)==0); */
bioset_exit(&drbd_io_bio_set);
bioset_exit(&drbd_md_io_bio_set);
+ mempool_exit(&drbd_buffer_page_pool);
mempool_exit(&drbd_md_io_page_pool);
mempool_exit(&drbd_ee_mempool);
mempool_exit(&drbd_request_mempool);
@@ -2088,9 +2072,8 @@ static void drbd_destroy_mempools(void)
static int drbd_create_mempools(void)
{
- struct page *page;
const int number = (DRBD_MAX_BIO_SIZE/PAGE_SIZE) * drbd_minor_count;
- int i, ret;
+ int ret;
/* caches */
drbd_request_cache = kmem_cache_create(
@@ -2127,6 +2110,10 @@ static int drbd_create_mempools(void)
if (ret)
goto Enomem;
+ ret = mempool_init_page_pool(&drbd_buffer_page_pool, number, 0);
+ if (ret)
+ goto Enomem;
+
ret = mempool_init_slab_pool(&drbd_request_mempool, number,
drbd_request_cache);
if (ret)
@@ -2136,15 +2123,6 @@ static int drbd_create_mempools(void)
if (ret)
goto Enomem;
- for (i = 0; i < number; i++) {
- page = alloc_page(GFP_HIGHUSER);
- if (!page)
- goto Enomem;
- set_page_private(page, (unsigned long)drbd_pp_pool);
- drbd_pp_pool = page;
- }
- drbd_pp_vacant = number;
-
return 0;
Enomem:
@@ -2171,10 +2149,6 @@ static void drbd_release_all_peer_reqs(struct drbd_device *device)
rr = drbd_free_peer_reqs(device, &device->done_ee);
if (rr)
drbd_err(device, "%d EEs in done list found!\n", rr);
-
- rr = drbd_free_peer_reqs(device, &device->net_ee);
- if (rr)
- drbd_err(device, "%d EEs in net list found!\n", rr);
}
/* caution. no locking. */
@@ -2184,7 +2158,7 @@ void drbd_destroy_device(struct kref *kref)
struct drbd_resource *resource = device->resource;
struct drbd_peer_device *peer_device, *tmp_peer_device;
- del_timer_sync(&device->request_timer);
+ timer_shutdown_sync(&device->request_timer);
/* paranoia asserts */
D_ASSERT(device, device->open_cnt == 0);
@@ -2217,7 +2191,8 @@ void drbd_destroy_device(struct kref *kref)
kref_put(&peer_device->connection->kref, drbd_destroy_connection);
kfree(peer_device);
}
- memset(device, 0xfd, sizeof(*device));
+ if (device->submit.wq)
+ destroy_workqueue(device->submit.wq);
kfree(device);
kref_put(&resource->kref, drbd_destroy_resource);
}
@@ -2249,9 +2224,9 @@ static void do_retry(struct work_struct *ws)
bool expected;
expected =
- expect(atomic_read(&req->completion_ref) == 0) &&
- expect(req->rq_state & RQ_POSTPONED) &&
- expect((req->rq_state & RQ_LOCAL_PENDING) == 0 ||
+ expect(device, atomic_read(&req->completion_ref) == 0) &&
+ expect(device, req->rq_state & RQ_POSTPONED) &&
+ expect(device, (req->rq_state & RQ_LOCAL_PENDING) == 0 ||
(req->rq_state & RQ_LOCAL_ABORTED) != 0);
if (!expected)
@@ -2309,7 +2284,6 @@ void drbd_destroy_resource(struct kref *kref)
idr_destroy(&resource->devices);
free_cpumask_var(resource->cpu_mask);
kfree(resource->name);
- memset(resource, 0xf2, sizeof(*resource));
kfree(resource);
}
@@ -2650,7 +2624,6 @@ void drbd_destroy_connection(struct kref *kref)
drbd_free_socket(&connection->data);
kfree(connection->int_dig_in);
kfree(connection->int_dig_vv);
- memset(connection, 0xfc, sizeof(*connection));
kfree(connection);
kref_put(&resource->kref, drbd_destroy_resource);
}
@@ -2672,13 +2645,24 @@ static int init_submitter(struct drbd_device *device)
enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsigned int minor)
{
struct drbd_resource *resource = adm_ctx->resource;
- struct drbd_connection *connection;
+ struct drbd_connection *connection, *n;
struct drbd_device *device;
struct drbd_peer_device *peer_device, *tmp_peer_device;
struct gendisk *disk;
int id;
int vnr = adm_ctx->volume;
enum drbd_ret_code err = ERR_NOMEM;
+ struct queue_limits lim = {
+ /*
+ * Setting the max_hw_sectors to an odd value of 8kibyte here.
+ * This triggers a max_bio_size message upon first attach or
+ * connect.
+ */
+ .max_hw_sectors = DRBD_MAX_BIO_SIZE_SAFE >> 8,
+ .features = BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA |
+ BLK_FEAT_ROTATIONAL |
+ BLK_FEAT_STABLE_WRITES,
+ };
device = minor_to_device(minor);
if (device)
@@ -2697,9 +2681,11 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
drbd_init_set_defaults(device);
- disk = blk_alloc_disk(NUMA_NO_NODE);
- if (!disk)
+ disk = blk_alloc_disk(&lim, NUMA_NO_NODE);
+ if (IS_ERR(disk)) {
+ err = PTR_ERR(disk);
goto out_no_disk;
+ }
device->vdisk = disk;
device->rq_queue = disk->queue;
@@ -2714,12 +2700,6 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
sprintf(disk->disk_name, "drbd%d", minor);
disk->private_data = device;
- blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, disk->queue);
- blk_queue_write_cache(disk->queue, true, true);
- /* Setting the max_hw_sectors to an odd value of 8kibyte here
- This triggers a max_bio_size message upon first attach or connect */
- blk_queue_max_hw_sectors(disk->queue, DRBD_MAX_BIO_SIZE_SAFE >> 8);
-
device->md_io.page = alloc_page(GFP_KERNEL);
if (!device->md_io.page)
goto out_no_io_page;
@@ -2774,7 +2754,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
err = add_disk(disk);
if (err)
- goto out_idr_remove_from_resource;
+ goto out_destroy_workqueue;
/* inherit the connection state */
device->state.conn = first_connection(resource)->cstate;
@@ -2788,8 +2768,10 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
drbd_debugfs_device_add(device);
return NO_ERROR;
+out_destroy_workqueue:
+ destroy_workqueue(device->submit.wq);
out_idr_remove_from_resource:
- for_each_connection(connection, resource) {
+ for_each_connection_safe(connection, n, resource) {
peer_device = idr_remove(&connection->peer_devices, vnr);
if (peer_device)
kref_put(&connection->kref, drbd_destroy_connection);
@@ -2857,11 +2839,6 @@ static int __init drbd_init(void)
return err;
}
- /*
- * allocate all necessary structs
- */
- init_waitqueue_head(&drbd_pp_wait);
-
drbd_proc = NULL; /* play safe for drbd_cleanup */
idr_init(&drbd_devices);
@@ -2898,7 +2875,7 @@ static int __init drbd_init(void)
pr_info("initialized. "
"Version: " REL_VERSION " (api:%d/proto:%d-%d)\n",
- API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX);
+ GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX);
pr_info("%s\n", drbd_buildtag());
pr_info("registered as block device major %d\n", DRBD_MAJOR);
return 0; /* Success! */
@@ -3028,7 +3005,7 @@ void drbd_md_sync(struct drbd_device *device)
BUILD_BUG_ON(UI_SIZE != 4);
BUILD_BUG_ON(sizeof(struct meta_data_on_disk) != 4096);
- del_timer(&device->md_sync_timer);
+ timer_delete(&device->md_sync_timer);
/* timer may be rearmed by drbd_md_mark_dirty() now. */
if (!test_and_clear_bit(MD_DIRTY, &device->flags))
return;
@@ -3379,10 +3356,12 @@ void drbd_uuid_new_current(struct drbd_device *device) __must_hold(local)
void drbd_uuid_set_bm(struct drbd_device *device, u64 val) __must_hold(local)
{
unsigned long flags;
- if (device->ldev->md.uuid[UI_BITMAP] == 0 && val == 0)
+ spin_lock_irqsave(&device->ldev->md.uuid_lock, flags);
+ if (device->ldev->md.uuid[UI_BITMAP] == 0 && val == 0) {
+ spin_unlock_irqrestore(&device->ldev->md.uuid_lock, flags);
return;
+ }
- spin_lock_irqsave(&device->ldev->md.uuid_lock, flags);
if (val == 0) {
drbd_uuid_move_history(device);
device->ldev->md.uuid[UI_HISTORY_START] = device->ldev->md.uuid[UI_BITMAP];
@@ -3402,10 +3381,13 @@ void drbd_uuid_set_bm(struct drbd_device *device, u64 val) __must_hold(local)
/**
* drbd_bmio_set_n_write() - io_fn for drbd_queue_bitmap_io() or drbd_bitmap_io()
* @device: DRBD device.
+ * @peer_device: Peer DRBD device.
*
* Sets all bits in the bitmap and writes the whole bitmap to stable storage.
*/
-int drbd_bmio_set_n_write(struct drbd_device *device) __must_hold(local)
+int drbd_bmio_set_n_write(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
+
{
int rv = -EIO;
@@ -3413,7 +3395,7 @@ int drbd_bmio_set_n_write(struct drbd_device *device) __must_hold(local)
drbd_md_sync(device);
drbd_bm_set_all(device);
- rv = drbd_bm_write(device);
+ rv = drbd_bm_write(device, peer_device);
if (!rv) {
drbd_md_clear_flag(device, MDF_FULL_SYNC);
@@ -3426,14 +3408,17 @@ int drbd_bmio_set_n_write(struct drbd_device *device) __must_hold(local)
/**
* drbd_bmio_clear_n_write() - io_fn for drbd_queue_bitmap_io() or drbd_bitmap_io()
* @device: DRBD device.
+ * @peer_device: Peer DRBD device.
*
* Clears all bits in the bitmap and writes the whole bitmap to stable storage.
*/
-int drbd_bmio_clear_n_write(struct drbd_device *device) __must_hold(local)
+int drbd_bmio_clear_n_write(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
+
{
drbd_resume_al(device);
drbd_bm_clear_all(device);
- return drbd_bm_write(device);
+ return drbd_bm_write(device, peer_device);
}
static int w_bitmap_io(struct drbd_work *w, int unused)
@@ -3452,7 +3437,7 @@ static int w_bitmap_io(struct drbd_work *w, int unused)
if (get_ldev(device)) {
drbd_bm_lock(device, work->why, work->flags);
- rv = work->io_fn(device);
+ rv = work->io_fn(device, work->peer_device);
drbd_bm_unlock(device);
put_ldev(device);
}
@@ -3477,6 +3462,7 @@ static int w_bitmap_io(struct drbd_work *w, int unused)
* @done: callback to be called after the bitmap IO was performed
* @why: Descriptive text of the reason for doing the IO
* @flags: Bitmap flags
+ * @peer_device: Peer DRBD device.
*
* While IO on the bitmap happens we freeze application IO thus we ensure
* that drbd_set_out_of_sync() can not be called. This function MAY ONLY be
@@ -3487,11 +3473,12 @@ static int w_bitmap_io(struct drbd_work *w, int unused)
* put_ldev().
*/
void drbd_queue_bitmap_io(struct drbd_device *device,
- int (*io_fn)(struct drbd_device *),
+ int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
void (*done)(struct drbd_device *, int),
- char *why, enum bm_flag flags)
+ char *why, enum bm_flag flags,
+ struct drbd_peer_device *peer_device)
{
- D_ASSERT(device, current == first_peer_device(device)->connection->worker.task);
+ D_ASSERT(device, current == peer_device->connection->worker.task);
D_ASSERT(device, !test_bit(BITMAP_IO_QUEUED, &device->flags));
D_ASSERT(device, !test_bit(BITMAP_IO, &device->flags));
@@ -3500,6 +3487,7 @@ void drbd_queue_bitmap_io(struct drbd_device *device,
drbd_err(device, "FIXME going to queue '%s' but '%s' still pending?\n",
why, device->bm_io_work.why);
+ device->bm_io_work.peer_device = peer_device;
device->bm_io_work.io_fn = io_fn;
device->bm_io_work.done = done;
device->bm_io_work.why = why;
@@ -3511,7 +3499,7 @@ void drbd_queue_bitmap_io(struct drbd_device *device,
* application IO does not conflict anyways. */
if (flags == BM_LOCKED_CHANGE_ALLOWED || atomic_read(&device->ap_bio_cnt) == 0) {
if (!test_and_set_bit(BITMAP_IO_QUEUED, &device->flags))
- drbd_queue_work(&first_peer_device(device)->connection->sender_work,
+ drbd_queue_work(&peer_device->connection->sender_work,
&device->bm_io_work.w);
}
spin_unlock_irq(&device->resource->req_lock);
@@ -3523,12 +3511,15 @@ void drbd_queue_bitmap_io(struct drbd_device *device,
* @io_fn: IO callback to be called when bitmap IO is possible
* @why: Descriptive text of the reason for doing the IO
* @flags: Bitmap flags
+ * @peer_device: Peer DRBD device.
*
* freezes application IO while that the actual IO operations runs. This
* functions MAY NOT be called from worker context.
*/
-int drbd_bitmap_io(struct drbd_device *device, int (*io_fn)(struct drbd_device *),
- char *why, enum bm_flag flags)
+int drbd_bitmap_io(struct drbd_device *device,
+ int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
+ char *why, enum bm_flag flags,
+ struct drbd_peer_device *peer_device)
{
/* Only suspend io, if some operation is supposed to be locked out */
const bool do_suspend_io = flags & (BM_DONT_CLEAR|BM_DONT_SET|BM_DONT_TEST);
@@ -3540,7 +3531,7 @@ int drbd_bitmap_io(struct drbd_device *device, int (*io_fn)(struct drbd_device *
drbd_suspend_io(device);
drbd_bm_lock(device, why, flags);
- rv = io_fn(device);
+ rv = io_fn(device, peer_device);
drbd_bm_unlock(device);
if (do_suspend_io)
@@ -3571,7 +3562,8 @@ int drbd_md_test_flag(struct drbd_backing_dev *bdev, int flag)
static void md_sync_timer_fn(struct timer_list *t)
{
- struct drbd_device *device = from_timer(device, t, md_sync_timer);
+ struct drbd_device *device = timer_container_of(device, t,
+ md_sync_timer);
drbd_device_post_work(device, MD_SYNC);
}
@@ -3766,7 +3758,7 @@ _drbd_insert_fault(struct drbd_device *device, unsigned int type)
if (ret) {
drbd_fault_count++;
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_warn(device, "***Simulating %s failure\n",
_drbd_fault_str(type));
}
@@ -3775,24 +3767,6 @@ _drbd_insert_fault(struct drbd_device *device, unsigned int type)
}
#endif
-const char *drbd_buildtag(void)
-{
- /* DRBD built from external sources has here a reference to the
- git hash of the source code. */
-
- static char buildtag[38] = "\0uilt-in";
-
- if (buildtag[0] == 0) {
-#ifdef MODULE
- sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
-#else
- buildtag[0] = 'b';
-#endif
- }
-
- return buildtag;
-}
-
module_init(drbd_init)
module_exit(drbd_cleanup)
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 013d355a2033..91f3b8afb63c 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_nl.c
@@ -25,7 +25,7 @@
#include "drbd_protocol.h"
#include "drbd_req.h"
#include "drbd_state_change.h"
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/drbd_limits.h>
#include <linux/kthread.h>
@@ -82,7 +82,7 @@ static atomic_t notify_genl_seq = ATOMIC_INIT(2); /* two. */
DEFINE_MUTEX(notification_mutex);
-/* used blkdev_get_by_path, to claim our meta data device(s) */
+/* used bdev_open_by_path, to claim our meta data device(s) */
static char *drbd_m_holder = "Hands off! this is DRBD's meta data device.";
static void drbd_adm_send_reply(struct sk_buff *skb, struct genl_info *info)
@@ -159,7 +159,7 @@ static int drbd_msg_sprintf_info(struct sk_buff *skb, const char *fmt, ...)
static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
struct sk_buff *skb, struct genl_info *info, unsigned flags)
{
- struct drbd_genlmsghdr *d_in = info->userhdr;
+ struct drbd_genlmsghdr *d_in = genl_info_userhdr(info);
const u8 cmd = info->genlhdr->cmd;
int err;
@@ -1033,7 +1033,7 @@ drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct
/* We do some synchronous IO below, which may take some time.
* Clear the timer, to avoid scary "timer expired!" messages,
* "Superblock" is written out at least twice below, anyways. */
- del_timer(&device->md_sync_timer);
+ timer_delete(&device->md_sync_timer);
/* We won't change the "al-extents" setting, we just may need
* to move the on-disk location of the activity log ringbuffer.
@@ -1053,7 +1053,7 @@ drbd_determine_dev_size(struct drbd_device *device, enum dds_flags flags, struct
la_size_changed ? "size changed" : "md moved");
/* next line implicitly does drbd_suspend_io()+drbd_resume_io() */
drbd_bitmap_io(device, md_moved ? &drbd_bm_write_all : &drbd_bm_write,
- "size changed", BM_LOCKED_MASK);
+ "size changed", BM_LOCKED_MASK, NULL);
/* on-disk bitmap and activity log is authoritative again
* (unless there was an IO error meanwhile...) */
@@ -1189,9 +1189,31 @@ static int drbd_check_al_size(struct drbd_device *device, struct disk_conf *dc)
return 0;
}
-static void blk_queue_discard_granularity(struct request_queue *q, unsigned int granularity)
+static unsigned int drbd_max_peer_bio_size(struct drbd_device *device)
{
- q->limits.discard_granularity = granularity;
+ /*
+ * We may ignore peer limits if the peer is modern enough. From 8.3.8
+ * onwards the peer can use multiple BIOs for a single peer_request.
+ */
+ if (device->state.conn < C_WF_REPORT_PARAMS)
+ return device->peer_max_bio_size;
+
+ if (first_peer_device(device)->connection->agreed_pro_version < 94)
+ return min(device->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+
+ /*
+ * Correct old drbd (up to 8.3.7) if it believes it can do more than
+ * 32KiB.
+ */
+ if (first_peer_device(device)->connection->agreed_pro_version == 94)
+ return DRBD_MAX_SIZE_H80_PACKET;
+
+ /*
+ * drbd 8.3.8 onwards, before 8.4.0
+ */
+ if (first_peer_device(device)->connection->agreed_pro_version < 100)
+ return DRBD_MAX_BIO_SIZE_P95;
+ return DRBD_MAX_BIO_SIZE;
}
static unsigned int drbd_max_discard_sectors(struct drbd_connection *connection)
@@ -1204,136 +1226,120 @@ static unsigned int drbd_max_discard_sectors(struct drbd_connection *connection)
return AL_EXTENT_SIZE >> 9;
}
-static void decide_on_discard_support(struct drbd_device *device,
+static bool drbd_discard_supported(struct drbd_connection *connection,
struct drbd_backing_dev *bdev)
{
- struct drbd_connection *connection =
- first_peer_device(device)->connection;
- struct request_queue *q = device->rq_queue;
-
if (bdev && !bdev_max_discard_sectors(bdev->backing_bdev))
- goto not_supported;
+ return false;
if (connection->cstate >= C_CONNECTED &&
!(connection->agreed_features & DRBD_FF_TRIM)) {
drbd_info(connection,
"peer DRBD too old, does not support TRIM: disabling discards\n");
- goto not_supported;
+ return false;
}
- /*
- * We don't care for the granularity, really.
- *
- * Stacking limits below should fix it for the local device. Whether or
- * not it is a suitable granularity on the remote device is not our
- * problem, really. If you care, you need to use devices with similar
- * topology on all peers.
- */
- blk_queue_discard_granularity(q, 512);
- q->limits.max_discard_sectors = drbd_max_discard_sectors(connection);
- q->limits.max_write_zeroes_sectors =
- drbd_max_discard_sectors(connection);
- return;
-
-not_supported:
- blk_queue_discard_granularity(q, 0);
- q->limits.max_discard_sectors = 0;
- q->limits.max_write_zeroes_sectors = 0;
+ return true;
}
-static void fixup_write_zeroes(struct drbd_device *device, struct request_queue *q)
+/* This is the workaround for "bio would need to, but cannot, be split" */
+static unsigned int drbd_backing_dev_max_segments(struct drbd_device *device)
{
- /* Fixup max_write_zeroes_sectors after blk_stack_limits():
- * if we can handle "zeroes" efficiently on the protocol,
- * we want to do that, even if our backend does not announce
- * max_write_zeroes_sectors itself. */
- struct drbd_connection *connection = first_peer_device(device)->connection;
- /* If the peer announces WZEROES support, use it. Otherwise, rather
- * send explicit zeroes than rely on some discard-zeroes-data magic. */
- if (connection->agreed_features & DRBD_FF_WZEROES)
- q->limits.max_write_zeroes_sectors = DRBD_MAX_BBIO_SECTORS;
- else
- q->limits.max_write_zeroes_sectors = 0;
+ unsigned int max_segments;
+
+ rcu_read_lock();
+ max_segments = rcu_dereference(device->ldev->disk_conf)->max_bio_bvecs;
+ rcu_read_unlock();
+
+ if (!max_segments)
+ return BLK_MAX_SEGMENTS;
+ return max_segments;
}
-static void drbd_setup_queue_param(struct drbd_device *device, struct drbd_backing_dev *bdev,
- unsigned int max_bio_size, struct o_qlim *o)
+void drbd_reconsider_queue_parameters(struct drbd_device *device,
+ struct drbd_backing_dev *bdev, struct o_qlim *o)
{
+ struct drbd_connection *connection =
+ first_peer_device(device)->connection;
struct request_queue * const q = device->rq_queue;
- unsigned int max_hw_sectors = max_bio_size >> 9;
- unsigned int max_segments = 0;
+ unsigned int now = queue_max_hw_sectors(q) << 9;
+ struct queue_limits lim;
struct request_queue *b = NULL;
- struct disk_conf *dc;
+ unsigned int new;
if (bdev) {
b = bdev->backing_bdev->bd_disk->queue;
- max_hw_sectors = min(queue_max_hw_sectors(b), max_bio_size >> 9);
- rcu_read_lock();
- dc = rcu_dereference(device->ldev->disk_conf);
- max_segments = dc->max_bio_bvecs;
- rcu_read_unlock();
-
- blk_set_stacking_limits(&q->limits);
+ device->local_max_bio_size =
+ queue_max_hw_sectors(b) << SECTOR_SHIFT;
}
- blk_queue_max_hw_sectors(q, max_hw_sectors);
- /* This is the workaround for "bio would need to, but cannot, be split" */
- blk_queue_max_segments(q, max_segments ? max_segments : BLK_MAX_SEGMENTS);
- blk_queue_segment_boundary(q, PAGE_SIZE-1);
- decide_on_discard_support(device, bdev);
-
- if (b) {
- blk_stack_limits(&q->limits, &b->limits, 0);
- disk_update_readahead(device->vdisk);
+ /*
+ * We may later detach and re-attach on a disconnected Primary. Avoid
+ * decreasing the value in this case.
+ *
+ * We want to store what we know the peer DRBD can handle, not what the
+ * peer IO backend can handle.
+ */
+ new = min3(DRBD_MAX_BIO_SIZE, device->local_max_bio_size,
+ max(drbd_max_peer_bio_size(device), device->peer_max_bio_size));
+ if (new != now) {
+ if (device->state.role == R_PRIMARY && new < now)
+ drbd_err(device, "ASSERT FAILED new < now; (%u < %u)\n",
+ new, now);
+ drbd_info(device, "max BIO size = %u\n", new);
}
- fixup_write_zeroes(device, q);
-}
-
-void drbd_reconsider_queue_parameters(struct drbd_device *device, struct drbd_backing_dev *bdev, struct o_qlim *o)
-{
- unsigned int now, new, local, peer;
-
- now = queue_max_hw_sectors(device->rq_queue) << 9;
- local = device->local_max_bio_size; /* Eventually last known value, from volatile memory */
- peer = device->peer_max_bio_size; /* Eventually last known value, from meta data */
+ lim = queue_limits_start_update(q);
if (bdev) {
- local = queue_max_hw_sectors(bdev->backing_bdev->bd_disk->queue) << 9;
- device->local_max_bio_size = local;
+ blk_set_stacking_limits(&lim);
+ lim.max_segments = drbd_backing_dev_max_segments(device);
+ } else {
+ lim.max_segments = BLK_MAX_SEGMENTS;
}
- local = min(local, DRBD_MAX_BIO_SIZE);
- /* We may ignore peer limits if the peer is modern enough.
- Because new from 8.3.8 onwards the peer can use multiple
- BIOs for a single peer_request */
- if (device->state.conn >= C_WF_REPORT_PARAMS) {
- if (first_peer_device(device)->connection->agreed_pro_version < 94)
- peer = min(device->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
- /* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */
- else if (first_peer_device(device)->connection->agreed_pro_version == 94)
- peer = DRBD_MAX_SIZE_H80_PACKET;
- else if (first_peer_device(device)->connection->agreed_pro_version < 100)
- peer = DRBD_MAX_BIO_SIZE_P95; /* drbd 8.3.8 onwards, before 8.4.0 */
- else
- peer = DRBD_MAX_BIO_SIZE;
+ lim.max_hw_sectors = new >> SECTOR_SHIFT;
+ lim.seg_boundary_mask = PAGE_SIZE - 1;
- /* We may later detach and re-attach on a disconnected Primary.
- * Avoid this setting to jump back in that case.
- * We want to store what we know the peer DRBD can handle,
- * not what the peer IO backend can handle. */
- if (peer > device->peer_max_bio_size)
- device->peer_max_bio_size = peer;
+ /*
+ * We don't care for the granularity, really.
+ *
+ * Stacking limits below should fix it for the local device. Whether or
+ * not it is a suitable granularity on the remote device is not our
+ * problem, really. If you care, you need to use devices with similar
+ * topology on all peers.
+ */
+ if (drbd_discard_supported(connection, bdev)) {
+ lim.discard_granularity = 512;
+ lim.max_hw_discard_sectors =
+ drbd_max_discard_sectors(connection);
+ } else {
+ lim.discard_granularity = 0;
+ lim.max_hw_discard_sectors = 0;
}
- new = min(local, peer);
- if (device->state.role == R_PRIMARY && new < now)
- drbd_err(device, "ASSERT FAILED new < now; (%u < %u)\n", new, now);
+ if (bdev)
+ blk_stack_limits(&lim, &b->limits, 0);
- if (new != now)
- drbd_info(device, "max BIO size = %u\n", new);
+ /*
+ * If we can handle "zeroes" efficiently on the protocol, we want to do
+ * that, even if our backend does not announce max_write_zeroes_sectors
+ * itself.
+ */
+ if (connection->agreed_features & DRBD_FF_WZEROES)
+ lim.max_write_zeroes_sectors = DRBD_MAX_BBIO_SECTORS;
+ else
+ lim.max_write_zeroes_sectors = 0;
+ lim.max_hw_wzeroes_unmap_sectors = 0;
+
+ if ((lim.discard_granularity >> SECTOR_SHIFT) >
+ lim.max_hw_discard_sectors) {
+ lim.discard_granularity = 0;
+ lim.max_hw_discard_sectors = 0;
+ }
- drbd_setup_queue_param(device, bdev, new, o);
+ if (queue_limits_commit_update(q, &lim))
+ drbd_err(device, "setting new queue limits failed\n");
}
/* Starts the worker thread */
@@ -1383,8 +1389,9 @@ static void drbd_suspend_al(struct drbd_device *device)
static bool should_set_defaults(struct genl_info *info)
{
- unsigned flags = ((struct drbd_genlmsghdr*)info->userhdr)->flags;
- return 0 != (flags & DRBD_GENL_F_SET_DEFAULTS);
+ struct drbd_genlmsghdr *dh = genl_info_userhdr(info);
+
+ return 0 != (dh->flags & DRBD_GENL_F_SET_DEFAULTS);
}
static unsigned int drbd_al_extents_max(struct drbd_backing_dev *bdev)
@@ -1530,7 +1537,7 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
goto fail_unlock;
}
- if (!expect(new_disk_conf->resync_rate >= 1))
+ if (!expect(device, new_disk_conf->resync_rate >= 1))
new_disk_conf->resync_rate = 1;
sanitize_disk_conf(device, new_disk_conf, device->ldev);
@@ -1602,7 +1609,7 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
drbd_send_sync_param(peer_device);
}
- kvfree_rcu(old_disk_conf);
+ kvfree_rcu_mightsleep(old_disk_conf);
kfree(old_plan);
mod_timer(&device->request_timer, jiffies + HZ);
goto success;
@@ -1621,43 +1628,45 @@ success:
return 0;
}
-static struct block_device *open_backing_dev(struct drbd_device *device,
+static struct file *open_backing_dev(struct drbd_device *device,
const char *bdev_path, void *claim_ptr, bool do_bd_link)
{
- struct block_device *bdev;
+ struct file *file;
int err = 0;
- bdev = blkdev_get_by_path(bdev_path,
- FMODE_READ | FMODE_WRITE | FMODE_EXCL, claim_ptr);
- if (IS_ERR(bdev)) {
+ file = bdev_file_open_by_path(bdev_path, BLK_OPEN_READ | BLK_OPEN_WRITE,
+ claim_ptr, NULL);
+ if (IS_ERR(file)) {
drbd_err(device, "open(\"%s\") failed with %ld\n",
- bdev_path, PTR_ERR(bdev));
- return bdev;
+ bdev_path, PTR_ERR(file));
+ return file;
}
if (!do_bd_link)
- return bdev;
+ return file;
- err = bd_link_disk_holder(bdev, device->vdisk);
+ err = bd_link_disk_holder(file_bdev(file), device->vdisk);
if (err) {
- blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
+ fput(file);
drbd_err(device, "bd_link_disk_holder(\"%s\", ...) failed with %d\n",
bdev_path, err);
- bdev = ERR_PTR(err);
+ file = ERR_PTR(err);
}
- return bdev;
+ return file;
}
static int open_backing_devices(struct drbd_device *device,
struct disk_conf *new_disk_conf,
struct drbd_backing_dev *nbc)
{
- struct block_device *bdev;
+ struct file *file;
- bdev = open_backing_dev(device, new_disk_conf->backing_dev, device, true);
- if (IS_ERR(bdev))
+ file = open_backing_dev(device, new_disk_conf->backing_dev, device,
+ true);
+ if (IS_ERR(file))
return ERR_OPEN_DISK;
- nbc->backing_bdev = bdev;
+ nbc->backing_bdev = file_bdev(file);
+ nbc->backing_bdev_file = file;
/*
* meta_dev_idx >= 0: external fixed size, possibly multiple
@@ -1667,7 +1676,7 @@ static int open_backing_devices(struct drbd_device *device,
* should check it for you already; but if you don't, or
* someone fooled it, we need to double check here)
*/
- bdev = open_backing_dev(device, new_disk_conf->meta_dev,
+ file = open_backing_dev(device, new_disk_conf->meta_dev,
/* claim ptr: device, if claimed exclusively; shared drbd_m_holder,
* if potentially shared with other drbd minors */
(new_disk_conf->meta_dev_idx < 0) ? (void*)device : (void*)drbd_m_holder,
@@ -1675,20 +1684,21 @@ static int open_backing_devices(struct drbd_device *device,
* as would happen with internal metadata. */
(new_disk_conf->meta_dev_idx != DRBD_MD_INDEX_FLEX_INT &&
new_disk_conf->meta_dev_idx != DRBD_MD_INDEX_INTERNAL));
- if (IS_ERR(bdev))
+ if (IS_ERR(file))
return ERR_OPEN_MD_DISK;
- nbc->md_bdev = bdev;
+ nbc->md_bdev = file_bdev(file);
+ nbc->f_md_bdev = file;
return NO_ERROR;
}
-static void close_backing_dev(struct drbd_device *device, struct block_device *bdev,
- bool do_bd_unlink)
+static void close_backing_dev(struct drbd_device *device,
+ struct file *bdev_file, bool do_bd_unlink)
{
- if (!bdev)
+ if (!bdev_file)
return;
if (do_bd_unlink)
- bd_unlink_disk_holder(bdev, device->vdisk);
- blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
+ bd_unlink_disk_holder(file_bdev(bdev_file), device->vdisk);
+ fput(bdev_file);
}
void drbd_backing_dev_free(struct drbd_device *device, struct drbd_backing_dev *ldev)
@@ -1696,8 +1706,9 @@ void drbd_backing_dev_free(struct drbd_device *device, struct drbd_backing_dev *
if (ldev == NULL)
return;
- close_backing_dev(device, ldev->md_bdev, ldev->md_bdev != ldev->backing_bdev);
- close_backing_dev(device, ldev->backing_bdev, true);
+ close_backing_dev(device, ldev->f_md_bdev,
+ ldev->md_bdev != ldev->backing_bdev);
+ close_backing_dev(device, ldev->backing_bdev_file, true);
kfree(ldev->disk_conf);
kfree(ldev);
@@ -2014,13 +2025,15 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
drbd_info(device, "Assuming that all blocks are out of sync "
"(aka FullSync)\n");
if (drbd_bitmap_io(device, &drbd_bmio_set_n_write,
- "set_n_write from attaching", BM_LOCKED_MASK)) {
+ "set_n_write from attaching", BM_LOCKED_MASK,
+ NULL)) {
retcode = ERR_IO_MD_DISK;
goto force_diskless_dec;
}
} else {
if (drbd_bitmap_io(device, &drbd_bm_read,
- "read from attaching", BM_LOCKED_MASK)) {
+ "read from attaching", BM_LOCKED_MASK,
+ NULL)) {
retcode = ERR_IO_MD_DISK;
goto force_diskless_dec;
}
@@ -2111,8 +2124,9 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
fail:
conn_reconfig_done(connection);
if (nbc) {
- close_backing_dev(device, nbc->md_bdev, nbc->md_bdev != nbc->backing_bdev);
- close_backing_dev(device, nbc->backing_bdev, true);
+ close_backing_dev(device, nbc->f_md_bdev,
+ nbc->md_bdev != nbc->backing_bdev);
+ close_backing_dev(device, nbc->backing_bdev_file, true);
kfree(nbc);
}
kfree(new_disk_conf);
@@ -2433,7 +2447,7 @@ int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info)
mutex_unlock(&connection->resource->conf_update);
mutex_unlock(&connection->data.mutex);
- kvfree_rcu(old_net_conf);
+ kvfree_rcu_mightsleep(old_net_conf);
if (connection->cstate >= C_WF_REPORT_PARAMS) {
struct drbd_peer_device *peer_device;
@@ -2847,7 +2861,7 @@ int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info)
new_disk_conf->disk_size = (sector_t)rs.resize_size;
rcu_assign_pointer(device->ldev->disk_conf, new_disk_conf);
mutex_unlock(&device->resource->conf_update);
- kvfree_rcu(old_disk_conf);
+ kvfree_rcu_mightsleep(old_disk_conf);
new_disk_conf = NULL;
}
@@ -2959,7 +2973,7 @@ int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info)
retcode = drbd_request_state(device, NS(disk, D_INCONSISTENT));
if (retcode >= SS_SUCCESS) {
if (drbd_bitmap_io(device, &drbd_bmio_set_n_write,
- "set_n_write from invalidate", BM_LOCKED_MASK))
+ "set_n_write from invalidate", BM_LOCKED_MASK, NULL))
retcode = ERR_IO_MD_DISK;
}
} else
@@ -2992,11 +3006,12 @@ out:
return 0;
}
-static int drbd_bmio_set_susp_al(struct drbd_device *device) __must_hold(local)
+static int drbd_bmio_set_susp_al(struct drbd_device *device,
+ struct drbd_peer_device *peer_device) __must_hold(local)
{
int rv;
- rv = drbd_bmio_set_n_write(device);
+ rv = drbd_bmio_set_n_write(device, peer_device);
drbd_suspend_al(device);
return rv;
}
@@ -3039,7 +3054,7 @@ int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info)
if (retcode >= SS_SUCCESS) {
if (drbd_bitmap_io(device, &drbd_bmio_set_susp_al,
"set_n_write from invalidate_peer",
- BM_LOCKED_SET_ALLOWED))
+ BM_LOCKED_SET_ALLOWED, NULL))
retcode = ERR_IO_MD_DISK;
}
} else
@@ -4135,7 +4150,7 @@ int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info)
if (args.clear_bm) {
err = drbd_bitmap_io(device, &drbd_bmio_clear_n_write,
- "clear_n_write from new_c_uuid", BM_LOCKED_MASK);
+ "clear_n_write from new_c_uuid", BM_LOCKED_MASK, NULL);
if (err) {
drbd_err(device, "Writing bitmap failed with %d\n", err);
retcode = ERR_IO_MD_DISK;
@@ -4254,7 +4269,7 @@ static void device_to_info(struct device_info *info,
int drbd_adm_new_minor(struct sk_buff *skb, struct genl_info *info)
{
struct drbd_config_context adm_ctx;
- struct drbd_genlmsghdr *dh = info->userhdr;
+ struct drbd_genlmsghdr *dh = genl_info_userhdr(info);
enum drbd_ret_code retcode;
retcode = drbd_adm_prepare(&adm_ctx, skb, info, DRBD_ADM_NEED_RESOURCE);
@@ -4752,7 +4767,7 @@ void notify_helper(enum drbd_notification_type type,
struct drbd_genlmsghdr *dh;
int err;
- strlcpy(helper_info.helper_name, name, sizeof(helper_info.helper_name));
+ strscpy(helper_info.helper_name, name, sizeof(helper_info.helper_name));
helper_info.helper_name_len = min(strlen(name), sizeof(helper_info.helper_name));
helper_info.helper_status = status;
diff --git a/drivers/block/drbd/drbd_nla.c b/drivers/block/drbd/drbd_nla.c
index 6a09b0b98018..df0d241d3f6a 100644
--- a/drivers/block/drbd/drbd_nla.c
+++ b/drivers/block/drbd/drbd_nla.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/kernel.h>
#include <net/netlink.h>
#include <linux/drbd_genl_api.h>
diff --git a/drivers/block/drbd/drbd_nla.h b/drivers/block/drbd/drbd_nla.h
index f5eaffb6474e..d3555df0d353 100644
--- a/drivers/block/drbd/drbd_nla.h
+++ b/drivers/block/drbd/drbd_nla.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __DRBD_NLA_H
#define __DRBD_NLA_H
diff --git a/drivers/block/drbd/drbd_polymorph_printk.h b/drivers/block/drbd/drbd_polymorph_printk.h
new file mode 100644
index 000000000000..8e0082d139ba
--- /dev/null
+++ b/drivers/block/drbd/drbd_polymorph_printk.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef DRBD_POLYMORPH_PRINTK_H
+#define DRBD_POLYMORPH_PRINTK_H
+
+#if !defined(CONFIG_DYNAMIC_DEBUG)
+#undef DEFINE_DYNAMIC_DEBUG_METADATA
+#undef __dynamic_pr_debug
+#undef DYNAMIC_DEBUG_BRANCH
+#define DEFINE_DYNAMIC_DEBUG_METADATA(D, F) const char *D = F; ((void)D)
+#define __dynamic_pr_debug(D, F, args...) do { (void)(D); if (0) printk(F, ## args); } while (0)
+#define DYNAMIC_DEBUG_BRANCH(D) false
+#endif
+
+
+#define __drbd_printk_drbd_device_prep(device) \
+ const struct drbd_device *__d = (device); \
+ const struct drbd_resource *__r = __d->resource
+#define __drbd_printk_drbd_device_fmt(fmt) "drbd %s/%u drbd%u: " fmt
+#define __drbd_printk_drbd_device_args() __r->name, __d->vnr, __d->minor
+#define __drbd_printk_drbd_device_unprep()
+
+#define __drbd_printk_drbd_peer_device_prep(peer_device) \
+ const struct drbd_device *__d; \
+ const struct drbd_resource *__r; \
+ __d = (peer_device)->device; \
+ __r = __d->resource
+#define __drbd_printk_drbd_peer_device_fmt(fmt) \
+ "drbd %s/%u drbd%u: " fmt
+#define __drbd_printk_drbd_peer_device_args() \
+ __r->name, __d->vnr, __d->minor
+#define __drbd_printk_drbd_peer_device_unprep()
+
+#define __drbd_printk_drbd_resource_prep(resource) \
+ const struct drbd_resource *__r = resource
+#define __drbd_printk_drbd_resource_fmt(fmt) "drbd %s: " fmt
+#define __drbd_printk_drbd_resource_args() __r->name
+#define __drbd_printk_drbd_resource_unprep(resource)
+
+#define __drbd_printk_drbd_connection_prep(connection) \
+ const struct drbd_connection *__c = (connection); \
+ const struct drbd_resource *__r = __c->resource
+#define __drbd_printk_drbd_connection_fmt(fmt) \
+ "drbd %s: " fmt
+#define __drbd_printk_drbd_connection_args() \
+ __r->name
+#define __drbd_printk_drbd_connection_unprep()
+
+void drbd_printk_with_wrong_object_type(void);
+void drbd_dyn_dbg_with_wrong_object_type(void);
+
+#define __drbd_printk_choose_cond(obj, struct_name) \
+ (__builtin_types_compatible_p(typeof(obj), struct struct_name *) || \
+ __builtin_types_compatible_p(typeof(obj), const struct struct_name *))
+#define __drbd_printk_if_same_type(obj, struct_name, level, fmt, args...) \
+ __drbd_printk_choose_cond(obj, struct_name), \
+({ \
+ __drbd_printk_ ## struct_name ## _prep((const struct struct_name *)(obj)); \
+ printk(level __drbd_printk_ ## struct_name ## _fmt(fmt), \
+ __drbd_printk_ ## struct_name ## _args(), ## args); \
+ __drbd_printk_ ## struct_name ## _unprep(); \
+})
+
+#define drbd_printk(level, obj, fmt, args...) \
+ __builtin_choose_expr( \
+ __drbd_printk_if_same_type(obj, drbd_device, level, fmt, ## args), \
+ __builtin_choose_expr( \
+ __drbd_printk_if_same_type(obj, drbd_resource, level, fmt, ## args), \
+ __builtin_choose_expr( \
+ __drbd_printk_if_same_type(obj, drbd_connection, level, fmt, ## args), \
+ __builtin_choose_expr( \
+ __drbd_printk_if_same_type(obj, drbd_peer_device, level, fmt, ## args), \
+ drbd_printk_with_wrong_object_type()))))
+
+#define __drbd_dyn_dbg_if_same_type(obj, struct_name, fmt, args...) \
+ __drbd_printk_choose_cond(obj, struct_name), \
+({ \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
+ if (DYNAMIC_DEBUG_BRANCH(descriptor)) { \
+ __drbd_printk_ ## struct_name ## _prep((const struct struct_name *)(obj)); \
+ __dynamic_pr_debug(&descriptor, __drbd_printk_ ## struct_name ## _fmt(fmt), \
+ __drbd_printk_ ## struct_name ## _args(), ## args); \
+ __drbd_printk_ ## struct_name ## _unprep(); \
+ } \
+})
+
+#define dynamic_drbd_dbg(obj, fmt, args...) \
+ __builtin_choose_expr( \
+ __drbd_dyn_dbg_if_same_type(obj, drbd_device, fmt, ## args), \
+ __builtin_choose_expr( \
+ __drbd_dyn_dbg_if_same_type(obj, drbd_resource, fmt, ## args), \
+ __builtin_choose_expr( \
+ __drbd_dyn_dbg_if_same_type(obj, drbd_connection, fmt, ## args), \
+ __builtin_choose_expr( \
+ __drbd_dyn_dbg_if_same_type(obj, drbd_peer_device, fmt, ## args), \
+ drbd_dyn_dbg_with_wrong_object_type()))))
+
+#define drbd_emerg(device, fmt, args...) \
+ drbd_printk(KERN_EMERG, device, fmt, ## args)
+#define drbd_alert(device, fmt, args...) \
+ drbd_printk(KERN_ALERT, device, fmt, ## args)
+#define drbd_crit(device, fmt, args...) \
+ drbd_printk(KERN_CRIT, device, fmt, ## args)
+#define drbd_err(device, fmt, args...) \
+ drbd_printk(KERN_ERR, device, fmt, ## args)
+#define drbd_warn(device, fmt, args...) \
+ drbd_printk(KERN_WARNING, device, fmt, ## args)
+#define drbd_notice(device, fmt, args...) \
+ drbd_printk(KERN_NOTICE, device, fmt, ## args)
+#define drbd_info(device, fmt, args...) \
+ drbd_printk(KERN_INFO, device, fmt, ## args)
+
+
+#define drbd_ratelimit() \
+({ \
+ static DEFINE_RATELIMIT_STATE(_rs, \
+ DEFAULT_RATELIMIT_INTERVAL, \
+ DEFAULT_RATELIMIT_BURST); \
+ __ratelimit(&_rs); \
+})
+
+#define D_ASSERT(x, exp) \
+ do { \
+ if (!(exp)) \
+ drbd_err(x, "ASSERTION %s FAILED in %s\n", \
+ #exp, __func__); \
+ } while (0)
+
+/**
+ * expect - Make an assertion
+ *
+ * Unlike the assert macro, this macro returns a boolean result.
+ */
+#define expect(x, exp) ({ \
+ bool _bool = (exp); \
+ if (!_bool && drbd_ratelimit()) \
+ drbd_err(x, "ASSERTION %s FAILED in %s\n", \
+ #exp, __func__); \
+ _bool; \
+ })
+
+#endif
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index 3c0193de2498..1d0feafceadc 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_proc.c
@@ -228,7 +228,7 @@ int drbd_seq_show(struct seq_file *seq, void *v)
};
seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n",
- API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
+ GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
/*
cs .. connection state
diff --git a/drivers/block/drbd/drbd_protocol.h b/drivers/block/drbd/drbd_protocol.h
index a882b65ab5d2..56bbca9d7700 100644
--- a/drivers/block/drbd/drbd_protocol.h
+++ b/drivers/block/drbd/drbd_protocol.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __DRBD_PROTOCOL_H
#define __DRBD_PROTOCOL_H
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index af4c7d65490b..3de919b6f0e1 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_receiver.c
@@ -27,13 +27,13 @@
#include <uapi/linux/sched/types.h>
#include <linux/sched/signal.h>
#include <linux/pkt_sched.h>
-#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <linux/vmalloc.h>
#include <linux/random.h>
#include <linux/string.h>
#include <linux/scatterlist.h>
#include <linux/part_stat.h>
+#include <linux/mempool.h>
#include "drbd_int.h"
#include "drbd_protocol.h"
#include "drbd_req.h"
@@ -64,182 +64,31 @@ static int e_end_block(struct drbd_work *, int);
#define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN)
-/*
- * some helper functions to deal with single linked page lists,
- * page->private being our "next" pointer.
- */
-
-/* If at least n pages are linked at head, get n pages off.
- * Otherwise, don't modify head, and return NULL.
- * Locking is the responsibility of the caller.
- */
-static struct page *page_chain_del(struct page **head, int n)
-{
- struct page *page;
- struct page *tmp;
-
- BUG_ON(!n);
- BUG_ON(!head);
-
- page = *head;
-
- if (!page)
- return NULL;
-
- while (page) {
- tmp = page_chain_next(page);
- if (--n == 0)
- break; /* found sufficient pages */
- if (tmp == NULL)
- /* insufficient pages, don't use any of them. */
- return NULL;
- page = tmp;
- }
-
- /* add end of list marker for the returned list */
- set_page_private(page, 0);
- /* actual return value, and adjustment of head */
- page = *head;
- *head = tmp;
- return page;
-}
-
-/* may be used outside of locks to find the tail of a (usually short)
- * "private" page chain, before adding it back to a global chain head
- * with page_chain_add() under a spinlock. */
-static struct page *page_chain_tail(struct page *page, int *len)
-{
- struct page *tmp;
- int i = 1;
- while ((tmp = page_chain_next(page))) {
- ++i;
- page = tmp;
- }
- if (len)
- *len = i;
- return page;
-}
-
-static int page_chain_free(struct page *page)
-{
- struct page *tmp;
- int i = 0;
- page_chain_for_each_safe(page, tmp) {
- put_page(page);
- ++i;
- }
- return i;
-}
-
-static void page_chain_add(struct page **head,
- struct page *chain_first, struct page *chain_last)
-{
-#if 1
- struct page *tmp;
- tmp = page_chain_tail(chain_first, NULL);
- BUG_ON(tmp != chain_last);
-#endif
-
- /* add chain to head */
- set_page_private(chain_last, (unsigned long)*head);
- *head = chain_first;
-}
-
-static struct page *__drbd_alloc_pages(struct drbd_device *device,
- unsigned int number)
+static struct page *__drbd_alloc_pages(unsigned int number)
{
struct page *page = NULL;
struct page *tmp = NULL;
unsigned int i = 0;
- /* Yes, testing drbd_pp_vacant outside the lock is racy.
- * So what. It saves a spin_lock. */
- if (drbd_pp_vacant >= number) {
- spin_lock(&drbd_pp_lock);
- page = page_chain_del(&drbd_pp_pool, number);
- if (page)
- drbd_pp_vacant -= number;
- spin_unlock(&drbd_pp_lock);
- if (page)
- return page;
- }
-
/* GFP_TRY, because we must not cause arbitrary write-out: in a DRBD
* "criss-cross" setup, that might cause write-out on some other DRBD,
* which in turn might block on the other node at this very place. */
for (i = 0; i < number; i++) {
- tmp = alloc_page(GFP_TRY);
+ tmp = mempool_alloc(&drbd_buffer_page_pool, GFP_TRY);
if (!tmp)
- break;
+ goto fail;
set_page_private(tmp, (unsigned long)page);
page = tmp;
}
-
- if (i == number)
- return page;
-
- /* Not enough pages immediately available this time.
- * No need to jump around here, drbd_alloc_pages will retry this
- * function "soon". */
- if (page) {
- tmp = page_chain_tail(page, NULL);
- spin_lock(&drbd_pp_lock);
- page_chain_add(&drbd_pp_pool, page, tmp);
- drbd_pp_vacant += i;
- spin_unlock(&drbd_pp_lock);
+ return page;
+fail:
+ page_chain_for_each_safe(page, tmp) {
+ set_page_private(page, 0);
+ mempool_free(page, &drbd_buffer_page_pool);
}
return NULL;
}
-static void reclaim_finished_net_peer_reqs(struct drbd_device *device,
- struct list_head *to_be_freed)
-{
- struct drbd_peer_request *peer_req, *tmp;
-
- /* The EEs are always appended to the end of the list. Since
- they are sent in order over the wire, they have to finish
- in order. As soon as we see the first not finished we can
- stop to examine the list... */
-
- list_for_each_entry_safe(peer_req, tmp, &device->net_ee, w.list) {
- if (drbd_peer_req_has_active_page(peer_req))
- break;
- list_move(&peer_req->w.list, to_be_freed);
- }
-}
-
-static void drbd_reclaim_net_peer_reqs(struct drbd_device *device)
-{
- LIST_HEAD(reclaimed);
- struct drbd_peer_request *peer_req, *t;
-
- spin_lock_irq(&device->resource->req_lock);
- reclaim_finished_net_peer_reqs(device, &reclaimed);
- spin_unlock_irq(&device->resource->req_lock);
- list_for_each_entry_safe(peer_req, t, &reclaimed, w.list)
- drbd_free_net_peer_req(device, peer_req);
-}
-
-static void conn_reclaim_net_peer_reqs(struct drbd_connection *connection)
-{
- struct drbd_peer_device *peer_device;
- int vnr;
-
- rcu_read_lock();
- idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
- struct drbd_device *device = peer_device->device;
- if (!atomic_read(&device->pp_in_use_by_net))
- continue;
-
- kref_get(&device->kref);
- rcu_read_unlock();
- drbd_reclaim_net_peer_reqs(device);
- kref_put(&device->kref, drbd_destroy_device);
- rcu_read_lock();
- }
- rcu_read_unlock();
-}
-
/**
* drbd_alloc_pages() - Returns @number pages, retries forever (or until signalled)
* @peer_device: DRBD device.
@@ -264,9 +113,8 @@ struct page *drbd_alloc_pages(struct drbd_peer_device *peer_device, unsigned int
bool retry)
{
struct drbd_device *device = peer_device->device;
- struct page *page = NULL;
+ struct page *page;
struct net_conf *nc;
- DEFINE_WAIT(wait);
unsigned int mxb;
rcu_read_lock();
@@ -274,37 +122,9 @@ struct page *drbd_alloc_pages(struct drbd_peer_device *peer_device, unsigned int
mxb = nc ? nc->max_buffers : 1000000;
rcu_read_unlock();
- if (atomic_read(&device->pp_in_use) < mxb)
- page = __drbd_alloc_pages(device, number);
-
- /* Try to keep the fast path fast, but occasionally we need
- * to reclaim the pages we lended to the network stack. */
- if (page && atomic_read(&device->pp_in_use_by_net) > 512)
- drbd_reclaim_net_peer_reqs(device);
-
- while (page == NULL) {
- prepare_to_wait(&drbd_pp_wait, &wait, TASK_INTERRUPTIBLE);
-
- drbd_reclaim_net_peer_reqs(device);
-
- if (atomic_read(&device->pp_in_use) < mxb) {
- page = __drbd_alloc_pages(device, number);
- if (page)
- break;
- }
-
- if (!retry)
- break;
-
- if (signal_pending(current)) {
- drbd_warn(device, "drbd_alloc_pages interrupted!\n");
- break;
- }
-
- if (schedule_timeout(HZ/10) == 0)
- mxb = UINT_MAX;
- }
- finish_wait(&drbd_pp_wait, &wait);
+ if (atomic_read(&device->pp_in_use) >= mxb)
+ schedule_timeout_interruptible(HZ / 10);
+ page = __drbd_alloc_pages(number);
if (page)
atomic_add(number, &device->pp_in_use);
@@ -315,29 +135,25 @@ struct page *drbd_alloc_pages(struct drbd_peer_device *peer_device, unsigned int
* Is also used from inside an other spin_lock_irq(&resource->req_lock);
* Either links the page chain back to the global pool,
* or returns all pages to the system. */
-static void drbd_free_pages(struct drbd_device *device, struct page *page, int is_net)
+static void drbd_free_pages(struct drbd_device *device, struct page *page)
{
- atomic_t *a = is_net ? &device->pp_in_use_by_net : &device->pp_in_use;
- int i;
+ struct page *tmp;
+ int i = 0;
if (page == NULL)
return;
- if (drbd_pp_vacant > (DRBD_MAX_BIO_SIZE/PAGE_SIZE) * drbd_minor_count)
- i = page_chain_free(page);
- else {
- struct page *tmp;
- tmp = page_chain_tail(page, &i);
- spin_lock(&drbd_pp_lock);
- page_chain_add(&drbd_pp_pool, page, tmp);
- drbd_pp_vacant += i;
- spin_unlock(&drbd_pp_lock);
- }
- i = atomic_sub_return(i, a);
+ page_chain_for_each_safe(page, tmp) {
+ set_page_private(page, 0);
+ if (page_count(page) == 1)
+ mempool_free(page, &drbd_buffer_page_pool);
+ else
+ put_page(page);
+ i++;
+ }
+ i = atomic_sub_return(i, &device->pp_in_use);
if (i < 0)
- drbd_warn(device, "ASSERTION FAILED: %s: %d < 0\n",
- is_net ? "pp_in_use_by_net" : "pp_in_use", i);
- wake_up(&drbd_pp_wait);
+ drbd_warn(device, "ASSERTION FAILED: pp_in_use: %d < 0\n", i);
}
/*
@@ -381,6 +197,8 @@ drbd_alloc_peer_req(struct drbd_peer_device *peer_device, u64 id, sector_t secto
gfpflags_allow_blocking(gfp_mask));
if (!page)
goto fail;
+ if (!mempool_is_saturated(&drbd_buffer_page_pool))
+ peer_req->flags |= EE_RELEASE_TO_MEMPOOL;
}
memset(peer_req, 0, sizeof(*peer_req));
@@ -404,16 +222,15 @@ drbd_alloc_peer_req(struct drbd_peer_device *peer_device, u64 id, sector_t secto
return NULL;
}
-void __drbd_free_peer_req(struct drbd_device *device, struct drbd_peer_request *peer_req,
- int is_net)
+void drbd_free_peer_req(struct drbd_device *device, struct drbd_peer_request *peer_req)
{
might_sleep();
if (peer_req->flags & EE_HAS_DIGEST)
kfree(peer_req->digest);
- drbd_free_pages(device, peer_req->pages, is_net);
+ drbd_free_pages(device, peer_req->pages);
D_ASSERT(device, atomic_read(&peer_req->pending_bios) == 0);
D_ASSERT(device, drbd_interval_empty(&peer_req->i));
- if (!expect(!(peer_req->flags & EE_CALL_AL_COMPLETE_IO))) {
+ if (!expect(device, !(peer_req->flags & EE_CALL_AL_COMPLETE_IO))) {
peer_req->flags &= ~EE_CALL_AL_COMPLETE_IO;
drbd_al_complete_io(device, &peer_req->i);
}
@@ -425,14 +242,13 @@ int drbd_free_peer_reqs(struct drbd_device *device, struct list_head *list)
LIST_HEAD(work_list);
struct drbd_peer_request *peer_req, *t;
int count = 0;
- int is_net = list == &device->net_ee;
spin_lock_irq(&device->resource->req_lock);
list_splice_init(list, &work_list);
spin_unlock_irq(&device->resource->req_lock);
list_for_each_entry_safe(peer_req, t, &work_list, w.list) {
- __drbd_free_peer_req(device, peer_req, is_net);
+ drbd_free_peer_req(device, peer_req);
count++;
}
return count;
@@ -444,18 +260,13 @@ int drbd_free_peer_reqs(struct drbd_device *device, struct list_head *list)
static int drbd_finish_peer_reqs(struct drbd_device *device)
{
LIST_HEAD(work_list);
- LIST_HEAD(reclaimed);
struct drbd_peer_request *peer_req, *t;
int err = 0;
spin_lock_irq(&device->resource->req_lock);
- reclaim_finished_net_peer_reqs(device, &reclaimed);
list_splice_init(&device->done_ee, &work_list);
spin_unlock_irq(&device->resource->req_lock);
- list_for_each_entry_safe(peer_req, t, &reclaimed, w.list)
- drbd_free_net_peer_req(device, peer_req);
-
/* possible callbacks here:
* e_end_block, and e_end_resync_block, e_send_superseded.
* all ignore the last argument.
@@ -507,7 +318,7 @@ static int drbd_recv_short(struct socket *sock, void *buf, size_t size, int flag
struct msghdr msg = {
.msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL)
};
- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, size);
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, size);
return sock_recvmsg(sock, &msg, msg.msg_flags);
}
@@ -639,7 +450,7 @@ static struct socket *drbd_try_connect(struct drbd_connection *connection)
* a free one dynamically.
*/
what = "bind before connect";
- err = sock->ops->bind(sock, (struct sockaddr *) &src_in6, my_addr_len);
+ err = sock->ops->bind(sock, (struct sockaddr_unsized *) &src_in6, my_addr_len);
if (err < 0)
goto out;
@@ -647,7 +458,7 @@ static struct socket *drbd_try_connect(struct drbd_connection *connection)
* stay C_WF_CONNECTION, don't go Disconnecting! */
disconnect_on_error = 0;
what = "connect";
- err = sock->ops->connect(sock, (struct sockaddr *) &peer_in6, peer_addr_len, 0);
+ err = sock->ops->connect(sock, (struct sockaddr_unsized *) &peer_in6, peer_addr_len, 0);
out:
if (err < 0) {
@@ -726,7 +537,7 @@ static int prepare_listen_socket(struct drbd_connection *connection, struct acce
drbd_setbufsize(s_listen, sndbuf_size, rcvbuf_size);
what = "bind before listen";
- err = s_listen->ops->bind(s_listen, (struct sockaddr *)&my_addr, my_addr_len);
+ err = s_listen->ops->bind(s_listen, (struct sockaddr_unsized *)&my_addr, my_addr_len);
if (err < 0)
goto out;
@@ -781,7 +592,7 @@ static struct socket *drbd_wait_for_connect(struct drbd_connection *connection,
timeo = connect_int * HZ;
/* 28.5% random jitter */
- timeo += (prandom_u32() & 1) ? timeo / 7 : -timeo / 7;
+ timeo += get_random_u32_below(2) ? timeo / 7 : -timeo / 7;
err = wait_for_completion_interruptible_timeout(&ad->door_bell, timeo);
if (err <= 0)
@@ -1004,7 +815,7 @@ retry:
drbd_warn(connection, "Error receiving initial packet\n");
sock_release(s);
randomize:
- if (prandom_u32() & 1)
+ if (get_random_u32_below(2))
goto retry;
}
}
@@ -1030,6 +841,9 @@ randomize:
sock.socket->sk->sk_allocation = GFP_NOIO;
msock.socket->sk->sk_allocation = GFP_NOIO;
+ sock.socket->sk->sk_use_task_frag = false;
+ msock.socket->sk->sk_use_task_frag = false;
+
sock.socket->sk->sk_priority = TC_PRIO_INTERACTIVE_BULK;
msock.socket->sk->sk_priority = TC_PRIO_INTERACTIVE;
@@ -1280,7 +1094,7 @@ static void one_flush_endio(struct bio *bio)
static void submit_one_flush(struct drbd_device *device, struct issue_flush_context *ctx)
{
struct bio *bio = bio_alloc(device->ldev->backing_bdev, 0,
- REQ_OP_FLUSH | REQ_PREFLUSH, GFP_NOIO);
+ REQ_OP_WRITE | REQ_PREFLUSH, GFP_NOIO);
struct one_flush_context *octx = kmalloc(sizeof(*octx), GFP_NOIO);
if (!octx) {
@@ -1603,9 +1417,19 @@ static void drbd_issue_peer_discard_or_zero_out(struct drbd_device *device, stru
drbd_endio_write_sec_final(peer_req);
}
+static int peer_request_fault_type(struct drbd_peer_request *peer_req)
+{
+ if (peer_req_op(peer_req) == REQ_OP_READ) {
+ return peer_req->flags & EE_APPLICATION ?
+ DRBD_FAULT_DT_RD : DRBD_FAULT_RS_RD;
+ } else {
+ return peer_req->flags & EE_APPLICATION ?
+ DRBD_FAULT_DT_WR : DRBD_FAULT_RS_WR;
+ }
+}
+
/**
* drbd_submit_peer_request()
- * @device: DRBD device.
* @peer_req: peer request
*
* May spread the pages to multiple bios,
@@ -1619,10 +1443,9 @@ static void drbd_issue_peer_discard_or_zero_out(struct drbd_device *device, stru
* on certain Xen deployments.
*/
/* TODO allocate from our own bio_set. */
-int drbd_submit_peer_request(struct drbd_device *device,
- struct drbd_peer_request *peer_req,
- const blk_opf_t opf, const int fault_type)
+int drbd_submit_peer_request(struct drbd_peer_request *peer_req)
{
+ struct drbd_device *device = peer_req->peer_device->device;
struct bio *bios = NULL;
struct bio *bio;
struct page *page = peer_req->pages;
@@ -1667,7 +1490,18 @@ int drbd_submit_peer_request(struct drbd_device *device,
* generated bio, but a bio allocated on behalf of the peer.
*/
next_bio:
- bio = bio_alloc(device->ldev->backing_bdev, nr_pages, opf, GFP_NOIO);
+ /* _DISCARD, _WRITE_ZEROES handled above.
+ * REQ_OP_FLUSH (empty flush) not expected,
+ * should have been mapped to a "drbd protocol barrier".
+ * REQ_OP_SECURE_ERASE: I don't see how we could ever support that.
+ */
+ if (!(peer_req_op(peer_req) == REQ_OP_WRITE ||
+ peer_req_op(peer_req) == REQ_OP_READ)) {
+ drbd_err(device, "Invalid bio op received: 0x%x\n", peer_req->opf);
+ return -EINVAL;
+ }
+
+ bio = bio_alloc(device->ldev->backing_bdev, nr_pages, peer_req->opf, GFP_NOIO);
/* > peer_req->i.sector, unless this is the first bio */
bio->bi_iter.bi_sector = sector;
bio->bi_private = peer_req;
@@ -1697,7 +1531,7 @@ next_bio:
bios = bios->bi_next;
bio->bi_next = NULL;
- drbd_submit_bio_noacct(device, fault_type, bio);
+ drbd_submit_bio_noacct(device, peer_request_fault_type(peer_req), bio);
} while (bios);
return 0;
}
@@ -1853,21 +1687,21 @@ read_in_block(struct drbd_peer_device *peer_device, u64 id, sector_t sector,
/* assume request_size == data_size, but special case trim. */
ds = data_size;
if (trim) {
- if (!expect(data_size == 0))
+ if (!expect(peer_device, data_size == 0))
return NULL;
ds = be32_to_cpu(trim->size);
} else if (zeroes) {
- if (!expect(data_size == 0))
+ if (!expect(peer_device, data_size == 0))
return NULL;
ds = be32_to_cpu(zeroes->size);
}
- if (!expect(IS_ALIGNED(ds, 512)))
+ if (!expect(peer_device, IS_ALIGNED(ds, 512)))
return NULL;
if (trim || zeroes) {
- if (!expect(ds <= (DRBD_MAX_BBIO_SECTORS << 9)))
+ if (!expect(peer_device, ds <= (DRBD_MAX_BBIO_SECTORS << 9)))
return NULL;
- } else if (!expect(ds <= DRBD_MAX_BIO_SIZE))
+ } else if (!expect(peer_device, ds <= DRBD_MAX_BIO_SIZE))
return NULL;
/* even though we trust out peer,
@@ -1902,13 +1736,13 @@ read_in_block(struct drbd_peer_device *peer_device, u64 id, sector_t sector,
page = peer_req->pages;
page_chain_for_each(page) {
unsigned len = min_t(int, ds, PAGE_SIZE);
- data = kmap(page);
+ data = kmap_local_page(page);
err = drbd_recv_all_warn(peer_device->connection, data, len);
if (drbd_insert_fault(device, DRBD_FAULT_RECEIVE)) {
drbd_err(device, "Fault injection: Corrupting data on receive\n");
data[0] = data[0] ^ (unsigned long)-1;
}
- kunmap(page);
+ kunmap_local(data);
if (err) {
drbd_free_peer_req(device, peer_req);
return NULL;
@@ -1943,7 +1777,7 @@ static int drbd_drain_block(struct drbd_peer_device *peer_device, int data_size)
page = drbd_alloc_pages(peer_device, 1, 1);
- data = kmap(page);
+ data = kmap_local_page(page);
while (data_size) {
unsigned int len = min_t(int, data_size, PAGE_SIZE);
@@ -1952,8 +1786,8 @@ static int drbd_drain_block(struct drbd_peer_device *peer_device, int data_size)
break;
data_size -= len;
}
- kunmap(page);
- drbd_free_pages(peer_device->device, page, 0);
+ kunmap_local(data);
+ drbd_free_pages(peer_device->device, page);
return err;
}
@@ -2021,11 +1855,11 @@ static int e_end_resync_block(struct drbd_work *w, int unused)
D_ASSERT(device, drbd_interval_empty(&peer_req->i));
if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
- drbd_set_in_sync(device, sector, peer_req->i.size);
+ drbd_set_in_sync(peer_device, sector, peer_req->i.size);
err = drbd_send_ack(peer_device, P_RS_WRITE_ACK, peer_req);
} else {
/* Record failure to sync */
- drbd_rs_failed_io(device, sector, peer_req->i.size);
+ drbd_rs_failed_io(peer_device, sector, peer_req->i.size);
err = drbd_send_ack(peer_device, P_NEG_ACK, peer_req);
}
@@ -2044,13 +1878,14 @@ static int recv_resync_read(struct drbd_peer_device *peer_device, sector_t secto
if (!peer_req)
goto fail;
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
inc_unacked(device);
/* corresponding dec_unacked() in e_end_resync_block()
* respective _drbd_clear_done_ee */
peer_req->w.cb = e_end_resync_block;
+ peer_req->opf = REQ_OP_WRITE;
peer_req->submit_jif = jiffies;
spin_lock_irq(&device->resource->req_lock);
@@ -2058,8 +1893,7 @@ static int recv_resync_read(struct drbd_peer_device *peer_device, sector_t secto
spin_unlock_irq(&device->resource->req_lock);
atomic_add(pi->size >> 9, &device->rs_sect_ev);
- if (drbd_submit_peer_request(device, peer_req, REQ_OP_WRITE,
- DRBD_FAULT_RS_WR) == 0)
+ if (drbd_submit_peer_request(peer_req) == 0)
return 0;
/* don't care for the reason here */
@@ -2113,12 +1947,9 @@ static int receive_DataReply(struct drbd_connection *connection, struct packet_i
if (unlikely(!req))
return -EIO;
- /* hlist_del(&req->collision) is done in _req_may_be_done, to avoid
- * special casing it there for the various failure cases.
- * still no race with drbd_fail_pending_reads */
err = recv_dless_read(peer_device, req, sector, pi->size);
if (!err)
- req_mod(req, DATA_RECEIVED);
+ req_mod(req, DATA_RECEIVED, peer_device);
/* else: nothing. handled from drbd_disconnect...
* I don't think we may complete this just yet
* in case we are "on-disconnect: freeze" */
@@ -2148,7 +1979,7 @@ static int receive_RSDataReply(struct drbd_connection *connection, struct packet
* or in drbd_peer_request_endio. */
err = recv_resync_read(peer_device, sector, pi);
} else {
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "Can not write resync data to local disk.\n");
err = drbd_drain_block(peer_device, pi->size);
@@ -2176,7 +2007,7 @@ static void restart_conflicting_writes(struct drbd_device *device,
continue;
/* as it is RQ_POSTPONED, this will cause it to
* be queued on the retry workqueue. */
- __req_mod(req, CONFLICT_RESOLVED, NULL);
+ __req_mod(req, CONFLICT_RESOLVED, NULL, NULL);
}
}
@@ -2200,7 +2031,7 @@ static int e_end_block(struct drbd_work *w, int cancel)
P_RS_WRITE_ACK : P_WRITE_ACK;
err = drbd_send_ack(peer_device, pcmd, peer_req);
if (pcmd == P_RS_WRITE_ACK)
- drbd_set_in_sync(device, sector, peer_req->i.size);
+ drbd_set_in_sync(peer_device, sector, peer_req->i.size);
} else {
err = drbd_send_ack(peer_device, P_NEG_ACK, peer_req);
/* we expect it to be marked out of sync anyways...
@@ -2378,16 +2209,6 @@ static int wait_for_and_update_peer_seq(struct drbd_peer_device *peer_device, co
return ret;
}
-/* see also bio_flags_to_wire()
- * DRBD_REQ_*, because we need to semantically map the flags to data packet
- * flags and back. We may replicate to other kernel versions. */
-static blk_opf_t wire_flags_to_bio_flags(u32 dpf)
-{
- return (dpf & DP_RW_SYNC ? REQ_SYNC : 0) |
- (dpf & DP_FUA ? REQ_FUA : 0) |
- (dpf & DP_FLUSH ? REQ_PREFLUSH : 0);
-}
-
static enum req_op wire_flags_to_bio_op(u32 dpf)
{
if (dpf & DP_ZEROES)
@@ -2398,9 +2219,19 @@ static enum req_op wire_flags_to_bio_op(u32 dpf)
return REQ_OP_WRITE;
}
+/* see also bio_flags_to_wire() */
+static blk_opf_t wire_flags_to_bio(struct drbd_connection *connection, u32 dpf)
+{
+ return wire_flags_to_bio_op(dpf) |
+ (dpf & DP_RW_SYNC ? REQ_SYNC : 0) |
+ (dpf & DP_FUA ? REQ_FUA : 0) |
+ (dpf & DP_FLUSH ? REQ_PREFLUSH : 0);
+}
+
static void fail_postponed_requests(struct drbd_device *device, sector_t sector,
unsigned int size)
{
+ struct drbd_peer_device *peer_device = first_peer_device(device);
struct drbd_interval *i;
repeat:
@@ -2414,7 +2245,7 @@ static void fail_postponed_requests(struct drbd_device *device, sector_t sector,
if (!(req->rq_state & RQ_POSTPONED))
continue;
req->rq_state &= ~RQ_POSTPONED;
- __req_mod(req, NEG_ACKED, &m);
+ __req_mod(req, NEG_ACKED, peer_device, &m);
spin_unlock_irq(&device->resource->req_lock);
if (m.bio)
complete_master_bio(device, &m);
@@ -2481,7 +2312,11 @@ static int handle_write_conflicts(struct drbd_device *device,
peer_req->w.cb = superseded ? e_send_superseded :
e_send_retry_write;
list_add_tail(&peer_req->w.list, &device->done_ee);
- queue_work(connection->ack_sender, &peer_req->peer_device->send_acks_work);
+ /* put is in drbd_send_acks_wf() */
+ kref_get(&device->kref);
+ if (!queue_work(connection->ack_sender,
+ &peer_req->peer_device->send_acks_work))
+ kref_put(&device->kref, drbd_destroy_device);
err = -ENOENT;
goto out;
@@ -2541,8 +2376,6 @@ static int receive_Data(struct drbd_connection *connection, struct packet_info *
struct drbd_peer_request *peer_req;
struct p_data *p = pi->data;
u32 peer_seq = be32_to_cpu(p->seq_num);
- enum req_op op;
- blk_opf_t op_flags;
u32 dp_flags;
int err, tp;
@@ -2581,11 +2414,10 @@ static int receive_Data(struct drbd_connection *connection, struct packet_info *
peer_req->flags |= EE_APPLICATION;
dp_flags = be32_to_cpu(p->dp_flags);
- op = wire_flags_to_bio_op(dp_flags);
- op_flags = wire_flags_to_bio_flags(dp_flags);
+ peer_req->opf = wire_flags_to_bio(connection, dp_flags);
if (pi->cmd == P_TRIM) {
D_ASSERT(peer_device, peer_req->i.size > 0);
- D_ASSERT(peer_device, op == REQ_OP_DISCARD);
+ D_ASSERT(peer_device, peer_req_op(peer_req) == REQ_OP_DISCARD);
D_ASSERT(peer_device, peer_req->pages == NULL);
/* need to play safe: an older DRBD sender
* may mean zero-out while sending P_TRIM. */
@@ -2593,7 +2425,7 @@ static int receive_Data(struct drbd_connection *connection, struct packet_info *
peer_req->flags |= EE_ZEROOUT;
} else if (pi->cmd == P_ZEROES) {
D_ASSERT(peer_device, peer_req->i.size > 0);
- D_ASSERT(peer_device, op == REQ_OP_WRITE_ZEROES);
+ D_ASSERT(peer_device, peer_req_op(peer_req) == REQ_OP_WRITE_ZEROES);
D_ASSERT(peer_device, peer_req->pages == NULL);
/* Do (not) pass down BLKDEV_ZERO_NOUNMAP? */
if (dp_flags & DP_DISCARD)
@@ -2674,14 +2506,13 @@ static int receive_Data(struct drbd_connection *connection, struct packet_info *
if (device->state.pdsk < D_INCONSISTENT) {
/* In case we have the only disk of the cluster, */
- drbd_set_out_of_sync(device, peer_req->i.sector, peer_req->i.size);
+ drbd_set_out_of_sync(peer_device, peer_req->i.sector, peer_req->i.size);
peer_req->flags &= ~EE_MAY_SET_IN_SYNC;
drbd_al_begin_io(device, &peer_req->i);
peer_req->flags |= EE_CALL_AL_COMPLETE_IO;
}
- err = drbd_submit_peer_request(device, peer_req, op | op_flags,
- DRBD_FAULT_DT_WR);
+ err = drbd_submit_peer_request(peer_req);
if (!err)
return 0;
@@ -2714,9 +2545,10 @@ out_interrupted:
* The current sync rate used here uses only the most recent two step marks,
* to have a short time average so we can react faster.
*/
-bool drbd_rs_should_slow_down(struct drbd_device *device, sector_t sector,
+bool drbd_rs_should_slow_down(struct drbd_peer_device *peer_device, sector_t sector,
bool throttle_if_app_is_waiting)
{
+ struct drbd_device *device = peer_device->device;
struct lc_element *tmp;
bool throttle = drbd_rs_c_min_rate_throttle(device);
@@ -2792,7 +2624,6 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
struct drbd_peer_request *peer_req;
struct digest_info *di = NULL;
int size, verb;
- unsigned int fault_type;
struct p_block_req *p = pi->data;
peer_device = conn_peer_device(connection, pi->vnr);
@@ -2829,13 +2660,13 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
break;
case P_OV_REPLY:
verb = 0;
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
drbd_send_ack_ex(peer_device, P_OV_RESULT, sector, size, ID_IN_SYNC);
break;
default:
BUG();
}
- if (verb && __ratelimit(&drbd_ratelimit_state))
+ if (verb && drbd_ratelimit())
drbd_err(device, "Can not satisfy peer's read request, "
"no local data.\n");
@@ -2852,11 +2683,11 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
put_ldev(device);
return -ENOMEM;
}
+ peer_req->opf = REQ_OP_READ;
switch (pi->cmd) {
case P_DATA_REQUEST:
peer_req->w.cb = w_e_end_data_req;
- fault_type = DRBD_FAULT_DT_RD;
/* application IO, don't drbd_rs_begin_io */
peer_req->flags |= EE_APPLICATION;
goto submit;
@@ -2870,14 +2701,12 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
fallthrough;
case P_RS_DATA_REQUEST:
peer_req->w.cb = w_e_end_rsdata_req;
- fault_type = DRBD_FAULT_RS_RD;
/* used in the sector offset progress display */
device->bm_resync_fo = BM_SECT_TO_BIT(sector);
break;
case P_OV_REPLY:
case P_CSUM_RS_REQUEST:
- fault_type = DRBD_FAULT_RS_RD;
di = kmalloc(sizeof(*di) + pi->size, GFP_NOIO);
if (!di)
goto out_free_e;
@@ -2902,7 +2731,7 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
/* track progress, we may need to throttle */
atomic_add(size >> 9, &device->rs_sect_in);
peer_req->w.cb = w_e_end_ov_reply;
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
/* drbd_rs_begin_io done when we sent this request,
* but accounting still needs to be done. */
goto submit_for_resync;
@@ -2926,7 +2755,6 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
(unsigned long long)sector);
}
peer_req->w.cb = w_e_end_ov_req;
- fault_type = DRBD_FAULT_RS_RD;
break;
default:
@@ -2966,7 +2794,7 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
update_receiver_timing_details(connection, drbd_rs_should_slow_down);
if (device->state.peer != R_PRIMARY
- && drbd_rs_should_slow_down(device, sector, false))
+ && drbd_rs_should_slow_down(peer_device, sector, false))
schedule_timeout_uninterruptible(HZ/10);
update_receiver_timing_details(connection, drbd_rs_begin_io);
if (drbd_rs_begin_io(device, sector))
@@ -2978,8 +2806,7 @@ submit_for_resync:
submit:
update_receiver_timing_details(connection, drbd_submit_peer_request);
inc_unacked(device);
- if (drbd_submit_peer_request(device, peer_req, REQ_OP_READ,
- fault_type) == 0)
+ if (drbd_submit_peer_request(peer_req) == 0)
return 0;
/* don't care for the reason here */
@@ -3216,10 +3043,11 @@ static void drbd_uuid_dump(struct drbd_device *device, char *text, u64 *uuid,
-1096 requires proto 96
*/
-static int drbd_uuid_compare(struct drbd_device *const device, enum drbd_role const peer_role, int *rule_nr) __must_hold(local)
+static int drbd_uuid_compare(struct drbd_peer_device *const peer_device,
+ enum drbd_role const peer_role, int *rule_nr) __must_hold(local)
{
- struct drbd_peer_device *const peer_device = first_peer_device(device);
- struct drbd_connection *const connection = peer_device ? peer_device->connection : NULL;
+ struct drbd_connection *const connection = peer_device->connection;
+ struct drbd_device *device = peer_device->device;
u64 self, peer;
int i, j;
@@ -3455,7 +3283,7 @@ static enum drbd_conns drbd_sync_handshake(struct drbd_peer_device *peer_device,
drbd_uuid_dump(device, "peer", device->p_uuid,
device->p_uuid[UI_SIZE], device->p_uuid[UI_FLAGS]);
- hg = drbd_uuid_compare(device, peer_role, &rule_nr);
+ hg = drbd_uuid_compare(peer_device, peer_role, &rule_nr);
spin_unlock_irq(&device->ldev->md.uuid_lock);
drbd_info(device, "uuid_compare()=%d by rule %d\n", hg, rule_nr);
@@ -3581,7 +3409,7 @@ static enum drbd_conns drbd_sync_handshake(struct drbd_peer_device *peer_device,
if (abs(hg) >= 2) {
drbd_info(device, "Writing the whole bitmap, full sync required after drbd_sync_handshake.\n");
if (drbd_bitmap_io(device, &drbd_bmio_set_n_write, "set_n_write from sync_handshake",
- BM_LOCKED_SET_ALLOWED))
+ BM_LOCKED_SET_ALLOWED, NULL))
return C_MASK;
}
@@ -3749,7 +3577,7 @@ static int receive_protocol(struct drbd_connection *connection, struct packet_in
drbd_info(connection, "peer data-integrity-alg: %s\n",
integrity_alg[0] ? integrity_alg : "(none)");
- kvfree_rcu(old_net_conf);
+ kvfree_rcu_mightsleep(old_net_conf);
return 0;
disconnect_rcu_unlock:
@@ -4117,7 +3945,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info
rcu_assign_pointer(device->ldev->disk_conf, new_disk_conf);
mutex_unlock(&connection->resource->conf_update);
- kvfree_rcu(old_disk_conf);
+ kvfree_rcu_mightsleep(old_disk_conf);
drbd_info(device, "Peer sets u_size to %lu sectors (old: %lu)\n",
(unsigned long)p_usize, (unsigned long)my_usize);
@@ -4260,7 +4088,7 @@ static int receive_uuids(struct drbd_connection *connection, struct packet_info
drbd_info(device, "Accepted new current UUID, preparing to skip initial sync\n");
drbd_bitmap_io(device, &drbd_bmio_clear_n_write,
"clear_n_write from receive_uuids",
- BM_LOCKED_TEST_ALLOWED);
+ BM_LOCKED_TEST_ALLOWED, NULL);
_drbd_uuid_set(device, UI_CURRENT, p_uuid[UI_CURRENT]);
_drbd_uuid_set(device, UI_BITMAP, 0);
_drbd_set_state(_NS2(device, disk, D_UP_TO_DATE, pdsk, D_UP_TO_DATE),
@@ -4438,7 +4266,7 @@ static int receive_state(struct drbd_connection *connection, struct packet_info
else if (os.conn >= C_SYNC_SOURCE &&
peer_state.conn == C_CONNECTED) {
if (drbd_bm_total_weight(device) <= device->rs_failed)
- drbd_resync_finished(device);
+ drbd_resync_finished(peer_device);
return 0;
}
}
@@ -4446,8 +4274,8 @@ static int receive_state(struct drbd_connection *connection, struct packet_info
/* explicit verify finished notification, stop sector reached. */
if (os.conn == C_VERIFY_T && os.disk == D_UP_TO_DATE &&
peer_state.conn == C_CONNECTED && real_peer_disk == D_UP_TO_DATE) {
- ov_out_of_sync_print(device);
- drbd_resync_finished(device);
+ ov_out_of_sync_print(peer_device);
+ drbd_resync_finished(peer_device);
return 0;
}
@@ -4756,11 +4584,11 @@ decode_bitmap_c(struct drbd_peer_device *peer_device,
return -EIO;
}
-void INFO_bm_xfer_stats(struct drbd_device *device,
+void INFO_bm_xfer_stats(struct drbd_peer_device *peer_device,
const char *direction, struct bm_xfer_ctx *c)
{
/* what would it take to transfer it "plaintext" */
- unsigned int header_size = drbd_header_size(first_peer_device(device)->connection);
+ unsigned int header_size = drbd_header_size(peer_device->connection);
unsigned int data_size = DRBD_SOCKET_BUFFER_SIZE - header_size;
unsigned int plain =
header_size * (DIV_ROUND_UP(c->bm_words, data_size) + 1) +
@@ -4784,7 +4612,7 @@ void INFO_bm_xfer_stats(struct drbd_device *device,
r = 1000;
r = 1000 - r;
- drbd_info(device, "%s bitmap stats [Bytes(packets)]: plain %u(%u), RLE %u(%u), "
+ drbd_info(peer_device, "%s bitmap stats [Bytes(packets)]: plain %u(%u), RLE %u(%u), "
"total %u; compression: %u.%u%%\n",
direction,
c->bytes[1], c->packets[1],
@@ -4862,12 +4690,12 @@ static int receive_bitmap(struct drbd_connection *connection, struct packet_info
goto out;
}
- INFO_bm_xfer_stats(device, "receive", &c);
+ INFO_bm_xfer_stats(peer_device, "receive", &c);
if (device->state.conn == C_WF_BITMAP_T) {
enum drbd_state_rv rv;
- err = drbd_send_bitmap(device);
+ err = drbd_send_bitmap(device, peer_device);
if (err)
goto out;
/* Omit CS_ORDERED with this state transition to avoid deadlocks. */
@@ -4925,7 +4753,7 @@ static int receive_out_of_sync(struct drbd_connection *connection, struct packet
drbd_conn_str(device->state.conn));
}
- drbd_set_out_of_sync(device, be64_to_cpu(p->sector), be32_to_cpu(p->blksize));
+ drbd_set_out_of_sync(peer_device, be64_to_cpu(p->sector), be32_to_cpu(p->blksize));
return 0;
}
@@ -4946,11 +4774,10 @@ static int receive_rs_deallocated(struct drbd_connection *connection, struct pac
sector = be64_to_cpu(p->sector);
size = be32_to_cpu(p->blksize);
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
if (get_ldev(device)) {
struct drbd_peer_request *peer_req;
- const enum req_op op = REQ_OP_WRITE_ZEROES;
peer_req = drbd_alloc_peer_req(peer_device, ID_SYNCER, sector,
size, 0, GFP_NOIO);
@@ -4960,6 +4787,7 @@ static int receive_rs_deallocated(struct drbd_connection *connection, struct pac
}
peer_req->w.cb = e_end_resync_block;
+ peer_req->opf = REQ_OP_DISCARD;
peer_req->submit_jif = jiffies;
peer_req->flags |= EE_TRIM;
@@ -4968,8 +4796,7 @@ static int receive_rs_deallocated(struct drbd_connection *connection, struct pac
spin_unlock_irq(&device->resource->req_lock);
atomic_add(pi->size >> 9, &device->rs_sect_ev);
- err = drbd_submit_peer_request(device, peer_req, op,
- DRBD_FAULT_RS_WR);
+ err = drbd_submit_peer_request(peer_req);
if (err) {
spin_lock_irq(&device->resource->req_lock);
@@ -5176,7 +5003,7 @@ static int drbd_disconnected(struct drbd_peer_device *peer_device)
atomic_set(&device->rs_pending_cnt, 0);
wake_up(&device->misc_wait);
- del_timer_sync(&device->resync_timer);
+ timer_delete_sync(&device->resync_timer);
resync_timer_fn(&device->resync_timer);
/* wait for all w_e_end_data_req, w_e_end_rsdata_req, w_send_barrier,
@@ -5205,20 +5032,10 @@ static int drbd_disconnected(struct drbd_peer_device *peer_device)
if (get_ldev(device)) {
drbd_bitmap_io(device, &drbd_bm_write_copy_pages,
- "write from disconnected", BM_LOCKED_CHANGE_ALLOWED);
+ "write from disconnected", BM_LOCKED_CHANGE_ALLOWED, NULL);
put_ldev(device);
}
- /* tcp_close and release of sendpage pages can be deferred. I don't
- * want to use SO_LINGER, because apparently it can be deferred for
- * more than 20 seconds (longest time I checked).
- *
- * Actually we don't care for exactly when the network stack does its
- * put_page(), but release our reference on these pages right here.
- */
- i = drbd_free_peer_reqs(device, &device->net_ee);
- if (i)
- drbd_info(device, "net_ee not empty, killed %u entries\n", i);
i = atomic_read(&device->pp_in_use_by_net);
if (i)
drbd_info(device, "pp_in_use_by_net = %d, expected 0\n", i);
@@ -5639,22 +5456,23 @@ static int got_IsInSync(struct drbd_connection *connection, struct packet_info *
if (get_ldev(device)) {
drbd_rs_complete_io(device, sector);
- drbd_set_in_sync(device, sector, blksize);
+ drbd_set_in_sync(peer_device, sector, blksize);
/* rs_same_csums is supposed to count in units of BM_BLOCK_SIZE */
device->rs_same_csum += (blksize >> BM_BLOCK_SHIFT);
put_ldev(device);
}
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
atomic_add(blksize >> 9, &device->rs_sect_in);
return 0;
}
static int
-validate_req_change_req_state(struct drbd_device *device, u64 id, sector_t sector,
+validate_req_change_req_state(struct drbd_peer_device *peer_device, u64 id, sector_t sector,
struct rb_root *root, const char *func,
enum drbd_req_event what, bool missing_ok)
{
+ struct drbd_device *device = peer_device->device;
struct drbd_request *req;
struct bio_and_error m;
@@ -5664,7 +5482,7 @@ validate_req_change_req_state(struct drbd_device *device, u64 id, sector_t secto
spin_unlock_irq(&device->resource->req_lock);
return -EIO;
}
- __req_mod(req, what, &m);
+ __req_mod(req, what, peer_device, &m);
spin_unlock_irq(&device->resource->req_lock);
if (m.bio)
@@ -5689,8 +5507,8 @@ static int got_BlockAck(struct drbd_connection *connection, struct packet_info *
update_peer_seq(peer_device, be32_to_cpu(p->seq_num));
if (p->block_id == ID_SYNCER) {
- drbd_set_in_sync(device, sector, blksize);
- dec_rs_pending(device);
+ drbd_set_in_sync(peer_device, sector, blksize);
+ dec_rs_pending(peer_device);
return 0;
}
switch (pi->cmd) {
@@ -5713,7 +5531,7 @@ static int got_BlockAck(struct drbd_connection *connection, struct packet_info *
BUG();
}
- return validate_req_change_req_state(device, p->block_id, sector,
+ return validate_req_change_req_state(peer_device, p->block_id, sector,
&device->write_requests, __func__,
what, false);
}
@@ -5735,12 +5553,12 @@ static int got_NegAck(struct drbd_connection *connection, struct packet_info *pi
update_peer_seq(peer_device, be32_to_cpu(p->seq_num));
if (p->block_id == ID_SYNCER) {
- dec_rs_pending(device);
- drbd_rs_failed_io(device, sector, size);
+ dec_rs_pending(peer_device);
+ drbd_rs_failed_io(peer_device, sector, size);
return 0;
}
- err = validate_req_change_req_state(device, p->block_id, sector,
+ err = validate_req_change_req_state(peer_device, p->block_id, sector,
&device->write_requests, __func__,
NEG_ACKED, true);
if (err) {
@@ -5749,7 +5567,7 @@ static int got_NegAck(struct drbd_connection *connection, struct packet_info *pi
request is no longer in the collision hash. */
/* In Protocol B we might already have got a P_RECV_ACK
but then get a P_NEG_ACK afterwards. */
- drbd_set_out_of_sync(device, sector, size);
+ drbd_set_out_of_sync(peer_device, sector, size);
}
return 0;
}
@@ -5771,7 +5589,7 @@ static int got_NegDReply(struct drbd_connection *connection, struct packet_info
drbd_err(device, "Got NegDReply; Sector %llus, len %u.\n",
(unsigned long long)sector, be32_to_cpu(p->blksize));
- return validate_req_change_req_state(device, p->block_id, sector,
+ return validate_req_change_req_state(peer_device, p->block_id, sector,
&device->read_requests, __func__,
NEG_ACKED, false);
}
@@ -5794,13 +5612,13 @@ static int got_NegRSDReply(struct drbd_connection *connection, struct packet_inf
update_peer_seq(peer_device, be32_to_cpu(p->seq_num));
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
if (get_ldev_if_state(device, D_FAILED)) {
drbd_rs_complete_io(device, sector);
switch (pi->cmd) {
case P_NEG_RS_DREPLY:
- drbd_rs_failed_io(device, sector, size);
+ drbd_rs_failed_io(peer_device, sector, size);
break;
case P_RS_CANCEL:
break;
@@ -5857,21 +5675,21 @@ static int got_OVResult(struct drbd_connection *connection, struct packet_info *
update_peer_seq(peer_device, be32_to_cpu(p->seq_num));
if (be64_to_cpu(p->block_id) == ID_OUT_OF_SYNC)
- drbd_ov_out_of_sync_found(device, sector, size);
+ drbd_ov_out_of_sync_found(peer_device, sector, size);
else
- ov_out_of_sync_print(device);
+ ov_out_of_sync_print(peer_device);
if (!get_ldev(device))
return 0;
drbd_rs_complete_io(device, sector);
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
--device->ov_left;
/* let's advance progress step marks only for every other megabyte */
if ((device->ov_left & 0x200) == 0x200)
- drbd_advance_rs_marks(device, device->ov_left);
+ drbd_advance_rs_marks(peer_device, device->ov_left);
if (device->ov_left == 0) {
dw = kmalloc(sizeof(*dw), GFP_NOIO);
@@ -5881,8 +5699,8 @@ static int got_OVResult(struct drbd_connection *connection, struct packet_info *
drbd_queue_work(&peer_device->connection->sender_work, &dw->w);
} else {
drbd_err(device, "kmalloc(dw) failed.");
- ov_out_of_sync_print(device);
- drbd_resync_finished(device);
+ ov_out_of_sync_print(peer_device);
+ drbd_resync_finished(peer_device);
}
}
put_ldev(device);
@@ -5964,8 +5782,6 @@ int drbd_ack_receiver(struct drbd_thread *thi)
while (get_t_state(thi) == RUNNING) {
drbd_thread_current_set_cpu(thi);
- conn_reclaim_net_peer_reqs(connection);
-
if (test_and_clear_bit(SEND_PING, &connection->flags)) {
if (drbd_send_ping(connection)) {
drbd_err(connection, "drbd_send_ping has failed\n");
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 8f7f144e54f3..d15826f6ee81 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_req.c
@@ -30,11 +30,6 @@ static struct drbd_request *drbd_req_new(struct drbd_device *device, struct bio
return NULL;
memset(req, 0, sizeof(*req));
- req->private_bio = bio_alloc_clone(device->ldev->backing_bdev, bio_src,
- GFP_NOIO, &drbd_io_bio_set);
- req->private_bio->bi_private = req;
- req->private_bio->bi_end_io = drbd_request_endio;
-
req->rq_state = (bio_data_dir(bio_src) == WRITE ? RQ_WRITE : 0)
| (bio_op(bio_src) == REQ_OP_WRITE_ZEROES ? RQ_ZEROES : 0)
| (bio_op(bio_src) == REQ_OP_DISCARD ? RQ_UNMAP : 0);
@@ -127,12 +122,13 @@ void drbd_req_destroy(struct kref *kref)
* before it even was submitted or sent.
* In that case we do not want to touch the bitmap at all.
*/
+ struct drbd_peer_device *peer_device = first_peer_device(device);
if ((s & (RQ_POSTPONED|RQ_LOCAL_MASK|RQ_NET_MASK)) != RQ_POSTPONED) {
if (!(s & RQ_NET_OK) || !(s & RQ_LOCAL_OK))
- drbd_set_out_of_sync(device, req->i.sector, req->i.size);
+ drbd_set_out_of_sync(peer_device, req->i.sector, req->i.size);
if ((s & RQ_NET_OK) && (s & RQ_LOCAL_OK) && (s & RQ_NET_SIS))
- drbd_set_in_sync(device, req->i.sector, req->i.size);
+ drbd_set_in_sync(peer_device, req->i.sector, req->i.size);
}
/* one might be tempted to move the drbd_al_complete_io
@@ -149,7 +145,7 @@ void drbd_req_destroy(struct kref *kref)
if (get_ldev_if_state(device, D_FAILED)) {
drbd_al_complete_io(device, &req->i);
put_ldev(device);
- } else if (__ratelimit(&drbd_ratelimit_state)) {
+ } else if (drbd_ratelimit()) {
drbd_warn(device, "Should have called drbd_al_complete_io(, %llu, %u), "
"but my Disk seems to have failed :(\n",
(unsigned long long) req->i.sector, req->i.size);
@@ -523,7 +519,7 @@ static void mod_rq_state(struct drbd_request *req, struct bio_and_error *m,
static void drbd_report_io_error(struct drbd_device *device, struct drbd_request *req)
{
- if (!__ratelimit(&drbd_ratelimit_state))
+ if (!drbd_ratelimit())
return;
drbd_warn(device, "local %s IO error sector %llu+%u on %pg\n",
@@ -557,12 +553,15 @@ static inline bool is_pending_write_protocol_A(struct drbd_request *req)
* happen "atomically" within the req_lock,
* and it enforces that we have to think in a very structured manner
* about the "events" that may happen to a request during its life time ...
+ *
+ *
+ * peer_device == NULL means local disk
*/
int __req_mod(struct drbd_request *req, enum drbd_req_event what,
+ struct drbd_peer_device *peer_device,
struct bio_and_error *m)
{
struct drbd_device *const device = req->device;
- struct drbd_peer_device *const peer_device = first_peer_device(device);
struct drbd_connection *const connection = peer_device ? peer_device->connection : NULL;
struct net_conf *nc;
int p, rv = 0;
@@ -622,7 +621,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
break;
case READ_COMPLETED_WITH_ERROR:
- drbd_set_out_of_sync(device, req->i.sector, req->i.size);
+ drbd_set_out_of_sync(peer_device, req->i.sector, req->i.size);
drbd_report_io_error(device, req);
__drbd_chk_io_error(device, DRBD_READ_ERROR);
fallthrough;
@@ -1105,6 +1104,7 @@ static bool drbd_should_send_out_of_sync(union drbd_dev_state s)
static int drbd_process_write_request(struct drbd_request *req)
{
struct drbd_device *device = req->device;
+ struct drbd_peer_device *peer_device = first_peer_device(device);
int remote, send_oos;
remote = drbd_should_do_remote(device->state);
@@ -1120,7 +1120,7 @@ static int drbd_process_write_request(struct drbd_request *req)
/* The only size==0 bios we expect are empty flushes. */
D_ASSERT(device, req->master_bio->bi_opf & REQ_PREFLUSH);
if (remote)
- _req_mod(req, QUEUE_AS_DRBD_BARRIER);
+ _req_mod(req, QUEUE_AS_DRBD_BARRIER, peer_device);
return remote;
}
@@ -1130,10 +1130,10 @@ static int drbd_process_write_request(struct drbd_request *req)
D_ASSERT(device, !(remote && send_oos));
if (remote) {
- _req_mod(req, TO_BE_SENT);
- _req_mod(req, QUEUE_FOR_NET_WRITE);
- } else if (drbd_set_out_of_sync(device, req->i.sector, req->i.size))
- _req_mod(req, QUEUE_FOR_SEND_OOS);
+ _req_mod(req, TO_BE_SENT, peer_device);
+ _req_mod(req, QUEUE_FOR_NET_WRITE, peer_device);
+ } else if (drbd_set_out_of_sync(peer_device, req->i.sector, req->i.size))
+ _req_mod(req, QUEUE_FOR_SEND_OOS, peer_device);
return remote;
}
@@ -1219,9 +1219,12 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio)
/* Update disk stats */
req->start_jif = bio_start_io_acct(req->master_bio);
- if (!get_ldev(device)) {
- bio_put(req->private_bio);
- req->private_bio = NULL;
+ if (get_ldev(device)) {
+ req->private_bio = bio_alloc_clone(device->ldev->backing_bdev,
+ bio, GFP_NOIO,
+ &drbd_io_bio_set);
+ req->private_bio->bi_private = req;
+ req->private_bio->bi_end_io = drbd_request_endio;
}
/* process discards always from our submitter thread */
@@ -1314,6 +1317,7 @@ static void drbd_update_plug(struct drbd_plug_cb *plug, struct drbd_request *req
static void drbd_send_and_submit(struct drbd_device *device, struct drbd_request *req)
{
struct drbd_resource *resource = device->resource;
+ struct drbd_peer_device *peer_device = first_peer_device(device);
const int rw = bio_data_dir(req->master_bio);
struct bio_and_error m = { NULL, };
bool no_remote = false;
@@ -1377,8 +1381,8 @@ static void drbd_send_and_submit(struct drbd_device *device, struct drbd_request
/* We either have a private_bio, or we can read from remote.
* Otherwise we had done the goto nodata above. */
if (req->private_bio == NULL) {
- _req_mod(req, TO_BE_SENT);
- _req_mod(req, QUEUE_FOR_NET_READ);
+ _req_mod(req, TO_BE_SENT, peer_device);
+ _req_mod(req, QUEUE_FOR_NET_READ, peer_device);
} else
no_remote = true;
}
@@ -1399,12 +1403,12 @@ static void drbd_send_and_submit(struct drbd_device *device, struct drbd_request
req->pre_submit_jif = jiffies;
list_add_tail(&req->req_pending_local,
&device->pending_completion[rw == WRITE]);
- _req_mod(req, TO_BE_SUBMITTED);
+ _req_mod(req, TO_BE_SUBMITTED, NULL);
/* but we need to give up the spinlock to submit */
submit_private_bio = true;
} else if (no_remote) {
nodata:
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "IO ERROR: neither local nor remote data, sector %llu+%u\n",
(unsigned long long)req->i.sector, req->i.size >> 9);
/* A write may have been queued for send_oos, however.
@@ -1609,6 +1613,8 @@ void drbd_submit_bio(struct bio *bio)
struct drbd_device *device = bio->bi_bdev->bd_disk->private_data;
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
/*
* what we "blindly" assume:
@@ -1693,7 +1699,8 @@ static bool net_timeout_reached(struct drbd_request *net_req,
void request_timer_fn(struct timer_list *t)
{
- struct drbd_device *device = from_timer(device, t, request_timer);
+ struct drbd_device *device = timer_container_of(device, t,
+ request_timer);
struct drbd_connection *connection = first_peer_device(device)->connection;
struct drbd_request *req_read, *req_write, *req_peer; /* oldest request */
struct net_conf *nc;
diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h
index 511f39a08de4..9ae860e7591b 100644
--- a/drivers/block/drbd/drbd_req.h
+++ b/drivers/block/drbd/drbd_req.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
drbd_req.h
@@ -266,9 +266,8 @@ struct bio_and_error {
extern void start_new_tl_epoch(struct drbd_connection *connection);
extern void drbd_req_destroy(struct kref *kref);
-extern void _req_may_be_done(struct drbd_request *req,
- struct bio_and_error *m);
extern int __req_mod(struct drbd_request *req, enum drbd_req_event what,
+ struct drbd_peer_device *peer_device,
struct bio_and_error *m);
extern void complete_master_bio(struct drbd_device *device,
struct bio_and_error *m);
@@ -282,14 +281,15 @@ extern void drbd_restart_request(struct drbd_request *req);
/* use this if you don't want to deal with calling complete_master_bio()
* outside the spinlock, e.g. when walking some list on cleanup. */
-static inline int _req_mod(struct drbd_request *req, enum drbd_req_event what)
+static inline int _req_mod(struct drbd_request *req, enum drbd_req_event what,
+ struct drbd_peer_device *peer_device)
{
struct drbd_device *device = req->device;
struct bio_and_error m;
int rv;
/* __req_mod possibly frees req, do not touch req after that! */
- rv = __req_mod(req, what, &m);
+ rv = __req_mod(req, what, peer_device, &m);
if (m.bio)
complete_master_bio(device, &m);
@@ -301,7 +301,8 @@ static inline int _req_mod(struct drbd_request *req, enum drbd_req_event what)
* of the lower level driver completion callback, so we need to
* spin_lock_irqsave here. */
static inline int req_mod(struct drbd_request *req,
- enum drbd_req_event what)
+ enum drbd_req_event what,
+ struct drbd_peer_device *peer_device)
{
unsigned long flags;
struct drbd_device *device = req->device;
@@ -309,7 +310,7 @@ static inline int req_mod(struct drbd_request *req,
int rv;
spin_lock_irqsave(&device->resource->req_lock, flags);
- rv = __req_mod(req, what, &m);
+ rv = __req_mod(req, what, peer_device, &m);
spin_unlock_irqrestore(&device->resource->req_lock, flags);
if (m.bio)
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
index 3f7bf9f2d874..c2b6c4d9729d 100644
--- a/drivers/block/drbd/drbd_state.c
+++ b/drivers/block/drbd/drbd_state.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_state.c
@@ -876,7 +876,7 @@ is_valid_state(struct drbd_device *device, union drbd_state ns)
ns.disk == D_OUTDATED)
rv = SS_CONNECTED_OUTDATES;
- else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
+ else if (nc && (ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
(nc->verify_alg[0] == 0))
rv = SS_NO_VERIFY_ALG;
@@ -1222,9 +1222,11 @@ void drbd_resume_al(struct drbd_device *device)
}
/* helper for _drbd_set_state */
-static void set_ov_position(struct drbd_device *device, enum drbd_conns cs)
+static void set_ov_position(struct drbd_peer_device *peer_device, enum drbd_conns cs)
{
- if (first_peer_device(device)->connection->agreed_pro_version < 90)
+ struct drbd_device *device = peer_device->device;
+
+ if (peer_device->connection->agreed_pro_version < 90)
device->ov_start_sector = 0;
device->rs_total = drbd_bm_bits(device);
device->ov_position = 0;
@@ -1387,7 +1389,7 @@ _drbd_set_state(struct drbd_device *device, union drbd_state ns,
unsigned long now = jiffies;
int i;
- set_ov_position(device, ns.conn);
+ set_ov_position(peer_device, ns.conn);
device->rs_start = now;
device->rs_last_sect_ev = 0;
device->ov_last_oos_size = 0;
@@ -1398,7 +1400,7 @@ _drbd_set_state(struct drbd_device *device, union drbd_state ns,
device->rs_mark_time[i] = now;
}
- drbd_rs_controller_reset(device);
+ drbd_rs_controller_reset(peer_device);
if (ns.conn == C_VERIFY_S) {
drbd_info(device, "Starting Online Verify from sector %llu\n",
@@ -1518,8 +1520,9 @@ static void abw_start_sync(struct drbd_device *device, int rv)
}
int drbd_bitmap_io_from_worker(struct drbd_device *device,
- int (*io_fn)(struct drbd_device *),
- char *why, enum bm_flag flags)
+ int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
+ char *why, enum bm_flag flags,
+ struct drbd_peer_device *peer_device)
{
int rv;
@@ -1529,7 +1532,7 @@ int drbd_bitmap_io_from_worker(struct drbd_device *device,
atomic_inc(&device->suspend_cnt);
drbd_bm_lock(device, why, flags);
- rv = io_fn(device);
+ rv = io_fn(device, peer_device);
drbd_bm_unlock(device);
drbd_resume_io(device);
@@ -1539,9 +1542,10 @@ int drbd_bitmap_io_from_worker(struct drbd_device *device,
int notify_resource_state_change(struct sk_buff *skb,
unsigned int seq,
- struct drbd_resource_state_change *resource_state_change,
+ void *state_change,
enum drbd_notification_type type)
{
+ struct drbd_resource_state_change *resource_state_change = state_change;
struct drbd_resource *resource = resource_state_change->resource;
struct resource_info resource_info = {
.res_role = resource_state_change->role[NEW],
@@ -1555,13 +1559,14 @@ int notify_resource_state_change(struct sk_buff *skb,
int notify_connection_state_change(struct sk_buff *skb,
unsigned int seq,
- struct drbd_connection_state_change *connection_state_change,
+ void *state_change,
enum drbd_notification_type type)
{
- struct drbd_connection *connection = connection_state_change->connection;
+ struct drbd_connection_state_change *p = state_change;
+ struct drbd_connection *connection = p->connection;
struct connection_info connection_info = {
- .conn_connection_state = connection_state_change->cstate[NEW],
- .conn_role = connection_state_change->peer_role[NEW],
+ .conn_connection_state = p->cstate[NEW],
+ .conn_role = p->peer_role[NEW],
};
return notify_connection_state(skb, seq, connection, &connection_info, type);
@@ -1569,9 +1574,10 @@ int notify_connection_state_change(struct sk_buff *skb,
int notify_device_state_change(struct sk_buff *skb,
unsigned int seq,
- struct drbd_device_state_change *device_state_change,
+ void *state_change,
enum drbd_notification_type type)
{
+ struct drbd_device_state_change *device_state_change = state_change;
struct drbd_device *device = device_state_change->device;
struct device_info device_info = {
.dev_disk_state = device_state_change->disk_state[NEW],
@@ -1582,9 +1588,10 @@ int notify_device_state_change(struct sk_buff *skb,
int notify_peer_device_state_change(struct sk_buff *skb,
unsigned int seq,
- struct drbd_peer_device_state_change *p,
+ void *state_change,
enum drbd_notification_type type)
{
+ struct drbd_peer_device_state_change *p = state_change;
struct drbd_peer_device *peer_device = p->peer_device;
struct peer_device_info peer_device_info = {
.peer_repl_state = p->repl_state[NEW],
@@ -1602,8 +1609,8 @@ static void broadcast_state_change(struct drbd_state_change *state_change)
struct drbd_resource_state_change *resource_state_change = &state_change->resource[0];
bool resource_state_has_changed;
unsigned int n_device, n_connection, n_peer_device, n_peer_devices;
- int (*last_func)(struct sk_buff *, unsigned int, void *,
- enum drbd_notification_type) = NULL;
+ int (*last_func)(struct sk_buff *, unsigned int,
+ void *, enum drbd_notification_type) = NULL;
void *last_arg = NULL;
#define HAS_CHANGED(state) ((state)[OLD] != (state)[NEW])
@@ -1613,7 +1620,7 @@ static void broadcast_state_change(struct drbd_state_change *state_change)
})
#define REMEMBER_STATE_CHANGE(func, arg, type) \
({ FINAL_STATE_CHANGE(type | NOTIFY_CONTINUES); \
- last_func = (typeof(last_func))func; \
+ last_func = func; \
last_arg = arg; \
})
@@ -1809,7 +1816,7 @@ static void after_state_ch(struct drbd_device *device, union drbd_state os,
device->state.conn == C_WF_BITMAP_S)
drbd_queue_bitmap_io(device, &drbd_send_bitmap, NULL,
"send_bitmap (WFBitMapS)",
- BM_LOCKED_TEST_ALLOWED);
+ BM_LOCKED_TEST_ALLOWED, peer_device);
/* Lost contact to peer's copy of the data */
if (lost_contact_to_peer_data(os.pdsk, ns.pdsk)) {
@@ -1839,7 +1846,7 @@ static void after_state_ch(struct drbd_device *device, union drbd_state os,
* No harm done if the bitmap still changes,
* redirtied pages will follow later. */
drbd_bitmap_io_from_worker(device, &drbd_bm_write,
- "demote diskless peer", BM_LOCKED_SET_ALLOWED);
+ "demote diskless peer", BM_LOCKED_SET_ALLOWED, peer_device);
put_ldev(device);
}
@@ -1851,7 +1858,7 @@ static void after_state_ch(struct drbd_device *device, union drbd_state os,
/* No changes to the bitmap expected this time, so assert that,
* even though no harm was done if it did change. */
drbd_bitmap_io_from_worker(device, &drbd_bm_write,
- "demote", BM_LOCKED_TEST_ALLOWED);
+ "demote", BM_LOCKED_TEST_ALLOWED, peer_device);
put_ldev(device);
}
@@ -1888,7 +1895,8 @@ static void after_state_ch(struct drbd_device *device, union drbd_state os,
/* no other bitmap changes expected during this phase */
drbd_queue_bitmap_io(device,
&drbd_bmio_set_n_write, &abw_start_sync,
- "set_n_write from StartingSync", BM_LOCKED_TEST_ALLOWED);
+ "set_n_write from StartingSync", BM_LOCKED_TEST_ALLOWED,
+ peer_device);
/* first half of local IO error, failure to attach,
* or administrative detach */
@@ -2011,7 +2019,8 @@ static void after_state_ch(struct drbd_device *device, union drbd_state os,
if ((os.conn > C_CONNECTED && os.conn < C_AHEAD) &&
(ns.conn == C_CONNECTED || ns.conn >= C_AHEAD) && get_ldev(device)) {
drbd_queue_bitmap_io(device, &drbd_bm_write_copy_pages, NULL,
- "write from resync_finished", BM_LOCKED_CHANGE_ALLOWED);
+ "write from resync_finished", BM_LOCKED_CHANGE_ALLOWED,
+ peer_device);
put_ldev(device);
}
@@ -2071,7 +2080,7 @@ static int w_after_conn_state_ch(struct drbd_work *w, int unused)
conn_free_crypto(connection);
mutex_unlock(&connection->resource->conf_update);
- kvfree_rcu(old_conf);
+ kvfree_rcu_mightsleep(old_conf);
}
if (ns_max.susp_fen) {
diff --git a/drivers/block/drbd/drbd_state.h b/drivers/block/drbd/drbd_state.h
index f87371e55e68..cbaeb8018dbf 100644
--- a/drivers/block/drbd/drbd_state.h
+++ b/drivers/block/drbd/drbd_state.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef DRBD_STATE_H
#define DRBD_STATE_H
diff --git a/drivers/block/drbd/drbd_state_change.h b/drivers/block/drbd/drbd_state_change.h
index d5b0479bc9a6..a56a57d67686 100644
--- a/drivers/block/drbd/drbd_state_change.h
+++ b/drivers/block/drbd/drbd_state_change.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef DRBD_STATE_CHANGE_H
#define DRBD_STATE_CHANGE_H
@@ -46,19 +46,19 @@ extern void forget_state_change(struct drbd_state_change *);
extern int notify_resource_state_change(struct sk_buff *,
unsigned int,
- struct drbd_resource_state_change *,
+ void *,
enum drbd_notification_type type);
extern int notify_connection_state_change(struct sk_buff *,
unsigned int,
- struct drbd_connection_state_change *,
+ void *,
enum drbd_notification_type type);
extern int notify_device_state_change(struct sk_buff *,
unsigned int,
- struct drbd_device_state_change *,
+ void *,
enum drbd_notification_type type);
extern int notify_peer_device_state_change(struct sk_buff *,
unsigned int,
- struct drbd_peer_device_state_change *,
+ void *,
enum drbd_notification_type type);
#endif /* DRBD_STATE_CHANGE_H */
diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c
index fc01307607ea..0a06f744b096 100644
--- a/drivers/block/drbd/drbd_strings.c
+++ b/drivers/block/drbd/drbd_strings.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd.h
diff --git a/drivers/block/drbd/drbd_strings.h b/drivers/block/drbd/drbd_strings.h
index 87b94a27358a..0201f6590f6a 100644
--- a/drivers/block/drbd/drbd_strings.h
+++ b/drivers/block/drbd/drbd_strings.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __DRBD_STRINGS_H
#define __DRBD_STRINGS_H
diff --git a/drivers/block/drbd/drbd_vli.h b/drivers/block/drbd/drbd_vli.h
index 01e3babc5277..941c511cc4da 100644
--- a/drivers/block/drbd/drbd_vli.h
+++ b/drivers/block/drbd/drbd_vli.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
-*- linux-c -*-
drbd_receiver.c
@@ -327,7 +327,7 @@ static inline int bitstream_get_bits(struct bitstream *bs, u64 *out, int bits)
*/
static inline int vli_encode_bits(struct bitstream *bs, u64 in)
{
- u64 code = code;
+ u64 code;
int bits = __vli_encode_bits(&code, in);
if (bits <= 0)
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 0bb1a900c2d5..dea3e79d044f 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-License-Identifier: GPL-2.0-only
/*
drbd_worker.c
@@ -28,8 +28,8 @@
#include "drbd_protocol.h"
#include "drbd_req.h"
-static int make_ov_request(struct drbd_device *, int);
-static int make_resync_request(struct drbd_device *, int);
+static int make_ov_request(struct drbd_peer_device *, int);
+static int make_resync_request(struct drbd_peer_device *, int);
/* endio handlers:
* drbd_md_endio (defined here)
@@ -124,7 +124,7 @@ void drbd_endio_write_sec_final(struct drbd_peer_request *peer_req) __releases(l
* In case of a write error, send the neg ack anyways. */
if (!__test_and_set_bit(__EE_SEND_WRITE_ACK, &peer_req->flags))
inc_unacked(device);
- drbd_set_out_of_sync(device, peer_req->i.sector, peer_req->i.size);
+ drbd_set_out_of_sync(peer_device, peer_req->i.sector, peer_req->i.size);
}
spin_lock_irqsave(&device->resource->req_lock, flags);
@@ -176,7 +176,7 @@ void drbd_peer_request_endio(struct bio *bio)
bool is_discard = bio_op(bio) == REQ_OP_WRITE_ZEROES ||
bio_op(bio) == REQ_OP_DISCARD;
- if (bio->bi_status && __ratelimit(&drbd_ratelimit_state))
+ if (bio->bi_status && drbd_ratelimit())
drbd_warn(device, "%s: error=%d s=%llus\n",
is_write ? (is_discard ? "discard" : "write")
: "read", bio->bi_status,
@@ -240,7 +240,7 @@ void drbd_request_endio(struct bio *bio)
* though we still will complain noisily about it.
*/
if (unlikely(req->rq_state & RQ_LOCAL_ABORTED)) {
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_emerg(device, "delayed completion of aborted local request; disk-timeout may be too aggressive\n");
if (!bio->bi_status)
@@ -276,7 +276,7 @@ void drbd_request_endio(struct bio *bio)
/* not req_mod(), we need irqsave here! */
spin_lock_irqsave(&device->resource->req_lock, flags);
- __req_mod(req, what, &m);
+ __req_mod(req, what, NULL, &m);
spin_unlock_irqrestore(&device->resource->req_lock, flags);
put_ldev(device);
@@ -363,7 +363,7 @@ static int w_e_send_csum(struct drbd_work *w, int cancel)
* drbd_alloc_pages due to pp_in_use > max_buffers. */
drbd_free_peer_req(device, peer_req);
peer_req = NULL;
- inc_rs_pending(device);
+ inc_rs_pending(peer_device);
err = drbd_send_drequest_csum(peer_device, sector, size,
digest, digest_size,
P_CSUM_RS_REQUEST);
@@ -400,13 +400,13 @@ static int read_for_csum(struct drbd_peer_device *peer_device, sector_t sector,
goto defer;
peer_req->w.cb = w_e_send_csum;
+ peer_req->opf = REQ_OP_READ;
spin_lock_irq(&device->resource->req_lock);
list_add_tail(&peer_req->w.list, &device->read_ee);
spin_unlock_irq(&device->resource->req_lock);
atomic_add(size >> 9, &device->rs_sect_ev);
- if (drbd_submit_peer_request(device, peer_req, REQ_OP_READ,
- DRBD_FAULT_RS_RD) == 0)
+ if (drbd_submit_peer_request(peer_req) == 0)
return 0;
/* If it failed because of ENOMEM, retry should help. If it failed
@@ -430,10 +430,10 @@ int w_resync_timer(struct drbd_work *w, int cancel)
switch (device->state.conn) {
case C_VERIFY_S:
- make_ov_request(device, cancel);
+ make_ov_request(first_peer_device(device), cancel);
break;
case C_SYNC_TARGET:
- make_resync_request(device, cancel);
+ make_resync_request(first_peer_device(device), cancel);
break;
}
@@ -442,7 +442,8 @@ int w_resync_timer(struct drbd_work *w, int cancel)
void resync_timer_fn(struct timer_list *t)
{
- struct drbd_device *device = from_timer(device, t, resync_timer);
+ struct drbd_device *device = timer_container_of(device, t,
+ resync_timer);
drbd_queue_work_if_unqueued(
&first_peer_device(device)->connection->sender_work,
@@ -493,8 +494,9 @@ struct fifo_buffer *fifo_alloc(unsigned int fifo_size)
return fb;
}
-static int drbd_rs_controller(struct drbd_device *device, unsigned int sect_in)
+static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int sect_in)
{
+ struct drbd_device *device = peer_device->device;
struct disk_conf *dc;
unsigned int want; /* The number of sectors we want in-flight */
int req_sect; /* Number of sectors to request in this turn */
@@ -545,8 +547,9 @@ static int drbd_rs_controller(struct drbd_device *device, unsigned int sect_in)
return req_sect;
}
-static int drbd_rs_number_requests(struct drbd_device *device)
+static int drbd_rs_number_requests(struct drbd_peer_device *peer_device)
{
+ struct drbd_device *device = peer_device->device;
unsigned int sect_in; /* Number of sectors that came in since the last turn */
int number, mxb;
@@ -556,7 +559,7 @@ static int drbd_rs_number_requests(struct drbd_device *device)
rcu_read_lock();
mxb = drbd_get_max_buffers(device) / 2;
if (rcu_dereference(device->rs_plan_s)->size) {
- number = drbd_rs_controller(device, sect_in) >> (BM_BLOCK_SHIFT - 9);
+ number = drbd_rs_controller(peer_device, sect_in) >> (BM_BLOCK_SHIFT - 9);
device->c_sync_rate = number * HZ * (BM_BLOCK_SIZE / 1024) / SLEEP_TIME;
} else {
device->c_sync_rate = rcu_dereference(device->ldev->disk_conf)->resync_rate;
@@ -580,9 +583,9 @@ static int drbd_rs_number_requests(struct drbd_device *device)
return number;
}
-static int make_resync_request(struct drbd_device *const device, int cancel)
+static int make_resync_request(struct drbd_peer_device *const peer_device, int cancel)
{
- struct drbd_peer_device *const peer_device = first_peer_device(device);
+ struct drbd_device *const device = peer_device->device;
struct drbd_connection *const connection = peer_device ? peer_device->connection : NULL;
unsigned long bit;
sector_t sector;
@@ -598,7 +601,7 @@ static int make_resync_request(struct drbd_device *const device, int cancel)
if (device->rs_total == 0) {
/* empty resync? */
- drbd_resync_finished(device);
+ drbd_resync_finished(peer_device);
return 0;
}
@@ -618,7 +621,7 @@ static int make_resync_request(struct drbd_device *const device, int cancel)
}
max_bio_size = queue_max_hw_sectors(device->rq_queue) << 9;
- number = drbd_rs_number_requests(device);
+ number = drbd_rs_number_requests(peer_device);
if (number <= 0)
goto requeue;
@@ -653,7 +656,7 @@ next_sector:
sector = BM_BIT_TO_SECT(bit);
- if (drbd_try_rs_begin_io(device, sector)) {
+ if (drbd_try_rs_begin_io(peer_device, sector)) {
device->bm_resync_fo = bit;
goto requeue;
}
@@ -729,13 +732,13 @@ next_sector:
} else {
int err;
- inc_rs_pending(device);
+ inc_rs_pending(peer_device);
err = drbd_send_drequest(peer_device,
size == discard_granularity ? P_RS_THIN_REQ : P_RS_DATA_REQUEST,
sector, size, ID_SYNCER);
if (err) {
drbd_err(device, "drbd_send_drequest() failed, aborting...\n");
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
put_ldev(device);
return err;
}
@@ -760,8 +763,9 @@ next_sector:
return 0;
}
-static int make_ov_request(struct drbd_device *device, int cancel)
+static int make_ov_request(struct drbd_peer_device *peer_device, int cancel)
{
+ struct drbd_device *device = peer_device->device;
int number, i, size;
sector_t sector;
const sector_t capacity = get_capacity(device->vdisk);
@@ -770,7 +774,7 @@ static int make_ov_request(struct drbd_device *device, int cancel)
if (unlikely(cancel))
return 1;
- number = drbd_rs_number_requests(device);
+ number = drbd_rs_number_requests(peer_device);
sector = device->ov_position;
for (i = 0; i < number; i++) {
@@ -788,7 +792,7 @@ static int make_ov_request(struct drbd_device *device, int cancel)
size = BM_BLOCK_SIZE;
- if (drbd_try_rs_begin_io(device, sector)) {
+ if (drbd_try_rs_begin_io(peer_device, sector)) {
device->ov_position = sector;
goto requeue;
}
@@ -796,9 +800,9 @@ static int make_ov_request(struct drbd_device *device, int cancel)
if (sector + (size>>9) > capacity)
size = (capacity-sector)<<9;
- inc_rs_pending(device);
+ inc_rs_pending(peer_device);
if (drbd_send_ov_request(first_peer_device(device), sector, size)) {
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
return 0;
}
sector += BM_SECT_PER_BIT;
@@ -818,8 +822,8 @@ int w_ov_finished(struct drbd_work *w, int cancel)
container_of(w, struct drbd_device_work, w);
struct drbd_device *device = dw->device;
kfree(dw);
- ov_out_of_sync_print(device);
- drbd_resync_finished(device);
+ ov_out_of_sync_print(first_peer_device(device));
+ drbd_resync_finished(first_peer_device(device));
return 0;
}
@@ -831,7 +835,7 @@ static int w_resync_finished(struct drbd_work *w, int cancel)
struct drbd_device *device = dw->device;
kfree(dw);
- drbd_resync_finished(device);
+ drbd_resync_finished(first_peer_device(device));
return 0;
}
@@ -846,9 +850,10 @@ static void ping_peer(struct drbd_device *device)
test_bit(GOT_PING_ACK, &connection->flags) || device->state.conn < C_CONNECTED);
}
-int drbd_resync_finished(struct drbd_device *device)
+int drbd_resync_finished(struct drbd_peer_device *peer_device)
{
- struct drbd_connection *connection = first_peer_device(device)->connection;
+ struct drbd_device *device = peer_device->device;
+ struct drbd_connection *connection = peer_device->connection;
unsigned long db, dt, dbdt;
unsigned long n_oos;
union drbd_state os, ns;
@@ -1025,22 +1030,6 @@ out:
return 1;
}
-/* helper */
-static void move_to_net_ee_or_free(struct drbd_device *device, struct drbd_peer_request *peer_req)
-{
- if (drbd_peer_req_has_active_page(peer_req)) {
- /* This might happen if sendpage() has not finished */
- int i = PFN_UP(peer_req->i.size);
- atomic_add(i, &device->pp_in_use_by_net);
- atomic_sub(i, &device->pp_in_use);
- spin_lock_irq(&device->resource->req_lock);
- list_add_tail(&peer_req->w.list, &device->net_ee);
- spin_unlock_irq(&device->resource->req_lock);
- wake_up(&drbd_pp_wait);
- } else
- drbd_free_peer_req(device, peer_req);
-}
-
/**
* w_e_end_data_req() - Worker callback, to send a P_DATA_REPLY packet in response to a P_DATA_REQUEST
* @w: work object.
@@ -1054,27 +1043,26 @@ int w_e_end_data_req(struct drbd_work *w, int cancel)
int err;
if (unlikely(cancel)) {
- drbd_free_peer_req(device, peer_req);
- dec_unacked(device);
- return 0;
+ err = 0;
+ goto out;
}
if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
err = drbd_send_block(peer_device, P_DATA_REPLY, peer_req);
} else {
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "Sending NegDReply. sector=%llus.\n",
(unsigned long long)peer_req->i.sector);
err = drbd_send_ack(peer_device, P_NEG_DREPLY, peer_req);
}
- dec_unacked(device);
-
- move_to_net_ee_or_free(device, peer_req);
-
if (unlikely(err))
drbd_err(device, "drbd_send_block() failed\n");
+out:
+ dec_unacked(device);
+ drbd_free_peer_req(device, peer_req);
+
return err;
}
@@ -1115,9 +1103,8 @@ int w_e_end_rsdata_req(struct drbd_work *w, int cancel)
int err;
if (unlikely(cancel)) {
- drbd_free_peer_req(device, peer_req);
- dec_unacked(device);
- return 0;
+ err = 0;
+ goto out;
}
if (get_ldev_if_state(device, D_FAILED)) {
@@ -1129,34 +1116,33 @@ int w_e_end_rsdata_req(struct drbd_work *w, int cancel)
err = drbd_send_ack(peer_device, P_RS_CANCEL, peer_req);
} else if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
if (likely(device->state.pdsk >= D_INCONSISTENT)) {
- inc_rs_pending(device);
+ inc_rs_pending(peer_device);
if (peer_req->flags & EE_RS_THIN_REQ && all_zero(peer_req))
err = drbd_send_rs_deallocated(peer_device, peer_req);
else
err = drbd_send_block(peer_device, P_RS_DATA_REPLY, peer_req);
} else {
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "Not sending RSDataReply, "
"partner DISKLESS!\n");
err = 0;
}
} else {
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "Sending NegRSDReply. sector %llus.\n",
(unsigned long long)peer_req->i.sector);
err = drbd_send_ack(peer_device, P_NEG_RS_DREPLY, peer_req);
/* update resync data with failure */
- drbd_rs_failed_io(device, peer_req->i.sector, peer_req->i.size);
+ drbd_rs_failed_io(peer_device, peer_req->i.sector, peer_req->i.size);
}
-
- dec_unacked(device);
-
- move_to_net_ee_or_free(device, peer_req);
-
if (unlikely(err))
drbd_err(device, "drbd_send_block() failed\n");
+out:
+ dec_unacked(device);
+ drbd_free_peer_req(device, peer_req);
+
return err;
}
@@ -1171,9 +1157,8 @@ int w_e_end_csum_rs_req(struct drbd_work *w, int cancel)
int err, eq = 0;
if (unlikely(cancel)) {
- drbd_free_peer_req(device, peer_req);
- dec_unacked(device);
- return 0;
+ err = 0;
+ goto out;
}
if (get_ldev(device)) {
@@ -1199,12 +1184,12 @@ int w_e_end_csum_rs_req(struct drbd_work *w, int cancel)
}
if (eq) {
- drbd_set_in_sync(device, peer_req->i.sector, peer_req->i.size);
+ drbd_set_in_sync(peer_device, peer_req->i.sector, peer_req->i.size);
/* rs_same_csums unit is BM_BLOCK_SIZE */
device->rs_same_csum += peer_req->i.size >> BM_BLOCK_SHIFT;
err = drbd_send_ack(peer_device, P_RS_IS_IN_SYNC, peer_req);
} else {
- inc_rs_pending(device);
+ inc_rs_pending(peer_device);
peer_req->block_id = ID_SYNCER; /* By setting block_id, digest pointer becomes invalid! */
peer_req->flags &= ~EE_HAS_DIGEST; /* This peer request no longer has a digest pointer */
kfree(di);
@@ -1212,15 +1197,15 @@ int w_e_end_csum_rs_req(struct drbd_work *w, int cancel)
}
} else {
err = drbd_send_ack(peer_device, P_NEG_RS_DREPLY, peer_req);
- if (__ratelimit(&drbd_ratelimit_state))
+ if (drbd_ratelimit())
drbd_err(device, "Sending NegDReply. I guess it gets messy.\n");
}
-
- dec_unacked(device);
- move_to_net_ee_or_free(device, peer_req);
-
if (unlikely(err))
drbd_err(device, "drbd_send_block/ack() failed\n");
+out:
+ dec_unacked(device);
+ drbd_free_peer_req(device, peer_req);
+
return err;
}
@@ -1257,10 +1242,10 @@ int w_e_end_ov_req(struct drbd_work *w, int cancel)
* drbd_alloc_pages due to pp_in_use > max_buffers. */
drbd_free_peer_req(device, peer_req);
peer_req = NULL;
- inc_rs_pending(device);
+ inc_rs_pending(peer_device);
err = drbd_send_drequest_csum(peer_device, sector, size, digest, digest_size, P_OV_REPLY);
if (err)
- dec_rs_pending(device);
+ dec_rs_pending(peer_device);
kfree(digest);
out:
@@ -1270,15 +1255,16 @@ out:
return err;
}
-void drbd_ov_out_of_sync_found(struct drbd_device *device, sector_t sector, int size)
+void drbd_ov_out_of_sync_found(struct drbd_peer_device *peer_device, sector_t sector, int size)
{
+ struct drbd_device *device = peer_device->device;
if (device->ov_last_oos_start + device->ov_last_oos_size == sector) {
device->ov_last_oos_size += size>>9;
} else {
device->ov_last_oos_start = sector;
device->ov_last_oos_size = size>>9;
}
- drbd_set_out_of_sync(device, sector, size);
+ drbd_set_out_of_sync(peer_device, sector, size);
}
int w_e_end_ov_reply(struct drbd_work *w, int cancel)
@@ -1328,9 +1314,9 @@ int w_e_end_ov_reply(struct drbd_work *w, int cancel)
* drbd_alloc_pages due to pp_in_use > max_buffers. */
drbd_free_peer_req(device, peer_req);
if (!eq)
- drbd_ov_out_of_sync_found(device, sector, size);
+ drbd_ov_out_of_sync_found(peer_device, sector, size);
else
- ov_out_of_sync_print(device);
+ ov_out_of_sync_print(peer_device);
err = drbd_send_ack_ex(peer_device, P_OV_RESULT, sector, size,
eq ? ID_IN_SYNC : ID_OUT_OF_SYNC);
@@ -1341,14 +1327,14 @@ int w_e_end_ov_reply(struct drbd_work *w, int cancel)
/* let's advance progress step marks only for every other megabyte */
if ((device->ov_left & 0x200) == 0x200)
- drbd_advance_rs_marks(device, device->ov_left);
+ drbd_advance_rs_marks(peer_device, device->ov_left);
stop_sector_reached = verify_can_do_stop_sector(device) &&
(sector + (size>>9)) >= device->ov_stop_sector;
if (device->ov_left == 0 || stop_sector_reached) {
- ov_out_of_sync_print(device);
- drbd_resync_finished(device);
+ ov_out_of_sync_print(peer_device);
+ drbd_resync_finished(peer_device);
}
return err;
@@ -1425,7 +1411,7 @@ int w_send_out_of_sync(struct drbd_work *w, int cancel)
int err;
if (unlikely(cancel)) {
- req_mod(req, SEND_CANCELED);
+ req_mod(req, SEND_CANCELED, peer_device);
return 0;
}
req->pre_send_jif = jiffies;
@@ -1437,7 +1423,7 @@ int w_send_out_of_sync(struct drbd_work *w, int cancel)
maybe_send_barrier(connection, req->epoch);
err = drbd_send_out_of_sync(peer_device, req);
- req_mod(req, OOS_HANDED_TO_NETWORK);
+ req_mod(req, OOS_HANDED_TO_NETWORK, peer_device);
return err;
}
@@ -1457,7 +1443,7 @@ int w_send_dblock(struct drbd_work *w, int cancel)
int err;
if (unlikely(cancel)) {
- req_mod(req, SEND_CANCELED);
+ req_mod(req, SEND_CANCELED, peer_device);
return 0;
}
req->pre_send_jif = jiffies;
@@ -1467,7 +1453,7 @@ int w_send_dblock(struct drbd_work *w, int cancel)
connection->send.current_epoch_writes++;
err = drbd_send_dblock(peer_device, req);
- req_mod(req, err ? SEND_FAILED : HANDED_OVER_TO_NETWORK);
+ req_mod(req, err ? SEND_FAILED : HANDED_OVER_TO_NETWORK, peer_device);
if (do_send_unplug && !err)
pd_send_unplug_remote(peer_device);
@@ -1490,7 +1476,7 @@ int w_send_read_req(struct drbd_work *w, int cancel)
int err;
if (unlikely(cancel)) {
- req_mod(req, SEND_CANCELED);
+ req_mod(req, SEND_CANCELED, peer_device);
return 0;
}
req->pre_send_jif = jiffies;
@@ -1502,7 +1488,7 @@ int w_send_read_req(struct drbd_work *w, int cancel)
err = drbd_send_drequest(peer_device, P_DATA_REQUEST, req->i.sector, req->i.size,
(unsigned long)req);
- req_mod(req, err ? SEND_FAILED : HANDED_OVER_TO_NETWORK);
+ req_mod(req, err ? SEND_FAILED : HANDED_OVER_TO_NETWORK, peer_device);
if (do_send_unplug && !err)
pd_send_unplug_remote(peer_device);
@@ -1668,8 +1654,9 @@ void drbd_resync_after_changed(struct drbd_device *device)
} while (changed);
}
-void drbd_rs_controller_reset(struct drbd_device *device)
+void drbd_rs_controller_reset(struct drbd_peer_device *peer_device)
{
+ struct drbd_device *device = peer_device->device;
struct gendisk *disk = device->ldev->backing_bdev->bd_disk;
struct fifo_buffer *plan;
@@ -1692,7 +1679,8 @@ void drbd_rs_controller_reset(struct drbd_device *device)
void start_resync_timer_fn(struct timer_list *t)
{
- struct drbd_device *device = from_timer(device, t, start_resync_timer);
+ struct drbd_device *device = timer_container_of(device, t,
+ start_resync_timer);
drbd_device_post_work(device, RS_START);
}
@@ -1891,10 +1879,10 @@ void drbd_start_resync(struct drbd_device *device, enum drbd_conns side)
rcu_read_unlock();
schedule_timeout_interruptible(timeo);
}
- drbd_resync_finished(device);
+ drbd_resync_finished(peer_device);
}
- drbd_rs_controller_reset(device);
+ drbd_rs_controller_reset(peer_device);
/* ns.conn may already be != device->state.conn,
* we may have been paused in between, or become paused until
* the timer triggers.
@@ -1909,8 +1897,9 @@ out:
mutex_unlock(device->state_mutex);
}
-static void update_on_disk_bitmap(struct drbd_device *device, bool resync_done)
+static void update_on_disk_bitmap(struct drbd_peer_device *peer_device, bool resync_done)
{
+ struct drbd_device *device = peer_device->device;
struct sib_info sib = { .sib_reason = SIB_SYNC_PROGRESS, };
device->rs_last_bcast = jiffies;
@@ -1919,7 +1908,7 @@ static void update_on_disk_bitmap(struct drbd_device *device, bool resync_done)
drbd_bm_write_lazy(device, 0);
if (resync_done && is_sync_state(device->state.conn))
- drbd_resync_finished(device);
+ drbd_resync_finished(peer_device);
drbd_bcast_event(device, &sib);
/* update timestamp, in case it took a while to write out stuff */
@@ -1945,6 +1934,7 @@ static void drbd_ldev_destroy(struct drbd_device *device)
static void go_diskless(struct drbd_device *device)
{
+ struct drbd_peer_device *peer_device = first_peer_device(device);
D_ASSERT(device, device->state.disk == D_FAILED);
/* we cannot assert local_cnt == 0 here, as get_ldev_if_state will
* inc/dec it frequently. Once we are D_DISKLESS, no one will touch
@@ -1970,7 +1960,7 @@ static void go_diskless(struct drbd_device *device)
* Any modifications would not be expected anymore, though.
*/
if (drbd_bitmap_io_from_worker(device, drbd_bm_write,
- "detach", BM_LOCKED_TEST_ALLOWED)) {
+ "detach", BM_LOCKED_TEST_ALLOWED, peer_device)) {
if (test_bit(WAS_READ_ERROR, &device->flags)) {
drbd_md_set_flag(device, MDF_FULL_SYNC);
drbd_md_sync(device);
@@ -2017,7 +2007,7 @@ static void do_device_work(struct drbd_device *device, const unsigned long todo)
do_md_sync(device);
if (test_bit(RS_DONE, &todo) ||
test_bit(RS_PROGRESS, &todo))
- update_on_disk_bitmap(device, test_bit(RS_DONE, &todo));
+ update_on_disk_bitmap(first_peer_device(device), test_bit(RS_DONE, &todo));
if (test_bit(GO_DISKLESS, &todo))
go_diskless(device);
if (test_bit(DESTROY_DISK, &todo))
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index ccad3d7b3ddd..c28786e0fe1c 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -163,35 +163,35 @@
/* do print messages for unexpected interrupts */
static int print_unex = 1;
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/workqueue.h>
-#include <linux/fdreg.h>
-#include <linux/fd.h>
-#include <linux/hdreg.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
+#include <linux/async.h>
#include <linux/bio.h>
-#include <linux/string.h>
-#include <linux/jiffies.h>
-#include <linux/fcntl.h>
+#include <linux/compat.h>
#include <linux/delay.h>
-#include <linux/mc146818rtc.h> /* CMOS defines */
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/fd.h>
+#include <linux/fdreg.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
#include <linux/major.h>
-#include <linux/platform_device.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/mm.h>
#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
#include <linux/uaccess.h>
-#include <linux/async.h>
-#include <linux/compat.h>
+#include <linux/workqueue.h>
/*
* PS/2 floppies have much slower step rates than regular floppies.
@@ -233,8 +233,6 @@ static unsigned short virtual_dma_port = 0x3f0;
irqreturn_t floppy_interrupt(int irq, void *dev_id);
static int set_dor(int fdc, char mask, char data);
-#define K_64 0x10000 /* 64KB */
-
/* the following is the mask of allowed drives. By default units 2 and
* 3 of both floppy controllers are disabled, because switching on the
* motor of these drives causes system hangs on some PCI computers. drive
@@ -331,7 +329,7 @@ static bool initialized;
* This default is used whenever the current disk size is unknown.
* [Now it is rather a minimum]
*/
-#define MAX_DISK_SIZE 4 /* 3984 */
+#define MAX_DISK_SIZE (PAGE_SIZE / 1024)
/*
* globals used by 'result()'
@@ -402,7 +400,7 @@ static struct floppy_drive_struct drive_state[N_DRIVE];
static struct floppy_write_errors write_errors[N_DRIVE];
static struct timer_list motor_off_timer[N_DRIVE];
static struct blk_mq_tag_set tag_sets[N_DRIVE];
-static struct block_device *opened_bdev[N_DRIVE];
+static struct gendisk *opened_disk[N_DRIVE];
static DEFINE_MUTEX(open_lock);
static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
@@ -530,14 +528,13 @@ static struct format_descr format_req;
static char *floppy_track_buffer;
static int max_buffer_sectors;
-typedef void (*done_f)(int);
static const struct cont_t {
void (*interrupt)(void);
/* this is called after the interrupt of the
* main command */
void (*redo)(void); /* this is called to retry the operation */
void (*error)(void); /* this is called to tally an error */
- done_f done; /* this is called to say if the operation has
+ void (*done)(int); /* this is called to say if the operation has
* succeeded/failed */
} *cont;
@@ -938,7 +935,7 @@ static void floppy_off(unsigned int drive)
if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))))
return;
- del_timer(motor_off_timer + drive);
+ timer_delete(motor_off_timer + drive);
/* make spindle stop in a position which minimizes spinup time
* next time */
@@ -985,6 +982,10 @@ static void empty(void)
{
}
+static void empty_done(int result)
+{
+}
+
static void (*floppy_work_fn)(void);
static void floppy_work_workfn(struct work_struct *work)
@@ -1915,7 +1916,7 @@ static int start_motor(void (*function)(void))
mask &= ~(0x10 << UNIT(current_drive));
/* starts motor and selects floppy */
- del_timer(motor_off_timer + current_drive);
+ timer_delete(motor_off_timer + current_drive);
set_dor(current_fdc, mask, data);
/* wait_for_completion also schedules reset if needed. */
@@ -1998,14 +1999,14 @@ static const struct cont_t wakeup_cont = {
.interrupt = empty,
.redo = do_wakeup,
.error = empty,
- .done = (done_f)empty
+ .done = empty_done,
};
static const struct cont_t intr_cont = {
.interrupt = empty,
.redo = process_fd_request,
.error = empty,
- .done = (done_f)empty
+ .done = empty_done,
};
/* schedules handler, waiting for completion. May be interrupted, will then
@@ -2784,7 +2785,6 @@ do_request:
pending = set_next_request();
spin_unlock_irq(&floppy_lock);
if (!pending) {
- do_floppy = NULL;
unlock_fdc();
return;
}
@@ -3079,7 +3079,7 @@ static void raw_cmd_free(struct floppy_raw_cmd **ptr)
}
}
-#define MAX_LEN (1UL << MAX_ORDER << PAGE_SHIFT)
+#define MAX_LEN (1UL << MAX_PAGE_ORDER << PAGE_SHIFT)
static int raw_cmd_copyin(int cmd, void __user *param,
struct floppy_raw_cmd **rcmd)
@@ -3090,16 +3090,13 @@ static int raw_cmd_copyin(int cmd, void __user *param,
*rcmd = NULL;
loop:
- ptr = kmalloc(sizeof(struct floppy_raw_cmd), GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
+ ptr = memdup_user(param, sizeof(*ptr));
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
*rcmd = ptr;
- ret = copy_from_user(ptr, param, sizeof(*ptr));
ptr->next = NULL;
ptr->buffer_length = 0;
ptr->kernel_data = NULL;
- if (ret)
- return -EFAULT;
param += sizeof(struct floppy_raw_cmd);
if (ptr->cmd_count > FD_RAW_CMD_FULLSIZE)
return -EINVAL;
@@ -3108,7 +3105,7 @@ loop:
ptr->resultcode = 0;
if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
- if (ptr->length <= 0 || ptr->length >= MAX_LEN)
+ if (ptr->length <= 0 || ptr->length > MAX_LEN)
return -EINVAL;
ptr->kernel_data = (char *)fd_dma_mem_alloc(ptr->length);
fallback_on_nodma_alloc(&ptr->kernel_data, ptr->length);
@@ -3210,13 +3207,15 @@ static int floppy_raw_cmd_ioctl(int type, int drive, int cmd,
#endif
-static int invalidate_drive(struct block_device *bdev)
+static int invalidate_drive(struct gendisk *disk)
{
/* invalidate the buffer track to force a reread */
- set_bit((long)bdev->bd_disk->private_data, &fake_change);
+ set_bit((long)disk->private_data, &fake_change);
process_fd_request();
- if (bdev_check_media_change(bdev))
- floppy_revalidate(bdev->bd_disk);
+ if (disk_check_media_change(disk)) {
+ bdev_mark_dead(disk->part0, true);
+ floppy_revalidate(disk);
+ }
return 0;
}
@@ -3251,10 +3250,11 @@ static int set_geometry(unsigned int cmd, struct floppy_struct *g,
floppy_type[type].size + 1;
process_fd_request();
for (cnt = 0; cnt < N_DRIVE; cnt++) {
- struct block_device *bdev = opened_bdev[cnt];
- if (!bdev || ITYPE(drive_state[cnt].fd_device) != type)
+ struct gendisk *disk = opened_disk[cnt];
+
+ if (!disk || ITYPE(drive_state[cnt].fd_device) != type)
continue;
- __invalidate_device(bdev, true);
+ disk_force_media_change(disk);
}
mutex_unlock(&open_lock);
} else {
@@ -3287,7 +3287,7 @@ static int set_geometry(unsigned int cmd, struct floppy_struct *g,
drive_state[current_drive].maxtrack ||
((user_params[drive].sect ^ oldStretch) &
(FD_SWAPSIDES | FD_SECTBASEMASK)))
- invalidate_drive(bdev);
+ invalidate_drive(bdev->bd_disk);
else
process_fd_request();
}
@@ -3358,9 +3358,9 @@ static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
return 0;
}
-static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+static int fd_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- int drive = (long)bdev->bd_disk->private_data;
+ int drive = (long)disk->private_data;
int type = ITYPE(drive_state[drive].fd_device);
struct floppy_struct *g;
int ret;
@@ -3393,8 +3393,8 @@ static bool valid_floppy_drive_params(const short autodetect[FD_AUTODETECT_SIZE]
return true;
}
-static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
- unsigned long param)
+static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
+ unsigned int cmd, unsigned long param)
{
int drive = (long)bdev->bd_disk->private_data;
int type = ITYPE(drive_state[drive].fd_device);
@@ -3406,7 +3406,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
struct floppy_max_errors max_errors;
struct floppy_drive_params dp;
} inparam; /* parameters coming from user space */
- const void *outparam; /* parameters passed back to user space */
+ const void *outparam = NULL; /* parameters passed back to user space */
/* convert compatibility eject ioctls into floppy eject ioctl.
* We do this in order to provide a means to eject floppy disks before
@@ -3427,7 +3427,8 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
return ret;
/* permission checks */
- if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) ||
+ if (((cmd & 0x40) &&
+ !(mode & (BLK_OPEN_WRITE | BLK_OPEN_WRITE_IOCTL))) ||
((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
return -EPERM;
@@ -3464,7 +3465,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
current_type[drive] = NULL;
floppy_sizes[drive] = MAX_DISK_SIZE << 1;
drive_state[drive].keep_data = 0;
- return invalidate_drive(bdev);
+ return invalidate_drive(bdev->bd_disk);
case FDSETPRM:
case FDDEFPRM:
return set_geometry(cmd, &inparam.g, drive, type, bdev);
@@ -3503,7 +3504,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
case FDFLUSH:
if (lock_fdc(drive))
return -EINTR;
- return invalidate_drive(bdev);
+ return invalidate_drive(bdev->bd_disk);
case FDSETEMSGTRESH:
drive_params[drive].max_errors.reporting = (unsigned short)(param & 0x0f);
return 0;
@@ -3565,7 +3566,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
return 0;
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
int ret;
@@ -3653,8 +3654,8 @@ struct compat_floppy_write_errors {
#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state)
#define FDWERRORGET32 _IOR(2, 0x17, struct compat_floppy_write_errors)
-static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd,
- struct compat_floppy_struct __user *arg)
+static int compat_set_geometry(struct block_device *bdev, blk_mode_t mode,
+ unsigned int cmd, struct compat_floppy_struct __user *arg)
{
struct floppy_struct v;
int drive, type;
@@ -3663,7 +3664,7 @@ static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned
BUILD_BUG_ON(offsetof(struct floppy_struct, name) !=
offsetof(struct compat_floppy_struct, name));
- if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL)))
+ if (!(mode & (BLK_OPEN_WRITE | BLK_OPEN_WRITE_IOCTL)))
return -EPERM;
memset(&v, 0, sizeof(struct floppy_struct));
@@ -3860,8 +3861,8 @@ static int compat_werrorget(int drive,
return 0;
}
-static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
- unsigned long param)
+static int fd_compat_ioctl(struct block_device *bdev, blk_mode_t mode,
+ unsigned int cmd, unsigned long param)
{
int drive = (long)bdev->bd_disk->private_data;
switch (cmd) {
@@ -3962,7 +3963,7 @@ static void __init config_types(void)
pr_cont("\n");
}
-static void floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk)
{
int drive = (long)disk->private_data;
@@ -3973,7 +3974,7 @@ static void floppy_release(struct gendisk *disk, fmode_t mode)
drive_state[drive].fd_ref = 0;
}
if (!drive_state[drive].fd_ref)
- opened_bdev[drive] = NULL;
+ opened_disk[drive] = NULL;
mutex_unlock(&open_lock);
mutex_unlock(&floppy_mutex);
}
@@ -3983,9 +3984,9 @@ static void floppy_release(struct gendisk *disk, fmode_t mode)
* /dev/PS0 etc), and disallows simultaneous access to the same
* drive with different device numbers.
*/
-static int floppy_open(struct block_device *bdev, fmode_t mode)
+static int floppy_open(struct gendisk *disk, blk_mode_t mode)
{
- int drive = (long)bdev->bd_disk->private_data;
+ int drive = (long)disk->private_data;
int old_dev, new_dev;
int try;
int res = -EBUSY;
@@ -3994,7 +3995,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
mutex_lock(&floppy_mutex);
mutex_lock(&open_lock);
old_dev = drive_state[drive].fd_device;
- if (opened_bdev[drive] && opened_bdev[drive] != bdev)
+ if (opened_disk[drive] && opened_disk[drive] != disk)
goto out2;
if (!drive_state[drive].fd_ref && (drive_params[drive].flags & FD_BROKEN_DCL)) {
@@ -4004,7 +4005,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
drive_state[drive].fd_ref++;
- opened_bdev[drive] = bdev;
+ opened_disk[drive] = disk;
res = -ENXIO;
@@ -4038,7 +4039,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
}
}
- new_dev = MINOR(bdev->bd_dev);
+ new_dev = disk->first_minor;
drive_state[drive].fd_device = new_dev;
set_capacity(disks[drive][ITYPE(new_dev)], floppy_sizes[new_dev]);
if (old_dev != -1 && old_dev != new_dev) {
@@ -4048,21 +4049,20 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
if (fdc_state[FDC(drive)].rawcmd == 1)
fdc_state[FDC(drive)].rawcmd = 2;
-
- if (!(mode & FMODE_NDELAY)) {
- if (mode & (FMODE_READ|FMODE_WRITE)) {
+ if (!(mode & BLK_OPEN_NDELAY)) {
+ if (mode & (BLK_OPEN_READ | BLK_OPEN_WRITE)) {
drive_state[drive].last_checked = 0;
clear_bit(FD_OPEN_SHOULD_FAIL_BIT,
&drive_state[drive].flags);
- if (bdev_check_media_change(bdev))
- floppy_revalidate(bdev->bd_disk);
+ if (disk_check_media_change(disk))
+ floppy_revalidate(disk);
if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags))
goto out;
if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags))
goto out;
}
res = -EROFS;
- if ((mode & FMODE_WRITE) &&
+ if ((mode & BLK_OPEN_WRITE) &&
!test_bit(FD_DISK_WRITABLE_BIT, &drive_state[drive].flags))
goto out;
}
@@ -4073,7 +4073,7 @@ out:
drive_state[drive].fd_ref--;
if (!drive_state[drive].fd_ref)
- opened_bdev[drive] = NULL;
+ opened_disk[drive] = NULL;
out2:
mutex_unlock(&open_lock);
mutex_unlock(&floppy_mutex);
@@ -4147,7 +4147,7 @@ static int __floppy_read_block_0(struct block_device *bdev, int drive)
cbdata.drive = drive;
bio_init(&bio, bdev, &bio_vec, 1, REQ_OP_READ);
- bio_add_page(&bio, page, block_size(bdev), 0);
+ __bio_add_page(&bio, page, block_size(bdev), 0);
bio.bi_iter.bi_sector = 0;
bio.bi_flags |= (1 << BIO_QUIET);
@@ -4203,7 +4203,8 @@ static int floppy_revalidate(struct gendisk *disk)
drive_state[drive].generation++;
if (drive_no_geom(drive)) {
/* auto-sensing */
- res = __floppy_read_block_0(opened_bdev[drive], drive);
+ res = __floppy_read_block_0(opened_disk[drive]->part0,
+ drive);
} else {
if (cf)
poll_drive(false, FD_RAW_NEED_DISK);
@@ -4509,13 +4510,16 @@ static bool floppy_available(int drive)
static int floppy_alloc_disk(unsigned int drive, unsigned int type)
{
+ struct queue_limits lim = {
+ .max_hw_sectors = 64,
+ .features = BLK_FEAT_ROTATIONAL,
+ };
struct gendisk *disk;
- disk = blk_mq_alloc_disk(&tag_sets[drive], NULL);
+ disk = blk_mq_alloc_disk(&tag_sets[drive], &lim, NULL);
if (IS_ERR(disk))
return PTR_ERR(disk);
- blk_queue_max_hw_sectors(disk->queue, 64);
disk->major = FLOPPY_MAJOR;
disk->first_minor = TOMINOR(drive) | (type << 2);
disk->minors = 1;
@@ -4587,14 +4591,15 @@ static int __init do_floppy_init(void)
tag_sets[drive].nr_maps = 1;
tag_sets[drive].queue_depth = 2;
tag_sets[drive].numa_node = NUMA_NO_NODE;
- tag_sets[drive].flags = BLK_MQ_F_SHOULD_MERGE;
err = blk_mq_alloc_tag_set(&tag_sets[drive]);
if (err)
goto out_put_disk;
err = floppy_alloc_disk(drive, 0);
- if (err)
+ if (err) {
+ blk_mq_free_tag_set(&tag_sets[drive]);
goto out_put_disk;
+ }
timer_setup(&motor_off_timer[drive], motor_off_callback, 0);
}
@@ -4752,7 +4757,7 @@ out_put_disk:
for (drive = 0; drive < N_DRIVE; drive++) {
if (!disks[drive][0])
break;
- del_timer_sync(&motor_off_timer[drive]);
+ timer_delete_sync(&motor_off_timer[drive]);
put_disk(disks[drive][0]);
blk_mq_free_tag_set(&tag_sets[drive]);
}
@@ -4973,7 +4978,7 @@ static void __exit floppy_module_exit(void)
destroy_workqueue(floppy_wq);
for (drive = 0; drive < N_DRIVE; drive++) {
- del_timer_sync(&motor_off_timer[drive]);
+ timer_delete_sync(&motor_off_timer[drive]);
if (floppy_available(drive)) {
for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
@@ -5006,6 +5011,7 @@ module_param(floppy, charp, 0);
module_param(FLOPPY_IRQ, int, 0);
module_param(FLOPPY_DMA, int, 0);
MODULE_AUTHOR("Alain L. Knaff");
+MODULE_DESCRIPTION("Normal floppy disk support");
MODULE_LICENSE("GPL");
/* This doesn't actually get used other than for module information */
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index ad92192c7d61..272bc608e528 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -45,8 +45,6 @@ enum {
Lo_deleting,
};
-struct loop_func_table;
-
struct loop_device {
int lo_number;
loff_t lo_offset;
@@ -54,7 +52,8 @@ struct loop_device {
int lo_flags;
char lo_file_name[LO_NAME_SIZE];
- struct file * lo_backing_file;
+ struct file *lo_backing_file;
+ unsigned int lo_min_dio_size;
struct block_device *lo_device;
gfp_t old_gfp_mask;
@@ -68,7 +67,6 @@ struct loop_device {
struct list_head idle_worker_list;
struct rb_root worker_tree;
struct timer_list timer;
- bool use_dio;
bool sysfs_inited;
struct request_queue *lo_queue;
@@ -90,7 +88,7 @@ struct loop_cmd {
};
#define LOOP_IDLE_WORKER_TIMEOUT (60 * HZ)
-#define LOOP_DEFAULT_HW_Q_DEPTH (128)
+#define LOOP_DEFAULT_HW_Q_DEPTH 128
static DEFINE_IDR(loop_index_idr);
static DEFINE_MUTEX(loop_ctl_mutex);
@@ -139,20 +137,35 @@ static void loop_global_unlock(struct loop_device *lo, bool global)
static int max_part;
static int part_shift;
-static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file)
+static loff_t lo_calculate_size(struct loop_device *lo, struct file *file)
{
loff_t loopsize;
+ int ret;
+
+ if (S_ISBLK(file_inode(file)->i_mode)) {
+ loopsize = i_size_read(file->f_mapping->host);
+ } else {
+ struct kstat stat;
+
+ /*
+ * Get the accurate file size. This provides better results than
+ * cached inode data, particularly for network filesystems where
+ * metadata may be stale.
+ */
+ ret = vfs_getattr_nosec(&file->f_path, &stat, STATX_SIZE, 0);
+ if (ret)
+ return 0;
- /* Compute loopsize in bytes */
- loopsize = i_size_read(file->f_mapping->host);
- if (offset > 0)
- loopsize -= offset;
+ loopsize = stat.size;
+ }
+
+ if (lo->lo_offset > 0)
+ loopsize -= lo->lo_offset;
/* offset is beyond i_size, weird but possible */
if (loopsize < 0)
return 0;
-
- if (sizelimit > 0 && sizelimit < loopsize)
- loopsize = sizelimit;
+ if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize)
+ loopsize = lo->lo_sizelimit;
/*
* Unfortunately, if we want to do I/O on the device,
* the number of 512-byte sectors has to fit into a sector_t.
@@ -160,68 +173,38 @@ static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file)
return loopsize >> 9;
}
-static loff_t get_loop_size(struct loop_device *lo, struct file *file)
+/*
+ * We support direct I/O only if lo_offset is aligned with the logical I/O size
+ * of backing device, and the logical block size of loop is bigger than that of
+ * the backing device.
+ */
+static bool lo_can_use_dio(struct loop_device *lo)
{
- return get_size(lo->lo_offset, lo->lo_sizelimit, file);
+ if (!(lo->lo_backing_file->f_mode & FMODE_CAN_ODIRECT))
+ return false;
+ if (queue_logical_block_size(lo->lo_queue) < lo->lo_min_dio_size)
+ return false;
+ if (lo->lo_offset & (lo->lo_min_dio_size - 1))
+ return false;
+ return true;
}
-static void __loop_update_dio(struct loop_device *lo, bool dio)
+/*
+ * Direct I/O can be enabled either by using an O_DIRECT file descriptor, or by
+ * passing in the LO_FLAGS_DIRECT_IO flag from userspace. It will be silently
+ * disabled when the device block size is too small or the offset is unaligned.
+ *
+ * loop_get_status will always report the effective LO_FLAGS_DIRECT_IO flag and
+ * not the originally passed in one.
+ */
+static inline void loop_update_dio(struct loop_device *lo)
{
- struct file *file = lo->lo_backing_file;
- struct address_space *mapping = file->f_mapping;
- struct inode *inode = mapping->host;
- unsigned short sb_bsize = 0;
- unsigned dio_align = 0;
- bool use_dio;
-
- if (inode->i_sb->s_bdev) {
- sb_bsize = bdev_logical_block_size(inode->i_sb->s_bdev);
- dio_align = sb_bsize - 1;
- }
-
- /*
- * We support direct I/O only if lo_offset is aligned with the
- * logical I/O size of backing device, and the logical block
- * size of loop is bigger than the backing device's.
- *
- * TODO: the above condition may be loosed in the future, and
- * direct I/O may be switched runtime at that time because most
- * of requests in sane applications should be PAGE_SIZE aligned
- */
- if (dio) {
- if (queue_logical_block_size(lo->lo_queue) >= sb_bsize &&
- !(lo->lo_offset & dio_align) &&
- (file->f_mode & FMODE_CAN_ODIRECT))
- use_dio = true;
- else
- use_dio = false;
- } else {
- use_dio = false;
- }
-
- if (lo->use_dio == use_dio)
- return;
-
- /* flush dirty pages before changing direct IO */
- vfs_fsync(file, 0);
+ lockdep_assert_held(&lo->lo_mutex);
+ WARN_ON_ONCE(lo->lo_state == Lo_bound &&
+ lo->lo_queue->mq_freeze_depth == 0);
- /*
- * The flag of LO_FLAGS_DIRECT_IO is handled similarly with
- * LO_FLAGS_READ_ONLY, both are set from kernel, and losetup
- * will get updated by ioctl(LOOP_GET_STATUS)
- */
- if (lo->lo_state == Lo_bound)
- blk_mq_freeze_queue(lo->lo_queue);
- lo->use_dio = use_dio;
- if (use_dio) {
- blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, lo->lo_queue);
- lo->lo_flags |= LO_FLAGS_DIRECT_IO;
- } else {
- blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ if ((lo->lo_flags & LO_FLAGS_DIRECT_IO) && !lo_can_use_dio(lo))
lo->lo_flags &= ~LO_FLAGS_DIRECT_IO;
- }
- if (lo->lo_state == Lo_bound)
- blk_mq_unfreeze_queue(lo->lo_queue);
}
/**
@@ -238,72 +221,26 @@ static void loop_set_size(struct loop_device *lo, loff_t size)
kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
}
-static int lo_write_bvec(struct file *file, struct bio_vec *bvec, loff_t *ppos)
+static void loop_clear_limits(struct loop_device *lo, int mode)
{
- struct iov_iter i;
- ssize_t bw;
-
- iov_iter_bvec(&i, WRITE, bvec, 1, bvec->bv_len);
+ struct queue_limits lim = queue_limits_start_update(lo->lo_queue);
- file_start_write(file);
- bw = vfs_iter_write(file, &i, ppos, 0);
- file_end_write(file);
-
- if (likely(bw == bvec->bv_len))
- return 0;
+ if (mode & FALLOC_FL_ZERO_RANGE)
+ lim.max_write_zeroes_sectors = 0;
- printk_ratelimited(KERN_ERR
- "loop: Write error at byte offset %llu, length %i.\n",
- (unsigned long long)*ppos, bvec->bv_len);
- if (bw >= 0)
- bw = -EIO;
- return bw;
-}
-
-static int lo_write_simple(struct loop_device *lo, struct request *rq,
- loff_t pos)
-{
- struct bio_vec bvec;
- struct req_iterator iter;
- int ret = 0;
-
- rq_for_each_segment(bvec, rq, iter) {
- ret = lo_write_bvec(lo->lo_backing_file, &bvec, &pos);
- if (ret < 0)
- break;
- cond_resched();
+ if (mode & FALLOC_FL_PUNCH_HOLE) {
+ lim.max_hw_discard_sectors = 0;
+ lim.discard_granularity = 0;
}
- return ret;
-}
-
-static int lo_read_simple(struct loop_device *lo, struct request *rq,
- loff_t pos)
-{
- struct bio_vec bvec;
- struct req_iterator iter;
- struct iov_iter i;
- ssize_t len;
-
- rq_for_each_segment(bvec, rq, iter) {
- iov_iter_bvec(&i, READ, &bvec, 1, bvec.bv_len);
- len = vfs_iter_read(lo->lo_backing_file, &i, &pos, 0);
- if (len < 0)
- return len;
-
- flush_dcache_page(bvec.bv_page);
-
- if (len != bvec.bv_len) {
- struct bio *bio;
-
- __rq_for_each_bio(bio, rq)
- zero_fill_bio(bio);
- break;
- }
- cond_resched();
- }
-
- return 0;
+ /*
+ * XXX: this updates the queue limits without freezing the queue, which
+ * is against the locking protocol and dangerous. But we can't just
+ * freeze the queue as we're inside the ->queue_rq method here. So this
+ * should move out into a workqueue unless we get the file operations to
+ * advertise if they support specific fallocate operations.
+ */
+ queue_limits_commit_update(lo->lo_queue, &lim);
}
static int lo_fallocate(struct loop_device *lo, struct request *rq, loff_t pos,
@@ -324,6 +261,14 @@ static int lo_fallocate(struct loop_device *lo, struct request *rq, loff_t pos,
ret = file->f_op->fallocate(file, mode, pos, blk_rq_bytes(rq));
if (unlikely(ret && ret != -EINVAL && ret != -EOPNOTSUPP))
return -EIO;
+
+ /*
+ * We initially configure the limits in a hope that fallocate is
+ * supported and clear them here if that turns out not to be true.
+ */
+ if (unlikely(ret == -EOPNOTSUPP))
+ loop_clear_limits(lo, mode);
+
return ret;
}
@@ -341,7 +286,7 @@ static void lo_complete_rq(struct request *rq)
struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
blk_status_t ret = BLK_STS_OK;
- if (!cmd->use_aio || cmd->ret < 0 || cmd->ret == blk_rq_bytes(rq) ||
+ if (cmd->ret < 0 || cmd->ret == blk_rq_bytes(rq) ||
req_op(rq) != REQ_OP_READ) {
if (cmd->ret < 0)
ret = errno_to_blk_status(cmd->ret);
@@ -357,14 +302,13 @@ static void lo_complete_rq(struct request *rq)
cmd->ret = 0;
blk_mq_requeue_request(rq, true);
} else {
- if (cmd->use_aio) {
- struct bio *bio = rq->bio;
+ struct bio *bio = rq->bio;
- while (bio) {
- zero_fill_bio(bio);
- bio = bio->bi_next;
- }
+ while (bio) {
+ zero_fill_bio(bio);
+ bio = bio->bi_next;
}
+
ret = BLK_STS_IOERR;
end_io:
blk_mq_end_request(rq, ret);
@@ -379,6 +323,8 @@ static void lo_rw_aio_do_completion(struct loop_cmd *cmd)
return;
kfree(cmd->bvec);
cmd->bvec = NULL;
+ if (req_op(rq) == REQ_OP_WRITE)
+ kiocb_end_write(&cmd->iocb);
if (likely(!blk_should_fake_timeout(rq->q)))
blk_mq_complete_request(rq);
}
@@ -392,7 +338,7 @@ static void lo_rw_aio_complete(struct kiocb *iocb, long ret)
}
static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
- loff_t pos, bool rw)
+ loff_t pos, int rw)
{
struct iov_iter iter;
struct req_iterator rq_iter;
@@ -402,11 +348,10 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
struct file *file = lo->lo_backing_file;
struct bio_vec tmp;
unsigned int offset;
- int nr_bvec = 0;
+ unsigned int nr_bvec;
int ret;
- rq_for_each_bvec(tmp, rq, rq_iter)
- nr_bvec++;
+ nr_bvec = blk_rq_nr_bvec(rq);
if (rq->bio != rq->biotail) {
@@ -444,20 +389,26 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
cmd->iocb.ki_pos = pos;
cmd->iocb.ki_filp = file;
- cmd->iocb.ki_complete = lo_rw_aio_complete;
- cmd->iocb.ki_flags = IOCB_DIRECT;
- cmd->iocb.ki_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0);
+ cmd->iocb.ki_ioprio = req_get_ioprio(rq);
+ if (cmd->use_aio) {
+ cmd->iocb.ki_complete = lo_rw_aio_complete;
+ cmd->iocb.ki_flags = IOCB_DIRECT;
+ } else {
+ cmd->iocb.ki_complete = NULL;
+ cmd->iocb.ki_flags = 0;
+ }
- if (rw == WRITE)
- ret = call_write_iter(file, &cmd->iocb, &iter);
- else
- ret = call_read_iter(file, &cmd->iocb, &iter);
+ if (rw == ITER_SOURCE) {
+ kiocb_start_write(&cmd->iocb);
+ ret = file->f_op->write_iter(&cmd->iocb, &iter);
+ } else
+ ret = file->f_op->read_iter(&cmd->iocb, &iter);
lo_rw_aio_do_completion(cmd);
if (ret != -EIOCBQUEUED)
lo_rw_aio_complete(&cmd->iocb, ret);
- return 0;
+ return -EIOCBQUEUED;
}
static int do_req_filebacked(struct loop_device *lo, struct request *rq)
@@ -465,15 +416,6 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
loff_t pos = ((loff_t) blk_rq_pos(rq) << 9) + lo->lo_offset;
- /*
- * lo_write_simple and lo_read_simple should have been covered
- * by io submit style function like lo_rw_aio(), one blocker
- * is that lo_read_simple() need to call flush_dcache_page after
- * the page is written from kernel, and it isn't easy to handle
- * this in io submit style function which submits all segments
- * of the req at one time. And direct read IO doesn't need to
- * run flush_dcache_page().
- */
switch (req_op(rq)) {
case REQ_OP_FLUSH:
return lo_req_flush(lo, rq);
@@ -489,27 +431,15 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
case REQ_OP_DISCARD:
return lo_fallocate(lo, rq, pos, FALLOC_FL_PUNCH_HOLE);
case REQ_OP_WRITE:
- if (cmd->use_aio)
- return lo_rw_aio(lo, cmd, pos, WRITE);
- else
- return lo_write_simple(lo, rq, pos);
+ return lo_rw_aio(lo, cmd, pos, ITER_SOURCE);
case REQ_OP_READ:
- if (cmd->use_aio)
- return lo_rw_aio(lo, cmd, pos, READ);
- else
- return lo_read_simple(lo, rq, pos);
+ return lo_rw_aio(lo, cmd, pos, ITER_DEST);
default:
WARN_ON_ONCE(1);
return -EIO;
}
}
-static inline void loop_update_dio(struct loop_device *lo)
-{
- __loop_update_dio(lo, (lo->lo_backing_file->f_flags & O_DIRECT) |
- lo->use_dio);
-}
-
static void loop_reread_partitions(struct loop_device *lo)
{
int rc;
@@ -522,6 +452,28 @@ static void loop_reread_partitions(struct loop_device *lo)
__func__, lo->lo_number, lo->lo_file_name, rc);
}
+static unsigned int loop_query_min_dio_size(struct loop_device *lo)
+{
+ struct file *file = lo->lo_backing_file;
+ struct block_device *sb_bdev = file->f_mapping->host->i_sb->s_bdev;
+ struct kstat st;
+
+ /*
+ * Use the minimal dio alignment of the file system if provided.
+ */
+ if (!vfs_getattr(&file->f_path, &st, STATX_DIOALIGN, 0) &&
+ (st.result_mask & STATX_DIOALIGN))
+ return st.dio_offset_align;
+
+ /*
+ * In a perfect world this wouldn't be needed, but as of Linux 6.13 only
+ * a handful of file systems support the STATX_DIOALIGN flag.
+ */
+ if (sb_bdev)
+ return bdev_logical_block_size(sb_bdev);
+ return SECTOR_SIZE;
+}
+
static inline int is_loop_device(struct file *file)
{
struct inode *i = file->f_mapping->host;
@@ -554,6 +506,28 @@ static int loop_validate_file(struct file *file, struct block_device *bdev)
return 0;
}
+static void loop_assign_backing_file(struct loop_device *lo, struct file *file)
+{
+ lo->lo_backing_file = file;
+ lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping);
+ mapping_set_gfp_mask(file->f_mapping,
+ lo->old_gfp_mask & ~(__GFP_IO | __GFP_FS));
+ if (lo->lo_backing_file->f_flags & O_DIRECT)
+ lo->lo_flags |= LO_FLAGS_DIRECT_IO;
+ lo->lo_min_dio_size = loop_query_min_dio_size(lo);
+}
+
+static int loop_check_backing_file(struct file *file)
+{
+ if (!file->f_op->read_iter)
+ return -EINVAL;
+
+ if ((file->f_mode & FMODE_WRITE) && !file->f_op->write_iter)
+ return -EINVAL;
+
+ return 0;
+}
+
/*
* loop_change_fd switched the backing store of a loopback device to
* a new file. This is useful for operating system installers to free up
@@ -567,6 +541,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
{
struct file *file = fget(arg);
struct file *old_file;
+ unsigned int memflags;
int error;
bool partscan;
bool is_loop;
@@ -574,6 +549,12 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
if (!file)
return -EBADF;
+ error = loop_check_backing_file(file);
+ if (error) {
+ fput(file);
+ return error;
+ }
+
/* suppress uevents while reconfiguring the device */
dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1);
@@ -599,19 +580,23 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
error = -EINVAL;
/* size of the new backing store needs to be the same */
- if (get_loop_size(lo, file) != get_loop_size(lo, old_file))
+ if (lo_calculate_size(lo, file) != lo_calculate_size(lo, old_file))
goto out_err;
+ /*
+ * We might switch to direct I/O mode for the loop device, write back
+ * all dirty data the page cache now that so that the individual I/O
+ * operations don't have to do that.
+ */
+ vfs_fsync(file, 0);
+
/* and ... switch */
- disk_force_media_change(lo->lo_disk, DISK_EVENT_MEDIA_CHANGE);
- blk_mq_freeze_queue(lo->lo_queue);
+ disk_force_media_change(lo->lo_disk);
+ memflags = blk_mq_freeze_queue(lo->lo_queue);
mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
- lo->lo_backing_file = file;
- lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping);
- mapping_set_gfp_mask(file->f_mapping,
- lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+ loop_assign_backing_file(lo, file);
loop_update_dio(lo);
- blk_mq_unfreeze_queue(lo->lo_queue);
+ blk_mq_unfreeze_queue(lo->lo_queue, memflags);
partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
loop_global_unlock(lo, is_loop);
@@ -629,19 +614,20 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
* dependency.
*/
fput(old_file);
+ dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);
if (partscan)
loop_reread_partitions(lo);
error = 0;
done:
- /* enable and uncork uevent now that we are done */
- dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);
+ kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
return error;
out_err:
loop_global_unlock(lo, is_loop);
out_putf:
fput(file);
+ dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);
goto done;
}
@@ -754,12 +740,12 @@ static void loop_sysfs_exit(struct loop_device *lo)
&loop_attribute_group);
}
-static void loop_config_discard(struct loop_device *lo)
+static void loop_get_discard_config(struct loop_device *lo,
+ u32 *granularity, u32 *max_discard_sectors)
{
struct file *file = lo->lo_backing_file;
struct inode *inode = file->f_mapping->host;
- struct request_queue *q = lo->lo_queue;
- u32 granularity, max_discard_sectors;
+ struct kstatfs sbuf;
/*
* If the backing device is a block device, mirror its zeroing
@@ -769,38 +755,18 @@ static void loop_config_discard(struct loop_device *lo)
* file-backed loop devices: discarded regions read back as zero.
*/
if (S_ISBLK(inode->i_mode)) {
- struct request_queue *backingq = bdev_get_queue(I_BDEV(inode));
+ struct block_device *bdev = I_BDEV(inode);
- max_discard_sectors = backingq->limits.max_write_zeroes_sectors;
- granularity = bdev_discard_granularity(I_BDEV(inode)) ?:
- queue_physical_block_size(backingq);
+ *max_discard_sectors = bdev_write_zeroes_sectors(bdev);
+ *granularity = bdev_discard_granularity(bdev);
/*
* We use punch hole to reclaim the free space used by the
* image a.k.a. discard.
*/
- } else if (!file->f_op->fallocate) {
- max_discard_sectors = 0;
- granularity = 0;
-
- } else {
- struct kstatfs sbuf;
-
- max_discard_sectors = UINT_MAX >> 9;
- if (!vfs_statfs(&file->f_path, &sbuf))
- granularity = sbuf.f_bsize;
- else
- max_discard_sectors = 0;
- }
-
- if (max_discard_sectors) {
- q->limits.discard_granularity = granularity;
- blk_queue_max_discard_sectors(q, max_discard_sectors);
- blk_queue_max_write_zeroes_sectors(q, max_discard_sectors);
- } else {
- q->limits.discard_granularity = 0;
- blk_queue_max_discard_sectors(q, 0);
- blk_queue_max_write_zeroes_sectors(q, 0);
+ } else if (file->f_op->fallocate && !vfs_statfs(&file->f_path, &sbuf)) {
+ *max_discard_sectors = UINT_MAX >> 9;
+ *granularity = sbuf.f_bsize;
}
}
@@ -857,7 +823,7 @@ static void loop_queue_work(struct loop_device *lo, struct loop_cmd *cmd)
if (worker)
goto queue_work;
- worker = kzalloc(sizeof(struct loop_worker), GFP_NOWAIT | __GFP_NOWARN);
+ worker = kzalloc(sizeof(struct loop_worker), GFP_NOWAIT);
/*
* In the event we cannot allocate a worker, just queue on the
* rootcg worker and issue the I/O as the rootcg
@@ -931,24 +897,6 @@ static void loop_free_idle_workers_timer(struct timer_list *timer)
return loop_free_idle_workers(lo, false);
}
-static void loop_update_rotational(struct loop_device *lo)
-{
- struct file *file = lo->lo_backing_file;
- struct inode *file_inode = file->f_mapping->host;
- struct block_device *file_bdev = file_inode->i_sb->s_bdev;
- struct request_queue *q = lo->lo_queue;
- bool nonrot = true;
-
- /* not all filesystems (e.g. tmpfs) have a sb->s_bdev */
- if (file_bdev)
- nonrot = bdev_nonrot(file_bdev);
-
- if (nonrot)
- blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
- else
- blk_queue_flag_clear(QUEUE_FLAG_NONROT, q);
-}
-
/**
* loop_set_status_from_info - configure device from loop_info
* @lo: struct loop_device to configure
@@ -977,48 +925,91 @@ loop_set_status_from_info(struct loop_device *lo,
return -EINVAL;
}
+ /* Avoid assigning overflow values */
+ if (info->lo_offset > LLONG_MAX || info->lo_sizelimit > LLONG_MAX)
+ return -EOVERFLOW;
+
lo->lo_offset = info->lo_offset;
lo->lo_sizelimit = info->lo_sizelimit;
- /* loff_t vars have been assigned __u64 */
- if (lo->lo_offset < 0 || lo->lo_sizelimit < 0)
- return -EOVERFLOW;
-
memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE);
lo->lo_file_name[LO_NAME_SIZE-1] = 0;
- lo->lo_flags = info->lo_flags;
return 0;
}
-static int loop_configure(struct loop_device *lo, fmode_t mode,
+static unsigned int loop_default_blocksize(struct loop_device *lo)
+{
+ /* In case of direct I/O, match underlying minimum I/O size */
+ if (lo->lo_flags & LO_FLAGS_DIRECT_IO)
+ return lo->lo_min_dio_size;
+ return SECTOR_SIZE;
+}
+
+static void loop_update_limits(struct loop_device *lo, struct queue_limits *lim,
+ unsigned int bsize)
+{
+ struct file *file = lo->lo_backing_file;
+ struct inode *inode = file->f_mapping->host;
+ struct block_device *backing_bdev = NULL;
+ u32 granularity = 0, max_discard_sectors = 0;
+
+ if (S_ISBLK(inode->i_mode))
+ backing_bdev = I_BDEV(inode);
+ else if (inode->i_sb->s_bdev)
+ backing_bdev = inode->i_sb->s_bdev;
+
+ if (!bsize)
+ bsize = loop_default_blocksize(lo);
+
+ loop_get_discard_config(lo, &granularity, &max_discard_sectors);
+
+ lim->logical_block_size = bsize;
+ lim->physical_block_size = bsize;
+ lim->io_min = bsize;
+ lim->features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_ROTATIONAL);
+ if (file->f_op->fsync && !(lo->lo_flags & LO_FLAGS_READ_ONLY))
+ lim->features |= BLK_FEAT_WRITE_CACHE;
+ if (backing_bdev && !bdev_nonrot(backing_bdev))
+ lim->features |= BLK_FEAT_ROTATIONAL;
+ lim->max_hw_discard_sectors = max_discard_sectors;
+ lim->max_write_zeroes_sectors = max_discard_sectors;
+ if (max_discard_sectors)
+ lim->discard_granularity = granularity;
+ else
+ lim->discard_granularity = 0;
+}
+
+static int loop_configure(struct loop_device *lo, blk_mode_t mode,
struct block_device *bdev,
const struct loop_config *config)
{
struct file *file = fget(config->fd);
- struct inode *inode;
- struct address_space *mapping;
+ struct queue_limits lim;
int error;
loff_t size;
bool partscan;
- unsigned short bsize;
bool is_loop;
if (!file)
return -EBADF;
+
+ error = loop_check_backing_file(file);
+ if (error) {
+ fput(file);
+ return error;
+ }
+
is_loop = is_loop_device(file);
/* This is safe, since we have a reference from open(). */
__module_get(THIS_MODULE);
- /* suppress uevents while reconfiguring the device */
- dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1);
-
/*
* If we don't hold exclusive handle for the device, upgrade to it
* here to avoid changing device under exclusive owner.
*/
- if (!(mode & FMODE_EXCL)) {
- error = bd_prepare_to_claim(bdev, loop_configure);
+ if (!(mode & BLK_OPEN_EXCL)) {
+ error = bd_prepare_to_claim(bdev, loop_configure, NULL);
if (error)
goto out_putf;
}
@@ -1035,25 +1026,17 @@ static int loop_configure(struct loop_device *lo, fmode_t mode,
if (error)
goto out_unlock;
- mapping = file->f_mapping;
- inode = mapping->host;
-
if ((config->info.lo_flags & ~LOOP_CONFIGURE_SETTABLE_FLAGS) != 0) {
error = -EINVAL;
goto out_unlock;
}
- if (config->block_size) {
- error = blk_validate_block_size(config->block_size);
- if (error)
- goto out_unlock;
- }
-
error = loop_set_status_from_info(lo, &config->info);
if (error)
goto out_unlock;
+ lo->lo_flags = config->info.lo_flags;
- if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) ||
+ if (!(file->f_mode & FMODE_WRITE) || !(mode & BLK_OPEN_WRITE) ||
!file->f_op->write_iter)
lo->lo_flags |= LO_FLAGS_READ_ONLY;
@@ -1067,36 +1050,33 @@ static int loop_configure(struct loop_device *lo, fmode_t mode,
}
}
- disk_force_media_change(lo->lo_disk, DISK_EVENT_MEDIA_CHANGE);
+ /* suppress uevents while reconfiguring the device */
+ dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1);
+
+ disk_force_media_change(lo->lo_disk);
set_disk_ro(lo->lo_disk, (lo->lo_flags & LO_FLAGS_READ_ONLY) != 0);
- lo->use_dio = lo->lo_flags & LO_FLAGS_DIRECT_IO;
lo->lo_device = bdev;
- lo->lo_backing_file = file;
- lo->old_gfp_mask = mapping_gfp_mask(mapping);
- mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+ loop_assign_backing_file(lo, file);
- if (!(lo->lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
- blk_queue_write_cache(lo->lo_queue, true, false);
-
- if (config->block_size)
- bsize = config->block_size;
- else if ((lo->lo_backing_file->f_flags & O_DIRECT) && inode->i_sb->s_bdev)
- /* In case of direct I/O, match underlying block size */
- bsize = bdev_logical_block_size(inode->i_sb->s_bdev);
- else
- bsize = 512;
+ lim = queue_limits_start_update(lo->lo_queue);
+ loop_update_limits(lo, &lim, config->block_size);
+ /* No need to freeze the queue as the device isn't bound yet. */
+ error = queue_limits_commit_update(lo->lo_queue, &lim);
+ if (error)
+ goto out_unlock;
- blk_queue_logical_block_size(lo->lo_queue, bsize);
- blk_queue_physical_block_size(lo->lo_queue, bsize);
- blk_queue_io_min(lo->lo_queue, bsize);
+ /*
+ * We might switch to direct I/O mode for the loop device, write back
+ * all dirty data the page cache now that so that the individual I/O
+ * operations don't have to do that.
+ */
+ vfs_fsync(file, 0);
- loop_config_discard(lo);
- loop_update_rotational(lo);
loop_update_dio(lo);
loop_sysfs_init(lo);
- size = get_loop_size(lo, file);
+ size = lo_calculate_size(lo, file);
loop_set_size(lo, size);
/* Order wrt reading lo_state in loop_validate_file(). */
@@ -1109,46 +1089,36 @@ static int loop_configure(struct loop_device *lo, fmode_t mode,
if (partscan)
clear_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state);
+ dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);
+ kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
+
loop_global_unlock(lo, is_loop);
if (partscan)
loop_reread_partitions(lo);
- if (!(mode & FMODE_EXCL))
+
+ if (!(mode & BLK_OPEN_EXCL))
bd_abort_claiming(bdev, loop_configure);
- error = 0;
-done:
- /* enable and uncork uevent now that we are done */
- dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);
- return error;
+ return 0;
out_unlock:
loop_global_unlock(lo, is_loop);
out_bdev:
- if (!(mode & FMODE_EXCL))
+ if (!(mode & BLK_OPEN_EXCL))
bd_abort_claiming(bdev, loop_configure);
out_putf:
fput(file);
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
- goto done;
+ return error;
}
-static void __loop_clr_fd(struct loop_device *lo, bool release)
+static void __loop_clr_fd(struct loop_device *lo)
{
+ struct queue_limits lim;
struct file *filp;
gfp_t gfp = lo->old_gfp_mask;
- if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags))
- blk_queue_write_cache(lo->lo_queue, false, false);
-
- /*
- * Freeze the request queue when unbinding on a live file descriptor and
- * thus an open device. When called from ->release we are guaranteed
- * that there is no I/O in progress already.
- */
- if (!release)
- blk_mq_freeze_queue(lo->lo_queue);
-
spin_lock_irq(&lo->lo_lock);
filp = lo->lo_backing_file;
lo->lo_backing_file = NULL;
@@ -1158,9 +1128,19 @@ static void __loop_clr_fd(struct loop_device *lo, bool release)
lo->lo_offset = 0;
lo->lo_sizelimit = 0;
memset(lo->lo_file_name, 0, LO_NAME_SIZE);
- blk_queue_logical_block_size(lo->lo_queue, 512);
- blk_queue_physical_block_size(lo->lo_queue, 512);
- blk_queue_io_min(lo->lo_queue, 512);
+
+ /*
+ * Reset the block size to the default.
+ *
+ * No queue freezing needed because this is called from the final
+ * ->release call only, so there can't be any outstanding I/O.
+ */
+ lim = queue_limits_start_update(lo->lo_queue);
+ lim.logical_block_size = SECTOR_SIZE;
+ lim.physical_block_size = SECTOR_SIZE;
+ lim.io_min = SECTOR_SIZE;
+ queue_limits_commit_update(lo->lo_queue, &lim);
+
invalidate_disk(lo->lo_disk);
loop_sysfs_exit(lo);
/* let user-space know about this change */
@@ -1168,10 +1148,8 @@ static void __loop_clr_fd(struct loop_device *lo, bool release)
mapping_set_gfp_mask(filp->f_mapping, gfp);
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
- if (!release)
- blk_mq_unfreeze_queue(lo->lo_queue);
- disk_force_media_change(lo->lo_disk, DISK_EVENT_MEDIA_CHANGE);
+ disk_force_media_change(lo->lo_disk);
if (lo->lo_flags & LO_FLAGS_PARTSCAN) {
int err;
@@ -1184,11 +1162,7 @@ static void __loop_clr_fd(struct loop_device *lo, bool release)
* must be at least one and it can only become zero when the
* current holder is released.
*/
- if (!release)
- mutex_lock(&lo->lo_disk->open_mutex);
err = bdev_disk_changed(lo->lo_disk, false);
- if (!release)
- mutex_unlock(&lo->lo_disk->open_mutex);
if (err)
pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
__func__, lo->lo_number, err);
@@ -1237,24 +1211,16 @@ static int loop_clr_fd(struct loop_device *lo)
return -ENXIO;
}
/*
- * If we've explicitly asked to tear down the loop device,
- * and it has an elevated reference count, set it for auto-teardown when
- * the last reference goes away. This stops $!~#$@ udev from
- * preventing teardown because it decided that it needs to run blkid on
- * the loopback device whenever they appear. xfstests is notorious for
- * failing tests because blkid via udev races with a losetup
- * <dev>/do something like mkfs/losetup -d <dev> causing the losetup -d
- * command to fail with EBUSY.
+ * Mark the device for removing the backing device on last close.
+ * If we are the only opener, also switch the state to roundown here to
+ * prevent new openers from coming in.
*/
- if (disk_openers(lo->lo_disk) > 1) {
- lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
- loop_global_unlock(lo, true);
- return 0;
- }
- lo->lo_state = Lo_rundown;
+
+ lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
+ if (disk_openers(lo->lo_disk) == 1)
+ lo->lo_state = Lo_rundown;
loop_global_unlock(lo, true);
- __loop_clr_fd(lo, false);
return 0;
}
@@ -1262,9 +1228,9 @@ static int
loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
{
int err;
- int prev_lo_flags;
bool partscan = false;
bool size_changed = false;
+ unsigned int memflags;
err = mutex_lock_killable(&lo->lo_mutex);
if (err)
@@ -1281,40 +1247,29 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
invalidate_bdev(lo->lo_device);
}
- /* I/O need to be drained during transfer transition */
- blk_mq_freeze_queue(lo->lo_queue);
-
- prev_lo_flags = lo->lo_flags;
+ /* I/O needs to be drained before changing lo_offset or lo_sizelimit */
+ memflags = blk_mq_freeze_queue(lo->lo_queue);
err = loop_set_status_from_info(lo, info);
if (err)
goto out_unfreeze;
- /* Mask out flags that can't be set using LOOP_SET_STATUS. */
- lo->lo_flags &= LOOP_SET_STATUS_SETTABLE_FLAGS;
- /* For those flags, use the previous values instead */
- lo->lo_flags |= prev_lo_flags & ~LOOP_SET_STATUS_SETTABLE_FLAGS;
- /* For flags that can't be cleared, use previous values too */
- lo->lo_flags |= prev_lo_flags & ~LOOP_SET_STATUS_CLEARABLE_FLAGS;
-
- if (size_changed) {
- loff_t new_size = get_size(lo->lo_offset, lo->lo_sizelimit,
- lo->lo_backing_file);
- loop_set_size(lo, new_size);
- }
+ partscan = !(lo->lo_flags & LO_FLAGS_PARTSCAN) &&
+ (info->lo_flags & LO_FLAGS_PARTSCAN);
- loop_config_discard(lo);
+ lo->lo_flags &= ~LOOP_SET_STATUS_CLEARABLE_FLAGS;
+ lo->lo_flags |= (info->lo_flags & LOOP_SET_STATUS_SETTABLE_FLAGS);
- /* update dio if lo_offset or transfer is changed */
- __loop_update_dio(lo, lo->use_dio);
+ /* update the direct I/O flag if lo_offset changed */
+ loop_update_dio(lo);
out_unfreeze:
- blk_mq_unfreeze_queue(lo->lo_queue);
-
- if (!err && (lo->lo_flags & LO_FLAGS_PARTSCAN) &&
- !(prev_lo_flags & LO_FLAGS_PARTSCAN)) {
+ blk_mq_unfreeze_queue(lo->lo_queue, memflags);
+ if (partscan)
clear_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state);
- partscan = true;
+ if (!err && size_changed) {
+ loff_t new_size = lo_calculate_size(lo, lo->lo_backing_file);
+ loop_set_size(lo, new_size);
}
out_unlock:
mutex_unlock(&lo->lo_mutex);
@@ -1456,7 +1411,7 @@ static int loop_set_capacity(struct loop_device *lo)
if (unlikely(lo->lo_state != Lo_bound))
return -ENXIO;
- size = get_loop_size(lo, lo->lo_backing_file);
+ size = lo_calculate_size(lo, lo->lo_backing_file);
loop_set_size(lo, size);
return 0;
@@ -1464,42 +1419,75 @@ static int loop_set_capacity(struct loop_device *lo)
static int loop_set_dio(struct loop_device *lo, unsigned long arg)
{
- int error = -ENXIO;
- if (lo->lo_state != Lo_bound)
- goto out;
+ bool use_dio = !!arg;
+ unsigned int memflags;
- __loop_update_dio(lo, !!arg);
- if (lo->use_dio == !!arg)
+ if (lo->lo_state != Lo_bound)
+ return -ENXIO;
+ if (use_dio == !!(lo->lo_flags & LO_FLAGS_DIRECT_IO))
return 0;
- error = -EINVAL;
- out:
- return error;
+
+ if (use_dio) {
+ if (!lo_can_use_dio(lo))
+ return -EINVAL;
+ /* flush dirty pages before starting to use direct I/O */
+ vfs_fsync(lo->lo_backing_file, 0);
+ }
+
+ memflags = blk_mq_freeze_queue(lo->lo_queue);
+ if (use_dio)
+ lo->lo_flags |= LO_FLAGS_DIRECT_IO;
+ else
+ lo->lo_flags &= ~LO_FLAGS_DIRECT_IO;
+ blk_mq_unfreeze_queue(lo->lo_queue, memflags);
+ return 0;
}
-static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
+static int loop_set_block_size(struct loop_device *lo, blk_mode_t mode,
+ struct block_device *bdev, unsigned long arg)
{
+ struct queue_limits lim;
+ unsigned int memflags;
int err = 0;
- if (lo->lo_state != Lo_bound)
- return -ENXIO;
+ /*
+ * If we don't hold exclusive handle for the device, upgrade to it
+ * here to avoid changing device under exclusive owner.
+ */
+ if (!(mode & BLK_OPEN_EXCL)) {
+ err = bd_prepare_to_claim(bdev, loop_set_block_size, NULL);
+ if (err)
+ return err;
+ }
- err = blk_validate_block_size(arg);
+ err = mutex_lock_killable(&lo->lo_mutex);
if (err)
- return err;
+ goto abort_claim;
+
+ if (lo->lo_state != Lo_bound) {
+ err = -ENXIO;
+ goto unlock;
+ }
if (lo->lo_queue->limits.logical_block_size == arg)
- return 0;
+ goto unlock;
sync_blockdev(lo->lo_device);
invalidate_bdev(lo->lo_device);
- blk_mq_freeze_queue(lo->lo_queue);
- blk_queue_logical_block_size(lo->lo_queue, arg);
- blk_queue_physical_block_size(lo->lo_queue, arg);
- blk_queue_io_min(lo->lo_queue, arg);
+ lim = queue_limits_start_update(lo->lo_queue);
+ loop_update_limits(lo, &lim, arg);
+
+ memflags = blk_mq_freeze_queue(lo->lo_queue);
+ err = queue_limits_commit_update(lo->lo_queue, &lim);
loop_update_dio(lo);
- blk_mq_unfreeze_queue(lo->lo_queue);
+ blk_mq_unfreeze_queue(lo->lo_queue, memflags);
+unlock:
+ mutex_unlock(&lo->lo_mutex);
+abort_claim:
+ if (!(mode & BLK_OPEN_EXCL))
+ bd_abort_claiming(bdev, loop_set_block_size);
return err;
}
@@ -1518,9 +1506,6 @@ static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd,
case LOOP_SET_DIRECT_IO:
err = loop_set_dio(lo, arg);
break;
- case LOOP_SET_BLOCK_SIZE:
- err = loop_set_block_size(lo, arg);
- break;
default:
err = -EINVAL;
}
@@ -1528,7 +1513,7 @@ static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd,
return err;
}
-static int lo_ioctl(struct block_device *bdev, fmode_t mode,
+static int lo_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg)
{
struct loop_device *lo = bdev->bd_disk->private_data;
@@ -1563,24 +1548,25 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
return loop_clr_fd(lo);
case LOOP_SET_STATUS:
err = -EPERM;
- if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) {
+ if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN))
err = loop_set_status_old(lo, argp);
- }
break;
case LOOP_GET_STATUS:
return loop_get_status_old(lo, argp);
case LOOP_SET_STATUS64:
err = -EPERM;
- if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) {
+ if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN))
err = loop_set_status64(lo, argp);
- }
break;
case LOOP_GET_STATUS64:
return loop_get_status64(lo, argp);
+ case LOOP_SET_BLOCK_SIZE:
+ if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return loop_set_block_size(lo, mode, bdev, arg);
case LOOP_SET_CAPACITY:
case LOOP_SET_DIRECT_IO:
- case LOOP_SET_BLOCK_SIZE:
- if (!(mode & FMODE_WRITE) && !capable(CAP_SYS_ADMIN))
+ if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN))
return -EPERM;
fallthrough;
default:
@@ -1691,7 +1677,7 @@ loop_get_status_compat(struct loop_device *lo,
return err;
}
-static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
+static int lo_compat_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg)
{
struct loop_device *lo = bdev->bd_disk->private_data;
@@ -1727,25 +1713,43 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
}
#endif
-static void lo_release(struct gendisk *disk, fmode_t mode)
+static int lo_open(struct gendisk *disk, blk_mode_t mode)
+{
+ struct loop_device *lo = disk->private_data;
+ int err;
+
+ err = mutex_lock_killable(&lo->lo_mutex);
+ if (err)
+ return err;
+
+ if (lo->lo_state == Lo_deleting || lo->lo_state == Lo_rundown)
+ err = -ENXIO;
+ mutex_unlock(&lo->lo_mutex);
+ return err;
+}
+
+static void lo_release(struct gendisk *disk)
{
struct loop_device *lo = disk->private_data;
+ bool need_clear = false;
if (disk_openers(disk) > 0)
return;
+ /*
+ * Clear the backing device information if this is the last close of
+ * a device that's been marked for auto clear, or on which LOOP_CLR_FD
+ * has been called.
+ */
mutex_lock(&lo->lo_mutex);
- if (lo->lo_state == Lo_bound && (lo->lo_flags & LO_FLAGS_AUTOCLEAR)) {
+ if (lo->lo_state == Lo_bound && (lo->lo_flags & LO_FLAGS_AUTOCLEAR))
lo->lo_state = Lo_rundown;
- mutex_unlock(&lo->lo_mutex);
- /*
- * In autoclear mode, stop the loop thread
- * and remove configuration after last close.
- */
- __loop_clr_fd(lo, true);
- return;
- }
+
+ need_clear = (lo->lo_state == Lo_rundown);
mutex_unlock(&lo->lo_mutex);
+
+ if (need_clear)
+ __loop_clr_fd(lo);
}
static void lo_free_disk(struct gendisk *disk)
@@ -1755,13 +1759,14 @@ static void lo_free_disk(struct gendisk *disk)
if (lo->workqueue)
destroy_workqueue(lo->workqueue);
loop_free_idle_workers(lo, true);
- del_timer_sync(&lo->timer);
+ timer_shutdown_sync(&lo->timer);
mutex_destroy(&lo->lo_mutex);
kfree(lo);
}
static const struct block_device_operations lo_fops = {
.owner = THIS_MODULE,
+ .open = lo_open,
.release = lo_release,
.ioctl = lo_ioctl,
#ifdef CONFIG_COMPAT
@@ -1773,9 +1778,47 @@ static const struct block_device_operations lo_fops = {
/*
* And now the modules code and kernel interface.
*/
-static int max_loop;
-module_param(max_loop, int, 0444);
+
+/*
+ * If max_loop is specified, create that many devices upfront.
+ * This also becomes a hard limit. If max_loop is not specified,
+ * the default isn't a hard limit (as before commit 85c50197716c
+ * changed the default value from 0 for max_loop=0 reasons), just
+ * create CONFIG_BLK_DEV_LOOP_MIN_COUNT loop devices at module
+ * init time. Loop devices can be requested on-demand with the
+ * /dev/loop-control interface, or be instantiated by accessing
+ * a 'dead' device node.
+ */
+static int max_loop = CONFIG_BLK_DEV_LOOP_MIN_COUNT;
+
+#ifdef CONFIG_BLOCK_LEGACY_AUTOLOAD
+static bool max_loop_specified;
+
+static int max_loop_param_set_int(const char *val,
+ const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_int(val, kp);
+ if (ret < 0)
+ return ret;
+
+ max_loop_specified = true;
+ return 0;
+}
+
+static const struct kernel_param_ops max_loop_param_ops = {
+ .set = max_loop_param_set_int,
+ .get = param_get_int,
+};
+
+module_param_cb(max_loop, &max_loop_param_ops, &max_loop, 0444);
MODULE_PARM_DESC(max_loop, "Maximum number of loop devices");
+#else
+module_param(max_loop, int, 0444);
+MODULE_PARM_DESC(max_loop, "Initial number of loop devices");
+#endif
+
module_param(max_part, int, 0444);
MODULE_PARM_DESC(max_part, "Maximum number of partitions per loop device");
@@ -1783,9 +1826,15 @@ static int hw_queue_depth = LOOP_DEFAULT_HW_Q_DEPTH;
static int loop_set_hw_queue_depth(const char *s, const struct kernel_param *p)
{
- int ret = kstrtoint(s, 10, &hw_queue_depth);
+ int qd, ret;
- return (ret || (hw_queue_depth < 1)) ? -EINVAL : 0;
+ ret = kstrtoint(s, 0, &qd);
+ if (ret < 0)
+ return ret;
+ if (qd < 1)
+ return -EINVAL;
+ hw_queue_depth = qd;
+ return 0;
}
static const struct kernel_param_ops loop_hw_qdepth_param_ops = {
@@ -1794,8 +1843,9 @@ static const struct kernel_param_ops loop_hw_qdepth_param_ops = {
};
device_param_cb(hw_queue_depth, &loop_hw_qdepth_param_ops, &hw_queue_depth, 0444);
-MODULE_PARM_DESC(hw_queue_depth, "Queue depth for each hardware queue. Default: 128");
+MODULE_PARM_DESC(hw_queue_depth, "Queue depth for each hardware queue. Default: " __stringify(LOOP_DEFAULT_HW_Q_DEPTH));
+MODULE_DESCRIPTION("Loopback device support");
MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(LOOP_MAJOR);
@@ -1818,7 +1868,7 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
cmd->use_aio = false;
break;
default:
- cmd->use_aio = lo->use_dio;
+ cmd->use_aio = lo->lo_flags & LO_FLAGS_DIRECT_IO;
break;
}
@@ -1844,6 +1894,8 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
static void loop_handle_cmd(struct loop_cmd *cmd)
{
+ struct cgroup_subsys_state *cmd_blkcg_css = cmd->blkcg_css;
+ struct cgroup_subsys_state *cmd_memcg_css = cmd->memcg_css;
struct request *rq = blk_mq_rq_from_pdu(cmd);
const bool write = op_is_write(req_op(rq));
struct loop_device *lo = rq->q->queuedata;
@@ -1855,24 +1907,34 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
goto failed;
}
- if (cmd->blkcg_css)
- kthread_associate_blkcg(cmd->blkcg_css);
- if (cmd->memcg_css)
+ /* We can block in this context, so ignore REQ_NOWAIT. */
+ if (rq->cmd_flags & REQ_NOWAIT)
+ rq->cmd_flags &= ~REQ_NOWAIT;
+
+ if (cmd_blkcg_css)
+ kthread_associate_blkcg(cmd_blkcg_css);
+ if (cmd_memcg_css)
old_memcg = set_active_memcg(
- mem_cgroup_from_css(cmd->memcg_css));
+ mem_cgroup_from_css(cmd_memcg_css));
+ /*
+ * do_req_filebacked() may call blk_mq_complete_request() synchronously
+ * or asynchronously if using aio. Hence, do not touch 'cmd' after
+ * do_req_filebacked() has returned unless we are sure that 'cmd' has
+ * not yet been completed.
+ */
ret = do_req_filebacked(lo, rq);
- if (cmd->blkcg_css)
+ if (cmd_blkcg_css)
kthread_associate_blkcg(NULL);
- if (cmd->memcg_css) {
+ if (cmd_memcg_css) {
set_active_memcg(old_memcg);
- css_put(cmd->memcg_css);
+ css_put(cmd_memcg_css);
}
failed:
/* complete non-aio request */
- if (!cmd->use_aio || ret) {
+ if (ret != -EIOCBQUEUED) {
if (ret == -EOPNOTSUPP)
cmd->ret = ret;
else
@@ -1937,6 +1999,12 @@ static const struct blk_mq_ops loop_mq_ops = {
static int loop_add(int i)
{
+ struct queue_limits lim = {
+ /*
+ * Random number picked from the historic block max_sectors cap.
+ */
+ .max_hw_sectors = 2560u,
+ };
struct loop_device *lo;
struct gendisk *disk;
int err;
@@ -1972,31 +2040,20 @@ static int loop_add(int i)
lo->tag_set.queue_depth = hw_queue_depth;
lo->tag_set.numa_node = NUMA_NO_NODE;
lo->tag_set.cmd_size = sizeof(struct loop_cmd);
- lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_STACKING |
- BLK_MQ_F_NO_SCHED_BY_DEFAULT;
+ lo->tag_set.flags = BLK_MQ_F_STACKING | BLK_MQ_F_NO_SCHED_BY_DEFAULT;
lo->tag_set.driver_data = lo;
err = blk_mq_alloc_tag_set(&lo->tag_set);
if (err)
goto out_free_idr;
- disk = lo->lo_disk = blk_mq_alloc_disk(&lo->tag_set, lo);
+ disk = lo->lo_disk = blk_mq_alloc_disk(&lo->tag_set, &lim, lo);
if (IS_ERR(disk)) {
err = PTR_ERR(disk);
goto out_cleanup_tags;
}
lo->lo_queue = lo->lo_disk->queue;
- blk_queue_max_hw_sectors(lo->lo_queue, BLK_DEF_MAX_SECTORS);
-
- /*
- * By default, we do buffer IO, so it doesn't make sense to enable
- * merge because the I/O submitted to backing file is handled page by
- * page. For directio mode, merge does help to dispatch bigger request
- * to underlayer disk. We will enable merge once directio is enabled.
- */
- blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue);
-
/*
* Disable partition scanning by default. The in-kernel partition
* scanning can be requested individually per-device during its
@@ -2071,14 +2128,18 @@ static void loop_remove(struct loop_device *lo)
put_disk(lo->lo_disk);
}
+#ifdef CONFIG_BLOCK_LEGACY_AUTOLOAD
static void loop_probe(dev_t dev)
{
int idx = MINOR(dev) >> part_shift;
- if (max_loop && idx >= max_loop)
+ if (max_loop_specified && max_loop && idx >= max_loop)
return;
loop_add(idx);
}
+#else
+#define loop_probe NULL
+#endif /* !CONFIG_BLOCK_LEGACY_AUTOLOAD */
static int loop_control_remove(int idx)
{
@@ -2181,7 +2242,7 @@ MODULE_ALIAS("devname:loop-control");
static int __init loop_init(void)
{
- int i, nr;
+ int i;
int err;
part_shift = 0;
@@ -2209,19 +2270,6 @@ static int __init loop_init(void)
goto err_out;
}
- /*
- * If max_loop is specified, create that many devices upfront.
- * This also becomes a hard limit. If max_loop is not specified,
- * create CONFIG_BLK_DEV_LOOP_MIN_COUNT loop devices at module
- * init time. Loop devices can be requested on-demand with the
- * /dev/loop-control interface, or be instantiated by accessing
- * a 'dead' device node.
- */
- if (max_loop)
- nr = max_loop;
- else
- nr = CONFIG_BLK_DEV_LOOP_MIN_COUNT;
-
err = misc_register(&loop_misc);
if (err < 0)
goto err_out;
@@ -2233,7 +2281,7 @@ static int __init loop_init(void)
}
/* pre-create number of devices given by config or max_loop */
- for (i = 0; i < nr; i++)
+ for (i = 0; i < max_loop; i++)
loop_add(i);
printk(KERN_INFO "loop: module loaded\n");
@@ -2272,6 +2320,9 @@ module_exit(loop_exit);
static int __init max_loop_setup(char *str)
{
max_loop = simple_strtol(str, NULL, 0);
+#ifdef CONFIG_BLOCK_LEGACY_AUTOLOAD
+ max_loop_specified = true;
+#endif
return 1;
}
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 562725d222a7..567192e371a8 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -1397,15 +1397,15 @@ static void mtip_dump_identify(struct mtip_port *port)
if (!port->identify_valid)
return;
- strlcpy(cbuf, (char *)(port->identify+10), 21);
+ strscpy(cbuf, (char *)(port->identify + 10), 21);
dev_info(&port->dd->pdev->dev,
"Serial No.: %s\n", cbuf);
- strlcpy(cbuf, (char *)(port->identify+23), 9);
+ strscpy(cbuf, (char *)(port->identify + 23), 9);
dev_info(&port->dd->pdev->dev,
"Firmware Ver.: %s\n", cbuf);
- strlcpy(cbuf, (char *)(port->identify+27), 41);
+ strscpy(cbuf, (char *)(port->identify + 27), 41);
dev_info(&port->dd->pdev->dev, "Model: %s\n", cbuf);
dev_info(&port->dd->pdev->dev, "Security: %04x %s\n",
@@ -1421,13 +1421,13 @@ static void mtip_dump_identify(struct mtip_port *port)
pci_read_config_word(port->dd->pdev, PCI_REVISION_ID, &revid);
switch (revid & 0xFF) {
case 0x1:
- strlcpy(cbuf, "A0", 3);
+ strscpy(cbuf, "A0", 3);
break;
case 0x3:
- strlcpy(cbuf, "A2", 3);
+ strscpy(cbuf, "A2", 3);
break;
default:
- strlcpy(cbuf, "?", 2);
+ strscpy(cbuf, "?", 2);
break;
}
dev_info(&port->dd->pdev->dev,
@@ -2040,11 +2040,12 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
* @dir Direction (read or write)
*
* return value
- * None
+ * 0 The IO completed successfully.
+ * -ENOMEM The DMA mapping failed.
*/
-static void mtip_hw_submit_io(struct driver_data *dd, struct request *rq,
- struct mtip_cmd *command,
- struct blk_mq_hw_ctx *hctx)
+static int mtip_hw_submit_io(struct driver_data *dd, struct request *rq,
+ struct mtip_cmd *command,
+ struct blk_mq_hw_ctx *hctx)
{
struct mtip_cmd_hdr *hdr =
dd->port->command_list + sizeof(struct mtip_cmd_hdr) * rq->tag;
@@ -2056,12 +2057,14 @@ static void mtip_hw_submit_io(struct driver_data *dd, struct request *rq,
unsigned int nents;
/* Map the scatter list for DMA access */
- nents = blk_rq_map_sg(hctx->queue, rq, command->sg);
- nents = dma_map_sg(&dd->pdev->dev, command->sg, nents, dma_dir);
+ command->scatter_ents = blk_rq_map_sg(rq, command->sg);
+ nents = dma_map_sg(&dd->pdev->dev, command->sg,
+ command->scatter_ents, dma_dir);
+ if (!nents)
+ return -ENOMEM;
- prefetch(&port->flags);
- command->scatter_ents = nents;
+ prefetch(&port->flags);
/*
* The number of retries for this command before it is
@@ -2112,11 +2115,13 @@ static void mtip_hw_submit_io(struct driver_data *dd, struct request *rq,
if (unlikely(port->flags & MTIP_PF_PAUSE_IO)) {
set_bit(rq->tag, port->cmds_to_issue);
set_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags);
- return;
+ return 0;
}
/* Issue the command to the hardware */
mtip_issue_ncq_command(port, rq->tag);
+
+ return 0;
}
/*
@@ -2259,35 +2264,20 @@ static const struct file_operations mtip_regs_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = mtip_hw_read_registers,
- .llseek = no_llseek,
};
static const struct file_operations mtip_flags_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = mtip_hw_read_flags,
- .llseek = no_llseek,
};
-static int mtip_hw_debugfs_init(struct driver_data *dd)
+static void mtip_hw_debugfs_init(struct driver_data *dd)
{
- if (!dfs_parent)
- return -1;
-
dd->dfs_node = debugfs_create_dir(dd->disk->disk_name, dfs_parent);
- if (IS_ERR_OR_NULL(dd->dfs_node)) {
- dev_warn(&dd->pdev->dev,
- "Error creating node %s under debugfs\n",
- dd->disk->disk_name);
- dd->dfs_node = NULL;
- return -1;
- }
-
debugfs_create_file("flags", 0444, dd->dfs_node, dd, &mtip_flags_fops);
debugfs_create_file("registers", 0444, dd->dfs_node, dd,
&mtip_regs_fops);
-
- return 0;
}
static void mtip_hw_debugfs_exit(struct driver_data *dd)
@@ -2716,7 +2706,12 @@ static int mtip_hw_init(struct driver_data *dd)
int rv;
unsigned long timeout, timetaken;
- dd->mmio = pcim_iomap_table(dd->pdev)[MTIP_ABAR];
+ dd->mmio = pcim_iomap_region(dd->pdev, MTIP_ABAR, MTIP_DRV_NAME);
+ if (IS_ERR(dd->mmio)) {
+ dev_err(&dd->pdev->dev, "Unable to request / ioremap PCI region\n");
+ return PTR_ERR(dd->mmio);
+ }
+
mtip_detect_product(dd);
if (dd->product_type == MTIP_PRODUCT_UNKNOWN) {
@@ -3041,7 +3036,7 @@ static int rssd_disk_name_format(char *prefix,
* structure pointer.
*/
static int mtip_block_ioctl(struct block_device *dev,
- fmode_t mode,
+ blk_mode_t mode,
unsigned cmd,
unsigned long arg)
{
@@ -3079,7 +3074,7 @@ static int mtip_block_ioctl(struct block_device *dev,
* structure pointer.
*/
static int mtip_block_compat_ioctl(struct block_device *dev,
- fmode_t mode,
+ blk_mode_t mode,
unsigned cmd,
unsigned long arg)
{
@@ -3153,17 +3148,17 @@ static int mtip_block_compat_ioctl(struct block_device *dev,
* that each partition is also 4KB aligned. Non-aligned partitions adversely
* affects performance.
*
- * @dev Pointer to the block_device strucutre.
+ * @disk Pointer to the gendisk strucutre.
* @geo Pointer to a hd_geometry structure.
*
* return value
* 0 Operation completed successfully.
* -ENOTTY An error occurred while reading the drive capacity.
*/
-static int mtip_block_getgeo(struct block_device *dev,
+static int mtip_block_getgeo(struct gendisk *disk,
struct hd_geometry *geo)
{
- struct driver_data *dd = dev->bd_disk->private_data;
+ struct driver_data *dd = disk->private_data;
sector_t capacity;
if (!dd)
@@ -3325,7 +3320,9 @@ static blk_status_t mtip_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(rq);
- mtip_hw_submit_io(dd, rq, cmd, hctx);
+ if (mtip_hw_submit_io(dd, rq, cmd, hctx))
+ return BLK_STS_IOERR;
+
return BLK_STS_OK;
}
@@ -3401,6 +3398,12 @@ static const struct blk_mq_ops mtip_mq_ops = {
*/
static int mtip_block_initialize(struct driver_data *dd)
{
+ struct queue_limits lim = {
+ .physical_block_size = 4096,
+ .max_hw_sectors = 0xffff,
+ .max_segments = MTIP_MAX_SG,
+ .max_segment_size = 0x400000,
+ };
int rv = 0, wait_for_rebuild = 0;
sector_t capacity;
unsigned int index = 0;
@@ -3420,7 +3423,6 @@ static int mtip_block_initialize(struct driver_data *dd)
dd->tags.reserved_tags = 1;
dd->tags.cmd_size = sizeof(struct mtip_cmd);
dd->tags.numa_node = dd->numa_node;
- dd->tags.flags = BLK_MQ_F_SHOULD_MERGE;
dd->tags.driver_data = dd;
dd->tags.timeout = MTIP_NCQ_CMD_TIMEOUT_MS;
@@ -3431,7 +3433,7 @@ static int mtip_block_initialize(struct driver_data *dd)
goto block_queue_alloc_tag_error;
}
- dd->disk = blk_mq_alloc_disk(&dd->tags, dd);
+ dd->disk = blk_mq_alloc_disk(&dd->tags, &lim, dd);
if (IS_ERR(dd->disk)) {
dev_err(&dd->pdev->dev,
"Unable to allocate request queue\n");
@@ -3479,14 +3481,7 @@ skip_create_disk:
goto start_service_thread;
/* Set device limits. */
- blk_queue_flag_set(QUEUE_FLAG_NONROT, dd->queue);
- blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, dd->queue);
- blk_queue_max_segments(dd->queue, MTIP_MAX_SG);
- blk_queue_physical_block_size(dd->queue, 4096);
- blk_queue_max_hw_sectors(dd->queue, 0xffff);
- blk_queue_max_segment_size(dd->queue, 0x400000);
dma_set_max_seg_size(&dd->pdev->dev, 0x400000);
- blk_queue_io_min(dd->queue, 4096);
/* Set the capacity of the device in 512 byte sectors. */
if (!(mtip_hw_get_capacity(dd, &capacity))) {
@@ -3726,17 +3721,10 @@ static int mtip_pci_probe(struct pci_dev *pdev,
goto iomap_err;
}
- /* Map BAR5 to memory. */
- rv = pcim_iomap_regions(pdev, 1 << MTIP_ABAR, MTIP_DRV_NAME);
- if (rv < 0) {
- dev_err(&pdev->dev, "Unable to map regions\n");
- goto iomap_err;
- }
-
rv = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rv) {
dev_warn(&pdev->dev, "64-bit DMA enable failed\n");
- goto setmask_err;
+ goto iomap_err;
}
/* Copy the info we may need later into the private data structure. */
@@ -3752,7 +3740,7 @@ static int mtip_pci_probe(struct pci_dev *pdev,
if (!dd->isr_workq) {
dev_warn(&pdev->dev, "Can't create wq %d\n", dd->instance);
rv = -ENOMEM;
- goto setmask_err;
+ goto iomap_err;
}
memset(cpu_list, 0, sizeof(cpu_list));
@@ -3849,8 +3837,6 @@ msi_initialize_err:
drop_cpu(dd->work[1].cpu_binding);
drop_cpu(dd->work[2].cpu_binding);
}
-setmask_err:
- pcim_iounmap_regions(pdev, 1 << MTIP_ABAR);
iomap_err:
kfree(dd);
@@ -3926,7 +3912,6 @@ static void mtip_pci_remove(struct pci_dev *pdev)
pci_disable_msi(pdev);
- pcim_iounmap_regions(pdev, 1 << MTIP_ABAR);
pci_set_drvdata(pdev, NULL);
put_disk(dd->disk);
@@ -4044,10 +4029,6 @@ static int __init mtip_init(void)
mtip_major = error;
dfs_parent = debugfs_create_dir("rssd", NULL);
- if (IS_ERR_OR_NULL(dfs_parent)) {
- pr_warn("Error creating debugfs parent\n");
- dfs_parent = NULL;
- }
/* Register our PCI operations. */
error = pci_register_driver(&mtip_pci_driver);
diff --git a/drivers/block/n64cart.c b/drivers/block/n64cart.c
index d914156db2d8..b9fdeff31caf 100644
--- a/drivers/block/n64cart.c
+++ b/drivers/block/n64cart.c
@@ -114,6 +114,10 @@ static const struct block_device_operations n64cart_fops = {
*/
static int __init n64cart_probe(struct platform_device *pdev)
{
+ struct queue_limits lim = {
+ .physical_block_size = 4096,
+ .logical_block_size = 4096,
+ };
struct gendisk *disk;
int err = -ENOMEM;
@@ -131,9 +135,11 @@ static int __init n64cart_probe(struct platform_device *pdev)
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
- disk = blk_alloc_disk(NUMA_NO_NODE);
- if (!disk)
+ disk = blk_alloc_disk(&lim, NUMA_NO_NODE);
+ if (IS_ERR(disk)) {
+ err = PTR_ERR(disk);
goto out;
+ }
disk->first_minor = 0;
disk->flags = GENHD_FL_NO_PART;
@@ -144,10 +150,6 @@ static int __init n64cart_probe(struct platform_device *pdev)
set_capacity(disk, size >> SECTOR_SHIFT);
set_disk_ro(disk, 1);
- blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue);
- blk_queue_physical_block_size(disk->queue, 4096);
- blk_queue_logical_block_size(disk->queue, 4096);
-
err = add_disk(disk);
if (err)
goto out_cleanup_disk;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 6cec9ce23fd3..f6c33b21f69e 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -62,11 +62,13 @@ struct nbd_sock {
bool dead;
int fallback_index;
int cookie;
+ struct work_struct work;
};
struct recv_thread_args {
struct work_struct work;
struct nbd_device *nbd;
+ struct nbd_sock *nsock;
int index;
};
@@ -140,6 +142,9 @@ struct nbd_device {
*/
#define NBD_CMD_INFLIGHT 2
+/* Just part of request header or data payload is sent successfully */
+#define NBD_CMD_PARTIAL_SEND 3
+
struct nbd_cmd {
struct nbd_device *nbd;
struct mutex lock;
@@ -157,8 +162,6 @@ static struct dentry *nbd_dbg_dir;
#define nbd_name(nbd) ((nbd)->disk->disk_name)
-#define NBD_MAGIC 0x68797548
-
#define NBD_DEF_BLKSIZE_BITS 10
static unsigned int nbds_max = 16;
@@ -182,6 +185,17 @@ static void nbd_requeue_cmd(struct nbd_cmd *cmd)
{
struct request *req = blk_mq_rq_from_pdu(cmd);
+ lockdep_assert_held(&cmd->lock);
+
+ /*
+ * Clear INFLIGHT flag so that this cmd won't be completed in
+ * normal completion path
+ *
+ * INFLIGHT flag will be set when the cmd is queued to nbd next
+ * time.
+ */
+ __clear_bit(NBD_CMD_INFLIGHT, &cmd->flags);
+
if (!test_and_set_bit(NBD_CMD_REQUEUED, &cmd->flags))
blk_mq_requeue_request(req, true);
}
@@ -223,7 +237,7 @@ static ssize_t pid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gendisk *disk = dev_to_disk(dev);
- struct nbd_device *nbd = (struct nbd_device *)disk->private_data;
+ struct nbd_device *nbd = disk->private_data;
return sprintf(buf, "%d\n", nbd->pid);
}
@@ -237,7 +251,7 @@ static ssize_t backend_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gendisk *disk = dev_to_disk(dev);
- struct nbd_device *nbd = (struct nbd_device *)disk->private_data;
+ struct nbd_device *nbd = disk->private_data;
return sprintf(buf, "%s\n", nbd->backend ?: "");
}
@@ -252,7 +266,6 @@ static void nbd_dev_remove(struct nbd_device *nbd)
struct gendisk *disk = nbd->disk;
del_gendisk(disk);
- put_disk(disk);
blk_mq_free_tag_set(&nbd->tag_set);
/*
@@ -263,7 +276,7 @@ static void nbd_dev_remove(struct nbd_device *nbd)
idr_remove(&nbd_index_idr, nbd->index);
mutex_unlock(&nbd_index_mutex);
destroy_workqueue(nbd->recv_workq);
- kfree(nbd);
+ put_disk(disk);
}
static void nbd_dev_remove_work(struct work_struct *work)
@@ -298,7 +311,7 @@ static void nbd_mark_nsock_dead(struct nbd_device *nbd, struct nbd_sock *nsock,
if (args) {
INIT_WORK(&args->work, nbd_dead_link_work);
args->index = nbd->index;
- queue_work(system_wq, &args->work);
+ queue_work(system_percpu_wq, &args->work);
}
}
if (!nsock->dead) {
@@ -318,27 +331,49 @@ static void nbd_mark_nsock_dead(struct nbd_device *nbd, struct nbd_sock *nsock,
nsock->sent = 0;
}
-static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize,
- loff_t blksize)
+static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, loff_t blksize)
{
+ struct queue_limits lim;
+ int error;
+
if (!blksize)
blksize = 1u << NBD_DEF_BLKSIZE_BITS;
if (blk_validate_block_size(blksize))
return -EINVAL;
+ if (bytesize < 0)
+ return -EINVAL;
+
nbd->config->bytesize = bytesize;
nbd->config->blksize_bits = __ffs(blksize);
if (!nbd->pid)
return 0;
- if (nbd->config->flags & NBD_FLAG_SEND_TRIM) {
- nbd->disk->queue->limits.discard_granularity = blksize;
- blk_queue_max_discard_sectors(nbd->disk->queue, UINT_MAX);
- }
- blk_queue_logical_block_size(nbd->disk->queue, blksize);
- blk_queue_physical_block_size(nbd->disk->queue, blksize);
+ lim = queue_limits_start_update(nbd->disk->queue);
+ if (nbd->config->flags & NBD_FLAG_SEND_TRIM)
+ lim.max_hw_discard_sectors = UINT_MAX >> SECTOR_SHIFT;
+ else
+ lim.max_hw_discard_sectors = 0;
+ if (!(nbd->config->flags & NBD_FLAG_SEND_FLUSH)) {
+ lim.features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA);
+ } else if (nbd->config->flags & NBD_FLAG_SEND_FUA) {
+ lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA;
+ } else {
+ lim.features |= BLK_FEAT_WRITE_CACHE;
+ lim.features &= ~BLK_FEAT_FUA;
+ }
+ if (nbd->config->flags & NBD_FLAG_ROTATIONAL)
+ lim.features |= BLK_FEAT_ROTATIONAL;
+ if (nbd->config->flags & NBD_FLAG_SEND_WRITE_ZEROES)
+ lim.max_write_zeroes_sectors = UINT_MAX >> SECTOR_SHIFT;
+
+ lim.logical_block_size = blksize;
+ lim.physical_block_size = blksize;
+ error = queue_limits_commit_update_frozen(nbd->disk->queue, &lim);
+ if (error)
+ return error;
if (max_part)
set_bit(GD_NEED_PART_SCAN, &nbd->disk->state);
@@ -390,11 +425,29 @@ static u32 req_to_nbd_cmd_type(struct request *req)
return NBD_CMD_WRITE;
case REQ_OP_READ:
return NBD_CMD_READ;
+ case REQ_OP_WRITE_ZEROES:
+ return NBD_CMD_WRITE_ZEROES;
default:
return U32_MAX;
}
}
+static struct nbd_config *nbd_get_config_unlocked(struct nbd_device *nbd)
+{
+ if (refcount_inc_not_zero(&nbd->config_refs)) {
+ /*
+ * Add smp_mb__after_atomic to ensure that reading nbd->config_refs
+ * and reading nbd->config is ordered. The pair is the barrier in
+ * nbd_alloc_and_init_config(), avoid nbd->config_refs is set
+ * before nbd->config.
+ */
+ smp_mb__after_atomic();
+ return nbd->config;
+ }
+
+ return NULL;
+}
+
static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req)
{
struct nbd_cmd *cmd = blk_mq_rq_to_pdu(req);
@@ -404,18 +457,24 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req)
if (!mutex_trylock(&cmd->lock))
return BLK_EH_RESET_TIMER;
+ /* partial send is handled in nbd_sock's work function */
+ if (test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags)) {
+ mutex_unlock(&cmd->lock);
+ return BLK_EH_RESET_TIMER;
+ }
+
if (!test_bit(NBD_CMD_INFLIGHT, &cmd->flags)) {
mutex_unlock(&cmd->lock);
return BLK_EH_DONE;
}
- if (!refcount_inc_not_zero(&nbd->config_refs)) {
+ config = nbd_get_config_unlocked(nbd);
+ if (!config) {
cmd->status = BLK_STS_TIMEOUT;
__clear_bit(NBD_CMD_INFLIGHT, &cmd->flags);
mutex_unlock(&cmd->lock);
goto done;
}
- config = nbd->config;
if (config->num_connections > 1 ||
(config->num_connections == 1 && nbd->tag_set.timeout)) {
@@ -444,8 +503,8 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req)
nbd_mark_nsock_dead(nbd, nsock, 1);
mutex_unlock(&nsock->tx_lock);
}
- mutex_unlock(&cmd->lock);
nbd_requeue_cmd(cmd);
+ mutex_unlock(&cmd->lock);
nbd_config_put(nbd);
return BLK_EH_DONE;
}
@@ -489,17 +548,11 @@ done:
return BLK_EH_DONE;
}
-/*
- * Send or receive packet. Return a positive value on success and
- * negtive value on failue, and never return 0.
- */
-static int sock_xmit(struct nbd_device *nbd, int index, int send,
- struct iov_iter *iter, int msg_flags, int *sent)
+static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send,
+ struct iov_iter *iter, int msg_flags, int *sent)
{
- struct nbd_config *config = nbd->config;
- struct socket *sock = config->socks[index]->sock;
int result;
- struct msghdr msg;
+ struct msghdr msg = {} ;
unsigned int noreclaim_flag;
if (unlikely(!sock)) {
@@ -512,27 +565,27 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send,
msg.msg_iter = *iter;
noreclaim_flag = memalloc_noreclaim_save();
- do {
- sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = msg_flags | MSG_NOSIGNAL;
-
- if (send)
- result = sock_sendmsg(sock, &msg);
- else
- result = sock_recvmsg(sock, &msg, msg.msg_flags);
-
- if (result <= 0) {
- if (result == 0)
- result = -EPIPE; /* short read */
- break;
- }
- if (sent)
- *sent += result;
- } while (msg_data_left(&msg));
+
+ scoped_with_kernel_creds() {
+ do {
+ sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
+ sock->sk->sk_use_task_frag = false;
+ msg.msg_flags = msg_flags | MSG_NOSIGNAL;
+
+ if (send)
+ result = sock_sendmsg(sock, &msg);
+ else
+ result = sock_recvmsg(sock, &msg, msg.msg_flags);
+
+ if (result <= 0) {
+ if (result == 0)
+ result = -EPIPE; /* short read */
+ break;
+ }
+ if (sent)
+ *sent += result;
+ } while (msg_data_left(&msg));
+ }
memalloc_noreclaim_restore(noreclaim_flag);
@@ -540,6 +593,19 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send,
}
/*
+ * Send or receive packet. Return a positive value on success and
+ * negtive value on failure, and never return 0.
+ */
+static int sock_xmit(struct nbd_device *nbd, int index, int send,
+ struct iov_iter *iter, int msg_flags, int *sent)
+{
+ struct nbd_config *config = nbd->config;
+ struct socket *sock = config->socks[index]->sock;
+
+ return __sock_xmit(nbd, sock, send, iter, msg_flags, sent);
+}
+
+/*
* Different settings for sk->sk_sndtimeo can result in different return values
* if there is a signal pending when we enter sendmsg, because reasons?
*/
@@ -548,8 +614,36 @@ static inline int was_interrupted(int result)
return result == -ERESTARTSYS || result == -EINTR;
}
-/* always call with the tx_lock held */
-static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index)
+/*
+ * We've already sent header or part of data payload, have no choice but
+ * to set pending and schedule it in work.
+ *
+ * And we have to return BLK_STS_OK to block core, otherwise this same
+ * request may be re-dispatched with different tag, but our header has
+ * been sent out with old tag, and this way does confuse reply handling.
+ */
+static void nbd_sched_pending_work(struct nbd_device *nbd,
+ struct nbd_sock *nsock,
+ struct nbd_cmd *cmd, int sent)
+{
+ struct request *req = blk_mq_rq_from_pdu(cmd);
+
+ /* pending work should be scheduled only once */
+ WARN_ON_ONCE(test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags));
+
+ nsock->pending = req;
+ nsock->sent = sent;
+ set_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags);
+ refcount_inc(&nbd->config_refs);
+ schedule_work(&nsock->work);
+}
+
+/*
+ * Returns BLK_STS_RESOURCE if the caller should retry after a delay.
+ * Returns BLK_STS_IOERR if sending failed.
+ */
+static blk_status_t nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd,
+ int index)
{
struct request *req = blk_mq_rq_from_pdu(cmd);
struct nbd_config *config = nbd->config;
@@ -558,28 +652,32 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index)
struct nbd_request request = {.magic = htonl(NBD_REQUEST_MAGIC)};
struct kvec iov = {.iov_base = &request, .iov_len = sizeof(request)};
struct iov_iter from;
- unsigned long size = blk_rq_bytes(req);
struct bio *bio;
u64 handle;
u32 type;
u32 nbd_cmd_flags = 0;
int sent = nsock->sent, skip = 0;
- iov_iter_kvec(&from, WRITE, &iov, 1, sizeof(request));
+ lockdep_assert_held(&cmd->lock);
+ lockdep_assert_held(&nsock->tx_lock);
+
+ iov_iter_kvec(&from, ITER_SOURCE, &iov, 1, sizeof(request));
type = req_to_nbd_cmd_type(req);
if (type == U32_MAX)
- return -EIO;
+ return BLK_STS_IOERR;
if (rq_data_dir(req) == WRITE &&
(config->flags & NBD_FLAG_READ_ONLY)) {
dev_err_ratelimited(disk_to_dev(nbd->disk),
"Write on read-only\n");
- return -EIO;
+ return BLK_STS_IOERR;
}
if (req->cmd_flags & REQ_FUA)
nbd_cmd_flags |= NBD_CMD_FLAG_FUA;
+ if ((req->cmd_flags & REQ_NOUNMAP) && (type == NBD_CMD_WRITE_ZEROES))
+ nbd_cmd_flags |= NBD_CMD_FLAG_NO_HOLE;
/* We did a partial send previously, and we at least sent the whole
* request struct, so just go and send the rest of the pages in the
@@ -604,10 +702,10 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index)
request.type = htonl(type | nbd_cmd_flags);
if (type != NBD_CMD_FLUSH) {
request.from = cpu_to_be64((u64)blk_rq_pos(req) << 9);
- request.len = htonl(size);
+ request.len = htonl(blk_rq_bytes(req));
}
handle = nbd_cmd_handle(cmd);
- memcpy(request.handle, &handle, sizeof(handle));
+ request.cookie = cpu_to_be64(handle);
trace_nbd_send_request(&request, nbd->index, blk_mq_rq_from_pdu(cmd));
@@ -619,21 +717,21 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index)
trace_nbd_header_sent(req, handle);
if (result < 0) {
if (was_interrupted(result)) {
- /* If we havne't sent anything we can just return BUSY,
+ /* If we haven't sent anything we can just return BUSY,
* however if we have sent something we need to make
* sure we only allow this req to be sent until we are
* completely done.
*/
if (sent) {
- nsock->pending = req;
- nsock->sent = sent;
+ nbd_sched_pending_work(nbd, nsock, cmd, sent);
+ return BLK_STS_OK;
}
set_bit(NBD_CMD_REQUEUED, &cmd->flags);
return BLK_STS_RESOURCE;
}
dev_err_ratelimited(disk_to_dev(nbd->disk),
"Send control failed (result %d)\n", result);
- return -EAGAIN;
+ goto requeue;
}
send_pages:
if (type != NBD_CMD_WRITE)
@@ -651,7 +749,7 @@ send_pages:
dev_dbg(nbd_to_dev(nbd), "request %p: sending %d bytes data\n",
req, bvec.bv_len);
- iov_iter_bvec(&from, WRITE, &bvec, 1, bvec.bv_len);
+ iov_iter_bvec(&from, ITER_SOURCE, &bvec, 1, bvec.bv_len);
if (skip) {
if (skip >= iov_iter_count(&from)) {
skip -= iov_iter_count(&from);
@@ -663,19 +761,13 @@ send_pages:
result = sock_xmit(nbd, index, 1, &from, flags, &sent);
if (result < 0) {
if (was_interrupted(result)) {
- /* We've already sent the header, we
- * have no choice but to set pending and
- * return BUSY.
- */
- nsock->pending = req;
- nsock->sent = sent;
- set_bit(NBD_CMD_REQUEUED, &cmd->flags);
- return BLK_STS_RESOURCE;
+ nbd_sched_pending_work(nbd, nsock, cmd, sent);
+ return BLK_STS_OK;
}
dev_err(disk_to_dev(nbd->disk),
"Send data failed (result %d)\n",
result);
- return -EAGAIN;
+ goto requeue;
}
/*
* The completion might already have come in,
@@ -692,10 +784,65 @@ out:
trace_nbd_payload_sent(req, handle);
nsock->pending = NULL;
nsock->sent = 0;
- return 0;
+ __set_bit(NBD_CMD_INFLIGHT, &cmd->flags);
+ return BLK_STS_OK;
+
+requeue:
+ /*
+ * Can't requeue in case we are dealing with partial send
+ *
+ * We must run from pending work function.
+ * */
+ if (test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags))
+ return BLK_STS_OK;
+
+ /* retry on a different socket */
+ dev_err_ratelimited(disk_to_dev(nbd->disk),
+ "Request send failed, requeueing\n");
+ nbd_mark_nsock_dead(nbd, nsock, 1);
+ nbd_requeue_cmd(cmd);
+ return BLK_STS_OK;
+}
+
+/* handle partial sending */
+static void nbd_pending_cmd_work(struct work_struct *work)
+{
+ struct nbd_sock *nsock = container_of(work, struct nbd_sock, work);
+ struct request *req = nsock->pending;
+ struct nbd_cmd *cmd = blk_mq_rq_to_pdu(req);
+ struct nbd_device *nbd = cmd->nbd;
+ unsigned long deadline = READ_ONCE(req->deadline);
+ unsigned int wait_ms = 2;
+
+ mutex_lock(&cmd->lock);
+
+ WARN_ON_ONCE(test_bit(NBD_CMD_REQUEUED, &cmd->flags));
+ if (WARN_ON_ONCE(!test_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags)))
+ goto out;
+
+ mutex_lock(&nsock->tx_lock);
+ while (true) {
+ nbd_send_cmd(nbd, cmd, cmd->index);
+ if (!nsock->pending)
+ break;
+
+ /* don't bother timeout handler for partial sending */
+ if (READ_ONCE(jiffies) + msecs_to_jiffies(wait_ms) >= deadline) {
+ cmd->status = BLK_STS_IOERR;
+ blk_mq_complete_request(req);
+ break;
+ }
+ msleep(wait_ms);
+ wait_ms *= 2;
+ }
+ mutex_unlock(&nsock->tx_lock);
+ clear_bit(NBD_CMD_PARTIAL_SEND, &cmd->flags);
+out:
+ mutex_unlock(&cmd->lock);
+ nbd_config_put(nbd);
}
-static int nbd_read_reply(struct nbd_device *nbd, int index,
+static int nbd_read_reply(struct nbd_device *nbd, struct socket *sock,
struct nbd_reply *reply)
{
struct kvec iov = {.iov_base = reply, .iov_len = sizeof(*reply)};
@@ -703,8 +850,8 @@ static int nbd_read_reply(struct nbd_device *nbd, int index,
int result;
reply->magic = 0;
- iov_iter_kvec(&to, READ, &iov, 1, sizeof(*reply));
- result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL);
+ iov_iter_kvec(&to, ITER_DEST, &iov, 1, sizeof(*reply));
+ result = __sock_xmit(nbd, sock, 0, &to, MSG_WAITALL, NULL);
if (result < 0) {
if (!nbd_disconnected(nbd->config))
dev_err(disk_to_dev(nbd->disk),
@@ -733,7 +880,7 @@ static struct nbd_cmd *nbd_handle_reply(struct nbd_device *nbd, int index,
u32 tag;
int ret = 0;
- memcpy(&handle, reply->handle, sizeof(handle));
+ handle = be64_to_cpu(reply->cookie);
tag = nbd_handle_to_tag(handle);
hwq = blk_mq_unique_tag_to_hwq(tag);
if (hwq < nbd->tag_set.nr_hw_queues)
@@ -792,7 +939,7 @@ static struct nbd_cmd *nbd_handle_reply(struct nbd_device *nbd, int index,
struct iov_iter to;
rq_for_each_segment(bvec, req, iter) {
- iov_iter_bvec(&to, READ, &bvec, 1, bvec.bv_len);
+ iov_iter_bvec(&to, ITER_DEST, &bvec, 1, bvec.bv_len);
result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL);
if (result < 0) {
dev_err(disk_to_dev(nbd->disk), "Receive data failed (result %d)\n",
@@ -828,14 +975,14 @@ static void recv_work(struct work_struct *work)
struct nbd_device *nbd = args->nbd;
struct nbd_config *config = nbd->config;
struct request_queue *q = nbd->disk->queue;
- struct nbd_sock *nsock;
+ struct nbd_sock *nsock = args->nsock;
struct nbd_cmd *cmd;
struct request *rq;
while (1) {
struct nbd_reply reply;
- if (nbd_read_reply(nbd, args->index, &reply))
+ if (nbd_read_reply(nbd, nsock->sock, &reply))
break;
/*
@@ -870,14 +1017,13 @@ static void recv_work(struct work_struct *work)
percpu_ref_put(&q->q_usage_counter);
}
- nsock = config->socks[args->index];
mutex_lock(&nsock->tx_lock);
nbd_mark_nsock_dead(nbd, nsock, 1);
mutex_unlock(&nsock->tx_lock);
- nbd_config_put(nbd);
atomic_dec(&config->recv_threads);
wake_up(&config->recv_wq);
+ nbd_config_put(nbd);
kfree(args);
}
@@ -968,26 +1114,28 @@ static int wait_for_reconnect(struct nbd_device *nbd)
return !test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags);
}
-static int nbd_handle_cmd(struct nbd_cmd *cmd, int index)
+static blk_status_t nbd_handle_cmd(struct nbd_cmd *cmd, int index)
{
struct request *req = blk_mq_rq_from_pdu(cmd);
struct nbd_device *nbd = cmd->nbd;
struct nbd_config *config;
struct nbd_sock *nsock;
- int ret;
+ blk_status_t ret;
- if (!refcount_inc_not_zero(&nbd->config_refs)) {
+ lockdep_assert_held(&cmd->lock);
+
+ config = nbd_get_config_unlocked(nbd);
+ if (!config) {
dev_err_ratelimited(disk_to_dev(nbd->disk),
"Socks array is empty\n");
- return -EINVAL;
+ return BLK_STS_IOERR;
}
- config = nbd->config;
if (index >= config->num_connections) {
dev_err_ratelimited(disk_to_dev(nbd->disk),
"Attempted send on invalid socket\n");
nbd_config_put(nbd);
- return -EINVAL;
+ return BLK_STS_IOERR;
}
cmd->status = BLK_STS_OK;
again:
@@ -1010,7 +1158,7 @@ again:
*/
sock_shutdown(nbd);
nbd_config_put(nbd);
- return -EIO;
+ return BLK_STS_IOERR;
}
goto again;
}
@@ -1023,27 +1171,10 @@ again:
blk_mq_start_request(req);
if (unlikely(nsock->pending && nsock->pending != req)) {
nbd_requeue_cmd(cmd);
- ret = 0;
+ ret = BLK_STS_OK;
goto out;
}
- /*
- * Some failures are related to the link going down, so anything that
- * returns EAGAIN can be retried on a different socket.
- */
ret = nbd_send_cmd(nbd, cmd, index);
- /*
- * Access to this flag is protected by cmd->lock, thus it's safe to set
- * the flag after nbd_send_cmd() succeed to send request to server.
- */
- if (!ret)
- __set_bit(NBD_CMD_INFLIGHT, &cmd->flags);
- else if (ret == -EAGAIN) {
- dev_err_ratelimited(disk_to_dev(nbd->disk),
- "Request send failed, requeueing\n");
- nbd_mark_nsock_dead(nbd, nsock, 1);
- nbd_requeue_cmd(cmd);
- ret = 0;
- }
out:
mutex_unlock(&nsock->tx_lock);
nbd_config_put(nbd);
@@ -1054,7 +1185,7 @@ static blk_status_t nbd_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
struct nbd_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
- int ret;
+ blk_status_t ret;
/*
* Since we look at the bio's to send the request over the network we
@@ -1074,10 +1205,6 @@ static blk_status_t nbd_queue_rq(struct blk_mq_hw_ctx *hctx,
* appropriate.
*/
ret = nbd_handle_cmd(cmd, hctx->queue_num);
- if (ret < 0)
- ret = BLK_STS_IOERR;
- else if (!ret)
- ret = BLK_STS_OK;
mutex_unlock(&cmd->lock);
return ret;
@@ -1093,6 +1220,14 @@ static struct socket *nbd_get_socket(struct nbd_device *nbd, unsigned long fd,
if (!sock)
return NULL;
+ if (!sk_is_tcp(sock->sk) &&
+ !sk_is_stream_unix(sock->sk)) {
+ dev_err(disk_to_dev(nbd->disk), "Unsupported socket: should be TCP or UNIX.\n");
+ *err = -EINVAL;
+ sockfd_put(sock);
+ return NULL;
+ }
+
if (sock->ops->shutdown == sock_no_shutdown) {
dev_err(disk_to_dev(nbd->disk), "Unsupported socket: shutdown callout must be supported.\n");
*err = -EINVAL;
@@ -1110,8 +1245,12 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
struct socket *sock;
struct nbd_sock **socks;
struct nbd_sock *nsock;
+ unsigned int memflags;
int err;
+ /* Arg will be cast to int, check it to avoid overflow */
+ if (arg > INT_MAX)
+ return -EINVAL;
sock = nbd_get_socket(nbd, arg, &err);
if (!sock)
return err;
@@ -1120,7 +1259,7 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
* We need to make sure we don't get any errant requests while we're
* reallocating the ->socks array.
*/
- blk_mq_freeze_queue(nbd->disk->queue);
+ memflags = blk_mq_freeze_queue(nbd->disk->queue);
if (!netlink && !nbd->task_setup &&
!test_bit(NBD_RT_BOUND, &config->runtime_flags))
@@ -1158,14 +1297,15 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
nsock->pending = NULL;
nsock->sent = 0;
nsock->cookie = 0;
+ INIT_WORK(&nsock->work, nbd_pending_cmd_work);
socks[config->num_connections++] = nsock;
atomic_inc(&config->live_connections);
- blk_mq_unfreeze_queue(nbd->disk->queue);
+ blk_mq_unfreeze_queue(nbd->disk->queue, memflags);
return 0;
put_socket:
- blk_mq_unfreeze_queue(nbd->disk->queue);
+ blk_mq_unfreeze_queue(nbd->disk->queue, memflags);
sockfd_put(sock);
return err;
}
@@ -1211,6 +1351,7 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
INIT_WORK(&args->work, recv_work);
args->index = i;
args->nbd = nbd;
+ args->nsock = nsock;
nsock->cookie++;
mutex_unlock(&nsock->tx_lock);
sockfd_put(old);
@@ -1240,19 +1381,10 @@ static void nbd_bdev_reset(struct nbd_device *nbd)
static void nbd_parse_flags(struct nbd_device *nbd)
{
- struct nbd_config *config = nbd->config;
- if (config->flags & NBD_FLAG_READ_ONLY)
+ if (nbd->config->flags & NBD_FLAG_READ_ONLY)
set_disk_ro(nbd->disk, true);
else
set_disk_ro(nbd->disk, false);
- if (config->flags & NBD_FLAG_SEND_FLUSH) {
- if (config->flags & NBD_FLAG_SEND_FUA)
- blk_queue_write_cache(nbd->disk->queue, true, true);
- else
- blk_queue_write_cache(nbd->disk->queue, true, false);
- }
- else
- blk_queue_write_cache(nbd->disk->queue, false, false);
}
static void send_disconnects(struct nbd_device *nbd)
@@ -1269,7 +1401,7 @@ static void send_disconnects(struct nbd_device *nbd)
for (i = 0; i < config->num_connections; i++) {
struct nbd_sock *nsock = config->socks[i];
- iov_iter_kvec(&from, WRITE, &iov, 1, sizeof(request));
+ iov_iter_kvec(&from, ITER_SOURCE, &iov, 1, sizeof(request));
mutex_lock(&nsock->tx_lock);
ret = sock_xmit(nbd, i, 1, &from, 0, NULL);
if (ret < 0)
@@ -1329,8 +1461,6 @@ static void nbd_config_put(struct nbd_device *nbd)
nbd->config = NULL;
nbd->tag_set.timeout = 0;
- nbd->disk->queue->limits.discard_granularity = 0;
- blk_queue_max_discard_sectors(nbd->disk->queue, 0);
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
@@ -1354,7 +1484,17 @@ static int nbd_start_device(struct nbd_device *nbd)
return -EINVAL;
}
- blk_mq_update_nr_hw_queues(&nbd->tag_set, config->num_connections);
+retry:
+ mutex_unlock(&nbd->config_lock);
+ blk_mq_update_nr_hw_queues(&nbd->tag_set, num_connections);
+ mutex_lock(&nbd->config_lock);
+
+ /* if another code path updated nr_hw_queues, retry until succeed */
+ if (num_connections != config->num_connections) {
+ num_connections = config->num_connections;
+ goto retry;
+ }
+
nbd->pid = task_pid_nr(current);
nbd_parse_flags(nbd);
@@ -1393,6 +1533,7 @@ static int nbd_start_device(struct nbd_device *nbd)
refcount_inc(&nbd->config_refs);
INIT_WORK(&args->work, recv_work);
args->nbd = nbd;
+ args->nsock = config->socks[i];
args->index = i;
queue_work(nbd->recv_workq, &args->work);
}
@@ -1413,10 +1554,12 @@ static int nbd_start_device_ioctl(struct nbd_device *nbd)
mutex_unlock(&nbd->config_lock);
ret = wait_event_interruptible(config->recv_wq,
atomic_read(&config->recv_threads) == 0);
- if (ret)
+ if (ret) {
sock_shutdown(nbd);
- flush_workqueue(nbd->recv_workq);
+ nbd_clear_que(nbd);
+ }
+ flush_workqueue(nbd->recv_workq);
mutex_lock(&nbd->config_lock);
nbd_bdev_reset(nbd);
/* user requested, ignore socket errors */
@@ -1427,11 +1570,10 @@ static int nbd_start_device_ioctl(struct nbd_device *nbd)
return ret;
}
-static void nbd_clear_sock_ioctl(struct nbd_device *nbd,
- struct block_device *bdev)
+static void nbd_clear_sock_ioctl(struct nbd_device *nbd)
{
nbd_clear_sock(nbd);
- __invalidate_device(bdev, true);
+ disk_force_media_change(nbd->disk);
nbd_bdev_reset(nbd);
if (test_and_clear_bit(NBD_RT_HAS_CONFIG_REF,
&nbd->config->runtime_flags))
@@ -1458,7 +1600,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
case NBD_DISCONNECT:
return nbd_disconnect(nbd);
case NBD_CLEAR_SOCK:
- nbd_clear_sock_ioctl(nbd, bdev);
+ nbd_clear_sock_ioctl(nbd);
return 0;
case NBD_SET_SOCK:
return nbd_add_socket(nbd, arg, false);
@@ -1495,7 +1637,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
return -ENOTTY;
}
-static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
+static int nbd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg)
{
struct nbd_device *nbd = bdev->bd_disk->private_data;
@@ -1525,17 +1667,20 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
return error;
}
-static struct nbd_config *nbd_alloc_config(void)
+static int nbd_alloc_and_init_config(struct nbd_device *nbd)
{
struct nbd_config *config;
+ if (WARN_ON(nbd->config))
+ return -EINVAL;
+
if (!try_module_get(THIS_MODULE))
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
config = kzalloc(sizeof(struct nbd_config), GFP_NOFS);
if (!config) {
module_put(THIS_MODULE);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
atomic_set(&config->recv_threads, 0);
@@ -1543,16 +1688,28 @@ static struct nbd_config *nbd_alloc_config(void)
init_waitqueue_head(&config->conn_wait);
config->blksize_bits = NBD_DEF_BLKSIZE_BITS;
atomic_set(&config->live_connections, 0);
- return config;
+
+ nbd->config = config;
+ /*
+ * Order refcount_set(&nbd->config_refs, 1) and nbd->config assignment,
+ * its pair is the barrier in nbd_get_config_unlocked().
+ * So nbd_get_config_unlocked() won't see nbd->config as null after
+ * refcount_inc_not_zero() succeed.
+ */
+ smp_mb__before_atomic();
+ refcount_set(&nbd->config_refs, 1);
+
+ return 0;
}
-static int nbd_open(struct block_device *bdev, fmode_t mode)
+static int nbd_open(struct gendisk *disk, blk_mode_t mode)
{
struct nbd_device *nbd;
+ struct nbd_config *config;
int ret = 0;
mutex_lock(&nbd_index_mutex);
- nbd = bdev->bd_disk->private_data;
+ nbd = disk->private_data;
if (!nbd) {
ret = -ENXIO;
goto out;
@@ -1561,36 +1718,34 @@ static int nbd_open(struct block_device *bdev, fmode_t mode)
ret = -ENXIO;
goto out;
}
- if (!refcount_inc_not_zero(&nbd->config_refs)) {
- struct nbd_config *config;
+ config = nbd_get_config_unlocked(nbd);
+ if (!config) {
mutex_lock(&nbd->config_lock);
if (refcount_inc_not_zero(&nbd->config_refs)) {
mutex_unlock(&nbd->config_lock);
goto out;
}
- config = nbd_alloc_config();
- if (IS_ERR(config)) {
- ret = PTR_ERR(config);
+ ret = nbd_alloc_and_init_config(nbd);
+ if (ret) {
mutex_unlock(&nbd->config_lock);
goto out;
}
- nbd->config = config;
- refcount_set(&nbd->config_refs, 1);
+
refcount_inc(&nbd->refs);
mutex_unlock(&nbd->config_lock);
if (max_part)
- set_bit(GD_NEED_PART_SCAN, &bdev->bd_disk->state);
- } else if (nbd_disconnected(nbd->config)) {
+ set_bit(GD_NEED_PART_SCAN, &disk->state);
+ } else if (nbd_disconnected(config)) {
if (max_part)
- set_bit(GD_NEED_PART_SCAN, &bdev->bd_disk->state);
+ set_bit(GD_NEED_PART_SCAN, &disk->state);
}
out:
mutex_unlock(&nbd_index_mutex);
return ret;
}
-static void nbd_release(struct gendisk *disk, fmode_t mode)
+static void nbd_release(struct gendisk *disk)
{
struct nbd_device *nbd = disk->private_data;
@@ -1602,6 +1757,13 @@ static void nbd_release(struct gendisk *disk, fmode_t mode)
nbd_put(nbd);
}
+static void nbd_free_disk(struct gendisk *disk)
+{
+ struct nbd_device *nbd = disk->private_data;
+
+ kfree(nbd);
+}
+
static const struct block_device_operations nbd_fops =
{
.owner = THIS_MODULE,
@@ -1609,6 +1771,7 @@ static const struct block_device_operations nbd_fops =
.release = nbd_release,
.ioctl = nbd_ioctl,
.compat_ioctl = nbd_ioctl,
+ .free_disk = nbd_free_disk,
};
#if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -1644,6 +1807,10 @@ static int nbd_dbg_flags_show(struct seq_file *s, void *unused)
seq_puts(s, "NBD_FLAG_SEND_FUA\n");
if (flags & NBD_FLAG_SEND_TRIM)
seq_puts(s, "NBD_FLAG_SEND_TRIM\n");
+ if (flags & NBD_FLAG_SEND_WRITE_ZEROES)
+ seq_puts(s, "NBD_FLAG_SEND_WRITE_ZEROES\n");
+ if (flags & NBD_FLAG_ROTATIONAL)
+ seq_puts(s, "NBD_FLAG_ROTATIONAL\n");
return 0;
}
@@ -1659,7 +1826,7 @@ static int nbd_dev_dbg_init(struct nbd_device *nbd)
return -EIO;
dir = debugfs_create_dir(nbd_name(nbd), nbd_dbg_dir);
- if (!dir) {
+ if (IS_ERR(dir)) {
dev_err(nbd_to_dev(nbd), "Failed to create debugfs dir for '%s'\n",
nbd_name(nbd));
return -EIO;
@@ -1685,7 +1852,7 @@ static int nbd_dbg_init(void)
struct dentry *dbg_dir;
dbg_dir = debugfs_create_dir("nbd", NULL);
- if (!dbg_dir)
+ if (IS_ERR(dbg_dir))
return -EIO;
nbd_dbg_dir = dbg_dir;
@@ -1739,6 +1906,12 @@ static const struct blk_mq_ops nbd_mq_ops = {
static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
{
+ struct queue_limits lim = {
+ .max_hw_sectors = 65536,
+ .io_opt = 256 << SECTOR_SHIFT,
+ .max_segments = USHRT_MAX,
+ .max_segment_size = UINT_MAX,
+ };
struct nbd_device *nbd;
struct gendisk *disk;
int err = -ENOMEM;
@@ -1752,8 +1925,7 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
nbd->tag_set.queue_depth = 128;
nbd->tag_set.numa_node = NUMA_NO_NODE;
nbd->tag_set.cmd_size = sizeof(struct nbd_cmd);
- nbd->tag_set.flags = BLK_MQ_F_SHOULD_MERGE |
- BLK_MQ_F_BLOCKING;
+ nbd->tag_set.flags = BLK_MQ_F_BLOCKING;
nbd->tag_set.driver_data = nbd;
INIT_WORK(&nbd->remove_work, nbd_dev_remove_work);
nbd->backend = NULL;
@@ -1769,7 +1941,8 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
if (err == -ENOSPC)
err = -EEXIST;
} else {
- err = idr_alloc(&nbd_index_idr, nbd, 0, 0, GFP_KERNEL);
+ err = idr_alloc(&nbd_index_idr, nbd, 0,
+ (MINORMASK >> part_shift) + 1, GFP_KERNEL);
if (err >= 0)
index = err;
}
@@ -1778,7 +1951,7 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
if (err < 0)
goto out_free_tags;
- disk = blk_mq_alloc_disk(&nbd->tag_set, NULL);
+ disk = blk_mq_alloc_disk(&nbd->tag_set, &lim, NULL);
if (IS_ERR(disk)) {
err = PTR_ERR(disk);
goto out_free_idr;
@@ -1794,18 +1967,6 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
goto out_err_disk;
}
- /*
- * Tell the block layer that we are not a rotational device
- */
- blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue);
- blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, disk->queue);
- disk->queue->limits.discard_granularity = 0;
- blk_queue_max_discard_sectors(disk->queue, 0);
- blk_queue_max_segment_size(disk->queue, UINT_MAX);
- blk_queue_max_segments(disk->queue, USHRT_MAX);
- blk_queue_max_hw_sectors(disk->queue, 65536);
- disk->queue->limits.max_sectors = 256;
-
mutex_init(&nbd->config_lock);
refcount_set(&nbd->config_refs, 0);
/*
@@ -1933,11 +2094,11 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
}
- if (!info->attrs[NBD_ATTR_SOCKETS]) {
+ if (GENL_REQ_ATTR_CHECK(info, NBD_ATTR_SOCKETS)) {
pr_err("must specify at least one socket\n");
return -EINVAL;
}
- if (!info->attrs[NBD_ATTR_SIZE_BYTES]) {
+ if (GENL_REQ_ATTR_CHECK(info, NBD_ATTR_SIZE_BYTES)) {
pr_err("must specify a size in bytes for the device\n");
return -EINVAL;
}
@@ -1977,22 +2138,17 @@ again:
pr_err("nbd%d already in use\n", index);
return -EBUSY;
}
- if (WARN_ON(nbd->config)) {
- mutex_unlock(&nbd->config_lock);
- nbd_put(nbd);
- return -EINVAL;
- }
- config = nbd_alloc_config();
- if (IS_ERR(config)) {
+
+ ret = nbd_alloc_and_init_config(nbd);
+ if (ret) {
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
pr_err("couldn't allocate config\n");
- return PTR_ERR(config);
+ return ret;
}
- nbd->config = config;
- refcount_set(&nbd->config_refs, 1);
- set_bit(NBD_RT_BOUND, &config->runtime_flags);
+ config = nbd->config;
+ set_bit(NBD_RT_BOUND, &config->runtime_flags);
ret = nbd_genl_size_set(info, nbd);
if (ret)
goto out;
@@ -2063,9 +2219,7 @@ again:
goto out;
}
}
- ret = nbd_start_device(nbd);
- if (ret)
- goto out;
+
if (info->attrs[NBD_ATTR_BACKEND_IDENTIFIER]) {
nbd->backend = nla_strdup(info->attrs[NBD_ATTR_BACKEND_IDENTIFIER],
GFP_KERNEL);
@@ -2081,13 +2235,16 @@ again:
goto out;
}
set_bit(NBD_RT_HAS_BACKEND_FILE, &config->runtime_flags);
+
+ ret = nbd_start_device(nbd);
out:
- mutex_unlock(&nbd->config_lock);
if (!ret) {
set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags);
refcount_inc(&nbd->config_refs);
nbd_connect_reply(info, nbd->index);
}
+ mutex_unlock(&nbd->config_lock);
+
nbd_config_put(nbd);
if (put_dev)
nbd_put(nbd);
@@ -2107,6 +2264,7 @@ static void nbd_disconnect_and_put(struct nbd_device *nbd)
flush_workqueue(nbd->recv_workq);
nbd_clear_que(nbd);
nbd->task_setup = NULL;
+ clear_bit(NBD_RT_BOUND, &nbd->config->runtime_flags);
mutex_unlock(&nbd->config_lock);
if (test_and_clear_bit(NBD_RT_HAS_CONFIG_REF,
@@ -2122,7 +2280,7 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info)
if (!netlink_capable(skb, CAP_SYS_ADMIN))
return -EPERM;
- if (!info->attrs[NBD_ATTR_INDEX]) {
+ if (GENL_REQ_ATTR_CHECK(info, NBD_ATTR_INDEX)) {
pr_err("must specify an index to disconnect\n");
return -EINVAL;
}
@@ -2160,7 +2318,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
if (!netlink_capable(skb, CAP_SYS_ADMIN))
return -EPERM;
- if (!info->attrs[NBD_ATTR_INDEX]) {
+ if (GENL_REQ_ATTR_CHECK(info, NBD_ATTR_INDEX)) {
pr_err("must specify a device to reconfigure\n");
return -EINVAL;
}
@@ -2195,7 +2353,8 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
}
mutex_unlock(&nbd_index_mutex);
- if (!refcount_inc_not_zero(&nbd->config_refs)) {
+ config = nbd_get_config_unlocked(nbd);
+ if (!config) {
dev_err(nbd_to_dev(nbd),
"not configured, cannot reconfigure\n");
nbd_put(nbd);
@@ -2203,7 +2362,6 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
}
mutex_lock(&nbd->config_lock);
- config = nbd->config;
if (!test_bit(NBD_RT_BOUND, &config->runtime_flags) ||
!nbd->pid) {
dev_err(nbd_to_dev(nbd),
@@ -2324,10 +2482,12 @@ static struct genl_family nbd_genl_family __ro_after_init = {
.n_small_ops = ARRAY_SIZE(nbd_connect_genl_ops),
.resv_start_op = NBD_CMD_STATUS + 1,
.maxattr = NBD_ATTR_MAX,
+ .netnsok = 1,
.policy = nbd_attr_policy,
.mcgrps = nbd_mcast_grps,
.n_mcgrps = ARRAY_SIZE(nbd_mcast_grps),
};
+MODULE_ALIAS_GENL_FAMILY(NBD_GENL_FAMILY_NAME);
static int populate_nbd_status(struct nbd_device *nbd, struct sk_buff *reply)
{
@@ -2393,6 +2553,12 @@ static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info)
}
dev_list = nla_nest_start_noflag(reply, NBD_ATTR_DEVICE_LIST);
+ if (!dev_list) {
+ nlmsg_free(reply);
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
if (index == -1) {
ret = idr_for_each(&nbd_index_idr, &status_cb, reply);
if (ret) {
diff --git a/drivers/block/null_blk/Kconfig b/drivers/block/null_blk/Kconfig
index 6bf1f8ca20a2..ff23bb9346d0 100644
--- a/drivers/block/null_blk/Kconfig
+++ b/drivers/block/null_blk/Kconfig
@@ -9,4 +9,4 @@ config BLK_DEV_NULL_BLK
config BLK_DEV_NULL_BLK_FAULT_INJECTION
bool "Support fault injection for Null test block driver"
- depends on BLK_DEV_NULL_BLK && FAULT_INJECTION
+ depends on BLK_DEV_NULL_BLK && FAULT_INJECTION_CONFIGFS
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index c451c477978f..c7c0fb79a6bf 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -77,7 +77,7 @@ enum {
NULL_IRQ_TIMER = 2,
};
-static bool g_virt_boundary = false;
+static bool g_virt_boundary;
module_param_named(virt_boundary, g_virt_boundary, bool, 0444);
MODULE_PARM_DESC(virt_boundary, "Require a virtual boundary for the device. Default: False");
@@ -115,6 +115,18 @@ module_param_string(init_hctx, g_init_hctx_str, sizeof(g_init_hctx_str), 0444);
MODULE_PARM_DESC(init_hctx, "Fault injection to fail hctx init. init_hctx=<interval>,<probability>,<space>,<times>");
#endif
+/*
+ * Historic queue modes.
+ *
+ * These days nothing but NULL_Q_MQ is actually supported, but we keep it the
+ * enum for error reporting.
+ */
+enum {
+ NULL_Q_BIO = 0,
+ NULL_Q_RQ = 1,
+ NULL_Q_MQ = 2,
+};
+
static int g_queue_mode = NULL_Q_MQ;
static int null_param_store_val(const char *str, int *val, int min, int max)
@@ -165,8 +177,8 @@ static bool g_blocking;
module_param_named(blocking, g_blocking, bool, 0444);
MODULE_PARM_DESC(blocking, "Register as a blocking blk-mq driver device");
-static bool shared_tags;
-module_param(shared_tags, bool, 0444);
+static bool g_shared_tags;
+module_param_named(shared_tags, g_shared_tags, bool, 0444);
MODULE_PARM_DESC(shared_tags, "Share tag set between devices for blk-mq");
static bool g_shared_tag_bitmap;
@@ -211,7 +223,11 @@ MODULE_PARM_DESC(discard, "Support discard operations (requires memory-backed nu
static unsigned long g_cache_size;
module_param_named(cache_size, g_cache_size, ulong, 0444);
-MODULE_PARM_DESC(mbps, "Cache size in MiB for memory-backed device. Default: 0 (none)");
+MODULE_PARM_DESC(cache_size, "Cache size in MiB for memory-backed device. Default: 0 (none)");
+
+static bool g_fua = true;
+module_param_named(fua, g_fua, bool, 0444);
+MODULE_PARM_DESC(fua, "Enable/disable FUA support when cache_size is used. Default: true");
static unsigned int g_mbps;
module_param_named(mbps, g_mbps, uint, 0444);
@@ -241,6 +257,19 @@ static unsigned int g_zone_max_active;
module_param_named(zone_max_active, g_zone_max_active, uint, 0444);
MODULE_PARM_DESC(zone_max_active, "Maximum number of active zones when block device is zoned. Default: 0 (no limit)");
+static int g_zone_append_max_sectors = INT_MAX;
+module_param_named(zone_append_max_sectors, g_zone_append_max_sectors, int, 0444);
+MODULE_PARM_DESC(zone_append_max_sectors,
+ "Maximum size of a zone append command (in 512B sectors). Specify 0 for zone append emulation");
+
+static bool g_zone_full;
+module_param_named(zone_full, g_zone_full, bool, S_IRUGO);
+MODULE_PARM_DESC(zone_full, "Initialize the sequential write required zones of a zoned device to be full. Default: false");
+
+static bool g_rotational;
+module_param_named(rotational, g_rotational, bool, S_IRUGO);
+MODULE_PARM_DESC(rotational, "Set the rotational feature for the device. Default: false");
+
static struct nullb_device *null_alloc_dev(void);
static void null_free_dev(struct nullb_device *dev);
static void null_del_dev(struct nullb *nullb);
@@ -250,7 +279,7 @@ static void null_free_device_storage(struct nullb_device *dev, bool is_cache);
static inline struct nullb_device *to_nullb_device(struct config_item *item)
{
- return item ? container_of(item, struct nullb_device, item) : NULL;
+ return item ? container_of(to_config_group(item), struct nullb_device, group) : NULL;
}
static inline ssize_t nullb_device_uint_attr_show(unsigned int val, char *page)
@@ -392,13 +421,25 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,
static int nullb_apply_submit_queues(struct nullb_device *dev,
unsigned int submit_queues)
{
- return nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
+ int ret;
+
+ mutex_lock(&lock);
+ ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
+ mutex_unlock(&lock);
+
+ return ret;
}
static int nullb_apply_poll_queues(struct nullb_device *dev,
unsigned int poll_queues)
{
- return nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
+ int ret;
+
+ mutex_lock(&lock);
+ ret = nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
+ mutex_unlock(&lock);
+
+ return ret;
}
NULLB_DEVICE_ATTR(size, ulong, NULL);
@@ -424,9 +465,16 @@ NULLB_DEVICE_ATTR(zone_capacity, ulong, NULL);
NULLB_DEVICE_ATTR(zone_nr_conv, uint, NULL);
NULLB_DEVICE_ATTR(zone_max_open, uint, NULL);
NULLB_DEVICE_ATTR(zone_max_active, uint, NULL);
+NULLB_DEVICE_ATTR(zone_append_max_sectors, uint, NULL);
+NULLB_DEVICE_ATTR(zone_full, bool, NULL);
NULLB_DEVICE_ATTR(virt_boundary, bool, NULL);
NULLB_DEVICE_ATTR(no_sched, bool, NULL);
+NULLB_DEVICE_ATTR(shared_tags, bool, NULL);
NULLB_DEVICE_ATTR(shared_tag_bitmap, bool, NULL);
+NULLB_DEVICE_ATTR(fua, bool, NULL);
+NULLB_DEVICE_ATTR(rotational, bool, NULL);
+NULLB_DEVICE_ATTR(badblocks_once, bool, NULL);
+NULLB_DEVICE_ATTR(badblocks_partial_io, bool, NULL);
static ssize_t nullb_device_power_show(struct config_item *item, char *page)
{
@@ -444,28 +492,32 @@ static ssize_t nullb_device_power_store(struct config_item *item,
if (ret < 0)
return ret;
+ ret = count;
+ mutex_lock(&lock);
if (!dev->power && newp) {
if (test_and_set_bit(NULLB_DEV_FL_UP, &dev->flags))
- return count;
+ goto out;
+
ret = null_add_dev(dev);
if (ret) {
clear_bit(NULLB_DEV_FL_UP, &dev->flags);
- return ret;
+ goto out;
}
set_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags);
dev->power = newp;
+ ret = count;
} else if (dev->power && !newp) {
if (test_and_clear_bit(NULLB_DEV_FL_UP, &dev->flags)) {
- mutex_lock(&lock);
dev->power = newp;
null_del_dev(dev->nullb);
- mutex_unlock(&lock);
}
clear_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags);
}
- return count;
+out:
+ mutex_unlock(&lock);
+ return ret;
}
CONFIGFS_ATTR(nullb_device_, power);
@@ -509,49 +561,76 @@ static ssize_t nullb_device_badblocks_store(struct config_item *item,
goto out;
/* enable badblocks */
cmpxchg(&t_dev->badblocks.shift, -1, 0);
- if (buf[0] == '+')
- ret = badblocks_set(&t_dev->badblocks, start,
- end - start + 1, 1);
- else
- ret = badblocks_clear(&t_dev->badblocks, start,
- end - start + 1);
- if (ret == 0)
+ if (buf[0] == '+') {
+ if (badblocks_set(&t_dev->badblocks, start,
+ end - start + 1, 1))
+ ret = count;
+ } else if (badblocks_clear(&t_dev->badblocks, start,
+ end - start + 1)) {
ret = count;
+ }
out:
kfree(orig);
return ret;
}
CONFIGFS_ATTR(nullb_device_, badblocks);
+static ssize_t nullb_device_zone_readonly_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct nullb_device *dev = to_nullb_device(item);
+
+ return zone_cond_store(dev, page, count, BLK_ZONE_COND_READONLY);
+}
+CONFIGFS_ATTR_WO(nullb_device_, zone_readonly);
+
+static ssize_t nullb_device_zone_offline_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct nullb_device *dev = to_nullb_device(item);
+
+ return zone_cond_store(dev, page, count, BLK_ZONE_COND_OFFLINE);
+}
+CONFIGFS_ATTR_WO(nullb_device_, zone_offline);
+
static struct configfs_attribute *nullb_device_attrs[] = {
- &nullb_device_attr_size,
+ &nullb_device_attr_badblocks,
+ &nullb_device_attr_badblocks_once,
+ &nullb_device_attr_badblocks_partial_io,
+ &nullb_device_attr_blocking,
+ &nullb_device_attr_blocksize,
+ &nullb_device_attr_cache_size,
&nullb_device_attr_completion_nsec,
- &nullb_device_attr_submit_queues,
- &nullb_device_attr_poll_queues,
+ &nullb_device_attr_discard,
+ &nullb_device_attr_fua,
&nullb_device_attr_home_node,
- &nullb_device_attr_queue_mode,
- &nullb_device_attr_blocksize,
- &nullb_device_attr_max_sectors,
- &nullb_device_attr_irqmode,
&nullb_device_attr_hw_queue_depth,
&nullb_device_attr_index,
- &nullb_device_attr_blocking,
- &nullb_device_attr_use_per_node_hctx,
- &nullb_device_attr_power,
- &nullb_device_attr_memory_backed,
- &nullb_device_attr_discard,
+ &nullb_device_attr_irqmode,
+ &nullb_device_attr_max_sectors,
&nullb_device_attr_mbps,
- &nullb_device_attr_cache_size,
- &nullb_device_attr_badblocks,
- &nullb_device_attr_zoned,
- &nullb_device_attr_zone_size,
- &nullb_device_attr_zone_capacity,
- &nullb_device_attr_zone_nr_conv,
- &nullb_device_attr_zone_max_open,
- &nullb_device_attr_zone_max_active,
- &nullb_device_attr_virt_boundary,
+ &nullb_device_attr_memory_backed,
&nullb_device_attr_no_sched,
+ &nullb_device_attr_poll_queues,
+ &nullb_device_attr_power,
+ &nullb_device_attr_queue_mode,
+ &nullb_device_attr_rotational,
&nullb_device_attr_shared_tag_bitmap,
+ &nullb_device_attr_shared_tags,
+ &nullb_device_attr_size,
+ &nullb_device_attr_submit_queues,
+ &nullb_device_attr_use_per_node_hctx,
+ &nullb_device_attr_virt_boundary,
+ &nullb_device_attr_zone_append_max_sectors,
+ &nullb_device_attr_zone_capacity,
+ &nullb_device_attr_zone_full,
+ &nullb_device_attr_zone_max_active,
+ &nullb_device_attr_zone_max_open,
+ &nullb_device_attr_zone_nr_conv,
+ &nullb_device_attr_zone_offline,
+ &nullb_device_attr_zone_readonly,
+ &nullb_device_attr_zone_size,
+ &nullb_device_attr_zoned,
NULL,
};
@@ -573,8 +652,29 @@ static const struct config_item_type nullb_device_type = {
.ct_owner = THIS_MODULE,
};
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+
+static void nullb_add_fault_config(struct nullb_device *dev)
+{
+ fault_config_init(&dev->timeout_config, "timeout_inject");
+ fault_config_init(&dev->requeue_config, "requeue_inject");
+ fault_config_init(&dev->init_hctx_fault_config, "init_hctx_fault_inject");
+
+ configfs_add_default_group(&dev->timeout_config.group, &dev->group);
+ configfs_add_default_group(&dev->requeue_config.group, &dev->group);
+ configfs_add_default_group(&dev->init_hctx_fault_config.group, &dev->group);
+}
+
+#else
+
+static void nullb_add_fault_config(struct nullb_device *dev)
+{
+}
+
+#endif
+
static struct
-config_item *nullb_group_make_item(struct config_group *group, const char *name)
+config_group *nullb_group_make_group(struct config_group *group, const char *name)
{
struct nullb_device *dev;
@@ -585,9 +685,10 @@ config_item *nullb_group_make_item(struct config_group *group, const char *name)
if (!dev)
return ERR_PTR(-ENOMEM);
- config_item_init_type_name(&dev->item, name, &nullb_device_type);
+ config_group_init_type_name(&dev->group, name, &nullb_device_type);
+ nullb_add_fault_config(dev);
- return &dev->item;
+ return &dev->group;
}
static void
@@ -607,14 +708,28 @@ nullb_group_drop_item(struct config_group *group, struct config_item *item)
static ssize_t memb_group_features_show(struct config_item *item, char *page)
{
- return snprintf(page, PAGE_SIZE,
- "badblocks,blocking,blocksize,cache_size,"
- "completion_nsec,discard,home_node,hw_queue_depth,"
- "irqmode,max_sectors,mbps,memory_backed,no_sched,"
- "poll_queues,power,queue_mode,shared_tag_bitmap,size,"
- "submit_queues,use_per_node_hctx,virt_boundary,zoned,"
- "zone_capacity,zone_max_active,zone_max_open,"
- "zone_nr_conv,zone_size\n");
+
+ struct configfs_attribute **entry;
+ char delimiter = ',';
+ size_t left = PAGE_SIZE;
+ size_t written = 0;
+ int ret;
+
+ for (entry = &nullb_device_attrs[0]; *entry && left > 0; entry++) {
+ if (!*(entry + 1))
+ delimiter = '\n';
+ ret = snprintf(page + written, left, "%s%c", (*entry)->ca_name,
+ delimiter);
+ if (ret >= left) {
+ WARN_ONCE(1, "Too many null_blk features to print\n");
+ memzero_explicit(page, PAGE_SIZE);
+ return -ENOBUFS;
+ }
+ left -= ret;
+ written += ret;
+ }
+
+ return written;
}
CONFIGFS_ATTR_RO(memb_group_, features);
@@ -625,7 +740,7 @@ static struct configfs_attribute *nullb_group_attrs[] = {
};
static struct configfs_group_operations nullb_group_ops = {
- .make_item = nullb_group_make_item,
+ .make_group = nullb_group_make_group,
.drop_item = nullb_group_drop_item,
};
@@ -656,6 +771,13 @@ static struct nullb_device *null_alloc_dev(void)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
+
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+ dev->timeout_config.attr = null_timeout_attr;
+ dev->requeue_config.attr = null_requeue_attr;
+ dev->init_hctx_fault_config.attr = null_init_hctx_attr;
+#endif
+
INIT_RADIX_TREE(&dev->data, GFP_ATOMIC);
INIT_RADIX_TREE(&dev->cache, GFP_ATOMIC);
if (badblocks_init(&dev->badblocks, 0)) {
@@ -687,9 +809,15 @@ static struct nullb_device *null_alloc_dev(void)
dev->zone_nr_conv = g_zone_nr_conv;
dev->zone_max_open = g_zone_max_open;
dev->zone_max_active = g_zone_max_active;
+ dev->zone_append_max_sectors = g_zone_append_max_sectors;
+ dev->zone_full = g_zone_full;
dev->virt_boundary = g_virt_boundary;
dev->no_sched = g_no_sched;
+ dev->shared_tags = g_shared_tags;
dev->shared_tag_bitmap = g_shared_tag_bitmap;
+ dev->fua = g_fua;
+ dev->rotational = g_rotational;
+
return dev;
}
@@ -703,98 +831,11 @@ static void null_free_dev(struct nullb_device *dev)
kfree(dev);
}
-static void put_tag(struct nullb_queue *nq, unsigned int tag)
-{
- clear_bit_unlock(tag, nq->tag_map);
-
- if (waitqueue_active(&nq->wait))
- wake_up(&nq->wait);
-}
-
-static unsigned int get_tag(struct nullb_queue *nq)
-{
- unsigned int tag;
-
- do {
- tag = find_first_zero_bit(nq->tag_map, nq->queue_depth);
- if (tag >= nq->queue_depth)
- return -1U;
- } while (test_and_set_bit_lock(tag, nq->tag_map));
-
- return tag;
-}
-
-static void free_cmd(struct nullb_cmd *cmd)
-{
- put_tag(cmd->nq, cmd->tag);
-}
-
-static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer);
-
-static struct nullb_cmd *__alloc_cmd(struct nullb_queue *nq)
-{
- struct nullb_cmd *cmd;
- unsigned int tag;
-
- tag = get_tag(nq);
- if (tag != -1U) {
- cmd = &nq->cmds[tag];
- cmd->tag = tag;
- cmd->error = BLK_STS_OK;
- cmd->nq = nq;
- if (nq->dev->irqmode == NULL_IRQ_TIMER) {
- hrtimer_init(&cmd->timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- cmd->timer.function = null_cmd_timer_expired;
- }
- return cmd;
- }
-
- return NULL;
-}
-
-static struct nullb_cmd *alloc_cmd(struct nullb_queue *nq, struct bio *bio)
-{
- struct nullb_cmd *cmd;
- DEFINE_WAIT(wait);
-
- do {
- /*
- * This avoids multiple return statements, multiple calls to
- * __alloc_cmd() and a fast path call to prepare_to_wait().
- */
- cmd = __alloc_cmd(nq);
- if (cmd) {
- cmd->bio = bio;
- return cmd;
- }
- prepare_to_wait(&nq->wait, &wait, TASK_UNINTERRUPTIBLE);
- io_schedule();
- finish_wait(&nq->wait, &wait);
- } while (1);
-}
-
-static void end_cmd(struct nullb_cmd *cmd)
-{
- int queue_mode = cmd->nq->dev->queue_mode;
-
- switch (queue_mode) {
- case NULL_Q_MQ:
- blk_mq_end_request(cmd->rq, cmd->error);
- return;
- case NULL_Q_BIO:
- cmd->bio->bi_status = cmd->error;
- bio_endio(cmd->bio);
- break;
- }
-
- free_cmd(cmd);
-}
-
static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer)
{
- end_cmd(container_of(timer, struct nullb_cmd, timer));
+ struct nullb_cmd *cmd = container_of(timer, struct nullb_cmd, timer);
+ blk_mq_end_request(blk_mq_rq_from_pdu(cmd), cmd->error);
return HRTIMER_NORESTART;
}
@@ -807,7 +848,9 @@ static void null_cmd_end_timer(struct nullb_cmd *cmd)
static void null_complete_rq(struct request *rq)
{
- end_cmd(blk_mq_rq_to_pdu(rq));
+ struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
+
+ blk_mq_end_request(rq, cmd->error);
}
static struct nullb_page *null_alloc_page(void)
@@ -880,7 +923,7 @@ static struct nullb_page *null_radix_tree_insert(struct nullb *nullb, u64 idx,
if (radix_tree_insert(root, idx, t_page)) {
null_free_page(t_page);
t_page = radix_tree_lookup(root, idx);
- WARN_ON(!t_page || t_page->page->index != idx);
+ WARN_ON(!t_page || t_page->page->private != idx);
} else if (is_cache)
nullb->dev->curr_cache += PAGE_SIZE;
@@ -903,7 +946,7 @@ static void null_free_device_storage(struct nullb_device *dev, bool is_cache)
(void **)t_pages, pos, FREE_BATCH);
for (i = 0; i < nr_pages; i++) {
- pos = t_pages[i]->page->index;
+ pos = t_pages[i]->page->private;
ret = radix_tree_delete_item(root, pos, t_pages[i]);
WARN_ON(ret != t_pages[i]);
null_free_page(ret);
@@ -929,7 +972,7 @@ static struct nullb_page *__null_lookup_page(struct nullb *nullb,
root = is_cache ? &nullb->dev->cache : &nullb->dev->data;
t_page = radix_tree_lookup(root, idx);
- WARN_ON(t_page && t_page->page->index != idx);
+ WARN_ON(t_page && t_page->page->private != idx);
if (t_page && (for_write || test_bit(sector_bit, t_page->bitmap)))
return t_page;
@@ -972,7 +1015,7 @@ static struct nullb_page *null_insert_page(struct nullb *nullb,
spin_lock_irq(&nullb->lock);
idx = sector >> PAGE_SECTORS_SHIFT;
- t_page->page->index = idx;
+ t_page->page->private = idx;
t_page = null_radix_tree_insert(nullb, idx, t_page, !ignore_cache);
radix_tree_preload_end();
@@ -992,7 +1035,7 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
struct nullb_page *t_page, *ret;
void *dst, *src;
- idx = c_page->page->index;
+ idx = c_page->page->private;
t_page = null_insert_page(nullb, idx << PAGE_SECTORS_SHIFT, true);
@@ -1010,8 +1053,8 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
if (!t_page)
return -ENOMEM;
- src = kmap_atomic(c_page->page);
- dst = kmap_atomic(t_page->page);
+ src = kmap_local_page(c_page->page);
+ dst = kmap_local_page(t_page->page);
for (i = 0; i < PAGE_SECTORS;
i += (nullb->dev->blocksize >> SECTOR_SHIFT)) {
@@ -1023,8 +1066,8 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
}
}
- kunmap_atomic(dst);
- kunmap_atomic(src);
+ kunmap_local(dst);
+ kunmap_local(src);
ret = radix_tree_delete_item(&nullb->dev->cache, idx, c_page);
null_free_page(ret);
@@ -1051,7 +1094,7 @@ again:
* avoid race, we don't allow page free
*/
for (i = 0; i < nr_pages; i++) {
- nullb->cache_flush_pos = c_pages[i]->page->index;
+ nullb->cache_flush_pos = c_pages[i]->page->private;
/*
* We found the page which is being flushed to disk by other
* threads
@@ -1086,31 +1129,28 @@ again:
return 0;
}
-static int copy_to_nullb(struct nullb *nullb, struct page *source,
- unsigned int off, sector_t sector, size_t n, bool is_fua)
+static blk_status_t copy_to_nullb(struct nullb *nullb, void *source,
+ loff_t pos, size_t n, bool is_fua)
{
size_t temp, count = 0;
- unsigned int offset;
struct nullb_page *t_page;
- void *dst, *src;
+ sector_t sector;
while (count < n) {
- temp = min_t(size_t, nullb->dev->blocksize, n - count);
+ temp = min3(nullb->dev->blocksize, n - count,
+ PAGE_SIZE - offset_in_page(pos));
+ sector = pos >> SECTOR_SHIFT;
if (null_cache_active(nullb) && !is_fua)
null_make_cache_space(nullb, PAGE_SIZE);
- offset = (sector & SECTOR_MASK) << SECTOR_SHIFT;
t_page = null_insert_page(nullb, sector,
!null_cache_active(nullb) || is_fua);
if (!t_page)
- return -ENOSPC;
+ return BLK_STS_NOSPC;
- src = kmap_atomic(source);
- dst = kmap_atomic(t_page->page);
- memcpy(dst + offset, src + off + count, temp);
- kunmap_atomic(dst);
- kunmap_atomic(src);
+ memcpy_to_page(t_page->page, offset_in_page(pos),
+ source + count, temp);
__set_bit(sector & SECTOR_MASK, t_page->bitmap);
@@ -1118,51 +1158,34 @@ static int copy_to_nullb(struct nullb *nullb, struct page *source,
null_free_sector(nullb, sector, true);
count += temp;
- sector += temp >> SECTOR_SHIFT;
+ pos += temp;
}
- return 0;
+ return BLK_STS_OK;
}
-static int copy_from_nullb(struct nullb *nullb, struct page *dest,
- unsigned int off, sector_t sector, size_t n)
+static void copy_from_nullb(struct nullb *nullb, void *dest, loff_t pos,
+ size_t n)
{
size_t temp, count = 0;
- unsigned int offset;
struct nullb_page *t_page;
- void *dst, *src;
+ sector_t sector;
while (count < n) {
- temp = min_t(size_t, nullb->dev->blocksize, n - count);
+ temp = min3(nullb->dev->blocksize, n - count,
+ PAGE_SIZE - offset_in_page(pos));
+ sector = pos >> SECTOR_SHIFT;
- offset = (sector & SECTOR_MASK) << SECTOR_SHIFT;
t_page = null_lookup_page(nullb, sector, false,
!null_cache_active(nullb));
-
- dst = kmap_atomic(dest);
- if (!t_page) {
- memset(dst + off + count, 0, temp);
- goto next;
- }
- src = kmap_atomic(t_page->page);
- memcpy(dst + off + count, src + offset, temp);
- kunmap_atomic(src);
-next:
- kunmap_atomic(dst);
+ if (t_page)
+ memcpy_from_page(dest + count, t_page->page,
+ offset_in_page(pos), temp);
+ else
+ memset(dest + count, 0, temp);
count += temp;
- sector += temp >> SECTOR_SHIFT;
+ pos += temp;
}
- return 0;
-}
-
-static void nullb_fill_pattern(struct nullb *nullb, struct page *page,
- unsigned int len, unsigned int off)
-{
- void *dst;
-
- dst = kmap_atomic(page);
- memset(dst + off, 0xFF, len);
- kunmap_atomic(dst);
}
blk_status_t null_handle_discard(struct nullb_device *dev,
@@ -1186,7 +1209,7 @@ blk_status_t null_handle_discard(struct nullb_device *dev,
return BLK_STS_OK;
}
-static int null_handle_flush(struct nullb *nullb)
+static blk_status_t null_handle_flush(struct nullb *nullb)
{
int err;
@@ -1203,107 +1226,80 @@ static int null_handle_flush(struct nullb *nullb)
WARN_ON(!radix_tree_empty(&nullb->dev->cache));
spin_unlock_irq(&nullb->lock);
- return err;
+ return errno_to_blk_status(err);
}
-static int null_transfer(struct nullb *nullb, struct page *page,
- unsigned int len, unsigned int off, bool is_write, sector_t sector,
+static blk_status_t null_transfer(struct nullb *nullb, struct page *page,
+ unsigned int len, unsigned int off, bool is_write, loff_t pos,
bool is_fua)
{
struct nullb_device *dev = nullb->dev;
+ blk_status_t err = BLK_STS_OK;
unsigned int valid_len = len;
- int err = 0;
+ void *p;
+ p = kmap_local_page(page) + off;
if (!is_write) {
- if (dev->zoned)
+ if (dev->zoned) {
valid_len = null_zone_valid_read_len(nullb,
- sector, len);
+ pos >> SECTOR_SHIFT, len);
+ if (valid_len && valid_len != len)
+ valid_len -= pos & (SECTOR_SIZE - 1);
+ }
if (valid_len) {
- err = copy_from_nullb(nullb, page, off,
- sector, valid_len);
+ copy_from_nullb(nullb, p, pos, valid_len);
off += valid_len;
len -= valid_len;
}
if (len)
- nullb_fill_pattern(nullb, page, len, off);
+ memset(p + valid_len, 0xff, len);
flush_dcache_page(page);
} else {
flush_dcache_page(page);
- err = copy_to_nullb(nullb, page, off, sector, len, is_fua);
+ err = copy_to_nullb(nullb, p, pos, len, is_fua);
}
+ kunmap_local(p);
return err;
}
-static int null_handle_rq(struct nullb_cmd *cmd)
+/*
+ * Transfer data for the given request. The transfer size is capped with the
+ * nr_sectors argument.
+ */
+static blk_status_t null_handle_data_transfer(struct nullb_cmd *cmd,
+ sector_t nr_sectors)
{
- struct request *rq = cmd->rq;
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
struct nullb *nullb = cmd->nq->dev->nullb;
- int err;
+ blk_status_t err = BLK_STS_OK;
unsigned int len;
- sector_t sector = blk_rq_pos(rq);
+ loff_t pos = blk_rq_pos(rq) << SECTOR_SHIFT;
+ unsigned int max_bytes = nr_sectors << SECTOR_SHIFT;
+ unsigned int transferred_bytes = 0;
struct req_iterator iter;
struct bio_vec bvec;
spin_lock_irq(&nullb->lock);
rq_for_each_segment(bvec, rq, iter) {
len = bvec.bv_len;
+ if (transferred_bytes + len > max_bytes)
+ len = max_bytes - transferred_bytes;
err = null_transfer(nullb, bvec.bv_page, len, bvec.bv_offset,
- op_is_write(req_op(rq)), sector,
+ op_is_write(req_op(rq)), pos,
rq->cmd_flags & REQ_FUA);
- if (err) {
- spin_unlock_irq(&nullb->lock);
- return err;
- }
- sector += len >> SECTOR_SHIFT;
- }
- spin_unlock_irq(&nullb->lock);
-
- return 0;
-}
-
-static int null_handle_bio(struct nullb_cmd *cmd)
-{
- struct bio *bio = cmd->bio;
- struct nullb *nullb = cmd->nq->dev->nullb;
- int err;
- unsigned int len;
- sector_t sector = bio->bi_iter.bi_sector;
- struct bio_vec bvec;
- struct bvec_iter iter;
-
- spin_lock_irq(&nullb->lock);
- bio_for_each_segment(bvec, bio, iter) {
- len = bvec.bv_len;
- err = null_transfer(nullb, bvec.bv_page, len, bvec.bv_offset,
- op_is_write(bio_op(bio)), sector,
- bio->bi_opf & REQ_FUA);
- if (err) {
- spin_unlock_irq(&nullb->lock);
- return err;
- }
- sector += len >> SECTOR_SHIFT;
+ if (err)
+ break;
+ pos += len;
+ transferred_bytes += len;
+ if (transferred_bytes >= max_bytes)
+ break;
}
spin_unlock_irq(&nullb->lock);
- return 0;
-}
-
-static void null_stop_queue(struct nullb *nullb)
-{
- struct request_queue *q = nullb->q;
-
- if (nullb->dev->queue_mode == NULL_Q_MQ)
- blk_mq_stop_hw_queues(q);
-}
-
-static void null_restart_queue_async(struct nullb *nullb)
-{
- struct request_queue *q = nullb->q;
- if (nullb->dev->queue_mode == NULL_Q_MQ)
- blk_mq_start_stopped_hw_queues(q, true);
+ return err;
}
static inline blk_status_t null_handle_throttled(struct nullb_cmd *cmd)
@@ -1311,73 +1307,85 @@ static inline blk_status_t null_handle_throttled(struct nullb_cmd *cmd)
struct nullb_device *dev = cmd->nq->dev;
struct nullb *nullb = dev->nullb;
blk_status_t sts = BLK_STS_OK;
- struct request *rq = cmd->rq;
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
if (!hrtimer_active(&nullb->bw_timer))
hrtimer_restart(&nullb->bw_timer);
if (atomic_long_sub_return(blk_rq_bytes(rq), &nullb->cur_bytes) < 0) {
- null_stop_queue(nullb);
+ blk_mq_stop_hw_queues(nullb->q);
/* race with timer */
if (atomic_long_read(&nullb->cur_bytes) > 0)
- null_restart_queue_async(nullb);
+ blk_mq_start_stopped_hw_queues(nullb->q, true);
/* requeue request */
sts = BLK_STS_DEV_RESOURCE;
}
return sts;
}
-static inline blk_status_t null_handle_badblocks(struct nullb_cmd *cmd,
- sector_t sector,
- sector_t nr_sectors)
+/*
+ * Check if the command should fail for the badblocks. If so, return
+ * BLK_STS_IOERR and return number of partial I/O sectors to be written or read,
+ * which may be less than the requested number of sectors.
+ *
+ * @cmd: The command to handle.
+ * @sector: The start sector for I/O.
+ * @nr_sectors: Specifies number of sectors to write or read, and returns the
+ * number of sectors to be written or read.
+ */
+blk_status_t null_handle_badblocks(struct nullb_cmd *cmd, sector_t sector,
+ unsigned int *nr_sectors)
{
struct badblocks *bb = &cmd->nq->dev->badblocks;
- sector_t first_bad;
- int bad_sectors;
+ struct nullb_device *dev = cmd->nq->dev;
+ unsigned int block_sectors = dev->blocksize >> SECTOR_SHIFT;
+ sector_t first_bad, bad_sectors;
+ unsigned int partial_io_sectors = 0;
- if (badblocks_check(bb, sector, nr_sectors, &first_bad, &bad_sectors))
- return BLK_STS_IOERR;
+ if (!badblocks_check(bb, sector, *nr_sectors, &first_bad, &bad_sectors))
+ return BLK_STS_OK;
- return BLK_STS_OK;
+ if (cmd->nq->dev->badblocks_once)
+ badblocks_clear(bb, first_bad, bad_sectors);
+
+ if (cmd->nq->dev->badblocks_partial_io) {
+ if (!IS_ALIGNED(first_bad, block_sectors))
+ first_bad = ALIGN_DOWN(first_bad, block_sectors);
+ if (sector < first_bad)
+ partial_io_sectors = first_bad - sector;
+ }
+ *nr_sectors = partial_io_sectors;
+
+ return BLK_STS_IOERR;
}
-static inline blk_status_t null_handle_memory_backed(struct nullb_cmd *cmd,
- enum req_op op,
- sector_t sector,
- sector_t nr_sectors)
+blk_status_t null_handle_memory_backed(struct nullb_cmd *cmd, enum req_op op,
+ sector_t sector, sector_t nr_sectors)
{
struct nullb_device *dev = cmd->nq->dev;
- int err;
if (op == REQ_OP_DISCARD)
return null_handle_discard(dev, sector, nr_sectors);
- if (dev->queue_mode == NULL_Q_BIO)
- err = null_handle_bio(cmd);
- else
- err = null_handle_rq(cmd);
-
- return errno_to_blk_status(err);
+ return null_handle_data_transfer(cmd, nr_sectors);
}
static void nullb_zero_read_cmd_buffer(struct nullb_cmd *cmd)
{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
struct nullb_device *dev = cmd->nq->dev;
struct bio *bio;
- if (dev->memory_backed)
- return;
-
- if (dev->queue_mode == NULL_Q_BIO && bio_op(cmd->bio) == REQ_OP_READ) {
- zero_fill_bio(cmd->bio);
- } else if (req_op(cmd->rq) == REQ_OP_READ) {
- __rq_for_each_bio(bio, cmd->rq)
+ if (!dev->memory_backed && req_op(rq) == REQ_OP_READ) {
+ __rq_for_each_bio(bio, rq)
zero_fill_bio(bio);
}
}
static inline void nullb_complete_cmd(struct nullb_cmd *cmd)
{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+
/*
* Since root privileges are required to configure the null_blk
* driver, it is fine that this driver does not initialize the
@@ -1391,21 +1399,10 @@ static inline void nullb_complete_cmd(struct nullb_cmd *cmd)
/* Complete IO by inline, softirq or timer */
switch (cmd->nq->dev->irqmode) {
case NULL_IRQ_SOFTIRQ:
- switch (cmd->nq->dev->queue_mode) {
- case NULL_Q_MQ:
- if (likely(!blk_should_fake_timeout(cmd->rq->q)))
- blk_mq_complete_request(cmd->rq);
- break;
- case NULL_Q_BIO:
- /*
- * XXX: no proper submitting cpu information available.
- */
- end_cmd(cmd);
- break;
- }
+ blk_mq_complete_request(rq);
break;
case NULL_IRQ_NONE:
- end_cmd(cmd);
+ blk_mq_end_request(rq, cmd->error);
break;
case NULL_IRQ_TIMER:
null_cmd_end_timer(cmd);
@@ -1417,35 +1414,30 @@ blk_status_t null_process_cmd(struct nullb_cmd *cmd, enum req_op op,
sector_t sector, unsigned int nr_sectors)
{
struct nullb_device *dev = cmd->nq->dev;
+ blk_status_t badblocks_ret = BLK_STS_OK;
blk_status_t ret;
- if (dev->badblocks.shift != -1) {
- ret = null_handle_badblocks(cmd, sector, nr_sectors);
+ if (dev->badblocks.shift != -1)
+ badblocks_ret = null_handle_badblocks(cmd, sector, &nr_sectors);
+
+ if (dev->memory_backed && nr_sectors) {
+ ret = null_handle_memory_backed(cmd, op, sector, nr_sectors);
if (ret != BLK_STS_OK)
return ret;
}
- if (dev->memory_backed)
- return null_handle_memory_backed(cmd, op, sector, nr_sectors);
-
- return BLK_STS_OK;
+ return badblocks_ret;
}
-static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
- sector_t nr_sectors, enum req_op op)
+static void null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
+ sector_t nr_sectors, enum req_op op)
{
struct nullb_device *dev = cmd->nq->dev;
struct nullb *nullb = dev->nullb;
blk_status_t sts;
- if (test_bit(NULLB_DEV_FL_THROTTLED, &dev->flags)) {
- sts = null_handle_throttled(cmd);
- if (sts != BLK_STS_OK)
- return sts;
- }
-
if (op == REQ_OP_FLUSH) {
- cmd->error = errno_to_blk_status(null_handle_flush(nullb));
+ cmd->error = null_handle_flush(nullb);
goto out;
}
@@ -1460,7 +1452,6 @@ static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
out:
nullb_complete_cmd(cmd);
- return BLK_STS_OK;
}
static enum hrtimer_restart nullb_bwtimer_fn(struct hrtimer *timer)
@@ -1473,7 +1464,7 @@ static enum hrtimer_restart nullb_bwtimer_fn(struct hrtimer *timer)
return HRTIMER_NORESTART;
atomic_long_set(&nullb->cur_bytes, mb_per_tick(mbps));
- null_restart_queue_async(nullb);
+ blk_mq_start_stopped_hw_queues(nullb->q, true);
hrtimer_forward_now(&nullb->bw_timer, timer_interval);
@@ -1484,51 +1475,54 @@ static void nullb_setup_bwtimer(struct nullb *nullb)
{
ktime_t timer_interval = ktime_set(0, TIMER_INTERVAL);
- hrtimer_init(&nullb->bw_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- nullb->bw_timer.function = nullb_bwtimer_fn;
+ hrtimer_setup(&nullb->bw_timer, nullb_bwtimer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
atomic_long_set(&nullb->cur_bytes, mb_per_tick(nullb->dev->mbps));
hrtimer_start(&nullb->bw_timer, timer_interval, HRTIMER_MODE_REL);
}
-static struct nullb_queue *nullb_to_queue(struct nullb *nullb)
-{
- int index = 0;
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
- if (nullb->nr_queues != 1)
- index = raw_smp_processor_id() / ((nr_cpu_ids + nullb->nr_queues - 1) / nullb->nr_queues);
+static bool should_timeout_request(struct request *rq)
+{
+ struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct nullb_device *dev = cmd->nq->dev;
- return &nullb->queues[index];
+ return should_fail(&dev->timeout_config.attr, 1);
}
-static void null_submit_bio(struct bio *bio)
+static bool should_requeue_request(struct request *rq)
{
- sector_t sector = bio->bi_iter.bi_sector;
- sector_t nr_sectors = bio_sectors(bio);
- struct nullb *nullb = bio->bi_bdev->bd_disk->private_data;
- struct nullb_queue *nq = nullb_to_queue(nullb);
+ struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct nullb_device *dev = cmd->nq->dev;
+
+ return should_fail(&dev->requeue_config.attr, 1);
+}
- null_handle_cmd(alloc_cmd(nq, bio), sector, nr_sectors, bio_op(bio));
+static bool should_init_hctx_fail(struct nullb_device *dev)
+{
+ return should_fail(&dev->init_hctx_fault_config.attr, 1);
}
+#else
+
static bool should_timeout_request(struct request *rq)
{
-#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
- if (g_timeout_str[0])
- return should_fail(&null_timeout_attr, 1);
-#endif
return false;
}
static bool should_requeue_request(struct request *rq)
{
-#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
- if (g_requeue_str[0])
- return should_fail(&null_requeue_attr, 1);
-#endif
return false;
}
-static int null_map_queues(struct blk_mq_tag_set *set)
+static bool should_init_hctx_fail(struct nullb_device *dev)
+{
+ return false;
+}
+
+#endif
+
+static void null_map_queues(struct blk_mq_tag_set *set)
{
struct nullb *nullb = set->driver_data;
int i, qoff;
@@ -1555,7 +1549,9 @@ static int null_map_queues(struct blk_mq_tag_set *set)
} else {
pr_warn("tag set has unexpected nr_hw_queues: %d\n",
set->nr_hw_queues);
- return -EINVAL;
+ WARN_ON_ONCE(true);
+ submit_queues = 1;
+ poll_queues = 0;
}
}
@@ -1577,8 +1573,6 @@ static int null_map_queues(struct blk_mq_tag_set *set)
qoff += map->nr_queues;
blk_mq_map_queues(map);
}
-
- return 0;
}
static int null_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
@@ -1586,9 +1580,12 @@ static int null_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
struct nullb_queue *nq = hctx->driver_data;
LIST_HEAD(list);
int nr = 0;
+ struct request *rq;
spin_lock(&nq->poll_lock);
list_splice_init(&nq->poll_list, &list);
+ list_for_each_entry(rq, &list, queuelist)
+ blk_mq_set_request_complete(rq);
spin_unlock(&nq->poll_lock);
while (!list_empty(&list)) {
@@ -1600,9 +1597,9 @@ static int null_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
cmd = blk_mq_rq_to_pdu(req);
cmd->error = null_process_cmd(cmd, req_op(req), blk_rq_pos(req),
blk_rq_sectors(req));
- if (!blk_mq_add_to_batch(req, iob, (__force int) cmd->error,
- blk_mq_end_request_batch))
- end_cmd(cmd);
+ if (!blk_mq_add_to_batch(req, iob, cmd->error != BLK_STS_OK,
+ blk_mq_end_request_batch))
+ blk_mq_end_request(req, cmd->error);
nr++;
}
@@ -1614,16 +1611,21 @@ static enum blk_eh_timer_return null_timeout_rq(struct request *rq)
struct blk_mq_hw_ctx *hctx = rq->mq_hctx;
struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
- pr_info("rq %p timed out\n", rq);
-
if (hctx->type == HCTX_TYPE_POLL) {
struct nullb_queue *nq = hctx->driver_data;
spin_lock(&nq->poll_lock);
+ /* The request may have completed meanwhile. */
+ if (blk_mq_request_completed(rq)) {
+ spin_unlock(&nq->poll_lock);
+ return BLK_EH_DONE;
+ }
list_del_init(&rq->queuelist);
spin_unlock(&nq->poll_lock);
}
+ pr_info("rq %p timed out\n", rq);
+
/*
* If the device is marked as blocking (i.e. memory backed or zoned
* device), the submission path may be blocked waiting for resources
@@ -1638,28 +1640,27 @@ static enum blk_eh_timer_return null_timeout_rq(struct request *rq)
}
static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd)
+ const struct blk_mq_queue_data *bd)
{
- struct nullb_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
+ struct request *rq = bd->rq;
+ struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
struct nullb_queue *nq = hctx->driver_data;
- sector_t nr_sectors = blk_rq_sectors(bd->rq);
- sector_t sector = blk_rq_pos(bd->rq);
+ sector_t nr_sectors = blk_rq_sectors(rq);
+ sector_t sector = blk_rq_pos(rq);
const bool is_poll = hctx->type == HCTX_TYPE_POLL;
might_sleep_if(hctx->flags & BLK_MQ_F_BLOCKING);
if (!is_poll && nq->dev->irqmode == NULL_IRQ_TIMER) {
- hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- cmd->timer.function = null_cmd_timer_expired;
+ hrtimer_setup(&cmd->timer, null_cmd_timer_expired, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
}
- cmd->rq = bd->rq;
cmd->error = BLK_STS_OK;
cmd->nq = nq;
- cmd->fake_timeout = should_timeout_request(bd->rq);
-
- blk_mq_start_request(bd->rq);
+ cmd->fake_timeout = should_timeout_request(rq) ||
+ blk_should_fake_timeout(rq->q);
- if (should_requeue_request(bd->rq)) {
+ if (should_requeue_request(rq)) {
/*
* Alternate between hitting the core BUSY path, and the
* driver driven requeue path
@@ -1667,52 +1668,52 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx,
nq->requeue_selection++;
if (nq->requeue_selection & 1)
return BLK_STS_RESOURCE;
- else {
- blk_mq_requeue_request(bd->rq, true);
- return BLK_STS_OK;
- }
+ blk_mq_requeue_request(rq, true);
+ return BLK_STS_OK;
+ }
+
+ if (test_bit(NULLB_DEV_FL_THROTTLED, &nq->dev->flags)) {
+ blk_status_t sts = null_handle_throttled(cmd);
+
+ if (sts != BLK_STS_OK)
+ return sts;
}
+ blk_mq_start_request(rq);
+
if (is_poll) {
spin_lock(&nq->poll_lock);
- list_add_tail(&bd->rq->queuelist, &nq->poll_list);
+ list_add_tail(&rq->queuelist, &nq->poll_list);
spin_unlock(&nq->poll_lock);
return BLK_STS_OK;
}
if (cmd->fake_timeout)
return BLK_STS_OK;
- return null_handle_cmd(cmd, sector, nr_sectors, req_op(bd->rq));
-}
-
-static void cleanup_queue(struct nullb_queue *nq)
-{
- bitmap_free(nq->tag_map);
- kfree(nq->cmds);
+ null_handle_cmd(cmd, sector, nr_sectors, req_op(rq));
+ return BLK_STS_OK;
}
-static void cleanup_queues(struct nullb *nullb)
+static void null_queue_rqs(struct rq_list *rqlist)
{
- int i;
-
- for (i = 0; i < nullb->nr_queues; i++)
- cleanup_queue(&nullb->queues[i]);
+ struct rq_list requeue_list = {};
+ struct blk_mq_queue_data bd = { };
+ blk_status_t ret;
- kfree(nullb->queues);
-}
+ do {
+ struct request *rq = rq_list_pop(rqlist);
-static void null_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx)
-{
- struct nullb_queue *nq = hctx->driver_data;
- struct nullb *nullb = nq->dev->nullb;
+ bd.rq = rq;
+ ret = null_queue_rq(rq->mq_hctx, &bd);
+ if (ret != BLK_STS_OK)
+ rq_list_add_tail(&requeue_list, rq);
+ } while (!rq_list_empty(rqlist));
- nullb->nr_queues--;
+ *rqlist = requeue_list;
}
static void null_init_queue(struct nullb *nullb, struct nullb_queue *nq)
{
- init_waitqueue_head(&nq->wait);
- nq->queue_depth = nullb->queue_depth;
nq->dev = nullb->dev;
INIT_LIST_HEAD(&nq->poll_list);
spin_lock_init(&nq->poll_lock);
@@ -1724,27 +1725,24 @@ static int null_init_hctx(struct blk_mq_hw_ctx *hctx, void *driver_data,
struct nullb *nullb = hctx->queue->queuedata;
struct nullb_queue *nq;
-#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
- if (g_init_hctx_str[0] && should_fail(&null_init_hctx_attr, 1))
+ if (should_init_hctx_fail(nullb->dev))
return -EFAULT;
-#endif
nq = &nullb->queues[hctx_idx];
hctx->driver_data = nq;
null_init_queue(nullb, nq);
- nullb->nr_queues++;
return 0;
}
static const struct blk_mq_ops null_mq_ops = {
.queue_rq = null_queue_rq,
+ .queue_rqs = null_queue_rqs,
.complete = null_complete_rq,
.timeout = null_timeout_rq,
.poll = null_poll,
.map_queues = null_map_queues,
.init_hctx = null_init_hctx,
- .exit_hctx = null_exit_hctx,
};
static void null_del_dev(struct nullb *nullb)
@@ -1756,7 +1754,7 @@ static void null_del_dev(struct nullb *nullb)
dev = nullb->dev;
- ida_simple_remove(&nullb_indexes, nullb->index);
+ ida_free(&nullb_indexes, nullb->index);
list_del_init(&nullb->list);
@@ -1765,21 +1763,20 @@ static void null_del_dev(struct nullb *nullb)
if (test_bit(NULLB_DEV_FL_THROTTLED, &nullb->dev->flags)) {
hrtimer_cancel(&nullb->bw_timer);
atomic_long_set(&nullb->cur_bytes, LONG_MAX);
- null_restart_queue_async(nullb);
+ blk_mq_start_stopped_hw_queues(nullb->q, true);
}
put_disk(nullb->disk);
- if (dev->queue_mode == NULL_Q_MQ &&
- nullb->tag_set == &nullb->__tag_set)
+ if (nullb->tag_set == &nullb->__tag_set)
blk_mq_free_tag_set(nullb->tag_set);
- cleanup_queues(nullb);
+ kfree(nullb->queues);
if (null_cache_active(nullb))
null_free_device_storage(nullb->dev, true);
kfree(nullb);
dev->nullb = NULL;
}
-static void null_config_discard(struct nullb *nullb)
+static void null_config_discard(struct nullb *nullb, struct queue_limits *lim)
{
if (nullb->dev->discard == false)
return;
@@ -1796,44 +1793,14 @@ static void null_config_discard(struct nullb *nullb)
return;
}
- nullb->q->limits.discard_granularity = nullb->dev->blocksize;
- blk_queue_max_discard_sectors(nullb->q, UINT_MAX >> 9);
+ lim->max_hw_discard_sectors = UINT_MAX >> 9;
}
-static const struct block_device_operations null_bio_ops = {
+static const struct block_device_operations null_ops = {
.owner = THIS_MODULE,
- .submit_bio = null_submit_bio,
.report_zones = null_report_zones,
};
-static const struct block_device_operations null_rq_ops = {
- .owner = THIS_MODULE,
- .report_zones = null_report_zones,
-};
-
-static int setup_commands(struct nullb_queue *nq)
-{
- struct nullb_cmd *cmd;
- int i;
-
- nq->cmds = kcalloc(nq->queue_depth, sizeof(*cmd), GFP_KERNEL);
- if (!nq->cmds)
- return -ENOMEM;
-
- nq->tag_map = bitmap_zalloc(nq->queue_depth, GFP_KERNEL);
- if (!nq->tag_map) {
- kfree(nq->cmds);
- return -ENOMEM;
- }
-
- for (i = 0; i < nq->queue_depth; i++) {
- cmd = &nq->cmds[i];
- cmd->tag = -1U;
- }
-
- return 0;
-}
-
static int setup_queues(struct nullb *nullb)
{
int nqueues = nr_cpu_ids;
@@ -1846,109 +1813,78 @@ static int setup_queues(struct nullb *nullb)
if (!nullb->queues)
return -ENOMEM;
- nullb->queue_depth = nullb->dev->hw_queue_depth;
return 0;
}
-static int init_driver_queues(struct nullb *nullb)
+static int null_init_tag_set(struct blk_mq_tag_set *set, int poll_queues)
{
- struct nullb_queue *nq;
- int i, ret = 0;
-
- for (i = 0; i < nullb->dev->submit_queues; i++) {
- nq = &nullb->queues[i];
-
- null_init_queue(nullb, nq);
-
- ret = setup_commands(nq);
- if (ret)
- return ret;
- nullb->nr_queues++;
+ set->ops = &null_mq_ops;
+ set->cmd_size = sizeof(struct nullb_cmd);
+ set->timeout = 5 * HZ;
+ set->nr_maps = 1;
+ if (poll_queues) {
+ set->nr_hw_queues += poll_queues;
+ set->nr_maps += 2;
}
- return 0;
+ return blk_mq_alloc_tag_set(set);
}
-static int null_gendisk_register(struct nullb *nullb)
+static int null_init_global_tag_set(void)
{
- sector_t size = ((sector_t)nullb->dev->size * SZ_1M) >> SECTOR_SHIFT;
- struct gendisk *disk = nullb->disk;
-
- set_capacity(disk, size);
-
- disk->major = null_major;
- disk->first_minor = nullb->index;
- disk->minors = 1;
- if (queue_is_mq(nullb->q))
- disk->fops = &null_rq_ops;
- else
- disk->fops = &null_bio_ops;
- disk->private_data = nullb;
- strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
+ int error;
- if (nullb->dev->zoned) {
- int ret = null_register_zoned_dev(nullb);
+ if (tag_set.ops)
+ return 0;
- if (ret)
- return ret;
- }
+ tag_set.nr_hw_queues = g_submit_queues;
+ tag_set.queue_depth = g_hw_queue_depth;
+ tag_set.numa_node = g_home_node;
+ if (g_no_sched)
+ tag_set.flags |= BLK_MQ_F_NO_SCHED_BY_DEFAULT;
+ if (g_shared_tag_bitmap)
+ tag_set.flags |= BLK_MQ_F_TAG_HCTX_SHARED;
+ if (g_blocking)
+ tag_set.flags |= BLK_MQ_F_BLOCKING;
- return add_disk(disk);
+ error = null_init_tag_set(&tag_set, g_poll_queues);
+ if (error)
+ tag_set.ops = NULL;
+ return error;
}
-static int null_init_tag_set(struct nullb *nullb, struct blk_mq_tag_set *set)
+static int null_setup_tagset(struct nullb *nullb)
{
- unsigned int flags = BLK_MQ_F_SHOULD_MERGE;
- int hw_queues, numa_node;
- unsigned int queue_depth;
- int poll_queues;
-
- if (nullb) {
- hw_queues = nullb->dev->submit_queues;
- poll_queues = nullb->dev->poll_queues;
- queue_depth = nullb->dev->hw_queue_depth;
- numa_node = nullb->dev->home_node;
- if (nullb->dev->no_sched)
- flags |= BLK_MQ_F_NO_SCHED;
- if (nullb->dev->shared_tag_bitmap)
- flags |= BLK_MQ_F_TAG_HCTX_SHARED;
- if (nullb->dev->blocking)
- flags |= BLK_MQ_F_BLOCKING;
- } else {
- hw_queues = g_submit_queues;
- poll_queues = g_poll_queues;
- queue_depth = g_hw_queue_depth;
- numa_node = g_home_node;
- if (g_no_sched)
- flags |= BLK_MQ_F_NO_SCHED;
- if (g_shared_tag_bitmap)
- flags |= BLK_MQ_F_TAG_HCTX_SHARED;
- if (g_blocking)
- flags |= BLK_MQ_F_BLOCKING;
+ if (nullb->dev->shared_tags) {
+ nullb->tag_set = &tag_set;
+ return null_init_global_tag_set();
}
- set->ops = &null_mq_ops;
- set->cmd_size = sizeof(struct nullb_cmd);
- set->flags = flags;
- set->driver_data = nullb;
- set->nr_hw_queues = hw_queues;
- set->queue_depth = queue_depth;
- set->numa_node = numa_node;
- if (poll_queues) {
- set->nr_hw_queues += poll_queues;
- set->nr_maps = 3;
- } else {
- set->nr_maps = 1;
- }
-
- return blk_mq_alloc_tag_set(set);
+ nullb->tag_set = &nullb->__tag_set;
+ nullb->tag_set->driver_data = nullb;
+ nullb->tag_set->nr_hw_queues = nullb->dev->submit_queues;
+ nullb->tag_set->queue_depth = nullb->dev->hw_queue_depth;
+ nullb->tag_set->numa_node = nullb->dev->home_node;
+ if (nullb->dev->no_sched)
+ nullb->tag_set->flags |= BLK_MQ_F_NO_SCHED_BY_DEFAULT;
+ if (nullb->dev->shared_tag_bitmap)
+ nullb->tag_set->flags |= BLK_MQ_F_TAG_HCTX_SHARED;
+ if (nullb->dev->blocking)
+ nullb->tag_set->flags |= BLK_MQ_F_BLOCKING;
+ return null_init_tag_set(nullb->tag_set, nullb->dev->poll_queues);
}
static int null_validate_conf(struct nullb_device *dev)
{
- dev->blocksize = round_down(dev->blocksize, 512);
- dev->blocksize = clamp_t(unsigned int, dev->blocksize, 512, 4096);
+ if (dev->queue_mode == NULL_Q_RQ) {
+ pr_err("legacy IO path is no longer available\n");
+ return -EINVAL;
+ }
+ if (dev->queue_mode == NULL_Q_BIO) {
+ pr_err("BIO-based IO path is no longer available, using blk-mq instead.\n");
+ dev->queue_mode = NULL_Q_MQ;
+ }
- if (dev->queue_mode == NULL_Q_MQ && dev->use_per_node_hctx) {
+ if (dev->use_per_node_hctx) {
if (dev->submit_queues != nr_online_nodes)
dev->submit_queues = nr_online_nodes;
} else if (dev->submit_queues > nr_cpu_ids)
@@ -1960,8 +1896,6 @@ static int null_validate_conf(struct nullb_device *dev)
if (dev->poll_queues > g_poll_queues)
dev->poll_queues = g_poll_queues;
dev->prev_poll_queues = dev->poll_queues;
-
- dev->queue_mode = min_t(unsigned int, dev->queue_mode, NULL_Q_MQ);
dev->irqmode = min_t(unsigned int, dev->irqmode, NULL_IRQ_TIMER);
/* Do memory allocation, so set blocking */
@@ -1972,9 +1906,6 @@ static int null_validate_conf(struct nullb_device *dev)
dev->cache_size = min_t(unsigned long, ULONG_MAX / 1024 / 1024,
dev->cache_size);
dev->mbps = min_t(unsigned int, 1024 * 40, dev->mbps);
- /* can not stop a queue */
- if (dev->queue_mode == NULL_Q_BIO)
- dev->mbps = 0;
if (dev->zoned &&
(!dev->zone_size || !is_power_of_2(dev->zone_size))) {
@@ -2014,6 +1945,13 @@ static bool null_setup_fault(void)
static int null_add_dev(struct nullb_device *dev)
{
+ struct queue_limits lim = {
+ .logical_block_size = dev->blocksize,
+ .physical_block_size = dev->blocksize,
+ .max_hw_sectors = dev->max_sectors,
+ .dma_alignment = 1,
+ };
+
struct nullb *nullb;
int rv;
@@ -2035,98 +1973,78 @@ static int null_add_dev(struct nullb_device *dev)
if (rv)
goto out_free_nullb;
- if (dev->queue_mode == NULL_Q_MQ) {
- if (shared_tags) {
- nullb->tag_set = &tag_set;
- rv = 0;
- } else {
- nullb->tag_set = &nullb->__tag_set;
- rv = null_init_tag_set(nullb, nullb->tag_set);
- }
+ rv = null_setup_tagset(nullb);
+ if (rv)
+ goto out_cleanup_queues;
+ if (dev->virt_boundary)
+ lim.virt_boundary_mask = PAGE_SIZE - 1;
+ null_config_discard(nullb, &lim);
+ if (dev->zoned) {
+ rv = null_init_zoned_dev(dev, &lim);
if (rv)
- goto out_cleanup_queues;
-
- if (!null_setup_fault())
goto out_cleanup_tags;
+ }
- nullb->tag_set->timeout = 5 * HZ;
- nullb->disk = blk_mq_alloc_disk(nullb->tag_set, nullb);
- if (IS_ERR(nullb->disk)) {
- rv = PTR_ERR(nullb->disk);
- goto out_cleanup_tags;
- }
- nullb->q = nullb->disk->queue;
- } else if (dev->queue_mode == NULL_Q_BIO) {
- rv = -ENOMEM;
- nullb->disk = blk_alloc_disk(nullb->dev->home_node);
- if (!nullb->disk)
- goto out_cleanup_queues;
+ if (dev->cache_size > 0) {
+ set_bit(NULLB_DEV_FL_CACHE, &nullb->dev->flags);
+ lim.features |= BLK_FEAT_WRITE_CACHE;
+ if (dev->fua)
+ lim.features |= BLK_FEAT_FUA;
+ }
- nullb->q = nullb->disk->queue;
- rv = init_driver_queues(nullb);
- if (rv)
- goto out_cleanup_disk;
+ if (dev->rotational)
+ lim.features |= BLK_FEAT_ROTATIONAL;
+
+ nullb->disk = blk_mq_alloc_disk(nullb->tag_set, &lim, nullb);
+ if (IS_ERR(nullb->disk)) {
+ rv = PTR_ERR(nullb->disk);
+ goto out_cleanup_zone;
}
+ nullb->q = nullb->disk->queue;
if (dev->mbps) {
set_bit(NULLB_DEV_FL_THROTTLED, &dev->flags);
nullb_setup_bwtimer(nullb);
}
- if (dev->cache_size > 0) {
- set_bit(NULLB_DEV_FL_CACHE, &nullb->dev->flags);
- blk_queue_write_cache(nullb->q, true, true);
- }
-
- if (dev->zoned) {
- rv = null_init_zoned_dev(dev, nullb->q);
- if (rv)
- goto out_cleanup_disk;
- }
-
nullb->q->queuedata = nullb;
- blk_queue_flag_set(QUEUE_FLAG_NONROT, nullb->q);
- blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, nullb->q);
- mutex_lock(&lock);
- rv = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL);
- if (rv < 0) {
- mutex_unlock(&lock);
- goto out_cleanup_zone;
- }
+ rv = ida_alloc(&nullb_indexes, GFP_KERNEL);
+ if (rv < 0)
+ goto out_cleanup_disk;
+
nullb->index = rv;
dev->index = rv;
- mutex_unlock(&lock);
-
- blk_queue_logical_block_size(nullb->q, dev->blocksize);
- blk_queue_physical_block_size(nullb->q, dev->blocksize);
- if (!dev->max_sectors)
- dev->max_sectors = queue_max_hw_sectors(nullb->q);
- dev->max_sectors = min_t(unsigned int, dev->max_sectors,
- BLK_DEF_MAX_SECTORS);
- blk_queue_max_hw_sectors(nullb->q, dev->max_sectors);
-
- if (dev->virt_boundary)
- blk_queue_virt_boundary(nullb->q, PAGE_SIZE - 1);
-
- null_config_discard(nullb);
- if (config_item_name(&dev->item)) {
+ if (config_item_name(&dev->group.cg_item)) {
/* Use configfs dir name as the device name */
snprintf(nullb->disk_name, sizeof(nullb->disk_name),
- "%s", config_item_name(&dev->item));
+ "%s", config_item_name(&dev->group.cg_item));
} else {
sprintf(nullb->disk_name, "nullb%d", nullb->index);
}
- rv = null_gendisk_register(nullb);
+ set_capacity(nullb->disk,
+ ((sector_t)nullb->dev->size * SZ_1M) >> SECTOR_SHIFT);
+ nullb->disk->major = null_major;
+ nullb->disk->first_minor = nullb->index;
+ nullb->disk->minors = 1;
+ nullb->disk->fops = &null_ops;
+ nullb->disk->private_data = nullb;
+ strscpy(nullb->disk->disk_name, nullb->disk_name);
+
+ if (nullb->dev->zoned) {
+ rv = null_register_zoned_dev(nullb);
+ if (rv)
+ goto out_ida_free;
+ }
+
+ rv = add_disk(nullb->disk);
if (rv)
goto out_ida_free;
- mutex_lock(&lock);
list_add_tail(&nullb->list, &nullb_list);
- mutex_unlock(&lock);
pr_info("disk %s created\n", nullb->disk_name);
@@ -2134,15 +2052,15 @@ static int null_add_dev(struct nullb_device *dev)
out_ida_free:
ida_free(&nullb_indexes, nullb->index);
-out_cleanup_zone:
- null_free_zoned_dev(dev);
out_cleanup_disk:
put_disk(nullb->disk);
+out_cleanup_zone:
+ null_free_zoned_dev(dev);
out_cleanup_tags:
- if (dev->queue_mode == NULL_Q_MQ && nullb->tag_set == &nullb->__tag_set)
+ if (nullb->tag_set == &nullb->__tag_set)
blk_mq_free_tag_set(nullb->tag_set);
out_cleanup_queues:
- cleanup_queues(nullb);
+ kfree(nullb->queues);
out_free_nullb:
kfree(nullb);
dev->nullb = NULL;
@@ -2175,7 +2093,9 @@ static int null_create_dev(void)
if (!dev)
return -ENOMEM;
+ mutex_lock(&lock);
ret = null_add_dev(dev);
+ mutex_unlock(&lock);
if (ret) {
null_free_dev(dev);
return ret;
@@ -2189,6 +2109,7 @@ static void null_destroy_dev(struct nullb *nullb)
struct nullb_device *dev = nullb->dev;
null_del_dev(nullb);
+ null_free_device_storage(dev, false);
null_free_dev(dev);
}
@@ -2204,23 +2125,20 @@ static int __init null_init(void)
g_bs = PAGE_SIZE;
}
- if (g_max_sectors > BLK_DEF_MAX_SECTORS) {
- pr_warn("invalid max sectors\n");
- pr_warn("defaults max sectors to %u\n", BLK_DEF_MAX_SECTORS);
- g_max_sectors = BLK_DEF_MAX_SECTORS;
- }
-
if (g_home_node != NUMA_NO_NODE && g_home_node >= nr_online_nodes) {
pr_err("invalid home_node value\n");
g_home_node = NUMA_NO_NODE;
}
+ if (!null_setup_fault())
+ return -EINVAL;
+
if (g_queue_mode == NULL_Q_RQ) {
pr_err("legacy IO path is no longer available\n");
return -EINVAL;
}
- if (g_queue_mode == NULL_Q_MQ && g_use_per_node_hctx) {
+ if (g_use_per_node_hctx) {
if (g_submit_queues != nr_online_nodes) {
pr_warn("submit_queues param is set to %u.\n",
nr_online_nodes);
@@ -2232,18 +2150,12 @@ static int __init null_init(void)
g_submit_queues = 1;
}
- if (g_queue_mode == NULL_Q_MQ && shared_tags) {
- ret = null_init_tag_set(NULL, &tag_set);
- if (ret)
- return ret;
- }
-
config_group_init(&nullb_subsys.su_group);
mutex_init(&nullb_subsys.su_mutex);
ret = configfs_register_subsystem(&nullb_subsys);
if (ret)
- goto err_tagset;
+ return ret;
mutex_init(&lock);
@@ -2270,9 +2182,6 @@ err_dev:
unregister_blkdev(null_major, "nullb");
err_conf:
configfs_unregister_subsystem(&nullb_subsys);
-err_tagset:
- if (g_queue_mode == NULL_Q_MQ && shared_tags)
- blk_mq_free_tag_set(&tag_set);
return ret;
}
@@ -2291,12 +2200,15 @@ static void __exit null_exit(void)
}
mutex_unlock(&lock);
- if (g_queue_mode == NULL_Q_MQ && shared_tags)
+ if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
+
+ mutex_destroy(&lock);
}
module_init(null_init);
module_exit(null_exit);
MODULE_AUTHOR("Jens Axboe <axboe@kernel.dk>");
+MODULE_DESCRIPTION("multi queue aware block test driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/block/null_blk/null_blk.h b/drivers/block/null_blk/null_blk.h
index 94ff68052b1e..6c4c4bbe7dad 100644
--- a/drivers/block/null_blk/null_blk.h
+++ b/drivers/block/null_blk/null_blk.h
@@ -16,11 +16,6 @@
#include <linux/mutex.h>
struct nullb_cmd {
- union {
- struct request *rq;
- struct bio *bio;
- };
- unsigned int tag;
blk_status_t error;
bool fake_timeout;
struct nullb_queue *nq;
@@ -28,16 +23,11 @@ struct nullb_cmd {
};
struct nullb_queue {
- unsigned long *tag_map;
- wait_queue_head_t wait;
- unsigned int queue_depth;
struct nullb_device *dev;
unsigned int requeue_selection;
struct list_head poll_list;
spinlock_t poll_lock;
-
- struct nullb_cmd *cmds;
};
struct nullb_zone {
@@ -60,21 +50,21 @@ struct nullb_zone {
unsigned int capacity;
};
-/* Queue modes */
-enum {
- NULL_Q_BIO = 0,
- NULL_Q_RQ = 1,
- NULL_Q_MQ = 2,
-};
-
struct nullb_device {
struct nullb *nullb;
- struct config_item item;
+ struct config_group group;
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+ struct fault_config timeout_config;
+ struct fault_config requeue_config;
+ struct fault_config init_hctx_fault_config;
+#endif
struct radix_tree_root data; /* data stored in the disk */
struct radix_tree_root cache; /* disk cache data */
unsigned long flags; /* device flags */
unsigned int curr_cache;
struct badblocks badblocks;
+ bool badblocks_once;
+ bool badblocks_partial_io;
unsigned int nr_zones;
unsigned int nr_zones_imp_open;
@@ -94,6 +84,7 @@ struct nullb_device {
unsigned int zone_nr_conv; /* number of conventional zones */
unsigned int zone_max_open; /* max number of open zones */
unsigned int zone_max_active; /* max number of active zones */
+ unsigned int zone_append_max_sectors; /* Max sectors per zone append command */
unsigned int submit_queues; /* number of submission queues */
unsigned int prev_submit_queues; /* number of submission queues before change */
unsigned int poll_queues; /* number of IOPOLL submission queues */
@@ -112,9 +103,13 @@ struct nullb_device {
bool memory_backed; /* if data is stored in memory */
bool discard; /* if support discard */
bool zoned; /* if device is zoned */
+ bool zone_full; /* Initialize zones to be full */
bool virt_boundary; /* virtual boundary on/off for the device */
bool no_sched; /* no IO scheduler for the device */
+ bool shared_tags; /* share tag set between devices for blk-mq */
bool shared_tag_bitmap; /* use hostwide shared tags */
+ bool fua; /* Support FUA */
+ bool rotational; /* Fake rotational device */
};
struct nullb {
@@ -125,14 +120,12 @@ struct nullb {
struct gendisk *disk;
struct blk_mq_tag_set *tag_set;
struct blk_mq_tag_set __tag_set;
- unsigned int queue_depth;
atomic_long_t cur_bytes;
struct hrtimer bw_timer;
unsigned long cache_flush_pos;
spinlock_t lock;
struct nullb_queue *queues;
- unsigned int nr_queues;
char disk_name[DISK_NAME_LEN];
};
@@ -140,20 +133,27 @@ blk_status_t null_handle_discard(struct nullb_device *dev, sector_t sector,
sector_t nr_sectors);
blk_status_t null_process_cmd(struct nullb_cmd *cmd, enum req_op op,
sector_t sector, unsigned int nr_sectors);
+blk_status_t null_handle_badblocks(struct nullb_cmd *cmd, sector_t sector,
+ unsigned int *nr_sectors);
+blk_status_t null_handle_memory_backed(struct nullb_cmd *cmd, enum req_op op,
+ sector_t sector, sector_t nr_sectors);
#ifdef CONFIG_BLK_DEV_ZONED
-int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q);
+int null_init_zoned_dev(struct nullb_device *dev, struct queue_limits *lim);
int null_register_zoned_dev(struct nullb *nullb);
void null_free_zoned_dev(struct nullb_device *dev);
int null_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data);
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args);
blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
sector_t sector, sector_t nr_sectors);
size_t null_zone_valid_read_len(struct nullb *nullb,
sector_t sector, unsigned int len);
+ssize_t zone_cond_store(struct nullb_device *dev, const char *page,
+ size_t count, enum blk_zone_cond cond);
#else
static inline int null_init_zoned_dev(struct nullb_device *dev,
- struct request_queue *q)
+ struct queue_limits *lim)
{
pr_err("CONFIG_BLK_DEV_ZONED not enabled\n");
return -EINVAL;
@@ -174,6 +174,12 @@ static inline size_t null_zone_valid_read_len(struct nullb *nullb,
{
return len;
}
+static inline ssize_t zone_cond_store(struct nullb_device *dev,
+ const char *page, size_t count,
+ enum blk_zone_cond cond)
+{
+ return -EOPNOTSUPP;
+}
#define null_report_zones NULL
#endif /* CONFIG_BLK_DEV_ZONED */
#endif /* __NULL_BLK_H */
diff --git a/drivers/block/null_blk/trace.h b/drivers/block/null_blk/trace.h
index 6b2b370e786f..82b8f6a5e5f0 100644
--- a/drivers/block/null_blk/trace.h
+++ b/drivers/block/null_blk/trace.h
@@ -36,15 +36,21 @@ TRACE_EVENT(nullb_zone_op,
TP_ARGS(cmd, zone_no, zone_cond),
TP_STRUCT__entry(
__array(char, disk, DISK_NAME_LEN)
- __field(enum req_op, op)
+ /*
+ * __field() uses is_signed_type(). is_signed_type() does not
+ * support bitwise types. Use __field_struct() instead because
+ * it does not use is_signed_type().
+ */
+ __field_struct(enum req_op, op)
__field(unsigned int, zone_no)
__field(unsigned int, zone_cond)
),
TP_fast_assign(
- __entry->op = req_op(cmd->rq);
+ __entry->op = req_op(blk_mq_rq_from_pdu(cmd));
__entry->zone_no = zone_no;
__entry->zone_cond = zone_cond;
- __assign_disk_name(__entry->disk, cmd->rq->q->disk);
+ __assign_disk_name(__entry->disk,
+ blk_mq_rq_from_pdu(cmd)->q->disk);
),
TP_printk("%s req=%-15s zone_no=%u zone_cond=%-10s",
__print_disk_name(__entry->disk),
diff --git a/drivers/block/null_blk/zoned.c b/drivers/block/null_blk/zoned.c
index 55a69e48ef8b..0ada35dc0989 100644
--- a/drivers/block/null_blk/zoned.c
+++ b/drivers/block/null_blk/zoned.c
@@ -9,6 +9,8 @@
#undef pr_fmt
#define pr_fmt(fmt) "null_blk: " fmt
+#define NULL_ZONE_INVALID_WP ((sector_t)-1)
+
static inline sector_t mb_to_sects(unsigned long mb)
{
return ((sector_t)mb * SZ_1M) >> SECTOR_SHIFT;
@@ -19,18 +21,6 @@ static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
return sect >> ilog2(dev->zone_size_sects);
}
-static inline void null_lock_zone_res(struct nullb_device *dev)
-{
- if (dev->need_zone_res_mgmt)
- spin_lock_irq(&dev->zone_res_lock);
-}
-
-static inline void null_unlock_zone_res(struct nullb_device *dev)
-{
- if (dev->need_zone_res_mgmt)
- spin_unlock_irq(&dev->zone_res_lock);
-}
-
static inline void null_init_zone_lock(struct nullb_device *dev,
struct nullb_zone *zone)
{
@@ -58,7 +48,8 @@ static inline void null_unlock_zone(struct nullb_device *dev,
mutex_unlock(&zone->mutex);
}
-int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
+int null_init_zoned_dev(struct nullb_device *dev,
+ struct queue_limits *lim)
{
sector_t dev_capacity_sects, zone_capacity_sects;
struct nullb_zone *zone;
@@ -83,6 +74,17 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
return -EINVAL;
}
+ /*
+ * If a smaller zone capacity was requested, do not allow a smaller last
+ * zone at the same time as such zone configuration does not correspond
+ * to any real zoned device.
+ */
+ if (dev->zone_capacity != dev->zone_size &&
+ dev->size & (dev->zone_size - 1)) {
+ pr_err("A smaller last zone is not allowed with zone capacity smaller than zone size.\n");
+ return -EINVAL;
+ }
+
zone_capacity_sects = mb_to_sects(dev->zone_capacity);
dev_capacity_sects = mb_to_sects(dev->size);
dev->zone_size_sects = mb_to_sects(dev->zone_size);
@@ -102,6 +104,11 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
dev->zone_nr_conv);
}
+ dev->zone_append_max_sectors =
+ min(ALIGN_DOWN(dev->zone_append_max_sectors,
+ dev->blocksize >> SECTOR_SHIFT),
+ zone_capacity_sects);
+
/* Max active zones has to be < nbr of seq zones in order to be enforceable */
if (dev->zone_max_active >= dev->nr_zones - dev->zone_nr_conv) {
dev->zone_max_active = 0;
@@ -112,7 +119,7 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
if (dev->zone_max_active && dev->zone_max_open > dev->zone_max_active) {
dev->zone_max_open = dev->zone_max_active;
pr_info("changed the maximum number of open zones to %u\n",
- dev->nr_zones);
+ dev->zone_max_open);
} else if (dev->zone_max_open >= dev->nr_zones - dev->zone_nr_conv) {
dev->zone_max_open = 0;
pr_info("zone_max_open limit disabled, limit >= zone count\n");
@@ -138,7 +145,7 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
zone = &dev->zones[i];
null_init_zone_lock(dev, zone);
- zone->start = zone->wp = sector;
+ zone->start = sector;
if (zone->start + dev->zone_size_sects > dev_capacity_sects)
zone->len = dev_capacity_sects - zone->start;
else
@@ -146,38 +153,35 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
zone->capacity =
min_t(sector_t, zone->len, zone_capacity_sects);
zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
- zone->cond = BLK_ZONE_COND_EMPTY;
+ if (dev->zone_full) {
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = zone->start + zone->capacity;
+ } else{
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ zone->wp = zone->start;
+ }
sector += dev->zone_size_sects;
}
+ lim->features |= BLK_FEAT_ZONED;
+ lim->chunk_sectors = dev->zone_size_sects;
+ lim->max_hw_zone_append_sectors = dev->zone_append_max_sectors;
+ lim->max_open_zones = dev->zone_max_open;
+ lim->max_active_zones = dev->zone_max_active;
return 0;
}
int null_register_zoned_dev(struct nullb *nullb)
{
- struct nullb_device *dev = nullb->dev;
struct request_queue *q = nullb->q;
+ struct gendisk *disk = nullb->disk;
- disk_set_zoned(nullb->disk, BLK_ZONED_HM);
- blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, q);
- blk_queue_required_elevator_features(q, ELEVATOR_F_ZBD_SEQ_WRITE);
-
- if (queue_is_mq(q)) {
- int ret = blk_revalidate_disk_zones(nullb->disk, NULL);
+ pr_info("%s: using %s zone append\n",
+ disk->disk_name,
+ queue_emulates_zone_append(q) ? "emulated" : "native");
- if (ret)
- return ret;
- } else {
- blk_queue_chunk_sectors(q, dev->zone_size_sects);
- nullb->disk->nr_zones = bdev_nr_zones(nullb->disk->part0);
- }
-
- blk_queue_max_zone_append_sectors(q, dev->zone_size_sects);
- disk_set_max_open_zones(nullb->disk, dev->zone_max_open);
- disk_set_max_active_zones(nullb->disk, dev->zone_max_active);
-
- return 0;
+ return blk_revalidate_disk_zones(disk);
}
void null_free_zoned_dev(struct nullb_device *dev)
@@ -187,7 +191,7 @@ void null_free_zoned_dev(struct nullb_device *dev)
}
int null_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
struct nullb *nullb = disk->private_data;
struct nullb_device *dev = nullb->dev;
@@ -221,7 +225,7 @@ int null_report_zones(struct gendisk *disk, sector_t sector,
blkz.capacity = zone->capacity;
null_unlock_zone(dev, zone);
- error = cb(&blkz, i, data);
+ error = disk_report_zone(disk, &blkz, i, args);
if (error)
return error;
}
@@ -238,7 +242,7 @@ size_t null_zone_valid_read_len(struct nullb *nullb,
{
struct nullb_device *dev = nullb->dev;
struct nullb_zone *zone = &dev->zones[null_zone_no(dev, sector)];
- unsigned int nr_sectors = len >> SECTOR_SHIFT;
+ unsigned int nr_sectors = DIV_ROUND_UP(len, SECTOR_SIZE);
/* Read must be below the write pointer position */
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL ||
@@ -251,35 +255,6 @@ size_t null_zone_valid_read_len(struct nullb *nullb,
return (zone->wp - sector) << SECTOR_SHIFT;
}
-static blk_status_t __null_close_zone(struct nullb_device *dev,
- struct nullb_zone *zone)
-{
- switch (zone->cond) {
- case BLK_ZONE_COND_CLOSED:
- /* close operation on closed is not an error */
- return BLK_STS_OK;
- case BLK_ZONE_COND_IMP_OPEN:
- dev->nr_zones_imp_open--;
- break;
- case BLK_ZONE_COND_EXP_OPEN:
- dev->nr_zones_exp_open--;
- break;
- case BLK_ZONE_COND_EMPTY:
- case BLK_ZONE_COND_FULL:
- default:
- return BLK_STS_IOERR;
- }
-
- if (zone->wp == zone->start) {
- zone->cond = BLK_ZONE_COND_EMPTY;
- } else {
- zone->cond = BLK_ZONE_COND_CLOSED;
- dev->nr_zones_closed++;
- }
-
- return BLK_STS_OK;
-}
-
static void null_close_imp_open_zone(struct nullb_device *dev)
{
struct nullb_zone *zone;
@@ -296,7 +271,13 @@ static void null_close_imp_open_zone(struct nullb_device *dev)
zno = dev->zone_nr_conv;
if (zone->cond == BLK_ZONE_COND_IMP_OPEN) {
- __null_close_zone(dev, zone);
+ dev->nr_zones_imp_open--;
+ if (zone->wp == zone->start) {
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ } else {
+ zone->cond = BLK_ZONE_COND_CLOSED;
+ dev->nr_zones_closed++;
+ }
dev->imp_close_zone_no = zno;
return;
}
@@ -372,6 +353,7 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
struct nullb_device *dev = cmd->nq->dev;
unsigned int zno = null_zone_no(dev, sector);
struct nullb_zone *zone = &dev->zones[zno];
+ blk_status_t badblocks_ret = BLK_STS_OK;
blk_status_t ret;
trace_nullb_zone_op(cmd, zno, zone->cond);
@@ -384,74 +366,84 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
null_lock_zone(dev, zone);
- if (zone->cond == BLK_ZONE_COND_FULL) {
- /* Cannot write to a full zone */
- ret = BLK_STS_IOERR;
- goto unlock;
- }
-
/*
- * Regular writes must be at the write pointer position.
- * Zone append writes are automatically issued at the write
- * pointer and the position returned using the request or BIO
- * sector.
+ * Regular writes must be at the write pointer position. Zone append
+ * writes are automatically issued at the write pointer and the position
+ * returned using the request sector. Note that we do not check the zone
+ * condition because for FULL, READONLY and OFFLINE zones, the sector
+ * check against the zone write pointer will always result in failing
+ * the command.
*/
if (append) {
+ if (WARN_ON_ONCE(!dev->zone_append_max_sectors) ||
+ zone->wp == NULL_ZONE_INVALID_WP) {
+ ret = BLK_STS_IOERR;
+ goto unlock_zone;
+ }
sector = zone->wp;
- if (dev->queue_mode == NULL_Q_MQ)
- cmd->rq->__sector = sector;
- else
- cmd->bio->bi_iter.bi_sector = sector;
- } else if (sector != zone->wp) {
- ret = BLK_STS_IOERR;
- goto unlock;
+ blk_mq_rq_from_pdu(cmd)->__sector = sector;
}
- if (zone->wp + nr_sectors > zone->start + zone->capacity) {
+ if (sector != zone->wp ||
+ zone->wp + nr_sectors > zone->start + zone->capacity) {
ret = BLK_STS_IOERR;
- goto unlock;
+ goto unlock_zone;
}
if (zone->cond == BLK_ZONE_COND_CLOSED ||
zone->cond == BLK_ZONE_COND_EMPTY) {
- null_lock_zone_res(dev);
+ if (dev->need_zone_res_mgmt) {
+ spin_lock(&dev->zone_res_lock);
- ret = null_check_zone_resources(dev, zone);
- if (ret != BLK_STS_OK) {
- null_unlock_zone_res(dev);
- goto unlock;
- }
- if (zone->cond == BLK_ZONE_COND_CLOSED) {
- dev->nr_zones_closed--;
- dev->nr_zones_imp_open++;
- } else if (zone->cond == BLK_ZONE_COND_EMPTY) {
- dev->nr_zones_imp_open++;
+ ret = null_check_zone_resources(dev, zone);
+ if (ret != BLK_STS_OK) {
+ spin_unlock(&dev->zone_res_lock);
+ goto unlock_zone;
+ }
+ if (zone->cond == BLK_ZONE_COND_CLOSED) {
+ dev->nr_zones_closed--;
+ dev->nr_zones_imp_open++;
+ } else if (zone->cond == BLK_ZONE_COND_EMPTY) {
+ dev->nr_zones_imp_open++;
+ }
+
+ spin_unlock(&dev->zone_res_lock);
}
- if (zone->cond != BLK_ZONE_COND_EXP_OPEN)
- zone->cond = BLK_ZONE_COND_IMP_OPEN;
+ zone->cond = BLK_ZONE_COND_IMP_OPEN;
+ }
- null_unlock_zone_res(dev);
+ if (dev->badblocks.shift != -1) {
+ badblocks_ret = null_handle_badblocks(cmd, sector, &nr_sectors);
+ if (badblocks_ret != BLK_STS_OK && !nr_sectors) {
+ ret = badblocks_ret;
+ goto unlock_zone;
+ }
}
- ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
- if (ret != BLK_STS_OK)
- goto unlock;
+ if (dev->memory_backed) {
+ ret = null_handle_memory_backed(cmd, REQ_OP_WRITE, sector,
+ nr_sectors);
+ if (ret != BLK_STS_OK)
+ goto unlock_zone;
+ }
zone->wp += nr_sectors;
if (zone->wp == zone->start + zone->capacity) {
- null_lock_zone_res(dev);
- if (zone->cond == BLK_ZONE_COND_EXP_OPEN)
- dev->nr_zones_exp_open--;
- else if (zone->cond == BLK_ZONE_COND_IMP_OPEN)
- dev->nr_zones_imp_open--;
+ if (dev->need_zone_res_mgmt) {
+ spin_lock(&dev->zone_res_lock);
+ if (zone->cond == BLK_ZONE_COND_EXP_OPEN)
+ dev->nr_zones_exp_open--;
+ else if (zone->cond == BLK_ZONE_COND_IMP_OPEN)
+ dev->nr_zones_imp_open--;
+ spin_unlock(&dev->zone_res_lock);
+ }
zone->cond = BLK_ZONE_COND_FULL;
- null_unlock_zone_res(dev);
}
- ret = BLK_STS_OK;
+ ret = badblocks_ret;
-unlock:
+unlock_zone:
null_unlock_zone(dev, zone);
return ret;
@@ -465,54 +457,100 @@ static blk_status_t null_open_zone(struct nullb_device *dev,
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
return BLK_STS_IOERR;
- null_lock_zone_res(dev);
-
switch (zone->cond) {
case BLK_ZONE_COND_EXP_OPEN:
- /* open operation on exp open is not an error */
- goto unlock;
+ /* Open operation on exp open is not an error */
+ return BLK_STS_OK;
case BLK_ZONE_COND_EMPTY:
- ret = null_check_zone_resources(dev, zone);
- if (ret != BLK_STS_OK)
- goto unlock;
- break;
case BLK_ZONE_COND_IMP_OPEN:
- dev->nr_zones_imp_open--;
- break;
case BLK_ZONE_COND_CLOSED:
- ret = null_check_zone_resources(dev, zone);
- if (ret != BLK_STS_OK)
- goto unlock;
- dev->nr_zones_closed--;
break;
case BLK_ZONE_COND_FULL:
default:
- ret = BLK_STS_IOERR;
- goto unlock;
+ return BLK_STS_IOERR;
}
- zone->cond = BLK_ZONE_COND_EXP_OPEN;
- dev->nr_zones_exp_open++;
+ if (dev->need_zone_res_mgmt) {
+ spin_lock(&dev->zone_res_lock);
-unlock:
- null_unlock_zone_res(dev);
+ switch (zone->cond) {
+ case BLK_ZONE_COND_EMPTY:
+ ret = null_check_zone_resources(dev, zone);
+ if (ret != BLK_STS_OK) {
+ spin_unlock(&dev->zone_res_lock);
+ return ret;
+ }
+ break;
+ case BLK_ZONE_COND_IMP_OPEN:
+ dev->nr_zones_imp_open--;
+ break;
+ case BLK_ZONE_COND_CLOSED:
+ ret = null_check_zone_resources(dev, zone);
+ if (ret != BLK_STS_OK) {
+ spin_unlock(&dev->zone_res_lock);
+ return ret;
+ }
+ dev->nr_zones_closed--;
+ break;
+ default:
+ break;
+ }
- return ret;
+ dev->nr_zones_exp_open++;
+
+ spin_unlock(&dev->zone_res_lock);
+ }
+
+ zone->cond = BLK_ZONE_COND_EXP_OPEN;
+
+ return BLK_STS_OK;
}
static blk_status_t null_close_zone(struct nullb_device *dev,
struct nullb_zone *zone)
{
- blk_status_t ret;
-
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
return BLK_STS_IOERR;
- null_lock_zone_res(dev);
- ret = __null_close_zone(dev, zone);
- null_unlock_zone_res(dev);
+ switch (zone->cond) {
+ case BLK_ZONE_COND_CLOSED:
+ /* close operation on closed is not an error */
+ return BLK_STS_OK;
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ break;
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ default:
+ return BLK_STS_IOERR;
+ }
+
+ if (dev->need_zone_res_mgmt) {
+ spin_lock(&dev->zone_res_lock);
- return ret;
+ switch (zone->cond) {
+ case BLK_ZONE_COND_IMP_OPEN:
+ dev->nr_zones_imp_open--;
+ break;
+ case BLK_ZONE_COND_EXP_OPEN:
+ dev->nr_zones_exp_open--;
+ break;
+ default:
+ break;
+ }
+
+ if (zone->wp > zone->start)
+ dev->nr_zones_closed++;
+
+ spin_unlock(&dev->zone_res_lock);
+ }
+
+ if (zone->wp == zone->start)
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ else
+ zone->cond = BLK_ZONE_COND_CLOSED;
+
+ return BLK_STS_OK;
}
static blk_status_t null_finish_zone(struct nullb_device *dev,
@@ -523,41 +561,47 @@ static blk_status_t null_finish_zone(struct nullb_device *dev,
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
return BLK_STS_IOERR;
- null_lock_zone_res(dev);
+ if (dev->need_zone_res_mgmt) {
+ spin_lock(&dev->zone_res_lock);
- switch (zone->cond) {
- case BLK_ZONE_COND_FULL:
- /* finish operation on full is not an error */
- goto unlock;
- case BLK_ZONE_COND_EMPTY:
- ret = null_check_zone_resources(dev, zone);
- if (ret != BLK_STS_OK)
- goto unlock;
- break;
- case BLK_ZONE_COND_IMP_OPEN:
- dev->nr_zones_imp_open--;
- break;
- case BLK_ZONE_COND_EXP_OPEN:
- dev->nr_zones_exp_open--;
- break;
- case BLK_ZONE_COND_CLOSED:
- ret = null_check_zone_resources(dev, zone);
- if (ret != BLK_STS_OK)
- goto unlock;
- dev->nr_zones_closed--;
- break;
- default:
- ret = BLK_STS_IOERR;
- goto unlock;
+ switch (zone->cond) {
+ case BLK_ZONE_COND_FULL:
+ /* Finish operation on full is not an error */
+ spin_unlock(&dev->zone_res_lock);
+ return BLK_STS_OK;
+ case BLK_ZONE_COND_EMPTY:
+ ret = null_check_zone_resources(dev, zone);
+ if (ret != BLK_STS_OK) {
+ spin_unlock(&dev->zone_res_lock);
+ return ret;
+ }
+ break;
+ case BLK_ZONE_COND_IMP_OPEN:
+ dev->nr_zones_imp_open--;
+ break;
+ case BLK_ZONE_COND_EXP_OPEN:
+ dev->nr_zones_exp_open--;
+ break;
+ case BLK_ZONE_COND_CLOSED:
+ ret = null_check_zone_resources(dev, zone);
+ if (ret != BLK_STS_OK) {
+ spin_unlock(&dev->zone_res_lock);
+ return ret;
+ }
+ dev->nr_zones_closed--;
+ break;
+ default:
+ spin_unlock(&dev->zone_res_lock);
+ return BLK_STS_IOERR;
+ }
+
+ spin_unlock(&dev->zone_res_lock);
}
zone->cond = BLK_ZONE_COND_FULL;
zone->wp = zone->start + zone->len;
-unlock:
- null_unlock_zone_res(dev);
-
- return ret;
+ return BLK_STS_OK;
}
static blk_status_t null_reset_zone(struct nullb_device *dev,
@@ -566,34 +610,33 @@ static blk_status_t null_reset_zone(struct nullb_device *dev,
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
return BLK_STS_IOERR;
- null_lock_zone_res(dev);
+ if (dev->need_zone_res_mgmt) {
+ spin_lock(&dev->zone_res_lock);
- switch (zone->cond) {
- case BLK_ZONE_COND_EMPTY:
- /* reset operation on empty is not an error */
- null_unlock_zone_res(dev);
- return BLK_STS_OK;
- case BLK_ZONE_COND_IMP_OPEN:
- dev->nr_zones_imp_open--;
- break;
- case BLK_ZONE_COND_EXP_OPEN:
- dev->nr_zones_exp_open--;
- break;
- case BLK_ZONE_COND_CLOSED:
- dev->nr_zones_closed--;
- break;
- case BLK_ZONE_COND_FULL:
- break;
- default:
- null_unlock_zone_res(dev);
- return BLK_STS_IOERR;
+ switch (zone->cond) {
+ case BLK_ZONE_COND_IMP_OPEN:
+ dev->nr_zones_imp_open--;
+ break;
+ case BLK_ZONE_COND_EXP_OPEN:
+ dev->nr_zones_exp_open--;
+ break;
+ case BLK_ZONE_COND_CLOSED:
+ dev->nr_zones_closed--;
+ break;
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ break;
+ default:
+ spin_unlock(&dev->zone_res_lock);
+ return BLK_STS_IOERR;
+ }
+
+ spin_unlock(&dev->zone_res_lock);
}
zone->cond = BLK_ZONE_COND_EMPTY;
zone->wp = zone->start;
- null_unlock_zone_res(dev);
-
if (dev->memory_backed)
return null_handle_discard(dev, zone->start, zone->len);
@@ -613,7 +656,9 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
zone = &dev->zones[i];
null_lock_zone(dev, zone);
- if (zone->cond != BLK_ZONE_COND_EMPTY) {
+ if (zone->cond != BLK_ZONE_COND_EMPTY &&
+ zone->cond != BLK_ZONE_COND_READONLY &&
+ zone->cond != BLK_ZONE_COND_OFFLINE) {
null_reset_zone(dev, zone);
trace_nullb_zone_op(cmd, i, zone->cond);
}
@@ -627,6 +672,12 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
null_lock_zone(dev, zone);
+ if (zone->cond == BLK_ZONE_COND_READONLY ||
+ zone->cond == BLK_ZONE_COND_OFFLINE) {
+ ret = BLK_STS_IOERR;
+ goto unlock;
+ }
+
switch (op) {
case REQ_OP_ZONE_RESET:
ret = null_reset_zone(dev, zone);
@@ -648,6 +699,7 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
if (ret == BLK_STS_OK)
trace_nullb_zone_op(cmd, zone_no, zone->cond);
+unlock:
null_unlock_zone(dev, zone);
return ret;
@@ -674,6 +726,8 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
default:
dev = cmd->nq->dev;
zone = &dev->zones[null_zone_no(dev, sector)];
+ if (zone->cond == BLK_ZONE_COND_OFFLINE)
+ return BLK_STS_IOERR;
null_lock_zone(dev, zone);
sts = null_process_cmd(cmd, op, sector, nr_sectors);
@@ -681,3 +735,79 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
return sts;
}
}
+
+/*
+ * Set a zone in the read-only or offline condition.
+ */
+static void null_set_zone_cond(struct nullb_device *dev,
+ struct nullb_zone *zone, enum blk_zone_cond cond)
+{
+ if (WARN_ON_ONCE(cond != BLK_ZONE_COND_READONLY &&
+ cond != BLK_ZONE_COND_OFFLINE))
+ return;
+
+ null_lock_zone(dev, zone);
+
+ /*
+ * If the read-only condition is requested again to zones already in
+ * read-only condition, restore back normal empty condition. Do the same
+ * if the offline condition is requested for offline zones. Otherwise,
+ * set the specified zone condition to the zones. Finish the zones
+ * beforehand to free up zone resources.
+ */
+ if (zone->cond == cond) {
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ zone->wp = zone->start;
+ if (dev->memory_backed)
+ null_handle_discard(dev, zone->start, zone->len);
+ } else {
+ if (zone->cond != BLK_ZONE_COND_READONLY &&
+ zone->cond != BLK_ZONE_COND_OFFLINE)
+ null_finish_zone(dev, zone);
+ zone->cond = cond;
+ zone->wp = NULL_ZONE_INVALID_WP;
+ }
+
+ null_unlock_zone(dev, zone);
+}
+
+/*
+ * Identify a zone from the sector written to configfs file. Then set zone
+ * condition to the zone.
+ */
+ssize_t zone_cond_store(struct nullb_device *dev, const char *page,
+ size_t count, enum blk_zone_cond cond)
+{
+ unsigned long long sector;
+ unsigned int zone_no;
+ int ret;
+
+ if (!dev->zoned) {
+ pr_err("null_blk device is not zoned\n");
+ return -EINVAL;
+ }
+
+ if (!dev->zones) {
+ pr_err("null_blk device is not yet powered\n");
+ return -EINVAL;
+ }
+
+ ret = kstrtoull(page, 0, &sector);
+ if (ret < 0)
+ return ret;
+
+ zone_no = null_zone_no(dev, sector);
+ if (zone_no >= dev->nr_zones) {
+ pr_err("Sector out of range\n");
+ return -EINVAL;
+ }
+
+ if (dev->zones[zone_no].type == BLK_ZONE_TYPE_CONVENTIONAL) {
+ pr_err("Can not change condition of conventional zones\n");
+ return -EINVAL;
+ }
+
+ null_set_zone_cond(dev, &dev->zones[zone_no], cond);
+
+ return count;
+}
diff --git a/drivers/block/paride/Kconfig b/drivers/block/paride/Kconfig
deleted file mode 100644
index a295634597ba..000000000000
--- a/drivers/block/paride/Kconfig
+++ /dev/null
@@ -1,302 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# PARIDE configuration
-#
-# PARIDE doesn't need PARPORT, but if PARPORT is configured as a module,
-# PARIDE must also be a module.
-# PARIDE only supports PC style parports. Tough for USB or other parports...
-
-comment "Parallel IDE high-level drivers"
- depends on PARIDE
-
-config PARIDE_PD
- tristate "Parallel port IDE disks"
- depends on PARIDE
- help
- This option enables the high-level driver for IDE-type disk devices
- connected through a parallel port. If you chose to build PARIDE
- support into your kernel, you may answer Y here to build in the
- parallel port IDE driver, otherwise you should answer M to build
- it as a loadable module. The module will be called pd. You
- must also have at least one parallel port protocol driver in your
- system. Among the devices supported by this driver are the SyQuest
- EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
- hard drives from MicroSolutions.
-
-config PARIDE_PCD
- tristate "Parallel port ATAPI CD-ROMs"
- depends on PARIDE
- select CDROM
- help
- This option enables the high-level driver for ATAPI CD-ROM devices
- connected through a parallel port. If you chose to build PARIDE
- support into your kernel, you may answer Y here to build in the
- parallel port ATAPI CD-ROM driver, otherwise you should answer M to
- build it as a loadable module. The module will be called pcd. You
- must also have at least one parallel port protocol driver in your
- system. Among the devices supported by this driver are the
- MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If
- you have such a CD-ROM drive, you should also say Y or M to "ISO
- 9660 CD-ROM file system support" below, because that's the file
- system used on CD-ROMs.
-
-config PARIDE_PF
- tristate "Parallel port ATAPI disks"
- depends on PARIDE
- help
- This option enables the high-level driver for ATAPI disk devices
- connected through a parallel port. If you chose to build PARIDE
- support into your kernel, you may answer Y here to build in the
- parallel port ATAPI disk driver, otherwise you should answer M
- to build it as a loadable module. The module will be called pf.
- You must also have at least one parallel port protocol driver in
- your system. Among the devices supported by this driver are the
- MicroSolutions backpack PD/CD drive and the Imation Superdisk
- LS-120 drive.
-
-config PARIDE_PT
- tristate "Parallel port ATAPI tapes"
- depends on PARIDE
- help
- This option enables the high-level driver for ATAPI tape devices
- connected through a parallel port. If you chose to build PARIDE
- support into your kernel, you may answer Y here to build in the
- parallel port ATAPI disk driver, otherwise you should answer M
- to build it as a loadable module. The module will be called pt.
- You must also have at least one parallel port protocol driver in
- your system. Among the devices supported by this driver is the
- parallel port version of the HP 5GB drive.
-
-config PARIDE_PG
- tristate "Parallel port generic ATAPI devices"
- depends on PARIDE
- help
- This option enables a special high-level driver for generic ATAPI
- devices connected through a parallel port. The driver allows user
- programs, such as cdrtools, to send ATAPI commands directly to a
- device.
-
- If you chose to build PARIDE support into your kernel, you may
- answer Y here to build in the parallel port generic ATAPI driver,
- otherwise you should answer M to build it as a loadable module. The
- module will be called pg.
-
- You must also have at least one parallel port protocol driver in
- your system.
-
- This driver implements an API loosely related to the generic SCSI
- driver. See <file:include/linux/pg.h>. for details.
-
- You can obtain the most recent version of cdrtools from
- <ftp://ftp.berlios.de/pub/cdrecord/>. Versions 1.6.1a3 and
- later fully support this driver.
-
-comment "Parallel IDE protocol modules"
- depends on PARIDE
-
-config PARIDE_ATEN
- tristate "ATEN EH-100 protocol"
- depends on PARIDE
- help
- This option enables support for the ATEN EH-100 parallel port IDE
- protocol. This protocol is used in some inexpensive low performance
- parallel port kits made in Hong Kong. If you chose to build PARIDE
- support into your kernel, you may answer Y here to build in the
- protocol driver, otherwise you should answer M to build it as a
- loadable module. The module will be called aten. You must also
- have a high-level driver for the type of device that you want to
- support.
-
-config PARIDE_BPCK
- tristate "MicroSolutions backpack (Series 5) protocol"
- depends on PARIDE
- help
- This option enables support for the Micro Solutions BACKPACK
- parallel port Series 5 IDE protocol. (Most BACKPACK drives made
- before 1999 were Series 5) Series 5 drives will NOT always have the
- Series noted on the bottom of the drive. Series 6 drivers will.
-
- In other words, if your BACKPACK drive doesn't say "Series 6" on the
- bottom, enable this option.
-
- If you chose to build PARIDE support into your kernel, you may
- answer Y here to build in the protocol driver, otherwise you should
- answer M to build it as a loadable module. The module will be
- called bpck. You must also have a high-level driver for the type
- of device that you want to support.
-
-config PARIDE_BPCK6
- tristate "MicroSolutions backpack (Series 6) protocol"
- depends on PARIDE && !64BIT
- help
- This option enables support for the Micro Solutions BACKPACK
- parallel port Series 6 IDE protocol. (Most BACKPACK drives made
- after 1999 were Series 6) Series 6 drives will have the Series noted
- on the bottom of the drive. Series 5 drivers don't always have it
- noted.
-
- In other words, if your BACKPACK drive says "Series 6" on the
- bottom, enable this option.
-
- If you chose to build PARIDE support into your kernel, you may
- answer Y here to build in the protocol driver, otherwise you should
- answer M to build it as a loadable module. The module will be
- called bpck6. You must also have a high-level driver for the type
- of device that you want to support.
-
-config PARIDE_COMM
- tristate "DataStor Commuter protocol"
- depends on PARIDE
- help
- This option enables support for the Commuter parallel port IDE
- protocol from DataStor. If you chose to build PARIDE support
- into your kernel, you may answer Y here to build in the protocol
- driver, otherwise you should answer M to build it as a loadable
- module. The module will be called comm. You must also have
- a high-level driver for the type of device that you want to support.
-
-config PARIDE_DSTR
- tristate "DataStor EP-2000 protocol"
- depends on PARIDE
- help
- This option enables support for the EP-2000 parallel port IDE
- protocol from DataStor. If you chose to build PARIDE support
- into your kernel, you may answer Y here to build in the protocol
- driver, otherwise you should answer M to build it as a loadable
- module. The module will be called dstr. You must also have
- a high-level driver for the type of device that you want to support.
-
-config PARIDE_FIT2
- tristate "FIT TD-2000 protocol"
- depends on PARIDE
- help
- This option enables support for the TD-2000 parallel port IDE
- protocol from Fidelity International Technology. This is a simple
- (low speed) adapter that is used in some portable hard drives. If
- you chose to build PARIDE support into your kernel, you may answer Y
- here to build in the protocol driver, otherwise you should answer M
- to build it as a loadable module. The module will be called ktti.
- You must also have a high-level driver for the type of device that
- you want to support.
-
-config PARIDE_FIT3
- tristate "FIT TD-3000 protocol"
- depends on PARIDE
- help
- This option enables support for the TD-3000 parallel port IDE
- protocol from Fidelity International Technology. This protocol is
- used in newer models of their portable disk, CD-ROM and PD/CD
- devices. If you chose to build PARIDE support into your kernel, you
- may answer Y here to build in the protocol driver, otherwise you
- should answer M to build it as a loadable module. The module will be
- called fit3. You must also have a high-level driver for the type
- of device that you want to support.
-
-config PARIDE_EPAT
- tristate "Shuttle EPAT/EPEZ protocol"
- depends on PARIDE
- help
- This option enables support for the EPAT parallel port IDE protocol.
- EPAT is a parallel port IDE adapter manufactured by Shuttle
- Technology and widely used in devices from major vendors such as
- Hewlett-Packard, SyQuest, Imation and Avatar. If you chose to build
- PARIDE support into your kernel, you may answer Y here to build in
- the protocol driver, otherwise you should answer M to build it as a
- loadable module. The module will be called epat. You must also
- have a high-level driver for the type of device that you want to
- support.
-
-config PARIDE_EPATC8
- bool "Support c7/c8 chips"
- depends on PARIDE_EPAT
- help
- This option enables support for the newer Shuttle EP1284 (aka c7 and
- c8) chip. You need this if you are using any recent Imation SuperDisk
- (LS-120) drive.
-
-config PARIDE_EPIA
- tristate "Shuttle EPIA protocol"
- depends on PARIDE
- help
- This option enables support for the (obsolete) EPIA parallel port
- IDE protocol from Shuttle Technology. This adapter can still be
- found in some no-name kits. If you chose to build PARIDE support
- into your kernel, you may answer Y here to build in the protocol
- driver, otherwise you should answer M to build it as a loadable
- module. The module will be called epia. You must also have a
- high-level driver for the type of device that you want to support.
-
-config PARIDE_FRIQ
- tristate "Freecom IQ ASIC-2 protocol"
- depends on PARIDE
- help
- This option enables support for version 2 of the Freecom IQ parallel
- port IDE adapter. This adapter is used by the Maxell Superdisk
- drive. If you chose to build PARIDE support into your kernel, you
- may answer Y here to build in the protocol driver, otherwise you
- should answer M to build it as a loadable module. The module will be
- called friq. You must also have a high-level driver for the type
- of device that you want to support.
-
-config PARIDE_FRPW
- tristate "FreeCom power protocol"
- depends on PARIDE
- help
- This option enables support for the Freecom power parallel port IDE
- protocol. If you chose to build PARIDE support into your kernel, you
- may answer Y here to build in the protocol driver, otherwise you
- should answer M to build it as a loadable module. The module will be
- called frpw. You must also have a high-level driver for the type
- of device that you want to support.
-
-config PARIDE_KBIC
- tristate "KingByte KBIC-951A/971A protocols"
- depends on PARIDE
- help
- This option enables support for the KBIC-951A and KBIC-971A parallel
- port IDE protocols from KingByte Information Corp. KingByte's
- adapters appear in many no-name portable disk and CD-ROM products,
- especially in Europe. If you chose to build PARIDE support into your
- kernel, you may answer Y here to build in the protocol driver,
- otherwise you should answer M to build it as a loadable module. The
- module will be called kbic. You must also have a high-level driver
- for the type of device that you want to support.
-
-config PARIDE_KTTI
- tristate "KT PHd protocol"
- depends on PARIDE
- help
- This option enables support for the "PHd" parallel port IDE protocol
- from KT Technology. This is a simple (low speed) adapter that is
- used in some 2.5" portable hard drives. If you chose to build PARIDE
- support into your kernel, you may answer Y here to build in the
- protocol driver, otherwise you should answer M to build it as a
- loadable module. The module will be called ktti. You must also
- have a high-level driver for the type of device that you want to
- support.
-
-config PARIDE_ON20
- tristate "OnSpec 90c20 protocol"
- depends on PARIDE
- help
- This option enables support for the (obsolete) 90c20 parallel port
- IDE protocol from OnSpec (often marketed under the ValuStore brand
- name). If you chose to build PARIDE support into your kernel, you
- may answer Y here to build in the protocol driver, otherwise you
- should answer M to build it as a loadable module. The module will
- be called on20. You must also have a high-level driver for the
- type of device that you want to support.
-
-config PARIDE_ON26
- tristate "OnSpec 90c26 protocol"
- depends on PARIDE
- help
- This option enables support for the 90c26 parallel port IDE protocol
- from OnSpec Electronics (often marketed under the ValuStore brand
- name). If you chose to build PARIDE support into your kernel, you
- may answer Y here to build in the protocol driver, otherwise you
- should answer M to build it as a loadable module. The module will be
- called on26. You must also have a high-level driver for the type
- of device that you want to support.
-
-#
diff --git a/drivers/block/paride/Makefile b/drivers/block/paride/Makefile
deleted file mode 100644
index cf1742a8475e..000000000000
--- a/drivers/block/paride/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for Parallel port IDE device drivers.
-#
-# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
-# Rewritten to use lists instead of if-statements.
-#
-
-obj-$(CONFIG_PARIDE) += paride.o
-obj-$(CONFIG_PARIDE_ATEN) += aten.o
-obj-$(CONFIG_PARIDE_BPCK) += bpck.o
-obj-$(CONFIG_PARIDE_COMM) += comm.o
-obj-$(CONFIG_PARIDE_DSTR) += dstr.o
-obj-$(CONFIG_PARIDE_KBIC) += kbic.o
-obj-$(CONFIG_PARIDE_EPAT) += epat.o
-obj-$(CONFIG_PARIDE_EPIA) += epia.o
-obj-$(CONFIG_PARIDE_FRPW) += frpw.o
-obj-$(CONFIG_PARIDE_FRIQ) += friq.o
-obj-$(CONFIG_PARIDE_FIT2) += fit2.o
-obj-$(CONFIG_PARIDE_FIT3) += fit3.o
-obj-$(CONFIG_PARIDE_ON20) += on20.o
-obj-$(CONFIG_PARIDE_ON26) += on26.o
-obj-$(CONFIG_PARIDE_KTTI) += ktti.o
-obj-$(CONFIG_PARIDE_BPCK6) += bpck6.o
-obj-$(CONFIG_PARIDE_PD) += pd.o
-obj-$(CONFIG_PARIDE_PCD) += pcd.o
-obj-$(CONFIG_PARIDE_PF) += pf.o
-obj-$(CONFIG_PARIDE_PT) += pt.o
-obj-$(CONFIG_PARIDE_PG) += pg.o
diff --git a/drivers/block/paride/Transition-notes b/drivers/block/paride/Transition-notes
deleted file mode 100644
index 70374907c020..000000000000
--- a/drivers/block/paride/Transition-notes
+++ /dev/null
@@ -1,128 +0,0 @@
-Lemma 1:
- If ps_tq is scheduled, ps_tq_active is 1. ps_tq_int() can be called
- only when ps_tq_active is 1.
-Proof: All assignments to ps_tq_active and all scheduling of ps_tq happen
- under ps_spinlock. There are three places where that can happen:
- one in ps_set_intr() (A) and two in ps_tq_int() (B and C).
- Consider the sequnce of these events. A can not be preceded by
- anything except B, since it is under if (!ps_tq_active) under
- ps_spinlock. C is always preceded by B, since we can't reach it
- other than through B and we don't drop ps_spinlock between them.
- IOW, the sequence is A?(BA|BC|B)*. OTOH, number of B can not exceed
- the sum of numbers of A and C, since each call of ps_tq_int() is
- the result of ps_tq execution. Therefore, the sequence starts with
- A and each B is preceded by either A or C. Moments when we enter
- ps_tq_int() are sandwiched between {A,C} and B in that sequence,
- since at any time number of B can not exceed the number of these
- moments which, in turn, can not exceed the number of A and C.
- In other words, the sequence of events is (A or C set ps_tq_active to
- 1 and schedule ps_tq, ps_tq is executed, ps_tq_int() is entered,
- B resets ps_tq_active)*.
-
-
-consider the following area:
- * in do_pd_request1(): to calls of pi_do_claimed() and return in
- case when pd_req is NULL.
- * in next_request(): to call of do_pd_request1()
- * in do_pd_read(): to call of ps_set_intr()
- * in do_pd_read_start(): to calls of pi_do_claimed(), next_request()
-and ps_set_intr()
- * in do_pd_read_drq(): to calls of pi_do_claimed() and next_request()
- * in do_pd_write(): to call of ps_set_intr()
- * in do_pd_write_start(): to calls of pi_do_claimed(), next_request()
-and ps_set_intr()
- * in do_pd_write_done(): to calls of pi_do_claimed() and next_request()
- * in ps_set_intr(): to check for ps_tq_active and to scheduling
- ps_tq if ps_tq_active was 0.
- * in ps_tq_int(): from the moment when we get ps_spinlock() to the
- return, call of con() or scheduling ps_tq.
- * in pi_schedule_claimed() when called from pi_do_claimed() called from
- pd.c, everything until returning 1 or setting or setting ->claim_cont
- on the path that returns 0
- * in pi_do_claimed() when called from pd.c, everything until the call
- of pi_do_claimed() plus the everything until the call of cont() if
- pi_do_claimed() has returned 1.
- * in pi_wake_up() called for PIA that belongs to pd.c, everything from
- the moment when pi_spinlock has been acquired.
-
-Lemma 2:
- 1) at any time at most one thread of execution can be in that area or
- be preempted there.
- 2) When there is such a thread, pd_busy is set or pd_lock is held by
- that thread.
- 3) When there is such a thread, ps_tq_active is 0 or ps_spinlock is
- held by that thread.
- 4) When there is such a thread, all PIA belonging to pd.c have NULL
- ->claim_cont or pi_spinlock is held by thread in question.
-
-Proof: consider the first moment when the above is not true.
-
-(1) can become not true if some thread enters that area while another is there.
- a) do_pd_request1() can be called from next_request() or do_pd_request()
- In the first case the thread was already in the area. In the second,
- the thread was holding pd_lock and found pd_busy not set, which would
- mean that (2) was already not true.
- b) ps_set_intr() and pi_schedule_claimed() can be called only from the
- area.
- c) pi_do_claimed() is called by pd.c only from the area.
- d) ps_tq_int() can enter the area only when the thread is holding
- ps_spinlock and ps_tq_active is 1 (due to Lemma 1). It means that
- (3) was already not true.
- e) do_pd_{read,write}* could be called only from the area. The only
- case that needs consideration is call from pi_wake_up() and there
- we would have to be called for the PIA that got ->claimed_cont
- from pd.c. That could happen only if pi_do_claimed() had been
- called from pd.c for that PIA, which happens only for PIA belonging
- to pd.c.
- f) pi_wake_up() can enter the area only when the thread is holding
- pi_spinlock and ->claimed_cont is non-NULL for PIA belonging to
- pd.c. It means that (4) was already not true.
-
-(2) can become not true only when pd_lock is released by the thread in question.
- Indeed, pd_busy is reset only in the area and thread that resets
- it is holding pd_lock. The only place within the area where we
- release pd_lock is in pd_next_buf() (called from within the area).
- But that code does not reset pd_busy, so pd_busy would have to be
- 0 when pd_next_buf() had acquired pd_lock. If it become 0 while
- we were acquiring the lock, (1) would be already false, since
- the thread that had reset it would be in the area simulateously.
- If it was 0 before we tried to acquire pd_lock, (2) would be
- already false.
-
-For similar reasons, (3) can become not true only when ps_spinlock is released
-by the thread in question. However, all such places within the area are right
-after resetting ps_tq_active to 0.
-
-(4) is done the same way - all places where we release pi_spinlock within
-the area are either after resetting ->claimed_cont to NULL while holding
-pi_spinlock, or after not tocuhing ->claimed_cont since acquiring pi_spinlock
-also in the area. The only place where ->claimed_cont is made non-NULL is
-in the area, under pi_spinlock and we do not release it until after leaving
-the area.
-
-QED.
-
-
-Corollary 1: ps_tq_active can be killed. Indeed, the only place where we
-check its value is in ps_set_intr() and if it had been non-zero at that
-point, we would have violated either (2.1) (if it was set while ps_set_intr()
-was acquiring ps_spinlock) or (2.3) (if it was set when we started to
-acquire ps_spinlock).
-
-Corollary 2: ps_spinlock can be killed. Indeed, Lemma 1 and Lemma 2 show
-that the only possible contention is between scheduling ps_tq followed by
-immediate release of spinlock and beginning of execution of ps_tq on
-another CPU.
-
-Corollary 3: assignment to pd_busy in do_pd_read_start() and do_pd_write_start()
-can be killed. Indeed, we are not holding pd_lock and thus pd_busy is already
-1 here.
-
-Corollary 4: in ps_tq_int() uses of con can be replaced with uses of
-ps_continuation, since the latter is changed only from the area.
-We don't need to reset it to NULL, since we are guaranteed that there
-will be a call of ps_set_intr() before we look at ps_continuation again.
-We can remove the check for ps_continuation being NULL for the same
-reason - the value is guaranteed to be set by the last ps_set_intr() and
-we never pass it NULL. Assignements in the beginning of ps_set_intr()
-can be taken to callers as long as they remain within the area.
diff --git a/drivers/block/paride/aten.c b/drivers/block/paride/aten.c
deleted file mode 100644
index 2695465568ad..000000000000
--- a/drivers/block/paride/aten.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- aten.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- aten.c is a low-level protocol driver for the ATEN EH-100
- parallel port adapter. The EH-100 supports 4-bit and 8-bit
- modes only. There is also an EH-132 which supports EPP mode
- transfers. The EH-132 is not yet supported.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.05 init_proto, release_proto
-
-*/
-
-#define ATEN_VERSION "1.01"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/wait.h>
-#include <linux/types.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x08, 0x20 };
-
-static void aten_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int r;
-
- r = regr + cont_map[cont] + 0x80;
-
- w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
-}
-
-static int aten_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- r = regr + cont_map[cont] + 0x40;
-
- switch (pi->mode) {
-
- case 0: w0(r); w2(0xe); w2(6);
- w2(7); w2(6); w2(0);
- a = r1(); w0(0x10); b = r1(); w2(0xc);
- return j44(a,b);
-
- case 1: r |= 0x10;
- w0(r); w2(0xe); w2(6); w0(0xff);
- w2(0x27); w2(0x26); w2(0x20);
- a = r0();
- w2(0x26); w2(0xc);
- return a;
- }
- return -1;
-}
-
-static void aten_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b, c, d;
-
- switch (pi->mode) {
-
- case 0: w0(0x48); w2(0xe); w2(6);
- for (k=0;k<count/2;k++) {
- w2(7); w2(6); w2(2);
- a = r1(); w0(0x58); b = r1();
- w2(0); d = r1(); w0(0x48); c = r1();
- buf[2*k] = j44(c,d);
- buf[2*k+1] = j44(a,b);
- }
- w2(0xc);
- break;
-
- case 1: w0(0x58); w2(0xe); w2(6);
- for (k=0;k<count/2;k++) {
- w2(0x27); w2(0x26); w2(0x22);
- a = r0(); w2(0x20); b = r0();
- buf[2*k] = b; buf[2*k+1] = a;
- }
- w2(0x26); w2(0xc);
- break;
- }
-}
-
-static void aten_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- w0(0x88); w2(0xe); w2(6);
- for (k=0;k<count/2;k++) {
- w0(buf[2*k+1]); w2(0xe); w2(6);
- w0(buf[2*k]); w2(7); w2(6);
- }
- w2(0xc);
-}
-
-static void aten_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(0xc);
-}
-
-static void aten_disconnect ( PIA *pi )
-
-{ w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void aten_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[2] = {"4-bit","8-bit"};
-
- printk("%s: aten %s, ATEN EH-100 at 0x%x, ",
- pi->device,ATEN_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol aten = {
- .owner = THIS_MODULE,
- .name = "aten",
- .max_mode = 2,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = aten_write_regr,
- .read_regr = aten_read_regr,
- .write_block = aten_write_block,
- .read_block = aten_read_block,
- .connect = aten_connect,
- .disconnect = aten_disconnect,
- .log_adapter = aten_log_adapter,
-};
-
-static int __init aten_init(void)
-{
- return paride_register(&aten);
-}
-
-static void __exit aten_exit(void)
-{
- paride_unregister( &aten );
-}
-
-MODULE_LICENSE("GPL");
-module_init(aten_init)
-module_exit(aten_exit)
diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c
deleted file mode 100644
index d880a9465e9b..000000000000
--- a/drivers/block/paride/bpck.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- bpck.c (c) 1996-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- bpck.c is a low-level protocol driver for the MicroSolutions
- "backpack" parallel port IDE adapter.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.05 init_proto, release_proto, pi->delay
- 1.02 GRG 1998.08.15 default pi->delay returned to 4
-
-*/
-
-#define BPCK_VERSION "1.02"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#undef r2
-#undef w2
-#undef PC
-
-#define PC pi->private
-#define r2() (PC=(in_p(2) & 0xff))
-#define w2(byte) {out_p(2,byte); PC = byte;}
-#define t2(pat) {PC ^= pat; out_p(2,PC);}
-#define e2() {PC &= 0xfe; out_p(2,PC);}
-#define o2() {PC |= 1; out_p(2,PC);}
-
-#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
- cont = 2 - use internal bpck register addressing
-*/
-
-static int cont_map[3] = { 0x40, 0x48, 0 };
-
-static int bpck_read_regr( PIA *pi, int cont, int regr )
-
-{ int r, l, h;
-
- r = regr + cont_map[cont];
-
- switch (pi->mode) {
-
- case 0: w0(r & 0xf); w0(r); t2(2); t2(4);
- l = r1();
- t2(4);
- h = r1();
- return j44(l,h);
-
- case 1: w0(r & 0xf); w0(r); t2(2);
- e2(); t2(0x20);
- t2(4); h = r0();
- t2(1); t2(0x20);
- return h;
-
- case 2:
- case 3:
- case 4: w0(r); w2(9); w2(0); w2(0x20);
- h = r4();
- w2(0);
- return h;
-
- }
- return -1;
-}
-
-static void bpck_write_regr( PIA *pi, int cont, int regr, int val )
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- switch (pi->mode) {
-
- case 0:
- case 1: w0(r);
- t2(2);
- w0(val);
- o2(); t2(4); t2(1);
- break;
-
- case 2:
- case 3:
- case 4: w0(r); w2(9); w2(0);
- w0(val); w2(1); w2(3); w2(0);
- break;
-
- }
-}
-
-/* These macros access the bpck registers in native addressing */
-
-#define WR(r,v) bpck_write_regr(pi,2,r,v)
-#define RR(r) (bpck_read_regr(pi,2,r))
-
-static void bpck_write_block( PIA *pi, char * buf, int count )
-
-{ int i;
-
- switch (pi->mode) {
-
- case 0: WR(4,0x40);
- w0(0x40); t2(2); t2(1);
- for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
- WR(4,0);
- break;
-
- case 1: WR(4,0x50);
- w0(0x40); t2(2); t2(1);
- for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
- WR(4,0x10);
- break;
-
- case 2: WR(4,0x48);
- w0(0x40); w2(9); w2(0); w2(1);
- for (i=0;i<count;i++) w4(buf[i]);
- w2(0);
- WR(4,8);
- break;
-
- case 3: WR(4,0x48);
- w0(0x40); w2(9); w2(0); w2(1);
- for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]);
- w2(0);
- WR(4,8);
- break;
-
- case 4: WR(4,0x48);
- w0(0x40); w2(9); w2(0); w2(1);
- for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]);
- w2(0);
- WR(4,8);
- break;
- }
-}
-
-static void bpck_read_block( PIA *pi, char * buf, int count )
-
-{ int i, l, h;
-
- switch (pi->mode) {
-
- case 0: WR(4,0x40);
- w0(0x40); t2(2);
- for (i=0;i<count;i++) {
- t2(4); l = r1();
- t2(4); h = r1();
- buf[i] = j44(l,h);
- }
- WR(4,0);
- break;
-
- case 1: WR(4,0x50);
- w0(0x40); t2(2); t2(0x20);
- for(i=0;i<count;i++) { t2(4); buf[i] = r0(); }
- t2(1); t2(0x20);
- WR(4,0x10);
- break;
-
- case 2: WR(4,0x48);
- w0(0x40); w2(9); w2(0); w2(0x20);
- for (i=0;i<count;i++) buf[i] = r4();
- w2(0);
- WR(4,8);
- break;
-
- case 3: WR(4,0x48);
- w0(0x40); w2(9); w2(0); w2(0x20);
- for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
- w2(0);
- WR(4,8);
- break;
-
- case 4: WR(4,0x48);
- w0(0x40); w2(9); w2(0); w2(0x20);
- for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
- w2(0);
- WR(4,8);
- break;
-
- }
-}
-
-static int bpck_probe_unit ( PIA *pi )
-
-{ int o1, o0, f7, id;
- int t, s;
-
- id = pi->unit;
- s = 0;
- w2(4); w2(0xe); r2(); t2(2);
- o1 = r1()&0xf8;
- o0 = r0();
- w0(255-id); w2(4); w0(id);
- t2(8); t2(8); t2(8);
- t2(2); t = r1()&0xf8;
- f7 = ((id % 8) == 7);
- if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; }
- if ((t == o1) && ((!f7) || (s == o1))) {
- w2(0x4c); w0(o0);
- return 0;
- }
- t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
- return 1;
-}
-
-static void bpck_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- w0(0xff-pi->unit); w2(4); w0(pi->unit);
- t2(8); t2(8); t2(8);
- t2(2); t2(2);
-
- switch (pi->mode) {
-
- case 0: t2(8); WR(4,0);
- break;
-
- case 1: t2(8); WR(4,0x10);
- break;
-
- case 2:
- case 3:
- case 4: w2(0); WR(4,8);
- break;
-
- }
-
- WR(5,8);
-
- if (pi->devtype == PI_PCD) {
- WR(0x46,0x10); /* fiddle with ESS logic ??? */
- WR(0x4c,0x38);
- WR(0x4d,0x88);
- WR(0x46,0xa0);
- WR(0x41,0);
- WR(0x4e,8);
- }
-}
-
-static void bpck_disconnect ( PIA *pi )
-
-{ w0(0);
- if (pi->mode >= 2) { w2(9); w2(0); } else t2(2);
- w2(0x4c); w0(pi->saved_r0);
-}
-
-static void bpck_force_spp ( PIA *pi )
-
-/* This fakes the EPP protocol to turn off EPP ... */
-
-{ pi->saved_r0 = r0();
- w0(0xff-pi->unit); w2(4); w0(pi->unit);
- t2(8); t2(8); t2(8);
- t2(2); t2(2);
-
- w2(0);
- w0(4); w2(9); w2(0);
- w0(0); w2(1); w2(3); w2(0);
- w0(0); w2(9); w2(0);
- w2(0x4c); w0(pi->saved_r0);
-}
-
-#define TEST_LEN 16
-
-static int bpck_test_proto( PIA *pi, char * scratch, int verbose )
-
-{ int i, e, l, h, om;
- char buf[TEST_LEN];
-
- bpck_force_spp(pi);
-
- switch (pi->mode) {
-
- case 0: bpck_connect(pi);
- WR(0x13,0x7f);
- w0(0x13); t2(2);
- for(i=0;i<TEST_LEN;i++) {
- t2(4); l = r1();
- t2(4); h = r1();
- buf[i] = j44(l,h);
- }
- bpck_disconnect(pi);
- break;
-
- case 1: bpck_connect(pi);
- WR(0x13,0x7f);
- w0(0x13); t2(2); t2(0x20);
- for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); }
- t2(1); t2(0x20);
- bpck_disconnect(pi);
- break;
-
- case 2:
- case 3:
- case 4: om = pi->mode;
- pi->mode = 0;
- bpck_connect(pi);
- WR(7,3);
- WR(4,8);
- bpck_disconnect(pi);
-
- pi->mode = om;
- bpck_connect(pi);
- w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
-
- switch (pi->mode) {
- case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4();
- break;
- case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w();
- break;
- case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l();
- break;
- }
-
- w2(0);
- WR(7,0);
- bpck_disconnect(pi);
-
- break;
-
- }
-
- if (verbose) {
- printk("%s: bpck: 0x%x unit %d mode %d: ",
- pi->device,pi->port,pi->unit,pi->mode);
- for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]);
- printk("\n");
- }
-
- e = 0;
- for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++;
- return e;
-}
-
-static void bpck_read_eeprom ( PIA *pi, char * buf )
-
-{ int i, j, k, p, v, f, om, od;
-
- bpck_force_spp(pi);
-
- om = pi->mode; od = pi->delay;
- pi->mode = 0; pi->delay = 6;
-
- bpck_connect(pi);
-
- WR(4,0);
- for (i=0;i<64;i++) {
- WR(6,8);
- WR(6,0xc);
- p = 0x100;
- for (k=0;k<9;k++) {
- f = (((i + 0x180) & p) != 0) * 2;
- WR(6,f+0xc);
- WR(6,f+0xd);
- WR(6,f+0xc);
- p = (p >> 1);
- }
- for (j=0;j<2;j++) {
- v = 0;
- for (k=0;k<8;k++) {
- WR(6,0xc);
- WR(6,0xd);
- WR(6,0xc);
- f = RR(0);
- v = 2*v + (f == 0x84);
- }
- buf[2*i+1-j] = v;
- }
- }
- WR(6,8);
- WR(6,0);
- WR(5,8);
-
- bpck_disconnect(pi);
-
- if (om >= 2) {
- bpck_connect(pi);
- WR(7,3);
- WR(4,8);
- bpck_disconnect(pi);
- }
-
- pi->mode = om; pi->delay = od;
-}
-
-static int bpck_test_port ( PIA *pi ) /* check for 8-bit port */
-
-{ int i, r, m;
-
- w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
- m = -1;
- if (r == i) m = 2;
- if (r == (255-i)) m = 0;
-
- w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i);
- if (r != (255-i)) m = -1;
-
- if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); }
- if (m == 2) { w2(0x26); w2(0xc); }
-
- if (m == -1) return 0;
- return 5;
-}
-
-static void bpck_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[5] = { "4-bit","8-bit","EPP-8",
- "EPP-16","EPP-32" };
-
-#ifdef DUMP_EEPROM
- int i;
-#endif
-
- bpck_read_eeprom(pi,scratch);
-
-#ifdef DUMP_EEPROM
- if (verbose) {
- for(i=0;i<128;i++)
- if ((scratch[i] < ' ') || (scratch[i] > '~'))
- scratch[i] = '.';
- printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch);
- printk("%s: %64.64s\n",pi->device,&scratch[64]);
- }
-#endif
-
- printk("%s: bpck %s, backpack %8.8s unit %d",
- pi->device,BPCK_VERSION,&scratch[110],pi->unit);
- printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port,
- pi->mode,mode_string[pi->mode],pi->delay);
-}
-
-static struct pi_protocol bpck = {
- .owner = THIS_MODULE,
- .name = "bpck",
- .max_mode = 5,
- .epp_first = 2,
- .default_delay = 4,
- .max_units = 255,
- .write_regr = bpck_write_regr,
- .read_regr = bpck_read_regr,
- .write_block = bpck_write_block,
- .read_block = bpck_read_block,
- .connect = bpck_connect,
- .disconnect = bpck_disconnect,
- .test_port = bpck_test_port,
- .probe_unit = bpck_probe_unit,
- .test_proto = bpck_test_proto,
- .log_adapter = bpck_log_adapter,
-};
-
-static int __init bpck_init(void)
-{
- return paride_register(&bpck);
-}
-
-static void __exit bpck_exit(void)
-{
- paride_unregister(&bpck);
-}
-
-MODULE_LICENSE("GPL");
-module_init(bpck_init)
-module_exit(bpck_exit)
diff --git a/drivers/block/paride/bpck6.c b/drivers/block/paride/bpck6.c
deleted file mode 100644
index ec64e7f5d1ce..000000000000
--- a/drivers/block/paride/bpck6.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- backpack.c (c) 2001 Micro Solutions Inc.
- Released under the terms of the GNU General Public license
-
- backpack.c is a low-level protocol driver for the Micro Solutions
- "BACKPACK" parallel port IDE adapter
- (Works on Series 6 drives)
-
- Written by: Ken Hahn (linux-dev@micro-solutions.com)
- Clive Turvey (linux-dev@micro-solutions.com)
-
-*/
-
-/*
- This is Ken's linux wrapper for the PPC library
- Version 1.0.0 is the backpack driver for which source is not available
- Version 2.0.0 is the first to have source released
- Version 2.0.1 is the "Cox-ified" source code
- Version 2.0.2 - fixed version string usage, and made ppc functions static
-*/
-
-
-#define BACKPACK_VERSION "2.0.2"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <asm/io.h>
-#include <linux/parport.h>
-
-#include "ppc6lnx.c"
-#include "paride.h"
-
-/* PARAMETERS */
-static bool verbose; /* set this to 1 to see debugging messages and whatnot */
-
-
-#define PPCSTRUCT(pi) ((Interface *)(pi->private))
-
-/****************************************************************/
-/*
- ATAPI CDROM DRIVE REGISTERS
-*/
-#define ATAPI_DATA 0 /* data port */
-#define ATAPI_ERROR 1 /* error register (read) */
-#define ATAPI_FEATURES 1 /* feature register (write) */
-#define ATAPI_INT_REASON 2 /* interrupt reason register */
-#define ATAPI_COUNT_LOW 4 /* byte count register (low) */
-#define ATAPI_COUNT_HIGH 5 /* byte count register (high) */
-#define ATAPI_DRIVE_SEL 6 /* drive select register */
-#define ATAPI_STATUS 7 /* status port (read) */
-#define ATAPI_COMMAND 7 /* command port (write) */
-#define ATAPI_ALT_STATUS 0x0e /* alternate status reg (read) */
-#define ATAPI_DEVICE_CONTROL 0x0e /* device control (write) */
-/****************************************************************/
-
-static int bpck6_read_regr(PIA *pi, int cont, int reg)
-{
- unsigned int out;
-
- /* check for bad settings */
- if (reg<0 || reg>7 || cont<0 || cont>2)
- {
- return(-1);
- }
- out=ppc6_rd_port(PPCSTRUCT(pi),cont?reg|8:reg);
- return(out);
-}
-
-static void bpck6_write_regr(PIA *pi, int cont, int reg, int val)
-{
- /* check for bad settings */
- if (reg>=0 && reg<=7 && cont>=0 && cont<=1)
- {
- ppc6_wr_port(PPCSTRUCT(pi),cont?reg|8:reg,(u8)val);
- }
-}
-
-static void bpck6_write_block( PIA *pi, char * buf, int len )
-{
- ppc6_wr_port16_blk(PPCSTRUCT(pi),ATAPI_DATA,buf,(u32)len>>1);
-}
-
-static void bpck6_read_block( PIA *pi, char * buf, int len )
-{
- ppc6_rd_port16_blk(PPCSTRUCT(pi),ATAPI_DATA,buf,(u32)len>>1);
-}
-
-static void bpck6_connect ( PIA *pi )
-{
- if(verbose)
- {
- printk(KERN_DEBUG "connect\n");
- }
-
- if(pi->mode >=2)
- {
- PPCSTRUCT(pi)->mode=4+pi->mode-2;
- }
- else if(pi->mode==1)
- {
- PPCSTRUCT(pi)->mode=3;
- }
- else
- {
- PPCSTRUCT(pi)->mode=1;
- }
-
- ppc6_open(PPCSTRUCT(pi));
- ppc6_wr_extout(PPCSTRUCT(pi),0x3);
-}
-
-static void bpck6_disconnect ( PIA *pi )
-{
- if(verbose)
- {
- printk("disconnect\n");
- }
- ppc6_wr_extout(PPCSTRUCT(pi),0x0);
- ppc6_close(PPCSTRUCT(pi));
-}
-
-static int bpck6_test_port ( PIA *pi ) /* check for 8-bit port */
-{
- if(verbose)
- {
- printk(KERN_DEBUG "PARPORT indicates modes=%x for lp=0x%lx\n",
- ((struct pardevice*)(pi->pardev))->port->modes,
- ((struct pardevice *)(pi->pardev))->port->base);
- }
-
- /*copy over duplicate stuff.. initialize state info*/
- PPCSTRUCT(pi)->ppc_id=pi->unit;
- PPCSTRUCT(pi)->lpt_addr=pi->port;
-
- /* look at the parport device to see if what modes we can use */
- if(((struct pardevice *)(pi->pardev))->port->modes &
- (PARPORT_MODE_EPP)
- )
- {
- return 5; /* Can do EPP*/
- }
- else if(((struct pardevice *)(pi->pardev))->port->modes &
- (PARPORT_MODE_TRISTATE)
- )
- {
- return 2;
- }
- else /*Just flat SPP*/
- {
- return 1;
- }
-}
-
-static int bpck6_probe_unit ( PIA *pi )
-{
- int out;
-
- if(verbose)
- {
- printk(KERN_DEBUG "PROBE UNIT %x on port:%x\n",pi->unit,pi->port);
- }
-
- /*SET PPC UNIT NUMBER*/
- PPCSTRUCT(pi)->ppc_id=pi->unit;
-
- /*LOWER DOWN TO UNIDIRECTIONAL*/
- PPCSTRUCT(pi)->mode=1;
-
- out=ppc6_open(PPCSTRUCT(pi));
-
- if(verbose)
- {
- printk(KERN_DEBUG "ppc_open returned %2x\n",out);
- }
-
- if(out)
- {
- ppc6_close(PPCSTRUCT(pi));
- if(verbose)
- {
- printk(KERN_DEBUG "leaving probe\n");
- }
- return(1);
- }
- else
- {
- if(verbose)
- {
- printk(KERN_DEBUG "Failed open\n");
- }
- return(0);
- }
-}
-
-static void bpck6_log_adapter( PIA *pi, char * scratch, int verbose )
-{
- char *mode_string[5]=
- {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
-
- printk("%s: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n",pi->device);
- printk("%s: Copyright 2001 by Micro Solutions, Inc., DeKalb IL.\n",pi->device);
- printk("%s: BACKPACK %s, Micro Solutions BACKPACK Drive at 0x%x\n",
- pi->device,BACKPACK_VERSION,pi->port);
- printk("%s: Unit: %d Mode:%d (%s) Delay %d\n",pi->device,
- pi->unit,pi->mode,mode_string[pi->mode],pi->delay);
-}
-
-static int bpck6_init_proto(PIA *pi)
-{
- Interface *p = kzalloc(sizeof(Interface), GFP_KERNEL);
-
- if (p) {
- pi->private = (unsigned long)p;
- return 0;
- }
-
- printk(KERN_ERR "%s: ERROR COULDN'T ALLOCATE MEMORY\n", pi->device);
- return -1;
-}
-
-static void bpck6_release_proto(PIA *pi)
-{
- kfree((void *)(pi->private));
-}
-
-static struct pi_protocol bpck6 = {
- .owner = THIS_MODULE,
- .name = "bpck6",
- .max_mode = 5,
- .epp_first = 2, /* 2-5 use epp (need 8 ports) */
- .max_units = 255,
- .write_regr = bpck6_write_regr,
- .read_regr = bpck6_read_regr,
- .write_block = bpck6_write_block,
- .read_block = bpck6_read_block,
- .connect = bpck6_connect,
- .disconnect = bpck6_disconnect,
- .test_port = bpck6_test_port,
- .probe_unit = bpck6_probe_unit,
- .log_adapter = bpck6_log_adapter,
- .init_proto = bpck6_init_proto,
- .release_proto = bpck6_release_proto,
-};
-
-static int __init bpck6_init(void)
-{
- printk(KERN_INFO "bpck6: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n");
- printk(KERN_INFO "bpck6: Copyright 2001 by Micro Solutions, Inc., DeKalb IL. USA\n");
- if(verbose)
- printk(KERN_DEBUG "bpck6: verbose debug enabled.\n");
- return paride_register(&bpck6);
-}
-
-static void __exit bpck6_exit(void)
-{
- paride_unregister(&bpck6);
-}
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Micro Solutions Inc.");
-MODULE_DESCRIPTION("BACKPACK Protocol module, compatible with PARIDE");
-module_param(verbose, bool, 0644);
-module_init(bpck6_init)
-module_exit(bpck6_exit)
diff --git a/drivers/block/paride/comm.c b/drivers/block/paride/comm.c
deleted file mode 100644
index 9bcd35495323..000000000000
--- a/drivers/block/paride/comm.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- comm.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- comm.c is a low-level protocol driver for some older models
- of the DataStor "Commuter" parallel to IDE adapter. Some of
- the parallel port devices marketed by Arista currently
- use this adapter.
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.05 init_proto, release_proto
-
-*/
-
-#define COMM_VERSION "1.01"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-/* mode codes: 0 nybble reads, 8-bit writes
- 1 8-bit reads and writes
- 2 8-bit EPP mode
-*/
-
-#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
-
-#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
-#define P2 w2(5);w2(7);w2(7);w2(5);w2(4);
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x08, 0x10 };
-
-static int comm_read_regr( PIA *pi, int cont, int regr )
-
-{ int l, h, r;
-
- r = regr + cont_map[cont];
-
- switch (pi->mode) {
-
- case 0: w0(r); P1; w0(0);
- w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
- return j44(l,h);
-
- case 1: w0(r+0x20); P1;
- w0(0); w2(0x26); h = r0(); w2(4);
- return h;
-
- case 2:
- case 3:
- case 4: w3(r+0x20); (void)r1();
- w2(0x24); h = r4(); w2(4);
- return h;
-
- }
- return -1;
-}
-
-static void comm_write_regr( PIA *pi, int cont, int regr, int val )
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- switch (pi->mode) {
-
- case 0:
- case 1: w0(r); P1; w0(val); P2;
- break;
-
- case 2:
- case 3:
- case 4: w3(r); (void)r1(); w4(val);
- break;
- }
-}
-
-static void comm_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(4); w0(0xff); w2(6);
- w2(4); w0(0xaa); w2(6);
- w2(4); w0(0x00); w2(6);
- w2(4); w0(0x87); w2(6);
- w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
-}
-
-static void comm_disconnect ( PIA *pi )
-
-{ w2(0); w2(0); w2(0); w2(4);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void comm_read_block( PIA *pi, char * buf, int count )
-
-{ int i, l, h;
-
- switch (pi->mode) {
-
- case 0: w0(0x48); P1;
- for(i=0;i<count;i++) {
- w0(0); w2(6); l = r1();
- w0(0x80); h = r1(); w2(4);
- buf[i] = j44(l,h);
- }
- break;
-
- case 1: w0(0x68); P1; w0(0);
- for(i=0;i<count;i++) {
- w2(0x26); buf[i] = r0(); w2(0x24);
- }
- w2(4);
- break;
-
- case 2: w3(0x68); (void)r1(); w2(0x24);
- for (i=0;i<count;i++) buf[i] = r4();
- w2(4);
- break;
-
- case 3: w3(0x68); (void)r1(); w2(0x24);
- for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
- w2(4);
- break;
-
- case 4: w3(0x68); (void)r1(); w2(0x24);
- for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
- w2(4);
- break;
-
- }
-}
-
-/* NB: Watch out for the byte swapped writes ! */
-
-static void comm_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- switch (pi->mode) {
-
- case 0:
- case 1: w0(0x68); P1;
- for (k=0;k<count;k++) {
- w2(5); w0(buf[k^1]); w2(7);
- }
- w2(5); w2(4);
- break;
-
- case 2: w3(0x48); (void)r1();
- for (k=0;k<count;k++) w4(buf[k^1]);
- break;
-
- case 3: w3(0x48); (void)r1();
- for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
- break;
-
- case 4: w3(0x48); (void)r1();
- for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
- break;
-
-
- }
-}
-
-static void comm_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
-
- printk("%s: comm %s, DataStor Commuter at 0x%x, ",
- pi->device,COMM_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol comm = {
- .owner = THIS_MODULE,
- .name = "comm",
- .max_mode = 5,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = comm_write_regr,
- .read_regr = comm_read_regr,
- .write_block = comm_write_block,
- .read_block = comm_read_block,
- .connect = comm_connect,
- .disconnect = comm_disconnect,
- .log_adapter = comm_log_adapter,
-};
-
-static int __init comm_init(void)
-{
- return paride_register(&comm);
-}
-
-static void __exit comm_exit(void)
-{
- paride_unregister(&comm);
-}
-
-MODULE_LICENSE("GPL");
-module_init(comm_init)
-module_exit(comm_exit)
diff --git a/drivers/block/paride/dstr.c b/drivers/block/paride/dstr.c
deleted file mode 100644
index accc5c777cbb..000000000000
--- a/drivers/block/paride/dstr.c
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- dstr.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- dstr.c is a low-level protocol driver for the
- DataStor EP2000 parallel to IDE adapter chip.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
-
-*/
-
-#define DSTR_VERSION "1.01"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-/* mode codes: 0 nybble reads, 8-bit writes
- 1 8-bit reads and writes
- 2 8-bit EPP mode
- 3 EPP-16
- 4 EPP-32
-*/
-
-#define j44(a,b) (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80))
-
-#define P1 w2(5);w2(0xd);w2(5);w2(4);
-#define P2 w2(5);w2(7);w2(5);w2(4);
-#define P3 w2(6);w2(4);w2(6);w2(4);
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x20, 0x40 };
-
-static int dstr_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- r = regr + cont_map[cont];
-
- w0(0x81); P1;
- if (pi->mode) { w0(0x11); } else { w0(1); }
- P2; w0(r); P1;
-
- switch (pi->mode) {
-
- case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
- return j44(a,b);
-
- case 1: w0(0); w2(0x26); a = r0(); w2(4);
- return a;
-
- case 2:
- case 3:
- case 4: w2(0x24); a = r4(); w2(4);
- return a;
-
- }
- return -1;
-}
-
-static void dstr_write_regr( PIA *pi, int cont, int regr, int val )
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- w0(0x81); P1;
- if (pi->mode >= 2) { w0(0x11); } else { w0(1); }
- P2; w0(r); P1;
-
- switch (pi->mode) {
-
- case 0:
- case 1: w0(val); w2(5); w2(7); w2(5); w2(4);
- break;
-
- case 2:
- case 3:
- case 4: w4(val);
- break;
- }
-}
-
-#define CCP(x) w0(0xff);w2(0xc);w2(4);\
- w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\
- w0(x);w2(5);w2(4);
-
-static void dstr_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(4); CCP(0xe0); w0(0xff);
-}
-
-static void dstr_disconnect ( PIA *pi )
-
-{ CCP(0x30);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void dstr_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b;
-
- w0(0x81); P1;
- if (pi->mode) { w0(0x19); } else { w0(9); }
- P2; w0(0x82); P1; P3; w0(0x20); P1;
-
- switch (pi->mode) {
-
- case 0: for (k=0;k<count;k++) {
- w2(6); a = r1(); w2(4);
- w2(6); b = r1(); w2(4);
- buf[k] = j44(a,b);
- }
- break;
-
- case 1: w0(0);
- for (k=0;k<count;k++) {
- w2(0x26); buf[k] = r0(); w2(0x24);
- }
- w2(4);
- break;
-
- case 2: w2(0x24);
- for (k=0;k<count;k++) buf[k] = r4();
- w2(4);
- break;
-
- case 3: w2(0x24);
- for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
- w2(4);
- break;
-
- case 4: w2(0x24);
- for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
- w2(4);
- break;
-
- }
-}
-
-static void dstr_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- w0(0x81); P1;
- if (pi->mode) { w0(0x19); } else { w0(9); }
- P2; w0(0x82); P1; P3; w0(0x20); P1;
-
- switch (pi->mode) {
-
- case 0:
- case 1: for (k=0;k<count;k++) {
- w2(5); w0(buf[k]); w2(7);
- }
- w2(5); w2(4);
- break;
-
- case 2: w2(0xc5);
- for (k=0;k<count;k++) w4(buf[k]);
- w2(0xc4);
- break;
-
- case 3: w2(0xc5);
- for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
- w2(0xc4);
- break;
-
- case 4: w2(0xc5);
- for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
- w2(0xc4);
- break;
-
- }
-}
-
-
-static void dstr_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[5] = {"4-bit","8-bit","EPP-8",
- "EPP-16","EPP-32"};
-
- printk("%s: dstr %s, DataStor EP2000 at 0x%x, ",
- pi->device,DSTR_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol dstr = {
- .owner = THIS_MODULE,
- .name = "dstr",
- .max_mode = 5,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = dstr_write_regr,
- .read_regr = dstr_read_regr,
- .write_block = dstr_write_block,
- .read_block = dstr_read_block,
- .connect = dstr_connect,
- .disconnect = dstr_disconnect,
- .log_adapter = dstr_log_adapter,
-};
-
-static int __init dstr_init(void)
-{
- return paride_register(&dstr);
-}
-
-static void __exit dstr_exit(void)
-{
- paride_unregister(&dstr);
-}
-
-MODULE_LICENSE("GPL");
-module_init(dstr_init)
-module_exit(dstr_exit)
diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c
deleted file mode 100644
index 1bcdff77322e..000000000000
--- a/drivers/block/paride/epat.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- epat.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is the low level protocol driver for the EPAT parallel
- to IDE adapter from Shuttle Technologies. This adapter is
- used in many popular parallel port disk products such as the
- SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
- 1.02 Joshua b. Jore CPP(renamed), epat_connect, epat_disconnect
-
-*/
-
-#define EPAT_VERSION "1.02"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
-#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
-
-static int epatc8;
-
-module_param(epatc8, int, 0);
-MODULE_PARM_DESC(epatc8, "support for the Shuttle EP1284 chip, "
- "used in any recent Imation SuperDisk (LS-120) drive.");
-
-/* cont = 0 IDE register file
- cont = 1 IDE control registers
- cont = 2 internal EPAT registers
-*/
-
-static int cont_map[3] = { 0x18, 0x10, 0 };
-
-static void epat_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- switch (pi->mode) {
-
- case 0:
- case 1:
- case 2: w0(0x60+r); w2(1); w0(val); w2(4);
- break;
-
- case 3:
- case 4:
- case 5: w3(0x40+r); w4(val);
- break;
-
- }
-}
-
-static int epat_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- r = regr + cont_map[cont];
-
- switch (pi->mode) {
-
- case 0: w0(r); w2(1); w2(3);
- a = r1(); w2(4); b = r1();
- return j44(a,b);
-
- case 1: w0(0x40+r); w2(1); w2(4);
- a = r1(); b = r2(); w0(0xff);
- return j53(a,b);
-
- case 2: w0(0x20+r); w2(1); w2(0x25);
- a = r0(); w2(4);
- return a;
-
- case 3:
- case 4:
- case 5: w3(r); w2(0x24); a = r4(); w2(4);
- return a;
-
- }
- return -1; /* never gets here */
-}
-
-static void epat_read_block( PIA *pi, char * buf, int count )
-
-{ int k, ph, a, b;
-
- switch (pi->mode) {
-
- case 0: w0(7); w2(1); w2(3); w0(0xff);
- ph = 0;
- for(k=0;k<count;k++) {
- if (k == count-1) w0(0xfd);
- w2(6+ph); a = r1();
- if (a & 8) b = a;
- else { w2(4+ph); b = r1(); }
- buf[k] = j44(a,b);
- ph = 1 - ph;
- }
- w0(0); w2(4);
- break;
-
- case 1: w0(0x47); w2(1); w2(5); w0(0xff);
- ph = 0;
- for(k=0;k<count;k++) {
- if (k == count-1) w0(0xfd);
- w2(4+ph);
- a = r1(); b = r2();
- buf[k] = j53(a,b);
- ph = 1 - ph;
- }
- w0(0); w2(4);
- break;
-
- case 2: w0(0x27); w2(1); w2(0x25); w0(0);
- ph = 0;
- for(k=0;k<count-1;k++) {
- w2(0x24+ph);
- buf[k] = r0();
- ph = 1 - ph;
- }
- w2(0x26); w2(0x27); buf[count-1] = r0();
- w2(0x25); w2(4);
- break;
-
- case 3: w3(0x80); w2(0x24);
- for(k=0;k<count-1;k++) buf[k] = r4();
- w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
- w2(4);
- break;
-
- case 4: w3(0x80); w2(0x24);
- for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
- buf[count-2] = r4();
- w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
- w2(4);
- break;
-
- case 5: w3(0x80); w2(0x24);
- for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
- for(k=count-4;k<count-1;k++) buf[k] = r4();
- w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
- w2(4);
- break;
-
- }
-}
-
-static void epat_write_block( PIA *pi, char * buf, int count )
-
-{ int ph, k;
-
- switch (pi->mode) {
-
- case 0:
- case 1:
- case 2: w0(0x67); w2(1); w2(5);
- ph = 0;
- for(k=0;k<count;k++) {
- w0(buf[k]);
- w2(4+ph);
- ph = 1 - ph;
- }
- w2(7); w2(4);
- break;
-
- case 3: w3(0xc0);
- for(k=0;k<count;k++) w4(buf[k]);
- w2(4);
- break;
-
- case 4: w3(0xc0);
- for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]);
- w2(4);
- break;
-
- case 5: w3(0xc0);
- for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]);
- w2(4);
- break;
-
- }
-}
-
-/* these macros access the EPAT registers in native addressing */
-
-#define WR(r,v) epat_write_regr(pi,2,r,v)
-#define RR(r) (epat_read_regr(pi,2,r))
-
-/* and these access the IDE task file */
-
-#define WRi(r,v) epat_write_regr(pi,0,r,v)
-#define RRi(r) (epat_read_regr(pi,0,r))
-
-/* FIXME: the CPP stuff should be fixed to handle multiple EPATs on a chain */
-
-#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
- w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
-
-static void epat_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
-
- /* Initialize the chip */
- CPP(0);
-
- if (epatc8) {
- CPP(0x40);CPP(0xe0);
- w0(0);w2(1);w2(4);
- WR(0x8,0x12);WR(0xc,0x14);WR(0x12,0x10);
- WR(0xe,0xf);WR(0xf,4);
- /* WR(0xe,0xa);WR(0xf,4); */
- WR(0xe,0xd);WR(0xf,0);
- /* CPP(0x30); */
- }
-
- /* Connect to the chip */
- CPP(0xe0);
- w0(0);w2(1);w2(4); /* Idle into SPP */
- if (pi->mode >= 3) {
- w0(0);w2(1);w2(4);w2(0xc);
- /* Request EPP */
- w0(0x40);w2(6);w2(7);w2(4);w2(0xc);w2(4);
- }
-
- if (!epatc8) {
- WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
- }
-}
-
-static void epat_disconnect (PIA *pi)
-{ CPP(0x30);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static int epat_test_proto( PIA *pi, char * scratch, int verbose )
-
-{ int k, j, f, cc;
- int e[2] = {0,0};
-
- epat_connect(pi);
- cc = RR(0xd);
- epat_disconnect(pi);
-
- epat_connect(pi);
- for (j=0;j<2;j++) {
- WRi(6,0xa0+j*0x10);
- for (k=0;k<256;k++) {
- WRi(2,k^0xaa);
- WRi(3,k^0x55);
- if (RRi(2) != (k^0xaa)) e[j]++;
- }
- }
- epat_disconnect(pi);
-
- f = 0;
- epat_connect(pi);
- WR(0x13,1); WR(0x13,0); WR(0xa,0x11);
- epat_read_block(pi,scratch,512);
-
- for (k=0;k<256;k++) {
- if ((scratch[2*k] & 0xff) != k) f++;
- if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++;
- }
- epat_disconnect(pi);
-
- if (verbose) {
- printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
- pi->device,pi->port,pi->mode,cc,e[0],e[1],f);
- }
-
- return (e[0] && e[1]) || f;
-}
-
-static void epat_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ int ver;
- char *mode_string[6] =
- {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"};
-
- epat_connect(pi);
- WR(0xa,0x38); /* read the version code */
- ver = RR(0xb);
- epat_disconnect(pi);
-
- printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ",
- pi->device,EPAT_VERSION,ver,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol epat = {
- .owner = THIS_MODULE,
- .name = "epat",
- .max_mode = 6,
- .epp_first = 3,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = epat_write_regr,
- .read_regr = epat_read_regr,
- .write_block = epat_write_block,
- .read_block = epat_read_block,
- .connect = epat_connect,
- .disconnect = epat_disconnect,
- .test_proto = epat_test_proto,
- .log_adapter = epat_log_adapter,
-};
-
-static int __init epat_init(void)
-{
-#ifdef CONFIG_PARIDE_EPATC8
- epatc8 = 1;
-#endif
- return paride_register(&epat);
-}
-
-static void __exit epat_exit(void)
-{
- paride_unregister(&epat);
-}
-
-MODULE_LICENSE("GPL");
-module_init(epat_init)
-module_exit(epat_exit)
diff --git a/drivers/block/paride/epia.c b/drivers/block/paride/epia.c
deleted file mode 100644
index fb0e782d055e..000000000000
--- a/drivers/block/paride/epia.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- epia.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- epia.c is a low-level protocol driver for Shuttle Technologies
- EPIA parallel to IDE adapter chip. This device is now obsolete
- and has been replaced with the EPAT chip, which is supported
- by epat.c, however, some devices based on EPIA are still
- available.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
- 1.02 GRG 1998.06.17 support older versions of EPIA
-
-*/
-
-#define EPIA_VERSION "1.02"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-/* mode codes: 0 nybble reads on port 1, 8-bit writes
- 1 5/3 reads on ports 1 & 2, 8-bit writes
- 2 8-bit reads and writes
- 3 8-bit EPP mode
- 4 16-bit EPP
- 5 32-bit EPP
-*/
-
-#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
-#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
-
-/* cont = 0 IDE register file
- cont = 1 IDE control registers
-*/
-
-static int cont_map[2] = { 0, 0x80 };
-
-static int epia_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- regr += cont_map[cont];
-
- switch (pi->mode) {
-
- case 0: r = regr^0x39;
- w0(r); w2(1); w2(3); w0(r);
- a = r1(); w2(1); b = r1(); w2(4);
- return j44(a,b);
-
- case 1: r = regr^0x31;
- w0(r); w2(1); w0(r&0x37);
- w2(3); w2(5); w0(r|0xf0);
- a = r1(); b = r2(); w2(4);
- return j53(a,b);
-
- case 2: r = regr^0x29;
- w0(r); w2(1); w2(0X21); w2(0x23);
- a = r0(); w2(4);
- return a;
-
- case 3:
- case 4:
- case 5: w3(regr); w2(0x24); a = r4(); w2(4);
- return a;
-
- }
- return -1;
-}
-
-static void epia_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int r;
-
- regr += cont_map[cont];
-
- switch (pi->mode) {
-
- case 0:
- case 1:
- case 2: r = regr^0x19;
- w0(r); w2(1); w0(val); w2(3); w2(4);
- break;
-
- case 3:
- case 4:
- case 5: r = regr^0x40;
- w3(r); w4(val); w2(4);
- break;
- }
-}
-
-#define WR(r,v) epia_write_regr(pi,0,r,v)
-#define RR(r) (epia_read_regr(pi,0,r))
-
-/* The use of register 0x84 is entirely unclear - it seems to control
- some EPP counters ... currently we know about 3 different block
- sizes: the standard 512 byte reads and writes, 12 byte writes and
- 2048 byte reads (the last two being used in the CDrom drivers.
-*/
-
-static void epia_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
-
- w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
- w2(1); w2(4);
- if (pi->mode >= 3) {
- w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
- w2(0x24); w2(0x26); w2(4);
- }
- WR(0x86,8);
-}
-
-static void epia_disconnect ( PIA *pi )
-
-{ /* WR(0x84,0x10); */
- w0(pi->saved_r0);
- w2(1); w2(4);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void epia_read_block( PIA *pi, char * buf, int count )
-
-{ int k, ph, a, b;
-
- switch (pi->mode) {
-
- case 0: w0(0x81); w2(1); w2(3); w0(0xc1);
- ph = 1;
- for (k=0;k<count;k++) {
- w2(2+ph); a = r1();
- w2(4+ph); b = r1();
- buf[k] = j44(a,b);
- ph = 1 - ph;
- }
- w0(0); w2(4);
- break;
-
- case 1: w0(0x91); w2(1); w0(0x10); w2(3);
- w0(0x51); w2(5); w0(0xd1);
- ph = 1;
- for (k=0;k<count;k++) {
- w2(4+ph);
- a = r1(); b = r2();
- buf[k] = j53(a,b);
- ph = 1 - ph;
- }
- w0(0); w2(4);
- break;
-
- case 2: w0(0x89); w2(1); w2(0x23); w2(0x21);
- ph = 1;
- for (k=0;k<count;k++) {
- w2(0x24+ph);
- buf[k] = r0();
- ph = 1 - ph;
- }
- w2(6); w2(4);
- break;
-
- case 3: if (count > 512) WR(0x84,3);
- w3(0); w2(0x24);
- for (k=0;k<count;k++) buf[k] = r4();
- w2(4); WR(0x84,0);
- break;
-
- case 4: if (count > 512) WR(0x84,3);
- w3(0); w2(0x24);
- for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
- w2(4); WR(0x84,0);
- break;
-
- case 5: if (count > 512) WR(0x84,3);
- w3(0); w2(0x24);
- for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
- w2(4); WR(0x84,0);
- break;
-
- }
-}
-
-static void epia_write_block( PIA *pi, char * buf, int count )
-
-{ int ph, k, last, d;
-
- switch (pi->mode) {
-
- case 0:
- case 1:
- case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5);
- ph = 0; last = 0x8000;
- for (k=0;k<count;k++) {
- d = buf[k];
- if (d != last) { last = d; w0(d); }
- w2(4+ph);
- ph = 1 - ph;
- }
- w2(7); w2(4);
- break;
-
- case 3: if (count < 512) WR(0x84,1);
- w3(0x40);
- for (k=0;k<count;k++) w4(buf[k]);
- if (count < 512) WR(0x84,0);
- break;
-
- case 4: if (count < 512) WR(0x84,1);
- w3(0x40);
- for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
- if (count < 512) WR(0x84,0);
- break;
-
- case 5: if (count < 512) WR(0x84,1);
- w3(0x40);
- for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
- if (count < 512) WR(0x84,0);
- break;
-
- }
-
-}
-
-static int epia_test_proto( PIA *pi, char * scratch, int verbose )
-
-{ int j, k, f;
- int e[2] = {0,0};
-
- epia_connect(pi);
- for (j=0;j<2;j++) {
- WR(6,0xa0+j*0x10);
- for (k=0;k<256;k++) {
- WR(2,k^0xaa);
- WR(3,k^0x55);
- if (RR(2) != (k^0xaa)) e[j]++;
- }
- WR(2,1); WR(3,1);
- }
- epia_disconnect(pi);
-
- f = 0;
- epia_connect(pi);
- WR(0x84,8);
- epia_read_block(pi,scratch,512);
- for (k=0;k<256;k++) {
- if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++;
- if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++;
- }
- WR(0x84,0);
- epia_disconnect(pi);
-
- if (verbose) {
- printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
- pi->device,pi->port,pi->mode,e[0],e[1],f);
- }
-
- return (e[0] && e[1]) || f;
-
-}
-
-
-static void epia_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[6] = {"4-bit","5/3","8-bit",
- "EPP-8","EPP-16","EPP-32"};
-
- printk("%s: epia %s, Shuttle EPIA at 0x%x, ",
- pi->device,EPIA_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol epia = {
- .owner = THIS_MODULE,
- .name = "epia",
- .max_mode = 6,
- .epp_first = 3,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = epia_write_regr,
- .read_regr = epia_read_regr,
- .write_block = epia_write_block,
- .read_block = epia_read_block,
- .connect = epia_connect,
- .disconnect = epia_disconnect,
- .test_proto = epia_test_proto,
- .log_adapter = epia_log_adapter,
-};
-
-static int __init epia_init(void)
-{
- return paride_register(&epia);
-}
-
-static void __exit epia_exit(void)
-{
- paride_unregister(&epia);
-}
-
-MODULE_LICENSE("GPL");
-module_init(epia_init)
-module_exit(epia_exit)
diff --git a/drivers/block/paride/fit2.c b/drivers/block/paride/fit2.c
deleted file mode 100644
index 381283753ae4..000000000000
--- a/drivers/block/paride/fit2.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- fit2.c (c) 1998 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- fit2.c is a low-level protocol driver for the older version
- of the Fidelity International Technology parallel port adapter.
- This adapter is used in their TransDisk 2000 and older TransDisk
- 3000 portable hard-drives. As far as I can tell, this device
- supports 4-bit mode _only_.
-
- Newer models of the FIT products use an enhanced protocol.
- The "fit3" protocol module should support current drives.
-
-*/
-
-#define FIT2_VERSION "1.0"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-
-NB: The FIT adapter does not appear to use the control registers.
-So, we map ALT_STATUS to STATUS and NO-OP writes to the device
-control register - this means that IDE reset will not work on these
-devices.
-
-*/
-
-static void fit2_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ if (cont == 1) return;
- w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4);
-}
-
-static int fit2_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- if (cont) {
- if (regr != 6) return 0xff;
- r = 7;
- } else r = regr + 0x10;
-
- w2(0xc); w0(r); w2(4); w2(5);
- w0(0); a = r1();
- w0(1); b = r1();
- w2(4);
-
- return j44(a,b);
-
-}
-
-static void fit2_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b, c, d;
-
- w2(0xc); w0(0x10);
-
- for (k=0;k<count/4;k++) {
-
- w2(4); w2(5);
- w0(0); a = r1(); w0(1); b = r1();
- w0(3); c = r1(); w0(2); d = r1();
- buf[4*k+0] = j44(a,b);
- buf[4*k+1] = j44(d,c);
-
- w2(4); w2(5);
- a = r1(); w0(3); b = r1();
- w0(1); c = r1(); w0(0); d = r1();
- buf[4*k+2] = j44(d,c);
- buf[4*k+3] = j44(a,b);
-
- }
-
- w2(4);
-
-}
-
-static void fit2_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
-
- w2(0xc); w0(0);
- for (k=0;k<count/2;k++) {
- w2(4); w0(buf[2*k]);
- w2(5); w0(buf[2*k+1]);
- }
- w2(4);
-}
-
-static void fit2_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(0xcc);
-}
-
-static void fit2_disconnect ( PIA *pi )
-
-{ w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void fit2_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n",
- pi->device,FIT2_VERSION,pi->port,pi->delay);
-
-}
-
-static struct pi_protocol fit2 = {
- .owner = THIS_MODULE,
- .name = "fit2",
- .max_mode = 1,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = fit2_write_regr,
- .read_regr = fit2_read_regr,
- .write_block = fit2_write_block,
- .read_block = fit2_read_block,
- .connect = fit2_connect,
- .disconnect = fit2_disconnect,
- .log_adapter = fit2_log_adapter,
-};
-
-static int __init fit2_init(void)
-{
- return paride_register(&fit2);
-}
-
-static void __exit fit2_exit(void)
-{
- paride_unregister(&fit2);
-}
-
-MODULE_LICENSE("GPL");
-module_init(fit2_init)
-module_exit(fit2_exit)
diff --git a/drivers/block/paride/fit3.c b/drivers/block/paride/fit3.c
deleted file mode 100644
index 275d269458eb..000000000000
--- a/drivers/block/paride/fit3.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- fit3.c (c) 1998 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- fit3.c is a low-level protocol driver for newer models
- of the Fidelity International Technology parallel port adapter.
- This adapter is used in their TransDisk 3000 portable
- hard-drives, as well as CD-ROM, PD-CD and other devices.
-
- The TD-2000 and certain older devices use a different protocol.
- Try the fit2 protocol module with them.
-
- NB: The FIT adapters do not appear to support the control
- registers. So, we map ALT_STATUS to STATUS and NO-OP writes
- to the device control register - this means that IDE reset
- will not work on these devices.
-
-*/
-
-#define FIT3_VERSION "1.0"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
-
-#define w7(byte) {out_p(7,byte);}
-#define r7() (in_p(7) & 0xff)
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-
-*/
-
-static void fit3_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ if (cont == 1) return;
-
- switch (pi->mode) {
-
- case 0:
- case 1: w2(0xc); w0(regr); w2(0x8); w2(0xc);
- w0(val); w2(0xd);
- w0(0); w2(0xc);
- break;
-
- case 2: w2(0xc); w0(regr); w2(0x8); w2(0xc);
- w4(val); w4(0);
- w2(0xc);
- break;
-
- }
-}
-
-static int fit3_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b;
-
- if (cont) {
- if (regr != 6) return 0xff;
- regr = 7;
- }
-
- switch (pi->mode) {
-
- case 0: w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc);
- w2(0xd); a = r1();
- w2(0xf); b = r1();
- w2(0xc);
- return j44(a,b);
-
- case 1: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
- w2(0xec); w2(0xee); w2(0xef); a = r0();
- w2(0xc);
- return a;
-
- case 2: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
- w2(0xec);
- a = r4(); b = r4();
- w2(0xc);
- return a;
-
- }
- return -1;
-
-}
-
-static void fit3_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b, c, d;
-
- switch (pi->mode) {
-
- case 0: w2(0xc); w0(0x10); w2(0x8); w2(0xc);
- for (k=0;k<count/2;k++) {
- w2(0xd); a = r1();
- w2(0xf); b = r1();
- w2(0xc); c = r1();
- w2(0xe); d = r1();
- buf[2*k ] = j44(a,b);
- buf[2*k+1] = j44(c,d);
- }
- w2(0xc);
- break;
-
- case 1: w2(0xc); w0(0x90); w2(0x8); w2(0xc);
- w2(0xec); w2(0xee);
- for (k=0;k<count/2;k++) {
- w2(0xef); a = r0();
- w2(0xee); b = r0();
- buf[2*k ] = a;
- buf[2*k+1] = b;
- }
- w2(0xec);
- w2(0xc);
- break;
-
- case 2: w2(0xc); w0(0x90); w2(0x8); w2(0xc);
- w2(0xec);
- for (k=0;k<count;k++) buf[k] = r4();
- w2(0xc);
- break;
-
- }
-}
-
-static void fit3_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- switch (pi->mode) {
-
- case 0:
- case 1: w2(0xc); w0(0); w2(0x8); w2(0xc);
- for (k=0;k<count/2;k++) {
- w0(buf[2*k ]); w2(0xd);
- w0(buf[2*k+1]); w2(0xc);
- }
- break;
-
- case 2: w2(0xc); w0(0); w2(0x8); w2(0xc);
- for (k=0;k<count;k++) w4(buf[k]);
- w2(0xc);
- break;
- }
-}
-
-static void fit3_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(0xc); w0(0); w2(0xa);
- if (pi->mode == 2) {
- w2(0xc); w0(0x9); w2(0x8); w2(0xc);
- }
-}
-
-static void fit3_disconnect ( PIA *pi )
-
-{ w2(0xc); w0(0xa); w2(0x8); w2(0xc);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void fit3_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[3] = {"4-bit","8-bit","EPP"};
-
- printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, "
- "mode %d (%s), delay %d\n",
- pi->device,FIT3_VERSION,pi->port,
- pi->mode,mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol fit3 = {
- .owner = THIS_MODULE,
- .name = "fit3",
- .max_mode = 3,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = fit3_write_regr,
- .read_regr = fit3_read_regr,
- .write_block = fit3_write_block,
- .read_block = fit3_read_block,
- .connect = fit3_connect,
- .disconnect = fit3_disconnect,
- .log_adapter = fit3_log_adapter,
-};
-
-static int __init fit3_init(void)
-{
- return paride_register(&fit3);
-}
-
-static void __exit fit3_exit(void)
-{
- paride_unregister(&fit3);
-}
-
-MODULE_LICENSE("GPL");
-module_init(fit3_init)
-module_exit(fit3_exit)
diff --git a/drivers/block/paride/friq.c b/drivers/block/paride/friq.c
deleted file mode 100644
index 4f2ba244689b..000000000000
--- a/drivers/block/paride/friq.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- friq.c (c) 1998 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License
-
- friq.c is a low-level protocol driver for the Freecom "IQ"
- parallel port IDE adapter. Early versions of this adapter
- use the 'frpw' protocol.
-
- Freecom uses this adapter in a battery powered external
- CD-ROM drive. It is also used in LS-120 drives by
- Maxell and Panasonic, and other devices.
-
- The battery powered drive requires software support to
- control the power to the drive. This module enables the
- drive power when the high level driver (pcd) is loaded
- and disables it when the module is unloaded. Note, if
- the friq module is built in to the kernel, the power
- will never be switched off, so other means should be
- used to conserve battery power.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.12.20 Added support for soft power switch
-*/
-
-#define FRIQ_VERSION "1.01"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define CMD(x) w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
- w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
-
-#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x08, 0x10 };
-
-static int friq_read_regr( PIA *pi, int cont, int regr )
-
-{ int h,l,r;
-
- r = regr + cont_map[cont];
-
- CMD(r);
- w2(6); l = r1();
- w2(4); h = r1();
- w2(4);
-
- return j44(l,h);
-
-}
-
-static void friq_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- CMD(r);
- w0(val);
- w2(5);w2(7);w2(5);w2(4);
-}
-
-static void friq_read_block_int( PIA *pi, char * buf, int count, int regr )
-
-{ int h, l, k, ph;
-
- switch(pi->mode) {
-
- case 0: CMD(regr);
- for (k=0;k<count;k++) {
- w2(6); l = r1();
- w2(4); h = r1();
- buf[k] = j44(l,h);
- }
- w2(4);
- break;
-
- case 1: ph = 2;
- CMD(regr+0xc0);
- w0(0xff);
- for (k=0;k<count;k++) {
- w2(0xa4 + ph);
- buf[k] = r0();
- ph = 2 - ph;
- }
- w2(0xac); w2(0xa4); w2(4);
- break;
-
- case 2: CMD(regr+0x80);
- for (k=0;k<count-2;k++) buf[k] = r4();
- w2(0xac); w2(0xa4);
- buf[count-2] = r4();
- buf[count-1] = r4();
- w2(4);
- break;
-
- case 3: CMD(regr+0x80);
- for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
- w2(0xac); w2(0xa4);
- buf[count-2] = r4();
- buf[count-1] = r4();
- w2(4);
- break;
-
- case 4: CMD(regr+0x80);
- for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
- buf[count-4] = r4();
- buf[count-3] = r4();
- w2(0xac); w2(0xa4);
- buf[count-2] = r4();
- buf[count-1] = r4();
- w2(4);
- break;
-
- }
-}
-
-static void friq_read_block( PIA *pi, char * buf, int count)
-
-{ friq_read_block_int(pi,buf,count,0x08);
-}
-
-static void friq_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- switch(pi->mode) {
-
- case 0:
- case 1: CMD(8); w2(5);
- for (k=0;k<count;k++) {
- w0(buf[k]);
- w2(7);w2(5);
- }
- w2(4);
- break;
-
- case 2: CMD(0xc8); w2(5);
- for (k=0;k<count;k++) w4(buf[k]);
- w2(4);
- break;
-
- case 3: CMD(0xc8); w2(5);
- for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
- w2(4);
- break;
-
- case 4: CMD(0xc8); w2(5);
- for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
- w2(4);
- break;
- }
-}
-
-static void friq_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(4);
-}
-
-static void friq_disconnect ( PIA *pi )
-
-{ CMD(0x20);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static int friq_test_proto( PIA *pi, char * scratch, int verbose )
-
-{ int j, k, r;
- int e[2] = {0,0};
-
- pi->saved_r0 = r0();
- w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
- udelay(500);
- w0(pi->saved_r0);
-
- friq_connect(pi);
- for (j=0;j<2;j++) {
- friq_write_regr(pi,0,6,0xa0+j*0x10);
- for (k=0;k<256;k++) {
- friq_write_regr(pi,0,2,k^0xaa);
- friq_write_regr(pi,0,3,k^0x55);
- if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
- }
- }
- friq_disconnect(pi);
-
- friq_connect(pi);
- friq_read_block_int(pi,scratch,512,0x10);
- r = 0;
- for (k=0;k<128;k++) if (scratch[k] != k) r++;
- friq_disconnect(pi);
-
- if (verbose) {
- printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
- pi->device,pi->port,pi->mode,e[0],e[1],r);
- }
-
- return (r || (e[0] && e[1]));
-}
-
-
-static void friq_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[6] = {"4-bit","8-bit",
- "EPP-8","EPP-16","EPP-32"};
-
- printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device,
- FRIQ_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
- pi->private = 1;
- friq_connect(pi);
- CMD(0x9e); /* disable sleep timer */
- friq_disconnect(pi);
-
-}
-
-static void friq_release_proto( PIA *pi)
-{
- if (pi->private) { /* turn off the power */
- friq_connect(pi);
- CMD(0x1d); CMD(0x1e);
- friq_disconnect(pi);
- pi->private = 0;
- }
-}
-
-static struct pi_protocol friq = {
- .owner = THIS_MODULE,
- .name = "friq",
- .max_mode = 5,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = friq_write_regr,
- .read_regr = friq_read_regr,
- .write_block = friq_write_block,
- .read_block = friq_read_block,
- .connect = friq_connect,
- .disconnect = friq_disconnect,
- .test_proto = friq_test_proto,
- .log_adapter = friq_log_adapter,
- .release_proto = friq_release_proto,
-};
-
-static int __init friq_init(void)
-{
- return paride_register(&friq);
-}
-
-static void __exit friq_exit(void)
-{
- paride_unregister(&friq);
-}
-
-MODULE_LICENSE("GPL");
-module_init(friq_init)
-module_exit(friq_exit)
diff --git a/drivers/block/paride/frpw.c b/drivers/block/paride/frpw.c
deleted file mode 100644
index c3cde364603a..000000000000
--- a/drivers/block/paride/frpw.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- frpw.c (c) 1996-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License
-
- frpw.c is a low-level protocol driver for the Freecom "Power"
- parallel port IDE adapter.
-
- Some applications of this adapter may require a "printer" reset
- prior to loading the driver. This can be done by loading and
- unloading the "lp" driver, or it can be done by this driver
- if you define FRPW_HARD_RESET. The latter is not recommended
- as it may upset devices on other ports.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
- fix chip detect
- added EPP-16 and EPP-32
- 1.02 GRG 1998.09.23 added hard reset to initialisation process
- 1.03 GRG 1998.12.14 made hard reset conditional
-
-*/
-
-#define FRPW_VERSION "1.03"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
-#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x08, 0x10 };
-
-static int frpw_read_regr( PIA *pi, int cont, int regr )
-
-{ int h,l,r;
-
- r = regr + cont_map[cont];
-
- w2(4);
- w0(r); cec4;
- w2(6); l = r1();
- w2(4); h = r1();
- w2(4);
-
- return j44(l,h);
-
-}
-
-static void frpw_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- w2(4); w0(r); cec4;
- w0(val);
- w2(5);w2(7);w2(5);w2(4);
-}
-
-static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
-
-{ int h, l, k, ph;
-
- switch(pi->mode) {
-
- case 0: w2(4); w0(regr); cec4;
- for (k=0;k<count;k++) {
- w2(6); l = r1();
- w2(4); h = r1();
- buf[k] = j44(l,h);
- }
- w2(4);
- break;
-
- case 1: ph = 2;
- w2(4); w0(regr + 0xc0); cec4;
- w0(0xff);
- for (k=0;k<count;k++) {
- w2(0xa4 + ph);
- buf[k] = r0();
- ph = 2 - ph;
- }
- w2(0xac); w2(0xa4); w2(4);
- break;
-
- case 2: w2(4); w0(regr + 0x80); cec4;
- for (k=0;k<count;k++) buf[k] = r4();
- w2(0xac); w2(0xa4);
- w2(4);
- break;
-
- case 3: w2(4); w0(regr + 0x80); cec4;
- for (k=0;k<count-2;k++) buf[k] = r4();
- w2(0xac); w2(0xa4);
- buf[count-2] = r4();
- buf[count-1] = r4();
- w2(4);
- break;
-
- case 4: w2(4); w0(regr + 0x80); cec4;
- for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
- w2(0xac); w2(0xa4);
- buf[count-2] = r4();
- buf[count-1] = r4();
- w2(4);
- break;
-
- case 5: w2(4); w0(regr + 0x80); cec4;
- for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
- buf[count-4] = r4();
- buf[count-3] = r4();
- w2(0xac); w2(0xa4);
- buf[count-2] = r4();
- buf[count-1] = r4();
- w2(4);
- break;
-
- }
-}
-
-static void frpw_read_block( PIA *pi, char * buf, int count)
-
-{ frpw_read_block_int(pi,buf,count,0x08);
-}
-
-static void frpw_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- switch(pi->mode) {
-
- case 0:
- case 1:
- case 2: w2(4); w0(8); cec4; w2(5);
- for (k=0;k<count;k++) {
- w0(buf[k]);
- w2(7);w2(5);
- }
- w2(4);
- break;
-
- case 3: w2(4); w0(0xc8); cec4; w2(5);
- for (k=0;k<count;k++) w4(buf[k]);
- w2(4);
- break;
-
- case 4: w2(4); w0(0xc8); cec4; w2(5);
- for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
- w2(4);
- break;
-
- case 5: w2(4); w0(0xc8); cec4; w2(5);
- for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
- w2(4);
- break;
- }
-}
-
-static void frpw_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(4);
-}
-
-static void frpw_disconnect ( PIA *pi )
-
-{ w2(4); w0(0x20); cec4;
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-/* Stub logic to see if PNP string is available - used to distinguish
- between the Xilinx and ASIC implementations of the Freecom adapter.
-*/
-
-static int frpw_test_pnp ( PIA *pi )
-
-/* returns chip_type: 0 = Xilinx, 1 = ASIC */
-
-{ int olddelay, a, b;
-
-#ifdef FRPW_HARD_RESET
- w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */
- mdelay(1500);
-#endif
-
- olddelay = pi->delay;
- pi->delay = 10;
-
- pi->saved_r0 = r0();
- pi->saved_r2 = r2();
-
- w2(4); w0(4); w2(6); w2(7);
- a = r1() & 0xff; w2(4); b = r1() & 0xff;
- w2(0xc); w2(0xe); w2(4);
-
- pi->delay = olddelay;
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-
- return ((~a&0x40) && (b&0x40));
-}
-
-/* We use the pi->private to remember the result of the PNP test.
- To make this work, private = port*2 + chip. Yes, I know it's
- a hack :-(
-*/
-
-static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
-
-{ int j, k, r;
- int e[2] = {0,0};
-
- if ((pi->private>>1) != pi->port)
- pi->private = frpw_test_pnp(pi) + 2*pi->port;
-
- if (((pi->private%2) == 0) && (pi->mode > 2)) {
- if (verbose)
- printk("%s: frpw: Xilinx does not support mode %d\n",
- pi->device, pi->mode);
- return 1;
- }
-
- if (((pi->private%2) == 1) && (pi->mode == 2)) {
- if (verbose)
- printk("%s: frpw: ASIC does not support mode 2\n",
- pi->device);
- return 1;
- }
-
- frpw_connect(pi);
- for (j=0;j<2;j++) {
- frpw_write_regr(pi,0,6,0xa0+j*0x10);
- for (k=0;k<256;k++) {
- frpw_write_regr(pi,0,2,k^0xaa);
- frpw_write_regr(pi,0,3,k^0x55);
- if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
- }
- }
- frpw_disconnect(pi);
-
- frpw_connect(pi);
- frpw_read_block_int(pi,scratch,512,0x10);
- r = 0;
- for (k=0;k<128;k++) if (scratch[k] != k) r++;
- frpw_disconnect(pi);
-
- if (verbose) {
- printk("%s: frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n",
- pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r);
- }
-
- return (r || (e[0] && e[1]));
-}
-
-
-static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[6] = {"4-bit","8-bit","EPP",
- "EPP-8","EPP-16","EPP-32"};
-
- printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
- FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol frpw = {
- .owner = THIS_MODULE,
- .name = "frpw",
- .max_mode = 6,
- .epp_first = 2,
- .default_delay = 2,
- .max_units = 1,
- .write_regr = frpw_write_regr,
- .read_regr = frpw_read_regr,
- .write_block = frpw_write_block,
- .read_block = frpw_read_block,
- .connect = frpw_connect,
- .disconnect = frpw_disconnect,
- .test_proto = frpw_test_proto,
- .log_adapter = frpw_log_adapter,
-};
-
-static int __init frpw_init(void)
-{
- return paride_register(&frpw);
-}
-
-static void __exit frpw_exit(void)
-{
- paride_unregister(&frpw);
-}
-
-MODULE_LICENSE("GPL");
-module_init(frpw_init)
-module_exit(frpw_exit)
diff --git a/drivers/block/paride/kbic.c b/drivers/block/paride/kbic.c
deleted file mode 100644
index 35999c415ee3..000000000000
--- a/drivers/block/paride/kbic.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- kbic.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is a low-level driver for the KBIC-951A and KBIC-971A
- parallel to IDE adapter chips from KingByte Information Systems.
-
- The chips are almost identical, however, the wakeup code
- required for the 971A interferes with the correct operation of
- the 951A, so this driver registers itself twice, once for
- each chip.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
-
-*/
-
-#define KBIC_VERSION "1.01"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define r12w() (delay_p,inw(pi->port+1)&0xffff)
-
-#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
-#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0))
-
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x80, 0x40 };
-
-static int kbic_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, s;
-
- s = cont_map[cont];
-
- switch (pi->mode) {
-
- case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8);
- a = r1(); w0(0x28); b = r1(); w2(4);
- return j44(a,b);
-
- case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8);
- a = r12w(); w2(4);
- return j53(a);
-
- case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
- a = r0(); w2(4);
- return a;
-
- case 3:
- case 4:
- case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
- a = r4(); b = r4(); w2(4); w2(0); w2(4);
- return a;
-
- }
- return -1;
-}
-
-static void kbic_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int s;
-
- s = cont_map[cont];
-
- switch (pi->mode) {
-
- case 0:
- case 1:
- case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4);
- w0(val); w2(5); w2(4);
- break;
-
- case 3:
- case 4:
- case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
- w4(val); w4(val);
- w2(4); w2(0); w2(4);
- break;
-
- }
-}
-
-static void k951_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(4);
-}
-
-static void k951_disconnect ( PIA *pi )
-
-{ w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-#define CCP(x) w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\
- w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff);
-
-static void k971_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- CCP(0x20);
- w2(4);
-}
-
-static void k971_disconnect ( PIA *pi )
-
-{ CCP(0x30);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-/* counts must be congruent to 0 MOD 4, but all known applications
- have this property.
-*/
-
-static void kbic_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b;
-
- switch (pi->mode) {
-
- case 0: w0(0x98); w2(4); w2(6); w2(4);
- for (k=0;k<count/2;k++) {
- w2(1); w0(8); a = r1();
- w0(0x28); b = r1();
- buf[2*k] = j44(a,b);
- w2(5); b = r1();
- w0(8); a = r1();
- buf[2*k+1] = j44(a,b);
- w2(4);
- }
- break;
-
- case 1: w0(0xb8); w2(4); w2(6); w2(4);
- for (k=0;k<count/4;k++) {
- w0(0xb8);
- w2(4); w2(5);
- w0(8); buf[4*k] = j53(r12w());
- w0(0xb8); buf[4*k+1] = j53(r12w());
- w2(4); w2(5);
- buf[4*k+3] = j53(r12w());
- w0(8); buf[4*k+2] = j53(r12w());
- }
- w2(4);
- break;
-
- case 2: w0(0x88); w2(4); w2(6); w2(4);
- for (k=0;k<count/2;k++) {
- w2(0xa0); w2(0xa1); buf[2*k] = r0();
- w2(0xa5); buf[2*k+1] = r0();
- }
- w2(4);
- break;
-
- case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
- for (k=0;k<count;k++) buf[k] = r4();
- w2(4); w2(0); w2(4);
- break;
-
- case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
- for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
- w2(4); w2(0); w2(4);
- break;
-
- case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
- for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
- w2(4); w2(0); w2(4);
- break;
-
-
- }
-}
-
-static void kbic_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- switch (pi->mode) {
-
- case 0:
- case 1:
- case 2: w0(0x90); w2(4); w2(6); w2(4);
- for(k=0;k<count/2;k++) {
- w0(buf[2*k+1]); w2(0); w2(4);
- w0(buf[2*k]); w2(5); w2(4);
- }
- break;
-
- case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
- for(k=0;k<count/2;k++) {
- w4(buf[2*k+1]);
- w4(buf[2*k]);
- }
- w2(4); w2(0); w2(4);
- break;
-
- case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
- for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
- w2(4); w2(0); w2(4);
- break;
-
- case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
- for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
- w2(4); w2(0); w2(4);
- break;
-
- }
-
-}
-
-static void kbic_log_adapter( PIA *pi, char * scratch,
- int verbose, char * chip )
-
-{ char *mode_string[6] = {"4-bit","5/3","8-bit",
- "EPP-8","EPP_16","EPP-32"};
-
- printk("%s: kbic %s, KingByte %s at 0x%x, ",
- pi->device,KBIC_VERSION,chip,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static void k951_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A");
-}
-
-static void k971_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A");
-}
-
-static struct pi_protocol k951 = {
- .owner = THIS_MODULE,
- .name = "k951",
- .max_mode = 6,
- .epp_first = 3,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = kbic_write_regr,
- .read_regr = kbic_read_regr,
- .write_block = kbic_write_block,
- .read_block = kbic_read_block,
- .connect = k951_connect,
- .disconnect = k951_disconnect,
- .log_adapter = k951_log_adapter,
-};
-
-static struct pi_protocol k971 = {
- .owner = THIS_MODULE,
- .name = "k971",
- .max_mode = 6,
- .epp_first = 3,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = kbic_write_regr,
- .read_regr = kbic_read_regr,
- .write_block = kbic_write_block,
- .read_block = kbic_read_block,
- .connect = k971_connect,
- .disconnect = k971_disconnect,
- .log_adapter = k971_log_adapter,
-};
-
-static int __init kbic_init(void)
-{
- int rv;
-
- rv = paride_register(&k951);
- if (rv < 0)
- return rv;
- rv = paride_register(&k971);
- if (rv < 0)
- paride_unregister(&k951);
- return rv;
-}
-
-static void __exit kbic_exit(void)
-{
- paride_unregister(&k951);
- paride_unregister(&k971);
-}
-
-MODULE_LICENSE("GPL");
-module_init(kbic_init)
-module_exit(kbic_exit)
diff --git a/drivers/block/paride/ktti.c b/drivers/block/paride/ktti.c
deleted file mode 100644
index 117ab0e8ccf0..000000000000
--- a/drivers/block/paride/ktti.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- ktti.c (c) 1998 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- ktti.c is a low-level protocol driver for the KT Technology
- parallel port adapter. This adapter is used in the "PHd"
- portable hard-drives. As far as I can tell, this device
- supports 4-bit mode _only_.
-
-*/
-
-#define KTTI_VERSION "1.0"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int cont_map[2] = { 0x10, 0x08 };
-
-static void ktti_write_regr( PIA *pi, int cont, int regr, int val)
-
-{ int r;
-
- r = regr + cont_map[cont];
-
- w0(r); w2(0xb); w2(0xa); w2(3); w2(6);
- w0(val); w2(3); w0(0); w2(6); w2(0xb);
-}
-
-static int ktti_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- r = regr + cont_map[cont];
-
- w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
- a = r1(); w2(0xc); b = r1(); w2(9); w2(0xc); w2(9);
- return j44(a,b);
-
-}
-
-static void ktti_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b;
-
- for (k=0;k<count/2;k++) {
- w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
- a = r1(); w2(0xc); b = r1(); w2(9);
- buf[2*k] = j44(a,b);
- a = r1(); w2(0xc); b = r1(); w2(9);
- buf[2*k+1] = j44(a,b);
- }
-}
-
-static void ktti_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- for (k=0;k<count/2;k++) {
- w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6);
- w0(buf[2*k]); w2(3);
- w0(buf[2*k+1]); w2(6);
- w2(0xb);
- }
-}
-
-static void ktti_connect ( PIA *pi )
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
- w2(0xb); w2(0xa); w0(0); w2(3); w2(6);
-}
-
-static void ktti_disconnect ( PIA *pi )
-
-{ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void ktti_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n",
- pi->device,KTTI_VERSION,pi->port,pi->delay);
-
-}
-
-static struct pi_protocol ktti = {
- .owner = THIS_MODULE,
- .name = "ktti",
- .max_mode = 1,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = ktti_write_regr,
- .read_regr = ktti_read_regr,
- .write_block = ktti_write_block,
- .read_block = ktti_read_block,
- .connect = ktti_connect,
- .disconnect = ktti_disconnect,
- .log_adapter = ktti_log_adapter,
-};
-
-static int __init ktti_init(void)
-{
- return paride_register(&ktti);
-}
-
-static void __exit ktti_exit(void)
-{
- paride_unregister(&ktti);
-}
-
-MODULE_LICENSE("GPL");
-module_init(ktti_init)
-module_exit(ktti_exit)
diff --git a/drivers/block/paride/mkd b/drivers/block/paride/mkd
deleted file mode 100644
index 6d0d802479ea..000000000000
--- a/drivers/block/paride/mkd
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# mkd -- a script to create the device special files for the PARIDE subsystem
-#
-# block devices: pd (45), pcd (46), pf (47)
-# character devices: pt (96), pg (97)
-#
-function mkdev {
- mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
-}
-#
-function pd {
- D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
- mkdev pd$D b 45 $[ $1 * 16 ]
- for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
- done
-}
-#
-cd /dev
-#
-for u in 0 1 2 3 ; do pd $u ; done
-for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done
-for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done
-for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done
-for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done
-for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done
-#
-# end of mkd
-
diff --git a/drivers/block/paride/on20.c b/drivers/block/paride/on20.c
deleted file mode 100644
index 0173697a1a4d..000000000000
--- a/drivers/block/paride/on20.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- on20.c (c) 1996-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- on20.c is a low-level protocol driver for the
- Onspec 90c20 parallel to IDE adapter.
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
-
-*/
-
-#define ON20_VERSION "1.01"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
-#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
-
-#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int on20_read_regr( PIA *pi, int cont, int regr )
-
-{ int h,l, r ;
-
- r = (regr<<2) + 1 + cont;
-
- op(1); vl(r); op(0);
-
- switch (pi->mode) {
-
- case 0: w2(4); w2(6); l = r1();
- w2(4); w2(6); h = r1();
- w2(4); w2(6); w2(4); w2(6); w2(4);
- return j44(l,h);
-
- case 1: w2(4); w2(0x26); r = r0();
- w2(4); w2(0x26); w2(4);
- return r;
-
- }
- return -1;
-}
-
-static void on20_write_regr( PIA *pi, int cont, int regr, int val )
-
-{ int r;
-
- r = (regr<<2) + 1 + cont;
-
- op(1); vl(r);
- op(0); vl(val);
- op(0); vl(val);
-}
-
-static void on20_connect ( PIA *pi)
-
-{ pi->saved_r0 = r0();
- pi->saved_r2 = r2();
-
- w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4);
- if (pi->mode) { op(2); vl(8); op(2); vl(9); }
- else { op(2); vl(0); op(2); vl(8); }
-}
-
-static void on20_disconnect ( PIA *pi )
-
-{ w2(4);w0(7);w2(4);w2(0xc);w2(4);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-static void on20_read_block( PIA *pi, char * buf, int count )
-
-{ int k, l, h;
-
- op(1); vl(1); op(0);
-
- for (k=0;k<count;k++)
- if (pi->mode) {
- w2(4); w2(0x26); buf[k] = r0();
- } else {
- w2(6); l = r1(); w2(4);
- w2(6); h = r1(); w2(4);
- buf[k] = j44(l,h);
- }
- w2(4);
-}
-
-static void on20_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- op(1); vl(1); op(0);
-
- for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); }
- w2(4);
-}
-
-static void on20_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[2] = {"4-bit","8-bit"};
-
- printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ",
- pi->device,ON20_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol on20 = {
- .owner = THIS_MODULE,
- .name = "on20",
- .max_mode = 2,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = on20_write_regr,
- .read_regr = on20_read_regr,
- .write_block = on20_write_block,
- .read_block = on20_read_block,
- .connect = on20_connect,
- .disconnect = on20_disconnect,
- .log_adapter = on20_log_adapter,
-};
-
-static int __init on20_init(void)
-{
- return paride_register(&on20);
-}
-
-static void __exit on20_exit(void)
-{
- paride_unregister(&on20);
-}
-
-MODULE_LICENSE("GPL");
-module_init(on20_init)
-module_exit(on20_exit)
diff --git a/drivers/block/paride/on26.c b/drivers/block/paride/on26.c
deleted file mode 100644
index 95ba256921f2..000000000000
--- a/drivers/block/paride/on26.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- on26.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- on26.c is a low-level protocol driver for the
- OnSpec 90c26 parallel to IDE adapter chip.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 init_proto, release_proto
- 1.02 GRG 1998.09.23 updates for the -E rev chip
- 1.03 GRG 1998.12.14 fix for slave drives
- 1.04 GRG 1998.12.20 yet another bug fix
-
-*/
-
-#define ON26_VERSION "1.04"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-
-#include "paride.h"
-
-/* mode codes: 0 nybble reads, 8-bit writes
- 1 8-bit reads and writes
- 2 8-bit EPP mode
- 3 EPP-16
- 4 EPP-32
-*/
-
-#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
-
-#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
-#define P2 w2(5);w2(7);w2(5);w2(4);
-
-/* cont = 0 - access the IDE register file
- cont = 1 - access the IDE command set
-*/
-
-static int on26_read_regr( PIA *pi, int cont, int regr )
-
-{ int a, b, r;
-
- r = (regr<<2) + 1 + cont;
-
- switch (pi->mode) {
-
- case 0: w0(1); P1; w0(r); P2; w0(0); P1;
- w2(6); a = r1(); w2(4);
- w2(6); b = r1(); w2(4);
- w2(6); w2(4); w2(6); w2(4);
- return j44(a,b);
-
- case 1: w0(1); P1; w0(r); P2; w0(0); P1;
- w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
- return a;
-
- case 2:
- case 3:
- case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
- w3(0); w3(0); w2(0x24); a = r4(); w2(4);
- w2(0x24); (void)r4(); w2(4);
- return a;
-
- }
- return -1;
-}
-
-static void on26_write_regr( PIA *pi, int cont, int regr, int val )
-
-{ int r;
-
- r = (regr<<2) + 1 + cont;
-
- switch (pi->mode) {
-
- case 0:
- case 1: w0(1); P1; w0(r); P2; w0(0); P1;
- w0(val); P2; w0(val); P2;
- break;
-
- case 2:
- case 3:
- case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
- w3(0); w3(0);
- w2(5); w4(val); w2(4);
- w2(5); w4(val); w2(4);
- break;
- }
-}
-
-#define CCP(x) w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
- w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
-
-static void on26_connect ( PIA *pi )
-
-{ int x;
-
- pi->saved_r0 = r0();
- pi->saved_r2 = r2();
-
- CCP(0x20);
- x = 8; if (pi->mode) x = 9;
-
- w0(2); P1; w0(8); P2;
- w0(2); P1; w0(x); P2;
-}
-
-static void on26_disconnect ( PIA *pi )
-
-{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
- else { w0(4); P1; w0(4); P1; }
- CCP(0x30);
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-}
-
-#define RESET_WAIT 200
-
-static int on26_test_port( PIA *pi) /* hard reset */
-
-{ int i, m, d, x=0, y=0;
-
- pi->saved_r0 = r0();
- pi->saved_r2 = r2();
-
- d = pi->delay;
- m = pi->mode;
- pi->delay = 5;
- pi->mode = 0;
-
- w2(0xc);
-
- CCP(0x30); CCP(0);
-
- w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);
- i = ((r1() & 0xf0) << 4); w0(0x87);
- i |= (r1() & 0xf0); w0(0x78);
- w0(0x20);w2(4);w2(5);
- i |= ((r1() & 0xf0) >> 4);
- w2(4);w0(0xff);
-
- if (i == 0xb5f) {
-
- w0(2); P1; w0(0); P2;
- w0(3); P1; w0(0); P2;
- w0(2); P1; w0(8); P2; udelay(100);
- w0(2); P1; w0(0xa); P2; udelay(100);
- w0(2); P1; w0(8); P2; udelay(1000);
-
- on26_write_regr(pi,0,6,0xa0);
-
- for (i=0;i<RESET_WAIT;i++) {
- on26_write_regr(pi,0,6,0xa0);
- x = on26_read_regr(pi,0,7);
- on26_write_regr(pi,0,6,0xb0);
- y = on26_read_regr(pi,0,7);
- if (!((x&0x80)||(y&0x80))) break;
- mdelay(100);
- }
-
- if (i == RESET_WAIT)
- printk("on26: Device reset failed (%x,%x)\n",x,y);
-
- w0(4); P1; w0(4); P1;
- }
-
- CCP(0x30);
-
- pi->delay = d;
- pi->mode = m;
- w0(pi->saved_r0);
- w2(pi->saved_r2);
-
- return 5;
-}
-
-
-static void on26_read_block( PIA *pi, char * buf, int count )
-
-{ int k, a, b;
-
- switch (pi->mode) {
-
- case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
- udelay(10);
- for (k=0;k<count;k++) {
- w2(6); a = r1();
- w2(4); b = r1();
- buf[k] = j44(a,b);
- }
- w0(2); P1; w0(8); P2;
- break;
-
- case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
- udelay(10);
- for (k=0;k<count/2;k++) {
- w2(0x26); buf[2*k] = r0();
- w2(0x24); buf[2*k+1] = r0();
- }
- w0(2); P1; w0(9); P2;
- break;
-
- case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
- w3(0); w3(0); w2(0x24);
- udelay(10);
- for (k=0;k<count;k++) buf[k] = r4();
- w2(4);
- break;
-
- case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
- w3(0); w3(0); w2(0x24);
- udelay(10);
- for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
- w2(4);
- break;
-
- case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
- w3(0); w3(0); w2(0x24);
- udelay(10);
- for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
- w2(4);
- break;
-
- }
-}
-
-static void on26_write_block( PIA *pi, char * buf, int count )
-
-{ int k;
-
- switch (pi->mode) {
-
- case 0:
- case 1: w0(1); P1; w0(1); P2;
- w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1;
- udelay(10);
- for (k=0;k<count/2;k++) {
- w2(5); w0(buf[2*k]);
- w2(7); w0(buf[2*k+1]);
- }
- w2(5); w2(4);
- w0(2); P1; w0(8+pi->mode); P2;
- break;
-
- case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
- w3(0); w3(0); w2(0xc5);
- udelay(10);
- for (k=0;k<count;k++) w4(buf[k]);
- w2(0xc4);
- break;
-
- case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
- w3(0); w3(0); w2(0xc5);
- udelay(10);
- for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
- w2(0xc4);
- break;
-
- case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
- w3(0); w3(0); w2(0xc5);
- udelay(10);
- for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
- w2(0xc4);
- break;
-
- }
-
-}
-
-static void on26_log_adapter( PIA *pi, char * scratch, int verbose )
-
-{ char *mode_string[5] = {"4-bit","8-bit","EPP-8",
- "EPP-16","EPP-32"};
-
- printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ",
- pi->device,ON26_VERSION,pi->port);
- printk("mode %d (%s), delay %d\n",pi->mode,
- mode_string[pi->mode],pi->delay);
-
-}
-
-static struct pi_protocol on26 = {
- .owner = THIS_MODULE,
- .name = "on26",
- .max_mode = 5,
- .epp_first = 2,
- .default_delay = 1,
- .max_units = 1,
- .write_regr = on26_write_regr,
- .read_regr = on26_read_regr,
- .write_block = on26_write_block,
- .read_block = on26_read_block,
- .connect = on26_connect,
- .disconnect = on26_disconnect,
- .test_port = on26_test_port,
- .log_adapter = on26_log_adapter,
-};
-
-static int __init on26_init(void)
-{
- return paride_register(&on26);
-}
-
-static void __exit on26_exit(void)
-{
- paride_unregister(&on26);
-}
-
-MODULE_LICENSE("GPL");
-module_init(on26_init)
-module_exit(on26_exit)
diff --git a/drivers/block/paride/paride.c b/drivers/block/paride/paride.c
deleted file mode 100644
index 0e287993b778..000000000000
--- a/drivers/block/paride/paride.c
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- paride.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is the base module for the family of device drivers
- that support parallel port IDE devices.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.03 Use spinlocks
- 1.02 GRG 1998.05.05 init_proto, release_proto, ktti
- 1.03 GRG 1998.08.15 eliminate compiler warning
- 1.04 GRG 1998.11.28 added support for FRIQ
- 1.05 TMW 2000.06.06 use parport_find_number instead of
- parport_enumerate
- 1.06 TMW 2001.03.26 more sane parport-or-not resource management
-*/
-
-#define PI_VERSION "1.06"
-
-#include <linux/module.h>
-#include <linux/kmod.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <linux/sched.h> /* TASK_* */
-#include <linux/parport.h>
-#include <linux/slab.h>
-
-#include "paride.h"
-
-MODULE_LICENSE("GPL");
-
-#define MAX_PROTOS 32
-
-static struct pi_protocol *protocols[MAX_PROTOS];
-
-static DEFINE_SPINLOCK(pi_spinlock);
-
-void pi_write_regr(PIA * pi, int cont, int regr, int val)
-{
- pi->proto->write_regr(pi, cont, regr, val);
-}
-
-EXPORT_SYMBOL(pi_write_regr);
-
-int pi_read_regr(PIA * pi, int cont, int regr)
-{
- return pi->proto->read_regr(pi, cont, regr);
-}
-
-EXPORT_SYMBOL(pi_read_regr);
-
-void pi_write_block(PIA * pi, char *buf, int count)
-{
- pi->proto->write_block(pi, buf, count);
-}
-
-EXPORT_SYMBOL(pi_write_block);
-
-void pi_read_block(PIA * pi, char *buf, int count)
-{
- pi->proto->read_block(pi, buf, count);
-}
-
-EXPORT_SYMBOL(pi_read_block);
-
-static void pi_wake_up(void *p)
-{
- PIA *pi = (PIA *) p;
- unsigned long flags;
- void (*cont) (void) = NULL;
-
- spin_lock_irqsave(&pi_spinlock, flags);
-
- if (pi->claim_cont && !parport_claim(pi->pardev)) {
- cont = pi->claim_cont;
- pi->claim_cont = NULL;
- pi->claimed = 1;
- }
-
- spin_unlock_irqrestore(&pi_spinlock, flags);
-
- wake_up(&(pi->parq));
-
- if (cont)
- cont();
-}
-
-int pi_schedule_claimed(PIA * pi, void (*cont) (void))
-{
- unsigned long flags;
-
- spin_lock_irqsave(&pi_spinlock, flags);
- if (pi->pardev && parport_claim(pi->pardev)) {
- pi->claim_cont = cont;
- spin_unlock_irqrestore(&pi_spinlock, flags);
- return 0;
- }
- pi->claimed = 1;
- spin_unlock_irqrestore(&pi_spinlock, flags);
- return 1;
-}
-EXPORT_SYMBOL(pi_schedule_claimed);
-
-void pi_do_claimed(PIA * pi, void (*cont) (void))
-{
- if (pi_schedule_claimed(pi, cont))
- cont();
-}
-
-EXPORT_SYMBOL(pi_do_claimed);
-
-static void pi_claim(PIA * pi)
-{
- if (pi->claimed)
- return;
- pi->claimed = 1;
- if (pi->pardev)
- wait_event(pi->parq,
- !parport_claim((struct pardevice *) pi->pardev));
-}
-
-static void pi_unclaim(PIA * pi)
-{
- pi->claimed = 0;
- if (pi->pardev)
- parport_release((struct pardevice *) (pi->pardev));
-}
-
-void pi_connect(PIA * pi)
-{
- pi_claim(pi);
- pi->proto->connect(pi);
-}
-
-EXPORT_SYMBOL(pi_connect);
-
-void pi_disconnect(PIA * pi)
-{
- pi->proto->disconnect(pi);
- pi_unclaim(pi);
-}
-
-EXPORT_SYMBOL(pi_disconnect);
-
-static void pi_unregister_parport(PIA * pi)
-{
- if (pi->pardev) {
- parport_unregister_device((struct pardevice *) (pi->pardev));
- pi->pardev = NULL;
- }
-}
-
-void pi_release(PIA * pi)
-{
- pi_unregister_parport(pi);
- if (pi->proto->release_proto)
- pi->proto->release_proto(pi);
- module_put(pi->proto->owner);
-}
-
-EXPORT_SYMBOL(pi_release);
-
-static int default_test_proto(PIA * pi, char *scratch, int verbose)
-{
- int j, k;
- int e[2] = { 0, 0 };
-
- pi->proto->connect(pi);
-
- for (j = 0; j < 2; j++) {
- pi_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
- for (k = 0; k < 256; k++) {
- pi_write_regr(pi, 0, 2, k ^ 0xaa);
- pi_write_regr(pi, 0, 3, k ^ 0x55);
- if (pi_read_regr(pi, 0, 2) != (k ^ 0xaa))
- e[j]++;
- }
- }
- pi->proto->disconnect(pi);
-
- if (verbose)
- printk("%s: %s: port 0x%x, mode %d, test=(%d,%d)\n",
- pi->device, pi->proto->name, pi->port,
- pi->mode, e[0], e[1]);
-
- return (e[0] && e[1]); /* not here if both > 0 */
-}
-
-static int pi_test_proto(PIA * pi, char *scratch, int verbose)
-{
- int res;
-
- pi_claim(pi);
- if (pi->proto->test_proto)
- res = pi->proto->test_proto(pi, scratch, verbose);
- else
- res = default_test_proto(pi, scratch, verbose);
- pi_unclaim(pi);
-
- return res;
-}
-
-int paride_register(PIP * pr)
-{
- int k;
-
- for (k = 0; k < MAX_PROTOS; k++)
- if (protocols[k] && !strcmp(pr->name, protocols[k]->name)) {
- printk("paride: %s protocol already registered\n",
- pr->name);
- return -1;
- }
- k = 0;
- while ((k < MAX_PROTOS) && (protocols[k]))
- k++;
- if (k == MAX_PROTOS) {
- printk("paride: protocol table full\n");
- return -1;
- }
- protocols[k] = pr;
- pr->index = k;
- printk("paride: %s registered as protocol %d\n", pr->name, k);
- return 0;
-}
-
-EXPORT_SYMBOL(paride_register);
-
-void paride_unregister(PIP * pr)
-{
- if (!pr)
- return;
- if (protocols[pr->index] != pr) {
- printk("paride: %s not registered\n", pr->name);
- return;
- }
- protocols[pr->index] = NULL;
-}
-
-EXPORT_SYMBOL(paride_unregister);
-
-static int pi_register_parport(PIA *pi, int verbose, int unit)
-{
- struct parport *port;
- struct pardev_cb par_cb;
-
- port = parport_find_base(pi->port);
- if (!port)
- return 0;
- memset(&par_cb, 0, sizeof(par_cb));
- par_cb.wakeup = pi_wake_up;
- par_cb.private = (void *)pi;
- pi->pardev = parport_register_dev_model(port, pi->device, &par_cb,
- unit);
- parport_put_port(port);
- if (!pi->pardev)
- return 0;
-
- init_waitqueue_head(&pi->parq);
-
- if (verbose)
- printk("%s: 0x%x is %s\n", pi->device, pi->port, port->name);
-
- pi->parname = (char *) port->name;
-
- return 1;
-}
-
-static int pi_probe_mode(PIA * pi, int max, char *scratch, int verbose)
-{
- int best, range;
-
- if (pi->mode != -1) {
- if (pi->mode >= max)
- return 0;
- range = 3;
- if (pi->mode >= pi->proto->epp_first)
- range = 8;
- if ((range == 8) && (pi->port % 8))
- return 0;
- pi->reserved = range;
- return (!pi_test_proto(pi, scratch, verbose));
- }
- best = -1;
- for (pi->mode = 0; pi->mode < max; pi->mode++) {
- range = 3;
- if (pi->mode >= pi->proto->epp_first)
- range = 8;
- if ((range == 8) && (pi->port % 8))
- break;
- pi->reserved = range;
- if (!pi_test_proto(pi, scratch, verbose))
- best = pi->mode;
- }
- pi->mode = best;
- return (best > -1);
-}
-
-static int pi_probe_unit(PIA * pi, int unit, char *scratch, int verbose)
-{
- int max, s, e;
-
- s = unit;
- e = s + 1;
-
- if (s == -1) {
- s = 0;
- e = pi->proto->max_units;
- }
-
- if (!pi_register_parport(pi, verbose, s))
- return 0;
-
- if (pi->proto->test_port) {
- pi_claim(pi);
- max = pi->proto->test_port(pi);
- pi_unclaim(pi);
- } else
- max = pi->proto->max_mode;
-
- if (pi->proto->probe_unit) {
- pi_claim(pi);
- for (pi->unit = s; pi->unit < e; pi->unit++)
- if (pi->proto->probe_unit(pi)) {
- pi_unclaim(pi);
- if (pi_probe_mode(pi, max, scratch, verbose))
- return 1;
- pi_unregister_parport(pi);
- return 0;
- }
- pi_unclaim(pi);
- pi_unregister_parport(pi);
- return 0;
- }
-
- if (!pi_probe_mode(pi, max, scratch, verbose)) {
- pi_unregister_parport(pi);
- return 0;
- }
- return 1;
-
-}
-
-int pi_init(PIA * pi, int autoprobe, int port, int mode,
- int unit, int protocol, int delay, char *scratch,
- int devtype, int verbose, char *device)
-{
- int p, k, s, e;
- int lpts[7] = { 0x3bc, 0x378, 0x278, 0x268, 0x27c, 0x26c, 0 };
-
- s = protocol;
- e = s + 1;
-
- if (!protocols[0])
- request_module("paride_protocol");
-
- if (autoprobe) {
- s = 0;
- e = MAX_PROTOS;
- } else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
- (!protocols[s]) || (unit < 0) ||
- (unit >= protocols[s]->max_units)) {
- printk("%s: Invalid parameters\n", device);
- return 0;
- }
-
- for (p = s; p < e; p++) {
- struct pi_protocol *proto = protocols[p];
- if (!proto)
- continue;
- /* still racy */
- if (!try_module_get(proto->owner))
- continue;
- pi->proto = proto;
- pi->private = 0;
- if (proto->init_proto && proto->init_proto(pi) < 0) {
- pi->proto = NULL;
- module_put(proto->owner);
- continue;
- }
- if (delay == -1)
- pi->delay = pi->proto->default_delay;
- else
- pi->delay = delay;
- pi->devtype = devtype;
- pi->device = device;
-
- pi->parname = NULL;
- pi->pardev = NULL;
- init_waitqueue_head(&pi->parq);
- pi->claimed = 0;
- pi->claim_cont = NULL;
-
- pi->mode = mode;
- if (port != -1) {
- pi->port = port;
- if (pi_probe_unit(pi, unit, scratch, verbose))
- break;
- pi->port = 0;
- } else {
- k = 0;
- while ((pi->port = lpts[k++]))
- if (pi_probe_unit
- (pi, unit, scratch, verbose))
- break;
- if (pi->port)
- break;
- }
- if (pi->proto->release_proto)
- pi->proto->release_proto(pi);
- module_put(proto->owner);
- }
-
- if (!pi->port) {
- if (autoprobe)
- printk("%s: Autoprobe failed\n", device);
- else
- printk("%s: Adapter not found\n", device);
- return 0;
- }
-
- if (pi->parname)
- printk("%s: Sharing %s at 0x%x\n", pi->device,
- pi->parname, pi->port);
-
- pi->proto->log_adapter(pi, scratch, verbose);
-
- return 1;
-}
-
-EXPORT_SYMBOL(pi_init);
-
-static int pi_probe(struct pardevice *par_dev)
-{
- struct device_driver *drv = par_dev->dev.driver;
- int len = strlen(drv->name);
-
- if (strncmp(par_dev->name, drv->name, len))
- return -ENODEV;
-
- return 0;
-}
-
-void *pi_register_driver(char *name)
-{
- struct parport_driver *parp_drv;
- int ret;
-
- parp_drv = kzalloc(sizeof(*parp_drv), GFP_KERNEL);
- if (!parp_drv)
- return NULL;
-
- parp_drv->name = name;
- parp_drv->probe = pi_probe;
- parp_drv->devmodel = true;
-
- ret = parport_register_driver(parp_drv);
- if (ret) {
- kfree(parp_drv);
- return NULL;
- }
- return (void *)parp_drv;
-}
-EXPORT_SYMBOL(pi_register_driver);
-
-void pi_unregister_driver(void *_drv)
-{
- struct parport_driver *drv = _drv;
-
- parport_unregister_driver(drv);
- kfree(drv);
-}
-EXPORT_SYMBOL(pi_unregister_driver);
diff --git a/drivers/block/paride/paride.h b/drivers/block/paride/paride.h
deleted file mode 100644
index ddb9e589da7f..000000000000
--- a/drivers/block/paride/paride.h
+++ /dev/null
@@ -1,172 +0,0 @@
-#ifndef __DRIVERS_PARIDE_H__
-#define __DRIVERS_PARIDE_H__
-
-/*
- paride.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GPL.
-
- This file defines the interface between the high-level parallel
- IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.05 init_proto, release_proto
-*/
-
-#define PARIDE_H_VERSION "1.01"
-
-/* Some adapters need to know what kind of device they are in
-
- Values for devtype:
-*/
-
-#define PI_PD 0 /* IDE disk */
-#define PI_PCD 1 /* ATAPI CDrom */
-#define PI_PF 2 /* ATAPI disk */
-#define PI_PT 3 /* ATAPI tape */
-#define PI_PG 4 /* ATAPI generic */
-
-/* The paride module contains no state, instead the drivers allocate
- a pi_adapter data structure and pass it to paride in every operation.
-
-*/
-
-struct pi_adapter {
-
- struct pi_protocol *proto; /* adapter protocol */
- int port; /* base address of parallel port */
- int mode; /* transfer mode in use */
- int delay; /* adapter delay setting */
- int devtype; /* device type: PI_PD etc. */
- char *device; /* name of driver */
- int unit; /* unit number for chained adapters */
- int saved_r0; /* saved port state */
- int saved_r2; /* saved port state */
- int reserved; /* number of ports reserved */
- unsigned long private; /* for protocol module */
-
- wait_queue_head_t parq; /* semaphore for parport sharing */
- void *pardev; /* pointer to pardevice */
- char *parname; /* parport name */
- int claimed; /* parport has already been claimed */
- void (*claim_cont)(void); /* continuation for parport wait */
-};
-
-typedef struct pi_adapter PIA;
-
-/* functions exported by paride to the high level drivers */
-
-extern int pi_init(PIA *pi,
- int autoprobe, /* 1 to autoprobe */
- int port, /* base port address */
- int mode, /* -1 for autoprobe */
- int unit, /* unit number, if supported */
- int protocol, /* protocol to use */
- int delay, /* -1 to use adapter specific default */
- char * scratch, /* address of 512 byte buffer */
- int devtype, /* device type: PI_PD, PI_PCD, etc ... */
- int verbose, /* log verbose data while probing */
- char *device /* name of the driver */
- ); /* returns 0 on failure, 1 on success */
-
-extern void pi_release(PIA *pi);
-
-/* registers are addressed as (cont,regr)
-
- cont: 0 for command register file, 1 for control register(s)
- regr: 0-7 for register number.
-
-*/
-
-extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
-
-extern int pi_read_regr(PIA *pi, int cont, int regr);
-
-extern void pi_write_block(PIA *pi, char * buf, int count);
-
-extern void pi_read_block(PIA *pi, char * buf, int count);
-
-extern void pi_connect(PIA *pi);
-
-extern void pi_disconnect(PIA *pi);
-
-extern void pi_do_claimed(PIA *pi, void (*cont)(void));
-extern int pi_schedule_claimed(PIA *pi, void (*cont)(void));
-
-/* macros and functions exported to the protocol modules */
-
-#define delay_p (pi->delay?udelay(pi->delay):(void)0)
-#define out_p(offs,byte) outb(byte,pi->port+offs); delay_p;
-#define in_p(offs) (delay_p,inb(pi->port+offs))
-
-#define w0(byte) {out_p(0,byte);}
-#define r0() (in_p(0) & 0xff)
-#define w1(byte) {out_p(1,byte);}
-#define r1() (in_p(1) & 0xff)
-#define w2(byte) {out_p(2,byte);}
-#define r2() (in_p(2) & 0xff)
-#define w3(byte) {out_p(3,byte);}
-#define w4(byte) {out_p(4,byte);}
-#define r4() (in_p(4) & 0xff)
-#define w4w(data) {outw(data,pi->port+4); delay_p;}
-#define w4l(data) {outl(data,pi->port+4); delay_p;}
-#define r4w() (delay_p,inw(pi->port+4)&0xffff)
-#define r4l() (delay_p,inl(pi->port+4)&0xffffffff)
-
-static inline u16 pi_swab16( char *b, int k)
-
-{ union { u16 u; char t[2]; } r;
-
- r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
- return r.u;
-}
-
-static inline u32 pi_swab32( char *b, int k)
-
-{ union { u32 u; char f[4]; } r;
-
- r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
- r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
- return r.u;
-}
-
-struct pi_protocol {
-
- char name[8]; /* name for this protocol */
- int index; /* index into protocol table */
-
- int max_mode; /* max mode number */
- int epp_first; /* modes >= this use 8 ports */
-
- int default_delay; /* delay parameter if not specified */
- int max_units; /* max chained units probed for */
-
- void (*write_regr)(PIA *,int,int,int);
- int (*read_regr)(PIA *,int,int);
- void (*write_block)(PIA *,char *,int);
- void (*read_block)(PIA *,char *,int);
-
- void (*connect)(PIA *);
- void (*disconnect)(PIA *);
-
- int (*test_port)(PIA *);
- int (*probe_unit)(PIA *);
- int (*test_proto)(PIA *,char *,int);
- void (*log_adapter)(PIA *,char *,int);
-
- int (*init_proto)(PIA *);
- void (*release_proto)(PIA *);
- struct module *owner;
-};
-
-typedef struct pi_protocol PIP;
-
-extern int paride_register( PIP * );
-extern void paride_unregister ( PIP * );
-void *pi_register_driver(char *);
-void pi_unregister_driver(void *);
-
-#endif /* __DRIVERS_PARIDE_H__ */
-/* end of paride.h */
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
deleted file mode 100644
index a5ab40784119..000000000000
--- a/drivers/block/paride/pcd.c
+++ /dev/null
@@ -1,1042 +0,0 @@
-/*
- pcd.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is a high-level driver for parallel port ATAPI CD-ROM
- drives based on chips supported by the paride module.
-
- By default, the driver will autoprobe for a single parallel
- port ATAPI CD-ROM drive, but if their individual parameters are
- specified, the driver can handle up to 4 drives.
-
- The behaviour of the pcd driver can be altered by setting
- some parameters from the insmod command line. The following
- parameters are adjustable:
-
- drive0 These four arguments can be arrays of
- drive1 1-6 integers as follows:
- drive2
- drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
-
- Where,
-
- <prt> is the base of the parallel port address for
- the corresponding drive. (required)
-
- <pro> is the protocol number for the adapter that
- supports this drive. These numbers are
- logged by 'paride' when the protocol modules
- are initialised. (0 if not given)
-
- <uni> for those adapters that support chained
- devices, this is the unit selector for the
- chain of devices on the given port. It should
- be zero for devices that don't support chaining.
- (0 if not given)
-
- <mod> this can be -1 to choose the best mode, or one
- of the mode numbers supported by the adapter.
- (-1 if not given)
-
- <slv> ATAPI CD-ROMs can be jumpered to master or slave.
- Set this to 0 to choose the master drive, 1 to
- choose the slave, -1 (the default) to choose the
- first drive found.
-
- <dly> some parallel ports require the driver to
- go more slowly. -1 sets a default value that
- should work with the chosen protocol. Otherwise,
- set this to a small integer, the larger it is
- the slower the port i/o. In some cases, setting
- this to zero will speed up the device. (default -1)
-
- major You may use this parameter to override the
- default major number (46) that this driver
- will use. Be sure to change the device
- name as well.
-
- name This parameter is a character string that
- contains the name the kernel will use for this
- device (in /proc output, for instance).
- (default "pcd")
-
- verbose This parameter controls the amount of logging
- that the driver will do. Set it to 0 for
- normal operation, 1 to see autoprobe progress
- messages, or 2 to see additional debugging
- output. (default 0)
-
- nice This parameter controls the driver's use of
- idle CPU time, at the expense of some speed.
-
- If this driver is built into the kernel, you can use the
- following kernel command line parameters, with the same values
- as the corresponding module parameters listed above:
-
- pcd.drive0
- pcd.drive1
- pcd.drive2
- pcd.drive3
- pcd.nice
-
- In addition, you can use the parameter pcd.disable to disable
- the driver entirely.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.01.24 Added test unit ready support
- 1.02 GRG 1998.05.06 Changes to pcd_completion, ready_wait,
- and loosen interpretation of ATAPI
- standard for clearing error status.
- Use spinlocks. Eliminate sti().
- 1.03 GRG 1998.06.16 Eliminated an Ugh
- 1.04 GRG 1998.08.15 Added extra debugging, improvements to
- pcd_completion, use HZ in loop timing
- 1.05 GRG 1998.08.16 Conformed to "Uniform CD-ROM" standard
- 1.06 GRG 1998.08.19 Added audio ioctl support
- 1.07 GRG 1998.09.24 Increased reset timeout, added jumbo support
-
-*/
-
-#define PCD_VERSION "1.07"
-#define PCD_MAJOR 46
-#define PCD_NAME "pcd"
-#define PCD_UNITS 4
-
-/* Here are things one can override from the insmod command.
- Most are autoprobed by paride unless set here. Verbose is off
- by default.
-
-*/
-
-static int verbose = 0;
-static int major = PCD_MAJOR;
-static char *name = PCD_NAME;
-static int nice = 0;
-static int disable = 0;
-
-static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
-
-static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
-static int pcd_drive_count;
-
-enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
-
-/* end of parameters */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/cdrom.h>
-#include <linux/spinlock.h>
-#include <linux/blk-mq.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-
-static DEFINE_MUTEX(pcd_mutex);
-static DEFINE_SPINLOCK(pcd_lock);
-
-module_param(verbose, int, 0644);
-module_param(major, int, 0);
-module_param(name, charp, 0);
-module_param(nice, int, 0);
-module_param_array(drive0, int, NULL, 0);
-module_param_array(drive1, int, NULL, 0);
-module_param_array(drive2, int, NULL, 0);
-module_param_array(drive3, int, NULL, 0);
-
-#include "paride.h"
-#include "pseudo.h"
-
-#define PCD_RETRIES 5
-#define PCD_TMO 800 /* timeout in jiffies */
-#define PCD_DELAY 50 /* spin delay in uS */
-#define PCD_READY_TMO 20 /* in seconds */
-#define PCD_RESET_TMO 100 /* in tenths of a second */
-
-#define PCD_SPIN (1000000*PCD_TMO)/(HZ*PCD_DELAY)
-
-#define IDE_ERR 0x01
-#define IDE_DRQ 0x08
-#define IDE_READY 0x40
-#define IDE_BUSY 0x80
-
-static int pcd_open(struct cdrom_device_info *cdi, int purpose);
-static void pcd_release(struct cdrom_device_info *cdi);
-static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr);
-static unsigned int pcd_check_events(struct cdrom_device_info *cdi,
- unsigned int clearing, int slot_nr);
-static int pcd_tray_move(struct cdrom_device_info *cdi, int position);
-static int pcd_lock_door(struct cdrom_device_info *cdi, int lock);
-static int pcd_drive_reset(struct cdrom_device_info *cdi);
-static int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn);
-static int pcd_audio_ioctl(struct cdrom_device_info *cdi,
- unsigned int cmd, void *arg);
-static int pcd_packet(struct cdrom_device_info *cdi,
- struct packet_command *cgc);
-
-static void do_pcd_read_drq(void);
-static blk_status_t pcd_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd);
-static void do_pcd_read(void);
-
-struct pcd_unit {
- struct pi_adapter pia; /* interface to paride layer */
- struct pi_adapter *pi;
- int drive; /* master/slave */
- int last_sense; /* result of last request sense */
- int changed; /* media change seen */
- int present; /* does this unit exist ? */
- char *name; /* pcd0, pcd1, etc */
- struct cdrom_device_info info; /* uniform cdrom interface */
- struct gendisk *disk;
- struct blk_mq_tag_set tag_set;
- struct list_head rq_list;
-};
-
-static struct pcd_unit pcd[PCD_UNITS];
-
-static char pcd_scratch[64];
-static char pcd_buffer[2048]; /* raw block buffer */
-static int pcd_bufblk = -1; /* block in buffer, in CD units,
- -1 for nothing there. See also
- pd_unit.
- */
-
-/* the variables below are used mainly in the I/O request engine, which
- processes only one request at a time.
-*/
-
-static struct pcd_unit *pcd_current; /* current request's drive */
-static struct request *pcd_req;
-static int pcd_retries; /* retries on current request */
-static int pcd_busy; /* request being processed ? */
-static int pcd_sector; /* address of next requested sector */
-static int pcd_count; /* number of blocks still to do */
-static char *pcd_buf; /* buffer for request in progress */
-static void *par_drv; /* reference of parport driver */
-
-/* kernel glue structures */
-
-static int pcd_block_open(struct block_device *bdev, fmode_t mode)
-{
- struct pcd_unit *cd = bdev->bd_disk->private_data;
- int ret;
-
- bdev_check_media_change(bdev);
-
- mutex_lock(&pcd_mutex);
- ret = cdrom_open(&cd->info, bdev, mode);
- mutex_unlock(&pcd_mutex);
-
- return ret;
-}
-
-static void pcd_block_release(struct gendisk *disk, fmode_t mode)
-{
- struct pcd_unit *cd = disk->private_data;
- mutex_lock(&pcd_mutex);
- cdrom_release(&cd->info, mode);
- mutex_unlock(&pcd_mutex);
-}
-
-static int pcd_block_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned cmd, unsigned long arg)
-{
- struct pcd_unit *cd = bdev->bd_disk->private_data;
- int ret;
-
- mutex_lock(&pcd_mutex);
- ret = cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
- mutex_unlock(&pcd_mutex);
-
- return ret;
-}
-
-static unsigned int pcd_block_check_events(struct gendisk *disk,
- unsigned int clearing)
-{
- struct pcd_unit *cd = disk->private_data;
- return cdrom_check_events(&cd->info, clearing);
-}
-
-static const struct block_device_operations pcd_bdops = {
- .owner = THIS_MODULE,
- .open = pcd_block_open,
- .release = pcd_block_release,
- .ioctl = pcd_block_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = blkdev_compat_ptr_ioctl,
-#endif
- .check_events = pcd_block_check_events,
-};
-
-static const struct cdrom_device_ops pcd_dops = {
- .open = pcd_open,
- .release = pcd_release,
- .drive_status = pcd_drive_status,
- .check_events = pcd_check_events,
- .tray_move = pcd_tray_move,
- .lock_door = pcd_lock_door,
- .get_mcn = pcd_get_mcn,
- .reset = pcd_drive_reset,
- .audio_ioctl = pcd_audio_ioctl,
- .generic_packet = pcd_packet,
- .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
- CDC_MCN | CDC_MEDIA_CHANGED | CDC_RESET |
- CDC_PLAY_AUDIO | CDC_GENERIC_PACKET | CDC_CD_R |
- CDC_CD_RW,
-};
-
-static const struct blk_mq_ops pcd_mq_ops = {
- .queue_rq = pcd_queue_rq,
-};
-
-static int pcd_open(struct cdrom_device_info *cdi, int purpose)
-{
- struct pcd_unit *cd = cdi->handle;
- if (!cd->present)
- return -ENODEV;
- return 0;
-}
-
-static void pcd_release(struct cdrom_device_info *cdi)
-{
-}
-
-static inline int status_reg(struct pcd_unit *cd)
-{
- return pi_read_regr(cd->pi, 1, 6);
-}
-
-static inline int read_reg(struct pcd_unit *cd, int reg)
-{
- return pi_read_regr(cd->pi, 0, reg);
-}
-
-static inline void write_reg(struct pcd_unit *cd, int reg, int val)
-{
- pi_write_regr(cd->pi, 0, reg, val);
-}
-
-static int pcd_wait(struct pcd_unit *cd, int go, int stop, char *fun, char *msg)
-{
- int j, r, e, s, p;
-
- j = 0;
- while ((((r = status_reg(cd)) & go) || (stop && (!(r & stop))))
- && (j++ < PCD_SPIN))
- udelay(PCD_DELAY);
-
- if ((r & (IDE_ERR & stop)) || (j > PCD_SPIN)) {
- s = read_reg(cd, 7);
- e = read_reg(cd, 1);
- p = read_reg(cd, 2);
- if (j > PCD_SPIN)
- e |= 0x100;
- if (fun)
- printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
- " loop=%d phase=%d\n",
- cd->name, fun, msg, r, s, e, j, p);
- return (s << 8) + r;
- }
- return 0;
-}
-
-static int pcd_command(struct pcd_unit *cd, char *cmd, int dlen, char *fun)
-{
- pi_connect(cd->pi);
-
- write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
-
- if (pcd_wait(cd, IDE_BUSY | IDE_DRQ, 0, fun, "before command")) {
- pi_disconnect(cd->pi);
- return -1;
- }
-
- write_reg(cd, 4, dlen % 256);
- write_reg(cd, 5, dlen / 256);
- write_reg(cd, 7, 0xa0); /* ATAPI packet command */
-
- if (pcd_wait(cd, IDE_BUSY, IDE_DRQ, fun, "command DRQ")) {
- pi_disconnect(cd->pi);
- return -1;
- }
-
- if (read_reg(cd, 2) != 1) {
- printk("%s: %s: command phase error\n", cd->name, fun);
- pi_disconnect(cd->pi);
- return -1;
- }
-
- pi_write_block(cd->pi, cmd, 12);
-
- return 0;
-}
-
-static int pcd_completion(struct pcd_unit *cd, char *buf, char *fun)
-{
- int r, d, p, n, k, j;
-
- r = -1;
- k = 0;
- j = 0;
-
- if (!pcd_wait(cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR,
- fun, "completion")) {
- r = 0;
- while (read_reg(cd, 7) & IDE_DRQ) {
- d = read_reg(cd, 4) + 256 * read_reg(cd, 5);
- n = (d + 3) & 0xfffc;
- p = read_reg(cd, 2) & 3;
-
- if ((p == 2) && (n > 0) && (j == 0)) {
- pi_read_block(cd->pi, buf, n);
- if (verbose > 1)
- printk("%s: %s: Read %d bytes\n",
- cd->name, fun, n);
- r = 0;
- j++;
- } else {
- if (verbose > 1)
- printk
- ("%s: %s: Unexpected phase %d, d=%d, k=%d\n",
- cd->name, fun, p, d, k);
- if (verbose < 2)
- printk_once(
- "%s: WARNING: ATAPI phase errors\n",
- cd->name);
- mdelay(1);
- }
- if (k++ > PCD_TMO) {
- printk("%s: Stuck DRQ\n", cd->name);
- break;
- }
- if (pcd_wait
- (cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR, fun,
- "completion")) {
- r = -1;
- break;
- }
- }
- }
-
- pi_disconnect(cd->pi);
-
- return r;
-}
-
-static void pcd_req_sense(struct pcd_unit *cd, char *fun)
-{
- char rs_cmd[12] = { 0x03, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
- char buf[16];
- int r, c;
-
- r = pcd_command(cd, rs_cmd, 16, "Request sense");
- mdelay(1);
- if (!r)
- pcd_completion(cd, buf, "Request sense");
-
- cd->last_sense = -1;
- c = 2;
- if (!r) {
- if (fun)
- printk("%s: %s: Sense key: %x, ASC: %x, ASQ: %x\n",
- cd->name, fun, buf[2] & 0xf, buf[12], buf[13]);
- c = buf[2] & 0xf;
- cd->last_sense =
- c | ((buf[12] & 0xff) << 8) | ((buf[13] & 0xff) << 16);
- }
- if ((c == 2) || (c == 6))
- cd->changed = 1;
-}
-
-static int pcd_atapi(struct pcd_unit *cd, char *cmd, int dlen, char *buf, char *fun)
-{
- int r;
-
- r = pcd_command(cd, cmd, dlen, fun);
- mdelay(1);
- if (!r)
- r = pcd_completion(cd, buf, fun);
- if (r)
- pcd_req_sense(cd, fun);
-
- return r;
-}
-
-static int pcd_packet(struct cdrom_device_info *cdi, struct packet_command *cgc)
-{
- return pcd_atapi(cdi->handle, cgc->cmd, cgc->buflen, cgc->buffer,
- "generic packet");
-}
-
-#define DBMSG(msg) ((verbose>1)?(msg):NULL)
-
-static unsigned int pcd_check_events(struct cdrom_device_info *cdi,
- unsigned int clearing, int slot_nr)
-{
- struct pcd_unit *cd = cdi->handle;
- int res = cd->changed;
- if (res)
- cd->changed = 0;
- return res ? DISK_EVENT_MEDIA_CHANGE : 0;
-}
-
-static int pcd_lock_door(struct cdrom_device_info *cdi, int lock)
-{
- char un_cmd[12] = { 0x1e, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0 };
-
- return pcd_atapi(cdi->handle, un_cmd, 0, pcd_scratch,
- lock ? "lock door" : "unlock door");
-}
-
-static int pcd_tray_move(struct cdrom_device_info *cdi, int position)
-{
- char ej_cmd[12] = { 0x1b, 0, 0, 0, 3 - position, 0, 0, 0, 0, 0, 0, 0 };
-
- return pcd_atapi(cdi->handle, ej_cmd, 0, pcd_scratch,
- position ? "eject" : "close tray");
-}
-
-static void pcd_sleep(int cs)
-{
- schedule_timeout_interruptible(cs);
-}
-
-static int pcd_reset(struct pcd_unit *cd)
-{
- int i, k, flg;
- int expect[5] = { 1, 1, 1, 0x14, 0xeb };
-
- pi_connect(cd->pi);
- write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
- write_reg(cd, 7, 8);
-
- pcd_sleep(20 * HZ / 1000); /* delay a bit */
-
- k = 0;
- while ((k++ < PCD_RESET_TMO) && (status_reg(cd) & IDE_BUSY))
- pcd_sleep(HZ / 10);
-
- flg = 1;
- for (i = 0; i < 5; i++)
- flg &= (read_reg(cd, i + 1) == expect[i]);
-
- if (verbose) {
- printk("%s: Reset (%d) signature = ", cd->name, k);
- for (i = 0; i < 5; i++)
- printk("%3x", read_reg(cd, i + 1));
- if (!flg)
- printk(" (incorrect)");
- printk("\n");
- }
-
- pi_disconnect(cd->pi);
- return flg - 1;
-}
-
-static int pcd_drive_reset(struct cdrom_device_info *cdi)
-{
- return pcd_reset(cdi->handle);
-}
-
-static int pcd_ready_wait(struct pcd_unit *cd, int tmo)
-{
- char tr_cmd[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int k, p;
-
- k = 0;
- while (k < tmo) {
- cd->last_sense = 0;
- pcd_atapi(cd, tr_cmd, 0, NULL, DBMSG("test unit ready"));
- p = cd->last_sense;
- if (!p)
- return 0;
- if (!(((p & 0xffff) == 0x0402) || ((p & 0xff) == 6)))
- return p;
- k++;
- pcd_sleep(HZ);
- }
- return 0x000020; /* timeout */
-}
-
-static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
-{
- char rc_cmd[12] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- struct pcd_unit *cd = cdi->handle;
-
- if (pcd_ready_wait(cd, PCD_READY_TMO))
- return CDS_DRIVE_NOT_READY;
- if (pcd_atapi(cd, rc_cmd, 8, pcd_scratch, DBMSG("check media")))
- return CDS_NO_DISC;
- return CDS_DISC_OK;
-}
-
-static int pcd_identify(struct pcd_unit *cd)
-{
- char id_cmd[12] = { 0x12, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
- char id[18];
- int k, s;
-
- pcd_bufblk = -1;
-
- s = pcd_atapi(cd, id_cmd, 36, pcd_buffer, "identify");
-
- if (s)
- return -1;
- if ((pcd_buffer[0] & 0x1f) != 5) {
- if (verbose)
- printk("%s: %s is not a CD-ROM\n",
- cd->name, cd->drive ? "Slave" : "Master");
- return -1;
- }
- memcpy(id, pcd_buffer + 16, 16);
- id[16] = 0;
- k = 16;
- while ((k >= 0) && (id[k] <= 0x20)) {
- id[k] = 0;
- k--;
- }
-
- printk("%s: %s: %s\n", cd->name, cd->drive ? "Slave" : "Master", id);
-
- return 0;
-}
-
-/*
- * returns 0, with id set if drive is detected, otherwise an error code.
- */
-static int pcd_probe(struct pcd_unit *cd, int ms)
-{
- if (ms == -1) {
- for (cd->drive = 0; cd->drive <= 1; cd->drive++)
- if (!pcd_reset(cd) && !pcd_identify(cd))
- return 0;
- } else {
- cd->drive = ms;
- if (!pcd_reset(cd) && !pcd_identify(cd))
- return 0;
- }
- return -ENODEV;
-}
-
-static int pcd_probe_capabilities(struct pcd_unit *cd)
-{
- char cmd[12] = { 0x5a, 1 << 3, 0x2a, 0, 0, 0, 0, 18, 0, 0, 0, 0 };
- char buffer[32];
- int ret;
-
- ret = pcd_atapi(cd, cmd, 18, buffer, "mode sense capabilities");
- if (ret)
- return ret;
-
- /* we should now have the cap page */
- if ((buffer[11] & 1) == 0)
- cd->info.mask |= CDC_CD_R;
- if ((buffer[11] & 2) == 0)
- cd->info.mask |= CDC_CD_RW;
- if ((buffer[12] & 1) == 0)
- cd->info.mask |= CDC_PLAY_AUDIO;
- if ((buffer[14] & 1) == 0)
- cd->info.mask |= CDC_LOCK;
- if ((buffer[14] & 8) == 0)
- cd->info.mask |= CDC_OPEN_TRAY;
- if ((buffer[14] >> 6) == 0)
- cd->info.mask |= CDC_CLOSE_TRAY;
-
- return 0;
-}
-
-/* I/O request processing */
-static int pcd_queue;
-
-static int set_next_request(void)
-{
- struct pcd_unit *cd;
- int old_pos = pcd_queue;
-
- do {
- cd = &pcd[pcd_queue];
- if (++pcd_queue == PCD_UNITS)
- pcd_queue = 0;
- if (cd->present && !list_empty(&cd->rq_list)) {
- pcd_req = list_first_entry(&cd->rq_list, struct request,
- queuelist);
- list_del_init(&pcd_req->queuelist);
- blk_mq_start_request(pcd_req);
- break;
- }
- } while (pcd_queue != old_pos);
-
- return pcd_req != NULL;
-}
-
-static void pcd_request(void)
-{
- struct pcd_unit *cd;
-
- if (pcd_busy)
- return;
-
- if (!pcd_req && !set_next_request())
- return;
-
- cd = pcd_req->q->disk->private_data;
- if (cd != pcd_current)
- pcd_bufblk = -1;
- pcd_current = cd;
- pcd_sector = blk_rq_pos(pcd_req);
- pcd_count = blk_rq_cur_sectors(pcd_req);
- pcd_buf = bio_data(pcd_req->bio);
- pcd_busy = 1;
- ps_set_intr(do_pcd_read, NULL, 0, nice);
-}
-
-static blk_status_t pcd_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd)
-{
- struct pcd_unit *cd = hctx->queue->queuedata;
-
- if (rq_data_dir(bd->rq) != READ) {
- blk_mq_start_request(bd->rq);
- return BLK_STS_IOERR;
- }
-
- spin_lock_irq(&pcd_lock);
- list_add_tail(&bd->rq->queuelist, &cd->rq_list);
- pcd_request();
- spin_unlock_irq(&pcd_lock);
-
- return BLK_STS_OK;
-}
-
-static inline void next_request(blk_status_t err)
-{
- unsigned long saved_flags;
-
- spin_lock_irqsave(&pcd_lock, saved_flags);
- if (!blk_update_request(pcd_req, err, blk_rq_cur_bytes(pcd_req))) {
- __blk_mq_end_request(pcd_req, err);
- pcd_req = NULL;
- }
- pcd_busy = 0;
- pcd_request();
- spin_unlock_irqrestore(&pcd_lock, saved_flags);
-}
-
-static int pcd_ready(void)
-{
- return (((status_reg(pcd_current) & (IDE_BUSY | IDE_DRQ)) == IDE_DRQ));
-}
-
-static void pcd_transfer(void)
-{
-
- while (pcd_count && (pcd_sector / 4 == pcd_bufblk)) {
- int o = (pcd_sector % 4) * 512;
- memcpy(pcd_buf, pcd_buffer + o, 512);
- pcd_count--;
- pcd_buf += 512;
- pcd_sector++;
- }
-}
-
-static void pcd_start(void)
-{
- int b, i;
- char rd_cmd[12] = { 0xa8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
-
- pcd_bufblk = pcd_sector / 4;
- b = pcd_bufblk;
- for (i = 0; i < 4; i++) {
- rd_cmd[5 - i] = b & 0xff;
- b = b >> 8;
- }
-
- if (pcd_command(pcd_current, rd_cmd, 2048, "read block")) {
- pcd_bufblk = -1;
- next_request(BLK_STS_IOERR);
- return;
- }
-
- mdelay(1);
-
- ps_set_intr(do_pcd_read_drq, pcd_ready, PCD_TMO, nice);
-}
-
-static void do_pcd_read(void)
-{
- pcd_busy = 1;
- pcd_retries = 0;
- pcd_transfer();
- if (!pcd_count) {
- next_request(0);
- return;
- }
-
- pi_do_claimed(pcd_current->pi, pcd_start);
-}
-
-static void do_pcd_read_drq(void)
-{
- unsigned long saved_flags;
-
- if (pcd_completion(pcd_current, pcd_buffer, "read block")) {
- if (pcd_retries < PCD_RETRIES) {
- mdelay(1);
- pcd_retries++;
- pi_do_claimed(pcd_current->pi, pcd_start);
- return;
- }
- pcd_bufblk = -1;
- next_request(BLK_STS_IOERR);
- return;
- }
-
- do_pcd_read();
- spin_lock_irqsave(&pcd_lock, saved_flags);
- pcd_request();
- spin_unlock_irqrestore(&pcd_lock, saved_flags);
-}
-
-/* the audio_ioctl stuff is adapted from sr_ioctl.c */
-
-static int pcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
-{
- struct pcd_unit *cd = cdi->handle;
-
- switch (cmd) {
-
- case CDROMREADTOCHDR:
-
- {
- char cmd[12] =
- { GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
- 0, 0, 0 };
- struct cdrom_tochdr *tochdr =
- (struct cdrom_tochdr *) arg;
- char buffer[32];
- int r;
-
- r = pcd_atapi(cd, cmd, 12, buffer, "read toc header");
-
- tochdr->cdth_trk0 = buffer[2];
- tochdr->cdth_trk1 = buffer[3];
-
- return r ? -EIO : 0;
- }
-
- case CDROMREADTOCENTRY:
-
- {
- char cmd[12] =
- { GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
- 0, 0, 0 };
-
- struct cdrom_tocentry *tocentry =
- (struct cdrom_tocentry *) arg;
- unsigned char buffer[32];
- int r;
-
- cmd[1] =
- (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
- cmd[6] = tocentry->cdte_track;
-
- r = pcd_atapi(cd, cmd, 12, buffer, "read toc entry");
-
- tocentry->cdte_ctrl = buffer[5] & 0xf;
- tocentry->cdte_adr = buffer[5] >> 4;
- tocentry->cdte_datamode =
- (tocentry->cdte_ctrl & 0x04) ? 1 : 0;
- if (tocentry->cdte_format == CDROM_MSF) {
- tocentry->cdte_addr.msf.minute = buffer[9];
- tocentry->cdte_addr.msf.second = buffer[10];
- tocentry->cdte_addr.msf.frame = buffer[11];
- } else
- tocentry->cdte_addr.lba =
- (((((buffer[8] << 8) + buffer[9]) << 8)
- + buffer[10]) << 8) + buffer[11];
-
- return r ? -EIO : 0;
- }
-
- default:
-
- return -ENOSYS;
- }
-}
-
-static int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
-{
- char cmd[12] =
- { GPCMD_READ_SUBCHANNEL, 0, 0x40, 2, 0, 0, 0, 0, 24, 0, 0, 0 };
- char buffer[32];
-
- if (pcd_atapi(cdi->handle, cmd, 24, buffer, "get mcn"))
- return -EIO;
-
- memcpy(mcn->medium_catalog_number, buffer + 9, 13);
- mcn->medium_catalog_number[13] = 0;
-
- return 0;
-}
-
-static int pcd_init_unit(struct pcd_unit *cd, bool autoprobe, int port,
- int mode, int unit, int protocol, int delay, int ms)
-{
- struct gendisk *disk;
- int ret;
-
- ret = blk_mq_alloc_sq_tag_set(&cd->tag_set, &pcd_mq_ops, 1,
- BLK_MQ_F_SHOULD_MERGE);
- if (ret)
- return ret;
-
- disk = blk_mq_alloc_disk(&cd->tag_set, cd);
- if (IS_ERR(disk)) {
- ret = PTR_ERR(disk);
- goto out_free_tag_set;
- }
-
- INIT_LIST_HEAD(&cd->rq_list);
- blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH);
- cd->disk = disk;
- cd->pi = &cd->pia;
- cd->present = 0;
- cd->last_sense = 0;
- cd->changed = 1;
- cd->drive = (*drives[cd - pcd])[D_SLV];
-
- cd->name = &cd->info.name[0];
- snprintf(cd->name, sizeof(cd->info.name), "%s%d", name, unit);
- cd->info.ops = &pcd_dops;
- cd->info.handle = cd;
- cd->info.speed = 0;
- cd->info.capacity = 1;
- cd->info.mask = 0;
- disk->major = major;
- disk->first_minor = unit;
- disk->minors = 1;
- strcpy(disk->disk_name, cd->name); /* umm... */
- disk->fops = &pcd_bdops;
- disk->flags |= GENHD_FL_NO_PART;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
- disk->event_flags = DISK_EVENT_FLAG_BLOCK_ON_EXCL_WRITE;
-
- if (!pi_init(cd->pi, autoprobe, port, mode, unit, protocol, delay,
- pcd_buffer, PI_PCD, verbose, cd->name)) {
- ret = -ENODEV;
- goto out_free_disk;
- }
- ret = pcd_probe(cd, ms);
- if (ret)
- goto out_pi_release;
-
- cd->present = 1;
- pcd_probe_capabilities(cd);
- ret = register_cdrom(cd->disk, &cd->info);
- if (ret)
- goto out_pi_release;
- ret = add_disk(cd->disk);
- if (ret)
- goto out_unreg_cdrom;
- return 0;
-
-out_unreg_cdrom:
- unregister_cdrom(&cd->info);
-out_pi_release:
- pi_release(cd->pi);
-out_free_disk:
- put_disk(cd->disk);
-out_free_tag_set:
- blk_mq_free_tag_set(&cd->tag_set);
- return ret;
-}
-
-static int __init pcd_init(void)
-{
- int found = 0, unit;
-
- if (disable)
- return -EINVAL;
-
- if (register_blkdev(major, name))
- return -EBUSY;
-
- pr_info("%s: %s version %s, major %d, nice %d\n",
- name, name, PCD_VERSION, major, nice);
-
- par_drv = pi_register_driver(name);
- if (!par_drv) {
- pr_err("failed to register %s driver\n", name);
- goto out_unregister_blkdev;
- }
-
- for (unit = 0; unit < PCD_UNITS; unit++) {
- if ((*drives[unit])[D_PRT])
- pcd_drive_count++;
- }
-
- if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
- if (!pcd_init_unit(pcd, 1, -1, -1, -1, -1, -1, -1))
- found++;
- } else {
- for (unit = 0; unit < PCD_UNITS; unit++) {
- struct pcd_unit *cd = &pcd[unit];
- int *conf = *drives[unit];
-
- if (!conf[D_PRT])
- continue;
- if (!pcd_init_unit(cd, 0, conf[D_PRT], conf[D_MOD],
- conf[D_UNI], conf[D_PRO], conf[D_DLY],
- conf[D_SLV]))
- found++;
- }
- }
-
- if (!found) {
- pr_info("%s: No CD-ROM drive found\n", name);
- goto out_unregister_pi_driver;
- }
-
- return 0;
-
-out_unregister_pi_driver:
- pi_unregister_driver(par_drv);
-out_unregister_blkdev:
- unregister_blkdev(major, name);
- return -ENODEV;
-}
-
-static void __exit pcd_exit(void)
-{
- struct pcd_unit *cd;
- int unit;
-
- for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
- if (!cd->present)
- continue;
-
- unregister_cdrom(&cd->info);
- del_gendisk(cd->disk);
- pi_release(cd->pi);
- put_disk(cd->disk);
-
- blk_mq_free_tag_set(&cd->tag_set);
- }
- pi_unregister_driver(par_drv);
- unregister_blkdev(major, name);
-}
-
-MODULE_LICENSE("GPL");
-module_init(pcd_init)
-module_exit(pcd_exit)
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
deleted file mode 100644
index f8a75bc90f70..000000000000
--- a/drivers/block/paride/pd.c
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
- pd.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is the high-level driver for parallel port IDE hard
- drives based on chips supported by the paride module.
-
- By default, the driver will autoprobe for a single parallel
- port IDE drive, but if their individual parameters are
- specified, the driver can handle up to 4 drives.
-
- The behaviour of the pd driver can be altered by setting
- some parameters from the insmod command line. The following
- parameters are adjustable:
-
- drive0 These four arguments can be arrays of
- drive1 1-8 integers as follows:
- drive2
- drive3 <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>,<slv>
-
- Where,
-
- <prt> is the base of the parallel port address for
- the corresponding drive. (required)
-
- <pro> is the protocol number for the adapter that
- supports this drive. These numbers are
- logged by 'paride' when the protocol modules
- are initialised. (0 if not given)
-
- <uni> for those adapters that support chained
- devices, this is the unit selector for the
- chain of devices on the given port. It should
- be zero for devices that don't support chaining.
- (0 if not given)
-
- <mod> this can be -1 to choose the best mode, or one
- of the mode numbers supported by the adapter.
- (-1 if not given)
-
- <geo> this defaults to 0 to indicate that the driver
- should use the CHS geometry provided by the drive
- itself. If set to 1, the driver will provide
- a logical geometry with 64 heads and 32 sectors
- per track, to be consistent with most SCSI
- drivers. (0 if not given)
-
- <sby> set this to zero to disable the power saving
- standby mode, if needed. (1 if not given)
-
- <dly> some parallel ports require the driver to
- go more slowly. -1 sets a default value that
- should work with the chosen protocol. Otherwise,
- set this to a small integer, the larger it is
- the slower the port i/o. In some cases, setting
- this to zero will speed up the device. (default -1)
-
- <slv> IDE disks can be jumpered to master or slave.
- Set this to 0 to choose the master drive, 1 to
- choose the slave, -1 (the default) to choose the
- first drive found.
-
-
- major You may use this parameter to override the
- default major number (45) that this driver
- will use. Be sure to change the device
- name as well.
-
- name This parameter is a character string that
- contains the name the kernel will use for this
- device (in /proc output, for instance).
- (default "pd")
-
- cluster The driver will attempt to aggregate requests
- for adjacent blocks into larger multi-block
- clusters. The maximum cluster size (in 512
- byte sectors) is set with this parameter.
- (default 64)
-
- verbose This parameter controls the amount of logging
- that the driver will do. Set it to 0 for
- normal operation, 1 to see autoprobe progress
- messages, or 2 to see additional debugging
- output. (default 0)
-
- nice This parameter controls the driver's use of
- idle CPU time, at the expense of some speed.
-
- If this driver is built into the kernel, you can use kernel
- the following command line parameters, with the same values
- as the corresponding module parameters listed above:
-
- pd.drive0
- pd.drive1
- pd.drive2
- pd.drive3
- pd.cluster
- pd.nice
-
- In addition, you can use the parameter pd.disable to disable
- the driver entirely.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1997.01.24 Restored pd_reset()
- Added eject ioctl
- 1.02 GRG 1998.05.06 SMP spinlock changes,
- Added slave support
- 1.03 GRG 1998.06.16 Eliminate an Ugh.
- 1.04 GRG 1998.08.15 Extra debugging, use HZ in loop timing
- 1.05 GRG 1998.09.24 Added jumbo support
-
-*/
-
-#define PD_VERSION "1.05"
-#define PD_MAJOR 45
-#define PD_NAME "pd"
-#define PD_UNITS 4
-
-/* Here are things one can override from the insmod command.
- Most are autoprobed by paride unless set here. Verbose is off
- by default.
-
-*/
-#include <linux/types.h>
-
-static int verbose = 0;
-static int major = PD_MAJOR;
-static char *name = PD_NAME;
-static int cluster = 64;
-static int nice = 0;
-static int disable = 0;
-
-static int drive0[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
-static int drive1[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
-static int drive2[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
-static int drive3[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
-
-static int (*drives[4])[8] = {&drive0, &drive1, &drive2, &drive3};
-
-enum {D_PRT, D_PRO, D_UNI, D_MOD, D_GEO, D_SBY, D_DLY, D_SLV};
-
-/* end of parameters */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/gfp.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/hdreg.h>
-#include <linux/cdrom.h> /* for the eject ioctl */
-#include <linux/blk-mq.h>
-#include <linux/blkpg.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/workqueue.h>
-
-static DEFINE_MUTEX(pd_mutex);
-static DEFINE_SPINLOCK(pd_lock);
-
-module_param(verbose, int, 0);
-module_param(major, int, 0);
-module_param(name, charp, 0);
-module_param(cluster, int, 0);
-module_param(nice, int, 0);
-module_param_array(drive0, int, NULL, 0);
-module_param_array(drive1, int, NULL, 0);
-module_param_array(drive2, int, NULL, 0);
-module_param_array(drive3, int, NULL, 0);
-
-#include "paride.h"
-
-#define PD_BITS 4
-
-/* numbers for "SCSI" geometry */
-
-#define PD_LOG_HEADS 64
-#define PD_LOG_SECTS 32
-
-#define PD_ID_OFF 54
-#define PD_ID_LEN 14
-
-#define PD_MAX_RETRIES 5
-#define PD_TMO 800 /* interrupt timeout in jiffies */
-#define PD_SPIN_DEL 50 /* spin delay in micro-seconds */
-
-#define PD_SPIN (1000000*PD_TMO)/(HZ*PD_SPIN_DEL)
-
-#define STAT_ERR 0x00001
-#define STAT_INDEX 0x00002
-#define STAT_ECC 0x00004
-#define STAT_DRQ 0x00008
-#define STAT_SEEK 0x00010
-#define STAT_WRERR 0x00020
-#define STAT_READY 0x00040
-#define STAT_BUSY 0x00080
-
-#define ERR_AMNF 0x00100
-#define ERR_TK0NF 0x00200
-#define ERR_ABRT 0x00400
-#define ERR_MCR 0x00800
-#define ERR_IDNF 0x01000
-#define ERR_MC 0x02000
-#define ERR_UNC 0x04000
-#define ERR_TMO 0x10000
-
-#define IDE_READ 0x20
-#define IDE_WRITE 0x30
-#define IDE_READ_VRFY 0x40
-#define IDE_INIT_DEV_PARMS 0x91
-#define IDE_STANDBY 0x96
-#define IDE_ACKCHANGE 0xdb
-#define IDE_DOORLOCK 0xde
-#define IDE_DOORUNLOCK 0xdf
-#define IDE_IDENTIFY 0xec
-#define IDE_EJECT 0xed
-
-#define PD_NAMELEN 8
-
-struct pd_unit {
- struct pi_adapter pia; /* interface to paride layer */
- struct pi_adapter *pi;
- int access; /* count of active opens ... */
- int capacity; /* Size of this volume in sectors */
- int heads; /* physical geometry */
- int sectors;
- int cylinders;
- int can_lba;
- int drive; /* master=0 slave=1 */
- int changed; /* Have we seen a disk change ? */
- int removable; /* removable media device ? */
- int standby;
- int alt_geom;
- char name[PD_NAMELEN]; /* pda, pdb, etc ... */
- struct gendisk *gd;
- struct blk_mq_tag_set tag_set;
- struct list_head rq_list;
-};
-
-static struct pd_unit pd[PD_UNITS];
-
-struct pd_req {
- /* for REQ_OP_DRV_IN: */
- enum action (*func)(struct pd_unit *disk);
-};
-
-static char pd_scratch[512]; /* scratch block buffer */
-
-static char *pd_errs[17] = { "ERR", "INDEX", "ECC", "DRQ", "SEEK", "WRERR",
- "READY", "BUSY", "AMNF", "TK0NF", "ABRT", "MCR",
- "IDNF", "MC", "UNC", "???", "TMO"
-};
-
-static void *par_drv; /* reference of parport driver */
-
-static inline int status_reg(struct pd_unit *disk)
-{
- return pi_read_regr(disk->pi, 1, 6);
-}
-
-static inline int read_reg(struct pd_unit *disk, int reg)
-{
- return pi_read_regr(disk->pi, 0, reg);
-}
-
-static inline void write_status(struct pd_unit *disk, int val)
-{
- pi_write_regr(disk->pi, 1, 6, val);
-}
-
-static inline void write_reg(struct pd_unit *disk, int reg, int val)
-{
- pi_write_regr(disk->pi, 0, reg, val);
-}
-
-static inline u8 DRIVE(struct pd_unit *disk)
-{
- return 0xa0+0x10*disk->drive;
-}
-
-/* ide command interface */
-
-static void pd_print_error(struct pd_unit *disk, char *msg, int status)
-{
- int i;
-
- printk("%s: %s: status = 0x%x =", disk->name, msg, status);
- for (i = 0; i < ARRAY_SIZE(pd_errs); i++)
- if (status & (1 << i))
- printk(" %s", pd_errs[i]);
- printk("\n");
-}
-
-static void pd_reset(struct pd_unit *disk)
-{ /* called only for MASTER drive */
- write_status(disk, 4);
- udelay(50);
- write_status(disk, 0);
- udelay(250);
-}
-
-#define DBMSG(msg) ((verbose>1)?(msg):NULL)
-
-static int pd_wait_for(struct pd_unit *disk, int w, char *msg)
-{ /* polled wait */
- int k, r, e;
-
- k = 0;
- while (k < PD_SPIN) {
- r = status_reg(disk);
- k++;
- if (((r & w) == w) && !(r & STAT_BUSY))
- break;
- udelay(PD_SPIN_DEL);
- }
- e = (read_reg(disk, 1) << 8) + read_reg(disk, 7);
- if (k >= PD_SPIN)
- e |= ERR_TMO;
- if ((e & (STAT_ERR | ERR_TMO)) && (msg != NULL))
- pd_print_error(disk, msg, e);
- return e;
-}
-
-static void pd_send_command(struct pd_unit *disk, int n, int s, int h, int c0, int c1, int func)
-{
- write_reg(disk, 6, DRIVE(disk) + h);
- write_reg(disk, 1, 0); /* the IDE task file */
- write_reg(disk, 2, n);
- write_reg(disk, 3, s);
- write_reg(disk, 4, c0);
- write_reg(disk, 5, c1);
- write_reg(disk, 7, func);
-
- udelay(1);
-}
-
-static void pd_ide_command(struct pd_unit *disk, int func, int block, int count)
-{
- int c1, c0, h, s;
-
- if (disk->can_lba) {
- s = block & 255;
- c0 = (block >>= 8) & 255;
- c1 = (block >>= 8) & 255;
- h = ((block >>= 8) & 15) + 0x40;
- } else {
- s = (block % disk->sectors) + 1;
- h = (block /= disk->sectors) % disk->heads;
- c0 = (block /= disk->heads) % 256;
- c1 = (block >>= 8);
- }
- pd_send_command(disk, count, s, h, c0, c1, func);
-}
-
-/* The i/o request engine */
-
-enum action {Fail = 0, Ok = 1, Hold, Wait};
-
-static struct request *pd_req; /* current request */
-static enum action (*phase)(void);
-
-static void run_fsm(void);
-
-static void ps_tq_int(struct work_struct *work);
-
-static DECLARE_DELAYED_WORK(fsm_tq, ps_tq_int);
-
-static void schedule_fsm(void)
-{
- if (!nice)
- schedule_delayed_work(&fsm_tq, 0);
- else
- schedule_delayed_work(&fsm_tq, nice-1);
-}
-
-static void ps_tq_int(struct work_struct *work)
-{
- run_fsm();
-}
-
-static enum action do_pd_io_start(void);
-static enum action pd_special(void);
-static enum action do_pd_read_start(void);
-static enum action do_pd_write_start(void);
-static enum action do_pd_read_drq(void);
-static enum action do_pd_write_done(void);
-
-static int pd_queue;
-static int pd_claimed;
-
-static struct pd_unit *pd_current; /* current request's drive */
-static PIA *pi_current; /* current request's PIA */
-
-static int set_next_request(void)
-{
- struct gendisk *disk;
- struct request_queue *q;
- int old_pos = pd_queue;
-
- do {
- disk = pd[pd_queue].gd;
- q = disk ? disk->queue : NULL;
- if (++pd_queue == PD_UNITS)
- pd_queue = 0;
- if (q) {
- struct pd_unit *disk = q->queuedata;
-
- if (list_empty(&disk->rq_list))
- continue;
-
- pd_req = list_first_entry(&disk->rq_list,
- struct request,
- queuelist);
- list_del_init(&pd_req->queuelist);
- blk_mq_start_request(pd_req);
- break;
- }
- } while (pd_queue != old_pos);
-
- return pd_req != NULL;
-}
-
-static void run_fsm(void)
-{
- while (1) {
- enum action res;
- int stop = 0;
-
- if (!phase) {
- pd_current = pd_req->q->disk->private_data;
- pi_current = pd_current->pi;
- phase = do_pd_io_start;
- }
-
- switch (pd_claimed) {
- case 0:
- pd_claimed = 1;
- if (!pi_schedule_claimed(pi_current, run_fsm))
- return;
- fallthrough;
- case 1:
- pd_claimed = 2;
- pi_current->proto->connect(pi_current);
- }
-
- switch(res = phase()) {
- case Ok: case Fail: {
- blk_status_t err;
-
- err = res == Ok ? 0 : BLK_STS_IOERR;
- pi_disconnect(pi_current);
- pd_claimed = 0;
- phase = NULL;
- spin_lock_irq(&pd_lock);
- if (!blk_update_request(pd_req, err,
- blk_rq_cur_bytes(pd_req))) {
- __blk_mq_end_request(pd_req, err);
- pd_req = NULL;
- stop = !set_next_request();
- }
- spin_unlock_irq(&pd_lock);
- if (stop)
- return;
- }
- fallthrough;
- case Hold:
- schedule_fsm();
- return;
- case Wait:
- pi_disconnect(pi_current);
- pd_claimed = 0;
- }
- }
-}
-
-static int pd_retries = 0; /* i/o error retry count */
-static int pd_block; /* address of next requested block */
-static int pd_count; /* number of blocks still to do */
-static int pd_run; /* sectors in current cluster */
-static char *pd_buf; /* buffer for request in progress */
-
-static enum action do_pd_io_start(void)
-{
- switch (req_op(pd_req)) {
- case REQ_OP_DRV_IN:
- phase = pd_special;
- return pd_special();
- case REQ_OP_READ:
- case REQ_OP_WRITE:
- pd_block = blk_rq_pos(pd_req);
- pd_count = blk_rq_cur_sectors(pd_req);
- if (pd_block + pd_count > get_capacity(pd_req->q->disk))
- return Fail;
- pd_run = blk_rq_sectors(pd_req);
- pd_buf = bio_data(pd_req->bio);
- pd_retries = 0;
- if (req_op(pd_req) == REQ_OP_READ)
- return do_pd_read_start();
- else
- return do_pd_write_start();
- default:
- break;
- }
- return Fail;
-}
-
-static enum action pd_special(void)
-{
- struct pd_req *req = blk_mq_rq_to_pdu(pd_req);
-
- return req->func(pd_current);
-}
-
-static int pd_next_buf(void)
-{
- unsigned long saved_flags;
-
- pd_count--;
- pd_run--;
- pd_buf += 512;
- pd_block++;
- if (!pd_run)
- return 1;
- if (pd_count)
- return 0;
- spin_lock_irqsave(&pd_lock, saved_flags);
- if (!blk_update_request(pd_req, 0, blk_rq_cur_bytes(pd_req))) {
- __blk_mq_end_request(pd_req, 0);
- pd_req = NULL;
- pd_count = 0;
- pd_buf = NULL;
- } else {
- pd_count = blk_rq_cur_sectors(pd_req);
- pd_buf = bio_data(pd_req->bio);
- }
- spin_unlock_irqrestore(&pd_lock, saved_flags);
- return !pd_count;
-}
-
-static unsigned long pd_timeout;
-
-static enum action do_pd_read_start(void)
-{
- if (pd_wait_for(pd_current, STAT_READY, "do_pd_read") & STAT_ERR) {
- if (pd_retries < PD_MAX_RETRIES) {
- pd_retries++;
- return Wait;
- }
- return Fail;
- }
- pd_ide_command(pd_current, IDE_READ, pd_block, pd_run);
- phase = do_pd_read_drq;
- pd_timeout = jiffies + PD_TMO;
- return Hold;
-}
-
-static enum action do_pd_write_start(void)
-{
- if (pd_wait_for(pd_current, STAT_READY, "do_pd_write") & STAT_ERR) {
- if (pd_retries < PD_MAX_RETRIES) {
- pd_retries++;
- return Wait;
- }
- return Fail;
- }
- pd_ide_command(pd_current, IDE_WRITE, pd_block, pd_run);
- while (1) {
- if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_write_drq") & STAT_ERR) {
- if (pd_retries < PD_MAX_RETRIES) {
- pd_retries++;
- return Wait;
- }
- return Fail;
- }
- pi_write_block(pd_current->pi, pd_buf, 512);
- if (pd_next_buf())
- break;
- }
- phase = do_pd_write_done;
- pd_timeout = jiffies + PD_TMO;
- return Hold;
-}
-
-static inline int pd_ready(void)
-{
- return !(status_reg(pd_current) & STAT_BUSY);
-}
-
-static enum action do_pd_read_drq(void)
-{
- if (!pd_ready() && !time_after_eq(jiffies, pd_timeout))
- return Hold;
-
- while (1) {
- if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_read_drq") & STAT_ERR) {
- if (pd_retries < PD_MAX_RETRIES) {
- pd_retries++;
- phase = do_pd_read_start;
- return Wait;
- }
- return Fail;
- }
- pi_read_block(pd_current->pi, pd_buf, 512);
- if (pd_next_buf())
- break;
- }
- return Ok;
-}
-
-static enum action do_pd_write_done(void)
-{
- if (!pd_ready() && !time_after_eq(jiffies, pd_timeout))
- return Hold;
-
- if (pd_wait_for(pd_current, STAT_READY, "do_pd_write_done") & STAT_ERR) {
- if (pd_retries < PD_MAX_RETRIES) {
- pd_retries++;
- phase = do_pd_write_start;
- return Wait;
- }
- return Fail;
- }
- return Ok;
-}
-
-/* special io requests */
-
-/* According to the ATA standard, the default CHS geometry should be
- available following a reset. Some Western Digital drives come up
- in a mode where only LBA addresses are accepted until the device
- parameters are initialised.
-*/
-
-static void pd_init_dev_parms(struct pd_unit *disk)
-{
- pd_wait_for(disk, 0, DBMSG("before init_dev_parms"));
- pd_send_command(disk, disk->sectors, 0, disk->heads - 1, 0, 0,
- IDE_INIT_DEV_PARMS);
- udelay(300);
- pd_wait_for(disk, 0, "Initialise device parameters");
-}
-
-static enum action pd_door_lock(struct pd_unit *disk)
-{
- if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) {
- pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORLOCK);
- pd_wait_for(disk, STAT_READY, "Lock done");
- }
- return Ok;
-}
-
-static enum action pd_door_unlock(struct pd_unit *disk)
-{
- if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) {
- pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK);
- pd_wait_for(disk, STAT_READY, "Lock done");
- }
- return Ok;
-}
-
-static enum action pd_eject(struct pd_unit *disk)
-{
- pd_wait_for(disk, 0, DBMSG("before unlock on eject"));
- pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK);
- pd_wait_for(disk, 0, DBMSG("after unlock on eject"));
- pd_wait_for(disk, 0, DBMSG("before eject"));
- pd_send_command(disk, 0, 0, 0, 0, 0, IDE_EJECT);
- pd_wait_for(disk, 0, DBMSG("after eject"));
- return Ok;
-}
-
-static enum action pd_media_check(struct pd_unit *disk)
-{
- int r = pd_wait_for(disk, STAT_READY, DBMSG("before media_check"));
- if (!(r & STAT_ERR)) {
- pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY);
- r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after READ_VRFY"));
- } else
- disk->changed = 1; /* say changed if other error */
- if (r & ERR_MC) {
- disk->changed = 1;
- pd_send_command(disk, 1, 0, 0, 0, 0, IDE_ACKCHANGE);
- pd_wait_for(disk, STAT_READY, DBMSG("RDY after ACKCHANGE"));
- pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY);
- r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after VRFY"));
- }
- return Ok;
-}
-
-static void pd_standby_off(struct pd_unit *disk)
-{
- pd_wait_for(disk, 0, DBMSG("before STANDBY"));
- pd_send_command(disk, 0, 0, 0, 0, 0, IDE_STANDBY);
- pd_wait_for(disk, 0, DBMSG("after STANDBY"));
-}
-
-static enum action pd_identify(struct pd_unit *disk)
-{
- int j;
- char id[PD_ID_LEN + 1];
-
-/* WARNING: here there may be dragons. reset() applies to both drives,
- but we call it only on probing the MASTER. This should allow most
- common configurations to work, but be warned that a reset can clear
- settings on the SLAVE drive.
-*/
-
- if (disk->drive == 0)
- pd_reset(disk);
-
- write_reg(disk, 6, DRIVE(disk));
- pd_wait_for(disk, 0, DBMSG("before IDENT"));
- pd_send_command(disk, 1, 0, 0, 0, 0, IDE_IDENTIFY);
-
- if (pd_wait_for(disk, STAT_DRQ, DBMSG("IDENT DRQ")) & STAT_ERR)
- return Fail;
- pi_read_block(disk->pi, pd_scratch, 512);
- disk->can_lba = pd_scratch[99] & 2;
- disk->sectors = le16_to_cpu(*(__le16 *) (pd_scratch + 12));
- disk->heads = le16_to_cpu(*(__le16 *) (pd_scratch + 6));
- disk->cylinders = le16_to_cpu(*(__le16 *) (pd_scratch + 2));
- if (disk->can_lba)
- disk->capacity = le32_to_cpu(*(__le32 *) (pd_scratch + 120));
- else
- disk->capacity = disk->sectors * disk->heads * disk->cylinders;
-
- for (j = 0; j < PD_ID_LEN; j++)
- id[j ^ 1] = pd_scratch[j + PD_ID_OFF];
- j = PD_ID_LEN - 1;
- while ((j >= 0) && (id[j] <= 0x20))
- j--;
- j++;
- id[j] = 0;
-
- disk->removable = pd_scratch[0] & 0x80;
-
- printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
- disk->name, id,
- disk->drive ? "slave" : "master",
- disk->capacity, disk->capacity / 2048,
- disk->cylinders, disk->heads, disk->sectors,
- disk->removable ? "removable" : "fixed");
-
- if (disk->capacity)
- pd_init_dev_parms(disk);
- if (!disk->standby)
- pd_standby_off(disk);
-
- return Ok;
-}
-
-/* end of io request engine */
-
-static blk_status_t pd_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd)
-{
- struct pd_unit *disk = hctx->queue->queuedata;
-
- spin_lock_irq(&pd_lock);
- if (!pd_req) {
- pd_req = bd->rq;
- blk_mq_start_request(pd_req);
- } else
- list_add_tail(&bd->rq->queuelist, &disk->rq_list);
- spin_unlock_irq(&pd_lock);
-
- run_fsm();
- return BLK_STS_OK;
-}
-
-static int pd_special_command(struct pd_unit *disk,
- enum action (*func)(struct pd_unit *disk))
-{
- struct request *rq;
- struct pd_req *req;
-
- rq = blk_mq_alloc_request(disk->gd->queue, REQ_OP_DRV_IN, 0);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- req = blk_mq_rq_to_pdu(rq);
-
- req->func = func;
- blk_execute_rq(rq, false);
- blk_mq_free_request(rq);
- return 0;
-}
-
-/* kernel glue structures */
-
-static int pd_open(struct block_device *bdev, fmode_t mode)
-{
- struct pd_unit *disk = bdev->bd_disk->private_data;
-
- mutex_lock(&pd_mutex);
- disk->access++;
-
- if (disk->removable) {
- pd_special_command(disk, pd_media_check);
- pd_special_command(disk, pd_door_lock);
- }
- mutex_unlock(&pd_mutex);
- return 0;
-}
-
-static int pd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
-{
- struct pd_unit *disk = bdev->bd_disk->private_data;
-
- if (disk->alt_geom) {
- geo->heads = PD_LOG_HEADS;
- geo->sectors = PD_LOG_SECTS;
- geo->cylinders = disk->capacity / (geo->heads * geo->sectors);
- } else {
- geo->heads = disk->heads;
- geo->sectors = disk->sectors;
- geo->cylinders = disk->cylinders;
- }
-
- return 0;
-}
-
-static int pd_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
-{
- struct pd_unit *disk = bdev->bd_disk->private_data;
-
- switch (cmd) {
- case CDROMEJECT:
- mutex_lock(&pd_mutex);
- if (disk->access == 1)
- pd_special_command(disk, pd_eject);
- mutex_unlock(&pd_mutex);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static void pd_release(struct gendisk *p, fmode_t mode)
-{
- struct pd_unit *disk = p->private_data;
-
- mutex_lock(&pd_mutex);
- if (!--disk->access && disk->removable)
- pd_special_command(disk, pd_door_unlock);
- mutex_unlock(&pd_mutex);
-}
-
-static unsigned int pd_check_events(struct gendisk *p, unsigned int clearing)
-{
- struct pd_unit *disk = p->private_data;
- int r;
- if (!disk->removable)
- return 0;
- pd_special_command(disk, pd_media_check);
- r = disk->changed;
- disk->changed = 0;
- return r ? DISK_EVENT_MEDIA_CHANGE : 0;
-}
-
-static const struct block_device_operations pd_fops = {
- .owner = THIS_MODULE,
- .open = pd_open,
- .release = pd_release,
- .ioctl = pd_ioctl,
- .compat_ioctl = pd_ioctl,
- .getgeo = pd_getgeo,
- .check_events = pd_check_events,
-};
-
-/* probing */
-
-static const struct blk_mq_ops pd_mq_ops = {
- .queue_rq = pd_queue_rq,
-};
-
-static int pd_probe_drive(struct pd_unit *disk, int autoprobe, int port,
- int mode, int unit, int protocol, int delay)
-{
- int index = disk - pd;
- int *parm = *drives[index];
- struct gendisk *p;
- int ret;
-
- disk->pi = &disk->pia;
- disk->access = 0;
- disk->changed = 1;
- disk->capacity = 0;
- disk->drive = parm[D_SLV];
- snprintf(disk->name, PD_NAMELEN, "%s%c", name, 'a' + index);
- disk->alt_geom = parm[D_GEO];
- disk->standby = parm[D_SBY];
- INIT_LIST_HEAD(&disk->rq_list);
-
- if (!pi_init(disk->pi, autoprobe, port, mode, unit, protocol, delay,
- pd_scratch, PI_PD, verbose, disk->name))
- return -ENXIO;
-
- memset(&disk->tag_set, 0, sizeof(disk->tag_set));
- disk->tag_set.ops = &pd_mq_ops;
- disk->tag_set.cmd_size = sizeof(struct pd_req);
- disk->tag_set.nr_hw_queues = 1;
- disk->tag_set.nr_maps = 1;
- disk->tag_set.queue_depth = 2;
- disk->tag_set.numa_node = NUMA_NO_NODE;
- disk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING;
- ret = blk_mq_alloc_tag_set(&disk->tag_set);
- if (ret)
- goto pi_release;
-
- p = blk_mq_alloc_disk(&disk->tag_set, disk);
- if (IS_ERR(p)) {
- ret = PTR_ERR(p);
- goto free_tag_set;
- }
- disk->gd = p;
-
- strcpy(p->disk_name, disk->name);
- p->fops = &pd_fops;
- p->major = major;
- p->first_minor = (disk - pd) << PD_BITS;
- p->minors = 1 << PD_BITS;
- p->events = DISK_EVENT_MEDIA_CHANGE;
- p->private_data = disk;
- blk_queue_max_hw_sectors(p->queue, cluster);
- blk_queue_bounce_limit(p->queue, BLK_BOUNCE_HIGH);
-
- if (disk->drive == -1) {
- for (disk->drive = 0; disk->drive <= 1; disk->drive++) {
- ret = pd_special_command(disk, pd_identify);
- if (ret == 0)
- break;
- }
- } else {
- ret = pd_special_command(disk, pd_identify);
- }
- if (ret)
- goto put_disk;
- set_capacity(disk->gd, disk->capacity);
- ret = add_disk(disk->gd);
- if (ret)
- goto cleanup_disk;
- return 0;
-cleanup_disk:
- put_disk(disk->gd);
-put_disk:
- put_disk(p);
- disk->gd = NULL;
-free_tag_set:
- blk_mq_free_tag_set(&disk->tag_set);
-pi_release:
- pi_release(disk->pi);
- return ret;
-}
-
-static int __init pd_init(void)
-{
- int found = 0, unit, pd_drive_count = 0;
- struct pd_unit *disk;
-
- if (disable)
- return -ENODEV;
-
- if (register_blkdev(major, name))
- return -ENODEV;
-
- printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
- name, name, PD_VERSION, major, cluster, nice);
-
- par_drv = pi_register_driver(name);
- if (!par_drv) {
- pr_err("failed to register %s driver\n", name);
- goto out_unregister_blkdev;
- }
-
- for (unit = 0; unit < PD_UNITS; unit++) {
- int *parm = *drives[unit];
-
- if (parm[D_PRT])
- pd_drive_count++;
- }
-
- if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
- if (!pd_probe_drive(pd, 1, -1, -1, -1, -1, -1))
- found++;
- } else {
- for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
- int *parm = *drives[unit];
- if (!parm[D_PRT])
- continue;
- if (!pd_probe_drive(disk, 0, parm[D_PRT], parm[D_MOD],
- parm[D_UNI], parm[D_PRO], parm[D_DLY]))
- found++;
- }
- }
- if (!found) {
- printk("%s: no valid drive found\n", name);
- goto out_pi_unregister_driver;
- }
-
- return 0;
-
-out_pi_unregister_driver:
- pi_unregister_driver(par_drv);
-out_unregister_blkdev:
- unregister_blkdev(major, name);
- return -ENODEV;
-}
-
-static void __exit pd_exit(void)
-{
- struct pd_unit *disk;
- int unit;
- unregister_blkdev(major, name);
- for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
- struct gendisk *p = disk->gd;
- if (p) {
- disk->gd = NULL;
- del_gendisk(p);
- put_disk(p);
- blk_mq_free_tag_set(&disk->tag_set);
- pi_release(disk->pi);
- }
- }
-}
-
-MODULE_LICENSE("GPL");
-module_init(pd_init)
-module_exit(pd_exit)
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
deleted file mode 100644
index eec1b9fde245..000000000000
--- a/drivers/block/paride/pf.c
+++ /dev/null
@@ -1,1057 +0,0 @@
-/*
- pf.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is the high-level driver for parallel port ATAPI disk
- drives based on chips supported by the paride module.
-
- By default, the driver will autoprobe for a single parallel
- port ATAPI disk drive, but if their individual parameters are
- specified, the driver can handle up to 4 drives.
-
- The behaviour of the pf driver can be altered by setting
- some parameters from the insmod command line. The following
- parameters are adjustable:
-
- drive0 These four arguments can be arrays of
- drive1 1-7 integers as follows:
- drive2
- drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly>
-
- Where,
-
- <prt> is the base of the parallel port address for
- the corresponding drive. (required)
-
- <pro> is the protocol number for the adapter that
- supports this drive. These numbers are
- logged by 'paride' when the protocol modules
- are initialised. (0 if not given)
-
- <uni> for those adapters that support chained
- devices, this is the unit selector for the
- chain of devices on the given port. It should
- be zero for devices that don't support chaining.
- (0 if not given)
-
- <mod> this can be -1 to choose the best mode, or one
- of the mode numbers supported by the adapter.
- (-1 if not given)
-
- <slv> ATAPI CDroms can be jumpered to master or slave.
- Set this to 0 to choose the master drive, 1 to
- choose the slave, -1 (the default) to choose the
- first drive found.
-
- <lun> Some ATAPI devices support multiple LUNs.
- One example is the ATAPI PD/CD drive from
- Matshita/Panasonic. This device has a
- CD drive on LUN 0 and a PD drive on LUN 1.
- By default, the driver will search for the
- first LUN with a supported device. Set
- this parameter to force it to use a specific
- LUN. (default -1)
-
- <dly> some parallel ports require the driver to
- go more slowly. -1 sets a default value that
- should work with the chosen protocol. Otherwise,
- set this to a small integer, the larger it is
- the slower the port i/o. In some cases, setting
- this to zero will speed up the device. (default -1)
-
- major You may use this parameter to override the
- default major number (47) that this driver
- will use. Be sure to change the device
- name as well.
-
- name This parameter is a character string that
- contains the name the kernel will use for this
- device (in /proc output, for instance).
- (default "pf").
-
- cluster The driver will attempt to aggregate requests
- for adjacent blocks into larger multi-block
- clusters. The maximum cluster size (in 512
- byte sectors) is set with this parameter.
- (default 64)
-
- verbose This parameter controls the amount of logging
- that the driver will do. Set it to 0 for
- normal operation, 1 to see autoprobe progress
- messages, or 2 to see additional debugging
- output. (default 0)
-
- nice This parameter controls the driver's use of
- idle CPU time, at the expense of some speed.
-
- If this driver is built into the kernel, you can use the
- following command line parameters, with the same values
- as the corresponding module parameters listed above:
-
- pf.drive0
- pf.drive1
- pf.drive2
- pf.drive3
- pf.cluster
- pf.nice
-
- In addition, you can use the parameter pf.disable to disable
- the driver entirely.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.03 Changes for SMP. Eliminate sti().
- Fix for drives that don't clear STAT_ERR
- until after next CDB delivered.
- Small change in pf_completion to round
- up transfer size.
- 1.02 GRG 1998.06.16 Eliminated an Ugh
- 1.03 GRG 1998.08.16 Use HZ in loop timings, extra debugging
- 1.04 GRG 1998.09.24 Added jumbo support
-
-*/
-
-#define PF_VERSION "1.04"
-#define PF_MAJOR 47
-#define PF_NAME "pf"
-#define PF_UNITS 4
-
-#include <linux/types.h>
-
-/* Here are things one can override from the insmod command.
- Most are autoprobed by paride unless set here. Verbose is off
- by default.
-
-*/
-
-static bool verbose = 0;
-static int major = PF_MAJOR;
-static char *name = PF_NAME;
-static int cluster = 64;
-static int nice = 0;
-static int disable = 0;
-
-static int drive0[7] = { 0, 0, 0, -1, -1, -1, -1 };
-static int drive1[7] = { 0, 0, 0, -1, -1, -1, -1 };
-static int drive2[7] = { 0, 0, 0, -1, -1, -1, -1 };
-static int drive3[7] = { 0, 0, 0, -1, -1, -1, -1 };
-
-static int (*drives[4])[7] = {&drive0, &drive1, &drive2, &drive3};
-static int pf_drive_count;
-
-enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_LUN, D_DLY};
-
-/* end of parameters */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/hdreg.h>
-#include <linux/cdrom.h>
-#include <linux/spinlock.h>
-#include <linux/blk-mq.h>
-#include <linux/blkpg.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-
-static DEFINE_MUTEX(pf_mutex);
-static DEFINE_SPINLOCK(pf_spin_lock);
-
-module_param(verbose, bool, 0644);
-module_param(major, int, 0);
-module_param(name, charp, 0);
-module_param(cluster, int, 0);
-module_param(nice, int, 0);
-module_param_array(drive0, int, NULL, 0);
-module_param_array(drive1, int, NULL, 0);
-module_param_array(drive2, int, NULL, 0);
-module_param_array(drive3, int, NULL, 0);
-
-#include "paride.h"
-#include "pseudo.h"
-
-/* constants for faking geometry numbers */
-
-#define PF_FD_MAX 8192 /* use FD geometry under this size */
-#define PF_FD_HDS 2
-#define PF_FD_SPT 18
-#define PF_HD_HDS 64
-#define PF_HD_SPT 32
-
-#define PF_MAX_RETRIES 5
-#define PF_TMO 800 /* interrupt timeout in jiffies */
-#define PF_SPIN_DEL 50 /* spin delay in micro-seconds */
-
-#define PF_SPIN (1000000*PF_TMO)/(HZ*PF_SPIN_DEL)
-
-#define STAT_ERR 0x00001
-#define STAT_INDEX 0x00002
-#define STAT_ECC 0x00004
-#define STAT_DRQ 0x00008
-#define STAT_SEEK 0x00010
-#define STAT_WRERR 0x00020
-#define STAT_READY 0x00040
-#define STAT_BUSY 0x00080
-
-#define ATAPI_REQ_SENSE 0x03
-#define ATAPI_LOCK 0x1e
-#define ATAPI_DOOR 0x1b
-#define ATAPI_MODE_SENSE 0x5a
-#define ATAPI_CAPACITY 0x25
-#define ATAPI_IDENTIFY 0x12
-#define ATAPI_READ_10 0x28
-#define ATAPI_WRITE_10 0x2a
-
-static int pf_open(struct block_device *bdev, fmode_t mode);
-static blk_status_t pf_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd);
-static int pf_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg);
-static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo);
-
-static void pf_release(struct gendisk *disk, fmode_t mode);
-
-static void do_pf_read(void);
-static void do_pf_read_start(void);
-static void do_pf_write(void);
-static void do_pf_write_start(void);
-static void do_pf_read_drq(void);
-static void do_pf_write_done(void);
-
-#define PF_NM 0
-#define PF_RO 1
-#define PF_RW 2
-
-#define PF_NAMELEN 8
-
-struct pf_unit {
- struct pi_adapter pia; /* interface to paride layer */
- struct pi_adapter *pi;
- int removable; /* removable media device ? */
- int media_status; /* media present ? WP ? */
- int drive; /* drive */
- int lun;
- int access; /* count of active opens ... */
- int present; /* device present ? */
- char name[PF_NAMELEN]; /* pf0, pf1, ... */
- struct gendisk *disk;
- struct blk_mq_tag_set tag_set;
- struct list_head rq_list;
-};
-
-static struct pf_unit units[PF_UNITS];
-
-static int pf_identify(struct pf_unit *pf);
-static void pf_lock(struct pf_unit *pf, int func);
-static void pf_eject(struct pf_unit *pf);
-static unsigned int pf_check_events(struct gendisk *disk,
- unsigned int clearing);
-
-static char pf_scratch[512]; /* scratch block buffer */
-
-/* the variables below are used mainly in the I/O request engine, which
- processes only one request at a time.
-*/
-
-static int pf_retries = 0; /* i/o error retry count */
-static int pf_busy = 0; /* request being processed ? */
-static struct request *pf_req; /* current request */
-static int pf_block; /* address of next requested block */
-static int pf_count; /* number of blocks still to do */
-static int pf_run; /* sectors in current cluster */
-static int pf_cmd; /* current command READ/WRITE */
-static struct pf_unit *pf_current;/* unit of current request */
-static int pf_mask; /* stopper for pseudo-int */
-static char *pf_buf; /* buffer for request in progress */
-static void *par_drv; /* reference of parport driver */
-
-/* kernel glue structures */
-
-static const struct block_device_operations pf_fops = {
- .owner = THIS_MODULE,
- .open = pf_open,
- .release = pf_release,
- .ioctl = pf_ioctl,
- .compat_ioctl = pf_ioctl,
- .getgeo = pf_getgeo,
- .check_events = pf_check_events,
-};
-
-static const struct blk_mq_ops pf_mq_ops = {
- .queue_rq = pf_queue_rq,
-};
-
-static int pf_open(struct block_device *bdev, fmode_t mode)
-{
- struct pf_unit *pf = bdev->bd_disk->private_data;
- int ret;
-
- mutex_lock(&pf_mutex);
- pf_identify(pf);
-
- ret = -ENODEV;
- if (pf->media_status == PF_NM)
- goto out;
-
- ret = -EROFS;
- if ((pf->media_status == PF_RO) && (mode & FMODE_WRITE))
- goto out;
-
- ret = 0;
- pf->access++;
- if (pf->removable)
- pf_lock(pf, 1);
-out:
- mutex_unlock(&pf_mutex);
- return ret;
-}
-
-static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo)
-{
- struct pf_unit *pf = bdev->bd_disk->private_data;
- sector_t capacity = get_capacity(pf->disk);
-
- if (capacity < PF_FD_MAX) {
- geo->cylinders = sector_div(capacity, PF_FD_HDS * PF_FD_SPT);
- geo->heads = PF_FD_HDS;
- geo->sectors = PF_FD_SPT;
- } else {
- geo->cylinders = sector_div(capacity, PF_HD_HDS * PF_HD_SPT);
- geo->heads = PF_HD_HDS;
- geo->sectors = PF_HD_SPT;
- }
-
- return 0;
-}
-
-static int pf_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
-{
- struct pf_unit *pf = bdev->bd_disk->private_data;
-
- if (cmd != CDROMEJECT)
- return -EINVAL;
-
- if (pf->access != 1)
- return -EBUSY;
- mutex_lock(&pf_mutex);
- pf_eject(pf);
- mutex_unlock(&pf_mutex);
-
- return 0;
-}
-
-static void pf_release(struct gendisk *disk, fmode_t mode)
-{
- struct pf_unit *pf = disk->private_data;
-
- mutex_lock(&pf_mutex);
- if (pf->access <= 0) {
- mutex_unlock(&pf_mutex);
- WARN_ON(1);
- return;
- }
-
- pf->access--;
-
- if (!pf->access && pf->removable)
- pf_lock(pf, 0);
-
- mutex_unlock(&pf_mutex);
-}
-
-static unsigned int pf_check_events(struct gendisk *disk, unsigned int clearing)
-{
- return DISK_EVENT_MEDIA_CHANGE;
-}
-
-static inline int status_reg(struct pf_unit *pf)
-{
- return pi_read_regr(pf->pi, 1, 6);
-}
-
-static inline int read_reg(struct pf_unit *pf, int reg)
-{
- return pi_read_regr(pf->pi, 0, reg);
-}
-
-static inline void write_reg(struct pf_unit *pf, int reg, int val)
-{
- pi_write_regr(pf->pi, 0, reg, val);
-}
-
-static int pf_wait(struct pf_unit *pf, int go, int stop, char *fun, char *msg)
-{
- int j, r, e, s, p;
-
- j = 0;
- while ((((r = status_reg(pf)) & go) || (stop && (!(r & stop))))
- && (j++ < PF_SPIN))
- udelay(PF_SPIN_DEL);
-
- if ((r & (STAT_ERR & stop)) || (j > PF_SPIN)) {
- s = read_reg(pf, 7);
- e = read_reg(pf, 1);
- p = read_reg(pf, 2);
- if (j > PF_SPIN)
- e |= 0x100;
- if (fun)
- printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
- " loop=%d phase=%d\n",
- pf->name, fun, msg, r, s, e, j, p);
- return (e << 8) + s;
- }
- return 0;
-}
-
-static int pf_command(struct pf_unit *pf, char *cmd, int dlen, char *fun)
-{
- pi_connect(pf->pi);
-
- write_reg(pf, 6, 0xa0+0x10*pf->drive);
-
- if (pf_wait(pf, STAT_BUSY | STAT_DRQ, 0, fun, "before command")) {
- pi_disconnect(pf->pi);
- return -1;
- }
-
- write_reg(pf, 4, dlen % 256);
- write_reg(pf, 5, dlen / 256);
- write_reg(pf, 7, 0xa0); /* ATAPI packet command */
-
- if (pf_wait(pf, STAT_BUSY, STAT_DRQ, fun, "command DRQ")) {
- pi_disconnect(pf->pi);
- return -1;
- }
-
- if (read_reg(pf, 2) != 1) {
- printk("%s: %s: command phase error\n", pf->name, fun);
- pi_disconnect(pf->pi);
- return -1;
- }
-
- pi_write_block(pf->pi, cmd, 12);
-
- return 0;
-}
-
-static int pf_completion(struct pf_unit *pf, char *buf, char *fun)
-{
- int r, s, n;
-
- r = pf_wait(pf, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
- fun, "completion");
-
- if ((read_reg(pf, 2) & 2) && (read_reg(pf, 7) & STAT_DRQ)) {
- n = (((read_reg(pf, 4) + 256 * read_reg(pf, 5)) +
- 3) & 0xfffc);
- pi_read_block(pf->pi, buf, n);
- }
-
- s = pf_wait(pf, STAT_BUSY, STAT_READY | STAT_ERR, fun, "data done");
-
- pi_disconnect(pf->pi);
-
- return (r ? r : s);
-}
-
-static void pf_req_sense(struct pf_unit *pf, int quiet)
-{
- char rs_cmd[12] =
- { ATAPI_REQ_SENSE, pf->lun << 5, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
- char buf[16];
- int r;
-
- r = pf_command(pf, rs_cmd, 16, "Request sense");
- mdelay(1);
- if (!r)
- pf_completion(pf, buf, "Request sense");
-
- if ((!r) && (!quiet))
- printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
- pf->name, buf[2] & 0xf, buf[12], buf[13]);
-}
-
-static int pf_atapi(struct pf_unit *pf, char *cmd, int dlen, char *buf, char *fun)
-{
- int r;
-
- r = pf_command(pf, cmd, dlen, fun);
- mdelay(1);
- if (!r)
- r = pf_completion(pf, buf, fun);
- if (r)
- pf_req_sense(pf, !fun);
-
- return r;
-}
-
-static void pf_lock(struct pf_unit *pf, int func)
-{
- char lo_cmd[12] = { ATAPI_LOCK, pf->lun << 5, 0, 0, func, 0, 0, 0, 0, 0, 0, 0 };
-
- pf_atapi(pf, lo_cmd, 0, pf_scratch, func ? "lock" : "unlock");
-}
-
-static void pf_eject(struct pf_unit *pf)
-{
- char ej_cmd[12] = { ATAPI_DOOR, pf->lun << 5, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 };
-
- pf_lock(pf, 0);
- pf_atapi(pf, ej_cmd, 0, pf_scratch, "eject");
-}
-
-#define PF_RESET_TMO 30 /* in tenths of a second */
-
-static void pf_sleep(int cs)
-{
- schedule_timeout_interruptible(cs);
-}
-
-/* the ATAPI standard actually specifies the contents of all 7 registers
- after a reset, but the specification is ambiguous concerning the last
- two bytes, and different drives interpret the standard differently.
- */
-
-static int pf_reset(struct pf_unit *pf)
-{
- int i, k, flg;
- int expect[5] = { 1, 1, 1, 0x14, 0xeb };
-
- pi_connect(pf->pi);
- write_reg(pf, 6, 0xa0+0x10*pf->drive);
- write_reg(pf, 7, 8);
-
- pf_sleep(20 * HZ / 1000);
-
- k = 0;
- while ((k++ < PF_RESET_TMO) && (status_reg(pf) & STAT_BUSY))
- pf_sleep(HZ / 10);
-
- flg = 1;
- for (i = 0; i < 5; i++)
- flg &= (read_reg(pf, i + 1) == expect[i]);
-
- if (verbose) {
- printk("%s: Reset (%d) signature = ", pf->name, k);
- for (i = 0; i < 5; i++)
- printk("%3x", read_reg(pf, i + 1));
- if (!flg)
- printk(" (incorrect)");
- printk("\n");
- }
-
- pi_disconnect(pf->pi);
- return flg - 1;
-}
-
-static void pf_mode_sense(struct pf_unit *pf)
-{
- char ms_cmd[12] =
- { ATAPI_MODE_SENSE, pf->lun << 5, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0 };
- char buf[8];
-
- pf_atapi(pf, ms_cmd, 8, buf, "mode sense");
- pf->media_status = PF_RW;
- if (buf[3] & 0x80)
- pf->media_status = PF_RO;
-}
-
-static void xs(char *buf, char *targ, int offs, int len)
-{
- int j, k, l;
-
- j = 0;
- l = 0;
- for (k = 0; k < len; k++)
- if ((buf[k + offs] != 0x20) || (buf[k + offs] != l))
- l = targ[j++] = buf[k + offs];
- if (l == 0x20)
- j--;
- targ[j] = 0;
-}
-
-static int xl(char *buf, int offs)
-{
- int v, k;
-
- v = 0;
- for (k = 0; k < 4; k++)
- v = v * 256 + (buf[k + offs] & 0xff);
- return v;
-}
-
-static void pf_get_capacity(struct pf_unit *pf)
-{
- char rc_cmd[12] = { ATAPI_CAPACITY, pf->lun << 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- char buf[8];
- int bs;
-
- if (pf_atapi(pf, rc_cmd, 8, buf, "get capacity")) {
- pf->media_status = PF_NM;
- return;
- }
- set_capacity(pf->disk, xl(buf, 0) + 1);
- bs = xl(buf, 4);
- if (bs != 512) {
- set_capacity(pf->disk, 0);
- if (verbose)
- printk("%s: Drive %d, LUN %d,"
- " unsupported block size %d\n",
- pf->name, pf->drive, pf->lun, bs);
- }
-}
-
-static int pf_identify(struct pf_unit *pf)
-{
- int dt, s;
- char *ms[2] = { "master", "slave" };
- char mf[10], id[18];
- char id_cmd[12] =
- { ATAPI_IDENTIFY, pf->lun << 5, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
- char buf[36];
-
- s = pf_atapi(pf, id_cmd, 36, buf, "identify");
- if (s)
- return -1;
-
- dt = buf[0] & 0x1f;
- if ((dt != 0) && (dt != 7)) {
- if (verbose)
- printk("%s: Drive %d, LUN %d, unsupported type %d\n",
- pf->name, pf->drive, pf->lun, dt);
- return -1;
- }
-
- xs(buf, mf, 8, 8);
- xs(buf, id, 16, 16);
-
- pf->removable = (buf[1] & 0x80);
-
- pf_mode_sense(pf);
- pf_mode_sense(pf);
- pf_mode_sense(pf);
-
- pf_get_capacity(pf);
-
- printk("%s: %s %s, %s LUN %d, type %d",
- pf->name, mf, id, ms[pf->drive], pf->lun, dt);
- if (pf->removable)
- printk(", removable");
- if (pf->media_status == PF_NM)
- printk(", no media\n");
- else {
- if (pf->media_status == PF_RO)
- printk(", RO");
- printk(", %llu blocks\n",
- (unsigned long long)get_capacity(pf->disk));
- }
- return 0;
-}
-
-/*
- * returns 0, with id set if drive is detected, otherwise an error code.
- */
-static int pf_probe(struct pf_unit *pf)
-{
- if (pf->drive == -1) {
- for (pf->drive = 0; pf->drive <= 1; pf->drive++)
- if (!pf_reset(pf)) {
- if (pf->lun != -1)
- return pf_identify(pf);
- else
- for (pf->lun = 0; pf->lun < 8; pf->lun++)
- if (!pf_identify(pf))
- return 0;
- }
- } else {
- if (pf_reset(pf))
- return -1;
- if (pf->lun != -1)
- return pf_identify(pf);
- for (pf->lun = 0; pf->lun < 8; pf->lun++)
- if (!pf_identify(pf))
- return 0;
- }
- return -ENODEV;
-}
-
-/* The i/o request engine */
-
-static int pf_start(struct pf_unit *pf, int cmd, int b, int c)
-{
- int i;
- char io_cmd[12] = { cmd, pf->lun << 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- for (i = 0; i < 4; i++) {
- io_cmd[5 - i] = b & 0xff;
- b = b >> 8;
- }
-
- io_cmd[8] = c & 0xff;
- io_cmd[7] = (c >> 8) & 0xff;
-
- i = pf_command(pf, io_cmd, c * 512, "start i/o");
-
- mdelay(1);
-
- return i;
-}
-
-static int pf_ready(void)
-{
- return (((status_reg(pf_current) & (STAT_BUSY | pf_mask)) == pf_mask));
-}
-
-static int pf_queue;
-
-static int set_next_request(void)
-{
- struct pf_unit *pf;
- int old_pos = pf_queue;
-
- do {
- pf = &units[pf_queue];
- if (++pf_queue == PF_UNITS)
- pf_queue = 0;
- if (pf->present && !list_empty(&pf->rq_list)) {
- pf_req = list_first_entry(&pf->rq_list, struct request,
- queuelist);
- list_del_init(&pf_req->queuelist);
- blk_mq_start_request(pf_req);
- break;
- }
- } while (pf_queue != old_pos);
-
- return pf_req != NULL;
-}
-
-static void pf_end_request(blk_status_t err)
-{
- if (!pf_req)
- return;
- if (!blk_update_request(pf_req, err, blk_rq_cur_bytes(pf_req))) {
- __blk_mq_end_request(pf_req, err);
- pf_req = NULL;
- }
-}
-
-static void pf_request(void)
-{
- if (pf_busy)
- return;
-repeat:
- if (!pf_req && !set_next_request())
- return;
-
- pf_current = pf_req->q->disk->private_data;
- pf_block = blk_rq_pos(pf_req);
- pf_run = blk_rq_sectors(pf_req);
- pf_count = blk_rq_cur_sectors(pf_req);
-
- if (pf_block + pf_count > get_capacity(pf_req->q->disk)) {
- pf_end_request(BLK_STS_IOERR);
- goto repeat;
- }
-
- pf_cmd = rq_data_dir(pf_req);
- pf_buf = bio_data(pf_req->bio);
- pf_retries = 0;
-
- pf_busy = 1;
- if (pf_cmd == READ)
- pi_do_claimed(pf_current->pi, do_pf_read);
- else if (pf_cmd == WRITE)
- pi_do_claimed(pf_current->pi, do_pf_write);
- else {
- pf_busy = 0;
- pf_end_request(BLK_STS_IOERR);
- goto repeat;
- }
-}
-
-static blk_status_t pf_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd)
-{
- struct pf_unit *pf = hctx->queue->queuedata;
-
- spin_lock_irq(&pf_spin_lock);
- list_add_tail(&bd->rq->queuelist, &pf->rq_list);
- pf_request();
- spin_unlock_irq(&pf_spin_lock);
-
- return BLK_STS_OK;
-}
-
-static int pf_next_buf(void)
-{
- unsigned long saved_flags;
-
- pf_count--;
- pf_run--;
- pf_buf += 512;
- pf_block++;
- if (!pf_run)
- return 1;
- if (!pf_count) {
- spin_lock_irqsave(&pf_spin_lock, saved_flags);
- pf_end_request(0);
- spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
- if (!pf_req)
- return 1;
- pf_count = blk_rq_cur_sectors(pf_req);
- pf_buf = bio_data(pf_req->bio);
- }
- return 0;
-}
-
-static inline void next_request(blk_status_t err)
-{
- unsigned long saved_flags;
-
- spin_lock_irqsave(&pf_spin_lock, saved_flags);
- pf_end_request(err);
- pf_busy = 0;
- pf_request();
- spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
-}
-
-/* detach from the calling context - in case the spinlock is held */
-static void do_pf_read(void)
-{
- ps_set_intr(do_pf_read_start, NULL, 0, nice);
-}
-
-static void do_pf_read_start(void)
-{
- pf_busy = 1;
-
- if (pf_start(pf_current, ATAPI_READ_10, pf_block, pf_run)) {
- pi_disconnect(pf_current->pi);
- if (pf_retries < PF_MAX_RETRIES) {
- pf_retries++;
- pi_do_claimed(pf_current->pi, do_pf_read_start);
- return;
- }
- next_request(BLK_STS_IOERR);
- return;
- }
- pf_mask = STAT_DRQ;
- ps_set_intr(do_pf_read_drq, pf_ready, PF_TMO, nice);
-}
-
-static void do_pf_read_drq(void)
-{
- while (1) {
- if (pf_wait(pf_current, STAT_BUSY, STAT_DRQ | STAT_ERR,
- "read block", "completion") & STAT_ERR) {
- pi_disconnect(pf_current->pi);
- if (pf_retries < PF_MAX_RETRIES) {
- pf_req_sense(pf_current, 0);
- pf_retries++;
- pi_do_claimed(pf_current->pi, do_pf_read_start);
- return;
- }
- next_request(BLK_STS_IOERR);
- return;
- }
- pi_read_block(pf_current->pi, pf_buf, 512);
- if (pf_next_buf())
- break;
- }
- pi_disconnect(pf_current->pi);
- next_request(0);
-}
-
-static void do_pf_write(void)
-{
- ps_set_intr(do_pf_write_start, NULL, 0, nice);
-}
-
-static void do_pf_write_start(void)
-{
- pf_busy = 1;
-
- if (pf_start(pf_current, ATAPI_WRITE_10, pf_block, pf_run)) {
- pi_disconnect(pf_current->pi);
- if (pf_retries < PF_MAX_RETRIES) {
- pf_retries++;
- pi_do_claimed(pf_current->pi, do_pf_write_start);
- return;
- }
- next_request(BLK_STS_IOERR);
- return;
- }
-
- while (1) {
- if (pf_wait(pf_current, STAT_BUSY, STAT_DRQ | STAT_ERR,
- "write block", "data wait") & STAT_ERR) {
- pi_disconnect(pf_current->pi);
- if (pf_retries < PF_MAX_RETRIES) {
- pf_retries++;
- pi_do_claimed(pf_current->pi, do_pf_write_start);
- return;
- }
- next_request(BLK_STS_IOERR);
- return;
- }
- pi_write_block(pf_current->pi, pf_buf, 512);
- if (pf_next_buf())
- break;
- }
- pf_mask = 0;
- ps_set_intr(do_pf_write_done, pf_ready, PF_TMO, nice);
-}
-
-static void do_pf_write_done(void)
-{
- if (pf_wait(pf_current, STAT_BUSY, 0, "write block", "done") & STAT_ERR) {
- pi_disconnect(pf_current->pi);
- if (pf_retries < PF_MAX_RETRIES) {
- pf_retries++;
- pi_do_claimed(pf_current->pi, do_pf_write_start);
- return;
- }
- next_request(BLK_STS_IOERR);
- return;
- }
- pi_disconnect(pf_current->pi);
- next_request(0);
-}
-
-static int __init pf_init_unit(struct pf_unit *pf, bool autoprobe, int port,
- int mode, int unit, int protocol, int delay, int ms)
-{
- struct gendisk *disk;
- int ret;
-
- ret = blk_mq_alloc_sq_tag_set(&pf->tag_set, &pf_mq_ops, 1,
- BLK_MQ_F_SHOULD_MERGE);
- if (ret)
- return ret;
-
- disk = blk_mq_alloc_disk(&pf->tag_set, pf);
- if (IS_ERR(disk)) {
- ret = PTR_ERR(disk);
- goto out_free_tag_set;
- }
- disk->major = major;
- disk->first_minor = pf - units;
- disk->minors = 1;
- strcpy(disk->disk_name, pf->name);
- disk->fops = &pf_fops;
- disk->flags |= GENHD_FL_NO_PART;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
- disk->private_data = pf;
-
- blk_queue_max_segments(disk->queue, cluster);
- blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH);
-
- INIT_LIST_HEAD(&pf->rq_list);
- pf->disk = disk;
- pf->pi = &pf->pia;
- pf->media_status = PF_NM;
- pf->drive = (*drives[disk->first_minor])[D_SLV];
- pf->lun = (*drives[disk->first_minor])[D_LUN];
- snprintf(pf->name, PF_NAMELEN, "%s%d", name, disk->first_minor);
-
- if (!pi_init(pf->pi, autoprobe, port, mode, unit, protocol, delay,
- pf_scratch, PI_PF, verbose, pf->name)) {
- ret = -ENODEV;
- goto out_free_disk;
- }
- ret = pf_probe(pf);
- if (ret)
- goto out_pi_release;
-
- ret = add_disk(disk);
- if (ret)
- goto out_pi_release;
- pf->present = 1;
- return 0;
-
-out_pi_release:
- pi_release(pf->pi);
-out_free_disk:
- put_disk(pf->disk);
-out_free_tag_set:
- blk_mq_free_tag_set(&pf->tag_set);
- return ret;
-}
-
-static int __init pf_init(void)
-{ /* preliminary initialisation */
- struct pf_unit *pf;
- int found = 0, unit;
-
- if (disable)
- return -EINVAL;
-
- if (register_blkdev(major, name))
- return -EBUSY;
-
- printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
- name, name, PF_VERSION, major, cluster, nice);
-
- par_drv = pi_register_driver(name);
- if (!par_drv) {
- pr_err("failed to register %s driver\n", name);
- goto out_unregister_blkdev;
- }
-
- for (unit = 0; unit < PF_UNITS; unit++) {
- if (!(*drives[unit])[D_PRT])
- pf_drive_count++;
- }
-
- pf = units;
- if (pf_drive_count == 0) {
- if (pf_init_unit(pf, 1, -1, -1, -1, -1, -1, verbose))
- found++;
- } else {
- for (unit = 0; unit < PF_UNITS; unit++, pf++) {
- int *conf = *drives[unit];
- if (!conf[D_PRT])
- continue;
- if (pf_init_unit(pf, 0, conf[D_PRT], conf[D_MOD],
- conf[D_UNI], conf[D_PRO], conf[D_DLY],
- verbose))
- found++;
- }
- }
- if (!found) {
- printk("%s: No ATAPI disk detected\n", name);
- goto out_unregister_pi_driver;
- }
- pf_busy = 0;
- return 0;
-
-out_unregister_pi_driver:
- pi_unregister_driver(par_drv);
-out_unregister_blkdev:
- unregister_blkdev(major, name);
- return -ENODEV;
-}
-
-static void __exit pf_exit(void)
-{
- struct pf_unit *pf;
- int unit;
-
- for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
- if (!pf->present)
- continue;
- del_gendisk(pf->disk);
- put_disk(pf->disk);
- blk_mq_free_tag_set(&pf->tag_set);
- pi_release(pf->pi);
- }
-
- unregister_blkdev(major, name);
-}
-
-MODULE_LICENSE("GPL");
-module_init(pf_init)
-module_exit(pf_exit)
diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c
deleted file mode 100644
index 3b5882bfb736..000000000000
--- a/drivers/block/paride/pg.c
+++ /dev/null
@@ -1,734 +0,0 @@
-/*
- pg.c (c) 1998 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- The pg driver provides a simple character device interface for
- sending ATAPI commands to a device. With the exception of the
- ATAPI reset operation, all operations are performed by a pair
- of read and write operations to the appropriate /dev/pgN device.
- A write operation delivers a command and any outbound data in
- a single buffer. Normally, the write will succeed unless the
- device is offline or malfunctioning, or there is already another
- command pending. If the write succeeds, it should be followed
- immediately by a read operation, to obtain any returned data and
- status information. A read will fail if there is no operation
- in progress.
-
- As a special case, the device can be reset with a write operation,
- and in this case, no following read is expected, or permitted.
-
- There are no ioctl() operations. Any single operation
- may transfer at most PG_MAX_DATA bytes. Note that the driver must
- copy the data through an internal buffer. In keeping with all
- current ATAPI devices, command packets are assumed to be exactly
- 12 bytes in length.
-
- To permit future changes to this interface, the headers in the
- read and write buffers contain a single character "magic" flag.
- Currently this flag must be the character "P".
-
- By default, the driver will autoprobe for a single parallel
- port ATAPI device, but if their individual parameters are
- specified, the driver can handle up to 4 devices.
-
- To use this device, you must have the following device
- special files defined:
-
- /dev/pg0 c 97 0
- /dev/pg1 c 97 1
- /dev/pg2 c 97 2
- /dev/pg3 c 97 3
-
- (You'll need to change the 97 to something else if you use
- the 'major' parameter to install the driver on a different
- major number.)
-
- The behaviour of the pg driver can be altered by setting
- some parameters from the insmod command line. The following
- parameters are adjustable:
-
- drive0 These four arguments can be arrays of
- drive1 1-6 integers as follows:
- drive2
- drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
-
- Where,
-
- <prt> is the base of the parallel port address for
- the corresponding drive. (required)
-
- <pro> is the protocol number for the adapter that
- supports this drive. These numbers are
- logged by 'paride' when the protocol modules
- are initialised. (0 if not given)
-
- <uni> for those adapters that support chained
- devices, this is the unit selector for the
- chain of devices on the given port. It should
- be zero for devices that don't support chaining.
- (0 if not given)
-
- <mod> this can be -1 to choose the best mode, or one
- of the mode numbers supported by the adapter.
- (-1 if not given)
-
- <slv> ATAPI devices can be jumpered to master or slave.
- Set this to 0 to choose the master drive, 1 to
- choose the slave, -1 (the default) to choose the
- first drive found.
-
- <dly> some parallel ports require the driver to
- go more slowly. -1 sets a default value that
- should work with the chosen protocol. Otherwise,
- set this to a small integer, the larger it is
- the slower the port i/o. In some cases, setting
- this to zero will speed up the device. (default -1)
-
- major You may use this parameter to override the
- default major number (97) that this driver
- will use. Be sure to change the device
- name as well.
-
- name This parameter is a character string that
- contains the name the kernel will use for this
- device (in /proc output, for instance).
- (default "pg").
-
- verbose This parameter controls the amount of logging
- that is done by the driver. Set it to 0 for
- quiet operation, to 1 to enable progress
- messages while the driver probes for devices,
- or to 2 for full debug logging. (default 0)
-
- If this driver is built into the kernel, you can use
- the following command line parameters, with the same values
- as the corresponding module parameters listed above:
-
- pg.drive0
- pg.drive1
- pg.drive2
- pg.drive3
-
- In addition, you can use the parameter pg.disable to disable
- the driver entirely.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.06.16 Bug fixes
- 1.02 GRG 1998.09.24 Added jumbo support
-
-*/
-
-#define PG_VERSION "1.02"
-#define PG_MAJOR 97
-#define PG_NAME "pg"
-#define PG_UNITS 4
-
-#ifndef PI_PG
-#define PI_PG 4
-#endif
-
-#include <linux/types.h>
-/* Here are things one can override from the insmod command.
- Most are autoprobed by paride unless set here. Verbose is 0
- by default.
-
-*/
-
-static int verbose;
-static int major = PG_MAJOR;
-static char *name = PG_NAME;
-static int disable = 0;
-
-static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
-
-static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
-static int pg_drive_count;
-
-enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
-
-/* end of parameters */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/mtio.h>
-#include <linux/pg.h>
-#include <linux/device.h>
-#include <linux/sched.h> /* current, TASK_* */
-#include <linux/mutex.h>
-#include <linux/jiffies.h>
-
-#include <linux/uaccess.h>
-
-module_param(verbose, int, 0644);
-module_param(major, int, 0);
-module_param(name, charp, 0);
-module_param_array(drive0, int, NULL, 0);
-module_param_array(drive1, int, NULL, 0);
-module_param_array(drive2, int, NULL, 0);
-module_param_array(drive3, int, NULL, 0);
-
-#include "paride.h"
-
-#define PG_SPIN_DEL 50 /* spin delay in micro-seconds */
-#define PG_SPIN 200
-#define PG_TMO HZ
-#define PG_RESET_TMO 10*HZ
-
-#define STAT_ERR 0x01
-#define STAT_INDEX 0x02
-#define STAT_ECC 0x04
-#define STAT_DRQ 0x08
-#define STAT_SEEK 0x10
-#define STAT_WRERR 0x20
-#define STAT_READY 0x40
-#define STAT_BUSY 0x80
-
-#define ATAPI_IDENTIFY 0x12
-
-static DEFINE_MUTEX(pg_mutex);
-static int pg_open(struct inode *inode, struct file *file);
-static int pg_release(struct inode *inode, struct file *file);
-static ssize_t pg_read(struct file *filp, char __user *buf,
- size_t count, loff_t * ppos);
-static ssize_t pg_write(struct file *filp, const char __user *buf,
- size_t count, loff_t * ppos);
-static int pg_detect(void);
-
-#define PG_NAMELEN 8
-
-struct pg {
- struct pi_adapter pia; /* interface to paride layer */
- struct pi_adapter *pi;
- int busy; /* write done, read expected */
- int start; /* jiffies at command start */
- int dlen; /* transfer size requested */
- unsigned long timeout; /* timeout requested */
- int status; /* last sense key */
- int drive; /* drive */
- unsigned long access; /* count of active opens ... */
- int present; /* device present ? */
- char *bufptr;
- char name[PG_NAMELEN]; /* pg0, pg1, ... */
-};
-
-static struct pg devices[PG_UNITS];
-
-static int pg_identify(struct pg *dev, int log);
-
-static char pg_scratch[512]; /* scratch block buffer */
-
-static struct class *pg_class;
-static void *par_drv; /* reference of parport driver */
-
-/* kernel glue structures */
-
-static const struct file_operations pg_fops = {
- .owner = THIS_MODULE,
- .read = pg_read,
- .write = pg_write,
- .open = pg_open,
- .release = pg_release,
- .llseek = noop_llseek,
-};
-
-static void pg_init_units(void)
-{
- int unit;
-
- pg_drive_count = 0;
- for (unit = 0; unit < PG_UNITS; unit++) {
- int *parm = *drives[unit];
- struct pg *dev = &devices[unit];
- dev->pi = &dev->pia;
- clear_bit(0, &dev->access);
- dev->busy = 0;
- dev->present = 0;
- dev->bufptr = NULL;
- dev->drive = parm[D_SLV];
- snprintf(dev->name, PG_NAMELEN, "%s%c", name, 'a'+unit);
- if (parm[D_PRT])
- pg_drive_count++;
- }
-}
-
-static inline int status_reg(struct pg *dev)
-{
- return pi_read_regr(dev->pi, 1, 6);
-}
-
-static inline int read_reg(struct pg *dev, int reg)
-{
- return pi_read_regr(dev->pi, 0, reg);
-}
-
-static inline void write_reg(struct pg *dev, int reg, int val)
-{
- pi_write_regr(dev->pi, 0, reg, val);
-}
-
-static inline u8 DRIVE(struct pg *dev)
-{
- return 0xa0+0x10*dev->drive;
-}
-
-static void pg_sleep(int cs)
-{
- schedule_timeout_interruptible(cs);
-}
-
-static int pg_wait(struct pg *dev, int go, int stop, unsigned long tmo, char *msg)
-{
- int j, r, e, s, p, to;
-
- dev->status = 0;
-
- j = 0;
- while ((((r = status_reg(dev)) & go) || (stop && (!(r & stop))))
- && time_before(jiffies, tmo)) {
- if (j++ < PG_SPIN)
- udelay(PG_SPIN_DEL);
- else
- pg_sleep(1);
- }
-
- to = time_after_eq(jiffies, tmo);
-
- if ((r & (STAT_ERR & stop)) || to) {
- s = read_reg(dev, 7);
- e = read_reg(dev, 1);
- p = read_reg(dev, 2);
- if (verbose > 1)
- printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n",
- dev->name, msg, s, e, p, to ? " timeout" : "");
- if (to)
- e |= 0x100;
- dev->status = (e >> 4) & 0xff;
- return -1;
- }
- return 0;
-}
-
-static int pg_command(struct pg *dev, char *cmd, int dlen, unsigned long tmo)
-{
- int k;
-
- pi_connect(dev->pi);
-
- write_reg(dev, 6, DRIVE(dev));
-
- if (pg_wait(dev, STAT_BUSY | STAT_DRQ, 0, tmo, "before command"))
- goto fail;
-
- write_reg(dev, 4, dlen % 256);
- write_reg(dev, 5, dlen / 256);
- write_reg(dev, 7, 0xa0); /* ATAPI packet command */
-
- if (pg_wait(dev, STAT_BUSY, STAT_DRQ, tmo, "command DRQ"))
- goto fail;
-
- if (read_reg(dev, 2) != 1) {
- printk("%s: command phase error\n", dev->name);
- goto fail;
- }
-
- pi_write_block(dev->pi, cmd, 12);
-
- if (verbose > 1) {
- printk("%s: Command sent, dlen=%d packet= ", dev->name, dlen);
- for (k = 0; k < 12; k++)
- printk("%02x ", cmd[k] & 0xff);
- printk("\n");
- }
- return 0;
-fail:
- pi_disconnect(dev->pi);
- return -1;
-}
-
-static int pg_completion(struct pg *dev, char *buf, unsigned long tmo)
-{
- int r, d, n, p;
-
- r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
- tmo, "completion");
-
- dev->dlen = 0;
-
- while (read_reg(dev, 7) & STAT_DRQ) {
- d = (read_reg(dev, 4) + 256 * read_reg(dev, 5));
- n = ((d + 3) & 0xfffc);
- p = read_reg(dev, 2) & 3;
- if (p == 0)
- pi_write_block(dev->pi, buf, n);
- if (p == 2)
- pi_read_block(dev->pi, buf, n);
- if (verbose > 1)
- printk("%s: %s %d bytes\n", dev->name,
- p ? "Read" : "Write", n);
- dev->dlen += (1 - p) * d;
- buf += d;
- r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
- tmo, "completion");
- }
-
- pi_disconnect(dev->pi);
-
- return r;
-}
-
-static int pg_reset(struct pg *dev)
-{
- int i, k, err;
- int expect[5] = { 1, 1, 1, 0x14, 0xeb };
- int got[5];
-
- pi_connect(dev->pi);
- write_reg(dev, 6, DRIVE(dev));
- write_reg(dev, 7, 8);
-
- pg_sleep(20 * HZ / 1000);
-
- k = 0;
- while ((k++ < PG_RESET_TMO) && (status_reg(dev) & STAT_BUSY))
- pg_sleep(1);
-
- for (i = 0; i < 5; i++)
- got[i] = read_reg(dev, i + 1);
-
- err = memcmp(expect, got, sizeof(got)) ? -1 : 0;
-
- if (verbose) {
- printk("%s: Reset (%d) signature = ", dev->name, k);
- for (i = 0; i < 5; i++)
- printk("%3x", got[i]);
- if (err)
- printk(" (incorrect)");
- printk("\n");
- }
-
- pi_disconnect(dev->pi);
- return err;
-}
-
-static void xs(char *buf, char *targ, int len)
-{
- char l = '\0';
- int k;
-
- for (k = 0; k < len; k++) {
- char c = *buf++;
- if (c != ' ' && c != l)
- l = *targ++ = c;
- }
- if (l == ' ')
- targ--;
- *targ = '\0';
-}
-
-static int pg_identify(struct pg *dev, int log)
-{
- int s;
- char *ms[2] = { "master", "slave" };
- char mf[10], id[18];
- char id_cmd[12] = { ATAPI_IDENTIFY, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
- char buf[36];
-
- s = pg_command(dev, id_cmd, 36, jiffies + PG_TMO);
- if (s)
- return -1;
- s = pg_completion(dev, buf, jiffies + PG_TMO);
- if (s)
- return -1;
-
- if (log) {
- xs(buf + 8, mf, 8);
- xs(buf + 16, id, 16);
- printk("%s: %s %s, %s\n", dev->name, mf, id, ms[dev->drive]);
- }
-
- return 0;
-}
-
-/*
- * returns 0, with id set if drive is detected
- * -1, if drive detection failed
- */
-static int pg_probe(struct pg *dev)
-{
- if (dev->drive == -1) {
- for (dev->drive = 0; dev->drive <= 1; dev->drive++)
- if (!pg_reset(dev))
- return pg_identify(dev, 1);
- } else {
- if (!pg_reset(dev))
- return pg_identify(dev, 1);
- }
- return -1;
-}
-
-static int pg_detect(void)
-{
- struct pg *dev = &devices[0];
- int k, unit;
-
- printk("%s: %s version %s, major %d\n", name, name, PG_VERSION, major);
-
- par_drv = pi_register_driver(name);
- if (!par_drv) {
- pr_err("failed to register %s driver\n", name);
- return -1;
- }
-
- k = 0;
- if (pg_drive_count == 0) {
- if (pi_init(dev->pi, 1, -1, -1, -1, -1, -1, pg_scratch,
- PI_PG, verbose, dev->name)) {
- if (!pg_probe(dev)) {
- dev->present = 1;
- k++;
- } else
- pi_release(dev->pi);
- }
-
- } else
- for (unit = 0; unit < PG_UNITS; unit++, dev++) {
- int *parm = *drives[unit];
- if (!parm[D_PRT])
- continue;
- if (pi_init(dev->pi, 0, parm[D_PRT], parm[D_MOD],
- parm[D_UNI], parm[D_PRO], parm[D_DLY],
- pg_scratch, PI_PG, verbose, dev->name)) {
- if (!pg_probe(dev)) {
- dev->present = 1;
- k++;
- } else
- pi_release(dev->pi);
- }
- }
-
- if (k)
- return 0;
-
- pi_unregister_driver(par_drv);
- printk("%s: No ATAPI device detected\n", name);
- return -1;
-}
-
-static int pg_open(struct inode *inode, struct file *file)
-{
- int unit = iminor(inode) & 0x7f;
- struct pg *dev = &devices[unit];
- int ret = 0;
-
- mutex_lock(&pg_mutex);
- if ((unit >= PG_UNITS) || (!dev->present)) {
- ret = -ENODEV;
- goto out;
- }
-
- if (test_and_set_bit(0, &dev->access)) {
- ret = -EBUSY;
- goto out;
- }
-
- if (dev->busy) {
- pg_reset(dev);
- dev->busy = 0;
- }
-
- pg_identify(dev, (verbose > 1));
-
- dev->bufptr = kmalloc(PG_MAX_DATA, GFP_KERNEL);
- if (dev->bufptr == NULL) {
- clear_bit(0, &dev->access);
- printk("%s: buffer allocation failed\n", dev->name);
- ret = -ENOMEM;
- goto out;
- }
-
- file->private_data = dev;
-
-out:
- mutex_unlock(&pg_mutex);
- return ret;
-}
-
-static int pg_release(struct inode *inode, struct file *file)
-{
- struct pg *dev = file->private_data;
-
- kfree(dev->bufptr);
- dev->bufptr = NULL;
- clear_bit(0, &dev->access);
-
- return 0;
-}
-
-static ssize_t pg_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
-{
- struct pg *dev = filp->private_data;
- struct pg_write_hdr hdr;
- int hs = sizeof (hdr);
-
- if (dev->busy)
- return -EBUSY;
- if (count < hs)
- return -EINVAL;
-
- if (copy_from_user(&hdr, buf, hs))
- return -EFAULT;
-
- if (hdr.magic != PG_MAGIC)
- return -EINVAL;
- if (hdr.dlen < 0 || hdr.dlen > PG_MAX_DATA)
- return -EINVAL;
- if ((count - hs) > PG_MAX_DATA)
- return -EINVAL;
-
- if (hdr.func == PG_RESET) {
- if (count != hs)
- return -EINVAL;
- if (pg_reset(dev))
- return -EIO;
- return count;
- }
-
- if (hdr.func != PG_COMMAND)
- return -EINVAL;
-
- dev->start = jiffies;
- dev->timeout = hdr.timeout * HZ + HZ / 2 + jiffies;
-
- if (pg_command(dev, hdr.packet, hdr.dlen, jiffies + PG_TMO)) {
- if (dev->status & 0x10)
- return -ETIME;
- return -EIO;
- }
-
- dev->busy = 1;
-
- if (copy_from_user(dev->bufptr, buf + hs, count - hs))
- return -EFAULT;
- return count;
-}
-
-static ssize_t pg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
-{
- struct pg *dev = filp->private_data;
- struct pg_read_hdr hdr;
- int hs = sizeof (hdr);
- int copy;
-
- if (!dev->busy)
- return -EINVAL;
- if (count < hs)
- return -EINVAL;
-
- dev->busy = 0;
-
- if (pg_completion(dev, dev->bufptr, dev->timeout))
- if (dev->status & 0x10)
- return -ETIME;
-
- memset(&hdr, 0, sizeof(hdr));
- hdr.magic = PG_MAGIC;
- hdr.dlen = dev->dlen;
- copy = 0;
-
- if (hdr.dlen < 0) {
- hdr.dlen = -1 * hdr.dlen;
- copy = hdr.dlen;
- if (copy > (count - hs))
- copy = count - hs;
- }
-
- hdr.duration = (jiffies - dev->start + HZ / 2) / HZ;
- hdr.scsi = dev->status & 0x0f;
-
- if (copy_to_user(buf, &hdr, hs))
- return -EFAULT;
- if (copy > 0)
- if (copy_to_user(buf + hs, dev->bufptr, copy))
- return -EFAULT;
- return copy + hs;
-}
-
-static int __init pg_init(void)
-{
- int unit;
- int err;
-
- if (disable){
- err = -EINVAL;
- goto out;
- }
-
- pg_init_units();
-
- if (pg_detect()) {
- err = -ENODEV;
- goto out;
- }
-
- err = register_chrdev(major, name, &pg_fops);
- if (err < 0) {
- printk("pg_init: unable to get major number %d\n", major);
- for (unit = 0; unit < PG_UNITS; unit++) {
- struct pg *dev = &devices[unit];
- if (dev->present)
- pi_release(dev->pi);
- }
- goto out;
- }
- major = err; /* In case the user specified `major=0' (dynamic) */
- pg_class = class_create(THIS_MODULE, "pg");
- if (IS_ERR(pg_class)) {
- err = PTR_ERR(pg_class);
- goto out_chrdev;
- }
- for (unit = 0; unit < PG_UNITS; unit++) {
- struct pg *dev = &devices[unit];
- if (dev->present)
- device_create(pg_class, NULL, MKDEV(major, unit), NULL,
- "pg%u", unit);
- }
- err = 0;
- goto out;
-
-out_chrdev:
- unregister_chrdev(major, "pg");
-out:
- return err;
-}
-
-static void __exit pg_exit(void)
-{
- int unit;
-
- for (unit = 0; unit < PG_UNITS; unit++) {
- struct pg *dev = &devices[unit];
- if (dev->present)
- device_destroy(pg_class, MKDEV(major, unit));
- }
- class_destroy(pg_class);
- unregister_chrdev(major, name);
-
- for (unit = 0; unit < PG_UNITS; unit++) {
- struct pg *dev = &devices[unit];
- if (dev->present)
- pi_release(dev->pi);
- }
-}
-
-MODULE_LICENSE("GPL");
-module_init(pg_init)
-module_exit(pg_exit)
diff --git a/drivers/block/paride/ppc6lnx.c b/drivers/block/paride/ppc6lnx.c
deleted file mode 100644
index 5e5521d3b1dd..000000000000
--- a/drivers/block/paride/ppc6lnx.c
+++ /dev/null
@@ -1,726 +0,0 @@
-/*
- ppc6lnx.c (c) 2001 Micro Solutions Inc.
- Released under the terms of the GNU General Public license
-
- ppc6lnx.c is a par of the protocol driver for the Micro Solutions
- "BACKPACK" parallel port IDE adapter
- (Works on Series 6 drives)
-
-*/
-
-//***************************************************************************
-
-// PPC 6 Code in C sanitized for LINUX
-// Original x86 ASM by Ron, Converted to C by Clive
-
-//***************************************************************************
-
-
-#define port_stb 1
-#define port_afd 2
-#define cmd_stb port_afd
-#define port_init 4
-#define data_stb port_init
-#define port_sel 8
-#define port_int 16
-#define port_dir 0x20
-
-#define ECR_EPP 0x80
-#define ECR_BI 0x20
-
-//***************************************************************************
-
-// 60772 Commands
-
-#define ACCESS_REG 0x00
-#define ACCESS_PORT 0x40
-
-#define ACCESS_READ 0x00
-#define ACCESS_WRITE 0x20
-
-// 60772 Command Prefix
-
-#define CMD_PREFIX_SET 0xe0 // Special command that modifies the next command's operation
-#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits
- #define PREFIX_IO16 0x01 // perform 16-bit wide I/O
- #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write
- #define PREFIX_BLK 0x08 // enable block transfer mode
-
-// 60772 Registers
-
-#define REG_STATUS 0x00 // status register
- #define STATUS_IRQA 0x01 // Peripheral IRQA line
- #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit
-#define REG_VERSION 0x01 // PPC version register (read)
-#define REG_HWCFG 0x02 // Hardware Config register
-#define REG_RAMSIZE 0x03 // Size of RAM Buffer
- #define RAMSIZE_128K 0x02
-#define REG_EEPROM 0x06 // EEPROM control register
- #define EEPROM_SK 0x01 // eeprom SK bit
- #define EEPROM_DI 0x02 // eeprom DI bit
- #define EEPROM_CS 0x04 // eeprom CS bit
- #define EEPROM_EN 0x08 // eeprom output enable
-#define REG_BLKSIZE 0x08 // Block transfer len (24 bit)
-
-//***************************************************************************
-
-typedef struct ppc_storage {
- u16 lpt_addr; // LPT base address
- u8 ppc_id;
- u8 mode; // operating mode
- // 0 = PPC Uni SW
- // 1 = PPC Uni FW
- // 2 = PPC Bi SW
- // 3 = PPC Bi FW
- // 4 = EPP Byte
- // 5 = EPP Word
- // 6 = EPP Dword
- u8 ppc_flags;
- u8 org_data; // original LPT data port contents
- u8 org_ctrl; // original LPT control port contents
- u8 cur_ctrl; // current control port contents
-} Interface;
-
-//***************************************************************************
-
-// ppc_flags
-
-#define fifo_wait 0x10
-
-//***************************************************************************
-
-// DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES
-
-#define PPCMODE_UNI_SW 0
-#define PPCMODE_UNI_FW 1
-#define PPCMODE_BI_SW 2
-#define PPCMODE_BI_FW 3
-#define PPCMODE_EPP_BYTE 4
-#define PPCMODE_EPP_WORD 5
-#define PPCMODE_EPP_DWORD 6
-
-//***************************************************************************
-
-static int ppc6_select(Interface *ppc);
-static void ppc6_deselect(Interface *ppc);
-static void ppc6_send_cmd(Interface *ppc, u8 cmd);
-static void ppc6_wr_data_byte(Interface *ppc, u8 data);
-static u8 ppc6_rd_data_byte(Interface *ppc);
-static u8 ppc6_rd_port(Interface *ppc, u8 port);
-static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
-static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
-static void ppc6_wait_for_fifo(Interface *ppc);
-static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
-static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
-static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
-static void ppc6_wr_extout(Interface *ppc, u8 regdata);
-static int ppc6_open(Interface *ppc);
-static void ppc6_close(Interface *ppc);
-
-//***************************************************************************
-
-static int ppc6_select(Interface *ppc)
-{
- u8 i, j, k;
-
- i = inb(ppc->lpt_addr + 1);
-
- if (i & 1)
- outb(i, ppc->lpt_addr + 1);
-
- ppc->org_data = inb(ppc->lpt_addr);
-
- ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl
-
- ppc->cur_ctrl = ppc->org_ctrl;
-
- ppc->cur_ctrl |= port_sel;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- if (ppc->org_data == 'b')
- outb('x', ppc->lpt_addr);
-
- outb('b', ppc->lpt_addr);
- outb('p', ppc->lpt_addr);
- outb(ppc->ppc_id, ppc->lpt_addr);
- outb(~ppc->ppc_id,ppc->lpt_addr);
-
- ppc->cur_ctrl &= ~port_sel;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- i = ppc->mode & 0x0C;
-
- if (i == 0)
- i = (ppc->mode & 2) | 1;
-
- outb(i, ppc->lpt_addr);
-
- ppc->cur_ctrl |= port_sel;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- // DELAY
-
- ppc->cur_ctrl |= port_afd;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
-
- k = inb(ppc->lpt_addr + 1) & 0xB8;
-
- if (j == k)
- {
- ppc->cur_ctrl &= ~port_afd;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8;
-
- if (j == k)
- {
- if (i & 4) // EPP
- ppc->cur_ctrl &= ~(port_sel | port_init);
- else // PPC/ECP
- ppc->cur_ctrl &= ~port_sel;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- return(1);
- }
- }
-
- outb(ppc->org_ctrl, ppc->lpt_addr + 2);
-
- outb(ppc->org_data, ppc->lpt_addr);
-
- return(0); // FAIL
-}
-
-//***************************************************************************
-
-static void ppc6_deselect(Interface *ppc)
-{
- if (ppc->mode & 4) // EPP
- ppc->cur_ctrl |= port_init;
- else // PPC/ECP
- ppc->cur_ctrl |= port_sel;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- outb(ppc->org_data, ppc->lpt_addr);
-
- outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2);
-
- outb(ppc->org_ctrl, ppc->lpt_addr + 2);
-}
-
-//***************************************************************************
-
-static void ppc6_send_cmd(Interface *ppc, u8 cmd)
-{
- switch(ppc->mode)
- {
- case PPCMODE_UNI_SW :
- case PPCMODE_UNI_FW :
- case PPCMODE_BI_SW :
- case PPCMODE_BI_FW :
- {
- outb(cmd, ppc->lpt_addr);
-
- ppc->cur_ctrl ^= cmd_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
-
- case PPCMODE_EPP_BYTE :
- case PPCMODE_EPP_WORD :
- case PPCMODE_EPP_DWORD :
- {
- outb(cmd, ppc->lpt_addr + 3);
-
- break;
- }
- }
-}
-
-//***************************************************************************
-
-static void ppc6_wr_data_byte(Interface *ppc, u8 data)
-{
- switch(ppc->mode)
- {
- case PPCMODE_UNI_SW :
- case PPCMODE_UNI_FW :
- case PPCMODE_BI_SW :
- case PPCMODE_BI_FW :
- {
- outb(data, ppc->lpt_addr);
-
- ppc->cur_ctrl ^= data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
-
- case PPCMODE_EPP_BYTE :
- case PPCMODE_EPP_WORD :
- case PPCMODE_EPP_DWORD :
- {
- outb(data, ppc->lpt_addr + 4);
-
- break;
- }
- }
-}
-
-//***************************************************************************
-
-static u8 ppc6_rd_data_byte(Interface *ppc)
-{
- u8 data = 0;
-
- switch(ppc->mode)
- {
- case PPCMODE_UNI_SW :
- case PPCMODE_UNI_FW :
- {
- ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- // DELAY
-
- data = inb(ppc->lpt_addr + 1);
-
- data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
-
- ppc->cur_ctrl |= port_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- // DELAY
-
- data |= inb(ppc->lpt_addr + 1) & 0xB8;
-
- break;
- }
-
- case PPCMODE_BI_SW :
- case PPCMODE_BI_FW :
- {
- ppc->cur_ctrl |= port_dir;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- data = inb(ppc->lpt_addr);
-
- ppc->cur_ctrl &= ~port_stb;
-
- outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
-
- ppc->cur_ctrl &= ~port_dir;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
-
- case PPCMODE_EPP_BYTE :
- case PPCMODE_EPP_WORD :
- case PPCMODE_EPP_DWORD :
- {
- outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
-
- data = inb(ppc->lpt_addr + 4);
-
- outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
-
- break;
- }
- }
-
- return(data);
-}
-
-//***************************************************************************
-
-static u8 ppc6_rd_port(Interface *ppc, u8 port)
-{
- ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ));
-
- return(ppc6_rd_data_byte(ppc));
-}
-
-//***************************************************************************
-
-static void ppc6_wr_port(Interface *ppc, u8 port, u8 data)
-{
- ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE));
-
- ppc6_wr_data_byte(ppc, data);
-}
-
-//***************************************************************************
-
-static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count)
-{
- switch(ppc->mode)
- {
- case PPCMODE_UNI_SW :
- case PPCMODE_UNI_FW :
- {
- while(count)
- {
- u8 d;
-
- ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- // DELAY
-
- d = inb(ppc->lpt_addr + 1);
-
- d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
-
- ppc->cur_ctrl |= port_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- // DELAY
-
- d |= inb(ppc->lpt_addr + 1) & 0xB8;
-
- *data++ = d;
- count--;
- }
-
- break;
- }
-
- case PPCMODE_BI_SW :
- case PPCMODE_BI_FW :
- {
- ppc->cur_ctrl |= port_dir;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- ppc->cur_ctrl |= port_stb;
-
- while(count)
- {
- ppc->cur_ctrl ^= data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- *data++ = inb(ppc->lpt_addr);
- count--;
- }
-
- ppc->cur_ctrl &= ~port_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- ppc->cur_ctrl &= ~port_dir;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
-
- case PPCMODE_EPP_BYTE :
- {
- outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
-
- // DELAY
-
- while(count)
- {
- *data++ = inb(ppc->lpt_addr + 4);
- count--;
- }
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
-
- case PPCMODE_EPP_WORD :
- {
- outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
-
- // DELAY
-
- while(count > 1)
- {
- *((u16 *)data) = inw(ppc->lpt_addr + 4);
- data += 2;
- count -= 2;
- }
-
- while(count)
- {
- *data++ = inb(ppc->lpt_addr + 4);
- count--;
- }
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
-
- case PPCMODE_EPP_DWORD :
- {
- outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
-
- // DELAY
-
- while(count > 3)
- {
- *((u32 *)data) = inl(ppc->lpt_addr + 4);
- data += 4;
- count -= 4;
- }
-
- while(count)
- {
- *data++ = inb(ppc->lpt_addr + 4);
- count--;
- }
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- break;
- }
- }
-
-}
-
-//***************************************************************************
-
-static void ppc6_wait_for_fifo(Interface *ppc)
-{
- int i;
-
- if (ppc->ppc_flags & fifo_wait)
- {
- for(i=0; i<20; i++)
- inb(ppc->lpt_addr + 1);
- }
-}
-
-//***************************************************************************
-
-static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count)
-{
- switch(ppc->mode)
- {
- case PPCMODE_UNI_SW :
- case PPCMODE_BI_SW :
- {
- while(count--)
- {
- outb(*data++, ppc->lpt_addr);
-
- ppc->cur_ctrl ^= data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
- }
-
- break;
- }
-
- case PPCMODE_UNI_FW :
- case PPCMODE_BI_FW :
- {
- u8 this, last;
-
- ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR));
-
- ppc->cur_ctrl |= port_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- last = *data;
-
- outb(last, ppc->lpt_addr);
-
- while(count)
- {
- this = *data++;
- count--;
-
- if (this == last)
- {
- ppc->cur_ctrl ^= data_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
- }
- else
- {
- outb(this, ppc->lpt_addr);
-
- last = this;
- }
- }
-
- ppc->cur_ctrl &= ~port_stb;
-
- outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
-
- ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR));
-
- break;
- }
-
- case PPCMODE_EPP_BYTE :
- {
- while(count)
- {
- outb(*data++,ppc->lpt_addr + 4);
- count--;
- }
-
- ppc6_wait_for_fifo(ppc);
-
- break;
- }
-
- case PPCMODE_EPP_WORD :
- {
- while(count > 1)
- {
- outw(*((u16 *)data),ppc->lpt_addr + 4);
- data += 2;
- count -= 2;
- }
-
- while(count)
- {
- outb(*data++,ppc->lpt_addr + 4);
- count--;
- }
-
- ppc6_wait_for_fifo(ppc);
-
- break;
- }
-
- case PPCMODE_EPP_DWORD :
- {
- while(count > 3)
- {
- outl(*((u32 *)data),ppc->lpt_addr + 4);
- data += 4;
- count -= 4;
- }
-
- while(count)
- {
- outb(*data++,ppc->lpt_addr + 4);
- count--;
- }
-
- ppc6_wait_for_fifo(ppc);
-
- break;
- }
- }
-}
-
-//***************************************************************************
-
-static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
-{
- length = length << 1;
-
- ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
- ppc6_wr_data_byte(ppc,(u8)length);
- ppc6_wr_data_byte(ppc,(u8)(length >> 8));
- ppc6_wr_data_byte(ppc,0);
-
- ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
-
- ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ));
-
- ppc6_rd_data_blk(ppc, data, length);
-
- ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
-}
-
-//***************************************************************************
-
-static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
-{
- length = length << 1;
-
- ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
- ppc6_wr_data_byte(ppc,(u8)length);
- ppc6_wr_data_byte(ppc,(u8)(length >> 8));
- ppc6_wr_data_byte(ppc,0);
-
- ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
-
- ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE));
-
- ppc6_wr_data_blk(ppc, data, length);
-
- ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
-}
-
-//***************************************************************************
-
-static void ppc6_wr_extout(Interface *ppc, u8 regdata)
-{
- ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE));
-
- ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6));
-}
-
-//***************************************************************************
-
-static int ppc6_open(Interface *ppc)
-{
- int ret;
-
- ret = ppc6_select(ppc);
-
- if (ret == 0)
- return(ret);
-
- ppc->ppc_flags &= ~fifo_wait;
-
- ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE));
- ppc6_wr_data_byte(ppc, RAMSIZE_128K);
-
- ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION));
-
- if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C)
- ppc->ppc_flags |= fifo_wait;
-
- return(ret);
-}
-
-//***************************************************************************
-
-static void ppc6_close(Interface *ppc)
-{
- ppc6_deselect(ppc);
-}
-
-//***************************************************************************
-
diff --git a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h
deleted file mode 100644
index bc3703294143..000000000000
--- a/drivers/block/paride/pseudo.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- pseudo.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is the "pseudo-interrupt" logic for parallel port drivers.
-
- This module is #included into each driver. It makes one
- function available:
-
- ps_set_intr( void (*continuation)(void),
- int (*ready)(void),
- int timeout,
- int nice )
-
- Which will arrange for ready() to be evaluated frequently and
- when either it returns true, or timeout jiffies have passed,
- continuation() will be invoked.
-
- If nice is 1, the test will done approximately once a
- jiffy. If nice is 0, the test will also be done whenever
- the scheduler runs (by adding it to a task queue). If
- nice is greater than 1, the test will be done once every
- (nice-1) jiffies.
-
-*/
-
-/* Changes:
-
- 1.01 1998.05.03 Switched from cli()/sti() to spinlocks
- 1.02 1998.12.14 Added support for nice > 1
-*/
-
-#define PS_VERSION "1.02"
-
-#include <linux/sched.h>
-#include <linux/workqueue.h>
-
-static void ps_tq_int(struct work_struct *work);
-
-static void (* ps_continuation)(void);
-static int (* ps_ready)(void);
-static unsigned long ps_timeout;
-static int ps_tq_active = 0;
-static int ps_nice = 0;
-
-static DEFINE_SPINLOCK(ps_spinlock __attribute__((unused)));
-
-static DECLARE_DELAYED_WORK(ps_tq, ps_tq_int);
-
-static void ps_set_intr(void (*continuation)(void),
- int (*ready)(void),
- int timeout, int nice)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ps_spinlock,flags);
-
- ps_continuation = continuation;
- ps_ready = ready;
- ps_timeout = jiffies + timeout;
- ps_nice = nice;
-
- if (!ps_tq_active) {
- ps_tq_active = 1;
- if (!ps_nice)
- schedule_delayed_work(&ps_tq, 0);
- else
- schedule_delayed_work(&ps_tq, ps_nice-1);
- }
- spin_unlock_irqrestore(&ps_spinlock,flags);
-}
-
-static void ps_tq_int(struct work_struct *work)
-{
- void (*con)(void);
- unsigned long flags;
-
- spin_lock_irqsave(&ps_spinlock,flags);
-
- con = ps_continuation;
- ps_tq_active = 0;
-
- if (!con) {
- spin_unlock_irqrestore(&ps_spinlock,flags);
- return;
- }
- if (!ps_ready || ps_ready() || time_after_eq(jiffies, ps_timeout)) {
- ps_continuation = NULL;
- spin_unlock_irqrestore(&ps_spinlock,flags);
- con();
- return;
- }
- ps_tq_active = 1;
- if (!ps_nice)
- schedule_delayed_work(&ps_tq, 0);
- else
- schedule_delayed_work(&ps_tq, ps_nice-1);
- spin_unlock_irqrestore(&ps_spinlock,flags);
-}
-
-/* end of pseudo.h */
-
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
deleted file mode 100644
index e815312a00ad..000000000000
--- a/drivers/block/paride/pt.c
+++ /dev/null
@@ -1,1024 +0,0 @@
-/*
- pt.c (c) 1998 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU General Public License.
-
- This is the high-level driver for parallel port ATAPI tape
- drives based on chips supported by the paride module.
-
- The driver implements both rewinding and non-rewinding
- devices, filemarks, and the rewind ioctl. It allocates
- a small internal "bounce buffer" for each open device, but
- otherwise expects buffering and blocking to be done at the
- user level. As with most block-structured tapes, short
- writes are padded to full tape blocks, so reading back a file
- may return more data than was actually written.
-
- By default, the driver will autoprobe for a single parallel
- port ATAPI tape drive, but if their individual parameters are
- specified, the driver can handle up to 4 drives.
-
- The rewinding devices are named /dev/pt0, /dev/pt1, ...
- while the non-rewinding devices are /dev/npt0, /dev/npt1, etc.
-
- The behaviour of the pt driver can be altered by setting
- some parameters from the insmod command line. The following
- parameters are adjustable:
-
- drive0 These four arguments can be arrays of
- drive1 1-6 integers as follows:
- drive2
- drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
-
- Where,
-
- <prt> is the base of the parallel port address for
- the corresponding drive. (required)
-
- <pro> is the protocol number for the adapter that
- supports this drive. These numbers are
- logged by 'paride' when the protocol modules
- are initialised. (0 if not given)
-
- <uni> for those adapters that support chained
- devices, this is the unit selector for the
- chain of devices on the given port. It should
- be zero for devices that don't support chaining.
- (0 if not given)
-
- <mod> this can be -1 to choose the best mode, or one
- of the mode numbers supported by the adapter.
- (-1 if not given)
-
- <slv> ATAPI devices can be jumpered to master or slave.
- Set this to 0 to choose the master drive, 1 to
- choose the slave, -1 (the default) to choose the
- first drive found.
-
- <dly> some parallel ports require the driver to
- go more slowly. -1 sets a default value that
- should work with the chosen protocol. Otherwise,
- set this to a small integer, the larger it is
- the slower the port i/o. In some cases, setting
- this to zero will speed up the device. (default -1)
-
- major You may use this parameter to override the
- default major number (96) that this driver
- will use. Be sure to change the device
- name as well.
-
- name This parameter is a character string that
- contains the name the kernel will use for this
- device (in /proc output, for instance).
- (default "pt").
-
- verbose This parameter controls the amount of logging
- that the driver will do. Set it to 0 for
- normal operation, 1 to see autoprobe progress
- messages, or 2 to see additional debugging
- output. (default 0)
-
- If this driver is built into the kernel, you can use
- the following command line parameters, with the same values
- as the corresponding module parameters listed above:
-
- pt.drive0
- pt.drive1
- pt.drive2
- pt.drive3
-
- In addition, you can use the parameter pt.disable to disable
- the driver entirely.
-
-*/
-
-/* Changes:
-
- 1.01 GRG 1998.05.06 Round up transfer size, fix ready_wait,
- loosed interpretation of ATAPI standard
- for clearing error status.
- Eliminate sti();
- 1.02 GRG 1998.06.16 Eliminate an Ugh.
- 1.03 GRG 1998.08.15 Adjusted PT_TMO, use HZ in loop timing,
- extra debugging
- 1.04 GRG 1998.09.24 Repair minor coding error, added jumbo support
-
-*/
-
-#define PT_VERSION "1.04"
-#define PT_MAJOR 96
-#define PT_NAME "pt"
-#define PT_UNITS 4
-
-#include <linux/types.h>
-
-/* Here are things one can override from the insmod command.
- Most are autoprobed by paride unless set here. Verbose is on
- by default.
-
-*/
-
-static int verbose = 0;
-static int major = PT_MAJOR;
-static char *name = PT_NAME;
-static int disable = 0;
-
-static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
-static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
-
-static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
-
-#define D_PRT 0
-#define D_PRO 1
-#define D_UNI 2
-#define D_MOD 3
-#define D_SLV 4
-#define D_DLY 5
-
-#define DU (*drives[unit])
-
-/* end of parameters */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/mtio.h>
-#include <linux/device.h>
-#include <linux/sched.h> /* current, TASK_*, schedule_timeout() */
-#include <linux/mutex.h>
-
-#include <linux/uaccess.h>
-
-module_param(verbose, int, 0);
-module_param(major, int, 0);
-module_param(name, charp, 0);
-module_param_array(drive0, int, NULL, 0);
-module_param_array(drive1, int, NULL, 0);
-module_param_array(drive2, int, NULL, 0);
-module_param_array(drive3, int, NULL, 0);
-
-#include "paride.h"
-
-#define PT_MAX_RETRIES 5
-#define PT_TMO 3000 /* interrupt timeout in jiffies */
-#define PT_SPIN_DEL 50 /* spin delay in micro-seconds */
-#define PT_RESET_TMO 30 /* 30 seconds */
-#define PT_READY_TMO 60 /* 60 seconds */
-#define PT_REWIND_TMO 1200 /* 20 minutes */
-
-#define PT_SPIN ((1000000/(HZ*PT_SPIN_DEL))*PT_TMO)
-
-#define STAT_ERR 0x00001
-#define STAT_INDEX 0x00002
-#define STAT_ECC 0x00004
-#define STAT_DRQ 0x00008
-#define STAT_SEEK 0x00010
-#define STAT_WRERR 0x00020
-#define STAT_READY 0x00040
-#define STAT_BUSY 0x00080
-#define STAT_SENSE 0x1f000
-
-#define ATAPI_TEST_READY 0x00
-#define ATAPI_REWIND 0x01
-#define ATAPI_REQ_SENSE 0x03
-#define ATAPI_READ_6 0x08
-#define ATAPI_WRITE_6 0x0a
-#define ATAPI_WFM 0x10
-#define ATAPI_IDENTIFY 0x12
-#define ATAPI_MODE_SENSE 0x1a
-#define ATAPI_LOG_SENSE 0x4d
-
-static DEFINE_MUTEX(pt_mutex);
-static int pt_open(struct inode *inode, struct file *file);
-static long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-static int pt_release(struct inode *inode, struct file *file);
-static ssize_t pt_read(struct file *filp, char __user *buf,
- size_t count, loff_t * ppos);
-static ssize_t pt_write(struct file *filp, const char __user *buf,
- size_t count, loff_t * ppos);
-static int pt_detect(void);
-
-/* bits in tape->flags */
-
-#define PT_MEDIA 1
-#define PT_WRITE_OK 2
-#define PT_REWIND 4
-#define PT_WRITING 8
-#define PT_READING 16
-#define PT_EOF 32
-
-#define PT_NAMELEN 8
-#define PT_BUFSIZE 16384
-
-struct pt_unit {
- struct pi_adapter pia; /* interface to paride layer */
- struct pi_adapter *pi;
- int flags; /* various state flags */
- int last_sense; /* result of last request sense */
- int drive; /* drive */
- atomic_t available; /* 1 if access is available 0 otherwise */
- int bs; /* block size */
- int capacity; /* Size of tape in KB */
- int present; /* device present ? */
- char *bufptr;
- char name[PT_NAMELEN]; /* pf0, pf1, ... */
-};
-
-static int pt_identify(struct pt_unit *tape);
-
-static struct pt_unit pt[PT_UNITS];
-
-static char pt_scratch[512]; /* scratch block buffer */
-static void *par_drv; /* reference of parport driver */
-
-/* kernel glue structures */
-
-static const struct file_operations pt_fops = {
- .owner = THIS_MODULE,
- .read = pt_read,
- .write = pt_write,
- .unlocked_ioctl = pt_ioctl,
- .open = pt_open,
- .release = pt_release,
- .llseek = noop_llseek,
-};
-
-/* sysfs class support */
-static struct class *pt_class;
-
-static inline int status_reg(struct pi_adapter *pi)
-{
- return pi_read_regr(pi, 1, 6);
-}
-
-static inline int read_reg(struct pi_adapter *pi, int reg)
-{
- return pi_read_regr(pi, 0, reg);
-}
-
-static inline void write_reg(struct pi_adapter *pi, int reg, int val)
-{
- pi_write_regr(pi, 0, reg, val);
-}
-
-static inline u8 DRIVE(struct pt_unit *tape)
-{
- return 0xa0+0x10*tape->drive;
-}
-
-static int pt_wait(struct pt_unit *tape, int go, int stop, char *fun, char *msg)
-{
- int j, r, e, s, p;
- struct pi_adapter *pi = tape->pi;
-
- j = 0;
- while ((((r = status_reg(pi)) & go) || (stop && (!(r & stop))))
- && (j++ < PT_SPIN))
- udelay(PT_SPIN_DEL);
-
- if ((r & (STAT_ERR & stop)) || (j > PT_SPIN)) {
- s = read_reg(pi, 7);
- e = read_reg(pi, 1);
- p = read_reg(pi, 2);
- if (j > PT_SPIN)
- e |= 0x100;
- if (fun)
- printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
- " loop=%d phase=%d\n",
- tape->name, fun, msg, r, s, e, j, p);
- return (e << 8) + s;
- }
- return 0;
-}
-
-static int pt_command(struct pt_unit *tape, char *cmd, int dlen, char *fun)
-{
- struct pi_adapter *pi = tape->pi;
- pi_connect(pi);
-
- write_reg(pi, 6, DRIVE(tape));
-
- if (pt_wait(tape, STAT_BUSY | STAT_DRQ, 0, fun, "before command")) {
- pi_disconnect(pi);
- return -1;
- }
-
- write_reg(pi, 4, dlen % 256);
- write_reg(pi, 5, dlen / 256);
- write_reg(pi, 7, 0xa0); /* ATAPI packet command */
-
- if (pt_wait(tape, STAT_BUSY, STAT_DRQ, fun, "command DRQ")) {
- pi_disconnect(pi);
- return -1;
- }
-
- if (read_reg(pi, 2) != 1) {
- printk("%s: %s: command phase error\n", tape->name, fun);
- pi_disconnect(pi);
- return -1;
- }
-
- pi_write_block(pi, cmd, 12);
-
- return 0;
-}
-
-static int pt_completion(struct pt_unit *tape, char *buf, char *fun)
-{
- struct pi_adapter *pi = tape->pi;
- int r, s, n, p;
-
- r = pt_wait(tape, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
- fun, "completion");
-
- if (read_reg(pi, 7) & STAT_DRQ) {
- n = (((read_reg(pi, 4) + 256 * read_reg(pi, 5)) +
- 3) & 0xfffc);
- p = read_reg(pi, 2) & 3;
- if (p == 0)
- pi_write_block(pi, buf, n);
- if (p == 2)
- pi_read_block(pi, buf, n);
- }
-
- s = pt_wait(tape, STAT_BUSY, STAT_READY | STAT_ERR, fun, "data done");
-
- pi_disconnect(pi);
-
- return (r ? r : s);
-}
-
-static void pt_req_sense(struct pt_unit *tape, int quiet)
-{
- char rs_cmd[12] = { ATAPI_REQ_SENSE, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
- char buf[16];
- int r;
-
- r = pt_command(tape, rs_cmd, 16, "Request sense");
- mdelay(1);
- if (!r)
- pt_completion(tape, buf, "Request sense");
-
- tape->last_sense = -1;
- if (!r) {
- if (!quiet)
- printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
- tape->name, buf[2] & 0xf, buf[12], buf[13]);
- tape->last_sense = (buf[2] & 0xf) | ((buf[12] & 0xff) << 8)
- | ((buf[13] & 0xff) << 16);
- }
-}
-
-static int pt_atapi(struct pt_unit *tape, char *cmd, int dlen, char *buf, char *fun)
-{
- int r;
-
- r = pt_command(tape, cmd, dlen, fun);
- mdelay(1);
- if (!r)
- r = pt_completion(tape, buf, fun);
- if (r)
- pt_req_sense(tape, !fun);
-
- return r;
-}
-
-static void pt_sleep(int cs)
-{
- schedule_timeout_interruptible(cs);
-}
-
-static int pt_poll_dsc(struct pt_unit *tape, int pause, int tmo, char *msg)
-{
- struct pi_adapter *pi = tape->pi;
- int k, e, s;
-
- k = 0;
- e = 0;
- s = 0;
- while (k < tmo) {
- pt_sleep(pause);
- k++;
- pi_connect(pi);
- write_reg(pi, 6, DRIVE(tape));
- s = read_reg(pi, 7);
- e = read_reg(pi, 1);
- pi_disconnect(pi);
- if (s & (STAT_ERR | STAT_SEEK))
- break;
- }
- if ((k >= tmo) || (s & STAT_ERR)) {
- if (k >= tmo)
- printk("%s: %s DSC timeout\n", tape->name, msg);
- else
- printk("%s: %s stat=0x%x err=0x%x\n", tape->name, msg, s,
- e);
- pt_req_sense(tape, 0);
- return 0;
- }
- return 1;
-}
-
-static void pt_media_access_cmd(struct pt_unit *tape, int tmo, char *cmd, char *fun)
-{
- if (pt_command(tape, cmd, 0, fun)) {
- pt_req_sense(tape, 0);
- return;
- }
- pi_disconnect(tape->pi);
- pt_poll_dsc(tape, HZ, tmo, fun);
-}
-
-static void pt_rewind(struct pt_unit *tape)
-{
- char rw_cmd[12] = { ATAPI_REWIND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- pt_media_access_cmd(tape, PT_REWIND_TMO, rw_cmd, "rewind");
-}
-
-static void pt_write_fm(struct pt_unit *tape)
-{
- char wm_cmd[12] = { ATAPI_WFM, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 };
-
- pt_media_access_cmd(tape, PT_TMO, wm_cmd, "write filemark");
-}
-
-#define DBMSG(msg) ((verbose>1)?(msg):NULL)
-
-static int pt_reset(struct pt_unit *tape)
-{
- struct pi_adapter *pi = tape->pi;
- int i, k, flg;
- int expect[5] = { 1, 1, 1, 0x14, 0xeb };
-
- pi_connect(pi);
- write_reg(pi, 6, DRIVE(tape));
- write_reg(pi, 7, 8);
-
- pt_sleep(20 * HZ / 1000);
-
- k = 0;
- while ((k++ < PT_RESET_TMO) && (status_reg(pi) & STAT_BUSY))
- pt_sleep(HZ / 10);
-
- flg = 1;
- for (i = 0; i < 5; i++)
- flg &= (read_reg(pi, i + 1) == expect[i]);
-
- if (verbose) {
- printk("%s: Reset (%d) signature = ", tape->name, k);
- for (i = 0; i < 5; i++)
- printk("%3x", read_reg(pi, i + 1));
- if (!flg)
- printk(" (incorrect)");
- printk("\n");
- }
-
- pi_disconnect(pi);
- return flg - 1;
-}
-
-static int pt_ready_wait(struct pt_unit *tape, int tmo)
-{
- char tr_cmd[12] = { ATAPI_TEST_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int k, p;
-
- k = 0;
- while (k < tmo) {
- tape->last_sense = 0;
- pt_atapi(tape, tr_cmd, 0, NULL, DBMSG("test unit ready"));
- p = tape->last_sense;
- if (!p)
- return 0;
- if (!(((p & 0xffff) == 0x0402) || ((p & 0xff) == 6)))
- return p;
- k++;
- pt_sleep(HZ);
- }
- return 0x000020; /* timeout */
-}
-
-static void xs(char *buf, char *targ, int offs, int len)
-{
- int j, k, l;
-
- j = 0;
- l = 0;
- for (k = 0; k < len; k++)
- if ((buf[k + offs] != 0x20) || (buf[k + offs] != l))
- l = targ[j++] = buf[k + offs];
- if (l == 0x20)
- j--;
- targ[j] = 0;
-}
-
-static int xn(char *buf, int offs, int size)
-{
- int v, k;
-
- v = 0;
- for (k = 0; k < size; k++)
- v = v * 256 + (buf[k + offs] & 0xff);
- return v;
-}
-
-static int pt_identify(struct pt_unit *tape)
-{
- int dt, s;
- char *ms[2] = { "master", "slave" };
- char mf[10], id[18];
- char id_cmd[12] = { ATAPI_IDENTIFY, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
- char ms_cmd[12] =
- { ATAPI_MODE_SENSE, 0, 0x2a, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
- char ls_cmd[12] =
- { ATAPI_LOG_SENSE, 0, 0x71, 0, 0, 0, 0, 0, 36, 0, 0, 0 };
- char buf[36];
-
- s = pt_atapi(tape, id_cmd, 36, buf, "identify");
- if (s)
- return -1;
-
- dt = buf[0] & 0x1f;
- if (dt != 1) {
- if (verbose)
- printk("%s: Drive %d, unsupported type %d\n",
- tape->name, tape->drive, dt);
- return -1;
- }
-
- xs(buf, mf, 8, 8);
- xs(buf, id, 16, 16);
-
- tape->flags = 0;
- tape->capacity = 0;
- tape->bs = 0;
-
- if (!pt_ready_wait(tape, PT_READY_TMO))
- tape->flags |= PT_MEDIA;
-
- if (!pt_atapi(tape, ms_cmd, 36, buf, "mode sense")) {
- if (!(buf[2] & 0x80))
- tape->flags |= PT_WRITE_OK;
- tape->bs = xn(buf, 10, 2);
- }
-
- if (!pt_atapi(tape, ls_cmd, 36, buf, "log sense"))
- tape->capacity = xn(buf, 24, 4);
-
- printk("%s: %s %s, %s", tape->name, mf, id, ms[tape->drive]);
- if (!(tape->flags & PT_MEDIA))
- printk(", no media\n");
- else {
- if (!(tape->flags & PT_WRITE_OK))
- printk(", RO");
- printk(", blocksize %d, %d MB\n", tape->bs, tape->capacity / 1024);
- }
-
- return 0;
-}
-
-
-/*
- * returns 0, with id set if drive is detected
- * -1, if drive detection failed
- */
-static int pt_probe(struct pt_unit *tape)
-{
- if (tape->drive == -1) {
- for (tape->drive = 0; tape->drive <= 1; tape->drive++)
- if (!pt_reset(tape))
- return pt_identify(tape);
- } else {
- if (!pt_reset(tape))
- return pt_identify(tape);
- }
- return -1;
-}
-
-static int pt_detect(void)
-{
- struct pt_unit *tape;
- int specified = 0, found = 0;
- int unit;
-
- printk("%s: %s version %s, major %d\n", name, name, PT_VERSION, major);
-
- par_drv = pi_register_driver(name);
- if (!par_drv) {
- pr_err("failed to register %s driver\n", name);
- return -1;
- }
-
- specified = 0;
- for (unit = 0; unit < PT_UNITS; unit++) {
- struct pt_unit *tape = &pt[unit];
- tape->pi = &tape->pia;
- atomic_set(&tape->available, 1);
- tape->flags = 0;
- tape->last_sense = 0;
- tape->present = 0;
- tape->bufptr = NULL;
- tape->drive = DU[D_SLV];
- snprintf(tape->name, PT_NAMELEN, "%s%d", name, unit);
- if (!DU[D_PRT])
- continue;
- specified++;
- if (pi_init(tape->pi, 0, DU[D_PRT], DU[D_MOD], DU[D_UNI],
- DU[D_PRO], DU[D_DLY], pt_scratch, PI_PT,
- verbose, tape->name)) {
- if (!pt_probe(tape)) {
- tape->present = 1;
- found++;
- } else
- pi_release(tape->pi);
- }
- }
- if (specified == 0) {
- tape = pt;
- if (pi_init(tape->pi, 1, -1, -1, -1, -1, -1, pt_scratch,
- PI_PT, verbose, tape->name)) {
- if (!pt_probe(tape)) {
- tape->present = 1;
- found++;
- } else
- pi_release(tape->pi);
- }
-
- }
- if (found)
- return 0;
-
- pi_unregister_driver(par_drv);
- printk("%s: No ATAPI tape drive detected\n", name);
- return -1;
-}
-
-static int pt_open(struct inode *inode, struct file *file)
-{
- int unit = iminor(inode) & 0x7F;
- struct pt_unit *tape = pt + unit;
- int err;
-
- mutex_lock(&pt_mutex);
- if (unit >= PT_UNITS || (!tape->present)) {
- mutex_unlock(&pt_mutex);
- return -ENODEV;
- }
-
- err = -EBUSY;
- if (!atomic_dec_and_test(&tape->available))
- goto out;
-
- pt_identify(tape);
-
- err = -ENODEV;
- if (!(tape->flags & PT_MEDIA))
- goto out;
-
- err = -EROFS;
- if ((!(tape->flags & PT_WRITE_OK)) && (file->f_mode & FMODE_WRITE))
- goto out;
-
- if (!(iminor(inode) & 128))
- tape->flags |= PT_REWIND;
-
- err = -ENOMEM;
- tape->bufptr = kmalloc(PT_BUFSIZE, GFP_KERNEL);
- if (tape->bufptr == NULL) {
- printk("%s: buffer allocation failed\n", tape->name);
- goto out;
- }
-
- file->private_data = tape;
- mutex_unlock(&pt_mutex);
- return 0;
-
-out:
- atomic_inc(&tape->available);
- mutex_unlock(&pt_mutex);
- return err;
-}
-
-static long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct pt_unit *tape = file->private_data;
- struct mtop __user *p = (void __user *)arg;
- struct mtop mtop;
-
- switch (cmd) {
- case MTIOCTOP:
- if (copy_from_user(&mtop, p, sizeof(struct mtop)))
- return -EFAULT;
-
- switch (mtop.mt_op) {
-
- case MTREW:
- mutex_lock(&pt_mutex);
- pt_rewind(tape);
- mutex_unlock(&pt_mutex);
- return 0;
-
- case MTWEOF:
- mutex_lock(&pt_mutex);
- pt_write_fm(tape);
- mutex_unlock(&pt_mutex);
- return 0;
-
- default:
- /* FIXME: rate limit ?? */
- printk(KERN_DEBUG "%s: Unimplemented mt_op %d\n", tape->name,
- mtop.mt_op);
- return -EINVAL;
- }
-
- default:
- return -ENOTTY;
- }
-}
-
-static int
-pt_release(struct inode *inode, struct file *file)
-{
- struct pt_unit *tape = file->private_data;
-
- if (atomic_read(&tape->available) > 1)
- return -EINVAL;
-
- if (tape->flags & PT_WRITING)
- pt_write_fm(tape);
-
- if (tape->flags & PT_REWIND)
- pt_rewind(tape);
-
- kfree(tape->bufptr);
- tape->bufptr = NULL;
-
- atomic_inc(&tape->available);
-
- return 0;
-
-}
-
-static ssize_t pt_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
-{
- struct pt_unit *tape = filp->private_data;
- struct pi_adapter *pi = tape->pi;
- char rd_cmd[12] = { ATAPI_READ_6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int k, n, r, p, s, t, b;
-
- if (!(tape->flags & (PT_READING | PT_WRITING))) {
- tape->flags |= PT_READING;
- if (pt_atapi(tape, rd_cmd, 0, NULL, "start read-ahead"))
- return -EIO;
- } else if (tape->flags & PT_WRITING)
- return -EIO;
-
- if (tape->flags & PT_EOF)
- return 0;
-
- t = 0;
-
- while (count > 0) {
-
- if (!pt_poll_dsc(tape, HZ / 100, PT_TMO, "read"))
- return -EIO;
-
- n = count;
- if (n > 32768)
- n = 32768; /* max per command */
- b = (n - 1 + tape->bs) / tape->bs;
- n = b * tape->bs; /* rounded up to even block */
-
- rd_cmd[4] = b;
-
- r = pt_command(tape, rd_cmd, n, "read");
-
- mdelay(1);
-
- if (r) {
- pt_req_sense(tape, 0);
- return -EIO;
- }
-
- while (1) {
-
- r = pt_wait(tape, STAT_BUSY,
- STAT_DRQ | STAT_ERR | STAT_READY,
- DBMSG("read DRQ"), "");
-
- if (r & STAT_SENSE) {
- pi_disconnect(pi);
- pt_req_sense(tape, 0);
- return -EIO;
- }
-
- if (r)
- tape->flags |= PT_EOF;
-
- s = read_reg(pi, 7);
-
- if (!(s & STAT_DRQ))
- break;
-
- n = (read_reg(pi, 4) + 256 * read_reg(pi, 5));
- p = (read_reg(pi, 2) & 3);
- if (p != 2) {
- pi_disconnect(pi);
- printk("%s: Phase error on read: %d\n", tape->name,
- p);
- return -EIO;
- }
-
- while (n > 0) {
- k = n;
- if (k > PT_BUFSIZE)
- k = PT_BUFSIZE;
- pi_read_block(pi, tape->bufptr, k);
- n -= k;
- b = k;
- if (b > count)
- b = count;
- if (copy_to_user(buf + t, tape->bufptr, b)) {
- pi_disconnect(pi);
- return -EFAULT;
- }
- t += b;
- count -= b;
- }
-
- }
- pi_disconnect(pi);
- if (tape->flags & PT_EOF)
- break;
- }
-
- return t;
-
-}
-
-static ssize_t pt_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
-{
- struct pt_unit *tape = filp->private_data;
- struct pi_adapter *pi = tape->pi;
- char wr_cmd[12] = { ATAPI_WRITE_6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int k, n, r, p, s, t, b;
-
- if (!(tape->flags & PT_WRITE_OK))
- return -EROFS;
-
- if (!(tape->flags & (PT_READING | PT_WRITING))) {
- tape->flags |= PT_WRITING;
- if (pt_atapi
- (tape, wr_cmd, 0, NULL, "start buffer-available mode"))
- return -EIO;
- } else if (tape->flags & PT_READING)
- return -EIO;
-
- if (tape->flags & PT_EOF)
- return -ENOSPC;
-
- t = 0;
-
- while (count > 0) {
-
- if (!pt_poll_dsc(tape, HZ / 100, PT_TMO, "write"))
- return -EIO;
-
- n = count;
- if (n > 32768)
- n = 32768; /* max per command */
- b = (n - 1 + tape->bs) / tape->bs;
- n = b * tape->bs; /* rounded up to even block */
-
- wr_cmd[4] = b;
-
- r = pt_command(tape, wr_cmd, n, "write");
-
- mdelay(1);
-
- if (r) { /* error delivering command only */
- pt_req_sense(tape, 0);
- return -EIO;
- }
-
- while (1) {
-
- r = pt_wait(tape, STAT_BUSY,
- STAT_DRQ | STAT_ERR | STAT_READY,
- DBMSG("write DRQ"), NULL);
-
- if (r & STAT_SENSE) {
- pi_disconnect(pi);
- pt_req_sense(tape, 0);
- return -EIO;
- }
-
- if (r)
- tape->flags |= PT_EOF;
-
- s = read_reg(pi, 7);
-
- if (!(s & STAT_DRQ))
- break;
-
- n = (read_reg(pi, 4) + 256 * read_reg(pi, 5));
- p = (read_reg(pi, 2) & 3);
- if (p != 0) {
- pi_disconnect(pi);
- printk("%s: Phase error on write: %d \n",
- tape->name, p);
- return -EIO;
- }
-
- while (n > 0) {
- k = n;
- if (k > PT_BUFSIZE)
- k = PT_BUFSIZE;
- b = k;
- if (b > count)
- b = count;
- if (copy_from_user(tape->bufptr, buf + t, b)) {
- pi_disconnect(pi);
- return -EFAULT;
- }
- pi_write_block(pi, tape->bufptr, k);
- t += b;
- count -= b;
- n -= k;
- }
-
- }
- pi_disconnect(pi);
- if (tape->flags & PT_EOF)
- break;
- }
-
- return t;
-}
-
-static int __init pt_init(void)
-{
- int unit;
- int err;
-
- if (disable) {
- err = -EINVAL;
- goto out;
- }
-
- if (pt_detect()) {
- err = -ENODEV;
- goto out;
- }
-
- err = register_chrdev(major, name, &pt_fops);
- if (err < 0) {
- printk("pt_init: unable to get major number %d\n", major);
- for (unit = 0; unit < PT_UNITS; unit++)
- if (pt[unit].present)
- pi_release(pt[unit].pi);
- goto out;
- }
- major = err;
- pt_class = class_create(THIS_MODULE, "pt");
- if (IS_ERR(pt_class)) {
- err = PTR_ERR(pt_class);
- goto out_chrdev;
- }
-
- for (unit = 0; unit < PT_UNITS; unit++)
- if (pt[unit].present) {
- device_create(pt_class, NULL, MKDEV(major, unit), NULL,
- "pt%d", unit);
- device_create(pt_class, NULL, MKDEV(major, unit + 128),
- NULL, "pt%dn", unit);
- }
- goto out;
-
-out_chrdev:
- unregister_chrdev(major, "pt");
-out:
- return err;
-}
-
-static void __exit pt_exit(void)
-{
- int unit;
- for (unit = 0; unit < PT_UNITS; unit++)
- if (pt[unit].present) {
- device_destroy(pt_class, MKDEV(major, unit));
- device_destroy(pt_class, MKDEV(major, unit + 128));
- }
- class_destroy(pt_class);
- unregister_chrdev(major, name);
- for (unit = 0; unit < PT_UNITS; unit++)
- if (pt[unit].present)
- pi_release(pt[unit].pi);
-}
-
-MODULE_LICENSE("GPL");
-module_init(pt_init)
-module_exit(pt_exit)
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
deleted file mode 100644
index 4cea3b08087e..000000000000
--- a/drivers/block/pktcdvd.c
+++ /dev/null
@@ -1,2944 +0,0 @@
-/*
- * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
- * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
- * Copyright (C) 2006 Thomas Maier <balagi@justmail.de>
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and
- * DVD-RAM devices.
- *
- * Theory of operation:
- *
- * At the lowest level, there is the standard driver for the CD/DVD device,
- * such as drivers/scsi/sr.c. This driver can handle read and write requests,
- * but it doesn't know anything about the special restrictions that apply to
- * packet writing. One restriction is that write requests must be aligned to
- * packet boundaries on the physical media, and the size of a write request
- * must be equal to the packet size. Another restriction is that a
- * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read
- * command, if the previous command was a write.
- *
- * The purpose of the packet writing driver is to hide these restrictions from
- * higher layers, such as file systems, and present a block device that can be
- * randomly read and written using 2kB-sized blocks.
- *
- * The lowest layer in the packet writing driver is the packet I/O scheduler.
- * Its data is defined by the struct packet_iosched and includes two bio
- * queues with pending read and write requests. These queues are processed
- * by the pkt_iosched_process_queue() function. The write requests in this
- * queue are already properly aligned and sized. This layer is responsible for
- * issuing the flush cache commands and scheduling the I/O in a good order.
- *
- * The next layer transforms unaligned write requests to aligned writes. This
- * transformation requires reading missing pieces of data from the underlying
- * block device, assembling the pieces to full packets and queuing them to the
- * packet I/O scheduler.
- *
- * At the top layer there is a custom ->submit_bio function that forwards
- * read requests directly to the iosched queue and puts write requests in the
- * unaligned write queue. A kernel thread performs the necessary read
- * gathering to convert the unaligned writes to aligned writes and then feeds
- * them to the packet I/O scheduler.
- *
- *************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/pktcdvd.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/compat.h>
-#include <linux/kthread.h>
-#include <linux/errno.h>
-#include <linux/spinlock.h>
-#include <linux/file.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/miscdevice.h>
-#include <linux/freezer.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/backing-dev.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_ioctl.h>
-#include <scsi/scsi.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/nospec.h>
-#include <linux/uaccess.h>
-
-#define DRIVER_NAME "pktcdvd"
-
-#define pkt_err(pd, fmt, ...) \
- pr_err("%s: " fmt, pd->name, ##__VA_ARGS__)
-#define pkt_notice(pd, fmt, ...) \
- pr_notice("%s: " fmt, pd->name, ##__VA_ARGS__)
-#define pkt_info(pd, fmt, ...) \
- pr_info("%s: " fmt, pd->name, ##__VA_ARGS__)
-
-#define pkt_dbg(level, pd, fmt, ...) \
-do { \
- if (level == 2 && PACKET_DEBUG >= 2) \
- pr_notice("%s: %s():" fmt, \
- pd->name, __func__, ##__VA_ARGS__); \
- else if (level == 1 && PACKET_DEBUG >= 1) \
- pr_notice("%s: " fmt, pd->name, ##__VA_ARGS__); \
-} while (0)
-
-#define MAX_SPEED 0xffff
-
-static DEFINE_MUTEX(pktcdvd_mutex);
-static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
-static struct proc_dir_entry *pkt_proc;
-static int pktdev_major;
-static int write_congestion_on = PKT_WRITE_CONGESTION_ON;
-static int write_congestion_off = PKT_WRITE_CONGESTION_OFF;
-static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */
-static mempool_t psd_pool;
-static struct bio_set pkt_bio_set;
-
-static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */
-static struct dentry *pkt_debugfs_root = NULL; /* /sys/kernel/debug/pktcdvd */
-
-/* forward declaration */
-static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
-static int pkt_remove_dev(dev_t pkt_dev);
-static int pkt_seq_show(struct seq_file *m, void *p);
-
-static sector_t get_zone(sector_t sector, struct pktcdvd_device *pd)
-{
- return (sector + pd->offset) & ~(sector_t)(pd->settings.size - 1);
-}
-
-/**********************************************************
- * sysfs interface for pktcdvd
- * by (C) 2006 Thomas Maier <balagi@justmail.de>
-
- /sys/class/pktcdvd/pktcdvd[0-7]/
- stat/reset
- stat/packets_started
- stat/packets_finished
- stat/kb_written
- stat/kb_read
- stat/kb_read_gather
- write_queue/size
- write_queue/congestion_off
- write_queue/congestion_on
- **********************************************************/
-
-static ssize_t packets_started_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%lu\n", pd->stats.pkt_started);
-}
-static DEVICE_ATTR_RO(packets_started);
-
-static ssize_t packets_finished_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%lu\n", pd->stats.pkt_ended);
-}
-static DEVICE_ATTR_RO(packets_finished);
-
-static ssize_t kb_written_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%lu\n", pd->stats.secs_w >> 1);
-}
-static DEVICE_ATTR_RO(kb_written);
-
-static ssize_t kb_read_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%lu\n", pd->stats.secs_r >> 1);
-}
-static DEVICE_ATTR_RO(kb_read);
-
-static ssize_t kb_read_gather_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%lu\n", pd->stats.secs_rg >> 1);
-}
-static DEVICE_ATTR_RO(kb_read_gather);
-
-static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
-
- if (len > 0) {
- pd->stats.pkt_started = 0;
- pd->stats.pkt_ended = 0;
- pd->stats.secs_w = 0;
- pd->stats.secs_rg = 0;
- pd->stats.secs_r = 0;
- }
- return len;
-}
-static DEVICE_ATTR_WO(reset);
-
-static struct attribute *pkt_stat_attrs[] = {
- &dev_attr_packets_finished.attr,
- &dev_attr_packets_started.attr,
- &dev_attr_kb_read.attr,
- &dev_attr_kb_written.attr,
- &dev_attr_kb_read_gather.attr,
- &dev_attr_reset.attr,
- NULL,
-};
-
-static const struct attribute_group pkt_stat_group = {
- .name = "stat",
- .attrs = pkt_stat_attrs,
-};
-
-static ssize_t size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
- int n;
-
- spin_lock(&pd->lock);
- n = sysfs_emit(buf, "%d\n", pd->bio_queue_size);
- spin_unlock(&pd->lock);
- return n;
-}
-static DEVICE_ATTR_RO(size);
-
-static void init_write_congestion_marks(int* lo, int* hi)
-{
- if (*hi > 0) {
- *hi = max(*hi, 500);
- *hi = min(*hi, 1000000);
- if (*lo <= 0)
- *lo = *hi - 100;
- else {
- *lo = min(*lo, *hi - 100);
- *lo = max(*lo, 100);
- }
- } else {
- *hi = -1;
- *lo = -1;
- }
-}
-
-static ssize_t congestion_off_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
- int n;
-
- spin_lock(&pd->lock);
- n = sysfs_emit(buf, "%d\n", pd->write_congestion_off);
- spin_unlock(&pd->lock);
- return n;
-}
-
-static ssize_t congestion_off_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
- int val;
-
- if (sscanf(buf, "%d", &val) == 1) {
- spin_lock(&pd->lock);
- pd->write_congestion_off = val;
- init_write_congestion_marks(&pd->write_congestion_off,
- &pd->write_congestion_on);
- spin_unlock(&pd->lock);
- }
- return len;
-}
-static DEVICE_ATTR_RW(congestion_off);
-
-static ssize_t congestion_on_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
- int n;
-
- spin_lock(&pd->lock);
- n = sysfs_emit(buf, "%d\n", pd->write_congestion_on);
- spin_unlock(&pd->lock);
- return n;
-}
-
-static ssize_t congestion_on_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct pktcdvd_device *pd = dev_get_drvdata(dev);
- int val;
-
- if (sscanf(buf, "%d", &val) == 1) {
- spin_lock(&pd->lock);
- pd->write_congestion_on = val;
- init_write_congestion_marks(&pd->write_congestion_off,
- &pd->write_congestion_on);
- spin_unlock(&pd->lock);
- }
- return len;
-}
-static DEVICE_ATTR_RW(congestion_on);
-
-static struct attribute *pkt_wq_attrs[] = {
- &dev_attr_congestion_on.attr,
- &dev_attr_congestion_off.attr,
- &dev_attr_size.attr,
- NULL,
-};
-
-static const struct attribute_group pkt_wq_group = {
- .name = "write_queue",
- .attrs = pkt_wq_attrs,
-};
-
-static const struct attribute_group *pkt_groups[] = {
- &pkt_stat_group,
- &pkt_wq_group,
- NULL,
-};
-
-static void pkt_sysfs_dev_new(struct pktcdvd_device *pd)
-{
- if (class_pktcdvd) {
- pd->dev = device_create_with_groups(class_pktcdvd, NULL,
- MKDEV(0, 0), pd, pkt_groups,
- "%s", pd->name);
- if (IS_ERR(pd->dev))
- pd->dev = NULL;
- }
-}
-
-static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd)
-{
- if (class_pktcdvd)
- device_unregister(pd->dev);
-}
-
-
-/********************************************************************
- /sys/class/pktcdvd/
- add map block device
- remove unmap packet dev
- device_map show mappings
- *******************************************************************/
-
-static void class_pktcdvd_release(struct class *cls)
-{
- kfree(cls);
-}
-
-static ssize_t device_map_show(struct class *c, struct class_attribute *attr,
- char *data)
-{
- int n = 0;
- int idx;
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- for (idx = 0; idx < MAX_WRITERS; idx++) {
- struct pktcdvd_device *pd = pkt_devs[idx];
- if (!pd)
- continue;
- n += sprintf(data+n, "%s %u:%u %u:%u\n",
- pd->name,
- MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev),
- MAJOR(pd->bdev->bd_dev),
- MINOR(pd->bdev->bd_dev));
- }
- mutex_unlock(&ctl_mutex);
- return n;
-}
-static CLASS_ATTR_RO(device_map);
-
-static ssize_t add_store(struct class *c, struct class_attribute *attr,
- const char *buf, size_t count)
-{
- unsigned int major, minor;
-
- if (sscanf(buf, "%u:%u", &major, &minor) == 2) {
- /* pkt_setup_dev() expects caller to hold reference to self */
- if (!try_module_get(THIS_MODULE))
- return -ENODEV;
-
- pkt_setup_dev(MKDEV(major, minor), NULL);
-
- module_put(THIS_MODULE);
-
- return count;
- }
-
- return -EINVAL;
-}
-static CLASS_ATTR_WO(add);
-
-static ssize_t remove_store(struct class *c, struct class_attribute *attr,
- const char *buf, size_t count)
-{
- unsigned int major, minor;
- if (sscanf(buf, "%u:%u", &major, &minor) == 2) {
- pkt_remove_dev(MKDEV(major, minor));
- return count;
- }
- return -EINVAL;
-}
-static CLASS_ATTR_WO(remove);
-
-static struct attribute *class_pktcdvd_attrs[] = {
- &class_attr_add.attr,
- &class_attr_remove.attr,
- &class_attr_device_map.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(class_pktcdvd);
-
-static int pkt_sysfs_init(void)
-{
- int ret = 0;
-
- /*
- * create control files in sysfs
- * /sys/class/pktcdvd/...
- */
- class_pktcdvd = kzalloc(sizeof(*class_pktcdvd), GFP_KERNEL);
- if (!class_pktcdvd)
- return -ENOMEM;
- class_pktcdvd->name = DRIVER_NAME;
- class_pktcdvd->owner = THIS_MODULE;
- class_pktcdvd->class_release = class_pktcdvd_release;
- class_pktcdvd->class_groups = class_pktcdvd_groups;
- ret = class_register(class_pktcdvd);
- if (ret) {
- kfree(class_pktcdvd);
- class_pktcdvd = NULL;
- pr_err("failed to create class pktcdvd\n");
- return ret;
- }
- return 0;
-}
-
-static void pkt_sysfs_cleanup(void)
-{
- if (class_pktcdvd)
- class_destroy(class_pktcdvd);
- class_pktcdvd = NULL;
-}
-
-/********************************************************************
- entries in debugfs
-
- /sys/kernel/debug/pktcdvd[0-7]/
- info
-
- *******************************************************************/
-
-static int pkt_debugfs_seq_show(struct seq_file *m, void *p)
-{
- return pkt_seq_show(m, p);
-}
-
-static int pkt_debugfs_fops_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pkt_debugfs_seq_show, inode->i_private);
-}
-
-static const struct file_operations debug_fops = {
- .open = pkt_debugfs_fops_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static void pkt_debugfs_dev_new(struct pktcdvd_device *pd)
-{
- if (!pkt_debugfs_root)
- return;
- pd->dfs_d_root = debugfs_create_dir(pd->name, pkt_debugfs_root);
- if (!pd->dfs_d_root)
- return;
-
- pd->dfs_f_info = debugfs_create_file("info", 0444,
- pd->dfs_d_root, pd, &debug_fops);
-}
-
-static void pkt_debugfs_dev_remove(struct pktcdvd_device *pd)
-{
- if (!pkt_debugfs_root)
- return;
- debugfs_remove(pd->dfs_f_info);
- debugfs_remove(pd->dfs_d_root);
- pd->dfs_f_info = NULL;
- pd->dfs_d_root = NULL;
-}
-
-static void pkt_debugfs_init(void)
-{
- pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL);
-}
-
-static void pkt_debugfs_cleanup(void)
-{
- debugfs_remove(pkt_debugfs_root);
- pkt_debugfs_root = NULL;
-}
-
-/* ----------------------------------------------------------*/
-
-
-static void pkt_bio_finished(struct pktcdvd_device *pd)
-{
- BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
- if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
- pkt_dbg(2, pd, "queue empty\n");
- atomic_set(&pd->iosched.attention, 1);
- wake_up(&pd->wqueue);
- }
-}
-
-/*
- * Allocate a packet_data struct
- */
-static struct packet_data *pkt_alloc_packet_data(int frames)
-{
- int i;
- struct packet_data *pkt;
-
- pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL);
- if (!pkt)
- goto no_pkt;
-
- pkt->frames = frames;
- pkt->w_bio = bio_kmalloc(frames, GFP_KERNEL);
- if (!pkt->w_bio)
- goto no_bio;
-
- for (i = 0; i < frames / FRAMES_PER_PAGE; i++) {
- pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
- if (!pkt->pages[i])
- goto no_page;
- }
-
- spin_lock_init(&pkt->lock);
- bio_list_init(&pkt->orig_bios);
-
- for (i = 0; i < frames; i++) {
- pkt->r_bios[i] = bio_kmalloc(1, GFP_KERNEL);
- if (!pkt->r_bios[i])
- goto no_rd_bio;
- }
-
- return pkt;
-
-no_rd_bio:
- for (i = 0; i < frames; i++)
- kfree(pkt->r_bios[i]);
-no_page:
- for (i = 0; i < frames / FRAMES_PER_PAGE; i++)
- if (pkt->pages[i])
- __free_page(pkt->pages[i]);
- kfree(pkt->w_bio);
-no_bio:
- kfree(pkt);
-no_pkt:
- return NULL;
-}
-
-/*
- * Free a packet_data struct
- */
-static void pkt_free_packet_data(struct packet_data *pkt)
-{
- int i;
-
- for (i = 0; i < pkt->frames; i++)
- kfree(pkt->r_bios[i]);
- for (i = 0; i < pkt->frames / FRAMES_PER_PAGE; i++)
- __free_page(pkt->pages[i]);
- kfree(pkt->w_bio);
- kfree(pkt);
-}
-
-static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
-{
- struct packet_data *pkt, *next;
-
- BUG_ON(!list_empty(&pd->cdrw.pkt_active_list));
-
- list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
- pkt_free_packet_data(pkt);
- }
- INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
-}
-
-static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
-{
- struct packet_data *pkt;
-
- BUG_ON(!list_empty(&pd->cdrw.pkt_free_list));
-
- while (nr_packets > 0) {
- pkt = pkt_alloc_packet_data(pd->settings.size >> 2);
- if (!pkt) {
- pkt_shrink_pktlist(pd);
- return 0;
- }
- pkt->id = nr_packets;
- pkt->pd = pd;
- list_add(&pkt->list, &pd->cdrw.pkt_free_list);
- nr_packets--;
- }
- return 1;
-}
-
-static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
-{
- struct rb_node *n = rb_next(&node->rb_node);
- if (!n)
- return NULL;
- return rb_entry(n, struct pkt_rb_node, rb_node);
-}
-
-static void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
-{
- rb_erase(&node->rb_node, &pd->bio_queue);
- mempool_free(node, &pd->rb_pool);
- pd->bio_queue_size--;
- BUG_ON(pd->bio_queue_size < 0);
-}
-
-/*
- * Find the first node in the pd->bio_queue rb tree with a starting sector >= s.
- */
-static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s)
-{
- struct rb_node *n = pd->bio_queue.rb_node;
- struct rb_node *next;
- struct pkt_rb_node *tmp;
-
- if (!n) {
- BUG_ON(pd->bio_queue_size > 0);
- return NULL;
- }
-
- for (;;) {
- tmp = rb_entry(n, struct pkt_rb_node, rb_node);
- if (s <= tmp->bio->bi_iter.bi_sector)
- next = n->rb_left;
- else
- next = n->rb_right;
- if (!next)
- break;
- n = next;
- }
-
- if (s > tmp->bio->bi_iter.bi_sector) {
- tmp = pkt_rbtree_next(tmp);
- if (!tmp)
- return NULL;
- }
- BUG_ON(s > tmp->bio->bi_iter.bi_sector);
- return tmp;
-}
-
-/*
- * Insert a node into the pd->bio_queue rb tree.
- */
-static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node)
-{
- struct rb_node **p = &pd->bio_queue.rb_node;
- struct rb_node *parent = NULL;
- sector_t s = node->bio->bi_iter.bi_sector;
- struct pkt_rb_node *tmp;
-
- while (*p) {
- parent = *p;
- tmp = rb_entry(parent, struct pkt_rb_node, rb_node);
- if (s < tmp->bio->bi_iter.bi_sector)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&node->rb_node, parent, p);
- rb_insert_color(&node->rb_node, &pd->bio_queue);
- pd->bio_queue_size++;
-}
-
-/*
- * Send a packet_command to the underlying block device and
- * wait for completion.
- */
-static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
-{
- struct request_queue *q = bdev_get_queue(pd->bdev);
- struct scsi_cmnd *scmd;
- struct request *rq;
- int ret = 0;
-
- rq = scsi_alloc_request(q, (cgc->data_direction == CGC_DATA_WRITE) ?
- REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- scmd = blk_mq_rq_to_pdu(rq);
-
- if (cgc->buflen) {
- ret = blk_rq_map_kern(q, rq, cgc->buffer, cgc->buflen,
- GFP_NOIO);
- if (ret)
- goto out;
- }
-
- scmd->cmd_len = COMMAND_SIZE(cgc->cmd[0]);
- memcpy(scmd->cmnd, cgc->cmd, CDROM_PACKET_SIZE);
-
- rq->timeout = 60*HZ;
- if (cgc->quiet)
- rq->rq_flags |= RQF_QUIET;
-
- blk_execute_rq(rq, false);
- if (scmd->result)
- ret = -EIO;
-out:
- blk_mq_free_request(rq);
- return ret;
-}
-
-static const char *sense_key_string(__u8 index)
-{
- static const char * const info[] = {
- "No sense", "Recovered error", "Not ready",
- "Medium error", "Hardware error", "Illegal request",
- "Unit attention", "Data protect", "Blank check",
- };
-
- return index < ARRAY_SIZE(info) ? info[index] : "INVALID";
-}
-
-/*
- * A generic sense dump / resolve mechanism should be implemented across
- * all ATAPI + SCSI devices.
- */
-static void pkt_dump_sense(struct pktcdvd_device *pd,
- struct packet_command *cgc)
-{
- struct scsi_sense_hdr *sshdr = cgc->sshdr;
-
- if (sshdr)
- pkt_err(pd, "%*ph - sense %02x.%02x.%02x (%s)\n",
- CDROM_PACKET_SIZE, cgc->cmd,
- sshdr->sense_key, sshdr->asc, sshdr->ascq,
- sense_key_string(sshdr->sense_key));
- else
- pkt_err(pd, "%*ph - no sense\n", CDROM_PACKET_SIZE, cgc->cmd);
-}
-
-/*
- * flush the drive cache to media
- */
-static int pkt_flush_cache(struct pktcdvd_device *pd)
-{
- struct packet_command cgc;
-
- init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
- cgc.cmd[0] = GPCMD_FLUSH_CACHE;
- cgc.quiet = 1;
-
- /*
- * the IMMED bit -- we default to not setting it, although that
- * would allow a much faster close, this is safer
- */
-#if 0
- cgc.cmd[1] = 1 << 1;
-#endif
- return pkt_generic_packet(pd, &cgc);
-}
-
-/*
- * speed is given as the normal factor, e.g. 4 for 4x
- */
-static noinline_for_stack int pkt_set_speed(struct pktcdvd_device *pd,
- unsigned write_speed, unsigned read_speed)
-{
- struct packet_command cgc;
- struct scsi_sense_hdr sshdr;
- int ret;
-
- init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
- cgc.sshdr = &sshdr;
- cgc.cmd[0] = GPCMD_SET_SPEED;
- cgc.cmd[2] = (read_speed >> 8) & 0xff;
- cgc.cmd[3] = read_speed & 0xff;
- cgc.cmd[4] = (write_speed >> 8) & 0xff;
- cgc.cmd[5] = write_speed & 0xff;
-
- ret = pkt_generic_packet(pd, &cgc);
- if (ret)
- pkt_dump_sense(pd, &cgc);
-
- return ret;
-}
-
-/*
- * Queue a bio for processing by the low-level CD device. Must be called
- * from process context.
- */
-static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio)
-{
- spin_lock(&pd->iosched.lock);
- if (bio_data_dir(bio) == READ)
- bio_list_add(&pd->iosched.read_queue, bio);
- else
- bio_list_add(&pd->iosched.write_queue, bio);
- spin_unlock(&pd->iosched.lock);
-
- atomic_set(&pd->iosched.attention, 1);
- wake_up(&pd->wqueue);
-}
-
-/*
- * Process the queued read/write requests. This function handles special
- * requirements for CDRW drives:
- * - A cache flush command must be inserted before a read request if the
- * previous request was a write.
- * - Switching between reading and writing is slow, so don't do it more often
- * than necessary.
- * - Optimize for throughput at the expense of latency. This means that streaming
- * writes will never be interrupted by a read, but if the drive has to seek
- * before the next write, switch to reading instead if there are any pending
- * read requests.
- * - Set the read speed according to current usage pattern. When only reading
- * from the device, it's best to use the highest possible read speed, but
- * when switching often between reading and writing, it's better to have the
- * same read and write speeds.
- */
-static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
-{
-
- if (atomic_read(&pd->iosched.attention) == 0)
- return;
- atomic_set(&pd->iosched.attention, 0);
-
- for (;;) {
- struct bio *bio;
- int reads_queued, writes_queued;
-
- spin_lock(&pd->iosched.lock);
- reads_queued = !bio_list_empty(&pd->iosched.read_queue);
- writes_queued = !bio_list_empty(&pd->iosched.write_queue);
- spin_unlock(&pd->iosched.lock);
-
- if (!reads_queued && !writes_queued)
- break;
-
- if (pd->iosched.writing) {
- int need_write_seek = 1;
- spin_lock(&pd->iosched.lock);
- bio = bio_list_peek(&pd->iosched.write_queue);
- spin_unlock(&pd->iosched.lock);
- if (bio && (bio->bi_iter.bi_sector ==
- pd->iosched.last_write))
- need_write_seek = 0;
- if (need_write_seek && reads_queued) {
- if (atomic_read(&pd->cdrw.pending_bios) > 0) {
- pkt_dbg(2, pd, "write, waiting\n");
- break;
- }
- pkt_flush_cache(pd);
- pd->iosched.writing = 0;
- }
- } else {
- if (!reads_queued && writes_queued) {
- if (atomic_read(&pd->cdrw.pending_bios) > 0) {
- pkt_dbg(2, pd, "read, waiting\n");
- break;
- }
- pd->iosched.writing = 1;
- }
- }
-
- spin_lock(&pd->iosched.lock);
- if (pd->iosched.writing)
- bio = bio_list_pop(&pd->iosched.write_queue);
- else
- bio = bio_list_pop(&pd->iosched.read_queue);
- spin_unlock(&pd->iosched.lock);
-
- if (!bio)
- continue;
-
- if (bio_data_dir(bio) == READ)
- pd->iosched.successive_reads +=
- bio->bi_iter.bi_size >> 10;
- else {
- pd->iosched.successive_reads = 0;
- pd->iosched.last_write = bio_end_sector(bio);
- }
- if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
- if (pd->read_speed == pd->write_speed) {
- pd->read_speed = MAX_SPEED;
- pkt_set_speed(pd, pd->write_speed, pd->read_speed);
- }
- } else {
- if (pd->read_speed != pd->write_speed) {
- pd->read_speed = pd->write_speed;
- pkt_set_speed(pd, pd->write_speed, pd->read_speed);
- }
- }
-
- atomic_inc(&pd->cdrw.pending_bios);
- submit_bio_noacct(bio);
- }
-}
-
-/*
- * Special care is needed if the underlying block device has a small
- * max_phys_segments value.
- */
-static int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_queue *q)
-{
- if ((pd->settings.size << 9) / CD_FRAMESIZE
- <= queue_max_segments(q)) {
- /*
- * The cdrom device can handle one segment/frame
- */
- clear_bit(PACKET_MERGE_SEGS, &pd->flags);
- return 0;
- } else if ((pd->settings.size << 9) / PAGE_SIZE
- <= queue_max_segments(q)) {
- /*
- * We can handle this case at the expense of some extra memory
- * copies during write operations
- */
- set_bit(PACKET_MERGE_SEGS, &pd->flags);
- return 0;
- } else {
- pkt_err(pd, "cdrom max_phys_segments too small\n");
- return -EIO;
- }
-}
-
-static void pkt_end_io_read(struct bio *bio)
-{
- struct packet_data *pkt = bio->bi_private;
- struct pktcdvd_device *pd = pkt->pd;
- BUG_ON(!pd);
-
- pkt_dbg(2, pd, "bio=%p sec0=%llx sec=%llx err=%d\n",
- bio, (unsigned long long)pkt->sector,
- (unsigned long long)bio->bi_iter.bi_sector, bio->bi_status);
-
- if (bio->bi_status)
- atomic_inc(&pkt->io_errors);
- bio_uninit(bio);
- if (atomic_dec_and_test(&pkt->io_wait)) {
- atomic_inc(&pkt->run_sm);
- wake_up(&pd->wqueue);
- }
- pkt_bio_finished(pd);
-}
-
-static void pkt_end_io_packet_write(struct bio *bio)
-{
- struct packet_data *pkt = bio->bi_private;
- struct pktcdvd_device *pd = pkt->pd;
- BUG_ON(!pd);
-
- pkt_dbg(2, pd, "id=%d, err=%d\n", pkt->id, bio->bi_status);
-
- pd->stats.pkt_ended++;
-
- bio_uninit(bio);
- pkt_bio_finished(pd);
- atomic_dec(&pkt->io_wait);
- atomic_inc(&pkt->run_sm);
- wake_up(&pd->wqueue);
-}
-
-/*
- * Schedule reads for the holes in a packet
- */
-static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
-{
- int frames_read = 0;
- struct bio *bio;
- int f;
- char written[PACKET_MAX_SIZE];
-
- BUG_ON(bio_list_empty(&pkt->orig_bios));
-
- atomic_set(&pkt->io_wait, 0);
- atomic_set(&pkt->io_errors, 0);
-
- /*
- * Figure out which frames we need to read before we can write.
- */
- memset(written, 0, sizeof(written));
- spin_lock(&pkt->lock);
- bio_list_for_each(bio, &pkt->orig_bios) {
- int first_frame = (bio->bi_iter.bi_sector - pkt->sector) /
- (CD_FRAMESIZE >> 9);
- int num_frames = bio->bi_iter.bi_size / CD_FRAMESIZE;
- pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9);
- BUG_ON(first_frame < 0);
- BUG_ON(first_frame + num_frames > pkt->frames);
- for (f = first_frame; f < first_frame + num_frames; f++)
- written[f] = 1;
- }
- spin_unlock(&pkt->lock);
-
- if (pkt->cache_valid) {
- pkt_dbg(2, pd, "zone %llx cached\n",
- (unsigned long long)pkt->sector);
- goto out_account;
- }
-
- /*
- * Schedule reads for missing parts of the packet.
- */
- for (f = 0; f < pkt->frames; f++) {
- int p, offset;
-
- if (written[f])
- continue;
-
- bio = pkt->r_bios[f];
- bio_init(bio, pd->bdev, bio->bi_inline_vecs, 1, REQ_OP_READ);
- bio->bi_iter.bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);
- bio->bi_end_io = pkt_end_io_read;
- bio->bi_private = pkt;
-
- p = (f * CD_FRAMESIZE) / PAGE_SIZE;
- offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
- pkt_dbg(2, pd, "Adding frame %d, page:%p offs:%d\n",
- f, pkt->pages[p], offset);
- if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))
- BUG();
-
- atomic_inc(&pkt->io_wait);
- pkt_queue_bio(pd, bio);
- frames_read++;
- }
-
-out_account:
- pkt_dbg(2, pd, "need %d frames for zone %llx\n",
- frames_read, (unsigned long long)pkt->sector);
- pd->stats.pkt_started++;
- pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);
-}
-
-/*
- * Find a packet matching zone, or the least recently used packet if
- * there is no match.
- */
-static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone)
-{
- struct packet_data *pkt;
-
- list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {
- if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {
- list_del_init(&pkt->list);
- if (pkt->sector != zone)
- pkt->cache_valid = 0;
- return pkt;
- }
- }
- BUG();
- return NULL;
-}
-
-static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt)
-{
- if (pkt->cache_valid) {
- list_add(&pkt->list, &pd->cdrw.pkt_free_list);
- } else {
- list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);
- }
-}
-
-static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state)
-{
-#if PACKET_DEBUG > 1
- static const char *state_name[] = {
- "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"
- };
- enum packet_data_state old_state = pkt->state;
- pkt_dbg(2, pd, "pkt %2d : s=%6llx %s -> %s\n",
- pkt->id, (unsigned long long)pkt->sector,
- state_name[old_state], state_name[state]);
-#endif
- pkt->state = state;
-}
-
-/*
- * Scan the work queue to see if we can start a new packet.
- * returns non-zero if any work was done.
- */
-static int pkt_handle_queue(struct pktcdvd_device *pd)
-{
- struct packet_data *pkt, *p;
- struct bio *bio = NULL;
- sector_t zone = 0; /* Suppress gcc warning */
- struct pkt_rb_node *node, *first_node;
- struct rb_node *n;
-
- atomic_set(&pd->scan_queue, 0);
-
- if (list_empty(&pd->cdrw.pkt_free_list)) {
- pkt_dbg(2, pd, "no pkt\n");
- return 0;
- }
-
- /*
- * Try to find a zone we are not already working on.
- */
- spin_lock(&pd->lock);
- first_node = pkt_rbtree_find(pd, pd->current_sector);
- if (!first_node) {
- n = rb_first(&pd->bio_queue);
- if (n)
- first_node = rb_entry(n, struct pkt_rb_node, rb_node);
- }
- node = first_node;
- while (node) {
- bio = node->bio;
- zone = get_zone(bio->bi_iter.bi_sector, pd);
- list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
- if (p->sector == zone) {
- bio = NULL;
- goto try_next_bio;
- }
- }
- break;
-try_next_bio:
- node = pkt_rbtree_next(node);
- if (!node) {
- n = rb_first(&pd->bio_queue);
- if (n)
- node = rb_entry(n, struct pkt_rb_node, rb_node);
- }
- if (node == first_node)
- node = NULL;
- }
- spin_unlock(&pd->lock);
- if (!bio) {
- pkt_dbg(2, pd, "no bio\n");
- return 0;
- }
-
- pkt = pkt_get_packet_data(pd, zone);
-
- pd->current_sector = zone + pd->settings.size;
- pkt->sector = zone;
- BUG_ON(pkt->frames != pd->settings.size >> 2);
- pkt->write_size = 0;
-
- /*
- * Scan work queue for bios in the same zone and link them
- * to this packet.
- */
- spin_lock(&pd->lock);
- pkt_dbg(2, pd, "looking for zone %llx\n", (unsigned long long)zone);
- while ((node = pkt_rbtree_find(pd, zone)) != NULL) {
- bio = node->bio;
- pkt_dbg(2, pd, "found zone=%llx\n", (unsigned long long)
- get_zone(bio->bi_iter.bi_sector, pd));
- if (get_zone(bio->bi_iter.bi_sector, pd) != zone)
- break;
- pkt_rbtree_erase(pd, node);
- spin_lock(&pkt->lock);
- bio_list_add(&pkt->orig_bios, bio);
- pkt->write_size += bio->bi_iter.bi_size / CD_FRAMESIZE;
- spin_unlock(&pkt->lock);
- }
- /* check write congestion marks, and if bio_queue_size is
- * below, wake up any waiters
- */
- if (pd->congested &&
- pd->bio_queue_size <= pd->write_congestion_off) {
- pd->congested = false;
- wake_up_var(&pd->congested);
- }
- spin_unlock(&pd->lock);
-
- pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
- pkt_set_state(pkt, PACKET_WAITING_STATE);
- atomic_set(&pkt->run_sm, 1);
-
- spin_lock(&pd->cdrw.active_list_lock);
- list_add(&pkt->list, &pd->cdrw.pkt_active_list);
- spin_unlock(&pd->cdrw.active_list_lock);
-
- return 1;
-}
-
-/**
- * bio_list_copy_data - copy contents of data buffers from one chain of bios to
- * another
- * @src: source bio list
- * @dst: destination bio list
- *
- * Stops when it reaches the end of either the @src list or @dst list - that is,
- * copies min(src->bi_size, dst->bi_size) bytes (or the equivalent for lists of
- * bios).
- */
-static void bio_list_copy_data(struct bio *dst, struct bio *src)
-{
- struct bvec_iter src_iter = src->bi_iter;
- struct bvec_iter dst_iter = dst->bi_iter;
-
- while (1) {
- if (!src_iter.bi_size) {
- src = src->bi_next;
- if (!src)
- break;
-
- src_iter = src->bi_iter;
- }
-
- if (!dst_iter.bi_size) {
- dst = dst->bi_next;
- if (!dst)
- break;
-
- dst_iter = dst->bi_iter;
- }
-
- bio_copy_data_iter(dst, &dst_iter, src, &src_iter);
- }
-}
-
-/*
- * Assemble a bio to write one packet and queue the bio for processing
- * by the underlying block device.
- */
-static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
-{
- int f;
-
- bio_init(pkt->w_bio, pd->bdev, pkt->w_bio->bi_inline_vecs, pkt->frames,
- REQ_OP_WRITE);
- pkt->w_bio->bi_iter.bi_sector = pkt->sector;
- pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
- pkt->w_bio->bi_private = pkt;
-
- /* XXX: locking? */
- for (f = 0; f < pkt->frames; f++) {
- struct page *page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
- unsigned offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
-
- if (!bio_add_page(pkt->w_bio, page, CD_FRAMESIZE, offset))
- BUG();
- }
- pkt_dbg(2, pd, "vcnt=%d\n", pkt->w_bio->bi_vcnt);
-
- /*
- * Fill-in bvec with data from orig_bios.
- */
- spin_lock(&pkt->lock);
- bio_list_copy_data(pkt->w_bio, pkt->orig_bios.head);
-
- pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
- spin_unlock(&pkt->lock);
-
- pkt_dbg(2, pd, "Writing %d frames for zone %llx\n",
- pkt->write_size, (unsigned long long)pkt->sector);
-
- if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames))
- pkt->cache_valid = 1;
- else
- pkt->cache_valid = 0;
-
- /* Start the write request */
- atomic_set(&pkt->io_wait, 1);
- pkt_queue_bio(pd, pkt->w_bio);
-}
-
-static void pkt_finish_packet(struct packet_data *pkt, blk_status_t status)
-{
- struct bio *bio;
-
- if (status)
- pkt->cache_valid = 0;
-
- /* Finish all bios corresponding to this packet */
- while ((bio = bio_list_pop(&pkt->orig_bios))) {
- bio->bi_status = status;
- bio_endio(bio);
- }
-}
-
-static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
-{
- pkt_dbg(2, pd, "pkt %d\n", pkt->id);
-
- for (;;) {
- switch (pkt->state) {
- case PACKET_WAITING_STATE:
- if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))
- return;
-
- pkt->sleep_time = 0;
- pkt_gather_data(pd, pkt);
- pkt_set_state(pkt, PACKET_READ_WAIT_STATE);
- break;
-
- case PACKET_READ_WAIT_STATE:
- if (atomic_read(&pkt->io_wait) > 0)
- return;
-
- if (atomic_read(&pkt->io_errors) > 0) {
- pkt_set_state(pkt, PACKET_RECOVERY_STATE);
- } else {
- pkt_start_write(pd, pkt);
- }
- break;
-
- case PACKET_WRITE_WAIT_STATE:
- if (atomic_read(&pkt->io_wait) > 0)
- return;
-
- if (!pkt->w_bio->bi_status) {
- pkt_set_state(pkt, PACKET_FINISHED_STATE);
- } else {
- pkt_set_state(pkt, PACKET_RECOVERY_STATE);
- }
- break;
-
- case PACKET_RECOVERY_STATE:
- pkt_dbg(2, pd, "No recovery possible\n");
- pkt_set_state(pkt, PACKET_FINISHED_STATE);
- break;
-
- case PACKET_FINISHED_STATE:
- pkt_finish_packet(pkt, pkt->w_bio->bi_status);
- return;
-
- default:
- BUG();
- break;
- }
- }
-}
-
-static void pkt_handle_packets(struct pktcdvd_device *pd)
-{
- struct packet_data *pkt, *next;
-
- /*
- * Run state machine for active packets
- */
- list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
- if (atomic_read(&pkt->run_sm) > 0) {
- atomic_set(&pkt->run_sm, 0);
- pkt_run_state_machine(pd, pkt);
- }
- }
-
- /*
- * Move no longer active packets to the free list
- */
- spin_lock(&pd->cdrw.active_list_lock);
- list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {
- if (pkt->state == PACKET_FINISHED_STATE) {
- list_del(&pkt->list);
- pkt_put_packet_data(pd, pkt);
- pkt_set_state(pkt, PACKET_IDLE_STATE);
- atomic_set(&pd->scan_queue, 1);
- }
- }
- spin_unlock(&pd->cdrw.active_list_lock);
-}
-
-static void pkt_count_states(struct pktcdvd_device *pd, int *states)
-{
- struct packet_data *pkt;
- int i;
-
- for (i = 0; i < PACKET_NUM_STATES; i++)
- states[i] = 0;
-
- spin_lock(&pd->cdrw.active_list_lock);
- list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
- states[pkt->state]++;
- }
- spin_unlock(&pd->cdrw.active_list_lock);
-}
-
-/*
- * kcdrwd is woken up when writes have been queued for one of our
- * registered devices
- */
-static int kcdrwd(void *foobar)
-{
- struct pktcdvd_device *pd = foobar;
- struct packet_data *pkt;
- long min_sleep_time, residue;
-
- set_user_nice(current, MIN_NICE);
- set_freezable();
-
- for (;;) {
- DECLARE_WAITQUEUE(wait, current);
-
- /*
- * Wait until there is something to do
- */
- add_wait_queue(&pd->wqueue, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- /* Check if we need to run pkt_handle_queue */
- if (atomic_read(&pd->scan_queue) > 0)
- goto work_to_do;
-
- /* Check if we need to run the state machine for some packet */
- list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
- if (atomic_read(&pkt->run_sm) > 0)
- goto work_to_do;
- }
-
- /* Check if we need to process the iosched queues */
- if (atomic_read(&pd->iosched.attention) != 0)
- goto work_to_do;
-
- /* Otherwise, go to sleep */
- if (PACKET_DEBUG > 1) {
- int states[PACKET_NUM_STATES];
- pkt_count_states(pd, states);
- pkt_dbg(2, pd, "i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
- states[0], states[1], states[2],
- states[3], states[4], states[5]);
- }
-
- min_sleep_time = MAX_SCHEDULE_TIMEOUT;
- list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
- if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)
- min_sleep_time = pkt->sleep_time;
- }
-
- pkt_dbg(2, pd, "sleeping\n");
- residue = schedule_timeout(min_sleep_time);
- pkt_dbg(2, pd, "wake up\n");
-
- /* make swsusp happy with our thread */
- try_to_freeze();
-
- list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
- if (!pkt->sleep_time)
- continue;
- pkt->sleep_time -= min_sleep_time - residue;
- if (pkt->sleep_time <= 0) {
- pkt->sleep_time = 0;
- atomic_inc(&pkt->run_sm);
- }
- }
-
- if (kthread_should_stop())
- break;
- }
-work_to_do:
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&pd->wqueue, &wait);
-
- if (kthread_should_stop())
- break;
-
- /*
- * if pkt_handle_queue returns true, we can queue
- * another request.
- */
- while (pkt_handle_queue(pd))
- ;
-
- /*
- * Handle packet state machine
- */
- pkt_handle_packets(pd);
-
- /*
- * Handle iosched queues
- */
- pkt_iosched_process_queue(pd);
- }
-
- return 0;
-}
-
-static void pkt_print_settings(struct pktcdvd_device *pd)
-{
- pkt_info(pd, "%s packets, %u blocks, Mode-%c disc\n",
- pd->settings.fp ? "Fixed" : "Variable",
- pd->settings.size >> 2,
- pd->settings.block_mode == 8 ? '1' : '2');
-}
-
-static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, int page_code, int page_control)
-{
- memset(cgc->cmd, 0, sizeof(cgc->cmd));
-
- cgc->cmd[0] = GPCMD_MODE_SENSE_10;
- cgc->cmd[2] = page_code | (page_control << 6);
- cgc->cmd[7] = cgc->buflen >> 8;
- cgc->cmd[8] = cgc->buflen & 0xff;
- cgc->data_direction = CGC_DATA_READ;
- return pkt_generic_packet(pd, cgc);
-}
-
-static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc)
-{
- memset(cgc->cmd, 0, sizeof(cgc->cmd));
- memset(cgc->buffer, 0, 2);
- cgc->cmd[0] = GPCMD_MODE_SELECT_10;
- cgc->cmd[1] = 0x10; /* PF */
- cgc->cmd[7] = cgc->buflen >> 8;
- cgc->cmd[8] = cgc->buflen & 0xff;
- cgc->data_direction = CGC_DATA_WRITE;
- return pkt_generic_packet(pd, cgc);
-}
-
-static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di)
-{
- struct packet_command cgc;
- int ret;
-
- /* set up command and get the disc info */
- init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DISC_INFO;
- cgc.cmd[8] = cgc.buflen = 2;
- cgc.quiet = 1;
-
- ret = pkt_generic_packet(pd, &cgc);
- if (ret)
- return ret;
-
- /* not all drives have the same disc_info length, so requeue
- * packet with the length the drive tells us it can supply
- */
- cgc.buflen = be16_to_cpu(di->disc_information_length) +
- sizeof(di->disc_information_length);
-
- if (cgc.buflen > sizeof(disc_information))
- cgc.buflen = sizeof(disc_information);
-
- cgc.cmd[8] = cgc.buflen;
- return pkt_generic_packet(pd, &cgc);
-}
-
-static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti)
-{
- struct packet_command cgc;
- int ret;
-
- init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
- cgc.cmd[1] = type & 3;
- cgc.cmd[4] = (track & 0xff00) >> 8;
- cgc.cmd[5] = track & 0xff;
- cgc.cmd[8] = 8;
- cgc.quiet = 1;
-
- ret = pkt_generic_packet(pd, &cgc);
- if (ret)
- return ret;
-
- cgc.buflen = be16_to_cpu(ti->track_information_length) +
- sizeof(ti->track_information_length);
-
- if (cgc.buflen > sizeof(track_information))
- cgc.buflen = sizeof(track_information);
-
- cgc.cmd[8] = cgc.buflen;
- return pkt_generic_packet(pd, &cgc);
-}
-
-static noinline_for_stack int pkt_get_last_written(struct pktcdvd_device *pd,
- long *last_written)
-{
- disc_information di;
- track_information ti;
- __u32 last_track;
- int ret;
-
- ret = pkt_get_disc_info(pd, &di);
- if (ret)
- return ret;
-
- last_track = (di.last_track_msb << 8) | di.last_track_lsb;
- ret = pkt_get_track_info(pd, last_track, 1, &ti);
- if (ret)
- return ret;
-
- /* if this track is blank, try the previous. */
- if (ti.blank) {
- last_track--;
- ret = pkt_get_track_info(pd, last_track, 1, &ti);
- if (ret)
- return ret;
- }
-
- /* if last recorded field is valid, return it. */
- if (ti.lra_v) {
- *last_written = be32_to_cpu(ti.last_rec_address);
- } else {
- /* make it up instead */
- *last_written = be32_to_cpu(ti.track_start) +
- be32_to_cpu(ti.track_size);
- if (ti.free_blocks)
- *last_written -= (be32_to_cpu(ti.free_blocks) + 7);
- }
- return 0;
-}
-
-/*
- * write mode select package based on pd->settings
- */
-static noinline_for_stack int pkt_set_write_settings(struct pktcdvd_device *pd)
-{
- struct packet_command cgc;
- struct scsi_sense_hdr sshdr;
- write_param_page *wp;
- char buffer[128];
- int ret, size;
-
- /* doesn't apply to DVD+RW or DVD-RAM */
- if ((pd->mmc3_profile == 0x1a) || (pd->mmc3_profile == 0x12))
- return 0;
-
- memset(buffer, 0, sizeof(buffer));
- init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);
- cgc.sshdr = &sshdr;
- ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0);
- if (ret) {
- pkt_dump_sense(pd, &cgc);
- return ret;
- }
-
- size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));
- pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);
- if (size > sizeof(buffer))
- size = sizeof(buffer);
-
- /*
- * now get it all
- */
- init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);
- cgc.sshdr = &sshdr;
- ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0);
- if (ret) {
- pkt_dump_sense(pd, &cgc);
- return ret;
- }
-
- /*
- * write page is offset header + block descriptor length
- */
- wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];
-
- wp->fp = pd->settings.fp;
- wp->track_mode = pd->settings.track_mode;
- wp->write_type = pd->settings.write_type;
- wp->data_block_type = pd->settings.block_mode;
-
- wp->multi_session = 0;
-
-#ifdef PACKET_USE_LS
- wp->link_size = 7;
- wp->ls_v = 1;
-#endif
-
- if (wp->data_block_type == PACKET_BLOCK_MODE1) {
- wp->session_format = 0;
- wp->subhdr2 = 0x20;
- } else if (wp->data_block_type == PACKET_BLOCK_MODE2) {
- wp->session_format = 0x20;
- wp->subhdr2 = 8;
-#if 0
- wp->mcn[0] = 0x80;
- memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);
-#endif
- } else {
- /*
- * paranoia
- */
- pkt_err(pd, "write mode wrong %d\n", wp->data_block_type);
- return 1;
- }
- wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
-
- cgc.buflen = cgc.cmd[8] = size;
- ret = pkt_mode_select(pd, &cgc);
- if (ret) {
- pkt_dump_sense(pd, &cgc);
- return ret;
- }
-
- pkt_print_settings(pd);
- return 0;
-}
-
-/*
- * 1 -- we can write to this track, 0 -- we can't
- */
-static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti)
-{
- switch (pd->mmc3_profile) {
- case 0x1a: /* DVD+RW */
- case 0x12: /* DVD-RAM */
- /* The track is always writable on DVD+RW/DVD-RAM */
- return 1;
- default:
- break;
- }
-
- if (!ti->packet || !ti->fp)
- return 0;
-
- /*
- * "good" settings as per Mt Fuji.
- */
- if (ti->rt == 0 && ti->blank == 0)
- return 1;
-
- if (ti->rt == 0 && ti->blank == 1)
- return 1;
-
- if (ti->rt == 1 && ti->blank == 0)
- return 1;
-
- pkt_err(pd, "bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
- return 0;
-}
-
-/*
- * 1 -- we can write to this disc, 0 -- we can't
- */
-static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di)
-{
- switch (pd->mmc3_profile) {
- case 0x0a: /* CD-RW */
- case 0xffff: /* MMC3 not supported */
- break;
- case 0x1a: /* DVD+RW */
- case 0x13: /* DVD-RW */
- case 0x12: /* DVD-RAM */
- return 1;
- default:
- pkt_dbg(2, pd, "Wrong disc profile (%x)\n",
- pd->mmc3_profile);
- return 0;
- }
-
- /*
- * for disc type 0xff we should probably reserve a new track.
- * but i'm not sure, should we leave this to user apps? probably.
- */
- if (di->disc_type == 0xff) {
- pkt_notice(pd, "unknown disc - no track?\n");
- return 0;
- }
-
- if (di->disc_type != 0x20 && di->disc_type != 0) {
- pkt_err(pd, "wrong disc type (%x)\n", di->disc_type);
- return 0;
- }
-
- if (di->erasable == 0) {
- pkt_notice(pd, "disc not erasable\n");
- return 0;
- }
-
- if (di->border_status == PACKET_SESSION_RESERVED) {
- pkt_err(pd, "can't write to last track (reserved)\n");
- return 0;
- }
-
- return 1;
-}
-
-static noinline_for_stack int pkt_probe_settings(struct pktcdvd_device *pd)
-{
- struct packet_command cgc;
- unsigned char buf[12];
- disc_information di;
- track_information ti;
- int ret, track;
-
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
- cgc.cmd[8] = 8;
- ret = pkt_generic_packet(pd, &cgc);
- pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7];
-
- memset(&di, 0, sizeof(disc_information));
- memset(&ti, 0, sizeof(track_information));
-
- ret = pkt_get_disc_info(pd, &di);
- if (ret) {
- pkt_err(pd, "failed get_disc\n");
- return ret;
- }
-
- if (!pkt_writable_disc(pd, &di))
- return -EROFS;
-
- pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR;
-
- track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
- ret = pkt_get_track_info(pd, track, 1, &ti);
- if (ret) {
- pkt_err(pd, "failed get_track\n");
- return ret;
- }
-
- if (!pkt_writable_track(pd, &ti)) {
- pkt_err(pd, "can't write to this track\n");
- return -EROFS;
- }
-
- /*
- * we keep packet size in 512 byte units, makes it easier to
- * deal with request calculations.
- */
- pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
- if (pd->settings.size == 0) {
- pkt_notice(pd, "detected zero packet size!\n");
- return -ENXIO;
- }
- if (pd->settings.size > PACKET_MAX_SECTORS) {
- pkt_err(pd, "packet size is too big\n");
- return -EROFS;
- }
- pd->settings.fp = ti.fp;
- pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1);
-
- if (ti.nwa_v) {
- pd->nwa = be32_to_cpu(ti.next_writable);
- set_bit(PACKET_NWA_VALID, &pd->flags);
- }
-
- /*
- * in theory we could use lra on -RW media as well and just zero
- * blocks that haven't been written yet, but in practice that
- * is just a no-go. we'll use that for -R, naturally.
- */
- if (ti.lra_v) {
- pd->lra = be32_to_cpu(ti.last_rec_address);
- set_bit(PACKET_LRA_VALID, &pd->flags);
- } else {
- pd->lra = 0xffffffff;
- set_bit(PACKET_LRA_VALID, &pd->flags);
- }
-
- /*
- * fine for now
- */
- pd->settings.link_loss = 7;
- pd->settings.write_type = 0; /* packet */
- pd->settings.track_mode = ti.track_mode;
-
- /*
- * mode1 or mode2 disc
- */
- switch (ti.data_mode) {
- case PACKET_MODE1:
- pd->settings.block_mode = PACKET_BLOCK_MODE1;
- break;
- case PACKET_MODE2:
- pd->settings.block_mode = PACKET_BLOCK_MODE2;
- break;
- default:
- pkt_err(pd, "unknown data mode\n");
- return -EROFS;
- }
- return 0;
-}
-
-/*
- * enable/disable write caching on drive
- */
-static noinline_for_stack int pkt_write_caching(struct pktcdvd_device *pd,
- int set)
-{
- struct packet_command cgc;
- struct scsi_sense_hdr sshdr;
- unsigned char buf[64];
- int ret;
-
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.sshdr = &sshdr;
- cgc.buflen = pd->mode_offset + 12;
-
- /*
- * caching mode page might not be there, so quiet this command
- */
- cgc.quiet = 1;
-
- ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0);
- if (ret)
- return ret;
-
- buf[pd->mode_offset + 10] |= (!!set << 2);
-
- cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
- ret = pkt_mode_select(pd, &cgc);
- if (ret) {
- pkt_err(pd, "write caching control failed\n");
- pkt_dump_sense(pd, &cgc);
- } else if (!ret && set)
- pkt_notice(pd, "enabled write caching\n");
- return ret;
-}
-
-static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag)
-{
- struct packet_command cgc;
-
- init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
- cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
- cgc.cmd[4] = lockflag ? 1 : 0;
- return pkt_generic_packet(pd, &cgc);
-}
-
-/*
- * Returns drive maximum write speed
- */
-static noinline_for_stack int pkt_get_max_speed(struct pktcdvd_device *pd,
- unsigned *write_speed)
-{
- struct packet_command cgc;
- struct scsi_sense_hdr sshdr;
- unsigned char buf[256+18];
- unsigned char *cap_buf;
- int ret, offset;
-
- cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
- cgc.sshdr = &sshdr;
-
- ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
- if (ret) {
- cgc.buflen = pd->mode_offset + cap_buf[1] + 2 +
- sizeof(struct mode_page_header);
- ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
- if (ret) {
- pkt_dump_sense(pd, &cgc);
- return ret;
- }
- }
-
- offset = 20; /* Obsoleted field, used by older drives */
- if (cap_buf[1] >= 28)
- offset = 28; /* Current write speed selected */
- if (cap_buf[1] >= 30) {
- /* If the drive reports at least one "Logical Unit Write
- * Speed Performance Descriptor Block", use the information
- * in the first block. (contains the highest speed)
- */
- int num_spdb = (cap_buf[30] << 8) + cap_buf[31];
- if (num_spdb > 0)
- offset = 34;
- }
-
- *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1];
- return 0;
-}
-
-/* These tables from cdrecord - I don't have orange book */
-/* standard speed CD-RW (1-4x) */
-static char clv_to_speed[16] = {
- /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
- 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-/* high speed CD-RW (-10x) */
-static char hs_clv_to_speed[16] = {
- /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
- 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-/* ultra high speed CD-RW */
-static char us_clv_to_speed[16] = {
- /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
- 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0
-};
-
-/*
- * reads the maximum media speed from ATIP
- */
-static noinline_for_stack int pkt_media_speed(struct pktcdvd_device *pd,
- unsigned *speed)
-{
- struct packet_command cgc;
- struct scsi_sense_hdr sshdr;
- unsigned char buf[64];
- unsigned int size, st, sp;
- int ret;
-
- init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ);
- cgc.sshdr = &sshdr;
- cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
- cgc.cmd[1] = 2;
- cgc.cmd[2] = 4; /* READ ATIP */
- cgc.cmd[8] = 2;
- ret = pkt_generic_packet(pd, &cgc);
- if (ret) {
- pkt_dump_sense(pd, &cgc);
- return ret;
- }
- size = ((unsigned int) buf[0]<<8) + buf[1] + 2;
- if (size > sizeof(buf))
- size = sizeof(buf);
-
- init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
- cgc.sshdr = &sshdr;
- cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
- cgc.cmd[1] = 2;
- cgc.cmd[2] = 4;
- cgc.cmd[8] = size;
- ret = pkt_generic_packet(pd, &cgc);
- if (ret) {
- pkt_dump_sense(pd, &cgc);
- return ret;
- }
-
- if (!(buf[6] & 0x40)) {
- pkt_notice(pd, "disc type is not CD-RW\n");
- return 1;
- }
- if (!(buf[6] & 0x4)) {
- pkt_notice(pd, "A1 values on media are not valid, maybe not CDRW?\n");
- return 1;
- }
-
- st = (buf[6] >> 3) & 0x7; /* disc sub-type */
-
- sp = buf[16] & 0xf; /* max speed from ATIP A1 field */
-
- /* Info from cdrecord */
- switch (st) {
- case 0: /* standard speed */
- *speed = clv_to_speed[sp];
- break;
- case 1: /* high speed */
- *speed = hs_clv_to_speed[sp];
- break;
- case 2: /* ultra high speed */
- *speed = us_clv_to_speed[sp];
- break;
- default:
- pkt_notice(pd, "unknown disc sub-type %d\n", st);
- return 1;
- }
- if (*speed) {
- pkt_info(pd, "maximum media speed: %d\n", *speed);
- return 0;
- } else {
- pkt_notice(pd, "unknown speed %d for sub-type %d\n", sp, st);
- return 1;
- }
-}
-
-static noinline_for_stack int pkt_perform_opc(struct pktcdvd_device *pd)
-{
- struct packet_command cgc;
- struct scsi_sense_hdr sshdr;
- int ret;
-
- pkt_dbg(2, pd, "Performing OPC\n");
-
- init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
- cgc.sshdr = &sshdr;
- cgc.timeout = 60*HZ;
- cgc.cmd[0] = GPCMD_SEND_OPC;
- cgc.cmd[1] = 1;
- ret = pkt_generic_packet(pd, &cgc);
- if (ret)
- pkt_dump_sense(pd, &cgc);
- return ret;
-}
-
-static int pkt_open_write(struct pktcdvd_device *pd)
-{
- int ret;
- unsigned int write_speed, media_write_speed, read_speed;
-
- ret = pkt_probe_settings(pd);
- if (ret) {
- pkt_dbg(2, pd, "failed probe\n");
- return ret;
- }
-
- ret = pkt_set_write_settings(pd);
- if (ret) {
- pkt_dbg(1, pd, "failed saving write settings\n");
- return -EIO;
- }
-
- pkt_write_caching(pd, USE_WCACHING);
-
- ret = pkt_get_max_speed(pd, &write_speed);
- if (ret)
- write_speed = 16 * 177;
- switch (pd->mmc3_profile) {
- case 0x13: /* DVD-RW */
- case 0x1a: /* DVD+RW */
- case 0x12: /* DVD-RAM */
- pkt_dbg(1, pd, "write speed %ukB/s\n", write_speed);
- break;
- default:
- ret = pkt_media_speed(pd, &media_write_speed);
- if (ret)
- media_write_speed = 16;
- write_speed = min(write_speed, media_write_speed * 177);
- pkt_dbg(1, pd, "write speed %ux\n", write_speed / 176);
- break;
- }
- read_speed = write_speed;
-
- ret = pkt_set_speed(pd, write_speed, read_speed);
- if (ret) {
- pkt_dbg(1, pd, "couldn't set write speed\n");
- return -EIO;
- }
- pd->write_speed = write_speed;
- pd->read_speed = read_speed;
-
- ret = pkt_perform_opc(pd);
- if (ret) {
- pkt_dbg(1, pd, "Optimum Power Calibration failed\n");
- }
-
- return 0;
-}
-
-/*
- * called at open time.
- */
-static int pkt_open_dev(struct pktcdvd_device *pd, fmode_t write)
-{
- int ret;
- long lba;
- struct request_queue *q;
- struct block_device *bdev;
-
- /*
- * We need to re-open the cdrom device without O_NONBLOCK to be able
- * to read/write from/to it. It is already opened in O_NONBLOCK mode
- * so open should not fail.
- */
- bdev = blkdev_get_by_dev(pd->bdev->bd_dev, FMODE_READ | FMODE_EXCL, pd);
- if (IS_ERR(bdev)) {
- ret = PTR_ERR(bdev);
- goto out;
- }
-
- ret = pkt_get_last_written(pd, &lba);
- if (ret) {
- pkt_err(pd, "pkt_get_last_written failed\n");
- goto out_putdev;
- }
-
- set_capacity(pd->disk, lba << 2);
- set_capacity_and_notify(pd->bdev->bd_disk, lba << 2);
-
- q = bdev_get_queue(pd->bdev);
- if (write) {
- ret = pkt_open_write(pd);
- if (ret)
- goto out_putdev;
- /*
- * Some CDRW drives can not handle writes larger than one packet,
- * even if the size is a multiple of the packet size.
- */
- blk_queue_max_hw_sectors(q, pd->settings.size);
- set_bit(PACKET_WRITABLE, &pd->flags);
- } else {
- pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
- clear_bit(PACKET_WRITABLE, &pd->flags);
- }
-
- ret = pkt_set_segment_merging(pd, q);
- if (ret)
- goto out_putdev;
-
- if (write) {
- if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
- pkt_err(pd, "not enough memory for buffers\n");
- ret = -ENOMEM;
- goto out_putdev;
- }
- pkt_info(pd, "%lukB available on disc\n", lba << 1);
- }
-
- return 0;
-
-out_putdev:
- blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
-out:
- return ret;
-}
-
-/*
- * called when the device is closed. makes sure that the device flushes
- * the internal cache before we close.
- */
-static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
-{
- if (flush && pkt_flush_cache(pd))
- pkt_dbg(1, pd, "not flushing cache\n");
-
- pkt_lock_door(pd, 0);
-
- pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
- blkdev_put(pd->bdev, FMODE_READ | FMODE_EXCL);
-
- pkt_shrink_pktlist(pd);
-}
-
-static struct pktcdvd_device *pkt_find_dev_from_minor(unsigned int dev_minor)
-{
- if (dev_minor >= MAX_WRITERS)
- return NULL;
-
- dev_minor = array_index_nospec(dev_minor, MAX_WRITERS);
- return pkt_devs[dev_minor];
-}
-
-static int pkt_open(struct block_device *bdev, fmode_t mode)
-{
- struct pktcdvd_device *pd = NULL;
- int ret;
-
- mutex_lock(&pktcdvd_mutex);
- mutex_lock(&ctl_mutex);
- pd = pkt_find_dev_from_minor(MINOR(bdev->bd_dev));
- if (!pd) {
- ret = -ENODEV;
- goto out;
- }
- BUG_ON(pd->refcnt < 0);
-
- pd->refcnt++;
- if (pd->refcnt > 1) {
- if ((mode & FMODE_WRITE) &&
- !test_bit(PACKET_WRITABLE, &pd->flags)) {
- ret = -EBUSY;
- goto out_dec;
- }
- } else {
- ret = pkt_open_dev(pd, mode & FMODE_WRITE);
- if (ret)
- goto out_dec;
- /*
- * needed here as well, since ext2 (among others) may change
- * the blocksize at mount time
- */
- set_blocksize(bdev, CD_FRAMESIZE);
- }
-
- mutex_unlock(&ctl_mutex);
- mutex_unlock(&pktcdvd_mutex);
- return 0;
-
-out_dec:
- pd->refcnt--;
-out:
- mutex_unlock(&ctl_mutex);
- mutex_unlock(&pktcdvd_mutex);
- return ret;
-}
-
-static void pkt_close(struct gendisk *disk, fmode_t mode)
-{
- struct pktcdvd_device *pd = disk->private_data;
-
- mutex_lock(&pktcdvd_mutex);
- mutex_lock(&ctl_mutex);
- pd->refcnt--;
- BUG_ON(pd->refcnt < 0);
- if (pd->refcnt == 0) {
- int flush = test_bit(PACKET_WRITABLE, &pd->flags);
- pkt_release_dev(pd, flush);
- }
- mutex_unlock(&ctl_mutex);
- mutex_unlock(&pktcdvd_mutex);
-}
-
-
-static void pkt_end_io_read_cloned(struct bio *bio)
-{
- struct packet_stacked_data *psd = bio->bi_private;
- struct pktcdvd_device *pd = psd->pd;
-
- psd->bio->bi_status = bio->bi_status;
- bio_put(bio);
- bio_endio(psd->bio);
- mempool_free(psd, &psd_pool);
- pkt_bio_finished(pd);
-}
-
-static void pkt_make_request_read(struct pktcdvd_device *pd, struct bio *bio)
-{
- struct bio *cloned_bio =
- bio_alloc_clone(pd->bdev, bio, GFP_NOIO, &pkt_bio_set);
- struct packet_stacked_data *psd = mempool_alloc(&psd_pool, GFP_NOIO);
-
- psd->pd = pd;
- psd->bio = bio;
- cloned_bio->bi_private = psd;
- cloned_bio->bi_end_io = pkt_end_io_read_cloned;
- pd->stats.secs_r += bio_sectors(bio);
- pkt_queue_bio(pd, cloned_bio);
-}
-
-static void pkt_make_request_write(struct request_queue *q, struct bio *bio)
-{
- struct pktcdvd_device *pd = q->queuedata;
- sector_t zone;
- struct packet_data *pkt;
- int was_empty, blocked_bio;
- struct pkt_rb_node *node;
-
- zone = get_zone(bio->bi_iter.bi_sector, pd);
-
- /*
- * If we find a matching packet in state WAITING or READ_WAIT, we can
- * just append this bio to that packet.
- */
- spin_lock(&pd->cdrw.active_list_lock);
- blocked_bio = 0;
- list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
- if (pkt->sector == zone) {
- spin_lock(&pkt->lock);
- if ((pkt->state == PACKET_WAITING_STATE) ||
- (pkt->state == PACKET_READ_WAIT_STATE)) {
- bio_list_add(&pkt->orig_bios, bio);
- pkt->write_size +=
- bio->bi_iter.bi_size / CD_FRAMESIZE;
- if ((pkt->write_size >= pkt->frames) &&
- (pkt->state == PACKET_WAITING_STATE)) {
- atomic_inc(&pkt->run_sm);
- wake_up(&pd->wqueue);
- }
- spin_unlock(&pkt->lock);
- spin_unlock(&pd->cdrw.active_list_lock);
- return;
- } else {
- blocked_bio = 1;
- }
- spin_unlock(&pkt->lock);
- }
- }
- spin_unlock(&pd->cdrw.active_list_lock);
-
- /*
- * Test if there is enough room left in the bio work queue
- * (queue size >= congestion on mark).
- * If not, wait till the work queue size is below the congestion off mark.
- */
- spin_lock(&pd->lock);
- if (pd->write_congestion_on > 0
- && pd->bio_queue_size >= pd->write_congestion_on) {
- struct wait_bit_queue_entry wqe;
-
- init_wait_var_entry(&wqe, &pd->congested, 0);
- for (;;) {
- prepare_to_wait_event(__var_waitqueue(&pd->congested),
- &wqe.wq_entry,
- TASK_UNINTERRUPTIBLE);
- if (pd->bio_queue_size <= pd->write_congestion_off)
- break;
- pd->congested = true;
- spin_unlock(&pd->lock);
- schedule();
- spin_lock(&pd->lock);
- }
- }
- spin_unlock(&pd->lock);
-
- /*
- * No matching packet found. Store the bio in the work queue.
- */
- node = mempool_alloc(&pd->rb_pool, GFP_NOIO);
- node->bio = bio;
- spin_lock(&pd->lock);
- BUG_ON(pd->bio_queue_size < 0);
- was_empty = (pd->bio_queue_size == 0);
- pkt_rbtree_insert(pd, node);
- spin_unlock(&pd->lock);
-
- /*
- * Wake up the worker thread.
- */
- atomic_set(&pd->scan_queue, 1);
- if (was_empty) {
- /* This wake_up is required for correct operation */
- wake_up(&pd->wqueue);
- } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {
- /*
- * This wake up is not required for correct operation,
- * but improves performance in some cases.
- */
- wake_up(&pd->wqueue);
- }
-}
-
-static void pkt_submit_bio(struct bio *bio)
-{
- struct pktcdvd_device *pd = bio->bi_bdev->bd_disk->queue->queuedata;
- struct bio *split;
-
- bio = bio_split_to_limits(bio);
-
- pkt_dbg(2, pd, "start = %6llx stop = %6llx\n",
- (unsigned long long)bio->bi_iter.bi_sector,
- (unsigned long long)bio_end_sector(bio));
-
- /*
- * Clone READ bios so we can have our own bi_end_io callback.
- */
- if (bio_data_dir(bio) == READ) {
- pkt_make_request_read(pd, bio);
- return;
- }
-
- if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
- pkt_notice(pd, "WRITE for ro device (%llu)\n",
- (unsigned long long)bio->bi_iter.bi_sector);
- goto end_io;
- }
-
- if (!bio->bi_iter.bi_size || (bio->bi_iter.bi_size % CD_FRAMESIZE)) {
- pkt_err(pd, "wrong bio size\n");
- goto end_io;
- }
-
- do {
- sector_t zone = get_zone(bio->bi_iter.bi_sector, pd);
- sector_t last_zone = get_zone(bio_end_sector(bio) - 1, pd);
-
- if (last_zone != zone) {
- BUG_ON(last_zone != zone + pd->settings.size);
-
- split = bio_split(bio, last_zone -
- bio->bi_iter.bi_sector,
- GFP_NOIO, &pkt_bio_set);
- bio_chain(split, bio);
- } else {
- split = bio;
- }
-
- pkt_make_request_write(bio->bi_bdev->bd_disk->queue, split);
- } while (split != bio);
-
- return;
-end_io:
- bio_io_error(bio);
-}
-
-static void pkt_init_queue(struct pktcdvd_device *pd)
-{
- struct request_queue *q = pd->disk->queue;
-
- blk_queue_logical_block_size(q, CD_FRAMESIZE);
- blk_queue_max_hw_sectors(q, PACKET_MAX_SECTORS);
- q->queuedata = pd;
-}
-
-static int pkt_seq_show(struct seq_file *m, void *p)
-{
- struct pktcdvd_device *pd = m->private;
- char *msg;
- int states[PACKET_NUM_STATES];
-
- seq_printf(m, "Writer %s mapped to %pg:\n", pd->name, pd->bdev);
-
- seq_printf(m, "\nSettings:\n");
- seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
-
- if (pd->settings.write_type == 0)
- msg = "Packet";
- else
- msg = "Unknown";
- seq_printf(m, "\twrite type:\t\t%s\n", msg);
-
- seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
- seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
-
- seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
-
- if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
- msg = "Mode 1";
- else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)
- msg = "Mode 2";
- else
- msg = "Unknown";
- seq_printf(m, "\tblock mode:\t\t%s\n", msg);
-
- seq_printf(m, "\nStatistics:\n");
- seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
- seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
- seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
- seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
- seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
-
- seq_printf(m, "\nMisc:\n");
- seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
- seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
- seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);
- seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);
- seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
- seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
-
- seq_printf(m, "\nQueue state:\n");
- seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);
- seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));
- seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);
-
- pkt_count_states(pd, states);
- seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
- states[0], states[1], states[2], states[3], states[4], states[5]);
-
- seq_printf(m, "\twrite congestion marks:\toff=%d on=%d\n",
- pd->write_congestion_off,
- pd->write_congestion_on);
- return 0;
-}
-
-static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
-{
- int i;
- struct block_device *bdev;
- struct scsi_device *sdev;
-
- if (pd->pkt_dev == dev) {
- pkt_err(pd, "recursive setup not allowed\n");
- return -EBUSY;
- }
- for (i = 0; i < MAX_WRITERS; i++) {
- struct pktcdvd_device *pd2 = pkt_devs[i];
- if (!pd2)
- continue;
- if (pd2->bdev->bd_dev == dev) {
- pkt_err(pd, "%pg already setup\n", pd2->bdev);
- return -EBUSY;
- }
- if (pd2->pkt_dev == dev) {
- pkt_err(pd, "can't chain pktcdvd devices\n");
- return -EBUSY;
- }
- }
-
- bdev = blkdev_get_by_dev(dev, FMODE_READ | FMODE_NDELAY, NULL);
- if (IS_ERR(bdev))
- return PTR_ERR(bdev);
- sdev = scsi_device_from_queue(bdev->bd_disk->queue);
- if (!sdev) {
- blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
- return -EINVAL;
- }
- put_device(&sdev->sdev_gendev);
-
- /* This is safe, since we have a reference from open(). */
- __module_get(THIS_MODULE);
-
- pd->bdev = bdev;
- set_blocksize(bdev, CD_FRAMESIZE);
-
- pkt_init_queue(pd);
-
- atomic_set(&pd->cdrw.pending_bios, 0);
- pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
- if (IS_ERR(pd->cdrw.thread)) {
- pkt_err(pd, "can't start kernel thread\n");
- goto out_mem;
- }
-
- proc_create_single_data(pd->name, 0, pkt_proc, pkt_seq_show, pd);
- pkt_dbg(1, pd, "writer mapped to %pg\n", bdev);
- return 0;
-
-out_mem:
- blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
- /* This is safe: open() is still holding a reference. */
- module_put(THIS_MODULE);
- return -ENOMEM;
-}
-
-static int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
-{
- struct pktcdvd_device *pd = bdev->bd_disk->private_data;
- int ret;
-
- pkt_dbg(2, pd, "cmd %x, dev %d:%d\n",
- cmd, MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
-
- mutex_lock(&pktcdvd_mutex);
- switch (cmd) {
- case CDROMEJECT:
- /*
- * The door gets locked when the device is opened, so we
- * have to unlock it or else the eject command fails.
- */
- if (pd->refcnt == 1)
- pkt_lock_door(pd, 0);
- fallthrough;
- /*
- * forward selected CDROM ioctls to CD-ROM, for UDF
- */
- case CDROMMULTISESSION:
- case CDROMREADTOCENTRY:
- case CDROM_LAST_WRITTEN:
- case CDROM_SEND_PACKET:
- case SCSI_IOCTL_SEND_COMMAND:
- if (!bdev->bd_disk->fops->ioctl)
- ret = -ENOTTY;
- else
- ret = bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg);
- break;
- default:
- pkt_dbg(2, pd, "Unknown ioctl (%x)\n", cmd);
- ret = -ENOTTY;
- }
- mutex_unlock(&pktcdvd_mutex);
-
- return ret;
-}
-
-static unsigned int pkt_check_events(struct gendisk *disk,
- unsigned int clearing)
-{
- struct pktcdvd_device *pd = disk->private_data;
- struct gendisk *attached_disk;
-
- if (!pd)
- return 0;
- if (!pd->bdev)
- return 0;
- attached_disk = pd->bdev->bd_disk;
- if (!attached_disk || !attached_disk->fops->check_events)
- return 0;
- return attached_disk->fops->check_events(attached_disk, clearing);
-}
-
-static char *pkt_devnode(struct gendisk *disk, umode_t *mode)
-{
- return kasprintf(GFP_KERNEL, "pktcdvd/%s", disk->disk_name);
-}
-
-static const struct block_device_operations pktcdvd_ops = {
- .owner = THIS_MODULE,
- .submit_bio = pkt_submit_bio,
- .open = pkt_open,
- .release = pkt_close,
- .ioctl = pkt_ioctl,
- .compat_ioctl = blkdev_compat_ptr_ioctl,
- .check_events = pkt_check_events,
- .devnode = pkt_devnode,
-};
-
-/*
- * Set up mapping from pktcdvd device to CD-ROM device.
- */
-static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev)
-{
- int idx;
- int ret = -ENOMEM;
- struct pktcdvd_device *pd;
- struct gendisk *disk;
-
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
- for (idx = 0; idx < MAX_WRITERS; idx++)
- if (!pkt_devs[idx])
- break;
- if (idx == MAX_WRITERS) {
- pr_err("max %d writers supported\n", MAX_WRITERS);
- ret = -EBUSY;
- goto out_mutex;
- }
-
- pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
- if (!pd)
- goto out_mutex;
-
- ret = mempool_init_kmalloc_pool(&pd->rb_pool, PKT_RB_POOL_SIZE,
- sizeof(struct pkt_rb_node));
- if (ret)
- goto out_mem;
-
- INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
- INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
- spin_lock_init(&pd->cdrw.active_list_lock);
-
- spin_lock_init(&pd->lock);
- spin_lock_init(&pd->iosched.lock);
- bio_list_init(&pd->iosched.read_queue);
- bio_list_init(&pd->iosched.write_queue);
- sprintf(pd->name, DRIVER_NAME"%d", idx);
- init_waitqueue_head(&pd->wqueue);
- pd->bio_queue = RB_ROOT;
-
- pd->write_congestion_on = write_congestion_on;
- pd->write_congestion_off = write_congestion_off;
-
- ret = -ENOMEM;
- disk = blk_alloc_disk(NUMA_NO_NODE);
- if (!disk)
- goto out_mem;
- pd->disk = disk;
- disk->major = pktdev_major;
- disk->first_minor = idx;
- disk->minors = 1;
- disk->fops = &pktcdvd_ops;
- disk->flags = GENHD_FL_REMOVABLE | GENHD_FL_NO_PART;
- strcpy(disk->disk_name, pd->name);
- disk->private_data = pd;
-
- pd->pkt_dev = MKDEV(pktdev_major, idx);
- ret = pkt_new_dev(pd, dev);
- if (ret)
- goto out_mem2;
-
- /* inherit events of the host device */
- disk->events = pd->bdev->bd_disk->events;
-
- ret = add_disk(disk);
- if (ret)
- goto out_mem2;
-
- pkt_sysfs_dev_new(pd);
- pkt_debugfs_dev_new(pd);
-
- pkt_devs[idx] = pd;
- if (pkt_dev)
- *pkt_dev = pd->pkt_dev;
-
- mutex_unlock(&ctl_mutex);
- return 0;
-
-out_mem2:
- put_disk(disk);
-out_mem:
- mempool_exit(&pd->rb_pool);
- kfree(pd);
-out_mutex:
- mutex_unlock(&ctl_mutex);
- pr_err("setup of pktcdvd device failed\n");
- return ret;
-}
-
-/*
- * Tear down mapping from pktcdvd device to CD-ROM device.
- */
-static int pkt_remove_dev(dev_t pkt_dev)
-{
- struct pktcdvd_device *pd;
- int idx;
- int ret = 0;
-
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
- for (idx = 0; idx < MAX_WRITERS; idx++) {
- pd = pkt_devs[idx];
- if (pd && (pd->pkt_dev == pkt_dev))
- break;
- }
- if (idx == MAX_WRITERS) {
- pr_debug("dev not setup\n");
- ret = -ENXIO;
- goto out;
- }
-
- if (pd->refcnt > 0) {
- ret = -EBUSY;
- goto out;
- }
- if (!IS_ERR(pd->cdrw.thread))
- kthread_stop(pd->cdrw.thread);
-
- pkt_devs[idx] = NULL;
-
- pkt_debugfs_dev_remove(pd);
- pkt_sysfs_dev_remove(pd);
-
- blkdev_put(pd->bdev, FMODE_READ | FMODE_NDELAY);
-
- remove_proc_entry(pd->name, pkt_proc);
- pkt_dbg(1, pd, "writer unmapped\n");
-
- del_gendisk(pd->disk);
- put_disk(pd->disk);
-
- mempool_exit(&pd->rb_pool);
- kfree(pd);
-
- /* This is safe: open() is still holding a reference. */
- module_put(THIS_MODULE);
-
-out:
- mutex_unlock(&ctl_mutex);
- return ret;
-}
-
-static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
-{
- struct pktcdvd_device *pd;
-
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
- pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
- if (pd) {
- ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
- ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
- } else {
- ctrl_cmd->dev = 0;
- ctrl_cmd->pkt_dev = 0;
- }
- ctrl_cmd->num_devices = MAX_WRITERS;
-
- mutex_unlock(&ctl_mutex);
-}
-
-static long pkt_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- struct pkt_ctrl_command ctrl_cmd;
- int ret = 0;
- dev_t pkt_dev = 0;
-
- if (cmd != PACKET_CTRL_CMD)
- return -ENOTTY;
-
- if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))
- return -EFAULT;
-
- switch (ctrl_cmd.command) {
- case PKT_CTRL_CMD_SETUP:
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev);
- ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev);
- break;
- case PKT_CTRL_CMD_TEARDOWN:
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev));
- break;
- case PKT_CTRL_CMD_STATUS:
- pkt_get_status(&ctrl_cmd);
- break;
- default:
- return -ENOTTY;
- }
-
- if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command)))
- return -EFAULT;
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-static long pkt_ctl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- return pkt_ctl_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
-
-static const struct file_operations pkt_ctl_fops = {
- .open = nonseekable_open,
- .unlocked_ioctl = pkt_ctl_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = pkt_ctl_compat_ioctl,
-#endif
- .owner = THIS_MODULE,
- .llseek = no_llseek,
-};
-
-static struct miscdevice pkt_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DRIVER_NAME,
- .nodename = "pktcdvd/control",
- .fops = &pkt_ctl_fops
-};
-
-static int __init pkt_init(void)
-{
- int ret;
-
- mutex_init(&ctl_mutex);
-
- ret = mempool_init_kmalloc_pool(&psd_pool, PSD_POOL_SIZE,
- sizeof(struct packet_stacked_data));
- if (ret)
- return ret;
- ret = bioset_init(&pkt_bio_set, BIO_POOL_SIZE, 0, 0);
- if (ret) {
- mempool_exit(&psd_pool);
- return ret;
- }
-
- ret = register_blkdev(pktdev_major, DRIVER_NAME);
- if (ret < 0) {
- pr_err("unable to register block device\n");
- goto out2;
- }
- if (!pktdev_major)
- pktdev_major = ret;
-
- ret = pkt_sysfs_init();
- if (ret)
- goto out;
-
- pkt_debugfs_init();
-
- ret = misc_register(&pkt_misc);
- if (ret) {
- pr_err("unable to register misc device\n");
- goto out_misc;
- }
-
- pkt_proc = proc_mkdir("driver/"DRIVER_NAME, NULL);
-
- return 0;
-
-out_misc:
- pkt_debugfs_cleanup();
- pkt_sysfs_cleanup();
-out:
- unregister_blkdev(pktdev_major, DRIVER_NAME);
-out2:
- mempool_exit(&psd_pool);
- bioset_exit(&pkt_bio_set);
- return ret;
-}
-
-static void __exit pkt_exit(void)
-{
- remove_proc_entry("driver/"DRIVER_NAME, NULL);
- misc_deregister(&pkt_misc);
-
- pkt_debugfs_cleanup();
- pkt_sysfs_cleanup();
-
- unregister_blkdev(pktdev_major, DRIVER_NAME);
- mempool_exit(&psd_pool);
- bioset_exit(&pkt_bio_set);
-}
-
-MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
-MODULE_AUTHOR("Jens Axboe <axboe@suse.de>");
-MODULE_LICENSE("GPL");
-
-module_init(pkt_init);
-module_exit(pkt_exit);
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index 36d7b36c60c7..8892f218a814 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -85,10 +85,14 @@ static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
struct bio_vec bvec;
rq_for_each_segment(bvec, req, iter) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %u sectors from %llu\n",
+ __func__, __LINE__, bio_sectors(iter.bio),
+ iter.bio->bi_iter.bi_sector);
if (gather)
memcpy_from_bvec(dev->bounce_buf + offset, &bvec);
else
memcpy_to_bvec(&bvec, dev->bounce_buf + offset);
+ offset += bvec.bv_len;
}
}
@@ -382,7 +386,15 @@ static int ps3disk_probe(struct ps3_system_bus_device *_dev)
struct ps3disk_private *priv;
int error;
unsigned int devidx;
- struct request_queue *queue;
+ struct queue_limits lim = {
+ .logical_block_size = dev->blk_size,
+ .max_hw_sectors = BOUNCE_SIZE >> 9,
+ .max_segments = -1,
+ .max_segment_size = BOUNCE_SIZE,
+ .dma_alignment = dev->blk_size - 1,
+ .features = BLK_FEAT_WRITE_CACHE |
+ BLK_FEAT_ROTATIONAL,
+ };
struct gendisk *gendisk;
if (dev->blk_size < 512) {
@@ -426,12 +438,11 @@ static int ps3disk_probe(struct ps3_system_bus_device *_dev)
ps3disk_identify(dev);
- error = blk_mq_alloc_sq_tag_set(&priv->tag_set, &ps3disk_mq_ops, 1,
- BLK_MQ_F_SHOULD_MERGE);
+ error = blk_mq_alloc_sq_tag_set(&priv->tag_set, &ps3disk_mq_ops, 1, 0);
if (error)
goto fail_teardown;
- gendisk = blk_mq_alloc_disk(&priv->tag_set, dev);
+ gendisk = blk_mq_alloc_disk(&priv->tag_set, &lim, dev);
if (IS_ERR(gendisk)) {
dev_err(&dev->sbd.core, "%s:%u: blk_mq_alloc_disk failed\n",
__func__, __LINE__);
@@ -439,17 +450,6 @@ static int ps3disk_probe(struct ps3_system_bus_device *_dev)
goto fail_free_tag_set;
}
- queue = gendisk->queue;
-
- blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
- blk_queue_dma_alignment(queue, dev->blk_size-1);
- blk_queue_logical_block_size(queue, dev->blk_size);
-
- blk_queue_write_cache(queue, true, false);
-
- blk_queue_max_segments(queue, -1);
- blk_queue_max_segment_size(queue, dev->bounce_size);
-
priv->gendisk = gendisk;
gendisk->major = ps3disk_major;
gendisk->first_minor = devidx * PS3DISK_MINORS;
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index e1d080f680ed..bdcf083b45e2 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -586,8 +586,6 @@ static void ps3vram_submit_bio(struct bio *bio)
dev_dbg(&dev->core, "%s\n", __func__);
- bio = bio_split_to_limits(bio);
-
spin_lock_irq(&priv->lock);
busy = !bio_list_empty(&priv->list);
bio_list_add(&priv->list, bio);
@@ -732,10 +730,10 @@ static int ps3vram_probe(struct ps3_system_bus_device *dev)
ps3vram_proc_init(dev);
- gendisk = blk_alloc_disk(NUMA_NO_NODE);
- if (!gendisk) {
+ gendisk = blk_alloc_disk(NULL, NUMA_NO_NODE);
+ if (IS_ERR(gendisk)) {
dev_err(&dev->core, "blk_alloc_disk failed\n");
- error = -ENOMEM;
+ error = PTR_ERR(gendisk);
goto out_cache_cleanup;
}
@@ -745,11 +743,8 @@ static int ps3vram_probe(struct ps3_system_bus_device *dev)
gendisk->flags |= GENHD_FL_NO_PART;
gendisk->fops = &ps3vram_fops;
gendisk->private_data = dev;
- strlcpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name));
+ strscpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name));
set_capacity(gendisk, priv->size >> 9);
- blk_queue_max_segments(gendisk->queue, BLK_MAX_SEGMENTS);
- blk_queue_max_segment_size(gendisk->queue, BLK_MAX_SEGMENT_SIZE);
- blk_queue_max_hw_sectors(gendisk->queue, BLK_SAFE_MAX_SECTORS);
dev_info(&dev->core, "%s: Using %llu MiB of GPU memory\n",
gendisk->disk_name, get_capacity(gendisk) >> 11);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index f9e39301c4af..af0e21149dbc 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -362,7 +362,7 @@ enum rbd_watch_state {
enum rbd_lock_state {
RBD_LOCK_STATE_UNLOCKED,
RBD_LOCK_STATE_LOCKED,
- RBD_LOCK_STATE_RELEASING,
+ RBD_LOCK_STATE_QUIESCING,
};
/* WatchNotify::ClientId */
@@ -422,7 +422,7 @@ struct rbd_device {
struct list_head running_list;
struct completion acquire_wait;
int acquire_err;
- struct completion releasing_wait;
+ struct completion quiescing_wait;
spinlock_t object_map_lock;
u8 *object_map;
@@ -491,12 +491,12 @@ static bool single_major = true;
module_param(single_major, bool, 0444);
MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: true)");
-static ssize_t add_store(struct bus_type *bus, const char *buf, size_t count);
-static ssize_t remove_store(struct bus_type *bus, const char *buf,
+static ssize_t add_store(const struct bus_type *bus, const char *buf, size_t count);
+static ssize_t remove_store(const struct bus_type *bus, const char *buf,
size_t count);
-static ssize_t add_single_major_store(struct bus_type *bus, const char *buf,
+static ssize_t add_single_major_store(const struct bus_type *bus, const char *buf,
size_t count);
-static ssize_t remove_single_major_store(struct bus_type *bus, const char *buf,
+static ssize_t remove_single_major_store(const struct bus_type *bus, const char *buf,
size_t count);
static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth);
@@ -525,7 +525,7 @@ static bool __rbd_is_lock_owner(struct rbd_device *rbd_dev)
lockdep_assert_held(&rbd_dev->lock_rwsem);
return rbd_dev->lock_state == RBD_LOCK_STATE_LOCKED ||
- rbd_dev->lock_state == RBD_LOCK_STATE_RELEASING;
+ rbd_dev->lock_state == RBD_LOCK_STATE_QUIESCING;
}
static bool rbd_is_lock_owner(struct rbd_device *rbd_dev)
@@ -538,7 +538,7 @@ static bool rbd_is_lock_owner(struct rbd_device *rbd_dev)
return is_lock_owner;
}
-static ssize_t supported_features_show(struct bus_type *bus, char *buf)
+static ssize_t supported_features_show(const struct bus_type *bus, char *buf)
{
return sprintf(buf, "0x%llx\n", RBD_FEATURES_SUPPORTED);
}
@@ -575,7 +575,7 @@ static const struct attribute_group rbd_bus_group = {
};
__ATTRIBUTE_GROUPS(rbd_bus);
-static struct bus_type rbd_bus_type = {
+static const struct bus_type rbd_bus_type = {
.name = "rbd",
.bus_groups = rbd_bus_groups,
};
@@ -632,9 +632,8 @@ void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...)
static void rbd_dev_remove_parent(struct rbd_device *rbd_dev);
static int rbd_dev_refresh(struct rbd_device *rbd_dev);
-static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev);
-static int rbd_dev_header_info(struct rbd_device *rbd_dev);
-static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev);
+static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev,
+ struct rbd_image_header *header);
static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev,
u64 snap_id);
static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
@@ -660,9 +659,9 @@ static bool pending_result_dec(struct pending_result *pending, int *result)
return true;
}
-static int rbd_open(struct block_device *bdev, fmode_t mode)
+static int rbd_open(struct gendisk *disk, blk_mode_t mode)
{
- struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
+ struct rbd_device *rbd_dev = disk->private_data;
bool removing = false;
spin_lock_irq(&rbd_dev->lock);
@@ -679,7 +678,7 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static void rbd_release(struct gendisk *disk, fmode_t mode)
+static void rbd_release(struct gendisk *disk)
{
struct rbd_device *rbd_dev = disk->private_data;
unsigned long open_count_before;
@@ -995,15 +994,24 @@ static void rbd_init_layout(struct rbd_device *rbd_dev)
RCU_INIT_POINTER(rbd_dev->layout.pool_ns, NULL);
}
+static void rbd_image_header_cleanup(struct rbd_image_header *header)
+{
+ kfree(header->object_prefix);
+ ceph_put_snap_context(header->snapc);
+ kfree(header->snap_sizes);
+ kfree(header->snap_names);
+
+ memset(header, 0, sizeof(*header));
+}
+
/*
* Fill an rbd image header with information from the given format 1
* on-disk header.
*/
-static int rbd_header_from_disk(struct rbd_device *rbd_dev,
- struct rbd_image_header_ondisk *ondisk)
+static int rbd_header_from_disk(struct rbd_image_header *header,
+ struct rbd_image_header_ondisk *ondisk,
+ bool first_time)
{
- struct rbd_image_header *header = &rbd_dev->header;
- bool first_time = header->object_prefix == NULL;
struct ceph_snap_context *snapc;
char *object_prefix = NULL;
char *snap_names = NULL;
@@ -1070,11 +1078,6 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev,
if (first_time) {
header->object_prefix = object_prefix;
header->obj_order = ondisk->options.order;
- rbd_init_layout(rbd_dev);
- } else {
- ceph_put_snap_context(header->snapc);
- kfree(header->snap_names);
- kfree(header->snap_sizes);
}
/* The remaining fields always get updated (when we refresh) */
@@ -1334,14 +1337,30 @@ static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req)
/*
* Must be called after rbd_obj_calc_img_extents().
*/
-static bool rbd_obj_copyup_enabled(struct rbd_obj_request *obj_req)
+static void rbd_obj_set_copyup_enabled(struct rbd_obj_request *obj_req)
{
- if (!obj_req->num_img_extents ||
- (rbd_obj_is_entire(obj_req) &&
- !obj_req->img_request->snapc->num_snaps))
- return false;
+ rbd_assert(obj_req->img_request->snapc);
- return true;
+ if (obj_req->img_request->op_type == OBJ_OP_DISCARD) {
+ dout("%s %p objno %llu discard\n", __func__, obj_req,
+ obj_req->ex.oe_objno);
+ return;
+ }
+
+ if (!obj_req->num_img_extents) {
+ dout("%s %p objno %llu not overlapping\n", __func__, obj_req,
+ obj_req->ex.oe_objno);
+ return;
+ }
+
+ if (rbd_obj_is_entire(obj_req) &&
+ !obj_req->img_request->snapc->num_snaps) {
+ dout("%s %p objno %llu entire\n", __func__, obj_req,
+ obj_req->ex.oe_objno);
+ return;
+ }
+
+ obj_req->flags |= RBD_OBJ_FLAG_COPYUP_ENABLED;
}
static u64 rbd_obj_img_extents_bytes(struct rbd_obj_request *obj_req)
@@ -1442,6 +1461,7 @@ __rbd_obj_add_osd_request(struct rbd_obj_request *obj_req,
static struct ceph_osd_request *
rbd_obj_add_osd_request(struct rbd_obj_request *obj_req, int num_ops)
{
+ rbd_assert(obj_req->img_request->snapc);
return __rbd_obj_add_osd_request(obj_req, obj_req->img_request->snapc,
num_ops);
}
@@ -1578,15 +1598,18 @@ static void rbd_img_request_init(struct rbd_img_request *img_request,
mutex_init(&img_request->state_mutex);
}
+/*
+ * Only snap_id is captured here, for reads. For writes, snapshot
+ * context is captured in rbd_img_object_requests() after exclusive
+ * lock is ensured to be held.
+ */
static void rbd_img_capture_header(struct rbd_img_request *img_req)
{
struct rbd_device *rbd_dev = img_req->rbd_dev;
lockdep_assert_held(&rbd_dev->header_rwsem);
- if (rbd_img_is_write(img_req))
- img_req->snapc = ceph_get_snap_context(rbd_dev->header.snapc);
- else
+ if (!rbd_img_is_write(img_req))
img_req->snap_id = rbd_dev->spec->snap_id;
if (rbd_dev_parent_get(rbd_dev))
@@ -2233,9 +2256,6 @@ static int rbd_obj_init_write(struct rbd_obj_request *obj_req)
if (ret)
return ret;
- if (rbd_obj_copyup_enabled(obj_req))
- obj_req->flags |= RBD_OBJ_FLAG_COPYUP_ENABLED;
-
obj_req->write_state = RBD_OBJ_WRITE_START;
return 0;
}
@@ -2341,8 +2361,6 @@ static int rbd_obj_init_zeroout(struct rbd_obj_request *obj_req)
if (ret)
return ret;
- if (rbd_obj_copyup_enabled(obj_req))
- obj_req->flags |= RBD_OBJ_FLAG_COPYUP_ENABLED;
if (!obj_req->num_img_extents) {
obj_req->flags |= RBD_OBJ_FLAG_NOOP_FOR_NONEXISTENT;
if (rbd_obj_is_entire(obj_req))
@@ -3068,13 +3086,12 @@ static int setup_copyup_bvecs(struct rbd_obj_request *obj_req, u64 obj_overlap)
for (i = 0; i < obj_req->copyup_bvec_count; i++) {
unsigned int len = min(obj_overlap, (u64)PAGE_SIZE);
+ struct page *page = alloc_page(GFP_NOIO);
- obj_req->copyup_bvecs[i].bv_page = alloc_page(GFP_NOIO);
- if (!obj_req->copyup_bvecs[i].bv_page)
+ if (!page)
return -ENOMEM;
- obj_req->copyup_bvecs[i].bv_offset = 0;
- obj_req->copyup_bvecs[i].bv_len = len;
+ bvec_set_page(&obj_req->copyup_bvecs[i], page, len, 0);
obj_overlap -= len;
}
@@ -3287,6 +3304,7 @@ again:
case RBD_OBJ_WRITE_START:
rbd_assert(!*result);
+ rbd_obj_set_copyup_enabled(obj_req);
if (rbd_obj_write_is_noop(obj_req))
return true;
@@ -3434,17 +3452,19 @@ static bool rbd_lock_add_request(struct rbd_img_request *img_req)
static void rbd_lock_del_request(struct rbd_img_request *img_req)
{
struct rbd_device *rbd_dev = img_req->rbd_dev;
- bool need_wakeup;
+ bool need_wakeup = false;
lockdep_assert_held(&rbd_dev->lock_rwsem);
spin_lock(&rbd_dev->lock_lists_lock);
- rbd_assert(!list_empty(&img_req->lock_item));
- list_del_init(&img_req->lock_item);
- need_wakeup = (rbd_dev->lock_state == RBD_LOCK_STATE_RELEASING &&
- list_empty(&rbd_dev->running_list));
+ if (!list_empty(&img_req->lock_item)) {
+ rbd_assert(!list_empty(&rbd_dev->running_list));
+ list_del_init(&img_req->lock_item);
+ need_wakeup = (rbd_dev->lock_state == RBD_LOCK_STATE_QUIESCING &&
+ list_empty(&rbd_dev->running_list));
+ }
spin_unlock(&rbd_dev->lock_lists_lock);
if (need_wakeup)
- complete(&rbd_dev->releasing_wait);
+ complete(&rbd_dev->quiescing_wait);
}
static int rbd_img_exclusive_lock(struct rbd_img_request *img_req)
@@ -3457,11 +3477,6 @@ static int rbd_img_exclusive_lock(struct rbd_img_request *img_req)
if (rbd_lock_add_request(img_req))
return 1;
- if (rbd_dev->opts->exclusive) {
- WARN_ON(1); /* lock got released? */
- return -EROFS;
- }
-
/*
* Note the use of mod_delayed_work() in rbd_acquire_lock()
* and cancel_delayed_work() in wake_lock_waiters().
@@ -3473,9 +3488,19 @@ static int rbd_img_exclusive_lock(struct rbd_img_request *img_req)
static void rbd_img_object_requests(struct rbd_img_request *img_req)
{
+ struct rbd_device *rbd_dev = img_req->rbd_dev;
struct rbd_obj_request *obj_req;
rbd_assert(!img_req->pending.result && !img_req->pending.num_pending);
+ rbd_assert(!need_exclusive_lock(img_req) ||
+ __rbd_is_lock_owner(rbd_dev));
+
+ if (rbd_img_is_write(img_req)) {
+ rbd_assert(!img_req->snapc);
+ down_read(&rbd_dev->header_rwsem);
+ img_req->snapc = ceph_get_snap_context(rbd_dev->header.snapc);
+ up_read(&rbd_dev->header_rwsem);
+ }
for_each_obj_request(img_req, obj_req) {
int result = 0;
@@ -3493,7 +3518,6 @@ static void rbd_img_object_requests(struct rbd_img_request *img_req)
static bool rbd_img_advance(struct rbd_img_request *img_req, int *result)
{
- struct rbd_device *rbd_dev = img_req->rbd_dev;
int ret;
again:
@@ -3514,9 +3538,6 @@ again:
if (*result)
return true;
- rbd_assert(!need_exclusive_lock(img_req) ||
- __rbd_is_lock_owner(rbd_dev));
-
rbd_img_object_requests(img_req);
if (!img_req->pending.num_pending) {
*result = img_req->pending.result;
@@ -3654,7 +3675,7 @@ static int rbd_lock(struct rbd_device *rbd_dev)
ret = ceph_cls_lock(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc,
RBD_LOCK_NAME, CEPH_CLS_LOCK_EXCLUSIVE, cookie,
RBD_LOCK_TAG, "", 0);
- if (ret)
+ if (ret && ret != -EEXIST)
return ret;
__rbd_lock(rbd_dev, cookie);
@@ -3818,61 +3839,97 @@ static void wake_lock_waiters(struct rbd_device *rbd_dev, int result)
return;
}
- list_for_each_entry(img_req, &rbd_dev->acquiring_list, lock_item) {
+ while (!list_empty(&rbd_dev->acquiring_list)) {
+ img_req = list_first_entry(&rbd_dev->acquiring_list,
+ struct rbd_img_request, lock_item);
mutex_lock(&img_req->state_mutex);
rbd_assert(img_req->state == RBD_IMG_EXCLUSIVE_LOCK);
+ if (!result)
+ list_move_tail(&img_req->lock_item,
+ &rbd_dev->running_list);
+ else
+ list_del_init(&img_req->lock_item);
rbd_img_schedule(img_req, result);
mutex_unlock(&img_req->state_mutex);
}
+}
+
+static bool locker_equal(const struct ceph_locker *lhs,
+ const struct ceph_locker *rhs)
+{
+ return lhs->id.name.type == rhs->id.name.type &&
+ lhs->id.name.num == rhs->id.name.num &&
+ !strcmp(lhs->id.cookie, rhs->id.cookie) &&
+ ceph_addr_equal_no_type(&lhs->info.addr, &rhs->info.addr);
+}
- list_splice_tail_init(&rbd_dev->acquiring_list, &rbd_dev->running_list);
+static void free_locker(struct ceph_locker *locker)
+{
+ if (locker)
+ ceph_free_lockers(locker, 1);
}
-static int get_lock_owner_info(struct rbd_device *rbd_dev,
- struct ceph_locker **lockers, u32 *num_lockers)
+static struct ceph_locker *get_lock_owner_info(struct rbd_device *rbd_dev)
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
+ struct ceph_locker *lockers;
+ u32 num_lockers;
u8 lock_type;
char *lock_tag;
+ u64 handle;
int ret;
- dout("%s rbd_dev %p\n", __func__, rbd_dev);
-
ret = ceph_cls_lock_info(osdc, &rbd_dev->header_oid,
&rbd_dev->header_oloc, RBD_LOCK_NAME,
- &lock_type, &lock_tag, lockers, num_lockers);
- if (ret)
- return ret;
+ &lock_type, &lock_tag, &lockers, &num_lockers);
+ if (ret) {
+ rbd_warn(rbd_dev, "failed to get header lockers: %d", ret);
+ return ERR_PTR(ret);
+ }
- if (*num_lockers == 0) {
+ if (num_lockers == 0) {
dout("%s rbd_dev %p no lockers detected\n", __func__, rbd_dev);
+ lockers = NULL;
goto out;
}
if (strcmp(lock_tag, RBD_LOCK_TAG)) {
rbd_warn(rbd_dev, "locked by external mechanism, tag %s",
lock_tag);
- ret = -EBUSY;
- goto out;
+ goto err_busy;
}
- if (lock_type == CEPH_CLS_LOCK_SHARED) {
- rbd_warn(rbd_dev, "shared lock type detected");
- ret = -EBUSY;
- goto out;
+ if (lock_type != CEPH_CLS_LOCK_EXCLUSIVE) {
+ rbd_warn(rbd_dev, "incompatible lock type detected");
+ goto err_busy;
}
- if (strncmp((*lockers)[0].id.cookie, RBD_LOCK_COOKIE_PREFIX,
- strlen(RBD_LOCK_COOKIE_PREFIX))) {
+ WARN_ON(num_lockers != 1);
+ ret = sscanf(lockers[0].id.cookie, RBD_LOCK_COOKIE_PREFIX " %llu",
+ &handle);
+ if (ret != 1) {
rbd_warn(rbd_dev, "locked by external mechanism, cookie %s",
- (*lockers)[0].id.cookie);
- ret = -EBUSY;
- goto out;
+ lockers[0].id.cookie);
+ goto err_busy;
+ }
+ if (ceph_addr_is_blank(&lockers[0].info.addr)) {
+ rbd_warn(rbd_dev, "locker has a blank address");
+ goto err_busy;
}
+ dout("%s rbd_dev %p got locker %s%llu@%pISpc/%u handle %llu\n",
+ __func__, rbd_dev, ENTITY_NAME(lockers[0].id.name),
+ &lockers[0].info.addr.in_addr,
+ le32_to_cpu(lockers[0].info.addr.nonce), handle);
+
out:
kfree(lock_tag);
- return ret;
+ return lockers;
+
+err_busy:
+ kfree(lock_tag);
+ ceph_free_lockers(lockers, num_lockers);
+ return ERR_PTR(-EBUSY);
}
static int find_watcher(struct rbd_device *rbd_dev,
@@ -3888,8 +3945,10 @@ static int find_watcher(struct rbd_device *rbd_dev,
ret = ceph_osdc_list_watchers(osdc, &rbd_dev->header_oid,
&rbd_dev->header_oloc, &watchers,
&num_watchers);
- if (ret)
+ if (ret) {
+ rbd_warn(rbd_dev, "failed to get watchers: %d", ret);
return ret;
+ }
sscanf(locker->id.cookie, RBD_LOCK_COOKIE_PREFIX " %llu", &cookie);
for (i = 0; i < num_watchers; i++) {
@@ -3926,51 +3985,72 @@ out:
static int rbd_try_lock(struct rbd_device *rbd_dev)
{
struct ceph_client *client = rbd_dev->rbd_client->client;
- struct ceph_locker *lockers;
- u32 num_lockers;
+ struct ceph_locker *locker, *refreshed_locker;
int ret;
for (;;) {
+ locker = refreshed_locker = NULL;
+
ret = rbd_lock(rbd_dev);
- if (ret != -EBUSY)
- return ret;
+ if (!ret)
+ goto out;
+ if (ret != -EBUSY) {
+ rbd_warn(rbd_dev, "failed to lock header: %d", ret);
+ goto out;
+ }
/* determine if the current lock holder is still alive */
- ret = get_lock_owner_info(rbd_dev, &lockers, &num_lockers);
- if (ret)
- return ret;
-
- if (num_lockers == 0)
+ locker = get_lock_owner_info(rbd_dev);
+ if (IS_ERR(locker)) {
+ ret = PTR_ERR(locker);
+ locker = NULL;
+ goto out;
+ }
+ if (!locker)
goto again;
- ret = find_watcher(rbd_dev, lockers);
+ ret = find_watcher(rbd_dev, locker);
if (ret)
goto out; /* request lock or error */
+ refreshed_locker = get_lock_owner_info(rbd_dev);
+ if (IS_ERR(refreshed_locker)) {
+ ret = PTR_ERR(refreshed_locker);
+ refreshed_locker = NULL;
+ goto out;
+ }
+ if (!refreshed_locker ||
+ !locker_equal(locker, refreshed_locker))
+ goto again;
+
rbd_warn(rbd_dev, "breaking header lock owned by %s%llu",
- ENTITY_NAME(lockers[0].id.name));
+ ENTITY_NAME(locker->id.name));
ret = ceph_monc_blocklist_add(&client->monc,
- &lockers[0].info.addr);
+ &locker->info.addr);
if (ret) {
- rbd_warn(rbd_dev, "blocklist of %s%llu failed: %d",
- ENTITY_NAME(lockers[0].id.name), ret);
+ rbd_warn(rbd_dev, "failed to blocklist %s%llu: %d",
+ ENTITY_NAME(locker->id.name), ret);
goto out;
}
ret = ceph_cls_break_lock(&client->osdc, &rbd_dev->header_oid,
&rbd_dev->header_oloc, RBD_LOCK_NAME,
- lockers[0].id.cookie,
- &lockers[0].id.name);
- if (ret && ret != -ENOENT)
+ locker->id.cookie, &locker->id.name);
+ if (ret && ret != -ENOENT) {
+ rbd_warn(rbd_dev, "failed to break header lock: %d",
+ ret);
goto out;
+ }
again:
- ceph_free_lockers(lockers, num_lockers);
+ free_locker(refreshed_locker);
+ free_locker(locker);
}
out:
- ceph_free_lockers(lockers, num_lockers);
+ free_locker(refreshed_locker);
+ free_locker(locker);
return ret;
}
@@ -3978,6 +4058,10 @@ static int rbd_post_acquire_action(struct rbd_device *rbd_dev)
{
int ret;
+ ret = rbd_dev_refresh(rbd_dev);
+ if (ret)
+ return ret;
+
if (rbd_dev->header.features & RBD_FEATURE_OBJECT_MAP) {
ret = rbd_object_map_open(rbd_dev);
if (ret)
@@ -4016,11 +4100,8 @@ static int rbd_try_acquire_lock(struct rbd_device *rbd_dev)
ret = rbd_try_lock(rbd_dev);
if (ret < 0) {
- rbd_warn(rbd_dev, "failed to lock header: %d", ret);
- if (ret == -EBLOCKLISTED)
- goto out;
-
- ret = 1; /* request lock anyway */
+ rbd_warn(rbd_dev, "failed to acquire lock: %d", ret);
+ goto out;
}
if (ret > 0) {
up_write(&rbd_dev->lock_rwsem);
@@ -4096,16 +4177,16 @@ static bool rbd_quiesce_lock(struct rbd_device *rbd_dev)
/*
* Ensure that all in-flight IO is flushed.
*/
- rbd_dev->lock_state = RBD_LOCK_STATE_RELEASING;
- rbd_assert(!completion_done(&rbd_dev->releasing_wait));
+ rbd_dev->lock_state = RBD_LOCK_STATE_QUIESCING;
+ rbd_assert(!completion_done(&rbd_dev->quiescing_wait));
if (list_empty(&rbd_dev->running_list))
return true;
up_write(&rbd_dev->lock_rwsem);
- wait_for_completion(&rbd_dev->releasing_wait);
+ wait_for_completion(&rbd_dev->quiescing_wait);
down_write(&rbd_dev->lock_rwsem);
- if (rbd_dev->lock_state != RBD_LOCK_STATE_RELEASING)
+ if (rbd_dev->lock_state != RBD_LOCK_STATE_QUIESCING)
return false;
rbd_assert(list_empty(&rbd_dev->running_list));
@@ -4516,6 +4597,10 @@ static void rbd_reacquire_lock(struct rbd_device *rbd_dev)
rbd_warn(rbd_dev, "failed to update lock cookie: %d",
ret);
+ if (rbd_dev->opts->exclusive)
+ rbd_warn(rbd_dev,
+ "temporarily releasing lock on exclusive mapping");
+
/*
* Lock cookie cannot be updated on older OSDs, so do
* a manual release and queue an acquire.
@@ -4783,7 +4868,9 @@ out_req:
* return, the rbd_dev->header field will contain up-to-date
* information about the image.
*/
-static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev)
+static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev,
+ struct rbd_image_header *header,
+ bool first_time)
{
struct rbd_image_header_ondisk *ondisk = NULL;
u32 snap_count = 0;
@@ -4831,7 +4918,7 @@ static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev)
snap_count = le32_to_cpu(ondisk->snap_count);
} while (snap_count != want_count);
- ret = rbd_header_from_disk(rbd_dev, ondisk);
+ ret = rbd_header_from_disk(header, ondisk, first_time);
out:
kfree(ondisk);
@@ -4855,39 +4942,6 @@ static void rbd_dev_update_size(struct rbd_device *rbd_dev)
}
}
-static int rbd_dev_refresh(struct rbd_device *rbd_dev)
-{
- u64 mapping_size;
- int ret;
-
- down_write(&rbd_dev->header_rwsem);
- mapping_size = rbd_dev->mapping.size;
-
- ret = rbd_dev_header_info(rbd_dev);
- if (ret)
- goto out;
-
- /*
- * If there is a parent, see if it has disappeared due to the
- * mapped image getting flattened.
- */
- if (rbd_dev->parent) {
- ret = rbd_dev_v2_parent_info(rbd_dev);
- if (ret)
- goto out;
- }
-
- rbd_assert(!rbd_is_snap(rbd_dev));
- rbd_dev->mapping.size = rbd_dev->header.image_size;
-
-out:
- up_write(&rbd_dev->header_rwsem);
- if (!ret && mapping_size != rbd_dev->mapping.size)
- rbd_dev_update_size(rbd_dev);
-
- return ret;
-}
-
static const struct blk_mq_ops rbd_mq_ops = {
.queue_rq = rbd_queue_rq,
};
@@ -4895,16 +4949,21 @@ static const struct blk_mq_ops rbd_mq_ops = {
static int rbd_init_disk(struct rbd_device *rbd_dev)
{
struct gendisk *disk;
- struct request_queue *q;
unsigned int objset_bytes =
rbd_dev->layout.object_size * rbd_dev->layout.stripe_count;
+ struct queue_limits lim = {
+ .max_hw_sectors = objset_bytes >> SECTOR_SHIFT,
+ .io_opt = objset_bytes,
+ .io_min = rbd_dev->opts->alloc_size,
+ .max_segments = USHRT_MAX,
+ .max_segment_size = UINT_MAX,
+ };
int err;
memset(&rbd_dev->tag_set, 0, sizeof(rbd_dev->tag_set));
rbd_dev->tag_set.ops = &rbd_mq_ops;
rbd_dev->tag_set.queue_depth = rbd_dev->opts->queue_depth;
rbd_dev->tag_set.numa_node = NUMA_NO_NODE;
- rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
rbd_dev->tag_set.nr_hw_queues = num_present_cpus();
rbd_dev->tag_set.cmd_size = sizeof(struct rbd_img_request);
@@ -4912,12 +4971,20 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
if (err)
return err;
- disk = blk_mq_alloc_disk(&rbd_dev->tag_set, rbd_dev);
+ if (rbd_dev->opts->trim) {
+ lim.discard_granularity = rbd_dev->opts->alloc_size;
+ lim.max_hw_discard_sectors = objset_bytes >> SECTOR_SHIFT;
+ lim.max_write_zeroes_sectors = objset_bytes >> SECTOR_SHIFT;
+ }
+
+ if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC))
+ lim.features |= BLK_FEAT_STABLE_WRITES;
+
+ disk = blk_mq_alloc_disk(&rbd_dev->tag_set, &lim, rbd_dev);
if (IS_ERR(disk)) {
err = PTR_ERR(disk);
goto out_tag_set;
}
- q = disk->queue;
snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d",
rbd_dev->dev_id);
@@ -4929,26 +4996,6 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
disk->minors = RBD_MINORS_PER_MAJOR;
disk->fops = &rbd_bd_ops;
disk->private_data = rbd_dev;
-
- blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
- /* QUEUE_FLAG_ADD_RANDOM is off by default for blk-mq */
-
- blk_queue_max_hw_sectors(q, objset_bytes >> SECTOR_SHIFT);
- q->limits.max_sectors = queue_max_hw_sectors(q);
- blk_queue_max_segments(q, USHRT_MAX);
- blk_queue_max_segment_size(q, UINT_MAX);
- blk_queue_io_min(q, rbd_dev->opts->alloc_size);
- blk_queue_io_opt(q, rbd_dev->opts->alloc_size);
-
- if (rbd_dev->opts->trim) {
- q->limits.discard_granularity = rbd_dev->opts->alloc_size;
- blk_queue_max_discard_sectors(q, objset_bytes >> SECTOR_SHIFT);
- blk_queue_max_write_zeroes_sectors(q, objset_bytes >> SECTOR_SHIFT);
- }
-
- if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC))
- blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, q);
-
rbd_dev->disk = disk;
return 0;
@@ -5278,7 +5325,7 @@ static void rbd_dev_release(struct device *dev)
if (need_put) {
destroy_workqueue(rbd_dev->task_wq);
- ida_simple_remove(&rbd_dev_id_ida, rbd_dev->dev_id);
+ ida_free(&rbd_dev_id_ida, rbd_dev->dev_id);
}
rbd_dev_free(rbd_dev);
@@ -5292,8 +5339,7 @@ static void rbd_dev_release(struct device *dev)
module_put(THIS_MODULE);
}
-static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
- struct rbd_spec *spec)
+static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec)
{
struct rbd_device *rbd_dev;
@@ -5329,7 +5375,7 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
INIT_LIST_HEAD(&rbd_dev->acquiring_list);
INIT_LIST_HEAD(&rbd_dev->running_list);
init_completion(&rbd_dev->acquire_wait);
- init_completion(&rbd_dev->releasing_wait);
+ init_completion(&rbd_dev->quiescing_wait);
spin_lock_init(&rbd_dev->object_map_lock);
@@ -5338,9 +5384,6 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc,
rbd_dev->dev.parent = &rbd_root_dev;
device_initialize(&rbd_dev->dev);
- rbd_dev->rbd_client = rbdc;
- rbd_dev->spec = spec;
-
return rbd_dev;
}
@@ -5353,16 +5396,14 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
{
struct rbd_device *rbd_dev;
- rbd_dev = __rbd_dev_create(rbdc, spec);
+ rbd_dev = __rbd_dev_create(spec);
if (!rbd_dev)
return NULL;
- rbd_dev->opts = opts;
-
/* get an id and fill in device name */
- rbd_dev->dev_id = ida_simple_get(&rbd_dev_id_ida, 0,
- minor_to_rbd_dev_id(1 << MINORBITS),
- GFP_KERNEL);
+ rbd_dev->dev_id = ida_alloc_max(&rbd_dev_id_ida,
+ minor_to_rbd_dev_id(1 << MINORBITS) - 1,
+ GFP_KERNEL);
if (rbd_dev->dev_id < 0)
goto fail_rbd_dev;
@@ -5375,11 +5416,15 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
/* we have a ref from do_rbd_add() */
__module_get(THIS_MODULE);
+ rbd_dev->rbd_client = rbdc;
+ rbd_dev->spec = spec;
+ rbd_dev->opts = opts;
+
dout("%s rbd_dev %p dev_id %d\n", __func__, rbd_dev, rbd_dev->dev_id);
return rbd_dev;
fail_dev_id:
- ida_simple_remove(&rbd_dev_id_ida, rbd_dev->dev_id);
+ ida_free(&rbd_dev_id_ida, rbd_dev->dev_id);
fail_rbd_dev:
rbd_dev_free(rbd_dev);
return NULL;
@@ -5429,17 +5474,12 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
return 0;
}
-static int rbd_dev_v2_image_size(struct rbd_device *rbd_dev)
-{
- return _rbd_dev_v2_snap_size(rbd_dev, CEPH_NOSNAP,
- &rbd_dev->header.obj_order,
- &rbd_dev->header.image_size);
-}
-
-static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev)
+static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev,
+ char **pobject_prefix)
{
size_t size;
void *reply_buf;
+ char *object_prefix;
int ret;
void *p;
@@ -5457,16 +5497,16 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev)
goto out;
p = reply_buf;
- rbd_dev->header.object_prefix = ceph_extract_encoded_string(&p,
- p + ret, NULL, GFP_NOIO);
+ object_prefix = ceph_extract_encoded_string(&p, p + ret, NULL,
+ GFP_NOIO);
+ if (IS_ERR(object_prefix)) {
+ ret = PTR_ERR(object_prefix);
+ goto out;
+ }
ret = 0;
- if (IS_ERR(rbd_dev->header.object_prefix)) {
- ret = PTR_ERR(rbd_dev->header.object_prefix);
- rbd_dev->header.object_prefix = NULL;
- } else {
- dout(" object_prefix = %s\n", rbd_dev->header.object_prefix);
- }
+ *pobject_prefix = object_prefix;
+ dout(" object_prefix = %s\n", object_prefix);
out:
kfree(reply_buf);
@@ -5517,13 +5557,6 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
return 0;
}
-static int rbd_dev_v2_features(struct rbd_device *rbd_dev)
-{
- return _rbd_dev_v2_snap_features(rbd_dev, CEPH_NOSNAP,
- rbd_is_ro(rbd_dev),
- &rbd_dev->header.features);
-}
-
/*
* These are generic image flags, but since they are used only for
* object map, store them in rbd_dev->object_map_flags.
@@ -5560,6 +5593,14 @@ struct parent_image_info {
u64 overlap;
};
+static void rbd_parent_info_cleanup(struct parent_image_info *pii)
+{
+ kfree(pii->pool_ns);
+ kfree(pii->image_id);
+
+ memset(pii, 0, sizeof(*pii));
+}
+
/*
* The caller is responsible for @pii.
*/
@@ -5629,6 +5670,9 @@ static int __get_parent_info(struct rbd_device *rbd_dev,
if (pii->has_overlap)
ceph_decode_64_safe(&p, end, pii->overlap, e_inval);
+ dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n",
+ __func__, pii->pool_id, pii->pool_ns, pii->image_id, pii->snap_id,
+ pii->has_overlap, pii->overlap);
return 0;
e_inval:
@@ -5667,14 +5711,17 @@ static int __get_parent_info_legacy(struct rbd_device *rbd_dev,
pii->has_overlap = true;
ceph_decode_64_safe(&p, end, pii->overlap, e_inval);
+ dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n",
+ __func__, pii->pool_id, pii->pool_ns, pii->image_id, pii->snap_id,
+ pii->has_overlap, pii->overlap);
return 0;
e_inval:
return -EINVAL;
}
-static int get_parent_info(struct rbd_device *rbd_dev,
- struct parent_image_info *pii)
+static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev,
+ struct parent_image_info *pii)
{
struct page *req_page, *reply_page;
void *p;
@@ -5702,7 +5749,7 @@ static int get_parent_info(struct rbd_device *rbd_dev,
return ret;
}
-static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
+static int rbd_dev_setup_parent(struct rbd_device *rbd_dev)
{
struct rbd_spec *parent_spec;
struct parent_image_info pii = { 0 };
@@ -5712,37 +5759,12 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
if (!parent_spec)
return -ENOMEM;
- ret = get_parent_info(rbd_dev, &pii);
+ ret = rbd_dev_v2_parent_info(rbd_dev, &pii);
if (ret)
goto out_err;
- dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n",
- __func__, pii.pool_id, pii.pool_ns, pii.image_id, pii.snap_id,
- pii.has_overlap, pii.overlap);
-
- if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) {
- /*
- * Either the parent never existed, or we have
- * record of it but the image got flattened so it no
- * longer has a parent. When the parent of a
- * layered image disappears we immediately set the
- * overlap to 0. The effect of this is that all new
- * requests will be treated as if the image had no
- * parent.
- *
- * If !pii.has_overlap, the parent image spec is not
- * applicable. It's there to avoid duplication in each
- * snapshot record.
- */
- if (rbd_dev->parent_overlap) {
- rbd_dev->parent_overlap = 0;
- rbd_dev_parent_put(rbd_dev);
- pr_info("%s: clone image has been flattened\n",
- rbd_dev->disk->disk_name);
- }
-
+ if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap)
goto out; /* No parent? No problem. */
- }
/* The ceph file layout needs to fit pool id in 32 bits */
@@ -5754,58 +5776,46 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
}
/*
- * The parent won't change (except when the clone is
- * flattened, already handled that). So we only need to
- * record the parent spec we have not already done so.
+ * The parent won't change except when the clone is flattened,
+ * so we only need to record the parent image spec once.
*/
- if (!rbd_dev->parent_spec) {
- parent_spec->pool_id = pii.pool_id;
- if (pii.pool_ns && *pii.pool_ns) {
- parent_spec->pool_ns = pii.pool_ns;
- pii.pool_ns = NULL;
- }
- parent_spec->image_id = pii.image_id;
- pii.image_id = NULL;
- parent_spec->snap_id = pii.snap_id;
-
- rbd_dev->parent_spec = parent_spec;
- parent_spec = NULL; /* rbd_dev now owns this */
+ parent_spec->pool_id = pii.pool_id;
+ if (pii.pool_ns && *pii.pool_ns) {
+ parent_spec->pool_ns = pii.pool_ns;
+ pii.pool_ns = NULL;
}
+ parent_spec->image_id = pii.image_id;
+ pii.image_id = NULL;
+ parent_spec->snap_id = pii.snap_id;
+
+ rbd_assert(!rbd_dev->parent_spec);
+ rbd_dev->parent_spec = parent_spec;
+ parent_spec = NULL; /* rbd_dev now owns this */
/*
- * We always update the parent overlap. If it's zero we issue
- * a warning, as we will proceed as if there was no parent.
+ * Record the parent overlap. If it's zero, issue a warning as
+ * we will proceed as if there is no parent.
*/
- if (!pii.overlap) {
- if (parent_spec) {
- /* refresh, careful to warn just once */
- if (rbd_dev->parent_overlap)
- rbd_warn(rbd_dev,
- "clone now standalone (overlap became 0)");
- } else {
- /* initial probe */
- rbd_warn(rbd_dev, "clone is standalone (overlap 0)");
- }
- }
+ if (!pii.overlap)
+ rbd_warn(rbd_dev, "clone is standalone (overlap 0)");
rbd_dev->parent_overlap = pii.overlap;
out:
ret = 0;
out_err:
- kfree(pii.pool_ns);
- kfree(pii.image_id);
+ rbd_parent_info_cleanup(&pii);
rbd_spec_put(parent_spec);
return ret;
}
-static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
+static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev,
+ u64 *stripe_unit, u64 *stripe_count)
{
struct {
__le64 stripe_unit;
__le64 stripe_count;
} __attribute__ ((packed)) striping_info_buf = { 0 };
size_t size = sizeof (striping_info_buf);
- void *p;
int ret;
ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
@@ -5817,27 +5827,33 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
if (ret < size)
return -ERANGE;
- p = &striping_info_buf;
- rbd_dev->header.stripe_unit = ceph_decode_64(&p);
- rbd_dev->header.stripe_count = ceph_decode_64(&p);
+ *stripe_unit = le64_to_cpu(striping_info_buf.stripe_unit);
+ *stripe_count = le64_to_cpu(striping_info_buf.stripe_count);
+ dout(" stripe_unit = %llu stripe_count = %llu\n", *stripe_unit,
+ *stripe_count);
+
return 0;
}
-static int rbd_dev_v2_data_pool(struct rbd_device *rbd_dev)
+static int rbd_dev_v2_data_pool(struct rbd_device *rbd_dev, s64 *data_pool_id)
{
- __le64 data_pool_id;
+ __le64 data_pool_buf;
int ret;
ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
&rbd_dev->header_oloc, "get_data_pool",
- NULL, 0, &data_pool_id, sizeof(data_pool_id));
+ NULL, 0, &data_pool_buf,
+ sizeof(data_pool_buf));
+ dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
return ret;
- if (ret < sizeof(data_pool_id))
+ if (ret < sizeof(data_pool_buf))
return -EBADMSG;
- rbd_dev->header.data_pool_id = le64_to_cpu(data_pool_id);
- WARN_ON(rbd_dev->header.data_pool_id == CEPH_NOPOOL);
+ *data_pool_id = le64_to_cpu(data_pool_buf);
+ dout(" data_pool_id = %lld\n", *data_pool_id);
+ WARN_ON(*data_pool_id == CEPH_NOPOOL);
+
return 0;
}
@@ -6029,7 +6045,8 @@ out_err:
return ret;
}
-static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev)
+static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev,
+ struct ceph_snap_context **psnapc)
{
size_t size;
int ret;
@@ -6090,9 +6107,7 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev)
for (i = 0; i < snap_count; i++)
snapc->snaps[i] = ceph_decode_64(&p);
- ceph_put_snap_context(rbd_dev->header.snapc);
- rbd_dev->header.snapc = snapc;
-
+ *psnapc = snapc;
dout(" snap context seq = %llu, snap_count = %u\n",
(unsigned long long)seq, (unsigned int)snap_count);
out:
@@ -6141,38 +6156,42 @@ out:
return snap_name;
}
-static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev)
+static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev,
+ struct rbd_image_header *header,
+ bool first_time)
{
- bool first_time = rbd_dev->header.object_prefix == NULL;
int ret;
- ret = rbd_dev_v2_image_size(rbd_dev);
+ ret = _rbd_dev_v2_snap_size(rbd_dev, CEPH_NOSNAP,
+ first_time ? &header->obj_order : NULL,
+ &header->image_size);
if (ret)
return ret;
if (first_time) {
- ret = rbd_dev_v2_header_onetime(rbd_dev);
+ ret = rbd_dev_v2_header_onetime(rbd_dev, header);
if (ret)
return ret;
}
- ret = rbd_dev_v2_snap_context(rbd_dev);
- if (ret && first_time) {
- kfree(rbd_dev->header.object_prefix);
- rbd_dev->header.object_prefix = NULL;
- }
+ ret = rbd_dev_v2_snap_context(rbd_dev, &header->snapc);
+ if (ret)
+ return ret;
- return ret;
+ return 0;
}
-static int rbd_dev_header_info(struct rbd_device *rbd_dev)
+static int rbd_dev_header_info(struct rbd_device *rbd_dev,
+ struct rbd_image_header *header,
+ bool first_time)
{
rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+ rbd_assert(!header->object_prefix && !header->snapc);
if (rbd_dev->image_format == 1)
- return rbd_dev_v1_header_info(rbd_dev);
+ return rbd_dev_v1_header_info(rbd_dev, header, first_time);
- return rbd_dev_v2_header_info(rbd_dev);
+ return rbd_dev_v2_header_info(rbd_dev, header, first_time);
}
/*
@@ -6556,18 +6575,12 @@ static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
cancel_delayed_work_sync(&rbd_dev->lock_dwork);
if (!ret)
ret = -ETIMEDOUT;
- }
- if (ret) {
- rbd_warn(rbd_dev, "failed to acquire exclusive lock: %ld", ret);
- return ret;
+ rbd_warn(rbd_dev, "failed to acquire lock: %ld", ret);
}
+ if (ret)
+ return ret;
- /*
- * The lock may have been released by now, unless automatic lock
- * transitions are disabled.
- */
- rbd_assert(!rbd_dev->opts->exclusive || rbd_is_lock_owner(rbd_dev));
return 0;
}
@@ -6661,60 +6674,49 @@ out:
*/
static void rbd_dev_unprobe(struct rbd_device *rbd_dev)
{
- struct rbd_image_header *header;
-
rbd_dev_parent_put(rbd_dev);
rbd_object_map_free(rbd_dev);
rbd_dev_mapping_clear(rbd_dev);
/* Free dynamic fields from the header, then zero it out */
- header = &rbd_dev->header;
- ceph_put_snap_context(header->snapc);
- kfree(header->snap_sizes);
- kfree(header->snap_names);
- kfree(header->object_prefix);
- memset(header, 0, sizeof (*header));
+ rbd_image_header_cleanup(&rbd_dev->header);
}
-static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev)
+static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev,
+ struct rbd_image_header *header)
{
int ret;
- ret = rbd_dev_v2_object_prefix(rbd_dev);
+ ret = rbd_dev_v2_object_prefix(rbd_dev, &header->object_prefix);
if (ret)
- goto out_err;
+ return ret;
/*
* Get the and check features for the image. Currently the
* features are assumed to never change.
*/
- ret = rbd_dev_v2_features(rbd_dev);
+ ret = _rbd_dev_v2_snap_features(rbd_dev, CEPH_NOSNAP,
+ rbd_is_ro(rbd_dev), &header->features);
if (ret)
- goto out_err;
+ return ret;
/* If the image supports fancy striping, get its parameters */
- if (rbd_dev->header.features & RBD_FEATURE_STRIPINGV2) {
- ret = rbd_dev_v2_striping_info(rbd_dev);
- if (ret < 0)
- goto out_err;
+ if (header->features & RBD_FEATURE_STRIPINGV2) {
+ ret = rbd_dev_v2_striping_info(rbd_dev, &header->stripe_unit,
+ &header->stripe_count);
+ if (ret)
+ return ret;
}
- if (rbd_dev->header.features & RBD_FEATURE_DATA_POOL) {
- ret = rbd_dev_v2_data_pool(rbd_dev);
+ if (header->features & RBD_FEATURE_DATA_POOL) {
+ ret = rbd_dev_v2_data_pool(rbd_dev, &header->data_pool_id);
if (ret)
- goto out_err;
+ return ret;
}
- rbd_init_layout(rbd_dev);
return 0;
-
-out_err:
- rbd_dev->header.features = 0;
- kfree(rbd_dev->header.object_prefix);
- rbd_dev->header.object_prefix = NULL;
- return ret;
}
/*
@@ -6736,7 +6738,7 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
goto out_err;
}
- parent = __rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec);
+ parent = __rbd_dev_create(rbd_dev->parent_spec);
if (!parent) {
ret = -ENOMEM;
goto out_err;
@@ -6746,8 +6748,8 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
* Images related by parent/child relationships always share
* rbd_client and spec/parent_spec, so bump their refcounts.
*/
- __rbd_get_client(rbd_dev->rbd_client);
- rbd_spec_get(rbd_dev->parent_spec);
+ parent->rbd_client = __rbd_get_client(rbd_dev->rbd_client);
+ parent->spec = rbd_spec_get(rbd_dev->parent_spec);
__set_bit(RBD_DEV_FLAG_READONLY, &parent->flags);
@@ -6909,13 +6911,15 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
if (!depth)
down_write(&rbd_dev->header_rwsem);
- ret = rbd_dev_header_info(rbd_dev);
+ ret = rbd_dev_header_info(rbd_dev, &rbd_dev->header, true);
if (ret) {
if (ret == -ENOENT && !need_watch)
rbd_print_dne(rbd_dev, false);
goto err_out_probe;
}
+ rbd_init_layout(rbd_dev);
+
/*
* If this image is the one being mapped, we have pool name and
* id, image name and id, and snap name - need to fill snap id.
@@ -6944,7 +6948,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
}
if (rbd_dev->header.features & RBD_FEATURE_LAYERING) {
- ret = rbd_dev_v2_parent_info(rbd_dev);
+ ret = rbd_dev_setup_parent(rbd_dev);
if (ret)
goto err_out_probe;
}
@@ -6970,9 +6974,108 @@ err_out_format:
return ret;
}
-static ssize_t do_rbd_add(struct bus_type *bus,
- const char *buf,
- size_t count)
+static void rbd_dev_update_header(struct rbd_device *rbd_dev,
+ struct rbd_image_header *header)
+{
+ rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+ rbd_assert(rbd_dev->header.object_prefix); /* !first_time */
+
+ if (rbd_dev->header.image_size != header->image_size) {
+ rbd_dev->header.image_size = header->image_size;
+
+ if (!rbd_is_snap(rbd_dev)) {
+ rbd_dev->mapping.size = header->image_size;
+ rbd_dev_update_size(rbd_dev);
+ }
+ }
+
+ ceph_put_snap_context(rbd_dev->header.snapc);
+ rbd_dev->header.snapc = header->snapc;
+ header->snapc = NULL;
+
+ if (rbd_dev->image_format == 1) {
+ kfree(rbd_dev->header.snap_names);
+ rbd_dev->header.snap_names = header->snap_names;
+ header->snap_names = NULL;
+
+ kfree(rbd_dev->header.snap_sizes);
+ rbd_dev->header.snap_sizes = header->snap_sizes;
+ header->snap_sizes = NULL;
+ }
+}
+
+static void rbd_dev_update_parent(struct rbd_device *rbd_dev,
+ struct parent_image_info *pii)
+{
+ if (pii->pool_id == CEPH_NOPOOL || !pii->has_overlap) {
+ /*
+ * Either the parent never existed, or we have
+ * record of it but the image got flattened so it no
+ * longer has a parent. When the parent of a
+ * layered image disappears we immediately set the
+ * overlap to 0. The effect of this is that all new
+ * requests will be treated as if the image had no
+ * parent.
+ *
+ * If !pii.has_overlap, the parent image spec is not
+ * applicable. It's there to avoid duplication in each
+ * snapshot record.
+ */
+ if (rbd_dev->parent_overlap) {
+ rbd_dev->parent_overlap = 0;
+ rbd_dev_parent_put(rbd_dev);
+ pr_info("%s: clone has been flattened\n",
+ rbd_dev->disk->disk_name);
+ }
+ } else {
+ rbd_assert(rbd_dev->parent_spec);
+
+ /*
+ * Update the parent overlap. If it became zero, issue
+ * a warning as we will proceed as if there is no parent.
+ */
+ if (!pii->overlap && rbd_dev->parent_overlap)
+ rbd_warn(rbd_dev,
+ "clone has become standalone (overlap 0)");
+ rbd_dev->parent_overlap = pii->overlap;
+ }
+}
+
+static int rbd_dev_refresh(struct rbd_device *rbd_dev)
+{
+ struct rbd_image_header header = { 0 };
+ struct parent_image_info pii = { 0 };
+ int ret;
+
+ dout("%s rbd_dev %p\n", __func__, rbd_dev);
+
+ ret = rbd_dev_header_info(rbd_dev, &header, false);
+ if (ret)
+ goto out;
+
+ /*
+ * If there is a parent, see if it has disappeared due to the
+ * mapped image getting flattened.
+ */
+ if (rbd_dev->parent) {
+ ret = rbd_dev_v2_parent_info(rbd_dev, &pii);
+ if (ret)
+ goto out;
+ }
+
+ down_write(&rbd_dev->header_rwsem);
+ rbd_dev_update_header(rbd_dev, &header);
+ if (rbd_dev->parent)
+ rbd_dev_update_parent(rbd_dev, &pii);
+ up_write(&rbd_dev->header_rwsem);
+
+out:
+ rbd_parent_info_cleanup(&pii);
+ rbd_image_header_cleanup(&header);
+ return ret;
+}
+
+static ssize_t do_rbd_add(const char *buf, size_t count)
{
struct rbd_device *rbd_dev = NULL;
struct ceph_options *ceph_opts = NULL;
@@ -7084,18 +7187,18 @@ err_out_args:
goto out;
}
-static ssize_t add_store(struct bus_type *bus, const char *buf, size_t count)
+static ssize_t add_store(const struct bus_type *bus, const char *buf, size_t count)
{
if (single_major)
return -EINVAL;
- return do_rbd_add(bus, buf, count);
+ return do_rbd_add(buf, count);
}
-static ssize_t add_single_major_store(struct bus_type *bus, const char *buf,
+static ssize_t add_single_major_store(const struct bus_type *bus, const char *buf,
size_t count)
{
- return do_rbd_add(bus, buf, count);
+ return do_rbd_add(buf, count);
}
static void rbd_dev_remove_parent(struct rbd_device *rbd_dev)
@@ -7125,12 +7228,9 @@ static void rbd_dev_remove_parent(struct rbd_device *rbd_dev)
}
}
-static ssize_t do_rbd_remove(struct bus_type *bus,
- const char *buf,
- size_t count)
+static ssize_t do_rbd_remove(const char *buf, size_t count)
{
struct rbd_device *rbd_dev = NULL;
- struct list_head *tmp;
int dev_id;
char opt_buf[6];
bool force = false;
@@ -7157,8 +7257,7 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
ret = -ENOENT;
spin_lock(&rbd_dev_list_lock);
- list_for_each(tmp, &rbd_dev_list) {
- rbd_dev = list_entry(tmp, struct rbd_device, node);
+ list_for_each_entry(rbd_dev, &rbd_dev_list, node) {
if (rbd_dev->dev_id == dev_id) {
ret = 0;
break;
@@ -7182,8 +7281,10 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
* Prevent new IO from being queued and wait for existing
* IO to complete/fail.
*/
- blk_mq_freeze_queue(rbd_dev->disk->queue);
+ unsigned int memflags = blk_mq_freeze_queue(rbd_dev->disk->queue);
+
blk_mark_disk_dead(rbd_dev->disk);
+ blk_mq_unfreeze_queue(rbd_dev->disk->queue, memflags);
}
del_gendisk(rbd_dev->disk);
@@ -7199,18 +7300,18 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
return count;
}
-static ssize_t remove_store(struct bus_type *bus, const char *buf, size_t count)
+static ssize_t remove_store(const struct bus_type *bus, const char *buf, size_t count)
{
if (single_major)
return -EINVAL;
- return do_rbd_remove(bus, buf, count);
+ return do_rbd_remove(buf, count);
}
-static ssize_t remove_single_major_store(struct bus_type *bus, const char *buf,
+static ssize_t remove_single_major_store(const struct bus_type *bus, const char *buf,
size_t count)
{
- return do_rbd_remove(bus, buf, count);
+ return do_rbd_remove(buf, count);
}
/*
@@ -7222,8 +7323,10 @@ static int __init rbd_sysfs_init(void)
int ret;
ret = device_register(&rbd_root_dev);
- if (ret < 0)
+ if (ret < 0) {
+ put_device(&rbd_root_dev);
return ret;
+ }
ret = bus_register(&rbd_bus_type);
if (ret < 0)
@@ -7286,7 +7389,7 @@ static int __init rbd_init(void)
* The number of active work items is limited by the number of
* rbd devices * queue depth, so leave @max_active at default.
*/
- rbd_wq = alloc_workqueue(RBD_DRV_NAME, WQ_MEM_RECLAIM, 0);
+ rbd_wq = alloc_workqueue(RBD_DRV_NAME, WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!rbd_wq) {
rc = -ENOMEM;
goto err_out_slab;
diff --git a/drivers/block/rnbd/Makefile b/drivers/block/rnbd/Makefile
index 5bb1a7ad1ada..208e5f865497 100644
--- a/drivers/block/rnbd/Makefile
+++ b/drivers/block/rnbd/Makefile
@@ -3,13 +3,13 @@
ccflags-y := -I$(srctree)/drivers/infiniband/ulp/rtrs
rnbd-client-y := rnbd-clt.o \
- rnbd-clt-sysfs.o \
- rnbd-common.o
+ rnbd-clt-sysfs.o
-rnbd-server-y := rnbd-common.o \
- rnbd-srv.o \
- rnbd-srv-dev.o \
- rnbd-srv-sysfs.o
+CFLAGS_rnbd-srv-trace.o = -I$(src)
+
+rnbd-server-y := rnbd-srv.o \
+ rnbd-srv-sysfs.o \
+ rnbd-srv-trace.o
obj-$(CONFIG_BLK_DEV_RNBD_CLIENT) += rnbd-client.o
obj-$(CONFIG_BLK_DEV_RNBD_SERVER) += rnbd-server.o
diff --git a/drivers/block/rnbd/rnbd-clt-sysfs.c b/drivers/block/rnbd/rnbd-clt-sysfs.c
index e7c7d9a68168..6ea7c12e3a87 100644
--- a/drivers/block/rnbd/rnbd-clt-sysfs.c
+++ b/drivers/block/rnbd/rnbd-clt-sysfs.c
@@ -24,7 +24,9 @@
#include "rnbd-clt.h"
static struct device *rnbd_dev;
-static struct class *rnbd_dev_class;
+static const struct class rnbd_dev_class = {
+ .name = "rnbd-client",
+};
static struct kobject *rnbd_devs_kobj;
enum {
@@ -278,7 +280,7 @@ static ssize_t access_mode_show(struct kobject *kobj,
dev = container_of(kobj, struct rnbd_clt_dev, kobj);
- return sysfs_emit(page, "%s\n", rnbd_access_mode_str(dev->access_mode));
+ return sysfs_emit(page, "%s\n", rnbd_access_modes[dev->access_mode].str);
}
static struct kobj_attribute rnbd_clt_access_mode =
@@ -473,7 +475,7 @@ void rnbd_clt_remove_dev_symlink(struct rnbd_clt_dev *dev)
}
}
-static struct kobj_type rnbd_dev_ktype = {
+static const struct kobj_type rnbd_dev_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_groups = rnbd_dev_groups,
};
@@ -596,7 +598,7 @@ static ssize_t rnbd_clt_map_device_store(struct kobject *kobj,
pr_info("Mapping device %s on session %s, (access_mode: %s, nr_poll_queues: %d)\n",
pathname, sessname,
- rnbd_access_mode_str(access_mode),
+ rnbd_access_modes[access_mode].str,
nr_poll_queues);
dev = rnbd_clt_map_device(sessname, paths, path_cnt, port_nr, pathname,
@@ -646,11 +648,11 @@ int rnbd_clt_create_sysfs_files(void)
{
int err;
- rnbd_dev_class = class_create(THIS_MODULE, "rnbd-client");
- if (IS_ERR(rnbd_dev_class))
- return PTR_ERR(rnbd_dev_class);
+ err = class_register(&rnbd_dev_class);
+ if (err)
+ return err;
- rnbd_dev = device_create_with_groups(rnbd_dev_class, NULL,
+ rnbd_dev = device_create_with_groups(&rnbd_dev_class, NULL,
MKDEV(0, 0), NULL,
default_attr_groups, "ctl");
if (IS_ERR(rnbd_dev)) {
@@ -666,9 +668,9 @@ int rnbd_clt_create_sysfs_files(void)
return 0;
dev_destroy:
- device_destroy(rnbd_dev_class, MKDEV(0, 0));
+ device_destroy(&rnbd_dev_class, MKDEV(0, 0));
cls_destroy:
- class_destroy(rnbd_dev_class);
+ class_unregister(&rnbd_dev_class);
return err;
}
@@ -678,6 +680,6 @@ void rnbd_clt_destroy_sysfs_files(void)
sysfs_remove_group(&rnbd_dev->kobj, &default_attr_group);
kobject_del(rnbd_devs_kobj);
kobject_put(rnbd_devs_kobj);
- device_destroy(rnbd_dev_class, MKDEV(0, 0));
- class_destroy(rnbd_dev_class);
+ device_destroy(&rnbd_dev_class, MKDEV(0, 0));
+ class_unregister(&rnbd_dev_class);
}
diff --git a/drivers/block/rnbd/rnbd-clt.c b/drivers/block/rnbd/rnbd-clt.c
index 04da33a22ef4..f1409e54010a 100644
--- a/drivers/block/rnbd/rnbd-clt.c
+++ b/drivers/block/rnbd/rnbd-clt.c
@@ -921,11 +921,11 @@ rnbd_clt_session *find_or_create_sess(const char *sessname, bool *first)
return sess;
}
-static int rnbd_client_open(struct block_device *block_device, fmode_t mode)
+static int rnbd_client_open(struct gendisk *disk, blk_mode_t mode)
{
- struct rnbd_clt_dev *dev = block_device->bd_disk->private_data;
+ struct rnbd_clt_dev *dev = disk->private_data;
- if (get_disk_ro(dev->gd) && (mode & FMODE_WRITE))
+ if (get_disk_ro(dev->gd) && (mode & BLK_OPEN_WRITE))
return -EPERM;
if (dev->dev_state == DEV_STATE_UNMAPPED ||
@@ -935,18 +935,18 @@ static int rnbd_client_open(struct block_device *block_device, fmode_t mode)
return 0;
}
-static void rnbd_client_release(struct gendisk *gen, fmode_t mode)
+static void rnbd_client_release(struct gendisk *gen)
{
struct rnbd_clt_dev *dev = gen->private_data;
rnbd_clt_put_dev(dev);
}
-static int rnbd_client_getgeo(struct block_device *block_device,
+static int rnbd_client_getgeo(struct gendisk *disk,
struct hd_geometry *geo)
{
u64 size;
- struct rnbd_clt_dev *dev = block_device->bd_disk->private_data;
+ struct rnbd_clt_dev *dev = disk->private_data;
struct queue_limits *limit = &dev->queue->limits;
size = dev->size * (limit->logical_block_size / SECTOR_SIZE);
@@ -1006,11 +1006,11 @@ static int rnbd_client_xfer_request(struct rnbd_clt_dev *dev,
msg.prio = cpu_to_le16(req_get_ioprio(rq));
/*
- * We only support discards with single segment for now.
+ * We only support discards/WRITE_ZEROES with single segment for now.
* See queue limits.
*/
- if (req_op(rq) != REQ_OP_DISCARD)
- sg_cnt = blk_rq_map_sg(dev->queue, rq, iu->sgt.sgl);
+ if ((req_op(rq) != REQ_OP_DISCARD) && (req_op(rq) != REQ_OP_WRITE_ZEROES))
+ sg_cnt = blk_rq_map_sg(rq, iu->sgt.sgl);
if (sg_cnt == 0)
sg_mark_end(&iu->sgt.sgl[0]);
@@ -1159,13 +1159,11 @@ static int rnbd_rdma_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
{
struct rnbd_queue *q = hctx->driver_data;
struct rnbd_clt_dev *dev = q->dev;
- int cnt;
- cnt = rtrs_clt_rdma_cq_direct(dev->sess->rtrs, hctx->queue_num);
- return cnt;
+ return rtrs_clt_rdma_cq_direct(dev->sess->rtrs, hctx->queue_num);
}
-static int rnbd_rdma_map_queues(struct blk_mq_tag_set *set)
+static void rnbd_rdma_map_queues(struct blk_mq_tag_set *set)
{
struct rnbd_clt_session *sess = set->driver_data;
@@ -1194,8 +1192,6 @@ static int rnbd_rdma_map_queues(struct blk_mq_tag_set *set)
set->map[HCTX_TYPE_DEFAULT].nr_queues,
set->map[HCTX_TYPE_READ].nr_queues);
}
-
- return 0;
}
static struct blk_mq_ops rnbd_mq_ops = {
@@ -1213,8 +1209,7 @@ static int setup_mq_tags(struct rnbd_clt_session *sess)
tag_set->ops = &rnbd_mq_ops;
tag_set->queue_depth = sess->queue_depth;
tag_set->numa_node = NUMA_NO_NODE;
- tag_set->flags = BLK_MQ_F_SHOULD_MERGE |
- BLK_MQ_F_TAG_QUEUE_SHARED;
+ tag_set->flags = BLK_MQ_F_TAG_QUEUE_SHARED;
tag_set->cmd_size = sizeof(struct rnbd_iu) + RNBD_RDMA_SGL_SIZE;
/* for HCTX_TYPE_DEFAULT, HCTX_TYPE_READ, HCTX_TYPE_POLL */
@@ -1333,41 +1328,6 @@ static void rnbd_init_mq_hw_queues(struct rnbd_clt_dev *dev)
}
}
-static void setup_request_queue(struct rnbd_clt_dev *dev,
- struct rnbd_msg_open_rsp *rsp)
-{
- blk_queue_logical_block_size(dev->queue,
- le16_to_cpu(rsp->logical_block_size));
- blk_queue_physical_block_size(dev->queue,
- le16_to_cpu(rsp->physical_block_size));
- blk_queue_max_hw_sectors(dev->queue,
- dev->sess->max_io_size / SECTOR_SIZE);
-
- /*
- * we don't support discards to "discontiguous" segments
- * in on request
- */
- blk_queue_max_discard_segments(dev->queue, 1);
-
- blk_queue_max_discard_sectors(dev->queue,
- le32_to_cpu(rsp->max_discard_sectors));
- dev->queue->limits.discard_granularity =
- le32_to_cpu(rsp->discard_granularity);
- dev->queue->limits.discard_alignment =
- le32_to_cpu(rsp->discard_alignment);
- if (le16_to_cpu(rsp->secure_discard))
- blk_queue_max_secure_erase_sectors(dev->queue,
- le32_to_cpu(rsp->max_discard_sectors));
- blk_queue_flag_set(QUEUE_FLAG_SAME_COMP, dev->queue);
- blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, dev->queue);
- blk_queue_max_segments(dev->queue, dev->sess->max_segments);
- blk_queue_io_opt(dev->queue, dev->sess->max_io_size);
- blk_queue_virt_boundary(dev->queue, SZ_4K - 1);
- blk_queue_write_cache(dev->queue,
- !!(rsp->cache_policy & RNBD_WRITEBACK),
- !!(rsp->cache_policy & RNBD_FUA));
-}
-
static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev,
struct rnbd_msg_open_rsp *rsp, int idx)
{
@@ -1391,10 +1351,6 @@ static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev,
if (dev->access_mode == RNBD_ACCESS_RO)
set_disk_ro(dev->gd, true);
- /*
- * Network device does not need rotational
- */
- blk_queue_flag_set(QUEUE_FLAG_NONROT, dev->queue);
err = add_disk(dev->gd);
if (err)
put_disk(dev->gd);
@@ -1405,18 +1361,41 @@ static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev,
static int rnbd_client_setup_device(struct rnbd_clt_dev *dev,
struct rnbd_msg_open_rsp *rsp)
{
+ struct queue_limits lim = {
+ .logical_block_size = le16_to_cpu(rsp->logical_block_size),
+ .physical_block_size = le16_to_cpu(rsp->physical_block_size),
+ .io_opt = dev->sess->max_io_size,
+ .max_hw_sectors = dev->sess->max_io_size / SECTOR_SIZE,
+ .max_hw_discard_sectors = le32_to_cpu(rsp->max_discard_sectors),
+ .discard_granularity = le32_to_cpu(rsp->discard_granularity),
+ .discard_alignment = le32_to_cpu(rsp->discard_alignment),
+ .max_segments = dev->sess->max_segments,
+ .virt_boundary_mask = SZ_4K - 1,
+ .max_write_zeroes_sectors =
+ le32_to_cpu(rsp->max_write_zeroes_sectors),
+ };
int idx = dev->clt_device_id;
dev->size = le64_to_cpu(rsp->nsectors) *
le16_to_cpu(rsp->logical_block_size);
- dev->gd = blk_mq_alloc_disk(&dev->sess->tag_set, dev);
+ if (rsp->secure_discard) {
+ lim.max_secure_erase_sectors =
+ le32_to_cpu(rsp->max_discard_sectors);
+ }
+
+ if (rsp->cache_policy & RNBD_WRITEBACK) {
+ lim.features |= BLK_FEAT_WRITE_CACHE;
+ if (rsp->cache_policy & RNBD_FUA)
+ lim.features |= BLK_FEAT_FUA;
+ }
+
+ dev->gd = blk_mq_alloc_disk(&dev->sess->tag_set, &lim, dev);
if (IS_ERR(dev->gd))
return PTR_ERR(dev->gd);
dev->queue = dev->gd->queue;
rnbd_init_mq_hw_queues(dev);
- setup_request_queue(dev, rsp);
return rnbd_clt_setup_gen_disk(dev, rsp, idx);
}
@@ -1444,7 +1423,7 @@ static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess,
goto out_alloc;
}
- ret = ida_alloc_max(&index_ida, 1 << (MINORBITS - RNBD_PART_BITS),
+ ret = ida_alloc_max(&index_ida, (1 << (MINORBITS - RNBD_PART_BITS)) - 1,
GFP_KERNEL);
if (ret < 0) {
pr_err("Failed to initialize device '%s' from session %s, allocating idr failed, err: %d\n",
@@ -1571,8 +1550,8 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname,
dev = init_dev(sess, access_mode, pathname, nr_poll_queues);
if (IS_ERR(dev)) {
- pr_err("map_device: failed to map device '%s' from session %s, can't initialize device, err: %ld\n",
- pathname, sess->sessname, PTR_ERR(dev));
+ pr_err("map_device: failed to map device '%s' from session %s, can't initialize device, err: %pe\n",
+ pathname, sess->sessname, dev);
ret = PTR_ERR(dev);
goto put_sess;
}
@@ -1630,10 +1609,11 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname,
}
rnbd_clt_info(dev,
- "map_device: Device mapped as %s (nsectors: %llu, logical_block_size: %d, physical_block_size: %d, max_discard_sectors: %d, discard_granularity: %d, discard_alignment: %d, secure_discard: %d, max_segments: %d, max_hw_sectors: %d, wc: %d, fua: %d)\n",
+ "map_device: Device mapped as %s (nsectors: %llu, logical_block_size: %d, physical_block_size: %d, max_write_zeroes_sectors: %d, max_discard_sectors: %d, discard_granularity: %d, discard_alignment: %d, secure_discard: %d, max_segments: %d, max_hw_sectors: %d, wc: %d, fua: %d)\n",
dev->gd->disk_name, le64_to_cpu(rsp->nsectors),
le16_to_cpu(rsp->logical_block_size),
le16_to_cpu(rsp->physical_block_size),
+ le32_to_cpu(rsp->max_write_zeroes_sectors),
le32_to_cpu(rsp->max_discard_sectors),
le32_to_cpu(rsp->discard_granularity),
le32_to_cpu(rsp->discard_alignment),
@@ -1829,7 +1809,7 @@ static int __init rnbd_client_init(void)
unregister_blkdev(rnbd_client_major, "rnbd");
return err;
}
- rnbd_clt_wq = alloc_workqueue("rnbd_clt_wq", 0, 0);
+ rnbd_clt_wq = alloc_workqueue("rnbd_clt_wq", WQ_PERCPU, 0);
if (!rnbd_clt_wq) {
pr_err("Failed to load module, alloc_workqueue failed.\n");
rnbd_clt_destroy_sysfs_files();
diff --git a/drivers/block/rnbd/rnbd-common.c b/drivers/block/rnbd/rnbd-common.c
deleted file mode 100644
index 596c3f732403..000000000000
--- a/drivers/block/rnbd/rnbd-common.c
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * RDMA Network Block Driver
- *
- * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
- * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
- * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
- */
-#include "rnbd-proto.h"
-
-const char *rnbd_access_mode_str(enum rnbd_access_mode mode)
-{
- switch (mode) {
- case RNBD_ACCESS_RO:
- return "ro";
- case RNBD_ACCESS_RW:
- return "rw";
- case RNBD_ACCESS_MIGRATION:
- return "migration";
- default:
- return "unknown";
- }
-}
diff --git a/drivers/block/rnbd/rnbd-proto.h b/drivers/block/rnbd/rnbd-proto.h
index ea7ac8bca63c..77360c2a6069 100644
--- a/drivers/block/rnbd/rnbd-proto.h
+++ b/drivers/block/rnbd/rnbd-proto.h
@@ -24,7 +24,7 @@
#define RTRS_PORT 1234
/**
- * enum rnbd_msg_types - RNBD message types
+ * enum rnbd_msg_type - RNBD message types
* @RNBD_MSG_SESS_INFO: initial session info from client to server
* @RNBD_MSG_SESS_INFO_RSP: initial session info from server to client
* @RNBD_MSG_OPEN: open (map) device request
@@ -47,10 +47,11 @@ enum rnbd_msg_type {
*/
struct rnbd_msg_hdr {
__le16 type;
+ /* private: */
__le16 __padding;
};
-/**
+/*
* We allow to map RO many times and RW only once. We allow to map yet another
* time RW, if MIGRATION is provided (second RW export can be required for
* example for VM migration)
@@ -61,6 +62,15 @@ enum rnbd_access_mode {
RNBD_ACCESS_MIGRATION,
};
+static const __maybe_unused struct {
+ enum rnbd_access_mode mode;
+ const char *str;
+} rnbd_access_modes[] = {
+ [RNBD_ACCESS_RO] = {RNBD_ACCESS_RO, "ro"},
+ [RNBD_ACCESS_RW] = {RNBD_ACCESS_RW, "rw"},
+ [RNBD_ACCESS_MIGRATION] = {RNBD_ACCESS_MIGRATION, "migration"},
+};
+
/**
* struct rnbd_msg_sess_info - initial session info from client to server
* @hdr: message header
@@ -69,6 +79,7 @@ enum rnbd_access_mode {
struct rnbd_msg_sess_info {
struct rnbd_msg_hdr hdr;
u8 ver;
+ /* private: */
u8 reserved[31];
};
@@ -80,6 +91,7 @@ struct rnbd_msg_sess_info {
struct rnbd_msg_sess_info_rsp {
struct rnbd_msg_hdr hdr;
u8 ver;
+ /* private: */
u8 reserved[31];
};
@@ -88,13 +100,16 @@ struct rnbd_msg_sess_info_rsp {
* @hdr: message header
* @access_mode: the mode to open remote device, valid values see:
* enum rnbd_access_mode
- * @device_name: device path on remote side
+ * @dev_name: device path on remote side
*/
struct rnbd_msg_open {
struct rnbd_msg_hdr hdr;
u8 access_mode;
+ /* private: */
u8 resv1;
+ /* public: */
s8 dev_name[NAME_MAX];
+ /* private: */
u8 reserved[3];
};
@@ -119,7 +134,7 @@ enum rnbd_cache_policy {
* @device_id: device_id on server side to identify the device
* @nsectors: number of sectors in the usual 512b unit
* @max_hw_sectors: max hardware sectors in the usual 512b unit
- * @max_write_same_sectors: max sectors for WRITE SAME in the 512b unit
+ * @max_write_zeroes_sectors: max sectors for WRITE ZEROES in the 512b unit
* @max_discard_sectors: max. sectors that can be discarded at once in 512b
* unit.
* @discard_granularity: size of the internal discard allocation unit in bytes
@@ -136,7 +151,7 @@ struct rnbd_msg_open_rsp {
__le32 device_id;
__le64 nsectors;
__le32 max_hw_sectors;
- __le32 max_write_same_sectors;
+ __le32 max_write_zeroes_sectors;
__le32 max_discard_sectors;
__le32 discard_granularity;
__le32 discard_alignment;
@@ -146,6 +161,7 @@ struct rnbd_msg_open_rsp {
__le16 secure_discard;
u8 obsolete_rotational;
u8 cache_policy;
+ /* private: */
u8 reserved[10];
};
@@ -177,31 +193,24 @@ struct rnbd_msg_io {
* @RNBD_OP_FLUSH: flush the volatile write cache
* @RNBD_OP_DISCARD: discard sectors
* @RNBD_OP_SECURE_ERASE: securely erase sectors
- * @RNBD_OP_WRITE_SAME: write the same sectors many times
-
+ * @RNBD_OP_WRITE_ZEROES: write zeroes sectors
+ *
* @RNBD_F_SYNC: request is sync (sync write or read)
* @RNBD_F_FUA: forced unit access
*/
enum rnbd_io_flags {
/* Operations */
-
RNBD_OP_READ = 0,
RNBD_OP_WRITE = 1,
RNBD_OP_FLUSH = 2,
RNBD_OP_DISCARD = 3,
RNBD_OP_SECURE_ERASE = 4,
- RNBD_OP_WRITE_SAME = 5,
-
- RNBD_OP_LAST,
+ RNBD_OP_WRITE_ZEROES = 5,
/* Flags */
-
RNBD_F_SYNC = 1<<(RNBD_OP_BITS + 0),
RNBD_F_FUA = 1<<(RNBD_OP_BITS + 1),
-
- RNBD_F_ALL = (RNBD_F_SYNC | RNBD_F_FUA)
-
};
static inline u32 rnbd_op(u32 flags)
@@ -214,21 +223,6 @@ static inline u32 rnbd_flags(u32 flags)
return flags & ~RNBD_OP_MASK;
}
-static inline bool rnbd_flags_supported(u32 flags)
-{
- u32 op;
-
- op = rnbd_op(flags);
- flags = rnbd_flags(flags);
-
- if (op >= RNBD_OP_LAST)
- return false;
- if (flags & ~RNBD_F_ALL)
- return false;
-
- return true;
-}
-
static inline blk_opf_t rnbd_to_bio_flags(u32 rnbd_opf)
{
blk_opf_t bio_opf;
@@ -241,7 +235,7 @@ static inline blk_opf_t rnbd_to_bio_flags(u32 rnbd_opf)
bio_opf = REQ_OP_WRITE;
break;
case RNBD_OP_FLUSH:
- bio_opf = REQ_OP_FLUSH | REQ_PREFLUSH;
+ bio_opf = REQ_OP_WRITE | REQ_PREFLUSH;
break;
case RNBD_OP_DISCARD:
bio_opf = REQ_OP_DISCARD;
@@ -249,6 +243,9 @@ static inline blk_opf_t rnbd_to_bio_flags(u32 rnbd_opf)
case RNBD_OP_SECURE_ERASE:
bio_opf = REQ_OP_SECURE_ERASE;
break;
+ case RNBD_OP_WRITE_ZEROES:
+ bio_opf = REQ_OP_WRITE_ZEROES;
+ break;
default:
WARN(1, "Unknown RNBD type: %d (flags %d)\n",
rnbd_op(rnbd_opf), rnbd_opf);
@@ -281,6 +278,9 @@ static inline u32 rq_to_rnbd_flags(struct request *rq)
case REQ_OP_SECURE_ERASE:
rnbd_opf = RNBD_OP_SECURE_ERASE;
break;
+ case REQ_OP_WRITE_ZEROES:
+ rnbd_opf = RNBD_OP_WRITE_ZEROES;
+ break;
case REQ_OP_FLUSH:
rnbd_opf = RNBD_OP_FLUSH;
break;
diff --git a/drivers/block/rnbd/rnbd-srv-dev.c b/drivers/block/rnbd/rnbd-srv-dev.c
deleted file mode 100644
index c63017f6e421..000000000000
--- a/drivers/block/rnbd/rnbd-srv-dev.c
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * RDMA Network Block Driver
- *
- * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
- * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
- * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
- */
-#undef pr_fmt
-#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
-
-#include "rnbd-srv-dev.h"
-#include "rnbd-log.h"
-
-struct rnbd_dev *rnbd_dev_open(const char *path, fmode_t flags)
-{
- struct rnbd_dev *dev;
- int ret;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return ERR_PTR(-ENOMEM);
-
- dev->blk_open_flags = flags;
- dev->bdev = blkdev_get_by_path(path, flags, THIS_MODULE);
- ret = PTR_ERR_OR_ZERO(dev->bdev);
- if (ret)
- goto err;
-
- dev->blk_open_flags = flags;
-
- return dev;
-
-err:
- kfree(dev);
- return ERR_PTR(ret);
-}
-
-void rnbd_dev_close(struct rnbd_dev *dev)
-{
- blkdev_put(dev->bdev, dev->blk_open_flags);
- kfree(dev);
-}
diff --git a/drivers/block/rnbd/rnbd-srv-dev.h b/drivers/block/rnbd/rnbd-srv-dev.h
deleted file mode 100644
index 8407d12f70af..000000000000
--- a/drivers/block/rnbd/rnbd-srv-dev.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * RDMA Network Block Driver
- *
- * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
- * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
- * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
- */
-#ifndef RNBD_SRV_DEV_H
-#define RNBD_SRV_DEV_H
-
-#include <linux/fs.h>
-#include "rnbd-proto.h"
-
-struct rnbd_dev {
- struct block_device *bdev;
- fmode_t blk_open_flags;
-};
-
-/**
- * rnbd_dev_open() - Open a device
- * @path: path to open
- * @flags: open flags
- */
-struct rnbd_dev *rnbd_dev_open(const char *path, fmode_t flags);
-
-/**
- * rnbd_dev_close() - Close a device
- */
-void rnbd_dev_close(struct rnbd_dev *dev);
-
-void rnbd_endio(void *priv, int error);
-
-static inline int rnbd_dev_get_max_segs(const struct rnbd_dev *dev)
-{
- return queue_max_segments(bdev_get_queue(dev->bdev));
-}
-
-static inline int rnbd_dev_get_max_hw_sects(const struct rnbd_dev *dev)
-{
- return queue_max_hw_sectors(bdev_get_queue(dev->bdev));
-}
-
-static inline int rnbd_dev_get_secure_discard(const struct rnbd_dev *dev)
-{
- return bdev_max_secure_erase_sectors(dev->bdev);
-}
-
-static inline int rnbd_dev_get_max_discard_sects(const struct rnbd_dev *dev)
-{
- return bdev_max_discard_sectors(dev->bdev);
-}
-
-static inline int rnbd_dev_get_discard_granularity(const struct rnbd_dev *dev)
-{
- return bdev_get_queue(dev->bdev)->limits.discard_granularity;
-}
-
-static inline int rnbd_dev_get_discard_alignment(const struct rnbd_dev *dev)
-{
- return bdev_discard_alignment(dev->bdev);
-}
-
-#endif /* RNBD_SRV_DEV_H */
diff --git a/drivers/block/rnbd/rnbd-srv-sysfs.c b/drivers/block/rnbd/rnbd-srv-sysfs.c
index 297a6924ff4e..64780094442c 100644
--- a/drivers/block/rnbd/rnbd-srv-sysfs.c
+++ b/drivers/block/rnbd/rnbd-srv-sysfs.c
@@ -9,7 +9,6 @@
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
-#include <uapi/linux/limits.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/stat.h>
@@ -20,7 +19,9 @@
#include "rnbd-srv.h"
static struct device *rnbd_dev;
-static struct class *rnbd_dev_class;
+static const struct class rnbd_dev_class = {
+ .name = "rnbd-server",
+};
static struct kobject *rnbd_devs_kobj;
static void rnbd_srv_dev_release(struct kobject *kobj)
@@ -32,7 +33,7 @@ static void rnbd_srv_dev_release(struct kobject *kobj)
kfree(dev);
}
-static struct kobj_type dev_ktype = {
+static const struct kobj_type dev_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = rnbd_srv_dev_release
};
@@ -88,8 +89,7 @@ static ssize_t read_only_show(struct kobject *kobj, struct kobj_attribute *attr,
sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
- return sysfs_emit(page, "%d\n",
- !(sess_dev->open_flags & FMODE_WRITE));
+ return sysfs_emit(page, "%d\n", sess_dev->readonly);
}
static struct kobj_attribute rnbd_srv_dev_session_ro_attr =
@@ -104,7 +104,7 @@ static ssize_t access_mode_show(struct kobject *kobj,
sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
return sysfs_emit(page, "%s\n",
- rnbd_access_mode_str(sess_dev->access_mode));
+ rnbd_access_modes[sess_dev->access_mode].str);
}
static struct kobj_attribute rnbd_srv_dev_session_access_mode_attr =
@@ -184,7 +184,7 @@ static void rnbd_srv_sess_dev_release(struct kobject *kobj)
rnbd_destroy_sess_dev(sess_dev, sess_dev->keep_id);
}
-static struct kobj_type rnbd_srv_sess_dev_ktype = {
+static const struct kobj_type rnbd_srv_sess_dev_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = rnbd_srv_sess_dev_release,
};
@@ -215,12 +215,12 @@ int rnbd_srv_create_sysfs_files(void)
{
int err;
- rnbd_dev_class = class_create(THIS_MODULE, "rnbd-server");
- if (IS_ERR(rnbd_dev_class))
- return PTR_ERR(rnbd_dev_class);
+ err = class_register(&rnbd_dev_class);
+ if (err)
+ return err;
- rnbd_dev = device_create(rnbd_dev_class, NULL,
- MKDEV(0, 0), NULL, "ctl");
+ rnbd_dev = device_create(&rnbd_dev_class, NULL,
+ MKDEV(0, 0), NULL, "ctl");
if (IS_ERR(rnbd_dev)) {
err = PTR_ERR(rnbd_dev);
goto cls_destroy;
@@ -234,9 +234,9 @@ int rnbd_srv_create_sysfs_files(void)
return 0;
dev_destroy:
- device_destroy(rnbd_dev_class, MKDEV(0, 0));
+ device_destroy(&rnbd_dev_class, MKDEV(0, 0));
cls_destroy:
- class_destroy(rnbd_dev_class);
+ class_unregister(&rnbd_dev_class);
return err;
}
@@ -245,6 +245,6 @@ void rnbd_srv_destroy_sysfs_files(void)
{
kobject_del(rnbd_devs_kobj);
kobject_put(rnbd_devs_kobj);
- device_destroy(rnbd_dev_class, MKDEV(0, 0));
- class_destroy(rnbd_dev_class);
+ device_destroy(&rnbd_dev_class, MKDEV(0, 0));
+ class_unregister(&rnbd_dev_class);
}
diff --git a/drivers/block/rnbd/rnbd-srv-trace.c b/drivers/block/rnbd/rnbd-srv-trace.c
new file mode 100644
index 000000000000..30f0895c18f5
--- /dev/null
+++ b/drivers/block/rnbd/rnbd-srv-trace.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * RDMA Network Block Driver
+ *
+ * Copyright (c) 2022 1&1 IONOS SE. All rights reserved.
+ */
+#include "rtrs.h"
+#include "rtrs-srv.h"
+#include "rnbd-srv.h"
+#include "rnbd-proto.h"
+
+/*
+ * We include this last to have the helpers above available for the trace
+ * event implementations.
+ */
+#define CREATE_TRACE_POINTS
+#include "rnbd-srv-trace.h"
diff --git a/drivers/block/rnbd/rnbd-srv-trace.h b/drivers/block/rnbd/rnbd-srv-trace.h
new file mode 100644
index 000000000000..89d0bcb17195
--- /dev/null
+++ b/drivers/block/rnbd/rnbd-srv-trace.h
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * RDMA Network Block Driver
+ *
+ * Copyright (c) 2022 1&1 IONOS SE. All rights reserved.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rnbd_srv
+
+#if !defined(_TRACE_RNBD_SRV_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RNBD_SRV_H
+
+#include <linux/tracepoint.h>
+
+struct rnbd_srv_session;
+struct rtrs_srv_op;
+
+DECLARE_EVENT_CLASS(rnbd_srv_link_class,
+ TP_PROTO(struct rnbd_srv_session *srv),
+
+ TP_ARGS(srv),
+
+ TP_STRUCT__entry(
+ __field(int, qdepth)
+ __string(sessname, srv->sessname)
+ ),
+
+ TP_fast_assign(
+ __entry->qdepth = srv->queue_depth;
+ __assign_str(sessname);
+ ),
+
+ TP_printk("sessname: %s qdepth: %d",
+ __get_str(sessname),
+ __entry->qdepth
+ )
+);
+
+#define DEFINE_LINK_EVENT(name) \
+DEFINE_EVENT(rnbd_srv_link_class, name, \
+ TP_PROTO(struct rnbd_srv_session *srv), \
+ TP_ARGS(srv))
+
+DEFINE_LINK_EVENT(create_sess);
+DEFINE_LINK_EVENT(destroy_sess);
+
+TRACE_DEFINE_ENUM(RNBD_OP_READ);
+TRACE_DEFINE_ENUM(RNBD_OP_WRITE);
+TRACE_DEFINE_ENUM(RNBD_OP_FLUSH);
+TRACE_DEFINE_ENUM(RNBD_OP_DISCARD);
+TRACE_DEFINE_ENUM(RNBD_OP_SECURE_ERASE);
+TRACE_DEFINE_ENUM(RNBD_F_SYNC);
+TRACE_DEFINE_ENUM(RNBD_F_FUA);
+
+#define show_rnbd_rw_flags(x) \
+ __print_flags(x, "|", \
+ { RNBD_OP_READ, "READ" }, \
+ { RNBD_OP_WRITE, "WRITE" }, \
+ { RNBD_OP_FLUSH, "FLUSH" }, \
+ { RNBD_OP_DISCARD, "DISCARD" }, \
+ { RNBD_OP_SECURE_ERASE, "SECURE_ERASE" }, \
+ { RNBD_F_SYNC, "SYNC" }, \
+ { RNBD_F_FUA, "FUA" })
+
+TRACE_EVENT(process_rdma,
+ TP_PROTO(struct rnbd_srv_session *srv,
+ const struct rnbd_msg_io *msg,
+ struct rtrs_srv_op *id,
+ u32 datalen,
+ size_t usrlen),
+
+ TP_ARGS(srv, msg, id, datalen, usrlen),
+
+ TP_STRUCT__entry(
+ __string(sessname, srv->sessname)
+ __field(u8, dir)
+ __field(u8, ver)
+ __field(u32, device_id)
+ __field(u64, sector)
+ __field(u32, flags)
+ __field(u32, bi_size)
+ __field(u16, ioprio)
+ __field(u32, datalen)
+ __field(size_t, usrlen)
+ ),
+
+ TP_fast_assign(
+ __assign_str(sessname);
+ __entry->dir = id->dir;
+ __entry->ver = srv->ver;
+ __entry->device_id = le32_to_cpu(msg->device_id);
+ __entry->sector = le64_to_cpu(msg->sector);
+ __entry->bi_size = le32_to_cpu(msg->bi_size);
+ __entry->flags = le32_to_cpu(msg->rw);
+ __entry->ioprio = le16_to_cpu(msg->prio);
+ __entry->datalen = datalen;
+ __entry->usrlen = usrlen;
+ ),
+
+ TP_printk("I/O req: sess: %s, type: %s, ver: %d, devid: %u, sector: %llu, bsize: %u, flags: %s, ioprio: %d, datalen: %u, usrlen: %zu",
+ __get_str(sessname),
+ __print_symbolic(__entry->dir,
+ { READ, "READ" },
+ { WRITE, "WRITE" }),
+ __entry->ver,
+ __entry->device_id,
+ __entry->sector,
+ __entry->bi_size,
+ show_rnbd_rw_flags(__entry->flags),
+ __entry->ioprio,
+ __entry->datalen,
+ __entry->usrlen
+ )
+);
+
+TRACE_EVENT(process_msg_sess_info,
+ TP_PROTO(struct rnbd_srv_session *srv,
+ const struct rnbd_msg_sess_info *msg),
+
+ TP_ARGS(srv, msg),
+
+ TP_STRUCT__entry(
+ __field(u8, proto_ver)
+ __field(u8, clt_ver)
+ __field(u8, srv_ver)
+ __string(sessname, srv->sessname)
+ ),
+
+ TP_fast_assign(
+ __entry->proto_ver = srv->ver;
+ __entry->clt_ver = msg->ver;
+ __entry->srv_ver = RNBD_PROTO_VER_MAJOR;
+ __assign_str(sessname);
+ ),
+
+ TP_printk("Session %s using proto-ver %d (clt-ver: %d, srv-ver: %d)",
+ __get_str(sessname),
+ __entry->proto_ver,
+ __entry->clt_ver,
+ __entry->srv_ver
+ )
+);
+
+TRACE_DEFINE_ENUM(RNBD_ACCESS_RO);
+TRACE_DEFINE_ENUM(RNBD_ACCESS_RW);
+TRACE_DEFINE_ENUM(RNBD_ACCESS_MIGRATION);
+
+#define show_rnbd_access_mode(x) \
+ __print_symbolic(x, \
+ { RNBD_ACCESS_RO, "RO" }, \
+ { RNBD_ACCESS_RW, "RW" }, \
+ { RNBD_ACCESS_MIGRATION, "MIGRATION" })
+
+TRACE_EVENT(process_msg_open,
+ TP_PROTO(struct rnbd_srv_session *srv,
+ const struct rnbd_msg_open *msg),
+
+ TP_ARGS(srv, msg),
+
+ TP_STRUCT__entry(
+ __field(u8, access_mode)
+ __string(sessname, srv->sessname)
+ __string(dev_name, msg->dev_name)
+ ),
+
+ TP_fast_assign(
+ __entry->access_mode = msg->access_mode;
+ __assign_str(sessname);
+ __assign_str(dev_name);
+ ),
+
+ TP_printk("Open message received: session='%s' path='%s' access_mode=%s",
+ __get_str(sessname),
+ __get_str(dev_name),
+ show_rnbd_access_mode(__entry->access_mode)
+ )
+);
+
+TRACE_EVENT(process_msg_close,
+ TP_PROTO(struct rnbd_srv_session *srv,
+ const struct rnbd_msg_close *msg),
+
+ TP_ARGS(srv, msg),
+
+ TP_STRUCT__entry(
+ __field(u32, device_id)
+ __string(sessname, srv->sessname)
+ ),
+
+ TP_fast_assign(
+ __entry->device_id = le32_to_cpu(msg->device_id);
+ __assign_str(sessname);
+ ),
+
+ TP_printk("Close message received: session='%s' device id='%d'",
+ __get_str(sessname),
+ __entry->device_id
+ )
+);
+
+#endif /* _TRACE_RNBD_SRV_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE rnbd-srv-trace
+#include <trace/define_trace.h>
+
diff --git a/drivers/block/rnbd/rnbd-srv.c b/drivers/block/rnbd/rnbd-srv.c
index 5e08da277ddf..2df8941a6b14 100644
--- a/drivers/block/rnbd/rnbd-srv.c
+++ b/drivers/block/rnbd/rnbd-srv.c
@@ -13,7 +13,7 @@
#include <linux/blkdev.h>
#include "rnbd-srv.h"
-#include "rnbd-srv-dev.h"
+#include "rnbd-srv-trace.h"
MODULE_DESCRIPTION("RDMA Network Block Device Server");
MODULE_LICENSE("GPL");
@@ -84,18 +84,6 @@ static inline void rnbd_put_sess_dev(struct rnbd_srv_sess_dev *sess_dev)
kref_put(&sess_dev->kref, rnbd_sess_dev_release);
}
-void rnbd_endio(void *priv, int error)
-{
- struct rnbd_io_private *rnbd_priv = priv;
- struct rnbd_srv_sess_dev *sess_dev = rnbd_priv->sess_dev;
-
- rnbd_put_sess_dev(sess_dev);
-
- rtrs_srv_resp_rdma(rnbd_priv->id, error);
-
- kfree(priv);
-}
-
static struct rnbd_srv_sess_dev *
rnbd_get_sess_dev(int dev_id, struct rnbd_srv_session *srv_sess)
{
@@ -108,7 +96,7 @@ rnbd_get_sess_dev(int dev_id, struct rnbd_srv_session *srv_sess)
ret = kref_get_unless_zero(&sess_dev->kref);
rcu_read_unlock();
- if (!sess_dev || !ret)
+ if (!ret)
return ERR_PTR(-ENXIO);
return sess_dev;
@@ -116,7 +104,13 @@ rnbd_get_sess_dev(int dev_id, struct rnbd_srv_session *srv_sess)
static void rnbd_dev_bi_end_io(struct bio *bio)
{
- rnbd_endio(bio->bi_private, blk_status_to_errno(bio->bi_status));
+ struct rnbd_io_private *rnbd_priv = bio->bi_private;
+ struct rnbd_srv_sess_dev *sess_dev = rnbd_priv->sess_dev;
+
+ rnbd_put_sess_dev(sess_dev);
+ rtrs_srv_resp_rdma(rnbd_priv->id, blk_status_to_errno(bio->bi_status));
+
+ kfree(rnbd_priv);
bio_put(bio);
}
@@ -132,6 +126,8 @@ static int process_rdma(struct rnbd_srv_session *srv_sess,
struct bio *bio;
short prio;
+ trace_process_rdma(srv_sess, msg, id, datalen, usrlen);
+
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -140,8 +136,8 @@ static int process_rdma(struct rnbd_srv_session *srv_sess,
sess_dev = rnbd_get_sess_dev(dev_id, srv_sess);
if (IS_ERR(sess_dev)) {
- pr_err_ratelimited("Got I/O request on session %s for unknown device id %d\n",
- srv_sess->sessname, dev_id);
+ pr_err_ratelimited("Got I/O request on session %s for unknown device id %d: %pe\n",
+ srv_sess->sessname, dev_id, sess_dev);
err = -ENOTCONN;
goto err;
}
@@ -149,22 +145,24 @@ static int process_rdma(struct rnbd_srv_session *srv_sess,
priv->sess_dev = sess_dev;
priv->id = id;
- bio = bio_alloc(sess_dev->rnbd_dev->bdev, 1,
+ bio = bio_alloc(file_bdev(sess_dev->bdev_file), 1,
rnbd_to_bio_flags(le32_to_cpu(msg->rw)), GFP_KERNEL);
- if (bio_add_page(bio, virt_to_page(data), datalen,
- offset_in_page(data)) != datalen) {
- rnbd_srv_err(sess_dev, "Failed to map data to bio\n");
+ bio_add_virt_nofail(bio, data, datalen);
+
+ bio->bi_opf = rnbd_to_bio_flags(le32_to_cpu(msg->rw));
+ if (bio_has_data(bio) &&
+ bio->bi_iter.bi_size != le32_to_cpu(msg->bi_size)) {
+ rnbd_srv_err_rl(sess_dev, "Datalen mismatch: bio bi_size (%u), bi_size (%u)\n",
+ bio->bi_iter.bi_size, msg->bi_size);
err = -EINVAL;
goto bio_put;
}
-
bio->bi_end_io = rnbd_dev_bi_end_io;
bio->bi_private = priv;
bio->bi_iter.bi_sector = le64_to_cpu(msg->sector);
- bio->bi_iter.bi_size = le32_to_cpu(msg->bi_size);
prio = srv_sess->ver < RNBD_PROTO_VER_MAJOR ||
usrlen < sizeof(*msg) ? 0 : le16_to_cpu(msg->prio);
- bio_set_prio(bio, prio);
+ bio->bi_ioprio = prio;
submit_bio(bio);
@@ -184,7 +182,7 @@ static void destroy_device(struct kref *kref)
WARN_ONCE(!list_empty(&dev->sess_dev_list),
"Device %s is being destroyed but still in use!\n",
- dev->id);
+ dev->name);
spin_lock(&dev_lock);
list_del(&dev->list);
@@ -223,10 +221,10 @@ void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev, bool keep_id)
rnbd_put_sess_dev(sess_dev);
wait_for_completion(&dc); /* wait for inflights to drop to zero */
- rnbd_dev_close(sess_dev->rnbd_dev);
+ fput(sess_dev->bdev_file);
mutex_lock(&sess_dev->dev->lock);
list_del(&sess_dev->dev_list);
- if (sess_dev->open_flags & FMODE_WRITE)
+ if (!sess_dev->readonly)
sess_dev->dev->open_write_cnt--;
mutex_unlock(&sess_dev->dev->lock);
@@ -244,6 +242,8 @@ static void destroy_sess(struct rnbd_srv_session *srv_sess)
if (xa_empty(&srv_sess->index_idr))
goto out;
+ trace_destroy_sess(srv_sess);
+
mutex_lock(&srv_sess->lock);
xa_for_each(&srv_sess->index_idr, index, sess_dev)
rnbd_srv_destroy_dev_session_sysfs(sess_dev);
@@ -290,6 +290,8 @@ static int create_sess(struct rtrs_srv_sess *rtrs)
rtrs_srv_set_sess_priv(rtrs, srv_sess);
+ trace_create_sess(srv_sess);
+
return 0;
}
@@ -332,37 +334,37 @@ void rnbd_srv_sess_dev_force_close(struct rnbd_srv_sess_dev *sess_dev,
mutex_unlock(&sess->lock);
}
-static int process_msg_close(struct rnbd_srv_session *srv_sess,
+static void process_msg_close(struct rnbd_srv_session *srv_sess,
void *data, size_t datalen, const void *usr,
size_t usrlen)
{
const struct rnbd_msg_close *close_msg = usr;
struct rnbd_srv_sess_dev *sess_dev;
+ trace_process_msg_close(srv_sess, close_msg);
+
sess_dev = rnbd_get_sess_dev(le32_to_cpu(close_msg->device_id),
srv_sess);
if (IS_ERR(sess_dev))
- return 0;
+ return;
rnbd_put_sess_dev(sess_dev);
mutex_lock(&srv_sess->lock);
rnbd_srv_destroy_dev_session_sysfs(sess_dev);
mutex_unlock(&srv_sess->lock);
- return 0;
}
static int process_msg_open(struct rnbd_srv_session *srv_sess,
const void *msg, size_t len,
void *data, size_t datalen);
-static int process_msg_sess_info(struct rnbd_srv_session *srv_sess,
+static void process_msg_sess_info(struct rnbd_srv_session *srv_sess,
const void *msg, size_t len,
void *data, size_t datalen);
-static int rnbd_srv_rdma_ev(void *priv,
- struct rtrs_srv_op *id, int dir,
- void *data, size_t datalen, const void *usr,
- size_t usrlen)
+static int rnbd_srv_rdma_ev(void *priv, struct rtrs_srv_op *id,
+ void *data, size_t datalen,
+ const void *usr, size_t usrlen)
{
struct rnbd_srv_session *srv_sess = priv;
const struct rnbd_msg_hdr *hdr = usr;
@@ -378,21 +380,25 @@ static int rnbd_srv_rdma_ev(void *priv,
case RNBD_MSG_IO:
return process_rdma(srv_sess, id, data, datalen, usr, usrlen);
case RNBD_MSG_CLOSE:
- ret = process_msg_close(srv_sess, data, datalen, usr, usrlen);
+ process_msg_close(srv_sess, data, datalen, usr, usrlen);
break;
case RNBD_MSG_OPEN:
ret = process_msg_open(srv_sess, usr, usrlen, data, datalen);
break;
case RNBD_MSG_SESS_INFO:
- ret = process_msg_sess_info(srv_sess, usr, usrlen, data,
- datalen);
+ process_msg_sess_info(srv_sess, usr, usrlen, data, datalen);
break;
default:
- pr_warn("Received unexpected message type %d with dir %d from session %s\n",
- type, dir, srv_sess->sessname);
+ pr_warn("Received unexpected message type %d from session %s\n",
+ type, srv_sess->sessname);
return -EINVAL;
}
+ /*
+ * Since ret is passed to rtrs to handle the failure case, we
+ * just return 0 at the end otherwise callers in rtrs would call
+ * send_io_resp_imm again to print redundant err message.
+ */
rtrs_srv_resp_rdma(id, ret);
return 0;
}
@@ -426,7 +432,7 @@ static struct rnbd_srv_dev *rnbd_srv_init_srv_dev(struct block_device *bdev)
if (!dev)
return ERR_PTR(-ENOMEM);
- snprintf(dev->id, sizeof(dev->id), "%pg", bdev);
+ snprintf(dev->name, sizeof(dev->name), "%pg", bdev);
kref_init(&dev->kref);
INIT_LIST_HEAD(&dev->sess_dev_list);
mutex_init(&dev->lock);
@@ -441,7 +447,7 @@ rnbd_srv_find_or_add_srv_dev(struct rnbd_srv_dev *new_dev)
spin_lock(&dev_lock);
list_for_each_entry(dev, &dev_list, list) {
- if (!strncmp(dev->id, new_dev->id, sizeof(dev->id))) {
+ if (!strncmp(dev->name, new_dev->name, sizeof(dev->name))) {
if (!kref_get_unless_zero(&dev->kref))
/*
* We lost the race, device is almost dead.
@@ -462,39 +468,38 @@ static int rnbd_srv_check_update_open_perm(struct rnbd_srv_dev *srv_dev,
struct rnbd_srv_session *srv_sess,
enum rnbd_access_mode access_mode)
{
- int ret = -EPERM;
+ int ret = 0;
mutex_lock(&srv_dev->lock);
switch (access_mode) {
case RNBD_ACCESS_RO:
- ret = 0;
break;
case RNBD_ACCESS_RW:
if (srv_dev->open_write_cnt == 0) {
srv_dev->open_write_cnt++;
- ret = 0;
} else {
pr_err("Mapping device '%s' for session %s with RW permissions failed. Device already opened as 'RW' by %d client(s), access mode %s.\n",
- srv_dev->id, srv_sess->sessname,
+ srv_dev->name, srv_sess->sessname,
srv_dev->open_write_cnt,
- rnbd_access_mode_str(access_mode));
+ rnbd_access_modes[access_mode].str);
+ ret = -EPERM;
}
break;
case RNBD_ACCESS_MIGRATION:
if (srv_dev->open_write_cnt < 2) {
srv_dev->open_write_cnt++;
- ret = 0;
} else {
pr_err("Mapping device '%s' for session %s with migration permissions failed. Device already opened as 'RW' by %d client(s), access mode %s.\n",
- srv_dev->id, srv_sess->sessname,
+ srv_dev->name, srv_sess->sessname,
srv_dev->open_write_cnt,
- rnbd_access_mode_str(access_mode));
+ rnbd_access_modes[access_mode].str);
+ ret = -EPERM;
}
break;
default:
pr_err("Received mapping request for device '%s' on session %s with invalid access mode: %d\n",
- srv_dev->id, srv_sess->sessname, access_mode);
+ srv_dev->name, srv_sess->sessname, access_mode);
ret = -EINVAL;
}
@@ -504,14 +509,14 @@ static int rnbd_srv_check_update_open_perm(struct rnbd_srv_dev *srv_dev,
}
static struct rnbd_srv_dev *
-rnbd_srv_get_or_create_srv_dev(struct rnbd_dev *rnbd_dev,
+rnbd_srv_get_or_create_srv_dev(struct block_device *bdev,
struct rnbd_srv_session *srv_sess,
enum rnbd_access_mode access_mode)
{
int ret;
struct rnbd_srv_dev *new_dev, *dev;
- new_dev = rnbd_srv_init_srv_dev(rnbd_dev->bdev);
+ new_dev = rnbd_srv_init_srv_dev(bdev);
if (IS_ERR(new_dev))
return new_dev;
@@ -531,41 +536,33 @@ rnbd_srv_get_or_create_srv_dev(struct rnbd_dev *rnbd_dev,
static void rnbd_srv_fill_msg_open_rsp(struct rnbd_msg_open_rsp *rsp,
struct rnbd_srv_sess_dev *sess_dev)
{
- struct rnbd_dev *rnbd_dev = sess_dev->rnbd_dev;
+ struct block_device *bdev = file_bdev(sess_dev->bdev_file);
rsp->hdr.type = cpu_to_le16(RNBD_MSG_OPEN_RSP);
- rsp->device_id =
- cpu_to_le32(sess_dev->device_id);
- rsp->nsectors =
- cpu_to_le64(get_capacity(rnbd_dev->bdev->bd_disk));
- rsp->logical_block_size =
- cpu_to_le16(bdev_logical_block_size(rnbd_dev->bdev));
- rsp->physical_block_size =
- cpu_to_le16(bdev_physical_block_size(rnbd_dev->bdev));
- rsp->max_segments =
- cpu_to_le16(rnbd_dev_get_max_segs(rnbd_dev));
+ rsp->device_id = cpu_to_le32(sess_dev->device_id);
+ rsp->nsectors = cpu_to_le64(bdev_nr_sectors(bdev));
+ rsp->logical_block_size = cpu_to_le16(bdev_logical_block_size(bdev));
+ rsp->physical_block_size = cpu_to_le16(bdev_physical_block_size(bdev));
+ rsp->max_segments = cpu_to_le16(bdev_max_segments(bdev));
rsp->max_hw_sectors =
- cpu_to_le32(rnbd_dev_get_max_hw_sects(rnbd_dev));
- rsp->max_write_same_sectors = 0;
- rsp->max_discard_sectors =
- cpu_to_le32(rnbd_dev_get_max_discard_sects(rnbd_dev));
- rsp->discard_granularity =
- cpu_to_le32(rnbd_dev_get_discard_granularity(rnbd_dev));
- rsp->discard_alignment =
- cpu_to_le32(rnbd_dev_get_discard_alignment(rnbd_dev));
- rsp->secure_discard =
- cpu_to_le16(rnbd_dev_get_secure_discard(rnbd_dev));
+ cpu_to_le32(queue_max_hw_sectors(bdev_get_queue(bdev)));
+ rsp->max_write_zeroes_sectors =
+ cpu_to_le32(bdev_write_zeroes_sectors(bdev));
+ rsp->max_discard_sectors = cpu_to_le32(bdev_max_discard_sectors(bdev));
+ rsp->discard_granularity = cpu_to_le32(bdev_discard_granularity(bdev));
+ rsp->discard_alignment = cpu_to_le32(bdev_discard_alignment(bdev));
+ rsp->secure_discard = cpu_to_le16(bdev_max_secure_erase_sectors(bdev));
rsp->cache_policy = 0;
- if (bdev_write_cache(rnbd_dev->bdev))
+ if (bdev_write_cache(bdev))
rsp->cache_policy |= RNBD_WRITEBACK;
- if (bdev_fua(rnbd_dev->bdev))
+ if (bdev_fua(bdev))
rsp->cache_policy |= RNBD_FUA;
}
static struct rnbd_srv_sess_dev *
rnbd_srv_create_set_sess_dev(struct rnbd_srv_session *srv_sess,
const struct rnbd_msg_open *open_msg,
- struct rnbd_dev *rnbd_dev, fmode_t open_flags,
+ struct file *bdev_file, bool readonly,
struct rnbd_srv_dev *srv_dev)
{
struct rnbd_srv_sess_dev *sdev = rnbd_sess_dev_alloc(srv_sess);
@@ -577,10 +574,10 @@ rnbd_srv_create_set_sess_dev(struct rnbd_srv_session *srv_sess,
strscpy(sdev->pathname, open_msg->dev_name, sizeof(sdev->pathname));
- sdev->rnbd_dev = rnbd_dev;
+ sdev->bdev_file = bdev_file;
sdev->sess = srv_sess;
sdev->dev = srv_dev;
- sdev->open_flags = open_flags;
+ sdev->readonly = readonly;
sdev->access_mode = open_msg->access_mode;
return sdev;
@@ -591,6 +588,7 @@ static char *rnbd_srv_get_full_path(struct rnbd_srv_session *srv_sess,
{
char *full_path;
char *a, *b;
+ int len;
full_path = kmalloc(PATH_MAX, GFP_KERNEL);
if (!full_path)
@@ -602,19 +600,19 @@ static char *rnbd_srv_get_full_path(struct rnbd_srv_session *srv_sess,
*/
a = strnstr(dev_search_path, "%SESSNAME%", sizeof(dev_search_path));
if (a) {
- int len = a - dev_search_path;
+ len = a - dev_search_path;
len = snprintf(full_path, PATH_MAX, "%.*s/%s/%s", len,
dev_search_path, srv_sess->sessname, dev_name);
- if (len >= PATH_MAX) {
- pr_err("Too long path: %s, %s, %s\n",
- dev_search_path, srv_sess->sessname, dev_name);
- kfree(full_path);
- return ERR_PTR(-EINVAL);
- }
} else {
- snprintf(full_path, PATH_MAX, "%s/%s",
- dev_search_path, dev_name);
+ len = snprintf(full_path, PATH_MAX, "%s/%s",
+ dev_search_path, dev_name);
+ }
+ if (len >= PATH_MAX) {
+ pr_err("Too long path: %s, %s, %s\n",
+ dev_search_path, srv_sess->sessname, dev_name);
+ kfree(full_path);
+ return ERR_PTR(-EINVAL);
}
/* eliminitate duplicated slashes */
@@ -635,7 +633,7 @@ static char *rnbd_srv_get_full_path(struct rnbd_srv_session *srv_sess,
return full_path;
}
-static int process_msg_sess_info(struct rnbd_srv_session *srv_sess,
+static void process_msg_sess_info(struct rnbd_srv_session *srv_sess,
const void *msg, size_t len,
void *data, size_t datalen)
{
@@ -643,14 +641,11 @@ static int process_msg_sess_info(struct rnbd_srv_session *srv_sess,
struct rnbd_msg_sess_info_rsp *rsp = data;
srv_sess->ver = min_t(u8, sess_info_msg->ver, RNBD_PROTO_VER_MAJOR);
- pr_debug("Session %s using protocol version %d (client version: %d, server version: %d)\n",
- srv_sess->sessname, srv_sess->ver,
- sess_info_msg->ver, RNBD_PROTO_VER_MAJOR);
+
+ trace_process_msg_sess_info(srv_sess, sess_info_msg);
rsp->hdr.type = cpu_to_le16(RNBD_MSG_SESS_INFO_RSP);
rsp->ver = srv_sess->ver;
-
- return 0;
}
/**
@@ -685,17 +680,15 @@ static int process_msg_open(struct rnbd_srv_session *srv_sess,
struct rnbd_srv_dev *srv_dev;
struct rnbd_srv_sess_dev *srv_sess_dev;
const struct rnbd_msg_open *open_msg = msg;
- fmode_t open_flags;
+ struct file *bdev_file;
+ blk_mode_t open_flags = BLK_OPEN_READ;
char *full_path;
- struct rnbd_dev *rnbd_dev;
struct rnbd_msg_open_rsp *rsp = data;
- pr_debug("Open message received: session='%s' path='%s' access_mode=%d\n",
- srv_sess->sessname, open_msg->dev_name,
- open_msg->access_mode);
- open_flags = FMODE_READ;
+ trace_process_msg_open(srv_sess, open_msg);
+
if (open_msg->access_mode != RNBD_ACCESS_RO)
- open_flags |= FMODE_WRITE;
+ open_flags |= BLK_OPEN_WRITE;
mutex_lock(&srv_sess->lock);
@@ -720,34 +713,35 @@ static int process_msg_open(struct rnbd_srv_session *srv_sess,
full_path = rnbd_srv_get_full_path(srv_sess, open_msg->dev_name);
if (IS_ERR(full_path)) {
ret = PTR_ERR(full_path);
- pr_err("Opening device '%s' for client %s failed, failed to get device full path, err: %d\n",
- open_msg->dev_name, srv_sess->sessname, ret);
+ pr_err("Opening device '%s' for client %s failed, failed to get device full path, err: %pe\n",
+ open_msg->dev_name, srv_sess->sessname, full_path);
goto reject;
}
- rnbd_dev = rnbd_dev_open(full_path, open_flags);
- if (IS_ERR(rnbd_dev)) {
- pr_err("Opening device '%s' on session %s failed, failed to open the block device, err: %ld\n",
- full_path, srv_sess->sessname, PTR_ERR(rnbd_dev));
- ret = PTR_ERR(rnbd_dev);
+ bdev_file = bdev_file_open_by_path(full_path, open_flags, NULL, NULL);
+ if (IS_ERR(bdev_file)) {
+ ret = PTR_ERR(bdev_file);
+ pr_err("Opening device '%s' on session %s failed, failed to open the block device, err: %pe\n",
+ full_path, srv_sess->sessname, bdev_file);
goto free_path;
}
- srv_dev = rnbd_srv_get_or_create_srv_dev(rnbd_dev, srv_sess,
+ srv_dev = rnbd_srv_get_or_create_srv_dev(file_bdev(bdev_file), srv_sess,
open_msg->access_mode);
if (IS_ERR(srv_dev)) {
- pr_err("Opening device '%s' on session %s failed, creating srv_dev failed, err: %ld\n",
- full_path, srv_sess->sessname, PTR_ERR(srv_dev));
+ pr_err("Opening device '%s' on session %s failed, creating srv_dev failed, err: %pe\n",
+ full_path, srv_sess->sessname, srv_dev);
ret = PTR_ERR(srv_dev);
- goto rnbd_dev_close;
+ goto blkdev_put;
}
srv_sess_dev = rnbd_srv_create_set_sess_dev(srv_sess, open_msg,
- rnbd_dev, open_flags,
- srv_dev);
+ bdev_file,
+ open_msg->access_mode == RNBD_ACCESS_RO,
+ srv_dev);
if (IS_ERR(srv_sess_dev)) {
- pr_err("Opening device '%s' on session %s failed, creating sess_dev failed, err: %ld\n",
- full_path, srv_sess->sessname, PTR_ERR(srv_sess_dev));
+ pr_err("Opening device '%s' on session %s failed, creating sess_dev failed, err: %pe\n",
+ full_path, srv_sess->sessname, srv_sess_dev);
ret = PTR_ERR(srv_sess_dev);
goto srv_dev_put;
}
@@ -758,7 +752,7 @@ static int process_msg_open(struct rnbd_srv_session *srv_sess,
*/
mutex_lock(&srv_dev->lock);
if (!srv_dev->dev_kobj.state_in_sysfs) {
- ret = rnbd_srv_create_dev_sysfs(srv_dev, rnbd_dev->bdev);
+ ret = rnbd_srv_create_dev_sysfs(srv_dev, file_bdev(bdev_file));
if (ret) {
mutex_unlock(&srv_dev->lock);
rnbd_srv_err(srv_sess_dev,
@@ -780,7 +774,7 @@ static int process_msg_open(struct rnbd_srv_session *srv_sess,
list_add(&srv_sess_dev->dev_list, &srv_dev->sess_dev_list);
mutex_unlock(&srv_dev->lock);
- rnbd_srv_info(srv_sess_dev, "Opened device '%s'\n", srv_dev->id);
+ rnbd_srv_info(srv_sess_dev, "Opened device '%s'\n", srv_dev->name);
kfree(full_path);
@@ -800,8 +794,8 @@ srv_dev_put:
mutex_unlock(&srv_dev->lock);
}
rnbd_put_srv_dev(srv_dev);
-rnbd_dev_close:
- rnbd_dev_close(rnbd_dev);
+blkdev_put:
+ fput(bdev_file);
free_path:
kfree(full_path);
reject:
@@ -814,7 +808,7 @@ static struct rtrs_srv_ctx *rtrs_ctx;
static struct rtrs_srv_ops rtrs_ops;
static int __init rnbd_srv_init_module(void)
{
- int err;
+ int err = 0;
BUILD_BUG_ON(sizeof(struct rnbd_msg_hdr) != 4);
BUILD_BUG_ON(sizeof(struct rnbd_msg_sess_info) != 36);
@@ -828,19 +822,17 @@ static int __init rnbd_srv_init_module(void)
};
rtrs_ctx = rtrs_srv_open(&rtrs_ops, port_nr);
if (IS_ERR(rtrs_ctx)) {
- err = PTR_ERR(rtrs_ctx);
- pr_err("rtrs_srv_open(), err: %d\n", err);
- return err;
+ pr_err("rtrs_srv_open(), err: %pe\n", rtrs_ctx);
+ return PTR_ERR(rtrs_ctx);
}
err = rnbd_srv_create_sysfs_files();
if (err) {
pr_err("rnbd_srv_create_sysfs_files(), err: %d\n", err);
rtrs_srv_close(rtrs_ctx);
- return err;
}
- return 0;
+ return err;
}
static void __exit rnbd_srv_cleanup_module(void)
diff --git a/drivers/block/rnbd/rnbd-srv.h b/drivers/block/rnbd/rnbd-srv.h
index 081bceaf4ae9..18d873808b8d 100644
--- a/drivers/block/rnbd/rnbd-srv.h
+++ b/drivers/block/rnbd/rnbd-srv.h
@@ -35,7 +35,7 @@ struct rnbd_srv_dev {
struct kobject dev_kobj;
struct kobject *dev_sessions_kobj;
struct kref kref;
- char id[NAME_MAX];
+ char name[NAME_MAX];
/* List of rnbd_srv_sess_dev structs */
struct list_head sess_dev_list;
struct mutex lock;
@@ -46,13 +46,13 @@ struct rnbd_srv_dev {
struct rnbd_srv_sess_dev {
/* Entry inside rnbd_srv_dev struct */
struct list_head dev_list;
- struct rnbd_dev *rnbd_dev;
+ struct file *bdev_file;
struct rnbd_srv_session *sess;
struct rnbd_srv_dev *dev;
struct kobject kobj;
u32 device_id;
bool keep_id;
- fmode_t open_flags;
+ bool readonly;
struct kref kref;
struct completion *destroy_comp;
char pathname[NAME_MAX];
diff --git a/drivers/block/rnull/Kconfig b/drivers/block/rnull/Kconfig
new file mode 100644
index 000000000000..7bc5b376c128
--- /dev/null
+++ b/drivers/block/rnull/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Rust null block device driver configuration
+
+config BLK_DEV_RUST_NULL
+ tristate "Rust null block driver (Experimental)"
+ depends on RUST && CONFIGFS_FS
+ help
+ This is the Rust implementation of the null block driver. Like
+ the C version, the driver allows the user to create virutal block
+ devices that can be configured via various configuration options.
+
+ If unsure, say N.
diff --git a/drivers/block/rnull/Makefile b/drivers/block/rnull/Makefile
new file mode 100644
index 000000000000..11cfa5e615dc
--- /dev/null
+++ b/drivers/block/rnull/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_BLK_DEV_RUST_NULL) += rnull_mod.o
+rnull_mod-y := rnull.o
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
new file mode 100644
index 000000000000..6713a6d92391
--- /dev/null
+++ b/drivers/block/rnull/configfs.rs
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use super::{NullBlkDevice, THIS_MODULE};
+use kernel::{
+ block::mq::gen_disk::{GenDisk, GenDiskBuilder},
+ c_str,
+ configfs::{self, AttributeOperations},
+ configfs_attrs,
+ fmt::{self, Write as _},
+ new_mutex,
+ page::PAGE_SIZE,
+ prelude::*,
+ str::{kstrtobool_bytes, CString},
+ sync::Mutex,
+};
+use pin_init::PinInit;
+
+pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error> {
+ let item_type = configfs_attrs! {
+ container: configfs::Subsystem<Config>,
+ data: Config,
+ child: DeviceConfig,
+ attributes: [
+ features: 0,
+ ],
+ };
+
+ kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, try_pin_init!(Config {}))
+}
+
+#[pin_data]
+pub(crate) struct Config {}
+
+#[vtable]
+impl AttributeOperations<0> for Config {
+ type Data = Config;
+
+ fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+ writer.write_str("blocksize,size,rotational,irqmode\n")?;
+ Ok(writer.bytes_written())
+ }
+}
+
+#[vtable]
+impl configfs::GroupOperations for Config {
+ type Child = DeviceConfig;
+
+ fn make_group(
+ &self,
+ name: &CStr,
+ ) -> Result<impl PinInit<configfs::Group<DeviceConfig>, Error>> {
+ let item_type = configfs_attrs! {
+ container: configfs::Group<DeviceConfig>,
+ data: DeviceConfig,
+ attributes: [
+ // Named for compatibility with C null_blk
+ power: 0,
+ blocksize: 1,
+ rotational: 2,
+ size: 3,
+ irqmode: 4,
+ ],
+ };
+
+ Ok(configfs::Group::new(
+ name.try_into()?,
+ item_type,
+ // TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>, so put mutex inside
+ try_pin_init!( DeviceConfig {
+ data <- new_mutex!(DeviceConfigInner {
+ powered: false,
+ block_size: 4096,
+ rotational: false,
+ disk: None,
+ capacity_mib: 4096,
+ irq_mode: IRQMode::None,
+ name: name.try_into()?,
+ }),
+ }),
+ ))
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum IRQMode {
+ None,
+ Soft,
+}
+
+impl TryFrom<u8> for IRQMode {
+ type Error = kernel::error::Error;
+
+ fn try_from(value: u8) -> Result<Self> {
+ match value {
+ 0 => Ok(Self::None),
+ 1 => Ok(Self::Soft),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
+impl fmt::Display for IRQMode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::None => f.write_str("0")?,
+ Self::Soft => f.write_str("1")?,
+ }
+ Ok(())
+ }
+}
+
+#[pin_data]
+pub(crate) struct DeviceConfig {
+ #[pin]
+ data: Mutex<DeviceConfigInner>,
+}
+
+#[pin_data]
+struct DeviceConfigInner {
+ powered: bool,
+ name: CString,
+ block_size: u32,
+ rotational: bool,
+ capacity_mib: u64,
+ irq_mode: IRQMode,
+ disk: Option<GenDisk<NullBlkDevice>>,
+}
+
+#[vtable]
+impl configfs::AttributeOperations<0> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+
+ if this.data.lock().powered {
+ writer.write_str("1\n")?;
+ } else {
+ writer.write_str("0\n")?;
+ }
+
+ Ok(writer.bytes_written())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ let power_op = kstrtobool_bytes(page)?;
+ let mut guard = this.data.lock();
+
+ if !guard.powered && power_op {
+ guard.disk = Some(NullBlkDevice::new(
+ &guard.name,
+ guard.block_size,
+ guard.rotational,
+ guard.capacity_mib,
+ guard.irq_mode,
+ )?);
+ guard.powered = true;
+ } else if guard.powered && !power_op {
+ drop(guard.disk.take());
+ guard.powered = false;
+ }
+
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<1> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+ writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?;
+ Ok(writer.bytes_written())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ if this.data.lock().powered {
+ return Err(EBUSY);
+ }
+
+ let text = core::str::from_utf8(page)?.trim();
+ let value = text.parse::<u32>().map_err(|_| EINVAL)?;
+
+ GenDiskBuilder::validate_block_size(value)?;
+ this.data.lock().block_size = value;
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<2> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+
+ if this.data.lock().rotational {
+ writer.write_str("1\n")?;
+ } else {
+ writer.write_str("0\n")?;
+ }
+
+ Ok(writer.bytes_written())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ if this.data.lock().powered {
+ return Err(EBUSY);
+ }
+
+ this.data.lock().rotational = kstrtobool_bytes(page)?;
+
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<3> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+ writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?;
+ Ok(writer.bytes_written())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ if this.data.lock().powered {
+ return Err(EBUSY);
+ }
+
+ let text = core::str::from_utf8(page)?.trim();
+ let value = text.parse::<u64>().map_err(|_| EINVAL)?;
+
+ this.data.lock().capacity_mib = value;
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<4> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+ writer.write_fmt(fmt!("{}\n", this.data.lock().irq_mode))?;
+ Ok(writer.bytes_written())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ if this.data.lock().powered {
+ return Err(EBUSY);
+ }
+
+ let text = core::str::from_utf8(page)?.trim();
+ let value = text.parse::<u8>().map_err(|_| EINVAL)?;
+
+ this.data.lock().irq_mode = IRQMode::try_from(value)?;
+ Ok(())
+ }
+}
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
new file mode 100644
index 000000000000..a9d5e575a2c4
--- /dev/null
+++ b/drivers/block/rnull/rnull.rs
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! This is a Rust implementation of the C null block driver.
+
+mod configfs;
+
+use configfs::IRQMode;
+use kernel::{
+ block::{
+ self,
+ mq::{
+ self,
+ gen_disk::{self, GenDisk},
+ Operations, TagSet,
+ },
+ },
+ error::Result,
+ pr_info,
+ prelude::*,
+ sync::{aref::ARef, Arc},
+};
+use pin_init::PinInit;
+
+module! {
+ type: NullBlkModule,
+ name: "rnull_mod",
+ authors: ["Andreas Hindborg"],
+ description: "Rust implementation of the C null block driver",
+ license: "GPL v2",
+}
+
+#[pin_data]
+struct NullBlkModule {
+ #[pin]
+ configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>,
+}
+
+impl kernel::InPlaceModule for NullBlkModule {
+ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
+ pr_info!("Rust null_blk loaded\n");
+
+ try_pin_init!(Self {
+ configfs_subsystem <- configfs::subsystem(),
+ })
+ }
+}
+
+struct NullBlkDevice;
+
+impl NullBlkDevice {
+ fn new(
+ name: &CStr,
+ block_size: u32,
+ rotational: bool,
+ capacity_mib: u64,
+ irq_mode: IRQMode,
+ ) -> Result<GenDisk<Self>> {
+ let tagset = Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?;
+
+ let queue_data = Box::new(QueueData { irq_mode }, GFP_KERNEL)?;
+
+ gen_disk::GenDiskBuilder::new()
+ .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT))
+ .logical_block_size(block_size)?
+ .physical_block_size(block_size)?
+ .rotational(rotational)
+ .build(fmt!("{}", name.to_str()?), tagset, queue_data)
+ }
+}
+
+struct QueueData {
+ irq_mode: IRQMode,
+}
+
+#[vtable]
+impl Operations for NullBlkDevice {
+ type QueueData = KBox<QueueData>;
+
+ #[inline(always)]
+ fn queue_rq(queue_data: &QueueData, rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
+ match queue_data.irq_mode {
+ IRQMode::None => mq::Request::end_ok(rq)
+ .map_err(|_e| kernel::error::code::EIO)
+ // We take no refcounts on the request, so we expect to be able to
+ // end the request. The request reference must be unique at this
+ // point, and so `end_ok` cannot fail.
+ .expect("Fatal error - expected to be able to end request"),
+ IRQMode::Soft => mq::Request::complete(rq),
+ }
+ Ok(())
+ }
+
+ fn commit_rqs(_queue_data: &QueueData) {}
+
+ fn complete(rq: ARef<mq::Request<Self>>) {
+ mq::Request::end_ok(rq)
+ .map_err(|_e| kernel::error::code::EIO)
+ // We take no refcounts on the request, so we expect to be able to
+ // end the request. The request reference must be unique at this
+ // point, and so `end_ok` cannot fail.
+ .expect("Fatal error - expected to be able to end request");
+ }
+}
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
index fb855da971ee..db1fe9772a4d 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -28,7 +28,7 @@
static char version[] =
DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
-MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
MODULE_DESCRIPTION("Sun LDOM virtual disk client driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
@@ -119,9 +119,8 @@ static inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr)
return vio_dring_avail(dr, VDC_TX_RING_SIZE);
}
-static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+static int vdc_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- struct gendisk *disk = bdev->bd_disk;
sector_t nsect = get_capacity(disk);
sector_t cylinders = nsect;
@@ -139,7 +138,7 @@ static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo)
* when vdisk_mtype is VD_MEDIA_TYPE_CD or VD_MEDIA_TYPE_DVD.
* Needed to be able to install inside an ldom from an iso image.
*/
-static int vdc_ioctl(struct block_device *bdev, fmode_t mode,
+static int vdc_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned command, unsigned long argument)
{
struct vdc_port *port = bdev->bd_disk->private_data;
@@ -485,7 +484,7 @@ static int __send_request(struct request *req)
}
sg_init_table(sg, port->ring_cookies);
- nsg = blk_rq_map_sg(req->q, req, sg);
+ nsg = blk_rq_map_sg(req, sg);
len = 0;
for (i = 0; i < nsg; i++)
@@ -784,6 +783,15 @@ static const struct blk_mq_ops vdc_mq_ops = {
static int probe_disk(struct vdc_port *port)
{
+ struct queue_limits lim = {
+ .physical_block_size = port->vdisk_phys_blksz,
+ .max_hw_sectors = port->max_xfer_size,
+ /* Each segment in a request is up to an aligned page in size. */
+ .seg_boundary_mask = PAGE_SIZE - 1,
+ .max_segment_size = PAGE_SIZE,
+ .max_segments = port->ring_cookies,
+ .features = BLK_FEAT_ROTATIONAL,
+ };
struct request_queue *q;
struct gendisk *g;
int err;
@@ -820,11 +828,11 @@ static int probe_disk(struct vdc_port *port)
}
err = blk_mq_alloc_sq_tag_set(&port->tag_set, &vdc_mq_ops,
- VDC_TX_RING_SIZE, BLK_MQ_F_SHOULD_MERGE);
+ VDC_TX_RING_SIZE, 0);
if (err)
return err;
- g = blk_mq_alloc_disk(&port->tag_set, port);
+ g = blk_mq_alloc_disk(&port->tag_set, &lim, port);
if (IS_ERR(g)) {
printk(KERN_ERR PFX "%s: Could not allocate gendisk.\n",
port->vio.name);
@@ -835,12 +843,6 @@ static int probe_disk(struct vdc_port *port)
port->disk = g;
q = g->queue;
- /* Each segment in a request is up to an aligned page in size. */
- blk_queue_segment_boundary(q, PAGE_SIZE - 1);
- blk_queue_max_segment_size(q, PAGE_SIZE);
-
- blk_queue_max_segments(q, port->ring_cookies);
- blk_queue_max_hw_sectors(q, port->max_xfer_size);
g->major = vdc_major;
g->first_minor = port->vio.vdev->dev_no << PARTITION_SHIFT;
g->minors = 1 << PARTITION_SHIFT;
@@ -872,8 +874,6 @@ static int probe_disk(struct vdc_port *port)
}
}
- blk_queue_physical_block_size(q, port->vdisk_phys_blksz);
-
pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n",
g->disk_name,
port->vdisk_size, (port->vdisk_size >> (20 - 9)),
@@ -917,12 +917,12 @@ struct vdc_check_port_data {
char *type;
};
-static int vdc_device_probed(struct device *dev, void *arg)
+static int vdc_device_probed(struct device *dev, const void *arg)
{
struct vio_dev *vdev = to_vio_dev(dev);
- struct vdc_check_port_data *port_data;
+ const struct vdc_check_port_data *port_data;
- port_data = (struct vdc_check_port_data *)arg;
+ port_data = (const struct vdc_check_port_data *)arg;
if ((vdev->dev_no == port_data->dev_no) &&
(!(strcmp((char *)&vdev->type, port_data->type))) &&
@@ -956,8 +956,10 @@ static bool vdc_port_mpgroup_check(struct vio_dev *vdev)
dev = device_find_child(vdev->dev.parent, &port_data,
vdc_device_probed);
- if (dev)
+ if (dev) {
+ put_device(dev);
return true;
+ }
return false;
}
@@ -972,6 +974,8 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
print_version();
hp = mdesc_grab();
+ if (!hp)
+ return -ENODEV;
err = -ENODEV;
if ((vdev->dev_no << PARTITION_SHIFT) & ~(u64)MINORMASK) {
@@ -1067,7 +1071,7 @@ static void vdc_port_remove(struct vio_dev *vdev)
flush_work(&port->ldc_reset_work);
cancel_delayed_work_sync(&port->ldc_reset_timer_work);
- del_timer_sync(&port->vio.timer);
+ timer_delete_sync(&port->vio.timer);
del_gendisk(port->disk);
put_disk(port->disk);
@@ -1110,6 +1114,7 @@ static void vdc_requeue_inflight(struct vdc_port *port)
static void vdc_queue_drain(struct vdc_port *port)
{
struct request_queue *q = port->disk->queue;
+ unsigned int memflags;
/*
* Mark the queue as draining, then freeze/quiesce to ensure
@@ -1118,13 +1123,13 @@ static void vdc_queue_drain(struct vdc_port *port)
port->drain = 1;
spin_unlock_irq(&port->vio.lock);
- blk_mq_freeze_queue(q);
+ memflags = blk_mq_freeze_queue(q);
blk_mq_quiesce_queue(q);
spin_lock_irq(&port->vio.lock);
port->drain = 0;
blk_mq_unquiesce_queue(q);
- blk_mq_unfreeze_queue(q);
+ blk_mq_unfreeze_queue(q, memflags);
}
static void vdc_ldc_reset_timer_work(struct work_struct *work)
@@ -1183,7 +1188,7 @@ static void vdc_ldc_reset(struct vdc_port *port)
}
if (port->ldc_timeout)
- mod_delayed_work(system_wq, &port->ldc_reset_timer_work,
+ mod_delayed_work(system_percpu_wq, &port->ldc_reset_timer_work,
round_jiffies(jiffies + HZ * port->ldc_timeout));
mod_timer(&port->vio.timer, round_jiffies(jiffies + HZ));
return;
@@ -1211,7 +1216,7 @@ static int __init vdc_init(void)
{
int err;
- sunvdc_wq = alloc_workqueue("sunvdc", 0, 0);
+ sunvdc_wq = alloc_workqueue("sunvdc", WQ_PERCPU, 0);
if (!sunvdc_wq)
return -ENOMEM;
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index 42b4b6828690..416015947ae6 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -608,20 +608,18 @@ static void setup_medium(struct floppy_state *fs)
}
}
-static int floppy_open(struct block_device *bdev, fmode_t mode)
+static int floppy_open(struct gendisk *disk, blk_mode_t mode)
{
- struct floppy_state *fs = bdev->bd_disk->private_data;
+ struct floppy_state *fs = disk->private_data;
struct swim __iomem *base = fs->swd->base;
int err;
- if (fs->ref_count == -1 || (fs->ref_count && mode & FMODE_EXCL))
+ if (fs->ref_count == -1 || (fs->ref_count && mode & BLK_OPEN_EXCL))
return -EBUSY;
-
- if (mode & FMODE_EXCL)
+ if (mode & BLK_OPEN_EXCL)
fs->ref_count = -1;
else
fs->ref_count++;
-
swim_write(base, setup, S_IBM_DRIVE | S_FCLK_DIV2);
udelay(10);
swim_drive(base, fs->location);
@@ -636,13 +634,13 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
set_capacity(fs->disk, fs->total_secs);
- if (mode & FMODE_NDELAY)
+ if (mode & BLK_OPEN_NDELAY)
return 0;
- if (mode & (FMODE_READ|FMODE_WRITE)) {
- if (bdev_check_media_change(bdev) && fs->disk_in)
+ if (mode & (BLK_OPEN_READ | BLK_OPEN_WRITE)) {
+ if (disk_check_media_change(disk) && fs->disk_in)
fs->ejected = 0;
- if ((mode & FMODE_WRITE) && fs->write_protected) {
+ if ((mode & BLK_OPEN_WRITE) && fs->write_protected) {
err = -EROFS;
goto out;
}
@@ -659,18 +657,18 @@ out:
return err;
}
-static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+static int floppy_unlocked_open(struct gendisk *disk, blk_mode_t mode)
{
int ret;
mutex_lock(&swim_mutex);
- ret = floppy_open(bdev, mode);
+ ret = floppy_open(disk, mode);
mutex_unlock(&swim_mutex);
return ret;
}
-static void floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk)
{
struct floppy_state *fs = disk->private_data;
struct swim __iomem *base = fs->swd->base;
@@ -686,7 +684,7 @@ static void floppy_release(struct gendisk *disk, fmode_t mode)
mutex_unlock(&swim_mutex);
}
-static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+static int floppy_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
struct floppy_state *fs = bdev->bd_disk->private_data;
@@ -713,9 +711,9 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
return -ENOTTY;
}
-static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+static int floppy_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- struct floppy_state *fs = bdev->bd_disk->private_data;
+ struct floppy_state *fs = disk->private_data;
struct floppy_struct *g;
int ret;
@@ -789,6 +787,9 @@ static void swim_cleanup_floppy_disk(struct floppy_state *fs)
static int swim_floppy_init(struct swim_priv *swd)
{
+ struct queue_limits lim = {
+ .features = BLK_FEAT_ROTATIONAL,
+ };
int err;
int drive;
struct swim __iomem *base = swd->base;
@@ -817,12 +818,12 @@ static int swim_floppy_init(struct swim_priv *swd)
for (drive = 0; drive < swd->floppy_count; drive++) {
err = blk_mq_alloc_sq_tag_set(&swd->unit[drive].tag_set,
- &swim_mq_ops, 2, BLK_MQ_F_SHOULD_MERGE);
+ &swim_mq_ops, 2, 0);
if (err)
goto exit_put_disks;
swd->unit[drive].disk =
- blk_mq_alloc_disk(&swd->unit[drive].tag_set,
+ blk_mq_alloc_disk(&swd->unit[drive].tag_set, &lim,
&swd->unit[drive]);
if (IS_ERR(swd->unit[drive].disk)) {
blk_mq_free_tag_set(&swd->unit[drive].tag_set);
@@ -918,7 +919,7 @@ out:
return ret;
}
-static int swim_remove(struct platform_device *dev)
+static void swim_remove(struct platform_device *dev)
{
struct swim_priv *swd = platform_get_drvdata(dev);
int drive;
@@ -939,8 +940,6 @@ static int swim_remove(struct platform_device *dev)
release_mem_region(res->start, resource_size(res));
kfree(swd);
-
- return 0;
}
static struct platform_driver swim_driver = {
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index da811a7da03f..01f7aef3fcfb 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -246,10 +246,9 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
int interruptible);
static void release_drive(struct floppy_state *fs);
static int fd_eject(struct floppy_state *fs);
-static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+static int floppy_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param);
-static int floppy_open(struct block_device *bdev, fmode_t mode);
-static void floppy_release(struct gendisk *disk, fmode_t mode);
+static int floppy_open(struct gendisk *disk, blk_mode_t mode);
static unsigned int floppy_check_events(struct gendisk *disk,
unsigned int clearing);
static int floppy_revalidate(struct gendisk *disk);
@@ -363,7 +362,7 @@ static void set_timeout(struct floppy_state *fs, int nticks,
void (*proc)(struct timer_list *t))
{
if (fs->timeout_pending)
- del_timer(&fs->timeout);
+ timer_delete(&fs->timeout);
fs->timeout.expires = jiffies + nticks;
fs->timeout.function = proc;
add_timer(&fs->timeout);
@@ -556,7 +555,7 @@ static void act(struct floppy_state *fs)
static void scan_timeout(struct timer_list *t)
{
- struct floppy_state *fs = from_timer(fs, t, timeout);
+ struct floppy_state *fs = timer_container_of(fs, t, timeout);
struct swim3 __iomem *sw = fs->swim3;
unsigned long flags;
@@ -580,7 +579,7 @@ static void scan_timeout(struct timer_list *t)
static void seek_timeout(struct timer_list *t)
{
- struct floppy_state *fs = from_timer(fs, t, timeout);
+ struct floppy_state *fs = timer_container_of(fs, t, timeout);
struct swim3 __iomem *sw = fs->swim3;
unsigned long flags;
@@ -599,7 +598,7 @@ static void seek_timeout(struct timer_list *t)
static void settle_timeout(struct timer_list *t)
{
- struct floppy_state *fs = from_timer(fs, t, timeout);
+ struct floppy_state *fs = timer_container_of(fs, t, timeout);
struct swim3 __iomem *sw = fs->swim3;
unsigned long flags;
@@ -628,7 +627,7 @@ static void settle_timeout(struct timer_list *t)
static void xfer_timeout(struct timer_list *t)
{
- struct floppy_state *fs = from_timer(fs, t, timeout);
+ struct floppy_state *fs = timer_container_of(fs, t, timeout);
struct swim3 __iomem *sw = fs->swim3;
struct dbdma_regs __iomem *dr = fs->dma;
unsigned long flags;
@@ -678,7 +677,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
out_8(&sw->select, RELAX);
out_8(&sw->intr_enable, 0);
- del_timer(&fs->timeout);
+ timer_delete(&fs->timeout);
fs->timeout_pending = 0;
if (sw->ctrack == 0xff) {
swim3_err("%s", "Seen sector but cyl=ff?\n");
@@ -707,7 +706,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
out_8(&sw->control_bic, DO_SEEK);
out_8(&sw->select, RELAX);
out_8(&sw->intr_enable, 0);
- del_timer(&fs->timeout);
+ timer_delete(&fs->timeout);
fs->timeout_pending = 0;
if (fs->state == seeking)
++fs->retries;
@@ -717,7 +716,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
break;
case settling:
out_8(&sw->intr_enable, 0);
- del_timer(&fs->timeout);
+ timer_delete(&fs->timeout);
fs->timeout_pending = 0;
act(fs);
break;
@@ -727,7 +726,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id)
out_8(&sw->intr_enable, 0);
out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
out_8(&sw->select, RELAX);
- del_timer(&fs->timeout);
+ timer_delete(&fs->timeout);
fs->timeout_pending = 0;
dr = fs->dma;
cp = fs->dma_cmd;
@@ -841,6 +840,7 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
static void release_drive(struct floppy_state *fs)
{
struct request_queue *q = disks[fs->index]->queue;
+ unsigned int memflags;
unsigned long flags;
swim3_dbg("%s", "-> release drive\n");
@@ -849,10 +849,10 @@ static void release_drive(struct floppy_state *fs)
fs->state = idle;
spin_unlock_irqrestore(&swim3_lock, flags);
- blk_mq_freeze_queue(q);
+ memflags = blk_mq_freeze_queue(q);
blk_mq_quiesce_queue(q);
blk_mq_unquiesce_queue(q);
- blk_mq_unfreeze_queue(q);
+ blk_mq_unfreeze_queue(q, memflags);
}
static int fd_eject(struct floppy_state *fs)
@@ -883,7 +883,7 @@ static int fd_eject(struct floppy_state *fs)
static struct floppy_struct floppy_type =
{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */
-static int floppy_locked_ioctl(struct block_device *bdev, fmode_t mode,
+static int floppy_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
struct floppy_state *fs = bdev->bd_disk->private_data;
@@ -911,7 +911,7 @@ static int floppy_locked_ioctl(struct block_device *bdev, fmode_t mode,
return -ENOTTY;
}
-static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+static int floppy_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
int ret;
@@ -923,9 +923,9 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
return ret;
}
-static int floppy_open(struct block_device *bdev, fmode_t mode)
+static int floppy_open(struct gendisk *disk, blk_mode_t mode)
{
- struct floppy_state *fs = bdev->bd_disk->private_data;
+ struct floppy_state *fs = disk->private_data;
struct swim3 __iomem *sw = fs->swim3;
int n, err = 0;
@@ -958,18 +958,18 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
swim3_action(fs, SETMFM);
swim3_select(fs, RELAX);
- } else if (fs->ref_count == -1 || mode & FMODE_EXCL)
+ } else if (fs->ref_count == -1 || mode & BLK_OPEN_EXCL)
return -EBUSY;
- if (err == 0 && (mode & FMODE_NDELAY) == 0
- && (mode & (FMODE_READ|FMODE_WRITE))) {
- if (bdev_check_media_change(bdev))
- floppy_revalidate(bdev->bd_disk);
+ if (err == 0 && !(mode & BLK_OPEN_NDELAY) &&
+ (mode & (BLK_OPEN_READ | BLK_OPEN_WRITE))) {
+ if (disk_check_media_change(disk))
+ floppy_revalidate(disk);
if (fs->ejected)
err = -ENXIO;
}
- if (err == 0 && (mode & FMODE_WRITE)) {
+ if (err == 0 && (mode & BLK_OPEN_WRITE)) {
if (fs->write_prot < 0)
fs->write_prot = swim3_readbit(fs, WRITE_PROT);
if (fs->write_prot)
@@ -985,7 +985,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return err;
}
- if (mode & FMODE_EXCL)
+ if (mode & BLK_OPEN_EXCL)
fs->ref_count = -1;
else
++fs->ref_count;
@@ -993,18 +993,18 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
return 0;
}
-static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+static int floppy_unlocked_open(struct gendisk *disk, blk_mode_t mode)
{
int ret;
mutex_lock(&swim3_mutex);
- ret = floppy_open(bdev, mode);
+ ret = floppy_open(disk, mode);
mutex_unlock(&swim3_mutex);
return ret;
}
-static void floppy_release(struct gendisk *disk, fmode_t mode)
+static void floppy_release(struct gendisk *disk)
{
struct floppy_state *fs = disk->private_data;
struct swim3 __iomem *sw = fs->swim3;
@@ -1190,6 +1190,9 @@ static int swim3_add_device(struct macio_dev *mdev, int index)
static int swim3_attach(struct macio_dev *mdev,
const struct of_device_id *match)
{
+ struct queue_limits lim = {
+ .features = BLK_FEAT_ROTATIONAL,
+ };
struct floppy_state *fs;
struct gendisk *disk;
int rc;
@@ -1206,12 +1209,11 @@ static int swim3_attach(struct macio_dev *mdev,
fs = &floppy_states[floppy_count];
memset(fs, 0, sizeof(*fs));
- rc = blk_mq_alloc_sq_tag_set(&fs->tag_set, &swim3_mq_ops, 2,
- BLK_MQ_F_SHOULD_MERGE);
+ rc = blk_mq_alloc_sq_tag_set(&fs->tag_set, &swim3_mq_ops, 2, 0);
if (rc)
goto out_unregister;
- disk = blk_mq_alloc_disk(&fs->tag_set, fs);
+ disk = blk_mq_alloc_disk(&fs->tag_set, &lim, fs);
if (IS_ERR(disk)) {
rc = PTR_ERR(disk);
goto out_free_tag_set;
@@ -1278,7 +1280,7 @@ static struct macio_driver swim3_driver =
};
-int swim3_init(void)
+static int swim3_init(void)
{
macio_register_driver(&swim3_driver);
return 0;
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 6a4a94b4cdf4..2c715df63f23 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -36,30 +36,76 @@
#include <linux/sched/mm.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
-#include <linux/io_uring.h>
+#include <linux/io_uring/cmd.h>
#include <linux/blk-mq.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <asm/page.h>
#include <linux/task_work.h>
+#include <linux/namei.h>
+#include <linux/kref.h>
#include <uapi/linux/ublk_cmd.h>
#define UBLK_MINORS (1U << MINORBITS)
+#define UBLK_INVALID_BUF_IDX ((u16)-1)
+
+/* private ioctl command mirror */
+#define UBLK_CMD_DEL_DEV_ASYNC _IOC_NR(UBLK_U_CMD_DEL_DEV_ASYNC)
+#define UBLK_CMD_UPDATE_SIZE _IOC_NR(UBLK_U_CMD_UPDATE_SIZE)
+#define UBLK_CMD_QUIESCE_DEV _IOC_NR(UBLK_U_CMD_QUIESCE_DEV)
+
+#define UBLK_IO_REGISTER_IO_BUF _IOC_NR(UBLK_U_IO_REGISTER_IO_BUF)
+#define UBLK_IO_UNREGISTER_IO_BUF _IOC_NR(UBLK_U_IO_UNREGISTER_IO_BUF)
+
/* All UBLK_F_* have to be included into UBLK_F_ALL */
#define UBLK_F_ALL (UBLK_F_SUPPORT_ZERO_COPY \
| UBLK_F_URING_CMD_COMP_IN_TASK \
- | UBLK_F_NEED_GET_DATA)
+ | UBLK_F_NEED_GET_DATA \
+ | UBLK_F_USER_RECOVERY \
+ | UBLK_F_USER_RECOVERY_REISSUE \
+ | UBLK_F_UNPRIVILEGED_DEV \
+ | UBLK_F_CMD_IOCTL_ENCODE \
+ | UBLK_F_USER_COPY \
+ | UBLK_F_ZONED \
+ | UBLK_F_USER_RECOVERY_FAIL_IO \
+ | UBLK_F_UPDATE_SIZE \
+ | UBLK_F_AUTO_BUF_REG \
+ | UBLK_F_QUIESCE \
+ | UBLK_F_PER_IO_DAEMON \
+ | UBLK_F_BUF_REG_OFF_DAEMON)
+
+#define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \
+ | UBLK_F_USER_RECOVERY_REISSUE \
+ | UBLK_F_USER_RECOVERY_FAIL_IO)
/* All UBLK_PARAM_TYPE_* should be included here */
-#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD)
-
-struct ublk_rq_data {
- struct callback_head work;
-};
+#define UBLK_PARAM_TYPE_ALL \
+ (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
+ UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED | \
+ UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
struct ublk_uring_cmd_pdu {
- struct request *req;
+ /*
+ * Store requests in same batch temporarily for queuing them to
+ * daemon context.
+ *
+ * It should have been stored to request payload, but we do want
+ * to avoid extra pre-allocation, and uring_cmd payload is always
+ * free for us
+ */
+ union {
+ struct request *req;
+ struct request *req_list;
+ };
+
+ /*
+ * The following two are valid in this cmd whole lifetime, and
+ * setup in ublk uring_cmd handler
+ */
+ struct ublk_queue *ubq;
+
+ u16 tag;
};
/*
@@ -83,15 +129,6 @@ struct ublk_uring_cmd_pdu {
#define UBLK_IO_FLAG_OWNED_BY_SRV 0x02
/*
- * IO command is aborted, so this flag is set in case of
- * !UBLK_IO_FLAG_ACTIVE.
- *
- * After this flag is observed, any pending or new incoming request
- * associated with this io command will be failed immediately
- */
-#define UBLK_IO_FLAG_ABORTED 0x04
-
-/*
* UBLK_IO_FLAG_NEED_GET_DATA is set because IO command requires
* get data buffer address from ublksrv.
*
@@ -100,39 +137,79 @@ struct ublk_uring_cmd_pdu {
*/
#define UBLK_IO_FLAG_NEED_GET_DATA 0x08
-struct ublk_io {
- /* userspace buffer address from io cmd */
+/*
+ * request buffer is registered automatically, so we have to unregister it
+ * before completing this request.
+ *
+ * io_uring will unregister buffer automatically for us during exiting.
+ */
+#define UBLK_IO_FLAG_AUTO_BUF_REG 0x10
+
+/* atomic RW with ubq->cancel_lock */
+#define UBLK_IO_FLAG_CANCELED 0x80000000
+
+/*
+ * Initialize refcount to a large number to include any registered buffers.
+ * UBLK_IO_COMMIT_AND_FETCH_REQ will release these references minus those for
+ * any buffers registered on the io daemon task.
+ */
+#define UBLK_REFCOUNT_INIT (REFCOUNT_MAX / 2)
+
+union ublk_io_buf {
__u64 addr;
+ struct ublk_auto_buf_reg auto_reg;
+};
+
+struct ublk_io {
+ union ublk_io_buf buf;
unsigned int flags;
int res;
- struct io_uring_cmd *cmd;
-};
+ union {
+ /* valid if UBLK_IO_FLAG_ACTIVE is set */
+ struct io_uring_cmd *cmd;
+ /* valid if UBLK_IO_FLAG_OWNED_BY_SRV is set */
+ struct request *req;
+ };
+
+ struct task_struct *task;
+
+ /*
+ * The number of uses of this I/O by the ublk server
+ * if user copy or zero copy are enabled:
+ * - UBLK_REFCOUNT_INIT from dispatch to the server
+ * until UBLK_IO_COMMIT_AND_FETCH_REQ
+ * - 1 for each inflight ublk_ch_{read,write}_iter() call
+ * - 1 for each io_uring registered buffer not registered on task
+ * The I/O can only be completed once all references are dropped.
+ * User copy and buffer registration operations are only permitted
+ * if the reference count is nonzero.
+ */
+ refcount_t ref;
+ /* Count of buffers registered on task and not yet unregistered */
+ unsigned task_registered_buffers;
+
+ void *buf_ctx_handle;
+} ____cacheline_aligned_in_smp;
struct ublk_queue {
int q_id;
int q_depth;
unsigned long flags;
- struct task_struct *ubq_daemon;
- char *io_cmd_buf;
+ struct ublksrv_io_desc *io_cmd_buf;
- unsigned long io_addr; /* mapped vm address */
- unsigned int max_io_sz;
- bool abort_work_pending;
- unsigned short nr_io_ready; /* how many ios setup */
+ bool force_abort;
+ bool canceling;
+ bool fail_io; /* copy of dev->state == UBLK_S_DEV_FAIL_IO */
+ spinlock_t cancel_lock;
struct ublk_device *dev;
- struct ublk_io ios[0];
+ struct ublk_io ios[] __counted_by(q_depth);
};
-#define UBLK_DAEMON_MONITOR_PERIOD (5 * HZ)
-
struct ublk_device {
struct gendisk *ub_disk;
- char *__queues;
-
- unsigned short queue_size;
struct ublksrv_ctrl_dev_info dev_info;
struct blk_mq_tag_set tag_set;
@@ -142,26 +219,26 @@ struct ublk_device {
#define UB_STATE_OPEN 0
#define UB_STATE_USED 1
+#define UB_STATE_DELETED 2
unsigned long state;
int ub_number;
struct mutex mutex;
- spinlock_t mm_lock;
+ spinlock_t lock;
struct mm_struct *mm;
struct ublk_params params;
struct completion completion;
- unsigned int nr_queues_ready;
- atomic_t nr_aborted_queues;
-
- /*
- * Our ubq->daemon may be killed without any notification, so
- * monitor each queue's daemon periodically
- */
- struct delayed_work monitor_work;
- struct work_struct stop_work;
+ u32 nr_io_ready;
+ bool unprivileged_daemons;
+ struct mutex cancel_mutex;
+ bool canceling;
+ pid_t ublksrv_tgid;
+ struct delayed_work exit_work;
+
+ struct ublk_queue *queues[];
};
/* header of ublk_params */
@@ -170,8 +247,296 @@ struct ublk_params_header {
__u32 types;
};
+static void ublk_io_release(void *priv);
+static void ublk_stop_dev_unlocked(struct ublk_device *ub);
+static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq);
+static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub,
+ u16 q_id, u16 tag, struct ublk_io *io, size_t offset);
+static inline unsigned int ublk_req_build_flags(struct request *req);
+
+static inline struct ublksrv_io_desc *
+ublk_get_iod(const struct ublk_queue *ubq, unsigned tag)
+{
+ return &ubq->io_cmd_buf[tag];
+}
+
+static inline bool ublk_dev_is_zoned(const struct ublk_device *ub)
+{
+ return ub->dev_info.flags & UBLK_F_ZONED;
+}
+
+static inline bool ublk_queue_is_zoned(const struct ublk_queue *ubq)
+{
+ return ubq->flags & UBLK_F_ZONED;
+}
+
+#ifdef CONFIG_BLK_DEV_ZONED
+
+struct ublk_zoned_report_desc {
+ __u64 sector;
+ __u32 operation;
+ __u32 nr_zones;
+};
+
+static DEFINE_XARRAY(ublk_zoned_report_descs);
+
+static int ublk_zoned_insert_report_desc(const struct request *req,
+ struct ublk_zoned_report_desc *desc)
+{
+ return xa_insert(&ublk_zoned_report_descs, (unsigned long)req,
+ desc, GFP_KERNEL);
+}
+
+static struct ublk_zoned_report_desc *ublk_zoned_erase_report_desc(
+ const struct request *req)
+{
+ return xa_erase(&ublk_zoned_report_descs, (unsigned long)req);
+}
+
+static struct ublk_zoned_report_desc *ublk_zoned_get_report_desc(
+ const struct request *req)
+{
+ return xa_load(&ublk_zoned_report_descs, (unsigned long)req);
+}
+
+static int ublk_get_nr_zones(const struct ublk_device *ub)
+{
+ const struct ublk_param_basic *p = &ub->params.basic;
+
+ /* Zone size is a power of 2 */
+ return p->dev_sectors >> ilog2(p->chunk_sectors);
+}
+
+static int ublk_revalidate_disk_zones(struct ublk_device *ub)
+{
+ return blk_revalidate_disk_zones(ub->ub_disk);
+}
+
+static int ublk_dev_param_zoned_validate(const struct ublk_device *ub)
+{
+ const struct ublk_param_zoned *p = &ub->params.zoned;
+ int nr_zones;
+
+ if (!ublk_dev_is_zoned(ub))
+ return -EINVAL;
+
+ if (!p->max_zone_append_sectors)
+ return -EINVAL;
+
+ nr_zones = ublk_get_nr_zones(ub);
+
+ if (p->max_active_zones > nr_zones)
+ return -EINVAL;
+
+ if (p->max_open_zones > nr_zones)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void ublk_dev_param_zoned_apply(struct ublk_device *ub)
+{
+ ub->ub_disk->nr_zones = ublk_get_nr_zones(ub);
+}
+
+/* Based on virtblk_alloc_report_buffer */
+static void *ublk_alloc_report_buffer(struct ublk_device *ublk,
+ unsigned int nr_zones, size_t *buflen)
+{
+ struct request_queue *q = ublk->ub_disk->queue;
+ size_t bufsize;
+ void *buf;
+
+ nr_zones = min_t(unsigned int, nr_zones,
+ ublk->ub_disk->nr_zones);
+
+ bufsize = nr_zones * sizeof(struct blk_zone);
+ bufsize =
+ min_t(size_t, bufsize, queue_max_hw_sectors(q) << SECTOR_SHIFT);
+
+ while (bufsize >= sizeof(struct blk_zone)) {
+ buf = kvmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY);
+ if (buf) {
+ *buflen = bufsize;
+ return buf;
+ }
+ bufsize >>= 1;
+ }
+
+ *buflen = 0;
+ return NULL;
+}
+
+static int ublk_report_zones(struct gendisk *disk, sector_t sector,
+ unsigned int nr_zones, struct blk_report_zones_args *args)
+{
+ struct ublk_device *ub = disk->private_data;
+ unsigned int zone_size_sectors = disk->queue->limits.chunk_sectors;
+ unsigned int first_zone = sector >> ilog2(zone_size_sectors);
+ unsigned int done_zones = 0;
+ unsigned int max_zones_per_request;
+ int ret;
+ struct blk_zone *buffer;
+ size_t buffer_length;
+
+ nr_zones = min_t(unsigned int, ub->ub_disk->nr_zones - first_zone,
+ nr_zones);
+
+ buffer = ublk_alloc_report_buffer(ub, nr_zones, &buffer_length);
+ if (!buffer)
+ return -ENOMEM;
+
+ max_zones_per_request = buffer_length / sizeof(struct blk_zone);
+
+ while (done_zones < nr_zones) {
+ unsigned int remaining_zones = nr_zones - done_zones;
+ unsigned int zones_in_request =
+ min_t(unsigned int, remaining_zones, max_zones_per_request);
+ struct request *req;
+ struct ublk_zoned_report_desc desc;
+ blk_status_t status;
+
+ memset(buffer, 0, buffer_length);
+
+ req = blk_mq_alloc_request(disk->queue, REQ_OP_DRV_IN, 0);
+ if (IS_ERR(req)) {
+ ret = PTR_ERR(req);
+ goto out;
+ }
+
+ desc.operation = UBLK_IO_OP_REPORT_ZONES;
+ desc.sector = sector;
+ desc.nr_zones = zones_in_request;
+ ret = ublk_zoned_insert_report_desc(req, &desc);
+ if (ret)
+ goto free_req;
+
+ ret = blk_rq_map_kern(req, buffer, buffer_length, GFP_KERNEL);
+ if (ret)
+ goto erase_desc;
+
+ status = blk_execute_rq(req, 0);
+ ret = blk_status_to_errno(status);
+erase_desc:
+ ublk_zoned_erase_report_desc(req);
+free_req:
+ blk_mq_free_request(req);
+ if (ret)
+ goto out;
+
+ for (unsigned int i = 0; i < zones_in_request; i++) {
+ struct blk_zone *zone = buffer + i;
+
+ /* A zero length zone means no more zones in this response */
+ if (!zone->len)
+ break;
+
+ ret = disk_report_zone(disk, zone, i, args);
+ if (ret)
+ goto out;
+
+ done_zones++;
+ sector += zone_size_sectors;
+
+ }
+ }
+
+ ret = done_zones;
+
+out:
+ kvfree(buffer);
+ return ret;
+}
+
+static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
+ struct request *req)
+{
+ struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag);
+ struct ublk_io *io = &ubq->ios[req->tag];
+ struct ublk_zoned_report_desc *desc;
+ u32 ublk_op;
+
+ switch (req_op(req)) {
+ case REQ_OP_ZONE_OPEN:
+ ublk_op = UBLK_IO_OP_ZONE_OPEN;
+ break;
+ case REQ_OP_ZONE_CLOSE:
+ ublk_op = UBLK_IO_OP_ZONE_CLOSE;
+ break;
+ case REQ_OP_ZONE_FINISH:
+ ublk_op = UBLK_IO_OP_ZONE_FINISH;
+ break;
+ case REQ_OP_ZONE_RESET:
+ ublk_op = UBLK_IO_OP_ZONE_RESET;
+ break;
+ case REQ_OP_ZONE_APPEND:
+ ublk_op = UBLK_IO_OP_ZONE_APPEND;
+ break;
+ case REQ_OP_ZONE_RESET_ALL:
+ ublk_op = UBLK_IO_OP_ZONE_RESET_ALL;
+ break;
+ case REQ_OP_DRV_IN:
+ desc = ublk_zoned_get_report_desc(req);
+ if (!desc)
+ return BLK_STS_IOERR;
+ ublk_op = desc->operation;
+ switch (ublk_op) {
+ case UBLK_IO_OP_REPORT_ZONES:
+ iod->op_flags = ublk_op | ublk_req_build_flags(req);
+ iod->nr_zones = desc->nr_zones;
+ iod->start_sector = desc->sector;
+ return BLK_STS_OK;
+ default:
+ return BLK_STS_IOERR;
+ }
+ case REQ_OP_DRV_OUT:
+ /* We do not support drv_out */
+ return BLK_STS_NOTSUPP;
+ default:
+ return BLK_STS_IOERR;
+ }
+
+ iod->op_flags = ublk_op | ublk_req_build_flags(req);
+ iod->nr_sectors = blk_rq_sectors(req);
+ iod->start_sector = blk_rq_pos(req);
+ iod->addr = io->buf.addr;
+
+ return BLK_STS_OK;
+}
+
+#else
+
+#define ublk_report_zones (NULL)
+
+static int ublk_dev_param_zoned_validate(const struct ublk_device *ub)
+{
+ return -EOPNOTSUPP;
+}
+
+static void ublk_dev_param_zoned_apply(struct ublk_device *ub)
+{
+}
+
+static int ublk_revalidate_disk_zones(struct ublk_device *ub)
+{
+ return 0;
+}
+
+static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
+ struct request *req)
+{
+ return BLK_STS_NOTSUPP;
+}
+
+#endif
+
+static inline void __ublk_complete_rq(struct request *req, struct ublk_io *io,
+ bool need_map);
+
static dev_t ublk_chr_devt;
-static struct class *ublk_chr_class;
+static const struct class ublk_chr_class = {
+ .name = "ublk-char",
+};
static DEFINE_IDR(ublk_index_idr);
static DEFINE_SPINLOCK(ublk_idr_lock);
@@ -179,28 +544,40 @@ static wait_queue_head_t ublk_idr_wq; /* wait until one idr is freed */
static DEFINE_MUTEX(ublk_ctl_mutex);
+
+#define UBLK_MAX_UBLKS UBLK_MINORS
+
+/*
+ * Max unprivileged ublk devices allowed to add
+ *
+ * It can be extended to one per-user limit in future or even controlled
+ * by cgroup.
+ */
+static unsigned int unprivileged_ublks_max = 64;
+static unsigned int unprivileged_ublks_added; /* protected by ublk_ctl_mutex */
+
static struct miscdevice ublk_misc;
-static void ublk_dev_param_basic_apply(struct ublk_device *ub)
+static inline unsigned ublk_pos_to_hwq(loff_t pos)
{
- struct request_queue *q = ub->ub_disk->queue;
- const struct ublk_param_basic *p = &ub->params.basic;
+ return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_QID_OFF) &
+ UBLK_QID_BITS_MASK;
+}
- blk_queue_logical_block_size(q, 1 << p->logical_bs_shift);
- blk_queue_physical_block_size(q, 1 << p->physical_bs_shift);
- blk_queue_io_min(q, 1 << p->io_min_shift);
- blk_queue_io_opt(q, 1 << p->io_opt_shift);
+static inline unsigned ublk_pos_to_buf_off(loff_t pos)
+{
+ return (pos - UBLKSRV_IO_BUF_OFFSET) & UBLK_IO_BUF_BITS_MASK;
+}
- blk_queue_write_cache(q, p->attrs & UBLK_ATTR_VOLATILE_CACHE,
- p->attrs & UBLK_ATTR_FUA);
- if (p->attrs & UBLK_ATTR_ROTATIONAL)
- blk_queue_flag_clear(QUEUE_FLAG_NONROT, q);
- else
- blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
+static inline unsigned ublk_pos_to_tag(loff_t pos)
+{
+ return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
+ UBLK_TAG_BITS_MASK;
+}
- blk_queue_max_hw_sectors(q, p->max_sectors);
- blk_queue_chunk_sectors(q, p->chunk_sectors);
- blk_queue_virt_boundary(q, p->virt_boundary_mask);
+static void ublk_dev_param_basic_apply(struct ublk_device *ub)
+{
+ const struct ublk_param_basic *p = &ub->params.basic;
if (p->attrs & UBLK_ATTR_READ_ONLY)
set_disk_ro(ub->ub_disk, true);
@@ -208,26 +585,13 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
set_capacity(ub->ub_disk, p->dev_sectors);
}
-static void ublk_dev_param_discard_apply(struct ublk_device *ub)
-{
- struct request_queue *q = ub->ub_disk->queue;
- const struct ublk_param_discard *p = &ub->params.discard;
-
- q->limits.discard_alignment = p->discard_alignment;
- q->limits.discard_granularity = p->discard_granularity;
- blk_queue_max_discard_sectors(q, p->max_discard_sectors);
- blk_queue_max_write_zeroes_sectors(q,
- p->max_write_zeroes_sectors);
- blk_queue_max_discard_segments(q, p->max_discard_segments);
-}
-
static int ublk_validate_params(const struct ublk_device *ub)
{
/* basic param is the only one which must be set */
if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
const struct ublk_param_basic *p = &ub->params.basic;
- if (p->logical_bs_shift > PAGE_SHIFT)
+ if (p->logical_bs_shift > PAGE_SHIFT || p->logical_bs_shift < 9)
return -EINVAL;
if (p->logical_bs_shift > p->physical_bs_shift)
@@ -235,6 +599,9 @@ static int ublk_validate_params(const struct ublk_device *ub)
if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9))
return -EINVAL;
+
+ if (ublk_dev_is_zoned(ub) && !p->chunk_sectors)
+ return -EINVAL;
} else
return -EINVAL;
@@ -249,45 +616,164 @@ static int ublk_validate_params(const struct ublk_device *ub)
return -EINVAL;
}
+ /* dev_t is read-only */
+ if (ub->params.types & UBLK_PARAM_TYPE_DEVT)
+ return -EINVAL;
+
+ if (ub->params.types & UBLK_PARAM_TYPE_ZONED)
+ return ublk_dev_param_zoned_validate(ub);
+ else if (ublk_dev_is_zoned(ub))
+ return -EINVAL;
+
+ if (ub->params.types & UBLK_PARAM_TYPE_DMA_ALIGN) {
+ const struct ublk_param_dma_align *p = &ub->params.dma;
+
+ if (p->alignment >= PAGE_SIZE)
+ return -EINVAL;
+
+ if (!is_power_of_2(p->alignment + 1))
+ return -EINVAL;
+ }
+
+ if (ub->params.types & UBLK_PARAM_TYPE_SEGMENT) {
+ const struct ublk_param_segment *p = &ub->params.seg;
+
+ if (!is_power_of_2(p->seg_boundary_mask + 1))
+ return -EINVAL;
+
+ if (p->seg_boundary_mask + 1 < UBLK_MIN_SEGMENT_SIZE)
+ return -EINVAL;
+ if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
+ return -EINVAL;
+ }
+
return 0;
}
-static int ublk_apply_params(struct ublk_device *ub)
+static void ublk_apply_params(struct ublk_device *ub)
{
- if (!(ub->params.types & UBLK_PARAM_TYPE_BASIC))
- return -EINVAL;
-
ublk_dev_param_basic_apply(ub);
- if (ub->params.types & UBLK_PARAM_TYPE_DISCARD)
- ublk_dev_param_discard_apply(ub);
+ if (ub->params.types & UBLK_PARAM_TYPE_ZONED)
+ ublk_dev_param_zoned_apply(ub);
+}
- return 0;
+static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq)
+{
+ return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY;
}
-static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq)
+static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub)
{
- if (IS_BUILTIN(CONFIG_BLK_DEV_UBLK) &&
- !(ubq->flags & UBLK_F_URING_CMD_COMP_IN_TASK))
- return true;
- return false;
+ return ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY;
+}
+
+static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq)
+{
+ return ubq->flags & UBLK_F_AUTO_BUF_REG;
+}
+
+static inline bool ublk_dev_support_auto_buf_reg(const struct ublk_device *ub)
+{
+ return ub->dev_info.flags & UBLK_F_AUTO_BUF_REG;
+}
+
+static inline bool ublk_support_user_copy(const struct ublk_queue *ubq)
+{
+ return ubq->flags & UBLK_F_USER_COPY;
+}
+
+static inline bool ublk_dev_support_user_copy(const struct ublk_device *ub)
+{
+ return ub->dev_info.flags & UBLK_F_USER_COPY;
+}
+
+static inline bool ublk_need_map_io(const struct ublk_queue *ubq)
+{
+ return !ublk_support_user_copy(ubq) && !ublk_support_zero_copy(ubq) &&
+ !ublk_support_auto_buf_reg(ubq);
+}
+
+static inline bool ublk_dev_need_map_io(const struct ublk_device *ub)
+{
+ return !ublk_dev_support_user_copy(ub) &&
+ !ublk_dev_support_zero_copy(ub) &&
+ !ublk_dev_support_auto_buf_reg(ub);
+}
+
+static inline bool ublk_need_req_ref(const struct ublk_queue *ubq)
+{
+ /*
+ * read()/write() is involved in user copy, so request reference
+ * has to be grabbed
+ *
+ * for zero copy, request buffer need to be registered to io_uring
+ * buffer table, so reference is needed
+ *
+ * For auto buffer register, ublk server still may issue
+ * UBLK_IO_COMMIT_AND_FETCH_REQ before one registered buffer is used up,
+ * so reference is required too.
+ */
+ return ublk_support_user_copy(ubq) || ublk_support_zero_copy(ubq) ||
+ ublk_support_auto_buf_reg(ubq);
+}
+
+static inline bool ublk_dev_need_req_ref(const struct ublk_device *ub)
+{
+ return ublk_dev_support_user_copy(ub) ||
+ ublk_dev_support_zero_copy(ub) ||
+ ublk_dev_support_auto_buf_reg(ub);
+}
+
+static inline void ublk_init_req_ref(const struct ublk_queue *ubq,
+ struct ublk_io *io)
+{
+ if (ublk_need_req_ref(ubq))
+ refcount_set(&io->ref, UBLK_REFCOUNT_INIT);
+}
+
+static inline bool ublk_get_req_ref(struct ublk_io *io)
+{
+ return refcount_inc_not_zero(&io->ref);
+}
+
+static inline void ublk_put_req_ref(struct ublk_io *io, struct request *req)
+{
+ if (!refcount_dec_and_test(&io->ref))
+ return;
+
+ /* ublk_need_map_io() and ublk_need_req_ref() are mutually exclusive */
+ __ublk_complete_rq(req, io, false);
+}
+
+static inline bool ublk_sub_req_ref(struct ublk_io *io)
+{
+ unsigned sub_refs = UBLK_REFCOUNT_INIT - io->task_registered_buffers;
+
+ io->task_registered_buffers = 0;
+ return refcount_sub_and_test(sub_refs, &io->ref);
}
static inline bool ublk_need_get_data(const struct ublk_queue *ubq)
{
- if (ubq->flags & UBLK_F_NEED_GET_DATA)
- return true;
- return false;
+ return ubq->flags & UBLK_F_NEED_GET_DATA;
+}
+
+static inline bool ublk_dev_need_get_data(const struct ublk_device *ub)
+{
+ return ub->dev_info.flags & UBLK_F_NEED_GET_DATA;
}
-static struct ublk_device *ublk_get_device(struct ublk_device *ub)
+/* Called in slow path only, keep it noinline for trace purpose */
+static noinline struct ublk_device *ublk_get_device(struct ublk_device *ub)
{
if (kobject_get_unless_zero(&ub->cdev_dev.kobj))
return ub;
return NULL;
}
-static void ublk_put_device(struct ublk_device *ub)
+/* Called in slow path only, keep it noinline for trace purpose */
+static noinline void ublk_put_device(struct ublk_device *ub)
{
put_device(&ub->cdev_dev);
}
@@ -295,32 +781,81 @@ static void ublk_put_device(struct ublk_device *ub)
static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev,
int qid)
{
- return (struct ublk_queue *)&(dev->__queues[qid * dev->queue_size]);
+ return dev->queues[qid];
}
static inline bool ublk_rq_has_data(const struct request *rq)
{
- return rq->bio && bio_has_data(rq->bio);
+ return bio_has_data(rq->bio);
}
-static inline struct ublksrv_io_desc *ublk_get_iod(struct ublk_queue *ubq,
- int tag)
+static inline struct ublksrv_io_desc *
+ublk_queue_cmd_buf(struct ublk_device *ub, int q_id)
{
- return (struct ublksrv_io_desc *)
- &(ubq->io_cmd_buf[tag * sizeof(struct ublksrv_io_desc)]);
+ return ublk_get_queue(ub, q_id)->io_cmd_buf;
}
-static inline char *ublk_queue_cmd_buf(struct ublk_device *ub, int q_id)
+static inline int __ublk_queue_cmd_buf_size(int depth)
{
- return ublk_get_queue(ub, q_id)->io_cmd_buf;
+ return round_up(depth * sizeof(struct ublksrv_io_desc), PAGE_SIZE);
+}
+
+static inline int ublk_queue_cmd_buf_size(struct ublk_device *ub)
+{
+ return __ublk_queue_cmd_buf_size(ub->dev_info.queue_depth);
}
-static inline int ublk_queue_cmd_buf_size(struct ublk_device *ub, int q_id)
+static int ublk_max_cmd_buf_size(void)
{
- struct ublk_queue *ubq = ublk_get_queue(ub, q_id);
+ return __ublk_queue_cmd_buf_size(UBLK_MAX_QUEUE_DEPTH);
+}
- return round_up(ubq->q_depth * sizeof(struct ublksrv_io_desc),
- PAGE_SIZE);
+/*
+ * Should I/O outstanding to the ublk server when it exits be reissued?
+ * If not, outstanding I/O will get errors.
+ */
+static inline bool ublk_nosrv_should_reissue_outstanding(struct ublk_device *ub)
+{
+ return (ub->dev_info.flags & UBLK_F_USER_RECOVERY) &&
+ (ub->dev_info.flags & UBLK_F_USER_RECOVERY_REISSUE);
+}
+
+/*
+ * Should I/O issued while there is no ublk server queue? If not, I/O
+ * issued while there is no ublk server will get errors.
+ */
+static inline bool ublk_nosrv_dev_should_queue_io(struct ublk_device *ub)
+{
+ return (ub->dev_info.flags & UBLK_F_USER_RECOVERY) &&
+ !(ub->dev_info.flags & UBLK_F_USER_RECOVERY_FAIL_IO);
+}
+
+/*
+ * Same as ublk_nosrv_dev_should_queue_io, but uses a queue-local copy
+ * of the device flags for smaller cache footprint - better for fast
+ * paths.
+ */
+static inline bool ublk_nosrv_should_queue_io(struct ublk_queue *ubq)
+{
+ return (ubq->flags & UBLK_F_USER_RECOVERY) &&
+ !(ubq->flags & UBLK_F_USER_RECOVERY_FAIL_IO);
+}
+
+/*
+ * Should ublk devices be stopped (i.e. no recovery possible) when the
+ * ublk server exits? If not, devices can be used again by a future
+ * incarnation of a ublk server via the start_recovery/end_recovery
+ * commands.
+ */
+static inline bool ublk_nosrv_should_stop_dev(struct ublk_device *ub)
+{
+ return !(ub->dev_info.flags & UBLK_F_USER_RECOVERY);
+}
+
+static inline bool ublk_dev_in_recoverable_state(struct ublk_device *ub)
+{
+ return ub->dev_info.state == UBLK_S_DEV_QUIESCED ||
+ ub->dev_info.state == UBLK_S_DEV_FAIL_IO;
}
static void ublk_free_disk(struct gendisk *disk)
@@ -328,160 +863,148 @@ static void ublk_free_disk(struct gendisk *disk)
struct ublk_device *ub = disk->private_data;
clear_bit(UB_STATE_USED, &ub->state);
- put_device(&ub->cdev_dev);
+ ublk_put_device(ub);
}
-static const struct block_device_operations ub_fops = {
- .owner = THIS_MODULE,
- .free_disk = ublk_free_disk,
-};
+static void ublk_store_owner_uid_gid(unsigned int *owner_uid,
+ unsigned int *owner_gid)
+{
+ kuid_t uid;
+ kgid_t gid;
-#define UBLK_MAX_PIN_PAGES 32
+ current_uid_gid(&uid, &gid);
-struct ublk_map_data {
- const struct ublk_queue *ubq;
- const struct request *rq;
- const struct ublk_io *io;
- unsigned max_bytes;
-};
+ *owner_uid = from_kuid(&init_user_ns, uid);
+ *owner_gid = from_kgid(&init_user_ns, gid);
+}
+
+static int ublk_open(struct gendisk *disk, blk_mode_t mode)
+{
+ struct ublk_device *ub = disk->private_data;
+
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+
+ /*
+ * If it is one unprivileged device, only owner can open
+ * the disk. Otherwise it could be one trap made by one
+ * evil user who grants this disk's privileges to other
+ * users deliberately.
+ *
+ * This way is reasonable too given anyone can create
+ * unprivileged device, and no need other's grant.
+ */
+ if (ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV) {
+ unsigned int curr_uid, curr_gid;
+
+ ublk_store_owner_uid_gid(&curr_uid, &curr_gid);
-struct ublk_io_iter {
- struct page *pages[UBLK_MAX_PIN_PAGES];
- unsigned pg_off; /* offset in the 1st page in pages */
- int nr_pages; /* how many page pointers in pages */
- struct bio *bio;
- struct bvec_iter iter;
+ if (curr_uid != ub->dev_info.owner_uid || curr_gid !=
+ ub->dev_info.owner_gid)
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static const struct block_device_operations ub_fops = {
+ .owner = THIS_MODULE,
+ .open = ublk_open,
+ .free_disk = ublk_free_disk,
+ .report_zones = ublk_report_zones,
};
-static inline unsigned ublk_copy_io_pages(struct ublk_io_iter *data,
- unsigned max_bytes, bool to_vm)
+/*
+ * Copy data between request pages and io_iter, and 'offset'
+ * is the start point of linear offset of request.
+ */
+static size_t ublk_copy_user_pages(const struct request *req,
+ unsigned offset, struct iov_iter *uiter, int dir)
{
- const unsigned total = min_t(unsigned, max_bytes,
- PAGE_SIZE - data->pg_off +
- ((data->nr_pages - 1) << PAGE_SHIFT));
- unsigned done = 0;
- unsigned pg_idx = 0;
+ struct req_iterator iter;
+ struct bio_vec bv;
+ size_t done = 0;
- while (done < total) {
- struct bio_vec bv = bio_iter_iovec(data->bio, data->iter);
- const unsigned int bytes = min3(bv.bv_len, total - done,
- (unsigned)(PAGE_SIZE - data->pg_off));
- void *bv_buf = bvec_kmap_local(&bv);
- void *pg_buf = kmap_local_page(data->pages[pg_idx]);
+ rq_for_each_segment(bv, req, iter) {
+ void *bv_buf;
+ size_t copied;
- if (to_vm)
- memcpy(pg_buf + data->pg_off, bv_buf, bytes);
+ if (offset >= bv.bv_len) {
+ offset -= bv.bv_len;
+ continue;
+ }
+
+ bv.bv_offset += offset;
+ bv.bv_len -= offset;
+ bv_buf = bvec_kmap_local(&bv);
+ if (dir == ITER_DEST)
+ copied = copy_to_iter(bv_buf, bv.bv_len, uiter);
else
- memcpy(bv_buf, pg_buf + data->pg_off, bytes);
+ copied = copy_from_iter(bv_buf, bv.bv_len, uiter);
- kunmap_local(pg_buf);
kunmap_local(bv_buf);
- /* advance page array */
- data->pg_off += bytes;
- if (data->pg_off == PAGE_SIZE) {
- pg_idx += 1;
- data->pg_off = 0;
- }
+ done += copied;
+ if (copied < bv.bv_len)
+ break;
- done += bytes;
-
- /* advance bio */
- bio_advance_iter_single(data->bio, &data->iter, bytes);
- if (!data->iter.bi_size) {
- data->bio = data->bio->bi_next;
- if (data->bio == NULL)
- break;
- data->iter = data->bio->bi_iter;
- }
+ offset = 0;
}
-
return done;
}
-static inline int ublk_copy_user_pages(struct ublk_map_data *data,
- bool to_vm)
+static inline bool ublk_need_map_req(const struct request *req)
{
- const unsigned int gup_flags = to_vm ? FOLL_WRITE : 0;
- const unsigned long start_vm = data->io->addr;
- unsigned int done = 0;
- struct ublk_io_iter iter = {
- .pg_off = start_vm & (PAGE_SIZE - 1),
- .bio = data->rq->bio,
- .iter = data->rq->bio->bi_iter,
- };
- const unsigned int nr_pages = round_up(data->max_bytes +
- (start_vm & (PAGE_SIZE - 1)), PAGE_SIZE) >> PAGE_SHIFT;
-
- while (done < nr_pages) {
- const unsigned to_pin = min_t(unsigned, UBLK_MAX_PIN_PAGES,
- nr_pages - done);
- unsigned i, len;
-
- iter.nr_pages = get_user_pages_fast(start_vm +
- (done << PAGE_SHIFT), to_pin, gup_flags,
- iter.pages);
- if (iter.nr_pages <= 0)
- return done == 0 ? iter.nr_pages : done;
- len = ublk_copy_io_pages(&iter, data->max_bytes, to_vm);
- for (i = 0; i < iter.nr_pages; i++) {
- if (to_vm)
- set_page_dirty(iter.pages[i]);
- put_page(iter.pages[i]);
- }
- data->max_bytes -= len;
- done += iter.nr_pages;
- }
+ return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
+}
- return done;
+static inline bool ublk_need_unmap_req(const struct request *req)
+{
+ return ublk_rq_has_data(req) &&
+ (req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN);
}
-static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req,
- struct ublk_io *io)
+static unsigned int ublk_map_io(const struct ublk_queue *ubq,
+ const struct request *req,
+ const struct ublk_io *io)
{
const unsigned int rq_bytes = blk_rq_bytes(req);
+
+ if (!ublk_need_map_io(ubq))
+ return rq_bytes;
+
/*
* no zero copy, we delay copy WRITE request data into ublksrv
* context and the big benefit is that pinning pages in current
* context is pretty fast, see ublk_pin_user_pages
*/
- if (req_op(req) != REQ_OP_WRITE && req_op(req) != REQ_OP_FLUSH)
- return rq_bytes;
-
- if (ublk_rq_has_data(req)) {
- struct ublk_map_data data = {
- .ubq = ubq,
- .rq = req,
- .io = io,
- .max_bytes = rq_bytes,
- };
+ if (ublk_need_map_req(req)) {
+ struct iov_iter iter;
+ const int dir = ITER_DEST;
- ublk_copy_user_pages(&data, true);
-
- return rq_bytes - data.max_bytes;
+ import_ubuf(dir, u64_to_user_ptr(io->buf.addr), rq_bytes, &iter);
+ return ublk_copy_user_pages(req, 0, &iter, dir);
}
return rq_bytes;
}
-static int ublk_unmap_io(const struct ublk_queue *ubq,
+static unsigned int ublk_unmap_io(bool need_map,
const struct request *req,
- struct ublk_io *io)
+ const struct ublk_io *io)
{
const unsigned int rq_bytes = blk_rq_bytes(req);
- if (req_op(req) == REQ_OP_READ && ublk_rq_has_data(req)) {
- struct ublk_map_data data = {
- .ubq = ubq,
- .rq = req,
- .io = io,
- .max_bytes = io->res,
- };
+ if (!need_map)
+ return rq_bytes;
- WARN_ON_ONCE(io->res > rq_bytes);
+ if (ublk_need_unmap_req(req)) {
+ struct iov_iter iter;
+ const int dir = ITER_SOURCE;
- ublk_copy_user_pages(&data, false);
+ WARN_ON_ONCE(io->res > rq_bytes);
- return io->res - data.max_bytes;
+ import_ubuf(dir, u64_to_user_ptr(io->buf.addr), io->res, &iter);
+ return ublk_copy_user_pages(req, 0, &iter, dir);
}
return rq_bytes;
}
@@ -537,6 +1060,8 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
ublk_op = UBLK_IO_OP_WRITE_ZEROES;
break;
default:
+ if (ublk_queue_is_zoned(ubq))
+ return ublk_setup_iod_zoned(ubq, req);
return BLK_STS_IOERR;
}
@@ -544,7 +1069,7 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
iod->op_flags = ublk_op | ublk_req_build_flags(req);
iod->nr_sectors = blk_rq_sectors(req);
iod->start_sector = blk_rq_pos(req);
- iod->addr = io->addr;
+ iod->addr = io->buf.addr;
return BLK_STS_OK;
}
@@ -552,43 +1077,37 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
static inline struct ublk_uring_cmd_pdu *ublk_get_uring_cmd_pdu(
struct io_uring_cmd *ioucmd)
{
- return (struct ublk_uring_cmd_pdu *)&ioucmd->pdu;
-}
-
-static inline bool ubq_daemon_is_dying(struct ublk_queue *ubq)
-{
- return ubq->ubq_daemon->flags & PF_EXITING;
+ return io_uring_cmd_to_pdu(ioucmd, struct ublk_uring_cmd_pdu);
}
/* todo: handle partial completion */
-static void ublk_complete_rq(struct request *req)
+static inline void __ublk_complete_rq(struct request *req, struct ublk_io *io,
+ bool need_map)
{
- struct ublk_queue *ubq = req->mq_hctx->driver_data;
- struct ublk_io *io = &ubq->ios[req->tag];
unsigned int unmapped_bytes;
+ blk_status_t res = BLK_STS_OK;
/* failed read IO if nothing is read */
if (!io->res && req_op(req) == REQ_OP_READ)
io->res = -EIO;
if (io->res < 0) {
- blk_mq_end_request(req, errno_to_blk_status(io->res));
- return;
+ res = errno_to_blk_status(io->res);
+ goto exit;
}
/*
- * FLUSH or DISCARD usually won't return bytes returned, so end them
+ * FLUSH, DISCARD or WRITE_ZEROES usually won't return bytes returned, so end them
* directly.
*
* Both the two needn't unmap.
*/
- if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE) {
- blk_mq_end_request(req, BLK_STS_OK);
- return;
- }
+ if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE &&
+ req_op(req) != REQ_OP_DRV_IN)
+ goto exit;
/* for READ request, writing data in iod->addr to rq buffers */
- unmapped_bytes = ublk_unmap_io(ubq, req, io);
+ unmapped_bytes = ublk_unmap_io(need_map, req, io);
/*
* Extremely impossible since we got data filled in just before
@@ -600,30 +1119,20 @@ static void ublk_complete_rq(struct request *req)
if (blk_update_request(req, BLK_STS_OK, io->res))
blk_mq_requeue_request(req, true);
- else
+ else if (likely(!blk_should_fake_timeout(req->q)))
__blk_mq_end_request(req, BLK_STS_OK);
-}
-/*
- * Since __ublk_rq_task_work always fails requests immediately during
- * exiting, __ublk_fail_req() is only called from abort context during
- * exiting. So lock is unnecessary.
- *
- * Also aborting may not be started yet, keep in mind that one failed
- * request may be issued by block layer again.
- */
-static void __ublk_fail_req(struct ublk_io *io, struct request *req)
-{
- WARN_ON_ONCE(io->flags & UBLK_IO_FLAG_ACTIVE);
-
- if (!(io->flags & UBLK_IO_FLAG_ABORTED)) {
- io->flags |= UBLK_IO_FLAG_ABORTED;
- blk_mq_end_request(req, BLK_STS_IOERR);
- }
+ return;
+exit:
+ blk_mq_end_request(req, res);
}
-static void ubq_complete_io_cmd(struct ublk_io *io, int res)
+static struct io_uring_cmd *__ublk_prep_compl_io_cmd(struct ublk_io *io,
+ struct request *req)
{
+ /* read cmd first because req will overwrite it */
+ struct io_uring_cmd *cmd = io->cmd;
+
/* mark this cmd owned by ublksrv */
io->flags |= UBLK_IO_FLAG_OWNED_BY_SRV;
@@ -633,61 +1142,97 @@ static void ubq_complete_io_cmd(struct ublk_io *io, int res)
*/
io->flags &= ~UBLK_IO_FLAG_ACTIVE;
+ io->req = req;
+ return cmd;
+}
+
+static void ublk_complete_io_cmd(struct ublk_io *io, struct request *req,
+ int res, unsigned issue_flags)
+{
+ struct io_uring_cmd *cmd = __ublk_prep_compl_io_cmd(io, req);
+
/* tell ublksrv one io request is coming */
- io_uring_cmd_done(io->cmd, res, 0);
+ io_uring_cmd_done(cmd, res, issue_flags);
}
#define UBLK_REQUEUE_DELAY_MS 3
-static inline void __ublk_rq_task_work(struct request *req)
+static inline void __ublk_abort_rq(struct ublk_queue *ubq,
+ struct request *rq)
{
- struct ublk_queue *ubq = req->mq_hctx->driver_data;
- struct ublk_device *ub = ubq->dev;
- int tag = req->tag;
- struct ublk_io *io = &ubq->ios[tag];
- bool task_exiting = current != ubq->ubq_daemon || ubq_daemon_is_dying(ubq);
- unsigned int mapped_bytes;
+ /* We cannot process this rq so just requeue it. */
+ if (ublk_nosrv_dev_should_queue_io(ubq->dev))
+ blk_mq_requeue_request(rq, false);
+ else
+ blk_mq_end_request(rq, BLK_STS_IOERR);
+}
- pr_devel("%s: complete: op %d, qid %d tag %d io_flags %x addr %llx\n",
- __func__, io->cmd->cmd_op, ubq->q_id, req->tag, io->flags,
- ublk_get_iod(ubq, req->tag)->addr);
+static void
+ublk_auto_buf_reg_fallback(const struct ublk_queue *ubq, unsigned tag)
+{
+ struct ublksrv_io_desc *iod = ublk_get_iod(ubq, tag);
- if (unlikely(task_exiting)) {
- blk_mq_end_request(req, BLK_STS_IOERR);
- mod_delayed_work(system_wq, &ub->monitor_work, 0);
- return;
+ iod->op_flags |= UBLK_IO_F_NEED_REG_BUF;
+}
+
+enum auto_buf_reg_res {
+ AUTO_BUF_REG_FAIL,
+ AUTO_BUF_REG_FALLBACK,
+ AUTO_BUF_REG_OK,
+};
+
+static void ublk_prep_auto_buf_reg_io(const struct ublk_queue *ubq,
+ struct request *req, struct ublk_io *io,
+ struct io_uring_cmd *cmd,
+ enum auto_buf_reg_res res)
+{
+ if (res == AUTO_BUF_REG_OK) {
+ io->task_registered_buffers = 1;
+ io->buf_ctx_handle = io_uring_cmd_ctx_handle(cmd);
+ io->flags |= UBLK_IO_FLAG_AUTO_BUF_REG;
}
+ ublk_init_req_ref(ubq, io);
+ __ublk_prep_compl_io_cmd(io, req);
+}
- if (ublk_need_get_data(ubq) &&
- (req_op(req) == REQ_OP_WRITE ||
- req_op(req) == REQ_OP_FLUSH)) {
- /*
- * We have not handled UBLK_IO_NEED_GET_DATA command yet,
- * so immepdately pass UBLK_IO_RES_NEED_GET_DATA to ublksrv
- * and notify it.
- */
- if (!(io->flags & UBLK_IO_FLAG_NEED_GET_DATA)) {
- io->flags |= UBLK_IO_FLAG_NEED_GET_DATA;
- pr_devel("%s: need get data. op %d, qid %d tag %d io_flags %x\n",
- __func__, io->cmd->cmd_op, ubq->q_id,
- req->tag, io->flags);
- ubq_complete_io_cmd(io, UBLK_IO_RES_NEED_GET_DATA);
- return;
+static enum auto_buf_reg_res
+__ublk_do_auto_buf_reg(const struct ublk_queue *ubq, struct request *req,
+ struct ublk_io *io, struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
+{
+ int ret;
+
+ ret = io_buffer_register_bvec(cmd, req, ublk_io_release,
+ io->buf.auto_reg.index, issue_flags);
+ if (ret) {
+ if (io->buf.auto_reg.flags & UBLK_AUTO_BUF_REG_FALLBACK) {
+ ublk_auto_buf_reg_fallback(ubq, req->tag);
+ return AUTO_BUF_REG_FALLBACK;
}
- /*
- * We have handled UBLK_IO_NEED_GET_DATA command,
- * so clear UBLK_IO_FLAG_NEED_GET_DATA now and just
- * do the copy work.
- */
- io->flags &= ~UBLK_IO_FLAG_NEED_GET_DATA;
- /* update iod->addr because ublksrv may have passed a new io buffer */
- ublk_get_iod(ubq, req->tag)->addr = io->addr;
- pr_devel("%s: update iod->addr: op %d, qid %d tag %d io_flags %x addr %llx\n",
- __func__, io->cmd->cmd_op, ubq->q_id, req->tag, io->flags,
- ublk_get_iod(ubq, req->tag)->addr);
+ blk_mq_end_request(req, BLK_STS_IOERR);
+ return AUTO_BUF_REG_FAIL;
+ }
+
+ return AUTO_BUF_REG_OK;
+}
+
+static void ublk_do_auto_buf_reg(const struct ublk_queue *ubq, struct request *req,
+ struct ublk_io *io, struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
+{
+ enum auto_buf_reg_res res = __ublk_do_auto_buf_reg(ubq, req, io, cmd,
+ issue_flags);
+
+ if (res != AUTO_BUF_REG_FAIL) {
+ ublk_prep_auto_buf_reg_io(ubq, req, io, cmd, res);
+ io_uring_cmd_done(cmd, UBLK_IO_RES_OK, issue_flags);
}
+}
- mapped_bytes = ublk_map_io(ubq, req, io);
+static bool ublk_start_io(const struct ublk_queue *ubq, struct request *req,
+ struct ublk_io *io)
+{
+ unsigned mapped_bytes = ublk_map_io(ubq, req, io);
/* partially mapped, update io descriptor */
if (unlikely(mapped_bytes != blk_rq_bytes(req))) {
@@ -702,92 +1247,222 @@ static inline void __ublk_rq_task_work(struct request *req)
blk_mq_requeue_request(req, false);
blk_mq_delay_kick_requeue_list(req->q,
UBLK_REQUEUE_DELAY_MS);
- return;
+ return false;
}
ublk_get_iod(ubq, req->tag)->nr_sectors =
mapped_bytes >> 9;
}
- ubq_complete_io_cmd(io, UBLK_IO_RES_OK);
+ return true;
}
-static void ublk_rq_task_work_cb(struct io_uring_cmd *cmd)
+static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req)
{
+ unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
+ int tag = req->tag;
+ struct ublk_io *io = &ubq->ios[tag];
+
+ pr_devel("%s: complete: qid %d tag %d io_flags %x addr %llx\n",
+ __func__, ubq->q_id, req->tag, io->flags,
+ ublk_get_iod(ubq, req->tag)->addr);
+
+ /*
+ * Task is exiting if either:
+ *
+ * (1) current != io->task.
+ * io_uring_cmd_complete_in_task() tries to run task_work
+ * in a workqueue if cmd's task is PF_EXITING.
+ *
+ * (2) current->flags & PF_EXITING.
+ */
+ if (unlikely(current != io->task || current->flags & PF_EXITING)) {
+ __ublk_abort_rq(ubq, req);
+ return;
+ }
+
+ if (ublk_need_get_data(ubq) && ublk_need_map_req(req)) {
+ /*
+ * We have not handled UBLK_IO_NEED_GET_DATA command yet,
+ * so immediately pass UBLK_IO_RES_NEED_GET_DATA to ublksrv
+ * and notify it.
+ */
+ io->flags |= UBLK_IO_FLAG_NEED_GET_DATA;
+ pr_devel("%s: need get data. qid %d tag %d io_flags %x\n",
+ __func__, ubq->q_id, req->tag, io->flags);
+ ublk_complete_io_cmd(io, req, UBLK_IO_RES_NEED_GET_DATA,
+ issue_flags);
+ return;
+ }
+
+ if (!ublk_start_io(ubq, req, io))
+ return;
+
+ if (ublk_support_auto_buf_reg(ubq) && ublk_rq_has_data(req)) {
+ ublk_do_auto_buf_reg(ubq, req, io, io->cmd, issue_flags);
+ } else {
+ ublk_init_req_ref(ubq, io);
+ ublk_complete_io_cmd(io, req, UBLK_IO_RES_OK, issue_flags);
+ }
+}
+
+static void ublk_cmd_tw_cb(struct io_tw_req tw_req, io_tw_token_t tw)
+{
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+ struct ublk_queue *ubq = pdu->ubq;
- __ublk_rq_task_work(pdu->req);
+ ublk_dispatch_req(ubq, pdu->req);
}
-static void ublk_rq_task_work_fn(struct callback_head *work)
+static void ublk_queue_cmd(struct ublk_queue *ubq, struct request *rq)
{
- struct ublk_rq_data *data = container_of(work,
- struct ublk_rq_data, work);
- struct request *req = blk_mq_rq_from_pdu(data);
+ struct io_uring_cmd *cmd = ubq->ios[rq->tag].cmd;
+ struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
- __ublk_rq_task_work(req);
+ pdu->req = rq;
+ io_uring_cmd_complete_in_task(cmd, ublk_cmd_tw_cb);
}
-static blk_status_t ublk_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd)
+static void ublk_cmd_list_tw_cb(struct io_tw_req tw_req, io_tw_token_t tw)
+{
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
+ struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+ struct request *rq = pdu->req_list;
+ struct request *next;
+
+ do {
+ next = rq->rq_next;
+ rq->rq_next = NULL;
+ ublk_dispatch_req(rq->mq_hctx->driver_data, rq);
+ rq = next;
+ } while (rq);
+}
+
+static void ublk_queue_cmd_list(struct ublk_io *io, struct rq_list *l)
+{
+ struct io_uring_cmd *cmd = io->cmd;
+ struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+
+ pdu->req_list = rq_list_peek(l);
+ rq_list_init(l);
+ io_uring_cmd_complete_in_task(cmd, ublk_cmd_list_tw_cb);
+}
+
+static enum blk_eh_timer_return ublk_timeout(struct request *rq)
+{
+ struct ublk_queue *ubq = rq->mq_hctx->driver_data;
+ pid_t tgid = ubq->dev->ublksrv_tgid;
+ struct task_struct *p;
+ struct pid *pid;
+
+ if (!(ubq->flags & UBLK_F_UNPRIVILEGED_DEV))
+ return BLK_EH_RESET_TIMER;
+
+ if (unlikely(!tgid))
+ return BLK_EH_RESET_TIMER;
+
+ rcu_read_lock();
+ pid = find_vpid(tgid);
+ p = pid_task(pid, PIDTYPE_PID);
+ if (p)
+ send_sig(SIGKILL, p, 0);
+ rcu_read_unlock();
+ return BLK_EH_DONE;
+}
+
+static blk_status_t ublk_prep_req(struct ublk_queue *ubq, struct request *rq,
+ bool check_cancel)
{
- struct ublk_queue *ubq = hctx->driver_data;
- struct request *rq = bd->rq;
blk_status_t res;
- /* fill iod to slot in io cmd buffer */
- res = ublk_setup_iod(ubq, rq);
- if (unlikely(res != BLK_STS_OK))
+ if (unlikely(READ_ONCE(ubq->fail_io)))
+ return BLK_STS_TARGET;
+
+ /* With recovery feature enabled, force_abort is set in
+ * ublk_stop_dev() before calling del_gendisk(). We have to
+ * abort all requeued and new rqs here to let del_gendisk()
+ * move on. Besides, we cannot not call io_uring_cmd_complete_in_task()
+ * to avoid UAF on io_uring ctx.
+ *
+ * Note: force_abort is guaranteed to be seen because it is set
+ * before request queue is unqiuesced.
+ */
+ if (ublk_nosrv_should_queue_io(ubq) &&
+ unlikely(READ_ONCE(ubq->force_abort)))
return BLK_STS_IOERR;
- blk_mq_start_request(bd->rq);
+ if (check_cancel && unlikely(ubq->canceling))
+ return BLK_STS_IOERR;
- if (unlikely(ubq_daemon_is_dying(ubq))) {
- fail:
- mod_delayed_work(system_wq, &ubq->dev->monitor_work, 0);
+ /* fill iod to slot in io cmd buffer */
+ res = ublk_setup_iod(ubq, rq);
+ if (unlikely(res != BLK_STS_OK))
return BLK_STS_IOERR;
- }
- if (ublk_can_use_task_work(ubq)) {
- struct ublk_rq_data *data = blk_mq_rq_to_pdu(rq);
- enum task_work_notify_mode notify_mode = bd->last ?
- TWA_SIGNAL_NO_IPI : TWA_NONE;
+ blk_mq_start_request(rq);
+ return BLK_STS_OK;
+}
- if (task_work_add(ubq->ubq_daemon, &data->work, notify_mode))
- goto fail;
- } else {
- struct ublk_io *io = &ubq->ios[rq->tag];
- struct io_uring_cmd *cmd = io->cmd;
- struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+static blk_status_t ublk_queue_rq(struct blk_mq_hw_ctx *hctx,
+ const struct blk_mq_queue_data *bd)
+{
+ struct ublk_queue *ubq = hctx->driver_data;
+ struct request *rq = bd->rq;
+ blk_status_t res;
- /*
- * If the check pass, we know that this is a re-issued request aborted
- * previously in monitor_work because the ubq_daemon(cmd's task) is
- * PF_EXITING. We cannot call io_uring_cmd_complete_in_task() anymore
- * because this ioucmd's io_uring context may be freed now if no inflight
- * ioucmd exists. Otherwise we may cause null-deref in ctx->fallback_work.
- *
- * Note: monitor_work sets UBLK_IO_FLAG_ABORTED and ends this request(releasing
- * the tag). Then the request is re-started(allocating the tag) and we are here.
- * Since releasing/allocating a tag implies smp_mb(), finding UBLK_IO_FLAG_ABORTED
- * guarantees that here is a re-issued request aborted previously.
- */
- if ((io->flags & UBLK_IO_FLAG_ABORTED))
- goto fail;
+ res = ublk_prep_req(ubq, rq, false);
+ if (res != BLK_STS_OK)
+ return res;
- pdu->req = rq;
- io_uring_cmd_complete_in_task(cmd, ublk_rq_task_work_cb);
+ /*
+ * ->canceling has to be handled after ->force_abort and ->fail_io
+ * is dealt with, otherwise this request may not be failed in case
+ * of recovery, and cause hang when deleting disk
+ */
+ if (unlikely(ubq->canceling)) {
+ __ublk_abort_rq(ubq, rq);
+ return BLK_STS_OK;
}
+ ublk_queue_cmd(ubq, rq);
return BLK_STS_OK;
}
-static void ublk_commit_rqs(struct blk_mq_hw_ctx *hctx)
+static inline bool ublk_belong_to_same_batch(const struct ublk_io *io,
+ const struct ublk_io *io2)
{
- struct ublk_queue *ubq = hctx->driver_data;
+ return (io_uring_cmd_ctx_handle(io->cmd) ==
+ io_uring_cmd_ctx_handle(io2->cmd)) &&
+ (io->task == io2->task);
+}
- if (ublk_can_use_task_work(ubq))
- __set_notify_signal(ubq->ubq_daemon);
+static void ublk_queue_rqs(struct rq_list *rqlist)
+{
+ struct rq_list requeue_list = { };
+ struct rq_list submit_list = { };
+ struct ublk_io *io = NULL;
+ struct request *req;
+
+ while ((req = rq_list_pop(rqlist))) {
+ struct ublk_queue *this_q = req->mq_hctx->driver_data;
+ struct ublk_io *this_io = &this_q->ios[req->tag];
+
+ if (ublk_prep_req(this_q, req, true) != BLK_STS_OK) {
+ rq_list_add_tail(&requeue_list, req);
+ continue;
+ }
+
+ if (io && !ublk_belong_to_same_batch(io, this_io) &&
+ !rq_list_empty(&submit_list))
+ ublk_queue_cmd_list(io, &submit_list);
+ io = this_io;
+ rq_list_add_tail(&submit_list, req);
+ }
+
+ if (!rq_list_empty(&submit_list))
+ ublk_queue_cmd_list(io, &submit_list);
+ *rqlist = requeue_list;
}
static int ublk_init_hctx(struct blk_mq_hw_ctx *hctx, void *driver_data,
@@ -800,22 +1475,44 @@ static int ublk_init_hctx(struct blk_mq_hw_ctx *hctx, void *driver_data,
return 0;
}
-static int ublk_init_rq(struct blk_mq_tag_set *set, struct request *req,
- unsigned int hctx_idx, unsigned int numa_node)
-{
- struct ublk_rq_data *data = blk_mq_rq_to_pdu(req);
-
- init_task_work(&data->work, ublk_rq_task_work_fn);
- return 0;
-}
-
static const struct blk_mq_ops ublk_mq_ops = {
.queue_rq = ublk_queue_rq,
- .commit_rqs = ublk_commit_rqs,
+ .queue_rqs = ublk_queue_rqs,
.init_hctx = ublk_init_hctx,
- .init_request = ublk_init_rq,
+ .timeout = ublk_timeout,
};
+static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq)
+{
+ int i;
+
+ for (i = 0; i < ubq->q_depth; i++) {
+ struct ublk_io *io = &ubq->ios[i];
+
+ /*
+ * UBLK_IO_FLAG_CANCELED is kept for avoiding to touch
+ * io->cmd
+ */
+ io->flags &= UBLK_IO_FLAG_CANCELED;
+ io->cmd = NULL;
+ io->buf.addr = 0;
+
+ /*
+ * old task is PF_EXITING, put it now
+ *
+ * It could be NULL in case of closing one quiesced
+ * device.
+ */
+ if (io->task) {
+ put_task_struct(io->task);
+ io->task = NULL;
+ }
+
+ WARN_ON_ONCE(refcount_read(&io->ref));
+ WARN_ON_ONCE(io->task_registered_buffers);
+ }
+}
+
static int ublk_ch_open(struct inode *inode, struct file *filp)
{
struct ublk_device *ub = container_of(inode->i_cdev,
@@ -824,14 +1521,209 @@ static int ublk_ch_open(struct inode *inode, struct file *filp)
if (test_and_set_bit(UB_STATE_OPEN, &ub->state))
return -EBUSY;
filp->private_data = ub;
+ ub->ublksrv_tgid = current->tgid;
return 0;
}
+static void ublk_reset_ch_dev(struct ublk_device *ub)
+{
+ int i;
+
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
+ ublk_queue_reinit(ub, ublk_get_queue(ub, i));
+
+ /* set to NULL, otherwise new tasks cannot mmap io_cmd_buf */
+ ub->mm = NULL;
+ ub->nr_io_ready = 0;
+ ub->unprivileged_daemons = false;
+ ub->ublksrv_tgid = -1;
+}
+
+static struct gendisk *ublk_get_disk(struct ublk_device *ub)
+{
+ struct gendisk *disk;
+
+ spin_lock(&ub->lock);
+ disk = ub->ub_disk;
+ if (disk)
+ get_device(disk_to_dev(disk));
+ spin_unlock(&ub->lock);
+
+ return disk;
+}
+
+static void ublk_put_disk(struct gendisk *disk)
+{
+ if (disk)
+ put_device(disk_to_dev(disk));
+}
+
+/*
+ * Use this function to ensure that ->canceling is consistently set for
+ * the device and all queues. Do not set these flags directly.
+ *
+ * Caller must ensure that:
+ * - cancel_mutex is held. This ensures that there is no concurrent
+ * access to ub->canceling and no concurrent writes to ubq->canceling.
+ * - there are no concurrent reads of ubq->canceling from the queue_rq
+ * path. This can be done by quiescing the queue, or through other
+ * means.
+ */
+static void ublk_set_canceling(struct ublk_device *ub, bool canceling)
+ __must_hold(&ub->cancel_mutex)
+{
+ int i;
+
+ ub->canceling = canceling;
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
+ ublk_get_queue(ub, i)->canceling = canceling;
+}
+
+static bool ublk_check_and_reset_active_ref(struct ublk_device *ub)
+{
+ int i, j;
+
+ if (!(ub->dev_info.flags & (UBLK_F_SUPPORT_ZERO_COPY |
+ UBLK_F_AUTO_BUF_REG)))
+ return false;
+
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
+ struct ublk_queue *ubq = ublk_get_queue(ub, i);
+
+ for (j = 0; j < ubq->q_depth; j++) {
+ struct ublk_io *io = &ubq->ios[j];
+ unsigned int refs = refcount_read(&io->ref) +
+ io->task_registered_buffers;
+
+ /*
+ * UBLK_REFCOUNT_INIT or zero means no active
+ * reference
+ */
+ if (refs != UBLK_REFCOUNT_INIT && refs != 0)
+ return true;
+
+ /* reset to zero if the io hasn't active references */
+ refcount_set(&io->ref, 0);
+ io->task_registered_buffers = 0;
+ }
+ }
+ return false;
+}
+
+static void ublk_ch_release_work_fn(struct work_struct *work)
+{
+ struct ublk_device *ub =
+ container_of(work, struct ublk_device, exit_work.work);
+ struct gendisk *disk;
+ int i;
+
+ /*
+ * For zero-copy and auto buffer register modes, I/O references
+ * might not be dropped naturally when the daemon is killed, but
+ * io_uring guarantees that registered bvec kernel buffers are
+ * unregistered finally when freeing io_uring context, then the
+ * active references are dropped.
+ *
+ * Wait until active references are dropped for avoiding use-after-free
+ *
+ * registered buffer may be unregistered in io_ring's release hander,
+ * so have to wait by scheduling work function for avoiding the two
+ * file release dependency.
+ */
+ if (ublk_check_and_reset_active_ref(ub)) {
+ schedule_delayed_work(&ub->exit_work, 1);
+ return;
+ }
+
+ /*
+ * disk isn't attached yet, either device isn't live, or it has
+ * been removed already, so we needn't to do anything
+ */
+ disk = ublk_get_disk(ub);
+ if (!disk)
+ goto out;
+
+ /*
+ * All uring_cmd are done now, so abort any request outstanding to
+ * the ublk server
+ *
+ * This can be done in lockless way because ublk server has been
+ * gone
+ *
+ * More importantly, we have to provide forward progress guarantee
+ * without holding ub->mutex, otherwise control task grabbing
+ * ub->mutex triggers deadlock
+ *
+ * All requests may be inflight, so ->canceling may not be set, set
+ * it now.
+ */
+ mutex_lock(&ub->cancel_mutex);
+ ublk_set_canceling(ub, true);
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
+ ublk_abort_queue(ub, ublk_get_queue(ub, i));
+ mutex_unlock(&ub->cancel_mutex);
+ blk_mq_kick_requeue_list(disk->queue);
+
+ /*
+ * All infligh requests have been completed or requeued and any new
+ * request will be failed or requeued via `->canceling` now, so it is
+ * fine to grab ub->mutex now.
+ */
+ mutex_lock(&ub->mutex);
+
+ /* double check after grabbing lock */
+ if (!ub->ub_disk)
+ goto unlock;
+
+ /*
+ * Transition the device to the nosrv state. What exactly this
+ * means depends on the recovery flags
+ */
+ if (ublk_nosrv_should_stop_dev(ub)) {
+ /*
+ * Allow any pending/future I/O to pass through quickly
+ * with an error. This is needed because del_gendisk
+ * waits for all pending I/O to complete
+ */
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
+ WRITE_ONCE(ublk_get_queue(ub, i)->force_abort, true);
+
+ ublk_stop_dev_unlocked(ub);
+ } else {
+ if (ublk_nosrv_dev_should_queue_io(ub)) {
+ /* ->canceling is set and all requests are aborted */
+ ub->dev_info.state = UBLK_S_DEV_QUIESCED;
+ } else {
+ ub->dev_info.state = UBLK_S_DEV_FAIL_IO;
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
+ WRITE_ONCE(ublk_get_queue(ub, i)->fail_io, true);
+ }
+ }
+unlock:
+ mutex_unlock(&ub->mutex);
+ ublk_put_disk(disk);
+
+ /* all uring_cmd has been done now, reset device & ubq */
+ ublk_reset_ch_dev(ub);
+out:
+ clear_bit(UB_STATE_OPEN, &ub->state);
+
+ /* put the reference grabbed in ublk_ch_release() */
+ ublk_put_device(ub);
+}
+
static int ublk_ch_release(struct inode *inode, struct file *filp)
{
struct ublk_device *ub = filp->private_data;
- clear_bit(UB_STATE_OPEN, &ub->state);
+ /*
+ * Grab ublk device reference, so it won't be gone until we are
+ * really released from work function.
+ */
+ ublk_get_device(ub);
+
+ INIT_DELAYED_WORK(&ub->exit_work, ublk_ch_release_work_fn);
+ schedule_delayed_work(&ub->exit_work, 0);
return 0;
}
@@ -840,16 +1732,16 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct ublk_device *ub = filp->private_data;
size_t sz = vma->vm_end - vma->vm_start;
- unsigned max_sz = UBLK_MAX_QUEUE_DEPTH * sizeof(struct ublksrv_io_desc);
+ unsigned max_sz = ublk_max_cmd_buf_size();
unsigned long pfn, end, phys_off = vma->vm_pgoff << PAGE_SHIFT;
int q_id, ret = 0;
- spin_lock(&ub->mm_lock);
+ spin_lock(&ub->lock);
if (!ub->mm)
ub->mm = current->mm;
if (current->mm != ub->mm)
ret = -EINVAL;
- spin_unlock(&ub->mm_lock);
+ spin_unlock(&ub->lock);
if (ret)
return ret;
@@ -866,207 +1758,641 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma)
__func__, q_id, current->pid, vma->vm_start,
phys_off, (unsigned long)sz);
- if (sz != ublk_queue_cmd_buf_size(ub, q_id))
+ if (sz != ublk_queue_cmd_buf_size(ub))
return -EINVAL;
pfn = virt_to_phys(ublk_queue_cmd_buf(ub, q_id)) >> PAGE_SHIFT;
return remap_pfn_range(vma, vma->vm_start, pfn, sz, vma->vm_page_prot);
}
-static void ublk_commit_completion(struct ublk_device *ub,
- struct ublksrv_io_cmd *ub_cmd)
+static void __ublk_fail_req(struct ublk_device *ub, struct ublk_io *io,
+ struct request *req)
{
- u32 qid = ub_cmd->q_id, tag = ub_cmd->tag;
- struct ublk_queue *ubq = ublk_get_queue(ub, qid);
- struct ublk_io *io = &ubq->ios[tag];
- struct request *req;
-
- /* now this cmd slot is owned by nbd driver */
- io->flags &= ~UBLK_IO_FLAG_OWNED_BY_SRV;
- io->res = ub_cmd->result;
-
- /* find the io request and complete */
- req = blk_mq_tag_to_rq(ub->tag_set.tags[qid], tag);
+ WARN_ON_ONCE(io->flags & UBLK_IO_FLAG_ACTIVE);
- if (req && likely(!blk_should_fake_timeout(req->q)))
- ublk_complete_rq(req);
+ if (ublk_nosrv_should_reissue_outstanding(ub))
+ blk_mq_requeue_request(req, false);
+ else {
+ io->res = -EIO;
+ __ublk_complete_rq(req, io, ublk_dev_need_map_io(ub));
+ }
}
/*
- * When ->ubq_daemon is exiting, either new request is ended immediately,
- * or any queued io command is drained, so it is safe to abort queue
- * lockless
+ * Called from ublk char device release handler, when any uring_cmd is
+ * done, meantime request queue is "quiesced" since all inflight requests
+ * can't be completed because ublk server is dead.
+ *
+ * So no one can hold our request IO reference any more, simply ignore the
+ * reference, and complete the request immediately
*/
static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq)
{
int i;
- if (!ublk_get_device(ub))
- return;
-
for (i = 0; i < ubq->q_depth; i++) {
struct ublk_io *io = &ubq->ios[i];
- if (!(io->flags & UBLK_IO_FLAG_ACTIVE)) {
- struct request *rq;
-
- /*
- * Either we fail the request or ublk_rq_task_work_fn
- * will do it
- */
- rq = blk_mq_tag_to_rq(ub->tag_set.tags[ubq->q_id], i);
- if (rq)
- __ublk_fail_req(io, rq);
- }
+ if (io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)
+ __ublk_fail_req(ub, io, io->req);
}
- ublk_put_device(ub);
}
-static void ublk_daemon_monitor_work(struct work_struct *work)
+static void ublk_start_cancel(struct ublk_device *ub)
{
- struct ublk_device *ub =
- container_of(work, struct ublk_device, monitor_work.work);
- int i;
+ struct gendisk *disk = ublk_get_disk(ub);
- for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
- struct ublk_queue *ubq = ublk_get_queue(ub, i);
+ /* Our disk has been dead */
+ if (!disk)
+ return;
- if (ubq_daemon_is_dying(ubq)) {
- schedule_work(&ub->stop_work);
+ mutex_lock(&ub->cancel_mutex);
+ if (ub->canceling)
+ goto out;
+ /*
+ * Now we are serialized with ublk_queue_rq()
+ *
+ * Make sure that ubq->canceling is set when queue is frozen,
+ * because ublk_queue_rq() has to rely on this flag for avoiding to
+ * touch completed uring_cmd
+ */
+ blk_mq_quiesce_queue(disk->queue);
+ ublk_set_canceling(ub, true);
+ blk_mq_unquiesce_queue(disk->queue);
+out:
+ mutex_unlock(&ub->cancel_mutex);
+ ublk_put_disk(disk);
+}
- /* abort queue is for making forward progress */
- ublk_abort_queue(ub, ubq);
- }
- }
+static void ublk_cancel_cmd(struct ublk_queue *ubq, unsigned tag,
+ unsigned int issue_flags)
+{
+ struct ublk_io *io = &ubq->ios[tag];
+ struct ublk_device *ub = ubq->dev;
+ struct request *req;
+ bool done;
+
+ if (!(io->flags & UBLK_IO_FLAG_ACTIVE))
+ return;
/*
- * We can't schedule monitor work after ublk_remove() is started.
+ * Don't try to cancel this command if the request is started for
+ * avoiding race between io_uring_cmd_done() and
+ * io_uring_cmd_complete_in_task().
*
- * No need ub->mutex, monitor work are canceled after state is marked
- * as DEAD, so DEAD state is observed reliably.
+ * Either the started request will be aborted via __ublk_abort_rq(),
+ * then this uring_cmd is canceled next time, or it will be done in
+ * task work function ublk_dispatch_req() because io_uring guarantees
+ * that ublk_dispatch_req() is always called
*/
- if (ub->dev_info.state != UBLK_S_DEV_DEAD)
- schedule_delayed_work(&ub->monitor_work,
- UBLK_DAEMON_MONITOR_PERIOD);
+ req = blk_mq_tag_to_rq(ub->tag_set.tags[ubq->q_id], tag);
+ if (req && blk_mq_request_started(req) && req->tag == tag)
+ return;
+
+ spin_lock(&ubq->cancel_lock);
+ done = !!(io->flags & UBLK_IO_FLAG_CANCELED);
+ if (!done)
+ io->flags |= UBLK_IO_FLAG_CANCELED;
+ spin_unlock(&ubq->cancel_lock);
+
+ if (!done)
+ io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, issue_flags);
}
-static inline bool ublk_queue_ready(struct ublk_queue *ubq)
+/*
+ * The ublk char device won't be closed when calling cancel fn, so both
+ * ublk device and queue are guaranteed to be live
+ *
+ * Two-stage cancel:
+ *
+ * - make every active uring_cmd done in ->cancel_fn()
+ *
+ * - aborting inflight ublk IO requests in ublk char device release handler,
+ * which depends on 1st stage because device can only be closed iff all
+ * uring_cmd are done
+ *
+ * Do _not_ try to acquire ub->mutex before all inflight requests are
+ * aborted, otherwise deadlock may be caused.
+ */
+static void ublk_uring_cmd_cancel_fn(struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
{
- return ubq->nr_io_ready == ubq->q_depth;
+ struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+ struct ublk_queue *ubq = pdu->ubq;
+ struct task_struct *task;
+ struct ublk_io *io;
+
+ if (WARN_ON_ONCE(!ubq))
+ return;
+
+ if (WARN_ON_ONCE(pdu->tag >= ubq->q_depth))
+ return;
+
+ task = io_uring_cmd_get_task(cmd);
+ io = &ubq->ios[pdu->tag];
+ if (WARN_ON_ONCE(task && task != io->task))
+ return;
+
+ ublk_start_cancel(ubq->dev);
+
+ WARN_ON_ONCE(io->cmd != cmd);
+ ublk_cancel_cmd(ubq, pdu->tag, issue_flags);
+}
+
+static inline bool ublk_dev_ready(const struct ublk_device *ub)
+{
+ u32 total = (u32)ub->dev_info.nr_hw_queues * ub->dev_info.queue_depth;
+
+ return ub->nr_io_ready == total;
}
static void ublk_cancel_queue(struct ublk_queue *ubq)
{
int i;
- if (!ublk_queue_ready(ubq))
- return;
+ for (i = 0; i < ubq->q_depth; i++)
+ ublk_cancel_cmd(ubq, i, IO_URING_F_UNLOCKED);
+}
- for (i = 0; i < ubq->q_depth; i++) {
- struct ublk_io *io = &ubq->ios[i];
+/* Cancel all pending commands, must be called after del_gendisk() returns */
+static void ublk_cancel_dev(struct ublk_device *ub)
+{
+ int i;
- if (io->flags & UBLK_IO_FLAG_ACTIVE)
- io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, 0);
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
+ ublk_cancel_queue(ublk_get_queue(ub, i));
+}
+
+static bool ublk_check_inflight_rq(struct request *rq, void *data)
+{
+ bool *idle = data;
+
+ if (blk_mq_request_started(rq)) {
+ *idle = false;
+ return false;
}
+ return true;
+}
+
+static void ublk_wait_tagset_rqs_idle(struct ublk_device *ub)
+{
+ bool idle;
- /* all io commands are canceled */
- ubq->nr_io_ready = 0;
+ WARN_ON_ONCE(!blk_queue_quiesced(ub->ub_disk->queue));
+ while (true) {
+ idle = true;
+ blk_mq_tagset_busy_iter(&ub->tag_set,
+ ublk_check_inflight_rq, &idle);
+ if (idle)
+ break;
+ msleep(UBLK_REQUEUE_DELAY_MS);
+ }
}
-/* Cancel all pending commands, must be called after del_gendisk() returns */
-static void ublk_cancel_dev(struct ublk_device *ub)
+static void ublk_force_abort_dev(struct ublk_device *ub)
{
int i;
+ pr_devel("%s: force abort ub: dev_id %d state %s\n",
+ __func__, ub->dev_info.dev_id,
+ ub->dev_info.state == UBLK_S_DEV_LIVE ?
+ "LIVE" : "QUIESCED");
+ blk_mq_quiesce_queue(ub->ub_disk->queue);
+ if (ub->dev_info.state == UBLK_S_DEV_LIVE)
+ ublk_wait_tagset_rqs_idle(ub);
+
for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
- ublk_cancel_queue(ublk_get_queue(ub, i));
+ ublk_get_queue(ub, i)->force_abort = true;
+ blk_mq_unquiesce_queue(ub->ub_disk->queue);
+ /* We may have requeued some rqs in ublk_quiesce_queue() */
+ blk_mq_kick_requeue_list(ub->ub_disk->queue);
}
-static void ublk_stop_dev(struct ublk_device *ub)
+static struct gendisk *ublk_detach_disk(struct ublk_device *ub)
{
- mutex_lock(&ub->mutex);
- if (ub->dev_info.state != UBLK_S_DEV_LIVE)
- goto unlock;
+ struct gendisk *disk;
- del_gendisk(ub->ub_disk);
+ /* Sync with ublk_abort_queue() by holding the lock */
+ spin_lock(&ub->lock);
+ disk = ub->ub_disk;
ub->dev_info.state = UBLK_S_DEV_DEAD;
ub->dev_info.ublksrv_pid = -1;
- put_disk(ub->ub_disk);
ub->ub_disk = NULL;
- unlock:
- ublk_cancel_dev(ub);
- mutex_unlock(&ub->mutex);
- cancel_delayed_work_sync(&ub->monitor_work);
+ spin_unlock(&ub->lock);
+
+ return disk;
}
-/* device can only be started after all IOs are ready */
-static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq)
+static void ublk_stop_dev_unlocked(struct ublk_device *ub)
+ __must_hold(&ub->mutex)
+{
+ struct gendisk *disk;
+
+ if (ub->dev_info.state == UBLK_S_DEV_DEAD)
+ return;
+
+ if (ublk_nosrv_dev_should_queue_io(ub))
+ ublk_force_abort_dev(ub);
+ del_gendisk(ub->ub_disk);
+ disk = ublk_detach_disk(ub);
+ put_disk(disk);
+}
+
+static void ublk_stop_dev(struct ublk_device *ub)
{
mutex_lock(&ub->mutex);
- ubq->nr_io_ready++;
- if (ublk_queue_ready(ubq)) {
- ubq->ubq_daemon = current;
- get_task_struct(ubq->ubq_daemon);
- ub->nr_queues_ready++;
+ ublk_stop_dev_unlocked(ub);
+ mutex_unlock(&ub->mutex);
+ ublk_cancel_dev(ub);
+}
+
+/* reset ublk io_uring queue & io flags */
+static void ublk_reset_io_flags(struct ublk_device *ub)
+{
+ int i, j;
+
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
+ struct ublk_queue *ubq = ublk_get_queue(ub, i);
+
+ /* UBLK_IO_FLAG_CANCELED can be cleared now */
+ spin_lock(&ubq->cancel_lock);
+ for (j = 0; j < ubq->q_depth; j++)
+ ubq->ios[j].flags &= ~UBLK_IO_FLAG_CANCELED;
+ spin_unlock(&ubq->cancel_lock);
+ ubq->fail_io = false;
}
- if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues)
+ mutex_lock(&ub->cancel_mutex);
+ ublk_set_canceling(ub, false);
+ mutex_unlock(&ub->cancel_mutex);
+}
+
+/* device can only be started after all IOs are ready */
+static void ublk_mark_io_ready(struct ublk_device *ub)
+ __must_hold(&ub->mutex)
+{
+ if (!ub->unprivileged_daemons && !capable(CAP_SYS_ADMIN))
+ ub->unprivileged_daemons = true;
+
+ ub->nr_io_ready++;
+ if (ublk_dev_ready(ub)) {
+ /* now we are ready for handling ublk io request */
+ ublk_reset_io_flags(ub);
complete_all(&ub->completion);
- mutex_unlock(&ub->mutex);
+ }
}
-static void ublk_handle_need_get_data(struct ublk_device *ub, int q_id,
- int tag, struct io_uring_cmd *cmd)
+static inline int ublk_check_cmd_op(u32 cmd_op)
{
- struct ublk_queue *ubq = ublk_get_queue(ub, q_id);
- struct request *req = blk_mq_tag_to_rq(ub->tag_set.tags[q_id], tag);
+ u32 ioc_type = _IOC_TYPE(cmd_op);
- if (ublk_can_use_task_work(ubq)) {
- struct ublk_rq_data *data = blk_mq_rq_to_pdu(req);
+ if (!IS_ENABLED(CONFIG_BLKDEV_UBLK_LEGACY_OPCODES) && ioc_type != 'u')
+ return -EOPNOTSUPP;
- /* should not fail since we call it just in ubq->ubq_daemon */
- task_work_add(ubq->ubq_daemon, &data->work, TWA_SIGNAL_NO_IPI);
- } else {
- struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+ if (ioc_type != 'u' && ioc_type != 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static inline int ublk_set_auto_buf_reg(struct ublk_io *io, struct io_uring_cmd *cmd)
+{
+ struct ublk_auto_buf_reg buf;
+
+ buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
+
+ if (buf.reserved0 || buf.reserved1)
+ return -EINVAL;
+
+ if (buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK)
+ return -EINVAL;
+ io->buf.auto_reg = buf;
+ return 0;
+}
+
+static int ublk_handle_auto_buf_reg(struct ublk_io *io,
+ struct io_uring_cmd *cmd,
+ u16 *buf_idx)
+{
+ if (io->flags & UBLK_IO_FLAG_AUTO_BUF_REG) {
+ io->flags &= ~UBLK_IO_FLAG_AUTO_BUF_REG;
- pdu->req = req;
- io_uring_cmd_complete_in_task(cmd, ublk_rq_task_work_cb);
+ /*
+ * `UBLK_F_AUTO_BUF_REG` only works iff `UBLK_IO_FETCH_REQ`
+ * and `UBLK_IO_COMMIT_AND_FETCH_REQ` are issued from same
+ * `io_ring_ctx`.
+ *
+ * If this uring_cmd's io_ring_ctx isn't same with the
+ * one for registering the buffer, it is ublk server's
+ * responsibility for unregistering the buffer, otherwise
+ * this ublk request gets stuck.
+ */
+ if (io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd))
+ *buf_idx = io->buf.auto_reg.index;
}
+
+ return ublk_set_auto_buf_reg(io, cmd);
}
-static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
+/* Once we return, `io->req` can't be used any more */
+static inline struct request *
+ublk_fill_io_cmd(struct ublk_io *io, struct io_uring_cmd *cmd)
{
- struct ublksrv_io_cmd *ub_cmd = (struct ublksrv_io_cmd *)cmd->cmd;
+ struct request *req = io->req;
+
+ io->cmd = cmd;
+ io->flags |= UBLK_IO_FLAG_ACTIVE;
+ /* now this cmd slot is owned by ublk driver */
+ io->flags &= ~UBLK_IO_FLAG_OWNED_BY_SRV;
+
+ return req;
+}
+
+static inline int
+ublk_config_io_buf(const struct ublk_device *ub, struct ublk_io *io,
+ struct io_uring_cmd *cmd, unsigned long buf_addr,
+ u16 *buf_idx)
+{
+ if (ublk_dev_support_auto_buf_reg(ub))
+ return ublk_handle_auto_buf_reg(io, cmd, buf_idx);
+
+ io->buf.addr = buf_addr;
+ return 0;
+}
+
+static inline void ublk_prep_cancel(struct io_uring_cmd *cmd,
+ unsigned int issue_flags,
+ struct ublk_queue *ubq, unsigned int tag)
+{
+ struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
+
+ /*
+ * Safe to refer to @ubq since ublk_queue won't be died until its
+ * commands are completed
+ */
+ pdu->ubq = ubq;
+ pdu->tag = tag;
+ io_uring_cmd_mark_cancelable(cmd, issue_flags);
+}
+
+static void ublk_io_release(void *priv)
+{
+ struct request *rq = priv;
+ struct ublk_queue *ubq = rq->mq_hctx->driver_data;
+ struct ublk_io *io = &ubq->ios[rq->tag];
+
+ /*
+ * task_registered_buffers may be 0 if buffers were registered off task
+ * but unregistered on task. Or after UBLK_IO_COMMIT_AND_FETCH_REQ.
+ */
+ if (current == io->task && io->task_registered_buffers)
+ io->task_registered_buffers--;
+ else
+ ublk_put_req_ref(io, rq);
+}
+
+static int ublk_register_io_buf(struct io_uring_cmd *cmd,
+ struct ublk_device *ub,
+ u16 q_id, u16 tag,
+ struct ublk_io *io,
+ unsigned int index, unsigned int issue_flags)
+{
+ struct request *req;
+ int ret;
+
+ if (!ublk_dev_support_zero_copy(ub))
+ return -EINVAL;
+
+ req = __ublk_check_and_get_req(ub, q_id, tag, io, 0);
+ if (!req)
+ return -EINVAL;
+
+ ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index,
+ issue_flags);
+ if (ret) {
+ ublk_put_req_ref(io, req);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+ublk_daemon_register_io_buf(struct io_uring_cmd *cmd,
+ struct ublk_device *ub,
+ u16 q_id, u16 tag, struct ublk_io *io,
+ unsigned index, unsigned issue_flags)
+{
+ unsigned new_registered_buffers;
+ struct request *req = io->req;
+ int ret;
+
+ /*
+ * Ensure there are still references for ublk_sub_req_ref() to release.
+ * If not, fall back on the thread-safe buffer registration.
+ */
+ new_registered_buffers = io->task_registered_buffers + 1;
+ if (unlikely(new_registered_buffers >= UBLK_REFCOUNT_INIT))
+ return ublk_register_io_buf(cmd, ub, q_id, tag, io, index,
+ issue_flags);
+
+ if (!ublk_dev_support_zero_copy(ub) || !ublk_rq_has_data(req))
+ return -EINVAL;
+
+ ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index,
+ issue_flags);
+ if (ret)
+ return ret;
+
+ io->task_registered_buffers = new_registered_buffers;
+ return 0;
+}
+
+static int ublk_unregister_io_buf(struct io_uring_cmd *cmd,
+ const struct ublk_device *ub,
+ unsigned int index, unsigned int issue_flags)
+{
+ if (!(ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY))
+ return -EINVAL;
+
+ return io_buffer_unregister_bvec(cmd, index, issue_flags);
+}
+
+static int ublk_check_fetch_buf(const struct ublk_device *ub, __u64 buf_addr)
+{
+ if (ublk_dev_need_map_io(ub)) {
+ /*
+ * FETCH_RQ has to provide IO buffer if NEED GET
+ * DATA is not enabled
+ */
+ if (!buf_addr && !ublk_dev_need_get_data(ub))
+ return -EINVAL;
+ } else if (buf_addr) {
+ /* User copy requires addr to be unset */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
+ struct ublk_io *io)
+{
+ /* UBLK_IO_FETCH_REQ is only allowed before dev is setup */
+ if (ublk_dev_ready(ub))
+ return -EBUSY;
+
+ /* allow each command to be FETCHed at most once */
+ if (io->flags & UBLK_IO_FLAG_ACTIVE)
+ return -EINVAL;
+
+ WARN_ON_ONCE(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV);
+
+ ublk_fill_io_cmd(io, cmd);
+
+ WRITE_ONCE(io->task, get_task_struct(current));
+ ublk_mark_io_ready(ub);
+
+ return 0;
+}
+
+static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
+ struct ublk_io *io, __u64 buf_addr)
+{
+ int ret;
+
+ /*
+ * When handling FETCH command for setting up ublk uring queue,
+ * ub->mutex is the innermost lock, and we won't block for handling
+ * FETCH, so it is fine even for IO_URING_F_NONBLOCK.
+ */
+ mutex_lock(&ub->mutex);
+ ret = __ublk_fetch(cmd, ub, io);
+ if (!ret)
+ ret = ublk_config_io_buf(ub, io, cmd, buf_addr, NULL);
+ mutex_unlock(&ub->mutex);
+ return ret;
+}
+
+static int ublk_check_commit_and_fetch(const struct ublk_device *ub,
+ struct ublk_io *io, __u64 buf_addr)
+{
+ struct request *req = io->req;
+
+ if (ublk_dev_need_map_io(ub)) {
+ /*
+ * COMMIT_AND_FETCH_REQ has to provide IO buffer if
+ * NEED GET DATA is not enabled or it is Read IO.
+ */
+ if (!buf_addr && (!ublk_dev_need_get_data(ub) ||
+ req_op(req) == REQ_OP_READ))
+ return -EINVAL;
+ } else if (req_op(req) != REQ_OP_ZONE_APPEND && buf_addr) {
+ /*
+ * User copy requires addr to be unset when command is
+ * not zone append
+ */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool ublk_need_complete_req(const struct ublk_device *ub,
+ struct ublk_io *io)
+{
+ if (ublk_dev_need_req_ref(ub))
+ return ublk_sub_req_ref(io);
+ return true;
+}
+
+static bool ublk_get_data(const struct ublk_queue *ubq, struct ublk_io *io,
+ struct request *req)
+{
+ /*
+ * We have handled UBLK_IO_NEED_GET_DATA command,
+ * so clear UBLK_IO_FLAG_NEED_GET_DATA now and just
+ * do the copy work.
+ */
+ io->flags &= ~UBLK_IO_FLAG_NEED_GET_DATA;
+ /* update iod->addr because ublksrv may have passed a new io buffer */
+ ublk_get_iod(ubq, req->tag)->addr = io->buf.addr;
+ pr_devel("%s: update iod->addr: qid %d tag %d io_flags %x addr %llx\n",
+ __func__, ubq->q_id, req->tag, io->flags,
+ ublk_get_iod(ubq, req->tag)->addr);
+
+ return ublk_start_io(ubq, req, io);
+}
+
+static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
+{
+ /* May point to userspace-mapped memory */
+ const struct ublksrv_io_cmd *ub_src = io_uring_sqe_cmd(cmd->sqe);
+ u16 buf_idx = UBLK_INVALID_BUF_IDX;
struct ublk_device *ub = cmd->file->private_data;
struct ublk_queue *ubq;
- struct ublk_io *io;
+ struct ublk_io *io = NULL;
u32 cmd_op = cmd->cmd_op;
- unsigned tag = ub_cmd->tag;
- int ret = -EINVAL;
+ u16 q_id = READ_ONCE(ub_src->q_id);
+ u16 tag = READ_ONCE(ub_src->tag);
+ s32 result = READ_ONCE(ub_src->result);
+ u64 addr = READ_ONCE(ub_src->addr); /* unioned with zone_append_lba */
+ struct request *req;
+ int ret;
+ bool compl;
+
+ WARN_ON_ONCE(issue_flags & IO_URING_F_UNLOCKED);
pr_devel("%s: received: cmd op %d queue %d tag %d result %d\n",
- __func__, cmd->cmd_op, ub_cmd->q_id, tag,
- ub_cmd->result);
+ __func__, cmd->cmd_op, q_id, tag, result);
- if (!(issue_flags & IO_URING_F_SQE128))
+ ret = ublk_check_cmd_op(cmd_op);
+ if (ret)
goto out;
- if (ub_cmd->q_id >= ub->dev_info.nr_hw_queues)
- goto out;
+ /*
+ * io_buffer_unregister_bvec() doesn't access the ubq or io,
+ * so no need to validate the q_id, tag, or task
+ */
+ if (_IOC_NR(cmd_op) == UBLK_IO_UNREGISTER_IO_BUF)
+ return ublk_unregister_io_buf(cmd, ub, addr, issue_flags);
- ubq = ublk_get_queue(ub, ub_cmd->q_id);
- if (!ubq || ub_cmd->q_id != ubq->q_id)
+ ret = -EINVAL;
+ if (q_id >= ub->dev_info.nr_hw_queues)
goto out;
- if (ubq->ubq_daemon && ubq->ubq_daemon != current)
- goto out;
+ ubq = ublk_get_queue(ub, q_id);
- if (tag >= ubq->q_depth)
+ if (tag >= ub->dev_info.queue_depth)
goto out;
io = &ubq->ios[tag];
+ /* UBLK_IO_FETCH_REQ can be handled on any task, which sets io->task */
+ if (unlikely(_IOC_NR(cmd_op) == UBLK_IO_FETCH_REQ)) {
+ ret = ublk_check_fetch_buf(ub, addr);
+ if (ret)
+ goto out;
+ ret = ublk_fetch(cmd, ub, io, addr);
+ if (ret)
+ goto out;
+
+ ublk_prep_cancel(cmd, issue_flags, ubq, tag);
+ return -EIOCBQUEUED;
+ }
+
+ if (READ_ONCE(io->task) != current) {
+ /*
+ * ublk_register_io_buf() accesses only the io's refcount,
+ * so can be handled on any task
+ */
+ if (_IOC_NR(cmd_op) == UBLK_IO_REGISTER_IO_BUF)
+ return ublk_register_io_buf(cmd, ub, q_id, tag, io,
+ addr, issue_flags);
+
+ goto out;
+ }
/* there is pending io cmd, something must be wrong */
- if (io->flags & UBLK_IO_FLAG_ACTIVE) {
+ if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)) {
ret = -EBUSY;
goto out;
}
@@ -1076,130 +2402,312 @@ static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
* iff the driver have set the UBLK_IO_FLAG_NEED_GET_DATA.
*/
if ((!!(io->flags & UBLK_IO_FLAG_NEED_GET_DATA))
- ^ (cmd_op == UBLK_IO_NEED_GET_DATA))
+ ^ (_IOC_NR(cmd_op) == UBLK_IO_NEED_GET_DATA))
goto out;
- switch (cmd_op) {
- case UBLK_IO_FETCH_REQ:
- /* UBLK_IO_FETCH_REQ is only allowed before queue is setup */
- if (ublk_queue_ready(ubq)) {
- ret = -EBUSY;
- goto out;
- }
- /*
- * The io is being handled by server, so COMMIT_RQ is expected
- * instead of FETCH_REQ
- */
- if (io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)
- goto out;
- /* FETCH_RQ has to provide IO buffer */
- if (!ub_cmd->addr)
- goto out;
- io->cmd = cmd;
- io->flags |= UBLK_IO_FLAG_ACTIVE;
- io->addr = ub_cmd->addr;
-
- ublk_mark_io_ready(ub, ubq);
- break;
+ switch (_IOC_NR(cmd_op)) {
+ case UBLK_IO_REGISTER_IO_BUF:
+ return ublk_daemon_register_io_buf(cmd, ub, q_id, tag, io, addr,
+ issue_flags);
case UBLK_IO_COMMIT_AND_FETCH_REQ:
- /* FETCH_RQ has to provide IO buffer */
- if (!ub_cmd->addr)
+ ret = ublk_check_commit_and_fetch(ub, io, addr);
+ if (ret)
goto out;
- if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV))
+ io->res = result;
+ req = ublk_fill_io_cmd(io, cmd);
+ ret = ublk_config_io_buf(ub, io, cmd, addr, &buf_idx);
+ compl = ublk_need_complete_req(ub, io);
+
+ /* can't touch 'ublk_io' any more */
+ if (buf_idx != UBLK_INVALID_BUF_IDX)
+ io_buffer_unregister_bvec(cmd, buf_idx, issue_flags);
+ if (req_op(req) == REQ_OP_ZONE_APPEND)
+ req->__sector = addr;
+ if (compl)
+ __ublk_complete_rq(req, io, ublk_dev_need_map_io(ub));
+
+ if (ret)
goto out;
- io->addr = ub_cmd->addr;
- io->flags |= UBLK_IO_FLAG_ACTIVE;
- io->cmd = cmd;
- ublk_commit_completion(ub, ub_cmd);
break;
case UBLK_IO_NEED_GET_DATA:
- if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV))
- goto out;
- io->addr = ub_cmd->addr;
- io->cmd = cmd;
- io->flags |= UBLK_IO_FLAG_ACTIVE;
- ublk_handle_need_get_data(ub, ub_cmd->q_id, ub_cmd->tag, cmd);
+ /*
+ * ublk_get_data() may fail and fallback to requeue, so keep
+ * uring_cmd active first and prepare for handling new requeued
+ * request
+ */
+ req = ublk_fill_io_cmd(io, cmd);
+ ret = ublk_config_io_buf(ub, io, cmd, addr, NULL);
+ WARN_ON_ONCE(ret);
+ if (likely(ublk_get_data(ubq, io, req))) {
+ __ublk_prep_compl_io_cmd(io, req);
+ return UBLK_IO_RES_OK;
+ }
break;
default:
goto out;
}
+ ublk_prep_cancel(cmd, issue_flags, ubq, tag);
return -EIOCBQUEUED;
out:
- io_uring_cmd_done(cmd, ret, 0);
pr_devel("%s: complete: cmd op %d, tag %d ret %x io_flags %x\n",
- __func__, cmd_op, tag, ret, io->flags);
- return -EIOCBQUEUED;
+ __func__, cmd_op, tag, ret, io ? io->flags : 0);
+ return ret;
+}
+
+static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub,
+ u16 q_id, u16 tag, struct ublk_io *io, size_t offset)
+{
+ struct request *req;
+
+ /*
+ * can't use io->req in case of concurrent UBLK_IO_COMMIT_AND_FETCH_REQ,
+ * which would overwrite it with io->cmd
+ */
+ req = blk_mq_tag_to_rq(ub->tag_set.tags[q_id], tag);
+ if (!req)
+ return NULL;
+
+ if (!ublk_get_req_ref(io))
+ return NULL;
+
+ if (unlikely(!blk_mq_request_started(req) || req->tag != tag))
+ goto fail_put;
+
+ if (!ublk_rq_has_data(req))
+ goto fail_put;
+
+ if (offset > blk_rq_bytes(req))
+ goto fail_put;
+
+ return req;
+fail_put:
+ ublk_put_req_ref(io, req);
+ return NULL;
+}
+
+static void ublk_ch_uring_cmd_cb(struct io_tw_req tw_req, io_tw_token_t tw)
+{
+ unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
+ int ret = ublk_ch_uring_cmd_local(cmd, issue_flags);
+
+ if (ret != -EIOCBQUEUED)
+ io_uring_cmd_done(cmd, ret, issue_flags);
+}
+
+static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
+{
+ if (unlikely(issue_flags & IO_URING_F_CANCEL)) {
+ ublk_uring_cmd_cancel_fn(cmd, issue_flags);
+ return 0;
+ }
+
+ /* well-implemented server won't run into unlocked */
+ if (unlikely(issue_flags & IO_URING_F_UNLOCKED)) {
+ io_uring_cmd_complete_in_task(cmd, ublk_ch_uring_cmd_cb);
+ return -EIOCBQUEUED;
+ }
+
+ return ublk_ch_uring_cmd_local(cmd, issue_flags);
+}
+
+static inline bool ublk_check_ubuf_dir(const struct request *req,
+ int ubuf_dir)
+{
+ /* copy ubuf to request pages */
+ if ((req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN) &&
+ ubuf_dir == ITER_SOURCE)
+ return true;
+
+ /* copy request pages to ubuf */
+ if ((req_op(req) == REQ_OP_WRITE ||
+ req_op(req) == REQ_OP_ZONE_APPEND) &&
+ ubuf_dir == ITER_DEST)
+ return true;
+
+ return false;
+}
+
+static struct request *ublk_check_and_get_req(struct kiocb *iocb,
+ struct iov_iter *iter, size_t *off, int dir,
+ struct ublk_io **io)
+{
+ struct ublk_device *ub = iocb->ki_filp->private_data;
+ struct ublk_queue *ubq;
+ struct request *req;
+ size_t buf_off;
+ u16 tag, q_id;
+
+ if (!user_backed_iter(iter))
+ return ERR_PTR(-EACCES);
+
+ if (ub->dev_info.state == UBLK_S_DEV_DEAD)
+ return ERR_PTR(-EACCES);
+
+ tag = ublk_pos_to_tag(iocb->ki_pos);
+ q_id = ublk_pos_to_hwq(iocb->ki_pos);
+ buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
+
+ if (q_id >= ub->dev_info.nr_hw_queues)
+ return ERR_PTR(-EINVAL);
+
+ ubq = ublk_get_queue(ub, q_id);
+ if (!ublk_dev_support_user_copy(ub))
+ return ERR_PTR(-EACCES);
+
+ if (tag >= ub->dev_info.queue_depth)
+ return ERR_PTR(-EINVAL);
+
+ *io = &ubq->ios[tag];
+ req = __ublk_check_and_get_req(ub, q_id, tag, *io, buf_off);
+ if (!req)
+ return ERR_PTR(-EINVAL);
+
+ if (!ublk_check_ubuf_dir(req, dir))
+ goto fail;
+
+ *off = buf_off;
+ return req;
+fail:
+ ublk_put_req_ref(*io, req);
+ return ERR_PTR(-EACCES);
+}
+
+static ssize_t ublk_ch_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct request *req;
+ struct ublk_io *io;
+ size_t buf_off;
+ size_t ret;
+
+ req = ublk_check_and_get_req(iocb, to, &buf_off, ITER_DEST, &io);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = ublk_copy_user_pages(req, buf_off, to, ITER_DEST);
+ ublk_put_req_ref(io, req);
+
+ return ret;
+}
+
+static ssize_t ublk_ch_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct request *req;
+ struct ublk_io *io;
+ size_t buf_off;
+ size_t ret;
+
+ req = ublk_check_and_get_req(iocb, from, &buf_off, ITER_SOURCE, &io);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ ret = ublk_copy_user_pages(req, buf_off, from, ITER_SOURCE);
+ ublk_put_req_ref(io, req);
+
+ return ret;
}
static const struct file_operations ublk_ch_fops = {
.owner = THIS_MODULE,
.open = ublk_ch_open,
.release = ublk_ch_release,
- .llseek = no_llseek,
+ .read_iter = ublk_ch_read_iter,
+ .write_iter = ublk_ch_write_iter,
.uring_cmd = ublk_ch_uring_cmd,
.mmap = ublk_ch_mmap,
};
static void ublk_deinit_queue(struct ublk_device *ub, int q_id)
{
- int size = ublk_queue_cmd_buf_size(ub, q_id);
- struct ublk_queue *ubq = ublk_get_queue(ub, q_id);
+ struct ublk_queue *ubq = ub->queues[q_id];
+ int size, i;
+
+ if (!ubq)
+ return;
+
+ size = ublk_queue_cmd_buf_size(ub);
+
+ for (i = 0; i < ubq->q_depth; i++) {
+ struct ublk_io *io = &ubq->ios[i];
+ if (io->task)
+ put_task_struct(io->task);
+ WARN_ON_ONCE(refcount_read(&io->ref));
+ WARN_ON_ONCE(io->task_registered_buffers);
+ }
- if (ubq->ubq_daemon)
- put_task_struct(ubq->ubq_daemon);
if (ubq->io_cmd_buf)
free_pages((unsigned long)ubq->io_cmd_buf, get_order(size));
+
+ kvfree(ubq);
+ ub->queues[q_id] = NULL;
+}
+
+static int ublk_get_queue_numa_node(struct ublk_device *ub, int q_id)
+{
+ unsigned int cpu;
+
+ /* Find first CPU mapped to this queue */
+ for_each_possible_cpu(cpu) {
+ if (ub->tag_set.map[HCTX_TYPE_DEFAULT].mq_map[cpu] == q_id)
+ return cpu_to_node(cpu);
+ }
+
+ return NUMA_NO_NODE;
}
static int ublk_init_queue(struct ublk_device *ub, int q_id)
{
- struct ublk_queue *ubq = ublk_get_queue(ub, q_id);
+ int depth = ub->dev_info.queue_depth;
gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
- void *ptr;
+ struct ublk_queue *ubq;
+ struct page *page;
+ int numa_node;
int size;
+ /* Determine NUMA node based on queue's CPU affinity */
+ numa_node = ublk_get_queue_numa_node(ub, q_id);
+
+ /* Allocate queue structure on local NUMA node */
+ ubq = kvzalloc_node(struct_size(ubq, ios, depth), GFP_KERNEL,
+ numa_node);
+ if (!ubq)
+ return -ENOMEM;
+
+ spin_lock_init(&ubq->cancel_lock);
ubq->flags = ub->dev_info.flags;
ubq->q_id = q_id;
- ubq->q_depth = ub->dev_info.queue_depth;
- size = ublk_queue_cmd_buf_size(ub, q_id);
+ ubq->q_depth = depth;
+ size = ublk_queue_cmd_buf_size(ub);
- ptr = (void *) __get_free_pages(gfp_flags, get_order(size));
- if (!ptr)
+ /* Allocate I/O command buffer on local NUMA node */
+ page = alloc_pages_node(numa_node, gfp_flags, get_order(size));
+ if (!page) {
+ kvfree(ubq);
return -ENOMEM;
+ }
+ ubq->io_cmd_buf = page_address(page);
- ubq->io_cmd_buf = ptr;
+ ub->queues[q_id] = ubq;
ubq->dev = ub;
return 0;
}
static void ublk_deinit_queues(struct ublk_device *ub)
{
- int nr_queues = ub->dev_info.nr_hw_queues;
int i;
- if (!ub->__queues)
- return;
-
- for (i = 0; i < nr_queues; i++)
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
ublk_deinit_queue(ub, i);
- kfree(ub->__queues);
}
static int ublk_init_queues(struct ublk_device *ub)
{
- int nr_queues = ub->dev_info.nr_hw_queues;
- int depth = ub->dev_info.queue_depth;
- int ubq_size = sizeof(struct ublk_queue) + depth * sizeof(struct ublk_io);
- int i, ret = -ENOMEM;
+ int i, ret;
- ub->queue_size = ubq_size;
- ub->__queues = kcalloc(nr_queues, ubq_size, GFP_KERNEL);
- if (!ub->__queues)
- return ret;
-
- for (i = 0; i < nr_queues; i++) {
- if (ublk_init_queue(ub, i))
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
+ ret = ublk_init_queue(ub, i);
+ if (ret)
goto fail;
}
@@ -1223,7 +2731,8 @@ static int ublk_alloc_dev_number(struct ublk_device *ub, int idx)
if (err == -ENOSPC)
err = -EEXIST;
} else {
- err = idr_alloc(&ublk_index_idr, ub, 0, 0, GFP_NOWAIT);
+ err = idr_alloc(&ublk_index_idr, ub, 0, UBLK_MAX_UBLKS,
+ GFP_NOWAIT);
}
spin_unlock(&ublk_idr_lock);
@@ -1249,6 +2758,7 @@ static void ublk_cdev_rel(struct device *dev)
ublk_deinit_queues(ub);
ublk_free_dev_number(ub);
mutex_destroy(&ub->mutex);
+ mutex_destroy(&ub->cancel_mutex);
kfree(ub);
}
@@ -1260,7 +2770,7 @@ static int ublk_add_chdev(struct ublk_device *ub)
dev->parent = ublk_misc.this_device;
dev->devt = MKDEV(MAJOR(ublk_chr_devt), minor);
- dev->class = ublk_chr_class;
+ dev->class = &ublk_chr_class;
dev->release = ublk_cdev_rel;
device_initialize(dev);
@@ -1272,20 +2782,15 @@ static int ublk_add_chdev(struct ublk_device *ub)
ret = cdev_device_add(&ub->cdev, dev);
if (ret)
goto fail;
+
+ if (ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV)
+ unprivileged_ublks_added++;
return 0;
fail:
put_device(dev);
return ret;
}
-static void ublk_stop_work_fn(struct work_struct *work)
-{
- struct ublk_device *ub =
- container_of(work, struct ublk_device, stop_work);
-
- ublk_stop_dev(ub);
-}
-
/* align max io buffer size with PAGE_SIZE */
static void ublk_align_max_io_size(struct ublk_device *ub)
{
@@ -1301,18 +2806,21 @@ static int ublk_add_tag_set(struct ublk_device *ub)
ub->tag_set.nr_hw_queues = ub->dev_info.nr_hw_queues;
ub->tag_set.queue_depth = ub->dev_info.queue_depth;
ub->tag_set.numa_node = NUMA_NO_NODE;
- ub->tag_set.cmd_size = sizeof(struct ublk_rq_data);
- ub->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
ub->tag_set.driver_data = ub;
return blk_mq_alloc_tag_set(&ub->tag_set);
}
static void ublk_remove(struct ublk_device *ub)
{
+ bool unprivileged;
+
ublk_stop_dev(ub);
- cancel_work_sync(&ub->stop_work);
cdev_device_del(&ub->cdev, &ub->cdev_dev);
- put_device(&ub->cdev_dev);
+ unprivileged = ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV;
+ ublk_put_device(ub);
+
+ if (unprivileged)
+ unprivileged_ublks_added--;
}
static struct ublk_device *ublk_get_device_from_id(int idx)
@@ -1331,24 +2839,76 @@ static struct ublk_device *ublk_get_device_from_id(int idx)
return ub;
}
-static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
+static int ublk_ctrl_start_dev(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+ const struct ublk_param_basic *p = &ub->params.basic;
int ublksrv_pid = (int)header->data[0];
- struct ublk_device *ub;
+ struct queue_limits lim = {
+ .logical_block_size = 1 << p->logical_bs_shift,
+ .physical_block_size = 1 << p->physical_bs_shift,
+ .io_min = 1 << p->io_min_shift,
+ .io_opt = 1 << p->io_opt_shift,
+ .max_hw_sectors = p->max_sectors,
+ .chunk_sectors = p->chunk_sectors,
+ .virt_boundary_mask = p->virt_boundary_mask,
+ .max_segments = USHRT_MAX,
+ .max_segment_size = UINT_MAX,
+ .dma_alignment = 3,
+ };
struct gendisk *disk;
int ret = -EINVAL;
if (ublksrv_pid <= 0)
return -EINVAL;
-
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
+ if (!(ub->params.types & UBLK_PARAM_TYPE_BASIC))
return -EINVAL;
- wait_for_completion_interruptible(&ub->completion);
+ if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) {
+ const struct ublk_param_discard *pd = &ub->params.discard;
- schedule_delayed_work(&ub->monitor_work, UBLK_DAEMON_MONITOR_PERIOD);
+ lim.discard_alignment = pd->discard_alignment;
+ lim.discard_granularity = pd->discard_granularity;
+ lim.max_hw_discard_sectors = pd->max_discard_sectors;
+ lim.max_write_zeroes_sectors = pd->max_write_zeroes_sectors;
+ lim.max_discard_segments = pd->max_discard_segments;
+ }
+
+ if (ub->params.types & UBLK_PARAM_TYPE_ZONED) {
+ const struct ublk_param_zoned *p = &ub->params.zoned;
+
+ if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED))
+ return -EOPNOTSUPP;
+
+ lim.features |= BLK_FEAT_ZONED;
+ lim.max_active_zones = p->max_active_zones;
+ lim.max_open_zones = p->max_open_zones;
+ lim.max_hw_zone_append_sectors = p->max_zone_append_sectors;
+ }
+
+ if (ub->params.basic.attrs & UBLK_ATTR_VOLATILE_CACHE) {
+ lim.features |= BLK_FEAT_WRITE_CACHE;
+ if (ub->params.basic.attrs & UBLK_ATTR_FUA)
+ lim.features |= BLK_FEAT_FUA;
+ }
+
+ if (ub->params.basic.attrs & UBLK_ATTR_ROTATIONAL)
+ lim.features |= BLK_FEAT_ROTATIONAL;
+
+ if (ub->params.types & UBLK_PARAM_TYPE_DMA_ALIGN)
+ lim.dma_alignment = ub->params.dma.alignment;
+
+ if (ub->params.types & UBLK_PARAM_TYPE_SEGMENT) {
+ lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
+ lim.max_segment_size = ub->params.seg.max_segment_size;
+ lim.max_segments = ub->params.seg.max_segments;
+ }
+
+ if (wait_for_completion_interruptible(&ub->completion) != 0)
+ return -EINTR;
+
+ if (ub->ublksrv_tgid != ublksrv_pid)
+ return -EINVAL;
mutex_lock(&ub->mutex);
if (ub->dev_info.state == UBLK_S_DEV_LIVE ||
@@ -1357,7 +2917,7 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
goto out_unlock;
}
- disk = blk_mq_alloc_disk(&ub->tag_set, ub);
+ disk = blk_mq_alloc_disk(&ub->tag_set, &lim, NULL);
if (IS_ERR(disk)) {
ret = PTR_ERR(disk);
goto out_unlock;
@@ -1369,42 +2929,49 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
ub->dev_info.ublksrv_pid = ublksrv_pid;
ub->ub_disk = disk;
- ret = ublk_apply_params(ub);
- if (ret)
- goto out_put_disk;
+ ublk_apply_params(ub);
+
+ /* don't probe partitions if any daemon task is un-trusted */
+ if (ub->unprivileged_daemons)
+ set_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
+
+ ublk_get_device(ub);
+ ub->dev_info.state = UBLK_S_DEV_LIVE;
+
+ if (ublk_dev_is_zoned(ub)) {
+ ret = ublk_revalidate_disk_zones(ub);
+ if (ret)
+ goto out_put_cdev;
+ }
- get_device(&ub->cdev_dev);
ret = add_disk(disk);
+ if (ret)
+ goto out_put_cdev;
+
+ set_bit(UB_STATE_USED, &ub->state);
+
+out_put_cdev:
if (ret) {
- /*
- * Has to drop the reference since ->free_disk won't be
- * called in case of add_disk failure.
- */
+ ublk_detach_disk(ub);
ublk_put_device(ub);
- goto out_put_disk;
}
- set_bit(UB_STATE_USED, &ub->state);
- ub->dev_info.state = UBLK_S_DEV_LIVE;
-out_put_disk:
if (ret)
put_disk(disk);
out_unlock:
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
-static int ublk_ctrl_get_queue_affinity(struct io_uring_cmd *cmd)
+static int ublk_ctrl_get_queue_affinity(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
- struct ublk_device *ub;
cpumask_var_t cpumask;
unsigned long queue;
unsigned int retlen;
unsigned int i;
- int ret = -EINVAL;
-
+ int ret;
+
if (header->len * BITS_PER_BYTE < nr_cpu_ids)
return -EINVAL;
if (header->len & (sizeof(unsigned long)-1))
@@ -1412,17 +2979,12 @@ static int ublk_ctrl_get_queue_affinity(struct io_uring_cmd *cmd)
if (!header->addr)
return -EINVAL;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
queue = header->data[0];
if (queue >= ub->dev_info.nr_hw_queues)
- goto out_put_device;
+ return -EINVAL;
- ret = -ENOMEM;
if (!zalloc_cpumask_var(&cpumask, GFP_KERNEL))
- goto out_put_device;
+ return -ENOMEM;
for_each_possible_cpu(i) {
if (ub->tag_set.map[HCTX_TYPE_DEFAULT].mq_map[i] == queue)
@@ -1440,8 +3002,6 @@ static int ublk_ctrl_get_queue_affinity(struct io_uring_cmd *cmd)
ret = 0;
out_free_cpumask:
free_cpumask_var(cpumask);
-out_put_device:
- ublk_put_device(ub);
return ret;
}
@@ -1453,9 +3013,8 @@ static inline void ublk_dump_dev_info(struct ublksrv_ctrl_dev_info *info)
info->nr_hw_queues, info->queue_depth);
}
-static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
+static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
struct ublksrv_ctrl_dev_info info;
struct ublk_device *ub;
@@ -1468,27 +3027,94 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
__func__, header->queue_id);
return -EINVAL;
}
+
if (copy_from_user(&info, argp, sizeof(info)))
return -EFAULT;
- ublk_dump_dev_info(&info);
+
+ if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || !info.queue_depth ||
+ info.nr_hw_queues > UBLK_MAX_NR_QUEUES || !info.nr_hw_queues)
+ return -EINVAL;
+
+ if (capable(CAP_SYS_ADMIN))
+ info.flags &= ~UBLK_F_UNPRIVILEGED_DEV;
+ else if (!(info.flags & UBLK_F_UNPRIVILEGED_DEV))
+ return -EPERM;
+
+ /* forbid nonsense combinations of recovery flags */
+ switch (info.flags & UBLK_F_ALL_RECOVERY_FLAGS) {
+ case 0:
+ case UBLK_F_USER_RECOVERY:
+ case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE):
+ case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_FAIL_IO):
+ break;
+ default:
+ pr_warn("%s: invalid recovery flags %llx\n", __func__,
+ info.flags & UBLK_F_ALL_RECOVERY_FLAGS);
+ return -EINVAL;
+ }
+
+ if ((info.flags & UBLK_F_QUIESCE) && !(info.flags & UBLK_F_USER_RECOVERY)) {
+ pr_warn("UBLK_F_QUIESCE requires UBLK_F_USER_RECOVERY\n");
+ return -EINVAL;
+ }
+
+ /*
+ * unprivileged device can't be trusted, but RECOVERY and
+ * RECOVERY_REISSUE still may hang error handling, so can't
+ * support recovery features for unprivileged ublk now
+ *
+ * TODO: provide forward progress for RECOVERY handler, so that
+ * unprivileged device can benefit from it
+ */
+ if (info.flags & UBLK_F_UNPRIVILEGED_DEV) {
+ info.flags &= ~(UBLK_F_USER_RECOVERY_REISSUE |
+ UBLK_F_USER_RECOVERY);
+
+ /*
+ * For USER_COPY, we depends on userspace to fill request
+ * buffer by pwrite() to ublk char device, which can't be
+ * used for unprivileged device
+ *
+ * Same with zero copy or auto buffer register.
+ */
+ if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
+ UBLK_F_AUTO_BUF_REG))
+ return -EINVAL;
+ }
+
+ /* the created device is always owned by current user */
+ ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
+
if (header->dev_id != info.dev_id) {
pr_warn("%s: dev id not match %u %u\n",
__func__, header->dev_id, info.dev_id);
return -EINVAL;
}
+ if (header->dev_id != U32_MAX && header->dev_id >= UBLK_MAX_UBLKS) {
+ pr_warn("%s: dev id is too large. Max supported is %d\n",
+ __func__, UBLK_MAX_UBLKS - 1);
+ return -EINVAL;
+ }
+
+ ublk_dump_dev_info(&info);
+
ret = mutex_lock_killable(&ublk_ctl_mutex);
if (ret)
return ret;
+ ret = -EACCES;
+ if ((info.flags & UBLK_F_UNPRIVILEGED_DEV) &&
+ unprivileged_ublks_added >= unprivileged_ublks_max)
+ goto out_unlock;
+
ret = -ENOMEM;
- ub = kzalloc(sizeof(*ub), GFP_KERNEL);
+ ub = kzalloc(struct_size(ub, queues, info.nr_hw_queues), GFP_KERNEL);
if (!ub)
goto out_unlock;
mutex_init(&ub->mutex);
- spin_lock_init(&ub->mm_lock);
- INIT_WORK(&ub->stop_work, ublk_stop_work_fn);
- INIT_DELAYED_WORK(&ub->monitor_work, ublk_daemon_monitor_work);
+ spin_lock_init(&ub->lock);
+ mutex_init(&ub->cancel_mutex);
ret = ublk_alloc_dev_number(ub, header->dev_id);
if (ret < 0)
@@ -1507,24 +3133,43 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
*/
ub->dev_info.flags &= UBLK_F_ALL;
- /* We are not ready to support zero copy */
- ub->dev_info.flags &= ~UBLK_F_SUPPORT_ZERO_COPY;
+ ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE |
+ UBLK_F_URING_CMD_COMP_IN_TASK |
+ UBLK_F_PER_IO_DAEMON |
+ UBLK_F_BUF_REG_OFF_DAEMON;
+
+ /* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */
+ if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
+ UBLK_F_AUTO_BUF_REG))
+ ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
+
+ /*
+ * Zoned storage support requires reuse `ublksrv_io_cmd->addr` for
+ * returning write_append_lba, which is only allowed in case of
+ * user copy or zero copy
+ */
+ if (ublk_dev_is_zoned(ub) &&
+ (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) || !(ub->dev_info.flags &
+ (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY)))) {
+ ret = -EINVAL;
+ goto out_free_dev_number;
+ }
ub->dev_info.nr_hw_queues = min_t(unsigned int,
ub->dev_info.nr_hw_queues, nr_cpu_ids);
ublk_align_max_io_size(ub);
- ret = ublk_init_queues(ub);
+ ret = ublk_add_tag_set(ub);
if (ret)
goto out_free_dev_number;
- ret = ublk_add_tag_set(ub);
+ ret = ublk_init_queues(ub);
if (ret)
- goto out_deinit_queues;
+ goto out_free_tag_set;
ret = -EFAULT;
if (copy_to_user(argp, &ub->dev_info, sizeof(info)))
- goto out_free_tag_set;
+ goto out_deinit_queues;
/*
* Add the char dev so that ublksrv daemon can be setup.
@@ -1533,14 +3178,15 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
ret = ublk_add_chdev(ub);
goto out_unlock;
-out_free_tag_set:
- blk_mq_free_tag_set(&ub->tag_set);
out_deinit_queues:
ublk_deinit_queues(ub);
+out_free_tag_set:
+ blk_mq_free_tag_set(&ub->tag_set);
out_free_dev_number:
ublk_free_dev_number(ub);
out_free_ub:
mutex_destroy(&ub->mutex);
+ mutex_destroy(&ub->cancel_mutex);
kfree(ub);
out_unlock:
mutex_unlock(&ublk_ctl_mutex);
@@ -1558,87 +3204,95 @@ static inline bool ublk_idr_freed(int id)
return ptr == NULL;
}
-static int ublk_ctrl_del_dev(int idx)
+static int ublk_ctrl_del_dev(struct ublk_device **p_ub, bool wait)
{
- struct ublk_device *ub;
+ struct ublk_device *ub = *p_ub;
+ int idx = ub->ub_number;
int ret;
ret = mutex_lock_killable(&ublk_ctl_mutex);
if (ret)
return ret;
- ub = ublk_get_device_from_id(idx);
- if (ub) {
+ if (!test_bit(UB_STATE_DELETED, &ub->state)) {
ublk_remove(ub);
- ublk_put_device(ub);
- ret = 0;
- } else {
- ret = -ENODEV;
+ set_bit(UB_STATE_DELETED, &ub->state);
}
+ /* Mark the reference as consumed */
+ *p_ub = NULL;
+ ublk_put_device(ub);
+ mutex_unlock(&ublk_ctl_mutex);
+
/*
* Wait until the idr is removed, then it can be reused after
* DEL_DEV command is returned.
+ *
+ * If we returns because of user interrupt, future delete command
+ * may come:
+ *
+ * - the device number isn't freed, this device won't or needn't
+ * be deleted again, since UB_STATE_DELETED is set, and device
+ * will be released after the last reference is dropped
+ *
+ * - the device number is freed already, we will not find this
+ * device via ublk_get_device_from_id()
*/
- if (!ret)
- wait_event(ublk_idr_wq, ublk_idr_freed(idx));
- mutex_unlock(&ublk_ctl_mutex);
-
- return ret;
+ if (wait && wait_event_interruptible(ublk_idr_wq, ublk_idr_freed(idx)))
+ return -EINTR;
+ return 0;
}
static inline void ublk_ctrl_cmd_dump(struct io_uring_cmd *cmd)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+ const struct ublksrv_ctrl_cmd *header = io_uring_sqe_cmd(cmd->sqe);
pr_devel("%s: cmd_op %x, dev id %d qid %d data %llx buf %llx len %u\n",
__func__, cmd->cmd_op, header->dev_id, header->queue_id,
header->data[0], header->addr, header->len);
}
-static int ublk_ctrl_stop_dev(struct io_uring_cmd *cmd)
+static int ublk_ctrl_stop_dev(struct ublk_device *ub)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
- struct ublk_device *ub;
-
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
ublk_stop_dev(ub);
- cancel_work_sync(&ub->stop_work);
-
- ublk_put_device(ub);
return 0;
}
-static int ublk_ctrl_get_dev_info(struct io_uring_cmd *cmd)
+static int ublk_ctrl_get_dev_info(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
- struct ublk_device *ub;
- int ret = 0;
if (header->len < sizeof(struct ublksrv_ctrl_dev_info) || !header->addr)
return -EINVAL;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
if (copy_to_user(argp, &ub->dev_info, sizeof(ub->dev_info)))
- ret = -EFAULT;
- ublk_put_device(ub);
+ return -EFAULT;
- return ret;
+ return 0;
}
-static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
+/* TYPE_DEVT is readonly, so fill it up before returning to userspace */
+static void ublk_ctrl_fill_params_devt(struct ublk_device *ub)
+{
+ ub->params.devt.char_major = MAJOR(ub->cdev_dev.devt);
+ ub->params.devt.char_minor = MINOR(ub->cdev_dev.devt);
+
+ if (ub->ub_disk) {
+ ub->params.devt.disk_major = MAJOR(disk_devt(ub->ub_disk));
+ ub->params.devt.disk_minor = MINOR(disk_devt(ub->ub_disk));
+ } else {
+ ub->params.devt.disk_major = 0;
+ ub->params.devt.disk_minor = 0;
+ }
+ ub->params.types |= UBLK_PARAM_TYPE_DEVT;
+}
+
+static int ublk_ctrl_get_params(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
struct ublk_params_header ph;
- struct ublk_device *ub;
int ret;
if (header->len <= sizeof(ph) || !header->addr)
@@ -1653,27 +3307,22 @@ static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
if (ph.len > sizeof(struct ublk_params))
ph.len = sizeof(struct ublk_params);
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
mutex_lock(&ub->mutex);
+ ublk_ctrl_fill_params_devt(ub);
if (copy_to_user(argp, &ub->params, ph.len))
ret = -EFAULT;
else
ret = 0;
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
-static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
+static int ublk_ctrl_set_params(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
struct ublk_params_header ph;
- struct ublk_device *ub;
int ret = -EFAULT;
if (header->len <= sizeof(ph) || !header->addr)
@@ -1688,13 +3337,12 @@ static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
if (ph.len > sizeof(struct ublk_params))
ph.len = sizeof(struct ublk_params);
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
- /* parameters can only be changed when device isn't live */
mutex_lock(&ub->mutex);
- if (ub->dev_info.state == UBLK_S_DEV_LIVE) {
+ if (test_bit(UB_STATE_USED, &ub->state)) {
+ /*
+ * Parameters can only be changed when device hasn't
+ * been started yet
+ */
ret = -EACCES;
} else if (copy_from_user(&ub->params, argp, ph.len)) {
ret = -EFAULT;
@@ -1702,62 +3350,419 @@ static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
/* clear all we don't support yet */
ub->params.types &= UBLK_PARAM_TYPE_ALL;
ret = ublk_validate_params(ub);
+ if (ret)
+ ub->params.types = 0;
}
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
+static int ublk_ctrl_start_recovery(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&ub->mutex);
+ if (ublk_nosrv_should_stop_dev(ub))
+ goto out_unlock;
+ /*
+ * START_RECOVERY is only allowd after:
+ *
+ * (1) UB_STATE_OPEN is not set, which means the dying process is exited
+ * and related io_uring ctx is freed so file struct of /dev/ublkcX is
+ * released.
+ *
+ * and one of the following holds
+ *
+ * (2) UBLK_S_DEV_QUIESCED is set, which means the quiesce_work:
+ * (a)has quiesced request queue
+ * (b)has requeued every inflight rqs whose io_flags is ACTIVE
+ * (c)has requeued/aborted every inflight rqs whose io_flags is NOT ACTIVE
+ * (d)has completed/camceled all ioucmds owned by ther dying process
+ *
+ * (3) UBLK_S_DEV_FAIL_IO is set, which means the queue is not
+ * quiesced, but all I/O is being immediately errored
+ */
+ if (test_bit(UB_STATE_OPEN, &ub->state) || !ublk_dev_in_recoverable_state(ub)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ pr_devel("%s: start recovery for dev id %d.\n", __func__, header->dev_id);
+ init_completion(&ub->completion);
+ ret = 0;
+ out_unlock:
+ mutex_unlock(&ub->mutex);
+ return ret;
+}
+
+static int ublk_ctrl_end_recovery(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
+{
+ int ublksrv_pid = (int)header->data[0];
+ int ret = -EINVAL;
+
+ pr_devel("%s: Waiting for all FETCH_REQs, dev id %d...\n", __func__,
+ header->dev_id);
+
+ if (wait_for_completion_interruptible(&ub->completion))
+ return -EINTR;
+
+ pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__,
+ header->dev_id);
+
+ if (ub->ublksrv_tgid != ublksrv_pid)
+ return -EINVAL;
+
+ mutex_lock(&ub->mutex);
+ if (ublk_nosrv_should_stop_dev(ub))
+ goto out_unlock;
+
+ if (!ublk_dev_in_recoverable_state(ub)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ ub->dev_info.ublksrv_pid = ublksrv_pid;
+ ub->dev_info.state = UBLK_S_DEV_LIVE;
+ pr_devel("%s: new ublksrv_pid %d, dev id %d\n",
+ __func__, ublksrv_pid, header->dev_id);
+ blk_mq_kick_requeue_list(ub->ub_disk->queue);
+ ret = 0;
+ out_unlock:
+ mutex_unlock(&ub->mutex);
+ return ret;
+}
+
+static int ublk_ctrl_get_features(const struct ublksrv_ctrl_cmd *header)
+{
+ void __user *argp = (void __user *)(unsigned long)header->addr;
+ u64 features = UBLK_F_ALL;
+
+ if (header->len != UBLK_FEATURES_LEN || !header->addr)
+ return -EINVAL;
+
+ if (copy_to_user(argp, &features, UBLK_FEATURES_LEN))
+ return -EFAULT;
+
+ return 0;
+}
+
+static void ublk_ctrl_set_size(struct ublk_device *ub, const struct ublksrv_ctrl_cmd *header)
+{
+ struct ublk_param_basic *p = &ub->params.basic;
+ u64 new_size = header->data[0];
+
+ mutex_lock(&ub->mutex);
+ p->dev_sectors = new_size;
+ set_capacity_and_notify(ub->ub_disk, p->dev_sectors);
+ mutex_unlock(&ub->mutex);
+}
+
+struct count_busy {
+ const struct ublk_queue *ubq;
+ unsigned int nr_busy;
+};
+
+static bool ublk_count_busy_req(struct request *rq, void *data)
+{
+ struct count_busy *idle = data;
+
+ if (!blk_mq_request_started(rq) && rq->mq_hctx->driver_data == idle->ubq)
+ idle->nr_busy += 1;
+ return true;
+}
+
+/* uring_cmd is guaranteed to be active if the associated request is idle */
+static bool ubq_has_idle_io(const struct ublk_queue *ubq)
+{
+ struct count_busy data = {
+ .ubq = ubq,
+ };
+
+ blk_mq_tagset_busy_iter(&ubq->dev->tag_set, ublk_count_busy_req, &data);
+ return data.nr_busy < ubq->q_depth;
+}
+
+/* Wait until each hw queue has at least one idle IO */
+static int ublk_wait_for_idle_io(struct ublk_device *ub,
+ unsigned int timeout_ms)
+{
+ unsigned int elapsed = 0;
+ int ret;
+
+ while (elapsed < timeout_ms && !signal_pending(current)) {
+ unsigned int queues_cancelable = 0;
+ int i;
+
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
+ struct ublk_queue *ubq = ublk_get_queue(ub, i);
+
+ queues_cancelable += !!ubq_has_idle_io(ubq);
+ }
+
+ /*
+ * Each queue needs at least one active command for
+ * notifying ublk server
+ */
+ if (queues_cancelable == ub->dev_info.nr_hw_queues)
+ break;
+
+ msleep(UBLK_REQUEUE_DELAY_MS);
+ elapsed += UBLK_REQUEUE_DELAY_MS;
+ }
+
+ if (signal_pending(current))
+ ret = -EINTR;
+ else if (elapsed >= timeout_ms)
+ ret = -EBUSY;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int ublk_ctrl_quiesce_dev(struct ublk_device *ub,
+ const struct ublksrv_ctrl_cmd *header)
+{
+ /* zero means wait forever */
+ u64 timeout_ms = header->data[0];
+ struct gendisk *disk;
+ int ret = -ENODEV;
+
+ if (!(ub->dev_info.flags & UBLK_F_QUIESCE))
+ return -EOPNOTSUPP;
+
+ mutex_lock(&ub->mutex);
+ disk = ublk_get_disk(ub);
+ if (!disk)
+ goto unlock;
+ if (ub->dev_info.state == UBLK_S_DEV_DEAD)
+ goto put_disk;
+
+ ret = 0;
+ /* already in expected state */
+ if (ub->dev_info.state != UBLK_S_DEV_LIVE)
+ goto put_disk;
+
+ /* Mark the device as canceling */
+ mutex_lock(&ub->cancel_mutex);
+ blk_mq_quiesce_queue(disk->queue);
+ ublk_set_canceling(ub, true);
+ blk_mq_unquiesce_queue(disk->queue);
+ mutex_unlock(&ub->cancel_mutex);
+
+ if (!timeout_ms)
+ timeout_ms = UINT_MAX;
+ ret = ublk_wait_for_idle_io(ub, timeout_ms);
+
+put_disk:
+ ublk_put_disk(disk);
+unlock:
+ mutex_unlock(&ub->mutex);
+
+ /* Cancel pending uring_cmd */
+ if (!ret)
+ ublk_cancel_dev(ub);
+ return ret;
+}
+
+/*
+ * All control commands are sent via /dev/ublk-control, so we have to check
+ * the destination device's permission
+ */
+static int ublk_char_dev_permission(struct ublk_device *ub,
+ const char *dev_path, int mask)
+{
+ int err;
+ struct path path;
+ struct kstat stat;
+
+ err = kern_path(dev_path, LOOKUP_FOLLOW, &path);
+ if (err)
+ return err;
+
+ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
+ if (err)
+ goto exit;
+
+ err = -EPERM;
+ if (stat.rdev != ub->cdev_dev.devt || !S_ISCHR(stat.mode))
+ goto exit;
+
+ err = inode_permission(&nop_mnt_idmap,
+ d_backing_inode(path.dentry), mask);
+exit:
+ path_put(&path);
+ return err;
+}
+
+static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
+{
+ struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)io_uring_sqe_cmd(cmd->sqe);
+ bool unprivileged = ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV;
+ void __user *argp = (void __user *)(unsigned long)header->addr;
+ char *dev_path = NULL;
+ int ret = 0;
+ int mask;
+
+ if (!unprivileged) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ /*
+ * The new added command of UBLK_CMD_GET_DEV_INFO2 includes
+ * char_dev_path in payload too, since userspace may not
+ * know if the specified device is created as unprivileged
+ * mode.
+ */
+ if (_IOC_NR(cmd->cmd_op) != UBLK_CMD_GET_DEV_INFO2)
+ return 0;
+ }
+
+ /*
+ * User has to provide the char device path for unprivileged ublk
+ *
+ * header->addr always points to the dev path buffer, and
+ * header->dev_path_len records length of dev path buffer.
+ */
+ if (!header->dev_path_len || header->dev_path_len > PATH_MAX)
+ return -EINVAL;
+
+ if (header->len < header->dev_path_len)
+ return -EINVAL;
+
+ dev_path = memdup_user_nul(argp, header->dev_path_len);
+ if (IS_ERR(dev_path))
+ return PTR_ERR(dev_path);
+
+ ret = -EINVAL;
+ switch (_IOC_NR(cmd->cmd_op)) {
+ case UBLK_CMD_GET_DEV_INFO:
+ case UBLK_CMD_GET_DEV_INFO2:
+ case UBLK_CMD_GET_QUEUE_AFFINITY:
+ case UBLK_CMD_GET_PARAMS:
+ case (_IOC_NR(UBLK_U_CMD_GET_FEATURES)):
+ mask = MAY_READ;
+ break;
+ case UBLK_CMD_START_DEV:
+ case UBLK_CMD_STOP_DEV:
+ case UBLK_CMD_ADD_DEV:
+ case UBLK_CMD_DEL_DEV:
+ case UBLK_CMD_SET_PARAMS:
+ case UBLK_CMD_START_USER_RECOVERY:
+ case UBLK_CMD_END_USER_RECOVERY:
+ case UBLK_CMD_UPDATE_SIZE:
+ case UBLK_CMD_QUIESCE_DEV:
+ mask = MAY_READ | MAY_WRITE;
+ break;
+ default:
+ goto exit;
+ }
+
+ ret = ublk_char_dev_permission(ub, dev_path, mask);
+ if (!ret) {
+ header->len -= header->dev_path_len;
+ header->addr += header->dev_path_len;
+ }
+ pr_devel("%s: dev id %d cmd_op %x uid %d gid %d path %s ret %d\n",
+ __func__, ub->ub_number, cmd->cmd_op,
+ ub->dev_info.owner_uid, ub->dev_info.owner_gid,
+ dev_path, ret);
+exit:
+ kfree(dev_path);
+ return ret;
+}
+
static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
unsigned int issue_flags)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+ const struct ublksrv_ctrl_cmd *header = io_uring_sqe_cmd(cmd->sqe);
+ struct ublk_device *ub = NULL;
+ u32 cmd_op = cmd->cmd_op;
int ret = -EINVAL;
+ if (issue_flags & IO_URING_F_NONBLOCK)
+ return -EAGAIN;
+
ublk_ctrl_cmd_dump(cmd);
if (!(issue_flags & IO_URING_F_SQE128))
goto out;
- ret = -EPERM;
- if (!capable(CAP_SYS_ADMIN))
+ ret = ublk_check_cmd_op(cmd_op);
+ if (ret)
goto out;
- ret = -ENODEV;
- switch (cmd->cmd_op) {
+ if (cmd_op == UBLK_U_CMD_GET_FEATURES) {
+ ret = ublk_ctrl_get_features(header);
+ goto out;
+ }
+
+ if (_IOC_NR(cmd_op) != UBLK_CMD_ADD_DEV) {
+ ret = -ENODEV;
+ ub = ublk_get_device_from_id(header->dev_id);
+ if (!ub)
+ goto out;
+
+ ret = ublk_ctrl_uring_cmd_permission(ub, cmd);
+ if (ret)
+ goto put_dev;
+ }
+
+ switch (_IOC_NR(cmd_op)) {
case UBLK_CMD_START_DEV:
- ret = ublk_ctrl_start_dev(cmd);
+ ret = ublk_ctrl_start_dev(ub, header);
break;
case UBLK_CMD_STOP_DEV:
- ret = ublk_ctrl_stop_dev(cmd);
+ ret = ublk_ctrl_stop_dev(ub);
break;
case UBLK_CMD_GET_DEV_INFO:
- ret = ublk_ctrl_get_dev_info(cmd);
+ case UBLK_CMD_GET_DEV_INFO2:
+ ret = ublk_ctrl_get_dev_info(ub, header);
break;
case UBLK_CMD_ADD_DEV:
- ret = ublk_ctrl_add_dev(cmd);
+ ret = ublk_ctrl_add_dev(header);
break;
case UBLK_CMD_DEL_DEV:
- ret = ublk_ctrl_del_dev(header->dev_id);
+ ret = ublk_ctrl_del_dev(&ub, true);
+ break;
+ case UBLK_CMD_DEL_DEV_ASYNC:
+ ret = ublk_ctrl_del_dev(&ub, false);
break;
case UBLK_CMD_GET_QUEUE_AFFINITY:
- ret = ublk_ctrl_get_queue_affinity(cmd);
+ ret = ublk_ctrl_get_queue_affinity(ub, header);
break;
case UBLK_CMD_GET_PARAMS:
- ret = ublk_ctrl_get_params(cmd);
+ ret = ublk_ctrl_get_params(ub, header);
break;
case UBLK_CMD_SET_PARAMS:
- ret = ublk_ctrl_set_params(cmd);
+ ret = ublk_ctrl_set_params(ub, header);
+ break;
+ case UBLK_CMD_START_USER_RECOVERY:
+ ret = ublk_ctrl_start_recovery(ub, header);
+ break;
+ case UBLK_CMD_END_USER_RECOVERY:
+ ret = ublk_ctrl_end_recovery(ub, header);
+ break;
+ case UBLK_CMD_UPDATE_SIZE:
+ ublk_ctrl_set_size(ub, header);
+ ret = 0;
+ break;
+ case UBLK_CMD_QUIESCE_DEV:
+ ret = ublk_ctrl_quiesce_dev(ub, header);
break;
default:
+ ret = -EOPNOTSUPP;
break;
}
+
+ put_dev:
+ if (ub)
+ ublk_put_device(ub);
out:
- io_uring_cmd_done(cmd, ret, 0);
pr_devel("%s: cmd done ret %d cmd_op %x, dev id %d qid %d\n",
__func__, ret, cmd->cmd_op, header->dev_id, header->queue_id);
- return -EIOCBQUEUED;
+ return ret;
}
static const struct file_operations ublk_ctl_fops = {
@@ -1777,6 +3782,10 @@ static int __init ublk_init(void)
{
int ret;
+ BUILD_BUG_ON((u64)UBLKSRV_IO_BUF_OFFSET +
+ UBLKSRV_IO_BUF_TOTAL_SIZE < UBLKSRV_IO_BUF_OFFSET);
+ BUILD_BUG_ON(sizeof(struct ublk_auto_buf_reg) != 8);
+
init_waitqueue_head(&ublk_idr_wq);
ret = misc_register(&ublk_misc);
@@ -1787,11 +3796,10 @@ static int __init ublk_init(void)
if (ret)
goto unregister_mis;
- ublk_chr_class = class_create(THIS_MODULE, "ublk-char");
- if (IS_ERR(ublk_chr_class)) {
- ret = PTR_ERR(ublk_chr_class);
+ ret = class_register(&ublk_chr_class);
+ if (ret)
goto free_chrdev_region;
- }
+
return 0;
free_chrdev_region:
@@ -1806,13 +3814,12 @@ static void __exit ublk_exit(void)
struct ublk_device *ub;
int id;
- class_destroy(ublk_chr_class);
-
- misc_deregister(&ublk_misc);
-
idr_for_each_entry(&ublk_index_idr, ub, id)
ublk_remove(ub);
+ class_unregister(&ublk_chr_class);
+ misc_deregister(&ublk_misc);
+
idr_destroy(&ublk_index_idr);
unregister_chrdev_region(ublk_chr_devt, UBLK_MINORS);
}
@@ -1820,5 +3827,27 @@ static void __exit ublk_exit(void)
module_init(ublk_init);
module_exit(ublk_exit);
+static int ublk_set_max_unprivileged_ublks(const char *buf,
+ const struct kernel_param *kp)
+{
+ return param_set_uint_minmax(buf, kp, 0, UBLK_MAX_UBLKS);
+}
+
+static int ublk_get_max_unprivileged_ublks(char *buf,
+ const struct kernel_param *kp)
+{
+ return sysfs_emit(buf, "%u\n", unprivileged_ublks_max);
+}
+
+static const struct kernel_param_ops ublk_max_unprivileged_ublks_ops = {
+ .set = ublk_set_max_unprivileged_ublks,
+ .get = ublk_get_max_unprivileged_ublks,
+};
+
+module_param_cb(ublks_max, &ublk_max_unprivileged_ublks_ops,
+ &unprivileged_ublks_max, 0644);
+MODULE_PARM_DESC(ublks_max, "max number of unprivileged ublk devices allowed to add(default: 64)");
+
MODULE_AUTHOR("Ming Lei <ming.lei@redhat.com>");
+MODULE_DESCRIPTION("Userspace block device");
MODULE_LICENSE("GPL");
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 30255fcaf181..357434bdae99 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -13,8 +13,8 @@
#include <linux/string_helpers.h>
#include <linux/idr.h>
#include <linux/blk-mq.h>
-#include <linux/blk-mq-virtio.h>
#include <linux/numa.h>
+#include <linux/vmalloc.h>
#include <uapi/linux/virtio_ring.h>
#define PART_BITS 4
@@ -80,22 +80,49 @@ struct virtio_blk {
int num_vqs;
int io_queues[HCTX_MAX_TYPES];
struct virtio_blk_vq *vqs;
+
+ /* For zoned device */
+ unsigned int zone_sectors;
};
struct virtblk_req {
+ /* Out header */
struct virtio_blk_outhdr out_hdr;
- u8 status;
+
+ /* In header */
+ union {
+ u8 status;
+
+ /*
+ * The zone append command has an extended in header.
+ * The status field in zone_append_in_hdr must always
+ * be the last byte.
+ */
+ struct {
+ __virtio64 sector;
+ u8 status;
+ } zone_append;
+ } in_hdr;
+
+ size_t in_hdr_len;
+
struct sg_table sg_table;
struct scatterlist sg[];
};
-static inline blk_status_t virtblk_result(struct virtblk_req *vbr)
+static inline blk_status_t virtblk_result(u8 status)
{
- switch (vbr->status) {
+ switch (status) {
case VIRTIO_BLK_S_OK:
return BLK_STS_OK;
case VIRTIO_BLK_S_UNSUPP:
return BLK_STS_NOTSUPP;
+ case VIRTIO_BLK_S_ZONE_OPEN_RESOURCE:
+ return BLK_STS_ZONE_OPEN_RESOURCE;
+ case VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE:
+ return BLK_STS_ZONE_ACTIVE_RESOURCE;
+ case VIRTIO_BLK_S_IOERR:
+ case VIRTIO_BLK_S_ZONE_UNALIGNED_WP:
default:
return BLK_STS_IOERR;
}
@@ -111,11 +138,11 @@ static inline struct virtio_blk_vq *get_virtio_blk_vq(struct blk_mq_hw_ctx *hctx
static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr)
{
- struct scatterlist hdr, status, *sgs[3];
+ struct scatterlist out_hdr, in_hdr, *sgs[3];
unsigned int num_out = 0, num_in = 0;
- sg_init_one(&hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
- sgs[num_out++] = &hdr;
+ sg_init_one(&out_hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
+ sgs[num_out++] = &out_hdr;
if (vbr->sg_table.nents) {
if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
@@ -124,13 +151,13 @@ static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr)
sgs[num_out + num_in++] = vbr->sg_table.sgl;
}
- sg_init_one(&status, &vbr->status, sizeof(vbr->status));
- sgs[num_out + num_in++] = &status;
+ sg_init_one(&in_hdr, &vbr->in_hdr.status, vbr->in_hdr_len);
+ sgs[num_out + num_in++] = &in_hdr;
return virtqueue_add_sgs(vq, sgs, num_out, num_in, vbr, GFP_ATOMIC);
}
-static int virtblk_setup_discard_write_zeroes(struct request *req, bool unmap)
+static int virtblk_setup_discard_write_zeroes_erase(struct request *req, bool unmap)
{
unsigned short segments = blk_rq_nr_discard_segments(req);
unsigned short n = 0;
@@ -170,9 +197,7 @@ static int virtblk_setup_discard_write_zeroes(struct request *req, bool unmap)
WARN_ON_ONCE(n != segments);
- req->special_vec.bv_page = virt_to_page(range);
- req->special_vec.bv_offset = offset_in_page(range);
- req->special_vec.bv_len = sizeof(*range) * segments;
+ bvec_set_virt(&req->special_vec, range, sizeof(*range) * segments);
req->rq_flags |= RQF_SPECIAL_PAYLOAD;
return 0;
@@ -201,7 +226,7 @@ static int virtblk_map_data(struct blk_mq_hw_ctx *hctx, struct request *req,
if (unlikely(err))
return -ENOMEM;
- return blk_rq_map_sg(hctx->queue, req, vbr->sg_table.sgl);
+ return blk_rq_map_sg(req, vbr->sg_table.sgl);
}
static void virtblk_cleanup_cmd(struct request *req)
@@ -214,21 +239,25 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
struct request *req,
struct virtblk_req *vbr)
{
+ size_t in_hdr_len = sizeof(vbr->in_hdr.status);
bool unmap = false;
u32 type;
+ u64 sector = 0;
- vbr->out_hdr.sector = 0;
+ if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) && op_is_zone_mgmt(req_op(req)))
+ return BLK_STS_NOTSUPP;
+
+ /* Set fields for all request types */
+ vbr->out_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
switch (req_op(req)) {
case REQ_OP_READ:
type = VIRTIO_BLK_T_IN;
- vbr->out_hdr.sector = cpu_to_virtio64(vdev,
- blk_rq_pos(req));
+ sector = blk_rq_pos(req);
break;
case REQ_OP_WRITE:
type = VIRTIO_BLK_T_OUT;
- vbr->out_hdr.sector = cpu_to_virtio64(vdev,
- blk_rq_pos(req));
+ sector = blk_rq_pos(req);
break;
case REQ_OP_FLUSH:
type = VIRTIO_BLK_T_FLUSH;
@@ -240,32 +269,82 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
type = VIRTIO_BLK_T_WRITE_ZEROES;
unmap = !(req->cmd_flags & REQ_NOUNMAP);
break;
- case REQ_OP_DRV_IN:
- type = VIRTIO_BLK_T_GET_ID;
+ case REQ_OP_SECURE_ERASE:
+ type = VIRTIO_BLK_T_SECURE_ERASE;
+ break;
+ case REQ_OP_ZONE_OPEN:
+ type = VIRTIO_BLK_T_ZONE_OPEN;
+ sector = blk_rq_pos(req);
break;
+ case REQ_OP_ZONE_CLOSE:
+ type = VIRTIO_BLK_T_ZONE_CLOSE;
+ sector = blk_rq_pos(req);
+ break;
+ case REQ_OP_ZONE_FINISH:
+ type = VIRTIO_BLK_T_ZONE_FINISH;
+ sector = blk_rq_pos(req);
+ break;
+ case REQ_OP_ZONE_APPEND:
+ type = VIRTIO_BLK_T_ZONE_APPEND;
+ sector = blk_rq_pos(req);
+ in_hdr_len = sizeof(vbr->in_hdr.zone_append);
+ break;
+ case REQ_OP_ZONE_RESET:
+ type = VIRTIO_BLK_T_ZONE_RESET;
+ sector = blk_rq_pos(req);
+ break;
+ case REQ_OP_ZONE_RESET_ALL:
+ type = VIRTIO_BLK_T_ZONE_RESET_ALL;
+ break;
+ case REQ_OP_DRV_IN:
+ /*
+ * Out header has already been prepared by the caller (virtblk_get_id()
+ * or virtblk_submit_zone_report()), nothing to do here.
+ */
+ return 0;
default:
WARN_ON_ONCE(1);
return BLK_STS_IOERR;
}
+ /* Set fields for non-REQ_OP_DRV_IN request types */
+ vbr->in_hdr_len = in_hdr_len;
vbr->out_hdr.type = cpu_to_virtio32(vdev, type);
- vbr->out_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
+ vbr->out_hdr.sector = cpu_to_virtio64(vdev, sector);
- if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES) {
- if (virtblk_setup_discard_write_zeroes(req, unmap))
+ if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES ||
+ type == VIRTIO_BLK_T_SECURE_ERASE) {
+ if (virtblk_setup_discard_write_zeroes_erase(req, unmap))
return BLK_STS_RESOURCE;
}
return 0;
}
+/*
+ * The status byte is always the last byte of the virtblk request
+ * in-header. This helper fetches its value for all in-header formats
+ * that are currently defined.
+ */
+static inline u8 virtblk_vbr_status(struct virtblk_req *vbr)
+{
+ return *((u8 *)&vbr->in_hdr + vbr->in_hdr_len - 1);
+}
+
static inline void virtblk_request_done(struct request *req)
{
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
+ blk_status_t status = virtblk_result(virtblk_vbr_status(vbr));
+ struct virtio_blk *vblk = req->mq_hctx->queue->queuedata;
virtblk_unmap_data(req, vbr);
virtblk_cleanup_cmd(req);
- blk_mq_end_request(req, virtblk_result(vbr));
+
+ if (req_op(req) == REQ_OP_ZONE_APPEND)
+ req->__sector = virtio64_to_cpu(vblk->vdev,
+ vbr->in_hdr.zone_append.sector);
+
+ blk_mq_end_request(req, status);
}
static void virtblk_done(struct virtqueue *vq)
@@ -287,8 +366,6 @@ static void virtblk_done(struct virtqueue *vq)
blk_mq_complete_request(req);
req_done = true;
}
- if (unlikely(virtqueue_is_broken(vq)))
- break;
} while (!virtqueue_enable_cb(vq));
/* In case queue is stopped waiting for more buffers. */
@@ -311,24 +388,37 @@ static void virtio_commit_rqs(struct blk_mq_hw_ctx *hctx)
virtqueue_notify(vq->vq);
}
+static blk_status_t virtblk_fail_to_queue(struct request *req, int rc)
+{
+ virtblk_cleanup_cmd(req);
+ switch (rc) {
+ case -ENOSPC:
+ return BLK_STS_DEV_RESOURCE;
+ case -ENOMEM:
+ return BLK_STS_RESOURCE;
+ default:
+ return BLK_STS_IOERR;
+ }
+}
+
static blk_status_t virtblk_prep_rq(struct blk_mq_hw_ctx *hctx,
struct virtio_blk *vblk,
struct request *req,
struct virtblk_req *vbr)
{
blk_status_t status;
+ int num;
status = virtblk_setup_cmd(vblk->vdev, req, vbr);
if (unlikely(status))
return status;
- blk_mq_start_request(req);
+ num = virtblk_map_data(hctx, req, vbr);
+ if (unlikely(num < 0))
+ return virtblk_fail_to_queue(req, -ENOMEM);
+ vbr->sg_table.nents = num;
- vbr->sg_table.nents = virtblk_map_data(hctx, req, vbr);
- if (unlikely(vbr->sg_table.nents < 0)) {
- virtblk_cleanup_cmd(req);
- return BLK_STS_RESOURCE;
- }
+ blk_mq_start_request(req);
return BLK_STS_OK;
}
@@ -360,15 +450,7 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_stop_hw_queue(hctx);
spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
virtblk_unmap_data(req, vbr);
- virtblk_cleanup_cmd(req);
- switch (err) {
- case -ENOSPC:
- return BLK_STS_DEV_RESOURCE;
- case -ENOMEM:
- return BLK_STS_RESOURCE;
- default:
- return BLK_STS_IOERR;
- }
+ return virtblk_fail_to_queue(req, err);
}
if (bd->last && virtqueue_kick_prepare(vblk->vqs[qid].vq))
@@ -385,70 +467,338 @@ static bool virtblk_prep_rq_batch(struct request *req)
struct virtio_blk *vblk = req->mq_hctx->queue->queuedata;
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
- req->mq_hctx->tags->rqs[req->tag] = req;
-
return virtblk_prep_rq(req->mq_hctx, vblk, req, vbr) == BLK_STS_OK;
}
-static bool virtblk_add_req_batch(struct virtio_blk_vq *vq,
- struct request **rqlist,
- struct request **requeue_list)
+static void virtblk_add_req_batch(struct virtio_blk_vq *vq,
+ struct rq_list *rqlist)
{
+ struct request *req;
unsigned long flags;
- int err;
bool kick;
spin_lock_irqsave(&vq->lock, flags);
- while (!rq_list_empty(*rqlist)) {
- struct request *req = rq_list_pop(rqlist);
+ while ((req = rq_list_pop(rqlist))) {
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
+ int err;
err = virtblk_add_req(vq->vq, vbr);
if (err) {
virtblk_unmap_data(req, vbr);
virtblk_cleanup_cmd(req);
- rq_list_add(requeue_list, req);
+ blk_mq_requeue_request(req, true);
}
}
kick = virtqueue_kick_prepare(vq->vq);
spin_unlock_irqrestore(&vq->lock, flags);
- return kick;
+ if (kick)
+ virtqueue_notify(vq->vq);
}
-static void virtio_queue_rqs(struct request **rqlist)
+static void virtio_queue_rqs(struct rq_list *rqlist)
{
- struct request *req, *next, *prev = NULL;
- struct request *requeue_list = NULL;
-
- rq_list_for_each_safe(rqlist, req, next) {
- struct virtio_blk_vq *vq = get_virtio_blk_vq(req->mq_hctx);
- bool kick;
-
- if (!virtblk_prep_rq_batch(req)) {
- rq_list_move(rqlist, &requeue_list, req, prev);
- req = prev;
- if (!req)
- continue;
- }
+ struct rq_list submit_list = { };
+ struct rq_list requeue_list = { };
+ struct virtio_blk_vq *vq = NULL;
+ struct request *req;
+
+ while ((req = rq_list_pop(rqlist))) {
+ struct virtio_blk_vq *this_vq = get_virtio_blk_vq(req->mq_hctx);
- if (!next || req->mq_hctx != next->mq_hctx) {
- req->rq_next = NULL;
- kick = virtblk_add_req_batch(vq, rqlist, &requeue_list);
- if (kick)
- virtqueue_notify(vq->vq);
+ if (vq && vq != this_vq)
+ virtblk_add_req_batch(vq, &submit_list);
+ vq = this_vq;
- *rqlist = next;
- prev = NULL;
- } else
- prev = req;
+ if (virtblk_prep_rq_batch(req))
+ rq_list_add_tail(&submit_list, req);
+ else
+ rq_list_add_tail(&requeue_list, req);
}
+ if (vq)
+ virtblk_add_req_batch(vq, &submit_list);
*rqlist = requeue_list;
}
+#ifdef CONFIG_BLK_DEV_ZONED
+static void *virtblk_alloc_report_buffer(struct virtio_blk *vblk,
+ unsigned int nr_zones,
+ size_t *buflen)
+{
+ struct request_queue *q = vblk->disk->queue;
+ size_t bufsize;
+ void *buf;
+
+ nr_zones = min_t(unsigned int, nr_zones,
+ get_capacity(vblk->disk) >> ilog2(vblk->zone_sectors));
+
+ bufsize = sizeof(struct virtio_blk_zone_report) +
+ nr_zones * sizeof(struct virtio_blk_zone_descriptor);
+ bufsize = min_t(size_t, bufsize,
+ queue_max_hw_sectors(q) << SECTOR_SHIFT);
+ bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT);
+
+ while (bufsize >= sizeof(struct virtio_blk_zone_report)) {
+ buf = __vmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY);
+ if (buf) {
+ *buflen = bufsize;
+ return buf;
+ }
+ bufsize >>= 1;
+ }
+
+ return NULL;
+}
+
+static int virtblk_submit_zone_report(struct virtio_blk *vblk,
+ char *report_buf, size_t report_len,
+ sector_t sector)
+{
+ struct request_queue *q = vblk->disk->queue;
+ struct request *req;
+ struct virtblk_req *vbr;
+ int err;
+
+ req = blk_mq_alloc_request(q, REQ_OP_DRV_IN, 0);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ vbr = blk_mq_rq_to_pdu(req);
+ vbr->in_hdr_len = sizeof(vbr->in_hdr.status);
+ vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_REPORT);
+ vbr->out_hdr.sector = cpu_to_virtio64(vblk->vdev, sector);
+
+ err = blk_rq_map_kern(req, report_buf, report_len, GFP_KERNEL);
+ if (err)
+ goto out;
+
+ blk_execute_rq(req, false);
+ err = blk_status_to_errno(virtblk_result(vbr->in_hdr.status));
+out:
+ blk_mq_free_request(req);
+ return err;
+}
+
+static int virtblk_parse_zone(struct virtio_blk *vblk,
+ struct virtio_blk_zone_descriptor *entry,
+ unsigned int idx,
+ struct blk_report_zones_args *args)
+{
+ struct blk_zone zone = { };
+
+ zone.start = virtio64_to_cpu(vblk->vdev, entry->z_start);
+ if (zone.start + vblk->zone_sectors <= get_capacity(vblk->disk))
+ zone.len = vblk->zone_sectors;
+ else
+ zone.len = get_capacity(vblk->disk) - zone.start;
+ zone.capacity = virtio64_to_cpu(vblk->vdev, entry->z_cap);
+ zone.wp = virtio64_to_cpu(vblk->vdev, entry->z_wp);
+
+ switch (entry->z_type) {
+ case VIRTIO_BLK_ZT_SWR:
+ zone.type = BLK_ZONE_TYPE_SEQWRITE_REQ;
+ break;
+ case VIRTIO_BLK_ZT_SWP:
+ zone.type = BLK_ZONE_TYPE_SEQWRITE_PREF;
+ break;
+ case VIRTIO_BLK_ZT_CONV:
+ zone.type = BLK_ZONE_TYPE_CONVENTIONAL;
+ break;
+ default:
+ dev_err(&vblk->vdev->dev, "zone %llu: invalid type %#x\n",
+ zone.start, entry->z_type);
+ return -EIO;
+ }
+
+ switch (entry->z_state) {
+ case VIRTIO_BLK_ZS_EMPTY:
+ zone.cond = BLK_ZONE_COND_EMPTY;
+ break;
+ case VIRTIO_BLK_ZS_CLOSED:
+ zone.cond = BLK_ZONE_COND_CLOSED;
+ break;
+ case VIRTIO_BLK_ZS_FULL:
+ zone.cond = BLK_ZONE_COND_FULL;
+ zone.wp = zone.start + zone.len;
+ break;
+ case VIRTIO_BLK_ZS_EOPEN:
+ zone.cond = BLK_ZONE_COND_EXP_OPEN;
+ break;
+ case VIRTIO_BLK_ZS_IOPEN:
+ zone.cond = BLK_ZONE_COND_IMP_OPEN;
+ break;
+ case VIRTIO_BLK_ZS_NOT_WP:
+ zone.cond = BLK_ZONE_COND_NOT_WP;
+ break;
+ case VIRTIO_BLK_ZS_RDONLY:
+ zone.cond = BLK_ZONE_COND_READONLY;
+ zone.wp = ULONG_MAX;
+ break;
+ case VIRTIO_BLK_ZS_OFFLINE:
+ zone.cond = BLK_ZONE_COND_OFFLINE;
+ zone.wp = ULONG_MAX;
+ break;
+ default:
+ dev_err(&vblk->vdev->dev, "zone %llu: invalid condition %#x\n",
+ zone.start, entry->z_state);
+ return -EIO;
+ }
+
+ /*
+ * The callback below checks the validity of the reported
+ * entry data, no need to further validate it here.
+ */
+ return disk_report_zone(vblk->disk, &zone, idx, args);
+}
+
+static int virtblk_report_zones(struct gendisk *disk, sector_t sector,
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args)
+{
+ struct virtio_blk *vblk = disk->private_data;
+ struct virtio_blk_zone_report *report;
+ unsigned long long nz, i;
+ size_t buflen;
+ unsigned int zone_idx = 0;
+ int ret;
+
+ if (WARN_ON_ONCE(!vblk->zone_sectors))
+ return -EOPNOTSUPP;
+
+ report = virtblk_alloc_report_buffer(vblk, nr_zones, &buflen);
+ if (!report)
+ return -ENOMEM;
+
+ mutex_lock(&vblk->vdev_mutex);
+
+ if (!vblk->vdev) {
+ ret = -ENXIO;
+ goto fail_report;
+ }
+
+ while (zone_idx < nr_zones && sector < get_capacity(vblk->disk)) {
+ memset(report, 0, buflen);
+
+ ret = virtblk_submit_zone_report(vblk, (char *)report,
+ buflen, sector);
+ if (ret)
+ goto fail_report;
+
+ nz = min_t(u64, virtio64_to_cpu(vblk->vdev, report->nr_zones),
+ nr_zones);
+ if (!nz)
+ break;
+
+ for (i = 0; i < nz && zone_idx < nr_zones; i++) {
+ ret = virtblk_parse_zone(vblk, &report->zones[i],
+ zone_idx, args);
+ if (ret)
+ goto fail_report;
+
+ sector = virtio64_to_cpu(vblk->vdev,
+ report->zones[i].z_start) +
+ vblk->zone_sectors;
+ zone_idx++;
+ }
+ }
+
+ if (zone_idx > 0)
+ ret = zone_idx;
+ else
+ ret = -EINVAL;
+fail_report:
+ mutex_unlock(&vblk->vdev_mutex);
+ kvfree(report);
+ return ret;
+}
+
+static int virtblk_read_zoned_limits(struct virtio_blk *vblk,
+ struct queue_limits *lim)
+{
+ struct virtio_device *vdev = vblk->vdev;
+ u32 v, wg;
+
+ dev_dbg(&vdev->dev, "probing host-managed zoned device\n");
+
+ lim->features |= BLK_FEAT_ZONED;
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ zoned.max_open_zones, &v);
+ lim->max_open_zones = v;
+ dev_dbg(&vdev->dev, "max open zones = %u\n", v);
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ zoned.max_active_zones, &v);
+ lim->max_active_zones = v;
+ dev_dbg(&vdev->dev, "max active zones = %u\n", v);
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ zoned.write_granularity, &wg);
+ if (!wg) {
+ dev_warn(&vdev->dev, "zero write granularity reported\n");
+ return -ENODEV;
+ }
+ lim->physical_block_size = wg;
+ lim->io_min = wg;
+
+ dev_dbg(&vdev->dev, "write granularity = %u\n", wg);
+
+ /*
+ * virtio ZBD specification doesn't require zones to be a power of
+ * two sectors in size, but the code in this driver expects that.
+ */
+ virtio_cread(vdev, struct virtio_blk_config, zoned.zone_sectors,
+ &vblk->zone_sectors);
+ if (vblk->zone_sectors == 0 || !is_power_of_2(vblk->zone_sectors)) {
+ dev_err(&vdev->dev,
+ "zoned device with non power of two zone size %u\n",
+ vblk->zone_sectors);
+ return -ENODEV;
+ }
+ lim->chunk_sectors = vblk->zone_sectors;
+ dev_dbg(&vdev->dev, "zone sectors = %u\n", vblk->zone_sectors);
+
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_DISCARD)) {
+ dev_warn(&vblk->vdev->dev,
+ "ignoring negotiated F_DISCARD for zoned device\n");
+ lim->max_hw_discard_sectors = 0;
+ }
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ zoned.max_append_sectors, &v);
+ if (!v) {
+ dev_warn(&vdev->dev, "zero max_append_sectors reported\n");
+ return -ENODEV;
+ }
+ if ((v << SECTOR_SHIFT) < wg) {
+ dev_err(&vdev->dev,
+ "write granularity %u exceeds max_append_sectors %u limit\n",
+ wg, v);
+ return -ENODEV;
+ }
+ lim->max_hw_zone_append_sectors = v;
+ dev_dbg(&vdev->dev, "max append sectors = %u\n", v);
+
+ return 0;
+}
+#else
+/*
+ * Zoned block device support is not configured in this kernel, host-managed
+ * zoned devices can't be supported.
+ */
+#define virtblk_report_zones NULL
+static inline int virtblk_read_zoned_limits(struct virtio_blk *vblk,
+ struct queue_limits *lim)
+{
+ dev_err(&vblk->vdev->dev,
+ "virtio_blk: zoned devices are not supported");
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_BLK_DEV_ZONED */
+
/* return id (s/n) string for *disk to *id_str
*/
static int virtblk_get_id(struct gendisk *disk, char *id_str)
@@ -456,27 +806,33 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
struct virtio_blk *vblk = disk->private_data;
struct request_queue *q = vblk->disk->queue;
struct request *req;
+ struct virtblk_req *vbr;
int err;
req = blk_mq_alloc_request(q, REQ_OP_DRV_IN, 0);
if (IS_ERR(req))
return PTR_ERR(req);
- err = blk_rq_map_kern(q, req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
+ vbr = blk_mq_rq_to_pdu(req);
+ vbr->in_hdr_len = sizeof(vbr->in_hdr.status);
+ vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
+ vbr->out_hdr.sector = 0;
+
+ err = blk_rq_map_kern(req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
if (err)
goto out;
blk_execute_rq(req, false);
- err = blk_status_to_errno(virtblk_result(blk_mq_rq_to_pdu(req)));
+ err = blk_status_to_errno(virtblk_result(vbr->in_hdr.status));
out:
blk_mq_free_request(req);
return err;
}
/* We provide getgeo only to please some old bootloader/partitioning tools */
-static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
+static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- struct virtio_blk *vblk = bd->bd_disk->private_data;
+ struct virtio_blk *vblk = disk->private_data;
int ret = 0;
mutex_lock(&vblk->vdev_mutex);
@@ -498,7 +854,7 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
/* some standard values, similar to sd */
geo->heads = 1 << 6;
geo->sectors = 1 << 5;
- geo->cylinders = get_capacity(bd->bd_disk) >> 11;
+ geo->cylinders = get_capacity(disk) >> 11;
}
out:
mutex_unlock(&vblk->vdev_mutex);
@@ -509,7 +865,7 @@ static void virtblk_free_disk(struct gendisk *disk)
{
struct virtio_blk *vblk = disk->private_data;
- ida_simple_remove(&vd_index_ida, vblk->index);
+ ida_free(&vd_index_ida, vblk->index);
mutex_destroy(&vblk->vdev_mutex);
kfree(vblk);
}
@@ -518,6 +874,7 @@ static const struct block_device_operations virtblk_fops = {
.owner = THIS_MODULE,
.getgeo = virtblk_getgeo,
.free_disk = virtblk_free_disk,
+ .report_zones = virtblk_report_zones,
};
static int index_to_minor(int index)
@@ -601,12 +958,11 @@ static void virtblk_config_changed(struct virtio_device *vdev)
static int init_vq(struct virtio_blk *vblk)
{
int err;
- int i;
- vq_callback_t **callbacks;
- const char **names;
+ unsigned short i;
+ struct virtqueue_info *vqs_info;
struct virtqueue **vqs;
unsigned short num_vqs;
- unsigned int num_poll_vqs;
+ unsigned short num_poll_vqs;
struct virtio_device *vdev = vblk->vdev;
struct irq_affinity desc = { 0, };
@@ -621,9 +977,8 @@ static int init_vq(struct virtio_blk *vblk)
return -EINVAL;
}
- num_vqs = min_t(unsigned int,
- min_not_zero(num_request_queues, nr_cpu_ids),
- num_vqs);
+ num_vqs = blk_mq_num_possible_queues(
+ min_not_zero(num_request_queues, num_vqs));
num_poll_vqs = min_t(unsigned int, poll_queues, num_vqs - 1);
@@ -640,28 +995,26 @@ static int init_vq(struct virtio_blk *vblk)
if (!vblk->vqs)
return -ENOMEM;
- names = kmalloc_array(num_vqs, sizeof(*names), GFP_KERNEL);
- callbacks = kmalloc_array(num_vqs, sizeof(*callbacks), GFP_KERNEL);
+ vqs_info = kcalloc(num_vqs, sizeof(*vqs_info), GFP_KERNEL);
vqs = kmalloc_array(num_vqs, sizeof(*vqs), GFP_KERNEL);
- if (!names || !callbacks || !vqs) {
+ if (!vqs_info || !vqs) {
err = -ENOMEM;
goto out;
}
for (i = 0; i < num_vqs - num_poll_vqs; i++) {
- callbacks[i] = virtblk_done;
- snprintf(vblk->vqs[i].name, VQ_NAME_LEN, "req.%d", i);
- names[i] = vblk->vqs[i].name;
+ vqs_info[i].callback = virtblk_done;
+ snprintf(vblk->vqs[i].name, VQ_NAME_LEN, "req.%u", i);
+ vqs_info[i].name = vblk->vqs[i].name;
}
for (; i < num_vqs; i++) {
- callbacks[i] = NULL;
- snprintf(vblk->vqs[i].name, VQ_NAME_LEN, "req_poll.%d", i);
- names[i] = vblk->vqs[i].name;
+ snprintf(vblk->vqs[i].name, VQ_NAME_LEN, "req_poll.%u", i);
+ vqs_info[i].name = vblk->vqs[i].name;
}
/* Discover virtqueues and write information to configuration. */
- err = virtio_find_vqs(vdev, num_vqs, vqs, callbacks, names, &desc);
+ err = virtio_find_vqs(vdev, num_vqs, vqs, vqs_info, &desc);
if (err)
goto out;
@@ -673,10 +1026,14 @@ static int init_vq(struct virtio_blk *vblk)
out:
kfree(vqs);
- kfree(callbacks);
- kfree(names);
- if (err)
+ kfree(vqs_info);
+ if (err) {
kfree(vblk->vqs);
+ /*
+ * Set to NULL to prevent freeing vqs again during freezing.
+ */
+ vblk->vqs = NULL;
+ }
return err;
}
@@ -727,14 +1084,6 @@ static int virtblk_get_cache_mode(struct virtio_device *vdev)
return writeback;
}
-static void virtblk_update_cache_mode(struct virtio_device *vdev)
-{
- u8 writeback = virtblk_get_cache_mode(vdev);
- struct virtio_blk *vblk = vdev->priv;
-
- blk_queue_write_cache(vblk->disk->queue, writeback, false);
-}
-
static const char *const virtblk_cache_types[] = {
"write through", "write back"
};
@@ -746,6 +1095,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
struct gendisk *disk = dev_to_disk(dev);
struct virtio_blk *vblk = disk->private_data;
struct virtio_device *vdev = vblk->vdev;
+ struct queue_limits lim;
int i;
BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE));
@@ -754,7 +1104,15 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
return i;
virtio_cwrite8(vdev, offsetof(struct virtio_blk_config, wce), i);
- virtblk_update_cache_mode(vdev);
+
+ lim = queue_limits_start_update(disk->queue);
+ if (virtblk_get_cache_mode(vdev))
+ lim.features |= BLK_FEAT_WRITE_CACHE;
+ else
+ lim.features &= ~BLK_FEAT_WRITE_CACHE;
+ i = queue_limits_commit_update_frozen(disk->queue, &lim);
+ if (i)
+ return i;
return count;
}
@@ -802,7 +1160,7 @@ static const struct attribute_group *virtblk_attr_groups[] = {
NULL,
};
-static int virtblk_map_queues(struct blk_mq_tag_set *set)
+static void virtblk_map_queues(struct blk_mq_tag_set *set)
{
struct virtio_blk *vblk = set->driver_data;
int i, qoff;
@@ -825,10 +1183,9 @@ static int virtblk_map_queues(struct blk_mq_tag_set *set)
if (i == HCTX_TYPE_POLL)
blk_mq_map_queues(&set->map[i]);
else
- blk_mq_virtio_map_queues(&set->map[i], vblk->vdev, 0);
+ blk_mq_map_hw_queues(&set->map[i],
+ &vblk->vdev->dev, 0);
}
-
- return 0;
}
static void virtblk_complete_batch(struct io_comp_batch *iob)
@@ -855,11 +1212,13 @@ static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
while ((vbr = virtqueue_get_buf(vq->vq, &len)) != NULL) {
struct request *req = blk_mq_rq_from_pdu(vbr);
+ u8 status = virtblk_vbr_status(vbr);
found++;
- if (!blk_mq_add_to_batch(req, iob, vbr->status,
- virtblk_complete_batch))
- blk_mq_complete_request(req);
+ if (!blk_mq_complete_request_remote(req) &&
+ !blk_mq_add_to_batch(req, iob, status != VIRTIO_BLK_S_OK,
+ virtblk_complete_batch))
+ virtblk_request_done(req);
}
if (found)
@@ -882,28 +1241,17 @@ static const struct blk_mq_ops virtio_mq_ops = {
static unsigned int virtblk_queue_depth;
module_param_named(queue_depth, virtblk_queue_depth, uint, 0444);
-static int virtblk_probe(struct virtio_device *vdev)
+static int virtblk_read_limits(struct virtio_blk *vblk,
+ struct queue_limits *lim)
{
- struct virtio_blk *vblk;
- struct request_queue *q;
- int err, index;
-
- u32 v, blk_size, max_size, sg_elems, opt_io_size;
+ struct virtio_device *vdev = vblk->vdev;
+ u32 v, max_size, sg_elems, opt_io_size;
+ u32 max_discard_segs = 0;
+ u32 discard_granularity = 0;
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
- unsigned int queue_depth;
-
- if (!vdev->config->get) {
- dev_err(&vdev->dev, "%s failure: config access disabled\n",
- __func__);
- return -EINVAL;
- }
-
- err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS),
- GFP_KERNEL);
- if (err < 0)
- goto out;
- index = err;
+ size_t max_dma_size;
+ int err;
/* We need to know how many segments before we allocate. */
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SEG_MAX,
@@ -917,6 +1265,196 @@ static int virtblk_probe(struct virtio_device *vdev)
/* Prevent integer overflows and honor max vq size */
sg_elems = min_t(u32, sg_elems, VIRTIO_BLK_MAX_SG_ELEMS - 2);
+ /* We can handle whatever the host told us to handle. */
+ lim->max_segments = sg_elems;
+
+ /* No real sector limit. */
+ lim->max_hw_sectors = UINT_MAX;
+
+ max_dma_size = virtio_max_dma_size(vdev);
+ max_size = max_dma_size > U32_MAX ? U32_MAX : max_dma_size;
+
+ /* Host can optionally specify maximum segment size and number of
+ * segments. */
+ err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
+ struct virtio_blk_config, size_max, &v);
+ if (!err)
+ max_size = min(max_size, v);
+
+ lim->max_segment_size = max_size;
+
+ /* Host can optionally specify the block size of the device */
+ virtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE,
+ struct virtio_blk_config, blk_size,
+ &lim->logical_block_size);
+
+ /* Use topology information if available */
+ err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ struct virtio_blk_config, physical_block_exp,
+ &physical_block_exp);
+ if (!err && physical_block_exp)
+ lim->physical_block_size =
+ lim->logical_block_size * (1 << physical_block_exp);
+
+ err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ struct virtio_blk_config, alignment_offset,
+ &alignment_offset);
+ if (!err && alignment_offset)
+ lim->alignment_offset =
+ lim->logical_block_size * alignment_offset;
+
+ err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ struct virtio_blk_config, min_io_size,
+ &min_io_size);
+ if (!err && min_io_size)
+ lim->io_min = lim->logical_block_size * min_io_size;
+
+ err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
+ struct virtio_blk_config, opt_io_size,
+ &opt_io_size);
+ if (!err && opt_io_size)
+ lim->io_opt = lim->logical_block_size * opt_io_size;
+
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_DISCARD)) {
+ virtio_cread(vdev, struct virtio_blk_config,
+ discard_sector_alignment, &discard_granularity);
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ max_discard_sectors, &v);
+ lim->max_hw_discard_sectors = v ? v : UINT_MAX;
+
+ virtio_cread(vdev, struct virtio_blk_config, max_discard_seg,
+ &max_discard_segs);
+ }
+
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_WRITE_ZEROES)) {
+ virtio_cread(vdev, struct virtio_blk_config,
+ max_write_zeroes_sectors, &v);
+ lim->max_write_zeroes_sectors = v ? v : UINT_MAX;
+ }
+
+ /* The discard and secure erase limits are combined since the Linux
+ * block layer uses the same limit for both commands.
+ *
+ * If both VIRTIO_BLK_F_SECURE_ERASE and VIRTIO_BLK_F_DISCARD features
+ * are negotiated, we will use the minimum between the limits.
+ *
+ * discard sector alignment is set to the minimum between discard_sector_alignment
+ * and secure_erase_sector_alignment.
+ *
+ * max discard sectors is set to the minimum between max_discard_seg and
+ * max_secure_erase_seg.
+ */
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_SECURE_ERASE)) {
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ secure_erase_sector_alignment, &v);
+
+ /* secure_erase_sector_alignment should not be zero, the device should set a
+ * valid number of sectors.
+ */
+ if (!v) {
+ dev_err(&vdev->dev,
+ "virtio_blk: secure_erase_sector_alignment can't be 0\n");
+ return -EINVAL;
+ }
+
+ discard_granularity = min_not_zero(discard_granularity, v);
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ max_secure_erase_sectors, &v);
+
+ /* max_secure_erase_sectors should not be zero, the device should set a
+ * valid number of sectors.
+ */
+ if (!v) {
+ dev_err(&vdev->dev,
+ "virtio_blk: max_secure_erase_sectors can't be 0\n");
+ return -EINVAL;
+ }
+
+ lim->max_secure_erase_sectors = v;
+
+ virtio_cread(vdev, struct virtio_blk_config,
+ max_secure_erase_seg, &v);
+
+ /* max_secure_erase_seg should not be zero, the device should set a
+ * valid number of segments
+ */
+ if (!v) {
+ dev_err(&vdev->dev,
+ "virtio_blk: max_secure_erase_seg can't be 0\n");
+ return -EINVAL;
+ }
+
+ max_discard_segs = min_not_zero(max_discard_segs, v);
+ }
+
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_DISCARD) ||
+ virtio_has_feature(vdev, VIRTIO_BLK_F_SECURE_ERASE)) {
+ /* max_discard_seg and discard_granularity will be 0 only
+ * if max_discard_seg and discard_sector_alignment fields in the virtio
+ * config are 0 and VIRTIO_BLK_F_SECURE_ERASE feature is not negotiated.
+ * In this case, we use default values.
+ */
+ if (!max_discard_segs)
+ max_discard_segs = sg_elems;
+
+ lim->max_discard_segments =
+ min(max_discard_segs, MAX_DISCARD_SEGMENTS);
+
+ if (discard_granularity)
+ lim->discard_granularity =
+ discard_granularity << SECTOR_SHIFT;
+ else
+ lim->discard_granularity = lim->logical_block_size;
+ }
+
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_ZONED)) {
+ u8 model;
+
+ virtio_cread(vdev, struct virtio_blk_config, zoned.model, &model);
+ switch (model) {
+ case VIRTIO_BLK_Z_NONE:
+ case VIRTIO_BLK_Z_HA:
+ /* treat host-aware devices as non-zoned */
+ return 0;
+ case VIRTIO_BLK_Z_HM:
+ err = virtblk_read_zoned_limits(vblk, lim);
+ if (err)
+ return err;
+ break;
+ default:
+ dev_err(&vdev->dev, "unsupported zone model %d\n", model);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int virtblk_probe(struct virtio_device *vdev)
+{
+ struct virtio_blk *vblk;
+ struct queue_limits lim = {
+ .features = BLK_FEAT_ROTATIONAL,
+ .logical_block_size = SECTOR_SIZE,
+ };
+ int err, index;
+ unsigned int queue_depth;
+
+ if (!vdev->config->get) {
+ dev_err(&vdev->dev, "%s failure: config access disabled\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ err = ida_alloc_range(&vd_index_ida, 0,
+ minor_to_index(1 << MINORBITS) - 1, GFP_KERNEL);
+ if (err < 0)
+ goto out;
+ index = err;
+
vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL);
if (!vblk) {
err = -ENOMEM;
@@ -947,7 +1485,6 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->tag_set.ops = &virtio_mq_ops;
vblk->tag_set.queue_depth = queue_depth;
vblk->tag_set.numa_node = NUMA_NO_NODE;
- vblk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
vblk->tag_set.cmd_size =
sizeof(struct virtblk_req) +
sizeof(struct scatterlist) * VIRTIO_BLK_INLINE_SG_CNT;
@@ -961,12 +1498,18 @@ static int virtblk_probe(struct virtio_device *vdev)
if (err)
goto out_free_vq;
- vblk->disk = blk_mq_alloc_disk(&vblk->tag_set, vblk);
+ err = virtblk_read_limits(vblk, &lim);
+ if (err)
+ goto out_free_tags;
+
+ if (virtblk_get_cache_mode(vdev))
+ lim.features |= BLK_FEAT_WRITE_CACHE;
+
+ vblk->disk = blk_mq_alloc_disk(&vblk->tag_set, &lim, vblk);
if (IS_ERR(vblk->disk)) {
err = PTR_ERR(vblk->disk);
goto out_free_tags;
}
- q = vblk->disk->queue;
virtblk_name_format("vd", index, vblk->disk->disk_name, DISK_NAME_LEN);
@@ -977,107 +1520,24 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->disk->fops = &virtblk_fops;
vblk->index = index;
- /* configure queue flush support */
- virtblk_update_cache_mode(vdev);
-
/* If disk is read-only in the host, the guest should obey */
if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))
set_disk_ro(vblk->disk, 1);
- /* We can handle whatever the host told us to handle. */
- blk_queue_max_segments(q, sg_elems);
-
- /* No real sector limit. */
- blk_queue_max_hw_sectors(q, -1U);
-
- max_size = virtio_max_dma_size(vdev);
-
- /* Host can optionally specify maximum segment size and number of
- * segments. */
- err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
- struct virtio_blk_config, size_max, &v);
- if (!err)
- max_size = min(max_size, v);
-
- blk_queue_max_segment_size(q, max_size);
+ virtblk_update_capacity(vblk, false);
+ virtio_device_ready(vdev);
- /* Host can optionally specify the block size of the device */
- err = virtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE,
- struct virtio_blk_config, blk_size,
- &blk_size);
- if (!err) {
- err = blk_validate_block_size(blk_size);
- if (err) {
- dev_err(&vdev->dev,
- "virtio_blk: invalid block size: 0x%x\n",
- blk_size);
+ /*
+ * All steps that follow use the VQs therefore they need to be
+ * placed after the virtio_device_ready() call above.
+ */
+ if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
+ (lim.features & BLK_FEAT_ZONED)) {
+ err = blk_revalidate_disk_zones(vblk->disk);
+ if (err)
goto out_cleanup_disk;
- }
-
- blk_queue_logical_block_size(q, blk_size);
- } else
- blk_size = queue_logical_block_size(q);
-
- /* Use topology information if available */
- err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
- struct virtio_blk_config, physical_block_exp,
- &physical_block_exp);
- if (!err && physical_block_exp)
- blk_queue_physical_block_size(q,
- blk_size * (1 << physical_block_exp));
-
- err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
- struct virtio_blk_config, alignment_offset,
- &alignment_offset);
- if (!err && alignment_offset)
- blk_queue_alignment_offset(q, blk_size * alignment_offset);
-
- err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
- struct virtio_blk_config, min_io_size,
- &min_io_size);
- if (!err && min_io_size)
- blk_queue_io_min(q, blk_size * min_io_size);
-
- err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
- struct virtio_blk_config, opt_io_size,
- &opt_io_size);
- if (!err && opt_io_size)
- blk_queue_io_opt(q, blk_size * opt_io_size);
-
- if (virtio_has_feature(vdev, VIRTIO_BLK_F_DISCARD)) {
- virtio_cread(vdev, struct virtio_blk_config,
- discard_sector_alignment, &v);
- if (v)
- q->limits.discard_granularity = v << SECTOR_SHIFT;
- else
- q->limits.discard_granularity = blk_size;
-
- virtio_cread(vdev, struct virtio_blk_config,
- max_discard_sectors, &v);
- blk_queue_max_discard_sectors(q, v ? v : UINT_MAX);
-
- virtio_cread(vdev, struct virtio_blk_config, max_discard_seg,
- &v);
-
- /*
- * max_discard_seg == 0 is out of spec but we always
- * handled it.
- */
- if (!v)
- v = sg_elems;
- blk_queue_max_discard_segments(q,
- min(v, MAX_DISCARD_SEGMENTS));
- }
-
- if (virtio_has_feature(vdev, VIRTIO_BLK_F_WRITE_ZEROES)) {
- virtio_cread(vdev, struct virtio_blk_config,
- max_write_zeroes_sectors, &v);
- blk_queue_max_write_zeroes_sectors(q, v ? v : UINT_MAX);
}
- virtblk_update_capacity(vblk, false);
- virtio_device_ready(vdev);
-
err = device_add_disk(&vdev->dev, vblk->disk, virtblk_attr_groups);
if (err)
goto out_cleanup_disk;
@@ -1094,7 +1554,7 @@ out_free_vq:
out_free_vblk:
kfree(vblk);
out_free_index:
- ida_simple_remove(&vd_index_ida, index);
+ ida_free(&vd_index_ida, index);
out:
return err;
}
@@ -1125,10 +1585,16 @@ static void virtblk_remove(struct virtio_device *vdev)
put_disk(vblk->disk);
}
-#ifdef CONFIG_PM_SLEEP
-static int virtblk_freeze(struct virtio_device *vdev)
+static int virtblk_freeze_priv(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
+ struct request_queue *q = vblk->disk->queue;
+ unsigned int memflags;
+
+ /* Ensure no requests in virtqueues before deleting vqs. */
+ memflags = blk_mq_freeze_queue(q);
+ blk_mq_quiesce_queue_nowait(q);
+ blk_mq_unfreeze_queue(q, memflags);
/* Ensure we don't receive any more interrupts */
virtio_reset_device(vdev);
@@ -1136,15 +1602,19 @@ static int virtblk_freeze(struct virtio_device *vdev)
/* Make sure no work handler is accessing the device. */
flush_work(&vblk->config_work);
- blk_mq_quiesce_queue(vblk->disk->queue);
-
vdev->config->del_vqs(vdev);
kfree(vblk->vqs);
+ /*
+ * Set to NULL to prevent freeing vqs again after a failed vqs
+ * allocation during resume. Note that kfree() already handles NULL
+ * pointers safely.
+ */
+ vblk->vqs = NULL;
return 0;
}
-static int virtblk_restore(struct virtio_device *vdev)
+static int virtblk_restore_priv(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
int ret;
@@ -1154,12 +1624,33 @@ static int virtblk_restore(struct virtio_device *vdev)
return ret;
virtio_device_ready(vdev);
-
blk_mq_unquiesce_queue(vblk->disk->queue);
+
return 0;
}
+
+#ifdef CONFIG_PM_SLEEP
+static int virtblk_freeze(struct virtio_device *vdev)
+{
+ return virtblk_freeze_priv(vdev);
+}
+
+static int virtblk_restore(struct virtio_device *vdev)
+{
+ return virtblk_restore_priv(vdev);
+}
#endif
+static int virtblk_reset_prepare(struct virtio_device *vdev)
+{
+ return virtblk_freeze_priv(vdev);
+}
+
+static int virtblk_reset_done(struct virtio_device *vdev)
+{
+ return virtblk_restore_priv(vdev);
+}
+
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
{ 0 },
@@ -1170,6 +1661,7 @@ static unsigned int features_legacy[] = {
VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,
+ VIRTIO_BLK_F_SECURE_ERASE,
}
;
static unsigned int features[] = {
@@ -1177,6 +1669,7 @@ static unsigned int features[] = {
VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,
+ VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_ZONED,
};
static struct virtio_driver virtio_blk = {
@@ -1185,7 +1678,6 @@ static struct virtio_driver virtio_blk = {
.feature_table_legacy = features_legacy,
.feature_table_size_legacy = ARRAY_SIZE(features_legacy),
.driver.name = KBUILD_MODNAME,
- .driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtblk_probe,
.remove = virtblk_remove,
@@ -1194,13 +1686,15 @@ static struct virtio_driver virtio_blk = {
.freeze = virtblk_freeze,
.restore = virtblk_restore,
#endif
+ .reset_prepare = virtblk_reset_prepare,
+ .reset_done = virtblk_reset_done,
};
static int __init virtio_blk_init(void)
{
int error;
- virtblk_wq = alloc_workqueue("virtio-blk", 0, 0);
+ virtblk_wq = alloc_workqueue("virtio-blk", WQ_PERCPU, 0);
if (!virtblk_wq)
return -ENOMEM;
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index a5cf7f1e871c..a7c2b04ab943 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -239,9 +239,9 @@ static void put_persistent_gnt(struct xen_blkif_ring *ring,
atomic_dec(&ring->persistent_gnt_in_use);
}
-static void free_persistent_gnts(struct xen_blkif_ring *ring, struct rb_root *root,
- unsigned int num)
+static void free_persistent_gnts(struct xen_blkif_ring *ring)
{
+ struct rb_root *root = &ring->persistent_gnts;
struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct page *pages[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct persistent_gnt *persistent_gnt;
@@ -249,6 +249,9 @@ static void free_persistent_gnts(struct xen_blkif_ring *ring, struct rb_root *ro
int segs_to_unmap = 0;
struct gntab_unmap_queue_data unmap_data;
+ if (RB_EMPTY_ROOT(root))
+ return;
+
unmap_data.pages = pages;
unmap_data.unmap_ops = unmap;
unmap_data.kunmap_ops = NULL;
@@ -277,9 +280,11 @@ static void free_persistent_gnts(struct xen_blkif_ring *ring, struct rb_root *ro
rb_erase(&persistent_gnt->node, root);
kfree(persistent_gnt);
- num--;
+ ring->persistent_gnt_c--;
}
- BUG_ON(num != 0);
+
+ BUG_ON(!RB_EMPTY_ROOT(&ring->persistent_gnts));
+ BUG_ON(ring->persistent_gnt_c != 0);
}
void xen_blkbk_unmap_purged_grants(struct work_struct *work)
@@ -460,7 +465,7 @@ static int xen_vbd_translate(struct phys_req *req, struct xen_blkif *blkif,
}
req->dev = vbd->pdevice;
- req->bdev = vbd->bdev;
+ req->bdev = file_bdev(vbd->bdev_file);
rc = 0;
out:
@@ -539,7 +544,7 @@ static void print_stats(struct xen_blkif_ring *ring)
ring->st_rd_req, ring->st_wr_req,
ring->st_f_req, ring->st_ds_req,
ring->persistent_gnt_c, max_pgrants);
- ring->st_print = jiffies + msecs_to_jiffies(10 * 1000);
+ ring->st_print = jiffies + secs_to_jiffies(10);
ring->st_rd_req = 0;
ring->st_wr_req = 0;
ring->st_oo_req = 0;
@@ -631,12 +636,7 @@ purge_gnt_list:
void xen_blkbk_free_caches(struct xen_blkif_ring *ring)
{
/* Free all persistent grant pages */
- if (!RB_EMPTY_ROOT(&ring->persistent_gnts))
- free_persistent_gnts(ring, &ring->persistent_gnts,
- ring->persistent_gnt_c);
-
- BUG_ON(!RB_EMPTY_ROOT(&ring->persistent_gnts));
- ring->persistent_gnt_c = 0;
+ free_persistent_gnts(ring);
/* Since we are shutting down remove all pages from the buffer */
gnttab_page_cache_shrink(&ring->free_pages, 0 /* All */);
@@ -891,7 +891,7 @@ next:
out:
for (i = last_map; i < num; i++) {
/* Don't zap current batch's valid persistent grants. */
- if(i >= map_until)
+ if (i >= map_until)
pages[i]->persistent_gnt = NULL;
pages[i]->handle = BLKBACK_INVALID_HANDLE;
}
@@ -969,7 +969,7 @@ static int dispatch_discard_io(struct xen_blkif_ring *ring,
int err = 0;
int status = BLKIF_RSP_OKAY;
struct xen_blkif *blkif = ring->blkif;
- struct block_device *bdev = blkif->vbd.bdev;
+ struct block_device *bdev = file_bdev(blkif->vbd.bdev_file);
struct phys_req preq;
xen_blkif_get(blkif);
@@ -1072,7 +1072,111 @@ static void end_block_io_op(struct bio *bio)
bio_put(bio);
}
+static void blkif_get_x86_32_req(struct blkif_request *dst,
+ const struct blkif_x86_32_request *src)
+{
+ unsigned int i, n;
+
+ dst->operation = READ_ONCE(src->operation);
+
+ switch (dst->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ dst->u.rw.nr_segments = READ_ONCE(src->u.rw.nr_segments);
+ dst->u.rw.handle = src->u.rw.handle;
+ dst->u.rw.id = src->u.rw.id;
+ dst->u.rw.sector_number = src->u.rw.sector_number;
+ n = min_t(unsigned int, BLKIF_MAX_SEGMENTS_PER_REQUEST,
+ dst->u.rw.nr_segments);
+ for (i = 0; i < n; i++)
+ dst->u.rw.seg[i] = src->u.rw.seg[i];
+ break;
+
+ case BLKIF_OP_DISCARD:
+ dst->u.discard.flag = src->u.discard.flag;
+ dst->u.discard.id = src->u.discard.id;
+ dst->u.discard.sector_number = src->u.discard.sector_number;
+ dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
+ break;
+
+ case BLKIF_OP_INDIRECT:
+ dst->u.indirect.indirect_op = src->u.indirect.indirect_op;
+ dst->u.indirect.nr_segments =
+ READ_ONCE(src->u.indirect.nr_segments);
+ dst->u.indirect.handle = src->u.indirect.handle;
+ dst->u.indirect.id = src->u.indirect.id;
+ dst->u.indirect.sector_number = src->u.indirect.sector_number;
+ n = min(MAX_INDIRECT_PAGES,
+ INDIRECT_PAGES(dst->u.indirect.nr_segments));
+ for (i = 0; i < n; i++)
+ dst->u.indirect.indirect_grefs[i] =
+ src->u.indirect.indirect_grefs[i];
+ break;
+
+ default:
+ /*
+ * Don't know how to translate this op. Only get the
+ * ID so failure can be reported to the frontend.
+ */
+ dst->u.other.id = src->u.other.id;
+ break;
+ }
+}
+
+static void blkif_get_x86_64_req(struct blkif_request *dst,
+ const struct blkif_x86_64_request *src)
+{
+ unsigned int i, n;
+
+ dst->operation = READ_ONCE(src->operation);
+
+ switch (dst->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ dst->u.rw.nr_segments = READ_ONCE(src->u.rw.nr_segments);
+ dst->u.rw.handle = src->u.rw.handle;
+ dst->u.rw.id = src->u.rw.id;
+ dst->u.rw.sector_number = src->u.rw.sector_number;
+ n = min_t(unsigned int, BLKIF_MAX_SEGMENTS_PER_REQUEST,
+ dst->u.rw.nr_segments);
+ for (i = 0; i < n; i++)
+ dst->u.rw.seg[i] = src->u.rw.seg[i];
+ break;
+
+ case BLKIF_OP_DISCARD:
+ dst->u.discard.flag = src->u.discard.flag;
+ dst->u.discard.id = src->u.discard.id;
+ dst->u.discard.sector_number = src->u.discard.sector_number;
+ dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
+ break;
+
+ case BLKIF_OP_INDIRECT:
+ dst->u.indirect.indirect_op = src->u.indirect.indirect_op;
+ dst->u.indirect.nr_segments =
+ READ_ONCE(src->u.indirect.nr_segments);
+ dst->u.indirect.handle = src->u.indirect.handle;
+ dst->u.indirect.id = src->u.indirect.id;
+ dst->u.indirect.sector_number = src->u.indirect.sector_number;
+ n = min(MAX_INDIRECT_PAGES,
+ INDIRECT_PAGES(dst->u.indirect.nr_segments));
+ for (i = 0; i < n; i++)
+ dst->u.indirect.indirect_grefs[i] =
+ src->u.indirect.indirect_grefs[i];
+ break;
+ default:
+ /*
+ * Don't know how to translate this op. Only get the
+ * ID so failure can be reported to the frontend.
+ */
+ dst->u.other.id = src->u.other.id;
+ break;
+ }
+}
/*
* Function to copy the from the ring buffer the 'struct blkif_request'
@@ -1459,5 +1563,6 @@ static void __exit xen_blkif_fini(void)
module_exit(xen_blkif_fini);
+MODULE_DESCRIPTION("Virtual block device back-end driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("xen-backend:vbd");
diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h
index a28473470e66..b427d54bc120 100644
--- a/drivers/block/xen-blkback/common.h
+++ b/drivers/block/xen-blkback/common.h
@@ -132,7 +132,7 @@ struct blkif_x86_32_request {
struct blkif_x86_64_request_rw {
uint8_t nr_segments; /* number of segments */
blkif_vdev_t handle; /* only for read/write requests */
- uint32_t _pad1; /* offsetof(blkif_reqest..,u.rw.id)==8 */
+ uint32_t _pad1; /* offsetof(blkif_request..,u.rw.id)==8 */
uint64_t id;
blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
@@ -221,7 +221,7 @@ struct xen_vbd {
unsigned char type;
/* phys device that this vbd maps to. */
u32 pdevice;
- struct block_device *bdev;
+ struct file *bdev_file;
/* Cached size parameter. */
sector_t size;
unsigned int flush_support:1;
@@ -296,7 +296,7 @@ struct xen_blkif_ring {
struct work_struct free_work;
/* Thread shutdown wait queue. */
wait_queue_head_t shutdown_wq;
- struct xen_blkif *blkif;
+ struct xen_blkif *blkif;
};
struct xen_blkif {
@@ -315,7 +315,7 @@ struct xen_blkif {
atomic_t drain;
struct work_struct free_work;
- unsigned int nr_ring_pages;
+ unsigned int nr_ring_pages;
bool multi_ref;
/* All rings for this device. */
struct xen_blkif_ring *rings;
@@ -329,7 +329,7 @@ struct seg_buf {
};
struct grant_page {
- struct page *page;
+ struct page *page;
struct persistent_gnt *persistent_gnt;
grant_handle_t handle;
grant_ref_t gref;
@@ -360,7 +360,7 @@ struct pending_req {
};
-#define vbd_sz(_v) bdev_nr_sectors((_v)->bdev)
+#define vbd_sz(_v) bdev_nr_sectors(file_bdev((_v)->bdev_file))
#define xen_blkif_get(_b) (atomic_inc(&(_b)->refcnt))
#define xen_blkif_put(_b) \
@@ -384,7 +384,6 @@ void xen_blkif_xenbus_fini(void);
irqreturn_t xen_blkif_be_int(int irq, void *dev_id);
int xen_blkif_schedule(void *arg);
-int xen_blkif_purge_persistent(void *arg);
void xen_blkbk_free_caches(struct xen_blkif_ring *ring);
int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
@@ -395,100 +394,4 @@ int xen_blkbk_barrier(struct xenbus_transaction xbt,
struct xenbus_device *xen_blkbk_xenbus(struct backend_info *be);
void xen_blkbk_unmap_purged_grants(struct work_struct *work);
-static inline void blkif_get_x86_32_req(struct blkif_request *dst,
- struct blkif_x86_32_request *src)
-{
- int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST, j;
- dst->operation = READ_ONCE(src->operation);
- switch (dst->operation) {
- case BLKIF_OP_READ:
- case BLKIF_OP_WRITE:
- case BLKIF_OP_WRITE_BARRIER:
- case BLKIF_OP_FLUSH_DISKCACHE:
- dst->u.rw.nr_segments = src->u.rw.nr_segments;
- dst->u.rw.handle = src->u.rw.handle;
- dst->u.rw.id = src->u.rw.id;
- dst->u.rw.sector_number = src->u.rw.sector_number;
- barrier();
- if (n > dst->u.rw.nr_segments)
- n = dst->u.rw.nr_segments;
- for (i = 0; i < n; i++)
- dst->u.rw.seg[i] = src->u.rw.seg[i];
- break;
- case BLKIF_OP_DISCARD:
- dst->u.discard.flag = src->u.discard.flag;
- dst->u.discard.id = src->u.discard.id;
- dst->u.discard.sector_number = src->u.discard.sector_number;
- dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
- break;
- case BLKIF_OP_INDIRECT:
- dst->u.indirect.indirect_op = src->u.indirect.indirect_op;
- dst->u.indirect.nr_segments = src->u.indirect.nr_segments;
- dst->u.indirect.handle = src->u.indirect.handle;
- dst->u.indirect.id = src->u.indirect.id;
- dst->u.indirect.sector_number = src->u.indirect.sector_number;
- barrier();
- j = min(MAX_INDIRECT_PAGES, INDIRECT_PAGES(dst->u.indirect.nr_segments));
- for (i = 0; i < j; i++)
- dst->u.indirect.indirect_grefs[i] =
- src->u.indirect.indirect_grefs[i];
- break;
- default:
- /*
- * Don't know how to translate this op. Only get the
- * ID so failure can be reported to the frontend.
- */
- dst->u.other.id = src->u.other.id;
- break;
- }
-}
-
-static inline void blkif_get_x86_64_req(struct blkif_request *dst,
- struct blkif_x86_64_request *src)
-{
- int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST, j;
- dst->operation = READ_ONCE(src->operation);
- switch (dst->operation) {
- case BLKIF_OP_READ:
- case BLKIF_OP_WRITE:
- case BLKIF_OP_WRITE_BARRIER:
- case BLKIF_OP_FLUSH_DISKCACHE:
- dst->u.rw.nr_segments = src->u.rw.nr_segments;
- dst->u.rw.handle = src->u.rw.handle;
- dst->u.rw.id = src->u.rw.id;
- dst->u.rw.sector_number = src->u.rw.sector_number;
- barrier();
- if (n > dst->u.rw.nr_segments)
- n = dst->u.rw.nr_segments;
- for (i = 0; i < n; i++)
- dst->u.rw.seg[i] = src->u.rw.seg[i];
- break;
- case BLKIF_OP_DISCARD:
- dst->u.discard.flag = src->u.discard.flag;
- dst->u.discard.id = src->u.discard.id;
- dst->u.discard.sector_number = src->u.discard.sector_number;
- dst->u.discard.nr_sectors = src->u.discard.nr_sectors;
- break;
- case BLKIF_OP_INDIRECT:
- dst->u.indirect.indirect_op = src->u.indirect.indirect_op;
- dst->u.indirect.nr_segments = src->u.indirect.nr_segments;
- dst->u.indirect.handle = src->u.indirect.handle;
- dst->u.indirect.id = src->u.indirect.id;
- dst->u.indirect.sector_number = src->u.indirect.sector_number;
- barrier();
- j = min(MAX_INDIRECT_PAGES, INDIRECT_PAGES(dst->u.indirect.nr_segments));
- for (i = 0; i < j; i++)
- dst->u.indirect.indirect_grefs[i] =
- src->u.indirect.indirect_grefs[i];
- break;
- default:
- /*
- * Don't know how to translate this op. Only get the
- * ID so failure can be reported to the frontend.
- */
- dst->u.other.id = src->u.other.id;
- break;
- }
-}
-
#endif /* __XEN_BLKIF__BACKEND__COMMON_H__ */
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index c0227dfa4688..0621878940ae 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -81,7 +81,7 @@ static void xen_update_blkif_status(struct xen_blkif *blkif)
int i;
/* Not ready to connect? */
- if (!blkif->rings || !blkif->rings[0].irq || !blkif->vbd.bdev)
+ if (!blkif->rings || !blkif->rings[0].irq || !blkif->vbd.bdev_file)
return;
/* Already connected? */
@@ -99,12 +99,12 @@ static void xen_update_blkif_status(struct xen_blkif *blkif)
return;
}
- err = sync_blockdev(blkif->vbd.bdev);
+ err = sync_blockdev(file_bdev(blkif->vbd.bdev_file));
if (err) {
xenbus_dev_error(blkif->be->dev, err, "block flush");
return;
}
- invalidate_inode_pages2(blkif->vbd.bdev->bd_inode->i_mapping);
+ invalidate_inode_pages2(blkif->vbd.bdev_file->f_mapping);
for (i = 0; i < blkif->nr_rings; i++) {
ring = &blkif->rings[i];
@@ -472,9 +472,9 @@ static void xenvbd_sysfs_delif(struct xenbus_device *dev)
static void xen_vbd_free(struct xen_vbd *vbd)
{
- if (vbd->bdev)
- blkdev_put(vbd->bdev, vbd->readonly ? FMODE_READ : FMODE_WRITE);
- vbd->bdev = NULL;
+ if (vbd->bdev_file)
+ fput(vbd->bdev_file);
+ vbd->bdev_file = NULL;
}
static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
@@ -482,7 +482,7 @@ static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
int cdrom)
{
struct xen_vbd *vbd;
- struct block_device *bdev;
+ struct file *bdev_file;
vbd = &blkif->vbd;
vbd->handle = handle;
@@ -491,17 +491,17 @@ static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
vbd->pdevice = MKDEV(major, minor);
- bdev = blkdev_get_by_dev(vbd->pdevice, vbd->readonly ?
- FMODE_READ : FMODE_WRITE, NULL);
+ bdev_file = bdev_file_open_by_dev(vbd->pdevice, vbd->readonly ?
+ BLK_OPEN_READ : BLK_OPEN_WRITE, NULL, NULL);
- if (IS_ERR(bdev)) {
+ if (IS_ERR(bdev_file)) {
pr_warn("xen_vbd_create: device %08x could not be opened\n",
vbd->pdevice);
return -ENOENT;
}
- vbd->bdev = bdev;
- if (vbd->bdev->bd_disk == NULL) {
+ vbd->bdev_file = bdev_file;
+ if (file_bdev(vbd->bdev_file)->bd_disk == NULL) {
pr_warn("xen_vbd_create: device %08x doesn't exist\n",
vbd->pdevice);
xen_vbd_free(vbd);
@@ -509,14 +509,14 @@ static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
}
vbd->size = vbd_sz(vbd);
- if (cdrom || disk_to_cdi(vbd->bdev->bd_disk))
+ if (cdrom || disk_to_cdi(file_bdev(vbd->bdev_file)->bd_disk))
vbd->type |= VDISK_CDROM;
- if (vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
+ if (file_bdev(vbd->bdev_file)->bd_disk->flags & GENHD_FL_REMOVABLE)
vbd->type |= VDISK_REMOVABLE;
- if (bdev_write_cache(bdev))
+ if (bdev_write_cache(file_bdev(bdev_file)))
vbd->flush_support = true;
- if (bdev_max_secure_erase_sectors(bdev))
+ if (bdev_max_secure_erase_sectors(file_bdev(bdev_file)))
vbd->discard_secure = true;
pr_debug("Successful creation of handle=%04x (dom=%u)\n",
@@ -524,7 +524,7 @@ static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
return 0;
}
-static int xen_blkbk_remove(struct xenbus_device *dev)
+static void xen_blkbk_remove(struct xenbus_device *dev)
{
struct backend_info *be = dev_get_drvdata(&dev->dev);
@@ -547,8 +547,6 @@ static int xen_blkbk_remove(struct xenbus_device *dev)
/* Put the reference we set in xen_blkif_alloc(). */
xen_blkif_put(be->blkif);
}
-
- return 0;
}
int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
@@ -571,7 +569,7 @@ static void xen_blkbk_discard(struct xenbus_transaction xbt, struct backend_info
struct xen_blkif *blkif = be->blkif;
int err;
int state = 0;
- struct block_device *bdev = be->blkif->vbd.bdev;
+ struct block_device *bdev = file_bdev(be->blkif->vbd.bdev_file);
if (!xenbus_read_unsigned(dev->nodename, "discard-enable", 1))
return;
@@ -932,15 +930,16 @@ again:
goto abort;
}
err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu",
- (unsigned long)
- bdev_logical_block_size(be->blkif->vbd.bdev));
+ (unsigned long)bdev_logical_block_size(
+ file_bdev(be->blkif->vbd.bdev_file)));
if (err) {
xenbus_dev_fatal(dev, err, "writing %s/sector-size",
dev->nodename);
goto abort;
}
err = xenbus_printf(xbt, dev->nodename, "physical-sector-size", "%u",
- bdev_physical_block_size(be->blkif->vbd.bdev));
+ bdev_physical_block_size(
+ file_bdev(be->blkif->vbd.bdev_file)));
if (err)
xenbus_dev_error(dev, err, "writing %s/physical-sector-size",
dev->nodename);
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 35b9bcad9db9..04fc6b552c04 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -493,11 +493,11 @@ static void blkif_restart_queue_callback(void *arg)
schedule_work(&rinfo->work);
}
-static int blkif_getgeo(struct block_device *bd, struct hd_geometry *hg)
+static int blkif_getgeo(struct gendisk *disk, struct hd_geometry *hg)
{
/* We don't have real geometry info, but let's at least return
values consistent with the size of the device */
- sector_t nsect = get_capacity(bd->bd_disk);
+ sector_t nsect = get_capacity(disk);
sector_t cylinders = nsect;
hg->heads = 0xff;
@@ -509,7 +509,7 @@ static int blkif_getgeo(struct block_device *bd, struct hd_geometry *hg)
return 0;
}
-static int blkif_ioctl(struct block_device *bdev, fmode_t mode,
+static int blkif_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned command, unsigned long argument)
{
struct blkfront_info *info = bdev->bd_disk->private_data;
@@ -751,7 +751,7 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
id = blkif_ring_get_request(rinfo, req, &final_ring_req);
ring_req = &rinfo->shadow[id].req;
- num_sg = blk_rq_map_sg(req->q, req, rinfo->shadow[id].sg);
+ num_sg = blk_rq_map_sg(req, rinfo->shadow[id].sg);
num_grant = 0;
/* Calculate the number of grant used */
for_each_sg(rinfo->shadow[id].sg, sg, num_sg, i)
@@ -780,13 +780,19 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
ring_req->u.rw.handle = info->handle;
ring_req->operation = rq_data_dir(req) ?
BLKIF_OP_WRITE : BLKIF_OP_READ;
- if (req_op(req) == REQ_OP_FLUSH || req->cmd_flags & REQ_FUA) {
+ if (req_op(req) == REQ_OP_FLUSH ||
+ (req_op(req) == REQ_OP_WRITE && (req->cmd_flags & REQ_FUA))) {
/*
* Ideally we can do an unordered flush-to-disk.
* In case the backend onlysupports barriers, use that.
* A barrier request a superset of FUA, so we can
* implement it the same way. (It's also a FLUSH+FUA,
* since it is guaranteed ordered WRT previous writes.)
+ *
+ * Note that can end up here with a FUA write and the
+ * flags cleared. This happens when the flag was
+ * run-time disabled after a failing I/O, and we'll
+ * simplify submit it as a normal write.
*/
if (info->feature_flush && info->feature_fua)
ring_req->operation =
@@ -794,8 +800,6 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
else if (info->feature_flush)
ring_req->operation =
BLKIF_OP_FLUSH_DISKCACHE;
- else
- ring_req->operation = 0;
}
ring_req->u.rw.nr_segments = num_grant;
if (unlikely(require_extra_req)) {
@@ -886,16 +890,6 @@ static inline void flush_requests(struct blkfront_ring_info *rinfo)
notify_remote_via_irq(rinfo->irq);
}
-static inline bool blkif_request_flush_invalid(struct request *req,
- struct blkfront_info *info)
-{
- return (blk_rq_is_passthrough(req) ||
- ((req_op(req) == REQ_OP_FLUSH) &&
- !info->feature_flush) ||
- ((req->cmd_flags & REQ_FUA) &&
- !info->feature_fua));
-}
-
static blk_status_t blkif_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *qd)
{
@@ -907,12 +901,22 @@ static blk_status_t blkif_queue_rq(struct blk_mq_hw_ctx *hctx,
rinfo = get_rinfo(info, qid);
blk_mq_start_request(qd->rq);
spin_lock_irqsave(&rinfo->ring_lock, flags);
- if (RING_FULL(&rinfo->ring))
- goto out_busy;
- if (blkif_request_flush_invalid(qd->rq, rinfo->dev_info))
- goto out_err;
+ /*
+ * Check if the backend actually supports flushes.
+ *
+ * While the block layer won't send us flushes if we don't claim to
+ * support them, the Xen protocol allows the backend to revoke support
+ * at any time. That is of course a really bad idea and dangerous, but
+ * has been allowed for 10+ years. In that case we simply clear the
+ * flags, and directly return here for an empty flush and ignore the
+ * FUA flag later on.
+ */
+ if (unlikely(req_op(qd->rq) == REQ_OP_FLUSH && !info->feature_flush))
+ goto complete;
+ if (RING_FULL(&rinfo->ring))
+ goto out_busy;
if (blkif_queue_request(qd->rq, rinfo))
goto out_busy;
@@ -920,14 +924,14 @@ static blk_status_t blkif_queue_rq(struct blk_mq_hw_ctx *hctx,
spin_unlock_irqrestore(&rinfo->ring_lock, flags);
return BLK_STS_OK;
-out_err:
- spin_unlock_irqrestore(&rinfo->ring_lock, flags);
- return BLK_STS_IOERR;
-
out_busy:
blk_mq_stop_hw_queue(hctx);
spin_unlock_irqrestore(&rinfo->ring_lock, flags);
return BLK_STS_DEV_RESOURCE;
+complete:
+ spin_unlock_irqrestore(&rinfo->ring_lock, flags);
+ blk_mq_end_request(qd->rq, BLK_STS_OK);
+ return BLK_STS_OK;
}
static void blkif_complete_rq(struct request *rq)
@@ -940,39 +944,41 @@ static const struct blk_mq_ops blkfront_mq_ops = {
.complete = blkif_complete_rq,
};
-static void blkif_set_queue_limits(struct blkfront_info *info)
+static void blkif_set_queue_limits(const struct blkfront_info *info,
+ struct queue_limits *lim)
{
- struct request_queue *rq = info->rq;
- struct gendisk *gd = info->gd;
unsigned int segments = info->max_indirect_segments ? :
BLKIF_MAX_SEGMENTS_PER_REQUEST;
- blk_queue_flag_set(QUEUE_FLAG_VIRT, rq);
-
if (info->feature_discard) {
- blk_queue_max_discard_sectors(rq, get_capacity(gd));
- rq->limits.discard_granularity = info->discard_granularity ?:
- info->physical_sector_size;
- rq->limits.discard_alignment = info->discard_alignment;
+ lim->max_hw_discard_sectors = UINT_MAX;
+ if (info->discard_granularity)
+ lim->discard_granularity = info->discard_granularity;
+ lim->discard_alignment = info->discard_alignment;
if (info->feature_secdiscard)
- blk_queue_max_secure_erase_sectors(rq,
- get_capacity(gd));
+ lim->max_secure_erase_sectors = UINT_MAX;
+ }
+
+ if (info->feature_flush) {
+ lim->features |= BLK_FEAT_WRITE_CACHE;
+ if (info->feature_fua)
+ lim->features |= BLK_FEAT_FUA;
}
/* Hard sector size and max sectors impersonate the equiv. hardware. */
- blk_queue_logical_block_size(rq, info->sector_size);
- blk_queue_physical_block_size(rq, info->physical_sector_size);
- blk_queue_max_hw_sectors(rq, (segments * XEN_PAGE_SIZE) / 512);
+ lim->logical_block_size = info->sector_size;
+ lim->physical_block_size = info->physical_sector_size;
+ lim->max_hw_sectors = (segments * XEN_PAGE_SIZE) / 512;
/* Each segment in a request is up to an aligned page in size. */
- blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
- blk_queue_max_segment_size(rq, PAGE_SIZE);
+ lim->seg_boundary_mask = PAGE_SIZE - 1;
+ lim->max_segment_size = PAGE_SIZE;
/* Ensure a merged request will fit in a single I/O ring slot. */
- blk_queue_max_segments(rq, segments / GRANTS_PER_PSEG);
+ lim->max_segments = segments / GRANTS_PER_PSEG;
/* Make sure buffer addresses are sector-aligned. */
- blk_queue_dma_alignment(rq, 511);
+ lim->dma_alignment = 511;
}
static const char *flush_info(struct blkfront_info *info)
@@ -987,8 +993,6 @@ static const char *flush_info(struct blkfront_info *info)
static void xlvbd_flush(struct blkfront_info *info)
{
- blk_queue_write_cache(info->rq, info->feature_flush ? true : false,
- info->feature_fua ? true : false);
pr_info("blkfront: %s: %s %s %s %s %s %s %s\n",
info->gd->disk_name, flush_info(info),
"persistent grants:", info->feature_persistent ?
@@ -1066,9 +1070,9 @@ static char *encode_disk_name(char *ptr, unsigned int n)
}
static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
- struct blkfront_info *info, u16 sector_size,
- unsigned int physical_sector_size)
+ struct blkfront_info *info)
{
+ struct queue_limits lim = {};
struct gendisk *gd;
int nr_minors = 1;
int err;
@@ -1127,7 +1131,6 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
} else
info->tag_set.queue_depth = BLK_RING_SIZE(info);
info->tag_set.numa_node = NUMA_NO_NODE;
- info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
info->tag_set.cmd_size = sizeof(struct blkif_req);
info->tag_set.driver_data = info;
@@ -1135,7 +1138,8 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
if (err)
goto out_release_minors;
- gd = blk_mq_alloc_disk(&info->tag_set, info);
+ blkif_set_queue_limits(info, &lim);
+ gd = blk_mq_alloc_disk(&info->tag_set, &lim, info);
if (IS_ERR(gd)) {
err = PTR_ERR(gd);
goto out_free_tag_set;
@@ -1159,9 +1163,6 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
info->rq = gd->queue;
info->gd = gd;
- info->sector_size = sector_size;
- info->physical_sector_size = physical_sector_size;
- blkif_set_queue_limits(info);
xlvbd_flush(info);
@@ -1606,8 +1607,8 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
blkif_req(req)->error = BLK_STS_NOTSUPP;
info->feature_discard = 0;
info->feature_secdiscard = 0;
- blk_queue_max_discard_sectors(rq, 0);
- blk_queue_max_secure_erase_sectors(rq, 0);
+ blk_queue_disable_discard(rq);
+ blk_queue_disable_secure_erase(rq);
}
break;
case BLKIF_OP_FLUSH_DISKCACHE:
@@ -1628,7 +1629,6 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
blkif_req(req)->error = BLK_STS_OK;
info->feature_fua = 0;
info->feature_flush = 0;
- xlvbd_flush(info);
}
fallthrough;
case BLKIF_OP_READ:
@@ -2005,18 +2005,19 @@ static int blkfront_probe(struct xenbus_device *dev,
static int blkif_recover(struct blkfront_info *info)
{
+ struct queue_limits lim;
unsigned int r_index;
struct request *req, *n;
int rc;
struct bio *bio;
- unsigned int segs;
struct blkfront_ring_info *rinfo;
+ lim = queue_limits_start_update(info->rq);
blkfront_gather_backend_features(info);
- /* Reset limits changed by blk_mq_update_nr_hw_queues(). */
- blkif_set_queue_limits(info);
- segs = info->max_indirect_segments ? : BLKIF_MAX_SEGMENTS_PER_REQUEST;
- blk_queue_max_segments(info->rq, segs / GRANTS_PER_PSEG);
+ blkif_set_queue_limits(info, &lim);
+ rc = queue_limits_commit_update(info->rq, &lim);
+ if (rc)
+ return rc;
for_each_rinfo(info, rinfo, r_index) {
rc = blkfront_setup_indirect(rinfo);
@@ -2036,7 +2037,9 @@ static int blkif_recover(struct blkfront_info *info)
list_for_each_entry_safe(req, n, &info->requests, queuelist) {
/* Requeue pending requests (flush or discard) */
list_del_init(&req->queuelist);
- BUG_ON(req->nr_phys_segments > segs);
+ BUG_ON(req->nr_phys_segments >
+ (info->max_indirect_segments ? :
+ BLKIF_MAX_SEGMENTS_PER_REQUEST));
blk_mq_requeue_request(req, false);
}
blk_mq_start_stopped_hw_queues(info->rq, true);
@@ -2129,7 +2132,6 @@ static void blkfront_closing(struct blkfront_info *info)
if (info->rq && info->gd) {
blk_mq_stop_hw_queues(info->rq);
blk_mark_disk_dead(info->gd);
- set_capacity(info->gd, 0);
}
for_each_rinfo(info, rinfo, i) {
@@ -2314,8 +2316,6 @@ static void blkfront_gather_backend_features(struct blkfront_info *info)
static void blkfront_connect(struct blkfront_info *info)
{
unsigned long long sectors;
- unsigned long sector_size;
- unsigned int physical_sector_size;
int err, i;
struct blkfront_ring_info *rinfo;
@@ -2354,7 +2354,7 @@ static void blkfront_connect(struct blkfront_info *info)
err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
"sectors", "%llu", &sectors,
"info", "%u", &info->vdisk_info,
- "sector-size", "%lu", &sector_size,
+ "sector-size", "%lu", &info->sector_size,
NULL);
if (err) {
xenbus_dev_fatal(info->xbdev, err,
@@ -2368,9 +2368,9 @@ static void blkfront_connect(struct blkfront_info *info)
* provide this. Assume physical sector size to be the same as
* sector_size in that case.
*/
- physical_sector_size = xenbus_read_unsigned(info->xbdev->otherend,
+ info->physical_sector_size = xenbus_read_unsigned(info->xbdev->otherend,
"physical-sector-size",
- sector_size);
+ info->sector_size);
blkfront_gather_backend_features(info);
for_each_rinfo(info, rinfo, i) {
err = blkfront_setup_indirect(rinfo);
@@ -2382,8 +2382,7 @@ static void blkfront_connect(struct blkfront_info *info)
}
}
- err = xlvbd_alloc_gendisk(sectors, info, sector_size,
- physical_sector_size);
+ err = xlvbd_alloc_gendisk(sectors, info);
if (err) {
xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s",
info->xbdev->otherend);
@@ -2468,7 +2467,7 @@ static void blkback_changed(struct xenbus_device *dev,
}
}
-static int blkfront_remove(struct xenbus_device *xbdev)
+static void blkfront_remove(struct xenbus_device *xbdev)
{
struct blkfront_info *info = dev_get_drvdata(&xbdev->dev);
@@ -2489,7 +2488,6 @@ static int blkfront_remove(struct xenbus_device *xbdev)
}
kfree(info);
- return 0;
}
static int blkfront_is_ready(struct xenbus_device *dev)
diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c
index c1e85f356e4d..8c1c7f4211eb 100644
--- a/drivers/block/z2ram.c
+++ b/drivers/block/z2ram.c
@@ -140,16 +140,14 @@ static void get_chipram(void)
return;
}
-static int z2_open(struct block_device *bdev, fmode_t mode)
+static int z2_open(struct gendisk *disk, blk_mode_t mode)
{
- int device;
+ int device = disk->first_minor;
int max_z2_map = (Z2RAM_SIZE / Z2RAM_CHUNKSIZE) * sizeof(z2ram_map[0]);
int max_chip_map = (amiga_chip_size / Z2RAM_CHUNKSIZE) *
sizeof(z2ram_map[0]);
int rc = -ENOMEM;
- device = MINOR(bdev->bd_dev);
-
mutex_lock(&z2ram_mutex);
if (current_device != -1 && current_device != device) {
rc = -EBUSY;
@@ -290,7 +288,7 @@ err_out:
return rc;
}
-static void z2_release(struct gendisk *disk, fmode_t mode)
+static void z2_release(struct gendisk *disk)
{
mutex_lock(&z2ram_mutex);
if (current_device == -1) {
@@ -320,7 +318,7 @@ static int z2ram_register_disk(int minor)
struct gendisk *disk;
int err;
- disk = blk_mq_alloc_disk(&tag_set, NULL);
+ disk = blk_mq_alloc_disk(&tag_set, NULL, NULL);
if (IS_ERR(disk))
return PTR_ERR(disk);
@@ -356,7 +354,6 @@ static int __init z2_init(void)
tag_set.nr_maps = 1;
tag_set.queue_depth = 16;
tag_set.numa_node = NUMA_NO_NODE;
- tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
ret = blk_mq_alloc_tag_set(&tag_set);
if (ret)
goto out_unregister_blkdev;
@@ -411,4 +408,5 @@ static void __exit z2_exit(void)
module_init(z2_init);
module_exit(z2_exit);
+MODULE_DESCRIPTION("Amiga Zorro II ramdisk driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
new file mode 100644
index 000000000000..77bd6081b244
--- /dev/null
+++ b/drivers/block/zloop.c
@@ -0,0 +1,1507 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025, Christoph Hellwig.
+ * Copyright (c) 2025, Western Digital Corporation or its affiliates.
+ *
+ * Zoned Loop Device driver - exports a zoned block device using one file per
+ * zone as backing storage.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/blk-mq.h>
+#include <linux/blkzoned.h>
+#include <linux/pagemap.h>
+#include <linux/miscdevice.h>
+#include <linux/falloc.h>
+#include <linux/mutex.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
+
+/*
+ * Options for adding (and removing) a device.
+ */
+enum {
+ ZLOOP_OPT_ERR = 0,
+ ZLOOP_OPT_ID = (1 << 0),
+ ZLOOP_OPT_CAPACITY = (1 << 1),
+ ZLOOP_OPT_ZONE_SIZE = (1 << 2),
+ ZLOOP_OPT_ZONE_CAPACITY = (1 << 3),
+ ZLOOP_OPT_NR_CONV_ZONES = (1 << 4),
+ ZLOOP_OPT_BASE_DIR = (1 << 5),
+ ZLOOP_OPT_NR_QUEUES = (1 << 6),
+ ZLOOP_OPT_QUEUE_DEPTH = (1 << 7),
+ ZLOOP_OPT_BUFFERED_IO = (1 << 8),
+ ZLOOP_OPT_ZONE_APPEND = (1 << 9),
+ ZLOOP_OPT_ORDERED_ZONE_APPEND = (1 << 10),
+};
+
+static const match_table_t zloop_opt_tokens = {
+ { ZLOOP_OPT_ID, "id=%d" },
+ { ZLOOP_OPT_CAPACITY, "capacity_mb=%u" },
+ { ZLOOP_OPT_ZONE_SIZE, "zone_size_mb=%u" },
+ { ZLOOP_OPT_ZONE_CAPACITY, "zone_capacity_mb=%u" },
+ { ZLOOP_OPT_NR_CONV_ZONES, "conv_zones=%u" },
+ { ZLOOP_OPT_BASE_DIR, "base_dir=%s" },
+ { ZLOOP_OPT_NR_QUEUES, "nr_queues=%u" },
+ { ZLOOP_OPT_QUEUE_DEPTH, "queue_depth=%u" },
+ { ZLOOP_OPT_BUFFERED_IO, "buffered_io" },
+ { ZLOOP_OPT_ZONE_APPEND, "zone_append=%u" },
+ { ZLOOP_OPT_ORDERED_ZONE_APPEND, "ordered_zone_append" },
+ { ZLOOP_OPT_ERR, NULL }
+};
+
+/* Default values for the "add" operation. */
+#define ZLOOP_DEF_ID -1
+#define ZLOOP_DEF_ZONE_SIZE ((256ULL * SZ_1M) >> SECTOR_SHIFT)
+#define ZLOOP_DEF_NR_ZONES 64
+#define ZLOOP_DEF_NR_CONV_ZONES 8
+#define ZLOOP_DEF_BASE_DIR "/var/local/zloop"
+#define ZLOOP_DEF_NR_QUEUES 1
+#define ZLOOP_DEF_QUEUE_DEPTH 128
+#define ZLOOP_DEF_BUFFERED_IO false
+#define ZLOOP_DEF_ZONE_APPEND true
+#define ZLOOP_DEF_ORDERED_ZONE_APPEND false
+
+/* Arbitrary limit on the zone size (16GB). */
+#define ZLOOP_MAX_ZONE_SIZE_MB 16384
+
+struct zloop_options {
+ unsigned int mask;
+ int id;
+ sector_t capacity;
+ sector_t zone_size;
+ sector_t zone_capacity;
+ unsigned int nr_conv_zones;
+ char *base_dir;
+ unsigned int nr_queues;
+ unsigned int queue_depth;
+ bool buffered_io;
+ bool zone_append;
+ bool ordered_zone_append;
+};
+
+/*
+ * Device states.
+ */
+enum {
+ Zlo_creating = 0,
+ Zlo_live,
+ Zlo_deleting,
+};
+
+enum zloop_zone_flags {
+ ZLOOP_ZONE_CONV = 0,
+ ZLOOP_ZONE_SEQ_ERROR,
+};
+
+struct zloop_zone {
+ struct file *file;
+
+ unsigned long flags;
+ struct mutex lock;
+ spinlock_t wp_lock;
+ enum blk_zone_cond cond;
+ sector_t start;
+ sector_t wp;
+
+ gfp_t old_gfp_mask;
+};
+
+struct zloop_device {
+ unsigned int id;
+ unsigned int state;
+
+ struct blk_mq_tag_set tag_set;
+ struct gendisk *disk;
+
+ struct workqueue_struct *workqueue;
+ bool buffered_io;
+ bool zone_append;
+ bool ordered_zone_append;
+
+ const char *base_dir;
+ struct file *data_dir;
+
+ unsigned int zone_shift;
+ sector_t zone_size;
+ sector_t zone_capacity;
+ unsigned int nr_zones;
+ unsigned int nr_conv_zones;
+ unsigned int block_size;
+
+ struct zloop_zone zones[] __counted_by(nr_zones);
+};
+
+struct zloop_cmd {
+ struct work_struct work;
+ atomic_t ref;
+ sector_t sector;
+ sector_t nr_sectors;
+ long ret;
+ struct kiocb iocb;
+ struct bio_vec *bvec;
+};
+
+static DEFINE_IDR(zloop_index_idr);
+static DEFINE_MUTEX(zloop_ctl_mutex);
+
+static unsigned int rq_zone_no(struct request *rq)
+{
+ struct zloop_device *zlo = rq->q->queuedata;
+
+ return blk_rq_pos(rq) >> zlo->zone_shift;
+}
+
+static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
+{
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ struct kstat stat;
+ sector_t file_sectors;
+ unsigned long flags;
+ int ret;
+
+ lockdep_assert_held(&zone->lock);
+
+ ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0);
+ if (ret < 0) {
+ pr_err("Failed to get zone %u file stat (err=%d)\n",
+ zone_no, ret);
+ set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ return ret;
+ }
+
+ file_sectors = stat.size >> SECTOR_SHIFT;
+ if (file_sectors > zlo->zone_capacity) {
+ pr_err("Zone %u file too large (%llu sectors > %llu)\n",
+ zone_no, file_sectors, zlo->zone_capacity);
+ return -EINVAL;
+ }
+
+ if (file_sectors & ((zlo->block_size >> SECTOR_SHIFT) - 1)) {
+ pr_err("Zone %u file size not aligned to block size %u\n",
+ zone_no, zlo->block_size);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&zone->wp_lock, flags);
+ if (!file_sectors) {
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ zone->wp = zone->start;
+ } else if (file_sectors == zlo->zone_capacity) {
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = ULLONG_MAX;
+ } else {
+ zone->cond = BLK_ZONE_COND_CLOSED;
+ zone->wp = zone->start + file_sectors;
+ }
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+
+ return 0;
+}
+
+static int zloop_open_zone(struct zloop_device *zlo, unsigned int zone_no)
+{
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ int ret = 0;
+
+ if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
+ return -EIO;
+
+ mutex_lock(&zone->lock);
+
+ if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
+ ret = zloop_update_seq_zone(zlo, zone_no);
+ if (ret)
+ goto unlock;
+ }
+
+ switch (zone->cond) {
+ case BLK_ZONE_COND_EXP_OPEN:
+ break;
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_IMP_OPEN:
+ zone->cond = BLK_ZONE_COND_EXP_OPEN;
+ break;
+ case BLK_ZONE_COND_FULL:
+ default:
+ ret = -EIO;
+ break;
+ }
+
+unlock:
+ mutex_unlock(&zone->lock);
+
+ return ret;
+}
+
+static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no)
+{
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ unsigned long flags;
+ int ret = 0;
+
+ if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
+ return -EIO;
+
+ mutex_lock(&zone->lock);
+
+ if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
+ ret = zloop_update_seq_zone(zlo, zone_no);
+ if (ret)
+ goto unlock;
+ }
+
+ switch (zone->cond) {
+ case BLK_ZONE_COND_CLOSED:
+ break;
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ spin_lock_irqsave(&zone->wp_lock, flags);
+ if (zone->wp == zone->start)
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ else
+ zone->cond = BLK_ZONE_COND_CLOSED;
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+ break;
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ default:
+ ret = -EIO;
+ break;
+ }
+
+unlock:
+ mutex_unlock(&zone->lock);
+
+ return ret;
+}
+
+static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no)
+{
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ unsigned long flags;
+ int ret = 0;
+
+ if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
+ return -EIO;
+
+ mutex_lock(&zone->lock);
+
+ if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) &&
+ zone->cond == BLK_ZONE_COND_EMPTY)
+ goto unlock;
+
+ if (vfs_truncate(&zone->file->f_path, 0)) {
+ set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ spin_lock_irqsave(&zone->wp_lock, flags);
+ zone->cond = BLK_ZONE_COND_EMPTY;
+ zone->wp = zone->start;
+ clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+
+unlock:
+ mutex_unlock(&zone->lock);
+
+ return ret;
+}
+
+static int zloop_reset_all_zones(struct zloop_device *zlo)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = zlo->nr_conv_zones; i < zlo->nr_zones; i++) {
+ ret = zloop_reset_zone(zlo, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
+{
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ unsigned long flags;
+ int ret = 0;
+
+ if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
+ return -EIO;
+
+ mutex_lock(&zone->lock);
+
+ if (!test_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags) &&
+ zone->cond == BLK_ZONE_COND_FULL)
+ goto unlock;
+
+ if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) {
+ set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ spin_lock_irqsave(&zone->wp_lock, flags);
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = ULLONG_MAX;
+ clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+
+ unlock:
+ mutex_unlock(&zone->lock);
+
+ return ret;
+}
+
+static void zloop_put_cmd(struct zloop_cmd *cmd)
+{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+
+ if (!atomic_dec_and_test(&cmd->ref))
+ return;
+ kfree(cmd->bvec);
+ cmd->bvec = NULL;
+ if (likely(!blk_should_fake_timeout(rq->q)))
+ blk_mq_complete_request(rq);
+}
+
+static void zloop_rw_complete(struct kiocb *iocb, long ret)
+{
+ struct zloop_cmd *cmd = container_of(iocb, struct zloop_cmd, iocb);
+
+ cmd->ret = ret;
+ zloop_put_cmd(cmd);
+}
+
+static void zloop_rw(struct zloop_cmd *cmd)
+{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+ struct zloop_device *zlo = rq->q->queuedata;
+ unsigned int zone_no = rq_zone_no(rq);
+ sector_t sector = blk_rq_pos(rq);
+ sector_t nr_sectors = blk_rq_sectors(rq);
+ bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND;
+ bool is_write = req_op(rq) == REQ_OP_WRITE || is_append;
+ int rw = is_write ? ITER_SOURCE : ITER_DEST;
+ struct req_iterator rq_iter;
+ struct zloop_zone *zone;
+ struct iov_iter iter;
+ struct bio_vec tmp;
+ unsigned long flags;
+ sector_t zone_end;
+ unsigned int nr_bvec;
+ int ret;
+
+ atomic_set(&cmd->ref, 2);
+ cmd->sector = sector;
+ cmd->nr_sectors = nr_sectors;
+ cmd->ret = 0;
+
+ if (WARN_ON_ONCE(is_append && !zlo->zone_append)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* We should never get an I/O beyond the device capacity. */
+ if (WARN_ON_ONCE(zone_no >= zlo->nr_zones)) {
+ ret = -EIO;
+ goto out;
+ }
+ zone = &zlo->zones[zone_no];
+ zone_end = zone->start + zlo->zone_capacity;
+
+ /*
+ * The block layer should never send requests that are not fully
+ * contained within the zone.
+ */
+ if (WARN_ON_ONCE(sector + nr_sectors > zone->start + zlo->zone_size)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
+ mutex_lock(&zone->lock);
+ ret = zloop_update_seq_zone(zlo, zone_no);
+ mutex_unlock(&zone->lock);
+ if (ret)
+ goto out;
+ }
+
+ if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write) {
+ mutex_lock(&zone->lock);
+
+ spin_lock_irqsave(&zone->wp_lock, flags);
+
+ /*
+ * Zone append operations always go at the current write
+ * pointer, but regular write operations must already be
+ * aligned to the write pointer when submitted.
+ */
+ if (is_append) {
+ /*
+ * If ordered zone append is in use, we already checked
+ * and set the target sector in zloop_queue_rq().
+ */
+ if (!zlo->ordered_zone_append) {
+ if (zone->cond == BLK_ZONE_COND_FULL ||
+ zone->wp + nr_sectors > zone_end) {
+ spin_unlock_irqrestore(&zone->wp_lock,
+ flags);
+ ret = -EIO;
+ goto unlock;
+ }
+ sector = zone->wp;
+ }
+ cmd->sector = sector;
+ } else if (sector != zone->wp) {
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+ pr_err("Zone %u: unaligned write: sect %llu, wp %llu\n",
+ zone_no, sector, zone->wp);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /* Implicitly open the target zone. */
+ if (zone->cond == BLK_ZONE_COND_CLOSED ||
+ zone->cond == BLK_ZONE_COND_EMPTY)
+ zone->cond = BLK_ZONE_COND_IMP_OPEN;
+
+ /*
+ * Advance the write pointer, unless ordered zone append is in
+ * use. If the write fails, the write pointer position will be
+ * corrected when the next I/O starts execution.
+ */
+ if (!is_append || !zlo->ordered_zone_append) {
+ zone->wp += nr_sectors;
+ if (zone->wp == zone_end) {
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = ULLONG_MAX;
+ }
+ }
+
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+ }
+
+ nr_bvec = blk_rq_nr_bvec(rq);
+
+ if (rq->bio != rq->biotail) {
+ struct bio_vec *bvec;
+
+ cmd->bvec = kmalloc_array(nr_bvec, sizeof(*cmd->bvec), GFP_NOIO);
+ if (!cmd->bvec) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /*
+ * The bios of the request may be started from the middle of
+ * the 'bvec' because of bio splitting, so we can't directly
+ * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec
+ * API will take care of all details for us.
+ */
+ bvec = cmd->bvec;
+ rq_for_each_bvec(tmp, rq, rq_iter) {
+ *bvec = tmp;
+ bvec++;
+ }
+ iov_iter_bvec(&iter, rw, cmd->bvec, nr_bvec, blk_rq_bytes(rq));
+ } else {
+ /*
+ * Same here, this bio may be started from the middle of the
+ * 'bvec' because of bio splitting, so offset from the bvec
+ * must be passed to iov iterator
+ */
+ iov_iter_bvec(&iter, rw,
+ __bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter),
+ nr_bvec, blk_rq_bytes(rq));
+ iter.iov_offset = rq->bio->bi_iter.bi_bvec_done;
+ }
+
+ cmd->iocb.ki_pos = (sector - zone->start) << SECTOR_SHIFT;
+ cmd->iocb.ki_filp = zone->file;
+ cmd->iocb.ki_complete = zloop_rw_complete;
+ if (!zlo->buffered_io)
+ cmd->iocb.ki_flags = IOCB_DIRECT;
+ cmd->iocb.ki_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0);
+
+ if (rw == ITER_SOURCE)
+ ret = zone->file->f_op->write_iter(&cmd->iocb, &iter);
+ else
+ ret = zone->file->f_op->read_iter(&cmd->iocb, &iter);
+unlock:
+ if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write)
+ mutex_unlock(&zone->lock);
+out:
+ if (ret != -EIOCBQUEUED)
+ zloop_rw_complete(&cmd->iocb, ret);
+ zloop_put_cmd(cmd);
+}
+
+static void zloop_handle_cmd(struct zloop_cmd *cmd)
+{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+ struct zloop_device *zlo = rq->q->queuedata;
+
+ /* We can block in this context, so ignore REQ_NOWAIT. */
+ if (rq->cmd_flags & REQ_NOWAIT)
+ rq->cmd_flags &= ~REQ_NOWAIT;
+
+ switch (req_op(rq)) {
+ case REQ_OP_READ:
+ case REQ_OP_WRITE:
+ case REQ_OP_ZONE_APPEND:
+ /*
+ * zloop_rw() always executes asynchronously or completes
+ * directly.
+ */
+ zloop_rw(cmd);
+ return;
+ case REQ_OP_FLUSH:
+ /*
+ * Sync the entire FS containing the zone files instead of
+ * walking all files
+ */
+ cmd->ret = sync_filesystem(file_inode(zlo->data_dir)->i_sb);
+ break;
+ case REQ_OP_ZONE_RESET:
+ cmd->ret = zloop_reset_zone(zlo, rq_zone_no(rq));
+ break;
+ case REQ_OP_ZONE_RESET_ALL:
+ cmd->ret = zloop_reset_all_zones(zlo);
+ break;
+ case REQ_OP_ZONE_FINISH:
+ cmd->ret = zloop_finish_zone(zlo, rq_zone_no(rq));
+ break;
+ case REQ_OP_ZONE_OPEN:
+ cmd->ret = zloop_open_zone(zlo, rq_zone_no(rq));
+ break;
+ case REQ_OP_ZONE_CLOSE:
+ cmd->ret = zloop_close_zone(zlo, rq_zone_no(rq));
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ pr_err("Unsupported operation %d\n", req_op(rq));
+ cmd->ret = -EOPNOTSUPP;
+ break;
+ }
+
+ blk_mq_complete_request(rq);
+}
+
+static void zloop_cmd_workfn(struct work_struct *work)
+{
+ struct zloop_cmd *cmd = container_of(work, struct zloop_cmd, work);
+ int orig_flags = current->flags;
+
+ current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
+ zloop_handle_cmd(cmd);
+ current->flags = orig_flags;
+}
+
+static void zloop_complete_rq(struct request *rq)
+{
+ struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct zloop_device *zlo = rq->q->queuedata;
+ unsigned int zone_no = cmd->sector >> zlo->zone_shift;
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ blk_status_t sts = BLK_STS_OK;
+
+ switch (req_op(rq)) {
+ case REQ_OP_READ:
+ if (cmd->ret < 0)
+ pr_err("Zone %u: failed read sector %llu, %llu sectors\n",
+ zone_no, cmd->sector, cmd->nr_sectors);
+
+ if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
+ /* short read */
+ struct bio *bio;
+
+ __rq_for_each_bio(bio, rq)
+ zero_fill_bio(bio);
+ }
+ break;
+ case REQ_OP_WRITE:
+ case REQ_OP_ZONE_APPEND:
+ if (cmd->ret < 0)
+ pr_err("Zone %u: failed %swrite sector %llu, %llu sectors\n",
+ zone_no,
+ req_op(rq) == REQ_OP_WRITE ? "" : "append ",
+ cmd->sector, cmd->nr_sectors);
+
+ if (cmd->ret >= 0 && cmd->ret != blk_rq_bytes(rq)) {
+ pr_err("Zone %u: partial write %ld/%u B\n",
+ zone_no, cmd->ret, blk_rq_bytes(rq));
+ cmd->ret = -EIO;
+ }
+
+ if (cmd->ret < 0 && !test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
+ /*
+ * A write to a sequential zone file failed: mark the
+ * zone as having an error. This will be corrected and
+ * cleared when the next IO is submitted.
+ */
+ set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ break;
+ }
+ if (req_op(rq) == REQ_OP_ZONE_APPEND)
+ rq->__sector = cmd->sector;
+
+ break;
+ default:
+ break;
+ }
+
+ if (cmd->ret < 0)
+ sts = errno_to_blk_status(cmd->ret);
+ blk_mq_end_request(rq, sts);
+}
+
+static bool zloop_set_zone_append_sector(struct request *rq)
+{
+ struct zloop_device *zlo = rq->q->queuedata;
+ unsigned int zone_no = rq_zone_no(rq);
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ sector_t zone_end = zone->start + zlo->zone_capacity;
+ sector_t nr_sectors = blk_rq_sectors(rq);
+ unsigned long flags;
+
+ spin_lock_irqsave(&zone->wp_lock, flags);
+
+ if (zone->cond == BLK_ZONE_COND_FULL ||
+ zone->wp + nr_sectors > zone_end) {
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+ return false;
+ }
+
+ rq->__sector = zone->wp;
+ zone->wp += blk_rq_sectors(rq);
+ if (zone->wp >= zone_end) {
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = ULLONG_MAX;
+ }
+
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+
+ return true;
+}
+
+static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx,
+ const struct blk_mq_queue_data *bd)
+{
+ struct request *rq = bd->rq;
+ struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct zloop_device *zlo = rq->q->queuedata;
+
+ if (zlo->state == Zlo_deleting)
+ return BLK_STS_IOERR;
+
+ /*
+ * If we need to strongly order zone append operations, set the request
+ * sector to the zone write pointer location now instead of when the
+ * command work runs.
+ */
+ if (zlo->ordered_zone_append && req_op(rq) == REQ_OP_ZONE_APPEND) {
+ if (!zloop_set_zone_append_sector(rq))
+ return BLK_STS_IOERR;
+ }
+
+ blk_mq_start_request(rq);
+
+ INIT_WORK(&cmd->work, zloop_cmd_workfn);
+ queue_work(zlo->workqueue, &cmd->work);
+
+ return BLK_STS_OK;
+}
+
+static const struct blk_mq_ops zloop_mq_ops = {
+ .queue_rq = zloop_queue_rq,
+ .complete = zloop_complete_rq,
+};
+
+static int zloop_open(struct gendisk *disk, blk_mode_t mode)
+{
+ struct zloop_device *zlo = disk->private_data;
+ int ret;
+
+ ret = mutex_lock_killable(&zloop_ctl_mutex);
+ if (ret)
+ return ret;
+
+ if (zlo->state != Zlo_live)
+ ret = -ENXIO;
+ mutex_unlock(&zloop_ctl_mutex);
+ return ret;
+}
+
+static int zloop_report_zones(struct gendisk *disk, sector_t sector,
+ unsigned int nr_zones, struct blk_report_zones_args *args)
+{
+ struct zloop_device *zlo = disk->private_data;
+ struct blk_zone blkz = {};
+ unsigned int first, i;
+ unsigned long flags;
+ int ret;
+
+ first = disk_zone_no(disk, sector);
+ if (first >= zlo->nr_zones)
+ return 0;
+ nr_zones = min(nr_zones, zlo->nr_zones - first);
+
+ for (i = 0; i < nr_zones; i++) {
+ unsigned int zone_no = first + i;
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+
+ mutex_lock(&zone->lock);
+
+ if (test_and_clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags)) {
+ ret = zloop_update_seq_zone(zlo, zone_no);
+ if (ret) {
+ mutex_unlock(&zone->lock);
+ return ret;
+ }
+ }
+
+ blkz.start = zone->start;
+ blkz.len = zlo->zone_size;
+ spin_lock_irqsave(&zone->wp_lock, flags);
+ blkz.wp = zone->wp;
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+ blkz.cond = zone->cond;
+ if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
+ blkz.type = BLK_ZONE_TYPE_CONVENTIONAL;
+ blkz.capacity = zlo->zone_size;
+ } else {
+ blkz.type = BLK_ZONE_TYPE_SEQWRITE_REQ;
+ blkz.capacity = zlo->zone_capacity;
+ }
+
+ mutex_unlock(&zone->lock);
+
+ ret = disk_report_zone(disk, &blkz, i, args);
+ if (ret)
+ return ret;
+ }
+
+ return nr_zones;
+}
+
+static void zloop_free_disk(struct gendisk *disk)
+{
+ struct zloop_device *zlo = disk->private_data;
+ unsigned int i;
+
+ blk_mq_free_tag_set(&zlo->tag_set);
+
+ for (i = 0; i < zlo->nr_zones; i++) {
+ struct zloop_zone *zone = &zlo->zones[i];
+
+ mapping_set_gfp_mask(zone->file->f_mapping,
+ zone->old_gfp_mask);
+ fput(zone->file);
+ }
+
+ fput(zlo->data_dir);
+ destroy_workqueue(zlo->workqueue);
+ kfree(zlo->base_dir);
+ kvfree(zlo);
+}
+
+static const struct block_device_operations zloop_fops = {
+ .owner = THIS_MODULE,
+ .open = zloop_open,
+ .report_zones = zloop_report_zones,
+ .free_disk = zloop_free_disk,
+};
+
+__printf(3, 4)
+static struct file *zloop_filp_open_fmt(int oflags, umode_t mode,
+ const char *fmt, ...)
+{
+ struct file *file;
+ va_list ap;
+ char *p;
+
+ va_start(ap, fmt);
+ p = kvasprintf(GFP_KERNEL, fmt, ap);
+ va_end(ap);
+
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+ file = filp_open(p, oflags, mode);
+ kfree(p);
+ return file;
+}
+
+static int zloop_get_block_size(struct zloop_device *zlo,
+ struct zloop_zone *zone)
+{
+ struct block_device *sb_bdev = zone->file->f_mapping->host->i_sb->s_bdev;
+ struct kstat st;
+
+ /*
+ * If the FS block size is lower than or equal to 4K, use that as the
+ * device block size. Otherwise, fallback to the FS direct IO alignment
+ * constraint if that is provided, and to the FS underlying device
+ * physical block size if the direct IO alignment is unknown.
+ */
+ if (file_inode(zone->file)->i_sb->s_blocksize <= SZ_4K)
+ zlo->block_size = file_inode(zone->file)->i_sb->s_blocksize;
+ else if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
+ (st.result_mask & STATX_DIOALIGN))
+ zlo->block_size = st.dio_offset_align;
+ else if (sb_bdev)
+ zlo->block_size = bdev_physical_block_size(sb_bdev);
+ else
+ zlo->block_size = SECTOR_SIZE;
+
+ if (zlo->zone_capacity & ((zlo->block_size >> SECTOR_SHIFT) - 1)) {
+ pr_err("Zone capacity is not aligned to block size %u\n",
+ zlo->block_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zloop_init_zone(struct zloop_device *zlo, struct zloop_options *opts,
+ unsigned int zone_no, bool restore)
+{
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ int oflags = O_RDWR;
+ struct kstat stat;
+ sector_t file_sectors;
+ int ret;
+
+ mutex_init(&zone->lock);
+ spin_lock_init(&zone->wp_lock);
+ zone->start = (sector_t)zone_no << zlo->zone_shift;
+
+ if (!restore)
+ oflags |= O_CREAT;
+
+ if (!opts->buffered_io)
+ oflags |= O_DIRECT;
+
+ if (zone_no < zlo->nr_conv_zones) {
+ /* Conventional zone file. */
+ set_bit(ZLOOP_ZONE_CONV, &zone->flags);
+ zone->cond = BLK_ZONE_COND_NOT_WP;
+ zone->wp = U64_MAX;
+
+ zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/cnv-%06u",
+ zlo->base_dir, zlo->id, zone_no);
+ if (IS_ERR(zone->file)) {
+ pr_err("Failed to open zone %u file %s/%u/cnv-%06u (err=%ld)",
+ zone_no, zlo->base_dir, zlo->id, zone_no,
+ PTR_ERR(zone->file));
+ return PTR_ERR(zone->file);
+ }
+
+ if (!zlo->block_size) {
+ ret = zloop_get_block_size(zlo, zone);
+ if (ret)
+ return ret;
+ }
+
+ ret = vfs_getattr(&zone->file->f_path, &stat, STATX_SIZE, 0);
+ if (ret < 0) {
+ pr_err("Failed to get zone %u file stat\n", zone_no);
+ return ret;
+ }
+ file_sectors = stat.size >> SECTOR_SHIFT;
+
+ if (restore && file_sectors != zlo->zone_size) {
+ pr_err("Invalid conventional zone %u file size (%llu sectors != %llu)\n",
+ zone_no, file_sectors, zlo->zone_capacity);
+ return ret;
+ }
+
+ ret = vfs_truncate(&zone->file->f_path,
+ zlo->zone_size << SECTOR_SHIFT);
+ if (ret < 0) {
+ pr_err("Failed to truncate zone %u file (err=%d)\n",
+ zone_no, ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+ /* Sequential zone file. */
+ zone->file = zloop_filp_open_fmt(oflags, 0600, "%s/%u/seq-%06u",
+ zlo->base_dir, zlo->id, zone_no);
+ if (IS_ERR(zone->file)) {
+ pr_err("Failed to open zone %u file %s/%u/seq-%06u (err=%ld)",
+ zone_no, zlo->base_dir, zlo->id, zone_no,
+ PTR_ERR(zone->file));
+ return PTR_ERR(zone->file);
+ }
+
+ if (!zlo->block_size) {
+ ret = zloop_get_block_size(zlo, zone);
+ if (ret)
+ return ret;
+ }
+
+ zloop_get_block_size(zlo, zone);
+
+ mutex_lock(&zone->lock);
+ ret = zloop_update_seq_zone(zlo, zone_no);
+ mutex_unlock(&zone->lock);
+
+ return ret;
+}
+
+static bool zloop_dev_exists(struct zloop_device *zlo)
+{
+ struct file *cnv, *seq;
+ bool exists;
+
+ cnv = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/cnv-%06u",
+ zlo->base_dir, zlo->id, 0);
+ seq = zloop_filp_open_fmt(O_RDONLY, 0600, "%s/%u/seq-%06u",
+ zlo->base_dir, zlo->id, 0);
+ exists = !IS_ERR(cnv) || !IS_ERR(seq);
+
+ if (!IS_ERR(cnv))
+ fput(cnv);
+ if (!IS_ERR(seq))
+ fput(seq);
+
+ return exists;
+}
+
+static int zloop_ctl_add(struct zloop_options *opts)
+{
+ struct queue_limits lim = {
+ .max_hw_sectors = SZ_1M >> SECTOR_SHIFT,
+ .chunk_sectors = opts->zone_size,
+ .features = BLK_FEAT_ZONED,
+ };
+ unsigned int nr_zones, i, j;
+ struct zloop_device *zlo;
+ int ret = -EINVAL;
+ bool restore;
+
+ __module_get(THIS_MODULE);
+
+ nr_zones = opts->capacity >> ilog2(opts->zone_size);
+ if (opts->nr_conv_zones >= nr_zones) {
+ pr_err("Invalid number of conventional zones %u\n",
+ opts->nr_conv_zones);
+ goto out;
+ }
+
+ zlo = kvzalloc(struct_size(zlo, zones, nr_zones), GFP_KERNEL);
+ if (!zlo) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ zlo->state = Zlo_creating;
+
+ ret = mutex_lock_killable(&zloop_ctl_mutex);
+ if (ret)
+ goto out_free_dev;
+
+ /* Allocate id, if @opts->id >= 0, we're requesting that specific id */
+ if (opts->id >= 0) {
+ ret = idr_alloc(&zloop_index_idr, zlo,
+ opts->id, opts->id + 1, GFP_KERNEL);
+ if (ret == -ENOSPC)
+ ret = -EEXIST;
+ } else {
+ ret = idr_alloc(&zloop_index_idr, zlo, 0, 0, GFP_KERNEL);
+ }
+ mutex_unlock(&zloop_ctl_mutex);
+ if (ret < 0)
+ goto out_free_dev;
+
+ zlo->id = ret;
+ zlo->zone_shift = ilog2(opts->zone_size);
+ zlo->zone_size = opts->zone_size;
+ if (opts->zone_capacity)
+ zlo->zone_capacity = opts->zone_capacity;
+ else
+ zlo->zone_capacity = zlo->zone_size;
+ zlo->nr_zones = nr_zones;
+ zlo->nr_conv_zones = opts->nr_conv_zones;
+ zlo->buffered_io = opts->buffered_io;
+ zlo->zone_append = opts->zone_append;
+ if (zlo->zone_append)
+ zlo->ordered_zone_append = opts->ordered_zone_append;
+
+ zlo->workqueue = alloc_workqueue("zloop%d", WQ_UNBOUND | WQ_FREEZABLE,
+ opts->nr_queues * opts->queue_depth, zlo->id);
+ if (!zlo->workqueue) {
+ ret = -ENOMEM;
+ goto out_free_idr;
+ }
+
+ if (opts->base_dir)
+ zlo->base_dir = kstrdup(opts->base_dir, GFP_KERNEL);
+ else
+ zlo->base_dir = kstrdup(ZLOOP_DEF_BASE_DIR, GFP_KERNEL);
+ if (!zlo->base_dir) {
+ ret = -ENOMEM;
+ goto out_destroy_workqueue;
+ }
+
+ zlo->data_dir = zloop_filp_open_fmt(O_RDONLY | O_DIRECTORY, 0, "%s/%u",
+ zlo->base_dir, zlo->id);
+ if (IS_ERR(zlo->data_dir)) {
+ ret = PTR_ERR(zlo->data_dir);
+ pr_warn("Failed to open directory %s/%u (err=%d)\n",
+ zlo->base_dir, zlo->id, ret);
+ goto out_free_base_dir;
+ }
+
+ /*
+ * If we already have zone files, we are restoring a device created by a
+ * previous add operation. In this case, zloop_init_zone() will check
+ * that the zone files are consistent with the zone configuration given.
+ */
+ restore = zloop_dev_exists(zlo);
+ for (i = 0; i < nr_zones; i++) {
+ ret = zloop_init_zone(zlo, opts, i, restore);
+ if (ret)
+ goto out_close_files;
+ }
+
+ lim.physical_block_size = zlo->block_size;
+ lim.logical_block_size = zlo->block_size;
+ if (zlo->zone_append)
+ lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
+
+ zlo->tag_set.ops = &zloop_mq_ops;
+ zlo->tag_set.nr_hw_queues = opts->nr_queues;
+ zlo->tag_set.queue_depth = opts->queue_depth;
+ zlo->tag_set.numa_node = NUMA_NO_NODE;
+ zlo->tag_set.cmd_size = sizeof(struct zloop_cmd);
+ zlo->tag_set.driver_data = zlo;
+
+ ret = blk_mq_alloc_tag_set(&zlo->tag_set);
+ if (ret) {
+ pr_err("blk_mq_alloc_tag_set failed (err=%d)\n", ret);
+ goto out_close_files;
+ }
+
+ zlo->disk = blk_mq_alloc_disk(&zlo->tag_set, &lim, zlo);
+ if (IS_ERR(zlo->disk)) {
+ pr_err("blk_mq_alloc_disk failed (err=%d)\n", ret);
+ ret = PTR_ERR(zlo->disk);
+ goto out_cleanup_tags;
+ }
+ zlo->disk->flags = GENHD_FL_NO_PART;
+ zlo->disk->fops = &zloop_fops;
+ zlo->disk->private_data = zlo;
+ sprintf(zlo->disk->disk_name, "zloop%d", zlo->id);
+ set_capacity(zlo->disk, (u64)lim.chunk_sectors * zlo->nr_zones);
+
+ ret = blk_revalidate_disk_zones(zlo->disk);
+ if (ret)
+ goto out_cleanup_disk;
+
+ ret = add_disk(zlo->disk);
+ if (ret) {
+ pr_err("add_disk failed (err=%d)\n", ret);
+ goto out_cleanup_disk;
+ }
+
+ mutex_lock(&zloop_ctl_mutex);
+ zlo->state = Zlo_live;
+ mutex_unlock(&zloop_ctl_mutex);
+
+ pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n",
+ zlo->id, zlo->nr_zones,
+ ((sector_t)zlo->zone_size << SECTOR_SHIFT) >> 20,
+ zlo->block_size);
+ pr_info("zloop%d: using %s%s zone append\n",
+ zlo->id,
+ zlo->ordered_zone_append ? "ordered " : "",
+ zlo->zone_append ? "native" : "emulated");
+
+ return 0;
+
+out_cleanup_disk:
+ put_disk(zlo->disk);
+out_cleanup_tags:
+ blk_mq_free_tag_set(&zlo->tag_set);
+out_close_files:
+ for (j = 0; j < i; j++) {
+ struct zloop_zone *zone = &zlo->zones[j];
+
+ if (!IS_ERR_OR_NULL(zone->file))
+ fput(zone->file);
+ }
+ fput(zlo->data_dir);
+out_free_base_dir:
+ kfree(zlo->base_dir);
+out_destroy_workqueue:
+ destroy_workqueue(zlo->workqueue);
+out_free_idr:
+ mutex_lock(&zloop_ctl_mutex);
+ idr_remove(&zloop_index_idr, zlo->id);
+ mutex_unlock(&zloop_ctl_mutex);
+out_free_dev:
+ kvfree(zlo);
+out:
+ module_put(THIS_MODULE);
+ if (ret == -ENOENT)
+ ret = -EINVAL;
+ return ret;
+}
+
+static int zloop_ctl_remove(struct zloop_options *opts)
+{
+ struct zloop_device *zlo;
+ int ret;
+
+ if (!(opts->mask & ZLOOP_OPT_ID)) {
+ pr_err("No ID specified\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_killable(&zloop_ctl_mutex);
+ if (ret)
+ return ret;
+
+ zlo = idr_find(&zloop_index_idr, opts->id);
+ if (!zlo || zlo->state == Zlo_creating) {
+ ret = -ENODEV;
+ } else if (zlo->state == Zlo_deleting) {
+ ret = -EINVAL;
+ } else {
+ idr_remove(&zloop_index_idr, zlo->id);
+ zlo->state = Zlo_deleting;
+ }
+
+ mutex_unlock(&zloop_ctl_mutex);
+ if (ret)
+ return ret;
+
+ del_gendisk(zlo->disk);
+ put_disk(zlo->disk);
+
+ pr_info("Removed device %d\n", opts->id);
+
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static int zloop_parse_options(struct zloop_options *opts, const char *buf)
+{
+ substring_t args[MAX_OPT_ARGS];
+ char *options, *o, *p;
+ unsigned int token;
+ int ret = 0;
+
+ /* Set defaults. */
+ opts->mask = 0;
+ opts->id = ZLOOP_DEF_ID;
+ opts->capacity = ZLOOP_DEF_ZONE_SIZE * ZLOOP_DEF_NR_ZONES;
+ opts->zone_size = ZLOOP_DEF_ZONE_SIZE;
+ opts->nr_conv_zones = ZLOOP_DEF_NR_CONV_ZONES;
+ opts->nr_queues = ZLOOP_DEF_NR_QUEUES;
+ opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH;
+ opts->buffered_io = ZLOOP_DEF_BUFFERED_IO;
+ opts->zone_append = ZLOOP_DEF_ZONE_APPEND;
+ opts->ordered_zone_append = ZLOOP_DEF_ORDERED_ZONE_APPEND;
+
+ if (!buf)
+ return 0;
+
+ /* Skip leading spaces before the options. */
+ while (isspace(*buf))
+ buf++;
+
+ options = o = kstrdup(buf, GFP_KERNEL);
+ if (!options)
+ return -ENOMEM;
+
+ /* Parse the options, doing only some light invalid value checks. */
+ while ((p = strsep(&o, ",\n")) != NULL) {
+ if (!*p)
+ continue;
+
+ token = match_token(p, zloop_opt_tokens, args);
+ opts->mask |= token;
+ switch (token) {
+ case ZLOOP_OPT_ID:
+ if (match_int(args, &opts->id)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
+ case ZLOOP_OPT_CAPACITY:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!token) {
+ pr_err("Invalid capacity\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->capacity =
+ ((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
+ break;
+ case ZLOOP_OPT_ZONE_SIZE:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!token || token > ZLOOP_MAX_ZONE_SIZE_MB ||
+ !is_power_of_2(token)) {
+ pr_err("Invalid zone size %u\n", token);
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->zone_size =
+ ((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
+ break;
+ case ZLOOP_OPT_ZONE_CAPACITY:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!token) {
+ pr_err("Invalid zone capacity\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->zone_capacity =
+ ((sector_t)token * SZ_1M) >> SECTOR_SHIFT;
+ break;
+ case ZLOOP_OPT_NR_CONV_ZONES:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->nr_conv_zones = token;
+ break;
+ case ZLOOP_OPT_BASE_DIR:
+ p = match_strdup(args);
+ if (!p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ kfree(opts->base_dir);
+ opts->base_dir = p;
+ break;
+ case ZLOOP_OPT_NR_QUEUES:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!token) {
+ pr_err("Invalid number of queues\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->nr_queues = min(token, num_online_cpus());
+ break;
+ case ZLOOP_OPT_QUEUE_DEPTH:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!token) {
+ pr_err("Invalid queue depth\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->queue_depth = token;
+ break;
+ case ZLOOP_OPT_BUFFERED_IO:
+ opts->buffered_io = true;
+ break;
+ case ZLOOP_OPT_ZONE_APPEND:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (token != 0 && token != 1) {
+ pr_err("Invalid zone_append value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->zone_append = token;
+ break;
+ case ZLOOP_OPT_ORDERED_ZONE_APPEND:
+ opts->ordered_zone_append = true;
+ break;
+ case ZLOOP_OPT_ERR:
+ default:
+ pr_warn("unknown parameter or missing value '%s'\n", p);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ ret = -EINVAL;
+ if (opts->capacity <= opts->zone_size) {
+ pr_err("Invalid capacity\n");
+ goto out;
+ }
+
+ if (opts->zone_capacity > opts->zone_size) {
+ pr_err("Invalid zone capacity\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+ kfree(options);
+ return ret;
+}
+
+enum {
+ ZLOOP_CTL_ADD,
+ ZLOOP_CTL_REMOVE,
+};
+
+static struct zloop_ctl_op {
+ int code;
+ const char *name;
+} zloop_ctl_ops[] = {
+ { ZLOOP_CTL_ADD, "add" },
+ { ZLOOP_CTL_REMOVE, "remove" },
+ { -1, NULL },
+};
+
+static ssize_t zloop_ctl_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *pos)
+{
+ struct zloop_options opts = { };
+ struct zloop_ctl_op *op;
+ const char *buf, *opts_buf;
+ int i, ret;
+
+ if (count > PAGE_SIZE)
+ return -ENOMEM;
+
+ buf = memdup_user_nul(ubuf, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ for (i = 0; i < ARRAY_SIZE(zloop_ctl_ops); i++) {
+ op = &zloop_ctl_ops[i];
+ if (!op->name) {
+ pr_err("Invalid operation\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!strncmp(buf, op->name, strlen(op->name)))
+ break;
+ }
+
+ if (count <= strlen(op->name))
+ opts_buf = NULL;
+ else
+ opts_buf = buf + strlen(op->name);
+
+ ret = zloop_parse_options(&opts, opts_buf);
+ if (ret) {
+ pr_err("Failed to parse options\n");
+ goto out;
+ }
+
+ switch (op->code) {
+ case ZLOOP_CTL_ADD:
+ ret = zloop_ctl_add(&opts);
+ break;
+ case ZLOOP_CTL_REMOVE:
+ ret = zloop_ctl_remove(&opts);
+ break;
+ default:
+ pr_err("Invalid operation\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ kfree(opts.base_dir);
+ kfree(buf);
+ return ret ? ret : count;
+}
+
+static int zloop_ctl_show(struct seq_file *seq_file, void *private)
+{
+ const struct match_token *tok;
+ int i;
+
+ /* Add operation */
+ seq_printf(seq_file, "%s ", zloop_ctl_ops[0].name);
+ for (i = 0; i < ARRAY_SIZE(zloop_opt_tokens); i++) {
+ tok = &zloop_opt_tokens[i];
+ if (!tok->pattern)
+ break;
+ if (i)
+ seq_putc(seq_file, ',');
+ seq_puts(seq_file, tok->pattern);
+ }
+ seq_putc(seq_file, '\n');
+
+ /* Remove operation */
+ seq_puts(seq_file, zloop_ctl_ops[1].name);
+ seq_puts(seq_file, " id=%d\n");
+
+ return 0;
+}
+
+static int zloop_ctl_open(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return single_open(file, zloop_ctl_show, NULL);
+}
+
+static int zloop_ctl_release(struct inode *inode, struct file *file)
+{
+ return single_release(inode, file);
+}
+
+static const struct file_operations zloop_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = zloop_ctl_open,
+ .release = zloop_ctl_release,
+ .write = zloop_ctl_write,
+ .read = seq_read,
+};
+
+static struct miscdevice zloop_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "zloop-control",
+ .fops = &zloop_ctl_fops,
+};
+
+static int __init zloop_init(void)
+{
+ int ret;
+
+ ret = misc_register(&zloop_misc);
+ if (ret) {
+ pr_err("Failed to register misc device: %d\n", ret);
+ return ret;
+ }
+ pr_info("Module loaded\n");
+
+ return 0;
+}
+
+static void __exit zloop_exit(void)
+{
+ misc_deregister(&zloop_misc);
+ idr_destroy(&zloop_index_idr);
+}
+
+module_init(zloop_init);
+module_exit(zloop_exit);
+
+MODULE_DESCRIPTION("Zoned loopback device");
+MODULE_LICENSE("GPL");
diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig
index d4100b0c083e..402b7b175863 100644
--- a/drivers/block/zram/Kconfig
+++ b/drivers/block/zram/Kconfig
@@ -2,7 +2,6 @@
config ZRAM
tristate "Compressed RAM block device support"
depends on BLOCK && SYSFS && MMU
- depends on CRYPTO_LZO || CRYPTO_ZSTD || CRYPTO_LZ4 || CRYPTO_LZ4HC || CRYPTO_842
select ZSMALLOC
help
Creates virtual block devices called /dev/zramX (X = 0, 1, ...).
@@ -15,6 +14,49 @@ config ZRAM
See Documentation/admin-guide/blockdev/zram.rst for more information.
+config ZRAM_BACKEND_LZ4
+ bool "lz4 compression support"
+ depends on ZRAM
+ select LZ4_COMPRESS
+ select LZ4_DECOMPRESS
+
+config ZRAM_BACKEND_LZ4HC
+ bool "lz4hc compression support"
+ depends on ZRAM
+ select LZ4HC_COMPRESS
+ select LZ4_DECOMPRESS
+
+config ZRAM_BACKEND_ZSTD
+ bool "zstd compression support"
+ depends on ZRAM
+ select ZSTD_COMPRESS
+ select ZSTD_DECOMPRESS
+
+config ZRAM_BACKEND_DEFLATE
+ bool "deflate compression support"
+ depends on ZRAM
+ select ZLIB_DEFLATE
+ select ZLIB_INFLATE
+
+config ZRAM_BACKEND_842
+ bool "842 compression support"
+ depends on ZRAM
+ select 842_COMPRESS
+ select 842_DECOMPRESS
+
+config ZRAM_BACKEND_FORCE_LZO
+ depends on ZRAM
+ def_bool !ZRAM_BACKEND_LZ4 && !ZRAM_BACKEND_LZ4HC && \
+ !ZRAM_BACKEND_ZSTD && !ZRAM_BACKEND_DEFLATE && \
+ !ZRAM_BACKEND_842
+
+config ZRAM_BACKEND_LZO
+ bool "lzo and lzo-rle compression support" if !ZRAM_BACKEND_FORCE_LZO
+ depends on ZRAM
+ default ZRAM_BACKEND_FORCE_LZO
+ select LZO_COMPRESS
+ select LZO_DECOMPRESS
+
choice
prompt "Default zram compressor"
default ZRAM_DEF_COMP_LZORLE
@@ -22,45 +64,52 @@ choice
config ZRAM_DEF_COMP_LZORLE
bool "lzo-rle"
- depends on CRYPTO_LZO
+ depends on ZRAM_BACKEND_LZO
-config ZRAM_DEF_COMP_ZSTD
- bool "zstd"
- depends on CRYPTO_ZSTD
+config ZRAM_DEF_COMP_LZO
+ bool "lzo"
+ depends on ZRAM_BACKEND_LZO
config ZRAM_DEF_COMP_LZ4
bool "lz4"
- depends on CRYPTO_LZ4
-
-config ZRAM_DEF_COMP_LZO
- bool "lzo"
- depends on CRYPTO_LZO
+ depends on ZRAM_BACKEND_LZ4
config ZRAM_DEF_COMP_LZ4HC
bool "lz4hc"
- depends on CRYPTO_LZ4HC
+ depends on ZRAM_BACKEND_LZ4HC
+
+config ZRAM_DEF_COMP_ZSTD
+ bool "zstd"
+ depends on ZRAM_BACKEND_ZSTD
+
+config ZRAM_DEF_COMP_DEFLATE
+ bool "deflate"
+ depends on ZRAM_BACKEND_DEFLATE
config ZRAM_DEF_COMP_842
bool "842"
- depends on CRYPTO_842
+ depends on ZRAM_BACKEND_842
endchoice
config ZRAM_DEF_COMP
string
+ depends on ZRAM
default "lzo-rle" if ZRAM_DEF_COMP_LZORLE
- default "zstd" if ZRAM_DEF_COMP_ZSTD
- default "lz4" if ZRAM_DEF_COMP_LZ4
default "lzo" if ZRAM_DEF_COMP_LZO
+ default "lz4" if ZRAM_DEF_COMP_LZ4
default "lz4hc" if ZRAM_DEF_COMP_LZ4HC
+ default "zstd" if ZRAM_DEF_COMP_ZSTD
+ default "deflate" if ZRAM_DEF_COMP_DEFLATE
default "842" if ZRAM_DEF_COMP_842
+ default "unset-value"
config ZRAM_WRITEBACK
bool "Write back incompressible or idle page to backing device"
depends on ZRAM
help
- With incompressible page, there is no memory saving to keep it
- in memory. Instead, write it out to backing device.
+ This lets zram entries (incompressible or idle pages) be written
+ back to a backing device, helping save memory.
For this feature, admin should set up backing device via
/sys/block/zramX/backing_dev.
@@ -69,12 +118,30 @@ config ZRAM_WRITEBACK
See Documentation/admin-guide/blockdev/zram.rst for more information.
+config ZRAM_TRACK_ENTRY_ACTIME
+ bool "Track access time of zram entries"
+ depends on ZRAM
+ help
+ With this feature zram tracks access time of every stored
+ entry (page), which can be used for a more fine grained IDLE
+ pages writeback.
+
config ZRAM_MEMORY_TRACKING
bool "Track zRam block status"
depends on ZRAM && DEBUG_FS
+ select ZRAM_TRACK_ENTRY_ACTIME
help
With this feature, admin can track the state of allocated blocks
of zRAM. Admin could see the information via
/sys/kernel/debug/zram/zramX/block_state.
See Documentation/admin-guide/blockdev/zram.rst for more information.
+
+config ZRAM_MULTI_COMP
+ bool "Enable multiple compression streams"
+ depends on ZRAM
+ help
+ This will enable multi-compression streams, so that ZRAM can
+ re-compress pages using a potentially slower but more effective
+ compression algorithm. Note, that IDLE page recompression
+ requires ZRAM_TRACK_ENTRY_ACTIME.
diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile
index de9e457907b1..0fdefd576691 100644
--- a/drivers/block/zram/Makefile
+++ b/drivers/block/zram/Makefile
@@ -1,4 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
+
zram-y := zcomp.o zram_drv.o
+zram-$(CONFIG_ZRAM_BACKEND_LZO) += backend_lzorle.o backend_lzo.o
+zram-$(CONFIG_ZRAM_BACKEND_LZ4) += backend_lz4.o
+zram-$(CONFIG_ZRAM_BACKEND_LZ4HC) += backend_lz4hc.o
+zram-$(CONFIG_ZRAM_BACKEND_ZSTD) += backend_zstd.o
+zram-$(CONFIG_ZRAM_BACKEND_DEFLATE) += backend_deflate.o
+zram-$(CONFIG_ZRAM_BACKEND_842) += backend_842.o
+
obj-$(CONFIG_ZRAM) += zram.o
diff --git a/drivers/block/zram/backend_842.c b/drivers/block/zram/backend_842.c
new file mode 100644
index 000000000000..10d9d5c60f53
--- /dev/null
+++ b/drivers/block/zram/backend_842.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sw842.h>
+#include <linux/vmalloc.h>
+
+#include "backend_842.h"
+
+static void release_params_842(struct zcomp_params *params)
+{
+}
+
+static int setup_params_842(struct zcomp_params *params)
+{
+ return 0;
+}
+
+static void destroy_842(struct zcomp_ctx *ctx)
+{
+ kfree(ctx->context);
+}
+
+static int create_842(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ ctx->context = kmalloc(SW842_MEM_COMPRESS, GFP_KERNEL);
+ if (!ctx->context)
+ return -ENOMEM;
+ return 0;
+}
+
+static int compress_842(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ unsigned int dlen = req->dst_len;
+ int ret;
+
+ ret = sw842_compress(req->src, req->src_len, req->dst, &dlen,
+ ctx->context);
+ if (ret == 0)
+ req->dst_len = dlen;
+ return ret;
+}
+
+static int decompress_842(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ unsigned int dlen = req->dst_len;
+
+ return sw842_decompress(req->src, req->src_len, req->dst, &dlen);
+}
+
+const struct zcomp_ops backend_842 = {
+ .compress = compress_842,
+ .decompress = decompress_842,
+ .create_ctx = create_842,
+ .destroy_ctx = destroy_842,
+ .setup_params = setup_params_842,
+ .release_params = release_params_842,
+ .name = "842",
+};
diff --git a/drivers/block/zram/backend_842.h b/drivers/block/zram/backend_842.h
new file mode 100644
index 000000000000..4dc85c188799
--- /dev/null
+++ b/drivers/block/zram/backend_842.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_842_H__
+#define __BACKEND_842_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_842;
+
+#endif /* __BACKEND_842_H__ */
diff --git a/drivers/block/zram/backend_deflate.c b/drivers/block/zram/backend_deflate.c
new file mode 100644
index 000000000000..b75016e0e654
--- /dev/null
+++ b/drivers/block/zram/backend_deflate.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/zlib.h>
+
+#include "backend_deflate.h"
+
+/* Use the same value as crypto API */
+#define DEFLATE_DEF_WINBITS (-11)
+#define DEFLATE_DEF_MEMLEVEL MAX_MEM_LEVEL
+
+struct deflate_ctx {
+ struct z_stream_s cctx;
+ struct z_stream_s dctx;
+};
+
+static void deflate_release_params(struct zcomp_params *params)
+{
+}
+
+static int deflate_setup_params(struct zcomp_params *params)
+{
+ if (params->level == ZCOMP_PARAM_NOT_SET)
+ params->level = Z_DEFAULT_COMPRESSION;
+ if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET)
+ params->deflate.winbits = DEFLATE_DEF_WINBITS;
+
+ return 0;
+}
+
+static void deflate_destroy(struct zcomp_ctx *ctx)
+{
+ struct deflate_ctx *zctx = ctx->context;
+
+ if (!zctx)
+ return;
+
+ if (zctx->cctx.workspace) {
+ zlib_deflateEnd(&zctx->cctx);
+ vfree(zctx->cctx.workspace);
+ }
+ if (zctx->dctx.workspace) {
+ zlib_inflateEnd(&zctx->dctx);
+ vfree(zctx->dctx.workspace);
+ }
+ kfree(zctx);
+}
+
+static int deflate_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ struct deflate_ctx *zctx;
+ size_t sz;
+ int ret;
+
+ zctx = kzalloc(sizeof(*zctx), GFP_KERNEL);
+ if (!zctx)
+ return -ENOMEM;
+
+ ctx->context = zctx;
+ sz = zlib_deflate_workspacesize(params->deflate.winbits, MAX_MEM_LEVEL);
+ zctx->cctx.workspace = vzalloc(sz);
+ if (!zctx->cctx.workspace)
+ goto error;
+
+ ret = zlib_deflateInit2(&zctx->cctx, params->level, Z_DEFLATED,
+ params->deflate.winbits, DEFLATE_DEF_MEMLEVEL,
+ Z_DEFAULT_STRATEGY);
+ if (ret != Z_OK)
+ goto error;
+
+ sz = zlib_inflate_workspacesize();
+ zctx->dctx.workspace = vzalloc(sz);
+ if (!zctx->dctx.workspace)
+ goto error;
+
+ ret = zlib_inflateInit2(&zctx->dctx, params->deflate.winbits);
+ if (ret != Z_OK)
+ goto error;
+
+ return 0;
+
+error:
+ deflate_destroy(ctx);
+ return -EINVAL;
+}
+
+static int deflate_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct deflate_ctx *zctx = ctx->context;
+ struct z_stream_s *deflate;
+ int ret;
+
+ deflate = &zctx->cctx;
+ ret = zlib_deflateReset(deflate);
+ if (ret != Z_OK)
+ return -EINVAL;
+
+ deflate->next_in = (u8 *)req->src;
+ deflate->avail_in = req->src_len;
+ deflate->next_out = (u8 *)req->dst;
+ deflate->avail_out = req->dst_len;
+
+ ret = zlib_deflate(deflate, Z_FINISH);
+ if (ret != Z_STREAM_END)
+ return -EINVAL;
+
+ req->dst_len = deflate->total_out;
+ return 0;
+}
+
+static int deflate_decompress(struct zcomp_params *params,
+ struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct deflate_ctx *zctx = ctx->context;
+ struct z_stream_s *inflate;
+ int ret;
+
+ inflate = &zctx->dctx;
+
+ ret = zlib_inflateReset(inflate);
+ if (ret != Z_OK)
+ return -EINVAL;
+
+ inflate->next_in = (u8 *)req->src;
+ inflate->avail_in = req->src_len;
+ inflate->next_out = (u8 *)req->dst;
+ inflate->avail_out = req->dst_len;
+
+ ret = zlib_inflate(inflate, Z_SYNC_FLUSH);
+ if (ret != Z_STREAM_END)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct zcomp_ops backend_deflate = {
+ .compress = deflate_compress,
+ .decompress = deflate_decompress,
+ .create_ctx = deflate_create,
+ .destroy_ctx = deflate_destroy,
+ .setup_params = deflate_setup_params,
+ .release_params = deflate_release_params,
+ .name = "deflate",
+};
diff --git a/drivers/block/zram/backend_deflate.h b/drivers/block/zram/backend_deflate.h
new file mode 100644
index 000000000000..a39ac12b114c
--- /dev/null
+++ b/drivers/block/zram/backend_deflate.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_DEFLATE_H__
+#define __BACKEND_DEFLATE_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_deflate;
+
+#endif /* __BACKEND_DEFLATE_H__ */
diff --git a/drivers/block/zram/backend_lz4.c b/drivers/block/zram/backend_lz4.c
new file mode 100644
index 000000000000..daccd60857eb
--- /dev/null
+++ b/drivers/block/zram/backend_lz4.c
@@ -0,0 +1,127 @@
+#include <linux/kernel.h>
+#include <linux/lz4.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "backend_lz4.h"
+
+struct lz4_ctx {
+ void *mem;
+
+ LZ4_streamDecode_t *dstrm;
+ LZ4_stream_t *cstrm;
+};
+
+static void lz4_release_params(struct zcomp_params *params)
+{
+}
+
+static int lz4_setup_params(struct zcomp_params *params)
+{
+ if (params->level == ZCOMP_PARAM_NOT_SET)
+ params->level = LZ4_ACCELERATION_DEFAULT;
+
+ return 0;
+}
+
+static void lz4_destroy(struct zcomp_ctx *ctx)
+{
+ struct lz4_ctx *zctx = ctx->context;
+
+ if (!zctx)
+ return;
+
+ vfree(zctx->mem);
+ kfree(zctx->dstrm);
+ kfree(zctx->cstrm);
+ kfree(zctx);
+}
+
+static int lz4_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ struct lz4_ctx *zctx;
+
+ zctx = kzalloc(sizeof(*zctx), GFP_KERNEL);
+ if (!zctx)
+ return -ENOMEM;
+
+ ctx->context = zctx;
+ if (params->dict_sz == 0) {
+ zctx->mem = vmalloc(LZ4_MEM_COMPRESS);
+ if (!zctx->mem)
+ goto error;
+ } else {
+ zctx->dstrm = kzalloc(sizeof(*zctx->dstrm), GFP_KERNEL);
+ if (!zctx->dstrm)
+ goto error;
+
+ zctx->cstrm = kzalloc(sizeof(*zctx->cstrm), GFP_KERNEL);
+ if (!zctx->cstrm)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ lz4_destroy(ctx);
+ return -ENOMEM;
+}
+
+static int lz4_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct lz4_ctx *zctx = ctx->context;
+ int ret;
+
+ if (!zctx->cstrm) {
+ ret = LZ4_compress_fast(req->src, req->dst, req->src_len,
+ req->dst_len, params->level,
+ zctx->mem);
+ } else {
+ /* Cstrm needs to be reset */
+ ret = LZ4_loadDict(zctx->cstrm, params->dict, params->dict_sz);
+ if (ret != params->dict_sz)
+ return -EINVAL;
+ ret = LZ4_compress_fast_continue(zctx->cstrm, req->src,
+ req->dst, req->src_len,
+ req->dst_len, params->level);
+ }
+ if (!ret)
+ return -EINVAL;
+ req->dst_len = ret;
+ return 0;
+}
+
+static int lz4_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct lz4_ctx *zctx = ctx->context;
+ int ret;
+
+ if (!zctx->dstrm) {
+ ret = LZ4_decompress_safe(req->src, req->dst, req->src_len,
+ req->dst_len);
+ } else {
+ /* Dstrm needs to be reset */
+ ret = LZ4_setStreamDecode(zctx->dstrm, params->dict,
+ params->dict_sz);
+ if (!ret)
+ return -EINVAL;
+ ret = LZ4_decompress_safe_continue(zctx->dstrm, req->src,
+ req->dst, req->src_len,
+ req->dst_len);
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+const struct zcomp_ops backend_lz4 = {
+ .compress = lz4_compress,
+ .decompress = lz4_decompress,
+ .create_ctx = lz4_create,
+ .destroy_ctx = lz4_destroy,
+ .setup_params = lz4_setup_params,
+ .release_params = lz4_release_params,
+ .name = "lz4",
+};
diff --git a/drivers/block/zram/backend_lz4.h b/drivers/block/zram/backend_lz4.h
new file mode 100644
index 000000000000..c11fa602a703
--- /dev/null
+++ b/drivers/block/zram/backend_lz4.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_LZ4_H__
+#define __BACKEND_LZ4_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_lz4;
+
+#endif /* __BACKEND_LZ4_H__ */
diff --git a/drivers/block/zram/backend_lz4hc.c b/drivers/block/zram/backend_lz4hc.c
new file mode 100644
index 000000000000..9e8a35dfa56d
--- /dev/null
+++ b/drivers/block/zram/backend_lz4hc.c
@@ -0,0 +1,128 @@
+#include <linux/kernel.h>
+#include <linux/lz4.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "backend_lz4hc.h"
+
+struct lz4hc_ctx {
+ void *mem;
+
+ LZ4_streamDecode_t *dstrm;
+ LZ4_streamHC_t *cstrm;
+};
+
+static void lz4hc_release_params(struct zcomp_params *params)
+{
+}
+
+static int lz4hc_setup_params(struct zcomp_params *params)
+{
+ if (params->level == ZCOMP_PARAM_NOT_SET)
+ params->level = LZ4HC_DEFAULT_CLEVEL;
+
+ return 0;
+}
+
+static void lz4hc_destroy(struct zcomp_ctx *ctx)
+{
+ struct lz4hc_ctx *zctx = ctx->context;
+
+ if (!zctx)
+ return;
+
+ kfree(zctx->dstrm);
+ kfree(zctx->cstrm);
+ vfree(zctx->mem);
+ kfree(zctx);
+}
+
+static int lz4hc_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ struct lz4hc_ctx *zctx;
+
+ zctx = kzalloc(sizeof(*zctx), GFP_KERNEL);
+ if (!zctx)
+ return -ENOMEM;
+
+ ctx->context = zctx;
+ if (params->dict_sz == 0) {
+ zctx->mem = vmalloc(LZ4HC_MEM_COMPRESS);
+ if (!zctx->mem)
+ goto error;
+ } else {
+ zctx->dstrm = kzalloc(sizeof(*zctx->dstrm), GFP_KERNEL);
+ if (!zctx->dstrm)
+ goto error;
+
+ zctx->cstrm = kzalloc(sizeof(*zctx->cstrm), GFP_KERNEL);
+ if (!zctx->cstrm)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ lz4hc_destroy(ctx);
+ return -EINVAL;
+}
+
+static int lz4hc_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct lz4hc_ctx *zctx = ctx->context;
+ int ret;
+
+ if (!zctx->cstrm) {
+ ret = LZ4_compress_HC(req->src, req->dst, req->src_len,
+ req->dst_len, params->level,
+ zctx->mem);
+ } else {
+ /* Cstrm needs to be reset */
+ LZ4_resetStreamHC(zctx->cstrm, params->level);
+ ret = LZ4_loadDictHC(zctx->cstrm, params->dict,
+ params->dict_sz);
+ if (ret != params->dict_sz)
+ return -EINVAL;
+ ret = LZ4_compress_HC_continue(zctx->cstrm, req->src, req->dst,
+ req->src_len, req->dst_len);
+ }
+ if (!ret)
+ return -EINVAL;
+ req->dst_len = ret;
+ return 0;
+}
+
+static int lz4hc_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct lz4hc_ctx *zctx = ctx->context;
+ int ret;
+
+ if (!zctx->dstrm) {
+ ret = LZ4_decompress_safe(req->src, req->dst, req->src_len,
+ req->dst_len);
+ } else {
+ /* Dstrm needs to be reset */
+ ret = LZ4_setStreamDecode(zctx->dstrm, params->dict,
+ params->dict_sz);
+ if (!ret)
+ return -EINVAL;
+ ret = LZ4_decompress_safe_continue(zctx->dstrm, req->src,
+ req->dst, req->src_len,
+ req->dst_len);
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+const struct zcomp_ops backend_lz4hc = {
+ .compress = lz4hc_compress,
+ .decompress = lz4hc_decompress,
+ .create_ctx = lz4hc_create,
+ .destroy_ctx = lz4hc_destroy,
+ .setup_params = lz4hc_setup_params,
+ .release_params = lz4hc_release_params,
+ .name = "lz4hc",
+};
diff --git a/drivers/block/zram/backend_lz4hc.h b/drivers/block/zram/backend_lz4hc.h
new file mode 100644
index 000000000000..6de03551ed4d
--- /dev/null
+++ b/drivers/block/zram/backend_lz4hc.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_LZ4HC_H__
+#define __BACKEND_LZ4HC_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_lz4hc;
+
+#endif /* __BACKEND_LZ4HC_H__ */
diff --git a/drivers/block/zram/backend_lzo.c b/drivers/block/zram/backend_lzo.c
new file mode 100644
index 000000000000..4c906beaae6b
--- /dev/null
+++ b/drivers/block/zram/backend_lzo.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lzo.h>
+
+#include "backend_lzo.h"
+
+static void lzo_release_params(struct zcomp_params *params)
+{
+}
+
+static int lzo_setup_params(struct zcomp_params *params)
+{
+ return 0;
+}
+
+static int lzo_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ ctx->context = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+ if (!ctx->context)
+ return -ENOMEM;
+ return 0;
+}
+
+static void lzo_destroy(struct zcomp_ctx *ctx)
+{
+ kfree(ctx->context);
+}
+
+static int lzo_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ int ret;
+
+ ret = lzo1x_1_compress(req->src, req->src_len, req->dst,
+ &req->dst_len, ctx->context);
+ return ret == LZO_E_OK ? 0 : ret;
+}
+
+static int lzo_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ int ret;
+
+ ret = lzo1x_decompress_safe(req->src, req->src_len,
+ req->dst, &req->dst_len);
+ return ret == LZO_E_OK ? 0 : ret;
+}
+
+const struct zcomp_ops backend_lzo = {
+ .compress = lzo_compress,
+ .decompress = lzo_decompress,
+ .create_ctx = lzo_create,
+ .destroy_ctx = lzo_destroy,
+ .setup_params = lzo_setup_params,
+ .release_params = lzo_release_params,
+ .name = "lzo",
+};
diff --git a/drivers/block/zram/backend_lzo.h b/drivers/block/zram/backend_lzo.h
new file mode 100644
index 000000000000..93d54749e63c
--- /dev/null
+++ b/drivers/block/zram/backend_lzo.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_LZO_H__
+#define __BACKEND_LZO_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_lzo;
+
+#endif /* __BACKEND_LZO_H__ */
diff --git a/drivers/block/zram/backend_lzorle.c b/drivers/block/zram/backend_lzorle.c
new file mode 100644
index 000000000000..10640c96cbfc
--- /dev/null
+++ b/drivers/block/zram/backend_lzorle.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lzo.h>
+
+#include "backend_lzorle.h"
+
+static void lzorle_release_params(struct zcomp_params *params)
+{
+}
+
+static int lzorle_setup_params(struct zcomp_params *params)
+{
+ return 0;
+}
+
+static int lzorle_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ ctx->context = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+ if (!ctx->context)
+ return -ENOMEM;
+ return 0;
+}
+
+static void lzorle_destroy(struct zcomp_ctx *ctx)
+{
+ kfree(ctx->context);
+}
+
+static int lzorle_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ int ret;
+
+ ret = lzorle1x_1_compress(req->src, req->src_len, req->dst,
+ &req->dst_len, ctx->context);
+ return ret == LZO_E_OK ? 0 : ret;
+}
+
+static int lzorle_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ int ret;
+
+ ret = lzo1x_decompress_safe(req->src, req->src_len,
+ req->dst, &req->dst_len);
+ return ret == LZO_E_OK ? 0 : ret;
+}
+
+const struct zcomp_ops backend_lzorle = {
+ .compress = lzorle_compress,
+ .decompress = lzorle_decompress,
+ .create_ctx = lzorle_create,
+ .destroy_ctx = lzorle_destroy,
+ .setup_params = lzorle_setup_params,
+ .release_params = lzorle_release_params,
+ .name = "lzo-rle",
+};
diff --git a/drivers/block/zram/backend_lzorle.h b/drivers/block/zram/backend_lzorle.h
new file mode 100644
index 000000000000..6ecb163b09f1
--- /dev/null
+++ b/drivers/block/zram/backend_lzorle.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_LZORLE_H__
+#define __BACKEND_LZORLE_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_lzorle;
+
+#endif /* __BACKEND_LZORLE_H__ */
diff --git a/drivers/block/zram/backend_zstd.c b/drivers/block/zram/backend_zstd.c
new file mode 100644
index 000000000000..81defb98ed09
--- /dev/null
+++ b/drivers/block/zram/backend_zstd.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/zstd.h>
+
+#include "backend_zstd.h"
+
+struct zstd_ctx {
+ zstd_cctx *cctx;
+ zstd_dctx *dctx;
+ void *cctx_mem;
+ void *dctx_mem;
+};
+
+struct zstd_params {
+ zstd_custom_mem custom_mem;
+ zstd_cdict *cdict;
+ zstd_ddict *ddict;
+ zstd_parameters cprm;
+};
+
+/*
+ * For C/D dictionaries we need to provide zstd with zstd_custom_mem,
+ * which zstd uses internally to allocate/free memory when needed.
+ */
+static void *zstd_custom_alloc(void *opaque, size_t size)
+{
+ return kvzalloc(size, GFP_NOIO | __GFP_NOWARN);
+}
+
+static void zstd_custom_free(void *opaque, void *address)
+{
+ kvfree(address);
+}
+
+static void zstd_release_params(struct zcomp_params *params)
+{
+ struct zstd_params *zp = params->drv_data;
+
+ params->drv_data = NULL;
+ if (!zp)
+ return;
+
+ zstd_free_cdict(zp->cdict);
+ zstd_free_ddict(zp->ddict);
+ kfree(zp);
+}
+
+static int zstd_setup_params(struct zcomp_params *params)
+{
+ zstd_compression_parameters prm;
+ struct zstd_params *zp;
+
+ zp = kzalloc(sizeof(*zp), GFP_KERNEL);
+ if (!zp)
+ return -ENOMEM;
+
+ params->drv_data = zp;
+ if (params->level == ZCOMP_PARAM_NOT_SET)
+ params->level = zstd_default_clevel();
+
+ zp->cprm = zstd_get_params(params->level, PAGE_SIZE);
+
+ zp->custom_mem.customAlloc = zstd_custom_alloc;
+ zp->custom_mem.customFree = zstd_custom_free;
+
+ prm = zstd_get_cparams(params->level, PAGE_SIZE,
+ params->dict_sz);
+
+ zp->cdict = zstd_create_cdict_byreference(params->dict,
+ params->dict_sz,
+ prm,
+ zp->custom_mem);
+ if (!zp->cdict)
+ goto error;
+
+ zp->ddict = zstd_create_ddict_byreference(params->dict,
+ params->dict_sz,
+ zp->custom_mem);
+ if (!zp->ddict)
+ goto error;
+
+ return 0;
+
+error:
+ zstd_release_params(params);
+ return -EINVAL;
+}
+
+static void zstd_destroy(struct zcomp_ctx *ctx)
+{
+ struct zstd_ctx *zctx = ctx->context;
+
+ if (!zctx)
+ return;
+
+ /*
+ * If ->cctx_mem and ->dctx_mem were allocated then we didn't use
+ * C/D dictionary and ->cctx / ->dctx were "embedded" into these
+ * buffers.
+ *
+ * If otherwise then we need to explicitly release ->cctx / ->dctx.
+ */
+ if (zctx->cctx_mem)
+ vfree(zctx->cctx_mem);
+ else
+ zstd_free_cctx(zctx->cctx);
+
+ if (zctx->dctx_mem)
+ vfree(zctx->dctx_mem);
+ else
+ zstd_free_dctx(zctx->dctx);
+
+ kfree(zctx);
+}
+
+static int zstd_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
+{
+ struct zstd_ctx *zctx;
+ zstd_parameters prm;
+ size_t sz;
+
+ zctx = kzalloc(sizeof(*zctx), GFP_KERNEL);
+ if (!zctx)
+ return -ENOMEM;
+
+ ctx->context = zctx;
+ if (params->dict_sz == 0) {
+ prm = zstd_get_params(params->level, PAGE_SIZE);
+ sz = zstd_cctx_workspace_bound(&prm.cParams);
+ zctx->cctx_mem = vzalloc(sz);
+ if (!zctx->cctx_mem)
+ goto error;
+
+ zctx->cctx = zstd_init_cctx(zctx->cctx_mem, sz);
+ if (!zctx->cctx)
+ goto error;
+
+ sz = zstd_dctx_workspace_bound();
+ zctx->dctx_mem = vzalloc(sz);
+ if (!zctx->dctx_mem)
+ goto error;
+
+ zctx->dctx = zstd_init_dctx(zctx->dctx_mem, sz);
+ if (!zctx->dctx)
+ goto error;
+ } else {
+ struct zstd_params *zp = params->drv_data;
+
+ zctx->cctx = zstd_create_cctx_advanced(zp->custom_mem);
+ if (!zctx->cctx)
+ goto error;
+
+ zctx->dctx = zstd_create_dctx_advanced(zp->custom_mem);
+ if (!zctx->dctx)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ zstd_release_params(params);
+ zstd_destroy(ctx);
+ return -EINVAL;
+}
+
+static int zstd_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct zstd_params *zp = params->drv_data;
+ struct zstd_ctx *zctx = ctx->context;
+ size_t ret;
+
+ if (params->dict_sz == 0)
+ ret = zstd_compress_cctx(zctx->cctx, req->dst, req->dst_len,
+ req->src, req->src_len, &zp->cprm);
+ else
+ ret = zstd_compress_using_cdict(zctx->cctx, req->dst,
+ req->dst_len, req->src,
+ req->src_len,
+ zp->cdict);
+ if (zstd_is_error(ret))
+ return -EINVAL;
+ req->dst_len = ret;
+ return 0;
+}
+
+static int zstd_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req)
+{
+ struct zstd_params *zp = params->drv_data;
+ struct zstd_ctx *zctx = ctx->context;
+ size_t ret;
+
+ if (params->dict_sz == 0)
+ ret = zstd_decompress_dctx(zctx->dctx, req->dst, req->dst_len,
+ req->src, req->src_len);
+ else
+ ret = zstd_decompress_using_ddict(zctx->dctx, req->dst,
+ req->dst_len, req->src,
+ req->src_len, zp->ddict);
+ if (zstd_is_error(ret))
+ return -EINVAL;
+ return 0;
+}
+
+const struct zcomp_ops backend_zstd = {
+ .compress = zstd_compress,
+ .decompress = zstd_decompress,
+ .create_ctx = zstd_create,
+ .destroy_ctx = zstd_destroy,
+ .setup_params = zstd_setup_params,
+ .release_params = zstd_release_params,
+ .name = "zstd",
+};
diff --git a/drivers/block/zram/backend_zstd.h b/drivers/block/zram/backend_zstd.h
new file mode 100644
index 000000000000..10fdfff1ec1c
--- /dev/null
+++ b/drivers/block/zram/backend_zstd.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_ZSTD_H__
+#define __BACKEND_ZSTD_H__
+
+#include "zcomp.h"
+
+extern const struct zcomp_ops backend_zstd;
+
+#endif /* __BACKEND_ZSTD_H__ */
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 0916de952e09..b1bd1daa0060 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -1,7 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2014 Sergey Senozhatsky.
- */
#include <linux/kernel.h>
#include <linux/string.h>
@@ -9,155 +6,173 @@
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
-#include <linux/cpu.h>
-#include <linux/crypto.h>
+#include <linux/cpuhotplug.h>
+#include <linux/vmalloc.h>
+#include <linux/sysfs.h>
#include "zcomp.h"
-static const char * const backends[] = {
-#if IS_ENABLED(CONFIG_CRYPTO_LZO)
- "lzo",
- "lzo-rle",
+#include "backend_lzo.h"
+#include "backend_lzorle.h"
+#include "backend_lz4.h"
+#include "backend_lz4hc.h"
+#include "backend_zstd.h"
+#include "backend_deflate.h"
+#include "backend_842.h"
+
+static const struct zcomp_ops *backends[] = {
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZO)
+ &backend_lzorle,
+ &backend_lzo,
+#endif
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZ4)
+ &backend_lz4,
#endif
-#if IS_ENABLED(CONFIG_CRYPTO_LZ4)
- "lz4",
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZ4HC)
+ &backend_lz4hc,
#endif
-#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC)
- "lz4hc",
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_ZSTD)
+ &backend_zstd,
#endif
-#if IS_ENABLED(CONFIG_CRYPTO_842)
- "842",
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_DEFLATE)
+ &backend_deflate,
#endif
-#if IS_ENABLED(CONFIG_CRYPTO_ZSTD)
- "zstd",
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_842)
+ &backend_842,
#endif
+ NULL
};
-static void zcomp_strm_free(struct zcomp_strm *zstrm)
+static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
{
- if (!IS_ERR_OR_NULL(zstrm->tfm))
- crypto_free_comp(zstrm->tfm);
- free_pages((unsigned long)zstrm->buffer, 1);
- zstrm->tfm = NULL;
+ comp->ops->destroy_ctx(&zstrm->ctx);
+ vfree(zstrm->local_copy);
+ vfree(zstrm->buffer);
zstrm->buffer = NULL;
}
-/*
- * Initialize zcomp_strm structure with ->tfm initialized by backend, and
- * ->buffer. Return a negative value on error.
- */
-static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp)
+static int zcomp_strm_init(struct zcomp *comp, struct zcomp_strm *zstrm)
{
- zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0);
+ int ret;
+
+ ret = comp->ops->create_ctx(comp->params, &zstrm->ctx);
+ if (ret)
+ return ret;
+
+ zstrm->local_copy = vzalloc(PAGE_SIZE);
/*
* allocate 2 pages. 1 for compressed data, plus 1 extra for the
* case when compressed size is larger than the original one
*/
- zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
- if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) {
- zcomp_strm_free(zstrm);
+ zstrm->buffer = vzalloc(2 * PAGE_SIZE);
+ if (!zstrm->buffer || !zstrm->local_copy) {
+ zcomp_strm_free(comp, zstrm);
return -ENOMEM;
}
return 0;
}
+static const struct zcomp_ops *lookup_backend_ops(const char *comp)
+{
+ int i = 0;
+
+ while (backends[i]) {
+ if (sysfs_streq(comp, backends[i]->name))
+ break;
+ i++;
+ }
+ return backends[i];
+}
+
bool zcomp_available_algorithm(const char *comp)
{
- /*
- * Crypto does not ignore a trailing new line symbol,
- * so make sure you don't supply a string containing
- * one.
- * This also means that we permit zcomp initialisation
- * with any compressing algorithm known to crypto api.
- */
- return crypto_has_comp(comp, 0, 0) == 1;
+ return lookup_backend_ops(comp) != NULL;
}
/* show available compressors */
-ssize_t zcomp_available_show(const char *comp, char *buf)
+ssize_t zcomp_available_show(const char *comp, char *buf, ssize_t at)
{
- bool known_algorithm = false;
- ssize_t sz = 0;
int i;
- for (i = 0; i < ARRAY_SIZE(backends); i++) {
- if (!strcmp(comp, backends[i])) {
- known_algorithm = true;
- sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
- "[%s] ", backends[i]);
+ for (i = 0; i < ARRAY_SIZE(backends) - 1; i++) {
+ if (!strcmp(comp, backends[i]->name)) {
+ at += sysfs_emit_at(buf, at, "[%s] ",
+ backends[i]->name);
} else {
- sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
- "%s ", backends[i]);
+ at += sysfs_emit_at(buf, at, "%s ", backends[i]->name);
}
}
- /*
- * Out-of-tree module known to crypto api or a missing
- * entry in `backends'.
- */
- if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1)
- sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
- "[%s] ", comp);
-
- sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
- return sz;
+ at += sysfs_emit_at(buf, at, "\n");
+ return at;
}
struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
{
- local_lock(&comp->stream->lock);
- return this_cpu_ptr(comp->stream);
+ for (;;) {
+ struct zcomp_strm *zstrm = raw_cpu_ptr(comp->stream);
+
+ /*
+ * Inspired by zswap
+ *
+ * stream is returned with ->mutex locked which prevents
+ * cpu_dead() from releasing this stream under us, however
+ * there is still a race window between raw_cpu_ptr() and
+ * mutex_lock(), during which we could have been migrated
+ * from a CPU that has already destroyed its stream. If
+ * so then unlock and re-try on the current CPU.
+ */
+ mutex_lock(&zstrm->lock);
+ if (likely(zstrm->buffer))
+ return zstrm;
+ mutex_unlock(&zstrm->lock);
+ }
}
-void zcomp_stream_put(struct zcomp *comp)
+void zcomp_stream_put(struct zcomp_strm *zstrm)
{
- local_unlock(&comp->stream->lock);
+ mutex_unlock(&zstrm->lock);
}
-int zcomp_compress(struct zcomp_strm *zstrm,
- const void *src, unsigned int *dst_len)
+int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
+ const void *src, unsigned int *dst_len)
{
- /*
- * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized
- * because sometimes we can endup having a bigger compressed data
- * due to various reasons: for example compression algorithms tend
- * to add some padding to the compressed buffer. Speaking of padding,
- * comp algorithm `842' pads the compressed length to multiple of 8
- * and returns -ENOSP when the dst memory is not big enough, which
- * is not something that ZRAM wants to see. We can handle the
- * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we
- * receive -ERRNO from the compressing backend we can't help it
- * anymore. To make `842' happy we need to tell the exact size of
- * the dst buffer, zram_drv will take care of the fact that
- * compressed buffer is too big.
- */
- *dst_len = PAGE_SIZE * 2;
+ struct zcomp_req req = {
+ .src = src,
+ .dst = zstrm->buffer,
+ .src_len = PAGE_SIZE,
+ .dst_len = 2 * PAGE_SIZE,
+ };
+ int ret;
- return crypto_comp_compress(zstrm->tfm,
- src, PAGE_SIZE,
- zstrm->buffer, dst_len);
+ might_sleep();
+ ret = comp->ops->compress(comp->params, &zstrm->ctx, &req);
+ if (!ret)
+ *dst_len = req.dst_len;
+ return ret;
}
-int zcomp_decompress(struct zcomp_strm *zstrm,
- const void *src, unsigned int src_len, void *dst)
+int zcomp_decompress(struct zcomp *comp, struct zcomp_strm *zstrm,
+ const void *src, unsigned int src_len, void *dst)
{
- unsigned int dst_len = PAGE_SIZE;
+ struct zcomp_req req = {
+ .src = src,
+ .dst = dst,
+ .src_len = src_len,
+ .dst_len = PAGE_SIZE,
+ };
- return crypto_comp_decompress(zstrm->tfm,
- src, src_len,
- dst, &dst_len);
+ might_sleep();
+ return comp->ops->decompress(comp->params, &zstrm->ctx, &req);
}
int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
{
struct zcomp *comp = hlist_entry(node, struct zcomp, node);
- struct zcomp_strm *zstrm;
+ struct zcomp_strm *zstrm = per_cpu_ptr(comp->stream, cpu);
int ret;
- zstrm = per_cpu_ptr(comp->stream, cpu);
- local_lock_init(&zstrm->lock);
-
- ret = zcomp_strm_init(zstrm, comp);
+ ret = zcomp_strm_init(comp, zstrm);
if (ret)
pr_err("Can't allocate a compression stream\n");
return ret;
@@ -166,27 +181,38 @@ int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node)
{
struct zcomp *comp = hlist_entry(node, struct zcomp, node);
- struct zcomp_strm *zstrm;
+ struct zcomp_strm *zstrm = per_cpu_ptr(comp->stream, cpu);
- zstrm = per_cpu_ptr(comp->stream, cpu);
- zcomp_strm_free(zstrm);
+ mutex_lock(&zstrm->lock);
+ zcomp_strm_free(comp, zstrm);
+ mutex_unlock(&zstrm->lock);
return 0;
}
-static int zcomp_init(struct zcomp *comp)
+static int zcomp_init(struct zcomp *comp, struct zcomp_params *params)
{
- int ret;
+ int ret, cpu;
comp->stream = alloc_percpu(struct zcomp_strm);
if (!comp->stream)
return -ENOMEM;
+ comp->params = params;
+ ret = comp->ops->setup_params(comp->params);
+ if (ret)
+ goto cleanup;
+
+ for_each_possible_cpu(cpu)
+ mutex_init(&per_cpu_ptr(comp->stream, cpu)->lock);
+
ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
if (ret < 0)
goto cleanup;
+
return 0;
cleanup:
+ comp->ops->release_params(comp->params);
free_percpu(comp->stream);
return ret;
}
@@ -194,37 +220,35 @@ cleanup:
void zcomp_destroy(struct zcomp *comp)
{
cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
+ comp->ops->release_params(comp->params);
free_percpu(comp->stream);
kfree(comp);
}
-/*
- * search available compressors for requested algorithm.
- * allocate new zcomp and initialize it. return compressing
- * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL)
- * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in
- * case of allocation error, or any other error potentially
- * returned by zcomp_init().
- */
-struct zcomp *zcomp_create(const char *compress)
+struct zcomp *zcomp_create(const char *alg, struct zcomp_params *params)
{
struct zcomp *comp;
int error;
/*
- * Crypto API will execute /sbin/modprobe if the compression module
- * is not loaded yet. We must do it here, otherwise we are about to
- * call /sbin/modprobe under CPU hot-plug lock.
+ * The backends array has a sentinel NULL value, so the minimum
+ * size is 1. In order to be valid the array, apart from the
+ * sentinel NULL element, should have at least one compression
+ * backend selected.
*/
- if (!zcomp_available_algorithm(compress))
- return ERR_PTR(-EINVAL);
+ BUILD_BUG_ON(ARRAY_SIZE(backends) <= 1);
comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
if (!comp)
return ERR_PTR(-ENOMEM);
- comp->name = compress;
- error = zcomp_init(comp);
+ comp->ops = lookup_backend_ops(alg);
+ if (!comp->ops) {
+ kfree(comp);
+ return ERR_PTR(-EINVAL);
+ }
+
+ error = zcomp_init(comp, params);
if (error) {
kfree(comp);
return ERR_PTR(error);
diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
index 40f6420f4b2e..eacfd3f7d61d 100644
--- a/drivers/block/zram/zcomp.h
+++ b/drivers/block/zram/zcomp.h
@@ -1,43 +1,96 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2014 Sergey Senozhatsky.
- */
#ifndef _ZCOMP_H_
#define _ZCOMP_H_
-#include <linux/local_lock.h>
+
+#include <linux/mutex.h>
+
+#define ZCOMP_PARAM_NOT_SET INT_MIN
+
+struct deflate_params {
+ s32 winbits;
+};
+
+/*
+ * Immutable driver (backend) parameters. The driver may attach private
+ * data to it (e.g. driver representation of the dictionary, etc.).
+ *
+ * This data is kept per-comp and is shared among execution contexts.
+ */
+struct zcomp_params {
+ void *dict;
+ size_t dict_sz;
+ s32 level;
+ union {
+ struct deflate_params deflate;
+ };
+
+ void *drv_data;
+};
+
+/*
+ * Run-time driver context - scratch buffers, etc. It is modified during
+ * request execution (compression/decompression), cannot be shared, so
+ * it's in per-CPU area.
+ */
+struct zcomp_ctx {
+ void *context;
+};
struct zcomp_strm {
- /* The members ->buffer and ->tfm are protected by ->lock. */
- local_lock_t lock;
- /* compression/decompression buffer */
+ struct mutex lock;
+ /* compression buffer */
void *buffer;
- struct crypto_comp *tfm;
+ /* local copy of handle memory */
+ void *local_copy;
+ struct zcomp_ctx ctx;
+};
+
+struct zcomp_req {
+ const unsigned char *src;
+ const size_t src_len;
+
+ unsigned char *dst;
+ size_t dst_len;
+};
+
+struct zcomp_ops {
+ int (*compress)(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req);
+ int (*decompress)(struct zcomp_params *params, struct zcomp_ctx *ctx,
+ struct zcomp_req *req);
+
+ int (*create_ctx)(struct zcomp_params *params, struct zcomp_ctx *ctx);
+ void (*destroy_ctx)(struct zcomp_ctx *ctx);
+
+ int (*setup_params)(struct zcomp_params *params);
+ void (*release_params)(struct zcomp_params *params);
+
+ const char *name;
};
/* dynamic per-device compression frontend */
struct zcomp {
struct zcomp_strm __percpu *stream;
- const char *name;
+ const struct zcomp_ops *ops;
+ struct zcomp_params *params;
struct hlist_node node;
};
int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node);
int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node);
-ssize_t zcomp_available_show(const char *comp, char *buf);
+ssize_t zcomp_available_show(const char *comp, char *buf, ssize_t at);
bool zcomp_available_algorithm(const char *comp);
-struct zcomp *zcomp_create(const char *comp);
+struct zcomp *zcomp_create(const char *alg, struct zcomp_params *params);
void zcomp_destroy(struct zcomp *comp);
struct zcomp_strm *zcomp_stream_get(struct zcomp *comp);
-void zcomp_stream_put(struct zcomp *comp);
-
-int zcomp_compress(struct zcomp_strm *zstrm,
- const void *src, unsigned int *dst_len);
+void zcomp_stream_put(struct zcomp_strm *zstrm);
-int zcomp_decompress(struct zcomp_strm *zstrm,
- const void *src, unsigned int src_len, void *dst);
+int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
+ const void *src, unsigned int *dst_len);
+int zcomp_decompress(struct zcomp *comp, struct zcomp_strm *zstrm,
+ const void *src, unsigned int src_len, void *dst);
-bool zcomp_set_max_streams(struct zcomp *comp, int num_strm);
#endif /* _ZCOMP_H_ */
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 226ea76cc819..5759823d6314 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -33,6 +33,7 @@
#include <linux/debugfs.h>
#include <linux/cpuhotplug.h>
#include <linux/part_stat.h>
+#include <linux/kernel_read_file.h>
#include "zram_drv.h"
@@ -43,6 +44,8 @@ static DEFINE_MUTEX(zram_index_mutex);
static int zram_major;
static const char *default_compressor = CONFIG_ZRAM_DEF_COMP;
+#define ZRAM_MAX_ALGO_NAME_SZ 128
+
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
/*
@@ -52,28 +55,61 @@ static unsigned int num_devices = 1;
static size_t huge_class_size;
static const struct block_device_operations zram_devops;
-#ifdef CONFIG_ZRAM_WRITEBACK
-static const struct block_device_operations zram_wb_devops;
-#endif
static void zram_free_page(struct zram *zram, size_t index);
-static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
- u32 index, int offset, struct bio *bio);
+static int zram_read_from_zspool(struct zram *zram, struct page *page,
+ u32 index);
+#define slot_dep_map(zram, index) (&(zram)->table[(index)].dep_map)
-static int zram_slot_trylock(struct zram *zram, u32 index)
+static void zram_slot_lock_init(struct zram *zram, u32 index)
{
- return bit_spin_trylock(ZRAM_LOCK, &zram->table[index].flags);
+ static struct lock_class_key __key;
+
+ lockdep_init_map(slot_dep_map(zram, index), "zram->table[index].lock",
+ &__key, 0);
+}
+
+/*
+ * entry locking rules:
+ *
+ * 1) Lock is exclusive
+ *
+ * 2) lock() function can sleep waiting for the lock
+ *
+ * 3) Lock owner can sleep
+ *
+ * 4) Use TRY lock variant when in atomic context
+ * - must check return value and handle locking failers
+ */
+static __must_check bool zram_slot_trylock(struct zram *zram, u32 index)
+{
+ unsigned long *lock = &zram->table[index].flags;
+
+ if (!test_and_set_bit_lock(ZRAM_ENTRY_LOCK, lock)) {
+ mutex_acquire(slot_dep_map(zram, index), 0, 1, _RET_IP_);
+ lock_acquired(slot_dep_map(zram, index), _RET_IP_);
+ return true;
+ }
+
+ return false;
}
static void zram_slot_lock(struct zram *zram, u32 index)
{
- bit_spin_lock(ZRAM_LOCK, &zram->table[index].flags);
+ unsigned long *lock = &zram->table[index].flags;
+
+ mutex_acquire(slot_dep_map(zram, index), 0, 0, _RET_IP_);
+ wait_on_bit_lock(lock, ZRAM_ENTRY_LOCK, TASK_UNINTERRUPTIBLE);
+ lock_acquired(slot_dep_map(zram, index), _RET_IP_);
}
static void zram_slot_unlock(struct zram *zram, u32 index)
{
- bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags);
+ unsigned long *lock = &zram->table[index].flags;
+
+ mutex_release(slot_dep_map(zram, index), _RET_IP_);
+ clear_and_wake_up_bit(ZRAM_ENTRY_LOCK, lock);
}
static inline bool init_done(struct zram *zram)
@@ -96,7 +132,6 @@ static void zram_set_handle(struct zram *zram, u32 index, unsigned long handle)
zram->table[index].handle = handle;
}
-/* flag operations require table entry bit_spin_lock() being held */
static bool zram_test_flag(struct zram *zram, u32 index,
enum zram_pageflags flag)
{
@@ -115,17 +150,6 @@ static void zram_clear_flag(struct zram *zram, u32 index,
zram->table[index].flags &= ~BIT(flag);
}
-static inline void zram_set_element(struct zram *zram, u32 index,
- unsigned long element)
-{
- zram->table[index].element = element;
-}
-
-static unsigned long zram_get_element(struct zram *zram, u32 index)
-{
- return zram->table[index].element;
-}
-
static size_t zram_get_obj_size(struct zram *zram, u32 index)
{
return zram->table[index].flags & (BIT(ZRAM_FLAG_SHIFT) - 1);
@@ -146,11 +170,33 @@ static inline bool zram_allocated(struct zram *zram, u32 index)
zram_test_flag(zram, index, ZRAM_WB);
}
+static inline void update_used_max(struct zram *zram, const unsigned long pages)
+{
+ unsigned long cur_max = atomic_long_read(&zram->stats.max_used_pages);
+
+ do {
+ if (cur_max >= pages)
+ return;
+ } while (!atomic_long_try_cmpxchg(&zram->stats.max_used_pages,
+ &cur_max, pages));
+}
+
+static bool zram_can_store_page(struct zram *zram)
+{
+ unsigned long alloced_pages;
+
+ alloced_pages = zs_get_total_pages(zram->mem_pool);
+ update_used_max(zram, alloced_pages);
+
+ return !zram->limit_pages || alloced_pages <= zram->limit_pages;
+}
+
#if PAGE_SIZE != 4096
static inline bool is_partial_io(struct bio_vec *bvec)
{
return bvec->bv_len != PAGE_SIZE;
}
+#define ZRAM_PARTIAL_IO 1
#else
static inline bool is_partial_io(struct bio_vec *bvec)
{
@@ -158,50 +204,135 @@ static inline bool is_partial_io(struct bio_vec *bvec)
}
#endif
+static inline void zram_set_priority(struct zram *zram, u32 index, u32 prio)
+{
+ prio &= ZRAM_COMP_PRIORITY_MASK;
+ /*
+ * Clear previous priority value first, in case if we recompress
+ * further an already recompressed page
+ */
+ zram->table[index].flags &= ~(ZRAM_COMP_PRIORITY_MASK <<
+ ZRAM_COMP_PRIORITY_BIT1);
+ zram->table[index].flags |= (prio << ZRAM_COMP_PRIORITY_BIT1);
+}
+
+static inline u32 zram_get_priority(struct zram *zram, u32 index)
+{
+ u32 prio = zram->table[index].flags >> ZRAM_COMP_PRIORITY_BIT1;
+
+ return prio & ZRAM_COMP_PRIORITY_MASK;
+}
+
+static void zram_accessed(struct zram *zram, u32 index)
+{
+ zram_clear_flag(zram, index, ZRAM_IDLE);
+ zram_clear_flag(zram, index, ZRAM_PP_SLOT);
+#ifdef CONFIG_ZRAM_TRACK_ENTRY_ACTIME
+ zram->table[index].ac_time = ktime_get_boottime();
+#endif
+}
+
+#if defined CONFIG_ZRAM_WRITEBACK || defined CONFIG_ZRAM_MULTI_COMP
+struct zram_pp_slot {
+ unsigned long index;
+ struct list_head entry;
+};
+
/*
- * Check if request is within bounds and aligned on zram logical blocks.
+ * A post-processing bucket is, essentially, a size class, this defines
+ * the range (in bytes) of pp-slots sizes in particular bucket.
*/
-static inline bool valid_io_request(struct zram *zram,
- sector_t start, unsigned int size)
+#define PP_BUCKET_SIZE_RANGE 64
+#define NUM_PP_BUCKETS ((PAGE_SIZE / PP_BUCKET_SIZE_RANGE) + 1)
+
+struct zram_pp_ctl {
+ struct list_head pp_buckets[NUM_PP_BUCKETS];
+};
+
+static struct zram_pp_ctl *init_pp_ctl(void)
{
- u64 end, bound;
+ struct zram_pp_ctl *ctl;
+ u32 idx;
- /* unaligned request */
- if (unlikely(start & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)))
- return false;
- if (unlikely(size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))
- return false;
+ ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl)
+ return NULL;
- end = start + (size >> SECTOR_SHIFT);
- bound = zram->disksize >> SECTOR_SHIFT;
- /* out of range range */
- if (unlikely(start >= bound || end > bound || start > end))
- return false;
+ for (idx = 0; idx < NUM_PP_BUCKETS; idx++)
+ INIT_LIST_HEAD(&ctl->pp_buckets[idx]);
+ return ctl;
+}
- /* I/O request is valid */
- return true;
+static void release_pp_slot(struct zram *zram, struct zram_pp_slot *pps)
+{
+ list_del_init(&pps->entry);
+
+ zram_slot_lock(zram, pps->index);
+ zram_clear_flag(zram, pps->index, ZRAM_PP_SLOT);
+ zram_slot_unlock(zram, pps->index);
+
+ kfree(pps);
+}
+
+static void release_pp_ctl(struct zram *zram, struct zram_pp_ctl *ctl)
+{
+ u32 idx;
+
+ if (!ctl)
+ return;
+
+ for (idx = 0; idx < NUM_PP_BUCKETS; idx++) {
+ while (!list_empty(&ctl->pp_buckets[idx])) {
+ struct zram_pp_slot *pps;
+
+ pps = list_first_entry(&ctl->pp_buckets[idx],
+ struct zram_pp_slot,
+ entry);
+ release_pp_slot(zram, pps);
+ }
+ }
+
+ kfree(ctl);
}
-static void update_position(u32 *index, int *offset, struct bio_vec *bvec)
+static bool place_pp_slot(struct zram *zram, struct zram_pp_ctl *ctl,
+ u32 index)
{
- *index += (*offset + bvec->bv_len) / PAGE_SIZE;
- *offset = (*offset + bvec->bv_len) % PAGE_SIZE;
+ struct zram_pp_slot *pps;
+ u32 bid;
+
+ pps = kmalloc(sizeof(*pps), GFP_NOIO | __GFP_NOWARN);
+ if (!pps)
+ return false;
+
+ INIT_LIST_HEAD(&pps->entry);
+ pps->index = index;
+
+ bid = zram_get_obj_size(zram, pps->index) / PP_BUCKET_SIZE_RANGE;
+ list_add(&pps->entry, &ctl->pp_buckets[bid]);
+
+ zram_set_flag(zram, pps->index, ZRAM_PP_SLOT);
+ return true;
}
-static inline void update_used_max(struct zram *zram,
- const unsigned long pages)
+static struct zram_pp_slot *select_pp_slot(struct zram_pp_ctl *ctl)
{
- unsigned long old_max, cur_max;
+ struct zram_pp_slot *pps = NULL;
+ s32 idx = NUM_PP_BUCKETS - 1;
- old_max = atomic_long_read(&zram->stats.max_used_pages);
+ /* The higher the bucket id the more optimal slot post-processing is */
+ while (idx >= 0) {
+ pps = list_first_entry_or_null(&ctl->pp_buckets[idx],
+ struct zram_pp_slot,
+ entry);
+ if (pps)
+ break;
- do {
- cur_max = old_max;
- if (pages > cur_max)
- old_max = atomic_long_cmpxchg(
- &zram->stats.max_used_pages, cur_max, pages);
- } while (old_max != cur_max);
+ idx--;
+ }
+ return pps;
}
+#endif
static inline void zram_fill_page(void *ptr, unsigned long len,
unsigned long value)
@@ -242,7 +373,7 @@ static ssize_t initstate_show(struct device *dev,
val = init_done(zram);
up_read(&zram->init_lock);
- return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+ return sysfs_emit(buf, "%u\n", val);
}
static ssize_t disksize_show(struct device *dev,
@@ -250,7 +381,7 @@ static ssize_t disksize_show(struct device *dev,
{
struct zram *zram = dev_to_zram(dev);
- return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize);
+ return sysfs_emit(buf, "%llu\n", zram->disksize);
}
static ssize_t mem_limit_store(struct device *dev,
@@ -304,18 +435,28 @@ static void mark_idle(struct zram *zram, ktime_t cutoff)
for (index = 0; index < nr_pages; index++) {
/*
- * Do not mark ZRAM_UNDER_WB slot as ZRAM_IDLE to close race.
- * See the comment in writeback_store.
+ * Do not mark ZRAM_SAME slots as ZRAM_IDLE, because no
+ * post-processing (recompress, writeback) happens to the
+ * ZRAM_SAME slot.
+ *
+ * And ZRAM_WB slots simply cannot be ZRAM_IDLE.
*/
zram_slot_lock(zram, index);
- if (zram_allocated(zram, index) &&
- !zram_test_flag(zram, index, ZRAM_UNDER_WB)) {
-#ifdef CONFIG_ZRAM_MEMORY_TRACKING
- is_idle = !cutoff || ktime_after(cutoff, zram->table[index].ac_time);
-#endif
- if (is_idle)
- zram_set_flag(zram, index, ZRAM_IDLE);
+ if (!zram_allocated(zram, index) ||
+ zram_test_flag(zram, index, ZRAM_WB) ||
+ zram_test_flag(zram, index, ZRAM_SAME)) {
+ zram_slot_unlock(zram, index);
+ continue;
}
+
+#ifdef CONFIG_ZRAM_TRACK_ENTRY_ACTIME
+ is_idle = !cutoff ||
+ ktime_after(cutoff, zram->table[index].ac_time);
+#endif
+ if (is_idle)
+ zram_set_flag(zram, index, ZRAM_IDLE);
+ else
+ zram_clear_flag(zram, index, ZRAM_IDLE);
zram_slot_unlock(zram, index);
}
}
@@ -329,12 +470,12 @@ static ssize_t idle_store(struct device *dev,
if (!sysfs_streq(buf, "all")) {
/*
- * If it did not parse as 'all' try to treat it as an integer when
- * we have memory tracking enabled.
+ * If it did not parse as 'all' try to treat it as an integer
+ * when we have memory tracking enabled.
*/
u64 age_sec;
- if (IS_ENABLED(CONFIG_ZRAM_MEMORY_TRACKING) && !kstrtoull(buf, 0, &age_sec))
+ if (IS_ENABLED(CONFIG_ZRAM_TRACK_ENTRY_ACTIME) && !kstrtoull(buf, 0, &age_sec))
cutoff_time = ktime_sub(ktime_get_boottime(),
ns_to_ktime(age_sec * NSEC_PER_SEC));
else
@@ -345,7 +486,10 @@ static ssize_t idle_store(struct device *dev,
if (!init_done(zram))
goto out_unlock;
- /* A cutoff_time of 0 marks everything as idle, this is the "all" behavior */
+ /*
+ * A cutoff_time of 0 marks everything as idle, this is the
+ * "all" behavior.
+ */
mark_idle(zram, cutoff_time);
rv = len;
@@ -356,8 +500,31 @@ out:
}
#ifdef CONFIG_ZRAM_WRITEBACK
+#define INVALID_BDEV_BLOCK (~0UL)
+
+struct zram_wb_ctl {
+ /* idle list is accessed only by the writeback task, no concurency */
+ struct list_head idle_reqs;
+ /* done list is accessed concurrently, protect by done_lock */
+ struct list_head done_reqs;
+ wait_queue_head_t done_wait;
+ spinlock_t done_lock;
+ atomic_t num_inflight;
+};
+
+struct zram_wb_req {
+ unsigned long blk_idx;
+ struct page *page;
+ struct zram_pp_slot *pps;
+ struct bio_vec bio_vec;
+ struct bio bio;
+
+ struct list_head entry;
+};
+
static ssize_t writeback_limit_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct zram *zram = dev_to_zram(dev);
u64 val;
@@ -366,33 +533,31 @@ static ssize_t writeback_limit_enable_store(struct device *dev,
if (kstrtoull(buf, 10, &val))
return ret;
- down_read(&zram->init_lock);
- spin_lock(&zram->wb_limit_lock);
+ down_write(&zram->init_lock);
zram->wb_limit_enable = val;
- spin_unlock(&zram->wb_limit_lock);
- up_read(&zram->init_lock);
+ up_write(&zram->init_lock);
ret = len;
return ret;
}
static ssize_t writeback_limit_enable_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
bool val;
struct zram *zram = dev_to_zram(dev);
down_read(&zram->init_lock);
- spin_lock(&zram->wb_limit_lock);
val = zram->wb_limit_enable;
- spin_unlock(&zram->wb_limit_lock);
up_read(&zram->init_lock);
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+ return sysfs_emit(buf, "%d\n", val);
}
static ssize_t writeback_limit_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct zram *zram = dev_to_zram(dev);
u64 val;
@@ -401,40 +566,76 @@ static ssize_t writeback_limit_store(struct device *dev,
if (kstrtoull(buf, 10, &val))
return ret;
- down_read(&zram->init_lock);
- spin_lock(&zram->wb_limit_lock);
+ /*
+ * When the page size is greater than 4KB, if bd_wb_limit is set to
+ * a value that is not page - size aligned, it will cause value
+ * wrapping. For example, when the page size is set to 16KB and
+ * bd_wb_limit is set to 3, a single write - back operation will
+ * cause bd_wb_limit to become -1. Even more terrifying is that
+ * bd_wb_limit is an unsigned number.
+ */
+ val = rounddown(val, PAGE_SIZE / 4096);
+
+ down_write(&zram->init_lock);
zram->bd_wb_limit = val;
- spin_unlock(&zram->wb_limit_lock);
- up_read(&zram->init_lock);
+ up_write(&zram->init_lock);
ret = len;
return ret;
}
static ssize_t writeback_limit_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
u64 val;
struct zram *zram = dev_to_zram(dev);
down_read(&zram->init_lock);
- spin_lock(&zram->wb_limit_lock);
val = zram->bd_wb_limit;
- spin_unlock(&zram->wb_limit_lock);
up_read(&zram->init_lock);
- return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
+ return sysfs_emit(buf, "%llu\n", val);
}
-static void reset_bdev(struct zram *zram)
+static ssize_t writeback_batch_size_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
- struct block_device *bdev;
+ struct zram *zram = dev_to_zram(dev);
+ u32 val;
+
+ if (kstrtouint(buf, 10, &val))
+ return -EINVAL;
+
+ if (!val)
+ return -EINVAL;
+
+ down_write(&zram->init_lock);
+ zram->wb_batch_size = val;
+ up_write(&zram->init_lock);
+ return len;
+}
+
+static ssize_t writeback_batch_size_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u32 val;
+ struct zram *zram = dev_to_zram(dev);
+
+ down_read(&zram->init_lock);
+ val = zram->wb_batch_size;
+ up_read(&zram->init_lock);
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static void reset_bdev(struct zram *zram)
+{
if (!zram->backing_dev)
return;
- bdev = zram->bdev;
- blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
/* hope filp_close flush all of IO */
filp_close(zram->backing_dev, NULL);
zram->backing_dev = NULL;
@@ -481,10 +682,8 @@ static ssize_t backing_dev_store(struct device *dev,
size_t sz;
struct file *backing_dev = NULL;
struct inode *inode;
- struct address_space *mapping;
unsigned int bitmap_sz;
unsigned long nr_pages, *bitmap = NULL;
- struct block_device *bdev = NULL;
int err;
struct zram *zram = dev_to_zram(dev);
@@ -499,21 +698,20 @@ static ssize_t backing_dev_store(struct device *dev,
goto out;
}
- strlcpy(file_name, buf, PATH_MAX);
+ strscpy(file_name, buf, PATH_MAX);
/* ignore trailing newline */
sz = strlen(file_name);
if (sz > 0 && file_name[sz - 1] == '\n')
file_name[sz - 1] = 0x00;
- backing_dev = filp_open(file_name, O_RDWR|O_LARGEFILE, 0);
+ backing_dev = filp_open(file_name, O_RDWR | O_LARGEFILE | O_EXCL, 0);
if (IS_ERR(backing_dev)) {
err = PTR_ERR(backing_dev);
backing_dev = NULL;
goto out;
}
- mapping = backing_dev->f_mapping;
- inode = mapping->host;
+ inode = backing_dev->f_mapping->host;
/* Support only block device in this moment */
if (!S_ISBLK(inode->i_mode)) {
@@ -521,15 +719,13 @@ static ssize_t backing_dev_store(struct device *dev,
goto out;
}
- bdev = blkdev_get_by_dev(inode->i_rdev,
- FMODE_READ | FMODE_WRITE | FMODE_EXCL, zram);
- if (IS_ERR(bdev)) {
- err = PTR_ERR(bdev);
- bdev = NULL;
+ nr_pages = i_size_read(inode) >> PAGE_SHIFT;
+ /* Refuse to use zero sized device (also prevents self reference) */
+ if (!nr_pages) {
+ err = -EINVAL;
goto out;
}
- nr_pages = i_size_read(inode) >> PAGE_SHIFT;
bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long);
bitmap = kvzalloc(bitmap_sz, GFP_KERNEL);
if (!bitmap) {
@@ -539,21 +735,10 @@ static ssize_t backing_dev_store(struct device *dev,
reset_bdev(zram);
- zram->bdev = bdev;
+ zram->bdev = I_BDEV(inode);
zram->backing_dev = backing_dev;
zram->bitmap = bitmap;
zram->nr_pages = nr_pages;
- /*
- * With writeback feature, zram does asynchronous IO so it's no longer
- * synchronous device so let's remove synchronous io flag. Othewise,
- * upper layer(e.g., swap) could wait IO completion rather than
- * (submit and return), which will cause system sluggish.
- * Furthermore, when the IO function returns(e.g., swap_readpage),
- * upper layer expects IO was done so it could deallocate the page
- * freely but in fact, IO is going on so finally could cause
- * use-after-free when the IO is really done.
- */
- zram->disk->fops = &zram_wb_devops;
up_write(&zram->init_lock);
pr_info("setup backing device %s\n", file_name);
@@ -563,9 +748,6 @@ static ssize_t backing_dev_store(struct device *dev,
out:
kvfree(bitmap);
- if (bdev)
- blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
-
if (backing_dev)
filp_close(backing_dev, NULL);
@@ -576,23 +758,20 @@ out:
return err;
}
-static unsigned long alloc_block_bdev(struct zram *zram)
+static unsigned long zram_reserve_bdev_block(struct zram *zram)
{
- unsigned long blk_idx = 1;
-retry:
- /* skip 0 bit to confuse zram.handle = 0 */
- blk_idx = find_next_zero_bit(zram->bitmap, zram->nr_pages, blk_idx);
- if (blk_idx == zram->nr_pages)
- return 0;
+ unsigned long blk_idx;
- if (test_and_set_bit(blk_idx, zram->bitmap))
- goto retry;
+ blk_idx = find_next_zero_bit(zram->bitmap, zram->nr_pages, 0);
+ if (blk_idx == zram->nr_pages)
+ return INVALID_BDEV_BLOCK;
+ set_bit(blk_idx, zram->bitmap);
atomic64_inc(&zram->stats.bd_count);
return blk_idx;
}
-static void free_block_bdev(struct zram *zram, unsigned long blk_idx)
+static void zram_release_bdev_block(struct zram *zram, unsigned long blk_idx)
{
int was_set;
@@ -601,212 +780,534 @@ static void free_block_bdev(struct zram *zram, unsigned long blk_idx)
atomic64_dec(&zram->stats.bd_count);
}
-static void zram_page_end_io(struct bio *bio)
+static void read_from_bdev_async(struct zram *zram, struct page *page,
+ unsigned long entry, struct bio *parent)
{
- struct page *page = bio_first_page_all(bio);
+ struct bio *bio;
- page_endio(page, op_is_write(bio_op(bio)),
- blk_status_to_errno(bio->bi_status));
- bio_put(bio);
+ bio = bio_alloc(zram->bdev, 1, parent->bi_opf, GFP_NOIO);
+ bio->bi_iter.bi_sector = entry * (PAGE_SIZE >> 9);
+ __bio_add_page(bio, page, PAGE_SIZE, 0);
+ bio_chain(bio, parent);
+ submit_bio(bio);
}
-/*
- * Returns 1 if the submission is successful.
- */
-static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec,
- unsigned long entry, struct bio *parent)
+static void release_wb_req(struct zram_wb_req *req)
{
- struct bio *bio;
+ __free_page(req->page);
+ kfree(req);
+}
- bio = bio_alloc(zram->bdev, 1, parent ? parent->bi_opf : REQ_OP_READ,
- GFP_NOIO);
- if (!bio)
- return -ENOMEM;
+static void release_wb_ctl(struct zram_wb_ctl *wb_ctl)
+{
+ if (!wb_ctl)
+ return;
- bio->bi_iter.bi_sector = entry * (PAGE_SIZE >> 9);
- if (!bio_add_page(bio, bvec->bv_page, bvec->bv_len, bvec->bv_offset)) {
- bio_put(bio);
- return -EIO;
- }
+ /* We should never have inflight requests at this point */
+ WARN_ON(atomic_read(&wb_ctl->num_inflight));
+ WARN_ON(!list_empty(&wb_ctl->done_reqs));
- if (!parent)
- bio->bi_end_io = zram_page_end_io;
- else
- bio_chain(bio, parent);
+ while (!list_empty(&wb_ctl->idle_reqs)) {
+ struct zram_wb_req *req;
- submit_bio(bio);
- return 1;
+ req = list_first_entry(&wb_ctl->idle_reqs,
+ struct zram_wb_req, entry);
+ list_del(&req->entry);
+ release_wb_req(req);
+ }
+
+ kfree(wb_ctl);
}
-#define PAGE_WB_SIG "page_index="
+static struct zram_wb_ctl *init_wb_ctl(struct zram *zram)
+{
+ struct zram_wb_ctl *wb_ctl;
+ int i;
-#define PAGE_WRITEBACK 0
-#define HUGE_WRITEBACK (1<<0)
-#define IDLE_WRITEBACK (1<<1)
+ wb_ctl = kmalloc(sizeof(*wb_ctl), GFP_KERNEL);
+ if (!wb_ctl)
+ return NULL;
+ INIT_LIST_HEAD(&wb_ctl->idle_reqs);
+ INIT_LIST_HEAD(&wb_ctl->done_reqs);
+ atomic_set(&wb_ctl->num_inflight, 0);
+ init_waitqueue_head(&wb_ctl->done_wait);
+ spin_lock_init(&wb_ctl->done_lock);
-static ssize_t writeback_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
-{
- struct zram *zram = dev_to_zram(dev);
- unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
- unsigned long index = 0;
- struct bio bio;
- struct bio_vec bio_vec;
- struct page *page;
- ssize_t ret = len;
- int mode, err;
- unsigned long blk_idx = 0;
-
- if (sysfs_streq(buf, "idle"))
- mode = IDLE_WRITEBACK;
- else if (sysfs_streq(buf, "huge"))
- mode = HUGE_WRITEBACK;
- else if (sysfs_streq(buf, "huge_idle"))
- mode = IDLE_WRITEBACK | HUGE_WRITEBACK;
- else {
- if (strncmp(buf, PAGE_WB_SIG, sizeof(PAGE_WB_SIG) - 1))
- return -EINVAL;
+ for (i = 0; i < zram->wb_batch_size; i++) {
+ struct zram_wb_req *req;
- if (kstrtol(buf + sizeof(PAGE_WB_SIG) - 1, 10, &index) ||
- index >= nr_pages)
- return -EINVAL;
+ /*
+ * This is fatal condition only if we couldn't allocate
+ * any requests at all. Otherwise we just work with the
+ * requests that we have successfully allocated, so that
+ * writeback can still proceed, even if there is only one
+ * request on the idle list.
+ */
+ req = kzalloc(sizeof(*req), GFP_KERNEL | __GFP_NOWARN);
+ if (!req)
+ break;
- nr_pages = 1;
- mode = PAGE_WRITEBACK;
+ req->page = alloc_page(GFP_KERNEL | __GFP_NOWARN);
+ if (!req->page) {
+ kfree(req);
+ break;
+ }
+
+ list_add(&req->entry, &wb_ctl->idle_reqs);
}
- down_read(&zram->init_lock);
- if (!init_done(zram)) {
- ret = -EINVAL;
- goto release_init_lock;
+ /* We couldn't allocate any requests, so writeabck is not possible */
+ if (list_empty(&wb_ctl->idle_reqs))
+ goto release_wb_ctl;
+
+ return wb_ctl;
+
+release_wb_ctl:
+ release_wb_ctl(wb_ctl);
+ return NULL;
+}
+
+static void zram_account_writeback_rollback(struct zram *zram)
+{
+ lockdep_assert_held_read(&zram->init_lock);
+
+ if (zram->wb_limit_enable)
+ zram->bd_wb_limit += 1UL << (PAGE_SHIFT - 12);
+}
+
+static void zram_account_writeback_submit(struct zram *zram)
+{
+ lockdep_assert_held_read(&zram->init_lock);
+
+ if (zram->wb_limit_enable && zram->bd_wb_limit > 0)
+ zram->bd_wb_limit -= 1UL << (PAGE_SHIFT - 12);
+}
+
+static int zram_writeback_complete(struct zram *zram, struct zram_wb_req *req)
+{
+ u32 index = req->pps->index;
+ int err;
+
+ err = blk_status_to_errno(req->bio.bi_status);
+ if (err) {
+ /*
+ * Failed wb requests should not be accounted in wb_limit
+ * (if enabled).
+ */
+ zram_account_writeback_rollback(zram);
+ zram_release_bdev_block(zram, req->blk_idx);
+ return err;
}
- if (!zram->backing_dev) {
- ret = -ENODEV;
- goto release_init_lock;
+ atomic64_inc(&zram->stats.bd_writes);
+ zram_slot_lock(zram, index);
+ /*
+ * We release slot lock during writeback so slot can change under us:
+ * slot_free() or slot_free() and zram_write_page(). In both cases
+ * slot loses ZRAM_PP_SLOT flag. No concurrent post-processing can
+ * set ZRAM_PP_SLOT on such slots until current post-processing
+ * finishes.
+ */
+ if (!zram_test_flag(zram, index, ZRAM_PP_SLOT)) {
+ zram_release_bdev_block(zram, req->blk_idx);
+ goto out;
}
- page = alloc_page(GFP_KERNEL);
- if (!page) {
- ret = -ENOMEM;
- goto release_init_lock;
+ zram_free_page(zram, index);
+ zram_set_flag(zram, index, ZRAM_WB);
+ zram_set_handle(zram, index, req->blk_idx);
+ atomic64_inc(&zram->stats.pages_stored);
+
+out:
+ zram_slot_unlock(zram, index);
+ return 0;
+}
+
+static void zram_writeback_endio(struct bio *bio)
+{
+ struct zram_wb_req *req = container_of(bio, struct zram_wb_req, bio);
+ struct zram_wb_ctl *wb_ctl = bio->bi_private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wb_ctl->done_lock, flags);
+ list_add(&req->entry, &wb_ctl->done_reqs);
+ spin_unlock_irqrestore(&wb_ctl->done_lock, flags);
+
+ wake_up(&wb_ctl->done_wait);
+}
+
+static void zram_submit_wb_request(struct zram *zram,
+ struct zram_wb_ctl *wb_ctl,
+ struct zram_wb_req *req)
+{
+ /*
+ * wb_limit (if enabled) should be adjusted before submission,
+ * so that we don't over-submit.
+ */
+ zram_account_writeback_submit(zram);
+ atomic_inc(&wb_ctl->num_inflight);
+ req->bio.bi_private = wb_ctl;
+ submit_bio(&req->bio);
+}
+
+static int zram_complete_done_reqs(struct zram *zram,
+ struct zram_wb_ctl *wb_ctl)
+{
+ struct zram_wb_req *req;
+ unsigned long flags;
+ int ret = 0, err;
+
+ while (atomic_read(&wb_ctl->num_inflight) > 0) {
+ spin_lock_irqsave(&wb_ctl->done_lock, flags);
+ req = list_first_entry_or_null(&wb_ctl->done_reqs,
+ struct zram_wb_req, entry);
+ if (req)
+ list_del(&req->entry);
+ spin_unlock_irqrestore(&wb_ctl->done_lock, flags);
+
+ /* ->num_inflight > 0 doesn't mean we have done requests */
+ if (!req)
+ break;
+
+ err = zram_writeback_complete(zram, req);
+ if (err)
+ ret = err;
+
+ atomic_dec(&wb_ctl->num_inflight);
+ release_pp_slot(zram, req->pps);
+ req->pps = NULL;
+
+ list_add(&req->entry, &wb_ctl->idle_reqs);
}
- for (; nr_pages != 0; index++, nr_pages--) {
- struct bio_vec bvec;
+ return ret;
+}
+
+static struct zram_wb_req *zram_select_idle_req(struct zram_wb_ctl *wb_ctl)
+{
+ struct zram_wb_req *req;
+
+ req = list_first_entry_or_null(&wb_ctl->idle_reqs,
+ struct zram_wb_req, entry);
+ if (req)
+ list_del(&req->entry);
+ return req;
+}
- bvec.bv_page = page;
- bvec.bv_len = PAGE_SIZE;
- bvec.bv_offset = 0;
+static int zram_writeback_slots(struct zram *zram,
+ struct zram_pp_ctl *ctl,
+ struct zram_wb_ctl *wb_ctl)
+{
+ unsigned long blk_idx = INVALID_BDEV_BLOCK;
+ struct zram_wb_req *req = NULL;
+ struct zram_pp_slot *pps;
+ int ret = 0, err = 0;
+ u32 index = 0;
- spin_lock(&zram->wb_limit_lock);
+ while ((pps = select_pp_slot(ctl))) {
if (zram->wb_limit_enable && !zram->bd_wb_limit) {
- spin_unlock(&zram->wb_limit_lock);
ret = -EIO;
break;
}
- spin_unlock(&zram->wb_limit_lock);
- if (!blk_idx) {
- blk_idx = alloc_block_bdev(zram);
- if (!blk_idx) {
+ while (!req) {
+ req = zram_select_idle_req(wb_ctl);
+ if (req)
+ break;
+
+ wait_event(wb_ctl->done_wait,
+ !list_empty(&wb_ctl->done_reqs));
+
+ err = zram_complete_done_reqs(zram, wb_ctl);
+ /*
+ * BIO errors are not fatal, we continue and simply
+ * attempt to writeback the remaining objects (pages).
+ * At the same time we need to signal user-space that
+ * some writes (at least one, but also could be all of
+ * them) were not successful and we do so by returning
+ * the most recent BIO error.
+ */
+ if (err)
+ ret = err;
+ }
+
+ if (blk_idx == INVALID_BDEV_BLOCK) {
+ blk_idx = zram_reserve_bdev_block(zram);
+ if (blk_idx == INVALID_BDEV_BLOCK) {
ret = -ENOSPC;
break;
}
}
+ index = pps->index;
+ zram_slot_lock(zram, index);
+ /*
+ * scan_slots() sets ZRAM_PP_SLOT and releases slot lock, so
+ * slots can change in the meantime. If slots are accessed or
+ * freed they lose ZRAM_PP_SLOT flag and hence we don't
+ * post-process them.
+ */
+ if (!zram_test_flag(zram, index, ZRAM_PP_SLOT))
+ goto next;
+ if (zram_read_from_zspool(zram, req->page, index))
+ goto next;
+ zram_slot_unlock(zram, index);
+
+ /*
+ * From now on pp-slot is owned by the req, remove it from
+ * its pp bucket.
+ */
+ list_del_init(&pps->entry);
+
+ req->blk_idx = blk_idx;
+ req->pps = pps;
+ bio_init(&req->bio, zram->bdev, &req->bio_vec, 1, REQ_OP_WRITE);
+ req->bio.bi_iter.bi_sector = req->blk_idx * (PAGE_SIZE >> 9);
+ req->bio.bi_end_io = zram_writeback_endio;
+ __bio_add_page(&req->bio, req->page, PAGE_SIZE, 0);
+
+ zram_submit_wb_request(zram, wb_ctl, req);
+ blk_idx = INVALID_BDEV_BLOCK;
+ req = NULL;
+ cond_resched();
+ continue;
+
+next:
+ zram_slot_unlock(zram, index);
+ release_pp_slot(zram, pps);
+ }
+
+ /*
+ * Selected idle req, but never submitted it due to some error or
+ * wb limit.
+ */
+ if (req)
+ release_wb_req(req);
+
+ while (atomic_read(&wb_ctl->num_inflight) > 0) {
+ wait_event(wb_ctl->done_wait, !list_empty(&wb_ctl->done_reqs));
+ err = zram_complete_done_reqs(zram, wb_ctl);
+ if (err)
+ ret = err;
+ }
+
+ return ret;
+}
+
+#define PAGE_WRITEBACK 0
+#define HUGE_WRITEBACK (1 << 0)
+#define IDLE_WRITEBACK (1 << 1)
+#define INCOMPRESSIBLE_WRITEBACK (1 << 2)
+
+static int parse_page_index(char *val, unsigned long nr_pages,
+ unsigned long *lo, unsigned long *hi)
+{
+ int ret;
+
+ ret = kstrtoul(val, 10, lo);
+ if (ret)
+ return ret;
+ if (*lo >= nr_pages)
+ return -ERANGE;
+ *hi = *lo + 1;
+ return 0;
+}
+
+static int parse_page_indexes(char *val, unsigned long nr_pages,
+ unsigned long *lo, unsigned long *hi)
+{
+ char *delim;
+ int ret;
+
+ delim = strchr(val, '-');
+ if (!delim)
+ return -EINVAL;
+
+ *delim = 0x00;
+ ret = kstrtoul(val, 10, lo);
+ if (ret)
+ return ret;
+ if (*lo >= nr_pages)
+ return -ERANGE;
+
+ ret = kstrtoul(delim + 1, 10, hi);
+ if (ret)
+ return ret;
+ if (*hi >= nr_pages || *lo > *hi)
+ return -ERANGE;
+ *hi += 1;
+ return 0;
+}
+
+static int parse_mode(char *val, u32 *mode)
+{
+ *mode = 0;
+
+ if (!strcmp(val, "idle"))
+ *mode = IDLE_WRITEBACK;
+ if (!strcmp(val, "huge"))
+ *mode = HUGE_WRITEBACK;
+ if (!strcmp(val, "huge_idle"))
+ *mode = IDLE_WRITEBACK | HUGE_WRITEBACK;
+ if (!strcmp(val, "incompressible"))
+ *mode = INCOMPRESSIBLE_WRITEBACK;
+
+ if (*mode == 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int scan_slots_for_writeback(struct zram *zram, u32 mode,
+ unsigned long lo, unsigned long hi,
+ struct zram_pp_ctl *ctl)
+{
+ u32 index = lo;
+
+ while (index < hi) {
+ bool ok = true;
+
zram_slot_lock(zram, index);
if (!zram_allocated(zram, index))
goto next;
if (zram_test_flag(zram, index, ZRAM_WB) ||
- zram_test_flag(zram, index, ZRAM_SAME) ||
- zram_test_flag(zram, index, ZRAM_UNDER_WB))
+ zram_test_flag(zram, index, ZRAM_SAME))
goto next;
if (mode & IDLE_WRITEBACK &&
- !zram_test_flag(zram, index, ZRAM_IDLE))
+ !zram_test_flag(zram, index, ZRAM_IDLE))
goto next;
if (mode & HUGE_WRITEBACK &&
- !zram_test_flag(zram, index, ZRAM_HUGE))
+ !zram_test_flag(zram, index, ZRAM_HUGE))
goto next;
- /*
- * Clearing ZRAM_UNDER_WB is duty of caller.
- * IOW, zram_free_page never clear it.
- */
- zram_set_flag(zram, index, ZRAM_UNDER_WB);
- /* Need for hugepage writeback racing */
- zram_set_flag(zram, index, ZRAM_IDLE);
+ if (mode & INCOMPRESSIBLE_WRITEBACK &&
+ !zram_test_flag(zram, index, ZRAM_INCOMPRESSIBLE))
+ goto next;
+
+ ok = place_pp_slot(zram, ctl, index);
+next:
zram_slot_unlock(zram, index);
- if (zram_bvec_read(zram, &bvec, index, 0, NULL)) {
- zram_slot_lock(zram, index);
- zram_clear_flag(zram, index, ZRAM_UNDER_WB);
- zram_clear_flag(zram, index, ZRAM_IDLE);
- zram_slot_unlock(zram, index);
- continue;
- }
+ if (!ok)
+ break;
+ index++;
+ }
+
+ return 0;
+}
+
+static ssize_t writeback_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct zram *zram = dev_to_zram(dev);
+ u64 nr_pages = zram->disksize >> PAGE_SHIFT;
+ unsigned long lo = 0, hi = nr_pages;
+ struct zram_pp_ctl *pp_ctl = NULL;
+ struct zram_wb_ctl *wb_ctl = NULL;
+ char *args, *param, *val;
+ ssize_t ret = len;
+ int err, mode = 0;
+
+ down_read(&zram->init_lock);
+ if (!init_done(zram)) {
+ up_read(&zram->init_lock);
+ return -EINVAL;
+ }
+
+ /* Do not permit concurrent post-processing actions. */
+ if (atomic_xchg(&zram->pp_in_progress, 1)) {
+ up_read(&zram->init_lock);
+ return -EAGAIN;
+ }
- bio_init(&bio, zram->bdev, &bio_vec, 1,
- REQ_OP_WRITE | REQ_SYNC);
- bio.bi_iter.bi_sector = blk_idx * (PAGE_SIZE >> 9);
+ if (!zram->backing_dev) {
+ ret = -ENODEV;
+ goto release_init_lock;
+ }
+
+ pp_ctl = init_pp_ctl();
+ if (!pp_ctl) {
+ ret = -ENOMEM;
+ goto release_init_lock;
+ }
+
+ wb_ctl = init_wb_ctl(zram);
+ if (!wb_ctl) {
+ ret = -ENOMEM;
+ goto release_init_lock;
+ }
+
+ args = skip_spaces(buf);
+ while (*args) {
+ args = next_arg(args, &param, &val);
- bio_add_page(&bio, bvec.bv_page, bvec.bv_len,
- bvec.bv_offset);
/*
- * XXX: A single page IO would be inefficient for write
- * but it would be not bad as starter.
+ * Workaround to support the old writeback interface.
+ *
+ * The old writeback interface has a minor inconsistency and
+ * requires key=value only for page_index parameter, while the
+ * writeback mode is a valueless parameter.
+ *
+ * This is not the case anymore and now all parameters are
+ * required to have values, however, we need to support the
+ * legacy writeback interface format so we check if we can
+ * recognize a valueless parameter as the (legacy) writeback
+ * mode.
*/
- err = submit_bio_wait(&bio);
- if (err) {
- zram_slot_lock(zram, index);
- zram_clear_flag(zram, index, ZRAM_UNDER_WB);
- zram_clear_flag(zram, index, ZRAM_IDLE);
- zram_slot_unlock(zram, index);
- /*
- * Return last IO error unless every IO were
- * not suceeded.
- */
- ret = err;
- continue;
+ if (!val || !*val) {
+ err = parse_mode(param, &mode);
+ if (err) {
+ ret = err;
+ goto release_init_lock;
+ }
+
+ scan_slots_for_writeback(zram, mode, lo, hi, pp_ctl);
+ break;
}
- atomic64_inc(&zram->stats.bd_writes);
- /*
- * We released zram_slot_lock so need to check if the slot was
- * changed. If there is freeing for the slot, we can catch it
- * easily by zram_allocated.
- * A subtle case is the slot is freed/reallocated/marked as
- * ZRAM_IDLE again. To close the race, idle_store doesn't
- * mark ZRAM_IDLE once it found the slot was ZRAM_UNDER_WB.
- * Thus, we could close the race by checking ZRAM_IDLE bit.
- */
- zram_slot_lock(zram, index);
- if (!zram_allocated(zram, index) ||
- !zram_test_flag(zram, index, ZRAM_IDLE)) {
- zram_clear_flag(zram, index, ZRAM_UNDER_WB);
- zram_clear_flag(zram, index, ZRAM_IDLE);
- goto next;
+ if (!strcmp(param, "type")) {
+ err = parse_mode(val, &mode);
+ if (err) {
+ ret = err;
+ goto release_init_lock;
+ }
+
+ scan_slots_for_writeback(zram, mode, lo, hi, pp_ctl);
+ break;
}
- zram_free_page(zram, index);
- zram_clear_flag(zram, index, ZRAM_UNDER_WB);
- zram_set_flag(zram, index, ZRAM_WB);
- zram_set_element(zram, index, blk_idx);
- blk_idx = 0;
- atomic64_inc(&zram->stats.pages_stored);
- spin_lock(&zram->wb_limit_lock);
- if (zram->wb_limit_enable && zram->bd_wb_limit > 0)
- zram->bd_wb_limit -= 1UL << (PAGE_SHIFT - 12);
- spin_unlock(&zram->wb_limit_lock);
-next:
- zram_slot_unlock(zram, index);
+ if (!strcmp(param, "page_index")) {
+ err = parse_page_index(val, nr_pages, &lo, &hi);
+ if (err) {
+ ret = err;
+ goto release_init_lock;
+ }
+
+ scan_slots_for_writeback(zram, mode, lo, hi, pp_ctl);
+ continue;
+ }
+
+ if (!strcmp(param, "page_indexes")) {
+ err = parse_page_indexes(val, nr_pages, &lo, &hi);
+ if (err) {
+ ret = err;
+ goto release_init_lock;
+ }
+
+ scan_slots_for_writeback(zram, mode, lo, hi, pp_ctl);
+ continue;
+ }
}
- if (blk_idx)
- free_block_bdev(zram, blk_idx);
- __free_page(page);
+ err = zram_writeback_slots(zram, pp_ctl, wb_ctl);
+ if (err)
+ ret = err;
+
release_init_lock:
+ release_pp_ctl(zram, pp_ctl);
+ release_wb_ctl(wb_ctl);
+ atomic_set(&zram->pp_in_progress, 0);
up_read(&zram->init_lock);
return ret;
@@ -816,19 +1317,20 @@ struct zram_work {
struct work_struct work;
struct zram *zram;
unsigned long entry;
- struct bio *bio;
- struct bio_vec bvec;
+ struct page *page;
+ int error;
};
-#if PAGE_SIZE != 4096
static void zram_sync_read(struct work_struct *work)
{
struct zram_work *zw = container_of(work, struct zram_work, work);
- struct zram *zram = zw->zram;
- unsigned long entry = zw->entry;
- struct bio *bio = zw->bio;
+ struct bio_vec bv;
+ struct bio bio;
- read_from_bdev_async(zram, &zw->bvec, entry, bio);
+ bio_init(&bio, zw->zram->bdev, &bv, 1, REQ_OP_READ);
+ bio.bi_iter.bi_sector = zw->entry * (PAGE_SIZE >> 9);
+ __bio_add_page(&bio, zw->page, PAGE_SIZE, 0);
+ zw->error = submit_bio_wait(&bio);
}
/*
@@ -836,50 +1338,46 @@ static void zram_sync_read(struct work_struct *work)
* chained IO with parent IO in same context, it's a deadlock. To avoid that,
* use a worker thread context.
*/
-static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec,
- unsigned long entry, struct bio *bio)
+static int read_from_bdev_sync(struct zram *zram, struct page *page,
+ unsigned long entry)
{
struct zram_work work;
- work.bvec = *bvec;
+ work.page = page;
work.zram = zram;
work.entry = entry;
- work.bio = bio;
INIT_WORK_ONSTACK(&work.work, zram_sync_read);
- queue_work(system_unbound_wq, &work.work);
+ queue_work(system_dfl_wq, &work.work);
flush_work(&work.work);
destroy_work_on_stack(&work.work);
- return 1;
+ return work.error;
}
-#else
-static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec,
- unsigned long entry, struct bio *bio)
-{
- WARN_ON(1);
- return -EIO;
-}
-#endif
-static int read_from_bdev(struct zram *zram, struct bio_vec *bvec,
- unsigned long entry, struct bio *parent, bool sync)
+static int read_from_bdev(struct zram *zram, struct page *page,
+ unsigned long entry, struct bio *parent)
{
atomic64_inc(&zram->stats.bd_reads);
- if (sync)
- return read_from_bdev_sync(zram, bvec, entry, parent);
- else
- return read_from_bdev_async(zram, bvec, entry, parent);
+ if (!parent) {
+ if (WARN_ON_ONCE(!IS_ENABLED(ZRAM_PARTIAL_IO)))
+ return -EIO;
+ return read_from_bdev_sync(zram, page, entry);
+ }
+ read_from_bdev_async(zram, page, entry, parent);
+ return 0;
}
#else
static inline void reset_bdev(struct zram *zram) {};
-static int read_from_bdev(struct zram *zram, struct bio_vec *bvec,
- unsigned long entry, struct bio *parent, bool sync)
+static int read_from_bdev(struct zram *zram, struct page *page,
+ unsigned long entry, struct bio *parent)
{
return -EIO;
}
-static void free_block_bdev(struct zram *zram, unsigned long blk_idx) {};
+static void zram_release_bdev_block(struct zram *zram, unsigned long blk_idx)
+{
+}
#endif
#ifdef CONFIG_ZRAM_MEMORY_TRACKING
@@ -896,12 +1394,6 @@ static void zram_debugfs_destroy(void)
debugfs_remove_recursive(zram_debugfs_root);
}
-static void zram_accessed(struct zram *zram, u32 index)
-{
- zram_clear_flag(zram, index, ZRAM_IDLE);
- zram->table[index].ac_time = ktime_get_boottime();
-}
-
static ssize_t read_block_state(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -931,13 +1423,16 @@ static ssize_t read_block_state(struct file *file, char __user *buf,
ts = ktime_to_timespec64(zram->table[index].ac_time);
copied = snprintf(kbuf + written, count,
- "%12zd %12lld.%06lu %c%c%c%c\n",
+ "%12zd %12lld.%06lu %c%c%c%c%c%c\n",
index, (s64)ts.tv_sec,
ts.tv_nsec / NSEC_PER_USEC,
zram_test_flag(zram, index, ZRAM_SAME) ? 's' : '.',
zram_test_flag(zram, index, ZRAM_WB) ? 'w' : '.',
zram_test_flag(zram, index, ZRAM_HUGE) ? 'h' : '.',
- zram_test_flag(zram, index, ZRAM_IDLE) ? 'i' : '.');
+ zram_test_flag(zram, index, ZRAM_IDLE) ? 'i' : '.',
+ zram_get_priority(zram, index) ? 'r' : '.',
+ zram_test_flag(zram, index,
+ ZRAM_INCOMPRESSIBLE) ? 'n' : '.');
if (count <= copied) {
zram_slot_unlock(zram, index);
@@ -982,76 +1477,252 @@ static void zram_debugfs_unregister(struct zram *zram)
#else
static void zram_debugfs_create(void) {};
static void zram_debugfs_destroy(void) {};
-static void zram_accessed(struct zram *zram, u32 index)
-{
- zram_clear_flag(zram, index, ZRAM_IDLE);
-};
static void zram_debugfs_register(struct zram *zram) {};
static void zram_debugfs_unregister(struct zram *zram) {};
#endif
-/*
- * We switched to per-cpu streams and this attr is not needed anymore.
- * However, we will keep it around for some time, because:
- * a) we may revert per-cpu streams in the future
- * b) it's visible to user space and we need to follow our 2 years
- * retirement rule; but we already have a number of 'soon to be
- * altered' attrs, so max_comp_streams need to wait for the next
- * layoff cycle.
- */
-static ssize_t max_comp_streams_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static void comp_algorithm_set(struct zram *zram, u32 prio, const char *alg)
{
- return scnprintf(buf, PAGE_SIZE, "%d\n", num_online_cpus());
-}
+ /* Do not free statically defined compression algorithms */
+ if (zram->comp_algs[prio] != default_compressor)
+ kfree(zram->comp_algs[prio]);
-static ssize_t max_comp_streams_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
-{
- return len;
+ zram->comp_algs[prio] = alg;
}
-static ssize_t comp_algorithm_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static int __comp_algorithm_store(struct zram *zram, u32 prio, const char *buf)
{
+ char *compressor;
size_t sz;
- struct zram *zram = dev_to_zram(dev);
- down_read(&zram->init_lock);
- sz = zcomp_available_show(zram->compressor, buf);
- up_read(&zram->init_lock);
+ sz = strlen(buf);
+ if (sz >= ZRAM_MAX_ALGO_NAME_SZ)
+ return -E2BIG;
- return sz;
-}
-
-static ssize_t comp_algorithm_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
-{
- struct zram *zram = dev_to_zram(dev);
- char compressor[ARRAY_SIZE(zram->compressor)];
- size_t sz;
+ compressor = kstrdup(buf, GFP_KERNEL);
+ if (!compressor)
+ return -ENOMEM;
- strlcpy(compressor, buf, sizeof(compressor));
/* ignore trailing newline */
- sz = strlen(compressor);
if (sz > 0 && compressor[sz - 1] == '\n')
compressor[sz - 1] = 0x00;
- if (!zcomp_available_algorithm(compressor))
+ if (!zcomp_available_algorithm(compressor)) {
+ kfree(compressor);
return -EINVAL;
+ }
down_write(&zram->init_lock);
if (init_done(zram)) {
up_write(&zram->init_lock);
+ kfree(compressor);
pr_info("Can't change algorithm for initialized device\n");
return -EBUSY;
}
- strcpy(zram->compressor, compressor);
+ comp_algorithm_set(zram, prio, compressor);
up_write(&zram->init_lock);
- return len;
+ return 0;
+}
+
+static void comp_params_reset(struct zram *zram, u32 prio)
+{
+ struct zcomp_params *params = &zram->params[prio];
+
+ vfree(params->dict);
+ params->level = ZCOMP_PARAM_NOT_SET;
+ params->deflate.winbits = ZCOMP_PARAM_NOT_SET;
+ params->dict_sz = 0;
+ params->dict = NULL;
+}
+
+static int comp_params_store(struct zram *zram, u32 prio, s32 level,
+ const char *dict_path,
+ struct deflate_params *deflate_params)
+{
+ ssize_t sz = 0;
+
+ comp_params_reset(zram, prio);
+
+ if (dict_path) {
+ sz = kernel_read_file_from_path(dict_path, 0,
+ &zram->params[prio].dict,
+ INT_MAX,
+ NULL,
+ READING_POLICY);
+ if (sz < 0)
+ return -EINVAL;
+ }
+
+ zram->params[prio].dict_sz = sz;
+ zram->params[prio].level = level;
+ zram->params[prio].deflate.winbits = deflate_params->winbits;
+ return 0;
+}
+
+static ssize_t algorithm_params_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ s32 prio = ZRAM_PRIMARY_COMP, level = ZCOMP_PARAM_NOT_SET;
+ char *args, *param, *val, *algo = NULL, *dict_path = NULL;
+ struct deflate_params deflate_params;
+ struct zram *zram = dev_to_zram(dev);
+ int ret;
+
+ deflate_params.winbits = ZCOMP_PARAM_NOT_SET;
+
+ args = skip_spaces(buf);
+ while (*args) {
+ args = next_arg(args, &param, &val);
+
+ if (!val || !*val)
+ return -EINVAL;
+
+ if (!strcmp(param, "priority")) {
+ ret = kstrtoint(val, 10, &prio);
+ if (ret)
+ return ret;
+ continue;
+ }
+
+ if (!strcmp(param, "level")) {
+ ret = kstrtoint(val, 10, &level);
+ if (ret)
+ return ret;
+ continue;
+ }
+
+ if (!strcmp(param, "algo")) {
+ algo = val;
+ continue;
+ }
+
+ if (!strcmp(param, "dict")) {
+ dict_path = val;
+ continue;
+ }
+
+ if (!strcmp(param, "deflate.winbits")) {
+ ret = kstrtoint(val, 10, &deflate_params.winbits);
+ if (ret)
+ return ret;
+ continue;
+ }
+ }
+
+ /* Lookup priority by algorithm name */
+ if (algo) {
+ s32 p;
+
+ prio = -EINVAL;
+ for (p = ZRAM_PRIMARY_COMP; p < ZRAM_MAX_COMPS; p++) {
+ if (!zram->comp_algs[p])
+ continue;
+
+ if (!strcmp(zram->comp_algs[p], algo)) {
+ prio = p;
+ break;
+ }
+ }
+ }
+
+ if (prio < ZRAM_PRIMARY_COMP || prio >= ZRAM_MAX_COMPS)
+ return -EINVAL;
+
+ ret = comp_params_store(zram, prio, level, dict_path, &deflate_params);
+ return ret ? ret : len;
}
+static ssize_t comp_algorithm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct zram *zram = dev_to_zram(dev);
+ ssize_t sz;
+
+ down_read(&zram->init_lock);
+ sz = zcomp_available_show(zram->comp_algs[ZRAM_PRIMARY_COMP], buf, 0);
+ up_read(&zram->init_lock);
+ return sz;
+}
+
+static ssize_t comp_algorithm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct zram *zram = dev_to_zram(dev);
+ int ret;
+
+ ret = __comp_algorithm_store(zram, ZRAM_PRIMARY_COMP, buf);
+ return ret ? ret : len;
+}
+
+#ifdef CONFIG_ZRAM_MULTI_COMP
+static ssize_t recomp_algorithm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct zram *zram = dev_to_zram(dev);
+ ssize_t sz = 0;
+ u32 prio;
+
+ down_read(&zram->init_lock);
+ for (prio = ZRAM_SECONDARY_COMP; prio < ZRAM_MAX_COMPS; prio++) {
+ if (!zram->comp_algs[prio])
+ continue;
+
+ sz += sysfs_emit_at(buf, sz, "#%d: ", prio);
+ sz += zcomp_available_show(zram->comp_algs[prio], buf, sz);
+ }
+ up_read(&zram->init_lock);
+ return sz;
+}
+
+static ssize_t recomp_algorithm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct zram *zram = dev_to_zram(dev);
+ int prio = ZRAM_SECONDARY_COMP;
+ char *args, *param, *val;
+ char *alg = NULL;
+ int ret;
+
+ args = skip_spaces(buf);
+ while (*args) {
+ args = next_arg(args, &param, &val);
+
+ if (!val || !*val)
+ return -EINVAL;
+
+ if (!strcmp(param, "algo")) {
+ alg = val;
+ continue;
+ }
+
+ if (!strcmp(param, "priority")) {
+ ret = kstrtoint(val, 10, &prio);
+ if (ret)
+ return ret;
+ continue;
+ }
+ }
+
+ if (!alg)
+ return -EINVAL;
+
+ if (prio < ZRAM_SECONDARY_COMP || prio >= ZRAM_MAX_COMPS)
+ return -EINVAL;
+
+ ret = __comp_algorithm_store(zram, prio, alg);
+ return ret ? ret : len;
+}
+#endif
+
static ssize_t compact_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
@@ -1076,11 +1747,10 @@ static ssize_t io_stat_show(struct device *dev,
ssize_t ret;
down_read(&zram->init_lock);
- ret = scnprintf(buf, PAGE_SIZE,
- "%8llu %8llu %8llu %8llu\n",
+ ret = sysfs_emit(buf,
+ "%8llu %8llu 0 %8llu\n",
(u64)atomic64_read(&zram->stats.failed_reads),
(u64)atomic64_read(&zram->stats.failed_writes),
- (u64)atomic64_read(&zram->stats.invalid_io),
(u64)atomic64_read(&zram->stats.notify_free));
up_read(&zram->init_lock);
@@ -1107,7 +1777,7 @@ static ssize_t mm_stat_show(struct device *dev,
orig_size = atomic64_read(&zram->stats.pages_stored);
max_used = atomic_long_read(&zram->stats.max_used_pages);
- ret = scnprintf(buf, PAGE_SIZE,
+ ret = sysfs_emit(buf,
"%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu %8llu\n",
orig_size << PAGE_SHIFT,
(u64)atomic64_read(&zram->stats.compr_data_size),
@@ -1132,8 +1802,8 @@ static ssize_t bd_stat_show(struct device *dev,
ssize_t ret;
down_read(&zram->init_lock);
- ret = scnprintf(buf, PAGE_SIZE,
- "%8llu %8llu %8llu\n",
+ ret = sysfs_emit(buf,
+ "%8llu %8llu %8llu\n",
FOUR_K((u64)atomic64_read(&zram->stats.bd_count)),
FOUR_K((u64)atomic64_read(&zram->stats.bd_reads)),
FOUR_K((u64)atomic64_read(&zram->stats.bd_writes)));
@@ -1151,10 +1821,9 @@ static ssize_t debug_stat_show(struct device *dev,
ssize_t ret;
down_read(&zram->init_lock);
- ret = scnprintf(buf, PAGE_SIZE,
- "version: %d\n%8llu %8llu\n",
+ ret = sysfs_emit(buf,
+ "version: %d\n0 %8llu\n",
version,
- (u64)atomic64_read(&zram->stats.writestall),
(u64)atomic64_read(&zram->stats.miss_free));
up_read(&zram->init_lock);
@@ -1173,17 +1842,21 @@ static void zram_meta_free(struct zram *zram, u64 disksize)
size_t num_pages = disksize >> PAGE_SHIFT;
size_t index;
+ if (!zram->table)
+ return;
+
/* Free all pages that are still in this zram device */
for (index = 0; index < num_pages; index++)
zram_free_page(zram, index);
zs_destroy_pool(zram->mem_pool);
vfree(zram->table);
+ zram->table = NULL;
}
static bool zram_meta_alloc(struct zram *zram, u64 disksize)
{
- size_t num_pages;
+ size_t num_pages, index;
num_pages = disksize >> PAGE_SHIFT;
zram->table = vzalloc(array_size(num_pages, sizeof(*zram->table)));
@@ -1193,28 +1866,31 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize)
zram->mem_pool = zs_create_pool(zram->disk->disk_name);
if (!zram->mem_pool) {
vfree(zram->table);
+ zram->table = NULL;
return false;
}
if (!huge_class_size)
huge_class_size = zs_huge_class_size(zram->mem_pool);
+
+ for (index = 0; index < num_pages; index++)
+ zram_slot_lock_init(zram, index);
+
return true;
}
-/*
- * To protect concurrent access to the same index entry,
- * caller should hold this table index entry's bit_spinlock to
- * indicate this index entry is accessing.
- */
static void zram_free_page(struct zram *zram, size_t index)
{
unsigned long handle;
-#ifdef CONFIG_ZRAM_MEMORY_TRACKING
+#ifdef CONFIG_ZRAM_TRACK_ENTRY_ACTIME
zram->table[index].ac_time = 0;
#endif
- if (zram_test_flag(zram, index, ZRAM_IDLE))
- zram_clear_flag(zram, index, ZRAM_IDLE);
+
+ zram_clear_flag(zram, index, ZRAM_IDLE);
+ zram_clear_flag(zram, index, ZRAM_INCOMPRESSIBLE);
+ zram_clear_flag(zram, index, ZRAM_PP_SLOT);
+ zram_set_priority(zram, index, 0);
if (zram_test_flag(zram, index, ZRAM_HUGE)) {
zram_clear_flag(zram, index, ZRAM_HUGE);
@@ -1223,7 +1899,7 @@ static void zram_free_page(struct zram *zram, size_t index)
if (zram_test_flag(zram, index, ZRAM_WB)) {
zram_clear_flag(zram, index, ZRAM_WB);
- free_block_bdev(zram, zram_get_element(zram, index));
+ zram_release_bdev_block(zram, zram_get_handle(zram, index));
goto out;
}
@@ -1244,273 +1920,646 @@ static void zram_free_page(struct zram *zram, size_t index)
zs_free(zram->mem_pool, handle);
atomic64_sub(zram_get_obj_size(zram, index),
- &zram->stats.compr_data_size);
+ &zram->stats.compr_data_size);
out:
atomic64_dec(&zram->stats.pages_stored);
zram_set_handle(zram, index, 0);
zram_set_obj_size(zram, index, 0);
- WARN_ON_ONCE(zram->table[index].flags &
- ~(1UL << ZRAM_LOCK | 1UL << ZRAM_UNDER_WB));
}
-static int __zram_bvec_read(struct zram *zram, struct page *page, u32 index,
- struct bio *bio, bool partial_io)
+static int read_same_filled_page(struct zram *zram, struct page *page,
+ u32 index)
+{
+ void *mem;
+
+ mem = kmap_local_page(page);
+ zram_fill_page(mem, PAGE_SIZE, zram_get_handle(zram, index));
+ kunmap_local(mem);
+ return 0;
+}
+
+static int read_incompressible_page(struct zram *zram, struct page *page,
+ u32 index)
+{
+ unsigned long handle;
+ void *src, *dst;
+
+ handle = zram_get_handle(zram, index);
+ src = zs_obj_read_begin(zram->mem_pool, handle, NULL);
+ dst = kmap_local_page(page);
+ copy_page(dst, src);
+ kunmap_local(dst);
+ zs_obj_read_end(zram->mem_pool, handle, src);
+
+ return 0;
+}
+
+static int read_compressed_page(struct zram *zram, struct page *page, u32 index)
{
struct zcomp_strm *zstrm;
unsigned long handle;
unsigned int size;
void *src, *dst;
- int ret;
+ int ret, prio;
- zram_slot_lock(zram, index);
- if (zram_test_flag(zram, index, ZRAM_WB)) {
- struct bio_vec bvec;
+ handle = zram_get_handle(zram, index);
+ size = zram_get_obj_size(zram, index);
+ prio = zram_get_priority(zram, index);
- zram_slot_unlock(zram, index);
+ zstrm = zcomp_stream_get(zram->comps[prio]);
+ src = zs_obj_read_begin(zram->mem_pool, handle, zstrm->local_copy);
+ dst = kmap_local_page(page);
+ ret = zcomp_decompress(zram->comps[prio], zstrm, src, size, dst);
+ kunmap_local(dst);
+ zs_obj_read_end(zram->mem_pool, handle, src);
+ zcomp_stream_put(zstrm);
- bvec.bv_page = page;
- bvec.bv_len = PAGE_SIZE;
- bvec.bv_offset = 0;
- return read_from_bdev(zram, &bvec,
- zram_get_element(zram, index),
- bio, partial_io);
- }
+ return ret;
+}
- handle = zram_get_handle(zram, index);
- if (!handle || zram_test_flag(zram, index, ZRAM_SAME)) {
- unsigned long value;
- void *mem;
-
- value = handle ? zram_get_element(zram, index) : 0;
- mem = kmap_atomic(page);
- zram_fill_page(mem, PAGE_SIZE, value);
- kunmap_atomic(mem);
- zram_slot_unlock(zram, index);
- return 0;
- }
+/*
+ * Reads (decompresses if needed) a page from zspool (zsmalloc).
+ * Corresponding ZRAM slot should be locked.
+ */
+static int zram_read_from_zspool(struct zram *zram, struct page *page,
+ u32 index)
+{
+ if (zram_test_flag(zram, index, ZRAM_SAME) ||
+ !zram_get_handle(zram, index))
+ return read_same_filled_page(zram, page, index);
- size = zram_get_obj_size(zram, index);
+ if (!zram_test_flag(zram, index, ZRAM_HUGE))
+ return read_compressed_page(zram, page, index);
+ else
+ return read_incompressible_page(zram, page, index);
+}
- if (size != PAGE_SIZE)
- zstrm = zcomp_stream_get(zram->comp);
+static int zram_read_page(struct zram *zram, struct page *page, u32 index,
+ struct bio *parent)
+{
+ int ret;
- src = zs_map_object(zram->mem_pool, handle, ZS_MM_RO);
- if (size == PAGE_SIZE) {
- dst = kmap_atomic(page);
- memcpy(dst, src, PAGE_SIZE);
- kunmap_atomic(dst);
- ret = 0;
+ zram_slot_lock(zram, index);
+ if (!zram_test_flag(zram, index, ZRAM_WB)) {
+ /* Slot should be locked through out the function call */
+ ret = zram_read_from_zspool(zram, page, index);
+ zram_slot_unlock(zram, index);
} else {
- dst = kmap_atomic(page);
- ret = zcomp_decompress(zstrm, src, size, dst);
- kunmap_atomic(dst);
- zcomp_stream_put(zram->comp);
+ unsigned long blk_idx = zram_get_handle(zram, index);
+
+ /*
+ * The slot should be unlocked before reading from the backing
+ * device.
+ */
+ zram_slot_unlock(zram, index);
+ ret = read_from_bdev(zram, page, blk_idx, parent);
}
- zs_unmap_object(zram->mem_pool, handle);
- zram_slot_unlock(zram, index);
/* Should NEVER happen. Return bio error if it does. */
- if (WARN_ON(ret))
+ if (WARN_ON(ret < 0))
pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
return ret;
}
-static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
- u32 index, int offset, struct bio *bio)
+/*
+ * Use a temporary buffer to decompress the page, as the decompressor
+ * always expects a full page for the output.
+ */
+static int zram_bvec_read_partial(struct zram *zram, struct bio_vec *bvec,
+ u32 index, int offset)
{
+ struct page *page = alloc_page(GFP_NOIO);
int ret;
- struct page *page;
- page = bvec->bv_page;
- if (is_partial_io(bvec)) {
- /* Use a temporary buffer to decompress the page */
- page = alloc_page(GFP_NOIO|__GFP_HIGHMEM);
- if (!page)
- return -ENOMEM;
- }
+ if (!page)
+ return -ENOMEM;
+ ret = zram_read_page(zram, page, index, NULL);
+ if (likely(!ret))
+ memcpy_to_bvec(bvec, page_address(page) + offset);
+ __free_page(page);
+ return ret;
+}
- ret = __zram_bvec_read(zram, page, index, bio, is_partial_io(bvec));
- if (unlikely(ret))
- goto out;
+static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
+ u32 index, int offset, struct bio *bio)
+{
+ if (is_partial_io(bvec))
+ return zram_bvec_read_partial(zram, bvec, index, offset);
+ return zram_read_page(zram, bvec->bv_page, index, bio);
+}
+
+static int write_same_filled_page(struct zram *zram, unsigned long fill,
+ u32 index)
+{
+ zram_slot_lock(zram, index);
+ zram_free_page(zram, index);
+ zram_set_flag(zram, index, ZRAM_SAME);
+ zram_set_handle(zram, index, fill);
+ zram_slot_unlock(zram, index);
+
+ atomic64_inc(&zram->stats.same_pages);
+ atomic64_inc(&zram->stats.pages_stored);
- if (is_partial_io(bvec)) {
- void *src = kmap_atomic(page);
+ return 0;
+}
- memcpy_to_bvec(bvec, src + offset);
- kunmap_atomic(src);
+static int write_incompressible_page(struct zram *zram, struct page *page,
+ u32 index)
+{
+ unsigned long handle;
+ void *src;
+
+ /*
+ * This function is called from preemptible context so we don't need
+ * to do optimistic and fallback to pessimistic handle allocation,
+ * like we do for compressible pages.
+ */
+ handle = zs_malloc(zram->mem_pool, PAGE_SIZE,
+ GFP_NOIO | __GFP_NOWARN |
+ __GFP_HIGHMEM | __GFP_MOVABLE, page_to_nid(page));
+ if (IS_ERR_VALUE(handle))
+ return PTR_ERR((void *)handle);
+
+ if (!zram_can_store_page(zram)) {
+ zs_free(zram->mem_pool, handle);
+ return -ENOMEM;
}
-out:
- if (is_partial_io(bvec))
- __free_page(page);
- return ret;
+ src = kmap_local_page(page);
+ zs_obj_write(zram->mem_pool, handle, src, PAGE_SIZE);
+ kunmap_local(src);
+
+ zram_slot_lock(zram, index);
+ zram_free_page(zram, index);
+ zram_set_flag(zram, index, ZRAM_HUGE);
+ zram_set_handle(zram, index, handle);
+ zram_set_obj_size(zram, index, PAGE_SIZE);
+ zram_slot_unlock(zram, index);
+
+ atomic64_add(PAGE_SIZE, &zram->stats.compr_data_size);
+ atomic64_inc(&zram->stats.huge_pages);
+ atomic64_inc(&zram->stats.huge_pages_since);
+ atomic64_inc(&zram->stats.pages_stored);
+
+ return 0;
}
-static int __zram_bvec_write(struct zram *zram, struct bio_vec *bvec,
- u32 index, struct bio *bio)
+static int zram_write_page(struct zram *zram, struct page *page, u32 index)
{
int ret = 0;
- unsigned long alloced_pages;
- unsigned long handle = -ENOMEM;
- unsigned int comp_len = 0;
- void *src, *dst, *mem;
+ unsigned long handle;
+ unsigned int comp_len;
+ void *mem;
struct zcomp_strm *zstrm;
- struct page *page = bvec->bv_page;
- unsigned long element = 0;
- enum zram_pageflags flags = 0;
-
- mem = kmap_atomic(page);
- if (page_same_filled(mem, &element)) {
- kunmap_atomic(mem);
- /* Free memory associated with this sector now. */
- flags = ZRAM_SAME;
- atomic64_inc(&zram->stats.same_pages);
- goto out;
- }
- kunmap_atomic(mem);
+ unsigned long element;
+ bool same_filled;
-compress_again:
- zstrm = zcomp_stream_get(zram->comp);
- src = kmap_atomic(page);
- ret = zcomp_compress(zstrm, src, &comp_len);
- kunmap_atomic(src);
+ mem = kmap_local_page(page);
+ same_filled = page_same_filled(mem, &element);
+ kunmap_local(mem);
+ if (same_filled)
+ return write_same_filled_page(zram, element, index);
+
+ zstrm = zcomp_stream_get(zram->comps[ZRAM_PRIMARY_COMP]);
+ mem = kmap_local_page(page);
+ ret = zcomp_compress(zram->comps[ZRAM_PRIMARY_COMP], zstrm,
+ mem, &comp_len);
+ kunmap_local(mem);
if (unlikely(ret)) {
- zcomp_stream_put(zram->comp);
+ zcomp_stream_put(zstrm);
pr_err("Compression failed! err=%d\n", ret);
- zs_free(zram->mem_pool, handle);
return ret;
}
- if (comp_len >= huge_class_size)
- comp_len = PAGE_SIZE;
- /*
- * handle allocation has 2 paths:
- * a) fast path is executed with preemption disabled (for
- * per-cpu streams) and has __GFP_DIRECT_RECLAIM bit clear,
- * since we can't sleep;
- * b) slow path enables preemption and attempts to allocate
- * the page with __GFP_DIRECT_RECLAIM bit set. we have to
- * put per-cpu compression stream and, thus, to re-do
- * the compression once handle is allocated.
- *
- * if we have a 'non-null' handle here then we are coming
- * from the slow path and handle has already been allocated.
- */
- if (IS_ERR((void *)handle))
- handle = zs_malloc(zram->mem_pool, comp_len,
- __GFP_KSWAPD_RECLAIM |
- __GFP_NOWARN |
- __GFP_HIGHMEM |
- __GFP_MOVABLE);
- if (IS_ERR((void *)handle)) {
- zcomp_stream_put(zram->comp);
- atomic64_inc(&zram->stats.writestall);
- handle = zs_malloc(zram->mem_pool, comp_len,
- GFP_NOIO | __GFP_HIGHMEM |
- __GFP_MOVABLE);
- if (!IS_ERR((void *)handle))
- goto compress_again;
- return PTR_ERR((void *)handle);
+ if (comp_len >= huge_class_size) {
+ zcomp_stream_put(zstrm);
+ return write_incompressible_page(zram, page, index);
}
- alloced_pages = zs_get_total_pages(zram->mem_pool);
- update_used_max(zram, alloced_pages);
+ handle = zs_malloc(zram->mem_pool, comp_len,
+ GFP_NOIO | __GFP_NOWARN |
+ __GFP_HIGHMEM | __GFP_MOVABLE, page_to_nid(page));
+ if (IS_ERR_VALUE(handle)) {
+ zcomp_stream_put(zstrm);
+ return PTR_ERR((void *)handle);
+ }
- if (zram->limit_pages && alloced_pages > zram->limit_pages) {
- zcomp_stream_put(zram->comp);
+ if (!zram_can_store_page(zram)) {
+ zcomp_stream_put(zstrm);
zs_free(zram->mem_pool, handle);
return -ENOMEM;
}
- dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO);
+ zs_obj_write(zram->mem_pool, handle, zstrm->buffer, comp_len);
+ zcomp_stream_put(zstrm);
- src = zstrm->buffer;
- if (comp_len == PAGE_SIZE)
- src = kmap_atomic(page);
- memcpy(dst, src, comp_len);
- if (comp_len == PAGE_SIZE)
- kunmap_atomic(src);
+ zram_slot_lock(zram, index);
+ zram_free_page(zram, index);
+ zram_set_handle(zram, index, handle);
+ zram_set_obj_size(zram, index, comp_len);
+ zram_slot_unlock(zram, index);
- zcomp_stream_put(zram->comp);
- zs_unmap_object(zram->mem_pool, handle);
+ /* Update stats */
+ atomic64_inc(&zram->stats.pages_stored);
atomic64_add(comp_len, &zram->stats.compr_data_size);
-out:
+
+ return ret;
+}
+
+/*
+ * This is a partial IO. Read the full page before writing the changes.
+ */
+static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec,
+ u32 index, int offset, struct bio *bio)
+{
+ struct page *page = alloc_page(GFP_NOIO);
+ int ret;
+
+ if (!page)
+ return -ENOMEM;
+
+ ret = zram_read_page(zram, page, index, bio);
+ if (!ret) {
+ memcpy_from_bvec(page_address(page) + offset, bvec);
+ ret = zram_write_page(zram, page, index);
+ }
+ __free_page(page);
+ return ret;
+}
+
+static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec,
+ u32 index, int offset, struct bio *bio)
+{
+ if (is_partial_io(bvec))
+ return zram_bvec_write_partial(zram, bvec, index, offset, bio);
+ return zram_write_page(zram, bvec->bv_page, index);
+}
+
+#ifdef CONFIG_ZRAM_MULTI_COMP
+#define RECOMPRESS_IDLE (1 << 0)
+#define RECOMPRESS_HUGE (1 << 1)
+
+static int scan_slots_for_recompress(struct zram *zram, u32 mode, u32 prio_max,
+ struct zram_pp_ctl *ctl)
+{
+ unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
+ unsigned long index;
+
+ for (index = 0; index < nr_pages; index++) {
+ bool ok = true;
+
+ zram_slot_lock(zram, index);
+ if (!zram_allocated(zram, index))
+ goto next;
+
+ if (mode & RECOMPRESS_IDLE &&
+ !zram_test_flag(zram, index, ZRAM_IDLE))
+ goto next;
+
+ if (mode & RECOMPRESS_HUGE &&
+ !zram_test_flag(zram, index, ZRAM_HUGE))
+ goto next;
+
+ if (zram_test_flag(zram, index, ZRAM_WB) ||
+ zram_test_flag(zram, index, ZRAM_SAME) ||
+ zram_test_flag(zram, index, ZRAM_INCOMPRESSIBLE))
+ goto next;
+
+ /* Already compressed with same of higher priority */
+ if (zram_get_priority(zram, index) + 1 >= prio_max)
+ goto next;
+
+ ok = place_pp_slot(zram, ctl, index);
+next:
+ zram_slot_unlock(zram, index);
+ if (!ok)
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * This function will decompress (unless it's ZRAM_HUGE) the page and then
+ * attempt to compress it using provided compression algorithm priority
+ * (which is potentially more effective).
+ *
+ * Corresponding ZRAM slot should be locked.
+ */
+static int recompress_slot(struct zram *zram, u32 index, struct page *page,
+ u64 *num_recomp_pages, u32 threshold, u32 prio,
+ u32 prio_max)
+{
+ struct zcomp_strm *zstrm = NULL;
+ unsigned long handle_old;
+ unsigned long handle_new;
+ unsigned int comp_len_old;
+ unsigned int comp_len_new;
+ unsigned int class_index_old;
+ unsigned int class_index_new;
+ void *src;
+ int ret = 0;
+
+ handle_old = zram_get_handle(zram, index);
+ if (!handle_old)
+ return -EINVAL;
+
+ comp_len_old = zram_get_obj_size(zram, index);
/*
- * Free memory associated with this sector
- * before overwriting unused sectors.
+ * Do not recompress objects that are already "small enough".
*/
- zram_slot_lock(zram, index);
- zram_free_page(zram, index);
+ if (comp_len_old < threshold)
+ return 0;
+
+ ret = zram_read_from_zspool(zram, page, index);
+ if (ret)
+ return ret;
+
+ /*
+ * We touched this entry so mark it as non-IDLE. This makes sure that
+ * we don't preserve IDLE flag and don't incorrectly pick this entry
+ * for different post-processing type (e.g. writeback).
+ */
+ zram_clear_flag(zram, index, ZRAM_IDLE);
+
+ class_index_old = zs_lookup_class_index(zram->mem_pool, comp_len_old);
- if (comp_len == PAGE_SIZE) {
- zram_set_flag(zram, index, ZRAM_HUGE);
- atomic64_inc(&zram->stats.huge_pages);
- atomic64_inc(&zram->stats.huge_pages_since);
+ prio = max(prio, zram_get_priority(zram, index) + 1);
+ /*
+ * Recompression slots scan should not select slots that are
+ * already compressed with a higher priority algorithm, but
+ * just in case
+ */
+ if (prio >= prio_max)
+ return 0;
+
+ /*
+ * Iterate the secondary comp algorithms list (in order of priority)
+ * and try to recompress the page.
+ */
+ for (; prio < prio_max; prio++) {
+ if (!zram->comps[prio])
+ continue;
+
+ zstrm = zcomp_stream_get(zram->comps[prio]);
+ src = kmap_local_page(page);
+ ret = zcomp_compress(zram->comps[prio], zstrm,
+ src, &comp_len_new);
+ kunmap_local(src);
+
+ if (ret) {
+ zcomp_stream_put(zstrm);
+ zstrm = NULL;
+ break;
+ }
+
+ class_index_new = zs_lookup_class_index(zram->mem_pool,
+ comp_len_new);
+
+ /* Continue until we make progress */
+ if (class_index_new >= class_index_old ||
+ (threshold && comp_len_new >= threshold)) {
+ zcomp_stream_put(zstrm);
+ zstrm = NULL;
+ continue;
+ }
+
+ /* Recompression was successful so break out */
+ break;
}
- if (flags) {
- zram_set_flag(zram, index, flags);
- zram_set_element(zram, index, element);
- } else {
- zram_set_handle(zram, index, handle);
- zram_set_obj_size(zram, index, comp_len);
+ /*
+ * Decrement the limit (if set) on pages we can recompress, even
+ * when current recompression was unsuccessful or did not compress
+ * the page below the threshold, because we still spent resources
+ * on it.
+ */
+ if (*num_recomp_pages)
+ *num_recomp_pages -= 1;
+
+ /* Compression error */
+ if (ret)
+ return ret;
+
+ if (!zstrm) {
+ /*
+ * Secondary algorithms failed to re-compress the page
+ * in a way that would save memory.
+ *
+ * Mark the object incompressible if the max-priority
+ * algorithm couldn't re-compress it.
+ */
+ if (prio < zram->num_active_comps)
+ return 0;
+ zram_set_flag(zram, index, ZRAM_INCOMPRESSIBLE);
+ return 0;
}
- zram_slot_unlock(zram, index);
- /* Update stats */
+ /*
+ * We are holding per-CPU stream mutex and entry lock so better
+ * avoid direct reclaim. Allocation error is not fatal since
+ * we still have the old object in the mem_pool.
+ *
+ * XXX: technically, the node we really want here is the node that holds
+ * the original compressed data. But that would require us to modify
+ * zsmalloc API to return this information. For now, we will make do with
+ * the node of the page allocated for recompression.
+ */
+ handle_new = zs_malloc(zram->mem_pool, comp_len_new,
+ GFP_NOIO | __GFP_NOWARN |
+ __GFP_HIGHMEM | __GFP_MOVABLE, page_to_nid(page));
+ if (IS_ERR_VALUE(handle_new)) {
+ zcomp_stream_put(zstrm);
+ return PTR_ERR((void *)handle_new);
+ }
+
+ zs_obj_write(zram->mem_pool, handle_new, zstrm->buffer, comp_len_new);
+ zcomp_stream_put(zstrm);
+
+ zram_free_page(zram, index);
+ zram_set_handle(zram, index, handle_new);
+ zram_set_obj_size(zram, index, comp_len_new);
+ zram_set_priority(zram, index, prio);
+
+ atomic64_add(comp_len_new, &zram->stats.compr_data_size);
atomic64_inc(&zram->stats.pages_stored);
- return ret;
+
+ return 0;
}
-static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec,
- u32 index, int offset, struct bio *bio)
+static ssize_t recompress_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
- int ret;
+ struct zram *zram = dev_to_zram(dev);
+ char *args, *param, *val, *algo = NULL;
+ u64 num_recomp_pages = ULLONG_MAX;
+ struct zram_pp_ctl *ctl = NULL;
+ struct zram_pp_slot *pps;
+ u32 mode = 0, threshold = 0;
+ u32 prio, prio_max;
struct page *page = NULL;
- struct bio_vec vec;
+ ssize_t ret;
- vec = *bvec;
- if (is_partial_io(bvec)) {
- void *dst;
- /*
- * This is a partial IO. We need to read the full page
- * before to write the changes.
- */
- page = alloc_page(GFP_NOIO|__GFP_HIGHMEM);
- if (!page)
- return -ENOMEM;
+ prio = ZRAM_SECONDARY_COMP;
+ prio_max = zram->num_active_comps;
- ret = __zram_bvec_read(zram, page, index, bio, true);
- if (ret)
- goto out;
+ args = skip_spaces(buf);
+ while (*args) {
+ args = next_arg(args, &param, &val);
- dst = kmap_atomic(page);
- memcpy_from_bvec(dst + offset, bvec);
- kunmap_atomic(dst);
+ if (!val || !*val)
+ return -EINVAL;
+
+ if (!strcmp(param, "type")) {
+ if (!strcmp(val, "idle"))
+ mode = RECOMPRESS_IDLE;
+ if (!strcmp(val, "huge"))
+ mode = RECOMPRESS_HUGE;
+ if (!strcmp(val, "huge_idle"))
+ mode = RECOMPRESS_IDLE | RECOMPRESS_HUGE;
+ continue;
+ }
- vec.bv_page = page;
- vec.bv_len = PAGE_SIZE;
- vec.bv_offset = 0;
+ if (!strcmp(param, "max_pages")) {
+ /*
+ * Limit the number of entries (pages) we attempt to
+ * recompress.
+ */
+ ret = kstrtoull(val, 10, &num_recomp_pages);
+ if (ret)
+ return ret;
+ continue;
+ }
+
+ if (!strcmp(param, "threshold")) {
+ /*
+ * We will re-compress only idle objects equal or
+ * greater in size than watermark.
+ */
+ ret = kstrtouint(val, 10, &threshold);
+ if (ret)
+ return ret;
+ continue;
+ }
+
+ if (!strcmp(param, "algo")) {
+ algo = val;
+ continue;
+ }
+
+ if (!strcmp(param, "priority")) {
+ ret = kstrtouint(val, 10, &prio);
+ if (ret)
+ return ret;
+
+ if (prio == ZRAM_PRIMARY_COMP)
+ prio = ZRAM_SECONDARY_COMP;
+
+ prio_max = prio + 1;
+ continue;
+ }
}
- ret = __zram_bvec_write(zram, &vec, index, bio);
-out:
- if (is_partial_io(bvec))
+ if (threshold >= huge_class_size)
+ return -EINVAL;
+
+ down_read(&zram->init_lock);
+ if (!init_done(zram)) {
+ ret = -EINVAL;
+ goto release_init_lock;
+ }
+
+ /* Do not permit concurrent post-processing actions. */
+ if (atomic_xchg(&zram->pp_in_progress, 1)) {
+ up_read(&zram->init_lock);
+ return -EAGAIN;
+ }
+
+ if (algo) {
+ bool found = false;
+
+ for (; prio < ZRAM_MAX_COMPS; prio++) {
+ if (!zram->comp_algs[prio])
+ continue;
+
+ if (!strcmp(zram->comp_algs[prio], algo)) {
+ prio_max = prio + 1;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret = -EINVAL;
+ goto release_init_lock;
+ }
+ }
+
+ prio_max = min(prio_max, (u32)zram->num_active_comps);
+ if (prio >= prio_max) {
+ ret = -EINVAL;
+ goto release_init_lock;
+ }
+
+ page = alloc_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto release_init_lock;
+ }
+
+ ctl = init_pp_ctl();
+ if (!ctl) {
+ ret = -ENOMEM;
+ goto release_init_lock;
+ }
+
+ scan_slots_for_recompress(zram, mode, prio_max, ctl);
+
+ ret = len;
+ while ((pps = select_pp_slot(ctl))) {
+ int err = 0;
+
+ if (!num_recomp_pages)
+ break;
+
+ zram_slot_lock(zram, pps->index);
+ if (!zram_test_flag(zram, pps->index, ZRAM_PP_SLOT))
+ goto next;
+
+ err = recompress_slot(zram, pps->index, page,
+ &num_recomp_pages, threshold,
+ prio, prio_max);
+next:
+ zram_slot_unlock(zram, pps->index);
+ release_pp_slot(zram, pps);
+
+ if (err) {
+ ret = err;
+ break;
+ }
+
+ cond_resched();
+ }
+
+release_init_lock:
+ if (page)
__free_page(page);
+ release_pp_ctl(zram, ctl);
+ atomic_set(&zram->pp_in_progress, 0);
+ up_read(&zram->init_lock);
return ret;
}
+#endif
-/*
- * zram_bio_discard - handler on discard request
- * @index: physical block index in PAGE_SIZE units
- * @offset: byte offset within physical block
- */
-static void zram_bio_discard(struct zram *zram, u32 index,
- int offset, struct bio *bio)
+static void zram_bio_discard(struct zram *zram, struct bio *bio)
{
size_t n = bio->bi_iter.bi_size;
+ u32 index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
+ u32 offset = (bio->bi_iter.bi_sector & (SECTORS_PER_PAGE - 1)) <<
+ SECTOR_SHIFT;
/*
* zram manages data in physical block size units. Because logical block
@@ -1538,83 +2587,67 @@ static void zram_bio_discard(struct zram *zram, u32 index,
index++;
n -= PAGE_SIZE;
}
+
+ bio_endio(bio);
}
-/*
- * Returns errno if it has some problem. Otherwise return 0 or 1.
- * Returns 0 if IO request was done synchronously
- * Returns 1 if IO request was successfully submitted.
- */
-static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
- int offset, enum req_op op, struct bio *bio)
+static void zram_bio_read(struct zram *zram, struct bio *bio)
{
- int ret;
+ unsigned long start_time = bio_start_io_acct(bio);
+ struct bvec_iter iter = bio->bi_iter;
- if (!op_is_write(op)) {
- atomic64_inc(&zram->stats.num_reads);
- ret = zram_bvec_read(zram, bvec, index, offset, bio);
- flush_dcache_page(bvec->bv_page);
- } else {
- atomic64_inc(&zram->stats.num_writes);
- ret = zram_bvec_write(zram, bvec, index, offset, bio);
- }
+ do {
+ u32 index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
+ u32 offset = (iter.bi_sector & (SECTORS_PER_PAGE - 1)) <<
+ SECTOR_SHIFT;
+ struct bio_vec bv = bio_iter_iovec(bio, iter);
- zram_slot_lock(zram, index);
- zram_accessed(zram, index);
- zram_slot_unlock(zram, index);
+ bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset);
- if (unlikely(ret < 0)) {
- if (!op_is_write(op))
+ if (zram_bvec_read(zram, &bv, index, offset, bio) < 0) {
atomic64_inc(&zram->stats.failed_reads);
- else
- atomic64_inc(&zram->stats.failed_writes);
- }
+ bio->bi_status = BLK_STS_IOERR;
+ break;
+ }
+ flush_dcache_page(bv.bv_page);
- return ret;
+ zram_slot_lock(zram, index);
+ zram_accessed(zram, index);
+ zram_slot_unlock(zram, index);
+
+ bio_advance_iter_single(bio, &iter, bv.bv_len);
+ } while (iter.bi_size);
+
+ bio_end_io_acct(bio, start_time);
+ bio_endio(bio);
}
-static void __zram_make_request(struct zram *zram, struct bio *bio)
+static void zram_bio_write(struct zram *zram, struct bio *bio)
{
- int offset;
- u32 index;
- struct bio_vec bvec;
- struct bvec_iter iter;
- unsigned long start_time;
+ unsigned long start_time = bio_start_io_acct(bio);
+ struct bvec_iter iter = bio->bi_iter;
- index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
- offset = (bio->bi_iter.bi_sector &
- (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT;
+ do {
+ u32 index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
+ u32 offset = (iter.bi_sector & (SECTORS_PER_PAGE - 1)) <<
+ SECTOR_SHIFT;
+ struct bio_vec bv = bio_iter_iovec(bio, iter);
- switch (bio_op(bio)) {
- case REQ_OP_DISCARD:
- case REQ_OP_WRITE_ZEROES:
- zram_bio_discard(zram, index, offset, bio);
- bio_endio(bio);
- return;
- default:
- break;
- }
+ bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset);
- start_time = bio_start_io_acct(bio);
- bio_for_each_segment(bvec, bio, iter) {
- struct bio_vec bv = bvec;
- unsigned int unwritten = bvec.bv_len;
+ if (zram_bvec_write(zram, &bv, index, offset, bio) < 0) {
+ atomic64_inc(&zram->stats.failed_writes);
+ bio->bi_status = BLK_STS_IOERR;
+ break;
+ }
- do {
- bv.bv_len = min_t(unsigned int, PAGE_SIZE - offset,
- unwritten);
- if (zram_bvec_rw(zram, &bv, index, offset,
- bio_op(bio), bio) < 0) {
- bio->bi_status = BLK_STS_IOERR;
- break;
- }
+ zram_slot_lock(zram, index);
+ zram_accessed(zram, index);
+ zram_slot_unlock(zram, index);
- bv.bv_offset += bv.bv_len;
- unwritten -= bv.bv_len;
+ bio_advance_iter_single(bio, &iter, bv.bv_len);
+ } while (iter.bi_size);
- update_position(&index, &offset, &bv);
- } while (unwritten);
- }
bio_end_io_acct(bio, start_time);
bio_endio(bio);
}
@@ -1626,14 +2659,21 @@ static void zram_submit_bio(struct bio *bio)
{
struct zram *zram = bio->bi_bdev->bd_disk->private_data;
- if (!valid_io_request(zram, bio->bi_iter.bi_sector,
- bio->bi_iter.bi_size)) {
- atomic64_inc(&zram->stats.invalid_io);
- bio_io_error(bio);
- return;
+ switch (bio_op(bio)) {
+ case REQ_OP_READ:
+ zram_bio_read(zram, bio);
+ break;
+ case REQ_OP_WRITE:
+ zram_bio_write(zram, bio);
+ break;
+ case REQ_OP_DISCARD:
+ case REQ_OP_WRITE_ZEROES:
+ zram_bio_discard(zram, bio);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ bio_endio(bio);
}
-
- __zram_make_request(zram, bio);
}
static void zram_slot_free_notify(struct block_device *bdev,
@@ -1653,88 +2693,57 @@ static void zram_slot_free_notify(struct block_device *bdev,
zram_slot_unlock(zram, index);
}
-static int zram_rw_page(struct block_device *bdev, sector_t sector,
- struct page *page, enum req_op op)
+static void zram_comp_params_reset(struct zram *zram)
{
- int offset, ret;
- u32 index;
- struct zram *zram;
- struct bio_vec bv;
- unsigned long start_time;
-
- if (PageTransHuge(page))
- return -ENOTSUPP;
- zram = bdev->bd_disk->private_data;
+ u32 prio;
- if (!valid_io_request(zram, sector, PAGE_SIZE)) {
- atomic64_inc(&zram->stats.invalid_io);
- ret = -EINVAL;
- goto out;
+ for (prio = ZRAM_PRIMARY_COMP; prio < ZRAM_MAX_COMPS; prio++) {
+ comp_params_reset(zram, prio);
}
+}
- index = sector >> SECTORS_PER_PAGE_SHIFT;
- offset = (sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT;
+static void zram_destroy_comps(struct zram *zram)
+{
+ u32 prio;
- bv.bv_page = page;
- bv.bv_len = PAGE_SIZE;
- bv.bv_offset = 0;
+ for (prio = ZRAM_PRIMARY_COMP; prio < ZRAM_MAX_COMPS; prio++) {
+ struct zcomp *comp = zram->comps[prio];
- start_time = bdev_start_io_acct(bdev->bd_disk->part0,
- SECTORS_PER_PAGE, op, jiffies);
- ret = zram_bvec_rw(zram, &bv, index, offset, op, NULL);
- bdev_end_io_acct(bdev->bd_disk->part0, op, start_time);
-out:
- /*
- * If I/O fails, just return error(ie, non-zero) without
- * calling page_endio.
- * It causes resubmit the I/O with bio request by upper functions
- * of rw_page(e.g., swap_readpage, __swap_writepage) and
- * bio->bi_end_io does things to handle the error
- * (e.g., SetPageError, set_page_dirty and extra works).
- */
- if (unlikely(ret < 0))
- return ret;
+ zram->comps[prio] = NULL;
+ if (!comp)
+ continue;
+ zcomp_destroy(comp);
+ zram->num_active_comps--;
+ }
- switch (ret) {
- case 0:
- page_endio(page, op_is_write(op), 0);
- break;
- case 1:
- ret = 0;
- break;
- default:
- WARN_ON(1);
+ for (prio = ZRAM_PRIMARY_COMP; prio < ZRAM_MAX_COMPS; prio++) {
+ /* Do not free statically defined compression algorithms */
+ if (zram->comp_algs[prio] != default_compressor)
+ kfree(zram->comp_algs[prio]);
+ zram->comp_algs[prio] = NULL;
}
- return ret;
+
+ zram_comp_params_reset(zram);
}
static void zram_reset_device(struct zram *zram)
{
- struct zcomp *comp;
- u64 disksize;
-
down_write(&zram->init_lock);
zram->limit_pages = 0;
- if (!init_done(zram)) {
- up_write(&zram->init_lock);
- return;
- }
-
- comp = zram->comp;
- disksize = zram->disksize;
- zram->disksize = 0;
-
set_capacity_and_notify(zram->disk, 0);
part_stat_set_all(zram->disk->part0, 0);
/* I/O operation under all of CPU are done so let's free */
- zram_meta_free(zram, disksize);
+ zram_meta_free(zram, zram->disksize);
+ zram->disksize = 0;
+ zram_destroy_comps(zram);
memset(&zram->stats, 0, sizeof(zram->stats));
- zcomp_destroy(comp);
+ atomic_set(&zram->pp_in_progress, 0);
reset_bdev(zram);
+ comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
up_write(&zram->init_lock);
}
@@ -1745,6 +2754,7 @@ static ssize_t disksize_store(struct device *dev,
struct zcomp *comp;
struct zram *zram = dev_to_zram(dev);
int err;
+ u32 prio;
disksize = memparse(buf, NULL);
if (!disksize)
@@ -1763,22 +2773,30 @@ static ssize_t disksize_store(struct device *dev,
goto out_unlock;
}
- comp = zcomp_create(zram->compressor);
- if (IS_ERR(comp)) {
- pr_err("Cannot initialise %s compressing backend\n",
- zram->compressor);
- err = PTR_ERR(comp);
- goto out_free_meta;
- }
+ for (prio = ZRAM_PRIMARY_COMP; prio < ZRAM_MAX_COMPS; prio++) {
+ if (!zram->comp_algs[prio])
+ continue;
+
+ comp = zcomp_create(zram->comp_algs[prio],
+ &zram->params[prio]);
+ if (IS_ERR(comp)) {
+ pr_err("Cannot initialise %s compressing backend\n",
+ zram->comp_algs[prio]);
+ err = PTR_ERR(comp);
+ goto out_free_comps;
+ }
- zram->comp = comp;
+ zram->comps[prio] = comp;
+ zram->num_active_comps++;
+ }
zram->disksize = disksize;
set_capacity_and_notify(zram->disk, zram->disksize >> SECTOR_SHIFT);
up_write(&zram->init_lock);
return len;
-out_free_meta:
+out_free_comps:
+ zram_destroy_comps(zram);
zram_meta_free(zram, disksize);
out_unlock:
up_write(&zram->init_lock);
@@ -1825,38 +2843,25 @@ static ssize_t reset_store(struct device *dev,
return len;
}
-static int zram_open(struct block_device *bdev, fmode_t mode)
+static int zram_open(struct gendisk *disk, blk_mode_t mode)
{
- int ret = 0;
- struct zram *zram;
+ struct zram *zram = disk->private_data;
- WARN_ON(!mutex_is_locked(&bdev->bd_disk->open_mutex));
+ WARN_ON(!mutex_is_locked(&disk->open_mutex));
- zram = bdev->bd_disk->private_data;
/* zram was claimed to reset so open request fails */
if (zram->claim)
- ret = -EBUSY;
-
- return ret;
+ return -EBUSY;
+ return 0;
}
static const struct block_device_operations zram_devops = {
.open = zram_open,
.submit_bio = zram_submit_bio,
.swap_slot_free_notify = zram_slot_free_notify,
- .rw_page = zram_rw_page,
.owner = THIS_MODULE
};
-#ifdef CONFIG_ZRAM_WRITEBACK
-static const struct block_device_operations zram_wb_devops = {
- .open = zram_open,
- .submit_bio = zram_submit_bio,
- .swap_slot_free_notify = zram_slot_free_notify,
- .owner = THIS_MODULE
-};
-#endif
-
static DEVICE_ATTR_WO(compact);
static DEVICE_ATTR_RW(disksize);
static DEVICE_ATTR_RO(initstate);
@@ -1864,14 +2869,19 @@ static DEVICE_ATTR_WO(reset);
static DEVICE_ATTR_WO(mem_limit);
static DEVICE_ATTR_WO(mem_used_max);
static DEVICE_ATTR_WO(idle);
-static DEVICE_ATTR_RW(max_comp_streams);
static DEVICE_ATTR_RW(comp_algorithm);
#ifdef CONFIG_ZRAM_WRITEBACK
static DEVICE_ATTR_RW(backing_dev);
static DEVICE_ATTR_WO(writeback);
static DEVICE_ATTR_RW(writeback_limit);
static DEVICE_ATTR_RW(writeback_limit_enable);
+static DEVICE_ATTR_RW(writeback_batch_size);
+#endif
+#ifdef CONFIG_ZRAM_MULTI_COMP
+static DEVICE_ATTR_RW(recomp_algorithm);
+static DEVICE_ATTR_WO(recompress);
#endif
+static DEVICE_ATTR_WO(algorithm_params);
static struct attribute *zram_disk_attrs[] = {
&dev_attr_disksize.attr,
@@ -1881,13 +2891,13 @@ static struct attribute *zram_disk_attrs[] = {
&dev_attr_mem_limit.attr,
&dev_attr_mem_used_max.attr,
&dev_attr_idle.attr,
- &dev_attr_max_comp_streams.attr,
&dev_attr_comp_algorithm.attr,
#ifdef CONFIG_ZRAM_WRITEBACK
&dev_attr_backing_dev.attr,
&dev_attr_writeback.attr,
&dev_attr_writeback_limit.attr,
&dev_attr_writeback_limit_enable.attr,
+ &dev_attr_writeback_batch_size.attr,
#endif
&dev_attr_io_stat.attr,
&dev_attr_mm_stat.attr,
@@ -1895,6 +2905,11 @@ static struct attribute *zram_disk_attrs[] = {
&dev_attr_bd_stat.attr,
#endif
&dev_attr_debug_stat.attr,
+#ifdef CONFIG_ZRAM_MULTI_COMP
+ &dev_attr_recomp_algorithm.attr,
+ &dev_attr_recompress.attr,
+#endif
+ &dev_attr_algorithm_params.attr,
NULL,
};
@@ -1906,6 +2921,30 @@ ATTRIBUTE_GROUPS(zram_disk);
*/
static int zram_add(void)
{
+ struct queue_limits lim = {
+ .logical_block_size = ZRAM_LOGICAL_BLOCK_SIZE,
+ /*
+ * To ensure that we always get PAGE_SIZE aligned and
+ * n*PAGE_SIZED sized I/O requests.
+ */
+ .physical_block_size = PAGE_SIZE,
+ .io_min = PAGE_SIZE,
+ .io_opt = PAGE_SIZE,
+ .max_hw_discard_sectors = UINT_MAX,
+ /*
+ * zram_bio_discard() will clear all logical blocks if logical
+ * block size is identical with physical block size(PAGE_SIZE).
+ * But if it is different, we will skip discarding some parts of
+ * logical blocks in the part of the request range which isn't
+ * aligned to physical block size. So we can't ensure that all
+ * discarded logical blocks are zeroed.
+ */
+#if ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE
+ .max_write_zeroes_sectors = UINT_MAX,
+#endif
+ .features = BLK_FEAT_STABLE_WRITES |
+ BLK_FEAT_SYNCHRONOUS,
+ };
struct zram *zram;
int ret, device_id;
@@ -1920,15 +2959,15 @@ static int zram_add(void)
init_rwsem(&zram->init_lock);
#ifdef CONFIG_ZRAM_WRITEBACK
- spin_lock_init(&zram->wb_limit_lock);
+ zram->wb_batch_size = 32;
#endif
/* gendisk structure */
- zram->disk = blk_alloc_disk(NUMA_NO_NODE);
- if (!zram->disk) {
+ zram->disk = blk_alloc_disk(&lim, NUMA_NO_NODE);
+ if (IS_ERR(zram->disk)) {
pr_err("Error allocating disk structure for device %d\n",
device_id);
- ret = -ENOMEM;
+ ret = PTR_ERR(zram->disk);
goto out_free_idr;
}
@@ -1939,43 +2978,16 @@ static int zram_add(void)
zram->disk->fops = &zram_devops;
zram->disk->private_data = zram;
snprintf(zram->disk->disk_name, 16, "zram%d", device_id);
+ atomic_set(&zram->pp_in_progress, 0);
+ zram_comp_params_reset(zram);
+ comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
- /* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
+ /* Actual capacity set using sysfs (/sys/block/zram<id>/disksize */
set_capacity(zram->disk, 0);
- /* zram devices sort of resembles non-rotational disks */
- blk_queue_flag_set(QUEUE_FLAG_NONROT, zram->disk->queue);
- blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue);
-
- /*
- * To ensure that we always get PAGE_SIZE aligned
- * and n*PAGE_SIZED sized I/O requests.
- */
- blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE);
- blk_queue_logical_block_size(zram->disk->queue,
- ZRAM_LOGICAL_BLOCK_SIZE);
- blk_queue_io_min(zram->disk->queue, PAGE_SIZE);
- blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
- zram->disk->queue->limits.discard_granularity = PAGE_SIZE;
- blk_queue_max_discard_sectors(zram->disk->queue, UINT_MAX);
-
- /*
- * zram_bio_discard() will clear all logical blocks if logical block
- * size is identical with physical block size(PAGE_SIZE). But if it is
- * different, we will skip discarding some parts of logical blocks in
- * the part of the request range which isn't aligned to physical block
- * size. So we can't ensure that all discarded logical blocks are
- * zeroed.
- */
- if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE)
- blk_queue_max_write_zeroes_sectors(zram->disk->queue, UINT_MAX);
-
- blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, zram->disk->queue);
ret = device_add_disk(NULL, zram->disk, zram_disk_groups);
if (ret)
goto out_cleanup_disk;
- strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor));
-
zram_debugfs_register(zram);
pr_info("Added device: %s\n", zram->disk->disk_name);
return device_id;
@@ -2045,8 +3057,8 @@ static int zram_remove(struct zram *zram)
* creates a new un-initialized zram device and returns back this device's
* device_id (or an error code if it fails to create a new device).
*/
-static ssize_t hot_add_show(struct class *class,
- struct class_attribute *attr,
+static ssize_t hot_add_show(const struct class *class,
+ const struct class_attribute *attr,
char *buf)
{
int ret;
@@ -2057,13 +3069,14 @@ static ssize_t hot_add_show(struct class *class,
if (ret < 0)
return ret;
- return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", ret);
}
+/* This attribute must be set to 0400, so CLASS_ATTR_RO() can not be used */
static struct class_attribute class_attr_hot_add =
__ATTR(hot_add, 0400, hot_add_show, NULL);
-static ssize_t hot_remove_store(struct class *class,
- struct class_attribute *attr,
+static ssize_t hot_remove_store(const struct class *class,
+ const struct class_attribute *attr,
const char *buf,
size_t count)
{
@@ -2102,7 +3115,6 @@ ATTRIBUTE_GROUPS(zram_control_class);
static struct class zram_control_class = {
.name = "zram-control",
- .owner = THIS_MODULE,
.class_groups = zram_control_class_groups,
};
@@ -2124,8 +3136,11 @@ static void destroy_devices(void)
static int __init zram_init(void)
{
+ struct zram_table_entry zram_te;
int ret;
+ BUILD_BUG_ON(__NR_ZRAM_PAGEFLAGS > sizeof(zram_te.flags) * 8);
+
ret = cpuhp_setup_state_multi(CPUHP_ZCOMP_PREPARE, "block/zram:prepare",
zcomp_cpu_up_prepare, zcomp_cpu_dead);
if (ret < 0)
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index 80c3b43b4828..c6d94501376c 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -17,7 +17,6 @@
#include <linux/rwsem.h>
#include <linux/zsmalloc.h>
-#include <linux/crypto.h>
#include "zcomp.h"
@@ -28,60 +27,61 @@
#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \
(1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT))
-
/*
- * The lower ZRAM_FLAG_SHIFT bits of table.flags is for
- * object size (excluding header), the higher bits is for
- * zram_pageflags.
+ * ZRAM is mainly used for memory efficiency so we want to keep memory
+ * footprint small and thus squeeze size and zram pageflags into a flags
+ * member. The lower ZRAM_FLAG_SHIFT bits is for object size (excluding
+ * header), which cannot be larger than PAGE_SIZE (requiring PAGE_SHIFT
+ * bits), the higher bits are for zram_pageflags.
*
- * zram is mainly used for memory efficiency so we want to keep memory
- * footprint small so we can squeeze size and flags into a field.
- * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header),
- * the higher bits is for zram_pageflags.
+ * We use BUILD_BUG_ON() to make sure that zram pageflags don't overflow.
*/
-#define ZRAM_FLAG_SHIFT 24
+#define ZRAM_FLAG_SHIFT (PAGE_SHIFT + 1)
+
+/* Only 2 bits are allowed for comp priority index */
+#define ZRAM_COMP_PRIORITY_MASK 0x3
/* Flags for zram pages (table[page_no].flags) */
enum zram_pageflags {
- /* zram slot is locked */
- ZRAM_LOCK = ZRAM_FLAG_SHIFT,
- ZRAM_SAME, /* Page consists the same element */
+ ZRAM_SAME = ZRAM_FLAG_SHIFT, /* Page consists the same element */
+ ZRAM_ENTRY_LOCK, /* entry access lock bit */
ZRAM_WB, /* page is stored on backing_device */
- ZRAM_UNDER_WB, /* page is under writeback */
+ ZRAM_PP_SLOT, /* Selected for post-processing */
ZRAM_HUGE, /* Incompressible page */
ZRAM_IDLE, /* not accessed page since last idle marking */
+ ZRAM_INCOMPRESSIBLE, /* none of the algorithms could compress it */
+
+ ZRAM_COMP_PRIORITY_BIT1, /* First bit of comp priority index */
+ ZRAM_COMP_PRIORITY_BIT2, /* Second bit of comp priority index */
__NR_ZRAM_PAGEFLAGS,
};
-/*-- Data structures */
-
-/* Allocated for each disk page */
+/*
+ * Allocated for each disk page. We use bit-lock (ZRAM_ENTRY_LOCK bit
+ * of flags) to save memory. There can be plenty of entries and standard
+ * locking primitives (e.g. mutex) will significantly increase sizeof()
+ * of each entry and hence of the meta table.
+ */
struct zram_table_entry {
- union {
- unsigned long handle;
- unsigned long element;
- };
+ unsigned long handle;
unsigned long flags;
-#ifdef CONFIG_ZRAM_MEMORY_TRACKING
+#ifdef CONFIG_ZRAM_TRACK_ENTRY_ACTIME
ktime_t ac_time;
#endif
+ struct lockdep_map dep_map;
};
struct zram_stats {
atomic64_t compr_data_size; /* compressed size of pages stored */
- atomic64_t num_reads; /* failed + successful */
- atomic64_t num_writes; /* --do-- */
atomic64_t failed_reads; /* can happen when memory is too low */
atomic64_t failed_writes; /* can happen when memory is too low */
- atomic64_t invalid_io; /* non-page-aligned I/O requests */
atomic64_t notify_free; /* no. of swap slot free notifications */
atomic64_t same_pages; /* no. of same element filled pages */
atomic64_t huge_pages; /* no. of huge pages */
atomic64_t huge_pages_since; /* no. of huge pages since zram set up */
atomic64_t pages_stored; /* no. of pages currently stored */
atomic_long_t max_used_pages; /* no. of maximum pages stored */
- atomic64_t writestall; /* no. of write slow paths */
atomic64_t miss_free; /* no. of missed free */
#ifdef CONFIG_ZRAM_WRITEBACK
atomic64_t bd_count; /* no. of pages in backing device */
@@ -90,10 +90,21 @@ struct zram_stats {
#endif
};
+#ifdef CONFIG_ZRAM_MULTI_COMP
+#define ZRAM_PRIMARY_COMP 0U
+#define ZRAM_SECONDARY_COMP 1U
+#define ZRAM_MAX_COMPS 4U
+#else
+#define ZRAM_PRIMARY_COMP 0U
+#define ZRAM_SECONDARY_COMP 0U
+#define ZRAM_MAX_COMPS 1U
+#endif
+
struct zram {
struct zram_table_entry *table;
struct zs_pool *mem_pool;
- struct zcomp *comp;
+ struct zcomp *comps[ZRAM_MAX_COMPS];
+ struct zcomp_params params[ZRAM_MAX_COMPS];
struct gendisk *disk;
/* Prevent concurrent execution of device init */
struct rw_semaphore init_lock;
@@ -108,15 +119,16 @@ struct zram {
* we can store in a disk.
*/
u64 disksize; /* bytes */
- char compressor[CRYPTO_MAX_ALG_NAME];
+ const char *comp_algs[ZRAM_MAX_COMPS];
+ s8 num_active_comps;
/*
* zram is claimed so open request will be failed
*/
bool claim; /* Protected by disk->open_mutex */
#ifdef CONFIG_ZRAM_WRITEBACK
struct file *backing_dev;
- spinlock_t wb_limit_lock;
bool wb_limit_enable;
+ u32 wb_batch_size;
u64 bd_wb_limit;
struct block_device *bdev;
unsigned long *bitmap;
@@ -125,5 +137,6 @@ struct zram {
#ifdef CONFIG_ZRAM_MEMORY_TRACKING
struct dentry *debugfs_dir;
#endif
+ atomic_t pp_in_progress;
};
#endif
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index e30707405455..c5d45cf91f88 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -45,6 +45,17 @@ config BT_HCIBTUSB_AUTOSUSPEND
This can be overridden by passing btusb.enable_autosuspend=[y|n]
on the kernel commandline.
+config BT_HCIBTUSB_POLL_SYNC
+ bool "Enable USB poll_sync for Bluetooth USB devices by default"
+ depends on BT_HCIBTUSB
+ default y
+ help
+ poll_sync synchronizes the USB data and event endpoints by
+ prioritizing the later.
+
+ Say Y here to enable USB poll_sync for Bluetooth USB devices by
+ default.
+
config BT_HCIBTUSB_BCM
bool "Broadcom protocol support"
depends on BT_HCIBTUSB
@@ -94,6 +105,7 @@ config BT_HCIUART
tristate "HCI UART driver"
depends on SERIAL_DEV_BUS || !SERIAL_DEV_BUS
depends on NVMEM || !NVMEM
+ depends on POWER_SEQUENCING || !POWER_SEQUENCING
depends on TTY
help
Bluetooth HCI UART driver.
@@ -176,6 +188,7 @@ config BT_HCIUART_3WIRE
bool "Three-wire UART (H5) protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
+ select CRC_CCITT
help
The HCI Three-wire UART Transport Layer makes it possible to
user the Bluetooth HCI over a serial port interface. The HCI
@@ -262,6 +275,18 @@ config BT_HCIUART_MRVL
Say Y here to compile support for HCI MRVL protocol.
+config BT_HCIUART_AML
+ bool "Amlogic protocol support"
+ depends on BT_HCIUART
+ depends on BT_HCIUART_SERDEV
+ select BT_HCIUART_H4
+ select FW_LOADER
+ help
+ The Amlogic protocol support enables Bluetooth HCI over serial
+ port interface for Amlogic Bluetooth controllers.
+
+ Say Y here to compile support for HCI AML protocol.
+
config BT_HCIBCM203X
tristate "HCI BCM203x USB driver"
depends on USB
@@ -274,9 +299,23 @@ config BT_HCIBCM203X
Say Y here to compile support for HCI BCM203x devices into the
kernel or say M to compile it as module (bcm203x).
+
+config BT_HCIBCM4377
+ tristate "HCI BCM4377/4378/4387/4388 PCIe driver"
+ depends on PCI
+ select FW_LOADER
+ help
+ Support for Broadcom BCM4377/4378/4387/4388 Bluetooth chipsets
+ attached via PCIe. These are usually found in Apple machines.
+
+ Say Y here to compile support for HCI BCM4377 family devices into the
+ kernel or say M to compile it as module (hci_bcm4377).
+
config BT_HCIBPA10X
tristate "HCI BPA10x USB driver"
+ depends on BT_HCIUART
depends on USB
+ select BT_HCIUART_H4
help
Bluetooth HCI BPA10x USB driver.
This driver provides support for the Digianswer BPA 100/105 Bluetooth
@@ -300,7 +339,7 @@ config BT_HCIBFUSB
config BT_HCIDTL1
tristate "HCI DTL1 (PC Card) driver"
- depends on PCMCIA
+ depends on PCMCIA && HAS_IOPORT
help
Bluetooth HCI DTL1 (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
@@ -313,7 +352,7 @@ config BT_HCIDTL1
config BT_HCIBT3C
tristate "HCI BT3C (PC Card) driver"
- depends on PCMCIA
+ depends on PCMCIA && HAS_IOPORT
select FW_LOADER
help
Bluetooth HCI BT3C (PC Card) driver.
@@ -327,7 +366,7 @@ config BT_HCIBT3C
config BT_HCIBLUECARD
tristate "HCI BlueCard (PC Card) driver"
- depends on PCMCIA
+ depends on PCMCIA && HAS_IOPORT
help
Bluetooth HCI BlueCard (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
@@ -340,6 +379,7 @@ config BT_HCIBLUECARD
config BT_HCIVHCI
tristate "HCI VHCI (Virtual HCI device) driver"
+ select WANT_DEV_COREDUMP
help
Bluetooth Virtual HCI device driver.
This driver is required if you want to use HCI Emulation software.
@@ -388,6 +428,7 @@ config BT_ATH3K
config BT_MTKSDIO
tristate "MediaTek HCI SDIO driver"
depends on MMC
+ depends on USB || !BT_HCIBTUSB_MTK
select BT_MTK
help
MediaTek Bluetooth HCI SDIO driver.
@@ -399,7 +440,10 @@ config BT_MTKSDIO
config BT_MTKUART
tristate "MediaTek HCI UART driver"
+ depends on BT_HCIUART
depends on SERIAL_DEV_BUS
+ depends on USB || !BT_HCIBTUSB_MTK
+ select BT_HCIUART_H4
select BT_MTK
help
MediaTek Bluetooth HCI UART driver.
@@ -442,4 +486,30 @@ config BT_VIRTIO
Say Y here to compile support for HCI over Virtio into the
kernel or say M to compile as a module.
+config BT_NXPUART
+ tristate "NXP protocol support"
+ depends on BT_HCIUART
+ depends on SERIAL_DEV_BUS
+ select BT_HCIUART_H4
+ select CRC32
+ select CRC8
+ help
+ NXP is serial driver required for NXP Bluetooth
+ devices with UART interface.
+
+ Say Y here to compile support for NXP Bluetooth UART device into
+ the kernel, or say M here to compile as a module (btnxpuart).
+
+config BT_INTEL_PCIE
+ tristate "Intel HCI PCIe driver"
+ depends on PCI
+ select BT_INTEL
+ select FW_LOADER
+ help
+ Intel Bluetooth transport driver for PCIe.
+ This driver is required if you want to use Intel Bluetooth device
+ with PCIe interface.
+
+ Say Y here to compiler support for Intel Bluetooth PCIe device into
+ the kernel or say M to compile it as module (btintel_pcie)
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 3321a8aea4a0..81856512ddd0 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
obj-$(CONFIG_BT_HCIUART) += hci_uart.o
obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
+obj-$(CONFIG_BT_HCIBCM4377) += hci_bcm4377.o
obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o
obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
@@ -16,6 +17,7 @@ obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_INTEL) += btintel.o
+obj-$(CONFIG_BT_INTEL_PCIE) += btintel_pcie.o btintel.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
@@ -28,6 +30,7 @@ obj-$(CONFIG_BT_QCA) += btqca.o
obj-$(CONFIG_BT_MTK) += btmtk.o
obj-$(CONFIG_BT_VIRTIO) += virtio_bt.o
+obj-$(CONFIG_BT_NXPUART) += btnxpuart.o
obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
@@ -48,4 +51,5 @@ hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o
hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o
hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
+hci_uart-$(CONFIG_BT_HCIUART_AML) += hci_aml.o
hci_uart-objs := $(hci_uart-y)
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 88262d3a9392..fc796f1dbda9 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -3,7 +3,6 @@
* Copyright (c) 2008-2009 Atheros Communications Inc.
*/
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -12,7 +11,7 @@
#include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/usb.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#define VERSION "1.0"
@@ -128,7 +127,6 @@ MODULE_DEVICE_TABLE(usb, ath3k_table);
* for AR3012
*/
static const struct usb_device_id ath3k_blist_tbl[] = {
-
/* Atheros AR3012 with sflash firmware*/
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
@@ -202,7 +200,7 @@ static inline void ath3k_log_failed_loading(int err, int len, int size,
#define TIMEGAP_USEC_MAX 100
static int ath3k_load_firmware(struct usb_device *udev,
- const struct firmware *firmware)
+ const struct firmware *firmware)
{
u8 *send_buf;
int len = 0;
@@ -237,9 +235,9 @@ static int ath3k_load_firmware(struct usb_device *udev,
memcpy(send_buf, firmware->data + sent, size);
err = usb_bulk_msg(udev, pipe, send_buf, size,
- &len, 3000);
+ &len, 3000);
- if (err || (len != size)) {
+ if (err || len != size) {
ath3k_log_failed_loading(err, len, size, count);
goto error;
}
@@ -262,7 +260,7 @@ static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
}
static int ath3k_get_version(struct usb_device *udev,
- struct ath3k_version *version)
+ struct ath3k_version *version)
{
return usb_control_msg_recv(udev, 0, ATH3K_GETVERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
@@ -271,7 +269,7 @@ static int ath3k_get_version(struct usb_device *udev,
}
static int ath3k_load_fwfile(struct usb_device *udev,
- const struct firmware *firmware)
+ const struct firmware *firmware)
{
u8 *send_buf;
int len = 0;
@@ -310,8 +308,8 @@ static int ath3k_load_fwfile(struct usb_device *udev,
memcpy(send_buf, firmware->data + sent, size);
err = usb_bulk_msg(udev, pipe, send_buf, size,
- &len, 3000);
- if (err || (len != size)) {
+ &len, 3000);
+ if (err || len != size) {
ath3k_log_failed_loading(err, len, size, count);
kfree(send_buf);
return err;
@@ -425,7 +423,6 @@ static int ath3k_load_syscfg(struct usb_device *udev)
}
switch (fw_version.ref_clock) {
-
case ATH3K_XTAL_FREQ_26M:
clk_value = 26;
break;
@@ -441,7 +438,7 @@ static int ath3k_load_syscfg(struct usb_device *udev)
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
- le32_to_cpu(fw_version.rom_version), clk_value, ".dfu");
+ le32_to_cpu(fw_version.rom_version), clk_value, ".dfu");
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
@@ -456,7 +453,7 @@ static int ath3k_load_syscfg(struct usb_device *udev)
}
static int ath3k_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+ const struct usb_device_id *id)
{
const struct firmware *firmware;
struct usb_device *udev = interface_to_usbdev(intf);
@@ -505,10 +502,10 @@ static int ath3k_probe(struct usb_interface *intf,
if (ret < 0) {
if (ret == -ENOENT)
BT_ERR("Firmware file \"%s\" not found",
- ATH3K_FIRMWARE);
+ ATH3K_FIRMWARE);
else
BT_ERR("Firmware file \"%s\" request failed (err=%d)",
- ATH3K_FIRMWARE, ret);
+ ATH3K_FIRMWARE, ret);
return ret;
}
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index cab93935cc7f..8df310983bf6 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -365,9 +365,8 @@ static void bfusb_rx_complete(struct urb *urb)
buf += 3;
}
- if (count < len) {
+ if (count < len)
bt_dev_err(data->hdev, "block extends over URB buffer ranges");
- }
if ((hdr & 0xe1) == 0xc1)
bfusb_recv_block(data, hdr, buf, len);
@@ -671,7 +670,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
hdev->flush = bfusb_flush;
hdev->send = bfusb_send_frame;
- set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LOCAL_COMMANDS);
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 36eabf61717f..1e3a56e9b139 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -158,7 +158,7 @@ static void bluecard_detach(struct pcmcia_device *p_dev);
static void bluecard_activity_led_timeout(struct timer_list *t)
{
- struct bluecard_info *info = from_timer(info, t, timer);
+ struct bluecard_info *info = timer_container_of(info, t, timer);
unsigned int iobase = info->p_dev->resource[0]->start;
if (test_bit(CARD_ACTIVITY, &(info->hw_state))) {
@@ -638,7 +638,7 @@ static int bluecard_hci_close(struct hci_dev *hdev)
bluecard_hci_flush(hdev);
/* Stop LED timer */
- del_timer_sync(&(info->timer));
+ timer_delete_sync(&(info->timer));
/* Disable power LED */
outb(0x00, iobase + 0x30);
@@ -885,7 +885,7 @@ static void bluecard_release(struct pcmcia_device *link)
bluecard_close(info);
- del_timer_sync(&(info->timer));
+ timer_delete_sync(&(info->timer));
pcmcia_disable_device(link);
}
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 1fa58c059cbf..e305d04aac9d 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -20,7 +20,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include "h4_recv.h"
+#include "hci_uart.h"
#define VERSION "0.11"
@@ -41,6 +41,7 @@ struct bpa10x_data {
struct usb_anchor rx_anchor;
struct sk_buff *rx_skb[2];
+ struct hci_uart hu;
};
static void bpa10x_tx_complete(struct urb *urb)
@@ -96,7 +97,7 @@ static void bpa10x_rx_complete(struct urb *urb)
if (urb->status == 0) {
bool idx = usb_pipebulk(urb->pipe);
- data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
+ data->rx_skb[idx] = h4_recv_buf(&data->hu, data->rx_skb[idx],
urb->transfer_buffer,
urb->actual_length,
bpa10x_recv_pkts,
@@ -388,6 +389,7 @@ static int bpa10x_probe(struct usb_interface *intf,
hci_set_drvdata(hdev, data);
data->hdev = hdev;
+ data->hu.hdev = hdev;
SET_HCIDEV_DEV(hdev, &intf->dev);
@@ -398,7 +400,7 @@ static int bpa10x_probe(struct usb_interface *intf,
hdev->send = bpa10x_send_frame;
hdev->set_diag = bpa10x_set_diag;
- set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
err = hci_register_dev(hdev);
if (err < 0) {
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 3006e2a0f37e..d33cc70eec66 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -6,11 +6,13 @@
* Copyright (C) 2015 Intel Corporation
*/
+#include <linux/efi.h>
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/dmi.h>
#include <linux/of.h>
-#include <asm/unaligned.h>
+#include <linux/string.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -23,6 +25,7 @@
#define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}})
#define BDADDR_BCM2076B1 (&(bdaddr_t) {{0x79, 0x56, 0x00, 0xa0, 0x76, 0x20}})
#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
+#define BDADDR_BCM43430A1 (&(bdaddr_t) {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}})
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
#define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}})
@@ -34,6 +37,43 @@
/* For kmalloc-ing the fw-name array instead of putting it on the stack */
typedef char bcm_fw_name[BCM_FW_NAME_LEN];
+#ifdef CONFIG_EFI
+static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev)
+{
+ efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f,
+ 0x43, 0x26, 0x81, 0x23, 0xd1, 0x13);
+ bdaddr_t efi_bdaddr, bdaddr;
+ efi_status_t status;
+ unsigned long len;
+ int ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
+ return -EOPNOTSUPP;
+
+ len = sizeof(efi_bdaddr);
+ status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr);
+ if (status != EFI_SUCCESS)
+ return -ENXIO;
+
+ if (len != sizeof(efi_bdaddr))
+ return -EIO;
+
+ baswap(&bdaddr, &efi_bdaddr);
+
+ ret = btbcm_set_bdaddr(hdev, &bdaddr);
+ if (ret)
+ return ret;
+
+ bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr);
+ return 0;
+}
+#else
+static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
int btbcm_check_bdaddr(struct hci_dev *hdev)
{
struct hci_rp_read_bd_addr *bda;
@@ -77,6 +117,9 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
*
* The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller
* with no configured address.
+ *
+ * The address AA:AA:AA:AA:AA:AA indicates a BCM43430A1 controller
+ * with no configured address.
*/
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM20702A1) ||
@@ -86,10 +129,14 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
!bacmp(&bda->bdaddr, BDADDR_BCM4334B0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
+ !bacmp(&bda->bdaddr, BDADDR_BCM43430A1) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
- bt_dev_info(hdev, "BCM: Using default device address (%pMR)",
- &bda->bdaddr);
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ /* Try falling back to BDADDR EFI variable */
+ if (btbcm_set_bdaddr_from_efi(hdev) != 0) {
+ bt_dev_info(hdev, "BCM: Using default device address (%pMR)",
+ &bda->bdaddr);
+ hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR);
+ }
}
kfree_skb(skb);
@@ -420,7 +467,7 @@ static int btbcm_print_controller_features(struct hci_dev *hdev)
/* Read DMI and disable broken Read LE Min/Max Tx Power */
if (dmi_first_match(disable_broken_read_transmit_power))
- set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER);
return 0;
}
@@ -494,13 +541,10 @@ static const struct bcm_subver_table bcm_usb_subver_table[] = {
static const char *btbcm_get_board_name(struct device *dev)
{
#ifdef CONFIG_OF
- struct device_node *root;
+ struct device_node *root __free(device_node) = of_find_node_by_path("/");
char *board_type;
const char *tmp;
- int len;
- int i;
- root = of_find_node_by_path("/");
if (!root)
return NULL;
@@ -508,14 +552,11 @@ static const char *btbcm_get_board_name(struct device *dev)
return NULL;
/* get rid of any '/' in the compatible string */
- len = strlen(tmp) + 1;
- board_type = devm_kzalloc(dev, len, GFP_KERNEL);
- strscpy(board_type, tmp, len);
- for (i = 0; i < board_type[i]; i++) {
- if (board_type[i] == '/')
- board_type[i] = '-';
- }
- of_node_put(root);
+ board_type = devm_kstrdup(dev, tmp, GFP_KERNEL);
+ if (!board_type)
+ return NULL;
+
+ strreplace(board_type, '/', '-');
return board_type;
#else
@@ -601,7 +642,9 @@ int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud
snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid);
}
- fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL);
+ fw_name = kmalloc_array(BCM_FW_NAME_COUNT_MAX,
+ sizeof(*fw_name),
+ GFP_KERNEL);
if (!fw_name)
return -ENOMEM;
@@ -665,7 +708,7 @@ int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_m
btbcm_check_bdaddr(hdev);
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
return 0;
}
@@ -728,7 +771,7 @@ int btbcm_setup_apple(struct hci_dev *hdev)
kfree_skb(skb);
}
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
return 0;
}
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 818681c89db8..9d29ab811f80 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -9,7 +9,11 @@
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/regmap.h>
-#include <asm/unaligned.h>
+#include <linux/string_choices.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <linux/unaligned.h>
+#include <linux/efi.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -24,14 +28,36 @@
#define ECDSA_OFFSET 644
#define ECDSA_HEADER_LEN 320
+#define BTINTEL_EFI_DSBR L"UefiCnvCommonDSBR"
+
+enum {
+ DSM_SET_WDISABLE2_DELAY = 1,
+ DSM_SET_RESET_METHOD = 3,
+};
+
+#define BTINTEL_BT_DOMAIN 0x12
+#define BTINTEL_SAR_LEGACY 0
+#define BTINTEL_SAR_INC_PWR 1
+#define BTINTEL_SAR_INC_PWR_SUPPORTED 0
+
#define CMD_WRITE_BOOT_PARAMS 0xfc0e
struct cmd_write_boot_params {
- u32 boot_addr;
+ __le32 boot_addr;
u8 fw_build_num;
u8 fw_build_ww;
u8 fw_build_yy;
} __packed;
+static struct {
+ const char *driver_name;
+ u8 hw_variant;
+ u32 fw_build_num;
+} coredump_info;
+
+static const guid_t btintel_guid_dsm =
+ GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233,
+ 0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9);
+
int btintel_check_bdaddr(struct hci_dev *hdev)
{
struct hci_rp_read_bd_addr *bda;
@@ -62,7 +88,7 @@ int btintel_check_bdaddr(struct hci_dev *hdev)
if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) {
bt_dev_err(hdev, "Found Intel default device address (%pMR)",
&bda->bdaddr);
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR);
}
kfree_skb(skb);
@@ -218,7 +244,7 @@ static int btintel_set_diag_combined(struct hci_dev *hdev, bool enable)
return ret;
}
-static void btintel_hw_error(struct hci_dev *hdev, u8 code)
+void btintel_hw_error(struct hci_dev *hdev, u8 code)
{
struct sk_buff *skb;
u8 type = 0x00;
@@ -250,6 +276,7 @@ static void btintel_hw_error(struct hci_dev *hdev, u8 code)
kfree_skb(skb);
}
+EXPORT_SYMBOL_GPL(btintel_hw_error);
int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
{
@@ -304,6 +331,9 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
return -EINVAL;
}
+ coredump_info.hw_variant = ver->hw_variant;
+ coredump_info.fw_build_num = ver->fw_build_num;
+
bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u",
variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
ver->fw_build_num, ver->fw_build_ww,
@@ -411,7 +441,7 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
return PTR_ERR(skb);
}
- if (skb->len != sizeof(*ver)) {
+ if (!skb || skb->len != sizeof(*ver)) {
bt_dev_err(hdev, "Intel version event size mismatch");
kfree_skb(skb);
return -EILSEQ;
@@ -425,8 +455,8 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
}
EXPORT_SYMBOL_GPL(btintel_read_version);
-static int btintel_version_info_tlv(struct hci_dev *hdev,
- struct intel_version_tlv *version)
+int btintel_version_info_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *version)
{
const char *variant;
@@ -449,6 +479,12 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
case 0x17: /* TyP */
case 0x18: /* Slr */
case 0x19: /* Slr-F */
+ case 0x1b: /* Mgr */
+ case 0x1c: /* Gale Peak (GaP) */
+ case 0x1d: /* BlazarU (BzrU) */
+ case 0x1e: /* BlazarI (Bzr) */
+ case 0x1f: /* Scorpious Peak */
+ case 0x22: /* BlazarIW (BzrIW) */
break;
default:
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
@@ -457,7 +493,7 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
}
switch (version->img_type) {
- case 0x01:
+ case BTINTEL_IMG_BOOTLOADER:
variant = "Bootloader";
/* It is required that every single firmware fragment is acknowledged
* with a command complete event. If the boot parameters indicate
@@ -478,18 +514,21 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id);
bt_dev_info(hdev, "Secure boot is %s",
- version->secure_boot ? "enabled" : "disabled");
+ str_enabled_disabled(version->secure_boot));
bt_dev_info(hdev, "OTP lock is %s",
- version->otp_lock ? "enabled" : "disabled");
+ str_enabled_disabled(version->otp_lock));
bt_dev_info(hdev, "API lock is %s",
- version->api_lock ? "enabled" : "disabled");
+ str_enabled_disabled(version->api_lock));
bt_dev_info(hdev, "Debug lock is %s",
- version->debug_lock ? "enabled" : "disabled");
+ str_enabled_disabled(version->debug_lock));
bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
version->min_fw_build_nn, version->min_fw_build_cw,
2000 + version->min_fw_build_yy);
break;
- case 0x03:
+ case BTINTEL_IMG_IML:
+ variant = "Intermediate loader";
+ break;
+ case BTINTEL_IMG_OP:
variant = "Firmware";
break;
default:
@@ -497,21 +536,27 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
return -EINVAL;
}
+ coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt);
+ coredump_info.fw_build_num = version->build_num;
+
bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
2000 + (version->timestamp >> 8), version->timestamp & 0xff,
version->build_type, version->build_num);
+ if (version->img_type == BTINTEL_IMG_OP)
+ bt_dev_info(hdev, "Firmware SHA1: 0x%8.8x", version->git_sha1);
return 0;
}
+EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
-static int btintel_parse_version_tlv(struct hci_dev *hdev,
- struct intel_version_tlv *version,
- struct sk_buff *skb)
+int btintel_parse_version_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *version,
+ struct sk_buff *skb)
{
/* Consume Command Complete Status field */
skb_pull(skb, 1);
- /* Event parameters contatin multiple TLVs. Read each of them
+ /* Event parameters contain multiple TLVs. Read each of them
* and only keep the required data. Also, it use existing legacy
* version field like hw_platform, hw_variant, and fw_variant
* to keep the existing setup flow
@@ -595,6 +640,13 @@ static int btintel_parse_version_tlv(struct hci_dev *hdev,
memcpy(&version->otp_bd_addr, tlv->val,
sizeof(bdaddr_t));
break;
+ case INTEL_TLV_GIT_SHA1:
+ version->git_sha1 = get_unaligned_le32(tlv->val);
+ break;
+ case INTEL_TLV_FW_ID:
+ snprintf(version->fw_id, sizeof(version->fw_id),
+ "%s", tlv->val);
+ break;
default:
/* Ignore rest of information */
break;
@@ -605,6 +657,7 @@ static int btintel_parse_version_tlv(struct hci_dev *hdev,
return 0;
}
+EXPORT_SYMBOL_GPL(btintel_parse_version_tlv);
static int btintel_read_version_tlv(struct hci_dev *hdev,
struct intel_version_tlv *version)
@@ -837,7 +890,7 @@ int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param)
params.boot_param = cpu_to_le32(boot_param);
- skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params), &params,
+ skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_RESET, sizeof(params), &params,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to send Intel Reset command");
@@ -882,16 +935,16 @@ int btintel_read_boot_params(struct hci_dev *hdev,
le16_to_cpu(params->dev_revid));
bt_dev_info(hdev, "Secure boot is %s",
- params->secure_boot ? "enabled" : "disabled");
+ str_enabled_disabled(params->secure_boot));
bt_dev_info(hdev, "OTP lock is %s",
- params->otp_lock ? "enabled" : "disabled");
+ str_enabled_disabled(params->otp_lock));
bt_dev_info(hdev, "API lock is %s",
- params->api_lock ? "enabled" : "disabled");
+ str_enabled_disabled(params->api_lock));
bt_dev_info(hdev, "Debug lock is %s",
- params->debug_lock ? "enabled" : "disabled");
+ str_enabled_disabled(params->debug_lock));
bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
params->min_fw_build_nn, params->min_fw_build_cw,
@@ -995,7 +1048,7 @@ static int btintel_download_firmware_payload(struct hci_dev *hdev,
* as needed.
*
* Send set of commands with 4 byte alignment from the
- * firmware data buffer as a single Data fragement.
+ * firmware data buffer as a single Data fragment.
*/
if (!(frag_len % 4)) {
err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
@@ -1132,7 +1185,7 @@ static int btintel_download_fw_tlv(struct hci_dev *hdev,
* If the firmware version has changed that means it needs to be reset
* to bootloader when operational so the new firmware can be loaded.
*/
- if (ver->img_type == 0x03)
+ if (ver->img_type == BTINTEL_IMG_OP)
return -EINVAL;
/* iBT hardware variants 0x0b, 0x0c, 0x11, 0x12, 0x13, 0x14 support
@@ -1207,6 +1260,12 @@ static void btintel_reset_to_bootloader(struct hci_dev *hdev)
struct intel_reset params;
struct sk_buff *skb;
+ /* PCIe transport uses shared hardware reset mechanism for recovery
+ * which gets triggered in pcie *setup* function on error.
+ */
+ if (hdev->bus == HCI_PCI)
+ return;
+
/* Send Intel Reset command. This will result in
* re-enumeration of BT controller.
*
@@ -1222,13 +1281,14 @@ static void btintel_reset_to_bootloader(struct hci_dev *hdev)
* boot_param: Boot address
*
*/
+
params.reset_type = 0x01;
params.patch_enable = 0x01;
params.ddc_reload = 0x01;
params.boot_option = 0x00;
params.boot_param = cpu_to_le32(0x00000000);
- skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params),
+ skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_RESET, sizeof(params),
&params, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "FW download error recovery failed (%ld)",
@@ -1391,6 +1451,59 @@ int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
}
EXPORT_SYMBOL_GPL(btintel_set_quality_report);
+static void btintel_coredump(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc4e, 0, NULL, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb));
+ return;
+ }
+
+ kfree_skb(skb);
+}
+
+static void btintel_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ char buf[80];
+
+ snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
+ coredump_info.hw_variant);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
+ coredump_info.fw_build_num);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info.driver_name);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Vendor: Intel\n");
+ skb_put_data(skb, buf, strlen(buf));
+}
+
+static int btintel_register_devcoredump_support(struct hci_dev *hdev)
+{
+ struct intel_debug_features features;
+ int err;
+
+ err = btintel_read_debug_features(hdev, &features);
+ if (err) {
+ bt_dev_info(hdev, "Error reading debug features");
+ return err;
+ }
+
+ if (!(features.page1[0] & 0x3f)) {
+ bt_dev_dbg(hdev, "Telemetry exception format not supported");
+ return -EOPNOTSUPP;
+ }
+
+ hci_devcd_register(hdev, btintel_coredump, btintel_dmp_hdr, NULL);
+
+ return err;
+}
+
static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev,
struct intel_version *ver)
{
@@ -1743,6 +1856,37 @@ static int btintel_boot_wait(struct hci_dev *hdev, ktime_t calltime, int msec)
return 0;
}
+static int btintel_boot_wait_d0(struct hci_dev *hdev, ktime_t calltime,
+ int msec)
+{
+ ktime_t delta, rettime;
+ unsigned long long duration;
+ int err;
+
+ bt_dev_info(hdev, "Waiting for device transition to d0");
+
+ err = btintel_wait_on_flag_timeout(hdev, INTEL_WAIT_FOR_D0,
+ TASK_INTERRUPTIBLE,
+ msecs_to_jiffies(msec));
+ if (err == -EINTR) {
+ bt_dev_err(hdev, "Device d0 move interrupted");
+ return -EINTR;
+ }
+
+ if (err) {
+ bt_dev_err(hdev, "Device d0 move timeout");
+ return -ETIMEDOUT;
+ }
+
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+
+ bt_dev_info(hdev, "Device moved to D0 in %llu usecs", duration);
+
+ return 0;
+}
+
static int btintel_boot(struct hci_dev *hdev, u32 boot_addr)
{
ktime_t calltime;
@@ -1751,6 +1895,7 @@ static int btintel_boot(struct hci_dev *hdev, u32 boot_addr)
calltime = ktime_get();
btintel_set_flag(hdev, INTEL_BOOTING);
+ btintel_set_flag(hdev, INTEL_WAIT_FOR_D0);
err = btintel_send_intel_reset(hdev, boot_addr);
if (err) {
@@ -1763,13 +1908,28 @@ static int btintel_boot(struct hci_dev *hdev, u32 boot_addr)
* is done by the operational firmware sending bootup notification.
*
* Booting into operational firmware should not take longer than
- * 1 second. However if that happens, then just fail the setup
+ * 5 second. However if that happens, then just fail the setup
* since something went wrong.
*/
- err = btintel_boot_wait(hdev, calltime, 1000);
- if (err == -ETIMEDOUT)
+ err = btintel_boot_wait(hdev, calltime, 5000);
+ if (err == -ETIMEDOUT) {
btintel_reset_to_bootloader(hdev);
+ goto exit_error;
+ }
+
+ if (hdev->bus == HCI_PCI) {
+ /* In case of PCIe, after receiving bootup event, driver performs
+ * D0 entry by writing 0 to sleep control register (check
+ * btintel_pcie_recv_event())
+ * Firmware acks with alive interrupt indicating host is full ready to
+ * perform BT operation. Lets wait here till INTEL_WAIT_FOR_D0
+ * bit is cleared.
+ */
+ calltime = ktime_get();
+ err = btintel_boot_wait_d0(hdev, calltime, 2000);
+ }
+exit_error:
return err;
}
@@ -1782,19 +1942,19 @@ static int btintel_get_fw_name(struct intel_version *ver,
case 0x0b: /* SfP */
case 0x0c: /* WsP */
snprintf(fw_name, len, "intel/ibt-%u-%u.%s",
- le16_to_cpu(ver->hw_variant),
- le16_to_cpu(params->dev_revid),
- suffix);
+ ver->hw_variant,
+ le16_to_cpu(params->dev_revid),
+ suffix);
break;
case 0x11: /* JfP */
case 0x12: /* ThP */
case 0x13: /* HrP */
case 0x14: /* CcP */
snprintf(fw_name, len, "intel/ibt-%u-%u-%u.%s",
- le16_to_cpu(ver->hw_variant),
- le16_to_cpu(ver->hw_revision),
- le16_to_cpu(ver->fw_revision),
- suffix);
+ ver->hw_variant,
+ ver->hw_revision,
+ ver->fw_revision,
+ suffix);
break;
default:
return -EINVAL;
@@ -1868,7 +2028,7 @@ static int btintel_download_fw(struct hci_dev *hdev,
*/
if (!bacmp(&params->otp_bdaddr, BDADDR_ANY)) {
bt_dev_info(hdev, "No device address configured");
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR);
}
download:
@@ -2042,15 +2202,62 @@ static void btintel_get_fw_name_tlv(const struct intel_version_tlv *ver,
char *fw_name, size_t len,
const char *suffix)
{
- /* The firmware file name for new generation controllers will be
+ const char *format;
+ u32 cnvi, cnvr;
+
+ cnvi = INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver->cnvi_top),
+ INTEL_CNVX_TOP_STEP(ver->cnvi_top));
+
+ cnvr = INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver->cnvr_top),
+ INTEL_CNVX_TOP_STEP(ver->cnvr_top));
+
+ /* Only Blazar product supports downloading of intermediate loader
+ * image
+ */
+ if (INTEL_HW_VARIANT(ver->cnvi_bt) >= 0x1e) {
+ u8 zero[BTINTEL_FWID_MAXLEN];
+
+ if (ver->img_type == BTINTEL_IMG_BOOTLOADER) {
+ format = "intel/ibt-%04x-%04x-iml.%s";
+ snprintf(fw_name, len, format, cnvi, cnvr, suffix);
+ return;
+ }
+
+ memset(zero, 0, sizeof(zero));
+
+ /* ibt-<cnvi_top type+cnvi_top step>-<cnvr_top type+cnvr_top step-fw_id> */
+ if (memcmp(ver->fw_id, zero, sizeof(zero))) {
+ format = "intel/ibt-%04x-%04x-%s.%s";
+ snprintf(fw_name, len, format, cnvi, cnvr,
+ ver->fw_id, suffix);
+ return;
+ }
+ /* If firmware id is not present, fallback to legacy naming
+ * convention
+ */
+ }
+ /* Fallback to legacy naming convention for other controllers
* ibt-<cnvi_top type+cnvi_top step>-<cnvr_top type+cnvr_top step>
*/
- snprintf(fw_name, len, "intel/ibt-%04x-%04x.%s",
- INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver->cnvi_top),
- INTEL_CNVX_TOP_STEP(ver->cnvi_top)),
- INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver->cnvr_top),
- INTEL_CNVX_TOP_STEP(ver->cnvr_top)),
- suffix);
+ format = "intel/ibt-%04x-%04x.%s";
+ snprintf(fw_name, len, format, cnvi, cnvr, suffix);
+}
+
+static void btintel_get_iml_tlv(const struct intel_version_tlv *ver,
+ char *fw_name, size_t len,
+ const char *suffix)
+{
+ const char *format;
+ u32 cnvi, cnvr;
+
+ cnvi = INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver->cnvi_top),
+ INTEL_CNVX_TOP_STEP(ver->cnvi_top));
+
+ cnvr = INTEL_CNVX_TOP_PACK_SWAB(INTEL_CNVX_TOP_TYPE(ver->cnvr_top),
+ INTEL_CNVX_TOP_STEP(ver->cnvr_top));
+
+ format = "intel/ibt-%04x-%04x-iml.%s";
+ snprintf(fw_name, len, format, cnvi, cnvr, suffix);
}
static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
@@ -2058,7 +2265,7 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
u32 *boot_param)
{
const struct firmware *fw;
- char fwname[64];
+ char fwname[128];
int err;
ktime_t calltime;
@@ -2078,7 +2285,7 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
* It is not possible to use the Secure Boot Parameters in this
* case since that command is only available in bootloader mode.
*/
- if (ver->img_type == 0x03) {
+ if (ver->img_type == BTINTEL_IMG_OP) {
btintel_clear_flag(hdev, INTEL_BOOTLOADER);
btintel_check_bdaddr(hdev);
} else {
@@ -2089,11 +2296,24 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
*/
if (!bacmp(&ver->otp_bd_addr, BDADDR_ANY)) {
bt_dev_info(hdev, "No device address configured");
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR);
}
}
- btintel_get_fw_name_tlv(ver, fwname, sizeof(fwname), "sfi");
+ if (ver->img_type == BTINTEL_IMG_OP) {
+ /* Controller running OP image. In case of FW downgrade,
+ * FWID TLV may not be present and driver may attempt to load
+ * firmware image which doesn't exist. Lets compare the version
+ * of IML image
+ */
+ if (INTEL_HW_VARIANT(ver->cnvi_bt) >= 0x1e)
+ btintel_get_iml_tlv(ver, fwname, sizeof(fwname), "sfi");
+ else
+ btintel_get_fw_name_tlv(ver, fwname, sizeof(fwname), "sfi");
+ } else {
+ btintel_get_fw_name_tlv(ver, fwname, sizeof(fwname), "sfi");
+ }
+
err = firmware_request_nowarn(&fw, fwname, &hdev->dev);
if (err < 0) {
if (!btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
@@ -2250,8 +2470,669 @@ error:
return err;
}
-static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
- struct intel_version_tlv *ver)
+static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver)
+{
+ struct sk_buff *skb;
+ struct hci_ppag_enable_cmd ppag_cmd;
+ acpi_handle handle;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *p, *elements;
+ u32 domain, mode;
+ acpi_status status;
+
+ /* PPAG is not supported if CRF is HrP2, Jfp2, JfP1 */
+ switch (ver->cnvr_top & 0xFFF) {
+ case 0x504: /* Hrp2 */
+ case 0x202: /* Jfp2 */
+ case 0x201: /* Jfp1 */
+ bt_dev_dbg(hdev, "PPAG not supported for Intel CNVr (0x%3x)",
+ ver->cnvr_top & 0xFFF);
+ return;
+ }
+
+ handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
+ if (!handle) {
+ bt_dev_info(hdev, "No support for BT device in ACPI firmware");
+ return;
+ }
+
+ status = acpi_evaluate_object(handle, "PPAG", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_FOUND) {
+ bt_dev_dbg(hdev, "PPAG-BT: ACPI entry not found");
+ return;
+ }
+ bt_dev_warn(hdev, "PPAG-BT: ACPI Failure: %s", acpi_format_exception(status));
+ return;
+ }
+
+ p = buffer.pointer;
+ if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 2) {
+ bt_dev_warn(hdev, "PPAG-BT: Invalid object type: %d or package count: %d",
+ p->type, p->package.count);
+ kfree(buffer.pointer);
+ return;
+ }
+
+ elements = p->package.elements;
+
+ /* PPAG table is located at element[1] */
+ p = &elements[1];
+
+ domain = (u32)p->package.elements[0].integer.value;
+ mode = (u32)p->package.elements[1].integer.value;
+ kfree(buffer.pointer);
+
+ if (domain != 0x12) {
+ bt_dev_dbg(hdev, "PPAG-BT: Bluetooth domain is disabled in ACPI firmware");
+ return;
+ }
+
+ /* PPAG mode
+ * BIT 0 : 0 Disabled in EU
+ * 1 Enabled in EU
+ * BIT 1 : 0 Disabled in China
+ * 1 Enabled in China
+ */
+ mode &= 0x03;
+
+ if (!mode) {
+ bt_dev_dbg(hdev, "PPAG-BT: EU, China mode are disabled in BIOS");
+ return;
+ }
+
+ ppag_cmd.ppag_enable_flags = cpu_to_le32(mode);
+
+ skb = __hci_cmd_sync(hdev, INTEL_OP_PPAG_CMD, sizeof(ppag_cmd),
+ &ppag_cmd, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_warn(hdev, "Failed to send PPAG Enable (%ld)", PTR_ERR(skb));
+ return;
+ }
+ bt_dev_info(hdev, "PPAG-BT: Enabled (Mode %d)", mode);
+ kfree_skb(skb);
+}
+
+static int btintel_acpi_reset_method(struct hci_dev *hdev)
+{
+ int ret = 0;
+ acpi_status status;
+ union acpi_object *p, *ref;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ bt_dev_err(hdev, "Failed to run _PRR method");
+ ret = -ENODEV;
+ return ret;
+ }
+ p = buffer.pointer;
+
+ if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) {
+ bt_dev_err(hdev, "Invalid arguments");
+ ret = -EINVAL;
+ goto exit_on_error;
+ }
+
+ ref = &p->package.elements[0];
+ if (ref->type != ACPI_TYPE_LOCAL_REFERENCE) {
+ bt_dev_err(hdev, "Invalid object type: 0x%x", ref->type);
+ ret = -EINVAL;
+ goto exit_on_error;
+ }
+
+ status = acpi_evaluate_object(ref->reference.handle, "_RST", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ bt_dev_err(hdev, "Failed to run_RST method");
+ ret = -ENODEV;
+ goto exit_on_error;
+ }
+
+exit_on_error:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static void btintel_set_dsm_reset_method(struct hci_dev *hdev,
+ struct intel_version_tlv *ver_tlv)
+{
+ struct btintel_data *data = hci_get_priv(hdev);
+ acpi_handle handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
+ u8 reset_payload[4] = {0x01, 0x00, 0x01, 0x00};
+ union acpi_object *obj, argv4;
+ enum {
+ RESET_TYPE_WDISABLE2,
+ RESET_TYPE_VSEC
+ };
+
+ handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
+
+ if (!handle) {
+ bt_dev_dbg(hdev, "No support for bluetooth device in ACPI firmware");
+ return;
+ }
+
+ if (!acpi_has_method(handle, "_PRR")) {
+ bt_dev_err(hdev, "No support for _PRR ACPI method");
+ return;
+ }
+
+ switch (ver_tlv->cnvi_top & 0xfff) {
+ case 0x910: /* GalePeak2 */
+ reset_payload[2] = RESET_TYPE_VSEC;
+ break;
+ default:
+ /* WDISABLE2 is the default reset method */
+ reset_payload[2] = RESET_TYPE_WDISABLE2;
+
+ if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0,
+ BIT(DSM_SET_WDISABLE2_DELAY))) {
+ bt_dev_err(hdev, "No dsm support to set reset delay");
+ return;
+ }
+ argv4.integer.type = ACPI_TYPE_INTEGER;
+ /* delay required to toggle BT power */
+ argv4.integer.value = 160;
+ obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
+ DSM_SET_WDISABLE2_DELAY, &argv4);
+ if (!obj) {
+ bt_dev_err(hdev, "Failed to call dsm to set reset delay");
+ return;
+ }
+ ACPI_FREE(obj);
+ }
+
+ bt_dev_info(hdev, "DSM reset method type: 0x%02x", reset_payload[2]);
+
+ if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0,
+ DSM_SET_RESET_METHOD)) {
+ bt_dev_warn(hdev, "No support for dsm to set reset method");
+ return;
+ }
+ argv4.buffer.type = ACPI_TYPE_BUFFER;
+ argv4.buffer.length = sizeof(reset_payload);
+ argv4.buffer.pointer = reset_payload;
+
+ obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
+ DSM_SET_RESET_METHOD, &argv4);
+ if (!obj) {
+ bt_dev_err(hdev, "Failed to call dsm to set reset method");
+ return;
+ }
+ ACPI_FREE(obj);
+ data->acpi_reset_method = btintel_acpi_reset_method;
+}
+
+#define BTINTEL_ISODATA_HANDLE_BASE 0x900
+
+static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ /*
+ * Distinguish ISO data packets form ACL data packets
+ * based on their connection handle value range.
+ */
+ if (iso_capable(hdev) && hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) {
+ __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
+
+ if (hci_handle(handle) >= BTINTEL_ISODATA_HANDLE_BASE)
+ return HCI_ISODATA_PKT;
+ }
+
+ return hci_skb_pkt_type(skb);
+}
+
+/*
+ * UefiCnvCommonDSBR UEFI variable provides information from the OEM platforms
+ * if they have replaced the BRI (Bluetooth Radio Interface) resistor to
+ * overcome the potential STEP errors on their designs. Based on the
+ * configauration, bluetooth firmware shall adjust the BRI response line drive
+ * strength. The below structure represents DSBR data.
+ * struct {
+ * u8 header;
+ * u32 dsbr;
+ * } __packed;
+ *
+ * header - defines revision number of the structure
+ * dsbr - defines drive strength BRI response
+ * bit0
+ * 0 - instructs bluetooth firmware to use default values
+ * 1 - instructs bluetooth firmware to override default values
+ * bit3:1
+ * Reserved
+ * bit7:4
+ * DSBR override values (only if bit0 is set. Default value is 0xF
+ * bit31:7
+ * Reserved
+ * Expected values for dsbr field:
+ * 1. 0xF1 - indicates that the resistor on board is 33 Ohm
+ * 2. 0x00 or 0xB1 - indicates that the resistor on board is 10 Ohm
+ * 3. Non existing UEFI variable or invalid (none of the above) - indicates
+ * that the resistor on board is 10 Ohm
+ * Even if uefi variable is not present, driver shall send 0xfc0a command to
+ * firmware to use default values.
+ *
+ */
+static int btintel_uefi_get_dsbr(u32 *dsbr_var)
+{
+ struct btintel_dsbr {
+ u8 header;
+ u32 dsbr;
+ } __packed data;
+
+ efi_status_t status;
+ unsigned long data_size = sizeof(data);
+ efi_guid_t guid = EFI_GUID(0xe65d8884, 0xd4af, 0x4b20, 0x8d, 0x03,
+ 0x77, 0x2e, 0xcc, 0x3d, 0xa5, 0x31);
+
+ if (!IS_ENABLED(CONFIG_EFI))
+ return -EOPNOTSUPP;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
+ return -EOPNOTSUPP;
+
+ status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size,
+ &data);
+
+ if (status != EFI_SUCCESS || data_size != sizeof(data))
+ return -ENXIO;
+
+ *dsbr_var = data.dsbr;
+ return 0;
+}
+
+static int btintel_set_dsbr(struct hci_dev *hdev, struct intel_version_tlv *ver)
+{
+ struct btintel_dsbr_cmd {
+ u8 enable;
+ u8 dsbr;
+ } __packed;
+
+ struct btintel_dsbr_cmd cmd;
+ struct sk_buff *skb;
+ u32 dsbr, cnvi;
+ u8 status;
+ int err;
+
+ cnvi = ver->cnvi_top & 0xfff;
+ /* DSBR command needs to be sent for,
+ * 1. BlazarI or BlazarIW + B0 step product in IML image.
+ * 2. Gale Peak2 or BlazarU in OP image.
+ * 3. Scorpious Peak in IML image.
+ */
+
+ switch (cnvi) {
+ case BTINTEL_CNVI_BLAZARI:
+ case BTINTEL_CNVI_BLAZARIW:
+ if (ver->img_type == BTINTEL_IMG_IML &&
+ INTEL_CNVX_TOP_STEP(ver->cnvi_top) == 0x01)
+ break;
+ return 0;
+ case BTINTEL_CNVI_GAP:
+ case BTINTEL_CNVI_BLAZARU:
+ if (ver->img_type == BTINTEL_IMG_OP &&
+ hdev->bus == HCI_USB)
+ break;
+ return 0;
+ case BTINTEL_CNVI_SCP:
+ if (ver->img_type == BTINTEL_IMG_IML)
+ break;
+ return 0;
+ default:
+ return 0;
+ }
+
+ dsbr = 0;
+ err = btintel_uefi_get_dsbr(&dsbr);
+ if (err < 0)
+ bt_dev_dbg(hdev, "Error reading efi: %ls (%d)",
+ BTINTEL_EFI_DSBR, err);
+
+ cmd.enable = dsbr & BIT(0);
+ cmd.dsbr = dsbr >> 4 & 0xF;
+
+ bt_dev_info(hdev, "dsbr: enable: 0x%2.2x value: 0x%2.2x", cmd.enable,
+ cmd.dsbr);
+
+ skb = __hci_cmd_sync(hdev, 0xfc0a, sizeof(cmd), &cmd, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb))
+ return -bt_to_errno(PTR_ERR(skb));
+
+ status = skb->data[0];
+ kfree_skb(skb);
+
+ if (status)
+ return -bt_to_errno(status);
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static acpi_status btintel_evaluate_acpi_method(struct hci_dev *hdev,
+ acpi_string method,
+ union acpi_object **ptr,
+ u8 pkg_size)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *p;
+ acpi_status status;
+ acpi_handle handle;
+
+ handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
+ if (!handle) {
+ bt_dev_dbg(hdev, "ACPI-BT: No ACPI support for Bluetooth device");
+ return AE_NOT_EXIST;
+ }
+
+ status = acpi_evaluate_object(handle, method, NULL, &buffer);
+
+ if (ACPI_FAILURE(status)) {
+ bt_dev_dbg(hdev, "ACPI-BT: ACPI Failure: %s method: %s",
+ acpi_format_exception(status), method);
+ return status;
+ }
+
+ p = buffer.pointer;
+
+ if (p->type != ACPI_TYPE_PACKAGE || p->package.count < pkg_size) {
+ bt_dev_warn(hdev, "ACPI-BT: Invalid object type: %d or package count: %d",
+ p->type, p->package.count);
+ kfree(buffer.pointer);
+ return AE_ERROR;
+ }
+
+ *ptr = buffer.pointer;
+ return 0;
+}
+
+static union acpi_object *btintel_acpi_get_bt_pkg(union acpi_object *buffer)
+{
+ union acpi_object *domain, *bt_pkg;
+ int i;
+
+ for (i = 1; i < buffer->package.count; i++) {
+ bt_pkg = &buffer->package.elements[i];
+ domain = &bt_pkg->package.elements[0];
+ if (domain->type == ACPI_TYPE_INTEGER &&
+ domain->integer.value == BTINTEL_BT_DOMAIN)
+ return bt_pkg;
+ }
+ return ERR_PTR(-ENOENT);
+}
+
+static int btintel_send_sar_ddc(struct hci_dev *hdev, struct btintel_cp_ddc_write *data, u8 len)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc8b, len, data, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_warn(hdev, "Failed to send sar ddc id:0x%4.4x (%ld)",
+ le16_to_cpu(data->id), PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+ return 0;
+}
+
+static int btintel_send_edr(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
+ int id, struct btintel_sar_inc_pwr *sar)
+{
+ cmd->len = 5;
+ cmd->id = cpu_to_le16(id);
+ cmd->data[0] = sar->br >> 3;
+ cmd->data[1] = sar->edr2 >> 3;
+ cmd->data[2] = sar->edr3 >> 3;
+ return btintel_send_sar_ddc(hdev, cmd, 6);
+}
+
+static int btintel_send_le(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
+ int id, struct btintel_sar_inc_pwr *sar)
+{
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(id);
+ cmd->data[0] = min3(sar->le, sar->le_lr, sar->le_2mhz) >> 3;
+ return btintel_send_sar_ddc(hdev, cmd, 4);
+}
+
+static int btintel_send_br(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
+ int id, struct btintel_sar_inc_pwr *sar)
+{
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(id);
+ cmd->data[0] = sar->br >> 3;
+ return btintel_send_sar_ddc(hdev, cmd, 4);
+}
+
+static int btintel_send_br_mutual(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
+ int id, struct btintel_sar_inc_pwr *sar)
+{
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(id);
+ cmd->data[0] = sar->br;
+ return btintel_send_sar_ddc(hdev, cmd, 4);
+}
+
+static int btintel_send_edr2(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
+ int id, struct btintel_sar_inc_pwr *sar)
+{
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(id);
+ cmd->data[0] = sar->edr2;
+ return btintel_send_sar_ddc(hdev, cmd, 4);
+}
+
+static int btintel_send_edr3(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
+ int id, struct btintel_sar_inc_pwr *sar)
+{
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(id);
+ cmd->data[0] = sar->edr3;
+ return btintel_send_sar_ddc(hdev, cmd, 4);
+}
+
+static int btintel_set_legacy_sar(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar)
+{
+ struct btintel_cp_ddc_write *cmd;
+ u8 buffer[64];
+ int ret;
+
+ cmd = (void *)buffer;
+ ret = btintel_send_br(hdev, cmd, 0x0131, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_br(hdev, cmd, 0x0132, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_le(hdev, cmd, 0x0133, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_edr(hdev, cmd, 0x0137, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_edr(hdev, cmd, 0x0138, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_edr(hdev, cmd, 0x013b, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_edr(hdev, cmd, 0x013c, sar);
+
+ return ret;
+}
+
+static int btintel_set_mutual_sar(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar)
+{
+ struct btintel_cp_ddc_write *cmd;
+ struct sk_buff *skb;
+ u8 buffer[64];
+ bool enable;
+ int ret;
+
+ cmd = (void *)buffer;
+
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(0x019e);
+
+ if (sar->revision == BTINTEL_SAR_INC_PWR &&
+ sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED)
+ cmd->data[0] = 0x01;
+ else
+ cmd->data[0] = 0x00;
+
+ ret = btintel_send_sar_ddc(hdev, cmd, 4);
+ if (ret)
+ return ret;
+
+ if (sar->revision == BTINTEL_SAR_INC_PWR &&
+ sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED) {
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(0x019f);
+ cmd->data[0] = sar->sar_2400_chain_a;
+
+ ret = btintel_send_sar_ddc(hdev, cmd, 4);
+ if (ret)
+ return ret;
+ }
+
+ ret = btintel_send_br_mutual(hdev, cmd, 0x01a0, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_edr2(hdev, cmd, 0x01a1, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_edr3(hdev, cmd, 0x01a2, sar);
+ if (ret)
+ return ret;
+
+ ret = btintel_send_le(hdev, cmd, 0x01a3, sar);
+ if (ret)
+ return ret;
+
+ enable = true;
+ skb = __hci_cmd_sync(hdev, 0xfe25, 1, &enable, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_warn(hdev, "Failed to send Intel SAR Enable (%ld)", PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int btintel_sar_send_to_device(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar,
+ struct intel_version_tlv *ver)
+{
+ u16 cnvi, cnvr;
+ int ret;
+
+ cnvi = ver->cnvi_top & 0xfff;
+ cnvr = ver->cnvr_top & 0xfff;
+
+ if (cnvi < BTINTEL_CNVI_BLAZARI && cnvr < BTINTEL_CNVR_FMP2) {
+ bt_dev_info(hdev, "Applying legacy Bluetooth SAR");
+ ret = btintel_set_legacy_sar(hdev, sar);
+ } else if (cnvi == BTINTEL_CNVI_GAP || cnvr == BTINTEL_CNVR_FMP2) {
+ bt_dev_info(hdev, "Applying mutual Bluetooth SAR");
+ ret = btintel_set_mutual_sar(hdev, sar);
+ } else {
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv *ver)
+{
+ union acpi_object *bt_pkg, *buffer = NULL;
+ struct btintel_sar_inc_pwr sar;
+ acpi_status status;
+ u8 revision;
+ int ret;
+
+ status = btintel_evaluate_acpi_method(hdev, "BRDS", &buffer, 2);
+ if (ACPI_FAILURE(status))
+ return -ENOENT;
+
+ bt_pkg = btintel_acpi_get_bt_pkg(buffer);
+
+ if (IS_ERR(bt_pkg)) {
+ ret = PTR_ERR(bt_pkg);
+ goto error;
+ }
+
+ if (!bt_pkg->package.count) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ revision = buffer->package.elements[0].integer.value;
+
+ if (revision > BTINTEL_SAR_INC_PWR) {
+ bt_dev_dbg(hdev, "BT_SAR: revision: 0x%2.2x not supported", revision);
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+
+ memset(&sar, 0, sizeof(sar));
+
+ if (revision == BTINTEL_SAR_LEGACY && bt_pkg->package.count == 8) {
+ sar.revision = revision;
+ sar.bt_sar_bios = bt_pkg->package.elements[1].integer.value;
+ sar.br = bt_pkg->package.elements[2].integer.value;
+ sar.edr2 = bt_pkg->package.elements[3].integer.value;
+ sar.edr3 = bt_pkg->package.elements[4].integer.value;
+ sar.le = bt_pkg->package.elements[5].integer.value;
+ sar.le_2mhz = bt_pkg->package.elements[6].integer.value;
+ sar.le_lr = bt_pkg->package.elements[7].integer.value;
+
+ } else if (revision == BTINTEL_SAR_INC_PWR && bt_pkg->package.count == 10) {
+ sar.revision = revision;
+ sar.bt_sar_bios = bt_pkg->package.elements[1].integer.value;
+ sar.inc_power_mode = bt_pkg->package.elements[2].integer.value;
+ sar.sar_2400_chain_a = bt_pkg->package.elements[3].integer.value;
+ sar.br = bt_pkg->package.elements[4].integer.value;
+ sar.edr2 = bt_pkg->package.elements[5].integer.value;
+ sar.edr3 = bt_pkg->package.elements[6].integer.value;
+ sar.le = bt_pkg->package.elements[7].integer.value;
+ sar.le_2mhz = bt_pkg->package.elements[8].integer.value;
+ sar.le_lr = bt_pkg->package.elements[9].integer.value;
+ } else {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Apply only if it is enabled in BIOS */
+ if (sar.bt_sar_bios != 1) {
+ bt_dev_dbg(hdev, "Bluetooth SAR is not enabled");
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+
+ ret = btintel_sar_send_to_device(hdev, &sar, ver);
+error:
+ kfree(buffer);
+ return ret;
+}
+#endif /* CONFIG_ACPI */
+
+static int btintel_set_specific_absorption_rate(struct hci_dev *hdev,
+ struct intel_version_tlv *ver)
+{
+#ifdef CONFIG_ACPI
+ return btintel_acpi_set_sar(hdev, ver);
+#endif
+ return 0;
+}
+
+int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *ver)
{
u32 boot_param;
char ddcname[64];
@@ -2266,6 +3147,13 @@ static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
*/
boot_param = 0x00000000;
+ /* In case of PCIe, this function might get called multiple times with
+ * same hdev instance if there is any error on firmware download.
+ * Need to clear stale bits of previous firmware download attempt.
+ */
+ for (int i = 0; i < __INTEL_NUM_FLAGS; i++)
+ btintel_clear_flag(hdev, i);
+
btintel_set_flag(hdev, INTEL_BOOTLOADER);
err = btintel_prepare_fw_download_tlv(hdev, ver, &boot_param);
@@ -2273,13 +3161,37 @@ static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
return err;
/* check if controller is already having an operational firmware */
- if (ver->img_type == 0x03)
+ if (ver->img_type == BTINTEL_IMG_OP)
goto finish;
err = btintel_boot(hdev, boot_param);
if (err)
return err;
+ err = btintel_read_version_tlv(hdev, ver);
+ if (err)
+ return err;
+
+ /* set drive strength of BRI response */
+ err = btintel_set_dsbr(hdev, ver);
+ if (err) {
+ bt_dev_err(hdev, "Failed to send dsbr command (%d)", err);
+ return err;
+ }
+
+ /* If image type returned is BTINTEL_IMG_IML, then controller supports
+ * intermediate loader image
+ */
+ if (ver->img_type == BTINTEL_IMG_IML) {
+ err = btintel_prepare_fw_download_tlv(hdev, ver, &boot_param);
+ if (err)
+ return err;
+
+ err = btintel_boot(hdev, boot_param);
+ if (err)
+ return err;
+ }
+
btintel_clear_flag(hdev, INTEL_BOOTLOADER);
btintel_get_fw_name_tlv(ver, ddcname, sizeof(ddcname), "ddc");
@@ -2296,6 +3208,12 @@ static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
+ /* Send sar values to controller */
+ btintel_set_specific_absorption_rate(hdev, ver);
+
+ /* Set PPAG feature */
+ btintel_set_ppag(hdev, ver);
+
/* Read the Intel version information after loading the FW */
err = btintel_read_version_tlv(hdev, &new_ver);
if (err)
@@ -2315,8 +3233,9 @@ finish:
return 0;
}
+EXPORT_SYMBOL_GPL(btintel_bootloader_setup_tlv);
-static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
+void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
{
switch (hw_variant) {
/* Legacy bootloader devices that supports MSFT Extension */
@@ -2324,12 +3243,18 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
case 0x12: /* ThP */
case 0x13: /* HrP */
case 0x14: /* CcP */
- /* All Intel new genration controllers support the Microsoft vendor
+ /* All Intel new generation controllers support the Microsoft vendor
* extension are using 0xFC1E for VsMsftOpCode.
*/
case 0x17:
case 0x18:
case 0x19:
+ case 0x1b:
+ case 0x1c:
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ case 0x22:
hci_set_msft_opcode(hdev, 0xFC1E);
break;
default:
@@ -2337,6 +3262,121 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
break;
}
}
+EXPORT_SYMBOL_GPL(btintel_set_msft_opcode);
+
+void btintel_print_fseq_info(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ u8 *p;
+ u32 val;
+ const char *str;
+
+ skb = __hci_cmd_sync(hdev, 0xfcb3, 0, NULL, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_dbg(hdev, "Reading fseq status command failed (%ld)",
+ PTR_ERR(skb));
+ return;
+ }
+
+ if (skb->len < (sizeof(u32) * 16 + 2)) {
+ bt_dev_dbg(hdev, "Malformed packet of length %u received",
+ skb->len);
+ kfree_skb(skb);
+ return;
+ }
+
+ p = skb_pull_data(skb, 1);
+ if (*p) {
+ bt_dev_dbg(hdev, "Failed to get fseq status (0x%2.2x)", *p);
+ kfree_skb(skb);
+ return;
+ }
+
+ p = skb_pull_data(skb, 1);
+ switch (*p) {
+ case 0:
+ str = "Success";
+ break;
+ case 1:
+ str = "Fatal error";
+ break;
+ case 2:
+ str = "Semaphore acquire error";
+ break;
+ default:
+ str = "Unknown error";
+ break;
+ }
+
+ if (*p) {
+ bt_dev_err(hdev, "Fseq status: %s (0x%2.2x)", str, *p);
+ kfree_skb(skb);
+ return;
+ }
+
+ bt_dev_info(hdev, "Fseq status: %s (0x%2.2x)", str, *p);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Reason: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Global version: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Installed version: 0x%8.8x", val);
+
+ p = skb->data;
+ skb_pull_data(skb, 4);
+ bt_dev_info(hdev, "Fseq executed: %2.2u.%2.2u.%2.2u.%2.2u", p[0], p[1],
+ p[2], p[3]);
+
+ p = skb->data;
+ skb_pull_data(skb, 4);
+ bt_dev_info(hdev, "Fseq BT Top: %2.2u.%2.2u.%2.2u.%2.2u", p[0], p[1],
+ p[2], p[3]);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq Top init version: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq Cnvio init version: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq MBX Wifi file version: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq BT version: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq Top reset address: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq MBX timeout: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq MBX ack: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq CNVi id: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq CNVr id: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq Error handle: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq Magic noalive indication: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq OTP version: 0x%8.8x", val);
+
+ val = get_unaligned_le32(skb_pull_data(skb, 4));
+ bt_dev_dbg(hdev, "Fseq MBX otp version: 0x%8.8x", val);
+
+ kfree_skb(skb);
+}
+EXPORT_SYMBOL_GPL(btintel_print_fseq_info);
static int btintel_setup_combined(struct hci_dev *hdev)
{
@@ -2397,9 +3437,9 @@ static int btintel_setup_combined(struct hci_dev *hdev)
}
/* Apply the common HCI quirks for Intel device */
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
- set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_DIAG);
/* Set up the quality report callback for Intel devices */
hdev->set_quality_report = btintel_set_quality_report;
@@ -2437,33 +3477,33 @@ static int btintel_setup_combined(struct hci_dev *hdev)
*/
if (!btintel_test_flag(hdev,
INTEL_ROM_LEGACY_NO_WBS_SUPPORT))
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
- &hdev->quirks);
+ hci_set_quirk(hdev,
+ HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
err = btintel_legacy_rom_setup(hdev, &ver);
break;
case 0x0b: /* SfP */
- case 0x0c: /* WsP */
case 0x11: /* JfP */
case 0x12: /* ThP */
case 0x13: /* HrP */
case 0x14: /* CcP */
+ fallthrough;
+ case 0x0c: /* WsP */
/* Apply the device specific HCI quirks
*
* All Legacy bootloader devices support WBS
*/
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
- &hdev->quirks);
+ hci_set_quirk(hdev,
+ HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
- /* Valid LE States quirk for JfP/ThP familiy */
- if (ver.hw_variant == 0x11 || ver.hw_variant == 0x12)
- set_bit(HCI_QUIRK_VALID_LE_STATES,
- &hdev->quirks);
+ /* These variants don't seem to support LE Coded PHY */
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_CODED);
/* Setup MSFT Extension support */
btintel_set_msft_opcode(hdev, ver.hw_variant);
err = btintel_bootloader_setup(hdev, &ver);
+ btintel_register_devcoredump_support(hdev);
break;
default:
bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
@@ -2471,6 +3511,11 @@ static int btintel_setup_combined(struct hci_dev *hdev)
err = -EINVAL;
}
+ hci_set_hw_info(hdev,
+ "INTEL platform=%u variant=%u revision=%u",
+ ver.hw_platform, ver.hw_variant,
+ ver.hw_revision);
+
goto exit_error;
}
@@ -2522,26 +3567,35 @@ static int btintel_setup_combined(struct hci_dev *hdev)
*/
err = btintel_read_version(hdev, &ver);
if (err)
- return err;
+ break;
/* Apply the device specific HCI quirks
*
* All Legacy bootloader devices support WBS
*/
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
- /* Valid LE States quirk for JfP/ThP familiy */
- if (ver.hw_variant == 0x11 || ver.hw_variant == 0x12)
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
+ /* These variants don't seem to support LE Coded PHY */
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_CODED);
/* Setup MSFT Extension support */
btintel_set_msft_opcode(hdev, ver.hw_variant);
err = btintel_bootloader_setup(hdev, &ver);
+ btintel_register_devcoredump_support(hdev);
break;
+ case 0x18: /* GfP2 */
+ case 0x1c: /* GaP */
+ /* Re-classify packet type for controllers with LE audio */
+ hdev->classify_pkt_type = btintel_classify_pkt_type;
+ fallthrough;
case 0x17:
- case 0x18:
case 0x19:
+ case 0x1b:
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ case 0x22:
/* Display version information of TLV type */
btintel_version_info_tlv(hdev, &ver_tlv);
@@ -2549,31 +3603,38 @@ static int btintel_setup_combined(struct hci_dev *hdev)
*
* All TLV based devices support WBS
*/
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
-
- /* Valid LE States quirk for GfP */
- if (INTEL_HW_VARIANT(ver_tlv.cnvi_bt) == 0x18)
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
/* Setup MSFT Extension support */
btintel_set_msft_opcode(hdev,
INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
+ btintel_set_dsm_reset_method(hdev, &ver_tlv);
err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
+ if (err)
+ goto exit_error;
+
+ btintel_register_devcoredump_support(hdev);
+ btintel_print_fseq_info(hdev);
break;
default:
bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
- return -EINVAL;
+ err = -EINVAL;
+ break;
}
+ hci_set_hw_info(hdev, "INTEL platform=%u variant=%u",
+ INTEL_HW_PLATFORM(ver_tlv.cnvi_bt),
+ INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
+
exit_error:
kfree_skb(skb);
return err;
}
-static int btintel_shutdown_combined(struct hci_dev *hdev)
+int btintel_shutdown_combined(struct hci_dev *hdev)
{
struct sk_buff *skb;
int ret;
@@ -2607,8 +3668,9 @@ static int btintel_shutdown_combined(struct hci_dev *hdev)
return 0;
}
+EXPORT_SYMBOL_GPL(btintel_shutdown_combined);
-int btintel_configure_setup(struct hci_dev *hdev)
+int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
{
hdev->manufacturer = 2;
hdev->setup = btintel_setup_combined;
@@ -2617,10 +3679,87 @@ int btintel_configure_setup(struct hci_dev *hdev)
hdev->set_diag = btintel_set_diag_combined;
hdev->set_bdaddr = btintel_set_bdaddr;
+ coredump_info.driver_name = driver_name;
+
return 0;
}
EXPORT_SYMBOL_GPL(btintel_configure_setup);
+static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct intel_tlv *tlv = (void *)&skb->data[5];
+
+ /* The first event is always an event type TLV */
+ if (tlv->type != INTEL_TLV_TYPE_ID)
+ goto recv_frame;
+
+ switch (tlv->val[0]) {
+ case INTEL_TLV_SYSTEM_EXCEPTION:
+ case INTEL_TLV_FATAL_EXCEPTION:
+ case INTEL_TLV_DEBUG_EXCEPTION:
+ case INTEL_TLV_TEST_EXCEPTION:
+ /* Generate devcoredump from exception */
+ if (!hci_devcd_init(hdev, skb->len)) {
+ hci_devcd_append(hdev, skb_clone(skb, GFP_ATOMIC));
+ hci_devcd_complete(hdev);
+ } else {
+ bt_dev_err(hdev, "Failed to generate devcoredump");
+ }
+ break;
+ default:
+ bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
+ }
+
+recv_frame:
+ return hci_recv_frame(hdev, skb);
+}
+
+int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *)skb->data;
+ const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };
+
+ if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+ hdr->plen > 0) {
+ const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
+ unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
+
+ if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
+ switch (skb->data[2]) {
+ case 0x02:
+ /* When switching to the operational firmware
+ * the device sends a vendor specific event
+ * indicating that the bootup completed.
+ */
+ btintel_bootup(hdev, ptr, len);
+ kfree_skb(skb);
+ return 0;
+ case 0x06:
+ /* When the firmware loading completes the
+ * device sends out a vendor specific event
+ * indicating the result of the firmware
+ * loading.
+ */
+ btintel_secure_send_result(hdev, ptr, len);
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ /* Handle all diagnostics events separately. May still call
+ * hci_recv_frame.
+ */
+ if (len >= sizeof(diagnostics_hdr) &&
+ memcmp(&skb->data[2], diagnostics_hdr,
+ sizeof(diagnostics_hdr)) == 0) {
+ return btintel_diagnostics(hdev, skb);
+ }
+ }
+
+ return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL_GPL(btintel_recv_event);
+
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len)
{
const struct intel_bootup *evt = ptr;
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index e0060e58573c..431998049e68 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -41,7 +41,9 @@ enum {
INTEL_TLV_LIMITED_CCE,
INTEL_TLV_SBE_TYPE,
INTEL_TLV_OTP_BDADDR,
- INTEL_TLV_UNLOCKED_STATE
+ INTEL_TLV_UNLOCKED_STATE,
+ INTEL_TLV_GIT_SHA1,
+ INTEL_TLV_FW_ID = 0x50
};
struct intel_tlv {
@@ -50,6 +52,23 @@ struct intel_tlv {
u8 val[];
} __packed;
+#define BTINTEL_HCI_OP_RESET 0xfc01
+
+#define BTINTEL_CNVI_BLAZARI 0x900
+#define BTINTEL_CNVI_BLAZARIW 0x901
+#define BTINTEL_CNVI_GAP 0x910
+#define BTINTEL_CNVI_BLAZARU 0x930
+#define BTINTEL_CNVI_SCP 0xA00
+
+/* CNVR */
+#define BTINTEL_CNVR_FMP2 0x910
+
+#define BTINTEL_IMG_BOOTLOADER 0x01 /* Bootloader image */
+#define BTINTEL_IMG_IML 0x02 /* Intermediate image */
+#define BTINTEL_IMG_OP 0x03 /* Operational image */
+
+#define BTINTEL_FWID_MAXLEN 64
+
struct intel_version_tlv {
u32 cnvi_top;
u32 cnvr_top;
@@ -69,6 +88,8 @@ struct intel_version_tlv {
u8 min_fw_build_yy;
u8 limited_cce;
u8 sbe_type;
+ u32 git_sha1;
+ u8 fw_id[BTINTEL_FWID_MAXLEN];
bdaddr_t otp_bd_addr;
};
@@ -137,6 +158,38 @@ struct intel_offload_use_cases {
__u8 preset[8];
} __packed;
+#define INTEL_OP_PPAG_CMD 0xFE0B
+struct hci_ppag_enable_cmd {
+ __le32 ppag_enable_flags;
+} __packed;
+
+#define INTEL_TLV_TYPE_ID 0x01
+
+#define INTEL_TLV_SYSTEM_EXCEPTION 0x00
+#define INTEL_TLV_FATAL_EXCEPTION 0x01
+#define INTEL_TLV_DEBUG_EXCEPTION 0x02
+#define INTEL_TLV_TEST_EXCEPTION 0xDE
+
+struct btintel_cp_ddc_write {
+ u8 len;
+ __le16 id;
+ u8 data[];
+} __packed;
+
+/* Bluetooth SAR feature (BRDS), Revision 1 */
+struct btintel_sar_inc_pwr {
+ u8 revision;
+ u32 bt_sar_bios; /* Mode of SAR control to be used, 1:enabled in bios */
+ u32 inc_power_mode; /* Increased power mode */
+ u8 sar_2400_chain_a; /* Sar power restriction LB */
+ u8 br;
+ u8 edr2;
+ u8 edr3;
+ u8 le;
+ u8 le_2mhz;
+ u8 le_lr;
+};
+
#define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
#define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16))
#define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff)
@@ -153,12 +206,15 @@ enum {
INTEL_BROKEN_SHUTDOWN_LED,
INTEL_ROM_LEGACY,
INTEL_ROM_LEGACY_NO_WBS_SUPPORT,
+ INTEL_ACPI_RESET_ACTIVE,
+ INTEL_WAIT_FOR_D0,
__INTEL_NUM_FLAGS,
};
struct btintel_data {
DECLARE_BITMAP(flags, __INTEL_NUM_FLAGS);
+ int (*acpi_reset_method)(struct hci_dev *hdev);
};
#define btintel_set_flag(hdev, nr) \
@@ -187,7 +243,7 @@ struct btintel_data {
#define btintel_wait_on_flag_timeout(hdev, nr, m, to) \
wait_on_bit_timeout(btintel_get_flag(hdev), (nr), m, to)
-#if IS_ENABLED(CONFIG_BT_INTEL)
+#if IS_ENABLED(CONFIG_BT_INTEL) || IS_ENABLED(CONFIG_BT_INTEL_PCIE)
int btintel_check_bdaddr(struct hci_dev *hdev);
int btintel_enter_mfg(struct hci_dev *hdev);
@@ -206,11 +262,23 @@ int btintel_read_boot_params(struct hci_dev *hdev,
struct intel_boot_params *params);
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
const struct firmware *fw, u32 *boot_param);
-int btintel_configure_setup(struct hci_dev *hdev);
+int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name);
+int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
void btintel_secure_send_result(struct hci_dev *hdev,
const void *ptr, unsigned int len);
int btintel_set_quality_report(struct hci_dev *hdev, bool enable);
+int btintel_version_info_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *version);
+int btintel_parse_version_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *version,
+ struct sk_buff *skb);
+void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant);
+int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *ver);
+int btintel_shutdown_combined(struct hci_dev *hdev);
+void btintel_hw_error(struct hci_dev *hdev, u8 code);
+void btintel_print_fseq_info(struct hci_dev *hdev);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@@ -287,7 +355,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
return -EOPNOTSUPP;
}
-static inline int btintel_configure_setup(struct hci_dev *hdev)
+static inline int btintel_configure_setup(struct hci_dev *hdev,
+ const char *driver_name)
{
return -ENODEV;
}
@@ -306,4 +375,41 @@ static inline int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
{
return -ENODEV;
}
+
+static inline int btintel_version_info_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *version)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btintel_parse_version_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *version,
+ struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
+
+{
+}
+
+static inline int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
+ struct intel_version_tlv *ver)
+{
+ return -ENODEV;
+}
+
+static inline int btintel_shutdown_combined(struct hci_dev *hdev)
+{
+ return -ENODEV;
+}
+
+static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
+{
+}
+
+static inline void btintel_print_fseq_info(struct hci_dev *hdev)
+{
+}
#endif
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
new file mode 100644
index 000000000000..2936b535479f
--- /dev/null
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -0,0 +1,2767 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Bluetooth support for Intel PCIe devices
+ *
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <linux/unaligned.h>
+#include <linux/devcoredump.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_drv.h>
+
+#include "btintel.h"
+#include "btintel_pcie.h"
+
+#define VERSION "0.1"
+
+#define BTINTEL_PCI_DEVICE(dev, subdev) \
+ .vendor = PCI_VENDOR_ID_INTEL, \
+ .device = (dev), \
+ .subvendor = PCI_ANY_ID, \
+ .subdevice = (subdev), \
+ .driver_data = 0
+
+#define POLL_INTERVAL_US 10
+
+/* Intel Bluetooth PCIe device id table */
+static const struct pci_device_id btintel_pcie_table[] = {
+ /* BlazarI, Wildcat Lake */
+ { BTINTEL_PCI_DEVICE(0x4D76, PCI_ANY_ID) },
+ /* BlazarI, Lunar Lake */
+ { BTINTEL_PCI_DEVICE(0xA876, PCI_ANY_ID) },
+ /* Scorpious, Panther Lake-H484 */
+ { BTINTEL_PCI_DEVICE(0xE376, PCI_ANY_ID) },
+ /* Scorpious, Panther Lake-H404 */
+ { BTINTEL_PCI_DEVICE(0xE476, PCI_ANY_ID) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, btintel_pcie_table);
+
+struct btintel_pcie_dev_recovery {
+ struct list_head list;
+ u8 count;
+ time64_t last_error;
+ char name[];
+};
+
+/* Intel PCIe uses 4 bytes of HCI type instead of 1 byte BT SIG HCI type */
+#define BTINTEL_PCIE_HCI_TYPE_LEN 4
+#define BTINTEL_PCIE_HCI_CMD_PKT 0x00000001
+#define BTINTEL_PCIE_HCI_ACL_PKT 0x00000002
+#define BTINTEL_PCIE_HCI_SCO_PKT 0x00000003
+#define BTINTEL_PCIE_HCI_EVT_PKT 0x00000004
+#define BTINTEL_PCIE_HCI_ISO_PKT 0x00000005
+
+#define BTINTEL_PCIE_MAGIC_NUM 0xA5A5A5A5
+
+#define BTINTEL_PCIE_BLZR_HWEXP_SIZE 1024
+#define BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR 0xB00A7C00
+
+#define BTINTEL_PCIE_SCP_HWEXP_SIZE 4096
+#define BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR 0xB030F800
+
+#define BTINTEL_PCIE_MAGIC_NUM 0xA5A5A5A5
+
+#define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER 0x17A2
+#define BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT 0x1E61
+
+#define BTINTEL_PCIE_RESET_WINDOW_SECS 5
+#define BTINTEL_PCIE_FLR_MAX_RETRY 1
+
+/* Alive interrupt context */
+enum {
+ BTINTEL_PCIE_ROM,
+ BTINTEL_PCIE_FW_DL,
+ BTINTEL_PCIE_HCI_RESET,
+ BTINTEL_PCIE_INTEL_HCI_RESET1,
+ BTINTEL_PCIE_INTEL_HCI_RESET2,
+ BTINTEL_PCIE_D0,
+ BTINTEL_PCIE_D3
+};
+
+/* Structure for dbgc fragment buffer
+ * @buf_addr_lsb: LSB of the buffer's physical address
+ * @buf_addr_msb: MSB of the buffer's physical address
+ * @buf_size: Total size of the buffer
+ */
+struct btintel_pcie_dbgc_ctxt_buf {
+ u32 buf_addr_lsb;
+ u32 buf_addr_msb;
+ u32 buf_size;
+};
+
+/* Structure for dbgc fragment
+ * @magic_num: 0XA5A5A5A5
+ * @ver: For Driver-FW compatibility
+ * @total_size: Total size of the payload debug info
+ * @num_buf: Num of allocated debug bufs
+ * @bufs: All buffer's addresses and sizes
+ */
+struct btintel_pcie_dbgc_ctxt {
+ u32 magic_num;
+ u32 ver;
+ u32 total_size;
+ u32 num_buf;
+ struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+};
+
+struct btintel_pcie_removal {
+ struct pci_dev *pdev;
+ struct work_struct work;
+};
+
+static LIST_HEAD(btintel_pcie_recovery_list);
+static DEFINE_SPINLOCK(btintel_pcie_recovery_lock);
+
+static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt)
+{
+ switch (alive_intr_ctxt) {
+ case BTINTEL_PCIE_ROM:
+ return "rom";
+ case BTINTEL_PCIE_FW_DL:
+ return "fw_dl";
+ case BTINTEL_PCIE_D0:
+ return "d0";
+ case BTINTEL_PCIE_D3:
+ return "d3";
+ case BTINTEL_PCIE_HCI_RESET:
+ return "hci_reset";
+ case BTINTEL_PCIE_INTEL_HCI_RESET1:
+ return "intel_reset1";
+ case BTINTEL_PCIE_INTEL_HCI_RESET2:
+ return "intel_reset2";
+ default:
+ return "unknown";
+ }
+}
+
+/* This function initializes the memory for DBGC buffers and formats the
+ * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
+ * size as the payload
+ */
+static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
+{
+ struct btintel_pcie_dbgc_ctxt db_frag;
+ struct data_buf *buf;
+ int i;
+
+ data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+ data->dbgc.bufs = devm_kcalloc(&data->pdev->dev, data->dbgc.count,
+ sizeof(*buf), GFP_KERNEL);
+ if (!data->dbgc.bufs)
+ return -ENOMEM;
+
+ data->dbgc.buf_v_addr = dmam_alloc_coherent(&data->pdev->dev,
+ data->dbgc.count *
+ BTINTEL_PCIE_DBGC_BUFFER_SIZE,
+ &data->dbgc.buf_p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!data->dbgc.buf_v_addr)
+ return -ENOMEM;
+
+ data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
+ sizeof(struct btintel_pcie_dbgc_ctxt),
+ &data->dbgc.frag_p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!data->dbgc.frag_v_addr)
+ return -ENOMEM;
+
+ data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
+
+ db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
+ db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
+ db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
+ db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
+
+ for (i = 0; i < data->dbgc.count; i++) {
+ buf = &data->dbgc.bufs[i];
+ buf->data_p_addr = data->dbgc.buf_p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ buf->data = data->dbgc.buf_v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ db_frag.bufs[i].buf_addr_lsb = lower_32_bits(buf->data_p_addr);
+ db_frag.bufs[i].buf_addr_msb = upper_32_bits(buf->data_p_addr);
+ db_frag.bufs[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ }
+
+ memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
+ return 0;
+}
+
+static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
+ u16 queue_num)
+{
+ bt_dev_dbg(hdev, "IA: %s: tr-h:%02u tr-t:%02u cr-h:%02u cr-t:%02u",
+ queue_num == BTINTEL_PCIE_TXQ_NUM ? "TXQ" : "RXQ",
+ ia->tr_hia[queue_num], ia->tr_tia[queue_num],
+ ia->cr_hia[queue_num], ia->cr_tia[queue_num]);
+}
+
+static inline void ipc_print_urbd1(struct hci_dev *hdev, struct urbd1 *urbd1,
+ u16 index)
+{
+ bt_dev_dbg(hdev, "RXQ:urbd1(%u) frbd_tag:%u status: 0x%x fixed:0x%x",
+ index, urbd1->frbd_tag, urbd1->status, urbd1->fixed);
+}
+
+static struct btintel_pcie_data *btintel_pcie_get_data(struct msix_entry *entry)
+{
+ u8 queue = entry->entry;
+ struct msix_entry *entries = entry - queue;
+
+ return container_of(entries, struct btintel_pcie_data, msix_entries[0]);
+}
+
+/* Set the doorbell for TXQ to notify the device that @index (actually index-1)
+ * of the TFD is updated and ready to transmit.
+ */
+static void btintel_pcie_set_tx_db(struct btintel_pcie_data *data, u16 index)
+{
+ u32 val;
+
+ val = index;
+ val |= (BTINTEL_PCIE_TX_DB_VEC << 16);
+
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR, val);
+}
+
+/* Copy the data to next(@tfd_index) data buffer and update the TFD(transfer
+ * descriptor) with the data length and the DMA address of the data buffer.
+ */
+static void btintel_pcie_prepare_tx(struct txq *txq, u16 tfd_index,
+ struct sk_buff *skb)
+{
+ struct data_buf *buf;
+ struct tfd *tfd;
+
+ tfd = &txq->tfds[tfd_index];
+ memset(tfd, 0, sizeof(*tfd));
+
+ buf = &txq->bufs[tfd_index];
+
+ tfd->size = skb->len;
+ tfd->addr = buf->data_p_addr;
+
+ /* Copy the outgoing data to DMA buffer */
+ memcpy(buf->data, skb->data, tfd->size);
+}
+
+static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+ u16 cr_hia, cr_tia;
+ u32 reg, mbox_reg;
+ struct sk_buff *skb;
+ u8 buf[80];
+
+ skb = alloc_skb(1024, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ snprintf(buf, sizeof(buf), "%s", "---- Dump of debug registers ---");
+ bt_dev_dbg(hdev, "%s", buf);
+ skb_put_data(skb, buf, strlen(buf));
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+ snprintf(buf, sizeof(buf), "boot stage: 0x%8.8x", reg);
+ bt_dev_dbg(hdev, "%s", buf);
+ skb_put_data(skb, buf, strlen(buf));
+ data->boot_stage_cache = reg;
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG);
+ snprintf(buf, sizeof(buf), "ipc status: 0x%8.8x", reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
+ snprintf(buf, sizeof(buf), "ipc control: 0x%8.8x", reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG);
+ snprintf(buf, sizeof(buf), "ipc sleep control: 0x%8.8x", reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+
+ /*Read the Mail box status and registers*/
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MBOX_STATUS_REG);
+ snprintf(buf, sizeof(buf), "mbox status: 0x%8.8x", reg);
+ skb_put_data(skb, buf, strlen(buf));
+ if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1) {
+ mbox_reg = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_1_REG);
+ snprintf(buf, sizeof(buf), "mbox_1: 0x%8.8x", mbox_reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+ }
+
+ if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2) {
+ mbox_reg = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_2_REG);
+ snprintf(buf, sizeof(buf), "mbox_2: 0x%8.8x", mbox_reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+ }
+
+ if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3) {
+ mbox_reg = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_3_REG);
+ snprintf(buf, sizeof(buf), "mbox_3: 0x%8.8x", mbox_reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+ }
+
+ if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4) {
+ mbox_reg = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_4_REG);
+ snprintf(buf, sizeof(buf), "mbox_4: 0x%8.8x", mbox_reg);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+ }
+
+ cr_hia = data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM];
+ cr_tia = data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
+ snprintf(buf, sizeof(buf), "rxq: cr_tia: %u cr_hia: %u", cr_tia, cr_hia);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+
+ cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
+ cr_tia = data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM];
+ snprintf(buf, sizeof(buf), "txq: cr_tia: %u cr_hia: %u", cr_tia, cr_hia);
+ skb_put_data(skb, buf, strlen(buf));
+ bt_dev_dbg(hdev, "%s", buf);
+ snprintf(buf, sizeof(buf), "--------------------------------");
+ bt_dev_dbg(hdev, "%s", buf);
+
+ hci_recv_diag(hdev, skb);
+}
+
+static int btintel_pcie_send_sync(struct btintel_pcie_data *data,
+ struct sk_buff *skb, u32 pkt_type, u16 opcode)
+{
+ int ret;
+ u16 tfd_index;
+ u32 old_ctxt;
+ bool wait_on_alive = false;
+ struct hci_dev *hdev = data->hdev;
+
+ struct txq *txq = &data->txq;
+
+ tfd_index = data->ia.tr_hia[BTINTEL_PCIE_TXQ_NUM];
+
+ if (tfd_index > txq->count)
+ return -ERANGE;
+
+ /* Firmware raises alive interrupt on HCI_OP_RESET or
+ * BTINTEL_HCI_OP_RESET
+ */
+ wait_on_alive = (pkt_type == BTINTEL_PCIE_HCI_CMD_PKT &&
+ (opcode == BTINTEL_HCI_OP_RESET || opcode == HCI_OP_RESET));
+
+ if (wait_on_alive) {
+ data->gp0_received = false;
+ old_ctxt = data->alive_intr_ctxt;
+ data->alive_intr_ctxt =
+ (opcode == BTINTEL_HCI_OP_RESET ? BTINTEL_PCIE_INTEL_HCI_RESET1 :
+ BTINTEL_PCIE_HCI_RESET);
+ bt_dev_dbg(data->hdev, "sending cmd: 0x%4.4x alive context changed: %s -> %s",
+ opcode, btintel_pcie_alivectxt_state2str(old_ctxt),
+ btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
+ }
+
+ memcpy(skb_push(skb, BTINTEL_PCIE_HCI_TYPE_LEN), &pkt_type,
+ BTINTEL_PCIE_HCI_TYPE_LEN);
+
+ /* Prepare for TX. It updates the TFD with the length of data and
+ * address of the DMA buffer, and copy the data to the DMA buffer
+ */
+ btintel_pcie_prepare_tx(txq, tfd_index, skb);
+
+ tfd_index = (tfd_index + 1) % txq->count;
+ data->ia.tr_hia[BTINTEL_PCIE_TXQ_NUM] = tfd_index;
+
+ /* Arm wait event condition */
+ data->tx_wait_done = false;
+
+ /* Set the doorbell to notify the device */
+ btintel_pcie_set_tx_db(data, tfd_index);
+
+ /* Wait for the complete interrupt - URBD0 */
+ ret = wait_event_timeout(data->tx_wait_q, data->tx_wait_done,
+ msecs_to_jiffies(BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS));
+ if (!ret) {
+ bt_dev_err(data->hdev, "Timeout (%u ms) on tx completion",
+ BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS);
+ btintel_pcie_dump_debug_registers(data->hdev);
+ return -ETIME;
+ }
+
+ if (wait_on_alive) {
+ ret = wait_event_timeout(data->gp0_wait_q,
+ data->gp0_received,
+ msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
+ if (!ret) {
+ hdev->stat.err_tx++;
+ bt_dev_err(hdev, "Timeout (%u ms) on alive interrupt, alive context: %s",
+ BTINTEL_DEFAULT_INTR_TIMEOUT_MS,
+ btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
+ return -ETIME;
+ }
+ }
+ return 0;
+}
+
+/* Set the doorbell for RXQ to notify the device that @index (actually index-1)
+ * is available to receive the data
+ */
+static void btintel_pcie_set_rx_db(struct btintel_pcie_data *data, u16 index)
+{
+ u32 val;
+
+ val = index;
+ val |= (BTINTEL_PCIE_RX_DB_VEC << 16);
+
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR, val);
+}
+
+/* Update the FRBD (free buffer descriptor) with the @frbd_index and the
+ * DMA address of the free buffer.
+ */
+static void btintel_pcie_prepare_rx(struct rxq *rxq, u16 frbd_index)
+{
+ struct data_buf *buf;
+ struct frbd *frbd;
+
+ /* Get the buffer of the FRBD for DMA */
+ buf = &rxq->bufs[frbd_index];
+
+ frbd = &rxq->frbds[frbd_index];
+ memset(frbd, 0, sizeof(*frbd));
+
+ /* Update FRBD */
+ frbd->tag = frbd_index;
+ frbd->addr = buf->data_p_addr;
+}
+
+static int btintel_pcie_submit_rx(struct btintel_pcie_data *data)
+{
+ u16 frbd_index;
+ struct rxq *rxq = &data->rxq;
+
+ frbd_index = data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM];
+
+ if (frbd_index > rxq->count)
+ return -ERANGE;
+
+ /* Prepare for RX submit. It updates the FRBD with the address of DMA
+ * buffer
+ */
+ btintel_pcie_prepare_rx(rxq, frbd_index);
+
+ frbd_index = (frbd_index + 1) % rxq->count;
+ data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM] = frbd_index;
+ ipc_print_ia_ring(data->hdev, &data->ia, BTINTEL_PCIE_RXQ_NUM);
+
+ /* Set the doorbell to notify the device */
+ btintel_pcie_set_rx_db(data, frbd_index);
+
+ return 0;
+}
+
+static int btintel_pcie_start_rx(struct btintel_pcie_data *data)
+{
+ int i, ret;
+ struct rxq *rxq = &data->rxq;
+
+ /* Post (BTINTEL_PCIE_RX_DESCS_COUNT - 3) buffers to overcome the
+ * hardware issues leading to race condition at the firmware.
+ */
+
+ for (i = 0; i < rxq->count - 3; i++) {
+ ret = btintel_pcie_submit_rx(data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void btintel_pcie_reset_ia(struct btintel_pcie_data *data)
+{
+ memset(data->ia.tr_hia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
+ memset(data->ia.tr_tia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
+ memset(data->ia.cr_hia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
+ memset(data->ia.cr_tia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
+}
+
+static int btintel_pcie_reset_bt(struct btintel_pcie_data *data)
+{
+ u32 reg;
+ int retry = 3;
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+ reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
+ reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON;
+
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+
+ do {
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS)
+ break;
+ usleep_range(10000, 12000);
+
+ } while (--retry > 0);
+ usleep_range(10000, 12000);
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+ reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
+ reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET;
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+ usleep_range(10000, 12000);
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ bt_dev_dbg(data->hdev, "csr register after reset: 0x%8.8x", reg);
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+
+ /* If shared hardware reset is success then boot stage register shall be
+ * set to 0
+ */
+ return reg == 0 ? 0 : -ENODEV;
+}
+
+static void btintel_pcie_mac_init(struct btintel_pcie_data *data)
+{
+ u32 reg;
+
+ /* Set MAC_INIT bit to start primary bootloader */
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET);
+ reg |= (BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+}
+
+static int btintel_pcie_get_mac_access(struct btintel_pcie_data *data)
+{
+ u32 reg;
+ int retry = 15;
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+ reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS;
+ reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ;
+ if ((reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS) == 0)
+ reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ;
+
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+
+ do {
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS)
+ return 0;
+ /* Need delay here for Target Access harwdware to settle down*/
+ usleep_range(1000, 1200);
+
+ } while (--retry > 0);
+
+ return -ETIME;
+}
+
+static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data)
+{
+ u32 reg;
+
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ)
+ reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ;
+
+ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS)
+ reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS;
+
+ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ)
+ reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ;
+
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+}
+
+static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
+ void *data, size_t size)
+{
+ struct intel_tlv *tlv;
+
+ tlv = dest;
+ tlv->type = type;
+ tlv->len = size;
+ memcpy(tlv->val, data, tlv->len);
+ return dest + sizeof(*tlv) + size;
+}
+
+static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
+{
+ u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
+ struct btintel_pcie_dbgc *dbgc = &data->dbgc;
+ struct hci_dev *hdev = data->hdev;
+ u8 *pdata, *p, buf_idx;
+ struct intel_tlv *tlv;
+ struct timespec64 now;
+ struct tm tm_now;
+ char fw_build[128];
+ char ts[128];
+ char vendor[64];
+ char driver[64];
+
+ if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
+ return -EOPNOTSUPP;
+
+
+ wr_ptr_status = btintel_pcie_rd_dev_mem(data, BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS);
+ offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
+
+ buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
+ if (buf_idx > dbgc->count) {
+ bt_dev_warn(hdev, "Buffer index is invalid");
+ return -EINVAL;
+ }
+
+ prev_size = buf_idx * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ if (prev_size + offset >= prev_size)
+ data->dmp_hdr.write_ptr = prev_size + offset;
+ else
+ return -EINVAL;
+
+ snprintf(vendor, sizeof(vendor), "Vendor: Intel\n");
+ snprintf(driver, sizeof(driver), "Driver: %s\n",
+ data->dmp_hdr.driver_name);
+
+ ktime_get_real_ts64(&now);
+ time64_to_tm(now.tv_sec, 0, &tm_now);
+ snprintf(ts, sizeof(ts), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
+ tm_now.tm_mday, tm_now.tm_mon + 1, tm_now.tm_year + 1900,
+ tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
+
+ snprintf(fw_build, sizeof(fw_build),
+ "Firmware Timestamp: Year %u WW %02u buildtype %u build %u",
+ 2000 + (data->dmp_hdr.fw_timestamp >> 8),
+ data->dmp_hdr.fw_timestamp & 0xff, data->dmp_hdr.fw_build_type,
+ data->dmp_hdr.fw_build_num);
+
+ data_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
+ sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
+ sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
+ sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
+ sizeof(*tlv) + sizeof(data->dmp_hdr.fw_git_sha1) +
+ sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
+ sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
+ sizeof(*tlv) + strlen(ts) +
+ sizeof(*tlv) + strlen(fw_build) +
+ sizeof(*tlv) + strlen(vendor) +
+ sizeof(*tlv) + strlen(driver);
+
+ /*
+ * sizeof(u32) - signature
+ * sizeof(data_len) - to store tlv data size
+ * data_len - TLV data
+ */
+ dump_size = sizeof(u32) + sizeof(data_len) + data_len;
+
+
+ /* Add debug buffers data length to dump size */
+ dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
+
+ pdata = vmalloc(dump_size);
+ if (!pdata)
+ return -ENOMEM;
+ p = pdata;
+
+ *(u32 *)p = BTINTEL_PCIE_MAGIC_NUM;
+ p += sizeof(u32);
+
+ *(u32 *)p = data_len;
+ p += sizeof(u32);
+
+
+ p = btintel_pcie_copy_tlv(p, BTINTEL_VENDOR, vendor, strlen(vendor));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_DRIVER, driver, strlen(driver));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_DUMP_TIME, ts, strlen(ts));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_FW_BUILD, fw_build,
+ strlen(fw_build));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
+ sizeof(data->dmp_hdr.cnvi_bt));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
+ sizeof(data->dmp_hdr.write_ptr));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
+ sizeof(data->dmp_hdr.wrap_ctr));
+
+ data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data,
+ BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND);
+
+ p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
+ sizeof(data->dmp_hdr.trigger_reason));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1,
+ sizeof(data->dmp_hdr.fw_git_sha1));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
+ sizeof(data->dmp_hdr.cnvr_top));
+ p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
+ sizeof(data->dmp_hdr.cnvi_top));
+
+ memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+ dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
+ return 0;
+}
+
+static void btintel_pcie_dump_traces(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+ int ret = 0;
+
+ ret = btintel_pcie_get_mac_access(data);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to get mac access: (%d)", ret);
+ return;
+ }
+
+ ret = btintel_pcie_read_dram_buffers(data);
+
+ btintel_pcie_release_mac_access(data);
+
+ if (ret)
+ bt_dev_err(hdev, "Failed to dump traces: (%d)", ret);
+}
+
+/* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in
+ * BTINTEL_PCIE_CSR_FUNC_CTRL_REG register and wait for MSI-X with
+ * BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0.
+ * Then the host reads firmware version from BTINTEL_CSR_F2D_MBX and the boot stage
+ * from BTINTEL_PCIE_CSR_BOOT_STAGE_REG.
+ */
+static int btintel_pcie_enable_bt(struct btintel_pcie_data *data)
+{
+ int err;
+ u32 reg;
+
+ data->gp0_received = false;
+
+ /* Update the DMA address of CI struct to CSR */
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_CI_ADDR_LSB_REG,
+ data->ci_p_addr & 0xffffffff);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_CI_ADDR_MSB_REG,
+ (u64)data->ci_p_addr >> 32);
+
+ /* Reset the cached value of boot stage. it is updated by the MSI-X
+ * gp0 interrupt handler.
+ */
+ data->boot_stage_cache = 0x0;
+
+ /* Set MAC_INIT bit to start primary bootloader */
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+ reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET);
+ reg |= (BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+ BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT);
+
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+
+ /* MAC is ready. Enable BT FUNC */
+ btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG,
+ BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
+
+ btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+ /* wait for interrupt from the device after booting up to primary
+ * bootloader.
+ */
+ data->alive_intr_ctxt = BTINTEL_PCIE_ROM;
+ err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
+ msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
+ if (!err)
+ return -ETIME;
+
+ /* Check cached boot stage is BTINTEL_PCIE_CSR_BOOT_STAGE_ROM(BIT(0)) */
+ if (~data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ROM)
+ return -ENODEV;
+
+ return 0;
+}
+
+static inline bool btintel_pcie_in_op(struct btintel_pcie_data *data)
+{
+ return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW;
+}
+
+static inline bool btintel_pcie_in_iml(struct btintel_pcie_data *data)
+{
+ return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_IML &&
+ !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW);
+}
+
+static inline bool btintel_pcie_in_d3(struct btintel_pcie_data *data)
+{
+ return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY;
+}
+
+static inline bool btintel_pcie_in_d0(struct btintel_pcie_data *data)
+{
+ return !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY);
+}
+
+static inline bool btintel_pcie_in_device_halt(struct btintel_pcie_data *data)
+{
+ return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED;
+}
+
+static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data,
+ u32 dxstate)
+{
+ bt_dev_dbg(data->hdev, "writing sleep_ctl_reg: 0x%8.8x", dxstate);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG, dxstate);
+}
+
+static int btintel_pcie_read_device_mem(struct btintel_pcie_data *data,
+ void *buf, u32 dev_addr, int len)
+{
+ int err;
+ u32 *val = buf;
+
+ /* Get device mac access */
+ err = btintel_pcie_get_mac_access(data);
+ if (err) {
+ bt_dev_err(data->hdev, "Failed to get mac access %d", err);
+ return err;
+ }
+
+ for (; len > 0; len -= 4, dev_addr += 4, val++)
+ *val = btintel_pcie_rd_dev_mem(data, dev_addr);
+
+ btintel_pcie_release_mac_access(data);
+
+ return 0;
+}
+
+static inline bool btintel_pcie_in_lockdown(struct btintel_pcie_data *data)
+{
+ return (data->boot_stage_cache &
+ BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN) ||
+ (data->boot_stage_cache &
+ BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN);
+}
+
+static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data)
+{
+ return (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR) ||
+ (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER);
+}
+
+static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
+{
+ bt_dev_err(data->hdev, "Received gp1 mailbox interrupt");
+ btintel_pcie_dump_debug_registers(data->hdev);
+}
+
+/* This function handles the MSI-X interrupt for gp0 cause (bit 0 in
+ * BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES) which is sent for boot stage and image response.
+ */
+static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
+{
+ bool submit_rx, signal_waitq;
+ u32 reg, old_ctxt;
+
+ /* This interrupt is for three different causes and it is not easy to
+ * know what causes the interrupt. So, it compares each register value
+ * with cached value and update it before it wake up the queue.
+ */
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+ if (reg != data->boot_stage_cache)
+ data->boot_stage_cache = reg;
+
+ bt_dev_dbg(data->hdev, "Alive context: %s old_boot_stage: 0x%8.8x new_boot_stage: 0x%8.8x",
+ btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt),
+ data->boot_stage_cache, reg);
+ reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IMG_RESPONSE_REG);
+ if (reg != data->img_resp_cache)
+ data->img_resp_cache = reg;
+
+ if (btintel_pcie_in_error(data)) {
+ bt_dev_err(data->hdev, "Controller in error state");
+ btintel_pcie_dump_debug_registers(data->hdev);
+ return;
+ }
+
+ if (btintel_pcie_in_lockdown(data)) {
+ bt_dev_err(data->hdev, "Controller in lockdown state");
+ btintel_pcie_dump_debug_registers(data->hdev);
+ return;
+ }
+
+ data->gp0_received = true;
+
+ old_ctxt = data->alive_intr_ctxt;
+ submit_rx = false;
+ signal_waitq = false;
+
+ switch (data->alive_intr_ctxt) {
+ case BTINTEL_PCIE_ROM:
+ data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL;
+ signal_waitq = true;
+ break;
+ case BTINTEL_PCIE_FW_DL:
+ /* Error case is already handled. Ideally control shall not
+ * reach here
+ */
+ break;
+ case BTINTEL_PCIE_INTEL_HCI_RESET1:
+ if (btintel_pcie_in_op(data)) {
+ submit_rx = true;
+ signal_waitq = true;
+ break;
+ }
+
+ if (btintel_pcie_in_iml(data)) {
+ submit_rx = true;
+ signal_waitq = true;
+ data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL;
+ break;
+ }
+ break;
+ case BTINTEL_PCIE_INTEL_HCI_RESET2:
+ if (btintel_test_and_clear_flag(data->hdev, INTEL_WAIT_FOR_D0)) {
+ btintel_wake_up_flag(data->hdev, INTEL_WAIT_FOR_D0);
+ data->alive_intr_ctxt = BTINTEL_PCIE_D0;
+ }
+ break;
+ case BTINTEL_PCIE_D0:
+ if (btintel_pcie_in_d3(data)) {
+ data->alive_intr_ctxt = BTINTEL_PCIE_D3;
+ signal_waitq = true;
+ break;
+ }
+ break;
+ case BTINTEL_PCIE_D3:
+ if (btintel_pcie_in_d0(data)) {
+ data->alive_intr_ctxt = BTINTEL_PCIE_D0;
+ submit_rx = true;
+ signal_waitq = true;
+ break;
+ }
+ break;
+ case BTINTEL_PCIE_HCI_RESET:
+ data->alive_intr_ctxt = BTINTEL_PCIE_D0;
+ submit_rx = true;
+ signal_waitq = true;
+ break;
+ default:
+ bt_dev_err(data->hdev, "Unknown state: 0x%2.2x",
+ data->alive_intr_ctxt);
+ break;
+ }
+
+ if (submit_rx) {
+ btintel_pcie_reset_ia(data);
+ btintel_pcie_start_rx(data);
+ }
+
+ if (signal_waitq) {
+ bt_dev_dbg(data->hdev, "wake up gp0 wait_q");
+ wake_up(&data->gp0_wait_q);
+ }
+
+ if (old_ctxt != data->alive_intr_ctxt)
+ bt_dev_dbg(data->hdev, "alive context changed: %s -> %s",
+ btintel_pcie_alivectxt_state2str(old_ctxt),
+ btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
+}
+
+/* This function handles the MSX-X interrupt for rx queue 0 which is for TX
+ */
+static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
+{
+ u16 cr_tia, cr_hia;
+ struct txq *txq;
+ struct urbd0 *urbd0;
+
+ cr_tia = data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM];
+ cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
+
+ if (cr_tia == cr_hia)
+ return;
+
+ txq = &data->txq;
+
+ while (cr_tia != cr_hia) {
+ data->tx_wait_done = true;
+ wake_up(&data->tx_wait_q);
+
+ urbd0 = &txq->urbd0s[cr_tia];
+
+ if (urbd0->tfd_index > txq->count)
+ return;
+
+ cr_tia = (cr_tia + 1) % txq->count;
+ data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] = cr_tia;
+ ipc_print_ia_ring(data->hdev, &data->ia, BTINTEL_PCIE_TXQ_NUM);
+ }
+}
+
+static int btintel_pcie_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *)skb->data;
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+ if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+ hdr->plen > 0) {
+ const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
+ unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
+
+ if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
+ switch (skb->data[2]) {
+ case 0x02:
+ /* When switching to the operational firmware
+ * the device sends a vendor specific event
+ * indicating that the bootup completed.
+ */
+ btintel_bootup(hdev, ptr, len);
+
+ /* If bootup event is from operational image,
+ * driver needs to write sleep control register to
+ * move into D0 state
+ */
+ if (btintel_pcie_in_op(data)) {
+ btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
+ data->alive_intr_ctxt = BTINTEL_PCIE_INTEL_HCI_RESET2;
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (btintel_pcie_in_iml(data)) {
+ /* In case of IML, there is no concept
+ * of D0 transition. Just mimic as if
+ * IML moved to D0 by clearing INTEL_WAIT_FOR_D0
+ * bit and waking up the task waiting on
+ * INTEL_WAIT_FOR_D0. This is required
+ * as intel_boot() is common function for
+ * both IML and OP image loading.
+ */
+ if (btintel_test_and_clear_flag(data->hdev,
+ INTEL_WAIT_FOR_D0))
+ btintel_wake_up_flag(data->hdev,
+ INTEL_WAIT_FOR_D0);
+ }
+ kfree_skb(skb);
+ return 0;
+ case 0x06:
+ /* When the firmware loading completes the
+ * device sends out a vendor specific event
+ * indicating the result of the firmware
+ * loading.
+ */
+ btintel_secure_send_result(hdev, ptr, len);
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ /* This is a debug event that comes from IML and OP image when it
+ * starts execution. There is no need pass this event to stack.
+ */
+ if (skb->data[2] == 0x97) {
+ hci_recv_diag(hdev, skb);
+ return 0;
+ }
+ }
+
+ return hci_recv_frame(hdev, skb);
+}
+/* Process the received rx data
+ * It check the frame header to identify the data type and create skb
+ * and calling HCI API
+ */
+static int btintel_pcie_recv_frame(struct btintel_pcie_data *data,
+ struct sk_buff *skb)
+{
+ int ret;
+ u8 pkt_type;
+ u16 plen;
+ u32 pcie_pkt_type;
+ void *pdata;
+ struct hci_dev *hdev = data->hdev;
+
+ spin_lock(&data->hci_rx_lock);
+
+ /* The first 4 bytes indicates the Intel PCIe specific packet type */
+ pdata = skb_pull_data(skb, BTINTEL_PCIE_HCI_TYPE_LEN);
+ if (!pdata) {
+ bt_dev_err(hdev, "Corrupted packet received");
+ ret = -EILSEQ;
+ goto exit_error;
+ }
+
+ pcie_pkt_type = get_unaligned_le32(pdata);
+
+ switch (pcie_pkt_type) {
+ case BTINTEL_PCIE_HCI_ACL_PKT:
+ if (skb->len >= HCI_ACL_HDR_SIZE) {
+ plen = HCI_ACL_HDR_SIZE + __le16_to_cpu(hci_acl_hdr(skb)->dlen);
+ pkt_type = HCI_ACLDATA_PKT;
+ } else {
+ bt_dev_err(hdev, "ACL packet is too short");
+ ret = -EILSEQ;
+ goto exit_error;
+ }
+ break;
+
+ case BTINTEL_PCIE_HCI_SCO_PKT:
+ if (skb->len >= HCI_SCO_HDR_SIZE) {
+ plen = HCI_SCO_HDR_SIZE + hci_sco_hdr(skb)->dlen;
+ pkt_type = HCI_SCODATA_PKT;
+ } else {
+ bt_dev_err(hdev, "SCO packet is too short");
+ ret = -EILSEQ;
+ goto exit_error;
+ }
+ break;
+
+ case BTINTEL_PCIE_HCI_EVT_PKT:
+ if (skb->len >= HCI_EVENT_HDR_SIZE) {
+ plen = HCI_EVENT_HDR_SIZE + hci_event_hdr(skb)->plen;
+ pkt_type = HCI_EVENT_PKT;
+ } else {
+ bt_dev_err(hdev, "Event packet is too short");
+ ret = -EILSEQ;
+ goto exit_error;
+ }
+ break;
+
+ case BTINTEL_PCIE_HCI_ISO_PKT:
+ if (skb->len >= HCI_ISO_HDR_SIZE) {
+ plen = HCI_ISO_HDR_SIZE + __le16_to_cpu(hci_iso_hdr(skb)->dlen);
+ pkt_type = HCI_ISODATA_PKT;
+ } else {
+ bt_dev_err(hdev, "ISO packet is too short");
+ ret = -EILSEQ;
+ goto exit_error;
+ }
+ break;
+
+ default:
+ bt_dev_err(hdev, "Invalid packet type received: 0x%4.4x",
+ pcie_pkt_type);
+ ret = -EINVAL;
+ goto exit_error;
+ }
+
+ if (skb->len < plen) {
+ bt_dev_err(hdev, "Received corrupted packet. type: 0x%2.2x",
+ pkt_type);
+ ret = -EILSEQ;
+ goto exit_error;
+ }
+
+ bt_dev_dbg(hdev, "pkt_type: 0x%2.2x len: %u", pkt_type, plen);
+
+ hci_skb_pkt_type(skb) = pkt_type;
+ hdev->stat.byte_rx += plen;
+ skb_trim(skb, plen);
+
+ if (pcie_pkt_type == BTINTEL_PCIE_HCI_EVT_PKT)
+ ret = btintel_pcie_recv_event(hdev, skb);
+ else
+ ret = hci_recv_frame(hdev, skb);
+ skb = NULL; /* skb is freed in the callee */
+
+exit_error:
+ if (skb)
+ kfree_skb(skb);
+
+ if (ret)
+ hdev->stat.err_rx++;
+
+ spin_unlock(&data->hci_rx_lock);
+
+ return ret;
+}
+
+static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data)
+{
+ int len, err, offset, pending;
+ struct sk_buff *skb;
+ u8 *buf, prefix[64];
+ u32 addr, val;
+ u16 pkt_len;
+
+ struct tlv {
+ u8 type;
+ __le16 len;
+ u8 val[];
+ } __packed;
+
+ struct tlv *tlv;
+
+ switch (data->dmp_hdr.cnvi_top & 0xfff) {
+ case BTINTEL_CNVI_BLAZARI:
+ case BTINTEL_CNVI_BLAZARIW:
+ /* only from step B0 onwards */
+ if (INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top) != 0x01)
+ return;
+ len = BTINTEL_PCIE_BLZR_HWEXP_SIZE; /* exception data length */
+ addr = BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR;
+ break;
+ case BTINTEL_CNVI_SCP:
+ len = BTINTEL_PCIE_SCP_HWEXP_SIZE;
+ addr = BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR;
+ break;
+ default:
+ bt_dev_err(data->hdev, "Unsupported cnvi 0x%8.8x", data->dmp_hdr.cnvi_top);
+ return;
+ }
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ goto exit_on_error;
+
+ btintel_pcie_mac_init(data);
+
+ err = btintel_pcie_read_device_mem(data, buf, addr, len);
+ if (err)
+ goto exit_on_error;
+
+ val = get_unaligned_le32(buf);
+ if (val != BTINTEL_PCIE_MAGIC_NUM) {
+ bt_dev_err(data->hdev, "Invalid exception dump signature: 0x%8.8x",
+ val);
+ goto exit_on_error;
+ }
+
+ snprintf(prefix, sizeof(prefix), "Bluetooth: %s: ", bt_dev_name(data->hdev));
+
+ offset = 4;
+ do {
+ pending = len - offset;
+ if (pending < sizeof(*tlv))
+ break;
+ tlv = (struct tlv *)(buf + offset);
+
+ /* If type == 0, then there are no more TLVs to be parsed */
+ if (!tlv->type) {
+ bt_dev_dbg(data->hdev, "Invalid TLV type 0");
+ break;
+ }
+ pkt_len = le16_to_cpu(tlv->len);
+ offset += sizeof(*tlv);
+ pending = len - offset;
+ if (pkt_len > pending)
+ break;
+
+ offset += pkt_len;
+
+ /* Only TLVs of type == 1 are HCI events, no need to process other
+ * TLVs
+ */
+ if (tlv->type != 1)
+ continue;
+
+ bt_dev_dbg(data->hdev, "TLV packet length: %u", pkt_len);
+ if (pkt_len > HCI_MAX_EVENT_SIZE)
+ break;
+ skb = bt_skb_alloc(pkt_len, GFP_KERNEL);
+ if (!skb)
+ goto exit_on_error;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ skb_put_data(skb, tlv->val, pkt_len);
+
+ /* copy Intel specific pcie packet type */
+ val = BTINTEL_PCIE_HCI_EVT_PKT;
+ memcpy(skb_push(skb, BTINTEL_PCIE_HCI_TYPE_LEN), &val,
+ BTINTEL_PCIE_HCI_TYPE_LEN);
+
+ print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_OFFSET, 16, 1,
+ tlv->val, pkt_len, false);
+
+ btintel_pcie_recv_frame(data, skb);
+ } while (offset < len);
+
+exit_on_error:
+ kfree(buf);
+}
+
+static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
+{
+ bt_dev_err(data->hdev, "Received hw exception interrupt");
+
+ if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
+ return;
+
+ if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags))
+ return;
+
+ /* Trigger device core dump when there is HW exception */
+ if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
+ data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
+
+ queue_work(data->workqueue, &data->rx_work);
+}
+
+static void btintel_pcie_rx_work(struct work_struct *work)
+{
+ struct btintel_pcie_data *data = container_of(work,
+ struct btintel_pcie_data, rx_work);
+ struct sk_buff *skb;
+
+ if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
+ btintel_pcie_dump_traces(data->hdev);
+ clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
+ }
+
+ if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) {
+ /* Unlike usb products, controller will not send hardware
+ * exception event on exception. Instead controller writes the
+ * hardware event to device memory along with optional debug
+ * events, raises MSIX and halts. Driver shall read the
+ * exception event from device memory and passes it stack for
+ * further processing.
+ */
+ btintel_pcie_read_hwexp(data);
+ clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
+ }
+
+ /* Process the sk_buf in queue and send to the HCI layer */
+ while ((skb = skb_dequeue(&data->rx_skb_q))) {
+ btintel_pcie_recv_frame(data, skb);
+ }
+}
+
+/* create sk_buff with data and save it to queue and start RX work */
+static int btintel_pcie_submit_rx_work(struct btintel_pcie_data *data, u8 status,
+ void *buf)
+{
+ int ret, len;
+ struct rfh_hdr *rfh_hdr;
+ struct sk_buff *skb;
+
+ rfh_hdr = buf;
+
+ len = rfh_hdr->packet_len;
+ if (len <= 0) {
+ ret = -EINVAL;
+ goto resubmit;
+ }
+
+ /* Remove RFH header */
+ buf += sizeof(*rfh_hdr);
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb)
+ goto resubmit;
+
+ skb_put_data(skb, buf, len);
+ skb_queue_tail(&data->rx_skb_q, skb);
+ queue_work(data->workqueue, &data->rx_work);
+
+resubmit:
+ ret = btintel_pcie_submit_rx(data);
+
+ return ret;
+}
+
+/* Handles the MSI-X interrupt for rx queue 1 which is for RX */
+static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data)
+{
+ u16 cr_hia, cr_tia;
+ struct rxq *rxq;
+ struct urbd1 *urbd1;
+ struct data_buf *buf;
+ int ret;
+ struct hci_dev *hdev = data->hdev;
+
+ cr_hia = data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM];
+ cr_tia = data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
+
+ bt_dev_dbg(hdev, "RXQ: cr_hia: %u cr_tia: %u", cr_hia, cr_tia);
+
+ /* Check CR_TIA and CR_HIA for change */
+ if (cr_tia == cr_hia)
+ return;
+
+ rxq = &data->rxq;
+
+ /* The firmware sends multiple CD in a single MSI-X and it needs to
+ * process all received CDs in this interrupt.
+ */
+ while (cr_tia != cr_hia) {
+ urbd1 = &rxq->urbd1s[cr_tia];
+ ipc_print_urbd1(data->hdev, urbd1, cr_tia);
+
+ buf = &rxq->bufs[urbd1->frbd_tag];
+ if (!buf) {
+ bt_dev_err(hdev, "RXQ: failed to get the DMA buffer for %d",
+ urbd1->frbd_tag);
+ return;
+ }
+
+ ret = btintel_pcie_submit_rx_work(data, urbd1->status,
+ buf->data);
+ if (ret) {
+ bt_dev_err(hdev, "RXQ: failed to submit rx request");
+ return;
+ }
+
+ cr_tia = (cr_tia + 1) % rxq->count;
+ data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM] = cr_tia;
+ ipc_print_ia_ring(data->hdev, &data->ia, BTINTEL_PCIE_RXQ_NUM);
+ }
+}
+
+static irqreturn_t btintel_pcie_msix_isr(int irq, void *data)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+static inline bool btintel_pcie_is_rxq_empty(struct btintel_pcie_data *data)
+{
+ return data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM] == data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
+}
+
+static inline bool btintel_pcie_is_txackq_empty(struct btintel_pcie_data *data)
+{
+ return data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] == data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
+}
+
+static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
+{
+ struct msix_entry *entry = dev_id;
+ struct btintel_pcie_data *data = btintel_pcie_get_data(entry);
+ u32 intr_fh, intr_hw;
+
+ spin_lock(&data->irq_lock);
+ intr_fh = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES);
+ intr_hw = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES);
+
+ /* Clear causes registers to avoid being handling the same cause */
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES, intr_fh);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, intr_hw);
+ spin_unlock(&data->irq_lock);
+
+ if (unlikely(!(intr_fh | intr_hw))) {
+ /* Ignore interrupt, inta == 0 */
+ return IRQ_NONE;
+ }
+
+ /* This interrupt is raised when there is an hardware exception */
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP)
+ btintel_pcie_msix_hw_exp_handler(data);
+
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1)
+ btintel_pcie_msix_gp1_handler(data);
+
+
+ /* For TX */
+ if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0) {
+ btintel_pcie_msix_tx_handle(data);
+ if (!btintel_pcie_is_rxq_empty(data))
+ btintel_pcie_msix_rx_handle(data);
+ }
+
+ /* For RX */
+ if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1) {
+ btintel_pcie_msix_rx_handle(data);
+ if (!btintel_pcie_is_txackq_empty(data))
+ btintel_pcie_msix_tx_handle(data);
+ }
+
+ /* This interrupt is triggered by the firmware after updating
+ * boot_stage register and image_response register
+ */
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0)
+ btintel_pcie_msix_gp0_handler(data);
+
+ /*
+ * Before sending the interrupt the HW disables it to prevent a nested
+ * interrupt. This is done by writing 1 to the corresponding bit in
+ * the mask register. After handling the interrupt, it should be
+ * re-enabled by clearing this bit. This register is defined as write 1
+ * clear (W1C) register, meaning that it's cleared by writing 1
+ * to the bit.
+ */
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST,
+ BIT(entry->entry));
+
+ return IRQ_HANDLED;
+}
+
+/* This function requests the irq for MSI-X and registers the handlers per irq.
+ * Currently, it requests only 1 irq for all interrupt causes.
+ */
+static int btintel_pcie_setup_irq(struct btintel_pcie_data *data)
+{
+ int err;
+ int num_irqs, i;
+
+ for (i = 0; i < BTINTEL_PCIE_MSIX_VEC_MAX; i++)
+ data->msix_entries[i].entry = i;
+
+ num_irqs = pci_alloc_irq_vectors(data->pdev, BTINTEL_PCIE_MSIX_VEC_MIN,
+ BTINTEL_PCIE_MSIX_VEC_MAX, PCI_IRQ_MSIX);
+ if (num_irqs < 0)
+ return num_irqs;
+
+ data->alloc_vecs = num_irqs;
+ data->msix_enabled = 1;
+ data->def_irq = 0;
+
+ /* setup irq handler */
+ for (i = 0; i < data->alloc_vecs; i++) {
+ struct msix_entry *msix_entry;
+
+ msix_entry = &data->msix_entries[i];
+ msix_entry->vector = pci_irq_vector(data->pdev, i);
+
+ err = devm_request_threaded_irq(&data->pdev->dev,
+ msix_entry->vector,
+ btintel_pcie_msix_isr,
+ btintel_pcie_irq_msix_handler,
+ IRQF_SHARED,
+ KBUILD_MODNAME,
+ msix_entry);
+ if (err) {
+ pci_free_irq_vectors(data->pdev);
+ data->alloc_vecs = 0;
+ return err;
+ }
+ }
+ return 0;
+}
+
+struct btintel_pcie_causes_list {
+ u32 cause;
+ u32 mask_reg;
+ u8 cause_num;
+};
+
+static struct btintel_pcie_causes_list causes_list[] = {
+ { BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x00 },
+ { BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 },
+ { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 },
+};
+
+/* This function configures the interrupt masks for both HW_INT_CAUSES and
+ * FH_INT_CAUSES which are meaningful to us.
+ *
+ * After resetting BT function via PCIE FLR or FUNC_CTRL reset, the driver
+ * need to call this function again to configure since the masks
+ * are reset to 0xFFFFFFFF after reset.
+ */
+static void btintel_pcie_config_msix(struct btintel_pcie_data *data)
+{
+ int i;
+ int val = data->def_irq | BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE;
+
+ /* Set Non Auto Clear Cause */
+ for (i = 0; i < ARRAY_SIZE(causes_list); i++) {
+ btintel_pcie_wr_reg8(data,
+ BTINTEL_PCIE_CSR_MSIX_IVAR(causes_list[i].cause_num),
+ val);
+ btintel_pcie_clr_reg_bits(data,
+ causes_list[i].mask_reg,
+ causes_list[i].cause);
+ }
+
+ /* Save the initial interrupt mask */
+ data->fh_init_mask = ~btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK);
+ data->hw_init_mask = ~btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK);
+}
+
+static int btintel_pcie_config_pcie(struct pci_dev *pdev,
+ struct btintel_pcie_data *data)
+{
+ int err;
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ pci_set_master(pdev);
+
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err) {
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+ }
+
+ data->base_addr = pcim_iomap_region(pdev, 0, KBUILD_MODNAME);
+ if (IS_ERR(data->base_addr))
+ return PTR_ERR(data->base_addr);
+
+ err = btintel_pcie_setup_irq(data);
+ if (err)
+ return err;
+
+ /* Configure MSI-X with causes list */
+ btintel_pcie_config_msix(data);
+
+ return 0;
+}
+
+static void btintel_pcie_init_ci(struct btintel_pcie_data *data,
+ struct ctx_info *ci)
+{
+ ci->version = 0x1;
+ ci->size = sizeof(*ci);
+ ci->config = 0x0000;
+ ci->addr_cr_hia = data->ia.cr_hia_p_addr;
+ ci->addr_tr_tia = data->ia.tr_tia_p_addr;
+ ci->addr_cr_tia = data->ia.cr_tia_p_addr;
+ ci->addr_tr_hia = data->ia.tr_hia_p_addr;
+ ci->num_cr_ia = BTINTEL_PCIE_NUM_QUEUES;
+ ci->num_tr_ia = BTINTEL_PCIE_NUM_QUEUES;
+ ci->addr_urbdq0 = data->txq.urbd0s_p_addr;
+ ci->addr_tfdq = data->txq.tfds_p_addr;
+ ci->num_tfdq = data->txq.count;
+ ci->num_urbdq0 = data->txq.count;
+ ci->tfdq_db_vec = BTINTEL_PCIE_TXQ_NUM;
+ ci->urbdq0_db_vec = BTINTEL_PCIE_TXQ_NUM;
+ ci->rbd_size = BTINTEL_PCIE_RBD_SIZE_4K;
+ ci->addr_frbdq = data->rxq.frbds_p_addr;
+ ci->num_frbdq = data->rxq.count;
+ ci->frbdq_db_vec = BTINTEL_PCIE_RXQ_NUM;
+ ci->addr_urbdq1 = data->rxq.urbd1s_p_addr;
+ ci->num_urbdq1 = data->rxq.count;
+ ci->urbdq_db_vec = BTINTEL_PCIE_RXQ_NUM;
+
+ ci->dbg_output_mode = 0x01;
+ ci->dbgc_addr = data->dbgc.frag_p_addr;
+ ci->dbgc_size = data->dbgc.frag_size;
+ ci->dbg_preset = 0x00;
+}
+
+static void btintel_pcie_free_txq_bufs(struct btintel_pcie_data *data,
+ struct txq *txq)
+{
+ /* Free data buffers first */
+ dma_free_coherent(&data->pdev->dev, txq->count * BTINTEL_PCIE_BUFFER_SIZE,
+ txq->buf_v_addr, txq->buf_p_addr);
+ kfree(txq->bufs);
+}
+
+static int btintel_pcie_setup_txq_bufs(struct btintel_pcie_data *data,
+ struct txq *txq)
+{
+ int i;
+ struct data_buf *buf;
+
+ /* Allocate the same number of buffers as the descriptor */
+ txq->bufs = kmalloc_array(txq->count, sizeof(*buf), GFP_KERNEL);
+ if (!txq->bufs)
+ return -ENOMEM;
+
+ /* Allocate full chunk of data buffer for DMA first and do indexing and
+ * initialization next, so it can be freed easily
+ */
+ txq->buf_v_addr = dma_alloc_coherent(&data->pdev->dev,
+ txq->count * BTINTEL_PCIE_BUFFER_SIZE,
+ &txq->buf_p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!txq->buf_v_addr) {
+ kfree(txq->bufs);
+ return -ENOMEM;
+ }
+
+ /* Setup the allocated DMA buffer to bufs. Each data_buf should
+ * have virtual address and physical address
+ */
+ for (i = 0; i < txq->count; i++) {
+ buf = &txq->bufs[i];
+ buf->data_p_addr = txq->buf_p_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
+ buf->data = txq->buf_v_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
+ }
+
+ return 0;
+}
+
+static void btintel_pcie_free_rxq_bufs(struct btintel_pcie_data *data,
+ struct rxq *rxq)
+{
+ /* Free data buffers first */
+ dma_free_coherent(&data->pdev->dev, rxq->count * BTINTEL_PCIE_BUFFER_SIZE,
+ rxq->buf_v_addr, rxq->buf_p_addr);
+ kfree(rxq->bufs);
+}
+
+static int btintel_pcie_setup_rxq_bufs(struct btintel_pcie_data *data,
+ struct rxq *rxq)
+{
+ int i;
+ struct data_buf *buf;
+
+ /* Allocate the same number of buffers as the descriptor */
+ rxq->bufs = kmalloc_array(rxq->count, sizeof(*buf), GFP_KERNEL);
+ if (!rxq->bufs)
+ return -ENOMEM;
+
+ /* Allocate full chunk of data buffer for DMA first and do indexing and
+ * initialization next, so it can be freed easily
+ */
+ rxq->buf_v_addr = dma_alloc_coherent(&data->pdev->dev,
+ rxq->count * BTINTEL_PCIE_BUFFER_SIZE,
+ &rxq->buf_p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!rxq->buf_v_addr) {
+ kfree(rxq->bufs);
+ return -ENOMEM;
+ }
+
+ /* Setup the allocated DMA buffer to bufs. Each data_buf should
+ * have virtual address and physical address
+ */
+ for (i = 0; i < rxq->count; i++) {
+ buf = &rxq->bufs[i];
+ buf->data_p_addr = rxq->buf_p_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
+ buf->data = rxq->buf_v_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
+ }
+
+ return 0;
+}
+
+static void btintel_pcie_setup_ia(struct btintel_pcie_data *data,
+ dma_addr_t p_addr, void *v_addr,
+ struct ia *ia)
+{
+ /* TR Head Index Array */
+ ia->tr_hia_p_addr = p_addr;
+ ia->tr_hia = v_addr;
+
+ /* TR Tail Index Array */
+ ia->tr_tia_p_addr = p_addr + sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES;
+ ia->tr_tia = v_addr + sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES;
+
+ /* CR Head index Array */
+ ia->cr_hia_p_addr = p_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 2);
+ ia->cr_hia = v_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 2);
+
+ /* CR Tail Index Array */
+ ia->cr_tia_p_addr = p_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 3);
+ ia->cr_tia = v_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 3);
+}
+
+static void btintel_pcie_free(struct btintel_pcie_data *data)
+{
+ btintel_pcie_free_rxq_bufs(data, &data->rxq);
+ btintel_pcie_free_txq_bufs(data, &data->txq);
+
+ dma_pool_free(data->dma_pool, data->dma_v_addr, data->dma_p_addr);
+ dma_pool_destroy(data->dma_pool);
+}
+
+/* Allocate tx and rx queues, any related data structures and buffers.
+ */
+static int btintel_pcie_alloc(struct btintel_pcie_data *data)
+{
+ int err = 0;
+ size_t total;
+ dma_addr_t p_addr;
+ void *v_addr;
+
+ /* Allocate the chunk of DMA memory for descriptors, index array, and
+ * context information, instead of allocating individually.
+ * The DMA memory for data buffer is allocated while setting up the
+ * each queue.
+ *
+ * Total size is sum of the following
+ * + size of TFD * Number of descriptors in queue
+ * + size of URBD0 * Number of descriptors in queue
+ * + size of FRBD * Number of descriptors in queue
+ * + size of URBD1 * Number of descriptors in queue
+ * + size of index * Number of queues(2) * type of index array(4)
+ * + size of context information
+ */
+ total = (sizeof(struct tfd) + sizeof(struct urbd0)) * BTINTEL_PCIE_TX_DESCS_COUNT;
+ total += (sizeof(struct frbd) + sizeof(struct urbd1)) * BTINTEL_PCIE_RX_DESCS_COUNT;
+
+ /* Add the sum of size of index array and size of ci struct */
+ total += (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 4) + sizeof(struct ctx_info);
+
+ /* Allocate DMA Pool */
+ data->dma_pool = dma_pool_create(KBUILD_MODNAME, &data->pdev->dev,
+ total, BTINTEL_PCIE_DMA_POOL_ALIGNMENT, 0);
+ if (!data->dma_pool) {
+ err = -ENOMEM;
+ goto exit_error;
+ }
+
+ v_addr = dma_pool_zalloc(data->dma_pool, GFP_KERNEL | __GFP_NOWARN,
+ &p_addr);
+ if (!v_addr) {
+ dma_pool_destroy(data->dma_pool);
+ err = -ENOMEM;
+ goto exit_error;
+ }
+
+ data->dma_p_addr = p_addr;
+ data->dma_v_addr = v_addr;
+
+ /* Setup descriptor count */
+ data->txq.count = BTINTEL_PCIE_TX_DESCS_COUNT;
+ data->rxq.count = BTINTEL_PCIE_RX_DESCS_COUNT;
+
+ /* Setup tfds */
+ data->txq.tfds_p_addr = p_addr;
+ data->txq.tfds = v_addr;
+
+ p_addr += (sizeof(struct tfd) * BTINTEL_PCIE_TX_DESCS_COUNT);
+ v_addr += (sizeof(struct tfd) * BTINTEL_PCIE_TX_DESCS_COUNT);
+
+ /* Setup urbd0 */
+ data->txq.urbd0s_p_addr = p_addr;
+ data->txq.urbd0s = v_addr;
+
+ p_addr += (sizeof(struct urbd0) * BTINTEL_PCIE_TX_DESCS_COUNT);
+ v_addr += (sizeof(struct urbd0) * BTINTEL_PCIE_TX_DESCS_COUNT);
+
+ /* Setup FRBD*/
+ data->rxq.frbds_p_addr = p_addr;
+ data->rxq.frbds = v_addr;
+
+ p_addr += (sizeof(struct frbd) * BTINTEL_PCIE_RX_DESCS_COUNT);
+ v_addr += (sizeof(struct frbd) * BTINTEL_PCIE_RX_DESCS_COUNT);
+
+ /* Setup urbd1 */
+ data->rxq.urbd1s_p_addr = p_addr;
+ data->rxq.urbd1s = v_addr;
+
+ p_addr += (sizeof(struct urbd1) * BTINTEL_PCIE_RX_DESCS_COUNT);
+ v_addr += (sizeof(struct urbd1) * BTINTEL_PCIE_RX_DESCS_COUNT);
+
+ /* Setup data buffers for txq */
+ err = btintel_pcie_setup_txq_bufs(data, &data->txq);
+ if (err)
+ goto exit_error_pool;
+
+ /* Setup data buffers for rxq */
+ err = btintel_pcie_setup_rxq_bufs(data, &data->rxq);
+ if (err)
+ goto exit_error_txq;
+
+ /* Setup Index Array */
+ btintel_pcie_setup_ia(data, p_addr, v_addr, &data->ia);
+
+ /* Setup data buffers for dbgc */
+ err = btintel_pcie_setup_dbgc(data);
+ if (err)
+ goto exit_error_txq;
+
+ /* Setup Context Information */
+ p_addr += sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 4;
+ v_addr += sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 4;
+
+ data->ci = v_addr;
+ data->ci_p_addr = p_addr;
+
+ /* Initialize the CI */
+ btintel_pcie_init_ci(data, data->ci);
+
+ return 0;
+
+exit_error_txq:
+ btintel_pcie_free_txq_bufs(data, &data->txq);
+exit_error_pool:
+ dma_pool_free(data->dma_pool, data->dma_v_addr, data->dma_p_addr);
+ dma_pool_destroy(data->dma_pool);
+exit_error:
+ return err;
+}
+
+static int btintel_pcie_open(struct hci_dev *hdev)
+{
+ bt_dev_dbg(hdev, "");
+
+ return 0;
+}
+
+static int btintel_pcie_close(struct hci_dev *hdev)
+{
+ bt_dev_dbg(hdev, "");
+
+ return 0;
+}
+
+static int btintel_pcie_inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
+{
+ struct sk_buff *skb;
+ struct hci_event_hdr *hdr;
+ struct hci_ev_cmd_complete *evt;
+
+ skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr = (struct hci_event_hdr *)skb_put(skb, sizeof(*hdr));
+ hdr->evt = HCI_EV_CMD_COMPLETE;
+ hdr->plen = sizeof(*evt) + 1;
+
+ evt = (struct hci_ev_cmd_complete *)skb_put(skb, sizeof(*evt));
+ evt->ncmd = 0x01;
+ evt->opcode = cpu_to_le16(opcode);
+
+ *(u8 *)skb_put(skb, 1) = 0x00;
+
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+
+ return hci_recv_frame(hdev, skb);
+}
+
+static int btintel_pcie_send_frame(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+ struct hci_command_hdr *cmd;
+ __u16 opcode = ~0;
+ int ret;
+ u32 type;
+
+ if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
+ return -ENODEV;
+
+ /* Due to the fw limitation, the type header of the packet should be
+ * 4 bytes unlike 1 byte for UART. In UART, the firmware can read
+ * the first byte to get the packet type and redirect the rest of data
+ * packet to the right handler.
+ *
+ * But for PCIe, THF(Transfer Flow Handler) fetches the 4 bytes of data
+ * from DMA memory and by the time it reads the first 4 bytes, it has
+ * already consumed some part of packet. Thus the packet type indicator
+ * for iBT PCIe is 4 bytes.
+ *
+ * Luckily, when HCI core creates the skb, it allocates 8 bytes of
+ * head room for profile and driver use, and before sending the data
+ * to the device, append the iBT PCIe packet type in the front.
+ */
+ switch (hci_skb_pkt_type(skb)) {
+ case HCI_COMMAND_PKT:
+ type = BTINTEL_PCIE_HCI_CMD_PKT;
+ cmd = (void *)skb->data;
+ opcode = le16_to_cpu(cmd->opcode);
+ if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
+ struct hci_command_hdr *cmd = (void *)skb->data;
+ __u16 opcode = le16_to_cpu(cmd->opcode);
+
+ /* When the BTINTEL_HCI_OP_RESET command is issued to
+ * boot into the operational firmware, it will actually
+ * not send a command complete event. To keep the flow
+ * control working inject that event here.
+ */
+ if (opcode == BTINTEL_HCI_OP_RESET)
+ btintel_pcie_inject_cmd_complete(hdev, opcode);
+ }
+
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ type = BTINTEL_PCIE_HCI_ACL_PKT;
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ type = BTINTEL_PCIE_HCI_SCO_PKT;
+ hdev->stat.sco_tx++;
+ break;
+ case HCI_ISODATA_PKT:
+ type = BTINTEL_PCIE_HCI_ISO_PKT;
+ break;
+ default:
+ bt_dev_err(hdev, "Unknown HCI packet type");
+ return -EILSEQ;
+ }
+
+ ret = btintel_pcie_send_sync(data, skb, type, opcode);
+ if (ret) {
+ hdev->stat.err_tx++;
+ bt_dev_err(hdev, "Failed to send frame (%d)", ret);
+ goto exit_error;
+ }
+
+ hdev->stat.byte_tx += skb->len;
+ kfree_skb(skb);
+
+exit_error:
+ return ret;
+}
+
+static void btintel_pcie_release_hdev(struct btintel_pcie_data *data)
+{
+ struct hci_dev *hdev;
+
+ hdev = data->hdev;
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ data->hdev = NULL;
+}
+
+static void btintel_pcie_disable_interrupts(struct btintel_pcie_data *data)
+{
+ spin_lock(&data->irq_lock);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, data->fh_init_mask);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, data->hw_init_mask);
+ spin_unlock(&data->irq_lock);
+}
+
+static void btintel_pcie_enable_interrupts(struct btintel_pcie_data *data)
+{
+ spin_lock(&data->irq_lock);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, ~data->fh_init_mask);
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, ~data->hw_init_mask);
+ spin_unlock(&data->irq_lock);
+}
+
+static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data)
+{
+ for (int i = 0; i < data->alloc_vecs; i++)
+ synchronize_irq(data->msix_entries[i].vector);
+}
+
+static int btintel_pcie_setup_internal(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+ const u8 param[1] = { 0xFF };
+ struct intel_version_tlv ver_tlv;
+ struct sk_buff *skb;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Reading Intel version command failed (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ /* Check the status */
+ if (skb->data[0]) {
+ bt_dev_err(hdev, "Intel Read Version command failed (%02x)",
+ skb->data[0]);
+ err = -EIO;
+ goto exit_error;
+ }
+
+ /* Apply the common HCI quirks for Intel device */
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_DIAG);
+
+ /* Set up the quality report callback for Intel devices */
+ hdev->set_quality_report = btintel_set_quality_report;
+
+ memset(&ver_tlv, 0, sizeof(ver_tlv));
+ /* For TLV type device, parse the tlv data */
+ err = btintel_parse_version_tlv(hdev, &ver_tlv, skb);
+ if (err) {
+ bt_dev_err(hdev, "Failed to parse TLV version information");
+ goto exit_error;
+ }
+
+ switch (INTEL_HW_PLATFORM(ver_tlv.cnvi_bt)) {
+ case 0x37:
+ break;
+ default:
+ bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)",
+ INTEL_HW_PLATFORM(ver_tlv.cnvi_bt));
+ err = -EINVAL;
+ goto exit_error;
+ }
+
+ /* Check for supported iBT hardware variants of this firmware
+ * loading method.
+ *
+ * This check has been put in place to ensure correct forward
+ * compatibility options when newer hardware variants come
+ * along.
+ */
+ switch (INTEL_HW_VARIANT(ver_tlv.cnvi_bt)) {
+ case 0x1e: /* BzrI */
+ case 0x1f: /* ScP */
+ case 0x22: /* BzrIW */
+ /* Display version information of TLV type */
+ btintel_version_info_tlv(hdev, &ver_tlv);
+
+ /* Apply the device specific HCI quirks for TLV based devices
+ *
+ * All TLV based devices support WBS
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
+
+ /* Setup MSFT Extension support */
+ btintel_set_msft_opcode(hdev,
+ INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
+
+ err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
+ if (err)
+ goto exit_error;
+ break;
+ default:
+ bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
+ INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
+ err = -EINVAL;
+ goto exit_error;
+ break;
+ }
+
+ data->dmp_hdr.cnvi_top = ver_tlv.cnvi_top;
+ data->dmp_hdr.cnvr_top = ver_tlv.cnvr_top;
+ data->dmp_hdr.fw_timestamp = ver_tlv.timestamp;
+ data->dmp_hdr.fw_build_type = ver_tlv.build_type;
+ data->dmp_hdr.fw_build_num = ver_tlv.build_num;
+ data->dmp_hdr.cnvi_bt = ver_tlv.cnvi_bt;
+
+ if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
+ data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
+
+ btintel_print_fseq_info(hdev);
+exit_error:
+ kfree_skb(skb);
+
+ return err;
+}
+
+static int btintel_pcie_setup(struct hci_dev *hdev)
+{
+ int err, fw_dl_retry = 0;
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+ while ((err = btintel_pcie_setup_internal(hdev)) && fw_dl_retry++ < 1) {
+ bt_dev_err(hdev, "Firmware download retry count: %d",
+ fw_dl_retry);
+ btintel_pcie_dump_debug_registers(hdev);
+ btintel_pcie_disable_interrupts(data);
+ btintel_pcie_synchronize_irqs(data);
+ err = btintel_pcie_reset_bt(data);
+ if (err) {
+ bt_dev_err(hdev, "Failed to do shr reset: %d", err);
+ break;
+ }
+ usleep_range(10000, 12000);
+ btintel_pcie_reset_ia(data);
+ btintel_pcie_enable_interrupts(data);
+ btintel_pcie_config_msix(data);
+ err = btintel_pcie_enable_bt(data);
+ if (err) {
+ bt_dev_err(hdev, "Failed to enable hardware: %d", err);
+ break;
+ }
+ btintel_pcie_start_rx(data);
+ }
+
+ if (!err)
+ set_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags);
+ return err;
+}
+
+static struct btintel_pcie_dev_recovery *
+btintel_pcie_get_recovery(struct pci_dev *pdev, struct device *dev)
+{
+ struct btintel_pcie_dev_recovery *tmp, *data = NULL;
+ const char *name = pci_name(pdev);
+ const size_t name_len = strlen(name) + 1;
+ struct hci_dev *hdev = to_hci_dev(dev);
+
+ spin_lock(&btintel_pcie_recovery_lock);
+ list_for_each_entry(tmp, &btintel_pcie_recovery_list, list) {
+ if (strcmp(tmp->name, name))
+ continue;
+ data = tmp;
+ break;
+ }
+ spin_unlock(&btintel_pcie_recovery_lock);
+
+ if (data) {
+ bt_dev_dbg(hdev, "Found restart data for BDF: %s", data->name);
+ return data;
+ }
+
+ data = kzalloc(struct_size(data, name, name_len), GFP_ATOMIC);
+ if (!data)
+ return NULL;
+
+ strscpy(data->name, name, name_len);
+ spin_lock(&btintel_pcie_recovery_lock);
+ list_add_tail(&data->list, &btintel_pcie_recovery_list);
+ spin_unlock(&btintel_pcie_recovery_lock);
+
+ return data;
+}
+
+static void btintel_pcie_free_restart_list(void)
+{
+ struct btintel_pcie_dev_recovery *tmp;
+
+ while ((tmp = list_first_entry_or_null(&btintel_pcie_recovery_list,
+ typeof(*tmp), list))) {
+ list_del(&tmp->list);
+ kfree(tmp);
+ }
+}
+
+static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
+ struct device *dev)
+{
+ struct btintel_pcie_dev_recovery *data;
+ time64_t retry_window;
+
+ data = btintel_pcie_get_recovery(pdev, dev);
+ if (!data)
+ return;
+
+ retry_window = ktime_get_boottime_seconds() - data->last_error;
+ if (data->count == 0) {
+ data->last_error = ktime_get_boottime_seconds();
+ data->count++;
+ } else if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
+ data->count <= BTINTEL_PCIE_FLR_MAX_RETRY) {
+ data->count++;
+ } else if (retry_window > BTINTEL_PCIE_RESET_WINDOW_SECS) {
+ data->last_error = 0;
+ data->count = 0;
+ }
+}
+
+static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
+
+static void btintel_pcie_removal_work(struct work_struct *wk)
+{
+ struct btintel_pcie_removal *removal =
+ container_of(wk, struct btintel_pcie_removal, work);
+ struct pci_dev *pdev = removal->pdev;
+ struct btintel_pcie_data *data;
+ int err;
+
+ pci_lock_rescan_remove();
+
+ if (!pdev->bus)
+ goto error;
+
+ data = pci_get_drvdata(pdev);
+
+ btintel_pcie_disable_interrupts(data);
+ btintel_pcie_synchronize_irqs(data);
+
+ flush_work(&data->rx_work);
+
+ bt_dev_dbg(data->hdev, "Release bluetooth interface");
+ btintel_pcie_release_hdev(data);
+
+ err = pci_reset_function(pdev);
+ if (err) {
+ BT_ERR("Failed resetting the pcie device (%d)", err);
+ goto error;
+ }
+
+ btintel_pcie_enable_interrupts(data);
+ btintel_pcie_config_msix(data);
+
+ err = btintel_pcie_enable_bt(data);
+ if (err) {
+ BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
+ err);
+ goto error;
+ }
+
+ btintel_pcie_reset_ia(data);
+ btintel_pcie_start_rx(data);
+ data->flags = 0;
+
+ err = btintel_pcie_setup_hdev(data);
+ if (err) {
+ BT_ERR("Failed registering hdev (%d)", err);
+ goto error;
+ }
+error:
+ pci_dev_put(pdev);
+ pci_unlock_rescan_remove();
+ kfree(removal);
+}
+
+static void btintel_pcie_reset(struct hci_dev *hdev)
+{
+ struct btintel_pcie_removal *removal;
+ struct btintel_pcie_data *data;
+
+ data = hci_get_drvdata(hdev);
+
+ if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
+ return;
+
+ if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
+ return;
+
+ removal = kzalloc(sizeof(*removal), GFP_ATOMIC);
+ if (!removal)
+ return;
+
+ removal->pdev = data->pdev;
+ INIT_WORK(&removal->work, btintel_pcie_removal_work);
+ pci_dev_get(removal->pdev);
+ schedule_work(&removal->work);
+}
+
+static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
+{
+ struct btintel_pcie_dev_recovery *data;
+ struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev);
+ struct pci_dev *pdev = dev_data->pdev;
+ time64_t retry_window;
+
+ if (code == 0x13) {
+ bt_dev_err(hdev, "Encountered top exception");
+ return;
+ }
+
+ data = btintel_pcie_get_recovery(pdev, &hdev->dev);
+ if (!data)
+ return;
+
+ retry_window = ktime_get_boottime_seconds() - data->last_error;
+
+ if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
+ data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
+ bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d",
+ BTINTEL_PCIE_FLR_MAX_RETRY, data->count);
+ bt_dev_dbg(hdev, "Boot time: %lld seconds",
+ ktime_get_boottime_seconds());
+ bt_dev_dbg(hdev, "last error at: %lld seconds",
+ data->last_error);
+ return;
+ }
+ btintel_pcie_inc_recovery_count(pdev, &hdev->dev);
+ btintel_pcie_reset(hdev);
+}
+
+static bool btintel_pcie_wakeup(struct hci_dev *hdev)
+{
+ struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+ return device_may_wakeup(&data->pdev->dev);
+}
+
+static const struct {
+ u16 opcode;
+ const char *desc;
+} btintel_pcie_hci_drv_supported_commands[] = {
+ /* Common commands */
+ { HCI_DRV_OP_READ_INFO, "Read Info" },
+};
+
+static int btintel_pcie_hci_drv_read_info(struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct hci_drv_rp_read_info *rp;
+ size_t rp_size;
+ int err, i;
+ u16 opcode, num_supported_commands =
+ ARRAY_SIZE(btintel_pcie_hci_drv_supported_commands);
+
+ rp_size = sizeof(*rp) + num_supported_commands * 2;
+
+ rp = kmalloc(rp_size, GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ strscpy_pad(rp->driver_name, KBUILD_MODNAME);
+
+ rp->num_supported_commands = cpu_to_le16(num_supported_commands);
+ for (i = 0; i < num_supported_commands; i++) {
+ opcode = btintel_pcie_hci_drv_supported_commands[i].opcode;
+ bt_dev_dbg(hdev,
+ "Supported HCI Drv command (0x%02x|0x%04x): %s",
+ hci_opcode_ogf(opcode),
+ hci_opcode_ocf(opcode),
+ btintel_pcie_hci_drv_supported_commands[i].desc);
+ rp->supported_commands[i] = cpu_to_le16(opcode);
+ }
+
+ err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
+ HCI_DRV_STATUS_SUCCESS,
+ rp, rp_size);
+
+ kfree(rp);
+ return err;
+}
+
+static const struct hci_drv_handler btintel_pcie_hci_drv_common_handlers[] = {
+ { btintel_pcie_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE },
+};
+
+static const struct hci_drv_handler btintel_pcie_hci_drv_specific_handlers[] = {};
+
+static struct hci_drv btintel_pcie_hci_drv = {
+ .common_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_common_handlers),
+ .common_handlers = btintel_pcie_hci_drv_common_handlers,
+ .specific_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_specific_handlers),
+ .specific_handlers = btintel_pcie_hci_drv_specific_handlers,
+};
+
+static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
+{
+ int err;
+ struct hci_dev *hdev;
+
+ hdev = hci_alloc_dev_priv(sizeof(struct btintel_data));
+ if (!hdev)
+ return -ENOMEM;
+
+ hdev->bus = HCI_PCI;
+ hci_set_drvdata(hdev, data);
+
+ data->hdev = hdev;
+ SET_HCIDEV_DEV(hdev, &data->pdev->dev);
+
+ hdev->manufacturer = 2;
+ hdev->open = btintel_pcie_open;
+ hdev->close = btintel_pcie_close;
+ hdev->send = btintel_pcie_send_frame;
+ hdev->setup = btintel_pcie_setup;
+ hdev->shutdown = btintel_shutdown_combined;
+ hdev->hw_error = btintel_pcie_hw_error;
+ hdev->set_diag = btintel_set_diag;
+ hdev->set_bdaddr = btintel_set_bdaddr;
+ hdev->reset = btintel_pcie_reset;
+ hdev->wakeup = btintel_pcie_wakeup;
+ hdev->hci_drv = &btintel_pcie_hci_drv;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BT_ERR("Failed to register to hdev (%d)", err);
+ goto exit_error;
+ }
+
+ data->dmp_hdr.driver_name = KBUILD_MODNAME;
+ return 0;
+
+exit_error:
+ hci_free_dev(hdev);
+ return err;
+}
+
+static int btintel_pcie_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ struct btintel_pcie_data *data;
+
+ if (!pdev)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->pdev = pdev;
+
+ spin_lock_init(&data->irq_lock);
+ spin_lock_init(&data->hci_rx_lock);
+
+ init_waitqueue_head(&data->gp0_wait_q);
+ data->gp0_received = false;
+
+ init_waitqueue_head(&data->tx_wait_q);
+ data->tx_wait_done = false;
+
+ data->workqueue = alloc_ordered_workqueue(KBUILD_MODNAME, WQ_HIGHPRI);
+ if (!data->workqueue)
+ return -ENOMEM;
+
+ skb_queue_head_init(&data->rx_skb_q);
+ INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
+
+ data->boot_stage_cache = 0x00;
+ data->img_resp_cache = 0x00;
+
+ err = btintel_pcie_config_pcie(pdev, data);
+ if (err)
+ goto exit_error;
+
+ pci_set_drvdata(pdev, data);
+
+ err = btintel_pcie_alloc(data);
+ if (err)
+ goto exit_error;
+
+ err = btintel_pcie_enable_bt(data);
+ if (err)
+ goto exit_error;
+
+ /* CNV information (CNVi and CNVr) is in CSR */
+ data->cnvi = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_HW_REV_REG);
+
+ data->cnvr = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_RF_ID_REG);
+
+ err = btintel_pcie_start_rx(data);
+ if (err)
+ goto exit_error;
+
+ err = btintel_pcie_setup_hdev(data);
+ if (err)
+ goto exit_error;
+
+ bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
+ data->cnvr);
+ return 0;
+
+exit_error:
+ /* reset device before exit */
+ btintel_pcie_reset_bt(data);
+
+ pci_clear_master(pdev);
+
+ pci_set_drvdata(pdev, NULL);
+
+ return err;
+}
+
+static void btintel_pcie_remove(struct pci_dev *pdev)
+{
+ struct btintel_pcie_data *data;
+
+ data = pci_get_drvdata(pdev);
+
+ btintel_pcie_disable_interrupts(data);
+
+ btintel_pcie_synchronize_irqs(data);
+
+ flush_work(&data->rx_work);
+
+ btintel_pcie_reset_bt(data);
+ for (int i = 0; i < data->alloc_vecs; i++) {
+ struct msix_entry *msix_entry;
+
+ msix_entry = &data->msix_entries[i];
+ free_irq(msix_entry->vector, msix_entry);
+ }
+
+ pci_free_irq_vectors(pdev);
+
+ btintel_pcie_release_hdev(data);
+
+ destroy_workqueue(data->workqueue);
+
+ btintel_pcie_free(data);
+
+ pci_clear_master(pdev);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_DEV_COREDUMP
+static void btintel_pcie_coredump(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data = pci_get_drvdata(pdev);
+
+ if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
+ return;
+
+ data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
+ queue_work(data->workqueue, &data->rx_work);
+}
+#endif
+
+static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
+{
+ int retry = 0, status;
+ u32 dx_intr_timeout_ms = 200;
+
+ do {
+ data->gp0_received = false;
+
+ btintel_pcie_wr_sleep_cntrl(data, dxstate);
+
+ status = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
+ msecs_to_jiffies(dx_intr_timeout_ms));
+
+ if (status)
+ return 0;
+
+ bt_dev_warn(data->hdev,
+ "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
+ dx_intr_timeout_ms, dxstate, retry);
+
+ /* clear gp0 cause */
+ btintel_pcie_clr_reg_bits(data,
+ BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
+
+ /* A hardware bug may cause the alive interrupt to be missed.
+ * Check if the controller reached the expected state and retry
+ * the operation only if it hasn't.
+ */
+ if (dxstate == BTINTEL_PCIE_STATE_D0) {
+ if (btintel_pcie_in_d0(data))
+ return 0;
+ } else {
+ if (btintel_pcie_in_d3(data))
+ return 0;
+ }
+
+ } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES);
+
+ return -EBUSY;
+}
+
+static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data;
+ ktime_t start;
+ u32 dxstate;
+ int err;
+
+ data = pci_get_drvdata(pdev);
+
+ dxstate = (mesg.event == PM_EVENT_SUSPEND ?
+ BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
+
+ data->pm_sx_event = mesg.event;
+
+ start = ktime_get();
+
+ /* Refer: 6.4.11.7 -> Platform power management */
+ err = btintel_pcie_set_dxstate(data, dxstate);
+
+ if (err)
+ return err;
+
+ bt_dev_dbg(data->hdev,
+ "device entered into d3 state from d0 in %lld us",
+ ktime_to_us(ktime_get() - start));
+ return err;
+}
+
+static int btintel_pcie_suspend(struct device *dev)
+{
+ return btintel_pcie_suspend_late(dev, PMSG_SUSPEND);
+}
+
+static int btintel_pcie_hibernate(struct device *dev)
+{
+ return btintel_pcie_suspend_late(dev, PMSG_HIBERNATE);
+}
+
+static int btintel_pcie_freeze(struct device *dev)
+{
+ return btintel_pcie_suspend_late(dev, PMSG_FREEZE);
+}
+
+static int btintel_pcie_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data;
+ ktime_t start;
+ int err;
+
+ data = pci_get_drvdata(pdev);
+ data->gp0_received = false;
+
+ start = ktime_get();
+
+ /* When the system enters S4 (hibernate) mode, bluetooth device loses
+ * power, which results in the erasure of its loaded firmware.
+ * Consequently, function level reset (flr) is required on system
+ * resume to bring the controller back into an operational state by
+ * initiating a new firmware download.
+ */
+
+ if (data->pm_sx_event == PM_EVENT_FREEZE ||
+ data->pm_sx_event == PM_EVENT_HIBERNATE) {
+ set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
+ btintel_pcie_reset(data->hdev);
+ return 0;
+ }
+
+ /* Refer: 6.4.11.7 -> Platform power management */
+ err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0);
+
+ if (err == 0) {
+ bt_dev_dbg(data->hdev,
+ "device entered into d0 state from d3 in %lld us",
+ ktime_to_us(ktime_get() - start));
+ return err;
+ }
+
+ /* Trigger function level reset if the controller is in error
+ * state during resume() to bring back the controller to
+ * operational mode
+ */
+
+ data->boot_stage_cache = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+ if (btintel_pcie_in_error(data) ||
+ btintel_pcie_in_device_halt(data)) {
+ bt_dev_err(data->hdev, "Controller in error state for D0 entry");
+ if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS,
+ &data->flags)) {
+ data->dmp_hdr.trigger_reason =
+ BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
+ queue_work(data->workqueue, &data->rx_work);
+ }
+ set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
+ btintel_pcie_reset(data->hdev);
+ }
+ return err;
+}
+
+static const struct dev_pm_ops btintel_pcie_pm_ops = {
+ .suspend = btintel_pcie_suspend,
+ .resume = btintel_pcie_resume,
+ .freeze = btintel_pcie_freeze,
+ .thaw = btintel_pcie_resume,
+ .poweroff = btintel_pcie_hibernate,
+ .restore = btintel_pcie_resume,
+};
+
+static struct pci_driver btintel_pcie_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = btintel_pcie_table,
+ .probe = btintel_pcie_probe,
+ .remove = btintel_pcie_remove,
+ .driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
+#ifdef CONFIG_DEV_COREDUMP
+ .driver.coredump = btintel_pcie_coredump
+#endif
+};
+
+static int __init btintel_pcie_init(void)
+{
+ return pci_register_driver(&btintel_pcie_driver);
+}
+
+static void __exit btintel_pcie_exit(void)
+{
+ pci_unregister_driver(&btintel_pcie_driver);
+ btintel_pcie_free_restart_list();
+}
+
+module_init(btintel_pcie_init);
+module_exit(btintel_pcie_exit);
+
+MODULE_AUTHOR("Tedd Ho-Jeong An <tedd.an@intel.com>");
+MODULE_DESCRIPTION("Intel Bluetooth PCIe transport driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
new file mode 100644
index 000000000000..e3d941ffef4a
--- /dev/null
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -0,0 +1,566 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ * Bluetooth support for Intel PCIe devices
+ *
+ * Copyright (C) 2024 Intel Corporation
+ */
+
+/* Control and Status Register(BTINTEL_PCIE_CSR) */
+#define BTINTEL_PCIE_CSR_BASE (0x000)
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_REG (BTINTEL_PCIE_CSR_BASE + 0x024)
+#define BTINTEL_PCIE_CSR_HW_REV_REG (BTINTEL_PCIE_CSR_BASE + 0x028)
+#define BTINTEL_PCIE_CSR_RF_ID_REG (BTINTEL_PCIE_CSR_BASE + 0x09C)
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_REG (BTINTEL_PCIE_CSR_BASE + 0x108)
+#define BTINTEL_PCIE_CSR_IPC_CONTROL_REG (BTINTEL_PCIE_CSR_BASE + 0x10C)
+#define BTINTEL_PCIE_CSR_IPC_STATUS_REG (BTINTEL_PCIE_CSR_BASE + 0x110)
+#define BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG (BTINTEL_PCIE_CSR_BASE + 0x114)
+#define BTINTEL_PCIE_CSR_CI_ADDR_LSB_REG (BTINTEL_PCIE_CSR_BASE + 0x118)
+#define BTINTEL_PCIE_CSR_CI_ADDR_MSB_REG (BTINTEL_PCIE_CSR_BASE + 0x11C)
+#define BTINTEL_PCIE_CSR_IMG_RESPONSE_REG (BTINTEL_PCIE_CSR_BASE + 0x12C)
+#define BTINTEL_PCIE_CSR_MBOX_1_REG (BTINTEL_PCIE_CSR_BASE + 0x170)
+#define BTINTEL_PCIE_CSR_MBOX_2_REG (BTINTEL_PCIE_CSR_BASE + 0x174)
+#define BTINTEL_PCIE_CSR_MBOX_3_REG (BTINTEL_PCIE_CSR_BASE + 0x178)
+#define BTINTEL_PCIE_CSR_MBOX_4_REG (BTINTEL_PCIE_CSR_BASE + 0x17C)
+#define BTINTEL_PCIE_CSR_MBOX_STATUS_REG (BTINTEL_PCIE_CSR_BASE + 0x180)
+#define BTINTEL_PCIE_PRPH_DEV_ADDR_REG (BTINTEL_PCIE_CSR_BASE + 0x440)
+#define BTINTEL_PCIE_PRPH_DEV_RD_REG (BTINTEL_PCIE_CSR_BASE + 0x458)
+#define BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR (BTINTEL_PCIE_CSR_BASE + 0x460)
+
+/* BTINTEL_PCIE_CSR Function Control Register */
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA (BIT(0))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT (BIT(6))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT (BIT(7))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS (BIT(20))
+
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ (BIT(21))
+/* Stop MAC Access disconnection request */
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS (BIT(22))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ (BIT(23))
+
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS (BIT(28))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON (BIT(29))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET (BIT(31))
+
+/* Value for BTINTEL_PCIE_CSR_BOOT_STAGE register */
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM (BIT(0))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_IML (BIT(1))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW (BIT(2))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN (BIT(10))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN (BIT(11))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR (BIT(12))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER (BIT(13))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_ALIVE (BIT(23))
+#define BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY (BIT(24))
+
+/* Registers for MSI-X */
+#define BTINTEL_PCIE_CSR_MSIX_BASE (0x2000)
+#define BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0800)
+#define BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0804)
+#define BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0808)
+#define BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK (BTINTEL_PCIE_CSR_MSIX_BASE + 0x080C)
+#define BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0810)
+#define BTINTEL_PCIE_CSR_MSIX_AUTOMASK_EN (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0814)
+#define BTINTEL_PCIE_CSR_MSIX_IVAR_BASE (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0880)
+#define BTINTEL_PCIE_CSR_MSIX_IVAR(cause) (BTINTEL_PCIE_CSR_MSIX_IVAR_BASE + (cause))
+
+/* IOSF Debug Register */
+#define BTINTEL_PCIE_DBGC_BASE_ADDR (0xf3800300)
+#define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x1C)
+#define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x2C)
+
+#define BTINTEL_PCIE_DBG_IDX_BIT_MASK 0x0F
+#define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data) (((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK)
+#define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK 0xFFFFFF
+
+/* The DRAM buffer count, each buffer size, and
+ * fragment buffer size
+ */
+#define BTINTEL_PCIE_DBGC_BUFFER_COUNT 16
+#define BTINTEL_PCIE_DBGC_BUFFER_SIZE (256 * 1024) /* 256 KB */
+
+#define BTINTEL_PCIE_DBGC_FRAG_VERSION 1
+#define BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT BTINTEL_PCIE_DBGC_BUFFER_COUNT
+
+/* Magic number(4), version(4), size of payload length(4) */
+#define BTINTEL_PCIE_DBGC_FRAG_HEADER_SIZE 12
+
+/* Num of alloc Dbg buff (4) + (LSB(4), MSB(4), Size(4)) for each buffer */
+#define BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE 196
+
+/* Causes for the FH register interrupts */
+enum msix_fh_int_causes {
+ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0 = BIT(0), /* cause 0 */
+ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1 = BIT(1), /* cause 1 */
+};
+
+/* Causes for the HW register interrupts */
+enum msix_hw_int_causes {
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */
+};
+
+/* PCIe device states
+ * Host-Device interface is active
+ * Host-Device interface is inactive(as reflected by IPC_SLEEP_CONTROL_CSR_AD)
+ * Host-Device interface is inactive(as reflected by IPC_SLEEP_CONTROL_CSR_AD)
+ */
+enum {
+ BTINTEL_PCIE_STATE_D0 = 0,
+ BTINTEL_PCIE_STATE_D3_HOT = 2,
+ BTINTEL_PCIE_STATE_D3_COLD = 3,
+};
+
+enum {
+ BTINTEL_PCIE_CORE_HALTED,
+ BTINTEL_PCIE_HWEXP_INPROGRESS,
+ BTINTEL_PCIE_COREDUMP_INPROGRESS,
+ BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
+ BTINTEL_PCIE_SETUP_DONE
+};
+
+enum btintel_pcie_tlv_type {
+ BTINTEL_CNVI_BT,
+ BTINTEL_WRITE_PTR,
+ BTINTEL_WRAP_CTR,
+ BTINTEL_TRIGGER_REASON,
+ BTINTEL_FW_SHA,
+ BTINTEL_CNVR_TOP,
+ BTINTEL_CNVI_TOP,
+ BTINTEL_DUMP_TIME,
+ BTINTEL_FW_BUILD,
+ BTINTEL_VENDOR,
+ BTINTEL_DRIVER
+};
+
+/* causes for the MBOX interrupts */
+enum msix_mbox_int_causes {
+ BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1 = BIT(0), /* cause MBOX1 */
+ BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2 = BIT(1), /* cause MBOX2 */
+ BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3 = BIT(2), /* cause MBOX3 */
+ BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */
+};
+
+#define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7)
+
+/* Minimum and Maximum number of MSI-X Vector
+ * Intel Bluetooth PCIe support only 1 vector
+ */
+#define BTINTEL_PCIE_MSIX_VEC_MAX 1
+#define BTINTEL_PCIE_MSIX_VEC_MIN 1
+
+/* Default poll time for MAC access during init */
+#define BTINTEL_DEFAULT_MAC_ACCESS_TIMEOUT_US 200000
+
+/* Default interrupt timeout in msec */
+#define BTINTEL_DEFAULT_INTR_TIMEOUT_MS 3000
+
+#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3
+
+/* The number of descriptors in TX queues */
+#define BTINTEL_PCIE_TX_DESCS_COUNT 32
+
+/* The number of descriptors in RX queues */
+#define BTINTEL_PCIE_RX_DESCS_COUNT 64
+
+/* Number of Queue for TX and RX
+ * It indicates the index of the IA(Index Array)
+ */
+enum {
+ BTINTEL_PCIE_TXQ_NUM = 0,
+ BTINTEL_PCIE_RXQ_NUM = 1,
+ BTINTEL_PCIE_NUM_QUEUES = 2,
+};
+
+/* The size of DMA buffer for TX and RX in bytes */
+#define BTINTEL_PCIE_BUFFER_SIZE 4096
+
+/* DMA allocation alignment */
+#define BTINTEL_PCIE_DMA_POOL_ALIGNMENT 256
+
+#define BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS 500
+
+/* Doorbell vector for TFD */
+#define BTINTEL_PCIE_TX_DB_VEC 0
+
+/* Doorbell vector for FRBD */
+#define BTINTEL_PCIE_RX_DB_VEC 513
+
+/* RBD buffer size mapping */
+#define BTINTEL_PCIE_RBD_SIZE_4K 0x04
+
+/*
+ * Struct for Context Information (v2)
+ *
+ * All members are write-only for host and read-only for device.
+ *
+ * @version: Version of context information
+ * @size: Size of context information
+ * @config: Config with which host wants peripheral to execute
+ * Subset of capability register published by device
+ * @addr_tr_hia: Address of TR Head Index Array
+ * @addr_tr_tia: Address of TR Tail Index Array
+ * @addr_cr_hia: Address of CR Head Index Array
+ * @addr_cr_tia: Address of CR Tail Index Array
+ * @num_tr_ia: Number of entries in TR Index Arrays
+ * @num_cr_ia: Number of entries in CR Index Arrays
+ * @rbd_siz: RBD Size { 0x4=4K }
+ * @addr_tfdq: Address of TFD Queue(tx)
+ * @addr_urbdq0: Address of URBD Queue(tx)
+ * @num_tfdq: Number of TFD in TFD Queue(tx)
+ * @num_urbdq0: Number of URBD in URBD Queue(tx)
+ * @tfdq_db_vec: Queue number of TFD
+ * @urbdq0_db_vec: Queue number of URBD
+ * @addr_frbdq: Address of FRBD Queue(rx)
+ * @addr_urbdq1: Address of URBD Queue(rx)
+ * @num_frbdq: Number of FRBD in FRBD Queue(rx)
+ * @frbdq_db_vec: Queue number of FRBD
+ * @num_urbdq1: Number of URBD in URBD Queue(rx)
+ * @urbdq_db_vec: Queue number of URBDQ1
+ * @tr_msi_vec: Transfer Ring MSI-X Vector
+ * @cr_msi_vec: Completion Ring MSI-X Vector
+ * @dbgc_addr: DBGC first fragment address
+ * @dbgc_size: DBGC buffer size
+ * @early_enable: Enarly debug enable
+ * @dbg_output_mode: Debug output mode
+ * Bit[4] DBGC O/P { 0=SRAM, 1=DRAM(not relevant for NPK) }
+ * Bit[5] DBGC I/P { 0=BDBG, 1=DBGI }
+ * Bits[6:7] DBGI O/P(relevant if bit[5] = 1)
+ * 0=BT DBGC, 1=WiFi DBGC, 2=NPK }
+ * @dbg_preset: Debug preset
+ * @ext_addr: Address of context information extension
+ * @ext_size: Size of context information part
+ *
+ * Total 38 DWords
+ */
+struct ctx_info {
+ u16 version;
+ u16 size;
+ u32 config;
+ u32 reserved_dw02;
+ u32 reserved_dw03;
+ u64 addr_tr_hia;
+ u64 addr_tr_tia;
+ u64 addr_cr_hia;
+ u64 addr_cr_tia;
+ u16 num_tr_ia;
+ u16 num_cr_ia;
+ u32 rbd_size:4,
+ reserved_dw13:28;
+ u64 addr_tfdq;
+ u64 addr_urbdq0;
+ u16 num_tfdq;
+ u16 num_urbdq0;
+ u16 tfdq_db_vec;
+ u16 urbdq0_db_vec;
+ u64 addr_frbdq;
+ u64 addr_urbdq1;
+ u16 num_frbdq;
+ u16 frbdq_db_vec;
+ u16 num_urbdq1;
+ u16 urbdq_db_vec;
+ u16 tr_msi_vec;
+ u16 cr_msi_vec;
+ u32 reserved_dw27;
+ u64 dbgc_addr;
+ u32 dbgc_size;
+ u32 early_enable:1,
+ reserved_dw31:3,
+ dbg_output_mode:4,
+ dbg_preset:8,
+ reserved2_dw31:16;
+ u64 ext_addr;
+ u32 ext_size;
+ u32 test_param;
+ u32 reserved_dw36;
+ u32 reserved_dw37;
+} __packed;
+
+/* Transfer Descriptor for TX
+ * @type: Not in use. Set to 0x0
+ * @size: Size of data in the buffer
+ * @addr: DMA Address of buffer
+ */
+struct tfd {
+ u8 type;
+ u16 size;
+ u8 reserved;
+ u64 addr;
+ u32 reserved1;
+} __packed;
+
+/* URB Descriptor for TX
+ * @tfd_index: Index of TFD in TFDQ + 1
+ * @num_txq: Queue index of TFD Queue
+ * @cmpl_count: Completion count. Always 0x01
+ * @immediate_cmpl: Immediate completion flag: Always 0x01
+ */
+struct urbd0 {
+ u32 tfd_index:16,
+ num_txq:8,
+ cmpl_count:4,
+ reserved:3,
+ immediate_cmpl:1;
+} __packed;
+
+/* FRB Descriptor for RX
+ * @tag: RX buffer tag (index of RX buffer queue)
+ * @addr: Address of buffer
+ */
+struct frbd {
+ u32 tag:16,
+ reserved:16;
+ u32 reserved2;
+ u64 addr;
+} __packed;
+
+/* URB Descriptor for RX
+ * @frbd_tag: Tag from FRBD
+ * @status: Status
+ */
+struct urbd1 {
+ u32 frbd_tag:16,
+ status:1,
+ reserved:14,
+ fixed:1;
+} __packed;
+
+/* RFH header in RX packet
+ * @packet_len: Length of the data in the buffer
+ * @rxq: RX Queue number
+ * @cmd_id: Command ID. Not in Use
+ */
+struct rfh_hdr {
+ u64 packet_len:16,
+ rxq:6,
+ reserved:10,
+ cmd_id:16,
+ reserved1:16;
+} __packed;
+
+/* Internal data buffer
+ * @data: pointer to the data buffer
+ * @p_addr: physical address of data buffer
+ */
+struct data_buf {
+ u8 *data;
+ dma_addr_t data_p_addr;
+};
+
+/* Index Array */
+struct ia {
+ dma_addr_t tr_hia_p_addr;
+ u16 *tr_hia;
+ dma_addr_t tr_tia_p_addr;
+ u16 *tr_tia;
+ dma_addr_t cr_hia_p_addr;
+ u16 *cr_hia;
+ dma_addr_t cr_tia_p_addr;
+ u16 *cr_tia;
+};
+
+/* Structure for TX Queue
+ * @count: Number of descriptors
+ * @tfds: Array of TFD
+ * @urbd0s: Array of URBD0
+ * @buf: Array of data_buf structure
+ */
+struct txq {
+ u16 count;
+
+ dma_addr_t tfds_p_addr;
+ struct tfd *tfds;
+
+ dma_addr_t urbd0s_p_addr;
+ struct urbd0 *urbd0s;
+
+ dma_addr_t buf_p_addr;
+ void *buf_v_addr;
+ struct data_buf *bufs;
+};
+
+/* Structure for RX Queue
+ * @count: Number of descriptors
+ * @frbds: Array of FRBD
+ * @urbd1s: Array of URBD1
+ * @buf: Array of data_buf structure
+ */
+struct rxq {
+ u16 count;
+
+ dma_addr_t frbds_p_addr;
+ struct frbd *frbds;
+
+ dma_addr_t urbd1s_p_addr;
+ struct urbd1 *urbd1s;
+
+ dma_addr_t buf_p_addr;
+ void *buf_v_addr;
+ struct data_buf *bufs;
+};
+
+/* Structure for DRAM Buffer
+ * @count: Number of descriptors
+ * @buf: Array of data_buf structure
+ */
+struct btintel_pcie_dbgc {
+ u16 count;
+
+ void *frag_v_addr;
+ dma_addr_t frag_p_addr;
+ u16 frag_size;
+
+ dma_addr_t buf_p_addr;
+ void *buf_v_addr;
+ struct data_buf *bufs;
+};
+
+struct btintel_pcie_dump_header {
+ const char *driver_name;
+ u32 cnvi_top;
+ u32 cnvr_top;
+ u16 fw_timestamp;
+ u8 fw_build_type;
+ u32 fw_build_num;
+ u32 fw_git_sha1;
+ u32 cnvi_bt;
+ u32 write_ptr;
+ u32 wrap_ctr;
+ u16 trigger_reason;
+ int state;
+};
+
+/* struct btintel_pcie_data
+ * @pdev: pci device
+ * @hdev: hdev device
+ * @flags: driver state
+ * @irq_lock: spinlock for MSI-X
+ * @hci_rx_lock: spinlock for HCI RX flow
+ * @base_addr: pci base address (from BAR)
+ * @msix_entries: array of MSI-X entries
+ * @msix_enabled: true if MSI-X is enabled;
+ * @alloc_vecs: number of interrupt vectors allocated
+ * @def_irq: default irq for all causes
+ * @fh_init_mask: initial unmasked rxq causes
+ * @hw_init_mask: initial unmaksed hw causes
+ * @boot_stage_cache: cached value of boot stage register
+ * @img_resp_cache: cached value of image response register
+ * @cnvi: CNVi register value
+ * @cnvr: CNVr register value
+ * @gp0_received: condition for gp0 interrupt
+ * @gp0_wait_q: wait_q for gp0 interrupt
+ * @tx_wait_done: condition for tx interrupt
+ * @tx_wait_q: wait_q for tx interrupt
+ * @workqueue: workqueue for RX work
+ * @rx_skb_q: SKB queue for RX packet
+ * @rx_work: RX work struct to process the RX packet in @rx_skb_q
+ * @dma_pool: DMA pool for descriptors, index array and ci
+ * @dma_p_addr: DMA address for pool
+ * @dma_v_addr: address of pool
+ * @ci_p_addr: DMA address for CI struct
+ * @ci: CI struct
+ * @ia: Index Array struct
+ * @txq: TX Queue struct
+ * @rxq: RX Queue struct
+ * @alive_intr_ctxt: Alive interrupt context
+ * @pm_sx_event: PM event on which system got suspended
+ */
+struct btintel_pcie_data {
+ struct pci_dev *pdev;
+ struct hci_dev *hdev;
+
+ unsigned long flags;
+ /* lock used in MSI-X interrupt */
+ spinlock_t irq_lock;
+ /* lock to serialize rx events */
+ spinlock_t hci_rx_lock;
+
+ void __iomem *base_addr;
+
+ struct msix_entry msix_entries[BTINTEL_PCIE_MSIX_VEC_MAX];
+ bool msix_enabled;
+ u32 alloc_vecs;
+ u32 def_irq;
+
+ u32 fh_init_mask;
+ u32 hw_init_mask;
+
+ u32 boot_stage_cache;
+ u32 img_resp_cache;
+
+ u32 cnvi;
+ u32 cnvr;
+
+ bool gp0_received;
+ wait_queue_head_t gp0_wait_q;
+
+ bool tx_wait_done;
+ wait_queue_head_t tx_wait_q;
+
+ struct workqueue_struct *workqueue;
+ struct sk_buff_head rx_skb_q;
+ struct work_struct rx_work;
+
+ struct dma_pool *dma_pool;
+ dma_addr_t dma_p_addr;
+ void *dma_v_addr;
+
+ dma_addr_t ci_p_addr;
+ struct ctx_info *ci;
+ struct ia ia;
+ struct txq txq;
+ struct rxq rxq;
+ u32 alive_intr_ctxt;
+ struct btintel_pcie_dbgc dbgc;
+ struct btintel_pcie_dump_header dmp_hdr;
+ u8 pm_sx_event;
+};
+
+static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
+ u32 offset)
+{
+ return ioread32(data->base_addr + offset);
+}
+
+static inline void btintel_pcie_wr_reg8(struct btintel_pcie_data *data,
+ u32 offset, u8 val)
+{
+ iowrite8(val, data->base_addr + offset);
+}
+
+static inline void btintel_pcie_wr_reg32(struct btintel_pcie_data *data,
+ u32 offset, u32 val)
+{
+ iowrite32(val, data->base_addr + offset);
+}
+
+static inline void btintel_pcie_set_reg_bits(struct btintel_pcie_data *data,
+ u32 offset, u32 bits)
+{
+ u32 r;
+
+ r = ioread32(data->base_addr + offset);
+ r |= bits;
+ iowrite32(r, data->base_addr + offset);
+}
+
+static inline void btintel_pcie_clr_reg_bits(struct btintel_pcie_data *data,
+ u32 offset, u32 bits)
+{
+ u32 r;
+
+ r = ioread32(data->base_addr + offset);
+ r &= ~bits;
+ iowrite32(r, data->base_addr + offset);
+}
+
+static inline u32 btintel_pcie_rd_dev_mem(struct btintel_pcie_data *data,
+ u32 addr)
+{
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_PRPH_DEV_ADDR_REG, addr);
+ return btintel_pcie_rd_reg32(data, BTINTEL_PCIE_PRPH_DEV_RD_REG);
+}
+
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 9658b33c824a..e26b07a9387d 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/string_choices.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <linux/mmc/sdio_func.h>
@@ -88,7 +89,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
else
adapter->psmode = 0;
BT_DBG("PS Mode:%s",
- (adapter->psmode) ? "Enable" : "Disable");
+ str_enable_disable(adapter->psmode));
} else {
BT_DBG("PS Mode command failed");
}
@@ -121,13 +122,6 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
((event->data[2] == MODULE_BROUGHT_UP) ||
(event->data[2] == MODULE_ALREADY_UP)) ?
"Bring-up succeed" : "Bring-up failed");
-
- if (event->length > 3 && event->data[3])
- priv->btmrvl_dev.dev_type = HCI_AMP;
- else
- priv->btmrvl_dev.dev_type = HCI_PRIMARY;
-
- BT_DBG("dev_type: %d", priv->btmrvl_dev.dev_type);
} else if (priv->btmrvl_dev.sendcmdflag &&
event->data[1] == MODULE_SHUTDOWN_REQ) {
BT_DBG("EVENT:%s", (event->data[2]) ?
@@ -686,8 +680,6 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
hdev->wakeup = btmrvl_wakeup;
SET_HCIDEV_DEV(hdev, &card->func->dev);
- hdev->dev_type = priv->btmrvl_dev.dev_type;
-
ret = hci_register_dev(hdev);
if (ret < 0) {
BT_ERR("Can not register HCI device");
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index ba057ebfda5c..93932a0d8625 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -40,7 +40,7 @@ static struct memory_type_mapping mem_type_mapping_tbl[] = {
{"EXTLAST", NULL, 0, 0xFE},
};
-static const struct of_device_id btmrvl_sdio_of_match_table[] = {
+static const struct of_device_id btmrvl_sdio_of_match_table[] __maybe_unused = {
{ .compatible = "marvell,sd8897-bt" },
{ .compatible = "marvell,sd8997-bt" },
{ }
@@ -92,7 +92,7 @@ static int btmrvl_sdio_probe_of(struct device *dev,
} else {
ret = devm_request_irq(dev, cfg->irq_bt,
btmrvl_wake_irq_bt,
- 0, "bt_wake", card);
+ IRQF_NO_AUTOEN, "bt_wake", card);
if (ret) {
dev_err(dev,
"Failed to request irq_bt %d (%d)\n",
@@ -100,8 +100,9 @@ static int btmrvl_sdio_probe_of(struct device *dev,
}
/* Configure wakeup (enabled by default) */
- device_init_wakeup(dev, true);
- disable_irq(cfg->irq_bt);
+ ret = devm_device_init_wakeup(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init wakeup\n");
}
}
@@ -1736,7 +1737,6 @@ static struct sdio_driver bt_mrvl_sdio = {
.probe = btmrvl_sdio_probe,
.remove = btmrvl_sdio_remove,
.drv = {
- .owner = THIS_MODULE,
.coredump = btmrvl_sdio_coredump,
.pm = &btmrvl_sdio_pm_ops,
}
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 809762d64fc6..a8c520dc09e1 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -4,6 +4,9 @@
*/
#include <linux/module.h>
#include <linux/firmware.h>
+#include <linux/usb.h>
+#include <linux/iopoll.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -19,6 +22,9 @@
#define MTK_SEC_MAP_COMMON_SIZE 12
#define MTK_SEC_MAP_NEED_SEND_SIZE 52
+/* It is for mt79xx iso data transmission setting */
+#define MTK_ISO_THRESHOLD 264
+
struct btmtk_patch_header {
u8 datetime[16];
u8 platform[4];
@@ -53,10 +59,79 @@ struct btmtk_section_map {
};
} __packed;
+static void btmtk_coredump(struct hci_dev *hdev)
+{
+ int err;
+
+ err = __hci_cmd_send(hdev, 0xfd5b, 0, NULL);
+ if (err < 0)
+ bt_dev_err(hdev, "Coredump failed (%d)", err);
+}
+
+static void btmtk_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ char buf[80];
+
+ snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
+ data->dev_id);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
+ data->cd_info.fw_version);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Driver: %s\n",
+ data->cd_info.driver_name);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Vendor: MediaTek\n");
+ skb_put_data(skb, buf, strlen(buf));
+}
+
+static void btmtk_coredump_notify(struct hci_dev *hdev, int state)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+
+ switch (state) {
+ case HCI_DEVCOREDUMP_IDLE:
+ data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
+ break;
+ case HCI_DEVCOREDUMP_ACTIVE:
+ data->cd_info.state = HCI_DEVCOREDUMP_ACTIVE;
+ break;
+ case HCI_DEVCOREDUMP_TIMEOUT:
+ case HCI_DEVCOREDUMP_ABORT:
+ case HCI_DEVCOREDUMP_DONE:
+ data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
+ btmtk_reset_sync(hdev);
+ break;
+ }
+}
+
+void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
+ u32 fw_flavor)
+{
+ if (dev_id == 0x7925)
+ snprintf(buf, size,
+ "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
+ dev_id & 0xffff, dev_id & 0xffff, (fw_ver & 0xff) + 1);
+ else if (dev_id == 0x7961 && fw_flavor)
+ snprintf(buf, size,
+ "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
+ dev_id & 0xffff, (fw_ver & 0xff) + 1);
+ else
+ snprintf(buf, size,
+ "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
+ dev_id & 0xffff, (fw_ver & 0xff) + 1);
+}
+EXPORT_SYMBOL_GPL(btmtk_fw_get_filename);
+
int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
wmt_cmd_sync_func_t wmt_cmd_sync)
{
struct btmtk_hci_wmt_params wmt_params;
+ struct btmtk_patch_header *hdr;
struct btmtk_global_desc *globaldesc = NULL;
struct btmtk_section_map *sectionmap;
const struct firmware *fw;
@@ -75,9 +150,13 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
fw_ptr = fw->data;
fw_bin_ptr = fw_ptr;
+ hdr = (struct btmtk_patch_header *)fw_ptr;
globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
section_num = le32_to_cpu(globaldesc->section_num);
+ bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s",
+ le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
+
for (i = 0; i < section_num; i++) {
first_block = 1;
fw_ptr = fw_bin_ptr;
@@ -245,7 +324,7 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
wmt_params.data = NULL;
wmt_params.status = NULL;
- /* Activate funciton the firmware providing to */
+ /* Activate function the firmware providing to */
err = wmt_cmd_sync(hdev, &wmt_params);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
@@ -280,6 +359,1133 @@ int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
}
EXPORT_SYMBOL_GPL(btmtk_set_bdaddr);
+void btmtk_reset_sync(struct hci_dev *hdev)
+{
+ struct btmtk_data *reset_work = hci_get_priv(hdev);
+ int err;
+
+ hci_dev_lock(hdev);
+
+ err = hci_cmd_sync_queue(hdev, reset_work->reset_sync, NULL, NULL);
+ if (err)
+ bt_dev_err(hdev, "failed to reset (%d)", err);
+
+ hci_dev_unlock(hdev);
+}
+EXPORT_SYMBOL_GPL(btmtk_reset_sync);
+
+int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
+ u32 fw_version)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+
+ if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
+ return -EOPNOTSUPP;
+
+ data->cd_info.fw_version = fw_version;
+ data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
+ data->cd_info.driver_name = name;
+
+ return hci_devcd_register(hdev, btmtk_coredump, btmtk_coredump_hdr,
+ btmtk_coredump_notify);
+}
+EXPORT_SYMBOL_GPL(btmtk_register_coredump);
+
+int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int err;
+ bool complete = false;
+
+ if (!IS_ENABLED(CONFIG_DEV_COREDUMP)) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ switch (data->cd_info.state) {
+ case HCI_DEVCOREDUMP_IDLE:
+ err = hci_devcd_init(hdev, MTK_COREDUMP_SIZE);
+ if (err < 0) {
+ kfree_skb(skb);
+ break;
+ }
+ data->cd_info.cnt = 0;
+
+ /* It is supposed coredump can be done within 5 seconds */
+ schedule_delayed_work(&hdev->dump.dump_timeout,
+ msecs_to_jiffies(5000));
+ fallthrough;
+ case HCI_DEVCOREDUMP_ACTIVE:
+ default:
+ /* Mediatek coredump data would be more than MTK_COREDUMP_NUM */
+ if (data->cd_info.cnt >= MTK_COREDUMP_NUM &&
+ skb->len > MTK_COREDUMP_END_LEN)
+ if (!memcmp((char *)&skb->data[skb->len - MTK_COREDUMP_END_LEN],
+ MTK_COREDUMP_END, MTK_COREDUMP_END_LEN - 1))
+ complete = true;
+
+ err = hci_devcd_append(hdev, skb);
+ if (err < 0)
+ break;
+ data->cd_info.cnt++;
+
+ if (complete) {
+ bt_dev_info(hdev, "Mediatek coredump end");
+ hci_devcd_complete(hdev);
+ }
+
+ break;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(btmtk_process_coredump);
+
+#if IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK)
+static void btmtk_usb_wmt_recv(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btmtk_data *data = hci_get_priv(hdev);
+ struct sk_buff *skb;
+ int err;
+
+ if (urb->status == 0 && urb->actual_length > 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ /* WMT event shouldn't be fragmented and the size should be
+ * less than HCI_WMT_MAX_EVENT_SIZE.
+ */
+ skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ hdev->stat.err_rx++;
+ kfree(urb->setup_packet);
+ return;
+ }
+
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ skb_put_data(skb, urb->transfer_buffer, urb->actual_length);
+
+ /* When someone waits for the WMT event, the skb is being cloned
+ * and being processed the events from there then.
+ */
+ if (test_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags)) {
+ data->evt_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!data->evt_skb) {
+ kfree_skb(skb);
+ kfree(urb->setup_packet);
+ return;
+ }
+ }
+
+ err = hci_recv_frame(hdev, skb);
+ if (err < 0) {
+ kfree_skb(data->evt_skb);
+ data->evt_skb = NULL;
+ kfree(urb->setup_packet);
+ return;
+ }
+
+ if (test_and_clear_bit(BTMTK_TX_WAIT_VND_EVT,
+ &data->flags)) {
+ /* Barrier to sync with other CPUs */
+ smp_mb__after_atomic();
+ wake_up_bit(&data->flags,
+ BTMTK_TX_WAIT_VND_EVT);
+ }
+ kfree(urb->setup_packet);
+ return;
+ } else if (urb->status == -ENOENT) {
+ /* Avoid suspend failed when usb_kill_urb */
+ return;
+ }
+
+ usb_mark_last_busy(data->udev);
+
+ /* The URB complete handler is still called with urb->actual_length = 0
+ * when the event is not available, so we should keep re-submitting
+ * URB until WMT event returns, Also, It's necessary to wait some time
+ * between the two consecutive control URBs to relax the target device
+ * to generate the event. Otherwise, the WMT event cannot return from
+ * the device successfully.
+ */
+ udelay(500);
+
+ usb_anchor_urb(urb, data->ctrl_anchor);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ kfree(urb->setup_packet);
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
+ urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ struct usb_ctrlrequest *dr;
+ unsigned char *buf;
+ int err, size = 64;
+ unsigned int pipe;
+ struct urb *urb;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
+ dr->bRequest = 1;
+ dr->wIndex = cpu_to_le16(0);
+ dr->wValue = cpu_to_le16(48);
+ dr->wLength = cpu_to_le16(size);
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ kfree(dr);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvctrlpipe(data->udev, 0);
+
+ usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
+ buf, size, btmtk_usb_wmt_recv, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, data->ctrl_anchor);
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ bt_dev_err(hdev, "urb %p submission failed (%d)",
+ urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
+ struct btmtk_hci_wmt_params *wmt_params)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
+ u32 hlen, status = BTMTK_WMT_INVALID;
+ struct btmtk_hci_wmt_evt *wmt_evt;
+ struct btmtk_hci_wmt_cmd *wc;
+ struct btmtk_wmt_hdr *hdr;
+ int err;
+
+ /* Send the WMT command and wait until the WMT event returns */
+ hlen = sizeof(*hdr) + wmt_params->dlen;
+ if (hlen > 255)
+ return -EINVAL;
+
+ wc = kzalloc(hlen, GFP_KERNEL);
+ if (!wc)
+ return -ENOMEM;
+
+ hdr = &wc->hdr;
+ hdr->dir = 1;
+ hdr->op = wmt_params->op;
+ hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
+ hdr->flag = wmt_params->flag;
+ memcpy(wc->data, wmt_params->data, wmt_params->dlen);
+
+ set_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags);
+
+ /* WMT cmd/event doesn't follow up the generic HCI cmd/event handling,
+ * it needs constantly polling control pipe until the host received the
+ * WMT event, thus, we should require to specifically acquire PM counter
+ * on the USB to prevent the interface from entering auto suspended
+ * while WMT cmd/event in progress.
+ */
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ goto err_free_wc;
+
+ err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
+
+ if (err < 0) {
+ clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags);
+ usb_autopm_put_interface(data->intf);
+ goto err_free_wc;
+ }
+
+ /* Submit control IN URB on demand to process the WMT event */
+ err = btmtk_usb_submit_wmt_recv_urb(hdev);
+
+ usb_autopm_put_interface(data->intf);
+
+ if (err < 0)
+ goto err_free_wc;
+
+ /* The vendor specific WMT commands are all answered by a vendor
+ * specific event and will have the Command Status or Command
+ * Complete as with usual HCI command flow control.
+ *
+ * After sending the command, wait for BTUSB_TX_WAIT_VND_EVT
+ * state to be cleared. The driver specific event receive routine
+ * will clear that state and with that indicate completion of the
+ * WMT command.
+ */
+ err = wait_on_bit_timeout(&data->flags, BTMTK_TX_WAIT_VND_EVT,
+ TASK_UNINTERRUPTIBLE, HCI_INIT_TIMEOUT);
+
+ if (err) {
+ bt_dev_err(hdev, "Execution of wmt command timed out");
+ clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags);
+ err = -ETIMEDOUT;
+ goto err_free_wc;
+ }
+
+ if (data->evt_skb == NULL)
+ goto err_free_wc;
+
+ /* Parse and handle the return WMT event */
+ wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data;
+ if (wmt_evt->whdr.op != hdr->op) {
+ bt_dev_err(hdev, "Wrong op received %d expected %d",
+ wmt_evt->whdr.op, hdr->op);
+ err = -EIO;
+ goto err_free_skb;
+ }
+
+ switch (wmt_evt->whdr.op) {
+ case BTMTK_WMT_SEMAPHORE:
+ if (wmt_evt->whdr.flag == 2)
+ status = BTMTK_WMT_PATCH_UNDONE;
+ else
+ status = BTMTK_WMT_PATCH_DONE;
+ break;
+ case BTMTK_WMT_FUNC_CTRL:
+ wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
+ if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
+ status = BTMTK_WMT_ON_DONE;
+ else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420)
+ status = BTMTK_WMT_ON_PROGRESS;
+ else
+ status = BTMTK_WMT_ON_UNDONE;
+ break;
+ case BTMTK_WMT_PATCH_DWNLD:
+ if (wmt_evt->whdr.flag == 2)
+ status = BTMTK_WMT_PATCH_DONE;
+ else if (wmt_evt->whdr.flag == 1)
+ status = BTMTK_WMT_PATCH_PROGRESS;
+ else
+ status = BTMTK_WMT_PATCH_UNDONE;
+ break;
+ }
+
+ if (wmt_params->status)
+ *wmt_params->status = status;
+
+err_free_skb:
+ kfree_skb(data->evt_skb);
+ data->evt_skb = NULL;
+err_free_wc:
+ kfree(wc);
+ return err;
+}
+
+static int btmtk_usb_func_query(struct hci_dev *hdev)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ int status, err;
+ u8 param = 0;
+
+ /* Query whether the function is enabled */
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 4;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = &status;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query function status (%d)", err);
+ return err;
+ }
+
+ return status;
+}
+
+static int btmtk_usb_uhw_reg_write(struct hci_dev *hdev, u32 reg, u32 val)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int pipe, err;
+ void *buf;
+
+ buf = kzalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ put_unaligned_le32(val, buf);
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x02,
+ 0x5E,
+ reg >> 16, reg & 0xffff,
+ buf, 4, USB_CTRL_SET_TIMEOUT);
+ if (err < 0)
+ bt_dev_err(hdev, "Failed to write uhw reg(%d)", err);
+
+ kfree(buf);
+
+ return err;
+}
+
+static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int pipe, err;
+ void *buf;
+
+ buf = kzalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pipe = usb_rcvctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x01,
+ 0xDE,
+ reg >> 16, reg & 0xffff,
+ buf, 4, USB_CTRL_GET_TIMEOUT);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
+ goto err_free_buf;
+ }
+
+ *val = get_unaligned_le32(buf);
+ bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
+
+err_free_buf:
+ kfree(buf);
+
+ return err;
+}
+
+static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int pipe, err, size = sizeof(u32);
+ void *buf;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pipe = usb_rcvctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x63,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ reg >> 16, reg & 0xffff,
+ buf, size, USB_CTRL_GET_TIMEOUT);
+ if (err < 0)
+ goto err_free_buf;
+
+ *val = get_unaligned_le32(buf);
+
+err_free_buf:
+ kfree(buf);
+
+ return err;
+}
+
+static int btmtk_usb_id_get(struct hci_dev *hdev, u32 reg, u32 *id)
+{
+ return btmtk_usb_reg_read(hdev, reg, id);
+}
+
+static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
+{
+ u32 val = 0;
+
+ btmtk_usb_uhw_reg_read(hdev, MTK_BT_MISC, &val);
+
+ return val & MTK_BT_RST_DONE;
+}
+
+int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
+{
+ u32 val;
+ int err;
+
+ if (dev_id == 0x7922) {
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
+ if (err < 0)
+ return err;
+ val |= 0x00002020;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT, 0x00010001);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
+ if (err < 0)
+ return err;
+ val |= BIT(0);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
+ if (err < 0)
+ return err;
+ msleep(100);
+ } else if (dev_id == 0x7925) {
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
+ if (err < 0)
+ return err;
+ val |= (1 << 5);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
+ if (err < 0)
+ return err;
+ val &= 0xFFFF00FF;
+ val |= (1 << 13);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT, 0x00010001);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
+ if (err < 0)
+ return err;
+ val |= (1 << 0);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
+ if (err < 0)
+ return err;
+ msleep(100);
+ } else {
+ /* It's Device EndPoint Reset Option Register */
+ bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val);
+ if (err < 0)
+ return err;
+ /* Reset the bluetooth chip via USB interface. */
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
+ if (err < 0)
+ return err;
+ /* MT7921 need to delay 20ms between toggle reset bit */
+ msleep(20);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 0);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
+ if (err < 0)
+ return err;
+ }
+
+ err = readx_poll_timeout(btmtk_usb_reset_done, hdev, val,
+ val & MTK_BT_RST_DONE, 20000, 1000000);
+ if (err < 0)
+ bt_dev_err(hdev, "Reset timeout");
+
+ if (dev_id == 0x7922) {
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
+ if (err < 0)
+ return err;
+ }
+
+ err = btmtk_usb_id_get(hdev, 0x70010200, &val);
+ if (err < 0 || !val)
+ bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_subsys_reset);
+
+int btmtk_usb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
+
+ switch (handle) {
+ case 0xfc6f: /* Firmware dump from device */
+ /* When the firmware hangs, the device can no longer
+ * suspend and thus disable auto-suspend.
+ */
+ usb_disable_autosuspend(data->udev);
+
+ /* We need to forward the diagnostic packet to userspace daemon
+ * for backward compatibility, so we have to clone the packet
+ * extraly for the in-kernel coredump support.
+ */
+ if (IS_ENABLED(CONFIG_DEV_COREDUMP)) {
+ struct sk_buff *skb_cd = skb_clone(skb, GFP_ATOMIC);
+
+ if (skb_cd)
+ btmtk_process_coredump(hdev, skb_cd);
+ }
+
+ fallthrough;
+ case 0x05ff: /* Firmware debug logging 1 */
+ case 0x05fe: /* Firmware debug logging 2 */
+ return hci_recv_diag(hdev, skb);
+ }
+
+ return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_recv_acl);
+
+static int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ if (skb->len > MTK_ISO_THRESHOLD)
+ return -EINVAL;
+
+ if (skb_pad(skb, MTK_ISO_THRESHOLD - skb->len))
+ return -ENOMEM;
+
+ __skb_put(skb, MTK_ISO_THRESHOLD - skb->len);
+
+ return 0;
+}
+
+static int __set_mtk_intr_interface(struct hci_dev *hdev)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ struct usb_interface *intf = btmtk_data->isopkt_intf;
+ int i, err;
+
+ if (!btmtk_data->isopkt_intf)
+ return -ENODEV;
+
+ err = usb_set_interface(btmtk_data->udev, MTK_ISO_IFNUM, 1);
+ if (err < 0) {
+ bt_dev_err(hdev, "setting interface failed (%d)", -err);
+ return err;
+ }
+
+ btmtk_data->isopkt_tx_ep = NULL;
+ btmtk_data->isopkt_rx_ep = NULL;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ struct usb_endpoint_descriptor *ep_desc;
+
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!btmtk_data->isopkt_tx_ep &&
+ usb_endpoint_is_int_out(ep_desc)) {
+ btmtk_data->isopkt_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!btmtk_data->isopkt_rx_ep &&
+ usb_endpoint_is_int_in(ep_desc)) {
+ btmtk_data->isopkt_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!btmtk_data->isopkt_tx_ep ||
+ !btmtk_data->isopkt_rx_ep) {
+ bt_dev_err(hdev, "invalid interrupt descriptors");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb,
+ usb_complete_t tx_complete)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ struct urb *urb;
+ unsigned int pipe;
+
+ if (!btmtk_data->isopkt_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ if (btmtk_isopkt_pad(hdev, skb))
+ return ERR_PTR(-EINVAL);
+
+ pipe = usb_sndintpipe(btmtk_data->udev,
+ btmtk_data->isopkt_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, btmtk_data->udev, pipe,
+ skb->data, skb->len, tx_complete,
+ skb, btmtk_data->isopkt_tx_ep->bInterval);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+EXPORT_SYMBOL_GPL(alloc_mtk_intr_urb);
+
+static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ struct sk_buff *skb;
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&btmtk_data->isorxlock, flags);
+ skb = btmtk_data->isopkt_skb;
+
+ while (count) {
+ int len;
+
+ if (!skb) {
+ skb = bt_skb_alloc(HCI_MAX_ISO_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ break;
+ }
+
+ hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
+ hci_skb_expect(skb) = HCI_ISO_HDR_SIZE;
+ }
+
+ len = min_t(uint, hci_skb_expect(skb), count);
+ skb_put_data(skb, buffer, len);
+
+ count -= len;
+ buffer += len;
+ hci_skb_expect(skb) -= len;
+
+ if (skb->len == HCI_ISO_HDR_SIZE) {
+ __le16 dlen = ((struct hci_iso_hdr *)skb->data)->dlen;
+
+ /* Complete ISO header */
+ hci_skb_expect(skb) = __le16_to_cpu(dlen);
+
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+ }
+
+ if (!hci_skb_expect(skb)) {
+ /* Complete frame */
+ hci_recv_frame(hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ btmtk_data->isopkt_skb = skb;
+ spin_unlock_irqrestore(&btmtk_data->isorxlock, flags);
+
+ return err;
+}
+
+static void btmtk_intr_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (hdev->suspended)
+ return;
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (btmtk_recv_isopkt(hdev, urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ bt_dev_err(hdev, "corrupted iso packet");
+ hdev->stat.err_rx++;
+ }
+ } else if (urb->status == -ENOENT) {
+ /* Avoid suspend failed when usb_kill_urb */
+ return;
+ }
+
+ usb_mark_last_busy(btmtk_data->udev);
+ usb_anchor_urb(urb, &btmtk_data->isopkt_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
+ urb, -err);
+ if (err != -EPERM)
+ hci_cmd_sync_cancel(hdev, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btmtk_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ unsigned char *buf;
+ unsigned int pipe;
+ struct urb *urb;
+ int err, size;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!btmtk_data->isopkt_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+ size = le16_to_cpu(btmtk_data->isopkt_rx_ep->wMaxPacketSize);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(btmtk_data->udev,
+ btmtk_data->isopkt_rx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, btmtk_data->udev, pipe, buf, size,
+ btmtk_intr_complete, hdev,
+ btmtk_data->isopkt_rx_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_mark_last_busy(btmtk_data->udev);
+ usb_anchor_urb(urb, &btmtk_data->isopkt_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ bt_dev_err(hdev, "urb %p submission failed (%d)",
+ urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static int btmtk_usb_isointf_init(struct hci_dev *hdev)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ u8 iso_param[2] = { 0x08, 0x01 };
+ struct sk_buff *skb;
+ int err;
+
+ spin_lock_init(&btmtk_data->isorxlock);
+
+ __set_mtk_intr_interface(hdev);
+
+ err = btmtk_submit_intr_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
+ bt_dev_err(hdev, "ISO intf not support (%d)", err);
+ return err;
+ }
+
+ skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), iso_param,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Failed to apply iso setting (%ld)", PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+int btmtk_usb_resume(struct hci_dev *hdev)
+{
+ /* This function describes the specific additional steps taken by MediaTek
+ * when Bluetooth usb driver's resume function is called.
+ */
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+
+ /* Resubmit urb for iso data transmission */
+ if (test_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags)) {
+ if (btmtk_submit_intr_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_resume);
+
+int btmtk_usb_suspend(struct hci_dev *hdev)
+{
+ /* This function describes the specific additional steps taken by MediaTek
+ * when Bluetooth usb driver's suspend function is called.
+ */
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+
+ /* Stop urb anchor for iso data transmission */
+ if (test_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags))
+ usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_suspend);
+
+int btmtk_usb_setup(struct hci_dev *hdev)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ struct btmtk_hci_wmt_params wmt_params;
+ ktime_t calltime, delta, rettime;
+ struct btmtk_tci_sleep tci_sleep;
+ unsigned long long duration;
+ struct sk_buff *skb;
+ const char *fwname;
+ int err, status;
+ u32 dev_id = 0;
+ char fw_bin_name[64];
+ u32 fw_version = 0, fw_flavor = 0;
+ u8 param;
+
+ calltime = ktime_get();
+
+ err = btmtk_usb_id_get(hdev, 0x80000008, &dev_id);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get device id (%d)", err);
+ return err;
+ }
+
+ if (!dev_id || dev_id != 0x7663) {
+ err = btmtk_usb_id_get(hdev, 0x70010200, &dev_id);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get device id (%d)", err);
+ return err;
+ }
+ err = btmtk_usb_id_get(hdev, 0x80021004, &fw_version);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get fw version (%d)", err);
+ return err;
+ }
+ err = btmtk_usb_id_get(hdev, 0x70010020, &fw_flavor);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get fw flavor (%d)", err);
+ return err;
+ }
+ fw_flavor = (fw_flavor & 0x00000080) >> 7;
+ }
+
+ btmtk_data->dev_id = dev_id;
+
+ err = btmtk_register_coredump(hdev, btmtk_data->drv_name, fw_version);
+ if (err < 0)
+ bt_dev_err(hdev, "Failed to register coredump (%d)", err);
+
+ switch (dev_id) {
+ case 0x7663:
+ fwname = FIRMWARE_MT7663;
+ break;
+ case 0x7668:
+ fwname = FIRMWARE_MT7668;
+ break;
+ case 0x7922:
+ case 0x7925:
+ case 0x7961:
+ btmtk_fw_get_filename(fw_bin_name, sizeof(fw_bin_name), dev_id,
+ fw_version, fw_flavor);
+
+ err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
+ btmtk_usb_hci_wmt_sync);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
+ return err;
+ }
+
+ /* It's Device EndPoint Reset Option Register */
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT,
+ MTK_EP_RST_IN_OUT_OPT);
+ if (err < 0)
+ return err;
+
+ /* Enable Bluetooth protocol */
+ param = 1;
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = NULL;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+ return err;
+ }
+
+ hci_set_msft_opcode(hdev, 0xFD30);
+ hci_set_aosp_capable(hdev);
+
+ /* Set up ISO interface after protocol enabled */
+ if (test_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags)) {
+ if (!btmtk_usb_isointf_init(hdev))
+ set_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags);
+ }
+
+ goto done;
+ default:
+ bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
+ dev_id);
+ return -ENODEV;
+ }
+
+ /* Query whether the firmware is already download */
+ wmt_params.op = BTMTK_WMT_SEMAPHORE;
+ wmt_params.flag = 1;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = &status;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
+ return err;
+ }
+
+ if (status == BTMTK_WMT_PATCH_DONE) {
+ bt_dev_info(hdev, "firmware already downloaded");
+ goto ignore_setup_fw;
+ }
+
+ /* Setup a firmware which the device definitely requires */
+ err = btmtk_setup_firmware(hdev, fwname,
+ btmtk_usb_hci_wmt_sync);
+ if (err < 0)
+ return err;
+
+ignore_setup_fw:
+ err = readx_poll_timeout(btmtk_usb_func_query, hdev, status,
+ status < 0 || status != BTMTK_WMT_ON_PROGRESS,
+ 2000, 5000000);
+ /* -ETIMEDOUT happens */
+ if (err < 0)
+ return err;
+
+ /* The other errors happen in btmtk_usb_func_query */
+ if (status < 0)
+ return status;
+
+ if (status == BTMTK_WMT_ON_DONE) {
+ bt_dev_info(hdev, "function already on");
+ goto ignore_func_on;
+ }
+
+ /* Enable Bluetooth protocol */
+ param = 1;
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = NULL;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+ return err;
+ }
+
+ignore_func_on:
+ /* Apply the low power environment setup */
+ tci_sleep.mode = 0x5;
+ tci_sleep.duration = cpu_to_le16(0x640);
+ tci_sleep.host_duration = cpu_to_le16(0x640);
+ tci_sleep.host_wakeup_pin = 0;
+ tci_sleep.time_compensation = 0;
+
+ skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
+ return err;
+ }
+ kfree_skb(skb);
+
+done:
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+
+ bt_dev_info(hdev, "Device setup in %llu usecs", duration);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_setup);
+
+int btmtk_usb_shutdown(struct hci_dev *hdev)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ struct btmtk_hci_wmt_params wmt_params;
+ u8 param = 0;
+ int err;
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return err;
+
+ /* Disable the device */
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = &param;
+ wmt_params.status = NULL;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+ usb_autopm_put_interface(data->intf);
+ return err;
+ }
+
+ usb_autopm_put_interface(data->intf);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_shutdown);
+#endif
+
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION);
@@ -288,4 +1494,6 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FIRMWARE_MT7622);
MODULE_FIRMWARE(FIRMWARE_MT7663);
MODULE_FIRMWARE(FIRMWARE_MT7668);
+MODULE_FIRMWARE(FIRMWARE_MT7922);
MODULE_FIRMWARE(FIRMWARE_MT7961);
+MODULE_FIRMWARE(FIRMWARE_MT7925);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index 2a88ea8e475e..5df7c3296624 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -4,7 +4,9 @@
#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin"
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
+#define FIRMWARE_MT7922 "mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin"
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
+#define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
#define HCI_EV_WMT 0xe4
#define HCI_WMT_MAX_EVENT_SIZE 64
@@ -21,6 +23,26 @@
#define MT7921_DLSTATUS 0x7c053c10
#define BT_DL_STATE BIT(1)
+#define MTK_COREDUMP_SIZE (1024 * 1000)
+#define MTK_COREDUMP_END "coredump end"
+#define MTK_COREDUMP_END_LEN (sizeof(MTK_COREDUMP_END))
+#define MTK_COREDUMP_NUM 255
+
+/* UHW CR mapping */
+#define MTK_BT_MISC 0x70002510
+#define MTK_BT_SUBSYS_RST 0x70002610
+#define MTK_UDMA_INT_STA_BT 0x74000024
+#define MTK_UDMA_INT_STA_BT1 0x74000308
+#define MTK_BT_WDT_STATUS 0x740003A0
+#define MTK_EP_RST_OPT 0x74011890
+#define MTK_EP_RST_IN_OUT_OPT 0x00010001
+#define MTK_BT_RST_DONE 0x00000100
+#define MTK_BT_RESET_REG_CONNV3 0x70028610
+#define MTK_BT_READ_DEV_ID 0x70010200
+
+/* MediaTek ISO Interface */
+#define MTK_ISO_IFNUM 2
+
enum {
BTMTK_WMT_PATCH_DWNLD = 0x1,
BTMTK_WMT_TEST = 0x2,
@@ -119,6 +141,44 @@ struct btmtk_hci_wmt_params {
u32 *status;
};
+enum {
+ BTMTK_TX_WAIT_VND_EVT,
+ BTMTK_FIRMWARE_LOADED,
+ BTMTK_HW_RESET_ACTIVE,
+ BTMTK_ISOPKT_OVER_INTR,
+ BTMTK_ISOPKT_RUNNING,
+};
+
+typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
+
+struct btmtk_coredump_info {
+ const char *driver_name;
+ u32 fw_version;
+ u16 cnt;
+ int state;
+};
+
+struct btmtk_data {
+ const char *drv_name;
+ unsigned long flags;
+ u32 dev_id;
+ btmtk_reset_sync_func_t reset_sync;
+ struct btmtk_coredump_info cd_info;
+
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct usb_anchor *ctrl_anchor;
+ struct sk_buff *evt_skb;
+ struct usb_endpoint_descriptor *isopkt_tx_ep;
+ struct usb_endpoint_descriptor *isopkt_rx_ep;
+ struct usb_interface *isopkt_intf;
+ struct usb_anchor isopkt_anchor;
+ struct sk_buff *isopkt_skb;
+
+ /* spinlock for ISO data transmission */
+ spinlock_t isorxlock;
+};
+
typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *,
struct btmtk_hci_wmt_params *);
@@ -131,6 +191,31 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
wmt_cmd_sync_func_t wmt_cmd_sync);
+
+void btmtk_reset_sync(struct hci_dev *hdev);
+
+int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
+ u32 fw_version);
+
+int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb);
+
+void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
+ u32 fw_flavor);
+
+int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id);
+
+int btmtk_usb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb);
+
+struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb,
+ usb_complete_t tx_complete);
+
+int btmtk_usb_resume(struct hci_dev *hdev);
+
+int btmtk_usb_suspend(struct hci_dev *hdev);
+
+int btmtk_usb_setup(struct hci_dev *hdev);
+
+int btmtk_usb_shutdown(struct hci_dev *hdev);
#else
static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
@@ -139,16 +224,74 @@ static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
return -EOPNOTSUPP;
}
-static int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
- wmt_cmd_sync_func_t wmt_cmd_sync)
+static inline int btmtk_setup_firmware_79xx(struct hci_dev *hdev,
+ const char *fwname,
+ wmt_cmd_sync_func_t wmt_cmd_sync)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
+ wmt_cmd_sync_func_t wmt_cmd_sync)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void btmtk_reset_sync(struct hci_dev *hdev)
+{
+}
+
+static inline int btmtk_register_coredump(struct hci_dev *hdev,
+ const char *name, u32 fw_version)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btmtk_process_coredump(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id,
+ u32 fw_ver, u32 fw_flavor)
+{
+}
+
+static inline int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btmtk_usb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev,
+ struct sk_buff *skb,
+ usb_complete_t tx_complete)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline int btmtk_usb_resume(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btmtk_usb_suspend(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
-static int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
- wmt_cmd_sync_func_t wmt_cmd_sync)
+static inline int btmtk_usb_setup(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
+static inline int btmtk_usb_shutdown(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
#endif
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index f9a3444753c2..fba3ab6d30a5 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -10,7 +10,7 @@
*
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/atomic.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/skbuff.h>
+#include <linux/usb.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_ids.h>
@@ -28,7 +29,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include "h4_recv.h"
+#include "hci_uart.h"
#include "btmtk.h"
#define VERSION "0.1"
@@ -118,6 +119,7 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table);
#define BTMTKSDIO_FUNC_ENABLED 3
#define BTMTKSDIO_PATCH_ENABLED 4
#define BTMTKSDIO_HW_RESET_ACTIVE 5
+#define BTMTKSDIO_BT_WAKE_ENABLED 6
struct mtkbtsdio_hdr {
__le16 len;
@@ -554,7 +556,7 @@ static void btmtksdio_txrx_work(struct work_struct *work)
sdio_claim_host(bdev->func);
/* Disable interrupt */
- sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
+ sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
txrx_timeout = jiffies + 5 * HZ;
@@ -576,7 +578,7 @@ static void btmtksdio_txrx_work(struct work_struct *work)
if ((int_status & FW_MAILBOX_INT) &&
bdev->data->chipid == 0x7921) {
sdio_writel(bdev->func, PH2DSM0R_DRIVER_OWN,
- MTK_REG_PH2DSM0R, 0);
+ MTK_REG_PH2DSM0R, NULL);
}
if (int_status & FW_OWN_BACK_INT)
@@ -608,11 +610,11 @@ static void btmtksdio_txrx_work(struct work_struct *work)
} while (int_status || time_is_before_jiffies(txrx_timeout));
/* Enable interrupt */
- sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, 0);
+ if (bdev->func->irq_handler)
+ sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, NULL);
sdio_release_host(bdev->func);
- pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
}
@@ -620,8 +622,14 @@ static void btmtksdio_interrupt(struct sdio_func *func)
{
struct btmtksdio_dev *bdev = sdio_get_drvdata(func);
+ if (test_bit(BTMTKSDIO_BT_WAKE_ENABLED, &bdev->tx_state)) {
+ if (bdev->hdev->suspended)
+ pm_wakeup_event(bdev->dev, 0);
+ clear_bit(BTMTKSDIO_BT_WAKE_ENABLED, &bdev->tx_state);
+ }
+
/* Disable interrupt */
- sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
+ sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
schedule_work(&bdev->txrx_work);
}
@@ -673,7 +681,7 @@ static int btmtksdio_open(struct hci_dev *hdev)
if (err < 0)
goto err_release_irq;
- /* Explitly set write-1-clear method */
+ /* Explicitly set write-1-clear method */
val = sdio_readl(bdev->func, MTK_REG_CHCR, &err);
if (err < 0)
goto err_release_irq;
@@ -714,6 +722,10 @@ static int btmtksdio_close(struct hci_dev *hdev)
{
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
+ /* Skip btmtksdio_close if BTMTKSDIO_FUNC_ENABLED isn't set */
+ if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
+ return 0;
+
sdio_claim_host(bdev->func);
/* Disable interrupt */
@@ -1110,6 +1122,9 @@ static int btmtksdio_setup(struct hci_dev *hdev)
return err;
}
+ btmtk_fw_get_filename(fwname, sizeof(fwname), dev_id,
+ fw_version, 0);
+
snprintf(fwname, sizeof(fwname),
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
dev_id & 0xffff, (fw_version & 0xff) + 1);
@@ -1125,7 +1140,7 @@ static int btmtksdio_setup(struct hci_dev *hdev)
}
/* Enable WBS with mSBC codec */
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
/* Enable GPIO reset mechanism */
if (bdev->reset) {
@@ -1137,9 +1152,6 @@ static int btmtksdio_setup(struct hci_dev *hdev)
}
}
- /* Valid LE States quirk for MediaTek 7921 */
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
-
break;
case 0x7663:
case 0x7668:
@@ -1241,7 +1253,7 @@ static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
-static void btmtksdio_cmd_timeout(struct hci_dev *hdev)
+static void btmtksdio_reset(struct hci_dev *hdev)
{
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
u32 status;
@@ -1257,6 +1269,12 @@ static void btmtksdio_cmd_timeout(struct hci_dev *hdev)
sdio_claim_host(bdev->func);
+ /* set drv_pmctrl if BT is closed before doing reset */
+ if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) {
+ sdio_enable_func(bdev->func);
+ btmtksdio_drv_pmctrl(bdev);
+ }
+
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
skb_queue_purge(&bdev->txq);
cancel_work_sync(&bdev->txrx_work);
@@ -1272,6 +1290,12 @@ static void btmtksdio_cmd_timeout(struct hci_dev *hdev)
goto err;
}
+ /* set fw_pmctrl back if BT is closed after doing reset */
+ if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) {
+ btmtksdio_fw_pmctrl(bdev);
+ sdio_disable_func(bdev->func);
+ }
+
clear_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state);
err:
sdio_release_host(bdev->func);
@@ -1320,6 +1344,8 @@ static int btmtksdio_probe(struct sdio_func *func,
{
struct btmtksdio_dev *bdev;
struct hci_dev *hdev;
+ struct device_node *old_node;
+ bool restore_node;
int err;
bdev = devm_kzalloc(&func->dev, sizeof(*bdev), GFP_KERNEL);
@@ -1350,7 +1376,7 @@ static int btmtksdio_probe(struct sdio_func *func,
hdev->open = btmtksdio_open;
hdev->close = btmtksdio_close;
- hdev->cmd_timeout = btmtksdio_cmd_timeout;
+ hdev->reset = btmtksdio_reset;
hdev->flush = btmtksdio_flush;
hdev->setup = btmtksdio_setup;
hdev->shutdown = btmtksdio_shutdown;
@@ -1369,7 +1395,7 @@ static int btmtksdio_probe(struct sdio_func *func,
SET_HCIDEV_DEV(hdev, &func->dev);
hdev->manufacturer = 70;
- set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
sdio_set_drvdata(func, bdev);
@@ -1388,7 +1414,7 @@ static int btmtksdio_probe(struct sdio_func *func,
if (pm_runtime_enabled(bdev->dev))
pm_runtime_disable(bdev->dev);
- /* As explaination in drivers/mmc/core/sdio_bus.c tells us:
+ /* As explanation in drivers/mmc/core/sdio_bus.c tells us:
* Unbound SDIO functions are always suspended.
* During probe, the function is set active and the usage count
* is incremented. If the driver supports runtime PM,
@@ -1399,17 +1425,28 @@ static int btmtksdio_probe(struct sdio_func *func,
*/
pm_runtime_put_noidle(bdev->dev);
- err = device_init_wakeup(bdev->dev, true);
+ err = devm_device_init_wakeup(bdev->dev);
if (err)
bt_dev_err(hdev, "failed to initialize device wakeup");
- bdev->dev->of_node = of_find_compatible_node(NULL, NULL,
- "mediatek,mt7921s-bluetooth");
+ restore_node = false;
+ if (!of_device_is_compatible(bdev->dev->of_node, "mediatek,mt7921s-bluetooth")) {
+ restore_node = true;
+ old_node = bdev->dev->of_node;
+ bdev->dev->of_node = of_find_compatible_node(NULL, NULL,
+ "mediatek,mt7921s-bluetooth");
+ }
+
bdev->reset = devm_gpiod_get_optional(bdev->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(bdev->reset))
err = PTR_ERR(bdev->reset);
+ if (restore_node) {
+ of_node_put(bdev->dev->of_node);
+ bdev->dev->of_node = old_node;
+ }
+
return err;
}
@@ -1421,11 +1458,15 @@ static void btmtksdio_remove(struct sdio_func *func)
if (!bdev)
return;
+ hdev = bdev->hdev;
+
+ /* Make sure to call btmtksdio_close before removing sdio card */
+ if (test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
+ btmtksdio_close(hdev);
+
/* Be consistent the state in btmtksdio_probe */
pm_runtime_get_noresume(bdev->dev);
- hdev = bdev->hdev;
-
sdio_set_drvdata(func, NULL);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
@@ -1454,6 +1495,23 @@ static int btmtksdio_runtime_suspend(struct device *dev)
return err;
}
+static int btmtksdio_system_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct btmtksdio_dev *bdev;
+
+ bdev = sdio_get_drvdata(func);
+ if (!bdev)
+ return 0;
+
+ if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
+ return 0;
+
+ set_bit(BTMTKSDIO_BT_WAKE_ENABLED, &bdev->tx_state);
+
+ return btmtksdio_runtime_suspend(dev);
+}
+
static int btmtksdio_runtime_resume(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
@@ -1474,8 +1532,16 @@ static int btmtksdio_runtime_resume(struct device *dev)
return err;
}
-static UNIVERSAL_DEV_PM_OPS(btmtksdio_pm_ops, btmtksdio_runtime_suspend,
- btmtksdio_runtime_resume, NULL);
+static int btmtksdio_system_resume(struct device *dev)
+{
+ return btmtksdio_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops btmtksdio_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(btmtksdio_system_suspend, btmtksdio_system_resume)
+ RUNTIME_PM_OPS(btmtksdio_runtime_suspend, btmtksdio_runtime_resume, NULL)
+};
+
#define BTMTKSDIO_PM_OPS (&btmtksdio_pm_ops)
#else /* CONFIG_PM */
#define BTMTKSDIO_PM_OPS NULL
@@ -1487,7 +1553,6 @@ static struct sdio_driver btmtksdio_driver = {
.remove = btmtksdio_remove,
.id_table = btmtksdio_table,
.drv = {
- .owner = THIS_MODULE,
.pm = BTMTKSDIO_PM_OPS,
}
};
diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c
index c98691cdbbd5..27aa48ff3ac2 100644
--- a/drivers/bluetooth/btmtkuart.c
+++ b/drivers/bluetooth/btmtkuart.c
@@ -8,7 +8,7 @@
*
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/firmware.h>
@@ -17,17 +17,17 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
+#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include "h4_recv.h"
+#include "hci_uart.h"
#include "btmtk.h"
#define VERSION "0.2"
@@ -79,6 +79,7 @@ struct btmtkuart_dev {
u16 stp_dlen;
const struct btmtkuart_data *data;
+ struct hci_uart hu;
};
#define btmtkuart_is_standalone(bdev) \
@@ -316,7 +317,7 @@ mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count,
/* Resync STP when unexpected data is being read */
if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) {
- bt_dev_err(bdev->hdev, "stp format unexpect (%d, %d)",
+ bt_dev_err(bdev->hdev, "stp format unexpected (%d, %d)",
shdr->prefix, bdev->stp_dlen);
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
@@ -327,7 +328,7 @@ mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count,
if (count <= 0)
return NULL;
- /* Tranlate to how much the size of data H4 can handle so far */
+ /* Translate to how much the size of data H4 can handle so far */
*sz_h4 = min_t(int, count, bdev->stp_dlen);
/* Update the remaining size of STP packet */
@@ -337,7 +338,7 @@ mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count,
return data;
}
-static int btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
+static void btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
const unsigned char *p_left = data, *p_h4;
@@ -368,7 +369,7 @@ static int btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
sz_left -= adv;
p_left += adv;
- bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, p_h4,
+ bdev->rx_skb = h4_recv_buf(&bdev->hu, bdev->rx_skb, p_h4,
sz_h4, mtk_recv_pkts,
ARRAY_SIZE(mtk_recv_pkts));
if (IS_ERR(bdev->rx_skb)) {
@@ -376,25 +377,20 @@ static int btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
bt_dev_err(bdev->hdev,
"Frame reassembly failed (%d)", err);
bdev->rx_skb = NULL;
- return err;
+ return;
}
sz_left -= sz_h4;
p_left += sz_h4;
}
-
- return 0;
}
-static int btmtkuart_receive_buf(struct serdev_device *serdev, const u8 *data,
- size_t count)
+static size_t btmtkuart_receive_buf(struct serdev_device *serdev,
+ const u8 *data, size_t count)
{
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
- int err;
- err = btmtkuart_recv(bdev->hdev, data, count);
- if (err < 0)
- return err;
+ btmtkuart_recv(bdev->hdev, data, count);
bdev->hdev->stat.byte_rx += count;
@@ -863,6 +859,7 @@ static int btmtkuart_probe(struct serdev_device *serdev)
}
bdev->hdev = hdev;
+ bdev->hu.hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, bdev);
@@ -877,7 +874,7 @@ static int btmtkuart_probe(struct serdev_device *serdev)
SET_HCIDEV_DEV(hdev, &serdev->dev);
hdev->manufacturer = 70;
- set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
if (btmtkuart_is_standalone(bdev)) {
err = clk_prepare_enable(bdev->osc);
@@ -959,16 +956,16 @@ static void btmtkuart_remove(struct serdev_device *serdev)
hci_free_dev(hdev);
}
-static const struct btmtkuart_data mt7622_data = {
+static const struct btmtkuart_data mt7622_data __maybe_unused = {
.fwname = FIRMWARE_MT7622,
};
-static const struct btmtkuart_data mt7663_data = {
+static const struct btmtkuart_data mt7663_data __maybe_unused = {
.flags = BTMTKUART_FLAG_STANDALONE_HW,
.fwname = FIRMWARE_MT7663,
};
-static const struct btmtkuart_data mt7668_data = {
+static const struct btmtkuart_data mt7668_data __maybe_unused = {
.flags = BTMTKUART_FLAG_STANDALONE_HW,
.fwname = FIRMWARE_MT7668,
};
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
new file mode 100644
index 000000000000..3b1e9224e965
--- /dev/null
+++ b/drivers/bluetooth/btnxpuart.c
@@ -0,0 +1,2031 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NXP Bluetooth driver
+ * Copyright 2023-2025 NXP
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/serdev.h>
+#include <linux/of.h>
+#include <linux/skbuff.h>
+#include <linux/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/string_helpers.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define MANUFACTURER_NXP 37
+
+#define BTNXPUART_TX_STATE_ACTIVE 1
+#define BTNXPUART_FW_DOWNLOADING 2
+#define BTNXPUART_CHECK_BOOT_SIGNATURE 3
+#define BTNXPUART_SERDEV_OPEN 4
+#define BTNXPUART_IR_IN_PROGRESS 5
+#define BTNXPUART_FW_DOWNLOAD_ABORT 6
+#define BTNXPUART_FW_DUMP_IN_PROGRESS 7
+
+/* NXP HW err codes */
+#define BTNXPUART_IR_HW_ERR 0xb0
+
+#define FIRMWARE_W8987 "uart8987_bt.bin"
+#define FIRMWARE_W8987_OLD "uartuart8987_bt.bin"
+#define FIRMWARE_W8997 "uart8997_bt_v4.bin"
+#define FIRMWARE_W8997_OLD "uartuart8997_bt_v4.bin"
+#define FIRMWARE_W9098 "uart9098_bt_v1.bin"
+#define FIRMWARE_W9098_OLD "uartuart9098_bt_v1.bin"
+#define FIRMWARE_IW416 "uartiw416_bt.bin"
+#define FIRMWARE_IW416_OLD "uartiw416_bt_v0.bin"
+#define FIRMWARE_IW612 "uartspi_n61x_v1.bin.se"
+#define FIRMWARE_IW610 "uartspi_iw610.bin"
+#define FIRMWARE_SECURE_IW610 "uartspi_iw610.bin.se"
+#define FIRMWARE_IW624 "uartiw624_bt.bin"
+#define FIRMWARE_SECURE_IW624 "uartiw624_bt.bin.se"
+#define FIRMWARE_AW693 "uartaw693_bt.bin"
+#define FIRMWARE_SECURE_AW693 "uartaw693_bt.bin.se"
+#define FIRMWARE_AW693_A1 "uartaw693_bt_v1.bin"
+#define FIRMWARE_SECURE_AW693_A1 "uartaw693_bt_v1.bin.se"
+#define FIRMWARE_HELPER "helper_uart_3000000.bin"
+
+#define CHIP_ID_W9098 0x5c03
+#define CHIP_ID_IW416 0x7201
+#define CHIP_ID_IW612 0x7601
+#define CHIP_ID_IW624a 0x8000
+#define CHIP_ID_IW624c 0x8001
+#define CHIP_ID_AW693a0 0x8200
+#define CHIP_ID_AW693a1 0x8201
+#define CHIP_ID_IW610a0 0x8800
+#define CHIP_ID_IW610a1 0x8801
+
+#define FW_SECURE_MASK 0xc0
+#define FW_OPEN 0x00
+#define FW_AUTH_ILLEGAL 0x40
+#define FW_AUTH_PLAIN 0x80
+#define FW_AUTH_ENC 0xc0
+
+#define HCI_NXP_PRI_BAUDRATE 115200
+#define HCI_NXP_SEC_BAUDRATE_3M 3000000
+#define HCI_NXP_SEC_BAUDRATE_4M 4000000
+
+#define MAX_FW_FILE_NAME_LEN 50
+
+/* Default ps timeout period in milliseconds */
+#define PS_DEFAULT_TIMEOUT_PERIOD_MS 2000
+
+/* wakeup methods */
+#define WAKEUP_METHOD_DTR 0
+#define WAKEUP_METHOD_BREAK 1
+#define WAKEUP_METHOD_EXT_BREAK 2
+#define WAKEUP_METHOD_RTS 3
+#define WAKEUP_METHOD_GPIO 4
+#define WAKEUP_METHOD_INVALID 0xff
+
+/* power save mode status */
+#define PS_MODE_DISABLE 0
+#define PS_MODE_ENABLE 1
+
+/* Power Save Commands to ps_work_func */
+#define PS_CMD_EXIT_PS 1
+#define PS_CMD_ENTER_PS 2
+
+/* power save state */
+#define PS_STATE_AWAKE 0
+#define PS_STATE_SLEEP 1
+
+/* NXP Vendor Commands. Refer user manual UM11628 on nxp.com */
+/* Set custom BD Address */
+#define HCI_NXP_SET_BD_ADDR 0xfc22
+/* Set Auto-Sleep mode */
+#define HCI_NXP_AUTO_SLEEP_MODE 0xfc23
+/* Set Wakeup method */
+#define HCI_NXP_WAKEUP_METHOD 0xfc53
+/* Set operational baudrate */
+#define HCI_NXP_SET_OPER_SPEED 0xfc09
+/* Independent Reset (Soft Reset) */
+#define HCI_NXP_IND_RESET 0xfcfc
+/* Bluetooth vendor command: Trigger FW dump */
+#define HCI_NXP_TRIGGER_DUMP 0xfe91
+
+/* Bluetooth Power State : Vendor cmd params */
+#define BT_PS_ENABLE 0x02
+#define BT_PS_DISABLE 0x03
+
+/* Bluetooth Host Wakeup Methods */
+#define BT_HOST_WAKEUP_METHOD_NONE 0x00
+#define BT_HOST_WAKEUP_METHOD_DTR 0x01
+#define BT_HOST_WAKEUP_METHOD_BREAK 0x02
+#define BT_HOST_WAKEUP_METHOD_GPIO 0x03
+
+/* Bluetooth Chip Wakeup Methods */
+#define BT_CTRL_WAKEUP_METHOD_DSR 0x00
+#define BT_CTRL_WAKEUP_METHOD_BREAK 0x01
+#define BT_CTRL_WAKEUP_METHOD_GPIO 0x02
+#define BT_CTRL_WAKEUP_METHOD_EXT_BREAK 0x04
+#define BT_CTRL_WAKEUP_METHOD_RTS 0x05
+
+struct ps_data {
+ u8 target_ps_mode; /* ps mode to be set */
+ u8 cur_psmode; /* current ps_mode */
+ u8 ps_state; /* controller's power save state */
+ u8 ps_cmd;
+ u8 h2c_wakeupmode;
+ u8 cur_h2c_wakeupmode;
+ u8 c2h_wakeupmode;
+ u8 c2h_wakeup_gpio;
+ u8 h2c_wakeup_gpio;
+ bool driver_sent_cmd;
+ u16 h2c_ps_interval;
+ u16 c2h_ps_interval;
+ bool wakeup_source;
+ struct gpio_desc *h2c_ps_gpio;
+ s32 irq_handler;
+ struct hci_dev *hdev;
+ struct work_struct work;
+ struct timer_list ps_timer;
+ struct mutex ps_lock;
+};
+
+struct wakeup_cmd_payload {
+ u8 c2h_wakeupmode;
+ u8 c2h_wakeup_gpio;
+ u8 h2c_wakeupmode;
+ u8 h2c_wakeup_gpio;
+} __packed;
+
+struct psmode_cmd_payload {
+ u8 ps_cmd;
+ __le16 c2h_ps_interval;
+} __packed;
+
+struct btnxpuart_data {
+ const char *helper_fw_name;
+ const char *fw_name;
+ const char *fw_name_old;
+};
+
+enum bootloader_param_change {
+ not_changed,
+ cmd_sent,
+ changed
+};
+
+struct btnxpuart_dev {
+ struct hci_dev *hdev;
+ struct serdev_device *serdev;
+
+ struct work_struct tx_work;
+ unsigned long tx_state;
+ struct sk_buff_head txq;
+ struct sk_buff *rx_skb;
+
+ const struct firmware *fw;
+ u8 fw_name[MAX_FW_FILE_NAME_LEN];
+ u32 fw_dnld_v1_offset;
+ u32 fw_v1_sent_bytes;
+ u32 fw_dnld_v3_offset;
+ u32 fw_v3_offset_correction;
+ u32 fw_v3_prev_sent;
+ u32 fw_v1_expected_len;
+ u32 boot_reg_offset;
+ wait_queue_head_t fw_dnld_done_wait_q;
+ wait_queue_head_t check_boot_sign_wait_q;
+
+ u32 new_baudrate;
+ u32 current_baudrate;
+ u32 fw_init_baudrate;
+ u32 secondary_baudrate;
+ enum bootloader_param_change timeout_changed;
+ enum bootloader_param_change baudrate_changed;
+ bool helper_downloaded;
+
+ struct ps_data psdata;
+ struct btnxpuart_data *nxp_data;
+ struct reset_control *pdn;
+ struct hci_uart hu;
+};
+
+#define NXP_V1_FW_REQ_PKT 0xa5
+#define NXP_V1_CHIP_VER_PKT 0xaa
+#define NXP_V3_FW_REQ_PKT 0xa7
+#define NXP_V3_CHIP_VER_PKT 0xab
+
+#define NXP_ACK_V1 0x5a
+#define NXP_NAK_V1 0xbf
+#define NXP_ACK_V3 0x7a
+#define NXP_NAK_V3 0x7b
+#define NXP_CRC_ERROR_V3 0x7c
+
+/* Bootloader signature error codes: Refer AN12820 from nxp.com */
+#define NXP_CRC_RX_ERROR BIT(0) /* CRC error in previous packet */
+#define NXP_ACK_RX_TIMEOUT BIT(2) /* ACK not received from host */
+#define NXP_HDR_RX_TIMEOUT BIT(3) /* FW Header chunk not received */
+#define NXP_DATA_RX_TIMEOUT BIT(4) /* FW Data chunk not received */
+
+#define HDR_LEN 16
+
+#define NXP_RECV_CHIP_VER_V1 \
+ .type = NXP_V1_CHIP_VER_PKT, \
+ .hlen = 4, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 4
+
+#define NXP_RECV_FW_REQ_V1 \
+ .type = NXP_V1_FW_REQ_PKT, \
+ .hlen = 4, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 4
+
+#define NXP_RECV_CHIP_VER_V3 \
+ .type = NXP_V3_CHIP_VER_PKT, \
+ .hlen = 4, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 4
+
+#define NXP_RECV_FW_REQ_V3 \
+ .type = NXP_V3_FW_REQ_PKT, \
+ .hlen = 9, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 9
+
+struct v1_data_req {
+ __le16 len;
+ __le16 len_comp;
+} __packed;
+
+struct v1_start_ind {
+ __le16 chip_id;
+ __le16 chip_id_comp;
+} __packed;
+
+struct v3_data_req {
+ __le16 len;
+ __le32 offset;
+ __le16 error;
+ u8 crc;
+} __packed;
+
+struct v3_start_ind {
+ __le16 chip_id;
+ u8 loader_ver;
+ u8 crc;
+} __packed;
+
+/* UART register addresses of BT chip */
+#define CLKDIVADDR 0x7f00008f
+#define UARTDIVADDR 0x7f000090
+#define UARTMCRADDR 0x7f000091
+#define UARTREINITADDR 0x7f000092
+#define UARTICRADDR 0x7f000093
+#define UARTFCRADDR 0x7f000094
+
+#define MCR 0x00000022
+#define INIT 0x00000001
+#define ICR 0x000000c7
+#define FCR 0x000000c7
+
+#define POLYNOMIAL8 0x07
+
+struct uart_reg {
+ __le32 address;
+ __le32 value;
+} __packed;
+
+struct uart_config {
+ struct uart_reg clkdiv;
+ struct uart_reg uartdiv;
+ struct uart_reg mcr;
+ struct uart_reg re_init;
+ struct uart_reg icr;
+ struct uart_reg fcr;
+ __be32 crc;
+} __packed;
+
+struct nxp_bootloader_cmd {
+ __le32 header;
+ __le32 arg;
+ __le32 payload_len;
+ __be32 crc;
+} __packed;
+
+struct nxp_v3_rx_timeout_nak {
+ u8 nak;
+ __le32 offset;
+ u8 crc;
+} __packed;
+
+union nxp_v3_rx_timeout_nak_u {
+ struct nxp_v3_rx_timeout_nak pkt;
+ u8 buf[6];
+};
+
+struct nxp_v3_crc_nak {
+ u8 nak;
+ u8 crc;
+} __packed;
+
+union nxp_v3_crc_nak_u {
+ struct nxp_v3_crc_nak pkt;
+ u8 buf[2];
+};
+
+/* FW dump */
+#define NXP_FW_DUMP_SIZE (1024 * 1000)
+
+struct nxp_fw_dump_hdr {
+ __le16 seq_num;
+ __le16 reserved;
+ __le16 buf_type;
+ __le16 buf_len;
+};
+
+union nxp_set_bd_addr_payload {
+ struct {
+ u8 param_id;
+ u8 param_len;
+ u8 param[6];
+ } __packed data;
+ u8 buf[8];
+};
+
+static u8 crc8_table[CRC8_TABLE_SIZE];
+
+/* Default configurations */
+#define DEFAULT_H2C_WAKEUP_MODE WAKEUP_METHOD_BREAK
+#define DEFAULT_PS_MODE PS_MODE_ENABLE
+#define FW_INIT_BAUDRATE HCI_NXP_PRI_BAUDRATE
+
+static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode,
+ u32 plen,
+ void *param,
+ bool resp)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+ struct sk_buff *skb = NULL;
+
+ /* set flag to prevent nxp_enqueue from parsing values from this command and
+ * calling hci_cmd_sync_queue() again.
+ */
+ psdata->driver_sent_cmd = true;
+ if (resp) {
+ skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT);
+ } else {
+ __hci_cmd_send(hdev, opcode, plen, param);
+ /* Allow command to be sent before tx_work is cancelled
+ * by btnxpuart_flush()
+ */
+ msleep(20);
+ }
+ psdata->driver_sent_cmd = false;
+
+ return skb;
+}
+
+static void btnxpuart_tx_wakeup(struct btnxpuart_dev *nxpdev)
+{
+ if (schedule_work(&nxpdev->tx_work))
+ set_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state);
+}
+
+/* NXP Power Save Feature */
+static void ps_start_timer(struct btnxpuart_dev *nxpdev)
+{
+ struct ps_data *psdata = &nxpdev->psdata;
+
+ if (!psdata)
+ return;
+
+ if (psdata->cur_psmode == PS_MODE_ENABLE)
+ mod_timer(&psdata->ps_timer, jiffies + msecs_to_jiffies(psdata->h2c_ps_interval));
+
+ if (psdata->ps_state == PS_STATE_AWAKE && psdata->ps_cmd == PS_CMD_ENTER_PS)
+ cancel_work_sync(&psdata->work);
+}
+
+static void ps_cancel_timer(struct btnxpuart_dev *nxpdev)
+{
+ struct ps_data *psdata = &nxpdev->psdata;
+
+ flush_work(&psdata->work);
+ timer_shutdown_sync(&psdata->ps_timer);
+}
+
+static void ps_control(struct hci_dev *hdev, u8 ps_state)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+ int status = 0;
+
+ if (psdata->ps_state == ps_state ||
+ !test_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state))
+ return;
+
+ mutex_lock(&psdata->ps_lock);
+ switch (psdata->cur_h2c_wakeupmode) {
+ case WAKEUP_METHOD_GPIO:
+ if (ps_state == PS_STATE_AWAKE)
+ gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 0);
+ else
+ gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 1);
+ bt_dev_dbg(hdev, "Set h2c_ps_gpio: %s",
+ str_high_low(ps_state == PS_STATE_SLEEP));
+ break;
+ case WAKEUP_METHOD_DTR:
+ if (ps_state == PS_STATE_AWAKE)
+ status = serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0);
+ else
+ status = serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR);
+ break;
+ case WAKEUP_METHOD_BREAK:
+ default:
+ if (ps_state == PS_STATE_AWAKE)
+ status = serdev_device_break_ctl(nxpdev->serdev, 0);
+ else
+ status = serdev_device_break_ctl(nxpdev->serdev, -1);
+ msleep(20); /* Allow chip to detect UART-break and enter sleep */
+ bt_dev_dbg(hdev, "Set UART break: %s, status=%d",
+ str_on_off(ps_state == PS_STATE_SLEEP), status);
+ break;
+ }
+ if (!status)
+ psdata->ps_state = ps_state;
+ mutex_unlock(&psdata->ps_lock);
+
+ if (ps_state == PS_STATE_AWAKE)
+ btnxpuart_tx_wakeup(nxpdev);
+}
+
+static void ps_work_func(struct work_struct *work)
+{
+ struct ps_data *data = container_of(work, struct ps_data, work);
+
+ if (data->ps_cmd == PS_CMD_ENTER_PS && data->cur_psmode == PS_MODE_ENABLE)
+ ps_control(data->hdev, PS_STATE_SLEEP);
+ else if (data->ps_cmd == PS_CMD_EXIT_PS)
+ ps_control(data->hdev, PS_STATE_AWAKE);
+}
+
+static void ps_timeout_func(struct timer_list *t)
+{
+ struct ps_data *data = timer_container_of(data, t, ps_timer);
+ struct hci_dev *hdev = data->hdev;
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+ if (test_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state)) {
+ ps_start_timer(nxpdev);
+ } else {
+ data->ps_cmd = PS_CMD_ENTER_PS;
+ schedule_work(&data->work);
+ }
+}
+
+static irqreturn_t ps_host_wakeup_irq_handler(int irq, void *priv)
+{
+ struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)priv;
+
+ bt_dev_dbg(nxpdev->hdev, "Host wakeup interrupt");
+ return IRQ_HANDLED;
+}
+static int ps_setup(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct serdev_device *serdev = nxpdev->serdev;
+ struct ps_data *psdata = &nxpdev->psdata;
+ int ret;
+
+ /* Out-Of-Band Device Wakeup */
+ psdata->h2c_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "device-wakeup",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(psdata->h2c_ps_gpio)) {
+ bt_dev_err(hdev, "Error fetching device-wakeup-gpios: %ld",
+ PTR_ERR(psdata->h2c_ps_gpio));
+ return PTR_ERR(psdata->h2c_ps_gpio);
+ }
+
+ if (device_property_read_u8(&serdev->dev, "nxp,wakein-pin", &psdata->h2c_wakeup_gpio)) {
+ psdata->h2c_wakeup_gpio = 0xff; /* 0xff: use default pin/gpio */
+ } else if (!psdata->h2c_ps_gpio) {
+ bt_dev_warn(hdev, "nxp,wakein-pin property without device-wakeup-gpios");
+ psdata->h2c_wakeup_gpio = 0xff;
+ }
+
+ /* Out-Of-Band Host Wakeup */
+ if (of_property_read_bool(serdev->dev.of_node, "wakeup-source")) {
+ psdata->irq_handler = of_irq_get_byname(serdev->dev.of_node, "wakeup");
+ bt_dev_info(nxpdev->hdev, "irq_handler: %d", psdata->irq_handler);
+ if (psdata->irq_handler > 0)
+ psdata->wakeup_source = true;
+ }
+
+ if (device_property_read_u8(&serdev->dev, "nxp,wakeout-pin", &psdata->c2h_wakeup_gpio)) {
+ psdata->c2h_wakeup_gpio = 0xff;
+ if (psdata->wakeup_source) {
+ bt_dev_warn(hdev, "host wakeup interrupt without nxp,wakeout-pin");
+ psdata->wakeup_source = false;
+ }
+ } else if (!psdata->wakeup_source) {
+ bt_dev_warn(hdev, "nxp,wakeout-pin property without host wakeup interrupt");
+ psdata->c2h_wakeup_gpio = 0xff;
+ }
+
+ if (psdata->wakeup_source) {
+ ret = devm_request_threaded_irq(&serdev->dev, psdata->irq_handler,
+ NULL, ps_host_wakeup_irq_handler,
+ IRQF_ONESHOT,
+ dev_name(&serdev->dev), nxpdev);
+ if (ret)
+ bt_dev_info(hdev, "error setting wakeup IRQ handler, ignoring\n");
+ disable_irq(psdata->irq_handler);
+ device_init_wakeup(&serdev->dev, true);
+ }
+
+ psdata->hdev = hdev;
+ INIT_WORK(&psdata->work, ps_work_func);
+ mutex_init(&psdata->ps_lock);
+ timer_setup(&psdata->ps_timer, ps_timeout_func, 0);
+
+ return 0;
+}
+
+static bool ps_wakeup(struct btnxpuart_dev *nxpdev)
+{
+ struct ps_data *psdata = &nxpdev->psdata;
+ u8 ps_state;
+
+ mutex_lock(&psdata->ps_lock);
+ ps_state = psdata->ps_state;
+ mutex_unlock(&psdata->ps_lock);
+
+ if (ps_state != PS_STATE_AWAKE) {
+ psdata->ps_cmd = PS_CMD_EXIT_PS;
+ schedule_work(&psdata->work);
+ return true;
+ }
+ return false;
+}
+
+static void ps_cleanup(struct btnxpuart_dev *nxpdev)
+{
+ struct ps_data *psdata = &nxpdev->psdata;
+ u8 ps_state;
+
+ mutex_lock(&psdata->ps_lock);
+ ps_state = psdata->ps_state;
+ mutex_unlock(&psdata->ps_lock);
+
+ if (ps_state != PS_STATE_AWAKE)
+ ps_control(psdata->hdev, PS_STATE_AWAKE);
+
+ ps_cancel_timer(nxpdev);
+ cancel_work_sync(&psdata->work);
+ mutex_destroy(&psdata->ps_lock);
+}
+
+static int send_ps_cmd(struct hci_dev *hdev, void *data)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+ struct psmode_cmd_payload pcmd;
+ struct sk_buff *skb;
+ u8 *status;
+
+ if (psdata->target_ps_mode == PS_MODE_ENABLE)
+ pcmd.ps_cmd = BT_PS_ENABLE;
+ else
+ pcmd.ps_cmd = BT_PS_DISABLE;
+ pcmd.c2h_ps_interval = __cpu_to_le16(psdata->c2h_ps_interval);
+
+ skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd),
+ &pcmd, true);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Setting Power Save mode failed (%ld)", PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ status = skb_pull_data(skb, 1);
+ if (status) {
+ if (!*status)
+ psdata->cur_psmode = psdata->target_ps_mode;
+ else
+ psdata->target_ps_mode = psdata->cur_psmode;
+ if (psdata->cur_psmode == PS_MODE_ENABLE)
+ ps_start_timer(nxpdev);
+ else
+ ps_wakeup(nxpdev);
+ bt_dev_dbg(hdev, "Power Save mode response: status=%d, ps_mode=%d",
+ *status, psdata->cur_psmode);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+ struct wakeup_cmd_payload pcmd;
+ struct sk_buff *skb;
+ u8 *status;
+
+ pcmd.c2h_wakeupmode = psdata->c2h_wakeupmode;
+ pcmd.c2h_wakeup_gpio = psdata->c2h_wakeup_gpio;
+ pcmd.h2c_wakeup_gpio = 0xff;
+ switch (psdata->h2c_wakeupmode) {
+ case WAKEUP_METHOD_GPIO:
+ pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_GPIO;
+ pcmd.h2c_wakeup_gpio = psdata->h2c_wakeup_gpio;
+ break;
+ case WAKEUP_METHOD_DTR:
+ pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_DSR;
+ break;
+ case WAKEUP_METHOD_BREAK:
+ default:
+ pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_BREAK;
+ break;
+ }
+
+ skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd),
+ &pcmd, true);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Setting wake-up method failed (%ld)", PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ status = skb_pull_data(skb, 1);
+ if (status) {
+ if (*status == 0)
+ psdata->cur_h2c_wakeupmode = psdata->h2c_wakeupmode;
+ else
+ psdata->h2c_wakeupmode = psdata->cur_h2c_wakeupmode;
+ bt_dev_dbg(hdev, "Set Wakeup Method response: status=%d, h2c_wakeupmode=%d",
+ *status, psdata->cur_h2c_wakeupmode);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void ps_init(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+ u8 default_h2c_wakeup_mode = DEFAULT_H2C_WAKEUP_MODE;
+
+ serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_RTS);
+ usleep_range(5000, 10000);
+ serdev_device_set_tiocm(nxpdev->serdev, TIOCM_RTS, 0);
+ usleep_range(5000, 10000);
+
+ psdata->ps_state = PS_STATE_AWAKE;
+
+ if (psdata->c2h_wakeup_gpio != 0xff)
+ psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_GPIO;
+ else
+ psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
+
+ psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
+ if (psdata->h2c_ps_gpio)
+ default_h2c_wakeup_mode = WAKEUP_METHOD_GPIO;
+
+ psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
+
+ switch (default_h2c_wakeup_mode) {
+ case WAKEUP_METHOD_GPIO:
+ psdata->h2c_wakeupmode = WAKEUP_METHOD_GPIO;
+ gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 0);
+ usleep_range(5000, 10000);
+ break;
+ case WAKEUP_METHOD_DTR:
+ psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
+ serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR);
+ serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0);
+ break;
+ case WAKEUP_METHOD_BREAK:
+ default:
+ psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK;
+ serdev_device_break_ctl(nxpdev->serdev, -1);
+ usleep_range(5000, 10000);
+ serdev_device_break_ctl(nxpdev->serdev, 0);
+ usleep_range(5000, 10000);
+ break;
+ }
+
+ psdata->cur_psmode = PS_MODE_DISABLE;
+ psdata->target_ps_mode = DEFAULT_PS_MODE;
+}
+
+/* NXP Firmware Download Feature */
+static int nxp_download_firmware(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ int err = 0;
+
+ nxpdev->fw_dnld_v1_offset = 0;
+ nxpdev->fw_v1_sent_bytes = 0;
+ nxpdev->fw_v1_expected_len = HDR_LEN;
+ nxpdev->boot_reg_offset = 0;
+ nxpdev->fw_dnld_v3_offset = 0;
+ nxpdev->fw_v3_offset_correction = 0;
+ nxpdev->baudrate_changed = not_changed;
+ nxpdev->timeout_changed = not_changed;
+ nxpdev->helper_downloaded = false;
+
+ serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE);
+ serdev_device_set_flow_control(nxpdev->serdev, false);
+ nxpdev->current_baudrate = HCI_NXP_PRI_BAUDRATE;
+
+ /* Wait till FW is downloaded */
+ err = wait_event_interruptible_timeout(nxpdev->fw_dnld_done_wait_q,
+ !test_bit(BTNXPUART_FW_DOWNLOADING,
+ &nxpdev->tx_state),
+ msecs_to_jiffies(60000));
+
+ if (nxpdev->fw && strlen(nxpdev->fw_name)) {
+ release_firmware(nxpdev->fw);
+ memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name));
+ }
+
+ if (err == 0) {
+ bt_dev_err(hdev, "FW Download Timeout. offset: %d",
+ nxpdev->fw_dnld_v1_offset ?
+ nxpdev->fw_dnld_v1_offset :
+ nxpdev->fw_dnld_v3_offset);
+ return -ETIMEDOUT;
+ }
+ if (test_bit(BTNXPUART_FW_DOWNLOAD_ABORT, &nxpdev->tx_state)) {
+ bt_dev_err(hdev, "FW Download Aborted");
+ return -EINTR;
+ }
+
+ serdev_device_set_flow_control(nxpdev->serdev, true);
+
+ /* Allow the downloaded FW to initialize */
+ msleep(1200);
+
+ return 0;
+}
+
+static void nxp_send_ack(u8 ack, struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ u8 ack_nak[2];
+ int len = 1;
+
+ ack_nak[0] = ack;
+ if (ack == NXP_ACK_V3) {
+ ack_nak[1] = crc8(crc8_table, ack_nak, 1, 0xff);
+ len = 2;
+ }
+ serdev_device_write_buf(nxpdev->serdev, ack_nak, len);
+}
+
+static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct nxp_bootloader_cmd nxp_cmd5;
+ struct uart_config uart_config;
+ u32 clkdivaddr = CLKDIVADDR - nxpdev->boot_reg_offset;
+ u32 uartdivaddr = UARTDIVADDR - nxpdev->boot_reg_offset;
+ u32 uartmcraddr = UARTMCRADDR - nxpdev->boot_reg_offset;
+ u32 uartreinitaddr = UARTREINITADDR - nxpdev->boot_reg_offset;
+ u32 uarticraddr = UARTICRADDR - nxpdev->boot_reg_offset;
+ u32 uartfcraddr = UARTFCRADDR - nxpdev->boot_reg_offset;
+
+ if (req_len == sizeof(nxp_cmd5)) {
+ nxp_cmd5.header = __cpu_to_le32(5);
+ nxp_cmd5.arg = 0;
+ nxp_cmd5.payload_len = __cpu_to_le32(sizeof(uart_config));
+ /* FW expects swapped CRC bytes */
+ nxp_cmd5.crc = __cpu_to_be32(crc32_be(0UL, (char *)&nxp_cmd5,
+ sizeof(nxp_cmd5) - 4));
+
+ serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd5, sizeof(nxp_cmd5));
+ nxpdev->fw_v3_offset_correction += req_len;
+ } else if (req_len == sizeof(uart_config)) {
+ uart_config.clkdiv.address = __cpu_to_le32(clkdivaddr);
+ if (nxpdev->new_baudrate == HCI_NXP_SEC_BAUDRATE_4M)
+ uart_config.clkdiv.value = __cpu_to_le32(0x01000000);
+ else
+ uart_config.clkdiv.value = __cpu_to_le32(0x00c00000);
+ uart_config.uartdiv.address = __cpu_to_le32(uartdivaddr);
+ uart_config.uartdiv.value = __cpu_to_le32(1);
+ uart_config.mcr.address = __cpu_to_le32(uartmcraddr);
+ uart_config.mcr.value = __cpu_to_le32(MCR);
+ uart_config.re_init.address = __cpu_to_le32(uartreinitaddr);
+ uart_config.re_init.value = __cpu_to_le32(INIT);
+ uart_config.icr.address = __cpu_to_le32(uarticraddr);
+ uart_config.icr.value = __cpu_to_le32(ICR);
+ uart_config.fcr.address = __cpu_to_le32(uartfcraddr);
+ uart_config.fcr.value = __cpu_to_le32(FCR);
+ /* FW expects swapped CRC bytes */
+ uart_config.crc = __cpu_to_be32(crc32_be(0UL, (char *)&uart_config,
+ sizeof(uart_config) - 4));
+
+ serdev_device_write_buf(nxpdev->serdev, (u8 *)&uart_config, sizeof(uart_config));
+ serdev_device_wait_until_sent(nxpdev->serdev, 0);
+ nxpdev->fw_v3_offset_correction += req_len;
+ return true;
+ }
+ return false;
+}
+
+static bool nxp_fw_change_timeout(struct hci_dev *hdev, u16 req_len)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct nxp_bootloader_cmd nxp_cmd7;
+
+ if (req_len != sizeof(nxp_cmd7))
+ return false;
+
+ nxp_cmd7.header = __cpu_to_le32(7);
+ nxp_cmd7.arg = __cpu_to_le32(0x70);
+ nxp_cmd7.payload_len = 0;
+ /* FW expects swapped CRC bytes */
+ nxp_cmd7.crc = __cpu_to_be32(crc32_be(0UL, (char *)&nxp_cmd7,
+ sizeof(nxp_cmd7) - 4));
+ serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd7, sizeof(nxp_cmd7));
+ serdev_device_wait_until_sent(nxpdev->serdev, 0);
+ nxpdev->fw_v3_offset_correction += req_len;
+ return true;
+}
+
+static u32 nxp_get_data_len(const u8 *buf)
+{
+ struct nxp_bootloader_cmd *hdr = (struct nxp_bootloader_cmd *)buf;
+
+ return __le32_to_cpu(hdr->payload_len);
+}
+
+static bool is_fw_downloading(struct btnxpuart_dev *nxpdev)
+{
+ return test_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+}
+
+static bool ind_reset_in_progress(struct btnxpuart_dev *nxpdev)
+{
+ return test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state);
+}
+
+static bool fw_dump_in_progress(struct btnxpuart_dev *nxpdev)
+{
+ return test_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state);
+}
+
+static bool process_boot_signature(struct btnxpuart_dev *nxpdev)
+{
+ if (test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state)) {
+ clear_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state);
+ wake_up_interruptible(&nxpdev->check_boot_sign_wait_q);
+ return false;
+ }
+ return is_fw_downloading(nxpdev);
+}
+
+static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name,
+ const char *fw_name_old)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ const char *fw_name_dt;
+ int err = 0;
+
+ if (!fw_name)
+ return -ENOENT;
+
+ if (!strlen(nxpdev->fw_name)) {
+ if (strcmp(fw_name, FIRMWARE_HELPER) &&
+ !device_property_read_string(&nxpdev->serdev->dev,
+ "firmware-name",
+ &fw_name_dt))
+ fw_name = fw_name_dt;
+ snprintf(nxpdev->fw_name, MAX_FW_FILE_NAME_LEN, "nxp/%s", fw_name);
+ err = request_firmware_direct(&nxpdev->fw, nxpdev->fw_name, &hdev->dev);
+ if (err < 0 && fw_name_old) {
+ snprintf(nxpdev->fw_name, MAX_FW_FILE_NAME_LEN, "nxp/%s", fw_name_old);
+ err = request_firmware_direct(&nxpdev->fw, nxpdev->fw_name, &hdev->dev);
+ }
+
+ bt_dev_info(hdev, "Request Firmware: %s", nxpdev->fw_name);
+ if (err < 0) {
+ bt_dev_err(hdev, "Firmware file %s not found", nxpdev->fw_name);
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ }
+ }
+ return err;
+}
+
+/* for legacy chipsets with V1 bootloader */
+static int nxp_recv_chip_ver_v1(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct v1_start_ind *req;
+ __u16 chip_id;
+
+ req = skb_pull_data(skb, sizeof(*req));
+ if (!req)
+ goto free_skb;
+
+ chip_id = le16_to_cpu(req->chip_id ^ req->chip_id_comp);
+ if (chip_id == 0xffff && nxpdev->fw_dnld_v1_offset) {
+ nxpdev->fw_dnld_v1_offset = 0;
+ nxpdev->fw_v1_sent_bytes = 0;
+ nxpdev->fw_v1_expected_len = HDR_LEN;
+ release_firmware(nxpdev->fw);
+ memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name));
+ nxp_send_ack(NXP_ACK_V1, hdev);
+ }
+
+free_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct btnxpuart_data *nxp_data = nxpdev->nxp_data;
+ struct v1_data_req *req;
+ __u16 len;
+
+ if (!process_boot_signature(nxpdev))
+ goto free_skb;
+
+ req = skb_pull_data(skb, sizeof(*req));
+ if (!req)
+ goto free_skb;
+
+ len = __le16_to_cpu(req->len ^ req->len_comp);
+ if (len != 0xffff) {
+ bt_dev_dbg(hdev, "ERR: Send NAK");
+ nxp_send_ack(NXP_NAK_V1, hdev);
+ goto free_skb;
+ }
+ nxp_send_ack(NXP_ACK_V1, hdev);
+
+ len = __le16_to_cpu(req->len);
+
+ if (!nxp_data->helper_fw_name) {
+ if (nxpdev->timeout_changed != changed) {
+ nxp_fw_change_timeout(hdev, len);
+ nxpdev->timeout_changed = changed;
+ goto free_skb;
+ }
+ if (nxpdev->baudrate_changed != changed) {
+ nxpdev->new_baudrate = nxpdev->secondary_baudrate;
+ if (nxp_fw_change_baudrate(hdev, len)) {
+ nxpdev->baudrate_changed = changed;
+ serdev_device_set_baudrate(nxpdev->serdev,
+ nxpdev->secondary_baudrate);
+ serdev_device_set_flow_control(nxpdev->serdev, true);
+ nxpdev->current_baudrate = nxpdev->secondary_baudrate;
+ }
+ goto free_skb;
+ }
+ }
+
+ if (!nxp_data->helper_fw_name || nxpdev->helper_downloaded) {
+ if (nxp_request_firmware(hdev, nxp_data->fw_name, nxp_data->fw_name_old))
+ goto free_skb;
+ } else if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) {
+ if (nxp_request_firmware(hdev, nxp_data->helper_fw_name, NULL))
+ goto free_skb;
+ }
+
+ if (!len) {
+ bt_dev_info(hdev, "FW Download Complete: %zu bytes",
+ nxpdev->fw->size);
+ if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) {
+ nxpdev->helper_downloaded = true;
+ serdev_device_wait_until_sent(nxpdev->serdev, 0);
+ serdev_device_set_baudrate(nxpdev->serdev,
+ HCI_NXP_SEC_BAUDRATE_3M);
+ serdev_device_set_flow_control(nxpdev->serdev, true);
+ } else {
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+ }
+ goto free_skb;
+ }
+ if (len & 0x01) {
+ /* The CRC did not match at the other end.
+ * Simply send the same bytes again.
+ */
+ len = nxpdev->fw_v1_sent_bytes;
+ bt_dev_dbg(hdev, "CRC error. Resend %d bytes of FW.", len);
+ } else {
+ nxpdev->fw_dnld_v1_offset += nxpdev->fw_v1_sent_bytes;
+
+ /* The FW bin file is made up of many blocks of
+ * 16 byte header and payload data chunks. If the
+ * FW has requested a header, read the payload length
+ * info from the header, before sending the header.
+ * In the next iteration, the FW should request the
+ * payload data chunk, which should be equal to the
+ * payload length read from header. If there is a
+ * mismatch, clearly the driver and FW are out of sync,
+ * and we need to re-send the previous header again.
+ */
+ if (len == nxpdev->fw_v1_expected_len) {
+ if (len == HDR_LEN)
+ nxpdev->fw_v1_expected_len = nxp_get_data_len(nxpdev->fw->data +
+ nxpdev->fw_dnld_v1_offset);
+ else
+ nxpdev->fw_v1_expected_len = HDR_LEN;
+ } else if (len == HDR_LEN) {
+ /* FW download out of sync. Send previous chunk again */
+ nxpdev->fw_dnld_v1_offset -= nxpdev->fw_v1_sent_bytes;
+ nxpdev->fw_v1_expected_len = HDR_LEN;
+ }
+ }
+
+ if (nxpdev->fw_dnld_v1_offset + len <= nxpdev->fw->size)
+ serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
+ nxpdev->fw_dnld_v1_offset, len);
+ nxpdev->fw_v1_sent_bytes = len;
+
+free_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
+static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid,
+ u8 loader_ver)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ char *fw_name = NULL;
+
+ switch (chipid) {
+ case CHIP_ID_W9098:
+ fw_name = FIRMWARE_W9098;
+ break;
+ case CHIP_ID_IW416:
+ fw_name = FIRMWARE_IW416;
+ break;
+ case CHIP_ID_IW612:
+ fw_name = FIRMWARE_IW612;
+ break;
+ case CHIP_ID_IW624a:
+ case CHIP_ID_IW624c:
+ nxpdev->boot_reg_offset = 1;
+ if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
+ fw_name = FIRMWARE_IW624;
+ else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
+ fw_name = FIRMWARE_SECURE_IW624;
+ else
+ bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
+ break;
+ case CHIP_ID_AW693a0:
+ if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
+ fw_name = FIRMWARE_AW693;
+ else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
+ fw_name = FIRMWARE_SECURE_AW693;
+ else
+ bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
+ break;
+ case CHIP_ID_AW693a1:
+ if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
+ fw_name = FIRMWARE_AW693_A1;
+ else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
+ fw_name = FIRMWARE_SECURE_AW693_A1;
+ else
+ bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
+ break;
+ case CHIP_ID_IW610a0:
+ case CHIP_ID_IW610a1:
+ if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
+ fw_name = FIRMWARE_IW610;
+ else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
+ fw_name = FIRMWARE_SECURE_IW610;
+ else
+ bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
+ break;
+ default:
+ bt_dev_err(hdev, "Unknown chip signature %04x", chipid);
+ break;
+ }
+ return fw_name;
+}
+
+static char *nxp_get_old_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid,
+ u8 loader_ver)
+{
+ char *fw_name_old = NULL;
+
+ switch (chipid) {
+ case CHIP_ID_W9098:
+ fw_name_old = FIRMWARE_W9098_OLD;
+ break;
+ case CHIP_ID_IW416:
+ fw_name_old = FIRMWARE_IW416_OLD;
+ break;
+ }
+ return fw_name_old;
+}
+
+static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct v3_start_ind *req = skb_pull_data(skb, sizeof(*req));
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ const char *fw_name;
+ const char *fw_name_old;
+ u16 chip_id;
+ u8 loader_ver;
+
+ if (!process_boot_signature(nxpdev))
+ goto free_skb;
+
+ chip_id = le16_to_cpu(req->chip_id);
+ loader_ver = req->loader_ver;
+ bt_dev_info(hdev, "ChipID: %04x, Version: %d", chip_id, loader_ver);
+ fw_name = nxp_get_fw_name_from_chipid(hdev, chip_id, loader_ver);
+ fw_name_old = nxp_get_old_fw_name_from_chipid(hdev, chip_id, loader_ver);
+ if (!nxp_request_firmware(hdev, fw_name, fw_name_old))
+ nxp_send_ack(NXP_ACK_V3, hdev);
+
+free_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
+static void nxp_handle_fw_download_error(struct hci_dev *hdev, struct v3_data_req *req)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ __u32 offset = __le32_to_cpu(req->offset);
+ __u16 err = __le16_to_cpu(req->error);
+ union nxp_v3_rx_timeout_nak_u timeout_nak_buf;
+ union nxp_v3_crc_nak_u crc_nak_buf;
+
+ if (err & NXP_CRC_RX_ERROR) {
+ crc_nak_buf.pkt.nak = NXP_CRC_ERROR_V3;
+ crc_nak_buf.pkt.crc = crc8(crc8_table, crc_nak_buf.buf,
+ sizeof(crc_nak_buf) - 1, 0xff);
+ serdev_device_write_buf(nxpdev->serdev, crc_nak_buf.buf,
+ sizeof(crc_nak_buf));
+ } else if (err & NXP_ACK_RX_TIMEOUT ||
+ err & NXP_HDR_RX_TIMEOUT ||
+ err & NXP_DATA_RX_TIMEOUT) {
+ timeout_nak_buf.pkt.nak = NXP_NAK_V3;
+ timeout_nak_buf.pkt.offset = __cpu_to_le32(offset);
+ timeout_nak_buf.pkt.crc = crc8(crc8_table, timeout_nak_buf.buf,
+ sizeof(timeout_nak_buf) - 1, 0xff);
+ serdev_device_write_buf(nxpdev->serdev, timeout_nak_buf.buf,
+ sizeof(timeout_nak_buf));
+ } else {
+ bt_dev_err(hdev, "Unknown bootloader error code: %d", err);
+ }
+}
+
+static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct v3_data_req *req;
+ __u16 len = 0;
+ __u16 err = 0;
+ __u32 offset;
+
+ if (!process_boot_signature(nxpdev))
+ goto free_skb;
+
+ req = skb_pull_data(skb, sizeof(*req));
+ if (!req || !nxpdev->fw)
+ goto free_skb;
+
+ err = __le16_to_cpu(req->error);
+
+ if (!err) {
+ nxp_send_ack(NXP_ACK_V3, hdev);
+ if (nxpdev->timeout_changed == cmd_sent)
+ nxpdev->timeout_changed = changed;
+ if (nxpdev->baudrate_changed == cmd_sent)
+ nxpdev->baudrate_changed = changed;
+ } else {
+ nxp_handle_fw_download_error(hdev, req);
+ if (nxpdev->timeout_changed == cmd_sent &&
+ err == NXP_CRC_RX_ERROR) {
+ nxpdev->fw_v3_offset_correction -= nxpdev->fw_v3_prev_sent;
+ nxpdev->timeout_changed = not_changed;
+ }
+ if (nxpdev->baudrate_changed == cmd_sent &&
+ err == NXP_CRC_RX_ERROR) {
+ nxpdev->fw_v3_offset_correction -= nxpdev->fw_v3_prev_sent;
+ nxpdev->baudrate_changed = not_changed;
+ }
+ goto free_skb;
+ }
+
+ len = __le16_to_cpu(req->len);
+
+ if (nxpdev->timeout_changed != changed) {
+ nxp_fw_change_timeout(hdev, len);
+ nxpdev->timeout_changed = cmd_sent;
+ goto free_skb;
+ }
+
+ if (nxpdev->baudrate_changed != changed) {
+ nxpdev->new_baudrate = nxpdev->secondary_baudrate;
+ if (nxp_fw_change_baudrate(hdev, len)) {
+ nxpdev->baudrate_changed = cmd_sent;
+ serdev_device_set_baudrate(nxpdev->serdev,
+ nxpdev->secondary_baudrate);
+ serdev_device_set_flow_control(nxpdev->serdev, true);
+ nxpdev->current_baudrate = nxpdev->secondary_baudrate;
+ }
+ goto free_skb;
+ }
+
+ if (req->len == 0) {
+ bt_dev_info(hdev, "FW Download Complete: %zu bytes",
+ nxpdev->fw->size);
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+ goto free_skb;
+ }
+
+ offset = __le32_to_cpu(req->offset);
+ if (offset < nxpdev->fw_v3_offset_correction) {
+ /* This scenario should ideally never occur. But if it ever does,
+ * FW is out of sync and needs a power cycle.
+ */
+ bt_dev_err(hdev, "Something went wrong during FW download");
+ bt_dev_err(hdev, "Please power cycle and try again");
+ goto free_skb;
+ }
+
+ nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
+ serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
+ nxpdev->fw_dnld_v3_offset, len);
+
+free_skb:
+ nxpdev->fw_v3_prev_sent = len;
+ kfree_skb(skb);
+ return 0;
+}
+
+static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ __le32 new_baudrate = __cpu_to_le32(nxpdev->new_baudrate);
+ struct ps_data *psdata = &nxpdev->psdata;
+ struct sk_buff *skb;
+ u8 *status;
+
+ if (!psdata)
+ return 0;
+
+ skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4,
+ (u8 *)&new_baudrate, true);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Setting baudrate failed (%ld)", PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ status = (u8 *)skb_pull_data(skb, 1);
+ if (status) {
+ if (*status == 0) {
+ serdev_device_set_baudrate(nxpdev->serdev, nxpdev->new_baudrate);
+ nxpdev->current_baudrate = nxpdev->new_baudrate;
+ }
+ bt_dev_dbg(hdev, "Set baudrate response: status=%d, baudrate=%d",
+ *status, nxpdev->new_baudrate);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
+{
+ serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE);
+ if (ind_reset_in_progress(nxpdev))
+ serdev_device_set_flow_control(nxpdev->serdev, false);
+ else
+ serdev_device_set_flow_control(nxpdev->serdev, true);
+ set_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state);
+
+ return wait_event_interruptible_timeout(nxpdev->check_boot_sign_wait_q,
+ !test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE,
+ &nxpdev->tx_state),
+ msecs_to_jiffies(1000));
+}
+
+static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
+{
+ static const u8 ir_hw_err[] = { HCI_EV_HARDWARE_ERROR,
+ 0x01, BTNXPUART_IR_HW_ERR };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(3, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ skb_put_data(skb, ir_hw_err, 3);
+
+ /* Inject Hardware Error to upper stack */
+ return hci_recv_frame(hdev, skb);
+}
+
+/* Firmware dump */
+static void nxp_coredump(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ u8 pcmd = 2;
+
+ skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd, true);
+ if (IS_ERR(skb))
+ bt_dev_err(hdev, "Failed to trigger FW Dump. (%ld)", PTR_ERR(skb));
+ else
+ kfree_skb(skb);
+}
+
+static void nxp_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ /* Nothing to be added in FW dump header */
+}
+
+static int nxp_process_fw_dump(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_acl_hdr *acl_hdr = (struct hci_acl_hdr *)skb_pull_data(skb,
+ sizeof(*acl_hdr));
+ struct nxp_fw_dump_hdr *fw_dump_hdr = (struct nxp_fw_dump_hdr *)skb->data;
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ __u16 seq_num = __le16_to_cpu(fw_dump_hdr->seq_num);
+ __u16 buf_len = __le16_to_cpu(fw_dump_hdr->buf_len);
+ int err;
+
+ if (seq_num == 0x0001) {
+ if (test_and_set_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state)) {
+ bt_dev_err(hdev, "FW dump already in progress");
+ goto free_skb;
+ }
+ bt_dev_warn(hdev, "==== Start FW dump ===");
+ err = hci_devcd_init(hdev, NXP_FW_DUMP_SIZE);
+ if (err < 0)
+ goto free_skb;
+
+ schedule_delayed_work(&hdev->dump.dump_timeout,
+ msecs_to_jiffies(20000));
+ }
+
+ err = hci_devcd_append(hdev, skb_clone(skb, GFP_ATOMIC));
+ if (err < 0)
+ goto free_skb;
+
+ if (buf_len == 0) {
+ bt_dev_warn(hdev, "==== FW dump complete ===");
+ hci_devcd_complete(hdev);
+ nxp_set_ind_reset(hdev, NULL);
+ }
+
+free_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int nxp_recv_acl_pkt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
+
+ /* FW dump chunks are ACL packets with conn handle 0xfff */
+ if ((handle & 0x0FFF) == 0xFFF)
+ return nxp_process_fw_dump(hdev, skb);
+ else
+ return hci_recv_frame(hdev, skb);
+}
+
+static int nxp_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ union nxp_set_bd_addr_payload pcmd;
+ int err;
+
+ pcmd.data.param_id = 0xfe;
+ pcmd.data.param_len = 6;
+ memcpy(pcmd.data.param, bdaddr, 6);
+
+ /* BD address can be assigned only after first reset command. */
+ err = __hci_cmd_sync_status(hdev, HCI_OP_RESET, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (err) {
+ bt_dev_err(hdev,
+ "Reset before setting local-bd-addr failed (%d)",
+ err);
+ return err;
+ }
+
+ err = __hci_cmd_sync_status(hdev, HCI_NXP_SET_BD_ADDR, sizeof(pcmd),
+ pcmd.buf, HCI_CMD_TIMEOUT);
+ if (err) {
+ bt_dev_err(hdev, "Changing device address failed (%d)", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* NXP protocol */
+static int nxp_setup(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct serdev_device *serdev = nxpdev->serdev;
+ char device_string[30];
+ char event_string[50];
+ char *envp[] = {device_string, event_string, NULL};
+ int err = 0;
+
+ if (nxp_check_boot_sign(nxpdev)) {
+ bt_dev_dbg(hdev, "Need FW Download.");
+ err = nxp_download_firmware(hdev);
+ if (err < 0)
+ return err;
+ } else {
+ bt_dev_info(hdev, "FW already running.");
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ }
+
+ snprintf(device_string, 30, "BTNXPUART_DEV=%s", dev_name(&serdev->dev));
+ snprintf(event_string, 50, "BTNXPUART_STATE=FW_READY");
+ bt_dev_dbg(hdev, "==== Send uevent: %s:%s ===", device_string,
+ event_string);
+ kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp);
+
+ serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate);
+ nxpdev->current_baudrate = nxpdev->fw_init_baudrate;
+
+ ps_init(hdev);
+
+ if (test_and_clear_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state))
+ hci_dev_clear_flag(hdev, HCI_SETUP);
+
+ return 0;
+}
+
+static int nxp_post_init(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+
+ if (nxpdev->current_baudrate != nxpdev->secondary_baudrate) {
+ nxpdev->new_baudrate = nxpdev->secondary_baudrate;
+ nxp_set_baudrate_cmd(hdev, NULL);
+ }
+ if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode)
+ send_wakeup_method_cmd(hdev, NULL);
+ if (psdata->cur_psmode != psdata->target_ps_mode)
+ send_ps_cmd(hdev, NULL);
+ return 0;
+}
+
+static void nxp_hw_err(struct hci_dev *hdev, u8 code)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+ switch (code) {
+ case BTNXPUART_IR_HW_ERR:
+ set_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state);
+ hci_dev_set_flag(hdev, HCI_SETUP);
+ break;
+ default:
+ break;
+ }
+}
+
+static int nxp_shutdown(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct sk_buff *skb;
+ u8 pcmd = 0;
+
+ if (ind_reset_in_progress(nxpdev)) {
+ if (test_and_clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS,
+ &nxpdev->tx_state))
+ skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1,
+ &pcmd, false);
+ else
+ skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1,
+ &pcmd, true);
+ serdev_device_set_flow_control(nxpdev->serdev, false);
+ set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ /* HCI_NXP_IND_RESET command may not returns any response */
+ if (!IS_ERR(skb))
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static bool nxp_wakeup(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+
+ if (psdata->c2h_wakeupmode != BT_HOST_WAKEUP_METHOD_NONE)
+ return true;
+
+ return false;
+}
+
+static void nxp_reset(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+ if (!ind_reset_in_progress(nxpdev) && !fw_dump_in_progress(nxpdev)) {
+ bt_dev_dbg(hdev, "CMD Timeout detected. Resetting.");
+ nxp_set_ind_reset(hdev, NULL);
+ }
+}
+
+static int btnxpuart_queue_skb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+ skb_queue_tail(&nxpdev->txq, skb);
+ btnxpuart_tx_wakeup(nxpdev);
+ return 0;
+}
+
+static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct ps_data *psdata = &nxpdev->psdata;
+ struct hci_command_hdr *hdr;
+ struct psmode_cmd_payload ps_parm;
+ struct wakeup_cmd_payload wakeup_parm;
+ __le32 baudrate_parm;
+
+ if (fw_dump_in_progress(nxpdev))
+ return -EBUSY;
+
+ /* if vendor commands are received from user space (e.g. hcitool), update
+ * driver flags accordingly and ask driver to re-send the command to FW.
+ * In case the payload for any command does not match expected payload
+ * length, let the firmware and user space program handle it, or throw
+ * an error.
+ */
+ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT && !psdata->driver_sent_cmd) {
+ hdr = (struct hci_command_hdr *)skb->data;
+ if (hdr->plen != (skb->len - HCI_COMMAND_HDR_SIZE))
+ return btnxpuart_queue_skb(hdev, skb);
+
+ switch (__le16_to_cpu(hdr->opcode)) {
+ case HCI_NXP_AUTO_SLEEP_MODE:
+ if (hdr->plen == sizeof(ps_parm)) {
+ memcpy(&ps_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen);
+ if (ps_parm.ps_cmd == BT_PS_ENABLE)
+ psdata->target_ps_mode = PS_MODE_ENABLE;
+ else if (ps_parm.ps_cmd == BT_PS_DISABLE)
+ psdata->target_ps_mode = PS_MODE_DISABLE;
+ psdata->c2h_ps_interval = __le16_to_cpu(ps_parm.c2h_ps_interval);
+ hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL);
+ goto free_skb;
+ }
+ break;
+ case HCI_NXP_WAKEUP_METHOD:
+ if (hdr->plen == sizeof(wakeup_parm)) {
+ memcpy(&wakeup_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen);
+ psdata->c2h_wakeupmode = wakeup_parm.c2h_wakeupmode;
+ psdata->c2h_wakeup_gpio = wakeup_parm.c2h_wakeup_gpio;
+ psdata->h2c_wakeup_gpio = wakeup_parm.h2c_wakeup_gpio;
+ switch (wakeup_parm.h2c_wakeupmode) {
+ case BT_CTRL_WAKEUP_METHOD_GPIO:
+ psdata->h2c_wakeupmode = WAKEUP_METHOD_GPIO;
+ break;
+ case BT_CTRL_WAKEUP_METHOD_DSR:
+ psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
+ break;
+ case BT_CTRL_WAKEUP_METHOD_BREAK:
+ default:
+ psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK;
+ break;
+ }
+ hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL);
+ goto free_skb;
+ }
+ break;
+ case HCI_NXP_SET_OPER_SPEED:
+ if (hdr->plen == sizeof(baudrate_parm)) {
+ memcpy(&baudrate_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen);
+ nxpdev->new_baudrate = __le32_to_cpu(baudrate_parm);
+ hci_cmd_sync_queue(hdev, nxp_set_baudrate_cmd, NULL, NULL);
+ goto free_skb;
+ }
+ break;
+ case HCI_NXP_IND_RESET:
+ if (hdr->plen == 1) {
+ hci_cmd_sync_queue(hdev, nxp_set_ind_reset, NULL, NULL);
+ goto free_skb;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return btnxpuart_queue_skb(hdev, skb);
+
+free_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct sk_buff *nxp_dequeue(void *data)
+{
+ struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)data;
+
+ ps_start_timer(nxpdev);
+ return skb_dequeue(&nxpdev->txq);
+}
+
+/* btnxpuart based on serdev */
+static void btnxpuart_tx_work(struct work_struct *work)
+{
+ struct btnxpuart_dev *nxpdev = container_of(work, struct btnxpuart_dev,
+ tx_work);
+ struct serdev_device *serdev = nxpdev->serdev;
+ struct hci_dev *hdev = nxpdev->hdev;
+ struct sk_buff *skb;
+ int len;
+
+ if (ps_wakeup(nxpdev))
+ return;
+
+ while ((skb = nxp_dequeue(nxpdev))) {
+ len = serdev_device_write_buf(serdev, skb->data, skb->len);
+ hdev->stat.byte_tx += len;
+
+ skb_pull(skb, len);
+ if (skb->len > 0) {
+ skb_queue_head(&nxpdev->txq, skb);
+ continue;
+ }
+
+ switch (hci_skb_pkt_type(skb)) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+
+ kfree_skb(skb);
+ }
+ clear_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state);
+}
+
+static int btnxpuart_open(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ int err = 0;
+
+ err = serdev_device_open(nxpdev->serdev);
+ if (err) {
+ bt_dev_err(hdev, "Unable to open UART device %s",
+ dev_name(&nxpdev->serdev->dev));
+ } else {
+ set_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state);
+ }
+ return err;
+}
+
+static int btnxpuart_close(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+ serdev_device_close(nxpdev->serdev);
+ skb_queue_purge(&nxpdev->txq);
+ if (!IS_ERR_OR_NULL(nxpdev->rx_skb)) {
+ kfree_skb(nxpdev->rx_skb);
+ nxpdev->rx_skb = NULL;
+ }
+ clear_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state);
+ return 0;
+}
+
+static int btnxpuart_flush(struct hci_dev *hdev)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+ /* Flush any pending characters */
+ serdev_device_write_flush(nxpdev->serdev);
+ skb_queue_purge(&nxpdev->txq);
+
+ cancel_work_sync(&nxpdev->tx_work);
+
+ if (!IS_ERR_OR_NULL(nxpdev->rx_skb)) {
+ kfree_skb(nxpdev->rx_skb);
+ nxpdev->rx_skb = NULL;
+ }
+
+ return 0;
+}
+
+static const struct h4_recv_pkt nxp_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = nxp_recv_acl_pkt },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+ { H4_RECV_ISO, .recv = hci_recv_frame },
+ { NXP_RECV_CHIP_VER_V1, .recv = nxp_recv_chip_ver_v1 },
+ { NXP_RECV_FW_REQ_V1, .recv = nxp_recv_fw_req_v1 },
+ { NXP_RECV_CHIP_VER_V3, .recv = nxp_recv_chip_ver_v3 },
+ { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 },
+};
+
+static size_t btnxpuart_receive_buf(struct serdev_device *serdev,
+ const u8 *data, size_t count)
+{
+ struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev);
+
+ ps_start_timer(nxpdev);
+
+ nxpdev->rx_skb = h4_recv_buf(&nxpdev->hu, nxpdev->rx_skb, data, count,
+ nxp_recv_pkts, ARRAY_SIZE(nxp_recv_pkts));
+ if (IS_ERR(nxpdev->rx_skb)) {
+ int err = PTR_ERR(nxpdev->rx_skb);
+ /* Safe to ignore out-of-sync bootloader signatures */
+ if (!is_fw_downloading(nxpdev) &&
+ !ind_reset_in_progress(nxpdev))
+ bt_dev_err(nxpdev->hdev, "Frame reassembly failed (%d)", err);
+ return count;
+ }
+ if (!is_fw_downloading(nxpdev) &&
+ !ind_reset_in_progress(nxpdev))
+ nxpdev->hdev->stat.byte_rx += count;
+ return count;
+}
+
+static void btnxpuart_write_wakeup(struct serdev_device *serdev)
+{
+ serdev_device_write_wakeup(serdev);
+}
+
+static const struct serdev_device_ops btnxpuart_client_ops = {
+ .receive_buf = btnxpuart_receive_buf,
+ .write_wakeup = btnxpuart_write_wakeup,
+};
+
+static void nxp_coredump_notify(struct hci_dev *hdev, int state)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct serdev_device *serdev = nxpdev->serdev;
+ char device_string[30];
+ char event_string[50];
+ char *envp[] = {device_string, event_string, NULL};
+
+ snprintf(device_string, 30, "BTNXPUART_DEV=%s", dev_name(&serdev->dev));
+ switch (state) {
+ case HCI_DEVCOREDUMP_ACTIVE:
+ snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_ACTIVE");
+ break;
+ case HCI_DEVCOREDUMP_DONE:
+ snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_DONE");
+ break;
+ case HCI_DEVCOREDUMP_TIMEOUT:
+ snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_TIMEOUT");
+ break;
+ default:
+ snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_STATE_%d",
+ state);
+ break;
+ }
+ bt_dev_dbg(hdev, "==== Send uevent: %s:%s ===", device_string,
+ event_string);
+ kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
+static int nxp_serdev_probe(struct serdev_device *serdev)
+{
+ struct hci_dev *hdev;
+ struct btnxpuart_dev *nxpdev;
+ bdaddr_t ba = {0};
+ int err;
+
+ nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL);
+ if (!nxpdev)
+ return -ENOMEM;
+
+ nxpdev->nxp_data = (struct btnxpuart_data *)device_get_match_data(&serdev->dev);
+
+ nxpdev->serdev = serdev;
+ serdev_device_set_drvdata(serdev, nxpdev);
+
+ serdev_device_set_client_ops(serdev, &btnxpuart_client_ops);
+
+ INIT_WORK(&nxpdev->tx_work, btnxpuart_tx_work);
+ skb_queue_head_init(&nxpdev->txq);
+
+ init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q);
+ init_waitqueue_head(&nxpdev->check_boot_sign_wait_q);
+
+ device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate",
+ &nxpdev->fw_init_baudrate);
+ if (!nxpdev->fw_init_baudrate)
+ nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE;
+
+ device_property_read_u32(&nxpdev->serdev->dev, "max-speed",
+ &nxpdev->secondary_baudrate);
+ if (!nxpdev->secondary_baudrate ||
+ (nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_3M &&
+ nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_4M)) {
+ if (nxpdev->secondary_baudrate)
+ dev_err(&serdev->dev,
+ "Invalid max-speed. Using default 3000000.");
+ nxpdev->secondary_baudrate = HCI_NXP_SEC_BAUDRATE_3M;
+ }
+
+ set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+
+ crc8_populate_msb(crc8_table, POLYNOMIAL8);
+
+ nxpdev->pdn = devm_reset_control_get_optional_shared(&serdev->dev, NULL);
+ if (IS_ERR(nxpdev->pdn))
+ return PTR_ERR(nxpdev->pdn);
+
+ err = devm_regulator_get_enable(&serdev->dev, "vcc");
+ if (err) {
+ dev_err(&serdev->dev, "Failed to enable vcc regulator\n");
+ return err;
+ }
+
+ /* Initialize and register HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ dev_err(&serdev->dev, "Can't allocate HCI device\n");
+ return -ENOMEM;
+ }
+
+ reset_control_deassert(nxpdev->pdn);
+
+ nxpdev->hdev = hdev;
+ nxpdev->hu.hdev = hdev;
+
+ hdev->bus = HCI_UART;
+ hci_set_drvdata(hdev, nxpdev);
+
+ hdev->manufacturer = MANUFACTURER_NXP;
+ hdev->open = btnxpuart_open;
+ hdev->close = btnxpuart_close;
+ hdev->flush = btnxpuart_flush;
+ hdev->setup = nxp_setup;
+ hdev->post_init = nxp_post_init;
+ hdev->send = nxp_enqueue;
+ hdev->hw_error = nxp_hw_err;
+ hdev->shutdown = nxp_shutdown;
+ hdev->wakeup = nxp_wakeup;
+ hdev->reset = nxp_reset;
+ hdev->set_bdaddr = nxp_set_bdaddr;
+ SET_HCIDEV_DEV(hdev, &serdev->dev);
+
+ device_property_read_u8_array(&nxpdev->serdev->dev,
+ "local-bd-address",
+ (u8 *)&ba, sizeof(ba));
+ if (bacmp(&ba, BDADDR_ANY))
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+
+ if (hci_register_dev(hdev) < 0) {
+ dev_err(&serdev->dev, "Can't register HCI device\n");
+ goto probe_fail;
+ }
+
+ if (ps_setup(hdev))
+ goto probe_fail;
+
+ hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr,
+ nxp_coredump_notify);
+
+ return 0;
+
+probe_fail:
+ reset_control_assert(nxpdev->pdn);
+ hci_free_dev(hdev);
+ return -ENODEV;
+}
+
+static void nxp_serdev_remove(struct serdev_device *serdev)
+{
+ struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev);
+ struct hci_dev *hdev = nxpdev->hdev;
+
+ if (is_fw_downloading(nxpdev)) {
+ set_bit(BTNXPUART_FW_DOWNLOAD_ABORT, &nxpdev->tx_state);
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ wake_up_interruptible(&nxpdev->check_boot_sign_wait_q);
+ wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+ } else {
+ /* Restore FW baudrate to fw_init_baudrate if changed.
+ * This will ensure FW baudrate is in sync with
+ * driver baudrate in case this driver is re-inserted.
+ */
+ if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) {
+ nxpdev->new_baudrate = nxpdev->fw_init_baudrate;
+ nxp_set_baudrate_cmd(hdev, NULL);
+ }
+ }
+
+ ps_cleanup(nxpdev);
+ hci_unregister_dev(hdev);
+ reset_control_assert(nxpdev->pdn);
+ hci_free_dev(hdev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nxp_serdev_suspend(struct device *dev)
+{
+ struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
+ struct ps_data *psdata = &nxpdev->psdata;
+
+ ps_control(psdata->hdev, PS_STATE_SLEEP);
+
+ if (psdata->wakeup_source) {
+ enable_irq_wake(psdata->irq_handler);
+ enable_irq(psdata->irq_handler);
+ }
+ return 0;
+}
+
+static int nxp_serdev_resume(struct device *dev)
+{
+ struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
+ struct ps_data *psdata = &nxpdev->psdata;
+
+ if (psdata->wakeup_source) {
+ disable_irq(psdata->irq_handler);
+ disable_irq_wake(psdata->irq_handler);
+ }
+
+ ps_control(psdata->hdev, PS_STATE_AWAKE);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_DEV_COREDUMP
+static void nxp_serdev_coredump(struct device *dev)
+{
+ struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
+ struct hci_dev *hdev = nxpdev->hdev;
+
+ if (hdev->dump.coredump)
+ hdev->dump.coredump(hdev);
+}
+#endif
+
+static struct btnxpuart_data w8987_data __maybe_unused = {
+ .helper_fw_name = NULL,
+ .fw_name = FIRMWARE_W8987,
+ .fw_name_old = FIRMWARE_W8987_OLD,
+};
+
+static struct btnxpuart_data w8997_data __maybe_unused = {
+ .helper_fw_name = FIRMWARE_HELPER,
+ .fw_name = FIRMWARE_W8997,
+ .fw_name_old = FIRMWARE_W8997_OLD,
+};
+
+static const struct of_device_id nxpuart_of_match_table[] __maybe_unused = {
+ { .compatible = "nxp,88w8987-bt", .data = &w8987_data },
+ { .compatible = "nxp,88w8997-bt", .data = &w8997_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nxpuart_of_match_table);
+
+static const struct dev_pm_ops nxp_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(nxp_serdev_suspend, nxp_serdev_resume)
+};
+
+static struct serdev_device_driver nxp_serdev_driver = {
+ .probe = nxp_serdev_probe,
+ .remove = nxp_serdev_remove,
+ .driver = {
+ .name = "btnxpuart",
+ .of_match_table = of_match_ptr(nxpuart_of_match_table),
+ .pm = &nxp_pm_ops,
+#ifdef CONFIG_DEV_COREDUMP
+ .coredump = nxp_serdev_coredump,
+#endif
+ },
+};
+
+module_serdev_device_driver(nxp_serdev_driver);
+
+MODULE_AUTHOR("Neeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>");
+MODULE_DESCRIPTION("NXP Bluetooth Serial driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index c9064d34d830..7c958d6065be 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -13,8 +13,6 @@
#include "btqca.h"
-#define VERSION "0.1"
-
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
enum qca_btsoc_type soc_type)
{
@@ -55,11 +53,6 @@ int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
}
edl = (struct edl_event_hdr *)(skb->data);
- if (!edl) {
- bt_dev_err(hdev, "QCA TLV with no header");
- err = -EILSEQ;
- goto out;
- }
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != rtype) {
@@ -99,7 +92,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
- char cmd, build_label[QCA_FW_BUILD_VER_LEN];
+ char *build_label;
+ char cmd;
int build_lbl_len, err = 0;
bt_dev_dbg(hdev, "QCA read fw build info");
@@ -114,13 +108,13 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
return err;
}
- edl = (struct edl_event_hdr *)(skb->data);
- if (!edl) {
- bt_dev_err(hdev, "QCA read fw build info with no header");
+ if (skb->len < sizeof(*edl)) {
err = -EILSEQ;
goto out;
}
+ edl = (struct edl_event_hdr *)(skb->data);
+
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_GET_BUILD_INFO_CMD) {
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
@@ -129,14 +123,27 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
goto out;
}
+ if (skb->len < sizeof(*edl) + 1) {
+ err = -EILSEQ;
+ goto out;
+ }
+
build_lbl_len = edl->data[0];
- if (build_lbl_len <= QCA_FW_BUILD_VER_LEN - 1) {
- memcpy(build_label, edl->data + 1, build_lbl_len);
- *(build_label + build_lbl_len) = '\0';
+
+ if (skb->len < sizeof(*edl) + 1 + build_lbl_len) {
+ err = -EILSEQ;
+ goto out;
+ }
+
+ build_label = kstrndup(&edl->data[1], build_lbl_len, GFP_KERNEL);
+ if (!build_label) {
+ err = -ENOMEM;
+ goto out;
}
hci_set_fw_info(hdev, "%s", build_label);
+ kfree(build_label);
out:
kfree_skb(skb);
return err;
@@ -152,7 +159,7 @@ static int qca_send_patch_config_cmd(struct hci_dev *hdev)
bt_dev_dbg(hdev, "QCA Patch config");
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, sizeof(cmd),
- cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
+ cmd, 0, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "Sending QCA Patch config failed (%d)", err);
@@ -166,11 +173,6 @@ static int qca_send_patch_config_cmd(struct hci_dev *hdev)
}
edl = (struct edl_event_hdr *)(skb->data);
- if (!edl) {
- bt_dev_err(hdev, "QCA Patch config with no header");
- err = -EILSEQ;
- goto out;
- }
if (edl->cresp != EDL_PATCH_CONFIG_RES_EVT || edl->rtype != EDL_PATCH_CONFIG_CMD) {
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
@@ -205,6 +207,49 @@ static int qca_send_reset(struct hci_dev *hdev)
return 0;
}
+static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
+{
+ u8 cmd;
+ struct sk_buff *skb;
+ struct edl_event_hdr *edl;
+ int err = 0;
+
+ cmd = EDL_GET_BID_REQ_CMD;
+ skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
+ &cmd, 0, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Reading QCA board ID failed (%d)", err);
+ return err;
+ }
+
+ edl = skb_pull_data(skb, sizeof(*edl));
+ if (!edl) {
+ bt_dev_err(hdev, "QCA read board ID with no header");
+ err = -EILSEQ;
+ goto out;
+ }
+
+ if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
+ edl->rtype != EDL_GET_BID_REQ_CMD) {
+ bt_dev_err(hdev, "QCA Wrong packet: %d %d", edl->cresp, edl->rtype);
+ err = -EIO;
+ goto out;
+ }
+
+ if (skb->len < 3) {
+ err = -EILSEQ;
+ goto out;
+ }
+
+ *bid = (edl->data[1] << 8) + edl->data[2];
+ bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid);
+
+out:
+ kfree_skb(skb);
+ return err;
+}
+
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
{
struct sk_buff *skb;
@@ -227,9 +272,43 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
-static void qca_tlv_check_data(struct hci_dev *hdev,
+static bool qca_filename_has_extension(const char *filename)
+{
+ const char *suffix = strrchr(filename, '.');
+
+ /* File extensions require a dot, but not as the first or last character */
+ if (!suffix || suffix == filename || *(suffix + 1) == '\0')
+ return 0;
+
+ /* Avoid matching directories with names that look like files with extensions */
+ return !strchr(suffix, '/');
+}
+
+static bool qca_get_alt_nvm_file(char *filename, size_t max_size)
+{
+ char fwname[64];
+ const char *suffix;
+
+ /* nvm file name has an extension, replace with .bin */
+ if (qca_filename_has_extension(filename)) {
+ suffix = strrchr(filename, '.');
+ strscpy(fwname, filename, suffix - filename + 1);
+ snprintf(fwname + (suffix - filename),
+ sizeof(fwname) - (suffix - filename), ".bin");
+ /* If nvm file is already the default one, return false to skip the retry. */
+ if (strcmp(fwname, filename) == 0)
+ return false;
+
+ snprintf(filename, max_size, "%s", fwname);
+ return true;
+ }
+ return false;
+}
+
+static int qca_tlv_check_data(struct hci_dev *hdev,
struct qca_fw_config *config,
- u8 *fw_data, enum qca_btsoc_type soc_type)
+ u8 *fw_data, size_t fw_size,
+ enum qca_btsoc_type soc_type)
{
const u8 *data;
u32 type_len;
@@ -239,12 +318,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
struct tlv_type_patch *tlv_patch;
struct tlv_type_nvm *tlv_nvm;
uint8_t nvm_baud_rate = config->user_baud_rate;
+ u8 type;
config->dnld_mode = QCA_SKIP_EVT_NONE;
config->dnld_type = QCA_SKIP_EVT_NONE;
switch (config->type) {
case ELF_TYPE_PATCH:
+ if (fw_size < 7)
+ return -EINVAL;
+
config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
config->dnld_type = QCA_SKIP_EVT_VSE_CC;
@@ -253,6 +336,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]);
break;
case TLV_TYPE_PATCH:
+ if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
+ return -EINVAL;
+
tlv = (struct tlv_type_hdr *)fw_data;
type_len = le32_to_cpu(tlv->type_len);
tlv_patch = (struct tlv_type_patch *)tlv->data;
@@ -292,25 +378,64 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break;
case TLV_TYPE_NVM:
+ if (fw_size < sizeof(struct tlv_type_hdr))
+ return -EINVAL;
+
tlv = (struct tlv_type_hdr *)fw_data;
type_len = le32_to_cpu(tlv->type_len);
- length = (type_len >> 8) & 0x00ffffff;
+ length = type_len >> 8;
+ type = type_len & 0xff;
- BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
+ /* Some NVM files have more than one set of tags, only parse
+ * the first set when it has type 2 for now. When there is
+ * more than one set there is an enclosing header of type 4.
+ */
+ if (type == 4) {
+ if (fw_size < 2 * sizeof(struct tlv_type_hdr))
+ return -EINVAL;
+
+ tlv++;
+
+ type_len = le32_to_cpu(tlv->type_len);
+ length = type_len >> 8;
+ type = type_len & 0xff;
+ }
+
+ BT_DBG("TLV Type\t\t : 0x%x", type);
BT_DBG("Length\t\t : %d bytes", length);
+ if (type != 2)
+ break;
+
+ if (fw_size < length + (tlv->data - fw_data))
+ return -EINVAL;
+
idx = 0;
data = tlv->data;
- while (idx < length) {
+ while (idx < length - sizeof(struct tlv_type_nvm)) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx);
tag_id = le16_to_cpu(tlv_nvm->tag_id);
tag_len = le16_to_cpu(tlv_nvm->tag_len);
+ if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
+ return -EINVAL;
+
/* Update NVM tags as needed */
switch (tag_id) {
+ case EDL_TAG_ID_BD_ADDR:
+ if (tag_len != sizeof(bdaddr_t))
+ return -EINVAL;
+
+ memcpy(&config->bdaddr, tlv_nvm->data, sizeof(bdaddr_t));
+
+ break;
+
case EDL_TAG_ID_HCI:
+ if (tag_len < 3)
+ return -EINVAL;
+
/* HCI transport layer parameters
* enabling software inband sleep
* onto controller side.
@@ -326,6 +451,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break;
case EDL_TAG_ID_DEEP_SLEEP:
+ if (tag_len < 1)
+ return -EINVAL;
+
/* Sleep enable mask
* enabling deep sleep feature on controller.
*/
@@ -334,14 +462,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break;
}
- idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
+ idx += sizeof(struct tlv_type_nvm) + tag_len;
}
break;
default:
BT_ERR("Unknown TLV type %d", config->type);
- break;
+ return -EINVAL;
}
+
+ return 0;
}
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
@@ -390,11 +520,6 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
}
edl = (struct edl_event_hdr *)(skb->data);
- if (!edl) {
- bt_dev_err(hdev, "TLV with no header");
- err = -EILSEQ;
- goto out;
- }
if (edl->cresp != EDL_CMD_REQ_RES_EVT || edl->rtype != rtype) {
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x",
@@ -472,6 +597,19 @@ static int qca_download_firmware(struct hci_dev *hdev,
config->fwname, ret);
return ret;
}
+ }
+ /* If the board-specific file is missing, try loading the default
+ * one, unless that was attempted already.
+ */
+ else if (config->type == TLV_TYPE_NVM &&
+ qca_get_alt_nvm_file(config->fwname, sizeof(config->fwname))) {
+ bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
+ ret = request_firmware(&fw, config->fwname, &hdev->dev);
+ if (ret) {
+ bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
+ config->fwname, ret);
+ return ret;
+ }
} else {
bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
config->fwname, ret);
@@ -491,7 +629,9 @@ static int qca_download_firmware(struct hci_dev *hdev,
memcpy(data, fw->data, size);
release_firmware(fw);
- qca_tlv_check_data(hdev, config, data, soc_type);
+ ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
+ if (ret)
+ goto out;
segment = data;
remain = size;
@@ -574,14 +714,82 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
}
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
+static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
+{
+ struct hci_rp_read_bd_addr *bda;
+ struct sk_buff *skb;
+ int err;
+
+ if (bacmp(&hdev->public_addr, BDADDR_ANY))
+ return 0;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to read device address (%d)", err);
+ return err;
+ }
+
+ if (skb->len != sizeof(*bda)) {
+ bt_dev_err(hdev, "Device address length mismatch");
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ bda = (struct hci_rp_read_bd_addr *)skb->data;
+ if (!bacmp(&bda->bdaddr, &config->bdaddr))
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void qca_get_nvm_name_by_board(char *fwname, size_t max_size,
+ const char *stem, enum qca_btsoc_type soc_type,
+ struct qca_btsoc_version ver, u8 rom_ver, u16 bid)
+{
+ const char *variant;
+ const char *prefix;
+
+ /* Set the default value to variant and prefix */
+ variant = "";
+ prefix = "b";
+
+ if (soc_type == QCA_QCA2066)
+ prefix = "";
+
+ if (soc_type == QCA_WCN6855 || soc_type == QCA_QCA2066) {
+ /* If the chip is manufactured by GlobalFoundries */
+ if ((le32_to_cpu(ver.soc_id) & QCA_HSP_GF_SOC_MASK) == QCA_HSP_GF_SOC_ID)
+ variant = "g";
+ }
+
+ if (rom_ver != 0) {
+ if (bid == 0x0 || bid == 0xffff)
+ snprintf(fwname, max_size, "qca/%s%02x%s.bin", stem, rom_ver, variant);
+ else
+ snprintf(fwname, max_size, "qca/%s%02x%s.%s%02x", stem, rom_ver,
+ variant, prefix, bid);
+ } else {
+ if (bid == 0x0 || bid == 0xffff)
+ snprintf(fwname, max_size, "qca/%s%s.bin", stem, variant);
+ else
+ snprintf(fwname, max_size, "qca/%s%s.%s%02x", stem, variant, prefix, bid);
+ }
+}
+
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
- const char *firmware_name)
+ const char *firmware_name, const char *rampatch_name)
{
- struct qca_fw_config config;
+ struct qca_fw_config config = {};
+ const char *variant = "";
int err;
u8 rom_ver = 0;
u32 soc_ver;
+ u16 boardid = 0;
bt_dev_dbg(hdev, "QCA setup on UART");
@@ -594,29 +802,62 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
/* Firmware files to download are based on ROM version.
* ROM version is derived from last two bytes of soc_ver.
*/
- rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
+ if (soc_type == QCA_WCN3988)
+ rom_ver = ((soc_ver & 0x00000f00) >> 0x05) | (soc_ver & 0x0000000f);
+ else
+ rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
if (soc_type == QCA_WCN6750)
qca_send_patch_config_cmd(hdev);
/* Download rampatch file */
config.type = TLV_TYPE_PATCH;
- if (qca_is_wcn399x(soc_type)) {
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/crbtfw%02x.tlv", rom_ver);
- } else if (soc_type == QCA_QCA6390) {
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/htbtfw%02x.tlv", rom_ver);
- } else if (soc_type == QCA_WCN6750) {
- /* Choose mbn file by default.If mbn file is not found
- * then choose tlv file
- */
- config.type = ELF_TYPE_PATCH;
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/msbtfw%02x.mbn", rom_ver);
+ if (rampatch_name) {
+ snprintf(config.fwname, sizeof(config.fwname), "qca/%s", rampatch_name);
} else {
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/rampatch_%08x.bin", soc_ver);
+ switch (soc_type) {
+ case QCA_WCN3950:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/cmbtfw%02x.tlv", rom_ver);
+ break;
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/crbtfw%02x.tlv", rom_ver);
+ break;
+ case QCA_WCN3988:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/apbtfw%02x.tlv", rom_ver);
+ break;
+ case QCA_QCA2066:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/hpbtfw%02x.tlv", rom_ver);
+ break;
+ case QCA_QCA6390:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/htbtfw%02x.tlv", rom_ver);
+ break;
+ case QCA_WCN6750:
+ /* Choose mbn file by default.If mbn file is not found
+ * then choose tlv file
+ */
+ config.type = ELF_TYPE_PATCH;
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/msbtfw%02x.mbn", rom_ver);
+ break;
+ case QCA_WCN6855:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/hpbtfw%02x.tlv", rom_ver);
+ break;
+ case QCA_WCN7850:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/hmtbtfw%02x.tlv", rom_ver);
+ break;
+ default:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/rampatch_%08x.bin", soc_ver);
+ }
}
err = qca_download_firmware(hdev, &config, soc_type, rom_ver);
@@ -628,29 +869,71 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
/* Give the controller some time to get ready to receive the NVM */
msleep(10);
+ if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
+ qca_read_fw_board_id(hdev, &boardid);
+
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
- if (firmware_name)
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/%s", firmware_name);
- else if (qca_is_wcn399x(soc_type)) {
- if (ver.soc_id == QCA_WCN3991_SOC_ID) {
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/crnv%02xu.bin", rom_ver);
+ if (firmware_name) {
+ /* The firmware name has an extension, use it directly */
+ if (qca_filename_has_extension(firmware_name)) {
+ snprintf(config.fwname, sizeof(config.fwname), "qca/%s", firmware_name);
} else {
+ qca_read_fw_board_id(hdev, &boardid);
+ qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
+ firmware_name, soc_type, ver, 0, boardid);
+ }
+ } else {
+ switch (soc_type) {
+ case QCA_WCN3950:
+ if (le32_to_cpu(ver.soc_id) == QCA_WCN3950_SOC_ID_T)
+ variant = "t";
+ else if (le32_to_cpu(ver.soc_id) == QCA_WCN3950_SOC_ID_S)
+ variant = "s";
+
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/cmnv%02x%s.bin", rom_ver, variant);
+ break;
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ if (le32_to_cpu(ver.soc_id) == QCA_WCN3991_SOC_ID)
+ variant = "u";
+
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/crnv%02x%s.bin", rom_ver, variant);
+ break;
+ case QCA_WCN3988:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/apnv%02x.bin", rom_ver);
+ break;
+ case QCA_QCA2066:
+ qca_get_nvm_name_by_board(config.fwname,
+ sizeof(config.fwname), "hpnv", soc_type, ver,
+ rom_ver, boardid);
+ break;
+ case QCA_QCA6390:
snprintf(config.fwname, sizeof(config.fwname),
- "qca/crnv%02x.bin", rom_ver);
+ "qca/htnv%02x.bin", rom_ver);
+ break;
+ case QCA_WCN6750:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/msnv%02x.bin", rom_ver);
+ break;
+ case QCA_WCN6855:
+ qca_read_fw_board_id(hdev, &boardid);
+ qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
+ "hpnv", soc_type, ver, rom_ver, boardid);
+ break;
+ case QCA_WCN7850:
+ qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
+ "hmtnv", soc_type, ver, rom_ver, boardid);
+ break;
+ default:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/nvm_%08x.bin", soc_ver);
}
}
- else if (soc_type == QCA_QCA6390)
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/htnv%02x.bin", rom_ver);
- else if (soc_type == QCA_WCN6750)
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/msnv%02x.bin", rom_ver);
- else
- snprintf(config.fwname, sizeof(config.fwname),
- "qca/nvm_%08x.bin", soc_ver);
err = qca_download_firmware(hdev, &config, soc_type, rom_ver);
if (err < 0) {
@@ -658,16 +941,27 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}
- if (soc_type >= QCA_WCN3991) {
+ switch (soc_type) {
+ case QCA_WCN3991:
+ case QCA_QCA2066:
+ case QCA_QCA6390:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
err = qca_disable_soc_logging(hdev);
if (err < 0)
return err;
+ break;
+ default:
+ break;
}
/* WCN399x and WCN6750 supports the Microsoft vendor extension with 0xFD70 as the
* VsMsftOpCode.
*/
switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
@@ -685,13 +979,24 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}
- if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750) {
+ switch (soc_type) {
+ case QCA_WCN3991:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
/* get fw build info */
err = qca_read_fw_build_info(hdev);
if (err < 0)
return err;
+ break;
+ default:
+ break;
}
+ err = qca_check_bdaddr(hdev, &config);
+ if (err)
+ return err;
+
bt_dev_info(hdev, "QCA setup on UART is completed");
return 0;
@@ -700,11 +1005,15 @@ EXPORT_SYMBOL_GPL(qca_uart_setup);
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
+ bdaddr_t bdaddr_swapped;
struct sk_buff *skb;
int err;
- skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6, bdaddr,
- HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
+ baswap(&bdaddr_swapped, bdaddr);
+
+ skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6,
+ &bdaddr_swapped, HCI_EV_VENDOR,
+ HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err);
@@ -719,6 +1028,5 @@ EXPORT_SYMBOL_GPL(qca_set_bdaddr);
MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>");
-MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION);
-MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family");
MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 61e9a50e66ae..8f3c1b1c77b3 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -5,31 +5,33 @@
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*/
-#define EDL_PATCH_CMD_OPCODE (0xFC00)
-#define EDL_NVM_ACCESS_OPCODE (0xFC0B)
-#define EDL_WRITE_BD_ADDR_OPCODE (0xFC14)
-#define EDL_PATCH_CMD_LEN (1)
-#define EDL_PATCH_VER_REQ_CMD (0x19)
-#define EDL_PATCH_TLV_REQ_CMD (0x1E)
-#define EDL_GET_BUILD_INFO_CMD (0x20)
-#define EDL_NVM_ACCESS_SET_REQ_CMD (0x01)
-#define EDL_PATCH_CONFIG_CMD (0x28)
-#define MAX_SIZE_PER_TLV_SEGMENT (243)
-#define QCA_PRE_SHUTDOWN_CMD (0xFC08)
-#define QCA_DISABLE_LOGGING (0xFC17)
-
-#define EDL_CMD_REQ_RES_EVT (0x00)
-#define EDL_PATCH_VER_RES_EVT (0x19)
-#define EDL_APP_VER_RES_EVT (0x02)
-#define EDL_TVL_DNLD_RES_EVT (0x04)
-#define EDL_CMD_EXE_STATUS_EVT (0x00)
-#define EDL_SET_BAUDRATE_RSP_EVT (0x92)
-#define EDL_NVM_ACCESS_CODE_EVT (0x0B)
-#define EDL_PATCH_CONFIG_RES_EVT (0x00)
-#define QCA_DISABLE_LOGGING_SUB_OP (0x14)
-
-#define EDL_TAG_ID_HCI (17)
-#define EDL_TAG_ID_DEEP_SLEEP (27)
+#define EDL_PATCH_CMD_OPCODE 0xFC00
+#define EDL_NVM_ACCESS_OPCODE 0xFC0B
+#define EDL_WRITE_BD_ADDR_OPCODE 0xFC14
+#define EDL_PATCH_CMD_LEN 1
+#define EDL_PATCH_VER_REQ_CMD 0x19
+#define EDL_PATCH_TLV_REQ_CMD 0x1E
+#define EDL_GET_BUILD_INFO_CMD 0x20
+#define EDL_GET_BID_REQ_CMD 0x23
+#define EDL_NVM_ACCESS_SET_REQ_CMD 0x01
+#define EDL_PATCH_CONFIG_CMD 0x28
+#define MAX_SIZE_PER_TLV_SEGMENT 243
+#define QCA_PRE_SHUTDOWN_CMD 0xFC08
+#define QCA_DISABLE_LOGGING 0xFC17
+
+#define EDL_CMD_REQ_RES_EVT 0x00
+#define EDL_PATCH_VER_RES_EVT 0x19
+#define EDL_APP_VER_RES_EVT 0x02
+#define EDL_TVL_DNLD_RES_EVT 0x04
+#define EDL_CMD_EXE_STATUS_EVT 0x00
+#define EDL_SET_BAUDRATE_RSP_EVT 0x92
+#define EDL_NVM_ACCESS_CODE_EVT 0x0B
+#define EDL_PATCH_CONFIG_RES_EVT 0x00
+#define QCA_DISABLE_LOGGING_SUB_OP 0x14
+
+#define EDL_TAG_ID_BD_ADDR 2
+#define EDL_TAG_ID_HCI 17
+#define EDL_TAG_ID_DEEP_SLEEP 27
#define QCA_WCN3990_POWERON_PULSE 0xFC
#define QCA_WCN3990_POWEROFF_PULSE 0xC0
@@ -37,7 +39,10 @@
#define QCA_HCI_CC_OPCODE 0xFC00
#define QCA_HCI_CC_SUCCESS 0x00
-#define QCA_WCN3991_SOC_ID (0x40014320)
+#define QCA_WCN3991_SOC_ID 0x40014320
+
+#define QCA_WCN3950_SOC_ID_T 0x40074130
+#define QCA_WCN3950_SOC_ID_S 0x40075130
/* QCA chipset version can be decided by patch and SoC
* version, combination with upper 2 bytes from SoC
@@ -46,11 +51,11 @@
#define get_soc_ver(soc_id, rom_ver) \
((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver)))
-#define QCA_FW_BUILD_VER_LEN 255
-
+#define QCA_HSP_GF_SOC_ID 0x1200
+#define QCA_HSP_GF_SOC_MASK 0x0000ff00
enum qca_baudrate {
- QCA_BAUDRATE_115200 = 0,
+ QCA_BAUDRATE_115200 = 0,
QCA_BAUDRATE_57600,
QCA_BAUDRATE_38400,
QCA_BAUDRATE_19200,
@@ -69,7 +74,7 @@ enum qca_baudrate {
QCA_BAUDRATE_1600000,
QCA_BAUDRATE_3200000,
QCA_BAUDRATE_3500000,
- QCA_BAUDRATE_AUTO = 0xFE,
+ QCA_BAUDRATE_AUTO = 0xFE,
QCA_BAUDRATE_RESERVED
};
@@ -92,6 +97,7 @@ struct qca_fw_config {
uint8_t user_baud_rate;
enum qca_tlv_dnld_mode dnld_mode;
enum qca_tlv_dnld_mode dnld_type;
+ bdaddr_t bdaddr;
};
struct edl_event_hdr {
@@ -142,11 +148,16 @@ enum qca_btsoc_type {
QCA_INVALID = -1,
QCA_AR3002,
QCA_ROME,
+ QCA_WCN3950,
+ QCA_WCN3988,
QCA_WCN3990,
QCA_WCN3998,
QCA_WCN3991,
+ QCA_QCA2066,
QCA_QCA6390,
QCA_WCN6750,
+ QCA_WCN6855,
+ QCA_WCN7850,
};
#if IS_ENABLED(CONFIG_BT_QCA)
@@ -154,21 +165,11 @@ enum qca_btsoc_type {
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
- const char *firmware_name);
+ const char *firmware_name, const char *rampatch_name);
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
enum qca_btsoc_type);
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
-static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
-{
- return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3991 ||
- soc_type == QCA_WCN3998;
-}
-static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
-{
- return soc_type == QCA_WCN6750;
-}
-
#else
static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
@@ -179,7 +180,8 @@ static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdad
static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type,
struct qca_btsoc_version ver,
- const char *firmware_name)
+ const char *firmware_name,
+ const char *rampatch_name)
{
return -EOPNOTSUPP;
}
@@ -196,16 +198,6 @@ static inline int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
return -EOPNOTSUPP;
}
-static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
-{
- return false;
-}
-
-static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
-{
- return false;
-}
-
static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c
index 2acb719e596f..d2e13fcb6bab 100644
--- a/drivers/bluetooth/btqcomsmd.c
+++ b/drivers/bluetooth/btqcomsmd.c
@@ -117,11 +117,26 @@ static int btqcomsmd_setup(struct hci_dev *hdev)
/* Devices do not have persistent storage for BD address. Retrieve
* it from the firmware node property.
*/
- set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
return 0;
}
+static int btqcomsmd_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ int ret;
+
+ ret = qca_set_bdaddr_rome(hdev, bdaddr);
+ if (ret)
+ return ret;
+
+ /* The firmware stops responding for a while after setting the bdaddr,
+ * causing timeouts for subsequent commands. Sleep a bit to avoid this.
+ */
+ usleep_range(1000, 10000);
+ return 0;
+}
+
static int btqcomsmd_probe(struct platform_device *pdev)
{
struct btqcomsmd *btq;
@@ -162,7 +177,7 @@ static int btqcomsmd_probe(struct platform_device *pdev)
hdev->close = btqcomsmd_close;
hdev->send = btqcomsmd_send;
hdev->setup = btqcomsmd_setup;
- hdev->set_bdaddr = qca_set_bdaddr_rome;
+ hdev->set_bdaddr = btqcomsmd_set_bdaddr;
ret = hci_register_dev(hdev);
if (ret < 0)
@@ -182,7 +197,7 @@ destroy_acl_channel:
return ret;
}
-static int btqcomsmd_remove(struct platform_device *pdev)
+static void btqcomsmd_remove(struct platform_device *pdev)
{
struct btqcomsmd *btq = platform_get_drvdata(pdev);
@@ -191,8 +206,6 @@ static int btqcomsmd_remove(struct platform_device *pdev)
rpmsg_destroy_ept(btq->cmd_channel);
rpmsg_destroy_ept(btq->acl_channel);
-
- return 0;
}
static const struct of_device_id btqcomsmd_of_match[] = {
diff --git a/drivers/bluetooth/btrsi.c b/drivers/bluetooth/btrsi.c
index 634cf8f5ed2d..6c1f584c8a33 100644
--- a/drivers/bluetooth/btrsi.c
+++ b/drivers/bluetooth/btrsi.c
@@ -17,7 +17,7 @@
#include <linux/kernel.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/rsi_91x.h>
#define RSI_DMA_ALIGN 8
@@ -134,7 +134,6 @@ static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, h_adapter);
- hdev->dev_type = HCI_PRIMARY;
hdev->open = rsi_hci_open;
hdev->close = rsi_hci_close;
hdev->flush = rsi_hci_flush;
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index fb52313a1d45..5603b282f9bc 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -7,7 +7,7 @@
#include <linux/module.h>
#include <linux/firmware.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
@@ -17,19 +17,29 @@
#define VERSION "0.1"
+#define RTL_CHIP_8723CS_CG 3
+#define RTL_CHIP_8723CS_VF 4
+#define RTL_CHIP_8723CS_XX 5
#define RTL_EPATCH_SIGNATURE "Realtech"
+#define RTL_EPATCH_SIGNATURE_V2 "RTBTCore"
+#define RTL_ROM_LMP_8703B 0x8703
#define RTL_ROM_LMP_8723A 0x1200
#define RTL_ROM_LMP_8723B 0x8723
#define RTL_ROM_LMP_8821A 0x8821
#define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822
#define RTL_ROM_LMP_8852A 0x8852
+#define RTL_ROM_LMP_8851B 0x8851
+#define RTL_ROM_LMP_8922A 0x8922
#define RTL_CONFIG_MAGIC 0x8723ab55
+#define RTL_VSC_OP_COREDUMP 0xfcff
+
#define IC_MATCH_FL_LMPSUBV (1 << 0)
#define IC_MATCH_FL_HCIREV (1 << 1)
#define IC_MATCH_FL_HCIVER (1 << 2)
#define IC_MATCH_FL_HCIBUS (1 << 3)
+#define IC_MATCH_FL_CHIP_TYPE (1 << 4)
#define IC_INFO(lmps, hcir, hciv, bus) \
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \
@@ -38,6 +48,14 @@
.hci_ver = (hciv), \
.hci_bus = (bus)
+#define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
+#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
+#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
+
+#define RTL_PATCH_SNIPPETS 0x01
+#define RTL_PATCH_DUMMY_HEADER 0x02
+#define RTL_PATCH_SECURITY_HEADER 0x03
+
enum btrtl_chip_id {
CHIP_ID_8723A,
CHIP_ID_8723B,
@@ -51,6 +69,10 @@ enum btrtl_chip_id {
CHIP_ID_8852A = 18,
CHIP_ID_8852B = 20,
CHIP_ID_8852C = 25,
+ CHIP_ID_8851B = 36,
+ CHIP_ID_8922A = 44,
+ CHIP_ID_8852BT = 47,
+ CHIP_ID_8761C = 51,
};
struct id_table {
@@ -59,11 +81,13 @@ struct id_table {
__u16 hci_rev;
__u8 hci_ver;
__u8 hci_bus;
+ __u8 chip_type;
bool config_needed;
bool has_rom_version;
bool has_msft_ext;
char *fw_name;
char *cfg_name;
+ char *hw_info;
};
struct btrtl_device_info {
@@ -75,6 +99,8 @@ struct btrtl_device_info {
int cfg_len;
bool drop_fw;
int project_id;
+ u8 key_id;
+ struct list_head patch_subsecs;
};
static const struct id_table ic_id_table[] = {
@@ -82,133 +108,240 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = false,
- .fw_name = "rtl_bt/rtl8723a_fw.bin",
- .cfg_name = NULL },
+ .fw_name = "rtl_bt/rtl8723a_fw",
+ .cfg_name = NULL,
+ .hw_info = "rtl8723au" },
/* 8723BS */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
.config_needed = true,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8723bs_fw.bin",
- .cfg_name = "rtl_bt/rtl8723bs_config" },
+ .fw_name = "rtl_bt/rtl8723bs_fw",
+ .cfg_name = "rtl_bt/rtl8723bs_config",
+ .hw_info = "rtl8723bs" },
/* 8723B */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8723b_fw.bin",
- .cfg_name = "rtl_bt/rtl8723b_config" },
+ .fw_name = "rtl_bt/rtl8723b_fw",
+ .cfg_name = "rtl_bt/rtl8723b_config",
+ .hw_info = "rtl8723bu" },
+
+ /* 8723CS-CG */
+ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
+ IC_MATCH_FL_HCIBUS,
+ .lmp_subver = RTL_ROM_LMP_8703B,
+ .chip_type = RTL_CHIP_8723CS_CG,
+ .hci_bus = HCI_UART,
+ .config_needed = true,
+ .has_rom_version = true,
+ .fw_name = "rtl_bt/rtl8723cs_cg_fw",
+ .cfg_name = "rtl_bt/rtl8723cs_cg_config",
+ .hw_info = "rtl8723cs-cg" },
+
+ /* 8723CS-VF */
+ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
+ IC_MATCH_FL_HCIBUS,
+ .lmp_subver = RTL_ROM_LMP_8703B,
+ .chip_type = RTL_CHIP_8723CS_VF,
+ .hci_bus = HCI_UART,
+ .config_needed = true,
+ .has_rom_version = true,
+ .fw_name = "rtl_bt/rtl8723cs_vf_fw",
+ .cfg_name = "rtl_bt/rtl8723cs_vf_config",
+ .hw_info = "rtl8723cs-vf" },
+
+ /* 8723CS-XX */
+ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
+ IC_MATCH_FL_HCIBUS,
+ .lmp_subver = RTL_ROM_LMP_8703B,
+ .chip_type = RTL_CHIP_8723CS_XX,
+ .hci_bus = HCI_UART,
+ .config_needed = true,
+ .has_rom_version = true,
+ .fw_name = "rtl_bt/rtl8723cs_xx_fw",
+ .cfg_name = "rtl_bt/rtl8723cs_xx_config",
+ .hw_info = "rtl8723cs" },
/* 8723D */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
.config_needed = true,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8723d_fw.bin",
- .cfg_name = "rtl_bt/rtl8723d_config" },
+ .fw_name = "rtl_bt/rtl8723d_fw",
+ .cfg_name = "rtl_bt/rtl8723d_config",
+ .hw_info = "rtl8723du" },
/* 8723DS */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
.config_needed = true,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8723ds_fw.bin",
- .cfg_name = "rtl_bt/rtl8723ds_config" },
+ .fw_name = "rtl_bt/rtl8723ds_fw",
+ .cfg_name = "rtl_bt/rtl8723ds_config",
+ .hw_info = "rtl8723ds" },
/* 8821A */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8821a_fw.bin",
- .cfg_name = "rtl_bt/rtl8821a_config" },
+ .fw_name = "rtl_bt/rtl8821a_fw",
+ .cfg_name = "rtl_bt/rtl8821a_config",
+ .hw_info = "rtl8821au" },
/* 8821C */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8821c_fw.bin",
- .cfg_name = "rtl_bt/rtl8821c_config" },
+ .fw_name = "rtl_bt/rtl8821c_fw",
+ .cfg_name = "rtl_bt/rtl8821c_config",
+ .hw_info = "rtl8821cu" },
+
+ /* 8821CS */
+ { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART),
+ .config_needed = true,
+ .has_rom_version = true,
+ .has_msft_ext = true,
+ .fw_name = "rtl_bt/rtl8821cs_fw",
+ .cfg_name = "rtl_bt/rtl8821cs_config",
+ .hw_info = "rtl8821cs" },
/* 8761A */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8761a_fw.bin",
- .cfg_name = "rtl_bt/rtl8761a_config" },
+ .fw_name = "rtl_bt/rtl8761a_fw",
+ .cfg_name = "rtl_bt/rtl8761a_config",
+ .hw_info = "rtl8761au" },
/* 8761B */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8761b_fw.bin",
- .cfg_name = "rtl_bt/rtl8761b_config" },
+ .fw_name = "rtl_bt/rtl8761b_fw",
+ .cfg_name = "rtl_bt/rtl8761b_config",
+ .hw_info = "rtl8761btv" },
/* 8761BU */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
.config_needed = false,
.has_rom_version = true,
- .fw_name = "rtl_bt/rtl8761bu_fw.bin",
- .cfg_name = "rtl_bt/rtl8761bu_config" },
+ .fw_name = "rtl_bt/rtl8761bu_fw",
+ .cfg_name = "rtl_bt/rtl8761bu_config",
+ .hw_info = "rtl8761bu" },
+
+ /* 8761CU */
+ { IC_INFO(RTL_ROM_LMP_8761A, 0x0e, 0, HCI_USB),
+ .config_needed = false,
+ .has_rom_version = true,
+ .fw_name = "rtl_bt/rtl8761cu_fw",
+ .cfg_name = "rtl_bt/rtl8761cu_config",
+ .hw_info = "rtl8761cu" },
/* 8822C with UART interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8822cs_fw.bin",
- .cfg_name = "rtl_bt/rtl8822cs_config" },
+ .fw_name = "rtl_bt/rtl8822cs_fw",
+ .cfg_name = "rtl_bt/rtl8822cs_config",
+ .hw_info = "rtl8822cs" },
/* 8822C with UART interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8822cs_fw.bin",
- .cfg_name = "rtl_bt/rtl8822cs_config" },
+ .fw_name = "rtl_bt/rtl8822cs_fw",
+ .cfg_name = "rtl_bt/rtl8822cs_config",
+ .hw_info = "rtl8822cs" },
/* 8822C with USB interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8822cu_fw.bin",
- .cfg_name = "rtl_bt/rtl8822cu_config" },
+ .fw_name = "rtl_bt/rtl8822cu_fw",
+ .cfg_name = "rtl_bt/rtl8822cu_config",
+ .hw_info = "rtl8822cu" },
/* 8822B */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8822b_fw.bin",
- .cfg_name = "rtl_bt/rtl8822b_config" },
+ .fw_name = "rtl_bt/rtl8822b_fw",
+ .cfg_name = "rtl_bt/rtl8822b_config",
+ .hw_info = "rtl8822bu" },
/* 8852A */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8852au_fw.bin",
- .cfg_name = "rtl_bt/rtl8852au_config" },
+ .fw_name = "rtl_bt/rtl8852au_fw",
+ .cfg_name = "rtl_bt/rtl8852au_config",
+ .hw_info = "rtl8852au" },
+
+ /* 8852B with UART interface */
+ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART),
+ .config_needed = true,
+ .has_rom_version = true,
+ .has_msft_ext = true,
+ .fw_name = "rtl_bt/rtl8852bs_fw",
+ .cfg_name = "rtl_bt/rtl8852bs_config",
+ .hw_info = "rtl8852bs" },
/* 8852B */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8852bu_fw.bin",
- .cfg_name = "rtl_bt/rtl8852bu_config" },
+ .fw_name = "rtl_bt/rtl8852bu_fw",
+ .cfg_name = "rtl_bt/rtl8852bu_config",
+ .hw_info = "rtl8852bu" },
/* 8852C */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
- .fw_name = "rtl_bt/rtl8852cu_fw.bin",
- .cfg_name = "rtl_bt/rtl8852cu_config" },
+ .fw_name = "rtl_bt/rtl8852cu_fw",
+ .cfg_name = "rtl_bt/rtl8852cu_config",
+ .hw_info = "rtl8852cu" },
+
+ /* 8851B */
+ { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB),
+ .config_needed = false,
+ .has_rom_version = true,
+ .has_msft_ext = false,
+ .fw_name = "rtl_bt/rtl8851bu_fw",
+ .cfg_name = "rtl_bt/rtl8851bu_config",
+ .hw_info = "rtl8851bu" },
+
+ /* 8922A */
+ { IC_INFO(RTL_ROM_LMP_8922A, 0xa, 0xc, HCI_USB),
+ .config_needed = false,
+ .has_rom_version = true,
+ .has_msft_ext = true,
+ .fw_name = "rtl_bt/rtl8922au_fw",
+ .cfg_name = "rtl_bt/rtl8922au_config",
+ .hw_info = "rtl8922au" },
+
+ /* 8852BT/8852BE-VT */
+ { IC_INFO(RTL_ROM_LMP_8852A, 0x87, 0xc, HCI_USB),
+ .config_needed = false,
+ .has_rom_version = true,
+ .has_msft_ext = true,
+ .fw_name = "rtl_bt/rtl8852btu_fw",
+ .cfg_name = "rtl_bt/rtl8852btu_config",
+ .hw_info = "rtl8852btu" },
};
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
- u8 hci_ver, u8 hci_bus)
+ u8 hci_ver, u8 hci_bus,
+ u8 chip_type)
{
int i;
@@ -220,11 +353,15 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
(ic_id_table[i].hci_rev != hci_rev))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
- (ic_id_table[i].hci_ver != hci_ver))
+ (ic_id_table[i].hci_ver != hci_ver) &&
+ (ic_id_table[i].hci_ver != 0))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
(ic_id_table[i].hci_bus != hci_bus))
continue;
+ if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) &&
+ (ic_id_table[i].chip_type != chip_type))
+ continue;
break;
}
@@ -284,11 +421,227 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
return 0;
}
+static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
+ struct rtl_vendor_cmd *cmd, u8 *rp)
+{
+ struct sk_buff *skb;
+ int err = 0;
+
+ skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err);
+ return err;
+ }
+
+ if (skb->len != 3 || skb->data[0]) {
+ bt_dev_err(hdev, "RTL: Read reg16 length mismatch");
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ if (rp)
+ memcpy(rp, skb->data + 1, 2);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
+{
+ void *data = iov->data;
+
+ if (iov->len < len)
+ return NULL;
+
+ iov->data += len;
+ iov->len -= len;
+
+ return data;
+}
+
+static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
+ struct btrtl_device_info *btrtl_dev)
+{
+ struct list_head *pos;
+ struct list_head *next;
+ struct rtl_subsection *subsec;
+
+ list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) {
+ subsec = list_entry(pos, struct rtl_subsection, list);
+ if (subsec->prio >= node->prio)
+ break;
+ }
+ __list_add(&node->list, pos->prev, pos);
+}
+
+static int btrtl_parse_section(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev, u32 opcode,
+ u8 *data, u32 len)
+{
+ struct rtl_section_hdr *hdr;
+ struct rtl_subsection *subsec;
+ struct rtl_common_subsec *common_subsec;
+ struct rtl_sec_hdr *sec_hdr;
+ int i;
+ u8 *ptr;
+ u16 num_subsecs;
+ u32 subsec_len;
+ int rc = 0;
+ struct rtl_iovec iov = {
+ .data = data,
+ .len = len,
+ };
+
+ hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+ if (!hdr)
+ return -EINVAL;
+ num_subsecs = le16_to_cpu(hdr->num);
+
+ for (i = 0; i < num_subsecs; i++) {
+ common_subsec = rtl_iov_pull_data(&iov, sizeof(*common_subsec));
+ if (!common_subsec)
+ break;
+ subsec_len = le32_to_cpu(common_subsec->len);
+
+ rtl_dev_dbg(hdev, "subsec, eco 0x%02x, len %08x",
+ common_subsec->eco, subsec_len);
+
+ ptr = rtl_iov_pull_data(&iov, subsec_len);
+ if (!ptr)
+ break;
+
+ if (common_subsec->eco != btrtl_dev->rom_version + 1)
+ continue;
+
+ switch (opcode) {
+ case RTL_PATCH_SECURITY_HEADER:
+ sec_hdr = (void *)common_subsec;
+ if (sec_hdr->key_id != btrtl_dev->key_id)
+ continue;
+ break;
+ }
+
+ subsec = kzalloc(sizeof(*subsec), GFP_KERNEL);
+ if (!subsec)
+ return -ENOMEM;
+ subsec->opcode = opcode;
+ subsec->prio = common_subsec->prio;
+ subsec->len = subsec_len;
+ subsec->data = ptr;
+ btrtl_insert_ordered_subsec(subsec, btrtl_dev);
+ rc += subsec_len;
+ }
+
+ return rc;
+}
+
+static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
+ struct btrtl_device_info *btrtl_dev,
+ unsigned char **_buf)
+{
+ struct rtl_epatch_header_v2 *hdr;
+ int rc;
+ u8 key_id;
+ u32 num_sections;
+ struct rtl_section *section;
+ struct rtl_subsection *entry, *tmp;
+ u32 section_len;
+ u32 opcode;
+ int len = 0;
+ int i;
+ u8 *ptr;
+ struct rtl_iovec iov = {
+ .data = btrtl_dev->fw_data,
+ .len = btrtl_dev->fw_len - 7, /* Cut the tail */
+ };
+
+ key_id = btrtl_dev->key_id;
+
+ hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+ if (!hdr)
+ return -EINVAL;
+ num_sections = le32_to_cpu(hdr->num_sections);
+
+ rtl_dev_dbg(hdev, "FW version %08x-%08x", *((u32 *)hdr->fw_version),
+ *((u32 *)(hdr->fw_version + 4)));
+
+ for (i = 0; i < num_sections; i++) {
+ section = rtl_iov_pull_data(&iov, sizeof(*section));
+ if (!section)
+ break;
+ section_len = le32_to_cpu(section->len);
+ opcode = le32_to_cpu(section->opcode);
+
+ rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
+
+ ptr = rtl_iov_pull_data(&iov, section_len);
+ if (!ptr)
+ break;
+
+ switch (opcode) {
+ case RTL_PATCH_SNIPPETS:
+ rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
+ ptr, section_len);
+ break;
+ case RTL_PATCH_SECURITY_HEADER:
+ /* If key_id from chip is zero, ignore all security
+ * headers.
+ */
+ if (!key_id)
+ break;
+ rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
+ ptr, section_len);
+ break;
+ case RTL_PATCH_DUMMY_HEADER:
+ rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
+ ptr, section_len);
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ if (rc < 0) {
+ rtl_dev_err(hdev, "RTL: Parse section (%u) err %d",
+ opcode, rc);
+ return rc;
+ }
+ len += rc;
+ }
+
+ if (!len)
+ return -ENODATA;
+
+ /* Allocate mem and copy all found subsecs. */
+ ptr = kvmalloc(len, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ len = 0;
+ list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) {
+ rtl_dev_dbg(hdev, "RTL: opcode %08x, addr %p, len 0x%x",
+ entry->opcode, entry->data, entry->len);
+ memcpy(ptr + len, entry->data, entry->len);
+ len += entry->len;
+ }
+
+ if (!len) {
+ kvfree(ptr);
+ return -EPERM;
+ }
+
+ *_buf = ptr;
+ return len;
+}
+
static int rtlbt_parse_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned char **_buf)
{
static const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
+ struct btrealtek_data *coredump_info = hci_get_priv(hdev);
struct rtl_epatch_header *epatch_info;
unsigned char *buf;
int i, len;
@@ -307,6 +660,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
{ RTL_ROM_LMP_8723B, 1 },
{ RTL_ROM_LMP_8821A, 2 },
{ RTL_ROM_LMP_8761A, 3 },
+ { RTL_ROM_LMP_8703B, 7 },
{ RTL_ROM_LMP_8822B, 8 },
{ RTL_ROM_LMP_8723B, 9 }, /* 8723D */
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */
@@ -315,9 +669,24 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
{ RTL_ROM_LMP_8852A, 18 }, /* 8852A */
{ RTL_ROM_LMP_8852A, 20 }, /* 8852B */
{ RTL_ROM_LMP_8852A, 25 }, /* 8852C */
+ { RTL_ROM_LMP_8851B, 36 }, /* 8851B */
+ { RTL_ROM_LMP_8922A, 44 }, /* 8922A */
+ { RTL_ROM_LMP_8852A, 47 }, /* 8852BT */
+ { RTL_ROM_LMP_8761A, 51 }, /* 8761C */
};
- min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
+ if (btrtl_dev->fw_len <= 8)
+ return -EINVAL;
+
+ if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
+ min_size = sizeof(struct rtl_epatch_header) +
+ sizeof(extension_sig) + 3;
+ else if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8))
+ min_size = sizeof(struct rtl_epatch_header_v2) +
+ sizeof(extension_sig) + 3;
+ else
+ return -EINVAL;
+
if (btrtl_dev->fw_len < min_size)
return -EINVAL;
@@ -329,7 +698,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
/* Loop from the end of the firmware parsing instructions, until
* we find an instruction that identifies the "project ID" for the
- * hardware supported by this firwmare file.
+ * hardware supported by this firmware file.
* Once we have that, we double-check that project_id is suitable
* for the hardware we are working with.
*/
@@ -382,15 +751,19 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
return -EINVAL;
}
- epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
- if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
+ if (memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8) != 0) {
+ if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8))
+ return rtlbt_parse_firmware_v2(hdev, btrtl_dev, _buf);
rtl_dev_err(hdev, "bad EPATCH signature");
return -EINVAL;
}
+ epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
num_patches = le16_to_cpu(epatch_info->num_patches);
+
BT_DBG("fw_version=%x, num_patches=%d",
le32_to_cpu(epatch_info->fw_version), num_patches);
+ coredump_info->rtl_dump.fw_version = le32_to_cpu(epatch_info->fw_version);
/* After the rtl_epatch_header there is a funky patch metadata section.
* Assuming 2 patches, the layout is:
@@ -451,27 +824,27 @@ static int rtl_download_firmware(struct hci_dev *hdev,
int frag_len = RTL_FRAG_LEN;
int ret = 0;
int i;
+ int j = 0;
struct sk_buff *skb;
struct hci_rp_read_local_version *rp;
- dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
+ dl_cmd = kmalloc(sizeof(*dl_cmd), GFP_KERNEL);
if (!dl_cmd)
return -ENOMEM;
for (i = 0; i < frag_num; i++) {
struct sk_buff *skb;
- BT_DBG("download fw (%d/%d)", i, frag_num);
-
- if (i > 0x7f)
- dl_cmd->index = (i & 0x7f) + 1;
- else
- dl_cmd->index = i;
+ dl_cmd->index = j++;
+ if (dl_cmd->index == 0x7f)
+ j = 1;
if (i == (frag_num - 1)) {
dl_cmd->index |= 0x80; /* data end */
frag_len = fw_len % RTL_FRAG_LEN;
}
+ rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i,
+ frag_num, dl_cmd->index);
memcpy(dl_cmd->data, data, frag_len);
/* Send download command */
@@ -522,10 +895,8 @@ static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
if (ret < 0)
return ret;
ret = fw->size;
- *buff = kvmalloc(fw->size, GFP_KERNEL);
- if (*buff)
- memcpy(*buff, fw->data, ret);
- else
+ *buff = kvmemdup(fw->data, fw->size, GFP_KERNEL);
+ if (!*buff)
ret = -ENOMEM;
release_firmware(fw);
@@ -587,10 +958,104 @@ out:
return ret;
}
+static void btrtl_coredump(struct hci_dev *hdev)
+{
+ static const u8 param[] = { 0x00, 0x00 };
+
+ __hci_cmd_send(hdev, RTL_VSC_OP_COREDUMP, sizeof(param), param);
+}
+
+static void btrtl_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btrealtek_data *coredump_info = hci_get_priv(hdev);
+ char buf[80];
+
+ if (coredump_info->rtl_dump.controller)
+ snprintf(buf, sizeof(buf), "Controller Name: %s\n",
+ coredump_info->rtl_dump.controller);
+ else
+ snprintf(buf, sizeof(buf), "Controller Name: Unknown\n");
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
+ coredump_info->rtl_dump.fw_version);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info->rtl_dump.driver_name);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Vendor: Realtek\n");
+ skb_put_data(skb, buf, strlen(buf));
+}
+
+static void btrtl_register_devcoredump_support(struct hci_dev *hdev)
+{
+ hci_devcd_register(hdev, btrtl_coredump, btrtl_dmp_hdr, NULL);
+
+}
+
+void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
+{
+ struct btrealtek_data *coredump_info = hci_get_priv(hdev);
+
+ coredump_info->rtl_dump.driver_name = driver_name;
+}
+EXPORT_SYMBOL_GPL(btrtl_set_driver_name);
+
+static bool rtl_has_chip_type(u16 lmp_subver)
+{
+ switch (lmp_subver) {
+ case RTL_ROM_LMP_8703B:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
+{
+ struct rtl_chip_type_evt *chip_type;
+ struct sk_buff *skb;
+ const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0};
+
+ /* Read RTL chip type command */
+ skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ rtl_dev_err(hdev, "Read chip type failed (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ chip_type = skb_pull_data(skb, sizeof(*chip_type));
+ if (!chip_type) {
+ rtl_dev_err(hdev, "RTL chip type event length mismatch");
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ rtl_dev_info(hdev, "chip_type status=%x type=%x",
+ chip_type->status, chip_type->type);
+
+ *type = chip_type->type & 0x0f;
+
+ kfree_skb(skb);
+ return 0;
+}
+
void btrtl_free(struct btrtl_device_info *btrtl_dev)
{
+ struct rtl_subsection *entry, *tmp;
+
kvfree(btrtl_dev->fw_data);
kvfree(btrtl_dev->cfg_data);
+
+ list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+
kfree(btrtl_dev);
}
EXPORT_SYMBOL_GPL(btrtl_free);
@@ -598,15 +1063,19 @@ EXPORT_SYMBOL_GPL(btrtl_free);
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
const char *postfix)
{
+ struct btrealtek_data *coredump_info = hci_get_priv(hdev);
struct btrtl_device_info *btrtl_dev;
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
+ struct hci_command_hdr *cmd;
+ char fw_name[40];
char cfg_name[40];
u16 hci_rev, lmp_subver;
- u8 hci_ver;
+ u8 hci_ver, lmp_ver, chip_type = 0;
int ret;
- u16 opcode;
- u8 cmd[2];
+ int rc;
+ u8 key_id;
+ u8 reg_val[2];
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
if (!btrtl_dev) {
@@ -614,6 +1083,31 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
goto err_alloc;
}
+ INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
+
+check_version:
+ ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
+ if (ret < 0)
+ goto err_free;
+ lmp_subver = get_unaligned_le16(reg_val);
+
+ if (lmp_subver == RTL_ROM_LMP_8822B) {
+ ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_REV, reg_val);
+ if (ret < 0)
+ goto err_free;
+ hci_rev = get_unaligned_le16(reg_val);
+
+ /* 8822E */
+ if (hci_rev == 0x000e) {
+ hci_ver = 0x0c;
+ lmp_ver = 0x0c;
+ btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev,
+ hci_ver, hdev->bus,
+ chip_type);
+ goto next;
+ }
+ }
+
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
@@ -621,62 +1115,58 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
}
resp = (struct hci_rp_read_local_version *)skb->data;
- rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
- resp->hci_ver, resp->hci_rev,
- resp->lmp_ver, resp->lmp_subver);
- hci_ver = resp->hci_ver;
- hci_rev = le16_to_cpu(resp->hci_rev);
+ hci_ver = resp->hci_ver;
+ hci_rev = le16_to_cpu(resp->hci_rev);
+ lmp_ver = resp->lmp_ver;
lmp_subver = le16_to_cpu(resp->lmp_subver);
+ kfree_skb(skb);
+
+ if (rtl_has_chip_type(lmp_subver)) {
+ ret = rtl_read_chip_type(hdev, &chip_type);
+ if (ret)
+ goto err_free;
+ }
+
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
- hdev->bus);
+ hdev->bus, chip_type);
- if (!btrtl_dev->ic_info)
+next:
+ rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
+ hci_ver, hci_rev,
+ lmp_ver, lmp_subver);
+
+ if (!btrtl_dev->ic_info && !btrtl_dev->drop_fw)
btrtl_dev->drop_fw = true;
+ else
+ btrtl_dev->drop_fw = false;
if (btrtl_dev->drop_fw) {
- opcode = hci_opcode_pack(0x3f, 0x66);
- cmd[0] = opcode & 0xff;
- cmd[1] = opcode >> 8;
-
- skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
+ skb = bt_skb_alloc(sizeof(*cmd), GFP_KERNEL);
if (!skb)
- goto out_free;
+ goto err_free;
+
+ cmd = skb_put(skb, HCI_COMMAND_HDR_SIZE);
+ cmd->opcode = cpu_to_le16(0xfc66);
+ cmd->plen = 0;
- skb_put_data(skb, cmd, sizeof(cmd));
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
- hdev->send(hdev, skb);
+ ret = hdev->send(hdev, skb);
+ if (ret < 0) {
+ bt_dev_err(hdev, "sending frame failed (%d)", ret);
+ kfree_skb(skb);
+ goto err_free;
+ }
/* Ensure the above vendor command is sent to controller and
* process has done.
*/
msleep(200);
- /* Read the local version again. Expect to have the vanilla
- * version as cold boot.
- */
- skb = btrtl_read_local_version(hdev);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- goto err_free;
- }
-
- resp = (struct hci_rp_read_local_version *)skb->data;
- rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
- resp->hci_ver, resp->hci_rev,
- resp->lmp_ver, resp->lmp_subver);
-
- hci_ver = resp->hci_ver;
- hci_rev = le16_to_cpu(resp->hci_rev);
- lmp_subver = le16_to_cpu(resp->lmp_subver);
-
- btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
- hdev->bus);
+ goto check_version;
}
-out_free:
- kfree_skb(skb);
if (!btrtl_dev->ic_info) {
rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
@@ -690,8 +1180,34 @@ out_free:
goto err_free;
}
- btrtl_dev->fw_len = rtl_load_file(hdev, btrtl_dev->ic_info->fw_name,
- &btrtl_dev->fw_data);
+ if (!btrtl_dev->ic_info->fw_name) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
+ if (rc < 0)
+ goto err_free;
+
+ key_id = reg_val[0];
+ btrtl_dev->key_id = key_id;
+ rtl_dev_info(hdev, "%s: key id %u", __func__, key_id);
+
+ btrtl_dev->fw_len = -EIO;
+ if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
+ snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
+ btrtl_dev->ic_info->fw_name);
+ btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
+ &btrtl_dev->fw_data);
+ }
+
+ if (btrtl_dev->fw_len < 0) {
+ snprintf(fw_name, sizeof(fw_name), "%s.bin",
+ btrtl_dev->ic_info->fw_name);
+ btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
+ &btrtl_dev->fw_data);
+ }
+
if (btrtl_dev->fw_len < 0) {
rtl_dev_err(hdev, "firmware file %s not found",
btrtl_dev->ic_info->fw_name);
@@ -699,7 +1215,7 @@ out_free:
goto err_free;
}
- if (btrtl_dev->ic_info->cfg_name) {
+ if (btrtl_dev->ic_info->cfg_name && !btrtl_dev->key_id) {
if (postfix) {
snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
btrtl_dev->ic_info->cfg_name, postfix);
@@ -714,6 +1230,8 @@ out_free:
rtl_dev_err(hdev, "mandatory config file %s not found",
btrtl_dev->ic_info->cfg_name);
ret = btrtl_dev->cfg_len;
+ if (!ret)
+ ret = -EINVAL;
goto err_free;
}
}
@@ -724,6 +1242,9 @@ out_free:
if (btrtl_dev->ic_info->has_msft_ext)
hci_set_msft_opcode(hdev, 0xFCF0);
+ if (btrtl_dev->ic_info)
+ coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
+
return btrtl_dev;
err_free:
@@ -736,6 +1257,8 @@ EXPORT_SYMBOL_GPL(btrtl_initialize);
int btrtl_download_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev)
{
+ int err = 0;
+
/* Match a set of subver values that correspond to stock firmware,
* which is not compatible with standard btusb.
* If matched, upload an alternative firmware that does conform to
@@ -744,22 +1267,33 @@ int btrtl_download_firmware(struct hci_dev *hdev,
*/
if (!btrtl_dev->ic_info) {
rtl_dev_info(hdev, "assuming no firmware upload needed");
- return 0;
+ err = 0;
+ goto done;
}
switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8723A:
- return btrtl_setup_rtl8723a(hdev, btrtl_dev);
+ err = btrtl_setup_rtl8723a(hdev, btrtl_dev);
+ break;
case RTL_ROM_LMP_8723B:
case RTL_ROM_LMP_8821A:
case RTL_ROM_LMP_8761A:
case RTL_ROM_LMP_8822B:
case RTL_ROM_LMP_8852A:
- return btrtl_setup_rtl8723b(hdev, btrtl_dev);
+ case RTL_ROM_LMP_8703B:
+ case RTL_ROM_LMP_8851B:
+ case RTL_ROM_LMP_8922A:
+ err = btrtl_setup_rtl8723b(hdev, btrtl_dev);
+ break;
default:
rtl_dev_info(hdev, "assuming no firmware upload needed");
- return 0;
+ break;
}
+
+done:
+ btrtl_register_devcoredump_support(hdev);
+
+ return err;
}
EXPORT_SYMBOL_GPL(btrtl_download_firmware);
@@ -768,7 +1302,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
/* Enable controller to do both LE scan and BR/EDR inquiry
* simultaneously.
*/
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
/* Enable central-peripheral role (able to create new connections with
* an existing connection in slave role).
@@ -779,8 +1313,24 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
case CHIP_ID_8852A:
case CHIP_ID_8852B:
case CHIP_ID_8852C:
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+ case CHIP_ID_8851B:
+ case CHIP_ID_8922A:
+ case CHIP_ID_8852BT:
+ case CHIP_ID_8761C:
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
+
+ /* RTL8852C needs to transmit mSBC data continuously without
+ * the zero length of USB packets for the ALT 6 supported chips
+ */
+ if (btrtl_dev->project_id == CHIP_ID_8852C)
+ btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP);
+
+ if (btrtl_dev->project_id == CHIP_ID_8852A ||
+ btrtl_dev->project_id == CHIP_ID_8852B ||
+ btrtl_dev->project_id == CHIP_ID_8852C)
+ hci_set_quirk(hdev,
+ HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER);
+
hci_set_aosp_capable(hdev);
break;
default:
@@ -788,6 +1338,21 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
rtl_dev_dbg(hdev, "WBS supported not enabled.");
break;
}
+
+ if (!btrtl_dev->ic_info)
+ return;
+
+ switch (btrtl_dev->ic_info->lmp_subver) {
+ case RTL_ROM_LMP_8703B:
+ /* 8723CS reports two pages for local ext features,
+ * but it doesn't support any features from page 2 -
+ * it either responds with garbage or with error status
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2);
+ break;
+ default:
+ break;
+ }
}
EXPORT_SYMBOL_GPL(btrtl_set_quirks);
@@ -804,6 +1369,15 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
btrtl_set_quirks(hdev, btrtl_dev);
+ if (btrtl_dev->ic_info) {
+ hci_set_hw_info(hdev,
+ "RTL lmp_subver=%u hci_rev=%u hci_ver=%u hci_bus=%u",
+ btrtl_dev->ic_info->lmp_subver,
+ btrtl_dev->ic_info->hci_rev,
+ btrtl_dev->ic_info->hci_ver,
+ btrtl_dev->ic_info->hci_bus);
+ }
+
btrtl_free(btrtl_dev);
return ret;
}
@@ -817,7 +1391,7 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
/* According to the vendor driver, BT must be reset on close to avoid
* firmware crash.
*/
- skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
bt_dev_err(hdev, "HCI reset during shutdown failed");
@@ -946,17 +1520,48 @@ MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723d_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723d_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761b_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761b_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761bu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761bu_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761cu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761cu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8821c_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8821c_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8821cs_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8821cs_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8822cs_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8822cs_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8822cu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8822cu_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8852btu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8852btu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw_v2.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8922au_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8922au_config.bin");
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index 2c441bda390a..a2d9d34f9fb0 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -14,6 +14,11 @@
struct btrtl_device_info;
+struct rtl_chip_type_evt {
+ __u8 status;
+ __u8 type;
+} __packed;
+
struct rtl_download_cmd {
__u8 index;
__u8 data[RTL_FRAG_LEN];
@@ -44,9 +49,89 @@ struct rtl_vendor_config_entry {
struct rtl_vendor_config {
__le32 signature;
__le16 total_len;
- struct rtl_vendor_config_entry entry[];
+ __u8 entry[];
+} __packed;
+
+struct rtl_epatch_header_v2 {
+ __u8 signature[8];
+ __u8 fw_version[8];
+ __le32 num_sections;
+} __packed;
+
+struct rtl_section {
+ __le32 opcode;
+ __le32 len;
+ u8 data[];
+} __packed;
+
+struct rtl_section_hdr {
+ __le16 num;
+ __le16 reserved;
+} __packed;
+
+struct rtl_common_subsec {
+ __u8 eco;
+ __u8 prio;
+ __u8 cb[2];
+ __le32 len;
+ __u8 data[];
+};
+
+struct rtl_sec_hdr {
+ __u8 eco;
+ __u8 prio;
+ __u8 key_id;
+ __u8 reserved;
+ __le32 len;
+ __u8 data[];
+} __packed;
+
+struct rtl_subsection {
+ struct list_head list;
+ u32 opcode;
+ u32 len;
+ u8 prio;
+ u8 *data;
+};
+
+struct rtl_iovec {
+ u8 *data;
+ u32 len;
+};
+
+struct rtl_vendor_cmd {
+ __u8 param[5];
} __packed;
+enum {
+ REALTEK_ALT6_CONTINUOUS_TX_CHIP,
+
+ __REALTEK_NUM_FLAGS,
+};
+
+struct rtl_dump_info {
+ const char *driver_name;
+ char *controller;
+ u32 fw_version;
+};
+
+struct btrealtek_data {
+ DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS);
+
+ struct rtl_dump_info rtl_dump;
+};
+
+#define btrealtek_set_flag(hdev, nr) \
+ do { \
+ struct btrealtek_data *realtek = hci_get_priv((hdev)); \
+ set_bit((nr), realtek->flags); \
+ } while (0)
+
+#define btrealtek_get_flag(hdev) \
+ (((struct btrealtek_data *)hci_get_priv(hdev))->flags)
+
+#define btrealtek_test_flag(hdev, nr) test_bit((nr), btrealtek_get_flag(hdev))
+
#if IS_ENABLED(CONFIG_BT_RTL)
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
@@ -62,6 +147,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned int *controller_baudrate,
u32 *device_baudrate, bool *flow_control);
+void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
#else
@@ -105,4 +191,8 @@ static inline int btrtl_get_uart_settings(struct hci_dev *hdev,
return -ENOENT;
}
+static inline void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
+{
+}
+
#endif
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index 795be33f2892..8325655ce6aa 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -32,9 +32,6 @@ static const struct sdio_device_id btsdio_table[] = {
/* Generic Bluetooth Type-B SDIO device */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) },
- /* Generic Bluetooth AMP controller */
- { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) },
-
{ } /* Terminating entry */
};
@@ -298,6 +295,7 @@ static int btsdio_probe(struct sdio_func *func,
case SDIO_DEVICE_ID_BROADCOM_4345:
case SDIO_DEVICE_ID_BROADCOM_43455:
case SDIO_DEVICE_ID_BROADCOM_4356:
+ case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
return -ENODEV;
}
}
@@ -319,11 +317,6 @@ static int btsdio_probe(struct sdio_func *func,
hdev->bus = HCI_SDIO;
hci_set_drvdata(hdev, data);
- if (id->class == SDIO_CLASS_BT_AMP)
- hdev->dev_type = HCI_AMP;
- else
- hdev->dev_type = HCI_PRIMARY;
-
data->hdev = hdev;
SET_HCIDEV_DEV(hdev, &func->dev);
@@ -334,7 +327,7 @@ static int btsdio_probe(struct sdio_func *func,
hdev->send = btsdio_send_frame;
if (func->vendor == 0x0104 && func->device == 0x00c5)
- set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
err = hci_register_dev(hdev);
if (err < 0) {
@@ -357,6 +350,7 @@ static void btsdio_remove(struct sdio_func *func)
if (!data)
return;
+ cancel_work_sync(&data->work);
hdev = data->hdev;
sdio_set_drvdata(func, NULL);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 15caa6469538..8ed3883ab8ee 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -17,10 +17,11 @@
#include <linux/suspend.h>
#include <linux/gpio/consumer.h>
#include <linux/debugfs.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_drv.h>
#include "btintel.h"
#include "btbcm.h"
@@ -32,6 +33,7 @@
static bool disable_scofix;
static bool force_scofix;
static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
+static bool enable_poll_sync = IS_ENABLED(CONFIG_BT_HCIBTUSB_POLL_SYNC);
static bool reset = true;
static struct usb_driver btusb_driver;
@@ -58,11 +60,13 @@ static struct usb_driver btusb_driver;
#define BTUSB_CW6622 BIT(19)
#define BTUSB_MEDIATEK BIT(20)
#define BTUSB_WIDEBAND_SPEECH BIT(21)
-#define BTUSB_VALID_LE_STATES BIT(22)
+#define BTUSB_INVALID_LE_STATES BIT(22)
#define BTUSB_QCA_WCN6855 BIT(23)
#define BTUSB_INTEL_BROKEN_SHUTDOWN_LED BIT(24)
#define BTUSB_INTEL_BROKEN_INITIAL_NCMD BIT(25)
#define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26)
+#define BTUSB_ACTIONS_SEMI BIT(27)
+#define BTUSB_BARROT BIT(28)
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -173,7 +177,7 @@ static const struct usb_device_id btusb_table[] = {
MODULE_DEVICE_TABLE(usb, btusb_table);
-static const struct usb_device_id blacklist_table[] = {
+static const struct usb_device_id quirks_table[] = {
/* CSR BlueCore devices */
{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
@@ -295,32 +299,118 @@ static const struct usb_device_id blacklist_table[] = {
BTUSB_WIDEBAND_SPEECH },
/* QCA WCN6855 chipset */
- { USB_DEVICE(0x0cf3, 0xe600), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ { USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0c9), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0ca), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0cb), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe0cc), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0ce), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0d0), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe0d6), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0de), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0df), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0e1), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe0e3), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0ea), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0ec), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3022), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3023), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3024), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3a22), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3a24), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3a26), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3a27), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0cf3, 0xe600), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9108), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9109), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9208), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9209), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9308), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x10ab, 0x9309), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9408), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x10ab, 0x9409), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
- { USB_DEVICE(0x0489, 0xe0d0), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9508), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9509), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9608), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9609), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x10ab, 0x9f09), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x28de, 0x1401), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
/* QCA WCN785x chipset */
{ USB_DEVICE(0x0cf3, 0xe700), .driver_info = BTUSB_QCA_WCN6855 |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0fc), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0f3), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe100), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe103), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe10a), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe10d), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe11b), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe11c), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe11f), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe141), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe14a), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe14b), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe14d), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3623), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3624), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2c7c, 0x0130), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2c7c, 0x0131), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2c7c, 0x0132), .driver_info = BTUSB_QCA_WCN6855 |
+ BTUSB_WIDEBAND_SPEECH },
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
@@ -390,6 +480,10 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0033), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0035), .driver_info = BTUSB_INTEL_COMBINED },
+ { USB_DEVICE(0x8087, 0x0036), .driver_info = BTUSB_INTEL_COMBINED },
+ { USB_DEVICE(0x8087, 0x0037), .driver_info = BTUSB_INTEL_COMBINED },
+ { USB_DEVICE(0x8087, 0x0038), .driver_info = BTUSB_INTEL_COMBINED },
+ { USB_DEVICE(0x8087, 0x0039), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED |
BTUSB_INTEL_NO_WBS_SUPPORT |
@@ -407,12 +501,33 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_IGNORE },
+ /* Realtek 8821CE Bluetooth devices */
+ { USB_DEVICE(0x13d3, 0x3529), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3533), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
/* Realtek 8822CE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0bda, 0xc822), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ /* Realtek 8822CU Bluetooth devices */
+ { USB_DEVICE(0x13d3, 0x3549), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* Realtek 8851BE Bluetooth devices */
+ { USB_DEVICE(0x0bda, 0xb850), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3600), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3601), .driver_info = BTUSB_REALTEK },
+
+ /* Realtek 8851BU Bluetooth devices */
+ { USB_DEVICE(0x3625, 0x010b), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2001, 0x332a), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
/* Realtek 8852AE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
@@ -426,6 +541,8 @@ static const struct usb_device_id blacklist_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x4006), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0cb8, 0xc549), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Realtek 8852CE Bluetooth devices */
{ USB_DEVICE(0x04ca, 0x4007), .driver_info = BTUSB_REALTEK |
@@ -438,6 +555,54 @@ static const struct usb_device_id blacklist_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3586), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3592), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe122), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* Realtek 8852BE Bluetooth devices */
+ { USB_DEVICE(0x0cb8, 0xc559), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0bda, 0x4853), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0bda, 0x887b), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0bda, 0xb85b), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3570), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3571), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3572), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3591), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3618), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe123), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe125), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* Realtek 8852BT/8852BE-VT Bluetooth devices */
+ { USB_DEVICE(0x0bda, 0x8520), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe12f), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3618), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3619), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* Realtek 8922AE Bluetooth devices */
+ { USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3617), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3616), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe130), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Realtek Bluetooth devices */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
@@ -446,55 +611,152 @@ static const struct usb_device_id blacklist_table[] = {
/* MediaTek Bluetooth devices */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
/* Additional MediaTek MT7615E Bluetooth devices */
{ USB_DEVICE(0x13d3, 0x3560), .driver_info = BTUSB_MEDIATEK},
/* Additional MediaTek MT7663 Bluetooth devices */
{ USB_DEVICE(0x043e, 0x310c), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3801), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional MediaTek MT7668 Bluetooth devices */
{ USB_DEVICE(0x043e, 0x3109), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* Additional MediaTek MT7920 Bluetooth devices */
+ { USB_DEVICE(0x0489, 0xe134), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe135), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3620), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3621), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3622), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional MediaTek MT7921 Bluetooth devices */
{ USB_DEVICE(0x0489, 0xe0c8), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0e0), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x3802), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0e8d, 0x0608), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3563), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3564), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3567), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
- { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3576), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3578), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3583), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3606), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* MediaTek MT7922 Bluetooth devices */
+ { USB_DEVICE(0x13d3, 0x3585), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3610), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
/* MediaTek MT7922A Bluetooth devices */
{ USB_DEVICE(0x0489, 0xe0d8), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe0d9), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
- { USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe0e2), .driver_info = BTUSB_MEDIATEK |
- BTUSB_WIDEBAND_SPEECH |
- BTUSB_VALID_LE_STATES },
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0e4), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0f1), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0f5), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe0f6), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe102), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe152), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe153), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe170), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x3804), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3584), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3605), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3607), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3614), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3615), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3633), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x35f5, 0x7922), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+
+ /* Additional MediaTek MT7925 Bluetooth devices */
+ { USB_DEVICE(0x0489, 0xe111), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe113), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe118), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe11e), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe124), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe139), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe14e), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe14f), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe150), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe151), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3602), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3603), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3604), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3608), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3627), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3628), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3630), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2c7c, 0x7009), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8723AE Bluetooth devices */
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
@@ -516,19 +778,23 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0bda, 0xb009), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK },
- /* Additional Realtek 8761B Bluetooth devices */
+ /* Additional Realtek 8761BUV Bluetooth devices */
{ USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
-
- /* Additional Realtek 8761BU Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x2550, 0x8761), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
-
- /* Additional Realtek 8761BUV Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x8771), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x6655, 0x8771), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x7392, 0xc611), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2b89, 0x8761), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
@@ -565,6 +831,13 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0cb5, 0xc547), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ /* Barrot Technology Bluetooth devices */
+ { USB_DEVICE(0x33fa, 0x0010), .driver_info = BTUSB_BARROT },
+ { USB_DEVICE(0x33fa, 0x0012), .driver_info = BTUSB_BARROT },
+
+ /* Actions Semiconductor ATS2851 based devices */
+ { USB_DEVICE(0x10d7, 0xb012), .driver_info = BTUSB_ACTIONS_SEMI },
+
/* Silicon Wave based devices */
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
@@ -600,6 +873,16 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {
{}
};
+struct qca_dump_info {
+ /* fields for dump collection */
+ u16 id_vendor;
+ u16 id_product;
+ u32 fw_version;
+ u32 controller_id;
+ u32 ram_dump_size;
+ u16 ram_dump_seqno;
+};
+
#define BTUSB_MAX_ISOC_FRAMES 10
#define BTUSB_INTR_RUNNING 0
@@ -618,6 +901,8 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {
#define BTUSB_TX_WAIT_VND_EVT 13
#define BTUSB_WAKEUP_AUTOSUSPEND 14
#define BTUSB_USE_ALT3_FOR_WBS 15
+#define BTUSB_ALT6_CONTINUOUS_TX 16
+#define BTUSB_HW_SSR_ACTIVE 17
struct btusb_data {
struct hci_dev *hdev;
@@ -678,20 +963,55 @@ struct btusb_data {
int (*setup_on_usb)(struct hci_dev *hdev);
+ int (*suspend)(struct hci_dev *hdev);
+ int (*resume)(struct hci_dev *hdev);
+ int (*disconnect)(struct hci_dev *hdev);
+
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
- unsigned cmd_timeout_cnt;
+
+ struct qca_dump_info qca_dump;
};
-static void btusb_intel_cmd_timeout(struct hci_dev *hdev)
+static void btusb_reset(struct hci_dev *hdev)
+{
+ struct btusb_data *data;
+ int err;
+
+ data = hci_get_drvdata(hdev);
+ /* This is not an unbalanced PM reference since the device will reset */
+ err = usb_autopm_get_interface(data->intf);
+ if (err) {
+ bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err);
+ return;
+ }
+
+ bt_dev_err(hdev, "Resetting usb device.");
+ usb_queue_reset_device(data->intf);
+}
+
+static void btusb_intel_reset(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct gpio_desc *reset_gpio = data->reset_gpio;
+ struct btintel_data *intel_data = hci_get_priv(hdev);
- if (++data->cmd_timeout_cnt < 5)
- return;
+ if (intel_data->acpi_reset_method) {
+ if (test_and_set_bit(INTEL_ACPI_RESET_ACTIVE, intel_data->flags)) {
+ bt_dev_err(hdev, "acpi: last reset failed ? Not resetting again");
+ return;
+ }
+
+ bt_dev_err(hdev, "Initiating acpi reset method");
+ /* If ACPI reset method fails, lets try with legacy GPIO
+ * toggling
+ */
+ if (!intel_data->acpi_reset_method(hdev)) {
+ return;
+ }
+ }
if (!reset_gpio) {
- bt_dev_err(hdev, "No way to reset. Ignoring and continuing");
+ btusb_reset(hdev);
return;
}
@@ -713,16 +1033,52 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev)
gpiod_set_value_cansleep(reset_gpio, 0);
}
-static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
+#define RTK_DEVCOREDUMP_CODE_MEMDUMP 0x01
+#define RTK_DEVCOREDUMP_CODE_HW_ERR 0x02
+#define RTK_DEVCOREDUMP_CODE_CMD_TIMEOUT 0x03
+
+#define RTK_SUB_EVENT_CODE_COREDUMP 0x34
+
+struct rtk_dev_coredump_hdr {
+ u8 type;
+ u8 code;
+ u8 reserved[2];
+} __packed;
+
+static inline void btusb_rtl_alloc_devcoredump(struct hci_dev *hdev,
+ struct rtk_dev_coredump_hdr *hdr, u8 *buf, u32 len)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(len + sizeof(*hdr), GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ skb_put_data(skb, hdr, sizeof(*hdr));
+ if (len)
+ skb_put_data(skb, buf, len);
+
+ if (!hci_devcd_init(hdev, skb->len)) {
+ hci_devcd_append(hdev, skb);
+ hci_devcd_complete(hdev);
+ } else {
+ bt_dev_err(hdev, "RTL: Failed to generate devcoredump");
+ kfree_skb(skb);
+ }
+}
+
+static void btusb_rtl_reset(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct gpio_desc *reset_gpio = data->reset_gpio;
+ struct rtk_dev_coredump_hdr hdr = {
+ .type = RTK_DEVCOREDUMP_CODE_CMD_TIMEOUT,
+ };
- if (++data->cmd_timeout_cnt < 5)
- return;
+ btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
if (!reset_gpio) {
- bt_dev_err(hdev, "No gpio to reset Realtek device, ignoring");
+ btusb_reset(hdev);
return;
}
@@ -743,14 +1099,27 @@ static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
gpiod_set_value_cansleep(reset_gpio, 0);
}
-static void btusb_qca_cmd_timeout(struct hci_dev *hdev)
+static void btusb_rtl_hw_error(struct hci_dev *hdev, u8 code)
+{
+ struct rtk_dev_coredump_hdr hdr = {
+ .type = RTK_DEVCOREDUMP_CODE_HW_ERR,
+ .code = code,
+ };
+
+ bt_dev_err(hdev, "RTL: hw err, trigger devcoredump (%d)", code);
+
+ btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
+}
+
+static void btusb_qca_reset(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct gpio_desc *reset_gpio = data->reset_gpio;
- int err;
- if (++data->cmd_timeout_cnt < 5)
+ if (test_bit(BTUSB_HW_SSR_ACTIVE, &data->flags)) {
+ bt_dev_info(hdev, "Ramdump in progress, defer reset");
return;
+ }
if (reset_gpio) {
bt_dev_err(hdev, "Reset qca device via bt_en gpio");
@@ -773,13 +1142,25 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev)
return;
}
- bt_dev_err(hdev, "Multiple cmd timeouts seen. Resetting usb device.");
- /* This is not an unbalanced PM reference since the device will reset */
- err = usb_autopm_get_interface(data->intf);
- if (!err)
- usb_queue_reset_device(data->intf);
- else
- bt_dev_err(hdev, "Failed usb_autopm_get_interface with %d", err);
+ btusb_reset(hdev);
+}
+
+static u8 btusb_classify_qca_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ /* Some Qualcomm controllers, e.g., QCNFA765 with WCN6855 chip, send debug
+ * packets as ACL frames with connection handle 0x2EDC. These are not real
+ * ACL packets and should be reclassified as HCI_DIAG_PKT to prevent
+ * "ACL packet for unknown connection handle 3804" errors.
+ */
+ if (skb->len >= 2) {
+ u16 handle = get_unaligned_le16(skb->data);
+
+ if (handle == 0x2EDC)
+ return HCI_DIAG_PKT;
+ }
+
+ /* Use default packet type for other packets */
+ return hci_skb_pkt_type(skb);
}
static inline void btusb_free_frags(struct btusb_data *data)
@@ -788,13 +1169,13 @@ static inline void btusb_free_frags(struct btusb_data *data)
spin_lock_irqsave(&data->rxlock, flags);
- kfree_skb(data->evt_skb);
+ dev_kfree_skb_irq(data->evt_skb);
data->evt_skb = NULL;
- kfree_skb(data->acl_skb);
+ dev_kfree_skb_irq(data->acl_skb);
data->acl_skb = NULL;
- kfree_skb(data->sco_skb);
+ dev_kfree_skb_irq(data->sco_skb);
data->sco_skb = NULL;
spin_unlock_irqrestore(&data->rxlock, flags);
@@ -803,7 +1184,7 @@ static inline void btusb_free_frags(struct btusb_data *data)
static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
{
if (data->intr_interval) {
- /* Trigger dequeue immediatelly if an event is received */
+ /* Trigger dequeue immediately if an event is received */
schedule_delayed_work(&data->rx_work, 0);
}
@@ -854,6 +1235,18 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
}
if (!hci_skb_expect(skb)) {
+ /* Each chunk should correspond to at least 1 or more
+ * events so if there are still bytes left that doesn't
+ * constitute a new event this is likely a bug in the
+ * controller.
+ */
+ if (count && count < HCI_EVENT_HDR_SIZE) {
+ bt_dev_warn(data->hdev,
+ "Unexpected continuation: %d bytes",
+ count);
+ count = 0;
+ }
+
/* Complete frame */
btusb_recv_event(data, skb);
skb = NULL;
@@ -911,21 +1304,11 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
hci_skb_expect(skb) -= len;
if (skb->len == HCI_ACL_HDR_SIZE) {
- __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
__le16 dlen = hci_acl_hdr(skb)->dlen;
- __u8 type;
/* Complete ACL header */
hci_skb_expect(skb) = __le16_to_cpu(dlen);
- /* Detect if ISO packet has been sent over bulk */
- if (hci_conn_num(data->hdev, ISO_LINK)) {
- type = hci_conn_lookup_type(data->hdev,
- hci_handle(handle));
- if (type == ISO_LINK)
- hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
- }
-
if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -948,6 +1331,34 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
return err;
}
+static bool btusb_validate_sco_handle(struct hci_dev *hdev,
+ struct hci_sco_hdr *hdr)
+{
+ __u16 handle;
+
+ if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
+ // Can't validate, userspace controls everything.
+ return true;
+
+ /*
+ * USB isochronous transfers are not designed to be reliable and may
+ * lose fragments. When this happens, the next first fragment
+ * encountered might actually be a continuation fragment.
+ * Validate the handle to detect it and drop it, or else the upper
+ * layer will get garbage for a while.
+ */
+
+ handle = hci_handle(__le16_to_cpu(hdr->handle));
+
+ switch (hci_conn_lookup_type(hdev, handle)) {
+ case SCO_LINK:
+ case ESCO_LINK:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
{
struct sk_buff *skb;
@@ -980,9 +1391,12 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
if (skb->len == HCI_SCO_HDR_SIZE) {
/* Complete SCO header */
- hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen;
+ struct hci_sco_hdr *hdr = hci_sco_hdr(skb);
- if (skb_tailroom(skb) < hci_skb_expect(skb)) {
+ hci_skb_expect(skb) = hdr->dlen;
+
+ if (skb_tailroom(skb) < hci_skb_expect(skb) ||
+ !btusb_validate_sco_handle(data->hdev, hdr)) {
kfree_skb(skb);
skb = NULL;
@@ -1066,7 +1480,15 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
if (!urb)
return -ENOMEM;
- size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
+ if (le16_to_cpu(data->udev->descriptor.idVendor) == 0x0a12 &&
+ le16_to_cpu(data->udev->descriptor.idProduct) == 0x0001)
+ /* Fake CSR devices don't seem to support sort-transter */
+ size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
+ else
+ /* Use maximum HCI Event size so the USB stack handles
+ * ZPL/short-transfer automatically.
+ */
+ size = HCI_MAX_EVENT_SIZE;
buf = kmalloc(size, mem_flags);
if (!buf) {
@@ -1262,11 +1684,17 @@ static void btusb_isoc_complete(struct urb *urb)
static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len,
int mtu, struct btusb_data *data)
{
- int i, offset = 0;
+ int i = 0, offset = 0;
unsigned int interval;
BT_DBG("len %d mtu %d", len, mtu);
+ /* For mSBC ALT 6 settings some chips need to transmit the data
+ * continuously without the zero length of USB packets.
+ */
+ if (test_bit(BTUSB_ALT6_CONTINUOUS_TX, &data->flags))
+ goto ignore_usb_alt6_packet_flow;
+
/* For mSBC ALT 6 setting the host will send the packet at continuous
* flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting
* 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets.
@@ -1286,6 +1714,7 @@ static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len,
urb->iso_frame_desc[i].length = offset;
}
+ignore_usb_alt6_packet_flow:
if (len && i < BTUSB_MAX_ISOC_FRAMES) {
urb->iso_frame_desc[i].offset = offset;
urb->iso_frame_desc[i].length = len;
@@ -1781,7 +2210,8 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return submit_or_queue_tx_urb(hdev, urb);
case HCI_SCODATA_PKT:
- if (hci_conn_num(hdev, SCO_LINK) < 1)
+ if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+ hci_conn_num(hdev, SCO_LINK) < 1)
return -ENODEV;
urb = alloc_isoc_urb(hdev, skb);
@@ -1879,7 +2309,7 @@ static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts)
* alternate setting.
*/
spin_lock_irqsave(&data->rxlock, flags);
- kfree_skb(data->sco_skb);
+ dev_kfree_skb_irq(data->sco_skb);
data->sco_skb = NULL;
spin_unlock_irqrestore(&data->rxlock, flags);
@@ -1967,10 +2397,11 @@ static void btusb_work(struct work_struct *work)
if (btusb_switch_alt_setting(hdev, new_alts) < 0)
bt_dev_err(hdev, "set USB alt:(%d) failed!", new_alts);
} else {
- clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor);
- __set_isoc_interface(hdev, 0);
+ if (test_and_clear_bit(BTUSB_ISOC_RUNNING, &data->flags))
+ __set_isoc_interface(hdev, 0);
+
if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
}
@@ -2034,13 +2465,19 @@ static int btusb_setup_csr(struct hci_dev *hdev)
return err;
}
- if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+ rp = skb_pull_data(skb, sizeof(*rp));
+ if (!rp) {
bt_dev_err(hdev, "CSR: Local version length mismatch");
kfree_skb(skb);
return -EIO;
}
- rp = (struct hci_rp_read_local_version *)skb->data;
+ bt_dev_info(hdev, "CSR: Setting up dongle with HCI ver=%u rev=%04x",
+ rp->hci_ver, le16_to_cpu(rp->hci_rev));
+
+ bt_dev_info(hdev, "LMP ver=%u subver=%04x; manufacturer=%u",
+ rp->lmp_ver, le16_to_cpu(rp->lmp_subver),
+ le16_to_cpu(rp->manufacturer));
/* Detect a wide host of Chinese controllers that aren't CSR.
*
@@ -2070,29 +2507,29 @@ static int btusb_setup_csr(struct hci_dev *hdev)
* third-party BT 4.0 dongle reuses it.
*/
else if (le16_to_cpu(rp->lmp_subver) <= 0x034e &&
- le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_1_1)
+ rp->hci_ver > BLUETOOTH_VER_1_1)
is_fake = true;
else if (le16_to_cpu(rp->lmp_subver) <= 0x0529 &&
- le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_1_2)
+ rp->hci_ver > BLUETOOTH_VER_1_2)
is_fake = true;
else if (le16_to_cpu(rp->lmp_subver) <= 0x0c5c &&
- le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_2_0)
+ rp->hci_ver > BLUETOOTH_VER_2_0)
is_fake = true;
else if (le16_to_cpu(rp->lmp_subver) <= 0x1899 &&
- le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_2_1)
+ rp->hci_ver > BLUETOOTH_VER_2_1)
is_fake = true;
else if (le16_to_cpu(rp->lmp_subver) <= 0x22bb &&
- le16_to_cpu(rp->hci_ver) > BLUETOOTH_VER_4_0)
+ rp->hci_ver > BLUETOOTH_VER_4_0)
is_fake = true;
/* Other clones which beat all the above checks */
else if (bcdDevice == 0x0134 &&
le16_to_cpu(rp->lmp_subver) == 0x0c5c &&
- le16_to_cpu(rp->hci_ver) == BLUETOOTH_VER_2_0)
+ rp->hci_ver == BLUETOOTH_VER_2_0)
is_fake = true;
if (is_fake) {
@@ -2103,15 +2540,18 @@ static int btusb_setup_csr(struct hci_dev *hdev)
* Probably will need to be expanded in the future;
* without these the controller will lock up.
*/
- set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
- set_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks);
- set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ERR_DATA_REPORTING);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL);
+ hci_set_quirk(hdev, HCI_QUIRK_NO_SUSPEND_NOTIFIER);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_VOICE_SETTING);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_PAGE_SCAN_TYPE);
/* Clear the reset quirk since this is not an actual
* early Bluetooth 1.1 device from CSR.
*/
- clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
- clear_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+ hci_clear_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
+ hci_clear_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
/*
* Special workaround for these BT 4.0 chip clones, and potentially more:
@@ -2201,39 +2641,6 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
return btusb_recv_bulk(data, buffer, count);
}
-static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
-{
- if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
- struct hci_event_hdr *hdr = (void *)skb->data;
-
- if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
- hdr->plen > 0) {
- const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
- unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
-
- switch (skb->data[2]) {
- case 0x02:
- /* When switching to the operational firmware
- * the device sends a vendor specific event
- * indicating that the bootup completed.
- */
- btintel_bootup(hdev, ptr, len);
- break;
- case 0x06:
- /* When the firmware loading completes the
- * device sends out a vendor specific event
- * indicating the result of the firmware
- * loading.
- */
- btintel_secure_send_result(hdev, ptr, len);
- break;
- }
- }
- }
-
- return hci_recv_frame(hdev, skb);
-}
-
static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
{
struct urb *urb;
@@ -2255,12 +2662,12 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
else
urb = alloc_ctrl_urb(hdev, skb);
- /* When the 0xfc01 command is issued to boot into
- * the operational firmware, it will actually not
- * send a command complete event. To keep the flow
+ /* When the BTINTEL_HCI_OP_RESET command is issued to
+ * boot into the operational firmware, it will actually
+ * not send a command complete event. To keep the flow
* control working inject that event here.
*/
- if (opcode == 0xfc01)
+ if (opcode == BTINTEL_HCI_OP_RESET)
inject_cmd_complete(hdev, opcode);
} else {
urb = alloc_ctrl_urb(hdev, skb);
@@ -2280,7 +2687,8 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
return submit_or_queue_tx_urb(hdev, urb);
case HCI_SCODATA_PKT:
- if (hci_conn_num(hdev, SCO_LINK) < 1)
+ if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+ hci_conn_num(hdev, SCO_LINK) < 1)
return -ENODEV;
urb = alloc_isoc_urb(hdev, skb);
@@ -2301,633 +2709,208 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
return -EILSEQ;
}
-/* UHW CR mapping */
-#define MTK_BT_MISC 0x70002510
-#define MTK_BT_SUBSYS_RST 0x70002610
-#define MTK_UDMA_INT_STA_BT 0x74000024
-#define MTK_UDMA_INT_STA_BT1 0x74000308
-#define MTK_BT_WDT_STATUS 0x740003A0
-#define MTK_EP_RST_OPT 0x74011890
-#define MTK_EP_RST_IN_OUT_OPT 0x00010001
-#define MTK_BT_RST_DONE 0x00000100
-#define MTK_BT_RESET_WAIT_MS 100
-#define MTK_BT_RESET_NUM_TRIES 10
-
-static void btusb_mtk_wmt_recv(struct urb *urb)
+static int btusb_setup_realtek(struct hci_dev *hdev)
{
- struct hci_dev *hdev = urb->context;
struct btusb_data *data = hci_get_drvdata(hdev);
- struct sk_buff *skb;
- int err;
-
- if (urb->status == 0 && urb->actual_length > 0) {
- hdev->stat.byte_rx += urb->actual_length;
-
- /* WMT event shouldn't be fragmented and the size should be
- * less than HCI_WMT_MAX_EVENT_SIZE.
- */
- skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC);
- if (!skb) {
- hdev->stat.err_rx++;
- kfree(urb->setup_packet);
- return;
- }
-
- hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
- skb_put_data(skb, urb->transfer_buffer, urb->actual_length);
-
- /* When someone waits for the WMT event, the skb is being cloned
- * and being processed the events from there then.
- */
- if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) {
- data->evt_skb = skb_clone(skb, GFP_ATOMIC);
- if (!data->evt_skb) {
- kfree_skb(skb);
- kfree(urb->setup_packet);
- return;
- }
- }
-
- err = hci_recv_frame(hdev, skb);
- if (err < 0) {
- kfree_skb(data->evt_skb);
- data->evt_skb = NULL;
- kfree(urb->setup_packet);
- return;
- }
-
- if (test_and_clear_bit(BTUSB_TX_WAIT_VND_EVT,
- &data->flags)) {
- /* Barrier to sync with other CPUs */
- smp_mb__after_atomic();
- wake_up_bit(&data->flags,
- BTUSB_TX_WAIT_VND_EVT);
- }
- kfree(urb->setup_packet);
- return;
- } else if (urb->status == -ENOENT) {
- /* Avoid suspend failed when usb_kill_urb */
- return;
- }
+ int ret;
- usb_mark_last_busy(data->udev);
+ ret = btrtl_setup_realtek(hdev);
- /* The URB complete handler is still called with urb->actual_length = 0
- * when the event is not available, so we should keep re-submitting
- * URB until WMT event returns, Also, It's necessary to wait some time
- * between the two consecutive control URBs to relax the target device
- * to generate the event. Otherwise, the WMT event cannot return from
- * the device successfully.
- */
- udelay(500);
+ if (btrealtek_test_flag(data->hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP))
+ set_bit(BTUSB_ALT6_CONTINUOUS_TX, &data->flags);
- usb_anchor_urb(urb, &data->ctrl_anchor);
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err < 0) {
- kfree(urb->setup_packet);
- /* -EPERM: urb is being killed;
- * -ENODEV: device got disconnected
- */
- if (err != -EPERM && err != -ENODEV)
- bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
- urb, -err);
- usb_unanchor_urb(urb);
- }
+ return ret;
}
-static int btusb_mtk_submit_wmt_recv_urb(struct hci_dev *hdev)
+static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct usb_ctrlrequest *dr;
- unsigned char *buf;
- int err, size = 64;
- unsigned int pipe;
- struct urb *urb;
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
- return -ENOMEM;
-
- dr = kmalloc(sizeof(*dr), GFP_KERNEL);
- if (!dr) {
- usb_free_urb(urb);
- return -ENOMEM;
- }
+ if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
+ struct rtk_dev_coredump_hdr hdr = {
+ .code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
+ };
- dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
- dr->bRequest = 1;
- dr->wIndex = cpu_to_le16(0);
- dr->wValue = cpu_to_le16(48);
- dr->wLength = cpu_to_le16(size);
+ bt_dev_dbg(hdev, "RTL: received coredump vendor evt, len %u",
+ skb->len);
- buf = kmalloc(size, GFP_KERNEL);
- if (!buf) {
- kfree(dr);
- usb_free_urb(urb);
- return -ENOMEM;
- }
-
- pipe = usb_rcvctrlpipe(data->udev, 0);
-
- usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
- buf, size, btusb_mtk_wmt_recv, hdev);
-
- urb->transfer_flags |= URB_FREE_BUFFER;
+ btusb_rtl_alloc_devcoredump(hdev, &hdr, skb->data, skb->len);
+ kfree_skb(skb);
- usb_anchor_urb(urb, &data->ctrl_anchor);
- err = usb_submit_urb(urb, GFP_KERNEL);
- if (err < 0) {
- if (err != -EPERM && err != -ENODEV)
- bt_dev_err(hdev, "urb %p submission failed (%d)",
- urb, -err);
- usb_unanchor_urb(urb);
+ return 0;
}
- usb_free_urb(urb);
-
- return err;
+ return hci_recv_frame(hdev, skb);
}
-static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
- struct btmtk_hci_wmt_params *wmt_params)
+static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
{
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
- u32 hlen, status = BTMTK_WMT_INVALID;
- struct btmtk_hci_wmt_evt *wmt_evt;
- struct btmtk_hci_wmt_cmd *wc;
- struct btmtk_wmt_hdr *hdr;
+ struct btmtk_data *btmtk_data;
int err;
- /* Send the WMT command and wait until the WMT event returns */
- hlen = sizeof(*hdr) + wmt_params->dlen;
- if (hlen > 255)
- return -EINVAL;
-
- wc = kzalloc(hlen, GFP_KERNEL);
- if (!wc)
- return -ENOMEM;
-
- hdr = &wc->hdr;
- hdr->dir = 1;
- hdr->op = wmt_params->op;
- hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
- hdr->flag = wmt_params->flag;
- memcpy(wc->data, wmt_params->data, wmt_params->dlen);
-
- set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
+ if (!data->hdev)
+ return;
- err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
+ btmtk_data = hci_get_priv(data->hdev);
+ if (!btmtk_data)
+ return;
- if (err < 0) {
- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
- goto err_free_wc;
+ if (!btmtk_data->isopkt_intf) {
+ bt_dev_err(data->hdev, "Can't claim NULL iso interface");
+ return;
}
- /* Submit control IN URB on demand to process the WMT event */
- err = btusb_mtk_submit_wmt_recv_urb(hdev);
- if (err < 0)
- goto err_free_wc;
-
- /* The vendor specific WMT commands are all answered by a vendor
- * specific event and will have the Command Status or Command
- * Complete as with usual HCI command flow control.
- *
- * After sending the command, wait for BTUSB_TX_WAIT_VND_EVT
- * state to be cleared. The driver specific event receive routine
- * will clear that state and with that indicate completion of the
- * WMT command.
+ /*
+ * The function usb_driver_claim_interface() is documented to need
+ * locks held if it's not called from a probe routine. The code here
+ * is called from the hci_power_on workqueue, so grab the lock.
*/
- err = wait_on_bit_timeout(&data->flags, BTUSB_TX_WAIT_VND_EVT,
- TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
- if (err == -EINTR) {
- bt_dev_err(hdev, "Execution of wmt command interrupted");
- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
- goto err_free_wc;
- }
-
- if (err) {
- bt_dev_err(hdev, "Execution of wmt command timed out");
- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
- err = -ETIMEDOUT;
- goto err_free_wc;
- }
-
- /* Parse and handle the return WMT event */
- wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data;
- if (wmt_evt->whdr.op != hdr->op) {
- bt_dev_err(hdev, "Wrong op received %d expected %d",
- wmt_evt->whdr.op, hdr->op);
- err = -EIO;
- goto err_free_skb;
- }
-
- switch (wmt_evt->whdr.op) {
- case BTMTK_WMT_SEMAPHORE:
- if (wmt_evt->whdr.flag == 2)
- status = BTMTK_WMT_PATCH_UNDONE;
- else
- status = BTMTK_WMT_PATCH_DONE;
- break;
- case BTMTK_WMT_FUNC_CTRL:
- wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
- if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
- status = BTMTK_WMT_ON_DONE;
- else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420)
- status = BTMTK_WMT_ON_PROGRESS;
- else
- status = BTMTK_WMT_ON_UNDONE;
- break;
- case BTMTK_WMT_PATCH_DWNLD:
- if (wmt_evt->whdr.flag == 2)
- status = BTMTK_WMT_PATCH_DONE;
- else if (wmt_evt->whdr.flag == 1)
- status = BTMTK_WMT_PATCH_PROGRESS;
- else
- status = BTMTK_WMT_PATCH_UNDONE;
- break;
- }
-
- if (wmt_params->status)
- *wmt_params->status = status;
-
-err_free_skb:
- kfree_skb(data->evt_skb);
- data->evt_skb = NULL;
-err_free_wc:
- kfree(wc);
- return err;
-}
-
-static int btusb_mtk_func_query(struct hci_dev *hdev)
-{
- struct btmtk_hci_wmt_params wmt_params;
- int status, err;
- u8 param = 0;
-
- /* Query whether the function is enabled */
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 4;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = &status;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
+ device_lock(&btmtk_data->isopkt_intf->dev);
+ err = usb_driver_claim_interface(&btusb_driver,
+ btmtk_data->isopkt_intf, data);
+ device_unlock(&btmtk_data->isopkt_intf->dev);
if (err < 0) {
- bt_dev_err(hdev, "Failed to query function status (%d)", err);
- return err;
+ btmtk_data->isopkt_intf = NULL;
+ bt_dev_err(data->hdev, "Failed to claim iso interface: %d", err);
+ return;
}
- return status;
+ set_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
+ init_usb_anchor(&btmtk_data->isopkt_anchor);
}
-static int btusb_mtk_uhw_reg_write(struct btusb_data *data, u32 reg, u32 val)
+static void btusb_mtk_release_iso_intf(struct hci_dev *hdev)
{
- struct hci_dev *hdev = data->hdev;
- int pipe, err;
- void *buf;
-
- buf = kzalloc(4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- put_unaligned_le32(val, buf);
-
- pipe = usb_sndctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x02,
- 0x5E,
- reg >> 16, reg & 0xffff,
- buf, 4, USB_CTRL_SET_TIMEOUT);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to write uhw reg(%d)", err);
- goto err_free_buf;
- }
+ struct btmtk_data *btmtk_data;
-err_free_buf:
- kfree(buf);
+ if (!hdev)
+ return;
- return err;
-}
+ btmtk_data = hci_get_priv(hdev);
+ if (!btmtk_data)
+ return;
-static int btusb_mtk_uhw_reg_read(struct btusb_data *data, u32 reg, u32 *val)
-{
- struct hci_dev *hdev = data->hdev;
- int pipe, err;
- void *buf;
+ if (test_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags)) {
+ usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
+ clear_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags);
- buf = kzalloc(4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ if (btmtk_data->isopkt_skb) {
+ dev_kfree_skb_irq(btmtk_data->isopkt_skb);
+ btmtk_data->isopkt_skb = NULL;
+ }
- pipe = usb_rcvctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x01,
- 0xDE,
- reg >> 16, reg & 0xffff,
- buf, 4, USB_CTRL_SET_TIMEOUT);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
- goto err_free_buf;
+ if (btmtk_data->isopkt_intf) {
+ usb_set_intfdata(btmtk_data->isopkt_intf, NULL);
+ usb_driver_release_interface(&btusb_driver,
+ btmtk_data->isopkt_intf);
+ btmtk_data->isopkt_intf = NULL;
+ }
}
- *val = get_unaligned_le32(buf);
- bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
-
-err_free_buf:
- kfree(buf);
-
- return err;
+ clear_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
}
-static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
+static int btusb_mtk_disconnect(struct hci_dev *hdev)
{
- int pipe, err, size = sizeof(u32);
- void *buf;
-
- buf = kzalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pipe = usb_rcvctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x63,
- USB_TYPE_VENDOR | USB_DIR_IN,
- reg >> 16, reg & 0xffff,
- buf, size, USB_CTRL_SET_TIMEOUT);
- if (err < 0)
- goto err_free_buf;
-
- *val = get_unaligned_le32(buf);
-
-err_free_buf:
- kfree(buf);
-
- return err;
-}
+ /* This function describes the specific additional steps taken by MediaTek
+ * when Bluetooth usb driver's resume function is called.
+ */
+ btusb_mtk_release_iso_intf(hdev);
-static int btusb_mtk_id_get(struct btusb_data *data, u32 reg, u32 *id)
-{
- return btusb_mtk_reg_read(data, reg, id);
+ return 0;
}
-static int btusb_mtk_setup(struct hci_dev *hdev)
+static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
{
struct btusb_data *data = hci_get_drvdata(hdev);
- struct btmtk_hci_wmt_params wmt_params;
- ktime_t calltime, delta, rettime;
- struct btmtk_tci_sleep tci_sleep;
- unsigned long long duration;
- struct sk_buff *skb;
- const char *fwname;
- int err, status;
- u32 dev_id;
- char fw_bin_name[64];
- u32 fw_version = 0;
- u8 param;
-
- calltime = ktime_get();
-
- err = btusb_mtk_id_get(data, 0x80000008, &dev_id);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get device id (%d)", err);
- return err;
- }
-
- if (!dev_id) {
- err = btusb_mtk_id_get(data, 0x70010200, &dev_id);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get device id (%d)", err);
- return err;
- }
- err = btusb_mtk_id_get(data, 0x80021004, &fw_version);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get fw version (%d)", err);
- return err;
- }
- }
-
- switch (dev_id) {
- case 0x7663:
- fwname = FIRMWARE_MT7663;
- break;
- case 0x7668:
- fwname = FIRMWARE_MT7668;
- break;
- case 0x7922:
- case 0x7961:
- snprintf(fw_bin_name, sizeof(fw_bin_name),
- "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
- dev_id & 0xffff, (fw_version & 0xff) + 1);
- err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
- btusb_mtk_hci_wmt_sync);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
- return err;
- }
-
- /* It's Device EndPoint Reset Option Register */
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
-
- /* Enable Bluetooth protocol */
- param = 1;
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = NULL;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
-
- hci_set_msft_opcode(hdev, 0xFD30);
- hci_set_aosp_capable(hdev);
- goto done;
- default:
- bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
- dev_id);
- return -ENODEV;
- }
-
- /* Query whether the firmware is already download */
- wmt_params.op = BTMTK_WMT_SEMAPHORE;
- wmt_params.flag = 1;
- wmt_params.dlen = 0;
- wmt_params.data = NULL;
- wmt_params.status = &status;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
- return err;
- }
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ int err;
- if (status == BTMTK_WMT_PATCH_DONE) {
- bt_dev_info(hdev, "firmware already downloaded");
- goto ignore_setup_fw;
+ /* It's MediaTek specific bluetooth reset mechanism via USB */
+ if (test_and_set_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags)) {
+ bt_dev_err(hdev, "last reset failed? Not resetting again");
+ return -EBUSY;
}
- /* Setup a firmware which the device definitely requires */
- err = btmtk_setup_firmware(hdev, fwname,
- btusb_mtk_hci_wmt_sync);
- if (err < 0)
- return err;
-
-ignore_setup_fw:
- err = readx_poll_timeout(btusb_mtk_func_query, hdev, status,
- status < 0 || status != BTMTK_WMT_ON_PROGRESS,
- 2000, 5000000);
- /* -ETIMEDOUT happens */
+ err = usb_autopm_get_interface(data->intf);
if (err < 0)
return err;
- /* The other errors happen in btusb_mtk_func_query */
- if (status < 0)
- return status;
-
- if (status == BTMTK_WMT_ON_DONE) {
- bt_dev_info(hdev, "function already on");
- goto ignore_func_on;
- }
-
- /* Enable Bluetooth protocol */
- param = 1;
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = NULL;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
+ /* Release MediaTek ISO data interface */
+ btusb_mtk_release_iso_intf(hdev);
-ignore_func_on:
- /* Apply the low power environment setup */
- tci_sleep.mode = 0x5;
- tci_sleep.duration = cpu_to_le16(0x640);
- tci_sleep.host_duration = cpu_to_le16(0x640);
- tci_sleep.host_wakeup_pin = 0;
- tci_sleep.time_compensation = 0;
+ btusb_stop_traffic(data);
+ usb_kill_anchored_urbs(&data->tx_anchor);
- skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
- return err;
+ /* Toggle the hard reset line. The MediaTek device is going to
+ * yank itself off the USB and then replug. The cleanup is handled
+ * correctly on the way out (standard USB disconnect), and the new
+ * device is detected cleanly and bound to the driver again like
+ * it should be.
+ */
+ if (data->reset_gpio) {
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
+ msleep(200);
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+ return 0;
}
- kfree_skb(skb);
-done:
- rettime = ktime_get();
- delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+ err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
- bt_dev_info(hdev, "Device setup in %llu usecs", duration);
+ usb_queue_reset_device(data->intf);
+ clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
- return 0;
+ return err;
}
-static int btusb_mtk_shutdown(struct hci_dev *hdev)
+static int btusb_send_frame_mtk(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct btmtk_hci_wmt_params wmt_params;
- u8 param = 0;
- int err;
+ struct urb *urb;
- /* Disable the device */
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = NULL;
+ BT_DBG("%s", hdev->name);
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
+ if (hci_skb_pkt_type(skb) == HCI_ISODATA_PKT) {
+ urb = alloc_mtk_intr_urb(hdev, skb, btusb_tx_complete);
+ if (IS_ERR(urb))
+ return PTR_ERR(urb);
- return 0;
+ return submit_or_queue_tx_urb(hdev, urb);
+ } else {
+ return btusb_send_frame(hdev, skb);
+ }
}
-static void btusb_mtk_cmd_timeout(struct hci_dev *hdev)
+static int btusb_mtk_setup(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
- u32 val;
- int err, retry = 0;
-
- /* It's MediaTek specific bluetooth reset mechanism via USB */
- if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
- bt_dev_err(hdev, "last reset failed? Not resetting again");
- return;
- }
-
- err = usb_autopm_get_interface(data->intf);
- if (err < 0)
- return;
-
- btusb_stop_traffic(data);
- usb_kill_anchored_urbs(&data->tx_anchor);
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
- /* It's Device EndPoint Reset Option Register */
- bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
- btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
-
- /* Reset the bluetooth chip via USB interface. */
- btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
- /* MT7921 need to delay 20ms between toggle reset bit */
- msleep(20);
- btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
- btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
-
- /* Poll the register until reset is completed */
- do {
- btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
- if (val & MTK_BT_RST_DONE) {
- bt_dev_dbg(hdev, "Bluetooth Reset Successfully");
- break;
- }
-
- bt_dev_dbg(hdev, "Polling Bluetooth Reset CR");
- retry++;
- msleep(MTK_BT_RESET_WAIT_MS);
- } while (retry < MTK_BT_RESET_NUM_TRIES);
-
- btusb_mtk_id_get(data, 0x70010200, &val);
- if (!val)
- bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
+ /* MediaTek WMT vendor cmd requiring below USB resources to
+ * complete the handshake.
+ */
+ btmtk_data->drv_name = btusb_driver.name;
+ btmtk_data->intf = data->intf;
+ btmtk_data->udev = data->udev;
+ btmtk_data->ctrl_anchor = &data->ctrl_anchor;
+ btmtk_data->reset_sync = btusb_mtk_reset;
- usb_queue_reset_device(data->intf);
+ /* Claim ISO data interface and endpoint */
+ if (!test_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags)) {
+ btmtk_data->isopkt_intf = usb_ifnum_to_if(data->udev, MTK_ISO_IFNUM);
+ btusb_mtk_claim_iso_intf(data);
+ }
- clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
+ return btmtk_usb_setup(hdev);
}
-static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
+static int btusb_mtk_shutdown(struct hci_dev *hdev)
{
- struct btusb_data *data = hci_get_drvdata(hdev);
- u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
+ int ret;
- switch (handle) {
- case 0xfc6f: /* Firmware dump from device */
- /* When the firmware hangs, the device can no longer
- * suspend and thus disable auto-suspend.
- */
- usb_disable_autosuspend(data->udev);
- fallthrough;
- case 0x05ff: /* Firmware debug logging 1 */
- case 0x05fe: /* Firmware debug logging 2 */
- return hci_recv_diag(hdev, skb);
- }
+ ret = btmtk_usb_shutdown(hdev);
- return hci_recv_frame(hdev, skb);
+ /* Release MediaTek iso interface after shutdown */
+ btusb_mtk_release_iso_intf(hdev);
+
+ return ret;
}
#ifdef CONFIG_PM
@@ -3042,6 +3025,228 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
return 0;
}
+#define QCA_MEMDUMP_ACL_HANDLE 0x2EDD
+#define QCA_MEMDUMP_SIZE_MAX 0x100000
+#define QCA_MEMDUMP_VSE_CLASS 0x01
+#define QCA_MEMDUMP_MSG_TYPE 0x08
+#define QCA_MEMDUMP_PKT_SIZE 248
+#define QCA_LAST_SEQUENCE_NUM 0xffff
+
+struct qca_dump_hdr {
+ u8 vse_class;
+ u8 msg_type;
+ __le16 seqno;
+ u8 reserved;
+ union {
+ u8 data[0];
+ struct {
+ __le32 ram_dump_size;
+ u8 data0[0];
+ } __packed;
+ };
+} __packed;
+
+
+static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ char buf[128];
+ struct btusb_data *btdata = hci_get_drvdata(hdev);
+
+ snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n",
+ btdata->qca_dump.controller_id);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n",
+ btdata->qca_dump.fw_version);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n",
+ btusb_driver.name);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n",
+ btdata->qca_dump.id_vendor, btdata->qca_dump.id_product);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n",
+ hdev->lmp_subver);
+ skb_put_data(skb, buf, strlen(buf));
+}
+
+static void btusb_coredump_qca(struct hci_dev *hdev)
+{
+ int err;
+ static const u8 param[] = { 0x26 };
+
+ err = __hci_cmd_send(hdev, 0xfc0c, 1, param);
+ if (err < 0)
+ bt_dev_err(hdev, "%s: triggle crash failed (%d)", __func__, err);
+}
+
+/* Return: 0 on success, negative errno on failure. */
+static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ int ret = 0;
+ unsigned int skip = 0;
+ u8 pkt_type;
+ u16 seqno;
+ u32 dump_size;
+
+ struct qca_dump_hdr *dump_hdr;
+ struct btusb_data *btdata = hci_get_drvdata(hdev);
+ struct usb_device *udev = btdata->udev;
+
+ pkt_type = hci_skb_pkt_type(skb);
+ skip = sizeof(struct hci_event_hdr);
+ if (pkt_type == HCI_ACLDATA_PKT)
+ skip += sizeof(struct hci_acl_hdr);
+
+ skb_pull(skb, skip);
+ dump_hdr = (struct qca_dump_hdr *)skb->data;
+
+ seqno = le16_to_cpu(dump_hdr->seqno);
+ if (seqno == 0) {
+ set_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags);
+ dump_size = le32_to_cpu(dump_hdr->ram_dump_size);
+ if (!dump_size || (dump_size > QCA_MEMDUMP_SIZE_MAX)) {
+ ret = -EILSEQ;
+ bt_dev_err(hdev, "Invalid memdump size(%u)",
+ dump_size);
+ goto out;
+ }
+
+ ret = hci_devcd_init(hdev, dump_size);
+ if (ret < 0) {
+ bt_dev_err(hdev, "memdump init error(%d)", ret);
+ goto out;
+ }
+
+ btdata->qca_dump.ram_dump_size = dump_size;
+ btdata->qca_dump.ram_dump_seqno = 0;
+
+ skb_pull(skb, offsetof(struct qca_dump_hdr, data0));
+
+ usb_disable_autosuspend(udev);
+ bt_dev_info(hdev, "%s memdump size(%u)\n",
+ (pkt_type == HCI_ACLDATA_PKT) ? "ACL" : "event",
+ dump_size);
+ } else {
+ skb_pull(skb, offsetof(struct qca_dump_hdr, data));
+ }
+
+ if (!btdata->qca_dump.ram_dump_size) {
+ ret = -EINVAL;
+ bt_dev_err(hdev, "memdump is not active");
+ goto out;
+ }
+
+ if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) {
+ dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1);
+ hci_devcd_append_pattern(hdev, 0x0, dump_size);
+ bt_dev_err(hdev,
+ "expected memdump seqno(%u) is not received(%u)\n",
+ btdata->qca_dump.ram_dump_seqno, seqno);
+ btdata->qca_dump.ram_dump_seqno = seqno;
+ kfree_skb(skb);
+ return ret;
+ }
+
+ hci_devcd_append(hdev, skb);
+ btdata->qca_dump.ram_dump_seqno++;
+ if (seqno == QCA_LAST_SEQUENCE_NUM) {
+ bt_dev_info(hdev,
+ "memdump done: pkts(%u), total(%u)\n",
+ btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size);
+
+ hci_devcd_complete(hdev);
+ goto out;
+ }
+ return ret;
+
+out:
+ if (btdata->qca_dump.ram_dump_size)
+ usb_enable_autosuspend(udev);
+ btdata->qca_dump.ram_dump_size = 0;
+ btdata->qca_dump.ram_dump_seqno = 0;
+ clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags);
+
+ if (ret < 0)
+ kfree_skb(skb);
+ return ret;
+}
+
+/* Return: true if the ACL packet is a dump packet, false otherwise. */
+static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *event_hdr;
+ struct hci_acl_hdr *acl_hdr;
+ struct qca_dump_hdr *dump_hdr;
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+ bool is_dump = false;
+
+ if (!clone)
+ return false;
+
+ acl_hdr = skb_pull_data(clone, sizeof(*acl_hdr));
+ if (!acl_hdr || (le16_to_cpu(acl_hdr->handle) != QCA_MEMDUMP_ACL_HANDLE))
+ goto out;
+
+ event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
+ if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
+ goto out;
+
+ dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
+ if (!dump_hdr || (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) ||
+ (dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE))
+ goto out;
+
+ is_dump = true;
+out:
+ consume_skb(clone);
+ return is_dump;
+}
+
+/* Return: true if the event packet is a dump packet, false otherwise. */
+static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *event_hdr;
+ struct qca_dump_hdr *dump_hdr;
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+ bool is_dump = false;
+
+ if (!clone)
+ return false;
+
+ event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
+ if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
+ goto out;
+
+ dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
+ if (!dump_hdr || (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) ||
+ (dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE))
+ goto out;
+
+ is_dump = true;
+out:
+ consume_skb(clone);
+ return is_dump;
+}
+
+static int btusb_recv_acl_qca(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ if (acl_pkt_is_dump_qca(hdev, skb))
+ return handle_dump_pkt_qca(hdev, skb);
+ return hci_recv_frame(hdev, skb);
+}
+
+static int btusb_recv_evt_qca(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ if (evt_pkt_is_dump_qca(hdev, skb))
+ return handle_dump_pkt_qca(hdev, skb);
+ return hci_recv_frame(hdev, skb);
+}
+
+
#define QCA_DFU_PACKET_LEN 4096
#define QCA_GET_TARGET_VERSION 0x09
@@ -3080,6 +3285,12 @@ struct qca_device_info {
u8 ver_offset; /* offset of version structure in rampatch */
};
+struct qca_custom_firmware {
+ u32 rom_version;
+ u16 board_id;
+ const char *subdirectory;
+};
+
static const struct qca_device_info qca_devices_table[] = {
{ 0x00000100, 20, 4, 8 }, /* Rome 1.0 */
{ 0x00000101, 20, 4, 8 }, /* Rome 1.1 */
@@ -3093,6 +3304,58 @@ static const struct qca_device_info qca_devices_table[] = {
{ 0x00190200, 40, 4, 16 }, /* WCN785x 2.0 */
};
+static const struct qca_custom_firmware qca_custom_btfws[] = {
+ { 0x00130201, 0x030A, "QCA2066" },
+ { 0x00130201, 0x030B, "QCA2066" },
+ { },
+};
+
+static u16 qca_extract_board_id(const struct qca_version *ver)
+{
+ u16 flag = le16_to_cpu(ver->flag);
+ u16 board_id = 0;
+
+ if (((flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) {
+ /* The board_id should be split into two bytes
+ * The 1st byte is chip ID, and the 2nd byte is platform ID
+ * For example, board ID 0x010A, 0x01 is platform ID. 0x0A is chip ID
+ * we have several platforms, and platform IDs are continuously added
+ * Platform ID:
+ * 0x00 is for Mobile
+ * 0x01 is for X86
+ * 0x02 is for Automotive
+ * 0x03 is for Consumer electronic
+ */
+ board_id = (ver->chip_id << 8) + ver->platform_id;
+ }
+
+ /* Take 0xffff as invalid board ID */
+ if (board_id == 0xffff)
+ board_id = 0;
+
+ return board_id;
+}
+
+static const char *qca_get_fw_subdirectory(const struct qca_version *ver)
+{
+ const struct qca_custom_firmware *ptr;
+ u32 rom_ver;
+ u16 board_id;
+
+ rom_ver = le32_to_cpu(ver->rom_version);
+ board_id = qca_extract_board_id(ver);
+ if (!board_id)
+ return NULL;
+
+ for (ptr = qca_custom_btfws; ptr->rom_version; ptr++) {
+ if (ptr->rom_version == rom_ver &&
+ ptr->board_id == board_id)
+ return ptr->subdirectory;
+ }
+
+ return NULL;
+}
+
static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request,
void *data, u16 size)
{
@@ -3108,7 +3371,7 @@ static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request,
*/
pipe = usb_rcvctrlpipe(udev, 0);
err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN,
- 0, 0, buf, size, USB_CTRL_SET_TIMEOUT);
+ 0, 0, buf, size, USB_CTRL_GET_TIMEOUT);
if (err < 0) {
dev_err(&udev->dev, "Failed to access otp area (%d)", err);
goto done;
@@ -3197,15 +3460,22 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
{
struct qca_rampatch_version *rver;
const struct firmware *fw;
+ const char *fw_subdir;
u32 ver_rom, ver_patch, rver_rom;
u16 rver_rom_low, rver_rom_high, rver_patch;
- char fwname[64];
+ char fwname[80];
int err;
ver_rom = le32_to_cpu(ver->rom_version);
ver_patch = le32_to_cpu(ver->patch_version);
- snprintf(fwname, sizeof(fwname), "qca/rampatch_usb_%08x.bin", ver_rom);
+ fw_subdir = qca_get_fw_subdirectory(ver);
+ if (fw_subdir)
+ snprintf(fwname, sizeof(fwname), "qca/%s/rampatch_usb_%08x.bin",
+ fw_subdir, ver_rom);
+ else
+ snprintf(fwname, sizeof(fwname), "qca/rampatch_usb_%08x.bin",
+ ver_rom);
err = request_firmware(&fw, fwname, &hdev->dev);
if (err) {
@@ -3222,7 +3492,7 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
if (ver_rom & ~0xffffU) {
rver_rom_high = le16_to_cpu(rver->rom_version_high);
- rver_rom = le32_to_cpu(rver_rom_high << 16 | rver_rom_low);
+ rver_rom = rver_rom_high << 16 | rver_rom_low;
} else {
rver_rom = rver_rom_low;
}
@@ -3249,44 +3519,34 @@ static void btusb_generate_qca_nvm_name(char *fwname, size_t max_size,
const struct qca_version *ver)
{
u32 rom_version = le32_to_cpu(ver->rom_version);
- u16 flag = le16_to_cpu(ver->flag);
+ const char *variant, *fw_subdir;
+ int len;
+ u16 board_id;
- if (((flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) {
- /* The board_id should be split into two bytes
- * The 1st byte is chip ID, and the 2nd byte is platform ID
- * For example, board ID 0x010A, 0x01 is platform ID. 0x0A is chip ID
- * we have several platforms, and platform IDs are continuously added
- * Platform ID:
- * 0x00 is for Mobile
- * 0x01 is for X86
- * 0x02 is for Automotive
- * 0x03 is for Consumer electronic
- */
- u16 board_id = (ver->chip_id << 8) + ver->platform_id;
- const char *variant;
-
- switch (le32_to_cpu(ver->ram_version)) {
- case WCN6855_2_0_RAM_VERSION_GF:
- case WCN6855_2_1_RAM_VERSION_GF:
- variant = "_gf";
- break;
- default:
- variant = "";
- break;
- }
+ fw_subdir = qca_get_fw_subdirectory(ver);
+ board_id = qca_extract_board_id(ver);
- if (board_id == 0) {
- snprintf(fwname, max_size, "qca/nvm_usb_%08x%s.bin",
- rom_version, variant);
- } else {
- snprintf(fwname, max_size, "qca/nvm_usb_%08x%s_%04x.bin",
- rom_version, variant, board_id);
- }
- } else {
- snprintf(fwname, max_size, "qca/nvm_usb_%08x.bin",
- rom_version);
+ switch (le32_to_cpu(ver->ram_version)) {
+ case WCN6855_2_0_RAM_VERSION_GF:
+ case WCN6855_2_1_RAM_VERSION_GF:
+ variant = "_gf";
+ break;
+ default:
+ variant = NULL;
+ break;
}
+ if (fw_subdir)
+ len = snprintf(fwname, max_size, "qca/%s/nvm_usb_%08x",
+ fw_subdir, rom_version);
+ else
+ len = snprintf(fwname, max_size, "qca/nvm_usb_%08x",
+ rom_version);
+ if (variant)
+ len += snprintf(fwname + len, max_size - len, "%s", variant);
+ if (board_id)
+ len += snprintf(fwname + len, max_size - len, "_%04x", board_id);
+ len += snprintf(fwname + len, max_size - len, ".bin");
}
static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
@@ -3294,7 +3554,7 @@ static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
const struct qca_device_info *info)
{
const struct firmware *fw;
- char fwname[64];
+ char fwname[80];
int err;
btusb_generate_qca_nvm_name(fwname, sizeof(fwname), ver);
@@ -3376,6 +3636,9 @@ static int btusb_setup_qca(struct hci_dev *hdev)
if (err < 0)
return err;
+ btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
+ btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version);
+
if (!(status & QCA_SYSCFG_UPDATED)) {
err = btusb_setup_qca_load_nvm(hdev, &ver, info);
if (err < 0)
@@ -3392,7 +3655,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
/* Mark HCI_OP_ENHANCED_SETUP_SYNC_CONN as broken as it doesn't seem to
* work with the likes of HSP/HFP mSBC.
*/
- set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
return 0;
}
@@ -3611,12 +3874,143 @@ static ssize_t force_poll_sync_write(struct file *file,
}
static const struct file_operations force_poll_sync_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = force_poll_sync_read,
.write = force_poll_sync_write,
.llseek = default_llseek,
};
+#define BTUSB_HCI_DRV_OP_SUPPORTED_ALTSETTINGS \
+ hci_opcode_pack(HCI_DRV_OGF_DRIVER_SPECIFIC, 0x0000)
+#define BTUSB_HCI_DRV_SUPPORTED_ALTSETTINGS_SIZE 0
+struct btusb_hci_drv_rp_supported_altsettings {
+ __u8 num;
+ __u8 altsettings[];
+} __packed;
+
+#define BTUSB_HCI_DRV_OP_SWITCH_ALTSETTING \
+ hci_opcode_pack(HCI_DRV_OGF_DRIVER_SPECIFIC, 0x0001)
+#define BTUSB_HCI_DRV_SWITCH_ALTSETTING_SIZE 1
+struct btusb_hci_drv_cmd_switch_altsetting {
+ __u8 altsetting;
+} __packed;
+
+static const struct {
+ u16 opcode;
+ const char *desc;
+} btusb_hci_drv_supported_commands[] = {
+ /* Common commands */
+ { HCI_DRV_OP_READ_INFO, "Read Info" },
+
+ /* Driver specific commands */
+ { BTUSB_HCI_DRV_OP_SUPPORTED_ALTSETTINGS, "Supported Altsettings" },
+ { BTUSB_HCI_DRV_OP_SWITCH_ALTSETTING, "Switch Altsetting" },
+};
+static int btusb_hci_drv_read_info(struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct hci_drv_rp_read_info *rp;
+ size_t rp_size;
+ int err, i;
+ u16 opcode, num_supported_commands =
+ ARRAY_SIZE(btusb_hci_drv_supported_commands);
+
+ rp_size = sizeof(*rp) + num_supported_commands * 2;
+
+ rp = kmalloc(rp_size, GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ strscpy_pad(rp->driver_name, btusb_driver.name);
+
+ rp->num_supported_commands = cpu_to_le16(num_supported_commands);
+ for (i = 0; i < num_supported_commands; i++) {
+ opcode = btusb_hci_drv_supported_commands[i].opcode;
+ bt_dev_info(hdev,
+ "Supported HCI Drv command (0x%02x|0x%04x): %s",
+ hci_opcode_ogf(opcode),
+ hci_opcode_ocf(opcode),
+ btusb_hci_drv_supported_commands[i].desc);
+ rp->supported_commands[i] = cpu_to_le16(opcode);
+ }
+
+ err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
+ HCI_DRV_STATUS_SUCCESS, rp, rp_size);
+
+ kfree(rp);
+ return err;
+}
+
+static int btusb_hci_drv_supported_altsettings(struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct btusb_data *drvdata = hci_get_drvdata(hdev);
+ struct btusb_hci_drv_rp_supported_altsettings *rp;
+ size_t rp_size;
+ int err;
+ u8 i;
+
+ /* There are at most 7 alt (0 - 6) */
+ rp = kmalloc(sizeof(*rp) + 7, GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ rp->num = 0;
+ if (!drvdata->isoc)
+ goto done;
+
+ for (i = 0; i <= 6; i++) {
+ if (btusb_find_altsetting(drvdata, i))
+ rp->altsettings[rp->num++] = i;
+ }
+
+done:
+ rp_size = sizeof(*rp) + rp->num;
+
+ err = hci_drv_cmd_complete(hdev, BTUSB_HCI_DRV_OP_SUPPORTED_ALTSETTINGS,
+ HCI_DRV_STATUS_SUCCESS, rp, rp_size);
+ kfree(rp);
+ return err;
+}
+
+static int btusb_hci_drv_switch_altsetting(struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct btusb_hci_drv_cmd_switch_altsetting *cmd = data;
+ u8 status;
+
+ if (cmd->altsetting > 6) {
+ status = HCI_DRV_STATUS_INVALID_PARAMETERS;
+ } else {
+ if (btusb_switch_alt_setting(hdev, cmd->altsetting))
+ status = HCI_DRV_STATUS_UNSPECIFIED_ERROR;
+ else
+ status = HCI_DRV_STATUS_SUCCESS;
+ }
+
+ return hci_drv_cmd_status(hdev, BTUSB_HCI_DRV_OP_SWITCH_ALTSETTING,
+ status);
+}
+
+static const struct hci_drv_handler btusb_hci_drv_common_handlers[] = {
+ { btusb_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE },
+};
+
+static const struct hci_drv_handler btusb_hci_drv_specific_handlers[] = {
+ { btusb_hci_drv_supported_altsettings,
+ BTUSB_HCI_DRV_SUPPORTED_ALTSETTINGS_SIZE },
+ { btusb_hci_drv_switch_altsetting,
+ BTUSB_HCI_DRV_SWITCH_ALTSETTING_SIZE },
+};
+
+static struct hci_drv btusb_hci_drv = {
+ .common_handler_count = ARRAY_SIZE(btusb_hci_drv_common_handlers),
+ .common_handlers = btusb_hci_drv_common_handlers,
+ .specific_handler_count = ARRAY_SIZE(btusb_hci_drv_specific_handlers),
+ .specific_handlers = btusb_hci_drv_specific_handlers,
+};
+
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -3629,20 +4023,17 @@ static int btusb_probe(struct usb_interface *intf,
BT_DBG("intf %p id %p", intf, id);
- /* interface numbers are hardcoded in the spec */
- if (intf->cur_altsetting->desc.bInterfaceNumber != 0) {
- if (!(id->driver_info & BTUSB_IFNUM_2))
- return -ENODEV;
- if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
- return -ENODEV;
- }
+ if ((id->driver_info & BTUSB_IFNUM_2) &&
+ (intf->cur_altsetting->desc.bInterfaceNumber != 0) &&
+ (intf->cur_altsetting->desc.bInterfaceNumber != 2))
+ return -ENODEV;
ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
if (!id->driver_info) {
const struct usb_device_id *match;
- match = usb_match_id(intf, blacklist_table);
+ match = usb_match_id(intf, quirks_table);
if (match)
id = match;
}
@@ -3725,8 +4116,16 @@ static int btusb_probe(struct usb_interface *intf,
priv_size += sizeof(struct btintel_data);
/* Override the rx handlers */
- data->recv_event = btusb_recv_event_intel;
+ data->recv_event = btintel_recv_event;
data->recv_bulk = btusb_recv_bulk_intel;
+ } else if (id->driver_info & BTUSB_REALTEK) {
+ /* Allocate extra space for Realtek device */
+ priv_size += sizeof(struct btrealtek_data);
+
+ data->recv_event = btusb_recv_event_realtek;
+ } else if (id->driver_info & BTUSB_MEDIATEK) {
+ /* Allocate extra space for Mediatek device */
+ priv_size += sizeof(struct btmtk_data);
}
data->recv_acl = hci_recv_frame;
@@ -3738,11 +4137,6 @@ static int btusb_probe(struct usb_interface *intf,
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, data);
- if (id->driver_info & BTUSB_AMP)
- hdev->dev_type = HCI_AMP;
- else
- hdev->dev_type = HCI_PRIMARY;
-
data->hdev = hdev;
SET_HCIDEV_DEV(hdev, &intf->dev);
@@ -3756,12 +4150,13 @@ static int btusb_probe(struct usb_interface *intf,
data->reset_gpio = reset_gpio;
}
- hdev->open = btusb_open;
- hdev->close = btusb_close;
- hdev->flush = btusb_flush;
- hdev->send = btusb_send_frame;
- hdev->notify = btusb_notify;
- hdev->wakeup = btusb_wakeup;
+ hdev->open = btusb_open;
+ hdev->close = btusb_close;
+ hdev->flush = btusb_flush;
+ hdev->send = btusb_send_frame;
+ hdev->notify = btusb_notify;
+ hdev->wakeup = btusb_wakeup;
+ hdev->hci_drv = &btusb_hci_drv;
#ifdef CONFIG_PM
err = btusb_config_oob_wake(hdev);
@@ -3776,10 +4171,10 @@ static int btusb_probe(struct usb_interface *intf,
}
#endif
if (id->driver_info & BTUSB_CW6622)
- set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY);
if (id->driver_info & BTUSB_BCM2045)
- set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY);
if (id->driver_info & BTUSB_BCM92035)
hdev->setup = btusb_setup_bcm92035;
@@ -3807,13 +4202,13 @@ static int btusb_probe(struct usb_interface *intf,
/* Combined Intel Device setup to support multiple setup routine */
if (id->driver_info & BTUSB_INTEL_COMBINED) {
- err = btintel_configure_setup(hdev);
+ err = btintel_configure_setup(hdev, btusb_driver.name);
if (err)
goto out_free_dev;
/* Transport specific configuration */
hdev->send = btusb_send_frame_intel;
- hdev->cmd_timeout = btusb_intel_cmd_timeout;
+ hdev->reset = btusb_intel_reset;
if (id->driver_info & BTUSB_INTEL_NO_WBS_SUPPORT)
btintel_set_flag(hdev, INTEL_ROM_LEGACY_NO_WBS_SUPPORT);
@@ -3833,44 +4228,55 @@ static int btusb_probe(struct usb_interface *intf,
hdev->setup = btusb_mtk_setup;
hdev->shutdown = btusb_mtk_shutdown;
hdev->manufacturer = 70;
- hdev->cmd_timeout = btusb_mtk_cmd_timeout;
+ hdev->reset = btmtk_reset_sync;
hdev->set_bdaddr = btmtk_set_bdaddr;
- set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
- set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
- data->recv_acl = btusb_recv_acl_mtk;
+ hdev->send = btusb_send_frame_mtk;
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+ data->recv_acl = btmtk_usb_recv_acl;
+ data->suspend = btmtk_usb_suspend;
+ data->resume = btmtk_usb_resume;
+ data->disconnect = btusb_mtk_disconnect;
}
if (id->driver_info & BTUSB_SWAVE) {
- set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks);
- set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_FIXUP_INQUIRY_MODE);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LOCAL_COMMANDS);
}
if (id->driver_info & BTUSB_INTEL_BOOT) {
hdev->manufacturer = 2;
- set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE);
}
if (id->driver_info & BTUSB_ATH3012) {
data->setup_on_usb = btusb_setup_qca;
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
}
if (id->driver_info & BTUSB_QCA_ROME) {
data->setup_on_usb = btusb_setup_qca;
+ hdev->shutdown = btusb_shutdown_qca;
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
- hdev->cmd_timeout = btusb_qca_cmd_timeout;
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+ hdev->reset = btusb_qca_reset;
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
btusb_check_needs_reset_resume(intf);
}
if (id->driver_info & BTUSB_QCA_WCN6855) {
+ data->qca_dump.id_vendor = id->idVendor;
+ data->qca_dump.id_product = id->idProduct;
+ data->recv_event = btusb_recv_evt_qca;
+ data->recv_acl = btusb_recv_acl_qca;
+ hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL);
data->setup_on_usb = btusb_setup_qca;
+ hdev->classify_pkt_type = btusb_classify_qca_pkt_type;
hdev->shutdown = btusb_shutdown_qca;
hdev->set_bdaddr = btusb_set_bdaddr_wcn6855;
- hdev->cmd_timeout = btusb_qca_cmd_timeout;
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+ hdev->reset = btusb_qca_reset;
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
hci_set_msft_opcode(hdev, 0xFD70);
}
@@ -3885,35 +4291,48 @@ static int btusb_probe(struct usb_interface *intf,
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) &&
(id->driver_info & BTUSB_REALTEK)) {
- hdev->setup = btrtl_setup_realtek;
+ btrtl_set_driver_name(hdev, btusb_driver.name);
+ hdev->setup = btusb_setup_realtek;
hdev->shutdown = btrtl_shutdown_realtek;
- hdev->cmd_timeout = btusb_rtl_cmd_timeout;
+ hdev->reset = btusb_rtl_reset;
+ hdev->hw_error = btusb_rtl_hw_error;
/* Realtek devices need to set remote wakeup on auto-suspend */
set_bit(BTUSB_WAKEUP_AUTOSUSPEND, &data->flags);
set_bit(BTUSB_USE_ALT3_FOR_WBS, &data->flags);
}
+ if (id->driver_info & BTUSB_ACTIONS_SEMI) {
+ /* Support is advertised, but not implemented */
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ERR_DATA_REPORTING);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_CREATE_CONN);
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_WRITE_AUTH_PAYLOAD_TIMEOUT);
+ }
+
if (!reset)
- set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
if (!disable_scofix)
- set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_FIXUP_BUFFER_SIZE);
}
if (id->driver_info & BTUSB_BROKEN_ISOC)
data->isoc = NULL;
if (id->driver_info & BTUSB_WIDEBAND_SPEECH)
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
- if (id->driver_info & BTUSB_VALID_LE_STATES)
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
+ if (id->driver_info & BTUSB_INVALID_LE_STATES)
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
if (id->driver_info & BTUSB_DIGIANSWER) {
data->cmdreq_type = USB_TYPE_VENDOR;
- set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
}
if (id->driver_info & BTUSB_CSR) {
@@ -3922,10 +4341,10 @@ static int btusb_probe(struct usb_interface *intf,
/* Old firmware would otherwise execute USB reset */
if (bcdDevice < 0x117)
- set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
/* This must be set first in case we disable it for fakes */
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
/* Fake CSR devices with broken commands */
if (le16_to_cpu(udev->descriptor.idVendor) == 0x0a12 &&
@@ -3938,7 +4357,7 @@ static int btusb_probe(struct usb_interface *intf,
/* New sniffer firmware has crippled HCI interface */
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
- set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE);
}
if (id->driver_info & BTUSB_INTEL_BOOT) {
@@ -3970,6 +4389,8 @@ static int btusb_probe(struct usb_interface *intf,
if (enable_autosuspend)
usb_enable_autosuspend(data->udev);
+ data->poll_sync = enable_poll_sync;
+
err = hci_register_dev(hdev);
if (err < 0)
goto out_free_dev;
@@ -4007,8 +4428,16 @@ static void btusb_disconnect(struct usb_interface *intf)
if (data->diag)
usb_set_intfdata(data->diag, NULL);
+ if (data->disconnect)
+ data->disconnect(hdev);
+
hci_unregister_dev(hdev);
+ if (data->oob_wake_irq)
+ device_init_wakeup(&data->udev->dev, false);
+ if (data->reset_gpio)
+ gpiod_put(data->reset_gpio);
+
if (intf == data->intf) {
if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
@@ -4019,17 +4448,11 @@ static void btusb_disconnect(struct usb_interface *intf)
usb_driver_release_interface(&btusb_driver, data->diag);
usb_driver_release_interface(&btusb_driver, data->intf);
} else if (intf == data->diag) {
- usb_driver_release_interface(&btusb_driver, data->intf);
if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
+ usb_driver_release_interface(&btusb_driver, data->intf);
}
- if (data->oob_wake_irq)
- device_init_wakeup(&data->udev->dev, false);
-
- if (data->reset_gpio)
- gpiod_put(data->reset_gpio);
-
hci_free_dev(hdev);
}
@@ -4040,6 +4463,12 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
BT_DBG("intf %p", intf);
+ /* Don't auto-suspend if there are connections; external suspend calls
+ * shall never fail.
+ */
+ if (PMSG_IS_AUTO(message) && hci_conn_count(data->hdev))
+ return -EBUSY;
+
if (data->suspend_count++)
return 0;
@@ -4055,6 +4484,9 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
cancel_work_sync(&data->work);
+ if (data->suspend)
+ data->suspend(data->hdev);
+
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
@@ -4158,6 +4590,9 @@ static int btusb_resume(struct usb_interface *intf)
btusb_submit_isoc_urb(hdev, GFP_NOIO);
}
+ if (data->resume)
+ data->resume(hdev);
+
spin_lock_irq(&data->txlock);
play_deferred(data);
clear_bit(BTUSB_SUSPENDING, &data->flags);
@@ -4177,6 +4612,17 @@ done:
}
#endif
+#ifdef CONFIG_DEV_COREDUMP
+static void btusb_coredump(struct device *dev)
+{
+ struct btusb_data *data = dev_get_drvdata(dev);
+ struct hci_dev *hdev = data->hdev;
+
+ if (hdev->dump.coredump)
+ hdev->dump.coredump(hdev);
+}
+#endif
+
static struct usb_driver btusb_driver = {
.name = "btusb",
.probe = btusb_probe,
@@ -4188,6 +4634,12 @@ static struct usb_driver btusb_driver = {
.id_table = btusb_table,
.supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1,
+
+#ifdef CONFIG_DEV_COREDUMP
+ .driver = {
+ .coredump = btusb_coredump,
+ },
+#endif
};
module_usb_driver(btusb_driver);
diff --git a/drivers/bluetooth/h4_recv.h b/drivers/bluetooth/h4_recv.h
deleted file mode 100644
index 4f2c89742245..000000000000
--- a/drivers/bluetooth/h4_recv.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- *
- * Generic Bluetooth HCI UART driver
- *
- * Copyright (C) 2015-2018 Intel Corporation
- */
-
-#include <asm/unaligned.h>
-
-struct h4_recv_pkt {
- u8 type; /* Packet type */
- u8 hlen; /* Header length */
- u8 loff; /* Data length offset in header */
- u8 lsize; /* Data length field size */
- u16 maxlen; /* Max overall packet length */
- int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
-};
-
-#define H4_RECV_ACL \
- .type = HCI_ACLDATA_PKT, \
- .hlen = HCI_ACL_HDR_SIZE, \
- .loff = 2, \
- .lsize = 2, \
- .maxlen = HCI_MAX_FRAME_SIZE \
-
-#define H4_RECV_SCO \
- .type = HCI_SCODATA_PKT, \
- .hlen = HCI_SCO_HDR_SIZE, \
- .loff = 2, \
- .lsize = 1, \
- .maxlen = HCI_MAX_SCO_SIZE
-
-#define H4_RECV_EVENT \
- .type = HCI_EVENT_PKT, \
- .hlen = HCI_EVENT_HDR_SIZE, \
- .loff = 1, \
- .lsize = 1, \
- .maxlen = HCI_MAX_EVENT_SIZE
-
-static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
- struct sk_buff *skb,
- const unsigned char *buffer,
- int count,
- const struct h4_recv_pkt *pkts,
- int pkts_count)
-{
- /* Check for error from previous call */
- if (IS_ERR(skb))
- skb = NULL;
-
- while (count) {
- int i, len;
-
- if (!skb) {
- for (i = 0; i < pkts_count; i++) {
- if (buffer[0] != (&pkts[i])->type)
- continue;
-
- skb = bt_skb_alloc((&pkts[i])->maxlen,
- GFP_ATOMIC);
- if (!skb)
- return ERR_PTR(-ENOMEM);
-
- hci_skb_pkt_type(skb) = (&pkts[i])->type;
- hci_skb_expect(skb) = (&pkts[i])->hlen;
- break;
- }
-
- /* Check for invalid packet type */
- if (!skb)
- return ERR_PTR(-EILSEQ);
-
- count -= 1;
- buffer += 1;
- }
-
- len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
- skb_put_data(skb, buffer, len);
-
- count -= len;
- buffer += len;
-
- /* Check for partial packet */
- if (skb->len < hci_skb_expect(skb))
- continue;
-
- for (i = 0; i < pkts_count; i++) {
- if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
- break;
- }
-
- if (i >= pkts_count) {
- kfree_skb(skb);
- return ERR_PTR(-EILSEQ);
- }
-
- if (skb->len == (&pkts[i])->hlen) {
- u16 dlen;
-
- switch ((&pkts[i])->lsize) {
- case 0:
- /* No variable data length */
- dlen = 0;
- break;
- case 1:
- /* Single octet variable length */
- dlen = skb->data[(&pkts[i])->loff];
- hci_skb_expect(skb) += dlen;
-
- if (skb_tailroom(skb) < dlen) {
- kfree_skb(skb);
- return ERR_PTR(-EMSGSIZE);
- }
- break;
- case 2:
- /* Double octet variable length */
- dlen = get_unaligned_le16(skb->data +
- (&pkts[i])->loff);
- hci_skb_expect(skb) += dlen;
-
- if (skb_tailroom(skb) < dlen) {
- kfree_skb(skb);
- return ERR_PTR(-EMSGSIZE);
- }
- break;
- default:
- /* Unsupported variable length */
- kfree_skb(skb);
- return ERR_PTR(-EILSEQ);
- }
-
- if (!dlen) {
- /* No more data, complete frame */
- (&pkts[i])->recv(hdev, skb);
- skb = NULL;
- }
- } else {
- /* Complete frame */
- (&pkts[i])->recv(hdev, skb);
- skb = NULL;
- }
- }
-
- return skb;
-}
diff --git a/drivers/bluetooth/hci_ag6xx.c b/drivers/bluetooth/hci_ag6xx.c
index 2d40302409ff..94588676510f 100644
--- a/drivers/bluetooth/hci_ag6xx.c
+++ b/drivers/bluetooth/hci_ag6xx.c
@@ -105,7 +105,7 @@ static int ag6xx_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- ag6xx->rx_skb = h4_recv_buf(hu->hdev, ag6xx->rx_skb, data, count,
+ ag6xx->rx_skb = h4_recv_buf(hu, ag6xx->rx_skb, data, count,
ag6xx_recv_pkts,
ARRAY_SIZE(ag6xx_recv_pkts));
if (IS_ERR(ag6xx->rx_skb)) {
diff --git a/drivers/bluetooth/hci_aml.c b/drivers/bluetooth/hci_aml.c
new file mode 100644
index 000000000000..b1f32c5a8a3f
--- /dev/null
+++ b/drivers/bluetooth/hci_aml.c
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/property.h>
+#include <linux/of.h>
+#include <linux/serdev.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include "hci_uart.h"
+
+#define AML_EVT_HEAD_SIZE 4
+#define AML_BDADDR_DEFAULT (&(bdaddr_t) {{ 0x00, 0xff, 0x00, 0x22, 0x2d, 0xae }})
+
+#define AML_FIRMWARE_OPERATION_SIZE (248)
+#define AML_FIRMWARE_MAX_SIZE (512 * 1024)
+
+/* TCI command */
+#define AML_TCI_CMD_READ 0xFEF0
+#define AML_TCI_CMD_WRITE 0xFEF1
+#define AML_TCI_CMD_UPDATE_BAUDRATE 0xFEF2
+#define AML_TCI_CMD_HARDWARE_RESET 0xFEF2
+#define AML_TCI_CMD_DOWNLOAD_BT_FW 0xFEF3
+
+/* Vendor command */
+#define AML_BT_HCI_VENDOR_CMD 0xFC1A
+
+/* TCI operation parameter in controller chip */
+#define AML_OP_UART_MODE 0x00A30128
+#define AML_OP_EVT_ENABLE 0x00A70014
+#define AML_OP_MEM_HARD_TRANS_EN 0x00A7000C
+#define AML_OP_RF_CFG 0x00F03040
+#define AML_OP_RAM_POWER_CTR 0x00F03050
+#define AML_OP_HARDWARE_RST 0x00F03058
+#define AML_OP_ICCM_RAM_BASE 0x00000000
+#define AML_OP_DCCM_RAM_BASE 0x00D00000
+
+/* UART configuration */
+#define AML_UART_XMIT_EN BIT(12)
+#define AML_UART_RECV_EN BIT(13)
+#define AML_UART_TIMEOUT_INT_EN BIT(14)
+#define AML_UART_CLK_SOURCE 40000000
+
+/* Controller event */
+#define AML_EVT_EN BIT(24)
+
+/* RAM power control */
+#define AML_RAM_POWER_ON (0)
+#define AML_RAM_POWER_OFF (1)
+
+/* RF configuration */
+#define AML_RF_ANT_SINGLE BIT(28)
+#define AML_RF_ANT_DOUBLE BIT(29)
+
+/* Memory transaction */
+#define AML_MM_CTR_HARD_TRAS_EN BIT(27)
+
+/* Controller reset */
+#define AML_CTR_CPU_RESET BIT(8)
+#define AML_CTR_MAC_RESET BIT(9)
+#define AML_CTR_PHY_RESET BIT(10)
+
+enum {
+ FW_ICCM,
+ FW_DCCM
+};
+
+struct aml_fw_len {
+ u32 iccm_len;
+ u32 dccm_len;
+};
+
+struct aml_tci_rsp {
+ u8 num_cmd_packet;
+ u16 opcode;
+ u8 status;
+} __packed;
+
+struct aml_device_data {
+ int iccm_offset;
+ int dccm_offset;
+ bool is_coex;
+};
+
+struct aml_serdev {
+ struct hci_uart serdev_hu;
+ struct device *dev;
+ struct gpio_desc *bt_en_gpio;
+ struct regulator *bt_supply;
+ struct clk *lpo_clk;
+ const struct aml_device_data *aml_dev_data;
+ const char *firmware_name;
+};
+
+struct aml_data {
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+};
+
+static const struct h4_recv_pkt aml_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+ { H4_RECV_ISO, .recv = hci_recv_frame },
+};
+
+/* The TCI command is a private command, which is for setting baud rate,
+ * downloading firmware, initiating RAM.
+ *
+ * op_code | op_len | op_addr | parameter |
+ * --------|-----------------------|---------|-------------|
+ * 2B | 1B len(addr+param) | 4B | len(param) |
+ */
+static int aml_send_tci_cmd(struct hci_dev *hdev, u16 op_code, u32 op_addr,
+ u32 *param, u32 param_len)
+{
+ struct aml_tci_rsp *rsp = NULL;
+ struct sk_buff *skb = NULL;
+ size_t buf_len = 0;
+ u8 *buf = NULL;
+ int err = 0;
+
+ buf_len = sizeof(op_addr) + param_len;
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, &op_addr, sizeof(op_addr));
+ if (param && param_len > 0)
+ memcpy(buf + sizeof(op_addr), param, param_len);
+
+ skb = __hci_cmd_sync_ev(hdev, op_code, buf_len, buf,
+ HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to send TCI cmd (error: %d)", err);
+ goto exit;
+ }
+
+ rsp = skb_pull_data(skb, sizeof(struct aml_tci_rsp));
+ if (!rsp)
+ goto skb_free;
+
+ if (rsp->opcode != op_code || rsp->status != 0x00) {
+ bt_dev_err(hdev, "send TCI cmd (0x%04X), response (0x%04X):(%d)",
+ op_code, rsp->opcode, rsp->status);
+ err = -EINVAL;
+ goto skb_free;
+ }
+
+skb_free:
+ kfree_skb(skb);
+
+exit:
+ kfree(buf);
+ return err;
+}
+
+static int aml_update_chip_baudrate(struct hci_dev *hdev, u32 baud)
+{
+ u32 value;
+
+ value = ((AML_UART_CLK_SOURCE / baud) - 1) & 0x0FFF;
+ value |= AML_UART_XMIT_EN | AML_UART_RECV_EN | AML_UART_TIMEOUT_INT_EN;
+
+ return aml_send_tci_cmd(hdev, AML_TCI_CMD_UPDATE_BAUDRATE,
+ AML_OP_UART_MODE, &value, sizeof(value));
+}
+
+static int aml_start_chip(struct hci_dev *hdev)
+{
+ u32 value = 0;
+ int ret;
+
+ value = AML_MM_CTR_HARD_TRAS_EN;
+ ret = aml_send_tci_cmd(hdev, AML_TCI_CMD_WRITE,
+ AML_OP_MEM_HARD_TRANS_EN,
+ &value, sizeof(value));
+ if (ret)
+ return ret;
+
+ /* controller hardware reset */
+ value = AML_CTR_CPU_RESET | AML_CTR_MAC_RESET | AML_CTR_PHY_RESET;
+ ret = aml_send_tci_cmd(hdev, AML_TCI_CMD_HARDWARE_RESET,
+ AML_OP_HARDWARE_RST,
+ &value, sizeof(value));
+ return ret;
+}
+
+static int aml_send_firmware_segment(struct hci_dev *hdev,
+ u8 fw_type,
+ u8 *seg,
+ u32 seg_size,
+ u32 offset)
+{
+ u32 op_addr = 0;
+
+ if (fw_type == FW_ICCM)
+ op_addr = AML_OP_ICCM_RAM_BASE + offset;
+ else if (fw_type == FW_DCCM)
+ op_addr = AML_OP_DCCM_RAM_BASE + offset;
+
+ return aml_send_tci_cmd(hdev, AML_TCI_CMD_DOWNLOAD_BT_FW,
+ op_addr, (u32 *)seg, seg_size);
+}
+
+static int aml_send_firmware(struct hci_dev *hdev, u8 fw_type,
+ u8 *fw, u32 fw_size, u32 offset)
+{
+ u32 seg_size = 0;
+ u32 seg_off = 0;
+
+ if (fw_size > AML_FIRMWARE_MAX_SIZE) {
+ bt_dev_err(hdev,
+ "Firmware size %d kB is larger than the maximum of 512 kB. Aborting.",
+ fw_size);
+ return -EINVAL;
+ }
+ while (fw_size > 0) {
+ seg_size = (fw_size > AML_FIRMWARE_OPERATION_SIZE) ?
+ AML_FIRMWARE_OPERATION_SIZE : fw_size;
+ if (aml_send_firmware_segment(hdev, fw_type, (fw + seg_off),
+ seg_size, offset)) {
+ bt_dev_err(hdev, "Failed send firmware, type: %d, offset: 0x%x",
+ fw_type, offset);
+ return -EINVAL;
+ }
+ seg_off += seg_size;
+ fw_size -= seg_size;
+ offset += seg_size;
+ }
+ return 0;
+}
+
+static int aml_download_firmware(struct hci_dev *hdev, const char *fw_name)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
+ const struct firmware *firmware = NULL;
+ struct aml_fw_len *fw_len = NULL;
+ u8 *iccm_start = NULL, *dccm_start = NULL;
+ u32 iccm_len, dccm_len;
+ u32 value = 0;
+ int ret = 0;
+
+ /* Enable firmware download event */
+ value = AML_EVT_EN;
+ ret = aml_send_tci_cmd(hdev, AML_TCI_CMD_WRITE,
+ AML_OP_EVT_ENABLE,
+ &value, sizeof(value));
+ if (ret)
+ goto exit;
+
+ /* RAM power on */
+ value = AML_RAM_POWER_ON;
+ ret = aml_send_tci_cmd(hdev, AML_TCI_CMD_WRITE,
+ AML_OP_RAM_POWER_CTR,
+ &value, sizeof(value));
+ if (ret)
+ goto exit;
+
+ /* Check RAM power status */
+ ret = aml_send_tci_cmd(hdev, AML_TCI_CMD_READ,
+ AML_OP_RAM_POWER_CTR, NULL, 0);
+ if (ret)
+ goto exit;
+
+ ret = request_firmware(&firmware, fw_name, &hdev->dev);
+ if (ret < 0) {
+ bt_dev_err(hdev, "Failed to load <%s>:(%d)", fw_name, ret);
+ goto exit;
+ }
+
+ fw_len = (struct aml_fw_len *)firmware->data;
+
+ /* Download ICCM */
+ iccm_start = (u8 *)(firmware->data) + sizeof(struct aml_fw_len)
+ + amldev->aml_dev_data->iccm_offset;
+ iccm_len = fw_len->iccm_len - amldev->aml_dev_data->iccm_offset;
+ ret = aml_send_firmware(hdev, FW_ICCM, iccm_start, iccm_len,
+ amldev->aml_dev_data->iccm_offset);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to send FW_ICCM (%d)", ret);
+ goto exit;
+ }
+
+ /* Download DCCM */
+ dccm_start = (u8 *)(firmware->data) + sizeof(struct aml_fw_len) + fw_len->iccm_len;
+ dccm_len = fw_len->dccm_len;
+ ret = aml_send_firmware(hdev, FW_DCCM, dccm_start, dccm_len,
+ amldev->aml_dev_data->dccm_offset);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to send FW_DCCM (%d)", ret);
+ goto exit;
+ }
+
+ /* Disable firmware download event */
+ value = 0;
+ ret = aml_send_tci_cmd(hdev, AML_TCI_CMD_WRITE,
+ AML_OP_EVT_ENABLE,
+ &value, sizeof(value));
+ if (ret)
+ goto exit;
+
+exit:
+ release_firmware(firmware);
+ return ret;
+}
+
+static int aml_send_reset(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = __hci_cmd_sync_ev(hdev, HCI_OP_RESET, 0, NULL,
+ HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to send hci reset cmd (%d)", err);
+ return err;
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int aml_dump_fw_version(struct hci_dev *hdev)
+{
+ struct aml_tci_rsp *rsp = NULL;
+ struct sk_buff *skb;
+ u8 value[6] = {0};
+ u8 *fw_ver = NULL;
+ int err = 0;
+
+ skb = __hci_cmd_sync_ev(hdev, AML_BT_HCI_VENDOR_CMD, sizeof(value), value,
+ HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to get fw version (error: %d)", err);
+ return err;
+ }
+
+ rsp = skb_pull_data(skb, sizeof(struct aml_tci_rsp));
+ if (!rsp)
+ goto exit;
+
+ if (rsp->opcode != AML_BT_HCI_VENDOR_CMD || rsp->status != 0x00) {
+ bt_dev_err(hdev, "dump version, error response (0x%04X):(%d)",
+ rsp->opcode, rsp->status);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ fw_ver = (u8 *)rsp + AML_EVT_HEAD_SIZE;
+ bt_dev_info(hdev, "fw_version: date = %02x.%02x, number = 0x%02x%02x",
+ *(fw_ver + 1), *fw_ver, *(fw_ver + 3), *(fw_ver + 2));
+
+exit:
+ kfree_skb(skb);
+ return err;
+}
+
+static int aml_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct aml_tci_rsp *rsp = NULL;
+ struct sk_buff *skb;
+ int err = 0;
+
+ bt_dev_info(hdev, "set bdaddr (%pM)", bdaddr);
+ skb = __hci_cmd_sync_ev(hdev, AML_BT_HCI_VENDOR_CMD,
+ sizeof(bdaddr_t), bdaddr,
+ HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to set bdaddr (error: %d)", err);
+ return err;
+ }
+
+ rsp = skb_pull_data(skb, sizeof(struct aml_tci_rsp));
+ if (!rsp)
+ goto exit;
+
+ if (rsp->opcode != AML_BT_HCI_VENDOR_CMD || rsp->status != 0x00) {
+ bt_dev_err(hdev, "error response (0x%x):(%d)", rsp->opcode, rsp->status);
+ err = -EINVAL;
+ goto exit;
+ }
+
+exit:
+ kfree_skb(skb);
+ return err;
+}
+
+static int aml_check_bdaddr(struct hci_dev *hdev)
+{
+ struct hci_rp_read_bd_addr *paddr;
+ struct sk_buff *skb;
+ int err;
+
+ if (bacmp(&hdev->public_addr, BDADDR_ANY))
+ return 0;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to read bdaddr (error: %d)", err);
+ return err;
+ }
+
+ paddr = skb_pull_data(skb, sizeof(struct hci_rp_read_bd_addr));
+ if (!paddr)
+ goto exit;
+
+ if (!bacmp(&paddr->bdaddr, AML_BDADDR_DEFAULT)) {
+ bt_dev_info(hdev, "amlbt using default bdaddr (%pM)", &paddr->bdaddr);
+ hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR);
+ }
+
+exit:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int aml_config_rf(struct hci_dev *hdev, bool is_coex)
+{
+ u32 value = AML_RF_ANT_DOUBLE;
+
+ /* Use a single antenna when co-existing with wifi */
+ if (is_coex)
+ value = AML_RF_ANT_SINGLE;
+
+ return aml_send_tci_cmd(hdev, AML_TCI_CMD_WRITE,
+ AML_OP_RF_CFG,
+ &value, sizeof(value));
+}
+
+static int aml_parse_dt(struct aml_serdev *amldev)
+{
+ struct device *pdev = amldev->dev;
+
+ amldev->bt_en_gpio = devm_gpiod_get(pdev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(amldev->bt_en_gpio)) {
+ dev_err(pdev, "Failed to acquire enable gpios");
+ return PTR_ERR(amldev->bt_en_gpio);
+ }
+
+ if (device_property_read_string(pdev, "firmware-name",
+ &amldev->firmware_name)) {
+ dev_err(pdev, "Failed to acquire firmware path");
+ return -ENODEV;
+ }
+
+ amldev->bt_supply = devm_regulator_get(pdev, "vddio");
+ if (IS_ERR(amldev->bt_supply)) {
+ dev_err(pdev, "Failed to acquire regulator");
+ return PTR_ERR(amldev->bt_supply);
+ }
+
+ amldev->lpo_clk = devm_clk_get(pdev, NULL);
+ if (IS_ERR(amldev->lpo_clk)) {
+ dev_err(pdev, "Failed to acquire clock source");
+ return PTR_ERR(amldev->lpo_clk);
+ }
+
+ return 0;
+}
+
+static int aml_power_on(struct aml_serdev *amldev)
+{
+ int err;
+
+ err = regulator_enable(amldev->bt_supply);
+ if (err) {
+ dev_err(amldev->dev, "Failed to enable regulator: (%d)", err);
+ return err;
+ }
+
+ err = clk_prepare_enable(amldev->lpo_clk);
+ if (err) {
+ dev_err(amldev->dev, "Failed to enable lpo clock: (%d)", err);
+ return err;
+ }
+
+ gpiod_set_value_cansleep(amldev->bt_en_gpio, 1);
+
+ /* Wait 20ms for bluetooth controller power on */
+ msleep(20);
+ return 0;
+}
+
+static int aml_power_off(struct aml_serdev *amldev)
+{
+ gpiod_set_value_cansleep(amldev->bt_en_gpio, 0);
+
+ clk_disable_unprepare(amldev->lpo_clk);
+
+ regulator_disable(amldev->bt_supply);
+
+ return 0;
+}
+
+static int aml_set_baudrate(struct hci_uart *hu, unsigned int speed)
+{
+ /* update controller baudrate */
+ if (aml_update_chip_baudrate(hu->hdev, speed) != 0) {
+ bt_dev_err(hu->hdev, "Failed to update baud rate");
+ return -EINVAL;
+ }
+
+ /* update local baudrate */
+ serdev_device_set_baudrate(hu->serdev, speed);
+
+ return 0;
+}
+
+/* Initialize protocol */
+static int aml_open(struct hci_uart *hu)
+{
+ struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
+ struct aml_data *aml_data;
+ int err;
+
+ err = aml_parse_dt(amldev);
+ if (err)
+ return err;
+
+ if (!hci_uart_has_flow_control(hu)) {
+ bt_dev_err(hu->hdev, "no flow control");
+ return -EOPNOTSUPP;
+ }
+
+ aml_data = kzalloc(sizeof(*aml_data), GFP_KERNEL);
+ if (!aml_data)
+ return -ENOMEM;
+
+ skb_queue_head_init(&aml_data->txq);
+
+ hu->priv = aml_data;
+
+ return 0;
+}
+
+static int aml_close(struct hci_uart *hu)
+{
+ struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
+ struct aml_data *aml_data = hu->priv;
+
+ skb_queue_purge(&aml_data->txq);
+ kfree_skb(aml_data->rx_skb);
+ kfree(aml_data);
+
+ hu->priv = NULL;
+
+ return aml_power_off(amldev);
+}
+
+static int aml_flush(struct hci_uart *hu)
+{
+ struct aml_data *aml_data = hu->priv;
+
+ skb_queue_purge(&aml_data->txq);
+
+ return 0;
+}
+
+static int aml_setup(struct hci_uart *hu)
+{
+ struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
+ struct hci_dev *hdev = amldev->serdev_hu.hdev;
+ int err;
+
+ /* Setup bdaddr */
+ hdev->set_bdaddr = aml_set_bdaddr;
+
+ err = aml_power_on(amldev);
+ if (err)
+ return err;
+
+ err = aml_set_baudrate(hu, amldev->serdev_hu.proto->oper_speed);
+ if (err)
+ return err;
+
+ err = aml_download_firmware(hdev, amldev->firmware_name);
+ if (err)
+ return err;
+
+ err = aml_config_rf(hdev, amldev->aml_dev_data->is_coex);
+ if (err)
+ return err;
+
+ err = aml_start_chip(hdev);
+ if (err)
+ return err;
+
+ /* Wait 60ms for controller startup */
+ msleep(60);
+
+ err = aml_dump_fw_version(hdev);
+ if (err)
+ return err;
+
+ err = aml_send_reset(hdev);
+ if (err)
+ return err;
+
+ err = aml_check_bdaddr(hdev);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int aml_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct aml_data *aml_data = hu->priv;
+
+ skb_queue_tail(&aml_data->txq, skb);
+
+ return 0;
+}
+
+static struct sk_buff *aml_dequeue(struct hci_uart *hu)
+{
+ struct aml_data *aml_data = hu->priv;
+ struct sk_buff *skb;
+
+ skb = skb_dequeue(&aml_data->txq);
+
+ /* Prepend skb with frame type */
+ if (skb)
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ return skb;
+}
+
+static int aml_recv(struct hci_uart *hu, const void *data, int count)
+{
+ struct aml_data *aml_data = hu->priv;
+ int err;
+
+ aml_data->rx_skb = h4_recv_buf(hu, aml_data->rx_skb, data, count,
+ aml_recv_pkts,
+ ARRAY_SIZE(aml_recv_pkts));
+ if (IS_ERR(aml_data->rx_skb)) {
+ err = PTR_ERR(aml_data->rx_skb);
+ bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
+ aml_data->rx_skb = NULL;
+ return err;
+ }
+
+ return count;
+}
+
+static const struct hci_uart_proto aml_hci_proto = {
+ .id = HCI_UART_AML,
+ .name = "AML",
+ .init_speed = 115200,
+ .oper_speed = 4000000,
+ .open = aml_open,
+ .close = aml_close,
+ .setup = aml_setup,
+ .flush = aml_flush,
+ .recv = aml_recv,
+ .enqueue = aml_enqueue,
+ .dequeue = aml_dequeue,
+};
+
+static void aml_device_driver_shutdown(struct device *dev)
+{
+ struct aml_serdev *amldev = dev_get_drvdata(dev);
+
+ aml_power_off(amldev);
+}
+
+static int aml_serdev_probe(struct serdev_device *serdev)
+{
+ struct aml_serdev *amldev;
+ int err;
+
+ amldev = devm_kzalloc(&serdev->dev, sizeof(*amldev), GFP_KERNEL);
+ if (!amldev)
+ return -ENOMEM;
+
+ amldev->serdev_hu.serdev = serdev;
+ amldev->dev = &serdev->dev;
+ serdev_device_set_drvdata(serdev, amldev);
+
+ err = hci_uart_register_device(&amldev->serdev_hu, &aml_hci_proto);
+ if (err)
+ return dev_err_probe(amldev->dev, err,
+ "Failed to register hci uart device");
+
+ amldev->aml_dev_data = device_get_match_data(&serdev->dev);
+
+ return 0;
+}
+
+static void aml_serdev_remove(struct serdev_device *serdev)
+{
+ struct aml_serdev *amldev = serdev_device_get_drvdata(serdev);
+
+ hci_uart_unregister_device(&amldev->serdev_hu);
+}
+
+static const struct aml_device_data data_w155s2 = {
+ .iccm_offset = 256 * 1024,
+};
+
+static const struct aml_device_data data_w265s2 = {
+ .iccm_offset = 384 * 1024,
+};
+
+static const struct of_device_id aml_bluetooth_of_match[] = {
+ { .compatible = "amlogic,w155s2-bt", .data = &data_w155s2 },
+ { .compatible = "amlogic,w265s2-bt", .data = &data_w265s2 },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, aml_bluetooth_of_match);
+
+static struct serdev_device_driver aml_serdev_driver = {
+ .probe = aml_serdev_probe,
+ .remove = aml_serdev_remove,
+ .driver = {
+ .name = "hci_uart_aml",
+ .of_match_table = aml_bluetooth_of_match,
+ .shutdown = aml_device_driver_shutdown,
+ },
+};
+
+int __init aml_init(void)
+{
+ serdev_device_driver_register(&aml_serdev_driver);
+
+ return hci_uart_register_proto(&aml_hci_proto);
+}
+
+int __exit aml_deinit(void)
+{
+ serdev_device_driver_unregister(&aml_serdev_driver);
+
+ return hci_uart_unregister_proto(&aml_hci_proto);
+}
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index dbfe34664633..8d2b5e7f0d6a 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -191,7 +191,7 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count)
{
struct ath_struct *ath = hu->priv;
- ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
+ ath->rx_skb = h4_recv_buf(hu, ath->rx_skb, data, count,
ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
if (IS_ERR(ath->rx_skb)) {
int err = PTR_ERR(ath->rx_skb);
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index d7e0b75db8a6..9286a5f40f55 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -53,12 +53,16 @@
* struct bcm_device_data - device specific data
* @no_early_set_baudrate: Disallow set baudrate before driver setup()
* @drive_rts_on_open: drive RTS signal on ->open() when platform requires it
+ * @no_uart_clock_set: UART clock set command for >3Mbps mode is unavailable
* @max_autobaud_speed: max baudrate supported by device in autobaud mode
+ * @max_speed: max baudrate supported
*/
struct bcm_device_data {
bool no_early_set_baudrate;
bool drive_rts_on_open;
+ bool no_uart_clock_set;
u32 max_autobaud_speed;
+ u32 max_speed;
};
/**
@@ -100,6 +104,7 @@ struct bcm_device_data {
* @is_suspended: whether flow control is currently disabled
* @no_early_set_baudrate: don't set_baudrate before setup()
* @drive_rts_on_open: drive RTS signal on ->open() when platform requires it
+ * @no_uart_clock_set: UART clock set command for >3Mbps mode is unavailable
* @pcm_int_params: keep the initial PCM configuration
* @use_autobaud_mode: start Bluetooth device in autobaud mode
* @max_autobaud_speed: max baudrate supported by device in autobaud mode
@@ -140,6 +145,7 @@ struct bcm_device {
#endif
bool no_early_set_baudrate;
bool drive_rts_on_open;
+ bool no_uart_clock_set;
bool use_autobaud_mode;
u8 pcm_int_params[5];
u32 max_autobaud_speed;
@@ -172,10 +178,11 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
{
struct hci_dev *hdev = hu->hdev;
+ struct bcm_data *bcm = hu->priv;
struct sk_buff *skb;
struct bcm_update_uart_baud_rate param;
- if (speed > 3000000) {
+ if (speed > 3000000 && !bcm->dev->no_uart_clock_set) {
struct bcm_write_uart_clock_setting clock;
clock.type = BCM_UART_CLOCK_48MHZ;
@@ -319,7 +326,6 @@ static irqreturn_t bcm_host_wake(int irq, void *data)
bt_dev_dbg(bdev, "Host wake IRQ");
pm_runtime_get(bdev->dev);
- pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
return IRQ_HANDLED;
@@ -636,7 +642,8 @@ static int bcm_setup(struct hci_uart *hu)
* Allow the bootloader to set a valid address through the
* device tree.
*/
- set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hu->hdev->quirks);
+ if (hci_test_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR))
+ hci_set_quirk(hu->hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
if (!bcm_request_irq(bcm))
err = bcm_setup_sleep(hu);
@@ -690,7 +697,7 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
+ bcm->rx_skb = h4_recv_buf(hu, bcm->rx_skb, data, count,
bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
if (IS_ERR(bcm->rx_skb)) {
int err = PTR_ERR(bcm->rx_skb);
@@ -702,7 +709,6 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
mutex_lock(&bcm_device_lock);
if (bcm->dev && bcm_device_exists(bcm->dev)) {
pm_runtime_get(bcm->dev->dev);
- pm_runtime_mark_last_busy(bcm->dev->dev);
pm_runtime_put_autosuspend(bcm->dev->dev);
}
mutex_unlock(&bcm_device_lock);
@@ -740,10 +746,8 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&bcm->txq);
- if (bdev) {
- pm_runtime_mark_last_busy(bdev->dev);
+ if (bdev)
pm_runtime_put_autosuspend(bdev->dev);
- }
mutex_unlock(&bcm_device_lock);
@@ -883,7 +887,7 @@ unlock:
#endif
/* Some firmware reports an IRQ which does not work (wrong pin in fw table?) */
-static struct gpiod_lookup_table asus_tf103c_irq_gpios = {
+static struct gpiod_lookup_table irq_on_int33fc02_pin17_gpios = {
.dev_id = "serial0-0",
.table = {
GPIO_LOOKUP("INT33FC:02", 17, "host-wakeup-alt", GPIO_ACTIVE_HIGH),
@@ -893,12 +897,31 @@ static struct gpiod_lookup_table asus_tf103c_irq_gpios = {
static const struct dmi_system_id bcm_broken_irq_dmi_table[] = {
{
+ .ident = "Acer Iconia One 7 B1-750",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"),
+ },
+ .driver_data = &irq_on_int33fc02_pin17_gpios,
+ },
+ {
.ident = "Asus TF103C",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"),
},
- .driver_data = &asus_tf103c_irq_gpios,
+ .driver_data = &irq_on_int33fc02_pin17_gpios,
+ },
+ {
+ .ident = "Lenovo Yoga Tablet 2 830F/L / 1050F/L",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
+ DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
+ /* Partial match on beginning of BIOS version */
+ DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
+ },
+ .driver_data = &irq_on_int33fc02_pin17_gpios,
},
{
.ident = "Meegopad T08",
@@ -1041,17 +1064,17 @@ static struct clk *bcm_get_txco(struct device *dev)
struct clk *clk;
/* New explicit name */
- clk = devm_clk_get(dev, "txco");
- if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
+ clk = devm_clk_get_optional(dev, "txco");
+ if (clk)
return clk;
/* Deprecated name */
- clk = devm_clk_get(dev, "extclk");
- if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
+ clk = devm_clk_get_optional(dev, "extclk");
+ if (clk)
return clk;
/* Original code used no name at all */
- return devm_clk_get(dev, NULL);
+ return devm_clk_get_optional(dev, NULL);
}
static int bcm_get_resources(struct bcm_device *dev)
@@ -1066,21 +1089,12 @@ static int bcm_get_resources(struct bcm_device *dev)
return 0;
dev->txco_clk = bcm_get_txco(dev->dev);
-
- /* Handle deferred probing */
- if (dev->txco_clk == ERR_PTR(-EPROBE_DEFER))
- return PTR_ERR(dev->txco_clk);
-
- /* Ignore all other errors as before */
if (IS_ERR(dev->txco_clk))
- dev->txco_clk = NULL;
-
- dev->lpo_clk = devm_clk_get(dev->dev, "lpo");
- if (dev->lpo_clk == ERR_PTR(-EPROBE_DEFER))
- return PTR_ERR(dev->lpo_clk);
+ return PTR_ERR(dev->txco_clk);
+ dev->lpo_clk = devm_clk_get_optional(dev->dev, "lpo");
if (IS_ERR(dev->lpo_clk))
- dev->lpo_clk = NULL;
+ return PTR_ERR(dev->lpo_clk);
/* Check if we accidentally fetched the lpo clock twice */
if (dev->lpo_clk && clk_is_match(dev->lpo_clk, dev->txco_clk)) {
@@ -1266,7 +1280,7 @@ static int bcm_probe(struct platform_device *pdev)
return 0;
}
-static int bcm_remove(struct platform_device *pdev)
+static void bcm_remove(struct platform_device *pdev)
{
struct bcm_device *dev = platform_get_drvdata(pdev);
@@ -1275,8 +1289,6 @@ static int bcm_remove(struct platform_device *pdev)
mutex_unlock(&bcm_device_lock);
dev_info(&pdev->dev, "%s device unregistered.\n", dev->name);
-
- return 0;
}
static const struct hci_uart_proto bcm_proto = {
@@ -1295,6 +1307,12 @@ static const struct hci_uart_proto bcm_proto = {
};
#ifdef CONFIG_ACPI
+
+/* bcm43430a0/a1 BT does not support 48MHz UART clock, limit to 2000000 baud */
+static struct bcm_device_data bcm43430_device_data = {
+ .max_speed = 2000000,
+};
+
static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E00" },
{ "BCM2E01" },
@@ -1409,19 +1427,19 @@ static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E71" },
{ "BCM2E72" },
{ "BCM2E73" },
- { "BCM2E74" },
- { "BCM2E75" },
+ { "BCM2E74", (long)&bcm43430_device_data },
+ { "BCM2E75", (long)&bcm43430_device_data },
{ "BCM2E76" },
{ "BCM2E77" },
{ "BCM2E78" },
{ "BCM2E79" },
{ "BCM2E7A" },
- { "BCM2E7B" },
+ { "BCM2E7B", (long)&bcm43430_device_data },
{ "BCM2E7C" },
{ "BCM2E7D" },
{ "BCM2E7E" },
{ "BCM2E7F" },
- { "BCM2E80" },
+ { "BCM2E80", (long)&bcm43430_device_data },
{ "BCM2E81" },
{ "BCM2E82" },
{ "BCM2E83" },
@@ -1430,7 +1448,7 @@ static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E86" },
{ "BCM2E87" },
{ "BCM2E88" },
- { "BCM2E89" },
+ { "BCM2E89", (long)&bcm43430_device_data },
{ "BCM2E8A" },
{ "BCM2E8B" },
{ "BCM2E8C" },
@@ -1439,29 +1457,30 @@ static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E90" },
{ "BCM2E92" },
{ "BCM2E93" },
- { "BCM2E94" },
+ { "BCM2E94", (long)&bcm43430_device_data },
{ "BCM2E95" },
{ "BCM2E96" },
{ "BCM2E97" },
{ "BCM2E98" },
- { "BCM2E99" },
+ { "BCM2E99", (long)&bcm43430_device_data },
{ "BCM2E9A" },
- { "BCM2E9B" },
+ { "BCM2E9B", (long)&bcm43430_device_data },
{ "BCM2E9C" },
{ "BCM2E9D" },
+ { "BCM2E9F", (long)&bcm43430_device_data },
{ "BCM2EA0" },
{ "BCM2EA1" },
- { "BCM2EA2" },
- { "BCM2EA3" },
- { "BCM2EA4" },
+ { "BCM2EA2", (long)&bcm43430_device_data },
+ { "BCM2EA3", (long)&bcm43430_device_data },
+ { "BCM2EA4", (long)&bcm43430_device_data }, /* bcm43455 */
{ "BCM2EA5" },
{ "BCM2EA6" },
{ "BCM2EA7" },
{ "BCM2EA8" },
{ "BCM2EA9" },
- { "BCM2EAA" },
- { "BCM2EAB" },
- { "BCM2EAC" },
+ { "BCM2EAA", (long)&bcm43430_device_data },
+ { "BCM2EAB", (long)&bcm43430_device_data },
+ { "BCM2EAC", (long)&bcm43430_device_data },
{ },
};
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
@@ -1529,6 +1548,9 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
bcmdev->max_autobaud_speed = data->max_autobaud_speed;
bcmdev->no_early_set_baudrate = data->no_early_set_baudrate;
bcmdev->drive_rts_on_open = data->drive_rts_on_open;
+ bcmdev->no_uart_clock_set = data->no_uart_clock_set;
+ if (data->max_speed && bcmdev->oper_speed > data->max_speed)
+ bcmdev->oper_speed = data->max_speed;
}
return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto);
@@ -1550,6 +1572,10 @@ static struct bcm_device_data bcm43438_device_data = {
.drive_rts_on_open = true,
};
+static struct bcm_device_data cyw4373a0_device_data = {
+ .no_uart_clock_set = true,
+};
+
static struct bcm_device_data cyw55572_device_data = {
.max_autobaud_speed = 921600,
};
@@ -1566,6 +1592,7 @@ static const struct of_device_id bcm_bluetooth_of_match[] = {
{ .compatible = "brcm,bcm4349-bt", .data = &bcm43438_device_data },
{ .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data },
{ .compatible = "brcm,bcm4335a0" },
+ { .compatible = "cypress,cyw4373a0-bt", .data = &cyw4373a0_device_data },
{ .compatible = "infineon,cyw55572-bt", .data = &cyw55572_device_data },
{ },
};
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
new file mode 100644
index 000000000000..45e6d84224ee
--- /dev/null
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -0,0 +1,2559 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Bluetooth HCI driver for Broadcom 4377/4378/4387/4388 devices attached via PCIe
+ *
+ * Copyright (C) The Asahi Linux Contributors
+ */
+
+#include <linux/async.h>
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/printk.h>
+
+#include <linux/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+enum bcm4377_chip {
+ BCM4377 = 0,
+ BCM4378,
+ BCM4387,
+ BCM4388,
+};
+
+#define BCM4377_DEVICE_ID 0x5fa0
+#define BCM4378_DEVICE_ID 0x5f69
+#define BCM4387_DEVICE_ID 0x5f71
+#define BCM4388_DEVICE_ID 0x5f72
+
+#define BCM4377_TIMEOUT msecs_to_jiffies(1000)
+#define BCM4377_BOOT_TIMEOUT msecs_to_jiffies(5000)
+
+/*
+ * These devices only support DMA transactions inside a 32bit window
+ * (possibly to avoid 64 bit arithmetic). The window size cannot exceed
+ * 0xffffffff but is always aligned down to the previous 0x200 byte boundary
+ * which effectively limits the window to [start, start+0xfffffe00].
+ * We just limit the DMA window to [0, 0xfffffe00] to make sure we don't
+ * run into this limitation.
+ */
+#define BCM4377_DMA_MASK 0xfffffe00
+
+#define BCM4377_PCIECFG_BAR0_WINDOW1 0x80
+#define BCM4377_PCIECFG_BAR0_WINDOW2 0x70
+#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW1 0x74
+#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW2 0x78
+#define BCM4377_PCIECFG_BAR2_WINDOW 0x84
+
+#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW1_DEFAULT 0x18011000
+#define BCM4377_PCIECFG_BAR2_WINDOW_DEFAULT 0x19000000
+
+#define BCM4377_PCIECFG_SUBSYSTEM_CTRL 0x88
+
+#define BCM4377_BAR0_FW_DOORBELL 0x140
+#define BCM4377_BAR0_RTI_CONTROL 0x144
+
+#define BCM4377_BAR0_SLEEP_CONTROL 0x150
+#define BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE 0
+#define BCM4377_BAR0_SLEEP_CONTROL_AWAKE 2
+#define BCM4377_BAR0_SLEEP_CONTROL_QUIESCE 3
+
+#define BCM4377_BAR0_DOORBELL 0x174
+#define BCM4377_BAR0_DOORBELL_VALUE GENMASK(31, 16)
+#define BCM4377_BAR0_DOORBELL_IDX GENMASK(15, 8)
+#define BCM4377_BAR0_DOORBELL_RING BIT(5)
+
+#define BCM4377_BAR0_HOST_WINDOW_LO 0x590
+#define BCM4377_BAR0_HOST_WINDOW_HI 0x594
+#define BCM4377_BAR0_HOST_WINDOW_SIZE 0x598
+
+#define BCM4377_BAR2_BOOTSTAGE 0x200454
+
+#define BCM4377_BAR2_FW_LO 0x200478
+#define BCM4377_BAR2_FW_HI 0x20047c
+#define BCM4377_BAR2_FW_SIZE 0x200480
+
+#define BCM4377_BAR2_CONTEXT_ADDR_LO 0x20048c
+#define BCM4377_BAR2_CONTEXT_ADDR_HI 0x200450
+
+#define BCM4377_BAR2_RTI_STATUS 0x20045c
+#define BCM4377_BAR2_RTI_WINDOW_LO 0x200494
+#define BCM4377_BAR2_RTI_WINDOW_HI 0x200498
+#define BCM4377_BAR2_RTI_WINDOW_SIZE 0x20049c
+
+#define BCM4377_OTP_SIZE 0xe0
+#define BCM4377_OTP_SYS_VENDOR 0x15
+#define BCM4377_OTP_CIS 0x80
+#define BCM4377_OTP_VENDOR_HDR 0x00000008
+#define BCM4377_OTP_MAX_PARAM_LEN 16
+
+#define BCM4377_N_TRANSFER_RINGS 9
+#define BCM4377_N_COMPLETION_RINGS 6
+
+#define BCM4377_MAX_RING_SIZE 256
+
+#define BCM4377_MSGID_GENERATION GENMASK(15, 8)
+#define BCM4377_MSGID_ID GENMASK(7, 0)
+
+#define BCM4377_RING_N_ENTRIES 128
+
+#define BCM4377_CONTROL_MSG_SIZE 0x34
+#define BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE (4 * 0xff)
+
+#define MAX_ACL_PAYLOAD_SIZE (HCI_MAX_FRAME_SIZE + HCI_ACL_HDR_SIZE)
+#define MAX_SCO_PAYLOAD_SIZE (HCI_MAX_SCO_SIZE + HCI_SCO_HDR_SIZE)
+#define MAX_EVENT_PAYLOAD_SIZE (HCI_MAX_EVENT_SIZE + HCI_EVENT_HDR_SIZE)
+
+enum bcm4377_otp_params_type {
+ BCM4377_OTP_BOARD_PARAMS,
+ BCM4377_OTP_CHIP_PARAMS
+};
+
+enum bcm4377_transfer_ring_id {
+ BCM4377_XFER_RING_CONTROL = 0,
+ BCM4377_XFER_RING_HCI_H2D = 1,
+ BCM4377_XFER_RING_HCI_D2H = 2,
+ BCM4377_XFER_RING_SCO_H2D = 3,
+ BCM4377_XFER_RING_SCO_D2H = 4,
+ BCM4377_XFER_RING_ACL_H2D = 5,
+ BCM4377_XFER_RING_ACL_D2H = 6,
+};
+
+enum bcm4377_completion_ring_id {
+ BCM4377_ACK_RING_CONTROL = 0,
+ BCM4377_ACK_RING_HCI_ACL = 1,
+ BCM4377_EVENT_RING_HCI_ACL = 2,
+ BCM4377_ACK_RING_SCO = 3,
+ BCM4377_EVENT_RING_SCO = 4,
+};
+
+enum bcm4377_doorbell {
+ BCM4377_DOORBELL_CONTROL = 0,
+ BCM4377_DOORBELL_HCI_H2D = 1,
+ BCM4377_DOORBELL_HCI_D2H = 2,
+ BCM4377_DOORBELL_ACL_H2D = 3,
+ BCM4377_DOORBELL_ACL_D2H = 4,
+ BCM4377_DOORBELL_SCO = 6,
+};
+
+/*
+ * Transfer ring entry
+ *
+ * flags: Flags to indicate if the payload is appended or mapped
+ * len: Payload length
+ * payload: Optional payload DMA address
+ * id: Message id to recognize the answer in the completion ring entry
+ */
+struct bcm4377_xfer_ring_entry {
+#define BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED BIT(0)
+#define BCM4377_XFER_RING_FLAG_PAYLOAD_IN_FOOTER BIT(1)
+ u8 flags;
+ __le16 len;
+ u8 _unk0;
+ __le64 payload;
+ __le16 id;
+ u8 _unk1[2];
+} __packed;
+static_assert(sizeof(struct bcm4377_xfer_ring_entry) == 0x10);
+
+/*
+ * Completion ring entry
+ *
+ * flags: Flags to indicate if the payload is appended or mapped. If the payload
+ * is mapped it can be found in the buffer of the corresponding transfer
+ * ring message.
+ * ring_id: Transfer ring ID which required this message
+ * msg_id: Message ID specified in transfer ring entry
+ * len: Payload length
+ */
+struct bcm4377_completion_ring_entry {
+ u8 flags;
+ u8 _unk0;
+ __le16 ring_id;
+ __le16 msg_id;
+ __le32 len;
+ u8 _unk1[6];
+} __packed;
+static_assert(sizeof(struct bcm4377_completion_ring_entry) == 0x10);
+
+enum bcm4377_control_message_type {
+ BCM4377_CONTROL_MSG_CREATE_XFER_RING = 1,
+ BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING = 2,
+ BCM4377_CONTROL_MSG_DESTROY_XFER_RING = 3,
+ BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING = 4,
+};
+
+/*
+ * Control message used to create a completion ring
+ *
+ * msg_type: Must be BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING
+ * header_size: Unknown, but probably reserved space in front of the entry
+ * footer_size: Number of 32 bit words reserved for payloads after the entry
+ * id/id_again: Completion ring index
+ * ring_iova: DMA address of the ring buffer
+ * n_elements: Number of elements inside the ring buffer
+ * msi: MSI index, doesn't work for all rings though and should be zero
+ * intmod_delay: Unknown delay
+ * intmod_bytes: Unknown
+ */
+struct bcm4377_create_completion_ring_msg {
+ u8 msg_type;
+ u8 header_size;
+ u8 footer_size;
+ u8 _unk0;
+ __le16 id;
+ __le16 id_again;
+ __le64 ring_iova;
+ __le16 n_elements;
+ __le32 unk;
+ u8 _unk1[6];
+ __le16 msi;
+ __le16 intmod_delay;
+ __le32 intmod_bytes;
+ __le16 _unk2;
+ __le32 _unk3;
+ u8 _unk4[10];
+} __packed;
+static_assert(sizeof(struct bcm4377_create_completion_ring_msg) ==
+ BCM4377_CONTROL_MSG_SIZE);
+
+/*
+ * Control ring message used to destroy a completion ring
+ *
+ * msg_type: Must be BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING
+ * ring_id: Completion ring to be destroyed
+ */
+struct bcm4377_destroy_completion_ring_msg {
+ u8 msg_type;
+ u8 _pad0;
+ __le16 ring_id;
+ u8 _pad1[48];
+} __packed;
+static_assert(sizeof(struct bcm4377_destroy_completion_ring_msg) ==
+ BCM4377_CONTROL_MSG_SIZE);
+
+/*
+ * Control message used to create a transfer ring
+ *
+ * msg_type: Must be BCM4377_CONTROL_MSG_CREATE_XFER_RING
+ * header_size: Number of 32 bit words reserved for unknown content before the
+ * entry
+ * footer_size: Number of 32 bit words reserved for payloads after the entry
+ * ring_id/ring_id_again: Transfer ring index
+ * ring_iova: DMA address of the ring buffer
+ * n_elements: Number of elements inside the ring buffer
+ * completion_ring_id: Completion ring index for acknowledgements and events
+ * doorbell: Doorbell index used to notify device of new entries
+ * flags: Transfer ring flags
+ * - virtual: set if there is no associated shared memory and only the
+ * corresponding completion ring is used
+ * - sync: only set for the SCO rings
+ */
+struct bcm4377_create_transfer_ring_msg {
+ u8 msg_type;
+ u8 header_size;
+ u8 footer_size;
+ u8 _unk0;
+ __le16 ring_id;
+ __le16 ring_id_again;
+ __le64 ring_iova;
+ u8 _unk1[8];
+ __le16 n_elements;
+ __le16 completion_ring_id;
+ __le16 doorbell;
+#define BCM4377_XFER_RING_FLAG_VIRTUAL BIT(7)
+#define BCM4377_XFER_RING_FLAG_SYNC BIT(8)
+ __le16 flags;
+ u8 _unk2[20];
+} __packed;
+static_assert(sizeof(struct bcm4377_create_transfer_ring_msg) ==
+ BCM4377_CONTROL_MSG_SIZE);
+
+/*
+ * Control ring message used to destroy a transfer ring
+ *
+ * msg_type: Must be BCM4377_CONTROL_MSG_DESTROY_XFER_RING
+ * ring_id: Transfer ring to be destroyed
+ */
+struct bcm4377_destroy_transfer_ring_msg {
+ u8 msg_type;
+ u8 _pad0;
+ __le16 ring_id;
+ u8 _pad1[48];
+} __packed;
+static_assert(sizeof(struct bcm4377_destroy_transfer_ring_msg) ==
+ BCM4377_CONTROL_MSG_SIZE);
+
+/*
+ * "Converged IPC" context struct used to make the device aware of all other
+ * shared memory structures. A pointer to this structure is configured inside a
+ * MMIO register.
+ *
+ * version: Protocol version, must be 2.
+ * size: Size of this structure, must be 0x68.
+ * enabled_caps: Enabled capabilities. Unknown bitfield but should be 2.
+ * peripheral_info_addr: DMA address for a 0x20 buffer to which the device will
+ * write unknown contents
+ * {completion,xfer}_ring_{tails,heads}_addr: DMA pointers to ring heads/tails
+ * n_completion_rings: Number of completion rings, the firmware only works if
+ * this is set to BCM4377_N_COMPLETION_RINGS.
+ * n_xfer_rings: Number of transfer rings, the firmware only works if
+ * this is set to BCM4377_N_TRANSFER_RINGS.
+ * control_completion_ring_addr: Control completion ring buffer DMA address
+ * control_xfer_ring_addr: Control transfer ring buffer DMA address
+ * control_xfer_ring_n_entries: Number of control transfer ring entries
+ * control_completion_ring_n_entries: Number of control completion ring entries
+ * control_xfer_ring_doorbell: Control transfer ring doorbell
+ * control_completion_ring_doorbell: Control completion ring doorbell,
+ * must be set to 0xffff
+ * control_xfer_ring_msi: Control completion ring MSI index, must be 0
+ * control_completion_ring_msi: Control completion ring MSI index, must be 0.
+ * control_xfer_ring_header_size: Number of 32 bit words reserved in front of
+ * every control transfer ring entry
+ * control_xfer_ring_footer_size: Number of 32 bit words reserved after every
+ * control transfer ring entry
+ * control_completion_ring_header_size: Number of 32 bit words reserved in front
+ * of every control completion ring entry
+ * control_completion_ring_footer_size: Number of 32 bit words reserved after
+ * every control completion ring entry
+ * scratch_pad: Optional scratch pad DMA address
+ * scratch_pad_size: Scratch pad size
+ */
+struct bcm4377_context {
+ __le16 version;
+ __le16 size;
+ __le32 enabled_caps;
+
+ __le64 peripheral_info_addr;
+
+ /* ring heads and tails */
+ __le64 completion_ring_heads_addr;
+ __le64 xfer_ring_tails_addr;
+ __le64 completion_ring_tails_addr;
+ __le64 xfer_ring_heads_addr;
+ __le16 n_completion_rings;
+ __le16 n_xfer_rings;
+
+ /* control ring configuration */
+ __le64 control_completion_ring_addr;
+ __le64 control_xfer_ring_addr;
+ __le16 control_xfer_ring_n_entries;
+ __le16 control_completion_ring_n_entries;
+ __le16 control_xfer_ring_doorbell;
+ __le16 control_completion_ring_doorbell;
+ __le16 control_xfer_ring_msi;
+ __le16 control_completion_ring_msi;
+ u8 control_xfer_ring_header_size;
+ u8 control_xfer_ring_footer_size;
+ u8 control_completion_ring_header_size;
+ u8 control_completion_ring_footer_size;
+
+ __le16 _unk0;
+ __le16 _unk1;
+
+ __le64 scratch_pad;
+ __le32 scratch_pad_size;
+
+ __le32 _unk3;
+} __packed;
+static_assert(sizeof(struct bcm4377_context) == 0x68);
+
+#define BCM4378_CALIBRATION_CHUNK_SIZE 0xe6
+struct bcm4378_hci_send_calibration_cmd {
+ u8 unk;
+ __le16 blocks_left;
+ u8 data[BCM4378_CALIBRATION_CHUNK_SIZE];
+} __packed;
+
+#define BCM4378_PTB_CHUNK_SIZE 0xcf
+struct bcm4378_hci_send_ptb_cmd {
+ __le16 blocks_left;
+ u8 data[BCM4378_PTB_CHUNK_SIZE];
+} __packed;
+
+/*
+ * Shared memory structure used to store the ring head and tail pointers.
+ */
+struct bcm4377_ring_state {
+ __le16 completion_ring_head[BCM4377_N_COMPLETION_RINGS];
+ __le16 completion_ring_tail[BCM4377_N_COMPLETION_RINGS];
+ __le16 xfer_ring_head[BCM4377_N_TRANSFER_RINGS];
+ __le16 xfer_ring_tail[BCM4377_N_TRANSFER_RINGS];
+};
+
+/*
+ * A transfer ring can be used in two configurations:
+ * 1) Send control or HCI messages to the device which are then acknowledged
+ * in the corresponding completion ring
+ * 2) Receiving HCI frames from the devices. In this case the transfer ring
+ * itself contains empty messages that are acknowledged once data is
+ * available from the device. If the payloads fit inside the footers
+ * of the completion ring the transfer ring can be configured to be
+ * virtual such that it has no ring buffer.
+ *
+ * ring_id: ring index hardcoded in the firmware
+ * doorbell: doorbell index to notify device of new entries
+ * payload_size: optional in-place payload size
+ * mapped_payload_size: optional out-of-place payload size
+ * completion_ring: index of corresponding completion ring
+ * n_entries: number of entries inside this ring
+ * generation: ring generation; incremented on hci_open to detect stale messages
+ * sync: set to true for SCO rings
+ * virtual: set to true if this ring has no entries and is just required to
+ * setup a corresponding completion ring for device->host messages
+ * d2h_buffers_only: set to true if this ring is only used to provide large
+ * buffers used by device->host messages in the completion
+ * ring
+ * allow_wait: allow to wait for messages to be acknowledged
+ * enabled: true once the ring has been created and can be used
+ * ring: ring buffer for entries (struct bcm4377_xfer_ring_entry)
+ * ring_dma: DMA address for ring entry buffer
+ * payloads: payload buffer for mapped_payload_size payloads
+ * payloads_dma:DMA address for payload buffer
+ * events: pointer to array of completions if waiting is allowed
+ * msgids: bitmap to keep track of used message ids
+ * lock: Spinlock to protect access to ring structures used in the irq handler
+ */
+struct bcm4377_transfer_ring {
+ enum bcm4377_transfer_ring_id ring_id;
+ enum bcm4377_doorbell doorbell;
+ size_t payload_size;
+ size_t mapped_payload_size;
+ u8 completion_ring;
+ u16 n_entries;
+ u8 generation;
+
+ bool sync;
+ bool virtual;
+ bool d2h_buffers_only;
+ bool allow_wait;
+ bool enabled;
+
+ void *ring;
+ dma_addr_t ring_dma;
+
+ void *payloads;
+ dma_addr_t payloads_dma;
+
+ struct completion **events;
+ DECLARE_BITMAP(msgids, BCM4377_MAX_RING_SIZE);
+ spinlock_t lock;
+};
+
+/*
+ * A completion ring can be either used to either acknowledge messages sent in
+ * the corresponding transfer ring or to receive messages associated with the
+ * transfer ring. When used to receive messages the transfer ring either
+ * has no ring buffer and is only advanced ("virtual transfer ring") or it
+ * only contains empty DMA buffers to be used for the payloads.
+ *
+ * ring_id: completion ring id, hardcoded in firmware
+ * payload_size: optional payload size after each entry
+ * delay: unknown delay
+ * n_entries: number of entries in this ring
+ * enabled: true once the ring has been created and can be used
+ * ring: ring buffer for entries (struct bcm4377_completion_ring_entry)
+ * ring_dma: DMA address of ring buffer
+ * transfer_rings: bitmap of corresponding transfer ring ids
+ */
+struct bcm4377_completion_ring {
+ enum bcm4377_completion_ring_id ring_id;
+ u16 payload_size;
+ u16 delay;
+ u16 n_entries;
+ bool enabled;
+
+ void *ring;
+ dma_addr_t ring_dma;
+
+ unsigned long transfer_rings;
+};
+
+struct bcm4377_data;
+
+/*
+ * Chip-specific configuration struct
+ *
+ * id: Chip id (e.g. 0x4377 for BCM4377)
+ * otp_offset: Offset to the start of the OTP inside BAR0
+ * bar0_window1: Backplane address mapped to the first window in BAR0
+ * bar0_window2: Backplane address mapped to the second window in BAR0
+ * bar0_core2_window2: Optional backplane address mapped to the second core's
+ * second window in BAR0
+ * has_bar0_core2_window2: Set to true if this chip requires the second core's
+ * second window to be configured
+ * bar2_offset: Offset to the start of the variables in BAR2
+ * clear_pciecfg_subsystem_ctrl_bit19: Set to true if bit 19 in the
+ * vendor-specific subsystem control
+ * register has to be cleared
+ * disable_aspm: Set to true if ASPM must be disabled due to hardware errata
+ * broken_ext_scan: Set to true if the chip erroneously claims to support
+ * extended scanning
+ * broken_mws_transport_config: Set to true if the chip erroneously claims to
+ * support MWS Transport Configuration
+ * broken_le_ext_adv_report_phy: Set to true if this chip stuffs flags inside
+ * reserved bits of Primary/Secondary_PHY inside
+ * LE Extended Advertising Report events which
+ * have to be ignored
+ * send_calibration: Optional callback to send calibration data
+ * send_ptb: Callback to send "PTB" regulatory/calibration data
+ */
+struct bcm4377_hw {
+ unsigned int id;
+
+ u32 otp_offset;
+
+ u32 bar0_window1;
+ u32 bar0_window2;
+ u32 bar0_core2_window2;
+ u32 bar2_offset;
+
+ unsigned long has_bar0_core2_window2 : 1;
+ unsigned long clear_pciecfg_subsystem_ctrl_bit19 : 1;
+ unsigned long disable_aspm : 1;
+ unsigned long broken_ext_scan : 1;
+ unsigned long broken_mws_transport_config : 1;
+ unsigned long broken_le_coded : 1;
+ unsigned long broken_le_ext_adv_report_phy : 1;
+
+ int (*send_calibration)(struct bcm4377_data *bcm4377);
+ int (*send_ptb)(struct bcm4377_data *bcm4377,
+ const struct firmware *fw);
+};
+
+static const struct bcm4377_hw bcm4377_hw_variants[];
+static const struct dmi_system_id bcm4377_dmi_board_table[];
+
+/*
+ * Private struct associated with each device containing global state
+ *
+ * pdev: Pointer to associated struct pci_dev
+ * hdev: Pointer to associated strucy hci_dev
+ * bar0: iomem pointing to BAR0
+ * bar1: iomem pointing to BAR2
+ * bootstage: Current value of the bootstage
+ * rti_status: Current "RTI" status value
+ * hw: Pointer to chip-specific struct bcm4377_hw
+ * taurus_cal_blob: "Taurus" calibration blob used for some chips
+ * taurus_cal_size: "Taurus" calibration blob size
+ * taurus_beamforming_cal_blob: "Taurus" beamforming calibration blob used for
+ * some chips
+ * taurus_beamforming_cal_size: "Taurus" beamforming calibration blob size
+ * stepping: Chip stepping read from OTP; used for firmware selection
+ * vendor: Antenna vendor read from OTP; used for firmware selection
+ * board_type: Board type from FDT or DMI match; used for firmware selection
+ * event: Event for changed bootstage or rti_status; used for booting firmware
+ * ctx: "Converged IPC" context
+ * ctx_dma: "Converged IPC" context DMA address
+ * ring_state: Shared memory buffer containing ring head and tail indexes
+ * ring_state_dma: DMA address for ring_state
+ * {control,hci_acl,sco}_ack_ring: Completion rings used to acknowledge messages
+ * {hci_acl,sco}_event_ring: Completion rings used for device->host messages
+ * control_h2d_ring: Transfer ring used for control messages
+ * {hci,sco,acl}_h2d_ring: Transfer ring used to transfer HCI frames
+ * {hci,sco,acl}_d2h_ring: Transfer ring used to receive HCI frames in the
+ * corresponding completion ring
+ */
+struct bcm4377_data {
+ struct pci_dev *pdev;
+ struct hci_dev *hdev;
+
+ void __iomem *bar0;
+ void __iomem *bar2;
+
+ u32 bootstage;
+ u32 rti_status;
+
+ const struct bcm4377_hw *hw;
+
+ const void *taurus_cal_blob;
+ int taurus_cal_size;
+ const void *taurus_beamforming_cal_blob;
+ int taurus_beamforming_cal_size;
+
+ char stepping[BCM4377_OTP_MAX_PARAM_LEN];
+ char vendor[BCM4377_OTP_MAX_PARAM_LEN];
+ const char *board_type;
+
+ struct completion event;
+
+ struct bcm4377_context *ctx;
+ dma_addr_t ctx_dma;
+
+ struct bcm4377_ring_state *ring_state;
+ dma_addr_t ring_state_dma;
+
+ /*
+ * The HCI and ACL rings have to be merged because this structure is
+ * hardcoded in the firmware.
+ */
+ struct bcm4377_completion_ring control_ack_ring;
+ struct bcm4377_completion_ring hci_acl_ack_ring;
+ struct bcm4377_completion_ring hci_acl_event_ring;
+ struct bcm4377_completion_ring sco_ack_ring;
+ struct bcm4377_completion_ring sco_event_ring;
+
+ struct bcm4377_transfer_ring control_h2d_ring;
+ struct bcm4377_transfer_ring hci_h2d_ring;
+ struct bcm4377_transfer_ring hci_d2h_ring;
+ struct bcm4377_transfer_ring sco_h2d_ring;
+ struct bcm4377_transfer_ring sco_d2h_ring;
+ struct bcm4377_transfer_ring acl_h2d_ring;
+ struct bcm4377_transfer_ring acl_d2h_ring;
+};
+
+static void bcm4377_ring_doorbell(struct bcm4377_data *bcm4377, u8 doorbell,
+ u16 val)
+{
+ u32 db = 0;
+
+ db |= FIELD_PREP(BCM4377_BAR0_DOORBELL_VALUE, val);
+ db |= FIELD_PREP(BCM4377_BAR0_DOORBELL_IDX, doorbell);
+ db |= BCM4377_BAR0_DOORBELL_RING;
+
+ dev_dbg(&bcm4377->pdev->dev, "write %d to doorbell #%d (0x%x)\n", val,
+ doorbell, db);
+ iowrite32(db, bcm4377->bar0 + BCM4377_BAR0_DOORBELL);
+}
+
+static int bcm4377_extract_msgid(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring,
+ u16 raw_msgid, u8 *msgid)
+{
+ u8 generation = FIELD_GET(BCM4377_MSGID_GENERATION, raw_msgid);
+ *msgid = FIELD_GET(BCM4377_MSGID_ID, raw_msgid);
+
+ if (generation != ring->generation) {
+ dev_warn(
+ &bcm4377->pdev->dev,
+ "invalid message generation %d should be %d in entry for ring %d\n",
+ generation, ring->generation, ring->ring_id);
+ return -EINVAL;
+ }
+
+ if (*msgid >= ring->n_entries) {
+ dev_warn(&bcm4377->pdev->dev,
+ "invalid message id in entry for ring %d: %d > %d\n",
+ ring->ring_id, *msgid, ring->n_entries);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void bcm4377_handle_event(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring,
+ u16 raw_msgid, u8 entry_flags, u8 type,
+ void *payload, size_t len)
+{
+ struct sk_buff *skb;
+ u16 head;
+ u8 msgid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ if (!ring->enabled) {
+ dev_warn(&bcm4377->pdev->dev,
+ "event for disabled transfer ring %d\n",
+ ring->ring_id);
+ goto out;
+ }
+
+ if (ring->d2h_buffers_only &&
+ entry_flags & BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED) {
+ if (bcm4377_extract_msgid(bcm4377, ring, raw_msgid, &msgid))
+ goto out;
+
+ if (len > ring->mapped_payload_size) {
+ dev_warn(
+ &bcm4377->pdev->dev,
+ "invalid payload len in event for ring %d: %zu > %zu\n",
+ ring->ring_id, len, ring->mapped_payload_size);
+ goto out;
+ }
+
+ payload = ring->payloads + msgid * ring->mapped_payload_size;
+ }
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ memcpy(skb_put(skb, len), payload, len);
+ hci_skb_pkt_type(skb) = type;
+ hci_recv_frame(bcm4377->hdev, skb);
+
+out:
+ head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]);
+ head = (head + 1) % ring->n_entries;
+ bcm4377->ring_state->xfer_ring_head[ring->ring_id] = cpu_to_le16(head);
+
+ bcm4377_ring_doorbell(bcm4377, ring->doorbell, head);
+
+ spin_unlock_irqrestore(&ring->lock, flags);
+}
+
+static void bcm4377_handle_ack(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring,
+ u16 raw_msgid)
+{
+ unsigned long flags;
+ u8 msgid;
+
+ spin_lock_irqsave(&ring->lock, flags);
+
+ if (bcm4377_extract_msgid(bcm4377, ring, raw_msgid, &msgid))
+ goto unlock;
+
+ if (!test_bit(msgid, ring->msgids)) {
+ dev_warn(
+ &bcm4377->pdev->dev,
+ "invalid message id in ack for ring %d: %d is not used\n",
+ ring->ring_id, msgid);
+ goto unlock;
+ }
+
+ if (ring->allow_wait && ring->events[msgid]) {
+ complete(ring->events[msgid]);
+ ring->events[msgid] = NULL;
+ }
+
+ bitmap_release_region(ring->msgids, msgid, 0);
+
+unlock:
+ spin_unlock_irqrestore(&ring->lock, flags);
+}
+
+static void bcm4377_handle_completion(struct bcm4377_data *bcm4377,
+ struct bcm4377_completion_ring *ring,
+ u16 pos)
+{
+ struct bcm4377_completion_ring_entry *entry;
+ u16 msg_id, transfer_ring;
+ size_t entry_size, data_len;
+ void *data;
+
+ if (pos >= ring->n_entries) {
+ dev_warn(&bcm4377->pdev->dev,
+ "invalid offset %d for completion ring %d\n", pos,
+ ring->ring_id);
+ return;
+ }
+
+ entry_size = sizeof(*entry) + ring->payload_size;
+ entry = ring->ring + pos * entry_size;
+ data = ring->ring + pos * entry_size + sizeof(*entry);
+ data_len = le32_to_cpu(entry->len);
+ msg_id = le16_to_cpu(entry->msg_id);
+ transfer_ring = le16_to_cpu(entry->ring_id);
+
+ if ((ring->transfer_rings & BIT(transfer_ring)) == 0) {
+ dev_warn(
+ &bcm4377->pdev->dev,
+ "invalid entry at offset %d for transfer ring %d in completion ring %d\n",
+ pos, transfer_ring, ring->ring_id);
+ return;
+ }
+
+ dev_dbg(&bcm4377->pdev->dev,
+ "entry in completion ring %d for transfer ring %d with msg_id %d\n",
+ ring->ring_id, transfer_ring, msg_id);
+
+ switch (transfer_ring) {
+ case BCM4377_XFER_RING_CONTROL:
+ bcm4377_handle_ack(bcm4377, &bcm4377->control_h2d_ring, msg_id);
+ break;
+ case BCM4377_XFER_RING_HCI_H2D:
+ bcm4377_handle_ack(bcm4377, &bcm4377->hci_h2d_ring, msg_id);
+ break;
+ case BCM4377_XFER_RING_SCO_H2D:
+ bcm4377_handle_ack(bcm4377, &bcm4377->sco_h2d_ring, msg_id);
+ break;
+ case BCM4377_XFER_RING_ACL_H2D:
+ bcm4377_handle_ack(bcm4377, &bcm4377->acl_h2d_ring, msg_id);
+ break;
+
+ case BCM4377_XFER_RING_HCI_D2H:
+ bcm4377_handle_event(bcm4377, &bcm4377->hci_d2h_ring, msg_id,
+ entry->flags, HCI_EVENT_PKT, data,
+ data_len);
+ break;
+ case BCM4377_XFER_RING_SCO_D2H:
+ bcm4377_handle_event(bcm4377, &bcm4377->sco_d2h_ring, msg_id,
+ entry->flags, HCI_SCODATA_PKT, data,
+ data_len);
+ break;
+ case BCM4377_XFER_RING_ACL_D2H:
+ bcm4377_handle_event(bcm4377, &bcm4377->acl_d2h_ring, msg_id,
+ entry->flags, HCI_ACLDATA_PKT, data,
+ data_len);
+ break;
+
+ default:
+ dev_warn(
+ &bcm4377->pdev->dev,
+ "entry in completion ring %d for unknown transfer ring %d with msg_id %d\n",
+ ring->ring_id, transfer_ring, msg_id);
+ }
+}
+
+static void bcm4377_poll_completion_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_completion_ring *ring)
+{
+ u16 tail;
+ __le16 *heads = bcm4377->ring_state->completion_ring_head;
+ __le16 *tails = bcm4377->ring_state->completion_ring_tail;
+
+ if (!ring->enabled)
+ return;
+
+ tail = le16_to_cpu(tails[ring->ring_id]);
+ dev_dbg(&bcm4377->pdev->dev,
+ "completion ring #%d: head: %d, tail: %d\n", ring->ring_id,
+ le16_to_cpu(heads[ring->ring_id]), tail);
+
+ while (tail != le16_to_cpu(READ_ONCE(heads[ring->ring_id]))) {
+ /*
+ * ensure the CPU doesn't speculate through the comparison.
+ * otherwise it might already read the (empty) queue entry
+ * before the updated head has been loaded and checked.
+ */
+ dma_rmb();
+
+ bcm4377_handle_completion(bcm4377, ring, tail);
+
+ tail = (tail + 1) % ring->n_entries;
+ tails[ring->ring_id] = cpu_to_le16(tail);
+ }
+}
+
+static irqreturn_t bcm4377_irq(int irq, void *data)
+{
+ struct bcm4377_data *bcm4377 = data;
+ u32 bootstage, rti_status;
+
+ bootstage = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_BOOTSTAGE);
+ rti_status = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_STATUS);
+
+ if (bootstage != bcm4377->bootstage ||
+ rti_status != bcm4377->rti_status) {
+ dev_dbg(&bcm4377->pdev->dev,
+ "bootstage = %d -> %d, rti state = %d -> %d\n",
+ bcm4377->bootstage, bootstage, bcm4377->rti_status,
+ rti_status);
+ complete(&bcm4377->event);
+ bcm4377->bootstage = bootstage;
+ bcm4377->rti_status = rti_status;
+ }
+
+ if (rti_status > 2)
+ dev_err(&bcm4377->pdev->dev, "RTI status is %d\n", rti_status);
+
+ bcm4377_poll_completion_ring(bcm4377, &bcm4377->control_ack_ring);
+ bcm4377_poll_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring);
+ bcm4377_poll_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring);
+ bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
+ bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+
+ return IRQ_HANDLED;
+}
+
+static int bcm4377_enqueue(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring, void *data,
+ size_t len, bool wait)
+{
+ unsigned long flags;
+ struct bcm4377_xfer_ring_entry *entry;
+ void *payload;
+ size_t offset;
+ u16 head, tail, new_head;
+ u16 raw_msgid;
+ int ret, msgid;
+ DECLARE_COMPLETION_ONSTACK(event);
+
+ if (len > ring->payload_size && len > ring->mapped_payload_size) {
+ dev_warn(
+ &bcm4377->pdev->dev,
+ "payload len %zu is too large for ring %d (max is %zu or %zu)\n",
+ len, ring->ring_id, ring->payload_size,
+ ring->mapped_payload_size);
+ return -EINVAL;
+ }
+ if (wait && !ring->allow_wait)
+ return -EINVAL;
+ if (ring->virtual)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ring->lock, flags);
+
+ head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]);
+ tail = le16_to_cpu(bcm4377->ring_state->xfer_ring_tail[ring->ring_id]);
+
+ new_head = (head + 1) % ring->n_entries;
+
+ if (new_head == tail) {
+ dev_warn(&bcm4377->pdev->dev,
+ "can't send message because ring %d is full\n",
+ ring->ring_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ msgid = bitmap_find_free_region(ring->msgids, ring->n_entries, 0);
+ if (msgid < 0) {
+ dev_warn(&bcm4377->pdev->dev,
+ "can't find message id for ring %d\n", ring->ring_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ raw_msgid = FIELD_PREP(BCM4377_MSGID_GENERATION, ring->generation);
+ raw_msgid |= FIELD_PREP(BCM4377_MSGID_ID, msgid);
+
+ offset = head * (sizeof(*entry) + ring->payload_size);
+ entry = ring->ring + offset;
+
+ memset(entry, 0, sizeof(*entry));
+ entry->id = cpu_to_le16(raw_msgid);
+ entry->len = cpu_to_le16(len);
+
+ if (len <= ring->payload_size) {
+ entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_IN_FOOTER;
+ payload = ring->ring + offset + sizeof(*entry);
+ } else {
+ entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED;
+ entry->payload = cpu_to_le64(ring->payloads_dma +
+ msgid * ring->mapped_payload_size);
+ payload = ring->payloads + msgid * ring->mapped_payload_size;
+ }
+
+ memcpy(payload, data, len);
+
+ if (wait)
+ ring->events[msgid] = &event;
+
+ /*
+ * The 4377 chips stop responding to any commands as soon as they
+ * have been idle for a while. Poking the sleep control register here
+ * makes them come alive again.
+ */
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_AWAKE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ dev_dbg(&bcm4377->pdev->dev,
+ "updating head for transfer queue #%d to %d\n", ring->ring_id,
+ new_head);
+ bcm4377->ring_state->xfer_ring_head[ring->ring_id] =
+ cpu_to_le16(new_head);
+
+ if (!ring->sync)
+ bcm4377_ring_doorbell(bcm4377, ring->doorbell, new_head);
+ ret = 0;
+
+out:
+ spin_unlock_irqrestore(&ring->lock, flags);
+
+ if (ret == 0 && wait) {
+ ret = wait_for_completion_interruptible_timeout(
+ &event, BCM4377_TIMEOUT);
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ else if (ret > 0)
+ ret = 0;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ ring->events[msgid] = NULL;
+ spin_unlock_irqrestore(&ring->lock, flags);
+ }
+
+ return ret;
+}
+
+static int bcm4377_create_completion_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_completion_ring *ring)
+{
+ struct bcm4377_create_completion_ring_msg msg;
+ int ret;
+
+ if (ring->enabled) {
+ dev_warn(&bcm4377->pdev->dev,
+ "completion ring %d already enabled\n", ring->ring_id);
+ return 0;
+ }
+
+ memset(ring->ring, 0,
+ ring->n_entries * (sizeof(struct bcm4377_completion_ring_entry) +
+ ring->payload_size));
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_type = BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING;
+ msg.id = cpu_to_le16(ring->ring_id);
+ msg.id_again = cpu_to_le16(ring->ring_id);
+ msg.ring_iova = cpu_to_le64(ring->ring_dma);
+ msg.n_elements = cpu_to_le16(ring->n_entries);
+ msg.intmod_bytes = cpu_to_le32(0xffffffff);
+ msg.unk = cpu_to_le32(0xffffffff);
+ msg.intmod_delay = cpu_to_le16(ring->delay);
+ msg.footer_size = ring->payload_size / 4;
+
+ ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg,
+ sizeof(msg), true);
+ if (!ret)
+ ring->enabled = true;
+
+ return ret;
+}
+
+static int bcm4377_destroy_completion_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_completion_ring *ring)
+{
+ struct bcm4377_destroy_completion_ring_msg msg;
+ int ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_type = BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING;
+ msg.ring_id = cpu_to_le16(ring->ring_id);
+
+ ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg,
+ sizeof(msg), true);
+ if (ret)
+ dev_warn(&bcm4377->pdev->dev,
+ "failed to destroy completion ring %d\n",
+ ring->ring_id);
+
+ ring->enabled = false;
+ return ret;
+}
+
+static int bcm4377_create_transfer_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring)
+{
+ struct bcm4377_create_transfer_ring_msg msg;
+ u16 flags = 0;
+ int ret, i;
+ unsigned long spinlock_flags;
+
+ if (ring->virtual)
+ flags |= BCM4377_XFER_RING_FLAG_VIRTUAL;
+ if (ring->sync)
+ flags |= BCM4377_XFER_RING_FLAG_SYNC;
+
+ spin_lock_irqsave(&ring->lock, spinlock_flags);
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_type = BCM4377_CONTROL_MSG_CREATE_XFER_RING;
+ msg.ring_id = cpu_to_le16(ring->ring_id);
+ msg.ring_id_again = cpu_to_le16(ring->ring_id);
+ msg.ring_iova = cpu_to_le64(ring->ring_dma);
+ msg.n_elements = cpu_to_le16(ring->n_entries);
+ msg.completion_ring_id = cpu_to_le16(ring->completion_ring);
+ msg.doorbell = cpu_to_le16(ring->doorbell);
+ msg.flags = cpu_to_le16(flags);
+ msg.footer_size = ring->payload_size / 4;
+
+ bcm4377->ring_state->xfer_ring_head[ring->ring_id] = 0;
+ bcm4377->ring_state->xfer_ring_tail[ring->ring_id] = 0;
+ ring->generation++;
+ spin_unlock_irqrestore(&ring->lock, spinlock_flags);
+
+ ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg,
+ sizeof(msg), true);
+
+ spin_lock_irqsave(&ring->lock, spinlock_flags);
+
+ if (ring->d2h_buffers_only) {
+ for (i = 0; i < ring->n_entries; ++i) {
+ struct bcm4377_xfer_ring_entry *entry =
+ ring->ring + i * sizeof(*entry);
+ u16 raw_msgid = FIELD_PREP(BCM4377_MSGID_GENERATION,
+ ring->generation);
+ raw_msgid |= FIELD_PREP(BCM4377_MSGID_ID, i);
+
+ memset(entry, 0, sizeof(*entry));
+ entry->id = cpu_to_le16(raw_msgid);
+ entry->len = cpu_to_le16(ring->mapped_payload_size);
+ entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED;
+ entry->payload =
+ cpu_to_le64(ring->payloads_dma +
+ i * ring->mapped_payload_size);
+ }
+ }
+
+ /*
+ * send some messages if this is a device->host ring to allow the device
+ * to reply by acknowledging them in the completion ring
+ */
+ if (ring->virtual || ring->d2h_buffers_only) {
+ bcm4377->ring_state->xfer_ring_head[ring->ring_id] =
+ cpu_to_le16(0xf);
+ bcm4377_ring_doorbell(bcm4377, ring->doorbell, 0xf);
+ }
+
+ ring->enabled = true;
+ spin_unlock_irqrestore(&ring->lock, spinlock_flags);
+
+ return ret;
+}
+
+static int bcm4377_destroy_transfer_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring)
+{
+ struct bcm4377_destroy_transfer_ring_msg msg;
+ int ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_type = BCM4377_CONTROL_MSG_DESTROY_XFER_RING;
+ msg.ring_id = cpu_to_le16(ring->ring_id);
+
+ ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg,
+ sizeof(msg), true);
+ if (ret)
+ dev_warn(&bcm4377->pdev->dev,
+ "failed to destroy transfer ring %d\n", ring->ring_id);
+
+ ring->enabled = false;
+ return ret;
+}
+
+static int __bcm4378_send_calibration_chunk(struct bcm4377_data *bcm4377,
+ const void *data, size_t data_len,
+ u16 blocks_left)
+{
+ struct bcm4378_hci_send_calibration_cmd cmd;
+ struct sk_buff *skb;
+
+ if (data_len > sizeof(cmd.data))
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.unk = 0x03;
+ cmd.blocks_left = cpu_to_le16(blocks_left);
+ memcpy(cmd.data, data, data_len);
+
+ skb = __hci_cmd_sync(bcm4377->hdev, 0xfd97, sizeof(cmd), &cmd,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int __bcm4378_send_calibration(struct bcm4377_data *bcm4377,
+ const void *data, size_t data_size)
+{
+ int ret;
+ size_t i, left, transfer_len;
+ size_t blocks =
+ DIV_ROUND_UP(data_size, (size_t)BCM4378_CALIBRATION_CHUNK_SIZE);
+
+ if (!data) {
+ dev_err(&bcm4377->pdev->dev,
+ "no calibration data available.\n");
+ return -ENOENT;
+ }
+
+ for (i = 0, left = data_size; i < blocks; ++i, left -= transfer_len) {
+ transfer_len =
+ min_t(size_t, left, BCM4378_CALIBRATION_CHUNK_SIZE);
+
+ ret = __bcm4378_send_calibration_chunk(
+ bcm4377, data + i * BCM4378_CALIBRATION_CHUNK_SIZE,
+ transfer_len, blocks - i - 1);
+ if (ret) {
+ dev_err(&bcm4377->pdev->dev,
+ "send calibration chunk failed with %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm4378_send_calibration(struct bcm4377_data *bcm4377)
+{
+ if ((strcmp(bcm4377->stepping, "b1") == 0) ||
+ strcmp(bcm4377->stepping, "b3") == 0)
+ return __bcm4378_send_calibration(
+ bcm4377, bcm4377->taurus_beamforming_cal_blob,
+ bcm4377->taurus_beamforming_cal_size);
+ else
+ return __bcm4378_send_calibration(bcm4377,
+ bcm4377->taurus_cal_blob,
+ bcm4377->taurus_cal_size);
+}
+
+static int bcm4387_send_calibration(struct bcm4377_data *bcm4377)
+{
+ if (strcmp(bcm4377->stepping, "c2") == 0)
+ return __bcm4378_send_calibration(
+ bcm4377, bcm4377->taurus_beamforming_cal_blob,
+ bcm4377->taurus_beamforming_cal_size);
+ else
+ return __bcm4378_send_calibration(bcm4377,
+ bcm4377->taurus_cal_blob,
+ bcm4377->taurus_cal_size);
+}
+
+static int bcm4388_send_calibration(struct bcm4377_data *bcm4377)
+{
+ /* BCM4388 always uses beamforming */
+ return __bcm4378_send_calibration(
+ bcm4377, bcm4377->taurus_beamforming_cal_blob,
+ bcm4377->taurus_beamforming_cal_size);
+}
+
+static const struct firmware *bcm4377_request_blob(struct bcm4377_data *bcm4377,
+ const char *suffix)
+{
+ const struct firmware *fw;
+ char name0[64], name1[64];
+ int ret;
+
+ snprintf(name0, sizeof(name0), "brcm/brcmbt%04x%s-%s-%s.%s",
+ bcm4377->hw->id, bcm4377->stepping, bcm4377->board_type,
+ bcm4377->vendor, suffix);
+ snprintf(name1, sizeof(name1), "brcm/brcmbt%04x%s-%s.%s",
+ bcm4377->hw->id, bcm4377->stepping, bcm4377->board_type,
+ suffix);
+ dev_dbg(&bcm4377->pdev->dev, "Trying to load firmware: '%s' or '%s'\n",
+ name0, name1);
+
+ ret = firmware_request_nowarn(&fw, name0, &bcm4377->pdev->dev);
+ if (!ret)
+ return fw;
+ ret = firmware_request_nowarn(&fw, name1, &bcm4377->pdev->dev);
+ if (!ret)
+ return fw;
+
+ dev_err(&bcm4377->pdev->dev,
+ "Unable to load firmware; tried '%s' and '%s'\n", name0, name1);
+ return NULL;
+}
+
+static int bcm4377_send_ptb(struct bcm4377_data *bcm4377,
+ const struct firmware *fw)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(bcm4377->hdev, 0xfd98, fw->size, fw->data,
+ HCI_INIT_TIMEOUT);
+ /*
+ * This command seems to always fail on more recent firmware versions
+ * (even in traces taken from the macOS driver). It's unclear why this
+ * happens but because the PTB file contains calibration and/or
+ * regulatory data and may be required on older firmware we still try to
+ * send it here just in case and just ignore if it fails.
+ */
+ if (!IS_ERR(skb))
+ kfree_skb(skb);
+ return 0;
+}
+
+static int bcm4378_send_ptb_chunk(struct bcm4377_data *bcm4377,
+ const void *data, size_t data_len,
+ u16 blocks_left)
+{
+ struct bcm4378_hci_send_ptb_cmd cmd;
+ struct sk_buff *skb;
+
+ if (data_len > BCM4378_PTB_CHUNK_SIZE)
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.blocks_left = cpu_to_le16(blocks_left);
+ memcpy(cmd.data, data, data_len);
+
+ skb = __hci_cmd_sync(bcm4377->hdev, 0xfe0d, sizeof(cmd), &cmd,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int bcm4378_send_ptb(struct bcm4377_data *bcm4377,
+ const struct firmware *fw)
+{
+ size_t chunks = DIV_ROUND_UP(fw->size, (size_t)BCM4378_PTB_CHUNK_SIZE);
+ size_t i, left, transfer_len;
+ int ret;
+
+ for (i = 0, left = fw->size; i < chunks; ++i, left -= transfer_len) {
+ transfer_len = min_t(size_t, left, BCM4378_PTB_CHUNK_SIZE);
+
+ dev_dbg(&bcm4377->pdev->dev, "sending ptb chunk %zu/%zu\n",
+ i + 1, chunks);
+ ret = bcm4378_send_ptb_chunk(
+ bcm4377, fw->data + i * BCM4378_PTB_CHUNK_SIZE,
+ transfer_len, chunks - i - 1);
+ if (ret) {
+ dev_err(&bcm4377->pdev->dev,
+ "sending ptb chunk %zu failed (%d)", i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int bcm4377_hci_open(struct hci_dev *hdev)
+{
+ struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
+ int ret;
+
+ dev_dbg(&bcm4377->pdev->dev, "creating rings\n");
+
+ ret = bcm4377_create_completion_ring(bcm4377,
+ &bcm4377->hci_acl_ack_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_create_completion_ring(bcm4377,
+ &bcm4377->hci_acl_event_ring);
+ if (ret)
+ goto destroy_hci_acl_ack;
+ ret = bcm4377_create_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
+ if (ret)
+ goto destroy_hci_acl_event;
+ ret = bcm4377_create_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+ if (ret)
+ goto destroy_sco_ack;
+ dev_dbg(&bcm4377->pdev->dev,
+ "all completion rings successfully created!\n");
+
+ ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring);
+ if (ret)
+ goto destroy_sco_event;
+ ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring);
+ if (ret)
+ goto destroy_hci_h2d;
+ ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring);
+ if (ret)
+ goto destroy_hci_d2h;
+ ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring);
+ if (ret)
+ goto destroy_sco_h2d;
+ ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring);
+ if (ret)
+ goto destroy_sco_d2h;
+ ret = bcm4377_create_transfer_ring(bcm4377, &bcm4377->acl_d2h_ring);
+ if (ret)
+ goto destroy_acl_h2d;
+ dev_dbg(&bcm4377->pdev->dev,
+ "all transfer rings successfully created!\n");
+
+ return 0;
+
+destroy_acl_h2d:
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring);
+destroy_sco_d2h:
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring);
+destroy_sco_h2d:
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring);
+destroy_hci_d2h:
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring);
+destroy_hci_h2d:
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring);
+destroy_sco_event:
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+destroy_sco_ack:
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
+destroy_hci_acl_event:
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring);
+destroy_hci_acl_ack:
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring);
+
+ dev_err(&bcm4377->pdev->dev, "Creating rings failed with %d\n", ret);
+ return ret;
+}
+
+static int bcm4377_hci_close(struct hci_dev *hdev)
+{
+ struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
+
+ dev_dbg(&bcm4377->pdev->dev, "destroying rings in hci_close\n");
+
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->acl_d2h_ring);
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring);
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring);
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring);
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring);
+ bcm4377_destroy_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring);
+
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring);
+ bcm4377_destroy_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring);
+
+ return 0;
+}
+
+static bool bcm4377_is_valid_bdaddr(struct bcm4377_data *bcm4377,
+ bdaddr_t *addr)
+{
+ if (addr->b[0] != 0x93)
+ return true;
+ if (addr->b[1] != 0x76)
+ return true;
+ if (addr->b[2] != 0x00)
+ return true;
+ if (addr->b[4] != (bcm4377->hw->id & 0xff))
+ return true;
+ if (addr->b[5] != (bcm4377->hw->id >> 8))
+ return true;
+ return false;
+}
+
+static int bcm4377_check_bdaddr(struct bcm4377_data *bcm4377)
+{
+ struct hci_rp_read_bd_addr *bda;
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(bcm4377->hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ int err = PTR_ERR(skb);
+
+ dev_err(&bcm4377->pdev->dev, "HCI_OP_READ_BD_ADDR failed (%d)",
+ err);
+ return err;
+ }
+
+ if (skb->len != sizeof(*bda)) {
+ dev_err(&bcm4377->pdev->dev,
+ "HCI_OP_READ_BD_ADDR reply length invalid");
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ bda = (struct hci_rp_read_bd_addr *)skb->data;
+ if (!bcm4377_is_valid_bdaddr(bcm4377, &bda->bdaddr))
+ hci_set_quirk(bcm4377->hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int bcm4377_hci_setup(struct hci_dev *hdev)
+{
+ struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
+ const struct firmware *fw;
+ int ret;
+
+ if (bcm4377->hw->send_calibration) {
+ ret = bcm4377->hw->send_calibration(bcm4377);
+ if (ret)
+ return ret;
+ }
+
+ fw = bcm4377_request_blob(bcm4377, "ptb");
+ if (!fw) {
+ dev_err(&bcm4377->pdev->dev, "failed to load PTB data");
+ return -ENOENT;
+ }
+
+ ret = bcm4377->hw->send_ptb(bcm4377, fw);
+ release_firmware(fw);
+ if (ret)
+ return ret;
+
+ return bcm4377_check_bdaddr(bcm4377);
+}
+
+static int bcm4377_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
+ struct bcm4377_transfer_ring *ring;
+ int ret;
+
+ switch (hci_skb_pkt_type(skb)) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ ring = &bcm4377->hci_h2d_ring;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ ring = &bcm4377->acl_h2d_ring;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ ring = &bcm4377->sco_h2d_ring;
+ break;
+
+ default:
+ return -EILSEQ;
+ }
+
+ ret = bcm4377_enqueue(bcm4377, ring, skb->data, skb->len, false);
+ if (ret < 0) {
+ hdev->stat.err_tx++;
+ return ret;
+ }
+
+ hdev->stat.byte_tx += skb->len;
+ kfree_skb(skb);
+ return ret;
+}
+
+static int bcm4377_hci_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct bcm4377_data *bcm4377 = hci_get_drvdata(hdev);
+ struct sk_buff *skb;
+ int err;
+
+ skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ dev_err(&bcm4377->pdev->dev,
+ "Change address command failed (%d)", err);
+ return err;
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int bcm4377_alloc_transfer_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_transfer_ring *ring)
+{
+ size_t entry_size;
+
+ spin_lock_init(&ring->lock);
+ ring->payload_size = ALIGN(ring->payload_size, 4);
+ ring->mapped_payload_size = ALIGN(ring->mapped_payload_size, 4);
+
+ if (ring->payload_size > BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE)
+ return -EINVAL;
+ if (ring->n_entries > BCM4377_MAX_RING_SIZE)
+ return -EINVAL;
+ if (ring->virtual && ring->allow_wait)
+ return -EINVAL;
+
+ if (ring->d2h_buffers_only) {
+ if (ring->virtual)
+ return -EINVAL;
+ if (ring->payload_size)
+ return -EINVAL;
+ if (!ring->mapped_payload_size)
+ return -EINVAL;
+ }
+ if (ring->virtual)
+ return 0;
+
+ entry_size =
+ ring->payload_size + sizeof(struct bcm4377_xfer_ring_entry);
+ ring->ring = dmam_alloc_coherent(&bcm4377->pdev->dev,
+ ring->n_entries * entry_size,
+ &ring->ring_dma, GFP_KERNEL);
+ if (!ring->ring)
+ return -ENOMEM;
+
+ if (ring->allow_wait) {
+ ring->events = devm_kcalloc(&bcm4377->pdev->dev,
+ ring->n_entries,
+ sizeof(*ring->events), GFP_KERNEL);
+ if (!ring->events)
+ return -ENOMEM;
+ }
+
+ if (ring->mapped_payload_size) {
+ ring->payloads = dmam_alloc_coherent(
+ &bcm4377->pdev->dev,
+ ring->n_entries * ring->mapped_payload_size,
+ &ring->payloads_dma, GFP_KERNEL);
+ if (!ring->payloads)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int bcm4377_alloc_completion_ring(struct bcm4377_data *bcm4377,
+ struct bcm4377_completion_ring *ring)
+{
+ size_t entry_size;
+
+ ring->payload_size = ALIGN(ring->payload_size, 4);
+ if (ring->payload_size > BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE)
+ return -EINVAL;
+ if (ring->n_entries > BCM4377_MAX_RING_SIZE)
+ return -EINVAL;
+
+ entry_size = ring->payload_size +
+ sizeof(struct bcm4377_completion_ring_entry);
+
+ ring->ring = dmam_alloc_coherent(&bcm4377->pdev->dev,
+ ring->n_entries * entry_size,
+ &ring->ring_dma, GFP_KERNEL);
+ if (!ring->ring)
+ return -ENOMEM;
+ return 0;
+}
+
+static int bcm4377_init_context(struct bcm4377_data *bcm4377)
+{
+ struct device *dev = &bcm4377->pdev->dev;
+ dma_addr_t peripheral_info_dma;
+
+ bcm4377->ctx = dmam_alloc_coherent(dev, sizeof(*bcm4377->ctx),
+ &bcm4377->ctx_dma, GFP_KERNEL);
+ if (!bcm4377->ctx)
+ return -ENOMEM;
+ memset(bcm4377->ctx, 0, sizeof(*bcm4377->ctx));
+
+ bcm4377->ring_state =
+ dmam_alloc_coherent(dev, sizeof(*bcm4377->ring_state),
+ &bcm4377->ring_state_dma, GFP_KERNEL);
+ if (!bcm4377->ring_state)
+ return -ENOMEM;
+ memset(bcm4377->ring_state, 0, sizeof(*bcm4377->ring_state));
+
+ bcm4377->ctx->version = cpu_to_le16(1);
+ bcm4377->ctx->size = cpu_to_le16(sizeof(*bcm4377->ctx));
+ bcm4377->ctx->enabled_caps = cpu_to_le32(2);
+
+ /*
+ * The BT device will write 0x20 bytes of data to this buffer but
+ * the exact contents are unknown. It only needs to exist for BT
+ * to work such that we can just allocate and then ignore it.
+ */
+ if (!dmam_alloc_coherent(&bcm4377->pdev->dev, 0x20,
+ &peripheral_info_dma, GFP_KERNEL))
+ return -ENOMEM;
+ bcm4377->ctx->peripheral_info_addr = cpu_to_le64(peripheral_info_dma);
+
+ bcm4377->ctx->xfer_ring_heads_addr = cpu_to_le64(
+ bcm4377->ring_state_dma +
+ offsetof(struct bcm4377_ring_state, xfer_ring_head));
+ bcm4377->ctx->xfer_ring_tails_addr = cpu_to_le64(
+ bcm4377->ring_state_dma +
+ offsetof(struct bcm4377_ring_state, xfer_ring_tail));
+ bcm4377->ctx->completion_ring_heads_addr = cpu_to_le64(
+ bcm4377->ring_state_dma +
+ offsetof(struct bcm4377_ring_state, completion_ring_head));
+ bcm4377->ctx->completion_ring_tails_addr = cpu_to_le64(
+ bcm4377->ring_state_dma +
+ offsetof(struct bcm4377_ring_state, completion_ring_tail));
+
+ bcm4377->ctx->n_completion_rings =
+ cpu_to_le16(BCM4377_N_COMPLETION_RINGS);
+ bcm4377->ctx->n_xfer_rings = cpu_to_le16(BCM4377_N_TRANSFER_RINGS);
+
+ bcm4377->ctx->control_completion_ring_addr =
+ cpu_to_le64(bcm4377->control_ack_ring.ring_dma);
+ bcm4377->ctx->control_completion_ring_n_entries =
+ cpu_to_le16(bcm4377->control_ack_ring.n_entries);
+ bcm4377->ctx->control_completion_ring_doorbell = cpu_to_le16(0xffff);
+ bcm4377->ctx->control_completion_ring_msi = 0;
+ bcm4377->ctx->control_completion_ring_header_size = 0;
+ bcm4377->ctx->control_completion_ring_footer_size = 0;
+
+ bcm4377->ctx->control_xfer_ring_addr =
+ cpu_to_le64(bcm4377->control_h2d_ring.ring_dma);
+ bcm4377->ctx->control_xfer_ring_n_entries =
+ cpu_to_le16(bcm4377->control_h2d_ring.n_entries);
+ bcm4377->ctx->control_xfer_ring_doorbell =
+ cpu_to_le16(bcm4377->control_h2d_ring.doorbell);
+ bcm4377->ctx->control_xfer_ring_msi = 0;
+ bcm4377->ctx->control_xfer_ring_header_size = 0;
+ bcm4377->ctx->control_xfer_ring_footer_size =
+ bcm4377->control_h2d_ring.payload_size / 4;
+
+ dev_dbg(&bcm4377->pdev->dev, "context initialized at IOVA %pad",
+ &bcm4377->ctx_dma);
+
+ return 0;
+}
+
+static int bcm4377_prepare_rings(struct bcm4377_data *bcm4377)
+{
+ int ret;
+
+ /*
+ * Even though many of these settings appear to be configurable
+ * when sending the "create ring" messages most of these are
+ * actually hardcoded in some (and quite possibly all) firmware versions
+ * and changing them on the host has no effect.
+ * Specifically, this applies to at least the doorbells, the transfer
+ * and completion ring ids and their mapping (e.g. both HCI and ACL
+ * entries will always be queued in completion rings 1 and 2 no matter
+ * what we configure here).
+ */
+ bcm4377->control_ack_ring.ring_id = BCM4377_ACK_RING_CONTROL;
+ bcm4377->control_ack_ring.n_entries = 32;
+ bcm4377->control_ack_ring.transfer_rings =
+ BIT(BCM4377_XFER_RING_CONTROL);
+
+ bcm4377->hci_acl_ack_ring.ring_id = BCM4377_ACK_RING_HCI_ACL;
+ bcm4377->hci_acl_ack_ring.n_entries = 2 * BCM4377_RING_N_ENTRIES;
+ bcm4377->hci_acl_ack_ring.transfer_rings =
+ BIT(BCM4377_XFER_RING_HCI_H2D) | BIT(BCM4377_XFER_RING_ACL_H2D);
+ bcm4377->hci_acl_ack_ring.delay = 1000;
+
+ /*
+ * A payload size of MAX_EVENT_PAYLOAD_SIZE is enough here since large
+ * ACL packets will be transmitted inside buffers mapped via
+ * acl_d2h_ring anyway.
+ */
+ bcm4377->hci_acl_event_ring.ring_id = BCM4377_EVENT_RING_HCI_ACL;
+ bcm4377->hci_acl_event_ring.payload_size = MAX_EVENT_PAYLOAD_SIZE;
+ bcm4377->hci_acl_event_ring.n_entries = 2 * BCM4377_RING_N_ENTRIES;
+ bcm4377->hci_acl_event_ring.transfer_rings =
+ BIT(BCM4377_XFER_RING_HCI_D2H) | BIT(BCM4377_XFER_RING_ACL_D2H);
+ bcm4377->hci_acl_event_ring.delay = 1000;
+
+ bcm4377->sco_ack_ring.ring_id = BCM4377_ACK_RING_SCO;
+ bcm4377->sco_ack_ring.n_entries = BCM4377_RING_N_ENTRIES;
+ bcm4377->sco_ack_ring.transfer_rings = BIT(BCM4377_XFER_RING_SCO_H2D);
+
+ bcm4377->sco_event_ring.ring_id = BCM4377_EVENT_RING_SCO;
+ bcm4377->sco_event_ring.payload_size = MAX_SCO_PAYLOAD_SIZE;
+ bcm4377->sco_event_ring.n_entries = BCM4377_RING_N_ENTRIES;
+ bcm4377->sco_event_ring.transfer_rings = BIT(BCM4377_XFER_RING_SCO_D2H);
+
+ bcm4377->control_h2d_ring.ring_id = BCM4377_XFER_RING_CONTROL;
+ bcm4377->control_h2d_ring.doorbell = BCM4377_DOORBELL_CONTROL;
+ bcm4377->control_h2d_ring.payload_size = BCM4377_CONTROL_MSG_SIZE;
+ bcm4377->control_h2d_ring.completion_ring = BCM4377_ACK_RING_CONTROL;
+ bcm4377->control_h2d_ring.allow_wait = true;
+ bcm4377->control_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ bcm4377->hci_h2d_ring.ring_id = BCM4377_XFER_RING_HCI_H2D;
+ bcm4377->hci_h2d_ring.doorbell = BCM4377_DOORBELL_HCI_H2D;
+ bcm4377->hci_h2d_ring.payload_size = MAX_EVENT_PAYLOAD_SIZE;
+ bcm4377->hci_h2d_ring.completion_ring = BCM4377_ACK_RING_HCI_ACL;
+ bcm4377->hci_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ bcm4377->hci_d2h_ring.ring_id = BCM4377_XFER_RING_HCI_D2H;
+ bcm4377->hci_d2h_ring.doorbell = BCM4377_DOORBELL_HCI_D2H;
+ bcm4377->hci_d2h_ring.completion_ring = BCM4377_EVENT_RING_HCI_ACL;
+ bcm4377->hci_d2h_ring.virtual = true;
+ bcm4377->hci_d2h_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ bcm4377->sco_h2d_ring.ring_id = BCM4377_XFER_RING_SCO_H2D;
+ bcm4377->sco_h2d_ring.doorbell = BCM4377_DOORBELL_SCO;
+ bcm4377->sco_h2d_ring.payload_size = MAX_SCO_PAYLOAD_SIZE;
+ bcm4377->sco_h2d_ring.completion_ring = BCM4377_ACK_RING_SCO;
+ bcm4377->sco_h2d_ring.sync = true;
+ bcm4377->sco_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ bcm4377->sco_d2h_ring.ring_id = BCM4377_XFER_RING_SCO_D2H;
+ bcm4377->sco_d2h_ring.doorbell = BCM4377_DOORBELL_SCO;
+ bcm4377->sco_d2h_ring.completion_ring = BCM4377_EVENT_RING_SCO;
+ bcm4377->sco_d2h_ring.virtual = true;
+ bcm4377->sco_d2h_ring.sync = true;
+ bcm4377->sco_d2h_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ /*
+ * This ring has to use mapped_payload_size because the largest ACL
+ * packet doesn't fit inside the largest possible footer
+ */
+ bcm4377->acl_h2d_ring.ring_id = BCM4377_XFER_RING_ACL_H2D;
+ bcm4377->acl_h2d_ring.doorbell = BCM4377_DOORBELL_ACL_H2D;
+ bcm4377->acl_h2d_ring.mapped_payload_size = MAX_ACL_PAYLOAD_SIZE;
+ bcm4377->acl_h2d_ring.completion_ring = BCM4377_ACK_RING_HCI_ACL;
+ bcm4377->acl_h2d_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ /*
+ * This ring only contains empty buffers to be used by incoming
+ * ACL packets that do not fit inside the footer of hci_acl_event_ring
+ */
+ bcm4377->acl_d2h_ring.ring_id = BCM4377_XFER_RING_ACL_D2H;
+ bcm4377->acl_d2h_ring.doorbell = BCM4377_DOORBELL_ACL_D2H;
+ bcm4377->acl_d2h_ring.completion_ring = BCM4377_EVENT_RING_HCI_ACL;
+ bcm4377->acl_d2h_ring.d2h_buffers_only = true;
+ bcm4377->acl_d2h_ring.mapped_payload_size = MAX_ACL_PAYLOAD_SIZE;
+ bcm4377->acl_d2h_ring.n_entries = BCM4377_RING_N_ENTRIES;
+
+ /*
+ * no need for any cleanup since this is only called from _probe
+ * and only devres-managed allocations are used
+ */
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->control_h2d_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->hci_h2d_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->hci_d2h_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->sco_h2d_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->sco_d2h_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->acl_h2d_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_transfer_ring(bcm4377, &bcm4377->acl_d2h_ring);
+ if (ret)
+ return ret;
+
+ ret = bcm4377_alloc_completion_ring(bcm4377,
+ &bcm4377->control_ack_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_completion_ring(bcm4377,
+ &bcm4377->hci_acl_ack_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_completion_ring(bcm4377,
+ &bcm4377->hci_acl_event_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
+ if (ret)
+ return ret;
+ ret = bcm4377_alloc_completion_ring(bcm4377, &bcm4377->sco_event_ring);
+ if (ret)
+ return ret;
+
+ dev_dbg(&bcm4377->pdev->dev, "all rings allocated and prepared\n");
+
+ return 0;
+}
+
+static int bcm4377_boot(struct bcm4377_data *bcm4377)
+{
+ const struct firmware *fw;
+ void *bfr;
+ dma_addr_t fw_dma;
+ int ret = 0;
+ u32 bootstage, rti_status;
+
+ bootstage = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_BOOTSTAGE);
+ rti_status = ioread32(bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_STATUS);
+
+ if (bootstage != 0) {
+ dev_err(&bcm4377->pdev->dev, "bootstage is %d and not 0\n",
+ bootstage);
+ return -EINVAL;
+ }
+
+ if (rti_status != 0) {
+ dev_err(&bcm4377->pdev->dev, "RTI status is %d and not 0\n",
+ rti_status);
+ return -EINVAL;
+ }
+
+ fw = bcm4377_request_blob(bcm4377, "bin");
+ if (!fw) {
+ dev_err(&bcm4377->pdev->dev, "Failed to load firmware\n");
+ return -ENOENT;
+ }
+
+ bfr = dma_alloc_coherent(&bcm4377->pdev->dev, fw->size, &fw_dma,
+ GFP_KERNEL);
+ if (!bfr) {
+ ret = -ENOMEM;
+ goto out_release_fw;
+ }
+
+ memcpy(bfr, fw->data, fw->size);
+
+ iowrite32(0, bcm4377->bar0 + BCM4377_BAR0_HOST_WINDOW_LO);
+ iowrite32(0, bcm4377->bar0 + BCM4377_BAR0_HOST_WINDOW_HI);
+ iowrite32(BCM4377_DMA_MASK,
+ bcm4377->bar0 + BCM4377_BAR0_HOST_WINDOW_SIZE);
+
+ iowrite32(lower_32_bits(fw_dma),
+ bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_FW_LO);
+ iowrite32(upper_32_bits(fw_dma),
+ bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_FW_HI);
+ iowrite32(fw->size,
+ bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_FW_SIZE);
+ iowrite32(0, bcm4377->bar0 + BCM4377_BAR0_FW_DOORBELL);
+
+ dev_dbg(&bcm4377->pdev->dev, "waiting for firmware to boot\n");
+
+ ret = wait_for_completion_interruptible_timeout(&bcm4377->event,
+ BCM4377_BOOT_TIMEOUT);
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ goto out_dma_free;
+ } else if (ret < 0) {
+ goto out_dma_free;
+ }
+
+ if (bcm4377->bootstage != 2) {
+ dev_err(&bcm4377->pdev->dev, "boostage %d != 2\n",
+ bcm4377->bootstage);
+ ret = -ENXIO;
+ goto out_dma_free;
+ }
+
+ dev_dbg(&bcm4377->pdev->dev, "firmware has booted (stage = %x)\n",
+ bcm4377->bootstage);
+ ret = 0;
+
+out_dma_free:
+ dma_free_coherent(&bcm4377->pdev->dev, fw->size, bfr, fw_dma);
+out_release_fw:
+ release_firmware(fw);
+ return ret;
+}
+
+static int bcm4377_setup_rti(struct bcm4377_data *bcm4377)
+{
+ int ret;
+
+ dev_dbg(&bcm4377->pdev->dev, "starting RTI\n");
+ iowrite32(1, bcm4377->bar0 + BCM4377_BAR0_RTI_CONTROL);
+
+ ret = wait_for_completion_interruptible_timeout(&bcm4377->event,
+ BCM4377_TIMEOUT);
+ if (ret == 0) {
+ dev_err(&bcm4377->pdev->dev,
+ "timed out while waiting for RTI to transition to state 1");
+ return -ETIMEDOUT;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ if (bcm4377->rti_status != 1) {
+ dev_err(&bcm4377->pdev->dev, "RTI did not ack state 1 (%d)\n",
+ bcm4377->rti_status);
+ return -ENODEV;
+ }
+ dev_dbg(&bcm4377->pdev->dev, "RTI is in state 1\n");
+
+ /* allow access to the entire IOVA space again */
+ iowrite32(0, bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_WINDOW_LO);
+ iowrite32(0, bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_WINDOW_HI);
+ iowrite32(BCM4377_DMA_MASK,
+ bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_RTI_WINDOW_SIZE);
+
+ /* setup "Converged IPC" context */
+ iowrite32(lower_32_bits(bcm4377->ctx_dma),
+ bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_CONTEXT_ADDR_LO);
+ iowrite32(upper_32_bits(bcm4377->ctx_dma),
+ bcm4377->bar2 + bcm4377->hw->bar2_offset + BCM4377_BAR2_CONTEXT_ADDR_HI);
+ iowrite32(2, bcm4377->bar0 + BCM4377_BAR0_RTI_CONTROL);
+
+ ret = wait_for_completion_interruptible_timeout(&bcm4377->event,
+ BCM4377_TIMEOUT);
+ if (ret == 0) {
+ dev_err(&bcm4377->pdev->dev,
+ "timed out while waiting for RTI to transition to state 2");
+ return -ETIMEDOUT;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ if (bcm4377->rti_status != 2) {
+ dev_err(&bcm4377->pdev->dev, "RTI did not ack state 2 (%d)\n",
+ bcm4377->rti_status);
+ return -ENODEV;
+ }
+
+ dev_dbg(&bcm4377->pdev->dev,
+ "RTI is in state 2; control ring is ready\n");
+ bcm4377->control_ack_ring.enabled = true;
+
+ return 0;
+}
+
+static int bcm4377_parse_otp_board_params(struct bcm4377_data *bcm4377,
+ char tag, const char *val, size_t len)
+{
+ if (tag != 'V')
+ return 0;
+ if (len >= sizeof(bcm4377->vendor))
+ return -EINVAL;
+
+ strscpy(bcm4377->vendor, val, len + 1);
+ return 0;
+}
+
+static int bcm4377_parse_otp_chip_params(struct bcm4377_data *bcm4377, char tag,
+ const char *val, size_t len)
+{
+ size_t idx = 0;
+
+ if (tag != 's')
+ return 0;
+ if (len >= sizeof(bcm4377->stepping))
+ return -EINVAL;
+
+ while (len != 0) {
+ bcm4377->stepping[idx] = tolower(val[idx]);
+ if (val[idx] == '\0')
+ return 0;
+
+ idx++;
+ len--;
+ }
+
+ bcm4377->stepping[idx] = '\0';
+ return 0;
+}
+
+static int bcm4377_parse_otp_str(struct bcm4377_data *bcm4377, const u8 *str,
+ enum bcm4377_otp_params_type type)
+{
+ const char *p;
+ int ret;
+
+ p = skip_spaces(str);
+ while (*p) {
+ char tag = *p++;
+ const char *end;
+ size_t len;
+
+ if (*p++ != '=') /* implicit NUL check */
+ return -EINVAL;
+
+ /* *p might be NUL here, if so end == p and len == 0 */
+ end = strchrnul(p, ' ');
+ len = end - p;
+
+ /* leave 1 byte for NUL in destination string */
+ if (len > (BCM4377_OTP_MAX_PARAM_LEN - 1))
+ return -EINVAL;
+
+ switch (type) {
+ case BCM4377_OTP_BOARD_PARAMS:
+ ret = bcm4377_parse_otp_board_params(bcm4377, tag, p,
+ len);
+ break;
+ case BCM4377_OTP_CHIP_PARAMS:
+ ret = bcm4377_parse_otp_chip_params(bcm4377, tag, p,
+ len);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ /* Skip to next arg, if any */
+ p = skip_spaces(end);
+ }
+
+ return 0;
+}
+
+static int bcm4377_parse_otp_sys_vendor(struct bcm4377_data *bcm4377, u8 *otp,
+ size_t size)
+{
+ int idx = 4;
+ const char *chip_params;
+ const char *board_params;
+ int ret;
+
+ /* 4-byte header and two empty strings */
+ if (size < 6)
+ return -EINVAL;
+
+ if (get_unaligned_le32(otp) != BCM4377_OTP_VENDOR_HDR)
+ return -EINVAL;
+
+ chip_params = &otp[idx];
+
+ /* Skip first string, including terminator */
+ idx += strnlen(chip_params, size - idx) + 1;
+ if (idx >= size)
+ return -EINVAL;
+
+ board_params = &otp[idx];
+
+ /* Skip to terminator of second string */
+ idx += strnlen(board_params, size - idx);
+ if (idx >= size)
+ return -EINVAL;
+
+ /* At this point both strings are guaranteed NUL-terminated */
+ dev_dbg(&bcm4377->pdev->dev,
+ "OTP: chip_params='%s' board_params='%s'\n", chip_params,
+ board_params);
+
+ ret = bcm4377_parse_otp_str(bcm4377, chip_params,
+ BCM4377_OTP_CHIP_PARAMS);
+ if (ret)
+ return ret;
+
+ ret = bcm4377_parse_otp_str(bcm4377, board_params,
+ BCM4377_OTP_BOARD_PARAMS);
+ if (ret)
+ return ret;
+
+ if (!bcm4377->stepping[0] || !bcm4377->vendor[0])
+ return -EINVAL;
+
+ dev_dbg(&bcm4377->pdev->dev, "OTP: stepping=%s, vendor=%s\n",
+ bcm4377->stepping, bcm4377->vendor);
+ return 0;
+}
+
+static int bcm4377_parse_otp(struct bcm4377_data *bcm4377)
+{
+ u8 *otp;
+ int i;
+ int ret = -ENOENT;
+
+ otp = kzalloc(BCM4377_OTP_SIZE, GFP_KERNEL);
+ if (!otp)
+ return -ENOMEM;
+
+ for (i = 0; i < BCM4377_OTP_SIZE; ++i)
+ otp[i] = ioread8(bcm4377->bar0 + bcm4377->hw->otp_offset + i);
+
+ i = 0;
+ while (i < (BCM4377_OTP_SIZE - 1)) {
+ u8 type = otp[i];
+ u8 length = otp[i + 1];
+
+ if (type == 0)
+ break;
+
+ if ((i + 2 + length) > BCM4377_OTP_SIZE)
+ break;
+
+ switch (type) {
+ case BCM4377_OTP_SYS_VENDOR:
+ dev_dbg(&bcm4377->pdev->dev,
+ "OTP @ 0x%x (%d): SYS_VENDOR", i, length);
+ ret = bcm4377_parse_otp_sys_vendor(bcm4377, &otp[i + 2],
+ length);
+ break;
+ case BCM4377_OTP_CIS:
+ dev_dbg(&bcm4377->pdev->dev, "OTP @ 0x%x (%d): CIS", i,
+ length);
+ break;
+ default:
+ dev_dbg(&bcm4377->pdev->dev, "OTP @ 0x%x (%d): unknown",
+ i, length);
+ break;
+ }
+
+ i += 2 + length;
+ }
+
+ kfree(otp);
+ return ret;
+}
+
+static int bcm4377_init_cfg(struct bcm4377_data *bcm4377)
+{
+ int ret;
+ u32 ctrl;
+
+ ret = pci_write_config_dword(bcm4377->pdev,
+ BCM4377_PCIECFG_BAR0_WINDOW1,
+ bcm4377->hw->bar0_window1);
+ if (ret)
+ return ret;
+
+ ret = pci_write_config_dword(bcm4377->pdev,
+ BCM4377_PCIECFG_BAR0_WINDOW2,
+ bcm4377->hw->bar0_window2);
+ if (ret)
+ return ret;
+
+ ret = pci_write_config_dword(
+ bcm4377->pdev, BCM4377_PCIECFG_BAR0_CORE2_WINDOW1,
+ BCM4377_PCIECFG_BAR0_CORE2_WINDOW1_DEFAULT);
+ if (ret)
+ return ret;
+
+ if (bcm4377->hw->has_bar0_core2_window2) {
+ ret = pci_write_config_dword(bcm4377->pdev,
+ BCM4377_PCIECFG_BAR0_CORE2_WINDOW2,
+ bcm4377->hw->bar0_core2_window2);
+ if (ret)
+ return ret;
+ }
+
+ ret = pci_write_config_dword(bcm4377->pdev, BCM4377_PCIECFG_BAR2_WINDOW,
+ BCM4377_PCIECFG_BAR2_WINDOW_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_dword(bcm4377->pdev,
+ BCM4377_PCIECFG_SUBSYSTEM_CTRL, &ctrl);
+ if (ret)
+ return ret;
+
+ if (bcm4377->hw->clear_pciecfg_subsystem_ctrl_bit19)
+ ctrl &= ~BIT(19);
+ ctrl |= BIT(16);
+
+ return pci_write_config_dword(bcm4377->pdev,
+ BCM4377_PCIECFG_SUBSYSTEM_CTRL, ctrl);
+}
+
+static int bcm4377_probe_dmi(struct bcm4377_data *bcm4377)
+{
+ const struct dmi_system_id *board_type_dmi_id;
+
+ board_type_dmi_id = dmi_first_match(bcm4377_dmi_board_table);
+ if (board_type_dmi_id && board_type_dmi_id->driver_data) {
+ bcm4377->board_type = board_type_dmi_id->driver_data;
+ dev_dbg(&bcm4377->pdev->dev,
+ "found board type via DMI match: %s\n",
+ bcm4377->board_type);
+ }
+
+ return 0;
+}
+
+static int bcm4377_probe_of(struct bcm4377_data *bcm4377)
+{
+ struct device_node *np = bcm4377->pdev->dev.of_node;
+ int ret;
+
+ if (!np)
+ return 0;
+
+ ret = of_property_read_string(np, "brcm,board-type",
+ &bcm4377->board_type);
+ if (ret) {
+ dev_err(&bcm4377->pdev->dev, "no brcm,board-type property\n");
+ return ret;
+ }
+
+ bcm4377->taurus_beamforming_cal_blob =
+ of_get_property(np, "brcm,taurus-bf-cal-blob",
+ &bcm4377->taurus_beamforming_cal_size);
+ if (!bcm4377->taurus_beamforming_cal_blob) {
+ dev_err(&bcm4377->pdev->dev,
+ "no brcm,taurus-bf-cal-blob property\n");
+ return -ENOENT;
+ }
+ bcm4377->taurus_cal_blob = of_get_property(np, "brcm,taurus-cal-blob",
+ &bcm4377->taurus_cal_size);
+ if (!bcm4377->taurus_cal_blob) {
+ dev_err(&bcm4377->pdev->dev,
+ "no brcm,taurus-cal-blob property\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static void bcm4377_disable_aspm(struct bcm4377_data *bcm4377)
+{
+ pci_disable_link_state(bcm4377->pdev,
+ PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+
+ /*
+ * pci_disable_link_state can fail if either CONFIG_PCIEASPM is disabled
+ * or if the BIOS hasn't handed over control to us. We must *always*
+ * disable ASPM for this device due to hardware errata though.
+ */
+ pcie_capability_clear_word(bcm4377->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC);
+}
+
+static void bcm4377_pci_free_irq_vectors(void *data)
+{
+ pci_free_irq_vectors(data);
+}
+
+static void bcm4377_hci_free_dev(void *data)
+{
+ hci_free_dev(data);
+}
+
+static void bcm4377_hci_unregister_dev(void *data)
+{
+ hci_unregister_dev(data);
+}
+
+static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct bcm4377_data *bcm4377;
+ struct hci_dev *hdev;
+ int ret, irq;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, BCM4377_DMA_MASK);
+ if (ret)
+ return ret;
+
+ bcm4377 = devm_kzalloc(&pdev->dev, sizeof(*bcm4377), GFP_KERNEL);
+ if (!bcm4377)
+ return -ENOMEM;
+
+ bcm4377->pdev = pdev;
+ bcm4377->hw = &bcm4377_hw_variants[id->driver_data];
+ init_completion(&bcm4377->event);
+
+ ret = bcm4377_prepare_rings(bcm4377);
+ if (ret)
+ return ret;
+
+ ret = bcm4377_init_context(bcm4377);
+ if (ret)
+ return ret;
+
+ ret = bcm4377_probe_dmi(bcm4377);
+ if (ret)
+ return ret;
+ ret = bcm4377_probe_of(bcm4377);
+ if (ret)
+ return ret;
+ if (!bcm4377->board_type) {
+ dev_err(&pdev->dev, "unable to determine board type\n");
+ return -ENODEV;
+ }
+
+ if (bcm4377->hw->disable_aspm)
+ bcm4377_disable_aspm(bcm4377);
+
+ ret = pci_reset_function_locked(pdev);
+ if (ret)
+ dev_warn(
+ &pdev->dev,
+ "function level reset failed with %d; trying to continue anyway\n",
+ ret);
+
+ /*
+ * If this number is too low and we try to access any BAR too
+ * early the device will crash. Experiments have shown that
+ * approximately 50 msec is the minimum amount we have to wait.
+ * Let's double that to be safe.
+ */
+ msleep(100);
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+ pci_set_master(pdev);
+
+ ret = bcm4377_init_cfg(bcm4377);
+ if (ret)
+ return ret;
+
+ bcm4377->bar0 = pcim_iomap(pdev, 0, 0);
+ if (!bcm4377->bar0)
+ return -EBUSY;
+ bcm4377->bar2 = pcim_iomap(pdev, 2, 0);
+ if (!bcm4377->bar2)
+ return -EBUSY;
+
+ ret = bcm4377_parse_otp(bcm4377);
+ if (ret) {
+ dev_err(&pdev->dev, "Reading OTP failed with %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Legacy interrupts result in an IRQ storm because we don't know where
+ * the interrupt mask and status registers for these chips are.
+ * MSIs are acked automatically instead.
+ */
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
+ if (ret < 0)
+ return -ENODEV;
+ ret = devm_add_action_or_reset(&pdev->dev, bcm4377_pci_free_irq_vectors,
+ pdev);
+ if (ret)
+ return ret;
+
+ irq = pci_irq_vector(pdev, 0);
+ if (irq <= 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, irq, bcm4377_irq, 0, "bcm4377",
+ bcm4377);
+ if (ret)
+ return ret;
+
+ hdev = hci_alloc_dev();
+ if (!hdev)
+ return -ENOMEM;
+ ret = devm_add_action_or_reset(&pdev->dev, bcm4377_hci_free_dev, hdev);
+ if (ret)
+ return ret;
+
+ bcm4377->hdev = hdev;
+
+ hdev->bus = HCI_PCI;
+ hdev->open = bcm4377_hci_open;
+ hdev->close = bcm4377_hci_close;
+ hdev->send = bcm4377_hci_send_frame;
+ hdev->set_bdaddr = bcm4377_hci_set_bdaddr;
+ hdev->setup = bcm4377_hci_setup;
+
+ if (bcm4377->hw->broken_mws_transport_config)
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG);
+ if (bcm4377->hw->broken_ext_scan)
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
+ if (bcm4377->hw->broken_le_coded)
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_CODED);
+ if (bcm4377->hw->broken_le_ext_adv_report_phy)
+ hci_set_quirk(hdev, HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY);
+
+ pci_set_drvdata(pdev, bcm4377);
+ hci_set_drvdata(hdev, bcm4377);
+ SET_HCIDEV_DEV(hdev, &pdev->dev);
+
+ ret = bcm4377_boot(bcm4377);
+ if (ret)
+ return ret;
+
+ ret = bcm4377_setup_rti(bcm4377);
+ if (ret)
+ return ret;
+
+ ret = hci_register_dev(hdev);
+ if (ret)
+ return ret;
+ return devm_add_action_or_reset(&pdev->dev, bcm4377_hci_unregister_dev,
+ hdev);
+}
+
+static int bcm4377_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+ int ret;
+
+ ret = hci_suspend_dev(bcm4377->hdev);
+ if (ret)
+ return ret;
+
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_QUIESCE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ return 0;
+}
+
+static int bcm4377_resume(struct pci_dev *pdev)
+{
+ struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev);
+
+ iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE,
+ bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
+
+ return hci_resume_dev(bcm4377->hdev);
+}
+
+static const struct dmi_system_id bcm4377_dmi_board_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir9,1"),
+ },
+ .driver_data = "apple,formosa",
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro15,4"),
+ },
+ .driver_data = "apple,formosa",
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,3"),
+ },
+ .driver_data = "apple,formosa",
+ },
+ {}
+};
+
+static const struct bcm4377_hw bcm4377_hw_variants[] = {
+ [BCM4377] = {
+ .id = 0x4377,
+ .otp_offset = 0x4120,
+ .bar0_window1 = 0x1800b000,
+ .bar0_window2 = 0x1810c000,
+ .disable_aspm = true,
+ .broken_ext_scan = true,
+ .send_ptb = bcm4377_send_ptb,
+ },
+
+ [BCM4378] = {
+ .id = 0x4378,
+ .otp_offset = 0x4120,
+ .bar0_window1 = 0x18002000,
+ .bar0_window2 = 0x1810a000,
+ .bar0_core2_window2 = 0x18107000,
+ .has_bar0_core2_window2 = true,
+ .broken_mws_transport_config = true,
+ .broken_le_coded = true,
+ .send_calibration = bcm4378_send_calibration,
+ .send_ptb = bcm4378_send_ptb,
+ },
+
+ [BCM4387] = {
+ .id = 0x4387,
+ .otp_offset = 0x413c,
+ .bar0_window1 = 0x18002000,
+ .bar0_window2 = 0x18109000,
+ .bar0_core2_window2 = 0x18106000,
+ .has_bar0_core2_window2 = true,
+ .clear_pciecfg_subsystem_ctrl_bit19 = true,
+ .broken_mws_transport_config = true,
+ .broken_le_coded = true,
+ .broken_le_ext_adv_report_phy = true,
+ .send_calibration = bcm4387_send_calibration,
+ .send_ptb = bcm4378_send_ptb,
+ },
+
+ [BCM4388] = {
+ .id = 0x4388,
+ .otp_offset = 0x415c,
+ .bar2_offset = 0x200000,
+ .bar0_window1 = 0x18002000,
+ .bar0_window2 = 0x18109000,
+ .bar0_core2_window2 = 0x18106000,
+ .has_bar0_core2_window2 = true,
+ .broken_mws_transport_config = true,
+ .broken_le_coded = true,
+ .broken_le_ext_adv_report_phy = true,
+ .send_calibration = bcm4388_send_calibration,
+ .send_ptb = bcm4378_send_ptb,
+ },
+};
+
+#define BCM4377_DEVID_ENTRY(id) \
+ { \
+ PCI_VENDOR_ID_BROADCOM, BCM##id##_DEVICE_ID, PCI_ANY_ID, \
+ PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \
+ BCM##id \
+ }
+
+static const struct pci_device_id bcm4377_devid_table[] = {
+ BCM4377_DEVID_ENTRY(4377),
+ BCM4377_DEVID_ENTRY(4378),
+ BCM4377_DEVID_ENTRY(4387),
+ BCM4377_DEVID_ENTRY(4388),
+ {},
+};
+MODULE_DEVICE_TABLE(pci, bcm4377_devid_table);
+
+static struct pci_driver bcm4377_pci_driver = {
+ .name = "hci_bcm4377",
+ .id_table = bcm4377_devid_table,
+ .probe = bcm4377_probe,
+ .suspend = bcm4377_suspend,
+ .resume = bcm4377_resume,
+};
+module_pci_driver(bcm4377_pci_driver);
+
+MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
+MODULE_DESCRIPTION("Bluetooth support for Broadcom 4377/4378/4387/4388 devices");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_FIRMWARE("brcm/brcmbt4377*.bin");
+MODULE_FIRMWARE("brcm/brcmbt4377*.ptb");
+MODULE_FIRMWARE("brcm/brcmbt4378*.bin");
+MODULE_FIRMWARE("brcm/brcmbt4378*.ptb");
+MODULE_FIRMWARE("brcm/brcmbt4387*.bin");
+MODULE_FIRMWARE("brcm/brcmbt4387*.ptb");
+MODULE_FIRMWARE("brcm/brcmbt4388*.bin");
+MODULE_FIRMWARE("brcm/brcmbt4388*.ptb");
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index cf4a56095817..591abe6d63dd 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -25,7 +25,7 @@
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <linux/bitrev.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -378,11 +378,11 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
i++;
__skb_unlink(skb, &bcsp->unack);
- kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
}
if (skb_queue_empty(&bcsp->unack))
- del_timer(&bcsp->tbcsp);
+ timer_delete(&bcsp->tbcsp);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
@@ -582,6 +582,9 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
struct bcsp_struct *bcsp = hu->priv;
const unsigned char *ptr;
+ if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+ return -EUNATCH;
+
BT_DBG("hu %p count %d rx_state %d rx_count %ld",
hu, count, bcsp->rx_state, bcsp->rx_count);
@@ -688,7 +691,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
/* Arrange to retransmit all messages in the relq. */
static void bcsp_timed_event(struct timer_list *t)
{
- struct bcsp_struct *bcsp = from_timer(bcsp, t, tbcsp);
+ struct bcsp_struct *bcsp = timer_container_of(bcsp, t, tbcsp);
struct hci_uart *hu = bcsp->hu;
struct sk_buff *skb;
unsigned long flags;
@@ -737,7 +740,7 @@ static int bcsp_close(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
- del_timer_sync(&bcsp->tbcsp);
+ timer_shutdown_sync(&bcsp->tbcsp);
hu->priv = NULL;
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 1d0cdf023243..ec017df8572c 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -25,7 +25,7 @@
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -112,7 +112,7 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count,
+ h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count,
h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
if (IS_ERR(h4->rx_skb)) {
int err = PTR_ERR(h4->rx_skb);
@@ -151,12 +151,12 @@ int __exit h4_deinit(void)
return hci_uart_unregister_proto(&h4p);
}
-struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+struct sk_buff *h4_recv_buf(struct hci_uart *hu, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count)
{
- struct hci_uart *hu = hci_get_drvdata(hdev);
u8 alignment = hu->alignment ? hu->alignment : 1;
+ struct hci_dev *hdev = hu->hdev;
/* Check for error from previous call */
if (IS_ERR(skb))
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index c5a0409ef84f..96e20a66ecd1 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -7,11 +7,13 @@
*/
#include <linux/acpi.h>
+#include <linux/bitrev.h>
+#include <linux/crc-ccitt.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
@@ -58,6 +60,7 @@ enum {
H5_TX_ACK_REQ, /* Pending ack to send */
H5_WAKEUP_DISABLE, /* Device cannot wake host */
H5_HW_FLOW_CONTROL, /* Use HW flow control */
+ H5_CRC, /* Use CRC */
};
struct h5 {
@@ -113,6 +116,7 @@ struct h5_vnd {
int (*suspend)(struct h5 *h5);
int (*resume)(struct h5 *h5);
const struct acpi_gpio_mapping *acpi_gpio_map;
+ int sizeof_priv;
};
struct h5_device_data {
@@ -140,15 +144,15 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
static u8 h5_cfg_field(struct h5 *h5)
{
- /* Sliding window size (first 3 bits) */
- return h5->tx_win & 0x07;
+ /* Sliding window size (first 3 bits) and CRC request (fifth bit). */
+ return (h5->tx_win & 0x07) | 0x10;
}
static void h5_timed_event(struct timer_list *t)
{
const unsigned char sync_req[] = { 0x01, 0x7e };
unsigned char conf_req[3] = { 0x03, 0xfc };
- struct h5 *h5 = from_timer(h5, t, timer);
+ struct h5 *h5 = timer_container_of(h5, t, timer);
struct hci_uart *hu = h5->hu;
struct sk_buff *skb;
unsigned long flags;
@@ -196,7 +200,7 @@ static void h5_peer_reset(struct hci_uart *hu)
h5->state = H5_UNINITIALIZED;
- del_timer(&h5->timer);
+ timer_delete(&h5->timer);
skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel);
@@ -212,7 +216,6 @@ static void h5_peer_reset(struct hci_uart *hu)
static int h5_open(struct hci_uart *hu)
{
struct h5 *h5;
- const unsigned char sync[] = { 0x01, 0x7e };
BT_DBG("hu %p", hu);
@@ -242,9 +245,11 @@ static int h5_open(struct hci_uart *hu)
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
- /* Send initial sync request */
- h5_link_control(hu, sync, sizeof(sync));
- mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
+ /*
+ * Wait one jiffy because the UART layer won't set HCI_UART_PROTO_READY,
+ * which allows us to send link packets, until this function returns.
+ */
+ mod_timer(&h5->timer, jiffies + 1);
return 0;
}
@@ -253,7 +258,7 @@ static int h5_close(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
- del_timer_sync(&h5->timer);
+ timer_delete_sync(&h5->timer);
skb_queue_purge(&h5->unack);
skb_queue_purge(&h5->rel);
@@ -313,11 +318,11 @@ static void h5_pkt_cull(struct h5 *h5)
break;
__skb_unlink(skb, &h5->unack);
- kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
}
if (skb_queue_empty(&h5->unack))
- del_timer(&h5->timer);
+ timer_delete(&h5->timer);
unlock:
spin_unlock_irqrestore(&h5->unack.lock, flags);
@@ -359,8 +364,10 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
h5_link_control(hu, conf_rsp, 2);
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_rsp, 2) == 0) {
- if (H5_HDR_LEN(hdr) > 2)
+ if (H5_HDR_LEN(hdr) > 2) {
h5->tx_win = (data[2] & 0x07);
+ assign_bit(H5_CRC, &h5->flags, data[2] & 0x10);
+ }
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
h5->state = H5_ACTIVE;
hci_uart_init_ready(hu);
@@ -424,7 +431,24 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
static int h5_rx_crc(struct hci_uart *hu, unsigned char c)
{
- h5_complete_rx_pkt(hu);
+ struct h5 *h5 = hu->priv;
+ const unsigned char *hdr = h5->rx_skb->data;
+ u16 crc;
+ __be16 crc_be;
+
+ crc = crc_ccitt(0xffff, hdr, 4 + H5_HDR_LEN(hdr));
+ crc = bitrev16(crc);
+
+ crc_be = cpu_to_be16(crc);
+
+ if (memcmp(&crc_be, hdr + 4 + H5_HDR_LEN(hdr), 2) != 0) {
+ bt_dev_err(hu->hdev, "Received packet with invalid CRC");
+ h5_reset_rx(h5);
+ } else {
+ /* Remove CRC bytes */
+ skb_trim(h5->rx_skb, 4 + H5_HDR_LEN(hdr));
+ h5_complete_rx_pkt(hu);
+ }
return 0;
}
@@ -463,6 +487,8 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c)
if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_ack) {
bt_dev_err(hu->hdev, "Out-of-order packet arrived (%u != %u)",
H5_HDR_SEQ(hdr), h5->tx_ack);
+ set_bit(H5_TX_ACK_REQ, &h5->flags);
+ hci_uart_tx_wakeup(hu);
h5_reset_rx(h5);
return 0;
}
@@ -553,6 +579,7 @@ static void h5_reset_rx(struct h5 *h5)
h5->rx_func = h5_rx_delimiter;
h5->rx_pending = 0;
clear_bit(H5_RX_ESC, &h5->flags);
+ clear_bit(H5_CRC, &h5->flags);
}
static int h5_recv(struct hci_uart *hu, const void *data, int count)
@@ -589,7 +616,6 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count)
if (hu->serdev) {
pm_runtime_get(&hu->serdev->dev);
- pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
@@ -631,7 +657,6 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
if (hu->serdev) {
pm_runtime_get_sync(&hu->serdev->dev);
- pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
@@ -683,6 +708,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
struct h5 *h5 = hu->priv;
struct sk_buff *nskb;
u8 hdr[4];
+ u16 crc;
int i;
if (!valid_packet_type(pkt_type)) {
@@ -710,6 +736,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
/* Reliable packet? */
if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) {
hdr[0] |= 1 << 7;
+ hdr[0] |= (test_bit(H5_CRC, &h5->flags) && 1) << 6;
hdr[0] |= h5->tx_seq;
h5->tx_seq = (h5->tx_seq + 1) % 8;
}
@@ -729,6 +756,15 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
for (i = 0; i < len; i++)
h5_slip_one_byte(nskb, data[i]);
+ if (H5_HDR_CRC(hdr)) {
+ crc = crc_ccitt(0xffff, hdr, 4);
+ crc = crc_ccitt(crc, data, len);
+ crc = bitrev16(crc);
+
+ h5_slip_one_byte(nskb, (crc >> 8) & 0xff);
+ h5_slip_one_byte(nskb, crc & 0xff);
+ }
+
h5_slip_delim(nskb);
return nskb;
@@ -861,7 +897,8 @@ static int h5_serdev_probe(struct serdev_device *serdev)
if (IS_ERR(h5->device_wake_gpio))
return PTR_ERR(h5->device_wake_gpio);
- return hci_uart_register_device(&h5->serdev_hu, &h5p);
+ return hci_uart_register_device_priv(&h5->serdev_hu, &h5p,
+ h5->vnd->sizeof_priv);
}
static void h5_serdev_remove(struct serdev_device *serdev)
@@ -936,6 +973,8 @@ static int h5_btrtl_setup(struct h5 *h5)
err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev);
/* Give the device some time before the hci-core sends it a reset */
usleep_range(10000, 20000);
+ if (err)
+ goto out_free;
btrtl_set_quirks(h5->hu->hdev, btrtl_dev);
@@ -1066,6 +1105,7 @@ static struct h5_vnd rtl_vnd = {
.suspend = h5_btrtl_suspend,
.resume = h5_btrtl_resume,
.acpi_gpio_map = acpi_btrtl_gpios,
+ .sizeof_priv = sizeof(struct btrealtek_data),
};
static const struct h5_device_data h5_data_rtl8822cs = {
@@ -1100,6 +1140,8 @@ static const struct of_device_id rtl_bluetooth_of_match[] = {
.data = (const void *)&h5_data_rtl8822cs },
{ .compatible = "realtek,rtl8723bs-bt",
.data = (const void *)&h5_data_rtl8723bs },
+ { .compatible = "realtek,rtl8723cs-bt",
+ .data = (const void *)&h5_data_rtl8723bs },
{ .compatible = "realtek,rtl8723ds-bt",
.data = (const void *)&h5_data_rtl8723bs },
#endif
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 78afb9a348e7..20baf2895dec 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -280,7 +280,6 @@ static irqreturn_t intel_irq(int irq, void *dev_id)
/* Host/Controller are now LPM resumed, trigger a new delayed suspend */
pm_runtime_get(&idev->pdev->dev);
- pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
return IRQ_HANDLED;
@@ -371,7 +370,6 @@ static void intel_busy_work(struct work_struct *work)
list_for_each_entry(idev, &intel_device_list, list) {
if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {
pm_runtime_get(&idev->pdev->dev);
- pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
break;
}
@@ -537,7 +535,7 @@ static int intel_setup(struct hci_uart *hu)
int speed_change = 0;
int err;
- bt_dev_dbg(hdev, "start intel_setup");
+ bt_dev_dbg(hdev, "");
hu->hdev->set_diag = btintel_set_diag;
hu->hdev->set_bdaddr = btintel_set_bdaddr;
@@ -591,12 +589,12 @@ static int intel_setup(struct hci_uart *hu)
return -EINVAL;
}
- /* Check for supported iBT hardware variants of this firmware
- * loading method.
- *
- * This check has been put in place to ensure correct forward
- * compatibility options when newer hardware variants come along.
- */
+ /* Check for supported iBT hardware variants of this firmware
+ * loading method.
+ *
+ * This check has been put in place to ensure correct forward
+ * compatibility options when newer hardware variants come along.
+ */
switch (ver.hw_variant) {
case 0x0b: /* LnP */
case 0x0c: /* WsP */
@@ -660,7 +658,7 @@ static int intel_setup(struct hci_uart *hu)
*/
if (!bacmp(&params.otp_bdaddr, BDADDR_ANY)) {
bt_dev_info(hdev, "No device address configured");
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_INVALID_BDADDR);
}
/* With this Intel bootloader only the hardware variant and device
@@ -777,7 +775,7 @@ static int intel_setup(struct hci_uart *hu)
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration);
@@ -822,7 +820,7 @@ done:
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
bt_dev_info(hdev, "Device booted in %llu usecs", duration);
@@ -972,11 +970,12 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- intel->rx_skb = h4_recv_buf(hu->hdev, intel->rx_skb, data, count,
+ intel->rx_skb = h4_recv_buf(hu, intel->rx_skb, data, count,
intel_recv_pkts,
ARRAY_SIZE(intel_recv_pkts));
if (IS_ERR(intel->rx_skb)) {
int err = PTR_ERR(intel->rx_skb);
+
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
intel->rx_skb = NULL;
return err;
@@ -1002,7 +1001,6 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
list_for_each_entry(idev, &intel_device_list, list) {
if (hu->tty->dev->parent == idev->pdev->dev.parent) {
pm_runtime_get_sync(&idev->pdev->dev);
- pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
break;
}
@@ -1028,12 +1026,12 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
struct hci_command_hdr *cmd = (void *)skb->data;
__u16 opcode = le16_to_cpu(cmd->opcode);
- /* When the 0xfc01 command is issued to boot into
- * the operational firmware, it will actually not
- * send a command complete event. To keep the flow
- * control working inject that event here.
+ /* When the BTINTEL_HCI_OP_RESET command is issued to boot into
+ * the operational firmware, it will actually not send a command
+ * complete event. To keep the flow control working inject that
+ * event here.
*/
- if (opcode == 0xfc01)
+ if (opcode == BTINTEL_HCI_OP_RESET)
inject_cmd_complete(hu->hdev, opcode);
}
@@ -1190,7 +1188,7 @@ no_irq:
return 0;
}
-static int intel_remove(struct platform_device *pdev)
+static void intel_remove(struct platform_device *pdev)
{
struct intel_device *idev = platform_get_drvdata(pdev);
@@ -1201,8 +1199,6 @@ static int intel_remove(struct platform_device *pdev)
mutex_unlock(&intel_device_list_lock);
dev_info(&pdev->dev, "unregistered.\n");
-
- return 0;
}
static struct platform_driver intel_driver = {
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index f537673ede17..d0adae3267b4 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -102,7 +102,8 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
if (!skb) {
percpu_down_read(&hu->proto_lock);
- if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ if (test_bit(HCI_UART_PROTO_READY, &hu->flags) ||
+ test_bit(HCI_UART_PROTO_INIT, &hu->flags))
skb = hu->proto->dequeue(hu);
percpu_up_read(&hu->proto_lock);
@@ -124,7 +125,8 @@ int hci_uart_tx_wakeup(struct hci_uart *hu)
if (!percpu_down_read_trylock(&hu->proto_lock))
return 0;
- if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags) &&
+ !test_bit(HCI_UART_PROTO_INIT, &hu->flags))
goto no_schedule;
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
@@ -278,7 +280,8 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
percpu_down_read(&hu->proto_lock);
- if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags) &&
+ !test_bit(HCI_UART_PROTO_INIT, &hu->flags)) {
percpu_up_read(&hu->proto_lock);
return -EUNATCH;
}
@@ -323,9 +326,9 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
/* Disable hardware flow control */
ktermios = tty->termios;
ktermios.c_cflag &= ~CRTSCTS;
- status = tty_set_termios(tty, &ktermios);
+ tty_set_termios(tty, &ktermios);
BT_DBG("Disabling hardware flow control: %s",
- status ? "failed" : "success");
+ (tty->termios.c_cflag & CRTSCTS) ? "failed" : "success");
/* Clear RTS to prevent the device from sending */
/* Most UARTs need OUT2 to enable interrupts */
@@ -357,9 +360,9 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
/* Re-enable hardware flow control */
ktermios = tty->termios;
ktermios.c_cflag |= CRTSCTS;
- status = tty_set_termios(tty, &ktermios);
+ tty_set_termios(tty, &ktermios);
BT_DBG("Enabling hardware flow control: %s",
- status ? "failed" : "success");
+ !(tty->termios.c_cflag & CRTSCTS) ? "failed" : "success");
}
}
@@ -488,11 +491,16 @@ static int hci_uart_tty_open(struct tty_struct *tty)
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
- hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL);
+ hu = kzalloc(sizeof(*hu), GFP_KERNEL);
if (!hu) {
BT_ERR("Can't allocate control structure");
return -ENFILE;
}
+ if (percpu_init_rwsem(&hu->proto_lock)) {
+ BT_ERR("Can't allocate semaphore structure");
+ kfree(hu);
+ return -ENOMEM;
+ }
tty->disc_data = hu;
hu->tty = tty;
@@ -502,11 +510,12 @@ static int hci_uart_tty_open(struct tty_struct *tty)
hu->alignment = 1;
hu->padding = 0;
+ /* Use serial port speed as oper_speed */
+ hu->oper_speed = tty->termios.c_ospeed;
+
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
- percpu_init_rwsem(&hu->proto_lock);
-
/* Flush any pending characters in the driver */
tty_driver_flush_buffer(tty);
@@ -579,7 +588,8 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
if (tty != hu->tty)
return;
- if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ if (test_bit(HCI_UART_PROTO_READY, &hu->flags) ||
+ test_bit(HCI_UART_PROTO_INIT, &hu->flags))
hci_uart_tx_wakeup(hu);
}
@@ -588,7 +598,7 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
* Called by tty low level driver when receive data is
* available.
*
- * Arguments: tty pointer to tty isntance data
+ * Arguments: tty pointer to tty instance data
* data pointer to received data
* flags pointer to flags for data
* count count of received data in bytes
@@ -596,7 +606,7 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
* Return Value: None
*/
static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
- const char *flags, int count)
+ const u8 *flags, size_t count)
{
struct hci_uart *hu = tty->disc_data;
@@ -605,7 +615,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
percpu_down_read(&hu->proto_lock);
- if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags) &&
+ !test_bit(HCI_UART_PROTO_INIT, &hu->flags)) {
percpu_up_read(&hu->proto_lock);
return;
}
@@ -656,18 +667,13 @@ static int hci_uart_register_dev(struct hci_uart *hu)
SET_HCIDEV_DEV(hdev, hu->tty->dev);
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
- set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE);
if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
- set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_EXTERNAL_CONFIG);
if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
- set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
-
- if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
- hdev->dev_type = HCI_AMP;
- else
- hdev->dev_type = HCI_PRIMARY;
+ hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
/* Only call open() for the protocol after hdev is fully initialized as
* open() (or a timer/workqueue it starts) may attempt to reference it.
@@ -706,12 +712,16 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
hu->proto = p;
+ set_bit(HCI_UART_PROTO_INIT, &hu->flags);
+
err = hci_uart_register_dev(hu);
if (err) {
return err;
}
set_bit(HCI_UART_PROTO_READY, &hu->flags);
+ clear_bit(HCI_UART_PROTO_INIT, &hu->flags);
+
return 0;
}
@@ -719,7 +729,6 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
{
unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) |
BIT(HCI_UART_RESET_ON_INIT) |
- BIT(HCI_UART_CREATE_AMP) |
BIT(HCI_UART_INIT_PENDING) |
BIT(HCI_UART_EXT_CONFIG) |
BIT(HCI_UART_VND_DETECT);
@@ -767,7 +776,8 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
break;
case HCIUARTGETPROTO:
- if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags) &&
+ test_bit(HCI_UART_PROTO_READY, &hu->flags))
err = hu->proto->id;
else
err = -EUNATCH;
@@ -803,20 +813,14 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
* We don't provide read/write/poll interface for user space.
*/
static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
- unsigned char *buf, size_t nr,
- void **cookie, unsigned long offset)
+ u8 *buf, size_t nr, void **cookie,
+ unsigned long offset)
{
return 0;
}
static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
- const unsigned char *data, size_t count)
-{
- return 0;
-}
-
-static __poll_t hci_uart_tty_poll(struct tty_struct *tty,
- struct file *filp, poll_table *wait)
+ const u8 *data, size_t count)
{
return 0;
}
@@ -831,7 +835,6 @@ static struct tty_ldisc_ops hci_uart_ldisc = {
.write = hci_uart_tty_write,
.ioctl = hci_uart_tty_ioctl,
.compat_ioctl = hci_uart_tty_ioctl,
- .poll = hci_uart_tty_poll,
.receive_buf = hci_uart_tty_receive,
.write_wakeup = hci_uart_tty_wakeup,
};
@@ -879,7 +882,9 @@ static int __init hci_uart_init(void)
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_init();
#endif
-
+#ifdef CONFIG_BT_HCIUART_AML
+ aml_init();
+#endif
return 0;
}
@@ -915,7 +920,9 @@ static void __exit hci_uart_exit(void)
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_deinit();
#endif
-
+#ifdef CONFIG_BT_HCIUART_AML
+ aml_deinit();
+#endif
tty_unregister_ldisc(&hci_uart_ldisc);
}
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 4eb420a9ed04..6f4e25917b86 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -305,7 +305,7 @@ static void ll_device_woke_up(struct hci_uart *hu)
hci_uart_tx_wakeup(hu);
}
-/* Enqueue frame for transmittion (padding, crc, etc) */
+/* Enqueue frame for transmission (padding, crc, etc) */
/* may be called from two simultaneous tasklets */
static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
@@ -345,7 +345,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
default:
BT_ERR("illegal hcill state: %ld (losing packet)",
ll->hcill_state);
- kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
break;
}
@@ -429,7 +429,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- ll->rx_skb = h4_recv_buf(hu->hdev, ll->rx_skb, data, count,
+ ll->rx_skb = h4_recv_buf(hu, ll->rx_skb, data, count,
ll_recv_pkts, ARRAY_SIZE(ll_recv_pkts));
if (IS_ERR(ll->rx_skb)) {
int err = PTR_ERR(ll->rx_skb);
@@ -649,11 +649,11 @@ static int ll_setup(struct hci_uart *hu)
/* This means that there was an error getting the BD address
* during probe, so mark the device as having a bad address.
*/
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
+ hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR);
} else if (bacmp(&lldev->bdaddr, BDADDR_ANY)) {
err = ll_set_bdaddr(hu->hdev, &lldev->bdaddr);
if (err)
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
+ hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR);
}
/* Operational speed if any */
@@ -786,7 +786,7 @@ MODULE_DEVICE_TABLE(of, hci_ti_of_match);
static struct serdev_device_driver hci_ti_drv = {
.driver = {
.name = "hci-ti",
- .of_match_table = of_match_ptr(hci_ti_of_match),
+ .of_match_table = hci_ti_of_match,
},
.probe = hci_ti_probe,
.remove = hci_ti_remove,
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
index fbc3f7c3a5c7..8767522ec4c6 100644
--- a/drivers/bluetooth/hci_mrvl.c
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -27,10 +27,12 @@
#define MRVL_ACK 0x5A
#define MRVL_NAK 0xBF
#define MRVL_RAW_DATA 0x1F
+#define MRVL_SET_BAUDRATE 0xFC09
enum {
STATE_CHIP_VER_PENDING,
STATE_FW_REQ_PENDING,
+ STATE_FW_LOADED,
};
struct mrvl_data {
@@ -254,9 +256,17 @@ static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
- mrvl_recv_pkts,
- ARRAY_SIZE(mrvl_recv_pkts));
+ /* We might receive some noise when there is no firmware loaded. Therefore,
+ * we drop data if the firmware is not loaded yet and if there is no fw load
+ * request pending.
+ */
+ if (!test_bit(STATE_FW_REQ_PENDING, &mrvl->flags) &&
+ !test_bit(STATE_FW_LOADED, &mrvl->flags))
+ return count;
+
+ mrvl->rx_skb = h4_recv_buf(hu, mrvl->rx_skb, data, count,
+ mrvl_recv_pkts,
+ ARRAY_SIZE(mrvl_recv_pkts));
if (IS_ERR(mrvl->rx_skb)) {
int err = PTR_ERR(mrvl->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
@@ -354,6 +364,7 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
static int mrvl_setup(struct hci_uart *hu)
{
int err;
+ struct mrvl_data *mrvl = hu->priv;
hci_uart_set_flow_control(hu, true);
@@ -367,9 +378,9 @@ static int mrvl_setup(struct hci_uart *hu)
hci_uart_wait_until_sent(hu);
if (hu->serdev)
- serdev_device_set_baudrate(hu->serdev, 3000000);
+ serdev_device_set_baudrate(hu->serdev, hu->oper_speed);
else
- hci_uart_set_baudrate(hu, 3000000);
+ hci_uart_set_baudrate(hu, hu->oper_speed);
hci_uart_set_flow_control(hu, false);
@@ -377,13 +388,54 @@ static int mrvl_setup(struct hci_uart *hu)
if (err)
return err;
+ set_bit(STATE_FW_LOADED, &mrvl->flags);
+
return 0;
}
-static const struct hci_uart_proto mrvl_proto = {
+static int mrvl_set_baudrate(struct hci_uart *hu, unsigned int speed)
+{
+ int err;
+ struct mrvl_data *mrvl = hu->priv;
+ __le32 speed_le = cpu_to_le32(speed);
+
+ /* The firmware might be loaded by the Wifi driver over SDIO. We wait
+ * up to 10s for the CTS to go up. Afterward, we know that the firmware
+ * is ready.
+ */
+ err = serdev_device_wait_for_cts(hu->serdev, true, 10000);
+ if (err) {
+ bt_dev_err(hu->hdev, "Wait for CTS failed with %d\n", err);
+ return err;
+ }
+
+ set_bit(STATE_FW_LOADED, &mrvl->flags);
+
+ err = __hci_cmd_sync_status(hu->hdev, MRVL_SET_BAUDRATE,
+ sizeof(speed_le), &speed_le,
+ HCI_INIT_TIMEOUT);
+ if (err) {
+ bt_dev_err(hu->hdev, "send command failed: %d", err);
+ return err;
+ }
+
+ serdev_device_set_baudrate(hu->serdev, speed);
+
+ /* We forcefully have to send a command to the bluetooth module so that
+ * the driver detects it after a baudrate change. This is foreseen by
+ * hci_serdev by setting HCI_UART_VND_DETECT which then causes a dummy
+ * local version read.
+ */
+ set_bit(HCI_UART_VND_DETECT, &hu->hdev_flags);
+
+ return 0;
+}
+
+static const struct hci_uart_proto mrvl_proto_8897 = {
.id = HCI_UART_MRVL,
.name = "Marvell",
.init_speed = 115200,
+ .oper_speed = 3000000,
.open = mrvl_open,
.close = mrvl_close,
.flush = mrvl_flush,
@@ -393,18 +445,37 @@ static const struct hci_uart_proto mrvl_proto = {
.dequeue = mrvl_dequeue,
};
+static const struct hci_uart_proto mrvl_proto_8997 = {
+ .id = HCI_UART_MRVL,
+ .name = "Marvell 8997",
+ .init_speed = 115200,
+ .oper_speed = 3000000,
+ .open = mrvl_open,
+ .close = mrvl_close,
+ .flush = mrvl_flush,
+ .set_baudrate = mrvl_set_baudrate,
+ .recv = mrvl_recv,
+ .enqueue = mrvl_enqueue,
+ .dequeue = mrvl_dequeue,
+};
+
static int mrvl_serdev_probe(struct serdev_device *serdev)
{
struct mrvl_serdev *mrvldev;
+ const struct hci_uart_proto *mrvl_proto = device_get_match_data(&serdev->dev);
mrvldev = devm_kzalloc(&serdev->dev, sizeof(*mrvldev), GFP_KERNEL);
if (!mrvldev)
return -ENOMEM;
+ mrvldev->hu.oper_speed = mrvl_proto->oper_speed;
+ if (mrvl_proto->set_baudrate)
+ of_property_read_u32(serdev->dev.of_node, "max-speed", &mrvldev->hu.oper_speed);
+
mrvldev->hu.serdev = serdev;
serdev_device_set_drvdata(serdev, mrvldev);
- return hci_uart_register_device(&mrvldev->hu, &mrvl_proto);
+ return hci_uart_register_device(&mrvldev->hu, mrvl_proto);
}
static void mrvl_serdev_remove(struct serdev_device *serdev)
@@ -414,13 +485,12 @@ static void mrvl_serdev_remove(struct serdev_device *serdev)
hci_uart_unregister_device(&mrvldev->hu);
}
-#ifdef CONFIG_OF
-static const struct of_device_id mrvl_bluetooth_of_match[] = {
- { .compatible = "mrvl,88w8897" },
+static const struct of_device_id __maybe_unused mrvl_bluetooth_of_match[] = {
+ { .compatible = "mrvl,88w8897", .data = &mrvl_proto_8897},
+ { .compatible = "mrvl,88w8997", .data = &mrvl_proto_8997},
{ },
};
MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match);
-#endif
static struct serdev_device_driver mrvl_serdev_driver = {
.probe = mrvl_serdev_probe,
@@ -435,12 +505,12 @@ int __init mrvl_init(void)
{
serdev_device_driver_register(&mrvl_serdev_driver);
- return hci_uart_register_proto(&mrvl_proto);
+ return hci_uart_register_proto(&mrvl_proto_8897);
}
int __exit mrvl_deinit(void)
{
serdev_device_driver_unregister(&mrvl_serdev_driver);
- return hci_uart_unregister_proto(&mrvl_proto);
+ return hci_uart_unregister_proto(&mrvl_proto_8897);
}
diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c
index 05f7f6de6863..1e65b541f8ad 100644
--- a/drivers/bluetooth/hci_nokia.c
+++ b/drivers/bluetooth/hci_nokia.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -116,11 +116,6 @@ struct hci_nokia_neg_evt {
#define SETUP_BAUD_RATE 921600
#define INIT_BAUD_RATE 120000
-struct hci_nokia_radio_hdr {
- u8 evt;
- u8 dlen;
-} __packed;
-
struct nokia_bt_dev {
struct hci_uart hu;
struct serdev_device *serdev;
@@ -444,7 +439,7 @@ static int nokia_setup(struct hci_uart *hu)
if (btdev->man_id == NOKIA_ID_BCM2048) {
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
+ hci_set_quirk(hu->hdev, HCI_QUIRK_INVALID_BDADDR);
dev_dbg(dev, "bcm2048 has invalid bluetooth address!");
}
@@ -506,7 +501,7 @@ static int nokia_close(struct hci_uart *hu)
return 0;
}
-/* Enqueue frame for transmittion (padding, crc, etc) */
+/* Enqueue frame for transmission (padding, crc, etc) */
static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct nokia_bt_dev *btdev = hu->priv;
@@ -629,8 +624,8 @@ static int nokia_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count,
- nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
+ btdev->rx_skb = h4_recv_buf(hu, btdev->rx_skb, data, count,
+ nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
if (IS_ERR(btdev->rx_skb)) {
err = PTR_ERR(btdev->rx_skb);
dev_err(dev, "Frame reassembly failed (%d)", err);
@@ -734,7 +729,11 @@ static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
return err;
}
- clk_prepare_enable(sysclk);
+ err = clk_prepare_enable(sysclk);
+ if (err) {
+ dev_err(dev, "could not enable sysclk: %d", err);
+ return err;
+ }
btdev->sysclk_speed = clk_get_rate(sysclk);
clk_disable_unprepare(sysclk);
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 8df11016fd51..888176b0faa9 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -25,13 +25,15 @@
#include <linux/gpio/consumer.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/serdev.h>
+#include <linux/string_choices.h>
#include <linux/mutex.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -78,7 +80,8 @@ enum qca_flags {
QCA_HW_ERROR_EVENT,
QCA_SSR_TRIGGERED,
QCA_BT_OFF,
- QCA_ROM_FW
+ QCA_ROM_FW,
+ QCA_DEBUGFS_CREATED,
};
enum qca_capabilities {
@@ -116,9 +119,7 @@ enum qca_memdump_states {
QCA_MEMDUMP_TIMEOUT,
};
-struct qca_memdump_data {
- char *memdump_buf_head;
- char *memdump_buf_tail;
+struct qca_memdump_info {
u32 current_seq_no;
u32 received_dump;
u32 ram_dump_size;
@@ -128,13 +129,13 @@ struct qca_memdump_event_hdr {
__u8 evt;
__u8 plen;
__u16 opcode;
- __u16 seq_no;
+ __le16 seq_no;
__u8 reserved;
} __packed;
struct qca_dump_size {
- u32 dump_size;
+ __le32 dump_size;
} __packed;
struct qca_data {
@@ -159,13 +160,15 @@ struct qca_data {
struct work_struct ws_tx_vote_off;
struct work_struct ctrl_memdump_evt;
struct delayed_work ctrl_memdump_timeout;
- struct qca_memdump_data *qca_memdump;
+ struct qca_memdump_info *qca_memdump;
unsigned long flags;
struct completion drop_ev_comp;
wait_queue_head_t suspend_wait_q;
enum qca_memdump_states memdump_state;
struct mutex hci_memdump_lock;
+ u16 fw_version;
+ u16 controller_id;
/* For debugging purpose */
u64 ibs_sent_wacks;
u64 ibs_sent_slps;
@@ -213,6 +216,7 @@ struct qca_power {
struct regulator_bulk_data *vreg_bulk;
int num_vregs;
bool vregs_on;
+ struct pwrseq_desc *pwrseq;
};
struct qca_serdev {
@@ -224,7 +228,8 @@ struct qca_serdev {
struct qca_power *bt_power;
u32 init_speed;
u32 oper_speed;
- const char *firmware_name;
+ bool bdaddr_property_broken;
+ const char *firmware_name[2];
};
static int qca_regulator_enable(struct qca_serdev *qcadev);
@@ -232,6 +237,7 @@ static void qca_regulator_disable(struct qca_serdev *qcadev);
static void qca_power_shutdown(struct hci_uart *hu);
static int qca_power_off(struct hci_dev *hdev);
static void qca_controller_memdump(struct work_struct *work);
+static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb);
static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu)
{
@@ -253,7 +259,18 @@ static const char *qca_get_firmware_name(struct hci_uart *hu)
if (hu->serdev) {
struct qca_serdev *qsd = serdev_device_get_drvdata(hu->serdev);
- return qsd->firmware_name;
+ return qsd->firmware_name[0];
+ } else {
+ return NULL;
+ }
+}
+
+static const char *qca_get_rampatch_name(struct hci_uart *hu)
+{
+ if (hu->serdev) {
+ struct qca_serdev *qsd = serdev_device_get_drvdata(hu->serdev);
+
+ return qsd->firmware_name[1];
} else {
return NULL;
}
@@ -327,8 +344,8 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu)
else
__serial_clock_off(hu->tty);
- BT_DBG("Vote serial clock %s(%s)", new_vote ? "true" : "false",
- vote ? "true" : "false");
+ BT_DBG("Vote serial clock %s(%s)", str_true_false(new_vote),
+ str_true_false(vote));
diff = jiffies_to_msecs(jiffies - qca->vote_last_jif);
@@ -457,7 +474,7 @@ static void qca_wq_serial_tx_clock_vote_off(struct work_struct *work)
static void hci_ibs_tx_idle_timeout(struct timer_list *t)
{
- struct qca_data *qca = from_timer(qca, t, tx_idle_timer);
+ struct qca_data *qca = timer_container_of(qca, t, tx_idle_timer);
struct hci_uart *hu = qca->hu;
unsigned long flags;
@@ -490,7 +507,7 @@ static void hci_ibs_tx_idle_timeout(struct timer_list *t)
static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
{
- struct qca_data *qca = from_timer(qca, t, wake_retrans_timer);
+ struct qca_data *qca = timer_container_of(qca, t, wake_retrans_timer);
struct hci_uart *hu = qca->hu;
unsigned long flags, retrans_delay;
bool retransmit = false;
@@ -566,7 +583,7 @@ static int qca_open(struct hci_uart *hu)
if (!hci_uart_has_flow_control(hu))
return -EOPNOTSUPP;
- qca = kzalloc(sizeof(struct qca_data), GFP_KERNEL);
+ qca = kzalloc(sizeof(*qca), GFP_KERNEL);
if (!qca)
return -ENOMEM;
@@ -605,9 +622,19 @@ static int qca_open(struct hci_uart *hu)
if (hu->serdev) {
qcadev = serdev_device_get_drvdata(hu->serdev);
- if (qca_is_wcn399x(qcadev->btsoc_type) ||
- qca_is_wcn6750(qcadev->btsoc_type))
+ switch (qcadev->btsoc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
hu->init_speed = qcadev->init_speed;
+ break;
+
+ default:
+ break;
+ }
if (qcadev->oper_speed)
hu->oper_speed = qcadev->oper_speed;
@@ -635,6 +662,9 @@ static void qca_debugfs_init(struct hci_dev *hdev)
if (!hdev->debugfs)
return;
+ if (test_and_set_bit(QCA_DEBUGFS_CREATED, &qca->flags))
+ return;
+
ibs_dir = debugfs_create_dir("ibs", hdev->debugfs);
/* read only */
@@ -696,9 +726,15 @@ static int qca_close(struct hci_uart *hu)
skb_queue_purge(&qca->tx_wait_q);
skb_queue_purge(&qca->txq);
skb_queue_purge(&qca->rx_memdump_q);
+ /*
+ * Shut the timers down so they can't be rearmed when
+ * destroy_workqueue() drains pending work which in turn might try
+ * to arm a timer. After shutdown rearm attempts are silently
+ * ignored by the timer core code.
+ */
+ timer_shutdown_sync(&qca->tx_idle_timer);
+ timer_shutdown_sync(&qca->wake_retrans_timer);
destroy_workqueue(qca->workqueue);
- del_timer_sync(&qca->tx_idle_timer);
- del_timer_sync(&qca->wake_retrans_timer);
qca->hu = NULL;
kfree_skb(qca->rx_skb);
@@ -831,7 +867,7 @@ static void device_woke_up(struct hci_uart *hu)
skb_queue_tail(&qca->txq, skb);
/* Switch timers and change state to HCI_IBS_TX_AWAKE */
- del_timer(&qca->wake_retrans_timer);
+ timer_delete(&qca->wake_retrans_timer);
idle_delay = msecs_to_jiffies(qca->tx_idle_delay);
mod_timer(&qca->tx_idle_timer, jiffies + idle_delay);
qca->tx_ibs_state = HCI_IBS_TX_AWAKE;
@@ -850,7 +886,7 @@ static void device_woke_up(struct hci_uart *hu)
hci_uart_tx_wakeup(hu);
}
-/* Enqueue frame for transmittion (padding, crc, etc) may be called from
+/* Enqueue frame for transmission (padding, crc, etc) may be called from
* two simultaneous tasklets.
*/
static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
@@ -912,7 +948,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
default:
BT_ERR("Illegal tx state: %d (losing packet)",
qca->tx_ibs_state);
- kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
break;
}
@@ -970,6 +1006,28 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
+static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ char buf[80];
+
+ snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n",
+ qca->controller_id);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n",
+ qca->fw_version);
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Vendor:Qualcomm\n");
+ skb_put_data(skb, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "Driver: %s\n",
+ hu->serdev->dev.driver->name);
+ skb_put_data(skb, buf, strlen(buf));
+}
+
static void qca_controller_memdump(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
@@ -977,13 +1035,11 @@ static void qca_controller_memdump(struct work_struct *work)
struct hci_uart *hu = qca->hu;
struct sk_buff *skb;
struct qca_memdump_event_hdr *cmd_hdr;
- struct qca_memdump_data *qca_memdump = qca->qca_memdump;
+ struct qca_memdump_info *qca_memdump = qca->qca_memdump;
struct qca_dump_size *dump;
- char *memdump_buf;
- char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
u16 seq_no;
- u32 dump_size;
u32 rx_size;
+ int ret = 0;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
@@ -999,8 +1055,7 @@ static void qca_controller_memdump(struct work_struct *work)
}
if (!qca_memdump) {
- qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
- GFP_ATOMIC);
+ qca_memdump = kzalloc(sizeof(*qca_memdump), GFP_ATOMIC);
if (!qca_memdump) {
mutex_unlock(&qca->hci_memdump_lock);
return;
@@ -1017,7 +1072,7 @@ static void qca_controller_memdump(struct work_struct *work)
if (!seq_no) {
/* This is the first frame of memdump packet from
- * the controller, Disable IBS to recevie dump
+ * the controller, Disable IBS to receive dump
* with out any interruption, ideally time required for
* the controller to send the dump is 8 seconds. let us
* start timer to handle this asynchronous activity.
@@ -1025,44 +1080,50 @@ static void qca_controller_memdump(struct work_struct *work)
set_bit(QCA_IBS_DISABLED, &qca->flags);
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
dump = (void *) skb->data;
- dump_size = __le32_to_cpu(dump->dump_size);
- if (!(dump_size)) {
+ qca_memdump->ram_dump_size = __le32_to_cpu(dump->dump_size);
+ if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
kfree_skb(skb);
- qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
- bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
- dump_size);
queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
- msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
- );
-
- skb_pull(skb, sizeof(dump_size));
- memdump_buf = vmalloc(dump_size);
- qca_memdump->ram_dump_size = dump_size;
- qca_memdump->memdump_buf_head = memdump_buf;
- qca_memdump->memdump_buf_tail = memdump_buf;
- }
+ msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
+ skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
+ qca_memdump->current_seq_no = 0;
+ qca_memdump->received_dump = 0;
+ ret = hci_devcd_init(hu->hdev, qca_memdump->ram_dump_size);
+ bt_dev_info(hu->hdev, "hci_devcd_init Return:%d",
+ ret);
+ if (ret < 0) {
+ kfree(qca->qca_memdump);
+ qca->qca_memdump = NULL;
+ qca->memdump_state = QCA_MEMDUMP_COLLECTED;
+ cancel_delayed_work(&qca->ctrl_memdump_timeout);
+ clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+ clear_bit(QCA_IBS_DISABLED, &qca->flags);
+ mutex_unlock(&qca->hci_memdump_lock);
+ return;
+ }
+
+ bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
+ qca_memdump->ram_dump_size);
- memdump_buf = qca_memdump->memdump_buf_tail;
+ }
/* If sequence no 0 is missed then there is no point in
* accepting the other sequences.
*/
- if (!memdump_buf) {
+ if (!test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
bt_dev_err(hu->hdev, "QCA: Discarding other packets");
kfree(qca_memdump);
kfree_skb(skb);
- qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
-
/* There could be chance of missing some packets from
* the controller. In such cases let us store the dummy
* packets in the buffer.
@@ -1072,8 +1133,8 @@ static void qca_controller_memdump(struct work_struct *work)
* bits, so skip this checking for missing packet.
*/
while ((seq_no > qca_memdump->current_seq_no + 1) &&
- (soc_type != QCA_QCA6390) &&
- seq_no != QCA_LAST_SEQUENCE_NUM) {
+ (soc_type != QCA_QCA6390) &&
+ seq_no != QCA_LAST_SEQUENCE_NUM) {
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
qca_memdump->current_seq_no);
rx_size = qca_memdump->received_dump;
@@ -1084,43 +1145,38 @@ static void qca_controller_memdump(struct work_struct *work)
qca_memdump->received_dump);
break;
}
- memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
- memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
+ hci_devcd_append_pattern(hu->hdev, 0x00,
+ QCA_DUMP_PACKET_SIZE);
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
qca_memdump->current_seq_no++;
}
- rx_size = qca_memdump->received_dump + skb->len;
+ rx_size = qca_memdump->received_dump + skb->len;
if (rx_size <= qca_memdump->ram_dump_size) {
if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
- (seq_no != qca_memdump->current_seq_no))
+ (seq_no != qca_memdump->current_seq_no)) {
bt_dev_err(hu->hdev,
"QCA memdump unexpected packet %d",
seq_no);
+ }
bt_dev_dbg(hu->hdev,
"QCA memdump packet %d with length %d",
seq_no, skb->len);
- memcpy(memdump_buf, (unsigned char *)skb->data,
- skb->len);
- memdump_buf = memdump_buf + skb->len;
- qca_memdump->memdump_buf_tail = memdump_buf;
- qca_memdump->current_seq_no = seq_no + 1;
- qca_memdump->received_dump += skb->len;
+ hci_devcd_append(hu->hdev, skb);
+ qca_memdump->current_seq_no += 1;
+ qca_memdump->received_dump = rx_size;
} else {
bt_dev_err(hu->hdev,
- "QCA memdump received %d, no space for packet %d",
- qca_memdump->received_dump, seq_no);
+ "QCA memdump received no space for packet %d",
+ qca_memdump->current_seq_no);
}
- qca->qca_memdump = qca_memdump;
- kfree_skb(skb);
+
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
bt_dev_info(hu->hdev,
- "QCA memdump Done, received %d, total %d",
- qca_memdump->received_dump,
- qca_memdump->ram_dump_size);
- memdump_buf = qca_memdump->memdump_buf_head;
- dev_coredumpv(&hu->serdev->dev, memdump_buf,
- qca_memdump->received_dump, GFP_KERNEL);
+ "QCA memdump Done, received %d, total %d",
+ qca_memdump->received_dump,
+ qca_memdump->ram_dump_size);
+ hci_devcd_complete(hu->hdev);
cancel_delayed_work(&qca->ctrl_memdump_timeout);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
@@ -1208,6 +1264,7 @@ static const struct h4_recv_pkt qca_recv_pkts[] = {
{ H4_RECV_ACL, .recv = qca_recv_acl_data },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = qca_recv_event },
+ { H4_RECV_ISO, .recv = hci_recv_frame },
{ QCA_IBS_WAKE_IND_EVENT, .recv = qca_ibs_wake_ind },
{ QCA_IBS_WAKE_ACK_EVENT, .recv = qca_ibs_wake_ack },
{ QCA_IBS_SLEEP_IND_EVENT, .recv = qca_ibs_sleep_ind },
@@ -1220,7 +1277,7 @@ static int qca_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- qca->rx_skb = h4_recv_buf(hu->hdev, qca->rx_skb, data, count,
+ qca->rx_skb = h4_recv_buf(hu, qca->rx_skb, data, count,
qca_recv_pkts, ARRAY_SIZE(qca_recv_pkts));
if (IS_ERR(qca->rx_skb)) {
int err = PTR_ERR(qca->rx_skb);
@@ -1310,11 +1367,21 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
/* Give the controller time to process the request */
- if (qca_is_wcn399x(qca_soc_type(hu)) ||
- qca_is_wcn6750(qca_soc_type(hu)))
+ switch (qca_soc_type(hu)) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
usleep_range(1000, 10000);
- else
+ break;
+
+ default:
msleep(300);
+ }
return 0;
}
@@ -1387,12 +1454,21 @@ static unsigned int qca_get_speed(struct hci_uart *hu,
static int qca_check_speeds(struct hci_uart *hu)
{
- if (qca_is_wcn399x(qca_soc_type(hu)) ||
- qca_is_wcn6750(qca_soc_type(hu))) {
+ switch (qca_soc_type(hu)) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
if (!qca_get_speed(hu, QCA_INIT_SPEED) &&
!qca_get_speed(hu, QCA_OPER_SPEED))
return -EINVAL;
- } else {
+ break;
+
+ default:
if (!qca_get_speed(hu, QCA_INIT_SPEED) ||
!qca_get_speed(hu, QCA_OPER_SPEED))
return -EINVAL;
@@ -1421,13 +1497,30 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
/* Disable flow control for wcn3990 to deassert RTS while
* changing the baudrate of chip and host.
*/
- if (qca_is_wcn399x(soc_type) ||
- qca_is_wcn6750(soc_type))
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
hci_uart_set_flow_control(hu, true);
+ break;
+
+ default:
+ break;
+ }
- if (soc_type == QCA_WCN3990) {
+ switch (soc_type) {
+ case QCA_WCN3990:
reinit_completion(&qca->drop_ev_comp);
set_bit(QCA_DROP_VENDOR_EVENT, &qca->flags);
+ break;
+
+ default:
+ break;
}
qca_baudrate = qca_get_baudrate_value(speed);
@@ -1439,11 +1532,24 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
host_set_baudrate(hu, speed);
error:
- if (qca_is_wcn399x(soc_type) ||
- qca_is_wcn6750(soc_type))
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
hci_uart_set_flow_control(hu, false);
+ break;
+
+ default:
+ break;
+ }
- if (soc_type == QCA_WCN3990) {
+ switch (soc_type) {
+ case QCA_WCN3990:
/* Wait for the controller to send the vendor event
* for the baudrate change command.
*/
@@ -1455,6 +1561,10 @@ error:
}
clear_bit(QCA_DROP_VENDOR_EVENT, &qca->flags);
+ break;
+
+ default:
+ break;
}
}
@@ -1527,8 +1637,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
+ hci_devcd_abort(hu->hdev);
if (qca->qca_memdump) {
- vfree(qca->qca_memdump->memdump_buf_head);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
}
@@ -1546,7 +1656,7 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
clear_bit(QCA_HW_ERROR_EVENT, &qca->flags);
}
-static void qca_cmd_timeout(struct hci_dev *hdev)
+static void qca_reset(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
@@ -1582,15 +1692,40 @@ static bool qca_wakeup(struct hci_dev *hdev)
struct hci_uart *hu = hci_get_drvdata(hdev);
bool wakeup;
- /* UART driver handles the interrupt from BT SoC.So we need to use
- * device handle of UART driver to get the status of device may wakeup.
+ if (!hu->serdev)
+ return true;
+
+ /* BT SoC attached through the serial bus is handled by the serdev driver.
+ * So we need to use the device handle of the serdev driver to get the
+ * status of device may wakeup.
*/
- wakeup = device_may_wakeup(hu->serdev->ctrl->dev.parent);
+ wakeup = device_may_wakeup(&hu->serdev->ctrl->dev);
bt_dev_dbg(hu->hdev, "wakeup status : %d", wakeup);
return wakeup;
}
+static int qca_port_reopen(struct hci_uart *hu)
+{
+ int ret;
+
+ /* Now the device is in ready state to communicate with host.
+ * To sync host with device we need to reopen port.
+ * Without this, we will have RTS and CTS synchronization
+ * issues.
+ */
+ serdev_device_close(hu->serdev);
+ ret = serdev_device_open(hu->serdev);
+ if (ret) {
+ bt_dev_err(hu->hdev, "failed to open port");
+ return ret;
+ }
+
+ hci_uart_set_flow_control(hu, false);
+
+ return 0;
+}
+
static int qca_regulator_init(struct hci_uart *hu)
{
enum qca_btsoc_type soc_type = qca_soc_type(hu);
@@ -1602,6 +1737,7 @@ static int qca_regulator_init(struct hci_uart *hu)
* off the voltage regulator.
*/
qcadev = serdev_device_get_drvdata(hu->serdev);
+
if (!qcadev->bt_power->vregs_on) {
serdev_device_close(hu->serdev);
ret = qca_regulator_enable(qcadev);
@@ -1615,12 +1751,21 @@ static int qca_regulator_init(struct hci_uart *hu)
}
}
- if (qca_is_wcn399x(soc_type)) {
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
/* Forcefully enable wcn399x to enter in to boot mode. */
host_set_baudrate(hu, 2400);
ret = qca_send_power_pulse(hu, false);
if (ret)
return ret;
+ break;
+
+ default:
+ break;
}
/* For wcn6750 need to enable gpio bt_en */
@@ -1637,27 +1782,22 @@ static int qca_regulator_init(struct hci_uart *hu)
qca_set_speed(hu, QCA_INIT_SPEED);
- if (qca_is_wcn399x(soc_type)) {
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
ret = qca_send_power_pulse(hu, true);
if (ret)
return ret;
- }
+ break;
- /* Now the device is in ready state to communicate with host.
- * To sync host with device we need to reopen port.
- * Without this, we will have RTS and CTS synchronization
- * issues.
- */
- serdev_device_close(hu->serdev);
- ret = serdev_device_open(hu->serdev);
- if (ret) {
- bt_dev_err(hu->hdev, "failed to open port");
- return ret;
+ default:
+ break;
}
- hci_uart_set_flow_control(hu, false);
-
- return 0;
+ return qca_port_reopen(hu);
}
static int qca_power_on(struct hci_dev *hdev)
@@ -1674,10 +1814,20 @@ static int qca_power_on(struct hci_dev *hdev)
if (!hu->serdev)
return 0;
- if (qca_is_wcn399x(soc_type) ||
- qca_is_wcn6750(soc_type)) {
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
+ case QCA_QCA6390:
ret = qca_regulator_init(hu);
- } else {
+ break;
+
+ default:
qcadev = serdev_device_get_drvdata(hu->serdev);
if (qcadev->bt_en) {
gpiod_set_value_cansleep(qcadev->bt_en, 1);
@@ -1690,6 +1840,34 @@ static int qca_power_on(struct hci_dev *hdev)
return ret;
}
+static void hci_coredump_qca(struct hci_dev *hdev)
+{
+ int err;
+ static const u8 param[] = { 0x26 };
+
+ err = __hci_cmd_send(hdev, 0xfc0c, 1, param);
+ if (err < 0)
+ bt_dev_err(hdev, "%s: trigger crash failed (%d)", __func__, err);
+}
+
+static int qca_get_data_path_id(struct hci_dev *hdev, __u8 *data_path_id)
+{
+ /* QCA uses 1 as non-HCI data path id for HFP */
+ *data_path_id = 1;
+ return 0;
+}
+
+static int qca_configure_hfp_offload(struct hci_dev *hdev)
+{
+ bt_dev_info(hdev, "HFP non-HCI data transport is supported");
+ hdev->get_data_path_id = qca_get_data_path_id;
+ /* Do not need to send HCI_Configure_Data_Path to configure non-HCI
+ * data transport path for QCA controllers, so set below field as NULL.
+ */
+ hdev->get_codec_config_data = NULL;
+ return 0;
+}
+
static int qca_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
@@ -1698,8 +1876,11 @@ static int qca_setup(struct hci_uart *hu)
unsigned int retries = 0;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
const char *firmware_name = qca_get_firmware_name(hu);
+ const char *rampatch_name = qca_get_rampatch_name(hu);
int ret;
struct qca_btsoc_version ver;
+ struct qca_serdev *qcadev;
+ const char *soc_name;
ret = qca_check_speeds(hu);
if (ret)
@@ -1712,11 +1893,37 @@ static int qca_setup(struct hci_uart *hu)
/* Enable controller to do both LE scan and BR/EDR inquiry
* simultaneously.
*/
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+
+ switch (soc_type) {
+ case QCA_QCA2066:
+ soc_name = "qca2066";
+ break;
+
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ soc_name = "wcn399x";
+ break;
+
+ case QCA_WCN6750:
+ soc_name = "wcn6750";
+ break;
- bt_dev_info(hdev, "setting up %s",
- qca_is_wcn399x(soc_type) ? "wcn399x" :
- (soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390");
+ case QCA_WCN6855:
+ soc_name = "wcn6855";
+ break;
+
+ case QCA_WCN7850:
+ soc_name = "wcn7850";
+ break;
+
+ default:
+ soc_name = "ROME/QCA6390";
+ }
+ bt_dev_info(hdev, "setting up %s", soc_name);
qca->memdump_state = QCA_MEMDUMP_IDLE;
@@ -1727,15 +1934,27 @@ retry:
clear_bit(QCA_SSR_TRIGGERED, &qca->flags);
- if (qca_is_wcn399x(soc_type) ||
- qca_is_wcn6750(soc_type)) {
- set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
+ qcadev = serdev_device_get_drvdata(hu->serdev);
+ if (qcadev->bdaddr_property_broken)
+ hci_set_quirk(hdev, HCI_QUIRK_BDADDR_PROPERTY_BROKEN);
+
hci_set_aosp_capable(hdev);
ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret)
goto out;
- } else {
+ break;
+
+ default:
qca_set_speed(hu, QCA_INIT_SPEED);
}
@@ -1749,8 +1968,18 @@ retry:
qca_baudrate = qca_get_baudrate_value(speed);
}
- if (!(qca_is_wcn399x(soc_type) ||
- qca_is_wcn6750(soc_type))) {
+ switch (soc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
+ break;
+
+ default:
/* Get QCA version information */
ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret)
@@ -1759,13 +1988,16 @@ retry:
/* Setup patch / NVM configurations */
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, ver,
- firmware_name);
+ firmware_name, rampatch_name);
if (!ret) {
clear_bit(QCA_IBS_DISABLED, &qca->flags);
qca_debugfs_init(hdev);
hu->hdev->hw_error = qca_hw_error;
- hu->hdev->cmd_timeout = qca_cmd_timeout;
- hu->hdev->wakeup = qca_wakeup;
+ hu->hdev->reset = qca_reset;
+ if (hu->serdev) {
+ if (device_can_wakeup(hu->serdev->ctrl->dev.parent))
+ hu->hdev->wakeup = qca_wakeup;
+ }
} else if (ret == -ENOENT) {
/* No patch/nvm-config found, run with original fw/config */
set_bit(QCA_ROM_FW, &qca->flags);
@@ -1801,6 +2033,13 @@ out:
else
hu->hdev->set_bdaddr = qca_set_bdaddr;
+ if (soc_type == QCA_QCA2066)
+ qca_configure_hfp_offload(hdev);
+
+ qca->fw_version = le16_to_cpu(ver.patch_ver);
+ qca->controller_id = le16_to_cpu(ver.rom_ver);
+ hci_devcd_register(hdev, hci_coredump_qca, qca_dmp_hdr, NULL);
+
return ret;
}
@@ -1819,7 +2058,29 @@ static const struct hci_uart_proto qca_proto = {
.dequeue = qca_dequeue,
};
-static const struct qca_device_data qca_soc_data_wcn3990 = {
+static const struct qca_device_data qca_soc_data_wcn3950 __maybe_unused = {
+ .soc_type = QCA_WCN3950,
+ .vregs = (struct qca_vreg []) {
+ { "vddio", 15000 },
+ { "vddxo", 60000 },
+ { "vddrf", 155000 },
+ { "vddch0", 585000 },
+ },
+ .num_vregs = 4,
+};
+
+static const struct qca_device_data qca_soc_data_wcn3988 __maybe_unused = {
+ .soc_type = QCA_WCN3988,
+ .vregs = (struct qca_vreg []) {
+ { "vddio", 15000 },
+ { "vddxo", 80000 },
+ { "vddrf", 300000 },
+ { "vddch0", 450000 },
+ },
+ .num_vregs = 4,
+};
+
+static const struct qca_device_data qca_soc_data_wcn3990 __maybe_unused = {
.soc_type = QCA_WCN3990,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
@@ -1830,7 +2091,7 @@ static const struct qca_device_data qca_soc_data_wcn3990 = {
.num_vregs = 4,
};
-static const struct qca_device_data qca_soc_data_wcn3991 = {
+static const struct qca_device_data qca_soc_data_wcn3991 __maybe_unused = {
.soc_type = QCA_WCN3991,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
@@ -1842,7 +2103,7 @@ static const struct qca_device_data qca_soc_data_wcn3991 = {
.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
};
-static const struct qca_device_data qca_soc_data_wcn3998 = {
+static const struct qca_device_data qca_soc_data_wcn3998 __maybe_unused = {
.soc_type = QCA_WCN3998,
.vregs = (struct qca_vreg []) {
{ "vddio", 10000 },
@@ -1853,12 +2114,18 @@ static const struct qca_device_data qca_soc_data_wcn3998 = {
.num_vregs = 4,
};
-static const struct qca_device_data qca_soc_data_qca6390 = {
+static const struct qca_device_data qca_soc_data_qca2066 __maybe_unused = {
+ .soc_type = QCA_QCA2066,
+ .num_vregs = 0,
+ .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
+};
+
+static const struct qca_device_data qca_soc_data_qca6390 __maybe_unused = {
.soc_type = QCA_QCA6390,
.num_vregs = 0,
};
-static const struct qca_device_data qca_soc_data_wcn6750 = {
+static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = {
.soc_type = QCA_WCN6750,
.vregs = (struct qca_vreg []) {
{ "vddio", 5000 },
@@ -1875,6 +2142,34 @@ static const struct qca_device_data qca_soc_data_wcn6750 = {
.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
};
+static const struct qca_device_data qca_soc_data_wcn6855 __maybe_unused = {
+ .soc_type = QCA_WCN6855,
+ .vregs = (struct qca_vreg []) {
+ { "vddio", 5000 },
+ { "vddbtcxmx", 126000 },
+ { "vddrfacmn", 12500 },
+ { "vddrfa0p8", 102000 },
+ { "vddrfa1p7", 302000 },
+ { "vddrfa1p2", 257000 },
+ },
+ .num_vregs = 6,
+ .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
+};
+
+static const struct qca_device_data qca_soc_data_wcn7850 __maybe_unused = {
+ .soc_type = QCA_WCN7850,
+ .vregs = (struct qca_vreg []) {
+ { "vddio", 5000 },
+ { "vddaon", 26000 },
+ { "vdddig", 126000 },
+ { "vddrfa0p8", 102000 },
+ { "vddrfa1p2", 257000 },
+ { "vddrfa1p9", 302000 },
+ },
+ .num_vregs = 6,
+ .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
+};
+
static void qca_power_shutdown(struct hci_uart *hu)
{
struct qca_serdev *qcadev;
@@ -1882,6 +2177,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
unsigned long flags;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
bool sw_ctrl_state;
+ struct qca_power *power;
/* From this point we go into power off state. But serial port is
* still open, stop queueing the IBS data and flush all the buffered
@@ -1899,12 +2195,26 @@ static void qca_power_shutdown(struct hci_uart *hu)
return;
qcadev = serdev_device_get_drvdata(hu->serdev);
+ power = qcadev->bt_power;
- if (qca_is_wcn399x(soc_type)) {
+ if (power && power->pwrseq) {
+ pwrseq_power_off(power->pwrseq);
+ set_bit(QCA_BT_OFF, &qca->flags);
+ return;
+ }
+
+ switch (soc_type) {
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
qca_regulator_disable(qcadev);
- } else if (soc_type == QCA_WCN6750) {
+ break;
+
+ case QCA_WCN6750:
+ case QCA_WCN6855:
gpiod_set_value_cansleep(qcadev->bt_en, 0);
msleep(100);
qca_regulator_disable(qcadev);
@@ -1912,7 +2222,9 @@ static void qca_power_shutdown(struct hci_uart *hu)
sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
}
- } else if (qcadev->bt_en) {
+ break;
+
+ default:
gpiod_set_value_cansleep(qcadev->bt_en, 0);
}
@@ -1926,10 +2238,10 @@ static int qca_power_off(struct hci_dev *hdev)
enum qca_btsoc_type soc_type = qca_soc_type(hu);
hu->hdev->hw_error = NULL;
- hu->hdev->cmd_timeout = NULL;
+ hu->hdev->reset = NULL;
- del_timer_sync(&qca->wake_retrans_timer);
- del_timer_sync(&qca->tx_idle_timer);
+ timer_delete_sync(&qca->wake_retrans_timer);
+ timer_delete_sync(&qca->tx_idle_timer);
/* Stop sending shutdown command if soc crashes. */
if (soc_type != QCA_ROME
@@ -1947,6 +2259,9 @@ static int qca_regulator_enable(struct qca_serdev *qcadev)
struct qca_power *power = qcadev->bt_power;
int ret;
+ if (power->pwrseq)
+ return pwrseq_power_on(power->pwrseq);
+
/* Already enabled */
if (power->vregs_on)
return 0;
@@ -2030,23 +2345,72 @@ static int qca_serdev_probe(struct serdev_device *serdev)
qcadev->serdev_hu.serdev = serdev;
data = device_get_match_data(&serdev->dev);
serdev_device_set_drvdata(serdev, qcadev);
- device_property_read_string(&serdev->dev, "firmware-name",
- &qcadev->firmware_name);
+ device_property_read_string_array(&serdev->dev, "firmware-name",
+ qcadev->firmware_name, ARRAY_SIZE(qcadev->firmware_name));
device_property_read_u32(&serdev->dev, "max-speed",
&qcadev->oper_speed);
if (!qcadev->oper_speed)
BT_DBG("UART will pick default operating speed");
- if (data &&
- (qca_is_wcn399x(data->soc_type) ||
- qca_is_wcn6750(data->soc_type))) {
+ qcadev->bdaddr_property_broken = device_property_read_bool(&serdev->dev,
+ "qcom,local-bd-address-broken");
+
+ if (data)
qcadev->btsoc_type = data->soc_type;
+ else
+ qcadev->btsoc_type = QCA_ROME;
+
+ switch (qcadev->btsoc_type) {
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
+ case QCA_QCA6390:
qcadev->bt_power = devm_kzalloc(&serdev->dev,
sizeof(struct qca_power),
GFP_KERNEL);
if (!qcadev->bt_power)
return -ENOMEM;
+ break;
+ default:
+ break;
+ }
+ switch (qcadev->btsoc_type) {
+ case QCA_WCN6855:
+ case QCA_WCN7850:
+ case QCA_WCN6750:
+ if (!device_property_present(&serdev->dev, "enable-gpios")) {
+ /*
+ * Backward compatibility with old DT sources. If the
+ * node doesn't have the 'enable-gpios' property then
+ * let's use the power sequencer. Otherwise, let's
+ * drive everything ourselves.
+ */
+ qcadev->bt_power->pwrseq = devm_pwrseq_get(&serdev->dev,
+ "bluetooth");
+
+ /*
+ * Some modules have BT_EN enabled via a hardware pull-up,
+ * meaning it is not defined in the DTS and is not controlled
+ * through the power sequence. In such cases, fall through
+ * to follow the legacy flow.
+ */
+ if (IS_ERR(qcadev->bt_power->pwrseq))
+ qcadev->bt_power->pwrseq = NULL;
+ else
+ break;
+ }
+ fallthrough;
+ case QCA_WCN3950:
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
qcadev->bt_power->dev = &serdev->dev;
err = qca_init_regulators(qcadev->bt_power, data->vregs,
data->num_vregs);
@@ -2059,65 +2423,72 @@ static int qca_serdev_probe(struct serdev_device *serdev)
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
GPIOD_OUT_LOW);
- if (IS_ERR_OR_NULL(qcadev->bt_en) && data->soc_type == QCA_WCN6750) {
- dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n");
+ if (IS_ERR(qcadev->bt_en))
+ return dev_err_probe(&serdev->dev,
+ PTR_ERR(qcadev->bt_en),
+ "failed to acquire BT_EN gpio\n");
+
+ if (!qcadev->bt_en &&
+ (data->soc_type == QCA_WCN6750 ||
+ data->soc_type == QCA_WCN6855))
power_ctrl_enabled = false;
- }
qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl",
GPIOD_IN);
- if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && data->soc_type == QCA_WCN6750)
- dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
+ if (IS_ERR(qcadev->sw_ctrl) &&
+ (data->soc_type == QCA_WCN6750 ||
+ data->soc_type == QCA_WCN6855 ||
+ data->soc_type == QCA_WCN7850)) {
+ dev_err(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
+ return PTR_ERR(qcadev->sw_ctrl);
+ }
qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
if (IS_ERR(qcadev->susclk)) {
dev_err(&serdev->dev, "failed to acquire clk\n");
return PTR_ERR(qcadev->susclk);
}
+ break;
- err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
- if (err) {
- BT_ERR("wcn3990 serdev registration failed");
- return err;
+ case QCA_QCA6390:
+ if (dev_of_node(&serdev->dev)) {
+ qcadev->bt_power->pwrseq = devm_pwrseq_get(&serdev->dev,
+ "bluetooth");
+ if (IS_ERR(qcadev->bt_power->pwrseq))
+ return PTR_ERR(qcadev->bt_power->pwrseq);
+ break;
}
- } else {
- if (data)
- qcadev->btsoc_type = data->soc_type;
- else
- qcadev->btsoc_type = QCA_ROME;
+ fallthrough;
+ default:
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
GPIOD_OUT_LOW);
- if (IS_ERR_OR_NULL(qcadev->bt_en)) {
- dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
- power_ctrl_enabled = false;
+ if (IS_ERR(qcadev->bt_en)) {
+ dev_err(&serdev->dev, "failed to acquire enable gpio\n");
+ return PTR_ERR(qcadev->bt_en);
}
- qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
+ if (!qcadev->bt_en)
+ power_ctrl_enabled = false;
+
+ qcadev->susclk = devm_clk_get_optional_enabled_with_rate(
+ &serdev->dev, NULL, SUSCLK_RATE_32KHZ);
if (IS_ERR(qcadev->susclk)) {
dev_warn(&serdev->dev, "failed to acquire clk\n");
return PTR_ERR(qcadev->susclk);
}
- err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
- if (err)
- return err;
-
- err = clk_prepare_enable(qcadev->susclk);
- if (err)
- return err;
-
- err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
- if (err) {
- BT_ERR("Rome serdev registration failed");
- clk_disable_unprepare(qcadev->susclk);
- return err;
- }
+ }
+
+ err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
+ if (err) {
+ BT_ERR("serdev registration failed");
+ return err;
}
hdev = qcadev->serdev_hu.hdev;
if (power_ctrl_enabled) {
- set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
hdev->shutdown = qca_power_off;
}
@@ -2126,11 +2497,11 @@ static int qca_serdev_probe(struct serdev_device *serdev)
* be queried via hci. Same with the valid le states quirk.
*/
if (data->capabilities & QCA_CAP_WIDEBAND_SPEECH)
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
- &hdev->quirks);
+ hci_set_quirk(hdev,
+ HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
- if (data->capabilities & QCA_CAP_VALID_LE_STATES)
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
+ if (!(data->capabilities & QCA_CAP_VALID_LE_STATES))
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
}
return 0;
@@ -2141,12 +2512,20 @@ static void qca_serdev_remove(struct serdev_device *serdev)
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
struct qca_power *power = qcadev->bt_power;
- if ((qca_is_wcn399x(qcadev->btsoc_type) ||
- qca_is_wcn6750(qcadev->btsoc_type)) &&
- power->vregs_on)
- qca_power_shutdown(&qcadev->serdev_hu);
- else if (qcadev->susclk)
- clk_disable_unprepare(qcadev->susclk);
+ switch (qcadev->btsoc_type) {
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
+ if (power->vregs_on)
+ qca_power_shutdown(&qcadev->serdev_hu);
+ break;
+ default:
+ break;
+ }
hci_uart_unregister_device(&qcadev->serdev_hu);
}
@@ -2157,10 +2536,29 @@ static void qca_serdev_shutdown(struct device *dev)
int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
struct serdev_device *serdev = to_serdev_device(dev);
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &qcadev->serdev_hu;
+ struct hci_dev *hdev = hu->hdev;
const u8 ibs_wake_cmd[] = { 0xFD };
const u8 edl_reset_soc_cmd[] = { 0x01, 0x00, 0xFC, 0x01, 0x05 };
if (qcadev->btsoc_type == QCA_QCA6390) {
+ /* The purpose of sending the VSC is to reset SOC into a initial
+ * state and the state will ensure next hdev->setup() success.
+ * if HCI_QUIRK_NON_PERSISTENT_SETUP is set, it means that
+ * hdev->setup() can do its job regardless of SoC state, so
+ * don't need to send the VSC.
+ * if HCI_SETUP is set, it means that hdev->setup() was never
+ * invoked and the SOC is already in the initial state, so
+ * don't also need to send the VSC.
+ */
+ if (hci_test_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP) ||
+ hci_dev_test_flag(hdev, HCI_SETUP))
+ return;
+
+ /* The serdev must be in open state when control logic arrives
+ * here, so also fix the use-after-free issue caused by that
+ * the serdev is flushed or wrote after it is closed.
+ */
serdev_device_write_flush(serdev);
ret = serdev_device_write_buf(serdev, ibs_wake_cmd,
sizeof(ibs_wake_cmd));
@@ -2239,10 +2637,10 @@ static int __maybe_unused qca_suspend(struct device *dev)
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_WAKING:
- del_timer(&qca->wake_retrans_timer);
+ timer_delete(&qca->wake_retrans_timer);
fallthrough;
case HCI_IBS_TX_AWAKE:
- del_timer(&qca->tx_idle_timer);
+ timer_delete(&qca->tx_idle_timer);
serdev_device_write_flush(hu->serdev);
cmd = HCI_IBS_SLEEP_IND;
@@ -2313,13 +2711,18 @@ static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
#ifdef CONFIG_OF
static const struct of_device_id qca_bluetooth_of_match[] = {
+ { .compatible = "qcom,qca2066-bt", .data = &qca_soc_data_qca2066},
{ .compatible = "qcom,qca6174-bt" },
{ .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
{ .compatible = "qcom,qca9377-bt" },
+ { .compatible = "qcom,wcn3950-bt", .data = &qca_soc_data_wcn3950},
+ { .compatible = "qcom,wcn3988-bt", .data = &qca_soc_data_wcn3988},
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
{ .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750},
+ { .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855},
+ { .compatible = "qcom,wcn7850-bt", .data = &qca_soc_data_wcn7850},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
@@ -2327,6 +2730,7 @@ MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
+ { "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 },
{ "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 },
{ "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
{ "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
@@ -2336,6 +2740,18 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
#endif
+#ifdef CONFIG_DEV_COREDUMP
+static void hciqca_coredump(struct device *dev)
+{
+ struct serdev_device *serdev = to_serdev_device(dev);
+ struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &qcadev->serdev_hu;
+ struct hci_dev *hdev = hu->hdev;
+
+ if (hdev->dump.coredump)
+ hdev->dump.coredump(hdev);
+}
+#endif
static struct serdev_device_driver qca_serdev_driver = {
.probe = qca_serdev_probe,
@@ -2346,6 +2762,9 @@ static struct serdev_device_driver qca_serdev_driver = {
.acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
.shutdown = qca_serdev_shutdown,
.pm = &qca_pm_ops,
+#ifdef CONFIG_DEV_COREDUMP
+ .coredump = hciqca_coredump,
+#endif
},
};
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index c0e5f42ec6b7..593d9cefbbf9 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -152,7 +152,7 @@ static int hci_uart_close(struct hci_dev *hdev)
* BT SOC is completely powered OFF during BT OFF, holding port
* open may drain the battery.
*/
- if (test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks)) {
+ if (hci_test_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP)) {
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
serdev_device_close(hu->serdev);
}
@@ -271,8 +271,8 @@ static void hci_uart_write_wakeup(struct serdev_device *serdev)
*
* Return: number of processed bytes
*/
-static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data,
- size_t count)
+static size_t hci_uart_receive_buf(struct serdev_device *serdev,
+ const u8 *data, size_t count)
{
struct hci_uart *hu = serdev_device_get_drvdata(serdev);
@@ -300,8 +300,9 @@ static const struct serdev_device_ops hci_serdev_client_ops = {
.write_wakeup = hci_uart_write_wakeup,
};
-int hci_uart_register_device(struct hci_uart *hu,
- const struct hci_uart_proto *p)
+int hci_uart_register_device_priv(struct hci_uart *hu,
+ const struct hci_uart_proto *p,
+ int sizeof_priv)
{
int err;
struct hci_dev *hdev;
@@ -310,11 +311,12 @@ int hci_uart_register_device(struct hci_uart *hu,
serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
+ if (percpu_init_rwsem(&hu->proto_lock))
+ return -ENOMEM;
+
err = serdev_device_open(hu->serdev);
if (err)
- return err;
-
- percpu_init_rwsem(&hu->proto_lock);
+ goto err_rwsem;
err = p->open(hu);
if (err)
@@ -324,7 +326,7 @@ int hci_uart_register_device(struct hci_uart *hu,
set_bit(HCI_UART_PROTO_READY, &hu->flags);
/* Initialize and register HCI device */
- hdev = hci_alloc_dev();
+ hdev = hci_alloc_dev_priv(sizeof_priv);
if (!hdev) {
BT_ERR("Can't allocate HCI device");
err = -ENOMEM;
@@ -356,18 +358,13 @@ int hci_uart_register_device(struct hci_uart *hu,
SET_HCIDEV_DEV(hdev, &hu->serdev->dev);
if (test_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &hu->flags))
- set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_NO_SUSPEND_NOTIFIER);
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
- set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE);
if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
- set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
-
- if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
- hdev->dev_type = HCI_AMP;
- else
- hdev->dev_type = HCI_PRIMARY;
+ hci_set_quirk(hdev, HCI_QUIRK_EXTERNAL_CONFIG);
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return 0;
@@ -389,9 +386,11 @@ err_alloc:
p->close(hu);
err_open:
serdev_device_close(hu->serdev);
+err_rwsem:
+ percpu_free_rwsem(&hu->proto_lock);
return err;
}
-EXPORT_SYMBOL_GPL(hci_uart_register_device);
+EXPORT_SYMBOL_GPL(hci_uart_register_device_priv);
void hci_uart_unregister_device(struct hci_uart *hu)
{
@@ -410,5 +409,6 @@ void hci_uart_unregister_device(struct hci_uart *hu)
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
serdev_device_close(hu->serdev);
}
+ percpu_free_rwsem(&hu->proto_lock);
}
EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index fb4a2d0d8cc8..48ac7ca9334e 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -20,7 +20,7 @@
#define HCIUARTGETFLAGS _IOR('U', 204, int)
/* UART protocols */
-#define HCI_UART_MAX_PROTO 12
+#define HCI_UART_MAX_PROTO 13
#define HCI_UART_H4 0
#define HCI_UART_BCSP 1
@@ -34,10 +34,10 @@
#define HCI_UART_AG6XX 9
#define HCI_UART_NOKIA 10
#define HCI_UART_MRVL 11
+#define HCI_UART_AML 12
#define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1
-#define HCI_UART_CREATE_AMP 2
#define HCI_UART_INIT_PENDING 3
#define HCI_UART_EXT_CONFIG 4
#define HCI_UART_VND_DETECT 5
@@ -90,6 +90,7 @@ struct hci_uart {
#define HCI_UART_REGISTERED 1
#define HCI_UART_PROTO_READY 2
#define HCI_UART_NO_SUSPEND_NOTIFIER 3
+#define HCI_UART_PROTO_INIT 4
/* TX states */
#define HCI_UART_SENDING 1
@@ -97,7 +98,17 @@ struct hci_uart {
int hci_uart_register_proto(const struct hci_uart_proto *p);
int hci_uart_unregister_proto(const struct hci_uart_proto *p);
-int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p);
+
+int hci_uart_register_device_priv(struct hci_uart *hu,
+ const struct hci_uart_proto *p,
+ int sizeof_priv);
+
+static inline int hci_uart_register_device(struct hci_uart *hu,
+ const struct hci_uart_proto *p)
+{
+ return hci_uart_register_device_priv(hu, p, 0);
+}
+
void hci_uart_unregister_device(struct hci_uart *hu);
int hci_uart_tx_wakeup(struct hci_uart *hu);
@@ -110,10 +121,6 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
unsigned int oper_speed);
-#ifdef CONFIG_BT_HCIUART_H4
-int h4_init(void);
-int h4_deinit(void);
-
struct h4_recv_pkt {
u8 type; /* Packet type */
u8 hlen; /* Header length */
@@ -151,7 +158,11 @@ struct h4_recv_pkt {
.lsize = 2, \
.maxlen = HCI_MAX_FRAME_SIZE \
-struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+#ifdef CONFIG_BT_HCIUART_H4
+int h4_init(void);
+int h4_deinit(void);
+
+struct sk_buff *h4_recv_buf(struct hci_uart *hu, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count);
#endif
@@ -200,3 +211,8 @@ int ag6xx_deinit(void);
int mrvl_init(void);
int mrvl_deinit(void);
#endif
+
+#ifdef CONFIG_BT_HCIUART_AML
+int aml_init(void);
+int aml_deinit(void);
+#endif
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index c443c3b0a4da..2fef08254d78 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -9,8 +9,9 @@
*/
#include <linux/module.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
+#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -44,6 +45,7 @@ struct vhci_data {
bool wakeup;
__u16 msft_opcode;
bool aosp_capable;
+ atomic_t initialized;
};
static int vhci_open_dev(struct hci_dev *hdev)
@@ -74,9 +76,11 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
struct vhci_data *data = hci_get_drvdata(hdev);
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+
skb_queue_tail(&data->readq, skb);
- wake_up_interruptible(&data->read_wait);
+ if (atomic_read(&data->initialized))
+ wake_up_interruptible(&data->read_wait);
return 0;
}
@@ -278,21 +282,134 @@ static int vhci_setup(struct hci_dev *hdev)
return 0;
}
+static void vhci_coredump(struct hci_dev *hdev)
+{
+ /* No need to do anything */
+}
+
+static void vhci_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ const char *buf;
+
+ buf = "Controller Name: vhci_ctrl\n";
+ skb_put_data(skb, buf, strlen(buf));
+
+ buf = "Firmware Version: vhci_fw\n";
+ skb_put_data(skb, buf, strlen(buf));
+
+ buf = "Driver: vhci_drv\n";
+ skb_put_data(skb, buf, strlen(buf));
+
+ buf = "Vendor: vhci\n";
+ skb_put_data(skb, buf, strlen(buf));
+}
+
+#define MAX_COREDUMP_LINE_LEN 40
+
+struct devcoredump_test_data {
+ enum devcoredump_state state;
+ unsigned int timeout;
+ char data[MAX_COREDUMP_LINE_LEN];
+};
+
+static inline void force_devcd_timeout(struct hci_dev *hdev,
+ unsigned int timeout)
+{
+#ifdef CONFIG_DEV_COREDUMP
+ hdev->dump.timeout = secs_to_jiffies(timeout);
+#endif
+}
+
+static ssize_t force_devcd_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct vhci_data *data = file->private_data;
+ struct hci_dev *hdev = data->hdev;
+ struct sk_buff *skb = NULL;
+ struct devcoredump_test_data dump_data;
+ size_t data_size;
+ int ret;
+
+ if (count < offsetof(struct devcoredump_test_data, data) ||
+ count > sizeof(dump_data))
+ return -EINVAL;
+
+ if (copy_from_user(&dump_data, user_buf, count))
+ return -EFAULT;
+
+ data_size = count - offsetof(struct devcoredump_test_data, data);
+ skb = alloc_skb(data_size, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+ skb_put_data(skb, &dump_data.data, data_size);
+
+ hci_devcd_register(hdev, vhci_coredump, vhci_coredump_hdr, NULL);
+
+ /* Force the devcoredump timeout */
+ if (dump_data.timeout)
+ force_devcd_timeout(hdev, dump_data.timeout);
+
+ ret = hci_devcd_init(hdev, skb->len);
+ if (ret) {
+ BT_ERR("Failed to generate devcoredump");
+ kfree_skb(skb);
+ return ret;
+ }
+
+ hci_devcd_append(hdev, skb);
+
+ switch (dump_data.state) {
+ case HCI_DEVCOREDUMP_DONE:
+ hci_devcd_complete(hdev);
+ break;
+ case HCI_DEVCOREDUMP_ABORT:
+ hci_devcd_abort(hdev);
+ break;
+ case HCI_DEVCOREDUMP_TIMEOUT:
+ /* Do nothing */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations force_devcoredump_fops = {
+ .open = simple_open,
+ .write = force_devcd_write,
+};
+
+static void vhci_debugfs_init(struct vhci_data *data)
+{
+ struct hci_dev *hdev = data->hdev;
+
+ debugfs_create_file("force_suspend", 0644, hdev->debugfs, data,
+ &force_suspend_fops);
+
+ debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data,
+ &force_wakeup_fops);
+
+ if (IS_ENABLED(CONFIG_BT_MSFTEXT))
+ debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data,
+ &msft_opcode_fops);
+
+ if (IS_ENABLED(CONFIG_BT_AOSPEXT))
+ debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data,
+ &aosp_capable_fops);
+
+ debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data,
+ &force_devcoredump_fops);
+}
+
static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
{
struct hci_dev *hdev;
struct sk_buff *skb;
- __u8 dev_type;
if (data->hdev)
return -EBADFD;
- /* bits 0-1 are dev_type (Primary or AMP) */
- dev_type = opcode & 0x03;
-
- if (dev_type != HCI_PRIMARY && dev_type != HCI_AMP)
- return -EINVAL;
-
/* bits 2-5 are reserved (must be zero) */
if (opcode & 0x3c)
return -EINVAL;
@@ -310,7 +427,6 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
data->hdev = hdev;
hdev->bus = HCI_VIRTUAL;
- hdev->dev_type = dev_type;
hci_set_drvdata(hdev, data);
hdev->open = vhci_open_dev;
@@ -321,17 +437,16 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
hdev->get_codec_config_data = vhci_get_codec_config_data;
hdev->wakeup = vhci_wakeup;
hdev->setup = vhci_setup;
- set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+ hci_set_quirk(hdev, HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED);
/* bit 6 is for external configuration */
if (opcode & 0x40)
- set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_EXTERNAL_CONFIG);
/* bit 7 is for raw device */
if (opcode & 0x80)
- set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
-
- set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_RAW_DEVICE);
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
@@ -341,26 +456,16 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
return -EBUSY;
}
- debugfs_create_file("force_suspend", 0644, hdev->debugfs, data,
- &force_suspend_fops);
-
- debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data,
- &force_wakeup_fops);
-
- if (IS_ENABLED(CONFIG_BT_MSFTEXT))
- debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data,
- &msft_opcode_fops);
-
- if (IS_ENABLED(CONFIG_BT_AOSPEXT))
- debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data,
- &aosp_capable_fops);
+ if (!IS_ERR_OR_NULL(hdev->debugfs))
+ vhci_debugfs_init(data);
hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
skb_put_u8(skb, 0xff);
skb_put_u8(skb, opcode);
put_unaligned_le16(hdev->id, skb_put(skb, 2));
- skb_queue_tail(&data->readq, skb);
+ skb_queue_head(&data->readq, skb);
+ atomic_inc(&data->initialized);
wake_up_interruptible(&data->read_wait);
return 0;
@@ -528,14 +633,14 @@ static void vhci_open_timeout(struct work_struct *work)
struct vhci_data *data = container_of(work, struct vhci_data,
open_timeout.work);
- vhci_create_device(data, amp ? HCI_AMP : HCI_PRIMARY);
+ vhci_create_device(data, 0x00);
}
static int vhci_open(struct inode *inode, struct file *file)
{
struct vhci_data *data;
- data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -549,11 +654,26 @@ static int vhci_open(struct inode *inode, struct file *file)
file->private_data = data;
nonseekable_open(inode, file);
- schedule_delayed_work(&data->open_timeout, msecs_to_jiffies(1000));
+ schedule_delayed_work(&data->open_timeout, secs_to_jiffies(1));
return 0;
}
+static void vhci_debugfs_remove(struct hci_dev *hdev)
+{
+ debugfs_lookup_and_remove("force_suspend", hdev->debugfs);
+
+ debugfs_lookup_and_remove("force_wakeup", hdev->debugfs);
+
+ if (IS_ENABLED(CONFIG_BT_MSFTEXT))
+ debugfs_lookup_and_remove("msft_opcode", hdev->debugfs);
+
+ if (IS_ENABLED(CONFIG_BT_AOSPEXT))
+ debugfs_lookup_and_remove("aosp_capable", hdev->debugfs);
+
+ debugfs_lookup_and_remove("force_devcoredump", hdev->debugfs);
+}
+
static int vhci_release(struct inode *inode, struct file *file)
{
struct vhci_data *data = file->private_data;
@@ -565,6 +685,8 @@ static int vhci_release(struct inode *inode, struct file *file)
hdev = data->hdev;
if (hdev) {
+ if (!IS_ERR_OR_NULL(hdev->debugfs))
+ vhci_debugfs_remove(hdev);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
@@ -583,7 +705,6 @@ static const struct file_operations vhci_fops = {
.poll = vhci_poll,
.open = vhci_open,
.release = vhci_release,
- .llseek = no_llseek,
};
static struct miscdevice vhci_miscdev = {
diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 67c21263f9e0..6f1a37e85c6a 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -50,8 +50,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt)
static int virtbt_open(struct hci_dev *hdev)
{
- struct virtio_bluetooth *vbt = hci_get_drvdata(hdev);
+ return 0;
+}
+static int virtbt_open_vdev(struct virtio_bluetooth *vbt)
+{
if (virtbt_add_inbuf(vbt) < 0)
return -EIO;
@@ -61,7 +64,11 @@ static int virtbt_open(struct hci_dev *hdev)
static int virtbt_close(struct hci_dev *hdev)
{
- struct virtio_bluetooth *vbt = hci_get_drvdata(hdev);
+ return 0;
+}
+
+static int virtbt_close_vdev(struct virtio_bluetooth *vbt)
+{
int i;
cancel_work_sync(&vbt->rx);
@@ -72,6 +79,7 @@ static int virtbt_close(struct hci_dev *hdev)
while ((skb = virtqueue_detach_unused_buf(vq)))
kfree_skb(skb);
+ cond_resched();
}
return 0;
@@ -219,7 +227,7 @@ static void virtbt_rx_work(struct work_struct *work)
if (!skb)
return;
- skb->len = len;
+ skb_put(skb, len);
virtbt_rx_handle(vbt, skb);
if (virtbt_add_inbuf(vbt) < 0)
@@ -246,13 +254,9 @@ static void virtbt_rx_done(struct virtqueue *vq)
static int virtbt_probe(struct virtio_device *vdev)
{
- vq_callback_t *callbacks[VIRTBT_NUM_VQS] = {
- [VIRTBT_VQ_TX] = virtbt_tx_done,
- [VIRTBT_VQ_RX] = virtbt_rx_done,
- };
- const char *names[VIRTBT_NUM_VQS] = {
- [VIRTBT_VQ_TX] = "tx",
- [VIRTBT_VQ_RX] = "rx",
+ struct virtqueue_info vqs_info[VIRTBT_NUM_VQS] = {
+ [VIRTBT_VQ_TX] = { "tx", virtbt_tx_done },
+ [VIRTBT_VQ_RX] = { "rx", virtbt_rx_done },
};
struct virtio_bluetooth *vbt;
struct hci_dev *hdev;
@@ -266,7 +270,6 @@ static int virtbt_probe(struct virtio_device *vdev)
switch (type) {
case VIRTIO_BT_CONFIG_TYPE_PRIMARY:
- case VIRTIO_BT_CONFIG_TYPE_AMP:
break;
default:
return -EINVAL;
@@ -281,8 +284,7 @@ static int virtbt_probe(struct virtio_device *vdev)
INIT_WORK(&vbt->rx, virtbt_rx_work);
- err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, callbacks,
- names, NULL);
+ err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, vqs_info, NULL);
if (err)
return err;
@@ -295,7 +297,6 @@ static int virtbt_probe(struct virtio_device *vdev)
vbt->hdev = hdev;
hdev->bus = HCI_VIRTIO;
- hdev->dev_type = type;
hci_set_drvdata(hdev, vbt);
hdev->open = virtbt_open;
@@ -306,7 +307,12 @@ static int virtbt_probe(struct virtio_device *vdev)
if (virtio_has_feature(vdev, VIRTIO_BT_F_VND_HCI)) {
__u16 vendor;
- virtio_cread(vdev, struct virtio_bt_config, vendor, &vendor);
+ if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2))
+ virtio_cread(vdev, struct virtio_bt_config_v2,
+ vendor, &vendor);
+ else
+ virtio_cread(vdev, struct virtio_bt_config,
+ vendor, &vendor);
switch (vendor) {
case VIRTIO_BT_CONFIG_VENDOR_ZEPHYR:
@@ -321,17 +327,17 @@ static int virtbt_probe(struct virtio_device *vdev)
hdev->setup = virtbt_setup_intel;
hdev->shutdown = virtbt_shutdown_generic;
hdev->set_bdaddr = virtbt_set_bdaddr_intel;
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
break;
case VIRTIO_BT_CONFIG_VENDOR_REALTEK:
hdev->manufacturer = 93;
hdev->setup = virtbt_setup_realtek;
hdev->shutdown = virtbt_shutdown_generic;
- set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
- set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+ hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
break;
}
}
@@ -339,8 +345,12 @@ static int virtbt_probe(struct virtio_device *vdev)
if (virtio_has_feature(vdev, VIRTIO_BT_F_MSFT_EXT)) {
__u16 msft_opcode;
- virtio_cread(vdev, struct virtio_bt_config,
- msft_opcode, &msft_opcode);
+ if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2))
+ virtio_cread(vdev, struct virtio_bt_config_v2,
+ msft_opcode, &msft_opcode);
+ else
+ virtio_cread(vdev, struct virtio_bt_config,
+ msft_opcode, &msft_opcode);
hci_set_msft_opcode(hdev, msft_opcode);
}
@@ -354,8 +364,15 @@ static int virtbt_probe(struct virtio_device *vdev)
goto failed;
}
+ virtio_device_ready(vdev);
+ err = virtbt_open_vdev(vbt);
+ if (err)
+ goto open_failed;
+
return 0;
+open_failed:
+ hci_free_dev(hdev);
failed:
vdev->config->del_vqs(vdev);
return err;
@@ -368,6 +385,7 @@ static void virtbt_remove(struct virtio_device *vdev)
hci_unregister_dev(hdev);
virtio_reset_device(vdev);
+ virtbt_close_vdev(vbt);
hci_free_dev(hdev);
vbt->hdev = NULL;
@@ -387,11 +405,11 @@ static const unsigned int virtbt_features[] = {
VIRTIO_BT_F_VND_HCI,
VIRTIO_BT_F_MSFT_EXT,
VIRTIO_BT_F_AOSP_EXT,
+ VIRTIO_BT_F_CONFIG_V2,
};
static struct virtio_driver virtbt_driver = {
.driver.name = KBUILD_MODNAME,
- .driver.owner = THIS_MODULE,
.feature_table = virtbt_features,
.feature_table_size = ARRAY_SIZE(virtbt_features),
.id_table = virtbt_table,
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 7bfe998f3514..fe7600283e70 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -31,7 +31,7 @@ config ARM_INTEGRATOR_LM
config BRCMSTB_GISB_ARB
tristate "Broadcom STB GISB bus arbiter"
- depends on ARM || ARM64 || MIPS
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC
default ARCH_BRCMSTB || BMIPS_GENERIC
help
Driver for the Broadcom Set Top Box System-on-a-chip internal bus
@@ -81,15 +81,21 @@ config MOXTET
config HISILICON_LPC
bool "Support for ISA I/O space on HiSilicon Hip06/7"
depends on (ARM64 && ARCH_HISI) || (COMPILE_TEST && !ALPHA && !HEXAGON && !PARISC)
- depends on HAS_IOMEM
+ depends on HAS_IOPORT
select INDIRECT_PIO if ARM64
help
Driver to enable I/O access to devices attached to the Low Pin
Count bus on the HiSilicon Hip06/7 SoC.
+config IMX_AIPSTZ
+ tristate "Support for IMX Secure AHB to IP Slave bus (AIPSTZ) bridge"
+ depends on ARCH_MXC
+ help
+ Enable support for IMX AIPSTZ bridge.
+
config IMX_WEIM
bool "Freescale EIM DRIVER"
- depends on ARCH_MXC
+ depends on ARCH_MXC || COMPILE_TEST
help
Driver for i.MX WEIM controller.
The WEIM(Wireless External Interface Module) works like a bus.
@@ -163,6 +169,16 @@ config QCOM_SSC_BLOCK_BUS
i2c/spi/uart controllers, a hexagon core, and a clock controller
which provides clocks for the above.
+config STM32_FIREWALL
+ bool "STM32 Firewall framework"
+ depends on (ARCH_STM32 || COMPILE_TEST) && OF
+ select OF_DYNAMIC
+ help
+ Say y to enable STM32 firewall framework and its services. Firewall
+ controllers will be able to register to the framework. Access for
+ hardware resources linked to a firewall controller can be requested
+ through this STM32 framework.
+
config SUN50I_DE2_BUS
bool "Allwinner A64 DE2 Bus Driver"
default ARM64
@@ -186,11 +202,12 @@ config SUNXI_RSB
config TEGRA_ACONNECT
tristate "Tegra ACONNECT Bus Driver"
- depends on ARCH_TEGRA_210_SOC
+ depends on ARCH_TEGRA
depends on OF && PM
help
Driver for the Tegra ACONNECT bus which is used to interface with
- the devices inside the Audio Processing Engine (APE) for Tegra210.
+ the devices inside the Audio Processing Engine (APE) for
+ Tegra210 and later.
config TEGRA_GMI
tristate "Tegra Generic Memory Interface bus driver"
@@ -210,7 +227,8 @@ config TI_PWMSS
config TI_SYSC
bool "TI sysc interconnect target module driver"
- depends on ARCH_OMAP2PLUS
+ depends on ARCH_OMAP2PLUS || ARCH_K3
+ default y
help
Generic driver for Texas Instruments interconnect target module
found on many TI SoCs.
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index d90eed189a65..8e693fe8a03a 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
obj-$(CONFIG_BT1_APB) += bt1-apb.o
obj-$(CONFIG_BT1_AXI) += bt1-axi.o
+obj-$(CONFIG_IMX_AIPSTZ) += imx-aipstz.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
obj-$(CONFIG_INTEL_IXP4XX_EB) += intel-ixp4xx-eb.o
obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
@@ -26,6 +27,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o
obj-$(CONFIG_QCOM_SSC_BLOCK_BUS) += qcom-ssc-block-bus.o
+obj-$(CONFIG_STM32_FIREWALL) += stm32_firewall.o stm32_rifsc.o stm32_etzpc.o
obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
obj-$(CONFIG_OF) += simple-pm-bus.o
diff --git a/drivers/bus/arm-integrator-lm.c b/drivers/bus/arm-integrator-lm.c
index 2344d560b144..a65c79b08804 100644
--- a/drivers/bus/arm-integrator-lm.c
+++ b/drivers/bus/arm-integrator-lm.c
@@ -85,6 +85,7 @@ static int integrator_ap_lm_probe(struct platform_device *pdev)
return -ENODEV;
}
map = syscon_node_to_regmap(syscon);
+ of_node_put(syscon);
if (IS_ERR(map)) {
dev_err(dev,
"could not find Integrator/AP system controller\n");
@@ -126,4 +127,3 @@ static struct platform_driver integrator_ap_lm_driver = {
module_platform_driver(integrator_ap_lm_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("Integrator AP Logical Module driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c
index b0c3704777e9..91ef99c42344 100644
--- a/drivers/bus/brcmstb_gisb.c
+++ b/drivers/bus/brcmstb_gisb.c
@@ -96,6 +96,20 @@ static const int gisb_offsets_bcm7400[] = {
[ARB_ERR_CAP_MASTER] = 0x0d8,
};
+static const int gisb_offsets_bcm74165[] = {
+ [ARB_TIMER] = 0x008,
+ [ARB_BP_CAP_CLR] = 0x044,
+ [ARB_BP_CAP_HI_ADDR] = -1,
+ [ARB_BP_CAP_ADDR] = 0x048,
+ [ARB_BP_CAP_STATUS] = 0x058,
+ [ARB_BP_CAP_MASTER] = 0x05c,
+ [ARB_ERR_CAP_CLR] = 0x038,
+ [ARB_ERR_CAP_HI_ADDR] = -1,
+ [ARB_ERR_CAP_ADDR] = 0x020,
+ [ARB_ERR_CAP_STATUS] = 0x030,
+ [ARB_ERR_CAP_MASTER] = 0x034,
+};
+
static const int gisb_offsets_bcm7435[] = {
[ARB_TIMER] = 0x00c,
[ARB_BP_CAP_CLR] = 0x014,
@@ -381,10 +395,7 @@ static struct attribute *gisb_arb_sysfs_attrs[] = {
&dev_attr_gisb_arb_timeout.attr,
NULL,
};
-
-static struct attribute_group gisb_arb_sysfs_attr_group = {
- .attrs = gisb_arb_sysfs_attrs,
-};
+ATTRIBUTE_GROUPS(gisb_arb_sysfs);
static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,gisb-arb", .data = gisb_offsets_bcm7445 },
@@ -393,20 +404,20 @@ static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 },
{ .compatible = "brcm,bcm7278-gisb-arb", .data = gisb_offsets_bcm7278 },
{ .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 },
+ { .compatible = "brcm,bcm74165-gisb-arb", .data = gisb_offsets_bcm74165 },
{ },
};
+MODULE_DEVICE_TABLE(of, brcmstb_gisb_arb_of_match);
static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
struct brcmstb_gisb_arb_device *gdev;
const struct of_device_id *of_id;
- struct resource *r;
int err, timeout_irq, tea_irq, bp_irq;
unsigned int num_masters, j = 0;
int i, first, last;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
timeout_irq = platform_get_irq(pdev, 0);
tea_irq = platform_get_irq(pdev, 1);
bp_irq = platform_get_irq(pdev, 2);
@@ -418,7 +429,7 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
mutex_init(&gdev->lock);
INIT_LIST_HEAD(&gdev->next);
- gdev->base = devm_ioremap_resource(&pdev->dev, r);
+ gdev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(gdev->base))
return PTR_ERR(gdev->base);
@@ -476,10 +487,6 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
}
}
- err = sysfs_create_group(&pdev->dev.kobj, &gisb_arb_sysfs_attr_group);
- if (err)
- return err;
-
platform_set_drvdata(pdev, gdev);
list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
@@ -536,6 +543,7 @@ static struct platform_driver brcmstb_gisb_arb_driver = {
.name = "brcm-gisb-arb",
.of_match_table = brcmstb_gisb_arb_of_match,
.pm = &brcmstb_gisb_arb_pm_ops,
+ .dev_groups = gisb_arb_sysfs_groups,
},
};
diff --git a/drivers/bus/bt1-apb.c b/drivers/bus/bt1-apb.c
index 63b1b4a76671..7463124b6dd9 100644
--- a/drivers/bus/bt1-apb.c
+++ b/drivers/bus/bt1-apb.c
@@ -22,7 +22,6 @@
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/time64.h>
-#include <linux/clk.h>
#include <linux/sysfs.h>
#define APB_EHB_ISR 0x00
@@ -186,34 +185,13 @@ static int bt1_apb_request_rst(struct bt1_apb *apb)
return ret;
}
-static void bt1_apb_disable_clk(void *data)
-{
- struct bt1_apb *apb = data;
-
- clk_disable_unprepare(apb->pclk);
-}
-
static int bt1_apb_request_clk(struct bt1_apb *apb)
{
- int ret;
-
- apb->pclk = devm_clk_get(apb->dev, "pclk");
+ apb->pclk = devm_clk_get_enabled(apb->dev, "pclk");
if (IS_ERR(apb->pclk))
return dev_err_probe(apb->dev, PTR_ERR(apb->pclk),
"Couldn't get APB clock descriptor\n");
- ret = clk_prepare_enable(apb->pclk);
- if (ret) {
- dev_err(apb->dev, "Couldn't enable the APB clock\n");
- return ret;
- }
-
- ret = devm_add_action_or_reset(apb->dev, bt1_apb_disable_clk, apb);
- if (ret) {
- dev_err(apb->dev, "Can't add APB EHB clocks disable action\n");
- return ret;
- }
-
apb->rate = clk_get_rate(apb->pclk);
if (!apb->rate) {
dev_err(apb->dev, "Invalid clock rate\n");
@@ -416,4 +394,3 @@ module_platform_driver(bt1_apb_driver);
MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
MODULE_DESCRIPTION("Baikal-T1 APB-bus driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/bt1-axi.c b/drivers/bus/bt1-axi.c
index 70e49a6e5374..a5254c73bf43 100644
--- a/drivers/bus/bt1-axi.c
+++ b/drivers/bus/bt1-axi.c
@@ -146,33 +146,14 @@ static int bt1_axi_request_rst(struct bt1_axi *axi)
return ret;
}
-static void bt1_axi_disable_clk(void *data)
-{
- struct bt1_axi *axi = data;
-
- clk_disable_unprepare(axi->aclk);
-}
-
static int bt1_axi_request_clk(struct bt1_axi *axi)
{
- int ret;
-
- axi->aclk = devm_clk_get(axi->dev, "aclk");
+ axi->aclk = devm_clk_get_enabled(axi->dev, "aclk");
if (IS_ERR(axi->aclk))
return dev_err_probe(axi->dev, PTR_ERR(axi->aclk),
"Couldn't get AXI Interconnect clock\n");
- ret = clk_prepare_enable(axi->aclk);
- if (ret) {
- dev_err(axi->dev, "Couldn't enable the AXI clock\n");
- return ret;
- }
-
- ret = devm_add_action_or_reset(axi->dev, bt1_axi_disable_clk, axi);
- if (ret)
- dev_err(axi->dev, "Can't add AXI clock disable action\n");
-
- return ret;
+ return 0;
}
static int bt1_axi_request_irq(struct bt1_axi *axi)
@@ -309,4 +290,3 @@ module_platform_driver(bt1_axi_driver);
MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
MODULE_DESCRIPTION("Baikal-T1 AXI-bus driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/fsl-mc/Kconfig b/drivers/bus/fsl-mc/Kconfig
index b1fd55901c50..9492342e7d13 100644
--- a/drivers/bus/fsl-mc/Kconfig
+++ b/drivers/bus/fsl-mc/Kconfig
@@ -8,7 +8,7 @@
config FSL_MC_BUS
bool "QorIQ DPAA2 fsl-mc bus driver"
depends on OF && (ARCH_LAYERSCAPE || (COMPILE_TEST && (ARM || ARM64 || X86_LOCAL_APIC || PPC)))
- select GENERIC_MSI_IRQ_DOMAIN
+ select GENERIC_MSI_IRQ
help
Driver to enable the bus infrastructure for the QorIQ DPAA2
architecture. The fsl-mc bus driver handles discovery of
diff --git a/drivers/bus/fsl-mc/dpmcp.c b/drivers/bus/fsl-mc/dpmcp.c
index 5fbd0dbde24a..7816c0a728ef 100644
--- a/drivers/bus/fsl-mc/dpmcp.c
+++ b/drivers/bus/fsl-mc/dpmcp.c
@@ -75,25 +75,3 @@ int dpmcp_close(struct fsl_mc_io *mc_io,
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
-
-/**
- * dpmcp_reset() - Reset the DPMCP, returns the object to initial state.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPMCP object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpmcp_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct fsl_mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET,
- cmd_flags, token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c
index 5e70f9775a0e..c63a7e688db6 100644
--- a/drivers/bus/fsl-mc/dprc-driver.c
+++ b/drivers/bus/fsl-mc/dprc-driver.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/msi.h>
#include <linux/fsl/mc.h>
#include "fsl-mc-private.h"
@@ -23,8 +22,8 @@ struct fsl_mc_child_objs {
struct fsl_mc_obj_desc *child_array;
};
-static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev,
- struct fsl_mc_obj_desc *obj_desc)
+static bool fsl_mc_device_match(const struct fsl_mc_device *mc_dev,
+ const struct fsl_mc_obj_desc *obj_desc)
{
return mc_dev->obj_desc.id == obj_desc->id &&
strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
@@ -46,6 +45,9 @@ static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
struct fsl_mc_child_objs *objs;
struct fsl_mc_device *mc_dev;
+ if (!dev_is_fsl_mc(dev))
+ return 0;
+
mc_dev = to_fsl_mc_device(dev);
objs = data;
@@ -65,6 +67,9 @@ static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
static int __fsl_mc_device_remove(struct device *dev, void *data)
{
+ if (!dev_is_fsl_mc(dev))
+ return 0;
+
fsl_mc_device_remove(to_fsl_mc_device(dev));
return 0;
}
@@ -107,9 +112,9 @@ void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
}
EXPORT_SYMBOL_GPL(dprc_remove_devices);
-static int __fsl_mc_device_match(struct device *dev, void *data)
+static int __fsl_mc_device_match(struct device *dev, const void *data)
{
- struct fsl_mc_obj_desc *obj_desc = data;
+ const struct fsl_mc_obj_desc *obj_desc = data;
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
return fsl_mc_device_match(mc_dev, obj_desc);
@@ -801,8 +806,6 @@ int dprc_cleanup(struct fsl_mc_device *mc_dev)
dev_set_msi_domain(&mc_dev->dev, NULL);
}
- fsl_mc_cleanup_all_resource_pools(mc_dev);
-
/* if this step fails we cannot go further with cleanup as there is no way of
* communicating with the firmware
*/
@@ -836,15 +839,14 @@ EXPORT_SYMBOL_GPL(dprc_cleanup);
* It tears down the interrupts that were configured for the DPRC device.
* It destroys the interrupt pool associated with this MC bus.
*/
-static int dprc_remove(struct fsl_mc_device *mc_dev)
+static void dprc_remove(struct fsl_mc_device *mc_dev)
{
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
- if (!is_fsl_mc_bus_dprc(mc_dev))
- return -EINVAL;
-
- if (!mc_bus->irq_resources)
- return -EINVAL;
+ if (!mc_bus->irq_resources) {
+ dev_err(&mc_dev->dev, "No irq resources, so unbinding the device failed\n");
+ return;
+ }
if (dev_get_msi_domain(&mc_dev->dev))
dprc_teardown_irq(mc_dev);
@@ -854,7 +856,6 @@ static int dprc_remove(struct fsl_mc_device *mc_dev)
dprc_cleanup(mc_dev);
dev_info(&mc_dev->dev, "DPRC device unbound from driver");
- return 0;
}
static const struct fsl_mc_device_id match_id_table[] = {
diff --git a/drivers/bus/fsl-mc/dprc.c b/drivers/bus/fsl-mc/dprc.c
index d129338b8bc0..38d40c09b719 100644
--- a/drivers/bus/fsl-mc/dprc.c
+++ b/drivers/bus/fsl-mc/dprc.c
@@ -450,10 +450,8 @@ int dprc_get_obj(struct fsl_mc_io *mc_io,
obj_desc->ver_major = le16_to_cpu(rsp_params->version_major);
obj_desc->ver_minor = le16_to_cpu(rsp_params->version_minor);
obj_desc->flags = le16_to_cpu(rsp_params->flags);
- strncpy(obj_desc->type, rsp_params->type, 16);
- obj_desc->type[15] = '\0';
- strncpy(obj_desc->label, rsp_params->label, 16);
- obj_desc->label[15] = '\0';
+ strscpy_pad(obj_desc->type, rsp_params->type, 16);
+ strscpy_pad(obj_desc->label, rsp_params->label, 16);
return 0;
}
EXPORT_SYMBOL_GPL(dprc_get_obj);
@@ -491,8 +489,7 @@ int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr);
cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num);
cmd_params->obj_id = cpu_to_le32(obj_id);
- strncpy(cmd_params->obj_type, obj_type, 16);
- cmd_params->obj_type[15] = '\0';
+ strscpy(cmd_params->obj_type, obj_type);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
@@ -564,8 +561,7 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io,
cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params;
cmd_params->obj_id = cpu_to_le32(obj_id);
cmd_params->region_index = region_index;
- strncpy(cmd_params->obj_type, obj_type, 16);
- cmd_params->obj_type[15] = '\0';
+ strscpy(cmd_params->obj_type, obj_type);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c
index dced427ca8ba..d2ea59471323 100644
--- a/drivers/bus/fsl-mc/fsl-mc-allocator.c
+++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c
@@ -103,26 +103,32 @@ static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
struct fsl_mc_resource *resource;
int error = -EINVAL;
- if (!fsl_mc_is_allocatable(mc_dev))
- goto out;
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
resource = mc_dev->resource;
- if (!resource || resource->data != mc_dev)
+ if (!resource || resource->data != mc_dev) {
+ dev_err(&mc_bus_dev->dev, "resource mismatch\n");
goto out;
+ }
- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
- mc_bus = to_fsl_mc_bus(mc_bus_dev);
res_pool = resource->parent_pool;
- if (res_pool != &mc_bus->resource_pools[resource->type])
+ if (res_pool != &mc_bus->resource_pools[resource->type]) {
+ dev_err(&mc_bus_dev->dev, "pool mismatch\n");
goto out;
+ }
mutex_lock(&res_pool->mutex);
- if (res_pool->max_count <= 0)
+ if (res_pool->max_count <= 0) {
+ dev_err(&mc_bus_dev->dev, "max_count underflow\n");
goto out_unlock;
+ }
if (res_pool->free_count <= 0 ||
- res_pool->free_count > res_pool->max_count)
+ res_pool->free_count > res_pool->max_count) {
+ dev_err(&mc_bus_dev->dev, "free_count mismatch\n");
goto out_unlock;
+ }
/*
* If the device is currently allocated, its resource is not
@@ -549,30 +555,6 @@ void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
}
}
-static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
- enum fsl_mc_pool_type pool_type)
-{
- struct fsl_mc_resource *resource;
- struct fsl_mc_resource *next;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
- struct fsl_mc_resource_pool *res_pool =
- &mc_bus->resource_pools[pool_type];
- int free_count = 0;
-
- list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
- free_count++;
- devm_kfree(&mc_bus_dev->dev, resource);
- }
-}
-
-void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
-{
- int pool_type;
-
- for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
- fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
-}
-
/*
* fsl_mc_allocator_probe - callback invoked when an allocatable device is
* being added to the system
@@ -609,22 +591,18 @@ static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
* fsl_mc_allocator_remove - callback invoked when an allocatable device is
* being removed from the system
*/
-static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
+static void fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
{
int error;
- if (!fsl_mc_is_allocatable(mc_dev))
- return -EINVAL;
-
if (mc_dev->resource) {
error = fsl_mc_resource_pool_remove_device(mc_dev);
if (error < 0)
- return error;
+ return;
}
dev_dbg(&mc_dev->dev,
"Allocatable fsl-mc device unbound from fsl_mc_allocator driver");
- return 0;
}
static const struct fsl_mc_device_id match_id_table[] = {
@@ -657,8 +635,3 @@ int __init fsl_mc_allocator_driver_init(void)
{
return fsl_mc_driver_register(&fsl_mc_allocator_driver);
}
-
-void fsl_mc_allocator_driver_exit(void)
-{
- fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
-}
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 6143dbf31f31..25845c04e562 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -14,10 +14,10 @@
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/ioport.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/limits.h>
#include <linux/bitops.h>
-#include <linux/msi.h>
#include <linux/dma-mapping.h>
#include <linux/acpi.h>
#include <linux/iommu.h>
@@ -80,11 +80,11 @@ static phys_addr_t mc_portal_base_phys_addr;
*
* Returns 1 on success, 0 otherwise.
*/
-static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
+static int fsl_mc_bus_match(struct device *dev, const struct device_driver *drv)
{
const struct fsl_mc_device_id *id;
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
+ const struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
bool found = false;
/* When driver_override is set, only bind to the matching driver */
@@ -125,9 +125,9 @@ out:
/*
* fsl_mc_bus_uevent - callback invoked when a device is added
*/
-static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ const struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s",
mc_dev->obj_desc.vendor,
@@ -139,9 +139,9 @@ static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
static int fsl_mc_dma_configure(struct device *dev)
{
+ const struct device_driver *drv = READ_ONCE(dev->driver);
struct device *dma_dev = dev;
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
u32 input_id = mc_dev->icid;
int ret;
@@ -153,7 +153,8 @@ static int fsl_mc_dma_configure(struct device *dev)
else
ret = acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id);
- if (!ret && !mc_drv->driver_managed_dma) {
+ /* @drv may not be valid when we're called from the IOMMU layer */
+ if (!ret && drv && !to_fsl_mc_driver(drv)->driver_managed_dma) {
ret = iommu_device_use_default_domain(dev);
if (ret)
arch_teardown_dma_ops(dev);
@@ -175,8 +176,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
{
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor,
- mc_dev->obj_desc.type);
+ return sysfs_emit(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor,
+ mc_dev->obj_desc.type);
}
static DEVICE_ATTR_RO(modalias);
@@ -202,7 +203,7 @@ static ssize_t driver_override_show(struct device *dev,
{
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override);
+ return sysfs_emit(buf, "%s\n", mc_dev->driver_override);
}
static DEVICE_ATTR_RW(driver_override);
@@ -232,7 +233,7 @@ exit:
return 0;
}
-static ssize_t rescan_store(struct bus_type *bus,
+static ssize_t rescan_store(const struct bus_type *bus,
const char *buf, size_t count)
{
unsigned long val;
@@ -285,7 +286,7 @@ exit:
return 0;
}
-static ssize_t autorescan_store(struct bus_type *bus,
+static ssize_t autorescan_store(const struct bus_type *bus,
const char *buf, size_t count)
{
bus_for_each_dev(bus, NULL, (void *)buf, fsl_mc_bus_set_autorescan);
@@ -293,7 +294,7 @@ static ssize_t autorescan_store(struct bus_type *bus,
return count;
}
-static ssize_t autorescan_show(struct bus_type *bus, char *buf)
+static ssize_t autorescan_show(const struct bus_type *bus, char *buf)
{
bus_for_each_dev(bus, NULL, (void *)buf, fsl_mc_bus_get_autorescan);
return strlen(buf);
@@ -309,7 +310,7 @@ static struct attribute *fsl_mc_bus_attrs[] = {
ATTRIBUTE_GROUPS(fsl_mc_bus);
-struct bus_type fsl_mc_bus_type = {
+const struct bus_type fsl_mc_bus_type = {
.name = "fsl-mc",
.match = fsl_mc_bus_match,
.uevent = fsl_mc_bus_uevent,
@@ -320,90 +321,90 @@ struct bus_type fsl_mc_bus_type = {
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
-struct device_type fsl_mc_bus_dprc_type = {
+const struct device_type fsl_mc_bus_dprc_type = {
.name = "fsl_mc_bus_dprc"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type);
-struct device_type fsl_mc_bus_dpni_type = {
+const struct device_type fsl_mc_bus_dpni_type = {
.name = "fsl_mc_bus_dpni"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type);
-struct device_type fsl_mc_bus_dpio_type = {
+const struct device_type fsl_mc_bus_dpio_type = {
.name = "fsl_mc_bus_dpio"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type);
-struct device_type fsl_mc_bus_dpsw_type = {
+const struct device_type fsl_mc_bus_dpsw_type = {
.name = "fsl_mc_bus_dpsw"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type);
-struct device_type fsl_mc_bus_dpbp_type = {
+const struct device_type fsl_mc_bus_dpbp_type = {
.name = "fsl_mc_bus_dpbp"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type);
-struct device_type fsl_mc_bus_dpcon_type = {
+const struct device_type fsl_mc_bus_dpcon_type = {
.name = "fsl_mc_bus_dpcon"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type);
-struct device_type fsl_mc_bus_dpmcp_type = {
+const struct device_type fsl_mc_bus_dpmcp_type = {
.name = "fsl_mc_bus_dpmcp"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type);
-struct device_type fsl_mc_bus_dpmac_type = {
+const struct device_type fsl_mc_bus_dpmac_type = {
.name = "fsl_mc_bus_dpmac"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type);
-struct device_type fsl_mc_bus_dprtc_type = {
+const struct device_type fsl_mc_bus_dprtc_type = {
.name = "fsl_mc_bus_dprtc"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type);
-struct device_type fsl_mc_bus_dpseci_type = {
+const struct device_type fsl_mc_bus_dpseci_type = {
.name = "fsl_mc_bus_dpseci"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type);
-struct device_type fsl_mc_bus_dpdmux_type = {
+const struct device_type fsl_mc_bus_dpdmux_type = {
.name = "fsl_mc_bus_dpdmux"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmux_type);
-struct device_type fsl_mc_bus_dpdcei_type = {
+const struct device_type fsl_mc_bus_dpdcei_type = {
.name = "fsl_mc_bus_dpdcei"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdcei_type);
-struct device_type fsl_mc_bus_dpaiop_type = {
+const struct device_type fsl_mc_bus_dpaiop_type = {
.name = "fsl_mc_bus_dpaiop"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpaiop_type);
-struct device_type fsl_mc_bus_dpci_type = {
+const struct device_type fsl_mc_bus_dpci_type = {
.name = "fsl_mc_bus_dpci"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpci_type);
-struct device_type fsl_mc_bus_dpdmai_type = {
+const struct device_type fsl_mc_bus_dpdmai_type = {
.name = "fsl_mc_bus_dpdmai"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type);
-struct device_type fsl_mc_bus_dpdbg_type = {
+const struct device_type fsl_mc_bus_dpdbg_type = {
.name = "fsl_mc_bus_dpdbg"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdbg_type);
-static struct device_type *fsl_mc_get_device_type(const char *type)
+static const struct device_type *fsl_mc_get_device_type(const char *type)
{
static const struct {
- struct device_type *dev_type;
+ const struct device_type *dev_type;
const char *type;
} dev_types[] = {
{ &fsl_mc_bus_dprc_type, "dprc" },
@@ -455,13 +456,8 @@ static int fsl_mc_driver_remove(struct device *dev)
{
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- int error;
- error = mc_drv->remove(mc_dev);
- if (error < 0) {
- dev_err(dev, "%s failed: %d\n", __func__, error);
- return error;
- }
+ mc_drv->remove(mc_dev);
return 0;
}
@@ -910,8 +906,10 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
error_cleanup_dev:
kfree(mc_dev->regions);
- kfree(mc_bus);
- kfree(mc_dev);
+ if (mc_bus)
+ kfree(mc_bus);
+ else
+ kfree(mc_dev);
return error;
}
@@ -945,6 +943,7 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev,
struct fsl_mc_obj_desc endpoint_desc = {{ 0 }};
struct dprc_endpoint endpoint1 = {{ 0 }};
struct dprc_endpoint endpoint2 = {{ 0 }};
+ struct fsl_mc_bus *mc_bus;
int state, err;
mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
@@ -968,6 +967,8 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev,
strcpy(endpoint_desc.type, endpoint2.type);
endpoint_desc.id = endpoint2.id;
endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev);
+ if (endpoint)
+ return endpoint;
/*
* We know that the device has an endpoint because we verified by
@@ -975,17 +976,13 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev,
* yet discovered by the fsl-mc bus, thus the lookup returned NULL.
* Force a rescan of the devices in this container and retry the lookup.
*/
- if (!endpoint) {
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
-
- if (mutex_trylock(&mc_bus->scan_mutex)) {
- err = dprc_scan_objects(mc_bus_dev, true);
- mutex_unlock(&mc_bus->scan_mutex);
- }
-
- if (err < 0)
- return ERR_PTR(err);
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ if (mutex_trylock(&mc_bus->scan_mutex)) {
+ err = dprc_scan_objects(mc_bus_dev, true);
+ mutex_unlock(&mc_bus->scan_mutex);
}
+ if (err < 0)
+ return ERR_PTR(err);
endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev);
/*
@@ -1000,75 +997,18 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev,
}
EXPORT_SYMBOL_GPL(fsl_mc_get_endpoint);
-static int parse_mc_ranges(struct device *dev,
- int *paddr_cells,
- int *mc_addr_cells,
- int *mc_size_cells,
- const __be32 **ranges_start)
-{
- const __be32 *prop;
- int range_tuple_cell_count;
- int ranges_len;
- int tuple_len;
- struct device_node *mc_node = dev->of_node;
-
- *ranges_start = of_get_property(mc_node, "ranges", &ranges_len);
- if (!(*ranges_start) || !ranges_len) {
- dev_warn(dev,
- "missing or empty ranges property for device tree node '%pOFn'\n",
- mc_node);
- return 0;
- }
-
- *paddr_cells = of_n_addr_cells(mc_node);
-
- prop = of_get_property(mc_node, "#address-cells", NULL);
- if (prop)
- *mc_addr_cells = be32_to_cpup(prop);
- else
- *mc_addr_cells = *paddr_cells;
-
- prop = of_get_property(mc_node, "#size-cells", NULL);
- if (prop)
- *mc_size_cells = be32_to_cpup(prop);
- else
- *mc_size_cells = of_n_size_cells(mc_node);
-
- range_tuple_cell_count = *paddr_cells + *mc_addr_cells +
- *mc_size_cells;
-
- tuple_len = range_tuple_cell_count * sizeof(__be32);
- if (ranges_len % tuple_len != 0) {
- dev_err(dev, "malformed ranges property '%pOFn'\n", mc_node);
- return -EINVAL;
- }
-
- return ranges_len / tuple_len;
-}
-
static int get_mc_addr_translation_ranges(struct device *dev,
struct fsl_mc_addr_translation_range
**ranges,
u8 *num_ranges)
{
- int ret;
- int paddr_cells;
- int mc_addr_cells;
- int mc_size_cells;
- int i;
- const __be32 *ranges_start;
- const __be32 *cell;
-
- ret = parse_mc_ranges(dev,
- &paddr_cells,
- &mc_addr_cells,
- &mc_size_cells,
- &ranges_start);
- if (ret < 0)
- return ret;
+ struct fsl_mc_addr_translation_range *r;
+ struct of_range_parser parser;
+ struct of_range range;
- *num_ranges = ret;
- if (!ret) {
+ of_range_parser_init(&parser, dev->of_node);
+ *num_ranges = of_range_count(&parser);
+ if (!*num_ranges) {
/*
* Missing or empty ranges property ("ranges;") for the
* 'fsl,qoriq-mc' node. In this case, identity mapping
@@ -1084,20 +1024,13 @@ static int get_mc_addr_translation_ranges(struct device *dev,
if (!(*ranges))
return -ENOMEM;
- cell = ranges_start;
- for (i = 0; i < *num_ranges; ++i) {
- struct fsl_mc_addr_translation_range *range = &(*ranges)[i];
-
- range->mc_region_type = of_read_number(cell, 1);
- range->start_mc_offset = of_read_number(cell + 1,
- mc_addr_cells - 1);
- cell += mc_addr_cells;
- range->start_phys_addr = of_read_number(cell, paddr_cells);
- cell += paddr_cells;
- range->end_mc_offset = range->start_mc_offset +
- of_read_number(cell, mc_size_cells);
-
- cell += mc_size_cells;
+ r = *ranges;
+ for_each_of_range(&parser, &range) {
+ r->mc_region_type = range.flags;
+ r->start_mc_offset = range.bus_addr;
+ r->end_mc_offset = range.bus_addr + range.size;
+ r->start_phys_addr = range.cpu_addr;
+ r++;
}
return 0;
@@ -1171,6 +1104,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
* Get physical address of MC portal for the root DPRC:
*/
plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!plat_res)
+ return -EINVAL;
+
mc_portal_phys_addr = plat_res->start;
mc_portal_size = resource_size(plat_res);
mc_portal_base_phys_addr = mc_portal_phys_addr & ~0x3ffffff;
@@ -1236,14 +1172,11 @@ error_cleanup_mc_io:
* fsl_mc_bus_remove - callback invoked when the root MC bus is being
* removed
*/
-static int fsl_mc_bus_remove(struct platform_device *pdev)
+static void fsl_mc_bus_remove(struct platform_device *pdev)
{
struct fsl_mc *mc = platform_get_drvdata(pdev);
struct fsl_mc_io *mc_io;
- if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))
- return -EINVAL;
-
mc_io = mc->root_mc_bus_dev->mc_io;
fsl_mc_device_remove(mc->root_mc_bus_dev);
fsl_destroy_mc_io(mc_io);
@@ -1259,13 +1192,6 @@ static int fsl_mc_bus_remove(struct platform_device *pdev)
(GCR1_P1_STOP | GCR1_P2_STOP),
mc->fsl_mc_regs + FSL_MC_GCR1);
}
-
- return 0;
-}
-
-static void fsl_mc_bus_shutdown(struct platform_device *pdev)
-{
- fsl_mc_bus_remove(pdev);
}
static const struct of_device_id fsl_mc_bus_match_table[] = {
@@ -1290,7 +1216,7 @@ static struct platform_driver fsl_mc_bus_driver = {
},
.probe = fsl_mc_bus_probe,
.remove = fsl_mc_bus_remove,
- .shutdown = fsl_mc_bus_shutdown,
+ .shutdown = fsl_mc_bus_remove,
};
static int fsl_mc_bus_notifier(struct notifier_block *nb,
diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c
index 0cfe859a4ac4..82cd69f7884c 100644
--- a/drivers/bus/fsl-mc/fsl-mc-msi.c
+++ b/drivers/bus/fsl-mc/fsl-mc-msi.c
@@ -7,8 +7,6 @@
*
*/
-#include <linux/of_device.h>
-#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
@@ -213,21 +211,8 @@ struct irq_domain *fsl_mc_find_msi_domain(struct device *dev)
int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count)
{
- struct irq_domain *msi_domain;
- int error;
-
- msi_domain = dev_get_msi_domain(dev);
- if (!msi_domain)
- return -EINVAL;
-
- error = msi_setup_device_data(dev);
- if (error)
- return error;
+ int error = msi_setup_device_data(dev);
- msi_lock_descs(dev);
- if (msi_first_desc(dev, MSI_DESC_ALL))
- error = -EINVAL;
- msi_unlock_descs(dev);
if (error)
return error;
@@ -235,7 +220,7 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count)
* NOTE: Calling this function will trigger the invocation of the
* its_fsl_mc_msi_prepare() callback
*/
- error = msi_domain_alloc_irqs(msi_domain, dev, irq_count);
+ error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1);
if (error)
dev_err(dev, "Failed to allocate IRQs\n");
@@ -244,11 +229,5 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count)
void fsl_mc_msi_domain_free_irqs(struct device *dev)
{
- struct irq_domain *msi_domain;
-
- msi_domain = dev_get_msi_domain(dev);
- if (!msi_domain)
- return;
-
- msi_domain_free_irqs(msi_domain, dev);
+ msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
}
diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h
index b3520ea1b9f4..beed4c53533d 100644
--- a/drivers/bus/fsl-mc/fsl-mc-private.h
+++ b/drivers/bus/fsl-mc/fsl-mc-private.h
@@ -66,10 +66,6 @@ int dpmcp_close(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token);
-int dpmcp_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
/*
* Data Path Resource Container (DPRC) API
*/
@@ -631,12 +627,8 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
int __init fsl_mc_allocator_driver_init(void);
-void fsl_mc_allocator_driver_exit(void);
-
void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
-void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
-
int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
enum fsl_mc_pool_type pool_type,
struct fsl_mc_resource
diff --git a/drivers/bus/fsl-mc/fsl-mc-uapi.c b/drivers/bus/fsl-mc/fsl-mc-uapi.c
index 9c4c1395fcdb..823969e4159c 100644
--- a/drivers/bus/fsl-mc/fsl-mc-uapi.c
+++ b/drivers/bus/fsl-mc/fsl-mc-uapi.c
@@ -48,6 +48,7 @@ enum fsl_mc_cmd_index {
DPRC_GET_POOL,
DPRC_GET_POOL_COUNT,
DPRC_GET_CONNECTION,
+ DPRC_GET_MEM,
DPCI_GET_LINK_STATE,
DPCI_GET_PEER_ATTR,
DPAIOP_GET_SL_VERSION,
@@ -194,6 +195,12 @@ static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
.token = true,
.size = 32,
},
+ [DPRC_GET_MEM] = {
+ .cmdid_value = 0x16D0,
+ .cmdid_mask = 0xFFF0,
+ .token = true,
+ .size = 12,
+ },
[DPCI_GET_LINK_STATE] = {
.cmdid_value = 0x0E10,
@@ -275,13 +282,13 @@ static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
.size = 8,
},
[DPSW_GET_TAILDROP] = {
- .cmdid_value = 0x0A80,
+ .cmdid_value = 0x0A90,
.cmdid_mask = 0xFFF0,
.token = true,
.size = 14,
},
[DPSW_SET_TAILDROP] = {
- .cmdid_value = 0x0A90,
+ .cmdid_value = 0x0A80,
.cmdid_mask = 0xFFF0,
.token = true,
.size = 24,
diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c
index 95b10a6cf307..cd8754763f40 100644
--- a/drivers/bus/fsl-mc/mc-io.c
+++ b/drivers/bus/fsl-mc/mc-io.c
@@ -214,12 +214,19 @@ int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
if (error < 0)
goto error_cleanup_resource;
- dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev,
- &dpmcp_dev->dev,
- DL_FLAG_AUTOREMOVE_CONSUMER);
- if (!dpmcp_dev->consumer_link) {
- error = -EINVAL;
- goto error_cleanup_mc_io;
+ /* If the DPRC device itself tries to allocate a portal (usually for
+ * UAPI interaction), don't add a device link between them since the
+ * DPMCP device is an actual child device of the DPRC and a reverse
+ * dependency is not allowed.
+ */
+ if (mc_dev != mc_bus_dev) {
+ dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev,
+ &dpmcp_dev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ if (!dpmcp_dev->consumer_link) {
+ error = -EINVAL;
+ goto error_cleanup_mc_io;
+ }
}
*new_mc_io = mc_io;
@@ -263,23 +270,3 @@ void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
dpmcp_dev->consumer_link = NULL;
}
EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
-
-/**
- * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
- *
- * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
- */
-int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
-{
- int error;
- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
-
- error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
- if (error < 0) {
- dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
- return error;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
diff --git a/drivers/bus/fsl-mc/mc-sys.c b/drivers/bus/fsl-mc/mc-sys.c
index f2052cd0a051..31037f41893e 100644
--- a/drivers/bus/fsl-mc/mc-sys.c
+++ b/drivers/bus/fsl-mc/mc-sys.c
@@ -19,7 +19,7 @@
/*
* Timeout in milliseconds to wait for the completion of an MC command
*/
-#define MC_CMD_COMPLETION_TIMEOUT_MS 500
+#define MC_CMD_COMPLETION_TIMEOUT_MS 15000
/*
* usleep_range() min and max values used to throttle down polling
@@ -248,7 +248,7 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd)
enum mc_cmd_status status;
unsigned long irq_flags = 0;
- if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
+ if (in_hardirq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
return -EINVAL;
if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index 2e564803e786..53dd1573e323 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -13,9 +13,9 @@
#include <linux/logic_pio.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/serial_8250.h>
#include <linux/slab.h>
@@ -85,7 +85,7 @@ static int wait_lpc_idle(void __iomem *mbase, unsigned int waitcnt)
ndelay(LPC_NSEC_PERWAIT);
} while (--waitcnt);
- return -ETIME;
+ return -ETIMEDOUT;
}
/*
@@ -347,7 +347,7 @@ static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev,
unsigned long sys_port;
resource_size_t len = resource_size(res);
- sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
+ sys_port = logic_pio_trans_hwaddr(acpi_fwnode_handle(host), res->start, len);
if (sys_port == ~0UL)
return -EFAULT;
@@ -472,9 +472,7 @@ static int hisi_lpc_acpi_clear_enumerated(struct acpi_device *adev, void *not_us
struct hisi_lpc_acpi_cell {
const char *hid;
- const char *name;
- void *pdata;
- size_t pdata_size;
+ const struct platform_device_info *pdevinfo;
};
static void hisi_lpc_acpi_remove(struct device *hostdev)
@@ -505,28 +503,45 @@ static int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data)
/* ipmi */
{
.hid = "IPI0001",
- .name = "hisi-lpc-ipmi",
+ .pdevinfo = (struct platform_device_info []) {
+ {
+ .parent = hostdev,
+ .fwnode = acpi_fwnode_handle(child),
+ .name = "hisi-lpc-ipmi",
+ .id = PLATFORM_DEVID_AUTO,
+ .res = res,
+ .num_res = num_res,
+ },
+ },
},
/* 8250-compatible uart */
{
.hid = "HISI1031",
- .name = "serial8250",
- .pdata = (struct plat_serial8250_port []) {
+ .pdevinfo = (struct platform_device_info []) {
{
- .iobase = res->start,
- .uartclk = 1843200,
- .iotype = UPIO_PORT,
- .flags = UPF_BOOT_AUTOCONF,
+ .parent = hostdev,
+ .fwnode = acpi_fwnode_handle(child),
+ .name = "serial8250",
+ .id = PLATFORM_DEVID_AUTO,
+ .res = res,
+ .num_res = num_res,
+ .data = (struct plat_serial8250_port []) {
+ {
+ .iobase = res->start,
+ .uartclk = 1843200,
+ .iotype = UPIO_PORT,
+ .flags = UPF_BOOT_AUTOCONF,
+ },
+ {}
+ },
+ .size_data = 2 * sizeof(struct plat_serial8250_port),
},
- {}
},
- .pdata_size = 2 *
- sizeof(struct plat_serial8250_port),
},
{}
};
- for (; cell && cell->name; cell++) {
+ for (; cell && cell->hid; cell++) {
if (!strcmp(cell->hid, hid)) {
found = true;
break;
@@ -540,31 +555,12 @@ static int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data)
return 0;
}
- pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO);
- if (!pdev)
- return -ENOMEM;
-
- pdev->dev.parent = hostdev;
- ACPI_COMPANION_SET(&pdev->dev, child);
-
- ret = platform_device_add_resources(pdev, res, num_res);
- if (ret)
- goto fail;
-
- ret = platform_device_add_data(pdev, cell->pdata, cell->pdata_size);
- if (ret)
- goto fail;
-
- ret = platform_device_add(pdev);
- if (ret)
- goto fail;
+ pdev = platform_device_register_full(cell->pdevinfo);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
acpi_device_set_enumerated(child);
return 0;
-
-fail:
- platform_device_put(pdev);
- return ret;
}
/*
@@ -589,11 +585,6 @@ static int hisi_lpc_acpi_probe(struct device *hostdev)
return ret;
}
-
-static const struct acpi_device_id hisi_lpc_acpi_match[] = {
- {"HISI0191"},
- {}
-};
#else
static int hisi_lpc_acpi_probe(struct device *dev)
{
@@ -615,11 +606,9 @@ static void hisi_lpc_acpi_remove(struct device *hostdev)
static int hisi_lpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct acpi_device *acpi_device = ACPI_COMPANION(dev);
struct logic_pio_hwaddr *range;
struct hisi_lpc_dev *lpcdev;
resource_size_t io_end;
- struct resource *res;
int ret;
lpcdev = devm_kzalloc(dev, sizeof(*lpcdev), GFP_KERNEL);
@@ -628,8 +617,7 @@ static int hisi_lpc_probe(struct platform_device *pdev)
spin_lock_init(&lpcdev->cycle_lock);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- lpcdev->membase = devm_ioremap_resource(dev, res);
+ lpcdev->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lpcdev->membase))
return PTR_ERR(lpcdev->membase);
@@ -637,7 +625,7 @@ static int hisi_lpc_probe(struct platform_device *pdev)
if (!range)
return -ENOMEM;
- range->fwnode = dev->fwnode;
+ range->fwnode = dev_fwnode(dev);
range->flags = LOGIC_PIO_INDIRECT;
range->size = PIO_INDIRECT_SIZE;
range->hostdata = lpcdev;
@@ -651,7 +639,7 @@ static int hisi_lpc_probe(struct platform_device *pdev)
}
/* register the LPC host PIO resources */
- if (acpi_device)
+ if (is_acpi_device_node(range->fwnode))
ret = hisi_lpc_acpi_probe(dev);
else
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
@@ -669,21 +657,18 @@ static int hisi_lpc_probe(struct platform_device *pdev)
return ret;
}
-static int hisi_lpc_remove(struct platform_device *pdev)
+static void hisi_lpc_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct acpi_device *acpi_device = ACPI_COMPANION(dev);
struct hisi_lpc_dev *lpcdev = dev_get_drvdata(dev);
struct logic_pio_hwaddr *range = lpcdev->io_host;
- if (acpi_device)
+ if (is_acpi_device_node(range->fwnode))
hisi_lpc_acpi_remove(dev);
else
of_platform_depopulate(dev);
logic_pio_unregister_range(range);
-
- return 0;
}
static const struct of_device_id hisi_lpc_of_match[] = {
@@ -692,11 +677,16 @@ static const struct of_device_id hisi_lpc_of_match[] = {
{}
};
+static const struct acpi_device_id hisi_lpc_acpi_match[] = {
+ {"HISI0191"},
+ {}
+};
+
static struct platform_driver hisi_lpc_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = hisi_lpc_of_match,
- .acpi_match_table = ACPI_PTR(hisi_lpc_acpi_match),
+ .acpi_match_table = hisi_lpc_acpi_match,
},
.probe = hisi_lpc_probe,
.remove = hisi_lpc_remove,
diff --git a/drivers/bus/imx-aipstz.c b/drivers/bus/imx-aipstz.c
new file mode 100644
index 000000000000..5fdf377f5d06
--- /dev/null
+++ b/drivers/bus/imx-aipstz.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#define IMX_AIPSTZ_MPR0 0x0
+
+struct imx_aipstz_config {
+ u32 mpr0;
+};
+
+struct imx_aipstz_data {
+ void __iomem *base;
+ const struct imx_aipstz_config *default_cfg;
+};
+
+static void imx_aipstz_apply_default(struct imx_aipstz_data *data)
+{
+ writel(data->default_cfg->mpr0, data->base + IMX_AIPSTZ_MPR0);
+}
+
+static const struct of_device_id imx_aipstz_match_table[] = {
+ { .compatible = "simple-bus", },
+ { }
+};
+
+static int imx_aipstz_probe(struct platform_device *pdev)
+{
+ struct imx_aipstz_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "failed to allocate data memory\n");
+
+ data->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(data->base))
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "failed to get/ioremap AC memory\n");
+
+ data->default_cfg = of_device_get_match_data(&pdev->dev);
+
+ imx_aipstz_apply_default(data);
+
+ dev_set_drvdata(&pdev->dev, data);
+
+ pm_runtime_set_active(&pdev->dev);
+ devm_pm_runtime_enable(&pdev->dev);
+
+ return of_platform_populate(pdev->dev.of_node, imx_aipstz_match_table,
+ NULL, &pdev->dev);
+}
+
+static void imx_aipstz_remove(struct platform_device *pdev)
+{
+ of_platform_depopulate(&pdev->dev);
+}
+
+static int imx_aipstz_runtime_resume(struct device *dev)
+{
+ struct imx_aipstz_data *data = dev_get_drvdata(dev);
+
+ /* restore potentially lost configuration during domain power-off */
+ imx_aipstz_apply_default(data);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx_aipstz_pm_ops = {
+ RUNTIME_PM_OPS(NULL, imx_aipstz_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+/*
+ * following configuration is equivalent to:
+ * masters 0-7 => trusted for R/W + use AHB's HPROT[1] to det. privilege
+ */
+static const struct imx_aipstz_config imx8mp_aipstz_default_cfg = {
+ .mpr0 = 0x77777777,
+};
+
+static const struct of_device_id imx_aipstz_of_ids[] = {
+ { .compatible = "fsl,imx8mp-aipstz", .data = &imx8mp_aipstz_default_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(of, imx_aipstz_of_ids);
+
+static struct platform_driver imx_aipstz_of_driver = {
+ .probe = imx_aipstz_probe,
+ .remove = imx_aipstz_remove,
+ .driver = {
+ .name = "imx-aipstz",
+ .of_match_table = imx_aipstz_of_ids,
+ .pm = pm_ptr(&imx_aipstz_pm_ops),
+ },
+};
+module_platform_driver(imx_aipstz_of_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IMX secure AHB to IP Slave bus (AIPSTZ) bridge driver");
+MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>");
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 828c66bbaa67..83d623d97f5f 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -10,7 +10,11 @@
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/regmap.h>
@@ -86,8 +90,8 @@ MODULE_DEVICE_TABLE(of, weim_id_table);
static int imx_weim_gpr_setup(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct property *prop;
- const __be32 *p;
+ struct of_range_parser parser;
+ struct of_range range;
struct regmap *gpr;
u32 gprvals[4] = {
05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
@@ -106,17 +110,17 @@ static int imx_weim_gpr_setup(struct platform_device *pdev)
return 0;
}
- of_property_for_each_u32(np, "ranges", prop, p, val) {
- if (i % 4 == 0) {
- cs = val;
- } else if (i % 4 == 3 && val) {
- val = (val / SZ_32M) | 1;
- gprval |= val << cs * 3;
- }
+ if (of_range_parser_init(&parser, np))
+ goto err;
+
+ for_each_of_range(&parser, &range) {
+ cs = range.bus_addr >> 32;
+ val = (range.size / SZ_32M) | 1;
+ gprval |= val << cs * 3;
i++;
}
- if (i == 0 || i % 4)
+ if (i == 0)
goto err;
for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
@@ -201,11 +205,9 @@ static int weim_timing_setup(struct device *dev, struct device_node *np,
static int weim_parse_dt(struct platform_device *pdev)
{
- const struct of_device_id *of_id = of_match_device(weim_id_table,
- &pdev->dev);
- const struct imx_weim_devtype *devtype = of_id->data;
+ const struct imx_weim_devtype *devtype = device_get_match_data(&pdev->dev);
+ int ret = 0, have_child = 0;
struct device_node *child;
- int ret, have_child = 0;
struct weim_priv *priv;
void __iomem *base;
u32 reg;
@@ -263,7 +265,6 @@ static int weim_parse_dt(struct platform_device *pdev)
static int weim_probe(struct platform_device *pdev)
{
struct weim_priv *priv;
- struct resource *res;
struct clk *clk;
void __iomem *base;
int ret;
@@ -273,8 +274,7 @@ static int weim_probe(struct platform_device *pdev)
return -ENOMEM;
/* get the resource */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -282,22 +282,18 @@ static int weim_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, priv);
/* get the clock */
- clk = devm_clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
/* parse the device node */
ret = weim_parse_dt(pdev);
if (ret)
- clk_disable_unprepare(clk);
- else
- dev_info(&pdev->dev, "Driver registered.\n");
+ return ret;
- return ret;
+ dev_info(&pdev->dev, "Driver registered.\n");
+
+ return 0;
}
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
@@ -331,6 +327,12 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action,
"Failed to setup timing for '%pOF'\n", rd->dn);
if (!of_node_check_flag(rd->dn, OF_POPULATED)) {
+ /*
+ * Clear the flag before adding the device so that
+ * fw_devlink doesn't skip adding consumers to this
+ * device.
+ */
+ rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE;
if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) {
dev_err(&pdev->dev,
"Failed to create child device '%pOF'\n",
diff --git a/drivers/bus/intel-ixp4xx-eb.c b/drivers/bus/intel-ixp4xx-eb.c
index a4388440aca7..320cf307db05 100644
--- a/drivers/bus/intel-ixp4xx-eb.c
+++ b/drivers/bus/intel-ixp4xx-eb.c
@@ -33,7 +33,7 @@
#define IXP4XX_EXP_TIMING_STRIDE 0x04
#define IXP4XX_EXP_CS_EN BIT(31)
#define IXP456_EXP_PAR_EN BIT(30) /* Only on IXP45x and IXP46x */
-#define IXP4XX_EXP_T1_MASK GENMASK(28, 27)
+#define IXP4XX_EXP_T1_MASK GENMASK(29, 28)
#define IXP4XX_EXP_T1_SHIFT 28
#define IXP4XX_EXP_T2_MASK GENMASK(27, 26)
#define IXP4XX_EXP_T2_SHIFT 26
@@ -49,7 +49,7 @@
#define IXP4XX_EXP_SIZE_SHIFT 10
#define IXP4XX_EXP_CNFG_0 BIT(9) /* Always zero */
#define IXP43X_EXP_SYNC_INTEL BIT(8) /* Only on IXP43x */
-#define IXP43X_EXP_EXP_CHIP BIT(7) /* Only on IXP43x */
+#define IXP43X_EXP_EXP_CHIP BIT(7) /* Only on IXP43x, dangerous to touch on IXP42x */
#define IXP4XX_EXP_BYTE_RD16 BIT(6)
#define IXP4XX_EXP_HRDY_POL BIT(5) /* Only on IXP42x */
#define IXP4XX_EXP_MUX_EN BIT(4)
@@ -57,8 +57,6 @@
#define IXP4XX_EXP_WORD BIT(2) /* Always zero */
#define IXP4XX_EXP_WR_EN BIT(1)
#define IXP4XX_EXP_BYTE_EN BIT(0)
-#define IXP42X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(8)|BIT(7)|IXP4XX_EXP_WORD)
-#define IXP43X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(5)|IXP4XX_EXP_WORD)
#define IXP4XX_EXP_CNFG0 0x20
#define IXP4XX_EXP_CNFG0_MEM_MAP BIT(31)
@@ -252,10 +250,9 @@ static void ixp4xx_exp_setup_chipselect(struct ixp4xx_eb *eb,
cs_cfg |= val << IXP4XX_EXP_CYC_TYPE_SHIFT;
}
- if (eb->is_42x)
- cs_cfg &= ~IXP42X_RESERVED;
if (eb->is_43x) {
- cs_cfg &= ~IXP43X_RESERVED;
+ /* Should always be zero */
+ cs_cfg &= ~IXP4XX_EXP_WORD;
/*
* This bit for Intel strata flash is currently unused, but let's
* report it if we find one.
@@ -426,4 +423,3 @@ static struct platform_driver ixp4xx_exp_driver = {
module_platform_driver(ixp4xx_exp_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("Intel IXP4xx external bus driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
index 46981331b38f..354204b0ef3a 100644
--- a/drivers/bus/mhi/Makefile
+++ b/drivers/bus/mhi/Makefile
@@ -1,5 +1,5 @@
# Host MHI stack
-obj-y += host/
+obj-$(CONFIG_MHI_BUS) += host/
# Endpoint MHI stack
-obj-y += ep/
+obj-$(CONFIG_MHI_BUS_EP) += ep/
diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
index f794b9c8049e..dda340aaed95 100644
--- a/drivers/bus/mhi/common.h
+++ b/drivers/bus/mhi/common.h
@@ -297,30 +297,30 @@ struct mhi_ring_element {
__le32 dword[2];
};
+#define MHI_STATE_LIST \
+ mhi_state(RESET, "RESET") \
+ mhi_state(READY, "READY") \
+ mhi_state(M0, "M0") \
+ mhi_state(M1, "M1") \
+ mhi_state(M2, "M2") \
+ mhi_state(M3, "M3") \
+ mhi_state(M3_FAST, "M3_FAST") \
+ mhi_state(BHI, "BHI") \
+ mhi_state_end(SYS_ERR, "SYS ERROR")
+
+#undef mhi_state
+#undef mhi_state_end
+
+#define mhi_state(a, b) case MHI_STATE_##a: return b;
+#define mhi_state_end(a, b) case MHI_STATE_##a: return b;
+
static inline const char *mhi_state_str(enum mhi_state state)
{
switch (state) {
- case MHI_STATE_RESET:
- return "RESET";
- case MHI_STATE_READY:
- return "READY";
- case MHI_STATE_M0:
- return "M0";
- case MHI_STATE_M1:
- return "M1";
- case MHI_STATE_M2:
- return "M2";
- case MHI_STATE_M3:
- return "M3";
- case MHI_STATE_M3_FAST:
- return "M3 FAST";
- case MHI_STATE_BHI:
- return "BHI";
- case MHI_STATE_SYS_ERR:
- return "SYS ERROR";
+ MHI_STATE_LIST
default:
return "Unknown state";
}
-};
+}
#endif /* _MHI_COMMON_H */
diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
index a2125fa5fe2f..512da7482acc 100644
--- a/drivers/bus/mhi/ep/internal.h
+++ b/drivers/bus/mhi/ep/internal.h
@@ -11,7 +11,7 @@
#include "../common.h"
-extern struct bus_type mhi_ep_bus_type;
+extern const struct bus_type mhi_ep_bus_type;
#define MHI_REG_OFFSET 0x100
#define BHI_REG_OFFSET 0x200
@@ -126,6 +126,7 @@ struct mhi_ep_ring {
union mhi_ep_ring_ctx *ring_ctx;
struct mhi_ring_element *ring_cache;
enum mhi_ep_ring_type type;
+ struct delayed_work intmodt_work;
u64 rbase;
size_t rd_offset;
size_t wr_offset;
@@ -135,7 +136,9 @@ struct mhi_ep_ring {
u32 ch_id;
u32 er_index;
u32 irq_vector;
+ u32 intmodt;
bool started;
+ bool irq_pending;
};
struct mhi_ep_cmd {
@@ -159,6 +162,7 @@ struct mhi_ep_chan {
void (*xfer_cb)(struct mhi_ep_device *mhi_dev, struct mhi_result *result);
enum mhi_ch_state state;
enum dma_data_direction dir;
+ size_t rd_offset;
u64 tre_loc;
u32 tre_size;
u32 tre_bytes_left;
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 1dc8a3557a46..3c208b5c8446 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -54,11 +54,27 @@ static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
mutex_unlock(&mhi_cntrl->event_lock);
/*
- * Raise IRQ to host only if the BEI flag is not set in TRE. Host might
- * set this flag for interrupt moderation as per MHI protocol.
+ * As per the MHI specification, section 4.3, Interrupt moderation:
+ *
+ * 1. If BEI flag is not set, cancel any pending intmodt work if started
+ * for the event ring and raise IRQ immediately.
+ *
+ * 2. If both BEI and intmodt are set, and if no IRQ is pending for the
+ * same event ring, start the IRQ delayed work as per the value of
+ * intmodt. If previous IRQ is pending, then do nothing as the pending
+ * IRQ is enough for the host to process the current event ring element.
+ *
+ * 3. If BEI is set and intmodt is not set, no need to raise IRQ.
*/
- if (!bei)
+ if (!bei) {
+ if (READ_ONCE(ring->irq_pending))
+ cancel_delayed_work(&ring->intmodt_work);
+
mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector);
+ } else if (ring->intmodt && !READ_ONCE(ring->irq_pending)) {
+ WRITE_ONCE(ring->irq_pending, true);
+ schedule_delayed_work(&ring->intmodt_work, msecs_to_jiffies(ring->intmodt));
+ }
return 0;
@@ -71,45 +87,77 @@ err_unlock:
static int mhi_ep_send_completion_event(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
struct mhi_ring_element *tre, u32 len, enum mhi_ev_ccs code)
{
- struct mhi_ring_element event = {};
+ struct mhi_ring_element *event;
+ int ret;
+
+ event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre));
+ event->dword[0] = MHI_TRE_EV_DWORD0(code, len);
+ event->dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT);
- event.ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre));
- event.dword[0] = MHI_TRE_EV_DWORD0(code, len);
- event.dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT);
+ ret = mhi_ep_send_event(mhi_cntrl, ring->er_index, event, MHI_TRE_DATA_GET_BEI(tre));
+ kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
- return mhi_ep_send_event(mhi_cntrl, ring->er_index, &event, MHI_TRE_DATA_GET_BEI(tre));
+ return ret;
}
int mhi_ep_send_state_change_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state state)
{
- struct mhi_ring_element event = {};
+ struct mhi_ring_element *event;
+ int ret;
+
+ event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->dword[0] = MHI_SC_EV_DWORD0(state);
+ event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT);
- event.dword[0] = MHI_SC_EV_DWORD0(state);
- event.dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT);
+ ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
+ kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
- return mhi_ep_send_event(mhi_cntrl, 0, &event, 0);
+ return ret;
}
int mhi_ep_send_ee_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ee_type exec_env)
{
- struct mhi_ring_element event = {};
+ struct mhi_ring_element *event;
+ int ret;
+
+ event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
- event.dword[0] = MHI_EE_EV_DWORD0(exec_env);
- event.dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT);
+ event->dword[0] = MHI_EE_EV_DWORD0(exec_env);
+ event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT);
- return mhi_ep_send_event(mhi_cntrl, 0, &event, 0);
+ ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
+ kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
+
+ return ret;
}
static int mhi_ep_send_cmd_comp_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ev_ccs code)
{
struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring;
- struct mhi_ring_element event = {};
+ struct mhi_ring_element *event;
+ int ret;
- event.ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element));
- event.dword[0] = MHI_CC_EV_DWORD0(code);
- event.dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT);
+ event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element));
+ event->dword[0] = MHI_CC_EV_DWORD0(code);
+ event->dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT);
+
+ ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0);
+ kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event);
- return mhi_ep_send_event(mhi_cntrl, 0, &event, 0);
+ return ret;
}
static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
@@ -123,6 +171,13 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
int ret;
ch_id = MHI_TRE_GET_CMD_CHID(el);
+
+ /* Check if the channel is supported by the controller */
+ if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) {
+ dev_dbg(dev, "Channel (%u) not supported!\n", ch_id);
+ return -ENODEV;
+ }
+
mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
@@ -144,6 +199,8 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
goto err_unlock;
}
+
+ mhi_chan->rd_offset = ch_ring->rd_offset;
}
/* Set channel state to RUNNING */
@@ -196,9 +253,11 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
mhi_ep_mmio_disable_chdb(mhi_cntrl, ch_id);
/* Send channel disconnect status to client drivers */
- result.transaction_status = -ENOTCONN;
- result.bytes_xferd = 0;
- mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+ if (mhi_chan->xfer_cb) {
+ result.transaction_status = -ENOTCONN;
+ result.bytes_xferd = 0;
+ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+ }
/* Set channel state to STOP */
mhi_chan->state = MHI_CH_STATE_STOP;
@@ -217,7 +276,7 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
mutex_unlock(&mhi_chan->lock);
break;
case MHI_PKT_TYPE_RESET_CHAN_CMD:
- dev_dbg(dev, "Received STOP command for channel (%u)\n", ch_id);
+ dev_dbg(dev, "Received RESET command for channel (%u)\n", ch_id);
if (!ch_ring->started) {
dev_err(dev, "Channel (%u) not opened\n", ch_id);
return -ENODEV;
@@ -228,9 +287,11 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
mhi_ep_ring_reset(mhi_cntrl, ch_ring);
/* Send channel disconnect status to client driver */
- result.transaction_status = -ENOTCONN;
- result.bytes_xferd = 0;
- mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+ if (mhi_chan->xfer_cb) {
+ result.transaction_status = -ENOTCONN;
+ result.bytes_xferd = 0;
+ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+ }
/* Set channel state to DISABLED */
mhi_chan->state = MHI_CH_STATE_DISABLED;
@@ -269,27 +330,86 @@ bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_directio
struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
- return !!(ring->rd_offset == ring->wr_offset);
+ return !!(mhi_chan->rd_offset == ring->wr_offset);
}
EXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty);
+static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info)
+{
+ struct mhi_ep_device *mhi_dev = buf_info->mhi_dev;
+ struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
+ struct mhi_ep_chan *mhi_chan = mhi_dev->ul_chan;
+ struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
+ struct mhi_ring_element *el = &ring->ring_cache[ring->rd_offset];
+ struct mhi_result result = {};
+ int ret;
+
+ if (mhi_chan->xfer_cb) {
+ result.buf_addr = buf_info->cb_buf;
+ result.dir = mhi_chan->dir;
+ result.bytes_xferd = buf_info->size;
+
+ mhi_chan->xfer_cb(mhi_dev, &result);
+ }
+
+ /*
+ * The host will split the data packet into multiple TREs if it can't fit
+ * the packet in a single TRE. In that case, CHAIN flag will be set by the
+ * host for all TREs except the last one.
+ */
+ if (buf_info->code != MHI_EV_CC_OVERFLOW) {
+ if (MHI_TRE_DATA_GET_CHAIN(el)) {
+ /*
+ * IEOB (Interrupt on End of Block) flag will be set by the host if
+ * it expects the completion event for all TREs of a TD.
+ */
+ if (MHI_TRE_DATA_GET_IEOB(el)) {
+ ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
+ MHI_TRE_DATA_GET_LEN(el),
+ MHI_EV_CC_EOB);
+ if (ret < 0) {
+ dev_err(&mhi_chan->mhi_dev->dev,
+ "Error sending transfer compl. event\n");
+ goto err_free_tre_buf;
+ }
+ }
+ } else {
+ /*
+ * IEOT (Interrupt on End of Transfer) flag will be set by the host
+ * for the last TRE of the TD and expects the completion event for
+ * the same.
+ */
+ if (MHI_TRE_DATA_GET_IEOT(el)) {
+ ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
+ MHI_TRE_DATA_GET_LEN(el),
+ MHI_EV_CC_EOT);
+ if (ret < 0) {
+ dev_err(&mhi_chan->mhi_dev->dev,
+ "Error sending transfer compl. event\n");
+ goto err_free_tre_buf;
+ }
+ }
+ }
+ }
+
+ mhi_ep_ring_inc_index(ring);
+
+err_free_tre_buf:
+ kmem_cache_free(mhi_cntrl->tre_buf_cache, buf_info->cb_buf);
+}
+
static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
- struct mhi_ep_ring *ring,
- struct mhi_result *result,
- u32 len)
+ struct mhi_ep_ring *ring)
{
struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
struct device *dev = &mhi_cntrl->mhi_dev->dev;
- size_t tr_len, read_offset, write_offset;
+ size_t tr_len, read_offset;
+ struct mhi_ep_buf_info buf_info = {};
+ u32 len = MHI_EP_DEFAULT_MTU;
struct mhi_ring_element *el;
- bool tr_done = false;
- void *write_addr;
- u64 read_addr;
- u32 buf_left;
+ void *buf_addr;
int ret;
- buf_left = len;
-
do {
/* Don't process the transfer ring if the channel is not in RUNNING state */
if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
@@ -297,97 +417,62 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
return -ENODEV;
}
- el = &ring->ring_cache[ring->rd_offset];
+ el = &ring->ring_cache[mhi_chan->rd_offset];
/* Check if there is data pending to be read from previous read operation */
if (mhi_chan->tre_bytes_left) {
dev_dbg(dev, "TRE bytes remaining: %u\n", mhi_chan->tre_bytes_left);
- tr_len = min(buf_left, mhi_chan->tre_bytes_left);
+ tr_len = min(len, mhi_chan->tre_bytes_left);
} else {
mhi_chan->tre_loc = MHI_TRE_DATA_GET_PTR(el);
mhi_chan->tre_size = MHI_TRE_DATA_GET_LEN(el);
mhi_chan->tre_bytes_left = mhi_chan->tre_size;
- tr_len = min(buf_left, mhi_chan->tre_size);
+ tr_len = min(len, mhi_chan->tre_size);
}
read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left;
- write_offset = len - buf_left;
- read_addr = mhi_chan->tre_loc + read_offset;
- write_addr = result->buf_addr + write_offset;
+
+ buf_addr = kmem_cache_zalloc(mhi_cntrl->tre_buf_cache, GFP_KERNEL);
+ if (!buf_addr)
+ return -ENOMEM;
+
+ buf_info.host_addr = mhi_chan->tre_loc + read_offset;
+ buf_info.dev_addr = buf_addr;
+ buf_info.size = tr_len;
+ buf_info.cb = mhi_ep_read_completion;
+ buf_info.cb_buf = buf_addr;
+ buf_info.mhi_dev = mhi_chan->mhi_dev;
+
+ if (mhi_chan->tre_bytes_left - tr_len)
+ buf_info.code = MHI_EV_CC_OVERFLOW;
dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id);
- ret = mhi_cntrl->read_from_host(mhi_cntrl, read_addr, write_addr, tr_len);
+ ret = mhi_cntrl->read_async(mhi_cntrl, &buf_info);
if (ret < 0) {
dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n");
- return ret;
+ goto err_free_buf_addr;
}
- buf_left -= tr_len;
mhi_chan->tre_bytes_left -= tr_len;
- /*
- * Once the TRE (Transfer Ring Element) of a TD (Transfer Descriptor) has been
- * read completely:
- *
- * 1. Send completion event to the host based on the flags set in TRE.
- * 2. Increment the local read offset of the transfer ring.
- */
- if (!mhi_chan->tre_bytes_left) {
- /*
- * The host will split the data packet into multiple TREs if it can't fit
- * the packet in a single TRE. In that case, CHAIN flag will be set by the
- * host for all TREs except the last one.
- */
- if (MHI_TRE_DATA_GET_CHAIN(el)) {
- /*
- * IEOB (Interrupt on End of Block) flag will be set by the host if
- * it expects the completion event for all TREs of a TD.
- */
- if (MHI_TRE_DATA_GET_IEOB(el)) {
- ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
- MHI_TRE_DATA_GET_LEN(el),
- MHI_EV_CC_EOB);
- if (ret < 0) {
- dev_err(&mhi_chan->mhi_dev->dev,
- "Error sending transfer compl. event\n");
- return ret;
- }
- }
- } else {
- /*
- * IEOT (Interrupt on End of Transfer) flag will be set by the host
- * for the last TRE of the TD and expects the completion event for
- * the same.
- */
- if (MHI_TRE_DATA_GET_IEOT(el)) {
- ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el,
- MHI_TRE_DATA_GET_LEN(el),
- MHI_EV_CC_EOT);
- if (ret < 0) {
- dev_err(&mhi_chan->mhi_dev->dev,
- "Error sending transfer compl. event\n");
- return ret;
- }
- }
-
- tr_done = true;
- }
+ if (!mhi_chan->tre_bytes_left)
+ mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size;
+ /* Read until the some buffer is left or the ring becomes not empty */
+ } while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));
- mhi_ep_ring_inc_index(ring);
- }
+ return 0;
- result->bytes_xferd += tr_len;
- } while (buf_left && !tr_done);
+err_free_buf_addr:
+ kmem_cache_free(mhi_cntrl->tre_buf_cache, buf_addr);
- return 0;
+ return ret;
}
-static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
+static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring)
{
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
struct mhi_result result = {};
- u32 len = MHI_EP_DEFAULT_MTU;
struct mhi_ep_chan *mhi_chan;
int ret;
@@ -408,30 +493,43 @@ static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_elem
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
} else {
/* UL channel */
- result.buf_addr = kzalloc(len, GFP_KERNEL);
- if (!result.buf_addr)
- return -ENOMEM;
+ ret = mhi_ep_read_channel(mhi_cntrl, ring);
+ if (ret < 0) {
+ dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n");
+ return ret;
+ }
+ }
- do {
- ret = mhi_ep_read_channel(mhi_cntrl, ring, &result, len);
- if (ret < 0) {
- dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n");
- kfree(result.buf_addr);
- return ret;
- }
+ return 0;
+}
- result.dir = mhi_chan->dir;
- mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
- result.bytes_xferd = 0;
- memset(result.buf_addr, 0, len);
+static void mhi_ep_skb_completion(struct mhi_ep_buf_info *buf_info)
+{
+ struct mhi_ep_device *mhi_dev = buf_info->mhi_dev;
+ struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
+ struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan;
+ struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
+ struct mhi_ring_element *el = &ring->ring_cache[ring->rd_offset];
+ struct device *dev = &mhi_dev->dev;
+ struct mhi_result result = {};
+ int ret;
- /* Read until the ring becomes empty */
- } while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));
+ if (mhi_chan->xfer_cb) {
+ result.buf_addr = buf_info->cb_buf;
+ result.dir = mhi_chan->dir;
+ result.bytes_xferd = buf_info->size;
- kfree(result.buf_addr);
+ mhi_chan->xfer_cb(mhi_dev, &result);
}
- return 0;
+ ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, buf_info->size,
+ buf_info->code);
+ if (ret) {
+ dev_err(dev, "Error sending transfer completion event\n");
+ return;
+ }
+
+ mhi_ep_ring_inc_index(ring);
}
/* TODO: Handle partially formed TDs */
@@ -440,12 +538,10 @@ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb)
struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan;
struct device *dev = &mhi_chan->mhi_dev->dev;
+ struct mhi_ep_buf_info buf_info = {};
struct mhi_ring_element *el;
u32 buf_left, read_offset;
struct mhi_ep_ring *ring;
- enum mhi_ev_ccs code;
- void *read_addr;
- u64 write_addr;
size_t tr_len;
u32 tre_len;
int ret;
@@ -469,40 +565,44 @@ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb)
goto err_exit;
}
- el = &ring->ring_cache[ring->rd_offset];
+ el = &ring->ring_cache[mhi_chan->rd_offset];
tre_len = MHI_TRE_DATA_GET_LEN(el);
tr_len = min(buf_left, tre_len);
read_offset = skb->len - buf_left;
- read_addr = skb->data + read_offset;
- write_addr = MHI_TRE_DATA_GET_PTR(el);
- dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id);
- ret = mhi_cntrl->write_to_host(mhi_cntrl, read_addr, write_addr, tr_len);
- if (ret < 0) {
- dev_err(dev, "Error writing to the channel\n");
- goto err_exit;
- }
+ buf_info.dev_addr = skb->data + read_offset;
+ buf_info.host_addr = MHI_TRE_DATA_GET_PTR(el);
+ buf_info.size = tr_len;
+ buf_info.cb = mhi_ep_skb_completion;
+ buf_info.cb_buf = skb;
+ buf_info.mhi_dev = mhi_dev;
- buf_left -= tr_len;
/*
* For all TREs queued by the host for DL channel, only the EOT flag will be set.
* If the packet doesn't fit into a single TRE, send the OVERFLOW event to
* the host so that the host can adjust the packet boundary to next TREs. Else send
* the EOT event to the host indicating the packet boundary.
*/
- if (buf_left)
- code = MHI_EV_CC_OVERFLOW;
+ if (buf_left - tr_len)
+ buf_info.code = MHI_EV_CC_OVERFLOW;
else
- code = MHI_EV_CC_EOT;
+ buf_info.code = MHI_EV_CC_EOT;
- ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, tr_len, code);
- if (ret) {
- dev_err(dev, "Error sending transfer completion event\n");
+ dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id);
+ ret = mhi_cntrl->write_async(mhi_cntrl, &buf_info);
+ if (ret < 0) {
+ dev_err(dev, "Error writing to the channel\n");
goto err_exit;
}
- mhi_ep_ring_inc_index(ring);
+ buf_left -= tr_len;
+
+ /*
+ * Update the read offset cached in mhi_chan. Actual read offset
+ * will be updated by the completion handler.
+ */
+ mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size;
} while (buf_left);
mutex_unlock(&mhi_chan->lock);
@@ -691,7 +791,7 @@ static void mhi_ep_cmd_ring_worker(struct work_struct *work)
el = &ring->ring_cache[ring->rd_offset];
ret = mhi_ep_process_cmd_ring(ring, el);
- if (ret)
+ if (ret && ret != -ENODEV)
dev_err(dev, "Error processing cmd ring element: %zu\n", ring->rd_offset);
mhi_ep_ring_inc_index(ring);
@@ -703,7 +803,6 @@ static void mhi_ep_ch_ring_worker(struct work_struct *work)
struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, ch_ring_work);
struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct mhi_ep_ring_item *itr, *tmp;
- struct mhi_ring_element *el;
struct mhi_ep_ring *ring;
struct mhi_ep_chan *chan;
unsigned long flags;
@@ -719,36 +818,47 @@ static void mhi_ep_ch_ring_worker(struct work_struct *work)
list_del(&itr->node);
ring = itr->ring;
+ chan = &mhi_cntrl->mhi_chan[ring->ch_id];
+ mutex_lock(&chan->lock);
+
+ /*
+ * The ring could've stopped while we waited to grab the (chan->lock), so do
+ * a sanity check before going further.
+ */
+ if (!ring->started) {
+ mutex_unlock(&chan->lock);
+ kfree(itr);
+ continue;
+ }
+
/* Update the write offset for the ring */
ret = mhi_ep_update_wr_offset(ring);
if (ret) {
dev_err(dev, "Error updating write offset for ring\n");
- kfree(itr);
+ mutex_unlock(&chan->lock);
+ kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
continue;
}
/* Sanity check to make sure there are elements in the ring */
- if (ring->rd_offset == ring->wr_offset) {
- kfree(itr);
+ if (chan->rd_offset == ring->wr_offset) {
+ mutex_unlock(&chan->lock);
+ kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
continue;
}
- el = &ring->ring_cache[ring->rd_offset];
- chan = &mhi_cntrl->mhi_chan[ring->ch_id];
-
- mutex_lock(&chan->lock);
dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id);
- ret = mhi_ep_process_ch_ring(ring, el);
+ ret = mhi_ep_process_ch_ring(ring);
if (ret) {
dev_err(dev, "Error processing ring for channel (%u): %d\n",
ring->ch_id, ret);
mutex_unlock(&chan->lock);
- kfree(itr);
+ kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
continue;
}
mutex_unlock(&chan->lock);
- kfree(itr);
+ kmem_cache_free(mhi_cntrl->ring_item_cache, itr);
}
}
@@ -804,7 +914,7 @@ static void mhi_ep_queue_channel_db(struct mhi_ep_cntrl *mhi_cntrl, unsigned lon
u32 ch_id = ch_idx + i;
ring = &mhi_cntrl->mhi_chan[ch_id].ring;
- item = kzalloc(sizeof(*item), GFP_ATOMIC);
+ item = kmem_cache_zalloc(mhi_cntrl->ring_item_cache, GFP_ATOMIC);
if (!item)
return;
@@ -973,44 +1083,25 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
static void mhi_ep_reset_worker(struct work_struct *work)
{
struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, reset_work);
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_state cur_state;
- int ret;
- mhi_ep_abort_transfer(mhi_cntrl);
+ mhi_ep_power_down(mhi_cntrl);
+
+ mutex_lock(&mhi_cntrl->state_lock);
- spin_lock_bh(&mhi_cntrl->state_lock);
/* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */
mhi_ep_mmio_reset(mhi_cntrl);
cur_state = mhi_cntrl->mhi_state;
- spin_unlock_bh(&mhi_cntrl->state_lock);
/*
* Only proceed further if the reset is due to SYS_ERR. The host will
* issue reset during shutdown also and we don't need to do re-init in
* that case.
*/
- if (cur_state == MHI_STATE_SYS_ERR) {
- mhi_ep_mmio_init(mhi_cntrl);
+ if (cur_state == MHI_STATE_SYS_ERR)
+ mhi_ep_power_up(mhi_cntrl);
- /* Set AMSS EE before signaling ready state */
- mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS);
-
- /* All set, notify the host that we are ready */
- ret = mhi_ep_set_ready_state(mhi_cntrl);
- if (ret)
- return;
-
- dev_dbg(dev, "READY state notification sent to the host\n");
-
- ret = mhi_ep_enable(mhi_cntrl);
- if (ret) {
- dev_err(dev, "Failed to enable MHI endpoint: %d\n", ret);
- return;
- }
-
- enable_irq(mhi_cntrl->irq);
- }
+ mutex_unlock(&mhi_cntrl->state_lock);
}
/*
@@ -1045,8 +1136,9 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl)
mhi_ep_mmio_mask_interrupts(mhi_cntrl);
mhi_ep_mmio_init(mhi_cntrl);
- mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)),
- GFP_KERNEL);
+ mhi_cntrl->mhi_event = kcalloc(mhi_cntrl->event_rings,
+ sizeof(*mhi_cntrl->mhi_event),
+ GFP_KERNEL);
if (!mhi_cntrl->mhi_event)
return -ENOMEM;
@@ -1089,11 +1181,11 @@ EXPORT_SYMBOL_GPL(mhi_ep_power_up);
void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
{
- if (mhi_cntrl->enabled)
+ if (mhi_cntrl->enabled) {
mhi_ep_abort_transfer(mhi_cntrl);
-
- kfree(mhi_cntrl->mhi_event);
- disable_irq(mhi_cntrl->irq);
+ kfree(mhi_cntrl->mhi_event);
+ disable_irq(mhi_cntrl->irq);
+ }
}
EXPORT_SYMBOL_GPL(mhi_ep_power_down);
@@ -1119,6 +1211,7 @@ void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl)
dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n");
/* Set channel state to SUSPENDED */
+ mhi_chan->state = MHI_CH_STATE_SUSPENDED;
tmp &= ~CHAN_CTX_CHSTATE_MASK;
tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED);
mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
@@ -1148,6 +1241,7 @@ void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl)
dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n");
/* Set channel state to RUNNING */
+ mhi_chan->state = MHI_CH_STATE_RUNNING;
tmp &= ~CHAN_CTX_CHSTATE_MASK;
tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
@@ -1358,6 +1452,10 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->mmio || !mhi_cntrl->irq)
return -EINVAL;
+ if (!mhi_cntrl->read_sync || !mhi_cntrl->write_sync ||
+ !mhi_cntrl->read_async || !mhi_cntrl->write_async)
+ return -EINVAL;
+
ret = mhi_ep_chan_init(mhi_cntrl, config);
if (ret)
return ret;
@@ -1368,21 +1466,44 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
goto err_free_ch;
}
+ mhi_cntrl->ev_ring_el_cache = kmem_cache_create("mhi_ep_event_ring_el",
+ sizeof(struct mhi_ring_element), 0,
+ 0, NULL);
+ if (!mhi_cntrl->ev_ring_el_cache) {
+ ret = -ENOMEM;
+ goto err_free_cmd;
+ }
+
+ mhi_cntrl->tre_buf_cache = kmem_cache_create("mhi_ep_tre_buf", MHI_EP_DEFAULT_MTU, 0,
+ 0, NULL);
+ if (!mhi_cntrl->tre_buf_cache) {
+ ret = -ENOMEM;
+ goto err_destroy_ev_ring_el_cache;
+ }
+
+ mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item",
+ sizeof(struct mhi_ep_ring_item), 0,
+ 0, NULL);
+ if (!mhi_cntrl->ring_item_cache) {
+ ret = -ENOMEM;
+ goto err_destroy_tre_buf_cache;
+ }
+
INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);
INIT_WORK(&mhi_cntrl->reset_work, mhi_ep_reset_worker);
INIT_WORK(&mhi_cntrl->cmd_ring_work, mhi_ep_cmd_ring_worker);
INIT_WORK(&mhi_cntrl->ch_ring_work, mhi_ep_ch_ring_worker);
- mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", 0, 0);
+ mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", WQ_PERCPU, 0);
if (!mhi_cntrl->wq) {
ret = -ENOMEM;
- goto err_free_cmd;
+ goto err_destroy_ring_item_cache;
}
INIT_LIST_HEAD(&mhi_cntrl->st_transition_list);
INIT_LIST_HEAD(&mhi_cntrl->ch_db_list);
- spin_lock_init(&mhi_cntrl->state_lock);
spin_lock_init(&mhi_cntrl->list_lock);
+ mutex_init(&mhi_cntrl->state_lock);
mutex_init(&mhi_cntrl->event_lock);
/* Set MHI version and AMSS EE before enumeration */
@@ -1435,6 +1556,12 @@ err_ida_free:
ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
err_destroy_wq:
destroy_workqueue(mhi_cntrl->wq);
+err_destroy_ring_item_cache:
+ kmem_cache_destroy(mhi_cntrl->ring_item_cache);
+err_destroy_ev_ring_el_cache:
+ kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache);
+err_destroy_tre_buf_cache:
+ kmem_cache_destroy(mhi_cntrl->tre_buf_cache);
err_free_cmd:
kfree(mhi_cntrl->mhi_cmd);
err_free_ch:
@@ -1456,6 +1583,9 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
free_irq(mhi_cntrl->irq, mhi_cntrl);
+ kmem_cache_destroy(mhi_cntrl->tre_buf_cache);
+ kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache);
+ kmem_cache_destroy(mhi_cntrl->ring_item_cache);
kfree(mhi_cntrl->mhi_cmd);
kfree(mhi_cntrl->mhi_chan);
@@ -1543,18 +1673,18 @@ void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv)
}
EXPORT_SYMBOL_GPL(mhi_ep_driver_unregister);
-static int mhi_ep_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int mhi_ep_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+ const struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
return add_uevent_var(env, "MODALIAS=" MHI_EP_DEVICE_MODALIAS_FMT,
mhi_dev->name);
}
-static int mhi_ep_match(struct device *dev, struct device_driver *drv)
+static int mhi_ep_match(struct device *dev, const struct device_driver *drv)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
- struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv);
+ const struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv);
const struct mhi_device_id *id;
/*
@@ -1573,7 +1703,7 @@ static int mhi_ep_match(struct device *dev, struct device_driver *drv)
return 0;
};
-struct bus_type mhi_ep_bus_type = {
+const struct bus_type mhi_ep_bus_type = {
.name = "mhi_ep",
.dev_name = "mhi_ep",
.match = mhi_ep_match,
diff --git a/drivers/bus/mhi/ep/ring.c b/drivers/bus/mhi/ep/ring.c
index 115518ec76a4..26357ee68dee 100644
--- a/drivers/bus/mhi/ep/ring.c
+++ b/drivers/bus/mhi/ep/ring.c
@@ -30,7 +30,8 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end)
{
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
- size_t start, copy_size;
+ struct mhi_ep_buf_info buf_info = {};
+ size_t start;
int ret;
/* Don't proceed in the case of event ring. This happens during mhi_ep_ring_start(). */
@@ -43,30 +44,34 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end)
start = ring->wr_offset;
if (start < end) {
- copy_size = (end - start) * sizeof(struct mhi_ring_element);
- ret = mhi_cntrl->read_from_host(mhi_cntrl, ring->rbase +
- (start * sizeof(struct mhi_ring_element)),
- &ring->ring_cache[start], copy_size);
+ buf_info.size = (end - start) * sizeof(struct mhi_ring_element);
+ buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element));
+ buf_info.dev_addr = &ring->ring_cache[start];
+
+ ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
if (ret < 0)
return ret;
} else {
- copy_size = (ring->ring_size - start) * sizeof(struct mhi_ring_element);
- ret = mhi_cntrl->read_from_host(mhi_cntrl, ring->rbase +
- (start * sizeof(struct mhi_ring_element)),
- &ring->ring_cache[start], copy_size);
+ buf_info.size = (ring->ring_size - start) * sizeof(struct mhi_ring_element);
+ buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element));
+ buf_info.dev_addr = &ring->ring_cache[start];
+
+ ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
if (ret < 0)
return ret;
if (end) {
- ret = mhi_cntrl->read_from_host(mhi_cntrl, ring->rbase,
- &ring->ring_cache[0],
- end * sizeof(struct mhi_ring_element));
+ buf_info.host_addr = ring->rbase;
+ buf_info.dev_addr = &ring->ring_cache[0];
+ buf_info.size = end * sizeof(struct mhi_ring_element);
+
+ ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
if (ret < 0)
return ret;
}
}
- dev_dbg(dev, "Cached ring: start %zu end %zu size %zu\n", start, end, copy_size);
+ dev_dbg(dev, "Cached ring: start %zu end %zu size %zu\n", start, end, buf_info.size);
return 0;
}
@@ -102,6 +107,7 @@ int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *e
{
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ struct mhi_ep_buf_info buf_info = {};
size_t old_offset = 0;
u32 num_free_elem;
__le64 rp;
@@ -125,20 +131,23 @@ int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *e
}
old_offset = ring->rd_offset;
- mhi_ep_ring_inc_index(ring);
dev_dbg(dev, "Adding an element to ring at offset (%zu)\n", ring->rd_offset);
+ buf_info.host_addr = ring->rbase + (old_offset * sizeof(*el));
+ buf_info.dev_addr = el;
+ buf_info.size = sizeof(*el);
+
+ ret = mhi_cntrl->write_sync(mhi_cntrl, &buf_info);
+ if (ret)
+ return ret;
+
+ mhi_ep_ring_inc_index(ring);
/* Update rp in ring context */
rp = cpu_to_le64(ring->rd_offset * sizeof(*el) + ring->rbase);
memcpy_toio((void __iomem *) &ring->ring_ctx->generic.rp, &rp, sizeof(u64));
- ret = mhi_cntrl->write_to_host(mhi_cntrl, el, ring->rbase + (old_offset * sizeof(*el)),
- sizeof(*el));
- if (ret < 0)
- return ret;
-
- return 0;
+ return ret;
}
void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32 id)
@@ -157,6 +166,15 @@ void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32
}
}
+static void mhi_ep_raise_irq(struct work_struct *work)
+{
+ struct mhi_ep_ring *ring = container_of(work, struct mhi_ep_ring, intmodt_work.work);
+ struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
+
+ mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector);
+ WRITE_ONCE(ring->irq_pending, false);
+}
+
int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
union mhi_ep_ring_ctx *ctx)
{
@@ -173,8 +191,13 @@ int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
if (ring->type == RING_TYPE_CH)
ring->er_index = le32_to_cpu(ring->ring_ctx->ch.erindex);
- if (ring->type == RING_TYPE_ER)
+ if (ring->type == RING_TYPE_ER) {
ring->irq_vector = le32_to_cpu(ring->ring_ctx->ev.msivec);
+ ring->intmodt = FIELD_GET(EV_CTX_INTMODT_MASK,
+ le32_to_cpu(ring->ring_ctx->ev.intmod));
+
+ INIT_DELAYED_WORK(&ring->intmodt_work, mhi_ep_raise_irq);
+ }
/* During ring init, both rp and wp are equal */
memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.rp, sizeof(u64));
@@ -201,6 +224,9 @@ int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
void mhi_ep_ring_reset(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring)
{
+ if (ring->type == RING_TYPE_ER)
+ cancel_delayed_work_sync(&ring->intmodt_work);
+
ring->started = false;
kfree(ring->ring_cache);
ring->ring_cache = NULL;
diff --git a/drivers/bus/mhi/ep/sm.c b/drivers/bus/mhi/ep/sm.c
index 3655c19e23c7..fd200b2ac0bb 100644
--- a/drivers/bus/mhi/ep/sm.c
+++ b/drivers/bus/mhi/ep/sm.c
@@ -63,24 +63,23 @@ int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
int ret;
/* If MHI is in M3, resume suspended channels */
- spin_lock_bh(&mhi_cntrl->state_lock);
+ mutex_lock(&mhi_cntrl->state_lock);
+
old_state = mhi_cntrl->mhi_state;
if (old_state == MHI_STATE_M3)
mhi_ep_resume_channels(mhi_cntrl);
ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
- spin_unlock_bh(&mhi_cntrl->state_lock);
-
if (ret) {
mhi_ep_handle_syserr(mhi_cntrl);
- return ret;
+ goto err_unlock;
}
/* Signal host that the device moved to M0 */
ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0);
if (ret) {
dev_err(dev, "Failed sending M0 state change event\n");
- return ret;
+ goto err_unlock;
}
if (old_state == MHI_STATE_READY) {
@@ -88,11 +87,14 @@ int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS);
if (ret) {
dev_err(dev, "Failed sending AMSS EE event\n");
- return ret;
+ goto err_unlock;
}
}
- return 0;
+err_unlock:
+ mutex_unlock(&mhi_cntrl->state_lock);
+
+ return ret;
}
int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
@@ -100,13 +102,12 @@ int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
struct device *dev = &mhi_cntrl->mhi_dev->dev;
int ret;
- spin_lock_bh(&mhi_cntrl->state_lock);
- ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
- spin_unlock_bh(&mhi_cntrl->state_lock);
+ mutex_lock(&mhi_cntrl->state_lock);
+ ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
if (ret) {
mhi_ep_handle_syserr(mhi_cntrl);
- return ret;
+ goto err_unlock;
}
mhi_ep_suspend_channels(mhi_cntrl);
@@ -115,10 +116,13 @@ int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3);
if (ret) {
dev_err(dev, "Failed sending M3 state change event\n");
- return ret;
+ goto err_unlock;
}
- return 0;
+err_unlock:
+ mutex_unlock(&mhi_cntrl->state_lock);
+
+ return ret;
}
int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl)
@@ -127,22 +131,24 @@ int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl)
enum mhi_state mhi_state;
int ret, is_ready;
- spin_lock_bh(&mhi_cntrl->state_lock);
+ mutex_lock(&mhi_cntrl->state_lock);
+
/* Ensure that the MHISTATUS is set to RESET by host */
mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK);
is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK);
if (mhi_state != MHI_STATE_RESET || is_ready) {
dev_err(dev, "READY state transition failed. MHI host not in RESET state\n");
- spin_unlock_bh(&mhi_cntrl->state_lock);
- return -EIO;
+ ret = -EIO;
+ goto err_unlock;
}
ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY);
- spin_unlock_bh(&mhi_cntrl->state_lock);
-
if (ret)
mhi_ep_handle_syserr(mhi_cntrl);
+err_unlock:
+ mutex_unlock(&mhi_cntrl->state_lock);
+
return ret;
}
diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c
index 26d0eddb1477..205d83ac069f 100644
--- a/drivers/bus/mhi/host/boot.c
+++ b/drivers/bus/mhi/host/boot.c
@@ -31,8 +31,8 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
int ret;
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
- bhi_vec->dma_addr = mhi_buf->dma_addr;
- bhi_vec->size = mhi_buf->len;
+ bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
+ bhi_vec->size = cpu_to_le64(mhi_buf->len);
}
dev_dbg(dev, "BHIe programming for RDDM\n");
@@ -82,9 +82,9 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
* other cores to shutdown while we're collecting RDDM buffer. After
* returning from this function, we expect the device to reset.
*
- * Normaly, we read/write pm_state only after grabbing the
+ * Normally, we read/write pm_state only after grabbing the
* pm_lock, since we're in a panic, skipping it. Also there is no
- * gurantee that this state change would take effect since
+ * guarantee that this state change would take effect since
* we're setting it w/o grabbing pm_lock
*/
mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
@@ -118,9 +118,7 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
/* Hardware reset so force device to enter RDDM */
dev_dbg(dev,
"Did not enter RDDM, do a host req reset\n");
- mhi_write_reg(mhi_cntrl, mhi_cntrl->regs,
- MHI_SOC_RESET_REQ_OFFSET,
- MHI_SOC_RESET_REQ);
+ mhi_soc_reset(mhi_cntrl);
udelay(delayus);
}
@@ -179,6 +177,36 @@ int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic)
}
EXPORT_SYMBOL_GPL(mhi_download_rddm_image);
+static void mhi_fw_load_error_dump(struct mhi_controller *mhi_cntrl)
+{
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
+ void __iomem *base = mhi_cntrl->bhi;
+ int ret, i;
+ u32 val;
+ struct {
+ char *name;
+ u32 offset;
+ } error_reg[] = {
+ { "ERROR_CODE", BHI_ERRCODE },
+ { "ERROR_DBG1", BHI_ERRDBG1 },
+ { "ERROR_DBG2", BHI_ERRDBG2 },
+ { "ERROR_DBG3", BHI_ERRDBG3 },
+ { NULL },
+ };
+
+ read_lock_bh(pm_lock);
+ if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
+ for (i = 0; error_reg[i].name; i++) {
+ ret = mhi_read_reg(mhi_cntrl, base, error_reg[i].offset, &val);
+ if (ret)
+ break;
+ dev_err(dev, "Reg: %s value: 0x%x\n", error_reg[i].name, val);
+ }
+ }
+ read_unlock_bh(pm_lock);
+}
+
static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
@@ -228,24 +256,13 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
}
static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
- dma_addr_t dma_addr,
- size_t size)
+ const struct mhi_buf *mhi_buf)
{
- u32 tx_status, val, session_id;
- int i, ret;
- void __iomem *base = mhi_cntrl->bhi;
- rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
- struct {
- char *name;
- u32 offset;
- } error_reg[] = {
- { "ERROR_CODE", BHI_ERRCODE },
- { "ERROR_DBG1", BHI_ERRDBG1 },
- { "ERROR_DBG2", BHI_ERRDBG2 },
- { "ERROR_DBG3", BHI_ERRDBG3 },
- { NULL },
- };
+ rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
+ void __iomem *base = mhi_cntrl->bhi;
+ u32 tx_status, session_id;
+ int ret;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
@@ -257,11 +274,9 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
dev_dbg(dev, "Starting image download via BHI. Session ID: %u\n",
session_id);
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
- mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
- upper_32_bits(dma_addr));
- mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
- lower_32_bits(dma_addr));
- mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
+ mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, upper_32_bits(mhi_buf->dma_addr));
+ mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW, lower_32_bits(mhi_buf->dma_addr));
+ mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, mhi_buf->len);
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
read_unlock_bh(pm_lock);
@@ -276,18 +291,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
if (tx_status == BHI_STATUS_ERROR) {
dev_err(dev, "Image transfer failed\n");
- read_lock_bh(pm_lock);
- if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
- for (i = 0; error_reg[i].name; i++) {
- ret = mhi_read_reg(mhi_cntrl, base,
- error_reg[i].offset, &val);
- if (ret)
- break;
- dev_err(dev, "Reg: %s value: 0x%x\n",
- error_reg[i].name, val);
- }
- }
- read_unlock_bh(pm_lock);
+ mhi_fw_load_error_dump(mhi_cntrl);
goto invalid_pm_state;
}
@@ -298,6 +302,16 @@ invalid_pm_state:
return -EIO;
}
+static void mhi_free_bhi_buffer(struct mhi_controller *mhi_cntrl,
+ struct image_info *image_info)
+{
+ struct mhi_buf *mhi_buf = image_info->mhi_buf;
+
+ dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr);
+ kfree(image_info->mhi_buf);
+ kfree(image_info);
+}
+
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
@@ -312,6 +326,45 @@ void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
kfree(image_info);
}
+static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl,
+ struct image_info **image_info,
+ size_t alloc_size)
+{
+ struct image_info *img_info;
+ struct mhi_buf *mhi_buf;
+
+ img_info = kzalloc(sizeof(*img_info), GFP_KERNEL);
+ if (!img_info)
+ return -ENOMEM;
+
+ /* Allocate memory for entry */
+ img_info->mhi_buf = kzalloc(sizeof(*img_info->mhi_buf), GFP_KERNEL);
+ if (!img_info->mhi_buf)
+ goto error_alloc_mhi_buf;
+
+ /* Allocate and populate vector table */
+ mhi_buf = img_info->mhi_buf;
+
+ mhi_buf->len = alloc_size;
+ mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
+ &mhi_buf->dma_addr, GFP_KERNEL);
+ if (!mhi_buf->buf)
+ goto error_alloc_segment;
+
+ img_info->bhi_vec = NULL;
+ img_info->entries = 1;
+ *image_info = img_info;
+
+ return 0;
+
+error_alloc_segment:
+ kfree(mhi_buf);
+error_alloc_mhi_buf:
+ kfree(img_info);
+
+ return -ENOMEM;
+}
+
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
@@ -359,6 +412,7 @@ error_alloc_segment:
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
mhi_buf->buf, mhi_buf->dma_addr);
+ kfree(img_info->mhi_buf);
error_alloc_mhi_buf:
kfree(img_info);
@@ -366,21 +420,19 @@ error_alloc_mhi_buf:
return -ENOMEM;
}
-static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
- const struct firmware *firmware,
- struct image_info *img_info)
+static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl,
+ const u8 *buf, size_t remainder,
+ struct image_info *img_info)
{
- size_t remainder = firmware->size;
size_t to_cpy;
- const u8 *buf = firmware->data;
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
while (remainder) {
to_cpy = min(remainder, mhi_buf->len);
memcpy(mhi_buf->buf, buf, to_cpy);
- bhi_vec->dma_addr = mhi_buf->dma_addr;
- bhi_vec->size = to_cpy;
+ bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
+ bhi_vec->size = cpu_to_le64(to_cpy);
buf += to_cpy;
remainder -= to_cpy;
@@ -389,15 +441,63 @@ static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
}
}
+static enum mhi_fw_load_type mhi_fw_load_type_get(const struct mhi_controller *mhi_cntrl)
+{
+ if (mhi_cntrl->fbc_download) {
+ return MHI_FW_LOAD_FBC;
+ } else {
+ if (mhi_cntrl->seg_len)
+ return MHI_FW_LOAD_BHIE;
+ else
+ return MHI_FW_LOAD_BHI;
+ }
+}
+
+static int mhi_load_image_bhi(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
+{
+ struct image_info *image;
+ int ret;
+
+ ret = mhi_alloc_bhi_buffer(mhi_cntrl, &image, size);
+ if (ret)
+ return ret;
+
+ /* Load the firmware into BHI vec table */
+ memcpy(image->mhi_buf->buf, fw_data, size);
+
+ ret = mhi_fw_load_bhi(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
+ mhi_free_bhi_buffer(mhi_cntrl, image);
+
+ return ret;
+}
+
+static int mhi_load_image_bhie(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
+{
+ struct image_info *image;
+ int ret;
+
+ ret = mhi_alloc_bhie_table(mhi_cntrl, &image, size);
+ if (ret)
+ return ret;
+
+ mhi_firmware_copy_bhie(mhi_cntrl, fw_data, size, image);
+
+ ret = mhi_fw_load_bhie(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
+ mhi_free_bhie_table(mhi_cntrl, image);
+
+ return ret;
+}
+
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
const struct firmware *firmware = NULL;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ enum mhi_fw_load_type fw_load_type;
+ enum mhi_pm_state new_state;
const char *fw_name;
- void *buf;
- dma_addr_t dma_addr;
- size_t size;
- int i, ret;
+ const u8 *fw_data;
+ size_t size, fw_sz;
+ int ret;
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device MHI is not in valid state\n");
@@ -410,15 +510,6 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
if (ret)
dev_err(dev, "Could not capture serial number via BHI\n");
- for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) {
- ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i),
- &mhi_cntrl->oem_pk_hash[i]);
- if (ret) {
- dev_err(dev, "Could not capture OEM PK HASH via BHI\n");
- break;
- }
- }
-
/* wait for ready on pass through or any other execution environment */
if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee))
goto fw_load_ready_state;
@@ -426,6 +517,20 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
+ /* check if the driver has already provided the firmware data */
+ if (!fw_name && mhi_cntrl->fbc_download &&
+ mhi_cntrl->fw_data && mhi_cntrl->fw_sz) {
+ if (!mhi_cntrl->sbl_size) {
+ dev_err(dev, "fw_data provided but no sbl_size\n");
+ goto error_fw_load;
+ }
+
+ size = mhi_cntrl->sbl_size;
+ fw_data = mhi_cntrl->fw_data;
+ fw_sz = mhi_cntrl->fw_sz;
+ goto skip_req_fw;
+ }
+
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
!mhi_cntrl->seg_len))) {
dev_err(dev,
@@ -445,27 +550,27 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
if (size > firmware->size)
size = firmware->size;
- buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, size, &dma_addr,
- GFP_KERNEL);
- if (!buf) {
- release_firmware(firmware);
- goto error_fw_load;
- }
+ fw_data = firmware->data;
+ fw_sz = firmware->size;
- /* Download image using BHI */
- memcpy(buf, firmware->data, size);
- ret = mhi_fw_load_bhi(mhi_cntrl, dma_addr, size);
- dma_free_coherent(mhi_cntrl->cntrl_dev, size, buf, dma_addr);
+skip_req_fw:
+ fw_load_type = mhi_fw_load_type_get(mhi_cntrl);
+ if (fw_load_type == MHI_FW_LOAD_BHIE)
+ ret = mhi_load_image_bhie(mhi_cntrl, fw_data, size);
+ else
+ ret = mhi_load_image_bhi(mhi_cntrl, fw_data, size);
/* Error or in EDL mode, we're done */
if (ret) {
- dev_err(dev, "MHI did not load image over BHI, ret: %d\n", ret);
+ dev_err(dev, "MHI did not load image over BHI%s, ret: %d\n",
+ fw_load_type == MHI_FW_LOAD_BHIE ? "e" : "",
+ ret);
release_firmware(firmware);
goto error_fw_load;
}
/* Wait for ready since EDL image was loaded */
- if (fw_name == mhi_cntrl->edl_image) {
+ if (fw_name && fw_name == mhi_cntrl->edl_image) {
release_firmware(firmware);
goto fw_load_ready_state;
}
@@ -478,16 +583,15 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
* If we're doing fbc, populate vector tables while
* device transitioning into MHI READY state
*/
- if (mhi_cntrl->fbc_download) {
- ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image,
- firmware->size);
+ if (fw_load_type == MHI_FW_LOAD_FBC) {
+ ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
if (ret) {
release_firmware(firmware);
goto error_fw_load;
}
/* Load the firmware into BHIE vec table */
- mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image);
+ mhi_firmware_copy_bhie(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
}
release_firmware(firmware);
@@ -504,20 +608,24 @@ fw_load_ready_state:
return;
error_ready_state:
- if (mhi_cntrl->fbc_download) {
+ if (mhi_cntrl->fbc_image) {
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
mhi_cntrl->fbc_image = NULL;
}
error_fw_load:
- mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR;
- wake_up_all(&mhi_cntrl->state_event);
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ if (new_state == MHI_PM_FW_DL_ERR)
+ wake_up_all(&mhi_cntrl->state_event);
}
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
{
struct image_info *image_info = mhi_cntrl->fbc_image;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ enum mhi_pm_state new_state;
int ret;
if (!image_info)
@@ -528,8 +636,11 @@ int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
&image_info->mhi_buf[image_info->entries - 1]);
if (ret) {
dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
- mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR;
- wake_up_all(&mhi_cntrl->state_event);
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ if (new_state == MHI_PM_FW_DL_ERR)
+ wake_up_all(&mhi_cntrl->state_event);
}
return ret;
diff --git a/drivers/bus/mhi/host/debugfs.c b/drivers/bus/mhi/host/debugfs.c
index cfec7811dfbb..39e45748a24c 100644
--- a/drivers/bus/mhi/host/debugfs.c
+++ b/drivers/bus/mhi/host/debugfs.c
@@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
+#include <linux/string_choices.h>
#include "internal.h"
static int mhi_debugfs_states_show(struct seq_file *m, void *d)
@@ -22,7 +23,7 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d)
mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
- mhi_cntrl->wake_set ? "true" : "false");
+ str_true_false(mhi_cntrl->wake_set));
/* counters */
seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2,
diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c
index bf672de35131..099be8dd1900 100644
--- a/drivers/bus/mhi/host/init.c
+++ b/drivers/bus/mhi/host/init.c
@@ -20,50 +20,49 @@
#include <linux/wait.h>
#include "internal.h"
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
static DEFINE_IDA(mhi_controller_ida);
+#undef mhi_ee
+#undef mhi_ee_end
+
+#define mhi_ee(a, b) [MHI_EE_##a] = b,
+#define mhi_ee_end(a, b) [MHI_EE_##a] = b,
+
const char * const mhi_ee_str[MHI_EE_MAX] = {
- [MHI_EE_PBL] = "PRIMARY BOOTLOADER",
- [MHI_EE_SBL] = "SECONDARY BOOTLOADER",
- [MHI_EE_AMSS] = "MISSION MODE",
- [MHI_EE_RDDM] = "RAMDUMP DOWNLOAD MODE",
- [MHI_EE_WFW] = "WLAN FIRMWARE",
- [MHI_EE_PTHRU] = "PASS THROUGH",
- [MHI_EE_EDL] = "EMERGENCY DOWNLOAD",
- [MHI_EE_FP] = "FLASH PROGRAMMER",
- [MHI_EE_DISABLE_TRANSITION] = "DISABLE",
- [MHI_EE_NOT_SUPPORTED] = "NOT SUPPORTED",
+ MHI_EE_LIST
};
+#undef dev_st_trans
+#undef dev_st_trans_end
+
+#define dev_st_trans(a, b) [DEV_ST_TRANSITION_##a] = b,
+#define dev_st_trans_end(a, b) [DEV_ST_TRANSITION_##a] = b,
+
const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
- [DEV_ST_TRANSITION_PBL] = "PBL",
- [DEV_ST_TRANSITION_READY] = "READY",
- [DEV_ST_TRANSITION_SBL] = "SBL",
- [DEV_ST_TRANSITION_MISSION_MODE] = "MISSION MODE",
- [DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
- [DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
- [DEV_ST_TRANSITION_DISABLE] = "DISABLE",
+ DEV_ST_TRANSITION_LIST
};
+#undef ch_state_type
+#undef ch_state_type_end
+
+#define ch_state_type(a, b) [MHI_CH_STATE_TYPE_##a] = b,
+#define ch_state_type_end(a, b) [MHI_CH_STATE_TYPE_##a] = b,
+
const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
- [MHI_CH_STATE_TYPE_RESET] = "RESET",
- [MHI_CH_STATE_TYPE_STOP] = "STOP",
- [MHI_CH_STATE_TYPE_START] = "START",
+ MHI_CH_STATE_TYPE_LIST
};
+#undef mhi_pm_state
+#undef mhi_pm_state_end
+
+#define mhi_pm_state(a, b) [MHI_PM_STATE_##a] = b,
+#define mhi_pm_state_end(a, b) [MHI_PM_STATE_##a] = b,
+
static const char * const mhi_pm_state_str[] = {
- [MHI_PM_STATE_DISABLE] = "DISABLE",
- [MHI_PM_STATE_POR] = "POWER ON RESET",
- [MHI_PM_STATE_M0] = "M0",
- [MHI_PM_STATE_M2] = "M2",
- [MHI_PM_STATE_M3_ENTER] = "M?->M3",
- [MHI_PM_STATE_M3] = "M3",
- [MHI_PM_STATE_M3_EXIT] = "M3->M0",
- [MHI_PM_STATE_FW_DL_ERR] = "Firmware Download Error",
- [MHI_PM_STATE_SYS_ERR_DETECT] = "SYS ERROR Detect",
- [MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS ERROR Process",
- [MHI_PM_STATE_SHUTDOWN_PROCESS] = "SHUTDOWN Process",
- [MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect",
+ MHI_PM_STATE_LIST
};
const char *to_mhi_pm_state_str(u32 state)
@@ -97,11 +96,19 @@ static ssize_t oem_pk_hash_show(struct device *dev,
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
- int i, cnt = 0;
+ u32 hash_segment[MHI_MAX_OEM_PK_HASH_SEGMENTS];
+ int i, cnt = 0, ret;
+
+ for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) {
+ ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), &hash_segment[i]);
+ if (ret) {
+ dev_err(dev, "Could not capture OEM PK HASH\n");
+ return ret;
+ }
+ }
- for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++)
- cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n",
- i, mhi_cntrl->oem_pk_hash[i]);
+ for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++)
+ cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", i, hash_segment[i]);
return cnt;
}
@@ -120,6 +127,30 @@ static ssize_t soc_reset_store(struct device *dev,
}
static DEVICE_ATTR_WO(soc_reset);
+static ssize_t trigger_edl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhi_device *mhi_dev = to_mhi_device(dev);
+ struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ if (!val)
+ return -EINVAL;
+
+ ret = mhi_cntrl->edl_trigger(mhi_cntrl);
+ if (ret)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_WO(trigger_edl);
+
static struct attribute *mhi_dev_attrs[] = {
&dev_attr_serial_number.attr,
&dev_attr_oem_pk_hash.attr,
@@ -145,7 +176,7 @@ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl,
return 0;
}
-void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
+static void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
{
int i;
struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
@@ -160,10 +191,9 @@ void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
free_irq(mhi_cntrl->irq[0], mhi_cntrl);
}
-int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
+static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
{
struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
unsigned long irq_flags = IRQF_SHARED | IRQF_NO_SUSPEND;
int i, ret;
@@ -190,7 +220,7 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
continue;
if (mhi_event->irq >= mhi_cntrl->nr_irqs) {
- dev_err(dev, "irq %d not available for event ring\n",
+ dev_err(mhi_cntrl->cntrl_dev, "irq %d not available for event ring\n",
mhi_event->irq);
ret = -EINVAL;
goto error_request;
@@ -201,7 +231,7 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
irq_flags,
"mhi", mhi_event);
if (ret) {
- dev_err(dev, "Error requesting irq:%d for ev:%d\n",
+ dev_err(mhi_cntrl->cntrl_dev, "Error requesting irq:%d for ev:%d\n",
mhi_cntrl->irq[mhi_event->irq], i);
goto error_request;
}
@@ -223,7 +253,7 @@ error_request:
return ret;
}
-void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
+static void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
{
int i;
struct mhi_ctxt *mhi_ctxt = mhi_cntrl->mhi_ctxt;
@@ -268,7 +298,7 @@ void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
mhi_cntrl->mhi_ctxt = NULL;
}
-int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
+static int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
{
struct mhi_ctxt *mhi_ctxt;
struct mhi_chan_ctxt *chan_ctxt;
@@ -510,10 +540,14 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
dev_dbg(dev, "Initializing MHI registers\n");
/* Read channel db offset */
- ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val);
- if (ret) {
- dev_err(dev, "Unable to read CHDBOFF register\n");
- return -EIO;
+ ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val);
+ if (ret)
+ return ret;
+
+ if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) {
+ dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n",
+ val, mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB));
+ return -ERANGE;
}
/* Setup wake db */
@@ -532,6 +566,12 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
return -EIO;
}
+ if (val >= mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings)) {
+ dev_err(dev, "ERDB offset: 0x%x is out of range: 0x%zx\n",
+ val, mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings));
+ return -ERANGE;
+ }
+
/* Setup event db address for each ev_ring */
mhi_event = mhi_cntrl->mhi_event;
for (i = 0; i < mhi_cntrl->total_ev_rings; i++, val += 8, mhi_event++) {
@@ -747,7 +787,7 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
* so to avoid any memory possible allocation failures, vzalloc is
* used here
*/
- mhi_cntrl->mhi_chan = vzalloc(mhi_cntrl->max_chan *
+ mhi_cntrl->mhi_chan = vcalloc(mhi_cntrl->max_chan,
sizeof(*mhi_cntrl->mhi_chan));
if (!mhi_cntrl->mhi_chan)
return -ENOMEM;
@@ -869,6 +909,7 @@ static int parse_config(struct mhi_controller *mhi_cntrl,
if (!mhi_cntrl->timeout_ms)
mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS;
+ mhi_cntrl->ready_timeout_ms = config->ready_timeout_ms;
mhi_cntrl->bounce_buf = config->use_bounce_buf;
mhi_cntrl->buffer_len = config->buf_len;
if (!mhi_cntrl->buffer_len)
@@ -894,7 +935,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan;
struct mhi_cmd *mhi_cmd;
struct mhi_device *mhi_dev;
- u32 soc_info;
int ret, i;
if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs ||
@@ -969,17 +1009,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
mhi_cntrl->unmap_single = mhi_unmap_single_no_bb;
}
- /* Read the MHI device info */
- ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs,
- SOC_HW_VERSION_OFFS, &soc_info);
- if (ret)
- goto err_destroy_wq;
-
- mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info);
- mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info);
- mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info);
- mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info);
-
mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL);
if (mhi_cntrl->index < 0) {
ret = mhi_cntrl->index;
@@ -1010,6 +1039,12 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
if (ret)
goto err_release_dev;
+ if (mhi_cntrl->edl_trigger) {
+ ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
+ if (ret)
+ goto err_release_dev;
+ }
+
mhi_cntrl->mhi_dev = mhi_dev;
mhi_create_debugfs(mhi_cntrl);
@@ -1043,6 +1078,9 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
mhi_deinit_free_irq(mhi_cntrl);
mhi_destroy_debugfs(mhi_cntrl);
+ if (mhi_cntrl->edl_trigger)
+ sysfs_remove_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
+
destroy_workqueue(mhi_cntrl->hiprio_wq);
kfree(mhi_cntrl->mhi_cmd);
kfree(mhi_cntrl->mhi_event);
@@ -1100,12 +1138,12 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
if (bhi_off >= mhi_cntrl->reg_len) {
dev_err(dev, "BHI offset: 0x%x is out of range: 0x%zx\n",
bhi_off, mhi_cntrl->reg_len);
- ret = -EINVAL;
+ ret = -ERANGE;
goto error_reg_offset;
}
mhi_cntrl->bhi = mhi_cntrl->regs + bhi_off;
- if (mhi_cntrl->fbc_download || mhi_cntrl->rddm_size) {
+ if (mhi_cntrl->fbc_download || mhi_cntrl->rddm_size || mhi_cntrl->seg_len) {
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIEOFF,
&bhie_off);
if (ret) {
@@ -1117,7 +1155,7 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
dev_err(dev,
"BHIe offset: 0x%x is out of range: 0x%zx\n",
bhie_off, mhi_cntrl->reg_len);
- ret = -EINVAL;
+ ret = -ERANGE;
goto error_reg_offset;
}
mhi_cntrl->bhie = mhi_cntrl->regs + bhie_off;
@@ -1395,18 +1433,18 @@ void mhi_driver_unregister(struct mhi_driver *mhi_drv)
}
EXPORT_SYMBOL_GPL(mhi_driver_unregister);
-static int mhi_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int mhi_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct mhi_device *mhi_dev = to_mhi_device(dev);
+ const struct mhi_device *mhi_dev = to_mhi_device(dev);
return add_uevent_var(env, "MODALIAS=" MHI_DEVICE_MODALIAS_FMT,
mhi_dev->name);
}
-static int mhi_match(struct device *dev, struct device_driver *drv)
+static int mhi_match(struct device *dev, const struct device_driver *drv)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
- struct mhi_driver *mhi_drv = to_mhi_driver(drv);
+ const struct mhi_driver *mhi_drv = to_mhi_driver(drv);
const struct mhi_device_id *id;
/*
@@ -1425,7 +1463,7 @@ static int mhi_match(struct device *dev, struct device_driver *drv)
return 0;
};
-struct bus_type mhi_bus_type = {
+const struct bus_type mhi_bus_type = {
.name = "mhi",
.dev_name = "mhi",
.match = mhi_match,
@@ -1449,4 +1487,4 @@ postcore_initcall(mhi_init);
module_exit(mhi_exit);
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("MHI Host Interface");
+MODULE_DESCRIPTION("Modem Host Interface");
diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
index 01fd10a399b6..7937bb1f742c 100644
--- a/drivers/bus/mhi/host/internal.h
+++ b/drivers/bus/mhi/host/internal.h
@@ -9,18 +9,12 @@
#include "../common.h"
-extern struct bus_type mhi_bus_type;
+extern const struct bus_type mhi_bus_type;
/* Host request register */
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
#define MHI_SOC_RESET_REQ BIT(0)
-#define SOC_HW_VERSION_OFFS 0x224
-#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28)
-#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16)
-#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8)
-#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0)
-
struct mhi_ctxt {
struct mhi_event_ctxt *er_ctxt;
struct mhi_chan_ctxt *chan_ctxt;
@@ -31,8 +25,15 @@ struct mhi_ctxt {
};
struct bhi_vec_entry {
- u64 dma_addr;
- u64 size;
+ __le64 dma_addr;
+ __le64 size;
+};
+
+enum mhi_fw_load_type {
+ MHI_FW_LOAD_BHI, /* BHI only in PBL */
+ MHI_FW_LOAD_BHIE, /* BHIe only in PBL */
+ MHI_FW_LOAD_FBC, /* BHI in PBL followed by BHIe in SBL */
+ MHI_FW_LOAD_MAX,
};
enum mhi_ch_state_type {
@@ -42,6 +43,11 @@ enum mhi_ch_state_type {
MHI_CH_STATE_TYPE_MAX,
};
+#define MHI_CH_STATE_TYPE_LIST \
+ ch_state_type(RESET, "RESET") \
+ ch_state_type(STOP, "STOP") \
+ ch_state_type_end(START, "START")
+
extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
"INVALID_STATE" : \
@@ -50,6 +56,18 @@ extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
mode != MHI_DB_BRST_ENABLE)
+#define MHI_EE_LIST \
+ mhi_ee(PBL, "PRIMARY BOOTLOADER") \
+ mhi_ee(SBL, "SECONDARY BOOTLOADER") \
+ mhi_ee(AMSS, "MISSION MODE") \
+ mhi_ee(RDDM, "RAMDUMP DOWNLOAD MODE")\
+ mhi_ee(WFW, "WLAN FIRMWARE") \
+ mhi_ee(PTHRU, "PASS THROUGH") \
+ mhi_ee(EDL, "EMERGENCY DOWNLOAD") \
+ mhi_ee(FP, "FLASH PROGRAMMER") \
+ mhi_ee(DISABLE_TRANSITION, "DISABLE") \
+ mhi_ee_end(NOT_SUPPORTED, "NOT SUPPORTED")
+
extern const char * const mhi_ee_str[MHI_EE_MAX];
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
"INVALID_EE" : mhi_ee_str[ee])
@@ -69,9 +87,20 @@ enum dev_st_transition {
DEV_ST_TRANSITION_FP,
DEV_ST_TRANSITION_SYS_ERR,
DEV_ST_TRANSITION_DISABLE,
+ DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
DEV_ST_TRANSITION_MAX,
};
+#define DEV_ST_TRANSITION_LIST \
+ dev_st_trans(PBL, "PBL") \
+ dev_st_trans(READY, "READY") \
+ dev_st_trans(SBL, "SBL") \
+ dev_st_trans(MISSION_MODE, "MISSION MODE") \
+ dev_st_trans(FP, "FLASH PROGRAMMER") \
+ dev_st_trans(SYS_ERR, "SYS ERROR") \
+ dev_st_trans(DISABLE, "DISABLE") \
+ dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)")
+
extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX];
#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \
"INVALID_STATE" : dev_state_tran_str[state])
@@ -88,11 +117,27 @@ enum mhi_pm_state {
MHI_PM_STATE_FW_DL_ERR,
MHI_PM_STATE_SYS_ERR_DETECT,
MHI_PM_STATE_SYS_ERR_PROCESS,
+ MHI_PM_STATE_SYS_ERR_FAIL,
MHI_PM_STATE_SHUTDOWN_PROCESS,
MHI_PM_STATE_LD_ERR_FATAL_DETECT,
MHI_PM_STATE_MAX
};
+#define MHI_PM_STATE_LIST \
+ mhi_pm_state(DISABLE, "DISABLE") \
+ mhi_pm_state(POR, "POWER ON RESET") \
+ mhi_pm_state(M0, "M0") \
+ mhi_pm_state(M2, "M2") \
+ mhi_pm_state(M3_ENTER, "M?->M3") \
+ mhi_pm_state(M3, "M3") \
+ mhi_pm_state(M3_EXIT, "M3->M0") \
+ mhi_pm_state(FW_DL_ERR, "Firmware Download Error") \
+ mhi_pm_state(SYS_ERR_DETECT, "SYS ERROR Detect") \
+ mhi_pm_state(SYS_ERR_PROCESS, "SYS ERROR Process") \
+ mhi_pm_state(SYS_ERR_FAIL, "SYS ERROR Failure") \
+ mhi_pm_state(SHUTDOWN_PROCESS, "SHUTDOWN Process") \
+ mhi_pm_state_end(LD_ERR_FATAL_DETECT, "Linkdown or Error Fatal Detect")
+
#define MHI_PM_DISABLE BIT(0)
#define MHI_PM_POR BIT(1)
#define MHI_PM_M0 BIT(2)
@@ -104,14 +149,16 @@ enum mhi_pm_state {
#define MHI_PM_FW_DL_ERR BIT(7)
#define MHI_PM_SYS_ERR_DETECT BIT(8)
#define MHI_PM_SYS_ERR_PROCESS BIT(9)
-#define MHI_PM_SHUTDOWN_PROCESS BIT(10)
+#define MHI_PM_SYS_ERR_FAIL BIT(10)
+#define MHI_PM_SHUTDOWN_PROCESS BIT(11)
/* link not accessible */
-#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11)
+#define MHI_PM_LD_ERR_FATAL_DETECT BIT(12)
#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
- MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
+ MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | \
+ MHI_PM_FW_DL_ERR)))
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access)
@@ -123,13 +170,15 @@ enum mhi_pm_state {
MHI_PM_IN_ERROR_STATE(pm_state))
#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \
(MHI_PM_M3_ENTER | MHI_PM_M3))
+#define MHI_PM_FATAL_ERROR(pm_state) ((pm_state == MHI_PM_FW_DL_ERR) || \
+ (pm_state >= MHI_PM_SYS_ERR_FAIL))
#define NR_OF_CMD_RINGS 1
#define CMD_EL_PER_RING 128
#define PRIMARY_CMD_RING 0
#define MHI_DEV_WAKE_DB 127
#define MHI_MAX_MTU 0xffff
-#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1)
+#define MHI_RANDOM_U32_NONZERO(bmsk) (get_random_u32_inclusive(1, bmsk))
enum mhi_er_type {
MHI_ER_TYPE_INVALID = 0x0,
@@ -215,7 +264,7 @@ struct mhi_chan {
/*
* Important: When consuming, increment tre_ring first and when
* releasing, decrement buf_ring first. If tre_ring has space, buf_ring
- * is guranteed to have space so we do not need to check both rings.
+ * is guaranteed to have space so we do not need to check both rings.
*/
struct mhi_ring buf_ring;
struct mhi_ring tre_ring;
@@ -321,7 +370,7 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
u32 *out);
int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
- u32 val, u32 delayus);
+ u32 val, u32 delayus, u32 timeout_ms);
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
u32 offset, u32 val);
int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
@@ -336,19 +385,12 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
/* Initialization methods */
int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
-int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl);
-void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl);
-int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl);
-void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
/* Automatically allocate and queue inbound buffers */
#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0)
-int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan, unsigned int flags);
-
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
@@ -363,6 +405,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event, u32 event_quota);
int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event, u32 event_quota);
+void mhi_uevent_notify(struct mhi_controller *mhi_cntrl, enum mhi_ee_type ee);
/* ISR handlers */
irqreturn_t mhi_irq_handler(int irq_number, void *dev);
diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c
index df0fbfee7b78..861551274319 100644
--- a/drivers/bus/mhi/host/main.c
+++ b/drivers/bus/mhi/host/main.c
@@ -15,6 +15,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include "internal.h"
+#include "trace.h"
int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 *out)
@@ -40,10 +41,11 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset,
- u32 mask, u32 val, u32 delayus)
+ u32 mask, u32 val, u32 delayus,
+ u32 timeout_ms)
{
int ret;
- u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
+ u32 out, retry = (timeout_ms * 1000) / delayus;
while (retry--) {
ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out);
@@ -268,7 +270,8 @@ static void mhi_del_ring_element(struct mhi_controller *mhi_cntrl,
static bool is_valid_ring_ptr(struct mhi_ring *ring, dma_addr_t addr)
{
- return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len;
+ return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len &&
+ !(addr & (sizeof(struct mhi_ring_element) - 1));
}
int mhi_destroy_device(struct device *dev, void *data)
@@ -491,11 +494,8 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_get_exec_env(mhi_cntrl);
- dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n",
- TO_MHI_EXEC_STR(mhi_cntrl->ee),
- mhi_state_str(mhi_cntrl->dev_state),
- TO_MHI_EXEC_STR(ee), mhi_state_str(state));
+ trace_mhi_intvec_states(mhi_cntrl, ee, state);
if (state == MHI_STATE_SYS_ERR) {
dev_dbg(dev, "System error detected\n");
pm_state = mhi_tryset_pm_state(mhi_cntrl,
@@ -503,7 +503,7 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
}
write_unlock_irq(&mhi_cntrl->pm_lock);
- if (pm_state != MHI_PM_SYS_ERR_DETECT || ee == mhi_cntrl->ee)
+ if (pm_state != MHI_PM_SYS_ERR_DETECT)
goto exit_intvec;
switch (ee) {
@@ -512,6 +512,7 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
if (mhi_cntrl->rddm_image && mhi_is_active(mhi_cntrl)) {
mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
mhi_cntrl->ee = ee;
+ mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
wake_up_all(&mhi_cntrl->state_event);
}
break;
@@ -602,7 +603,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
{
dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event);
struct mhi_ring_element *local_rp, *ev_tre;
- void *dev_rp;
+ void *dev_rp, *next_rp;
struct mhi_buf_info *buf_info;
u16 xfer_len;
@@ -621,6 +622,16 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
result.dir = mhi_chan->dir;
local_rp = tre_ring->rp;
+
+ next_rp = local_rp + 1;
+ if (next_rp >= tre_ring->base + tre_ring->len)
+ next_rp = tre_ring->base;
+ if (dev_rp != next_rp && !MHI_TRE_DATA_GET_CHAIN(local_rp)) {
+ dev_err(&mhi_cntrl->mhi_dev->dev,
+ "Event element points to an unexpected TRE\n");
+ break;
+ }
+
while (local_rp != dev_rp) {
buf_info = buf_ring->rp;
/* If it's the last TRE, get length from the event */
@@ -642,6 +653,8 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
mhi_del_ring_element(mhi_cntrl, tre_ring);
local_rp = tre_ring->rp;
+ read_unlock_bh(&mhi_chan->lock);
+
/* notify client */
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
@@ -667,6 +680,8 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
kfree(buf_info->cb_buf);
}
}
+
+ read_lock_bh(&mhi_chan->lock);
}
break;
} /* CC_EOT */
@@ -832,6 +847,8 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
while (dev_rp != local_rp) {
enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
+ trace_mhi_ctrl_event(mhi_cntrl, local_rp);
+
switch (type) {
case MHI_PKT_TYPE_BW_REQ_EVENT:
{
@@ -938,7 +955,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
if (!mhi_chan->configured)
break;
parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
- event_quota--;
}
break;
default:
@@ -961,7 +977,9 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
}
read_lock_bh(&mhi_cntrl->pm_lock);
- if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
+
+ /* Ring EV DB only if there is any pending element to process */
+ if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count)
mhi_ring_er_db(mhi_event);
read_unlock_bh(&mhi_cntrl->pm_lock);
@@ -996,6 +1014,8 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
while (dev_rp != local_rp && event_quota > 0) {
enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
+ trace_mhi_data_event(mhi_cntrl, local_rp);
+
chan = MHI_TRE_GET_EV_CHID(local_rp);
WARN_ON(chan >= mhi_cntrl->max_chan);
@@ -1031,7 +1051,9 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
count++;
}
read_lock_bh(&mhi_cntrl->pm_lock);
- if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
+
+ /* Ring EV DB only if there is any pending element to process */
+ if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count)
mhi_ring_er_db(mhi_event);
read_unlock_bh(&mhi_cntrl->pm_lock);
@@ -1119,17 +1141,15 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info,
if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)))
return -EIO;
- read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
-
ret = mhi_is_ring_full(mhi_cntrl, tre_ring);
- if (unlikely(ret)) {
- ret = -EAGAIN;
- goto exit_unlock;
- }
+ if (unlikely(ret))
+ return -EAGAIN;
ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf_info, mflags);
if (unlikely(ret))
- goto exit_unlock;
+ return ret;
+
+ read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
/* Packet is queued, take a usage ref to exit M3 if necessary
* for host->device buffer, balanced put is done on buffer completion
@@ -1149,7 +1169,6 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info,
if (dir == DMA_FROM_DEVICE)
mhi_cntrl->runtime_put(mhi_cntrl);
-exit_unlock:
read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags);
return ret;
@@ -1173,25 +1192,6 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
}
EXPORT_SYMBOL_GPL(mhi_queue_skb);
-int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
- struct mhi_buf *mhi_buf, size_t len, enum mhi_flags mflags)
-{
- struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
- mhi_dev->dl_chan;
- struct mhi_buf_info buf_info = { };
-
- buf_info.p_addr = mhi_buf->dma_addr;
- buf_info.cb_buf = mhi_buf;
- buf_info.pre_mapped = true;
- buf_info.len = len;
-
- if (unlikely(mhi_chan->pre_alloc))
- return -EINVAL;
-
- return mhi_queue(mhi_dev, &buf_info, dir, mflags);
-}
-EXPORT_SYMBOL_GPL(mhi_queue_dma);
-
int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_buf_info *info, enum mhi_flags flags)
{
@@ -1199,7 +1199,15 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_ring_element *mhi_tre;
struct mhi_buf_info *buf_info;
int eot, eob, chain, bei;
- int ret;
+ int ret = 0;
+
+ /* Protect accesses for reading and incrementing WP */
+ write_lock_bh(&mhi_chan->lock);
+
+ if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) {
+ ret = -ENODEV;
+ goto out;
+ }
buf_ring = &mhi_chan->buf_ring;
tre_ring = &mhi_chan->tre_ring;
@@ -1219,7 +1227,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
if (!info->pre_mapped) {
ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
if (ret)
- return ret;
+ goto out;
}
eob = !!(flags & MHI_EOB);
@@ -1232,11 +1240,15 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len);
mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain);
+ trace_mhi_gen_tre(mhi_cntrl, mhi_chan, mhi_tre);
/* increment WP */
mhi_add_ring_element(mhi_cntrl, tre_ring);
mhi_add_ring_element(mhi_cntrl, buf_ring);
- return 0;
+out:
+ write_unlock_bh(&mhi_chan->lock);
+
+ return ret;
}
int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
@@ -1324,9 +1336,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
enum mhi_cmd_type cmd = MHI_CMD_NOP;
int ret;
- dev_dbg(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan,
- TO_CH_STATE_TYPE_STR(to_state));
-
+ trace_mhi_channel_command_start(mhi_cntrl, mhi_chan, to_state, TPS("Updating"));
switch (to_state) {
case MHI_CH_STATE_TYPE_RESET:
write_lock_irq(&mhi_chan->lock);
@@ -1393,9 +1403,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
write_unlock_irq(&mhi_chan->lock);
}
- dev_dbg(dev, "%d: Channel state change to %s successful\n",
- mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
-
+ trace_mhi_channel_command_end(mhi_cntrl, mhi_chan, to_state, TPS("Updated"));
exit_channel_update:
mhi_cntrl->runtime_put(mhi_cntrl);
mhi_device_put(mhi_cntrl->mhi_dev);
@@ -1438,7 +1446,7 @@ exit_unprepare_channel:
mutex_unlock(&mhi_chan->mutex);
}
-int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
+static int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan, unsigned int flags)
{
int ret = 0;
@@ -1680,17 +1688,18 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
}
EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
-int mhi_poll(struct mhi_device *mhi_dev, u32 budget)
+int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset)
{
- struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
- struct mhi_chan *mhi_chan = mhi_dev->dl_chan;
- struct mhi_event *mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ void __iomem *base = mhi_cntrl->regs;
int ret;
- spin_lock_bh(&mhi_event->lock);
- ret = mhi_event->process_event(mhi_cntrl, mhi_event, budget);
- spin_unlock_bh(&mhi_event->lock);
+ ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, chdb_offset);
+ if (ret) {
+ dev_err(dev, "Unable to read CHDBOFF register\n");
+ return -EIO;
+ }
- return ret;
+ return 0;
}
-EXPORT_SYMBOL_GPL(mhi_poll);
+EXPORT_SYMBOL_GPL(mhi_get_channel_doorbell_offset);
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index fa2246da63c1..e3bc737313a2 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -8,7 +8,6 @@
* Copyright (C) 2020 Linaro Ltd <loic.poulain@linaro.org>
*/
-#include <linux/aer.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mhi.h>
@@ -24,27 +23,45 @@
#define HEALTH_CHECK_PERIOD (HZ * 2)
+/* PCI VID definitions */
+#define PCI_VENDOR_ID_THALES 0x1269
+#define PCI_VENDOR_ID_QUECTEL 0x1eac
+#define PCI_VENDOR_ID_NETPRISMA 0x203e
+
+#define MHI_EDL_DB 91
+#define MHI_EDL_COOKIE 0xEDEDEDED
+
/**
* struct mhi_pci_dev_info - MHI PCI device specific information
* @config: MHI controller configuration
+ * @vf_config: MHI controller configuration for Virtual function (optional)
* @name: name of the PCI module
* @fw: firmware path (if any)
* @edl: emergency download mode firmware path (if any)
+ * @edl_trigger: capable of triggering EDL mode in the device (if supported)
* @bar_num: PCI base address register to use for MHI MMIO register space
* @dma_data_width: DMA transfer word size (32 or 64 bits)
+ * @vf_dma_data_width: DMA transfer word size for VF's (optional)
* @mru_default: default MRU size for MBIM network packets
* @sideband_wake: Devices using dedicated sideband GPIO for wakeup instead
* of inband wake support (such as sdx24)
+ * @no_m3: M3 not supported
+ * @reset_on_remove: Set true for devices that require SoC during driver removal
*/
struct mhi_pci_dev_info {
const struct mhi_controller_config *config;
+ const struct mhi_controller_config *vf_config;
const char *name;
const char *fw;
const char *edl;
+ bool edl_trigger;
unsigned int bar_num;
unsigned int dma_data_width;
+ unsigned int vf_dma_data_width;
unsigned int mru_default;
bool sideband_wake;
+ bool no_m3;
+ bool reset_on_remove;
};
#define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \
@@ -209,6 +226,19 @@ struct mhi_pci_dev_info {
.offload_channel = false, \
}
+#define MHI_EVENT_CONFIG_SW_DATA(ev_ring, el_count) \
+ { \
+ .num_elements = el_count, \
+ .irq_moderation_ms = 0, \
+ .irq = (ev_ring) + 1, \
+ .priority = 1, \
+ .mode = MHI_DB_BRST_DISABLE, \
+ .data_type = MHI_ER_DATA, \
+ .hardware_event = false, \
+ .client_managed = false, \
+ .offload_channel = false, \
+ }
+
#define MHI_EVENT_CONFIG_HW_DATA(ev_ring, el_count, ch_num) \
{ \
.num_elements = el_count, \
@@ -223,6 +253,74 @@ struct mhi_pci_dev_info {
.channel = ch_num, \
}
+static const struct mhi_channel_config mhi_qcom_qdu100_channels[] = {
+ MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 2),
+ MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 2),
+ MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 128, 1),
+ MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 128, 1),
+ MHI_CHANNEL_CONFIG_UL(4, "DIAG", 64, 3),
+ MHI_CHANNEL_CONFIG_DL(5, "DIAG", 64, 3),
+ MHI_CHANNEL_CONFIG_UL(9, "QDSS", 64, 3),
+ MHI_CHANNEL_CONFIG_UL(14, "NMEA", 32, 4),
+ MHI_CHANNEL_CONFIG_DL(15, "NMEA", 32, 4),
+ MHI_CHANNEL_CONFIG_UL(16, "CSM_CTRL", 32, 4),
+ MHI_CHANNEL_CONFIG_DL(17, "CSM_CTRL", 32, 4),
+ MHI_CHANNEL_CONFIG_UL(40, "MHI_PHC", 32, 4),
+ MHI_CHANNEL_CONFIG_DL(41, "MHI_PHC", 32, 4),
+ MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 256, 5),
+ MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 256, 5),
+};
+
+static struct mhi_event_config mhi_qcom_qdu100_events[] = {
+ /* first ring is control+data ring */
+ MHI_EVENT_CONFIG_CTRL(0, 64),
+ /* SAHARA dedicated event ring */
+ MHI_EVENT_CONFIG_SW_DATA(1, 256),
+ /* Software channels dedicated event ring */
+ MHI_EVENT_CONFIG_SW_DATA(2, 64),
+ MHI_EVENT_CONFIG_SW_DATA(3, 256),
+ MHI_EVENT_CONFIG_SW_DATA(4, 256),
+ /* Software IP channels dedicated event ring */
+ MHI_EVENT_CONFIG_SW_DATA(5, 512),
+ MHI_EVENT_CONFIG_SW_DATA(6, 512),
+ MHI_EVENT_CONFIG_SW_DATA(7, 512),
+};
+
+static const struct mhi_controller_config mhi_qcom_qdu100_config = {
+ .max_channels = 128,
+ .timeout_ms = 120000,
+ .num_channels = ARRAY_SIZE(mhi_qcom_qdu100_channels),
+ .ch_cfg = mhi_qcom_qdu100_channels,
+ .num_events = ARRAY_SIZE(mhi_qcom_qdu100_events),
+ .event_cfg = mhi_qcom_qdu100_events,
+};
+
+static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = {
+ .name = "qcom-qdu100",
+ .fw = "qcom/qdu100/xbl_s.melf",
+ .edl_trigger = true,
+ .config = &mhi_qcom_qdu100_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .vf_dma_data_width = 40,
+ .sideband_wake = false,
+ .no_m3 = true,
+ .reset_on_remove = true,
+};
+
+static const struct mhi_channel_config mhi_qcom_sa8775p_channels[] = {
+ MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 2048, 1),
+ MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 2048, 2),
+};
+
+static struct mhi_event_config mhi_qcom_sa8775p_events[] = {
+ /* first ring is control+data ring */
+ MHI_EVENT_CONFIG_CTRL(0, 64),
+ /* Software channels dedicated event ring */
+ MHI_EVENT_CONFIG_SW_DATA(1, 64),
+ MHI_EVENT_CONFIG_SW_DATA(2, 64),
+};
+
static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 16, 1),
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 16, 1),
@@ -234,8 +332,10 @@ static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 8, 0),
MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
- MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2),
- MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3),
+ MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 64, 2),
+ MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 64, 3),
+ MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 4),
+ MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 5),
};
static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
@@ -243,9 +343,31 @@ static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
MHI_EVENT_CONFIG_CTRL(0, 64),
/* DIAG dedicated event ring */
MHI_EVENT_CONFIG_DATA(1, 128),
+ /* Software channels dedicated event ring */
+ MHI_EVENT_CONFIG_SW_DATA(2, 64),
+ MHI_EVENT_CONFIG_SW_DATA(3, 64),
/* Hardware channels request dedicated hardware event rings */
- MHI_EVENT_CONFIG_HW_DATA(2, 1024, 100),
- MHI_EVENT_CONFIG_HW_DATA(3, 2048, 101)
+ MHI_EVENT_CONFIG_HW_DATA(4, 1024, 100),
+ MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101)
+};
+
+static const struct mhi_controller_config mhi_qcom_sa8775p_config = {
+ .max_channels = 128,
+ .timeout_ms = 8000,
+ .num_channels = ARRAY_SIZE(mhi_qcom_sa8775p_channels),
+ .ch_cfg = mhi_qcom_sa8775p_channels,
+ .num_events = ARRAY_SIZE(mhi_qcom_sa8775p_events),
+ .event_cfg = mhi_qcom_sa8775p_events,
+};
+
+static const struct mhi_controller_config modem_qcom_v2_mhiv_config = {
+ .max_channels = 128,
+ .timeout_ms = 8000,
+ .ready_timeout_ms = 50000,
+ .num_channels = ARRAY_SIZE(modem_qcom_v1_mhi_channels),
+ .ch_cfg = modem_qcom_v1_mhi_channels,
+ .num_events = ARRAY_SIZE(modem_qcom_v1_mhi_events),
+ .event_cfg = modem_qcom_v1_mhi_events,
};
static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
@@ -257,10 +379,32 @@ static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
.event_cfg = modem_qcom_v1_mhi_events,
};
+static const struct mhi_pci_dev_info mhi_qcom_sa8775p_info = {
+ .name = "qcom-sa8775p",
+ .edl_trigger = false,
+ .config = &mhi_qcom_sa8775p_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = {
+ .name = "qcom-sdx75m",
+ .fw = "qcom/sdx75m/xbl.elf",
+ .edl = "qcom/sdx75m/edl.mbn",
+ .edl_trigger = true,
+ .config = &modem_qcom_v2_mhiv_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .sideband_wake = false,
+};
+
static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = {
.name = "qcom-sdx65m",
.fw = "qcom/sdx65m/xbl.elf",
.edl = "qcom/sdx65m/edl.mbn",
+ .edl_trigger = true,
.config = &modem_qcom_v1_mhiv_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -271,6 +415,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = {
.name = "qcom-sdx55m",
.fw = "qcom/sdx55m/sbl1.mbn",
.edl = "qcom/sdx55m/edl.mbn",
+ .edl_trigger = true,
.config = &modem_qcom_v1_mhiv_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -331,6 +476,16 @@ static const struct mhi_pci_dev_info mhi_quectel_em1xx_info = {
.sideband_wake = true,
};
+static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = {
+ .name = "quectel-rm5xx",
+ .edl = "qcom/prog_firehose_sdx6x.elf",
+ .config = &modem_quectel_em1xx_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = true,
+};
+
static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
@@ -340,6 +495,25 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0),
MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
+ MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
+ MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
+ MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2),
+ MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3),
+};
+
+static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = {
+ MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
+ MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
+ MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
+ MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0),
+ MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
+ MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
+ MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
+ MHI_CHANNEL_CONFIG_UL(50, "NMEA", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(51, "NMEA", 32, 0),
MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2),
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3),
};
@@ -360,10 +534,29 @@ static const struct mhi_controller_config modem_foxconn_sdx55_config = {
.event_cfg = mhi_foxconn_sdx55_events,
};
+static const struct mhi_controller_config modem_foxconn_sdx61_config = {
+ .max_channels = 128,
+ .timeout_ms = 20000,
+ .num_channels = ARRAY_SIZE(mhi_foxconn_sdx61_channels),
+ .ch_cfg = mhi_foxconn_sdx61_channels,
+ .num_events = ARRAY_SIZE(mhi_foxconn_sdx55_events),
+ .event_cfg = mhi_foxconn_sdx55_events,
+};
+
+static const struct mhi_controller_config modem_foxconn_sdx72_config = {
+ .max_channels = 128,
+ .timeout_ms = 20000,
+ .ready_timeout_ms = 50000,
+ .num_channels = ARRAY_SIZE(mhi_foxconn_sdx55_channels),
+ .ch_cfg = mhi_foxconn_sdx55_channels,
+ .num_events = ARRAY_SIZE(mhi_foxconn_sdx55_events),
+ .event_cfg = mhi_foxconn_sdx55_events,
+};
+
static const struct mhi_pci_dev_info mhi_foxconn_sdx55_info = {
.name = "foxconn-sdx55",
- .fw = "qcom/sdx55m/sbl1.mbn",
- .edl = "qcom/sdx55m/edl.mbn",
+ .edl = "qcom/sdx55m/foxconn/prog_firehose_sdx55.mbn",
+ .edl_trigger = true,
.config = &modem_foxconn_sdx55_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -371,8 +564,10 @@ static const struct mhi_pci_dev_info mhi_foxconn_sdx55_info = {
.sideband_wake = false,
};
-static const struct mhi_pci_dev_info mhi_foxconn_sdx65_info = {
- .name = "foxconn-sdx65",
+static const struct mhi_pci_dev_info mhi_foxconn_t99w175_info = {
+ .name = "foxconn-t99w175",
+ .edl = "qcom/sdx55m/foxconn/prog_firehose_sdx55.mbn",
+ .edl_trigger = true,
.config = &modem_foxconn_sdx55_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
@@ -380,6 +575,105 @@ static const struct mhi_pci_dev_info mhi_foxconn_sdx65_info = {
.sideband_wake = false,
};
+static const struct mhi_pci_dev_info mhi_foxconn_dw5930e_info = {
+ .name = "foxconn-dw5930e",
+ .edl = "qcom/sdx55m/foxconn/prog_firehose_sdx55.mbn",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx55_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_t99w368_info = {
+ .name = "foxconn-t99w368",
+ .edl = "qcom/sdx65m/foxconn/prog_firehose_lite.elf",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx55_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_t99w373_info = {
+ .name = "foxconn-t99w373",
+ .edl = "qcom/sdx65m/foxconn/prog_firehose_lite.elf",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx55_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_t99w510_info = {
+ .name = "foxconn-t99w510",
+ .edl = "qcom/sdx24m/foxconn/prog_firehose_sdx24.mbn",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx55_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_dw5932e_info = {
+ .name = "foxconn-dw5932e",
+ .edl = "qcom/sdx65m/foxconn/prog_firehose_lite.elf",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx55_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_t99w640_info = {
+ .name = "foxconn-t99w640",
+ .edl = "qcom/sdx72m/foxconn/edl.mbn",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx72_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_dw5934e_info = {
+ .name = "foxconn-dw5934e",
+ .edl = "qcom/sdx72m/foxconn/edl.mbn",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx72_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_t99w696_info = {
+ .name = "foxconn-t99w696",
+ .edl = "qcom/sdx61/foxconn/prog_firehose_lite.elf",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx61_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
+static const struct mhi_pci_dev_info mhi_foxconn_t99w760_info = {
+ .name = "foxconn-t99w760",
+ .edl = "qcom/sdx35/foxconn/xbl_s_devprg_ns.melf",
+ .edl_trigger = true,
+ .config = &modem_foxconn_sdx61_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = false,
+};
+
static const struct mhi_channel_config mhi_mv3x_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 64, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 64, 0),
@@ -460,6 +754,7 @@ static const struct mhi_pci_dev_info mhi_sierra_em919x_info = {
.config = &modem_sierra_em919x_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
+ .mru_default = 32768,
.sideband_wake = false,
};
@@ -478,7 +773,7 @@ static struct mhi_event_config mhi_telit_fn980_hw_v1_events[] = {
MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101)
};
-static struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
+static const struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
.max_channels = 128,
.timeout_ms = 20000,
.num_channels = ARRAY_SIZE(mhi_telit_fn980_hw_v1_channels),
@@ -538,13 +833,108 @@ static const struct mhi_pci_dev_info mhi_telit_fn990_info = {
.mru_default = 32768,
};
+static const struct mhi_pci_dev_info mhi_telit_fe990a_info = {
+ .name = "telit-fe990a",
+ .config = &modem_telit_fn990_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .sideband_wake = false,
+ .mru_default = 32768,
+};
+
+static const struct mhi_channel_config mhi_telit_fn920c04_channels[] = {
+ MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
+ MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 32, 0),
+ MHI_CHANNEL_CONFIG_UL(4, "DIAG", 64, 1),
+ MHI_CHANNEL_CONFIG_DL(5, "DIAG", 64, 1),
+ MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0),
+ MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
+ MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
+ MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
+ MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1),
+ MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1),
+ MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2),
+ MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3),
+};
+
+static const struct mhi_controller_config modem_telit_fn920c04_config = {
+ .max_channels = 128,
+ .timeout_ms = 50000,
+ .num_channels = ARRAY_SIZE(mhi_telit_fn920c04_channels),
+ .ch_cfg = mhi_telit_fn920c04_channels,
+ .num_events = ARRAY_SIZE(mhi_telit_fn990_events),
+ .event_cfg = mhi_telit_fn990_events,
+};
+
+static const struct mhi_pci_dev_info mhi_telit_fn920c04_info = {
+ .name = "telit-fn920c04",
+ .config = &modem_telit_fn920c04_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .sideband_wake = false,
+ .mru_default = 32768,
+ .edl_trigger = true,
+};
+
+static const struct mhi_pci_dev_info mhi_telit_fn990b40_info = {
+ .name = "telit-fn990b40",
+ .config = &modem_telit_fn920c04_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .sideband_wake = false,
+ .mru_default = 32768,
+ .edl_trigger = true,
+};
+
+static const struct mhi_pci_dev_info mhi_telit_fe990b40_info = {
+ .name = "telit-fe990b40",
+ .config = &modem_telit_fn920c04_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .sideband_wake = false,
+ .mru_default = 32768,
+ .edl_trigger = true,
+};
+
+static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = {
+ .name = "netprisma-lcur57",
+ .edl = "qcom/prog_firehose_sdx24.mbn",
+ .config = &modem_quectel_em1xx_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = true,
+};
+
+static const struct mhi_pci_dev_info mhi_netprisma_fcun69_info = {
+ .name = "netprisma-fcun69",
+ .edl = "qcom/prog_firehose_sdx6x.elf",
+ .config = &modem_quectel_em1xx_config,
+ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
+ .dma_data_width = 32,
+ .mru_default = 32768,
+ .sideband_wake = true,
+};
+
/* Keep the list sorted based on the PID. New VID should be added as the last entry */
static const struct pci_device_id mhi_pci_id_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0116),
+ .driver_data = (kernel_ulong_t) &mhi_qcom_sa8775p_info },
+ /* Telit FN920C04 (sdx35) */
+ {PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2020),
+ .driver_data = (kernel_ulong_t) &mhi_telit_fn920c04_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304),
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx24_info },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, PCI_VENDOR_ID_QCOM, 0x010c),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info },
/* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200),
.driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info },
+ /* EM929x (sdx65), use the same configuration as EM919x */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x18d7, 0x0301),
+ .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info },
/* Telit FN980 hardware revision v1 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x1C5D, 0x2000),
.driver_data = (kernel_ulong_t) &mhi_telit_fn980_hw_v1_info },
@@ -553,47 +943,107 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* Telit FN990 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2010),
.driver_data = (kernel_ulong_t) &mhi_telit_fn990_info },
+ /* Telit FE990A */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015),
+ .driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info },
+ /* Foxconn T99W696, all variants */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, PCI_ANY_ID),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
- { PCI_DEVICE(0x1eac, 0x1001), /* EM120R-GL (sdx24) */
+ /* Telit FN990B40 (sdx72) */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0309, 0x1c5d, 0x201a),
+ .driver_data = (kernel_ulong_t) &mhi_telit_fn990b40_info },
+ /* Telit FE990B40 (sdx72) */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0309, 0x1c5d, 0x2025),
+ .driver_data = (kernel_ulong_t) &mhi_telit_fe990b40_info },
+ { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309),
+ .driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info },
+ /* QDU100, x100-DU */
+ { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0601),
+ .driver_data = (kernel_ulong_t) &mhi_qcom_qdu100_info },
+ { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */
+ .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
+ { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
- { PCI_DEVICE(0x1eac, 0x1002), /* EM160R-GL (sdx24) */
+ /* RM520N-GL (sdx6x), eSIM */
+ { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1004),
+ .driver_data = (kernel_ulong_t) &mhi_quectel_rm5xx_info },
+ /* RM520N-GL (sdx6x), Lenovo variant */
+ { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1007),
+ .driver_data = (kernel_ulong_t) &mhi_quectel_rm5xx_info },
+ { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x100d), /* EM160R-GL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
- { PCI_DEVICE(0x1eac, 0x2001), /* EM120R-GL for FCCL (sdx24) */
+ { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x2001), /* EM120R-GL for FCCL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
/* T99W175 (sdx55), Both for eSIM and Non-eSIM */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0ab),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w175_info },
/* DW5930e (sdx55), With eSIM, It's also T99W175 */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0b0),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5930e_info },
/* DW5930e (sdx55), Non-eSIM, It's also T99W175 */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0b1),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5930e_info },
/* T99W175 (sdx55), Based on Qualcomm new baseline */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0bf),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w175_info },
/* T99W175 (sdx55) */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0c3),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w175_info },
/* T99W368 (sdx65) */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0d8),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w368_info },
/* T99W373 (sdx62) */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0d9),
- .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w373_info },
+ /* T99W510 (sdx24), variant 1 */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f0),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w510_info },
+ /* T99W510 (sdx24), variant 2 */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f1),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w510_info },
+ /* T99W510 (sdx24), variant 3 */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f2),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w510_info },
+ /* DW5932e-eSIM (sdx62), With eSIM */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f5),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5932e_info },
+ /* DW5932e (sdx62), Non-eSIM */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f9),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5932e_info },
+ /* T99W640 (sdx72) */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe118),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w640_info },
+ /* DW5934e(sdx72), With eSIM */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe11d),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5934e_info },
+ /* DW5934e(sdx72), Non-eSIM */
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe11e),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5934e_info },
+ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe123),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w760_info },
/* MV31-W (Cinterion) */
- { PCI_DEVICE(0x1269, 0x00b3),
+ { PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00b3),
.driver_data = (kernel_ulong_t) &mhi_mv31_info },
/* MV31-W (Cinterion), based on new baseline */
- { PCI_DEVICE(0x1269, 0x00b4),
+ { PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00b4),
.driver_data = (kernel_ulong_t) &mhi_mv31_info },
/* MV32-WA (Cinterion) */
- { PCI_DEVICE(0x1269, 0x00ba),
+ { PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00ba),
.driver_data = (kernel_ulong_t) &mhi_mv32_info },
/* MV32-WB (Cinterion) */
- { PCI_DEVICE(0x1269, 0x00bb),
+ { PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00bb),
.driver_data = (kernel_ulong_t) &mhi_mv32_info },
+ /* T99W175 (sdx55), HP variant */
+ { PCI_DEVICE(0x03f0, 0x0a6c),
+ .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w175_info },
+ /* NETPRISMA LCUR57 (SDX24) */
+ { PCI_DEVICE(PCI_VENDOR_ID_NETPRISMA, 0x1000),
+ .driver_data = (kernel_ulong_t) &mhi_netprisma_lcur57_info },
+ /* NETPRISMA FCUN69 (SDX6X) */
+ { PCI_DEVICE(PCI_VENDOR_ID_NETPRISMA, 0x1001),
+ .driver_data = (kernel_ulong_t) &mhi_netprisma_fcun69_info },
{ }
};
MODULE_DEVICE_TABLE(pci, mhi_pci_id_table);
@@ -609,6 +1059,7 @@ struct mhi_pci_device {
struct work_struct recovery_work;
struct timer_list health_check_timer;
unsigned long status;
+ bool reset_on_remove;
};
static int mhi_pci_read_reg(struct mhi_controller *mhi_cntrl,
@@ -664,7 +1115,7 @@ static bool mhi_pci_is_alive(struct mhi_controller *mhi_cntrl)
struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
u16 vendor = 0;
- if (pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor))
+ if (pci_read_config_word(pci_physfn(pdev), PCI_VENDOR_ID, &vendor))
return false;
if (vendor == (u16) ~0 || vendor == 0)
@@ -679,22 +1130,18 @@ static int mhi_pci_claim(struct mhi_controller *mhi_cntrl,
struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
int err;
- err = pci_assign_resource(pdev, bar_num);
- if (err)
- return err;
-
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "failed to enable pci device: %d\n", err);
return err;
}
- err = pcim_iomap_regions(pdev, 1 << bar_num, pci_name(pdev));
- if (err) {
+ mhi_cntrl->regs = pcim_iomap_region(pdev, bar_num, pci_name(pdev));
+ if (IS_ERR(mhi_cntrl->regs)) {
+ err = PTR_ERR(mhi_cntrl->regs);
dev_err(&pdev->dev, "failed to map pci region: %d\n", err);
return err;
}
- mhi_cntrl->regs = pcim_iomap_table(pdev)[bar_num];
mhi_cntrl->reg_len = pci_resource_len(pdev, bar_num);
err = dma_set_mask_and_coherent(&pdev->dev, dma_mask);
@@ -721,7 +1168,7 @@ static int mhi_pci_get_irqs(struct mhi_controller *mhi_cntrl,
*/
mhi_cntrl->nr_irqs = 1 + mhi_cntrl_config->num_events;
- nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSI);
+ nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSIX | PCI_IRQ_MSI);
if (nr_vectors < 0) {
dev_err(&pdev->dev, "Error allocating MSI vectors %d\n",
nr_vectors);
@@ -779,7 +1226,9 @@ static void mhi_pci_recovery_work(struct work_struct *work)
dev_warn(&pdev->dev, "device recovery started\n");
- del_timer(&mhi_pdev->health_check_timer);
+ if (pdev->is_physfn)
+ timer_delete(&mhi_pdev->health_check_timer);
+
pm_runtime_forbid(&pdev->dev);
/* Clean up MHI state */
@@ -806,19 +1255,24 @@ static void mhi_pci_recovery_work(struct work_struct *work)
dev_dbg(&pdev->dev, "Recovery completed\n");
set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
- mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
+
+ if (pdev->is_physfn)
+ mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
+
return;
err_unprepare:
mhi_unprepare_after_power_down(mhi_cntrl);
err_try_reset:
- if (pci_reset_function(pdev))
- dev_err(&pdev->dev, "Recovery failed\n");
+ err = pci_try_reset_function(pdev);
+ if (err)
+ dev_err(&pdev->dev, "Recovery failed: %d\n", err);
}
static void health_check(struct timer_list *t)
{
- struct mhi_pci_device *mhi_pdev = from_timer(mhi_pdev, t, health_check_timer);
+ struct mhi_pci_device *mhi_pdev = timer_container_of(mhi_pdev, t,
+ health_check_timer);
struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
if (!test_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status) ||
@@ -835,15 +1289,50 @@ static void health_check(struct timer_list *t)
mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
}
+static int mhi_pci_generic_edl_trigger(struct mhi_controller *mhi_cntrl)
+{
+ void __iomem *base = mhi_cntrl->regs;
+ void __iomem *edl_db;
+ int ret;
+ u32 val;
+
+ ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
+ if (ret) {
+ dev_err(mhi_cntrl->cntrl_dev, "Failed to wakeup the device\n");
+ return ret;
+ }
+
+ pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
+ mhi_cntrl->runtime_get(mhi_cntrl);
+
+ ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val);
+ if (ret)
+ goto err_get_chdb;
+
+ edl_db = base + val + (8 * MHI_EDL_DB);
+
+ mhi_cntrl->write_reg(mhi_cntrl, edl_db + 4, upper_32_bits(MHI_EDL_COOKIE));
+ mhi_cntrl->write_reg(mhi_cntrl, edl_db, lower_32_bits(MHI_EDL_COOKIE));
+
+ mhi_soc_reset(mhi_cntrl);
+
+err_get_chdb:
+ mhi_cntrl->runtime_put(mhi_cntrl);
+ mhi_device_put(mhi_cntrl->mhi_dev);
+
+ return ret;
+}
+
static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
const struct mhi_pci_dev_info *info = (struct mhi_pci_dev_info *) id->driver_data;
const struct mhi_controller_config *mhi_cntrl_config;
struct mhi_pci_device *mhi_pdev;
struct mhi_controller *mhi_cntrl;
+ unsigned int dma_data_width;
int err;
- dev_dbg(&pdev->dev, "MHI PCI device found: %s\n", info->name);
+ dev_info(&pdev->dev, "MHI PCI device found: %s\n", info->name);
/* mhi_pdev.mhi_cntrl must be zero-initialized */
mhi_pdev = devm_kzalloc(&pdev->dev, sizeof(*mhi_pdev), GFP_KERNEL);
@@ -851,14 +1340,24 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENOMEM;
INIT_WORK(&mhi_pdev->recovery_work, mhi_pci_recovery_work);
- timer_setup(&mhi_pdev->health_check_timer, health_check, 0);
- mhi_cntrl_config = info->config;
+ if (pdev->is_virtfn && info->vf_config)
+ mhi_cntrl_config = info->vf_config;
+ else
+ mhi_cntrl_config = info->config;
+
+ /* Initialize health check monitor only for Physical functions */
+ if (pdev->is_physfn)
+ timer_setup(&mhi_pdev->health_check_timer, health_check, 0);
+
mhi_cntrl = &mhi_pdev->mhi_cntrl;
+ dma_data_width = (pdev->is_virtfn && info->vf_dma_data_width) ?
+ info->vf_dma_data_width : info->dma_data_width;
+
mhi_cntrl->cntrl_dev = &pdev->dev;
mhi_cntrl->iova_start = 0;
- mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(info->dma_data_width);
+ mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width);
mhi_cntrl->fw_image = info->fw;
mhi_cntrl->edl_image = info->edl;
@@ -868,6 +1367,13 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_cntrl->runtime_get = mhi_pci_runtime_get;
mhi_cntrl->runtime_put = mhi_pci_runtime_put;
mhi_cntrl->mru = info->mru_default;
+ mhi_cntrl->name = info->name;
+
+ if (pdev->is_physfn)
+ mhi_pdev->reset_on_remove = info->reset_on_remove;
+
+ if (info->edl_trigger)
+ mhi_cntrl->edl_trigger = mhi_pci_generic_edl_trigger;
if (info->sideband_wake) {
mhi_cntrl->wake_get = mhi_pci_wake_get_nop;
@@ -875,7 +1381,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_cntrl->wake_toggle = mhi_pci_wake_toggle_nop;
}
- err = mhi_pci_claim(mhi_cntrl, info->bar_num, DMA_BIT_MASK(info->dma_data_width));
+ err = mhi_pci_claim(mhi_cntrl, info->bar_num, DMA_BIT_MASK(dma_data_width));
if (err)
return err;
@@ -892,11 +1398,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_pdev->pci_state = pci_store_saved_state(pdev);
pci_load_saved_state(pdev, NULL);
- pci_enable_pcie_error_reporting(pdev);
-
err = mhi_register_controller(mhi_cntrl, mhi_cntrl_config);
if (err)
- goto err_disable_reporting;
+ return err;
/* MHI bus does not power up the controller by default */
err = mhi_prepare_for_power_up(mhi_cntrl);
@@ -914,10 +1418,11 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
/* start health check */
- mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
+ if (pdev->is_physfn)
+ mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
- /* Only allow runtime-suspend if PME capable (for wakeup) */
- if (pci_pme_capable(pdev, PCI_D3hot)) {
+ /* Allow runtime suspend only if both PME from D3Hot and M3 are supported */
+ if (pci_pme_capable(pdev, PCI_D3hot) && !(info->no_m3)) {
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_mark_last_busy(&pdev->dev);
@@ -930,8 +1435,6 @@ err_unprepare:
mhi_unprepare_after_power_down(mhi_cntrl);
err_unregister:
mhi_unregister_controller(mhi_cntrl);
-err_disable_reporting:
- pci_disable_pcie_error_reporting(pdev);
return err;
}
@@ -941,7 +1444,10 @@ static void mhi_pci_remove(struct pci_dev *pdev)
struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;
- del_timer_sync(&mhi_pdev->health_check_timer);
+ pci_disable_sriov(pdev);
+
+ if (pdev->is_physfn)
+ timer_delete_sync(&mhi_pdev->health_check_timer);
cancel_work_sync(&mhi_pdev->recovery_work);
if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
@@ -953,8 +1459,10 @@ static void mhi_pci_remove(struct pci_dev *pdev)
if (pci_pme_capable(pdev, PCI_D3hot))
pm_runtime_get_noresume(&pdev->dev);
+ if (mhi_pdev->reset_on_remove)
+ mhi_soc_reset(mhi_cntrl);
+
mhi_unregister_controller(mhi_cntrl);
- pci_disable_pcie_error_reporting(pdev);
}
static void mhi_pci_shutdown(struct pci_dev *pdev)
@@ -970,7 +1478,8 @@ static void mhi_pci_reset_prepare(struct pci_dev *pdev)
dev_info(&pdev->dev, "reset\n");
- del_timer(&mhi_pdev->health_check_timer);
+ if (pdev->is_physfn)
+ timer_delete(&mhi_pdev->health_check_timer);
/* Clean up MHI state */
if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) {
@@ -1015,7 +1524,8 @@ static void mhi_pci_reset_done(struct pci_dev *pdev)
}
set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
- mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
+ if (pdev->is_physfn)
+ mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
}
static pci_ers_result_t mhi_pci_error_detected(struct pci_dev *pdev,
@@ -1080,7 +1590,9 @@ static int __maybe_unused mhi_pci_runtime_suspend(struct device *dev)
if (test_and_set_bit(MHI_PCI_DEV_SUSPENDED, &mhi_pdev->status))
return 0;
- del_timer(&mhi_pdev->health_check_timer);
+ if (pdev->is_physfn)
+ timer_delete(&mhi_pdev->health_check_timer);
+
cancel_work_sync(&mhi_pdev->recovery_work);
if (!test_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status) ||
@@ -1131,7 +1643,8 @@ static int __maybe_unused mhi_pci_runtime_resume(struct device *dev)
}
/* Resume health check */
- mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
+ if (pdev->is_physfn)
+ mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
/* It can be a remote wakeup (no mhi runtime_get), update access time */
pm_runtime_mark_last_busy(dev);
@@ -1217,7 +1730,8 @@ static struct pci_driver mhi_pci_driver = {
.remove = mhi_pci_remove,
.shutdown = mhi_pci_shutdown,
.err_handler = &mhi_pci_err_handler,
- .driver.pm = &mhi_pci_pm_ops
+ .driver.pm = &mhi_pci_pm_ops,
+ .sriov_configure = pci_sriov_configure_simple,
};
module_pci_driver(mhi_pci_driver);
diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c
index 4a42186ff111..b4ef115189b5 100644
--- a/drivers/bus/mhi/host/pm.c
+++ b/drivers/bus/mhi/host/pm.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/wait.h>
#include "internal.h"
+#include "trace.h"
/*
* Not all MHI state transitions are synchronous. Transitions like Linkdown,
@@ -36,7 +37,10 @@
* M0 <--> M0
* M0 -> FW_DL_ERR
* M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0
- * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
+ * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS
+ * SYS_ERR_PROCESS -> SYS_ERR_FAIL
+ * SYS_ERR_FAIL -> SYS_ERR_DETECT
+ * SYS_ERR_PROCESS --> POR
* L2: SHUTDOWN_PROCESS -> LD_ERR_FATAL_DETECT
* SHUTDOWN_PROCESS -> DISABLE
* L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT
@@ -93,7 +97,12 @@ static const struct mhi_pm_transitions dev_state_transitions[] = {
},
{
MHI_PM_SYS_ERR_PROCESS,
- MHI_PM_POR | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_POR | MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_SYS_ERR_FAIL,
+ MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
MHI_PM_LD_ERR_FATAL_DETECT
},
/* L2 States */
@@ -123,6 +132,7 @@ enum mhi_pm_state __must_check mhi_tryset_pm_state(struct mhi_controller *mhi_cn
if (unlikely(!(dev_state_transitions[index].to_states & state)))
return cur_state;
+ trace_mhi_tryset_pm_state(mhi_cntrl, state);
mhi_cntrl->pm_state = state;
return mhi_cntrl->pm_state;
}
@@ -163,6 +173,7 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
enum mhi_pm_state cur_state;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 interval_us = 25000; /* poll register field every 25 milliseconds */
+ u32 timeout_ms;
int ret, i;
/* Check if device entered error state */
@@ -173,14 +184,18 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
/* Wait for RESET to be cleared and READY bit to be set by the device */
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, 0, interval_us);
+ MHICTRL_RESET_MASK, 0, interval_us,
+ mhi_cntrl->timeout_ms);
if (ret) {
dev_err(dev, "Device failed to clear MHI Reset\n");
return ret;
}
+ timeout_ms = mhi_cntrl->ready_timeout_ms ?
+ mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms;
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
- MHISTATUS_READY_MASK, 1, interval_us);
+ MHISTATUS_READY_MASK, 1, interval_us,
+ timeout_ms);
if (ret) {
dev_err(dev, "Device failed to enter MHI Ready\n");
return ret;
@@ -301,7 +316,8 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl)
read_lock_irq(&mhi_chan->lock);
/* Only ring DB if ring is not empty */
- if (tre_ring->base && tre_ring->wp != tre_ring->rp)
+ if (tre_ring->base && tre_ring->wp != tre_ring->rp &&
+ mhi_chan->ch_state == MHI_CH_STATE_ENABLED)
mhi_ring_chan_db(mhi_cntrl, mhi_chan);
read_unlock_irq(&mhi_chan->lock);
}
@@ -402,6 +418,7 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
device_for_each_child(&mhi_cntrl->mhi_dev->dev, &current_ee,
mhi_destroy_device);
mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_MISSION_MODE);
+ mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
/* Force MHI to be in M0 state before continuing */
ret = __mhi_device_get_sync(mhi_cntrl);
@@ -452,7 +469,8 @@ error_mission_mode:
}
/* Handle shutdown transitions */
-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
+static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
+ bool destroy_device)
{
enum mhi_pm_state cur_state;
struct mhi_event *mhi_event;
@@ -469,12 +487,16 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
/* Trigger MHI RESET so that the device will not access host memory */
if (!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
+ /* Skip MHI RESET if in RDDM state */
+ if (mhi_cntrl->rddm_image && mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM)
+ goto skip_mhi_reset;
+
dev_dbg(dev, "Triggering MHI Reset in device\n");
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
/* Wait for the reset bit to be cleared by the device */
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, 0, 25000);
+ MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms);
if (ret)
dev_err(dev, "Device failed to clear MHI Reset\n");
@@ -487,13 +509,14 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
if (!MHI_IN_PBL(mhi_get_exec_env(mhi_cntrl))) {
/* wait for ready to be set */
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs,
- MHISTATUS,
- MHISTATUS_READY_MASK, 1, 25000);
+ MHISTATUS, MHISTATUS_READY_MASK,
+ 1, 25000, mhi_cntrl->timeout_ms);
if (ret)
dev_err(dev, "Device failed to enter READY state\n");
}
}
+skip_mhi_reset:
dev_dbg(dev,
"Waiting for all pending event ring processing to complete\n");
mhi_event = mhi_cntrl->mhi_event;
@@ -509,8 +532,16 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
dev_dbg(dev, "Waiting for all pending threads to complete\n");
wake_up_all(&mhi_cntrl->state_event);
- dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
- device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+ /*
+ * Only destroy the 'struct device' for channels if indicated by the
+ * 'destroy_device' flag. Because, during system suspend or hibernation
+ * state, there is no need to destroy the 'struct device' as the endpoint
+ * device would still be physically attached to the machine.
+ */
+ if (destroy_device) {
+ dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+ device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
+ }
mutex_lock(&mhi_cntrl->pm_mutex);
@@ -572,6 +603,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
struct mhi_cmd *mhi_cmd;
struct mhi_event_ctxt *er_ctxt;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ bool reset_device = false;
int ret, i;
dev_dbg(dev, "Transitioning from PM state: %s to: %s\n",
@@ -600,8 +632,25 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
/* Wake up threads waiting for state transition */
wake_up_all(&mhi_cntrl->state_event);
- /* Trigger MHI RESET so that the device will not access host memory */
+ mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
+
if (MHI_REG_ACCESS_VALID(prev_state)) {
+ /*
+ * If the device is in PBL or SBL, it will only respond to
+ * RESET if the device is in SYSERR state. SYSERR might
+ * already be cleared at this point.
+ */
+ enum mhi_state cur_state = mhi_get_mhi_state(mhi_cntrl);
+ enum mhi_ee_type cur_ee = mhi_get_exec_env(mhi_cntrl);
+
+ if (cur_state == MHI_STATE_SYS_ERR)
+ reset_device = true;
+ else if (cur_ee != MHI_EE_PBL && cur_ee != MHI_EE_SBL)
+ reset_device = true;
+ }
+
+ /* Trigger MHI RESET so that the device will not access host memory */
+ if (reset_device) {
u32 in_reset = -1;
unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms);
@@ -618,7 +667,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
!in_reset, timeout);
if (!ret || in_reset) {
dev_err(dev, "Device failed to exit MHI Reset state\n");
- goto exit_sys_error_transition;
+ write_lock_irq(&mhi_cntrl->pm_lock);
+ cur_state = mhi_tryset_pm_state(mhi_cntrl,
+ MHI_PM_SYS_ERR_FAIL);
+ write_unlock_irq(&mhi_cntrl->pm_lock);
+ /* Shutdown may have occurred, otherwise cleanup now */
+ if (cur_state != MHI_PM_SYS_ERR_FAIL)
+ goto exit_sys_error_transition;
}
/*
@@ -747,7 +802,6 @@ void mhi_pm_st_worker(struct work_struct *work)
struct mhi_controller *mhi_cntrl = container_of(work,
struct mhi_controller,
st_worker);
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
spin_lock_irq(&mhi_cntrl->transition_lock);
list_splice_tail_init(&mhi_cntrl->transition_list, &head);
@@ -755,8 +809,8 @@ void mhi_pm_st_worker(struct work_struct *work)
list_for_each_entry_safe(itr, tmp, &head, node) {
list_del(&itr->node);
- dev_dbg(dev, "Handling state transition: %s\n",
- TO_DEV_STATE_TRANS_STR(itr->state));
+
+ trace_mhi_pm_st_transition(mhi_cntrl, itr->state);
switch (itr->state) {
case DEV_ST_TRANSITION_PBL:
@@ -778,6 +832,8 @@ void mhi_pm_st_worker(struct work_struct *work)
mhi_create_devices(mhi_cntrl);
if (mhi_cntrl->fbc_download)
mhi_download_amss_image(mhi_cntrl);
+
+ mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
break;
case DEV_ST_TRANSITION_MISSION_MODE:
mhi_pm_mission_mode_transition(mhi_cntrl);
@@ -787,6 +843,7 @@ void mhi_pm_st_worker(struct work_struct *work)
mhi_cntrl->ee = MHI_EE_FP;
write_unlock_irq(&mhi_cntrl->pm_lock);
mhi_create_devices(mhi_cntrl);
+ mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
break;
case DEV_ST_TRANSITION_READY:
mhi_ready_state_transition(mhi_cntrl);
@@ -795,7 +852,10 @@ void mhi_pm_st_worker(struct work_struct *work)
mhi_pm_sys_error_transition(mhi_cntrl);
break;
case DEV_ST_TRANSITION_DISABLE:
- mhi_pm_disable_transition(mhi_cntrl);
+ mhi_pm_disable_transition(mhi_cntrl, false);
+ break;
+ case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
+ mhi_pm_disable_transition(mhi_cntrl, true);
break;
default:
break;
@@ -1105,7 +1165,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
if (state == MHI_STATE_SYS_ERR) {
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
- MHICTRL_RESET_MASK, 0, interval_us);
+ MHICTRL_RESET_MASK, 0, interval_us,
+ mhi_cntrl->timeout_ms);
if (ret) {
dev_info(dev, "Failed to reset MHI due to syserr state\n");
goto error_exit;
@@ -1148,7 +1209,8 @@ error_exit:
}
EXPORT_SYMBOL_GPL(mhi_async_power_up);
-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+static void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
+ bool destroy_device)
{
enum mhi_pm_state cur_state, transition_state;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
@@ -1184,26 +1246,49 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
write_unlock_irq(&mhi_cntrl->pm_lock);
mutex_unlock(&mhi_cntrl->pm_mutex);
- mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
+ mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
+
+ if (destroy_device)
+ mhi_queue_state_transition(mhi_cntrl,
+ DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
+ else
+ mhi_queue_state_transition(mhi_cntrl,
+ DEV_ST_TRANSITION_DISABLE);
/* Wait for shutdown to complete */
flush_work(&mhi_cntrl->st_worker);
disable_irq(mhi_cntrl->irq[0]);
}
+
+void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
+{
+ __mhi_power_down(mhi_cntrl, graceful, true);
+}
EXPORT_SYMBOL_GPL(mhi_power_down);
+void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl,
+ bool graceful)
+{
+ __mhi_power_down(mhi_cntrl, graceful, false);
+}
+EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev);
+
int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
{
int ret = mhi_async_power_up(mhi_cntrl);
+ u32 timeout_ms;
if (ret)
return ret;
+ /* Some devices need more time to set ready during power up */
+ timeout_ms = mhi_cntrl->ready_timeout_ms ?
+ mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms;
wait_event_timeout(mhi_cntrl->state_event,
MHI_IN_MISSION_MODE(mhi_cntrl->ee) ||
- MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
+ MHI_PM_FATAL_ERROR(mhi_cntrl->pm_state),
+ msecs_to_jiffies(timeout_ms));
ret = (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -ETIMEDOUT;
if (ret)
@@ -1235,20 +1320,6 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl)
}
EXPORT_SYMBOL_GPL(mhi_force_rddm_mode);
-void mhi_device_get(struct mhi_device *mhi_dev)
-{
- struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
-
- mhi_dev->dev_wake++;
- read_lock_bh(&mhi_cntrl->pm_lock);
- if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
- mhi_trigger_resume(mhi_cntrl);
-
- mhi_cntrl->wake_get(mhi_cntrl, true);
- read_unlock_bh(&mhi_cntrl->pm_lock);
-}
-EXPORT_SYMBOL_GPL(mhi_device_get);
-
int mhi_device_get_sync(struct mhi_device *mhi_dev)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
@@ -1275,3 +1346,22 @@ void mhi_device_put(struct mhi_device *mhi_dev)
read_unlock_bh(&mhi_cntrl->pm_lock);
}
EXPORT_SYMBOL_GPL(mhi_device_put);
+
+void mhi_uevent_notify(struct mhi_controller *mhi_cntrl, enum mhi_ee_type ee)
+{
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ char *buf[2];
+ int ret;
+
+ buf[0] = kasprintf(GFP_KERNEL, "EXEC_ENV=%s", TO_MHI_EXEC_STR(ee));
+ buf[1] = NULL;
+
+ if (!buf[0])
+ return;
+
+ ret = kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, buf);
+ if (ret)
+ dev_err(dev, "Failed to send %s uevent\n", TO_MHI_EXEC_STR(ee));
+
+ kfree(buf[0]);
+}
diff --git a/drivers/bus/mhi/host/trace.h b/drivers/bus/mhi/host/trace.h
new file mode 100644
index 000000000000..3e0c41777429
--- /dev/null
+++ b/drivers/bus/mhi/host/trace.h
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mhi_host
+
+#if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_EVENT_MHI_HOST_H
+
+#include <linux/byteorder/generic.h>
+#include <linux/tracepoint.h>
+#include <linux/trace_seq.h>
+#include "../common.h"
+#include "internal.h"
+
+#undef mhi_state
+#undef mhi_state_end
+
+#define mhi_state(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
+#define mhi_state_end(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
+
+MHI_STATE_LIST
+
+#undef mhi_state
+#undef mhi_state_end
+
+#define mhi_state(a, b) { MHI_STATE_##a, b },
+#define mhi_state_end(a, b) { MHI_STATE_##a, b }
+
+#undef mhi_pm_state
+#undef mhi_pm_state_end
+
+#define mhi_pm_state(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
+#define mhi_pm_state_end(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
+
+MHI_PM_STATE_LIST
+
+#undef mhi_pm_state
+#undef mhi_pm_state_end
+
+#define mhi_pm_state(a, b) { MHI_PM_STATE_##a, b },
+#define mhi_pm_state_end(a, b) { MHI_PM_STATE_##a, b }
+
+#undef mhi_ee
+#undef mhi_ee_end
+
+#define mhi_ee(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
+#define mhi_ee_end(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
+
+MHI_EE_LIST
+
+#undef mhi_ee
+#undef mhi_ee_end
+
+#define mhi_ee(a, b) { MHI_EE_##a, b },
+#define mhi_ee_end(a, b) { MHI_EE_##a, b }
+
+#undef ch_state_type
+#undef ch_state_type_end
+
+#define ch_state_type(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
+#define ch_state_type_end(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
+
+MHI_CH_STATE_TYPE_LIST
+
+#undef ch_state_type
+#undef ch_state_type_end
+
+#define ch_state_type(a, b) { MHI_CH_STATE_TYPE_##a, b },
+#define ch_state_type_end(a, b) { MHI_CH_STATE_TYPE_##a, b }
+
+#undef dev_st_trans
+#undef dev_st_trans_end
+
+#define dev_st_trans(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
+#define dev_st_trans_end(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
+
+DEV_ST_TRANSITION_LIST
+
+#undef dev_st_trans
+#undef dev_st_trans_end
+
+#define dev_st_trans(a, b) { DEV_ST_TRANSITION_##a, b },
+#define dev_st_trans_end(a, b) { DEV_ST_TRANSITION_##a, b }
+
+#define TPS(x) tracepoint_string(x)
+
+TRACE_EVENT(mhi_gen_tre,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
+ struct mhi_ring_element *mhi_tre),
+
+ TP_ARGS(mhi_cntrl, mhi_chan, mhi_tre),
+
+ TP_STRUCT__entry(
+ __string(name, mhi_cntrl->mhi_dev->name)
+ __field(int, ch_num)
+ __field(void *, wp)
+ __field(uint64_t, tre_ptr)
+ __field(uint32_t, dword0)
+ __field(uint32_t, dword1)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->ch_num = mhi_chan->chan;
+ __entry->wp = mhi_tre;
+ __entry->tre_ptr = le64_to_cpu(mhi_tre->ptr);
+ __entry->dword0 = le32_to_cpu(mhi_tre->dword[0]);
+ __entry->dword1 = le32_to_cpu(mhi_tre->dword[1]);
+ ),
+
+ TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n",
+ __get_str(name), __entry->ch_num, __entry->wp, __entry->tre_ptr,
+ __entry->dword0, __entry->dword1)
+);
+
+TRACE_EVENT(mhi_intvec_states,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, int dev_ee, int dev_state),
+
+ TP_ARGS(mhi_cntrl, dev_ee, dev_state),
+
+ TP_STRUCT__entry(
+ __string(name, mhi_cntrl->mhi_dev->name)
+ __field(int, local_ee)
+ __field(int, state)
+ __field(int, dev_ee)
+ __field(int, dev_state)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->local_ee = mhi_cntrl->ee;
+ __entry->state = mhi_cntrl->dev_state;
+ __entry->dev_ee = dev_ee;
+ __entry->dev_state = dev_state;
+ ),
+
+ TP_printk("%s: Local EE: %s State: %s Device EE: %s Dev State: %s\n",
+ __get_str(name),
+ __print_symbolic(__entry->local_ee, MHI_EE_LIST),
+ __print_symbolic(__entry->state, MHI_STATE_LIST),
+ __print_symbolic(__entry->dev_ee, MHI_EE_LIST),
+ __print_symbolic(__entry->dev_state, MHI_STATE_LIST))
+);
+
+TRACE_EVENT(mhi_tryset_pm_state,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, int pm_state),
+
+ TP_ARGS(mhi_cntrl, pm_state),
+
+ TP_STRUCT__entry(
+ __string(name, mhi_cntrl->mhi_dev->name)
+ __field(int, pm_state)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ if (pm_state)
+ pm_state = __fls(pm_state);
+ __entry->pm_state = pm_state;
+ ),
+
+ TP_printk("%s: PM state: %s\n", __get_str(name),
+ __print_symbolic(__entry->pm_state, MHI_PM_STATE_LIST))
+);
+
+DECLARE_EVENT_CLASS(mhi_process_event_ring,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
+
+ TP_ARGS(mhi_cntrl, rp),
+
+ TP_STRUCT__entry(
+ __string(name, mhi_cntrl->mhi_dev->name)
+ __field(uint32_t, dword0)
+ __field(uint32_t, dword1)
+ __field(int, state)
+ __field(uint64_t, ptr)
+ __field(void *, rp)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->rp = rp;
+ __entry->ptr = le64_to_cpu(rp->ptr);
+ __entry->dword0 = le32_to_cpu(rp->dword[0]);
+ __entry->dword1 = le32_to_cpu(rp->dword[1]);
+ __entry->state = MHI_TRE_GET_EV_STATE(rp);
+ ),
+
+ TP_printk("%s: TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x State: %s\n",
+ __get_str(name), __entry->rp, __entry->ptr, __entry->dword0,
+ __entry->dword1, __print_symbolic(__entry->state, MHI_STATE_LIST))
+);
+
+DEFINE_EVENT(mhi_process_event_ring, mhi_data_event,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
+
+ TP_ARGS(mhi_cntrl, rp)
+);
+
+DEFINE_EVENT(mhi_process_event_ring, mhi_ctrl_event,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
+
+ TP_ARGS(mhi_cntrl, rp)
+);
+
+DECLARE_EVENT_CLASS(mhi_update_channel_state,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
+ const char *reason),
+
+ TP_ARGS(mhi_cntrl, mhi_chan, state, reason),
+
+ TP_STRUCT__entry(
+ __string(name, mhi_cntrl->mhi_dev->name)
+ __field(int, ch_num)
+ __field(int, state)
+ __field(const char *, reason)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->ch_num = mhi_chan->chan;
+ __entry->state = state;
+ __entry->reason = reason;
+ ),
+
+ TP_printk("%s: chan%d: %s state to: %s\n",
+ __get_str(name), __entry->ch_num, __entry->reason,
+ __print_symbolic(__entry->state, MHI_CH_STATE_TYPE_LIST))
+);
+
+DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_start,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
+ const char *reason),
+
+ TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
+);
+
+DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_end,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
+ const char *reason),
+
+ TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
+);
+
+TRACE_EVENT(mhi_pm_st_transition,
+
+ TP_PROTO(struct mhi_controller *mhi_cntrl, int state),
+
+ TP_ARGS(mhi_cntrl, state),
+
+ TP_STRUCT__entry(
+ __string(name, mhi_cntrl->mhi_dev->name)
+ __field(int, state)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->state = state;
+ ),
+
+ TP_printk("%s: Handling state transition: %s\n", __get_str(name),
+ __print_symbolic(__entry->state, DEV_ST_TRANSITION_LIST))
+);
+
+#endif
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/bus/mhi/host
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c
index fca0d0669aa9..12dd32fd0b62 100644
--- a/drivers/bus/mips_cdmm.c
+++ b/drivers/bus/mips_cdmm.c
@@ -37,7 +37,7 @@
/* Each block of device registers is 64 bytes */
#define CDMM_DRB_SIZE 64
-#define to_mips_cdmm_driver(d) container_of(d, struct mips_cdmm_driver, drv)
+#define to_mips_cdmm_driver(d) container_of_const(d, struct mips_cdmm_driver, drv)
/* Default physical base address */
static phys_addr_t mips_cdmm_default_base;
@@ -59,17 +59,17 @@ mips_cdmm_lookup(const struct mips_cdmm_device_id *table,
return ret ? table : NULL;
}
-static int mips_cdmm_match(struct device *dev, struct device_driver *drv)
+static int mips_cdmm_match(struct device *dev, const struct device_driver *drv)
{
struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
- struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
+ const struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
}
-static int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int mips_cdmm_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
+ const struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
int retval = 0;
retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
@@ -118,7 +118,7 @@ static struct attribute *mips_cdmm_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(mips_cdmm_dev);
-struct bus_type mips_cdmm_bustype = {
+const struct bus_type mips_cdmm_bustype = {
.name = "cdmm",
.dev_groups = mips_cdmm_dev_groups,
.match = mips_cdmm_match,
diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c
index 5eb0fe73ddc4..7ce61d629a87 100644
--- a/drivers/bus/moxtet.c
+++ b/drivers/bus/moxtet.c
@@ -83,10 +83,10 @@ static const struct attribute_group *moxtet_dev_groups[] = {
NULL,
};
-static int moxtet_match(struct device *dev, struct device_driver *drv)
+static int moxtet_match(struct device *dev, const struct device_driver *drv)
{
struct moxtet_device *mdev = to_moxtet_device(dev);
- struct moxtet_driver *tdrv = to_moxtet_driver(drv);
+ const struct moxtet_driver *tdrv = to_moxtet_driver(drv);
const enum turris_mox_module_id *t;
if (of_driver_match_device(dev, drv))
@@ -102,7 +102,7 @@ static int moxtet_match(struct device *dev, struct device_driver *drv)
return 0;
}
-static struct bus_type moxtet_bus_type = {
+static const struct bus_type moxtet_bus_type = {
.name = "moxtet",
.dev_groups = moxtet_dev_groups,
.match = moxtet_match,
@@ -484,7 +484,6 @@ static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = moxtet_debug_open,
.read = input_read,
- .llseek = no_llseek,
};
static ssize_t output_read(struct file *file, char __user *buf, size_t len,
@@ -549,7 +548,6 @@ static const struct file_operations output_fops = {
.open = moxtet_debug_open,
.read = output_read,
.write = output_write,
- .llseek = no_llseek,
};
static int moxtet_register_debugfs(struct moxtet *moxtet)
@@ -659,7 +657,7 @@ static void moxtet_irq_print_chip(struct irq_data *d, struct seq_file *p)
id = moxtet->modules[pos->idx];
- seq_printf(p, " moxtet-%s.%i#%i", mox_module_name(id), pos->idx,
+ seq_printf(p, "moxtet-%s.%i#%i", mox_module_name(id), pos->idx,
pos->bit);
}
@@ -739,9 +737,8 @@ static int moxtet_irq_setup(struct moxtet *moxtet)
{
int i, ret;
- moxtet->irq.domain = irq_domain_add_simple(moxtet->dev->of_node,
- MOXTET_NIRQS, 0,
- &moxtet_irq_domain, moxtet);
+ moxtet->irq.domain = irq_domain_create_simple(dev_fwnode(moxtet->dev), MOXTET_NIRQS, 0,
+ &moxtet_irq_domain, moxtet);
if (moxtet->irq.domain == NULL) {
dev_err(moxtet->dev, "Could not add IRQ domain\n");
return -ENOMEM;
@@ -755,7 +752,7 @@ static int moxtet_irq_setup(struct moxtet *moxtet)
moxtet->irq.masked = ~0;
ret = request_threaded_irq(moxtet->dev_irq, NULL, moxtet_irq_thread_fn,
- IRQF_ONESHOT, "moxtet", moxtet);
+ IRQF_SHARED | IRQF_ONESHOT, "moxtet", moxtet);
if (ret < 0)
goto err_free;
@@ -830,6 +827,12 @@ static void moxtet_remove(struct spi_device *spi)
mutex_destroy(&moxtet->lock);
}
+static const struct spi_device_id moxtet_spi_ids[] = {
+ { "moxtet" },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, moxtet_spi_ids);
+
static const struct of_device_id moxtet_dt_ids[] = {
{ .compatible = "cznic,moxtet" },
{},
@@ -841,6 +844,7 @@ static struct spi_driver moxtet_spi_driver = {
.name = "moxtet",
.of_match_table = moxtet_dt_ids,
},
+ .id_table = moxtet_spi_ids,
.probe = moxtet_probe,
.remove = moxtet_remove,
};
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 5dc2669432ba..dd94145c9b22 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -466,18 +466,7 @@ static int mvebu_sdram_debug_show(struct seq_file *seq, void *v)
struct mvebu_mbus_state *mbus = &mbus_state;
return mbus->soc->show_cpu_target(mbus, seq, v);
}
-
-static int mvebu_sdram_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mvebu_sdram_debug_show, inode->i_private);
-}
-
-static const struct file_operations mvebu_sdram_debug_fops = {
- .open = mvebu_sdram_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(mvebu_sdram_debug);
static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
{
@@ -516,18 +505,7 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int mvebu_devs_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mvebu_devs_debug_show, inode->i_private);
-}
-
-static const struct file_operations mvebu_devs_debug_fops = {
- .open = mvebu_devs_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(mvebu_devs_debug);
/*
* SoC-specific functions and definitions
@@ -1028,7 +1006,7 @@ static __init int mvebu_mbus_debugfs_init(void)
}
fs_initcall(mvebu_mbus_debugfs_init);
-static int mvebu_mbus_suspend(void)
+static int mvebu_mbus_suspend(void *data)
{
struct mvebu_mbus_state *s = &mbus_state;
int win;
@@ -1062,7 +1040,7 @@ static int mvebu_mbus_suspend(void)
return 0;
}
-static void mvebu_mbus_resume(void)
+static void mvebu_mbus_resume(void *data)
{
struct mvebu_mbus_state *s = &mbus_state;
int win;
@@ -1091,9 +1069,13 @@ static void mvebu_mbus_resume(void)
}
}
-static struct syscore_ops mvebu_mbus_syscore_ops = {
- .suspend = mvebu_mbus_suspend,
- .resume = mvebu_mbus_resume,
+static const struct syscore_ops mvebu_mbus_syscore_ops = {
+ .suspend = mvebu_mbus_suspend,
+ .resume = mvebu_mbus_resume,
+};
+
+static struct syscore mvebu_mbus_syscore = {
+ .ops = &mvebu_mbus_syscore_ops,
};
static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
@@ -1140,7 +1122,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
writel(UNIT_SYNC_BARRIER_ALL,
mbus->mbuswins_base + UNIT_SYNC_BARRIER_OFF);
- register_syscore_ops(&mvebu_mbus_syscore_ops);
+ register_syscore(&mvebu_mbus_syscore);
return 0;
}
@@ -1201,74 +1183,32 @@ static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
return 0;
}
-static int __init
-mbus_parse_ranges(struct device_node *node,
- int *addr_cells, int *c_addr_cells, int *c_size_cells,
- int *cell_count, const __be32 **ranges_start,
- const __be32 **ranges_end)
-{
- const __be32 *prop;
- int ranges_len, tuple_len;
-
- /* Allow a node with no 'ranges' property */
- *ranges_start = of_get_property(node, "ranges", &ranges_len);
- if (*ranges_start == NULL) {
- *addr_cells = *c_addr_cells = *c_size_cells = *cell_count = 0;
- *ranges_start = *ranges_end = NULL;
- return 0;
- }
- *ranges_end = *ranges_start + ranges_len / sizeof(__be32);
-
- *addr_cells = of_n_addr_cells(node);
-
- prop = of_get_property(node, "#address-cells", NULL);
- *c_addr_cells = be32_to_cpup(prop);
-
- prop = of_get_property(node, "#size-cells", NULL);
- *c_size_cells = be32_to_cpup(prop);
-
- *cell_count = *addr_cells + *c_addr_cells + *c_size_cells;
- tuple_len = (*cell_count) * sizeof(__be32);
-
- if (ranges_len % tuple_len) {
- pr_warn("malformed ranges entry '%pOFn'\n", node);
- return -EINVAL;
- }
- return 0;
-}
-
static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus,
struct device_node *np)
{
- int addr_cells, c_addr_cells, c_size_cells;
- int i, ret, cell_count;
- const __be32 *r, *ranges_start, *ranges_end;
+ int ret;
+ struct of_range_parser parser;
+ struct of_range range;
- ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells,
- &c_size_cells, &cell_count,
- &ranges_start, &ranges_end);
+ ret = of_range_parser_init(&parser, np);
if (ret < 0)
- return ret;
+ return 0;
- for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) {
- u32 windowid, base, size;
+ for_each_of_range(&parser, &range) {
+ u32 windowid = upper_32_bits(range.bus_addr);
u8 target, attr;
/*
* An entry with a non-zero custom field do not
* correspond to a static window, so skip it.
*/
- windowid = of_read_number(r, 1);
if (CUSTOM(windowid))
continue;
target = TARGET(windowid);
attr = ATTR(windowid);
- base = of_read_number(r + c_addr_cells, addr_cells);
- size = of_read_number(r + c_addr_cells + addr_cells,
- c_size_cells);
- ret = mbus_dt_setup_win(mbus, base, size, target, attr);
+ ret = mbus_dt_setup_win(mbus, range.cpu_addr, range.size, target, attr);
if (ret < 0)
return ret;
}
diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c
index e02d0656242b..e4dfda7b3b10 100644
--- a/drivers/bus/omap-ocp2scp.c
+++ b/drivers/bus/omap-ocp2scp.c
@@ -84,12 +84,10 @@ err0:
return ret;
}
-static int omap_ocp2scp_remove(struct platform_device *pdev)
+static void omap_ocp2scp_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
-
- return 0;
}
#ifdef CONFIG_OF
diff --git a/drivers/bus/omap_l3_smx.c b/drivers/bus/omap_l3_smx.c
index bb1606f5ce2d..7f0a8f8b3f4c 100644
--- a/drivers/bus/omap_l3_smx.c
+++ b/drivers/bus/omap_l3_smx.c
@@ -15,7 +15,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include "omap_l3_smx.h"
@@ -166,19 +165,10 @@ static irqreturn_t omap3_l3_app_irq(int irq, void *_l3)
irqreturn_t ret = IRQ_NONE;
int_type = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
- if (!int_type) {
+ if (!int_type)
status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_0);
- /*
- * if we have a timeout error, there's nothing we can
- * do besides rebooting the board. So let's BUG on any
- * of such errors and handle the others. timeout error
- * is severe and not expected to occur.
- */
- BUG_ON(status & L3_STATUS_0_TIMEOUT_MASK);
- } else {
+ else
status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_1);
- /* No timeout error for debug sources */
- }
/* identify the error source */
err_source = __ffs(status);
@@ -190,6 +180,14 @@ static irqreturn_t omap3_l3_app_irq(int irq, void *_l3)
ret |= omap3_l3_block_irq(l3, error, error_addr);
}
+ /*
+ * if we have a timeout error, there's nothing we can
+ * do besides rebooting the board. So let's BUG on any
+ * of such errors and handle the others. timeout error
+ * is severe and not expected to occur.
+ */
+ BUG_ON(!int_type && status & L3_STATUS_0_TIMEOUT_MASK);
+
/* Clear the status register */
clear = (L3_AGENT_STATUS_CLEAR_IA << int_type) |
L3_AGENT_STATUS_CLEAR_TA;
@@ -263,7 +261,7 @@ err0:
return ret;
}
-static int omap3_l3_remove(struct platform_device *pdev)
+static void omap3_l3_remove(struct platform_device *pdev)
{
struct omap3_l3 *l3 = platform_get_drvdata(pdev);
@@ -271,8 +269,6 @@ static int omap3_l3_remove(struct platform_device *pdev)
free_irq(l3->debug_irq, l3);
iounmap(l3->rt);
kfree(l3);
-
- return 0;
}
static struct platform_driver omap3_l3_driver = {
diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c
index 663c82749222..c1fef1b4bd89 100644
--- a/drivers/bus/qcom-ebi2.c
+++ b/drivers/bus/qcom-ebi2.c
@@ -403,4 +403,3 @@ static struct platform_driver qcom_ebi2_driver = {
module_platform_driver(qcom_ebi2_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("Qualcomm EBI2 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/bus/qcom-ssc-block-bus.c b/drivers/bus/qcom-ssc-block-bus.c
index eedeb29a5ff3..7f5fd4e0940d 100644
--- a/drivers/bus/qcom-ssc-block-bus.c
+++ b/drivers/bus/qcom-ssc-block-bus.c
@@ -264,18 +264,6 @@ static int qcom_ssc_block_bus_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- data->pd_names = qcom_ssc_block_pd_names;
- data->num_pds = ARRAY_SIZE(qcom_ssc_block_pd_names);
-
- /* power domains */
- ret = qcom_ssc_block_bus_pds_attach(&pdev->dev, data->pds, data->pd_names, data->num_pds);
- if (ret < 0)
- return dev_err_probe(&pdev->dev, ret, "error when attaching power domains\n");
-
- ret = qcom_ssc_block_bus_pds_enable(data->pds, data->num_pds);
- if (ret < 0)
- return dev_err_probe(&pdev->dev, ret, "error when enabling power domains\n");
-
/* low level overrides for when the HW logic doesn't "just work" */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config0");
data->reg_mpm_sscaon_config0 = devm_ioremap_resource(&pdev->dev, res);
@@ -343,28 +331,42 @@ static int qcom_ssc_block_bus_probe(struct platform_device *pdev)
data->ssc_axi_halt = halt_args.args[0];
+ /* power domains */
+ data->pd_names = qcom_ssc_block_pd_names;
+ data->num_pds = ARRAY_SIZE(qcom_ssc_block_pd_names);
+
+ ret = qcom_ssc_block_bus_pds_attach(&pdev->dev, data->pds, data->pd_names, data->num_pds);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "error when attaching power domains\n");
+
+ ret = qcom_ssc_block_bus_pds_enable(data->pds, data->num_pds);
+ if (ret < 0) {
+ dev_err_probe(&pdev->dev, ret, "error when enabling power domains\n");
+ goto err_detach_pds_bus;
+ }
+
qcom_ssc_block_bus_init(&pdev->dev);
of_platform_populate(np, NULL, NULL, &pdev->dev);
return 0;
+
+err_detach_pds_bus:
+ qcom_ssc_block_bus_pds_detach(&pdev->dev, data->pds, data->num_pds);
+
+ return ret;
}
-static int qcom_ssc_block_bus_remove(struct platform_device *pdev)
+static void qcom_ssc_block_bus_remove(struct platform_device *pdev)
{
struct qcom_ssc_block_bus_data *data = platform_get_drvdata(pdev);
qcom_ssc_block_bus_deinit(&pdev->dev);
- iounmap(data->reg_mpm_sscaon_config0);
- iounmap(data->reg_mpm_sscaon_config1);
-
qcom_ssc_block_bus_pds_disable(data->pds, data->num_pds);
qcom_ssc_block_bus_pds_detach(&pdev->dev, data->pds, data->num_pds);
pm_runtime_disable(&pdev->dev);
pm_clk_destroy(&pdev->dev);
-
- return 0;
}
static const struct of_device_id qcom_ssc_block_bus_of_match[] = {
@@ -386,4 +388,3 @@ module_platform_driver(qcom_ssc_block_bus_driver);
MODULE_DESCRIPTION("A driver for handling the init sequence needed for accessing the SSC block on (some) qcom SoCs over AHB");
MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c
index 6b8d6257ed8a..d8e029e7e53f 100644
--- a/drivers/bus/simple-pm-bus.c
+++ b/drivers/bus/simple-pm-bus.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Simple Power-Managed Bus Driver
*
@@ -8,17 +9,26 @@
* for more details.
*/
+#include <linux/clk.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+struct simple_pm_bus {
+ struct clk_bulk_data *clks;
+ int num_clks;
+};
+
static int simple_pm_bus_probe(struct platform_device *pdev)
{
const struct device *dev = &pdev->dev;
const struct of_dev_auxdata *lookup = dev_get_platdata(dev);
struct device_node *np = dev->of_node;
const struct of_device_id *match;
+ struct simple_pm_bus *bus;
/*
* Allow user to use driver_override to bind this driver to a
@@ -44,6 +54,16 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
return -ENODEV;
}
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->num_clks = devm_clk_bulk_get_all(&pdev->dev, &bus->clks);
+ if (bus->num_clks < 0)
+ return dev_err_probe(&pdev->dev, bus->num_clks, "failed to get clocks\n");
+
+ dev_set_drvdata(&pdev->dev, bus);
+
dev_dbg(&pdev->dev, "%s\n", __func__);
pm_runtime_enable(&pdev->dev);
@@ -54,19 +74,66 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
return 0;
}
-static int simple_pm_bus_remove(struct platform_device *pdev)
+static void simple_pm_bus_remove(struct platform_device *pdev)
{
const void *data = of_device_get_match_data(&pdev->dev);
if (pdev->driver_override || data)
- return 0;
+ return;
dev_dbg(&pdev->dev, "%s\n", __func__);
pm_runtime_disable(&pdev->dev);
+}
+
+static int simple_pm_bus_runtime_suspend(struct device *dev)
+{
+ struct simple_pm_bus *bus = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(bus->num_clks, bus->clks);
+
+ return 0;
+}
+
+static int simple_pm_bus_runtime_resume(struct device *dev)
+{
+ struct simple_pm_bus *bus = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(bus->num_clks, bus->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks: %d\n", ret);
+ return ret;
+ }
+
return 0;
}
+static int simple_pm_bus_suspend(struct device *dev)
+{
+ struct simple_pm_bus *bus = dev_get_drvdata(dev);
+
+ if (!bus)
+ return 0;
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int simple_pm_bus_resume(struct device *dev)
+{
+ struct simple_pm_bus *bus = dev_get_drvdata(dev);
+
+ if (!bus)
+ return 0;
+
+ return pm_runtime_force_resume(dev);
+}
+
+static const struct dev_pm_ops simple_pm_bus_pm_ops = {
+ RUNTIME_PM_OPS(simple_pm_bus_runtime_suspend, simple_pm_bus_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(simple_pm_bus_suspend, simple_pm_bus_resume)
+};
+
#define ONLY_BUS ((void *) 1) /* Match if the device is only a bus. */
static const struct of_device_id simple_pm_bus_of_match[] = {
@@ -85,6 +152,7 @@ static struct platform_driver simple_pm_bus_driver = {
.driver = {
.name = "simple-pm-bus",
.of_match_table = simple_pm_bus_of_match,
+ .pm = pm_ptr(&simple_pm_bus_pm_ops),
},
};
@@ -92,4 +160,3 @@ module_platform_driver(simple_pm_bus_driver);
MODULE_DESCRIPTION("Simple Power-Managed Bus Driver");
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/stm32_etzpc.c b/drivers/bus/stm32_etzpc.c
new file mode 100644
index 000000000000..7fc0f16960be
--- /dev/null
+++ b/drivers/bus/stm32_etzpc.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include "stm32_firewall.h"
+
+/*
+ * ETZPC registers
+ */
+#define ETZPC_DECPROT 0x10
+#define ETZPC_HWCFGR 0x3F0
+
+/*
+ * HWCFGR register
+ */
+#define ETZPC_HWCFGR_NUM_TZMA GENMASK(7, 0)
+#define ETZPC_HWCFGR_NUM_PER_SEC GENMASK(15, 8)
+#define ETZPC_HWCFGR_NUM_AHB_SEC GENMASK(23, 16)
+#define ETZPC_HWCFGR_CHUNKS1N4 GENMASK(31, 24)
+
+/*
+ * ETZPC miscellaneous
+ */
+#define ETZPC_PROT_MASK GENMASK(1, 0)
+#define ETZPC_PROT_A7NS 0x3
+#define ETZPC_DECPROT_SHIFT 1
+
+#define IDS_PER_DECPROT_REGS 16
+
+static int stm32_etzpc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
+{
+ u32 offset, reg_offset, sec_val;
+
+ if (firewall_id >= ctrl->max_entries) {
+ dev_err(ctrl->dev, "Invalid sys bus ID %u", firewall_id);
+ return -EINVAL;
+ }
+
+ /* Check access configuration, 16 peripherals per register */
+ reg_offset = ETZPC_DECPROT + 0x4 * (firewall_id / IDS_PER_DECPROT_REGS);
+ offset = (firewall_id % IDS_PER_DECPROT_REGS) << ETZPC_DECPROT_SHIFT;
+
+ /* Verify peripheral is non-secure and attributed to cortex A7 */
+ sec_val = (readl(ctrl->mmio + reg_offset) >> offset) & ETZPC_PROT_MASK;
+ if (sec_val != ETZPC_PROT_A7NS) {
+ dev_dbg(ctrl->dev, "Invalid bus configuration: reg_offset %#x, value %d\n",
+ reg_offset, sec_val);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static void stm32_etzpc_release_access(struct stm32_firewall_controller *ctrl __maybe_unused,
+ u32 firewall_id __maybe_unused)
+{
+}
+
+static int stm32_etzpc_probe(struct platform_device *pdev)
+{
+ struct stm32_firewall_controller *etzpc_controller;
+ struct device_node *np = pdev->dev.of_node;
+ u32 nb_per, nb_master;
+ struct resource *res;
+ void __iomem *mmio;
+ int rc;
+
+ etzpc_controller = devm_kzalloc(&pdev->dev, sizeof(*etzpc_controller), GFP_KERNEL);
+ if (!etzpc_controller)
+ return -ENOMEM;
+
+ mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ etzpc_controller->dev = &pdev->dev;
+ etzpc_controller->mmio = mmio;
+ etzpc_controller->name = dev_driver_string(etzpc_controller->dev);
+ etzpc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
+ etzpc_controller->grant_access = stm32_etzpc_grant_access;
+ etzpc_controller->release_access = stm32_etzpc_release_access;
+
+ /* Get number of etzpc entries*/
+ nb_per = FIELD_GET(ETZPC_HWCFGR_NUM_PER_SEC,
+ readl(etzpc_controller->mmio + ETZPC_HWCFGR));
+ nb_master = FIELD_GET(ETZPC_HWCFGR_NUM_AHB_SEC,
+ readl(etzpc_controller->mmio + ETZPC_HWCFGR));
+ etzpc_controller->max_entries = nb_per + nb_master;
+
+ platform_set_drvdata(pdev, etzpc_controller);
+
+ rc = stm32_firewall_controller_register(etzpc_controller);
+ if (rc) {
+ dev_err(etzpc_controller->dev, "Couldn't register as a firewall controller: %d",
+ rc);
+ return rc;
+ }
+
+ rc = stm32_firewall_populate_bus(etzpc_controller);
+ if (rc) {
+ dev_err(etzpc_controller->dev, "Couldn't populate ETZPC bus: %d",
+ rc);
+ return rc;
+ }
+
+ /* Populate all allowed nodes */
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static const struct of_device_id stm32_etzpc_of_match[] = {
+ { .compatible = "st,stm32-etzpc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_etzpc_of_match);
+
+static struct platform_driver stm32_etzpc_driver = {
+ .probe = stm32_etzpc_probe,
+ .driver = {
+ .name = "stm32-etzpc",
+ .of_match_table = stm32_etzpc_of_match,
+ },
+};
+module_platform_driver(stm32_etzpc_driver);
+
+MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ETZPC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c
new file mode 100644
index 000000000000..2fc9761dadec
--- /dev/null
+++ b/drivers/bus/stm32_firewall.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bus/stm32_firewall_device.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#include "stm32_firewall.h"
+
+/* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */
+#define STM32_FIREWALL_MAX_ARGS (STM32_FIREWALL_MAX_EXTRA_ARGS + 1)
+
+static LIST_HEAD(firewall_controller_list);
+static DEFINE_MUTEX(firewall_controller_list_lock);
+
+/* Firewall device API */
+
+int stm32_firewall_get_firewall(struct device_node *np, struct stm32_firewall *firewall,
+ unsigned int nb_firewall)
+{
+ struct stm32_firewall_controller *ctrl;
+ struct of_phandle_iterator it;
+ unsigned int i, j = 0;
+ int err;
+
+ if (!firewall || !nb_firewall)
+ return -EINVAL;
+
+ /* Parse property with phandle parsed out */
+ of_for_each_phandle(&it, err, np, "access-controllers", "#access-controller-cells", 0) {
+ struct of_phandle_args provider_args;
+ struct device_node *provider = it.node;
+ const char *fw_entry;
+ bool match = false;
+
+ if (err) {
+ pr_err("Unable to get access-controllers property for node %s\n, err: %d",
+ np->full_name, err);
+ of_node_put(provider);
+ return err;
+ }
+
+ if (j >= nb_firewall) {
+ pr_err("Too many firewall controllers");
+ of_node_put(provider);
+ return -EINVAL;
+ }
+
+ provider_args.args_count = of_phandle_iterator_args(&it, provider_args.args,
+ STM32_FIREWALL_MAX_ARGS);
+
+ /* Check if the parsed phandle corresponds to a registered firewall controller */
+ mutex_lock(&firewall_controller_list_lock);
+ list_for_each_entry(ctrl, &firewall_controller_list, entry) {
+ if (ctrl->dev->of_node->phandle == it.phandle) {
+ match = true;
+ firewall[j].firewall_ctrl = ctrl;
+ break;
+ }
+ }
+ mutex_unlock(&firewall_controller_list_lock);
+
+ if (!match) {
+ firewall[j].firewall_ctrl = NULL;
+ pr_err("No firewall controller registered for %s\n", np->full_name);
+ of_node_put(provider);
+ return -ENODEV;
+ }
+
+ err = of_property_read_string_index(np, "access-controller-names", j, &fw_entry);
+ if (err == 0)
+ firewall[j].entry = fw_entry;
+
+ /* Handle the case when there are no arguments given along with the phandle */
+ if (provider_args.args_count < 0 ||
+ provider_args.args_count > STM32_FIREWALL_MAX_ARGS) {
+ of_node_put(provider);
+ return -EINVAL;
+ } else if (provider_args.args_count == 0) {
+ firewall[j].extra_args_size = 0;
+ firewall[j].firewall_id = U32_MAX;
+ j++;
+ continue;
+ }
+
+ /* The firewall ID is always the first argument */
+ firewall[j].firewall_id = provider_args.args[0];
+
+ /* Extra args start at the second argument */
+ for (i = 0; i < provider_args.args_count - 1; i++)
+ firewall[j].extra_args[i] = provider_args.args[i + 1];
+
+ /* Remove the firewall ID arg that is not an extra argument */
+ firewall[j].extra_args_size = provider_args.args_count - 1;
+
+ j++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_get_firewall);
+
+int stm32_firewall_grant_access(struct stm32_firewall *firewall)
+{
+ struct stm32_firewall_controller *firewall_controller;
+
+ if (!firewall || firewall->firewall_id == U32_MAX)
+ return -EINVAL;
+
+ firewall_controller = firewall->firewall_ctrl;
+
+ if (!firewall_controller)
+ return -ENODEV;
+
+ return firewall_controller->grant_access(firewall_controller, firewall->firewall_id);
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_grant_access);
+
+int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id)
+{
+ struct stm32_firewall_controller *firewall_controller;
+
+ if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX)
+ return -EINVAL;
+
+ firewall_controller = firewall->firewall_ctrl;
+
+ if (!firewall_controller)
+ return -ENODEV;
+
+ return firewall_controller->grant_access(firewall_controller, subsystem_id);
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_grant_access_by_id);
+
+void stm32_firewall_release_access(struct stm32_firewall *firewall)
+{
+ struct stm32_firewall_controller *firewall_controller;
+
+ if (!firewall || firewall->firewall_id == U32_MAX) {
+ pr_debug("Incorrect arguments when releasing a firewall access\n");
+ return;
+ }
+
+ firewall_controller = firewall->firewall_ctrl;
+
+ if (!firewall_controller) {
+ pr_debug("No firewall controller to release\n");
+ return;
+ }
+
+ firewall_controller->release_access(firewall_controller, firewall->firewall_id);
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_release_access);
+
+void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id)
+{
+ struct stm32_firewall_controller *firewall_controller;
+
+ if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) {
+ pr_debug("Incorrect arguments when releasing a firewall access");
+ return;
+ }
+
+ firewall_controller = firewall->firewall_ctrl;
+
+ if (!firewall_controller) {
+ pr_debug("No firewall controller to release");
+ return;
+ }
+
+ firewall_controller->release_access(firewall_controller, subsystem_id);
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id);
+
+/* Firewall controller API */
+
+int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller)
+{
+ struct stm32_firewall_controller *ctrl;
+
+ if (!firewall_controller)
+ return -ENODEV;
+
+ pr_info("Registering %s firewall controller\n", firewall_controller->name);
+
+ mutex_lock(&firewall_controller_list_lock);
+ list_for_each_entry(ctrl, &firewall_controller_list, entry) {
+ if (ctrl == firewall_controller) {
+ pr_debug("%s firewall controller already registered\n",
+ firewall_controller->name);
+ mutex_unlock(&firewall_controller_list_lock);
+ return 0;
+ }
+ }
+ list_add_tail(&firewall_controller->entry, &firewall_controller_list);
+ mutex_unlock(&firewall_controller_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_controller_register);
+
+void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller)
+{
+ struct stm32_firewall_controller *ctrl;
+ bool controller_removed = false;
+
+ if (!firewall_controller) {
+ pr_debug("Null reference while unregistering firewall controller\n");
+ return;
+ }
+
+ mutex_lock(&firewall_controller_list_lock);
+ list_for_each_entry(ctrl, &firewall_controller_list, entry) {
+ if (ctrl == firewall_controller) {
+ controller_removed = true;
+ list_del_init(&ctrl->entry);
+ break;
+ }
+ }
+ mutex_unlock(&firewall_controller_list_lock);
+
+ if (!controller_removed)
+ pr_debug("There was no firewall controller named %s to unregister\n",
+ firewall_controller->name);
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister);
+
+int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller)
+{
+ struct stm32_firewall *firewalls;
+ struct device_node *child;
+ struct device *parent;
+ unsigned int i;
+ int len;
+ int err;
+
+ parent = firewall_controller->dev;
+
+ dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev));
+
+ for_each_available_child_of_node(dev_of_node(parent), child) {
+ /* The access-controllers property is mandatory for firewall bus devices */
+ len = of_count_phandle_with_args(child, "access-controllers",
+ "#access-controller-cells");
+ if (len <= 0) {
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ firewalls = kcalloc(len, sizeof(*firewalls), GFP_KERNEL);
+ if (!firewalls) {
+ of_node_put(child);
+ return -ENOMEM;
+ }
+
+ err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len);
+ if (err) {
+ kfree(firewalls);
+ of_node_put(child);
+ return err;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (firewall_controller->grant_access(firewall_controller,
+ firewalls[i].firewall_id)) {
+ /*
+ * Peripheral access not allowed or not defined.
+ * Mark the node as populated so platform bus won't probe it
+ */
+ of_detach_node(child);
+ dev_err(parent, "%s: Device driver will not be probed\n",
+ child->full_name);
+ }
+ }
+
+ kfree(firewalls);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_firewall_populate_bus);
diff --git a/drivers/bus/stm32_firewall.h b/drivers/bus/stm32_firewall.h
new file mode 100644
index 000000000000..e5fac85fe346
--- /dev/null
+++ b/drivers/bus/stm32_firewall.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef _STM32_FIREWALL_H
+#define _STM32_FIREWALL_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/**
+ * STM32_PERIPHERAL_FIREWALL: This type of firewall protects peripherals
+ * STM32_MEMORY_FIREWALL: This type of firewall protects memories/subsets of memory
+ * zones
+ * STM32_NOTYPE_FIREWALL: Undefined firewall type
+ */
+
+#define STM32_PERIPHERAL_FIREWALL BIT(1)
+#define STM32_MEMORY_FIREWALL BIT(2)
+#define STM32_NOTYPE_FIREWALL BIT(3)
+
+/**
+ * struct stm32_firewall_controller - Information on firewall controller supplying services
+ *
+ * @name: Name of the firewall controller
+ * @dev: Device reference of the firewall controller
+ * @mmio: Base address of the firewall controller
+ * @entry: List entry of the firewall controller list
+ * @type: Type of firewall
+ * @max_entries: Number of entries covered by the firewall
+ * @grant_access: Callback used to grant access for a device access against a
+ * firewall controller
+ * @release_access: Callback used to release resources taken by a device when access was
+ * granted
+ * @grant_memory_range_access: Callback used to grant access for a device to a given memory region
+ */
+struct stm32_firewall_controller {
+ const char *name;
+ struct device *dev;
+ void __iomem *mmio;
+ struct list_head entry;
+ unsigned int type;
+ unsigned int max_entries;
+
+ int (*grant_access)(struct stm32_firewall_controller *ctrl, u32 id);
+ void (*release_access)(struct stm32_firewall_controller *ctrl, u32 id);
+ int (*grant_memory_range_access)(struct stm32_firewall_controller *ctrl, phys_addr_t paddr,
+ size_t size);
+};
+
+/**
+ * stm32_firewall_controller_register - Register a firewall controller to the STM32 firewall
+ * framework
+ * @firewall_controller: Firewall controller to register
+ *
+ * Returns 0 in case of success or -ENODEV if no controller was given.
+ */
+int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller);
+
+/**
+ * stm32_firewall_controller_unregister - Unregister a firewall controller from the STM32
+ * firewall framework
+ * @firewall_controller: Firewall controller to unregister
+ */
+void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller);
+
+/**
+ * stm32_firewall_populate_bus - Populate device tree nodes that have a correct firewall
+ * configuration. This is used at boot-time only, as a sanity check
+ * between device tree and firewalls hardware configurations to
+ * prevent a kernel crash when a device driver is not granted access
+ *
+ * @firewall_controller: Firewall controller which nodes will be populated or not
+ *
+ * Returns 0 in case of success or appropriate errno code if error occurred.
+ */
+int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller);
+
+#endif /* _STM32_FIREWALL_H */
diff --git a/drivers/bus/stm32_rifsc.c b/drivers/bus/stm32_rifsc.c
new file mode 100644
index 000000000000..debeaf8ea1bd
--- /dev/null
+++ b/drivers/bus/stm32_rifsc.c
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include "stm32_firewall.h"
+
+/*
+ * RIFSC offset register
+ */
+#define RIFSC_RISC_SECCFGR0 0x10
+#define RIFSC_RISC_PRIVCFGR0 0x30
+#define RIFSC_RISC_PER0_CIDCFGR 0x100
+#define RIFSC_RISC_PER0_SEMCR 0x104
+#define RIFSC_RISC_REG0_ACFGR 0x900
+#define RIFSC_RISC_REG3_AADDR 0x924
+#define RIFSC_RISC_HWCFGR2 0xFEC
+
+/*
+ * SEMCR register
+ */
+#define SEMCR_MUTEX BIT(0)
+
+/*
+ * HWCFGR2 register
+ */
+#define HWCFGR2_CONF1_MASK GENMASK(15, 0)
+#define HWCFGR2_CONF2_MASK GENMASK(23, 16)
+#define HWCFGR2_CONF3_MASK GENMASK(31, 24)
+
+/*
+ * RIFSC miscellaneous
+ */
+#define RIFSC_RISC_CFEN_MASK BIT(0)
+#define RIFSC_RISC_SEM_EN_MASK BIT(1)
+#define RIFSC_RISC_SCID_MASK GENMASK(6, 4)
+#define RIFSC_RISC_SEML_SHIFT 16
+#define RIFSC_RISC_SEMWL_MASK GENMASK(23, 16)
+#define RIFSC_RISC_PER_ID_MASK GENMASK(31, 24)
+
+#define RIFSC_RISC_PERx_CID_MASK (RIFSC_RISC_CFEN_MASK | \
+ RIFSC_RISC_SEM_EN_MASK | \
+ RIFSC_RISC_SCID_MASK | \
+ RIFSC_RISC_SEMWL_MASK)
+
+#define IDS_PER_RISC_SEC_PRIV_REGS 32
+
+/* RIF miscellaneous */
+/*
+ * CIDCFGR register fields
+ */
+#define CIDCFGR_CFEN BIT(0)
+#define CIDCFGR_SEMEN BIT(1)
+#define CIDCFGR_SEMWL(x) BIT(RIFSC_RISC_SEML_SHIFT + (x))
+
+#define SEMWL_SHIFT 16
+
+/* Compartiment IDs */
+#define RIF_CID0 0x0
+#define RIF_CID1 0x1
+
+#if defined(CONFIG_DEBUG_FS)
+#define RIFSC_RISUP_ENTRIES 128
+#define RIFSC_RIMU_ENTRIES 16
+#define RIFSC_RISAL_SUBREGIONS 2
+#define RIFSC_RISAL_GRANULARITY 8
+
+#define RIFSC_RIMC_ATTR0 0xC10
+
+#define RIFSC_RIMC_CIDSEL BIT(2)
+#define RIFSC_RIMC_MCID_MASK GENMASK(6, 4)
+#define RIFSC_RIMC_MSEC BIT(8)
+#define RIFSC_RIMC_MPRIV BIT(9)
+
+#define RIFSC_RISC_SRCID_MASK GENMASK(6, 4)
+#define RIFSC_RISC_SRPRIV BIT(9)
+#define RIFSC_RISC_SRSEC BIT(8)
+#define RIFSC_RISC_SRRLOCK BIT(1)
+#define RIFSC_RISC_SREN BIT(0)
+#define RIFSC_RISC_SRLENGTH_MASK GENMASK(27, 16)
+#define RIFSC_RISC_SRSTART_MASK GENMASK(10, 0)
+
+static const char *stm32mp21_rifsc_rimu_names[RIFSC_RIMU_ENTRIES] = {
+ "ETR",
+ "SDMMC1",
+ "SDMMC2",
+ "SDMMC3",
+ "OTG_HS",
+ "USBH",
+ "ETH1",
+ "ETH2",
+ "RESERVED",
+ "RESERVED",
+ "DCMIPP",
+ "LTDC_L1/L2",
+ "LTDC_L3",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+};
+
+static const char *stm32mp25_rifsc_rimu_names[RIFSC_RIMU_ENTRIES] = {
+ "ETR",
+ "SDMMC1",
+ "SDMMC2",
+ "SDMMC3",
+ "USB3DR",
+ "USBH",
+ "ETH1",
+ "ETH2",
+ "PCIE",
+ "GPU",
+ "DMCIPP",
+ "LTDC_L0/L1",
+ "LTDC_L2",
+ "LTDC_ROT",
+ "VDEC",
+ "VENC"
+};
+
+static const char *stm32mp21_rifsc_risup_names[RIFSC_RISUP_ENTRIES] = {
+ "TIM1",
+ "TIM2",
+ "TIM3",
+ "TIM4",
+ "TIM5",
+ "TIM6",
+ "TIM7",
+ "TIM8",
+ "TIM10",
+ "TIM11",
+ "TIM12",
+ "TIM13",
+ "TIM14",
+ "TIM15",
+ "TIM16",
+ "TIM17",
+ "RESERVED",
+ "LPTIM1",
+ "LPTIM2",
+ "LPTIM3",
+ "LPTIM4",
+ "LPTIM5",
+ "SPI1",
+ "SPI2",
+ "SPI3",
+ "SPI4",
+ "SPI5",
+ "SPI6",
+ "RESERVED",
+ "RESERVED",
+ "SPDIFRX",
+ "USART1",
+ "USART2",
+ "USART3",
+ "UART4",
+ "UART5",
+ "USART6",
+ "UART7",
+ "RESERVED",
+ "RESERVED",
+ "LPUART1",
+ "I2C1",
+ "I2C2",
+ "I2C3",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "SAI1",
+ "SAI2",
+ "SAI3",
+ "SAI4",
+ "RESERVED",
+ "MDF1",
+ "RESERVED",
+ "FDCAN",
+ "HDP",
+ "ADC1",
+ "ADC2",
+ "ETH1",
+ "ETH2",
+ "RESERVED",
+ "USBH",
+ "RESERVED",
+ "RESERVED",
+ "OTG_HS",
+ "DDRPERFM",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "STGEN",
+ "OCTOSPI1",
+ "RESERVED",
+ "SDMMC1",
+ "SDMMC2",
+ "SDMMC3",
+ "RESERVED",
+ "LTDC_CMN",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "CSI",
+ "DCMIPP",
+ "DCMI_PSSI",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RNG1",
+ "RNG2",
+ "PKA",
+ "SAES",
+ "HASH1",
+ "HASH2",
+ "CRYP1",
+ "CRYP2",
+ "IWDG1",
+ "IWDG2",
+ "IWDG3",
+ "IWDG4",
+ "WWDG1",
+ "RESERVED",
+ "VREFBUF",
+ "DTS",
+ "RAMCFG",
+ "CRC",
+ "SERC",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "I3C1",
+ "I3C2",
+ "I3C3",
+ "RESERVED",
+ "ICACHE_DCACHE",
+ "LTDC_L1L2",
+ "LTDC_L3",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "RESERVED",
+ "OTFDEC1",
+ "RESERVED",
+ "IAC",
+};
+
+static const char *stm32mp25_rifsc_risup_names[RIFSC_RISUP_ENTRIES] = {
+ "TIM1",
+ "TIM2",
+ "TIM3",
+ "TIM4",
+ "TIM5",
+ "TIM6",
+ "TIM7",
+ "TIM8",
+ "TIM10",
+ "TIM11",
+ "TIM12",
+ "TIM13",
+ "TIM14",
+ "TIM15",
+ "TIM16",
+ "TIM17",
+ "TIM20",
+ "LPTIM1",
+ "LPTIM2",
+ "LPTIM3",
+ "LPTIM4",
+ "LPTIM5",
+ "SPI1",
+ "SPI2",
+ "SPI3",
+ "SPI4",
+ "SPI5",
+ "SPI6",
+ "SPI7",
+ "SPI8",
+ "SPDIFRX",
+ "USART1",
+ "USART2",
+ "USART3",
+ "UART4",
+ "UART5",
+ "USART6",
+ "UART7",
+ "UART8",
+ "UART9",
+ "LPUART1",
+ "I2C1",
+ "I2C2",
+ "I2C3",
+ "I2C4",
+ "I2C5",
+ "I2C6",
+ "I2C7",
+ "I2C8",
+ "SAI1",
+ "SAI2",
+ "SAI3",
+ "SAI4",
+ "RESERVED",
+ "MDF1",
+ "ADF1",
+ "FDCAN",
+ "HDP",
+ "ADC12",
+ "ADC3",
+ "ETH1",
+ "ETH2",
+ "RESERVED",
+ "USBH",
+ "RESERVED",
+ "RESERVED",
+ "USB3DR",
+ "COMBOPHY",
+ "PCIE",
+ "UCPD1",
+ "ETHSW_DEIP",
+ "ETHSW_ACM_CF",
+ "ETHSW_ACM_MSGBU",
+ "STGEN",
+ "OCTOSPI1",
+ "OCTOSPI2",
+ "SDMMC1",
+ "SDMMC2",
+ "SDMMC3",
+ "GPU",
+ "LTDC_CMN",
+ "DSI_CMN",
+ "RESERVED",
+ "RESERVED",
+ "LVDS",
+ "RESERVED",
+ "CSI",
+ "DCMIPP",
+ "DCMI_PSSI",
+ "VDEC",
+ "VENC",
+ "RESERVED",
+ "RNG",
+ "PKA",
+ "SAES",
+ "HASH",
+ "CRYP1",
+ "CRYP2",
+ "IWDG1",
+ "IWDG2",
+ "IWDG3",
+ "IWDG4",
+ "IWDG5",
+ "WWDG1",
+ "WWDG2",
+ "RESERVED",
+ "VREFBUF",
+ "DTS",
+ "RAMCFG",
+ "CRC",
+ "SERC",
+ "OCTOSPIM",
+ "GICV2M",
+ "RESERVED",
+ "I3C1",
+ "I3C2",
+ "I3C3",
+ "I3C4",
+ "ICACHE_DCACHE",
+ "LTDC_L0L1",
+ "LTDC_L2",
+ "LTDC_ROT",
+ "DSI_TRIG",
+ "DSI_RDFIFO",
+ "RESERVED",
+ "OTFDEC1",
+ "OTFDEC2",
+ "IAC",
+};
+struct rifsc_risup_debug_data {
+ char dev_name[15];
+ u8 dev_cid;
+ u8 dev_sem_cids;
+ u8 dev_id;
+ bool dev_cid_filt_en;
+ bool dev_sem_en;
+ bool dev_priv;
+ bool dev_sec;
+};
+
+struct rifsc_rimu_debug_data {
+ char m_name[11];
+ u8 m_cid;
+ bool cidsel;
+ bool m_sec;
+ bool m_priv;
+};
+
+struct rifsc_subreg_debug_data {
+ bool sr_sec;
+ bool sr_priv;
+ u8 sr_cid;
+ bool sr_rlock;
+ bool sr_enable;
+ u16 sr_start;
+ u16 sr_length;
+};
+
+struct stm32_rifsc_resources_names {
+ const char **device_names;
+ const char **initiator_names;
+};
+struct rifsc_dbg_private {
+ const struct stm32_rifsc_resources_names *res_names;
+ void __iomem *mmio;
+ unsigned int nb_risup;
+ unsigned int nb_rimu;
+ unsigned int nb_risal;
+};
+
+static const struct stm32_rifsc_resources_names rifsc_mp21_res_names = {
+ .device_names = stm32mp21_rifsc_risup_names,
+ .initiator_names = stm32mp21_rifsc_rimu_names,
+};
+
+static const struct stm32_rifsc_resources_names rifsc_mp25_res_names = {
+ .device_names = stm32mp25_rifsc_risup_names,
+ .initiator_names = stm32mp25_rifsc_rimu_names,
+};
+
+static void stm32_rifsc_fill_rimu_dbg_entry(struct rifsc_dbg_private *rifsc,
+ struct rifsc_rimu_debug_data *dbg_entry, int i)
+{
+ const struct stm32_rifsc_resources_names *dbg_names = rifsc->res_names;
+ u32 rimc_attr = readl_relaxed(rifsc->mmio + RIFSC_RIMC_ATTR0 + 0x4 * i);
+
+ snprintf(dbg_entry->m_name, sizeof(dbg_entry->m_name), "%s", dbg_names->initiator_names[i]);
+ dbg_entry->m_cid = FIELD_GET(RIFSC_RIMC_MCID_MASK, rimc_attr);
+ dbg_entry->cidsel = rimc_attr & RIFSC_RIMC_CIDSEL;
+ dbg_entry->m_sec = rimc_attr & RIFSC_RIMC_MSEC;
+ dbg_entry->m_priv = rimc_attr & RIFSC_RIMC_MPRIV;
+}
+
+static void stm32_rifsc_fill_dev_dbg_entry(struct rifsc_dbg_private *rifsc,
+ struct rifsc_risup_debug_data *dbg_entry, int i)
+{
+ const struct stm32_rifsc_resources_names *dbg_names = rifsc->res_names;
+ u32 cid_cfgr, sec_cfgr, priv_cfgr;
+ u8 reg_id = i / IDS_PER_RISC_SEC_PRIV_REGS;
+ u8 reg_offset = i % IDS_PER_RISC_SEC_PRIV_REGS;
+
+ cid_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * i);
+ sec_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id);
+ priv_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_PRIVCFGR0 + 0x4 * reg_id);
+
+ snprintf(dbg_entry->dev_name, sizeof(dbg_entry->dev_name), "%s",
+ dbg_names->device_names[i]);
+ dbg_entry->dev_id = i;
+ dbg_entry->dev_cid_filt_en = cid_cfgr & CIDCFGR_CFEN;
+ dbg_entry->dev_sem_en = cid_cfgr & CIDCFGR_SEMEN;
+ dbg_entry->dev_cid = FIELD_GET(RIFSC_RISC_SCID_MASK, cid_cfgr);
+ dbg_entry->dev_sem_cids = FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_cfgr);
+ dbg_entry->dev_sec = sec_cfgr & BIT(reg_offset) ? true : false;
+ dbg_entry->dev_priv = priv_cfgr & BIT(reg_offset) ? true : false;
+}
+
+
+static void stm32_rifsc_fill_subreg_dbg_entry(struct rifsc_dbg_private *rifsc,
+ struct rifsc_subreg_debug_data *dbg_entry, int i,
+ int j)
+{
+ u32 risc_xcfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_REG0_ACFGR + 0x10 * i + 0x8 * j);
+ u32 risc_xaddr;
+
+ dbg_entry->sr_sec = risc_xcfgr & RIFSC_RISC_SRSEC;
+ dbg_entry->sr_priv = risc_xcfgr & RIFSC_RISC_SRPRIV;
+ dbg_entry->sr_cid = FIELD_GET(RIFSC_RISC_SRCID_MASK, risc_xcfgr);
+ dbg_entry->sr_rlock = risc_xcfgr & RIFSC_RISC_SRRLOCK;
+ dbg_entry->sr_enable = risc_xcfgr & RIFSC_RISC_SREN;
+ if (i == 2) {
+ risc_xaddr = readl_relaxed(rifsc->mmio + RIFSC_RISC_REG3_AADDR + 0x8 * j);
+ dbg_entry->sr_length = FIELD_GET(RIFSC_RISC_SRLENGTH_MASK, risc_xaddr);
+ dbg_entry->sr_start = FIELD_GET(RIFSC_RISC_SRSTART_MASK, risc_xaddr);
+ } else {
+ dbg_entry->sr_start = 0;
+ dbg_entry->sr_length = U16_MAX;
+ }
+}
+
+static int stm32_rifsc_conf_dump_show(struct seq_file *s, void *data)
+{
+ struct rifsc_dbg_private *rifsc = (struct rifsc_dbg_private *)s->private;
+ int i, j;
+
+ seq_puts(s, "\n=============================================\n");
+ seq_puts(s, " RIFSC dump\n");
+ seq_puts(s, "=============================================\n\n");
+
+ seq_puts(s, "\n=============================================\n");
+ seq_puts(s, " RISUP dump\n");
+ seq_puts(s, "=============================================\n");
+
+ seq_printf(s, "\n| %-15s |", "Peripheral name");
+ seq_puts(s, "| Firewall ID |");
+ seq_puts(s, "| N/SECURE |");
+ seq_puts(s, "| N/PRIVILEGED |");
+ seq_puts(s, "| CID filtering |");
+ seq_puts(s, "| Semaphore mode |");
+ seq_puts(s, "| SCID |");
+ seq_printf(s, "| %7s |\n", "SEMWL");
+
+ for (i = 0; i < RIFSC_RISUP_ENTRIES && i < rifsc->nb_risup; i++) {
+ struct rifsc_risup_debug_data d_dbg_entry;
+
+ stm32_rifsc_fill_dev_dbg_entry(rifsc, &d_dbg_entry, i);
+
+ seq_printf(s, "| %-15s |", d_dbg_entry.dev_name);
+ seq_printf(s, "| %-11d |", d_dbg_entry.dev_id);
+ seq_printf(s, "| %-8s |", d_dbg_entry.dev_sec ? "SEC" : "NSEC");
+ seq_printf(s, "| %-12s |", d_dbg_entry.dev_priv ? "PRIV" : "NPRIV");
+ seq_printf(s, "| %-13s |", str_enabled_disabled(d_dbg_entry.dev_cid_filt_en));
+ seq_printf(s, "| %-14s |", str_enabled_disabled(d_dbg_entry.dev_sem_en));
+ seq_printf(s, "| %-4d |", d_dbg_entry.dev_cid);
+ seq_printf(s, "| %#-7x |\n", d_dbg_entry.dev_sem_cids);
+ }
+
+ seq_puts(s, "\n=============================================\n");
+ seq_puts(s, " RIMU dump\n");
+ seq_puts(s, "=============================================\n");
+
+ seq_puts(s, "| RIMU's name |");
+ seq_puts(s, "| CIDSEL |");
+ seq_puts(s, "| MCID |");
+ seq_puts(s, "| N/SECURE |");
+ seq_puts(s, "| N/PRIVILEGED |\n");
+
+ for (i = 0; i < RIFSC_RIMU_ENTRIES && rifsc->nb_rimu; i++) {
+ struct rifsc_rimu_debug_data m_dbg_entry;
+
+ stm32_rifsc_fill_rimu_dbg_entry(rifsc, &m_dbg_entry, i);
+
+ seq_printf(s, "| %-11s |", m_dbg_entry.m_name);
+ seq_printf(s, "| %-6s |", m_dbg_entry.cidsel ? "CIDSEL" : "");
+ seq_printf(s, "| %-4d |", m_dbg_entry.m_cid);
+ seq_printf(s, "| %-8s |", m_dbg_entry.m_sec ? "SEC" : "NSEC");
+ seq_printf(s, "| %-12s |\n", m_dbg_entry.m_priv ? "PRIV" : "NPRIV");
+ }
+
+ if (rifsc->nb_risal > 0) {
+ seq_puts(s, "\n=============================================\n");
+ seq_puts(s, " RISAL dump\n");
+ seq_puts(s, "=============================================\n");
+
+ seq_puts(s, "| Memory |");
+ seq_puts(s, "| Subreg. |");
+ seq_puts(s, "| N/SECURE |");
+ seq_puts(s, "| N/PRIVILEGED |");
+ seq_puts(s, "| Subreg. CID |");
+ seq_puts(s, "| Resource lock |");
+ seq_puts(s, "| Subreg. enable |");
+ seq_puts(s, "| Subreg. start |");
+ seq_puts(s, "| Subreg. end |\n");
+
+ for (i = 0; i < rifsc->nb_risal; i++) {
+ for (j = 0; j < RIFSC_RISAL_SUBREGIONS; j++) {
+ struct rifsc_subreg_debug_data sr_dbg_entry;
+
+ stm32_rifsc_fill_subreg_dbg_entry(rifsc, &sr_dbg_entry, i, j);
+
+ seq_printf(s, "| LPSRAM%1d |", i + 1);
+ seq_printf(s, "| %1s |", (j == 0) ? "A" : "B");
+ seq_printf(s, "| %-8s |", sr_dbg_entry.sr_sec ? "SEC" : "NSEC");
+ seq_printf(s, "| %-12s |", sr_dbg_entry.sr_priv ? "PRIV" : "NPRIV");
+ seq_printf(s, "| 0x%-9x |", sr_dbg_entry.sr_cid);
+ seq_printf(s, "| %-13s |",
+ sr_dbg_entry.sr_rlock ? "locked (1)" : "unlocked (0)");
+ seq_printf(s, "| %-14s |",
+ str_enabled_disabled(sr_dbg_entry.sr_enable));
+ seq_printf(s, "| 0x%-11x |", sr_dbg_entry.sr_start);
+ seq_printf(s, "| 0x%-11x |\n", sr_dbg_entry.sr_start +
+ sr_dbg_entry.sr_length - 1);
+ }
+ }
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(stm32_rifsc_conf_dump);
+
+static int stm32_rifsc_register_debugfs(struct stm32_firewall_controller *rifsc_controller,
+ u32 nb_risup, u32 nb_rimu, u32 nb_risal)
+{
+ struct rifsc_dbg_private *rifsc_priv;
+ struct dentry *root = NULL;
+
+ rifsc_priv = devm_kzalloc(rifsc_controller->dev, sizeof(*rifsc_priv), GFP_KERNEL);
+ if (!rifsc_priv)
+ return -ENOMEM;
+
+ rifsc_priv->mmio = rifsc_controller->mmio;
+ rifsc_priv->nb_risup = nb_risup;
+ rifsc_priv->nb_rimu = nb_rimu;
+ rifsc_priv->nb_risal = nb_risal;
+ rifsc_priv->res_names = of_device_get_match_data(rifsc_controller->dev);
+
+ root = debugfs_lookup("stm32_firewall", NULL);
+ if (!root)
+ root = debugfs_create_dir("stm32_firewall", NULL);
+
+ if (IS_ERR(root))
+ return PTR_ERR(root);
+
+ debugfs_create_file("rifsc", 0444, root, rifsc_priv, &stm32_rifsc_conf_dump_fops);
+
+ return 0;
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static bool stm32_rifsc_is_semaphore_available(void __iomem *addr)
+{
+ return !(readl(addr) & SEMCR_MUTEX);
+}
+
+static int stm32_rif_acquire_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
+ int id)
+{
+ void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
+
+ writel(SEMCR_MUTEX, addr);
+
+ /* Check that CID1 has the semaphore */
+ if (stm32_rifsc_is_semaphore_available(addr) ||
+ FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) != RIF_CID1)
+ return -EACCES;
+
+ return 0;
+}
+
+static void stm32_rif_release_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
+ int id)
+{
+ void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
+
+ if (stm32_rifsc_is_semaphore_available(addr))
+ return;
+
+ writel(SEMCR_MUTEX, addr);
+
+ /* Ok if another compartment takes the semaphore before the check */
+ WARN_ON(!stm32_rifsc_is_semaphore_available(addr) &&
+ FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) == RIF_CID1);
+}
+
+static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
+{
+ struct stm32_firewall_controller *rifsc_controller = ctrl;
+ u32 reg_offset, reg_id, sec_reg_value, cid_reg_value;
+ int rc;
+
+ if (firewall_id >= rifsc_controller->max_entries) {
+ dev_err(rifsc_controller->dev, "Invalid sys bus ID %u", firewall_id);
+ return -EINVAL;
+ }
+
+ /*
+ * RIFSC_RISC_PRIVCFGRx and RIFSC_RISC_SECCFGRx both handle configuration access for
+ * 32 peripherals. On the other hand, there is one _RIFSC_RISC_PERx_CIDCFGR register
+ * per peripheral
+ */
+ reg_id = firewall_id / IDS_PER_RISC_SEC_PRIV_REGS;
+ reg_offset = firewall_id % IDS_PER_RISC_SEC_PRIV_REGS;
+ sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id);
+ cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id);
+
+ /* First check conditions for semaphore mode, which doesn't take into account static CID. */
+ if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) {
+ if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) {
+ /* Static CID is irrelevant if semaphore mode */
+ goto skip_cid_check;
+ } else {
+ dev_dbg(rifsc_controller->dev,
+ "Invalid bus semaphore configuration: index %d\n", firewall_id);
+ return -EACCES;
+ }
+ }
+
+ /*
+ * Skip CID check if CID filtering isn't enabled or filtering is enabled on CID0, which
+ * corresponds to whatever CID.
+ */
+ if (!(cid_reg_value & CIDCFGR_CFEN) ||
+ FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0)
+ goto skip_cid_check;
+
+ /* Coherency check with the CID configuration */
+ if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) {
+ dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n",
+ firewall_id);
+ return -EACCES;
+ }
+
+skip_cid_check:
+ /* Check security configuration */
+ if (sec_reg_value & BIT(reg_offset)) {
+ dev_dbg(rifsc_controller->dev,
+ "Invalid security configuration for peripheral: %d\n", firewall_id);
+ return -EACCES;
+ }
+
+ /*
+ * If the peripheral is in semaphore mode, take the semaphore so that
+ * the CID1 has the ownership.
+ */
+ if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) {
+ rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id);
+ if (rc) {
+ dev_err(rifsc_controller->dev,
+ "Couldn't acquire semaphore for peripheral: %d\n", firewall_id);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void stm32_rifsc_release_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
+{
+ stm32_rif_release_semaphore(ctrl, firewall_id);
+}
+
+static int stm32_rifsc_probe(struct platform_device *pdev)
+{
+ struct stm32_firewall_controller *rifsc_controller;
+ struct device_node *np = pdev->dev.of_node;
+ u32 nb_risup, nb_rimu, nb_risal;
+ struct resource *res;
+ void __iomem *mmio;
+ int rc;
+
+ rifsc_controller = devm_kzalloc(&pdev->dev, sizeof(*rifsc_controller), GFP_KERNEL);
+ if (!rifsc_controller)
+ return -ENOMEM;
+
+ mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ rifsc_controller->dev = &pdev->dev;
+ rifsc_controller->mmio = mmio;
+ rifsc_controller->name = dev_driver_string(rifsc_controller->dev);
+ rifsc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
+ rifsc_controller->grant_access = stm32_rifsc_grant_access;
+ rifsc_controller->release_access = stm32_rifsc_release_access;
+
+ /* Get number of RIFSC entries*/
+ nb_risup = FIELD_GET(HWCFGR2_CONF1_MASK,
+ readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2));
+ nb_rimu = FIELD_GET(HWCFGR2_CONF2_MASK,
+ readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2));
+ nb_risal = FIELD_GET(HWCFGR2_CONF3_MASK,
+ readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2));
+ /*
+ * On STM32MP21, RIFSC_RISC_HWCFGR2 shows an incorrect number of RISAL (NUM_RISAL is 3
+ * instead of 0). A software workaround is implemented using the st,mem-map property in the
+ * device tree. This property is absent or left empty if there is no RISAL.
+ */
+ if (of_device_is_compatible(np, "st,stm32mp21-rifsc"))
+ nb_risal = 0;
+ rifsc_controller->max_entries = nb_risup + nb_rimu + nb_risal;
+
+ platform_set_drvdata(pdev, rifsc_controller);
+
+ rc = stm32_firewall_controller_register(rifsc_controller);
+ if (rc) {
+ dev_err(rifsc_controller->dev, "Couldn't register as a firewall controller: %d",
+ rc);
+ return rc;
+ }
+
+ rc = stm32_firewall_populate_bus(rifsc_controller);
+ if (rc) {
+ dev_err(rifsc_controller->dev, "Couldn't populate RIFSC bus: %d",
+ rc);
+ return rc;
+ }
+
+#if defined(CONFIG_DEBUG_FS)
+ rc = stm32_rifsc_register_debugfs(rifsc_controller, nb_risup, nb_rimu, nb_risal);
+ if (rc)
+ return dev_err_probe(rifsc_controller->dev, rc, "Failed creating debugfs entry\n");
+#endif
+
+ /* Populate all allowed nodes */
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static const struct of_device_id stm32_rifsc_of_match[] = {
+ {
+ .compatible = "st,stm32mp25-rifsc",
+#if defined(CONFIG_DEBUG_FS)
+ .data = &rifsc_mp25_res_names,
+#endif
+ },
+ {
+ .compatible = "st,stm32mp21-rifsc",
+#if defined(CONFIG_DEBUG_FS)
+ .data = &rifsc_mp21_res_names,
+#endif
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_rifsc_of_match);
+
+static struct platform_driver stm32_rifsc_driver = {
+ .probe = stm32_rifsc_probe,
+ .driver = {
+ .name = "stm32-rifsc",
+ .of_match_table = stm32_rifsc_of_match,
+ },
+};
+module_platform_driver(stm32_rifsc_driver);
+
+MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>");
+MODULE_DESCRIPTION("STMicroelectronics RIFSC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/bus/sun50i-de2.c b/drivers/bus/sun50i-de2.c
index 414f29cdedf0..dfe588179aca 100644
--- a/drivers/bus/sun50i-de2.c
+++ b/drivers/bus/sun50i-de2.c
@@ -24,10 +24,9 @@ static int sun50i_de2_bus_probe(struct platform_device *pdev)
return 0;
}
-static int sun50i_de2_bus_remove(struct platform_device *pdev)
+static void sun50i_de2_bus_remove(struct platform_device *pdev)
{
sunxi_sram_release(&pdev->dev);
- return 0;
}
static const struct of_device_id sun50i_de2_bus_of_match[] = {
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
index 4cd2e127946e..82735c58be11 100644
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -39,7 +39,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -128,9 +128,9 @@ struct sunxi_rsb {
};
/* bus / slave device related functions */
-static struct bus_type sunxi_rsb_bus;
+static const struct bus_type sunxi_rsb_bus;
-static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv)
+static int sunxi_rsb_device_match(struct device *dev, const struct device_driver *drv)
{
return of_driver_match_device(dev, drv);
}
@@ -172,12 +172,17 @@ static void sunxi_rsb_device_remove(struct device *dev)
drv->remove(to_sunxi_rsb_device(dev));
}
-static struct bus_type sunxi_rsb_bus = {
+static int sunxi_rsb_device_modalias(const struct device *dev, struct kobj_uevent_env *env)
+{
+ return of_device_uevent_modalias(dev, env);
+}
+
+static const struct bus_type sunxi_rsb_bus = {
.name = RSB_CTRL_NAME,
.match = sunxi_rsb_device_match,
.probe = sunxi_rsb_device_probe,
.remove = sunxi_rsb_device_remove,
- .uevent = of_device_uevent_modalias,
+ .uevent = sunxi_rsb_device_modalias,
};
static void sunxi_rsb_dev_release(struct device *dev)
@@ -267,6 +272,9 @@ EXPORT_SYMBOL_GPL(sunxi_rsb_driver_register);
/* common code that starts a transfer */
static int _sunxi_rsb_run_xfer(struct sunxi_rsb *rsb)
{
+ u32 int_mask, status;
+ bool timeout;
+
if (readl(rsb->regs + RSB_CTRL) & RSB_CTRL_START_TRANS) {
dev_dbg(rsb->dev, "RSB transfer still in progress\n");
return -EBUSY;
@@ -274,13 +282,23 @@ static int _sunxi_rsb_run_xfer(struct sunxi_rsb *rsb)
reinit_completion(&rsb->complete);
- writel(RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER,
- rsb->regs + RSB_INTE);
+ int_mask = RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER;
+ writel(int_mask, rsb->regs + RSB_INTE);
writel(RSB_CTRL_START_TRANS | RSB_CTRL_GLOBAL_INT_ENB,
rsb->regs + RSB_CTRL);
- if (!wait_for_completion_io_timeout(&rsb->complete,
- msecs_to_jiffies(100))) {
+ if (irqs_disabled()) {
+ timeout = readl_poll_timeout_atomic(rsb->regs + RSB_INTS,
+ status, (status & int_mask),
+ 10, 100000);
+ writel(status, rsb->regs + RSB_INTS);
+ } else {
+ timeout = !wait_for_completion_io_timeout(&rsb->complete,
+ msecs_to_jiffies(100));
+ status = rsb->status;
+ }
+
+ if (timeout) {
dev_dbg(rsb->dev, "RSB timeout\n");
/* abort the transfer */
@@ -292,18 +310,18 @@ static int _sunxi_rsb_run_xfer(struct sunxi_rsb *rsb)
return -ETIMEDOUT;
}
- if (rsb->status & RSB_INTS_LOAD_BSY) {
+ if (status & RSB_INTS_LOAD_BSY) {
dev_dbg(rsb->dev, "RSB busy\n");
return -EBUSY;
}
- if (rsb->status & RSB_INTS_TRANS_ERR) {
- if (rsb->status & RSB_INTS_TRANS_ERR_ACK) {
+ if (status & RSB_INTS_TRANS_ERR) {
+ if (status & RSB_INTS_TRANS_ERR_ACK) {
dev_dbg(rsb->dev, "RSB slave nack\n");
return -EINVAL;
}
- if (rsb->status & RSB_INTS_TRANS_ERR_DATA) {
+ if (status & RSB_INTS_TRANS_ERR_DATA) {
dev_dbg(rsb->dev, "RSB transfer data error\n");
return -EIO;
}
@@ -355,7 +373,6 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
unlock:
mutex_unlock(&rsb->lock);
- pm_runtime_mark_last_busy(rsb->dev);
pm_runtime_put_autosuspend(rsb->dev);
return ret;
@@ -399,7 +416,6 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
mutex_unlock(&rsb->lock);
- pm_runtime_mark_last_busy(rsb->dev);
pm_runtime_put_autosuspend(rsb->dev);
return ret;
@@ -439,7 +455,7 @@ static void regmap_sunxi_rsb_free_ctx(void *context)
kfree(ctx);
}
-static struct regmap_bus regmap_sunxi_rsb = {
+static const struct regmap_bus regmap_sunxi_rsb = {
.reg_write = regmap_sunxi_rsb_reg_write,
.reg_read = regmap_sunxi_rsb_reg_read,
.free_context = regmap_sunxi_rsb_free_ctx,
@@ -728,18 +744,15 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- struct resource *r;
struct sunxi_rsb *rsb;
u32 clk_freq = 3000000;
int irq, ret;
of_property_read_u32(np, "clock-frequency", &clk_freq);
- if (clk_freq > RSB_MAX_FREQ) {
- dev_err(dev,
- "clock-frequency (%u Hz) is too high (max = 20MHz)\n",
- clk_freq);
- return -EINVAL;
- }
+ if (clk_freq > RSB_MAX_FREQ)
+ return dev_err_probe(dev, -EINVAL,
+ "clock-frequency (%u Hz) is too high (max = 20MHz)\n",
+ clk_freq);
rsb = devm_kzalloc(dev, sizeof(*rsb), GFP_KERNEL);
if (!rsb)
@@ -748,8 +761,7 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
rsb->dev = dev;
rsb->clk_freq = clk_freq;
platform_set_drvdata(pdev, rsb);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- rsb->regs = devm_ioremap_resource(dev, r);
+ rsb->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rsb->regs))
return PTR_ERR(rsb->regs);
@@ -758,28 +770,22 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
return irq;
rsb->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(rsb->clk)) {
- ret = PTR_ERR(rsb->clk);
- dev_err(dev, "failed to retrieve clk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(rsb->clk))
+ return dev_err_probe(dev, PTR_ERR(rsb->clk),
+ "failed to retrieve clk\n");
rsb->rstc = devm_reset_control_get(dev, NULL);
- if (IS_ERR(rsb->rstc)) {
- ret = PTR_ERR(rsb->rstc);
- dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(rsb->rstc))
+ return dev_err_probe(dev, PTR_ERR(rsb->rstc),
+ "failed to retrieve reset controller\n");
init_completion(&rsb->complete);
mutex_init(&rsb->lock);
ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
- if (ret) {
- dev_err(dev, "can't register interrupt handler irq %d: %d\n",
- irq, ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "can't register interrupt handler irq %d\n", irq);
ret = sunxi_rsb_hw_init(rsb);
if (ret)
@@ -801,23 +807,13 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
return 0;
}
-static int sunxi_rsb_remove(struct platform_device *pdev)
+static void sunxi_rsb_remove(struct platform_device *pdev)
{
struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
pm_runtime_disable(&pdev->dev);
sunxi_rsb_hw_exit(rsb);
-
- return 0;
-}
-
-static void sunxi_rsb_shutdown(struct platform_device *pdev)
-{
- struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
-
- pm_runtime_disable(&pdev->dev);
- sunxi_rsb_hw_exit(rsb);
}
static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = {
@@ -834,8 +830,7 @@ MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
static struct platform_driver sunxi_rsb_driver = {
.probe = sunxi_rsb_probe,
- .remove = sunxi_rsb_remove,
- .shutdown = sunxi_rsb_shutdown,
+ .remove = sunxi_rsb_remove,
.driver = {
.name = RSB_CTRL_NAME,
.of_match_table = sunxi_rsb_of_match_table,
@@ -853,7 +848,13 @@ static int __init sunxi_rsb_init(void)
return ret;
}
- return platform_driver_register(&sunxi_rsb_driver);
+ ret = platform_driver_register(&sunxi_rsb_driver);
+ if (ret) {
+ bus_unregister(&sunxi_rsb_bus);
+ return ret;
+ }
+
+ return 0;
}
module_init(sunxi_rsb_init);
diff --git a/drivers/bus/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c
index ac58142301f4..90e3b0a10816 100644
--- a/drivers/bus/tegra-aconnect.c
+++ b/drivers/bus/tegra-aconnect.c
@@ -53,11 +53,9 @@ static int tegra_aconnect_probe(struct platform_device *pdev)
return 0;
}
-static int tegra_aconnect_remove(struct platform_device *pdev)
+static void tegra_aconnect_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static int tegra_aconnect_runtime_resume(struct device *dev)
diff --git a/drivers/bus/tegra-gmi.c b/drivers/bus/tegra-gmi.c
index 662266719682..9c09141961d8 100644
--- a/drivers/bus/tegra-gmi.c
+++ b/drivers/bus/tegra-gmi.c
@@ -9,7 +9,9 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -209,7 +211,6 @@ static int tegra_gmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra_gmi *gmi;
- struct resource *res;
int err;
gmi = devm_kzalloc(dev, sizeof(*gmi), GFP_KERNEL);
@@ -219,8 +220,7 @@ static int tegra_gmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, gmi);
gmi->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gmi->base = devm_ioremap_resource(dev, res);
+ gmi->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gmi->base))
return PTR_ERR(gmi->base);
@@ -258,14 +258,12 @@ static int tegra_gmi_probe(struct platform_device *pdev)
return 0;
}
-static int tegra_gmi_remove(struct platform_device *pdev)
+static void tegra_gmi_remove(struct platform_device *pdev)
{
struct tegra_gmi *gmi = platform_get_drvdata(pdev);
of_platform_depopulate(gmi->dev);
tegra_gmi_disable(gmi);
-
- return 0;
}
static int __maybe_unused tegra_gmi_runtime_resume(struct device *dev)
diff --git a/drivers/bus/ti-pwmss.c b/drivers/bus/ti-pwmss.c
index e9c26c94251b..1f2cab91e438 100644
--- a/drivers/bus/ti-pwmss.c
+++ b/drivers/bus/ti-pwmss.c
@@ -10,7 +10,7 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
static const struct of_device_id pwmss_of_match[] = {
{ .compatible = "ti,am33xx-pwmss" },
@@ -33,10 +33,9 @@ static int pwmss_probe(struct platform_device *pdev)
return ret;
}
-static int pwmss_remove(struct platform_device *pdev)
+static void pwmss_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- return 0;
}
static struct platform_driver pwmss_driver = {
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index 9a7d12332fad..610354ce7f8f 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -1,6 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ti-sysc.c - Texas Instruments sysc interconnect target driver
+ *
+ * TI SoCs have an interconnect target wrapper IP for many devices. The wrapper
+ * IP manages clock gating, resets, and PM capabilities for the connected devices.
+ *
+ * Copyright (C) 2017-2024 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Many features are based on the earlier omap_hwmod arch code with thanks to all
+ * the people who developed and debugged the code over the years:
+ *
+ * Copyright (C) 2009-2011 Nokia Corporation
+ * Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/
*/
#include <linux/io.h>
@@ -37,7 +48,9 @@ enum sysc_soc {
SOC_UNKNOWN,
SOC_2420,
SOC_2430,
+ SOC_AM33,
SOC_3430,
+ SOC_AM35,
SOC_3630,
SOC_4430,
SOC_4460,
@@ -109,11 +122,11 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = {
* @cookie: data used by legacy platform callbacks
* @name: name if available
* @revision: interconnect target module revision
+ * @sysconfig: saved sysconfig register value
* @reserved: target module is reserved and already in use
* @enabled: sysc runtime enabled status
* @needs_resume: runtime resume needed on resume from suspend
* @child_needs_resume: runtime resume needed for child on resume from suspend
- * @disable_on_idle: status flag used for disabling modules with resets
* @idle_work: work structure used to perform delayed idle on a module
* @pre_reset_quirk: module specific pre-reset quirk
* @post_reset_quirk: module specific post-reset quirk
@@ -648,91 +661,23 @@ static int sysc_init_resets(struct sysc *ddata)
static int sysc_parse_and_check_child_range(struct sysc *ddata)
{
struct device_node *np = ddata->dev->of_node;
- const __be32 *ranges;
- u32 nr_addr, nr_size;
- int len, error;
-
- ranges = of_get_property(np, "ranges", &len);
- if (!ranges) {
- dev_err(ddata->dev, "missing ranges for %pOF\n", np);
-
- return -ENOENT;
- }
-
- len /= sizeof(*ranges);
-
- if (len < 3) {
- dev_err(ddata->dev, "incomplete ranges for %pOF\n", np);
-
- return -EINVAL;
- }
-
- error = of_property_read_u32(np, "#address-cells", &nr_addr);
- if (error)
- return -ENOENT;
+ struct of_range_parser parser;
+ struct of_range range;
+ int error;
- error = of_property_read_u32(np, "#size-cells", &nr_size);
+ error = of_range_parser_init(&parser, np);
if (error)
- return -ENOENT;
-
- if (nr_addr != 1 || nr_size != 1) {
- dev_err(ddata->dev, "invalid ranges for %pOF\n", np);
+ return error;
- return -EINVAL;
+ for_each_of_range(&parser, &range) {
+ ddata->module_pa = range.cpu_addr;
+ ddata->module_size = range.size;
+ break;
}
- ranges++;
- ddata->module_pa = of_translate_address(np, ranges++);
- ddata->module_size = be32_to_cpup(ranges);
-
return 0;
}
-/* Interconnect instances to probe before l4_per instances */
-static struct resource early_bus_ranges[] = {
- /* am3/4 l4_wkup */
- { .start = 0x44c00000, .end = 0x44c00000 + 0x300000, },
- /* omap4/5 and dra7 l4_cfg */
- { .start = 0x4a000000, .end = 0x4a000000 + 0x300000, },
- /* omap4 l4_wkup */
- { .start = 0x4a300000, .end = 0x4a300000 + 0x30000, },
- /* omap5 and dra7 l4_wkup without dra7 dcan segment */
- { .start = 0x4ae00000, .end = 0x4ae00000 + 0x30000, },
-};
-
-static atomic_t sysc_defer = ATOMIC_INIT(10);
-
-/**
- * sysc_defer_non_critical - defer non_critical interconnect probing
- * @ddata: device driver data
- *
- * We want to probe l4_cfg and l4_wkup interconnect instances before any
- * l4_per instances as l4_per instances depend on resources on l4_cfg and
- * l4_wkup interconnects.
- */
-static int sysc_defer_non_critical(struct sysc *ddata)
-{
- struct resource *res;
- int i;
-
- if (!atomic_read(&sysc_defer))
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(early_bus_ranges); i++) {
- res = &early_bus_ranges[i];
- if (ddata->module_pa >= res->start &&
- ddata->module_pa <= res->end) {
- atomic_set(&sysc_defer, 0);
-
- return 0;
- }
- }
-
- atomic_dec_if_positive(&sysc_defer);
-
- return -EPROBE_DEFER;
-}
-
static struct device_node *stdout_path;
static void sysc_init_stdout_path(struct sysc *ddata)
@@ -913,7 +858,7 @@ static int sysc_check_registers(struct sysc *ddata)
* within the interconnect target module range. For example, SGX has
* them at offset 0x1fc00 in the 32MB module address space. And cpsw
* has them at offset 0x1200 in the CPSW_WR child. Usually the
- * the interconnect target module registers are at the beginning of
+ * interconnect target module registers are at the beginning of
* the module range though.
*/
static int sysc_ioremap(struct sysc *ddata)
@@ -958,13 +903,9 @@ static int sysc_map_and_check_registers(struct sysc *ddata)
if (error)
return error;
- error = sysc_defer_non_critical(ddata);
- if (error)
- return error;
-
sysc_check_children(ddata);
- if (!of_get_property(np, "reg", NULL))
+ if (!of_property_present(np, "reg"))
return 0;
error = sysc_parse_registers(ddata);
@@ -1119,6 +1060,11 @@ static int sysc_enable_module(struct device *dev)
if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_SIDLE |
SYSC_QUIRK_SWSUP_SIDLE_ACT)) {
best_mode = SYSC_IDLE_NO;
+
+ /* Clear WAKEUP */
+ if (regbits->enwkup_shift >= 0 &&
+ ddata->cfg.sysc_val & BIT(regbits->enwkup_shift))
+ reg &= ~BIT(regbits->enwkup_shift);
} else {
best_mode = fls(ddata->cfg.sidlemodes) - 1;
if (best_mode > SYSC_IDLE_MASK) {
@@ -1246,6 +1192,13 @@ set_sidle:
}
}
+ if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE_ACT) {
+ /* Set WAKEUP */
+ if (regbits->enwkup_shift >= 0 &&
+ ddata->cfg.sysc_val & BIT(regbits->enwkup_shift))
+ reg |= BIT(regbits->enwkup_shift);
+ }
+
reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift);
reg |= best_mode << regbits->sidle_shift;
if (regbits->autoidle_shift >= 0 &&
@@ -1467,8 +1420,7 @@ static int __maybe_unused sysc_noirq_suspend(struct device *dev)
ddata = dev_get_drvdata(dev);
- if (ddata->cfg.quirks &
- (SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE))
+ if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE)
return 0;
if (!ddata->enabled)
@@ -1486,8 +1438,7 @@ static int __maybe_unused sysc_noirq_resume(struct device *dev)
ddata = dev_get_drvdata(dev);
- if (ddata->cfg.quirks &
- (SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE))
+ if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE)
return 0;
if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_RESUME) {
@@ -1538,17 +1489,6 @@ struct sysc_revision_quirk {
}
static const struct sysc_revision_quirk sysc_revision_quirks[] = {
- /* These drivers need to be fixed to not use pm_runtime_irq_safe() */
- SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
- SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
- SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
- SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
- /* Uarts on omap4 and later */
- SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
- SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
- SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
- SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
-
/* Quirks that need to be set based on the module address */
SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -ENODEV, 0x50000800, 0xffffffff,
SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT |
@@ -1606,10 +1546,27 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("sata", 0, 0xfc, 0x1100, -ENODEV, 0x5e412000, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
+ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
+ SYSC_QUIRK_SWSUP_SIDLE_ACT),
+ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
+ SYSC_QUIRK_SWSUP_SIDLE_ACT),
+ /* Uarts on omap4 and later */
+ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
+ SYSC_QUIRK_SWSUP_SIDLE_ACT),
+ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
+ SYSC_QUIRK_SWSUP_SIDLE_ACT),
+ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff,
+ SYSC_QUIRK_SWSUP_SIDLE_ACT),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
+ SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000033,
+ 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
+ SYSC_MODULE_QUIRK_OTG),
+ SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000040,
+ 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
+ SYSC_MODULE_QUIRK_OTG),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
SYSC_MODULE_QUIRK_OTG),
@@ -1808,7 +1765,7 @@ static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset,
if (!ddata->module_va)
return -EIO;
- /* DISP_CONTROL */
+ /* DISP_CONTROL, shut down lcd and digit on disable if enabled */
val = sysc_read(ddata, dispc_offset + 0x40);
lcd_en = val & lcd_en_mask;
digit_en = val & digit_en_mask;
@@ -1820,7 +1777,7 @@ static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset,
else
irq_mask |= BIT(2) | BIT(3); /* EVSYNC bits */
}
- if (disable & (lcd_en | digit_en))
+ if (disable && (lcd_en || digit_en))
sysc_write(ddata, dispc_offset + 0x40,
val & ~(lcd_en_mask | digit_en_mask));
@@ -1876,7 +1833,7 @@ static void sysc_pre_reset_quirk_dss(struct sysc *ddata)
dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n",
__func__, val, irq_mask);
- if (sysc_soc->soc == SOC_3430) {
+ if (sysc_soc->soc == SOC_3430 || sysc_soc->soc == SOC_AM35) {
/* Clear DSS_SDI_CONTROL */
sysc_write(ddata, 0x44, 0);
@@ -2031,6 +1988,21 @@ static void sysc_module_disable_quirk_pruss(struct sysc *ddata)
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
}
+static void sysc_module_enable_quirk_pruss(struct sysc *ddata)
+{
+ u32 reg;
+
+ reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
+
+ /*
+ * Clearing the SYSC_PRUSS_STANDBY_INIT bit - Updates OCP master
+ * port configuration to enable memory access outside of the
+ * PRU-ICSS subsystem.
+ */
+ reg &= (~SYSC_PRUSS_STANDBY_INIT);
+ sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
+}
+
static void sysc_init_module_quirks(struct sysc *ddata)
{
if (ddata->legacy_mode || !ddata->name)
@@ -2083,8 +2055,10 @@ static void sysc_init_module_quirks(struct sysc *ddata)
ddata->module_disable_quirk = sysc_reset_done_quirk_wdt;
}
- if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_PRUSS)
+ if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_PRUSS) {
+ ddata->module_enable_quirk = sysc_module_enable_quirk_pruss;
ddata->module_disable_quirk = sysc_module_disable_quirk_pruss;
+ }
}
static int sysc_clockdomain_init(struct sysc *ddata)
@@ -2146,8 +2120,7 @@ static int sysc_reset(struct sysc *ddata)
sysc_offset = ddata->offsets[SYSC_SYSCONFIG];
if (ddata->legacy_mode ||
- ddata->cap->regbits->srst_shift < 0 ||
- ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)
+ ddata->cap->regbits->srst_shift < 0)
return 0;
sysc_mask = BIT(ddata->cap->regbits->srst_shift);
@@ -2159,11 +2132,22 @@ static int sysc_reset(struct sysc *ddata)
sysc_val = sysc_read_sysconfig(ddata);
sysc_val |= sysc_mask;
sysc_write(ddata, sysc_offset, sysc_val);
- }
- if (ddata->cfg.srst_udelay)
- usleep_range(ddata->cfg.srst_udelay,
- ddata->cfg.srst_udelay * 2);
+ /*
+ * Some devices need a delay before reading registers
+ * after reset. Presumably a srst_udelay is not needed
+ * for devices that use a rstctrl register reset.
+ */
+ if (ddata->cfg.srst_udelay)
+ fsleep(ddata->cfg.srst_udelay);
+
+ /*
+ * Flush posted write. For devices needing srst_udelay
+ * this should trigger an interconnect error if the
+ * srst_udelay value is needed but not configured.
+ */
+ sysc_val = sysc_read_sysconfig(ddata);
+ }
if (ddata->post_reset_quirk)
ddata->post_reset_quirk(ddata);
@@ -2187,9 +2171,8 @@ static int sysc_reset(struct sysc *ddata)
static int sysc_init_module(struct sysc *ddata)
{
bool rstctrl_deasserted = false;
- int error = 0;
+ int error = sysc_clockdomain_init(ddata);
- error = sysc_clockdomain_init(ddata);
if (error)
return error;
@@ -2230,12 +2213,14 @@ static int sysc_init_module(struct sysc *ddata)
goto err_main_clocks;
}
- error = sysc_reset(ddata);
- if (error)
- dev_err(ddata->dev, "Reset failed with %d\n", error);
+ if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) {
+ error = sysc_reset(ddata);
+ if (error)
+ dev_err(ddata->dev, "Reset failed with %d\n", error);
- if (error && !ddata->legacy_mode)
- sysc_disable_module(ddata->dev);
+ if (error && !ddata->legacy_mode)
+ sysc_disable_module(ddata->dev);
+ }
err_main_clocks:
if (error)
@@ -2273,11 +2258,9 @@ static int sysc_init_idlemode(struct sysc *ddata, u8 *idlemodes,
const char *name)
{
struct device_node *np = ddata->dev->of_node;
- struct property *prop;
- const __be32 *p;
u32 val;
- of_property_for_each_u32(np, name, prop, p, val) {
+ of_property_for_each_u32(np, name, val) {
if (val >= SYSC_NR_IDLEMODES) {
dev_err(ddata->dev, "invalid idlemode: %i\n", val);
return -EINVAL;
@@ -2390,7 +2373,7 @@ static int sysc_child_add_clocks(struct sysc *ddata,
return 0;
}
-static struct device_type sysc_device_type = {
+static const struct device_type sysc_device_type = {
};
static struct sysc *sysc_child_to_parent(struct device *dev)
@@ -2437,98 +2420,13 @@ static int __maybe_unused sysc_child_runtime_resume(struct device *dev)
return pm_generic_runtime_resume(dev);
}
-#ifdef CONFIG_PM_SLEEP
-static int sysc_child_suspend_noirq(struct device *dev)
-{
- struct sysc *ddata;
- int error;
-
- ddata = sysc_child_to_parent(dev);
-
- dev_dbg(ddata->dev, "%s %s\n", __func__,
- ddata->name ? ddata->name : "");
-
- error = pm_generic_suspend_noirq(dev);
- if (error) {
- dev_err(dev, "%s error at %i: %i\n",
- __func__, __LINE__, error);
-
- return error;
- }
-
- if (!pm_runtime_status_suspended(dev)) {
- error = pm_generic_runtime_suspend(dev);
- if (error) {
- dev_dbg(dev, "%s busy at %i: %i\n",
- __func__, __LINE__, error);
-
- return 0;
- }
-
- error = sysc_runtime_suspend(ddata->dev);
- if (error) {
- dev_err(dev, "%s error at %i: %i\n",
- __func__, __LINE__, error);
-
- return error;
- }
-
- ddata->child_needs_resume = true;
- }
-
- return 0;
-}
-
-static int sysc_child_resume_noirq(struct device *dev)
-{
- struct sysc *ddata;
- int error;
-
- ddata = sysc_child_to_parent(dev);
-
- dev_dbg(ddata->dev, "%s %s\n", __func__,
- ddata->name ? ddata->name : "");
-
- if (ddata->child_needs_resume) {
- ddata->child_needs_resume = false;
-
- error = sysc_runtime_resume(ddata->dev);
- if (error)
- dev_err(ddata->dev,
- "%s runtime resume error: %i\n",
- __func__, error);
-
- error = pm_generic_runtime_resume(dev);
- if (error)
- dev_err(ddata->dev,
- "%s generic runtime resume: %i\n",
- __func__, error);
- }
-
- return pm_generic_resume_noirq(dev);
-}
-#endif
-
-static struct dev_pm_domain sysc_child_pm_domain = {
- .ops = {
- SET_RUNTIME_PM_OPS(sysc_child_runtime_suspend,
- sysc_child_runtime_resume,
- NULL)
- USE_PLATFORM_PM_SLEEP_OPS
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_child_suspend_noirq,
- sysc_child_resume_noirq)
- }
-};
-
/* Caller needs to take list_lock if ever used outside of cpu_pm */
static void sysc_reinit_modules(struct sysc_soc_info *soc)
{
struct sysc_module *module;
- struct list_head *pos;
struct sysc *ddata;
- list_for_each(pos, &sysc_soc->restored_modules) {
- module = list_entry(pos, struct sysc_module, node);
+ list_for_each_entry(module, &sysc_soc->restored_modules, node) {
ddata = module->ddata;
sysc_reinit_module(ddata, ddata->enabled);
}
@@ -2592,25 +2490,6 @@ out_unlock:
mutex_unlock(&sysc_soc->list_lock);
}
-/**
- * sysc_legacy_idle_quirk - handle children in omap_device compatible way
- * @ddata: device driver data
- * @child: child device driver
- *
- * Allow idle for child devices as done with _od_runtime_suspend().
- * Otherwise many child devices will not idle because of the permanent
- * parent usecount set in pm_runtime_irq_safe().
- *
- * Note that the long term solution is to just modify the child device
- * drivers to not set pm_runtime_irq_safe() and then this can be just
- * dropped.
- */
-static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child)
-{
- if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
- dev_pm_domain_set(child, &sysc_child_pm_domain);
-}
-
static int sysc_notifier_call(struct notifier_block *nb,
unsigned long event, void *device)
{
@@ -2627,7 +2506,6 @@ static int sysc_notifier_call(struct notifier_block *nb,
error = sysc_child_add_clocks(ddata, dev);
if (error)
return error;
- sysc_legacy_idle_quirk(ddata, dev);
break;
default:
break;
@@ -2658,14 +2536,12 @@ static const struct sysc_dts_quirk sysc_dts_quirks[] = {
static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
bool is_child)
{
- const struct property *prop;
- int i, len;
+ int i;
for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) {
const char *name = sysc_dts_quirks[i].name;
- prop = of_get_property(np, name, &len);
- if (!prop)
+ if (!of_property_present(np, name))
continue;
ddata->cfg.quirks |= sysc_dts_quirks[i].mask;
@@ -2851,8 +2727,7 @@ static const struct sysc_capabilities sysc_34xx_sr = {
.type = TI_SYSC_OMAP34XX_SR,
.sysc_mask = SYSC_OMAP2_CLOCKACTIVITY,
.regbits = &sysc_regbits_omap34xx_sr,
- .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED |
- SYSC_QUIRK_LEGACY_IDLE,
+ .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED,
};
/*
@@ -2873,13 +2748,12 @@ static const struct sysc_capabilities sysc_36xx_sr = {
.type = TI_SYSC_OMAP36XX_SR,
.sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP,
.regbits = &sysc_regbits_omap36xx_sr,
- .mod_quirks = SYSC_QUIRK_UNCACHED | SYSC_QUIRK_LEGACY_IDLE,
+ .mod_quirks = SYSC_QUIRK_UNCACHED,
};
static const struct sysc_capabilities sysc_omap4_sr = {
.type = TI_SYSC_OMAP4_SR,
.regbits = &sysc_regbits_omap36xx_sr,
- .mod_quirks = SYSC_QUIRK_LEGACY_IDLE,
};
/*
@@ -3039,6 +2913,8 @@ static void ti_sysc_idle(struct work_struct *work)
static const struct soc_device_attribute sysc_soc_match[] = {
SOC_FLAG("OMAP242*", SOC_2420),
SOC_FLAG("OMAP243*", SOC_2430),
+ SOC_FLAG("AM33*", SOC_AM33),
+ SOC_FLAG("AM35*", SOC_AM35),
SOC_FLAG("OMAP3[45]*", SOC_3430),
SOC_FLAG("OMAP3[67]*", SOC_3630),
SOC_FLAG("OMAP443*", SOC_4430),
@@ -3123,7 +2999,7 @@ static int sysc_init_static_data(struct sysc *ddata)
match = soc_device_match(sysc_soc_match);
if (match && match->data)
- sysc_soc->soc = (int)match->data;
+ sysc_soc->soc = (enum sysc_soc)(uintptr_t)match->data;
/*
* Check and warn about possible old incomplete dtb. We now want to see
@@ -3208,12 +3084,10 @@ static void sysc_cleanup_static_data(void)
static int sysc_check_disabled_devices(struct sysc *ddata)
{
struct sysc_address *disabled_module;
- struct list_head *pos;
int error = 0;
mutex_lock(&sysc_soc->list_lock);
- list_for_each(pos, &sysc_soc->disabled_modules) {
- disabled_module = list_entry(pos, struct sysc_address, node);
+ list_for_each_entry(disabled_module, &sysc_soc->disabled_modules, node) {
if (ddata->module_pa == disabled_module->base) {
dev_dbg(ddata->dev, "module disabled for this SoC\n");
error = -ENODEV;
@@ -3245,10 +3119,15 @@ static int sysc_check_active_timer(struct sysc *ddata)
* can be dropped if we stop supporting old beagleboard revisions
* A to B4 at some point.
*/
- if (sysc_soc->soc == SOC_3430)
+ switch (sysc_soc->soc) {
+ case SOC_AM33:
+ case SOC_3430:
+ case SOC_AM35:
error = -ENXIO;
- else
+ break;
+ default:
error = -EBUSY;
+ }
if ((ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) &&
(ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE))
@@ -3390,7 +3269,7 @@ unprepare:
return error;
}
-static int sysc_remove(struct platform_device *pdev)
+static void sysc_remove(struct platform_device *pdev)
{
struct sysc *ddata = platform_get_drvdata(pdev);
int error;
@@ -3415,8 +3294,6 @@ static int sysc_remove(struct platform_device *pdev)
unprepare:
sysc_unprepare(ddata);
-
- return 0;
}
static const struct of_device_id sysc_match[] = {
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 38c886dc2ed6..2328c48b9b12 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -39,45 +39,39 @@ struct ts_nbus {
/*
* request all gpios required by the bus.
*/
-static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus
- *ts_nbus)
+static int ts_nbus_init_pdata(struct platform_device *pdev,
+ struct ts_nbus *ts_nbus)
{
ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data",
GPIOD_OUT_HIGH);
- if (IS_ERR(ts_nbus->data)) {
- dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n");
- return PTR_ERR(ts_nbus->data);
- }
+ if (IS_ERR(ts_nbus->data))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->data),
+ "failed to retrieve ts,data-gpio from dts\n");
ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH);
- if (IS_ERR(ts_nbus->csn)) {
- dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n");
- return PTR_ERR(ts_nbus->csn);
- }
+ if (IS_ERR(ts_nbus->csn))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->csn),
+ "failed to retrieve ts,csn-gpio from dts\n");
ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH);
- if (IS_ERR(ts_nbus->txrx)) {
- dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n");
- return PTR_ERR(ts_nbus->txrx);
- }
+ if (IS_ERR(ts_nbus->txrx))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->txrx),
+ "failed to retrieve ts,txrx-gpio from dts\n");
ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH);
- if (IS_ERR(ts_nbus->strobe)) {
- dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n");
- return PTR_ERR(ts_nbus->strobe);
- }
+ if (IS_ERR(ts_nbus->strobe))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->strobe),
+ "failed to retrieve ts,strobe-gpio from dts\n");
ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH);
- if (IS_ERR(ts_nbus->ale)) {
- dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n");
- return PTR_ERR(ts_nbus->ale);
- }
+ if (IS_ERR(ts_nbus->ale))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->ale),
+ "failed to retrieve ts,ale-gpio from dts\n");
ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN);
- if (IS_ERR(ts_nbus->rdy)) {
- dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n");
- return PTR_ERR(ts_nbus->rdy);
- }
+ if (IS_ERR(ts_nbus->rdy))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->rdy),
+ "failed to retrieve ts,rdy-gpio from dts\n");
return 0;
}
@@ -273,7 +267,7 @@ EXPORT_SYMBOL_GPL(ts_nbus_write);
static int ts_nbus_probe(struct platform_device *pdev)
{
struct pwm_device *pwm;
- struct pwm_args pargs;
+ struct pwm_state state;
struct device *dev = &pdev->dev;
struct ts_nbus *ts_nbus;
int ret;
@@ -289,32 +283,24 @@ static int ts_nbus_probe(struct platform_device *pdev)
return ret;
pwm = devm_pwm_get(dev, NULL);
- if (IS_ERR(pwm)) {
- ret = PTR_ERR(pwm);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "unable to request PWM\n");
- return ret;
- }
+ if (IS_ERR(pwm))
+ return dev_err_probe(dev, PTR_ERR(pwm),
+ "unable to request PWM\n");
- pwm_get_args(pwm, &pargs);
- if (!pargs.period) {
- dev_err(&pdev->dev, "invalid PWM period\n");
- return -EINVAL;
- }
+ pwm_init_state(pwm, &state);
+ if (!state.period)
+ return dev_err_probe(dev, -EINVAL, "invalid PWM period\n");
- /*
- * FIXME: pwm_apply_args() should be removed when switching to
- * the atomic PWM API.
- */
- pwm_apply_args(pwm);
- ret = pwm_config(pwm, pargs.period, pargs.period);
+ state.duty_cycle = state.period;
+ state.enabled = true;
+
+ ret = pwm_apply_might_sleep(pwm, &state);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret, "failed to configure PWM\n");
/*
* we can now start the FPGA and populate the peripherals.
*/
- pwm_enable(pwm);
ts_nbus->pwm = pwm;
/*
@@ -324,14 +310,15 @@ static int ts_nbus_probe(struct platform_device *pdev)
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret < 0)
- return ret;
+ return dev_err_probe(dev, ret,
+ "failed to populate platform devices on bus\n");
dev_info(dev, "initialized\n");
return 0;
}
-static int ts_nbus_remove(struct platform_device *pdev)
+static void ts_nbus_remove(struct platform_device *pdev)
{
struct ts_nbus *ts_nbus = dev_get_drvdata(&pdev->dev);
@@ -339,8 +326,6 @@ static int ts_nbus_remove(struct platform_device *pdev)
mutex_lock(&ts_nbus->lock);
pwm_disable(ts_nbus->pwm);
mutex_unlock(&ts_nbus->lock);
-
- return 0;
}
static const struct of_device_id ts_nbus_of_match[] = {
diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c
index f70dedace20b..cb5c89ce7b86 100644
--- a/drivers/bus/uniphier-system-bus.c
+++ b/drivers/bus/uniphier-system-bus.c
@@ -176,10 +176,9 @@ static int uniphier_system_bus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_system_bus_priv *priv;
- const __be32 *ranges;
- u32 cells, addr, size;
- u64 paddr;
- int pna, bank, rlen, rone, ret;
+ struct of_range_parser parser;
+ struct of_range range;
+ int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -191,48 +190,17 @@ static int uniphier_system_bus_probe(struct platform_device *pdev)
priv->dev = dev;
- pna = of_n_addr_cells(dev->of_node);
-
- ret = of_property_read_u32(dev->of_node, "#address-cells", &cells);
- if (ret) {
- dev_err(dev, "failed to get #address-cells\n");
- return ret;
- }
- if (cells != 2) {
- dev_err(dev, "#address-cells must be 2\n");
- return -EINVAL;
- }
-
- ret = of_property_read_u32(dev->of_node, "#size-cells", &cells);
- if (ret) {
- dev_err(dev, "failed to get #size-cells\n");
+ ret = of_range_parser_init(&parser, dev->of_node);
+ if (ret)
return ret;
- }
- if (cells != 1) {
- dev_err(dev, "#size-cells must be 1\n");
- return -EINVAL;
- }
- ranges = of_get_property(dev->of_node, "ranges", &rlen);
- if (!ranges) {
- dev_err(dev, "failed to get ranges property\n");
- return -ENOENT;
- }
-
- rlen /= sizeof(*ranges);
- rone = pna + 2;
-
- for (; rlen >= rone; rlen -= rone) {
- bank = be32_to_cpup(ranges++);
- addr = be32_to_cpup(ranges++);
- paddr = of_translate_address(dev->of_node, ranges);
- if (paddr == OF_BAD_ADDR)
+ for_each_of_range(&parser, &range) {
+ if (range.cpu_addr == OF_BAD_ADDR)
return -EINVAL;
- ranges += pna;
- size = be32_to_cpup(ranges++);
-
- ret = uniphier_system_bus_add_bank(priv, bank, addr,
- paddr, size);
+ ret = uniphier_system_bus_add_bank(priv,
+ upper_32_bits(range.bus_addr),
+ lower_32_bits(range.bus_addr),
+ range.cpu_addr, range.size);
if (ret)
return ret;
}
diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c
index a58ac0c8e282..64ee920721ee 100644
--- a/drivers/bus/vexpress-config.c
+++ b/drivers/bus/vexpress-config.c
@@ -10,7 +10,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/vexpress.h>
@@ -54,7 +54,7 @@ struct vexpress_syscfg_func {
struct vexpress_syscfg *syscfg;
struct regmap *regmap;
int num_templates;
- u32 template[]; /* Keep it last! */
+ u32 template[] __counted_by(num_templates); /* Keep it last! */
};
struct vexpress_config_bridge_ops {
@@ -350,7 +350,6 @@ static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
static int vexpress_syscfg_probe(struct platform_device *pdev)
{
struct vexpress_syscfg *syscfg;
- struct resource *res;
struct vexpress_config_bridge *bridge;
struct device_node *node;
int master;
@@ -362,8 +361,7 @@ static int vexpress_syscfg_probe(struct platform_device *pdev)
syscfg->dev = &pdev->dev;
INIT_LIST_HEAD(&syscfg->funcs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- syscfg->base = devm_ioremap_resource(&pdev->dev, res);
+ syscfg->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(syscfg->base))
return PTR_ERR(syscfg->base);
@@ -416,4 +414,5 @@ static struct platform_driver vexpress_syscfg_driver = {
.probe = vexpress_syscfg_probe,
};
module_platform_driver(vexpress_syscfg_driver);
+MODULE_DESCRIPTION("Versatile Express configuration bus");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig
new file mode 100644
index 000000000000..1518449d47b5
--- /dev/null
+++ b/drivers/cache/Kconfig
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menuconfig CACHEMAINT_FOR_DMA
+ bool "Cache management for noncoherent DMA"
+ depends on RISCV
+ default y
+ help
+ These drivers implement support for noncoherent DMA master devices
+ on platforms that lack the standard CPU interfaces for this.
+
+if CACHEMAINT_FOR_DMA
+
+config AX45MP_L2_CACHE
+ bool "Andes Technology AX45MP L2 Cache controller"
+ select RISCV_NONSTANDARD_CACHE_OPS
+ help
+ Support for the L2 cache controller on Andes Technology AX45MP platforms.
+
+config SIFIVE_CCACHE
+ bool "Sifive Composable Cache controller"
+ depends on ARCH_SIFIVE || ARCH_STARFIVE
+ help
+ Support for the composable cache controller on SiFive platforms.
+
+config STARFIVE_STARLINK_CACHE
+ bool "StarFive StarLink Cache controller"
+ depends on ARCH_STARFIVE
+ depends on 64BIT
+ select RISCV_DMA_NONCOHERENT
+ select RISCV_NONSTANDARD_CACHE_OPS
+ help
+ Support for the StarLink cache controller IP from StarFive.
+
+endif #CACHEMAINT_FOR_DMA
+
+menuconfig CACHEMAINT_FOR_HOTPLUG
+ bool "Cache management for memory hot plug like operations"
+ depends on GENERIC_CPU_CACHE_MAINTENANCE
+ help
+ These drivers implement cache management for flows where it is necessary
+ to flush data from all host caches.
+
+if CACHEMAINT_FOR_HOTPLUG
+
+config HISI_SOC_HHA
+ tristate "HiSilicon Hydra Home Agent (HHA) device driver"
+ depends on (ARM64 && ACPI) || COMPILE_TEST
+ help
+ The Hydra Home Agent (HHA) is responsible for cache coherency
+ on the SoC. This drivers enables the cache maintenance functions of
+ the HHA.
+
+ This driver can be built as a module. If so, the module will be
+ called hisi_soc_hha.
+
+endif #CACHEMAINT_FOR_HOTPLUG
diff --git a/drivers/cache/Makefile b/drivers/cache/Makefile
new file mode 100644
index 000000000000..b3362b15d6c1
--- /dev/null
+++ b/drivers/cache/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_AX45MP_L2_CACHE) += ax45mp_cache.o
+obj-$(CONFIG_SIFIVE_CCACHE) += sifive_ccache.o
+obj-$(CONFIG_STARFIVE_STARLINK_CACHE) += starfive_starlink_cache.o
+
+obj-$(CONFIG_HISI_SOC_HHA) += hisi_soc_hha.o
diff --git a/drivers/cache/ax45mp_cache.c b/drivers/cache/ax45mp_cache.c
new file mode 100644
index 000000000000..1d7dd3d2c101
--- /dev/null
+++ b/drivers/cache/ax45mp_cache.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * non-coherent cache functions for Andes AX45MP
+ *
+ * Copyright (C) 2023 Renesas Electronics Corp.
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/cacheinfo.h>
+#include <linux/dma-direction.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#include <asm/dma-noncoherent.h>
+
+/* L2 cache registers */
+#define AX45MP_L2C_REG_CTL_OFFSET 0x8
+
+#define AX45MP_L2C_REG_C0_CMD_OFFSET 0x40
+#define AX45MP_L2C_REG_C0_ACC_OFFSET 0x48
+#define AX45MP_L2C_REG_STATUS_OFFSET 0x80
+
+/* D-cache operation */
+#define AX45MP_CCTL_L1D_VA_INVAL 0 /* Invalidate an L1 cache entry */
+#define AX45MP_CCTL_L1D_VA_WB 1 /* Write-back an L1 cache entry */
+
+/* L2 CCTL status */
+#define AX45MP_CCTL_L2_STATUS_IDLE 0
+
+/* L2 CCTL status cores mask */
+#define AX45MP_CCTL_L2_STATUS_C0_MASK 0xf
+
+/* L2 cache operation */
+#define AX45MP_CCTL_L2_PA_INVAL 0x8 /* Invalidate an L2 cache entry */
+#define AX45MP_CCTL_L2_PA_WB 0x9 /* Write-back an L2 cache entry */
+
+#define AX45MP_L2C_REG_PER_CORE_OFFSET 0x10
+#define AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET 4
+
+#define AX45MP_L2C_REG_CN_CMD_OFFSET(n) \
+ (AX45MP_L2C_REG_C0_CMD_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET))
+#define AX45MP_L2C_REG_CN_ACC_OFFSET(n) \
+ (AX45MP_L2C_REG_C0_ACC_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET))
+#define AX45MP_CCTL_L2_STATUS_CN_MASK(n) \
+ (AX45MP_CCTL_L2_STATUS_C0_MASK << ((n) * AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET))
+
+#define AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM 0x80b
+#define AX45MP_CCTL_REG_UCCTLCOMMAND_NUM 0x80c
+
+#define AX45MP_CACHE_LINE_SIZE 64
+
+struct ax45mp_priv {
+ void __iomem *l2c_base;
+ u32 ax45mp_cache_line_size;
+};
+
+static struct ax45mp_priv ax45mp_priv;
+
+/* L2 Cache operations */
+static inline uint32_t ax45mp_cpu_l2c_get_cctl_status(void)
+{
+ return readl(ax45mp_priv.l2c_base + AX45MP_L2C_REG_STATUS_OFFSET);
+}
+
+static void ax45mp_cpu_cache_operation(unsigned long start, unsigned long end,
+ unsigned int l1_op, unsigned int l2_op)
+{
+ unsigned long line_size = ax45mp_priv.ax45mp_cache_line_size;
+ void __iomem *base = ax45mp_priv.l2c_base;
+ int mhartid = smp_processor_id();
+ unsigned long pa;
+
+ while (end > start) {
+ csr_write(AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM, start);
+ csr_write(AX45MP_CCTL_REG_UCCTLCOMMAND_NUM, l1_op);
+
+ pa = virt_to_phys((void *)start);
+ writel(pa, base + AX45MP_L2C_REG_CN_ACC_OFFSET(mhartid));
+ writel(l2_op, base + AX45MP_L2C_REG_CN_CMD_OFFSET(mhartid));
+ while ((ax45mp_cpu_l2c_get_cctl_status() &
+ AX45MP_CCTL_L2_STATUS_CN_MASK(mhartid)) !=
+ AX45MP_CCTL_L2_STATUS_IDLE)
+ ;
+
+ start += line_size;
+ }
+}
+
+/* Write-back L1 and L2 cache entry */
+static inline void ax45mp_cpu_dcache_wb_range(unsigned long start, unsigned long end)
+{
+ ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_WB,
+ AX45MP_CCTL_L2_PA_WB);
+}
+
+/* Invalidate the L1 and L2 cache entry */
+static inline void ax45mp_cpu_dcache_inval_range(unsigned long start, unsigned long end)
+{
+ ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_INVAL,
+ AX45MP_CCTL_L2_PA_INVAL);
+}
+
+static void ax45mp_dma_cache_inv(phys_addr_t paddr, size_t size)
+{
+ unsigned long start = (unsigned long)phys_to_virt(paddr);
+ unsigned long end = start + size;
+ unsigned long line_size;
+ unsigned long flags;
+
+ if (unlikely(start == end))
+ return;
+
+ line_size = ax45mp_priv.ax45mp_cache_line_size;
+
+ start = start & (~(line_size - 1));
+ end = ((end + line_size - 1) & (~(line_size - 1)));
+
+ local_irq_save(flags);
+
+ ax45mp_cpu_dcache_inval_range(start, end);
+
+ local_irq_restore(flags);
+}
+
+static void ax45mp_dma_cache_wback(phys_addr_t paddr, size_t size)
+{
+ unsigned long start = (unsigned long)phys_to_virt(paddr);
+ unsigned long end = start + size;
+ unsigned long line_size;
+ unsigned long flags;
+
+ if (unlikely(start == end))
+ return;
+
+ line_size = ax45mp_priv.ax45mp_cache_line_size;
+ start = start & (~(line_size - 1));
+ end = ((end + line_size - 1) & (~(line_size - 1)));
+ local_irq_save(flags);
+ ax45mp_cpu_dcache_wb_range(start, end);
+ local_irq_restore(flags);
+}
+
+static void ax45mp_dma_cache_wback_inv(phys_addr_t paddr, size_t size)
+{
+ ax45mp_dma_cache_wback(paddr, size);
+ ax45mp_dma_cache_inv(paddr, size);
+}
+
+static int ax45mp_get_l2_line_size(struct device_node *np)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "cache-line-size", &ax45mp_priv.ax45mp_cache_line_size);
+ if (ret) {
+ pr_err("Failed to get cache-line-size, defaulting to 64 bytes\n");
+ return ret;
+ }
+
+ if (ax45mp_priv.ax45mp_cache_line_size != AX45MP_CACHE_LINE_SIZE) {
+ pr_err("Expected cache-line-size to be 64 bytes (found:%u)\n",
+ ax45mp_priv.ax45mp_cache_line_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct riscv_nonstd_cache_ops ax45mp_cmo_ops __initdata = {
+ .wback = &ax45mp_dma_cache_wback,
+ .inv = &ax45mp_dma_cache_inv,
+ .wback_inv = &ax45mp_dma_cache_wback_inv,
+};
+
+static const struct of_device_id ax45mp_cache_ids[] = {
+ { .compatible = "andestech,ax45mp-cache" },
+ { /* sentinel */ }
+};
+
+static int __init ax45mp_cache_init(void)
+{
+ struct device_node *np;
+ struct resource res;
+ int ret;
+
+ np = of_find_matching_node(NULL, ax45mp_cache_ids);
+ if (!of_device_is_available(np))
+ return -ENODEV;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret)
+ return ret;
+
+ /*
+ * If IOCP is present on the Andes AX45MP core riscv_cbom_block_size
+ * will be 0 for sure, so we can definitely rely on it. If
+ * riscv_cbom_block_size = 0 we don't need to handle CMO using SW any
+ * more so we just return success here and only if its being set we
+ * continue further in the probe path.
+ */
+ if (!riscv_cbom_block_size)
+ return 0;
+
+ ax45mp_priv.l2c_base = ioremap(res.start, resource_size(&res));
+ if (!ax45mp_priv.l2c_base)
+ return -ENOMEM;
+
+ ret = ax45mp_get_l2_line_size(np);
+ if (ret) {
+ iounmap(ax45mp_priv.l2c_base);
+ return ret;
+ }
+
+ riscv_noncoherent_register_cache_ops(&ax45mp_cmo_ops);
+
+ return 0;
+}
+early_initcall(ax45mp_cache_init);
diff --git a/drivers/cache/hisi_soc_hha.c b/drivers/cache/hisi_soc_hha.c
new file mode 100644
index 000000000000..25ff0f5ae79b
--- /dev/null
+++ b/drivers/cache/hisi_soc_hha.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for HiSilicon Hydra Home Agent (HHA).
+ *
+ * Copyright (c) 2025 HiSilicon Technologies Co., Ltd.
+ * Author: Yicong Yang <yangyicong@hisilicon.com>
+ * Yushan Wang <wangyushan12@huawei.com>
+ *
+ * A system typically contains multiple HHAs. Each is responsible for a subset
+ * of the physical addresses in the system, but interleave can make the mapping
+ * from a particular cache line to a responsible HHA complex. As such no
+ * filtering is done in the driver, with the hardware being responsible for
+ * responding with success for even if it was not responsible for any addresses
+ * in the range on which the operation was requested.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cache_coherency.h>
+#include <linux/dev_printk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/memregion.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#define HISI_HHA_CTRL 0x5004
+#define HISI_HHA_CTRL_EN BIT(0)
+#define HISI_HHA_CTRL_RANGE BIT(1)
+#define HISI_HHA_CTRL_TYPE GENMASK(3, 2)
+#define HISI_HHA_START_L 0x5008
+#define HISI_HHA_START_H 0x500c
+#define HISI_HHA_LEN_L 0x5010
+#define HISI_HHA_LEN_H 0x5014
+
+/* The maintain operation performs in a 128 Byte granularity */
+#define HISI_HHA_MAINT_ALIGN 128
+
+#define HISI_HHA_POLL_GAP_US 10
+#define HISI_HHA_POLL_TIMEOUT_US 50000
+
+struct hisi_soc_hha {
+ /* Must be first element */
+ struct cache_coherency_ops_inst cci;
+ /* Locks HHA instance to forbid overlapping access. */
+ struct mutex lock;
+ void __iomem *base;
+};
+
+static bool hisi_hha_cache_maintain_wait_finished(struct hisi_soc_hha *soc_hha)
+{
+ u32 val;
+
+ return !readl_poll_timeout_atomic(soc_hha->base + HISI_HHA_CTRL, val,
+ !(val & HISI_HHA_CTRL_EN),
+ HISI_HHA_POLL_GAP_US,
+ HISI_HHA_POLL_TIMEOUT_US);
+}
+
+static int hisi_soc_hha_wbinv(struct cache_coherency_ops_inst *cci,
+ struct cc_inval_params *invp)
+{
+ struct hisi_soc_hha *soc_hha =
+ container_of(cci, struct hisi_soc_hha, cci);
+ phys_addr_t top, addr = invp->addr;
+ size_t size = invp->size;
+ u32 reg;
+
+ if (!size)
+ return -EINVAL;
+
+ addr = ALIGN_DOWN(addr, HISI_HHA_MAINT_ALIGN);
+ top = ALIGN(addr + size, HISI_HHA_MAINT_ALIGN);
+ size = top - addr;
+
+ guard(mutex)(&soc_hha->lock);
+
+ if (!hisi_hha_cache_maintain_wait_finished(soc_hha))
+ return -EBUSY;
+
+ /*
+ * Hardware will search for addresses ranging [addr, addr + size - 1],
+ * last byte included, and perform maintenance in 128 byte granules
+ * on those cachelines which contain the addresses. If a given instance
+ * is either not responsible for a cacheline or that cacheline is not
+ * currently present then the search will fail, no operation will be
+ * necessary and the device will report success.
+ */
+ size -= 1;
+
+ writel(lower_32_bits(addr), soc_hha->base + HISI_HHA_START_L);
+ writel(upper_32_bits(addr), soc_hha->base + HISI_HHA_START_H);
+ writel(lower_32_bits(size), soc_hha->base + HISI_HHA_LEN_L);
+ writel(upper_32_bits(size), soc_hha->base + HISI_HHA_LEN_H);
+
+ reg = FIELD_PREP(HISI_HHA_CTRL_TYPE, 1); /* Clean Invalid */
+ reg |= HISI_HHA_CTRL_RANGE | HISI_HHA_CTRL_EN;
+ writel(reg, soc_hha->base + HISI_HHA_CTRL);
+
+ return 0;
+}
+
+static int hisi_soc_hha_done(struct cache_coherency_ops_inst *cci)
+{
+ struct hisi_soc_hha *soc_hha =
+ container_of(cci, struct hisi_soc_hha, cci);
+
+ guard(mutex)(&soc_hha->lock);
+ if (!hisi_hha_cache_maintain_wait_finished(soc_hha))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static const struct cache_coherency_ops hha_ops = {
+ .wbinv = hisi_soc_hha_wbinv,
+ .done = hisi_soc_hha_done,
+};
+
+static int hisi_soc_hha_probe(struct platform_device *pdev)
+{
+ struct hisi_soc_hha *soc_hha;
+ struct resource *mem;
+ int ret;
+
+ soc_hha = cache_coherency_ops_instance_alloc(&hha_ops,
+ struct hisi_soc_hha, cci);
+ if (!soc_hha)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, soc_hha);
+
+ mutex_init(&soc_hha->lock);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ ret = -ENOMEM;
+ goto err_free_cci;
+ }
+
+ soc_hha->base = ioremap(mem->start, resource_size(mem));
+ if (!soc_hha->base) {
+ ret = dev_err_probe(&pdev->dev, -ENOMEM,
+ "failed to remap io memory");
+ goto err_free_cci;
+ }
+
+ ret = cache_coherency_ops_instance_register(&soc_hha->cci);
+ if (ret)
+ goto err_iounmap;
+
+ return 0;
+
+err_iounmap:
+ iounmap(soc_hha->base);
+err_free_cci:
+ cache_coherency_ops_instance_put(&soc_hha->cci);
+ return ret;
+}
+
+static void hisi_soc_hha_remove(struct platform_device *pdev)
+{
+ struct hisi_soc_hha *soc_hha = platform_get_drvdata(pdev);
+
+ cache_coherency_ops_instance_unregister(&soc_hha->cci);
+ iounmap(soc_hha->base);
+ cache_coherency_ops_instance_put(&soc_hha->cci);
+}
+
+static const struct acpi_device_id hisi_soc_hha_ids[] = {
+ { "HISI0511", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, hisi_soc_hha_ids);
+
+static struct platform_driver hisi_soc_hha_driver = {
+ .driver = {
+ .name = "hisi_soc_hha",
+ .acpi_match_table = hisi_soc_hha_ids,
+ },
+ .probe = hisi_soc_hha_probe,
+ .remove = hisi_soc_hha_remove,
+};
+
+module_platform_driver(hisi_soc_hha_driver);
+
+MODULE_IMPORT_NS("CACHE_COHERENCY");
+MODULE_DESCRIPTION("HiSilicon Hydra Home Agent driver supporting cache maintenance");
+MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>");
+MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cache/sifive_ccache.c b/drivers/cache/sifive_ccache.c
new file mode 100644
index 000000000000..a86800b123b9
--- /dev/null
+++ b/drivers/cache/sifive_ccache.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SiFive composable cache controller Driver
+ *
+ * Copyright (C) 2018-2022 SiFive, Inc.
+ *
+ */
+
+#define pr_fmt(fmt) "CCACHE: " fmt
+
+#include <linux/align.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/device.h>
+#include <linux/bitfield.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <asm/cacheflush.h>
+#include <asm/cacheinfo.h>
+#include <asm/dma-noncoherent.h>
+#include <soc/sifive/sifive_ccache.h>
+
+#define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100
+#define SIFIVE_CCACHE_DIRECCFIX_HIGH 0x104
+#define SIFIVE_CCACHE_DIRECCFIX_COUNT 0x108
+
+#define SIFIVE_CCACHE_DIRECCFAIL_LOW 0x120
+#define SIFIVE_CCACHE_DIRECCFAIL_HIGH 0x124
+#define SIFIVE_CCACHE_DIRECCFAIL_COUNT 0x128
+
+#define SIFIVE_CCACHE_DATECCFIX_LOW 0x140
+#define SIFIVE_CCACHE_DATECCFIX_HIGH 0x144
+#define SIFIVE_CCACHE_DATECCFIX_COUNT 0x148
+
+#define SIFIVE_CCACHE_DATECCFAIL_LOW 0x160
+#define SIFIVE_CCACHE_DATECCFAIL_HIGH 0x164
+#define SIFIVE_CCACHE_DATECCFAIL_COUNT 0x168
+
+#define SIFIVE_CCACHE_CONFIG 0x00
+#define SIFIVE_CCACHE_CONFIG_BANK_MASK GENMASK_ULL(7, 0)
+#define SIFIVE_CCACHE_CONFIG_WAYS_MASK GENMASK_ULL(15, 8)
+#define SIFIVE_CCACHE_CONFIG_SETS_MASK GENMASK_ULL(23, 16)
+#define SIFIVE_CCACHE_CONFIG_BLKS_MASK GENMASK_ULL(31, 24)
+
+#define SIFIVE_CCACHE_FLUSH64 0x200
+#define SIFIVE_CCACHE_FLUSH32 0x240
+
+#define SIFIVE_CCACHE_WAYENABLE 0x08
+#define SIFIVE_CCACHE_ECCINJECTERR 0x40
+
+#define SIFIVE_CCACHE_MAX_ECCINTR 4
+#define SIFIVE_CCACHE_LINE_SIZE 64
+
+static void __iomem *ccache_base;
+static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR];
+static struct riscv_cacheinfo_ops ccache_cache_ops;
+static int level;
+
+enum {
+ DIR_CORR = 0,
+ DATA_CORR,
+ DATA_UNCORR,
+ DIR_UNCORR,
+};
+
+enum {
+ QUIRK_NONSTANDARD_CACHE_OPS = BIT(0),
+ QUIRK_BROKEN_DATA_UNCORR = BIT(1),
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *sifive_test;
+
+static ssize_t ccache_write(struct file *file, const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ unsigned int val;
+
+ if (kstrtouint_from_user(data, count, 0, &val))
+ return -EINVAL;
+ if ((val < 0xFF) || (val >= 0x10000 && val < 0x100FF))
+ writel(val, ccache_base + SIFIVE_CCACHE_ECCINJECTERR);
+ else
+ return -EINVAL;
+ return count;
+}
+
+static const struct file_operations ccache_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = ccache_write
+};
+
+static void setup_sifive_debug(void)
+{
+ sifive_test = debugfs_create_dir("sifive_ccache_cache", NULL);
+
+ debugfs_create_file("sifive_debug_inject_error", 0200,
+ sifive_test, NULL, &ccache_fops);
+}
+#endif
+
+static void ccache_config_read(void)
+{
+ u32 cfg;
+
+ cfg = readl(ccache_base + SIFIVE_CCACHE_CONFIG);
+ pr_info("%llu banks, %llu ways, sets/bank=%llu, bytes/block=%llu\n",
+ FIELD_GET(SIFIVE_CCACHE_CONFIG_BANK_MASK, cfg),
+ FIELD_GET(SIFIVE_CCACHE_CONFIG_WAYS_MASK, cfg),
+ BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_SETS_MASK, cfg)),
+ BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_BLKS_MASK, cfg)));
+
+ cfg = readl(ccache_base + SIFIVE_CCACHE_WAYENABLE);
+ pr_info("Index of the largest way enabled: %u\n", cfg);
+}
+
+static const struct of_device_id sifive_ccache_ids[] = {
+ { .compatible = "eswin,eic7700-l3-cache",
+ .data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS) },
+ { .compatible = "sifive,fu540-c000-ccache" },
+ { .compatible = "sifive,fu740-c000-ccache" },
+ { .compatible = "starfive,jh7100-ccache",
+ .data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS | QUIRK_BROKEN_DATA_UNCORR) },
+ { .compatible = "sifive,ccache0" },
+ { /* end of table */ }
+};
+
+static ATOMIC_NOTIFIER_HEAD(ccache_err_chain);
+
+int register_sifive_ccache_error_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&ccache_err_chain, nb);
+}
+EXPORT_SYMBOL_GPL(register_sifive_ccache_error_notifier);
+
+int unregister_sifive_ccache_error_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&ccache_err_chain, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier);
+
+#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
+static void ccache_flush_range(phys_addr_t start, size_t len)
+{
+ phys_addr_t end = start + len;
+ phys_addr_t line;
+
+ if (!len)
+ return;
+
+ mb(); /* complete earlier memory accesses before the cache flush */
+ for (line = ALIGN_DOWN(start, SIFIVE_CCACHE_LINE_SIZE); line < end;
+ line += SIFIVE_CCACHE_LINE_SIZE) {
+#ifdef CONFIG_32BIT
+ writel_relaxed(line >> 4, ccache_base + SIFIVE_CCACHE_FLUSH32);
+#else
+ writeq_relaxed(line, ccache_base + SIFIVE_CCACHE_FLUSH64);
+#endif
+ }
+ mb(); /* issue later memory accesses after the cache flush */
+}
+
+static const struct riscv_nonstd_cache_ops ccache_mgmt_ops __initconst = {
+ .wback = &ccache_flush_range,
+ .inv = &ccache_flush_range,
+ .wback_inv = &ccache_flush_range,
+};
+#endif /* CONFIG_RISCV_NONSTANDARD_CACHE_OPS */
+
+static int ccache_largest_wayenabled(void)
+{
+ return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF;
+}
+
+static ssize_t number_of_ways_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", ccache_largest_wayenabled());
+}
+
+static DEVICE_ATTR_RO(number_of_ways_enabled);
+
+static struct attribute *priv_attrs[] = {
+ &dev_attr_number_of_ways_enabled.attr,
+ NULL,
+};
+
+static const struct attribute_group priv_attr_group = {
+ .attrs = priv_attrs,
+};
+
+static const struct attribute_group *ccache_get_priv_group(struct cacheinfo
+ *this_leaf)
+{
+ /* We want to use private group for composable cache only */
+ if (this_leaf->level == level)
+ return &priv_attr_group;
+ else
+ return NULL;
+}
+
+static irqreturn_t ccache_int_handler(int irq, void *device)
+{
+ unsigned int add_h, add_l;
+
+ if (irq == g_irq[DIR_CORR]) {
+ add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_HIGH);
+ add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_LOW);
+ pr_err("DirError @ 0x%08X.%08X\n", add_h, add_l);
+ /* Reading this register clears the DirError interrupt sig */
+ readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_COUNT);
+ atomic_notifier_call_chain(&ccache_err_chain,
+ SIFIVE_CCACHE_ERR_TYPE_CE,
+ "DirECCFix");
+ }
+ if (irq == g_irq[DIR_UNCORR]) {
+ add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_HIGH);
+ add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_LOW);
+ /* Reading this register clears the DirFail interrupt sig */
+ readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_COUNT);
+ atomic_notifier_call_chain(&ccache_err_chain,
+ SIFIVE_CCACHE_ERR_TYPE_UE,
+ "DirECCFail");
+ panic("CCACHE: DirFail @ 0x%08X.%08X\n", add_h, add_l);
+ }
+ if (irq == g_irq[DATA_CORR]) {
+ add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_HIGH);
+ add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_LOW);
+ pr_err("DataError @ 0x%08X.%08X\n", add_h, add_l);
+ /* Reading this register clears the DataError interrupt sig */
+ readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_COUNT);
+ atomic_notifier_call_chain(&ccache_err_chain,
+ SIFIVE_CCACHE_ERR_TYPE_CE,
+ "DatECCFix");
+ }
+ if (irq == g_irq[DATA_UNCORR]) {
+ add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_HIGH);
+ add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_LOW);
+ pr_err("DataFail @ 0x%08X.%08X\n", add_h, add_l);
+ /* Reading this register clears the DataFail interrupt sig */
+ readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_COUNT);
+ atomic_notifier_call_chain(&ccache_err_chain,
+ SIFIVE_CCACHE_ERR_TYPE_UE,
+ "DatECCFail");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int sifive_ccache_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned long quirks;
+ int intr_num, rc;
+
+ quirks = (unsigned long)device_get_match_data(dev);
+
+ intr_num = platform_irq_count(pdev);
+ if (!intr_num)
+ return dev_err_probe(dev, -ENODEV, "No interrupts property\n");
+
+ for (int i = 0; i < intr_num; i++) {
+ if (i == DATA_UNCORR && (quirks & QUIRK_BROKEN_DATA_UNCORR))
+ continue;
+
+ g_irq[i] = platform_get_irq(pdev, i);
+ if (g_irq[i] < 0)
+ return g_irq[i];
+
+ rc = devm_request_irq(dev, g_irq[i], ccache_int_handler, 0, "ccache_ecc", NULL);
+ if (rc)
+ return dev_err_probe(dev, rc, "Could not request IRQ %d\n", g_irq[i]);
+ }
+
+ return 0;
+}
+
+static struct platform_driver sifive_ccache_driver = {
+ .probe = sifive_ccache_probe,
+ .driver = {
+ .name = "sifive_ccache",
+ .of_match_table = sifive_ccache_ids,
+ },
+};
+
+static int __init sifive_ccache_init(void)
+{
+ struct device_node *np;
+ struct resource res;
+ const struct of_device_id *match;
+ unsigned long quirks __maybe_unused;
+ int rc;
+
+ np = of_find_matching_node_and_match(NULL, sifive_ccache_ids, &match);
+ if (!np)
+ return -ENODEV;
+
+ quirks = (uintptr_t)match->data;
+
+ if (of_address_to_resource(np, 0, &res)) {
+ rc = -ENODEV;
+ goto err_node_put;
+ }
+
+ ccache_base = ioremap(res.start, resource_size(&res));
+ if (!ccache_base) {
+ rc = -ENOMEM;
+ goto err_node_put;
+ }
+
+ if (of_property_read_u32(np, "cache-level", &level)) {
+ rc = -ENOENT;
+ goto err_unmap;
+ }
+
+#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
+ if (quirks & QUIRK_NONSTANDARD_CACHE_OPS) {
+ riscv_cbom_block_size = SIFIVE_CCACHE_LINE_SIZE;
+ riscv_noncoherent_supported();
+ riscv_noncoherent_register_cache_ops(&ccache_mgmt_ops);
+ }
+#endif
+
+ ccache_config_read();
+
+ ccache_cache_ops.get_priv_group = ccache_get_priv_group;
+ riscv_set_cacheinfo_ops(&ccache_cache_ops);
+
+#ifdef CONFIG_DEBUG_FS
+ setup_sifive_debug();
+#endif
+
+ rc = platform_driver_register(&sifive_ccache_driver);
+ if (rc)
+ goto err_unmap;
+
+ of_node_put(np);
+
+ return 0;
+
+err_unmap:
+ iounmap(ccache_base);
+err_node_put:
+ of_node_put(np);
+ return rc;
+}
+
+arch_initcall(sifive_ccache_init);
diff --git a/drivers/cache/starfive_starlink_cache.c b/drivers/cache/starfive_starlink_cache.c
new file mode 100644
index 000000000000..24c7d078ca22
--- /dev/null
+++ b/drivers/cache/starfive_starlink_cache.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cache Management Operations for StarFive's Starlink cache controller
+ *
+ * Copyright (C) 2024 Shanghai StarFive Technology Co., Ltd.
+ *
+ * Author: Joshua Yeong <joshua.yeong@starfivetech.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cacheflush.h>
+#include <linux/iopoll.h>
+#include <linux/of_address.h>
+
+#include <asm/dma-noncoherent.h>
+
+#define STARLINK_CACHE_FLUSH_START_ADDR 0x0
+#define STARLINK_CACHE_FLUSH_END_ADDR 0x8
+#define STARLINK_CACHE_FLUSH_CTL 0x10
+#define STARLINK_CACHE_ALIGN 0x40
+
+#define STARLINK_CACHE_ADDRESS_RANGE_MASK GENMASK(39, 0)
+#define STARLINK_CACHE_FLUSH_CTL_MODE_MASK GENMASK(2, 1)
+#define STARLINK_CACHE_FLUSH_CTL_ENABLE_MASK BIT(0)
+
+#define STARLINK_CACHE_FLUSH_CTL_CLEAN_INVALIDATE 0
+#define STARLINK_CACHE_FLUSH_CTL_MAKE_INVALIDATE 1
+#define STARLINK_CACHE_FLUSH_CTL_CLEAN_SHARED 2
+#define STARLINK_CACHE_FLUSH_POLL_DELAY_US 1
+#define STARLINK_CACHE_FLUSH_TIMEOUT_US 5000000
+
+static void __iomem *starlink_cache_base;
+
+static void starlink_cache_flush_complete(void)
+{
+ volatile void __iomem *ctl = starlink_cache_base + STARLINK_CACHE_FLUSH_CTL;
+ u64 v;
+ int ret;
+
+ ret = readq_poll_timeout_atomic(ctl, v, !(v & STARLINK_CACHE_FLUSH_CTL_ENABLE_MASK),
+ STARLINK_CACHE_FLUSH_POLL_DELAY_US,
+ STARLINK_CACHE_FLUSH_TIMEOUT_US);
+ if (ret)
+ WARN(1, "StarFive Starlink cache flush operation timeout\n");
+}
+
+static void starlink_cache_dma_cache_wback(phys_addr_t paddr, unsigned long size)
+{
+ writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
+ writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);
+
+ mb();
+ writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
+ STARLINK_CACHE_FLUSH_CTL_CLEAN_SHARED),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);
+
+ starlink_cache_flush_complete();
+}
+
+static void starlink_cache_dma_cache_invalidate(phys_addr_t paddr, unsigned long size)
+{
+ writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
+ writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);
+
+ mb();
+ writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
+ STARLINK_CACHE_FLUSH_CTL_MAKE_INVALIDATE),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);
+
+ starlink_cache_flush_complete();
+}
+
+static void starlink_cache_dma_cache_wback_inv(phys_addr_t paddr, unsigned long size)
+{
+ writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
+ writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);
+
+ mb();
+ writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
+ STARLINK_CACHE_FLUSH_CTL_CLEAN_INVALIDATE),
+ starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);
+
+ starlink_cache_flush_complete();
+}
+
+static const struct riscv_nonstd_cache_ops starlink_cache_ops = {
+ .wback = &starlink_cache_dma_cache_wback,
+ .inv = &starlink_cache_dma_cache_invalidate,
+ .wback_inv = &starlink_cache_dma_cache_wback_inv,
+};
+
+static const struct of_device_id starlink_cache_ids[] = {
+ { .compatible = "starfive,jh8100-starlink-cache" },
+ { /* sentinel */ }
+};
+
+static int __init starlink_cache_init(void)
+{
+ struct device_node *np;
+ u32 block_size;
+ int ret;
+
+ np = of_find_matching_node(NULL, starlink_cache_ids);
+ if (!of_device_is_available(np))
+ return -ENODEV;
+
+ ret = of_property_read_u32(np, "cache-block-size", &block_size);
+ if (ret)
+ return ret;
+
+ if (block_size % STARLINK_CACHE_ALIGN)
+ return -EINVAL;
+
+ starlink_cache_base = of_iomap(np, 0);
+ if (!starlink_cache_base)
+ return -ENOMEM;
+
+ riscv_cbom_block_size = block_size;
+ riscv_noncoherent_supported();
+ riscv_noncoherent_register_cache_ops(&starlink_cache_ops);
+
+ return 0;
+}
+arch_initcall(starlink_cache_init);
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 416f723a2dbb..31ba1f8c1f78 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -264,6 +264,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/nospec.h>
#include <linux/slab.h>
#include <linux/cdrom.h>
#include <linux/sysctl.h>
@@ -623,9 +624,6 @@ int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi)
if (check_media_type == 1)
cdi->options |= (int) CDO_CHECK_TYPE;
- if (CDROM_CAN(CDC_MRW_W))
- cdi->exit = cdrom_mrw_exit;
-
if (cdi->ops->read_cdda_bpc)
cdi->cdda_method = CDDA_BPC_FULL;
else
@@ -650,9 +648,6 @@ void unregister_cdrom(struct cdrom_device_info *cdi)
list_del(&cdi->list);
mutex_unlock(&cdrom_mutex);
- if (cdi->exit)
- cdi->exit(cdi);
-
cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name);
}
EXPORT_SYMBOL(unregister_cdrom);
@@ -978,15 +973,6 @@ static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi)
cdi->media_written = 0;
}
-static int cdrom_close_write(struct cdrom_device_info *cdi)
-{
-#if 0
- return cdrom_flush_cache(cdi);
-#else
- return 0;
-#endif
-}
-
/* badly broken, I know. Is due for a fixup anytime. */
static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype *tracks)
{
@@ -1114,7 +1100,7 @@ int open_for_data(struct cdrom_device_info *cdi)
}
}
- cd_dbg(CD_OPEN, "all seems well, opening the devicen");
+ cd_dbg(CD_OPEN, "all seems well, opening the device\n");
/* all seems well, we can open the device */
ret = cdo->open(cdi, 0); /* open for data */
@@ -1155,8 +1141,7 @@ clean_up_and_return:
* is in their own interest: device control becomes a lot easier
* this way.
*/
-int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
- fmode_t mode)
+int cdrom_open(struct cdrom_device_info *cdi, blk_mode_t mode)
{
int ret;
@@ -1165,7 +1150,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
/* if this was a O_NONBLOCK open and we should honor the flags,
* do a quick open without drive/disc integrity checks. */
cdi->use_count++;
- if ((mode & FMODE_NDELAY) && (cdi->options & CDO_USE_FFLAGS)) {
+ if ((mode & BLK_OPEN_NDELAY) && (cdi->options & CDO_USE_FFLAGS)) {
ret = cdi->ops->open(cdi, 1);
} else {
ret = open_for_data(cdi);
@@ -1173,7 +1158,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
goto err;
if (CDROM_CAN(CDC_GENERIC_PACKET))
cdrom_mmc3_profile(cdi);
- if (mode & FMODE_WRITE) {
+ if (mode & BLK_OPEN_WRITE) {
ret = -EROFS;
if (cdrom_open_write(cdi))
goto err_release;
@@ -1182,6 +1167,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
ret = 0;
cdi->media_written = 0;
}
+ cdi->opened_for_data = true;
}
if (ret)
@@ -1259,10 +1245,9 @@ static int check_for_audio_disc(struct cdrom_device_info *cdi,
return 0;
}
-void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode)
+void cdrom_release(struct cdrom_device_info *cdi)
{
const struct cdrom_device_ops *cdo = cdi->ops;
- int opened_for_data;
cd_dbg(CD_CLOSE, "entering cdrom_release\n");
@@ -1273,6 +1258,8 @@ void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode)
cd_dbg(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n",
cdi->name);
cdrom_dvd_rw_close_write(cdi);
+ if (CDROM_CAN(CDC_MRW_W))
+ cdrom_mrw_exit(cdi);
if ((cdo->capability & CDC_LOCK) && !cdi->keeplocked) {
cd_dbg(CD_CLOSE, "Unlocking door!\n");
@@ -1280,20 +1267,12 @@ void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode)
}
}
- opened_for_data = !(cdi->options & CDO_USE_FFLAGS) ||
- !(mode & FMODE_NDELAY);
-
- /*
- * flush cache on last write release
- */
- if (CDROM_CAN(CDC_RAM) && !cdi->use_count && cdi->for_data)
- cdrom_close_write(cdi);
-
cdo->release(cdi);
- if (cdi->use_count == 0) { /* last process that closes dev*/
- if (opened_for_data &&
- cdi->options & CDO_AUTO_EJECT && CDROM_CAN(CDC_OPEN_TRAY))
+
+ if (cdi->use_count == 0 && cdi->opened_for_data) {
+ if (cdi->options & CDO_AUTO_EJECT && CDROM_CAN(CDC_OPEN_TRAY))
cdo->tray_move(cdi, 1);
+ cdi->opened_for_data = false;
}
}
EXPORT_SYMBOL(cdrom_release);
@@ -2329,6 +2308,9 @@ static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
if (arg >= cdi->capacity)
return -EINVAL;
+ /* Prevent arg from speculatively bypassing the length check */
+ arg = array_index_nospec(arg, cdi->capacity);
+
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -2372,7 +2354,7 @@ static int cdrom_ioctl_timed_media_change(struct cdrom_device_info *cdi,
return -EFAULT;
tmp_info.media_flags = 0;
- if (tmp_info.last_media_change - cdi->last_media_change_ms < 0)
+ if (cdi->last_media_change_ms > tmp_info.last_media_change)
tmp_info.media_flags |= MEDIA_CHANGED_FLAG;
tmp_info.last_media_change = cdi->last_media_change_ms;
@@ -3337,7 +3319,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
* ATAPI / SCSI specific code now mainly resides in mmc_ioctl().
*/
int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
- fmode_t mode, unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int ret;
@@ -3487,7 +3469,7 @@ static int cdrom_print_info(const char *header, int val, char *info,
return 0;
}
-static int cdrom_sysctl_info(struct ctl_table *ctl, int write,
+static int cdrom_sysctl_info(const struct ctl_table *ctl, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int pos;
@@ -3600,7 +3582,7 @@ static void cdrom_update_settings(void)
mutex_unlock(&cdrom_mutex);
}
-static int cdrom_sysctl_handler(struct ctl_table *ctl, int write,
+static int cdrom_sysctl_handler(const struct ctl_table *ctl, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
@@ -3626,7 +3608,7 @@ static int cdrom_sysctl_handler(struct ctl_table *ctl, int write,
}
/* Place files in /proc/sys/dev/cdrom */
-static struct ctl_table cdrom_table[] = {
+static const struct ctl_table cdrom_table[] = {
{
.procname = "info",
.data = &cdrom_sysctl_settings.info,
@@ -3669,7 +3651,6 @@ static struct ctl_table cdrom_table[] = {
.mode = 0644,
.proc_handler = cdrom_sysctl_handler
},
- { }
};
static struct ctl_table_header *cdrom_sysctl_header;
@@ -3692,8 +3673,7 @@ static void cdrom_sysctl_register(void)
static void cdrom_sysctl_unregister(void)
{
- if (cdrom_sysctl_header)
- unregister_sysctl_table(cdrom_sysctl_header);
+ unregister_sysctl_table(cdrom_sysctl_header);
}
#else /* CONFIG_SYSCTL */
@@ -3723,4 +3703,5 @@ static void __exit cdrom_exit(void)
module_init(cdrom_init);
module_exit(cdrom_exit);
+MODULE_DESCRIPTION("Uniform CD-ROM driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index ceded5772aac..85aceab5eac6 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -474,22 +474,22 @@ static const struct cdrom_device_ops gdrom_ops = {
CDC_RESET | CDC_DRIVE_STATUS | CDC_CD_R,
};
-static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode)
+static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
{
int ret;
- bdev_check_media_change(bdev);
+ disk_check_media_change(disk);
mutex_lock(&gdrom_mutex);
- ret = cdrom_open(gd.cd_info, bdev, mode);
+ ret = cdrom_open(gd.cd_info, mode);
mutex_unlock(&gdrom_mutex);
return ret;
}
-static void gdrom_bdops_release(struct gendisk *disk, fmode_t mode)
+static void gdrom_bdops_release(struct gendisk *disk)
{
mutex_lock(&gdrom_mutex);
- cdrom_release(gd.cd_info, mode);
+ cdrom_release(gd.cd_info);
mutex_unlock(&gdrom_mutex);
}
@@ -499,13 +499,13 @@ static unsigned int gdrom_bdops_check_events(struct gendisk *disk,
return cdrom_check_events(gd.cd_info, clearing);
}
-static int gdrom_bdops_ioctl(struct block_device *bdev, fmode_t mode,
+static int gdrom_bdops_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned cmd, unsigned long arg)
{
int ret;
mutex_lock(&gdrom_mutex);
- ret = cdrom_ioctl(gd.cd_info, bdev, mode, cmd, arg);
+ ret = cdrom_ioctl(gd.cd_info, bdev, cmd, arg);
mutex_unlock(&gdrom_mutex);
return ret;
@@ -724,11 +724,6 @@ static void probe_gdrom_setupdisk(void)
static int probe_gdrom_setupqueue(void)
{
- blk_queue_logical_block_size(gd.gdrom_rq, GDROM_HARD_SECTOR);
- /* using DMA so memory will need to be contiguous */
- blk_queue_max_segments(gd.gdrom_rq, 1);
- /* set a large max size to get most from DMA */
- blk_queue_max_segment_size(gd.gdrom_rq, 0x40000);
gd.disk->queue = gd.gdrom_rq;
return gdrom_init_dma_mode();
}
@@ -743,6 +738,14 @@ static const struct blk_mq_ops gdrom_mq_ops = {
*/
static int probe_gdrom(struct platform_device *devptr)
{
+ struct queue_limits lim = {
+ .logical_block_size = GDROM_HARD_SECTOR,
+ /* using DMA so memory will need to be contiguous */
+ .max_segments = 1,
+ /* set a large max size to get most from DMA */
+ .max_segment_size = 0x40000,
+ .features = BLK_FEAT_ROTATIONAL,
+ };
int err;
/*
@@ -774,11 +777,11 @@ static int probe_gdrom(struct platform_device *devptr)
probe_gdrom_setupcd();
err = blk_mq_alloc_sq_tag_set(&gd.tag_set, &gdrom_mq_ops, 1,
- BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING);
+ BLK_MQ_F_BLOCKING);
if (err)
goto probe_fail_free_cd_info;
- gd.disk = blk_mq_alloc_disk(&gd.tag_set, NULL);
+ gd.disk = blk_mq_alloc_disk(&gd.tag_set, &lim, NULL);
if (IS_ERR(gd.disk)) {
err = PTR_ERR(gd.disk);
goto probe_fail_free_tag_set;
@@ -829,7 +832,7 @@ probe_fail_no_mem:
return err;
}
-static int remove_gdrom(struct platform_device *devptr)
+static void remove_gdrom(struct platform_device *devptr)
{
blk_mq_free_tag_set(&gd.tag_set);
free_irq(HW_EVENT_GDROM_CMD, &gd);
@@ -840,8 +843,6 @@ static int remove_gdrom(struct platform_device *devptr)
unregister_cdrom(gd.cd_info);
kfree(gd.cd_info);
kfree(gd.toc);
-
- return 0;
}
static struct platform_driver gdrom_driver = {
diff --git a/drivers/cdx/Kconfig b/drivers/cdx/Kconfig
new file mode 100644
index 000000000000..1f1e360507d7
--- /dev/null
+++ b/drivers/cdx/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# CDX bus configuration
+#
+# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+#
+
+config CDX_BUS
+ bool "CDX Bus driver"
+ depends on OF && ARM64 || COMPILE_TEST
+ help
+ Driver to enable Composable DMA Transfer(CDX) Bus. CDX bus
+ exposes Fabric devices which uses composable DMA IP to the
+ APU. CDX bus provides a mechanism for scanning and probing
+ of CDX devices. CDX devices are memory mapped on system bus
+ for embedded CPUs. CDX bus uses CDX controller and firmware
+ to scan these CDX devices.
+
+source "drivers/cdx/controller/Kconfig"
diff --git a/drivers/cdx/Makefile b/drivers/cdx/Makefile
new file mode 100644
index 000000000000..3ca7068a3052
--- /dev/null
+++ b/drivers/cdx/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for CDX
+#
+# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+#
+
+ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"CDX_BUS"'
+
+obj-$(CONFIG_CDX_BUS) += cdx.o controller/
+
+ifdef CONFIG_GENERIC_MSI_IRQ
+obj-$(CONFIG_CDX_BUS) += cdx_msi.o
+endif
diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c
new file mode 100644
index 000000000000..b39af2f1937f
--- /dev/null
+++ b/drivers/cdx/cdx.c
@@ -0,0 +1,978 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CDX bus driver.
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+/*
+ * Architecture Overview
+ * =====================
+ * CDX is a Hardware Architecture designed for AMD FPGA devices. It
+ * consists of sophisticated mechanism for interaction between FPGA,
+ * Firmware and the APUs (Application CPUs).
+ *
+ * Firmware resides on RPU (Realtime CPUs) which interacts with
+ * the FPGA program manager and the APUs. The RPU provides memory-mapped
+ * interface (RPU if) which is used to communicate with APUs.
+ *
+ * The diagram below shows an overview of the CDX architecture:
+ *
+ * +--------------------------------------+
+ * | Application CPUs (APU) |
+ * | |
+ * | CDX device drivers|
+ * | Linux OS | |
+ * | CDX bus |
+ * | | |
+ * | CDX controller |
+ * | | |
+ * +-----------------------------|--------+
+ * | (discover, config,
+ * | reset, rescan)
+ * |
+ * +------------------------| RPU if |----+
+ * | | |
+ * | V |
+ * | Realtime CPUs (RPU) |
+ * | |
+ * +--------------------------------------+
+ * |
+ * +---------------------|----------------+
+ * | FPGA | |
+ * | +-----------------------+ |
+ * | | | | |
+ * | +-------+ +-------+ +-------+ |
+ * | | dev 1 | | dev 2 | | dev 3 | |
+ * | +-------+ +-------+ +-------+ |
+ * +--------------------------------------+
+ *
+ * The RPU firmware extracts the device information from the loaded FPGA
+ * image and implements a mechanism that allows the APU drivers to
+ * enumerate such devices (device personality and resource details) via
+ * a dedicated communication channel. RPU mediates operations such as
+ * discover, reset and rescan of the FPGA devices for the APU. This is
+ * done using memory mapped interface provided by the RPU to APU.
+ */
+
+#include <linux/init.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/idr.h>
+#include <linux/cdx/cdx_bus.h>
+#include <linux/iommu.h>
+#include <linux/dma-map-ops.h>
+#include <linux/debugfs.h>
+#include "cdx.h"
+
+/* Default DMA mask for devices on a CDX bus */
+#define CDX_DEFAULT_DMA_MASK (~0ULL)
+#define MAX_CDX_CONTROLLERS 16
+
+/* IDA for CDX controllers registered with the CDX bus */
+static DEFINE_IDA(cdx_controller_ida);
+/* Lock to protect controller ops */
+static DEFINE_MUTEX(cdx_controller_lock);
+/* Debugfs dir for cdx bus */
+static struct dentry *cdx_debugfs_dir;
+
+static char *compat_node_name = "xlnx,versal-net-cdx";
+
+static void cdx_destroy_res_attr(struct cdx_device *cdx_dev, int num);
+
+/**
+ * cdx_dev_reset - Reset a CDX device
+ * @dev: CDX device
+ *
+ * Return: -errno on failure, 0 on success.
+ */
+int cdx_dev_reset(struct device *dev)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ struct cdx_device_config dev_config = {0};
+ struct cdx_driver *cdx_drv;
+ int ret;
+
+ cdx_drv = to_cdx_driver(dev->driver);
+ /* Notify driver that device is being reset */
+ if (cdx_drv && cdx_drv->reset_prepare)
+ cdx_drv->reset_prepare(cdx_dev);
+
+ dev_config.type = CDX_DEV_RESET_CONF;
+ ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num,
+ cdx_dev->dev_num, &dev_config);
+ if (ret)
+ dev_err(dev, "cdx device reset failed\n");
+
+ /* Notify driver that device reset is complete */
+ if (cdx_drv && cdx_drv->reset_done)
+ cdx_drv->reset_done(cdx_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cdx_dev_reset);
+
+/**
+ * reset_cdx_device - Reset a CDX device
+ * @dev: CDX device
+ * @data: This is always passed as NULL, and is not used in this API,
+ * but is required here as the device_for_each_child() API expects
+ * the passed function to have this as an argument.
+ *
+ * Return: -errno on failure, 0 on success.
+ */
+static int reset_cdx_device(struct device *dev, void *data)
+{
+ return cdx_dev_reset(dev);
+}
+
+/**
+ * cdx_unregister_device - Unregister a CDX device
+ * @dev: CDX device
+ * @data: This is always passed as NULL, and is not used in this API,
+ * but is required here as the bus_for_each_dev() API expects
+ * the passed function (cdx_unregister_device) to have this
+ * as an argument.
+ *
+ * Return: 0 on success.
+ */
+static int cdx_unregister_device(struct device *dev,
+ void *data)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+
+ if (cdx_dev->is_bus) {
+ device_for_each_child(dev, NULL, cdx_unregister_device);
+ if (cdx_dev->enabled && cdx->ops->bus_disable)
+ cdx->ops->bus_disable(cdx, cdx_dev->bus_num);
+ } else {
+ cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES);
+ debugfs_remove_recursive(cdx_dev->debugfs_dir);
+ kfree(cdx_dev->driver_override);
+ cdx_dev->driver_override = NULL;
+ }
+
+ /*
+ * Do not free cdx_dev here as it would be freed in
+ * cdx_device_release() called from within put_device().
+ */
+ device_del(&cdx_dev->dev);
+ put_device(&cdx_dev->dev);
+
+ return 0;
+}
+
+static void cdx_unregister_devices(const struct bus_type *bus)
+{
+ /* Reset all the devices attached to cdx bus */
+ bus_for_each_dev(bus, NULL, NULL, cdx_unregister_device);
+}
+
+/**
+ * cdx_match_one_device - Tell if a CDX device structure has a matching
+ * CDX device id structure
+ * @id: single CDX device id structure to match
+ * @dev: the CDX device structure to match against
+ *
+ * Return: matching cdx_device_id structure or NULL if there is no match.
+ */
+static inline const struct cdx_device_id *
+cdx_match_one_device(const struct cdx_device_id *id,
+ const struct cdx_device *dev)
+{
+ /* Use vendor ID and device ID for matching */
+ if ((id->vendor == CDX_ANY_ID || id->vendor == dev->vendor) &&
+ (id->device == CDX_ANY_ID || id->device == dev->device) &&
+ (id->subvendor == CDX_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
+ (id->subdevice == CDX_ANY_ID || id->subdevice == dev->subsystem_device) &&
+ !((id->class ^ dev->class) & id->class_mask))
+ return id;
+ return NULL;
+}
+
+/**
+ * cdx_match_id - See if a CDX device matches a given cdx_id table
+ * @ids: array of CDX device ID structures to search in
+ * @dev: the CDX device structure to match against.
+ *
+ * Used by a driver to check whether a CDX device is in its list of
+ * supported devices. Returns the matching cdx_device_id structure or
+ * NULL if there is no match.
+ *
+ * Return: matching cdx_device_id structure or NULL if there is no match.
+ */
+static inline const struct cdx_device_id *
+cdx_match_id(const struct cdx_device_id *ids, struct cdx_device *dev)
+{
+ if (ids) {
+ while (ids->vendor || ids->device) {
+ if (cdx_match_one_device(ids, dev))
+ return ids;
+ ids++;
+ }
+ }
+ return NULL;
+}
+
+int cdx_set_master(struct cdx_device *cdx_dev)
+{
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ struct cdx_device_config dev_config;
+ int ret = -EOPNOTSUPP;
+
+ dev_config.type = CDX_DEV_BUS_MASTER_CONF;
+ dev_config.bus_master_enable = true;
+ if (cdx->ops->dev_configure)
+ ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num,
+ cdx_dev->dev_num, &dev_config);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cdx_set_master);
+
+int cdx_clear_master(struct cdx_device *cdx_dev)
+{
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ struct cdx_device_config dev_config;
+ int ret = -EOPNOTSUPP;
+
+ dev_config.type = CDX_DEV_BUS_MASTER_CONF;
+ dev_config.bus_master_enable = false;
+ if (cdx->ops->dev_configure)
+ ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num,
+ cdx_dev->dev_num, &dev_config);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cdx_clear_master);
+
+/**
+ * cdx_bus_match - device to driver matching callback
+ * @dev: the cdx device to match against
+ * @drv: the device driver to search for matching cdx device
+ * structures
+ *
+ * Return: true on success, false otherwise.
+ */
+static int cdx_bus_match(struct device *dev, const struct device_driver *drv)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ const struct cdx_driver *cdx_drv = to_cdx_driver(drv);
+ const struct cdx_device_id *found_id = NULL;
+ const struct cdx_device_id *ids;
+
+ if (cdx_dev->is_bus)
+ return false;
+
+ ids = cdx_drv->match_id_table;
+
+ /* When driver_override is set, only bind to the matching driver */
+ if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name))
+ return false;
+
+ found_id = cdx_match_id(ids, cdx_dev);
+ if (!found_id)
+ return false;
+
+ do {
+ /*
+ * In case override_only was set, enforce driver_override
+ * matching.
+ */
+ if (!found_id->override_only)
+ return true;
+ if (cdx_dev->driver_override)
+ return true;
+
+ ids = found_id + 1;
+ found_id = cdx_match_id(ids, cdx_dev);
+ } while (found_id);
+
+ return false;
+}
+
+static int cdx_probe(struct device *dev)
+{
+ struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ int error;
+
+ /*
+ * Setup MSI device data so that generic MSI alloc/free can
+ * be used by the device driver.
+ */
+ if (IS_ENABLED(CONFIG_GENERIC_MSI_IRQ) && cdx->msi_domain) {
+ error = msi_setup_device_data(&cdx_dev->dev);
+ if (error)
+ return error;
+ }
+
+ error = cdx_drv->probe(cdx_dev);
+ if (error) {
+ dev_err_probe(dev, error, "%s failed\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void cdx_remove(struct device *dev)
+{
+ struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ if (cdx_drv && cdx_drv->remove)
+ cdx_drv->remove(cdx_dev);
+}
+
+static void cdx_shutdown(struct device *dev)
+{
+ struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+
+ if (cdx_dev->is_bus && cdx_dev->enabled && cdx->ops->bus_disable)
+ cdx->ops->bus_disable(cdx, cdx_dev->bus_num);
+ if (cdx_drv && cdx_drv->shutdown)
+ cdx_drv->shutdown(cdx_dev);
+}
+
+static int cdx_dma_configure(struct device *dev)
+{
+ struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ u32 input_id = cdx_dev->req_id;
+ int ret;
+
+ ret = of_dma_configure_id(dev, cdx->dev->of_node, 0, &input_id);
+ if (ret && ret != -EPROBE_DEFER) {
+ dev_err(dev, "of_dma_configure_id() failed\n");
+ return ret;
+ }
+
+ /* @cdx_drv may not be valid when we're called from the IOMMU layer */
+ if (!ret && dev->driver && !cdx_drv->driver_managed_dma) {
+ ret = iommu_device_use_default_domain(dev);
+ if (ret)
+ arch_teardown_dma_ops(dev);
+ }
+
+ return 0;
+}
+
+static void cdx_dma_cleanup(struct device *dev)
+{
+ struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
+
+ if (!cdx_drv->driver_managed_dma)
+ iommu_device_unuse_default_domain(dev);
+}
+
+/* show configuration fields */
+#define cdx_config_attr(field, format_string) \
+static ssize_t \
+field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct cdx_device *cdx_dev = to_cdx_device(dev); \
+ return sysfs_emit(buf, format_string, cdx_dev->field); \
+} \
+static DEVICE_ATTR_RO(field)
+
+cdx_config_attr(vendor, "0x%04x\n");
+cdx_config_attr(device, "0x%04x\n");
+cdx_config_attr(subsystem_vendor, "0x%04x\n");
+cdx_config_attr(subsystem_device, "0x%04x\n");
+cdx_config_attr(revision, "0x%02x\n");
+cdx_config_attr(class, "0x%06x\n");
+
+static ssize_t remove_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ bool val;
+
+ if (kstrtobool(buf, &val) < 0)
+ return -EINVAL;
+
+ if (!val)
+ return -EINVAL;
+
+ if (device_remove_file_self(dev, attr)) {
+ int ret;
+
+ ret = cdx_unregister_device(dev, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_WO(remove);
+
+static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ bool val;
+ int ret;
+
+ if (kstrtobool(buf, &val) < 0)
+ return -EINVAL;
+
+ if (!val)
+ return -EINVAL;
+
+ if (cdx_dev->is_bus)
+ /* Reset all the devices attached to cdx bus */
+ ret = device_for_each_child(dev, NULL, reset_cdx_device);
+ else
+ ret = cdx_dev_reset(dev);
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ return sprintf(buf, "cdx:v%04Xd%04Xsv%04Xsd%04Xc%06X\n", cdx_dev->vendor,
+ cdx_dev->device, cdx_dev->subsystem_vendor, cdx_dev->subsystem_device,
+ cdx_dev->class);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static ssize_t driver_override_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ int ret;
+
+ if (WARN_ON(dev->bus != &cdx_bus_type))
+ return -EINVAL;
+
+ ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t driver_override_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ ssize_t len;
+
+ device_lock(dev);
+ len = sysfs_emit(buf, "%s\n", cdx_dev->driver_override);
+ device_unlock(dev);
+ return len;
+}
+static DEVICE_ATTR_RW(driver_override);
+
+static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ bool enable;
+ int ret;
+
+ if (kstrtobool(buf, &enable) < 0)
+ return -EINVAL;
+
+ if (enable == cdx_dev->enabled)
+ return count;
+
+ if (enable && cdx->ops->bus_enable)
+ ret = cdx->ops->bus_enable(cdx, cdx_dev->bus_num);
+ else if (!enable && cdx->ops->bus_disable)
+ ret = cdx->ops->bus_disable(cdx, cdx_dev->bus_num);
+ else
+ ret = -EOPNOTSUPP;
+
+ if (!ret)
+ cdx_dev->enabled = enable;
+
+ return ret < 0 ? ret : count;
+}
+
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ return sysfs_emit(buf, "%u\n", cdx_dev->enabled);
+}
+static DEVICE_ATTR_RW(enable);
+
+static umode_t cdx_dev_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct cdx_device *cdx_dev;
+
+ cdx_dev = to_cdx_device(dev);
+ if (!cdx_dev->is_bus)
+ return a->mode;
+
+ return 0;
+}
+
+static umode_t cdx_bus_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct cdx_device *cdx_dev;
+
+ cdx_dev = to_cdx_device(dev);
+ if (cdx_dev->is_bus)
+ return a->mode;
+
+ return 0;
+}
+
+static struct attribute *cdx_dev_attrs[] = {
+ &dev_attr_remove.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_vendor.attr,
+ &dev_attr_device.attr,
+ &dev_attr_subsystem_vendor.attr,
+ &dev_attr_subsystem_device.attr,
+ &dev_attr_class.attr,
+ &dev_attr_revision.attr,
+ &dev_attr_modalias.attr,
+ &dev_attr_driver_override.attr,
+ NULL,
+};
+
+static const struct attribute_group cdx_dev_group = {
+ .attrs = cdx_dev_attrs,
+ .is_visible = cdx_dev_attrs_are_visible,
+};
+
+static struct attribute *cdx_bus_dev_attrs[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_reset.attr,
+ NULL,
+};
+
+static const struct attribute_group cdx_bus_dev_group = {
+ .attrs = cdx_bus_dev_attrs,
+ .is_visible = cdx_bus_attrs_are_visible,
+};
+
+static const struct attribute_group *cdx_dev_groups[] = {
+ &cdx_dev_group,
+ &cdx_bus_dev_group,
+ NULL,
+};
+
+static int cdx_debug_resource_show(struct seq_file *s, void *data)
+{
+ struct cdx_device *cdx_dev = s->private;
+ int i;
+
+ for (i = 0; i < MAX_CDX_DEV_RESOURCES; i++) {
+ struct resource *res = &cdx_dev->res[i];
+
+ seq_printf(s, "%pr\n", res);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(cdx_debug_resource);
+
+static void cdx_device_debugfs_init(struct cdx_device *cdx_dev)
+{
+ cdx_dev->debugfs_dir = debugfs_create_dir(dev_name(&cdx_dev->dev), cdx_debugfs_dir);
+ if (IS_ERR(cdx_dev->debugfs_dir))
+ return;
+
+ debugfs_create_file("resource", 0444, cdx_dev->debugfs_dir, cdx_dev,
+ &cdx_debug_resource_fops);
+}
+
+static ssize_t rescan_store(const struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ struct cdx_controller *cdx;
+ struct platform_device *pd;
+ struct device_node *np;
+ bool val;
+
+ if (kstrtobool(buf, &val) < 0)
+ return -EINVAL;
+
+ if (!val)
+ return -EINVAL;
+
+ mutex_lock(&cdx_controller_lock);
+
+ /* Unregister all the devices on the bus */
+ cdx_unregister_devices(&cdx_bus_type);
+
+ /* Rescan all the devices */
+ for_each_compatible_node(np, NULL, compat_node_name) {
+ pd = of_find_device_by_node(np);
+ if (!pd) {
+ of_node_put(np);
+ count = -EINVAL;
+ goto unlock;
+ }
+
+ cdx = platform_get_drvdata(pd);
+ if (cdx && cdx->controller_registered && cdx->ops->scan)
+ cdx->ops->scan(cdx);
+
+ put_device(&pd->dev);
+ }
+
+unlock:
+ mutex_unlock(&cdx_controller_lock);
+
+ return count;
+}
+static BUS_ATTR_WO(rescan);
+
+static struct attribute *cdx_bus_attrs[] = {
+ &bus_attr_rescan.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(cdx_bus);
+
+const struct bus_type cdx_bus_type = {
+ .name = "cdx",
+ .match = cdx_bus_match,
+ .probe = cdx_probe,
+ .remove = cdx_remove,
+ .shutdown = cdx_shutdown,
+ .dma_configure = cdx_dma_configure,
+ .dma_cleanup = cdx_dma_cleanup,
+ .bus_groups = cdx_bus_groups,
+ .dev_groups = cdx_dev_groups,
+};
+EXPORT_SYMBOL_GPL(cdx_bus_type);
+
+int __cdx_driver_register(struct cdx_driver *cdx_driver,
+ struct module *owner)
+{
+ int error;
+
+ cdx_driver->driver.owner = owner;
+ cdx_driver->driver.bus = &cdx_bus_type;
+
+ error = driver_register(&cdx_driver->driver);
+ if (error) {
+ pr_err("driver_register() failed for %s: %d\n",
+ cdx_driver->driver.name, error);
+ return error;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__cdx_driver_register);
+
+void cdx_driver_unregister(struct cdx_driver *cdx_driver)
+{
+ driver_unregister(&cdx_driver->driver);
+}
+EXPORT_SYMBOL_GPL(cdx_driver_unregister);
+
+static void cdx_device_release(struct device *dev)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+
+ kfree(cdx_dev);
+}
+
+static const struct vm_operations_struct cdx_phys_vm_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ .access = generic_access_phys,
+#endif
+};
+
+/**
+ * cdx_mmap_resource - map a CDX resource into user memory space
+ * @fp: File pointer. Not used in this function, but required where
+ * this API is registered as a callback.
+ * @kobj: kobject for mapping
+ * @attr: struct bin_attribute for the file being mapped
+ * @vma: struct vm_area_struct passed into the mmap
+ *
+ * Use the regular CDX mapping routines to map a CDX resource into userspace.
+ *
+ * Return: true on success, false otherwise.
+ */
+static int cdx_mmap_resource(struct file *fp, struct kobject *kobj,
+ const struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(kobj_to_dev(kobj));
+ int num = (unsigned long)attr->private;
+ struct resource *res;
+ unsigned long size;
+
+ res = &cdx_dev->res[num];
+ if (iomem_is_exclusive(res->start))
+ return -EINVAL;
+
+ /* Make sure the caller is mapping a valid resource for this device */
+ size = ((cdx_resource_len(cdx_dev, num) - 1) >> PAGE_SHIFT) + 1;
+ if (vma->vm_pgoff + vma_pages(vma) > size)
+ return -EINVAL;
+
+ /*
+ * Map memory region and vm->vm_pgoff is expected to be an
+ * offset within that region.
+ */
+ vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
+ vma->vm_pgoff += (cdx_resource_start(cdx_dev, num) >> PAGE_SHIFT);
+ vma->vm_ops = &cdx_phys_vm_ops;
+ return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+}
+
+static void cdx_destroy_res_attr(struct cdx_device *cdx_dev, int num)
+{
+ int i;
+
+ /* removing the bin attributes */
+ for (i = 0; i < num; i++) {
+ struct bin_attribute *res_attr;
+
+ res_attr = cdx_dev->res_attr[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&cdx_dev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
+ }
+}
+
+#define CDX_RES_ATTR_NAME_LEN 10
+static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num)
+{
+ struct bin_attribute *res_attr;
+ char *res_attr_name;
+ int ret;
+
+ res_attr = kzalloc(sizeof(*res_attr) + CDX_RES_ATTR_NAME_LEN, GFP_ATOMIC);
+ if (!res_attr)
+ return -ENOMEM;
+
+ res_attr_name = (char *)(res_attr + 1);
+
+ sysfs_bin_attr_init(res_attr);
+
+ cdx_dev->res_attr[num] = res_attr;
+ sprintf(res_attr_name, "resource%d", num);
+
+ res_attr->mmap = cdx_mmap_resource;
+ res_attr->attr.name = res_attr_name;
+ res_attr->attr.mode = 0600;
+ res_attr->size = cdx_resource_len(cdx_dev, num);
+ res_attr->private = (void *)(unsigned long)num;
+ ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr);
+ if (ret)
+ kfree(res_attr);
+
+ return ret;
+}
+
+int cdx_device_add(struct cdx_dev_params *dev_params)
+{
+ struct cdx_controller *cdx = dev_params->cdx;
+ struct cdx_device *cdx_dev;
+ int ret, i;
+
+ cdx_dev = kzalloc(sizeof(*cdx_dev), GFP_KERNEL);
+ if (!cdx_dev)
+ return -ENOMEM;
+
+ /* Populate resource */
+ memcpy(cdx_dev->res, dev_params->res, sizeof(struct resource) *
+ dev_params->res_count);
+ cdx_dev->res_count = dev_params->res_count;
+
+ /* Populate CDX dev params */
+ cdx_dev->req_id = dev_params->req_id;
+ cdx_dev->msi_dev_id = dev_params->msi_dev_id;
+ cdx_dev->vendor = dev_params->vendor;
+ cdx_dev->device = dev_params->device;
+ cdx_dev->subsystem_vendor = dev_params->subsys_vendor;
+ cdx_dev->subsystem_device = dev_params->subsys_device;
+ cdx_dev->class = dev_params->class;
+ cdx_dev->revision = dev_params->revision;
+ cdx_dev->bus_num = dev_params->bus_num;
+ cdx_dev->dev_num = dev_params->dev_num;
+ cdx_dev->cdx = dev_params->cdx;
+ cdx_dev->dma_mask = CDX_DEFAULT_DMA_MASK;
+
+ /* Initialize generic device */
+ device_initialize(&cdx_dev->dev);
+ cdx_dev->dev.parent = dev_params->parent;
+ cdx_dev->dev.bus = &cdx_bus_type;
+ cdx_dev->dev.dma_mask = &cdx_dev->dma_mask;
+ cdx_dev->dev.release = cdx_device_release;
+ cdx_dev->msi_write_pending = false;
+ mutex_init(&cdx_dev->irqchip_lock);
+
+ /* Set Name */
+ dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x",
+ ((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)),
+ cdx_dev->dev_num);
+
+ if (IS_ENABLED(CONFIG_GENERIC_MSI_IRQ) && cdx->msi_domain) {
+ cdx_dev->num_msi = dev_params->num_msi;
+ dev_set_msi_domain(&cdx_dev->dev, cdx->msi_domain);
+ }
+
+ ret = device_add(&cdx_dev->dev);
+ if (ret) {
+ dev_err(&cdx_dev->dev,
+ "cdx device add failed: %d", ret);
+ goto fail;
+ }
+
+ /* Create resource<N> attributes */
+ for (i = 0; i < MAX_CDX_DEV_RESOURCES; i++) {
+ if (cdx_resource_flags(cdx_dev, i) & IORESOURCE_MEM) {
+ /* skip empty resources */
+ if (!cdx_resource_len(cdx_dev, i))
+ continue;
+
+ ret = cdx_create_res_attr(cdx_dev, i);
+ if (ret != 0) {
+ dev_err(&cdx_dev->dev,
+ "cdx device resource<%d> file creation failed: %d", i, ret);
+ goto resource_create_fail;
+ }
+ }
+ }
+
+ cdx_device_debugfs_init(cdx_dev);
+
+ return 0;
+resource_create_fail:
+ cdx_destroy_res_attr(cdx_dev, i);
+ device_del(&cdx_dev->dev);
+fail:
+ /*
+ * Do not free cdx_dev here as it would be freed in
+ * cdx_device_release() called from put_device().
+ */
+ put_device(&cdx_dev->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cdx_device_add, "CDX_BUS_CONTROLLER");
+
+struct device *cdx_bus_add(struct cdx_controller *cdx, u8 bus_num)
+{
+ struct cdx_device *cdx_dev;
+ int ret;
+
+ cdx_dev = kzalloc(sizeof(*cdx_dev), GFP_KERNEL);
+ if (!cdx_dev)
+ return NULL;
+
+ device_initialize(&cdx_dev->dev);
+ cdx_dev->cdx = cdx;
+
+ cdx_dev->dev.parent = cdx->dev;
+ cdx_dev->dev.bus = &cdx_bus_type;
+ cdx_dev->dev.release = cdx_device_release;
+ cdx_dev->is_bus = true;
+ cdx_dev->bus_num = bus_num;
+
+ dev_set_name(&cdx_dev->dev, "cdx-%02x",
+ ((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (bus_num & CDX_BUS_NUM_MASK)));
+
+ ret = device_add(&cdx_dev->dev);
+ if (ret) {
+ dev_err(&cdx_dev->dev, "cdx bus device add failed: %d\n", ret);
+ goto device_add_fail;
+ }
+
+ if (cdx->ops->bus_enable) {
+ ret = cdx->ops->bus_enable(cdx, bus_num);
+ if (ret && ret != -EALREADY) {
+ dev_err(cdx->dev, "cdx bus enable failed: %d\n", ret);
+ goto bus_enable_fail;
+ }
+ }
+
+ cdx_dev->enabled = true;
+ return &cdx_dev->dev;
+
+bus_enable_fail:
+ device_del(&cdx_dev->dev);
+device_add_fail:
+ put_device(&cdx_dev->dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(cdx_bus_add, "CDX_BUS_CONTROLLER");
+
+int cdx_register_controller(struct cdx_controller *cdx)
+{
+ int ret;
+
+ ret = ida_alloc_range(&cdx_controller_ida, 0, MAX_CDX_CONTROLLERS - 1, GFP_KERNEL);
+ if (ret < 0) {
+ dev_err(cdx->dev,
+ "No free index available. Maximum controllers already registered\n");
+ cdx->id = (u8)MAX_CDX_CONTROLLERS;
+ return ret;
+ }
+
+ mutex_lock(&cdx_controller_lock);
+ cdx->id = ret;
+
+ /* Scan all the devices */
+ if (cdx->ops->scan)
+ cdx->ops->scan(cdx);
+ cdx->controller_registered = true;
+ mutex_unlock(&cdx_controller_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cdx_register_controller, "CDX_BUS_CONTROLLER");
+
+void cdx_unregister_controller(struct cdx_controller *cdx)
+{
+ if (cdx->id >= MAX_CDX_CONTROLLERS)
+ return;
+
+ mutex_lock(&cdx_controller_lock);
+
+ cdx->controller_registered = false;
+ device_for_each_child(cdx->dev, NULL, cdx_unregister_device);
+ ida_free(&cdx_controller_ida, cdx->id);
+
+ mutex_unlock(&cdx_controller_lock);
+}
+EXPORT_SYMBOL_NS_GPL(cdx_unregister_controller, "CDX_BUS_CONTROLLER");
+
+static int __init cdx_bus_init(void)
+{
+ int ret;
+
+ ret = bus_register(&cdx_bus_type);
+ if (!ret)
+ cdx_debugfs_dir = debugfs_create_dir(cdx_bus_type.name, NULL);
+
+ return ret;
+}
+postcore_initcall(cdx_bus_init);
diff --git a/drivers/cdx/cdx.h b/drivers/cdx/cdx.h
new file mode 100644
index 000000000000..9c60c04dcf87
--- /dev/null
+++ b/drivers/cdx/cdx.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Header file for the CDX Bus
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _CDX_H_
+#define _CDX_H_
+
+#include <linux/cdx/cdx_bus.h>
+
+/**
+ * struct cdx_dev_params - CDX device parameters
+ * @cdx: CDX controller associated with the device
+ * @parent: Associated CDX Bus device
+ * @vendor: Vendor ID for CDX device
+ * @device: Device ID for CDX device
+ * @subsys_vendor: Sub vendor ID for CDX device
+ * @subsys_device: Sub device ID for CDX device
+ * @bus_num: Bus number for this CDX device
+ * @dev_num: Device number for this device
+ * @res: array of MMIO region entries
+ * @res_count: number of valid MMIO regions
+ * @req_id: Requestor ID associated with CDX device
+ * @class: Class of the CDX Device
+ * @revision: Revision of the CDX device
+ * @msi_dev_id: MSI device ID associated with CDX device
+ * @num_msi: Number of MSI's supported by the device
+ */
+struct cdx_dev_params {
+ struct cdx_controller *cdx;
+ struct device *parent;
+ u16 vendor;
+ u16 device;
+ u16 subsys_vendor;
+ u16 subsys_device;
+ u8 bus_num;
+ u8 dev_num;
+ struct resource res[MAX_CDX_DEV_RESOURCES];
+ u8 res_count;
+ u32 req_id;
+ u32 class;
+ u8 revision;
+ u32 msi_dev_id;
+ u32 num_msi;
+};
+
+/**
+ * cdx_register_controller - Register a CDX controller and its ports
+ * on the CDX bus.
+ * @cdx: The CDX controller to register
+ *
+ * Return: -errno on failure, 0 on success.
+ */
+int cdx_register_controller(struct cdx_controller *cdx);
+
+/**
+ * cdx_unregister_controller - Unregister a CDX controller
+ * @cdx: The CDX controller to unregister
+ */
+void cdx_unregister_controller(struct cdx_controller *cdx);
+
+/**
+ * cdx_device_add - Add a CDX device. This function adds a CDX device
+ * on the CDX bus as per the device parameters provided
+ * by caller. It also creates and registers an associated
+ * Linux generic device.
+ * @dev_params: device parameters associated with the device to be created.
+ *
+ * Return: -errno on failure, 0 on success.
+ */
+int cdx_device_add(struct cdx_dev_params *dev_params);
+
+/**
+ * cdx_bus_add - Add a CDX bus. This function adds a bus on the CDX bus
+ * subsystem. It creates a CDX device for the corresponding bus and
+ * also registers an associated Linux generic device.
+ * @cdx: Associated CDX controller
+ * @us_num: Bus number
+ *
+ * Return: associated Linux generic device pointer on success or NULL on failure.
+ */
+struct device *cdx_bus_add(struct cdx_controller *cdx, u8 bus_num);
+
+/**
+ * cdx_msi_domain_init - Init the CDX bus MSI domain.
+ * @dev: Device of the CDX bus controller
+ *
+ * Return: CDX MSI domain, NULL on failure
+ */
+struct irq_domain *cdx_msi_domain_init(struct device *dev);
+
+#endif /* _CDX_H_ */
diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c
new file mode 100644
index 000000000000..91b95422b263
--- /dev/null
+++ b/drivers/cdx/cdx_msi.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD CDX bus driver MSI support
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+#include <linux/cdx/cdx_bus.h>
+
+#include "cdx.h"
+
+static void cdx_msi_write_msg(struct irq_data *irq_data, struct msi_msg *msg)
+{
+ struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
+ struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev);
+
+ /* We would not operate on msg here rather we wait for irq_bus_sync_unlock()
+ * to be called from preemptible task context.
+ */
+ msi_desc->msg = *msg;
+ cdx_dev->msi_write_pending = true;
+}
+
+static void cdx_msi_write_irq_lock(struct irq_data *irq_data)
+{
+ struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
+ struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev);
+
+ mutex_lock(&cdx_dev->irqchip_lock);
+}
+
+static void cdx_msi_write_irq_unlock(struct irq_data *irq_data)
+{
+ struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
+ struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev);
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ struct cdx_device_config dev_config;
+
+ if (!cdx_dev->msi_write_pending) {
+ mutex_unlock(&cdx_dev->irqchip_lock);
+ return;
+ }
+
+ cdx_dev->msi_write_pending = false;
+ mutex_unlock(&cdx_dev->irqchip_lock);
+
+ dev_config.msi.msi_index = msi_desc->msi_index;
+ dev_config.msi.data = msi_desc->msg.data;
+ dev_config.msi.addr = ((u64)(msi_desc->msg.address_hi) << 32) | msi_desc->msg.address_lo;
+
+ /*
+ * dev_configure() is a controller callback which can interact with
+ * Firmware or other entities, and can sleep, so invoke this function
+ * outside of the mutex held region.
+ */
+ dev_config.type = CDX_DEV_MSI_CONF;
+ if (cdx->ops->dev_configure)
+ cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config);
+}
+
+int cdx_enable_msi(struct cdx_device *cdx_dev)
+{
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ struct cdx_device_config dev_config;
+
+ dev_config.type = CDX_DEV_MSI_ENABLE;
+ dev_config.msi_enable = true;
+ if (cdx->ops->dev_configure) {
+ return cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num,
+ &dev_config);
+ }
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(cdx_enable_msi);
+
+void cdx_disable_msi(struct cdx_device *cdx_dev)
+{
+ struct cdx_controller *cdx = cdx_dev->cdx;
+ struct cdx_device_config dev_config;
+
+ dev_config.type = CDX_DEV_MSI_ENABLE;
+ dev_config.msi_enable = false;
+ if (cdx->ops->dev_configure)
+ cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config);
+}
+EXPORT_SYMBOL_GPL(cdx_disable_msi);
+
+static struct irq_chip cdx_msi_irq_chip = {
+ .name = "CDX-MSI",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_affinity = msi_domain_set_affinity,
+ .irq_write_msi_msg = cdx_msi_write_msg,
+ .irq_bus_lock = cdx_msi_write_irq_lock,
+ .irq_bus_sync_unlock = cdx_msi_write_irq_unlock
+};
+
+/* Convert an msi_desc to a unique identifier within the domain. */
+static irq_hw_number_t cdx_domain_calc_hwirq(struct cdx_device *dev,
+ struct msi_desc *desc)
+{
+ return ((irq_hw_number_t)dev->msi_dev_id << 10) | desc->msi_index;
+}
+
+static void cdx_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+{
+ arg->desc = desc;
+ arg->hwirq = cdx_domain_calc_hwirq(to_cdx_device(desc->dev), desc);
+}
+
+static int cdx_msi_prepare(struct irq_domain *msi_domain,
+ struct device *dev,
+ int nvec, msi_alloc_info_t *info)
+{
+ struct cdx_device *cdx_dev = to_cdx_device(dev);
+ struct device *parent = cdx_dev->cdx->dev;
+ struct msi_domain_info *msi_info;
+ u32 dev_id;
+ int ret;
+
+ /* Retrieve device ID from requestor ID using parent device */
+ ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask",
+ NULL, &dev_id);
+ if (ret) {
+ dev_err(dev, "of_map_id failed for MSI: %d\n", ret);
+ return ret;
+ }
+
+#ifdef GENERIC_MSI_DOMAIN_OPS
+ /* Set the device Id to be passed to the GIC-ITS */
+ info->scratchpad[0].ul = dev_id;
+#endif
+
+ msi_info = msi_get_domain_info(msi_domain->parent);
+
+ return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
+}
+
+static struct msi_domain_ops cdx_msi_ops = {
+ .msi_prepare = cdx_msi_prepare,
+ .set_desc = cdx_msi_set_desc
+};
+
+static struct msi_domain_info cdx_msi_domain_info = {
+ .ops = &cdx_msi_ops,
+ .chip = &cdx_msi_irq_chip,
+ .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS
+};
+
+struct irq_domain *cdx_msi_domain_init(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct fwnode_handle *fwnode_handle;
+ struct irq_domain *cdx_msi_domain;
+ struct device_node *parent_node;
+ struct irq_domain *parent;
+
+ fwnode_handle = of_fwnode_handle(np);
+
+ parent_node = of_parse_phandle(np, "msi-map", 1);
+ if (!parent_node) {
+ dev_err(dev, "msi-map not present on cdx controller\n");
+ return NULL;
+ }
+
+ parent = irq_find_matching_fwnode(of_fwnode_handle(parent_node), DOMAIN_BUS_NEXUS);
+ of_node_put(parent_node);
+ if (!parent || !msi_get_domain_info(parent)) {
+ dev_err(dev, "unable to locate ITS domain\n");
+ return NULL;
+ }
+
+ cdx_msi_domain = msi_create_irq_domain(fwnode_handle, &cdx_msi_domain_info, parent);
+ if (!cdx_msi_domain) {
+ dev_err(dev, "unable to create CDX-MSI domain\n");
+ return NULL;
+ }
+
+ dev_dbg(dev, "CDX-MSI domain created\n");
+
+ return cdx_msi_domain;
+}
+EXPORT_SYMBOL_NS_GPL(cdx_msi_domain_init, "CDX_BUS_CONTROLLER");
diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig
new file mode 100644
index 000000000000..a480b62cbd1f
--- /dev/null
+++ b/drivers/cdx/controller/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# CDX controller configuration
+#
+# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+#
+
+if CDX_BUS
+
+config CDX_CONTROLLER
+ tristate "CDX bus controller"
+ depends on HAS_DMA
+ select REMOTEPROC
+ select RPMSG
+ help
+ CDX controller drives the CDX bus. It interacts with
+ firmware to get the hardware devices and registers with
+ the CDX bus. Say Y to enable the CDX hardware driver.
+
+ If unsure, say N.
+
+endif
diff --git a/drivers/cdx/controller/Makefile b/drivers/cdx/controller/Makefile
new file mode 100644
index 000000000000..f071be411d96
--- /dev/null
+++ b/drivers/cdx/controller/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for CDX controller drivers
+#
+# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+#
+
+obj-$(CONFIG_CDX_CONTROLLER) += cdx-controller.o
+cdx-controller-objs := cdx_controller.o cdx_rpmsg.o mcdi.o mcdi_functions.o
diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c
new file mode 100644
index 000000000000..280f207735da
--- /dev/null
+++ b/drivers/cdx/controller/cdx_controller.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CDX host controller driver for AMD versal-net platform.
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/cdx/cdx_bus.h>
+#include <linux/irqdomain.h>
+
+#include "cdx_controller.h"
+#include "../cdx.h"
+#include "mcdi_functions.h"
+#include "mcdid.h"
+
+static unsigned int cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
+{
+ return MCDI_RPC_TIMEOUT;
+}
+
+static void cdx_mcdi_request(struct cdx_mcdi *cdx,
+ const struct cdx_dword *hdr, size_t hdr_len,
+ const struct cdx_dword *sdu, size_t sdu_len)
+{
+ if (cdx_rpmsg_send(cdx, hdr, hdr_len, sdu, sdu_len))
+ dev_err(&cdx->rpdev->dev, "Failed to send rpmsg data\n");
+}
+
+static const struct cdx_mcdi_ops mcdi_ops = {
+ .mcdi_rpc_timeout = cdx_mcdi_rpc_timeout,
+ .mcdi_request = cdx_mcdi_request,
+};
+
+static int cdx_bus_enable(struct cdx_controller *cdx, u8 bus_num)
+{
+ return cdx_mcdi_bus_enable(cdx->priv, bus_num);
+}
+
+static int cdx_bus_disable(struct cdx_controller *cdx, u8 bus_num)
+{
+ return cdx_mcdi_bus_disable(cdx->priv, bus_num);
+}
+
+void cdx_rpmsg_post_probe(struct cdx_controller *cdx)
+{
+ /* Register CDX controller with CDX bus driver */
+ if (cdx_register_controller(cdx))
+ dev_err(cdx->dev, "Failed to register CDX controller\n");
+}
+
+void cdx_rpmsg_pre_remove(struct cdx_controller *cdx)
+{
+ cdx_unregister_controller(cdx);
+ cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT);
+}
+
+static int cdx_configure_device(struct cdx_controller *cdx,
+ u8 bus_num, u8 dev_num,
+ struct cdx_device_config *dev_config)
+{
+ u16 msi_index;
+ int ret = 0;
+ u32 data;
+ u64 addr;
+
+ switch (dev_config->type) {
+ case CDX_DEV_MSI_CONF:
+ msi_index = dev_config->msi.msi_index;
+ data = dev_config->msi.data;
+ addr = dev_config->msi.addr;
+
+ ret = cdx_mcdi_write_msi(cdx->priv, bus_num, dev_num, msi_index, addr, data);
+ break;
+ case CDX_DEV_RESET_CONF:
+ ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num);
+ break;
+ case CDX_DEV_BUS_MASTER_CONF:
+ ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num,
+ dev_config->bus_master_enable);
+ break;
+ case CDX_DEV_MSI_ENABLE:
+ ret = cdx_mcdi_msi_enable(cdx->priv, bus_num, dev_num, dev_config->msi_enable);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cdx_scan_devices(struct cdx_controller *cdx)
+{
+ struct cdx_mcdi *cdx_mcdi = cdx->priv;
+ u8 bus_num, dev_num, num_cdx_bus;
+ int ret;
+
+ /* MCDI FW Read: Fetch the number of CDX buses on this controller */
+ ret = cdx_mcdi_get_num_buses(cdx_mcdi);
+ if (ret < 0) {
+ dev_err(cdx->dev,
+ "Get number of CDX buses failed: %d\n", ret);
+ return ret;
+ }
+ num_cdx_bus = (u8)ret;
+
+ for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) {
+ struct device *bus_dev;
+ u8 num_cdx_dev;
+
+ /* Add the bus on cdx subsystem */
+ bus_dev = cdx_bus_add(cdx, bus_num);
+ if (!bus_dev)
+ continue;
+
+ /* MCDI FW Read: Fetch the number of devices present */
+ ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num);
+ if (ret < 0) {
+ dev_err(cdx->dev,
+ "Get devices on CDX bus %d failed: %d\n", bus_num, ret);
+ continue;
+ }
+ num_cdx_dev = (u8)ret;
+
+ for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) {
+ struct cdx_dev_params dev_params;
+
+ /* MCDI FW: Get the device config */
+ ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num,
+ dev_num, &dev_params);
+ if (ret) {
+ dev_err(cdx->dev,
+ "CDX device config get failed for %d(bus):%d(dev), %d\n",
+ bus_num, dev_num, ret);
+ continue;
+ }
+ dev_params.cdx = cdx;
+ dev_params.parent = bus_dev;
+
+ /* Add the device to the cdx bus */
+ ret = cdx_device_add(&dev_params);
+ if (ret) {
+ dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n",
+ dev_num, ret);
+ continue;
+ }
+
+ dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n",
+ dev_num, bus_num);
+ }
+ }
+
+ return 0;
+}
+
+static struct cdx_ops cdx_ops = {
+ .bus_enable = cdx_bus_enable,
+ .bus_disable = cdx_bus_disable,
+ .scan = cdx_scan_devices,
+ .dev_configure = cdx_configure_device,
+};
+
+static int xlnx_cdx_probe(struct platform_device *pdev)
+{
+ struct cdx_controller *cdx;
+ struct cdx_mcdi *cdx_mcdi;
+ int ret;
+
+ cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL);
+ if (!cdx_mcdi)
+ return -ENOMEM;
+
+ /* Store the MCDI ops */
+ cdx_mcdi->mcdi_ops = &mcdi_ops;
+ /* MCDI FW: Initialize the FW path */
+ ret = cdx_mcdi_init(cdx_mcdi);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n");
+ goto mcdi_init_fail;
+ }
+
+ cdx = kzalloc(sizeof(*cdx), GFP_KERNEL);
+ if (!cdx) {
+ ret = -ENOMEM;
+ goto cdx_alloc_fail;
+ }
+ platform_set_drvdata(pdev, cdx);
+
+ cdx->dev = &pdev->dev;
+ cdx->priv = cdx_mcdi;
+ cdx->ops = &cdx_ops;
+
+ /* Create MSI domain */
+ if (IS_ENABLED(CONFIG_GENERIC_MSI_IRQ))
+ cdx->msi_domain = cdx_msi_domain_init(&pdev->dev);
+ if (!cdx->msi_domain) {
+ ret = dev_err_probe(&pdev->dev, -ENODEV, "cdx_msi_domain_init() failed");
+ goto cdx_msi_fail;
+ }
+
+ ret = cdx_setup_rpmsg(pdev);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "Failed to register CDX RPMsg transport\n");
+ goto cdx_rpmsg_fail;
+ }
+
+ return 0;
+
+cdx_rpmsg_fail:
+ irq_domain_remove(cdx->msi_domain);
+cdx_msi_fail:
+ kfree(cdx);
+cdx_alloc_fail:
+ cdx_mcdi_finish(cdx_mcdi);
+mcdi_init_fail:
+ kfree(cdx_mcdi);
+
+ return ret;
+}
+
+static void xlnx_cdx_remove(struct platform_device *pdev)
+{
+ struct cdx_controller *cdx = platform_get_drvdata(pdev);
+ struct cdx_mcdi *cdx_mcdi = cdx->priv;
+
+ cdx_destroy_rpmsg(pdev);
+
+ irq_domain_remove(cdx->msi_domain);
+ kfree(cdx);
+
+ cdx_mcdi_finish(cdx_mcdi);
+ kfree(cdx_mcdi);
+}
+
+static const struct of_device_id cdx_match_table[] = {
+ {.compatible = "xlnx,versal-net-cdx",},
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, cdx_match_table);
+
+static struct platform_driver cdx_pdriver = {
+ .driver = {
+ .name = "cdx-controller",
+ .of_match_table = cdx_match_table,
+ },
+ .probe = xlnx_cdx_probe,
+ .remove = xlnx_cdx_remove,
+};
+
+module_platform_driver(cdx_pdriver);
+
+MODULE_AUTHOR("AMD Inc.");
+MODULE_DESCRIPTION("CDX controller for AMD devices");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CDX_BUS_CONTROLLER");
diff --git a/drivers/cdx/controller/cdx_controller.h b/drivers/cdx/controller/cdx_controller.h
new file mode 100644
index 000000000000..43b7c742df87
--- /dev/null
+++ b/drivers/cdx/controller/cdx_controller.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Header file for the CDX Controller
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _CDX_CONTROLLER_H_
+#define _CDX_CONTROLLER_H_
+
+#include <linux/cdx/cdx_bus.h>
+#include "mcdi_functions.h"
+
+void cdx_rpmsg_post_probe(struct cdx_controller *cdx);
+
+void cdx_rpmsg_pre_remove(struct cdx_controller *cdx);
+
+int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi,
+ const struct cdx_dword *hdr, size_t hdr_len,
+ const struct cdx_dword *sdu, size_t sdu_len);
+
+void cdx_rpmsg_read_resp(struct cdx_mcdi *cdx_mcdi,
+ struct cdx_dword *outbuf, size_t offset,
+ size_t outlen);
+
+int cdx_setup_rpmsg(struct platform_device *pdev);
+
+void cdx_destroy_rpmsg(struct platform_device *pdev);
+
+#endif /* _CDX_CONT_PRIV_H_ */
diff --git a/drivers/cdx/controller/cdx_rpmsg.c b/drivers/cdx/controller/cdx_rpmsg.c
new file mode 100644
index 000000000000..59aabd99fa8f
--- /dev/null
+++ b/drivers/cdx/controller/cdx_rpmsg.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Platform driver for CDX bus.
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/rpmsg.h>
+#include <linux/remoteproc.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/cdx/cdx_bus.h>
+#include <linux/module.h>
+
+#include "../cdx.h"
+#include "cdx_controller.h"
+#include "mcdi_functions.h"
+#include "mcdid.h"
+
+static struct rpmsg_device_id cdx_rpmsg_id_table[] = {
+ { .name = "mcdi_ipc" },
+ { },
+};
+MODULE_DEVICE_TABLE(rpmsg, cdx_rpmsg_id_table);
+
+int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi,
+ const struct cdx_dword *hdr, size_t hdr_len,
+ const struct cdx_dword *sdu, size_t sdu_len)
+{
+ unsigned char *send_buf;
+ int ret;
+
+ send_buf = kzalloc(hdr_len + sdu_len, GFP_KERNEL);
+ if (!send_buf)
+ return -ENOMEM;
+
+ memcpy(send_buf, hdr, hdr_len);
+ memcpy(send_buf + hdr_len, sdu, sdu_len);
+
+ ret = rpmsg_send(cdx_mcdi->ept, send_buf, hdr_len + sdu_len);
+ kfree(send_buf);
+
+ return ret;
+}
+
+static int cdx_attach_to_rproc(struct platform_device *pdev)
+{
+ struct device_node *r5_core_node;
+ struct cdx_controller *cdx_c;
+ struct cdx_mcdi *cdx_mcdi;
+ struct device *dev;
+ struct rproc *rp;
+ int ret;
+
+ dev = &pdev->dev;
+ cdx_c = platform_get_drvdata(pdev);
+ cdx_mcdi = cdx_c->priv;
+
+ r5_core_node = of_parse_phandle(dev->of_node, "xlnx,rproc", 0);
+ if (!r5_core_node) {
+ dev_err(&pdev->dev, "xlnx,rproc: invalid phandle\n");
+ return -EINVAL;
+ }
+
+ rp = rproc_get_by_phandle(r5_core_node->phandle);
+ if (!rp) {
+ ret = -EPROBE_DEFER;
+ goto pdev_err;
+ }
+
+ /* Attach to remote processor */
+ ret = rproc_boot(rp);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to attach to remote processor\n");
+ rproc_put(rp);
+ goto pdev_err;
+ }
+
+ cdx_mcdi->r5_rproc = rp;
+pdev_err:
+ of_node_put(r5_core_node);
+ return ret;
+}
+
+static void cdx_detach_to_r5(struct platform_device *pdev)
+{
+ struct cdx_controller *cdx_c;
+ struct cdx_mcdi *cdx_mcdi;
+
+ cdx_c = platform_get_drvdata(pdev);
+ cdx_mcdi = cdx_c->priv;
+
+ rproc_detach(cdx_mcdi->r5_rproc);
+ rproc_put(cdx_mcdi->r5_rproc);
+}
+
+static int cdx_rpmsg_cb(struct rpmsg_device *rpdev, void *data,
+ int len, void *priv, u32 src)
+{
+ struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev);
+ struct cdx_mcdi *cdx_mcdi = cdx_c->priv;
+
+ if (len > MCDI_BUF_LEN)
+ return -EINVAL;
+
+ cdx_mcdi_process_cmd(cdx_mcdi, (struct cdx_dword *)data, len);
+
+ return 0;
+}
+
+static void cdx_rpmsg_post_probe_work(struct work_struct *work)
+{
+ struct cdx_controller *cdx_c;
+ struct cdx_mcdi *cdx_mcdi;
+
+ cdx_mcdi = container_of(work, struct cdx_mcdi, work);
+ cdx_c = dev_get_drvdata(&cdx_mcdi->rpdev->dev);
+ cdx_rpmsg_post_probe(cdx_c);
+}
+
+static int cdx_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+ struct rpmsg_channel_info chinfo = {0};
+ struct cdx_controller *cdx_c;
+ struct cdx_mcdi *cdx_mcdi;
+
+ cdx_c = (struct cdx_controller *)cdx_rpmsg_id_table[0].driver_data;
+ cdx_mcdi = cdx_c->priv;
+
+ chinfo.src = RPMSG_ADDR_ANY;
+ chinfo.dst = rpdev->dst;
+ strscpy(chinfo.name, cdx_rpmsg_id_table[0].name, sizeof(chinfo.name));
+
+ cdx_mcdi->ept = rpmsg_create_ept(rpdev, cdx_rpmsg_cb, NULL, chinfo);
+ if (!cdx_mcdi->ept) {
+ dev_err_probe(&rpdev->dev, -ENXIO,
+ "Failed to create ept for channel %s\n",
+ chinfo.name);
+ return -EINVAL;
+ }
+
+ cdx_mcdi->rpdev = rpdev;
+ dev_set_drvdata(&rpdev->dev, cdx_c);
+
+ schedule_work(&cdx_mcdi->work);
+ return 0;
+}
+
+static void cdx_rpmsg_remove(struct rpmsg_device *rpdev)
+{
+ struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev);
+ struct cdx_mcdi *cdx_mcdi = cdx_c->priv;
+
+ flush_work(&cdx_mcdi->work);
+ cdx_rpmsg_pre_remove(cdx_c);
+
+ rpmsg_destroy_ept(cdx_mcdi->ept);
+ dev_set_drvdata(&rpdev->dev, NULL);
+}
+
+static struct rpmsg_driver cdx_rpmsg_driver = {
+ .drv.name = KBUILD_MODNAME,
+ .id_table = cdx_rpmsg_id_table,
+ .probe = cdx_rpmsg_probe,
+ .remove = cdx_rpmsg_remove,
+ .callback = cdx_rpmsg_cb,
+};
+
+int cdx_setup_rpmsg(struct platform_device *pdev)
+{
+ struct cdx_controller *cdx_c;
+ struct cdx_mcdi *cdx_mcdi;
+ int ret;
+
+ /* Attach to remote processor */
+ ret = cdx_attach_to_rproc(pdev);
+ if (ret)
+ return ret;
+
+ cdx_c = platform_get_drvdata(pdev);
+ cdx_mcdi = cdx_c->priv;
+
+ /* Register RPMsg driver */
+ cdx_rpmsg_id_table[0].driver_data = (kernel_ulong_t)cdx_c;
+
+ INIT_WORK(&cdx_mcdi->work, cdx_rpmsg_post_probe_work);
+ ret = register_rpmsg_driver(&cdx_rpmsg_driver);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register cdx RPMsg driver: %d\n", ret);
+ cdx_detach_to_r5(pdev);
+ }
+
+ return ret;
+}
+
+void cdx_destroy_rpmsg(struct platform_device *pdev)
+{
+ unregister_rpmsg_driver(&cdx_rpmsg_driver);
+
+ cdx_detach_to_r5(pdev);
+}
diff --git a/drivers/cdx/controller/mc_cdx_pcol.h b/drivers/cdx/controller/mc_cdx_pcol.h
new file mode 100644
index 000000000000..832a44af963e
--- /dev/null
+++ b/drivers/cdx/controller/mc_cdx_pcol.h
@@ -0,0 +1,708 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Driver for AMD network controllers and boards
+ *
+ * Copyright (C) 2021, Xilinx, Inc.
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#ifndef MC_CDX_PCOL_H
+#define MC_CDX_PCOL_H
+
+/* The current version of the MCDI protocol. */
+#define MCDI_PCOL_VERSION 2
+
+/*
+ * Each MCDI request starts with an MCDI_HEADER, which is a 32bit
+ * structure, filled in by the client.
+ *
+ * 0 7 8 16 20 22 23 24 31
+ * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS |
+ * | | |
+ * | | \--- Response
+ * | \------- Error
+ * \------------------------------ Resync (always set)
+ *
+ * The client writes its request into MC shared memory, and rings the
+ * doorbell. Each request is completed either by the MC writing
+ * back into shared memory, or by writing out an event.
+ *
+ * All MCDI commands support completion by shared memory response. Each
+ * request may also contain additional data (accounted for by HEADER.LEN),
+ * and some responses may also contain additional data (again, accounted
+ * for by HEADER.LEN).
+ *
+ * Some MCDI commands support completion by event, in which any associated
+ * response data is included in the event.
+ *
+ * The protocol requires one response to be delivered for every request; a
+ * request should not be sent unless the response for the previous request
+ * has been received (either by polling shared memory, or by receiving
+ * an event).
+ */
+
+/** Request/Response structure */
+#define MCDI_HEADER_OFST 0
+#define MCDI_HEADER_CODE_LBN 0
+#define MCDI_HEADER_CODE_WIDTH 7
+#define MCDI_HEADER_RESYNC_LBN 7
+#define MCDI_HEADER_RESYNC_WIDTH 1
+#define MCDI_HEADER_DATALEN_LBN 8
+#define MCDI_HEADER_DATALEN_WIDTH 8
+#define MCDI_HEADER_SEQ_LBN 16
+#define MCDI_HEADER_SEQ_WIDTH 4
+#define MCDI_HEADER_RSVD_LBN 20
+#define MCDI_HEADER_RSVD_WIDTH 1
+#define MCDI_HEADER_NOT_EPOCH_LBN 21
+#define MCDI_HEADER_NOT_EPOCH_WIDTH 1
+#define MCDI_HEADER_ERROR_LBN 22
+#define MCDI_HEADER_ERROR_WIDTH 1
+#define MCDI_HEADER_RESPONSE_LBN 23
+#define MCDI_HEADER_RESPONSE_WIDTH 1
+#define MCDI_HEADER_XFLAGS_LBN 24
+#define MCDI_HEADER_XFLAGS_WIDTH 8
+/* Request response using event */
+#define MCDI_HEADER_XFLAGS_EVREQ 0x01
+/* Request (and signal) early doorbell return */
+#define MCDI_HEADER_XFLAGS_DBRET 0x02
+
+/* Maximum number of payload bytes */
+#define MCDI_CTL_SDU_LEN_MAX_V2 0x400
+
+#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V2
+
+/*
+ * The MC can generate events for two reasons:
+ * - To advance a shared memory request if XFLAGS_EVREQ was set
+ * - As a notification (link state, i2c event), controlled
+ * via MC_CMD_LOG_CTRL
+ *
+ * Both events share a common structure:
+ *
+ * 0 32 33 36 44 52 60
+ * | Data | Cont | Level | Src | Code | Rsvd |
+ * |
+ * \ There is another event pending in this notification
+ *
+ * If Code==CMDDONE, then the fields are further interpreted as:
+ *
+ * - LEVEL==INFO Command succeeded
+ * - LEVEL==ERR Command failed
+ *
+ * 0 8 16 24 32
+ * | Seq | Datalen | Errno | Rsvd |
+ *
+ * These fields are taken directly out of the standard MCDI header, i.e.,
+ * LEVEL==ERR, Datalen == 0 => Reboot
+ *
+ * Events can be squirted out of the UART (using LOG_CTRL) without a
+ * MCDI header. An event can be distinguished from a MCDI response by
+ * examining the first byte which is 0xc0. This corresponds to the
+ * non-existent MCDI command MC_CMD_DEBUG_LOG.
+ *
+ * 0 7 8
+ * | command | Resync | = 0xc0
+ *
+ * Since the event is written in big-endian byte order, this works
+ * providing bits 56-63 of the event are 0xc0.
+ *
+ * 56 60 63
+ * | Rsvd | Code | = 0xc0
+ *
+ * Which means for convenience the event code is 0xc for all MC
+ * generated events.
+ */
+
+/*
+ * the errno value may be followed by the (0-based) number of the
+ * first argument that could not be processed.
+ */
+#define MC_CMD_ERR_ARG_OFST 4
+
+/* MC_CMD_ERR MCDI error codes. */
+/* Operation not permitted. */
+#define MC_CMD_ERR_EPERM 0x1
+/* Non-existent command target */
+#define MC_CMD_ERR_ENOENT 0x2
+/* assert() has killed the MC */
+#define MC_CMD_ERR_EINTR 0x4
+/* I/O failure */
+#define MC_CMD_ERR_EIO 0x5
+/* Already exists */
+#define MC_CMD_ERR_EEXIST 0x6
+/* Try again */
+#define MC_CMD_ERR_EAGAIN 0xb
+/* Out of memory */
+#define MC_CMD_ERR_ENOMEM 0xc
+/* Caller does not hold required locks */
+#define MC_CMD_ERR_EACCES 0xd
+/* Resource is currently unavailable (e.g. lock contention) */
+#define MC_CMD_ERR_EBUSY 0x10
+/* No such device */
+#define MC_CMD_ERR_ENODEV 0x13
+/* Invalid argument to target */
+#define MC_CMD_ERR_EINVAL 0x16
+/* No space */
+#define MC_CMD_ERR_ENOSPC 0x1c
+/* Read-only */
+#define MC_CMD_ERR_EROFS 0x1e
+/* Broken pipe */
+#define MC_CMD_ERR_EPIPE 0x20
+/* Out of range */
+#define MC_CMD_ERR_ERANGE 0x22
+/* Non-recursive resource is already acquired */
+#define MC_CMD_ERR_EDEADLK 0x23
+/* Operation not implemented */
+#define MC_CMD_ERR_ENOSYS 0x26
+/* Operation timed out */
+#define MC_CMD_ERR_ETIME 0x3e
+/* Link has been severed */
+#define MC_CMD_ERR_ENOLINK 0x43
+/* Protocol error */
+#define MC_CMD_ERR_EPROTO 0x47
+/* Bad message */
+#define MC_CMD_ERR_EBADMSG 0x4a
+/* Operation not supported */
+#define MC_CMD_ERR_ENOTSUP 0x5f
+/* Address not available */
+#define MC_CMD_ERR_EADDRNOTAVAIL 0x63
+/* Not connected */
+#define MC_CMD_ERR_ENOTCONN 0x6b
+/* Operation already in progress */
+#define MC_CMD_ERR_EALREADY 0x72
+/* Stale handle. The handle references resource that no longer exists */
+#define MC_CMD_ERR_ESTALE 0x74
+/* Resource allocation failed. */
+#define MC_CMD_ERR_ALLOC_FAIL 0x1000
+/* V-adaptor not found. */
+#define MC_CMD_ERR_NO_VADAPTOR 0x1001
+/* EVB port not found. */
+#define MC_CMD_ERR_NO_EVB_PORT 0x1002
+/* V-switch not found. */
+#define MC_CMD_ERR_NO_VSWITCH 0x1003
+/* Too many VLAN tags. */
+#define MC_CMD_ERR_VLAN_LIMIT 0x1004
+/* Bad PCI function number. */
+#define MC_CMD_ERR_BAD_PCI_FUNC 0x1005
+/* Invalid VLAN mode. */
+#define MC_CMD_ERR_BAD_VLAN_MODE 0x1006
+/* Invalid v-switch type. */
+#define MC_CMD_ERR_BAD_VSWITCH_TYPE 0x1007
+/* Invalid v-port type. */
+#define MC_CMD_ERR_BAD_VPORT_TYPE 0x1008
+/* MAC address exists. */
+#define MC_CMD_ERR_MAC_EXIST 0x1009
+/* Slave core not present */
+#define MC_CMD_ERR_SLAVE_NOT_PRESENT 0x100a
+/* The datapath is disabled. */
+#define MC_CMD_ERR_DATAPATH_DISABLED 0x100b
+/* The requesting client is not a function */
+#define MC_CMD_ERR_CLIENT_NOT_FN 0x100c
+/*
+ * The requested operation might require the command to be passed between
+ * MCs, and the transport doesn't support that. Should only ever been seen over
+ * the UART.
+ */
+#define MC_CMD_ERR_NO_PRIVILEGE 0x1013
+/*
+ * Workaround 26807 could not be turned on/off because some functions
+ * have already installed filters. See the comment at
+ * MC_CMD_WORKAROUND_BUG26807. May also returned for other operations such as
+ * sub-variant switching.
+ */
+#define MC_CMD_ERR_FILTERS_PRESENT 0x1014
+/* The clock whose frequency you've attempted to set doesn't exist */
+#define MC_CMD_ERR_NO_CLOCK 0x1015
+/*
+ * Returned by MC_CMD_TESTASSERT if the action that should have caused an
+ * assertion failed to do so.
+ */
+#define MC_CMD_ERR_UNREACHABLE 0x1016
+/*
+ * This command needs to be processed in the background but there were no
+ * resources to do so. Send it again after a command has completed.
+ */
+#define MC_CMD_ERR_QUEUE_FULL 0x1017
+/*
+ * The operation could not be completed because the PCIe link has gone
+ * away. This error code is never expected to be returned over the TLP
+ * transport.
+ */
+#define MC_CMD_ERR_NO_PCIE 0x1018
+/*
+ * The operation could not be completed because the datapath has gone
+ * away. This is distinct from MC_CMD_ERR_DATAPATH_DISABLED in that the
+ * datapath absence may be temporary
+ */
+#define MC_CMD_ERR_NO_DATAPATH 0x1019
+/* The operation could not complete because some VIs are allocated */
+#define MC_CMD_ERR_VIS_PRESENT 0x101a
+/*
+ * The operation could not complete because some PIO buffers are
+ * allocated
+ */
+#define MC_CMD_ERR_PIOBUFS_PRESENT 0x101b
+
+/***********************************/
+/*
+ * MC_CMD_CDX_BUS_ENUM_BUSES
+ * CDX bus hosts devices (functions) that are implemented using the Composable
+ * DMA subsystem and directly mapped into the memory space of the FGPA PSX
+ * Application Processors (APUs). As such, they only apply to the PSX APU side,
+ * not the host (PCIe). Unlike PCIe, these devices have no native configuration
+ * space or enumeration mechanism, so this message set provides a minimal
+ * interface for discovery and management (bus reset, FLR, BME) of such
+ * devices. This command returns the number of CDX buses present in the system.
+ */
+#define MC_CMD_CDX_BUS_ENUM_BUSES 0x1
+#define MC_CMD_CDX_BUS_ENUM_BUSES_MSGSET 0x1
+#undef MC_CMD_0x1_PRIVILEGE_CTG
+
+#define MC_CMD_0x1_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_BUS_ENUM_BUSES_IN msgrequest */
+#define MC_CMD_CDX_BUS_ENUM_BUSES_IN_LEN 0
+
+/* MC_CMD_CDX_BUS_ENUM_BUSES_OUT msgresponse */
+#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN 4
+/*
+ * Number of CDX buses present in the system. Buses are numbered 0 to
+ * BUS_COUNT-1
+ */
+#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT_OFST 0
+#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT_LEN 4
+
+/***********************************/
+/*
+ * MC_CMD_CDX_BUS_ENUM_DEVICES
+ * Enumerate CDX bus devices on a given bus
+ */
+#define MC_CMD_CDX_BUS_ENUM_DEVICES 0x2
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_MSGSET 0x2
+#undef MC_CMD_0x2_PRIVILEGE_CTG
+
+#define MC_CMD_0x2_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_BUS_ENUM_DEVICES_IN msgrequest */
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_LEN 4
+/*
+ * Bus number to enumerate, in range 0 to BUS_COUNT-1, as returned by
+ * MC_CMD_CDX_BUS_ENUM_BUSES_OUT
+ */
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_BUS_OFST 0
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_BUS_LEN 4
+
+/* MC_CMD_CDX_BUS_ENUM_DEVICES_OUT msgresponse */
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN 4
+/*
+ * Number of devices present on the bus. Devices on the bus are numbered 0 to
+ * DEVICE_COUNT-1. Returns EAGAIN if number of devices unknown or if the target
+ * devices are not ready (e.g. undergoing a bus reset)
+ */
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT_OFST 0
+#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT_LEN 4
+
+/***********************************/
+/*
+ * MC_CMD_CDX_BUS_GET_DEVICE_CONFIG
+ * Returns device identification and MMIO/MSI resource data for a CDX device.
+ * The expected usage is for the caller to first retrieve the number of devices
+ * on the bus using MC_CMD_BUS_ENUM_DEVICES, then loop through the range (0,
+ * DEVICE_COUNT - 1), retrieving device resource data. May return EAGAIN if the
+ * number of exposed devices or device resources change during enumeration (due
+ * to e.g. a PL reload / bus reset), in which case the caller is expected to
+ * restart the enumeration loop. MMIO addresses are specified in terms of bus
+ * addresses (prior to any potential IOMMU translation). For versal-net, these
+ * are equivalent to APU physical addresses. Implementation note - for this to
+ * work, the implementation needs to keep state (generation count) per client.
+ */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG 0x3
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_MSGSET 0x3
+#undef MC_CMD_0x3_PRIVILEGE_CTG
+
+#define MC_CMD_0x3_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN msgrequest */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN 8
+/* Device bus number, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_BUS_OFST 0
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_BUS_LEN 4
+/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE_OFST 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE_LEN 4
+
+/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT msgresponse */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN 88
+/* 16-bit Vendor identifier, compliant with PCI-SIG VendorID assignment. */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID_OFST 0
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID_LEN 2
+/* 16-bit Device ID assigned by the vendor */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID_OFST 2
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID_LEN 2
+/*
+ * 16-bit Subsystem Vendor ID, , compliant with PCI-SIG VendorID assignment.
+ * For further device differentiation, as required. 0 if unused.
+ */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID_OFST 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID_LEN 2
+/*
+ * 16-bit Subsystem Device ID assigned by the vendor. For further device
+ * differentiation, as required. 0 if unused.
+ */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID_OFST 6
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID_LEN 2
+/* 24-bit Device Class code, compliant with PCI-SIG Device Class codes */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS_OFST 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS_LEN 3
+/* 8-bit vendor-assigned revision */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION_OFST 11
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION_LEN 1
+/* Reserved (alignment) */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_RESERVED_OFST 12
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_RESERVED_LEN 4
+/* MMIO region 0 base address (bus address), 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_OFST 16
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_OFST 16
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_LBN 128
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_OFST 20
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_LBN 160
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_WIDTH 32
+/* MMIO region 0 size, 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_OFST 24
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_OFST 24
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_LBN 192
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_OFST 28
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_LBN 224
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_WIDTH 32
+/* MMIO region 1 base address (bus address), 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_OFST 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_OFST 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_LBN 256
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_OFST 36
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_LBN 288
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_WIDTH 32
+/* MMIO region 1 size, 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_OFST 40
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_OFST 40
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_LBN 320
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_OFST 44
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_LBN 352
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_WIDTH 32
+/* MMIO region 2 base address (bus address), 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_OFST 48
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_OFST 48
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_LBN 384
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_OFST 52
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_LBN 416
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_WIDTH 32
+/* MMIO region 2 size, 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_OFST 56
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_OFST 56
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_LBN 448
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_OFST 60
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_LBN 480
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_WIDTH 32
+/* MMIO region 3 base address (bus address), 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_OFST 64
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_OFST 64
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_LBN 512
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_OFST 68
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_LBN 544
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_WIDTH 32
+/* MMIO region 3 size, 0 if unused */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_OFST 72
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LEN 8
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_OFST 72
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_LBN 576
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_WIDTH 32
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_OFST 76
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_LEN 4
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_LBN 608
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_WIDTH 32
+/* MSI vector count */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT_OFST 80
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT_LEN 4
+/* Requester ID used by device (SMMU StreamID, GIC ITS DeviceID) */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST 84
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN 4
+
+/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2 msgresponse */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN 92
+/* Requester ID used by device for GIC ITS DeviceID */
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_OFST 88
+#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_LEN 4
+
+/***********************************/
+/*
+ * MC_CMD_CDX_BUS_DOWN
+ * Asserting reset on the CDX bus causes all devices on the bus to be quiesced.
+ * DMA bus mastering is disabled and any pending DMA request are flushed. Once
+ * the response is returned, the devices are guaranteed to no longer issue DMA
+ * requests or raise MSI interrupts. Further device MMIO accesses may have
+ * undefined results. While the bus reset is asserted, any of the enumeration
+ * or device configuration MCDIs will fail with EAGAIN. It is only legal to
+ * reload the relevant PL region containing CDX devices if the corresponding CDX
+ * bus is in reset. Depending on the implementation, the firmware may or may
+ * not enforce this restriction and it is up to the caller to make sure this
+ * requirement is satisfied.
+ */
+#define MC_CMD_CDX_BUS_DOWN 0x4
+#define MC_CMD_CDX_BUS_DOWN_MSGSET 0x4
+
+/* MC_CMD_CDX_BUS_DOWN_IN msgrequest */
+#define MC_CMD_CDX_BUS_DOWN_IN_LEN 4
+/* Bus number to put in reset, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_BUS_DOWN_IN_BUS_OFST 0
+#define MC_CMD_CDX_BUS_DOWN_IN_BUS_LEN 4
+
+/*
+ * MC_CMD_CDX_BUS_DOWN_OUT msgresponse: The bus is quiesced, no further
+ * upstream traffic for devices on this bus.
+ */
+#define MC_CMD_CDX_BUS_DOWN_OUT_LEN 0
+
+/***********************************/
+/*
+ * MC_CMD_CDX_BUS_UP
+ * After bus reset is de-asserted, devices are in a state which is functionally
+ * equivalent to each device having been reset with MC_CMD_CDX_DEVICE_RESET. In
+ * other words, device logic is reset in a hardware-specific way, MMIO accesses
+ * are forwarded to the device, DMA bus mastering is disabled and needs to be
+ * re-enabled with MC_CMD_CDX_DEVICE_DMA_ENABLE once the driver is ready to
+ * start servicing DMA. If the underlying number of devices or device resources
+ * changed (e.g. if PL was reloaded) while the bus was in reset, the bus driver
+ * is expected to re-enumerate the bus. Returns EALREADY if the bus was already
+ * up before the call.
+ */
+#define MC_CMD_CDX_BUS_UP 0x5
+#define MC_CMD_CDX_BUS_UP_MSGSET 0x5
+
+/* MC_CMD_CDX_BUS_UP_IN msgrequest */
+#define MC_CMD_CDX_BUS_UP_IN_LEN 4
+/* Bus number to take out of reset, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_BUS_UP_IN_BUS_OFST 0
+#define MC_CMD_CDX_BUS_UP_IN_BUS_LEN 4
+
+/* MC_CMD_CDX_BUS_UP_OUT msgresponse: The bus can now be enumerated. */
+#define MC_CMD_CDX_BUS_UP_OUT_LEN 0
+
+/***********************************/
+/*
+ * MC_CMD_CDX_DEVICE_RESET
+ * After this call completes, device DMA and interrupts are quiesced, devices
+ * logic is reset in a hardware-specific way and DMA bus mastering is disabled.
+ */
+#define MC_CMD_CDX_DEVICE_RESET 0x6
+#define MC_CMD_CDX_DEVICE_RESET_MSGSET 0x6
+#undef MC_CMD_0x6_PRIVILEGE_CTG
+
+#define MC_CMD_0x6_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_DEVICE_RESET_IN msgrequest */
+#define MC_CMD_CDX_DEVICE_RESET_IN_LEN 8
+/* Device bus number, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_DEVICE_RESET_IN_BUS_OFST 0
+#define MC_CMD_CDX_DEVICE_RESET_IN_BUS_LEN 4
+/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
+#define MC_CMD_CDX_DEVICE_RESET_IN_DEVICE_OFST 4
+#define MC_CMD_CDX_DEVICE_RESET_IN_DEVICE_LEN 4
+
+/*
+ * MC_CMD_CDX_DEVICE_RESET_OUT msgresponse: The device is quiesced and all
+ * pending device initiated DMA has completed.
+ */
+#define MC_CMD_CDX_DEVICE_RESET_OUT_LEN 0
+
+/***********************************/
+/*
+ * MC_CMD_CDX_DEVICE_CONTROL_SET
+ * If BUS_MASTER is set to disabled, device DMA and interrupts are quiesced.
+ * Pending DMA requests and MSI interrupts are flushed and no further DMA or
+ * interrupts are issued after this command returns. If BUS_MASTER is set to
+ * enabled, device is allowed to initiate DMA. Whether interrupts are enabled
+ * also depends on the value of MSI_ENABLE bit. Note that, in this case, the
+ * device may start DMA before the host receives and processes the MCDI
+ * response. MSI_ENABLE masks or unmasks device interrupts only. Note that for
+ * interrupts to be delivered to the host, both BUS_MASTER and MSI_ENABLE needs
+ * to be set. MMIO_REGIONS_ENABLE enables or disables host accesses to device
+ * MMIO regions. Note that an implementation is allowed to permanently set this
+ * bit to 1, in which case MC_CMD_CDX_DEVICE_CONTROL_GET will always return 1
+ * for this bit, regardless of the value set here.
+ */
+#define MC_CMD_CDX_DEVICE_CONTROL_SET 0x7
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_MSGSET 0x7
+#undef MC_CMD_0x7_PRIVILEGE_CTG
+
+#define MC_CMD_0x7_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_DEVICE_CONTROL_SET_IN msgrequest */
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_LEN 12
+/* Device bus number, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_OFST 0
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_LEN 4
+/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_DEVICE_OFST 4
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_DEVICE_LEN 4
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_FLAGS_OFST 8
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_FLAGS_LEN 4
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_OFST 8
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN 0
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_WIDTH 1
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_OFST 8
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN 1
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_WIDTH 1
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_OFST 8
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_LBN 2
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_WIDTH 1
+
+/* MC_CMD_CDX_DEVICE_CONTROL_SET_OUT msgresponse */
+#define MC_CMD_CDX_DEVICE_CONTROL_SET_OUT_LEN 0
+
+/***********************************/
+/*
+ * MC_CMD_CDX_DEVICE_CONTROL_GET
+ * Returns device DMA, interrupt and MMIO region access control bits. See
+ * MC_CMD_CDX_DEVICE_CONTROL_SET for definition of the available control bits.
+ */
+#define MC_CMD_CDX_DEVICE_CONTROL_GET 0x8
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_MSGSET 0x8
+#undef MC_CMD_0x8_PRIVILEGE_CTG
+
+#define MC_CMD_0x8_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_DEVICE_CONTROL_GET_IN msgrequest */
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_LEN 8
+/* Device bus number, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_BUS_OFST 0
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_BUS_LEN 4
+/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_DEVICE_OFST 4
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_DEVICE_LEN 4
+
+/* MC_CMD_CDX_DEVICE_CONTROL_GET_OUT msgresponse */
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_LEN 4
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_FLAGS_OFST 0
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_FLAGS_LEN 4
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_OFST 0
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_LBN 0
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_WIDTH 1
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_OFST 0
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_LBN 1
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_WIDTH 1
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_OFST 0
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_LBN 2
+#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_WIDTH 1
+
+/***********************************/
+/*
+ * MC_CMD_CDX_DEVICE_WRITE_MSI_MSG
+ * Populates the MSI message to be used by the hardware to raise the specified
+ * interrupt vector. Versal-net implementation specific limitations are that
+ * only 4 CDX devices with MSI interrupt capability are supported and all
+ * vectors within a device must use the same write address. The command will
+ * return EINVAL if any of these limitations is violated.
+ */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG 0x9
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_MSGSET 0x9
+#undef MC_CMD_0x9_PRIVILEGE_CTG
+
+#define MC_CMD_0x9_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+
+/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN msgrequest */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN 28
+/* Device bus number, in range 0 to BUS_COUNT-1 */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_OFST 0
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_LEN 4
+/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_OFST 4
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_LEN 4
+/*
+ * Device-relative MSI vector number. Must be < MSI_COUNT reported for the
+ * device.
+ */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_OFST 8
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_LEN 4
+/* Reserved (alignment) */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_OFST 12
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_LEN 4
+/*
+ * MSI address to be used by the hardware. Typically, on ARM systems this
+ * address is translated by the IOMMU (if enabled) and it is the responsibility
+ * of the entity managing the IOMMU (APU kernel) to supply the correct IOVA
+ * here.
+ */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_OFST 16
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LEN 8
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_OFST 16
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LEN 4
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LBN 128
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_WIDTH 32
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_OFST 20
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LEN 4
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LBN 160
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_WIDTH 32
+/*
+ * MSI data to be used by the hardware. On versal-net, only the lower 16-bits
+ * are used, the remaining bits are ignored and should be set to zero.
+ */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_OFST 24
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_LEN 4
+
+/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT msgresponse */
+#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT_LEN 0
+
+/***********************************/
+/* MC_CMD_V2_EXTN - Encapsulation for a v2 extended command */
+#define MC_CMD_V2_EXTN 0x7f
+
+/* MC_CMD_V2_EXTN_IN msgrequest */
+#define MC_CMD_V2_EXTN_IN_LEN 4
+/* the extended command number */
+#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_LBN 0
+#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_WIDTH 15
+#define MC_CMD_V2_EXTN_IN_UNUSED_LBN 15
+#define MC_CMD_V2_EXTN_IN_UNUSED_WIDTH 1
+/* the actual length of the encapsulated command */
+#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_LBN 16
+#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_WIDTH 10
+#define MC_CMD_V2_EXTN_IN_UNUSED2_LBN 26
+#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 2
+/* Type of command/response */
+#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_LBN 28
+#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_WIDTH 4
+/*
+ * enum: MCDI command directed to versal-net. MCDI responses of this type
+ * are not defined.
+ */
+#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM 0x2
+
+#endif /* MC_CDX_PCOL_H */
diff --git a/drivers/cdx/controller/mcdi.c b/drivers/cdx/controller/mcdi.c
new file mode 100644
index 000000000000..2e82ffc18d89
--- /dev/null
+++ b/drivers/cdx/controller/mcdi.c
@@ -0,0 +1,870 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Management-Controller-to-Driver Interface
+ *
+ * Copyright 2008-2013 Solarflare Communications Inc.
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/rwsem.h>
+#include <linux/vmalloc.h>
+#include <net/netevent.h>
+#include <linux/log2.h>
+#include <linux/net_tstamp.h>
+#include <linux/wait.h>
+#include <linux/cdx/bitfield.h>
+
+#include <linux/cdx/mcdi.h>
+#include "mcdid.h"
+
+static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd);
+static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx);
+static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
+ struct cdx_mcdi_cmd *cmd,
+ unsigned int *handle);
+static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
+ bool allow_retry);
+static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd);
+static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd,
+ struct cdx_dword *outbuf,
+ int len,
+ struct list_head *cleanup_list);
+static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd,
+ struct list_head *cleanup_list);
+static void cdx_mcdi_cmd_work(struct work_struct *context);
+static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list);
+static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
+ size_t inlen, int raw, int arg, int err_no);
+
+static bool cdx_cmd_cancelled(struct cdx_mcdi_cmd *cmd)
+{
+ return cmd->state == MCDI_STATE_RUNNING_CANCELLED;
+}
+
+static void cdx_mcdi_cmd_release(struct kref *ref)
+{
+ kfree(container_of(ref, struct cdx_mcdi_cmd, ref));
+}
+
+static unsigned int cdx_mcdi_cmd_handle(struct cdx_mcdi_cmd *cmd)
+{
+ return cmd->handle;
+}
+
+static void _cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd,
+ struct list_head *cleanup_list)
+{
+ /* if cancelled, the completers have already been called */
+ if (cdx_cmd_cancelled(cmd))
+ return;
+
+ if (cmd->completer) {
+ list_add_tail(&cmd->cleanup_list, cleanup_list);
+ ++mcdi->outstanding_cleanups;
+ kref_get(&cmd->ref);
+ }
+}
+
+static void cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd,
+ struct list_head *cleanup_list)
+{
+ list_del(&cmd->list);
+ _cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
+ cmd->state = MCDI_STATE_FINISHED;
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+ if (list_empty(&mcdi->cmd_list))
+ wake_up(&mcdi->cmd_complete_wq);
+}
+
+static unsigned long cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
+{
+ if (!cdx->mcdi_ops->mcdi_rpc_timeout)
+ return MCDI_RPC_TIMEOUT;
+ else
+ return cdx->mcdi_ops->mcdi_rpc_timeout(cdx, cmd);
+}
+
+/**
+ * cdx_mcdi_init - Initialize MCDI (Management Controller Driver Interface) state
+ * @cdx: Handle to the CDX MCDI structure
+ *
+ * This function allocates and initializes internal MCDI structures and resources
+ * for the CDX device, including the workqueue, locking primitives, and command
+ * tracking mechanisms. It sets the initial operating mode and prepares the device
+ * for MCDI operations.
+ *
+ * Return:
+ * * 0 - on success
+ * * -ENOMEM - if memory allocation or workqueue creation fails
+ */
+int cdx_mcdi_init(struct cdx_mcdi *cdx)
+{
+ struct cdx_mcdi_iface *mcdi;
+ int rc = -ENOMEM;
+
+ cdx->mcdi = kzalloc(sizeof(*cdx->mcdi), GFP_KERNEL);
+ if (!cdx->mcdi)
+ goto fail;
+
+ mcdi = cdx_mcdi_if(cdx);
+ mcdi->cdx = cdx;
+
+ mcdi->workqueue = alloc_ordered_workqueue("mcdi_wq", 0);
+ if (!mcdi->workqueue)
+ goto fail2;
+ mutex_init(&mcdi->iface_lock);
+ mcdi->mode = MCDI_MODE_EVENTS;
+ INIT_LIST_HEAD(&mcdi->cmd_list);
+ init_waitqueue_head(&mcdi->cmd_complete_wq);
+
+ mcdi->new_epoch = true;
+
+ return 0;
+fail2:
+ kfree(cdx->mcdi);
+ cdx->mcdi = NULL;
+fail:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cdx_mcdi_init);
+
+/**
+ * cdx_mcdi_finish - Cleanup MCDI (Management Controller Driver Interface) state
+ * @cdx: Handle to the CDX MCDI structure
+ *
+ * This function is responsible for cleaning up the MCDI (Management Controller Driver Interface)
+ * resources associated with a cdx_mcdi structure. Also destroys the mcdi workqueue.
+ *
+ */
+void cdx_mcdi_finish(struct cdx_mcdi *cdx)
+{
+ struct cdx_mcdi_iface *mcdi;
+
+ mcdi = cdx_mcdi_if(cdx);
+ if (!mcdi)
+ return;
+
+ cdx_mcdi_wait_for_cleanup(cdx);
+
+ destroy_workqueue(mcdi->workqueue);
+ kfree(cdx->mcdi);
+ cdx->mcdi = NULL;
+}
+EXPORT_SYMBOL_GPL(cdx_mcdi_finish);
+
+static bool cdx_mcdi_flushed(struct cdx_mcdi_iface *mcdi, bool ignore_cleanups)
+{
+ bool flushed;
+
+ mutex_lock(&mcdi->iface_lock);
+ flushed = list_empty(&mcdi->cmd_list) &&
+ (ignore_cleanups || !mcdi->outstanding_cleanups);
+ mutex_unlock(&mcdi->iface_lock);
+ return flushed;
+}
+
+/* Wait for outstanding MCDI commands to complete. */
+static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+
+ if (!mcdi)
+ return;
+
+ wait_event(mcdi->cmd_complete_wq,
+ cdx_mcdi_flushed(mcdi, false));
+}
+
+int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
+ unsigned int timeout_jiffies)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
+ int rc = 0;
+
+ if (!mcdi)
+ return -EINVAL;
+
+ flush_workqueue(mcdi->workqueue);
+
+ add_wait_queue(&mcdi->cmd_complete_wq, &wait);
+
+ while (!cdx_mcdi_flushed(mcdi, true)) {
+ rc = wait_woken(&wait, TASK_IDLE, timeout_jiffies);
+ if (rc)
+ continue;
+ break;
+ }
+
+ remove_wait_queue(&mcdi->cmd_complete_wq, &wait);
+
+ if (rc > 0)
+ rc = 0;
+ else if (rc == 0)
+ rc = -ETIMEDOUT;
+
+ return rc;
+}
+
+static u8 cdx_mcdi_payload_csum(const struct cdx_dword *hdr, size_t hdr_len,
+ const struct cdx_dword *sdu, size_t sdu_len)
+{
+ u8 *p = (u8 *)hdr;
+ u8 csum = 0;
+ int i;
+
+ for (i = 0; i < hdr_len; i++)
+ csum += p[i];
+
+ p = (u8 *)sdu;
+ for (i = 0; i < sdu_len; i++)
+ csum += p[i];
+
+ return ~csum & 0xff;
+}
+
+static void cdx_mcdi_send_request(struct cdx_mcdi *cdx,
+ struct cdx_mcdi_cmd *cmd)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+ const struct cdx_dword *inbuf = cmd->inbuf;
+ size_t inlen = cmd->inlen;
+ struct cdx_dword hdr[2];
+ size_t hdr_len;
+ bool not_epoch;
+ u32 xflags;
+
+ if (!mcdi)
+ return;
+
+ mcdi->prev_seq = cmd->seq;
+ mcdi->seq_held_by[cmd->seq] = cmd;
+ mcdi->db_held_by = cmd;
+ cmd->started = jiffies;
+
+ not_epoch = !mcdi->new_epoch;
+ xflags = 0;
+
+ /* MCDI v2 */
+ WARN_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2);
+ CDX_POPULATE_DWORD_7(hdr[0],
+ MCDI_HEADER_RESPONSE, 0,
+ MCDI_HEADER_RESYNC, 1,
+ MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
+ MCDI_HEADER_DATALEN, 0,
+ MCDI_HEADER_SEQ, cmd->seq,
+ MCDI_HEADER_XFLAGS, xflags,
+ MCDI_HEADER_NOT_EPOCH, not_epoch);
+ CDX_POPULATE_DWORD_3(hdr[1],
+ MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd->cmd,
+ MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen,
+ MC_CMD_V2_EXTN_IN_MESSAGE_TYPE,
+ MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM);
+ hdr_len = 8;
+
+ hdr[0].cdx_u32 |= (__force __le32)(cdx_mcdi_payload_csum(hdr, hdr_len, inbuf, inlen) <<
+ MCDI_HEADER_XFLAGS_LBN);
+
+ print_hex_dump_debug("MCDI REQ HEADER: ", DUMP_PREFIX_NONE, 32, 4, hdr, hdr_len, false);
+ print_hex_dump_debug("MCDI REQ PAYLOAD: ", DUMP_PREFIX_NONE, 32, 4, inbuf, inlen, false);
+
+ cdx->mcdi_ops->mcdi_request(cdx, hdr, hdr_len, inbuf, inlen);
+
+ mcdi->new_epoch = false;
+}
+
+static int cdx_mcdi_errno(struct cdx_mcdi *cdx, unsigned int mcdi_err)
+{
+ switch (mcdi_err) {
+ case 0:
+ case MC_CMD_ERR_QUEUE_FULL:
+ return mcdi_err;
+ case MC_CMD_ERR_EPERM:
+ return -EPERM;
+ case MC_CMD_ERR_ENOENT:
+ return -ENOENT;
+ case MC_CMD_ERR_EINTR:
+ return -EINTR;
+ case MC_CMD_ERR_EAGAIN:
+ return -EAGAIN;
+ case MC_CMD_ERR_EACCES:
+ return -EACCES;
+ case MC_CMD_ERR_EBUSY:
+ return -EBUSY;
+ case MC_CMD_ERR_EINVAL:
+ return -EINVAL;
+ case MC_CMD_ERR_ERANGE:
+ return -ERANGE;
+ case MC_CMD_ERR_EDEADLK:
+ return -EDEADLK;
+ case MC_CMD_ERR_ENOSYS:
+ return -EOPNOTSUPP;
+ case MC_CMD_ERR_ETIME:
+ return -ETIME;
+ case MC_CMD_ERR_EALREADY:
+ return -EALREADY;
+ case MC_CMD_ERR_ENOSPC:
+ return -ENOSPC;
+ case MC_CMD_ERR_ENOMEM:
+ return -ENOMEM;
+ case MC_CMD_ERR_ENOTSUP:
+ return -EOPNOTSUPP;
+ case MC_CMD_ERR_ALLOC_FAIL:
+ return -ENOBUFS;
+ case MC_CMD_ERR_MAC_EXIST:
+ return -EADDRINUSE;
+ case MC_CMD_ERR_NO_EVB_PORT:
+ return -EAGAIN;
+ default:
+ return -EPROTO;
+ }
+}
+
+static void cdx_mcdi_process_cleanup_list(struct cdx_mcdi *cdx,
+ struct list_head *cleanup_list)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+ unsigned int cleanups = 0;
+
+ if (!mcdi)
+ return;
+
+ while (!list_empty(cleanup_list)) {
+ struct cdx_mcdi_cmd *cmd =
+ list_first_entry(cleanup_list,
+ struct cdx_mcdi_cmd, cleanup_list);
+ cmd->completer(cdx, cmd->cookie, cmd->rc,
+ cmd->outbuf, cmd->outlen);
+ list_del(&cmd->cleanup_list);
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+ ++cleanups;
+ }
+
+ if (cleanups) {
+ bool all_done;
+
+ mutex_lock(&mcdi->iface_lock);
+ CDX_WARN_ON_PARANOID(cleanups > mcdi->outstanding_cleanups);
+ all_done = (mcdi->outstanding_cleanups -= cleanups) == 0;
+ mutex_unlock(&mcdi->iface_lock);
+ if (all_done)
+ wake_up(&mcdi->cmd_complete_wq);
+ }
+}
+
+static void _cdx_mcdi_cancel_cmd(struct cdx_mcdi_iface *mcdi,
+ unsigned int handle,
+ struct list_head *cleanup_list)
+{
+ struct cdx_mcdi_cmd *cmd;
+
+ list_for_each_entry(cmd, &mcdi->cmd_list, list)
+ if (cdx_mcdi_cmd_handle(cmd) == handle) {
+ switch (cmd->state) {
+ case MCDI_STATE_QUEUED:
+ case MCDI_STATE_RETRY:
+ pr_debug("command %#x inlen %zu cancelled in queue\n",
+ cmd->cmd, cmd->inlen);
+ /* if not yet running, properly cancel it */
+ cmd->rc = -EPIPE;
+ cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
+ break;
+ case MCDI_STATE_RUNNING:
+ case MCDI_STATE_RUNNING_CANCELLED:
+ case MCDI_STATE_FINISHED:
+ default:
+ /* invalid state? */
+ WARN_ON(1);
+ }
+ break;
+ }
+}
+
+static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+ LIST_HEAD(cleanup_list);
+
+ if (!mcdi)
+ return;
+
+ mutex_lock(&mcdi->iface_lock);
+ cdx_mcdi_timeout_cmd(mcdi, cmd, &cleanup_list);
+ mutex_unlock(&mcdi->iface_lock);
+ cdx_mcdi_process_cleanup_list(cdx, &cleanup_list);
+}
+
+struct cdx_mcdi_blocking_data {
+ struct kref ref;
+ bool done;
+ wait_queue_head_t wq;
+ int rc;
+ struct cdx_dword *outbuf;
+ size_t outlen;
+ size_t outlen_actual;
+};
+
+static void cdx_mcdi_blocking_data_release(struct kref *ref)
+{
+ kfree(container_of(ref, struct cdx_mcdi_blocking_data, ref));
+}
+
+static void cdx_mcdi_rpc_completer(struct cdx_mcdi *cdx, unsigned long cookie,
+ int rc, struct cdx_dword *outbuf,
+ size_t outlen_actual)
+{
+ struct cdx_mcdi_blocking_data *wait_data =
+ (struct cdx_mcdi_blocking_data *)cookie;
+
+ wait_data->rc = rc;
+ memcpy(wait_data->outbuf, outbuf,
+ min(outlen_actual, wait_data->outlen));
+ wait_data->outlen_actual = outlen_actual;
+ /* memory barrier */
+ smp_wmb();
+ wait_data->done = true;
+ wake_up(&wait_data->wq);
+ kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
+}
+
+static int cdx_mcdi_rpc_sync(struct cdx_mcdi *cdx, unsigned int cmd,
+ const struct cdx_dword *inbuf, size_t inlen,
+ struct cdx_dword *outbuf, size_t outlen,
+ size_t *outlen_actual, bool quiet)
+{
+ struct cdx_mcdi_blocking_data *wait_data;
+ struct cdx_mcdi_cmd *cmd_item;
+ unsigned int handle;
+ int rc;
+
+ if (outlen_actual)
+ *outlen_actual = 0;
+
+ wait_data = kmalloc(sizeof(*wait_data), GFP_KERNEL);
+ if (!wait_data)
+ return -ENOMEM;
+
+ cmd_item = kmalloc(sizeof(*cmd_item), GFP_KERNEL);
+ if (!cmd_item) {
+ kfree(wait_data);
+ return -ENOMEM;
+ }
+
+ kref_init(&wait_data->ref);
+ wait_data->done = false;
+ init_waitqueue_head(&wait_data->wq);
+ wait_data->outbuf = outbuf;
+ wait_data->outlen = outlen;
+
+ kref_init(&cmd_item->ref);
+ cmd_item->quiet = quiet;
+ cmd_item->cookie = (unsigned long)wait_data;
+ cmd_item->completer = &cdx_mcdi_rpc_completer;
+ cmd_item->cmd = cmd;
+ cmd_item->inlen = inlen;
+ cmd_item->inbuf = inbuf;
+
+ /* Claim an extra reference for the completer to put. */
+ kref_get(&wait_data->ref);
+ rc = cdx_mcdi_rpc_async_internal(cdx, cmd_item, &handle);
+ if (rc) {
+ kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
+ goto out;
+ }
+
+ if (!wait_event_timeout(wait_data->wq, wait_data->done,
+ cdx_mcdi_rpc_timeout(cdx, cmd)) &&
+ !wait_data->done) {
+ pr_err("MC command 0x%x inlen %zu timed out (sync)\n",
+ cmd, inlen);
+
+ cdx_mcdi_cancel_cmd(cdx, cmd_item);
+
+ wait_data->rc = -ETIMEDOUT;
+ wait_data->outlen_actual = 0;
+ }
+
+ if (outlen_actual)
+ *outlen_actual = wait_data->outlen_actual;
+ rc = wait_data->rc;
+
+out:
+ kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
+
+ return rc;
+}
+
+static bool cdx_mcdi_get_seq(struct cdx_mcdi_iface *mcdi, unsigned char *seq)
+{
+ *seq = mcdi->prev_seq;
+ do {
+ *seq = (*seq + 1) % ARRAY_SIZE(mcdi->seq_held_by);
+ } while (mcdi->seq_held_by[*seq] && *seq != mcdi->prev_seq);
+ return !mcdi->seq_held_by[*seq];
+}
+
+static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
+ struct cdx_mcdi_cmd *cmd,
+ unsigned int *handle)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+ LIST_HEAD(cleanup_list);
+
+ if (!mcdi) {
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+ return -ENETDOWN;
+ }
+
+ if (mcdi->mode == MCDI_MODE_FAIL) {
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+ return -ENETDOWN;
+ }
+
+ cmd->mcdi = mcdi;
+ INIT_WORK(&cmd->work, cdx_mcdi_cmd_work);
+ INIT_LIST_HEAD(&cmd->list);
+ INIT_LIST_HEAD(&cmd->cleanup_list);
+ cmd->rc = 0;
+ cmd->outbuf = NULL;
+ cmd->outlen = 0;
+
+ queue_work(mcdi->workqueue, &cmd->work);
+ return 0;
+}
+
+static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd)
+{
+ struct cdx_mcdi *cdx = mcdi->cdx;
+ u8 seq;
+
+ if (!mcdi->db_held_by &&
+ cdx_mcdi_get_seq(mcdi, &seq)) {
+ cmd->seq = seq;
+ cmd->reboot_seen = false;
+ cdx_mcdi_send_request(cdx, cmd);
+ cmd->state = MCDI_STATE_RUNNING;
+ } else {
+ cmd->state = MCDI_STATE_QUEUED;
+ }
+}
+
+/* try to advance other commands */
+static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
+ bool allow_retry)
+{
+ struct cdx_mcdi_cmd *cmd, *tmp;
+
+ list_for_each_entry_safe(cmd, tmp, &mcdi->cmd_list, list)
+ if (cmd->state == MCDI_STATE_QUEUED ||
+ (cmd->state == MCDI_STATE_RETRY && allow_retry))
+ cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
+}
+
+/**
+ * cdx_mcdi_process_cmd - Process an incoming MCDI response
+ * @cdx: Handle to the CDX MCDI structure
+ * @outbuf: Pointer to the response buffer received from the management controller
+ * @len: Length of the response buffer in bytes
+ *
+ * This function handles a response from the management controller. It locates the
+ * corresponding command using the sequence number embedded in the header,
+ * completes the command if it is still pending, and initiates any necessary cleanup.
+ *
+ * The function assumes that the response buffer is well-formed and at least one
+ * dword in size.
+ */
+void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len)
+{
+ struct cdx_mcdi_iface *mcdi;
+ struct cdx_mcdi_cmd *cmd;
+ LIST_HEAD(cleanup_list);
+ unsigned int respseq;
+
+ if (!len || !outbuf) {
+ pr_err("Got empty MC response\n");
+ return;
+ }
+
+ mcdi = cdx_mcdi_if(cdx);
+ if (!mcdi)
+ return;
+
+ respseq = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_SEQ);
+
+ mutex_lock(&mcdi->iface_lock);
+ cmd = mcdi->seq_held_by[respseq];
+
+ if (cmd) {
+ if (cmd->state == MCDI_STATE_FINISHED) {
+ mutex_unlock(&mcdi->iface_lock);
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+ return;
+ }
+
+ cdx_mcdi_complete_cmd(mcdi, cmd, outbuf, len, &cleanup_list);
+ } else {
+ pr_err("MC response unexpected for seq : %0X\n", respseq);
+ }
+
+ mutex_unlock(&mcdi->iface_lock);
+
+ cdx_mcdi_process_cleanup_list(mcdi->cdx, &cleanup_list);
+}
+EXPORT_SYMBOL_GPL(cdx_mcdi_process_cmd);
+
+static void cdx_mcdi_cmd_work(struct work_struct *context)
+{
+ struct cdx_mcdi_cmd *cmd =
+ container_of(context, struct cdx_mcdi_cmd, work);
+ struct cdx_mcdi_iface *mcdi = cmd->mcdi;
+
+ mutex_lock(&mcdi->iface_lock);
+
+ cmd->handle = mcdi->prev_handle++;
+ list_add_tail(&cmd->list, &mcdi->cmd_list);
+ cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
+
+ mutex_unlock(&mcdi->iface_lock);
+}
+
+/*
+ * Returns true if the MCDI module is finished with the command.
+ * (examples of false would be if the command was proxied, or it was
+ * rejected by the MC due to lack of resources and requeued).
+ */
+static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd,
+ struct cdx_dword *outbuf,
+ int len,
+ struct list_head *cleanup_list)
+{
+ size_t resp_hdr_len, resp_data_len;
+ struct cdx_mcdi *cdx = mcdi->cdx;
+ unsigned int respcmd, error;
+ bool completed = false;
+ int rc;
+
+ /* ensure the command can't go away before this function returns */
+ kref_get(&cmd->ref);
+
+ respcmd = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_CODE);
+ error = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_ERROR);
+
+ if (respcmd != MC_CMD_V2_EXTN) {
+ resp_hdr_len = 4;
+ resp_data_len = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_DATALEN);
+ } else {
+ resp_data_len = 0;
+ resp_hdr_len = 8;
+ if (len >= 8)
+ resp_data_len =
+ CDX_DWORD_FIELD(outbuf[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
+ }
+
+ if ((resp_hdr_len + resp_data_len) > len) {
+ pr_warn("Incomplete MCDI response received %d. Expected %zu\n",
+ len, (resp_hdr_len + resp_data_len));
+ resp_data_len = 0;
+ }
+
+ print_hex_dump_debug("MCDI RESP HEADER: ", DUMP_PREFIX_NONE, 32, 4,
+ outbuf, resp_hdr_len, false);
+ print_hex_dump_debug("MCDI RESP PAYLOAD: ", DUMP_PREFIX_NONE, 32, 4,
+ outbuf + (resp_hdr_len / 4), resp_data_len, false);
+
+ if (error && resp_data_len == 0) {
+ /* MC rebooted during command */
+ rc = -EIO;
+ } else {
+ if (WARN_ON_ONCE(error && resp_data_len < 4))
+ resp_data_len = 4;
+ if (error) {
+ rc = CDX_DWORD_FIELD(outbuf[resp_hdr_len / 4], CDX_DWORD);
+ if (!cmd->quiet) {
+ int err_arg = 0;
+
+ if (resp_data_len >= MC_CMD_ERR_ARG_OFST + 4) {
+ int offset = (resp_hdr_len + MC_CMD_ERR_ARG_OFST) / 4;
+
+ err_arg = CDX_DWORD_VAL(outbuf[offset]);
+ }
+
+ _cdx_mcdi_display_error(cdx, cmd->cmd,
+ cmd->inlen, rc, err_arg,
+ cdx_mcdi_errno(cdx, rc));
+ }
+ rc = cdx_mcdi_errno(cdx, rc);
+ } else {
+ rc = 0;
+ }
+ }
+
+ /* free doorbell */
+ if (mcdi->db_held_by == cmd)
+ mcdi->db_held_by = NULL;
+
+ if (cdx_cmd_cancelled(cmd)) {
+ list_del(&cmd->list);
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+ completed = true;
+ } else if (rc == MC_CMD_ERR_QUEUE_FULL) {
+ cmd->state = MCDI_STATE_RETRY;
+ } else {
+ cmd->rc = rc;
+ cmd->outbuf = outbuf + DIV_ROUND_UP(resp_hdr_len, 4);
+ cmd->outlen = resp_data_len;
+ cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
+ completed = true;
+ }
+
+ /* free sequence number and buffer */
+ mcdi->seq_held_by[cmd->seq] = NULL;
+
+ cdx_mcdi_start_or_queue(mcdi, rc != MC_CMD_ERR_QUEUE_FULL);
+
+ /* wake up anyone waiting for flush */
+ wake_up(&mcdi->cmd_complete_wq);
+
+ kref_put(&cmd->ref, cdx_mcdi_cmd_release);
+
+ return completed;
+}
+
+static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
+ struct cdx_mcdi_cmd *cmd,
+ struct list_head *cleanup_list)
+{
+ struct cdx_mcdi *cdx = mcdi->cdx;
+
+ pr_err("MC command 0x%x inlen %zu state %d timed out after %u ms\n",
+ cmd->cmd, cmd->inlen, cmd->state,
+ jiffies_to_msecs(jiffies - cmd->started));
+
+ cmd->rc = -ETIMEDOUT;
+ cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
+
+ cdx_mcdi_mode_fail(cdx, cleanup_list);
+}
+
+/**
+ * cdx_mcdi_rpc - Issue an MCDI command and wait for completion
+ * @cdx: NIC through which to issue the command
+ * @cmd: Command type number
+ * @inbuf: Command parameters
+ * @inlen: Length of command parameters, in bytes. Must be a multiple
+ * of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
+ * @outbuf: Response buffer. May be %NULL if @outlen is 0.
+ * @outlen: Length of response buffer, in bytes. If the actual
+ * response is longer than @outlen & ~3, it will be truncated
+ * to that length.
+ * @outlen_actual: Pointer through which to return the actual response
+ * length. May be %NULL if this is not needed.
+ *
+ * This function may sleep and therefore must be called in process
+ * context.
+ *
+ * Return: A negative error code, or zero if successful. The error
+ * code may come from the MCDI response or may indicate a failure
+ * to communicate with the MC. In the former case, the response
+ * will still be copied to @outbuf and *@outlen_actual will be
+ * set accordingly. In the latter case, *@outlen_actual will be
+ * set to zero.
+ */
+int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd,
+ const struct cdx_dword *inbuf, size_t inlen,
+ struct cdx_dword *outbuf, size_t outlen,
+ size_t *outlen_actual)
+{
+ return cdx_mcdi_rpc_sync(cdx, cmd, inbuf, inlen, outbuf, outlen,
+ outlen_actual, false);
+}
+EXPORT_SYMBOL_GPL(cdx_mcdi_rpc);
+
+/**
+ * cdx_mcdi_rpc_async - Schedule an MCDI command to run asynchronously
+ * @cdx: NIC through which to issue the command
+ * @cmd: Command type number
+ * @inbuf: Command parameters
+ * @inlen: Length of command parameters, in bytes
+ * @complete: Function to be called on completion or cancellation.
+ * @cookie: Arbitrary value to be passed to @complete.
+ *
+ * This function does not sleep and therefore may be called in atomic
+ * context. It will fail if event queues are disabled or if MCDI
+ * event completions have been disabled due to an error.
+ *
+ * If it succeeds, the @complete function will be called exactly once
+ * in process context, when one of the following occurs:
+ * (a) the completion event is received (in process context)
+ * (b) event queues are disabled (in the process that disables them)
+ */
+int
+cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
+ const struct cdx_dword *inbuf, size_t inlen,
+ cdx_mcdi_async_completer *complete, unsigned long cookie)
+{
+ struct cdx_mcdi_cmd *cmd_item =
+ kmalloc(sizeof(struct cdx_mcdi_cmd) + inlen, GFP_ATOMIC);
+
+ if (!cmd_item)
+ return -ENOMEM;
+
+ kref_init(&cmd_item->ref);
+ cmd_item->quiet = true;
+ cmd_item->cookie = cookie;
+ cmd_item->completer = complete;
+ cmd_item->cmd = cmd;
+ cmd_item->inlen = inlen;
+ /* inbuf is probably not valid after return, so take a copy */
+ cmd_item->inbuf = (struct cdx_dword *)(cmd_item + 1);
+ memcpy(cmd_item + 1, inbuf, inlen);
+
+ return cdx_mcdi_rpc_async_internal(cdx, cmd_item, NULL);
+}
+
+static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
+ size_t inlen, int raw, int arg, int err_no)
+{
+ pr_err("MC command 0x%x inlen %d failed err_no=%d (raw=%d) arg=%d\n",
+ cmd, (int)inlen, err_no, raw, arg);
+}
+
+/*
+ * Set MCDI mode to fail to prevent any new commands, then cancel any
+ * outstanding commands.
+ * Caller must hold the mcdi iface_lock.
+ */
+static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list)
+{
+ struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
+
+ if (!mcdi)
+ return;
+
+ mcdi->mode = MCDI_MODE_FAIL;
+
+ while (!list_empty(&mcdi->cmd_list)) {
+ struct cdx_mcdi_cmd *cmd;
+
+ cmd = list_first_entry(&mcdi->cmd_list, struct cdx_mcdi_cmd,
+ list);
+ _cdx_mcdi_cancel_cmd(mcdi, cdx_mcdi_cmd_handle(cmd), cleanup_list);
+ }
+}
diff --git a/drivers/cdx/controller/mcdi_functions.c b/drivers/cdx/controller/mcdi_functions.c
new file mode 100644
index 000000000000..8ae2d99be81e
--- /dev/null
+++ b/drivers/cdx/controller/mcdi_functions.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/module.h>
+
+#include "mcdi_functions.h"
+
+int cdx_mcdi_get_num_buses(struct cdx_mcdi *cdx)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN);
+ size_t outlen;
+ int ret;
+
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_ENUM_BUSES, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (ret)
+ return ret;
+
+ if (outlen != MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN)
+ return -EIO;
+
+ return MCDI_DWORD(outbuf, CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT);
+}
+
+int cdx_mcdi_get_num_devs(struct cdx_mcdi *cdx, int bus_num)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN);
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_ENUM_DEVICES_IN_LEN);
+ size_t outlen;
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_BUS_ENUM_DEVICES_IN_BUS, bus_num);
+
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_ENUM_DEVICES, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (ret)
+ return ret;
+
+ if (outlen != MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN)
+ return -EIO;
+
+ return MCDI_DWORD(outbuf, CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT);
+}
+
+int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
+ u8 bus_num, u8 dev_num,
+ struct cdx_dev_params *dev_params)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN);
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN);
+ struct resource *res = &dev_params->res[0];
+ size_t outlen;
+ u32 req_id;
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_BUS_GET_DEVICE_CONFIG_IN_BUS, bus_num);
+ MCDI_SET_DWORD(inbuf, CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE, dev_num);
+
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (ret)
+ return ret;
+
+ if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN)
+ return -EIO;
+
+ dev_params->bus_num = bus_num;
+ dev_params->dev_num = dev_num;
+
+ req_id = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID);
+ dev_params->req_id = req_id;
+
+ dev_params->msi_dev_id = MCDI_DWORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID);
+
+ dev_params->res_count = 0;
+ if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) != 0) {
+ res[dev_params->res_count].start =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE);
+ res[dev_params->res_count].end =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE) +
+ MCDI_QWORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) - 1;
+ res[dev_params->res_count].flags = IORESOURCE_MEM;
+ dev_params->res_count++;
+ }
+
+ if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE) != 0) {
+ res[dev_params->res_count].start =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE);
+ res[dev_params->res_count].end =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE) +
+ MCDI_QWORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE) - 1;
+ res[dev_params->res_count].flags = IORESOURCE_MEM;
+ dev_params->res_count++;
+ }
+
+ if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE) != 0) {
+ res[dev_params->res_count].start =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE);
+ res[dev_params->res_count].end =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE) +
+ MCDI_QWORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE) - 1;
+ res[dev_params->res_count].flags = IORESOURCE_MEM;
+ dev_params->res_count++;
+ }
+
+ if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE) != 0) {
+ res[dev_params->res_count].start =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE);
+ res[dev_params->res_count].end =
+ MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE) +
+ MCDI_QWORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE) - 1;
+ res[dev_params->res_count].flags = IORESOURCE_MEM;
+ dev_params->res_count++;
+ }
+
+ dev_params->vendor = MCDI_WORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID);
+ dev_params->device = MCDI_WORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID);
+ dev_params->subsys_vendor = MCDI_WORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID);
+ dev_params->subsys_device = MCDI_WORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID);
+ dev_params->class = MCDI_DWORD(outbuf,
+ CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS) & 0xFFFFFF;
+ dev_params->revision = MCDI_BYTE(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION);
+ dev_params->num_msi = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT);
+
+ return 0;
+}
+
+int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_UP_IN_LEN);
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_BUS_UP_IN_BUS, bus_num);
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_UP, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+
+ return ret;
+}
+
+int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_DOWN_IN_LEN);
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_BUS_DOWN_IN_BUS, bus_num);
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_DOWN, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+
+ return ret;
+}
+
+int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num,
+ u32 msi_vector, u64 msi_address, u32 msi_data)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN);
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_BUS, bus_num);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE, dev_num);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR, msi_vector);
+ MCDI_SET_QWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS, msi_address);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA, msi_data);
+
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+
+ return ret;
+}
+
+int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN);
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_BUS, bus_num);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_DEVICE, dev_num);
+
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_RESET, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+
+ return ret;
+}
+
+static int cdx_mcdi_ctrl_flag_get(struct cdx_mcdi *cdx, u8 bus_num,
+ u8 dev_num, u32 *flags)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_CONTROL_GET_IN_LEN);
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_LEN);
+ size_t outlen;
+ int ret;
+
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_CONTROL_GET_IN_BUS, bus_num);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_CONTROL_GET_IN_DEVICE, dev_num);
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_CONTROL_GET, inbuf,
+ sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
+ if (ret)
+ return ret;
+
+ if (outlen != MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_LEN)
+ return -EIO;
+
+ *flags = MCDI_DWORD(outbuf, CDX_DEVICE_CONTROL_GET_OUT_FLAGS);
+
+ return 0;
+}
+
+static int cdx_mcdi_ctrl_flag_set(struct cdx_mcdi *cdx, u8 bus_num,
+ u8 dev_num, bool enable, int bit_pos)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_CONTROL_SET_IN_LEN);
+ u32 flags;
+ int ret;
+
+ /*
+ * Get flags and then set/reset bit at bit_pos according to
+ * the input params.
+ */
+ ret = cdx_mcdi_ctrl_flag_get(cdx, bus_num, dev_num, &flags);
+ if (ret)
+ return ret;
+
+ flags = flags & (u32)(~(BIT(bit_pos)));
+ if (enable)
+ flags |= (1 << bit_pos);
+
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_CONTROL_SET_IN_BUS, bus_num);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_CONTROL_SET_IN_DEVICE, dev_num);
+ MCDI_SET_DWORD(inbuf, CDX_DEVICE_CONTROL_SET_IN_FLAGS, flags);
+ ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_CONTROL_SET, inbuf,
+ sizeof(inbuf), NULL, 0, NULL);
+
+ return ret;
+}
+
+int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num,
+ u8 dev_num, bool enable)
+{
+ return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable,
+ MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN);
+}
+
+int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num,
+ u8 dev_num, bool enable)
+{
+ return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable,
+ MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN);
+}
diff --git a/drivers/cdx/controller/mcdi_functions.h b/drivers/cdx/controller/mcdi_functions.h
new file mode 100644
index 000000000000..57fd1bae706b
--- /dev/null
+++ b/drivers/cdx/controller/mcdi_functions.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Header file for MCDI FW interaction for CDX bus.
+ *
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#ifndef CDX_MCDI_FUNCTIONS_H
+#define CDX_MCDI_FUNCTIONS_H
+
+#include <linux/cdx/mcdi.h>
+#include "mcdid.h"
+#include "../cdx.h"
+
+/**
+ * cdx_mcdi_get_num_buses - Get the total number of buses on
+ * the controller.
+ * @cdx: pointer to MCDI interface.
+ *
+ * Return: total number of buses available on the controller,
+ * <0 on failure
+ */
+int cdx_mcdi_get_num_buses(struct cdx_mcdi *cdx);
+
+/**
+ * cdx_mcdi_get_num_devs - Get the total number of devices on
+ * a particular bus of the controller.
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ *
+ * Return: total number of devices available on the bus, <0 on failure
+ */
+int cdx_mcdi_get_num_devs(struct cdx_mcdi *cdx, int bus_num);
+
+/**
+ * cdx_mcdi_get_dev_config - Get configuration for a particular
+ * bus_num:dev_num
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ * @dev_params: Pointer to cdx_dev_params, this is populated by this
+ * device with the configuration corresponding to the provided
+ * bus_num:dev_num.
+ *
+ * Return: 0 total number of devices available on the bus, <0 on failure
+ */
+int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
+ u8 bus_num, u8 dev_num,
+ struct cdx_dev_params *dev_params);
+
+/**
+ * cdx_mcdi_bus_enable - Enable CDX bus represented by bus_num
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ *
+ * Return: 0 on success, <0 on failure
+ */
+int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num);
+
+/**
+ * cdx_mcdi_bus_disable - Disable CDX bus represented by bus_num
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ *
+ * Return: 0 on success, <0 on failure
+ */
+int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num);
+
+/**
+ * cdx_mcdi_write_msi - Write MSI configuration for CDX device
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ * @msi_vector: Device-relative MSI vector number.
+ * Must be < MSI_COUNT reported for the device.
+ * @msi_address: MSI address to be used by the hardware. Typically, on ARM
+ * systems this address is translated by the IOMMU (if enabled) and
+ * it is the responsibility of the entity managing the IOMMU (APU kernel)
+ * to supply the correct IOVA here.
+ * @msi_data: MSI data to be used by the hardware. On versal-net, only the
+ * lower 16-bits are used, the remaining bits are ignored and should be
+ * set to zero.
+ *
+ * Return: 0 on success, <0 on failure
+ */
+int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num,
+ u32 msi_vector, u64 msi_address, u32 msi_data);
+
+/**
+ * cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ *
+ * Return: 0 on success, <0 on failure
+ */
+int cdx_mcdi_reset_device(struct cdx_mcdi *cdx,
+ u8 bus_num, u8 dev_num);
+
+/**
+ * cdx_mcdi_bus_master_enable - Set/Reset bus mastering for cdx device
+ * represented by bus_num:dev_num
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ * @enable: Enable bus mastering if set, disable otherwise.
+ *
+ * Return: 0 on success, <0 on failure
+ */
+int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num,
+ u8 dev_num, bool enable);
+
+/**
+ * cdx_mcdi_msi_enable - Enable/Disable MSIs for cdx device represented
+ * by bus_num:dev_num
+ * @cdx: pointer to MCDI interface.
+ * @bus_num: Bus number.
+ * @dev_num: Device number.
+ * @enable: Enable msi's if set, disable otherwise.
+ *
+ * Return: 0 on success, <0 on failure
+ */
+int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num,
+ u8 dev_num, bool enable);
+
+#endif /* CDX_MCDI_FUNCTIONS_H */
diff --git a/drivers/cdx/controller/mcdid.h b/drivers/cdx/controller/mcdid.h
new file mode 100644
index 000000000000..7fc29f099265
--- /dev/null
+++ b/drivers/cdx/controller/mcdid.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2008-2013 Solarflare Communications Inc.
+ * Copyright (C) 2022-2025, Advanced Micro Devices, Inc.
+ */
+
+#ifndef CDX_MCDID_H
+#define CDX_MCDID_H
+
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/rpmsg.h>
+
+#include "mc_cdx_pcol.h"
+
+#ifdef DEBUG
+#define CDX_WARN_ON_ONCE_PARANOID(x) WARN_ON_ONCE(x)
+#define CDX_WARN_ON_PARANOID(x) WARN_ON(x)
+#else
+#define CDX_WARN_ON_ONCE_PARANOID(x) do {} while (0)
+#define CDX_WARN_ON_PARANOID(x) do {} while (0)
+#endif
+
+#define MCDI_BUF_LEN (8 + MCDI_CTL_SDU_LEN_MAX)
+
+static inline struct cdx_mcdi_iface *cdx_mcdi_if(struct cdx_mcdi *cdx)
+{
+ return cdx->mcdi ? &cdx->mcdi->iface : NULL;
+}
+
+int cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
+ const struct cdx_dword *inbuf, size_t inlen,
+ cdx_mcdi_async_completer *complete,
+ unsigned long cookie);
+int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
+ unsigned int timeout_jiffies);
+
+/*
+ * We expect that 16- and 32-bit fields in MCDI requests and responses
+ * are appropriately aligned, but 64-bit fields are only
+ * 32-bit-aligned.
+ */
+#define MCDI_BYTE(_buf, _field) \
+ ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \
+ *MCDI_PTR(_buf, _field))
+#define MCDI_WORD(_buf, _field) \
+ ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2), \
+ le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field)))
+#define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1) \
+ CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), \
+ MC_CMD_ ## _name1, _value1)
+#define MCDI_SET_QWORD(_buf, _field, _value) \
+ do { \
+ CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[0], \
+ CDX_DWORD, (u32)(_value)); \
+ CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[1], \
+ CDX_DWORD, (u64)(_value) >> 32); \
+ } while (0)
+#define MCDI_QWORD(_buf, _field) \
+ (CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[0], CDX_DWORD) | \
+ (u64)CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[1], CDX_DWORD) << 32)
+
+#endif /* CDX_MCDID_H */
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 0f378d29dab0..d2cfc584e202 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -34,6 +34,7 @@ config TTY_PRINTK_LEVEL
config PRINTER
tristate "Parallel printer support"
depends on PARPORT
+ depends on HAS_IOPORT || PARPORT_NOT_PC
help
If you intend to attach a printer to the parallel port of your Linux
box (as opposed to using a serial printer; if the connector at the
@@ -236,7 +237,8 @@ config APPLICOM
config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support"
- depends on X86_32 && PCI && INPUT
+ depends on X86_32 && PCI && INPUT && HAS_IOPORT
+ depends on ACPI_EC || !ACPI
help
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
@@ -247,8 +249,6 @@ config SONYPI
To compile this driver as a module, choose M here: the
module will be called sonypi.
-source "drivers/char/pcmcia/Kconfig"
-
config MWAVE
tristate "ACP Modem (Mwave) support"
depends on X86 && TTY
@@ -342,14 +342,14 @@ config NVRAM
config DEVPORT
bool "/dev/port character device"
- depends on ISA || PCI
+ depends on HAS_IOPORT
default y
help
Say Y here if you want to support the /dev/port device. The /dev/port
device is similar to /dev/mem, but for I/O ports.
config HPET
- bool "HPET - High Precision Event Timer" if (X86 || IA64)
+ bool "HPET - High Precision Event Timer" if X86
default n
depends on ACPI
help
@@ -378,7 +378,7 @@ config HPET_MMAP_DEFAULT
config HANGCHECK_TIMER
tristate "Hangcheck timer"
- depends on X86 || IA64 || PPC64 || S390
+ depends on X86 || PPC64 || S390
help
The hangcheck-timer module detects when the system has gone
out to lunch past a certain margin. It can reboot the system
@@ -404,7 +404,7 @@ config TELCLOCK
configuration of the telecom clock configuration settings. This
device is used for hardware synchronization across the ATCA backplane
fabric. Upon loading, the driver exports a sysfs directory,
- /sys/devices/platform/telco_clock, with a number of files for
+ /sys/devices/faux/telco_clock, with a number of files for
controlling the behavior of this hardware.
source "drivers/s390/char/Kconfig"
@@ -423,40 +423,4 @@ config ADI
and SSM (Silicon Secured Memory). Intended consumers of this
driver include crash and makedumpfile.
-config RANDOM_TRUST_CPU
- bool "Initialize RNG using CPU RNG instructions"
- default y
- help
- Initialize the RNG using random numbers supplied by the CPU's
- RNG instructions (e.g. RDRAND), if supported and available. These
- random numbers are never used directly, but are rather hashed into
- the main input pool, and this happens regardless of whether or not
- this option is enabled. Instead, this option controls whether the
- they are credited and hence can initialize the RNG. Additionally,
- other sources of randomness are always used, regardless of this
- setting. Enabling this implies trusting that the CPU can supply high
- quality and non-backdoored random numbers.
-
- Say Y here unless you have reason to mistrust your CPU or believe
- its RNG facilities may be faulty. This may also be configured at
- boot time with "random.trust_cpu=on/off".
-
-config RANDOM_TRUST_BOOTLOADER
- bool "Initialize RNG using bootloader-supplied seed"
- default y
- help
- Initialize the RNG using a seed supplied by the bootloader or boot
- environment (e.g. EFI or a bootloader-generated device tree). This
- seed is not used directly, but is rather hashed into the main input
- pool, and this happens regardless of whether or not this option is
- enabled. Instead, this option controls whether the seed is credited
- and hence can initialize the RNG. Additionally, other sources of
- randomness are always used, regardless of this setting. Enabling
- this implies trusting that the bootloader can supply high quality and
- non-backdoored seeds.
-
- Say Y here unless you have reason to mistrust your bootloader or
- believe its RNG facilities may be faulty. This may also be configured
- at boot time with "random.trust_bootloader=on/off".
-
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 1b35d1724565..1291369b9126 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -6,9 +6,9 @@
obj-y += mem.o random.o
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
+obj-$(CONFIG_TEST_MISC_MINOR) += misc_minor_kunit.o
obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
-obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
obj-$(CONFIG_IBM_BSR) += bsr.o
@@ -35,7 +35,6 @@ obj-$(CONFIG_TELCLOCK) += tlclk.o
obj-$(CONFIG_MWAVE) += mwave/
obj-y += agp/
-obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
diff --git a/drivers/char/adi.c b/drivers/char/adi.c
index 751d7cc0da1b..0849d933a2d5 100644
--- a/drivers/char/adi.c
+++ b/drivers/char/adi.c
@@ -14,12 +14,6 @@
#define MAX_BUF_SZ PAGE_SIZE
-static int adi_open(struct inode *inode, struct file *file)
-{
- file->f_mode |= FMODE_UNSIGNED_OFFSET;
- return 0;
-}
-
static int read_mcd_tag(unsigned long addr)
{
long err;
@@ -86,8 +80,8 @@ static ssize_t adi_read(struct file *file, char __user *buf,
bytes_read += ver_buf_sz;
ver_buf_idx = 0;
- ver_buf_sz = min(count - bytes_read,
- (size_t)MAX_BUF_SZ);
+ ver_buf_sz = min_t(size_t, count - bytes_read,
+ MAX_BUF_SZ);
}
}
@@ -137,7 +131,7 @@ static ssize_t adi_write(struct file *file, const char __user *buf,
ssize_t ret;
int i;
- if (count <= 0)
+ if (count == 0)
return -EINVAL;
ver_buf_sz = min_t(size_t, count, MAX_BUF_SZ);
@@ -163,7 +157,7 @@ static ssize_t adi_write(struct file *file, const char __user *buf,
}
bytes_written += ver_buf_sz;
- ver_buf_sz = min(count - bytes_written, (size_t)MAX_BUF_SZ);
+ ver_buf_sz = min_t(size_t, count - bytes_written, MAX_BUF_SZ);
} while (bytes_written < count);
(*offp) += bytes_written;
@@ -196,7 +190,6 @@ static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
if (offset != file->f_pos) {
file->f_pos = offset;
- file->f_version = 0;
ret = offset;
}
@@ -206,9 +199,9 @@ static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
static const struct file_operations adi_fops = {
.owner = THIS_MODULE,
.llseek = adi_llseek,
- .open = adi_open,
.read = adi_read,
.write = adi_write,
+ .fop_flags = FOP_UNSIGNED_OFFSET,
};
static struct miscdevice adi_miscdev = {
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index 4f501e4842ab..c47eb7bf06d4 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
menuconfig AGP
tristate "/dev/agpgart (AGP Support)"
- depends on ALPHA || IA64 || PARISC || PPC || X86
+ depends on ALPHA || PARISC || PPC || X86
depends on PCI
help
AGP (Accelerated Graphics Port) is a bus system mainly used to
@@ -109,20 +109,6 @@ config AGP_VIA
This option gives you AGP support for the GLX component of
X on VIA MVP3/Apollo Pro chipsets.
-config AGP_I460
- tristate "Intel 460GX chipset support"
- depends on AGP && IA64
- help
- This option gives you AGP GART support for the Intel 460GX chipset
- for IA64 processors.
-
-config AGP_HP_ZX1
- tristate "HP ZX1 chipset AGP support"
- depends on AGP && IA64
- help
- This option gives you AGP GART support for the HP ZX1 chipset
- for IA64 processors.
-
config AGP_PARISC
tristate "HP Quicksilver AGP support"
depends on AGP && PARISC && 64BIT && IOMMU_SBA
diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile
index 90ed8c789e48..43b09cf193bb 100644
--- a/drivers/char/agp/Makefile
+++ b/drivers/char/agp/Makefile
@@ -1,12 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
agpgart-y := backend.o generic.o isoch.o
-ifeq ($(CONFIG_DRM_LEGACY),y)
-agpgart-$(CONFIG_COMPAT) += compat_ioctl.o
-agpgart-y += frontend.o
-endif
-
-
obj-$(CONFIG_AGP) += agpgart.o
obj-$(CONFIG_AGP_ALI) += ali-agp.o
obj-$(CONFIG_AGP_ATI) += ati-agp.o
@@ -14,9 +8,7 @@ obj-$(CONFIG_AGP_AMD) += amd-k7-agp.o
obj-$(CONFIG_AGP_AMD64) += amd64-agp.o
obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o
obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o
-obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o
obj-$(CONFIG_AGP_PARISC) += parisc-agp.o
-obj-$(CONFIG_AGP_I460) += i460-agp.o
obj-$(CONFIG_AGP_INTEL) += intel-agp.o
obj-$(CONFIG_INTEL_GTT) += intel-gtt.o
obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index bb09d64cd51e..67d7be800a7c 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -138,7 +138,6 @@ struct agp_bridge_data {
unsigned long gart_bus_addr;
unsigned long gatt_bus_addr;
u32 mode;
- enum chipset_type type;
unsigned long *key_list;
atomic_t current_memory_agp;
atomic_t agp_in_use;
@@ -185,15 +184,6 @@ void agp_put_bridge(struct agp_bridge_data *bridge);
int agp_add_bridge(struct agp_bridge_data *bridge);
void agp_remove_bridge(struct agp_bridge_data *bridge);
-/* Frontend routines. */
-#if IS_ENABLED(CONFIG_DRM_LEGACY)
-int agp_frontend_initialize(void);
-void agp_frontend_cleanup(void);
-#else
-static inline int agp_frontend_initialize(void) { return 0; }
-static inline void agp_frontend_cleanup(void) {}
-#endif
-
/* Generic routines. */
void agp_generic_enable(struct agp_bridge_data *bridge, u32 mode);
int agp_generic_create_gatt_table(struct agp_bridge_data *bridge);
@@ -236,6 +226,12 @@ void agp3_generic_tlbflush(struct agp_memory *mem);
int agp3_generic_configure(void);
void agp3_generic_cleanup(void);
+/* GATT allocation. Returns/accepts GATT kernel virtual address. */
+#define alloc_gatt_pages(order) \
+ ((char *)__get_free_pages(GFP_KERNEL, (order)))
+#define free_gatt_pages(table, order) \
+ free_pages((unsigned long)(table), (order))
+
/* aperture sizes have been standardised since v3 */
#define AGP_GENERIC_SIZES_ENTRIES 11
extern const struct aper_size_info_16 agp3_generic_sizes[];
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c
index 760d9a931289..2eaab502ec29 100644
--- a/drivers/char/agp/ali-agp.c
+++ b/drivers/char/agp/ali-agp.c
@@ -418,5 +418,6 @@ module_init(agp_ali_init);
module_exit(agp_ali_cleanup);
MODULE_AUTHOR("Dave Jones");
+MODULE_DESCRIPTION("ALi AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c
index c9bf2c219841..e1763ecb8111 100644
--- a/drivers/char/agp/alpha-agp.c
+++ b/drivers/char/agp/alpha-agp.c
@@ -149,7 +149,7 @@ struct agp_bridge_driver alpha_core_agp_driver = {
struct agp_bridge_data *alpha_bridge;
-int __init
+static int __init
alpha_core_agp_setup(void)
{
alpha_agp_info *agp = alpha_mv.agp_info();
@@ -217,4 +217,5 @@ module_init(agp_alpha_core_init);
module_exit(agp_alpha_core_cleanup);
MODULE_AUTHOR("Jeff Wiedemeier <Jeff.Wiedemeier@hp.com>");
+MODULE_DESCRIPTION("Alpha AGP support");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index 2b2095542816..795c8c9ff680 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -488,26 +488,11 @@ static void agp_amdk7_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-
-static int agp_amdk7_suspend(struct pci_dev *pdev, pm_message_t state)
+static int agp_amdk7_resume(struct device *dev)
{
- pci_save_state(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
- return 0;
-}
-
-static int agp_amdk7_resume(struct pci_dev *pdev)
-{
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
return amd_irongate_driver.configure();
}
-#endif /* CONFIG_PM */
-
/* must be the same order as name table above */
static const struct pci_device_id agp_amdk7_pci_table[] = {
{
@@ -539,15 +524,14 @@ static const struct pci_device_id agp_amdk7_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_amdk7_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_amdk7_pm_ops, NULL, agp_amdk7_resume);
+
static struct pci_driver agp_amdk7_pci_driver = {
.name = "agpgart-amdk7",
.id_table = agp_amdk7_pci_table,
.probe = agp_amdk7_probe,
.remove = agp_amdk7_remove,
-#ifdef CONFIG_PM
- .suspend = agp_amdk7_suspend,
- .resume = agp_amdk7_resume,
-#endif
+ .driver.pm = &agp_amdk7_pm_ops,
};
static int __init agp_amdk7_init(void)
@@ -565,4 +549,5 @@ static void __exit agp_amdk7_cleanup(void)
module_init(agp_amdk7_init);
module_exit(agp_amdk7_cleanup);
+MODULE_DESCRIPTION("AMD K7 AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index 84a4aa9312cf..2505df1f4e69 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -16,7 +16,7 @@
#include <linux/mmzone.h>
#include <asm/page.h> /* PAGE_SIZE */
#include <asm/e820/api.h>
-#include <asm/amd_nb.h>
+#include <asm/amd/nb.h>
#include <asm/gart.h>
#include "agp.h"
@@ -588,9 +588,7 @@ static void agp_amd64_remove(struct pci_dev *pdev)
agp_bridges_found--;
}
-#define agp_amd64_suspend NULL
-
-static int __maybe_unused agp_amd64_resume(struct device *dev)
+static int agp_amd64_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -722,12 +720,7 @@ static const struct pci_device_id agp_amd64_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table);
-static const struct pci_device_id agp_amd64_pci_promisc_table[] = {
- { PCI_DEVICE_CLASS(0, 0) },
- { }
-};
-
-static SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, agp_amd64_suspend, agp_amd64_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, NULL, agp_amd64_resume);
static struct pci_driver agp_amd64_pci_driver = {
.name = "agpgart-amd64",
@@ -741,6 +734,7 @@ static struct pci_driver agp_amd64_pci_driver = {
/* Not static due to IOMMU code calling it early. */
int __init agp_amd64_init(void)
{
+ struct pci_dev *pdev = NULL;
int err = 0;
if (agp_off)
@@ -769,9 +763,13 @@ int __init agp_amd64_init(void)
}
/* Look for any AGP bridge */
- agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table;
- err = driver_attach(&agp_amd64_pci_driver.driver);
- if (err == 0 && agp_bridges_found == 0) {
+ for_each_pci_dev(pdev)
+ if (pci_find_capability(pdev, PCI_CAP_ID_AGP))
+ pci_add_dynid(&agp_amd64_pci_driver,
+ pdev->vendor, pdev->device,
+ pdev->subsystem_vendor,
+ pdev->subsystem_device, 0, 0, 0);
+ if (agp_bridges_found == 0) {
pci_unregister_driver(&agp_amd64_pci_driver);
err = -ENODEV;
}
@@ -804,4 +802,5 @@ module_exit(agp_amd64_cleanup);
MODULE_AUTHOR("Dave Jones, Andi Kleen");
module_param(agp_try_unsupported, bool, 0);
+MODULE_DESCRIPTION("GART driver for the AMD Opteron/Athlon64 on-CPU northbridge");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c
index 6f5530482d83..f7871afe08cf 100644
--- a/drivers/char/agp/ati-agp.c
+++ b/drivers/char/agp/ati-agp.c
@@ -238,23 +238,10 @@ static int ati_configure(void)
}
-#ifdef CONFIG_PM
-static int agp_ati_suspend(struct pci_dev *dev, pm_message_t state)
+static int agp_ati_resume(struct device *dev)
{
- pci_save_state(dev);
- pci_set_power_state(dev, PCI_D3hot);
-
- return 0;
-}
-
-static int agp_ati_resume(struct pci_dev *dev)
-{
- pci_set_power_state(dev, PCI_D0);
- pci_restore_state(dev);
-
return ati_configure();
}
-#endif
/*
*Since we don't need contiguous memory we just try
@@ -559,15 +546,14 @@ static const struct pci_device_id agp_ati_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_ati_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_ati_pm_ops, NULL, agp_ati_resume);
+
static struct pci_driver agp_ati_pci_driver = {
.name = "agpgart-ati",
.id_table = agp_ati_pci_table,
.probe = agp_ati_probe,
.remove = agp_ati_remove,
-#ifdef CONFIG_PM
- .suspend = agp_ati_suspend,
- .resume = agp_ati_resume,
-#endif
+ .driver.pm = &agp_ati_pm_ops,
};
static int __init agp_ati_init(void)
@@ -586,5 +572,6 @@ module_init(agp_ati_init);
module_exit(agp_ati_cleanup);
MODULE_AUTHOR("Dave Jones");
+MODULE_DESCRIPTION("ATi AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c
index 0e19c600db53..1776afd3ee07 100644
--- a/drivers/char/agp/backend.c
+++ b/drivers/char/agp/backend.c
@@ -293,13 +293,6 @@ int agp_add_bridge(struct agp_bridge_data *bridge)
}
if (list_empty(&agp_bridges)) {
- error = agp_frontend_initialize();
- if (error) {
- dev_info(&bridge->dev->dev,
- "agp_frontend_initialize() failed\n");
- goto frontend_err;
- }
-
dev_info(&bridge->dev->dev, "AGP aperture is %dM @ 0x%lx\n",
bridge->driver->fetch_size(), bridge->gart_bus_addr);
@@ -308,8 +301,6 @@ int agp_add_bridge(struct agp_bridge_data *bridge)
list_add(&bridge->list, &agp_bridges);
return 0;
-frontend_err:
- agp_backend_cleanup(bridge);
err_out:
module_put(bridge->driver->owner);
err_put_bridge:
@@ -323,8 +314,6 @@ void agp_remove_bridge(struct agp_bridge_data *bridge)
{
agp_backend_cleanup(bridge);
list_del(&bridge->list);
- if (list_empty(&agp_bridges))
- agp_frontend_cleanup();
module_put(bridge->driver->owner);
}
EXPORT_SYMBOL_GPL(agp_remove_bridge);
diff --git a/drivers/char/agp/compat_ioctl.c b/drivers/char/agp/compat_ioctl.c
deleted file mode 100644
index 52ffe1706ce0..000000000000
--- a/drivers/char/agp/compat_ioctl.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * AGPGART driver frontend compatibility ioctls
- * Copyright (C) 2004 Silicon Graphics, Inc.
- * Copyright (C) 2002-2003 Dave Jones
- * Copyright (C) 1999 Jeff Hartmann
- * Copyright (C) 1999 Precision Insight, Inc.
- * Copyright (C) 1999 Xi Graphics, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/agpgart.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include "agp.h"
-#include "compat_ioctl.h"
-
-static int compat_agpioc_info_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_info32 userinfo;
- struct agp_kern_info kerninfo;
-
- agp_copy_info(agp_bridge, &kerninfo);
-
- userinfo.version.major = kerninfo.version.major;
- userinfo.version.minor = kerninfo.version.minor;
- userinfo.bridge_id = kerninfo.device->vendor |
- (kerninfo.device->device << 16);
- userinfo.agp_mode = kerninfo.mode;
- userinfo.aper_base = (compat_long_t)kerninfo.aper_base;
- userinfo.aper_size = kerninfo.aper_size;
- userinfo.pg_total = userinfo.pg_system = kerninfo.max_memory;
- userinfo.pg_used = kerninfo.current_memory;
-
- if (copy_to_user(arg, &userinfo, sizeof(userinfo)))
- return -EFAULT;
-
- return 0;
-}
-
-static int compat_agpioc_reserve_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_region32 ureserve;
- struct agp_region kreserve;
- struct agp_client *client;
- struct agp_file_private *client_priv;
-
- DBG("");
- if (copy_from_user(&ureserve, arg, sizeof(ureserve)))
- return -EFAULT;
-
- if ((unsigned) ureserve.seg_count >= ~0U/sizeof(struct agp_segment32))
- return -EFAULT;
-
- kreserve.pid = ureserve.pid;
- kreserve.seg_count = ureserve.seg_count;
-
- client = agp_find_client_by_pid(kreserve.pid);
-
- if (kreserve.seg_count == 0) {
- /* remove a client */
- client_priv = agp_find_private(kreserve.pid);
-
- if (client_priv != NULL) {
- set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags);
- set_bit(AGP_FF_IS_VALID, &client_priv->access_flags);
- }
- if (client == NULL) {
- /* client is already removed */
- return 0;
- }
- return agp_remove_client(kreserve.pid);
- } else {
- struct agp_segment32 *usegment;
- struct agp_segment *ksegment;
- int seg;
-
- if (ureserve.seg_count >= 16384)
- return -EINVAL;
-
- usegment = kmalloc_array(ureserve.seg_count,
- sizeof(*usegment),
- GFP_KERNEL);
- if (!usegment)
- return -ENOMEM;
-
- ksegment = kmalloc_array(kreserve.seg_count,
- sizeof(*ksegment),
- GFP_KERNEL);
- if (!ksegment) {
- kfree(usegment);
- return -ENOMEM;
- }
-
- if (copy_from_user(usegment, (void __user *) ureserve.seg_list,
- sizeof(*usegment) * ureserve.seg_count)) {
- kfree(usegment);
- kfree(ksegment);
- return -EFAULT;
- }
-
- for (seg = 0; seg < ureserve.seg_count; seg++) {
- ksegment[seg].pg_start = usegment[seg].pg_start;
- ksegment[seg].pg_count = usegment[seg].pg_count;
- ksegment[seg].prot = usegment[seg].prot;
- }
-
- kfree(usegment);
- kreserve.seg_list = ksegment;
-
- if (client == NULL) {
- /* Create the client and add the segment */
- client = agp_create_client(kreserve.pid);
-
- if (client == NULL) {
- kfree(ksegment);
- return -ENOMEM;
- }
- client_priv = agp_find_private(kreserve.pid);
-
- if (client_priv != NULL) {
- set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags);
- set_bit(AGP_FF_IS_VALID, &client_priv->access_flags);
- }
- }
- return agp_create_segment(client, &kreserve);
- }
- /* Will never really happen */
- return -EINVAL;
-}
-
-static int compat_agpioc_allocate_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_memory *memory;
- struct agp_allocate32 alloc;
-
- DBG("");
- if (copy_from_user(&alloc, arg, sizeof(alloc)))
- return -EFAULT;
-
- memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type);
-
- if (memory == NULL)
- return -ENOMEM;
-
- alloc.key = memory->key;
- alloc.physical = memory->physical;
-
- if (copy_to_user(arg, &alloc, sizeof(alloc))) {
- agp_free_memory_wrap(memory);
- return -EFAULT;
- }
- return 0;
-}
-
-static int compat_agpioc_bind_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_bind32 bind_info;
- struct agp_memory *memory;
-
- DBG("");
- if (copy_from_user(&bind_info, arg, sizeof(bind_info)))
- return -EFAULT;
-
- memory = agp_find_mem_by_key(bind_info.key);
-
- if (memory == NULL)
- return -EINVAL;
-
- return agp_bind_memory(memory, bind_info.pg_start);
-}
-
-static int compat_agpioc_unbind_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_memory *memory;
- struct agp_unbind32 unbind;
-
- DBG("");
- if (copy_from_user(&unbind, arg, sizeof(unbind)))
- return -EFAULT;
-
- memory = agp_find_mem_by_key(unbind.key);
-
- if (memory == NULL)
- return -EINVAL;
-
- return agp_unbind_memory(memory);
-}
-
-long compat_agp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct agp_file_private *curr_priv = file->private_data;
- int ret_val = -ENOTTY;
-
- mutex_lock(&(agp_fe.agp_mutex));
-
- if ((agp_fe.current_controller == NULL) &&
- (cmd != AGPIOC_ACQUIRE32)) {
- ret_val = -EINVAL;
- goto ioctl_out;
- }
- if ((agp_fe.backend_acquired != true) &&
- (cmd != AGPIOC_ACQUIRE32)) {
- ret_val = -EBUSY;
- goto ioctl_out;
- }
- if (cmd != AGPIOC_ACQUIRE32) {
- if (!(test_bit(AGP_FF_IS_CONTROLLER, &curr_priv->access_flags))) {
- ret_val = -EPERM;
- goto ioctl_out;
- }
- /* Use the original pid of the controller,
- * in case it's threaded */
-
- if (agp_fe.current_controller->pid != curr_priv->my_pid) {
- ret_val = -EBUSY;
- goto ioctl_out;
- }
- }
-
- switch (cmd) {
- case AGPIOC_INFO32:
- ret_val = compat_agpioc_info_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_ACQUIRE32:
- ret_val = agpioc_acquire_wrap(curr_priv);
- break;
-
- case AGPIOC_RELEASE32:
- ret_val = agpioc_release_wrap(curr_priv);
- break;
-
- case AGPIOC_SETUP32:
- ret_val = agpioc_setup_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_RESERVE32:
- ret_val = compat_agpioc_reserve_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_PROTECT32:
- ret_val = agpioc_protect_wrap(curr_priv);
- break;
-
- case AGPIOC_ALLOCATE32:
- ret_val = compat_agpioc_allocate_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_DEALLOCATE32:
- ret_val = agpioc_deallocate_wrap(curr_priv, (int) arg);
- break;
-
- case AGPIOC_BIND32:
- ret_val = compat_agpioc_bind_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_UNBIND32:
- ret_val = compat_agpioc_unbind_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_CHIPSET_FLUSH32:
- break;
- }
-
-ioctl_out:
- DBG("ioctl returns %d\n", ret_val);
- mutex_unlock(&(agp_fe.agp_mutex));
- return ret_val;
-}
-
diff --git a/drivers/char/agp/compat_ioctl.h b/drivers/char/agp/compat_ioctl.h
deleted file mode 100644
index f30e0fd97963..000000000000
--- a/drivers/char/agp/compat_ioctl.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 1999 Jeff Hartmann
- * Copyright (C) 1999 Precision Insight, Inc.
- * Copyright (C) 1999 Xi Graphics, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#ifndef _AGP_COMPAT_IOCTL_H
-#define _AGP_COMPAT_IOCTL_H
-
-#include <linux/compat.h>
-#include <linux/agpgart.h>
-
-#define AGPIOC_INFO32 _IOR (AGPIOC_BASE, 0, compat_uptr_t)
-#define AGPIOC_ACQUIRE32 _IO (AGPIOC_BASE, 1)
-#define AGPIOC_RELEASE32 _IO (AGPIOC_BASE, 2)
-#define AGPIOC_SETUP32 _IOW (AGPIOC_BASE, 3, compat_uptr_t)
-#define AGPIOC_RESERVE32 _IOW (AGPIOC_BASE, 4, compat_uptr_t)
-#define AGPIOC_PROTECT32 _IOW (AGPIOC_BASE, 5, compat_uptr_t)
-#define AGPIOC_ALLOCATE32 _IOWR(AGPIOC_BASE, 6, compat_uptr_t)
-#define AGPIOC_DEALLOCATE32 _IOW (AGPIOC_BASE, 7, compat_int_t)
-#define AGPIOC_BIND32 _IOW (AGPIOC_BASE, 8, compat_uptr_t)
-#define AGPIOC_UNBIND32 _IOW (AGPIOC_BASE, 9, compat_uptr_t)
-#define AGPIOC_CHIPSET_FLUSH32 _IO (AGPIOC_BASE, 10)
-
-struct agp_info32 {
- struct agp_version version; /* version of the driver */
- u32 bridge_id; /* bridge vendor/device */
- u32 agp_mode; /* mode info of bridge */
- compat_long_t aper_base; /* base of aperture */
- compat_size_t aper_size; /* size of aperture */
- compat_size_t pg_total; /* max pages (swap + system) */
- compat_size_t pg_system; /* max pages (system) */
- compat_size_t pg_used; /* current pages used */
-};
-
-/*
- * The "prot" down below needs still a "sleep" flag somehow ...
- */
-struct agp_segment32 {
- compat_off_t pg_start; /* starting page to populate */
- compat_size_t pg_count; /* number of pages */
- compat_int_t prot; /* prot flags for mmap */
-};
-
-struct agp_region32 {
- compat_pid_t pid; /* pid of process */
- compat_size_t seg_count; /* number of segments */
- struct agp_segment32 *seg_list;
-};
-
-struct agp_allocate32 {
- compat_int_t key; /* tag of allocation */
- compat_size_t pg_count; /* number of pages */
- u32 type; /* 0 == normal, other devspec */
- u32 physical; /* device specific (some devices
- * need a phys address of the
- * actual page behind the gatt
- * table) */
-};
-
-struct agp_bind32 {
- compat_int_t key; /* tag of allocation */
- compat_off_t pg_start; /* starting page to populate */
-};
-
-struct agp_unbind32 {
- compat_int_t key; /* tag of allocation */
- u32 priority; /* priority for paging out */
-};
-
-extern struct agp_front_data agp_fe;
-
-int agpioc_acquire_wrap(struct agp_file_private *priv);
-int agpioc_release_wrap(struct agp_file_private *priv);
-int agpioc_protect_wrap(struct agp_file_private *priv);
-int agpioc_setup_wrap(struct agp_file_private *priv, void __user *arg);
-int agpioc_deallocate_wrap(struct agp_file_private *priv, int arg);
-struct agp_file_private *agp_find_private(pid_t pid);
-struct agp_client *agp_create_client(pid_t id);
-int agp_remove_client(pid_t id);
-int agp_create_segment(struct agp_client *client, struct agp_region *region);
-void agp_free_memory_wrap(struct agp_memory *memory);
-struct agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type);
-struct agp_memory *agp_find_mem_by_key(int key);
-struct agp_client *agp_find_client_by_pid(pid_t id);
-
-#endif /* _AGP_COMPAT_H */
diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c
index c53f0f9ef5b0..0d25bbdc7e6a 100644
--- a/drivers/char/agp/efficeon-agp.c
+++ b/drivers/char/agp/efficeon-agp.c
@@ -412,18 +412,11 @@ static void agp_efficeon_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-static int agp_efficeon_suspend(struct pci_dev *dev, pm_message_t state)
-{
- return 0;
-}
-
-static int agp_efficeon_resume(struct pci_dev *pdev)
+static int agp_efficeon_resume(struct device *dev)
{
printk(KERN_DEBUG PFX "agp_efficeon_resume()\n");
return efficeon_configure();
}
-#endif
static const struct pci_device_id agp_efficeon_pci_table[] = {
{
@@ -437,6 +430,8 @@ static const struct pci_device_id agp_efficeon_pci_table[] = {
{ }
};
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_efficeon_pm_ops, NULL, agp_efficeon_resume);
+
MODULE_DEVICE_TABLE(pci, agp_efficeon_pci_table);
static struct pci_driver agp_efficeon_pci_driver = {
@@ -444,10 +439,7 @@ static struct pci_driver agp_efficeon_pci_driver = {
.id_table = agp_efficeon_pci_table,
.probe = agp_efficeon_probe,
.remove = agp_efficeon_remove,
-#ifdef CONFIG_PM
- .suspend = agp_efficeon_suspend,
- .resume = agp_efficeon_resume,
-#endif
+ .driver.pm = &agp_efficeon_pm_ops,
};
static int __init agp_efficeon_init(void)
@@ -473,4 +465,5 @@ module_init(agp_efficeon_init);
module_exit(agp_efficeon_cleanup);
MODULE_AUTHOR("Carlos Puchol <cpglinux@puchol.com>");
+MODULE_DESCRIPTION("Transmeta's Efficeon AGPGART driver");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c
deleted file mode 100644
index 321118a9cfa5..000000000000
--- a/drivers/char/agp/frontend.c
+++ /dev/null
@@ -1,1068 +0,0 @@
-/*
- * AGPGART driver frontend
- * Copyright (C) 2004 Silicon Graphics, Inc.
- * Copyright (C) 2002-2003 Dave Jones
- * Copyright (C) 1999 Jeff Hartmann
- * Copyright (C) 1999 Precision Insight, Inc.
- * Copyright (C) 1999 Xi Graphics, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mman.h>
-#include <linux/pci.h>
-#include <linux/miscdevice.h>
-#include <linux/agp_backend.h>
-#include <linux/agpgart.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/uaccess.h>
-
-#include "agp.h"
-#include "compat_ioctl.h"
-
-struct agp_front_data agp_fe;
-
-struct agp_memory *agp_find_mem_by_key(int key)
-{
- struct agp_memory *curr;
-
- if (agp_fe.current_controller == NULL)
- return NULL;
-
- curr = agp_fe.current_controller->pool;
-
- while (curr != NULL) {
- if (curr->key == key)
- break;
- curr = curr->next;
- }
-
- DBG("key=%d -> mem=%p", key, curr);
- return curr;
-}
-
-static void agp_remove_from_pool(struct agp_memory *temp)
-{
- struct agp_memory *prev;
- struct agp_memory *next;
-
- /* Check to see if this is even in the memory pool */
-
- DBG("mem=%p", temp);
- if (agp_find_mem_by_key(temp->key) != NULL) {
- next = temp->next;
- prev = temp->prev;
-
- if (prev != NULL) {
- prev->next = next;
- if (next != NULL)
- next->prev = prev;
-
- } else {
- /* This is the first item on the list */
- if (next != NULL)
- next->prev = NULL;
-
- agp_fe.current_controller->pool = next;
- }
- }
-}
-
-/*
- * Routines for managing each client's segment list -
- * These routines handle adding and removing segments
- * to each auth'ed client.
- */
-
-static struct
-agp_segment_priv *agp_find_seg_in_client(const struct agp_client *client,
- unsigned long offset,
- int size, pgprot_t page_prot)
-{
- struct agp_segment_priv *seg;
- int i;
- off_t pg_start;
- size_t pg_count;
-
- pg_start = offset / 4096;
- pg_count = size / 4096;
- seg = *(client->segments);
-
- for (i = 0; i < client->num_segments; i++) {
- if ((seg[i].pg_start == pg_start) &&
- (seg[i].pg_count == pg_count) &&
- (pgprot_val(seg[i].prot) == pgprot_val(page_prot))) {
- return seg + i;
- }
- }
-
- return NULL;
-}
-
-static void agp_remove_seg_from_client(struct agp_client *client)
-{
- DBG("client=%p", client);
-
- if (client->segments != NULL) {
- if (*(client->segments) != NULL) {
- DBG("Freeing %p from client %p", *(client->segments), client);
- kfree(*(client->segments));
- }
- DBG("Freeing %p from client %p", client->segments, client);
- kfree(client->segments);
- client->segments = NULL;
- }
-}
-
-static void agp_add_seg_to_client(struct agp_client *client,
- struct agp_segment_priv ** seg, int num_segments)
-{
- struct agp_segment_priv **prev_seg;
-
- prev_seg = client->segments;
-
- if (prev_seg != NULL)
- agp_remove_seg_from_client(client);
-
- DBG("Adding seg %p (%d segments) to client %p", seg, num_segments, client);
- client->num_segments = num_segments;
- client->segments = seg;
-}
-
-static pgprot_t agp_convert_mmap_flags(int prot)
-{
- unsigned long prot_bits;
-
- prot_bits = calc_vm_prot_bits(prot, 0) | VM_SHARED;
- return vm_get_page_prot(prot_bits);
-}
-
-int agp_create_segment(struct agp_client *client, struct agp_region *region)
-{
- struct agp_segment_priv **ret_seg;
- struct agp_segment_priv *seg;
- struct agp_segment *user_seg;
- size_t i;
-
- seg = kzalloc((sizeof(struct agp_segment_priv) * region->seg_count), GFP_KERNEL);
- if (seg == NULL) {
- kfree(region->seg_list);
- region->seg_list = NULL;
- return -ENOMEM;
- }
- user_seg = region->seg_list;
-
- for (i = 0; i < region->seg_count; i++) {
- seg[i].pg_start = user_seg[i].pg_start;
- seg[i].pg_count = user_seg[i].pg_count;
- seg[i].prot = agp_convert_mmap_flags(user_seg[i].prot);
- }
- kfree(region->seg_list);
- region->seg_list = NULL;
-
- ret_seg = kmalloc(sizeof(void *), GFP_KERNEL);
- if (ret_seg == NULL) {
- kfree(seg);
- return -ENOMEM;
- }
- *ret_seg = seg;
- agp_add_seg_to_client(client, ret_seg, region->seg_count);
- return 0;
-}
-
-/* End - Routines for managing each client's segment list */
-
-/* This function must only be called when current_controller != NULL */
-static void agp_insert_into_pool(struct agp_memory * temp)
-{
- struct agp_memory *prev;
-
- prev = agp_fe.current_controller->pool;
-
- if (prev != NULL) {
- prev->prev = temp;
- temp->next = prev;
- }
- agp_fe.current_controller->pool = temp;
-}
-
-
-/* File private list routines */
-
-struct agp_file_private *agp_find_private(pid_t pid)
-{
- struct agp_file_private *curr;
-
- curr = agp_fe.file_priv_list;
-
- while (curr != NULL) {
- if (curr->my_pid == pid)
- return curr;
- curr = curr->next;
- }
-
- return NULL;
-}
-
-static void agp_insert_file_private(struct agp_file_private * priv)
-{
- struct agp_file_private *prev;
-
- prev = agp_fe.file_priv_list;
-
- if (prev != NULL)
- prev->prev = priv;
- priv->next = prev;
- agp_fe.file_priv_list = priv;
-}
-
-static void agp_remove_file_private(struct agp_file_private * priv)
-{
- struct agp_file_private *next;
- struct agp_file_private *prev;
-
- next = priv->next;
- prev = priv->prev;
-
- if (prev != NULL) {
- prev->next = next;
-
- if (next != NULL)
- next->prev = prev;
-
- } else {
- if (next != NULL)
- next->prev = NULL;
-
- agp_fe.file_priv_list = next;
- }
-}
-
-/* End - File flag list routines */
-
-/*
- * Wrappers for agp_free_memory & agp_allocate_memory
- * These make sure that internal lists are kept updated.
- */
-void agp_free_memory_wrap(struct agp_memory *memory)
-{
- agp_remove_from_pool(memory);
- agp_free_memory(memory);
-}
-
-struct agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type)
-{
- struct agp_memory *memory;
-
- memory = agp_allocate_memory(agp_bridge, pg_count, type);
- if (memory == NULL)
- return NULL;
-
- agp_insert_into_pool(memory);
- return memory;
-}
-
-/* Routines for managing the list of controllers -
- * These routines manage the current controller, and the list of
- * controllers
- */
-
-static struct agp_controller *agp_find_controller_by_pid(pid_t id)
-{
- struct agp_controller *controller;
-
- controller = agp_fe.controllers;
-
- while (controller != NULL) {
- if (controller->pid == id)
- return controller;
- controller = controller->next;
- }
-
- return NULL;
-}
-
-static struct agp_controller *agp_create_controller(pid_t id)
-{
- struct agp_controller *controller;
-
- controller = kzalloc(sizeof(struct agp_controller), GFP_KERNEL);
- if (controller == NULL)
- return NULL;
-
- controller->pid = id;
- return controller;
-}
-
-static int agp_insert_controller(struct agp_controller *controller)
-{
- struct agp_controller *prev_controller;
-
- prev_controller = agp_fe.controllers;
- controller->next = prev_controller;
-
- if (prev_controller != NULL)
- prev_controller->prev = controller;
-
- agp_fe.controllers = controller;
-
- return 0;
-}
-
-static void agp_remove_all_clients(struct agp_controller *controller)
-{
- struct agp_client *client;
- struct agp_client *temp;
-
- client = controller->clients;
-
- while (client) {
- struct agp_file_private *priv;
-
- temp = client;
- agp_remove_seg_from_client(temp);
- priv = agp_find_private(temp->pid);
-
- if (priv != NULL) {
- clear_bit(AGP_FF_IS_VALID, &priv->access_flags);
- clear_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
- }
- client = client->next;
- kfree(temp);
- }
-}
-
-static void agp_remove_all_memory(struct agp_controller *controller)
-{
- struct agp_memory *memory;
- struct agp_memory *temp;
-
- memory = controller->pool;
-
- while (memory) {
- temp = memory;
- memory = memory->next;
- agp_free_memory_wrap(temp);
- }
-}
-
-static int agp_remove_controller(struct agp_controller *controller)
-{
- struct agp_controller *prev_controller;
- struct agp_controller *next_controller;
-
- prev_controller = controller->prev;
- next_controller = controller->next;
-
- if (prev_controller != NULL) {
- prev_controller->next = next_controller;
- if (next_controller != NULL)
- next_controller->prev = prev_controller;
-
- } else {
- if (next_controller != NULL)
- next_controller->prev = NULL;
-
- agp_fe.controllers = next_controller;
- }
-
- agp_remove_all_memory(controller);
- agp_remove_all_clients(controller);
-
- if (agp_fe.current_controller == controller) {
- agp_fe.current_controller = NULL;
- agp_fe.backend_acquired = false;
- agp_backend_release(agp_bridge);
- }
- kfree(controller);
- return 0;
-}
-
-static void agp_controller_make_current(struct agp_controller *controller)
-{
- struct agp_client *clients;
-
- clients = controller->clients;
-
- while (clients != NULL) {
- struct agp_file_private *priv;
-
- priv = agp_find_private(clients->pid);
-
- if (priv != NULL) {
- set_bit(AGP_FF_IS_VALID, &priv->access_flags);
- set_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
- }
- clients = clients->next;
- }
-
- agp_fe.current_controller = controller;
-}
-
-static void agp_controller_release_current(struct agp_controller *controller,
- struct agp_file_private *controller_priv)
-{
- struct agp_client *clients;
-
- clear_bit(AGP_FF_IS_VALID, &controller_priv->access_flags);
- clients = controller->clients;
-
- while (clients != NULL) {
- struct agp_file_private *priv;
-
- priv = agp_find_private(clients->pid);
-
- if (priv != NULL)
- clear_bit(AGP_FF_IS_VALID, &priv->access_flags);
-
- clients = clients->next;
- }
-
- agp_fe.current_controller = NULL;
- agp_fe.used_by_controller = false;
- agp_backend_release(agp_bridge);
-}
-
-/*
- * Routines for managing client lists -
- * These routines are for managing the list of auth'ed clients.
- */
-
-static struct agp_client
-*agp_find_client_in_controller(struct agp_controller *controller, pid_t id)
-{
- struct agp_client *client;
-
- if (controller == NULL)
- return NULL;
-
- client = controller->clients;
-
- while (client != NULL) {
- if (client->pid == id)
- return client;
- client = client->next;
- }
-
- return NULL;
-}
-
-static struct agp_controller *agp_find_controller_for_client(pid_t id)
-{
- struct agp_controller *controller;
-
- controller = agp_fe.controllers;
-
- while (controller != NULL) {
- if ((agp_find_client_in_controller(controller, id)) != NULL)
- return controller;
- controller = controller->next;
- }
-
- return NULL;
-}
-
-struct agp_client *agp_find_client_by_pid(pid_t id)
-{
- struct agp_client *temp;
-
- if (agp_fe.current_controller == NULL)
- return NULL;
-
- temp = agp_find_client_in_controller(agp_fe.current_controller, id);
- return temp;
-}
-
-static void agp_insert_client(struct agp_client *client)
-{
- struct agp_client *prev_client;
-
- prev_client = agp_fe.current_controller->clients;
- client->next = prev_client;
-
- if (prev_client != NULL)
- prev_client->prev = client;
-
- agp_fe.current_controller->clients = client;
- agp_fe.current_controller->num_clients++;
-}
-
-struct agp_client *agp_create_client(pid_t id)
-{
- struct agp_client *new_client;
-
- new_client = kzalloc(sizeof(struct agp_client), GFP_KERNEL);
- if (new_client == NULL)
- return NULL;
-
- new_client->pid = id;
- agp_insert_client(new_client);
- return new_client;
-}
-
-int agp_remove_client(pid_t id)
-{
- struct agp_client *client;
- struct agp_client *prev_client;
- struct agp_client *next_client;
- struct agp_controller *controller;
-
- controller = agp_find_controller_for_client(id);
- if (controller == NULL)
- return -EINVAL;
-
- client = agp_find_client_in_controller(controller, id);
- if (client == NULL)
- return -EINVAL;
-
- prev_client = client->prev;
- next_client = client->next;
-
- if (prev_client != NULL) {
- prev_client->next = next_client;
- if (next_client != NULL)
- next_client->prev = prev_client;
-
- } else {
- if (next_client != NULL)
- next_client->prev = NULL;
- controller->clients = next_client;
- }
-
- controller->num_clients--;
- agp_remove_seg_from_client(client);
- kfree(client);
- return 0;
-}
-
-/* End - Routines for managing client lists */
-
-/* File Operations */
-
-static int agp_mmap(struct file *file, struct vm_area_struct *vma)
-{
- unsigned int size, current_size;
- unsigned long offset;
- struct agp_client *client;
- struct agp_file_private *priv = file->private_data;
- struct agp_kern_info kerninfo;
-
- mutex_lock(&(agp_fe.agp_mutex));
-
- if (agp_fe.backend_acquired != true)
- goto out_eperm;
-
- if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags)))
- goto out_eperm;
-
- agp_copy_info(agp_bridge, &kerninfo);
- size = vma->vm_end - vma->vm_start;
- current_size = kerninfo.aper_size;
- current_size = current_size * 0x100000;
- offset = vma->vm_pgoff << PAGE_SHIFT;
- DBG("%lx:%lx", offset, offset+size);
-
- if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) {
- if ((size + offset) > current_size)
- goto out_inval;
-
- client = agp_find_client_by_pid(current->pid);
-
- if (client == NULL)
- goto out_eperm;
-
- if (!agp_find_seg_in_client(client, offset, size, vma->vm_page_prot))
- goto out_inval;
-
- DBG("client vm_ops=%p", kerninfo.vm_ops);
- if (kerninfo.vm_ops) {
- vma->vm_ops = kerninfo.vm_ops;
- } else if (io_remap_pfn_range(vma, vma->vm_start,
- (kerninfo.aper_base + offset) >> PAGE_SHIFT,
- size,
- pgprot_writecombine(vma->vm_page_prot))) {
- goto out_again;
- }
- mutex_unlock(&(agp_fe.agp_mutex));
- return 0;
- }
-
- if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) {
- if (size != current_size)
- goto out_inval;
-
- DBG("controller vm_ops=%p", kerninfo.vm_ops);
- if (kerninfo.vm_ops) {
- vma->vm_ops = kerninfo.vm_ops;
- } else if (io_remap_pfn_range(vma, vma->vm_start,
- kerninfo.aper_base >> PAGE_SHIFT,
- size,
- pgprot_writecombine(vma->vm_page_prot))) {
- goto out_again;
- }
- mutex_unlock(&(agp_fe.agp_mutex));
- return 0;
- }
-
-out_eperm:
- mutex_unlock(&(agp_fe.agp_mutex));
- return -EPERM;
-
-out_inval:
- mutex_unlock(&(agp_fe.agp_mutex));
- return -EINVAL;
-
-out_again:
- mutex_unlock(&(agp_fe.agp_mutex));
- return -EAGAIN;
-}
-
-static int agp_release(struct inode *inode, struct file *file)
-{
- struct agp_file_private *priv = file->private_data;
-
- mutex_lock(&(agp_fe.agp_mutex));
-
- DBG("priv=%p", priv);
-
- if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) {
- struct agp_controller *controller;
-
- controller = agp_find_controller_by_pid(priv->my_pid);
-
- if (controller != NULL) {
- if (controller == agp_fe.current_controller)
- agp_controller_release_current(controller, priv);
- agp_remove_controller(controller);
- controller = NULL;
- }
- }
-
- if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags))
- agp_remove_client(priv->my_pid);
-
- agp_remove_file_private(priv);
- kfree(priv);
- file->private_data = NULL;
- mutex_unlock(&(agp_fe.agp_mutex));
- return 0;
-}
-
-static int agp_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct agp_file_private *priv;
- struct agp_client *client;
-
- if (minor != AGPGART_MINOR)
- return -ENXIO;
-
- mutex_lock(&(agp_fe.agp_mutex));
-
- priv = kzalloc(sizeof(struct agp_file_private), GFP_KERNEL);
- if (priv == NULL) {
- mutex_unlock(&(agp_fe.agp_mutex));
- return -ENOMEM;
- }
-
- set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags);
- priv->my_pid = current->pid;
-
- if (capable(CAP_SYS_RAWIO))
- /* Root priv, can be controller */
- set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags);
-
- client = agp_find_client_by_pid(current->pid);
-
- if (client != NULL) {
- set_bit(AGP_FF_IS_CLIENT, &priv->access_flags);
- set_bit(AGP_FF_IS_VALID, &priv->access_flags);
- }
- file->private_data = (void *) priv;
- agp_insert_file_private(priv);
- DBG("private=%p, client=%p", priv, client);
-
- mutex_unlock(&(agp_fe.agp_mutex));
-
- return 0;
-}
-
-static int agpioc_info_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_info userinfo;
- struct agp_kern_info kerninfo;
-
- agp_copy_info(agp_bridge, &kerninfo);
-
- memset(&userinfo, 0, sizeof(userinfo));
- userinfo.version.major = kerninfo.version.major;
- userinfo.version.minor = kerninfo.version.minor;
- userinfo.bridge_id = kerninfo.device->vendor |
- (kerninfo.device->device << 16);
- userinfo.agp_mode = kerninfo.mode;
- userinfo.aper_base = kerninfo.aper_base;
- userinfo.aper_size = kerninfo.aper_size;
- userinfo.pg_total = userinfo.pg_system = kerninfo.max_memory;
- userinfo.pg_used = kerninfo.current_memory;
-
- if (copy_to_user(arg, &userinfo, sizeof(struct agp_info)))
- return -EFAULT;
-
- return 0;
-}
-
-int agpioc_acquire_wrap(struct agp_file_private *priv)
-{
- struct agp_controller *controller;
-
- DBG("");
-
- if (!(test_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags)))
- return -EPERM;
-
- if (agp_fe.current_controller != NULL)
- return -EBUSY;
-
- if (!agp_bridge)
- return -ENODEV;
-
- if (atomic_read(&agp_bridge->agp_in_use))
- return -EBUSY;
-
- atomic_inc(&agp_bridge->agp_in_use);
-
- agp_fe.backend_acquired = true;
-
- controller = agp_find_controller_by_pid(priv->my_pid);
-
- if (controller != NULL) {
- agp_controller_make_current(controller);
- } else {
- controller = agp_create_controller(priv->my_pid);
-
- if (controller == NULL) {
- agp_fe.backend_acquired = false;
- agp_backend_release(agp_bridge);
- return -ENOMEM;
- }
- agp_insert_controller(controller);
- agp_controller_make_current(controller);
- }
-
- set_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags);
- set_bit(AGP_FF_IS_VALID, &priv->access_flags);
- return 0;
-}
-
-int agpioc_release_wrap(struct agp_file_private *priv)
-{
- DBG("");
- agp_controller_release_current(agp_fe.current_controller, priv);
- return 0;
-}
-
-int agpioc_setup_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_setup mode;
-
- DBG("");
- if (copy_from_user(&mode, arg, sizeof(struct agp_setup)))
- return -EFAULT;
-
- agp_enable(agp_bridge, mode.agp_mode);
- return 0;
-}
-
-static int agpioc_reserve_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_region reserve;
- struct agp_client *client;
- struct agp_file_private *client_priv;
-
- DBG("");
- if (copy_from_user(&reserve, arg, sizeof(struct agp_region)))
- return -EFAULT;
-
- if ((unsigned) reserve.seg_count >= ~0U/sizeof(struct agp_segment))
- return -EFAULT;
-
- client = agp_find_client_by_pid(reserve.pid);
-
- if (reserve.seg_count == 0) {
- /* remove a client */
- client_priv = agp_find_private(reserve.pid);
-
- if (client_priv != NULL) {
- set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags);
- set_bit(AGP_FF_IS_VALID, &client_priv->access_flags);
- }
- if (client == NULL) {
- /* client is already removed */
- return 0;
- }
- return agp_remove_client(reserve.pid);
- } else {
- struct agp_segment *segment;
-
- if (reserve.seg_count >= 16384)
- return -EINVAL;
-
- segment = kmalloc((sizeof(struct agp_segment) * reserve.seg_count),
- GFP_KERNEL);
-
- if (segment == NULL)
- return -ENOMEM;
-
- if (copy_from_user(segment, (void __user *) reserve.seg_list,
- sizeof(struct agp_segment) * reserve.seg_count)) {
- kfree(segment);
- return -EFAULT;
- }
- reserve.seg_list = segment;
-
- if (client == NULL) {
- /* Create the client and add the segment */
- client = agp_create_client(reserve.pid);
-
- if (client == NULL) {
- kfree(segment);
- return -ENOMEM;
- }
- client_priv = agp_find_private(reserve.pid);
-
- if (client_priv != NULL) {
- set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags);
- set_bit(AGP_FF_IS_VALID, &client_priv->access_flags);
- }
- }
- return agp_create_segment(client, &reserve);
- }
- /* Will never really happen */
- return -EINVAL;
-}
-
-int agpioc_protect_wrap(struct agp_file_private *priv)
-{
- DBG("");
- /* This function is not currently implemented */
- return -EINVAL;
-}
-
-static int agpioc_allocate_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_memory *memory;
- struct agp_allocate alloc;
-
- DBG("");
- if (copy_from_user(&alloc, arg, sizeof(struct agp_allocate)))
- return -EFAULT;
-
- if (alloc.type >= AGP_USER_TYPES)
- return -EINVAL;
-
- memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type);
-
- if (memory == NULL)
- return -ENOMEM;
-
- alloc.key = memory->key;
- alloc.physical = memory->physical;
-
- if (copy_to_user(arg, &alloc, sizeof(struct agp_allocate))) {
- agp_free_memory_wrap(memory);
- return -EFAULT;
- }
- return 0;
-}
-
-int agpioc_deallocate_wrap(struct agp_file_private *priv, int arg)
-{
- struct agp_memory *memory;
-
- DBG("");
- memory = agp_find_mem_by_key(arg);
-
- if (memory == NULL)
- return -EINVAL;
-
- agp_free_memory_wrap(memory);
- return 0;
-}
-
-static int agpioc_bind_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_bind bind_info;
- struct agp_memory *memory;
-
- DBG("");
- if (copy_from_user(&bind_info, arg, sizeof(struct agp_bind)))
- return -EFAULT;
-
- memory = agp_find_mem_by_key(bind_info.key);
-
- if (memory == NULL)
- return -EINVAL;
-
- return agp_bind_memory(memory, bind_info.pg_start);
-}
-
-static int agpioc_unbind_wrap(struct agp_file_private *priv, void __user *arg)
-{
- struct agp_memory *memory;
- struct agp_unbind unbind;
-
- DBG("");
- if (copy_from_user(&unbind, arg, sizeof(struct agp_unbind)))
- return -EFAULT;
-
- memory = agp_find_mem_by_key(unbind.key);
-
- if (memory == NULL)
- return -EINVAL;
-
- return agp_unbind_memory(memory);
-}
-
-static long agp_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct agp_file_private *curr_priv = file->private_data;
- int ret_val = -ENOTTY;
-
- DBG("priv=%p, cmd=%x", curr_priv, cmd);
- mutex_lock(&(agp_fe.agp_mutex));
-
- if ((agp_fe.current_controller == NULL) &&
- (cmd != AGPIOC_ACQUIRE)) {
- ret_val = -EINVAL;
- goto ioctl_out;
- }
- if ((agp_fe.backend_acquired != true) &&
- (cmd != AGPIOC_ACQUIRE)) {
- ret_val = -EBUSY;
- goto ioctl_out;
- }
- if (cmd != AGPIOC_ACQUIRE) {
- if (!(test_bit(AGP_FF_IS_CONTROLLER, &curr_priv->access_flags))) {
- ret_val = -EPERM;
- goto ioctl_out;
- }
- /* Use the original pid of the controller,
- * in case it's threaded */
-
- if (agp_fe.current_controller->pid != curr_priv->my_pid) {
- ret_val = -EBUSY;
- goto ioctl_out;
- }
- }
-
- switch (cmd) {
- case AGPIOC_INFO:
- ret_val = agpioc_info_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_ACQUIRE:
- ret_val = agpioc_acquire_wrap(curr_priv);
- break;
-
- case AGPIOC_RELEASE:
- ret_val = agpioc_release_wrap(curr_priv);
- break;
-
- case AGPIOC_SETUP:
- ret_val = agpioc_setup_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_RESERVE:
- ret_val = agpioc_reserve_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_PROTECT:
- ret_val = agpioc_protect_wrap(curr_priv);
- break;
-
- case AGPIOC_ALLOCATE:
- ret_val = agpioc_allocate_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_DEALLOCATE:
- ret_val = agpioc_deallocate_wrap(curr_priv, (int) arg);
- break;
-
- case AGPIOC_BIND:
- ret_val = agpioc_bind_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_UNBIND:
- ret_val = agpioc_unbind_wrap(curr_priv, (void __user *) arg);
- break;
-
- case AGPIOC_CHIPSET_FLUSH:
- break;
- }
-
-ioctl_out:
- DBG("ioctl returns %d\n", ret_val);
- mutex_unlock(&(agp_fe.agp_mutex));
- return ret_val;
-}
-
-static const struct file_operations agp_fops =
-{
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = agp_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = compat_agp_ioctl,
-#endif
- .mmap = agp_mmap,
- .open = agp_open,
- .release = agp_release,
-};
-
-static struct miscdevice agp_miscdev =
-{
- .minor = AGPGART_MINOR,
- .name = "agpgart",
- .fops = &agp_fops
-};
-
-int agp_frontend_initialize(void)
-{
- memset(&agp_fe, 0, sizeof(struct agp_front_data));
- mutex_init(&(agp_fe.agp_mutex));
-
- if (misc_register(&agp_miscdev)) {
- printk(KERN_ERR PFX "unable to get minor: %d\n", AGPGART_MINOR);
- return -EIO;
- }
- return 0;
-}
-
-void agp_frontend_cleanup(void)
-{
- misc_deregister(&agp_miscdev);
-}
diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c
deleted file mode 100644
index 84d9adbb62f6..000000000000
--- a/drivers/char/agp/hp-agp.c
+++ /dev/null
@@ -1,550 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HP zx1 AGPGART routines.
- *
- * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P.
- * Bjorn Helgaas <bjorn.helgaas@hp.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/agp_backend.h>
-#include <linux/log2.h>
-#include <linux/slab.h>
-
-#include <asm/acpi-ext.h>
-
-#include "agp.h"
-
-#define HP_ZX1_IOC_OFFSET 0x1000 /* ACPI reports SBA, we want IOC */
-
-/* HP ZX1 IOC registers */
-#define HP_ZX1_IBASE 0x300
-#define HP_ZX1_IMASK 0x308
-#define HP_ZX1_PCOM 0x310
-#define HP_ZX1_TCNFG 0x318
-#define HP_ZX1_PDIR_BASE 0x320
-
-#define HP_ZX1_IOVA_BASE GB(1UL)
-#define HP_ZX1_IOVA_SIZE GB(1UL)
-#define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2)
-#define HP_ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL
-
-#define HP_ZX1_PDIR_VALID_BIT 0x8000000000000000UL
-#define HP_ZX1_IOVA_TO_PDIR(va) ((va - hp_private.iova_base) >> hp_private.io_tlb_shift)
-
-#define AGP8X_MODE_BIT 3
-#define AGP8X_MODE (1 << AGP8X_MODE_BIT)
-
-/* AGP bridge need not be PCI device, but DRM thinks it is. */
-static struct pci_dev fake_bridge_dev;
-
-static int hp_zx1_gart_found;
-
-static struct aper_size_info_fixed hp_zx1_sizes[] =
-{
- {0, 0, 0}, /* filled in by hp_zx1_fetch_size() */
-};
-
-static struct gatt_mask hp_zx1_masks[] =
-{
- {.mask = HP_ZX1_PDIR_VALID_BIT, .type = 0}
-};
-
-static struct _hp_private {
- volatile u8 __iomem *ioc_regs;
- volatile u8 __iomem *lba_regs;
- int lba_cap_offset;
- u64 *io_pdir; // PDIR for entire IOVA
- u64 *gatt; // PDIR just for GART (subset of above)
- u64 gatt_entries;
- u64 iova_base;
- u64 gart_base;
- u64 gart_size;
- u64 io_pdir_size;
- int io_pdir_owner; // do we own it, or share it with sba_iommu?
- int io_page_size;
- int io_tlb_shift;
- int io_tlb_ps; // IOC ps config
- int io_pages_per_kpage;
-} hp_private;
-
-static int __init hp_zx1_ioc_shared(void)
-{
- struct _hp_private *hp = &hp_private;
-
- printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR shared with sba_iommu\n");
-
- /*
- * IOC already configured by sba_iommu module; just use
- * its setup. We assume:
- * - IOVA space is 1Gb in size
- * - first 512Mb is IOMMU, second 512Mb is GART
- */
- hp->io_tlb_ps = readq(hp->ioc_regs+HP_ZX1_TCNFG);
- switch (hp->io_tlb_ps) {
- case 0: hp->io_tlb_shift = 12; break;
- case 1: hp->io_tlb_shift = 13; break;
- case 2: hp->io_tlb_shift = 14; break;
- case 3: hp->io_tlb_shift = 16; break;
- default:
- printk(KERN_ERR PFX "Invalid IOTLB page size "
- "configuration 0x%x\n", hp->io_tlb_ps);
- hp->gatt = NULL;
- hp->gatt_entries = 0;
- return -ENODEV;
- }
- hp->io_page_size = 1 << hp->io_tlb_shift;
- hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size;
-
- hp->iova_base = readq(hp->ioc_regs+HP_ZX1_IBASE) & ~0x1;
- hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE;
-
- hp->gart_size = HP_ZX1_GART_SIZE;
- hp->gatt_entries = hp->gart_size / hp->io_page_size;
-
- hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE));
- hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)];
-
- if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) {
- /* Normal case when no AGP device in system */
- hp->gatt = NULL;
- hp->gatt_entries = 0;
- printk(KERN_ERR PFX "No reserved IO PDIR entry found; "
- "GART disabled\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int __init
-hp_zx1_ioc_owner (void)
-{
- struct _hp_private *hp = &hp_private;
-
- printk(KERN_INFO PFX "HP ZX1 IOC: IOPDIR dedicated to GART\n");
-
- /*
- * Select an IOV page size no larger than system page size.
- */
- if (PAGE_SIZE >= KB(64)) {
- hp->io_tlb_shift = 16;
- hp->io_tlb_ps = 3;
- } else if (PAGE_SIZE >= KB(16)) {
- hp->io_tlb_shift = 14;
- hp->io_tlb_ps = 2;
- } else if (PAGE_SIZE >= KB(8)) {
- hp->io_tlb_shift = 13;
- hp->io_tlb_ps = 1;
- } else {
- hp->io_tlb_shift = 12;
- hp->io_tlb_ps = 0;
- }
- hp->io_page_size = 1 << hp->io_tlb_shift;
- hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size;
-
- hp->iova_base = HP_ZX1_IOVA_BASE;
- hp->gart_size = HP_ZX1_GART_SIZE;
- hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - hp->gart_size;
-
- hp->gatt_entries = hp->gart_size / hp->io_page_size;
- hp->io_pdir_size = (HP_ZX1_IOVA_SIZE / hp->io_page_size) * sizeof(u64);
-
- return 0;
-}
-
-static int __init
-hp_zx1_ioc_init (u64 hpa)
-{
- struct _hp_private *hp = &hp_private;
-
- hp->ioc_regs = ioremap(hpa, 1024);
- if (!hp->ioc_regs)
- return -ENOMEM;
-
- /*
- * If the IOTLB is currently disabled, we can take it over.
- * Otherwise, we have to share with sba_iommu.
- */
- hp->io_pdir_owner = (readq(hp->ioc_regs+HP_ZX1_IBASE) & 0x1) == 0;
-
- if (hp->io_pdir_owner)
- return hp_zx1_ioc_owner();
-
- return hp_zx1_ioc_shared();
-}
-
-static int
-hp_zx1_lba_find_capability (volatile u8 __iomem *hpa, int cap)
-{
- u16 status;
- u8 pos, id;
- int ttl = 48;
-
- status = readw(hpa+PCI_STATUS);
- if (!(status & PCI_STATUS_CAP_LIST))
- return 0;
- pos = readb(hpa+PCI_CAPABILITY_LIST);
- while (ttl-- && pos >= 0x40) {
- pos &= ~3;
- id = readb(hpa+pos+PCI_CAP_LIST_ID);
- if (id == 0xff)
- break;
- if (id == cap)
- return pos;
- pos = readb(hpa+pos+PCI_CAP_LIST_NEXT);
- }
- return 0;
-}
-
-static int __init
-hp_zx1_lba_init (u64 hpa)
-{
- struct _hp_private *hp = &hp_private;
- int cap;
-
- hp->lba_regs = ioremap(hpa, 256);
- if (!hp->lba_regs)
- return -ENOMEM;
-
- hp->lba_cap_offset = hp_zx1_lba_find_capability(hp->lba_regs, PCI_CAP_ID_AGP);
-
- cap = readl(hp->lba_regs+hp->lba_cap_offset) & 0xff;
- if (cap != PCI_CAP_ID_AGP) {
- printk(KERN_ERR PFX "Invalid capability ID 0x%02x at 0x%x\n",
- cap, hp->lba_cap_offset);
- iounmap(hp->lba_regs);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int
-hp_zx1_fetch_size(void)
-{
- int size;
-
- size = hp_private.gart_size / MB(1);
- hp_zx1_sizes[0].size = size;
- agp_bridge->current_size = (void *) &hp_zx1_sizes[0];
- return size;
-}
-
-static int
-hp_zx1_configure (void)
-{
- struct _hp_private *hp = &hp_private;
-
- agp_bridge->gart_bus_addr = hp->gart_base;
- agp_bridge->capndx = hp->lba_cap_offset;
- agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS);
-
- if (hp->io_pdir_owner) {
- writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE);
- readl(hp->ioc_regs+HP_ZX1_PDIR_BASE);
- writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG);
- readl(hp->ioc_regs+HP_ZX1_TCNFG);
- writel((unsigned int)(~(HP_ZX1_IOVA_SIZE-1)), hp->ioc_regs+HP_ZX1_IMASK);
- readl(hp->ioc_regs+HP_ZX1_IMASK);
- writel(hp->iova_base|1, hp->ioc_regs+HP_ZX1_IBASE);
- readl(hp->ioc_regs+HP_ZX1_IBASE);
- writel(hp->iova_base|ilog2(HP_ZX1_IOVA_SIZE), hp->ioc_regs+HP_ZX1_PCOM);
- readl(hp->ioc_regs+HP_ZX1_PCOM);
- }
-
- return 0;
-}
-
-static void
-hp_zx1_cleanup (void)
-{
- struct _hp_private *hp = &hp_private;
-
- if (hp->ioc_regs) {
- if (hp->io_pdir_owner) {
- writeq(0, hp->ioc_regs+HP_ZX1_IBASE);
- readq(hp->ioc_regs+HP_ZX1_IBASE);
- }
- iounmap(hp->ioc_regs);
- }
- if (hp->lba_regs)
- iounmap(hp->lba_regs);
-}
-
-static void
-hp_zx1_tlbflush (struct agp_memory *mem)
-{
- struct _hp_private *hp = &hp_private;
-
- writeq(hp->gart_base | ilog2(hp->gart_size), hp->ioc_regs+HP_ZX1_PCOM);
- readq(hp->ioc_regs+HP_ZX1_PCOM);
-}
-
-static int
-hp_zx1_create_gatt_table (struct agp_bridge_data *bridge)
-{
- struct _hp_private *hp = &hp_private;
- int i;
-
- if (hp->io_pdir_owner) {
- hp->io_pdir = (u64 *) __get_free_pages(GFP_KERNEL,
- get_order(hp->io_pdir_size));
- if (!hp->io_pdir) {
- printk(KERN_ERR PFX "Couldn't allocate contiguous "
- "memory for I/O PDIR\n");
- hp->gatt = NULL;
- hp->gatt_entries = 0;
- return -ENOMEM;
- }
- memset(hp->io_pdir, 0, hp->io_pdir_size);
-
- hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)];
- }
-
- for (i = 0; i < hp->gatt_entries; i++) {
- hp->gatt[i] = (unsigned long) agp_bridge->scratch_page;
- }
-
- return 0;
-}
-
-static int
-hp_zx1_free_gatt_table (struct agp_bridge_data *bridge)
-{
- struct _hp_private *hp = &hp_private;
-
- if (hp->io_pdir_owner)
- free_pages((unsigned long) hp->io_pdir,
- get_order(hp->io_pdir_size));
- else
- hp->gatt[0] = HP_ZX1_SBA_IOMMU_COOKIE;
- return 0;
-}
-
-static int
-hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type)
-{
- struct _hp_private *hp = &hp_private;
- int i, k;
- off_t j, io_pg_start;
- int io_pg_count;
-
- if (type != mem->type ||
- agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
- return -EINVAL;
- }
-
- io_pg_start = hp->io_pages_per_kpage * pg_start;
- io_pg_count = hp->io_pages_per_kpage * mem->page_count;
- if ((io_pg_start + io_pg_count) > hp->gatt_entries) {
- return -EINVAL;
- }
-
- j = io_pg_start;
- while (j < (io_pg_start + io_pg_count)) {
- if (hp->gatt[j]) {
- return -EBUSY;
- }
- j++;
- }
-
- if (!mem->is_flushed) {
- global_cache_flush();
- mem->is_flushed = true;
- }
-
- for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
- unsigned long paddr;
-
- paddr = page_to_phys(mem->pages[i]);
- for (k = 0;
- k < hp->io_pages_per_kpage;
- k++, j++, paddr += hp->io_page_size) {
- hp->gatt[j] = HP_ZX1_PDIR_VALID_BIT | paddr;
- }
- }
-
- agp_bridge->driver->tlb_flush(mem);
- return 0;
-}
-
-static int
-hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type)
-{
- struct _hp_private *hp = &hp_private;
- int i, io_pg_start, io_pg_count;
-
- if (type != mem->type ||
- agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
- return -EINVAL;
- }
-
- io_pg_start = hp->io_pages_per_kpage * pg_start;
- io_pg_count = hp->io_pages_per_kpage * mem->page_count;
- for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) {
- hp->gatt[i] = agp_bridge->scratch_page;
- }
-
- agp_bridge->driver->tlb_flush(mem);
- return 0;
-}
-
-static unsigned long
-hp_zx1_mask_memory (struct agp_bridge_data *bridge, dma_addr_t addr, int type)
-{
- return HP_ZX1_PDIR_VALID_BIT | addr;
-}
-
-static void
-hp_zx1_enable (struct agp_bridge_data *bridge, u32 mode)
-{
- struct _hp_private *hp = &hp_private;
- u32 command;
-
- command = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS);
- command = agp_collect_device_status(bridge, mode, command);
- command |= 0x00000100;
-
- writel(command, hp->lba_regs+hp->lba_cap_offset+PCI_AGP_COMMAND);
-
- agp_device_command(command, (mode & AGP8X_MODE) != 0);
-}
-
-const struct agp_bridge_driver hp_zx1_driver = {
- .owner = THIS_MODULE,
- .size_type = FIXED_APER_SIZE,
- .configure = hp_zx1_configure,
- .fetch_size = hp_zx1_fetch_size,
- .cleanup = hp_zx1_cleanup,
- .tlb_flush = hp_zx1_tlbflush,
- .mask_memory = hp_zx1_mask_memory,
- .masks = hp_zx1_masks,
- .agp_enable = hp_zx1_enable,
- .cache_flush = global_cache_flush,
- .create_gatt_table = hp_zx1_create_gatt_table,
- .free_gatt_table = hp_zx1_free_gatt_table,
- .insert_memory = hp_zx1_insert_memory,
- .remove_memory = hp_zx1_remove_memory,
- .alloc_by_type = agp_generic_alloc_by_type,
- .free_by_type = agp_generic_free_by_type,
- .agp_alloc_page = agp_generic_alloc_page,
- .agp_alloc_pages = agp_generic_alloc_pages,
- .agp_destroy_page = agp_generic_destroy_page,
- .agp_destroy_pages = agp_generic_destroy_pages,
- .agp_type_to_mask_type = agp_generic_type_to_mask_type,
- .cant_use_aperture = true,
-};
-
-static int __init
-hp_zx1_setup (u64 ioc_hpa, u64 lba_hpa)
-{
- struct agp_bridge_data *bridge;
- int error = 0;
-
- error = hp_zx1_ioc_init(ioc_hpa);
- if (error)
- goto fail;
-
- error = hp_zx1_lba_init(lba_hpa);
- if (error)
- goto fail;
-
- bridge = agp_alloc_bridge();
- if (!bridge) {
- error = -ENOMEM;
- goto fail;
- }
- bridge->driver = &hp_zx1_driver;
-
- fake_bridge_dev.vendor = PCI_VENDOR_ID_HP;
- fake_bridge_dev.device = PCI_DEVICE_ID_HP_PCIX_LBA;
- bridge->dev = &fake_bridge_dev;
-
- error = agp_add_bridge(bridge);
- fail:
- if (error)
- hp_zx1_cleanup();
- return error;
-}
-
-static acpi_status __init
-zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret)
-{
- acpi_handle handle, parent;
- acpi_status status;
- struct acpi_device_info *info;
- u64 lba_hpa, sba_hpa, length;
- int match;
-
- status = hp_acpi_csr_space(obj, &lba_hpa, &length);
- if (ACPI_FAILURE(status))
- return AE_OK; /* keep looking for another bridge */
-
- /* Look for an enclosing IOC scope and find its CSR space */
- handle = obj;
- do {
- status = acpi_get_object_info(handle, &info);
- if (ACPI_SUCCESS(status) && (info->valid & ACPI_VALID_HID)) {
- /* TBD check _CID also */
- match = (strcmp(info->hardware_id.string, "HWP0001") == 0);
- kfree(info);
- if (match) {
- status = hp_acpi_csr_space(handle, &sba_hpa, &length);
- if (ACPI_SUCCESS(status))
- break;
- else {
- printk(KERN_ERR PFX "Detected HP ZX1 "
- "AGP LBA but no IOC.\n");
- return AE_OK;
- }
- }
- }
-
- status = acpi_get_parent(handle, &parent);
- handle = parent;
- } while (ACPI_SUCCESS(status));
-
- if (ACPI_FAILURE(status))
- return AE_OK; /* found no enclosing IOC */
-
- if (hp_zx1_setup(sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa))
- return AE_OK;
-
- printk(KERN_INFO PFX "Detected HP ZX1 %s AGP chipset "
- "(ioc=%llx, lba=%llx)\n", (char *)context,
- sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa);
-
- hp_zx1_gart_found = 1;
- return AE_CTRL_TERMINATE; /* we only support one bridge; quit looking */
-}
-
-static int __init
-agp_hp_init (void)
-{
- if (agp_off)
- return -EINVAL;
-
- acpi_get_devices("HWP0003", zx1_gart_probe, "HWP0003", NULL);
- if (hp_zx1_gart_found)
- return 0;
-
- acpi_get_devices("HWP0007", zx1_gart_probe, "HWP0007", NULL);
- if (hp_zx1_gart_found)
- return 0;
-
- return -ENODEV;
-}
-
-static void __exit
-agp_hp_cleanup (void)
-{
-}
-
-module_init(agp_hp_init);
-module_exit(agp_hp_cleanup);
-
-MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c
deleted file mode 100644
index 15b240ea4848..000000000000
--- a/drivers/char/agp/i460-agp.c
+++ /dev/null
@@ -1,659 +0,0 @@
-/*
- * For documentation on the i460 AGP interface, see Chapter 7 (AGP Subsystem) of
- * the "Intel 460GTX Chipset Software Developer's Manual":
- * http://www.intel.com/design/archives/itanium/downloads/248704.htm
- */
-/*
- * 460GX support by Chris Ahna <christopher.j.ahna@intel.com>
- * Clean up & simplification by David Mosberger-Tang <davidm@hpl.hp.com>
- */
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/agp_backend.h>
-#include <linux/log2.h>
-
-#include "agp.h"
-
-#define INTEL_I460_BAPBASE 0x98
-#define INTEL_I460_GXBCTL 0xa0
-#define INTEL_I460_AGPSIZ 0xa2
-#define INTEL_I460_ATTBASE 0xfe200000
-#define INTEL_I460_GATT_VALID (1UL << 24)
-#define INTEL_I460_GATT_COHERENT (1UL << 25)
-
-/*
- * The i460 can operate with large (4MB) pages, but there is no sane way to support this
- * within the current kernel/DRM environment, so we disable the relevant code for now.
- * See also comments in ia64_alloc_page()...
- */
-#define I460_LARGE_IO_PAGES 0
-
-#if I460_LARGE_IO_PAGES
-# define I460_IO_PAGE_SHIFT i460.io_page_shift
-#else
-# define I460_IO_PAGE_SHIFT 12
-#endif
-
-#define I460_IOPAGES_PER_KPAGE (PAGE_SIZE >> I460_IO_PAGE_SHIFT)
-#define I460_KPAGES_PER_IOPAGE (1 << (I460_IO_PAGE_SHIFT - PAGE_SHIFT))
-#define I460_SRAM_IO_DISABLE (1 << 4)
-#define I460_BAPBASE_ENABLE (1 << 3)
-#define I460_AGPSIZ_MASK 0x7
-#define I460_4M_PS (1 << 1)
-
-/* Control bits for Out-Of-GART coherency and Burst Write Combining */
-#define I460_GXBCTL_OOG (1UL << 0)
-#define I460_GXBCTL_BWC (1UL << 2)
-
-/*
- * gatt_table entries are 32-bits wide on the i460; the generic code ought to declare the
- * gatt_table and gatt_table_real pointers a "void *"...
- */
-#define RD_GATT(index) readl((u32 *) i460.gatt + (index))
-#define WR_GATT(index, val) writel((val), (u32 *) i460.gatt + (index))
-/*
- * The 460 spec says we have to read the last location written to make sure that all
- * writes have taken effect
- */
-#define WR_FLUSH_GATT(index) RD_GATT(index)
-
-static unsigned long i460_mask_memory (struct agp_bridge_data *bridge,
- dma_addr_t addr, int type);
-
-static struct {
- void *gatt; /* ioremap'd GATT area */
-
- /* i460 supports multiple GART page sizes, so GART pageshift is dynamic: */
- u8 io_page_shift;
-
- /* BIOS configures chipset to one of 2 possible apbase values: */
- u8 dynamic_apbase;
-
- /* structure for tracking partial use of 4MB GART pages: */
- struct lp_desc {
- unsigned long *alloced_map; /* bitmap of kernel-pages in use */
- int refcount; /* number of kernel pages using the large page */
- u64 paddr; /* physical address of large page */
- struct page *page; /* page pointer */
- } *lp_desc;
-} i460;
-
-static const struct aper_size_info_8 i460_sizes[3] =
-{
- /*
- * The 32GB aperture is only available with a 4M GART page size. Due to the
- * dynamic GART page size, we can't figure out page_order or num_entries until
- * runtime.
- */
- {32768, 0, 0, 4},
- {1024, 0, 0, 2},
- {256, 0, 0, 1}
-};
-
-static struct gatt_mask i460_masks[] =
-{
- {
- .mask = INTEL_I460_GATT_VALID | INTEL_I460_GATT_COHERENT,
- .type = 0
- }
-};
-
-static int i460_fetch_size (void)
-{
- int i;
- u8 temp;
- struct aper_size_info_8 *values;
-
- /* Determine the GART page size */
- pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &temp);
- i460.io_page_shift = (temp & I460_4M_PS) ? 22 : 12;
- pr_debug("i460_fetch_size: io_page_shift=%d\n", i460.io_page_shift);
-
- if (i460.io_page_shift != I460_IO_PAGE_SHIFT) {
- printk(KERN_ERR PFX
- "I/O (GART) page-size %luKB doesn't match expected "
- "size %luKB\n",
- 1UL << (i460.io_page_shift - 10),
- 1UL << (I460_IO_PAGE_SHIFT));
- return 0;
- }
-
- values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
-
- pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp);
-
- /* Exit now if the IO drivers for the GART SRAMS are turned off */
- if (temp & I460_SRAM_IO_DISABLE) {
- printk(KERN_ERR PFX "GART SRAMS disabled on 460GX chipset\n");
- printk(KERN_ERR PFX "AGPGART operation not possible\n");
- return 0;
- }
-
- /* Make sure we don't try to create an 2 ^ 23 entry GATT */
- if ((i460.io_page_shift == 0) && ((temp & I460_AGPSIZ_MASK) == 4)) {
- printk(KERN_ERR PFX "We can't have a 32GB aperture with 4KB GART pages\n");
- return 0;
- }
-
- /* Determine the proper APBASE register */
- if (temp & I460_BAPBASE_ENABLE)
- i460.dynamic_apbase = INTEL_I460_BAPBASE;
- else
- i460.dynamic_apbase = AGP_APBASE;
-
- for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
- /*
- * Dynamically calculate the proper num_entries and page_order values for
- * the define aperture sizes. Take care not to shift off the end of
- * values[i].size.
- */
- values[i].num_entries = (values[i].size << 8) >> (I460_IO_PAGE_SHIFT - 12);
- values[i].page_order = ilog2((sizeof(u32)*values[i].num_entries) >> PAGE_SHIFT);
- }
-
- for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
- /* Neglect control bits when matching up size_value */
- if ((temp & I460_AGPSIZ_MASK) == values[i].size_value) {
- agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + i);
- agp_bridge->aperture_size_idx = i;
- return values[i].size;
- }
- }
-
- return 0;
-}
-
-/* There isn't anything to do here since 460 has no GART TLB. */
-static void i460_tlb_flush (struct agp_memory *mem)
-{
- return;
-}
-
-/*
- * This utility function is needed to prevent corruption of the control bits
- * which are stored along with the aperture size in 460's AGPSIZ register
- */
-static void i460_write_agpsiz (u8 size_value)
-{
- u8 temp;
-
- pci_read_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ, &temp);
- pci_write_config_byte(agp_bridge->dev, INTEL_I460_AGPSIZ,
- ((temp & ~I460_AGPSIZ_MASK) | size_value));
-}
-
-static void i460_cleanup (void)
-{
- struct aper_size_info_8 *previous_size;
-
- previous_size = A_SIZE_8(agp_bridge->previous_size);
- i460_write_agpsiz(previous_size->size_value);
-
- if (I460_IO_PAGE_SHIFT > PAGE_SHIFT)
- kfree(i460.lp_desc);
-}
-
-static int i460_configure (void)
-{
- union {
- u32 small[2];
- u64 large;
- } temp;
- size_t size;
- u8 scratch;
- struct aper_size_info_8 *current_size;
-
- temp.large = 0;
-
- current_size = A_SIZE_8(agp_bridge->current_size);
- i460_write_agpsiz(current_size->size_value);
-
- /*
- * Do the necessary rigmarole to read all eight bytes of APBASE.
- * This has to be done since the AGP aperture can be above 4GB on
- * 460 based systems.
- */
- pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase, &(temp.small[0]));
- pci_read_config_dword(agp_bridge->dev, i460.dynamic_apbase + 4, &(temp.small[1]));
-
- /* Clear BAR control bits */
- agp_bridge->gart_bus_addr = temp.large & ~((1UL << 3) - 1);
-
- pci_read_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL, &scratch);
- pci_write_config_byte(agp_bridge->dev, INTEL_I460_GXBCTL,
- (scratch & 0x02) | I460_GXBCTL_OOG | I460_GXBCTL_BWC);
-
- /*
- * Initialize partial allocation trackers if a GART page is bigger than a kernel
- * page.
- */
- if (I460_IO_PAGE_SHIFT > PAGE_SHIFT) {
- size = current_size->num_entries * sizeof(i460.lp_desc[0]);
- i460.lp_desc = kzalloc(size, GFP_KERNEL);
- if (!i460.lp_desc)
- return -ENOMEM;
- }
- return 0;
-}
-
-static int i460_create_gatt_table (struct agp_bridge_data *bridge)
-{
- int page_order, num_entries, i;
- void *temp;
-
- /*
- * Load up the fixed address of the GART SRAMS which hold our GATT table.
- */
- temp = agp_bridge->current_size;
- page_order = A_SIZE_8(temp)->page_order;
- num_entries = A_SIZE_8(temp)->num_entries;
-
- i460.gatt = ioremap(INTEL_I460_ATTBASE, PAGE_SIZE << page_order);
- if (!i460.gatt) {
- printk(KERN_ERR PFX "ioremap failed\n");
- return -ENOMEM;
- }
-
- /* These are no good, the should be removed from the agp_bridge strucure... */
- agp_bridge->gatt_table_real = NULL;
- agp_bridge->gatt_table = NULL;
- agp_bridge->gatt_bus_addr = 0;
-
- for (i = 0; i < num_entries; ++i)
- WR_GATT(i, 0);
- WR_FLUSH_GATT(i - 1);
- return 0;
-}
-
-static int i460_free_gatt_table (struct agp_bridge_data *bridge)
-{
- int num_entries, i;
- void *temp;
-
- temp = agp_bridge->current_size;
-
- num_entries = A_SIZE_8(temp)->num_entries;
-
- for (i = 0; i < num_entries; ++i)
- WR_GATT(i, 0);
- WR_FLUSH_GATT(num_entries - 1);
-
- iounmap(i460.gatt);
- return 0;
-}
-
-/*
- * The following functions are called when the I/O (GART) page size is smaller than
- * PAGE_SIZE.
- */
-
-static int i460_insert_memory_small_io_page (struct agp_memory *mem,
- off_t pg_start, int type)
-{
- unsigned long paddr, io_pg_start, io_page_size;
- int i, j, k, num_entries;
- void *temp;
-
- pr_debug("i460_insert_memory_small_io_page(mem=%p, pg_start=%ld, type=%d, paddr0=0x%lx)\n",
- mem, pg_start, type, page_to_phys(mem->pages[0]));
-
- if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
- return -EINVAL;
-
- io_pg_start = I460_IOPAGES_PER_KPAGE * pg_start;
-
- temp = agp_bridge->current_size;
- num_entries = A_SIZE_8(temp)->num_entries;
-
- if ((io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count) > num_entries) {
- printk(KERN_ERR PFX "Looks like we're out of AGP memory\n");
- return -EINVAL;
- }
-
- j = io_pg_start;
- while (j < (io_pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count)) {
- if (!PGE_EMPTY(agp_bridge, RD_GATT(j))) {
- pr_debug("i460_insert_memory_small_io_page: GATT[%d]=0x%x is busy\n",
- j, RD_GATT(j));
- return -EBUSY;
- }
- j++;
- }
-
- io_page_size = 1UL << I460_IO_PAGE_SHIFT;
- for (i = 0, j = io_pg_start; i < mem->page_count; i++) {
- paddr = page_to_phys(mem->pages[i]);
- for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size)
- WR_GATT(j, i460_mask_memory(agp_bridge, paddr, mem->type));
- }
- WR_FLUSH_GATT(j - 1);
- return 0;
-}
-
-static int i460_remove_memory_small_io_page(struct agp_memory *mem,
- off_t pg_start, int type)
-{
- int i;
-
- pr_debug("i460_remove_memory_small_io_page(mem=%p, pg_start=%ld, type=%d)\n",
- mem, pg_start, type);
-
- pg_start = I460_IOPAGES_PER_KPAGE * pg_start;
-
- for (i = pg_start; i < (pg_start + I460_IOPAGES_PER_KPAGE * mem->page_count); i++)
- WR_GATT(i, 0);
- WR_FLUSH_GATT(i - 1);
- return 0;
-}
-
-#if I460_LARGE_IO_PAGES
-
-/*
- * These functions are called when the I/O (GART) page size exceeds PAGE_SIZE.
- *
- * This situation is interesting since AGP memory allocations that are smaller than a
- * single GART page are possible. The i460.lp_desc array tracks partial allocation of the
- * large GART pages to work around this issue.
- *
- * i460.lp_desc[pg_num].refcount tracks the number of kernel pages in use within GART page
- * pg_num. i460.lp_desc[pg_num].paddr is the physical address of the large page and
- * i460.lp_desc[pg_num].alloced_map is a bitmap of kernel pages that are in use (allocated).
- */
-
-static int i460_alloc_large_page (struct lp_desc *lp)
-{
- unsigned long order = I460_IO_PAGE_SHIFT - PAGE_SHIFT;
- size_t map_size;
-
- lp->page = alloc_pages(GFP_KERNEL, order);
- if (!lp->page) {
- printk(KERN_ERR PFX "Couldn't alloc 4M GART page...\n");
- return -ENOMEM;
- }
-
- map_size = ((I460_KPAGES_PER_IOPAGE + BITS_PER_LONG - 1) & -BITS_PER_LONG)/8;
- lp->alloced_map = kzalloc(map_size, GFP_KERNEL);
- if (!lp->alloced_map) {
- __free_pages(lp->page, order);
- printk(KERN_ERR PFX "Out of memory, we're in trouble...\n");
- return -ENOMEM;
- }
-
- lp->paddr = page_to_phys(lp->page);
- lp->refcount = 0;
- atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp);
- return 0;
-}
-
-static void i460_free_large_page (struct lp_desc *lp)
-{
- kfree(lp->alloced_map);
- lp->alloced_map = NULL;
-
- __free_pages(lp->page, I460_IO_PAGE_SHIFT - PAGE_SHIFT);
- atomic_sub(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp);
-}
-
-static int i460_insert_memory_large_io_page (struct agp_memory *mem,
- off_t pg_start, int type)
-{
- int i, start_offset, end_offset, idx, pg, num_entries;
- struct lp_desc *start, *end, *lp;
- void *temp;
-
- if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
- return -EINVAL;
-
- temp = agp_bridge->current_size;
- num_entries = A_SIZE_8(temp)->num_entries;
-
- /* Figure out what pg_start means in terms of our large GART pages */
- start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE];
- end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE];
- start_offset = pg_start % I460_KPAGES_PER_IOPAGE;
- end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE;
-
- if (end > i460.lp_desc + num_entries) {
- printk(KERN_ERR PFX "Looks like we're out of AGP memory\n");
- return -EINVAL;
- }
-
- /* Check if the requested region of the aperture is free */
- for (lp = start; lp <= end; ++lp) {
- if (!lp->alloced_map)
- continue; /* OK, the entire large page is available... */
-
- for (idx = ((lp == start) ? start_offset : 0);
- idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE);
- idx++)
- {
- if (test_bit(idx, lp->alloced_map))
- return -EBUSY;
- }
- }
-
- for (lp = start, i = 0; lp <= end; ++lp) {
- if (!lp->alloced_map) {
- /* Allocate new GART pages... */
- if (i460_alloc_large_page(lp) < 0)
- return -ENOMEM;
- pg = lp - i460.lp_desc;
- WR_GATT(pg, i460_mask_memory(agp_bridge,
- lp->paddr, 0));
- WR_FLUSH_GATT(pg);
- }
-
- for (idx = ((lp == start) ? start_offset : 0);
- idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE);
- idx++, i++)
- {
- mem->pages[i] = lp->page;
- __set_bit(idx, lp->alloced_map);
- ++lp->refcount;
- }
- }
- return 0;
-}
-
-static int i460_remove_memory_large_io_page (struct agp_memory *mem,
- off_t pg_start, int type)
-{
- int i, pg, start_offset, end_offset, idx, num_entries;
- struct lp_desc *start, *end, *lp;
- void *temp;
-
- temp = agp_bridge->current_size;
- num_entries = A_SIZE_8(temp)->num_entries;
-
- /* Figure out what pg_start means in terms of our large GART pages */
- start = &i460.lp_desc[pg_start / I460_KPAGES_PER_IOPAGE];
- end = &i460.lp_desc[(pg_start + mem->page_count - 1) / I460_KPAGES_PER_IOPAGE];
- start_offset = pg_start % I460_KPAGES_PER_IOPAGE;
- end_offset = (pg_start + mem->page_count - 1) % I460_KPAGES_PER_IOPAGE;
-
- for (i = 0, lp = start; lp <= end; ++lp) {
- for (idx = ((lp == start) ? start_offset : 0);
- idx < ((lp == end) ? (end_offset + 1) : I460_KPAGES_PER_IOPAGE);
- idx++, i++)
- {
- mem->pages[i] = NULL;
- __clear_bit(idx, lp->alloced_map);
- --lp->refcount;
- }
-
- /* Free GART pages if they are unused */
- if (lp->refcount == 0) {
- pg = lp - i460.lp_desc;
- WR_GATT(pg, 0);
- WR_FLUSH_GATT(pg);
- i460_free_large_page(lp);
- }
- }
- return 0;
-}
-
-/* Wrapper routines to call the approriate {small_io_page,large_io_page} function */
-
-static int i460_insert_memory (struct agp_memory *mem,
- off_t pg_start, int type)
-{
- if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT)
- return i460_insert_memory_small_io_page(mem, pg_start, type);
- else
- return i460_insert_memory_large_io_page(mem, pg_start, type);
-}
-
-static int i460_remove_memory (struct agp_memory *mem,
- off_t pg_start, int type)
-{
- if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT)
- return i460_remove_memory_small_io_page(mem, pg_start, type);
- else
- return i460_remove_memory_large_io_page(mem, pg_start, type);
-}
-
-/*
- * If the I/O (GART) page size is bigger than the kernel page size, we don't want to
- * allocate memory until we know where it is to be bound in the aperture (a
- * multi-kernel-page alloc might fit inside of an already allocated GART page).
- *
- * Let's just hope nobody counts on the allocated AGP memory being there before bind time
- * (I don't think current drivers do)...
- */
-static struct page *i460_alloc_page (struct agp_bridge_data *bridge)
-{
- void *page;
-
- if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) {
- page = agp_generic_alloc_page(agp_bridge);
- } else
- /* Returning NULL would cause problems */
- /* AK: really dubious code. */
- page = (void *)~0UL;
- return page;
-}
-
-static void i460_destroy_page (struct page *page, int flags)
-{
- if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) {
- agp_generic_destroy_page(page, flags);
- }
-}
-
-#endif /* I460_LARGE_IO_PAGES */
-
-static unsigned long i460_mask_memory (struct agp_bridge_data *bridge,
- dma_addr_t addr, int type)
-{
- /* Make sure the returned address is a valid GATT entry */
- return bridge->driver->masks[0].mask
- | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12);
-}
-
-const struct agp_bridge_driver intel_i460_driver = {
- .owner = THIS_MODULE,
- .aperture_sizes = i460_sizes,
- .size_type = U8_APER_SIZE,
- .num_aperture_sizes = 3,
- .configure = i460_configure,
- .fetch_size = i460_fetch_size,
- .cleanup = i460_cleanup,
- .tlb_flush = i460_tlb_flush,
- .mask_memory = i460_mask_memory,
- .masks = i460_masks,
- .agp_enable = agp_generic_enable,
- .cache_flush = global_cache_flush,
- .create_gatt_table = i460_create_gatt_table,
- .free_gatt_table = i460_free_gatt_table,
-#if I460_LARGE_IO_PAGES
- .insert_memory = i460_insert_memory,
- .remove_memory = i460_remove_memory,
- .agp_alloc_page = i460_alloc_page,
- .agp_destroy_page = i460_destroy_page,
-#else
- .insert_memory = i460_insert_memory_small_io_page,
- .remove_memory = i460_remove_memory_small_io_page,
- .agp_alloc_page = agp_generic_alloc_page,
- .agp_alloc_pages = agp_generic_alloc_pages,
- .agp_destroy_page = agp_generic_destroy_page,
- .agp_destroy_pages = agp_generic_destroy_pages,
-#endif
- .alloc_by_type = agp_generic_alloc_by_type,
- .free_by_type = agp_generic_free_by_type,
- .agp_type_to_mask_type = agp_generic_type_to_mask_type,
- .cant_use_aperture = true,
-};
-
-static int agp_intel_i460_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct agp_bridge_data *bridge;
- u8 cap_ptr;
-
- cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
- if (!cap_ptr)
- return -ENODEV;
-
- bridge = agp_alloc_bridge();
- if (!bridge)
- return -ENOMEM;
-
- bridge->driver = &intel_i460_driver;
- bridge->dev = pdev;
- bridge->capndx = cap_ptr;
-
- printk(KERN_INFO PFX "Detected Intel 460GX chipset\n");
-
- pci_set_drvdata(pdev, bridge);
- return agp_add_bridge(bridge);
-}
-
-static void agp_intel_i460_remove(struct pci_dev *pdev)
-{
- struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
-
- agp_remove_bridge(bridge);
- agp_put_bridge(bridge);
-}
-
-static struct pci_device_id agp_intel_i460_pci_table[] = {
- {
- .class = (PCI_CLASS_BRIDGE_HOST << 8),
- .class_mask = ~0,
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_84460GX,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- { }
-};
-
-MODULE_DEVICE_TABLE(pci, agp_intel_i460_pci_table);
-
-static struct pci_driver agp_intel_i460_pci_driver = {
- .name = "agpgart-intel-i460",
- .id_table = agp_intel_i460_pci_table,
- .probe = agp_intel_i460_probe,
- .remove = agp_intel_i460_remove,
-};
-
-static int __init agp_intel_i460_init(void)
-{
- if (agp_off)
- return -EINVAL;
- return pci_register_driver(&agp_intel_i460_pci_driver);
-}
-
-static void __exit agp_intel_i460_cleanup(void)
-{
- pci_unregister_driver(&agp_intel_i460_pci_driver);
-}
-
-module_init(agp_intel_i460_init);
-module_exit(agp_intel_i460_cleanup);
-
-MODULE_AUTHOR("Chris Ahna <Christopher.J.Ahna@intel.com>");
-MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 9e4f27a6cb5a..3111e320b2c5 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -12,7 +12,7 @@
#include <asm/smp.h>
#include "agp.h"
#include "intel-agp.h"
-#include <drm/intel-gtt.h>
+#include <drm/intel/intel-gtt.h>
static int intel_fetch_size(void)
{
@@ -817,16 +817,15 @@ static void agp_intel_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-static int agp_intel_resume(struct pci_dev *pdev)
+static int agp_intel_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
bridge->driver->configure();
return 0;
}
-#endif
static const struct pci_device_id agp_intel_pci_table[] = {
#define ID(x) \
@@ -895,14 +894,14 @@ static const struct pci_device_id agp_intel_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_intel_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_intel_pm_ops, NULL, agp_intel_resume);
+
static struct pci_driver agp_intel_pci_driver = {
.name = "agpgart-intel",
.id_table = agp_intel_pci_table,
.probe = agp_intel_probe,
.remove = agp_intel_remove,
-#ifdef CONFIG_PM
- .resume = agp_intel_resume,
-#endif
+ .driver.pm = &agp_intel_pm_ops,
};
static int __init agp_intel_init(void)
@@ -921,4 +920,5 @@ module_init(agp_intel_init);
module_exit(agp_intel_cleanup);
MODULE_AUTHOR("Dave Jones, Various @Intel");
+MODULE_DESCRIPTION("Intel AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index bf6716ff863b..bcc26785175d 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -25,7 +25,7 @@
#include <asm/smp.h>
#include "agp.h"
#include "intel-agp.h"
-#include <drm/intel-gtt.h>
+#include <drm/intel/intel-gtt.h>
#include <asm/set_memory.h>
/*
@@ -53,6 +53,7 @@ struct intel_gtt_driver {
* of the mmio register file, that's done in the generic code. */
void (*cleanup)(void);
void (*write_entry)(dma_addr_t addr, unsigned int entry, unsigned int flags);
+ dma_addr_t (*read_entry)(unsigned int entry, bool *is_present, bool *is_local);
/* Flags is a more or less chipset specific opaque value.
* For chipsets that need to support old ums (non-gem) code, this
* needs to be identical to the various supported agp memory types! */
@@ -336,6 +337,19 @@ static void i810_write_entry(dma_addr_t addr, unsigned int entry,
writel_relaxed(addr | pte_flags, intel_private.gtt + entry);
}
+static dma_addr_t i810_read_entry(unsigned int entry,
+ bool *is_present, bool *is_local)
+{
+ u32 val;
+
+ val = readl(intel_private.gtt + entry);
+
+ *is_present = val & I810_PTE_VALID;
+ *is_local = val & I810_PTE_LOCAL;
+
+ return val & ~0xfff;
+}
+
static resource_size_t intel_gtt_stolen_size(void)
{
u16 gmch_ctrl;
@@ -741,6 +755,19 @@ static void i830_write_entry(dma_addr_t addr, unsigned int entry,
writel_relaxed(addr | pte_flags, intel_private.gtt + entry);
}
+static dma_addr_t i830_read_entry(unsigned int entry,
+ bool *is_present, bool *is_local)
+{
+ u32 val;
+
+ val = readl(intel_private.gtt + entry);
+
+ *is_present = val & I810_PTE_VALID;
+ *is_local = false;
+
+ return val & ~0xfff;
+}
+
bool intel_gmch_enable_gtt(void)
{
u8 __iomem *reg;
@@ -878,6 +905,13 @@ void intel_gmch_gtt_insert_sg_entries(struct sg_table *st,
}
EXPORT_SYMBOL(intel_gmch_gtt_insert_sg_entries);
+dma_addr_t intel_gmch_gtt_read_entry(unsigned int pg,
+ bool *is_present, bool *is_local)
+{
+ return intel_private.driver->read_entry(pg, is_present, is_local);
+}
+EXPORT_SYMBOL(intel_gmch_gtt_read_entry);
+
#if IS_ENABLED(CONFIG_AGP_INTEL)
static void intel_gmch_gtt_insert_pages(unsigned int first_entry,
unsigned int num_entries,
@@ -1126,6 +1160,19 @@ static void i965_write_entry(dma_addr_t addr,
writel_relaxed(addr | pte_flags, intel_private.gtt + entry);
}
+static dma_addr_t i965_read_entry(unsigned int entry,
+ bool *is_present, bool *is_local)
+{
+ u64 val;
+
+ val = readl(intel_private.gtt + entry);
+
+ *is_present = val & I810_PTE_VALID;
+ *is_local = false;
+
+ return ((val & 0xf0) << 28) | (val & ~0xfff);
+}
+
static int i9xx_setup(void)
{
phys_addr_t reg_addr;
@@ -1187,6 +1234,7 @@ static const struct intel_gtt_driver i81x_gtt_driver = {
.cleanup = i810_cleanup,
.check_flags = i830_check_flags,
.write_entry = i810_write_entry,
+ .read_entry = i810_read_entry,
};
static const struct intel_gtt_driver i8xx_gtt_driver = {
.gen = 2,
@@ -1194,6 +1242,7 @@ static const struct intel_gtt_driver i8xx_gtt_driver = {
.setup = i830_setup,
.cleanup = i830_cleanup,
.write_entry = i830_write_entry,
+ .read_entry = i830_read_entry,
.dma_mask_size = 32,
.check_flags = i830_check_flags,
.chipset_flush = i830_chipset_flush,
@@ -1205,6 +1254,7 @@ static const struct intel_gtt_driver i915_gtt_driver = {
.cleanup = i9xx_cleanup,
/* i945 is the last gpu to need phys mem (for overlay and cursors). */
.write_entry = i830_write_entry,
+ .read_entry = i830_read_entry,
.dma_mask_size = 32,
.check_flags = i830_check_flags,
.chipset_flush = i9xx_chipset_flush,
@@ -1215,6 +1265,7 @@ static const struct intel_gtt_driver g33_gtt_driver = {
.setup = i9xx_setup,
.cleanup = i9xx_cleanup,
.write_entry = i965_write_entry,
+ .read_entry = i965_read_entry,
.dma_mask_size = 36,
.check_flags = i830_check_flags,
.chipset_flush = i9xx_chipset_flush,
@@ -1225,6 +1276,7 @@ static const struct intel_gtt_driver pineview_gtt_driver = {
.setup = i9xx_setup,
.cleanup = i9xx_cleanup,
.write_entry = i965_write_entry,
+ .read_entry = i965_read_entry,
.dma_mask_size = 36,
.check_flags = i830_check_flags,
.chipset_flush = i9xx_chipset_flush,
@@ -1235,6 +1287,7 @@ static const struct intel_gtt_driver i965_gtt_driver = {
.setup = i9xx_setup,
.cleanup = i9xx_cleanup,
.write_entry = i965_write_entry,
+ .read_entry = i965_read_entry,
.dma_mask_size = 36,
.check_flags = i830_check_flags,
.chipset_flush = i9xx_chipset_flush,
@@ -1244,6 +1297,7 @@ static const struct intel_gtt_driver g4x_gtt_driver = {
.setup = i9xx_setup,
.cleanup = i9xx_cleanup,
.write_entry = i965_write_entry,
+ .read_entry = i965_read_entry,
.dma_mask_size = 36,
.check_flags = i830_check_flags,
.chipset_flush = i9xx_chipset_flush,
@@ -1254,6 +1308,7 @@ static const struct intel_gtt_driver ironlake_gtt_driver = {
.setup = i9xx_setup,
.cleanup = i9xx_cleanup,
.write_entry = i965_write_entry,
+ .read_entry = i965_read_entry,
.dma_mask_size = 36,
.check_flags = i830_check_flags,
.chipset_flush = i9xx_chipset_flush,
@@ -1461,4 +1516,5 @@ void intel_gmch_remove(void)
EXPORT_SYMBOL(intel_gmch_remove);
MODULE_AUTHOR("Dave Jones, Various @Intel");
+MODULE_DESCRIPTION("Intel GTT (Graphics Translation Table) routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c
index 826dbd06f6bb..4787391bb6b4 100644
--- a/drivers/char/agp/nvidia-agp.c
+++ b/drivers/char/agp/nvidia-agp.c
@@ -11,6 +11,7 @@
#include <linux/page-flags.h>
#include <linux/mm.h>
#include <linux/jiffies.h>
+#include <asm/msr.h>
#include "agp.h"
/* NVIDIA registers */
@@ -404,28 +405,13 @@ static void agp_nvidia_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-static int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
+static int agp_nvidia_resume(struct device *dev)
{
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
- return 0;
-}
-
-static int agp_nvidia_resume(struct pci_dev *pdev)
-{
- /* set power state 0 and restore PCI space */
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
/* reconfigure AGP hardware again */
nvidia_configure();
return 0;
}
-#endif
-
static const struct pci_device_id agp_nvidia_pci_table[] = {
{
@@ -449,15 +435,14 @@ static const struct pci_device_id agp_nvidia_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_nvidia_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_nvidia_pm_ops, NULL, agp_nvidia_resume);
+
static struct pci_driver agp_nvidia_pci_driver = {
.name = "agpgart-nvidia",
.id_table = agp_nvidia_pci_table,
.probe = agp_nvidia_probe,
.remove = agp_nvidia_remove,
-#ifdef CONFIG_PM
- .suspend = agp_nvidia_suspend,
- .resume = agp_nvidia_resume,
-#endif
+ .driver.pm = &agp_nvidia_pm_ops,
};
static int __init agp_nvidia_init(void)
@@ -478,6 +463,7 @@ static void __exit agp_nvidia_cleanup(void)
module_init(agp_nvidia_init);
module_exit(agp_nvidia_cleanup);
+MODULE_DESCRIPTION("Nvidia AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
MODULE_AUTHOR("NVIDIA Corporation");
diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c
index d68d05d5d383..93a48070b2a1 100644
--- a/drivers/char/agp/parisc-agp.c
+++ b/drivers/char/agp/parisc-agp.c
@@ -38,7 +38,7 @@ static struct _parisc_agp_info {
int lba_cap_offset;
- u64 *gatt;
+ __le64 *gatt;
u64 gatt_entries;
u64 gart_base;
@@ -90,6 +90,9 @@ parisc_agp_tlbflush(struct agp_memory *mem)
{
struct _parisc_agp_info *info = &parisc_agp_info;
+ /* force fdc ops to be visible to IOMMU */
+ asm_io_sync();
+
writeq(info->gart_base | ilog2(info->gart_size), info->ioc_regs+IOC_PCOM);
readq(info->ioc_regs+IOC_PCOM); /* flush */
}
@@ -101,7 +104,7 @@ parisc_agp_create_gatt_table(struct agp_bridge_data *bridge)
int i;
for (i = 0; i < info->gatt_entries; i++) {
- info->gatt[i] = (unsigned long)agp_bridge->scratch_page;
+ info->gatt[i] = cpu_to_le64(agp_bridge->scratch_page);
}
return 0;
@@ -155,9 +158,10 @@ parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
for (k = 0;
k < info->io_pages_per_kpage;
k++, j++, paddr += info->io_page_size) {
- info->gatt[j] =
+ info->gatt[j] = cpu_to_le64(
parisc_agp_mask_memory(agp_bridge,
- paddr, type);
+ paddr, type));
+ asm_io_fdc(&info->gatt[j]);
}
}
@@ -180,7 +184,7 @@ parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
io_pg_start = info->io_pages_per_kpage * pg_start;
io_pg_count = info->io_pages_per_kpage * mem->page_count;
for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) {
- info->gatt[i] = agp_bridge->scratch_page;
+ info->gatt[i] = cpu_to_le64(agp_bridge->scratch_page);
}
agp_bridge->driver->tlb_flush(mem);
@@ -191,7 +195,17 @@ static unsigned long
parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr,
int type)
{
- return SBA_PDIR_VALID_BIT | addr;
+ unsigned ci; /* coherent index */
+ dma_addr_t pa;
+
+ pa = addr & IOVP_MASK;
+ asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pa)));
+
+ pa |= (ci >> PAGE_SHIFT) & 0xff;/* move CI (8 bits) into lowest byte */
+ pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */
+
+ /* return native (big-endian) PDIR entry */
+ return pa;
}
static void
@@ -238,7 +252,8 @@ static int __init
agp_ioc_init(void __iomem *ioc_regs)
{
struct _parisc_agp_info *info = &parisc_agp_info;
- u64 iova_base, *io_pdir, io_tlb_ps;
+ u64 iova_base, io_tlb_ps;
+ __le64 *io_pdir;
int io_tlb_shift;
printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n");
@@ -381,8 +396,6 @@ find_quicksilver(struct device *dev, void *data)
static int __init
parisc_agp_init(void)
{
- extern struct sba_device *sba_list;
-
int err = -1;
struct parisc_device *sba = NULL, *lba = NULL;
struct lba_device *lbadev = NULL;
@@ -419,4 +432,5 @@ out:
module_init(parisc_agp_init);
MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
+MODULE_DESCRIPTION("HP Quicksilver AGP GART routines");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c
index f8a02f4bef1b..a0deb97cedb0 100644
--- a/drivers/char/agp/sis-agp.c
+++ b/drivers/char/agp/sis-agp.c
@@ -217,10 +217,7 @@ static void agp_sis_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#define agp_sis_suspend NULL
-
-static int __maybe_unused agp_sis_resume(
- __attribute__((unused)) struct device *dev)
+static int agp_sis_resume(__attribute__((unused)) struct device *dev)
{
return sis_driver.configure();
}
@@ -407,7 +404,7 @@ static const struct pci_device_id agp_sis_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_sis_pci_table);
-static SIMPLE_DEV_PM_OPS(agp_sis_pm_ops, agp_sis_suspend, agp_sis_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_sis_pm_ops, NULL, agp_sis_resume);
static struct pci_driver agp_sis_pci_driver = {
.name = "agpgart-sis",
@@ -436,4 +433,5 @@ module_param(agp_sis_force_delay, bool, 0);
MODULE_PARM_DESC(agp_sis_force_delay,"forces sis delay hack");
module_param(agp_sis_agp_spec, int, 0);
MODULE_PARM_DESC(agp_sis_agp_spec,"0=force sis init, 1=force generic agp3 init, default: autodetect");
+MODULE_DESCRIPTION("SiS AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c
index b91da5998dd7..0ab7562d17c9 100644
--- a/drivers/char/agp/sworks-agp.c
+++ b/drivers/char/agp/sworks-agp.c
@@ -564,5 +564,6 @@ static void __exit agp_serverworks_cleanup(void)
module_init(agp_serverworks_init);
module_exit(agp_serverworks_cleanup);
+MODULE_DESCRIPTION("Serverworks AGPGART routines");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c
index 62de7f4ba864..b8d7115b8c9e 100644
--- a/drivers/char/agp/uninorth-agp.c
+++ b/drivers/char/agp/uninorth-agp.c
@@ -3,6 +3,7 @@
* UniNorth AGPGART routines.
*/
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -725,4 +726,5 @@ MODULE_PARM_DESC(aperture,
"\t\tDefault: " DEFAULT_APERTURE_STRING "M");
MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras");
+MODULE_DESCRIPTION("Apple UniNorth & U3 AGP support");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c
index b2f484f527fb..8b19a5d1a09b 100644
--- a/drivers/char/agp/via-agp.c
+++ b/drivers/char/agp/via-agp.c
@@ -489,9 +489,7 @@ static void agp_via_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#define agp_via_suspend NULL
-
-static int __maybe_unused agp_via_resume(struct device *dev)
+static int agp_via_resume(struct device *dev)
{
struct agp_bridge_data *bridge = dev_get_drvdata(dev);
@@ -551,7 +549,7 @@ static const struct pci_device_id agp_via_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_via_pci_table);
-static SIMPLE_DEV_PM_OPS(agp_via_pm_ops, agp_via_suspend, agp_via_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_via_pm_ops, NULL, agp_via_resume);
static struct pci_driver agp_via_pci_driver = {
.name = "agpgart-via",
@@ -577,5 +575,6 @@ static void __exit agp_via_cleanup(void)
module_init(agp_via_init);
module_exit(agp_via_cleanup);
+MODULE_DESCRIPTION("VIA AGPGART routines");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dave Jones");
diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c
index e795390b070f..4aa5d1c76f83 100644
--- a/drivers/char/apm-emulation.c
+++ b/drivers/char/apm-emulation.c
@@ -141,22 +141,11 @@ static struct apm_queue kapmd_queue;
static DEFINE_MUTEX(state_lock);
-static const char driver_version[] = "1.13"; /* no spaces */
-
-
-
-/*
- * Compatibility cruft until the IPAQ people move over to the new
- * interface.
- */
-static void __apm_get_power_status(struct apm_power_info *info)
-{
-}
/*
* This allows machines to provide their own "apm get power status" function.
*/
-void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status;
+void (*apm_get_power_status)(struct apm_power_info *);
EXPORT_SYMBOL(apm_get_power_status);
@@ -435,6 +424,8 @@ static struct miscdevice apm_device = {
*/
static int proc_apm_show(struct seq_file *m, void *v)
{
+ static const char driver_version[] = "1.13"; /* no spaces */
+
struct apm_power_info info;
char *units;
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c
index 36203d3fa6ea..c138c468f3a4 100644
--- a/drivers/char/applicom.c
+++ b/drivers/char/applicom.c
@@ -111,7 +111,6 @@ static irqreturn_t ac_interrupt(int, void *);
static const struct file_operations ac_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = ac_read,
.write = ac_write,
.unlocked_ioctl = ac_ioctl,
@@ -197,8 +196,10 @@ static int __init applicom_init(void)
if (!pci_match_id(applicom_pci_tbl, dev))
continue;
- if (pci_enable_device(dev))
+ if (pci_enable_device(dev)) {
+ pci_dev_put(dev);
return -EIO;
+ }
RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO);
@@ -207,6 +208,7 @@ static int __init applicom_init(void)
"space at 0x%llx\n",
(unsigned long long)pci_resource_start(dev, 0));
pci_disable_device(dev);
+ pci_dev_put(dev);
return -EIO;
}
@@ -833,7 +835,10 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
ret = -ENOTTY;
break;
}
- Dummy = readb(apbs[IndexCard].RamIO + VERS);
+
+ if (cmd != 6)
+ Dummy = readb(apbs[IndexCard].RamIO + VERS);
+
kfree(adgl);
mutex_unlock(&ac_mutex);
return ret;
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index d5f943938427..837109ef6766 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -6,11 +6,10 @@
* Author: Sonny Rao <sonnyrao@us.ibm.com>
*/
+#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/cdev.h>
@@ -61,7 +60,6 @@ struct bsr_dev {
static unsigned total_bsr_devs;
static LIST_HEAD(bsr_devs);
-static struct class *bsr_class;
static int bsr_major;
enum {
@@ -108,6 +106,11 @@ static struct attribute *bsr_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(bsr_dev);
+static const struct class bsr_class = {
+ .name = "bsr",
+ .dev_groups = bsr_dev_groups,
+};
+
static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long size = vma->vm_end - vma->vm_start;
@@ -244,7 +247,7 @@ static int bsr_add_node(struct device_node *bn)
goto out_err;
}
- cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
+ cur->bsr_device = device_create(&bsr_class, NULL, cur->bsr_dev,
cur, "%s", cur->bsr_name);
if (IS_ERR(cur->bsr_device)) {
printk(KERN_ERR "device_create failed for %s\n",
@@ -293,13 +296,9 @@ static int __init bsr_init(void)
if (!np)
goto out_err;
- bsr_class = class_create(THIS_MODULE, "bsr");
- if (IS_ERR(bsr_class)) {
- printk(KERN_ERR "class_create() failed for bsr_class\n");
- ret = PTR_ERR(bsr_class);
+ ret = class_register(&bsr_class);
+ if (ret)
goto out_err_1;
- }
- bsr_class->dev_groups = bsr_dev_groups;
ret = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
bsr_major = MAJOR(bsr_dev);
@@ -320,7 +319,7 @@ static int __init bsr_init(void)
unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
out_err_2:
- class_destroy(bsr_class);
+ class_unregister(&bsr_class);
out_err_1:
of_node_put(np);
@@ -335,8 +334,7 @@ static void __exit bsr_exit(void)
bsr_cleanup_devs();
- if (bsr_class)
- class_destroy(bsr_class);
+ class_unregister(&bsr_class);
if (bsr_major)
unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
@@ -344,5 +342,6 @@ static void __exit bsr_exit(void)
module_init(bsr_init);
module_exit(bsr_exit);
+MODULE_DESCRIPTION("IBM POWER Barrier Synchronization Register Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");
diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c
index cf89a9631107..44a1cdbd4bfb 100644
--- a/drivers/char/ds1620.c
+++ b/drivers/char/ds1620.c
@@ -353,7 +353,6 @@ static const struct file_operations ds1620_fops = {
.open = ds1620_open,
.read = ds1620_read,
.unlocked_ioctl = ds1620_unlocked_ioctl,
- .llseek = no_llseek,
};
static struct miscdevice ds1620_miscdev = {
@@ -421,4 +420,5 @@ static void __exit ds1620_exit(void)
module_init(ds1620_init);
module_exit(ds1620_exit);
+MODULE_DESCRIPTION("Dallas Semiconductor DS1620 thermometer driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c
index 06749e295ada..1c2c8439797c 100644
--- a/drivers/char/dsp56k.c
+++ b/drivers/char/dsp56k.c
@@ -101,7 +101,9 @@ static struct dsp56k_device {
int tx_wsize, rx_wsize;
} dsp56k;
-static struct class *dsp56k_class;
+static const struct class dsp56k_class = {
+ .name = "dsp56k",
+};
static int dsp56k_reset(void)
{
@@ -493,7 +495,7 @@ static const char banner[] __initconst = KERN_INFO "DSP56k driver installed\n";
static int __init dsp56k_init_driver(void)
{
- int err = 0;
+ int err;
if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
printk("DSP56k driver: Hardware not present\n");
@@ -504,12 +506,10 @@ static int __init dsp56k_init_driver(void)
printk("DSP56k driver: Unable to register driver\n");
return -ENODEV;
}
- dsp56k_class = class_create(THIS_MODULE, "dsp56k");
- if (IS_ERR(dsp56k_class)) {
- err = PTR_ERR(dsp56k_class);
+ err = class_register(&dsp56k_class);
+ if (err)
goto out_chrdev;
- }
- device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL,
+ device_create(&dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL,
"dsp56k");
printk(banner);
@@ -524,11 +524,12 @@ module_init(dsp56k_init_driver);
static void __exit dsp56k_cleanup_driver(void)
{
- device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
- class_destroy(dsp56k_class);
+ device_destroy(&dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
+ class_unregister(&dsp56k_class);
unregister_chrdev(DSP56K_MAJOR, "dsp56k");
}
module_exit(dsp56k_cleanup_driver);
+MODULE_DESCRIPTION("Atari DSP56001 Device Driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("dsp56k/bootstrap.bin");
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
index 6946c1cad9f6..16618079298a 100644
--- a/drivers/char/dtlk.c
+++ b/drivers/char/dtlk.c
@@ -107,7 +107,6 @@ static const struct file_operations dtlk_fops =
.unlocked_ioctl = dtlk_ioctl,
.open = dtlk_open,
.release = dtlk_release,
- .llseek = no_llseek,
};
/* local prototypes */
@@ -244,11 +243,11 @@ static __poll_t dtlk_poll(struct file *file, poll_table * wait)
poll_wait(file, &dtlk_process_list, wait);
if (dtlk_has_indexing && dtlk_readable()) {
- del_timer(&dtlk_timer);
+ timer_delete(&dtlk_timer);
mask = EPOLLIN | EPOLLRDNORM;
}
if (dtlk_writeable()) {
- del_timer(&dtlk_timer);
+ timer_delete(&dtlk_timer);
mask |= EPOLLOUT | EPOLLWRNORM;
}
/* there are no exception conditions */
@@ -323,7 +322,7 @@ static int dtlk_release(struct inode *inode, struct file *file)
}
TRACE_RET;
- del_timer_sync(&dtlk_timer);
+ timer_delete_sync(&dtlk_timer);
return 0;
}
@@ -660,4 +659,5 @@ static char dtlk_write_tts(char ch)
return 0;
}
+MODULE_DESCRIPTION("RC Systems DoubleTalk PC speech card driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c
index 4181bcc1c796..231cbf7b300f 100644
--- a/drivers/char/hangcheck-timer.c
+++ b/drivers/char/hangcheck-timer.c
@@ -69,7 +69,8 @@ MODULE_VERSION(VERSION_STR);
static int __init hangcheck_parse_tick(char *str)
{
int par;
- if (get_option(&str,&par))
+
+ if (get_option(&str, &par))
hangcheck_tick = par;
return 1;
}
@@ -77,7 +78,8 @@ static int __init hangcheck_parse_tick(char *str)
static int __init hangcheck_parse_margin(char *str)
{
int par;
- if (get_option(&str,&par))
+
+ if (get_option(&str, &par))
hangcheck_margin = par;
return 1;
}
@@ -85,7 +87,8 @@ static int __init hangcheck_parse_margin(char *str)
static int __init hangcheck_parse_reboot(char *str)
{
int par;
- if (get_option(&str,&par))
+
+ if (get_option(&str, &par))
hangcheck_reboot = par;
return 1;
}
@@ -93,7 +96,8 @@ static int __init hangcheck_parse_reboot(char *str)
static int __init hangcheck_parse_dump_tasks(char *str)
{
int par;
- if (get_option(&str,&par))
+
+ if (get_option(&str, &par))
hangcheck_dump_tasks = par;
return 1;
}
@@ -126,23 +130,23 @@ static void hangcheck_fire(struct timer_list *unused)
if (tsc_diff > hangcheck_tsc_margin) {
if (hangcheck_dump_tasks) {
- printk(KERN_CRIT "Hangcheck: Task state:\n");
+ pr_crit("Hangcheck: Task state:\n");
#ifdef CONFIG_MAGIC_SYSRQ
handle_sysrq('t');
#endif /* CONFIG_MAGIC_SYSRQ */
}
if (hangcheck_reboot) {
- printk(KERN_CRIT "Hangcheck: hangcheck is restarting the machine.\n");
+ pr_crit("Hangcheck: hangcheck is restarting the machine.\n");
emergency_restart();
} else {
- printk(KERN_CRIT "Hangcheck: hangcheck value past margin!\n");
+ pr_crit("Hangcheck: hangcheck value past margin!\n");
}
}
#if 0
/*
* Enable to investigate delays in detail
*/
- printk("Hangcheck: called %Ld ns since last time (%Ld ns overshoot)\n",
+ pr_debug("Hangcheck: called %lld ns since last time (%lld ns overshoot)\n",
tsc_diff, tsc_diff - hangcheck_tick*TIMER_FREQ);
#endif
mod_timer(&hangcheck_ticktock, jiffies + (hangcheck_tick*HZ));
@@ -152,7 +156,7 @@ static void hangcheck_fire(struct timer_list *unused)
static int __init hangcheck_init(void)
{
- printk("Hangcheck: starting hangcheck timer %s (tick is %d seconds, margin is %d seconds).\n",
+ pr_debug("Hangcheck: starting hangcheck timer %s (tick is %d seconds, margin is %d seconds).\n",
VERSION_STR, hangcheck_tick, hangcheck_margin);
hangcheck_tsc_margin =
(unsigned long long)hangcheck_margin + hangcheck_tick;
@@ -167,8 +171,8 @@ static int __init hangcheck_init(void)
static void __exit hangcheck_exit(void)
{
- del_timer_sync(&hangcheck_ticktock);
- printk("Hangcheck: Stopped hangcheck timer.\n");
+ timer_delete_sync(&hangcheck_ticktock);
+ pr_debug("Hangcheck: Stopped hangcheck timer.\n");
}
module_init(hangcheck_init);
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index ee71376f174b..4f5ccd3a1f56 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -64,25 +64,6 @@
static DEFINE_MUTEX(hpet_mutex); /* replaces BKL */
static u32 hpet_nhpet, hpet_max_freq = HPET_USER_FREQ;
-/* This clocksource driver currently only works on ia64 */
-#ifdef CONFIG_IA64
-static void __iomem *hpet_mctr;
-
-static u64 read_hpet(struct clocksource *cs)
-{
- return (u64)read_counter((void __iomem *)hpet_mctr);
-}
-
-static struct clocksource clocksource_hpet = {
- .name = "hpet",
- .rating = 250,
- .read = read_hpet,
- .mask = CLOCKSOURCE_MASK(64),
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-static struct clocksource *hpet_clocksource;
-#endif
-
/* A lock for concurrent access by app and isr hpet activity. */
static DEFINE_SPINLOCK(hpet_lock);
@@ -106,12 +87,11 @@ struct hpets {
struct hpets *hp_next;
struct hpet __iomem *hp_hpet;
unsigned long hp_hpet_phys;
- struct clocksource *hp_clocksource;
unsigned long long hp_tick_freq;
unsigned long hp_delta;
unsigned int hp_ntimer;
unsigned int hp_which;
- struct hpet_dev hp_dev[];
+ struct hpet_dev hp_dev[] __counted_by(hp_ntimer);
};
static struct hpets *hpets;
@@ -182,6 +162,7 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
static void hpet_timer_set_irq(struct hpet_dev *devp)
{
+ const unsigned int nr_irqs = irq_get_nr_irqs();
unsigned long v;
int irq, gsi;
struct hpet_timer __iomem *timer;
@@ -289,8 +270,13 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
if (!devp->hd_ireqfreq)
return -EIO;
- if (count < sizeof(unsigned long))
- return -EINVAL;
+ if (in_compat_syscall()) {
+ if (count < sizeof(compat_ulong_t))
+ return -EINVAL;
+ } else {
+ if (count < sizeof(unsigned long))
+ return -EINVAL;
+ }
add_wait_queue(&devp->hd_waitqueue, &wait);
@@ -314,9 +300,16 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
schedule();
}
- retval = put_user(data, (unsigned long __user *)buf);
- if (!retval)
- retval = sizeof(unsigned long);
+ if (in_compat_syscall()) {
+ retval = put_user(data, (compat_ulong_t __user *)buf);
+ if (!retval)
+ retval = sizeof(compat_ulong_t);
+ } else {
+ retval = put_user(data, (unsigned long __user *)buf);
+ if (!retval)
+ retval = sizeof(unsigned long);
+ }
+
out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&devp->hd_waitqueue, &wait);
@@ -671,12 +664,24 @@ struct compat_hpet_info {
unsigned short hi_timer;
};
+/* 32-bit types would lead to different command codes which should be
+ * translated into 64-bit ones before passed to hpet_ioctl_common
+ */
+#define COMPAT_HPET_INFO _IOR('h', 0x03, struct compat_hpet_info)
+#define COMPAT_HPET_IRQFREQ _IOW('h', 0x6, compat_ulong_t)
+
static long
hpet_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct hpet_info info;
int err;
+ if (cmd == COMPAT_HPET_INFO)
+ cmd = HPET_INFO;
+
+ if (cmd == COMPAT_HPET_IRQFREQ)
+ cmd = HPET_IRQFREQ;
+
mutex_lock(&hpet_mutex);
err = hpet_ioctl_common(file->private_data, cmd, arg, &info);
mutex_unlock(&hpet_mutex);
@@ -696,7 +701,6 @@ hpet_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
static const struct file_operations hpet_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = hpet_read,
.poll = hpet_poll,
.unlocked_ioctl = hpet_ioctl,
@@ -720,7 +724,7 @@ static int hpet_is_known(struct hpet_data *hdp)
return 0;
}
-static struct ctl_table hpet_table[] = {
+static const struct ctl_table hpet_table[] = {
{
.procname = "max-user-freq",
.data = &hpet_max_freq,
@@ -728,7 +732,6 @@ static struct ctl_table hpet_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
- {}
};
static struct ctl_table_header *sysctl_header;
@@ -805,7 +808,7 @@ int hpet_alloc(struct hpet_data *hdp)
struct hpets *hpetp;
struct hpet __iomem *hpet;
static struct hpets *last;
- unsigned long period;
+ u32 period;
unsigned long long temp;
u32 remainder;
@@ -862,11 +865,11 @@ int hpet_alloc(struct hpet_data *hdp)
do_div(temp, period);
hpetp->hp_tick_freq = temp; /* ticks per second */
- printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s",
+ printk(KERN_INFO "hpet%u: at MMIO 0x%lx, IRQ%s",
hpetp->hp_which, hdp->hd_phys_address,
- hpetp->hp_ntimer > 1 ? "s" : "");
+ str_plural(hpetp->hp_ntimer));
for (i = 0; i < hpetp->hp_ntimer; i++)
- printk(KERN_CONT "%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
+ printk(KERN_CONT "%s %u", i > 0 ? "," : "", hdp->hd_irq[i]);
printk(KERN_CONT "\n");
temp = hpetp->hp_tick_freq;
@@ -907,17 +910,6 @@ int hpet_alloc(struct hpet_data *hdp)
hpetp->hp_delta = hpet_calibrate(hpetp);
-/* This clocksource driver currently only works on ia64 */
-#ifdef CONFIG_IA64
- if (!hpet_clocksource) {
- hpet_mctr = (void __iomem *)&hpetp->hp_hpet->hpet_mc;
- clocksource_hpet.archdata.fsys_mmio = hpet_mctr;
- clocksource_register_hz(&clocksource_hpet, hpetp->hp_tick_freq);
- hpetp->hp_clocksource = &clocksource_hpet;
- hpet_clocksource = &clocksource_hpet;
- }
-#endif
-
return 0;
}
@@ -1031,8 +1023,7 @@ static int __init hpet_init(void)
result = acpi_bus_register_driver(&hpet_acpi_driver);
if (result < 0) {
- if (sysctl_header)
- unregister_sysctl_table(sysctl_header);
+ unregister_sysctl_table(sysctl_header);
misc_deregister(&hpet_misc);
return result;
}
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 3da8e85f8aae..492a2a61a65b 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -37,7 +37,7 @@ config HW_RANDOM_TIMERIOMEM
config HW_RANDOM_INTEL
tristate "Intel HW Random Number Generator support"
- depends on (X86 || IA64) && PCI
+ depends on (X86 || COMPILE_TEST) && PCI
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -50,7 +50,8 @@ config HW_RANDOM_INTEL
config HW_RANDOM_AMD
tristate "AMD HW Random Number Generator support"
- depends on (X86 || PPC_MAPLE) && PCI
+ depends on (X86 || COMPILE_TEST)
+ depends on PCI && HAS_IOPORT_MAP
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -61,9 +62,22 @@ config HW_RANDOM_AMD
If unsure, say Y.
+config HW_RANDOM_AIROHA
+ tristate "Airoha True HW Random Number Generator support"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ default HW_RANDOM
+ help
+ This driver provides kernel-side support for the True Random Number
+ Generator hardware found on Airoha SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called airoha-rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_ATMEL
tristate "Atmel Random Number Generator support"
- depends on (ARCH_AT91 || COMPILE_TEST) && HAVE_CLK && OF
+ depends on (ARCH_MICROCHIP || COMPILE_TEST)
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -98,9 +112,22 @@ config HW_RANDOM_BCM2835
If unsure, say Y.
+config HW_RANDOM_BCM74110
+ tristate "Broadcom BCM74110 Random Number Generator support"
+ depends on ARCH_BRCMSTB || COMPILE_TEST
+ default HW_RANDOM
+ help
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on the Broadcom BCM74110 SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm74110-rng
+
+ If unsure, say Y.
+
config HW_RANDOM_IPROC_RNG200
tristate "Broadcom iProc/STB RNG200 support"
- depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
+ depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST
default HW_RANDOM
help
This driver provides kernel-side support for the RNG200
@@ -113,7 +140,8 @@ config HW_RANDOM_IPROC_RNG200
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
- depends on X86_32 && PCI
+ depends on (X86_32 || COMPILE_TEST)
+ depends on PCI
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -205,7 +233,7 @@ config HW_RANDOM_OCTEON
config HW_RANDOM_PASEMI
tristate "PA Semi HW Random Number Generator support"
- depends on PPC_PASEMI
+ depends on PPC_PASEMI || (PPC && COMPILE_TEST)
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -228,7 +256,7 @@ config HW_RANDOM_VIRTIO
config HW_RANDOM_MXC_RNGA
tristate "Freescale i.MX RNGA Random Number Generator"
- depends on SOC_IMX31
+ depends on SOC_IMX31 || COMPILE_TEST
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -241,7 +269,7 @@ config HW_RANDOM_MXC_RNGA
config HW_RANDOM_IMX_RNGC
tristate "Freescale i.MX RNGC Random Number Generator"
- depends on HAS_IOMEM && HAVE_CLK
+ depends on HAS_IOMEM
depends on SOC_IMX25 || SOC_IMX6SL || SOC_IMX6SLL || SOC_IMX6UL || COMPILE_TEST
default HW_RANDOM
help
@@ -256,8 +284,7 @@ config HW_RANDOM_IMX_RNGC
config HW_RANDOM_INGENIC_RNG
tristate "Ingenic Random Number Generator support"
- depends on HW_RANDOM
- depends on MACH_JZ4780 || MACH_X1000
+ depends on MACH_JZ4780 || MACH_X1000 || COMPILE_TEST
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number Generator
@@ -271,8 +298,7 @@ config HW_RANDOM_INGENIC_RNG
config HW_RANDOM_INGENIC_TRNG
tristate "Ingenic True Random Number Generator support"
- depends on HW_RANDOM
- depends on MACH_X1830
+ depends on MACH_X1830 || COMPILE_TEST
default HW_RANDOM
help
This driver provides kernel-side support for the True Random Number Generator
@@ -286,6 +312,7 @@ config HW_RANDOM_INGENIC_TRNG
config HW_RANDOM_NOMADIK
tristate "ST-Ericsson Nomadik Random Number Generator support"
depends on ARCH_NOMADIK || COMPILE_TEST
+ depends on ARM_AMBA
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -324,7 +351,7 @@ config HW_RANDOM_POWERNV
config HW_RANDOM_HISI
tristate "Hisilicon Random Number Generator support"
- depends on HW_RANDOM && ARCH_HISI
+ depends on ARCH_HISI || COMPILE_TEST
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -335,9 +362,20 @@ config HW_RANDOM_HISI
If unsure, say Y.
+config HW_RANDOM_HISTB
+ tristate "Hisilicon STB Random Number Generator support"
+ depends on ARCH_HISI || COMPILE_TEST
+ default ARCH_HISI
+ help
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Hisilicon Hi37xx SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called histb-rng.
+
config HW_RANDOM_ST
tristate "ST Microelectronics HW Random Number Generator support"
- depends on HW_RANDOM && ARCH_STI
+ depends on ARCH_STI || COMPILE_TEST
help
This driver provides kernel-side support for the Random Number
Generator hardware found on STi series of SoCs.
@@ -347,7 +385,7 @@ config HW_RANDOM_ST
config HW_RANDOM_XGENE
tristate "APM X-Gene True Random Number Generator (TRNG) support"
- depends on HW_RANDOM && ARCH_XGENE
+ depends on ARCH_XGENE || COMPILE_TEST
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -360,7 +398,7 @@ config HW_RANDOM_XGENE
config HW_RANDOM_STM32
tristate "STMicroelectronics STM32 random number generator"
- depends on HW_RANDOM && (ARCH_STM32 || COMPILE_TEST)
+ depends on ARCH_STM32 || COMPILE_TEST
depends on HAS_IOMEM
default HW_RANDOM
help
@@ -374,8 +412,8 @@ config HW_RANDOM_STM32
config HW_RANDOM_PIC32
tristate "Microchip PIC32 Random Number Generator support"
- depends on HW_RANDOM && MACH_PIC32
- default y
+ depends on MACH_PIC32 || COMPILE_TEST
+ default HW_RANDOM if MACH_PIC32
help
This driver provides kernel-side support for the Random Number
Generator hardware found on a PIC32.
@@ -400,9 +438,9 @@ config HW_RANDOM_POLARFIRE_SOC
config HW_RANDOM_MESON
tristate "Amlogic Meson Random Number Generator support"
- depends on HW_RANDOM
depends on ARCH_MESON || COMPILE_TEST
- default y
+ depends on HAS_IOMEM && OF
+ default HW_RANDOM if ARCH_MESON
help
This driver provides kernel-side support for the Random Number
Generator hardware found on Amlogic Meson SoCs.
@@ -414,7 +452,8 @@ config HW_RANDOM_MESON
config HW_RANDOM_CAVIUM
tristate "Cavium ThunderX Random Number Generator support"
- depends on HW_RANDOM && PCI && ARCH_THUNDER
+ depends on PCI
+ depends on ARCH_THUNDER || (ARM64 && COMPILE_TEST)
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
@@ -427,9 +466,9 @@ config HW_RANDOM_CAVIUM
config HW_RANDOM_MTK
tristate "Mediatek Random Number Generator support"
- depends on HW_RANDOM
depends on ARCH_MEDIATEK || COMPILE_TEST
- default y
+ depends on HAS_IOMEM && OF
+ default HW_RANDOM if ARCH_MEDIATEK
help
This driver provides kernel-side support for the Random Number
Generator hardware found on Mediatek SoCs.
@@ -456,7 +495,8 @@ config HW_RANDOM_S390
config HW_RANDOM_EXYNOS
tristate "Samsung Exynos True Random Number Generator support"
depends on ARCH_EXYNOS || COMPILE_TEST
- default HW_RANDOM
+ depends on HAS_IOMEM
+ default HW_RANDOM if ARCH_EXYNOS
help
This driver provides support for the True Random Number
Generator available in Exynos SoCs.
@@ -483,7 +523,8 @@ config HW_RANDOM_OPTEE
config HW_RANDOM_NPCM
tristate "NPCM Random Number Generator support"
depends on ARCH_NPCM || COMPILE_TEST
- default HW_RANDOM
+ depends on HAS_IOMEM
+ default HW_RANDOM if ARCH_NPCM
help
This driver provides support for the Random Number
Generator hardware available in Nuvoton NPCM SoCs.
@@ -494,10 +535,10 @@ config HW_RANDOM_NPCM
If unsure, say Y.
config HW_RANDOM_KEYSTONE
+ tristate "TI Keystone NETCP SA Hardware random number generator"
depends on ARCH_KEYSTONE || COMPILE_TEST
depends on HAS_IOMEM && OF
default HW_RANDOM
- tristate "TI Keystone NETCP SA Hardware random number generator"
help
This option enables Keystone's hardware random generator.
@@ -539,15 +580,40 @@ config HW_RANDOM_ARM_SMCCC_TRNG
module will be called arm_smccc_trng.
config HW_RANDOM_CN10K
- tristate "Marvell CN10K Random Number Generator support"
- depends on HW_RANDOM && PCI && (ARM64 || (64BIT && COMPILE_TEST))
- default HW_RANDOM
- help
- This driver provides support for the True Random Number
- generator available in Marvell CN10K SoCs.
-
- To compile this driver as a module, choose M here.
- The module will be called cn10k_rng. If unsure, say Y.
+ tristate "Marvell CN10K Random Number Generator support"
+ depends on HW_RANDOM && PCI && (ARM64 || (64BIT && COMPILE_TEST))
+ default HW_RANDOM if ARCH_THUNDER
+ help
+ This driver provides support for the True Random Number
+ generator available in Marvell CN10K SoCs.
+
+ To compile this driver as a module, choose M here.
+ The module will be called cn10k_rng. If unsure, say Y.
+
+config HW_RANDOM_JH7110
+ tristate "StarFive JH7110 Random Number Generator support"
+ depends on SOC_STARFIVE || COMPILE_TEST
+ help
+ This driver provides support for the True Random Number
+ Generator in StarFive JH7110 SoCs.
+
+ To compile this driver as a module, choose M here.
+ The module will be called jh7110-trng.
+
+config HW_RANDOM_ROCKCHIP
+ tristate "Rockchip True Random Number Generator"
+ depends on HW_RANDOM && (ARCH_ROCKCHIP || COMPILE_TEST)
+ depends on HAS_IOMEM
+ default HW_RANDOM
+ help
+ This driver provides kernel-side support for the True Random Number
+ Generator hardware found on some Rockchip SoCs like RK3566, RK3568
+ or RK3588.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rockchip-rng.
+
+ If unsure, say Y.
endif # HW_RANDOM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 3e948cf04476..b9132b3f5d21 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -8,6 +8,7 @@ rng-core-y := core.o
obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
+obj-$(CONFIG_HW_RANDOM_AIROHA) += airoha-trng.o
obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
obj-$(CONFIG_HW_RANDOM_BA431) += ba431-rng.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
@@ -29,7 +30,9 @@ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o
+obj-$(CONFIG_HW_RANDOM_HISTB) += histb-rng.o
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM74110) += bcm74110-rng.o
obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o
@@ -47,3 +50,5 @@ obj-$(CONFIG_HW_RANDOM_XIPHERA) += xiphera-trng.o
obj-$(CONFIG_HW_RANDOM_ARM_SMCCC_TRNG) += arm_smccc_trng.o
obj-$(CONFIG_HW_RANDOM_CN10K) += cn10k-rng.o
obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o
+obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o
+obj-$(CONFIG_HW_RANDOM_JH7110) += jh7110-trng.o
diff --git a/drivers/char/hw_random/airoha-trng.c b/drivers/char/hw_random/airoha-trng.c
new file mode 100644
index 000000000000..1dbfa9505c21
--- /dev/null
+++ b/drivers/char/hw_random/airoha-trng.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2024 Christian Marangi */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/hw_random.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+
+#define TRNG_IP_RDY 0x800
+#define CNT_TRANS GENMASK(15, 8)
+#define SAMPLE_RDY BIT(0)
+#define TRNG_NS_SEK_AND_DAT_EN 0x804
+#define RNG_EN BIT(31) /* referenced as ring_en */
+#define RAW_DATA_EN BIT(16)
+#define TRNG_HEALTH_TEST_SW_RST 0x808
+#define SW_RST BIT(0) /* Active High */
+#define TRNG_INTR_EN 0x818
+#define INTR_MASK BIT(16)
+#define CONTINUOUS_HEALTH_INITR_EN BIT(2)
+#define SW_STARTUP_INITR_EN BIT(1)
+#define RST_STARTUP_INITR_EN BIT(0)
+/* Notice that Health Test are done only out of Reset and with RNG_EN */
+#define TRNG_HEALTH_TEST_STATUS 0x824
+#define CONTINUOUS_HEALTH_AP_TEST_FAIL BIT(23)
+#define CONTINUOUS_HEALTH_RC_TEST_FAIL BIT(22)
+#define SW_STARTUP_TEST_DONE BIT(21)
+#define SW_STARTUP_AP_TEST_FAIL BIT(20)
+#define SW_STARTUP_RC_TEST_FAIL BIT(19)
+#define RST_STARTUP_TEST_DONE BIT(18)
+#define RST_STARTUP_AP_TEST_FAIL BIT(17)
+#define RST_STARTUP_RC_TEST_FAIL BIT(16)
+#define RAW_DATA_VALID BIT(7)
+
+#define TRNG_RAW_DATA_OUT 0x828
+
+#define TRNG_CNT_TRANS_VALID 0x80
+#define BUSY_LOOP_SLEEP 10
+#define BUSY_LOOP_TIMEOUT (BUSY_LOOP_SLEEP * 10000)
+
+struct airoha_trng {
+ void __iomem *base;
+ struct hwrng rng;
+ struct device *dev;
+
+ struct completion rng_op_done;
+};
+
+static int airoha_trng_irq_mask(struct airoha_trng *trng)
+{
+ u32 val;
+
+ val = readl(trng->base + TRNG_INTR_EN);
+ val |= INTR_MASK;
+ writel(val, trng->base + TRNG_INTR_EN);
+
+ return 0;
+}
+
+static int airoha_trng_irq_unmask(struct airoha_trng *trng)
+{
+ u32 val;
+
+ val = readl(trng->base + TRNG_INTR_EN);
+ val &= ~INTR_MASK;
+ writel(val, trng->base + TRNG_INTR_EN);
+
+ return 0;
+}
+
+static int airoha_trng_init(struct hwrng *rng)
+{
+ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng);
+ int ret;
+ u32 val;
+
+ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN);
+ val |= RNG_EN;
+ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN);
+
+ /* Set out of SW Reset */
+ airoha_trng_irq_unmask(trng);
+ writel(0, trng->base + TRNG_HEALTH_TEST_SW_RST);
+
+ ret = wait_for_completion_timeout(&trng->rng_op_done, BUSY_LOOP_TIMEOUT);
+ if (ret <= 0) {
+ dev_err(trng->dev, "Timeout waiting for Health Check\n");
+ airoha_trng_irq_mask(trng);
+ return -ENODEV;
+ }
+
+ /* Check if Health Test Failed */
+ val = readl(trng->base + TRNG_HEALTH_TEST_STATUS);
+ if (val & (RST_STARTUP_AP_TEST_FAIL | RST_STARTUP_RC_TEST_FAIL)) {
+ dev_err(trng->dev, "Health Check fail: %s test fail\n",
+ val & RST_STARTUP_AP_TEST_FAIL ? "AP" : "RC");
+ return -ENODEV;
+ }
+
+ /* Check if IP is ready */
+ ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val,
+ val & SAMPLE_RDY, 10, 1000);
+ if (ret < 0) {
+ dev_err(trng->dev, "Timeout waiting for IP ready");
+ return -ENODEV;
+ }
+
+ /* CNT_TRANS must be 0x80 for IP to be considered ready */
+ ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val,
+ FIELD_GET(CNT_TRANS, val) == TRNG_CNT_TRANS_VALID,
+ 10, 1000);
+ if (ret < 0) {
+ dev_err(trng->dev, "Timeout waiting for IP ready");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void airoha_trng_cleanup(struct hwrng *rng)
+{
+ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng);
+ u32 val;
+
+ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN);
+ val &= ~RNG_EN;
+ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN);
+
+ /* Put it in SW Reset */
+ writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST);
+}
+
+static int airoha_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng);
+ u32 *data = buf;
+ u32 status;
+ int ret;
+
+ ret = readl_poll_timeout(trng->base + TRNG_HEALTH_TEST_STATUS, status,
+ status & RAW_DATA_VALID, 10, 1000);
+ if (ret < 0) {
+ dev_err(trng->dev, "Timeout waiting for TRNG RAW Data valid\n");
+ return ret;
+ }
+
+ *data = readl(trng->base + TRNG_RAW_DATA_OUT);
+
+ return 4;
+}
+
+static irqreturn_t airoha_trng_irq(int irq, void *priv)
+{
+ struct airoha_trng *trng = (struct airoha_trng *)priv;
+
+ airoha_trng_irq_mask(trng);
+ /* Just complete the task, we will read the value later */
+ complete(&trng->rng_op_done);
+
+ return IRQ_HANDLED;
+}
+
+static int airoha_trng_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct airoha_trng *trng;
+ int irq, ret;
+ u32 val;
+
+ trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ trng->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(trng->base))
+ return PTR_ERR(trng->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ airoha_trng_irq_mask(trng);
+ ret = devm_request_irq(&pdev->dev, irq, airoha_trng_irq, 0,
+ pdev->name, (void *)trng);
+ if (ret) {
+ dev_err(dev, "Can't get interrupt working.\n");
+ return ret;
+ }
+
+ init_completion(&trng->rng_op_done);
+
+ /* Enable interrupt for SW reset Health Check */
+ val = readl(trng->base + TRNG_INTR_EN);
+ val |= RST_STARTUP_INITR_EN;
+ writel(val, trng->base + TRNG_INTR_EN);
+
+ /* Set output to raw data */
+ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN);
+ val |= RAW_DATA_EN;
+ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN);
+
+ /* Put it in SW Reset */
+ writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST);
+
+ trng->dev = dev;
+ trng->rng.name = pdev->name;
+ trng->rng.init = airoha_trng_init;
+ trng->rng.cleanup = airoha_trng_cleanup;
+ trng->rng.read = airoha_trng_read;
+
+ ret = devm_hwrng_register(dev, &trng->rng);
+ if (ret) {
+ dev_err(dev, "failed to register rng device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id airoha_trng_of_match[] = {
+ { .compatible = "airoha,en7581-trng", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, airoha_trng_of_match);
+
+static struct platform_driver airoha_trng_driver = {
+ .driver = {
+ .name = "airoha-trng",
+ .of_match_table = airoha_trng_of_match,
+ },
+ .probe = airoha_trng_probe,
+};
+
+module_platform_driver(airoha_trng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Airoha True Random Number Generator driver");
diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c
index c22d4184bb61..9a24d19236dc 100644
--- a/drivers/char/hw_random/amd-rng.c
+++ b/drivers/char/hw_random/amd-rng.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/hw_random.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -142,16 +143,22 @@ static int __init amd_rng_mod_init(void)
found:
err = pci_read_config_dword(pdev, 0x58, &pmbase);
- if (err)
- return err;
+ if (err) {
+ err = pcibios_err_to_errno(err);
+ goto put_dev;
+ }
pmbase &= 0x0000FF00;
- if (pmbase == 0)
- return -EIO;
+ if (pmbase == 0) {
+ err = -EIO;
+ goto put_dev;
+ }
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+ if (!priv) {
+ err = -ENOMEM;
+ goto put_dev;
+ }
if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {
dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
@@ -185,6 +192,8 @@ err_iomap:
release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
out:
kfree(priv);
+put_dev:
+ pci_dev_put(pdev);
return err;
}
@@ -200,6 +209,8 @@ static void __exit amd_rng_mod_exit(void)
release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);
+ pci_dev_put(priv->pcidev);
+
kfree(priv);
}
diff --git a/drivers/char/hw_random/arm_smccc_trng.c b/drivers/char/hw_random/arm_smccc_trng.c
index b24ac39a903b..dcb8e7f37f25 100644
--- a/drivers/char/hw_random/arm_smccc_trng.c
+++ b/drivers/char/hw_random/arm_smccc_trng.c
@@ -71,8 +71,6 @@ static int smccc_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
MAX_BITS_PER_CALL);
arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND, bits, &res);
- if ((int)res.a0 < 0)
- return (int)res.a0;
switch ((int)res.a0) {
case SMCCC_RET_SUCCESS:
@@ -88,6 +86,8 @@ static int smccc_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
return copied;
cond_resched();
break;
+ default:
+ return -EIO;
}
}
@@ -105,8 +105,6 @@ static int smccc_trng_probe(struct platform_device *pdev)
trng->name = "smccc_trng";
trng->read = smccc_trng_read;
- platform_set_drvdata(pdev, trng);
-
return devm_hwrng_register(&pdev->dev, trng);
}
@@ -120,4 +118,5 @@ module_platform_driver(smccc_trng_driver);
MODULE_ALIAS("platform:smccc_trng");
MODULE_AUTHOR("Andre Przywara");
+MODULE_DESCRIPTION("Arm SMCCC TRNG firmware interface support");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c
index b8effe77d80f..6ed24be3481d 100644
--- a/drivers/char/hw_random/atmel-rng.c
+++ b/drivers/char/hw_random/atmel-rng.c
@@ -15,7 +15,7 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/hw_random.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -37,6 +37,7 @@ struct atmel_trng {
struct clk *clk;
void __iomem *base;
struct hwrng rng;
+ struct device *dev;
bool has_half_rate;
};
@@ -59,9 +60,9 @@ static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
u32 *data = buf;
int ret;
- ret = pm_runtime_get_sync((struct device *)trng->rng.priv);
+ ret = pm_runtime_get_sync(trng->dev);
if (ret < 0) {
- pm_runtime_put_sync((struct device *)trng->rng.priv);
+ pm_runtime_put_sync(trng->dev);
return ret;
}
@@ -79,8 +80,7 @@ static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
ret = 4;
out:
- pm_runtime_mark_last_busy((struct device *)trng->rng.priv);
- pm_runtime_put_sync_autosuspend((struct device *)trng->rng.priv);
+ pm_runtime_put_sync_autosuspend(trng->dev);
return ret;
}
@@ -134,9 +134,9 @@ static int atmel_trng_probe(struct platform_device *pdev)
return -ENODEV;
trng->has_half_rate = data->has_half_rate;
+ trng->dev = &pdev->dev;
trng->rng.name = pdev->name;
trng->rng.read = atmel_trng_read;
- trng->rng.priv = (unsigned long)&pdev->dev;
platform_set_drvdata(pdev, trng);
#ifndef CONFIG_PM
@@ -161,15 +161,13 @@ static int atmel_trng_probe(struct platform_device *pdev)
return ret;
}
-static int atmel_trng_remove(struct platform_device *pdev)
+static void atmel_trng_remove(struct platform_device *pdev)
{
struct atmel_trng *trng = platform_get_drvdata(pdev);
atmel_trng_cleanup(trng);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
-
- return 0;
}
static int __maybe_unused atmel_trng_runtime_suspend(struct device *dev)
diff --git a/drivers/char/hw_random/ba431-rng.c b/drivers/char/hw_random/ba431-rng.c
index 5b7ca0416490..9de7466e6896 100644
--- a/drivers/char/hw_random/ba431-rng.c
+++ b/drivers/char/hw_random/ba431-rng.c
@@ -189,13 +189,9 @@ static int ba431_trng_probe(struct platform_device *pdev)
ba431->rng.cleanup = ba431_trng_cleanup;
ba431->rng.read = ba431_trng_read;
- platform_set_drvdata(pdev, ba431);
-
ret = devm_hwrng_register(&pdev->dev, &ba431->rng);
- if (ret) {
- dev_err(&pdev->dev, "BA431 registration failed (%d)\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "BA431 registration failed\n");
dev_info(&pdev->dev, "BA431 TRNG registered\n");
@@ -203,7 +199,7 @@ static int ba431_trng_probe(struct platform_device *pdev)
}
static const struct of_device_id ba431_trng_dt_ids[] = {
- { .compatible = "silex-insight,ba431-rng", .data = NULL },
+ { .compatible = "silex-insight,ba431-rng" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ba431_trng_dt_ids);
diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c
index e7dd457e9b22..6d6ac409efcf 100644
--- a/drivers/char/hw_random/bcm2835-rng.c
+++ b/drivers/char/hw_random/bcm2835-rng.c
@@ -8,8 +8,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/clk.h>
@@ -71,7 +70,7 @@ static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max,
while ((rng_readl(priv, RNG_STATUS) >> 24) == 0) {
if (!wait)
return 0;
- cpu_relax();
+ hwrng_yield(rng);
}
num_words = rng_readl(priv, RNG_STATUS) >> 24;
@@ -95,8 +94,10 @@ static int bcm2835_rng_init(struct hwrng *rng)
return ret;
ret = reset_control_reset(priv->reset);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(priv->clk);
return ret;
+ }
if (priv->mask_interrupts) {
/* mask the interrupt */
@@ -137,12 +138,11 @@ static const struct of_device_id bcm2835_rng_of_match[] = {
{ .compatible = "brcm,bcm6368-rng"},
{},
};
+MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
static int bcm2835_rng_probe(struct platform_device *pdev)
{
- const struct bcm2835_rng_of_data *of_data;
struct device *dev = &pdev->dev;
- const struct of_device_id *rng_id;
struct bcm2835_rng_priv *priv;
int err;
@@ -150,8 +150,6 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- platform_set_drvdata(pdev, priv);
-
/* map peripheral */
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
@@ -172,12 +170,10 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
priv->rng.cleanup = bcm2835_rng_cleanup;
if (dev_of_node(dev)) {
- rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
- if (!rng_id)
- return -EINVAL;
+ const struct bcm2835_rng_of_data *of_data;
/* Check for rng init function, execute it */
- of_data = rng_id->data;
+ of_data = of_device_get_match_data(dev);
if (of_data)
priv->mask_interrupts = of_data->mask_interrupts;
}
@@ -192,8 +188,6 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
return err;
}
-MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
-
static const struct platform_device_id bcm2835_rng_devtype[] = {
{ .name = "bcm2835-rng" },
{ .name = "bcm63xx-rng" },
diff --git a/drivers/char/hw_random/bcm74110-rng.c b/drivers/char/hw_random/bcm74110-rng.c
new file mode 100644
index 000000000000..5c64148e91f1
--- /dev/null
+++ b/drivers/char/hw_random/bcm74110-rng.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Broadcom
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/hw_random.h>
+
+#define HOST_REV_ID 0x00
+#define HOST_FIFO_DEPTH 0x04
+#define HOST_FIFO_COUNT 0x08
+#define HOST_FIFO_THRESHOLD 0x0c
+#define HOST_FIFO_DATA 0x10
+
+#define HOST_FIFO_COUNT_MASK 0xffff
+
+/* Delay range in microseconds */
+#define FIFO_DELAY_MIN_US 3
+#define FIFO_DELAY_MAX_US 7
+#define FIFO_DELAY_MAX_COUNT 10
+
+struct bcm74110_priv {
+ void __iomem *base;
+};
+
+static inline int bcm74110_rng_fifo_count(void __iomem *mem)
+{
+ return readl_relaxed(mem) & HOST_FIFO_COUNT_MASK;
+}
+
+static int bcm74110_rng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct bcm74110_priv *priv = (struct bcm74110_priv *)rng->priv;
+ void __iomem *fc_addr = priv->base + HOST_FIFO_COUNT;
+ void __iomem *fd_addr = priv->base + HOST_FIFO_DATA;
+ unsigned underrun_count = 0;
+ u32 max_words = max / sizeof(u32);
+ u32 num_words;
+ unsigned i;
+
+ /*
+ * We need to check how many words are available in the RNG FIFO. If
+ * there aren't any, we need to wait for some to become available.
+ */
+ while ((num_words = bcm74110_rng_fifo_count(fc_addr)) == 0) {
+ if (!wait)
+ return 0;
+ /*
+ * As a precaution, limit how long we wait. If the FIFO doesn't
+ * refill within the allotted time, return 0 (=no data) to the
+ * caller.
+ */
+ if (likely(underrun_count < FIFO_DELAY_MAX_COUNT))
+ usleep_range(FIFO_DELAY_MIN_US, FIFO_DELAY_MAX_US);
+ else
+ return 0;
+ underrun_count++;
+ }
+ if (num_words > max_words)
+ num_words = max_words;
+
+ /* Bail early if we run out of random numbers unexpectedly */
+ for (i = 0; i < num_words && bcm74110_rng_fifo_count(fc_addr) > 0; i++)
+ ((u32 *)buf)[i] = readl_relaxed(fd_addr);
+
+ return i * sizeof(u32);
+}
+
+static struct hwrng bcm74110_hwrng = {
+ .read = bcm74110_rng_read,
+};
+
+static int bcm74110_rng_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm74110_priv *priv;
+ int rc;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ bcm74110_hwrng.name = pdev->name;
+ bcm74110_hwrng.priv = (unsigned long)priv;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ rc = devm_hwrng_register(dev, &bcm74110_hwrng);
+ if (rc)
+ dev_err(dev, "hwrng registration failed (%d)\n", rc);
+ else
+ dev_info(dev, "hwrng registered\n");
+
+ return rc;
+}
+
+static const struct of_device_id bcm74110_rng_match[] = {
+ { .compatible = "brcm,bcm74110-rng", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm74110_rng_match);
+
+static struct platform_driver bcm74110_rng_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = bcm74110_rng_match,
+ },
+ .probe = bcm74110_rng_probe,
+};
+module_platform_driver(bcm74110_rng_driver);
+
+MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
+MODULE_DESCRIPTION("BCM 74110 Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/cavium-rng-vf.c b/drivers/char/hw_random/cavium-rng-vf.c
index 7c55f4cf4a8b..c1b8918b2292 100644
--- a/drivers/char/hw_random/cavium-rng-vf.c
+++ b/drivers/char/hw_random/cavium-rng-vf.c
@@ -225,7 +225,6 @@ static int cavium_rng_probe_vf(struct pci_dev *pdev,
return -ENOMEM;
rng->ops.read = cavium_rng_read;
- rng->ops.quality = 1000;
pci_set_drvdata(pdev, rng);
@@ -267,4 +266,5 @@ static struct pci_driver cavium_rng_vf_driver = {
module_pci_driver(cavium_rng_vf_driver);
MODULE_AUTHOR("Omer Khaliq <okhaliq@caviumnetworks.com>");
+MODULE_DESCRIPTION("Cavium ThunderX Random Number Generator VF support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/cavium-rng.c b/drivers/char/hw_random/cavium-rng.c
index b96579222408..d9d7b6038c06 100644
--- a/drivers/char/hw_random/cavium-rng.c
+++ b/drivers/char/hw_random/cavium-rng.c
@@ -88,4 +88,5 @@ static struct pci_driver cavium_rng_pf_driver = {
module_pci_driver(cavium_rng_pf_driver);
MODULE_AUTHOR("Omer Khaliq <okhaliq@caviumnetworks.com>");
+MODULE_DESCRIPTION("Cavium ThunderX Random Number Generator support");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c
index 302ffa354c2f..a5be9258037f 100644
--- a/drivers/char/hw_random/cctrng.c
+++ b/drivers/char/hw_random/cctrng.c
@@ -98,7 +98,6 @@ static void cc_trng_pm_put_suspend(struct device *dev)
{
int rc = 0;
- pm_runtime_mark_last_busy(dev);
rc = pm_runtime_put_autosuspend(dev);
if (rc)
dev_err(dev, "pm_runtime_put_autosuspend returned %x\n", rc);
@@ -455,35 +454,6 @@ static void cc_trng_startwork_handler(struct work_struct *w)
cc_trng_hw_trigger(drvdata);
}
-
-static int cc_trng_clk_init(struct cctrng_drvdata *drvdata)
-{
- struct clk *clk;
- struct device *dev = &(drvdata->pdev->dev);
- int rc = 0;
-
- clk = devm_clk_get_optional(dev, NULL);
- if (IS_ERR(clk))
- return dev_err_probe(dev, PTR_ERR(clk),
- "Error getting clock\n");
-
- drvdata->clk = clk;
-
- rc = clk_prepare_enable(drvdata->clk);
- if (rc) {
- dev_err(dev, "Failed to enable clock\n");
- return rc;
- }
-
- return 0;
-}
-
-static void cc_trng_clk_fini(struct cctrng_drvdata *drvdata)
-{
- clk_disable_unprepare(drvdata->clk);
-}
-
-
static int cctrng_probe(struct platform_device *pdev)
{
struct cctrng_drvdata *drvdata;
@@ -492,6 +462,10 @@ static int cctrng_probe(struct platform_device *pdev)
u32 val;
int irq;
+ /* Compile time assertion checks */
+ BUILD_BUG_ON(CCTRNG_DATA_BUF_WORDS < 6);
+ BUILD_BUG_ON((CCTRNG_DATA_BUF_WORDS & (CCTRNG_DATA_BUF_WORDS-1)) != 0);
+
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
@@ -510,10 +484,8 @@ static int cctrng_probe(struct platform_device *pdev)
drvdata->circ.buf = (char *)drvdata->data_buf;
drvdata->cc_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(drvdata->cc_base)) {
- dev_err(dev, "Failed to ioremap registers");
- return PTR_ERR(drvdata->cc_base);
- }
+ if (IS_ERR(drvdata->cc_base))
+ return dev_err_probe(dev, PTR_ERR(drvdata->cc_base), "Failed to ioremap registers");
/* Then IRQ */
irq = platform_get_irq(pdev, 0);
@@ -522,16 +494,13 @@ static int cctrng_probe(struct platform_device *pdev)
/* parse sampling rate from device tree */
rc = cc_trng_parse_sampling_ratio(drvdata);
- if (rc) {
- dev_err(dev, "Failed to get legal sampling ratio for rosc\n");
- return rc;
- }
+ if (rc)
+ return dev_err_probe(dev, rc, "Failed to get legal sampling ratio for rosc\n");
- rc = cc_trng_clk_init(drvdata);
- if (rc) {
- dev_err(dev, "cc_trng_clk_init failed\n");
- return rc;
- }
+ drvdata->clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(drvdata->clk))
+ return dev_err_probe(dev, PTR_ERR(drvdata->clk),
+ "Failed to get or enable the clock\n");
INIT_WORK(&drvdata->compwork, cc_trng_compwork_handler);
INIT_WORK(&drvdata->startwork, cc_trng_startwork_handler);
@@ -539,10 +508,8 @@ static int cctrng_probe(struct platform_device *pdev)
/* register the driver isr function */
rc = devm_request_irq(dev, irq, cc_isr, IRQF_SHARED, "cctrng", drvdata);
- if (rc) {
- dev_err(dev, "Could not register to interrupt %d\n", irq);
- goto post_clk_err;
- }
+ if (rc)
+ return dev_err_probe(dev, rc, "Could not register to interrupt %d\n", irq);
dev_dbg(dev, "Registered to IRQ: %d\n", irq);
/* Clear all pending interrupts */
@@ -557,17 +524,13 @@ static int cctrng_probe(struct platform_device *pdev)
/* init PM */
rc = cc_trng_pm_init(drvdata);
- if (rc) {
- dev_err(dev, "cc_trng_pm_init failed\n");
- goto post_clk_err;
- }
+ if (rc)
+ return dev_err_probe(dev, rc, "cc_trng_pm_init failed\n");
/* increment device's usage counter */
rc = cc_trng_pm_get(dev);
- if (rc) {
- dev_err(dev, "cc_trng_pm_get returned %x\n", rc);
- goto post_pm_err;
- }
+ if (rc)
+ return dev_err_probe(dev, rc, "cc_trng_pm_get returned %x\n", rc);
/* set pending_hw to verify that HW won't be triggered from read */
atomic_set(&drvdata->pending_hw, 1);
@@ -593,13 +556,10 @@ static int cctrng_probe(struct platform_device *pdev)
post_pm_err:
cc_trng_pm_fini(drvdata);
-post_clk_err:
- cc_trng_clk_fini(drvdata);
-
return rc;
}
-static int cctrng_remove(struct platform_device *pdev)
+static void cctrng_remove(struct platform_device *pdev)
{
struct cctrng_drvdata *drvdata = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
@@ -608,11 +568,7 @@ static int cctrng_remove(struct platform_device *pdev)
cc_trng_pm_fini(drvdata);
- cc_trng_clk_fini(drvdata);
-
dev_info(dev, "ARM cctrng device terminated\n");
-
- return 0;
}
static int __maybe_unused cctrng_suspend(struct device *dev)
@@ -665,6 +621,7 @@ static int __maybe_unused cctrng_resume(struct device *dev)
/* wait for Cryptocell reset completion */
if (!cctrng_wait_for_reset_completion(drvdata)) {
dev_err(dev, "Cryptocell reset not completed");
+ clk_disable_unprepare(drvdata->clk);
return -EBUSY;
}
@@ -698,21 +655,7 @@ static struct platform_driver cctrng_driver = {
.remove = cctrng_remove,
};
-static int __init cctrng_mod_init(void)
-{
- /* Compile time assertion checks */
- BUILD_BUG_ON(CCTRNG_DATA_BUF_WORDS < 6);
- BUILD_BUG_ON((CCTRNG_DATA_BUF_WORDS & (CCTRNG_DATA_BUF_WORDS-1)) != 0);
-
- return platform_driver_register(&cctrng_driver);
-}
-module_init(cctrng_mod_init);
-
-static void __exit cctrng_mod_exit(void)
-{
- platform_driver_unregister(&cctrng_driver);
-}
-module_exit(cctrng_mod_exit);
+module_platform_driver(cctrng_driver);
/* Module description */
MODULE_DESCRIPTION("ARM CryptoCell TRNG Driver");
diff --git a/drivers/char/hw_random/cn10k-rng.c b/drivers/char/hw_random/cn10k-rng.c
index a01e9307737c..3b4e78182e14 100644
--- a/drivers/char/hw_random/cn10k-rng.c
+++ b/drivers/char/hw_random/cn10k-rng.c
@@ -23,14 +23,49 @@
#define RNM_PF_RANDOM 0x400
#define RNM_TRNG_RESULT 0x408
+/* Extended TRNG Read and Status Registers */
+#define RNM_PF_TRNG_DAT 0x1000
+#define RNM_PF_TRNG_RES 0x1008
+
struct cn10k_rng {
void __iomem *reg_base;
struct hwrng ops;
struct pci_dev *pdev;
+ /* Octeon CN10K-A A0/A1, CNF10K-A A0/A1 and CNF10K-B A0/B0
+ * does not support extended TRNG registers
+ */
+ bool extended_trng_regs;
};
#define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE 0xc2000b0f
+#define PCI_SUBSYS_DEVID_CN10K_A_RNG 0xB900
+#define PCI_SUBSYS_DEVID_CNF10K_A_RNG 0xBA00
+#define PCI_SUBSYS_DEVID_CNF10K_B_RNG 0xBC00
+
+static bool cn10k_is_extended_trng_regs_supported(struct pci_dev *pdev)
+{
+ /* CN10K-A A0/A1 */
+ if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_RNG) &&
+ (!pdev->revision || (pdev->revision & 0xff) == 0x50 ||
+ (pdev->revision & 0xff) == 0x51))
+ return false;
+
+ /* CNF10K-A A0 */
+ if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_RNG) &&
+ (!pdev->revision || (pdev->revision & 0xff) == 0x60 ||
+ (pdev->revision & 0xff) == 0x61))
+ return false;
+
+ /* CNF10K-B A0/B0 */
+ if ((pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_B_RNG) &&
+ (!pdev->revision || (pdev->revision & 0xff) == 0x70 ||
+ (pdev->revision & 0xff) == 0x74))
+ return false;
+
+ return true;
+}
+
static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
{
struct arm_smccc_res res;
@@ -63,9 +98,23 @@ static int check_rng_health(struct cn10k_rng *rng)
return 0;
}
-static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
+/* Returns true when valid data available otherwise return false */
+static bool cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
{
+ u16 retry_count = 0;
u64 upper, lower;
+ u64 status;
+
+ if (rng->extended_trng_regs) {
+ do {
+ *value = readq(rng->reg_base + RNM_PF_TRNG_DAT);
+ if (*value)
+ return true;
+ status = readq(rng->reg_base + RNM_PF_TRNG_RES);
+ if (!status && (retry_count++ > 0x1000))
+ return false;
+ } while (!status);
+ }
*value = readq(rng->reg_base + RNM_PF_RANDOM);
@@ -82,6 +131,7 @@ static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
*value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
}
+ return true;
}
static int cn10k_rng_read(struct hwrng *hwrng, void *data,
@@ -100,7 +150,8 @@ static int cn10k_rng_read(struct hwrng *hwrng, void *data,
size = max;
while (size >= 8) {
- cn10k_read_trng(rng, &value);
+ if (!cn10k_read_trng(rng, &value))
+ goto out;
*((u64 *)pos) = value;
size -= 8;
@@ -108,7 +159,8 @@ static int cn10k_rng_read(struct hwrng *hwrng, void *data,
}
if (size > 0) {
- cn10k_read_trng(rng, &value);
+ if (!cn10k_read_trng(rng, &value))
+ goto out;
while (size > 0) {
*pos = (u8)value;
@@ -118,6 +170,7 @@ static int cn10k_rng_read(struct hwrng *hwrng, void *data,
}
}
+out:
return max - size;
}
@@ -134,36 +187,28 @@ static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, rng);
rng->reg_base = pcim_iomap(pdev, 0, 0);
- if (!rng->reg_base) {
- dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n");
+ if (!rng->reg_base)
return -ENOMEM;
- }
rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"cn10k-rng-%s", dev_name(&pdev->dev));
if (!rng->ops.name)
return -ENOMEM;
- rng->ops.read = cn10k_rng_read;
- rng->ops.quality = 1000;
+ rng->ops.read = cn10k_rng_read;
rng->ops.priv = (unsigned long)rng;
+ rng->extended_trng_regs = cn10k_is_extended_trng_regs_supported(pdev);
+
reset_rng_health_state(rng);
err = devm_hwrng_register(&pdev->dev, &rng->ops);
- if (err) {
- dev_err(&pdev->dev, "Could not register hwrng device.\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(&pdev->dev, err, "Could not register hwrng device.\n");
return 0;
}
-static void cn10k_rng_remove(struct pci_dev *pdev)
-{
- /* Nothing to do */
-}
-
static const struct pci_device_id cn10k_rng_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
{0,},
@@ -175,7 +220,6 @@ static struct pci_driver cn10k_rng_driver = {
.name = "cn10k_rng",
.id_table = cn10k_rng_id_table,
.probe = cn10k_rng_probe,
- .remove = cn10k_rng_remove,
};
module_pci_driver(cn10k_rng_driver);
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 16f227b995e8..96d7fe41b373 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -15,19 +15,21 @@
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/hw_random.h>
-#include <linux/random.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
-#include <linux/sched/signal.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/uaccess.h>
#define RNG_MODULE_NAME "hw_random"
+#define RNG_BUFFER_SIZE (SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES)
+
static struct hwrng *current_rng;
/* the current rng has been explicitly chosen by user via sysfs */
static int cur_rng_set_by_user;
@@ -41,36 +43,25 @@ static DEFINE_MUTEX(reading_mutex);
static int data_avail;
static u8 *rng_buffer, *rng_fillbuf;
static unsigned short current_quality;
-static unsigned short default_quality; /* = 0; default to "off" */
+static unsigned short default_quality = 1024; /* default to maximum */
module_param(current_quality, ushort, 0644);
MODULE_PARM_DESC(current_quality,
"current hwrng entropy estimation per 1024 bits of input -- obsolete, use rng_quality instead");
module_param(default_quality, ushort, 0644);
MODULE_PARM_DESC(default_quality,
- "default entropy content of hwrng per 1024 bits of input");
+ "default maximum entropy content of hwrng per 1024 bits of input");
static void drop_current_rng(void);
static int hwrng_init(struct hwrng *rng);
-static void hwrng_manage_rngd(struct hwrng *rng);
+static int hwrng_fillfn(void *unused);
static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
int wait);
static size_t rng_buffer_size(void)
{
- return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
-}
-
-static void add_early_randomness(struct hwrng *rng)
-{
- int bytes_read;
-
- mutex_lock(&reading_mutex);
- bytes_read = rng_get_data(rng, rng_fillbuf, 32, 0);
- mutex_unlock(&reading_mutex);
- if (bytes_read > 0)
- add_device_randomness(rng_fillbuf, bytes_read);
+ return RNG_BUFFER_SIZE;
}
static inline void cleanup_rng(struct kref *kref)
@@ -96,6 +87,15 @@ static int set_current_rng(struct hwrng *rng)
drop_current_rng();
current_rng = rng;
+ /* if necessary, start hwrng thread */
+ if (!hwrng_fill) {
+ hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng");
+ if (IS_ERR(hwrng_fill)) {
+ pr_err("hwrng_fill thread creation failed\n");
+ hwrng_fill = NULL;
+ }
+ }
+
return 0;
}
@@ -161,14 +161,8 @@ static int hwrng_init(struct hwrng *rng)
reinit_completion(&rng->cleanup_done);
skip_init:
- if (!rng->quality)
- rng->quality = default_quality;
- if (rng->quality > 1024)
- rng->quality = 1024;
current_quality = rng->quality; /* obsolete */
- hwrng_manage_rngd(rng);
-
return 0;
}
@@ -187,8 +181,15 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
int present;
BUG_ON(!mutex_is_locked(&reading_mutex));
- if (rng->read)
- return rng->read(rng, (void *)buffer, size, wait);
+ if (rng->read) {
+ int err;
+
+ err = rng->read(rng, buffer, size, wait);
+ if (WARN_ON_ONCE(err > 0 && err > size))
+ err = size;
+
+ return err;
+ }
if (rng->data_present)
present = rng->data_present(rng, wait);
@@ -204,6 +205,7 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
static ssize_t rng_dev_read(struct file *filp, char __user *buf,
size_t size, loff_t *offp)
{
+ u8 buffer[RNG_BUFFER_SIZE];
ssize_t ret = 0;
int err = 0;
int bytes_read, len;
@@ -231,34 +233,37 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
if (bytes_read < 0) {
err = bytes_read;
goto out_unlock_reading;
+ } else if (bytes_read == 0 &&
+ (filp->f_flags & O_NONBLOCK)) {
+ err = -EAGAIN;
+ goto out_unlock_reading;
}
+
data_avail = bytes_read;
}
- if (!data_avail) {
- if (filp->f_flags & O_NONBLOCK) {
- err = -EAGAIN;
- goto out_unlock_reading;
- }
- } else {
- len = data_avail;
+ len = data_avail;
+ if (len) {
if (len > size)
len = size;
data_avail -= len;
- if (copy_to_user(buf + ret, rng_buffer + data_avail,
- len)) {
+ memcpy(buffer, rng_buffer + data_avail, len);
+ }
+ mutex_unlock(&reading_mutex);
+ put_rng(rng);
+
+ if (len) {
+ if (copy_to_user(buf + ret, buffer, len)) {
err = -EFAULT;
- goto out_unlock_reading;
+ goto out;
}
size -= len;
ret += len;
}
- mutex_unlock(&reading_mutex);
- put_rng(rng);
if (need_resched())
schedule_timeout_interruptible(1);
@@ -269,6 +274,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
}
}
out:
+ memzero_explicit(buffer, sizeof(buffer));
return ret ? : err;
out_unlock_reading:
@@ -327,15 +333,17 @@ static ssize_t rng_current_store(struct device *dev,
const char *buf, size_t len)
{
int err;
- struct hwrng *rng, *old_rng, *new_rng;
+ struct hwrng *rng, *new_rng;
err = mutex_lock_interruptible(&rng_mutex);
if (err)
return -ERESTARTSYS;
- old_rng = current_rng;
if (sysfs_streq(buf, "")) {
err = enable_best_rng();
+ } else if (sysfs_streq(buf, "none")) {
+ cur_rng_set_by_user = 1;
+ drop_current_rng();
} else {
list_for_each_entry(rng, &rng_list, list) {
if (sysfs_streq(rng->name, buf)) {
@@ -349,11 +357,8 @@ static ssize_t rng_current_store(struct device *dev,
new_rng = get_current_rng_nolock();
mutex_unlock(&rng_mutex);
- if (new_rng) {
- if (new_rng != old_rng)
- add_early_randomness(new_rng);
+ if (new_rng)
put_rng(new_rng);
- }
return err ? : len;
}
@@ -369,7 +374,7 @@ static ssize_t rng_current_show(struct device *dev,
if (IS_ERR(rng))
return PTR_ERR(rng);
- ret = snprintf(buf, PAGE_SIZE, "%s\n", rng ? rng->name : "none");
+ ret = sysfs_emit(buf, "%s\n", rng ? rng->name : "none");
put_rng(rng);
return ret;
@@ -390,7 +395,7 @@ static ssize_t rng_available_show(struct device *dev,
strlcat(buf, rng->name, PAGE_SIZE);
strlcat(buf, " ", PAGE_SIZE);
}
- strlcat(buf, "\n", PAGE_SIZE);
+ strlcat(buf, "none\n", PAGE_SIZE);
mutex_unlock(&rng_mutex);
return strlen(buf);
@@ -454,10 +459,6 @@ static ssize_t rng_quality_store(struct device *dev,
/* the best available RNG may have changed */
ret = enable_best_rng();
- /* start/stop rngd if necessary */
- if (current_rng)
- hwrng_manage_rngd(current_rng);
-
out:
mutex_unlock(&rng_mutex);
return ret ? ret : len;
@@ -478,16 +479,6 @@ static struct attribute *rng_dev_attrs[] = {
ATTRIBUTE_GROUPS(rng_dev);
-static void __exit unregister_miscdev(void)
-{
- misc_deregister(&rng_miscdev);
-}
-
-static int __init register_miscdev(void)
-{
- return misc_register(&rng_miscdev);
-}
-
static int hwrng_fillfn(void *unused)
{
size_t entropy, entropy_credit = 0; /* in 1/1024 of a bit */
@@ -507,16 +498,14 @@ static int hwrng_fillfn(void *unused)
rng->quality = current_quality; /* obsolete */
quality = rng->quality;
mutex_unlock(&reading_mutex);
- put_rng(rng);
- if (!quality)
- break;
+ if (rc <= 0)
+ hwrng_msleep(rng, 10000);
- if (rc <= 0) {
- pr_warn("hwrng: no data available\n");
- msleep_interruptible(10000);
+ put_rng(rng);
+
+ if (rc <= 0)
continue;
- }
/* If we cannot credit at least one bit of entropy,
* keep track of the remainder for the next iteration
@@ -527,33 +516,16 @@ static int hwrng_fillfn(void *unused)
/* Outside lock, sure, but y'know: randomness. */
add_hwgenerator_randomness((void *)rng_fillbuf, rc,
- entropy >> 10);
+ entropy >> 10, true);
}
hwrng_fill = NULL;
return 0;
}
-static void hwrng_manage_rngd(struct hwrng *rng)
-{
- if (WARN_ON(!mutex_is_locked(&rng_mutex)))
- return;
-
- if (rng->quality == 0 && hwrng_fill)
- kthread_stop(hwrng_fill);
- if (rng->quality > 0 && !hwrng_fill) {
- hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng");
- if (IS_ERR(hwrng_fill)) {
- pr_err("hwrng_fill thread creation failed\n");
- hwrng_fill = NULL;
- }
- }
-}
-
int hwrng_register(struct hwrng *rng)
{
int err = -EINVAL;
struct hwrng *tmp;
- bool is_new_current = false;
if (!rng->name || (!rng->data_read && !rng->read))
goto out;
@@ -570,9 +542,13 @@ int hwrng_register(struct hwrng *rng)
init_completion(&rng->cleanup_done);
complete(&rng->cleanup_done);
+ init_completion(&rng->dying);
+
+ /* Adjust quality field to always have a proper value */
+ rng->quality = min3(default_quality, 1024, rng->quality ?: 1024);
- if (!current_rng ||
- (!cur_rng_set_by_user && rng->quality > current_rng->quality)) {
+ if (!cur_rng_set_by_user &&
+ (!current_rng || rng->quality > current_rng->quality)) {
/*
* Set new rng as current as the new rng source
* provides better entropy quality and was not
@@ -581,25 +557,8 @@ int hwrng_register(struct hwrng *rng)
err = set_current_rng(rng);
if (err)
goto out_unlock;
- /* to use current_rng in add_early_randomness() we need
- * to take a ref
- */
- is_new_current = true;
- kref_get(&rng->ref);
}
mutex_unlock(&rng_mutex);
- if (is_new_current || !rng->init) {
- /*
- * Use a new device's input to add some randomness to
- * the system. If this rng device isn't going to be
- * used right away, its init function hasn't been
- * called yet by set_current_rng(); so only use the
- * randomness from devices that don't need an init callback
- */
- add_early_randomness(rng);
- }
- if (is_new_current)
- put_rng(rng);
return 0;
out_unlock:
mutex_unlock(&rng_mutex);
@@ -610,13 +569,13 @@ EXPORT_SYMBOL_GPL(hwrng_register);
void hwrng_unregister(struct hwrng *rng)
{
- struct hwrng *old_rng, *new_rng;
+ struct hwrng *new_rng;
int err;
mutex_lock(&rng_mutex);
- old_rng = current_rng;
list_del(&rng->list);
+ complete_all(&rng->dying);
if (current_rng == rng) {
err = enable_best_rng();
if (err) {
@@ -633,11 +592,8 @@ void hwrng_unregister(struct hwrng *rng)
} else
mutex_unlock(&rng_mutex);
- if (new_rng) {
- if (old_rng != new_rng)
- add_early_randomness(new_rng);
+ if (new_rng)
put_rng(new_rng);
- }
wait_for_completion(&rng->cleanup_done);
}
@@ -685,6 +641,20 @@ void devm_hwrng_unregister(struct device *dev, struct hwrng *rng)
}
EXPORT_SYMBOL_GPL(devm_hwrng_unregister);
+long hwrng_msleep(struct hwrng *rng, unsigned int msecs)
+{
+ unsigned long timeout = msecs_to_jiffies(msecs) + 1;
+
+ return wait_for_completion_interruptible_timeout(&rng->dying, timeout);
+}
+EXPORT_SYMBOL_GPL(hwrng_msleep);
+
+long hwrng_yield(struct hwrng *rng)
+{
+ return wait_for_completion_interruptible_timeout(&rng->dying, 1);
+}
+EXPORT_SYMBOL_GPL(hwrng_yield);
+
static int __init hwrng_modinit(void)
{
int ret;
@@ -700,7 +670,7 @@ static int __init hwrng_modinit(void)
return -ENOMEM;
}
- ret = register_miscdev();
+ ret = misc_register(&rng_miscdev);
if (ret) {
kfree(rng_fillbuf);
kfree(rng_buffer);
@@ -717,7 +687,7 @@ static void __exit hwrng_modexit(void)
kfree(rng_fillbuf);
mutex_unlock(&rng_mutex);
- unregister_miscdev();
+ misc_deregister(&rng_miscdev);
}
fs_initcall(hwrng_modinit); /* depends on misc_register() */
diff --git a/drivers/char/hw_random/exynos-trng.c b/drivers/char/hw_random/exynos-trng.c
index 9cc3d542dd0f..02e207c09e81 100644
--- a/drivers/char/hw_random/exynos-trng.c
+++ b/drivers/char/hw_random/exynos-trng.c
@@ -10,6 +10,7 @@
* Krzysztof Kozłowski <krzk@kernel.org>
*/
+#include <linux/arm-smccc.h>
#include <linux/clk.h>
#include <linux/crypto.h>
#include <linux/delay.h>
@@ -22,46 +23,69 @@
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-
-#define EXYNOS_TRNG_CLKDIV (0x0)
-
-#define EXYNOS_TRNG_CTRL (0x20)
-#define EXYNOS_TRNG_CTRL_RNGEN BIT(31)
-
-#define EXYNOS_TRNG_POST_CTRL (0x30)
-#define EXYNOS_TRNG_ONLINE_CTRL (0x40)
-#define EXYNOS_TRNG_ONLINE_STAT (0x44)
-#define EXYNOS_TRNG_ONLINE_MAXCHI2 (0x48)
-#define EXYNOS_TRNG_FIFO_CTRL (0x50)
-#define EXYNOS_TRNG_FIFO_0 (0x80)
-#define EXYNOS_TRNG_FIFO_1 (0x84)
-#define EXYNOS_TRNG_FIFO_2 (0x88)
-#define EXYNOS_TRNG_FIFO_3 (0x8c)
-#define EXYNOS_TRNG_FIFO_4 (0x90)
-#define EXYNOS_TRNG_FIFO_5 (0x94)
-#define EXYNOS_TRNG_FIFO_6 (0x98)
-#define EXYNOS_TRNG_FIFO_7 (0x9c)
-#define EXYNOS_TRNG_FIFO_LEN (8)
-#define EXYNOS_TRNG_CLOCK_RATE (500000)
-
+#include <linux/property.h>
+
+#define EXYNOS_TRNG_CLKDIV 0x0
+
+#define EXYNOS_TRNG_CTRL 0x20
+#define EXYNOS_TRNG_CTRL_RNGEN BIT(31)
+
+#define EXYNOS_TRNG_POST_CTRL 0x30
+#define EXYNOS_TRNG_ONLINE_CTRL 0x40
+#define EXYNOS_TRNG_ONLINE_STAT 0x44
+#define EXYNOS_TRNG_ONLINE_MAXCHI2 0x48
+#define EXYNOS_TRNG_FIFO_CTRL 0x50
+#define EXYNOS_TRNG_FIFO_0 0x80
+#define EXYNOS_TRNG_FIFO_1 0x84
+#define EXYNOS_TRNG_FIFO_2 0x88
+#define EXYNOS_TRNG_FIFO_3 0x8c
+#define EXYNOS_TRNG_FIFO_4 0x90
+#define EXYNOS_TRNG_FIFO_5 0x94
+#define EXYNOS_TRNG_FIFO_6 0x98
+#define EXYNOS_TRNG_FIFO_7 0x9c
+#define EXYNOS_TRNG_FIFO_LEN 8
+#define EXYNOS_TRNG_CLOCK_RATE 500000
+
+/* Driver feature flags */
+#define EXYNOS_SMC BIT(0)
+
+#define EXYNOS_SMC_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_SIP, \
+ func_num)
+
+/* SMC command for DTRNG access */
+#define SMC_CMD_RANDOM EXYNOS_SMC_CALL_VAL(0x1012)
+
+/* SMC_CMD_RANDOM: arguments */
+#define HWRNG_INIT 0x0
+#define HWRNG_EXIT 0x1
+#define HWRNG_GET_DATA 0x2
+#define HWRNG_RESUME 0x3
+
+/* SMC_CMD_RANDOM: return values */
+#define HWRNG_RET_OK 0x0
+#define HWRNG_RET_RETRY_ERROR 0x2
+
+#define HWRNG_MAX_TRIES 100
struct exynos_trng_dev {
- struct device *dev;
- void __iomem *mem;
- struct clk *clk;
- struct hwrng rng;
+ struct device *dev;
+ void __iomem *mem;
+ struct clk *clk; /* operating clock */
+ struct clk *pclk; /* bus clock */
+ struct hwrng rng;
+ unsigned long flags;
};
-static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
- bool wait)
+static int exynos_trng_do_read_reg(struct hwrng *rng, void *data, size_t max,
+ bool wait)
{
- struct exynos_trng_dev *trng;
+ struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
int val;
max = min_t(size_t, max, (EXYNOS_TRNG_FIFO_LEN * 4));
-
- trng = (struct exynos_trng_dev *)rng->priv;
-
writel_relaxed(max * 8, trng->mem + EXYNOS_TRNG_FIFO_CTRL);
val = readl_poll_timeout(trng->mem + EXYNOS_TRNG_FIFO_CTRL, val,
val == 0, 200, 1000000);
@@ -73,7 +97,40 @@ static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
return max;
}
-static int exynos_trng_init(struct hwrng *rng)
+static int exynos_trng_do_read_smc(struct hwrng *rng, void *data, size_t max,
+ bool wait)
+{
+ struct arm_smccc_res res;
+ unsigned int copied = 0;
+ u32 *buf = data;
+ int tries = 0;
+
+ while (copied < max) {
+ arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_GET_DATA, 0, 0, 0, 0, 0, 0,
+ &res);
+ switch (res.a0) {
+ case HWRNG_RET_OK:
+ *buf++ = res.a2;
+ *buf++ = res.a3;
+ copied += 8;
+ tries = 0;
+ break;
+ case HWRNG_RET_RETRY_ERROR:
+ if (!wait)
+ return copied;
+ if (++tries >= HWRNG_MAX_TRIES)
+ return copied;
+ cond_resched();
+ break;
+ default:
+ return -EIO;
+ }
+ }
+
+ return copied;
+}
+
+static int exynos_trng_init_reg(struct hwrng *rng)
{
struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
unsigned long sss_rate;
@@ -87,7 +144,7 @@ static int exynos_trng_init(struct hwrng *rng)
*/
val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
if (val > 0x7fff) {
- dev_err(trng->dev, "clock divider too large: %d", val);
+ dev_err(trng->dev, "clock divider too large: %d\n", val);
return -ERANGE;
}
val = val << 1;
@@ -106,6 +163,24 @@ static int exynos_trng_init(struct hwrng *rng)
return 0;
}
+static int exynos_trng_init_smc(struct hwrng *rng)
+{
+ struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
+ struct arm_smccc_res res;
+ int ret = 0;
+
+ arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 != HWRNG_RET_OK) {
+ dev_err(trng->dev, "SMC command for TRNG init failed (%d)\n",
+ (int)res.a0);
+ ret = -EIO;
+ }
+ if ((int)res.a0 == -1)
+ dev_info(trng->dev, "Make sure LDFW is loaded by your BL\n");
+
+ return ret;
+}
+
static int exynos_trng_probe(struct platform_device *pdev)
{
struct exynos_trng_dev *trng;
@@ -115,21 +190,29 @@ static int exynos_trng_probe(struct platform_device *pdev)
if (!trng)
return ret;
+ platform_set_drvdata(pdev, trng);
+ trng->dev = &pdev->dev;
+
+ trng->flags = (unsigned long)device_get_match_data(&pdev->dev);
+
trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev),
GFP_KERNEL);
if (!trng->rng.name)
return ret;
- trng->rng.init = exynos_trng_init;
- trng->rng.read = exynos_trng_do_read;
- trng->rng.priv = (unsigned long) trng;
+ trng->rng.priv = (unsigned long)trng;
- platform_set_drvdata(pdev, trng);
- trng->dev = &pdev->dev;
+ if (trng->flags & EXYNOS_SMC) {
+ trng->rng.init = exynos_trng_init_smc;
+ trng->rng.read = exynos_trng_do_read_smc;
+ } else {
+ trng->rng.init = exynos_trng_init_reg;
+ trng->rng.read = exynos_trng_do_read_reg;
- trng->mem = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(trng->mem))
- return PTR_ERR(trng->mem);
+ trng->mem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(trng->mem))
+ return PTR_ERR(trng->mem);
+ }
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
@@ -138,32 +221,30 @@ static int exynos_trng_probe(struct platform_device *pdev)
goto err_pm_get;
}
- trng->clk = devm_clk_get(&pdev->dev, "secss");
+ trng->clk = devm_clk_get_enabled(&pdev->dev, "secss");
if (IS_ERR(trng->clk)) {
- ret = PTR_ERR(trng->clk);
- dev_err(&pdev->dev, "Could not get clock.\n");
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(trng->clk),
+ "Could not get clock\n");
goto err_clock;
}
- ret = clk_prepare_enable(trng->clk);
- if (ret) {
- dev_err(&pdev->dev, "Could not enable the clk.\n");
+ trng->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(trng->pclk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(trng->pclk),
+ "Could not get pclk\n");
goto err_clock;
}
ret = devm_hwrng_register(&pdev->dev, &trng->rng);
if (ret) {
dev_err(&pdev->dev, "Could not register hwrng device.\n");
- goto err_register;
+ goto err_clock;
}
dev_info(&pdev->dev, "Exynos True Random Number Generator.\n");
return 0;
-err_register:
- clk_disable_unprepare(trng->clk);
-
err_clock:
pm_runtime_put_noidle(&pdev->dev);
@@ -173,27 +254,41 @@ err_pm_get:
return ret;
}
-static int exynos_trng_remove(struct platform_device *pdev)
+static void exynos_trng_remove(struct platform_device *pdev)
{
- struct exynos_trng_dev *trng = platform_get_drvdata(pdev);
+ struct exynos_trng_dev *trng = platform_get_drvdata(pdev);
+
+ if (trng->flags & EXYNOS_SMC) {
+ struct arm_smccc_res res;
- clk_disable_unprepare(trng->clk);
+ arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0,
+ &res);
+ }
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
-static int __maybe_unused exynos_trng_suspend(struct device *dev)
+static int exynos_trng_suspend(struct device *dev)
{
+ struct exynos_trng_dev *trng = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
+
+ if (trng->flags & EXYNOS_SMC) {
+ arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0 != HWRNG_RET_OK)
+ return -EIO;
+ }
+
pm_runtime_put_sync(dev);
return 0;
}
-static int __maybe_unused exynos_trng_resume(struct device *dev)
+static int exynos_trng_resume(struct device *dev)
{
+ struct exynos_trng_dev *trng = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_resume_and_get(dev);
@@ -202,15 +297,32 @@ static int __maybe_unused exynos_trng_resume(struct device *dev)
return ret;
}
+ if (trng->flags & EXYNOS_SMC) {
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_RESUME, 0, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0 != HWRNG_RET_OK)
+ return -EIO;
+
+ arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0 != HWRNG_RET_OK)
+ return -EIO;
+ }
+
return 0;
}
-static SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend,
- exynos_trng_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(exynos_trng_pm_ops, exynos_trng_suspend,
+ exynos_trng_resume);
static const struct of_device_id exynos_trng_dt_match[] = {
{
.compatible = "samsung,exynos5250-trng",
+ }, {
+ .compatible = "samsung,exynos850-trng",
+ .data = (void *)EXYNOS_SMC,
},
{ },
};
@@ -219,7 +331,7 @@ MODULE_DEVICE_TABLE(of, exynos_trng_dt_match);
static struct platform_driver exynos_trng_driver = {
.driver = {
.name = "exynos-trng",
- .pm = &exynos_trng_pm_ops,
+ .pm = pm_sleep_ptr(&exynos_trng_pm_ops),
.of_match_table = exynos_trng_dt_match,
},
.probe = exynos_trng_probe,
@@ -227,6 +339,7 @@ static struct platform_driver exynos_trng_driver = {
};
module_platform_driver(exynos_trng_driver);
+
MODULE_AUTHOR("Łukasz Stelmach");
MODULE_DESCRIPTION("H/W TRNG driver for Exynos chips");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c
index 138ce434f86b..159baf00a867 100644
--- a/drivers/char/hw_random/geode-rng.c
+++ b/drivers/char/hw_random/geode-rng.c
@@ -51,10 +51,15 @@ static const struct pci_device_id pci_tbl[] = {
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
+struct amd_geode_priv {
+ struct pci_dev *pcidev;
+ void __iomem *membase;
+};
static int geode_rng_data_read(struct hwrng *rng, u32 *data)
{
- void __iomem *mem = (void __iomem *)rng->priv;
+ struct amd_geode_priv *priv = (struct amd_geode_priv *)rng->priv;
+ void __iomem *mem = priv->membase;
*data = readl(mem + GEODE_RNG_DATA_REG);
@@ -63,7 +68,8 @@ static int geode_rng_data_read(struct hwrng *rng, u32 *data)
static int geode_rng_data_present(struct hwrng *rng, int wait)
{
- void __iomem *mem = (void __iomem *)rng->priv;
+ struct amd_geode_priv *priv = (struct amd_geode_priv *)rng->priv;
+ void __iomem *mem = priv->membase;
int data, i;
for (i = 0; i < 20; i++) {
@@ -90,6 +96,7 @@ static int __init geode_rng_init(void)
const struct pci_device_id *ent;
void __iomem *mem;
unsigned long rng_base;
+ struct amd_geode_priv *priv;
for_each_pci_dev(pdev) {
ent = pci_match_id(pci_tbl, pdev);
@@ -97,17 +104,26 @@ static int __init geode_rng_init(void)
goto found;
}
/* Device not found. */
- goto out;
+ return err;
found:
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ err = -ENOMEM;
+ goto put_dev;
+ }
+
rng_base = pci_resource_start(pdev, 0);
if (rng_base == 0)
- goto out;
+ goto free_priv;
err = -ENOMEM;
mem = ioremap(rng_base, 0x58);
if (!mem)
- goto out;
- geode_rng.priv = (unsigned long)mem;
+ goto free_priv;
+
+ geode_rng.priv = (unsigned long)priv;
+ priv->membase = mem;
+ priv->pcidev = pdev;
pr_info("AMD Geode RNG detected\n");
err = hwrng_register(&geode_rng);
@@ -116,20 +132,26 @@ found:
err);
goto err_unmap;
}
-out:
return err;
err_unmap:
iounmap(mem);
- goto out;
+free_priv:
+ kfree(priv);
+put_dev:
+ pci_dev_put(pdev);
+ return err;
}
static void __exit geode_rng_exit(void)
{
- void __iomem *mem = (void __iomem *)geode_rng.priv;
+ struct amd_geode_priv *priv;
+ priv = (struct amd_geode_priv *)geode_rng.priv;
hwrng_unregister(&geode_rng);
- iounmap(mem);
+ iounmap(priv->membase);
+ pci_dev_put(priv->pcidev);
+ kfree(priv);
}
module_init(geode_rng_init);
diff --git a/drivers/char/hw_random/hisi-rng.c b/drivers/char/hw_random/hisi-rng.c
index 96438f85cafa..4e501d5c121f 100644
--- a/drivers/char/hw_random/hisi-rng.c
+++ b/drivers/char/hw_random/hisi-rng.c
@@ -79,8 +79,6 @@ static int hisi_rng_probe(struct platform_device *pdev)
if (!rng)
return -ENOMEM;
- platform_set_drvdata(pdev, rng);
-
rng->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rng->base))
return PTR_ERR(rng->base);
@@ -91,10 +89,8 @@ static int hisi_rng_probe(struct platform_device *pdev)
rng->rng.read = hisi_rng_read;
ret = devm_hwrng_register(&pdev->dev, &rng->rng);
- if (ret) {
- dev_err(&pdev->dev, "failed to register hwrng\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to register hwrng\n");
return 0;
}
diff --git a/drivers/char/hw_random/histb-rng.c b/drivers/char/hw_random/histb-rng.c
new file mode 100644
index 000000000000..1b91e88cc4c0
--- /dev/null
+++ b/drivers/char/hw_random/histb-rng.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * Copyright (c) 2023 David Yang
+ */
+
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define RNG_CTRL 0x0
+#define RNG_SOURCE GENMASK(1, 0)
+#define DROP_ENABLE BIT(5)
+#define POST_PROCESS_ENABLE BIT(7)
+#define POST_PROCESS_DEPTH GENMASK(15, 8)
+#define RNG_NUMBER 0x4
+#define RNG_STAT 0x8
+#define DATA_COUNT GENMASK(2, 0) /* max 4 */
+
+struct histb_rng_priv {
+ struct hwrng rng;
+ void __iomem *base;
+};
+
+/*
+ * Observed:
+ * depth = 1 -> ~1ms
+ * depth = 255 -> ~16ms
+ */
+static int histb_rng_wait(void __iomem *base)
+{
+ u32 val;
+
+ return readl_relaxed_poll_timeout(base + RNG_STAT, val,
+ val & DATA_COUNT, 1000, 30 * 1000);
+}
+
+static void histb_rng_init(void __iomem *base, unsigned int depth)
+{
+ u32 val;
+
+ val = readl_relaxed(base + RNG_CTRL);
+
+ val &= ~RNG_SOURCE;
+ val |= 2;
+
+ val &= ~POST_PROCESS_DEPTH;
+ val |= min(depth, 0xffu) << 8;
+
+ val |= POST_PROCESS_ENABLE;
+ val |= DROP_ENABLE;
+
+ writel_relaxed(val, base + RNG_CTRL);
+}
+
+static int histb_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ struct histb_rng_priv *priv = container_of(rng, typeof(*priv), rng);
+ void __iomem *base = priv->base;
+
+ for (int i = 0; i < max; i += sizeof(u32)) {
+ if (!(readl_relaxed(base + RNG_STAT) & DATA_COUNT)) {
+ if (!wait)
+ return i;
+ if (histb_rng_wait(base)) {
+ pr_err("failed to generate random number, generated %d\n",
+ i);
+ return i ? i : -ETIMEDOUT;
+ }
+ }
+ *(u32 *) (data + i) = readl_relaxed(base + RNG_NUMBER);
+ }
+
+ return max;
+}
+
+static unsigned int histb_rng_get_depth(void __iomem *base)
+{
+ return (readl_relaxed(base + RNG_CTRL) & POST_PROCESS_DEPTH) >> 8;
+}
+
+static ssize_t
+depth_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct histb_rng_priv *priv = dev_get_drvdata(dev);
+ void __iomem *base = priv->base;
+
+ return sprintf(buf, "%u\n", histb_rng_get_depth(base));
+}
+
+static ssize_t
+depth_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct histb_rng_priv *priv = dev_get_drvdata(dev);
+ void __iomem *base = priv->base;
+ unsigned int depth;
+
+ if (kstrtouint(buf, 0, &depth))
+ return -ERANGE;
+
+ histb_rng_init(base, depth);
+ return count;
+}
+
+static DEVICE_ATTR_RW(depth);
+
+static struct attribute *histb_rng_attrs[] = {
+ &dev_attr_depth.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(histb_rng);
+
+static int histb_rng_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct histb_rng_priv *priv;
+ void __iomem *base;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ histb_rng_init(base, 144);
+ if (histb_rng_wait(base)) {
+ dev_err(dev, "cannot bring up device\n");
+ return -ENODEV;
+ }
+
+ priv->base = base;
+ priv->rng.name = pdev->name;
+ priv->rng.read = histb_rng_read;
+ ret = devm_hwrng_register(dev, &priv->rng);
+ if (ret) {
+ dev_err(dev, "failed to register hwrng: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ dev_set_drvdata(dev, priv);
+ return 0;
+}
+
+static const struct of_device_id histb_rng_of_match[] = {
+ { .compatible = "hisilicon,histb-rng", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, histb_rng_of_match);
+
+static struct platform_driver histb_rng_driver = {
+ .probe = histb_rng_probe,
+ .driver = {
+ .name = "histb-rng",
+ .of_match_table = histb_rng_of_match,
+ .dev_groups = histb_rng_groups,
+ },
+};
+
+module_platform_driver(histb_rng_driver);
+
+MODULE_DESCRIPTION("Hisilicon STB random number generator driver");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c
index b05d676ca814..241664a9b5d9 100644
--- a/drivers/char/hw_random/imx-rngc.c
+++ b/drivers/char/hw_random/imx-rngc.c
@@ -13,10 +13,13 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/hw_random.h>
#include <linux/completion.h>
#include <linux/io.h>
+#include <linux/bitfield.h>
#define RNGC_VER_ID 0x0000
#define RNGC_COMMAND 0x0004
@@ -26,7 +29,7 @@
#define RNGC_FIFO 0x0014
/* the fields in the ver id register */
-#define RNGC_TYPE_SHIFT 28
+#define RNG_TYPE GENMASK(31, 28)
#define RNGC_VER_MAJ_SHIFT 8
/* the rng_type field */
@@ -34,25 +37,25 @@
#define RNGC_TYPE_RNGC 0x2
-#define RNGC_CMD_CLR_ERR 0x00000020
-#define RNGC_CMD_CLR_INT 0x00000010
-#define RNGC_CMD_SEED 0x00000002
-#define RNGC_CMD_SELF_TEST 0x00000001
+#define RNGC_CMD_CLR_ERR BIT(5)
+#define RNGC_CMD_CLR_INT BIT(4)
+#define RNGC_CMD_SEED BIT(1)
+#define RNGC_CMD_SELF_TEST BIT(0)
-#define RNGC_CTRL_MASK_ERROR 0x00000040
-#define RNGC_CTRL_MASK_DONE 0x00000020
-#define RNGC_CTRL_AUTO_SEED 0x00000010
+#define RNGC_CTRL_MASK_ERROR BIT(6)
+#define RNGC_CTRL_MASK_DONE BIT(5)
+#define RNGC_CTRL_AUTO_SEED BIT(4)
-#define RNGC_STATUS_ERROR 0x00010000
-#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00
-#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
-#define RNGC_STATUS_SEED_DONE 0x00000020
-#define RNGC_STATUS_ST_DONE 0x00000010
+#define RNGC_STATUS_ERROR BIT(16)
+#define RNGC_STATUS_FIFO_LEVEL_MASK GENMASK(11, 8)
+#define RNGC_STATUS_SEED_DONE BIT(5)
+#define RNGC_STATUS_ST_DONE BIT(4)
#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008
-#define RNGC_TIMEOUT 3000 /* 3 sec */
-
+#define RNGC_SELFTEST_TIMEOUT 2500 /* us */
+#define RNGC_SEED_TIMEOUT 200 /* ms */
+#define RNGC_PM_TIMEOUT 500 /* ms */
static bool self_test = true;
module_param(self_test, bool, 0);
@@ -110,7 +113,8 @@ static int imx_rngc_self_test(struct imx_rngc *rngc)
cmd = readl(rngc->base + RNGC_COMMAND);
writel(cmd | RNGC_CMD_SELF_TEST, rngc->base + RNGC_COMMAND);
- ret = wait_for_completion_timeout(&rngc->rng_op_done, RNGC_TIMEOUT);
+ ret = wait_for_completion_timeout(&rngc->rng_op_done,
+ usecs_to_jiffies(RNGC_SELFTEST_TIMEOUT));
imx_rngc_irq_mask_clear(rngc);
if (!ret)
return -ETIMEDOUT;
@@ -122,8 +126,11 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng);
unsigned int status;
- unsigned int level;
- int retval = 0;
+ int err, retval = 0;
+
+ err = pm_runtime_resume_and_get(rngc->dev);
+ if (err)
+ return err;
while (max >= sizeof(u32)) {
status = readl(rngc->base + RNGC_STATUS);
@@ -132,11 +139,7 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait)
if (status & RNGC_STATUS_ERROR)
break;
- /* how many random numbers are in FIFO? [0-16] */
- level = (status & RNGC_STATUS_FIFO_LEVEL_MASK) >>
- RNGC_STATUS_FIFO_LEVEL_SHIFT;
-
- if (level) {
+ if (status & RNGC_STATUS_FIFO_LEVEL_MASK) {
/* retrieve a random number from FIFO */
*(u32 *)data = readl(rngc->base + RNGC_FIFO);
@@ -145,6 +148,8 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait)
max -= sizeof(u32);
}
}
+ pm_runtime_mark_last_busy(rngc->dev);
+ pm_runtime_put(rngc->dev);
return retval ? retval : -EIO;
}
@@ -173,7 +178,11 @@ static int imx_rngc_init(struct hwrng *rng)
{
struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng);
u32 cmd, ctrl;
- int ret;
+ int ret, err;
+
+ err = pm_runtime_resume_and_get(rngc->dev);
+ if (err)
+ return err;
/* clear error */
cmd = readl(rngc->base + RNGC_COMMAND);
@@ -188,18 +197,17 @@ static int imx_rngc_init(struct hwrng *rng)
writel(cmd | RNGC_CMD_SEED, rngc->base + RNGC_COMMAND);
ret = wait_for_completion_timeout(&rngc->rng_op_done,
- RNGC_TIMEOUT);
-
+ msecs_to_jiffies(RNGC_SEED_TIMEOUT));
if (!ret) {
- ret = -ETIMEDOUT;
- goto err;
+ err = -ETIMEDOUT;
+ goto out;
}
} while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR);
if (rngc->err_reg) {
- ret = -EIO;
- goto err;
+ err = -EIO;
+ goto out;
}
/*
@@ -210,26 +218,32 @@ static int imx_rngc_init(struct hwrng *rng)
ctrl |= RNGC_CTRL_AUTO_SEED;
writel(ctrl, rngc->base + RNGC_CONTROL);
+out:
/*
* if initialisation was successful, we keep the interrupt
* unmasked until imx_rngc_cleanup is called
* we mask the interrupt ourselves if we return an error
*/
- return 0;
+ if (err)
+ imx_rngc_irq_mask_clear(rngc);
-err:
- imx_rngc_irq_mask_clear(rngc);
- return ret;
+ pm_runtime_put(rngc->dev);
+ return err;
}
static void imx_rngc_cleanup(struct hwrng *rng)
{
struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng);
+ int err;
- imx_rngc_irq_mask_clear(rngc);
+ err = pm_runtime_resume_and_get(rngc->dev);
+ if (!err) {
+ imx_rngc_irq_mask_clear(rngc);
+ pm_runtime_put(rngc->dev);
+ }
}
-static int imx_rngc_probe(struct platform_device *pdev)
+static int __init imx_rngc_probe(struct platform_device *pdev)
{
struct imx_rngc *rngc;
int ret;
@@ -246,35 +260,24 @@ static int imx_rngc_probe(struct platform_device *pdev)
return PTR_ERR(rngc->base);
rngc->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(rngc->clk)) {
- dev_err(&pdev->dev, "Can not get rng_clk\n");
- return PTR_ERR(rngc->clk);
- }
+ if (IS_ERR(rngc->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rngc->clk), "Cannot get rng_clk\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- ret = clk_prepare_enable(rngc->clk);
- if (ret)
- return ret;
+ clk_prepare_enable(rngc->clk);
ver_id = readl(rngc->base + RNGC_VER_ID);
- rng_type = ver_id >> RNGC_TYPE_SHIFT;
+ rng_type = FIELD_GET(RNG_TYPE, ver_id);
/*
* This driver supports only RNGC and RNGB. (There's a different
* driver for RNGA.)
*/
if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) {
- ret = -ENODEV;
- goto err;
- }
-
- ret = devm_request_irq(&pdev->dev,
- irq, imx_rngc_irq, 0, pdev->name, (void *)rngc);
- if (ret) {
- dev_err(rngc->dev, "Can't get interrupt working.\n");
- goto err;
+ clk_disable_unprepare(rngc->clk);
+ return -ENODEV;
}
init_completion(&rngc->rng_op_done);
@@ -290,44 +293,38 @@ static int imx_rngc_probe(struct platform_device *pdev)
imx_rngc_irq_mask_clear(rngc);
+ ret = devm_request_irq(&pdev->dev,
+ irq, imx_rngc_irq, 0, pdev->name, (void *)rngc);
+ if (ret) {
+ clk_disable_unprepare(rngc->clk);
+ return dev_err_probe(&pdev->dev, ret, "Can't get interrupt working.\n");
+ }
+
if (self_test) {
ret = imx_rngc_self_test(rngc);
if (ret) {
- dev_err(rngc->dev, "self test failed\n");
- goto err;
+ clk_disable_unprepare(rngc->clk);
+ return dev_err_probe(&pdev->dev, ret, "self test failed\n");
}
}
- ret = hwrng_register(&rngc->rng);
- if (ret) {
- dev_err(&pdev->dev, "hwrng registration failed\n");
- goto err;
- }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, RNGC_PM_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ devm_pm_runtime_enable(&pdev->dev);
+
+ ret = devm_hwrng_register(&pdev->dev, &rngc->rng);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "hwrng registration failed\n");
dev_info(&pdev->dev,
"Freescale RNG%c registered (HW revision %d.%02d)\n",
rng_type == RNGC_TYPE_RNGB ? 'B' : 'C',
(ver_id >> RNGC_VER_MAJ_SHIFT) & 0xff, ver_id & 0xff);
return 0;
-
-err:
- clk_disable_unprepare(rngc->clk);
-
- return ret;
}
-static int __exit imx_rngc_remove(struct platform_device *pdev)
-{
- struct imx_rngc *rngc = platform_get_drvdata(pdev);
-
- hwrng_unregister(&rngc->rng);
-
- clk_disable_unprepare(rngc->clk);
-
- return 0;
-}
-
-static int __maybe_unused imx_rngc_suspend(struct device *dev)
+static int imx_rngc_suspend(struct device *dev)
{
struct imx_rngc *rngc = dev_get_drvdata(dev);
@@ -336,7 +333,7 @@ static int __maybe_unused imx_rngc_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused imx_rngc_resume(struct device *dev)
+static int imx_rngc_resume(struct device *dev)
{
struct imx_rngc *rngc = dev_get_drvdata(dev);
@@ -345,21 +342,23 @@ static int __maybe_unused imx_rngc_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume);
+static const struct dev_pm_ops imx_rngc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(imx_rngc_suspend, imx_rngc_resume, NULL)
+};
static const struct of_device_id imx_rngc_dt_ids[] = {
- { .compatible = "fsl,imx25-rngb", .data = NULL, },
+ { .compatible = "fsl,imx25-rngb" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_rngc_dt_ids);
static struct platform_driver imx_rngc_driver = {
.driver = {
- .name = "imx_rngc",
- .pm = &imx_rngc_pm_ops,
+ .name = KBUILD_MODNAME,
+ .pm = pm_ptr(&imx_rngc_pm_ops),
.of_match_table = imx_rngc_dt_ids,
},
- .remove = __exit_p(imx_rngc_remove),
};
module_platform_driver_probe(imx_rngc_driver, imx_rngc_probe);
diff --git a/drivers/char/hw_random/ingenic-rng.c b/drivers/char/hw_random/ingenic-rng.c
index 055cfe59f519..bbfd662d25a6 100644
--- a/drivers/char/hw_random/ingenic-rng.c
+++ b/drivers/char/hw_random/ingenic-rng.c
@@ -11,7 +11,7 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -95,7 +95,7 @@ static int ingenic_rng_probe(struct platform_device *pdev)
return PTR_ERR(priv->base);
}
- priv->version = (enum ingenic_rng_version)of_device_get_match_data(&pdev->dev);
+ priv->version = (enum ingenic_rng_version)(uintptr_t)of_device_get_match_data(&pdev->dev);
priv->rng.name = pdev->name;
priv->rng.init = ingenic_rng_init;
@@ -114,15 +114,13 @@ static int ingenic_rng_probe(struct platform_device *pdev)
return 0;
}
-static int ingenic_rng_remove(struct platform_device *pdev)
+static void ingenic_rng_remove(struct platform_device *pdev)
{
struct ingenic_rng *priv = platform_get_drvdata(pdev);
hwrng_unregister(&priv->rng);
writel(0, priv->base + RNG_REG_ERNG_OFFSET);
-
- return 0;
}
static const struct of_device_id ingenic_rng_of_match[] = {
diff --git a/drivers/char/hw_random/ingenic-trng.c b/drivers/char/hw_random/ingenic-trng.c
index 0eb80f786f4d..1672320e7d3d 100644
--- a/drivers/char/hw_random/ingenic-trng.c
+++ b/drivers/char/hw_random/ingenic-trng.c
@@ -11,8 +11,8 @@
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -22,8 +22,6 @@
#define TRNG_REG_STATUS_OFFSET 0x08
/* bits within the CFG register */
-#define CFG_RDY_CLR BIT(12)
-#define CFG_INT_MASK BIT(11)
#define CFG_GEN_EN BIT(0)
/* bits within the STATUS register */
@@ -31,7 +29,6 @@
struct ingenic_trng {
void __iomem *base;
- struct clk *clk;
struct hwrng rng;
};
@@ -79,6 +76,7 @@ static int ingenic_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait
static int ingenic_trng_probe(struct platform_device *pdev)
{
struct ingenic_trng *trng;
+ struct clk *clk;
int ret;
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
@@ -86,60 +84,28 @@ static int ingenic_trng_probe(struct platform_device *pdev)
return -ENOMEM;
trng->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(trng->base)) {
- pr_err("%s: Failed to map DTRNG registers\n", __func__);
- ret = PTR_ERR(trng->base);
- return PTR_ERR(trng->base);
- }
+ if (IS_ERR(trng->base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),
+ "%s: Failed to map DTRNG registers\n", __func__);
- trng->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(trng->clk)) {
- ret = PTR_ERR(trng->clk);
- pr_crit("%s: Cannot get DTRNG clock\n", __func__);
- return PTR_ERR(trng->clk);
- }
-
- ret = clk_prepare_enable(trng->clk);
- if (ret) {
- pr_crit("%s: Unable to enable DTRNG clock\n", __func__);
- return ret;
- }
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "%s: Cannot get and enable DTRNG clock\n", __func__);
trng->rng.name = pdev->name;
trng->rng.init = ingenic_trng_init;
trng->rng.cleanup = ingenic_trng_cleanup;
trng->rng.read = ingenic_trng_read;
- ret = hwrng_register(&trng->rng);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register hwrng\n");
- goto err_unprepare_clk;
- }
+ ret = devm_hwrng_register(&pdev->dev, &trng->rng);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
platform_set_drvdata(pdev, trng);
dev_info(&pdev->dev, "Ingenic DTRNG driver registered\n");
return 0;
-
-err_unprepare_clk:
- clk_disable_unprepare(trng->clk);
- return ret;
-}
-
-static int ingenic_trng_remove(struct platform_device *pdev)
-{
- struct ingenic_trng *trng = platform_get_drvdata(pdev);
- unsigned int ctrl;
-
- hwrng_unregister(&trng->rng);
-
- ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);
- ctrl &= ~CFG_GEN_EN;
- writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);
-
- clk_disable_unprepare(trng->clk);
-
- return 0;
}
static const struct of_device_id ingenic_trng_of_match[] = {
@@ -150,7 +116,6 @@ MODULE_DEVICE_TABLE(of, ingenic_trng_of_match);
static struct platform_driver ingenic_trng_driver = {
.probe = ingenic_trng_probe,
- .remove = ingenic_trng_remove,
.driver = {
.name = "ingenic-trng",
.of_match_table = ingenic_trng_of_match,
diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c
index 06bc060534d8..440fe28bddc0 100644
--- a/drivers/char/hw_random/iproc-rng200.c
+++ b/drivers/char/hw_random/iproc-rng200.c
@@ -12,8 +12,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
@@ -182,6 +181,8 @@ static int iproc_rng200_probe(struct platform_device *pdev)
return PTR_ERR(priv->base);
}
+ dev_set_drvdata(dev, priv);
+
priv->rng.name = "iproc-rng200";
priv->rng.read = iproc_rng200_read;
priv->rng.init = iproc_rng200_init;
@@ -199,6 +200,28 @@ static int iproc_rng200_probe(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused iproc_rng200_suspend(struct device *dev)
+{
+ struct iproc_rng200_dev *priv = dev_get_drvdata(dev);
+
+ iproc_rng200_cleanup(&priv->rng);
+
+ return 0;
+}
+
+static int __maybe_unused iproc_rng200_resume(struct device *dev)
+{
+ struct iproc_rng200_dev *priv = dev_get_drvdata(dev);
+
+ iproc_rng200_init(&priv->rng);
+
+ return 0;
+}
+
+static const struct dev_pm_ops iproc_rng200_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(iproc_rng200_suspend, iproc_rng200_resume)
+};
+
static const struct of_device_id iproc_rng200_of_match[] = {
{ .compatible = "brcm,bcm2711-rng200", },
{ .compatible = "brcm,bcm7211-rng200", },
@@ -212,6 +235,7 @@ static struct platform_driver iproc_rng200_driver = {
.driver = {
.name = "iproc-rng200",
.of_match_table = iproc_rng200_of_match,
+ .pm = &iproc_rng200_pm_ops,
},
.probe = iproc_rng200_probe,
};
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
new file mode 100644
index 000000000000..9776f4daa044
--- /dev/null
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TRNG driver for the StarFive JH7110 SoC
+ *
+ * Copyright (C) 2022 StarFive Technology Co.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/random.h>
+#include <linux/reset.h>
+
+/* trng register offset */
+#define STARFIVE_CTRL 0x00
+#define STARFIVE_STAT 0x04
+#define STARFIVE_MODE 0x08
+#define STARFIVE_SMODE 0x0C
+#define STARFIVE_IE 0x10
+#define STARFIVE_ISTAT 0x14
+#define STARFIVE_RAND0 0x20
+#define STARFIVE_RAND1 0x24
+#define STARFIVE_RAND2 0x28
+#define STARFIVE_RAND3 0x2C
+#define STARFIVE_RAND4 0x30
+#define STARFIVE_RAND5 0x34
+#define STARFIVE_RAND6 0x38
+#define STARFIVE_RAND7 0x3C
+#define STARFIVE_AUTO_RQSTS 0x60
+#define STARFIVE_AUTO_AGE 0x64
+
+/* CTRL CMD */
+#define STARFIVE_CTRL_EXEC_NOP 0x0
+#define STARFIVE_CTRL_GENE_RANDNUM 0x1
+#define STARFIVE_CTRL_EXEC_RANDRESEED 0x2
+
+/* STAT */
+#define STARFIVE_STAT_NONCE_MODE BIT(2)
+#define STARFIVE_STAT_R256 BIT(3)
+#define STARFIVE_STAT_MISSION_MODE BIT(8)
+#define STARFIVE_STAT_SEEDED BIT(9)
+#define STARFIVE_STAT_LAST_RESEED(x) ((x) << 16)
+#define STARFIVE_STAT_SRVC_RQST BIT(27)
+#define STARFIVE_STAT_RAND_GENERATING BIT(30)
+#define STARFIVE_STAT_RAND_SEEDING BIT(31)
+
+/* MODE */
+#define STARFIVE_MODE_R256 BIT(3)
+
+/* SMODE */
+#define STARFIVE_SMODE_NONCE_MODE BIT(2)
+#define STARFIVE_SMODE_MISSION_MODE BIT(8)
+#define STARFIVE_SMODE_MAX_REJECTS(x) ((x) << 16)
+
+/* IE */
+#define STARFIVE_IE_RAND_RDY_EN BIT(0)
+#define STARFIVE_IE_SEED_DONE_EN BIT(1)
+#define STARFIVE_IE_LFSR_LOCKUP_EN BIT(4)
+#define STARFIVE_IE_GLBL_EN BIT(31)
+
+#define STARFIVE_IE_ALL (STARFIVE_IE_GLBL_EN | \
+ STARFIVE_IE_RAND_RDY_EN | \
+ STARFIVE_IE_SEED_DONE_EN | \
+ STARFIVE_IE_LFSR_LOCKUP_EN)
+
+/* ISTAT */
+#define STARFIVE_ISTAT_RAND_RDY BIT(0)
+#define STARFIVE_ISTAT_SEED_DONE BIT(1)
+#define STARFIVE_ISTAT_LFSR_LOCKUP BIT(4)
+
+#define STARFIVE_RAND_LEN sizeof(u32)
+
+#define to_trng(p) container_of(p, struct starfive_trng, rng)
+
+enum reseed {
+ RANDOM_RESEED,
+ NONCE_RESEED,
+};
+
+enum mode {
+ PRNG_128BIT,
+ PRNG_256BIT,
+};
+
+struct starfive_trng {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *hclk;
+ struct clk *ahb;
+ struct reset_control *rst;
+ struct hwrng rng;
+ struct completion random_done;
+ struct completion reseed_done;
+ u32 mode;
+ u32 mission;
+ u32 reseed;
+ /* protects against concurrent write to ctrl register */
+ spinlock_t write_lock;
+};
+
+static u16 autoreq;
+module_param(autoreq, ushort, 0);
+MODULE_PARM_DESC(autoreq, "Auto-reseeding after random number requests by host reaches specified counter:\n"
+ " 0 - disable counter\n"
+ " other - reload value for internal counter");
+
+static u16 autoage;
+module_param(autoage, ushort, 0);
+MODULE_PARM_DESC(autoage, "Auto-reseeding after specified timer countdowns to 0:\n"
+ " 0 - disable timer\n"
+ " other - reload value for internal timer");
+
+static inline int starfive_trng_wait_idle(struct starfive_trng *trng)
+{
+ u32 stat;
+
+ return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,
+ !(stat & (STARFIVE_STAT_RAND_GENERATING |
+ STARFIVE_STAT_RAND_SEEDING)),
+ 10, 100000);
+}
+
+static inline void starfive_trng_irq_mask_clear(struct starfive_trng *trng)
+{
+ /* clear register: ISTAT */
+ u32 data = readl(trng->base + STARFIVE_ISTAT);
+
+ writel(data, trng->base + STARFIVE_ISTAT);
+}
+
+static int starfive_trng_cmd(struct starfive_trng *trng, u32 cmd, bool wait)
+{
+ int wait_time = 1000;
+
+ /* allow up to 40 us for wait == 0 */
+ if (!wait)
+ wait_time = 40;
+
+ switch (cmd) {
+ case STARFIVE_CTRL_GENE_RANDNUM:
+ reinit_completion(&trng->random_done);
+ spin_lock_irq(&trng->write_lock);
+ writel(cmd, trng->base + STARFIVE_CTRL);
+ spin_unlock_irq(&trng->write_lock);
+ if (!wait_for_completion_timeout(&trng->random_done, usecs_to_jiffies(wait_time)))
+ return -ETIMEDOUT;
+ break;
+ case STARFIVE_CTRL_EXEC_RANDRESEED:
+ reinit_completion(&trng->reseed_done);
+ spin_lock_irq(&trng->write_lock);
+ writel(cmd, trng->base + STARFIVE_CTRL);
+ spin_unlock_irq(&trng->write_lock);
+ if (!wait_for_completion_timeout(&trng->reseed_done, usecs_to_jiffies(wait_time)))
+ return -ETIMEDOUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int starfive_trng_init(struct hwrng *rng)
+{
+ struct starfive_trng *trng = to_trng(rng);
+ u32 mode, intr = 0;
+
+ /* setup Auto Request/Age register */
+ writel(autoage, trng->base + STARFIVE_AUTO_AGE);
+ writel(autoreq, trng->base + STARFIVE_AUTO_RQSTS);
+
+ /* clear register: ISTAT */
+ starfive_trng_irq_mask_clear(trng);
+
+ intr |= STARFIVE_IE_ALL;
+ writel(intr, trng->base + STARFIVE_IE);
+
+ mode = readl(trng->base + STARFIVE_MODE);
+
+ switch (trng->mode) {
+ case PRNG_128BIT:
+ mode &= ~STARFIVE_MODE_R256;
+ break;
+ case PRNG_256BIT:
+ mode |= STARFIVE_MODE_R256;
+ break;
+ default:
+ mode |= STARFIVE_MODE_R256;
+ break;
+ }
+
+ writel(mode, trng->base + STARFIVE_MODE);
+
+ return starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED, 1);
+}
+
+static irqreturn_t starfive_trng_irq(int irq, void *priv)
+{
+ u32 status;
+ struct starfive_trng *trng = (struct starfive_trng *)priv;
+
+ status = readl(trng->base + STARFIVE_ISTAT);
+ if (status & STARFIVE_ISTAT_RAND_RDY) {
+ writel(STARFIVE_ISTAT_RAND_RDY, trng->base + STARFIVE_ISTAT);
+ complete(&trng->random_done);
+ }
+
+ if (status & STARFIVE_ISTAT_SEED_DONE) {
+ writel(STARFIVE_ISTAT_SEED_DONE, trng->base + STARFIVE_ISTAT);
+ complete(&trng->reseed_done);
+ }
+
+ if (status & STARFIVE_ISTAT_LFSR_LOCKUP) {
+ writel(STARFIVE_ISTAT_LFSR_LOCKUP, trng->base + STARFIVE_ISTAT);
+ /* SEU occurred, reseeding required*/
+ spin_lock(&trng->write_lock);
+ writel(STARFIVE_CTRL_EXEC_RANDRESEED, trng->base + STARFIVE_CTRL);
+ spin_unlock(&trng->write_lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void starfive_trng_cleanup(struct hwrng *rng)
+{
+ struct starfive_trng *trng = to_trng(rng);
+
+ writel(0, trng->base + STARFIVE_CTRL);
+
+ reset_control_assert(trng->rst);
+ clk_disable_unprepare(trng->hclk);
+ clk_disable_unprepare(trng->ahb);
+}
+
+static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct starfive_trng *trng = to_trng(rng);
+ int ret;
+
+ pm_runtime_get_sync(trng->dev);
+
+ if (trng->mode == PRNG_256BIT)
+ max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
+ else
+ max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));
+
+ if (wait) {
+ ret = starfive_trng_wait_idle(trng);
+ if (ret)
+ return -ETIMEDOUT;
+ }
+
+ ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait);
+ if (ret)
+ return ret;
+
+ memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max);
+
+ pm_runtime_put_sync_autosuspend(trng->dev);
+
+ return max;
+}
+
+static int starfive_trng_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq;
+ struct starfive_trng *trng;
+
+ trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, trng);
+ trng->dev = &pdev->dev;
+
+ trng->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(trng->base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),
+ "Error remapping memory for platform device.\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ init_completion(&trng->random_done);
+ init_completion(&trng->reseed_done);
+ spin_lock_init(&trng->write_lock);
+
+ ret = devm_request_irq(&pdev->dev, irq, starfive_trng_irq, 0, pdev->name,
+ (void *)trng);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register interrupt handler\n");
+
+ trng->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(trng->hclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(trng->hclk),
+ "Error getting hardware reference clock\n");
+
+ trng->ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(trng->ahb))
+ return dev_err_probe(&pdev->dev, PTR_ERR(trng->ahb),
+ "Error getting ahb reference clock\n");
+
+ trng->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
+ if (IS_ERR(trng->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(trng->rst),
+ "Error getting hardware reset line\n");
+
+ clk_prepare_enable(trng->hclk);
+ clk_prepare_enable(trng->ahb);
+ reset_control_deassert(trng->rst);
+
+ trng->rng.name = dev_driver_string(&pdev->dev);
+ trng->rng.init = starfive_trng_init;
+ trng->rng.cleanup = starfive_trng_cleanup;
+ trng->rng.read = starfive_trng_read;
+
+ trng->mode = PRNG_256BIT;
+ trng->mission = 1;
+ trng->reseed = RANDOM_RESEED;
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_hwrng_register(&pdev->dev, &trng->rng);
+ if (ret) {
+ pm_runtime_disable(&pdev->dev);
+
+ reset_control_assert(trng->rst);
+ clk_disable_unprepare(trng->ahb);
+ clk_disable_unprepare(trng->hclk);
+
+ return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
+ }
+
+ return 0;
+}
+
+static int __maybe_unused starfive_trng_suspend(struct device *dev)
+{
+ struct starfive_trng *trng = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(trng->hclk);
+ clk_disable_unprepare(trng->ahb);
+
+ return 0;
+}
+
+static int __maybe_unused starfive_trng_resume(struct device *dev)
+{
+ struct starfive_trng *trng = dev_get_drvdata(dev);
+
+ clk_prepare_enable(trng->hclk);
+ clk_prepare_enable(trng->ahb);
+
+ return 0;
+}
+
+static const struct dev_pm_ops starfive_trng_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(starfive_trng_suspend,
+ starfive_trng_resume)
+ SET_RUNTIME_PM_OPS(starfive_trng_suspend,
+ starfive_trng_resume, NULL)
+};
+
+static const struct of_device_id trng_dt_ids[] __maybe_unused = {
+ { .compatible = "starfive,jh7110-trng" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, trng_dt_ids);
+
+static struct platform_driver starfive_trng_driver = {
+ .probe = starfive_trng_probe,
+ .driver = {
+ .name = "jh7110-trng",
+ .pm = &starfive_trng_pm_ops,
+ .of_match_table = of_match_ptr(trng_dt_ids),
+ },
+};
+
+module_platform_driver(starfive_trng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("StarFive True Random Number Generator");
diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c
index 2f2f21f1b659..9e408144a10c 100644
--- a/drivers/char/hw_random/ks-sa-rng.c
+++ b/drivers/char/hw_random/ks-sa-rng.c
@@ -81,7 +81,6 @@ struct trng_regs {
};
struct ks_sa_rng {
- struct device *dev;
struct hwrng rng;
struct clk *clk;
struct regmap *regmap_cfg;
@@ -113,8 +112,7 @@ static unsigned int refill_delay_ns(unsigned long clk_rate)
static int ks_sa_rng_init(struct hwrng *rng)
{
u32 value;
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
unsigned long clk_rate = clk_get_rate(ks_sa_rng->clk);
/* Enable RNG module */
@@ -153,8 +151,7 @@ static int ks_sa_rng_init(struct hwrng *rng)
static void ks_sa_rng_cleanup(struct hwrng *rng)
{
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
/* Disable RNG */
writel(0, &ks_sa_rng->reg_rng->control);
@@ -164,8 +161,7 @@ static void ks_sa_rng_cleanup(struct hwrng *rng)
static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data)
{
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
/* Read random data */
data[0] = readl(&ks_sa_rng->reg_rng->output_l);
@@ -179,8 +175,7 @@ static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data)
static int ks_sa_rng_data_present(struct hwrng *rng, int wait)
{
- struct device *dev = (struct device *)rng->priv;
- struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+ struct ks_sa_rng *ks_sa_rng = container_of(rng, struct ks_sa_rng, rng);
u64 now = ktime_get_ns();
u32 ready;
@@ -217,7 +212,6 @@ static int ks_sa_rng_probe(struct platform_device *pdev)
if (!ks_sa_rng)
return -ENOMEM;
- ks_sa_rng->dev = dev;
ks_sa_rng->rng = (struct hwrng) {
.name = "ks_sa_hwrng",
.init = ks_sa_rng_init,
@@ -225,7 +219,6 @@ static int ks_sa_rng_probe(struct platform_device *pdev)
.data_present = ks_sa_rng_data_present,
.cleanup = ks_sa_rng_cleanup,
};
- ks_sa_rng->rng.priv = (unsigned long)dev;
ks_sa_rng->reg_rng = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ks_sa_rng->reg_rng))
@@ -235,30 +228,27 @@ static int ks_sa_rng_probe(struct platform_device *pdev)
syscon_regmap_lookup_by_phandle(dev->of_node,
"ti,syscon-sa-cfg");
- if (IS_ERR(ks_sa_rng->regmap_cfg)) {
- dev_err(dev, "syscon_node_to_regmap failed\n");
- return -EINVAL;
- }
+ if (IS_ERR(ks_sa_rng->regmap_cfg))
+ return dev_err_probe(dev, -EINVAL, "syscon_node_to_regmap failed\n");
+
+ ks_sa_rng->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(ks_sa_rng->clk))
+ return dev_err_probe(dev, PTR_ERR(ks_sa_rng->clk), "Failed to get clock\n");
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
- dev_err(dev, "Failed to enable SA power-domain\n");
pm_runtime_disable(dev);
- return ret;
+ return dev_err_probe(dev, ret, "Failed to enable SA power-domain\n");
}
- platform_set_drvdata(pdev, ks_sa_rng);
-
return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng);
}
-static int ks_sa_rng_remove(struct platform_device *pdev)
+static void ks_sa_rng_remove(struct platform_device *pdev)
{
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static const struct of_device_id ks_sa_rng_dt_match[] = {
diff --git a/drivers/char/hw_random/meson-rng.c b/drivers/char/hw_random/meson-rng.c
index 8bb30282ca46..75225eb9fef6 100644
--- a/drivers/char/hw_random/meson-rng.c
+++ b/drivers/char/hw_random/meson-rng.c
@@ -13,14 +13,23 @@
#include <linux/types.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/iopoll.h>
-#define RNG_DATA 0x00
+#define RNG_DATA 0x00
+#define RNG_S4_DATA 0x08
+#define RNG_S4_CFG 0x00
+
+#define RUN_BIT BIT(0)
+#define SEED_READY_STS_BIT BIT(31)
+
+struct meson_rng_priv {
+ int (*read)(struct hwrng *rng, void *buf, size_t max, bool wait);
+};
struct meson_rng_data {
void __iomem *base;
- struct platform_device *pdev;
struct hwrng rng;
- struct clk *core_clk;
+ struct device *dev;
};
static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
@@ -33,52 +42,96 @@ static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
return sizeof(u32);
}
-static void meson_rng_clk_disable(void *data)
+static int meson_rng_wait_status(void __iomem *cfg_addr, int bit)
{
- clk_disable_unprepare(data);
+ u32 status = 0;
+ int ret;
+
+ ret = readl_relaxed_poll_timeout_atomic(cfg_addr,
+ status, !(status & bit),
+ 10, 10000);
+ if (ret)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int meson_s4_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct meson_rng_data *data =
+ container_of(rng, struct meson_rng_data, rng);
+
+ void __iomem *cfg_addr = data->base + RNG_S4_CFG;
+ int err;
+
+ writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr);
+
+ err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT);
+ if (err) {
+ dev_err(data->dev, "Seed isn't ready, try again\n");
+ return err;
+ }
+
+ err = meson_rng_wait_status(cfg_addr, RUN_BIT);
+ if (err) {
+ dev_err(data->dev, "Can't get random number, try again\n");
+ return err;
+ }
+
+ *(u32 *)buf = readl_relaxed(data->base + RNG_S4_DATA);
+
+ return sizeof(u32);
}
static int meson_rng_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct meson_rng_data *data;
- int ret;
+ struct clk *core_clk;
+ const struct meson_rng_priv *priv;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->pdev = pdev;
+ priv = device_get_match_data(&pdev->dev);
+ if (!priv)
+ return -ENODEV;
data->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
- data->core_clk = devm_clk_get_optional(dev, "core");
- if (IS_ERR(data->core_clk))
- return dev_err_probe(dev, PTR_ERR(data->core_clk),
+ core_clk = devm_clk_get_optional_enabled(dev, "core");
+ if (IS_ERR(core_clk))
+ return dev_err_probe(dev, PTR_ERR(core_clk),
"Failed to get core clock\n");
- if (data->core_clk) {
- ret = clk_prepare_enable(data->core_clk);
- if (ret)
- return ret;
- ret = devm_add_action_or_reset(dev, meson_rng_clk_disable,
- data->core_clk);
- if (ret)
- return ret;
- }
-
data->rng.name = pdev->name;
- data->rng.read = meson_rng_read;
+ data->rng.read = priv->read;
- platform_set_drvdata(pdev, data);
+ data->dev = &pdev->dev;
return devm_hwrng_register(dev, &data->rng);
}
+static const struct meson_rng_priv meson_rng_priv = {
+ .read = meson_rng_read,
+};
+
+static const struct meson_rng_priv meson_rng_priv_s4 = {
+ .read = meson_s4_rng_read,
+};
+
static const struct of_device_id meson_rng_of_match[] = {
- { .compatible = "amlogic,meson-rng", },
+ {
+ .compatible = "amlogic,meson-rng",
+ .data = (void *)&meson_rng_priv,
+ },
+ {
+ .compatible = "amlogic,meson-s4-rng",
+ .data = (void *)&meson_rng_priv_s4,
+ },
{},
};
MODULE_DEVICE_TABLE(of, meson_rng_of_match);
diff --git a/drivers/char/hw_random/mpfs-rng.c b/drivers/char/hw_random/mpfs-rng.c
index 5813da617a48..0994024daa70 100644
--- a/drivers/char/hw_random/mpfs-rng.c
+++ b/drivers/char/hw_random/mpfs-rng.c
@@ -78,9 +78,6 @@ static int mpfs_rng_probe(struct platform_device *pdev)
rng_priv->rng.read = mpfs_rng_read;
rng_priv->rng.name = pdev->name;
- rng_priv->rng.quality = 1024;
-
- platform_set_drvdata(pdev, rng_priv);
ret = devm_hwrng_register(&pdev->dev, &rng_priv->rng);
if (ret)
diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c
index 6c00ea008555..5808d09d12c4 100644
--- a/drivers/char/hw_random/mtk-rng.c
+++ b/drivers/char/hw_random/mtk-rng.c
@@ -22,7 +22,7 @@
#define RNG_AUTOSUSPEND_TIMEOUT 100
#define USEC_POLL 2
-#define TIMEOUT_POLL 20
+#define TIMEOUT_POLL 60
#define RNG_CTRL 0x00
#define RNG_EN BIT(0)
@@ -36,6 +36,7 @@ struct mtk_rng {
void __iomem *base;
struct clk *clk;
struct hwrng rng;
+ struct device *dev;
};
static int mtk_rng_init(struct hwrng *rng)
@@ -77,7 +78,7 @@ static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait)
readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready,
ready & RNG_READY, USEC_POLL,
TIMEOUT_POLL);
- return !!ready;
+ return !!(ready & RNG_READY);
}
static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
@@ -85,7 +86,7 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
struct mtk_rng *priv = to_mtk_rng(rng);
int retval = 0;
- pm_runtime_get_sync((struct device *)priv->rng.priv);
+ pm_runtime_get_sync(priv->dev);
while (max >= sizeof(u32)) {
if (!mtk_rng_wait_ready(rng, wait))
@@ -97,8 +98,7 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
max -= sizeof(u32);
}
- pm_runtime_mark_last_busy((struct device *)priv->rng.priv);
- pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv);
+ pm_runtime_put_sync_autosuspend(priv->dev);
return retval || !wait ? retval : -EIO;
}
@@ -112,13 +112,13 @@ static int mtk_rng_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ priv->dev = &pdev->dev;
priv->rng.name = pdev->name;
#ifndef CONFIG_PM
priv->rng.init = mtk_rng_init;
priv->rng.cleanup = mtk_rng_cleanup;
#endif
priv->rng.read = mtk_rng_read;
- priv->rng.priv = (unsigned long)&pdev->dev;
priv->rng.quality = 900;
priv->clk = devm_clk_get(&pdev->dev, "rng");
@@ -142,7 +142,9 @@ static int mtk_rng_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, priv);
pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
dev_info(&pdev->dev, "registered RNG driver\n");
@@ -179,6 +181,7 @@ static const struct dev_pm_ops mtk_rng_pm_ops = {
#endif /* CONFIG_PM */
static const struct of_device_id mtk_rng_match[] = {
+ { .compatible = "mediatek,mt7986-rng" },
{ .compatible = "mediatek,mt7623-rng" },
{},
};
diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c
index 008763c988ed..e3fcb8bcc29b 100644
--- a/drivers/char/hw_random/mxc-rnga.c
+++ b/drivers/char/hw_random/mxc-rnga.c
@@ -131,7 +131,7 @@ static void mxc_rnga_cleanup(struct hwrng *rng)
__raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);
}
-static int __init mxc_rnga_probe(struct platform_device *pdev)
+static int mxc_rnga_probe(struct platform_device *pdev)
{
int err;
struct mxc_rng *mxc_rng;
@@ -147,44 +147,32 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
mxc_rng->rng.data_present = mxc_rnga_data_present;
mxc_rng->rng.data_read = mxc_rnga_data_read;
- mxc_rng->clk = devm_clk_get(&pdev->dev, NULL);
+ mxc_rng->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(mxc_rng->clk)) {
dev_err(&pdev->dev, "Could not get rng_clk!\n");
return PTR_ERR(mxc_rng->clk);
}
- err = clk_prepare_enable(mxc_rng->clk);
- if (err)
- return err;
-
mxc_rng->mem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mxc_rng->mem)) {
err = PTR_ERR(mxc_rng->mem);
- goto err_ioremap;
+ return err;
}
err = hwrng_register(&mxc_rng->rng);
if (err) {
dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
- goto err_ioremap;
+ return err;
}
return 0;
-
-err_ioremap:
- clk_disable_unprepare(mxc_rng->clk);
- return err;
}
-static int __exit mxc_rnga_remove(struct platform_device *pdev)
+static void mxc_rnga_remove(struct platform_device *pdev)
{
struct mxc_rng *mxc_rng = platform_get_drvdata(pdev);
hwrng_unregister(&mxc_rng->rng);
-
- clk_disable_unprepare(mxc_rng->clk);
-
- return 0;
}
static const struct of_device_id mxc_rnga_of_match[] = {
@@ -199,10 +187,11 @@ static struct platform_driver mxc_rnga_driver = {
.name = "mxc_rnga",
.of_match_table = mxc_rnga_of_match,
},
- .remove = __exit_p(mxc_rnga_remove),
+ .probe = mxc_rnga_probe,
+ .remove = mxc_rnga_remove,
};
-module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe);
+module_platform_driver(mxc_rnga_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index 73e408146420..ea6d5599242f 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -14,7 +14,8 @@
#include <linux/hw_random.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <asm/hypervisor.h>
@@ -28,7 +29,7 @@
static char version[] =
DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
-MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
MODULE_DESCRIPTION("Niagara2 RNG driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
@@ -695,20 +696,15 @@ static void n2rng_driver_version(void)
static const struct of_device_id n2rng_match[];
static int n2rng_probe(struct platform_device *op)
{
- const struct of_device_id *match;
int err = -ENOMEM;
struct n2rng *np;
- match = of_match_device(n2rng_match, &op->dev);
- if (!match)
- return -EINVAL;
-
n2rng_driver_version();
np = devm_kzalloc(&op->dev, sizeof(*np), GFP_KERNEL);
if (!np)
goto out;
np->op = op;
- np->data = (struct n2rng_template *)match->data;
+ np->data = (struct n2rng_template *)device_get_match_data(&op->dev);
INIT_DELAYED_WORK(&np->work, n2rng_work);
@@ -785,7 +781,7 @@ out:
return err;
}
-static int n2rng_remove(struct platform_device *op)
+static void n2rng_remove(struct platform_device *op)
{
struct n2rng *np = platform_get_drvdata(op);
@@ -794,8 +790,6 @@ static int n2rng_remove(struct platform_device *op)
cancel_delayed_work_sync(&np->work);
sun4v_hvapi_unregister(HV_GRP_RNG);
-
- return 0;
}
static struct n2rng_template n2_template = {
diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h
index 9a870f5dc371..7612f15a261f 100644
--- a/drivers/char/hw_random/n2rng.h
+++ b/drivers/char/hw_random/n2rng.h
@@ -48,7 +48,7 @@
#define HV_RNG_NUM_CONTROL 4
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
extern unsigned long sun4v_rng_get_diag_ctl(void);
extern unsigned long sun4v_rng_ctl_read_v1(unsigned long ctl_regs_ra,
unsigned long *state,
@@ -147,6 +147,6 @@ struct n2rng {
#define N2RNG_BUSY_LIMIT 100
#define N2RNG_HCHECK_LIMIT 100
-#endif /* !(__ASSEMBLY__) */
+#endif /* !(__ASSEMBLER__) */
#endif /* _N2RNG_H */
diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
index e8f9621e7954..f2a2aa7a531c 100644
--- a/drivers/char/hw_random/nomadik-rng.c
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -13,8 +13,6 @@
#include <linux/clk.h>
#include <linux/err.h>
-static struct clk *rng_clk;
-
static int nmk_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
void __iomem *base = (void __iomem *)rng->priv;
@@ -36,21 +34,17 @@ static struct hwrng nmk_rng = {
static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
{
+ struct clk *rng_clk;
void __iomem *base;
int ret;
- rng_clk = devm_clk_get(&dev->dev, NULL);
- if (IS_ERR(rng_clk)) {
- dev_err(&dev->dev, "could not get rng clock\n");
- ret = PTR_ERR(rng_clk);
- return ret;
- }
-
- clk_prepare_enable(rng_clk);
+ rng_clk = devm_clk_get_enabled(&dev->dev, NULL);
+ if (IS_ERR(rng_clk))
+ return dev_err_probe(&dev->dev, PTR_ERR(rng_clk), "could not get rng clock\n");
ret = amba_request_regions(dev, dev->dev.init_name);
if (ret)
- goto out_clk;
+ return ret;
ret = -ENOMEM;
base = devm_ioremap(&dev->dev, dev->res.start,
resource_size(&dev->res));
@@ -64,15 +58,12 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
out_release:
amba_release_regions(dev);
-out_clk:
- clk_disable_unprepare(rng_clk);
return ret;
}
static void nmk_rng_remove(struct amba_device *dev)
{
amba_release_regions(dev);
- clk_disable_unprepare(rng_clk);
}
static const struct amba_id nmk_rng_ids[] = {
@@ -87,7 +78,6 @@ MODULE_DEVICE_TABLE(amba, nmk_rng_ids);
static struct amba_driver nmk_rng_driver = {
.drv = {
- .owner = THIS_MODULE,
.name = "rng",
},
.probe = nmk_rng_probe,
@@ -97,4 +87,5 @@ static struct amba_driver nmk_rng_driver = {
module_amba_driver(nmk_rng_driver);
+MODULE_DESCRIPTION("ST-Ericsson Nomadik Random Number Generator");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/npcm-rng.c b/drivers/char/hw_random/npcm-rng.c
index 1ec5f267a656..40d6e29dea03 100644
--- a/drivers/char/hw_random/npcm-rng.c
+++ b/drivers/char/hw_random/npcm-rng.c
@@ -8,16 +8,17 @@
#include <linux/init.h>
#include <linux/random.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
-#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#define NPCM_RNGCS_REG 0x00 /* Control and status register */
#define NPCM_RNGD_REG 0x04 /* Data register */
#define NPCM_RNGMODE_REG 0x08 /* Mode register */
+#define NPCM_RNG_CLK_SET_62_5MHZ BIT(2) /* 60-80 MHz */
#define NPCM_RNG_CLK_SET_25MHZ GENMASK(4, 3) /* 20-25 MHz */
#define NPCM_RNG_DATA_VALID BIT(1)
#define NPCM_RNG_ENABLE BIT(0)
@@ -31,14 +32,15 @@
struct npcm_rng {
void __iomem *base;
struct hwrng rng;
+ struct device *dev;
+ u32 clkp;
};
static int npcm_rng_init(struct hwrng *rng)
{
struct npcm_rng *priv = to_npcm_rng(rng);
- writel(NPCM_RNG_CLK_SET_25MHZ | NPCM_RNG_ENABLE,
- priv->base + NPCM_RNGCS_REG);
+ writel(priv->clkp | NPCM_RNG_ENABLE, priv->base + NPCM_RNGCS_REG);
return 0;
}
@@ -47,7 +49,7 @@ static void npcm_rng_cleanup(struct hwrng *rng)
{
struct npcm_rng *priv = to_npcm_rng(rng);
- writel(NPCM_RNG_CLK_SET_25MHZ, priv->base + NPCM_RNGCS_REG);
+ writel(priv->clkp, priv->base + NPCM_RNGCS_REG);
}
static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
@@ -56,7 +58,7 @@ static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
int retval = 0;
int ready;
- pm_runtime_get_sync((struct device *)priv->rng.priv);
+ pm_runtime_get_sync(priv->dev);
while (max) {
if (wait) {
@@ -78,8 +80,7 @@ static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
max--;
}
- pm_runtime_mark_last_busy((struct device *)priv->rng.priv);
- pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv);
+ pm_runtime_put_sync_autosuspend(priv->dev);
return retval || !wait ? retval : -EIO;
}
@@ -108,8 +109,8 @@ static int npcm_rng_probe(struct platform_device *pdev)
#endif
priv->rng.name = pdev->name;
priv->rng.read = npcm_rng_read;
- priv->rng.priv = (unsigned long)&pdev->dev;
- priv->rng.quality = 1000;
+ priv->dev = &pdev->dev;
+ priv->clkp = (u32)(uintptr_t)of_device_get_match_data(&pdev->dev);
writel(NPCM_RNG_M1ROSEL, priv->base + NPCM_RNGMODE_REG);
@@ -125,15 +126,13 @@ static int npcm_rng_probe(struct platform_device *pdev)
return 0;
}
-static int npcm_rng_remove(struct platform_device *pdev)
+static void npcm_rng_remove(struct platform_device *pdev)
{
struct npcm_rng *priv = platform_get_drvdata(pdev);
devm_hwrng_unregister(&pdev->dev, &priv->rng);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -162,7 +161,10 @@ static const struct dev_pm_ops npcm_rng_pm_ops = {
};
static const struct of_device_id rng_dt_id[] __maybe_unused = {
- { .compatible = "nuvoton,npcm750-rng", },
+ { .compatible = "nuvoton,npcm750-rng",
+ .data = (void *)NPCM_RNG_CLK_SET_25MHZ },
+ { .compatible = "nuvoton,npcm845-rng",
+ .data = (void *)NPCM_RNG_CLK_SET_62_5MHZ },
{},
};
MODULE_DEVICE_TABLE(of, rng_dt_id);
diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c
index 8561a09b4681..412f54405036 100644
--- a/drivers/char/hw_random/octeon-rng.c
+++ b/drivers/char/hw_random/octeon-rng.c
@@ -33,7 +33,7 @@ static int octeon_rng_init(struct hwrng *rng)
ctl.u64 = 0;
ctl.s.ent_en = 1; /* Enable the entropy source. */
ctl.s.rng_en = 1; /* Enable the RNG hardware. */
- cvmx_write_csr((__force u64)p->control_status, ctl.u64);
+ cvmx_write_csr((unsigned long)p->control_status, ctl.u64);
return 0;
}
@@ -44,14 +44,14 @@ static void octeon_rng_cleanup(struct hwrng *rng)
ctl.u64 = 0;
/* Disable everything. */
- cvmx_write_csr((__force u64)p->control_status, ctl.u64);
+ cvmx_write_csr((unsigned long)p->control_status, ctl.u64);
}
static int octeon_rng_data_read(struct hwrng *rng, u32 *data)
{
struct octeon_rng *p = container_of(rng, struct octeon_rng, ops);
- *data = cvmx_read64_uint32((__force u64)p->result);
+ *data = cvmx_read64_uint32((unsigned long)p->result);
return sizeof(u32);
}
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 00ff96703dd2..5e8b50f15db7 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -26,8 +26,6 @@
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_address.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/io.h>
@@ -511,7 +509,7 @@ err_ioremap:
return ret;
}
-static int omap_rng_remove(struct platform_device *pdev)
+static void omap_rng_remove(struct platform_device *pdev)
{
struct omap_rng_dev *priv = platform_get_drvdata(pdev);
@@ -523,8 +521,6 @@ static int omap_rng_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk_reg);
-
- return 0;
}
static int __maybe_unused omap_rng_suspend(struct device *dev)
@@ -568,4 +564,5 @@ static struct platform_driver omap_rng_driver = {
module_platform_driver(omap_rng_driver);
MODULE_ALIAS("platform:omap_rng");
MODULE_AUTHOR("Deepak Saxena (and others)");
+MODULE_DESCRIPTION("RNG driver for TI OMAP CPU family");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c
index f06e4f95114f..aa71f61c3dc9 100644
--- a/drivers/char/hw_random/omap3-rom-rng.c
+++ b/drivers/char/hw_random/omap3-rom-rng.c
@@ -20,7 +20,6 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -57,7 +56,6 @@ static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w)
else
r = 4;
- pm_runtime_mark_last_busy(ddata->dev);
pm_runtime_put_autosuspend(ddata->dev);
return r;
@@ -179,4 +177,5 @@ module_platform_driver(omap3_rom_rng_driver);
MODULE_ALIAS("platform:omap3-rom-rng");
MODULE_AUTHOR("Juha Yrjola");
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
+MODULE_DESCRIPTION("RNG driver for TI OMAP3 CPU family");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c
index 2498d4ef9fe2..6959d6edd44c 100644
--- a/drivers/char/hw_random/pasemi-rng.c
+++ b/drivers/char/hw_random/pasemi-rng.c
@@ -9,11 +9,10 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/io.h>
#define SDCRNG_CTL_REG 0x00
diff --git a/drivers/char/hw_random/pic32-rng.c b/drivers/char/hw_random/pic32-rng.c
index 99c8bd0859a1..888e6f5cec1f 100644
--- a/drivers/char/hw_random/pic32-rng.c
+++ b/drivers/char/hw_random/pic32-rng.c
@@ -12,31 +12,22 @@
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define RNGCON 0x04
-#define TRNGEN BIT(8)
-#define PRNGEN BIT(9)
-#define PRNGCONT BIT(10)
-#define TRNGMOD BIT(11)
-#define SEEDLOAD BIT(12)
-#define RNGPOLY1 0x08
-#define RNGPOLY2 0x0C
-#define RNGNUMGEN1 0x10
-#define RNGNUMGEN2 0x14
+#define TRNGEN BIT(8)
+#define TRNGMOD BIT(11)
#define RNGSEED1 0x18
#define RNGSEED2 0x1C
#define RNGRCNT 0x20
-#define RCNT_MASK 0x7F
+#define RCNT_MASK 0x7F
struct pic32_rng {
void __iomem *base;
struct hwrng rng;
- struct clk *clk;
};
/*
@@ -46,6 +37,15 @@ struct pic32_rng {
*/
#define RNG_TIMEOUT 500
+static int pic32_rng_init(struct hwrng *rng)
+{
+ struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng);
+
+ /* enable TRNG in enhanced mode */
+ writel(TRNGEN | TRNGMOD, priv->base + RNGCON);
+ return 0;
+}
+
static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max,
bool wait)
{
@@ -67,11 +67,17 @@ static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max,
return -EIO;
}
+static void pic32_rng_cleanup(struct hwrng *rng)
+{
+ struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng);
+
+ writel(0, priv->base + RNGCON);
+}
+
static int pic32_rng_probe(struct platform_device *pdev)
{
struct pic32_rng *priv;
- u32 v;
- int ret;
+ struct clk *clk;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -81,41 +87,16 @@ static int pic32_rng_probe(struct platform_device *pdev)
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
- priv->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
-
- ret = clk_prepare_enable(priv->clk);
- if (ret)
- return ret;
-
- /* enable TRNG in enhanced mode */
- v = TRNGEN | TRNGMOD;
- writel(v, priv->base + RNGCON);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
priv->rng.name = pdev->name;
+ priv->rng.init = pic32_rng_init;
priv->rng.read = pic32_rng_read;
+ priv->rng.cleanup = pic32_rng_cleanup;
- ret = devm_hwrng_register(&pdev->dev, &priv->rng);
- if (ret)
- goto err_register;
-
- platform_set_drvdata(pdev, priv);
-
- return 0;
-
-err_register:
- clk_disable_unprepare(priv->clk);
- return ret;
-}
-
-static int pic32_rng_remove(struct platform_device *pdev)
-{
- struct pic32_rng *rng = platform_get_drvdata(pdev);
-
- writel(0, rng->base + RNGCON);
- clk_disable_unprepare(rng->clk);
- return 0;
+ return devm_hwrng_register(&pdev->dev, &priv->rng);
}
static const struct of_device_id pic32_rng_of_match[] __maybe_unused = {
@@ -126,10 +107,9 @@ MODULE_DEVICE_TABLE(of, pic32_rng_of_match);
static struct platform_driver pic32_rng_driver = {
.probe = pic32_rng_probe,
- .remove = pic32_rng_remove,
.driver = {
.name = "pic32-rng",
- .of_match_table = of_match_ptr(pic32_rng_of_match),
+ .of_match_table = pic32_rng_of_match,
},
};
diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c
index 429e956f34e1..47b88de029f2 100644
--- a/drivers/char/hw_random/powernv-rng.c
+++ b/drivers/char/hw_random/powernv-rng.c
@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/random.h>
#include <linux/hw_random.h>
+#include <asm/archrandom.h>
static int powernv_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
diff --git a/drivers/char/hw_random/rockchip-rng.c b/drivers/char/hw_random/rockchip-rng.c
new file mode 100644
index 000000000000..6e3ed4b85605
--- /dev/null
+++ b/drivers/char/hw_random/rockchip-rng.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rockchip-rng.c True Random Number Generator driver for Rockchip SoCs
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2022, Aurelien Jarno
+ * Copyright (c) 2025, Collabora Ltd.
+ * Authors:
+ * Lin Jinhan <troy.lin@rock-chips.com>
+ * Aurelien Jarno <aurelien@aurel32.net>
+ * Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
+ */
+#include <linux/clk.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define RK_RNG_AUTOSUSPEND_DELAY 100
+#define RK_RNG_MAX_BYTE 32
+#define RK_RNG_POLL_PERIOD_US 100
+#define RK_RNG_POLL_TIMEOUT_US 10000
+
+/*
+ * TRNG collects osc ring output bit every RK_RNG_SAMPLE_CNT time. The value is
+ * a tradeoff between speed and quality and has been adjusted to get a quality
+ * of ~900 (~87.5% of FIPS 140-2 successes).
+ */
+#define RK_RNG_SAMPLE_CNT 1000
+
+/* after how many bytes of output TRNGv1 implementations should be reseeded */
+#define RK_TRNG_V1_AUTO_RESEED_CNT 16000
+
+/* TRNG registers from RK3568 TRM-Part2, section 5.4.1 */
+#define TRNG_RST_CTL 0x0004
+#define TRNG_RNG_CTL 0x0400
+#define TRNG_RNG_CTL_LEN_64_BIT (0x00 << 4)
+#define TRNG_RNG_CTL_LEN_128_BIT (0x01 << 4)
+#define TRNG_RNG_CTL_LEN_192_BIT (0x02 << 4)
+#define TRNG_RNG_CTL_LEN_256_BIT (0x03 << 4)
+#define TRNG_RNG_CTL_OSC_RING_SPEED_0 (0x00 << 2)
+#define TRNG_RNG_CTL_OSC_RING_SPEED_1 (0x01 << 2)
+#define TRNG_RNG_CTL_OSC_RING_SPEED_2 (0x02 << 2)
+#define TRNG_RNG_CTL_OSC_RING_SPEED_3 (0x03 << 2)
+#define TRNG_RNG_CTL_MASK GENMASK(15, 0)
+#define TRNG_RNG_CTL_ENABLE BIT(1)
+#define TRNG_RNG_CTL_START BIT(0)
+#define TRNG_RNG_SAMPLE_CNT 0x0404
+#define TRNG_RNG_DOUT 0x0410
+
+/*
+ * TRNG V1 register definitions
+ * The TRNG V1 IP is a stand-alone TRNG implementation (not part of a crypto IP)
+ * and can be found in the Rockchip RK3588 SoC
+ */
+#define TRNG_V1_CTRL 0x0000
+#define TRNG_V1_CTRL_NOP 0x00
+#define TRNG_V1_CTRL_RAND 0x01
+#define TRNG_V1_CTRL_SEED 0x02
+
+#define TRNG_V1_STAT 0x0004
+#define TRNG_V1_STAT_SEEDED BIT(9)
+#define TRNG_V1_STAT_GENERATING BIT(30)
+#define TRNG_V1_STAT_RESEEDING BIT(31)
+
+#define TRNG_V1_MODE 0x0008
+#define TRNG_V1_MODE_128_BIT (0x00 << 3)
+#define TRNG_V1_MODE_256_BIT (0x01 << 3)
+
+/* Interrupt Enable register; unused because polling is faster */
+#define TRNG_V1_IE 0x0010
+#define TRNG_V1_IE_GLBL_EN BIT(31)
+#define TRNG_V1_IE_SEED_DONE_EN BIT(1)
+#define TRNG_V1_IE_RAND_RDY_EN BIT(0)
+
+#define TRNG_V1_ISTAT 0x0014
+#define TRNG_V1_ISTAT_RAND_RDY BIT(0)
+
+/* RAND0 ~ RAND7 */
+#define TRNG_V1_RAND0 0x0020
+#define TRNG_V1_RAND7 0x003C
+
+/* Auto Reseed Register */
+#define TRNG_V1_AUTO_RQSTS 0x0060
+
+#define TRNG_V1_VERSION 0x00F0
+#define TRNG_v1_VERSION_CODE 0x46bc
+/* end of TRNG_V1 register definitions */
+
+/*
+ * RKRNG register definitions
+ * The RKRNG IP is a stand-alone TRNG implementation (not part of a crypto IP)
+ * and can be found in the Rockchip RK3576, Rockchip RK3562 and Rockchip RK3528
+ * SoCs. It can either output true randomness (TRNG) or "deterministic"
+ * randomness derived from hashing the true entropy (DRNG). This driver
+ * implementation uses just the true entropy, and leaves stretching the entropy
+ * up to Linux.
+ */
+#define RKRNG_CFG 0x0000
+#define RKRNG_CTRL 0x0010
+#define RKRNG_CTRL_REQ_TRNG BIT(4)
+#define RKRNG_STATE 0x0014
+#define RKRNG_STATE_TRNG_RDY BIT(4)
+#define RKRNG_TRNG_DATA0 0x0050
+#define RKRNG_TRNG_DATA1 0x0054
+#define RKRNG_TRNG_DATA2 0x0058
+#define RKRNG_TRNG_DATA3 0x005C
+#define RKRNG_TRNG_DATA4 0x0060
+#define RKRNG_TRNG_DATA5 0x0064
+#define RKRNG_TRNG_DATA6 0x0068
+#define RKRNG_TRNG_DATA7 0x006C
+#define RKRNG_READ_LEN 32
+
+/* Before removing this assert, give rk3588_rng_read an upper bound of 32 */
+static_assert(RK_RNG_MAX_BYTE <= (TRNG_V1_RAND7 + 4 - TRNG_V1_RAND0),
+ "You raised RK_RNG_MAX_BYTE and broke rk3588-rng, congrats.");
+
+struct rk_rng {
+ struct hwrng rng;
+ void __iomem *base;
+ int clk_num;
+ struct clk_bulk_data *clk_bulks;
+ const struct rk_rng_soc_data *soc_data;
+ struct device *dev;
+};
+
+struct rk_rng_soc_data {
+ int (*rk_rng_init)(struct hwrng *rng);
+ int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait);
+ void (*rk_rng_cleanup)(struct hwrng *rng);
+ unsigned short quality;
+ bool reset_optional;
+};
+
+/* The mask in the upper 16 bits determines the bits that are updated */
+static void rk_rng_write_ctl(struct rk_rng *rng, u32 val, u32 mask)
+{
+ writel((mask << 16) | val, rng->base + TRNG_RNG_CTL);
+}
+
+static inline void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset)
+{
+ writel(val, rng->base + offset);
+}
+
+static inline u32 rk_rng_readl(struct rk_rng *rng, u32 offset)
+{
+ return readl(rng->base + offset);
+}
+
+static int rk_rng_enable_clks(struct rk_rng *rk_rng)
+{
+ int ret;
+ /* start clocks */
+ ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks);
+ if (ret < 0) {
+ dev_err(rk_rng->dev, "Failed to enable clocks: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rk3568_rng_init(struct hwrng *rng)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+ int ret;
+
+ ret = rk_rng_enable_clks(rk_rng);
+ if (ret < 0)
+ return ret;
+
+ /* set the sample period */
+ writel(RK_RNG_SAMPLE_CNT, rk_rng->base + TRNG_RNG_SAMPLE_CNT);
+
+ /* set osc ring speed and enable it */
+ rk_rng_write_ctl(rk_rng, TRNG_RNG_CTL_LEN_256_BIT |
+ TRNG_RNG_CTL_OSC_RING_SPEED_0 |
+ TRNG_RNG_CTL_ENABLE,
+ TRNG_RNG_CTL_MASK);
+
+ return 0;
+}
+
+static void rk3568_rng_cleanup(struct hwrng *rng)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ /* stop TRNG */
+ rk_rng_write_ctl(rk_rng, 0, TRNG_RNG_CTL_MASK);
+
+ /* stop clocks */
+ clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks);
+}
+
+static int rk3568_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+ size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE);
+ u32 reg;
+ int ret = 0;
+
+ ret = pm_runtime_resume_and_get(rk_rng->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Start collecting random data */
+ rk_rng_write_ctl(rk_rng, TRNG_RNG_CTL_START, TRNG_RNG_CTL_START);
+
+ ret = readl_poll_timeout(rk_rng->base + TRNG_RNG_CTL, reg,
+ !(reg & TRNG_RNG_CTL_START),
+ RK_RNG_POLL_PERIOD_US,
+ RK_RNG_POLL_TIMEOUT_US);
+ if (ret < 0)
+ goto out;
+
+ /* Read random data stored in the registers */
+ memcpy_fromio(buf, rk_rng->base + TRNG_RNG_DOUT, to_read);
+out:
+ pm_runtime_put_sync_autosuspend(rk_rng->dev);
+
+ return (ret < 0) ? ret : to_read;
+}
+
+static int rk3576_rng_init(struct hwrng *rng)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ return rk_rng_enable_clks(rk_rng);
+}
+
+static int rk3576_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+ size_t to_read = min_t(size_t, max, RKRNG_READ_LEN);
+ int ret = 0;
+ u32 val;
+
+ ret = pm_runtime_resume_and_get(rk_rng->dev);
+ if (ret < 0)
+ return ret;
+
+ rk_rng_writel(rk_rng, RKRNG_CTRL_REQ_TRNG | (RKRNG_CTRL_REQ_TRNG << 16),
+ RKRNG_CTRL);
+
+ if (readl_poll_timeout(rk_rng->base + RKRNG_STATE, val,
+ (val & RKRNG_STATE_TRNG_RDY), RK_RNG_POLL_PERIOD_US,
+ RK_RNG_POLL_TIMEOUT_US)) {
+ dev_err(rk_rng->dev, "timed out waiting for data\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ rk_rng_writel(rk_rng, RKRNG_STATE_TRNG_RDY, RKRNG_STATE);
+
+ memcpy_fromio(buf, rk_rng->base + RKRNG_TRNG_DATA0, to_read);
+
+out:
+ pm_runtime_put_sync_autosuspend(rk_rng->dev);
+
+ return (ret < 0) ? ret : to_read;
+}
+
+static int rk3588_rng_init(struct hwrng *rng)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+ u32 version, status, mask, istat;
+ int ret;
+
+ ret = rk_rng_enable_clks(rk_rng);
+ if (ret < 0)
+ return ret;
+
+ version = rk_rng_readl(rk_rng, TRNG_V1_VERSION);
+ if (version != TRNG_v1_VERSION_CODE) {
+ dev_err(rk_rng->dev,
+ "wrong trng version, expected = %08x, actual = %08x\n",
+ TRNG_V1_VERSION, version);
+ ret = -EFAULT;
+ goto err_disable_clk;
+ }
+
+ mask = TRNG_V1_STAT_SEEDED | TRNG_V1_STAT_GENERATING |
+ TRNG_V1_STAT_RESEEDING;
+ if (readl_poll_timeout(rk_rng->base + TRNG_V1_STAT, status,
+ (status & mask) == TRNG_V1_STAT_SEEDED,
+ RK_RNG_POLL_PERIOD_US, RK_RNG_POLL_TIMEOUT_US) < 0) {
+ dev_err(rk_rng->dev, "timed out waiting for hwrng to reseed\n");
+ ret = -ETIMEDOUT;
+ goto err_disable_clk;
+ }
+
+ /*
+ * clear ISTAT flag, downstream advises to do this to avoid
+ * auto-reseeding "on power on"
+ */
+ istat = rk_rng_readl(rk_rng, TRNG_V1_ISTAT);
+ rk_rng_writel(rk_rng, istat, TRNG_V1_ISTAT);
+
+ /* auto reseed after RK_TRNG_V1_AUTO_RESEED_CNT bytes */
+ rk_rng_writel(rk_rng, RK_TRNG_V1_AUTO_RESEED_CNT / 16, TRNG_V1_AUTO_RQSTS);
+
+ return 0;
+err_disable_clk:
+ clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks);
+ return ret;
+}
+
+static void rk3588_rng_cleanup(struct hwrng *rng)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks);
+}
+
+static int rk3588_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+ size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE);
+ int ret = 0;
+ u32 reg;
+
+ ret = pm_runtime_resume_and_get(rk_rng->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Clear ISTAT, even without interrupts enabled, this will be updated */
+ reg = rk_rng_readl(rk_rng, TRNG_V1_ISTAT);
+ rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT);
+
+ /* generate 256 bits of random data */
+ rk_rng_writel(rk_rng, TRNG_V1_MODE_256_BIT, TRNG_V1_MODE);
+ rk_rng_writel(rk_rng, TRNG_V1_CTRL_RAND, TRNG_V1_CTRL);
+
+ ret = readl_poll_timeout_atomic(rk_rng->base + TRNG_V1_ISTAT, reg,
+ (reg & TRNG_V1_ISTAT_RAND_RDY), 0,
+ RK_RNG_POLL_TIMEOUT_US);
+ if (ret < 0)
+ goto out;
+
+ /* Read random data that's in registers TRNG_V1_RAND0 through RAND7 */
+ memcpy_fromio(buf, rk_rng->base + TRNG_V1_RAND0, to_read);
+
+out:
+ /* Clear ISTAT */
+ rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT);
+ /* close the TRNG */
+ rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL);
+
+ pm_runtime_put_sync_autosuspend(rk_rng->dev);
+
+ return (ret < 0) ? ret : to_read;
+}
+
+static const struct rk_rng_soc_data rk3568_soc_data = {
+ .rk_rng_init = rk3568_rng_init,
+ .rk_rng_read = rk3568_rng_read,
+ .rk_rng_cleanup = rk3568_rng_cleanup,
+ .quality = 900,
+ .reset_optional = false,
+};
+
+static const struct rk_rng_soc_data rk3576_soc_data = {
+ .rk_rng_init = rk3576_rng_init,
+ .rk_rng_read = rk3576_rng_read,
+ .rk_rng_cleanup = rk3588_rng_cleanup,
+ .quality = 999, /* as determined by actual testing */
+ .reset_optional = true,
+};
+
+static const struct rk_rng_soc_data rk3588_soc_data = {
+ .rk_rng_init = rk3588_rng_init,
+ .rk_rng_read = rk3588_rng_read,
+ .rk_rng_cleanup = rk3588_rng_cleanup,
+ .quality = 999, /* as determined by actual testing */
+ .reset_optional = true,
+};
+
+static int rk_rng_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct reset_control *rst;
+ struct rk_rng *rk_rng;
+ int ret;
+
+ rk_rng = devm_kzalloc(dev, sizeof(*rk_rng), GFP_KERNEL);
+ if (!rk_rng)
+ return -ENOMEM;
+
+ rk_rng->soc_data = of_device_get_match_data(dev);
+ rk_rng->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rk_rng->base))
+ return PTR_ERR(rk_rng->base);
+
+ rk_rng->clk_num = devm_clk_bulk_get_all(dev, &rk_rng->clk_bulks);
+ if (rk_rng->clk_num < 0)
+ return dev_err_probe(dev, rk_rng->clk_num,
+ "Failed to get clks property\n");
+
+ if (rk_rng->soc_data->reset_optional)
+ rst = devm_reset_control_array_get_optional_exclusive(dev);
+ else
+ rst = devm_reset_control_array_get_exclusive(dev);
+
+ if (rst) {
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n");
+
+ reset_control_assert(rst);
+ udelay(2);
+ reset_control_deassert(rst);
+ }
+
+ platform_set_drvdata(pdev, rk_rng);
+
+ rk_rng->rng.name = dev_driver_string(dev);
+ if (!IS_ENABLED(CONFIG_PM)) {
+ rk_rng->rng.init = rk_rng->soc_data->rk_rng_init;
+ rk_rng->rng.cleanup = rk_rng->soc_data->rk_rng_cleanup;
+ }
+ rk_rng->rng.read = rk_rng->soc_data->rk_rng_read;
+ rk_rng->dev = dev;
+ rk_rng->rng.quality = rk_rng->soc_data->quality;
+
+ pm_runtime_set_autosuspend_delay(dev, RK_RNG_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Runtime pm activation failed.\n");
+
+ ret = devm_hwrng_register(dev, &rk_rng->rng);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register Rockchip hwrng\n");
+
+ return 0;
+}
+
+static int __maybe_unused rk_rng_runtime_suspend(struct device *dev)
+{
+ struct rk_rng *rk_rng = dev_get_drvdata(dev);
+
+ rk_rng->soc_data->rk_rng_cleanup(&rk_rng->rng);
+
+ return 0;
+}
+
+static int __maybe_unused rk_rng_runtime_resume(struct device *dev)
+{
+ struct rk_rng *rk_rng = dev_get_drvdata(dev);
+
+ return rk_rng->soc_data->rk_rng_init(&rk_rng->rng);
+}
+
+static const struct dev_pm_ops rk_rng_pm_ops = {
+ SET_RUNTIME_PM_OPS(rk_rng_runtime_suspend,
+ rk_rng_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id rk_rng_dt_match[] = {
+ { .compatible = "rockchip,rk3568-rng", .data = (void *)&rk3568_soc_data },
+ { .compatible = "rockchip,rk3576-rng", .data = (void *)&rk3576_soc_data },
+ { .compatible = "rockchip,rk3588-rng", .data = (void *)&rk3588_soc_data },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, rk_rng_dt_match);
+
+static struct platform_driver rk_rng_driver = {
+ .driver = {
+ .name = "rockchip-rng",
+ .pm = &rk_rng_pm_ops,
+ .of_match_table = rk_rng_dt_match,
+ },
+ .probe = rk_rng_probe,
+};
+
+module_platform_driver(rk_rng_driver);
+
+MODULE_DESCRIPTION("Rockchip True Random Number Generator driver");
+MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>");
+MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/s390-trng.c b/drivers/char/hw_random/s390-trng.c
index 795853dfc46b..3024d5e9fd61 100644
--- a/drivers/char/hw_random/s390-trng.c
+++ b/drivers/char/hw_random/s390-trng.c
@@ -9,8 +9,7 @@
* Author(s): Harald Freudenberger <freude@de.ibm.com>
*/
-#define KMSG_COMPONENT "trng"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "trng: " fmt
#include <linux/hw_random.h>
#include <linux/kernel.h>
@@ -23,6 +22,7 @@
#include <linux/sched/signal.h>
#include <asm/debug.h>
#include <asm/cpacf.h>
+#include <asm/archrandom.h>
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("IBM Corporation");
@@ -191,7 +191,6 @@ static struct hwrng trng_hwrng_dev = {
.name = "s390-trng",
.data_read = trng_hwrng_data_read,
.read = trng_hwrng_read,
- .quality = 1024,
};
diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c
index 15ba1e6fae4d..23749817d83c 100644
--- a/drivers/char/hw_random/st-rng.c
+++ b/drivers/char/hw_random/st-rng.c
@@ -42,7 +42,6 @@
struct st_rng_data {
void __iomem *base;
- struct clk *clk;
struct hwrng ops;
};
@@ -85,26 +84,18 @@ static int st_rng_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- clk = devm_clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
ddata->ops.priv = (unsigned long)ddata;
ddata->ops.read = st_rng_read;
ddata->ops.name = pdev->name;
ddata->base = base;
- ddata->clk = clk;
-
- dev_set_drvdata(&pdev->dev, ddata);
ret = devm_hwrng_register(&pdev->dev, &ddata->ops);
if (ret) {
dev_err(&pdev->dev, "Failed to register HW RNG\n");
- clk_disable_unprepare(clk);
return ret;
}
@@ -113,15 +104,6 @@ static int st_rng_probe(struct platform_device *pdev)
return 0;
}
-static int st_rng_remove(struct platform_device *pdev)
-{
- struct st_rng_data *ddata = dev_get_drvdata(&pdev->dev);
-
- clk_disable_unprepare(ddata->clk);
-
- return 0;
-}
-
static const struct of_device_id st_rng_match[] __maybe_unused = {
{ .compatible = "st,rng" },
{},
@@ -134,10 +116,10 @@ static struct platform_driver st_rng_driver = {
.of_match_table = of_match_ptr(st_rng_match),
},
.probe = st_rng_probe,
- .remove = st_rng_remove
};
module_platform_driver(st_rng_driver);
MODULE_AUTHOR("Pankaj Dev <pankaj.dev@st.com>");
+MODULE_DESCRIPTION("ST Microelectronics HW Random Number Generator");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
index bc22178f83e8..9a8c00586ab0 100644
--- a/drivers/char/hw_random/stm32-rng.c
+++ b/drivers/char/hw_random/stm32-rng.c
@@ -4,208 +4,601 @@
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
-#define RNG_CR 0x00
-#define RNG_CR_RNGEN BIT(2)
-#define RNG_CR_CED BIT(5)
-
-#define RNG_SR 0x04
-#define RNG_SR_SEIS BIT(6)
-#define RNG_SR_CEIS BIT(5)
-#define RNG_SR_DRDY BIT(0)
+#define RNG_CR 0x00
+#define RNG_CR_RNGEN BIT(2)
+#define RNG_CR_CED BIT(5)
+#define RNG_CR_CONFIG1 GENMASK(11, 8)
+#define RNG_CR_NISTC BIT(12)
+#define RNG_CR_CONFIG2 GENMASK(15, 13)
+#define RNG_CR_CLKDIV_SHIFT 16
+#define RNG_CR_CLKDIV GENMASK(19, 16)
+#define RNG_CR_CONFIG3 GENMASK(25, 20)
+#define RNG_CR_CONDRST BIT(30)
+#define RNG_CR_CONFLOCK BIT(31)
+#define RNG_CR_ENTROPY_SRC_MASK (RNG_CR_CONFIG1 | RNG_CR_NISTC | RNG_CR_CONFIG2 | RNG_CR_CONFIG3)
+#define RNG_CR_CONFIG_MASK (RNG_CR_ENTROPY_SRC_MASK | RNG_CR_CED | RNG_CR_CLKDIV)
+
+#define RNG_SR 0x04
+#define RNG_SR_DRDY BIT(0)
+#define RNG_SR_CECS BIT(1)
+#define RNG_SR_SECS BIT(2)
+#define RNG_SR_CEIS BIT(5)
+#define RNG_SR_SEIS BIT(6)
+
+#define RNG_DR 0x08
+
+#define RNG_NSCR 0x0C
+#define RNG_NSCR_MASK GENMASK(17, 0)
+
+#define RNG_HTCR 0x10
+
+#define RNG_NB_RECOVER_TRIES 3
+
+struct stm32_rng_data {
+ uint max_clock_rate;
+ uint nb_clock;
+ u32 cr;
+ u32 nscr;
+ u32 htcr;
+ bool has_cond_reset;
+};
-#define RNG_DR 0x08
+/**
+ * struct stm32_rng_config - RNG configuration data
+ *
+ * @cr: RNG configuration. 0 means default hardware RNG configuration
+ * @nscr: Noise sources control configuration.
+ * @htcr: Health tests configuration.
+ */
+struct stm32_rng_config {
+ u32 cr;
+ u32 nscr;
+ u32 htcr;
+};
struct stm32_rng_private {
struct hwrng rng;
+ struct device *dev;
void __iomem *base;
- struct clk *clk;
+ struct clk_bulk_data *clk_bulk;
struct reset_control *rst;
+ struct stm32_rng_config pm_conf;
+ const struct stm32_rng_data *data;
bool ced;
+ bool lock_conf;
};
+/*
+ * Extracts from the STM32 RNG specification when RNG supports CONDRST.
+ *
+ * When a noise source (or seed) error occurs, the RNG stops generating
+ * random numbers and sets to “1” both SEIS and SECS bits to indicate
+ * that a seed error occurred. (...)
+ *
+ * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield
+ * description for details). This step is needed only if SECS is set.
+ * Indeed, when SEIS is set and SECS is cleared it means RNG performed
+ * the reset automatically (auto-reset).
+ * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST
+ * to be cleared in the RNG_CR register, then confirm that SEIS is
+ * cleared in the RNG_SR register. Otherwise just clear SEIS bit in
+ * the RNG_SR register.
+ * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be
+ * cleared by RNG. The random number generation is now back to normal.
+ */
+static int stm32_rng_conceal_seed_error_cond_reset(struct stm32_rng_private *priv)
+{
+ struct device *dev = priv->dev;
+ u32 sr = readl_relaxed(priv->base + RNG_SR);
+ u32 cr = readl_relaxed(priv->base + RNG_CR);
+ int err;
+
+ if (sr & RNG_SR_SECS) {
+ /* Conceal by resetting the subsystem (step 1.) */
+ writel_relaxed(cr | RNG_CR_CONDRST, priv->base + RNG_CR);
+ writel_relaxed(cr & ~RNG_CR_CONDRST, priv->base + RNG_CR);
+ } else {
+ /* RNG auto-reset (step 2.) */
+ writel_relaxed(sr & ~RNG_SR_SEIS, priv->base + RNG_SR);
+ goto end;
+ }
+
+ err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_CR, cr, !(cr & RNG_CR_CONDRST), 10,
+ 100000);
+ if (err) {
+ dev_err(dev, "%s: timeout %x\n", __func__, sr);
+ return err;
+ }
+
+ /* Check SEIS is cleared (step 2.) */
+ if (readl_relaxed(priv->base + RNG_SR) & RNG_SR_SEIS)
+ return -EINVAL;
+
+ err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_SR, sr, !(sr & RNG_SR_SECS), 10,
+ 100000);
+ if (err) {
+ dev_err(dev, "%s: timeout %x\n", __func__, sr);
+ return err;
+ }
+
+end:
+ return 0;
+}
+
+/*
+ * Extracts from the STM32 RNG specification, when CONDRST is not supported
+ *
+ * When a noise source (or seed) error occurs, the RNG stops generating
+ * random numbers and sets to “1” both SEIS and SECS bits to indicate
+ * that a seed error occurred. (...)
+ *
+ * The following sequence shall be used to fully recover from a seed
+ * error after the RNG initialization:
+ * 1. Clear the SEIS bit by writing it to “0”.
+ * 2. Read out 12 words from the RNG_DR register, and discard each of
+ * them in order to clean the pipeline.
+ * 3. Confirm that SEIS is still cleared. Random number generation is
+ * back to normal.
+ */
+static int stm32_rng_conceal_seed_error_sw_reset(struct stm32_rng_private *priv)
+{
+ unsigned int i = 0;
+ u32 sr = readl_relaxed(priv->base + RNG_SR);
+
+ writel_relaxed(sr & ~RNG_SR_SEIS, priv->base + RNG_SR);
+
+ for (i = 12; i != 0; i--)
+ (void)readl_relaxed(priv->base + RNG_DR);
+
+ if (readl_relaxed(priv->base + RNG_SR) & RNG_SR_SEIS)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int stm32_rng_conceal_seed_error(struct hwrng *rng)
+{
+ struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng);
+
+ dev_dbg(priv->dev, "Concealing seed error\n");
+
+ if (priv->data->has_cond_reset)
+ return stm32_rng_conceal_seed_error_cond_reset(priv);
+ else
+ return stm32_rng_conceal_seed_error_sw_reset(priv);
+};
+
+
static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
- struct stm32_rng_private *priv =
- container_of(rng, struct stm32_rng_private, rng);
+ struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng);
+ unsigned int i = 0;
+ int retval = 0, err = 0;
u32 sr;
- int retval = 0;
- pm_runtime_get_sync((struct device *) priv->rng.priv);
+ retval = pm_runtime_resume_and_get(priv->dev);
+ if (retval)
+ return retval;
- while (max > sizeof(u32)) {
+ if (readl_relaxed(priv->base + RNG_SR) & RNG_SR_SEIS)
+ stm32_rng_conceal_seed_error(rng);
+
+ while (max >= sizeof(u32)) {
sr = readl_relaxed(priv->base + RNG_SR);
- /* Manage timeout which is based on timer and take */
- /* care of initial delay time when enabling rng */
+ /*
+ * Manage timeout which is based on timer and take
+ * care of initial delay time when enabling the RNG.
+ */
if (!sr && wait) {
- retval = readl_relaxed_poll_timeout_atomic(priv->base
+ err = readl_relaxed_poll_timeout_atomic(priv->base
+ RNG_SR,
sr, sr,
10, 50000);
- if (retval)
- dev_err((struct device *)priv->rng.priv,
- "%s: timeout %x!\n", __func__, sr);
+ if (err) {
+ dev_err(priv->dev, "%s: timeout %x!\n", __func__, sr);
+ break;
+ }
+ } else if (!sr) {
+ /* The FIFO is being filled up */
+ break;
}
- /* If error detected or data not ready... */
if (sr != RNG_SR_DRDY) {
- if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS),
- "bad RNG status - %x\n", sr))
+ if (sr & RNG_SR_SEIS) {
+ err = stm32_rng_conceal_seed_error(rng);
+ i++;
+ if (err && i > RNG_NB_RECOVER_TRIES) {
+ dev_err(priv->dev, "Couldn't recover from seed error\n");
+ retval = -ENOTRECOVERABLE;
+ goto exit_rpm;
+ }
+
+ continue;
+ }
+
+ if (WARN_ONCE((sr & RNG_SR_CEIS), "RNG clock too slow - %x\n", sr))
writel_relaxed(0, priv->base + RNG_SR);
- break;
}
+ /* Late seed error case: DR being 0 is an error status */
*(u32 *)data = readl_relaxed(priv->base + RNG_DR);
+ if (!*(u32 *)data) {
+ err = stm32_rng_conceal_seed_error(rng);
+ i++;
+ if (err && i > RNG_NB_RECOVER_TRIES) {
+ dev_err(priv->dev, "Couldn't recover from seed error");
+ retval = -ENOTRECOVERABLE;
+ goto exit_rpm;
+ }
+
+ continue;
+ }
+ i = 0;
retval += sizeof(u32);
data += sizeof(u32);
max -= sizeof(u32);
}
- pm_runtime_mark_last_busy((struct device *) priv->rng.priv);
- pm_runtime_put_sync_autosuspend((struct device *) priv->rng.priv);
+exit_rpm:
+ pm_runtime_put_sync_autosuspend(priv->dev);
return retval || !wait ? retval : -EIO;
}
+static uint stm32_rng_clock_freq_restrain(struct hwrng *rng)
+{
+ struct stm32_rng_private *priv =
+ container_of(rng, struct stm32_rng_private, rng);
+ unsigned long clock_rate = 0;
+ uint clock_div = 0;
+
+ clock_rate = clk_get_rate(priv->clk_bulk[0].clk);
+
+ /*
+ * Get the exponent to apply on the CLKDIV field in RNG_CR register
+ * No need to handle the case when clock-div > 0xF as it is physically
+ * impossible
+ */
+ while ((clock_rate >> clock_div) > priv->data->max_clock_rate)
+ clock_div++;
+
+ pr_debug("RNG clk rate : %lu\n", clk_get_rate(priv->clk_bulk[0].clk) >> clock_div);
+
+ return clock_div;
+}
+
static int stm32_rng_init(struct hwrng *rng)
{
struct stm32_rng_private *priv =
container_of(rng, struct stm32_rng_private, rng);
int err;
+ u32 reg;
- err = clk_prepare_enable(priv->clk);
+ err = clk_bulk_prepare_enable(priv->data->nb_clock, priv->clk_bulk);
if (err)
return err;
- if (priv->ced)
- writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR);
- else
- writel_relaxed(RNG_CR_RNGEN | RNG_CR_CED,
- priv->base + RNG_CR);
-
/* clear error indicators */
writel_relaxed(0, priv->base + RNG_SR);
+ reg = readl_relaxed(priv->base + RNG_CR);
+
+ /*
+ * Keep default RNG configuration if none was specified.
+ * 0 is an invalid value as it disables all entropy sources.
+ */
+ if (priv->data->has_cond_reset && priv->data->cr) {
+ uint clock_div = stm32_rng_clock_freq_restrain(rng);
+
+ reg &= ~RNG_CR_CONFIG_MASK;
+ reg |= RNG_CR_CONDRST | (priv->data->cr & RNG_CR_ENTROPY_SRC_MASK) |
+ (clock_div << RNG_CR_CLKDIV_SHIFT);
+ if (priv->ced)
+ reg &= ~RNG_CR_CED;
+ else
+ reg |= RNG_CR_CED;
+ writel_relaxed(reg, priv->base + RNG_CR);
+
+ /* Health tests and noise control registers */
+ writel_relaxed(priv->data->htcr, priv->base + RNG_HTCR);
+ writel_relaxed(priv->data->nscr & RNG_NSCR_MASK, priv->base + RNG_NSCR);
+
+ reg &= ~RNG_CR_CONDRST;
+ reg |= RNG_CR_RNGEN;
+ if (priv->lock_conf)
+ reg |= RNG_CR_CONFLOCK;
+
+ writel_relaxed(reg, priv->base + RNG_CR);
+
+ err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_CR, reg,
+ (!(reg & RNG_CR_CONDRST)),
+ 10, 50000);
+ if (err) {
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
+ dev_err(priv->dev, "%s: timeout %x!\n", __func__, reg);
+ return -EINVAL;
+ }
+ } else {
+ /* Handle all RNG versions by checking if conditional reset should be set */
+ if (priv->data->has_cond_reset)
+ reg |= RNG_CR_CONDRST;
+
+ if (priv->ced)
+ reg &= ~RNG_CR_CED;
+ else
+ reg |= RNG_CR_CED;
+
+ writel_relaxed(reg, priv->base + RNG_CR);
+
+ if (priv->data->has_cond_reset)
+ reg &= ~RNG_CR_CONDRST;
+
+ reg |= RNG_CR_RNGEN;
+
+ writel_relaxed(reg, priv->base + RNG_CR);
+ }
+
+ err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_SR, reg,
+ reg & RNG_SR_DRDY,
+ 10, 100000);
+ if (err || (reg & ~RNG_SR_DRDY)) {
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
+ dev_err(priv->dev, "%s: timeout:%x SR: %x!\n", __func__, err, reg);
+
+ return -EINVAL;
+ }
+
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
+
return 0;
}
-static void stm32_rng_cleanup(struct hwrng *rng)
+static void stm32_rng_remove(struct platform_device *ofdev)
{
- struct stm32_rng_private *priv =
- container_of(rng, struct stm32_rng_private, rng);
+ pm_runtime_disable(&ofdev->dev);
+}
+
+static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev)
+{
+ struct stm32_rng_private *priv = dev_get_drvdata(dev);
+ u32 reg;
- writel_relaxed(0, priv->base + RNG_CR);
- clk_disable_unprepare(priv->clk);
+ reg = readl_relaxed(priv->base + RNG_CR);
+ reg &= ~RNG_CR_RNGEN;
+ writel_relaxed(reg, priv->base + RNG_CR);
+
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
+
+ return 0;
}
-static int stm32_rng_probe(struct platform_device *ofdev)
+static int __maybe_unused stm32_rng_suspend(struct device *dev)
{
- struct device *dev = &ofdev->dev;
- struct device_node *np = ofdev->dev.of_node;
- struct stm32_rng_private *priv;
- struct resource res;
+ struct stm32_rng_private *priv = dev_get_drvdata(dev);
int err;
- priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- err = of_address_to_resource(np, 0, &res);
+ err = clk_bulk_prepare_enable(priv->data->nb_clock, priv->clk_bulk);
if (err)
return err;
- priv->base = devm_ioremap_resource(dev, &res);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
-
- priv->clk = devm_clk_get(&ofdev->dev, NULL);
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
-
- priv->rst = devm_reset_control_get(&ofdev->dev, NULL);
- if (!IS_ERR(priv->rst)) {
- reset_control_assert(priv->rst);
- udelay(2);
- reset_control_deassert(priv->rst);
+ if (priv->data->has_cond_reset) {
+ priv->pm_conf.nscr = readl_relaxed(priv->base + RNG_NSCR);
+ priv->pm_conf.htcr = readl_relaxed(priv->base + RNG_HTCR);
}
- priv->ced = of_property_read_bool(np, "clock-error-detect");
+ /* Do not save that RNG is enabled as it will be handled at resume */
+ priv->pm_conf.cr = readl_relaxed(priv->base + RNG_CR) & ~RNG_CR_RNGEN;
- dev_set_drvdata(dev, priv);
+ writel_relaxed(priv->pm_conf.cr, priv->base + RNG_CR);
- priv->rng.name = dev_driver_string(dev);
-#ifndef CONFIG_PM
- priv->rng.init = stm32_rng_init;
- priv->rng.cleanup = stm32_rng_cleanup;
-#endif
- priv->rng.read = stm32_rng_read;
- priv->rng.priv = (unsigned long) dev;
- priv->rng.quality = 900;
-
- pm_runtime_set_autosuspend_delay(dev, 100);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_enable(dev);
-
- return devm_hwrng_register(dev, &priv->rng);
-}
-
-static int stm32_rng_remove(struct platform_device *ofdev)
-{
- pm_runtime_disable(&ofdev->dev);
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
return 0;
}
-#ifdef CONFIG_PM
-static int stm32_rng_runtime_suspend(struct device *dev)
+static int __maybe_unused stm32_rng_runtime_resume(struct device *dev)
{
struct stm32_rng_private *priv = dev_get_drvdata(dev);
+ int err;
+ u32 reg;
- stm32_rng_cleanup(&priv->rng);
+ err = clk_bulk_prepare_enable(priv->data->nb_clock, priv->clk_bulk);
+ if (err)
+ return err;
+
+ /* Clean error indications */
+ writel_relaxed(0, priv->base + RNG_SR);
+
+ reg = readl_relaxed(priv->base + RNG_CR);
+ reg |= RNG_CR_RNGEN;
+ writel_relaxed(reg, priv->base + RNG_CR);
return 0;
}
-static int stm32_rng_runtime_resume(struct device *dev)
+static int __maybe_unused stm32_rng_resume(struct device *dev)
{
struct stm32_rng_private *priv = dev_get_drvdata(dev);
+ int err;
+ u32 reg;
+
+ err = clk_bulk_prepare_enable(priv->data->nb_clock, priv->clk_bulk);
+ if (err)
+ return err;
+
+ /* Clean error indications */
+ writel_relaxed(0, priv->base + RNG_SR);
+
+ if (priv->data->has_cond_reset) {
+ /*
+ * Correct configuration in bits [29:4] must be set in the same
+ * access that set RNG_CR_CONDRST bit. Else config setting is
+ * not taken into account. CONFIGLOCK bit must also be unset but
+ * it is not handled at the moment.
+ */
+ writel_relaxed(priv->pm_conf.cr | RNG_CR_CONDRST, priv->base + RNG_CR);
+
+ writel_relaxed(priv->pm_conf.nscr, priv->base + RNG_NSCR);
+ writel_relaxed(priv->pm_conf.htcr, priv->base + RNG_HTCR);
+
+ reg = readl_relaxed(priv->base + RNG_CR);
+ reg |= RNG_CR_RNGEN;
+ reg &= ~RNG_CR_CONDRST;
+ writel_relaxed(reg, priv->base + RNG_CR);
+
+ err = readl_relaxed_poll_timeout_atomic(priv->base + RNG_CR, reg,
+ reg & ~RNG_CR_CONDRST, 10, 100000);
+
+ if (err) {
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
+ dev_err(priv->dev, "%s: timeout:%x CR: %x!\n", __func__, err, reg);
+ return -EINVAL;
+ }
+ } else {
+ reg = priv->pm_conf.cr;
+ reg |= RNG_CR_RNGEN;
+ writel_relaxed(reg, priv->base + RNG_CR);
+ }
+
+ clk_bulk_disable_unprepare(priv->data->nb_clock, priv->clk_bulk);
- return stm32_rng_init(&priv->rng);
+ return 0;
}
-#endif
-static const struct dev_pm_ops stm32_rng_pm_ops = {
+static const struct dev_pm_ops __maybe_unused stm32_rng_pm_ops = {
SET_RUNTIME_PM_OPS(stm32_rng_runtime_suspend,
stm32_rng_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_rng_suspend,
+ stm32_rng_resume)
};
+static const struct stm32_rng_data stm32mp25_rng_data = {
+ .has_cond_reset = true,
+ .max_clock_rate = 48000000,
+ .nb_clock = 2,
+ .cr = 0x00F00D00,
+ .nscr = 0x2B5BB,
+ .htcr = 0x969D,
+};
+
+static const struct stm32_rng_data stm32mp13_rng_data = {
+ .has_cond_reset = true,
+ .max_clock_rate = 48000000,
+ .nb_clock = 1,
+ .cr = 0x00F00D00,
+ .nscr = 0x2B5BB,
+ .htcr = 0x969D,
+};
+
+static const struct stm32_rng_data stm32_rng_data = {
+ .has_cond_reset = false,
+ .max_clock_rate = 48000000,
+ .nb_clock = 1,
+};
static const struct of_device_id stm32_rng_match[] = {
{
+ .compatible = "st,stm32mp25-rng",
+ .data = &stm32mp25_rng_data,
+ },
+ {
+ .compatible = "st,stm32mp13-rng",
+ .data = &stm32mp13_rng_data,
+ },
+ {
.compatible = "st,stm32-rng",
+ .data = &stm32_rng_data,
},
{},
};
MODULE_DEVICE_TABLE(of, stm32_rng_match);
+static int stm32_rng_probe(struct platform_device *ofdev)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *np = ofdev->dev.of_node;
+ struct stm32_rng_private *priv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_get_and_ioremap_resource(ofdev, 0, &res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->rst = devm_reset_control_get(&ofdev->dev, NULL);
+ if (!IS_ERR(priv->rst)) {
+ reset_control_assert(priv->rst);
+ udelay(2);
+ reset_control_deassert(priv->rst);
+ }
+
+ priv->ced = of_property_read_bool(np, "clock-error-detect");
+ priv->lock_conf = of_property_read_bool(np, "st,rng-lock-conf");
+ priv->dev = dev;
+
+ priv->data = of_device_get_match_data(dev);
+ if (!priv->data)
+ return -ENODEV;
+
+ dev_set_drvdata(dev, priv);
+
+ priv->rng.name = dev_driver_string(dev);
+ priv->rng.init = stm32_rng_init;
+ priv->rng.read = stm32_rng_read;
+ priv->rng.quality = 900;
+
+ if (!priv->data->nb_clock || priv->data->nb_clock > 2)
+ return -EINVAL;
+
+ ret = devm_clk_bulk_get_all(dev, &priv->clk_bulk);
+ if (ret != priv->data->nb_clock)
+ return dev_err_probe(dev, -EINVAL, "Failed to get clocks: %d\n", ret);
+
+ if (priv->data->nb_clock == 2) {
+ const char *id = priv->clk_bulk[1].id;
+ struct clk *clk = priv->clk_bulk[1].clk;
+
+ if (!priv->clk_bulk[0].id || !priv->clk_bulk[1].id)
+ return dev_err_probe(dev, -EINVAL, "Missing clock name\n");
+
+ if (strcmp(priv->clk_bulk[0].id, "core")) {
+ priv->clk_bulk[1].id = priv->clk_bulk[0].id;
+ priv->clk_bulk[1].clk = priv->clk_bulk[0].clk;
+ priv->clk_bulk[0].id = id;
+ priv->clk_bulk[0].clk = clk;
+ }
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ return devm_hwrng_register(dev, &priv->rng);
+}
+
static struct platform_driver stm32_rng_driver = {
.driver = {
.name = "stm32-rng",
- .pm = &stm32_rng_pm_ops,
+ .pm = pm_ptr(&stm32_rng_pm_ops),
.of_match_table = stm32_rng_match,
},
.probe = stm32_rng_probe,
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index 8ea1fc831eb7..e61f06393209 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -113,16 +113,6 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
-
- if (res->start % 4 != 0 || resource_size(res) < 4) {
- dev_err(&pdev->dev,
- "address must be at least four bytes wide and 32-bit aligned\n");
- return -EINVAL;
- }
-
/* Allocate memory for the device structure (and zero it) */
priv = devm_kzalloc(&pdev->dev,
sizeof(struct timeriomem_rng_private), GFP_KERNEL);
@@ -131,6 +121,16 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
+ priv->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->io_base))
+ return PTR_ERR(priv->io_base);
+
+ if (res->start % 4 != 0 || resource_size(res) < 4) {
+ dev_err(&pdev->dev,
+ "address must be at least four bytes wide and 32-bit aligned\n");
+ return -EINVAL;
+ }
+
if (pdev->dev.of_node) {
int i;
@@ -145,26 +145,18 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node,
"quality", &i))
priv->rng_ops.quality = i;
- else
- priv->rng_ops.quality = 0;
} else {
period = pdata->period;
priv->rng_ops.quality = pdata->quality;
}
- priv->period = ns_to_ktime(period * NSEC_PER_USEC);
+ priv->period = us_to_ktime(period);
init_completion(&priv->completion);
- hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- priv->timer.function = timeriomem_rng_trigger;
+ hrtimer_setup(&priv->timer, timeriomem_rng_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
priv->rng_ops.name = dev_name(&pdev->dev);
priv->rng_ops.read = timeriomem_rng_read;
- priv->io_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(priv->io_base)) {
- return PTR_ERR(priv->io_base);
- }
-
/* Assume random data is already available. */
priv->present = 1;
complete(&priv->completion);
@@ -181,13 +173,11 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
return 0;
}
-static int timeriomem_rng_remove(struct platform_device *pdev)
+static void timeriomem_rng_remove(struct platform_device *pdev)
{
struct timeriomem_rng_private *priv = platform_get_drvdata(pdev);
hrtimer_cancel(&priv->timer);
-
- return 0;
}
static const struct of_device_id timeriomem_rng_match[] = {
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index a6f3a8a2aca6..dd998f4fe4f2 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -4,6 +4,7 @@
* Copyright (C) 2007, 2008 Rusty Russell IBM Corporation
*/
+#include <asm/barrier.h>
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/scatterlist.h>
@@ -37,13 +38,13 @@ struct virtrng_info {
static void random_recv_done(struct virtqueue *vq)
{
struct virtrng_info *vi = vq->vdev->priv;
+ unsigned int len;
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
- if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
+ if (!virtqueue_get_buf(vi->vq, &len))
return;
- vi->data_idx = 0;
-
+ smp_store_release(&vi->data_avail, len);
complete(&vi->have_data);
}
@@ -52,7 +53,6 @@ static void request_entropy(struct virtrng_info *vi)
struct scatterlist sg;
reinit_completion(&vi->have_data);
- vi->data_avail = 0;
vi->data_idx = 0;
sg_init_one(&sg, vi->data, sizeof(vi->data));
@@ -88,7 +88,7 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
read = 0;
/* copy available data */
- if (vi->data_avail) {
+ if (smp_load_acquire(&vi->data_avail)) {
chunk = copy_data(vi, buf, size);
size -= chunk;
read += chunk;
@@ -135,7 +135,7 @@ static int probe_common(struct virtio_device *vdev)
if (!vi)
return -ENOMEM;
- vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
+ vi->index = index = ida_alloc(&rng_index_ida, GFP_KERNEL);
if (index < 0) {
err = index;
goto err_ida;
@@ -148,7 +148,6 @@ static int probe_common(struct virtio_device *vdev)
.cleanup = virtio_cleanup,
.priv = (unsigned long)vi,
.name = vi->name,
- .quality = 1000,
};
vdev->priv = vi;
@@ -167,7 +166,7 @@ static int probe_common(struct virtio_device *vdev)
return 0;
err_find:
- ida_simple_remove(&rng_index_ida, index);
+ ida_free(&rng_index_ida, index);
err_ida:
kfree(vi);
return err;
@@ -185,7 +184,7 @@ static void remove_common(struct virtio_device *vdev)
hwrng_unregister(&vi->hwrng);
virtio_reset_device(vdev);
vdev->config->del_vqs(vdev);
- ida_simple_remove(&rng_index_ida, vi->index);
+ ida_free(&rng_index_ida, vi->index);
kfree(vi);
}
@@ -209,7 +208,6 @@ static void virtrng_scan(struct virtio_device *vdev)
vi->hwrng_register_done = true;
}
-#ifdef CONFIG_PM_SLEEP
static int virtrng_freeze(struct virtio_device *vdev)
{
remove_common(vdev);
@@ -239,7 +237,6 @@ static int virtrng_restore(struct virtio_device *vdev)
return err;
}
-#endif
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
@@ -248,15 +245,12 @@ static const struct virtio_device_id id_table[] = {
static struct virtio_driver virtio_rng_driver = {
.driver.name = KBUILD_MODNAME,
- .driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtrng_probe,
.remove = virtrng_remove,
.scan = virtrng_scan,
-#ifdef CONFIG_PM_SLEEP
- .freeze = virtrng_freeze,
- .restore = virtrng_restore,
-#endif
+ .freeze = pm_sleep_ptr(virtrng_freeze),
+ .restore = pm_sleep_ptr(virtrng_restore),
};
module_virtio_driver(virtio_rng_driver);
diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c
index 008e6db9ce01..709a36507145 100644
--- a/drivers/char/hw_random/xgene-rng.c
+++ b/drivers/char/hw_random/xgene-rng.c
@@ -14,10 +14,10 @@
#include <linux/hw_random.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
-#include <linux/of_address.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
#include <linux/timer.h>
#define RNG_MAX_DATUM 4
@@ -84,17 +84,16 @@ struct xgene_rng_dev {
unsigned long failure_ts;/* First failure timestamp */
struct timer_list failure_timer;
struct device *dev;
- struct clk *clk;
};
static void xgene_rng_expired_timer(struct timer_list *t)
{
- struct xgene_rng_dev *ctx = from_timer(ctx, t, failure_timer);
+ struct xgene_rng_dev *ctx = timer_container_of(ctx, t, failure_timer);
/* Clear failure counter as timer expired */
disable_irq(ctx->irq);
ctx->failure_cnt = 0;
- del_timer(&ctx->failure_timer);
+ timer_delete(&ctx->failure_timer);
enable_irq(ctx->irq);
}
@@ -200,7 +199,7 @@ static void xgene_rng_chk_overflow(struct xgene_rng_dev *ctx)
static irqreturn_t xgene_rng_irq_handler(int irq, void *id)
{
- struct xgene_rng_dev *ctx = (struct xgene_rng_dev *) id;
+ struct xgene_rng_dev *ctx = id;
/* RNG Alarm Counter overflow */
xgene_rng_chk_overflow(ctx);
@@ -314,6 +313,7 @@ static struct hwrng xgene_rng_func = {
static int xgene_rng_probe(struct platform_device *pdev)
{
struct xgene_rng_dev *ctx;
+ struct clk *clk;
int rc = 0;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
@@ -321,7 +321,6 @@ static int xgene_rng_probe(struct platform_device *pdev)
return -ENOMEM;
ctx->dev = &pdev->dev;
- platform_set_drvdata(pdev, ctx);
ctx->csr_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ctx->csr_base))
@@ -337,58 +336,34 @@ static int xgene_rng_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, ctx->irq, xgene_rng_irq_handler, 0,
dev_name(&pdev->dev), ctx);
- if (rc) {
- dev_err(&pdev->dev, "Could not request RNG alarm IRQ\n");
- return rc;
- }
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc, "Could not request RNG alarm IRQ\n");
/* Enable IP clock */
- ctx->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(ctx->clk)) {
- dev_warn(&pdev->dev, "Couldn't get the clock for RNG\n");
- } else {
- rc = clk_prepare_enable(ctx->clk);
- if (rc) {
- dev_warn(&pdev->dev,
- "clock prepare enable failed for RNG");
- return rc;
- }
- }
+ clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Couldn't get the clock for RNG\n");
xgene_rng_func.priv = (unsigned long) ctx;
rc = devm_hwrng_register(&pdev->dev, &xgene_rng_func);
- if (rc) {
- dev_err(&pdev->dev, "RNG registering failed error %d\n", rc);
- if (!IS_ERR(ctx->clk))
- clk_disable_unprepare(ctx->clk);
- return rc;
- }
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc, "RNG registering failed\n");
rc = device_init_wakeup(&pdev->dev, 1);
- if (rc) {
- dev_err(&pdev->dev, "RNG device_init_wakeup failed error %d\n",
- rc);
- if (!IS_ERR(ctx->clk))
- clk_disable_unprepare(ctx->clk);
- return rc;
- }
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc, "RNG device_init_wakeup failed\n");
return 0;
}
-static int xgene_rng_remove(struct platform_device *pdev)
+static void xgene_rng_remove(struct platform_device *pdev)
{
- struct xgene_rng_dev *ctx = platform_get_drvdata(pdev);
int rc;
rc = device_init_wakeup(&pdev->dev, 0);
if (rc)
dev_err(&pdev->dev, "RNG init wakeup failed error %d\n", rc);
- if (!IS_ERR(ctx->clk))
- clk_disable_unprepare(ctx->clk);
-
- return rc;
}
static const struct of_device_id xgene_rng_of_match[] = {
@@ -400,7 +375,7 @@ MODULE_DEVICE_TABLE(of, xgene_rng_of_match);
static struct platform_driver xgene_rng_driver = {
.probe = xgene_rng_probe,
- .remove = xgene_rng_remove,
+ .remove = xgene_rng_remove,
.driver = {
.name = "xgene-rng",
.of_match_table = xgene_rng_of_match,
diff --git a/drivers/char/hw_random/xiphera-trng.c b/drivers/char/hw_random/xiphera-trng.c
index 2a9fea72b2e0..4af64f76c8d6 100644
--- a/drivers/char/hw_random/xiphera-trng.c
+++ b/drivers/char/hw_random/xiphera-trng.c
@@ -7,7 +7,6 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/hw_random.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
@@ -122,8 +121,6 @@ static int xiphera_trng_probe(struct platform_device *pdev)
return ret;
}
- platform_set_drvdata(pdev, trng);
-
return 0;
}
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index b061e6b513ed..92bed266d07c 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -84,6 +84,13 @@ config IPMI_IPMB
bus, and it also supports direct messaging on the bus using
IPMB direct messages. This module requires I2C support.
+config IPMI_LS2K
+ bool 'Loongson-2K IPMI interface'
+ depends on LOONGARCH
+ select MFD_LS2K_BMC_CORE
+ help
+ Provides a driver for Loongson-2K IPMI interfaces.
+
config IPMI_POWERNV
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'
@@ -119,13 +126,13 @@ config ASPEED_KCS_IPMI_BMC
provides the access of KCS IO space for BMC side.
config NPCM7XX_KCS_IPMI_BMC
- depends on ARCH_NPCM7XX || COMPILE_TEST
+ depends on ARCH_NPCM || COMPILE_TEST
select IPMI_KCS_BMC
select REGMAP_MMIO
- tristate "NPCM7xx KCS IPMI BMC driver"
+ tristate "NPCM KCS IPMI BMC driver"
help
Provides a driver for the KCS (Keyboard Controller Style) IPMI
- interface found on Nuvoton NPCM7xx SOCs.
+ interface found on Nuvoton NPCM SOCs.
The driver implements the BMC side of the KCS contorller, it
provides the access of KCS IO space for BMC side.
@@ -162,13 +169,24 @@ config IPMI_KCS_BMC_SERIO
config ASPEED_BT_IPMI_BMC
depends on ARCH_ASPEED || COMPILE_TEST
- depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
+ depends on MFD_SYSCON
+ select REGMAP_MMIO
tristate "BT IPMI bmc driver"
help
Provides a driver for the BT (Block Transfer) IPMI interface
found on Aspeed SOCs (AST2400 and AST2500). The driver
implements the BMC side of the BT interface.
+config SSIF_IPMI_BMC
+ tristate "SSIF IPMI BMC driver"
+ depends on I2C && I2C_SLAVE
+ help
+ This enables the IPMI SMBus system interface (SSIF) at the
+ management (BMC) side.
+
+ The driver implements the BMC side of the SMBus system
+ interface (SSIF).
+
config IPMB_DEVICE_INTERFACE
tristate 'IPMB Interface handler'
depends on I2C
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 7ce790efad92..4ea450a82242 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -5,13 +5,11 @@
ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \
ipmi_si_hotmod.o ipmi_si_hardcode.o ipmi_si_platform.o \
- ipmi_si_port_io.o ipmi_si_mem_io.o
-ifdef CONFIG_PCI
-ipmi_si-y += ipmi_si_pci.o
-endif
-ifdef CONFIG_PARISC
-ipmi_si-y += ipmi_si_parisc.o
-endif
+ ipmi_si_mem_io.o
+ipmi_si-$(CONFIG_HAS_IOPORT) += ipmi_si_port_io.o
+ipmi_si-$(CONFIG_PCI) += ipmi_si_pci.o
+ipmi_si-$(CONFIG_IPMI_LS2K) += ipmi_si_ls2k.o
+ipmi_si-$(CONFIG_PARISC) += ipmi_si_parisc.o
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
@@ -30,3 +28,4 @@ obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o
+obj-$(CONFIG_SSIF_IPMI_BMC) += ssif_bmc.o
diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
index 7450904e330a..a179d4797011 100644
--- a/drivers/char/ipmi/bt-bmc.c
+++ b/drivers/char/ipmi/bt-bmc.c
@@ -347,7 +347,7 @@ static const struct file_operations bt_bmc_fops = {
static void poll_timer(struct timer_list *t)
{
- struct bt_bmc *bt_bmc = from_timer(bt_bmc, t, poll_timer);
+ struct bt_bmc *bt_bmc = timer_container_of(bt_bmc, t, poll_timer);
bt_bmc->poll_timer.expires += msecs_to_jiffies(500);
wake_up(&bt_bmc->queue);
@@ -459,14 +459,13 @@ static int bt_bmc_probe(struct platform_device *pdev)
return 0;
}
-static int bt_bmc_remove(struct platform_device *pdev)
+static void bt_bmc_remove(struct platform_device *pdev)
{
struct bt_bmc *bt_bmc = dev_get_drvdata(&pdev->dev);
misc_deregister(&bt_bmc->miscdev);
if (bt_bmc->irq < 0)
- del_timer_sync(&bt_bmc->poll_timer);
- return 0;
+ timer_delete_sync(&bt_bmc->poll_timer);
}
static const struct of_device_id bt_bmc_match[] = {
diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c
index db40037eb347..ee2bdc7ed0da 100644
--- a/drivers/char/ipmi/ipmb_dev_int.c
+++ b/drivers/char/ipmi/ipmb_dev_int.c
@@ -321,6 +321,9 @@ static int ipmb_probe(struct i2c_client *client)
ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL,
"%s%d", "ipmb-",
client->adapter->nr);
+ if (!ipmb_dev->miscdev.name)
+ return -ENOMEM;
+
ipmb_dev->miscdev.fops = &ipmb_fops;
ipmb_dev->miscdev.parent = &client->dev;
ret = misc_register(&ipmb_dev->miscdev);
@@ -341,34 +344,34 @@ static int ipmb_probe(struct i2c_client *client)
return 0;
}
-static int ipmb_remove(struct i2c_client *client)
+static void ipmb_remove(struct i2c_client *client)
{
struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client);
i2c_slave_unregister(client);
misc_deregister(&ipmb_dev->miscdev);
-
- return 0;
}
static const struct i2c_device_id ipmb_id[] = {
- { "ipmb-dev", 0 },
- {},
+ { "ipmb-dev" },
+ {}
};
MODULE_DEVICE_TABLE(i2c, ipmb_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id acpi_ipmb_id[] = {
{ "IPMB0001", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id);
+#endif
static struct i2c_driver ipmb_driver = {
.driver = {
.name = "ipmb-dev",
.acpi_match_table = ACPI_PTR(acpi_ipmb_id),
},
- .probe_new = ipmb_probe,
+ .probe = ipmb_probe,
.remove = ipmb_remove,
.id_table = ipmb_id,
};
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index d160fa4c73fe..e6ba35b71f10 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -122,12 +122,9 @@ out:
static int ipmi_release(struct inode *inode, struct file *file)
{
struct ipmi_file_private *priv = file->private_data;
- int rv;
struct ipmi_recv_msg *msg, *next;
- rv = ipmi_destroy_user(priv->user);
- if (rv)
- return rv;
+ ipmi_destroy_user(priv->user);
list_for_each_entry_safe(msg, next, &priv->recv_msgs, link)
ipmi_free_recv_msg(msg);
@@ -807,7 +804,9 @@ struct ipmi_reg_list {
static LIST_HEAD(reg_list);
static DEFINE_MUTEX(reg_list_mutex);
-static struct class *ipmi_class;
+static const struct class ipmi_class = {
+ .name = "ipmi",
+};
static void ipmi_new_smi(int if_num, struct device *device)
{
@@ -822,7 +821,7 @@ static void ipmi_new_smi(int if_num, struct device *device)
entry->dev = dev;
mutex_lock(&reg_list_mutex);
- device_create(ipmi_class, device, dev, NULL, "ipmi%d", if_num);
+ device_create(&ipmi_class, device, dev, NULL, "ipmi%d", if_num);
list_add(&entry->link, &reg_list);
mutex_unlock(&reg_list_mutex);
}
@@ -840,7 +839,7 @@ static void ipmi_smi_gone(int if_num)
break;
}
}
- device_destroy(ipmi_class, dev);
+ device_destroy(&ipmi_class, dev);
mutex_unlock(&reg_list_mutex);
}
@@ -860,15 +859,13 @@ static int __init init_ipmi_devintf(void)
pr_info("ipmi device interface\n");
- ipmi_class = class_create(THIS_MODULE, "ipmi");
- if (IS_ERR(ipmi_class)) {
- pr_err("ipmi: can't register device class\n");
- return PTR_ERR(ipmi_class);
- }
+ rv = class_register(&ipmi_class);
+ if (rv)
+ return rv;
rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
if (rv < 0) {
- class_destroy(ipmi_class);
+ class_unregister(&ipmi_class);
pr_err("ipmi: can't get major %d\n", ipmi_major);
return rv;
}
@@ -880,7 +877,7 @@ static int __init init_ipmi_devintf(void)
rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) {
unregister_chrdev(ipmi_major, DEVICE_NAME);
- class_destroy(ipmi_class);
+ class_unregister(&ipmi_class);
pr_warn("ipmi: can't register smi watcher\n");
return rv;
}
@@ -895,11 +892,11 @@ static void __exit cleanup_ipmi(void)
mutex_lock(&reg_list_mutex);
list_for_each_entry_safe(entry, entry2, &reg_list, link) {
list_del(&entry->link);
- device_destroy(ipmi_class, entry->dev);
+ device_destroy(&ipmi_class, entry->dev);
kfree(entry);
}
mutex_unlock(&reg_list_mutex);
- class_destroy(ipmi_class);
+ class_unregister(&ipmi_class);
ipmi_smi_watcher_unregister(&smi_watcher);
unregister_chrdev(ipmi_major, DEVICE_NAME);
}
diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c
index ab19b4b3317e..3a51e58b2487 100644
--- a/drivers/char/ipmi/ipmi_ipmb.c
+++ b/drivers/char/ipmi/ipmi_ipmb.c
@@ -27,7 +27,7 @@ MODULE_PARM_DESC(bmcaddr, "Address to use for BMC.");
static unsigned int retry_time_ms = 250;
module_param(retry_time_ms, uint, 0644);
-MODULE_PARM_DESC(max_retries, "Timeout time between retries, in milliseconds.");
+MODULE_PARM_DESC(retry_time_ms, "Timeout time between retries, in milliseconds.");
static unsigned int max_retries = 1;
module_param(max_retries, uint, 0644);
@@ -218,8 +218,8 @@ static void ipmi_ipmb_send_response(struct ipmi_ipmb_dev *iidev,
{
if ((msg->data[0] >> 2) & 1) {
/*
- * It's a response being sent, we needto return a
- * response response. Fake a send msg command
+ * It's a response being sent, we need to return a
+ * response to the response. Fake a send msg command
* response with channel 0. This will always be ipmb
* direct.
*/
@@ -404,8 +404,7 @@ static void ipmi_ipmb_shutdown(void *send_info)
ipmi_ipmb_stop_thread(iidev);
}
-static void ipmi_ipmb_sender(void *send_info,
- struct ipmi_smi_msg *msg)
+static int ipmi_ipmb_sender(void *send_info, struct ipmi_smi_msg *msg)
{
struct ipmi_ipmb_dev *iidev = send_info;
unsigned long flags;
@@ -417,6 +416,7 @@ static void ipmi_ipmb_sender(void *send_info,
spin_unlock_irqrestore(&iidev->lock, flags);
up(&iidev->wake_thread);
+ return IPMI_CC_NO_ERROR;
}
static void ipmi_ipmb_request_events(void *send_info)
@@ -424,10 +424,8 @@ static void ipmi_ipmb_request_events(void *send_info)
/* We don't fetch events here. */
}
-static int ipmi_ipmb_remove(struct i2c_client *client)
+static void ipmi_ipmb_cleanup(struct ipmi_ipmb_dev *iidev)
{
- struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
-
if (iidev->slave) {
i2c_slave_unregister(iidev->slave);
if (iidev->slave != iidev->client)
@@ -436,10 +434,14 @@ static int ipmi_ipmb_remove(struct i2c_client *client)
iidev->slave = NULL;
iidev->client = NULL;
ipmi_ipmb_stop_thread(iidev);
+}
- ipmi_unregister_smi(iidev->intf);
+static void ipmi_ipmb_remove(struct i2c_client *client)
+{
+ struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
- return 0;
+ ipmi_ipmb_cleanup(iidev);
+ ipmi_unregister_smi(iidev->intf);
}
static int ipmi_ipmb_probe(struct i2c_client *client)
@@ -544,7 +546,7 @@ static int ipmi_ipmb_probe(struct i2c_client *client)
out_err:
if (slave && slave != client)
i2c_unregister_device(slave);
- ipmi_ipmb_remove(client);
+ ipmi_ipmb_cleanup(iidev);
return rv;
}
@@ -559,8 +561,8 @@ MODULE_DEVICE_TABLE(of, of_ipmi_ipmb_match);
#endif
static const struct i2c_device_id ipmi_ipmb_id[] = {
- { DEVICE_NAME, 0 },
- {},
+ { DEVICE_NAME },
+ {}
};
MODULE_DEVICE_TABLE(i2c, ipmi_ipmb_id);
@@ -570,7 +572,7 @@ static struct i2c_driver ipmi_ipmb_driver = {
.name = DEVICE_NAME,
.of_match_table = of_ipmi_ipmb_match,
},
- .probe_new = ipmi_ipmb_probe,
+ .probe = ipmi_ipmb_probe,
.remove = ipmi_ipmb_remove,
.id_table = ipmi_ipmb_id,
};
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 703433493c85..3f48fc6ab596 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -27,7 +27,6 @@
#include <linux/ipmi_smi.h>
#include <linux/notifier.h>
#include <linux/init.h>
-#include <linux/proc_fs.h>
#include <linux/rcupdate.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
@@ -39,17 +38,22 @@
#define IPMI_DRIVER_VERSION "39.2"
-static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
+static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user);
+static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg,
+ struct ipmi_user *user);
static int ipmi_init_msghandler(void);
-static void smi_recv_tasklet(struct tasklet_struct *t);
+static void smi_work(struct work_struct *t);
static void handle_new_recv_msgs(struct ipmi_smi *intf);
static void need_waiter(struct ipmi_smi *intf);
static int handle_one_recv_msg(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg);
+static void intf_free(struct kref *ref);
static bool initialized;
static bool drvregistered;
+static struct timer_list ipmi_timer;
+
/* Numbers in this enumerator should be mapped to ipmi_panic_event_str */
enum ipmi_panic_event_op {
IPMI_SEND_PANIC_EVENT_NONE,
@@ -180,14 +184,8 @@ MODULE_PARM_DESC(max_msgs_per_user,
struct ipmi_user {
struct list_head link;
- /*
- * Set to NULL when the user is destroyed, a pointer to myself
- * so srcu_dereference can be used on it.
- */
- struct ipmi_user *self;
- struct srcu_struct release_barrier;
-
struct kref refcount;
+ refcount_t destroyed;
/* The upper layer that handles receive messages. */
const struct ipmi_user_hndl *handler;
@@ -200,30 +198,8 @@ struct ipmi_user {
bool gets_events;
atomic_t nr_msgs;
-
- /* Free must run in process context for RCU cleanup. */
- struct work_struct remove_work;
};
-static struct workqueue_struct *remove_work_wq;
-
-static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index)
- __acquires(user->release_barrier)
-{
- struct ipmi_user *ruser;
-
- *index = srcu_read_lock(&user->release_barrier);
- ruser = srcu_dereference(user->self, &user->release_barrier);
- if (!ruser)
- srcu_read_unlock(&user->release_barrier, *index);
- return ruser;
-}
-
-static void release_ipmi_user(struct ipmi_user *user, int index)
-{
- srcu_read_unlock(&user->release_barrier, index);
-}
-
struct cmd_rcvr {
struct list_head link;
@@ -327,6 +303,8 @@ struct bmc_device {
};
#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
+static struct workqueue_struct *bmc_remove_work_wq;
+
static int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc,
struct ipmi_device_id *id,
bool *guid_set, guid_t *guid);
@@ -451,14 +429,14 @@ struct ipmi_smi {
struct list_head link;
/*
- * The list of upper layers that are using me. seq_lock write
- * protects this. Read protection is with srcu.
+ * The list of upper layers that are using me.
*/
struct list_head users;
- struct srcu_struct users_srcu;
+ struct mutex users_mutex;
atomic_t nr_users;
struct device_attribute nr_users_devattr;
struct device_attribute nr_msgs_devattr;
+ struct device_attribute maintenance_mode_devattr;
/* Used for wake ups at startup. */
@@ -491,20 +469,27 @@ struct ipmi_smi {
* interface to match them up with their responses. A routine
* is called periodically to time the items in this list.
*/
- spinlock_t seq_lock;
+ struct mutex seq_lock;
struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];
int curr_seq;
/*
- * Messages queued for delivery. If delivery fails (out of memory
- * for instance), They will stay in here to be processed later in a
- * periodic timer interrupt. The tasklet is for handling received
- * messages directly from the handler.
+ * Messages queued for deliver to the user.
+ */
+ struct mutex user_msgs_mutex;
+ struct list_head user_msgs;
+
+ /*
+ * Messages queued for processing. If processing fails (out
+ * of memory for instance), They will stay in here to be
+ * processed later in a periodic timer interrupt. The
+ * workqueue is for handling received messages directly from
+ * the handler.
*/
spinlock_t waiting_rcv_msgs_lock;
struct list_head waiting_rcv_msgs;
atomic_t watchdog_pretimeouts_to_deliver;
- struct tasklet_struct recv_tasklet;
+ struct work_struct smi_work;
spinlock_t xmit_msgs_lock;
struct list_head xmit_msgs;
@@ -522,10 +507,9 @@ struct ipmi_smi {
* Events that were queues because no one was there to receive
* them.
*/
- spinlock_t events_lock; /* For dealing with event stuff. */
+ struct mutex events_mutex; /* For dealing with event stuff. */
struct list_head waiting_events;
unsigned int waiting_events_count; /* How many events in queue? */
- char delivering_events;
char event_msg_printed;
/* How many users are waiting for events? */
@@ -560,7 +544,11 @@ struct ipmi_smi {
/* For handling of maintenance mode. */
int maintenance_mode;
- bool maintenance_mode_enable;
+
+#define IPMI_MAINTENANCE_MODE_STATE_OFF 0
+#define IPMI_MAINTENANCE_MODE_STATE_FIRMWARE 1
+#define IPMI_MAINTENANCE_MODE_STATE_RESET 2
+ int maintenance_mode_state;
int auto_maintenance_timeout;
spinlock_t maintenance_mode_lock; /* Used in a timer... */
@@ -611,10 +599,33 @@ static void __ipmi_bmc_unregister(struct ipmi_smi *intf);
static int __ipmi_bmc_register(struct ipmi_smi *intf,
struct ipmi_device_id *id,
bool guid_set, guid_t *guid, int intf_num);
-static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id);
+static int __scan_channels(struct ipmi_smi *intf,
+ struct ipmi_device_id *id, bool rescan);
+
+static void free_ipmi_user(struct kref *ref)
+{
+ struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount);
+ struct module *owner;
+ owner = user->intf->owner;
+ kref_put(&user->intf->refcount, intf_free);
+ module_put(owner);
+ vfree(user);
+}
+
+static void release_ipmi_user(struct ipmi_user *user)
+{
+ kref_put(&user->refcount, free_ipmi_user);
+}
+
+static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user)
+{
+ if (!kref_get_unless_zero(&user->refcount))
+ return NULL;
+ return user;
+}
-/**
+/*
* The driver model view of the IPMI messaging driver.
*/
static struct platform_driver ipmidriver = {
@@ -630,9 +641,6 @@ static DEFINE_MUTEX(ipmidriver_mutex);
static LIST_HEAD(ipmi_interfaces);
static DEFINE_MUTEX(ipmi_interfaces_mutex);
-#define ipmi_interfaces_mutex_held() \
- lockdep_is_held(&ipmi_interfaces_mutex)
-static struct srcu_struct ipmi_interfaces_srcu;
/*
* List of watchers that want to know when smi's are added and deleted.
@@ -698,27 +706,20 @@ static void free_smi_msg_list(struct list_head *q)
}
}
-static void clean_up_interface_data(struct ipmi_smi *intf)
+static void intf_free(struct kref *ref)
{
+ struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount);
int i;
struct cmd_rcvr *rcvr, *rcvr2;
- struct list_head list;
-
- tasklet_kill(&intf->recv_tasklet);
free_smi_msg_list(&intf->waiting_rcv_msgs);
free_recv_msg_list(&intf->waiting_events);
/*
* Wholesale remove all the entries from the list in the
- * interface and wait for RCU to know that none are in use.
+ * interface. No need for locks, this is single-threaded.
*/
- mutex_lock(&intf->cmd_rcvrs_mutex);
- INIT_LIST_HEAD(&list);
- list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu);
- mutex_unlock(&intf->cmd_rcvrs_mutex);
-
- list_for_each_entry_safe(rcvr, rcvr2, &list, link)
+ list_for_each_entry_safe(rcvr, rcvr2, &intf->cmd_rcvrs, link)
kfree(rcvr);
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
@@ -726,26 +727,17 @@ static void clean_up_interface_data(struct ipmi_smi *intf)
&& (intf->seq_table[i].recv_msg))
ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
}
-}
-static void intf_free(struct kref *ref)
-{
- struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount);
-
- clean_up_interface_data(intf);
kfree(intf);
}
-struct watcher_entry {
- int intf_num;
- struct ipmi_smi *intf;
- struct list_head link;
-};
-
int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
{
struct ipmi_smi *intf;
- int index, rv;
+ unsigned int count = 0, i;
+ int *interfaces = NULL;
+ struct device **devices = NULL;
+ int rv = 0;
/*
* Make sure the driver is actually initialized, this handles
@@ -759,20 +751,53 @@ int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
list_add(&watcher->link, &smi_watchers);
- index = srcu_read_lock(&ipmi_interfaces_srcu);
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link,
- lockdep_is_held(&smi_watchers_mutex)) {
- int intf_num = READ_ONCE(intf->intf_num);
+ /*
+ * Build an array of ipmi interfaces and fill it in, and
+ * another array of the devices. We can't call the callback
+ * with ipmi_interfaces_mutex held. smi_watchers_mutex will
+ * keep things in order for the user.
+ */
+ mutex_lock(&ipmi_interfaces_mutex);
+ list_for_each_entry(intf, &ipmi_interfaces, link)
+ count++;
+ if (count > 0) {
+ interfaces = kmalloc_array(count, sizeof(*interfaces),
+ GFP_KERNEL);
+ if (!interfaces) {
+ rv = -ENOMEM;
+ } else {
+ devices = kmalloc_array(count, sizeof(*devices),
+ GFP_KERNEL);
+ if (!devices) {
+ kfree(interfaces);
+ interfaces = NULL;
+ rv = -ENOMEM;
+ }
+ }
+ count = 0;
+ }
+ if (interfaces) {
+ list_for_each_entry(intf, &ipmi_interfaces, link) {
+ int intf_num = READ_ONCE(intf->intf_num);
- if (intf_num == -1)
- continue;
- watcher->new_smi(intf_num, intf->si_dev);
+ if (intf_num == -1)
+ continue;
+ devices[count] = intf->si_dev;
+ interfaces[count++] = intf_num;
+ }
+ }
+ mutex_unlock(&ipmi_interfaces_mutex);
+
+ if (interfaces) {
+ for (i = 0; i < count; i++)
+ watcher->new_smi(interfaces[i], devices[i]);
+ kfree(interfaces);
+ kfree(devices);
}
- srcu_read_unlock(&ipmi_interfaces_srcu, index);
mutex_unlock(&smi_watchers_mutex);
- return 0;
+ return rv;
}
EXPORT_SYMBOL(ipmi_smi_watcher_register);
@@ -785,22 +810,17 @@ int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
}
EXPORT_SYMBOL(ipmi_smi_watcher_unregister);
-/*
- * Must be called with smi_watchers_mutex held.
- */
static void
call_smi_watchers(int i, struct device *dev)
{
struct ipmi_smi_watcher *w;
- mutex_lock(&smi_watchers_mutex);
list_for_each_entry(w, &smi_watchers, link) {
if (try_module_get(w->owner)) {
w->new_smi(i, dev);
module_put(w->owner);
}
}
- mutex_unlock(&smi_watchers_mutex);
}
static int
@@ -945,20 +965,15 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
* risk. At this moment, simply skip it in that case.
*/
ipmi_free_recv_msg(msg);
- atomic_dec(&msg->user->nr_msgs);
} else {
- int index;
- struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
-
- if (user) {
- atomic_dec(&user->nr_msgs);
- user->handler->ipmi_recv_hndl(msg, user->handler_data);
- release_ipmi_user(user, index);
- } else {
- /* User went away, give up. */
- ipmi_free_recv_msg(msg);
- rv = -EINVAL;
- }
+ /*
+ * Deliver it in smi_work. The message will hold a
+ * refcount to the user.
+ */
+ mutex_lock(&intf->user_msgs_mutex);
+ list_add_tail(&msg->link, &intf->user_msgs);
+ mutex_unlock(&intf->user_msgs_mutex);
+ queue_work(system_wq, &intf->smi_work);
}
return rv;
@@ -1110,12 +1125,11 @@ static int intf_find_seq(struct ipmi_smi *intf,
struct ipmi_recv_msg **recv_msg)
{
int rv = -ENODEV;
- unsigned long flags;
if (seq >= IPMI_IPMB_NUM_SEQ)
return -EINVAL;
- spin_lock_irqsave(&intf->seq_lock, flags);
+ mutex_lock(&intf->seq_lock);
if (intf->seq_table[seq].inuse) {
struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;
@@ -1128,7 +1142,7 @@ static int intf_find_seq(struct ipmi_smi *intf,
rv = 0;
}
}
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_unlock(&intf->seq_lock);
return rv;
}
@@ -1139,14 +1153,13 @@ static int intf_start_seq_timer(struct ipmi_smi *intf,
long msgid)
{
int rv = -ENODEV;
- unsigned long flags;
unsigned char seq;
unsigned long seqid;
GET_SEQ_FROM_MSGID(msgid, seq, seqid);
- spin_lock_irqsave(&intf->seq_lock, flags);
+ mutex_lock(&intf->seq_lock);
/*
* We do this verification because the user can be deleted
* while a message is outstanding.
@@ -1157,7 +1170,7 @@ static int intf_start_seq_timer(struct ipmi_smi *intf,
ent->timeout = ent->orig_timeout;
rv = 0;
}
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_unlock(&intf->seq_lock);
return rv;
}
@@ -1168,7 +1181,6 @@ static int intf_err_seq(struct ipmi_smi *intf,
unsigned int err)
{
int rv = -ENODEV;
- unsigned long flags;
unsigned char seq;
unsigned long seqid;
struct ipmi_recv_msg *msg = NULL;
@@ -1176,7 +1188,7 @@ static int intf_err_seq(struct ipmi_smi *intf,
GET_SEQ_FROM_MSGID(msgid, seq, seqid);
- spin_lock_irqsave(&intf->seq_lock, flags);
+ mutex_lock(&intf->seq_lock);
/*
* We do this verification because the user can be deleted
* while a message is outstanding.
@@ -1190,7 +1202,7 @@ static int intf_err_seq(struct ipmi_smi *intf,
msg = ent->recv_msg;
rv = 0;
}
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_unlock(&intf->seq_lock);
if (msg)
deliver_err_response(intf, msg, err);
@@ -1198,23 +1210,13 @@ static int intf_err_seq(struct ipmi_smi *intf,
return rv;
}
-static void free_user_work(struct work_struct *work)
-{
- struct ipmi_user *user = container_of(work, struct ipmi_user,
- remove_work);
-
- cleanup_srcu_struct(&user->release_barrier);
- vfree(user);
-}
-
int ipmi_create_user(unsigned int if_num,
const struct ipmi_user_hndl *handler,
void *handler_data,
struct ipmi_user **user)
{
- unsigned long flags;
- struct ipmi_user *new_user;
- int rv, index;
+ struct ipmi_user *new_user = NULL;
+ int rv = 0;
struct ipmi_smi *intf;
/*
@@ -1236,30 +1238,31 @@ int ipmi_create_user(unsigned int if_num,
if (rv)
return rv;
- new_user = vzalloc(sizeof(*new_user));
- if (!new_user)
- return -ENOMEM;
-
- index = srcu_read_lock(&ipmi_interfaces_srcu);
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
+ mutex_lock(&ipmi_interfaces_mutex);
+ list_for_each_entry(intf, &ipmi_interfaces, link) {
if (intf->intf_num == if_num)
goto found;
}
/* Not found, return an error */
rv = -EINVAL;
- goto out_kfree;
+ goto out_unlock;
found:
+ if (intf->in_shutdown) {
+ rv = -ENODEV;
+ goto out_unlock;
+ }
+
if (atomic_add_return(1, &intf->nr_users) > max_users) {
rv = -EBUSY;
goto out_kfree;
}
- INIT_WORK(&new_user->remove_work, free_user_work);
-
- rv = init_srcu_struct(&new_user->release_barrier);
- if (rv)
+ new_user = vzalloc(sizeof(*new_user));
+ if (!new_user) {
+ rv = -ENOMEM;
goto out_kfree;
+ }
if (!try_module_get(intf->owner)) {
rv = -ENODEV;
@@ -1271,85 +1274,68 @@ int ipmi_create_user(unsigned int if_num,
atomic_set(&new_user->nr_msgs, 0);
kref_init(&new_user->refcount);
+ refcount_set(&new_user->destroyed, 1);
+ kref_get(&new_user->refcount); /* Destroy owns a refcount. */
new_user->handler = handler;
new_user->handler_data = handler_data;
new_user->intf = intf;
new_user->gets_events = false;
- rcu_assign_pointer(new_user->self, new_user);
- spin_lock_irqsave(&intf->seq_lock, flags);
- list_add_rcu(&new_user->link, &intf->users);
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_lock(&intf->users_mutex);
+ mutex_lock(&intf->seq_lock);
+ list_add(&new_user->link, &intf->users);
+ mutex_unlock(&intf->seq_lock);
+ mutex_unlock(&intf->users_mutex);
+
if (handler->ipmi_watchdog_pretimeout)
/* User wants pretimeouts, so make sure to watch for them. */
smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
- srcu_read_unlock(&ipmi_interfaces_srcu, index);
- *user = new_user;
- return 0;
out_kfree:
- atomic_dec(&intf->nr_users);
- srcu_read_unlock(&ipmi_interfaces_srcu, index);
- vfree(new_user);
+ if (rv) {
+ atomic_dec(&intf->nr_users);
+ vfree(new_user);
+ } else {
+ *user = new_user;
+ }
+out_unlock:
+ mutex_unlock(&ipmi_interfaces_mutex);
return rv;
}
EXPORT_SYMBOL(ipmi_create_user);
int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data)
{
- int rv, index;
+ int rv = -EINVAL;
struct ipmi_smi *intf;
- index = srcu_read_lock(&ipmi_interfaces_srcu);
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- if (intf->intf_num == if_num)
- goto found;
+ mutex_lock(&ipmi_interfaces_mutex);
+ list_for_each_entry(intf, &ipmi_interfaces, link) {
+ if (intf->intf_num == if_num) {
+ if (!intf->handlers->get_smi_info)
+ rv = -ENOTTY;
+ else
+ rv = intf->handlers->get_smi_info(intf->send_info, data);
+ break;
+ }
}
- srcu_read_unlock(&ipmi_interfaces_srcu, index);
-
- /* Not found, return an error */
- return -EINVAL;
-
-found:
- if (!intf->handlers->get_smi_info)
- rv = -ENOTTY;
- else
- rv = intf->handlers->get_smi_info(intf->send_info, data);
- srcu_read_unlock(&ipmi_interfaces_srcu, index);
+ mutex_unlock(&ipmi_interfaces_mutex);
return rv;
}
EXPORT_SYMBOL(ipmi_get_smi_info);
-static void free_user(struct kref *ref)
-{
- struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount);
-
- /* SRCU cleanup must happen in task context. */
- queue_work(remove_work_wq, &user->remove_work);
-}
-
+/* Must be called with intf->users_mutex held. */
static void _ipmi_destroy_user(struct ipmi_user *user)
{
struct ipmi_smi *intf = user->intf;
int i;
- unsigned long flags;
struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
+ struct ipmi_recv_msg *msg, *msg2;
- if (!acquire_ipmi_user(user, &i)) {
- /*
- * The user has already been cleaned up, just make sure
- * nothing is using it and return.
- */
- synchronize_srcu(&user->release_barrier);
+ if (!refcount_dec_if_one(&user->destroyed))
return;
- }
-
- rcu_assign_pointer(user->self, NULL);
- release_ipmi_user(user, i);
-
- synchronize_srcu(&user->release_barrier);
if (user->handler->shutdown)
user->handler->shutdown(user->handler_data);
@@ -1360,11 +1346,11 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
if (user->gets_events)
atomic_dec(&intf->event_waiters);
- /* Remove the user from the interface's sequence table. */
- spin_lock_irqsave(&intf->seq_lock, flags);
- list_del_rcu(&user->link);
+ /* Remove the user from the interface's list and sequence table. */
+ list_del(&user->link);
atomic_dec(&intf->nr_users);
+ mutex_lock(&intf->seq_lock);
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
if (intf->seq_table[i].inuse
&& (intf->seq_table[i].recv_msg->user == user)) {
@@ -1373,13 +1359,13 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
}
}
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_unlock(&intf->seq_lock);
/*
* Remove the user from the command receiver's table. First
* we build a list of everything (not using the standard link,
* since other things may be using it till we do
- * synchronize_srcu()) then free everything in that list.
+ * synchronize_rcu()) then free everything in that list.
*/
mutex_lock(&intf->cmd_rcvrs_mutex);
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
@@ -1391,24 +1377,33 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
}
}
mutex_unlock(&intf->cmd_rcvrs_mutex);
- synchronize_rcu();
while (rcvrs) {
rcvr = rcvrs;
rcvrs = rcvr->next;
kfree(rcvr);
}
- kref_put(&intf->refcount, intf_free);
- module_put(intf->owner);
+ mutex_lock(&intf->user_msgs_mutex);
+ list_for_each_entry_safe(msg, msg2, &intf->user_msgs, link) {
+ if (msg->user != user)
+ continue;
+ list_del(&msg->link);
+ ipmi_free_recv_msg(msg);
+ }
+ mutex_unlock(&intf->user_msgs_mutex);
+
+ release_ipmi_user(user);
}
-int ipmi_destroy_user(struct ipmi_user *user)
+void ipmi_destroy_user(struct ipmi_user *user)
{
- _ipmi_destroy_user(user);
+ struct ipmi_smi *intf = user->intf;
- kref_put(&user->refcount, free_user);
+ mutex_lock(&intf->users_mutex);
+ _ipmi_destroy_user(user);
+ mutex_unlock(&intf->users_mutex);
- return 0;
+ kref_put(&user->refcount, free_ipmi_user);
}
EXPORT_SYMBOL(ipmi_destroy_user);
@@ -1417,9 +1412,9 @@ int ipmi_get_version(struct ipmi_user *user,
unsigned char *minor)
{
struct ipmi_device_id id;
- int rv, index;
+ int rv;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1428,7 +1423,7 @@ int ipmi_get_version(struct ipmi_user *user,
*major = ipmi_version_major(&id);
*minor = ipmi_version_minor(&id);
}
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1438,9 +1433,9 @@ int ipmi_set_my_address(struct ipmi_user *user,
unsigned int channel,
unsigned char address)
{
- int index, rv = 0;
+ int rv = 0;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1450,7 +1445,7 @@ int ipmi_set_my_address(struct ipmi_user *user,
channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
user->intf->addrinfo[channel].address = address;
}
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1460,9 +1455,9 @@ int ipmi_get_my_address(struct ipmi_user *user,
unsigned int channel,
unsigned char *address)
{
- int index, rv = 0;
+ int rv = 0;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1472,7 +1467,7 @@ int ipmi_get_my_address(struct ipmi_user *user,
channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
*address = user->intf->addrinfo[channel].address;
}
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1482,9 +1477,9 @@ int ipmi_set_my_LUN(struct ipmi_user *user,
unsigned int channel,
unsigned char LUN)
{
- int index, rv = 0;
+ int rv = 0;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1494,7 +1489,7 @@ int ipmi_set_my_LUN(struct ipmi_user *user,
channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
user->intf->addrinfo[channel].lun = LUN & 0x3;
}
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1504,9 +1499,9 @@ int ipmi_get_my_LUN(struct ipmi_user *user,
unsigned int channel,
unsigned char *address)
{
- int index, rv = 0;
+ int rv = 0;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1516,7 +1511,7 @@ int ipmi_get_my_LUN(struct ipmi_user *user,
channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
*address = user->intf->addrinfo[channel].lun;
}
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1524,17 +1519,17 @@ EXPORT_SYMBOL(ipmi_get_my_LUN);
int ipmi_get_maintenance_mode(struct ipmi_user *user)
{
- int mode, index;
+ int mode;
unsigned long flags;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags);
mode = user->intf->maintenance_mode;
spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags);
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return mode;
}
@@ -1543,17 +1538,24 @@ EXPORT_SYMBOL(ipmi_get_maintenance_mode);
static void maintenance_mode_update(struct ipmi_smi *intf)
{
if (intf->handlers->set_maintenance_mode)
+ /*
+ * Lower level drivers only care about firmware mode
+ * as it affects their timing. They don't care about
+ * reset, which disables all commands for a while.
+ */
intf->handlers->set_maintenance_mode(
- intf->send_info, intf->maintenance_mode_enable);
+ intf->send_info,
+ (intf->maintenance_mode_state ==
+ IPMI_MAINTENANCE_MODE_STATE_FIRMWARE));
}
int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
{
- int rv = 0, index;
+ int rv = 0;
unsigned long flags;
struct ipmi_smi *intf = user->intf;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1561,16 +1563,17 @@ int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
if (intf->maintenance_mode != mode) {
switch (mode) {
case IPMI_MAINTENANCE_MODE_AUTO:
- intf->maintenance_mode_enable
- = (intf->auto_maintenance_timeout > 0);
+ /* Just leave it alone. */
break;
case IPMI_MAINTENANCE_MODE_OFF:
- intf->maintenance_mode_enable = false;
+ intf->maintenance_mode_state =
+ IPMI_MAINTENANCE_MODE_STATE_OFF;
break;
case IPMI_MAINTENANCE_MODE_ON:
- intf->maintenance_mode_enable = true;
+ intf->maintenance_mode_state =
+ IPMI_MAINTENANCE_MODE_STATE_FIRMWARE;
break;
default:
@@ -1583,7 +1586,7 @@ int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
}
out_unlock:
spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags);
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1591,19 +1594,17 @@ EXPORT_SYMBOL(ipmi_set_maintenance_mode);
int ipmi_set_gets_events(struct ipmi_user *user, bool val)
{
- unsigned long flags;
struct ipmi_smi *intf = user->intf;
struct ipmi_recv_msg *msg, *msg2;
struct list_head msgs;
- int index;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
INIT_LIST_HEAD(&msgs);
- spin_lock_irqsave(&intf->events_lock, flags);
+ mutex_lock(&intf->events_mutex);
if (user->gets_events == val)
goto out;
@@ -1616,13 +1617,6 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val)
atomic_dec(&intf->event_waiters);
}
- if (intf->delivering_events)
- /*
- * Another thread is delivering events for this, so
- * let it handle any new events.
- */
- goto out;
-
/* Deliver any queued events. */
while (user->gets_events && !list_empty(&intf->waiting_events)) {
list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link)
@@ -1633,22 +1627,15 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val)
intf->event_msg_printed = 0;
}
- intf->delivering_events = 1;
- spin_unlock_irqrestore(&intf->events_lock, flags);
-
list_for_each_entry_safe(msg, msg2, &msgs, link) {
- msg->user = user;
- kref_get(&user->refcount);
+ ipmi_set_recv_msg_user(msg, user);
deliver_local_response(intf, msg);
}
-
- spin_lock_irqsave(&intf->events_lock, flags);
- intf->delivering_events = 0;
}
out:
- spin_unlock_irqrestore(&intf->events_lock, flags);
- release_ipmi_user(user, index);
+ mutex_unlock(&intf->events_mutex);
+ release_ipmi_user(user);
return 0;
}
@@ -1693,9 +1680,9 @@ int ipmi_register_for_cmd(struct ipmi_user *user,
{
struct ipmi_smi *intf = user->intf;
struct cmd_rcvr *rcvr;
- int rv = 0, index;
+ int rv = 0;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1725,7 +1712,7 @@ out_unlock:
if (rv)
kfree(rcvr);
out_release:
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
@@ -1739,9 +1726,9 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
struct ipmi_smi *intf = user->intf;
struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
- int i, rv = -ENOENT, index;
+ int i, rv = -ENOENT;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -1764,7 +1751,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
}
mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
while (rcvrs) {
smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
rcvr = rcvrs;
@@ -1888,13 +1875,12 @@ static void smi_send(struct ipmi_smi *intf,
const struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority)
{
- int run_to_completion = intf->run_to_completion;
+ int run_to_completion = READ_ONCE(intf->run_to_completion);
unsigned long flags = 0;
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
smi_msg = smi_add_send_msg(intf, smi_msg, priority);
-
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
@@ -1947,14 +1933,20 @@ static int i_ipmi_req_sysintf(struct ipmi_smi *intf,
if (is_maintenance_mode_cmd(msg)) {
unsigned long flags;
+ int newst;
+
+ if (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)
+ newst = IPMI_MAINTENANCE_MODE_STATE_FIRMWARE;
+ else
+ newst = IPMI_MAINTENANCE_MODE_STATE_RESET;
spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
- intf->auto_maintenance_timeout
- = maintenance_mode_timeout_ms;
+ intf->auto_maintenance_timeout = maintenance_mode_timeout_ms;
if (!intf->maintenance_mode
- && !intf->maintenance_mode_enable) {
- intf->maintenance_mode_enable = true;
+ && intf->maintenance_mode_state < newst) {
+ intf->maintenance_mode_state = newst;
maintenance_mode_update(intf);
+ mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
}
spin_unlock_irqrestore(&intf->maintenance_mode_lock,
flags);
@@ -1968,7 +1960,7 @@ static int i_ipmi_req_sysintf(struct ipmi_smi *intf,
smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
smi_msg->data[1] = msg->cmd;
smi_msg->msgid = msgid;
- smi_msg->user_data = recv_msg;
+ smi_msg->recv_msg = recv_msg;
if (msg->data_len > 0)
memcpy(&smi_msg->data[2], msg->data, msg->data_len);
smi_msg->data_size = msg->data_len + 2;
@@ -2049,12 +2041,9 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf,
* Save the receive message so we can use it
* to deliver the response.
*/
- smi_msg->user_data = recv_msg;
+ smi_msg->recv_msg = recv_msg;
} else {
- /* It's a command, so get a sequence for it. */
- unsigned long flags;
-
- spin_lock_irqsave(&intf->seq_lock, flags);
+ mutex_lock(&intf->seq_lock);
if (is_maintenance_mode_cmd(msg))
intf->ipmb_maintenance_mode_timeout =
@@ -2112,7 +2101,7 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf,
* to be correct.
*/
out_err:
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_unlock(&intf->seq_lock);
}
return rv;
@@ -2165,7 +2154,7 @@ static int i_ipmi_req_ipmb_direct(struct ipmi_smi *intf,
memcpy(smi_msg->data + 4, msg->data, msg->data_len);
smi_msg->data_size = msg->data_len + 4;
- smi_msg->user_data = recv_msg;
+ smi_msg->recv_msg = recv_msg;
return 0;
}
@@ -2228,12 +2217,9 @@ static int i_ipmi_req_lan(struct ipmi_smi *intf,
* Save the receive message so we can use it
* to deliver the response.
*/
- smi_msg->user_data = recv_msg;
+ smi_msg->recv_msg = recv_msg;
} else {
- /* It's a command, so get a sequence for it. */
- unsigned long flags;
-
- spin_lock_irqsave(&intf->seq_lock, flags);
+ mutex_lock(&intf->seq_lock);
/*
* Create a sequence number with a 1 second
@@ -2282,7 +2268,7 @@ static int i_ipmi_req_lan(struct ipmi_smi *intf,
* to be correct.
*/
out_err:
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ mutex_unlock(&intf->seq_lock);
}
return rv;
@@ -2310,24 +2296,21 @@ static int i_ipmi_request(struct ipmi_user *user,
{
struct ipmi_smi_msg *smi_msg;
struct ipmi_recv_msg *recv_msg;
+ int run_to_completion = READ_ONCE(intf->run_to_completion);
int rv = 0;
- if (user) {
- if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
- /* Decrement will happen at the end of the routine. */
- rv = -EBUSY;
- goto out;
- }
- }
-
- if (supplied_recv)
+ if (supplied_recv) {
recv_msg = supplied_recv;
- else {
- recv_msg = ipmi_alloc_recv_msg();
- if (recv_msg == NULL) {
- rv = -ENOMEM;
- goto out;
+ recv_msg->user = user;
+ if (user) {
+ atomic_inc(&user->nr_msgs);
+ /* The put happens when the message is freed. */
+ kref_get(&user->refcount);
}
+ } else {
+ recv_msg = ipmi_alloc_recv_msg(user);
+ if (IS_ERR(recv_msg))
+ return PTR_ERR(recv_msg);
}
recv_msg->user_msg_data = user_msg_data;
@@ -2338,21 +2321,22 @@ static int i_ipmi_request(struct ipmi_user *user,
if (smi_msg == NULL) {
if (!supplied_recv)
ipmi_free_recv_msg(recv_msg);
- rv = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
}
- rcu_read_lock();
+ if (!run_to_completion)
+ mutex_lock(&intf->users_mutex);
+ if (intf->maintenance_mode_state == IPMI_MAINTENANCE_MODE_STATE_RESET) {
+ /* No messages while the BMC is in reset. */
+ rv = -EBUSY;
+ goto out_err;
+ }
if (intf->in_shutdown) {
rv = -ENODEV;
goto out_err;
}
- recv_msg->user = user;
- if (user)
- /* The put happens when the message is freed. */
- kref_get(&user->refcount);
recv_msg->msgid = msgid;
/*
* Store the message to send in the receive message so timeout
@@ -2381,19 +2365,19 @@ static int i_ipmi_request(struct ipmi_user *user,
if (rv) {
out_err:
- ipmi_free_smi_msg(smi_msg);
- ipmi_free_recv_msg(recv_msg);
+ if (!supplied_smi)
+ ipmi_free_smi_msg(smi_msg);
+ if (!supplied_recv)
+ ipmi_free_recv_msg(recv_msg);
} else {
dev_dbg(intf->si_dev, "Send: %*ph\n",
smi_msg->data_size, smi_msg->data);
smi_send(intf, intf->handlers, smi_msg, priority);
}
- rcu_read_unlock();
+ if (!run_to_completion)
+ mutex_unlock(&intf->users_mutex);
-out:
- if (rv && user)
- atomic_dec(&user->nr_msgs);
return rv;
}
@@ -2420,12 +2404,12 @@ int ipmi_request_settime(struct ipmi_user *user,
unsigned int retry_time_ms)
{
unsigned char saddr = 0, lun = 0;
- int rv, index;
+ int rv;
if (!user)
return -EINVAL;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -2444,7 +2428,7 @@ int ipmi_request_settime(struct ipmi_user *user,
retries,
retry_time_ms);
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
EXPORT_SYMBOL(ipmi_request_settime);
@@ -2459,12 +2443,12 @@ int ipmi_request_supply_msgs(struct ipmi_user *user,
int priority)
{
unsigned char saddr = 0, lun = 0;
- int rv, index;
+ int rv;
if (!user)
return -EINVAL;
- user = acquire_ipmi_user(user, &index);
+ user = acquire_ipmi_user(user);
if (!user)
return -ENODEV;
@@ -2483,7 +2467,7 @@ int ipmi_request_supply_msgs(struct ipmi_user *user,
lun,
-1, 0);
- release_ipmi_user(user, index);
+ release_ipmi_user(user);
return rv;
}
EXPORT_SYMBOL(ipmi_request_supply_msgs);
@@ -2644,6 +2628,12 @@ retry_bmc_lock:
(bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry)))
goto out_noprocessing;
+ /* Don't allow sysfs access when in maintenance mode. */
+ if (intf->maintenance_mode_state) {
+ rv = -EBUSY;
+ goto out_noprocessing;
+ }
+
prev_guid_set = bmc->dyn_guid_set;
__get_guid(intf);
@@ -2679,7 +2669,7 @@ retry_bmc_lock:
if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num))
need_waiter(intf); /* Retry later on an error. */
else
- __scan_channels(intf, &id);
+ __scan_channels(intf, &id, false);
if (!intf_set) {
@@ -2699,7 +2689,7 @@ retry_bmc_lock:
goto out_noprocessing;
} else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id)))
/* Version info changes, scan the channels again. */
- __scan_channels(intf, &bmc->fetch_id);
+ __scan_channels(intf, &bmc->fetch_id, true);
bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
@@ -3057,7 +3047,7 @@ static void cleanup_bmc_work(struct work_struct *work)
int id = bmc->pdev.id; /* Unregister overwrites id */
platform_device_unregister(&bmc->pdev);
- ida_simple_remove(&ipmi_bmc_ida, id);
+ ida_free(&ipmi_bmc_ida, id);
}
static void
@@ -3070,7 +3060,7 @@ cleanup_bmc_device(struct kref *ref)
* with removing the device attributes while reading a device
* attribute.
*/
- queue_work(remove_work_wq, &bmc->remove_work);
+ queue_work(bmc_remove_work_wq, &bmc->remove_work);
}
/*
@@ -3173,7 +3163,7 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf,
bmc->pdev.name = "ipmi_bmc";
- rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL);
+ rv = ida_alloc(&ipmi_bmc_ida, GFP_KERNEL);
if (rv < 0) {
kfree(bmc);
goto out;
@@ -3428,8 +3418,6 @@ channel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
intf->channels_ready = true;
wake_up(&intf->waitq);
} else {
- intf->channel_list = intf->wchannels + set;
- intf->channels_ready = true;
rv = send_channel_info_cmd(intf, intf->curr_channel);
}
@@ -3451,10 +3439,21 @@ channel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
/*
* Must be holding intf->bmc_reg_mutex to call this.
*/
-static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id)
+static int __scan_channels(struct ipmi_smi *intf,
+ struct ipmi_device_id *id,
+ bool rescan)
{
int rv;
+ if (rescan) {
+ /* Clear channels_ready to force channels rescan. */
+ intf->channels_ready = false;
+ }
+
+ /* Skip channel scan if channels are already marked ready */
+ if (intf->channels_ready)
+ return 0;
+
if (ipmi_version_major(id) > 1
|| (ipmi_version_major(id) == 1
&& ipmi_version_minor(id) >= 5)) {
@@ -3526,20 +3525,32 @@ static ssize_t nr_msgs_show(struct device *dev,
char *buf)
{
struct ipmi_smi *intf = container_of(attr,
- struct ipmi_smi, nr_msgs_devattr);
+ struct ipmi_smi, nr_msgs_devattr);
struct ipmi_user *user;
- int index;
unsigned int count = 0;
- index = srcu_read_lock(&intf->users_srcu);
- list_for_each_entry_rcu(user, &intf->users, link)
+ mutex_lock(&intf->users_mutex);
+ list_for_each_entry(user, &intf->users, link)
count += atomic_read(&user->nr_msgs);
- srcu_read_unlock(&intf->users_srcu, index);
+ mutex_unlock(&intf->users_mutex);
return sysfs_emit(buf, "%u\n", count);
}
static DEVICE_ATTR_RO(nr_msgs);
+static ssize_t maintenance_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipmi_smi *intf = container_of(attr,
+ struct ipmi_smi,
+ maintenance_mode_devattr);
+
+ return sysfs_emit(buf, "%u %d\n", intf->maintenance_mode_state,
+ intf->auto_maintenance_timeout);
+}
+static DEVICE_ATTR_RO(maintenance_mode);
+
static void redo_bmc_reg(struct work_struct *work)
{
struct ipmi_smi *intf = container_of(work, struct ipmi_smi,
@@ -3575,12 +3586,6 @@ int ipmi_add_smi(struct module *owner,
if (!intf)
return -ENOMEM;
- rv = init_srcu_struct(&intf->users_srcu);
- if (rv) {
- kfree(intf);
- return rv;
- }
-
intf->owner = owner;
intf->bmc = &intf->tmp_bmc;
INIT_LIST_HEAD(&intf->bmc->intfs);
@@ -3597,11 +3602,14 @@ int ipmi_add_smi(struct module *owner,
}
if (slave_addr != 0)
intf->addrinfo[0].address = slave_addr;
+ INIT_LIST_HEAD(&intf->user_msgs);
+ mutex_init(&intf->user_msgs_mutex);
INIT_LIST_HEAD(&intf->users);
+ mutex_init(&intf->users_mutex);
atomic_set(&intf->nr_users, 0);
intf->handlers = handlers;
intf->send_info = send_info;
- spin_lock_init(&intf->seq_lock);
+ mutex_init(&intf->seq_lock);
for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) {
intf->seq_table[j].inuse = 0;
intf->seq_table[j].seqid = 0;
@@ -3609,13 +3617,12 @@ int ipmi_add_smi(struct module *owner,
intf->curr_seq = 0;
spin_lock_init(&intf->waiting_rcv_msgs_lock);
INIT_LIST_HEAD(&intf->waiting_rcv_msgs);
- tasklet_setup(&intf->recv_tasklet,
- smi_recv_tasklet);
+ INIT_WORK(&intf->smi_work, smi_work);
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0);
spin_lock_init(&intf->xmit_msgs_lock);
INIT_LIST_HEAD(&intf->xmit_msgs);
INIT_LIST_HEAD(&intf->hp_xmit_msgs);
- spin_lock_init(&intf->events_lock);
+ mutex_init(&intf->events_mutex);
spin_lock_init(&intf->watch_lock);
atomic_set(&intf->event_waiters, 0);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
@@ -3628,12 +3635,16 @@ int ipmi_add_smi(struct module *owner,
for (i = 0; i < IPMI_NUM_STATS; i++)
atomic_set(&intf->stats[i], 0);
+ /*
+ * Grab the watchers mutex so we can deliver the new interface
+ * without races.
+ */
+ mutex_lock(&smi_watchers_mutex);
mutex_lock(&ipmi_interfaces_mutex);
/* Look for a hole in the numbers. */
i = 0;
link = &ipmi_interfaces;
- list_for_each_entry_rcu(tintf, &ipmi_interfaces, link,
- ipmi_interfaces_mutex_held()) {
+ list_for_each_entry(tintf, &ipmi_interfaces, link) {
if (tintf->intf_num != i) {
link = &tintf->link;
break;
@@ -3642,9 +3653,9 @@ int ipmi_add_smi(struct module *owner,
}
/* Add the new interface in numeric order. */
if (i == 0)
- list_add_rcu(&intf->link, &ipmi_interfaces);
+ list_add(&intf->link, &ipmi_interfaces);
else
- list_add_tail_rcu(&intf->link, link);
+ list_add_tail(&intf->link, link);
rv = handlers->start_processing(send_info, intf);
if (rv)
@@ -3657,7 +3668,7 @@ int ipmi_add_smi(struct module *owner,
}
mutex_lock(&intf->bmc_reg_mutex);
- rv = __scan_channels(intf, &id);
+ rv = __scan_channels(intf, &id, false);
mutex_unlock(&intf->bmc_reg_mutex);
if (rv)
goto out_err_bmc_reg;
@@ -3676,18 +3687,22 @@ int ipmi_add_smi(struct module *owner,
goto out_err_bmc_reg;
}
- /*
- * Keep memory order straight for RCU readers. Make
- * sure everything else is committed to memory before
- * setting intf_num to mark the interface valid.
- */
- smp_wmb();
+ intf->maintenance_mode_devattr = dev_attr_maintenance_mode;
+ sysfs_attr_init(&intf->maintenance_mode_devattr.attr);
+ rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr);
+ if (rv) {
+ device_remove_file(intf->si_dev, &intf->nr_users_devattr);
+ goto out_err_bmc_reg;
+ }
+
intf->intf_num = i;
mutex_unlock(&ipmi_interfaces_mutex);
/* After this point the interface is legal to use. */
call_smi_watchers(i, intf->si_dev);
+ mutex_unlock(&smi_watchers_mutex);
+
return 0;
out_err_bmc_reg:
@@ -3696,10 +3711,9 @@ int ipmi_add_smi(struct module *owner,
if (intf->handlers->shutdown)
intf->handlers->shutdown(intf->send_info);
out_err:
- list_del_rcu(&intf->link);
+ list_del(&intf->link);
mutex_unlock(&ipmi_interfaces_mutex);
- synchronize_srcu(&ipmi_interfaces_srcu);
- cleanup_srcu_struct(&intf->users_srcu);
+ mutex_unlock(&smi_watchers_mutex);
kref_put(&intf->refcount, intf_free);
return rv;
@@ -3710,12 +3724,16 @@ static void deliver_smi_err_response(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg,
unsigned char err)
{
+ int rv;
msg->rsp[0] = msg->data[0] | 4;
msg->rsp[1] = msg->data[1];
msg->rsp[2] = err;
msg->rsp_size = 3;
- /* It's an error, so it will never requeue, no need to check return. */
- handle_one_recv_msg(intf, msg);
+
+ /* This will never requeue, but it may ask us to free the message. */
+ rv = handle_one_recv_msg(intf, msg);
+ if (rv == 0)
+ ipmi_free_smi_msg(msg);
}
static void cleanup_smi_msgs(struct ipmi_smi *intf)
@@ -3761,20 +3779,30 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf)
void ipmi_unregister_smi(struct ipmi_smi *intf)
{
struct ipmi_smi_watcher *w;
- int intf_num, index;
+ int intf_num;
if (!intf)
return;
+
intf_num = intf->intf_num;
mutex_lock(&ipmi_interfaces_mutex);
+ cancel_work_sync(&intf->smi_work);
+ /* smi_work() can no longer be in progress after this. */
+
intf->intf_num = -1;
intf->in_shutdown = true;
- list_del_rcu(&intf->link);
+ list_del(&intf->link);
mutex_unlock(&ipmi_interfaces_mutex);
- synchronize_srcu(&ipmi_interfaces_srcu);
- /* At this point no users can be added to the interface. */
+ /*
+ * At this point no users can be added to the interface and no
+ * new messages can be sent.
+ */
+
+ if (intf->handlers->shutdown)
+ intf->handlers->shutdown(intf->send_info);
+ device_remove_file(intf->si_dev, &intf->maintenance_mode_devattr);
device_remove_file(intf->si_dev, &intf->nr_msgs_devattr);
device_remove_file(intf->si_dev, &intf->nr_users_devattr);
@@ -3787,24 +3815,19 @@ void ipmi_unregister_smi(struct ipmi_smi *intf)
w->smi_gone(intf_num);
mutex_unlock(&smi_watchers_mutex);
- index = srcu_read_lock(&intf->users_srcu);
+ mutex_lock(&intf->users_mutex);
while (!list_empty(&intf->users)) {
- struct ipmi_user *user =
- container_of(list_next_rcu(&intf->users),
- struct ipmi_user, link);
+ struct ipmi_user *user = list_first_entry(&intf->users,
+ struct ipmi_user, link);
_ipmi_destroy_user(user);
}
- srcu_read_unlock(&intf->users_srcu, index);
-
- if (intf->handlers->shutdown)
- intf->handlers->shutdown(intf->send_info);
+ mutex_unlock(&intf->users_mutex);
cleanup_smi_msgs(intf);
ipmi_bmc_unregister(intf);
- cleanup_srcu_struct(&intf->users_srcu);
kref_put(&intf->refcount, intf_free);
}
EXPORT_SYMBOL(ipmi_unregister_smi);
@@ -3882,7 +3905,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
unsigned char chan;
struct ipmi_user *user = NULL;
struct ipmi_ipmb_addr *ipmb_addr;
- struct ipmi_recv_msg *recv_msg;
+ struct ipmi_recv_msg *recv_msg = NULL;
if (msg->rsp_size < 10) {
/* Message not big enough, just ignore it. */
@@ -3903,9 +3926,8 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) {
user = rcvr->user;
- kref_get(&user->refcount);
- } else
- user = NULL;
+ recv_msg = ipmi_alloc_recv_msg(user);
+ }
rcu_read_unlock();
if (user == NULL) {
@@ -3929,58 +3951,47 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
dev_dbg(intf->si_dev, "Invalid command: %*ph\n",
msg->data_size, msg->data);
- rcu_read_lock();
- if (!intf->in_shutdown) {
- smi_send(intf, intf->handlers, msg, 0);
- /*
- * We used the message, so return the value
- * that causes it to not be freed or
- * queued.
- */
- rv = -1;
- }
- rcu_read_unlock();
- } else {
- recv_msg = ipmi_alloc_recv_msg();
- if (!recv_msg) {
- /*
- * We couldn't allocate memory for the
- * message, so requeue it for handling
- * later.
- */
- rv = 1;
- kref_put(&user->refcount, free_user);
- } else {
- /* Extract the source address from the data. */
- ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
- ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
- ipmb_addr->slave_addr = msg->rsp[6];
- ipmb_addr->lun = msg->rsp[7] & 3;
- ipmb_addr->channel = msg->rsp[3] & 0xf;
+ smi_send(intf, intf->handlers, msg, 0);
+ /*
+ * We used the message, so return the value that
+ * causes it to not be freed or queued.
+ */
+ rv = -1;
+ } else if (!IS_ERR(recv_msg)) {
+ /* Extract the source address from the data. */
+ ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
+ ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+ ipmb_addr->slave_addr = msg->rsp[6];
+ ipmb_addr->lun = msg->rsp[7] & 3;
+ ipmb_addr->channel = msg->rsp[3] & 0xf;
- /*
- * Extract the rest of the message information
- * from the IPMB header.
- */
- recv_msg->user = user;
- recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
- recv_msg->msgid = msg->rsp[7] >> 2;
- recv_msg->msg.netfn = msg->rsp[4] >> 2;
- recv_msg->msg.cmd = msg->rsp[8];
- recv_msg->msg.data = recv_msg->msg_data;
+ /*
+ * Extract the rest of the message information
+ * from the IPMB header.
+ */
+ recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+ recv_msg->msgid = msg->rsp[7] >> 2;
+ recv_msg->msg.netfn = msg->rsp[4] >> 2;
+ recv_msg->msg.cmd = msg->rsp[8];
+ recv_msg->msg.data = recv_msg->msg_data;
- /*
- * We chop off 10, not 9 bytes because the checksum
- * at the end also needs to be removed.
- */
- recv_msg->msg.data_len = msg->rsp_size - 10;
- memcpy(recv_msg->msg_data, &msg->rsp[9],
- msg->rsp_size - 10);
- if (deliver_response(intf, recv_msg))
- ipmi_inc_stat(intf, unhandled_commands);
- else
- ipmi_inc_stat(intf, handled_commands);
- }
+ /*
+ * We chop off 10, not 9 bytes because the checksum
+ * at the end also needs to be removed.
+ */
+ recv_msg->msg.data_len = msg->rsp_size - 10;
+ memcpy(recv_msg->msg_data, &msg->rsp[9],
+ msg->rsp_size - 10);
+ if (deliver_response(intf, recv_msg))
+ ipmi_inc_stat(intf, unhandled_commands);
+ else
+ ipmi_inc_stat(intf, handled_commands);
+ } else {
+ /*
+ * We couldn't allocate memory for the message, so
+ * requeue it for handling later.
+ */
+ rv = 1;
}
return rv;
@@ -3993,7 +4004,7 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
int rv = 0;
struct ipmi_user *user = NULL;
struct ipmi_ipmb_direct_addr *daddr;
- struct ipmi_recv_msg *recv_msg;
+ struct ipmi_recv_msg *recv_msg = NULL;
unsigned char netfn = msg->rsp[0] >> 2;
unsigned char cmd = msg->rsp[3];
@@ -4002,9 +4013,8 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
rcvr = find_cmd_rcvr(intf, netfn, cmd, 0);
if (rcvr) {
user = rcvr->user;
- kref_get(&user->refcount);
- } else
- user = NULL;
+ recv_msg = ipmi_alloc_recv_msg(user);
+ }
rcu_read_unlock();
if (user == NULL) {
@@ -4020,55 +4030,44 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE;
msg->data_size = 5;
- rcu_read_lock();
- if (!intf->in_shutdown) {
- smi_send(intf, intf->handlers, msg, 0);
- /*
- * We used the message, so return the value
- * that causes it to not be freed or
- * queued.
- */
- rv = -1;
- }
- rcu_read_unlock();
- } else {
- recv_msg = ipmi_alloc_recv_msg();
- if (!recv_msg) {
- /*
- * We couldn't allocate memory for the
- * message, so requeue it for handling
- * later.
- */
- rv = 1;
- kref_put(&user->refcount, free_user);
- } else {
- /* Extract the source address from the data. */
- daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
- daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
- daddr->channel = 0;
- daddr->slave_addr = msg->rsp[1];
- daddr->rs_lun = msg->rsp[0] & 3;
- daddr->rq_lun = msg->rsp[2] & 3;
+ smi_send(intf, intf->handlers, msg, 0);
+ /*
+ * We used the message, so return the value that
+ * causes it to not be freed or queued.
+ */
+ rv = -1;
+ } else if (!IS_ERR(recv_msg)) {
+ /* Extract the source address from the data. */
+ daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
+ daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
+ daddr->channel = 0;
+ daddr->slave_addr = msg->rsp[1];
+ daddr->rs_lun = msg->rsp[0] & 3;
+ daddr->rq_lun = msg->rsp[2] & 3;
- /*
- * Extract the rest of the message information
- * from the IPMB header.
- */
- recv_msg->user = user;
- recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
- recv_msg->msgid = (msg->rsp[2] >> 2);
- recv_msg->msg.netfn = msg->rsp[0] >> 2;
- recv_msg->msg.cmd = msg->rsp[3];
- recv_msg->msg.data = recv_msg->msg_data;
-
- recv_msg->msg.data_len = msg->rsp_size - 4;
- memcpy(recv_msg->msg_data, msg->rsp + 4,
- msg->rsp_size - 4);
- if (deliver_response(intf, recv_msg))
- ipmi_inc_stat(intf, unhandled_commands);
- else
- ipmi_inc_stat(intf, handled_commands);
- }
+ /*
+ * Extract the rest of the message information
+ * from the IPMB header.
+ */
+ recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+ recv_msg->msgid = (msg->rsp[2] >> 2);
+ recv_msg->msg.netfn = msg->rsp[0] >> 2;
+ recv_msg->msg.cmd = msg->rsp[3];
+ recv_msg->msg.data = recv_msg->msg_data;
+
+ recv_msg->msg.data_len = msg->rsp_size - 4;
+ memcpy(recv_msg->msg_data, msg->rsp + 4,
+ msg->rsp_size - 4);
+ if (deliver_response(intf, recv_msg))
+ ipmi_inc_stat(intf, unhandled_commands);
+ else
+ ipmi_inc_stat(intf, handled_commands);
+ } else {
+ /*
+ * We couldn't allocate memory for the message, so
+ * requeue it for handling later.
+ */
+ rv = 1;
}
return rv;
@@ -4080,7 +4079,7 @@ static int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf,
struct ipmi_recv_msg *recv_msg;
struct ipmi_ipmb_direct_addr *daddr;
- recv_msg = msg->user_data;
+ recv_msg = msg->recv_msg;
if (recv_msg == NULL) {
dev_warn(intf->si_dev,
"IPMI direct message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
@@ -4182,7 +4181,7 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
unsigned char chan;
struct ipmi_user *user = NULL;
struct ipmi_lan_addr *lan_addr;
- struct ipmi_recv_msg *recv_msg;
+ struct ipmi_recv_msg *recv_msg = NULL;
if (msg->rsp_size < 12) {
/* Message not big enough, just ignore it. */
@@ -4203,63 +4202,76 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) {
user = rcvr->user;
- kref_get(&user->refcount);
- } else
- user = NULL;
+ recv_msg = ipmi_alloc_recv_msg(user);
+ }
rcu_read_unlock();
if (user == NULL) {
- /* We didn't find a user, just give up. */
+ /* We didn't find a user, just give up and return an error. */
ipmi_inc_stat(intf, unhandled_commands);
+ msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
+ msg->data[1] = IPMI_SEND_MSG_CMD;
+ msg->data[2] = chan;
+ msg->data[3] = msg->rsp[4]; /* handle */
+ msg->data[4] = msg->rsp[8]; /* rsSWID */
+ msg->data[5] = ((netfn + 1) << 2) | (msg->rsp[9] & 0x3);
+ msg->data[6] = ipmb_checksum(&msg->data[3], 3);
+ msg->data[7] = msg->rsp[5]; /* rqSWID */
+ /* rqseq/lun */
+ msg->data[8] = (msg->rsp[9] & 0xfc) | (msg->rsp[6] & 0x3);
+ msg->data[9] = cmd;
+ msg->data[10] = IPMI_INVALID_CMD_COMPLETION_CODE;
+ msg->data[11] = ipmb_checksum(&msg->data[7], 4);
+ msg->data_size = 12;
+
+ dev_dbg(intf->si_dev, "Invalid command: %*ph\n",
+ msg->data_size, msg->data);
+
+ smi_send(intf, intf->handlers, msg, 0);
/*
- * Don't do anything with these messages, just allow
- * them to be freed.
+ * We used the message, so return the value that
+ * causes it to not be freed or queued.
*/
- rv = 0;
- } else {
- recv_msg = ipmi_alloc_recv_msg();
- if (!recv_msg) {
- /*
- * We couldn't allocate memory for the
- * message, so requeue it for handling later.
- */
- rv = 1;
- kref_put(&user->refcount, free_user);
- } else {
- /* Extract the source address from the data. */
- lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
- lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
- lan_addr->session_handle = msg->rsp[4];
- lan_addr->remote_SWID = msg->rsp[8];
- lan_addr->local_SWID = msg->rsp[5];
- lan_addr->lun = msg->rsp[9] & 3;
- lan_addr->channel = msg->rsp[3] & 0xf;
- lan_addr->privilege = msg->rsp[3] >> 4;
+ rv = -1;
+ } else if (!IS_ERR(recv_msg)) {
+ /* Extract the source address from the data. */
+ lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
+ lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
+ lan_addr->session_handle = msg->rsp[4];
+ lan_addr->remote_SWID = msg->rsp[8];
+ lan_addr->local_SWID = msg->rsp[5];
+ lan_addr->lun = msg->rsp[9] & 3;
+ lan_addr->channel = msg->rsp[3] & 0xf;
+ lan_addr->privilege = msg->rsp[3] >> 4;
- /*
- * Extract the rest of the message information
- * from the IPMB header.
- */
- recv_msg->user = user;
- recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
- recv_msg->msgid = msg->rsp[9] >> 2;
- recv_msg->msg.netfn = msg->rsp[6] >> 2;
- recv_msg->msg.cmd = msg->rsp[10];
- recv_msg->msg.data = recv_msg->msg_data;
+ /*
+ * Extract the rest of the message information
+ * from the IPMB header.
+ */
+ recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+ recv_msg->msgid = msg->rsp[9] >> 2;
+ recv_msg->msg.netfn = msg->rsp[6] >> 2;
+ recv_msg->msg.cmd = msg->rsp[10];
+ recv_msg->msg.data = recv_msg->msg_data;
- /*
- * We chop off 12, not 11 bytes because the checksum
- * at the end also needs to be removed.
- */
- recv_msg->msg.data_len = msg->rsp_size - 12;
- memcpy(recv_msg->msg_data, &msg->rsp[11],
- msg->rsp_size - 12);
- if (deliver_response(intf, recv_msg))
- ipmi_inc_stat(intf, unhandled_commands);
- else
- ipmi_inc_stat(intf, handled_commands);
- }
+ /*
+ * We chop off 12, not 11 bytes because the checksum
+ * at the end also needs to be removed.
+ */
+ recv_msg->msg.data_len = msg->rsp_size - 12;
+ memcpy(recv_msg->msg_data, &msg->rsp[11],
+ msg->rsp_size - 12);
+ if (deliver_response(intf, recv_msg))
+ ipmi_inc_stat(intf, unhandled_commands);
+ else
+ ipmi_inc_stat(intf, handled_commands);
+ } else {
+ /*
+ * We couldn't allocate memory for the message, so
+ * requeue it for handling later.
+ */
+ rv = 1;
}
return rv;
@@ -4281,7 +4293,7 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
unsigned char chan;
struct ipmi_user *user = NULL;
struct ipmi_system_interface_addr *smi_addr;
- struct ipmi_recv_msg *recv_msg;
+ struct ipmi_recv_msg *recv_msg = NULL;
/*
* We expect the OEM SW to perform error checking
@@ -4310,9 +4322,8 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) {
user = rcvr->user;
- kref_get(&user->refcount);
- } else
- user = NULL;
+ recv_msg = ipmi_alloc_recv_msg(user);
+ }
rcu_read_unlock();
if (user == NULL) {
@@ -4325,48 +4336,42 @@ static int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
*/
rv = 0;
- } else {
- recv_msg = ipmi_alloc_recv_msg();
- if (!recv_msg) {
- /*
- * We couldn't allocate memory for the
- * message, so requeue it for handling
- * later.
- */
- rv = 1;
- kref_put(&user->refcount, free_user);
- } else {
- /*
- * OEM Messages are expected to be delivered via
- * the system interface to SMS software. We might
- * need to visit this again depending on OEM
- * requirements
- */
- smi_addr = ((struct ipmi_system_interface_addr *)
- &recv_msg->addr);
- smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
- smi_addr->channel = IPMI_BMC_CHANNEL;
- smi_addr->lun = msg->rsp[0] & 3;
-
- recv_msg->user = user;
- recv_msg->user_msg_data = NULL;
- recv_msg->recv_type = IPMI_OEM_RECV_TYPE;
- recv_msg->msg.netfn = msg->rsp[0] >> 2;
- recv_msg->msg.cmd = msg->rsp[1];
- recv_msg->msg.data = recv_msg->msg_data;
+ } else if (!IS_ERR(recv_msg)) {
+ /*
+ * OEM Messages are expected to be delivered via
+ * the system interface to SMS software. We might
+ * need to visit this again depending on OEM
+ * requirements
+ */
+ smi_addr = ((struct ipmi_system_interface_addr *)
+ &recv_msg->addr);
+ smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ smi_addr->channel = IPMI_BMC_CHANNEL;
+ smi_addr->lun = msg->rsp[0] & 3;
+
+ recv_msg->user_msg_data = NULL;
+ recv_msg->recv_type = IPMI_OEM_RECV_TYPE;
+ recv_msg->msg.netfn = msg->rsp[0] >> 2;
+ recv_msg->msg.cmd = msg->rsp[1];
+ recv_msg->msg.data = recv_msg->msg_data;
- /*
- * The message starts at byte 4 which follows the
- * the Channel Byte in the "GET MESSAGE" command
- */
- recv_msg->msg.data_len = msg->rsp_size - 4;
- memcpy(recv_msg->msg_data, &msg->rsp[4],
- msg->rsp_size - 4);
- if (deliver_response(intf, recv_msg))
- ipmi_inc_stat(intf, unhandled_commands);
- else
- ipmi_inc_stat(intf, handled_commands);
- }
+ /*
+ * The message starts at byte 4 which follows the
+ * Channel Byte in the "GET MESSAGE" command
+ */
+ recv_msg->msg.data_len = msg->rsp_size - 4;
+ memcpy(recv_msg->msg_data, &msg->rsp[4],
+ msg->rsp_size - 4);
+ if (deliver_response(intf, recv_msg))
+ ipmi_inc_stat(intf, unhandled_commands);
+ else
+ ipmi_inc_stat(intf, handled_commands);
+ } else {
+ /*
+ * We couldn't allocate memory for the message, so
+ * requeue it for handling later.
+ */
+ rv = 1;
}
return rv;
@@ -4396,8 +4401,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
struct ipmi_recv_msg *recv_msg, *recv_msg2;
struct list_head msgs;
struct ipmi_user *user;
- int rv = 0, deliver_count = 0, index;
- unsigned long flags;
+ int rv = 0, deliver_count = 0;
if (msg->rsp_size < 19) {
/* Message is too small to be an IPMB event. */
@@ -4412,7 +4416,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
INIT_LIST_HEAD(&msgs);
- spin_lock_irqsave(&intf->events_lock, flags);
+ mutex_lock(&intf->events_mutex);
ipmi_inc_stat(intf, events);
@@ -4420,18 +4424,20 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
* Allocate and fill in one message for every user that is
* getting events.
*/
- index = srcu_read_lock(&intf->users_srcu);
- list_for_each_entry_rcu(user, &intf->users, link) {
+ mutex_lock(&intf->users_mutex);
+ list_for_each_entry(user, &intf->users, link) {
if (!user->gets_events)
continue;
- recv_msg = ipmi_alloc_recv_msg();
- if (!recv_msg) {
- rcu_read_unlock();
+ recv_msg = ipmi_alloc_recv_msg(user);
+ if (IS_ERR(recv_msg)) {
+ mutex_unlock(&intf->users_mutex);
list_for_each_entry_safe(recv_msg, recv_msg2, &msgs,
link) {
+ user = recv_msg->user;
list_del(&recv_msg->link);
ipmi_free_recv_msg(recv_msg);
+ kref_put(&user->refcount, free_ipmi_user);
}
/*
* We couldn't allocate memory for the
@@ -4445,11 +4451,9 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
deliver_count++;
copy_event_into_recv_msg(recv_msg, msg);
- recv_msg->user = user;
- kref_get(&user->refcount);
list_add_tail(&recv_msg->link, &msgs);
}
- srcu_read_unlock(&intf->users_srcu, index);
+ mutex_unlock(&intf->users_mutex);
if (deliver_count) {
/* Now deliver all the messages. */
@@ -4462,8 +4466,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
* No one to receive the message, put it in queue if there's
* not already too many things in the queue.
*/
- recv_msg = ipmi_alloc_recv_msg();
- if (!recv_msg) {
+ recv_msg = ipmi_alloc_recv_msg(NULL);
+ if (IS_ERR(recv_msg)) {
/*
* We couldn't allocate memory for the
* message, so requeue it for handling
@@ -4487,7 +4491,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
}
out:
- spin_unlock_irqrestore(&intf->events_lock, flags);
+ mutex_unlock(&intf->events_mutex);
return rv;
}
@@ -4498,7 +4502,7 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
struct ipmi_recv_msg *recv_msg;
struct ipmi_system_interface_addr *smi_addr;
- recv_msg = msg->user_data;
+ recv_msg = msg->recv_msg;
if (recv_msg == NULL) {
dev_warn(intf->si_dev,
"IPMI SMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
@@ -4539,9 +4543,10 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
if (msg->rsp_size < 2) {
/* Message is too small to be correct. */
- dev_warn(intf->si_dev,
- "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
- (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
+ dev_warn_ratelimited(intf->si_dev,
+ "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
+ (msg->data[0] >> 2) | 1,
+ msg->data[1], msg->rsp_size);
return_unspecified:
/* Generate an error response for the message. */
@@ -4571,14 +4576,14 @@ return_unspecified:
} else if ((msg->data_size >= 2)
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
- && (msg->user_data == NULL)) {
+ && (msg->recv_msg == NULL)) {
- if (intf->in_shutdown)
+ if (intf->in_shutdown || intf->run_to_completion)
goto out;
/*
* This is the local response to a command send, start
- * the timer for these. The user_data will not be
+ * the timer for these. The recv_msg will not be
* NULL if this is a response send, and we will let
* response sends just go through.
*/
@@ -4617,10 +4622,10 @@ return_unspecified:
* The NetFN and Command in the response is not even
* marginally correct.
*/
- dev_warn(intf->si_dev,
- "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n",
- (msg->data[0] >> 2) | 1, msg->data[1],
- msg->rsp[0] >> 2, msg->rsp[1]);
+ dev_warn_ratelimited(intf->si_dev,
+ "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n",
+ (msg->data[0] >> 2) | 1, msg->data[1],
+ msg->rsp[0] >> 2, msg->rsp[1]);
goto return_unspecified;
}
@@ -4638,13 +4643,16 @@ return_unspecified:
requeue = handle_ipmb_direct_rcv_rsp(intf, msg);
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
- && (msg->user_data != NULL)) {
+ && (msg->recv_msg != NULL)) {
/*
* It's a response to a response we sent. For this we
* deliver a send message response to the user.
*/
struct ipmi_recv_msg *recv_msg;
+ if (intf->run_to_completion)
+ goto out;
+
chan = msg->data[2] & 0x0f;
if (chan >= IPMI_MAX_CHANNELS)
/* Invalid channel number */
@@ -4652,7 +4660,7 @@ return_unspecified:
cc = msg->rsp[2];
process_response_response:
- recv_msg = msg->user_data;
+ recv_msg = msg->recv_msg;
requeue = 0;
if (!recv_msg)
@@ -4667,6 +4675,9 @@ process_response_response:
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
struct ipmi_channel *chans;
+ if (intf->run_to_completion)
+ goto out;
+
/* It's from the receive queue. */
chan = msg->rsp[3] & 0xf;
if (chan >= IPMI_MAX_CHANNELS) {
@@ -4741,6 +4752,9 @@ process_response_response:
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) {
/* It's an asynchronous event. */
+ if (intf->run_to_completion)
+ goto out;
+
requeue = handle_read_event_rsp(intf, msg);
} else {
/* It's a response from the local BMC. */
@@ -4756,10 +4770,10 @@ process_response_response:
*/
static void handle_new_recv_msgs(struct ipmi_smi *intf)
{
- struct ipmi_smi_msg *smi_msg;
- unsigned long flags = 0;
- int rv;
- int run_to_completion = intf->run_to_completion;
+ struct ipmi_smi_msg *smi_msg;
+ unsigned long flags = 0;
+ int rv;
+ int run_to_completion = READ_ONCE(intf->run_to_completion);
/* See if any waiting messages need to be processed. */
if (!run_to_completion)
@@ -4779,7 +4793,7 @@ static void handle_new_recv_msgs(struct ipmi_smi *intf)
* To preserve message order, quit if we
* can't handle a message. Add the message
* back at the head, this is safe because this
- * tasklet is the only thing that pulls the
+ * workqueue is the only thing that pulls the
* messages.
*/
list_add(&smi_msg->link, &intf->waiting_rcv_msgs);
@@ -4793,31 +4807,16 @@ static void handle_new_recv_msgs(struct ipmi_smi *intf)
}
if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags);
-
- /*
- * If the pretimout count is non-zero, decrement one from it and
- * deliver pretimeouts to all the users.
- */
- if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) {
- struct ipmi_user *user;
- int index;
-
- index = srcu_read_lock(&intf->users_srcu);
- list_for_each_entry_rcu(user, &intf->users, link) {
- if (user->handler->ipmi_watchdog_pretimeout)
- user->handler->ipmi_watchdog_pretimeout(
- user->handler_data);
- }
- srcu_read_unlock(&intf->users_srcu, index);
- }
}
-static void smi_recv_tasklet(struct tasklet_struct *t)
+static void smi_work(struct work_struct *t)
{
unsigned long flags = 0; /* keep us warning-free. */
- struct ipmi_smi *intf = from_tasklet(intf, t, recv_tasklet);
- int run_to_completion = intf->run_to_completion;
+ struct ipmi_smi *intf = from_work(intf, t, smi_work);
+ int run_to_completion = READ_ONCE(intf->run_to_completion);
struct ipmi_smi_msg *newmsg = NULL;
+ struct ipmi_recv_msg *msg, *msg2;
+ int cc;
/*
* Start the next message if available.
@@ -4826,9 +4825,7 @@ static void smi_recv_tasklet(struct tasklet_struct *t)
* because the lower layer is allowed to hold locks while calling
* message delivery.
*/
-
- rcu_read_lock();
-
+restart:
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
if (intf->curr_msg == NULL && !intf->in_shutdown) {
@@ -4846,15 +4843,64 @@ static void smi_recv_tasklet(struct tasklet_struct *t)
intf->curr_msg = newmsg;
}
}
-
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
- if (newmsg)
- intf->handlers->sender(intf->send_info, newmsg);
- rcu_read_unlock();
+ if (newmsg) {
+ cc = intf->handlers->sender(intf->send_info, newmsg);
+ if (cc) {
+ if (newmsg->recv_msg)
+ deliver_err_response(intf,
+ newmsg->recv_msg, cc);
+ else
+ ipmi_free_smi_msg(newmsg);
+ goto restart;
+ }
+ }
handle_new_recv_msgs(intf);
+
+ /* Nothing below applies during panic time. */
+ if (run_to_completion)
+ return;
+
+ /*
+ * If the pretimout count is non-zero, decrement one from it and
+ * deliver pretimeouts to all the users.
+ */
+ if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) {
+ struct ipmi_user *user;
+
+ mutex_lock(&intf->users_mutex);
+ list_for_each_entry(user, &intf->users, link) {
+ if (user->handler->ipmi_watchdog_pretimeout)
+ user->handler->ipmi_watchdog_pretimeout(
+ user->handler_data);
+ }
+ mutex_unlock(&intf->users_mutex);
+ }
+
+ /*
+ * Freeing the message can cause a user to be released, which
+ * can then cause the interface to be freed. Make sure that
+ * doesn't happen until we are ready.
+ */
+ kref_get(&intf->refcount);
+
+ mutex_lock(&intf->user_msgs_mutex);
+ list_for_each_entry_safe(msg, msg2, &intf->user_msgs, link) {
+ struct ipmi_user *user = msg->user;
+
+ list_del(&msg->link);
+
+ if (refcount_read(&user->destroyed) == 0)
+ ipmi_free_recv_msg(msg);
+ else
+ user->handler->ipmi_recv_hndl(msg, user->handler_data);
+ }
+ mutex_unlock(&intf->user_msgs_mutex);
+
+ kref_put(&intf->refcount, intf_free);
}
/* Handle a new message from the lower layer. */
@@ -4862,11 +4908,11 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg)
{
unsigned long flags = 0; /* keep us warning-free. */
- int run_to_completion = intf->run_to_completion;
+ int run_to_completion = READ_ONCE(intf->run_to_completion);
/*
* To preserve message order, we keep a queue and deliver from
- * a tasklet.
+ * a workqueue.
*/
if (!run_to_completion)
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
@@ -4887,9 +4933,9 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (run_to_completion)
- smi_recv_tasklet(&intf->recv_tasklet);
+ smi_work(&intf->smi_work);
else
- tasklet_schedule(&intf->recv_tasklet);
+ queue_work(system_wq, &intf->smi_work);
}
EXPORT_SYMBOL(ipmi_smi_msg_received);
@@ -4899,7 +4945,7 @@ void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf)
return;
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
- tasklet_schedule(&intf->recv_tasklet);
+ queue_work(system_wq, &intf->smi_work);
}
EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
@@ -4928,8 +4974,7 @@ smi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg,
static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct list_head *timeouts,
unsigned long timeout_period,
- int slot, unsigned long *flags,
- bool *need_timer)
+ int slot, bool *need_timer)
{
struct ipmi_recv_msg *msg;
@@ -4981,7 +5026,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
return;
}
- spin_unlock_irqrestore(&intf->seq_lock, *flags);
+ mutex_unlock(&intf->seq_lock);
/*
* Send the new message. We send with a zero
@@ -5002,7 +5047,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
} else
ipmi_free_smi_msg(smi_msg);
- spin_lock_irqsave(&intf->seq_lock, *flags);
+ mutex_lock(&intf->seq_lock);
}
}
@@ -5029,7 +5074,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
* list.
*/
INIT_LIST_HEAD(&timeouts);
- spin_lock_irqsave(&intf->seq_lock, flags);
+ mutex_lock(&intf->seq_lock);
if (intf->ipmb_maintenance_mode_timeout) {
if (intf->ipmb_maintenance_mode_timeout <= timeout_period)
intf->ipmb_maintenance_mode_timeout = 0;
@@ -5039,8 +5084,8 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
check_msg_timeout(intf, &intf->seq_table[i],
&timeouts, timeout_period, i,
- &flags, &need_timer);
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ &need_timer);
+ mutex_unlock(&intf->seq_lock);
list_for_each_entry_safe(msg, msg2, &timeouts, link)
deliver_err_response(intf, msg, IPMI_TIMEOUT_COMPLETION_CODE);
@@ -5060,7 +5105,9 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
-= timeout_period;
if (!intf->maintenance_mode
&& (intf->auto_maintenance_timeout <= 0)) {
- intf->maintenance_mode_enable = false;
+ intf->maintenance_mode_state =
+ IPMI_MAINTENANCE_MODE_STATE_OFF;
+ intf->auto_maintenance_timeout = 0;
maintenance_mode_update(intf);
}
}
@@ -5068,7 +5115,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
flags);
}
- tasklet_schedule(&intf->recv_tasklet);
+ queue_work(system_wq, &intf->smi_work);
return need_timer;
}
@@ -5076,28 +5123,28 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
static void ipmi_request_event(struct ipmi_smi *intf)
{
/* No event requests when in maintenance mode. */
- if (intf->maintenance_mode_enable)
+ if (intf->maintenance_mode_state)
return;
if (!intf->in_shutdown)
intf->handlers->request_events(intf->send_info);
}
-static struct timer_list ipmi_timer;
-
static atomic_t stop_operation;
-static void ipmi_timeout(struct timer_list *unused)
+static void ipmi_timeout_work(struct work_struct *work)
{
+ if (atomic_read(&stop_operation))
+ return;
+
struct ipmi_smi *intf;
bool need_timer = false;
- int index;
if (atomic_read(&stop_operation))
return;
- index = srcu_read_lock(&ipmi_interfaces_srcu);
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
+ mutex_lock(&ipmi_interfaces_mutex);
+ list_for_each_entry(intf, &ipmi_interfaces, link) {
if (atomic_read(&intf->event_waiters)) {
intf->ticks_to_req_ev--;
if (intf->ticks_to_req_ev == 0) {
@@ -5106,15 +5153,27 @@ static void ipmi_timeout(struct timer_list *unused)
}
need_timer = true;
}
+ if (intf->maintenance_mode_state)
+ need_timer = true;
need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
}
- srcu_read_unlock(&ipmi_interfaces_srcu, index);
+ mutex_unlock(&ipmi_interfaces_mutex);
if (need_timer)
mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
}
+static DECLARE_WORK(ipmi_timer_work, ipmi_timeout_work);
+
+static void ipmi_timeout(struct timer_list *unused)
+{
+ if (atomic_read(&stop_operation))
+ return;
+
+ queue_work(system_wq, &ipmi_timer_work);
+}
+
static void need_waiter(struct ipmi_smi *intf)
{
/* Racy, but worst case we start the timer twice. */
@@ -5139,7 +5198,7 @@ struct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC);
if (rv) {
rv->done = free_smi_msg;
- rv->user_data = NULL;
+ rv->recv_msg = NULL;
rv->type = IPMI_SMI_MSG_TYPE_NORMAL;
atomic_inc(&smi_msg_inuse_count);
}
@@ -5155,27 +5214,51 @@ static void free_recv_msg(struct ipmi_recv_msg *msg)
kfree(msg);
}
-static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
+static struct ipmi_recv_msg *ipmi_alloc_recv_msg(struct ipmi_user *user)
{
struct ipmi_recv_msg *rv;
+ if (user) {
+ if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
+ atomic_dec(&user->nr_msgs);
+ return ERR_PTR(-EBUSY);
+ }
+ }
+
rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
- if (rv) {
- rv->user = NULL;
- rv->done = free_recv_msg;
- atomic_inc(&recv_msg_inuse_count);
+ if (!rv) {
+ if (user)
+ atomic_dec(&user->nr_msgs);
+ return ERR_PTR(-ENOMEM);
}
+
+ rv->user = user;
+ rv->done = free_recv_msg;
+ if (user)
+ kref_get(&user->refcount);
+ atomic_inc(&recv_msg_inuse_count);
return rv;
}
void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
{
- if (msg->user && !oops_in_progress)
- kref_put(&msg->user->refcount, free_user);
+ if (msg->user && !oops_in_progress) {
+ atomic_dec(&msg->user->nr_msgs);
+ kref_put(&msg->user->refcount, free_ipmi_user);
+ }
msg->done(msg);
}
EXPORT_SYMBOL(ipmi_free_recv_msg);
+static void ipmi_set_recv_msg_user(struct ipmi_recv_msg *msg,
+ struct ipmi_user *user)
+{
+ WARN_ON_ONCE(msg->user); /* User should not be set. */
+ msg->user = user;
+ atomic_inc(&user->nr_msgs);
+ kref_get(&user->refcount);
+}
+
static atomic_t panic_done_count = ATOMIC_INIT(0);
static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
@@ -5191,9 +5274,9 @@ static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
/*
* Inside a panic, send a message and wait for a response.
*/
-static void ipmi_panic_request_and_wait(struct ipmi_smi *intf,
- struct ipmi_addr *addr,
- struct kernel_ipmi_msg *msg)
+static void _ipmi_panic_request_and_wait(struct ipmi_smi *intf,
+ struct ipmi_addr *addr,
+ struct kernel_ipmi_msg *msg)
{
struct ipmi_smi_msg smi_msg;
struct ipmi_recv_msg recv_msg;
@@ -5223,6 +5306,15 @@ static void ipmi_panic_request_and_wait(struct ipmi_smi *intf,
ipmi_poll(intf);
}
+void ipmi_panic_request_and_wait(struct ipmi_user *user,
+ struct ipmi_addr *addr,
+ struct kernel_ipmi_msg *msg)
+{
+ user->intf->run_to_completion = 1;
+ _ipmi_panic_request_and_wait(user->intf, addr, msg);
+}
+EXPORT_SYMBOL(ipmi_panic_request_and_wait);
+
static void event_receiver_fetcher(struct ipmi_smi *intf,
struct ipmi_recv_msg *msg)
{
@@ -5291,7 +5383,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
}
/* Send the event announcing the panic. */
- ipmi_panic_request_and_wait(intf, &addr, &msg);
+ _ipmi_panic_request_and_wait(intf, &addr, &msg);
/*
* On every interface, dump a bunch of OEM event holding the
@@ -5327,7 +5419,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
msg.data = NULL;
msg.data_len = 0;
intf->null_user_handler = device_id_fetcher;
- ipmi_panic_request_and_wait(intf, &addr, &msg);
+ _ipmi_panic_request_and_wait(intf, &addr, &msg);
if (intf->local_event_generator) {
/* Request the event receiver from the local MC. */
@@ -5336,7 +5428,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
msg.data = NULL;
msg.data_len = 0;
intf->null_user_handler = event_receiver_fetcher;
- ipmi_panic_request_and_wait(intf, &addr, &msg);
+ _ipmi_panic_request_and_wait(intf, &addr, &msg);
}
intf->null_user_handler = NULL;
@@ -5377,23 +5469,18 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
j = 0;
while (*p) {
- int size = strlen(p);
+ int size = strnlen(p, 11);
- if (size > 11)
- size = 11;
data[0] = 0;
data[1] = 0;
data[2] = 0xf0; /* OEM event without timestamp. */
data[3] = intf->addrinfo[0].address;
data[4] = j++; /* sequence # */
- /*
- * Always give 11 bytes, so strncpy will fill
- * it with zeroes for me.
- */
- strncpy(data+5, p, 11);
+
+ memcpy_and_pad(data+5, 11, p, size, '\0');
p += size;
- ipmi_panic_request_and_wait(intf, &addr, &msg);
+ _ipmi_panic_request_and_wait(intf, &addr, &msg);
}
}
@@ -5411,7 +5498,7 @@ static int panic_event(struct notifier_block *this,
has_panicked = 1;
/* For every registered interface, set it to run to completion. */
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
+ list_for_each_entry(intf, &ipmi_interfaces, link) {
if (!intf->handlers || intf->intf_num == -1)
/* Interface is not ready. */
continue;
@@ -5441,7 +5528,7 @@ static int panic_event(struct notifier_block *this,
intf->handlers->set_run_to_completion(intf->send_info,
1);
- list_for_each_entry_rcu(user, &intf->users, link) {
+ list_for_each_entry(user, &intf->users, link) {
if (user->handler->ipmi_panic_handler)
user->handler->ipmi_panic_handler(
user->handler_data);
@@ -5486,15 +5573,11 @@ static int ipmi_init_msghandler(void)
if (initialized)
goto out;
- rv = init_srcu_struct(&ipmi_interfaces_srcu);
- if (rv)
- goto out;
-
- remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq");
- if (!remove_work_wq) {
+ bmc_remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq");
+ if (!bmc_remove_work_wq) {
pr_err("unable to create ipmi-msghandler-remove-wq workqueue");
rv = -ENOMEM;
- goto out_wq;
+ goto out;
}
timer_setup(&ipmi_timer, ipmi_timeout, 0);
@@ -5504,9 +5587,6 @@ static int ipmi_init_msghandler(void)
initialized = true;
-out_wq:
- if (rv)
- cleanup_srcu_struct(&ipmi_interfaces_srcu);
out:
mutex_unlock(&ipmi_interfaces_mutex);
return rv;
@@ -5530,7 +5610,7 @@ static void __exit cleanup_ipmi(void)
int count;
if (initialized) {
- destroy_workqueue(remove_work_wq);
+ destroy_workqueue(bmc_remove_work_wq);
atomic_notifier_chain_unregister(&panic_notifier_list,
&panic_block);
@@ -5546,7 +5626,8 @@ static void __exit cleanup_ipmi(void)
* here.
*/
atomic_set(&stop_operation, 1);
- del_timer_sync(&ipmi_timer);
+ timer_delete_sync(&ipmi_timer);
+ cancel_work_sync(&ipmi_timer_work);
initialized = false;
@@ -5557,8 +5638,6 @@ static void __exit cleanup_ipmi(void)
count = atomic_read(&recv_msg_inuse_count);
if (count != 0)
pr_warn("recv message count %d at exit\n", count);
-
- cleanup_srcu_struct(&ipmi_interfaces_srcu);
}
if (drvregistered)
driver_unregister(&ipmidriver.driver);
diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c
index da22a8cbe68e..52a1130defe5 100644
--- a/drivers/char/ipmi/ipmi_powernv.c
+++ b/drivers/char/ipmi/ipmi_powernv.c
@@ -51,7 +51,7 @@ static void send_error_reply(struct ipmi_smi_powernv *smi,
ipmi_smi_msg_received(smi->intf, msg);
}
-static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
+static int ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
{
struct ipmi_smi_powernv *smi = send_info;
struct opal_ipmi_msg *opal_msg;
@@ -93,18 +93,19 @@ static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
smi->interface_id, opal_msg, size);
rc = opal_ipmi_send(smi->interface_id, opal_msg, size);
pr_devel("%s: -> %d\n", __func__, rc);
-
- if (!rc) {
- smi->cur_msg = msg;
- spin_unlock_irqrestore(&smi->msg_lock, flags);
- return;
+ if (rc) {
+ comp = IPMI_ERR_UNSPECIFIED;
+ goto err_unlock;
}
- comp = IPMI_ERR_UNSPECIFIED;
+ smi->cur_msg = msg;
+ spin_unlock_irqrestore(&smi->msg_lock, flags);
+ return IPMI_CC_NO_ERROR;
+
err_unlock:
spin_unlock_irqrestore(&smi->msg_lock, flags);
err:
- send_error_reply(smi, msg, comp);
+ return comp;
}
static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi)
@@ -281,15 +282,13 @@ err_free:
return rc;
}
-static int ipmi_powernv_remove(struct platform_device *pdev)
+static void ipmi_powernv_remove(struct platform_device *pdev)
{
struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev);
ipmi_unregister_smi(smi->intf);
free_irq(smi->irq, smi);
irq_dispose_mapping(smi->irq);
-
- return 0;
}
static const struct of_device_id ipmi_powernv_match[] = {
@@ -304,7 +303,7 @@ static struct platform_driver powernv_ipmi_driver = {
.of_match_table = ipmi_powernv_match,
},
.probe = ipmi_powernv_probe,
- .remove = ipmi_powernv_remove,
+ .remove = ipmi_powernv_remove,
};
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 163ec9749e55..e63c316d8aaa 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -650,27 +650,12 @@ static struct ipmi_smi_watcher smi_watcher = {
#ifdef CONFIG_PROC_FS
#include <linux/sysctl.h>
-static struct ctl_table ipmi_table[] = {
+static const struct ctl_table ipmi_table[] = {
{ .procname = "poweroff_powercycle",
.data = &poweroff_powercycle,
.maxlen = sizeof(poweroff_powercycle),
.mode = 0644,
.proc_handler = proc_dointvec },
- { }
-};
-
-static struct ctl_table ipmi_dir_table[] = {
- { .procname = "ipmi",
- .mode = 0555,
- .child = ipmi_table },
- { }
-};
-
-static struct ctl_table ipmi_root_table[] = {
- { .procname = "dev",
- .mode = 0555,
- .child = ipmi_dir_table },
- { }
};
static struct ctl_table_header *ipmi_table_header;
@@ -689,7 +674,7 @@ static int __init ipmi_poweroff_init(void)
pr_info("Power cycle is enabled\n");
#ifdef CONFIG_PROC_FS
- ipmi_table_header = register_sysctl_table(ipmi_root_table);
+ ipmi_table_header = register_sysctl("dev/ipmi", ipmi_table);
if (!ipmi_table_header) {
pr_err("Unable to register powercycle sysctl\n");
rv = -ENOMEM;
@@ -714,8 +699,6 @@ static int __init ipmi_poweroff_init(void)
#ifdef MODULE
static void __exit ipmi_poweroff_cleanup(void)
{
- int rv;
-
#ifdef CONFIG_PROC_FS
unregister_sysctl_table(ipmi_table_header);
#endif
@@ -723,9 +706,7 @@ static void __exit ipmi_poweroff_cleanup(void)
ipmi_smi_watcher_unregister(&smi_watcher);
if (ready) {
- rv = ipmi_destroy_user(ipmi_user);
- if (rv)
- pr_err("could not cleanup the IPMI user: 0x%x\n", rv);
+ ipmi_destroy_user(ipmi_user);
pm_power_off = old_poweroff_func;
}
}
diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
index a7ead2a4c753..687835b53da5 100644
--- a/drivers/char/ipmi/ipmi_si.h
+++ b/drivers/char/ipmi/ipmi_si.h
@@ -26,6 +26,14 @@ enum si_type {
/* Array is defined in the ipmi_si_intf.c */
extern const char *const si_to_str[];
+struct ipmi_match_info {
+ enum si_type type;
+};
+
+extern const struct ipmi_match_info ipmi_kcs_si_info;
+extern const struct ipmi_match_info ipmi_smic_si_info;
+extern const struct ipmi_match_info ipmi_bt_si_info;
+
enum ipmi_addr_space {
IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
};
@@ -64,7 +72,7 @@ struct si_sm_io {
void (*irq_cleanup)(struct si_sm_io *io);
u8 slave_addr;
- enum si_type si_type;
+ const struct ipmi_match_info *si_info;
struct device *dev;
};
@@ -93,6 +101,13 @@ void ipmi_si_pci_shutdown(void);
static inline void ipmi_si_pci_init(void) { }
static inline void ipmi_si_pci_shutdown(void) { }
#endif
+#ifdef CONFIG_IPMI_LS2K
+void ipmi_si_ls2k_init(void);
+void ipmi_si_ls2k_shutdown(void);
+#else
+static inline void ipmi_si_ls2k_init(void) { }
+static inline void ipmi_si_ls2k_shutdown(void) { }
+#endif
#ifdef CONFIG_PARISC
void ipmi_si_parisc_init(void);
void ipmi_si_parisc_shutdown(void);
diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c
index ed5e91b1e040..0c92fa3eee88 100644
--- a/drivers/char/ipmi/ipmi_si_hardcode.c
+++ b/drivers/char/ipmi/ipmi_si_hardcode.c
@@ -80,10 +80,10 @@ static void __init ipmi_hardcode_init_one(const char *si_type_str,
}
p.regsize = regsizes[i];
+ p.regspacing = regspacings[i];
p.slave_addr = slave_addrs[i];
p.addr_source = SI_HARDCODED;
p.regshift = regshifts[i];
- p.regsize = regsizes[i];
p.addr = addr;
p.space = addr_space;
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 6e357ad76f2e..5459ffdde8dc 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -53,6 +53,7 @@
#define SI_TIMEOUT_JIFFIES (SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY)
#define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a
short timeout */
+#define SI_TIMEOUT_HOSED (HZ) /* 1 second when in hosed state. */
enum si_intf_state {
SI_NORMAL,
@@ -61,7 +62,8 @@ enum si_intf_state {
SI_CLEARING_FLAGS,
SI_GETTING_MESSAGES,
SI_CHECKING_ENABLES,
- SI_SETTING_ENABLES
+ SI_SETTING_ENABLES,
+ SI_HOSED
/* FIXME - add watchdog stuff. */
};
@@ -73,6 +75,10 @@ enum si_intf_state {
/* 'invalid' to allow a firmware-specified interface to be disabled */
const char *const si_to_str[] = { "invalid", "kcs", "smic", "bt", NULL };
+const struct ipmi_match_info ipmi_kcs_si_info = { .type = SI_KCS };
+const struct ipmi_match_info ipmi_smic_si_info = { .type = SI_SMIC };
+const struct ipmi_match_info ipmi_bt_si_info = { .type = SI_BT };
+
static bool initialized;
/*
@@ -269,8 +275,7 @@ void debug_timestamp(struct smi_info *smi_info, char *msg)
struct timespec64 t;
ktime_get_ts64(&t);
- dev_dbg(smi_info->io.dev, "**%s: %lld.%9.9ld\n",
- msg, t.tv_sec, t.tv_nsec);
+ dev_dbg(smi_info->io.dev, "**%s: %ptSp\n", msg, &t);
}
#else
#define debug_timestamp(smi_info, x)
@@ -309,7 +314,7 @@ static void return_hosed_msg(struct smi_info *smi_info, int cCode)
static enum si_sm_result start_next_msg(struct smi_info *smi_info)
{
- int rv;
+ int rv;
if (!smi_info->waiting_msg) {
smi_info->curr_msg = NULL;
@@ -386,6 +391,17 @@ static void start_clear_flags(struct smi_info *smi_info)
smi_info->si_state = SI_CLEARING_FLAGS;
}
+static void start_get_flags(struct smi_info *smi_info)
+{
+ unsigned char msg[2];
+
+ msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
+ msg[1] = IPMI_GET_MSG_FLAGS_CMD;
+
+ start_new_msg(smi_info, msg, 2);
+ smi_info->si_state = SI_GETTING_FLAGS;
+}
+
static void start_getting_msg_queue(struct smi_info *smi_info)
{
smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
@@ -692,7 +708,7 @@ static void handle_transaction_done(struct smi_info *smi_info)
break;
}
enables = current_global_enables(smi_info, 0, &irq_on);
- if (smi_info->io.si_type == SI_BT)
+ if (smi_info->io.si_info->type == SI_BT)
/* BT has its own interrupt enable bit. */
check_bt_irq(smi_info, irq_on);
if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) {
@@ -738,6 +754,8 @@ static void handle_transaction_done(struct smi_info *smi_info)
}
break;
}
+ case SI_HOSED: /* Shouldn't happen. */
+ break;
}
}
@@ -752,6 +770,10 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
enum si_sm_result si_sm_result;
restart:
+ if (smi_info->si_state == SI_HOSED)
+ /* Just in case, hosed state is only left from the timeout. */
+ return SI_SM_HOSED;
+
/*
* There used to be a loop here that waited a little while
* (around 25us) before giving up. That turned out to be
@@ -775,18 +797,20 @@ restart:
/*
* Do the before return_hosed_msg, because that
- * releases the lock.
+ * releases the lock. We just disable operations for
+ * a while and retry in hosed state.
*/
- smi_info->si_state = SI_NORMAL;
+ smi_info->si_state = SI_HOSED;
if (smi_info->curr_msg != NULL) {
/*
* If we were handling a user message, format
* a response to send to the upper layer to
* tell it about the error.
*/
- return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED);
+ return_hosed_msg(smi_info, IPMI_BUS_ERR);
}
- goto restart;
+ smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_HOSED);
+ goto out;
}
/*
@@ -794,8 +818,6 @@ restart:
* this if there is not yet an upper layer to handle anything.
*/
if (si_sm_result == SI_SM_ATTN || smi_info->got_attn) {
- unsigned char msg[2];
-
if (smi_info->si_state != SI_NORMAL) {
/*
* We got an ATTN, but we are doing something else.
@@ -813,11 +835,7 @@ restart:
* interrupts work with the SMI, that's not really
* possible.
*/
- msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
- msg[1] = IPMI_GET_MSG_FLAGS_CMD;
-
- start_new_msg(smi_info, msg, 2);
- smi_info->si_state = SI_GETTING_FLAGS;
+ start_get_flags(smi_info);
goto restart;
}
}
@@ -859,7 +877,7 @@ restart:
if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) {
/* Ok it if fails, the timer will just go off. */
- if (del_timer(&smi_info->si_timer))
+ if (timer_delete(&smi_info->si_timer))
smi_info->timer_running = false;
}
@@ -890,27 +908,29 @@ static void flush_messages(void *send_info)
* mode. This means we are single-threaded, no need for locks.
*/
result = smi_event_handler(smi_info, 0);
- while (result != SI_SM_IDLE) {
+ while (result != SI_SM_IDLE && result != SI_SM_HOSED) {
udelay(SI_SHORT_TIMEOUT_USEC);
result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC);
}
}
-static void sender(void *send_info,
- struct ipmi_smi_msg *msg)
+static int sender(void *send_info, struct ipmi_smi_msg *msg)
{
struct smi_info *smi_info = send_info;
unsigned long flags;
debug_timestamp(smi_info, "Enqueue");
+ if (smi_info->si_state == SI_HOSED)
+ return IPMI_BUS_ERR;
+
if (smi_info->run_to_completion) {
/*
* If we are running to completion, start it. Upper
* layer will call flush_messages to clear it out.
*/
smi_info->waiting_msg = msg;
- return;
+ return IPMI_CC_NO_ERROR;
}
spin_lock_irqsave(&smi_info->si_lock, flags);
@@ -925,6 +945,7 @@ static void sender(void *send_info,
smi_info->waiting_msg = msg;
check_start_timer_thread(smi_info);
spin_unlock_irqrestore(&smi_info->si_lock, flags);
+ return IPMI_CC_NO_ERROR;
}
static void set_run_to_completion(void *send_info, bool i_run_to_completion)
@@ -1072,7 +1093,8 @@ static void set_need_watch(void *send_info, unsigned int watch_mask)
static void smi_timeout(struct timer_list *t)
{
- struct smi_info *smi_info = from_timer(smi_info, t, si_timer);
+ struct smi_info *smi_info = timer_container_of(smi_info, t,
+ si_timer);
enum si_sm_result smi_result;
unsigned long flags;
unsigned long jiffies_now;
@@ -1082,6 +1104,10 @@ static void smi_timeout(struct timer_list *t)
spin_lock_irqsave(&(smi_info->si_lock), flags);
debug_timestamp(smi_info, "Timer");
+ if (smi_info->si_state == SI_HOSED)
+ /* Try something to see if the BMC is now operational. */
+ start_get_flags(smi_info);
+
jiffies_now = jiffies;
time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
* SI_USEC_PER_JIFFY);
@@ -1091,14 +1117,11 @@ static void smi_timeout(struct timer_list *t)
/* Running with interrupts, only do long timeouts. */
timeout = jiffies + SI_TIMEOUT_JIFFIES;
smi_inc_stat(smi_info, long_timeouts);
- goto do_mod_timer;
- }
-
- /*
- * If the state machine asks for a short delay, then shorten
- * the timer timeout.
- */
- if (smi_result == SI_SM_CALL_WITH_DELAY) {
+ } else if (smi_result == SI_SM_CALL_WITH_DELAY) {
+ /*
+ * If the state machine asks for a short delay, then shorten
+ * the timer timeout.
+ */
smi_inc_stat(smi_info, short_timeouts);
timeout = jiffies + 1;
} else {
@@ -1106,7 +1129,6 @@ static void smi_timeout(struct timer_list *t)
timeout = jiffies + SI_TIMEOUT_JIFFIES;
}
-do_mod_timer:
if (smi_result != SI_SM_IDLE)
smi_mod_timer(smi_info, timeout);
else
@@ -1119,7 +1141,7 @@ irqreturn_t ipmi_si_irq_handler(int irq, void *data)
struct smi_info *smi_info = data;
unsigned long flags;
- if (smi_info->io.si_type == SI_BT)
+ if (smi_info->io.si_info->type == SI_BT)
/* We need to clear the IRQ flag for the BT interface. */
smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
IPMI_BT_INTMASK_CLEAR_IRQ_BIT
@@ -1164,7 +1186,7 @@ static int smi_start_processing(void *send_info,
* The BT interface is efficient enough to not need a thread,
* and there is no need for a thread if we have interrupts.
*/
- else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq))
+ else if (new_smi->io.si_info->type != SI_BT && !new_smi->io.irq)
enable = 1;
if (enable) {
@@ -1235,7 +1257,7 @@ MODULE_PARM_DESC(kipmid_max_busy_us,
void ipmi_irq_finish_setup(struct si_sm_io *io)
{
- if (io->si_type == SI_BT)
+ if (io->si_info->type == SI_BT)
/* Enable the interrupt in the BT interface. */
io->outputb(io, IPMI_BT_INTMASK_REG,
IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
@@ -1243,7 +1265,7 @@ void ipmi_irq_finish_setup(struct si_sm_io *io)
void ipmi_irq_start_cleanup(struct si_sm_io *io)
{
- if (io->si_type == SI_BT)
+ if (io->si_info->type == SI_BT)
/* Disable the interrupt in the BT interface. */
io->outputb(io, IPMI_BT_INTMASK_REG, 0);
}
@@ -1614,7 +1636,7 @@ static ssize_t type_show(struct device *dev,
{
struct smi_info *smi_info = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]);
+ return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_info->type]);
}
static DEVICE_ATTR_RO(type);
@@ -1649,7 +1671,7 @@ static ssize_t params_show(struct device *dev,
return sysfs_emit(buf,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
- si_to_str[smi_info->io.si_type],
+ si_to_str[smi_info->io.si_info->type],
addr_space_to_str[smi_info->io.addr_space],
smi_info->io.addr_data,
smi_info->io.regspacing,
@@ -1803,7 +1825,7 @@ setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
{
struct ipmi_device_id *id = &smi_info->device_id;
if (id->manufacturer_id == DELL_IANA_MFR_ID &&
- smi_info->io.si_type == SI_BT)
+ smi_info->io.si_info->type == SI_BT)
register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
}
@@ -1839,7 +1861,7 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info)
}
smi_info->timer_can_start = false;
- del_timer_sync(&smi_info->si_timer);
+ timer_delete_sync(&smi_info->si_timer);
}
static struct smi_info *find_dup_si(struct smi_info *info)
@@ -1882,7 +1904,8 @@ int ipmi_si_add_smi(struct si_sm_io *io)
}
if (!io->io_setup) {
- if (io->addr_space == IPMI_IO_ADDR_SPACE) {
+ if (IS_ENABLED(CONFIG_HAS_IOPORT) &&
+ io->addr_space == IPMI_IO_ADDR_SPACE) {
io->io_setup = ipmi_si_port_setup;
} else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
io->io_setup = ipmi_si_mem_setup;
@@ -1906,13 +1929,13 @@ int ipmi_si_add_smi(struct si_sm_io *io)
/* We prefer ACPI over SMBIOS. */
dev_info(dup->io.dev,
"Removing SMBIOS-specified %s state machine in favor of ACPI\n",
- si_to_str[new_smi->io.si_type]);
+ si_to_str[new_smi->io.si_info->type]);
cleanup_one_si(dup);
} else {
dev_info(new_smi->io.dev,
"%s-specified %s state machine: duplicate\n",
ipmi_addr_src_to_str(new_smi->io.addr_source),
- si_to_str[new_smi->io.si_type]);
+ si_to_str[new_smi->io.si_info->type]);
rv = -EBUSY;
kfree(new_smi);
goto out_err;
@@ -1921,7 +1944,7 @@ int ipmi_si_add_smi(struct si_sm_io *io)
pr_info("Adding %s-specified %s state machine\n",
ipmi_addr_src_to_str(new_smi->io.addr_source),
- si_to_str[new_smi->io.si_type]);
+ si_to_str[new_smi->io.si_info->type]);
list_add_tail(&new_smi->link, &smi_infos);
@@ -1944,12 +1967,12 @@ static int try_smi_init(struct smi_info *new_smi)
pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
ipmi_addr_src_to_str(new_smi->io.addr_source),
- si_to_str[new_smi->io.si_type],
+ si_to_str[new_smi->io.si_info->type],
addr_space_to_str[new_smi->io.addr_space],
new_smi->io.addr_data,
new_smi->io.slave_addr, new_smi->io.irq);
- switch (new_smi->io.si_type) {
+ switch (new_smi->io.si_info->type) {
case SI_KCS:
new_smi->handlers = &kcs_smi_handlers;
break;
@@ -2072,7 +2095,7 @@ static int try_smi_init(struct smi_info *new_smi)
smi_num++;
dev_info(new_smi->io.dev, "IPMI %s interface initialized\n",
- si_to_str[new_smi->io.si_type]);
+ si_to_str[new_smi->io.si_info->type]);
WARN_ON(new_smi->io.dev->init_name != NULL);
@@ -2082,13 +2105,26 @@ static int try_smi_init(struct smi_info *new_smi)
new_smi->io.io_cleanup = NULL;
}
+ if (rv && new_smi->si_sm) {
+ kfree(new_smi->si_sm);
+ new_smi->si_sm = NULL;
+ }
+
return rv;
}
+/*
+ * Devices in the same address space at the same address are the same.
+ */
+static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2)
+{
+ return (e1->io.addr_space == e2->io.addr_space &&
+ e1->io.addr_data == e2->io.addr_data);
+}
+
static int __init init_ipmi_si(void)
{
- struct smi_info *e;
- enum ipmi_addr_src type = SI_INVALID;
+ struct smi_info *e, *e2;
if (initialized)
return 0;
@@ -2101,45 +2137,77 @@ static int __init init_ipmi_si(void)
ipmi_si_pci_init();
+ ipmi_si_ls2k_init();
+
ipmi_si_parisc_init();
- /* We prefer devices with interrupts, but in the case of a machine
- with multiple BMCs we assume that there will be several instances
- of a given type so if we succeed in registering a type then also
- try to register everything else of the same type */
mutex_lock(&smi_infos_lock);
+
+ /*
+ * Scan through all the devices. We prefer devices with
+ * interrupts, so go through those first in case there are any
+ * duplicates that don't have the interrupt set.
+ */
list_for_each_entry(e, &smi_infos, link) {
- /* Try to register a device if it has an IRQ and we either
- haven't successfully registered a device yet or this
- device has the same type as one we successfully registered */
- if (e->io.irq && (!type || e->io.addr_source == type)) {
- if (!try_smi_init(e)) {
- type = e->io.addr_source;
+ bool dup = false;
+
+ /* Register ones with interrupts first. */
+ if (!e->io.irq)
+ continue;
+
+ /*
+ * Go through the ones we have already seen to see if this
+ * is a dup.
+ */
+ list_for_each_entry(e2, &smi_infos, link) {
+ if (e2 == e)
+ break;
+ if (e2->io.irq && ipmi_smi_info_same(e, e2)) {
+ dup = true;
+ break;
}
}
+ if (!dup)
+ try_smi_init(e);
}
- /* type will only have been set if we successfully registered an si */
- if (type)
- goto skip_fallback_noirq;
+ /*
+ * Now try devices without interrupts.
+ */
+ list_for_each_entry(e, &smi_infos, link) {
+ bool dup = false;
- /* Fall back to the preferred device */
+ if (e->io.irq)
+ continue;
- list_for_each_entry(e, &smi_infos, link) {
- if (!e->io.irq && (!type || e->io.addr_source == type)) {
- if (!try_smi_init(e)) {
- type = e->io.addr_source;
+ /*
+ * Go through the ones we have already seen to see if
+ * this is a dup. We have already looked at the ones
+ * with interrupts.
+ */
+ list_for_each_entry(e2, &smi_infos, link) {
+ if (!e2->io.irq)
+ continue;
+ if (ipmi_smi_info_same(e, e2)) {
+ dup = true;
+ break;
}
}
+ list_for_each_entry(e2, &smi_infos, link) {
+ if (e2 == e)
+ break;
+ if (ipmi_smi_info_same(e, e2)) {
+ dup = true;
+ break;
+ }
+ }
+ if (!dup)
+ try_smi_init(e);
}
-skip_fallback_noirq:
initialized = true;
mutex_unlock(&smi_infos_lock);
- if (type)
- return 0;
-
mutex_lock(&smi_infos_lock);
if (unload_when_empty && list_empty(&smi_infos)) {
mutex_unlock(&smi_infos_lock);
@@ -2153,6 +2221,20 @@ skip_fallback_noirq:
}
module_init(init_ipmi_si);
+static void wait_msg_processed(struct smi_info *smi_info)
+{
+ unsigned long jiffies_now;
+ long time_diff;
+
+ while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
+ jiffies_now = jiffies;
+ time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
+ * SI_USEC_PER_JIFFY);
+ smi_event_handler(smi_info, time_diff);
+ schedule_timeout_uninterruptible(1);
+ }
+}
+
static void shutdown_smi(void *send_info)
{
struct smi_info *smi_info = send_info;
@@ -2187,16 +2269,13 @@ static void shutdown_smi(void *send_info)
* in the BMC. Note that timers and CPU interrupts are off,
* so no need for locks.
*/
- while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
- poll(smi_info);
- schedule_timeout_uninterruptible(1);
- }
+ wait_msg_processed(smi_info);
+
if (smi_info->handlers)
disable_si_irq(smi_info);
- while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
- poll(smi_info);
- schedule_timeout_uninterruptible(1);
- }
+
+ wait_msg_processed(smi_info);
+
if (smi_info->handlers)
smi_info->handlers->cleanup(smi_info->si_sm);
@@ -2250,7 +2329,7 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
if (e->io.addr_space != addr_space)
continue;
- if (e->io.si_type != si_type)
+ if (e->io.si_info->type != si_type)
continue;
if (e->io.addr_data == addr) {
dev = get_device(e->io.dev);
@@ -2271,6 +2350,8 @@ static void cleanup_ipmi_si(void)
ipmi_si_pci_shutdown();
+ ipmi_si_ls2k_shutdown();
+
ipmi_si_parisc_shutdown();
ipmi_si_platform_shutdown();
diff --git a/drivers/char/ipmi/ipmi_si_ls2k.c b/drivers/char/ipmi/ipmi_si_ls2k.c
new file mode 100644
index 000000000000..45442c257efd
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_ls2k.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Loongson-2K BMC IPMI interface
+ *
+ * Copyright (C) 2024-2025 Loongson Technology Corporation Limited.
+ *
+ * Authors:
+ * Chong Qiao <qiaochong@loongson.cn>
+ * Binbin Zhou <zhoubinbin@loongson.cn>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "ipmi_si.h"
+
+#define LS2K_KCS_FIFO_IBFH 0x0
+#define LS2K_KCS_FIFO_IBFT 0x1
+#define LS2K_KCS_FIFO_OBFH 0x2
+#define LS2K_KCS_FIFO_OBFT 0x3
+
+/* KCS registers */
+#define LS2K_KCS_REG_STS 0x4
+#define LS2K_KCS_REG_DATA_OUT 0x5
+#define LS2K_KCS_REG_DATA_IN 0x6
+#define LS2K_KCS_REG_CMD 0x8
+
+#define LS2K_KCS_CMD_DATA 0xa
+#define LS2K_KCS_VERSION 0xb
+#define LS2K_KCS_WR_REQ 0xc
+#define LS2K_KCS_WR_ACK 0x10
+
+#define LS2K_KCS_STS_OBF BIT(0)
+#define LS2K_KCS_STS_IBF BIT(1)
+#define LS2K_KCS_STS_SMS_ATN BIT(2)
+#define LS2K_KCS_STS_CMD BIT(3)
+
+#define LS2K_KCS_DATA_MASK (LS2K_KCS_STS_OBF | LS2K_KCS_STS_IBF | LS2K_KCS_STS_CMD)
+
+static bool ls2k_registered;
+
+static unsigned char ls2k_mem_inb_v0(const struct si_sm_io *io, unsigned int offset)
+{
+ void __iomem *addr = io->addr;
+ int reg_offset;
+
+ if (offset & BIT(0)) {
+ reg_offset = LS2K_KCS_REG_STS;
+ } else {
+ writeb(readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_STS_OBF, addr + LS2K_KCS_REG_STS);
+ reg_offset = LS2K_KCS_REG_DATA_OUT;
+ }
+
+ return readb(addr + reg_offset);
+}
+
+static unsigned char ls2k_mem_inb_v1(const struct si_sm_io *io, unsigned int offset)
+{
+ void __iomem *addr = io->addr;
+ unsigned char inb = 0, cmd;
+ bool obf, ibf;
+
+ obf = readb(addr + LS2K_KCS_FIFO_OBFH) ^ readb(addr + LS2K_KCS_FIFO_OBFT);
+ ibf = readb(addr + LS2K_KCS_FIFO_IBFH) ^ readb(addr + LS2K_KCS_FIFO_IBFT);
+ cmd = readb(addr + LS2K_KCS_CMD_DATA);
+
+ if (offset & BIT(0)) {
+ inb = readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_DATA_MASK;
+ inb |= FIELD_PREP(LS2K_KCS_STS_OBF, obf)
+ | FIELD_PREP(LS2K_KCS_STS_IBF, ibf)
+ | FIELD_PREP(LS2K_KCS_STS_CMD, cmd);
+ } else {
+ inb = readb(addr + LS2K_KCS_REG_DATA_OUT);
+ writeb(readb(addr + LS2K_KCS_FIFO_OBFH), addr + LS2K_KCS_FIFO_OBFT);
+ }
+
+ return inb;
+}
+
+static void ls2k_mem_outb_v0(const struct si_sm_io *io, unsigned int offset,
+ unsigned char val)
+{
+ void __iomem *addr = io->addr;
+ unsigned char sts = readb(addr + LS2K_KCS_REG_STS);
+ int reg_offset;
+
+ if (sts & LS2K_KCS_STS_IBF)
+ return;
+
+ if (offset & BIT(0)) {
+ reg_offset = LS2K_KCS_REG_CMD;
+ sts |= LS2K_KCS_STS_CMD;
+ } else {
+ reg_offset = LS2K_KCS_REG_DATA_IN;
+ sts &= ~LS2K_KCS_STS_CMD;
+ }
+
+ writew(val, addr + reg_offset);
+ writeb(sts | LS2K_KCS_STS_IBF, addr + LS2K_KCS_REG_STS);
+ writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ);
+}
+
+static void ls2k_mem_outb_v1(const struct si_sm_io *io, unsigned int offset,
+ unsigned char val)
+{
+ void __iomem *addr = io->addr;
+ unsigned char ibfh, ibft;
+ int reg_offset;
+
+ ibfh = readb(addr + LS2K_KCS_FIFO_IBFH);
+ ibft = readb(addr + LS2K_KCS_FIFO_IBFT);
+
+ if (ibfh ^ ibft)
+ return;
+
+ reg_offset = (offset & BIT(0)) ? LS2K_KCS_REG_CMD : LS2K_KCS_REG_DATA_IN;
+ writew(val, addr + reg_offset);
+
+ writeb(offset & BIT(0), addr + LS2K_KCS_CMD_DATA);
+ writeb(!ibft, addr + LS2K_KCS_FIFO_IBFH);
+ writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ);
+}
+
+static void ls2k_mem_cleanup(struct si_sm_io *io)
+{
+ if (io->addr)
+ iounmap(io->addr);
+}
+
+static int ipmi_ls2k_mem_setup(struct si_sm_io *io)
+{
+ unsigned char version;
+
+ io->addr = ioremap(io->addr_data, io->regspacing);
+ if (!io->addr)
+ return -EIO;
+
+ version = readb(io->addr + LS2K_KCS_VERSION);
+
+ io->inputb = version ? ls2k_mem_inb_v1 : ls2k_mem_inb_v0;
+ io->outputb = version ? ls2k_mem_outb_v1 : ls2k_mem_outb_v0;
+ io->io_cleanup = ls2k_mem_cleanup;
+
+ return 0;
+}
+
+static int ipmi_ls2k_probe(struct platform_device *pdev)
+{
+ struct si_sm_io io;
+
+ memset(&io, 0, sizeof(io));
+
+ io.si_info = &ipmi_kcs_si_info;
+ io.io_setup = ipmi_ls2k_mem_setup;
+ io.addr_data = pdev->resource[0].start;
+ io.regspacing = resource_size(&pdev->resource[0]);
+ io.dev = &pdev->dev;
+
+ dev_dbg(&pdev->dev, "addr 0x%lx, spacing %d.\n", io.addr_data, io.regspacing);
+
+ return ipmi_si_add_smi(&io);
+}
+
+static void ipmi_ls2k_remove(struct platform_device *pdev)
+{
+ ipmi_si_remove_by_dev(&pdev->dev);
+}
+
+struct platform_driver ipmi_ls2k_platform_driver = {
+ .driver = {
+ .name = "ls2k-ipmi-si",
+ },
+ .probe = ipmi_ls2k_probe,
+ .remove = ipmi_ls2k_remove,
+};
+
+void ipmi_si_ls2k_init(void)
+{
+ platform_driver_register(&ipmi_ls2k_platform_driver);
+ ls2k_registered = true;
+}
+
+void ipmi_si_ls2k_shutdown(void)
+{
+ if (ls2k_registered)
+ platform_driver_unregister(&ipmi_ls2k_platform_driver);
+}
diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c
index 2be2967f6b5f..3b0a70d9adbb 100644
--- a/drivers/char/ipmi/ipmi_si_parisc.c
+++ b/drivers/char/ipmi/ipmi_si_parisc.c
@@ -13,7 +13,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev)
memset(&io, 0, sizeof(io));
- io.si_type = SI_KCS;
+ io.si_info = &ipmi_kcs_si_info;
io.addr_source = SI_DEVICETREE;
io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = dev->hpa.start;
diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c
index 74fa2055868b..17f72763322d 100644
--- a/drivers/char/ipmi/ipmi_si_pci.c
+++ b/drivers/char/ipmi/ipmi_si_pci.c
@@ -23,30 +23,32 @@ MODULE_PARM_DESC(trypci,
static int ipmi_pci_probe_regspacing(struct si_sm_io *io)
{
- if (io->si_type == SI_KCS) {
- unsigned char status;
- int regspacing;
-
- io->regsize = DEFAULT_REGSIZE;
- io->regshift = 0;
-
- /* detect 1, 4, 16byte spacing */
- for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
- io->regspacing = regspacing;
- if (io->io_setup(io)) {
- dev_err(io->dev, "Could not setup I/O space\n");
- return DEFAULT_REGSPACING;
- }
- /* write invalid cmd */
- io->outputb(io, 1, 0x10);
- /* read status back */
- status = io->inputb(io, 1);
- io->io_cleanup(io);
- if (status)
- return regspacing;
- regspacing *= 4;
+ unsigned char status;
+ int regspacing;
+
+ if (io->si_info->type != SI_KCS)
+ return DEFAULT_REGSPACING;
+
+ io->regsize = DEFAULT_REGSIZE;
+ io->regshift = 0;
+
+ /* detect 1, 4, 16byte spacing */
+ for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
+ io->regspacing = regspacing;
+ if (io->io_setup(io)) {
+ dev_err(io->dev, "Could not setup I/O space\n");
+ return DEFAULT_REGSPACING;
}
+ /* write invalid cmd */
+ io->outputb(io, 1, 0x10);
+ /* read status back */
+ status = io->inputb(io, 1);
+ io->io_cleanup(io);
+ if (status)
+ return regspacing;
+ regspacing *= 4;
}
+
return DEFAULT_REGSPACING;
}
@@ -74,15 +76,15 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
switch (pdev->class) {
case PCI_CLASS_SERIAL_IPMI_SMIC:
- io.si_type = SI_SMIC;
+ io.si_info = &ipmi_smic_si_info;
break;
case PCI_CLASS_SERIAL_IPMI_KCS:
- io.si_type = SI_KCS;
+ io.si_info = &ipmi_kcs_si_info;
break;
case PCI_CLASS_SERIAL_IPMI_BT:
- io.si_type = SI_BT;
+ io.si_info = &ipmi_bt_si_info;
break;
default:
@@ -97,6 +99,9 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
}
if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
+ if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+ return -ENXIO;
+
io.addr_space = IPMI_IO_ADDR_SPACE;
io.io_setup = ipmi_si_port_setup;
} else {
@@ -115,7 +120,7 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
if (io.irq)
io.irq_setup = ipmi_std_irq_setup;
- dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
+ dev_info(&pdev->dev, "%pR regsize %u spacing %u irq %d\n",
&pdev->resource[0], io.regsize, io.regspacing, io.irq);
return ipmi_si_add_smi(&io);
diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
index 505cc978c97a..fb6e359ae494 100644
--- a/drivers/char/ipmi/ipmi_si_platform.c
+++ b/drivers/char/ipmi/ipmi_si_platform.c
@@ -11,10 +11,11 @@
#include <linux/types.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/acpi.h>
#include "ipmi_si.h"
#include "ipmi_dmi.h"
@@ -162,9 +163,13 @@ static int platform_ipmi_probe(struct platform_device *pdev)
switch (type) {
case SI_KCS:
+ io.si_info = &ipmi_kcs_si_info;
+ break;
case SI_SMIC:
+ io.si_info = &ipmi_smic_si_info;
+ break;
case SI_BT:
- io.si_type = type;
+ io.si_info = &ipmi_bt_si_info;
break;
case SI_TYPE_INVALID: /* User disabled this in hardcode. */
return -ENODEV;
@@ -212,19 +217,15 @@ static int platform_ipmi_probe(struct platform_device *pdev)
#ifdef CONFIG_OF
static const struct of_device_id of_ipmi_match[] = {
- { .type = "ipmi", .compatible = "ipmi-kcs",
- .data = (void *)(unsigned long) SI_KCS },
- { .type = "ipmi", .compatible = "ipmi-smic",
- .data = (void *)(unsigned long) SI_SMIC },
- { .type = "ipmi", .compatible = "ipmi-bt",
- .data = (void *)(unsigned long) SI_BT },
- {},
+ { .type = "ipmi", .compatible = "ipmi-kcs", .data = &ipmi_kcs_si_info },
+ { .type = "ipmi", .compatible = "ipmi-smic", .data = &ipmi_smic_si_info },
+ { .type = "ipmi", .compatible = "ipmi-bt", .data = &ipmi_bt_si_info },
+ {}
};
MODULE_DEVICE_TABLE(of, of_ipmi_match);
static int of_ipmi_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
struct si_sm_io io;
struct resource resource;
const __be32 *regsize, *regspacing, *regshift;
@@ -237,10 +238,6 @@ static int of_ipmi_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "probing via device tree\n");
- match = of_match_device(of_ipmi_match, &pdev->dev);
- if (!match)
- return -ENODEV;
-
if (!of_device_is_available(np))
return -EINVAL;
@@ -269,7 +266,7 @@ static int of_ipmi_probe(struct platform_device *pdev)
}
memset(&io, 0, sizeof(io));
- io.si_type = (enum si_type) match->data;
+ io.si_info = device_get_match_data(&pdev->dev);
io.addr_source = SI_DEVICETREE;
io.irq_setup = ipmi_std_irq_setup;
@@ -300,7 +297,7 @@ static int find_slave_address(struct si_sm_io *io, int slave_addr)
{
#ifdef CONFIG_IPMI_DMI_DECODE
if (!slave_addr)
- slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
+ slave_addr = ipmi_dmi_get_slave_addr(io->si_info->type,
io->addr_space,
io->addr_data);
#endif
@@ -339,13 +336,13 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
switch (tmp) {
case 1:
- io.si_type = SI_KCS;
+ io.si_info = &ipmi_kcs_si_info;
break;
case 2:
- io.si_type = SI_SMIC;
+ io.si_info = &ipmi_smic_si_info;
break;
case 3:
- io.si_type = SI_BT;
+ io.si_info = &ipmi_bt_si_info;
break;
case 4: /* SSIF, just ignore */
return -ENODEV;
@@ -381,7 +378,7 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
dev_info(dev, "%pR regsize %d spacing %d irq %d\n",
res, io.regsize, io.regspacing, io.irq);
- request_module("acpi_ipmi");
+ request_module_nowait("acpi_ipmi");
return ipmi_si_add_smi(&io);
}
@@ -409,11 +406,9 @@ static int ipmi_probe(struct platform_device *pdev)
return platform_ipmi_probe(pdev);
}
-static int ipmi_remove(struct platform_device *pdev)
+static void ipmi_remove(struct platform_device *pdev)
{
ipmi_si_remove_by_dev(&pdev->dev);
-
- return 0;
}
static int pdev_match_name(struct device *dev, const void *data)
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index fc742ee9c046..ef1582a029f4 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -74,7 +74,8 @@
/*
* Timer values
*/
-#define SSIF_MSG_USEC 20000 /* 20ms between message tries. */
+#define SSIF_MSG_USEC 60000 /* 60ms between message tries (T3). */
+#define SSIF_REQ_RETRY_USEC 60000 /* 60ms between send retries (T6). */
#define SSIF_MSG_PART_USEC 5000 /* 5ms for a message part */
/* How many times to we retry sending/receiving the message. */
@@ -82,7 +83,9 @@
#define SSIF_RECV_RETRIES 250
#define SSIF_MSG_MSEC (SSIF_MSG_USEC / 1000)
+#define SSIF_REQ_RETRY_MSEC (SSIF_REQ_RETRY_USEC / 1000)
#define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC)
+#define SSIF_REQ_RETRY_JIFFIES ((SSIF_REQ_RETRY_USEC * 1000) / TICK_NSEC)
#define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC)
/*
@@ -92,7 +95,7 @@
#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250)
enum ssif_intf_state {
- SSIF_NORMAL,
+ SSIF_IDLE,
SSIF_GETTING_FLAGS,
SSIF_GETTING_EVENTS,
SSIF_CLEARING_FLAGS,
@@ -100,8 +103,8 @@ enum ssif_intf_state {
/* FIXME - add watchdog stuff. */
};
-#define SSIF_IDLE(ssif) ((ssif)->ssif_state == SSIF_NORMAL \
- && (ssif)->curr_msg == NULL)
+#define IS_SSIF_IDLE(ssif) ((ssif)->ssif_state == SSIF_IDLE \
+ && (ssif)->curr_msg == NULL)
/*
* Indexes into stats[] in ssif_info below.
@@ -229,6 +232,9 @@ struct ssif_info {
bool got_alert;
bool waiting_alert;
+ /* Used to inform the timeout that it should do a resend. */
+ bool do_resend;
+
/*
* If set to true, this will request events the next time the
* state machine is idle.
@@ -241,12 +247,6 @@ struct ssif_info {
*/
bool req_flags;
- /*
- * Used to perform timer operations when run-to-completion
- * mode is on. This is a countdown timer.
- */
- int rtc_us_timer;
-
/* Used for sending/receiving data. +1 for the length. */
unsigned char data[IPMI_MAX_MSG_LENGTH + 1];
unsigned int data_len;
@@ -348,9 +348,9 @@ static void return_hosed_msg(struct ssif_info *ssif_info,
/*
* Must be called with the message lock held. This will release the
- * message lock. Note that the caller will check SSIF_IDLE and start a
- * new operation, so there is no need to check for new messages to
- * start in here.
+ * message lock. Note that the caller will check IS_SSIF_IDLE and
+ * start a new operation, so there is no need to check for new
+ * messages to start in here.
*/
static void start_clear_flags(struct ssif_info *ssif_info, unsigned long *flags)
{
@@ -367,7 +367,7 @@ static void start_clear_flags(struct ssif_info *ssif_info, unsigned long *flags)
if (start_send(ssif_info, msg, 3) != 0) {
/* Error, just go to normal state. */
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
}
}
@@ -382,7 +382,7 @@ static void start_flag_fetch(struct ssif_info *ssif_info, unsigned long *flags)
mb[0] = (IPMI_NETFN_APP_REQUEST << 2);
mb[1] = IPMI_GET_MSG_FLAGS_CMD;
if (start_send(ssif_info, mb, 2) != 0)
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
}
static void check_start_send(struct ssif_info *ssif_info, unsigned long *flags,
@@ -393,7 +393,7 @@ static void check_start_send(struct ssif_info *ssif_info, unsigned long *flags,
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
ssif_info->curr_msg = NULL;
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
ipmi_free_smi_msg(msg);
}
@@ -407,7 +407,7 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags)
msg = ipmi_alloc_smi_msg();
if (!msg) {
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -430,7 +430,7 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info,
msg = ipmi_alloc_smi_msg();
if (!msg) {
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -448,9 +448,9 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info,
/*
* Must be called with the message lock held. This will release the
- * message lock. Note that the caller will check SSIF_IDLE and start a
- * new operation, so there is no need to check for new messages to
- * start in here.
+ * message lock. Note that the caller will check IS_SSIF_IDLE and
+ * start a new operation, so there is no need to check for new
+ * messages to start in here.
*/
static void handle_flags(struct ssif_info *ssif_info, unsigned long *flags)
{
@@ -466,7 +466,7 @@ static void handle_flags(struct ssif_info *ssif_info, unsigned long *flags)
/* Events available. */
start_event_fetch(ssif_info, flags);
else {
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
}
}
@@ -481,8 +481,6 @@ static int ipmi_ssif_thread(void *data)
/* Wait for something to do */
result = wait_for_completion_interruptible(
&ssif_info->wake_thread);
- if (ssif_info->stopping)
- break;
if (result == -ERESTARTSYS)
continue;
init_completion(&ssif_info->wake_thread);
@@ -530,7 +528,6 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
static void start_get(struct ssif_info *ssif_info)
{
- ssif_info->rtc_us_timer = 0;
ssif_info->multi_pos = 0;
ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ,
@@ -538,27 +535,37 @@ static void start_get(struct ssif_info *ssif_info)
ssif_info->recv, I2C_SMBUS_BLOCK_DATA);
}
+static void start_resend(struct ssif_info *ssif_info);
+
static void retry_timeout(struct timer_list *t)
{
- struct ssif_info *ssif_info = from_timer(ssif_info, t, retry_timer);
+ struct ssif_info *ssif_info = timer_container_of(ssif_info, t,
+ retry_timer);
unsigned long oflags, *flags;
- bool waiting;
+ bool waiting, resend;
if (ssif_info->stopping)
return;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ resend = ssif_info->do_resend;
+ ssif_info->do_resend = false;
waiting = ssif_info->waiting_alert;
ssif_info->waiting_alert = false;
ipmi_ssif_unlock_cond(ssif_info, flags);
if (waiting)
start_get(ssif_info);
+ if (resend) {
+ start_resend(ssif_info);
+ ssif_inc_stat(ssif_info, send_retries);
+ }
}
static void watch_timeout(struct timer_list *t)
{
- struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer);
+ struct ssif_info *ssif_info = timer_container_of(ssif_info, t,
+ watch_timer);
unsigned long oflags, *flags;
if (ssif_info->stopping)
@@ -568,7 +575,7 @@ static void watch_timeout(struct timer_list *t)
if (ssif_info->watch_timeout) {
mod_timer(&ssif_info->watch_timer,
jiffies + ssif_info->watch_timeout);
- if (SSIF_IDLE(ssif_info)) {
+ if (IS_SSIF_IDLE(ssif_info)) {
start_flag_fetch(ssif_info, flags); /* Releases lock */
return;
}
@@ -592,7 +599,7 @@ static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type,
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
if (ssif_info->waiting_alert) {
ssif_info->waiting_alert = false;
- del_timer(&ssif_info->retry_timer);
+ timer_delete(&ssif_info->retry_timer);
do_get = true;
} else if (ssif_info->curr_msg) {
ssif_info->got_alert = true;
@@ -602,8 +609,6 @@ static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type,
start_get(ssif_info);
}
-static int start_resend(struct ssif_info *ssif_info);
-
static void msg_done_handler(struct ssif_info *ssif_info, int result,
unsigned char *data, unsigned int len)
{
@@ -622,7 +627,6 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
ssif_info->waiting_alert = true;
- ssif_info->rtc_us_timer = SSIF_MSG_USEC;
if (!ssif_info->stopping)
mod_timer(&ssif_info->retry_timer,
jiffies + SSIF_MSG_JIFFIES);
@@ -756,7 +760,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
}
switch (ssif_info->ssif_state) {
- case SSIF_NORMAL:
+ case SSIF_IDLE:
ipmi_ssif_unlock_cond(ssif_info, flags);
if (!msg)
break;
@@ -774,7 +778,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
* Error fetching flags, or invalid length,
* just give up for now.
*/
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
dev_warn(&ssif_info->client->dev,
"Error getting flags: %d %d, %x\n",
@@ -782,9 +786,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
/*
- * Don't abort here, maybe it was a queued
- * response to a previous command.
+ * Recv error response, give up.
*/
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
dev_warn(&ssif_info->client->dev,
"Invalid response getting flags: %x %x\n",
@@ -809,7 +813,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
"Invalid response clearing flags: %x %x\n",
data[0], data[1]);
}
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
break;
@@ -887,7 +891,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
}
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
- if (SSIF_IDLE(ssif_info) && !ssif_info->stopping) {
+ if (IS_SSIF_IDLE(ssif_info) && !ssif_info->stopping) {
if (ssif_info->req_events)
start_event_fetch(ssif_info, flags);
else if (ssif_info->req_flags)
@@ -909,31 +913,23 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
if (result < 0) {
ssif_info->retries_left--;
if (ssif_info->retries_left > 0) {
- if (!start_resend(ssif_info)) {
- ssif_inc_stat(ssif_info, send_retries);
- return;
- }
- /* request failed, just return the error. */
- ssif_inc_stat(ssif_info, send_errors);
-
- if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- dev_dbg(&ssif_info->client->dev,
- "%s: Out of retries\n", __func__);
- msg_done_handler(ssif_info, -EIO, NULL, 0);
+ /*
+ * Wait the retry timeout time per the spec,
+ * then redo the send.
+ */
+ ssif_info->do_resend = true;
+ mod_timer(&ssif_info->retry_timer,
+ jiffies + SSIF_REQ_RETRY_JIFFIES);
return;
}
ssif_inc_stat(ssif_info, send_errors);
- /*
- * Got an error on transmit, let the done routine
- * handle it.
- */
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev,
- "%s: Error %d\n", __func__, result);
+ "%s: Out of retries\n", __func__);
- msg_done_handler(ssif_info, result, NULL, 0);
+ msg_done_handler(ssif_info, -EIO, NULL, 0);
return;
}
@@ -984,10 +980,9 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ipmi_ssif_unlock_cond(ssif_info, flags);
start_get(ssif_info);
} else {
- /* Wait a jiffie then request the next message */
+ /* Wait a jiffy then request the next message */
ssif_info->waiting_alert = true;
ssif_info->retries_left = SSIF_RECV_RETRIES;
- ssif_info->rtc_us_timer = SSIF_MSG_PART_USEC;
if (!ssif_info->stopping)
mod_timer(&ssif_info->retry_timer,
jiffies + SSIF_MSG_PART_JIFFIES);
@@ -996,7 +991,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
}
}
-static int start_resend(struct ssif_info *ssif_info)
+static void start_resend(struct ssif_info *ssif_info)
{
int command;
@@ -1021,7 +1016,6 @@ static int start_resend(struct ssif_info *ssif_info)
ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE,
command, ssif_info->data, I2C_SMBUS_BLOCK_DATA);
- return 0;
}
static int start_send(struct ssif_info *ssif_info,
@@ -1036,7 +1030,8 @@ static int start_send(struct ssif_info *ssif_info,
ssif_info->retries_left = SSIF_SEND_RETRIES;
memcpy(ssif_info->data + 1, data, len);
ssif_info->data_len = len;
- return start_resend(ssif_info);
+ start_resend(ssif_info);
+ return 0;
}
/* Must be called with the message lock held. */
@@ -1046,7 +1041,7 @@ static void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags)
unsigned long oflags;
restart:
- if (!SSIF_IDLE(ssif_info)) {
+ if (!IS_SSIF_IDLE(ssif_info)) {
ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -1073,8 +1068,7 @@ static void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags)
}
}
-static void sender(void *send_info,
- struct ipmi_smi_msg *msg)
+static int sender(void *send_info, struct ipmi_smi_msg *msg)
{
struct ssif_info *ssif_info = send_info;
unsigned long oflags, *flags;
@@ -1089,11 +1083,10 @@ static void sender(void *send_info,
struct timespec64 t;
ktime_get_real_ts64(&t);
- dev_dbg(&ssif_info->client->dev,
- "**Enqueue %02x %02x: %lld.%6.6ld\n",
- msg->data[0], msg->data[1],
- (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC);
+ dev_dbg(&ssif_info->client->dev, "**Enqueue %02x %02x: %ptSp\n",
+ msg->data[0], msg->data[1], &t);
}
+ return IPMI_CC_NO_ERROR;
}
static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
@@ -1269,28 +1262,23 @@ static void shutdown_ssif(void *send_info)
dev_set_drvdata(&ssif_info->client->dev, NULL);
/* make sure the driver is not looking for flags any more. */
- while (ssif_info->ssif_state != SSIF_NORMAL)
+ while (ssif_info->ssif_state != SSIF_IDLE)
schedule_timeout(1);
ssif_info->stopping = true;
- del_timer_sync(&ssif_info->watch_timer);
- del_timer_sync(&ssif_info->retry_timer);
- if (ssif_info->thread) {
- complete(&ssif_info->wake_thread);
+ timer_delete_sync(&ssif_info->watch_timer);
+ timer_delete_sync(&ssif_info->retry_timer);
+ if (ssif_info->thread)
kthread_stop(ssif_info->thread);
- }
}
-static int ssif_remove(struct i2c_client *client)
+static void ssif_remove(struct i2c_client *client)
{
struct ssif_info *ssif_info = i2c_get_clientdata(client);
struct ssif_addr_info *addr_info;
- if (!ssif_info)
- return 0;
-
/*
- * After this point, we won't deliver anything asychronously
+ * After this point, we won't deliver anything asynchronously
* to the message handler. We can unregister ourself.
*/
ipmi_unregister_smi(ssif_info->intf);
@@ -1303,8 +1291,6 @@ static int ssif_remove(struct i2c_client *client)
}
kfree(ssif_info);
-
- return 0;
}
static int read_response(struct i2c_client *client, unsigned char *resp)
@@ -1336,8 +1322,10 @@ static int do_cmd(struct i2c_client *client, int len, unsigned char *msg,
ret = i2c_smbus_write_block_data(client, SSIF_IPMI_REQUEST, len, msg);
if (ret) {
retry_cnt--;
- if (retry_cnt > 0)
+ if (retry_cnt > 0) {
+ msleep(SSIF_REQ_RETRY_MSEC);
goto retry1;
+ }
return -ENODEV;
}
@@ -1376,8 +1364,20 @@ static int ssif_detect(struct i2c_client *client, struct i2c_board_info *info)
rv = do_cmd(client, 2, msg, &len, resp);
if (rv)
rv = -ENODEV;
- else
+ else {
+ if (len < 3) {
+ rv = -ENODEV;
+ } else {
+ struct ipmi_device_id id;
+
+ rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1],
+ resp + 2, len - 2, &id);
+ if (rv)
+ rv = -ENODEV; /* Error means a BMC probably isn't there. */
+ }
+ if (!rv && info)
strscpy(info->type, DEVICE_NAME, I2C_NAME_SIZE);
+ }
kfree(resp);
return rv;
}
@@ -1408,7 +1408,7 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr,
restart:
list_for_each_entry(info, &ssif_infos, link) {
if (info->binfo.addr == addr) {
- if (info->addr_src == SI_SMBIOS)
+ if (info->addr_src == SI_SMBIOS && !info->adapter_name)
info->adapter_name = kstrdup(adapter_name,
GFP_KERNEL);
@@ -1447,7 +1447,7 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev)
if (acpi_handle) {
ssif_info->addr_source = SI_ACPI;
ssif_info->addr_info.acpi_info.acpi_handle = acpi_handle;
- request_module("acpi_ipmi");
+ request_module_nowait("acpi_ipmi");
return true;
}
#endif
@@ -1478,8 +1478,10 @@ retry_write:
32, msg);
if (ret) {
retry_cnt--;
- if (retry_cnt > 0)
+ if (retry_cnt > 0) {
+ msleep(SSIF_REQ_RETRY_MSEC);
goto retry_write;
+ }
dev_err(&client->dev, "Could not write multi-part start, though the BMC said it could handle it. Just limit sends to one part.\n");
return ret;
}
@@ -1606,6 +1608,11 @@ static int ssif_add_infos(struct i2c_client *client)
info->addr_src = SI_ACPI;
info->client = client;
info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL);
+ if (!info->adapter_name) {
+ kfree(info);
+ return -ENOMEM;
+ }
+
info->binfo.addr = client->addr;
list_add_tail(&info->link, &ssif_infos);
return 0;
@@ -1705,6 +1712,16 @@ static int ssif_probe(struct i2c_client *client)
ipmi_addr_src_to_str(ssif_info->addr_source),
client->addr, client->adapter->name, slave_addr);
+ /*
+ * Send a get device id command and validate its response to
+ * make sure a valid BMC is there.
+ */
+ rv = ssif_detect(client, NULL);
+ if (rv) {
+ dev_err(&client->dev, "Not present\n");
+ goto out;
+ }
+
/* Now check for system interface capabilities */
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_SYSTEM_INTERFACE_CAPABILITIES_CMD;
@@ -1841,7 +1858,7 @@ static int ssif_probe(struct i2c_client *client)
}
spin_lock_init(&ssif_info->lock);
- ssif_info->ssif_state = SSIF_NORMAL;
+ ssif_info->ssif_state = SSIF_IDLE;
timer_setup(&ssif_info->retry_timer, retry_timeout, 0);
timer_setup(&ssif_info->watch_timer, watch_timeout, 0);
@@ -1946,7 +1963,7 @@ static int new_ssif_client(int addr, char *adapter_name,
}
}
- strncpy(addr_info->binfo.type, DEVICE_NAME,
+ strscpy(addr_info->binfo.type, DEVICE_NAME,
sizeof(addr_info->binfo.type));
addr_info->binfo.addr = addr;
addr_info->binfo.platform_data = addr_info;
@@ -2050,7 +2067,7 @@ static int dmi_ipmi_probe(struct platform_device *pdev)
#endif
static const struct i2c_device_id ssif_id[] = {
- { DEVICE_NAME, 0 },
+ { DEVICE_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssif_id);
@@ -2060,7 +2077,7 @@ static struct i2c_driver ssif_i2c_driver = {
.driver = {
.name = DEVICE_NAME
},
- .probe_new = ssif_probe,
+ .probe = ssif_probe,
.remove = ssif_remove,
.alert = ssif_alert,
.id_table = ssif_id,
@@ -2072,24 +2089,21 @@ static int ssif_platform_probe(struct platform_device *dev)
return dmi_ipmi_probe(dev);
}
-static int ssif_platform_remove(struct platform_device *dev)
+static void ssif_platform_remove(struct platform_device *dev)
{
struct ssif_addr_info *addr_info = dev_get_drvdata(&dev->dev);
- if (!addr_info)
- return 0;
-
mutex_lock(&ssif_infos_mutex);
list_del(&addr_info->link);
kfree(addr_info);
mutex_unlock(&ssif_infos_mutex);
- return 0;
}
static const struct platform_device_id ssif_plat_ids[] = {
{ "dmi-ipmi-ssif", 0 },
{ }
};
+MODULE_DEVICE_TABLE(platform, ssif_plat_ids);
static struct platform_driver ipmi_driver = {
.driver = {
@@ -2100,7 +2114,7 @@ static struct platform_driver ipmi_driver = {
.id_table = ssif_plat_ids
};
-static int init_ipmi_ssif(void)
+static int __init init_ipmi_ssif(void)
{
int i;
int rv;
@@ -2142,7 +2156,7 @@ static int init_ipmi_ssif(void)
}
module_init(init_ipmi_ssif);
-static void cleanup_ipmi_ssif(void)
+static void __exit cleanup_ipmi_ssif(void)
{
if (!initialized)
return;
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 5b4e677929ca..a013ddbf1466 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -23,6 +23,7 @@
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/kdebug.h>
+#include <linux/kstrtox.h>
#include <linux/rwsem.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
@@ -149,7 +150,7 @@ static char preaction[16] = "pre_none";
static unsigned char preop_val = WDOG_PREOP_NONE;
static char preop[16] = "preop_none";
-static DEFINE_SPINLOCK(ipmi_read_lock);
+static DEFINE_MUTEX(ipmi_read_mutex);
static char data_to_read;
static DECLARE_WAIT_QUEUE_HEAD(read_q);
static struct fasync_struct *fasync_q;
@@ -212,8 +213,7 @@ static int set_param_str(const char *val, const struct kernel_param *kp)
char valcp[16];
char *s;
- strncpy(valcp, val, 15);
- valcp[15] = '\0';
+ strscpy(valcp, val, 16);
s = strstrip(valcp);
@@ -363,7 +363,7 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg,
{
struct kernel_ipmi_msg msg;
unsigned char data[6];
- int rv;
+ int rv = 0;
struct ipmi_system_interface_addr addr;
int hbnow = 0;
@@ -405,14 +405,18 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg,
msg.cmd = IPMI_WDOG_SET_TIMER;
msg.data = data;
msg.data_len = sizeof(data);
- rv = ipmi_request_supply_msgs(watchdog_user,
- (struct ipmi_addr *) &addr,
- 0,
- &msg,
- NULL,
- smi_msg,
- recv_msg,
- 1);
+ if (smi_msg)
+ rv = ipmi_request_supply_msgs(watchdog_user,
+ (struct ipmi_addr *) &addr,
+ 0,
+ &msg,
+ NULL,
+ smi_msg,
+ recv_msg,
+ 1);
+ else
+ ipmi_panic_request_and_wait(watchdog_user,
+ (struct ipmi_addr *) &addr, &msg);
if (rv)
pr_warn("set timeout error: %d\n", rv);
else if (send_heartbeat_now)
@@ -431,9 +435,7 @@ static int _ipmi_set_timeout(int do_heartbeat)
atomic_set(&msg_tofree, 2);
- rv = __ipmi_set_timeout(&smi_msg,
- &recv_msg,
- &send_heartbeat_now);
+ rv = __ipmi_set_timeout(&smi_msg, &recv_msg, &send_heartbeat_now);
if (rv) {
atomic_set(&msg_tofree, 0);
return rv;
@@ -460,27 +462,10 @@ static int ipmi_set_timeout(int do_heartbeat)
return rv;
}
-static atomic_t panic_done_count = ATOMIC_INIT(0);
-
-static void panic_smi_free(struct ipmi_smi_msg *msg)
-{
- atomic_dec(&panic_done_count);
-}
-static void panic_recv_free(struct ipmi_recv_msg *msg)
-{
- atomic_dec(&panic_done_count);
-}
-
-static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg =
- INIT_IPMI_SMI_MSG(panic_smi_free);
-static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
- INIT_IPMI_RECV_MSG(panic_recv_free);
-
static void panic_halt_ipmi_heartbeat(void)
{
struct kernel_ipmi_msg msg;
struct ipmi_system_interface_addr addr;
- int rv;
/*
* Don't reset the timer if we have the timer turned off, that
@@ -497,24 +482,10 @@ static void panic_halt_ipmi_heartbeat(void)
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
- atomic_add(2, &panic_done_count);
- rv = ipmi_request_supply_msgs(watchdog_user,
- (struct ipmi_addr *) &addr,
- 0,
- &msg,
- NULL,
- &panic_halt_heartbeat_smi_msg,
- &panic_halt_heartbeat_recv_msg,
- 1);
- if (rv)
- atomic_sub(2, &panic_done_count);
+ ipmi_panic_request_and_wait(watchdog_user, (struct ipmi_addr *) &addr,
+ &msg);
}
-static struct ipmi_smi_msg panic_halt_smi_msg =
- INIT_IPMI_SMI_MSG(panic_smi_free);
-static struct ipmi_recv_msg panic_halt_recv_msg =
- INIT_IPMI_RECV_MSG(panic_recv_free);
-
/*
* Special call, doesn't claim any locks. This is only to be called
* at panic or halt time, in run-to-completion mode, when the caller
@@ -526,22 +497,13 @@ static void panic_halt_ipmi_set_timeout(void)
int send_heartbeat_now;
int rv;
- /* Wait for the messages to be free. */
- while (atomic_read(&panic_done_count) != 0)
- ipmi_poll_interface(watchdog_user);
- atomic_add(2, &panic_done_count);
- rv = __ipmi_set_timeout(&panic_halt_smi_msg,
- &panic_halt_recv_msg,
- &send_heartbeat_now);
+ rv = __ipmi_set_timeout(NULL, NULL, &send_heartbeat_now);
if (rv) {
- atomic_sub(2, &panic_done_count);
pr_warn("Unable to extend the watchdog timeout\n");
} else {
if (send_heartbeat_now)
panic_halt_ipmi_heartbeat();
}
- while (atomic_read(&panic_done_count) != 0)
- ipmi_poll_interface(watchdog_user);
}
static int __ipmi_heartbeat(void)
@@ -793,7 +755,7 @@ static ssize_t ipmi_read(struct file *file,
* Reading returns if the pretimeout has gone off, and it only does
* it once per pretimeout.
*/
- spin_lock_irq(&ipmi_read_lock);
+ mutex_lock(&ipmi_read_mutex);
if (!data_to_read) {
if (file->f_flags & O_NONBLOCK) {
rv = -EAGAIN;
@@ -802,11 +764,11 @@ static ssize_t ipmi_read(struct file *file,
init_waitqueue_entry(&wait, current);
add_wait_queue(&read_q, &wait);
- while (!data_to_read) {
+ while (!data_to_read && !signal_pending(current)) {
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&ipmi_read_lock);
+ mutex_unlock(&ipmi_read_mutex);
schedule();
- spin_lock_irq(&ipmi_read_lock);
+ mutex_lock(&ipmi_read_mutex);
}
remove_wait_queue(&read_q, &wait);
@@ -818,7 +780,7 @@ static ssize_t ipmi_read(struct file *file,
data_to_read = 0;
out:
- spin_unlock_irq(&ipmi_read_lock);
+ mutex_unlock(&ipmi_read_mutex);
if (rv == 0) {
if (copy_to_user(buf, &data_to_read, 1))
@@ -856,10 +818,10 @@ static __poll_t ipmi_poll(struct file *file, poll_table *wait)
poll_wait(file, &read_q, wait);
- spin_lock_irq(&ipmi_read_lock);
+ mutex_lock(&ipmi_read_mutex);
if (data_to_read)
mask |= (EPOLLIN | EPOLLRDNORM);
- spin_unlock_irq(&ipmi_read_lock);
+ mutex_unlock(&ipmi_read_mutex);
return mask;
}
@@ -903,7 +865,6 @@ static const struct file_operations ipmi_wdog_fops = {
.open = ipmi_open,
.release = ipmi_close,
.fasync = ipmi_fasync,
- .llseek = no_llseek,
};
static struct miscdevice ipmi_wdog_miscdev = {
@@ -933,13 +894,11 @@ static void ipmi_wdog_pretimeout_handler(void *handler_data)
if (atomic_inc_and_test(&preop_panic_excl))
panic("Watchdog pre-timeout");
} else if (preop_val == WDOG_PREOP_GIVE_DATA) {
- unsigned long flags;
-
- spin_lock_irqsave(&ipmi_read_lock, flags);
+ mutex_lock(&ipmi_read_mutex);
data_to_read = 1;
wake_up_interruptible(&read_q);
kill_fasync(&fasync_q, SIGIO, POLL_IN);
- spin_unlock_irqrestore(&ipmi_read_lock, flags);
+ mutex_unlock(&ipmi_read_mutex);
}
}
@@ -1065,7 +1024,6 @@ static void ipmi_register_watchdog(int ipmi_intf)
static void ipmi_unregister_watchdog(int ipmi_intf)
{
- int rv;
struct ipmi_user *loc_user = watchdog_user;
if (!loc_user)
@@ -1090,9 +1048,7 @@ static void ipmi_unregister_watchdog(int ipmi_intf)
mutex_lock(&ipmi_watchdog_mutex);
/* Disconnect from IPMI. */
- rv = ipmi_destroy_user(loc_user);
- if (rv)
- pr_warn("error unlinking from IPMI: %d\n", rv);
+ ipmi_destroy_user(loc_user);
/* If it comes back, restart it properly. */
ipmi_start_timer_on_heartbeat = 1;
@@ -1190,14 +1146,8 @@ static struct ipmi_smi_watcher smi_watcher = {
.smi_gone = ipmi_smi_gone
};
-static int action_op(const char *inval, char *outval)
+static int action_op_set_val(const char *inval)
{
- if (outval)
- strcpy(outval, action);
-
- if (!inval)
- return 0;
-
if (strcmp(inval, "reset") == 0)
action_val = WDOG_TIMEOUT_RESET;
else if (strcmp(inval, "none") == 0)
@@ -1208,18 +1158,26 @@ static int action_op(const char *inval, char *outval)
action_val = WDOG_TIMEOUT_POWER_DOWN;
else
return -EINVAL;
- strcpy(action, inval);
return 0;
}
-static int preaction_op(const char *inval, char *outval)
+static int action_op(const char *inval, char *outval)
{
+ int rv;
+
if (outval)
- strcpy(outval, preaction);
+ strcpy(outval, action);
if (!inval)
return 0;
+ rv = action_op_set_val(inval);
+ if (!rv)
+ strcpy(action, inval);
+ return rv;
+}
+static int preaction_op_set_val(const char *inval)
+{
if (strcmp(inval, "pre_none") == 0)
preaction_val = WDOG_PRETIMEOUT_NONE;
else if (strcmp(inval, "pre_smi") == 0)
@@ -1232,18 +1190,26 @@ static int preaction_op(const char *inval, char *outval)
preaction_val = WDOG_PRETIMEOUT_MSG_INT;
else
return -EINVAL;
- strcpy(preaction, inval);
return 0;
}
-static int preop_op(const char *inval, char *outval)
+static int preaction_op(const char *inval, char *outval)
{
+ int rv;
+
if (outval)
- strcpy(outval, preop);
+ strcpy(outval, preaction);
if (!inval)
return 0;
+ rv = preaction_op_set_val(inval);
+ if (!rv)
+ strcpy(preaction, inval);
+ return 0;
+}
+static int preop_op_set_val(const char *inval)
+{
if (strcmp(inval, "preop_none") == 0)
preop_val = WDOG_PREOP_NONE;
else if (strcmp(inval, "preop_panic") == 0)
@@ -1252,7 +1218,22 @@ static int preop_op(const char *inval, char *outval)
preop_val = WDOG_PREOP_GIVE_DATA;
else
return -EINVAL;
- strcpy(preop, inval);
+ return 0;
+}
+
+static int preop_op(const char *inval, char *outval)
+{
+ int rv;
+
+ if (outval)
+ strcpy(outval, preop);
+
+ if (!inval)
+ return 0;
+
+ rv = preop_op_set_val(inval);
+ if (!rv)
+ strcpy(preop, inval);
return 0;
}
@@ -1289,18 +1270,18 @@ static int __init ipmi_wdog_init(void)
{
int rv;
- if (action_op(action, NULL)) {
+ if (action_op_set_val(action)) {
action_op("reset", NULL);
pr_info("Unknown action '%s', defaulting to reset\n", action);
}
- if (preaction_op(preaction, NULL)) {
+ if (preaction_op_set_val(preaction)) {
preaction_op("pre_none", NULL);
pr_info("Unknown preaction '%s', defaulting to none\n",
preaction);
}
- if (preop_op(preop, NULL)) {
+ if (preop_op_set_val(preop)) {
preop_op("preop_none", NULL);
pr_info("Unknown preop '%s', defaulting to none\n", preop);
}
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index 03d02a848f3a..8b1161d5194a 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -56,12 +56,13 @@ irqreturn_t kcs_bmc_handle_event(struct kcs_bmc_device *kcs_bmc)
{
struct kcs_bmc_client *client;
irqreturn_t rc = IRQ_NONE;
+ unsigned long flags;
- spin_lock(&kcs_bmc->lock);
+ spin_lock_irqsave(&kcs_bmc->lock, flags);
client = kcs_bmc->client;
if (client)
rc = client->ops->event(client);
- spin_unlock(&kcs_bmc->lock);
+ spin_unlock_irqrestore(&kcs_bmc->lock, flags);
return rc;
}
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index cdc88cde1e9a..a13a3470c17a 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/regmap.h>
@@ -207,17 +206,24 @@ static void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask,
}
/*
- * AST_usrGuide_KCS.pdf
- * 2. Background:
- * we note D for Data, and C for Cmd/Status, default rules are
- * A. KCS1 / KCS2 ( D / C:X / X+4 )
- * D / C : CA0h / CA4h
- * D / C : CA8h / CACh
- * B. KCS3 ( D / C:XX2h / XX3h )
- * D / C : CA2h / CA3h
- * D / C : CB2h / CB3h
- * C. KCS4
- * D / C : CA4h / CA5h
+ * We note D for Data, and C for Cmd/Status, default rules are
+ *
+ * 1. Only the D address is given:
+ * A. KCS1/KCS2 (D/C: X/X+4)
+ * D/C: CA0h/CA4h
+ * D/C: CA8h/CACh
+ * B. KCS3 (D/C: XX2/XX3h)
+ * D/C: CA2h/CA3h
+ * C. KCS4 (D/C: X/X+1)
+ * D/C: CA4h/CA5h
+ *
+ * 2. Both the D/C addresses are given:
+ * A. KCS1/KCS2/KCS4 (D/C: X/Y)
+ * D/C: CA0h/CA1h
+ * D/C: CA8h/CA9h
+ * D/C: CA4h/CA5h
+ * B. KCS3 (D/C: XX2/XX3h)
+ * D/C: CA2h/CA3h
*/
static int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32 addrs[2], int nr_addrs)
{
@@ -399,13 +405,31 @@ static void aspeed_kcs_check_obe(struct timer_list *timer)
static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
{
struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
+ int rc;
+ u8 str;
/* We don't have an OBE IRQ, emulate it */
if (mask & KCS_BMC_EVENT_TYPE_OBE) {
- if (KCS_BMC_EVENT_TYPE_OBE & state)
- mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
- else
- del_timer(&priv->obe.timer);
+ if (KCS_BMC_EVENT_TYPE_OBE & state) {
+ /*
+ * Given we don't have an OBE IRQ, delay by polling briefly to see if we can
+ * observe such an event before returning to the caller. This is not
+ * incorrect because OBF may have already become clear before enabling the
+ * IRQ if we had one, under which circumstance no event will be propagated
+ * anyway.
+ *
+ * The onus is on the client to perform a race-free check that it hasn't
+ * missed the event.
+ */
+ rc = read_poll_timeout_atomic(aspeed_kcs_inb, str,
+ !(str & KCS_BMC_STR_OBF), 1, 100, false,
+ &priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
+ /* Time for the slow path? */
+ if (rc == -ETIMEDOUT)
+ mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
+ } else {
+ timer_delete(&priv->obe.timer);
+ }
}
if (mask & KCS_BMC_EVENT_TYPE_IBF) {
@@ -617,7 +641,7 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
return 0;
}
-static int aspeed_kcs_remove(struct platform_device *pdev)
+static void aspeed_kcs_remove(struct platform_device *pdev)
{
struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
@@ -631,9 +655,7 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
spin_lock_irq(&priv->obe.lock);
priv->obe.remove = true;
spin_unlock_irq(&priv->obe.lock);
- del_timer_sync(&priv->obe.timer);
-
- return 0;
+ timer_delete_sync(&priv->obe.timer);
}
static const struct of_device_id ast_kcs_bmc_match[] = {
diff --git a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
index 486834a962c3..cf670e891966 100644
--- a/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
+++ b/drivers/char/ipmi/kcs_bmc_cdev_ipmi.c
@@ -548,7 +548,7 @@ static struct kcs_bmc_driver kcs_bmc_ipmi_driver = {
.ops = &kcs_bmc_ipmi_driver_ops,
};
-static int kcs_bmc_ipmi_init(void)
+static int __init kcs_bmc_ipmi_init(void)
{
kcs_bmc_register_driver(&kcs_bmc_ipmi_driver);
@@ -556,7 +556,7 @@ static int kcs_bmc_ipmi_init(void)
}
module_init(kcs_bmc_ipmi_init);
-static void kcs_bmc_ipmi_exit(void)
+static void __exit kcs_bmc_ipmi_exit(void)
{
kcs_bmc_unregister_driver(&kcs_bmc_ipmi_driver);
}
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
index 7961fec56476..4808a61bf273 100644
--- a/drivers/char/ipmi/kcs_bmc_npcm7xx.c
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -218,7 +218,7 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
return 0;
}
-static int npcm7xx_kcs_remove(struct platform_device *pdev)
+static void npcm7xx_kcs_remove(struct platform_device *pdev)
{
struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
@@ -227,8 +227,6 @@ static int npcm7xx_kcs_remove(struct platform_device *pdev)
npcm7xx_kcs_enable_channel(kcs_bmc, false);
npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
-
- return 0;
}
static const struct of_device_id npcm_kcs_bmc_match[] = {
@@ -243,7 +241,7 @@ static struct platform_driver npcm_kcs_bmc_driver = {
.of_match_table = npcm_kcs_bmc_match,
},
.probe = npcm7xx_kcs_probe,
- .remove = npcm7xx_kcs_remove,
+ .remove = npcm7xx_kcs_remove,
};
module_platform_driver(npcm_kcs_bmc_driver);
diff --git a/drivers/char/ipmi/kcs_bmc_serio.c b/drivers/char/ipmi/kcs_bmc_serio.c
index 7e2067628a6c..1793358be782 100644
--- a/drivers/char/ipmi/kcs_bmc_serio.c
+++ b/drivers/char/ipmi/kcs_bmc_serio.c
@@ -140,7 +140,7 @@ static struct kcs_bmc_driver kcs_bmc_serio_driver = {
.ops = &kcs_bmc_serio_driver_ops,
};
-static int kcs_bmc_serio_init(void)
+static int __init kcs_bmc_serio_init(void)
{
kcs_bmc_register_driver(&kcs_bmc_serio_driver);
@@ -148,7 +148,7 @@ static int kcs_bmc_serio_init(void)
}
module_init(kcs_bmc_serio_init);
-static void kcs_bmc_serio_exit(void)
+static void __exit kcs_bmc_serio_exit(void)
{
kcs_bmc_unregister_driver(&kcs_bmc_serio_driver);
}
diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c
new file mode 100644
index 000000000000..7a52e3ea49ed
--- /dev/null
+++ b/drivers/char/ipmi/ssif_bmc.c
@@ -0,0 +1,877 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The driver for BMC side of SSIF interface
+ *
+ * Copyright (c) 2022, Ampere Computing LLC
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/ipmi_ssif_bmc.h>
+
+#define DEVICE_NAME "ipmi-ssif-host"
+
+#define GET_8BIT_ADDR(addr_7bit) (((addr_7bit) << 1) & 0xff)
+
+/* A standard SMBus Transaction is limited to 32 data bytes */
+#define MAX_PAYLOAD_PER_TRANSACTION 32
+/* Transaction includes the address, the command, the length and the PEC byte */
+#define MAX_TRANSACTION (MAX_PAYLOAD_PER_TRANSACTION + 4)
+
+#define MAX_IPMI_DATA_PER_START_TRANSACTION 30
+#define MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION 31
+
+#define SSIF_IPMI_SINGLEPART_WRITE 0x2
+#define SSIF_IPMI_SINGLEPART_READ 0x3
+#define SSIF_IPMI_MULTIPART_WRITE_START 0x6
+#define SSIF_IPMI_MULTIPART_WRITE_MIDDLE 0x7
+#define SSIF_IPMI_MULTIPART_WRITE_END 0x8
+#define SSIF_IPMI_MULTIPART_READ_START 0x3
+#define SSIF_IPMI_MULTIPART_READ_MIDDLE 0x9
+
+/*
+ * IPMI 2.0 Spec, section 12.7 SSIF Timing,
+ * Request-to-Response Time is T6max(250ms) - T1max(20ms) - 3ms = 227ms
+ * Recover ssif_bmc from busy state if it takes up to 500ms
+ */
+#define RESPONSE_TIMEOUT 500 /* ms */
+
+struct ssif_part_buffer {
+ u8 address;
+ u8 smbus_cmd;
+ u8 length;
+ u8 payload[MAX_PAYLOAD_PER_TRANSACTION];
+ u8 pec;
+ u8 index;
+};
+
+/*
+ * SSIF internal states:
+ * SSIF_READY 0x00 : Ready state
+ * SSIF_START 0x01 : Start smbus transaction
+ * SSIF_SMBUS_CMD 0x02 : Received SMBus command
+ * SSIF_REQ_RECVING 0x03 : Receiving request
+ * SSIF_RES_SENDING 0x04 : Sending response
+ * SSIF_ABORTING 0x05 : Aborting state
+ */
+enum ssif_state {
+ SSIF_READY,
+ SSIF_START,
+ SSIF_SMBUS_CMD,
+ SSIF_REQ_RECVING,
+ SSIF_RES_SENDING,
+ SSIF_ABORTING,
+ SSIF_STATE_MAX
+};
+
+struct ssif_bmc_ctx {
+ struct i2c_client *client;
+ struct miscdevice miscdev;
+ int msg_idx;
+ bool pec_support;
+ /* ssif bmc spinlock */
+ spinlock_t lock;
+ wait_queue_head_t wait_queue;
+ u8 running;
+ enum ssif_state state;
+ /* Timeout waiting for response */
+ struct timer_list response_timer;
+ bool response_timer_inited;
+ /* Flag to identify a Multi-part Read Transaction */
+ bool is_singlepart_read;
+ u8 nbytes_processed;
+ u8 remain_len;
+ u8 recv_len;
+ /* Block Number of a Multi-part Read Transaction */
+ u8 block_num;
+ bool request_available;
+ bool response_in_progress;
+ bool busy;
+ bool aborting;
+ /* Buffer for SSIF Transaction part*/
+ struct ssif_part_buffer part_buf;
+ struct ipmi_ssif_msg response;
+ struct ipmi_ssif_msg request;
+};
+
+static inline struct ssif_bmc_ctx *to_ssif_bmc(struct file *file)
+{
+ return container_of(file->private_data, struct ssif_bmc_ctx, miscdev);
+}
+
+static const char *state_to_string(enum ssif_state state)
+{
+ switch (state) {
+ case SSIF_READY:
+ return "SSIF_READY";
+ case SSIF_START:
+ return "SSIF_START";
+ case SSIF_SMBUS_CMD:
+ return "SSIF_SMBUS_CMD";
+ case SSIF_REQ_RECVING:
+ return "SSIF_REQ_RECVING";
+ case SSIF_RES_SENDING:
+ return "SSIF_RES_SENDING";
+ case SSIF_ABORTING:
+ return "SSIF_ABORTING";
+ default:
+ return "SSIF_STATE_UNKNOWN";
+ }
+}
+
+/* Handle SSIF message that will be sent to user */
+static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file);
+ struct ipmi_ssif_msg msg;
+ unsigned long flags;
+ ssize_t ret;
+
+ spin_lock_irqsave(&ssif_bmc->lock, flags);
+ while (!ssif_bmc->request_available) {
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ ret = wait_event_interruptible(ssif_bmc->wait_queue,
+ ssif_bmc->request_available);
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&ssif_bmc->lock, flags);
+ }
+
+ if (count < min_t(ssize_t,
+ sizeof_field(struct ipmi_ssif_msg, len) + ssif_bmc->request.len,
+ sizeof(struct ipmi_ssif_msg))) {
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+ ret = -EINVAL;
+ } else {
+ count = min_t(ssize_t,
+ sizeof_field(struct ipmi_ssif_msg, len) + ssif_bmc->request.len,
+ sizeof(struct ipmi_ssif_msg));
+ memcpy(&msg, &ssif_bmc->request, count);
+ ssif_bmc->request_available = false;
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+
+ ret = copy_to_user(buf, &msg, count);
+ }
+
+ return (ret < 0) ? ret : count;
+}
+
+/* Handle SSIF message that is written by user */
+static ssize_t ssif_bmc_write(struct file *file, const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file);
+ struct ipmi_ssif_msg msg;
+ unsigned long flags;
+ ssize_t ret;
+
+ if (count < sizeof(msg.len) ||
+ count > sizeof(struct ipmi_ssif_msg))
+ return -EINVAL;
+
+ if (copy_from_user(&msg, buf, count))
+ return -EFAULT;
+
+ if (!msg.len || msg.len > IPMI_SSIF_PAYLOAD_MAX ||
+ count < sizeof_field(struct ipmi_ssif_msg, len) + msg.len)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ssif_bmc->lock, flags);
+ while (ssif_bmc->response_in_progress) {
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ ret = wait_event_interruptible(ssif_bmc->wait_queue,
+ !ssif_bmc->response_in_progress);
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&ssif_bmc->lock, flags);
+ }
+
+ /*
+ * The write must complete before the response timeout fired, otherwise
+ * the response is aborted and wait for next request
+ * Return -EINVAL if the response is aborted
+ */
+ ret = (ssif_bmc->response_timer_inited) ? 0 : -EINVAL;
+ if (ret)
+ goto exit;
+
+ timer_delete(&ssif_bmc->response_timer);
+ ssif_bmc->response_timer_inited = false;
+
+ memcpy(&ssif_bmc->response, &msg, count);
+ ssif_bmc->is_singlepart_read = (msg.len <= MAX_PAYLOAD_PER_TRANSACTION);
+
+ ssif_bmc->response_in_progress = true;
+
+ /* ssif_bmc not busy */
+ ssif_bmc->busy = false;
+
+ /* Clean old request buffer */
+ memset(&ssif_bmc->request, 0, sizeof(struct ipmi_ssif_msg));
+exit:
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+
+ return (ret < 0) ? ret : count;
+}
+
+static int ssif_bmc_open(struct inode *inode, struct file *file)
+{
+ struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file);
+ int ret = 0;
+
+ spin_lock_irq(&ssif_bmc->lock);
+ if (!ssif_bmc->running)
+ ssif_bmc->running = 1;
+ else
+ ret = -EBUSY;
+ spin_unlock_irq(&ssif_bmc->lock);
+
+ return ret;
+}
+
+static __poll_t ssif_bmc_poll(struct file *file, poll_table *wait)
+{
+ struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file);
+ __poll_t mask = 0;
+
+ poll_wait(file, &ssif_bmc->wait_queue, wait);
+
+ spin_lock_irq(&ssif_bmc->lock);
+ /* The request is available, userspace application can get the request */
+ if (ssif_bmc->request_available)
+ mask |= EPOLLIN;
+
+ spin_unlock_irq(&ssif_bmc->lock);
+
+ return mask;
+}
+
+static int ssif_bmc_release(struct inode *inode, struct file *file)
+{
+ struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file);
+
+ spin_lock_irq(&ssif_bmc->lock);
+ ssif_bmc->running = 0;
+ spin_unlock_irq(&ssif_bmc->lock);
+
+ return 0;
+}
+
+/*
+ * System calls to device interface for user apps
+ */
+static const struct file_operations ssif_bmc_fops = {
+ .owner = THIS_MODULE,
+ .open = ssif_bmc_open,
+ .read = ssif_bmc_read,
+ .write = ssif_bmc_write,
+ .release = ssif_bmc_release,
+ .poll = ssif_bmc_poll,
+};
+
+/* Called with ssif_bmc->lock held. */
+static void complete_response(struct ssif_bmc_ctx *ssif_bmc)
+{
+ /* Invalidate response in buffer to denote it having been sent. */
+ ssif_bmc->response.len = 0;
+ ssif_bmc->response_in_progress = false;
+ ssif_bmc->nbytes_processed = 0;
+ ssif_bmc->remain_len = 0;
+ ssif_bmc->busy = false;
+ wake_up_all(&ssif_bmc->wait_queue);
+}
+
+static void response_timeout(struct timer_list *t)
+{
+ struct ssif_bmc_ctx *ssif_bmc = timer_container_of(ssif_bmc, t,
+ response_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ssif_bmc->lock, flags);
+
+ /* Do nothing if the response is in progress */
+ if (!ssif_bmc->response_in_progress) {
+ /* Recover ssif_bmc from busy */
+ ssif_bmc->busy = false;
+ ssif_bmc->response_timer_inited = false;
+ /* Set aborting flag */
+ ssif_bmc->aborting = true;
+ }
+
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+}
+
+/* Called with ssif_bmc->lock held. */
+static void handle_request(struct ssif_bmc_ctx *ssif_bmc)
+{
+ /* set ssif_bmc to busy waiting for response */
+ ssif_bmc->busy = true;
+ /* Request message is available to process */
+ ssif_bmc->request_available = true;
+ /* Clean old response buffer */
+ memset(&ssif_bmc->response, 0, sizeof(struct ipmi_ssif_msg));
+ /* This is the new READ request.*/
+ wake_up_all(&ssif_bmc->wait_queue);
+
+ /* Armed timer to recover slave from busy state in case of no response */
+ if (!ssif_bmc->response_timer_inited) {
+ timer_setup(&ssif_bmc->response_timer, response_timeout, 0);
+ ssif_bmc->response_timer_inited = true;
+ }
+ mod_timer(&ssif_bmc->response_timer, jiffies + msecs_to_jiffies(RESPONSE_TIMEOUT));
+}
+
+static void calculate_response_part_pec(struct ssif_part_buffer *part)
+{
+ u8 addr = part->address;
+
+ /* PEC - Start Read Address */
+ part->pec = i2c_smbus_pec(0, &addr, 1);
+ /* PEC - SSIF Command */
+ part->pec = i2c_smbus_pec(part->pec, &part->smbus_cmd, 1);
+ /* PEC - Restart Write Address */
+ addr = addr | 0x01;
+ part->pec = i2c_smbus_pec(part->pec, &addr, 1);
+ part->pec = i2c_smbus_pec(part->pec, &part->length, 1);
+ if (part->length)
+ part->pec = i2c_smbus_pec(part->pec, part->payload, part->length);
+}
+
+static void set_singlepart_response_buffer(struct ssif_bmc_ctx *ssif_bmc)
+{
+ struct ssif_part_buffer *part = &ssif_bmc->part_buf;
+
+ part->address = GET_8BIT_ADDR(ssif_bmc->client->addr);
+ part->length = (u8)ssif_bmc->response.len;
+
+ /* Clear the rest to 0 */
+ memset(part->payload + part->length, 0, MAX_PAYLOAD_PER_TRANSACTION - part->length);
+ memcpy(&part->payload[0], &ssif_bmc->response.payload[0], part->length);
+}
+
+static void set_multipart_response_buffer(struct ssif_bmc_ctx *ssif_bmc)
+{
+ struct ssif_part_buffer *part = &ssif_bmc->part_buf;
+ u8 part_len = 0;
+
+ part->address = GET_8BIT_ADDR(ssif_bmc->client->addr);
+ switch (part->smbus_cmd) {
+ case SSIF_IPMI_MULTIPART_READ_START:
+ /*
+ * Read Start length is 32 bytes.
+ * Read Start transfer first 30 bytes of IPMI response
+ * and 2 special code 0x00, 0x01.
+ */
+ ssif_bmc->nbytes_processed = 0;
+ ssif_bmc->block_num = 0;
+ part->length = MAX_PAYLOAD_PER_TRANSACTION;
+ part_len = MAX_IPMI_DATA_PER_START_TRANSACTION;
+ ssif_bmc->remain_len = ssif_bmc->response.len - part_len;
+
+ part->payload[0] = 0x00; /* Start Flag */
+ part->payload[1] = 0x01; /* Start Flag */
+
+ memcpy(&part->payload[2], &ssif_bmc->response.payload[0], part_len);
+ break;
+
+ case SSIF_IPMI_MULTIPART_READ_MIDDLE:
+ /*
+ * IPMI READ Middle or READ End messages can carry up to 31 bytes
+ * IPMI data plus block number byte.
+ */
+ if (ssif_bmc->remain_len <= MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION) {
+ /*
+ * This is READ End message
+ * Return length is the remaining response data length
+ * plus block number
+ * Block number 0xFF is to indicate this is last message
+ *
+ */
+ /* Clean the buffer */
+ memset(&part->payload[0], 0, MAX_PAYLOAD_PER_TRANSACTION);
+ part->length = ssif_bmc->remain_len + 1;
+ part_len = ssif_bmc->remain_len;
+ ssif_bmc->block_num = 0xFF;
+ part->payload[0] = ssif_bmc->block_num;
+ } else {
+ /*
+ * This is READ Middle message
+ * Response length is the maximum SMBUS transfer length
+ * Block number byte is incremented
+ * Return length is maximum SMBUS transfer length
+ */
+ part->length = MAX_PAYLOAD_PER_TRANSACTION;
+ part_len = MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION;
+ part->payload[0] = ssif_bmc->block_num;
+ ssif_bmc->block_num++;
+ }
+
+ ssif_bmc->remain_len -= part_len;
+ memcpy(&part->payload[1], ssif_bmc->response.payload + ssif_bmc->nbytes_processed,
+ part_len);
+ break;
+
+ default:
+ /* Do not expect to go to this case */
+ dev_err(&ssif_bmc->client->dev, "%s: Unexpected SMBus command 0x%x\n",
+ __func__, part->smbus_cmd);
+ break;
+ }
+
+ ssif_bmc->nbytes_processed += part_len;
+}
+
+static bool supported_read_cmd(u8 cmd)
+{
+ if (cmd == SSIF_IPMI_SINGLEPART_READ ||
+ cmd == SSIF_IPMI_MULTIPART_READ_START ||
+ cmd == SSIF_IPMI_MULTIPART_READ_MIDDLE)
+ return true;
+
+ return false;
+}
+
+static bool supported_write_cmd(u8 cmd)
+{
+ if (cmd == SSIF_IPMI_SINGLEPART_WRITE ||
+ cmd == SSIF_IPMI_MULTIPART_WRITE_START ||
+ cmd == SSIF_IPMI_MULTIPART_WRITE_MIDDLE ||
+ cmd == SSIF_IPMI_MULTIPART_WRITE_END)
+ return true;
+
+ return false;
+}
+
+/* Process the IPMI response that will be read by master */
+static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ struct ssif_part_buffer *part = &ssif_bmc->part_buf;
+
+ /* msg_idx start from 0 */
+ if (part->index < part->length)
+ *val = part->payload[part->index];
+ else if (part->index == part->length && ssif_bmc->pec_support)
+ *val = part->pec;
+ else
+ *val = 0;
+
+ part->index++;
+}
+
+static void handle_write_received(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ /*
+ * The msg_idx must be 1 when first enter SSIF_REQ_RECVING state
+ * And it would never exceeded 36 bytes included the 32 bytes max payload +
+ * the address + the command + the len and the PEC.
+ */
+ if (ssif_bmc->msg_idx < 1 || ssif_bmc->msg_idx > MAX_TRANSACTION)
+ return;
+
+ if (ssif_bmc->msg_idx == 1) {
+ ssif_bmc->part_buf.length = *val;
+ ssif_bmc->part_buf.index = 0;
+ } else {
+ ssif_bmc->part_buf.payload[ssif_bmc->part_buf.index++] = *val;
+ }
+
+ ssif_bmc->msg_idx++;
+}
+
+static bool validate_request_part(struct ssif_bmc_ctx *ssif_bmc)
+{
+ struct ssif_part_buffer *part = &ssif_bmc->part_buf;
+ bool ret = true;
+ u8 cpec;
+ u8 addr;
+
+ if (part->index == part->length) {
+ /* PEC is not included */
+ ssif_bmc->pec_support = false;
+ ret = true;
+ goto exit;
+ }
+
+ if (part->index != part->length + 1) {
+ ret = false;
+ goto exit;
+ }
+
+ /* PEC is included */
+ ssif_bmc->pec_support = true;
+ part->pec = part->payload[part->length];
+ addr = GET_8BIT_ADDR(ssif_bmc->client->addr);
+ cpec = i2c_smbus_pec(0, &addr, 1);
+ cpec = i2c_smbus_pec(cpec, &part->smbus_cmd, 1);
+ cpec = i2c_smbus_pec(cpec, &part->length, 1);
+ /*
+ * As SMBus specification does not allow the length
+ * (byte count) in the Write-Block protocol to be zero.
+ * Therefore, it is illegal to have the last Middle
+ * transaction in the sequence carry 32-byte and have
+ * a length of ‘0’ in the End transaction.
+ * But some users may try to use this way and we should
+ * prevent ssif_bmc driver broken in this case.
+ */
+ if (part->length)
+ cpec = i2c_smbus_pec(cpec, part->payload, part->length);
+
+ if (cpec != part->pec)
+ ret = false;
+
+exit:
+ return ret;
+}
+
+static void process_request_part(struct ssif_bmc_ctx *ssif_bmc)
+{
+ struct ssif_part_buffer *part = &ssif_bmc->part_buf;
+ unsigned int len;
+
+ switch (part->smbus_cmd) {
+ case SSIF_IPMI_SINGLEPART_WRITE:
+ /* save the whole part to request*/
+ ssif_bmc->request.len = part->length;
+ memcpy(ssif_bmc->request.payload, part->payload, part->length);
+
+ break;
+ case SSIF_IPMI_MULTIPART_WRITE_START:
+ ssif_bmc->request.len = 0;
+
+ fallthrough;
+ case SSIF_IPMI_MULTIPART_WRITE_MIDDLE:
+ case SSIF_IPMI_MULTIPART_WRITE_END:
+ len = ssif_bmc->request.len + part->length;
+ /* Do the bound check here, not allow the request len exceed 254 bytes */
+ if (len > IPMI_SSIF_PAYLOAD_MAX) {
+ dev_warn(&ssif_bmc->client->dev,
+ "Warn: Request exceeded 254 bytes, aborting");
+ /* Request too long, aborting */
+ ssif_bmc->aborting = true;
+ } else {
+ memcpy(ssif_bmc->request.payload + ssif_bmc->request.len,
+ part->payload, part->length);
+ ssif_bmc->request.len += part->length;
+ }
+ break;
+ default:
+ /* Do not expect to go to this case */
+ dev_err(&ssif_bmc->client->dev, "%s: Unexpected SMBus command 0x%x\n",
+ __func__, part->smbus_cmd);
+ break;
+ }
+}
+
+static void process_smbus_cmd(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ /* SMBUS command can vary (single or multi-part) */
+ ssif_bmc->part_buf.smbus_cmd = *val;
+ ssif_bmc->msg_idx = 1;
+ memset(&ssif_bmc->part_buf.payload[0], 0, MAX_PAYLOAD_PER_TRANSACTION);
+
+ if (*val == SSIF_IPMI_SINGLEPART_WRITE || *val == SSIF_IPMI_MULTIPART_WRITE_START) {
+ /*
+ * The response maybe not come in-time, causing host SSIF driver
+ * to timeout and resend a new request. In such case check for
+ * pending response and clear it
+ */
+ if (ssif_bmc->response_in_progress)
+ complete_response(ssif_bmc);
+
+ /* This is new request, flip aborting flag if set */
+ if (ssif_bmc->aborting)
+ ssif_bmc->aborting = false;
+ }
+}
+
+static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ if (ssif_bmc->state == SSIF_READY ||
+ ssif_bmc->state == SSIF_START ||
+ ssif_bmc->state == SSIF_REQ_RECVING ||
+ ssif_bmc->state == SSIF_RES_SENDING) {
+ dev_warn(&ssif_bmc->client->dev,
+ "Warn: %s unexpected READ REQUESTED in state=%s\n",
+ __func__, state_to_string(ssif_bmc->state));
+ ssif_bmc->state = SSIF_ABORTING;
+ *val = 0;
+ return;
+
+ } else if (ssif_bmc->state == SSIF_SMBUS_CMD) {
+ if (!supported_read_cmd(ssif_bmc->part_buf.smbus_cmd)) {
+ dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x",
+ ssif_bmc->part_buf.smbus_cmd);
+ ssif_bmc->aborting = true;
+ }
+
+ if (ssif_bmc->aborting)
+ ssif_bmc->state = SSIF_ABORTING;
+ else
+ ssif_bmc->state = SSIF_RES_SENDING;
+ }
+
+ ssif_bmc->msg_idx = 0;
+
+ /* Send 0 if there is nothing to send */
+ if (!ssif_bmc->response_in_progress || ssif_bmc->state == SSIF_ABORTING) {
+ *val = 0;
+ return;
+ }
+
+ if (ssif_bmc->is_singlepart_read)
+ set_singlepart_response_buffer(ssif_bmc);
+ else
+ set_multipart_response_buffer(ssif_bmc);
+
+ calculate_response_part_pec(&ssif_bmc->part_buf);
+ ssif_bmc->part_buf.index = 0;
+ *val = ssif_bmc->part_buf.length;
+}
+
+static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ if (ssif_bmc->state == SSIF_READY ||
+ ssif_bmc->state == SSIF_START ||
+ ssif_bmc->state == SSIF_REQ_RECVING ||
+ ssif_bmc->state == SSIF_SMBUS_CMD) {
+ dev_warn(&ssif_bmc->client->dev,
+ "Warn: %s unexpected READ PROCESSED in state=%s\n",
+ __func__, state_to_string(ssif_bmc->state));
+ ssif_bmc->state = SSIF_ABORTING;
+ *val = 0;
+ return;
+ }
+
+ /* Send 0 if there is nothing to send */
+ if (!ssif_bmc->response_in_progress || ssif_bmc->state == SSIF_ABORTING) {
+ *val = 0;
+ return;
+ }
+
+ handle_read_processed(ssif_bmc, val);
+}
+
+static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_SMBUS_CMD) {
+ ssif_bmc->state = SSIF_START;
+
+ } else if (ssif_bmc->state == SSIF_START ||
+ ssif_bmc->state == SSIF_REQ_RECVING ||
+ ssif_bmc->state == SSIF_RES_SENDING) {
+ dev_warn(&ssif_bmc->client->dev,
+ "Warn: %s unexpected WRITE REQUEST in state=%s\n",
+ __func__, state_to_string(ssif_bmc->state));
+ ssif_bmc->state = SSIF_ABORTING;
+ return;
+ }
+
+ ssif_bmc->msg_idx = 0;
+ ssif_bmc->part_buf.address = *val;
+}
+
+static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ if (ssif_bmc->state == SSIF_READY ||
+ ssif_bmc->state == SSIF_RES_SENDING) {
+ dev_warn(&ssif_bmc->client->dev,
+ "Warn: %s unexpected WRITE RECEIVED in state=%s\n",
+ __func__, state_to_string(ssif_bmc->state));
+ ssif_bmc->state = SSIF_ABORTING;
+
+ } else if (ssif_bmc->state == SSIF_START) {
+ ssif_bmc->state = SSIF_SMBUS_CMD;
+
+ } else if (ssif_bmc->state == SSIF_SMBUS_CMD) {
+ if (!supported_write_cmd(ssif_bmc->part_buf.smbus_cmd)) {
+ dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x",
+ ssif_bmc->part_buf.smbus_cmd);
+ ssif_bmc->aborting = true;
+ }
+
+ if (ssif_bmc->aborting)
+ ssif_bmc->state = SSIF_ABORTING;
+ else
+ ssif_bmc->state = SSIF_REQ_RECVING;
+ }
+
+ /* This is response sending state */
+ if (ssif_bmc->state == SSIF_REQ_RECVING)
+ handle_write_received(ssif_bmc, val);
+ else if (ssif_bmc->state == SSIF_SMBUS_CMD)
+ process_smbus_cmd(ssif_bmc, val);
+}
+
+static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
+{
+ if (ssif_bmc->state == SSIF_READY ||
+ ssif_bmc->state == SSIF_START ||
+ ssif_bmc->state == SSIF_SMBUS_CMD ||
+ ssif_bmc->state == SSIF_ABORTING) {
+ dev_warn(&ssif_bmc->client->dev,
+ "Warn: %s unexpected SLAVE STOP in state=%s\n",
+ __func__, state_to_string(ssif_bmc->state));
+ ssif_bmc->state = SSIF_READY;
+
+ } else if (ssif_bmc->state == SSIF_REQ_RECVING) {
+ if (validate_request_part(ssif_bmc)) {
+ process_request_part(ssif_bmc);
+ if (ssif_bmc->part_buf.smbus_cmd == SSIF_IPMI_SINGLEPART_WRITE ||
+ ssif_bmc->part_buf.smbus_cmd == SSIF_IPMI_MULTIPART_WRITE_END)
+ handle_request(ssif_bmc);
+ ssif_bmc->state = SSIF_READY;
+ } else {
+ /*
+ * A BMC that receives an invalid request drop the data for the write
+ * transaction and any further transactions (read or write) until
+ * the next valid read or write Start transaction is received
+ */
+ dev_err(&ssif_bmc->client->dev, "Error: invalid pec\n");
+ ssif_bmc->aborting = true;
+ }
+ } else if (ssif_bmc->state == SSIF_RES_SENDING) {
+ if (ssif_bmc->is_singlepart_read || ssif_bmc->block_num == 0xFF) {
+ memset(&ssif_bmc->part_buf, 0, sizeof(struct ssif_part_buffer));
+ /* Invalidate response buffer to denote it is sent */
+ complete_response(ssif_bmc);
+ }
+ ssif_bmc->state = SSIF_READY;
+ }
+
+ /* Reset message index */
+ ssif_bmc->msg_idx = 0;
+}
+
+/*
+ * Callback function to handle I2C slave events
+ */
+static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val)
+{
+ unsigned long flags;
+ struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client);
+ int ret = 0;
+
+ spin_lock_irqsave(&ssif_bmc->lock, flags);
+
+ switch (event) {
+ case I2C_SLAVE_READ_REQUESTED:
+ on_read_requested_event(ssif_bmc, val);
+ break;
+
+ case I2C_SLAVE_WRITE_REQUESTED:
+ on_write_requested_event(ssif_bmc, val);
+ break;
+
+ case I2C_SLAVE_READ_PROCESSED:
+ on_read_processed_event(ssif_bmc, val);
+ break;
+
+ case I2C_SLAVE_WRITE_RECEIVED:
+ on_write_received_event(ssif_bmc, val);
+ break;
+
+ case I2C_SLAVE_STOP:
+ on_stop_event(ssif_bmc, val);
+ break;
+
+ default:
+ dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n");
+ break;
+ }
+
+ if (!ssif_bmc->aborting && ssif_bmc->busy)
+ ret = -EBUSY;
+
+ spin_unlock_irqrestore(&ssif_bmc->lock, flags);
+
+ return ret;
+}
+
+static int ssif_bmc_probe(struct i2c_client *client)
+{
+ struct ssif_bmc_ctx *ssif_bmc;
+ int ret;
+
+ ssif_bmc = devm_kzalloc(&client->dev, sizeof(*ssif_bmc), GFP_KERNEL);
+ if (!ssif_bmc)
+ return -ENOMEM;
+
+ spin_lock_init(&ssif_bmc->lock);
+
+ init_waitqueue_head(&ssif_bmc->wait_queue);
+ ssif_bmc->request_available = false;
+ ssif_bmc->response_in_progress = false;
+ ssif_bmc->busy = false;
+ ssif_bmc->response_timer_inited = false;
+
+ /* Register misc device interface */
+ ssif_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
+ ssif_bmc->miscdev.name = DEVICE_NAME;
+ ssif_bmc->miscdev.fops = &ssif_bmc_fops;
+ ssif_bmc->miscdev.parent = &client->dev;
+ ret = misc_register(&ssif_bmc->miscdev);
+ if (ret)
+ return ret;
+
+ ssif_bmc->client = client;
+ ssif_bmc->client->flags |= I2C_CLIENT_SLAVE;
+
+ /* Register I2C slave */
+ i2c_set_clientdata(client, ssif_bmc);
+ ret = i2c_slave_register(client, ssif_bmc_cb);
+ if (ret)
+ misc_deregister(&ssif_bmc->miscdev);
+
+ return ret;
+}
+
+static void ssif_bmc_remove(struct i2c_client *client)
+{
+ struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client);
+
+ i2c_slave_unregister(client);
+ misc_deregister(&ssif_bmc->miscdev);
+}
+
+static const struct of_device_id ssif_bmc_match[] = {
+ { .compatible = "ssif-bmc" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ssif_bmc_match);
+
+static const struct i2c_device_id ssif_bmc_id[] = {
+ { DEVICE_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssif_bmc_id);
+
+static struct i2c_driver ssif_bmc_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = ssif_bmc_match,
+ },
+ .probe = ssif_bmc_probe,
+ .remove = ssif_bmc_remove,
+ .id_table = ssif_bmc_id,
+};
+
+module_i2c_driver(ssif_bmc_driver);
+
+MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
+MODULE_AUTHOR("Chuong Tran <chuong@os.amperecomputing.com>");
+MODULE_DESCRIPTION("Linux device driver of the BMC IPMI SSIF interface.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 38aad99ebb61..24417a00dfe9 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -145,7 +145,9 @@ static struct lp_struct lp_table[LP_NO];
static int port_num[LP_NO];
static unsigned int lp_count = 0;
-static struct class *lp_class;
+static const struct class lp_class = {
+ .name = "printer",
+};
#ifdef CONFIG_LP_CONSOLE
static struct parport *console_registered;
@@ -932,7 +934,7 @@ static int lp_register(int nr, struct parport *port)
if (reset)
lp_reset(nr);
- device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL,
+ device_create(&lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL,
"lp%d", nr);
printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
@@ -1004,7 +1006,7 @@ static void lp_detach(struct parport *port)
if (port_num[n] == port->number) {
port_num[n] = -1;
lp_count--;
- device_destroy(lp_class, MKDEV(LP_MAJOR, n));
+ device_destroy(&lp_class, MKDEV(LP_MAJOR, n));
parport_unregister_device(lp_table[n].dev);
}
}
@@ -1014,7 +1016,6 @@ static struct parport_driver lp_driver = {
.name = "lp",
.match_port = lp_attach,
.detach = lp_detach,
- .devmodel = true,
};
static int __init lp_init(void)
@@ -1049,11 +1050,9 @@ static int __init lp_init(void)
return -EIO;
}
- lp_class = class_create(THIS_MODULE, "printer");
- if (IS_ERR(lp_class)) {
- err = PTR_ERR(lp_class);
+ err = class_register(&lp_class);
+ if (err)
goto out_reg;
- }
if (parport_register_driver(&lp_driver)) {
printk(KERN_ERR "lp: unable to register with parport\n");
@@ -1072,7 +1071,7 @@ static int __init lp_init(void)
return 0;
out_class:
- class_destroy(lp_class);
+ class_unregister(&lp_class);
out_reg:
unregister_chrdev(LP_MAJOR, "lp");
return err;
@@ -1115,7 +1114,7 @@ static void lp_cleanup_module(void)
#endif
unregister_chrdev(LP_MAJOR, "lp");
- class_destroy(lp_class);
+ class_unregister(&lp_class);
}
__setup("lp=", lp_setup);
@@ -1123,4 +1122,5 @@ module_init(lp_init_module);
module_exit(lp_cleanup_module);
MODULE_ALIAS_CHARDEV_MAJOR(LP_MAJOR);
+MODULE_DESCRIPTION("Generic parallel printer driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 32a932a065a6..52039fae1594 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -31,10 +31,6 @@
#include <linux/uaccess.h>
#include <linux/security.h>
-#ifdef CONFIG_IA64
-# include <linux/efi.h>
-#endif
-
#define DEVMEM_MINOR 1
#define DEVPORT_MINOR 4
@@ -65,36 +61,11 @@ static inline int page_is_allowed(unsigned long pfn)
{
return devmem_is_allowed(pfn);
}
-static inline int range_is_allowed(unsigned long pfn, unsigned long size)
-{
- u64 from = ((u64)pfn) << PAGE_SHIFT;
- u64 to = from + size;
- u64 cursor = from;
-
- while (cursor < to) {
- if (!devmem_is_allowed(pfn))
- return 0;
- cursor += PAGE_SIZE;
- pfn++;
- }
- return 1;
-}
#else
static inline int page_is_allowed(unsigned long pfn)
{
return 1;
}
-static inline int range_is_allowed(unsigned long pfn, unsigned long size)
-{
- return 1;
-}
-#endif
-
-#ifndef unxlate_dev_mem_ptr
-#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr
-void __weak unxlate_dev_mem_ptr(phys_addr_t phys, void *addr)
-{
-}
#endif
static inline bool should_stop_iteration(void)
@@ -284,13 +255,6 @@ int __weak phys_mem_access_prot_allowed(struct file *file,
#ifdef pgprot_noncached
static int uncached_access(struct file *file, phys_addr_t addr)
{
-#if defined(CONFIG_IA64)
- /*
- * On ia64, we ignore O_DSYNC because we cannot tolerate memory
- * attribute aliases.
- */
- return !(efi_mem_attributes(addr) & EFI_MEMORY_WB);
-#else
/*
* Accessing memory above the top the kernel knows about or through a
* file pointer
@@ -299,7 +263,6 @@ static int uncached_access(struct file *file, phys_addr_t addr)
if (file->f_flags & O_DSYNC)
return 1;
return addr >= __pa(high_memory);
-#endif
}
#endif
@@ -341,13 +304,13 @@ static unsigned zero_mmap_capabilities(struct file *file)
}
/* can't do an in-place private mapping if there's no MMU */
-static inline int private_mapping_ok(struct vm_area_struct *vma)
+static inline int private_mapping_ok(struct vm_area_desc *desc)
{
- return vma->vm_flags & VM_MAYSHARE;
+ return is_nommu_shared_mapping(desc->vm_flags);
}
#else
-static inline int private_mapping_ok(struct vm_area_struct *vma)
+static inline int private_mapping_ok(struct vm_area_desc *desc)
{
return 1;
}
@@ -359,49 +322,53 @@ static const struct vm_operations_struct mmap_mem_ops = {
#endif
};
-static int mmap_mem(struct file *file, struct vm_area_struct *vma)
+static int mmap_filter_error(int err)
+{
+ return -EAGAIN;
+}
+
+static int mmap_mem_prepare(struct vm_area_desc *desc)
{
- size_t size = vma->vm_end - vma->vm_start;
- phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
+ struct file *file = desc->file;
+ const size_t size = vma_desc_size(desc);
+ const phys_addr_t offset = (phys_addr_t)desc->pgoff << PAGE_SHIFT;
/* Does it even fit in phys_addr_t? */
- if (offset >> PAGE_SHIFT != vma->vm_pgoff)
+ if (offset >> PAGE_SHIFT != desc->pgoff)
return -EINVAL;
/* It's illegal to wrap around the end of the physical address space. */
if (offset + (phys_addr_t)size - 1 < offset)
return -EINVAL;
- if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
+ if (!valid_mmap_phys_addr_range(desc->pgoff, size))
return -EINVAL;
- if (!private_mapping_ok(vma))
+ if (!private_mapping_ok(desc))
return -ENOSYS;
- if (!range_is_allowed(vma->vm_pgoff, size))
+ if (!range_is_allowed(desc->pgoff, size))
return -EPERM;
- if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
- &vma->vm_page_prot))
+ if (!phys_mem_access_prot_allowed(file, desc->pgoff, size,
+ &desc->page_prot))
return -EINVAL;
- vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
- size,
- vma->vm_page_prot);
+ desc->page_prot = phys_mem_access_prot(file, desc->pgoff,
+ size,
+ desc->page_prot);
- vma->vm_ops = &mmap_mem_ops;
+ desc->vm_ops = &mmap_mem_ops;
+
+ /* Remap-pfn-range will mark the range VM_IO. */
+ mmap_action_remap_full(desc, desc->pgoff);
+ /* We filter remap errors to -EAGAIN. */
+ desc->action.error_hook = mmap_filter_error;
- /* Remap-pfn-range will mark the range VM_IO */
- if (remap_pfn_range(vma,
- vma->vm_start,
- vma->vm_pgoff,
- size,
- vma->vm_page_prot)) {
- return -EAGAIN;
- }
return 0;
}
+#ifdef CONFIG_DEVPORT
static ssize_t read_port(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -443,6 +410,7 @@ static ssize_t write_port(struct file *file, const char __user *buf,
*ppos = i;
return tmp-buf;
}
+#endif
static ssize_t read_null(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
@@ -536,38 +504,64 @@ static ssize_t read_zero(struct file *file, char __user *buf,
return cleared;
}
-static int mmap_zero(struct file *file, struct vm_area_struct *vma)
+static int mmap_zero_private_success(const struct vm_area_struct *vma)
+{
+ /*
+ * This is a highly unique situation where we mark a MAP_PRIVATE mapping
+ * of /dev/zero anonymous, despite it not being.
+ */
+ vma_set_anonymous((struct vm_area_struct *)vma);
+
+ return 0;
+}
+
+static int mmap_zero_prepare(struct vm_area_desc *desc)
{
#ifndef CONFIG_MMU
return -ENOSYS;
#endif
- if (vma->vm_flags & VM_SHARED)
- return shmem_zero_setup(vma);
- vma_set_anonymous(vma);
+ if (desc->vm_flags & VM_SHARED)
+ return shmem_zero_setup_desc(desc);
+
+ desc->action.success_hook = mmap_zero_private_success;
return 0;
}
+#ifndef CONFIG_MMU
+static unsigned long get_unmapped_area_zero(struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
+{
+ return -ENOSYS;
+}
+#else
static unsigned long get_unmapped_area_zero(struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
-#ifdef CONFIG_MMU
if (flags & MAP_SHARED) {
/*
- * mmap_zero() will call shmem_zero_setup() to create a file,
- * so use shmem's get_unmapped_area in case it can be huge;
- * and pass NULL for file as in mmap.c's get_unmapped_area(),
- * so as not to confuse shmem with our handle on "/dev/zero".
+ * mmap_zero_prepare() will call shmem_zero_setup() to create a
+ * file, so use shmem's get_unmapped_area in case it can be
+ * huge; and pass NULL for file as in mmap.c's
+ * get_unmapped_area(), so as not to confuse shmem with our
+ * handle on "/dev/zero".
*/
return shmem_get_unmapped_area(NULL, addr, len, pgoff, flags);
}
- /* Otherwise flags & MAP_PRIVATE: with no shmem object beneath it */
- return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
+ /*
+ * Otherwise flags & MAP_PRIVATE: with no shmem object beneath it,
+ * attempt to map aligned to huge page size if possible, otherwise we
+ * fall back to system page size mappings.
+ */
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ return thp_get_unmapped_area(file, addr, len, pgoff, flags);
#else
- return -ENOSYS;
+ return mm_get_unmapped_area(file, addr, len, pgoff, flags);
#endif
}
+#endif /* CONFIG_MMU */
static ssize_t write_full(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
@@ -647,18 +641,20 @@ static int open_port(struct inode *inode, struct file *filp)
#define full_lseek null_lseek
#define write_zero write_null
#define write_iter_zero write_iter_null
+#define splice_write_zero splice_write_null
#define open_mem open_port
static const struct file_operations __maybe_unused mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
.write = write_mem,
- .mmap = mmap_mem,
+ .mmap_prepare = mmap_mem_prepare,
.open = open_mem,
#ifndef CONFIG_MMU
.get_unmapped_area = get_unmapped_area_mem,
.mmap_capabilities = memory_mmap_capabilities,
#endif
+ .fop_flags = FOP_UNSIGNED_OFFSET,
};
static const struct file_operations null_fops = {
@@ -671,12 +667,14 @@ static const struct file_operations null_fops = {
.uring_cmd = uring_cmd_null,
};
-static const struct file_operations __maybe_unused port_fops = {
+#ifdef CONFIG_DEVPORT
+static const struct file_operations port_fops = {
.llseek = memory_lseek,
.read = read_port,
.write = write_port,
.open = open_port,
};
+#endif
static const struct file_operations zero_fops = {
.llseek = zero_lseek,
@@ -684,7 +682,9 @@ static const struct file_operations zero_fops = {
.read_iter = read_iter_zero,
.read = read_zero,
.write_iter = write_iter_zero,
- .mmap = mmap_zero,
+ .splice_read = copy_splice_read,
+ .splice_write = splice_write_zero,
+ .mmap_prepare = mmap_zero_prepare,
.get_unmapped_area = get_unmapped_area_zero,
#ifndef CONFIG_MMU
.mmap_capabilities = zero_mmap_capabilities,
@@ -695,27 +695,28 @@ static const struct file_operations full_fops = {
.llseek = full_lseek,
.read_iter = read_iter_zero,
.write = write_full,
+ .splice_read = copy_splice_read,
};
static const struct memdev {
const char *name;
- umode_t mode;
const struct file_operations *fops;
fmode_t fmode;
+ umode_t mode;
} devlist[] = {
#ifdef CONFIG_DEVMEM
- [DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
+ [DEVMEM_MINOR] = { "mem", &mem_fops, 0, 0 },
#endif
- [3] = { "null", 0666, &null_fops, FMODE_NOWAIT },
+ [3] = { "null", &null_fops, FMODE_NOWAIT, 0666 },
#ifdef CONFIG_DEVPORT
- [4] = { "port", 0, &port_fops, 0 },
+ [4] = { "port", &port_fops, 0, 0 },
#endif
- [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT },
- [7] = { "full", 0666, &full_fops, 0 },
- [8] = { "random", 0666, &random_fops, 0 },
- [9] = { "urandom", 0666, &urandom_fops, 0 },
+ [5] = { "zero", &zero_fops, FMODE_NOWAIT, 0666 },
+ [7] = { "full", &full_fops, 0, 0666 },
+ [8] = { "random", &random_fops, FMODE_NOWAIT, 0666 },
+ [9] = { "urandom", &urandom_fops, FMODE_NOWAIT, 0666 },
#ifdef CONFIG_PRINTK
- [11] = { "kmsg", 0644, &kmsg_fops, 0 },
+ [11] = { "kmsg", &kmsg_fops, 0, 0644 },
#endif
};
@@ -746,27 +747,30 @@ static const struct file_operations memory_fops = {
.llseek = noop_llseek,
};
-static char *mem_devnode(struct device *dev, umode_t *mode)
+static char *mem_devnode(const struct device *dev, umode_t *mode)
{
if (mode && devlist[MINOR(dev->devt)].mode)
*mode = devlist[MINOR(dev->devt)].mode;
return NULL;
}
-static struct class *mem_class;
+static const struct class mem_class = {
+ .name = "mem",
+ .devnode = mem_devnode,
+};
static int __init chr_dev_init(void)
{
+ int retval;
int minor;
if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
- mem_class = class_create(THIS_MODULE, "mem");
- if (IS_ERR(mem_class))
- return PTR_ERR(mem_class);
+ retval = class_register(&mem_class);
+ if (retval)
+ return retval;
- mem_class->devnode = mem_devnode;
for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
if (!devlist[minor].name)
continue;
@@ -777,7 +781,7 @@ static int __init chr_dev_init(void)
if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
continue;
- device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+ device_create(&mem_class, NULL, MKDEV(MEM_MAJOR, minor),
NULL, devlist[minor].name);
}
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index cba19bfdc44d..726516fb0a3b 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -58,10 +58,28 @@ static LIST_HEAD(misc_list);
static DEFINE_MUTEX(misc_mtx);
/*
- * Assigned numbers, used for dynamic minors
+ * Assigned numbers.
*/
-#define DYNAMIC_MINORS 128 /* like dynamic majors */
-static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
+static DEFINE_IDA(misc_minors_ida);
+
+static int misc_minor_alloc(int minor)
+{
+ int ret = 0;
+
+ if (minor == MISC_DYNAMIC_MINOR) {
+ /* allocate free id */
+ ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
+ MINORMASK, GFP_KERNEL);
+ } else {
+ ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
+ }
+ return ret;
+}
+
+static void misc_minor_free(int minor)
+{
+ ida_free(&misc_minors_ida, minor);
+}
#ifdef CONFIG_PROC_FS
static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
@@ -114,7 +132,8 @@ static int misc_open(struct inode *inode, struct file *file)
break;
}
- if (!new_fops) {
+ /* Only request module for fixed minor code */
+ if (!new_fops && minor < MISC_DYNAMIC_MINOR) {
mutex_unlock(&misc_mtx);
request_module("char-major-%d-%d", MISC_MAJOR, minor);
mutex_lock(&misc_mtx);
@@ -126,10 +145,11 @@ static int misc_open(struct inode *inode, struct file *file)
new_fops = fops_get(iter->fops);
break;
}
- if (!new_fops)
- goto fail;
}
+ if (!new_fops)
+ goto fail;
+
/*
* Place the miscdevice in the file's
* private_data so it can be used by the
@@ -146,7 +166,21 @@ fail:
return err;
}
-static struct class *misc_class;
+static char *misc_devnode(const struct device *dev, umode_t *mode)
+{
+ const struct miscdevice *c = dev_get_drvdata(dev);
+
+ if (mode && c->mode)
+ *mode = c->mode;
+ if (c->nodename)
+ return kstrdup(c->nodename, GFP_KERNEL);
+ return NULL;
+}
+
+static const struct class misc_class = {
+ .name = "misc",
+ .devnode = misc_devnode,
+};
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
@@ -178,21 +212,27 @@ int misc_register(struct miscdevice *misc)
int err = 0;
bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
+ if (misc->minor > MISC_DYNAMIC_MINOR) {
+ pr_err("Invalid fixed minor %d for miscdevice '%s'\n",
+ misc->minor, misc->name);
+ return -EINVAL;
+ }
+
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
if (is_dynamic) {
- int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
+ int i = misc_minor_alloc(misc->minor);
- if (i >= DYNAMIC_MINORS) {
+ if (i < 0) {
err = -EBUSY;
goto out;
}
- misc->minor = DYNAMIC_MINORS - i - 1;
- set_bit(i, misc_minors);
+ misc->minor = i;
} else {
struct miscdevice *c;
+ int i;
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
@@ -200,19 +240,22 @@ int misc_register(struct miscdevice *misc)
goto out;
}
}
+
+ i = misc_minor_alloc(misc->minor);
+ if (i < 0) {
+ err = -EBUSY;
+ goto out;
+ }
}
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device =
- device_create_with_groups(misc_class, misc->parent, dev,
+ device_create_with_groups(&misc_class, misc->parent, dev,
misc, misc->groups, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
+ misc_minor_free(misc->minor);
if (is_dynamic) {
- int i = DYNAMIC_MINORS - misc->minor - 1;
-
- if (i < DYNAMIC_MINORS && i >= 0)
- clear_bit(i, misc_minors);
misc->minor = MISC_DYNAMIC_MINOR;
}
err = PTR_ERR(misc->this_device);
@@ -240,53 +283,36 @@ EXPORT_SYMBOL(misc_register);
void misc_deregister(struct miscdevice *misc)
{
- int i = DYNAMIC_MINORS - misc->minor - 1;
-
- if (WARN_ON(list_empty(&misc->list)))
- return;
-
mutex_lock(&misc_mtx);
- list_del(&misc->list);
- device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
- if (i < DYNAMIC_MINORS && i >= 0)
- clear_bit(i, misc_minors);
+ list_del_init(&misc->list);
+ device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor));
+ misc_minor_free(misc->minor);
+ if (misc->minor > MISC_DYNAMIC_MINOR)
+ misc->minor = MISC_DYNAMIC_MINOR;
mutex_unlock(&misc_mtx);
}
EXPORT_SYMBOL(misc_deregister);
-static char *misc_devnode(struct device *dev, umode_t *mode)
-{
- struct miscdevice *c = dev_get_drvdata(dev);
-
- if (mode && c->mode)
- *mode = c->mode;
- if (c->nodename)
- return kstrdup(c->nodename, GFP_KERNEL);
- return NULL;
-}
-
static int __init misc_init(void)
{
int err;
- struct proc_dir_entry *ret;
+ struct proc_dir_entry *misc_proc_file;
- ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
- misc_class = class_create(THIS_MODULE, "misc");
- err = PTR_ERR(misc_class);
- if (IS_ERR(misc_class))
+ misc_proc_file = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
+ err = class_register(&misc_class);
+ if (err)
goto fail_remove;
- err = -EIO;
- if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
+ err = __register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops);
+ if (err < 0)
goto fail_printk;
- misc_class->devnode = misc_devnode;
return 0;
fail_printk:
pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
- class_destroy(misc_class);
+ class_unregister(&misc_class);
fail_remove:
- if (ret)
+ if (misc_proc_file)
remove_proc_entry("misc", NULL);
return err;
}
diff --git a/drivers/char/misc_minor_kunit.c b/drivers/char/misc_minor_kunit.c
new file mode 100644
index 000000000000..6fc8b05169c5
--- /dev/null
+++ b/drivers/char/misc_minor_kunit.c
@@ -0,0 +1,689 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/init_syscalls.h>
+
+/* static minor (LCD_MINOR) */
+static struct miscdevice dev_static_minor = {
+ .minor = LCD_MINOR,
+ .name = "dev_static_minor",
+};
+
+/* misc dynamic minor */
+static struct miscdevice dev_misc_dynamic_minor = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "dev_misc_dynamic_minor",
+};
+
+static void kunit_static_minor(struct kunit *test)
+{
+ int ret;
+
+ ret = misc_register(&dev_static_minor);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor);
+ misc_deregister(&dev_static_minor);
+}
+
+static void kunit_misc_dynamic_minor(struct kunit *test)
+{
+ int ret;
+
+ ret = misc_register(&dev_misc_dynamic_minor);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ misc_deregister(&dev_misc_dynamic_minor);
+}
+
+struct miscdev_test_case {
+ const char *str;
+ int minor;
+};
+
+static struct miscdev_test_case miscdev_test_ranges[] = {
+ {
+ .str = "lower static range, top",
+ .minor = 15,
+ },
+ {
+ .str = "upper static range, bottom",
+ .minor = 130,
+ },
+ {
+ .str = "lower static range, bottom",
+ .minor = 0,
+ },
+ {
+ .str = "upper static range, top",
+ .minor = MISC_DYNAMIC_MINOR - 1,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str);
+
+static int miscdev_find_minors(struct kunit_suite *suite)
+{
+ int ret;
+ struct miscdevice miscstat = {
+ .name = "miscstat",
+ };
+ int i;
+
+ for (i = 15; i >= 0; i--) {
+ miscstat.minor = i;
+ ret = misc_register(&miscstat);
+ if (ret == 0)
+ break;
+ }
+
+ if (ret == 0) {
+ kunit_info(suite, "found misc device minor %d available\n",
+ miscstat.minor);
+ miscdev_test_ranges[0].minor = miscstat.minor;
+ misc_deregister(&miscstat);
+ } else {
+ return ret;
+ }
+
+ for (i = 128; i < MISC_DYNAMIC_MINOR; i++) {
+ miscstat.minor = i;
+ ret = misc_register(&miscstat);
+ if (ret == 0)
+ break;
+ }
+
+ if (ret == 0) {
+ kunit_info(suite, "found misc device minor %d available\n",
+ miscstat.minor);
+ miscdev_test_ranges[1].minor = miscstat.minor;
+ misc_deregister(&miscstat);
+ } else {
+ return ret;
+ }
+
+ for (i = 0; i < miscdev_test_ranges[0].minor; i++) {
+ miscstat.minor = i;
+ ret = misc_register(&miscstat);
+ if (ret == 0)
+ break;
+ }
+
+ if (ret == 0) {
+ kunit_info(suite, "found misc device minor %d available\n",
+ miscstat.minor);
+ miscdev_test_ranges[2].minor = miscstat.minor;
+ misc_deregister(&miscstat);
+ } else {
+ return ret;
+ }
+
+ for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) {
+ miscstat.minor = i;
+ ret = misc_register(&miscstat);
+ if (ret == 0)
+ break;
+ }
+
+ if (ret == 0) {
+ kunit_info(suite, "found misc device minor %d available\n",
+ miscstat.minor);
+ miscdev_test_ranges[3].minor = miscstat.minor;
+ misc_deregister(&miscstat);
+ }
+
+ return ret;
+}
+
+static bool is_valid_dynamic_minor(int minor)
+{
+ if (minor < 0)
+ return false;
+ return minor > MISC_DYNAMIC_MINOR;
+}
+
+static int miscdev_test_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations miscdev_test_fops = {
+ .open = miscdev_test_open,
+};
+
+static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc)
+{
+ int ret;
+ struct file *filp;
+ char *devname;
+
+ devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name);
+ ret = init_mknod(devname, S_IFCHR | 0600,
+ new_encode_dev(MKDEV(MISC_MAJOR, misc->minor)));
+ if (ret != 0)
+ KUNIT_FAIL(test, "failed to create node\n");
+
+ filp = filp_open(devname, O_RDONLY, 0);
+ if (IS_ERR_OR_NULL(filp))
+ KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp));
+ else
+ fput(filp);
+
+ init_unlink(devname);
+ kfree(devname);
+}
+
+static void __init miscdev_test_static_basic(struct kunit *test)
+{
+ struct miscdevice misc_test = {
+ .name = "misc_test",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+ const struct miscdev_test_case *params = test->param_value;
+
+ misc_test.minor = params->minor;
+
+ ret = misc_register(&misc_test);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
+
+ if (ret == 0) {
+ miscdev_test_can_open(test, &misc_test);
+ misc_deregister(&misc_test);
+ }
+}
+
+static void __init miscdev_test_dynamic_basic(struct kunit *test)
+{
+ struct miscdevice misc_test = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "misc_test",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+
+ ret = misc_register(&misc_test);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor));
+
+ if (ret == 0) {
+ miscdev_test_can_open(test, &misc_test);
+ misc_deregister(&misc_test);
+ }
+}
+
+static void miscdev_test_twice(struct kunit *test)
+{
+ struct miscdevice misc_test = {
+ .name = "misc_test",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+ const struct miscdev_test_case *params = test->param_value;
+
+ misc_test.minor = params->minor;
+
+ ret = misc_register(&misc_test);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
+ if (ret == 0)
+ misc_deregister(&misc_test);
+
+ ret = misc_register(&misc_test);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor);
+ if (ret == 0)
+ misc_deregister(&misc_test);
+}
+
+static void miscdev_test_duplicate_minor(struct kunit *test)
+{
+ struct miscdevice misc1 = {
+ .name = "misc1",
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice misc2 = {
+ .name = "misc2",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+ const struct miscdev_test_case *params = test->param_value;
+
+ misc1.minor = params->minor;
+ misc2.minor = params->minor;
+
+ ret = misc_register(&misc1);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, misc1.minor, params->minor);
+
+ ret = misc_register(&misc2);
+ KUNIT_EXPECT_EQ(test, ret, -EBUSY);
+ if (ret == 0)
+ misc_deregister(&misc2);
+
+ misc_deregister(&misc1);
+}
+
+static void miscdev_test_duplicate_name(struct kunit *test)
+{
+ struct miscdevice misc1 = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "misc1",
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice misc2 = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "misc1",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+
+ ret = misc_register(&misc1);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
+
+ ret = misc_register(&misc2);
+ KUNIT_EXPECT_EQ(test, ret, -EEXIST);
+ if (ret == 0)
+ misc_deregister(&misc2);
+
+ misc_deregister(&misc1);
+}
+
+/*
+ * Test that after a duplicate name failure, the reserved minor number is
+ * freed to be allocated next.
+ */
+static void miscdev_test_duplicate_name_leak(struct kunit *test)
+{
+ struct miscdevice misc1 = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "misc1",
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice misc2 = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "misc1",
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice misc3 = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "misc3",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+ int dyn_minor;
+
+ ret = misc_register(&misc1);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor));
+
+ /*
+ * Find out what is the next minor number available.
+ */
+ ret = misc_register(&misc3);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
+ dyn_minor = misc3.minor;
+ misc_deregister(&misc3);
+ misc3.minor = MISC_DYNAMIC_MINOR;
+
+ ret = misc_register(&misc2);
+ KUNIT_EXPECT_EQ(test, ret, -EEXIST);
+ if (ret == 0)
+ misc_deregister(&misc2);
+
+ /*
+ * Now check that we can still get the same minor we found before.
+ */
+ ret = misc_register(&misc3);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor));
+ KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor);
+ misc_deregister(&misc3);
+
+ misc_deregister(&misc1);
+}
+
+/*
+ * Try to register a static minor with a duplicate name. That might not
+ * deallocate the minor, preventing it from being used again.
+ */
+static void miscdev_test_duplicate_error(struct kunit *test)
+{
+ struct miscdevice miscdyn = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "name1",
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice miscstat = {
+ .name = "name1",
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice miscnew = {
+ .name = "name2",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+ const struct miscdev_test_case *params = test->param_value;
+
+ miscstat.minor = params->minor;
+ miscnew.minor = params->minor;
+
+ ret = misc_register(&miscdyn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
+
+ ret = misc_register(&miscstat);
+ KUNIT_EXPECT_EQ(test, ret, -EEXIST);
+ if (ret == 0)
+ misc_deregister(&miscstat);
+
+ ret = misc_register(&miscnew);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor);
+ if (ret == 0)
+ misc_deregister(&miscnew);
+
+ misc_deregister(&miscdyn);
+}
+
+static void __init miscdev_test_dynamic_only_range(struct kunit *test)
+{
+ int ret;
+ struct miscdevice *miscdev;
+ const int dynamic_minors = 256;
+ int i;
+
+ miscdev = kunit_kmalloc_array(test, dynamic_minors,
+ sizeof(struct miscdevice),
+ GFP_KERNEL | __GFP_ZERO);
+
+ for (i = 0; i < dynamic_minors; i++) {
+ miscdev[i].minor = MISC_DYNAMIC_MINOR;
+ miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
+ miscdev[i].fops = &miscdev_test_fops;
+ ret = misc_register(&miscdev[i]);
+ if (ret != 0)
+ break;
+ /*
+ * This is the bug we are looking for!
+ * We asked for a dynamic minor and got a minor in the static range space.
+ */
+ if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) {
+ KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor);
+ i++;
+ break;
+ }
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
+ }
+
+ for (i--; i >= 0; i--) {
+ miscdev_test_can_open(test, &miscdev[i]);
+ misc_deregister(&miscdev[i]);
+ kfree_const(miscdev[i].name);
+ }
+
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static void __init miscdev_test_collision(struct kunit *test)
+{
+ int ret;
+ struct miscdevice *miscdev;
+ struct miscdevice miscstat = {
+ .name = "miscstat",
+ .fops = &miscdev_test_fops,
+ };
+ const int dynamic_minors = 256;
+ int i;
+
+ miscdev = kunit_kmalloc_array(test, dynamic_minors,
+ sizeof(struct miscdevice),
+ GFP_KERNEL | __GFP_ZERO);
+
+ miscstat.minor = miscdev_test_ranges[0].minor;
+ ret = misc_register(&miscstat);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
+
+ for (i = 0; i < dynamic_minors; i++) {
+ miscdev[i].minor = MISC_DYNAMIC_MINOR;
+ miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
+ miscdev[i].fops = &miscdev_test_fops;
+ ret = misc_register(&miscdev[i]);
+ if (ret != 0)
+ break;
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
+ }
+
+ for (i--; i >= 0; i--) {
+ miscdev_test_can_open(test, &miscdev[i]);
+ misc_deregister(&miscdev[i]);
+ kfree_const(miscdev[i].name);
+ }
+
+ misc_deregister(&miscstat);
+
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static void __init miscdev_test_collision_reverse(struct kunit *test)
+{
+ int ret;
+ struct miscdevice *miscdev;
+ struct miscdevice miscstat = {
+ .name = "miscstat",
+ .fops = &miscdev_test_fops,
+ };
+ const int dynamic_minors = 256;
+ int i;
+
+ miscdev = kunit_kmalloc_array(test, dynamic_minors,
+ sizeof(struct miscdevice),
+ GFP_KERNEL | __GFP_ZERO);
+
+ for (i = 0; i < dynamic_minors; i++) {
+ miscdev[i].minor = MISC_DYNAMIC_MINOR;
+ miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i);
+ miscdev[i].fops = &miscdev_test_fops;
+ ret = misc_register(&miscdev[i]);
+ if (ret != 0)
+ break;
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor));
+ }
+
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ miscstat.minor = miscdev_test_ranges[0].minor;
+ ret = misc_register(&miscstat);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor);
+ if (ret == 0)
+ misc_deregister(&miscstat);
+
+ for (i--; i >= 0; i--) {
+ miscdev_test_can_open(test, &miscdev[i]);
+ misc_deregister(&miscdev[i]);
+ kfree_const(miscdev[i].name);
+ }
+}
+
+static void __init miscdev_test_conflict(struct kunit *test)
+{
+ int ret;
+ struct miscdevice miscdyn = {
+ .name = "miscdyn",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice miscstat = {
+ .name = "miscstat",
+ .fops = &miscdev_test_fops,
+ };
+
+ ret = misc_register(&miscdyn);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
+
+ /*
+ * Try to register a static minor with the same minor as the
+ * dynamic one.
+ */
+ miscstat.minor = miscdyn.minor;
+ ret = misc_register(&miscstat);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+ if (ret == 0)
+ misc_deregister(&miscstat);
+
+ miscdev_test_can_open(test, &miscdyn);
+
+ misc_deregister(&miscdyn);
+}
+
+static void __init miscdev_test_conflict_reverse(struct kunit *test)
+{
+ int ret;
+ struct miscdevice miscdyn = {
+ .name = "miscdyn",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice miscstat = {
+ .name = "miscstat",
+ .fops = &miscdev_test_fops,
+ };
+
+ /*
+ * Find the first available dynamic minor to use it as a static
+ * minor later on.
+ */
+ ret = misc_register(&miscdyn);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
+ miscstat.minor = miscdyn.minor;
+ misc_deregister(&miscdyn);
+
+ ret = misc_register(&miscstat);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+ if (ret == 0)
+ misc_deregister(&miscstat);
+
+ /*
+ * Try to register a dynamic minor after registering a static minor
+ * within the dynamic range. It should work but get a different
+ * minor.
+ */
+ miscdyn.minor = MISC_DYNAMIC_MINOR;
+ ret = misc_register(&miscdyn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, miscdyn.minor, miscstat.minor);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor));
+ if (ret == 0)
+ misc_deregister(&miscdyn);
+}
+
+/* Take minor(> MISC_DYNAMIC_MINOR) as invalid when register miscdevice */
+static void miscdev_test_invalid_input(struct kunit *test)
+{
+ struct miscdevice misc_test = {
+ .minor = MISC_DYNAMIC_MINOR + 1,
+ .name = "misc_test",
+ .fops = &miscdev_test_fops,
+ };
+ int ret;
+
+ ret = misc_register(&misc_test);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+ if (ret == 0)
+ misc_deregister(&misc_test);
+}
+
+/*
+ * Verify if @miscdyn_a can still be registered successfully without
+ * reinitialization even if its minor ever owned was requested by
+ * another miscdevice such as @miscdyn_b.
+ */
+static void miscdev_test_dynamic_reentry(struct kunit *test)
+{
+ struct miscdevice miscdyn_a = {
+ .name = "miscdyn_a",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &miscdev_test_fops,
+ };
+ struct miscdevice miscdyn_b = {
+ .name = "miscdyn_b",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &miscdev_test_fops,
+ };
+ int ret, minor_a;
+
+ ret = misc_register(&miscdyn_a);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn_a.minor));
+ minor_a = miscdyn_a.minor;
+ if (ret != 0)
+ return;
+ misc_deregister(&miscdyn_a);
+
+ ret = misc_register(&miscdyn_b);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, miscdyn_b.minor, minor_a);
+ if (ret != 0)
+ return;
+
+ ret = misc_register(&miscdyn_a);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn_a.minor));
+ KUNIT_EXPECT_NE(test, miscdyn_a.minor, miscdyn_b.minor);
+ if (ret == 0)
+ misc_deregister(&miscdyn_a);
+
+ misc_deregister(&miscdyn_b);
+}
+
+static struct kunit_case test_cases[] = {
+ KUNIT_CASE(kunit_static_minor),
+ KUNIT_CASE(kunit_misc_dynamic_minor),
+ KUNIT_CASE(miscdev_test_invalid_input),
+ KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params),
+ KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params),
+ KUNIT_CASE(miscdev_test_duplicate_name),
+ KUNIT_CASE(miscdev_test_duplicate_name_leak),
+ KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params),
+ KUNIT_CASE(miscdev_test_dynamic_reentry),
+ {}
+};
+
+static struct kunit_suite test_suite = {
+ .name = "miscdev",
+ .suite_init = miscdev_find_minors,
+ .test_cases = test_cases,
+};
+kunit_test_suite(test_suite);
+
+static struct kunit_case __refdata test_init_cases[] = {
+ KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params),
+ KUNIT_CASE(miscdev_test_dynamic_basic),
+ KUNIT_CASE(miscdev_test_dynamic_only_range),
+ KUNIT_CASE(miscdev_test_collision),
+ KUNIT_CASE(miscdev_test_collision_reverse),
+ KUNIT_CASE(miscdev_test_conflict),
+ KUNIT_CASE(miscdev_test_conflict_reverse),
+ {}
+};
+
+static struct kunit_suite test_init_suite = {
+ .name = "miscdev_init",
+ .suite_init = miscdev_find_minors,
+ .test_cases = test_init_cases,
+};
+kunit_test_init_section_suite(test_init_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vimal Agrawal");
+MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>");
+MODULE_DESCRIPTION("Test module for misc character devices");
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
deleted file mode 100644
index f8231e2e84be..000000000000
--- a/drivers/char/mspec.c
+++ /dev/null
@@ -1,295 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights
- * reserved.
- */
-
-/*
- * SN Platform Special Memory (mspec) Support
- *
- * This driver exports the SN special memory (mspec) facility to user
- * processes.
- * There are two types of memory made available thru this driver:
- * uncached and cached.
- *
- * Uncached are used for memory write combining feature of the ia64
- * cpu.
- *
- * Cached are used for areas of memory that are used as cached addresses
- * on our partition and used as uncached addresses from other partitions.
- * Due to a design constraint of the SN2 Shub, you can not have processors
- * on the same FSB perform both a cached and uncached reference to the
- * same cache line. These special memory cached regions prevent the
- * kernel from ever dropping in a TLB entry and therefore prevent the
- * processor from ever speculating a cache line from this page.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/vmalloc.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/numa.h>
-#include <linux/refcount.h>
-#include <asm/page.h>
-#include <linux/atomic.h>
-#include <asm/tlbflush.h>
-#include <asm/uncached.h>
-
-
-#define CACHED_ID "Cached,"
-#define UNCACHED_ID "Uncached"
-#define REVISION "4.0"
-#define MSPEC_BASENAME "mspec"
-
-/*
- * Page types allocated by the device.
- */
-enum mspec_page_type {
- MSPEC_CACHED = 2,
- MSPEC_UNCACHED
-};
-
-/*
- * One of these structures is allocated when an mspec region is mmaped. The
- * structure is pointed to by the vma->vm_private_data field in the vma struct.
- * This structure is used to record the addresses of the mspec pages.
- * This structure is shared by all vma's that are split off from the
- * original vma when split_vma()'s are done.
- *
- * The refcnt is incremented atomically because mm->mmap_lock does not
- * protect in fork case where multiple tasks share the vma_data.
- */
-struct vma_data {
- refcount_t refcnt; /* Number of vmas sharing the data. */
- spinlock_t lock; /* Serialize access to this structure. */
- int count; /* Number of pages allocated. */
- enum mspec_page_type type; /* Type of pages allocated. */
- unsigned long vm_start; /* Original (unsplit) base. */
- unsigned long vm_end; /* Original (unsplit) end. */
- unsigned long maddr[]; /* Array of MSPEC addresses. */
-};
-
-/*
- * mspec_open
- *
- * Called when a device mapping is created by a means other than mmap
- * (via fork, munmap, etc.). Increments the reference count on the
- * underlying mspec data so it is not freed prematurely.
- */
-static void
-mspec_open(struct vm_area_struct *vma)
-{
- struct vma_data *vdata;
-
- vdata = vma->vm_private_data;
- refcount_inc(&vdata->refcnt);
-}
-
-/*
- * mspec_close
- *
- * Called when unmapping a device mapping. Frees all mspec pages
- * belonging to all the vma's sharing this vma_data structure.
- */
-static void
-mspec_close(struct vm_area_struct *vma)
-{
- struct vma_data *vdata;
- int index, last_index;
- unsigned long my_page;
-
- vdata = vma->vm_private_data;
-
- if (!refcount_dec_and_test(&vdata->refcnt))
- return;
-
- last_index = (vdata->vm_end - vdata->vm_start) >> PAGE_SHIFT;
- for (index = 0; index < last_index; index++) {
- if (vdata->maddr[index] == 0)
- continue;
- /*
- * Clear the page before sticking it back
- * into the pool.
- */
- my_page = vdata->maddr[index];
- vdata->maddr[index] = 0;
- memset((char *)my_page, 0, PAGE_SIZE);
- uncached_free_page(my_page, 1);
- }
-
- kvfree(vdata);
-}
-
-/*
- * mspec_fault
- *
- * Creates a mspec page and maps it to user space.
- */
-static vm_fault_t
-mspec_fault(struct vm_fault *vmf)
-{
- unsigned long paddr, maddr;
- unsigned long pfn;
- pgoff_t index = vmf->pgoff;
- struct vma_data *vdata = vmf->vma->vm_private_data;
-
- maddr = (volatile unsigned long) vdata->maddr[index];
- if (maddr == 0) {
- maddr = uncached_alloc_page(numa_node_id(), 1);
- if (maddr == 0)
- return VM_FAULT_OOM;
-
- spin_lock(&vdata->lock);
- if (vdata->maddr[index] == 0) {
- vdata->count++;
- vdata->maddr[index] = maddr;
- } else {
- uncached_free_page(maddr, 1);
- maddr = vdata->maddr[index];
- }
- spin_unlock(&vdata->lock);
- }
-
- paddr = maddr & ~__IA64_UNCACHED_OFFSET;
- pfn = paddr >> PAGE_SHIFT;
-
- return vmf_insert_pfn(vmf->vma, vmf->address, pfn);
-}
-
-static const struct vm_operations_struct mspec_vm_ops = {
- .open = mspec_open,
- .close = mspec_close,
- .fault = mspec_fault,
-};
-
-/*
- * mspec_mmap
- *
- * Called when mmapping the device. Initializes the vma with a fault handler
- * and private data structure necessary to allocate, track, and free the
- * underlying pages.
- */
-static int
-mspec_mmap(struct file *file, struct vm_area_struct *vma,
- enum mspec_page_type type)
-{
- struct vma_data *vdata;
- int pages, vdata_size;
-
- if (vma->vm_pgoff != 0)
- return -EINVAL;
-
- if ((vma->vm_flags & VM_SHARED) == 0)
- return -EINVAL;
-
- if ((vma->vm_flags & VM_WRITE) == 0)
- return -EPERM;
-
- pages = vma_pages(vma);
- vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
- vdata = kvzalloc(vdata_size, GFP_KERNEL);
- if (!vdata)
- return -ENOMEM;
-
- vdata->vm_start = vma->vm_start;
- vdata->vm_end = vma->vm_end;
- vdata->type = type;
- spin_lock_init(&vdata->lock);
- refcount_set(&vdata->refcnt, 1);
- vma->vm_private_data = vdata;
-
- vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
- if (vdata->type == MSPEC_UNCACHED)
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vma->vm_ops = &mspec_vm_ops;
-
- return 0;
-}
-
-static int
-cached_mmap(struct file *file, struct vm_area_struct *vma)
-{
- return mspec_mmap(file, vma, MSPEC_CACHED);
-}
-
-static int
-uncached_mmap(struct file *file, struct vm_area_struct *vma)
-{
- return mspec_mmap(file, vma, MSPEC_UNCACHED);
-}
-
-static const struct file_operations cached_fops = {
- .owner = THIS_MODULE,
- .mmap = cached_mmap,
- .llseek = noop_llseek,
-};
-
-static struct miscdevice cached_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "mspec_cached",
- .fops = &cached_fops
-};
-
-static const struct file_operations uncached_fops = {
- .owner = THIS_MODULE,
- .mmap = uncached_mmap,
- .llseek = noop_llseek,
-};
-
-static struct miscdevice uncached_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "mspec_uncached",
- .fops = &uncached_fops
-};
-
-/*
- * mspec_init
- *
- * Called at boot time to initialize the mspec facility.
- */
-static int __init
-mspec_init(void)
-{
- int ret;
-
- ret = misc_register(&cached_miscdev);
- if (ret) {
- printk(KERN_ERR "%s: failed to register device %i\n",
- CACHED_ID, ret);
- return ret;
- }
- ret = misc_register(&uncached_miscdev);
- if (ret) {
- printk(KERN_ERR "%s: failed to register device %i\n",
- UNCACHED_ID, ret);
- misc_deregister(&cached_miscdev);
- return ret;
- }
-
- printk(KERN_INFO "%s %s initialized devices: %s %s\n",
- MSPEC_BASENAME, REVISION, CACHED_ID, UNCACHED_ID);
-
- return 0;
-}
-
-static void __exit
-mspec_exit(void)
-{
- misc_deregister(&uncached_miscdev);
- misc_deregister(&cached_miscdev);
-}
-
-module_init(mspec_init);
-module_exit(mspec_exit);
-
-MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>");
-MODULE_DESCRIPTION("Driver for SGI SN special memory operations");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c
index 4a8937f80570..90f93cefb21c 100644
--- a/drivers/char/mwave/3780i.c
+++ b/drivers/char/mwave/3780i.c
@@ -46,6 +46,8 @@
* First release to the public
*/
+#define pr_fmt(fmt) "3780i: " fmt
+
#include <linux/kernel.h>
#include <linux/unistd.h>
#include <linux/delay.h>
@@ -75,18 +77,12 @@ unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO,
unsigned long flags;
unsigned short val;
- PRINTK_3(TRACE_3780I,
- "3780i::dsp3780I_ReadMsaCfg entry usDspBaseIO %x ulMsaAddr %lx\n",
- usDspBaseIO, ulMsaAddr);
-
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr);
OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16));
val = InWordDsp(DSP_MsaDataDSISHigh);
spin_unlock_irqrestore(&dsp_lock, flags);
- PRINTK_2(TRACE_3780I, "3780i::dsp3780I_ReadMsaCfg exit val %x\n", val);
-
return val;
}
@@ -95,10 +91,6 @@ void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO,
{
unsigned long flags;
- PRINTK_4(TRACE_3780I,
- "3780i::dsp3780i_WriteMsaCfg entry usDspBaseIO %x ulMsaAddr %lx usValue %x\n",
- usDspBaseIO, ulMsaAddr, usValue);
-
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr);
OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16));
@@ -112,64 +104,18 @@ static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex,
DSP_ISA_SLAVE_CONTROL rSlaveControl;
DSP_ISA_SLAVE_CONTROL rSlaveControl_Save;
-
- PRINTK_4(TRACE_3780I,
- "3780i::dsp3780i_WriteGenCfg entry usDspBaseIO %x uIndex %x ucValue %x\n",
- usDspBaseIO, uIndex, ucValue);
-
MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl);
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_WriteGenCfg rSlaveControl %x\n",
- MKBYTE(rSlaveControl));
-
rSlaveControl_Save = rSlaveControl;
rSlaveControl.ConfigMode = true;
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n",
- MKBYTE(rSlaveControl));
-
OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl));
OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex);
OutByteDsp(DSP_ConfigData, ucValue);
OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl_Save));
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_WriteGenCfg exit\n");
-
-
}
-#if 0
-unsigned char dsp3780I_ReadGenCfg(unsigned short usDspBaseIO,
- unsigned uIndex)
-{
- DSP_ISA_SLAVE_CONTROL rSlaveControl;
- DSP_ISA_SLAVE_CONTROL rSlaveControl_Save;
- unsigned char ucValue;
-
-
- PRINTK_3(TRACE_3780I,
- "3780i::dsp3780i_ReadGenCfg entry usDspBaseIO %x uIndex %x\n",
- usDspBaseIO, uIndex);
-
- MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl);
- rSlaveControl_Save = rSlaveControl;
- rSlaveControl.ConfigMode = true;
- OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl));
- OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex);
- ucValue = InByteDsp(DSP_ConfigData);
- OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl_Save));
-
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_ReadGenCfg exit ucValue %x\n", ucValue);
-
-
- return ucValue;
-}
-#endif /* 0 */
-
-int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
+int dsp3780I_EnableDSP(struct dsp_3780i_config_settings *pSettings,
unsigned short *pIrqMap,
unsigned short *pDmaMap)
{
@@ -191,25 +137,13 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
DSP_CLOCK_CONTROL_2 rClockControl2;
DSP_ISA_SLAVE_CONTROL rSlaveControl;
DSP_HBRIDGE_CONTROL rHBridgeControl;
- unsigned short ChipID = 0;
unsigned short tval;
-
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780I_EnableDSP entry pSettings->bDSPEnabled %x\n",
- pSettings->bDSPEnabled);
-
-
if (!pSettings->bDSPEnabled) {
- PRINTK_ERROR( KERN_ERR "3780i::dsp3780I_EnableDSP: Error: DSP not enabled. Aborting.\n" );
+ pr_err("%s: Error: DSP not enabled. Aborting.\n", __func__);
return -EIO;
}
-
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_EnableDSP entry pSettings->bModemEnabled %x\n",
- pSettings->bModemEnabled);
-
if (pSettings->bModemEnabled) {
rUartCfg1.Reserved = rUartCfg2.Reserved = 0;
rUartCfg1.IrqActiveLow = pSettings->bUartIrqActiveLow;
@@ -282,23 +216,10 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
rSlaveControl.ConfigMode = false;
rSlaveControl.Reserved = 0;
- PRINTK_4(TRACE_3780I,
- "3780i::dsp3780i_EnableDSP usDspBaseIO %x index %x taddr %x\n",
- usDspBaseIO, DSP_IsaSlaveControl,
- usDspBaseIO + DSP_IsaSlaveControl);
-
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_EnableDSP rSlaveContrl %x\n",
- MKWORD(rSlaveControl));
-
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_EnableDSP rSlaveControl 2 %x\n", tval);
-
-
for (i = 0; i < 11; i++)
udelay(2000);
@@ -307,10 +228,6 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780i_EnableDSP rSlaveControl 3 %x\n", tval);
-
-
/* Program our general configuration registers */
WriteGenCfg(DSP_HBridgeCfg1Index, MKBYTE(rHBridgeCfg1));
WriteGenCfg(DSP_HBridgeCfg2Index, MKBYTE(rHBridgeCfg2));
@@ -331,10 +248,6 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
rHBridgeControl.IoAutoInc = false;
rHBridgeControl.DiagnosticMode = false;
- PRINTK_3(TRACE_3780I,
- "3780i::dsp3780i_EnableDSP DSP_HBridgeControl %x rHBridgeControl %x\n",
- DSP_HBridgeControl, MKWORD(rHBridgeControl));
-
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags);
WriteMsaCfg(DSP_LBusTimeoutDisable, MKWORD(rLBusTimeoutDisable));
@@ -342,24 +255,17 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
WriteMsaCfg(DSP_ClockControl_2, MKWORD(rClockControl2));
WriteMsaCfg(DSP_ChipReset, MKWORD(rChipReset));
- ChipID = ReadMsaCfg(DSP_ChipID);
-
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780I_EnableDSP exiting bRC=true, ChipID %x\n",
- ChipID);
+ ReadMsaCfg(DSP_ChipID);
return 0;
}
-int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings)
+int dsp3780I_DisableDSP(struct dsp_3780i_config_settings *pSettings)
{
unsigned long flags;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
DSP_ISA_SLAVE_CONTROL rSlaveControl;
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n");
-
rSlaveControl.ClockControl = 0;
rSlaveControl.SoftReset = true;
rSlaveControl.ConfigMode = false;
@@ -375,29 +281,20 @@ int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings)
udelay(5);
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP exit\n");
-
return 0;
}
-int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings)
+int dsp3780I_Reset(struct dsp_3780i_config_settings *pSettings)
{
unsigned long flags;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
DSP_BOOT_DOMAIN rBootDomain;
DSP_HBRIDGE_CONTROL rHBridgeControl;
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Reset entry\n");
-
spin_lock_irqsave(&dsp_lock, flags);
/* Mask DSP to PC interrupt */
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
- PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rHBridgeControl %x\n",
- MKWORD(rHBridgeControl));
-
rHBridgeControl.EnableDspInt = false;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags);
@@ -408,9 +305,6 @@ int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings)
rBootDomain.NMI = true;
rBootDomain.Reserved = 0;
- PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rBootDomain %x\n",
- MKWORD(rBootDomain));
-
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
/* Reset all the chiplets and then reactivate them */
@@ -419,24 +313,17 @@ int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings)
WriteMsaCfg(DSP_ChipReset,
(unsigned short) (~pSettings->usChipletEnable));
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Reset exit bRC=0\n");
-
return 0;
}
-int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
+int dsp3780I_Run(struct dsp_3780i_config_settings *pSettings)
{
unsigned long flags;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
DSP_BOOT_DOMAIN rBootDomain;
DSP_HBRIDGE_CONTROL rHBridgeControl;
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run entry\n");
-
-
/* Transition the core to a running state */
rBootDomain.ResetCore = true;
rBootDomain.Halt = false;
@@ -459,15 +346,9 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
rHBridgeControl.EnableDspInt = true;
- PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n",
- MKWORD(rHBridgeControl));
-
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags);
-
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=true\n");
-
return 0;
}
@@ -479,12 +360,6 @@ int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
unsigned short __user *pusBuffer = pvBuffer;
unsigned short val;
-
- PRINTK_5(TRACE_3780I,
- "3780i::dsp3780I_ReadDStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n",
- usDspBaseIO, pusBuffer, uCount, ulDSPAddr);
-
-
/* Set the initial MSA address. No adjustments need to be made to data store addresses */
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
@@ -499,17 +374,9 @@ int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
if(put_user(val, pusBuffer++))
return -EFAULT;
- PRINTK_3(TRACE_3780I,
- "3780I::dsp3780I_ReadDStore uCount %x val %x\n",
- uCount, val);
-
PaceMsaAccess(usDspBaseIO);
}
-
- PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_ReadDStore exit bRC=true\n");
-
return 0;
}
@@ -521,12 +388,6 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
unsigned short __user *pusBuffer = pvBuffer;
unsigned short val;
-
- PRINTK_5(TRACE_3780I,
- "3780i::dsp3780I_ReadAndDStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n",
- usDspBaseIO, pusBuffer, uCount, ulDSPAddr);
-
-
/* Set the initial MSA address. No adjustments need to be made to data store addresses */
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
@@ -541,17 +402,9 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
if(put_user(val, pusBuffer++))
return -EFAULT;
- PRINTK_3(TRACE_3780I,
- "3780I::dsp3780I_ReadAndCleanDStore uCount %x val %x\n",
- uCount, val);
-
PaceMsaAccess(usDspBaseIO);
}
-
- PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_ReadAndClearDStore exit bRC=true\n");
-
return 0;
}
@@ -562,12 +415,6 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
unsigned long flags;
unsigned short __user *pusBuffer = pvBuffer;
-
- PRINTK_5(TRACE_3780I,
- "3780i::dsp3780D_WriteDStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n",
- usDspBaseIO, pusBuffer, uCount, ulDSPAddr);
-
-
/* Set the initial MSA address. No adjustments need to be made to data store addresses */
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr);
@@ -583,17 +430,9 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
OutWordDsp(DSP_MsaDataDSISHigh, val);
spin_unlock_irqrestore(&dsp_lock, flags);
- PRINTK_3(TRACE_3780I,
- "3780I::dsp3780I_WriteDStore uCount %x val %x\n",
- uCount, val);
-
PaceMsaAccess(usDspBaseIO);
}
-
- PRINTK_1(TRACE_3780I,
- "3780I::dsp3780D_WriteDStore exit bRC=true\n");
-
return 0;
}
@@ -604,10 +443,6 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
unsigned long flags;
unsigned short __user *pusBuffer = pvBuffer;
- PRINTK_5(TRACE_3780I,
- "3780i::dsp3780I_ReadIStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n",
- usDspBaseIO, pusBuffer, uCount, ulDSPAddr);
-
/*
* Set the initial MSA address. To convert from an instruction store
* address to an MSA address
@@ -631,17 +466,10 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
if(put_user(val_hi, pusBuffer++))
return -EFAULT;
- PRINTK_4(TRACE_3780I,
- "3780I::dsp3780I_ReadIStore uCount %x val_lo %x val_hi %x\n",
- uCount, val_lo, val_hi);
-
PaceMsaAccess(usDspBaseIO);
}
- PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_ReadIStore exit bRC=true\n");
-
return 0;
}
@@ -652,11 +480,6 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
unsigned long flags;
unsigned short __user *pusBuffer = pvBuffer;
- PRINTK_5(TRACE_3780I,
- "3780i::dsp3780I_WriteIStore entry usDspBaseIO %x, pusBuffer %p, uCount %x, ulDSPAddr %lx\n",
- usDspBaseIO, pusBuffer, uCount, ulDSPAddr);
-
-
/*
* Set the initial MSA address. To convert from an instruction store
* address to an MSA address
@@ -680,17 +503,9 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
OutWordDsp(DSP_MsaDataDSISHigh, val_hi);
spin_unlock_irqrestore(&dsp_lock, flags);
- PRINTK_4(TRACE_3780I,
- "3780I::dsp3780I_WriteIStore uCount %x val_lo %x val_hi %x\n",
- uCount, val_lo, val_hi);
-
PaceMsaAccess(usDspBaseIO);
-
}
- PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_WriteIStore exit bRC=true\n");
-
return 0;
}
@@ -700,12 +515,6 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
{
unsigned long flags;
DSP_HBRIDGE_CONTROL rHBridgeControl;
- unsigned short temp;
-
-
- PRINTK_3(TRACE_3780I,
- "3780i::dsp3780I_GetIPCSource entry usDspBaseIO %x pusIPCSource %p\n",
- usDspBaseIO, pusIPCSource);
/*
* Disable DSP to PC interrupts, read the interrupt register,
@@ -717,22 +526,11 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
*pusIPCSource = InWordDsp(DSP_Interrupt);
- temp = (unsigned short) ~(*pusIPCSource);
-
- PRINTK_3(TRACE_3780I,
- "3780i::dsp3780I_GetIPCSource, usIPCSource %x ~ %x\n",
- *pusIPCSource, temp);
-
OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource));
rHBridgeControl.EnableDspInt = true;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags);
-
- PRINTK_2(TRACE_3780I,
- "3780i::dsp3780I_GetIPCSource exit usIPCSource %x\n",
- *pusIPCSource);
-
return 0;
}
diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h
index 95164246afd1..53dafceb20e0 100644
--- a/drivers/char/mwave/3780i.h
+++ b/drivers/char/mwave/3780i.h
@@ -261,7 +261,7 @@ typedef struct {
* the only values maintained by the 3780i support layer are the saved UART
* registers.
*/
-typedef struct _DSP_3780I_CONFIG_SETTINGS {
+struct dsp_3780i_config_settings {
/* Location of base configuration register */
unsigned short usBaseConfigIO;
@@ -313,16 +313,16 @@ typedef struct _DSP_3780I_CONFIG_SETTINGS {
unsigned char ucSCR; /* Scratch register */
unsigned char ucDLL; /* Divisor latch, low byte */
unsigned char ucDLM; /* Divisor latch, high byte */
-} DSP_3780I_CONFIG_SETTINGS;
+};
/* 3780i support functions */
-int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
+int dsp3780I_EnableDSP(struct dsp_3780i_config_settings *pSettings,
unsigned short *pIrqMap,
unsigned short *pDmaMap);
-int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings);
-int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings);
-int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings);
+int dsp3780I_DisableDSP(struct dsp_3780i_config_settings *pSettings);
+int dsp3780I_Reset(struct dsp_3780i_config_settings *pSettings);
+int dsp3780I_Run(struct dsp_3780i_config_settings *pSettings);
int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
unsigned uCount, unsigned long ulDSPAddr);
int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
diff --git a/drivers/char/mwave/Makefile b/drivers/char/mwave/Makefile
index a24fe96e3c96..e56c1a375535 100644
--- a/drivers/char/mwave/Makefile
+++ b/drivers/char/mwave/Makefile
@@ -8,9 +8,3 @@
obj-$(CONFIG_MWAVE) += mwave.o
mwave-y := mwavedd.o smapi.o tp3780i.o 3780i.o
-
-# To have the mwave driver disable other uarts if necessary
-# ccflags-y := -DMWAVE_FUTZ_WITH_OTHER_DEVICES
-
-# To compile in lots (~20 KiB) of run-time enablable printk()s for debugging:
-ccflags-y += -DMW_TRACE
diff --git a/drivers/char/mwave/README b/drivers/char/mwave/README
index c2a58f428bc8..6224aa814c62 100644
--- a/drivers/char/mwave/README
+++ b/drivers/char/mwave/README
@@ -4,16 +4,6 @@ Module options
The mwave module takes the following options. Note that these options
are not saved by the BIOS and so do not persist after unload and reload.
- mwave_debug=value, where value is bitwise OR of trace flags:
- 0x0001 mwavedd api tracing
- 0x0002 smapi api tracing
- 0x0004 3780i tracing
- 0x0008 tp3780i tracing
-
- Tracing only occurs if the driver has been compiled with the
- MW_TRACE macro #defined (i.e. let ccflags-y := -DMW_TRACE
- in the Makefile).
-
mwave_3780i_irq=5/7/10/11/15
If the dsp irq has not been setup and stored in bios by the
thinkpad configuration utility then this parameter allows the
diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c
index 11272d605ecd..640a9cb0dd8d 100644
--- a/drivers/char/mwave/mwavedd.c
+++ b/drivers/char/mwave/mwavedd.c
@@ -46,6 +46,8 @@
* First release to the public
*/
+#define pr_fmt(fmt) "mwavedd: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
@@ -75,131 +77,62 @@ MODULE_LICENSE("GPL");
* We'll depend on users using the tpctl utility to do that for now
*/
static DEFINE_MUTEX(mwave_mutex);
-int mwave_debug = 0;
int mwave_3780i_irq = 0;
int mwave_3780i_io = 0;
int mwave_uart_irq = 0;
int mwave_uart_io = 0;
-module_param(mwave_debug, int, 0);
module_param_hw(mwave_3780i_irq, int, irq, 0);
module_param_hw(mwave_3780i_io, int, ioport, 0);
module_param_hw(mwave_uart_irq, int, irq, 0);
module_param_hw(mwave_uart_io, int, ioport, 0);
-static int mwave_open(struct inode *inode, struct file *file);
-static int mwave_close(struct inode *inode, struct file *file);
-static long mwave_ioctl(struct file *filp, unsigned int iocmd,
- unsigned long ioarg);
-
-MWAVE_DEVICE_DATA mwave_s_mdd;
-
-static int mwave_open(struct inode *inode, struct file *file)
-{
- unsigned int retval = 0;
-
- PRINTK_3(TRACE_MWAVE,
- "mwavedd::mwave_open, entry inode %p file %p\n",
- inode, file);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_open, exit return retval %x\n", retval);
-
- return retval;
-}
-
-static int mwave_close(struct inode *inode, struct file *file)
-{
- unsigned int retval = 0;
-
- PRINTK_3(TRACE_MWAVE,
- "mwavedd::mwave_close, entry inode %p file %p\n",
- inode, file);
-
- PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_close, exit retval %x\n",
- retval);
-
- return retval;
-}
+struct mwave_device_data mwave_s_mdd;
static long mwave_ioctl(struct file *file, unsigned int iocmd,
unsigned long ioarg)
{
unsigned int retval = 0;
- pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
+ struct mwave_device_data *pDrvData = &mwave_s_mdd;
void __user *arg = (void __user *)ioarg;
- PRINTK_4(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, entry file %p cmd %x arg %x\n",
- file, iocmd, (int) ioarg);
-
switch (iocmd) {
case IOCTL_MW_RESET:
- PRINTK_1(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, IOCTL_MW_RESET"
- " calling tp3780I_ResetDSP\n");
mutex_lock(&mwave_mutex);
retval = tp3780I_ResetDSP(&pDrvData->rBDData);
mutex_unlock(&mwave_mutex);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, IOCTL_MW_RESET"
- " retval %x from tp3780I_ResetDSP\n",
- retval);
break;
case IOCTL_MW_RUN:
- PRINTK_1(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, IOCTL_MW_RUN"
- " calling tp3780I_StartDSP\n");
mutex_lock(&mwave_mutex);
retval = tp3780I_StartDSP(&pDrvData->rBDData);
mutex_unlock(&mwave_mutex);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, IOCTL_MW_RUN"
- " retval %x from tp3780I_StartDSP\n",
- retval);
break;
case IOCTL_MW_DSP_ABILITIES: {
- MW_ABILITIES rAbilities;
+ struct mw_abilities rAbilities;
- PRINTK_1(TRACE_MWAVE,
- "mwavedd::mwave_ioctl,"
- " IOCTL_MW_DSP_ABILITIES calling"
- " tp3780I_QueryAbilities\n");
mutex_lock(&mwave_mutex);
retval = tp3780I_QueryAbilities(&pDrvData->rBDData,
&rAbilities);
mutex_unlock(&mwave_mutex);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES"
- " retval %x from tp3780I_QueryAbilities\n",
- retval);
if (retval == 0) {
- if( copy_to_user(arg, &rAbilities,
- sizeof(MW_ABILITIES)) )
+ if (copy_to_user(arg, &rAbilities, sizeof(rAbilities)))
return -EFAULT;
}
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES"
- " exit retval %x\n",
- retval);
}
break;
case IOCTL_MW_READ_DATA:
case IOCTL_MW_READCLEAR_DATA: {
- MW_READWRITE rReadData;
+ struct mw_readwrite rReadData;
unsigned short __user *pusBuffer = NULL;
if( copy_from_user(&rReadData, arg,
- sizeof(MW_READWRITE)) )
+ sizeof(struct mw_readwrite)) )
return -EFAULT;
pusBuffer = (unsigned short __user *) (rReadData.pBuf);
- PRINTK_4(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_READ_DATA,"
- " size %lx, ioarg %lx pusBuffer %p\n",
- rReadData.ulDataLength, ioarg, pusBuffer);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd,
@@ -211,19 +144,13 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
break;
case IOCTL_MW_READ_INST: {
- MW_READWRITE rReadData;
+ struct mw_readwrite rReadData;
unsigned short __user *pusBuffer = NULL;
- if( copy_from_user(&rReadData, arg,
- sizeof(MW_READWRITE)) )
+ if (copy_from_user(&rReadData, arg, sizeof(rReadData)))
return -EFAULT;
pusBuffer = (unsigned short __user *) (rReadData.pBuf);
- PRINTK_4(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_READ_INST,"
- " size %lx, ioarg %lx pusBuffer %p\n",
- rReadData.ulDataLength / 2, ioarg,
- pusBuffer);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd, pusBuffer,
@@ -234,19 +161,13 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
break;
case IOCTL_MW_WRITE_DATA: {
- MW_READWRITE rWriteData;
+ struct mw_readwrite rWriteData;
unsigned short __user *pusBuffer = NULL;
- if( copy_from_user(&rWriteData, arg,
- sizeof(MW_READWRITE)) )
+ if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
return -EFAULT;
pusBuffer = (unsigned short __user *) (rWriteData.pBuf);
- PRINTK_4(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_WRITE_DATA,"
- " size %lx, ioarg %lx pusBuffer %p\n",
- rWriteData.ulDataLength, ioarg,
- pusBuffer);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd, pusBuffer,
@@ -257,19 +178,13 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
break;
case IOCTL_MW_WRITE_INST: {
- MW_READWRITE rWriteData;
+ struct mw_readwrite rWriteData;
unsigned short __user *pusBuffer = NULL;
- if( copy_from_user(&rWriteData, arg,
- sizeof(MW_READWRITE)) )
+ if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
return -EFAULT;
pusBuffer = (unsigned short __user *)(rWriteData.pBuf);
- PRINTK_4(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_WRITE_INST,"
- " size %lx, ioarg %lx pusBuffer %p\n",
- rWriteData.ulDataLength, ioarg,
- pusBuffer);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData,
iocmd, pusBuffer,
@@ -283,30 +198,17 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
unsigned int ipcnum = (unsigned int) ioarg;
if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::mwave_ioctl:"
- " IOCTL_MW_REGISTER_IPC:"
- " Error: Invalid ipcnum %x\n",
- ipcnum);
+ pr_err("%s: IOCTL_MW_REGISTER_IPC: Error: Invalid ipcnum %x\n",
+ __func__, ipcnum);
return -EINVAL;
}
ipcnum = array_index_nospec(ipcnum,
ARRAY_SIZE(pDrvData->IPCs));
- PRINTK_3(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC"
- " ipcnum %x entry usIntCount %x\n",
- ipcnum,
- pDrvData->IPCs[ipcnum].usIntCount);
mutex_lock(&mwave_mutex);
pDrvData->IPCs[ipcnum].bIsHere = false;
pDrvData->IPCs[ipcnum].bIsEnabled = true;
mutex_unlock(&mwave_mutex);
-
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC"
- " ipcnum %x exit\n",
- ipcnum);
}
break;
@@ -314,28 +216,17 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
unsigned int ipcnum = (unsigned int) ioarg;
if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::mwave_ioctl:"
- " IOCTL_MW_GET_IPC: Error:"
- " Invalid ipcnum %x\n", ipcnum);
+ pr_err("%s: IOCTL_MW_GET_IPC: Error: Invalid ipcnum %x\n", __func__,
+ ipcnum);
return -EINVAL;
}
ipcnum = array_index_nospec(ipcnum,
ARRAY_SIZE(pDrvData->IPCs));
- PRINTK_3(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC"
- " ipcnum %x, usIntCount %x\n",
- ipcnum,
- pDrvData->IPCs[ipcnum].usIntCount);
-
+
mutex_lock(&mwave_mutex);
if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
DECLARE_WAITQUEUE(wait, current);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, thread for"
- " ipc %x going to sleep\n",
- ipcnum);
add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
pDrvData->IPCs[ipcnum].bIsHere = true;
set_current_state(TASK_INTERRUPTIBLE);
@@ -343,31 +234,15 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
/* the interrupt handler while we were gone */
if (pDrvData->IPCs[ipcnum].usIntCount == 1) { /* first int has occurred (race condition) */
pDrvData->IPCs[ipcnum].usIntCount = 2; /* first int has been handled */
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl"
- " IOCTL_MW_GET_IPC ipcnum %x"
- " handling first int\n",
- ipcnum);
} else { /* either 1st int has not yet occurred, or we have already handled the first int */
schedule();
if (pDrvData->IPCs[ipcnum].usIntCount == 1) {
pDrvData->IPCs[ipcnum].usIntCount = 2;
}
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl"
- " IOCTL_MW_GET_IPC ipcnum %x"
- " woke up and returning to"
- " application\n",
- ipcnum);
}
pDrvData->IPCs[ipcnum].bIsHere = false;
remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
set_current_state(TASK_RUNNING);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC,"
- " returning thread for ipc %x"
- " processing\n",
- ipcnum);
}
mutex_unlock(&mwave_mutex);
}
@@ -376,16 +251,9 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
case IOCTL_MW_UNREGISTER_IPC: {
unsigned int ipcnum = (unsigned int) ioarg;
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_ioctl IOCTL_MW_UNREGISTER_IPC"
- " ipcnum %x\n",
- ipcnum);
if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::mwave_ioctl:"
- " IOCTL_MW_UNREGISTER_IPC:"
- " Error: Invalid ipcnum %x\n",
- ipcnum);
+ pr_err("%s: IOCTL_MW_UNREGISTER_IPC: Error: Invalid ipcnum %x\n",
+ __func__, ipcnum);
return -EINVAL;
}
ipcnum = array_index_nospec(ipcnum,
@@ -405,35 +273,9 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
return -ENOTTY;
} /* switch */
- PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl, exit retval %x\n", retval);
-
return retval;
}
-
-static ssize_t mwave_read(struct file *file, char __user *buf, size_t count,
- loff_t * ppos)
-{
- PRINTK_5(TRACE_MWAVE,
- "mwavedd::mwave_read entry file %p, buf %p, count %zx ppos %p\n",
- file, buf, count, ppos);
-
- return -EINVAL;
-}
-
-
-static ssize_t mwave_write(struct file *file, const char __user *buf,
- size_t count, loff_t * ppos)
-{
- PRINTK_5(TRACE_MWAVE,
- "mwavedd::mwave_write entry file %p, buf %p,"
- " count %zx ppos %p\n",
- file, buf, count, ppos);
-
- return -EINVAL;
-}
-
-
static int register_serial_portandirq(unsigned int port, int irq)
{
struct uart_8250_port uart;
@@ -446,9 +288,7 @@ static int register_serial_portandirq(unsigned int port, int irq)
/* OK */
break;
default:
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::register_serial_portandirq:"
- " Error: Illegal port %x\n", port );
+ pr_err("%s: Error: Illegal port %x\n", __func__, port);
return -1;
} /* switch */
/* port is okay */
@@ -461,9 +301,7 @@ static int register_serial_portandirq(unsigned int port, int irq)
/* OK */
break;
default:
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::register_serial_portandirq:"
- " Error: Illegal irq %x\n", irq );
+ pr_err("%s: Error: Illegal irq %x\n", __func__, irq);
return -1;
} /* switch */
/* irq is okay */
@@ -478,56 +316,14 @@ static int register_serial_portandirq(unsigned int port, int irq)
return serial8250_register_8250_port(&uart);
}
-
static const struct file_operations mwave_fops = {
.owner = THIS_MODULE,
- .read = mwave_read,
- .write = mwave_write,
.unlocked_ioctl = mwave_ioctl,
- .open = mwave_open,
- .release = mwave_close,
.llseek = default_llseek,
};
-
static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops };
-#if 0 /* totally b0rked */
-/*
- * sysfs support <paulsch@us.ibm.com>
- */
-
-struct device mwave_device;
-
-/* Prevent code redundancy, create a macro for mwave_show_* functions. */
-#define mwave_show_function(attr_name, format_string, field) \
-static ssize_t mwave_show_##attr_name(struct device *dev, struct device_attribute *attr, char *buf) \
-{ \
- DSP_3780I_CONFIG_SETTINGS *pSettings = \
- &mwave_s_mdd.rBDData.rDspSettings; \
- return sprintf(buf, format_string, pSettings->field); \
-}
-
-/* All of our attributes are read attributes. */
-#define mwave_dev_rd_attr(attr_name, format_string, field) \
- mwave_show_function(attr_name, format_string, field) \
-static DEVICE_ATTR(attr_name, S_IRUGO, mwave_show_##attr_name, NULL)
-
-mwave_dev_rd_attr (3780i_dma, "%i\n", usDspDma);
-mwave_dev_rd_attr (3780i_irq, "%i\n", usDspIrq);
-mwave_dev_rd_attr (3780i_io, "%#.4x\n", usDspBaseIO);
-mwave_dev_rd_attr (uart_irq, "%i\n", usUartIrq);
-mwave_dev_rd_attr (uart_io, "%#.4x\n", usUartBaseIO);
-
-static struct device_attribute * const mwave_dev_attrs[] = {
- &dev_attr_3780i_dma,
- &dev_attr_3780i_irq,
- &dev_attr_3780i_io,
- &dev_attr_uart_irq,
- &dev_attr_uart_io,
-};
-#endif
-
/*
* mwave_init is called on module load
*
@@ -536,20 +332,7 @@ static struct device_attribute * const mwave_dev_attrs[] = {
*/
static void mwave_exit(void)
{
- pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
-
- PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_exit entry\n");
-
-#if 0
- for (i = 0; i < pDrvData->nr_registered_attrs; i++)
- device_remove_file(&mwave_device, mwave_dev_attrs[i]);
- pDrvData->nr_registered_attrs = 0;
-
- if (pDrvData->device_registered) {
- device_unregister(&mwave_device);
- pDrvData->device_registered = false;
- }
-#endif
+ struct mwave_device_data *pDrvData = &mwave_s_mdd;
if ( pDrvData->sLine >= 0 ) {
serial8250_unregister_port(pDrvData->sLine);
@@ -566,8 +349,6 @@ static void mwave_exit(void)
if (pDrvData->bBDInitialized) {
tp3780I_Cleanup(&pDrvData->rBDData);
}
-
- PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_exit exit\n");
}
module_exit(mwave_exit);
@@ -576,11 +357,9 @@ static int __init mwave_init(void)
{
int i;
int retval = 0;
- pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
+ struct mwave_device_data *pDrvData = &mwave_s_mdd;
- PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_init entry\n");
-
- memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA));
+ memset(&mwave_s_mdd, 0, sizeof(mwave_s_mdd));
pDrvData->bBDInitialized = false;
pDrvData->bResourcesClaimed = false;
@@ -597,60 +376,34 @@ static int __init mwave_init(void)
}
retval = tp3780I_InitializeBoardData(&pDrvData->rBDData);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_init, return from tp3780I_InitializeBoardData"
- " retval %x\n",
- retval);
if (retval) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::mwave_init: Error:"
- " Failed to initialize board data\n");
+ pr_err("%s: Error: Failed to initialize board data\n", __func__);
goto cleanup_error;
}
pDrvData->bBDInitialized = true;
retval = tp3780I_CalcResources(&pDrvData->rBDData);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_init, return from tp3780I_CalcResources"
- " retval %x\n",
- retval);
if (retval) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd:mwave_init: Error:"
- " Failed to calculate resources\n");
+ pr_err("%s: Error: Failed to calculate resources\n", __func__);
goto cleanup_error;
}
retval = tp3780I_ClaimResources(&pDrvData->rBDData);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_init, return from tp3780I_ClaimResources"
- " retval %x\n",
- retval);
if (retval) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd:mwave_init: Error:"
- " Failed to claim resources\n");
+ pr_err("%s: Error: Failed to claim resources\n", __func__);
goto cleanup_error;
}
pDrvData->bResourcesClaimed = true;
retval = tp3780I_EnableDSP(&pDrvData->rBDData);
- PRINTK_2(TRACE_MWAVE,
- "mwavedd::mwave_init, return from tp3780I_EnableDSP"
- " retval %x\n",
- retval);
if (retval) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd:mwave_init: Error:"
- " Failed to enable DSP\n");
+ pr_err("%s: Error: Failed to enable DSP\n", __func__);
goto cleanup_error;
}
pDrvData->bDSPEnabled = true;
if (misc_register(&mwave_misc_dev) < 0) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd:mwave_init: Error:"
- " Failed to register misc device\n");
+ pr_err("%s: Error: Failed to register misc device\n", __func__);
goto cleanup_error;
}
pDrvData->bMwaveDevRegistered = true;
@@ -660,40 +413,16 @@ static int __init mwave_init(void)
pDrvData->rBDData.rDspSettings.usUartIrq
);
if (pDrvData->sLine < 0) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd:mwave_init: Error:"
- " Failed to register serial driver\n");
+ pr_err("%s: Error: Failed to register serial driver\n", __func__);
goto cleanup_error;
}
/* uart is registered */
-#if 0
- /* sysfs */
- memset(&mwave_device, 0, sizeof (struct device));
- dev_set_name(&mwave_device, "mwave");
-
- if (device_register(&mwave_device))
- goto cleanup_error;
- pDrvData->device_registered = true;
- for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) {
- if(device_create_file(&mwave_device, mwave_dev_attrs[i])) {
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd:mwave_init: Error:"
- " Failed to create sysfs file %s\n",
- mwave_dev_attrs[i]->attr.name);
- goto cleanup_error;
- }
- pDrvData->nr_registered_attrs++;
- }
-#endif
-
/* SUCCESS! */
return 0;
cleanup_error:
- PRINTK_ERROR(KERN_ERR_MWAVE
- "mwavedd::mwave_init: Error:"
- " Failed to initialize\n");
+ pr_err("%s: Error: Failed to initialize\n", __func__);
mwave_exit(); /* clean up */
return -EIO;
diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h
index 21cb09c7bed7..e1da1493eec5 100644
--- a/drivers/char/mwave/mwavedd.h
+++ b/drivers/char/mwave/mwavedd.h
@@ -56,97 +56,35 @@
#include <linux/uaccess.h>
#include <linux/wait.h>
-extern int mwave_debug;
extern int mwave_3780i_irq;
extern int mwave_3780i_io;
extern int mwave_uart_irq;
extern int mwave_uart_io;
-#define PRINTK_ERROR printk
-#define KERN_ERR_MWAVE KERN_ERR "mwave: "
-
-#define TRACE_MWAVE 0x0001
-#define TRACE_SMAPI 0x0002
-#define TRACE_3780I 0x0004
-#define TRACE_TP3780I 0x0008
-
-#ifdef MW_TRACE
-#define PRINTK_1(f,s) \
- if (f & (mwave_debug)) { \
- printk(s); \
- }
-
-#define PRINTK_2(f,s,v1) \
- if (f & (mwave_debug)) { \
- printk(s,v1); \
- }
-
-#define PRINTK_3(f,s,v1,v2) \
- if (f & (mwave_debug)) { \
- printk(s,v1,v2); \
- }
-
-#define PRINTK_4(f,s,v1,v2,v3) \
- if (f & (mwave_debug)) { \
- printk(s,v1,v2,v3); \
- }
-
-#define PRINTK_5(f,s,v1,v2,v3,v4) \
- if (f & (mwave_debug)) { \
- printk(s,v1,v2,v3,v4); \
- }
-
-#define PRINTK_6(f,s,v1,v2,v3,v4,v5) \
- if (f & (mwave_debug)) { \
- printk(s,v1,v2,v3,v4,v5); \
- }
-
-#define PRINTK_7(f,s,v1,v2,v3,v4,v5,v6) \
- if (f & (mwave_debug)) { \
- printk(s,v1,v2,v3,v4,v5,v6); \
- }
-
-#define PRINTK_8(f,s,v1,v2,v3,v4,v5,v6,v7) \
- if (f & (mwave_debug)) { \
- printk(s,v1,v2,v3,v4,v5,v6,v7); \
- }
-
-#else
-#define PRINTK_1(f,s)
-#define PRINTK_2(f,s,v1)
-#define PRINTK_3(f,s,v1,v2)
-#define PRINTK_4(f,s,v1,v2,v3)
-#define PRINTK_5(f,s,v1,v2,v3,v4)
-#define PRINTK_6(f,s,v1,v2,v3,v4,v5)
-#define PRINTK_7(f,s,v1,v2,v3,v4,v5,v6)
-#define PRINTK_8(f,s,v1,v2,v3,v4,v5,v6,v7)
-#endif
-
-
-typedef struct _MWAVE_IPC {
+struct mwave_ipc {
unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */
bool bIsEnabled;
bool bIsHere;
/* entry spin lock */
wait_queue_head_t ipc_wait_queue;
-} MWAVE_IPC;
+};
-typedef struct _MWAVE_DEVICE_DATA {
- THINKPAD_BD_DATA rBDData; /* board driver's data area */
+struct mwave_device_data {
+ struct thinkpad_bd_data rBDData; /* board driver's data area */
unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */
unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */
bool bBDInitialized;
bool bResourcesClaimed;
bool bDSPEnabled;
bool bDSPReset;
- MWAVE_IPC IPCs[16];
+ struct mwave_ipc IPCs[16];
bool bMwaveDevRegistered;
short sLine;
int nr_registered_attrs;
int device_registered;
-} MWAVE_DEVICE_DATA, *pMWAVE_DEVICE_DATA;
+};
-extern MWAVE_DEVICE_DATA mwave_s_mdd;
+extern struct mwave_device_data mwave_s_mdd;
#endif
diff --git a/drivers/char/mwave/mwavepub.h b/drivers/char/mwave/mwavepub.h
index 60c961ae23b4..280327bdaa38 100644
--- a/drivers/char/mwave/mwavepub.h
+++ b/drivers/char/mwave/mwavepub.h
@@ -53,7 +53,7 @@
#include <linux/miscdevice.h>
-typedef struct _MW_ABILITIES {
+struct mw_abilities {
unsigned long instr_per_sec;
unsigned long data_size;
unsigned long inst_size;
@@ -63,27 +63,27 @@ typedef struct _MW_ABILITIES {
unsigned long component_list[7];
char mwave_os_name[16];
char bios_task_name[16];
-} MW_ABILITIES, *pMW_ABILITIES;
+};
-typedef struct _MW_READWRITE {
+struct mw_readwrite {
unsigned short usDspAddress; /* The dsp address */
unsigned long ulDataLength; /* The size in bytes of the data or user buffer */
void __user *pBuf; /* Input:variable sized buffer */
-} MW_READWRITE, *pMW_READWRITE;
+};
#define IOCTL_MW_RESET _IO(MWAVE_MINOR,1)
#define IOCTL_MW_RUN _IO(MWAVE_MINOR,2)
-#define IOCTL_MW_DSP_ABILITIES _IOR(MWAVE_MINOR,3,MW_ABILITIES)
-#define IOCTL_MW_READ_DATA _IOR(MWAVE_MINOR,4,MW_READWRITE)
-#define IOCTL_MW_READCLEAR_DATA _IOR(MWAVE_MINOR,5,MW_READWRITE)
-#define IOCTL_MW_READ_INST _IOR(MWAVE_MINOR,6,MW_READWRITE)
-#define IOCTL_MW_WRITE_DATA _IOW(MWAVE_MINOR,7,MW_READWRITE)
-#define IOCTL_MW_WRITE_INST _IOW(MWAVE_MINOR,8,MW_READWRITE)
+#define IOCTL_MW_DSP_ABILITIES _IOR(MWAVE_MINOR,3,struct mw_abilities)
+#define IOCTL_MW_READ_DATA _IOR(MWAVE_MINOR,4,struct mw_readwrite)
+#define IOCTL_MW_READCLEAR_DATA _IOR(MWAVE_MINOR,5,struct mw_readwrite)
+#define IOCTL_MW_READ_INST _IOR(MWAVE_MINOR,6,struct mw_readwrite)
+#define IOCTL_MW_WRITE_DATA _IOW(MWAVE_MINOR,7,struct mw_readwrite)
+#define IOCTL_MW_WRITE_INST _IOW(MWAVE_MINOR,8,struct mw_readwrite)
#define IOCTL_MW_REGISTER_IPC _IOW(MWAVE_MINOR,9,int)
#define IOCTL_MW_UNREGISTER_IPC _IOW(MWAVE_MINOR,10,int)
#define IOCTL_MW_GET_IPC _IOW(MWAVE_MINOR,11,int)
-#define IOCTL_MW_TRACE _IOR(MWAVE_MINOR,12,MW_READWRITE)
+#define IOCTL_MW_TRACE _IOR(MWAVE_MINOR,12,struct mw_readwrite)
#endif
diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c
index f8d79d393b69..df6354b24339 100644
--- a/drivers/char/mwave/smapi.c
+++ b/drivers/char/mwave/smapi.c
@@ -46,6 +46,8 @@
* First release to the public
*/
+#define pr_fmt(fmt) "smapi: " fmt
+
#include <linux/kernel.h>
#include <linux/mc146818rtc.h> /* CMOS defines */
#include "smapi.h"
@@ -69,10 +71,6 @@ static int smapi_request(unsigned short inBX, unsigned short inCX,
unsigned short usSmapiOK = -EIO, *pusSmapiOK = &usSmapiOK;
unsigned int inBXCX = (inBX << 16) | inCX;
unsigned int inDISI = (inDI << 16) | inSI;
- int retval = 0;
-
- PRINTK_5(TRACE_SMAPI, "inBX %x inCX %x inDI %x inSI %x\n",
- inBX, inCX, inDI, inSI);
__asm__ __volatile__("movw $0x5380,%%ax\n\t"
"movl %7,%%ebx\n\t"
@@ -107,10 +105,6 @@ static int smapi_request(unsigned short inBX, unsigned short inCX,
:"%eax", "%ebx", "%ecx", "%edx", "%edi",
"%esi");
- PRINTK_8(TRACE_SMAPI,
- "myoutAX %x myoutBX %x myoutCX %x myoutDX %x myoutDI %x myoutSI %x usSmapiOK %x\n",
- myoutAX, myoutBX, myoutCX, myoutDX, myoutDI, myoutSI,
- usSmapiOK);
*outAX = myoutAX;
*outBX = myoutBX;
*outCX = myoutCX;
@@ -118,13 +112,11 @@ static int smapi_request(unsigned short inBX, unsigned short inCX,
*outDI = myoutDI;
*outSI = myoutSI;
- retval = (usSmapiOK == 1) ? 0 : -EIO;
- PRINTK_2(TRACE_SMAPI, "smapi::smapi_request exit retval %x\n", retval);
- return retval;
+ return usSmapiOK == 1 ? 0 : -EIO;
}
-int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings)
+int smapi_query_DSP_cfg(struct smapi_dsp_settings *pSettings)
{
int bRC;
unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
@@ -134,17 +126,13 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings)
static const unsigned short ausUartBases[] = {
0x03F8, 0x02F8, 0x03E8, 0x02E8 };
- PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg entry\n");
-
bRC = smapi_request(0x1802, 0x0000, 0, 0,
&usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
if (bRC) {
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Error: Could not get DSP Settings. Aborting.\n");
+ pr_err("%s: Error: Could not get DSP Settings. Aborting.\n", __func__);
return bRC;
}
- PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg, smapi_request OK\n");
-
pSettings->bDSPPresent = ((usBX & 0x0100) != 0);
pSettings->bDSPEnabled = ((usCX & 0x0001) != 0);
pSettings->usDspIRQ = usSI & 0x00FF;
@@ -154,27 +142,20 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings)
} else {
pSettings->usDspBaseIO = 0;
}
- PRINTK_6(TRACE_SMAPI,
- "smapi::smapi_query_DSP_cfg get DSP Settings bDSPPresent %x bDSPEnabled %x usDspIRQ %x usDspDMA %x usDspBaseIO %x\n",
- pSettings->bDSPPresent, pSettings->bDSPEnabled,
- pSettings->usDspIRQ, pSettings->usDspDMA,
- pSettings->usDspBaseIO);
/* check for illegal values */
if ( pSettings->usDspBaseIO == 0 )
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: DSP base I/O address is 0\n");
+ pr_err("%s: Worry: DSP base I/O address is 0\n", __func__);
if ( pSettings->usDspIRQ == 0 )
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: DSP IRQ line is 0\n");
+ pr_err("%s: Worry: DSP IRQ line is 0\n", __func__);
bRC = smapi_request(0x1804, 0x0000, 0, 0,
&usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
if (bRC) {
- PRINTK_ERROR("smapi::smapi_query_DSP_cfg: Error: Could not get DSP modem settings. Aborting.\n");
+ pr_err("%s: Error: Could not get DSP modem settings. Aborting.\n", __func__);
return bRC;
}
- PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg, smapi_request OK\n");
-
pSettings->bModemEnabled = ((usCX & 0x0001) != 0);
pSettings->usUartIRQ = usSI & 0x000F;
if (((usSI & 0xFF00) >> 8) < ARRAY_SIZE(ausUartBases)) {
@@ -183,19 +164,11 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings)
pSettings->usUartBaseIO = 0;
}
- PRINTK_4(TRACE_SMAPI,
- "smapi::smapi_query_DSP_cfg get DSP modem settings bModemEnabled %x usUartIRQ %x usUartBaseIO %x\n",
- pSettings->bModemEnabled,
- pSettings->usUartIRQ,
- pSettings->usUartBaseIO);
-
/* check for illegal values */
if ( pSettings->usUartBaseIO == 0 )
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: UART base I/O address is 0\n");
+ pr_err("%s: Worry: UART base I/O address is 0\n", __func__);
if ( pSettings->usUartIRQ == 0 )
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_query_DSP_cfg: Worry: UART IRQ line is 0\n");
-
- PRINTK_2(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg exit bRC %x\n", bRC);
+ pr_err("%s: Worry: UART IRQ line is 0\n", __func__);
return bRC;
}
@@ -218,17 +191,14 @@ int smapi_set_DSP_cfg(void)
unsigned short dspio_index = 0, uartio_index = 0;
- PRINTK_5(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg entry mwave_3780i_irq %x mwave_3780i_io %x mwave_uart_irq %x mwave_uart_io %x\n",
- mwave_3780i_irq, mwave_3780i_io, mwave_uart_irq, mwave_uart_io);
-
if (mwave_3780i_io) {
for (i = 0; i < ARRAY_SIZE(ausDspBases); i++) {
if (mwave_3780i_io == ausDspBases[i])
break;
}
if (i == ARRAY_SIZE(ausDspBases)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_io address %x. Aborting.\n", mwave_3780i_io);
+ pr_err("%s: Error: Invalid mwave_3780i_io address %x. Aborting.\n",
+ __func__, mwave_3780i_io);
return bRC;
}
dspio_index = i;
@@ -240,7 +210,8 @@ int smapi_set_DSP_cfg(void)
break;
}
if (i == ARRAY_SIZE(ausDspIrqs)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_irq %x. Aborting.\n", mwave_3780i_irq);
+ pr_err("%s: Error: Invalid mwave_3780i_irq %x. Aborting.\n", __func__,
+ mwave_3780i_irq);
return bRC;
}
}
@@ -251,7 +222,8 @@ int smapi_set_DSP_cfg(void)
break;
}
if (i == ARRAY_SIZE(ausUartBases)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_io address %x. Aborting.\n", mwave_uart_io);
+ pr_err("%s: Error: Invalid mwave_uart_io address %x. Aborting.\n", __func__,
+ mwave_uart_io);
return bRC;
}
uartio_index = i;
@@ -264,7 +236,8 @@ int smapi_set_DSP_cfg(void)
break;
}
if (i == ARRAY_SIZE(ausUartIrqs)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_irq %x. Aborting.\n", mwave_uart_irq);
+ pr_err("%s: Error: Invalid mwave_uart_irq %x. Aborting.\n", __func__,
+ mwave_uart_irq);
return bRC;
}
}
@@ -279,46 +252,15 @@ int smapi_set_DSP_cfg(void)
if (usBX & 0x0100) { /* serial port A is present */
if (usCX & 1) { /* serial port is enabled */
if ((usSI & 0xFF) == mwave_uart_irq) {
-#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_ERROR(KERN_ERR_MWAVE
- "smapi::smapi_set_DSP_cfg: Serial port A irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq);
-#else
- PRINTK_3(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg: Serial port A irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq);
-#endif
-#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_1(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg Disabling conflicting serial port\n");
- bRC = smapi_request(0x1403, 0x0100, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1402, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-#else
+ pr_err("%s: Serial port A irq %x conflicts with mwave_uart_irq %x\n",
+ __func__, usSI & 0xFF, mwave_uart_irq);
goto exit_conflict;
-#endif
} else {
if ((usSI >> 8) == uartio_index) {
-#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_ERROR(KERN_ERR_MWAVE
- "smapi::smapi_set_DSP_cfg: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]);
-#else
- PRINTK_3(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]);
-#endif
-#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_1(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg Disabling conflicting serial port A\n");
- bRC = smapi_request (0x1403, 0x0100, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request (0x1402, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-#else
+ pr_err("%s: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n",
+ __func__, ausUartBases[usSI >> 8],
+ ausUartBases[uartio_index]);
goto exit_conflict;
-#endif
}
}
}
@@ -332,46 +274,15 @@ int smapi_set_DSP_cfg(void)
if (usBX & 0x0100) { /* serial port B is present */
if (usCX & 1) { /* serial port is enabled */
if ((usSI & 0xFF) == mwave_uart_irq) {
-#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_ERROR(KERN_ERR_MWAVE
- "smapi::smapi_set_DSP_cfg: Serial port B irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq);
-#else
- PRINTK_3(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg: Serial port B irq %x conflicts with mwave_uart_irq %x\n", usSI & 0xFF, mwave_uart_irq);
-#endif
-#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_1(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg Disabling conflicting serial port B\n");
- bRC = smapi_request(0x1405, 0x0100, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1404, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-#else
+ pr_err("%s: Serial port B irq %x conflicts with mwave_uart_irq %x\n",
+ __func__, usSI & 0xFF, mwave_uart_irq);
goto exit_conflict;
-#endif
} else {
if ((usSI >> 8) == uartio_index) {
-#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_ERROR(KERN_ERR_MWAVE
- "smapi::smapi_set_DSP_cfg: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]);
-#else
- PRINTK_3(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI >> 8], ausUartBases[uartio_index]);
-#endif
-#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_1 (TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg Disabling conflicting serial port B\n");
- bRC = smapi_request (0x1405, 0x0100, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request (0x1404, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-#else
+ pr_err("%s: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n",
+ __func__, ausUartBases[usSI >> 8],
+ ausUartBases[uartio_index]);
goto exit_conflict;
-#endif
}
}
}
@@ -387,58 +298,15 @@ int smapi_set_DSP_cfg(void)
/* bRC == 0 */
if ((usCX & 0xff) != 0xff) { /* IR port not disabled */
if ((usCX & 0xff) == mwave_uart_irq) {
-#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_ERROR(KERN_ERR_MWAVE
- "smapi::smapi_set_DSP_cfg: IR port irq %x conflicts with mwave_uart_irq %x\n", usCX & 0xff, mwave_uart_irq);
-#else
- PRINTK_3(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg: IR port irq %x conflicts with mwave_uart_irq %x\n", usCX & 0xff, mwave_uart_irq);
-#endif
-#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_1(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg Disabling conflicting IR port\n");
- bRC = smapi_request(0x1701, 0x0100, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1700, 0, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1705, 0x01ff, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1704, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-#else
+ pr_err("%s: IR port irq %x conflicts with mwave_uart_irq %x\n",
+ __func__, usCX & 0xff, mwave_uart_irq);
goto exit_conflict;
-#endif
} else {
if ((usSI & 0xff) == uartio_index) {
-#ifndef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_ERROR(KERN_ERR_MWAVE
- "smapi::smapi_set_DSP_cfg: IR port base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI & 0xff], ausUartBases[uartio_index]);
-#else
- PRINTK_3(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg: IR port base I/O address %x conflicts with mwave uart I/O %x\n", ausUartBases[usSI & 0xff], ausUartBases[uartio_index]);
-#endif
-#ifdef MWAVE_FUTZ_WITH_OTHER_DEVICES
- PRINTK_1(TRACE_SMAPI,
- "smapi::smapi_set_DSP_cfg Disabling conflicting IR port\n");
- bRC = smapi_request(0x1701, 0x0100, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1700, 0, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1705, 0x01ff, 0, usSI,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
- bRC = smapi_request(0x1704, 0x0000, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
- if (bRC) goto exit_smapi_request_error;
-#else
+ pr_err("%s: IR port base I/O address %x conflicts with mwave uart I/O %x\n",
+ __func__, ausUartBases[usSI & 0xff],
+ ausUartBases[uartio_index]);
goto exit_conflict;
-#endif
}
}
}
@@ -482,7 +350,6 @@ int smapi_set_DSP_cfg(void)
if (bRC) goto exit_smapi_request_error;
/* normal exit: */
- PRINTK_1(TRACE_SMAPI, "smapi::smapi_set_DSP_cfg exit\n");
return 0;
exit_conflict:
@@ -490,64 +357,32 @@ exit_conflict:
return -EIO;
exit_smapi_request_error:
- PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg exit on smapi_request error bRC %x\n", bRC);
+ pr_err("%s: exit on smapi_request error bRC %x\n", __func__, bRC);
return bRC;
}
int smapi_set_DSP_power_state(bool bOn)
{
- int bRC;
unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
unsigned short usPowerFunction;
- PRINTK_2(TRACE_SMAPI, "smapi::smapi_set_DSP_power_state entry bOn %x\n", bOn);
-
usPowerFunction = (bOn) ? 1 : 0;
- bRC = smapi_request(0x4901, 0x0000, 0, usPowerFunction,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
-
- PRINTK_2(TRACE_SMAPI, "smapi::smapi_set_DSP_power_state exit bRC %x\n", bRC);
-
- return bRC;
+ return smapi_request(0x4901, 0x0000, 0, usPowerFunction, &usAX, &usBX, &usCX, &usDX, &usDI,
+ &usSI);
}
-#if 0
-static int SmapiQuerySystemID(void)
-{
- int bRC = -EIO;
- unsigned short usAX = 0xffff, usBX = 0xffff, usCX = 0xffff,
- usDX = 0xffff, usDI = 0xffff, usSI = 0xffff;
-
- printk("smapi::SmapiQUerySystemID entry\n");
- bRC = smapi_request(0x0000, 0, 0, 0,
- &usAX, &usBX, &usCX, &usDX, &usDI, &usSI);
-
- if (bRC == 0) {
- printk("AX=%x, BX=%x, CX=%x, DX=%x, DI=%x, SI=%x\n",
- usAX, usBX, usCX, usDX, usDI, usSI);
- } else {
- printk("smapi::SmapiQuerySystemID smapi_request error\n");
- }
-
- return bRC;
-}
-#endif /* 0 */
-
int smapi_init(void)
{
int retval = -EIO;
unsigned short usSmapiID = 0;
unsigned long flags;
- PRINTK_1(TRACE_SMAPI, "smapi::smapi_init entry\n");
-
spin_lock_irqsave(&rtc_lock, flags);
usSmapiID = CMOS_READ(0x7C);
usSmapiID |= (CMOS_READ(0x7D) << 8);
spin_unlock_irqrestore(&rtc_lock, flags);
- PRINTK_2(TRACE_SMAPI, "smapi::smapi_init usSmapiID %x\n", usSmapiID);
if (usSmapiID == 0x5349) {
spin_lock_irqsave(&rtc_lock, flags);
@@ -555,16 +390,13 @@ int smapi_init(void)
g_usSmapiPort |= (CMOS_READ(0x7F) << 8);
spin_unlock_irqrestore(&rtc_lock, flags);
if (g_usSmapiPort == 0) {
- PRINTK_ERROR("smapi::smapi_init, ERROR unable to read from SMAPI port\n");
+ pr_err("%s: ERROR unable to read from SMAPI port\n", __func__);
} else {
- PRINTK_2(TRACE_SMAPI,
- "smapi::smapi_init, exit true g_usSmapiPort %x\n",
- g_usSmapiPort);
retval = 0;
//SmapiQuerySystemID();
}
} else {
- PRINTK_ERROR("smapi::smapi_init, ERROR invalid usSmapiID\n");
+ pr_err("%s: ERROR invalid usSmapiID\n", __func__);
retval = -ENXIO;
}
diff --git a/drivers/char/mwave/smapi.h b/drivers/char/mwave/smapi.h
index ebc206b000b9..e605b16ed23c 100644
--- a/drivers/char/mwave/smapi.h
+++ b/drivers/char/mwave/smapi.h
@@ -49,7 +49,7 @@
#ifndef _LINUX_SMAPI_H
#define _LINUX_SMAPI_H
-typedef struct {
+struct smapi_dsp_settings {
int bDSPPresent;
int bDSPEnabled;
int bModemEnabled;
@@ -65,10 +65,10 @@ typedef struct {
unsigned short usSndblstIRQ;
unsigned short usSndblstDMA;
unsigned short usSndblstBaseIO;
-} SMAPI_DSP_SETTINGS;
+};
int smapi_init(void);
-int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings);
+int smapi_query_DSP_cfg(struct smapi_dsp_settings *pSettings);
int smapi_set_DSP_cfg(void);
int smapi_set_DSP_power_state(bool bOn);
diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c
index 83eaffeb22c8..7363b0f764e0 100644
--- a/drivers/char/mwave/tp3780i.c
+++ b/drivers/char/mwave/tp3780i.c
@@ -46,6 +46,8 @@
* First release to the public
*/
+#define pr_fmt(fmt) "tp3780i: " fmt
+
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
@@ -65,16 +67,14 @@ static unsigned short s_ausThinkpadDmaToField[8] =
static unsigned short s_numIrqs = 16, s_numDmas = 8;
-static void EnableSRAM(THINKPAD_BD_DATA * pBDData)
+static void EnableSRAM(struct thinkpad_bd_data *pBDData)
{
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
DSP_GPIO_OUTPUT_DATA_15_8 rGpioOutputData;
DSP_GPIO_DRIVER_ENABLE_15_8 rGpioDriverEnable;
DSP_GPIO_MODE_15_8 rGpioMode;
- PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM, entry\n");
-
MKWORD(rGpioMode) = ReadMsaCfg(DSP_GpioModeControl_15_8);
rGpioMode.GpioMode10 = 0;
WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode));
@@ -88,54 +88,31 @@ static void EnableSRAM(THINKPAD_BD_DATA * pBDData)
rGpioOutputData.Latch10 = 0;
rGpioOutputData.Mask10 = true;
WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData));
-
- PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM exit\n");
}
static irqreturn_t UartInterrupt(int irq, void *dev_id)
{
- PRINTK_3(TRACE_TP3780I,
- "tp3780i::UartInterrupt entry irq %x dev_id %p\n", irq, dev_id);
return IRQ_HANDLED;
}
static irqreturn_t DspInterrupt(int irq, void *dev_id)
{
- pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pDrvData->rBDData.rDspSettings;
+ struct mwave_device_data *pDrvData = &mwave_s_mdd;
+ struct dsp_3780i_config_settings *pSettings = &pDrvData->rBDData.rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
unsigned short usIPCSource = 0, usIsolationMask, usPCNum;
- PRINTK_3(TRACE_TP3780I,
- "tp3780i::DspInterrupt entry irq %x dev_id %p\n", irq, dev_id);
-
if (dsp3780I_GetIPCSource(usDspBaseIO, &usIPCSource) == 0) {
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::DspInterrupt, return from dsp3780i_GetIPCSource, usIPCSource %x\n",
- usIPCSource);
usIsolationMask = 1;
for (usPCNum = 1; usPCNum <= 16; usPCNum++) {
if (usIPCSource & usIsolationMask) {
usIPCSource &= ~usIsolationMask;
- PRINTK_3(TRACE_TP3780I,
- "tp3780i::DspInterrupt usPCNum %x usIPCSource %x\n",
- usPCNum, usIPCSource);
if (pDrvData->IPCs[usPCNum - 1].usIntCount == 0) {
pDrvData->IPCs[usPCNum - 1].usIntCount = 1;
}
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::DspInterrupt usIntCount %x\n",
- pDrvData->IPCs[usPCNum - 1].usIntCount);
if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) {
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::DspInterrupt, waking up usPCNum %x\n",
- usPCNum - 1);
wake_up_interruptible(&pDrvData->IPCs[usPCNum - 1].ipc_wait_queue);
- } else {
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::DspInterrupt, no one waiting for IPC %x\n",
- usPCNum - 1);
}
}
if (usIPCSource == 0)
@@ -143,56 +120,42 @@ static irqreturn_t DspInterrupt(int irq, void *dev_id)
/* try next IPC */
usIsolationMask = usIsolationMask << 1;
}
- } else {
- PRINTK_1(TRACE_TP3780I,
- "tp3780i::DspInterrupt, return false from dsp3780i_GetIPCSource\n");
}
- PRINTK_1(TRACE_TP3780I, "tp3780i::DspInterrupt exit\n");
return IRQ_HANDLED;
}
-int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData)
+int tp3780I_InitializeBoardData(struct thinkpad_bd_data *pBDData)
{
int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
-
-
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData entry pBDData %p\n", pBDData);
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
pBDData->bDSPEnabled = false;
pSettings->bInterruptClaimed = false;
retval = smapi_init();
if (retval) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_InitializeBoardData: Error: SMAPI is not available on this machine\n");
+ pr_err("%s: Error: SMAPI is not available on this machine\n", __func__);
} else {
if (mwave_3780i_irq || mwave_3780i_io || mwave_uart_irq || mwave_uart_io) {
retval = smapi_set_DSP_cfg();
}
}
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData exit retval %x\n", retval);
-
return retval;
}
-void tp3780I_Cleanup(THINKPAD_BD_DATA *pBDData)
+void tp3780I_Cleanup(struct thinkpad_bd_data *pBDData)
{
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_Cleanup entry and exit pBDData %p\n", pBDData);
}
-int tp3780I_CalcResources(THINKPAD_BD_DATA * pBDData)
+int tp3780I_CalcResources(struct thinkpad_bd_data *pBDData)
{
- SMAPI_DSP_SETTINGS rSmapiInfo;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
-
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_CalcResources entry pBDData %p\n", pBDData);
+ struct smapi_dsp_settings rSmapiInfo;
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
if (smapi_query_DSP_cfg(&rSmapiInfo)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_CalcResources: Error: Could not query DSP config. Aborting.\n");
+ pr_err("%s: Error: Could not query DSP config. Aborting.\n", __func__);
return -EIO;
}
@@ -203,7 +166,7 @@ int tp3780I_CalcResources(THINKPAD_BD_DATA * pBDData)
|| ( rSmapiInfo.usUartIRQ == 0 )
|| ( rSmapiInfo.usUartBaseIO == 0 )
) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_CalcResources: Error: Illegal resource setting. Aborting.\n");
+ pr_err("%s: Error: Illegal resource setting. Aborting.\n", __func__);
return -EIO;
}
@@ -225,41 +188,31 @@ int tp3780I_CalcResources(THINKPAD_BD_DATA * pBDData)
pBDData->bShareDspIrq = pBDData->bShareUartIrq = 0;
}
- PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_CalcResources exit\n");
-
return 0;
}
-int tp3780I_ClaimResources(THINKPAD_BD_DATA * pBDData)
+int tp3780I_ClaimResources(struct thinkpad_bd_data *pBDData)
{
int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
struct resource *pres;
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_ClaimResources entry pBDData %p\n", pBDData);
-
pres = request_region(pSettings->usDspBaseIO, 16, "mwave_3780i");
if ( pres == NULL ) retval = -EIO;
if (retval) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_ClaimResources: Error: Could not claim I/O region starting at %x\n", pSettings->usDspBaseIO);
- retval = -EIO;
+ pr_err("%s: Error: Could not claim I/O region starting at %x\n", __func__,
+ pSettings->usDspBaseIO);
+ return -EIO;
}
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ClaimResources exit retval %x\n", retval);
-
return retval;
}
-int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
+int tp3780I_ReleaseResources(struct thinkpad_bd_data *pBDData)
{
- int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
-
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_ReleaseResources entry pBDData %p\n", pBDData);
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
release_region(pSettings->usDspBaseIO & (~3), 16);
@@ -268,28 +221,23 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
pSettings->bInterruptClaimed = false;
}
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_ReleaseResources exit retval %x\n", retval);
-
- return retval;
+ return 0;
}
-int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
+int tp3780I_EnableDSP(struct thinkpad_bd_data *pBDData)
{
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
bool bDSPPoweredUp = false, bInterruptAllocated = false;
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP entry pBDData %p\n", pBDData);
-
if (pBDData->bDSPEnabled) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: DSP already enabled!\n");
+ pr_err("%s: Error: DSP already enabled!\n", __func__);
goto exit_cleanup;
}
if (!pSettings->bDSPEnabled) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780::tp3780I_EnableDSP: Error: pSettings->bDSPEnabled not set\n");
+ pr_err("%s: Error: pSettings->bDSPEnabled not set\n", __func__);
goto exit_cleanup;
}
@@ -299,7 +247,7 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
|| (s_ausThinkpadIrqToField[pSettings->usDspIrq] == 0xFFFF)
|| (s_ausThinkpadDmaToField[pSettings->usDspDma] == 0xFFFF)
) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: invalid irq %x\n", pSettings->usDspIrq);
+ pr_err("%s: Error: invalid irq %x\n", __func__, pSettings->usDspIrq);
goto exit_cleanup;
}
@@ -307,7 +255,8 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
((pSettings->usDspBaseIO & 0xF00F) != 0)
|| (pSettings->usDspBaseIO & 0x0FF0) == 0
) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Invalid DSP base I/O address %x\n", pSettings->usDspBaseIO);
+ pr_err("%s: Error: Invalid DSP base I/O address %x\n", __func__,
+ pSettings->usDspBaseIO);
goto exit_cleanup;
}
@@ -316,7 +265,7 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
pSettings->usUartIrq >= s_numIrqs
|| s_ausThinkpadIrqToField[pSettings->usUartIrq] == 0xFFFF
) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Invalid UART IRQ %x\n", pSettings->usUartIrq);
+ pr_err("%s: Error: Invalid UART IRQ %x\n", __func__, pSettings->usUartIrq);
goto exit_cleanup;
}
switch (pSettings->usUartBaseIO) {
@@ -327,7 +276,8 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
break;
default:
- PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: Invalid UART base I/O address %x\n", pSettings->usUartBaseIO);
+ pr_err("%s: Error: Invalid UART base I/O address %x\n", __func__,
+ pSettings->usUartBaseIO);
goto exit_cleanup;
}
}
@@ -356,33 +306,30 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
pSettings->usChipletEnable = TP_CFG_ChipletEnable;
if (request_irq(pSettings->usUartIrq, &UartInterrupt, 0, "mwave_uart", NULL)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: Could not get UART IRQ %x\n", pSettings->usUartIrq);
+ pr_err("%s: Error: Could not get UART IRQ %x\n", __func__, pSettings->usUartIrq);
goto exit_cleanup;
} else { /* no conflict just release */
free_irq(pSettings->usUartIrq, NULL);
}
if (request_irq(pSettings->usDspIrq, &DspInterrupt, 0, "mwave_3780i", NULL)) {
- PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: Could not get 3780i IRQ %x\n", pSettings->usDspIrq);
+ pr_err("%s: Error: Could not get 3780i IRQ %x\n", __func__, pSettings->usDspIrq);
goto exit_cleanup;
} else {
- PRINTK_3(TRACE_TP3780I,
- "tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n",
- pSettings->usDspIrq, pBDData->bShareDspIrq);
bInterruptAllocated = true;
pSettings->bInterruptClaimed = true;
}
smapi_set_DSP_power_state(false);
if (smapi_set_DSP_power_state(true)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(true) failed\n");
+ pr_err("%s: Error: smapi_set_DSP_power_state(true) failed\n", __func__);
goto exit_cleanup;
} else {
bDSPPoweredUp = true;
}
if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) {
- PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Error: dsp7880I_EnableDSP() failed\n");
+ pr_err("%s: Error: dsp7880I_EnableDSP() failed\n", __func__);
goto exit_cleanup;
}
@@ -390,12 +337,10 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
pBDData->bDSPEnabled = true;
- PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n");
-
return 0;
exit_cleanup:
- PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n");
+ pr_err("%s: Cleaning up\n", __func__);
if (bDSPPoweredUp)
smapi_set_DSP_power_state(false);
if (bInterruptAllocated) {
@@ -406,12 +351,9 @@ exit_cleanup:
}
-int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData)
+int tp3780I_DisableDSP(struct thinkpad_bd_data *pBDData)
{
- int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
-
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP entry pBDData %p\n", pBDData);
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
if (pBDData->bDSPEnabled) {
dsp3780I_DisableDSP(&pBDData->rDspSettings);
@@ -423,56 +365,38 @@ int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData)
pBDData->bDSPEnabled = false;
}
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP exit retval %x\n", retval);
-
- return retval;
+ return 0;
}
-int tp3780I_ResetDSP(THINKPAD_BD_DATA * pBDData)
+int tp3780I_ResetDSP(struct thinkpad_bd_data *pBDData)
{
- int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
-
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ResetDSP entry pBDData %p\n",
- pBDData);
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
if (dsp3780I_Reset(pSettings) == 0) {
EnableSRAM(pBDData);
- } else {
- retval = -EIO;
+ return 0;
}
-
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ResetDSP exit retval %x\n", retval);
-
- return retval;
+ return -EIO;
}
-int tp3780I_StartDSP(THINKPAD_BD_DATA * pBDData)
+int tp3780I_StartDSP(struct thinkpad_bd_data *pBDData)
{
- int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
-
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_StartDSP entry pBDData %p\n", pBDData);
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
if (dsp3780I_Run(pSettings) == 0) {
// @BUG @TBD EnableSRAM(pBDData);
} else {
- retval = -EIO;
+ return -EIO;
}
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_StartDSP exit retval %x\n", retval);
-
- return retval;
+ return 0;
}
-int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities)
+int tp3780I_QueryAbilities(struct thinkpad_bd_data *pBDData, struct mw_abilities *pAbilities)
{
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_QueryAbilities entry pBDData %p\n", pBDData);
-
memset(pAbilities, 0, sizeof(*pAbilities));
/* fill out standard constant fields */
pAbilities->instr_per_sec = pBDData->rDspSettings.uIps;
@@ -497,25 +421,17 @@ int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities
memcpy(pAbilities->bios_task_name, TP_ABILITIES_BIOSTASK_NAME,
sizeof(TP_ABILITIES_BIOSTASK_NAME));
- PRINTK_1(TRACE_TP3780I,
- "tp3780i::tp3780I_QueryAbilities exit retval=SUCCESSFUL\n");
-
return 0;
}
-int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
+int tp3780I_ReadWriteDspDStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
void __user *pvBuffer, unsigned int uCount,
unsigned long ulDSPAddr)
{
- int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
bool bRC = 0;
- PRINTK_6(TRACE_TP3780I,
- "tp3780i::tp3780I_ReadWriteDspDStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",
- pBDData, uOpcode, pvBuffer, uCount, ulDSPAddr);
-
if (pBDData->bDSPEnabled) {
switch (uOpcode) {
case IOCTL_MW_READ_DATA:
@@ -532,26 +448,18 @@ int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
}
}
- retval = (bRC) ? -EIO : 0;
- PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_ReadWriteDspDStore exit retval %x\n", retval);
-
- return retval;
+ return bRC ? -EIO : 0;
}
-int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
+int tp3780I_ReadWriteDspIStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
void __user *pvBuffer, unsigned int uCount,
unsigned long ulDSPAddr)
{
- int retval = 0;
- DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
+ struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
bool bRC = 0;
- PRINTK_6(TRACE_TP3780I,
- "tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",
- pBDData, uOpcode, pvBuffer, uCount, ulDSPAddr);
-
if (pBDData->bDSPEnabled) {
switch (uOpcode) {
case IOCTL_MW_READ_INST:
@@ -564,11 +472,6 @@ int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
}
}
- retval = (bRC) ? -EIO : 0;
-
- PRINTK_2(TRACE_TP3780I,
- "tp3780i::tp3780I_ReadWriteDspIStore exit retval %x\n", retval);
-
- return retval;
+ return bRC ? -EIO : 0;
}
diff --git a/drivers/char/mwave/tp3780i.h b/drivers/char/mwave/tp3780i.h
index 8bd976d42fae..c0001a344741 100644
--- a/drivers/char/mwave/tp3780i.h
+++ b/drivers/char/mwave/tp3780i.h
@@ -75,27 +75,27 @@
#define TP_CFG_PllBypass 0 /* don't bypass */
#define TP_CFG_ChipletEnable 0xFFFF /* Enable all chiplets */
-typedef struct {
+struct thinkpad_bd_data {
int bDSPEnabled;
int bShareDspIrq;
int bShareUartIrq;
- DSP_3780I_CONFIG_SETTINGS rDspSettings;
-} THINKPAD_BD_DATA;
+ struct dsp_3780i_config_settings rDspSettings;
+};
-int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData);
-int tp3780I_CalcResources(THINKPAD_BD_DATA * pBDData);
-int tp3780I_ClaimResources(THINKPAD_BD_DATA * pBDData);
-int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData);
-int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData);
-int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData);
-int tp3780I_ResetDSP(THINKPAD_BD_DATA * pBDData);
-int tp3780I_StartDSP(THINKPAD_BD_DATA * pBDData);
-int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities);
-void tp3780I_Cleanup(THINKPAD_BD_DATA *pBDData);
-int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
+int tp3780I_InitializeBoardData(struct thinkpad_bd_data *pBDData);
+int tp3780I_CalcResources(struct thinkpad_bd_data *pBDData);
+int tp3780I_ClaimResources(struct thinkpad_bd_data *pBDData);
+int tp3780I_ReleaseResources(struct thinkpad_bd_data *pBDData);
+int tp3780I_EnableDSP(struct thinkpad_bd_data *pBDData);
+int tp3780I_DisableDSP(struct thinkpad_bd_data *pBDData);
+int tp3780I_ResetDSP(struct thinkpad_bd_data *pBDData);
+int tp3780I_StartDSP(struct thinkpad_bd_data *pBDData);
+int tp3780I_QueryAbilities(struct thinkpad_bd_data *pBDData, struct mw_abilities *pAbilities);
+void tp3780I_Cleanup(struct thinkpad_bd_data *pBDData);
+int tp3780I_ReadWriteDspDStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
void __user *pvBuffer, unsigned int uCount,
unsigned long ulDSPAddr);
-int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
+int tp3780I_ReadWriteDspIStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode,
void __user *pvBuffer, unsigned int uCount,
unsigned long ulDSPAddr);
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index e9f694b36871..9eff426a9286 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -540,6 +540,7 @@ static void __exit nvram_module_exit(void)
module_init(nvram_module_init);
module_exit(nvram_module_exit);
+MODULE_DESCRIPTION("CMOS/NV-RAM driver for Linux");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(NVRAM_MINOR);
MODULE_ALIAS("devname:nvram");
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
index ea378c0ed549..92cee5717237 100644
--- a/drivers/char/nwbutton.c
+++ b/drivers/char/nwbutton.c
@@ -241,6 +241,7 @@ static void __exit nwbutton_exit (void)
MODULE_AUTHOR("Alex Holden");
+MODULE_DESCRIPTION("NetWinder button driver");
MODULE_LICENSE("GPL");
module_init(nwbutton_init);
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index 0973c2c2b01a..9f52f0306ef7 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -618,6 +618,7 @@ static void __exit nwflash_exit(void)
iounmap((void *)FLASH_BASE);
}
+MODULE_DESCRIPTION("NetWinder flash memory driver");
MODULE_LICENSE("GPL");
module_param(flashdebug, bool, 0644);
diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c
index c39a836ebd15..5f4696813cea 100644
--- a/drivers/char/pc8736x_gpio.c
+++ b/drivers/char/pc8736x_gpio.c
@@ -235,7 +235,6 @@ static const struct file_operations pc8736x_gpio_fileops = {
.open = pc8736x_gpio_open,
.write = nsc_gpio_write,
.read = nsc_gpio_read,
- .llseek = no_llseek,
};
static void __init pc8736x_init_shadow(void)
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig
deleted file mode 100644
index f5d589b2be44..000000000000
--- a/drivers/char/pcmcia/Kconfig
+++ /dev/null
@@ -1,68 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# PCMCIA character device configuration
-#
-
-menu "PCMCIA character devices"
- depends on PCMCIA!=n
-
-config SYNCLINK_CS
- tristate "SyncLink PC Card support"
- depends on PCMCIA && TTY
- help
- Enable support for the SyncLink PC Card serial adapter, running
- asynchronous and HDLC communications up to 512Kbps. The port is
- selectable for RS-232, V.35, RS-449, RS-530, and X.21
-
- This driver may be built as a module ( = code which can be
- inserted in and removed from the running kernel whenever you want).
- The module will be called synclink_cs. If you want to do that, say M
- here.
-
-config CARDMAN_4000
- tristate "Omnikey Cardman 4000 support"
- depends on PCMCIA
- select BITREVERSE
- help
- Enable support for the Omnikey Cardman 4000 PCMCIA Smartcard
- reader.
-
- This kernel driver requires additional userspace support, either
- by the vendor-provided PC/SC ifd_handler (http://www.omnikey.com/),
- or via the cm4000 backend of OpenCT (http://www.opensc-project.org/opensc).
-
-config CARDMAN_4040
- tristate "Omnikey CardMan 4040 support"
- depends on PCMCIA
- help
- Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard
- reader.
-
- This card is basically a USB CCID device connected to a FIFO
- in I/O space. To use the kernel driver, you will need either the
- PC/SC ifdhandler provided from the Omnikey homepage
- (http://www.omnikey.com/), or a current development version of OpenCT
- (http://www.opensc-project.org/opensc).
-
-config SCR24X
- tristate "SCR24x Chip Card Interface support"
- depends on PCMCIA
- help
- Enable support for the SCR24x PCMCIA Chip Card Interface.
-
- To compile this driver as a module, choose M here.
- The module will be called scr24x_cs..
-
- If unsure say N.
-
-config IPWIRELESS
- tristate "IPWireless 3G UMTS PCMCIA card support"
- depends on PCMCIA && NETDEVICES && TTY
- select PPP
- help
- This is a driver for 3G UMTS PCMCIA card from IPWireless company. In
- some countries (for example Czech Republic, T-Mobile ISP) this card
- is shipped for service called UMTS 4G.
-
-endmenu
-
diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile
deleted file mode 100644
index 024eed1c4ca5..000000000000
--- a/drivers/char/pcmcia/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# drivers/char/pcmcia/Makefile
-#
-# Makefile for the Linux PCMCIA char device drivers.
-#
-
-obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
-obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o
-obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
-obj-$(CONFIG_SCR24X) += scr24x_cs.o
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
deleted file mode 100644
index adaec8fd4b16..000000000000
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ /dev/null
@@ -1,1910 +0,0 @@
- /*
- * A driver for the PCMCIA Smartcard Reader "Omnikey CardMan Mobile 4000"
- *
- * cm4000_cs.c support.linux@omnikey.com
- *
- * Tue Oct 23 11:32:43 GMT 2001 herp - cleaned up header files
- * Sun Jan 20 10:11:15 MET 2002 herp - added modversion header files
- * Thu Nov 14 16:34:11 GMT 2002 mh - added PPS functionality
- * Tue Nov 19 16:36:27 GMT 2002 mh - added SUSPEND/RESUME functionailty
- * Wed Jul 28 12:55:01 CEST 2004 mh - kernel 2.6 adjustments
- *
- * current version: 2.4.0gm4
- *
- * (C) 2000,2001,2002,2003,2004 Omnikey AG
- *
- * (C) 2005-2006 Harald Welte <laforge@gnumonks.org>
- * - Adhere to Kernel process/coding-style.rst
- * - Port to 2.6.13 "new" style PCMCIA
- * - Check for copy_{from,to}_user return values
- * - Use nonseekable_open()
- * - add class interface for udev device creation
- *
- * All rights reserved. Licensed under dual BSD/GPL license.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/bitrev.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include <linux/cm4000_cs.h>
-
-/* #define ATR_CSUM */
-
-#define reader_to_dev(x) (&x->p_dev->dev)
-
-/* n (debug level) is ignored */
-/* additional debug output may be enabled by re-compiling with
- * CM4000_DEBUG set */
-/* #define CM4000_DEBUG */
-#define DEBUGP(n, rdr, x, args...) do { \
- dev_dbg(reader_to_dev(rdr), "%s:" x, \
- __func__ , ## args); \
- } while (0)
-
-static DEFINE_MUTEX(cmm_mutex);
-
-#define T_1SEC (HZ)
-#define T_10MSEC msecs_to_jiffies(10)
-#define T_20MSEC msecs_to_jiffies(20)
-#define T_40MSEC msecs_to_jiffies(40)
-#define T_50MSEC msecs_to_jiffies(50)
-#define T_100MSEC msecs_to_jiffies(100)
-#define T_500MSEC msecs_to_jiffies(500)
-
-static void cm4000_release(struct pcmcia_device *link);
-
-static int major; /* major number we get from the kernel */
-
-/* note: the first state has to have number 0 always */
-
-#define M_FETCH_ATR 0
-#define M_TIMEOUT_WAIT 1
-#define M_READ_ATR_LEN 2
-#define M_READ_ATR 3
-#define M_ATR_PRESENT 4
-#define M_BAD_CARD 5
-#define M_CARDOFF 6
-
-#define LOCK_IO 0
-#define LOCK_MONITOR 1
-
-#define IS_AUTOPPS_ACT 6
-#define IS_PROCBYTE_PRESENT 7
-#define IS_INVREV 8
-#define IS_ANY_T0 9
-#define IS_ANY_T1 10
-#define IS_ATR_PRESENT 11
-#define IS_ATR_VALID 12
-#define IS_CMM_ABSENT 13
-#define IS_BAD_LENGTH 14
-#define IS_BAD_CSUM 15
-#define IS_BAD_CARD 16
-
-#define REG_FLAGS0(x) (x + 0)
-#define REG_FLAGS1(x) (x + 1)
-#define REG_NUM_BYTES(x) (x + 2)
-#define REG_BUF_ADDR(x) (x + 3)
-#define REG_BUF_DATA(x) (x + 4)
-#define REG_NUM_SEND(x) (x + 5)
-#define REG_BAUDRATE(x) (x + 6)
-#define REG_STOPBITS(x) (x + 7)
-
-struct cm4000_dev {
- struct pcmcia_device *p_dev;
-
- unsigned char atr[MAX_ATR];
- unsigned char rbuf[512];
- unsigned char sbuf[512];
-
- wait_queue_head_t devq; /* when removing cardman must not be
- zeroed! */
-
- wait_queue_head_t ioq; /* if IO is locked, wait on this Q */
- wait_queue_head_t atrq; /* wait for ATR valid */
- wait_queue_head_t readq; /* used by write to wake blk.read */
-
- /* warning: do not move this struct group.
- * initialising to zero depends on it - see ZERO_DEV below. */
- struct_group(init,
- unsigned char atr_csum;
- unsigned char atr_len_retry;
- unsigned short atr_len;
- unsigned short rlen; /* bytes avail. after write */
- unsigned short rpos; /* latest read pos. write zeroes */
- unsigned char procbyte; /* T=0 procedure byte */
- unsigned char mstate; /* state of card monitor */
- unsigned char cwarn; /* slow down warning */
- unsigned char flags0; /* cardman IO-flags 0 */
- unsigned char flags1; /* cardman IO-flags 1 */
- unsigned int mdelay; /* variable monitor speeds, in jiffies */
-
- unsigned int baudv; /* baud value for speed */
- unsigned char ta1;
- unsigned char proto; /* T=0, T=1, ... */
- unsigned long flags; /* lock+flags (MONITOR,IO,ATR) * for concurrent
- access */
-
- unsigned char pts[4];
-
- struct timer_list timer; /* used to keep monitor running */
- int monitor_running;
- );
-};
-
-#define ZERO_DEV(dev) memset(&((dev)->init), 0, sizeof((dev)->init))
-
-static struct pcmcia_device *dev_table[CM4000_MAX_DEV];
-static struct class *cmm_class;
-
-/* This table doesn't use spaces after the comma between fields and thus
- * violates process/coding-style.rst. However, I don't really think wrapping it around will
- * make it any clearer to read -HW */
-static unsigned char fi_di_table[10][14] = {
-/*FI 00 01 02 03 04 05 06 07 08 09 10 11 12 13 */
-/*DI */
-/* 0 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
-/* 1 */ {0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x91,0x11,0x11,0x11,0x11},
-/* 2 */ {0x02,0x12,0x22,0x32,0x11,0x11,0x11,0x11,0x11,0x92,0xA2,0xB2,0x11,0x11},
-/* 3 */ {0x03,0x13,0x23,0x33,0x43,0x53,0x63,0x11,0x11,0x93,0xA3,0xB3,0xC3,0xD3},
-/* 4 */ {0x04,0x14,0x24,0x34,0x44,0x54,0x64,0x11,0x11,0x94,0xA4,0xB4,0xC4,0xD4},
-/* 5 */ {0x00,0x15,0x25,0x35,0x45,0x55,0x65,0x11,0x11,0x95,0xA5,0xB5,0xC5,0xD5},
-/* 6 */ {0x06,0x16,0x26,0x36,0x46,0x56,0x66,0x11,0x11,0x96,0xA6,0xB6,0xC6,0xD6},
-/* 7 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
-/* 8 */ {0x08,0x11,0x28,0x38,0x48,0x58,0x68,0x11,0x11,0x98,0xA8,0xB8,0xC8,0xD8},
-/* 9 */ {0x09,0x19,0x29,0x39,0x49,0x59,0x69,0x11,0x11,0x99,0xA9,0xB9,0xC9,0xD9}
-};
-
-#ifndef CM4000_DEBUG
-#define xoutb outb
-#define xinb inb
-#else
-static inline void xoutb(unsigned char val, unsigned short port)
-{
- pr_debug("outb(val=%.2x,port=%.4x)\n", val, port);
- outb(val, port);
-}
-static inline unsigned char xinb(unsigned short port)
-{
- unsigned char val;
-
- val = inb(port);
- pr_debug("%.2x=inb(%.4x)\n", val, port);
-
- return val;
-}
-#endif
-
-static inline unsigned char invert_revert(unsigned char ch)
-{
- return bitrev8(~ch);
-}
-
-static void str_invert_revert(unsigned char *b, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- b[i] = invert_revert(b[i]);
-}
-
-#define ATRLENCK(dev,pos) \
- if (pos>=dev->atr_len || pos>=MAX_ATR) \
- goto return_0;
-
-static unsigned int calc_baudv(unsigned char fidi)
-{
- unsigned int wcrcf, wbrcf, fi_rfu, di_rfu;
-
- fi_rfu = 372;
- di_rfu = 1;
-
- /* FI */
- switch ((fidi >> 4) & 0x0F) {
- case 0x00:
- wcrcf = 372;
- break;
- case 0x01:
- wcrcf = 372;
- break;
- case 0x02:
- wcrcf = 558;
- break;
- case 0x03:
- wcrcf = 744;
- break;
- case 0x04:
- wcrcf = 1116;
- break;
- case 0x05:
- wcrcf = 1488;
- break;
- case 0x06:
- wcrcf = 1860;
- break;
- case 0x07:
- wcrcf = fi_rfu;
- break;
- case 0x08:
- wcrcf = fi_rfu;
- break;
- case 0x09:
- wcrcf = 512;
- break;
- case 0x0A:
- wcrcf = 768;
- break;
- case 0x0B:
- wcrcf = 1024;
- break;
- case 0x0C:
- wcrcf = 1536;
- break;
- case 0x0D:
- wcrcf = 2048;
- break;
- default:
- wcrcf = fi_rfu;
- break;
- }
-
- /* DI */
- switch (fidi & 0x0F) {
- case 0x00:
- wbrcf = di_rfu;
- break;
- case 0x01:
- wbrcf = 1;
- break;
- case 0x02:
- wbrcf = 2;
- break;
- case 0x03:
- wbrcf = 4;
- break;
- case 0x04:
- wbrcf = 8;
- break;
- case 0x05:
- wbrcf = 16;
- break;
- case 0x06:
- wbrcf = 32;
- break;
- case 0x07:
- wbrcf = di_rfu;
- break;
- case 0x08:
- wbrcf = 12;
- break;
- case 0x09:
- wbrcf = 20;
- break;
- default:
- wbrcf = di_rfu;
- break;
- }
-
- return (wcrcf / wbrcf);
-}
-
-static unsigned short io_read_num_rec_bytes(unsigned int iobase,
- unsigned short *s)
-{
- unsigned short tmp;
-
- tmp = *s = 0;
- do {
- *s = tmp;
- tmp = inb(REG_NUM_BYTES(iobase)) |
- (inb(REG_FLAGS0(iobase)) & 4 ? 0x100 : 0);
- } while (tmp != *s);
-
- return *s;
-}
-
-static int parse_atr(struct cm4000_dev *dev)
-{
- unsigned char any_t1, any_t0;
- unsigned char ch, ifno;
- int ix, done;
-
- DEBUGP(3, dev, "-> parse_atr: dev->atr_len = %i\n", dev->atr_len);
-
- if (dev->atr_len < 3) {
- DEBUGP(5, dev, "parse_atr: atr_len < 3\n");
- return 0;
- }
-
- if (dev->atr[0] == 0x3f)
- set_bit(IS_INVREV, &dev->flags);
- else
- clear_bit(IS_INVREV, &dev->flags);
- ix = 1;
- ifno = 1;
- ch = dev->atr[1];
- dev->proto = 0; /* XXX PROTO */
- any_t1 = any_t0 = done = 0;
- dev->ta1 = 0x11; /* defaults to 9600 baud */
- do {
- if (ifno == 1 && (ch & 0x10)) {
- /* read first interface byte and TA1 is present */
- dev->ta1 = dev->atr[2];
- DEBUGP(5, dev, "Card says FiDi is 0x%.2x\n", dev->ta1);
- ifno++;
- } else if ((ifno == 2) && (ch & 0x10)) { /* TA(2) */
- dev->ta1 = 0x11;
- ifno++;
- }
-
- DEBUGP(5, dev, "Yi=%.2x\n", ch & 0xf0);
- ix += ((ch & 0x10) >> 4) /* no of int.face chars */
- +((ch & 0x20) >> 5)
- + ((ch & 0x40) >> 6)
- + ((ch & 0x80) >> 7);
- /* ATRLENCK(dev,ix); */
- if (ch & 0x80) { /* TDi */
- ch = dev->atr[ix];
- if ((ch & 0x0f)) {
- any_t1 = 1;
- DEBUGP(5, dev, "card is capable of T=1\n");
- } else {
- any_t0 = 1;
- DEBUGP(5, dev, "card is capable of T=0\n");
- }
- } else
- done = 1;
- } while (!done);
-
- DEBUGP(5, dev, "ix=%d noHist=%d any_t1=%d\n",
- ix, dev->atr[1] & 15, any_t1);
- if (ix + 1 + (dev->atr[1] & 0x0f) + any_t1 != dev->atr_len) {
- DEBUGP(5, dev, "length error\n");
- return 0;
- }
- if (any_t0)
- set_bit(IS_ANY_T0, &dev->flags);
-
- if (any_t1) { /* compute csum */
- dev->atr_csum = 0;
-#ifdef ATR_CSUM
- for (i = 1; i < dev->atr_len; i++)
- dev->atr_csum ^= dev->atr[i];
- if (dev->atr_csum) {
- set_bit(IS_BAD_CSUM, &dev->flags);
- DEBUGP(5, dev, "bad checksum\n");
- goto return_0;
- }
-#endif
- if (any_t0 == 0)
- dev->proto = 1; /* XXX PROTO */
- set_bit(IS_ANY_T1, &dev->flags);
- }
-
- return 1;
-}
-
-struct card_fixup {
- char atr[12];
- u_int8_t atr_len;
- u_int8_t stopbits;
-};
-
-static struct card_fixup card_fixups[] = {
- { /* ACOS */
- .atr = { 0x3b, 0xb3, 0x11, 0x00, 0x00, 0x41, 0x01 },
- .atr_len = 7,
- .stopbits = 0x03,
- },
- { /* Motorola */
- .atr = {0x3b, 0x76, 0x13, 0x00, 0x00, 0x80, 0x62, 0x07,
- 0x41, 0x81, 0x81 },
- .atr_len = 11,
- .stopbits = 0x04,
- },
-};
-
-static void set_cardparameter(struct cm4000_dev *dev)
-{
- int i;
- unsigned int iobase = dev->p_dev->resource[0]->start;
- u_int8_t stopbits = 0x02; /* ISO default */
-
- DEBUGP(3, dev, "-> set_cardparameter\n");
-
- dev->flags1 = dev->flags1 | (((dev->baudv - 1) & 0x0100) >> 8);
- xoutb(dev->flags1, REG_FLAGS1(iobase));
- DEBUGP(5, dev, "flags1 = 0x%02x\n", dev->flags1);
-
- /* set baudrate */
- xoutb((unsigned char)((dev->baudv - 1) & 0xFF), REG_BAUDRATE(iobase));
-
- DEBUGP(5, dev, "baudv = %i -> write 0x%02x\n", dev->baudv,
- ((dev->baudv - 1) & 0xFF));
-
- /* set stopbits */
- for (i = 0; i < ARRAY_SIZE(card_fixups); i++) {
- if (!memcmp(dev->atr, card_fixups[i].atr,
- card_fixups[i].atr_len))
- stopbits = card_fixups[i].stopbits;
- }
- xoutb(stopbits, REG_STOPBITS(iobase));
-
- DEBUGP(3, dev, "<- set_cardparameter\n");
-}
-
-static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq)
-{
-
- unsigned long tmp, i;
- unsigned short num_bytes_read;
- unsigned char pts_reply[4];
- ssize_t rc;
- unsigned int iobase = dev->p_dev->resource[0]->start;
-
- rc = 0;
-
- DEBUGP(3, dev, "-> set_protocol\n");
- DEBUGP(5, dev, "ptsreq->Protocol = 0x%.8x, ptsreq->Flags=0x%.8x, "
- "ptsreq->pts1=0x%.2x, ptsreq->pts2=0x%.2x, "
- "ptsreq->pts3=0x%.2x\n", (unsigned int)ptsreq->protocol,
- (unsigned int)ptsreq->flags, ptsreq->pts1, ptsreq->pts2,
- ptsreq->pts3);
-
- /* Fill PTS structure */
- dev->pts[0] = 0xff;
- dev->pts[1] = 0x00;
- tmp = ptsreq->protocol;
- while ((tmp = (tmp >> 1)) > 0)
- dev->pts[1]++;
- dev->proto = dev->pts[1]; /* Set new protocol */
- dev->pts[1] = (0x01 << 4) | (dev->pts[1]);
-
- /* Correct Fi/Di according to CM4000 Fi/Di table */
- DEBUGP(5, dev, "Ta(1) from ATR is 0x%.2x\n", dev->ta1);
- /* set Fi/Di according to ATR TA(1) */
- dev->pts[2] = fi_di_table[dev->ta1 & 0x0F][(dev->ta1 >> 4) & 0x0F];
-
- /* Calculate PCK character */
- dev->pts[3] = dev->pts[0] ^ dev->pts[1] ^ dev->pts[2];
-
- DEBUGP(5, dev, "pts0=%.2x, pts1=%.2x, pts2=%.2x, pts3=%.2x\n",
- dev->pts[0], dev->pts[1], dev->pts[2], dev->pts[3]);
-
- /* check card convention */
- if (test_bit(IS_INVREV, &dev->flags))
- str_invert_revert(dev->pts, 4);
-
- /* reset SM */
- xoutb(0x80, REG_FLAGS0(iobase));
-
- /* Enable access to the message buffer */
- DEBUGP(5, dev, "Enable access to the messages buffer\n");
- dev->flags1 = 0x20 /* T_Active */
- | (test_bit(IS_INVREV, &dev->flags) ? 0x02 : 0x00) /* inv parity */
- | ((dev->baudv >> 8) & 0x01); /* MSB-baud */
- xoutb(dev->flags1, REG_FLAGS1(iobase));
-
- DEBUGP(5, dev, "Enable message buffer -> flags1 = 0x%.2x\n",
- dev->flags1);
-
- /* write challenge to the buffer */
- DEBUGP(5, dev, "Write challenge to buffer: ");
- for (i = 0; i < 4; i++) {
- xoutb(i, REG_BUF_ADDR(iobase));
- xoutb(dev->pts[i], REG_BUF_DATA(iobase)); /* buf data */
-#ifdef CM4000_DEBUG
- pr_debug("0x%.2x ", dev->pts[i]);
- }
- pr_debug("\n");
-#else
- }
-#endif
-
- /* set number of bytes to write */
- DEBUGP(5, dev, "Set number of bytes to write\n");
- xoutb(0x04, REG_NUM_SEND(iobase));
-
- /* Trigger CARDMAN CONTROLLER */
- xoutb(0x50, REG_FLAGS0(iobase));
-
- /* Monitor progress */
- /* wait for xmit done */
- DEBUGP(5, dev, "Waiting for NumRecBytes getting valid\n");
-
- for (i = 0; i < 100; i++) {
- if (inb(REG_FLAGS0(iobase)) & 0x08) {
- DEBUGP(5, dev, "NumRecBytes is valid\n");
- break;
- }
- usleep_range(10000, 11000);
- }
- if (i == 100) {
- DEBUGP(5, dev, "Timeout waiting for NumRecBytes getting "
- "valid\n");
- rc = -EIO;
- goto exit_setprotocol;
- }
-
- DEBUGP(5, dev, "Reading NumRecBytes\n");
- for (i = 0; i < 100; i++) {
- io_read_num_rec_bytes(iobase, &num_bytes_read);
- if (num_bytes_read >= 4) {
- DEBUGP(2, dev, "NumRecBytes = %i\n", num_bytes_read);
- if (num_bytes_read > 4) {
- rc = -EIO;
- goto exit_setprotocol;
- }
- break;
- }
- usleep_range(10000, 11000);
- }
-
- /* check whether it is a short PTS reply? */
- if (num_bytes_read == 3)
- i = 0;
-
- if (i == 100) {
- DEBUGP(5, dev, "Timeout reading num_bytes_read\n");
- rc = -EIO;
- goto exit_setprotocol;
- }
-
- DEBUGP(5, dev, "Reset the CARDMAN CONTROLLER\n");
- xoutb(0x80, REG_FLAGS0(iobase));
-
- /* Read PPS reply */
- DEBUGP(5, dev, "Read PPS reply\n");
- for (i = 0; i < num_bytes_read; i++) {
- xoutb(i, REG_BUF_ADDR(iobase));
- pts_reply[i] = inb(REG_BUF_DATA(iobase));
- }
-
-#ifdef CM4000_DEBUG
- DEBUGP(2, dev, "PTSreply: ");
- for (i = 0; i < num_bytes_read; i++) {
- pr_debug("0x%.2x ", pts_reply[i]);
- }
- pr_debug("\n");
-#endif /* CM4000_DEBUG */
-
- DEBUGP(5, dev, "Clear Tactive in Flags1\n");
- xoutb(0x20, REG_FLAGS1(iobase));
-
- /* Compare ptsreq and ptsreply */
- if ((dev->pts[0] == pts_reply[0]) &&
- (dev->pts[1] == pts_reply[1]) &&
- (dev->pts[2] == pts_reply[2]) && (dev->pts[3] == pts_reply[3])) {
- /* setcardparameter according to PPS */
- dev->baudv = calc_baudv(dev->pts[2]);
- set_cardparameter(dev);
- } else if ((dev->pts[0] == pts_reply[0]) &&
- ((dev->pts[1] & 0xef) == pts_reply[1]) &&
- ((pts_reply[0] ^ pts_reply[1]) == pts_reply[2])) {
- /* short PTS reply, set card parameter to default values */
- dev->baudv = calc_baudv(0x11);
- set_cardparameter(dev);
- } else
- rc = -EIO;
-
-exit_setprotocol:
- DEBUGP(3, dev, "<- set_protocol\n");
- return rc;
-}
-
-static int io_detect_cm4000(unsigned int iobase, struct cm4000_dev *dev)
-{
-
- /* note: statemachine is assumed to be reset */
- if (inb(REG_FLAGS0(iobase)) & 8) {
- clear_bit(IS_ATR_VALID, &dev->flags);
- set_bit(IS_CMM_ABSENT, &dev->flags);
- return 0; /* detect CMM = 1 -> failure */
- }
- /* xoutb(0x40, REG_FLAGS1(iobase)); detectCMM */
- xoutb(dev->flags1 | 0x40, REG_FLAGS1(iobase));
- if ((inb(REG_FLAGS0(iobase)) & 8) == 0) {
- clear_bit(IS_ATR_VALID, &dev->flags);
- set_bit(IS_CMM_ABSENT, &dev->flags);
- return 0; /* detect CMM=0 -> failure */
- }
- /* clear detectCMM again by restoring original flags1 */
- xoutb(dev->flags1, REG_FLAGS1(iobase));
- return 1;
-}
-
-static void terminate_monitor(struct cm4000_dev *dev)
-{
-
- /* tell the monitor to stop and wait until
- * it terminates.
- */
- DEBUGP(3, dev, "-> terminate_monitor\n");
- wait_event_interruptible(dev->devq,
- test_and_set_bit(LOCK_MONITOR,
- (void *)&dev->flags));
-
- /* now, LOCK_MONITOR has been set.
- * allow a last cycle in the monitor.
- * the monitor will indicate that it has
- * finished by clearing this bit.
- */
- DEBUGP(5, dev, "Now allow last cycle of monitor!\n");
- while (test_bit(LOCK_MONITOR, (void *)&dev->flags))
- msleep(25);
-
- DEBUGP(5, dev, "Delete timer\n");
- del_timer_sync(&dev->timer);
-#ifdef CM4000_DEBUG
- dev->monitor_running = 0;
-#endif
-
- DEBUGP(3, dev, "<- terminate_monitor\n");
-}
-
-/*
- * monitor the card every 50msec. as a side-effect, retrieve the
- * atr once a card is inserted. another side-effect of retrieving the
- * atr is that the card will be powered on, so there is no need to
- * power on the card explicitly from the application: the driver
- * is already doing that for you.
- */
-
-static void monitor_card(struct timer_list *t)
-{
- struct cm4000_dev *dev = from_timer(dev, t, timer);
- unsigned int iobase = dev->p_dev->resource[0]->start;
- unsigned short s;
- struct ptsreq ptsreq;
- int i, atrc;
-
- DEBUGP(7, dev, "-> monitor_card\n");
-
- /* if someone has set the lock for us: we're done! */
- if (test_and_set_bit(LOCK_MONITOR, &dev->flags)) {
- DEBUGP(4, dev, "About to stop monitor\n");
- /* no */
- dev->rlen =
- dev->rpos =
- dev->atr_csum = dev->atr_len_retry = dev->cwarn = 0;
- dev->mstate = M_FETCH_ATR;
- clear_bit(LOCK_MONITOR, &dev->flags);
- /* close et al. are sleeping on devq, so wake it */
- wake_up_interruptible(&dev->devq);
- DEBUGP(2, dev, "<- monitor_card (we are done now)\n");
- return;
- }
-
- /* try to lock io: if it is already locked, just add another timer */
- if (test_and_set_bit(LOCK_IO, (void *)&dev->flags)) {
- DEBUGP(4, dev, "Couldn't get IO lock\n");
- goto return_with_timer;
- }
-
- /* is a card/a reader inserted at all ? */
- dev->flags0 = xinb(REG_FLAGS0(iobase));
- DEBUGP(7, dev, "dev->flags0 = 0x%2x\n", dev->flags0);
- DEBUGP(7, dev, "smartcard present: %s\n",
- dev->flags0 & 1 ? "yes" : "no");
- DEBUGP(7, dev, "cardman present: %s\n",
- dev->flags0 == 0xff ? "no" : "yes");
-
- if ((dev->flags0 & 1) == 0 /* no smartcard inserted */
- || dev->flags0 == 0xff) { /* no cardman inserted */
- /* no */
- dev->rlen =
- dev->rpos =
- dev->atr_csum = dev->atr_len_retry = dev->cwarn = 0;
- dev->mstate = M_FETCH_ATR;
-
- dev->flags &= 0x000000ff; /* only keep IO and MONITOR locks */
-
- if (dev->flags0 == 0xff) {
- DEBUGP(4, dev, "set IS_CMM_ABSENT bit\n");
- set_bit(IS_CMM_ABSENT, &dev->flags);
- } else if (test_bit(IS_CMM_ABSENT, &dev->flags)) {
- DEBUGP(4, dev, "clear IS_CMM_ABSENT bit "
- "(card is removed)\n");
- clear_bit(IS_CMM_ABSENT, &dev->flags);
- }
-
- goto release_io;
- } else if ((dev->flags0 & 1) && test_bit(IS_CMM_ABSENT, &dev->flags)) {
- /* cardman and card present but cardman was absent before
- * (after suspend with inserted card) */
- DEBUGP(4, dev, "clear IS_CMM_ABSENT bit (card is inserted)\n");
- clear_bit(IS_CMM_ABSENT, &dev->flags);
- }
-
- if (test_bit(IS_ATR_VALID, &dev->flags) == 1) {
- DEBUGP(7, dev, "believe ATR is already valid (do nothing)\n");
- goto release_io;
- }
-
- switch (dev->mstate) {
- case M_CARDOFF: {
- unsigned char flags0;
-
- DEBUGP(4, dev, "M_CARDOFF\n");
- flags0 = inb(REG_FLAGS0(iobase));
- if (flags0 & 0x02) {
- /* wait until Flags0 indicate power is off */
- dev->mdelay = T_10MSEC;
- } else {
- /* Flags0 indicate power off and no card inserted now;
- * Reset CARDMAN CONTROLLER */
- xoutb(0x80, REG_FLAGS0(iobase));
-
- /* prepare for fetching ATR again: after card off ATR
- * is read again automatically */
- dev->rlen =
- dev->rpos =
- dev->atr_csum =
- dev->atr_len_retry = dev->cwarn = 0;
- dev->mstate = M_FETCH_ATR;
-
- /* minimal gap between CARDOFF and read ATR is 50msec */
- dev->mdelay = T_50MSEC;
- }
- break;
- }
- case M_FETCH_ATR:
- DEBUGP(4, dev, "M_FETCH_ATR\n");
- xoutb(0x80, REG_FLAGS0(iobase));
- DEBUGP(4, dev, "Reset BAUDV to 9600\n");
- dev->baudv = 0x173; /* 9600 */
- xoutb(0x02, REG_STOPBITS(iobase)); /* stopbits=2 */
- xoutb(0x73, REG_BAUDRATE(iobase)); /* baud value */
- xoutb(0x21, REG_FLAGS1(iobase)); /* T_Active=1, baud
- value */
- /* warm start vs. power on: */
- xoutb(dev->flags0 & 2 ? 0x46 : 0x44, REG_FLAGS0(iobase));
- dev->mdelay = T_40MSEC;
- dev->mstate = M_TIMEOUT_WAIT;
- break;
- case M_TIMEOUT_WAIT:
- DEBUGP(4, dev, "M_TIMEOUT_WAIT\n");
- /* numRecBytes */
- io_read_num_rec_bytes(iobase, &dev->atr_len);
- dev->mdelay = T_10MSEC;
- dev->mstate = M_READ_ATR_LEN;
- break;
- case M_READ_ATR_LEN:
- DEBUGP(4, dev, "M_READ_ATR_LEN\n");
- /* infinite loop possible, since there is no timeout */
-
-#define MAX_ATR_LEN_RETRY 100
-
- if (dev->atr_len == io_read_num_rec_bytes(iobase, &s)) {
- if (dev->atr_len_retry++ >= MAX_ATR_LEN_RETRY) { /* + XX msec */
- dev->mdelay = T_10MSEC;
- dev->mstate = M_READ_ATR;
- }
- } else {
- dev->atr_len = s;
- dev->atr_len_retry = 0; /* set new timeout */
- }
-
- DEBUGP(4, dev, "Current ATR_LEN = %i\n", dev->atr_len);
- break;
- case M_READ_ATR:
- DEBUGP(4, dev, "M_READ_ATR\n");
- xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */
- for (i = 0; i < dev->atr_len; i++) {
- xoutb(i, REG_BUF_ADDR(iobase));
- dev->atr[i] = inb(REG_BUF_DATA(iobase));
- }
- /* Deactivate T_Active flags */
- DEBUGP(4, dev, "Deactivate T_Active flags\n");
- dev->flags1 = 0x01;
- xoutb(dev->flags1, REG_FLAGS1(iobase));
-
- /* atr is present (which doesn't mean it's valid) */
- set_bit(IS_ATR_PRESENT, &dev->flags);
- if (dev->atr[0] == 0x03)
- str_invert_revert(dev->atr, dev->atr_len);
- atrc = parse_atr(dev);
- if (atrc == 0) { /* atr invalid */
- dev->mdelay = 0;
- dev->mstate = M_BAD_CARD;
- } else {
- dev->mdelay = T_50MSEC;
- dev->mstate = M_ATR_PRESENT;
- set_bit(IS_ATR_VALID, &dev->flags);
- }
-
- if (test_bit(IS_ATR_VALID, &dev->flags) == 1) {
- DEBUGP(4, dev, "monitor_card: ATR valid\n");
- /* if ta1 == 0x11, no PPS necessary (default values) */
- /* do not do PPS with multi protocol cards */
- if ((test_bit(IS_AUTOPPS_ACT, &dev->flags) == 0) &&
- (dev->ta1 != 0x11) &&
- !(test_bit(IS_ANY_T0, &dev->flags) &&
- test_bit(IS_ANY_T1, &dev->flags))) {
- DEBUGP(4, dev, "Perform AUTOPPS\n");
- set_bit(IS_AUTOPPS_ACT, &dev->flags);
- ptsreq.protocol = (0x01 << dev->proto);
- ptsreq.flags = 0x01;
- ptsreq.pts1 = 0x00;
- ptsreq.pts2 = 0x00;
- ptsreq.pts3 = 0x00;
- if (set_protocol(dev, &ptsreq) == 0) {
- DEBUGP(4, dev, "AUTOPPS ret SUCC\n");
- clear_bit(IS_AUTOPPS_ACT, &dev->flags);
- wake_up_interruptible(&dev->atrq);
- } else {
- DEBUGP(4, dev, "AUTOPPS failed: "
- "repower using defaults\n");
- /* prepare for repowering */
- clear_bit(IS_ATR_PRESENT, &dev->flags);
- clear_bit(IS_ATR_VALID, &dev->flags);
- dev->rlen =
- dev->rpos =
- dev->atr_csum =
- dev->atr_len_retry = dev->cwarn = 0;
- dev->mstate = M_FETCH_ATR;
-
- dev->mdelay = T_50MSEC;
- }
- } else {
- /* for cards which use slightly different
- * params (extra guard time) */
- set_cardparameter(dev);
- if (test_bit(IS_AUTOPPS_ACT, &dev->flags) == 1)
- DEBUGP(4, dev, "AUTOPPS already active "
- "2nd try:use default values\n");
- if (dev->ta1 == 0x11)
- DEBUGP(4, dev, "No AUTOPPS necessary "
- "TA(1)==0x11\n");
- if (test_bit(IS_ANY_T0, &dev->flags)
- && test_bit(IS_ANY_T1, &dev->flags))
- DEBUGP(4, dev, "Do NOT perform AUTOPPS "
- "with multiprotocol cards\n");
- clear_bit(IS_AUTOPPS_ACT, &dev->flags);
- wake_up_interruptible(&dev->atrq);
- }
- } else {
- DEBUGP(4, dev, "ATR invalid\n");
- wake_up_interruptible(&dev->atrq);
- }
- break;
- case M_BAD_CARD:
- DEBUGP(4, dev, "M_BAD_CARD\n");
- /* slow down warning, but prompt immediately after insertion */
- if (dev->cwarn == 0 || dev->cwarn == 10) {
- set_bit(IS_BAD_CARD, &dev->flags);
- dev_warn(&dev->p_dev->dev, MODULE_NAME ": ");
- if (test_bit(IS_BAD_CSUM, &dev->flags)) {
- DEBUGP(4, dev, "ATR checksum (0x%.2x, should "
- "be zero) failed\n", dev->atr_csum);
- }
-#ifdef CM4000_DEBUG
- else if (test_bit(IS_BAD_LENGTH, &dev->flags)) {
- DEBUGP(4, dev, "ATR length error\n");
- } else {
- DEBUGP(4, dev, "card damaged or wrong way "
- "inserted\n");
- }
-#endif
- dev->cwarn = 0;
- wake_up_interruptible(&dev->atrq); /* wake open */
- }
- dev->cwarn++;
- dev->mdelay = T_100MSEC;
- dev->mstate = M_FETCH_ATR;
- break;
- default:
- DEBUGP(7, dev, "Unknown action\n");
- break; /* nothing */
- }
-
-release_io:
- DEBUGP(7, dev, "release_io\n");
- clear_bit(LOCK_IO, &dev->flags);
- wake_up_interruptible(&dev->ioq); /* whoever needs IO */
-
-return_with_timer:
- DEBUGP(7, dev, "<- monitor_card (returns with timer)\n");
- mod_timer(&dev->timer, jiffies + dev->mdelay);
- clear_bit(LOCK_MONITOR, &dev->flags);
-}
-
-/* Interface to userland (file_operations) */
-
-static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count,
- loff_t *ppos)
-{
- struct cm4000_dev *dev = filp->private_data;
- unsigned int iobase = dev->p_dev->resource[0]->start;
- ssize_t rc;
- int i, j, k;
-
- DEBUGP(2, dev, "-> cmm_read(%s,%d)\n", current->comm, current->pid);
-
- if (count == 0) /* according to manpage */
- return 0;
-
- if (!pcmcia_dev_present(dev->p_dev) || /* device removed */
- test_bit(IS_CMM_ABSENT, &dev->flags))
- return -ENODEV;
-
- if (test_bit(IS_BAD_CSUM, &dev->flags))
- return -EIO;
-
- /* also see the note about this in cmm_write */
- if (wait_event_interruptible
- (dev->atrq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- return -ERESTARTSYS;
- }
-
- if (test_bit(IS_ATR_VALID, &dev->flags) == 0)
- return -EIO;
-
- /* this one implements blocking IO */
- if (wait_event_interruptible
- (dev->readq,
- ((filp->f_flags & O_NONBLOCK) || (dev->rpos < dev->rlen)))) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- return -ERESTARTSYS;
- }
-
- /* lock io */
- if (wait_event_interruptible
- (dev->ioq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- return -ERESTARTSYS;
- }
-
- rc = 0;
- dev->flags0 = inb(REG_FLAGS0(iobase));
- if ((dev->flags0 & 1) == 0 /* no smartcard inserted */
- || dev->flags0 == 0xff) { /* no cardman inserted */
- clear_bit(IS_ATR_VALID, &dev->flags);
- if (dev->flags0 & 1) {
- set_bit(IS_CMM_ABSENT, &dev->flags);
- rc = -ENODEV;
- } else {
- rc = -EIO;
- }
- goto release_io;
- }
-
- DEBUGP(4, dev, "begin read answer\n");
- j = min(count, (size_t)(dev->rlen - dev->rpos));
- k = dev->rpos;
- if (k + j > 255)
- j = 256 - k;
- DEBUGP(4, dev, "read1 j=%d\n", j);
- for (i = 0; i < j; i++) {
- xoutb(k++, REG_BUF_ADDR(iobase));
- dev->rbuf[i] = xinb(REG_BUF_DATA(iobase));
- }
- j = min(count, (size_t)(dev->rlen - dev->rpos));
- if (k + j > 255) {
- DEBUGP(4, dev, "read2 j=%d\n", j);
- dev->flags1 |= 0x10; /* MSB buf addr set */
- xoutb(dev->flags1, REG_FLAGS1(iobase));
- for (; i < j; i++) {
- xoutb(k++, REG_BUF_ADDR(iobase));
- dev->rbuf[i] = xinb(REG_BUF_DATA(iobase));
- }
- }
-
- if (dev->proto == 0 && count > dev->rlen - dev->rpos && i) {
- DEBUGP(4, dev, "T=0 and count > buffer\n");
- dev->rbuf[i] = dev->rbuf[i - 1];
- dev->rbuf[i - 1] = dev->procbyte;
- j++;
- }
- count = j;
-
- dev->rpos = dev->rlen + 1;
-
- /* Clear T1Active */
- DEBUGP(4, dev, "Clear T1Active\n");
- dev->flags1 &= 0xdf;
- xoutb(dev->flags1, REG_FLAGS1(iobase));
-
- xoutb(0, REG_FLAGS1(iobase)); /* clear detectCMM */
- /* last check before exit */
- if (!io_detect_cm4000(iobase, dev)) {
- rc = -ENODEV;
- goto release_io;
- }
-
- if (test_bit(IS_INVREV, &dev->flags) && count > 0)
- str_invert_revert(dev->rbuf, count);
-
- if (copy_to_user(buf, dev->rbuf, count))
- rc = -EFAULT;
-
-release_io:
- clear_bit(LOCK_IO, &dev->flags);
- wake_up_interruptible(&dev->ioq);
-
- DEBUGP(2, dev, "<- cmm_read returns: rc = %zi\n",
- (rc < 0 ? rc : count));
- return rc < 0 ? rc : count;
-}
-
-static ssize_t cmm_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct cm4000_dev *dev = filp->private_data;
- unsigned int iobase = dev->p_dev->resource[0]->start;
- unsigned short s;
- unsigned char infolen;
- unsigned char sendT0;
- unsigned short nsend;
- unsigned short nr;
- ssize_t rc;
- int i;
-
- DEBUGP(2, dev, "-> cmm_write(%s,%d)\n", current->comm, current->pid);
-
- if (count == 0) /* according to manpage */
- return 0;
-
- if (dev->proto == 0 && count < 4) {
- /* T0 must have at least 4 bytes */
- DEBUGP(4, dev, "T0 short write\n");
- return -EIO;
- }
-
- nr = count & 0x1ff; /* max bytes to write */
-
- sendT0 = dev->proto ? 0 : nr > 5 ? 0x08 : 0;
-
- if (!pcmcia_dev_present(dev->p_dev) || /* device removed */
- test_bit(IS_CMM_ABSENT, &dev->flags))
- return -ENODEV;
-
- if (test_bit(IS_BAD_CSUM, &dev->flags)) {
- DEBUGP(4, dev, "bad csum\n");
- return -EIO;
- }
-
- /*
- * wait for atr to become valid.
- * note: it is important to lock this code. if we dont, the monitor
- * could be run between test_bit and the call to sleep on the
- * atr-queue. if *then* the monitor detects atr valid, it will wake up
- * any process on the atr-queue, *but* since we have been interrupted,
- * we do not yet sleep on this queue. this would result in a missed
- * wake_up and the calling process would sleep forever (until
- * interrupted). also, do *not* restore_flags before sleep_on, because
- * this could result in the same situation!
- */
- if (wait_event_interruptible
- (dev->atrq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- return -ERESTARTSYS;
- }
-
- if (test_bit(IS_ATR_VALID, &dev->flags) == 0) { /* invalid atr */
- DEBUGP(4, dev, "invalid ATR\n");
- return -EIO;
- }
-
- /* lock io */
- if (wait_event_interruptible
- (dev->ioq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- return -ERESTARTSYS;
- }
-
- if (copy_from_user(dev->sbuf, buf, ((count > 512) ? 512 : count)))
- return -EFAULT;
-
- rc = 0;
- dev->flags0 = inb(REG_FLAGS0(iobase));
- if ((dev->flags0 & 1) == 0 /* no smartcard inserted */
- || dev->flags0 == 0xff) { /* no cardman inserted */
- clear_bit(IS_ATR_VALID, &dev->flags);
- if (dev->flags0 & 1) {
- set_bit(IS_CMM_ABSENT, &dev->flags);
- rc = -ENODEV;
- } else {
- DEBUGP(4, dev, "IO error\n");
- rc = -EIO;
- }
- goto release_io;
- }
-
- xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */
-
- if (!io_detect_cm4000(iobase, dev)) {
- rc = -ENODEV;
- goto release_io;
- }
-
- /* reflect T=0 send/read mode in flags1 */
- dev->flags1 |= (sendT0);
-
- set_cardparameter(dev);
-
- /* dummy read, reset flag procedure received */
- inb(REG_FLAGS1(iobase));
-
- dev->flags1 = 0x20 /* T_Active */
- | (sendT0)
- | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)/* inverse parity */
- | (((dev->baudv - 1) & 0x0100) >> 8); /* MSB-Baud */
- DEBUGP(1, dev, "set dev->flags1 = 0x%.2x\n", dev->flags1);
- xoutb(dev->flags1, REG_FLAGS1(iobase));
-
- /* xmit data */
- DEBUGP(4, dev, "Xmit data\n");
- for (i = 0; i < nr; i++) {
- if (i >= 256) {
- dev->flags1 = 0x20 /* T_Active */
- | (sendT0) /* SendT0 */
- /* inverse parity: */
- | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)
- | (((dev->baudv - 1) & 0x0100) >> 8) /* MSB-Baud */
- | 0x10; /* set address high */
- DEBUGP(4, dev, "dev->flags = 0x%.2x - set address "
- "high\n", dev->flags1);
- xoutb(dev->flags1, REG_FLAGS1(iobase));
- }
- if (test_bit(IS_INVREV, &dev->flags)) {
- DEBUGP(4, dev, "Apply inverse convention for 0x%.2x "
- "-> 0x%.2x\n", (unsigned char)dev->sbuf[i],
- invert_revert(dev->sbuf[i]));
- xoutb(i, REG_BUF_ADDR(iobase));
- xoutb(invert_revert(dev->sbuf[i]),
- REG_BUF_DATA(iobase));
- } else {
- xoutb(i, REG_BUF_ADDR(iobase));
- xoutb(dev->sbuf[i], REG_BUF_DATA(iobase));
- }
- }
- DEBUGP(4, dev, "Xmit done\n");
-
- if (dev->proto == 0) {
- /* T=0 proto: 0 byte reply */
- if (nr == 4) {
- DEBUGP(4, dev, "T=0 assumes 0 byte reply\n");
- xoutb(i, REG_BUF_ADDR(iobase));
- if (test_bit(IS_INVREV, &dev->flags))
- xoutb(0xff, REG_BUF_DATA(iobase));
- else
- xoutb(0x00, REG_BUF_DATA(iobase));
- }
-
- /* numSendBytes */
- if (sendT0)
- nsend = nr;
- else {
- if (nr == 4)
- nsend = 5;
- else {
- nsend = 5 + (unsigned char)dev->sbuf[4];
- if (dev->sbuf[4] == 0)
- nsend += 0x100;
- }
- }
- } else
- nsend = nr;
-
- /* T0: output procedure byte */
- if (test_bit(IS_INVREV, &dev->flags)) {
- DEBUGP(4, dev, "T=0 set Procedure byte (inverse-reverse) "
- "0x%.2x\n", invert_revert(dev->sbuf[1]));
- xoutb(invert_revert(dev->sbuf[1]), REG_NUM_BYTES(iobase));
- } else {
- DEBUGP(4, dev, "T=0 set Procedure byte 0x%.2x\n", dev->sbuf[1]);
- xoutb(dev->sbuf[1], REG_NUM_BYTES(iobase));
- }
-
- DEBUGP(1, dev, "set NumSendBytes = 0x%.2x\n",
- (unsigned char)(nsend & 0xff));
- xoutb((unsigned char)(nsend & 0xff), REG_NUM_SEND(iobase));
-
- DEBUGP(1, dev, "Trigger CARDMAN CONTROLLER (0x%.2x)\n",
- 0x40 /* SM_Active */
- | (dev->flags0 & 2 ? 0 : 4) /* power on if needed */
- |(dev->proto ? 0x10 : 0x08) /* T=1/T=0 */
- |(nsend & 0x100) >> 8 /* MSB numSendBytes */ );
- xoutb(0x40 /* SM_Active */
- | (dev->flags0 & 2 ? 0 : 4) /* power on if needed */
- |(dev->proto ? 0x10 : 0x08) /* T=1/T=0 */
- |(nsend & 0x100) >> 8, /* MSB numSendBytes */
- REG_FLAGS0(iobase));
-
- /* wait for xmit done */
- if (dev->proto == 1) {
- DEBUGP(4, dev, "Wait for xmit done\n");
- for (i = 0; i < 1000; i++) {
- if (inb(REG_FLAGS0(iobase)) & 0x08)
- break;
- msleep_interruptible(10);
- }
- if (i == 1000) {
- DEBUGP(4, dev, "timeout waiting for xmit done\n");
- rc = -EIO;
- goto release_io;
- }
- }
-
- /* T=1: wait for infoLen */
-
- infolen = 0;
- if (dev->proto) {
- /* wait until infoLen is valid */
- for (i = 0; i < 6000; i++) { /* max waiting time of 1 min */
- io_read_num_rec_bytes(iobase, &s);
- if (s >= 3) {
- infolen = inb(REG_FLAGS1(iobase));
- DEBUGP(4, dev, "infolen=%d\n", infolen);
- break;
- }
- msleep_interruptible(10);
- }
- if (i == 6000) {
- DEBUGP(4, dev, "timeout waiting for infoLen\n");
- rc = -EIO;
- goto release_io;
- }
- } else
- clear_bit(IS_PROCBYTE_PRESENT, &dev->flags);
-
- /* numRecBytes | bit9 of numRecytes */
- io_read_num_rec_bytes(iobase, &dev->rlen);
- for (i = 0; i < 600; i++) { /* max waiting time of 2 sec */
- if (dev->proto) {
- if (dev->rlen >= infolen + 4)
- break;
- }
- msleep_interruptible(10);
- /* numRecBytes | bit9 of numRecytes */
- io_read_num_rec_bytes(iobase, &s);
- if (s > dev->rlen) {
- DEBUGP(1, dev, "NumRecBytes inc (reset timeout)\n");
- i = 0; /* reset timeout */
- dev->rlen = s;
- }
- /* T=0: we are done when numRecBytes doesn't
- * increment any more and NoProcedureByte
- * is set and numRecBytes == bytes sent + 6
- * (header bytes + data + 1 for sw2)
- * except when the card replies an error
- * which means, no data will be sent back.
- */
- else if (dev->proto == 0) {
- if ((inb(REG_BUF_ADDR(iobase)) & 0x80)) {
- /* no procedure byte received since last read */
- DEBUGP(1, dev, "NoProcedure byte set\n");
- /* i=0; */
- } else {
- /* procedure byte received since last read */
- DEBUGP(1, dev, "NoProcedure byte unset "
- "(reset timeout)\n");
- dev->procbyte = inb(REG_FLAGS1(iobase));
- DEBUGP(1, dev, "Read procedure byte 0x%.2x\n",
- dev->procbyte);
- i = 0; /* resettimeout */
- }
- if (inb(REG_FLAGS0(iobase)) & 0x08) {
- DEBUGP(1, dev, "T0Done flag (read reply)\n");
- break;
- }
- }
- if (dev->proto)
- infolen = inb(REG_FLAGS1(iobase));
- }
- if (i == 600) {
- DEBUGP(1, dev, "timeout waiting for numRecBytes\n");
- rc = -EIO;
- goto release_io;
- } else {
- if (dev->proto == 0) {
- DEBUGP(1, dev, "Wait for T0Done bit to be set\n");
- for (i = 0; i < 1000; i++) {
- if (inb(REG_FLAGS0(iobase)) & 0x08)
- break;
- msleep_interruptible(10);
- }
- if (i == 1000) {
- DEBUGP(1, dev, "timeout waiting for T0Done\n");
- rc = -EIO;
- goto release_io;
- }
-
- dev->procbyte = inb(REG_FLAGS1(iobase));
- DEBUGP(4, dev, "Read procedure byte 0x%.2x\n",
- dev->procbyte);
-
- io_read_num_rec_bytes(iobase, &dev->rlen);
- DEBUGP(4, dev, "Read NumRecBytes = %i\n", dev->rlen);
-
- }
- }
- /* T=1: read offset=zero, T=0: read offset=after challenge */
- dev->rpos = dev->proto ? 0 : nr == 4 ? 5 : nr > dev->rlen ? 5 : nr;
- DEBUGP(4, dev, "dev->rlen = %i, dev->rpos = %i, nr = %i\n",
- dev->rlen, dev->rpos, nr);
-
-release_io:
- DEBUGP(4, dev, "Reset SM\n");
- xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */
-
- if (rc < 0) {
- DEBUGP(4, dev, "Write failed but clear T_Active\n");
- dev->flags1 &= 0xdf;
- xoutb(dev->flags1, REG_FLAGS1(iobase));
- }
-
- clear_bit(LOCK_IO, &dev->flags);
- wake_up_interruptible(&dev->ioq);
- wake_up_interruptible(&dev->readq); /* tell read we have data */
-
- /* ITSEC E2: clear write buffer */
- memset((char *)dev->sbuf, 0, 512);
-
- /* return error or actually written bytes */
- DEBUGP(2, dev, "<- cmm_write\n");
- return rc < 0 ? rc : nr;
-}
-
-static void start_monitor(struct cm4000_dev *dev)
-{
- DEBUGP(3, dev, "-> start_monitor\n");
- if (!dev->monitor_running) {
- DEBUGP(5, dev, "create, init and add timer\n");
- timer_setup(&dev->timer, monitor_card, 0);
- dev->monitor_running = 1;
- mod_timer(&dev->timer, jiffies);
- } else
- DEBUGP(5, dev, "monitor already running\n");
- DEBUGP(3, dev, "<- start_monitor\n");
-}
-
-static void stop_monitor(struct cm4000_dev *dev)
-{
- DEBUGP(3, dev, "-> stop_monitor\n");
- if (dev->monitor_running) {
- DEBUGP(5, dev, "stopping monitor\n");
- terminate_monitor(dev);
- /* reset monitor SM */
- clear_bit(IS_ATR_VALID, &dev->flags);
- clear_bit(IS_ATR_PRESENT, &dev->flags);
- } else
- DEBUGP(5, dev, "monitor already stopped\n");
- DEBUGP(3, dev, "<- stop_monitor\n");
-}
-
-static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct cm4000_dev *dev = filp->private_data;
- unsigned int iobase = dev->p_dev->resource[0]->start;
- struct inode *inode = file_inode(filp);
- struct pcmcia_device *link;
- int rc;
- void __user *argp = (void __user *)arg;
-#ifdef CM4000_DEBUG
- char *ioctl_names[CM_IOC_MAXNR + 1] = {
- [_IOC_NR(CM_IOCGSTATUS)] "CM_IOCGSTATUS",
- [_IOC_NR(CM_IOCGATR)] "CM_IOCGATR",
- [_IOC_NR(CM_IOCARDOFF)] "CM_IOCARDOFF",
- [_IOC_NR(CM_IOCSPTS)] "CM_IOCSPTS",
- [_IOC_NR(CM_IOSDBGLVL)] "CM4000_DBGLVL",
- };
- DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode),
- iminor(inode), ioctl_names[_IOC_NR(cmd)]);
-#endif
-
- mutex_lock(&cmm_mutex);
- rc = -ENODEV;
- link = dev_table[iminor(inode)];
- if (!pcmcia_dev_present(link)) {
- DEBUGP(4, dev, "DEV_OK false\n");
- goto out;
- }
-
- if (test_bit(IS_CMM_ABSENT, &dev->flags)) {
- DEBUGP(4, dev, "CMM_ABSENT flag set\n");
- goto out;
- }
- rc = -EINVAL;
-
- if (_IOC_TYPE(cmd) != CM_IOC_MAGIC) {
- DEBUGP(4, dev, "ioctype mismatch\n");
- goto out;
- }
- if (_IOC_NR(cmd) > CM_IOC_MAXNR) {
- DEBUGP(4, dev, "iocnr mismatch\n");
- goto out;
- }
- rc = 0;
-
- switch (cmd) {
- case CM_IOCGSTATUS:
- DEBUGP(4, dev, " ... in CM_IOCGSTATUS\n");
- {
- int status;
-
- /* clear other bits, but leave inserted & powered as
- * they are */
- status = dev->flags0 & 3;
- if (test_bit(IS_ATR_PRESENT, &dev->flags))
- status |= CM_ATR_PRESENT;
- if (test_bit(IS_ATR_VALID, &dev->flags))
- status |= CM_ATR_VALID;
- if (test_bit(IS_CMM_ABSENT, &dev->flags))
- status |= CM_NO_READER;
- if (test_bit(IS_BAD_CARD, &dev->flags))
- status |= CM_BAD_CARD;
- if (copy_to_user(argp, &status, sizeof(int)))
- rc = -EFAULT;
- }
- break;
- case CM_IOCGATR:
- DEBUGP(4, dev, "... in CM_IOCGATR\n");
- {
- struct atreq __user *atreq = argp;
- int tmp;
- /* allow nonblocking io and being interrupted */
- if (wait_event_interruptible
- (dev->atrq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
- != 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- rc = -EAGAIN;
- else
- rc = -ERESTARTSYS;
- break;
- }
-
- rc = -EFAULT;
- if (test_bit(IS_ATR_VALID, &dev->flags) == 0) {
- tmp = -1;
- if (copy_to_user(&(atreq->atr_len), &tmp,
- sizeof(int)))
- break;
- } else {
- if (copy_to_user(atreq->atr, dev->atr,
- dev->atr_len))
- break;
-
- tmp = dev->atr_len;
- if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int)))
- break;
- }
- rc = 0;
- break;
- }
- case CM_IOCARDOFF:
-
-#ifdef CM4000_DEBUG
- DEBUGP(4, dev, "... in CM_IOCARDOFF\n");
- if (dev->flags0 & 0x01) {
- DEBUGP(4, dev, " Card inserted\n");
- } else {
- DEBUGP(2, dev, " No card inserted\n");
- }
- if (dev->flags0 & 0x02) {
- DEBUGP(4, dev, " Card powered\n");
- } else {
- DEBUGP(2, dev, " Card not powered\n");
- }
-#endif
-
- /* is a card inserted and powered? */
- if ((dev->flags0 & 0x01) && (dev->flags0 & 0x02)) {
-
- /* get IO lock */
- if (wait_event_interruptible
- (dev->ioq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
- == 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- rc = -EAGAIN;
- else
- rc = -ERESTARTSYS;
- break;
- }
- /* Set Flags0 = 0x42 */
- DEBUGP(4, dev, "Set Flags0=0x42 \n");
- xoutb(0x42, REG_FLAGS0(iobase));
- clear_bit(IS_ATR_PRESENT, &dev->flags);
- clear_bit(IS_ATR_VALID, &dev->flags);
- dev->mstate = M_CARDOFF;
- clear_bit(LOCK_IO, &dev->flags);
- if (wait_event_interruptible
- (dev->atrq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_bit(IS_ATR_VALID, (void *)&dev->flags) !=
- 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- rc = -EAGAIN;
- else
- rc = -ERESTARTSYS;
- break;
- }
- }
- /* release lock */
- clear_bit(LOCK_IO, &dev->flags);
- wake_up_interruptible(&dev->ioq);
-
- rc = 0;
- break;
- case CM_IOCSPTS:
- {
- struct ptsreq krnptsreq;
-
- if (copy_from_user(&krnptsreq, argp,
- sizeof(struct ptsreq))) {
- rc = -EFAULT;
- break;
- }
-
- rc = 0;
- DEBUGP(4, dev, "... in CM_IOCSPTS\n");
- /* wait for ATR to get valid */
- if (wait_event_interruptible
- (dev->atrq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
- != 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- rc = -EAGAIN;
- else
- rc = -ERESTARTSYS;
- break;
- }
- /* get IO lock */
- if (wait_event_interruptible
- (dev->ioq,
- ((filp->f_flags & O_NONBLOCK)
- || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
- == 0)))) {
- if (filp->f_flags & O_NONBLOCK)
- rc = -EAGAIN;
- else
- rc = -ERESTARTSYS;
- break;
- }
-
- if ((rc = set_protocol(dev, &krnptsreq)) != 0) {
- /* auto power_on again */
- dev->mstate = M_FETCH_ATR;
- clear_bit(IS_ATR_VALID, &dev->flags);
- }
- /* release lock */
- clear_bit(LOCK_IO, &dev->flags);
- wake_up_interruptible(&dev->ioq);
-
- }
- break;
-#ifdef CM4000_DEBUG
- case CM_IOSDBGLVL:
- rc = -ENOTTY;
- break;
-#endif
- default:
- DEBUGP(4, dev, "... in default (unknown IOCTL code)\n");
- rc = -ENOTTY;
- }
-out:
- mutex_unlock(&cmm_mutex);
- return rc;
-}
-
-static int cmm_open(struct inode *inode, struct file *filp)
-{
- struct cm4000_dev *dev;
- struct pcmcia_device *link;
- int minor = iminor(inode);
- int ret;
-
- if (minor >= CM4000_MAX_DEV)
- return -ENODEV;
-
- mutex_lock(&cmm_mutex);
- link = dev_table[minor];
- if (link == NULL || !pcmcia_dev_present(link)) {
- ret = -ENODEV;
- goto out;
- }
-
- if (link->open) {
- ret = -EBUSY;
- goto out;
- }
-
- dev = link->priv;
- filp->private_data = dev;
-
- DEBUGP(2, dev, "-> cmm_open(device=%d.%d process=%s,%d)\n",
- imajor(inode), minor, current->comm, current->pid);
-
- /* init device variables, they may be "polluted" after close
- * or, the device may never have been closed (i.e. open failed)
- */
-
- ZERO_DEV(dev);
-
- /* opening will always block since the
- * monitor will be started by open, which
- * means we have to wait for ATR becoming
- * valid = block until valid (or card
- * inserted)
- */
- if (filp->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- goto out;
- }
-
- dev->mdelay = T_50MSEC;
-
- /* start monitoring the cardstatus */
- start_monitor(dev);
-
- link->open = 1; /* only one open per device */
-
- DEBUGP(2, dev, "<- cmm_open\n");
- ret = stream_open(inode, filp);
-out:
- mutex_unlock(&cmm_mutex);
- return ret;
-}
-
-static int cmm_close(struct inode *inode, struct file *filp)
-{
- struct cm4000_dev *dev;
- struct pcmcia_device *link;
- int minor = iminor(inode);
-
- if (minor >= CM4000_MAX_DEV)
- return -ENODEV;
-
- link = dev_table[minor];
- if (link == NULL)
- return -ENODEV;
-
- dev = link->priv;
-
- DEBUGP(2, dev, "-> cmm_close(maj/min=%d.%d)\n",
- imajor(inode), minor);
-
- stop_monitor(dev);
-
- ZERO_DEV(dev);
-
- link->open = 0; /* only one open per device */
- wake_up(&dev->devq); /* socket removed? */
-
- DEBUGP(2, dev, "cmm_close\n");
- return 0;
-}
-
-static void cmm_cm4000_release(struct pcmcia_device * link)
-{
- struct cm4000_dev *dev = link->priv;
-
- /* dont terminate the monitor, rather rely on
- * close doing that for us.
- */
- DEBUGP(3, dev, "-> cmm_cm4000_release\n");
- while (link->open) {
- printk(KERN_INFO MODULE_NAME ": delaying release until "
- "process has terminated\n");
- /* note: don't interrupt us:
- * close the applications which own
- * the devices _first_ !
- */
- wait_event(dev->devq, (link->open == 0));
- }
- /* dev->devq=NULL; this cannot be zeroed earlier */
- DEBUGP(3, dev, "<- cmm_cm4000_release\n");
- return;
-}
-
-/*==== Interface to PCMCIA Layer =======================================*/
-
-static int cm4000_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- return pcmcia_request_io(p_dev);
-}
-
-static int cm4000_config(struct pcmcia_device * link, int devno)
-{
- link->config_flags |= CONF_AUTO_SET_IO;
-
- /* read the config-tuples */
- if (pcmcia_loop_config(link, cm4000_config_check, NULL))
- goto cs_release;
-
- if (pcmcia_enable_device(link))
- goto cs_release;
-
- return 0;
-
-cs_release:
- cm4000_release(link);
- return -ENODEV;
-}
-
-static int cm4000_suspend(struct pcmcia_device *link)
-{
- struct cm4000_dev *dev;
-
- dev = link->priv;
- stop_monitor(dev);
-
- return 0;
-}
-
-static int cm4000_resume(struct pcmcia_device *link)
-{
- struct cm4000_dev *dev;
-
- dev = link->priv;
- if (link->open)
- start_monitor(dev);
-
- return 0;
-}
-
-static void cm4000_release(struct pcmcia_device *link)
-{
- cmm_cm4000_release(link); /* delay release until device closed */
- pcmcia_disable_device(link);
-}
-
-static int cm4000_probe(struct pcmcia_device *link)
-{
- struct cm4000_dev *dev;
- int i, ret;
-
- for (i = 0; i < CM4000_MAX_DEV; i++)
- if (dev_table[i] == NULL)
- break;
-
- if (i == CM4000_MAX_DEV) {
- printk(KERN_NOTICE MODULE_NAME ": all devices in use\n");
- return -ENODEV;
- }
-
- /* create a new cm4000_cs device */
- dev = kzalloc(sizeof(struct cm4000_dev), GFP_KERNEL);
- if (dev == NULL)
- return -ENOMEM;
-
- dev->p_dev = link;
- link->priv = dev;
- dev_table[i] = link;
-
- init_waitqueue_head(&dev->devq);
- init_waitqueue_head(&dev->ioq);
- init_waitqueue_head(&dev->atrq);
- init_waitqueue_head(&dev->readq);
-
- ret = cm4000_config(link, i);
- if (ret) {
- dev_table[i] = NULL;
- kfree(dev);
- return ret;
- }
-
- device_create(cmm_class, NULL, MKDEV(major, i), NULL, "cmm%d", i);
-
- return 0;
-}
-
-static void cm4000_detach(struct pcmcia_device *link)
-{
- struct cm4000_dev *dev = link->priv;
- int devno;
-
- /* find device */
- for (devno = 0; devno < CM4000_MAX_DEV; devno++)
- if (dev_table[devno] == link)
- break;
- if (devno == CM4000_MAX_DEV)
- return;
-
- stop_monitor(dev);
-
- cm4000_release(link);
-
- dev_table[devno] = NULL;
- kfree(dev);
-
- device_destroy(cmm_class, MKDEV(major, devno));
-
- return;
-}
-
-static const struct file_operations cm4000_fops = {
- .owner = THIS_MODULE,
- .read = cmm_read,
- .write = cmm_write,
- .unlocked_ioctl = cmm_ioctl,
- .open = cmm_open,
- .release= cmm_close,
- .llseek = no_llseek,
-};
-
-static const struct pcmcia_device_id cm4000_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0002),
- PCMCIA_DEVICE_PROD_ID12("CardMan", "4000", 0x2FB368CA, 0xA2BD8C39),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, cm4000_ids);
-
-static struct pcmcia_driver cm4000_driver = {
- .owner = THIS_MODULE,
- .name = "cm4000_cs",
- .probe = cm4000_probe,
- .remove = cm4000_detach,
- .suspend = cm4000_suspend,
- .resume = cm4000_resume,
- .id_table = cm4000_ids,
-};
-
-static int __init cmm_init(void)
-{
- int rc;
-
- cmm_class = class_create(THIS_MODULE, "cardman_4000");
- if (IS_ERR(cmm_class))
- return PTR_ERR(cmm_class);
-
- major = register_chrdev(0, DEVICE_NAME, &cm4000_fops);
- if (major < 0) {
- printk(KERN_WARNING MODULE_NAME
- ": could not get major number\n");
- class_destroy(cmm_class);
- return major;
- }
-
- rc = pcmcia_register_driver(&cm4000_driver);
- if (rc < 0) {
- unregister_chrdev(major, DEVICE_NAME);
- class_destroy(cmm_class);
- return rc;
- }
-
- return 0;
-}
-
-static void __exit cmm_exit(void)
-{
- pcmcia_unregister_driver(&cm4000_driver);
- unregister_chrdev(major, DEVICE_NAME);
- class_destroy(cmm_class);
-};
-
-module_init(cmm_init);
-module_exit(cmm_exit);
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
deleted file mode 100644
index 827711911da4..000000000000
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
- *
- * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
- *
- * (C) 2005-2006 Harald Welte <laforge@gnumonks.org>
- * - add support for poll()
- * - driver cleanup
- * - add waitqueues
- * - adhere to linux kernel coding style and policies
- * - support 2.6.13 "new style" pcmcia interface
- * - add class interface for udev device creation
- *
- * The device basically is a USB CCID compliant device that has been
- * attached to an I/O-Mapped FIFO.
- *
- * All rights reserved, Dual BSD/GPL Licensed.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/poll.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include "cm4040_cs.h"
-
-
-#define reader_to_dev(x) (&x->p_dev->dev)
-
-/* n (debug level) is ignored */
-/* additional debug output may be enabled by re-compiling with
- * CM4040_DEBUG set */
-/* #define CM4040_DEBUG */
-#define DEBUGP(n, rdr, x, args...) do { \
- dev_dbg(reader_to_dev(rdr), "%s:" x, \
- __func__ , ## args); \
- } while (0)
-
-static DEFINE_MUTEX(cm4040_mutex);
-
-#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ)
-#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ)
-#define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ)
-#define READ_WRITE_BUFFER_SIZE 512
-#define POLL_LOOP_COUNT 1000
-
-/* how often to poll for fifo status change */
-#define POLL_PERIOD msecs_to_jiffies(10)
-
-static void reader_release(struct pcmcia_device *link);
-
-static int major;
-static struct class *cmx_class;
-
-#define BS_READABLE 0x01
-#define BS_WRITABLE 0x02
-
-struct reader_dev {
- struct pcmcia_device *p_dev;
- wait_queue_head_t devq;
- wait_queue_head_t poll_wait;
- wait_queue_head_t read_wait;
- wait_queue_head_t write_wait;
- unsigned long buffer_status;
- unsigned long timeout;
- unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
- unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
- struct timer_list poll_timer;
-};
-
-static struct pcmcia_device *dev_table[CM_MAX_DEV];
-
-#ifndef CM4040_DEBUG
-#define xoutb outb
-#define xinb inb
-#else
-static inline void xoutb(unsigned char val, unsigned short port)
-{
- pr_debug("outb(val=%.2x,port=%.4x)\n", val, port);
- outb(val, port);
-}
-
-static inline unsigned char xinb(unsigned short port)
-{
- unsigned char val;
-
- val = inb(port);
- pr_debug("%.2x=inb(%.4x)\n", val, port);
- return val;
-}
-#endif
-
-/* poll the device fifo status register. not to be confused with
- * the poll syscall. */
-static void cm4040_do_poll(struct timer_list *t)
-{
- struct reader_dev *dev = from_timer(dev, t, poll_timer);
- unsigned int obs = xinb(dev->p_dev->resource[0]->start
- + REG_OFFSET_BUFFER_STATUS);
-
- if ((obs & BSR_BULK_IN_FULL)) {
- set_bit(BS_READABLE, &dev->buffer_status);
- DEBUGP(4, dev, "waking up read_wait\n");
- wake_up_interruptible(&dev->read_wait);
- } else
- clear_bit(BS_READABLE, &dev->buffer_status);
-
- if (!(obs & BSR_BULK_OUT_FULL)) {
- set_bit(BS_WRITABLE, &dev->buffer_status);
- DEBUGP(4, dev, "waking up write_wait\n");
- wake_up_interruptible(&dev->write_wait);
- } else
- clear_bit(BS_WRITABLE, &dev->buffer_status);
-
- if (dev->buffer_status)
- wake_up_interruptible(&dev->poll_wait);
-
- mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
-}
-
-static void cm4040_stop_poll(struct reader_dev *dev)
-{
- del_timer_sync(&dev->poll_timer);
-}
-
-static int wait_for_bulk_out_ready(struct reader_dev *dev)
-{
- int i, rc;
- int iobase = dev->p_dev->resource[0]->start;
-
- for (i = 0; i < POLL_LOOP_COUNT; i++) {
- if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
- & BSR_BULK_OUT_FULL) == 0) {
- DEBUGP(4, dev, "BulkOut empty (i=%d)\n", i);
- return 1;
- }
- }
-
- DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
- dev->timeout);
- rc = wait_event_interruptible_timeout(dev->write_wait,
- test_and_clear_bit(BS_WRITABLE,
- &dev->buffer_status),
- dev->timeout);
-
- if (rc > 0)
- DEBUGP(4, dev, "woke up: BulkOut empty\n");
- else if (rc == 0)
- DEBUGP(4, dev, "woke up: BulkOut full, returning 0 :(\n");
- else if (rc < 0)
- DEBUGP(4, dev, "woke up: signal arrived\n");
-
- return rc;
-}
-
-/* Write to Sync Control Register */
-static int write_sync_reg(unsigned char val, struct reader_dev *dev)
-{
- int iobase = dev->p_dev->resource[0]->start;
- int rc;
-
- rc = wait_for_bulk_out_ready(dev);
- if (rc <= 0)
- return rc;
-
- xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL);
- rc = wait_for_bulk_out_ready(dev);
- if (rc <= 0)
- return rc;
-
- return 1;
-}
-
-static int wait_for_bulk_in_ready(struct reader_dev *dev)
-{
- int i, rc;
- int iobase = dev->p_dev->resource[0]->start;
-
- for (i = 0; i < POLL_LOOP_COUNT; i++) {
- if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
- & BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
- DEBUGP(3, dev, "BulkIn full (i=%d)\n", i);
- return 1;
- }
- }
-
- DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
- dev->timeout);
- rc = wait_event_interruptible_timeout(dev->read_wait,
- test_and_clear_bit(BS_READABLE,
- &dev->buffer_status),
- dev->timeout);
- if (rc > 0)
- DEBUGP(4, dev, "woke up: BulkIn full\n");
- else if (rc == 0)
- DEBUGP(4, dev, "woke up: BulkIn not full, returning 0 :(\n");
- else if (rc < 0)
- DEBUGP(4, dev, "woke up: signal arrived\n");
-
- return rc;
-}
-
-static ssize_t cm4040_read(struct file *filp, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct reader_dev *dev = filp->private_data;
- int iobase = dev->p_dev->resource[0]->start;
- size_t bytes_to_read;
- unsigned long i;
- size_t min_bytes_to_read;
- int rc;
-
- DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);
-
- if (count == 0)
- return 0;
-
- if (count < 10)
- return -EFAULT;
-
- if (filp->f_flags & O_NONBLOCK) {
- DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
- DEBUGP(2, dev, "<- cm4040_read (failure)\n");
- return -EAGAIN;
- }
-
- if (!pcmcia_dev_present(dev->p_dev))
- return -ENODEV;
-
- for (i = 0; i < 5; i++) {
- rc = wait_for_bulk_in_ready(dev);
- if (rc <= 0) {
- DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
- DEBUGP(2, dev, "<- cm4040_read (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- return -EIO;
- }
- dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN);
-#ifdef CM4040_DEBUG
- pr_debug("%lu:%2x ", i, dev->r_buf[i]);
- }
- pr_debug("\n");
-#else
- }
-#endif
-
- bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]);
-
- DEBUGP(6, dev, "BytesToRead=%zu\n", bytes_to_read);
-
- min_bytes_to_read = min(count, bytes_to_read + 5);
- min_bytes_to_read = min_t(size_t, min_bytes_to_read, READ_WRITE_BUFFER_SIZE);
-
- DEBUGP(6, dev, "Min=%zu\n", min_bytes_to_read);
-
- for (i = 0; i < (min_bytes_to_read-5); i++) {
- rc = wait_for_bulk_in_ready(dev);
- if (rc <= 0) {
- DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
- DEBUGP(2, dev, "<- cm4040_read (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- return -EIO;
- }
- dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN);
-#ifdef CM4040_DEBUG
- pr_debug("%lu:%2x ", i, dev->r_buf[i]);
- }
- pr_debug("\n");
-#else
- }
-#endif
-
- *ppos = min_bytes_to_read;
- if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
- return -EFAULT;
-
- rc = wait_for_bulk_in_ready(dev);
- if (rc <= 0) {
- DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
- DEBUGP(2, dev, "<- cm4040_read (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- return -EIO;
- }
-
- rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev);
- if (rc <= 0) {
- DEBUGP(5, dev, "write_sync_reg c=%.2x\n", rc);
- DEBUGP(2, dev, "<- cm4040_read (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- else
- return -EIO;
- }
-
- xinb(iobase + REG_OFFSET_BULK_IN);
-
- DEBUGP(2, dev, "<- cm4040_read (successfully)\n");
- return min_bytes_to_read;
-}
-
-static ssize_t cm4040_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct reader_dev *dev = filp->private_data;
- int iobase = dev->p_dev->resource[0]->start;
- ssize_t rc;
- int i;
- unsigned int bytes_to_write;
-
- DEBUGP(2, dev, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);
-
- if (count == 0) {
- DEBUGP(2, dev, "<- cm4040_write empty read (successfully)\n");
- return 0;
- }
-
- if ((count < 5) || (count > READ_WRITE_BUFFER_SIZE)) {
- DEBUGP(2, dev, "<- cm4040_write buffersize=%zd < 5\n", count);
- return -EIO;
- }
-
- if (filp->f_flags & O_NONBLOCK) {
- DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
- DEBUGP(4, dev, "<- cm4040_write (failure)\n");
- return -EAGAIN;
- }
-
- if (!pcmcia_dev_present(dev->p_dev))
- return -ENODEV;
-
- bytes_to_write = count;
- if (copy_from_user(dev->s_buf, buf, bytes_to_write))
- return -EFAULT;
-
- switch (dev->s_buf[0]) {
- case CMD_PC_TO_RDR_XFRBLOCK:
- case CMD_PC_TO_RDR_SECURE:
- case CMD_PC_TO_RDR_TEST_SECURE:
- case CMD_PC_TO_RDR_OK_SECURE:
- dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
- break;
-
- case CMD_PC_TO_RDR_ICCPOWERON:
- dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
- break;
-
- case CMD_PC_TO_RDR_GETSLOTSTATUS:
- case CMD_PC_TO_RDR_ICCPOWEROFF:
- case CMD_PC_TO_RDR_GETPARAMETERS:
- case CMD_PC_TO_RDR_RESETPARAMETERS:
- case CMD_PC_TO_RDR_SETPARAMETERS:
- case CMD_PC_TO_RDR_ESCAPE:
- case CMD_PC_TO_RDR_ICCCLOCK:
- default:
- dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
- break;
- }
-
- rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
- if (rc <= 0) {
- DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
- DEBUGP(2, dev, "<- cm4040_write (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- else
- return -EIO;
- }
-
- DEBUGP(4, dev, "start \n");
-
- for (i = 0; i < bytes_to_write; i++) {
- rc = wait_for_bulk_out_ready(dev);
- if (rc <= 0) {
- DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n",
- rc);
- DEBUGP(2, dev, "<- cm4040_write (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- else
- return -EIO;
- }
-
- xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
- }
- DEBUGP(4, dev, "end\n");
-
- rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
-
- if (rc <= 0) {
- DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
- DEBUGP(2, dev, "<- cm4040_write (failed)\n");
- if (rc == -ERESTARTSYS)
- return rc;
- else
- return -EIO;
- }
-
- DEBUGP(2, dev, "<- cm4040_write (successfully)\n");
- return count;
-}
-
-static __poll_t cm4040_poll(struct file *filp, poll_table *wait)
-{
- struct reader_dev *dev = filp->private_data;
- __poll_t mask = 0;
-
- poll_wait(filp, &dev->poll_wait, wait);
-
- if (test_and_clear_bit(BS_READABLE, &dev->buffer_status))
- mask |= EPOLLIN | EPOLLRDNORM;
- if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status))
- mask |= EPOLLOUT | EPOLLWRNORM;
-
- DEBUGP(2, dev, "<- cm4040_poll(%u)\n", mask);
-
- return mask;
-}
-
-static int cm4040_open(struct inode *inode, struct file *filp)
-{
- struct reader_dev *dev;
- struct pcmcia_device *link;
- int minor = iminor(inode);
- int ret;
-
- if (minor >= CM_MAX_DEV)
- return -ENODEV;
-
- mutex_lock(&cm4040_mutex);
- link = dev_table[minor];
- if (link == NULL || !pcmcia_dev_present(link)) {
- ret = -ENODEV;
- goto out;
- }
-
- if (link->open) {
- ret = -EBUSY;
- goto out;
- }
-
- dev = link->priv;
- filp->private_data = dev;
-
- if (filp->f_flags & O_NONBLOCK) {
- DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
- ret = -EAGAIN;
- goto out;
- }
-
- link->open = 1;
-
- mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
-
- DEBUGP(2, dev, "<- cm4040_open (successfully)\n");
- ret = nonseekable_open(inode, filp);
-out:
- mutex_unlock(&cm4040_mutex);
- return ret;
-}
-
-static int cm4040_close(struct inode *inode, struct file *filp)
-{
- struct reader_dev *dev = filp->private_data;
- struct pcmcia_device *link;
- int minor = iminor(inode);
-
- DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode),
- iminor(inode));
-
- if (minor >= CM_MAX_DEV)
- return -ENODEV;
-
- link = dev_table[minor];
- if (link == NULL)
- return -ENODEV;
-
- cm4040_stop_poll(dev);
-
- link->open = 0;
- wake_up(&dev->devq);
-
- DEBUGP(2, dev, "<- cm4040_close\n");
- return 0;
-}
-
-static void cm4040_reader_release(struct pcmcia_device *link)
-{
- struct reader_dev *dev = link->priv;
-
- DEBUGP(3, dev, "-> cm4040_reader_release\n");
- while (link->open) {
- DEBUGP(3, dev, MODULE_NAME ": delaying release "
- "until process has terminated\n");
- wait_event(dev->devq, (link->open == 0));
- }
- DEBUGP(3, dev, "<- cm4040_reader_release\n");
- return;
-}
-
-static int cm4040_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- return pcmcia_request_io(p_dev);
-}
-
-
-static int reader_config(struct pcmcia_device *link, int devno)
-{
- struct reader_dev *dev;
- int fail_rc;
-
- link->config_flags |= CONF_AUTO_SET_IO;
-
- if (pcmcia_loop_config(link, cm4040_config_check, NULL))
- goto cs_release;
-
- fail_rc = pcmcia_enable_device(link);
- if (fail_rc != 0) {
- dev_info(&link->dev, "pcmcia_enable_device failed 0x%x\n",
- fail_rc);
- goto cs_release;
- }
-
- dev = link->priv;
-
- DEBUGP(2, dev, "device " DEVICE_NAME "%d at %pR\n", devno,
- link->resource[0]);
- DEBUGP(2, dev, "<- reader_config (succ)\n");
-
- return 0;
-
-cs_release:
- reader_release(link);
- return -ENODEV;
-}
-
-static void reader_release(struct pcmcia_device *link)
-{
- cm4040_reader_release(link);
- pcmcia_disable_device(link);
-}
-
-static int reader_probe(struct pcmcia_device *link)
-{
- struct reader_dev *dev;
- int i, ret;
-
- for (i = 0; i < CM_MAX_DEV; i++) {
- if (dev_table[i] == NULL)
- break;
- }
-
- if (i == CM_MAX_DEV)
- return -ENODEV;
-
- dev = kzalloc(sizeof(struct reader_dev), GFP_KERNEL);
- if (dev == NULL)
- return -ENOMEM;
-
- dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
- dev->buffer_status = 0;
-
- link->priv = dev;
- dev->p_dev = link;
-
- dev_table[i] = link;
-
- init_waitqueue_head(&dev->devq);
- init_waitqueue_head(&dev->poll_wait);
- init_waitqueue_head(&dev->read_wait);
- init_waitqueue_head(&dev->write_wait);
- timer_setup(&dev->poll_timer, cm4040_do_poll, 0);
-
- ret = reader_config(link, i);
- if (ret) {
- dev_table[i] = NULL;
- kfree(dev);
- return ret;
- }
-
- device_create(cmx_class, NULL, MKDEV(major, i), NULL, "cmx%d", i);
-
- return 0;
-}
-
-static void reader_detach(struct pcmcia_device *link)
-{
- struct reader_dev *dev = link->priv;
- int devno;
-
- /* find device */
- for (devno = 0; devno < CM_MAX_DEV; devno++) {
- if (dev_table[devno] == link)
- break;
- }
- if (devno == CM_MAX_DEV)
- return;
-
- reader_release(link);
-
- dev_table[devno] = NULL;
- kfree(dev);
-
- device_destroy(cmx_class, MKDEV(major, devno));
-
- return;
-}
-
-static const struct file_operations reader_fops = {
- .owner = THIS_MODULE,
- .read = cm4040_read,
- .write = cm4040_write,
- .open = cm4040_open,
- .release = cm4040_close,
- .poll = cm4040_poll,
- .llseek = no_llseek,
-};
-
-static const struct pcmcia_device_id cm4040_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
- PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
- 0xE32CDD8C, 0x8F23318B),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, cm4040_ids);
-
-static struct pcmcia_driver reader_driver = {
- .owner = THIS_MODULE,
- .name = "cm4040_cs",
- .probe = reader_probe,
- .remove = reader_detach,
- .id_table = cm4040_ids,
-};
-
-static int __init cm4040_init(void)
-{
- int rc;
-
- cmx_class = class_create(THIS_MODULE, "cardman_4040");
- if (IS_ERR(cmx_class))
- return PTR_ERR(cmx_class);
-
- major = register_chrdev(0, DEVICE_NAME, &reader_fops);
- if (major < 0) {
- printk(KERN_WARNING MODULE_NAME
- ": could not get major number\n");
- class_destroy(cmx_class);
- return major;
- }
-
- rc = pcmcia_register_driver(&reader_driver);
- if (rc < 0) {
- unregister_chrdev(major, DEVICE_NAME);
- class_destroy(cmx_class);
- return rc;
- }
-
- return 0;
-}
-
-static void __exit cm4040_exit(void)
-{
- pcmcia_unregister_driver(&reader_driver);
- unregister_chrdev(major, DEVICE_NAME);
- class_destroy(cmx_class);
-}
-
-module_init(cm4040_init);
-module_exit(cm4040_exit);
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/char/pcmcia/cm4040_cs.h b/drivers/char/pcmcia/cm4040_cs.h
deleted file mode 100644
index e2ffff995d51..000000000000
--- a/drivers/char/pcmcia/cm4040_cs.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _CM4040_H_
-#define _CM4040_H_
-
-#define CM_MAX_DEV 4
-
-#define DEVICE_NAME "cmx"
-#define MODULE_NAME "cm4040_cs"
-
-#define REG_OFFSET_BULK_OUT 0
-#define REG_OFFSET_BULK_IN 0
-#define REG_OFFSET_BUFFER_STATUS 1
-#define REG_OFFSET_SYNC_CONTROL 2
-
-#define BSR_BULK_IN_FULL 0x02
-#define BSR_BULK_OUT_FULL 0x01
-
-#define SCR_HOST_TO_READER_START 0x80
-#define SCR_ABORT 0x40
-#define SCR_EN_NOTIFY 0x20
-#define SCR_ACK_NOTIFY 0x10
-#define SCR_READER_TO_HOST_DONE 0x08
-#define SCR_HOST_TO_READER_DONE 0x04
-#define SCR_PULSE_INTERRUPT 0x02
-#define SCR_POWER_DOWN 0x01
-
-
-#define CMD_PC_TO_RDR_ICCPOWERON 0x62
-#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
-#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
-#define CMD_PC_TO_RDR_SECURE 0x69
-#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
-#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
-#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
-#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
-#define CMD_PC_TO_RDR_ESCAPE 0x6B
-#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
-#define CMD_PC_TO_RDR_TEST_SECURE 0x74
-#define CMD_PC_TO_RDR_OK_SECURE 0x89
-
-
-#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
-#define CMD_RDR_TO_PC_DATABLOCK 0x80
-#define CMD_RDR_TO_PC_PARAMETERS 0x82
-#define CMD_RDR_TO_PC_ESCAPE 0x83
-#define CMD_RDR_TO_PC_OK_SECURE 0x89
-
-#endif /* _CM4040_H_ */
diff --git a/drivers/char/pcmcia/scr24x_cs.c b/drivers/char/pcmcia/scr24x_cs.c
deleted file mode 100644
index 1bdce08fae3d..000000000000
--- a/drivers/char/pcmcia/scr24x_cs.c
+++ /dev/null
@@ -1,359 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SCR24x PCMCIA Smart Card Reader Driver
- *
- * Copyright (C) 2005-2006 TL Sudheendran
- * Copyright (C) 2016 Lubomir Rintel
- *
- * Derived from "scr24x_v4.2.6_Release.tar.gz" driver by TL Sudheendran.
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/cdev.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-#define CCID_HEADER_SIZE 10
-#define CCID_LENGTH_OFFSET 1
-#define CCID_MAX_LEN 271
-
-#define SCR24X_DATA(n) (1 + n)
-#define SCR24X_CMD_STATUS 7
-#define CMD_START 0x40
-#define CMD_WRITE_BYTE 0x41
-#define CMD_READ_BYTE 0x42
-#define STATUS_BUSY 0x80
-
-struct scr24x_dev {
- struct device *dev;
- struct cdev c_dev;
- unsigned char buf[CCID_MAX_LEN];
- int devno;
- struct mutex lock;
- struct kref refcnt;
- u8 __iomem *regs;
-};
-
-#define SCR24X_DEVS 8
-static DECLARE_BITMAP(scr24x_minors, SCR24X_DEVS);
-
-static struct class *scr24x_class;
-static dev_t scr24x_devt;
-
-static void scr24x_delete(struct kref *kref)
-{
- struct scr24x_dev *dev = container_of(kref, struct scr24x_dev,
- refcnt);
-
- kfree(dev);
-}
-
-static int scr24x_wait_ready(struct scr24x_dev *dev)
-{
- u_char status;
- int timeout = 100;
-
- do {
- status = ioread8(dev->regs + SCR24X_CMD_STATUS);
- if (!(status & STATUS_BUSY))
- return 0;
-
- msleep(20);
- } while (--timeout);
-
- return -EIO;
-}
-
-static int scr24x_open(struct inode *inode, struct file *filp)
-{
- struct scr24x_dev *dev = container_of(inode->i_cdev,
- struct scr24x_dev, c_dev);
-
- kref_get(&dev->refcnt);
- filp->private_data = dev;
-
- return stream_open(inode, filp);
-}
-
-static int scr24x_release(struct inode *inode, struct file *filp)
-{
- struct scr24x_dev *dev = filp->private_data;
-
- /* We must not take the dev->lock here as scr24x_delete()
- * might be called to remove the dev structure altogether.
- * We don't need the lock anyway, since after the reference
- * acquired in probe() is released in remove() the chrdev
- * is already unregistered and noone can possibly acquire
- * a reference via open() anymore. */
- kref_put(&dev->refcnt, scr24x_delete);
- return 0;
-}
-
-static int read_chunk(struct scr24x_dev *dev, size_t offset, size_t limit)
-{
- size_t i, y;
- int ret;
-
- for (i = offset; i < limit; i += 5) {
- iowrite8(CMD_READ_BYTE, dev->regs + SCR24X_CMD_STATUS);
- ret = scr24x_wait_ready(dev);
- if (ret < 0)
- return ret;
-
- for (y = 0; y < 5 && i + y < limit; y++)
- dev->buf[i + y] = ioread8(dev->regs + SCR24X_DATA(y));
- }
-
- return 0;
-}
-
-static ssize_t scr24x_read(struct file *filp, char __user *buf, size_t count,
- loff_t *ppos)
-{
- struct scr24x_dev *dev = filp->private_data;
- int ret;
- int len;
-
- if (count < CCID_HEADER_SIZE)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&dev->lock))
- return -ERESTARTSYS;
-
- if (!dev->dev) {
- ret = -ENODEV;
- goto out;
- }
-
- ret = scr24x_wait_ready(dev);
- if (ret < 0)
- goto out;
- len = CCID_HEADER_SIZE;
- ret = read_chunk(dev, 0, len);
- if (ret < 0)
- goto out;
-
- len += le32_to_cpu(*(__le32 *)(&dev->buf[CCID_LENGTH_OFFSET]));
- if (len > sizeof(dev->buf)) {
- ret = -EIO;
- goto out;
- }
- ret = read_chunk(dev, CCID_HEADER_SIZE, len);
- if (ret < 0)
- goto out;
-
- if (len < count)
- count = len;
-
- if (copy_to_user(buf, dev->buf, count)) {
- ret = -EFAULT;
- goto out;
- }
-
- ret = count;
-out:
- mutex_unlock(&dev->lock);
- return ret;
-}
-
-static ssize_t scr24x_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct scr24x_dev *dev = filp->private_data;
- size_t i, y;
- int ret;
-
- if (mutex_lock_interruptible(&dev->lock))
- return -ERESTARTSYS;
-
- if (!dev->dev) {
- ret = -ENODEV;
- goto out;
- }
-
- if (count > sizeof(dev->buf)) {
- ret = -EINVAL;
- goto out;
- }
-
- if (copy_from_user(dev->buf, buf, count)) {
- ret = -EFAULT;
- goto out;
- }
-
- ret = scr24x_wait_ready(dev);
- if (ret < 0)
- goto out;
-
- iowrite8(CMD_START, dev->regs + SCR24X_CMD_STATUS);
- ret = scr24x_wait_ready(dev);
- if (ret < 0)
- goto out;
-
- for (i = 0; i < count; i += 5) {
- for (y = 0; y < 5 && i + y < count; y++)
- iowrite8(dev->buf[i + y], dev->regs + SCR24X_DATA(y));
-
- iowrite8(CMD_WRITE_BYTE, dev->regs + SCR24X_CMD_STATUS);
- ret = scr24x_wait_ready(dev);
- if (ret < 0)
- goto out;
- }
-
- ret = count;
-out:
- mutex_unlock(&dev->lock);
- return ret;
-}
-
-static const struct file_operations scr24x_fops = {
- .owner = THIS_MODULE,
- .read = scr24x_read,
- .write = scr24x_write,
- .open = scr24x_open,
- .release = scr24x_release,
- .llseek = no_llseek,
-};
-
-static int scr24x_config_check(struct pcmcia_device *link, void *priv_data)
-{
- if (resource_size(link->resource[PCMCIA_IOPORT_0]) != 0x11)
- return -ENODEV;
- return pcmcia_request_io(link);
-}
-
-static int scr24x_probe(struct pcmcia_device *link)
-{
- struct scr24x_dev *dev;
- int ret;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- dev->devno = find_first_zero_bit(scr24x_minors, SCR24X_DEVS);
- if (dev->devno >= SCR24X_DEVS) {
- ret = -EBUSY;
- goto err;
- }
-
- mutex_init(&dev->lock);
- kref_init(&dev->refcnt);
-
- link->priv = dev;
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- ret = pcmcia_loop_config(link, scr24x_config_check, NULL);
- if (ret < 0)
- goto err;
-
- dev->dev = &link->dev;
- dev->regs = devm_ioport_map(&link->dev,
- link->resource[PCMCIA_IOPORT_0]->start,
- resource_size(link->resource[PCMCIA_IOPORT_0]));
- if (!dev->regs) {
- ret = -EIO;
- goto err;
- }
-
- cdev_init(&dev->c_dev, &scr24x_fops);
- dev->c_dev.owner = THIS_MODULE;
- ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1);
- if (ret < 0)
- goto err;
-
- ret = pcmcia_enable_device(link);
- if (ret < 0) {
- pcmcia_disable_device(link);
- goto err;
- }
-
- device_create(scr24x_class, NULL, MKDEV(MAJOR(scr24x_devt), dev->devno),
- NULL, "scr24x%d", dev->devno);
-
- dev_info(&link->dev, "SCR24x Chip Card Interface\n");
- return 0;
-
-err:
- if (dev->devno < SCR24X_DEVS)
- clear_bit(dev->devno, scr24x_minors);
- kfree (dev);
- return ret;
-}
-
-static void scr24x_remove(struct pcmcia_device *link)
-{
- struct scr24x_dev *dev = (struct scr24x_dev *)link->priv;
-
- device_destroy(scr24x_class, MKDEV(MAJOR(scr24x_devt), dev->devno));
- mutex_lock(&dev->lock);
- pcmcia_disable_device(link);
- cdev_del(&dev->c_dev);
- clear_bit(dev->devno, scr24x_minors);
- dev->dev = NULL;
- mutex_unlock(&dev->lock);
-
- kref_put(&dev->refcnt, scr24x_delete);
-}
-
-static const struct pcmcia_device_id scr24x_ids[] = {
- PCMCIA_DEVICE_PROD_ID12("HP", "PC Card Smart Card Reader",
- 0x53cb94f9, 0xbfdf89a5),
- PCMCIA_DEVICE_PROD_ID1("SCR241 PCMCIA", 0x6271efa3),
- PCMCIA_DEVICE_PROD_ID1("SCR243 PCMCIA", 0x2054e8de),
- PCMCIA_DEVICE_PROD_ID1("SCR24x PCMCIA", 0x54a33665),
- PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, scr24x_ids);
-
-static struct pcmcia_driver scr24x_driver = {
- .owner = THIS_MODULE,
- .name = "scr24x_cs",
- .probe = scr24x_probe,
- .remove = scr24x_remove,
- .id_table = scr24x_ids,
-};
-
-static int __init scr24x_init(void)
-{
- int ret;
-
- scr24x_class = class_create(THIS_MODULE, "scr24x");
- if (IS_ERR(scr24x_class))
- return PTR_ERR(scr24x_class);
-
- ret = alloc_chrdev_region(&scr24x_devt, 0, SCR24X_DEVS, "scr24x");
- if (ret < 0) {
- class_destroy(scr24x_class);
- return ret;
- }
-
- ret = pcmcia_register_driver(&scr24x_driver);
- if (ret < 0) {
- unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
- class_destroy(scr24x_class);
- }
-
- return ret;
-}
-
-static void __exit scr24x_exit(void)
-{
- pcmcia_unregister_driver(&scr24x_driver);
- unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
- class_destroy(scr24x_class);
-}
-
-module_init(scr24x_init);
-module_exit(scr24x_exit);
-
-MODULE_AUTHOR("Lubomir Rintel");
-MODULE_DESCRIPTION("SCR24x PCMCIA Smart Card Reader Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
deleted file mode 100644
index 8fc49b038372..000000000000
--- a/drivers/char/pcmcia/synclink_cs.c
+++ /dev/null
@@ -1,4291 +0,0 @@
-/*
- * linux/drivers/char/pcmcia/synclink_cs.c
- *
- * $Id: synclink_cs.c,v 4.34 2005/09/08 13:20:54 paulkf Exp $
- *
- * Device driver for Microgate SyncLink PC Card
- * multiprotocol serial adapter.
- *
- * written by Paul Fulghum for Microgate Corporation
- * paulkf@microgate.com
- *
- * Microgate and SyncLink are trademarks of Microgate Corporation
- *
- * This code is released under the GNU General Public License (GPL)
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
-#if defined(__i386__)
-# define BREAKPOINT() asm(" int $3");
-#else
-# define BREAKPOINT() { }
-#endif
-
-#define MAX_DEVICE_COUNT 4
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/time.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ioctl.h>
-#include <linux/synclink.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <linux/bitops.h>
-#include <asm/types.h>
-#include <linux/termios.h>
-#include <linux/workqueue.h>
-#include <linux/hdlc.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-
-#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_CS_MODULE))
-#define SYNCLINK_GENERIC_HDLC 1
-#else
-#define SYNCLINK_GENERIC_HDLC 0
-#endif
-
-#define GET_USER(error,value,addr) error = get_user(value,addr)
-#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
-#define PUT_USER(error,value,addr) error = put_user(value,addr)
-#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
-
-#include <linux/uaccess.h>
-
-static MGSL_PARAMS default_params = {
- MGSL_MODE_HDLC, /* unsigned long mode */
- 0, /* unsigned char loopback; */
- HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */
- HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */
- 0, /* unsigned long clock_speed; */
- 0xff, /* unsigned char addr_filter; */
- HDLC_CRC_16_CCITT, /* unsigned short crc_type; */
- HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */
- HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */
- 9600, /* unsigned long data_rate; */
- 8, /* unsigned char data_bits; */
- 1, /* unsigned char stop_bits; */
- ASYNC_PARITY_NONE /* unsigned char parity; */
-};
-
-typedef struct {
- int count;
- unsigned char status;
- char data[1];
-} RXBUF;
-
-/* The queue of BH actions to be performed */
-
-#define BH_RECEIVE 1
-#define BH_TRANSMIT 2
-#define BH_STATUS 4
-
-#define IO_PIN_SHUTDOWN_LIMIT 100
-
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-struct _input_signal_events {
- int ri_up;
- int ri_down;
- int dsr_up;
- int dsr_down;
- int dcd_up;
- int dcd_down;
- int cts_up;
- int cts_down;
-};
-
-
-/*
- * Device instance data structure
- */
-
-typedef struct _mgslpc_info {
- struct tty_port port;
- void *if_ptr; /* General purpose pointer (used by SPPP) */
- int magic;
- int line;
-
- struct mgsl_icount icount;
-
- int timeout;
- int x_char; /* xon/xoff character */
- unsigned char read_status_mask;
- unsigned char ignore_status_mask;
-
- unsigned char *tx_buf;
- int tx_put;
- int tx_get;
- int tx_count;
-
- /* circular list of fixed length rx buffers */
-
- unsigned char *rx_buf; /* memory allocated for all rx buffers */
- int rx_buf_total_size; /* size of memory allocated for rx buffers */
- int rx_put; /* index of next empty rx buffer */
- int rx_get; /* index of next full rx buffer */
- int rx_buf_size; /* size in bytes of single rx buffer */
- int rx_buf_count; /* total number of rx buffers */
- int rx_frame_count; /* number of full rx buffers */
-
- wait_queue_head_t status_event_wait_q;
- wait_queue_head_t event_wait_q;
- struct timer_list tx_timer; /* HDLC transmit timeout timer */
- struct _mgslpc_info *next_device; /* device list link */
-
- unsigned short imra_value;
- unsigned short imrb_value;
- unsigned char pim_value;
-
- spinlock_t lock;
- struct work_struct task; /* task structure for scheduling bh */
-
- u32 max_frame_size;
-
- u32 pending_bh;
-
- bool bh_running;
- bool bh_requested;
-
- int dcd_chkcount; /* check counts to prevent */
- int cts_chkcount; /* too many IRQs if a signal */
- int dsr_chkcount; /* is floating */
- int ri_chkcount;
-
- bool rx_enabled;
- bool rx_overflow;
-
- bool tx_enabled;
- bool tx_active;
- bool tx_aborting;
- u32 idle_mode;
-
- int if_mode; /* serial interface selection (RS-232, v.35 etc) */
-
- char device_name[25]; /* device instance name */
-
- unsigned int io_base; /* base I/O address of adapter */
- unsigned int irq_level;
-
- MGSL_PARAMS params; /* communications parameters */
-
- unsigned char serial_signals; /* current serial signal states */
-
- bool irq_occurred; /* for diagnostics use */
- char testing_irq;
- unsigned int init_error; /* startup error (DIAGS) */
-
- char *flag_buf;
- bool drop_rts_on_tx_done;
-
- struct _input_signal_events input_signal_events;
-
- /* PCMCIA support */
- struct pcmcia_device *p_dev;
- int stop;
-
- /* SPPP/Cisco HDLC device parts */
- int netcount;
- spinlock_t netlock;
-
-#if SYNCLINK_GENERIC_HDLC
- struct net_device *netdev;
-#endif
-
-} MGSLPC_INFO;
-
-#define MGSLPC_MAGIC 0x5402
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define TXBUFSIZE 4096
-
-
-#define CHA 0x00 /* channel A offset */
-#define CHB 0x40 /* channel B offset */
-
-/*
- * FIXME: PPC has PVR defined in asm/reg.h. For now we just undef it.
- */
-#undef PVR
-
-#define RXFIFO 0
-#define TXFIFO 0
-#define STAR 0x20
-#define CMDR 0x20
-#define RSTA 0x21
-#define PRE 0x21
-#define MODE 0x22
-#define TIMR 0x23
-#define XAD1 0x24
-#define XAD2 0x25
-#define RAH1 0x26
-#define RAH2 0x27
-#define DAFO 0x27
-#define RAL1 0x28
-#define RFC 0x28
-#define RHCR 0x29
-#define RAL2 0x29
-#define RBCL 0x2a
-#define XBCL 0x2a
-#define RBCH 0x2b
-#define XBCH 0x2b
-#define CCR0 0x2c
-#define CCR1 0x2d
-#define CCR2 0x2e
-#define CCR3 0x2f
-#define VSTR 0x34
-#define BGR 0x34
-#define RLCR 0x35
-#define AML 0x36
-#define AMH 0x37
-#define GIS 0x38
-#define IVA 0x38
-#define IPC 0x39
-#define ISR 0x3a
-#define IMR 0x3a
-#define PVR 0x3c
-#define PIS 0x3d
-#define PIM 0x3d
-#define PCR 0x3e
-#define CCR4 0x3f
-
-// IMR/ISR
-
-#define IRQ_BREAK_ON BIT15 // rx break detected
-#define IRQ_DATAOVERRUN BIT14 // receive data overflow
-#define IRQ_ALLSENT BIT13 // all sent
-#define IRQ_UNDERRUN BIT12 // transmit data underrun
-#define IRQ_TIMER BIT11 // timer interrupt
-#define IRQ_CTS BIT10 // CTS status change
-#define IRQ_TXREPEAT BIT9 // tx message repeat
-#define IRQ_TXFIFO BIT8 // transmit pool ready
-#define IRQ_RXEOM BIT7 // receive message end
-#define IRQ_EXITHUNT BIT6 // receive frame start
-#define IRQ_RXTIME BIT6 // rx char timeout
-#define IRQ_DCD BIT2 // carrier detect status change
-#define IRQ_OVERRUN BIT1 // receive frame overflow
-#define IRQ_RXFIFO BIT0 // receive pool full
-
-// STAR
-
-#define XFW BIT6 // transmit FIFO write enable
-#define CEC BIT2 // command executing
-#define CTS BIT1 // CTS state
-
-#define PVR_DTR BIT0
-#define PVR_DSR BIT1
-#define PVR_RI BIT2
-#define PVR_AUTOCTS BIT3
-#define PVR_RS232 0x20 /* 0010b */
-#define PVR_V35 0xe0 /* 1110b */
-#define PVR_RS422 0x40 /* 0100b */
-
-/* Register access functions */
-
-#define write_reg(info, reg, val) outb((val),(info)->io_base + (reg))
-#define read_reg(info, reg) inb((info)->io_base + (reg))
-
-#define read_reg16(info, reg) inw((info)->io_base + (reg))
-#define write_reg16(info, reg, val) outw((val), (info)->io_base + (reg))
-
-#define set_reg_bits(info, reg, mask) \
- write_reg(info, (reg), \
- (unsigned char) (read_reg(info, (reg)) | (mask)))
-#define clear_reg_bits(info, reg, mask) \
- write_reg(info, (reg), \
- (unsigned char) (read_reg(info, (reg)) & ~(mask)))
-/*
- * interrupt enable/disable routines
- */
-static void irq_disable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask)
-{
- if (channel == CHA) {
- info->imra_value |= mask;
- write_reg16(info, CHA + IMR, info->imra_value);
- } else {
- info->imrb_value |= mask;
- write_reg16(info, CHB + IMR, info->imrb_value);
- }
-}
-static void irq_enable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask)
-{
- if (channel == CHA) {
- info->imra_value &= ~mask;
- write_reg16(info, CHA + IMR, info->imra_value);
- } else {
- info->imrb_value &= ~mask;
- write_reg16(info, CHB + IMR, info->imrb_value);
- }
-}
-
-#define port_irq_disable(info, mask) \
- { info->pim_value |= (mask); write_reg(info, PIM, info->pim_value); }
-
-#define port_irq_enable(info, mask) \
- { info->pim_value &= ~(mask); write_reg(info, PIM, info->pim_value); }
-
-static void rx_start(MGSLPC_INFO *info);
-static void rx_stop(MGSLPC_INFO *info);
-
-static void tx_start(MGSLPC_INFO *info, struct tty_struct *tty);
-static void tx_stop(MGSLPC_INFO *info);
-static void tx_set_idle(MGSLPC_INFO *info);
-
-static void get_signals(MGSLPC_INFO *info);
-static void set_signals(MGSLPC_INFO *info);
-
-static void reset_device(MGSLPC_INFO *info);
-
-static void hdlc_mode(MGSLPC_INFO *info);
-static void async_mode(MGSLPC_INFO *info);
-
-static void tx_timeout(struct timer_list *t);
-
-static int carrier_raised(struct tty_port *port);
-static void dtr_rts(struct tty_port *port, int onoff);
-
-#if SYNCLINK_GENERIC_HDLC
-#define dev_to_port(D) (dev_to_hdlc(D)->priv)
-static void hdlcdev_tx_done(MGSLPC_INFO *info);
-static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size);
-static int hdlcdev_init(MGSLPC_INFO *info);
-static void hdlcdev_exit(MGSLPC_INFO *info);
-#endif
-
-static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit);
-
-static bool register_test(MGSLPC_INFO *info);
-static bool irq_test(MGSLPC_INFO *info);
-static int adapter_test(MGSLPC_INFO *info);
-
-static int claim_resources(MGSLPC_INFO *info);
-static void release_resources(MGSLPC_INFO *info);
-static int mgslpc_add_device(MGSLPC_INFO *info);
-static void mgslpc_remove_device(MGSLPC_INFO *info);
-
-static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty);
-static void rx_reset_buffers(MGSLPC_INFO *info);
-static int rx_alloc_buffers(MGSLPC_INFO *info);
-static void rx_free_buffers(MGSLPC_INFO *info);
-
-static irqreturn_t mgslpc_isr(int irq, void *dev_id);
-
-/*
- * Bottom half interrupt handlers
- */
-static void bh_handler(struct work_struct *work);
-static void bh_transmit(MGSLPC_INFO *info, struct tty_struct *tty);
-static void bh_status(MGSLPC_INFO *info);
-
-/*
- * ioctl handlers
- */
-static int tiocmget(struct tty_struct *tty);
-static int tiocmset(struct tty_struct *tty,
- unsigned int set, unsigned int clear);
-static int get_stats(MGSLPC_INFO *info, struct mgsl_icount __user *user_icount);
-static int get_params(MGSLPC_INFO *info, MGSL_PARAMS __user *user_params);
-static int set_params(MGSLPC_INFO *info, MGSL_PARAMS __user *new_params, struct tty_struct *tty);
-static int get_txidle(MGSLPC_INFO *info, int __user *idle_mode);
-static int set_txidle(MGSLPC_INFO *info, int idle_mode);
-static int set_txenable(MGSLPC_INFO *info, int enable, struct tty_struct *tty);
-static int tx_abort(MGSLPC_INFO *info);
-static int set_rxenable(MGSLPC_INFO *info, int enable);
-static int wait_events(MGSLPC_INFO *info, int __user *mask);
-
-static MGSLPC_INFO *mgslpc_device_list = NULL;
-static int mgslpc_device_count = 0;
-
-/*
- * Set this param to non-zero to load eax with the
- * .text section address and breakpoint on module load.
- * This is useful for use with gdb and add-symbol-file command.
- */
-static bool break_on_load;
-
-/*
- * Driver major number, defaults to zero to get auto
- * assigned major number. May be forced as module parameter.
- */
-static int ttymajor=0;
-
-static int debug_level = 0;
-static int maxframe[MAX_DEVICE_COUNT] = {0,};
-
-module_param(break_on_load, bool, 0);
-module_param(ttymajor, int, 0);
-module_param(debug_level, int, 0);
-module_param_array(maxframe, int, NULL, 0);
-
-MODULE_LICENSE("GPL");
-
-static char *driver_name = "SyncLink PC Card driver";
-static char *driver_version = "$Revision: 4.34 $";
-
-static struct tty_driver *serial_driver;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty);
-static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout);
-
-/* PCMCIA prototypes */
-
-static int mgslpc_config(struct pcmcia_device *link);
-static void mgslpc_release(u_long arg);
-static void mgslpc_detach(struct pcmcia_device *p_dev);
-
-/*
- * 1st function defined in .text section. Calling this function in
- * init_module() followed by a breakpoint allows a remote debugger
- * (gdb) to get the .text address for the add-symbol-file command.
- * This allows remote debugging of dynamically loadable modules.
- */
-static void* mgslpc_get_text_ptr(void)
-{
- return mgslpc_get_text_ptr;
-}
-
-/**
- * line discipline callback wrappers
- *
- * The wrappers maintain line discipline references
- * while calling into the line discipline.
- *
- * ldisc_receive_buf - pass receive data to line discipline
- */
-
-static void ldisc_receive_buf(struct tty_struct *tty,
- const __u8 *data, char *flags, int count)
-{
- struct tty_ldisc *ld;
- if (!tty)
- return;
- ld = tty_ldisc_ref(tty);
- if (ld) {
- if (ld->ops->receive_buf)
- ld->ops->receive_buf(tty, data, flags, count);
- tty_ldisc_deref(ld);
- }
-}
-
-static const struct tty_port_operations mgslpc_port_ops = {
- .carrier_raised = carrier_raised,
- .dtr_rts = dtr_rts
-};
-
-static int mgslpc_probe(struct pcmcia_device *link)
-{
- MGSLPC_INFO *info;
- int ret;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_attach\n");
-
- info = kzalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
- if (!info) {
- printk("Error can't allocate device instance data\n");
- return -ENOMEM;
- }
-
- info->magic = MGSLPC_MAGIC;
- tty_port_init(&info->port);
- info->port.ops = &mgslpc_port_ops;
- INIT_WORK(&info->task, bh_handler);
- info->max_frame_size = 4096;
- init_waitqueue_head(&info->status_event_wait_q);
- init_waitqueue_head(&info->event_wait_q);
- spin_lock_init(&info->lock);
- spin_lock_init(&info->netlock);
- memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
- info->idle_mode = HDLC_TXIDLE_FLAGS;
- info->imra_value = 0xffff;
- info->imrb_value = 0xffff;
- info->pim_value = 0xff;
-
- info->p_dev = link;
- link->priv = info;
-
- /* Initialize the struct pcmcia_device structure */
-
- ret = mgslpc_config(link);
- if (ret != 0)
- goto failed;
-
- ret = mgslpc_add_device(info);
- if (ret != 0)
- goto failed_release;
-
- return 0;
-
-failed_release:
- mgslpc_release((u_long)link);
-failed:
- tty_port_destroy(&info->port);
- kfree(info);
- return ret;
-}
-
-/* Card has been inserted.
- */
-
-static int mgslpc_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
-{
- return pcmcia_request_io(p_dev);
-}
-
-static int mgslpc_config(struct pcmcia_device *link)
-{
- MGSLPC_INFO *info = link->priv;
- int ret;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_config(0x%p)\n", link);
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- ret = pcmcia_loop_config(link, mgslpc_ioprobe, NULL);
- if (ret != 0)
- goto failed;
-
- link->config_index = 8;
- link->config_regs = PRESENT_OPTION;
-
- ret = pcmcia_request_irq(link, mgslpc_isr);
- if (ret)
- goto failed;
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- info->io_base = link->resource[0]->start;
- info->irq_level = link->irq;
- return 0;
-
-failed:
- mgslpc_release((u_long)link);
- return -ENODEV;
-}
-
-/* Card has been removed.
- * Unregister device and release PCMCIA configuration.
- * If device is open, postpone until it is closed.
- */
-static void mgslpc_release(u_long arg)
-{
- struct pcmcia_device *link = (struct pcmcia_device *)arg;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_release(0x%p)\n", link);
-
- pcmcia_disable_device(link);
-}
-
-static void mgslpc_detach(struct pcmcia_device *link)
-{
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_detach(0x%p)\n", link);
-
- ((MGSLPC_INFO *)link->priv)->stop = 1;
- mgslpc_release((u_long)link);
-
- mgslpc_remove_device((MGSLPC_INFO *)link->priv);
-}
-
-static int mgslpc_suspend(struct pcmcia_device *link)
-{
- MGSLPC_INFO *info = link->priv;
-
- info->stop = 1;
-
- return 0;
-}
-
-static int mgslpc_resume(struct pcmcia_device *link)
-{
- MGSLPC_INFO *info = link->priv;
-
- info->stop = 0;
-
- return 0;
-}
-
-
-static inline bool mgslpc_paranoia_check(MGSLPC_INFO *info,
- char *name, const char *routine)
-{
-#ifdef MGSLPC_PARANOIA_CHECK
- static const char *badmagic =
- "Warning: bad magic number for mgsl struct (%s) in %s\n";
- static const char *badinfo =
- "Warning: null mgslpc_info for (%s) in %s\n";
-
- if (!info) {
- printk(badinfo, name, routine);
- return true;
- }
- if (info->magic != MGSLPC_MAGIC) {
- printk(badmagic, name, routine);
- return true;
- }
-#else
- if (!info)
- return true;
-#endif
- return false;
-}
-
-
-#define CMD_RXFIFO BIT7 // release current rx FIFO
-#define CMD_RXRESET BIT6 // receiver reset
-#define CMD_RXFIFO_READ BIT5
-#define CMD_START_TIMER BIT4
-#define CMD_TXFIFO BIT3 // release current tx FIFO
-#define CMD_TXEOM BIT1 // transmit end message
-#define CMD_TXRESET BIT0 // transmit reset
-
-static bool wait_command_complete(MGSLPC_INFO *info, unsigned char channel)
-{
- int i = 0;
- /* wait for command completion */
- while (read_reg(info, (unsigned char)(channel+STAR)) & BIT2) {
- udelay(1);
- if (i++ == 1000)
- return false;
- }
- return true;
-}
-
-static void issue_command(MGSLPC_INFO *info, unsigned char channel, unsigned char cmd)
-{
- wait_command_complete(info, channel);
- write_reg(info, (unsigned char) (channel + CMDR), cmd);
-}
-
-static void tx_pause(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (mgslpc_paranoia_check(info, tty->name, "tx_pause"))
- return;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("tx_pause(%s)\n", info->device_name);
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->tx_enabled)
- tx_stop(info);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void tx_release(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (mgslpc_paranoia_check(info, tty->name, "tx_release"))
- return;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("tx_release(%s)\n", info->device_name);
-
- spin_lock_irqsave(&info->lock, flags);
- if (!info->tx_enabled)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/* Return next bottom half action to perform.
- * or 0 if nothing to do.
- */
-static int bh_action(MGSLPC_INFO *info)
-{
- unsigned long flags;
- int rc = 0;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->pending_bh & BH_RECEIVE) {
- info->pending_bh &= ~BH_RECEIVE;
- rc = BH_RECEIVE;
- } else if (info->pending_bh & BH_TRANSMIT) {
- info->pending_bh &= ~BH_TRANSMIT;
- rc = BH_TRANSMIT;
- } else if (info->pending_bh & BH_STATUS) {
- info->pending_bh &= ~BH_STATUS;
- rc = BH_STATUS;
- }
-
- if (!rc) {
- /* Mark BH routine as complete */
- info->bh_running = false;
- info->bh_requested = false;
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- return rc;
-}
-
-static void bh_handler(struct work_struct *work)
-{
- MGSLPC_INFO *info = container_of(work, MGSLPC_INFO, task);
- struct tty_struct *tty;
- int action;
-
- if (debug_level >= DEBUG_LEVEL_BH)
- printk("%s(%d):bh_handler(%s) entry\n",
- __FILE__,__LINE__,info->device_name);
-
- info->bh_running = true;
- tty = tty_port_tty_get(&info->port);
-
- while((action = bh_action(info)) != 0) {
-
- /* Process work item */
- if (debug_level >= DEBUG_LEVEL_BH)
- printk("%s(%d):bh_handler() work item action=%d\n",
- __FILE__,__LINE__,action);
-
- switch (action) {
-
- case BH_RECEIVE:
- while(rx_get_frame(info, tty));
- break;
- case BH_TRANSMIT:
- bh_transmit(info, tty);
- break;
- case BH_STATUS:
- bh_status(info);
- break;
- default:
- /* unknown work item ID */
- printk("Unknown work item ID=%08X!\n", action);
- break;
- }
- }
-
- tty_kref_put(tty);
- if (debug_level >= DEBUG_LEVEL_BH)
- printk("%s(%d):bh_handler(%s) exit\n",
- __FILE__,__LINE__,info->device_name);
-}
-
-static void bh_transmit(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- if (debug_level >= DEBUG_LEVEL_BH)
- printk("bh_transmit() entry on %s\n", info->device_name);
-
- if (tty)
- tty_wakeup(tty);
-}
-
-static void bh_status(MGSLPC_INFO *info)
-{
- info->ri_chkcount = 0;
- info->dsr_chkcount = 0;
- info->dcd_chkcount = 0;
- info->cts_chkcount = 0;
-}
-
-/* eom: non-zero = end of frame */
-static void rx_ready_hdlc(MGSLPC_INFO *info, int eom)
-{
- unsigned char data[2];
- unsigned char fifo_count, read_count, i;
- RXBUF *buf = (RXBUF*)(info->rx_buf + (info->rx_put * info->rx_buf_size));
-
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):rx_ready_hdlc(eom=%d)\n", __FILE__, __LINE__, eom);
-
- if (!info->rx_enabled)
- return;
-
- if (info->rx_frame_count >= info->rx_buf_count) {
- /* no more free buffers */
- issue_command(info, CHA, CMD_RXRESET);
- info->pending_bh |= BH_RECEIVE;
- info->rx_overflow = true;
- info->icount.buf_overrun++;
- return;
- }
-
- if (eom) {
- /* end of frame, get FIFO count from RBCL register */
- fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
- if (fifo_count == 0)
- fifo_count = 32;
- } else
- fifo_count = 32;
-
- do {
- if (fifo_count == 1) {
- read_count = 1;
- data[0] = read_reg(info, CHA + RXFIFO);
- } else {
- read_count = 2;
- *((unsigned short *) data) = read_reg16(info, CHA + RXFIFO);
- }
- fifo_count -= read_count;
- if (!fifo_count && eom)
- buf->status = data[--read_count];
-
- for (i = 0; i < read_count; i++) {
- if (buf->count >= info->max_frame_size) {
- /* frame too large, reset receiver and reset current buffer */
- issue_command(info, CHA, CMD_RXRESET);
- buf->count = 0;
- return;
- }
- *(buf->data + buf->count) = data[i];
- buf->count++;
- }
- } while (fifo_count);
-
- if (eom) {
- info->pending_bh |= BH_RECEIVE;
- info->rx_frame_count++;
- info->rx_put++;
- if (info->rx_put >= info->rx_buf_count)
- info->rx_put = 0;
- }
- issue_command(info, CHA, CMD_RXFIFO);
-}
-
-static void rx_ready_async(MGSLPC_INFO *info, int tcd)
-{
- struct tty_port *port = &info->port;
- unsigned char data, status, flag;
- int fifo_count;
- int work = 0;
- struct mgsl_icount *icount = &info->icount;
-
- if (tcd) {
- /* early termination, get FIFO count from RBCL register */
- fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
-
- /* Zero fifo count could mean 0 or 32 bytes available.
- * If BIT5 of STAR is set then at least 1 byte is available.
- */
- if (!fifo_count && (read_reg(info,CHA+STAR) & BIT5))
- fifo_count = 32;
- } else
- fifo_count = 32;
-
- tty_buffer_request_room(port, fifo_count);
- /* Flush received async data to receive data buffer. */
- while (fifo_count) {
- data = read_reg(info, CHA + RXFIFO);
- status = read_reg(info, CHA + RXFIFO);
- fifo_count -= 2;
-
- icount->rx++;
- flag = TTY_NORMAL;
-
- // if no frameing/crc error then save data
- // BIT7:parity error
- // BIT6:framing error
-
- if (status & (BIT7 | BIT6)) {
- if (status & BIT7)
- icount->parity++;
- else
- icount->frame++;
-
- /* discard char if tty control flags say so */
- if (status & info->ignore_status_mask)
- continue;
-
- status &= info->read_status_mask;
-
- if (status & BIT7)
- flag = TTY_PARITY;
- else if (status & BIT6)
- flag = TTY_FRAME;
- }
- work += tty_insert_flip_char(port, data, flag);
- }
- issue_command(info, CHA, CMD_RXFIFO);
-
- if (debug_level >= DEBUG_LEVEL_ISR) {
- printk("%s(%d):rx_ready_async",
- __FILE__,__LINE__);
- printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
- __FILE__,__LINE__,icount->rx,icount->brk,
- icount->parity,icount->frame,icount->overrun);
- }
-
- if (work)
- tty_flip_buffer_push(port);
-}
-
-
-static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- if (!info->tx_active)
- return;
-
- info->tx_active = false;
- info->tx_aborting = false;
-
- if (info->params.mode == MGSL_MODE_ASYNC)
- return;
-
- info->tx_count = info->tx_put = info->tx_get = 0;
- del_timer(&info->tx_timer);
-
- if (info->drop_rts_on_tx_done) {
- get_signals(info);
- if (info->serial_signals & SerialSignal_RTS) {
- info->serial_signals &= ~SerialSignal_RTS;
- set_signals(info);
- }
- info->drop_rts_on_tx_done = false;
- }
-
-#if SYNCLINK_GENERIC_HDLC
- if (info->netcount)
- hdlcdev_tx_done(info);
- else
-#endif
- {
- if (tty && (tty->flow.stopped || tty->hw_stopped)) {
- tx_stop(info);
- return;
- }
- info->pending_bh |= BH_TRANSMIT;
- }
-}
-
-static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- unsigned char fifo_count = 32;
- int c;
-
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):tx_ready(%s)\n", __FILE__, __LINE__, info->device_name);
-
- if (info->params.mode == MGSL_MODE_HDLC) {
- if (!info->tx_active)
- return;
- } else {
- if (tty && (tty->flow.stopped || tty->hw_stopped)) {
- tx_stop(info);
- return;
- }
- if (!info->tx_count)
- info->tx_active = false;
- }
-
- if (!info->tx_count)
- return;
-
- while (info->tx_count && fifo_count) {
- c = min(2, min_t(int, fifo_count, min(info->tx_count, TXBUFSIZE - info->tx_get)));
-
- if (c == 1) {
- write_reg(info, CHA + TXFIFO, *(info->tx_buf + info->tx_get));
- } else {
- write_reg16(info, CHA + TXFIFO,
- *((unsigned short*)(info->tx_buf + info->tx_get)));
- }
- info->tx_count -= c;
- info->tx_get = (info->tx_get + c) & (TXBUFSIZE - 1);
- fifo_count -= c;
- }
-
- if (info->params.mode == MGSL_MODE_ASYNC) {
- if (info->tx_count < WAKEUP_CHARS)
- info->pending_bh |= BH_TRANSMIT;
- issue_command(info, CHA, CMD_TXFIFO);
- } else {
- if (info->tx_count)
- issue_command(info, CHA, CMD_TXFIFO);
- else
- issue_command(info, CHA, CMD_TXFIFO + CMD_TXEOM);
- }
-}
-
-static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- get_signals(info);
- if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
- irq_disable(info, CHB, IRQ_CTS);
- info->icount.cts++;
- if (info->serial_signals & SerialSignal_CTS)
- info->input_signal_events.cts_up++;
- else
- info->input_signal_events.cts_down++;
- wake_up_interruptible(&info->status_event_wait_q);
- wake_up_interruptible(&info->event_wait_q);
-
- if (tty && tty_port_cts_enabled(&info->port)) {
- if (tty->hw_stopped) {
- if (info->serial_signals & SerialSignal_CTS) {
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("CTS tx start...");
- tty->hw_stopped = 0;
- tx_start(info, tty);
- info->pending_bh |= BH_TRANSMIT;
- return;
- }
- } else {
- if (!(info->serial_signals & SerialSignal_CTS)) {
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("CTS tx stop...");
- tty->hw_stopped = 1;
- tx_stop(info);
- }
- }
- }
- info->pending_bh |= BH_STATUS;
-}
-
-static void dcd_change(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- get_signals(info);
- if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
- irq_disable(info, CHB, IRQ_DCD);
- info->icount.dcd++;
- if (info->serial_signals & SerialSignal_DCD) {
- info->input_signal_events.dcd_up++;
- }
- else
- info->input_signal_events.dcd_down++;
-#if SYNCLINK_GENERIC_HDLC
- if (info->netcount) {
- if (info->serial_signals & SerialSignal_DCD)
- netif_carrier_on(info->netdev);
- else
- netif_carrier_off(info->netdev);
- }
-#endif
- wake_up_interruptible(&info->status_event_wait_q);
- wake_up_interruptible(&info->event_wait_q);
-
- if (tty_port_check_carrier(&info->port)) {
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s CD now %s...", info->device_name,
- (info->serial_signals & SerialSignal_DCD) ? "on" : "off");
- if (info->serial_signals & SerialSignal_DCD)
- wake_up_interruptible(&info->port.open_wait);
- else {
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("doing serial hangup...");
- if (tty)
- tty_hangup(tty);
- }
- }
- info->pending_bh |= BH_STATUS;
-}
-
-static void dsr_change(MGSLPC_INFO *info)
-{
- get_signals(info);
- if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
- port_irq_disable(info, PVR_DSR);
- info->icount.dsr++;
- if (info->serial_signals & SerialSignal_DSR)
- info->input_signal_events.dsr_up++;
- else
- info->input_signal_events.dsr_down++;
- wake_up_interruptible(&info->status_event_wait_q);
- wake_up_interruptible(&info->event_wait_q);
- info->pending_bh |= BH_STATUS;
-}
-
-static void ri_change(MGSLPC_INFO *info)
-{
- get_signals(info);
- if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
- port_irq_disable(info, PVR_RI);
- info->icount.rng++;
- if (info->serial_signals & SerialSignal_RI)
- info->input_signal_events.ri_up++;
- else
- info->input_signal_events.ri_down++;
- wake_up_interruptible(&info->status_event_wait_q);
- wake_up_interruptible(&info->event_wait_q);
- info->pending_bh |= BH_STATUS;
-}
-
-/* Interrupt service routine entry point.
- *
- * Arguments:
- *
- * irq interrupt number that caused interrupt
- * dev_id device ID supplied during interrupt registration
- */
-static irqreturn_t mgslpc_isr(int dummy, void *dev_id)
-{
- MGSLPC_INFO *info = dev_id;
- struct tty_struct *tty;
- unsigned short isr;
- unsigned char gis, pis;
- int count=0;
-
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("mgslpc_isr(%d) entry.\n", info->irq_level);
-
- if (!(info->p_dev->_locked))
- return IRQ_HANDLED;
-
- tty = tty_port_tty_get(&info->port);
-
- spin_lock(&info->lock);
-
- while ((gis = read_reg(info, CHA + GIS))) {
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("mgslpc_isr %s gis=%04X\n", info->device_name,gis);
-
- if ((gis & 0x70) || count > 1000) {
- printk("synclink_cs:hardware failed or ejected\n");
- break;
- }
- count++;
-
- if (gis & (BIT1 | BIT0)) {
- isr = read_reg16(info, CHB + ISR);
- if (isr & IRQ_DCD)
- dcd_change(info, tty);
- if (isr & IRQ_CTS)
- cts_change(info, tty);
- }
- if (gis & (BIT3 | BIT2))
- {
- isr = read_reg16(info, CHA + ISR);
- if (isr & IRQ_TIMER) {
- info->irq_occurred = true;
- irq_disable(info, CHA, IRQ_TIMER);
- }
-
- /* receive IRQs */
- if (isr & IRQ_EXITHUNT) {
- info->icount.exithunt++;
- wake_up_interruptible(&info->event_wait_q);
- }
- if (isr & IRQ_BREAK_ON) {
- info->icount.brk++;
- if (info->port.flags & ASYNC_SAK)
- do_SAK(tty);
- }
- if (isr & IRQ_RXTIME) {
- issue_command(info, CHA, CMD_RXFIFO_READ);
- }
- if (isr & (IRQ_RXEOM | IRQ_RXFIFO)) {
- if (info->params.mode == MGSL_MODE_HDLC)
- rx_ready_hdlc(info, isr & IRQ_RXEOM);
- else
- rx_ready_async(info, isr & IRQ_RXEOM);
- }
-
- /* transmit IRQs */
- if (isr & IRQ_UNDERRUN) {
- if (info->tx_aborting)
- info->icount.txabort++;
- else
- info->icount.txunder++;
- tx_done(info, tty);
- }
- else if (isr & IRQ_ALLSENT) {
- info->icount.txok++;
- tx_done(info, tty);
- }
- else if (isr & IRQ_TXFIFO)
- tx_ready(info, tty);
- }
- if (gis & BIT7) {
- pis = read_reg(info, CHA + PIS);
- if (pis & BIT1)
- dsr_change(info);
- if (pis & BIT2)
- ri_change(info);
- }
- }
-
- /* Request bottom half processing if there's something
- * for it to do and the bh is not already running
- */
-
- if (info->pending_bh && !info->bh_running && !info->bh_requested) {
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):%s queueing bh task.\n",
- __FILE__,__LINE__,info->device_name);
- schedule_work(&info->task);
- info->bh_requested = true;
- }
-
- spin_unlock(&info->lock);
- tty_kref_put(tty);
-
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):mgslpc_isr(%d)exit.\n",
- __FILE__, __LINE__, info->irq_level);
-
- return IRQ_HANDLED;
-}
-
-/* Initialize and start device.
- */
-static int startup(MGSLPC_INFO * info, struct tty_struct *tty)
-{
- int retval = 0;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):startup(%s)\n", __FILE__, __LINE__, info->device_name);
-
- if (tty_port_initialized(&info->port))
- return 0;
-
- if (!info->tx_buf) {
- /* allocate a page of memory for a transmit buffer */
- info->tx_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
- if (!info->tx_buf) {
- printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
- __FILE__, __LINE__, info->device_name);
- return -ENOMEM;
- }
- }
-
- info->pending_bh = 0;
-
- memset(&info->icount, 0, sizeof(info->icount));
-
- timer_setup(&info->tx_timer, tx_timeout, 0);
-
- /* Allocate and claim adapter resources */
- retval = claim_resources(info);
-
- /* perform existence check and diagnostics */
- if (!retval)
- retval = adapter_test(info);
-
- if (retval) {
- if (capable(CAP_SYS_ADMIN) && tty)
- set_bit(TTY_IO_ERROR, &tty->flags);
- release_resources(info);
- return retval;
- }
-
- /* program hardware for current parameters */
- mgslpc_change_params(info, tty);
-
- if (tty)
- clear_bit(TTY_IO_ERROR, &tty->flags);
-
- tty_port_set_initialized(&info->port, 1);
-
- return 0;
-}
-
-/* Called by mgslpc_close() and mgslpc_hangup() to shutdown hardware
- */
-static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty)
-{
- unsigned long flags;
-
- if (!tty_port_initialized(&info->port))
- return;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_shutdown(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- /* clear status wait queue because status changes */
- /* can't happen after shutting down the hardware */
- wake_up_interruptible(&info->status_event_wait_q);
- wake_up_interruptible(&info->event_wait_q);
-
- del_timer_sync(&info->tx_timer);
-
- if (info->tx_buf) {
- free_page((unsigned long) info->tx_buf);
- info->tx_buf = NULL;
- }
-
- spin_lock_irqsave(&info->lock, flags);
-
- rx_stop(info);
- tx_stop(info);
-
- /* TODO:disable interrupts instead of reset to preserve signal states */
- reset_device(info);
-
- if (!tty || C_HUPCL(tty)) {
- info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
- set_signals(info);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- release_resources(info);
-
- if (tty)
- set_bit(TTY_IO_ERROR, &tty->flags);
-
- tty_port_set_initialized(&info->port, 0);
-}
-
-static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
-
- rx_stop(info);
- tx_stop(info);
- info->tx_count = info->tx_put = info->tx_get = 0;
-
- if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
- hdlc_mode(info);
- else
- async_mode(info);
-
- set_signals(info);
-
- info->dcd_chkcount = 0;
- info->cts_chkcount = 0;
- info->ri_chkcount = 0;
- info->dsr_chkcount = 0;
-
- irq_enable(info, CHB, IRQ_DCD | IRQ_CTS);
- port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI);
- get_signals(info);
-
- if (info->netcount || (tty && C_CREAD(tty)))
- rx_start(info);
-
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/* Reconfigure adapter based on new parameters
- */
-static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- unsigned cflag;
- int bits_per_char;
-
- if (!tty)
- return;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_change_params(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- cflag = tty->termios.c_cflag;
-
- /* if B0 rate (hangup) specified then negate RTS and DTR */
- /* otherwise assert RTS and DTR */
- if (cflag & CBAUD)
- info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR;
- else
- info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
-
- /* byte size and parity */
- if ((cflag & CSIZE) != CS8) {
- cflag &= ~CSIZE;
- cflag |= CS7;
- tty->termios.c_cflag = cflag;
- }
- info->params.data_bits = tty_get_char_size(cflag);
-
- if (cflag & CSTOPB)
- info->params.stop_bits = 2;
- else
- info->params.stop_bits = 1;
-
- info->params.parity = ASYNC_PARITY_NONE;
- if (cflag & PARENB) {
- if (cflag & PARODD)
- info->params.parity = ASYNC_PARITY_ODD;
- else
- info->params.parity = ASYNC_PARITY_EVEN;
- if (cflag & CMSPAR)
- info->params.parity = ASYNC_PARITY_SPACE;
- }
-
- /* calculate number of jiffies to transmit a full
- * FIFO (32 bytes) at specified data rate
- */
- bits_per_char = info->params.data_bits +
- info->params.stop_bits + 1;
-
- /* if port data rate is set to 460800 or less then
- * allow tty settings to override, otherwise keep the
- * current data rate.
- */
- if (info->params.data_rate <= 460800) {
- info->params.data_rate = tty_get_baud_rate(tty);
- }
-
- if (info->params.data_rate) {
- info->timeout = (32*HZ*bits_per_char) /
- info->params.data_rate;
- }
- info->timeout += HZ/50; /* Add .02 seconds of slop */
-
- tty_port_set_cts_flow(&info->port, cflag & CRTSCTS);
- tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL);
-
- /* process tty input control flags */
-
- info->read_status_mask = 0;
- if (I_INPCK(tty))
- info->read_status_mask |= BIT7 | BIT6;
- if (I_IGNPAR(tty))
- info->ignore_status_mask |= BIT7 | BIT6;
-
- mgslpc_program_hw(info, tty);
-}
-
-/* Add a character to the transmit buffer
- */
-static int mgslpc_put_char(struct tty_struct *tty, unsigned char ch)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO) {
- printk("%s(%d):mgslpc_put_char(%d) on %s\n",
- __FILE__, __LINE__, ch, info->device_name);
- }
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_put_char"))
- return 0;
-
- if (!info->tx_buf)
- return 0;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->params.mode == MGSL_MODE_ASYNC || !info->tx_active) {
- if (info->tx_count < TXBUFSIZE - 1) {
- info->tx_buf[info->tx_put++] = ch;
- info->tx_put &= TXBUFSIZE-1;
- info->tx_count++;
- }
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
- return 1;
-}
-
-/* Enable transmitter so remaining characters in the
- * transmit buffer are sent.
- */
-static void mgslpc_flush_chars(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_flush_chars() entry on %s tx_count=%d\n",
- __FILE__, __LINE__, info->device_name, info->tx_count);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_flush_chars"))
- return;
-
- if (info->tx_count <= 0 || tty->flow.stopped ||
- tty->hw_stopped || !info->tx_buf)
- return;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_flush_chars() entry on %s starting transmitter\n",
- __FILE__, __LINE__, info->device_name);
-
- spin_lock_irqsave(&info->lock, flags);
- if (!info->tx_active)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/* Send a block of data
- *
- * Arguments:
- *
- * tty pointer to tty information structure
- * buf pointer to buffer containing send data
- * count size of send data in bytes
- *
- * Returns: number of characters written
- */
-static int mgslpc_write(struct tty_struct * tty,
- const unsigned char *buf, int count)
-{
- int c, ret = 0;
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_write(%s) count=%d\n",
- __FILE__, __LINE__, info->device_name, count);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_write") ||
- !info->tx_buf)
- goto cleanup;
-
- if (info->params.mode == MGSL_MODE_HDLC) {
- if (count > TXBUFSIZE) {
- ret = -EIO;
- goto cleanup;
- }
- if (info->tx_active)
- goto cleanup;
- else if (info->tx_count)
- goto start;
- }
-
- for (;;) {
- c = min(count,
- min(TXBUFSIZE - info->tx_count - 1,
- TXBUFSIZE - info->tx_put));
- if (c <= 0)
- break;
-
- memcpy(info->tx_buf + info->tx_put, buf, c);
-
- spin_lock_irqsave(&info->lock, flags);
- info->tx_put = (info->tx_put + c) & (TXBUFSIZE-1);
- info->tx_count += c;
- spin_unlock_irqrestore(&info->lock, flags);
-
- buf += c;
- count -= c;
- ret += c;
- }
-start:
- if (info->tx_count && !tty->flow.stopped && !tty->hw_stopped) {
- spin_lock_irqsave(&info->lock, flags);
- if (!info->tx_active)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-cleanup:
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_write(%s) returning=%d\n",
- __FILE__, __LINE__, info->device_name, ret);
- return ret;
-}
-
-/* Return the count of free bytes in transmit buffer
- */
-static unsigned int mgslpc_write_room(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- int ret;
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_write_room"))
- return 0;
-
- if (info->params.mode == MGSL_MODE_HDLC) {
- /* HDLC (frame oriented) mode */
- if (info->tx_active)
- return 0;
- else
- return HDLC_MAX_FRAME_SIZE;
- } else {
- ret = TXBUFSIZE - info->tx_count - 1;
- if (ret < 0)
- ret = 0;
- }
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_write_room(%s)=%d\n",
- __FILE__, __LINE__, info->device_name, ret);
- return ret;
-}
-
-/* Return the count of bytes in transmit buffer
- */
-static unsigned int mgslpc_chars_in_buffer(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned int rc;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_chars_in_buffer(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_chars_in_buffer"))
- return 0;
-
- if (info->params.mode == MGSL_MODE_HDLC)
- rc = info->tx_active ? info->max_frame_size : 0;
- else
- rc = info->tx_count;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_chars_in_buffer(%s)=%u\n",
- __FILE__, __LINE__, info->device_name, rc);
-
- return rc;
-}
-
-/* Discard all data in the send buffer
- */
-static void mgslpc_flush_buffer(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_flush_buffer(%s) entry\n",
- __FILE__, __LINE__, info->device_name);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_flush_buffer"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- info->tx_count = info->tx_put = info->tx_get = 0;
- del_timer(&info->tx_timer);
- spin_unlock_irqrestore(&info->lock, flags);
-
- wake_up_interruptible(&tty->write_wait);
- tty_wakeup(tty);
-}
-
-/* Send a high-priority XON/XOFF character
- */
-static void mgslpc_send_xchar(struct tty_struct *tty, char ch)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_send_xchar(%s,%d)\n",
- __FILE__, __LINE__, info->device_name, ch);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_send_xchar"))
- return;
-
- info->x_char = ch;
- if (ch) {
- spin_lock_irqsave(&info->lock, flags);
- if (!info->tx_enabled)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-}
-
-/* Signal remote device to throttle send data (our receive data)
- */
-static void mgslpc_throttle(struct tty_struct * tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_throttle(%s) entry\n",
- __FILE__, __LINE__, info->device_name);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_throttle"))
- return;
-
- if (I_IXOFF(tty))
- mgslpc_send_xchar(tty, STOP_CHAR(tty));
-
- if (C_CRTSCTS(tty)) {
- spin_lock_irqsave(&info->lock, flags);
- info->serial_signals &= ~SerialSignal_RTS;
- set_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-}
-
-/* Signal remote device to stop throttling send data (our receive data)
- */
-static void mgslpc_unthrottle(struct tty_struct * tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_unthrottle(%s) entry\n",
- __FILE__, __LINE__, info->device_name);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_unthrottle"))
- return;
-
- if (I_IXOFF(tty)) {
- if (info->x_char)
- info->x_char = 0;
- else
- mgslpc_send_xchar(tty, START_CHAR(tty));
- }
-
- if (C_CRTSCTS(tty)) {
- spin_lock_irqsave(&info->lock, flags);
- info->serial_signals |= SerialSignal_RTS;
- set_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-}
-
-/* get the current serial statistics
- */
-static int get_stats(MGSLPC_INFO * info, struct mgsl_icount __user *user_icount)
-{
- int err;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("get_params(%s)\n", info->device_name);
- if (!user_icount) {
- memset(&info->icount, 0, sizeof(info->icount));
- } else {
- COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
- if (err)
- return -EFAULT;
- }
- return 0;
-}
-
-/* get the current serial parameters
- */
-static int get_params(MGSLPC_INFO * info, MGSL_PARAMS __user *user_params)
-{
- int err;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("get_params(%s)\n", info->device_name);
- COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
- if (err)
- return -EFAULT;
- return 0;
-}
-
-/* set the serial parameters
- *
- * Arguments:
- *
- * info pointer to device instance data
- * new_params user buffer containing new serial params
- *
- * Returns: 0 if success, otherwise error code
- */
-static int set_params(MGSLPC_INFO * info, MGSL_PARAMS __user *new_params, struct tty_struct *tty)
-{
- unsigned long flags;
- MGSL_PARAMS tmp_params;
- int err;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):set_params %s\n", __FILE__,__LINE__,
- info->device_name);
- COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
- if (err) {
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):set_params(%s) user buffer copy failed\n",
- __FILE__, __LINE__, info->device_name);
- return -EFAULT;
- }
-
- spin_lock_irqsave(&info->lock, flags);
- memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
- spin_unlock_irqrestore(&info->lock, flags);
-
- mgslpc_change_params(info, tty);
-
- return 0;
-}
-
-static int get_txidle(MGSLPC_INFO * info, int __user *idle_mode)
-{
- int err;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("get_txidle(%s)=%d\n", info->device_name, info->idle_mode);
- COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
- if (err)
- return -EFAULT;
- return 0;
-}
-
-static int set_txidle(MGSLPC_INFO * info, int idle_mode)
-{
- unsigned long flags;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("set_txidle(%s,%d)\n", info->device_name, idle_mode);
- spin_lock_irqsave(&info->lock, flags);
- info->idle_mode = idle_mode;
- tx_set_idle(info);
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-static int get_interface(MGSLPC_INFO * info, int __user *if_mode)
-{
- int err;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("get_interface(%s)=%d\n", info->device_name, info->if_mode);
- COPY_TO_USER(err,if_mode, &info->if_mode, sizeof(int));
- if (err)
- return -EFAULT;
- return 0;
-}
-
-static int set_interface(MGSLPC_INFO * info, int if_mode)
-{
- unsigned long flags;
- unsigned char val;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("set_interface(%s,%d)\n", info->device_name, if_mode);
- spin_lock_irqsave(&info->lock, flags);
- info->if_mode = if_mode;
-
- val = read_reg(info, PVR) & 0x0f;
- switch (info->if_mode)
- {
- case MGSL_INTERFACE_RS232: val |= PVR_RS232; break;
- case MGSL_INTERFACE_V35: val |= PVR_V35; break;
- case MGSL_INTERFACE_RS422: val |= PVR_RS422; break;
- }
- write_reg(info, PVR, val);
-
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-static int set_txenable(MGSLPC_INFO * info, int enable, struct tty_struct *tty)
-{
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("set_txenable(%s,%d)\n", info->device_name, enable);
-
- spin_lock_irqsave(&info->lock, flags);
- if (enable) {
- if (!info->tx_enabled)
- tx_start(info, tty);
- } else {
- if (info->tx_enabled)
- tx_stop(info);
- }
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-static int tx_abort(MGSLPC_INFO * info)
-{
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("tx_abort(%s)\n", info->device_name);
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->tx_active && info->tx_count &&
- info->params.mode == MGSL_MODE_HDLC) {
- /* clear data count so FIFO is not filled on next IRQ.
- * This results in underrun and abort transmission.
- */
- info->tx_count = info->tx_put = info->tx_get = 0;
- info->tx_aborting = true;
- }
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-static int set_rxenable(MGSLPC_INFO * info, int enable)
-{
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("set_rxenable(%s,%d)\n", info->device_name, enable);
-
- spin_lock_irqsave(&info->lock, flags);
- if (enable) {
- if (!info->rx_enabled)
- rx_start(info);
- } else {
- if (info->rx_enabled)
- rx_stop(info);
- }
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-/* wait for specified event to occur
- *
- * Arguments: info pointer to device instance data
- * mask pointer to bitmask of events to wait for
- * Return Value: 0 if successful and bit mask updated with
- * of events triggerred,
- * otherwise error code
- */
-static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr)
-{
- unsigned long flags;
- int s;
- int rc=0;
- struct mgsl_icount cprev, cnow;
- int events;
- int mask;
- struct _input_signal_events oldsigs, newsigs;
- DECLARE_WAITQUEUE(wait, current);
-
- COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
- if (rc)
- return -EFAULT;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("wait_events(%s,%d)\n", info->device_name, mask);
-
- spin_lock_irqsave(&info->lock, flags);
-
- /* return immediately if state matches requested events */
- get_signals(info);
- s = info->serial_signals;
- events = mask &
- ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
- ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
- ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
- ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) );
- if (events) {
- spin_unlock_irqrestore(&info->lock, flags);
- goto exit;
- }
-
- /* save current irq counts */
- cprev = info->icount;
- oldsigs = info->input_signal_events;
-
- if ((info->params.mode == MGSL_MODE_HDLC) &&
- (mask & MgslEvent_ExitHuntMode))
- irq_enable(info, CHA, IRQ_EXITHUNT);
-
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&info->event_wait_q, &wait);
-
- spin_unlock_irqrestore(&info->lock, flags);
-
-
- for(;;) {
- schedule();
- if (signal_pending(current)) {
- rc = -ERESTARTSYS;
- break;
- }
-
- /* get current irq counts */
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount;
- newsigs = info->input_signal_events;
- set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&info->lock, flags);
-
- /* if no change, wait aborted for some reason */
- if (newsigs.dsr_up == oldsigs.dsr_up &&
- newsigs.dsr_down == oldsigs.dsr_down &&
- newsigs.dcd_up == oldsigs.dcd_up &&
- newsigs.dcd_down == oldsigs.dcd_down &&
- newsigs.cts_up == oldsigs.cts_up &&
- newsigs.cts_down == oldsigs.cts_down &&
- newsigs.ri_up == oldsigs.ri_up &&
- newsigs.ri_down == oldsigs.ri_down &&
- cnow.exithunt == cprev.exithunt &&
- cnow.rxidle == cprev.rxidle) {
- rc = -EIO;
- break;
- }
-
- events = mask &
- ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) +
- (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
- (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) +
- (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
- (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) +
- (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
- (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) +
- (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) +
- (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) +
- (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) );
- if (events)
- break;
-
- cprev = cnow;
- oldsigs = newsigs;
- }
-
- remove_wait_queue(&info->event_wait_q, &wait);
- set_current_state(TASK_RUNNING);
-
- if (mask & MgslEvent_ExitHuntMode) {
- spin_lock_irqsave(&info->lock, flags);
- if (!waitqueue_active(&info->event_wait_q))
- irq_disable(info, CHA, IRQ_EXITHUNT);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-exit:
- if (rc == 0)
- PUT_USER(rc, events, mask_ptr);
- return rc;
-}
-
-static int modem_input_wait(MGSLPC_INFO *info,int arg)
-{
- unsigned long flags;
- int rc;
- struct mgsl_icount cprev, cnow;
- DECLARE_WAITQUEUE(wait, current);
-
- /* save current irq counts */
- spin_lock_irqsave(&info->lock, flags);
- cprev = info->icount;
- add_wait_queue(&info->status_event_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&info->lock, flags);
-
- for(;;) {
- schedule();
- if (signal_pending(current)) {
- rc = -ERESTARTSYS;
- break;
- }
-
- /* get new irq counts */
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount;
- set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&info->lock, flags);
-
- /* if no change, wait aborted for some reason */
- if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
- cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
- rc = -EIO;
- break;
- }
-
- /* check for change in caller specified modem input */
- if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) ||
- (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) ||
- (arg & TIOCM_CD && cnow.dcd != cprev.dcd) ||
- (arg & TIOCM_CTS && cnow.cts != cprev.cts)) {
- rc = 0;
- break;
- }
-
- cprev = cnow;
- }
- remove_wait_queue(&info->status_event_wait_q, &wait);
- set_current_state(TASK_RUNNING);
- return rc;
-}
-
-/* return the state of the serial control and status signals
- */
-static int tiocmget(struct tty_struct *tty)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned int result;
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
-
- result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
- ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
- ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
- ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) +
- ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
- ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):%s tiocmget() value=%08X\n",
- __FILE__, __LINE__, info->device_name, result);
- return result;
-}
-
-/* set modem control signals (DTR/RTS)
- */
-static int tiocmset(struct tty_struct *tty,
- unsigned int set, unsigned int clear)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):%s tiocmset(%x,%x)\n",
- __FILE__, __LINE__, info->device_name, set, clear);
-
- if (set & TIOCM_RTS)
- info->serial_signals |= SerialSignal_RTS;
- if (set & TIOCM_DTR)
- info->serial_signals |= SerialSignal_DTR;
- if (clear & TIOCM_RTS)
- info->serial_signals &= ~SerialSignal_RTS;
- if (clear & TIOCM_DTR)
- info->serial_signals &= ~SerialSignal_DTR;
-
- spin_lock_irqsave(&info->lock, flags);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
-
- return 0;
-}
-
-/* Set or clear transmit break condition
- *
- * Arguments: tty pointer to tty instance data
- * break_state -1=set break condition, 0=clear
- */
-static int mgslpc_break(struct tty_struct *tty, int break_state)
-{
- MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_break(%s,%d)\n",
- __FILE__, __LINE__, info->device_name, break_state);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break"))
- return -EINVAL;
-
- spin_lock_irqsave(&info->lock, flags);
- if (break_state == -1)
- set_reg_bits(info, CHA+DAFO, BIT6);
- else
- clear_reg_bits(info, CHA+DAFO, BIT6);
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-static int mgslpc_get_icount(struct tty_struct *tty,
- struct serial_icounter_struct *icount)
-{
- MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
- struct mgsl_icount cnow; /* kernel counter temps */
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount;
- spin_unlock_irqrestore(&info->lock, flags);
-
- icount->cts = cnow.cts;
- icount->dsr = cnow.dsr;
- icount->rng = cnow.rng;
- icount->dcd = cnow.dcd;
- icount->rx = cnow.rx;
- icount->tx = cnow.tx;
- icount->frame = cnow.frame;
- icount->overrun = cnow.overrun;
- icount->parity = cnow.parity;
- icount->brk = cnow.brk;
- icount->buf_overrun = cnow.buf_overrun;
-
- return 0;
-}
-
-/* Service an IOCTL request
- *
- * Arguments:
- *
- * tty pointer to tty instance data
- * cmd IOCTL command code
- * arg command argument/context
- *
- * Return Value: 0 if success, otherwise error code
- */
-static int mgslpc_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
- void __user *argp = (void __user *)arg;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__, __LINE__,
- info->device_name, cmd);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_ioctl"))
- return -ENODEV;
-
- if (cmd != TIOCMIWAIT) {
- if (tty_io_error(tty))
- return -EIO;
- }
-
- switch (cmd) {
- case MGSL_IOCGPARAMS:
- return get_params(info, argp);
- case MGSL_IOCSPARAMS:
- return set_params(info, argp, tty);
- case MGSL_IOCGTXIDLE:
- return get_txidle(info, argp);
- case MGSL_IOCSTXIDLE:
- return set_txidle(info, (int)arg);
- case MGSL_IOCGIF:
- return get_interface(info, argp);
- case MGSL_IOCSIF:
- return set_interface(info,(int)arg);
- case MGSL_IOCTXENABLE:
- return set_txenable(info,(int)arg, tty);
- case MGSL_IOCRXENABLE:
- return set_rxenable(info,(int)arg);
- case MGSL_IOCTXABORT:
- return tx_abort(info);
- case MGSL_IOCGSTATS:
- return get_stats(info, argp);
- case MGSL_IOCWAITEVENT:
- return wait_events(info, argp);
- case TIOCMIWAIT:
- return modem_input_wait(info,(int)arg);
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-/* Set new termios settings
- *
- * Arguments:
- *
- * tty pointer to tty structure
- * termios pointer to buffer to hold returned old termios
- */
-static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
- MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_set_termios %s\n", __FILE__, __LINE__,
- tty->driver->name);
-
- /* just return if nothing has changed */
- if ((tty->termios.c_cflag == old_termios->c_cflag)
- && (RELEVANT_IFLAG(tty->termios.c_iflag)
- == RELEVANT_IFLAG(old_termios->c_iflag)))
- return;
-
- mgslpc_change_params(info, tty);
-
- /* Handle transition to B0 status */
- if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) {
- info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
- spin_lock_irqsave(&info->lock, flags);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- /* Handle transition away from B0 status */
- if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) {
- info->serial_signals |= SerialSignal_DTR;
- if (!C_CRTSCTS(tty) || !tty_throttled(tty))
- info->serial_signals |= SerialSignal_RTS;
- spin_lock_irqsave(&info->lock, flags);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- /* Handle turning off CRTSCTS */
- if (old_termios->c_cflag & CRTSCTS && !C_CRTSCTS(tty)) {
- tty->hw_stopped = 0;
- tx_release(tty);
- }
-}
-
-static void mgslpc_close(struct tty_struct *tty, struct file * filp)
-{
- MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
- struct tty_port *port = &info->port;
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_close"))
- return;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_close(%s) entry, count=%d\n",
- __FILE__, __LINE__, info->device_name, port->count);
-
- if (tty_port_close_start(port, tty, filp) == 0)
- goto cleanup;
-
- if (tty_port_initialized(port))
- mgslpc_wait_until_sent(tty, info->timeout);
-
- mgslpc_flush_buffer(tty);
-
- tty_ldisc_flush(tty);
- shutdown(info, tty);
-
- tty_port_close_end(port, tty);
- tty_port_tty_set(port, NULL);
-cleanup:
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_close(%s) exit, count=%d\n", __FILE__, __LINE__,
- tty->driver->name, port->count);
-}
-
-/* Wait until the transmitter is empty.
- */
-static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long orig_jiffies, char_time;
-
- if (!info)
- return;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_wait_until_sent(%s) entry\n",
- __FILE__, __LINE__, info->device_name);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_wait_until_sent"))
- return;
-
- if (!tty_port_initialized(&info->port))
- goto exit;
-
- orig_jiffies = jiffies;
-
- /* Set check interval to 1/5 of estimated time to
- * send a character, and make it at least 1. The check
- * interval should also be less than the timeout.
- * Note: use tight timings here to satisfy the NIST-PCTS.
- */
-
- if (info->params.data_rate) {
- char_time = info->timeout/(32 * 5);
- if (!char_time)
- char_time++;
- } else
- char_time = 1;
-
- if (timeout)
- char_time = min_t(unsigned long, char_time, timeout);
-
- if (info->params.mode == MGSL_MODE_HDLC) {
- while (info->tx_active) {
- msleep_interruptible(jiffies_to_msecs(char_time));
- if (signal_pending(current))
- break;
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- break;
- }
- } else {
- while ((info->tx_count || info->tx_active) &&
- info->tx_enabled) {
- msleep_interruptible(jiffies_to_msecs(char_time));
- if (signal_pending(current))
- break;
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- break;
- }
- }
-
-exit:
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_wait_until_sent(%s) exit\n",
- __FILE__, __LINE__, info->device_name);
-}
-
-/* Called by tty_hangup() when a hangup is signaled.
- * This is the same as closing all open files for the port.
- */
-static void mgslpc_hangup(struct tty_struct *tty)
-{
- MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_hangup(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_hangup"))
- return;
-
- mgslpc_flush_buffer(tty);
- shutdown(info, tty);
- tty_port_hangup(&info->port);
-}
-
-static int carrier_raised(struct tty_port *port)
-{
- MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port);
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
-
- if (info->serial_signals & SerialSignal_DCD)
- return 1;
- return 0;
-}
-
-static void dtr_rts(struct tty_port *port, int onoff)
-{
- MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port);
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- if (onoff)
- info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR;
- else
- info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-
-static int mgslpc_open(struct tty_struct *tty, struct file * filp)
-{
- MGSLPC_INFO *info;
- struct tty_port *port;
- int retval, line;
- unsigned long flags;
-
- /* verify range of specified line number */
- line = tty->index;
- if (line >= mgslpc_device_count) {
- printk("%s(%d):mgslpc_open with invalid line #%d.\n",
- __FILE__, __LINE__, line);
- return -ENODEV;
- }
-
- /* find the info structure for the specified line */
- info = mgslpc_device_list;
- while(info && info->line != line)
- info = info->next_device;
- if (mgslpc_paranoia_check(info, tty->name, "mgslpc_open"))
- return -ENODEV;
-
- port = &info->port;
- tty->driver_data = info;
- tty_port_tty_set(port, tty);
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_open(%s), old ref count = %d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
-
- spin_lock_irqsave(&info->netlock, flags);
- if (info->netcount) {
- retval = -EBUSY;
- spin_unlock_irqrestore(&info->netlock, flags);
- goto cleanup;
- }
- spin_lock(&port->lock);
- port->count++;
- spin_unlock(&port->lock);
- spin_unlock_irqrestore(&info->netlock, flags);
-
- if (port->count == 1) {
- /* 1st open on this device, init hardware */
- retval = startup(info, tty);
- if (retval < 0)
- goto cleanup;
- }
-
- retval = tty_port_block_til_ready(&info->port, tty, filp);
- if (retval) {
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):block_til_ready(%s) returned %d\n",
- __FILE__, __LINE__, info->device_name, retval);
- goto cleanup;
- }
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_open(%s) success\n",
- __FILE__, __LINE__, info->device_name);
- retval = 0;
-
-cleanup:
- return retval;
-}
-
-/*
- * /proc fs routines....
- */
-
-static inline void line_info(struct seq_file *m, MGSLPC_INFO *info)
-{
- char stat_buf[30];
- unsigned long flags;
-
- seq_printf(m, "%s:io:%04X irq:%d",
- info->device_name, info->io_base, info->irq_level);
-
- /* output current serial signal states */
- spin_lock_irqsave(&info->lock, flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
-
- stat_buf[0] = 0;
- stat_buf[1] = 0;
- if (info->serial_signals & SerialSignal_RTS)
- strcat(stat_buf, "|RTS");
- if (info->serial_signals & SerialSignal_CTS)
- strcat(stat_buf, "|CTS");
- if (info->serial_signals & SerialSignal_DTR)
- strcat(stat_buf, "|DTR");
- if (info->serial_signals & SerialSignal_DSR)
- strcat(stat_buf, "|DSR");
- if (info->serial_signals & SerialSignal_DCD)
- strcat(stat_buf, "|CD");
- if (info->serial_signals & SerialSignal_RI)
- strcat(stat_buf, "|RI");
-
- if (info->params.mode == MGSL_MODE_HDLC) {
- seq_printf(m, " HDLC txok:%d rxok:%d",
- info->icount.txok, info->icount.rxok);
- if (info->icount.txunder)
- seq_printf(m, " txunder:%d", info->icount.txunder);
- if (info->icount.txabort)
- seq_printf(m, " txabort:%d", info->icount.txabort);
- if (info->icount.rxshort)
- seq_printf(m, " rxshort:%d", info->icount.rxshort);
- if (info->icount.rxlong)
- seq_printf(m, " rxlong:%d", info->icount.rxlong);
- if (info->icount.rxover)
- seq_printf(m, " rxover:%d", info->icount.rxover);
- if (info->icount.rxcrc)
- seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
- } else {
- seq_printf(m, " ASYNC tx:%d rx:%d",
- info->icount.tx, info->icount.rx);
- if (info->icount.frame)
- seq_printf(m, " fe:%d", info->icount.frame);
- if (info->icount.parity)
- seq_printf(m, " pe:%d", info->icount.parity);
- if (info->icount.brk)
- seq_printf(m, " brk:%d", info->icount.brk);
- if (info->icount.overrun)
- seq_printf(m, " oe:%d", info->icount.overrun);
- }
-
- /* Append serial signal status to end */
- seq_printf(m, " %s\n", stat_buf+1);
-
- seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
- info->tx_active,info->bh_requested,info->bh_running,
- info->pending_bh);
-}
-
-/* Called to print information about devices
- */
-static int mgslpc_proc_show(struct seq_file *m, void *v)
-{
- MGSLPC_INFO *info;
-
- seq_printf(m, "synclink driver:%s\n", driver_version);
-
- info = mgslpc_device_list;
- while (info) {
- line_info(m, info);
- info = info->next_device;
- }
- return 0;
-}
-
-static int rx_alloc_buffers(MGSLPC_INFO *info)
-{
- /* each buffer has header and data */
- info->rx_buf_size = sizeof(RXBUF) + info->max_frame_size;
-
- /* calculate total allocation size for 8 buffers */
- info->rx_buf_total_size = info->rx_buf_size * 8;
-
- /* limit total allocated memory */
- if (info->rx_buf_total_size > 0x10000)
- info->rx_buf_total_size = 0x10000;
-
- /* calculate number of buffers */
- info->rx_buf_count = info->rx_buf_total_size / info->rx_buf_size;
-
- info->rx_buf = kmalloc(info->rx_buf_total_size, GFP_KERNEL);
- if (info->rx_buf == NULL)
- return -ENOMEM;
-
- /* unused flag buffer to satisfy receive_buf calling interface */
- info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL);
- if (!info->flag_buf) {
- kfree(info->rx_buf);
- info->rx_buf = NULL;
- return -ENOMEM;
- }
-
- rx_reset_buffers(info);
- return 0;
-}
-
-static void rx_free_buffers(MGSLPC_INFO *info)
-{
- kfree(info->rx_buf);
- info->rx_buf = NULL;
- kfree(info->flag_buf);
- info->flag_buf = NULL;
-}
-
-static int claim_resources(MGSLPC_INFO *info)
-{
- if (rx_alloc_buffers(info) < 0) {
- printk("Can't allocate rx buffer %s\n", info->device_name);
- release_resources(info);
- return -ENODEV;
- }
- return 0;
-}
-
-static void release_resources(MGSLPC_INFO *info)
-{
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("release_resources(%s)\n", info->device_name);
- rx_free_buffers(info);
-}
-
-/* Add the specified device instance data structure to the
- * global linked list of devices and increment the device count.
- *
- * Arguments: info pointer to device instance data
- */
-static int mgslpc_add_device(MGSLPC_INFO *info)
-{
- MGSLPC_INFO *current_dev = NULL;
- struct device *tty_dev;
- int ret;
-
- info->next_device = NULL;
- info->line = mgslpc_device_count;
- sprintf(info->device_name,"ttySLP%d",info->line);
-
- if (info->line < MAX_DEVICE_COUNT) {
- if (maxframe[info->line])
- info->max_frame_size = maxframe[info->line];
- }
-
- mgslpc_device_count++;
-
- if (!mgslpc_device_list)
- mgslpc_device_list = info;
- else {
- current_dev = mgslpc_device_list;
- while (current_dev->next_device)
- current_dev = current_dev->next_device;
- current_dev->next_device = info;
- }
-
- if (info->max_frame_size < 4096)
- info->max_frame_size = 4096;
- else if (info->max_frame_size > 65535)
- info->max_frame_size = 65535;
-
- printk("SyncLink PC Card %s:IO=%04X IRQ=%d\n",
- info->device_name, info->io_base, info->irq_level);
-
-#if SYNCLINK_GENERIC_HDLC
- ret = hdlcdev_init(info);
- if (ret != 0)
- goto failed;
-#endif
-
- tty_dev = tty_port_register_device(&info->port, serial_driver, info->line,
- &info->p_dev->dev);
- if (IS_ERR(tty_dev)) {
- ret = PTR_ERR(tty_dev);
-#if SYNCLINK_GENERIC_HDLC
- hdlcdev_exit(info);
-#endif
- goto failed;
- }
-
- return 0;
-
-failed:
- if (current_dev)
- current_dev->next_device = NULL;
- else
- mgslpc_device_list = NULL;
- mgslpc_device_count--;
- return ret;
-}
-
-static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
-{
- MGSLPC_INFO *info = mgslpc_device_list;
- MGSLPC_INFO *last = NULL;
-
- while(info) {
- if (info == remove_info) {
- if (last)
- last->next_device = info->next_device;
- else
- mgslpc_device_list = info->next_device;
- tty_unregister_device(serial_driver, info->line);
-#if SYNCLINK_GENERIC_HDLC
- hdlcdev_exit(info);
-#endif
- release_resources(info);
- tty_port_destroy(&info->port);
- kfree(info);
- mgslpc_device_count--;
- return;
- }
- last = info;
- info = info->next_device;
- }
-}
-
-static const struct pcmcia_device_id mgslpc_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x02c5, 0x0050),
- PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, mgslpc_ids);
-
-static struct pcmcia_driver mgslpc_driver = {
- .owner = THIS_MODULE,
- .name = "synclink_cs",
- .probe = mgslpc_probe,
- .remove = mgslpc_detach,
- .id_table = mgslpc_ids,
- .suspend = mgslpc_suspend,
- .resume = mgslpc_resume,
-};
-
-static const struct tty_operations mgslpc_ops = {
- .open = mgslpc_open,
- .close = mgslpc_close,
- .write = mgslpc_write,
- .put_char = mgslpc_put_char,
- .flush_chars = mgslpc_flush_chars,
- .write_room = mgslpc_write_room,
- .chars_in_buffer = mgslpc_chars_in_buffer,
- .flush_buffer = mgslpc_flush_buffer,
- .ioctl = mgslpc_ioctl,
- .throttle = mgslpc_throttle,
- .unthrottle = mgslpc_unthrottle,
- .send_xchar = mgslpc_send_xchar,
- .break_ctl = mgslpc_break,
- .wait_until_sent = mgslpc_wait_until_sent,
- .set_termios = mgslpc_set_termios,
- .stop = tx_pause,
- .start = tx_release,
- .hangup = mgslpc_hangup,
- .tiocmget = tiocmget,
- .tiocmset = tiocmset,
- .get_icount = mgslpc_get_icount,
- .proc_show = mgslpc_proc_show,
-};
-
-static int __init synclink_cs_init(void)
-{
- int rc;
-
- if (break_on_load) {
- mgslpc_get_text_ptr();
- BREAKPOINT();
- }
-
- serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT,
- TTY_DRIVER_REAL_RAW |
- TTY_DRIVER_DYNAMIC_DEV);
- if (IS_ERR(serial_driver)) {
- rc = PTR_ERR(serial_driver);
- goto err;
- }
-
- /* Initialize the tty_driver structure */
- serial_driver->driver_name = "synclink_cs";
- serial_driver->name = "ttySLP";
- serial_driver->major = ttymajor;
- serial_driver->minor_start = 64;
- serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
- serial_driver->subtype = SERIAL_TYPE_NORMAL;
- serial_driver->init_termios = tty_std_termios;
- serial_driver->init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- tty_set_operations(serial_driver, &mgslpc_ops);
-
- rc = tty_register_driver(serial_driver);
- if (rc < 0) {
- printk(KERN_ERR "%s(%d):Couldn't register serial driver\n",
- __FILE__, __LINE__);
- goto err_put_tty;
- }
-
- rc = pcmcia_register_driver(&mgslpc_driver);
- if (rc < 0)
- goto err_unreg_tty;
-
- printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version,
- serial_driver->major);
-
- return 0;
-err_unreg_tty:
- tty_unregister_driver(serial_driver);
-err_put_tty:
- tty_driver_kref_put(serial_driver);
-err:
- return rc;
-}
-
-static void __exit synclink_cs_exit(void)
-{
- pcmcia_unregister_driver(&mgslpc_driver);
- tty_unregister_driver(serial_driver);
- tty_driver_kref_put(serial_driver);
-}
-
-module_init(synclink_cs_init);
-module_exit(synclink_cs_exit);
-
-static void mgslpc_set_rate(MGSLPC_INFO *info, unsigned char channel, unsigned int rate)
-{
- unsigned int M, N;
- unsigned char val;
-
- /* note:standard BRG mode is broken in V3.2 chip
- * so enhanced mode is always used
- */
-
- if (rate) {
- N = 3686400 / rate;
- if (!N)
- N = 1;
- N >>= 1;
- for (M = 1; N > 64 && M < 16; M++)
- N >>= 1;
- N--;
-
- /* BGR[5..0] = N
- * BGR[9..6] = M
- * BGR[7..0] contained in BGR register
- * BGR[9..8] contained in CCR2[7..6]
- * divisor = (N+1)*2^M
- *
- * Note: M *must* not be zero (causes asymetric duty cycle)
- */
- write_reg(info, (unsigned char) (channel + BGR),
- (unsigned char) ((M << 6) + N));
- val = read_reg(info, (unsigned char) (channel + CCR2)) & 0x3f;
- val |= ((M << 4) & 0xc0);
- write_reg(info, (unsigned char) (channel + CCR2), val);
- }
-}
-
-/* Enabled the AUX clock output at the specified frequency.
- */
-static void enable_auxclk(MGSLPC_INFO *info)
-{
- unsigned char val;
-
- /* MODE
- *
- * 07..06 MDS[1..0] 10 = transparent HDLC mode
- * 05 ADM Address Mode, 0 = no addr recognition
- * 04 TMD Timer Mode, 0 = external
- * 03 RAC Receiver Active, 0 = inactive
- * 02 RTS 0=RTS active during xmit, 1=RTS always active
- * 01 TRS Timer Resolution, 1=512
- * 00 TLP Test Loop, 0 = no loop
- *
- * 1000 0010
- */
- val = 0x82;
-
- /* channel B RTS is used to enable AUXCLK driver on SP505 */
- if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed)
- val |= BIT2;
- write_reg(info, CHB + MODE, val);
-
- /* CCR0
- *
- * 07 PU Power Up, 1=active, 0=power down
- * 06 MCE Master Clock Enable, 1=enabled
- * 05 Reserved, 0
- * 04..02 SC[2..0] Encoding
- * 01..00 SM[1..0] Serial Mode, 00=HDLC
- *
- * 11000000
- */
- write_reg(info, CHB + CCR0, 0xc0);
-
- /* CCR1
- *
- * 07 SFLG Shared Flag, 0 = disable shared flags
- * 06 GALP Go Active On Loop, 0 = not used
- * 05 GLP Go On Loop, 0 = not used
- * 04 ODS Output Driver Select, 1=TxD is push-pull output
- * 03 ITF Interframe Time Fill, 0=mark, 1=flag
- * 02..00 CM[2..0] Clock Mode
- *
- * 0001 0111
- */
- write_reg(info, CHB + CCR1, 0x17);
-
- /* CCR2 (Channel B)
- *
- * 07..06 BGR[9..8] Baud rate bits 9..8
- * 05 BDF Baud rate divisor factor, 0=1, 1=BGR value
- * 04 SSEL Clock source select, 1=submode b
- * 03 TOE 0=TxCLK is input, 1=TxCLK is output
- * 02 RWX Read/Write Exchange 0=disabled
- * 01 C32, CRC select, 0=CRC-16, 1=CRC-32
- * 00 DIV, data inversion 0=disabled, 1=enabled
- *
- * 0011 1000
- */
- if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed)
- write_reg(info, CHB + CCR2, 0x38);
- else
- write_reg(info, CHB + CCR2, 0x30);
-
- /* CCR4
- *
- * 07 MCK4 Master Clock Divide by 4, 1=enabled
- * 06 EBRG Enhanced Baud Rate Generator Mode, 1=enabled
- * 05 TST1 Test Pin, 0=normal operation
- * 04 ICD Ivert Carrier Detect, 1=enabled (active low)
- * 03..02 Reserved, must be 0
- * 01..00 RFT[1..0] RxFIFO Threshold 00=32 bytes
- *
- * 0101 0000
- */
- write_reg(info, CHB + CCR4, 0x50);
-
- /* if auxclk not enabled, set internal BRG so
- * CTS transitions can be detected (requires TxC)
- */
- if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed)
- mgslpc_set_rate(info, CHB, info->params.clock_speed);
- else
- mgslpc_set_rate(info, CHB, 921600);
-}
-
-static void loopback_enable(MGSLPC_INFO *info)
-{
- unsigned char val;
-
- /* CCR1:02..00 CM[2..0] Clock Mode = 111 (clock mode 7) */
- val = read_reg(info, CHA + CCR1) | (BIT2 | BIT1 | BIT0);
- write_reg(info, CHA + CCR1, val);
-
- /* CCR2:04 SSEL Clock source select, 1=submode b */
- val = read_reg(info, CHA + CCR2) | (BIT4 | BIT5);
- write_reg(info, CHA + CCR2, val);
-
- /* set LinkSpeed if available, otherwise default to 2Mbps */
- if (info->params.clock_speed)
- mgslpc_set_rate(info, CHA, info->params.clock_speed);
- else
- mgslpc_set_rate(info, CHA, 1843200);
-
- /* MODE:00 TLP Test Loop, 1=loopback enabled */
- val = read_reg(info, CHA + MODE) | BIT0;
- write_reg(info, CHA + MODE, val);
-}
-
-static void hdlc_mode(MGSLPC_INFO *info)
-{
- unsigned char val;
- unsigned char clkmode, clksubmode;
-
- /* disable all interrupts */
- irq_disable(info, CHA, 0xffff);
- irq_disable(info, CHB, 0xffff);
- port_irq_disable(info, 0xff);
-
- /* assume clock mode 0a, rcv=RxC xmt=TxC */
- clkmode = clksubmode = 0;
- if (info->params.flags & HDLC_FLAG_RXC_DPLL
- && info->params.flags & HDLC_FLAG_TXC_DPLL) {
- /* clock mode 7a, rcv = DPLL, xmt = DPLL */
- clkmode = 7;
- } else if (info->params.flags & HDLC_FLAG_RXC_BRG
- && info->params.flags & HDLC_FLAG_TXC_BRG) {
- /* clock mode 7b, rcv = BRG, xmt = BRG */
- clkmode = 7;
- clksubmode = 1;
- } else if (info->params.flags & HDLC_FLAG_RXC_DPLL) {
- if (info->params.flags & HDLC_FLAG_TXC_BRG) {
- /* clock mode 6b, rcv = DPLL, xmt = BRG/16 */
- clkmode = 6;
- clksubmode = 1;
- } else {
- /* clock mode 6a, rcv = DPLL, xmt = TxC */
- clkmode = 6;
- }
- } else if (info->params.flags & HDLC_FLAG_TXC_BRG) {
- /* clock mode 0b, rcv = RxC, xmt = BRG */
- clksubmode = 1;
- }
-
- /* MODE
- *
- * 07..06 MDS[1..0] 10 = transparent HDLC mode
- * 05 ADM Address Mode, 0 = no addr recognition
- * 04 TMD Timer Mode, 0 = external
- * 03 RAC Receiver Active, 0 = inactive
- * 02 RTS 0=RTS active during xmit, 1=RTS always active
- * 01 TRS Timer Resolution, 1=512
- * 00 TLP Test Loop, 0 = no loop
- *
- * 1000 0010
- */
- val = 0x82;
- if (info->params.loopback)
- val |= BIT0;
-
- /* preserve RTS state */
- if (info->serial_signals & SerialSignal_RTS)
- val |= BIT2;
- write_reg(info, CHA + MODE, val);
-
- /* CCR0
- *
- * 07 PU Power Up, 1=active, 0=power down
- * 06 MCE Master Clock Enable, 1=enabled
- * 05 Reserved, 0
- * 04..02 SC[2..0] Encoding
- * 01..00 SM[1..0] Serial Mode, 00=HDLC
- *
- * 11000000
- */
- val = 0xc0;
- switch (info->params.encoding)
- {
- case HDLC_ENCODING_NRZI:
- val |= BIT3;
- break;
- case HDLC_ENCODING_BIPHASE_SPACE:
- val |= BIT4;
- break; // FM0
- case HDLC_ENCODING_BIPHASE_MARK:
- val |= BIT4 | BIT2;
- break; // FM1
- case HDLC_ENCODING_BIPHASE_LEVEL:
- val |= BIT4 | BIT3;
- break; // Manchester
- }
- write_reg(info, CHA + CCR0, val);
-
- /* CCR1
- *
- * 07 SFLG Shared Flag, 0 = disable shared flags
- * 06 GALP Go Active On Loop, 0 = not used
- * 05 GLP Go On Loop, 0 = not used
- * 04 ODS Output Driver Select, 1=TxD is push-pull output
- * 03 ITF Interframe Time Fill, 0=mark, 1=flag
- * 02..00 CM[2..0] Clock Mode
- *
- * 0001 0000
- */
- val = 0x10 + clkmode;
- write_reg(info, CHA + CCR1, val);
-
- /* CCR2
- *
- * 07..06 BGR[9..8] Baud rate bits 9..8
- * 05 BDF Baud rate divisor factor, 0=1, 1=BGR value
- * 04 SSEL Clock source select, 1=submode b
- * 03 TOE 0=TxCLK is input, 0=TxCLK is input
- * 02 RWX Read/Write Exchange 0=disabled
- * 01 C32, CRC select, 0=CRC-16, 1=CRC-32
- * 00 DIV, data inversion 0=disabled, 1=enabled
- *
- * 0000 0000
- */
- val = 0x00;
- if (clkmode == 2 || clkmode == 3 || clkmode == 6
- || clkmode == 7 || (clkmode == 0 && clksubmode == 1))
- val |= BIT5;
- if (clksubmode)
- val |= BIT4;
- if (info->params.crc_type == HDLC_CRC_32_CCITT)
- val |= BIT1;
- if (info->params.encoding == HDLC_ENCODING_NRZB)
- val |= BIT0;
- write_reg(info, CHA + CCR2, val);
-
- /* CCR3
- *
- * 07..06 PRE[1..0] Preamble count 00=1, 01=2, 10=4, 11=8
- * 05 EPT Enable preamble transmission, 1=enabled
- * 04 RADD Receive address pushed to FIFO, 0=disabled
- * 03 CRL CRC Reset Level, 0=FFFF
- * 02 RCRC Rx CRC 0=On 1=Off
- * 01 TCRC Tx CRC 0=On 1=Off
- * 00 PSD DPLL Phase Shift Disable
- *
- * 0000 0000
- */
- val = 0x00;
- if (info->params.crc_type == HDLC_CRC_NONE)
- val |= BIT2 | BIT1;
- if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
- val |= BIT5;
- switch (info->params.preamble_length)
- {
- case HDLC_PREAMBLE_LENGTH_16BITS:
- val |= BIT6;
- break;
- case HDLC_PREAMBLE_LENGTH_32BITS:
- val |= BIT6;
- break;
- case HDLC_PREAMBLE_LENGTH_64BITS:
- val |= BIT7 | BIT6;
- break;
- }
- write_reg(info, CHA + CCR3, val);
-
- /* PRE - Preamble pattern */
- val = 0;
- switch (info->params.preamble)
- {
- case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break;
- case HDLC_PREAMBLE_PATTERN_10: val = 0xaa; break;
- case HDLC_PREAMBLE_PATTERN_01: val = 0x55; break;
- case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break;
- }
- write_reg(info, CHA + PRE, val);
-
- /* CCR4
- *
- * 07 MCK4 Master Clock Divide by 4, 1=enabled
- * 06 EBRG Enhanced Baud Rate Generator Mode, 1=enabled
- * 05 TST1 Test Pin, 0=normal operation
- * 04 ICD Ivert Carrier Detect, 1=enabled (active low)
- * 03..02 Reserved, must be 0
- * 01..00 RFT[1..0] RxFIFO Threshold 00=32 bytes
- *
- * 0101 0000
- */
- val = 0x50;
- write_reg(info, CHA + CCR4, val);
- if (info->params.flags & HDLC_FLAG_RXC_DPLL)
- mgslpc_set_rate(info, CHA, info->params.clock_speed * 16);
- else
- mgslpc_set_rate(info, CHA, info->params.clock_speed);
-
- /* RLCR Receive length check register
- *
- * 7 1=enable receive length check
- * 6..0 Max frame length = (RL + 1) * 32
- */
- write_reg(info, CHA + RLCR, 0);
-
- /* XBCH Transmit Byte Count High
- *
- * 07 DMA mode, 0 = interrupt driven
- * 06 NRM, 0=ABM (ignored)
- * 05 CAS Carrier Auto Start
- * 04 XC Transmit Continuously (ignored)
- * 03..00 XBC[10..8] Transmit byte count bits 10..8
- *
- * 0000 0000
- */
- val = 0x00;
- if (info->params.flags & HDLC_FLAG_AUTO_DCD)
- val |= BIT5;
- write_reg(info, CHA + XBCH, val);
- enable_auxclk(info);
- if (info->params.loopback || info->testing_irq)
- loopback_enable(info);
- if (info->params.flags & HDLC_FLAG_AUTO_CTS)
- {
- irq_enable(info, CHB, IRQ_CTS);
- /* PVR[3] 1=AUTO CTS active */
- set_reg_bits(info, CHA + PVR, BIT3);
- } else
- clear_reg_bits(info, CHA + PVR, BIT3);
-
- irq_enable(info, CHA,
- IRQ_RXEOM | IRQ_RXFIFO | IRQ_ALLSENT |
- IRQ_UNDERRUN | IRQ_TXFIFO);
- issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
- wait_command_complete(info, CHA);
- read_reg16(info, CHA + ISR); /* clear pending IRQs */
-
- /* Master clock mode enabled above to allow reset commands
- * to complete even if no data clocks are present.
- *
- * Disable master clock mode for normal communications because
- * V3.2 of the ESCC2 has a bug that prevents the transmit all sent
- * IRQ when in master clock mode.
- *
- * Leave master clock mode enabled for IRQ test because the
- * timer IRQ used by the test can only happen in master clock mode.
- */
- if (!info->testing_irq)
- clear_reg_bits(info, CHA + CCR0, BIT6);
-
- tx_set_idle(info);
-
- tx_stop(info);
- rx_stop(info);
-}
-
-static void rx_stop(MGSLPC_INFO *info)
-{
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):rx_stop(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- /* MODE:03 RAC Receiver Active, 0=inactive */
- clear_reg_bits(info, CHA + MODE, BIT3);
-
- info->rx_enabled = false;
- info->rx_overflow = false;
-}
-
-static void rx_start(MGSLPC_INFO *info)
-{
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):rx_start(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- rx_reset_buffers(info);
- info->rx_enabled = false;
- info->rx_overflow = false;
-
- /* MODE:03 RAC Receiver Active, 1=active */
- set_reg_bits(info, CHA + MODE, BIT3);
-
- info->rx_enabled = true;
-}
-
-static void tx_start(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):tx_start(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- if (info->tx_count) {
- /* If auto RTS enabled and RTS is inactive, then assert */
- /* RTS and set a flag indicating that the driver should */
- /* negate RTS when the transmission completes. */
- info->drop_rts_on_tx_done = false;
-
- if (info->params.flags & HDLC_FLAG_AUTO_RTS) {
- get_signals(info);
- if (!(info->serial_signals & SerialSignal_RTS)) {
- info->serial_signals |= SerialSignal_RTS;
- set_signals(info);
- info->drop_rts_on_tx_done = true;
- }
- }
-
- if (info->params.mode == MGSL_MODE_ASYNC) {
- if (!info->tx_active) {
- info->tx_active = true;
- tx_ready(info, tty);
- }
- } else {
- info->tx_active = true;
- tx_ready(info, tty);
- mod_timer(&info->tx_timer, jiffies +
- msecs_to_jiffies(5000));
- }
- }
-
- if (!info->tx_enabled)
- info->tx_enabled = true;
-}
-
-static void tx_stop(MGSLPC_INFO *info)
-{
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):tx_stop(%s)\n",
- __FILE__, __LINE__, info->device_name);
-
- del_timer(&info->tx_timer);
-
- info->tx_enabled = false;
- info->tx_active = false;
-}
-
-/* Reset the adapter to a known state and prepare it for further use.
- */
-static void reset_device(MGSLPC_INFO *info)
-{
- /* power up both channels (set BIT7) */
- write_reg(info, CHA + CCR0, 0x80);
- write_reg(info, CHB + CCR0, 0x80);
- write_reg(info, CHA + MODE, 0);
- write_reg(info, CHB + MODE, 0);
-
- /* disable all interrupts */
- irq_disable(info, CHA, 0xffff);
- irq_disable(info, CHB, 0xffff);
- port_irq_disable(info, 0xff);
-
- /* PCR Port Configuration Register
- *
- * 07..04 DEC[3..0] Serial I/F select outputs
- * 03 output, 1=AUTO CTS control enabled
- * 02 RI Ring Indicator input 0=active
- * 01 DSR input 0=active
- * 00 DTR output 0=active
- *
- * 0000 0110
- */
- write_reg(info, PCR, 0x06);
-
- /* PVR Port Value Register
- *
- * 07..04 DEC[3..0] Serial I/F select (0000=disabled)
- * 03 AUTO CTS output 1=enabled
- * 02 RI Ring Indicator input
- * 01 DSR input
- * 00 DTR output (1=inactive)
- *
- * 0000 0001
- */
-// write_reg(info, PVR, PVR_DTR);
-
- /* IPC Interrupt Port Configuration
- *
- * 07 VIS 1=Masked interrupts visible
- * 06..05 Reserved, 0
- * 04..03 SLA Slave address, 00 ignored
- * 02 CASM Cascading Mode, 1=daisy chain
- * 01..00 IC[1..0] Interrupt Config, 01=push-pull output, active low
- *
- * 0000 0101
- */
- write_reg(info, IPC, 0x05);
-}
-
-static void async_mode(MGSLPC_INFO *info)
-{
- unsigned char val;
-
- /* disable all interrupts */
- irq_disable(info, CHA, 0xffff);
- irq_disable(info, CHB, 0xffff);
- port_irq_disable(info, 0xff);
-
- /* MODE
- *
- * 07 Reserved, 0
- * 06 FRTS RTS State, 0=active
- * 05 FCTS Flow Control on CTS
- * 04 FLON Flow Control Enable
- * 03 RAC Receiver Active, 0 = inactive
- * 02 RTS 0=Auto RTS, 1=manual RTS
- * 01 TRS Timer Resolution, 1=512
- * 00 TLP Test Loop, 0 = no loop
- *
- * 0000 0110
- */
- val = 0x06;
- if (info->params.loopback)
- val |= BIT0;
-
- /* preserve RTS state */
- if (!(info->serial_signals & SerialSignal_RTS))
- val |= BIT6;
- write_reg(info, CHA + MODE, val);
-
- /* CCR0
- *
- * 07 PU Power Up, 1=active, 0=power down
- * 06 MCE Master Clock Enable, 1=enabled
- * 05 Reserved, 0
- * 04..02 SC[2..0] Encoding, 000=NRZ
- * 01..00 SM[1..0] Serial Mode, 11=Async
- *
- * 1000 0011
- */
- write_reg(info, CHA + CCR0, 0x83);
-
- /* CCR1
- *
- * 07..05 Reserved, 0
- * 04 ODS Output Driver Select, 1=TxD is push-pull output
- * 03 BCR Bit Clock Rate, 1=16x
- * 02..00 CM[2..0] Clock Mode, 111=BRG
- *
- * 0001 1111
- */
- write_reg(info, CHA + CCR1, 0x1f);
-
- /* CCR2 (channel A)
- *
- * 07..06 BGR[9..8] Baud rate bits 9..8
- * 05 BDF Baud rate divisor factor, 0=1, 1=BGR value
- * 04 SSEL Clock source select, 1=submode b
- * 03 TOE 0=TxCLK is input, 0=TxCLK is input
- * 02 RWX Read/Write Exchange 0=disabled
- * 01 Reserved, 0
- * 00 DIV, data inversion 0=disabled, 1=enabled
- *
- * 0001 0000
- */
- write_reg(info, CHA + CCR2, 0x10);
-
- /* CCR3
- *
- * 07..01 Reserved, 0
- * 00 PSD DPLL Phase Shift Disable
- *
- * 0000 0000
- */
- write_reg(info, CHA + CCR3, 0);
-
- /* CCR4
- *
- * 07 MCK4 Master Clock Divide by 4, 1=enabled
- * 06 EBRG Enhanced Baud Rate Generator Mode, 1=enabled
- * 05 TST1 Test Pin, 0=normal operation
- * 04 ICD Ivert Carrier Detect, 1=enabled (active low)
- * 03..00 Reserved, must be 0
- *
- * 0101 0000
- */
- write_reg(info, CHA + CCR4, 0x50);
- mgslpc_set_rate(info, CHA, info->params.data_rate * 16);
-
- /* DAFO Data Format
- *
- * 07 Reserved, 0
- * 06 XBRK transmit break, 0=normal operation
- * 05 Stop bits (0=1, 1=2)
- * 04..03 PAR[1..0] Parity (01=odd, 10=even)
- * 02 PAREN Parity Enable
- * 01..00 CHL[1..0] Character Length (00=8, 01=7)
- *
- */
- val = 0x00;
- if (info->params.data_bits != 8)
- val |= BIT0; /* 7 bits */
- if (info->params.stop_bits != 1)
- val |= BIT5;
- if (info->params.parity != ASYNC_PARITY_NONE)
- {
- val |= BIT2; /* Parity enable */
- if (info->params.parity == ASYNC_PARITY_ODD)
- val |= BIT3;
- else
- val |= BIT4;
- }
- write_reg(info, CHA + DAFO, val);
-
- /* RFC Rx FIFO Control
- *
- * 07 Reserved, 0
- * 06 DPS, 1=parity bit not stored in data byte
- * 05 DXS, 0=all data stored in FIFO (including XON/XOFF)
- * 04 RFDF Rx FIFO Data Format, 1=status byte stored in FIFO
- * 03..02 RFTH[1..0], rx threshold, 11=16 status + 16 data byte
- * 01 Reserved, 0
- * 00 TCDE Terminate Char Detect Enable, 0=disabled
- *
- * 0101 1100
- */
- write_reg(info, CHA + RFC, 0x5c);
-
- /* RLCR Receive length check register
- *
- * Max frame length = (RL + 1) * 32
- */
- write_reg(info, CHA + RLCR, 0);
-
- /* XBCH Transmit Byte Count High
- *
- * 07 DMA mode, 0 = interrupt driven
- * 06 NRM, 0=ABM (ignored)
- * 05 CAS Carrier Auto Start
- * 04 XC Transmit Continuously (ignored)
- * 03..00 XBC[10..8] Transmit byte count bits 10..8
- *
- * 0000 0000
- */
- val = 0x00;
- if (info->params.flags & HDLC_FLAG_AUTO_DCD)
- val |= BIT5;
- write_reg(info, CHA + XBCH, val);
- if (info->params.flags & HDLC_FLAG_AUTO_CTS)
- irq_enable(info, CHA, IRQ_CTS);
-
- /* MODE:03 RAC Receiver Active, 1=active */
- set_reg_bits(info, CHA + MODE, BIT3);
- enable_auxclk(info);
- if (info->params.flags & HDLC_FLAG_AUTO_CTS) {
- irq_enable(info, CHB, IRQ_CTS);
- /* PVR[3] 1=AUTO CTS active */
- set_reg_bits(info, CHA + PVR, BIT3);
- } else
- clear_reg_bits(info, CHA + PVR, BIT3);
- irq_enable(info, CHA,
- IRQ_RXEOM | IRQ_RXFIFO | IRQ_BREAK_ON | IRQ_RXTIME |
- IRQ_ALLSENT | IRQ_TXFIFO);
- issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
- wait_command_complete(info, CHA);
- read_reg16(info, CHA + ISR); /* clear pending IRQs */
-}
-
-/* Set the HDLC idle mode for the transmitter.
- */
-static void tx_set_idle(MGSLPC_INFO *info)
-{
- /* Note: ESCC2 only supports flags and one idle modes */
- if (info->idle_mode == HDLC_TXIDLE_FLAGS)
- set_reg_bits(info, CHA + CCR1, BIT3);
- else
- clear_reg_bits(info, CHA + CCR1, BIT3);
-}
-
-/* get state of the V24 status (input) signals.
- */
-static void get_signals(MGSLPC_INFO *info)
-{
- unsigned char status = 0;
-
- /* preserve RTS and DTR */
- info->serial_signals &= SerialSignal_RTS | SerialSignal_DTR;
-
- if (read_reg(info, CHB + VSTR) & BIT7)
- info->serial_signals |= SerialSignal_DCD;
- if (read_reg(info, CHB + STAR) & BIT1)
- info->serial_signals |= SerialSignal_CTS;
-
- status = read_reg(info, CHA + PVR);
- if (!(status & PVR_RI))
- info->serial_signals |= SerialSignal_RI;
- if (!(status & PVR_DSR))
- info->serial_signals |= SerialSignal_DSR;
-}
-
-/* Set the state of RTS and DTR based on contents of
- * serial_signals member of device extension.
- */
-static void set_signals(MGSLPC_INFO *info)
-{
- unsigned char val;
-
- val = read_reg(info, CHA + MODE);
- if (info->params.mode == MGSL_MODE_ASYNC) {
- if (info->serial_signals & SerialSignal_RTS)
- val &= ~BIT6;
- else
- val |= BIT6;
- } else {
- if (info->serial_signals & SerialSignal_RTS)
- val |= BIT2;
- else
- val &= ~BIT2;
- }
- write_reg(info, CHA + MODE, val);
-
- if (info->serial_signals & SerialSignal_DTR)
- clear_reg_bits(info, CHA + PVR, PVR_DTR);
- else
- set_reg_bits(info, CHA + PVR, PVR_DTR);
-}
-
-static void rx_reset_buffers(MGSLPC_INFO *info)
-{
- RXBUF *buf;
- int i;
-
- info->rx_put = 0;
- info->rx_get = 0;
- info->rx_frame_count = 0;
- for (i=0 ; i < info->rx_buf_count ; i++) {
- buf = (RXBUF*)(info->rx_buf + (i * info->rx_buf_size));
- buf->status = buf->count = 0;
- }
-}
-
-/* Attempt to return a received HDLC frame
- * Only frames received without errors are returned.
- *
- * Returns true if frame returned, otherwise false
- */
-static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty)
-{
- unsigned short status;
- RXBUF *buf;
- unsigned int framesize = 0;
- unsigned long flags;
- bool return_frame = false;
-
- if (info->rx_frame_count == 0)
- return false;
-
- buf = (RXBUF*)(info->rx_buf + (info->rx_get * info->rx_buf_size));
-
- status = buf->status;
-
- /* 07 VFR 1=valid frame
- * 06 RDO 1=data overrun
- * 05 CRC 1=OK, 0=error
- * 04 RAB 1=frame aborted
- */
- if ((status & 0xf0) != 0xA0) {
- if (!(status & BIT7) || (status & BIT4))
- info->icount.rxabort++;
- else if (status & BIT6)
- info->icount.rxover++;
- else if (!(status & BIT5)) {
- info->icount.rxcrc++;
- if (info->params.crc_type & HDLC_CRC_RETURN_EX)
- return_frame = true;
- }
- framesize = 0;
-#if SYNCLINK_GENERIC_HDLC
- {
- info->netdev->stats.rx_errors++;
- info->netdev->stats.rx_frame_errors++;
- }
-#endif
- } else
- return_frame = true;
-
- if (return_frame)
- framesize = buf->count;
-
- if (debug_level >= DEBUG_LEVEL_BH)
- printk("%s(%d):rx_get_frame(%s) status=%04X size=%d\n",
- __FILE__, __LINE__, info->device_name, status, framesize);
-
- if (debug_level >= DEBUG_LEVEL_DATA)
- trace_block(info, buf->data, framesize, 0);
-
- if (framesize) {
- if ((info->params.crc_type & HDLC_CRC_RETURN_EX &&
- framesize+1 > info->max_frame_size) ||
- framesize > info->max_frame_size)
- info->icount.rxlong++;
- else {
- if (status & BIT5)
- info->icount.rxok++;
-
- if (info->params.crc_type & HDLC_CRC_RETURN_EX) {
- *(buf->data + framesize) = status & BIT5 ? RX_OK:RX_CRC_ERROR;
- ++framesize;
- }
-
-#if SYNCLINK_GENERIC_HDLC
- if (info->netcount)
- hdlcdev_rx(info, buf->data, framesize);
- else
-#endif
- ldisc_receive_buf(tty, buf->data, info->flag_buf, framesize);
- }
- }
-
- spin_lock_irqsave(&info->lock, flags);
- buf->status = buf->count = 0;
- info->rx_frame_count--;
- info->rx_get++;
- if (info->rx_get >= info->rx_buf_count)
- info->rx_get = 0;
- spin_unlock_irqrestore(&info->lock, flags);
-
- return true;
-}
-
-static bool register_test(MGSLPC_INFO *info)
-{
- static unsigned char patterns[] =
- { 0x00, 0xff, 0xaa, 0x55, 0x69, 0x96, 0x0f };
- static unsigned int count = ARRAY_SIZE(patterns);
- unsigned int i;
- bool rc = true;
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- reset_device(info);
-
- for (i = 0; i < count; i++) {
- write_reg(info, XAD1, patterns[i]);
- write_reg(info, XAD2, patterns[(i + 1) % count]);
- if ((read_reg(info, XAD1) != patterns[i]) ||
- (read_reg(info, XAD2) != patterns[(i + 1) % count])) {
- rc = false;
- break;
- }
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
- return rc;
-}
-
-static bool irq_test(MGSLPC_INFO *info)
-{
- unsigned long end_time;
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- reset_device(info);
-
- info->testing_irq = true;
- hdlc_mode(info);
-
- info->irq_occurred = false;
-
- /* init hdlc mode */
-
- irq_enable(info, CHA, IRQ_TIMER);
- write_reg(info, CHA + TIMR, 0); /* 512 cycles */
- issue_command(info, CHA, CMD_START_TIMER);
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- end_time=100;
- while(end_time-- && !info->irq_occurred) {
- msleep_interruptible(10);
- }
-
- info->testing_irq = false;
-
- spin_lock_irqsave(&info->lock, flags);
- reset_device(info);
- spin_unlock_irqrestore(&info->lock, flags);
-
- return info->irq_occurred;
-}
-
-static int adapter_test(MGSLPC_INFO *info)
-{
- if (!register_test(info)) {
- info->init_error = DiagStatus_AddressFailure;
- printk("%s(%d):Register test failure for device %s Addr=%04X\n",
- __FILE__, __LINE__, info->device_name, (unsigned short)(info->io_base));
- return -ENODEV;
- }
-
- if (!irq_test(info)) {
- info->init_error = DiagStatus_IrqFailure;
- printk("%s(%d):Interrupt test failure for device %s IRQ=%d\n",
- __FILE__, __LINE__, info->device_name, (unsigned short)(info->irq_level));
- return -ENODEV;
- }
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):device %s passed diagnostics\n",
- __FILE__, __LINE__, info->device_name);
- return 0;
-}
-
-static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit)
-{
- int i;
- int linecount;
- if (xmit)
- printk("%s tx data:\n", info->device_name);
- else
- printk("%s rx data:\n", info->device_name);
-
- while(count) {
- if (count > 16)
- linecount = 16;
- else
- linecount = count;
-
- for(i=0;i<linecount;i++)
- printk("%02X ", (unsigned char)data[i]);
- for(;i<17;i++)
- printk(" ");
- for(i=0;i<linecount;i++) {
- if (data[i]>=040 && data[i]<=0176)
- printk("%c", data[i]);
- else
- printk(".");
- }
- printk("\n");
-
- data += linecount;
- count -= linecount;
- }
-}
-
-/* HDLC frame time out
- * update stats and do tx completion processing
- */
-static void tx_timeout(struct timer_list *t)
-{
- MGSLPC_INFO *info = from_timer(info, t, tx_timer);
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):tx_timeout(%s)\n",
- __FILE__, __LINE__, info->device_name);
- if (info->tx_active &&
- info->params.mode == MGSL_MODE_HDLC) {
- info->icount.txtimeout++;
- }
- spin_lock_irqsave(&info->lock, flags);
- info->tx_active = false;
- info->tx_count = info->tx_put = info->tx_get = 0;
-
- spin_unlock_irqrestore(&info->lock, flags);
-
-#if SYNCLINK_GENERIC_HDLC
- if (info->netcount)
- hdlcdev_tx_done(info);
- else
-#endif
- {
- struct tty_struct *tty = tty_port_tty_get(&info->port);
- bh_transmit(info, tty);
- tty_kref_put(tty);
- }
-}
-
-#if SYNCLINK_GENERIC_HDLC
-
-/**
- * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
- * set encoding and frame check sequence (FCS) options
- *
- * dev pointer to network device structure
- * encoding serial encoding setting
- * parity FCS setting
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
- unsigned short parity)
-{
- MGSLPC_INFO *info = dev_to_port(dev);
- struct tty_struct *tty;
- unsigned char new_encoding;
- unsigned short new_crctype;
-
- /* return error if TTY interface open */
- if (info->port.count)
- return -EBUSY;
-
- switch (encoding)
- {
- case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break;
- case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
- case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
- case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
- case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
- default: return -EINVAL;
- }
-
- switch (parity)
- {
- case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break;
- case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
- case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
- default: return -EINVAL;
- }
-
- info->params.encoding = new_encoding;
- info->params.crc_type = new_crctype;
-
- /* if network interface up, reprogram hardware */
- if (info->netcount) {
- tty = tty_port_tty_get(&info->port);
- mgslpc_program_hw(info, tty);
- tty_kref_put(tty);
- }
-
- return 0;
-}
-
-/**
- * called by generic HDLC layer to send frame
- *
- * skb socket buffer containing HDLC frame
- * dev pointer to network device structure
- */
-static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- MGSLPC_INFO *info = dev_to_port(dev);
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk(KERN_INFO "%s:hdlc_xmit(%s)\n", __FILE__, dev->name);
-
- /* stop sending until this frame completes */
- netif_stop_queue(dev);
-
- /* copy data to device buffers */
- skb_copy_from_linear_data(skb, info->tx_buf, skb->len);
- info->tx_get = 0;
- info->tx_put = info->tx_count = skb->len;
-
- /* update network statistics */
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- /* done with socket buffer, so free it */
- dev_kfree_skb(skb);
-
- /* save start time for transmit timeout detection */
- netif_trans_update(dev);
-
- /* start hardware transmitter if necessary */
- spin_lock_irqsave(&info->lock, flags);
- if (!info->tx_active) {
- struct tty_struct *tty = tty_port_tty_get(&info->port);
- tx_start(info, tty);
- tty_kref_put(tty);
- }
- spin_unlock_irqrestore(&info->lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-/**
- * called by network layer when interface enabled
- * claim resources and initialize hardware
- *
- * dev pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_open(struct net_device *dev)
-{
- MGSLPC_INFO *info = dev_to_port(dev);
- struct tty_struct *tty;
- int rc;
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s:hdlcdev_open(%s)\n", __FILE__, dev->name);
-
- /* generic HDLC layer open processing */
- rc = hdlc_open(dev);
- if (rc != 0)
- return rc;
-
- /* arbitrate between network and tty opens */
- spin_lock_irqsave(&info->netlock, flags);
- if (info->port.count != 0 || info->netcount != 0) {
- printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
- spin_unlock_irqrestore(&info->netlock, flags);
- return -EBUSY;
- }
- info->netcount=1;
- spin_unlock_irqrestore(&info->netlock, flags);
-
- tty = tty_port_tty_get(&info->port);
- /* claim resources and init adapter */
- rc = startup(info, tty);
- if (rc != 0) {
- tty_kref_put(tty);
- spin_lock_irqsave(&info->netlock, flags);
- info->netcount=0;
- spin_unlock_irqrestore(&info->netlock, flags);
- return rc;
- }
- /* assert RTS and DTR, apply hardware settings */
- info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR;
- mgslpc_program_hw(info, tty);
- tty_kref_put(tty);
-
- /* enable network layer transmit */
- netif_trans_update(dev);
- netif_start_queue(dev);
-
- /* inform generic HDLC layer of current DCD status */
- spin_lock_irqsave(&info->lock, flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock, flags);
- if (info->serial_signals & SerialSignal_DCD)
- netif_carrier_on(dev);
- else
- netif_carrier_off(dev);
- return 0;
-}
-
-/**
- * called by network layer when interface is disabled
- * shutdown hardware and release resources
- *
- * dev pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_close(struct net_device *dev)
-{
- MGSLPC_INFO *info = dev_to_port(dev);
- struct tty_struct *tty = tty_port_tty_get(&info->port);
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s:hdlcdev_close(%s)\n", __FILE__, dev->name);
-
- netif_stop_queue(dev);
-
- /* shutdown adapter and release resources */
- shutdown(info, tty);
- tty_kref_put(tty);
- hdlc_close(dev);
-
- spin_lock_irqsave(&info->netlock, flags);
- info->netcount=0;
- spin_unlock_irqrestore(&info->netlock, flags);
-
- return 0;
-}
-
-/**
- * called by network layer to process IOCTL call to network device
- *
- * dev pointer to network device structure
- * ifs pointer to network interface settings structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_wan_ioctl(struct net_device *dev, struct if_settings *ifs)
-{
- const size_t size = sizeof(sync_serial_settings);
- sync_serial_settings new_line;
- sync_serial_settings __user *line = ifs->ifs_ifsu.sync;
- MGSLPC_INFO *info = dev_to_port(dev);
- unsigned int flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s:hdlcdev_ioctl(%s)\n", __FILE__, dev->name);
-
- /* return error if TTY interface open */
- if (info->port.count)
- return -EBUSY;
-
- memset(&new_line, 0, size);
-
- switch (ifs->type) {
- case IF_GET_IFACE: /* return current sync_serial_settings */
-
- ifs->type = IF_IFACE_SYNC_SERIAL;
- if (ifs->size < size) {
- ifs->size = size; /* data size wanted */
- return -ENOBUFS;
- }
-
- flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
- HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN |
- HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
- HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN);
-
- switch (flags){
- case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
- case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break;
- case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break;
- case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
- default: new_line.clock_type = CLOCK_DEFAULT;
- }
-
- new_line.clock_rate = info->params.clock_speed;
- new_line.loopback = info->params.loopback ? 1:0;
-
- if (copy_to_user(line, &new_line, size))
- return -EFAULT;
- return 0;
-
- case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
-
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
- if (copy_from_user(&new_line, line, size))
- return -EFAULT;
-
- switch (new_line.clock_type)
- {
- case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
- case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
- case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break;
- case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break;
- case CLOCK_DEFAULT: flags = info->params.flags &
- (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
- HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN |
- HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
- HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break;
- default: return -EINVAL;
- }
-
- if (new_line.loopback != 0 && new_line.loopback != 1)
- return -EINVAL;
-
- info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
- HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN |
- HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
- HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN);
- info->params.flags |= flags;
-
- info->params.loopback = new_line.loopback;
-
- if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
- info->params.clock_speed = new_line.clock_rate;
- else
- info->params.clock_speed = 0;
-
- /* if network interface up, reprogram hardware */
- if (info->netcount) {
- struct tty_struct *tty = tty_port_tty_get(&info->port);
- mgslpc_program_hw(info, tty);
- tty_kref_put(tty);
- }
- return 0;
- default:
- return hdlc_ioctl(dev, ifs);
- }
-}
-
-/**
- * called by network layer when transmit timeout is detected
- *
- * dev pointer to network device structure
- */
-static void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- MGSLPC_INFO *info = dev_to_port(dev);
- unsigned long flags;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("hdlcdev_tx_timeout(%s)\n", dev->name);
-
- dev->stats.tx_errors++;
- dev->stats.tx_aborted_errors++;
-
- spin_lock_irqsave(&info->lock, flags);
- tx_stop(info);
- spin_unlock_irqrestore(&info->lock, flags);
-
- netif_wake_queue(dev);
-}
-
-/**
- * called by device driver when transmit completes
- * reenable network layer transmit if stopped
- *
- * info pointer to device instance information
- */
-static void hdlcdev_tx_done(MGSLPC_INFO *info)
-{
- if (netif_queue_stopped(info->netdev))
- netif_wake_queue(info->netdev);
-}
-
-/**
- * called by device driver when frame received
- * pass frame to network layer
- *
- * info pointer to device instance information
- * buf pointer to buffer contianing frame data
- * size count of data bytes in buf
- */
-static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size)
-{
- struct sk_buff *skb = dev_alloc_skb(size);
- struct net_device *dev = info->netdev;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("hdlcdev_rx(%s)\n", dev->name);
-
- if (skb == NULL) {
- printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name);
- dev->stats.rx_dropped++;
- return;
- }
-
- skb_put_data(skb, buf, size);
-
- skb->protocol = hdlc_type_trans(skb, dev);
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += size;
-
- netif_rx(skb);
-}
-
-static const struct net_device_ops hdlcdev_ops = {
- .ndo_open = hdlcdev_open,
- .ndo_stop = hdlcdev_close,
- .ndo_start_xmit = hdlc_start_xmit,
- .ndo_siocwandev = hdlcdev_wan_ioctl,
- .ndo_tx_timeout = hdlcdev_tx_timeout,
-};
-
-/**
- * called by device driver when adding device instance
- * do generic HDLC initialization
- *
- * info pointer to device instance information
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_init(MGSLPC_INFO *info)
-{
- int rc;
- struct net_device *dev;
- hdlc_device *hdlc;
-
- /* allocate and initialize network and HDLC layer objects */
-
- dev = alloc_hdlcdev(info);
- if (dev == NULL) {
- printk(KERN_ERR "%s:hdlc device allocation failure\n", __FILE__);
- return -ENOMEM;
- }
-
- /* for network layer reporting purposes only */
- dev->base_addr = info->io_base;
- dev->irq = info->irq_level;
-
- /* network layer callbacks and settings */
- dev->netdev_ops = &hdlcdev_ops;
- dev->watchdog_timeo = 10 * HZ;
- dev->tx_queue_len = 50;
-
- /* generic HDLC layer callbacks and settings */
- hdlc = dev_to_hdlc(dev);
- hdlc->attach = hdlcdev_attach;
- hdlc->xmit = hdlcdev_xmit;
-
- /* register objects with HDLC layer */
- rc = register_hdlc_device(dev);
- if (rc) {
- printk(KERN_WARNING "%s:unable to register hdlc device\n", __FILE__);
- free_netdev(dev);
- return rc;
- }
-
- info->netdev = dev;
- return 0;
-}
-
-/**
- * called by device driver when removing device instance
- * do generic HDLC cleanup
- *
- * info pointer to device instance information
- */
-static void hdlcdev_exit(MGSLPC_INFO *info)
-{
- unregister_hdlc_device(info->netdev);
- free_netdev(info->netdev);
- info->netdev = NULL;
-}
-
-#endif /* CONFIG_HDLC */
-
diff --git a/drivers/char/powernv-op-panel.c b/drivers/char/powernv-op-panel.c
index 3c99696b145e..53467b0a6187 100644
--- a/drivers/char/powernv-op-panel.c
+++ b/drivers/char/powernv-op-panel.c
@@ -195,12 +195,11 @@ free_oppanel_data:
return rc;
}
-static int oppanel_remove(struct platform_device *pdev)
+static void oppanel_remove(struct platform_device *pdev)
{
misc_deregister(&oppanel_dev);
kfree(oppanel_lines);
kfree(oppanel_data);
- return 0;
}
static const struct of_device_id oppanel_match[] = {
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 38b46c7d1737..d1dfbd8d4d42 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -296,28 +296,35 @@ static int register_device(int minor, struct pp_struct *pp)
if (!port) {
pr_warn("%s: no associated port!\n", name);
rc = -ENXIO;
- goto err;
+ goto err_free_name;
+ }
+
+ index = ida_alloc(&ida_index, GFP_KERNEL);
+ if (index < 0) {
+ pr_warn("%s: failed to get index!\n", name);
+ rc = index;
+ goto err_put_port;
}
- index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
memset(&ppdev_cb, 0, sizeof(ppdev_cb));
ppdev_cb.irq_func = pp_irq;
ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
ppdev_cb.private = pp;
pdev = parport_register_dev_model(port, name, &ppdev_cb, index);
- parport_put_port(port);
if (!pdev) {
pr_warn("%s: failed to register device!\n", name);
rc = -ENXIO;
- ida_simple_remove(&ida_index, index);
- goto err;
+ ida_free(&ida_index, index);
+ goto err_put_port;
}
pp->pdev = pdev;
pp->index = index;
dev_dbg(&pdev->dev, "registered pardevice\n");
-err:
+err_put_port:
+ parport_put_port(port);
+err_free_name:
kfree(name);
return rc;
}
@@ -750,7 +757,7 @@ static int pp_release(struct inode *inode, struct file *file)
if (pp->pdev) {
parport_unregister_device(pp->pdev);
- ida_simple_remove(&ida_index, pp->index);
+ ida_free(&ida_index, pp->index);
pp->pdev = NULL;
pr_debug(CHRDEV "%x: unregistered pardevice\n", minor);
}
@@ -773,11 +780,12 @@ static __poll_t pp_poll(struct file *file, poll_table *wait)
return mask;
}
-static struct class *ppdev_class;
+static const struct class ppdev_class = {
+ .name = CHRDEV,
+};
static const struct file_operations pp_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = pp_read,
.write = pp_write,
.poll = pp_poll,
@@ -794,7 +802,7 @@ static void pp_attach(struct parport *port)
if (devices[port->number])
return;
- ret = device_create(ppdev_class, port->dev,
+ ret = device_create(&ppdev_class, port->dev,
MKDEV(PP_MAJOR, port->number), NULL,
"parport%d", port->number);
if (IS_ERR(ret)) {
@@ -810,7 +818,7 @@ static void pp_detach(struct parport *port)
if (!devices[port->number])
return;
- device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
+ device_destroy(&ppdev_class, MKDEV(PP_MAJOR, port->number));
devices[port->number] = NULL;
}
@@ -830,7 +838,6 @@ static struct parport_driver pp_driver = {
.probe = pp_probe,
.match_port = pp_attach,
.detach = pp_detach,
- .devmodel = true,
};
static int __init ppdev_init(void)
@@ -841,11 +848,10 @@ static int __init ppdev_init(void)
pr_warn(CHRDEV ": unable to get major %d\n", PP_MAJOR);
return -EIO;
}
- ppdev_class = class_create(THIS_MODULE, CHRDEV);
- if (IS_ERR(ppdev_class)) {
- err = PTR_ERR(ppdev_class);
+ err = class_register(&ppdev_class);
+ if (err)
goto out_chrdev;
- }
+
err = parport_register_driver(&pp_driver);
if (err < 0) {
pr_warn(CHRDEV ": unable to register with parport\n");
@@ -856,7 +862,7 @@ static int __init ppdev_init(void)
goto out;
out_class:
- class_destroy(ppdev_class);
+ class_unregister(&ppdev_class);
out_chrdev:
unregister_chrdev(PP_MAJOR, CHRDEV);
out:
@@ -867,12 +873,13 @@ static void __exit ppdev_cleanup(void)
{
/* Clean up all parport stuff */
parport_unregister_driver(&pp_driver);
- class_destroy(ppdev_class);
+ class_unregister(&ppdev_class);
unregister_chrdev(PP_MAJOR, CHRDEV);
}
module_init(ppdev_init);
module_exit(ppdev_cleanup);
+MODULE_DESCRIPTION("Support for user-space parallel port device drivers");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(PP_MAJOR);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 79d7d4e4e582..bab03c7c4194 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
- * Copyright (C) 2017-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (C) 2017-2024 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright Matt Mackall <mpm@selenic.com>, 2003, 2004, 2005
* Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All rights reserved.
*
@@ -53,8 +53,15 @@
#include <linux/uaccess.h>
#include <linux/suspend.h>
#include <linux/siphash.h>
+#include <linux/sched/isolation.h>
#include <crypto/chacha.h>
#include <crypto/blake2s.h>
+#ifdef CONFIG_VDSO_GETRANDOM
+#include <vdso/getrandom.h>
+#include <vdso/datapage.h>
+#include <vdso/vsyscall.h>
+#endif
+#include <asm/archrandom.h>
#include <asm/processor.h>
#include <asm/irq.h>
#include <asm/irq_regs.h>
@@ -84,6 +91,7 @@ static DEFINE_STATIC_KEY_FALSE(crng_is_ready);
/* Various types of waiters for crng_init->CRNG_READY transition. */
static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait);
static struct fasync_struct *fasync;
+static ATOMIC_NOTIFIER_HEAD(random_ready_notifier);
/* Control how we warn userspace. */
static struct ratelimit_state urandom_warning =
@@ -96,8 +104,8 @@ MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
/*
* Returns whether or not the input pool has been seeded and thus guaranteed
* to supply cryptographically secure random numbers. This applies to: the
- * /dev/urandom device, the get_random_bytes function, and the get_random_{u32,
- * ,u64,int,long} family of functions.
+ * /dev/urandom device, the get_random_bytes function, and the get_random_{u8,
+ * u16,u32,u64,long} family of functions.
*
* Returns: true if the input pool has been seeded.
* false if the input pool has not been seeded.
@@ -119,9 +127,9 @@ static void try_to_generate_entropy(void);
/*
* Wait for the input pool to be seeded and thus guaranteed to supply
* cryptographically secure random numbers. This applies to: the /dev/urandom
- * device, the get_random_bytes function, and the get_random_{u32,u64,int,long}
- * family of functions. Using any of these functions without first calling
- * this function forfeits the guarantee of security.
+ * device, the get_random_bytes function, and the get_random_{u8,u16,u32,u64,
+ * long} family of functions. Using any of these functions without first
+ * calling this function forfeits the guarantee of security.
*
* Returns: 0 if the input pool has been seeded.
* -ERESTARTSYS if the function was interrupted by a signal.
@@ -140,6 +148,26 @@ int wait_for_random_bytes(void)
}
EXPORT_SYMBOL(wait_for_random_bytes);
+/*
+ * Add a callback function that will be invoked when the crng is initialised,
+ * or immediately if it already has been. Only use this is you are absolutely
+ * sure it is required. Most users should instead be able to test
+ * `rng_is_initialized()` on demand, or make use of `get_random_bytes_wait()`.
+ */
+int __cold execute_with_initialized_rng(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&random_ready_notifier.lock, flags);
+ if (crng_ready())
+ nb->notifier_call(nb, 0, NULL);
+ else
+ ret = raw_notifier_chain_register((struct raw_notifier_head *)&random_ready_notifier.head, nb);
+ spin_unlock_irqrestore(&random_ready_notifier.lock, flags);
+ return ret;
+}
+
#define warn_unseeded_randomness() \
if (IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) && !crng_ready()) \
printk_deferred(KERN_NOTICE "random: %s called from %pS with crng_init=%d\n", \
@@ -157,14 +185,18 @@ EXPORT_SYMBOL(wait_for_random_bytes);
* There are a few exported interfaces for use by other drivers:
*
* void get_random_bytes(void *buf, size_t len)
+ * u8 get_random_u8()
+ * u16 get_random_u16()
* u32 get_random_u32()
+ * u32 get_random_u32_below(u32 ceil)
+ * u32 get_random_u32_above(u32 floor)
+ * u32 get_random_u32_inclusive(u32 floor, u32 ceil)
* u64 get_random_u64()
- * unsigned int get_random_int()
* unsigned long get_random_long()
*
* These interfaces will return the requested number of random bytes
* into the given buffer or as a return value. This is equivalent to
- * a read from /dev/urandom. The u32, u64, int, and long family of
+ * a read from /dev/urandom. The u8, u16, u32, u64, long family of
* functions may be higher performance for one-off random integers,
* because they do a bit of buffering and do not invoke reseeding
* until the buffer is emptied.
@@ -178,7 +210,6 @@ enum {
static struct {
u8 key[CHACHA_KEY_SIZE] __aligned(__alignof__(long));
- unsigned long birth;
unsigned long generation;
spinlock_t lock;
} base_crng = {
@@ -196,16 +227,41 @@ static DEFINE_PER_CPU(struct crng, crngs) = {
.lock = INIT_LOCAL_LOCK(crngs.lock),
};
+/*
+ * Return the interval until the next reseeding, which is normally
+ * CRNG_RESEED_INTERVAL, but during early boot, it is at an interval
+ * proportional to the uptime.
+ */
+static unsigned int crng_reseed_interval(void)
+{
+ static bool early_boot = true;
+
+ if (unlikely(READ_ONCE(early_boot))) {
+ time64_t uptime = ktime_get_seconds();
+ if (uptime >= CRNG_RESEED_INTERVAL / HZ * 2)
+ WRITE_ONCE(early_boot, false);
+ else
+ return max_t(unsigned int, CRNG_RESEED_START_INTERVAL,
+ (unsigned int)uptime / 2 * HZ);
+ }
+ return CRNG_RESEED_INTERVAL;
+}
+
/* Used by crng_reseed() and crng_make_state() to extract a new seed from the input pool. */
static void extract_entropy(void *buf, size_t len);
/* This extracts a new crng key from the input pool. */
-static void crng_reseed(void)
+static void crng_reseed(struct work_struct *work)
{
+ static DECLARE_DELAYED_WORK(next_reseed, crng_reseed);
unsigned long flags;
unsigned long next_gen;
u8 key[CHACHA_KEY_SIZE];
+ /* Immediately schedule the next reseeding, so that it fires sooner rather than later. */
+ if (likely(system_dfl_wq))
+ queue_delayed_work(system_dfl_wq, &next_reseed, crng_reseed_interval());
+
extract_entropy(key, sizeof(key));
/*
@@ -220,7 +276,22 @@ static void crng_reseed(void)
if (next_gen == ULONG_MAX)
++next_gen;
WRITE_ONCE(base_crng.generation, next_gen);
- WRITE_ONCE(base_crng.birth, jiffies);
+#ifdef CONFIG_VDSO_GETRANDOM
+ /* base_crng.generation's invalid value is ULONG_MAX, while
+ * vdso_k_rng_data->generation's invalid value is 0, so add one to the
+ * former to arrive at the latter. Use smp_store_release so that this
+ * is ordered with the write above to base_crng.generation. Pairs with
+ * the smp_rmb() before the syscall in the vDSO code.
+ *
+ * Cast to unsigned long for 32-bit architectures, since atomic 64-bit
+ * operations are not supported on those architectures. This is safe
+ * because base_crng.generation is a 32-bit value. On big-endian
+ * architectures it will be stored in the upper 32 bits, but that's okay
+ * because the vDSO side only checks whether the value changed, without
+ * actually using or interpreting the value.
+ */
+ smp_store_release((unsigned long *)&vdso_k_rng_data->generation, next_gen + 1);
+#endif
if (!static_branch_likely(&crng_is_ready))
crng_init = CRNG_READY;
spin_unlock_irqrestore(&base_crng.lock, flags);
@@ -238,11 +309,11 @@ static void crng_reseed(void)
* key value, at index 4, so the state should always be zeroed out
* immediately after using in order to maintain forward secrecy.
* If the state cannot be erased in a timely manner, then it is
- * safer to set the random_data parameter to &chacha_state[4] so
- * that this function overwrites it before returning.
+ * safer to set the random_data parameter to &chacha_state->x[4]
+ * so that this function overwrites it before returning.
*/
static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE],
- u32 chacha_state[CHACHA_STATE_WORDS],
+ struct chacha_state *chacha_state,
u8 *random_data, size_t random_data_len)
{
u8 first_block[CHACHA_BLOCK_SIZE];
@@ -250,8 +321,8 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE],
BUG_ON(random_data_len > 32);
chacha_init_consts(chacha_state);
- memcpy(&chacha_state[4], key, CHACHA_KEY_SIZE);
- memset(&chacha_state[12], 0, sizeof(u32) * 4);
+ memcpy(&chacha_state->x[4], key, CHACHA_KEY_SIZE);
+ memset(&chacha_state->x[12], 0, sizeof(u32) * 4);
chacha20_block(chacha_state, first_block);
memcpy(key, first_block, CHACHA_KEY_SIZE);
@@ -260,33 +331,11 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE],
}
/*
- * Return whether the crng seed is considered to be sufficiently old
- * that a reseeding is needed. This happens if the last reseeding
- * was CRNG_RESEED_INTERVAL ago, or during early boot, at an interval
- * proportional to the uptime.
- */
-static bool crng_has_old_seed(void)
-{
- static bool early_boot = true;
- unsigned long interval = CRNG_RESEED_INTERVAL;
-
- if (unlikely(READ_ONCE(early_boot))) {
- time64_t uptime = ktime_get_seconds();
- if (uptime >= CRNG_RESEED_INTERVAL / HZ * 2)
- WRITE_ONCE(early_boot, false);
- else
- interval = max_t(unsigned int, CRNG_RESEED_START_INTERVAL,
- (unsigned int)uptime / 2 * HZ);
- }
- return time_is_before_jiffies(READ_ONCE(base_crng.birth) + interval);
-}
-
-/*
* This function returns a ChaCha state that you may use for generating
* random data. It also returns up to 32 bytes on its own of random data
* that may be used; random_data_len may not be greater than 32.
*/
-static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS],
+static void crng_make_state(struct chacha_state *chacha_state,
u8 *random_data, size_t random_data_len)
{
unsigned long flags;
@@ -316,13 +365,6 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS],
return;
}
- /*
- * If the base_crng is old enough, we reseed, which in turn bumps the
- * generation counter that we check below.
- */
- if (unlikely(crng_has_old_seed()))
- crng_reseed();
-
local_lock_irqsave(&crngs.lock, flags);
crng = raw_cpu_ptr(&crngs);
@@ -353,7 +395,7 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS],
static void _get_random_bytes(void *buf, size_t len)
{
- u32 chacha_state[CHACHA_STATE_WORDS];
+ struct chacha_state chacha_state;
u8 tmp[CHACHA_BLOCK_SIZE];
size_t first_block_len;
@@ -361,34 +403,34 @@ static void _get_random_bytes(void *buf, size_t len)
return;
first_block_len = min_t(size_t, 32, len);
- crng_make_state(chacha_state, buf, first_block_len);
+ crng_make_state(&chacha_state, buf, first_block_len);
len -= first_block_len;
buf += first_block_len;
while (len) {
if (len < CHACHA_BLOCK_SIZE) {
- chacha20_block(chacha_state, tmp);
+ chacha20_block(&chacha_state, tmp);
memcpy(buf, tmp, len);
memzero_explicit(tmp, sizeof(tmp));
break;
}
- chacha20_block(chacha_state, buf);
- if (unlikely(chacha_state[12] == 0))
- ++chacha_state[13];
+ chacha20_block(&chacha_state, buf);
+ if (unlikely(chacha_state.x[12] == 0))
+ ++chacha_state.x[13];
len -= CHACHA_BLOCK_SIZE;
buf += CHACHA_BLOCK_SIZE;
}
- memzero_explicit(chacha_state, sizeof(chacha_state));
+ chacha_zeroize_state(&chacha_state);
}
/*
- * This function is the exported kernel interface. It returns some
- * number of good random numbers, suitable for key generation, seeding
- * TCP sequence numbers, etc. In order to ensure that the randomness
- * by this function is okay, the function wait_for_random_bytes()
- * should be called and return 0 at least once at any point prior.
+ * This returns random bytes in arbitrary quantities. The quality of the
+ * random bytes is as good as /dev/urandom. In order to ensure that the
+ * randomness provided by this function is okay, the function
+ * wait_for_random_bytes() should be called and return 0 at least once
+ * at any point prior.
*/
void get_random_bytes(void *buf, size_t len)
{
@@ -399,7 +441,7 @@ EXPORT_SYMBOL(get_random_bytes);
static ssize_t get_random_bytes_user(struct iov_iter *iter)
{
- u32 chacha_state[CHACHA_STATE_WORDS];
+ struct chacha_state chacha_state;
u8 block[CHACHA_BLOCK_SIZE];
size_t ret = 0, copied;
@@ -411,21 +453,22 @@ static ssize_t get_random_bytes_user(struct iov_iter *iter)
* bytes, in case userspace causes copy_to_iter() below to sleep
* forever, so that we still retain forward secrecy in that case.
*/
- crng_make_state(chacha_state, (u8 *)&chacha_state[4], CHACHA_KEY_SIZE);
+ crng_make_state(&chacha_state, (u8 *)&chacha_state.x[4],
+ CHACHA_KEY_SIZE);
/*
* However, if we're doing a read of len <= 32, we don't need to
* use chacha_state after, so we can simply return those bytes to
* the user directly.
*/
if (iov_iter_count(iter) <= CHACHA_KEY_SIZE) {
- ret = copy_to_iter(&chacha_state[4], CHACHA_KEY_SIZE, iter);
+ ret = copy_to_iter(&chacha_state.x[4], CHACHA_KEY_SIZE, iter);
goto out_zero_chacha;
}
for (;;) {
- chacha20_block(chacha_state, block);
- if (unlikely(chacha_state[12] == 0))
- ++chacha_state[13];
+ chacha20_block(&chacha_state, block);
+ if (unlikely(chacha_state.x[12] == 0))
+ ++chacha_state.x[13];
copied = copy_to_iter(block, sizeof(block), iter);
ret += copied;
@@ -442,13 +485,13 @@ static ssize_t get_random_bytes_user(struct iov_iter *iter)
memzero_explicit(block, sizeof(block));
out_zero_chacha:
- memzero_explicit(chacha_state, sizeof(chacha_state));
+ chacha_zeroize_state(&chacha_state);
return ret ? ret : -EFAULT;
}
/*
* Batched entropy returns random integers. The quality of the random
- * number is good as /dev/urandom. In order to ensure that the randomness
+ * number is as good as /dev/urandom. In order to ensure that the randomness
* provided by this function is okay, the function wait_for_random_bytes()
* should be called and return 0 at least once at any point prior.
*/
@@ -506,8 +549,45 @@ type get_random_ ##type(void) \
} \
EXPORT_SYMBOL(get_random_ ##type);
-DEFINE_BATCHED_ENTROPY(u64)
+DEFINE_BATCHED_ENTROPY(u8)
+DEFINE_BATCHED_ENTROPY(u16)
DEFINE_BATCHED_ENTROPY(u32)
+DEFINE_BATCHED_ENTROPY(u64)
+
+u32 __get_random_u32_below(u32 ceil)
+{
+ /*
+ * This is the slow path for variable ceil. It is still fast, most of
+ * the time, by doing traditional reciprocal multiplication and
+ * opportunistically comparing the lower half to ceil itself, before
+ * falling back to computing a larger bound, and then rejecting samples
+ * whose lower half would indicate a range indivisible by ceil. The use
+ * of `-ceil % ceil` is analogous to `2^32 % ceil`, but is computable
+ * in 32-bits.
+ */
+ u32 rand = get_random_u32();
+ u64 mult;
+
+ /*
+ * This function is technically undefined for ceil == 0, and in fact
+ * for the non-underscored constant version in the header, we build bug
+ * on that. But for the non-constant case, it's convenient to have that
+ * evaluate to being a straight call to get_random_u32(), so that
+ * get_random_u32_inclusive() can work over its whole range without
+ * undefined behavior.
+ */
+ if (unlikely(!ceil))
+ return rand;
+
+ mult = (u64)ceil * rand;
+ if (unlikely((u32)mult < ceil)) {
+ u32 bound = -ceil % ceil;
+ while (unlikely((u32)mult < bound))
+ mult = (u64)ceil * get_random_u32();
+ }
+ return mult >> 32;
+}
+EXPORT_SYMBOL(__get_random_u32_below);
#ifdef CONFIG_SMP
/*
@@ -522,6 +602,8 @@ int __cold random_prepare_cpu(unsigned int cpu)
* randomness.
*/
per_cpu_ptr(&crngs, cpu)->generation = ULONG_MAX;
+ per_cpu_ptr(&batched_entropy_u8, cpu)->position = UINT_MAX;
+ per_cpu_ptr(&batched_entropy_u16, cpu)->position = UINT_MAX;
per_cpu_ptr(&batched_entropy_u32, cpu)->position = UINT_MAX;
per_cpu_ptr(&batched_entropy_u64, cpu)->position = UINT_MAX;
return 0;
@@ -554,7 +636,7 @@ enum {
};
static struct {
- struct blake2s_state hash;
+ struct blake2s_ctx hash;
spinlock_t lock;
unsigned int init_bits;
} input_pool = {
@@ -619,7 +701,7 @@ static void extract_entropy(void *buf, size_t len)
/* next_key = HASHPRF(seed, RDSEED || 0) */
block.counter = 0;
- blake2s(next_key, (u8 *)&block, seed, sizeof(next_key), sizeof(block), sizeof(seed));
+ blake2s(seed, sizeof(seed), (const u8 *)&block, sizeof(block), next_key, sizeof(next_key));
blake2s_init_key(&input_pool.hash, BLAKE2S_HASH_SIZE, next_key, sizeof(next_key));
spin_unlock_irqrestore(&input_pool.lock, flags);
@@ -629,7 +711,7 @@ static void extract_entropy(void *buf, size_t len)
i = min_t(size_t, len, BLAKE2S_HASH_SIZE);
/* output = HASHPRF(seed, RDSEED || ++counter) */
++block.counter;
- blake2s(buf, (u8 *)&block, seed, i, sizeof(block), sizeof(seed));
+ blake2s(seed, sizeof(seed), (const u8 *)&block, sizeof(block), buf, i);
len -= i;
buf += i;
}
@@ -642,9 +724,10 @@ static void extract_entropy(void *buf, size_t len)
static void __cold _credit_init_bits(size_t bits)
{
- static struct execute_work set_ready;
+ static DECLARE_WORK(set_ready, crng_set_ready);
unsigned int new, orig, add;
unsigned long flags;
+ int m;
if (!bits)
return;
@@ -657,15 +740,19 @@ static void __cold _credit_init_bits(size_t bits)
} while (!try_cmpxchg(&input_pool.init_bits, &orig, new));
if (orig < POOL_READY_BITS && new >= POOL_READY_BITS) {
- crng_reseed(); /* Sets crng_init to CRNG_READY under base_crng.lock. */
- if (static_key_initialized)
- execute_in_process_context(crng_set_ready, &set_ready);
+ crng_reseed(NULL); /* Sets crng_init to CRNG_READY under base_crng.lock. */
+ if (system_dfl_wq)
+ queue_work(system_dfl_wq, &set_ready);
+ atomic_notifier_call_chain(&random_ready_notifier, 0, NULL);
+#ifdef CONFIG_VDSO_GETRANDOM
+ WRITE_ONCE(vdso_k_rng_data->is_ready, true);
+#endif
wake_up_interruptible(&crng_init_wait);
kill_fasync(&fasync, SIGIO, POLL_IN);
pr_notice("crng init done\n");
- if (urandom_warning.missed)
- pr_notice("%d urandom warning(s) missed due to ratelimiting\n",
- urandom_warning.missed);
+ m = ratelimit_state_get_miss(&urandom_warning);
+ if (m)
+ pr_notice("%d urandom warning(s) missed due to ratelimiting\n", m);
} else if (orig < POOL_EARLY_BITS && new >= POOL_EARLY_BITS) {
spin_lock_irqsave(&base_crng.lock, flags);
/* Check if crng_init is CRNG_EMPTY, to avoid race with crng_reseed(). */
@@ -686,7 +773,7 @@ static void __cold _credit_init_bits(size_t bits)
* the above entropy accumulation routines:
*
* void add_device_randomness(const void *buf, size_t len);
- * void add_hwgenerator_randomness(const void *buf, size_t len, size_t entropy);
+ * void add_hwgenerator_randomness(const void *buf, size_t len, size_t entropy, bool sleep_after);
* void add_bootloader_randomness(const void *buf, size_t len);
* void add_vmfork_randomness(const void *unique_vm_id, size_t len);
* void add_interrupt_randomness(int irq);
@@ -707,7 +794,7 @@ static void __cold _credit_init_bits(size_t bits)
*
* add_bootloader_randomness() is called by bootloader drivers, such as EFI
* and device tree, and credits its input depending on whether or not the
- * configuration option CONFIG_RANDOM_TRUST_BOOTLOADER is set.
+ * command line option 'random.trust_bootloader' is set.
*
* add_vmfork_randomness() adds a unique (but not necessarily secret) ID
* representing the current instance of a VM to the pool, without crediting,
@@ -733,8 +820,8 @@ static void __cold _credit_init_bits(size_t bits)
*
**********************************************************************/
-static bool trust_cpu __initdata = IS_ENABLED(CONFIG_RANDOM_TRUST_CPU);
-static bool trust_bootloader __initdata = IS_ENABLED(CONFIG_RANDOM_TRUST_BOOTLOADER);
+static bool trust_cpu __initdata = true;
+static bool trust_bootloader __initdata = true;
static int __init parse_trust_cpu(char *arg)
{
return kstrtobool(arg, &trust_cpu);
@@ -765,7 +852,7 @@ static int random_pm_notification(struct notifier_block *nb, unsigned long actio
if (crng_ready() && (action == PM_RESTORE_PREPARE ||
(action == PM_POST_SUSPEND && !IS_ENABLED(CONFIG_PM_AUTOSLEEP) &&
!IS_ENABLED(CONFIG_PM_USERSPACE_AUTOSLEEP)))) {
- crng_reseed();
+ crng_reseed(NULL);
pr_notice("crng reseeded on system resumption\n");
}
return 0;
@@ -774,18 +861,13 @@ static int random_pm_notification(struct notifier_block *nb, unsigned long actio
static struct notifier_block pm_notifier = { .notifier_call = random_pm_notification };
/*
- * The first collection of entropy occurs at system boot while interrupts
- * are still turned off. Here we push in latent entropy, RDSEED, a timestamp,
- * utsname(), and the command line. Depending on the above configuration knob,
- * RDSEED may be considered sufficient for initialization. Note that much
- * earlier setup may already have pushed entropy into the input pool by the
- * time we get here.
+ * This is called extremely early, before time keeping functionality is
+ * available, but arch randomness is. Interrupts are not yet enabled.
*/
-int __init random_init(const char *command_line)
+void __init random_init_early(const char *command_line)
{
- ktime_t now = ktime_get_real();
- size_t i, longs, arch_bits;
unsigned long entropy[BLAKE2S_BLOCK_SIZE / sizeof(long)];
+ size_t i, longs, arch_bits;
#if defined(LATENT_ENTROPY_PLUGIN)
static const u8 compiletime_seed[BLAKE2S_BLOCK_SIZE] __initconst __latent_entropy;
@@ -805,34 +887,48 @@ int __init random_init(const char *command_line)
i += longs;
continue;
}
- entropy[0] = random_get_entropy();
- _mix_pool_bytes(entropy, sizeof(*entropy));
arch_bits -= sizeof(*entropy) * 8;
++i;
}
- _mix_pool_bytes(&now, sizeof(now));
- _mix_pool_bytes(utsname(), sizeof(*(utsname())));
+
+ _mix_pool_bytes(init_utsname(), sizeof(*(init_utsname())));
_mix_pool_bytes(command_line, strlen(command_line));
+
+ /* Reseed if already seeded by earlier phases. */
+ if (crng_ready())
+ crng_reseed(NULL);
+ else if (trust_cpu)
+ _credit_init_bits(arch_bits);
+}
+
+/*
+ * This is called a little bit after the prior function, and now there is
+ * access to timestamps counters. Interrupts are not yet enabled.
+ */
+void __init random_init(void)
+{
+ unsigned long entropy = random_get_entropy();
+ ktime_t now = ktime_get_real();
+
+ _mix_pool_bytes(&now, sizeof(now));
+ _mix_pool_bytes(&entropy, sizeof(entropy));
add_latent_entropy();
/*
- * If we were initialized by the bootloader before jump labels are
- * initialized, then we should enable the static branch here, where
- * it's guaranteed that jump labels have been initialized.
+ * If we were initialized by the cpu or bootloader before workqueues
+ * are initialized, then we should enable the static branch here.
*/
if (!static_branch_likely(&crng_is_ready) && crng_init >= CRNG_READY)
crng_set_ready(NULL);
+ /* Reseed if already seeded by earlier phases. */
if (crng_ready())
- crng_reseed();
- else if (trust_cpu)
- _credit_init_bits(arch_bits);
+ crng_reseed(NULL);
WARN_ON(register_pm_notifier(&pm_notifier));
- WARN(!random_get_entropy(), "Missing cycle counter and fallback timer; RNG "
- "entropy collection will consequently suffer.");
- return 0;
+ WARN(!entropy, "Missing cycle counter and fallback timer; RNG "
+ "entropy collection will consequently suffer.");
}
/*
@@ -856,27 +952,27 @@ void add_device_randomness(const void *buf, size_t len)
EXPORT_SYMBOL(add_device_randomness);
/*
- * Interface for in-kernel drivers of true hardware RNGs.
- * Those devices may produce endless random bits and will be throttled
- * when our pool is full.
+ * Interface for in-kernel drivers of true hardware RNGs. Those devices
+ * may produce endless random bits, so this function will sleep for
+ * some amount of time after, if the sleep_after parameter is true.
*/
-void add_hwgenerator_randomness(const void *buf, size_t len, size_t entropy)
+void add_hwgenerator_randomness(const void *buf, size_t len, size_t entropy, bool sleep_after)
{
mix_pool_bytes(buf, len);
credit_init_bits(entropy);
/*
- * Throttle writing to once every CRNG_RESEED_INTERVAL, unless
- * we're not yet initialized.
+ * Throttle writing to once every reseed interval, unless we're not yet
+ * initialized or no entropy is credited.
*/
- if (!kthread_should_stop() && crng_ready())
- schedule_timeout_interruptible(CRNG_RESEED_INTERVAL);
+ if (sleep_after && !kthread_should_stop() && (crng_ready() || !entropy))
+ schedule_timeout_interruptible(crng_reseed_interval());
}
EXPORT_SYMBOL_GPL(add_hwgenerator_randomness);
/*
- * Handle random seed passed by bootloader, and credit it if
- * CONFIG_RANDOM_TRUST_BOOTLOADER is set.
+ * Handle random seed passed by bootloader, and credit it depending
+ * on the command line option 'random.trust_bootloader'.
*/
void __init add_bootloader_randomness(const void *buf, size_t len)
{
@@ -897,7 +993,7 @@ void __cold add_vmfork_randomness(const void *unique_vm_id, size_t len)
{
add_device_randomness(unique_vm_id, len);
if (crng_ready()) {
- crng_reseed();
+ crng_reseed(NULL);
pr_notice("crng reseeded due to virtual machine fork\n");
}
blocking_notifier_call_chain(&vmfork_chain, 0, NULL);
@@ -920,20 +1016,23 @@ EXPORT_SYMBOL_GPL(unregister_random_vmfork_notifier);
#endif
struct fast_pool {
- struct work_struct mix;
unsigned long pool[4];
unsigned long last;
unsigned int count;
+ struct timer_list mix;
};
+static void mix_interrupt_randomness(struct timer_list *work);
+
static DEFINE_PER_CPU(struct fast_pool, irq_randomness) = {
#ifdef CONFIG_64BIT
#define FASTMIX_PERM SIPHASH_PERMUTATION
- .pool = { SIPHASH_CONST_0, SIPHASH_CONST_1, SIPHASH_CONST_2, SIPHASH_CONST_3 }
+ .pool = { SIPHASH_CONST_0, SIPHASH_CONST_1, SIPHASH_CONST_2, SIPHASH_CONST_3 },
#else
#define FASTMIX_PERM HSIPHASH_PERMUTATION
- .pool = { HSIPHASH_CONST_0, HSIPHASH_CONST_1, HSIPHASH_CONST_2, HSIPHASH_CONST_3 }
+ .pool = { HSIPHASH_CONST_0, HSIPHASH_CONST_1, HSIPHASH_CONST_2, HSIPHASH_CONST_3 },
#endif
+ .mix = __TIMER_INITIALIZER(mix_interrupt_randomness, 0)
};
/*
@@ -975,7 +1074,7 @@ int __cold random_online_cpu(unsigned int cpu)
}
#endif
-static void mix_interrupt_randomness(struct work_struct *work)
+static void mix_interrupt_randomness(struct timer_list *work)
{
struct fast_pool *fast_pool = container_of(work, struct fast_pool, mix);
/*
@@ -1006,7 +1105,7 @@ static void mix_interrupt_randomness(struct work_struct *work)
local_irq_enable();
mix_pool_bytes(pool, sizeof(pool));
- credit_init_bits(max(1u, (count & U16_MAX) / 64));
+ credit_init_bits(clamp_t(unsigned int, (count & U16_MAX) / 64, 1, sizeof(pool) * 8));
memzero_explicit(pool, sizeof(pool));
}
@@ -1029,10 +1128,11 @@ void add_interrupt_randomness(int irq)
if (new_count < 1024 && !time_is_before_jiffies(fast_pool->last + HZ))
return;
- if (unlikely(!fast_pool->mix.func))
- INIT_WORK(&fast_pool->mix, mix_interrupt_randomness);
fast_pool->count |= MIX_INFLIGHT;
- queue_work_on(raw_smp_processor_id(), system_highpri_wq, &fast_pool->mix);
+ if (!timer_pending(&fast_pool->mix)) {
+ fast_pool->mix.expires = jiffies;
+ add_timer_on(&fast_pool->mix, raw_smp_processor_id());
+ }
}
EXPORT_SYMBOL_GPL(add_interrupt_randomness);
@@ -1159,66 +1259,107 @@ void __cold rand_initialize_disk(struct gendisk *disk)
struct entropy_timer_state {
unsigned long entropy;
struct timer_list timer;
- unsigned int samples, samples_per_bit;
+ atomic_t samples;
+ unsigned int samples_per_bit;
};
/*
- * Each time the timer fires, we expect that we got an unpredictable
- * jump in the cycle counter. Even if the timer is running on another
- * CPU, the timer activity will be touching the stack of the CPU that is
- * generating entropy..
+ * Each time the timer fires, we expect that we got an unpredictable jump in
+ * the cycle counter. Even if the timer is running on another CPU, the timer
+ * activity will be touching the stack of the CPU that is generating entropy.
*
- * Note that we don't re-arm the timer in the timer itself - we are
- * happy to be scheduled away, since that just makes the load more
- * complex, but we do not want the timer to keep ticking unless the
- * entropy loop is running.
+ * Note that we don't re-arm the timer in the timer itself - we are happy to be
+ * scheduled away, since that just makes the load more complex, but we do not
+ * want the timer to keep ticking unless the entropy loop is running.
*
* So the re-arming always happens in the entropy loop itself.
*/
static void __cold entropy_timer(struct timer_list *timer)
{
struct entropy_timer_state *state = container_of(timer, struct entropy_timer_state, timer);
+ unsigned long entropy = random_get_entropy();
- if (++state->samples == state->samples_per_bit) {
+ mix_pool_bytes(&entropy, sizeof(entropy));
+ if (atomic_inc_return(&state->samples) % state->samples_per_bit == 0)
credit_init_bits(1);
- state->samples = 0;
- }
}
/*
- * If we have an actual cycle counter, see if we can
- * generate enough entropy with timing noise
+ * If we have an actual cycle counter, see if we can generate enough entropy
+ * with timing noise.
*/
static void __cold try_to_generate_entropy(void)
{
- enum { NUM_TRIAL_SAMPLES = 8192, MAX_SAMPLES_PER_BIT = HZ / 30 };
- struct entropy_timer_state stack;
+ enum { NUM_TRIAL_SAMPLES = 8192, MAX_SAMPLES_PER_BIT = HZ / 15 };
+ u8 stack_bytes[sizeof(struct entropy_timer_state) + SMP_CACHE_BYTES - 1];
+ struct entropy_timer_state *stack = PTR_ALIGN((void *)stack_bytes, SMP_CACHE_BYTES);
unsigned int i, num_different = 0;
unsigned long last = random_get_entropy();
+ cpumask_var_t timer_cpus;
+ int cpu = -1;
for (i = 0; i < NUM_TRIAL_SAMPLES - 1; ++i) {
- stack.entropy = random_get_entropy();
- if (stack.entropy != last)
+ stack->entropy = random_get_entropy();
+ if (stack->entropy != last)
++num_different;
- last = stack.entropy;
+ last = stack->entropy;
}
- stack.samples_per_bit = DIV_ROUND_UP(NUM_TRIAL_SAMPLES, num_different + 1);
- if (stack.samples_per_bit > MAX_SAMPLES_PER_BIT)
+ stack->samples_per_bit = DIV_ROUND_UP(NUM_TRIAL_SAMPLES, num_different + 1);
+ if (stack->samples_per_bit > MAX_SAMPLES_PER_BIT)
return;
- stack.samples = 0;
- timer_setup_on_stack(&stack.timer, entropy_timer, 0);
+ atomic_set(&stack->samples, 0);
+ timer_setup_on_stack(&stack->timer, entropy_timer, 0);
+ if (!alloc_cpumask_var(&timer_cpus, GFP_KERNEL))
+ goto out;
+
while (!crng_ready() && !signal_pending(current)) {
- if (!timer_pending(&stack.timer))
- mod_timer(&stack.timer, jiffies + 1);
- mix_pool_bytes(&stack.entropy, sizeof(stack.entropy));
+ /*
+ * Check !timer_pending() and then ensure that any previous callback has finished
+ * executing by checking timer_delete_sync_try(), before queueing the next one.
+ */
+ if (!timer_pending(&stack->timer) && timer_delete_sync_try(&stack->timer) >= 0) {
+ unsigned int num_cpus;
+
+ /*
+ * Preemption must be disabled here, both to read the current CPU number
+ * and to avoid scheduling a timer on a dead CPU.
+ */
+ preempt_disable();
+
+ /* Only schedule callbacks on timer CPUs that are online. */
+ cpumask_and(timer_cpus, housekeeping_cpumask(HK_TYPE_TIMER), cpu_online_mask);
+ num_cpus = cpumask_weight(timer_cpus);
+ /* In very bizarre case of misconfiguration, fallback to all online. */
+ if (unlikely(num_cpus == 0)) {
+ *timer_cpus = *cpu_online_mask;
+ num_cpus = cpumask_weight(timer_cpus);
+ }
+
+ /* Basic CPU round-robin, which avoids the current CPU. */
+ do {
+ cpu = cpumask_next(cpu, timer_cpus);
+ if (cpu >= nr_cpu_ids)
+ cpu = cpumask_first(timer_cpus);
+ } while (cpu == smp_processor_id() && num_cpus > 1);
+
+ /* Expiring the timer at `jiffies` means it's the next tick. */
+ stack->timer.expires = jiffies;
+
+ add_timer_on(&stack->timer, cpu);
+
+ preempt_enable();
+ }
+ mix_pool_bytes(&stack->entropy, sizeof(stack->entropy));
schedule();
- stack.entropy = random_get_entropy();
+ stack->entropy = random_get_entropy();
}
+ mix_pool_bytes(&stack->entropy, sizeof(stack->entropy));
- del_timer_sync(&stack.timer);
- destroy_timer_on_stack(&stack.timer);
- mix_pool_bytes(&stack.entropy, sizeof(stack.entropy));
+ free_cpumask_var(timer_cpus);
+out:
+ timer_delete_sync(&stack->timer);
+ timer_destroy_on_stack(&stack->timer);
}
@@ -1253,7 +1394,6 @@ static void __cold try_to_generate_entropy(void)
SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags)
{
struct iov_iter iter;
- struct iovec iov;
int ret;
if (flags & ~(GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE))
@@ -1274,7 +1414,7 @@ SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags
return ret;
}
- ret = import_single_range(READ, ubuf, len, &iov, &iter);
+ ret = import_ubuf(ITER_DEST, ubuf, len, &iter);
if (unlikely(ret))
return ret;
return get_random_bytes_user(&iter);
@@ -1332,7 +1472,7 @@ static ssize_t urandom_read_iter(struct kiocb *kiocb, struct iov_iter *iter)
if (!crng_ready()) {
if (!ratelimit_disable && maxwarn <= 0)
- ++urandom_warning.missed;
+ ratelimit_state_inc_miss(&urandom_warning);
else if (ratelimit_disable || __ratelimit(&urandom_warning)) {
--maxwarn;
pr_notice("%s: uninitialized urandom read (%zu bytes read)\n",
@@ -1347,6 +1487,11 @@ static ssize_t random_read_iter(struct kiocb *kiocb, struct iov_iter *iter)
{
int ret;
+ if (!crng_ready() &&
+ ((kiocb->ki_flags & (IOCB_NOWAIT | IOCB_NOIO)) ||
+ (kiocb->ki_filp->f_flags & O_NONBLOCK)))
+ return -EAGAIN;
+
ret = wait_for_random_bytes();
if (ret != 0)
return ret;
@@ -1375,7 +1520,6 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return 0;
case RNDADDENTROPY: {
struct iov_iter iter;
- struct iovec iov;
ssize_t ret;
int len;
@@ -1387,7 +1531,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -EINVAL;
if (get_user(len, p++))
return -EFAULT;
- ret = import_single_range(WRITE, p, len, &iov, &iter);
+ ret = import_ubuf(ITER_SOURCE, p, len, &iter);
if (unlikely(ret))
return ret;
ret = write_pool_user(&iter);
@@ -1410,7 +1554,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -EPERM;
if (!crng_ready())
return -ENODATA;
- crng_reseed();
+ crng_reseed(NULL);
return 0;
default:
return -EINVAL;
@@ -1430,7 +1574,7 @@ const struct file_operations random_fops = {
.compat_ioctl = compat_ptr_ioctl,
.fasync = random_fasync,
.llseek = noop_llseek,
- .splice_read = generic_file_splice_read,
+ .splice_read = copy_splice_read,
.splice_write = iter_file_splice_write,
};
@@ -1441,7 +1585,7 @@ const struct file_operations urandom_fops = {
.compat_ioctl = compat_ptr_ioctl,
.fasync = random_fasync,
.llseek = noop_llseek,
- .splice_read = generic_file_splice_read,
+ .splice_read = copy_splice_read,
.splice_write = iter_file_splice_write,
};
@@ -1490,7 +1634,7 @@ static u8 sysctl_bootid[UUID_SIZE];
* UUID. The difference is in whether table->data is NULL; if it is,
* then a new UUID is generated and returned to the user.
*/
-static int proc_do_uuid(struct ctl_table *table, int write, void *buf,
+static int proc_do_uuid(const struct ctl_table *table, int write, void *buf,
size_t *lenp, loff_t *ppos)
{
u8 tmp_uuid[UUID_SIZE], *uuid;
@@ -1521,13 +1665,13 @@ static int proc_do_uuid(struct ctl_table *table, int write, void *buf,
}
/* The same as proc_dointvec, but writes don't change anything. */
-static int proc_do_rointvec(struct ctl_table *table, int write, void *buf,
+static int proc_do_rointvec(const struct ctl_table *table, int write, void *buf,
size_t *lenp, loff_t *ppos)
{
return write ? 0 : proc_dointvec(table, 0, buf, lenp, ppos);
}
-static struct ctl_table random_table[] = {
+static const struct ctl_table random_table[] = {
{
.procname = "poolsize",
.data = &sysctl_poolsize,
@@ -1567,7 +1711,6 @@ static struct ctl_table random_table[] = {
.mode = 0444,
.proc_handler = proc_do_uuid,
},
- { }
};
/*
diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c
index 9f701dcba95c..700e6affea6f 100644
--- a/drivers/char/scx200_gpio.c
+++ b/drivers/char/scx200_gpio.c
@@ -68,7 +68,6 @@ static const struct file_operations scx200_gpio_fileops = {
.read = nsc_gpio_read,
.open = scx200_gpio_open,
.release = scx200_gpio_release,
- .llseek = no_llseek,
};
static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index 27e301a6bb7a..677bb5ac950a 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -37,6 +37,7 @@
#include <linux/kfifo.h>
#include <linux/platform_device.h>
#include <linux/gfp.h>
+#include <linux/string_choices.h>
#include <linux/uaccess.h>
#include <asm/io.h>
@@ -920,7 +921,7 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
if (ret > 0) {
struct inode *inode = file_inode(file);
- inode->i_atime = current_time(inode);
+ inode_set_atime_to_ts(inode, current_time(inode));
}
return ret;
@@ -1054,7 +1055,6 @@ static const struct file_operations sonypi_misc_fops = {
.release = sonypi_misc_release,
.fasync = sonypi_misc_fasync,
.unlocked_ioctl = sonypi_misc_ioctl,
- .llseek = no_llseek,
};
static struct miscdevice sonypi_misc_device = {
@@ -1123,10 +1123,9 @@ static int sonypi_acpi_add(struct acpi_device *device)
return 0;
}
-static int sonypi_acpi_remove(struct acpi_device *device)
+static void sonypi_acpi_remove(struct acpi_device *device)
{
sonypi_acpi_device = NULL;
- return 0;
}
static const struct acpi_device_id sonypi_device_ids[] = {
@@ -1270,12 +1269,12 @@ static void sonypi_display_info(void)
"compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n",
sonypi_device.model,
verbose,
- fnkeyinit ? "on" : "off",
- camera ? "on" : "off",
- compat ? "on" : "off",
+ str_on_off(fnkeyinit),
+ str_on_off(camera),
+ str_on_off(compat),
mask,
- useinput ? "on" : "off",
- SONYPI_ACPI_ACTIVE ? "on" : "off");
+ str_on_off(useinput),
+ str_on_off(SONYPI_ACPI_ACTIVE));
printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
sonypi_device.irq,
sonypi_device.ioport1, sonypi_device.ioport2);
@@ -1409,7 +1408,7 @@ static int sonypi_probe(struct platform_device *dev)
return error;
}
-static int sonypi_remove(struct platform_device *dev)
+static void sonypi_remove(struct platform_device *dev)
{
sonypi_disable();
@@ -1433,8 +1432,6 @@ static int sonypi_remove(struct platform_device *dev)
}
kfifo_free(&sonypi_device.fifo);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index 896a3550fba9..b381ea7e85d2 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -42,11 +42,12 @@
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
+#include <linux/device/faux.h>
#include <asm/io.h> /* inb/outb */
#include <linux/uaccess.h>
MODULE_AUTHOR("Sebastien Bouchard <sebastien.bouchard@ca.kontron.com>");
+MODULE_DESCRIPTION("Telecom Clock driver for Intel NetStructure(tm) MPCBL0010");
MODULE_LICENSE("GPL");
/*Hardware Reset of the PLL */
@@ -741,7 +742,7 @@ static ssize_t store_reset (struct device *d,
static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset);
-static struct attribute *tlclk_sysfs_entries[] = {
+static struct attribute *tlclk_attrs[] = {
&dev_attr_current_ref.attr,
&dev_attr_telclock_version.attr,
&dev_attr_alarms.attr,
@@ -765,13 +766,9 @@ static struct attribute *tlclk_sysfs_entries[] = {
&dev_attr_reset.attr,
NULL
};
+ATTRIBUTE_GROUPS(tlclk);
-static const struct attribute_group tlclk_attribute_group = {
- .name = NULL, /* put in device directory */
- .attrs = tlclk_sysfs_entries,
-};
-
-static struct platform_device *tlclk_device;
+static struct faux_device *tlclk_device;
static int __init tlclk_init(void)
{
@@ -816,24 +813,13 @@ static int __init tlclk_init(void)
goto out3;
}
- tlclk_device = platform_device_register_simple("telco_clock",
- -1, NULL, 0);
- if (IS_ERR(tlclk_device)) {
- printk(KERN_ERR "tlclk: platform_device_register failed.\n");
- ret = PTR_ERR(tlclk_device);
+ tlclk_device = faux_device_create_with_groups("telco_clock", NULL, NULL, tlclk_groups);
+ if (!tlclk_device) {
+ ret = -ENODEV;
goto out4;
}
- ret = sysfs_create_group(&tlclk_device->dev.kobj,
- &tlclk_attribute_group);
- if (ret) {
- printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n");
- goto out5;
- }
-
return 0;
-out5:
- platform_device_unregister(tlclk_device);
out4:
misc_deregister(&tlclk_miscdev);
out3:
@@ -847,13 +833,12 @@ out1:
static void __exit tlclk_cleanup(void)
{
- sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group);
- platform_device_unregister(tlclk_device);
+ faux_device_destroy(tlclk_device);
misc_deregister(&tlclk_miscdev);
unregister_chrdev(tlclk_major, "telco_clock");
release_region(TLCLK_BASE, 8);
- del_timer_sync(&switchover_timer);
+ timer_delete_sync(&switchover_timer);
kfree(alarm_events);
}
@@ -871,7 +856,7 @@ static void switchover_timeout(struct timer_list *unused)
}
/* Alarm processing is done, wake up read task */
- del_timer(&switchover_timer);
+ timer_delete(&switchover_timer);
got_event = 1;
wake_up(&wq);
}
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 927088b2c3d3..8a8f692b6088 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -27,6 +27,21 @@ menuconfig TCG_TPM
if TCG_TPM
+config TCG_TPM2_HMAC
+ bool "Use HMAC and encrypted transactions on the TPM bus"
+ default n
+ select CRYPTO_ECDH
+ select CRYPTO_LIB_AESCFB
+ select CRYPTO_LIB_SHA256
+ select CRYPTO_LIB_UTILS
+ help
+ Setting this causes us to deploy a scheme which uses request
+ and response HMACs in addition to encryption for
+ communicating with the TPM to prevent or detect bus snooping
+ and interposer attacks (see tpm-security.rst). Saying Y
+ here adds some encryption overhead to all kernel to TPM
+ transactions.
+
config HW_RANDOM_TPM
bool "TPM HW Random Number Generator support"
depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m)
@@ -148,7 +163,8 @@ config TCG_NSC
config TCG_ATMEL
tristate "Atmel TPM Interface"
- depends on PPC64 || HAS_IOPORT_MAP
+ depends on HAS_IOPORT_MAP
+ depends on HAS_IOPORT
help
If you have a TPM security chip from Atmel say Yes and it
will be accessible from within Linux. To compile this driver
@@ -156,7 +172,7 @@ config TCG_ATMEL
config TCG_INFINEON
tristate "Infineon Technologies TPM Interface"
- depends on PNP
+ depends on PNP || COMPILE_TEST
help
If you have a TPM security chip from Infineon Technologies
(either SLD 9630 TT 1.1 or SLB 9635 TT 1.2) say Yes and it
@@ -174,6 +190,15 @@ config TCG_IBMVTPM
will be accessible from within Linux. To compile this driver
as a module, choose M here; the module will be called tpm_ibmvtpm.
+config TCG_LOONGSON
+ tristate "Loongson TPM Interface"
+ depends on MFD_LOONGSON_SE
+ help
+ If you want to make Loongson TPM support available, say Yes and
+ it will be accessible from within Linux. To compile this
+ driver as a module, choose M here; the module will be called
+ tpm_loongson.
+
config TCG_XEN
tristate "XEN TPM Interface"
depends on TCG_TPM && XEN
@@ -195,6 +220,15 @@ config TCG_CRB
from within Linux. To compile this driver as a module, choose
M here; the module will be called tpm_crb.
+config TCG_ARM_CRB_FFA
+ tristate "TPM CRB over Arm FF-A Transport"
+ depends on ARM_FFA_TRANSPORT && TCG_CRB
+ default TCG_CRB
+ help
+ If the Arm FF-A transport is used to access the TPM say Yes.
+ To compile this driver as a module, choose M here; the module
+ will be called tpm_crb_ffa.
+
config TCG_VTPM_PROXY
tristate "VTPM Proxy Interface"
depends on TCG_TPM
@@ -210,5 +244,15 @@ config TCG_FTPM_TEE
help
This driver proxies for firmware TPM running in TEE.
+config TCG_SVSM
+ tristate "SNP SVSM vTPM interface"
+ depends on AMD_MEM_ENCRYPT
+ help
+ This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest
+ OS can use to discover and talk to a vTPM emulated by the Secure VM
+ Service Module (SVSM) in the guest context, but at a more privileged
+ level (usually VMPL0). To compile this driver as a module, choose M
+ here; the module will be called tpm_svsm.
+
source "drivers/char/tpm/st33zp24/Kconfig"
endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 0222b1ddb310..5b5cdc0d32e4 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -15,6 +15,8 @@ tpm-y += tpm-sysfs.o
tpm-y += eventlog/common.o
tpm-y += eventlog/tpm1.o
tpm-y += eventlog/tpm2.o
+tpm-y += tpm-buf.o
+tpm-y += tpm2-sessions.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o
tpm-$(CONFIG_EFI) += eventlog/efi.o
@@ -40,5 +42,8 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
obj-$(CONFIG_TCG_CRB) += tpm_crb.o
+obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o
obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
+obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o
+obj-$(CONFIG_TCG_LOONGSON) += tpm_loongson.o
diff --git a/drivers/char/tpm/eventlog/acpi.c b/drivers/char/tpm/eventlog/acpi.c
index 1b18ce5ebab1..cf02ec646f46 100644
--- a/drivers/char/tpm/eventlog/acpi.c
+++ b/drivers/char/tpm/eventlog/acpi.c
@@ -14,6 +14,7 @@
* Access to the event log extended by the TCG BIOS of PC platform
*/
+#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/fs.h>
#include <linux/security.h>
@@ -62,6 +63,11 @@ static bool tpm_is_tpm2_log(void *bios_event_log, u64 len)
return n == 0;
}
+static void tpm_bios_log_free(void *data)
+{
+ kvfree(data);
+}
+
/* read binary bios log */
int tpm_read_log_acpi(struct tpm_chip *chip)
{
@@ -90,16 +96,21 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
return -ENODEV;
if (tbl->header.length <
- sizeof(*tbl) + sizeof(struct acpi_tpm2_phy))
+ sizeof(*tbl) + sizeof(struct acpi_tpm2_phy)) {
+ acpi_put_table((struct acpi_table_header *)tbl);
return -ENODEV;
+ }
tpm2_phy = (void *)tbl + sizeof(*tbl);
len = tpm2_phy->log_area_minimum_length;
start = tpm2_phy->log_area_start_address;
- if (!start || !len)
+ if (!start || !len) {
+ acpi_put_table((struct acpi_table_header *)tbl);
return -ENODEV;
+ }
+ acpi_put_table((struct acpi_table_header *)tbl);
format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
} else {
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
@@ -120,24 +131,29 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
break;
}
+ acpi_put_table((struct acpi_table_header *)buff);
format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
}
+
if (!len) {
dev_warn(&chip->dev, "%s: TCPA log area empty\n", __func__);
return -EIO;
}
/* malloc EventLog space */
- log->bios_event_log = kmalloc(len, GFP_KERNEL);
+ log->bios_event_log = kvmalloc(len, GFP_KERNEL);
if (!log->bios_event_log)
return -ENOMEM;
log->bios_event_log_end = log->bios_event_log + len;
- ret = -EIO;
virt = acpi_os_map_iomem(start, len);
- if (!virt)
+ if (!virt) {
+ dev_warn(&chip->dev, "%s: Failed to map ACPI memory\n", __func__);
+ /* try EFI log next */
+ ret = -ENODEV;
goto err;
+ }
memcpy_fromio(log->bios_event_log, virt, len);
@@ -150,11 +166,16 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
goto err;
}
+ ret = devm_add_action(&chip->dev, tpm_bios_log_free, log->bios_event_log);
+ if (ret) {
+ log->bios_event_log = NULL;
+ goto err;
+ }
+
return format;
err:
- kfree(log->bios_event_log);
+ tpm_bios_log_free(log->bios_event_log);
log->bios_event_log = NULL;
return ret;
-
}
diff --git a/drivers/char/tpm/eventlog/common.c b/drivers/char/tpm/eventlog/common.c
index 8512ec76d526..691813d2a5a2 100644
--- a/drivers/char/tpm/eventlog/common.c
+++ b/drivers/char/tpm/eventlog/common.c
@@ -32,11 +32,11 @@ static int tpm_bios_measurements_open(struct inode *inode,
struct tpm_chip *chip;
inode_lock(inode);
- if (!inode->i_private) {
+ if (!inode->i_nlink) {
inode_unlock(inode);
return -ENODEV;
}
- chip_seqops = (struct tpm_chip_seqops *)inode->i_private;
+ chip_seqops = inode->i_private;
seqops = chip_seqops->seqops;
chip = chip_seqops->chip;
get_device(&chip->dev);
@@ -47,6 +47,8 @@ static int tpm_bios_measurements_open(struct inode *inode,
if (!err) {
seq = file->private_data;
seq->private = chip;
+ } else {
+ put_device(&chip->dev);
}
return err;
@@ -55,8 +57,8 @@ static int tpm_bios_measurements_open(struct inode *inode,
static int tpm_bios_measurements_release(struct inode *inode,
struct file *file)
{
- struct seq_file *seq = (struct seq_file *)file->private_data;
- struct tpm_chip *chip = (struct tpm_chip *)seq->private;
+ struct seq_file *seq = file->private_data;
+ struct tpm_chip *chip = seq->private;
put_device(&chip->dev);
@@ -103,7 +105,7 @@ static int tpm_read_log(struct tpm_chip *chip)
void tpm_bios_log_setup(struct tpm_chip *chip)
{
const char *name = dev_name(&chip->dev);
- unsigned int cnt;
+ struct dentry *dentry;
int log_version;
int rc = 0;
@@ -115,14 +117,12 @@ void tpm_bios_log_setup(struct tpm_chip *chip)
return;
log_version = rc;
- cnt = 0;
- chip->bios_dir[cnt] = securityfs_create_dir(name, NULL);
+ chip->bios_dir = securityfs_create_dir(name, NULL);
/* NOTE: securityfs_create_dir can return ENODEV if securityfs is
* compiled out. The caller should ignore the ENODEV return code.
*/
- if (IS_ERR(chip->bios_dir[cnt]))
- goto err;
- cnt++;
+ if (IS_ERR(chip->bios_dir))
+ return;
chip->bin_log_seqops.chip = chip;
if (log_version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2)
@@ -133,14 +133,13 @@ void tpm_bios_log_setup(struct tpm_chip *chip)
&tpm1_binary_b_measurements_seqops;
- chip->bios_dir[cnt] =
+ dentry =
securityfs_create_file("binary_bios_measurements",
- 0440, chip->bios_dir[0],
+ 0440, chip->bios_dir,
(void *)&chip->bin_log_seqops,
&tpm_bios_measurements_ops);
- if (IS_ERR(chip->bios_dir[cnt]))
+ if (IS_ERR(dentry))
goto err;
- cnt++;
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
@@ -148,42 +147,23 @@ void tpm_bios_log_setup(struct tpm_chip *chip)
chip->ascii_log_seqops.seqops =
&tpm1_ascii_b_measurements_seqops;
- chip->bios_dir[cnt] =
+ dentry =
securityfs_create_file("ascii_bios_measurements",
- 0440, chip->bios_dir[0],
+ 0440, chip->bios_dir,
(void *)&chip->ascii_log_seqops,
&tpm_bios_measurements_ops);
- if (IS_ERR(chip->bios_dir[cnt]))
+ if (IS_ERR(dentry))
goto err;
- cnt++;
}
return;
err:
- chip->bios_dir[cnt] = NULL;
tpm_bios_log_teardown(chip);
return;
}
void tpm_bios_log_teardown(struct tpm_chip *chip)
{
- int i;
- struct inode *inode;
-
- /* securityfs_remove currently doesn't take care of handling sync
- * between removal and opening of pseudo files. To handle this, a
- * workaround is added by making i_private = NULL here during removal
- * and to check it during open(), both within inode_lock()/unlock().
- * This design ensures that open() either safely gets kref or fails.
- */
- for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) {
- if (chip->bios_dir[i]) {
- inode = d_inode(chip->bios_dir[i]);
- inode_lock(inode);
- inode->i_private = NULL;
- inode_unlock(inode);
- securityfs_remove(chip->bios_dir[i]);
- }
- }
+ securityfs_remove(chip->bios_dir);
}
diff --git a/drivers/char/tpm/eventlog/efi.c b/drivers/char/tpm/eventlog/efi.c
index e6cb9d525e30..4e9d7c2bf32e 100644
--- a/drivers/char/tpm/eventlog/efi.c
+++ b/drivers/char/tpm/eventlog/efi.c
@@ -6,6 +6,7 @@
* Thiebaud Weksteen <tweek@google.com>
*/
+#include <linux/device.h>
#include <linux/efi.h>
#include <linux/tpm_eventlog.h>
@@ -55,7 +56,7 @@ int tpm_read_log_efi(struct tpm_chip *chip)
}
/* malloc EventLog space */
- log->bios_event_log = kmemdup(log_tbl->log, log_size, GFP_KERNEL);
+ log->bios_event_log = devm_kmemdup(&chip->dev, log_tbl->log, log_size, GFP_KERNEL);
if (!log->bios_event_log) {
ret = -ENOMEM;
goto out;
@@ -76,7 +77,7 @@ int tpm_read_log_efi(struct tpm_chip *chip)
MEMREMAP_WB);
if (!final_tbl) {
pr_err("Could not map UEFI TPM final log\n");
- kfree(log->bios_event_log);
+ devm_kfree(&chip->dev, log->bios_event_log);
ret = -ENOMEM;
goto out;
}
@@ -91,11 +92,11 @@ int tpm_read_log_efi(struct tpm_chip *chip)
* Allocate memory for the 'combined log' where we will append the
* 'final events log' to.
*/
- tmp = krealloc(log->bios_event_log,
- log_size + final_events_log_size,
- GFP_KERNEL);
+ tmp = devm_krealloc(&chip->dev, log->bios_event_log,
+ log_size + final_events_log_size,
+ GFP_KERNEL);
if (!tmp) {
- kfree(log->bios_event_log);
+ devm_kfree(&chip->dev, log->bios_event_log);
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/char/tpm/eventlog/of.c b/drivers/char/tpm/eventlog/of.c
index a9ce66d09a75..92cec9722ee4 100644
--- a/drivers/char/tpm/eventlog/of.c
+++ b/drivers/char/tpm/eventlog/of.c
@@ -10,13 +10,38 @@
* Read the event log created by the firmware on PPC64
*/
+#include <linux/device.h>
#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/tpm_eventlog.h>
#include "../tpm.h"
#include "common.h"
+static int tpm_read_log_memory_region(struct tpm_chip *chip)
+{
+ struct resource res;
+ int rc;
+
+ rc = of_reserved_mem_region_to_resource(chip->dev.parent->of_node, 0, &res);
+ if (rc)
+ return rc;
+
+ chip->log.bios_event_log = devm_memremap(&chip->dev, res.start, resource_size(&res),
+ MEMREMAP_WB);
+ if (IS_ERR(chip->log.bios_event_log))
+ return -ENOMEM;
+
+ chip->log.bios_event_log_end = chip->log.bios_event_log + resource_size(&res);
+
+ return chip->flags & TPM_CHIP_FLAG_TPM2 ? EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 :
+ EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
+}
+
int tpm_read_log_of(struct tpm_chip *chip)
{
struct device_node *np;
@@ -38,7 +63,7 @@ int tpm_read_log_of(struct tpm_chip *chip)
sizep = of_get_property(np, "linux,sml-size", NULL);
basep = of_get_property(np, "linux,sml-base", NULL);
if (sizep == NULL && basep == NULL)
- return -ENODEV;
+ return tpm_read_log_memory_region(chip);
if (sizep == NULL || basep == NULL)
return -EIO;
@@ -65,7 +90,7 @@ int tpm_read_log_of(struct tpm_chip *chip)
return -EIO;
}
- log->bios_event_log = kmemdup(__va(base), size, GFP_KERNEL);
+ log->bios_event_log = devm_kmemdup(&chip->dev, __va(base), size, GFP_KERNEL);
if (!log->bios_event_log)
return -ENOMEM;
diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
index 8aa9057601d6..e7913b2853d5 100644
--- a/drivers/char/tpm/eventlog/tpm1.c
+++ b/drivers/char/tpm/eventlog/tpm1.c
@@ -251,18 +251,14 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v)
static int tpm1_ascii_bios_measurements_show(struct seq_file *m, void *v)
{
- int len = 0;
char *eventname;
struct tcpa_event *event = v;
unsigned char *event_entry =
(unsigned char *)(v + sizeof(struct tcpa_event));
eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
- if (!eventname) {
- printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
- __func__);
- return -EFAULT;
- }
+ if (!eventname)
+ return -ENOMEM;
/* 1st: PCR */
seq_printf(m, "%2d ", do_endian_conversion(event->pcr_index));
@@ -273,7 +269,7 @@ static int tpm1_ascii_bios_measurements_show(struct seq_file *m, void *v)
/* 3rd: event type identifier */
seq_printf(m, " %02x", do_endian_conversion(event->event_type));
- len += get_event_name(eventname, event, event_entry);
+ get_event_name(eventname, event, event_entry);
/* 4th: eventname <= max + \'0' delimiter */
seq_printf(m, " %s\n", eventname);
diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c
index 3170d59d660c..81348487c125 100644
--- a/drivers/char/tpm/st33zp24/i2c.c
+++ b/drivers/char/tpm/st33zp24/i2c.c
@@ -6,13 +6,9 @@
#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/tpm.h>
-#include <linux/platform_data/st33zp24.h>
#include "../tpm.h"
#include "st33zp24.h"
@@ -22,7 +18,6 @@
struct st33zp24_i2c_phy {
struct i2c_client *client;
u8 buf[ST33ZP24_BUFSIZE + 1];
- int io_lpcpd;
};
/*
@@ -99,115 +94,6 @@ static const struct st33zp24_phy_ops i2c_phy_ops = {
.recv = st33zp24_i2c_recv,
};
-static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
-
-static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
- { "lpcpd-gpios", &lpcpd_gpios, 1 },
- {},
-};
-
-static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
-{
- struct tpm_chip *chip = i2c_get_clientdata(client);
- struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
- struct gpio_desc *gpiod_lpcpd;
- struct device *dev = &client->dev;
- int ret;
-
- ret = devm_acpi_dev_add_driver_gpios(dev, acpi_st33zp24_gpios);
- if (ret)
- return ret;
-
- /* Get LPCPD GPIO from ACPI */
- gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_lpcpd)) {
- dev_err(&client->dev,
- "Failed to retrieve lpcpd-gpios from acpi.\n");
- phy->io_lpcpd = -1;
- /*
- * lpcpd pin is not specified. This is not an issue as
- * power management can be also managed by TPM specific
- * commands. So leave with a success status code.
- */
- return 0;
- }
-
- phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd);
-
- return 0;
-}
-
-static int st33zp24_i2c_of_request_resources(struct i2c_client *client)
-{
- struct tpm_chip *chip = i2c_get_clientdata(client);
- struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
- struct device_node *pp;
- int gpio;
- int ret;
-
- pp = client->dev.of_node;
- if (!pp) {
- dev_err(&client->dev, "No platform data\n");
- return -ENODEV;
- }
-
- /* Get GPIO from device tree */
- gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
- if (gpio < 0) {
- dev_err(&client->dev,
- "Failed to retrieve lpcpd-gpios from dts.\n");
- phy->io_lpcpd = -1;
- /*
- * lpcpd pin is not specified. This is not an issue as
- * power management can be also managed by TPM specific
- * commands. So leave with a success status code.
- */
- return 0;
- }
- /* GPIO request and configuration */
- ret = devm_gpio_request_one(&client->dev, gpio,
- GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
- if (ret) {
- dev_err(&client->dev, "Failed to request lpcpd pin\n");
- return -ENODEV;
- }
- phy->io_lpcpd = gpio;
-
- return 0;
-}
-
-static int st33zp24_i2c_request_resources(struct i2c_client *client)
-{
- struct tpm_chip *chip = i2c_get_clientdata(client);
- struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
- struct st33zp24_platform_data *pdata;
- int ret;
-
- pdata = client->dev.platform_data;
- if (!pdata) {
- dev_err(&client->dev, "No platform data\n");
- return -ENODEV;
- }
-
- /* store for late use */
- phy->io_lpcpd = pdata->io_lpcpd;
-
- if (gpio_is_valid(pdata->io_lpcpd)) {
- ret = devm_gpio_request_one(&client->dev,
- pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH,
- "TPM IO_LPCPD");
- if (ret) {
- dev_err(&client->dev, "Failed to request lpcpd pin\n");
- return ret;
- }
- }
-
- return 0;
-}
-
/*
* st33zp24_i2c_probe initialize the TPM device
* @param: client, the i2c_client description (TPM I2C description).
@@ -215,19 +101,10 @@ static int st33zp24_i2c_request_resources(struct i2c_client *client)
* @return: 0 in case of success.
* -1 in other case.
*/
-static int st33zp24_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int st33zp24_i2c_probe(struct i2c_client *client)
{
- int ret;
- struct st33zp24_platform_data *pdata;
struct st33zp24_i2c_phy *phy;
- if (!client) {
- pr_info("%s: i2c client is NULL. Device not accessible.\n",
- __func__);
- return -ENODEV;
- }
-
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_info(&client->dev, "client not i2c capable\n");
return -ENODEV;
@@ -240,23 +117,7 @@ static int st33zp24_i2c_probe(struct i2c_client *client,
phy->client = client;
- pdata = client->dev.platform_data;
- if (!pdata && client->dev.of_node) {
- ret = st33zp24_i2c_of_request_resources(client);
- if (ret)
- return ret;
- } else if (pdata) {
- ret = st33zp24_i2c_request_resources(client);
- if (ret)
- return ret;
- } else if (ACPI_HANDLE(&client->dev)) {
- ret = st33zp24_i2c_acpi_request_resources(client);
- if (ret)
- return ret;
- }
-
- return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq,
- phy->io_lpcpd);
+ return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq);
}
/*
@@ -264,28 +125,26 @@ static int st33zp24_i2c_probe(struct i2c_client *client,
* @param: client, the i2c_client description (TPM I2C description).
* @return: 0 in case of success.
*/
-static int st33zp24_i2c_remove(struct i2c_client *client)
+static void st33zp24_i2c_remove(struct i2c_client *client)
{
struct tpm_chip *chip = i2c_get_clientdata(client);
st33zp24_remove(chip);
-
- return 0;
}
static const struct i2c_device_id st33zp24_i2c_id[] = {
- {TPM_ST33_I2C, 0},
+ { TPM_ST33_I2C },
{}
};
MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id);
-static const struct of_device_id of_st33zp24_i2c_match[] = {
+static const struct of_device_id of_st33zp24_i2c_match[] __maybe_unused = {
{ .compatible = "st,st33zp24-i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match);
-static const struct acpi_device_id st33zp24_i2c_acpi_match[] = {
+static const struct acpi_device_id st33zp24_i2c_acpi_match[] __maybe_unused = {
{"SMO3324"},
{}
};
@@ -308,7 +167,7 @@ static struct i2c_driver st33zp24_i2c_driver = {
module_i2c_driver(st33zp24_i2c_driver);
-MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
+MODULE_AUTHOR("TPM support <TPMsupport@list.st.com>");
MODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver");
MODULE_VERSION("1.3.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c
index 22d184884694..5149231f3de2 100644
--- a/drivers/char/tpm/st33zp24/spi.c
+++ b/drivers/char/tpm/st33zp24/spi.c
@@ -6,13 +6,9 @@
#include <linux/module.h>
#include <linux/spi/spi.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/tpm.h>
-#include <linux/platform_data/st33zp24.h>
#include "../tpm.h"
#include "st33zp24.h"
@@ -61,7 +57,6 @@ struct st33zp24_spi_phy {
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
- int io_lpcpd;
int latency;
};
@@ -218,115 +213,6 @@ static const struct st33zp24_phy_ops spi_phy_ops = {
.recv = st33zp24_spi_recv,
};
-static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
-
-static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
- { "lpcpd-gpios", &lpcpd_gpios, 1 },
- {},
-};
-
-static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
-{
- struct tpm_chip *chip = spi_get_drvdata(spi_dev);
- struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
- struct gpio_desc *gpiod_lpcpd;
- struct device *dev = &spi_dev->dev;
- int ret;
-
- ret = devm_acpi_dev_add_driver_gpios(dev, acpi_st33zp24_gpios);
- if (ret)
- return ret;
-
- /* Get LPCPD GPIO from ACPI */
- gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_lpcpd)) {
- dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n");
- phy->io_lpcpd = -1;
- /*
- * lpcpd pin is not specified. This is not an issue as
- * power management can be also managed by TPM specific
- * commands. So leave with a success status code.
- */
- return 0;
- }
-
- phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd);
-
- return 0;
-}
-
-static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev)
-{
- struct tpm_chip *chip = spi_get_drvdata(spi_dev);
- struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
- struct device_node *pp;
- int gpio;
- int ret;
-
- pp = spi_dev->dev.of_node;
- if (!pp) {
- dev_err(&spi_dev->dev, "No platform data\n");
- return -ENODEV;
- }
-
- /* Get GPIO from device tree */
- gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
- if (gpio < 0) {
- dev_err(&spi_dev->dev,
- "Failed to retrieve lpcpd-gpios from dts.\n");
- phy->io_lpcpd = -1;
- /*
- * lpcpd pin is not specified. This is not an issue as
- * power management can be also managed by TPM specific
- * commands. So leave with a success status code.
- */
- return 0;
- }
- /* GPIO request and configuration */
- ret = devm_gpio_request_one(&spi_dev->dev, gpio,
- GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
- if (ret) {
- dev_err(&spi_dev->dev, "Failed to request lpcpd pin\n");
- return -ENODEV;
- }
- phy->io_lpcpd = gpio;
-
- return 0;
-}
-
-static int st33zp24_spi_request_resources(struct spi_device *dev)
-{
- struct tpm_chip *chip = spi_get_drvdata(dev);
- struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
- struct st33zp24_platform_data *pdata;
- int ret;
-
- pdata = dev->dev.platform_data;
- if (!pdata) {
- dev_err(&dev->dev, "No platform data\n");
- return -ENODEV;
- }
-
- /* store for late use */
- phy->io_lpcpd = pdata->io_lpcpd;
-
- if (gpio_is_valid(pdata->io_lpcpd)) {
- ret = devm_gpio_request_one(&dev->dev,
- pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH,
- "TPM IO_LPCPD");
- if (ret) {
- dev_err(&dev->dev, "%s : reset gpio_request failed\n",
- __FILE__);
- return ret;
- }
- }
-
- return 0;
-}
-
/*
* st33zp24_spi_probe initialize the TPM device
* @param: dev, the spi_device description (TPM SPI description).
@@ -335,17 +221,8 @@ static int st33zp24_spi_request_resources(struct spi_device *dev)
*/
static int st33zp24_spi_probe(struct spi_device *dev)
{
- int ret;
- struct st33zp24_platform_data *pdata;
struct st33zp24_spi_phy *phy;
- /* Check SPI platform functionnalities */
- if (!dev) {
- pr_info("%s: dev is NULL. Device is not accessible.\n",
- __func__);
- return -ENODEV;
- }
-
phy = devm_kzalloc(&dev->dev, sizeof(struct st33zp24_spi_phy),
GFP_KERNEL);
if (!phy)
@@ -353,27 +230,11 @@ static int st33zp24_spi_probe(struct spi_device *dev)
phy->spi_device = dev;
- pdata = dev->dev.platform_data;
- if (!pdata && dev->dev.of_node) {
- ret = st33zp24_spi_of_request_resources(dev);
- if (ret)
- return ret;
- } else if (pdata) {
- ret = st33zp24_spi_request_resources(dev);
- if (ret)
- return ret;
- } else if (ACPI_HANDLE(&dev->dev)) {
- ret = st33zp24_spi_acpi_request_resources(dev);
- if (ret)
- return ret;
- }
-
phy->latency = st33zp24_spi_evaluate_latency(phy);
if (phy->latency <= 0)
return -ENODEV;
- return st33zp24_probe(phy, &spi_phy_ops, &dev->dev, dev->irq,
- phy->io_lpcpd);
+ return st33zp24_probe(phy, &spi_phy_ops, &dev->dev, dev->irq);
}
/*
@@ -394,13 +255,13 @@ static const struct spi_device_id st33zp24_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, st33zp24_spi_id);
-static const struct of_device_id of_st33zp24_spi_match[] = {
+static const struct of_device_id of_st33zp24_spi_match[] __maybe_unused = {
{ .compatible = "st,st33zp24-spi", },
{}
};
MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match);
-static const struct acpi_device_id st33zp24_spi_acpi_match[] = {
+static const struct acpi_device_id st33zp24_spi_acpi_match[] __maybe_unused = {
{"SMO3324"},
{}
};
@@ -411,7 +272,7 @@ static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend,
static struct spi_driver st33zp24_spi_driver = {
.driver = {
- .name = TPM_ST33_SPI,
+ .name = "st33zp24-spi",
.pm = &st33zp24_spi_ops,
.of_match_table = of_match_ptr(of_st33zp24_spi_match),
.acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match),
@@ -423,7 +284,7 @@ static struct spi_driver st33zp24_spi_driver = {
module_spi_driver(st33zp24_spi_driver);
-MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
+MODULE_AUTHOR("TPM support <TPMsupport@list.st.com>");
MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver");
MODULE_VERSION("1.3.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c
index 15b393e92c8e..2ed7815e4899 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.c
+++ b/drivers/char/tpm/st33zp24/st33zp24.c
@@ -4,6 +4,7 @@
* Copyright (C) 2009 - 2016 STMicroelectronics
*/
+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
@@ -12,7 +13,7 @@
#include <linux/freezer.h>
#include <linux/string.h>
#include <linux/interrupt.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/io.h>
@@ -299,7 +300,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
* send TPM commands through the I2C bus.
*/
static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
- size_t len)
+ size_t bufsiz, size_t len)
{
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u32 status, i, size, ordinal;
@@ -432,11 +433,18 @@ static const struct tpm_class_ops st33zp24_tpm = {
.req_canceled = st33zp24_req_canceled,
};
+static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
+
+static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
+ { "lpcpd-gpios", &lpcpd_gpios, 1 },
+ { },
+};
+
/*
* initialize the TPM device
*/
int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
- struct device *dev, int irq, int io_lpcpd)
+ struct device *dev, int irq)
{
int ret;
u8 intmask = 0;
@@ -463,6 +471,25 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
tpm_dev->locality = LOCALITY0;
+ if (ACPI_COMPANION(dev)) {
+ ret = devm_acpi_dev_add_driver_gpios(dev, acpi_st33zp24_gpios);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Get LPCPD GPIO. If lpcpd pin is not specified. This is not an
+ * issue as power management can be also managed by TPM specific
+ * commands.
+ */
+ tpm_dev->io_lpcpd = devm_gpiod_get_optional(dev, "lpcpd",
+ GPIOD_OUT_HIGH);
+ ret = PTR_ERR_OR_ZERO(tpm_dev->io_lpcpd);
+ if (ret) {
+ dev_err(dev, "failed to request lpcpd gpio: %d\n", ret);
+ return ret;
+ }
+
if (irq) {
/* INTERRUPT Setup */
init_waitqueue_head(&tpm_dev->read_queue);
@@ -525,8 +552,8 @@ int st33zp24_pm_suspend(struct device *dev)
int ret = 0;
- if (gpio_is_valid(tpm_dev->io_lpcpd))
- gpio_set_value(tpm_dev->io_lpcpd, 0);
+ if (tpm_dev->io_lpcpd)
+ gpiod_set_value_cansleep(tpm_dev->io_lpcpd, 0);
else
ret = tpm_pm_suspend(dev);
@@ -540,8 +567,8 @@ int st33zp24_pm_resume(struct device *dev)
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
int ret = 0;
- if (gpio_is_valid(tpm_dev->io_lpcpd)) {
- gpio_set_value(tpm_dev->io_lpcpd, 1);
+ if (tpm_dev->io_lpcpd) {
+ gpiod_set_value_cansleep(tpm_dev->io_lpcpd, 1);
ret = wait_for_stat(chip,
TPM_STS_VALID, chip->timeout_b,
&tpm_dev->read_queue, false);
@@ -555,7 +582,7 @@ int st33zp24_pm_resume(struct device *dev)
EXPORT_SYMBOL(st33zp24_pm_resume);
#endif
-MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
+MODULE_AUTHOR("TPM support <TPMsupport@list.st.com>");
MODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver");
MODULE_VERSION("1.3.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h
index b387a476c555..5acc85f711e6 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.h
+++ b/drivers/char/tpm/st33zp24/st33zp24.h
@@ -7,6 +7,9 @@
#ifndef __LOCAL_ST33ZP24_H__
#define __LOCAL_ST33ZP24_H__
+#define TPM_ST33_I2C "st33zp24-i2c"
+#define TPM_ST33_SPI "st33zp24-spi"
+
#define TPM_WRITE_DIRECTION 0x80
#define ST33ZP24_BUFSIZE 2048
@@ -17,7 +20,7 @@ struct st33zp24_dev {
int locality;
int irq;
u32 intrs;
- int io_lpcpd;
+ struct gpio_desc *io_lpcpd;
wait_queue_head_t read_queue;
};
@@ -33,6 +36,6 @@ int st33zp24_pm_resume(struct device *dev);
#endif
int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
- struct device *dev, int irq, int io_lpcpd);
+ struct device *dev, int irq);
void st33zp24_remove(struct tpm_chip *chip);
#endif /* __LOCAL_ST33ZP24_H__ */
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
new file mode 100644
index 000000000000..dc882fc9fa9e
--- /dev/null
+++ b/drivers/char/tpm/tpm-buf.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Handling of TPM command and other buffers.
+ */
+
+#include <linux/tpm_command.h>
+#include <linux/module.h>
+#include <linux/tpm.h>
+
+/**
+ * tpm_buf_init() - Allocate and initialize a TPM command
+ * @buf: A &tpm_buf
+ * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
+ * @ordinal: A command ordinal
+ *
+ * Return: 0 or -ENOMEM
+ */
+int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
+{
+ buf->data = (u8 *)__get_free_page(GFP_KERNEL);
+ if (!buf->data)
+ return -ENOMEM;
+
+ tpm_buf_reset(buf, tag, ordinal);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tpm_buf_init);
+
+/**
+ * tpm_buf_reset() - Initialize a TPM command
+ * @buf: A &tpm_buf
+ * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
+ * @ordinal: A command ordinal
+ */
+void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
+{
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+
+ WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
+ tag != TPM2_ST_SESSIONS && tag != 0);
+
+ buf->flags = 0;
+ buf->length = sizeof(*head);
+ head->tag = cpu_to_be16(tag);
+ head->length = cpu_to_be32(sizeof(*head));
+ head->ordinal = cpu_to_be32(ordinal);
+ buf->handles = 0;
+}
+EXPORT_SYMBOL_GPL(tpm_buf_reset);
+
+/**
+ * tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
+ * @buf: A @tpm_buf
+ *
+ * Return: 0 or -ENOMEM
+ */
+int tpm_buf_init_sized(struct tpm_buf *buf)
+{
+ buf->data = (u8 *)__get_free_page(GFP_KERNEL);
+ if (!buf->data)
+ return -ENOMEM;
+
+ tpm_buf_reset_sized(buf);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tpm_buf_init_sized);
+
+/**
+ * tpm_buf_reset_sized() - Initialize a sized buffer
+ * @buf: A &tpm_buf
+ */
+void tpm_buf_reset_sized(struct tpm_buf *buf)
+{
+ buf->flags = TPM_BUF_TPM2B;
+ buf->length = 2;
+ buf->data[0] = 0;
+ buf->data[1] = 0;
+}
+EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
+
+void tpm_buf_destroy(struct tpm_buf *buf)
+{
+ free_page((unsigned long)buf->data);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_destroy);
+
+/**
+ * tpm_buf_length() - Return the number of bytes consumed by the data
+ * @buf: A &tpm_buf
+ *
+ * Return: The number of bytes consumed by the buffer
+ */
+u32 tpm_buf_length(struct tpm_buf *buf)
+{
+ return buf->length;
+}
+EXPORT_SYMBOL_GPL(tpm_buf_length);
+
+/**
+ * tpm_buf_append() - Append data to an initialized buffer
+ * @buf: A &tpm_buf
+ * @new_data: A data blob
+ * @new_length: Size of the appended data
+ */
+void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
+{
+ /* Return silently if overflow has already happened. */
+ if (buf->flags & TPM_BUF_OVERFLOW)
+ return;
+
+ if ((buf->length + new_length) > PAGE_SIZE) {
+ WARN(1, "tpm_buf: write overflow\n");
+ buf->flags |= TPM_BUF_OVERFLOW;
+ return;
+ }
+
+ memcpy(&buf->data[buf->length], new_data, new_length);
+ buf->length += new_length;
+
+ if (buf->flags & TPM_BUF_TPM2B)
+ ((__be16 *)buf->data)[0] = cpu_to_be16(buf->length - 2);
+ else
+ ((struct tpm_header *)buf->data)->length = cpu_to_be32(buf->length);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_append);
+
+void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value)
+{
+ tpm_buf_append(buf, &value, 1);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_append_u8);
+
+void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value)
+{
+ __be16 value2 = cpu_to_be16(value);
+
+ tpm_buf_append(buf, (u8 *)&value2, 2);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_append_u16);
+
+void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
+{
+ __be32 value2 = cpu_to_be32(value);
+
+ tpm_buf_append(buf, (u8 *)&value2, 4);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
+
+/**
+ * tpm_buf_append_handle() - Add a handle
+ * @chip: &tpm_chip instance
+ * @buf: &tpm_buf instance
+ * @handle: a TPM object handle
+ *
+ * Add a handle to the buffer, and increase the count tracking the number of
+ * handles in the command buffer. Works only for command buffers.
+ */
+void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
+{
+ if (buf->flags & TPM_BUF_TPM2B) {
+ dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
+ return;
+ }
+
+ tpm_buf_append_u32(buf, handle);
+ buf->handles++;
+}
+
+/**
+ * tpm_buf_read() - Read from a TPM buffer
+ * @buf: &tpm_buf instance
+ * @offset: offset within the buffer
+ * @count: the number of bytes to read
+ * @output: the output buffer
+ */
+static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void *output)
+{
+ off_t next_offset;
+
+ /* Return silently if overflow has already happened. */
+ if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+ return;
+
+ next_offset = *offset + count;
+ if (next_offset > buf->length) {
+ WARN(1, "tpm_buf: read out of boundary\n");
+ buf->flags |= TPM_BUF_BOUNDARY_ERROR;
+ return;
+ }
+
+ memcpy(output, &buf->data[*offset], count);
+ *offset = next_offset;
+}
+
+/**
+ * tpm_buf_read_u8() - Read 8-bit word from a TPM buffer
+ * @buf: &tpm_buf instance
+ * @offset: offset within the buffer
+ *
+ * Return: next 8-bit word
+ */
+u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset)
+{
+ u8 value = 0;
+
+ tpm_buf_read(buf, offset, sizeof(value), &value);
+
+ return value;
+}
+EXPORT_SYMBOL_GPL(tpm_buf_read_u8);
+
+/**
+ * tpm_buf_read_u16() - Read 16-bit word from a TPM buffer
+ * @buf: &tpm_buf instance
+ * @offset: offset within the buffer
+ *
+ * Return: next 16-bit word
+ */
+u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset)
+{
+ u16 value = 0;
+
+ tpm_buf_read(buf, offset, sizeof(value), &value);
+
+ return be16_to_cpu(value);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_read_u16);
+
+/**
+ * tpm_buf_read_u32() - Read 32-bit word from a TPM buffer
+ * @buf: &tpm_buf instance
+ * @offset: offset within the buffer
+ *
+ * Return: next 32-bit word
+ */
+u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset)
+{
+ u32 value = 0;
+
+ tpm_buf_read(buf, offset, sizeof(value), &value);
+
+ return be32_to_cpu(value);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_read_u32);
+
+
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 783d65fc71f0..082b910ddf0d 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -28,8 +28,13 @@
DEFINE_IDR(dev_nums_idr);
static DEFINE_MUTEX(idr_lock);
-struct class *tpm_class;
-struct class *tpmrm_class;
+const struct class tpm_class = {
+ .name = "tpm",
+ .shutdown_pre = tpm_class_shutdown,
+};
+const struct class tpmrm_class = {
+ .name = "tpmrm",
+};
dev_t tpm_devt;
static int tpm_request_locality(struct tpm_chip *chip)
@@ -153,6 +158,9 @@ int tpm_try_get_ops(struct tpm_chip *chip)
{
int rc = -EIO;
+ if (chip->flags & TPM_CHIP_FLAG_DISABLE)
+ return rc;
+
get_device(&chip->dev);
down_read(&chip->ops_sem);
@@ -160,6 +168,11 @@ int tpm_try_get_ops(struct tpm_chip *chip)
goto out_ops;
mutex_lock(&chip->tpm_mutex);
+
+ /* tmp_chip_start may issue IO that is denied while suspended */
+ if (chip->flags & TPM_CHIP_FLAG_SUSPENDED)
+ goto out_lock;
+
rc = tpm_chip_start(chip);
if (rc)
goto out_lock;
@@ -218,42 +231,6 @@ struct tpm_chip *tpm_default_chip(void)
EXPORT_SYMBOL_GPL(tpm_default_chip);
/**
- * tpm_find_get_ops() - find and reserve a TPM chip
- * @chip: a &struct tpm_chip instance, %NULL for the default chip
- *
- * Finds a TPM chip and reserves its class device and operations. The chip must
- * be released with tpm_put_ops() after use.
- * This function is for internal use only. It supports existing TPM callers
- * by accepting NULL, but those callers should be converted to pass in a chip
- * directly.
- *
- * Return:
- * A reserved &struct tpm_chip instance.
- * %NULL if a chip is not found.
- * %NULL if the chip is not available.
- */
-struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip)
-{
- int rc;
-
- if (chip) {
- if (!tpm_try_get_ops(chip))
- return chip;
- return NULL;
- }
-
- chip = tpm_default_chip();
- if (!chip)
- return NULL;
- rc = tpm_try_get_ops(chip);
- /* release additional reference we got from tpm_default_chip() */
- put_device(&chip->dev);
- if (rc)
- return NULL;
- return chip;
-}
-
-/**
* tpm_dev_release() - free chip memory and the device number
* @dev: the character device for the TPM chip
*
@@ -267,10 +244,11 @@ static void tpm_dev_release(struct device *dev)
idr_remove(&dev_nums_idr, chip->dev_num);
mutex_unlock(&idr_lock);
- kfree(chip->log.bios_event_log);
kfree(chip->work_space.context_buf);
kfree(chip->work_space.session_buf);
- kfree(chip->allocated_banks);
+#ifdef CONFIG_TCG_TPM2_HMAC
+ kfree(chip->auth);
+#endif
kfree(chip);
}
@@ -283,13 +261,14 @@ static void tpm_dev_release(struct device *dev)
*
* Return: always 0 (i.e. success)
*/
-static int tpm_class_shutdown(struct device *dev)
+int tpm_class_shutdown(struct device *dev)
{
struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
down_write(&chip->ops_sem);
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
if (!tpm_chip_start(chip)) {
+ tpm2_end_auth_session(chip);
tpm2_shutdown(chip, TPM2_SU_CLEAR);
tpm_chip_stop(chip);
}
@@ -337,8 +316,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
device_initialize(&chip->dev);
- chip->dev.class = tpm_class;
- chip->dev.class->shutdown_pre = tpm_class_shutdown;
+ chip->dev.class = &tpm_class;
chip->dev.release = tpm_dev_release;
chip->dev.parent = pdev;
chip->dev.groups = chip->groups;
@@ -373,6 +351,11 @@ out:
}
EXPORT_SYMBOL_GPL(tpm_chip_alloc);
+static void tpm_put_device(void *dev)
+{
+ put_device(dev);
+}
+
/**
* tpmm_chip_alloc() - allocate a new struct tpm_chip instance
* @pdev: parent device to which the chip is associated
@@ -391,7 +374,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
return chip;
rc = devm_add_action_or_reset(pdev,
- (void (*)(void *)) put_device,
+ tpm_put_device,
&chip->dev);
if (rc)
return ERR_PTR(rc);
@@ -514,9 +497,20 @@ static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
return tpm_get_random(chip, data, max);
}
+static bool tpm_is_hwrng_enabled(struct tpm_chip *chip)
+{
+ if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM))
+ return false;
+ if (tpm_is_firmware_upgrade(chip))
+ return false;
+ if (chip->flags & TPM_CHIP_FLAG_HWRNG_DISABLED)
+ return false;
+ return true;
+}
+
static int tpm_add_hwrng(struct tpm_chip *chip)
{
- if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM) || tpm_is_firmware_upgrade(chip))
+ if (!tpm_is_hwrng_enabled(chip))
return 0;
snprintf(chip->hwrng_name, sizeof(chip->hwrng_name),
@@ -544,6 +538,42 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip)
}
/*
+ * tpm_chip_bootstrap() - Boostrap TPM chip after power on
+ * @chip: TPM chip to use.
+ *
+ * Initialize TPM chip after power on. This a one-shot function: subsequent
+ * calls will have no effect.
+ */
+int tpm_chip_bootstrap(struct tpm_chip *chip)
+{
+ int rc;
+
+ if (chip->flags & TPM_CHIP_FLAG_BOOTSTRAPPED)
+ return 0;
+
+ rc = tpm_chip_start(chip);
+ if (rc)
+ return rc;
+
+ rc = tpm_auto_startup(chip);
+ if (rc)
+ goto stop;
+
+ rc = tpm_get_pcr_allocation(chip);
+stop:
+ tpm_chip_stop(chip);
+
+ /*
+ * Unconditionally set, as driver initialization should cease, when the
+ * boostrapping process fails.
+ */
+ chip->flags |= TPM_CHIP_FLAG_BOOTSTRAPPED;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_chip_bootstrap);
+
+/*
* tpm_chip_register() - create a character device for the TPM chip
* @chip: TPM chip to use.
*
@@ -558,17 +588,7 @@ int tpm_chip_register(struct tpm_chip *chip)
{
int rc;
- rc = tpm_chip_start(chip);
- if (rc)
- return rc;
- rc = tpm_auto_startup(chip);
- if (rc) {
- tpm_chip_stop(chip);
- return rc;
- }
-
- rc = tpm_get_pcr_allocation(chip);
- tpm_chip_stop(chip);
+ rc = tpm_chip_bootstrap(chip);
if (rc)
return rc;
@@ -595,7 +615,7 @@ int tpm_chip_register(struct tpm_chip *chip)
return 0;
out_hwrng:
- if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip))
+ if (tpm_is_hwrng_enabled(chip))
hwrng_unregister(&chip->hwrng);
out_ppi:
tpm_bios_log_teardown(chip);
@@ -619,8 +639,18 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
*/
void tpm_chip_unregister(struct tpm_chip *chip)
{
+#ifdef CONFIG_TCG_TPM2_HMAC
+ int rc;
+
+ rc = tpm_try_get_ops(chip);
+ if (!rc) {
+ tpm2_end_auth_session(chip);
+ tpm_put_ops(chip);
+ }
+#endif
+
tpm_del_legacy_sysfs(chip);
- if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip))
+ if (tpm_is_hwrng_enabled(chip))
hwrng_unregister(&chip->hwrng);
tpm_bios_log_teardown(chip);
if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip))
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index dc4c0a0a5129..f942c0c8e402 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -27,6 +27,9 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
struct tpm_header *header = (void *)buf;
ssize_t ret, len;
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ tpm2_end_auth_session(chip);
+
ret = tpm2_prepare_space(chip, space, buf, bufsiz);
/* If the command is not implemented by the TPM, synthesize a
* response with a TPM2_RC_COMMAND_CODE return for user-space.
@@ -47,6 +50,8 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
if (!ret)
ret = tpm2_commit_space(chip, space, buf, &len);
+ else
+ tpm2_flush_space(chip);
out_rc:
return ret ? ret : len;
@@ -86,7 +91,7 @@ out:
static void user_reader_timeout(struct timer_list *t)
{
- struct file_priv *priv = from_timer(priv, t, user_read_timer);
+ struct file_priv *priv = timer_container_of(priv, t, user_read_timer);
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
task_tgid_nr(current));
@@ -155,7 +160,7 @@ ssize_t tpm_common_read(struct file *file, char __user *buf,
out:
if (!priv->response_length) {
*off = 0;
- del_singleshot_timer_sync(&priv->user_read_timer);
+ timer_delete_sync(&priv->user_read_timer);
flush_work(&priv->timeout_work);
}
mutex_unlock(&priv->buffer_mutex);
@@ -262,7 +267,7 @@ __poll_t tpm_common_poll(struct file *file, poll_table *wait)
void tpm_common_release(struct file *file, struct file_priv *priv)
{
flush_work(&priv->async_work);
- del_singleshot_timer_sync(&priv->user_read_timer);
+ timer_delete_sync(&priv->user_read_timer);
flush_work(&priv->timeout_work);
file->private_data = NULL;
priv->response_length = 0;
@@ -270,7 +275,8 @@ void tpm_common_release(struct file *file, struct file_priv *priv)
int __init tpm_dev_common_init(void)
{
- tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0);
+ tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
return !tpm_dev_wq ? -ENOMEM : 0;
}
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index e2c0baa69fef..97c94b5e9340 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -59,7 +59,6 @@ static int tpm_release(struct inode *inode, struct file *file)
const struct file_operations tpm_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.open = tpm_open,
.read = tpm_common_read,
.write = tpm_common_write,
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 1621ce818705..f745a098908b 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -52,12 +52,43 @@ MODULE_PARM_DESC(suspend_pcr,
unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
{
if (chip->flags & TPM_CHIP_FLAG_TPM2)
- return tpm2_calc_ordinal_duration(chip, ordinal);
+ return tpm2_calc_ordinal_duration(ordinal);
else
return tpm1_calc_ordinal_duration(chip, ordinal);
}
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
+static void tpm_chip_cancel(struct tpm_chip *chip)
+{
+ if (!chip->ops->cancel)
+ return;
+
+ chip->ops->cancel(chip);
+}
+
+static u8 tpm_chip_status(struct tpm_chip *chip)
+{
+ if (!chip->ops->status)
+ return 0;
+
+ return chip->ops->status(chip);
+}
+
+static bool tpm_chip_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ if (!chip->ops->req_canceled)
+ return false;
+
+ return chip->ops->req_canceled(chip, status);
+}
+
+static bool tpm_transmit_completed(u8 status, struct tpm_chip *chip)
+{
+ u8 status_masked = status & chip->ops->req_complete_mask;
+
+ return status_masked == chip->ops->req_complete_val;
+}
+
static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
{
struct tpm_header *header = buf;
@@ -82,7 +113,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
return -E2BIG;
}
- rc = chip->ops->send(chip, buf, count);
+ rc = chip->ops->send(chip, buf, bufsiz, count);
if (rc < 0) {
if (rc != -EPIPE)
dev_err(&chip->dev,
@@ -90,8 +121,19 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
return rc;
}
- /* A sanity check. send() should just return zero on success e.g.
- * not the command length.
+ /*
+ * Synchronous devices return the response directly during the send()
+ * call in the same buffer.
+ */
+ if (chip->flags & TPM_CHIP_FLAG_SYNC) {
+ len = rc;
+ rc = 0;
+ goto out_sync;
+ }
+
+ /*
+ * A sanity check. send() of asynchronous devices should just return
+ * zero on success e.g. not the command length.
*/
if (rc > 0) {
dev_warn(&chip->dev,
@@ -104,12 +146,11 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
do {
- u8 status = chip->ops->status(chip);
- if ((status & chip->ops->req_complete_mask) ==
- chip->ops->req_complete_val)
+ u8 status = tpm_chip_status(chip);
+ if (tpm_transmit_completed(status, chip))
goto out_recv;
- if (chip->ops->req_canceled(chip, status)) {
+ if (tpm_chip_req_canceled(chip, status)) {
dev_err(&chip->dev, "Operation Canceled\n");
return -ECANCELED;
}
@@ -118,7 +159,14 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
rmb();
} while (time_before(jiffies, stop));
- chip->ops->cancel(chip);
+ /*
+ * Check for completion one more time, just in case the device reported
+ * it while the driver was sleeping in the busy loop above.
+ */
+ if (tpm_transmit_completed(tpm_chip_status(chip), chip))
+ goto out_recv;
+
+ tpm_chip_cancel(chip);
dev_err(&chip->dev, "Operation Timed out\n");
return -ETIME;
@@ -127,7 +175,10 @@ out_recv:
if (len < 0) {
rc = len;
dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc);
- } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length))
+ return rc;
+ }
+out_sync:
+ if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length))
rc = -EFAULT;
return rc ? rc : len;
@@ -232,6 +283,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
if (len < min_rsp_body_length + TPM_HEADER_SIZE)
return -EFAULT;
+ buf->length = len;
return 0;
}
EXPORT_SYMBOL_GPL(tpm_transmit_cmd);
@@ -261,10 +313,13 @@ int tpm_is_tpm2(struct tpm_chip *chip)
{
int rc;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
tpm_put_ops(chip);
@@ -286,10 +341,13 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
{
int rc;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL);
else
@@ -317,10 +375,13 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
int rc;
int i;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
for (i = 0; i < chip->nr_allocated_banks; i++) {
if (digests[i].alg_id != chip->allocated_banks[i].alg_id) {
rc = -EINVAL;
@@ -342,31 +403,6 @@ out:
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);
-/**
- * tpm_send - send a TPM command
- * @chip: a &struct tpm_chip instance, %NULL for the default chip
- * @cmd: a TPM command buffer
- * @buflen: the length of the TPM command buffer
- *
- * Return: same as with tpm_transmit_cmd()
- */
-int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
-{
- struct tpm_buf buf;
- int rc;
-
- chip = tpm_find_get_ops(chip);
- if (!chip)
- return -ENODEV;
-
- buf.data = cmd;
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command");
-
- tpm_put_ops(chip);
- return rc;
-}
-EXPORT_SYMBOL_GPL(tpm_send);
-
int tpm_auto_startup(struct tpm_chip *chip)
{
int rc;
@@ -394,6 +430,13 @@ int tpm_pm_suspend(struct device *dev)
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc) {
+ /* Can be safely set out of locks, as no action cannot race: */
+ chip->flags |= TPM_CHIP_FLAG_SUSPENDED;
+ goto out;
+ }
+
if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED)
goto suspended;
@@ -401,17 +444,22 @@ int tpm_pm_suspend(struct device *dev)
!pm_suspend_via_firmware())
goto suspended;
- if (!tpm_chip_start(chip)) {
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_shutdown(chip, TPM2_SU_STATE);
- else
- rc = tpm1_pm_suspend(chip, tpm_suspend_pcr);
-
- tpm_chip_stop(chip);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ tpm2_end_auth_session(chip);
+ tpm2_shutdown(chip, TPM2_SU_STATE);
+ goto suspended;
}
+ rc = tpm1_pm_suspend(chip, tpm_suspend_pcr);
+
suspended:
- return rc;
+ chip->flags |= TPM_CHIP_FLAG_SUSPENDED;
+ tpm_put_ops(chip);
+
+out:
+ if (rc)
+ dev_err(dev, "Ignoring error %d while suspending\n", rc);
+ return 0;
}
EXPORT_SYMBOL_GPL(tpm_pm_suspend);
@@ -426,6 +474,14 @@ int tpm_pm_resume(struct device *dev)
if (chip == NULL)
return -ENODEV;
+ chip->flags &= ~TPM_CHIP_FLAG_SUSPENDED;
+
+ /*
+ * Guarantee that SUSPENDED is written last, so that hwrng does not
+ * activate before the chip has been fully resumed.
+ */
+ wmb();
+
return 0;
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
@@ -445,10 +501,13 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
if (!out || max > TPM_MAX_RNG_DATA)
return -EINVAL;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm2_get_random(chip, out, max);
else
@@ -463,16 +522,15 @@ static int __init tpm_init(void)
{
int rc;
- tpm_class = class_create(THIS_MODULE, "tpm");
- if (IS_ERR(tpm_class)) {
+ rc = class_register(&tpm_class);
+ if (rc) {
pr_err("couldn't create tpm class\n");
- return PTR_ERR(tpm_class);
+ return rc;
}
- tpmrm_class = class_create(THIS_MODULE, "tpmrm");
- if (IS_ERR(tpmrm_class)) {
+ rc = class_register(&tpmrm_class);
+ if (rc) {
pr_err("couldn't create tpmrm class\n");
- rc = PTR_ERR(tpmrm_class);
goto out_destroy_tpm_class;
}
@@ -493,9 +551,9 @@ static int __init tpm_init(void)
out_unreg_chrdev:
unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES);
out_destroy_tpmrm_class:
- class_destroy(tpmrm_class);
+ class_unregister(&tpmrm_class);
out_destroy_tpm_class:
- class_destroy(tpm_class);
+ class_unregister(&tpm_class);
return rc;
}
@@ -503,8 +561,8 @@ out_destroy_tpm_class:
static void __exit tpm_exit(void)
{
idr_destroy(&dev_nums_idr);
- class_destroy(tpm_class);
- class_destroy(tpmrm_class);
+ class_unregister(&tpm_class);
+ class_unregister(&tpmrm_class);
unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
tpm_dev_common_exit();
}
@@ -512,7 +570,7 @@ static void __exit tpm_exit(void)
subsys_initcall(tpm_init);
module_exit(tpm_exit);
-MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_AUTHOR("Leendert van Doorn <leendert@watson.ibm.com>");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 54c71473aa29..94231f052ea7 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -309,6 +309,21 @@ static ssize_t tpm_version_major_show(struct device *dev,
}
static DEVICE_ATTR_RO(tpm_version_major);
+#ifdef CONFIG_TCG_TPM2_HMAC
+static ssize_t null_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = to_tpm_chip(dev);
+ int size = TPM2_NAME_SIZE;
+
+ bin2hex(buf, chip->null_key_name, size);
+ size *= 2;
+ buf[size++] = '\n';
+ return size;
+}
+static DEVICE_ATTR_RO(null_name);
+#endif
+
static struct attribute *tpm1_dev_attrs[] = {
&dev_attr_pubek.attr,
&dev_attr_pcrs.attr,
@@ -326,6 +341,9 @@ static struct attribute *tpm1_dev_attrs[] = {
static struct attribute *tpm2_dev_attrs[] = {
&dev_attr_tpm_version_major.attr,
+#ifdef CONFIG_TCG_TPM2_HMAC
+ &dev_attr_null_name.attr,
+#endif
NULL
};
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 24ee4e1cc452..02c07fef41ba 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -28,7 +28,7 @@
#include <linux/tpm_eventlog.h>
#ifdef CONFIG_X86
-#include <asm/intel-family.h>
+#include <asm/cpu_device_id.h>
#endif
#define TPM_MINOR 224 /* officially assigned */
@@ -150,6 +150,79 @@ enum tpm_sub_capabilities {
TPM_CAP_PROP_TIS_DURATION = 0x120,
};
+enum tpm2_pt_props {
+ TPM2_PT_NONE = 0x00000000,
+ TPM2_PT_GROUP = 0x00000100,
+ TPM2_PT_FIXED = TPM2_PT_GROUP * 1,
+ TPM2_PT_FAMILY_INDICATOR = TPM2_PT_FIXED + 0,
+ TPM2_PT_LEVEL = TPM2_PT_FIXED + 1,
+ TPM2_PT_REVISION = TPM2_PT_FIXED + 2,
+ TPM2_PT_DAY_OF_YEAR = TPM2_PT_FIXED + 3,
+ TPM2_PT_YEAR = TPM2_PT_FIXED + 4,
+ TPM2_PT_MANUFACTURER = TPM2_PT_FIXED + 5,
+ TPM2_PT_VENDOR_STRING_1 = TPM2_PT_FIXED + 6,
+ TPM2_PT_VENDOR_STRING_2 = TPM2_PT_FIXED + 7,
+ TPM2_PT_VENDOR_STRING_3 = TPM2_PT_FIXED + 8,
+ TPM2_PT_VENDOR_STRING_4 = TPM2_PT_FIXED + 9,
+ TPM2_PT_VENDOR_TPM_TYPE = TPM2_PT_FIXED + 10,
+ TPM2_PT_FIRMWARE_VERSION_1 = TPM2_PT_FIXED + 11,
+ TPM2_PT_FIRMWARE_VERSION_2 = TPM2_PT_FIXED + 12,
+ TPM2_PT_INPUT_BUFFER = TPM2_PT_FIXED + 13,
+ TPM2_PT_HR_TRANSIENT_MIN = TPM2_PT_FIXED + 14,
+ TPM2_PT_HR_PERSISTENT_MIN = TPM2_PT_FIXED + 15,
+ TPM2_PT_HR_LOADED_MIN = TPM2_PT_FIXED + 16,
+ TPM2_PT_ACTIVE_SESSIONS_MAX = TPM2_PT_FIXED + 17,
+ TPM2_PT_PCR_COUNT = TPM2_PT_FIXED + 18,
+ TPM2_PT_PCR_SELECT_MIN = TPM2_PT_FIXED + 19,
+ TPM2_PT_CONTEXT_GAP_MAX = TPM2_PT_FIXED + 20,
+ TPM2_PT_NV_COUNTERS_MAX = TPM2_PT_FIXED + 22,
+ TPM2_PT_NV_INDEX_MAX = TPM2_PT_FIXED + 23,
+ TPM2_PT_MEMORY = TPM2_PT_FIXED + 24,
+ TPM2_PT_CLOCK_UPDATE = TPM2_PT_FIXED + 25,
+ TPM2_PT_CONTEXT_HASH = TPM2_PT_FIXED + 26,
+ TPM2_PT_CONTEXT_SYM = TPM2_PT_FIXED + 27,
+ TPM2_PT_CONTEXT_SYM_SIZE = TPM2_PT_FIXED + 28,
+ TPM2_PT_ORDERLY_COUNT = TPM2_PT_FIXED + 29,
+ TPM2_PT_MAX_COMMAND_SIZE = TPM2_PT_FIXED + 30,
+ TPM2_PT_MAX_RESPONSE_SIZE = TPM2_PT_FIXED + 31,
+ TPM2_PT_MAX_DIGEST = TPM2_PT_FIXED + 32,
+ TPM2_PT_MAX_OBJECT_CONTEXT = TPM2_PT_FIXED + 33,
+ TPM2_PT_MAX_SESSION_CONTEXT = TPM2_PT_FIXED + 34,
+ TPM2_PT_PS_FAMILY_INDICATOR = TPM2_PT_FIXED + 35,
+ TPM2_PT_PS_LEVEL = TPM2_PT_FIXED + 36,
+ TPM2_PT_PS_REVISION = TPM2_PT_FIXED + 37,
+ TPM2_PT_PS_DAY_OF_YEAR = TPM2_PT_FIXED + 38,
+ TPM2_PT_PS_YEAR = TPM2_PT_FIXED + 39,
+ TPM2_PT_SPLIT_MAX = TPM2_PT_FIXED + 40,
+ TPM2_PT_TOTAL_COMMANDS = TPM2_PT_FIXED + 41,
+ TPM2_PT_LIBRARY_COMMANDS = TPM2_PT_FIXED + 42,
+ TPM2_PT_VENDOR_COMMANDS = TPM2_PT_FIXED + 43,
+ TPM2_PT_NV_BUFFER_MAX = TPM2_PT_FIXED + 44,
+ TPM2_PT_MODES = TPM2_PT_FIXED + 45,
+ TPM2_PT_MAX_CAP_BUFFER = TPM2_PT_FIXED + 46,
+ TPM2_PT_VAR = TPM2_PT_GROUP * 2,
+ TPM2_PT_PERMANENT = TPM2_PT_VAR + 0,
+ TPM2_PT_STARTUP_CLEAR = TPM2_PT_VAR + 1,
+ TPM2_PT_HR_NV_INDEX = TPM2_PT_VAR + 2,
+ TPM2_PT_HR_LOADED = TPM2_PT_VAR + 3,
+ TPM2_PT_HR_LOADED_AVAIL = TPM2_PT_VAR + 4,
+ TPM2_PT_HR_ACTIVE = TPM2_PT_VAR + 5,
+ TPM2_PT_HR_ACTIVE_AVAIL = TPM2_PT_VAR + 6,
+ TPM2_PT_HR_TRANSIENT_AVAIL = TPM2_PT_VAR + 7,
+ TPM2_PT_HR_PERSISTENT = TPM2_PT_VAR + 8,
+ TPM2_PT_HR_PERSISTENT_AVAIL = TPM2_PT_VAR + 9,
+ TPM2_PT_NV_COUNTERS = TPM2_PT_VAR + 10,
+ TPM2_PT_NV_COUNTERS_AVAIL = TPM2_PT_VAR + 11,
+ TPM2_PT_ALGORITHM_SET = TPM2_PT_VAR + 12,
+ TPM2_PT_LOADED_CURVES = TPM2_PT_VAR + 13,
+ TPM2_PT_LOCKOUT_COUNTER = TPM2_PT_VAR + 14,
+ TPM2_PT_MAX_AUTH_FAIL = TPM2_PT_VAR + 15,
+ TPM2_PT_LOCKOUT_INTERVAL = TPM2_PT_VAR + 16,
+ TPM2_PT_LOCKOUT_RECOVERY = TPM2_PT_VAR + 17,
+ TPM2_PT_NV_WRITE_RECOVERY = TPM2_PT_VAR + 18,
+ TPM2_PT_AUDIT_COUNTER_0 = TPM2_PT_VAR + 19,
+ TPM2_PT_AUDIT_COUNTER_1 = TPM2_PT_VAR + 20,
+};
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
* bytes, but 128 is still a relatively large number of random bytes and
@@ -157,8 +230,8 @@ enum tpm_sub_capabilities {
* compiler warnings about stack frame size. */
#define TPM_MAX_RNG_DATA 128
-extern struct class *tpm_class;
-extern struct class *tpmrm_class;
+extern const struct class tpm_class;
+extern const struct class tpmrm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
extern const struct file_operations tpmrm_fops;
@@ -183,6 +256,7 @@ int tpm1_get_pcr_allocation(struct tpm_chip *chip);
unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
int tpm_pm_suspend(struct device *dev);
int tpm_pm_resume(struct device *dev);
+int tpm_class_shutdown(struct device *dev);
static inline void tpm_msleep(unsigned int delay_msec)
{
@@ -190,9 +264,9 @@ static inline void tpm_msleep(unsigned int delay_msec)
delay_msec * 1000);
};
+int tpm_chip_bootstrap(struct tpm_chip *chip);
int tpm_chip_start(struct tpm_chip *chip);
void tpm_chip_stop(struct tpm_chip *chip);
-struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip);
struct tpm_chip *tpm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops);
@@ -224,7 +298,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
int tpm2_auto_startup(struct tpm_chip *chip);
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
-unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
+unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
int tpm2_probe(struct tpm_chip *chip);
int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip);
int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
@@ -237,9 +311,23 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, void *buf,
size_t *bufsiz);
int tpm_devs_add(struct tpm_chip *chip);
void tpm_devs_remove(struct tpm_chip *chip);
+int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
+ unsigned int buf_size, unsigned int *offset);
+int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
+ unsigned int *offset, u32 *handle);
void tpm_bios_log_setup(struct tpm_chip *chip);
void tpm_bios_log_teardown(struct tpm_chip *chip);
int tpm_dev_common_init(void);
void tpm_dev_common_exit(void);
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+int tpm2_sessions_init(struct tpm_chip *chip);
+#else
+static inline int tpm2_sessions_init(struct tpm_chip *chip)
+{
+ return 0;
+}
+#endif
+
#endif
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index cf64c7385105..b49a790f1bd5 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -799,11 +799,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
*/
int tpm1_get_pcr_allocation(struct tpm_chip *chip)
{
- chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks),
- GFP_KERNEL);
- if (!chip->allocated_banks)
- return -ENOMEM;
-
chip->allocated_banks[0].alg_id = TPM_ALG_SHA1;
chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1];
chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 65d03867e114..3a77be7ebf4a 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -11,10 +11,17 @@
* used by the kernel internally.
*/
+#include "linux/dev_printk.h"
+#include "linux/tpm.h"
#include "tpm.h"
#include <crypto/hash_info.h>
+#include <linux/unaligned.h>
-static struct tpm2_hash tpm2_hash_map[] = {
+static bool disable_pcr_integrity;
+module_param(disable_pcr_integrity, bool, 0444);
+MODULE_PARM_DESC(disable_pcr_integrity, "Disable integrity protection of TPM2_PCR_Extend");
+
+struct tpm2_hash tpm2_hash_map[] = {
{HASH_ALGO_SHA1, TPM_ALG_SHA1},
{HASH_ALGO_SHA256, TPM_ALG_SHA256},
{HASH_ALGO_SHA384, TPM_ALG_SHA384},
@@ -22,122 +29,71 @@ static struct tpm2_hash tpm2_hash_map[] = {
{HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
};
+int tpm2_find_hash_alg(unsigned int crypto_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++)
+ if (crypto_id == tpm2_hash_map[i].crypto_id)
+ return tpm2_hash_map[i].tpm_id;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(tpm2_find_hash_alg);
+
int tpm2_get_timeouts(struct tpm_chip *chip)
{
- /* Fixed timeouts for TPM2 */
chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
-
- /* PTP spec timeouts */
- chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT);
- chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM);
- chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG);
-
- /* Key creation commands long timeouts */
- chip->duration[TPM_LONG_LONG] =
- msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
-
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
-
return 0;
}
-/**
- * tpm2_ordinal_duration_index() - returns an index to the chip duration table
- * @ordinal: TPM command ordinal.
- *
- * The function returns an index to the chip duration table
- * (enum tpm_duration), that describes the maximum amount of
- * time the chip could take to return the result for a particular ordinal.
- *
- * The values of the MEDIUM, and LONG durations are taken
- * from the PC Client Profile (PTP) specification (750, 2000 msec)
- *
- * LONG_LONG is for commands that generates keys which empirically takes
- * a longer time on some systems.
- *
- * Return:
- * * TPM_MEDIUM
- * * TPM_LONG
- * * TPM_LONG_LONG
- * * TPM_UNDEFINED
+/*
+ * Contains the maximum durations in milliseconds for TPM2 commands.
*/
-static u8 tpm2_ordinal_duration_index(u32 ordinal)
-{
- switch (ordinal) {
- /* Startup */
- case TPM2_CC_STARTUP: /* 144 */
- return TPM_MEDIUM;
-
- case TPM2_CC_SELF_TEST: /* 143 */
- return TPM_LONG;
-
- case TPM2_CC_GET_RANDOM: /* 17B */
- return TPM_LONG;
-
- case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
- return TPM_MEDIUM;
- case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
- return TPM_MEDIUM;
- case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
- return TPM_MEDIUM;
- case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
- return TPM_MEDIUM;
-
- case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
- return TPM_LONG_LONG;
-
- case TPM2_CC_PCR_EXTEND: /* 182 */
- return TPM_MEDIUM;
-
- case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
- return TPM_LONG;
- case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
- return TPM_LONG;
-
- case TPM2_CC_GET_CAPABILITY: /* 17A */
- return TPM_MEDIUM;
-
- case TPM2_CC_NV_READ: /* 14E */
- return TPM_LONG;
-
- case TPM2_CC_CREATE_PRIMARY: /* 131 */
- return TPM_LONG_LONG;
- case TPM2_CC_CREATE: /* 153 */
- return TPM_LONG_LONG;
- case TPM2_CC_CREATE_LOADED: /* 191 */
- return TPM_LONG_LONG;
-
- default:
- return TPM_UNDEFINED;
- }
-}
+static const struct {
+ unsigned long ordinal;
+ unsigned long duration;
+} tpm2_ordinal_duration_map[] = {
+ {TPM2_CC_STARTUP, 750},
+ {TPM2_CC_SELF_TEST, 3000},
+ {TPM2_CC_GET_RANDOM, 2000},
+ {TPM2_CC_SEQUENCE_UPDATE, 750},
+ {TPM2_CC_SEQUENCE_COMPLETE, 750},
+ {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
+ {TPM2_CC_HASH_SEQUENCE_START, 750},
+ {TPM2_CC_VERIFY_SIGNATURE, 30000},
+ {TPM2_CC_PCR_EXTEND, 750},
+ {TPM2_CC_HIERARCHY_CONTROL, 2000},
+ {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
+ {TPM2_CC_GET_CAPABILITY, 750},
+ {TPM2_CC_NV_READ, 2000},
+ {TPM2_CC_CREATE_PRIMARY, 30000},
+ {TPM2_CC_CREATE, 30000},
+ {TPM2_CC_CREATE_LOADED, 30000},
+};
/**
- * tpm2_calc_ordinal_duration() - calculate the maximum command duration
- * @chip: TPM chip to use.
+ * tpm2_calc_ordinal_duration() - Calculate the maximum command duration
* @ordinal: TPM command ordinal.
*
- * The function returns the maximum amount of time the chip could take
- * to return the result for a particular ordinal in jiffies.
- *
- * Return: A maximal duration time for an ordinal in jiffies.
+ * Returns the maximum amount of time the chip is expected by kernel to
+ * take in jiffies.
*/
-unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
+unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
{
- unsigned int index;
+ int i;
- index = tpm2_ordinal_duration_index(ordinal);
+ for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
+ if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
+ return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
- if (index != TPM_UNDEFINED)
- return chip->duration[index];
- else
- return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
+ return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
}
-
struct tpm2_pcr_read_out {
__be32 update_cnt;
__be32 pcr_selects_cnt;
@@ -216,13 +172,6 @@ out:
return rc;
}
-struct tpm2_null_auth_area {
- __be32 handle;
- __be16 nonce_size;
- u8 attributes;
- __be16 auth_size;
-} __packed;
-
/**
* tpm2_pcr_extend() - extend a PCR value
*
@@ -236,24 +185,34 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests)
{
struct tpm_buf buf;
- struct tpm2_null_auth_area auth_area;
int rc;
int i;
+ if (!disable_pcr_integrity) {
+ rc = tpm2_start_auth_session(chip);
+ if (rc)
+ return rc;
+ }
+
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
- if (rc)
+ if (rc) {
+ if (!disable_pcr_integrity)
+ tpm2_end_auth_session(chip);
return rc;
+ }
- tpm_buf_append_u32(&buf, pcr_idx);
-
- auth_area.handle = cpu_to_be32(TPM2_RS_PW);
- auth_area.nonce_size = 0;
- auth_area.attributes = 0;
- auth_area.auth_size = 0;
+ if (!disable_pcr_integrity) {
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
+ tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
+ } else {
+ tpm_buf_append_handle(chip, &buf, pcr_idx);
+ tpm_buf_append_auth(chip, &buf, NULL, 0);
+ }
- tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
- tpm_buf_append(&buf, (const unsigned char *)&auth_area,
- sizeof(auth_area));
tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
for (i = 0; i < chip->nr_allocated_banks; i++) {
@@ -262,7 +221,17 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
+ if (!disable_pcr_integrity) {
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
+ }
+
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
+ if (!disable_pcr_integrity)
+ rc = tpm_buf_check_hmac_response(chip, &buf, rc);
tpm_buf_destroy(&buf);
@@ -288,6 +257,7 @@ struct tpm2_get_random_out {
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm2_get_random_out *out;
+ struct tpm_header *head;
struct tpm_buf buf;
u32 recd;
u32 num_bytes = max;
@@ -295,29 +265,59 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
int total = 0;
int retries = 5;
u8 *dest_ptr = dest;
+ off_t offset;
if (!num_bytes || max > TPM_MAX_RNG_DATA)
return -EINVAL;
- err = tpm_buf_init(&buf, 0, 0);
+ err = tpm2_start_auth_session(chip);
if (err)
return err;
+ err = tpm_buf_init(&buf, 0, 0);
+ if (err) {
+ tpm2_end_auth_session(chip);
+ return err;
+ }
+
do {
- tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM);
+ tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
+ if (tpm2_chip_auth(chip)) {
+ tpm_buf_append_hmac_session(chip, &buf,
+ TPM2_SA_ENCRYPT |
+ TPM2_SA_CONTINUE_SESSION,
+ NULL, 0);
+ } else {
+ offset = buf.handles * 4 + TPM_HEADER_SIZE;
+ head = (struct tpm_header *)buf.data;
+ if (tpm_buf_length(&buf) == offset)
+ head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+ }
tpm_buf_append_u16(&buf, num_bytes);
+ err = tpm_buf_fill_hmac_session(chip, &buf);
+ if (err) {
+ tpm_buf_destroy(&buf);
+ return err;
+ }
+
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
"attempting get random");
+ err = tpm_buf_check_hmac_response(chip, &buf, err);
if (err) {
if (err > 0)
err = -EIO;
goto out;
}
- out = (struct tpm2_get_random_out *)
- &buf.data[TPM_HEADER_SIZE];
+ head = (struct tpm_header *)buf.data;
+ offset = TPM_HEADER_SIZE;
+ /* Skip the parameter size field: */
+ if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
+ offset += 4;
+
+ out = (struct tpm2_get_random_out *)&buf.data[offset];
recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
if (tpm_buf_length(&buf) <
TPM_HEADER_SIZE +
@@ -334,9 +334,11 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
} while (retries-- && total < max);
tpm_buf_destroy(&buf);
+
return total ? total : -EIO;
out:
tpm_buf_destroy(&buf);
+ tpm2_end_auth_session(chip);
return err;
}
@@ -574,11 +576,9 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
nr_possible_banks = be32_to_cpup(
(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
-
- chip->allocated_banks = kcalloc(nr_possible_banks,
- sizeof(*chip->allocated_banks),
- GFP_KERNEL);
- if (!chip->allocated_banks) {
+ if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
+ pr_err("tpm: out of bank capacity: %u > %u\n",
+ nr_possible_banks, TPM2_MAX_PCR_BANKS);
rc = -ENOMEM;
goto out;
}
@@ -759,6 +759,11 @@ int tpm2_auto_startup(struct tpm_chip *chip)
rc = 0;
}
+ if (rc)
+ goto out;
+
+ rc = tpm2_sessions_init(chip);
+
out:
/*
* Infineon TPM in field upgrade mode will return no data for the number
@@ -777,10 +782,12 @@ out:
int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
{
+ u32 cc_mask;
int i;
+ cc_mask = 1 << TPM2_CC_ATTR_VENDOR | GENMASK(15, 0);
for (i = 0; i < chip->nr_commands; i++)
- if (cc == (chip->cc_attrs_tbl[i] & GENMASK(15, 0)))
+ if (cc == (chip->cc_attrs_tbl[i] & cc_mask))
return i;
return -1;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
new file mode 100644
index 000000000000..4149379665c4
--- /dev/null
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -0,0 +1,1398 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2018 James.Bottomley@HansenPartnership.com
+ *
+ * Cryptographic helper routines for handling TPM2 sessions for
+ * authorization HMAC and request response encryption.
+ *
+ * The idea is to ensure that every TPM command is HMAC protected by a
+ * session, meaning in-flight tampering would be detected and in
+ * addition all sensitive inputs and responses should be encrypted.
+ *
+ * The basic way this works is to use a TPM feature called salted
+ * sessions where a random secret used in session construction is
+ * encrypted to the public part of a known TPM key. The problem is we
+ * have no known keys, so initially a primary Elliptic Curve key is
+ * derived from the NULL seed (we use EC because most TPMs generate
+ * these keys much faster than RSA ones). The curve used is NIST_P256
+ * because that's now mandated to be present in 'TCG TPM v2.0
+ * Provisioning Guidance'
+ *
+ * Threat problems: the initial TPM2_CreatePrimary is not (and cannot
+ * be) session protected, so a clever Man in the Middle could return a
+ * public key they control to this command and from there intercept
+ * and decode all subsequent session based transactions. The kernel
+ * cannot mitigate this threat but, after boot, userspace can get
+ * proof this has not happened by asking the TPM to certify the NULL
+ * key. This certification would chain back to the TPM Endorsement
+ * Certificate and prove the NULL seed primary had not been tampered
+ * with and thus all sessions must have been cryptographically secure.
+ * To assist with this, the initial NULL seed public key name is made
+ * available in a sysfs file.
+ *
+ * Use of these functions:
+ *
+ * The design is all the crypto, hash and hmac gunk is confined in this
+ * file and never needs to be seen even by the kernel internal user. To
+ * the user there's an init function tpm2_sessions_init() that needs to
+ * be called once per TPM which generates the NULL seed primary key.
+ *
+ * These are the usage functions:
+ *
+ * tpm2_end_auth_session() kills the session and frees the resources.
+ * Under normal operation this function is done by
+ * tpm_buf_check_hmac_response(), so this is only to be used on
+ * error legs where the latter is not executed.
+ * tpm_buf_append_name() to add a handle to the buffer. This must be
+ * used in place of the usual tpm_buf_append_u32() for adding
+ * handles because handles have to be processed specially when
+ * calculating the HMAC. In particular, for NV, volatile and
+ * permanent objects you now need to provide the name.
+ * tpm_buf_append_hmac_session() which appends the hmac session to the
+ * buf in the same way tpm_buf_append_auth does().
+ * tpm_buf_fill_hmac_session() This calculates the correct hash and
+ * places it in the buffer. It must be called after the complete
+ * command buffer is finalized so it can fill in the correct HMAC
+ * based on the parameters.
+ * tpm_buf_check_hmac_response() which checks the session response in
+ * the buffer and calculates what it should be. If there's a
+ * mismatch it will log a warning and return an error. If
+ * tpm_buf_append_hmac_session() did not specify
+ * TPM_SA_CONTINUE_SESSION then the session will be closed (if it
+ * hasn't been consumed) and the auth structure freed.
+ */
+
+#include "tpm.h"
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/unaligned.h>
+#include <crypto/kpp.h>
+#include <crypto/ecdh.h>
+#include <crypto/sha2.h>
+#include <crypto/utils.h>
+
+/* maximum number of names the TPM must remember for authorization */
+#define AUTH_MAX_NAMES 3
+
+#define AES_KEY_BYTES AES_KEYSIZE_128
+#define AES_KEY_BITS (AES_KEY_BYTES*8)
+
+/*
+ * This is the structure that carries all the auth information (like
+ * session handle, nonces, session key and auth) from use to use it is
+ * designed to be opaque to anything outside.
+ */
+struct tpm2_auth {
+ u32 handle;
+ /*
+ * This has two meanings: before tpm_buf_fill_hmac_session()
+ * it marks the offset in the buffer of the start of the
+ * sessions (i.e. after all the handles). Once the buffer has
+ * been filled it markes the session number of our auth
+ * session so we can find it again in the response buffer.
+ *
+ * The two cases are distinguished because the first offset
+ * must always be greater than TPM_HEADER_SIZE and the second
+ * must be less than or equal to 5.
+ */
+ u32 session;
+ /*
+ * the size here is variable and set by the size of our_nonce
+ * which must be between 16 and the name hash length. we set
+ * the maximum sha256 size for the greatest protection
+ */
+ u8 our_nonce[SHA256_DIGEST_SIZE];
+ u8 tpm_nonce[SHA256_DIGEST_SIZE];
+ /*
+ * the salt is only used across the session command/response
+ * after that it can be used as a scratch area
+ */
+ union {
+ u8 salt[EC_PT_SZ];
+ /* scratch for key + IV */
+ u8 scratch[AES_KEY_BYTES + AES_BLOCK_SIZE];
+ };
+ /*
+ * the session key and passphrase are the same size as the
+ * name digest (sha256 again). The session key is constant
+ * for the use of the session and the passphrase can change
+ * with every invocation.
+ *
+ * Note: these fields must be adjacent and in this order
+ * because several HMAC/KDF schemes use the combination of the
+ * session_key and passphrase.
+ */
+ u8 session_key[SHA256_DIGEST_SIZE];
+ u8 passphrase[SHA256_DIGEST_SIZE];
+ int passphrase_len;
+ struct crypto_aes_ctx aes_ctx;
+ /* saved session attributes: */
+ u8 attrs;
+ __be32 ordinal;
+
+ /*
+ * memory for three authorization handles. We know them by
+ * handle, but they are part of the session by name, which
+ * we must compute and remember
+ */
+ u32 name_h[AUTH_MAX_NAMES];
+ u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+};
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+/*
+ * Name Size based on TPM algorithm (assumes no hash bigger than 255)
+ */
+static int name_size(const u8 *name)
+{
+ u16 hash_alg = get_unaligned_be16(name);
+
+ switch (hash_alg) {
+ case TPM_ALG_SHA1:
+ return SHA1_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA256:
+ return SHA256_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA384:
+ return SHA384_DIGEST_SIZE + 2;
+ case TPM_ALG_SHA512:
+ return SHA512_DIGEST_SIZE + 2;
+ default:
+ pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+ return -EINVAL;
+ }
+}
+
+static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+{
+ u32 mso = tpm2_handle_mso(handle);
+ off_t offset = TPM_HEADER_SIZE;
+ int rc, name_size_alg;
+ struct tpm_buf buf;
+
+ if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
+ mso != TPM2_MSO_NVRAM) {
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+ }
+
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+ if (rc)
+ return rc;
+
+ tpm_buf_append_u32(&buf, handle);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return tpm_ret_to_err(rc);
+ }
+
+ /* Skip TPMT_PUBLIC: */
+ offset += tpm_buf_read_u16(&buf, &offset);
+
+ /*
+ * Ensure space for the length field of TPM2B_NAME and hashAlg field of
+ * TPMT_HA (the extra four bytes).
+ */
+ if (offset + 4 > tpm_buf_length(&buf)) {
+ tpm_buf_destroy(&buf);
+ return -EIO;
+ }
+
+ rc = tpm_buf_read_u16(&buf, &offset);
+ name_size_alg = name_size(&buf.data[offset]);
+
+ if (name_size_alg < 0)
+ return name_size_alg;
+
+ if (rc != name_size_alg) {
+ tpm_buf_destroy(&buf);
+ return -EIO;
+ }
+
+ if (offset + rc > tpm_buf_length(&buf)) {
+ tpm_buf_destroy(&buf);
+ return -EIO;
+ }
+
+ memcpy(name, &buf.data[offset], rc);
+ return name_size_alg;
+}
+#endif /* CONFIG_TCG_TPM2_HMAC */
+
+/**
+ * tpm_buf_append_name() - add a handle area to the buffer
+ * @chip: the TPM chip structure
+ * @buf: The buffer to be appended
+ * @handle: The handle to be appended
+ * @name: The name of the handle (may be NULL)
+ *
+ * In order to compute session HMACs, we need to know the names of the
+ * objects pointed to by the handles. For most objects, this is simply
+ * the actual 4 byte handle or an empty buf (in these cases @name
+ * should be NULL) but for volatile objects, permanent objects and NV
+ * areas, the name is defined as the hash (according to the name
+ * algorithm which should be set to sha256) of the public area to
+ * which the two byte algorithm id has been appended. For these
+ * objects, the @name pointer should point to this. If a name is
+ * required but @name is NULL, then TPM2_ReadPublic() will be called
+ * on the handle to obtain the name.
+ *
+ * As with most tpm_buf operations, success is assumed because failure
+ * will be caused by an incorrect programming model and indicated by a
+ * kernel message.
+ *
+ * Ends the authorization session on failure.
+ */
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name)
+{
+#ifdef CONFIG_TCG_TPM2_HMAC
+ enum tpm2_mso_type mso = tpm2_handle_mso(handle);
+ struct tpm2_auth *auth;
+ u16 name_size_alg;
+ int slot;
+ int ret;
+#endif
+
+ if (!tpm2_chip_auth(chip)) {
+ tpm_buf_append_handle(chip, buf, handle);
+ return 0;
+ }
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+ slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
+ if (slot >= AUTH_MAX_NAMES) {
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
+ }
+ auth = chip->auth;
+ if (auth->session != tpm_buf_length(buf)) {
+ dev_err(&chip->dev, "session state malformed");
+ ret = -EIO;
+ goto err;
+ }
+ tpm_buf_append_u32(buf, handle);
+ auth->session += 4;
+
+ if (mso == TPM2_MSO_PERSISTENT ||
+ mso == TPM2_MSO_VOLATILE ||
+ mso == TPM2_MSO_NVRAM) {
+ if (!name) {
+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
+ if (ret < 0)
+ goto err;
+
+ name_size_alg = ret;
+ }
+ } else {
+ if (name) {
+ dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
+ handle);
+ ret = -EIO;
+ goto err;
+ }
+ }
+
+ auth->name_h[slot] = handle;
+ if (name)
+ memcpy(auth->name[slot], name, name_size_alg);
+#endif
+ return 0;
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+err:
+ tpm2_end_auth_session(chip);
+ return tpm_ret_to_err(ret);
+#endif
+}
+EXPORT_SYMBOL_GPL(tpm_buf_append_name);
+
+void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
+ u8 *passphrase, int passphrase_len)
+{
+ /* offset tells us where the sessions area begins */
+ int offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ u32 len = 9 + passphrase_len;
+
+ if (tpm_buf_length(buf) != offset) {
+ /* not the first session so update the existing length */
+ len += get_unaligned_be32(&buf->data[offset]);
+ put_unaligned_be32(len, &buf->data[offset]);
+ } else {
+ tpm_buf_append_u32(buf, len);
+ }
+ /* auth handle */
+ tpm_buf_append_u32(buf, TPM2_RS_PW);
+ /* nonce */
+ tpm_buf_append_u16(buf, 0);
+ /* attributes */
+ tpm_buf_append_u8(buf, 0);
+ /* passphrase */
+ tpm_buf_append_u16(buf, passphrase_len);
+ tpm_buf_append(buf, passphrase, passphrase_len);
+}
+
+/**
+ * tpm_buf_append_hmac_session() - Append a TPM session element
+ * @chip: the TPM chip structure
+ * @buf: The buffer to be appended
+ * @attributes: The session attributes
+ * @passphrase: The session authority (NULL if none)
+ * @passphrase_len: The length of the session authority (0 if none)
+ *
+ * This fills in a session structure in the TPM command buffer, except
+ * for the HMAC which cannot be computed until the command buffer is
+ * complete. The type of session is controlled by the @attributes,
+ * the main ones of which are TPM2_SA_CONTINUE_SESSION which means the
+ * session won't terminate after tpm_buf_check_hmac_response(),
+ * TPM2_SA_DECRYPT which means this buffers first parameter should be
+ * encrypted with a session key and TPM2_SA_ENCRYPT, which means the
+ * response buffer's first parameter needs to be decrypted (confusing,
+ * but the defines are written from the point of view of the TPM).
+ *
+ * Any session appended by this command must be finalized by calling
+ * tpm_buf_fill_hmac_session() otherwise the HMAC will be incorrect
+ * and the TPM will reject the command.
+ *
+ * As with most tpm_buf operations, success is assumed because failure
+ * will be caused by an incorrect programming model and indicated by a
+ * kernel message.
+ */
+void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
+ u8 attributes, u8 *passphrase,
+ int passphrase_len)
+{
+#ifdef CONFIG_TCG_TPM2_HMAC
+ u8 nonce[SHA256_DIGEST_SIZE];
+ struct tpm2_auth *auth;
+ u32 len;
+#endif
+
+ if (!tpm2_chip_auth(chip)) {
+ tpm_buf_append_auth(chip, buf, passphrase, passphrase_len);
+ return;
+ }
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+ /* The first write to /dev/tpm{rm0} will flush the session. */
+ attributes |= TPM2_SA_CONTINUE_SESSION;
+
+ /*
+ * The Architecture Guide requires us to strip trailing zeros
+ * before computing the HMAC
+ */
+ while (passphrase && passphrase_len > 0 && passphrase[passphrase_len - 1] == '\0')
+ passphrase_len--;
+
+ auth = chip->auth;
+ auth->attrs = attributes;
+ auth->passphrase_len = passphrase_len;
+ if (passphrase_len)
+ memcpy(auth->passphrase, passphrase, passphrase_len);
+
+ if (auth->session != tpm_buf_length(buf)) {
+ /* we're not the first session */
+ len = get_unaligned_be32(&buf->data[auth->session]);
+ if (4 + len + auth->session != tpm_buf_length(buf)) {
+ WARN(1, "session length mismatch, cannot append");
+ return;
+ }
+
+ /* add our new session */
+ len += 9 + 2 * SHA256_DIGEST_SIZE;
+ put_unaligned_be32(len, &buf->data[auth->session]);
+ } else {
+ tpm_buf_append_u32(buf, 9 + 2 * SHA256_DIGEST_SIZE);
+ }
+
+ /* random number for our nonce */
+ get_random_bytes(nonce, sizeof(nonce));
+ memcpy(auth->our_nonce, nonce, sizeof(nonce));
+ tpm_buf_append_u32(buf, auth->handle);
+ /* our new nonce */
+ tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
+ tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
+ tpm_buf_append_u8(buf, auth->attrs);
+ /* and put a placeholder for the hmac */
+ tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
+ tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
+#endif
+}
+EXPORT_SYMBOL_GPL(tpm_buf_append_hmac_session);
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+
+static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
+ u32 *handle, u8 *name);
+
+/*
+ * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but
+ * otherwise standard tpm2_KDFa. Note output is in bytes not bits.
+ */
+static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u,
+ u8 *v, u32 bytes, u8 *out)
+{
+ u32 counter = 1;
+ const __be32 bits = cpu_to_be32(bytes * 8);
+
+ while (bytes > 0) {
+ struct hmac_sha256_ctx hctx;
+ __be32 c = cpu_to_be32(counter);
+
+ hmac_sha256_init_usingrawkey(&hctx, key, key_len);
+ hmac_sha256_update(&hctx, (u8 *)&c, sizeof(c));
+ hmac_sha256_update(&hctx, label, strlen(label) + 1);
+ hmac_sha256_update(&hctx, u, SHA256_DIGEST_SIZE);
+ hmac_sha256_update(&hctx, v, SHA256_DIGEST_SIZE);
+ hmac_sha256_update(&hctx, (u8 *)&bits, sizeof(bits));
+ hmac_sha256_final(&hctx, out);
+
+ bytes -= SHA256_DIGEST_SIZE;
+ counter++;
+ out += SHA256_DIGEST_SIZE;
+ }
+}
+
+/*
+ * Somewhat of a bastardization of the real KDFe. We're assuming
+ * we're working with known point sizes for the input parameters and
+ * the hash algorithm is fixed at sha256. Because we know that the
+ * point size is 32 bytes like the hash size, there's no need to loop
+ * in this KDF.
+ */
+static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
+ u8 *out)
+{
+ struct sha256_ctx sctx;
+ /*
+ * this should be an iterative counter, but because we know
+ * we're only taking 32 bytes for the point using a sha256
+ * hash which is also 32 bytes, there's only one loop
+ */
+ __be32 c = cpu_to_be32(1);
+
+ sha256_init(&sctx);
+ /* counter (BE) */
+ sha256_update(&sctx, (u8 *)&c, sizeof(c));
+ /* secret value */
+ sha256_update(&sctx, z, EC_PT_SZ);
+ /* string including trailing zero */
+ sha256_update(&sctx, str, strlen(str)+1);
+ sha256_update(&sctx, pt_u, EC_PT_SZ);
+ sha256_update(&sctx, pt_v, EC_PT_SZ);
+ sha256_final(&sctx, out);
+}
+
+static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
+ struct tpm2_auth *auth)
+{
+ struct crypto_kpp *kpp;
+ struct kpp_request *req;
+ struct scatterlist s[2], d[1];
+ struct ecdh p = {0};
+ u8 encoded_key[EC_PT_SZ], *x, *y;
+ unsigned int buf_len;
+
+ /* secret is two sized points */
+ tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2);
+ /*
+ * we cheat here and append uninitialized data to form
+ * the points. All we care about is getting the two
+ * co-ordinate pointers, which will be used to overwrite
+ * the uninitialized data
+ */
+ tpm_buf_append_u16(buf, EC_PT_SZ);
+ x = &buf->data[tpm_buf_length(buf)];
+ tpm_buf_append(buf, encoded_key, EC_PT_SZ);
+ tpm_buf_append_u16(buf, EC_PT_SZ);
+ y = &buf->data[tpm_buf_length(buf)];
+ tpm_buf_append(buf, encoded_key, EC_PT_SZ);
+ sg_init_table(s, 2);
+ sg_set_buf(&s[0], x, EC_PT_SZ);
+ sg_set_buf(&s[1], y, EC_PT_SZ);
+
+ kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0);
+ if (IS_ERR(kpp)) {
+ dev_err(&chip->dev, "crypto ecdh allocation failed\n");
+ return;
+ }
+
+ buf_len = crypto_ecdh_key_len(&p);
+ if (sizeof(encoded_key) < buf_len) {
+ dev_err(&chip->dev, "salt buffer too small needs %d\n",
+ buf_len);
+ goto out;
+ }
+ crypto_ecdh_encode_key(encoded_key, buf_len, &p);
+ /* this generates a random private key */
+ crypto_kpp_set_secret(kpp, encoded_key, buf_len);
+
+ /* salt is now the public point of this private key */
+ req = kpp_request_alloc(kpp, GFP_KERNEL);
+ if (!req)
+ goto out;
+ kpp_request_set_input(req, NULL, 0);
+ kpp_request_set_output(req, s, EC_PT_SZ*2);
+ crypto_kpp_generate_public_key(req);
+ /*
+ * we're not done: now we have to compute the shared secret
+ * which is our private key multiplied by the tpm_key public
+ * point, we actually only take the x point and discard the y
+ * point and feed it through KDFe to get the final secret salt
+ */
+ sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ);
+ sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ);
+ kpp_request_set_input(req, s, EC_PT_SZ*2);
+ sg_init_one(d, auth->salt, EC_PT_SZ);
+ kpp_request_set_output(req, d, EC_PT_SZ);
+ crypto_kpp_compute_shared_secret(req);
+ kpp_request_free(req);
+
+ /*
+ * pass the shared secret through KDFe for salt. Note salt
+ * area is used both for input shared secret and output salt.
+ * This works because KDFe fully consumes the secret before it
+ * writes the salt
+ */
+ tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt);
+
+ out:
+ crypto_free_kpp(kpp);
+}
+
+/**
+ * tpm_buf_fill_hmac_session() - finalize the session HMAC
+ * @chip: the TPM chip structure
+ * @buf: The buffer to be appended
+ *
+ * This command must not be called until all of the parameters have
+ * been appended to @buf otherwise the computed HMAC will be
+ * incorrect.
+ *
+ * This function computes and fills in the session HMAC using the
+ * session key and, if TPM2_SA_DECRYPT was specified, computes the
+ * encryption key and encrypts the first parameter of the command
+ * buffer with it.
+ *
+ * Ends the authorization session on failure.
+ */
+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
+{
+ u32 cc, handles, val;
+ struct tpm2_auth *auth = chip->auth;
+ int i;
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+ off_t offset_s = TPM_HEADER_SIZE, offset_p;
+ u8 *hmac = NULL;
+ u32 attrs;
+ u8 cphash[SHA256_DIGEST_SIZE];
+ struct sha256_ctx sctx;
+ struct hmac_sha256_ctx hctx;
+ int ret;
+
+ if (!auth) {
+ ret = -EIO;
+ goto err;
+ }
+
+ /* save the command code in BE format */
+ auth->ordinal = head->ordinal;
+
+ cc = be32_to_cpu(head->ordinal);
+
+ i = tpm2_find_cc(chip, cc);
+ if (i < 0) {
+ dev_err(&chip->dev, "command 0x%08x not found\n", cc);
+ ret = -EIO;
+ goto err;
+ }
+
+ attrs = chip->cc_attrs_tbl[i];
+
+ handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+
+ /*
+ * just check the names, it's easy to make mistakes. This
+ * would happen if someone added a handle via
+ * tpm_buf_append_u32() instead of tpm_buf_append_name()
+ */
+ for (i = 0; i < handles; i++) {
+ u32 handle = tpm_buf_read_u32(buf, &offset_s);
+
+ if (auth->name_h[i] != handle) {
+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
+ ret = -EIO;
+ goto err;
+ }
+ }
+ /* point offset_s to the start of the sessions */
+ val = tpm_buf_read_u32(buf, &offset_s);
+ /* point offset_p to the start of the parameters */
+ offset_p = offset_s + val;
+ for (i = 1; offset_s < offset_p; i++) {
+ u32 handle = tpm_buf_read_u32(buf, &offset_s);
+ u16 len;
+ u8 a;
+
+ /* nonce (already in auth) */
+ len = tpm_buf_read_u16(buf, &offset_s);
+ offset_s += len;
+
+ a = tpm_buf_read_u8(buf, &offset_s);
+
+ len = tpm_buf_read_u16(buf, &offset_s);
+ if (handle == auth->handle && auth->attrs == a) {
+ hmac = &buf->data[offset_s];
+ /*
+ * save our session number so we know which
+ * session in the response belongs to us
+ */
+ auth->session = i;
+ }
+
+ offset_s += len;
+ }
+ if (offset_s != offset_p) {
+ dev_err(&chip->dev, "session length is incorrect\n");
+ ret = -EIO;
+ goto err;
+ }
+ if (!hmac) {
+ dev_err(&chip->dev, "could not find HMAC session\n");
+ ret = -EIO;
+ goto err;
+ }
+
+ /* encrypt before HMAC */
+ if (auth->attrs & TPM2_SA_DECRYPT) {
+ u16 len;
+
+ /* need key and IV */
+ tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE
+ + auth->passphrase_len, "CFB", auth->our_nonce,
+ auth->tpm_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE,
+ auth->scratch);
+
+ len = tpm_buf_read_u16(buf, &offset_p);
+ aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES);
+ aescfb_encrypt(&auth->aes_ctx, &buf->data[offset_p],
+ &buf->data[offset_p], len,
+ auth->scratch + AES_KEY_BYTES);
+ /* reset p to beginning of parameters for HMAC */
+ offset_p -= 2;
+ }
+
+ sha256_init(&sctx);
+ /* ordinal is already BE */
+ sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
+ /* add the handle names */
+ for (i = 0; i < handles; i++) {
+ enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
+
+ if (mso == TPM2_MSO_PERSISTENT ||
+ mso == TPM2_MSO_VOLATILE ||
+ mso == TPM2_MSO_NVRAM) {
+ ret = name_size(auth->name[i]);
+ if (ret < 0)
+ goto err;
+
+ sha256_update(&sctx, auth->name[i], ret);
+ } else {
+ __be32 h = cpu_to_be32(auth->name_h[i]);
+
+ sha256_update(&sctx, (u8 *)&h, 4);
+ }
+ }
+ if (offset_s != tpm_buf_length(buf))
+ sha256_update(&sctx, &buf->data[offset_s],
+ tpm_buf_length(buf) - offset_s);
+ sha256_final(&sctx, cphash);
+
+ /* now calculate the hmac */
+ hmac_sha256_init_usingrawkey(&hctx, auth->session_key,
+ sizeof(auth->session_key) +
+ auth->passphrase_len);
+ hmac_sha256_update(&hctx, cphash, sizeof(cphash));
+ hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce));
+ hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
+ hmac_sha256_update(&hctx, &auth->attrs, 1);
+ hmac_sha256_final(&hctx, hmac);
+ return 0;
+
+err:
+ tpm2_end_auth_session(chip);
+ return ret;
+}
+EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
+
+/**
+ * tpm_buf_check_hmac_response() - check the TPM return HMAC for correctness
+ * @chip: the TPM chip structure
+ * @buf: the original command buffer (which now contains the response)
+ * @rc: the return code from tpm_transmit_cmd
+ *
+ * If @rc is non zero, @buf may not contain an actual return, so @rc
+ * is passed through as the return and the session cleaned up and
+ * de-allocated if required (this is required if
+ * TPM2_SA_CONTINUE_SESSION was not specified as a session flag).
+ *
+ * If @rc is zero, the response HMAC is computed against the returned
+ * @buf and matched to the TPM one in the session area. If there is a
+ * mismatch, an error is logged and -EINVAL returned.
+ *
+ * The reason for this is that the command issue and HMAC check
+ * sequence should look like:
+ *
+ * rc = tpm_transmit_cmd(...);
+ * rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+ * if (rc)
+ * ...
+ *
+ * Which is easily layered into the current contrl flow.
+ *
+ * Returns: 0 on success or an error.
+ */
+int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
+ int rc)
+{
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+ struct tpm2_auth *auth = chip->auth;
+ off_t offset_s, offset_p;
+ u8 rphash[SHA256_DIGEST_SIZE];
+ u32 attrs, cc;
+ struct sha256_ctx sctx;
+ struct hmac_sha256_ctx hctx;
+ u16 tag = be16_to_cpu(head->tag);
+ int parm_len, len, i, handles;
+
+ if (!auth)
+ return rc;
+
+ cc = be32_to_cpu(auth->ordinal);
+
+ if (auth->session >= TPM_HEADER_SIZE) {
+ WARN(1, "tpm session not filled correctly\n");
+ goto out;
+ }
+
+ if (rc != 0)
+ /* pass non success rc through and close the session */
+ goto out;
+
+ rc = -EINVAL;
+ if (tag != TPM2_ST_SESSIONS) {
+ dev_err(&chip->dev, "TPM: HMAC response check has no sessions tag\n");
+ goto out;
+ }
+
+ i = tpm2_find_cc(chip, cc);
+ if (i < 0)
+ goto out;
+ attrs = chip->cc_attrs_tbl[i];
+ handles = (attrs >> TPM2_CC_ATTR_RHANDLE) & 1;
+
+ /* point to area beyond handles */
+ offset_s = TPM_HEADER_SIZE + handles * 4;
+ parm_len = tpm_buf_read_u32(buf, &offset_s);
+ offset_p = offset_s;
+ offset_s += parm_len;
+ /* skip over any sessions before ours */
+ for (i = 0; i < auth->session - 1; i++) {
+ len = tpm_buf_read_u16(buf, &offset_s);
+ offset_s += len + 1;
+ len = tpm_buf_read_u16(buf, &offset_s);
+ offset_s += len;
+ }
+ /* TPM nonce */
+ len = tpm_buf_read_u16(buf, &offset_s);
+ if (offset_s + len > tpm_buf_length(buf))
+ goto out;
+ if (len != SHA256_DIGEST_SIZE)
+ goto out;
+ memcpy(auth->tpm_nonce, &buf->data[offset_s], len);
+ offset_s += len;
+ attrs = tpm_buf_read_u8(buf, &offset_s);
+ len = tpm_buf_read_u16(buf, &offset_s);
+ if (offset_s + len != tpm_buf_length(buf))
+ goto out;
+ if (len != SHA256_DIGEST_SIZE)
+ goto out;
+ /*
+ * offset_s points to the HMAC. now calculate comparison, beginning
+ * with rphash
+ */
+ sha256_init(&sctx);
+ /* yes, I know this is now zero, but it's what the standard says */
+ sha256_update(&sctx, (u8 *)&head->return_code,
+ sizeof(head->return_code));
+ /* ordinal is already BE */
+ sha256_update(&sctx, (u8 *)&auth->ordinal, sizeof(auth->ordinal));
+ sha256_update(&sctx, &buf->data[offset_p], parm_len);
+ sha256_final(&sctx, rphash);
+
+ /* now calculate the hmac */
+ hmac_sha256_init_usingrawkey(&hctx, auth->session_key,
+ sizeof(auth->session_key) +
+ auth->passphrase_len);
+ hmac_sha256_update(&hctx, rphash, sizeof(rphash));
+ hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
+ hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce));
+ hmac_sha256_update(&hctx, &auth->attrs, 1);
+ /* we're done with the rphash, so put our idea of the hmac there */
+ hmac_sha256_final(&hctx, rphash);
+ if (crypto_memneq(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE)) {
+ dev_err(&chip->dev, "TPM: HMAC check failed\n");
+ goto out;
+ }
+ rc = 0;
+
+ /* now do response decryption */
+ if (auth->attrs & TPM2_SA_ENCRYPT) {
+ /* need key and IV */
+ tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE
+ + auth->passphrase_len, "CFB", auth->tpm_nonce,
+ auth->our_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE,
+ auth->scratch);
+
+ len = tpm_buf_read_u16(buf, &offset_p);
+ aes_expandkey(&auth->aes_ctx, auth->scratch, AES_KEY_BYTES);
+ aescfb_decrypt(&auth->aes_ctx, &buf->data[offset_p],
+ &buf->data[offset_p], len,
+ auth->scratch + AES_KEY_BYTES);
+ }
+
+ out:
+ if ((auth->attrs & TPM2_SA_CONTINUE_SESSION) == 0) {
+ if (rc)
+ /* manually close the session if it wasn't consumed */
+ tpm2_flush_context(chip, auth->handle);
+
+ kfree_sensitive(auth);
+ chip->auth = NULL;
+ } else {
+ /* reset for next use */
+ auth->session = TPM_HEADER_SIZE;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(tpm_buf_check_hmac_response);
+
+/**
+ * tpm2_end_auth_session() - kill the allocated auth session
+ * @chip: the TPM chip structure
+ *
+ * ends the session started by tpm2_start_auth_session and frees all
+ * the resources. Under normal conditions,
+ * tpm_buf_check_hmac_response() will correctly end the session if
+ * required, so this function is only for use in error legs that will
+ * bypass the normal invocation of tpm_buf_check_hmac_response().
+ */
+void tpm2_end_auth_session(struct tpm_chip *chip)
+{
+ struct tpm2_auth *auth = chip->auth;
+
+ if (!auth)
+ return;
+
+ tpm2_flush_context(chip, auth->handle);
+ kfree_sensitive(auth);
+ chip->auth = NULL;
+}
+EXPORT_SYMBOL(tpm2_end_auth_session);
+
+static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
+ struct tpm_buf *buf)
+{
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+ u32 tot_len = be32_to_cpu(head->length);
+ off_t offset = TPM_HEADER_SIZE;
+ u32 val;
+
+ /* we're starting after the header so adjust the length */
+ tot_len -= TPM_HEADER_SIZE;
+
+ /* should have handle plus nonce */
+ if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce))
+ return -EINVAL;
+
+ auth->handle = tpm_buf_read_u32(buf, &offset);
+ val = tpm_buf_read_u16(buf, &offset);
+ if (val != sizeof(auth->tpm_nonce))
+ return -EINVAL;
+ memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce));
+ /* now compute the session key from the nonces */
+ tpm2_KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce,
+ auth->our_nonce, sizeof(auth->session_key),
+ auth->session_key);
+
+ return 0;
+}
+
+static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
+{
+ unsigned int offset = 0; /* dummy offset for null seed context */
+ u8 name[SHA256_DIGEST_SIZE + 2];
+ u32 tmp_null_key;
+ int rc;
+
+ rc = tpm2_load_context(chip, chip->null_key_context, &offset,
+ &tmp_null_key);
+ if (rc != -EINVAL) {
+ if (!rc)
+ *null_key = tmp_null_key;
+ goto err;
+ }
+
+ /* Try to re-create null key, given the integrity failure: */
+ rc = tpm2_create_primary(chip, TPM2_RH_NULL, &tmp_null_key, name);
+ if (rc)
+ goto err;
+
+ /* Return null key if the name has not been changed: */
+ if (!memcmp(name, chip->null_key_name, sizeof(name))) {
+ *null_key = tmp_null_key;
+ return 0;
+ }
+
+ /* Deduce from the name change TPM interference: */
+ dev_err(&chip->dev, "null key integrity check failed\n");
+ tpm2_flush_context(chip, tmp_null_key);
+
+err:
+ if (rc) {
+ chip->flags |= TPM_CHIP_FLAG_DISABLE;
+ rc = -ENODEV;
+ }
+ return rc;
+}
+
+/**
+ * tpm2_start_auth_session() - Create an a HMAC authentication session
+ * @chip: A TPM chip
+ *
+ * Loads the ephemeral key (null seed), and starts an HMAC authenticated
+ * session. The null seed is flushed before the return.
+ *
+ * Returns zero on success, or a POSIX error code.
+ */
+int tpm2_start_auth_session(struct tpm_chip *chip)
+{
+ struct tpm2_auth *auth;
+ struct tpm_buf buf;
+ u32 null_key;
+ int rc;
+
+ if (chip->auth) {
+ dev_dbg_once(&chip->dev, "auth session is active\n");
+ return 0;
+ }
+
+ auth = kzalloc(sizeof(*auth), GFP_KERNEL);
+ if (!auth)
+ return -ENOMEM;
+
+ rc = tpm2_load_null(chip, &null_key);
+ if (rc)
+ goto out;
+
+ auth->session = TPM_HEADER_SIZE;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
+ if (rc)
+ goto out;
+
+ /* salt key handle */
+ tpm_buf_append_u32(&buf, null_key);
+ /* bind key handle */
+ tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+ /* nonce caller */
+ get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
+ tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
+ tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
+
+ /* append encrypted salt and squirrel away unencrypted in auth */
+ tpm_buf_append_salt(&buf, chip, auth);
+ /* session type (HMAC, audit or policy) */
+ tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
+
+ /* symmetric encryption parameters */
+ /* symmetric algorithm */
+ tpm_buf_append_u16(&buf, TPM_ALG_AES);
+ /* bits for symmetric algorithm */
+ tpm_buf_append_u16(&buf, AES_KEY_BITS);
+ /* symmetric algorithm mode (must be CFB) */
+ tpm_buf_append_u16(&buf, TPM_ALG_CFB);
+ /* hash algorithm for session */
+ tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+
+ rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
+ tpm2_flush_context(chip, null_key);
+
+ if (rc == TPM2_RC_SUCCESS)
+ rc = tpm2_parse_start_auth_session(auth, &buf);
+
+ tpm_buf_destroy(&buf);
+
+ if (rc == TPM2_RC_SUCCESS) {
+ chip->auth = auth;
+ return 0;
+ }
+
+out:
+ kfree_sensitive(auth);
+ return rc;
+}
+EXPORT_SYMBOL(tpm2_start_auth_session);
+
+/*
+ * A mask containing the object attributes for the kernel held null primary key
+ * used in HMAC encryption. For more information on specific attributes look up
+ * to "8.3 TPMA_OBJECT (Object Attributes)".
+ */
+#define TPM2_OA_NULL_KEY ( \
+ TPM2_OA_NO_DA | \
+ TPM2_OA_FIXED_TPM | \
+ TPM2_OA_FIXED_PARENT | \
+ TPM2_OA_SENSITIVE_DATA_ORIGIN | \
+ TPM2_OA_USER_WITH_AUTH | \
+ TPM2_OA_DECRYPT | \
+ TPM2_OA_RESTRICTED)
+
+/**
+ * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY
+ *
+ * @chip: The TPM the primary was created under
+ * @buf: The response buffer from the chip
+ * @handle: pointer to be filled in with the return handle of the primary
+ * @hierarchy: The hierarchy the primary was created for
+ * @name: pointer to be filled in with the primary key name
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 *handle, u32 hierarchy, u8 *name)
+{
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+ off_t offset_r = TPM_HEADER_SIZE, offset_t;
+ u16 len = TPM_HEADER_SIZE;
+ u32 total_len = be32_to_cpu(head->length);
+ u32 val, param_len, keyhandle;
+
+ keyhandle = tpm_buf_read_u32(buf, &offset_r);
+ if (handle)
+ *handle = keyhandle;
+ else
+ tpm2_flush_context(chip, keyhandle);
+
+ param_len = tpm_buf_read_u32(buf, &offset_r);
+ /*
+ * param_len doesn't include the header, but all the other
+ * lengths and offsets do, so add it to parm len to make
+ * the comparisons easier
+ */
+ param_len += TPM_HEADER_SIZE;
+
+ if (param_len + 8 > total_len)
+ return -EINVAL;
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_t = offset_r;
+ if (name) {
+ /*
+ * now we have the public area, compute the name of
+ * the object
+ */
+ put_unaligned_be16(TPM_ALG_SHA256, name);
+ sha256(&buf->data[offset_r], len, name + 2);
+ }
+
+ /* validate the public key */
+ val = tpm_buf_read_u16(buf, &offset_t);
+
+ /* key type (must be what we asked for) */
+ if (val != TPM_ALG_ECC)
+ return -EINVAL;
+ val = tpm_buf_read_u16(buf, &offset_t);
+
+ /* name algorithm */
+ if (val != TPM_ALG_SHA256)
+ return -EINVAL;
+ val = tpm_buf_read_u32(buf, &offset_t);
+
+ /* object properties */
+ if (val != TPM2_OA_NULL_KEY)
+ return -EINVAL;
+
+ /* auth policy (empty) */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != 0)
+ return -EINVAL;
+
+ /* symmetric key parameters */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_AES)
+ return -EINVAL;
+
+ /* symmetric key length */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != AES_KEY_BITS)
+ return -EINVAL;
+
+ /* symmetric encryption scheme */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_CFB)
+ return -EINVAL;
+
+ /* signing scheme */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_NULL)
+ return -EINVAL;
+
+ /* ECC Curve */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM2_ECC_NIST_P256)
+ return -EINVAL;
+
+ /* KDF Scheme */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_NULL)
+ return -EINVAL;
+
+ /* extract public key (x and y points) */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != EC_PT_SZ)
+ return -EINVAL;
+ memcpy(chip->null_ec_key_x, &buf->data[offset_t], val);
+ offset_t += val;
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != EC_PT_SZ)
+ return -EINVAL;
+ memcpy(chip->null_ec_key_y, &buf->data[offset_t], val);
+ offset_t += val;
+
+ /* original length of the whole TPM2B */
+ offset_r += len;
+
+ /* should have exactly consumed the TPM2B public structure */
+ if (offset_t != offset_r)
+ return -EINVAL;
+ if (offset_r > param_len)
+ return -EINVAL;
+
+ /* creation data (skip) */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_r += len;
+ if (offset_r > param_len)
+ return -EINVAL;
+
+ /* creation digest (must be sha256) */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_r += len;
+ if (len != SHA256_DIGEST_SIZE || offset_r > param_len)
+ return -EINVAL;
+
+ /* TPMT_TK_CREATION follows */
+ /* tag, must be TPM_ST_CREATION (0x8021) */
+ val = tpm_buf_read_u16(buf, &offset_r);
+ if (val != TPM2_ST_CREATION || offset_r > param_len)
+ return -EINVAL;
+
+ /* hierarchy */
+ val = tpm_buf_read_u32(buf, &offset_r);
+ if (val != hierarchy || offset_r > param_len)
+ return -EINVAL;
+
+ /* the ticket digest HMAC (might not be sha256) */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_r += len;
+ if (offset_r > param_len)
+ return -EINVAL;
+
+ /*
+ * finally we have the name, which is a sha256 digest plus a 2
+ * byte algorithm type
+ */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ if (offset_r + len != param_len + 8)
+ return -EINVAL;
+ if (len != SHA256_DIGEST_SIZE + 2)
+ return -EINVAL;
+
+ if (memcmp(chip->null_key_name, &buf->data[offset_r],
+ SHA256_DIGEST_SIZE + 2) != 0) {
+ dev_err(&chip->dev, "NULL Seed name comparison failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * tpm2_create_primary() - create a primary key using a fixed P-256 template
+ *
+ * @chip: the TPM chip to create under
+ * @hierarchy: The hierarchy handle to create under
+ * @handle: The returned volatile handle on success
+ * @name: The name of the returned key
+ *
+ * For platforms that might not have a persistent primary, this can be
+ * used to create one quickly on the fly (it uses Elliptic Curve not
+ * RSA, so even slow TPMs can create one fast). The template uses the
+ * TCG mandated H one for non-endorsement ECC primaries, i.e. P-256
+ * elliptic curve (the only current one all TPM2s are required to
+ * have) a sha256 name hash and no policy.
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
+ u32 *handle, u8 *name)
+{
+ int rc;
+ struct tpm_buf buf;
+ struct tpm_buf template;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
+ if (rc)
+ return rc;
+
+ rc = tpm_buf_init_sized(&template);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
+
+ /*
+ * create the template. Note: in order for userspace to
+ * verify the security of the system, it will have to create
+ * and certify this NULL primary, meaning all the template
+ * parameters will have to be identical, so conform exactly to
+ * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC
+ * key H template (H has zero size unique points)
+ */
+
+ /* key type */
+ tpm_buf_append_u16(&template, TPM_ALG_ECC);
+
+ /* name algorithm */
+ tpm_buf_append_u16(&template, TPM_ALG_SHA256);
+
+ /* object properties */
+ tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
+
+ /* sauth policy (empty) */
+ tpm_buf_append_u16(&template, 0);
+
+ /* BEGIN parameters: key specific; for ECC*/
+
+ /* symmetric algorithm */
+ tpm_buf_append_u16(&template, TPM_ALG_AES);
+
+ /* bits for symmetric algorithm */
+ tpm_buf_append_u16(&template, AES_KEY_BITS);
+
+ /* algorithm mode (must be CFB) */
+ tpm_buf_append_u16(&template, TPM_ALG_CFB);
+
+ /* scheme (NULL means any scheme) */
+ tpm_buf_append_u16(&template, TPM_ALG_NULL);
+
+ /* ECC Curve ID */
+ tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
+
+ /* KDF Scheme */
+ tpm_buf_append_u16(&template, TPM_ALG_NULL);
+
+ /* unique: key specific; for ECC it is two zero size points */
+ tpm_buf_append_u16(&template, 0);
+ tpm_buf_append_u16(&template, 0);
+
+ /* END parameters */
+
+ /* primary handle */
+ tpm_buf_append_u32(&buf, hierarchy);
+ tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
+
+ /* sensitive create size is 4 for two empty buffers */
+ tpm_buf_append_u16(&buf, 4);
+
+ /* sensitive create auth data (empty) */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* sensitive create sensitive data (empty) */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* the public template */
+ tpm_buf_append(&buf, template.data, template.length);
+ tpm_buf_destroy(&template);
+
+ /* outside info (empty) */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* creation PCR (none) */
+ tpm_buf_append_u32(&buf, 0);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0,
+ "attempting to create NULL primary");
+
+ if (rc == TPM2_RC_SUCCESS)
+ rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
+ name);
+
+ tpm_buf_destroy(&buf);
+
+ return rc;
+}
+
+static int tpm2_create_null_primary(struct tpm_chip *chip)
+{
+ u32 null_key;
+ int rc;
+
+ rc = tpm2_create_primary(chip, TPM2_RH_NULL, &null_key,
+ chip->null_key_name);
+
+ if (rc == TPM2_RC_SUCCESS) {
+ unsigned int offset = 0; /* dummy offset for null key context */
+
+ rc = tpm2_save_context(chip, null_key, chip->null_key_context,
+ sizeof(chip->null_key_context), &offset);
+ tpm2_flush_context(chip, null_key);
+ }
+
+ return rc;
+}
+
+/**
+ * tpm2_sessions_init() - start of day initialization for the sessions code
+ * @chip: TPM chip
+ *
+ * Derive and context save the null primary and allocate memory in the
+ * struct tpm_chip for the authorizations.
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+int tpm2_sessions_init(struct tpm_chip *chip)
+{
+ int rc;
+
+ rc = tpm2_create_null_primary(chip);
+ if (rc) {
+ dev_err(&chip->dev, "null key creation failed with %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+#endif /* CONFIG_TCG_TPM2_HMAC */
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index ffb35f0154c1..60354cd53b5c 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -12,7 +12,7 @@
*/
#include <linux/gfp.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "tpm.h"
enum tpm2_handle_types {
@@ -68,8 +68,8 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
kfree(space->session_buf);
}
-static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
- unsigned int *offset, u32 *handle)
+int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
+ unsigned int *offset, u32 *handle)
{
struct tpm_buf tbuf;
struct tpm2_context *ctx;
@@ -105,6 +105,9 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
*handle = 0;
tpm_buf_destroy(&tbuf);
return -ENOENT;
+ } else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
+ tpm_buf_destroy(&tbuf);
+ return -EINVAL;
} else if (rc > 0) {
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
__func__, rc);
@@ -119,8 +122,8 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
return 0;
}
-static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
- unsigned int buf_size, unsigned int *offset)
+int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
+ unsigned int buf_size, unsigned int *offset)
{
struct tpm_buf tbuf;
unsigned int body_size;
@@ -166,6 +169,9 @@ void tpm2_flush_space(struct tpm_chip *chip)
struct tpm_space *space = &chip->work_space;
int i;
+ if (!space)
+ return;
+
for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
if (space->context_tbl[i] && ~space->context_tbl[i])
tpm2_flush_context(chip, space->context_tbl[i]);
@@ -606,7 +612,7 @@ int tpm_devs_add(struct tpm_chip *chip)
device_initialize(&chip->devs);
chip->devs.parent = chip->dev.parent;
- chip->devs.class = tpmrm_class;
+ chip->devs.class = &tpmrm_class;
/*
* Get extra reference on main device to hold on behalf of devs.
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index 54a6750a6757..f25faf468bba 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -15,7 +15,66 @@
*/
#include "tpm.h"
-#include "tpm_atmel.h"
+
+struct tpm_atmel_priv {
+ int region_size;
+ int have_region;
+ unsigned long base;
+ void __iomem *iobase;
+};
+
+#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + (offset))
+#define atmel_putb(val, chip, offset) \
+ outb(val, atmel_get_priv(chip)->base + (offset))
+#define atmel_request_region request_region
+#define atmel_release_region release_region
+/* Atmel definitions */
+enum tpm_atmel_addr {
+ TPM_ATMEL_BASE_ADDR_LO = 0x08,
+ TPM_ATMEL_BASE_ADDR_HI = 0x09
+};
+
+static inline int tpm_read_index(int base, int index)
+{
+ outb(index, base);
+ return inb(base + 1) & 0xFF;
+}
+
+/* Verify this is a 1.1 Atmel TPM */
+static int atmel_verify_tpm11(void)
+{
+ /* verify that it is an Atmel part */
+ if (tpm_read_index(TPM_ADDR, 4) != 'A' ||
+ tpm_read_index(TPM_ADDR, 5) != 'T' ||
+ tpm_read_index(TPM_ADDR, 6) != 'M' ||
+ tpm_read_index(TPM_ADDR, 7) != 'L')
+ return 1;
+
+ /* query chip for its version number */
+ if (tpm_read_index(TPM_ADDR, 0x00) != 1 ||
+ tpm_read_index(TPM_ADDR, 0x01) != 1)
+ return 1;
+
+ /* This is an atmel supported part */
+ return 0;
+}
+
+/* Determine where to talk to device */
+static void __iomem *atmel_get_base_addr(unsigned long *base, int *region_size)
+{
+ int lo, hi;
+
+ if (atmel_verify_tpm11() != 0)
+ return NULL;
+
+ lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO);
+ hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI);
+
+ *base = (hi << 8) | lo;
+ *region_size = 2;
+
+ return ioport_map(*base, *region_size);
+}
/* write status bits */
enum tpm_atmel_write_status {
@@ -89,7 +148,8 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return size;
}
-static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t count)
{
struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
int i;
@@ -142,7 +202,6 @@ static void atml_plat_remove(void)
tpm_chip_unregister(chip);
if (priv->have_region)
atmel_release_region(priv->base, priv->region_size);
- atmel_put_base_addr(priv->iobase);
platform_device_unregister(pdev);
}
@@ -211,7 +270,6 @@ static int __init init_atmel(void)
err_unreg_dev:
platform_device_unregister(pdev);
err_rel_reg:
- atmel_put_base_addr(iobase);
if (have_region)
atmel_release_region(base,
region_size);
@@ -229,7 +287,7 @@ static void __exit cleanup_atmel(void)
module_init(init_atmel);
module_exit(cleanup_atmel);
-MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_AUTHOR("Leendert van Doorn <leendert@watson.ibm.com>");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h
deleted file mode 100644
index ba37e77e8af3..000000000000
--- a/drivers/char/tpm/tpm_atmel.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2005 IBM Corporation
- *
- * Authors:
- * Kylene Hall <kjhall@us.ibm.com>
- *
- * Maintained by: <tpmdd-devel@lists.sourceforge.net>
- *
- * Device driver for TCG/TCPA TPM (trusted platform module).
- * Specifications at www.trustedcomputinggroup.org
- *
- * These difference are required on power because the device must be
- * discovered through the device tree and iomap must be used to get
- * around the need for holes in the io_page_mask. This does not happen
- * automatically because the tpm is not a normal pci device and lives
- * under the root node.
- */
-
-struct tpm_atmel_priv {
- int region_size;
- int have_region;
- unsigned long base;
- void __iomem *iobase;
-};
-
-#ifdef CONFIG_PPC64
-
-#include <asm/prom.h>
-
-#define atmel_getb(priv, offset) readb(priv->iobase + offset)
-#define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset)
-#define atmel_request_region request_mem_region
-#define atmel_release_region release_mem_region
-
-static inline void atmel_put_base_addr(void __iomem *iobase)
-{
- iounmap(iobase);
-}
-
-static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)
-{
- struct device_node *dn;
- unsigned long address, size;
- const unsigned int *reg;
- int reglen;
- int naddrc;
- int nsizec;
-
- dn = of_find_node_by_name(NULL, "tpm");
-
- if (!dn)
- return NULL;
-
- if (!of_device_is_compatible(dn, "AT97SC3201")) {
- of_node_put(dn);
- return NULL;
- }
-
- reg = of_get_property(dn, "reg", &reglen);
- naddrc = of_n_addr_cells(dn);
- nsizec = of_n_size_cells(dn);
-
- of_node_put(dn);
-
-
- if (naddrc == 2)
- address = ((unsigned long) reg[0] << 32) | reg[1];
- else
- address = reg[0];
-
- if (nsizec == 2)
- size =
- ((unsigned long) reg[naddrc] << 32) | reg[naddrc + 1];
- else
- size = reg[naddrc];
-
- *base = address;
- *region_size = size;
- return ioremap(*base, *region_size);
-}
-#else
-#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset)
-#define atmel_putb(val, chip, offset) \
- outb(val, atmel_get_priv(chip)->base + offset)
-#define atmel_request_region request_region
-#define atmel_release_region release_region
-/* Atmel definitions */
-enum tpm_atmel_addr {
- TPM_ATMEL_BASE_ADDR_LO = 0x08,
- TPM_ATMEL_BASE_ADDR_HI = 0x09
-};
-
-static inline int tpm_read_index(int base, int index)
-{
- outb(index, base);
- return inb(base+1) & 0xFF;
-}
-
-/* Verify this is a 1.1 Atmel TPM */
-static int atmel_verify_tpm11(void)
-{
-
- /* verify that it is an Atmel part */
- if (tpm_read_index(TPM_ADDR, 4) != 'A' ||
- tpm_read_index(TPM_ADDR, 5) != 'T' ||
- tpm_read_index(TPM_ADDR, 6) != 'M' ||
- tpm_read_index(TPM_ADDR, 7) != 'L')
- return 1;
-
- /* query chip for its version number */
- if (tpm_read_index(TPM_ADDR, 0x00) != 1 ||
- tpm_read_index(TPM_ADDR, 0x01) != 1)
- return 1;
-
- /* This is an atmel supported part */
- return 0;
-}
-
-static inline void atmel_put_base_addr(void __iomem *iobase)
-{
-}
-
-/* Determine where to talk to device */
-static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)
-{
- int lo, hi;
-
- if (atmel_verify_tpm11() != 0)
- return NULL;
-
- lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO);
- hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI);
-
- *base = (hi << 8) | lo;
- *region_size = 2;
-
- return ioport_map(*base, *region_size);
-}
-#endif
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 18606651d1aa..6c25305c256e 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -19,6 +19,7 @@
#ifdef CONFIG_ARM64
#include <linux/arm-smccc.h>
#endif
+#include "tpm_crb_ffa.h"
#include "tpm.h"
#define ACPI_SIG_TPM2 "TPM2"
@@ -98,6 +99,10 @@ struct crb_priv {
u8 __iomem *rsp;
u32 cmd_size;
u32 smc_func_id;
+ u32 __iomem *pluton_start_addr;
+ u32 __iomem *pluton_reply_addr;
+ u8 ffa_flags;
+ u8 ffa_attributes;
};
struct tpm2_crb_smc {
@@ -108,6 +113,29 @@ struct tpm2_crb_smc {
u32 smc_func_id;
};
+/* CRB over FFA start method parameters in TCG2 ACPI table */
+struct tpm2_crb_ffa {
+ u8 flags;
+ u8 attributes;
+ u16 partition_id;
+ u8 reserved[8];
+};
+
+struct tpm2_crb_pluton {
+ u64 start_addr;
+ u64 reply_addr;
+};
+
+/*
+ * Returns true if the start method supports idle.
+ */
+static inline bool tpm_crb_has_idle(u32 start_method)
+{
+ return !(start_method == ACPI_TPM2_START_METHOD ||
+ start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD ||
+ start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC);
+}
+
static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
unsigned long timeout)
{
@@ -127,11 +155,31 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
return ((ioread32(reg) & mask) == value);
}
+static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete)
+{
+ if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON)
+ return 0;
+
+ if (!crb_wait_for_reg_32(priv->pluton_reply_addr, ~0, 1, TPM2_TIMEOUT_C))
+ return -ETIME;
+
+ iowrite32(1, priv->pluton_start_addr);
+ if (wait_for_complete == false)
+ return 0;
+
+ if (!crb_wait_for_reg_32(priv->pluton_start_addr,
+ 0xffffffff, 0, 200))
+ return -ETIME;
+
+ return 0;
+}
+
/**
* __crb_go_idle - request tpm crb device to go the idle state
*
* @dev: crb device
* @priv: crb private data
+ * @loc: locality
*
* Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ
* The device should respond within TIMEOUT_C by clearing the bit.
@@ -143,15 +191,25 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
*
* Return: 0 always
*/
-static int __crb_go_idle(struct device *dev, struct crb_priv *priv)
+static int __crb_go_idle(struct device *dev, struct crb_priv *priv, int loc)
{
- if ((priv->sm == ACPI_TPM2_START_METHOD) ||
- (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
- (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC))
+ int rc;
+
+ if (!tpm_crb_has_idle(priv->sm))
return 0;
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, loc);
+ if (rc)
+ return rc;
+ }
+
+ rc = crb_try_pluton_doorbell(priv, true);
+ if (rc)
+ return rc;
+
if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
CRB_CTRL_REQ_GO_IDLE/* mask */,
0, /* value */
@@ -168,7 +226,7 @@ static int crb_go_idle(struct tpm_chip *chip)
struct device *dev = &chip->dev;
struct crb_priv *priv = dev_get_drvdata(dev);
- return __crb_go_idle(dev, priv);
+ return __crb_go_idle(dev, priv, chip->locality);
}
/**
@@ -176,6 +234,7 @@ static int crb_go_idle(struct tpm_chip *chip)
*
* @dev: crb device
* @priv: crb private data
+ * @loc: locality
*
* Write CRB_CTRL_REQ_CMD_READY to TPM_CRB_CTRL_REQ
* and poll till the device acknowledge it by clearing the bit.
@@ -186,14 +245,25 @@ static int crb_go_idle(struct tpm_chip *chip)
*
* Return: 0 on success -ETIME on timeout;
*/
-static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv)
+static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv, int loc)
{
- if ((priv->sm == ACPI_TPM2_START_METHOD) ||
- (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
- (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC))
+ int rc;
+
+ if (!tpm_crb_has_idle(priv->sm))
return 0;
iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req);
+
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, loc);
+ if (rc)
+ return rc;
+ }
+
+ rc = crb_try_pluton_doorbell(priv, true);
+ if (rc)
+ return rc;
+
if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
CRB_CTRL_REQ_CMD_READY /* mask */,
0, /* value */
@@ -210,19 +280,26 @@ static int crb_cmd_ready(struct tpm_chip *chip)
struct device *dev = &chip->dev;
struct crb_priv *priv = dev_get_drvdata(dev);
- return __crb_cmd_ready(dev, priv);
+ return __crb_cmd_ready(dev, priv, chip->locality);
}
static int __crb_request_locality(struct device *dev,
struct crb_priv *priv, int loc)
{
- u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
- CRB_LOC_STATE_TPM_REG_VALID_STS;
+ u32 value = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS;
+ int rc;
if (!priv->regs_h)
return 0;
iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
+
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc);
+ if (rc)
+ return rc;
+ }
+
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
TPM2_TIMEOUT_C)) {
dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
@@ -242,17 +319,24 @@ static int crb_request_locality(struct tpm_chip *chip, int loc)
static int __crb_relinquish_locality(struct device *dev,
struct crb_priv *priv, int loc)
{
- u32 mask = CRB_LOC_STATE_LOC_ASSIGNED |
- CRB_LOC_STATE_TPM_REG_VALID_STS;
+ u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS;
u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS;
+ int rc;
if (!priv->regs_h)
return 0;
iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
+
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc);
+ if (rc)
+ return rc;
+ }
+
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value,
TPM2_TIMEOUT_C)) {
- dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
+ dev_warn(dev, "TPM_LOC_STATE_x.Relinquish timed out\n");
return -ETIME;
}
@@ -330,7 +414,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
#ifdef CONFIG_ARM64
/*
* This is a TPM Command Response Buffer start method that invokes a
- * Secure Monitor Call to requrest the firmware to execute or cancel
+ * Secure Monitor Call to request the firmware to execute or cancel
* a TPM 2.0 command.
*/
static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
@@ -355,7 +439,7 @@ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
}
#endif
-static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len)
{
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
int rc = 0;
@@ -371,6 +455,10 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
return -E2BIG;
}
+ /* Seems to be necessary for every command */
+ if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON)
+ __crb_cmd_ready(&chip->dev, priv, chip->locality);
+
memcpy_toio(priv->cmd, buf, len);
/* Make sure that cmd is populated before issuing start. */
@@ -380,13 +468,13 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
* report only ACPI start but in practice seems to require both
* CRB start, hence invoking CRB start method if hid == MSFT0101.
*/
- if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) ||
- (priv->sm == ACPI_TPM2_MEMORY_MAPPED) ||
- (!strcmp(priv->hid, "MSFT0101")))
+ if (priv->sm == ACPI_TPM2_COMMAND_BUFFER ||
+ priv->sm == ACPI_TPM2_MEMORY_MAPPED ||
+ !strcmp(priv->hid, "MSFT0101"))
iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
- if ((priv->sm == ACPI_TPM2_START_METHOD) ||
- (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD))
+ if (priv->sm == ACPI_TPM2_START_METHOD ||
+ priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
rc = crb_do_acpi_start(chip);
if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) {
@@ -394,19 +482,34 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id);
}
- return rc;
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality);
+ }
+
+ if (rc)
+ return rc;
+
+ return crb_try_pluton_doorbell(priv, false);
}
static void crb_cancel(struct tpm_chip *chip)
{
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+ int rc;
iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel);
- if (((priv->sm == ACPI_TPM2_START_METHOD) ||
- (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)) &&
+ if ((priv->sm == ACPI_TPM2_START_METHOD ||
+ priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) &&
crb_do_acpi_start(chip))
dev_err(&chip->dev, "ACPI Start failed\n");
+
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality);
+ if (rc)
+ dev_err(&chip->dev, "FF-A Start failed\n");
+ }
}
static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
@@ -517,22 +620,28 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
u32 rsp_size;
int ret;
- INIT_LIST_HEAD(&acpi_resource_list);
- ret = acpi_dev_get_resources(device, &acpi_resource_list,
- crb_check_resource, iores_array);
- if (ret < 0)
- return ret;
- acpi_dev_free_resource_list(&acpi_resource_list);
-
- if (resource_type(iores_array) != IORESOURCE_MEM) {
- dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n");
- return -EINVAL;
- } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) ==
- IORESOURCE_MEM) {
- dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n");
- memset(iores_array + TPM_CRB_MAX_RESOURCES,
- 0, sizeof(*iores_array));
- iores_array[TPM_CRB_MAX_RESOURCES].flags = 0;
+ /*
+ * Pluton sometimes does not define ACPI memory regions.
+ * Mapping is then done in crb_map_pluton
+ */
+ if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) {
+ INIT_LIST_HEAD(&acpi_resource_list);
+ ret = acpi_dev_get_resources(device, &acpi_resource_list,
+ crb_check_resource, iores_array);
+ if (ret < 0)
+ return ret;
+ acpi_dev_free_resource_list(&acpi_resource_list);
+
+ if (resource_type(iores_array) != IORESOURCE_MEM) {
+ dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n");
+ return -EINVAL;
+ } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) ==
+ IORESOURCE_MEM) {
+ dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n");
+ memset(iores_array + TPM_CRB_MAX_RESOURCES,
+ 0, sizeof(*iores_array));
+ iores_array[TPM_CRB_MAX_RESOURCES].flags = 0;
+ }
}
iores = NULL;
@@ -557,8 +666,9 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
* the control area, as one nice sane region except for some older
* stuff that puts the control area outside the ACPI IO region.
*/
- if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) ||
- (priv->sm == ACPI_TPM2_MEMORY_MAPPED)) {
+ if (priv->sm == ACPI_TPM2_COMMAND_BUFFER ||
+ priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA ||
+ priv->sm == ACPI_TPM2_MEMORY_MAPPED) {
if (iores &&
buf->control_address == iores->start +
sizeof(*priv->regs_h))
@@ -575,7 +685,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
* PTT HW bug w/a: wake up the device to access
* possibly not retained registers.
*/
- ret = __crb_cmd_ready(dev, priv);
+ ret = __crb_cmd_ready(dev, priv, 0);
if (ret)
goto out_relinquish_locality;
@@ -647,7 +757,7 @@ out:
if (!ret)
priv->cmd_size = cmd_size;
- __crb_go_idle(dev, priv);
+ __crb_go_idle(dev, priv, 0);
out_relinquish_locality:
@@ -656,6 +766,22 @@ out_relinquish_locality:
return ret;
}
+static int crb_map_pluton(struct device *dev, struct crb_priv *priv,
+ struct acpi_table_tpm2 *buf, struct tpm2_crb_pluton *crb_pluton)
+{
+ priv->pluton_start_addr = crb_map_res(dev, NULL, NULL,
+ crb_pluton->start_addr, 4);
+ if (IS_ERR(priv->pluton_start_addr))
+ return PTR_ERR(priv->pluton_start_addr);
+
+ priv->pluton_reply_addr = crb_map_res(dev, NULL, NULL,
+ crb_pluton->reply_addr, 4);
+ if (IS_ERR(priv->pluton_reply_addr))
+ return PTR_ERR(priv->pluton_reply_addr);
+
+ return 0;
+}
+
static int crb_acpi_add(struct acpi_device *device)
{
struct acpi_table_tpm2 *buf;
@@ -663,6 +789,8 @@ static int crb_acpi_add(struct acpi_device *device)
struct tpm_chip *chip;
struct device *dev = &device->dev;
struct tpm2_crb_smc *crb_smc;
+ struct tpm2_crb_ffa *crb_ffa;
+ struct tpm2_crb_pluton *crb_pluton;
acpi_status status;
u32 sm;
int rc;
@@ -676,12 +804,16 @@ static int crb_acpi_add(struct acpi_device *device)
/* Should the FIFO driver handle this? */
sm = buf->start_method;
- if (sm == ACPI_TPM2_MEMORY_MAPPED)
- return -ENODEV;
+ if (sm == ACPI_TPM2_MEMORY_MAPPED) {
+ rc = -ENODEV;
+ goto out;
+ }
priv = devm_kzalloc(dev, sizeof(struct crb_priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+ if (!priv) {
+ rc = -ENOMEM;
+ goto out;
+ }
if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) {
if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) {
@@ -689,38 +821,92 @@ static int crb_acpi_add(struct acpi_device *device)
FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n",
buf->header.length,
ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf, sizeof(*buf));
priv->smc_func_id = crb_smc->smc_func_id;
}
+ if (sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ if (buf->header.length < (sizeof(*buf) + sizeof(*crb_ffa))) {
+ dev_err(dev,
+ FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n",
+ buf->header.length,
+ ACPI_TPM2_CRB_WITH_ARM_FFA);
+ rc = -EINVAL;
+ goto out;
+ }
+ crb_ffa = ACPI_ADD_PTR(struct tpm2_crb_ffa, buf, sizeof(*buf));
+ priv->ffa_flags = crb_ffa->flags;
+ priv->ffa_attributes = crb_ffa->attributes;
+ rc = tpm_crb_ffa_init();
+ if (rc) {
+ /* If FF-A driver is not available yet, request probe retry */
+ if (rc == -ENOENT)
+ rc = -EPROBE_DEFER;
+ goto out;
+ }
+ }
+
+ if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) {
+ if (buf->header.length < (sizeof(*buf) + sizeof(*crb_pluton))) {
+ dev_err(dev,
+ FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n",
+ buf->header.length,
+ ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON);
+ rc = -EINVAL;
+ goto out;
+ }
+ crb_pluton = ACPI_ADD_PTR(struct tpm2_crb_pluton, buf, sizeof(*buf));
+ rc = crb_map_pluton(dev, priv, buf, crb_pluton);
+ if (rc)
+ goto out;
+ }
+
priv->sm = sm;
priv->hid = acpi_device_hid(device);
rc = crb_map_io(device, priv, buf);
if (rc)
- return rc;
+ goto out;
chip = tpmm_chip_alloc(dev, &tpm_crb);
- if (IS_ERR(chip))
- return PTR_ERR(chip);
+ if (IS_ERR(chip)) {
+ rc = PTR_ERR(chip);
+ goto out;
+ }
dev_set_drvdata(&chip->dev, priv);
chip->acpi_dev_handle = device->handle;
chip->flags = TPM_CHIP_FLAG_TPM2;
- return tpm_chip_register(chip);
+ rc = tpm_chip_bootstrap(chip);
+ if (rc)
+ goto out;
+
+#ifdef CONFIG_X86
+ /* A quirk for https://www.amd.com/en/support/kb/faq/pa-410 */
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+ priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) {
+ dev_info(dev, "Disabling hwrng\n");
+ chip->flags |= TPM_CHIP_FLAG_HWRNG_DISABLED;
+ }
+#endif /* CONFIG_X86 */
+
+ rc = tpm_chip_register(chip);
+
+out:
+ acpi_put_table((struct acpi_table_header *)buf);
+ return rc;
}
-static int crb_acpi_remove(struct acpi_device *device)
+static void crb_acpi_remove(struct acpi_device *device)
{
struct device *dev = &device->dev;
struct tpm_chip *chip = dev_get_drvdata(dev);
tpm_chip_unregister(chip);
-
- return 0;
}
static const struct dev_pm_ops crb_pm = {
diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
new file mode 100644
index 000000000000..755b77b32ea4
--- /dev/null
+++ b/drivers/char/tpm/tpm_crb_ffa.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ *
+ * This device driver implements the TPM CRB start method
+ * as defined in the TPM Service Command Response Buffer
+ * Interface Over FF-A (DEN0138).
+ */
+
+#define pr_fmt(fmt) "CRB_FFA: " fmt
+
+#include <linux/arm_ffa.h>
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include "tpm_crb_ffa.h"
+
+static unsigned int busy_timeout_ms = 2000;
+
+module_param(busy_timeout_ms, uint, 0644);
+MODULE_PARM_DESC(busy_timeout_ms,
+ "Maximum time in ms to retry before giving up on busy");
+
+/* TPM service function status codes */
+#define CRB_FFA_OK 0x05000001
+#define CRB_FFA_OK_RESULTS_RETURNED 0x05000002
+#define CRB_FFA_NOFUNC 0x8e000001
+#define CRB_FFA_NOTSUP 0x8e000002
+#define CRB_FFA_INVARG 0x8e000005
+#define CRB_FFA_INV_CRB_CTRL_DATA 0x8e000006
+#define CRB_FFA_ALREADY 0x8e000009
+#define CRB_FFA_DENIED 0x8e00000a
+#define CRB_FFA_NOMEM 0x8e00000b
+
+#define CRB_FFA_VERSION_MAJOR 1
+#define CRB_FFA_VERSION_MINOR 0
+
+/* version encoding */
+#define CRB_FFA_MAJOR_VERSION_MASK GENMASK(30, 16)
+#define CRB_FFA_MINOR_VERSION_MASK GENMASK(15, 0)
+#define CRB_FFA_MAJOR_VERSION(x) ((u16)(FIELD_GET(CRB_FFA_MAJOR_VERSION_MASK, (x))))
+#define CRB_FFA_MINOR_VERSION(x) ((u16)(FIELD_GET(CRB_FFA_MINOR_VERSION_MASK, (x))))
+
+/*
+ * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and
+ * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal
+ * messages.
+ *
+ * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP
+ * are using the AArch32 or AArch64 SMC calling convention with register usage
+ * as defined in FF-A specification:
+ * w0: Function ID
+ * -for 32-bit: 0x8400006F or 0x84000070
+ * -for 64-bit: 0xC400006F or 0xC4000070
+ * w1: Source/Destination IDs
+ * w2: Reserved (MBZ)
+ * w3-w7: Implementation defined, free to be used below
+ */
+
+/*
+ * Returns the version of the interface that is available
+ * Call register usage:
+ * w3: Not used (MBZ)
+ * w4: TPM service function ID, CRB_FFA_GET_INTERFACE_VERSION
+ * w5-w7: Reserved (MBZ)
+ *
+ * Return register usage:
+ * w3: Not used (MBZ)
+ * w4: TPM service function status
+ * w5: TPM service interface version
+ * Bits[31:16]: major version
+ * Bits[15:0]: minor version
+ * w6-w7: Reserved (MBZ)
+ *
+ * Possible function status codes in register w4:
+ * CRB_FFA_OK_RESULTS_RETURNED: The version of the interface has been
+ * returned.
+ */
+#define CRB_FFA_GET_INTERFACE_VERSION 0x0f000001
+
+/*
+ * Notifies the TPM service that a TPM command or TPM locality request is
+ * ready to be processed, and allows the TPM service to process it.
+ * Call register usage:
+ * w3: Not used (MBZ)
+ * w4: TPM service function ID, CRB_FFA_START
+ * w5: Start function qualifier
+ * Bits[31:8] (MBZ)
+ * Bits[7:0]
+ * 0: Notifies TPM that a command is ready to be processed
+ * 1: Notifies TPM that a locality request is ready to be processed
+ * w6: TPM locality, one of 0..4
+ * -If the start function qualifier is 0, identifies the locality
+ * from where the command originated.
+ * -If the start function qualifier is 1, identifies the locality
+ * of the locality request
+ * w6-w7: Reserved (MBZ)
+ *
+ * Return register usage:
+ * w3: Not used (MBZ)
+ * w4: TPM service function status
+ * w5-w7: Reserved (MBZ)
+ *
+ * Possible function status codes in register w4:
+ * CRB_FFA_OK: the TPM service has been notified successfully
+ * CRB_FFA_INVARG: one or more arguments are not valid
+ * CRB_FFA_INV_CRB_CTRL_DATA: CRB control data or locality control
+ * data at the given TPM locality is not valid
+ * CRB_FFA_DENIED: the TPM has previously disabled locality requests and
+ * command processing at the given locality
+ */
+#define CRB_FFA_START 0x0f000201
+
+struct tpm_crb_ffa {
+ struct ffa_device *ffa_dev;
+ u16 major_version;
+ u16 minor_version;
+ /* lock to protect sending of FF-A messages: */
+ struct mutex msg_data_lock;
+ union {
+ struct ffa_send_direct_data direct_msg_data;
+ struct ffa_send_direct_data2 direct_msg_data2;
+ };
+};
+
+static struct tpm_crb_ffa *tpm_crb_ffa;
+static struct ffa_driver tpm_crb_ffa_driver;
+
+static int tpm_crb_ffa_to_linux_errno(int errno)
+{
+ int rc;
+
+ switch (errno) {
+ case CRB_FFA_OK:
+ rc = 0;
+ break;
+ case CRB_FFA_OK_RESULTS_RETURNED:
+ rc = 0;
+ break;
+ case CRB_FFA_NOFUNC:
+ rc = -ENOENT;
+ break;
+ case CRB_FFA_NOTSUP:
+ rc = -EPERM;
+ break;
+ case CRB_FFA_INVARG:
+ rc = -EINVAL;
+ break;
+ case CRB_FFA_INV_CRB_CTRL_DATA:
+ rc = -ENOEXEC;
+ break;
+ case CRB_FFA_ALREADY:
+ rc = -EEXIST;
+ break;
+ case CRB_FFA_DENIED:
+ rc = -EACCES;
+ break;
+ case CRB_FFA_NOMEM:
+ rc = -ENOMEM;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+/**
+ * tpm_crb_ffa_init - called by the CRB driver to do any needed initialization
+ *
+ * This function is called by the tpm_crb driver during the tpm_crb
+ * driver's initialization. If the tpm_crb_ffa has not been probed
+ * yet, returns -ENOENT in order to force a retry. If th ffa_crb
+ * driver had been probed but failed with an error, returns -ENODEV
+ * in order to prevent further retries.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int tpm_crb_ffa_init(void)
+{
+ int ret = 0;
+
+ if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
+ ret = ffa_register(&tpm_crb_ffa_driver);
+ if (ret) {
+ tpm_crb_ffa = ERR_PTR(-ENODEV);
+ return ret;
+ }
+ }
+
+ if (!tpm_crb_ffa)
+ ret = -ENOENT;
+
+ if (IS_ERR_VALUE(tpm_crb_ffa))
+ ret = -ENODEV;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
+
+static int __tpm_crb_ffa_try_send_receive(unsigned long func_id,
+ unsigned long a0, unsigned long a1,
+ unsigned long a2)
+{
+ const struct ffa_msg_ops *msg_ops;
+ int ret;
+
+ msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops;
+
+ if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) {
+ tpm_crb_ffa->direct_msg_data2 = (struct ffa_send_direct_data2){
+ .data = { func_id, a0, a1, a2 },
+ };
+
+ ret = msg_ops->sync_send_receive2(tpm_crb_ffa->ffa_dev,
+ &tpm_crb_ffa->direct_msg_data2);
+ if (!ret)
+ ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data2.data[0]);
+ } else {
+ tpm_crb_ffa->direct_msg_data = (struct ffa_send_direct_data){
+ .data1 = func_id,
+ .data2 = a0,
+ .data3 = a1,
+ .data4 = a2,
+ };
+
+ ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev,
+ &tpm_crb_ffa->direct_msg_data);
+ if (!ret)
+ ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1);
+ }
+
+ return ret;
+}
+
+static int __tpm_crb_ffa_send_receive(unsigned long func_id, unsigned long a0,
+ unsigned long a1, unsigned long a2)
+{
+ ktime_t start, stop;
+ int ret;
+
+ if (!tpm_crb_ffa)
+ return -ENOENT;
+
+ start = ktime_get();
+ stop = ktime_add(start, ms_to_ktime(busy_timeout_ms));
+
+ for (;;) {
+ ret = __tpm_crb_ffa_try_send_receive(func_id, a0, a1, a2);
+ if (ret != -EBUSY)
+ break;
+
+ usleep_range(50, 100);
+ if (ktime_after(ktime_get(), stop)) {
+ dev_warn(&tpm_crb_ffa->ffa_dev->dev,
+ "Busy retry timed out\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * tpm_crb_ffa_get_interface_version() - gets the ABI version of the TPM service
+ * @major: Pointer to caller-allocated buffer to hold the major version
+ * number the ABI
+ * @minor: Pointer to caller-allocated buffer to hold the minor version
+ * number the ABI
+ *
+ * Returns the major and minor version of the ABI of the FF-A based TPM.
+ * Allows the caller to evaluate its compatibility with the version of
+ * the ABI.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor)
+{
+ int rc;
+
+ if (!tpm_crb_ffa)
+ return -ENOENT;
+
+ if (IS_ERR_VALUE(tpm_crb_ffa))
+ return -ENODEV;
+
+ if (!major || !minor)
+ return -EINVAL;
+
+ guard(mutex)(&tpm_crb_ffa->msg_data_lock);
+
+ rc = __tpm_crb_ffa_send_receive(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00);
+ if (!rc) {
+ if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) {
+ *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]);
+ *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]);
+ } else {
+ *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data.data2);
+ *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data.data2);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * tpm_crb_ffa_start() - signals the TPM that a field has changed in the CRB
+ * @request_type: Identifies whether the change to the CRB is in the command
+ * fields or locality fields.
+ * @locality: Specifies the locality number.
+ *
+ * Used by the CRB driver
+ * that might be useful to those using or modifying it. Begins with
+ * empty comment line, and may include additional embedded empty
+ * comment lines.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int tpm_crb_ffa_start(int request_type, int locality)
+{
+ if (!tpm_crb_ffa)
+ return -ENOENT;
+
+ if (IS_ERR_VALUE(tpm_crb_ffa))
+ return -ENODEV;
+
+ guard(mutex)(&tpm_crb_ffa->msg_data_lock);
+
+ return __tpm_crb_ffa_send_receive(CRB_FFA_START, request_type, locality, 0x00);
+}
+EXPORT_SYMBOL_GPL(tpm_crb_ffa_start);
+
+static int tpm_crb_ffa_probe(struct ffa_device *ffa_dev)
+{
+ struct tpm_crb_ffa *p;
+ int rc;
+
+ /* only one instance of a TPM partition is supported */
+ if (tpm_crb_ffa && !IS_ERR_VALUE(tpm_crb_ffa))
+ return -EEXIST;
+
+ tpm_crb_ffa = ERR_PTR(-ENODEV); // set tpm_crb_ffa so we can detect probe failure
+
+ if (!ffa_partition_supports_direct_recv(ffa_dev) &&
+ !ffa_partition_supports_direct_req2_recv(ffa_dev)) {
+ dev_warn(&ffa_dev->dev, "partition doesn't support direct message receive.\n");
+ return -EINVAL;
+ }
+
+ p = kzalloc(sizeof(*tpm_crb_ffa), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ tpm_crb_ffa = p;
+
+ mutex_init(&tpm_crb_ffa->msg_data_lock);
+ tpm_crb_ffa->ffa_dev = ffa_dev;
+ ffa_dev_set_drvdata(ffa_dev, tpm_crb_ffa);
+
+ /* if TPM is aarch32 use 32-bit SMCs */
+ if (!ffa_partition_check_property(ffa_dev, FFA_PARTITION_AARCH64_EXEC))
+ ffa_dev->ops->msg_ops->mode_32bit_set(ffa_dev);
+
+ /* verify compatibility of TPM service version number */
+ rc = tpm_crb_ffa_get_interface_version(&tpm_crb_ffa->major_version,
+ &tpm_crb_ffa->minor_version);
+ if (rc) {
+ dev_err(&ffa_dev->dev, "failed to get crb interface version. rc:%d\n", rc);
+ goto out;
+ }
+
+ dev_info(&ffa_dev->dev, "ABI version %u.%u\n", tpm_crb_ffa->major_version,
+ tpm_crb_ffa->minor_version);
+
+ if (tpm_crb_ffa->major_version != CRB_FFA_VERSION_MAJOR ||
+ (tpm_crb_ffa->minor_version > 0 &&
+ tpm_crb_ffa->minor_version < CRB_FFA_VERSION_MINOR)) {
+ dev_warn(&ffa_dev->dev, "Incompatible ABI version\n");
+ goto out;
+ }
+
+ return 0;
+
+out:
+ kfree(tpm_crb_ffa);
+ tpm_crb_ffa = ERR_PTR(-ENODEV);
+ return -EINVAL;
+}
+
+static void tpm_crb_ffa_remove(struct ffa_device *ffa_dev)
+{
+ kfree(tpm_crb_ffa);
+ tpm_crb_ffa = NULL;
+}
+
+static const struct ffa_device_id tpm_crb_ffa_device_id[] = {
+ /* 17b862a4-1806-4faf-86b3-089a58353861 */
+ { UUID_INIT(0x17b862a4, 0x1806, 0x4faf,
+ 0x86, 0xb3, 0x08, 0x9a, 0x58, 0x35, 0x38, 0x61) },
+ {}
+};
+
+static struct ffa_driver tpm_crb_ffa_driver = {
+ .name = "ffa-crb",
+ .probe = tpm_crb_ffa_probe,
+ .remove = tpm_crb_ffa_remove,
+ .id_table = tpm_crb_ffa_device_id,
+};
+
+#ifdef MODULE
+module_ffa_driver(tpm_crb_ffa_driver);
+#endif
+
+MODULE_AUTHOR("Arm");
+MODULE_DESCRIPTION("TPM CRB FFA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_crb_ffa.h b/drivers/char/tpm/tpm_crb_ffa.h
new file mode 100644
index 000000000000..d7e1344ea003
--- /dev/null
+++ b/drivers/char/tpm/tpm_crb_ffa.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ *
+ * This device driver implements the TPM CRB start method
+ * as defined in the TPM Service Command Response Buffer
+ * Interface Over FF-A (DEN0138).
+ */
+#ifndef _TPM_CRB_FFA_H
+#define _TPM_CRB_FFA_H
+
+#if IS_REACHABLE(CONFIG_TCG_ARM_CRB_FFA)
+int tpm_crb_ffa_init(void);
+int tpm_crb_ffa_start(int request_type, int locality);
+#else
+static inline int tpm_crb_ffa_init(void) { return 0; }
+static inline int tpm_crb_ffa_start(int request_type, int locality) { return 0; }
+#endif
+
+#define CRB_FFA_START_TYPE_COMMAND 0
+#define CRB_FFA_START_TYPE_LOCALITY_REQUEST 1
+
+#endif
diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
index 5c233423c56f..4e63c30aeaf1 100644
--- a/drivers/char/tpm/tpm_ftpm_tee.c
+++ b/drivers/char/tpm/tpm_ftpm_tee.c
@@ -11,7 +11,6 @@
#include <linux/acpi.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/tee_drv.h>
#include <linux/tpm.h>
@@ -32,45 +31,19 @@ static const uuid_t ftpm_ta_uuid =
0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96);
/**
- * ftpm_tee_tpm_op_recv() - retrieve fTPM response.
- * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h.
- * @buf: the buffer to store data.
- * @count: the number of bytes to read.
- *
- * Return:
- * In case of success the number of bytes received.
- * On failure, -errno.
- */
-static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
- size_t len;
-
- len = pvt_data->resp_len;
- if (count < len) {
- dev_err(&chip->dev,
- "%s: Invalid size in recv: count=%zd, resp_len=%zd\n",
- __func__, count, len);
- return -EIO;
- }
-
- memcpy(buf, pvt_data->resp_buf, len);
- pvt_data->resp_len = 0;
-
- return len;
-}
-
-/**
- * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory.
+ * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory
+ * and retrieve the response.
* @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h
- * @buf: the buffer to send.
- * @len: the number of bytes to send.
+ * @buf: the buffer to send and to store the response.
+ * @bufsiz: the size of the buffer.
+ * @cmd_len: the number of bytes to send.
*
* Return:
- * In case of success, returns 0.
+ * In case of success, returns the number of bytes received.
* On failure, -errno
*/
-static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t cmd_len)
{
struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
size_t resp_len;
@@ -81,16 +54,15 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
struct tee_param command_params[4];
struct tee_shm *shm = pvt_data->shm;
- if (len > MAX_COMMAND_SIZE) {
+ if (cmd_len > MAX_COMMAND_SIZE) {
dev_err(&chip->dev,
"%s: len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM TA\n",
- __func__, len);
+ __func__, cmd_len);
return -EIO;
}
memset(&transceive_args, 0, sizeof(transceive_args));
memset(command_params, 0, sizeof(command_params));
- pvt_data->resp_len = 0;
/* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */
transceive_args = (struct tee_ioctl_invoke_arg) {
@@ -104,7 +76,7 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = shm,
- .size = len,
+ .size = cmd_len,
.shm_offs = 0,
},
};
@@ -116,7 +88,7 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
return PTR_ERR(temp_buf);
}
memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
- memcpy(temp_buf, buf, len);
+ memcpy(temp_buf, buf, cmd_len);
command_params[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
@@ -157,38 +129,21 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
__func__, resp_len);
return -EIO;
}
+ if (resp_len > bufsiz) {
+ dev_err(&chip->dev,
+ "%s: resp_len=%zd exceeds bufsiz=%zd\n",
+ __func__, resp_len, bufsiz);
+ return -EIO;
+ }
- /* sanity checks look good, cache the response */
- memcpy(pvt_data->resp_buf, temp_buf, resp_len);
- pvt_data->resp_len = resp_len;
-
- return 0;
-}
-
-static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip)
-{
- /* not supported */
-}
+ memcpy(buf, temp_buf, resp_len);
-static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
-{
- return 0;
-}
-
-static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
-{
- return false;
+ return resp_len;
}
static const struct tpm_class_ops ftpm_tee_tpm_ops = {
.flags = TPM_OPS_AUTO_STARTUP,
- .recv = ftpm_tee_tpm_op_recv,
.send = ftpm_tee_tpm_op_send,
- .cancel = ftpm_tee_tpm_op_cancel,
- .status = ftpm_tee_tpm_op_status,
- .req_complete_mask = 0,
- .req_complete_val = 0,
- .req_canceled = ftpm_tee_tpm_req_canceled,
};
/*
@@ -209,7 +164,7 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data)
/**
* ftpm_tee_probe() - initialize the fTPM
- * @pdev: the platform_device description.
+ * @dev: the device description.
*
* Return:
* On success, 0. On failure, -errno.
@@ -272,7 +227,7 @@ static int ftpm_tee_probe(struct device *dev)
}
pvt_data->chip = chip;
- pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2;
+ pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC;
/* Create a character device for the fTPM */
rc = tpm_chip_register(pvt_data->chip);
@@ -305,7 +260,7 @@ static int ftpm_plat_tee_probe(struct platform_device *pdev)
/**
* ftpm_tee_remove() - remove the TPM device
- * @pdev: the platform_device description.
+ * @dev: the device description.
*
* Return:
* 0 always.
@@ -334,15 +289,15 @@ static int ftpm_tee_remove(struct device *dev)
return 0;
}
-static int ftpm_plat_tee_remove(struct platform_device *pdev)
+static void ftpm_plat_tee_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- return ftpm_tee_remove(dev);
+ ftpm_tee_remove(dev);
}
/**
- * ftpm_tee_shutdown() - shutdown the TPM device
+ * ftpm_plat_tee_shutdown() - shutdown the TPM device
* @pdev: the platform_device description.
*/
static void ftpm_plat_tee_shutdown(struct platform_device *pdev)
@@ -363,7 +318,7 @@ MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids);
static struct platform_driver ftpm_tee_plat_driver = {
.driver = {
.name = "ftpm-tee",
- .of_match_table = of_match_ptr(of_ftpm_tee_ids),
+ .of_match_table = of_ftpm_tee_ids,
},
.shutdown = ftpm_plat_tee_shutdown,
.probe = ftpm_plat_tee_probe,
@@ -397,7 +352,13 @@ static int __init ftpm_mod_init(void)
if (rc)
return rc;
- return driver_register(&ftpm_tee_driver.driver);
+ rc = driver_register(&ftpm_tee_driver.driver);
+ if (rc) {
+ platform_driver_unregister(&ftpm_tee_plat_driver);
+ return rc;
+ }
+
+ return 0;
}
static void __exit ftpm_mod_exit(void)
diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h
index f98daa7bf68c..8d5c3f0d2879 100644
--- a/drivers/char/tpm/tpm_ftpm_tee.h
+++ b/drivers/char/tpm/tpm_ftpm_tee.h
@@ -21,18 +21,13 @@
/**
* struct ftpm_tee_private - fTPM's private data
* @chip: struct tpm_chip instance registered with tpm framework.
- * @state: internal state
* @session: fTPM TA session identifier.
- * @resp_len: cached response buffer length.
- * @resp_buf: cached response buffer.
* @ctx: TEE context handler.
* @shm: Memory pool shared with fTPM TA in TEE.
*/
struct ftpm_tee_private {
struct tpm_chip *chip;
u32 session;
- size_t resp_len;
- u8 resp_buf[MAX_RESPONSE_SIZE];
struct tee_context *ctx;
struct tee_shm *shm;
};
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
index d5ac85558214..4f229656a8e2 100644
--- a/drivers/char/tpm/tpm_i2c_atmel.c
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -37,7 +37,8 @@ struct priv_data {
u8 buffer[sizeof(struct tpm_header) + 25];
};
-static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t len)
{
struct priv_data *priv = dev_get_drvdata(&chip->dev);
struct i2c_client *client = to_i2c_client(chip->dev.parent);
@@ -146,8 +147,7 @@ static const struct tpm_class_ops i2c_atmel = {
.req_canceled = i2c_atmel_req_canceled,
};
-static int i2c_atmel_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int i2c_atmel_probe(struct i2c_client *client)
{
struct tpm_chip *chip;
struct device *dev = &client->dev;
@@ -179,16 +179,15 @@ static int i2c_atmel_probe(struct i2c_client *client,
return tpm_chip_register(chip);
}
-static int i2c_atmel_remove(struct i2c_client *client)
+static void i2c_atmel_remove(struct i2c_client *client)
{
struct device *dev = &(client->dev);
struct tpm_chip *chip = dev_get_drvdata(dev);
tpm_chip_unregister(chip);
- return 0;
}
static const struct i2c_device_id i2c_atmel_id[] = {
- {I2C_DRIVER_NAME, 0},
+ { I2C_DRIVER_NAME },
{}
};
MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index a19d32cb4e94..bdf1f329a679 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -514,7 +514,8 @@ out:
return size;
}
-static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t len)
{
int rc, status;
ssize_t burstcnt;
@@ -681,8 +682,7 @@ MODULE_DEVICE_TABLE(of, tpm_tis_i2c_of_match);
static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume);
-static int tpm_tis_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tpm_tis_i2c_probe(struct i2c_client *client)
{
int rc;
struct device *dev = &(client->dev);
@@ -706,15 +706,13 @@ static int tpm_tis_i2c_probe(struct i2c_client *client,
return rc;
}
-static int tpm_tis_i2c_remove(struct i2c_client *client)
+static void tpm_tis_i2c_remove(struct i2c_client *client)
{
struct tpm_chip *chip = tpm_dev.chip;
tpm_chip_unregister(chip);
release_locality(chip, tpm_dev.locality, 1);
tpm_dev.client = NULL;
-
- return 0;
}
static struct i2c_driver tpm_tis_i2c_driver = {
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index b77c18e38662..d44903b29929 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -19,7 +19,8 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include "tpm.h"
/* I2C interface offsets */
@@ -349,7 +350,8 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t len)
{
struct priv_data *priv = dev_get_drvdata(&chip->dev);
struct device *dev = chip->dev.parent;
@@ -522,8 +524,7 @@ static int get_vid(struct i2c_client *client, u32 *res)
return 0;
}
-static int i2c_nuvoton_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int i2c_nuvoton_probe(struct i2c_client *client)
{
int rc;
struct tpm_chip *chip;
@@ -546,15 +547,8 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
if (!priv)
return -ENOMEM;
- if (dev->of_node) {
- const struct of_device_id *of_id;
-
- of_id = of_match_device(dev->driver->of_match_table, dev);
- if (of_id && of_id->data == OF_IS_TPM2)
- chip->flags |= TPM_CHIP_FLAG_TPM2;
- } else
- if (id->driver_data == I2C_IS_TPM2)
- chip->flags |= TPM_CHIP_FLAG_TPM2;
+ if (i2c_get_match_data(client))
+ chip->flags |= TPM_CHIP_FLAG_TPM2;
init_waitqueue_head(&priv->read_queue);
@@ -622,12 +616,11 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
return tpm_chip_register(chip);
}
-static int i2c_nuvoton_remove(struct i2c_client *client)
+static void i2c_nuvoton_remove(struct i2c_client *client)
{
struct tpm_chip *chip = i2c_get_clientdata(client);
tpm_chip_unregister(chip);
- return 0;
}
static const struct i2c_device_id i2c_nuvoton_id[] = {
@@ -662,6 +655,6 @@ static struct i2c_driver i2c_nuvoton_driver = {
module_i2c_driver(i2c_nuvoton_driver);
-MODULE_AUTHOR("Dan Morav (dan.morav@nuvoton.com)");
+MODULE_AUTHOR("Dan Morav <dan.morav@nuvoton.com>");
MODULE_DESCRIPTION("Nuvoton TPM I2C Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
index d3989b257f42..4734a69406ce 100644
--- a/drivers/char/tpm/tpm_ibmvtpm.c
+++ b/drivers/char/tpm/tpm_ibmvtpm.c
@@ -191,13 +191,15 @@ static int tpm_ibmvtpm_resume(struct device *dev)
* tpm_ibmvtpm_send() - Send a TPM command
* @chip: tpm chip struct
* @buf: buffer contains data to send
- * @count: size of buffer
+ * @bufsiz: size of the buffer
+ * @count: length of the command
*
* Return:
* 0 on success,
* -errno on error
*/
-static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t count)
{
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
bool retry = true;
@@ -450,6 +452,7 @@ static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
}
static const struct tpm_class_ops tpm_ibmvtpm = {
+ .flags = TPM_OPS_AUTO_STARTUP,
.recv = tpm_ibmvtpm_recv,
.send = tpm_ibmvtpm_send,
.cancel = tpm_ibmvtpm_cancel,
@@ -690,16 +693,6 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
if (!strcmp(id->compat, "IBM,vtpm20"))
chip->flags |= TPM_CHIP_FLAG_TPM2;
- rc = tpm_get_timeouts(chip);
- if (rc)
- goto init_irq_cleanup;
-
- if (chip->flags & TPM_CHIP_FLAG_TPM2) {
- rc = tpm2_get_cc_attrs_tbl(chip);
- if (rc)
- goto init_irq_cleanup;
- }
-
return tpm_chip_register(chip);
init_irq_cleanup:
do {
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index 9c924a1440a9..7638b65b851b 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -51,34 +51,40 @@ static struct tpm_inf_dev tpm_dev;
static inline void tpm_data_out(unsigned char data, unsigned char offset)
{
+#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
outb(data, tpm_dev.data_regs + offset);
else
+#endif
writeb(data, tpm_dev.mem_base + tpm_dev.data_regs + offset);
}
static inline unsigned char tpm_data_in(unsigned char offset)
{
+#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
return inb(tpm_dev.data_regs + offset);
- else
- return readb(tpm_dev.mem_base + tpm_dev.data_regs + offset);
+#endif
+ return readb(tpm_dev.mem_base + tpm_dev.data_regs + offset);
}
static inline void tpm_config_out(unsigned char data, unsigned char offset)
{
+#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
outb(data, tpm_dev.config_port + offset);
else
+#endif
writeb(data, tpm_dev.mem_base + tpm_dev.index_off + offset);
}
static inline unsigned char tpm_config_in(unsigned char offset)
{
+#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
return inb(tpm_dev.config_port + offset);
- else
- return readb(tpm_dev.mem_base + tpm_dev.index_off + offset);
+#endif
+ return readb(tpm_dev.mem_base + tpm_dev.index_off + offset);
}
/* TPM header definitions */
@@ -306,7 +312,8 @@ recv_begin:
return -EIO;
}
-static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
+static int tpm_inf_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t count)
{
int i;
int ret;
diff --git a/drivers/char/tpm/tpm_loongson.c b/drivers/char/tpm/tpm_loongson.c
new file mode 100644
index 000000000000..9e50250763d1
--- /dev/null
+++ b/drivers/char/tpm/tpm_loongson.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Loongson Technology Corporation Limited. */
+
+#include <linux/device.h>
+#include <linux/mfd/loongson-se.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include "tpm.h"
+
+struct tpm_loongson_cmd {
+ u32 cmd_id;
+ u32 data_off;
+ u32 data_len;
+ u32 pad[5];
+};
+
+static int tpm_loongson_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev);
+ struct tpm_loongson_cmd *cmd_ret = tpm_engine->command_ret;
+
+ if (cmd_ret->data_len > count)
+ return -EIO;
+
+ memcpy(buf, tpm_engine->data_buffer, cmd_ret->data_len);
+
+ return cmd_ret->data_len;
+}
+
+static int tpm_loongson_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t count)
+{
+ struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev);
+ struct tpm_loongson_cmd *cmd = tpm_engine->command;
+
+ if (count > tpm_engine->buffer_size)
+ return -E2BIG;
+
+ cmd->data_len = count;
+ memcpy(tpm_engine->data_buffer, buf, count);
+
+ return loongson_se_send_engine_cmd(tpm_engine);
+}
+
+static const struct tpm_class_ops tpm_loongson_ops = {
+ .flags = TPM_OPS_AUTO_STARTUP,
+ .recv = tpm_loongson_recv,
+ .send = tpm_loongson_send,
+};
+
+static int tpm_loongson_probe(struct platform_device *pdev)
+{
+ struct loongson_se_engine *tpm_engine;
+ struct device *dev = &pdev->dev;
+ struct tpm_loongson_cmd *cmd;
+ struct tpm_chip *chip;
+
+ tpm_engine = loongson_se_init_engine(dev->parent, SE_ENGINE_TPM);
+ if (!tpm_engine)
+ return -ENODEV;
+ cmd = tpm_engine->command;
+ cmd->cmd_id = SE_CMD_TPM;
+ cmd->data_off = tpm_engine->buffer_off;
+
+ chip = tpmm_chip_alloc(dev, &tpm_loongson_ops);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ chip->flags = TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_IRQ;
+ dev_set_drvdata(&chip->dev, tpm_engine);
+
+ return tpm_chip_register(chip);
+}
+
+static struct platform_driver tpm_loongson = {
+ .probe = tpm_loongson_probe,
+ .driver = {
+ .name = "tpm_loongson",
+ },
+};
+module_platform_driver(tpm_loongson);
+
+MODULE_ALIAS("platform:tpm_loongson");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Loongson TPM driver");
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 038701d48351..879ac88f5783 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -178,7 +178,8 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
return size;
}
-static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+static int tpm_nsc_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t count)
{
struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
u8 data;
@@ -410,7 +411,7 @@ static void __exit cleanup_nsc(void)
module_init(init_nsc);
module_exit(cleanup_nsc);
-MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_AUTHOR("Leendert van Doorn <leendert@watson.ibm.com>");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
index 40018a73b3cb..c9793a3d986d 100644
--- a/drivers/char/tpm/tpm_ppi.c
+++ b/drivers/char/tpm/tpm_ppi.c
@@ -33,6 +33,20 @@ static const guid_t tpm_ppi_guid =
GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4,
0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53);
+static const char * const tpm_ppi_info[] = {
+ "Not implemented",
+ "BIOS only",
+ "Blocked for OS by system firmware",
+ "User required",
+ "User not required",
+};
+
+/* A spinlock to protect access to the cache from concurrent reads */
+static DEFINE_MUTEX(tpm_ppi_lock);
+
+static u32 ppi_operations_cache[PPI_VS_REQ_END + 1];
+static bool ppi_cache_populated;
+
static bool tpm_ppi_req_has_parameter(u64 req)
{
return req == 23;
@@ -52,7 +66,7 @@ static ssize_t tpm_show_ppi_version(struct device *dev,
{
struct tpm_chip *chip = to_tpm_chip(dev);
- return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version);
+ return sysfs_emit(buf, "%s\n", chip->ppi_version);
}
static ssize_t tpm_show_ppi_request(struct device *dev,
@@ -87,12 +101,10 @@ static ssize_t tpm_show_ppi_request(struct device *dev,
else {
req = obj->package.elements[1].integer.value;
if (tpm_ppi_req_has_parameter(req))
- size = scnprintf(buf, PAGE_SIZE,
- "%llu %llu\n", req,
- obj->package.elements[2].integer.value);
+ size = sysfs_emit(buf, "%llu %llu\n", req,
+ obj->package.elements[2].integer.value);
else
- size = scnprintf(buf, PAGE_SIZE,
- "%llu\n", req);
+ size = sysfs_emit(buf, "%llu\n", req);
}
} else if (obj->package.count == 2 &&
obj->package.elements[0].type == ACPI_TYPE_INTEGER &&
@@ -100,8 +112,8 @@ static ssize_t tpm_show_ppi_request(struct device *dev,
if (obj->package.elements[0].integer.value)
size = -EFAULT;
else
- size = scnprintf(buf, PAGE_SIZE, "%llu\n",
- obj->package.elements[1].integer.value);
+ size = sysfs_emit(buf, "%llu\n",
+ obj->package.elements[1].integer.value);
}
ACPI_FREE(obj);
@@ -211,10 +223,10 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev,
}
if (ret < ARRAY_SIZE(info) - 1)
- status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
+ status = sysfs_emit(buf, "%d: %s\n", ret, info[ret]);
else
- status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
- info[ARRAY_SIZE(info)-1]);
+ status = sysfs_emit(buf, "%d: %s\n", ret,
+ info[ARRAY_SIZE(info) - 1]);
return status;
}
@@ -255,23 +267,23 @@ static ssize_t tpm_show_ppi_response(struct device *dev,
res = ret_obj[2].integer.value;
if (req) {
if (res == 0)
- status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
- "0: Success");
+ status = sysfs_emit(buf, "%llu %s\n", req,
+ "0: Success");
else if (res == 0xFFFFFFF0)
- status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
- "0xFFFFFFF0: User Abort");
+ status = sysfs_emit(buf, "%llu %s\n", req,
+ "0xFFFFFFF0: User Abort");
else if (res == 0xFFFFFFF1)
- status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
- "0xFFFFFFF1: BIOS Failure");
+ status = sysfs_emit(buf, "%llu %s\n", req,
+ "0xFFFFFFF1: BIOS Failure");
else if (res >= 1 && res <= 0x00000FFF)
- status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
- req, res, "Corresponding TPM error");
+ status = sysfs_emit(buf, "%llu %llu: %s\n",
+ req, res, "Corresponding TPM error");
else
- status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
- req, res, "Error");
+ status = sysfs_emit(buf, "%llu %llu: %s\n",
+ req, res, "Error");
} else {
- status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
- req, "No Recent Request");
+ status = sysfs_emit(buf, "%llu: %s\n",
+ req, "No Recent Request");
}
cleanup:
@@ -279,46 +291,33 @@ cleanup:
return status;
}
-static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start,
- u32 end)
+static ssize_t cache_ppi_operations(acpi_handle dev_handle, char *buf)
{
int i;
u32 ret;
- char *str = buf;
+ int len = 0;
union acpi_object *obj, tmp;
union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp);
- static char *info[] = {
- "Not implemented",
- "BIOS only",
- "Blocked for OS by BIOS",
- "User required",
- "User not required",
- };
-
if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1,
1 << TPM_PPI_FN_GETOPR))
return -EPERM;
tmp.integer.type = ACPI_TYPE_INTEGER;
- for (i = start; i <= end; i++) {
+ for (i = 0; i <= PPI_VS_REQ_END; i++) {
tmp.integer.value = i;
obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR,
ACPI_TYPE_INTEGER, &argv,
TPM_PPI_REVISION_ID_1);
- if (!obj) {
+ if (!obj)
return -ENOMEM;
- } else {
- ret = obj->integer.value;
- ACPI_FREE(obj);
- }
- if (ret > 0 && ret < ARRAY_SIZE(info))
- str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
- i, ret, info[ret]);
+ ret = obj->integer.value;
+ ppi_operations_cache[i] = ret;
+ ACPI_FREE(obj);
}
- return str - buf;
+ return len;
}
static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
@@ -326,9 +325,30 @@ static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
char *buf)
{
struct tpm_chip *chip = to_tpm_chip(dev);
+ ssize_t len = 0;
+ u32 ret;
+ int i;
+
+ mutex_lock(&tpm_ppi_lock);
+ if (!ppi_cache_populated) {
+ len = cache_ppi_operations(chip->acpi_dev_handle, buf);
+ if (len < 0) {
+ mutex_unlock(&tpm_ppi_lock);
+ return len;
+ }
+
+ ppi_cache_populated = true;
+ }
- return show_ppi_operations(chip->acpi_dev_handle, buf, 0,
- PPI_TPM_REQ_MAX);
+ for (i = 0; i <= PPI_TPM_REQ_MAX; i++) {
+ ret = ppi_operations_cache[i];
+ if (ret >= 0 && ret < ARRAY_SIZE(tpm_ppi_info))
+ len += sysfs_emit_at(buf, len, "%d %d: %s\n",
+ i, ret, tpm_ppi_info[ret]);
+ }
+ mutex_unlock(&tpm_ppi_lock);
+
+ return len;
}
static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
@@ -336,9 +356,30 @@ static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
char *buf)
{
struct tpm_chip *chip = to_tpm_chip(dev);
+ ssize_t len = 0;
+ u32 ret;
+ int i;
+
+ mutex_lock(&tpm_ppi_lock);
+ if (!ppi_cache_populated) {
+ len = cache_ppi_operations(chip->acpi_dev_handle, buf);
+ if (len < 0) {
+ mutex_unlock(&tpm_ppi_lock);
+ return len;
+ }
+
+ ppi_cache_populated = true;
+ }
+
+ for (i = PPI_VS_REQ_START; i <= PPI_VS_REQ_END; i++) {
+ ret = ppi_operations_cache[i];
+ if (ret >= 0 && ret < ARRAY_SIZE(tpm_ppi_info))
+ len += sysfs_emit_at(buf, len, "%d %d: %s\n",
+ i, ret, tpm_ppi_info[ret]);
+ }
+ mutex_unlock(&tpm_ppi_lock);
- return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START,
- PPI_VS_REQ_END);
+ return len;
}
static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
@@ -380,7 +421,7 @@ void tpm_add_ppi(struct tpm_chip *chip)
TPM_PPI_FN_VERSION,
NULL, ACPI_TYPE_STRING);
if (obj) {
- strlcpy(chip->ppi_version, obj->string.pointer,
+ strscpy(chip->ppi_version, obj->string.pointer,
sizeof(chip->ppi_version));
ACPI_FREE(obj);
}
diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c
new file mode 100644
index 000000000000..f5ba0f64850b
--- /dev/null
+++ b/drivers/char/tpm/tpm_svsm.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ *
+ * Driver for the vTPM defined by the AMD SVSM spec [1].
+ *
+ * The specification defines a protocol that a SEV-SNP guest OS can use to
+ * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM)
+ * in the guest context, but at a more privileged level (usually VMPL0).
+ *
+ * [1] "Secure VM Service Module for SEV-SNP Guests"
+ * Publication # 58019 Revision: 1.00
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/tpm_svsm.h>
+
+#include <asm/sev.h>
+
+#include "tpm.h"
+
+struct tpm_svsm_priv {
+ void *buffer;
+};
+
+static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t cmd_len)
+{
+ struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev);
+ int ret;
+
+ ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, cmd_len);
+ if (ret)
+ return ret;
+
+ /*
+ * The SVSM call uses the same buffer for the command and for the
+ * response, so after this call, the buffer will contain the response.
+ *
+ * Note: we have to use an internal buffer because the device in SVSM
+ * expects the svsm_vtpm header + data to be physically contiguous.
+ */
+ ret = snp_svsm_vtpm_send_command(priv->buffer);
+ if (ret)
+ return ret;
+
+ return svsm_vtpm_cmd_response_parse(priv->buffer, buf, bufsiz);
+}
+
+static struct tpm_class_ops tpm_chip_ops = {
+ .flags = TPM_OPS_AUTO_STARTUP,
+ .send = tpm_svsm_send,
+};
+
+static int __init tpm_svsm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tpm_svsm_priv *priv;
+ struct tpm_chip *chip;
+ int err;
+
+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /*
+ * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER
+ * in tpm_svsm.h).
+ */
+ priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0);
+ if (!priv->buffer)
+ return -ENOMEM;
+
+ chip = tpmm_chip_alloc(dev, &tpm_chip_ops);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ dev_set_drvdata(&chip->dev, priv);
+
+ chip->flags |= TPM_CHIP_FLAG_SYNC;
+ err = tpm2_probe(chip);
+ if (err)
+ return err;
+
+ err = tpm_chip_register(chip);
+ if (err)
+ return err;
+
+ dev_info(dev, "SNP SVSM vTPM %s device\n",
+ (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2");
+
+ return 0;
+}
+
+static void __exit tpm_svsm_remove(struct platform_device *pdev)
+{
+ struct tpm_chip *chip = platform_get_drvdata(pdev);
+
+ tpm_chip_unregister(chip);
+}
+
+/*
+ * tpm_svsm_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound
+ * at runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver tpm_svsm_driver __refdata = {
+ .remove = __exit_p(tpm_svsm_remove),
+ .driver = {
+ .name = "tpm-svsm",
+ },
+};
+
+module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe);
+
+MODULE_DESCRIPTION("SNP SVSM vTPM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tpm-svsm");
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index bcff6429e0b4..9aa230a63616 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -25,9 +25,7 @@
#include <linux/acpi.h>
#include <linux/freezer.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/kernel.h>
-#include <linux/dmi.h>
#include "tpm.h"
#include "tpm_tis_core.h"
@@ -50,8 +48,47 @@ static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *da
return container_of(data, struct tpm_tis_tcg_phy, priv);
}
-static int interrupts = -1;
-module_param(interrupts, int, 0444);
+#ifdef CONFIG_PREEMPT_RT
+/*
+ * Flush previous write operations with a dummy read operation to the
+ * TPM MMIO base address.
+ */
+static inline void tpm_tis_flush(void __iomem *iobase)
+{
+ ioread8(iobase + TPM_ACCESS(0));
+}
+#else
+#define tpm_tis_flush(iobase) do { } while (0)
+#endif
+
+/*
+ * Write a byte word to the TPM MMIO address, and flush the write queue.
+ * The flush ensures that the data is sent immediately over the bus and not
+ * aggregated with further requests and transferred later in a batch. The large
+ * write requests can lead to unwanted latency spikes by blocking the CPU until
+ * the complete batch has been transferred.
+ */
+static inline void tpm_tis_iowrite8(u8 b, void __iomem *iobase, u32 addr)
+{
+ iowrite8(b, iobase + addr);
+ tpm_tis_flush(iobase);
+}
+
+/*
+ * Write a 32-bit word to the TPM MMIO address, and flush the write queue.
+ * The flush ensures that the data is sent immediately over the bus and not
+ * aggregated with further requests and transferred later in a batch. The large
+ * write requests can lead to unwanted latency spikes by blocking the CPU until
+ * the complete batch has been transferred.
+ */
+static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr)
+{
+ iowrite32(b, iobase + addr);
+ tpm_tis_flush(iobase);
+}
+
+static bool interrupts;
+module_param(interrupts, bool, 0444);
MODULE_PARM_DESC(interrupts, "Enable interrupts");
static bool itpm;
@@ -64,28 +101,6 @@ module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
#endif
-static int tpm_tis_disable_irq(const struct dmi_system_id *d)
-{
- if (interrupts == -1) {
- pr_notice("tpm_tis: %s detected: disabling interrupts.\n", d->ident);
- interrupts = 0;
- }
-
- return 0;
-}
-
-static const struct dmi_system_id tpm_tis_dmi_table[] = {
- {
- .callback = tpm_tis_disable_irq,
- .ident = "ThinkPad T490s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T490s"),
- },
- },
- {}
-};
-
#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
static int has_hid(struct acpi_device *dev, const char *hid)
{
@@ -125,6 +140,7 @@ static int check_acpi_tpm2(struct device *dev)
const struct acpi_device_id *aid = acpi_match_device(tpm_acpi_tbl, dev);
struct acpi_table_tpm2 *tbl;
acpi_status st;
+ int ret = 0;
if (!aid || aid->driver_data != DEVICE_IS_TPM2)
return 0;
@@ -132,8 +148,7 @@ static int check_acpi_tpm2(struct device *dev)
/* If the ACPI TPM2 signature is matched then a global ACPI_SIG_TPM2
* table is mandatory
*/
- st =
- acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **)&tbl);
+ st = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **)&tbl);
if (ACPI_FAILURE(st) || tbl->header.length < sizeof(*tbl)) {
dev_err(dev, FW_BUG "failed to get TPM2 ACPI table\n");
return -EINVAL;
@@ -141,9 +156,10 @@ static int check_acpi_tpm2(struct device *dev)
/* The tpm2_crb driver handles this device */
if (tbl->start_method != ACPI_TPM2_MEMORY_MAPPED)
- return -ENODEV;
+ ret = -ENODEV;
- return 0;
+ acpi_put_table((struct acpi_table_header *)tbl);
+ return ret;
}
#else
static int check_acpi_tpm2(struct device *dev)
@@ -185,12 +201,12 @@ static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
switch (io_mode) {
case TPM_TIS_PHYS_8:
while (len--)
- iowrite8(*value++, phy->iobase + addr);
+ tpm_tis_iowrite8(*value++, phy->iobase, addr);
break;
case TPM_TIS_PHYS_16:
return -EINVAL;
case TPM_TIS_PHYS_32:
- iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase + addr);
+ tpm_tis_iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase, addr);
break;
}
@@ -208,8 +224,6 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
int irq = -1;
int rc;
- dmi_check_system(tpm_tis_dmi_table);
-
rc = check_acpi_tpm2(dev);
if (rc)
return rc;
@@ -226,7 +240,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
irq = tpm_info->irq;
if (itpm || is_itpm(ACPI_COMPANION(dev)))
- phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND;
+ set_bit(TPM_TIS_ITPM_WORKAROUND, &phy->priv.flags);
return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
ACPI_HANDLE(dev));
@@ -323,18 +337,17 @@ static int tpm_tis_plat_probe(struct platform_device *pdev)
return tpm_tis_init(&pdev->dev, &tpm_info);
}
-static int tpm_tis_plat_remove(struct platform_device *pdev)
+static void tpm_tis_plat_remove(struct platform_device *pdev)
{
struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
tpm_chip_unregister(chip);
tpm_tis_remove(chip);
-
- return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id tis_of_platform_match[] = {
+ {.compatible = "atmel,at97sc3204"},
{.compatible = "tcg,tpm-tis-mmio"},
{},
};
@@ -416,7 +429,7 @@ static void __exit cleanup_tis(void)
module_init(init_tis);
module_exit(cleanup_tis);
-MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_AUTHOR("Leendert van Doorn <leendert@watson.ibm.com>");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 757623bacfd5..e2a1769081b1 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -24,9 +24,12 @@
#include <linux/wait.h>
#include <linux/acpi.h>
#include <linux/freezer.h>
+#include <linux/dmi.h>
#include "tpm.h"
#include "tpm_tis_core.h"
+#define TPM_TIS_MAX_UNHANDLED_IRQS 1000
+
static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value);
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
@@ -44,6 +47,20 @@ static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
return false;
}
+static u8 tpm_tis_filter_sts_mask(u8 int_mask, u8 sts_mask)
+{
+ if (!(int_mask & TPM_INTF_STS_VALID_INT))
+ sts_mask &= ~TPM_STS_VALID;
+
+ if (!(int_mask & TPM_INTF_DATA_AVAIL_INT))
+ sts_mask &= ~TPM_STS_DATA_AVAIL;
+
+ if (!(int_mask & TPM_INTF_CMD_READY_INT))
+ sts_mask &= ~TPM_STS_COMMAND_READY;
+
+ return sts_mask;
+}
+
static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
unsigned long timeout, wait_queue_head_t *queue,
bool check_cancel)
@@ -53,41 +70,55 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
long rc;
u8 status;
bool canceled = false;
+ u8 sts_mask;
+ int ret = 0;
/* check current status */
status = chip->ops->status(chip);
if ((status & mask) == mask)
return 0;
- stop = jiffies + timeout;
+ sts_mask = mask & (TPM_STS_VALID | TPM_STS_DATA_AVAIL |
+ TPM_STS_COMMAND_READY);
+ /* check what status changes can be handled by irqs */
+ sts_mask = tpm_tis_filter_sts_mask(priv->int_mask, sts_mask);
- if (chip->flags & TPM_CHIP_FLAG_IRQ) {
+ stop = jiffies + timeout;
+ /* process status changes with irq support */
+ if (sts_mask) {
+ ret = -ETIME;
again:
timeout = stop - jiffies;
if ((long)timeout <= 0)
return -ETIME;
rc = wait_event_interruptible_timeout(*queue,
- wait_for_tpm_stat_cond(chip, mask, check_cancel,
+ wait_for_tpm_stat_cond(chip, sts_mask, check_cancel,
&canceled),
timeout);
if (rc > 0) {
if (canceled)
return -ECANCELED;
- return 0;
+ ret = 0;
}
if (rc == -ERESTARTSYS && freezing(current)) {
clear_thread_flag(TIF_SIGPENDING);
goto again;
}
- } else {
- do {
- usleep_range(priv->timeout_min,
- priv->timeout_max);
- status = chip->ops->status(chip);
- if ((status & mask) == mask)
- return 0;
- } while (time_before(jiffies, stop));
}
+
+ if (ret)
+ return ret;
+
+ mask &= ~sts_mask;
+ if (!mask) /* all done */
+ return 0;
+ /* process status changes without irq support */
+ do {
+ usleep_range(priv->timeout_min, priv->timeout_max);
+ status = chip->ops->status(chip);
+ if ((status & mask) == mask)
+ return 0;
+ } while (time_before(jiffies, stop));
return -ETIME;
}
@@ -136,16 +167,27 @@ static bool check_locality(struct tpm_chip *chip, int l)
return false;
}
-static int release_locality(struct tpm_chip *chip, int l)
+static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l)
+{
+ tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+
+ return 0;
+}
+
+static int tpm_tis_relinquish_locality(struct tpm_chip *chip, int l)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
- tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+ mutex_lock(&priv->locality_count_mutex);
+ priv->locality_count--;
+ if (priv->locality_count == 0)
+ __tpm_tis_relinquish_locality(priv, l);
+ mutex_unlock(&priv->locality_count_mutex);
return 0;
}
-static int request_locality(struct tpm_chip *chip, int l)
+static int __tpm_tis_request_locality(struct tpm_chip *chip, int l)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
unsigned long stop, timeout;
@@ -186,6 +228,20 @@ again:
return -1;
}
+static int tpm_tis_request_locality(struct tpm_chip *chip, int l)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int ret = 0;
+
+ mutex_lock(&priv->locality_count_mutex);
+ if (priv->locality_count == 0)
+ ret = __tpm_tis_request_locality(chip, l);
+ if (!ret)
+ priv->locality_count++;
+ mutex_unlock(&priv->locality_count_mutex);
+ return ret;
+}
+
static u8 tpm_tis_status(struct tpm_chip *chip)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
@@ -209,8 +265,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip)
/*
* Dump stack for forensics, as invalid TPM_STS.x could be
- * potentially triggered by impaired tpm_try_get_ops() or
- * tpm_find_get_ops().
+ * potentially triggered by impaired tpm_try_get_ops().
*/
dump_stack();
}
@@ -283,7 +338,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
return size;
}
-static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_tis_try_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int size = 0;
@@ -291,11 +346,6 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
u32 expected;
int rc;
- if (count < TPM_HEADER_SIZE) {
- size = -EIO;
- goto out;
- }
-
size = recv_data(chip, buf, TPM_HEADER_SIZE);
/* read first 10 bytes, including tag, paramsize, and result */
if (size < TPM_HEADER_SIZE) {
@@ -309,8 +359,13 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
goto out;
}
- size += recv_data(chip, &buf[TPM_HEADER_SIZE],
- expected - TPM_HEADER_SIZE);
+ rc = recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_SIZE);
+ if (rc < 0) {
+ size = rc;
+ goto out;
+ }
+ size += rc;
if (size < expected) {
dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
@@ -323,7 +378,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
goto out;
}
status = tpm_tis_status(chip);
- if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ if (status & TPM_STS_DATA_AVAIL) {
dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
@@ -337,10 +392,36 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
out:
- tpm_tis_ready(chip);
return size;
}
+static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ unsigned int try;
+ int rc = 0;
+
+ if (count < TPM_HEADER_SIZE)
+ return -EIO;
+
+ for (try = 0; try < TPM_RETRY; try++) {
+ rc = tpm_tis_try_recv(chip, buf, count);
+
+ if (rc == -EIO)
+ /* Data transfer errors, indicated by EIO, can be
+ * recovered by rereading the response.
+ */
+ tpm_tis_write8(priv, TPM_STS(priv->locality),
+ TPM_STS_RESPONSE_RETRY);
+ else
+ break;
+ }
+
+ tpm_tis_ready(chip);
+
+ return rc;
+}
+
/*
* If interrupts are used (signaled by an irq set in the vendor structure)
* tpm.c can skip polling for the data to be available as the interrupt is
@@ -351,7 +432,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int rc, status, burstcnt;
size_t count = 0;
- bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND;
+ bool itpm = test_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags);
status = tpm_tis_status(chip);
if ((status & TPM_STS_COMMAND_READY) == 0) {
@@ -381,7 +462,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
&priv->int_queue, false) < 0) {
- rc = -ETIME;
+ if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags))
+ rc = -EAGAIN;
+ else
+ rc = -ETIME;
goto out_err;
}
status = tpm_tis_status(chip);
@@ -398,7 +482,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
&priv->int_queue, false) < 0) {
- rc = -ETIME;
+ if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags))
+ rc = -EAGAIN;
+ else
+ rc = -ETIME;
goto out_err;
}
status = tpm_tis_status(chip);
@@ -407,6 +494,12 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
goto out_err;
}
+ rc = tpm_tis_verify_crc(priv, len, buf);
+ if (rc < 0) {
+ dev_err(&chip->dev, "CRC mismatch for command.\n");
+ goto out_err;
+ }
+
return 0;
out_err:
@@ -414,25 +507,29 @@ out_err:
return rc;
}
-static void disable_interrupts(struct tpm_chip *chip)
+static void __tpm_tis_disable_interrupts(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u32 int_mask = 0;
+
+ tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &int_mask);
+ int_mask &= ~TPM_GLOBAL_INT_ENABLE;
+ tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), int_mask);
+
+ chip->flags &= ~TPM_CHIP_FLAG_IRQ;
+}
+
+static void tpm_tis_disable_interrupts(struct tpm_chip *chip)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
- u32 intmask;
- int rc;
if (priv->irq == 0)
return;
- rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
- if (rc < 0)
- intmask = 0;
-
- intmask &= ~TPM_GLOBAL_INT_ENABLE;
- rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+ __tpm_tis_disable_interrupts(chip);
devm_free_irq(chip->dev.parent, priv->irq, chip);
priv->irq = 0;
- chip->flags &= ~TPM_CHIP_FLAG_IRQ;
}
/*
@@ -446,15 +543,18 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len)
int rc;
u32 ordinal;
unsigned long dur;
+ unsigned int try;
+
+ for (try = 0; try < TPM_RETRY; try++) {
+ rc = tpm_tis_send_data(chip, buf, len);
+ if (rc >= 0)
+ /* Data transfer done successfully */
+ break;
+ else if (rc != -EAGAIN && rc != -EIO)
+ /* Data transfer failed, not recoverable */
+ return rc;
- rc = tpm_tis_send_data(chip, buf, len);
- if (rc < 0)
- return rc;
-
- rc = tpm_tis_verify_crc(priv, len, buf);
- if (rc < 0) {
- dev_err(&chip->dev, "CRC mismatch for command.\n");
- return rc;
+ usleep_range(priv->timeout_min, priv->timeout_max);
}
/* go and do it */
@@ -479,12 +579,14 @@ out_err:
return rc;
}
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t len)
{
int rc, irq;
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
- if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested)
+ if (!(chip->flags & TPM_CHIP_FLAG_IRQ) ||
+ test_bit(TPM_TIS_IRQ_TESTED, &priv->flags))
return tpm_tis_send_main(chip, buf, len);
/* Verify receipt of the expected IRQ */
@@ -494,11 +596,11 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
rc = tpm_tis_send_main(chip, buf, len);
priv->irq = irq;
chip->flags |= TPM_CHIP_FLAG_IRQ;
- if (!priv->irq_tested)
+ if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags))
tpm_msleep(1);
- if (!priv->irq_tested)
- disable_interrupts(chip);
- priv->irq_tested = true;
+ if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags))
+ tpm_tis_disable_interrupts(chip);
+ set_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
return rc;
}
@@ -641,7 +743,7 @@ static int probe_itpm(struct tpm_chip *chip)
size_t len = sizeof(cmd_getticks);
u16 vendor;
- if (priv->flags & TPM_TIS_ITPM_WORKAROUND)
+ if (test_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags))
return 0;
rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor);
@@ -652,7 +754,7 @@ static int probe_itpm(struct tpm_chip *chip)
if (vendor != TPM_VID_INTEL)
return 0;
- if (request_locality(chip, 0) != 0)
+ if (tpm_tis_request_locality(chip, 0) != 0)
return -EBUSY;
rc = tpm_tis_send_data(chip, cmd_getticks, len);
@@ -661,19 +763,19 @@ static int probe_itpm(struct tpm_chip *chip)
tpm_tis_ready(chip);
- priv->flags |= TPM_TIS_ITPM_WORKAROUND;
+ set_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags);
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0)
dev_info(&chip->dev, "Detected an iTPM.\n");
else {
- priv->flags &= ~TPM_TIS_ITPM_WORKAROUND;
+ clear_bit(TPM_TIS_ITPM_WORKAROUND, &priv->flags);
rc = -EFAULT;
}
out:
tpm_tis_ready(chip);
- release_locality(chip, priv->locality);
+ tpm_tis_relinquish_locality(chip, priv->locality);
return rc;
}
@@ -682,15 +784,70 @@ static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
- switch (priv->manufacturer_id) {
- case TPM_VID_WINBOND:
- return ((status == TPM_STS_VALID) ||
- (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
- case TPM_VID_STM:
- return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
- default:
- return (status == TPM_STS_COMMAND_READY);
+ if (!test_bit(TPM_TIS_DEFAULT_CANCELLATION, &priv->flags)) {
+ switch (priv->manufacturer_id) {
+ case TPM_VID_WINBOND:
+ return ((status == TPM_STS_VALID) ||
+ (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
+ case TPM_VID_STM:
+ return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
+ default:
+ break;
+ }
}
+
+ return status == TPM_STS_COMMAND_READY;
+}
+
+static irqreturn_t tpm_tis_revert_interrupts(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ const char *product;
+ const char *vendor;
+
+ dev_warn(&chip->dev, FW_BUG
+ "TPM interrupt storm detected, polling instead\n");
+
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ product = dmi_get_system_info(DMI_PRODUCT_VERSION);
+
+ if (vendor && product) {
+ dev_info(&chip->dev,
+ "Consider adding the following entry to tpm_tis_dmi_table:\n");
+ dev_info(&chip->dev, "\tDMI_SYS_VENDOR: %s\n", vendor);
+ dev_info(&chip->dev, "\tDMI_PRODUCT_VERSION: %s\n", product);
+ }
+
+ if (tpm_tis_request_locality(chip, 0) != 0)
+ return IRQ_NONE;
+
+ __tpm_tis_disable_interrupts(chip);
+ tpm_tis_relinquish_locality(chip, 0);
+
+ schedule_work(&priv->free_irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tpm_tis_update_unhandled_irqs(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ irqreturn_t irqret = IRQ_HANDLED;
+
+ if (!(chip->flags & TPM_CHIP_FLAG_IRQ))
+ return IRQ_HANDLED;
+
+ if (time_after(jiffies, priv->last_unhandled_irq + HZ/10))
+ priv->unhandled_irqs = 1;
+ else
+ priv->unhandled_irqs++;
+
+ priv->last_unhandled_irq = jiffies;
+
+ if (priv->unhandled_irqs > TPM_TIS_MAX_UNHANDLED_IRQS)
+ irqret = tpm_tis_revert_interrupts(chip);
+
+ return irqret;
}
static irqreturn_t tis_int_handler(int dummy, void *dev_id)
@@ -698,55 +855,63 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
struct tpm_chip *chip = dev_id;
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
u32 interrupt;
- int i, rc;
+ int rc;
rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
if (rc < 0)
- return IRQ_NONE;
+ goto err;
if (interrupt == 0)
- return IRQ_NONE;
+ goto err;
- priv->irq_tested = true;
+ set_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
if (interrupt & TPM_INTF_DATA_AVAIL_INT)
wake_up_interruptible(&priv->read_queue);
- if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
- for (i = 0; i < 5; i++)
- if (check_locality(chip, i))
- break;
+
if (interrupt &
(TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
TPM_INTF_CMD_READY_INT))
wake_up_interruptible(&priv->int_queue);
/* Clear interrupts handled with TPM_EOI */
+ tpm_tis_request_locality(chip, 0);
rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt);
+ tpm_tis_relinquish_locality(chip, 0);
if (rc < 0)
- return IRQ_NONE;
+ goto err;
tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
return IRQ_HANDLED;
+
+err:
+ return tpm_tis_update_unhandled_irqs(chip);
}
-static int tpm_tis_gen_interrupt(struct tpm_chip *chip)
+static void tpm_tis_gen_interrupt(struct tpm_chip *chip)
{
const char *desc = "attempting to generate an interrupt";
u32 cap2;
cap_t cap;
int ret;
- ret = request_locality(chip, 0);
- if (ret < 0)
- return ret;
+ chip->flags |= TPM_CHIP_FLAG_IRQ;
if (chip->flags & TPM_CHIP_FLAG_TPM2)
ret = tpm2_get_tpm_pt(chip, 0x100, &cap2, desc);
else
ret = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, desc, 0);
- release_locality(chip, 0);
+ if (ret)
+ chip->flags &= ~TPM_CHIP_FLAG_IRQ;
+}
- return ret;
+static void tpm_tis_free_irq_func(struct work_struct *work)
+{
+ struct tpm_tis_data *priv = container_of(work, typeof(*priv), free_irq_work);
+ struct tpm_chip *chip = priv->chip;
+
+ devm_free_irq(chip->dev.parent, priv->irq, chip);
+ priv->irq = 0;
}
/* Register the IRQ and issue a command that will cause an interrupt. If an
@@ -761,60 +926,65 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
int rc;
u32 int_status;
- if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags,
- dev_name(&chip->dev), chip) != 0) {
+ rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL,
+ tis_int_handler, IRQF_ONESHOT | flags,
+ dev_name(&chip->dev), chip);
+ if (rc) {
dev_info(&chip->dev, "Unable to request irq: %d for probe\n",
irq);
return -1;
}
priv->irq = irq;
+ rc = tpm_tis_request_locality(chip, 0);
+ if (rc < 0)
+ return rc;
+
rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality),
&original_int_vec);
- if (rc < 0)
+ if (rc < 0) {
+ tpm_tis_relinquish_locality(chip, priv->locality);
return rc;
+ }
rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq);
if (rc < 0)
- return rc;
+ goto restore_irqs;
rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status);
if (rc < 0)
- return rc;
+ goto restore_irqs;
/* Clear all existing */
rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status);
if (rc < 0)
- return rc;
-
+ goto restore_irqs;
/* Turn on */
rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality),
intmask | TPM_GLOBAL_INT_ENABLE);
if (rc < 0)
- return rc;
+ goto restore_irqs;
- priv->irq_tested = false;
+ clear_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
/* Generate an interrupt by having the core call through to
* tpm_tis_send
*/
- rc = tpm_tis_gen_interrupt(chip);
- if (rc < 0)
- return rc;
+ tpm_tis_gen_interrupt(chip);
+restore_irqs:
/* tpm_tis_send will either confirm the interrupt is working or it
* will call disable_irq which undoes all of the above.
*/
if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) {
- rc = tpm_tis_write8(priv, original_int_vec,
- TPM_INT_VECTOR(priv->locality));
- if (rc < 0)
- return rc;
-
- return 1;
+ tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality),
+ original_int_vec);
+ rc = -1;
}
- return 0;
+ tpm_tis_relinquish_locality(chip, priv->locality);
+
+ return rc;
}
/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that
@@ -857,6 +1027,8 @@ void tpm_tis_remove(struct tpm_chip *chip)
interrupt = 0;
tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
+ if (priv->free_irq_work.func)
+ flush_work(&priv->free_irq_work);
tpm_tis_clkrun_enable(chip, false);
@@ -893,11 +1065,6 @@ static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value)
clkrun_val &= ~LPC_CLKRUN_EN;
iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
- /*
- * Write any random value on port 0x80 which is on LPC, to make
- * sure LPC clock is running before sending any TPM command.
- */
- outb(0xCC, 0x80);
} else {
data->clkrun_enabled--;
if (data->clkrun_enabled)
@@ -908,13 +1075,15 @@ static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value)
/* Enable LPC CLKRUN# */
clkrun_val |= LPC_CLKRUN_EN;
iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
-
- /*
- * Write any random value on port 0x80 which is on LPC, to make
- * sure LPC clock is running before sending any TPM command.
- */
- outb(0xCC, 0x80);
}
+
+#ifdef CONFIG_HAS_IOPORT
+ /*
+ * Write any random value on port 0x80 which is on LPC, to make
+ * sure LPC clock is running before sending any TPM command.
+ */
+ outb(0xCC, 0x80);
+#endif
}
static const struct tpm_class_ops tpm_tis = {
@@ -928,8 +1097,8 @@ static const struct tpm_class_ops tpm_tis = {
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_canceled = tpm_tis_req_canceled,
- .request_locality = request_locality,
- .relinquish_locality = release_locality,
+ .request_locality = tpm_tis_request_locality,
+ .relinquish_locality = tpm_tis_relinquish_locality,
.clk_enable = tpm_tis_clkrun_enable,
};
@@ -960,9 +1129,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
+ priv->chip = chip;
priv->timeout_min = TPM_TIMEOUT_USECS_MIN;
priv->timeout_max = TPM_TIMEOUT_USECS_MAX;
priv->phy_ops = phy_ops;
+ priv->locality_count = 0;
+ mutex_init(&priv->locality_count_mutex);
+ INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func);
dev_set_drvdata(&chip->dev, priv);
@@ -978,6 +1151,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
priv->timeout_max = TIS_TIMEOUT_MAX_ATML;
}
+ if (priv->manufacturer_id == TPM_VID_IFX)
+ set_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags);
+
if (is_bsw()) {
priv->ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR,
ILB_REMAP_SIZE);
@@ -1005,18 +1181,50 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
if (rc < 0)
goto out_err;
- intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
- TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
+ /* Figure out the capabilities */
+ rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps);
+ if (rc < 0)
+ goto out_err;
+
+ dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
+ intfcaps);
+ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+ dev_dbg(dev, "\tBurst Count Static\n");
+ if (intfcaps & TPM_INTF_CMD_READY_INT) {
+ intmask |= TPM_INTF_CMD_READY_INT;
+ dev_dbg(dev, "\tCommand Ready Int Support\n");
+ }
+ if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+ dev_dbg(dev, "\tInterrupt Edge Falling\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+ dev_dbg(dev, "\tInterrupt Edge Rising\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+ dev_dbg(dev, "\tInterrupt Level Low\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+ dev_dbg(dev, "\tInterrupt Level High\n");
+ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) {
+ intmask |= TPM_INTF_LOCALITY_CHANGE_INT;
+ dev_dbg(dev, "\tLocality Change Int Support\n");
+ }
+ if (intfcaps & TPM_INTF_STS_VALID_INT) {
+ intmask |= TPM_INTF_STS_VALID_INT;
+ dev_dbg(dev, "\tSts Valid Int Support\n");
+ }
+ if (intfcaps & TPM_INTF_DATA_AVAIL_INT) {
+ intmask |= TPM_INTF_DATA_AVAIL_INT;
+ dev_dbg(dev, "\tData Avail Int Support\n");
+ }
+
intmask &= ~TPM_GLOBAL_INT_ENABLE;
- rc = request_locality(chip, 0);
+ rc = tpm_tis_request_locality(chip, 0);
if (rc < 0) {
rc = -ENODEV;
goto out_err;
}
tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
- release_locality(chip, 0);
+ tpm_tis_relinquish_locality(chip, 0);
rc = tpm_chip_start(chip);
if (rc)
@@ -1040,35 +1248,14 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
goto out_err;
}
- /* Figure out the capabilities */
- rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps);
- if (rc < 0)
- goto out_err;
-
- dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
- intfcaps);
- if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
- dev_dbg(dev, "\tBurst Count Static\n");
- if (intfcaps & TPM_INTF_CMD_READY_INT)
- dev_dbg(dev, "\tCommand Ready Int Support\n");
- if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
- dev_dbg(dev, "\tInterrupt Edge Falling\n");
- if (intfcaps & TPM_INTF_INT_EDGE_RISING)
- dev_dbg(dev, "\tInterrupt Edge Rising\n");
- if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
- dev_dbg(dev, "\tInterrupt Level Low\n");
- if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
- dev_dbg(dev, "\tInterrupt Level High\n");
- if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
- dev_dbg(dev, "\tLocality Change Int Support\n");
- if (intfcaps & TPM_INTF_STS_VALID_INT)
- dev_dbg(dev, "\tSts Valid Int Support\n");
- if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
- dev_dbg(dev, "\tData Avail Int Support\n");
-
/* INTERRUPT Setup */
init_waitqueue_head(&priv->read_queue);
init_waitqueue_head(&priv->int_queue);
+
+ rc = tpm_chip_bootstrap(chip);
+ if (rc)
+ goto out_err;
+
if (irq != -1) {
/*
* Before doing irq testing issue a command to the TPM in polling mode
@@ -1076,13 +1263,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
* proper timeouts for the driver.
*/
- rc = request_locality(chip, 0);
+ rc = tpm_tis_request_locality(chip, 0);
if (rc < 0)
goto out_err;
rc = tpm_get_timeouts(chip);
- release_locality(chip, 0);
+ tpm_tis_relinquish_locality(chip, 0);
if (rc) {
dev_err(dev, "Could not get TPM timeouts and durations\n");
@@ -1090,17 +1277,23 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
goto out_err;
}
- if (irq) {
+ if (irq)
tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
irq);
- if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) {
- dev_err(&chip->dev, FW_BUG
- "TPM interrupt not working, polling instead\n");
+ else
+ tpm_tis_probe_irq(chip, intmask);
- disable_interrupts(chip);
- }
+ if (chip->flags & TPM_CHIP_FLAG_IRQ) {
+ priv->int_mask = intmask;
} else {
- tpm_tis_probe_irq(chip, intmask);
+ dev_err(&chip->dev, FW_BUG
+ "TPM interrupt not working, polling instead\n");
+
+ rc = tpm_tis_request_locality(chip, 0);
+ if (rc < 0)
+ goto out_err;
+ tpm_tis_disable_interrupts(chip);
+ tpm_tis_relinquish_locality(chip, 0);
}
}
@@ -1129,31 +1322,20 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
u32 intmask;
int rc;
- if (chip->ops->clk_enable != NULL)
- chip->ops->clk_enable(chip, true);
-
- /* reenable interrupts that device may have lost or
- * BIOS/firmware may have disabled
+ /*
+ * Re-enable interrupts that device may have lost or BIOS/firmware may
+ * have disabled.
*/
rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq);
- if (rc < 0)
- goto out;
+ if (rc < 0) {
+ dev_err(&chip->dev, "Setting IRQ failed.\n");
+ return;
+ }
- rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+ intmask = priv->int_mask | TPM_GLOBAL_INT_ENABLE;
+ rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
if (rc < 0)
- goto out;
-
- intmask |= TPM_INTF_CMD_READY_INT
- | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
- | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
-
- tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
-
-out:
- if (chip->ops->clk_enable != NULL)
- chip->ops->clk_enable(chip, false);
-
- return;
+ dev_err(&chip->dev, "Enabling interrupts failed.\n");
}
int tpm_tis_resume(struct device *dev)
@@ -1161,33 +1343,32 @@ int tpm_tis_resume(struct device *dev)
struct tpm_chip *chip = dev_get_drvdata(dev);
int ret;
- if (chip->flags & TPM_CHIP_FLAG_IRQ)
- tpm_tis_reenable_interrupts(chip);
-
- ret = tpm_pm_resume(dev);
+ ret = tpm_chip_start(chip);
if (ret)
return ret;
+ if (chip->flags & TPM_CHIP_FLAG_IRQ)
+ tpm_tis_reenable_interrupts(chip);
+
/*
* TPM 1.2 requires self-test on resume. This function actually returns
* an error code but for unknown reason it isn't handled.
*/
- if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
- ret = request_locality(chip, 0);
- if (ret < 0)
- return ret;
-
+ if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
tpm1_do_selftest(chip);
- release_locality(chip, 0);
- }
+ tpm_chip_stop(chip);
+
+ ret = tpm_pm_resume(dev);
+ if (ret)
+ return ret;
return 0;
}
EXPORT_SYMBOL_GPL(tpm_tis_resume);
#endif
-MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_AUTHOR("Leendert van Doorn <leendert@watson.ibm.com>");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index 66a5a13cd1df..6c3aa480396b 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -34,6 +34,7 @@ enum tis_status {
TPM_STS_GO = 0x20,
TPM_STS_DATA_AVAIL = 0x10,
TPM_STS_DATA_EXPECT = 0x08,
+ TPM_STS_RESPONSE_RETRY = 0x02,
TPM_STS_READ_ZERO = 0x23, /* bits that must be zero on read */
};
@@ -53,7 +54,7 @@ enum tis_int_flags {
enum tis_defaults {
TIS_MEM_LEN = 0x5000,
TIS_SHORT_TIMEOUT = 750, /* ms */
- TIS_LONG_TIMEOUT = 2000, /* 2 sec */
+ TIS_LONG_TIMEOUT = 4000, /* 4 secs */
TIS_TIMEOUT_MIN_ATML = 14700, /* usecs */
TIS_TIMEOUT_MAX_ATML = 15000, /* usecs */
};
@@ -84,15 +85,24 @@ enum tis_defaults {
#define ILB_REMAP_SIZE 0x100
enum tpm_tis_flags {
- TPM_TIS_ITPM_WORKAROUND = BIT(0),
- TPM_TIS_INVALID_STATUS = BIT(1),
+ TPM_TIS_ITPM_WORKAROUND = 0,
+ TPM_TIS_INVALID_STATUS = 1,
+ TPM_TIS_DEFAULT_CANCELLATION = 2,
+ TPM_TIS_IRQ_TESTED = 3,
+ TPM_TIS_STATUS_VALID_RETRY = 4,
};
struct tpm_tis_data {
+ struct tpm_chip *chip;
u16 manufacturer_id;
+ struct mutex locality_count_mutex;
+ unsigned int locality_count;
int locality;
int irq;
- bool irq_tested;
+ struct work_struct free_irq_work;
+ unsigned long last_unhandled_irq;
+ unsigned int unhandled_irqs;
+ unsigned int int_mask;
unsigned long flags;
void __iomem *ilb_base_addr;
u16 clkrun_enabled;
@@ -201,7 +211,7 @@ static inline int tpm_tis_verify_crc(struct tpm_tis_data *data, size_t len,
static inline bool is_bsw(void)
{
#ifdef CONFIG_X86
- return ((boot_cpu_data.x86_model == INTEL_FAM6_ATOM_AIRMONT) ? 1 : 0);
+ return (boot_cpu_data.x86_vfm == INTEL_ATOM_AIRMONT) ? 1 : 0;
#else
return false;
#endif
diff --git a/drivers/char/tpm/tpm_tis_i2c.c b/drivers/char/tpm/tpm_tis_i2c.c
index ba0911b1d1ff..6cd07dd34507 100644
--- a/drivers/char/tpm/tpm_tis_i2c.c
+++ b/drivers/char/tpm/tpm_tis_i2c.c
@@ -49,7 +49,7 @@
/* Masks with bits that must be read zero */
#define TPM_ACCESS_READ_ZERO 0x48
-#define TPM_INT_ENABLE_ZERO 0x7FFFFF6
+#define TPM_INT_ENABLE_ZERO 0x7FFFFF60
#define TPM_STS_READ_ZERO 0x23
#define TPM_INTF_CAPABILITY_ZERO 0x0FFFF000
#define TPM_I2C_INTERFACE_CAPABILITY_ZERO 0x80000000
@@ -189,21 +189,28 @@ static int tpm_tis_i2c_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
int ret;
for (i = 0; i < TPM_RETRY; i++) {
- /* write register */
- msg.len = sizeof(reg);
- msg.buf = &reg;
- msg.flags = 0;
- ret = tpm_tis_i2c_retry_transfer_until_ack(data, &msg);
- if (ret < 0)
- return ret;
-
- /* read data */
- msg.buf = result;
- msg.len = len;
- msg.flags = I2C_M_RD;
- ret = tpm_tis_i2c_retry_transfer_until_ack(data, &msg);
- if (ret < 0)
- return ret;
+ u16 read = 0;
+
+ while (read < len) {
+ /* write register */
+ msg.len = sizeof(reg);
+ msg.buf = &reg;
+ msg.flags = 0;
+ ret = tpm_tis_i2c_retry_transfer_until_ack(data, &msg);
+ if (ret < 0)
+ return ret;
+
+ /* read data */
+ msg.buf = result + read;
+ msg.len = len - read;
+ msg.flags = I2C_M_RD;
+ if (msg.len > I2C_SMBUS_BLOCK_MAX)
+ msg.len = I2C_SMBUS_BLOCK_MAX;
+ ret = tpm_tis_i2c_retry_transfer_until_ack(data, &msg);
+ if (ret < 0)
+ return ret;
+ read += msg.len;
+ }
ret = tpm_tis_i2c_sanity_check_read(reg, len, result);
if (ret == 0)
@@ -223,19 +230,27 @@ static int tpm_tis_i2c_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
struct i2c_msg msg = { .addr = phy->i2c_client->addr };
u8 reg = tpm_tis_i2c_address_to_register(addr);
int ret;
+ u16 wrote = 0;
if (len > TPM_BUFSIZE - 1)
return -EIO;
- /* write register and data in one go */
phy->io_buf[0] = reg;
- memcpy(phy->io_buf + sizeof(reg), value, len);
-
- msg.len = sizeof(reg) + len;
msg.buf = phy->io_buf;
- ret = tpm_tis_i2c_retry_transfer_until_ack(data, &msg);
- if (ret < 0)
- return ret;
+ while (wrote < len) {
+ /* write register and data in one go */
+ msg.len = sizeof(reg) + len - wrote;
+ if (msg.len > I2C_SMBUS_BLOCK_MAX)
+ msg.len = I2C_SMBUS_BLOCK_MAX;
+
+ memcpy(phy->io_buf + sizeof(reg), value + wrote,
+ msg.len - sizeof(reg));
+
+ ret = tpm_tis_i2c_retry_transfer_until_ack(data, &msg);
+ if (ret < 0)
+ return ret;
+ wrote += msg.len - sizeof(reg);
+ }
return 0;
}
@@ -312,8 +327,7 @@ static const struct tpm_tis_phy_ops tpm_i2c_phy_ops = {
.verify_crc = tpm_tis_i2c_verify_crc,
};
-static int tpm_tis_i2c_probe(struct i2c_client *dev,
- const struct i2c_device_id *id)
+static int tpm_tis_i2c_probe(struct i2c_client *dev)
{
struct tpm_tis_i2c_phy *phy;
const u8 crc_enable = 1;
@@ -329,6 +343,7 @@ static int tpm_tis_i2c_probe(struct i2c_client *dev,
if (!phy->io_buf)
return -ENOMEM;
+ set_bit(TPM_TIS_DEFAULT_CANCELLATION, &phy->priv.flags);
phy->i2c_client = dev;
/* must precede all communication with the tpm */
@@ -351,17 +366,16 @@ static int tpm_tis_i2c_probe(struct i2c_client *dev,
NULL);
}
-static int tpm_tis_i2c_remove(struct i2c_client *client)
+static void tpm_tis_i2c_remove(struct i2c_client *client)
{
struct tpm_chip *chip = i2c_get_clientdata(client);
tpm_chip_unregister(chip);
tpm_tis_remove(chip);
- return 0;
}
static const struct i2c_device_id tpm_tis_i2c_id[] = {
- { "tpm_tis_i2c", 0 },
+ { "tpm_tis_i2c" },
{}
};
MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_id);
@@ -369,6 +383,8 @@ MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_id);
#ifdef CONFIG_OF
static const struct of_device_id of_tis_i2c_match[] = {
{ .compatible = "infineon,slb9673", },
+ { .compatible = "nuvoton,npct75x", },
+ { .compatible = "tcg,tpm-tis-i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_tis_i2c_match);
diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c
index 974479a1ec5a..fc6891a0b693 100644
--- a/drivers/char/tpm/tpm_tis_i2c_cr50.c
+++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c
@@ -17,6 +17,7 @@
*/
#include <linux/acpi.h>
+#include <linux/bug.h>
#include <linux/completion.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -30,11 +31,13 @@
#define TPM_CR50_MAX_BUFSIZE 64
#define TPM_CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */
#define TPM_CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */
-#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID reg value */
-#define TPM_TI50_I2C_DID_VID 0x504a6666L /* Device and vendor ID reg value */
+#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID for Cr50 H1 */
+#define TPM_TI50_DT_I2C_DID_VID 0x504a6666L /* Device and vendor ID for Ti50 DT */
+#define TPM_TI50_OT_I2C_DID_VID 0x50666666L /* Device and vendor ID for TI50 OT */
#define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */
#define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */
#define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */
+#define TPM_CR50_I2C_DEFAULT_LOC 0
#define TPM_I2C_ACCESS(l) (0x0000 | ((l) << 4))
#define TPM_I2C_STS(l) (0x0001 | ((l) << 4))
@@ -100,8 +103,7 @@ static int tpm_cr50_i2c_wait_tpm_ready(struct tpm_chip *chip)
}
/* Wait for interrupt to indicate TPM is ready to respond */
- if (!wait_for_completion_timeout(&priv->tpm_ready,
- msecs_to_jiffies(chip->timeout_a))) {
+ if (!wait_for_completion_timeout(&priv->tpm_ready, chip->timeout_a)) {
dev_warn(&chip->dev, "Timeout waiting for TPM ready\n");
return -ETIMEDOUT;
}
@@ -200,8 +202,6 @@ static int tpm_cr50_i2c_read(struct tpm_chip *chip, u8 addr, u8 *buffer, size_t
};
int rc;
- i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
-
/* Prepare for completion interrupt */
tpm_cr50_i2c_enable_tpm_irq(chip);
@@ -220,7 +220,6 @@ static int tpm_cr50_i2c_read(struct tpm_chip *chip, u8 addr, u8 *buffer, size_t
out:
tpm_cr50_i2c_disable_tpm_irq(chip);
- i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
if (rc < 0)
return rc;
@@ -236,7 +235,7 @@ out:
* @len: Number of bytes to write.
*
* The provided address is prepended to the data in 'buffer', the
- * cobined address+data is sent to the TPM, then wait for TPM to
+ * combined address+data is sent to the TPM, then wait for TPM to
* indicate it is done writing.
*
* Return:
@@ -262,8 +261,6 @@ static int tpm_cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer,
priv->buf[0] = addr;
memcpy(priv->buf + 1, buffer, len);
- i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
-
/* Prepare for completion interrupt */
tpm_cr50_i2c_enable_tpm_irq(chip);
@@ -277,7 +274,6 @@ static int tpm_cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer,
out:
tpm_cr50_i2c_disable_tpm_irq(chip);
- i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
if (rc < 0)
return rc;
@@ -286,25 +282,26 @@ out:
}
/**
- * tpm_cr50_check_locality() - Verify TPM locality 0 is active.
+ * tpm_cr50_check_locality() - Verify if required TPM locality is active.
* @chip: A TPM chip.
+ * @loc: Locality to be verified
*
* Return:
- * - 0: Success.
+ * - loc: Success.
* - -errno: A POSIX error code.
*/
-static int tpm_cr50_check_locality(struct tpm_chip *chip)
+static int tpm_cr50_check_locality(struct tpm_chip *chip, int loc)
{
u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
u8 buf;
int rc;
- rc = tpm_cr50_i2c_read(chip, TPM_I2C_ACCESS(0), &buf, sizeof(buf));
+ rc = tpm_cr50_i2c_read(chip, TPM_I2C_ACCESS(loc), &buf, sizeof(buf));
if (rc < 0)
return rc;
if ((buf & mask) == mask)
- return 0;
+ return loc;
return -EIO;
}
@@ -312,53 +309,72 @@ static int tpm_cr50_check_locality(struct tpm_chip *chip)
/**
* tpm_cr50_release_locality() - Release TPM locality.
* @chip: A TPM chip.
- * @force: Flag to force release if set.
+ * @loc: Locality to be released
+ *
+ * Return:
+ * - 0: Success.
+ * - -errno: A POSIX error code.
*/
-static void tpm_cr50_release_locality(struct tpm_chip *chip, bool force)
+static int tpm_cr50_release_locality(struct tpm_chip *chip, int loc)
{
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING;
- u8 addr = TPM_I2C_ACCESS(0);
+ u8 addr = TPM_I2C_ACCESS(loc);
u8 buf;
+ int rc;
- if (tpm_cr50_i2c_read(chip, addr, &buf, sizeof(buf)) < 0)
- return;
+ rc = tpm_cr50_i2c_read(chip, addr, &buf, sizeof(buf));
+ if (rc < 0)
+ goto unlock_out;
- if (force || (buf & mask) == mask) {
+ if ((buf & mask) == mask) {
buf = TPM_ACCESS_ACTIVE_LOCALITY;
- tpm_cr50_i2c_write(chip, addr, &buf, sizeof(buf));
+ rc = tpm_cr50_i2c_write(chip, addr, &buf, sizeof(buf));
}
+
+unlock_out:
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ return rc;
}
/**
- * tpm_cr50_request_locality() - Request TPM locality 0.
+ * tpm_cr50_request_locality() - Request TPM locality.
* @chip: A TPM chip.
+ * @loc: Locality to be requested.
*
* Return:
- * - 0: Success.
+ * - loc: Success.
* - -errno: A POSIX error code.
*/
-static int tpm_cr50_request_locality(struct tpm_chip *chip)
+static int tpm_cr50_request_locality(struct tpm_chip *chip, int loc)
{
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
u8 buf = TPM_ACCESS_REQUEST_USE;
unsigned long stop;
int rc;
- if (!tpm_cr50_check_locality(chip))
- return 0;
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+ if (tpm_cr50_check_locality(chip, loc) == loc)
+ return loc;
- rc = tpm_cr50_i2c_write(chip, TPM_I2C_ACCESS(0), &buf, sizeof(buf));
+ rc = tpm_cr50_i2c_write(chip, TPM_I2C_ACCESS(loc), &buf, sizeof(buf));
if (rc < 0)
- return rc;
+ goto unlock_out;
stop = jiffies + chip->timeout_a;
do {
- if (!tpm_cr50_check_locality(chip))
- return 0;
+ if (tpm_cr50_check_locality(chip, loc) == loc)
+ return loc;
msleep(TPM_CR50_TIMEOUT_SHORT_MS);
} while (time_before(jiffies, stop));
- return -ETIMEDOUT;
+ rc = -ETIMEDOUT;
+
+unlock_out:
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ return rc;
}
/**
@@ -374,7 +390,7 @@ static u8 tpm_cr50_i2c_tis_status(struct tpm_chip *chip)
{
u8 buf[4];
- if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(0), buf, sizeof(buf)) < 0)
+ if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)) < 0)
return 0;
return buf[0];
@@ -390,7 +406,7 @@ static void tpm_cr50_i2c_tis_set_ready(struct tpm_chip *chip)
{
u8 buf[4] = { TPM_STS_COMMAND_READY };
- tpm_cr50_i2c_write(chip, TPM_I2C_STS(0), buf, sizeof(buf));
+ tpm_cr50_i2c_write(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf));
msleep(TPM_CR50_TIMEOUT_SHORT_MS);
}
@@ -420,7 +436,7 @@ static int tpm_cr50_i2c_get_burst_and_status(struct tpm_chip *chip, u8 mask,
stop = jiffies + chip->timeout_b;
do {
- if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(0), buf, sizeof(buf)) < 0) {
+ if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)) < 0) {
msleep(TPM_CR50_TIMEOUT_SHORT_MS);
continue;
}
@@ -454,7 +470,7 @@ static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len)
u8 mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL;
size_t burstcnt, cur, len, expected;
- u8 addr = TPM_I2C_DATA_FIFO(0);
+ u8 addr = TPM_I2C_DATA_FIFO(chip->locality);
u32 status;
int rc;
@@ -516,7 +532,6 @@ static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len)
goto out_err;
}
- tpm_cr50_release_locality(chip, false);
return cur;
out_err:
@@ -524,7 +539,6 @@ out_err:
if (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)
tpm_cr50_i2c_tis_set_ready(chip);
- tpm_cr50_release_locality(chip, false);
return rc;
}
@@ -532,13 +546,15 @@ out_err:
* tpm_cr50_i2c_tis_send() - TPM transmission callback.
* @chip: A TPM chip.
* @buf: Buffer to send.
- * @len: Buffer length.
+ * @bufsiz: Buffer size.
+ * @len: Command length.
*
* Return:
* - 0: Success.
* - -errno: A POSIX error code.
*/
-static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t len)
{
size_t burstcnt, limit, sent = 0;
u8 tpm_go[4] = { TPM_STS_GO };
@@ -546,10 +562,6 @@ static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
u32 status;
int rc;
- rc = tpm_cr50_request_locality(chip);
- if (rc < 0)
- return rc;
-
/* Wait until TPM is ready for a command */
stop = jiffies + chip->timeout_b;
while (!(tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)) {
@@ -578,7 +590,8 @@ static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
* that is inserted by tpm_cr50_i2c_write()
*/
limit = min_t(size_t, burstcnt - 1, len);
- rc = tpm_cr50_i2c_write(chip, TPM_I2C_DATA_FIFO(0), &buf[sent], limit);
+ rc = tpm_cr50_i2c_write(chip, TPM_I2C_DATA_FIFO(chip->locality),
+ &buf[sent], limit);
if (rc < 0) {
dev_err(&chip->dev, "Write failed\n");
goto out_err;
@@ -599,7 +612,7 @@ static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
}
/* Start the TPM command */
- rc = tpm_cr50_i2c_write(chip, TPM_I2C_STS(0), tpm_go,
+ rc = tpm_cr50_i2c_write(chip, TPM_I2C_STS(chip->locality), tpm_go,
sizeof(tpm_go));
if (rc < 0) {
dev_err(&chip->dev, "Start command failed\n");
@@ -612,7 +625,6 @@ out_err:
if (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)
tpm_cr50_i2c_tis_set_ready(chip);
- tpm_cr50_release_locality(chip, false);
return rc;
}
@@ -651,6 +663,8 @@ static const struct tpm_class_ops cr50_i2c = {
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_canceled = &tpm_cr50_i2c_req_canceled,
+ .request_locality = &tpm_cr50_request_locality,
+ .relinquish_locality = &tpm_cr50_release_locality,
};
#ifdef CONFIG_ACPI
@@ -670,9 +684,29 @@ MODULE_DEVICE_TABLE(of, of_cr50_i2c_match);
#endif
/**
+ * tpm_cr50_vid_to_name() - Maps VID to name.
+ * @vendor: Vendor identifier to map to name
+ *
+ * Return:
+ * A valid string for the vendor or empty string
+ */
+static const char *tpm_cr50_vid_to_name(u32 vendor)
+{
+ switch (vendor) {
+ case TPM_CR50_I2C_DID_VID:
+ return "cr50";
+ case TPM_TI50_DT_I2C_DID_VID:
+ return "ti50 DT";
+ case TPM_TI50_OT_I2C_DID_VID:
+ return "ti50 OT";
+ default:
+ return "unknown";
+ }
+}
+
+/**
* tpm_cr50_i2c_probe() - Driver probe function.
* @client: I2C client information.
- * @id: I2C device id.
*
* Return:
* - 0: Success.
@@ -686,6 +720,7 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
u32 vendor;
u8 buf[4];
int rc;
+ int loc;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
@@ -728,29 +763,37 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
TPM_CR50_TIMEOUT_NOIRQ_MS);
}
- rc = tpm_cr50_request_locality(chip);
- if (rc < 0) {
+ loc = tpm_cr50_request_locality(chip, TPM_CR50_I2C_DEFAULT_LOC);
+ if (loc < 0) {
dev_err(dev, "Could not request locality\n");
- return rc;
+ return loc;
}
/* Read four bytes from DID_VID register */
- rc = tpm_cr50_i2c_read(chip, TPM_I2C_DID_VID(0), buf, sizeof(buf));
+ rc = tpm_cr50_i2c_read(chip, TPM_I2C_DID_VID(loc), buf, sizeof(buf));
if (rc < 0) {
dev_err(dev, "Could not read vendor id\n");
- tpm_cr50_release_locality(chip, true);
+ if (tpm_cr50_release_locality(chip, loc))
+ dev_err(dev, "Could not release locality\n");
+ return rc;
+ }
+
+ rc = tpm_cr50_release_locality(chip, loc);
+ if (rc) {
+ dev_err(dev, "Could not release locality\n");
return rc;
}
vendor = le32_to_cpup((__le32 *)buf);
- if (vendor != TPM_CR50_I2C_DID_VID && vendor != TPM_TI50_I2C_DID_VID) {
+ if (vendor != TPM_CR50_I2C_DID_VID &&
+ vendor != TPM_TI50_DT_I2C_DID_VID &&
+ vendor != TPM_TI50_OT_I2C_DID_VID) {
dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor);
- tpm_cr50_release_locality(chip, true);
return -ENODEV;
}
dev_info(dev, "%s TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n",
- vendor == TPM_TI50_I2C_DID_VID ? "ti50" : "cr50",
+ tpm_cr50_vid_to_name(vendor),
client->addr, client->irq, vendor >> 16);
return tpm_chip_register(chip);
}
@@ -763,26 +806,23 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
* - 0: Success.
* - -errno: A POSIX error code.
*/
-static int tpm_cr50_i2c_remove(struct i2c_client *client)
+static void tpm_cr50_i2c_remove(struct i2c_client *client)
{
struct tpm_chip *chip = i2c_get_clientdata(client);
struct device *dev = &client->dev;
if (!chip) {
dev_crit(dev, "Could not get client data at remove, memory corruption ahead\n");
- return 0;
+ return;
}
tpm_chip_unregister(chip);
- tpm_cr50_release_locality(chip, true);
-
- return 0;
}
static SIMPLE_DEV_PM_OPS(cr50_i2c_pm, tpm_pm_suspend, tpm_pm_resume);
static struct i2c_driver cr50_i2c_driver = {
- .probe_new = tpm_cr50_i2c_probe,
+ .probe = tpm_cr50_i2c_probe,
.remove = tpm_cr50_i2c_remove,
.driver = {
.name = "cr50_i2c",
diff --git a/drivers/char/tpm/tpm_tis_spi_main.c b/drivers/char/tpm/tpm_tis_spi_main.c
index a0963a3e92bd..61b42c83ced8 100644
--- a/drivers/char/tpm/tpm_tis_spi_main.c
+++ b/drivers/char/tpm/tpm_tis_spi_main.c
@@ -28,7 +28,7 @@
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/tpm.h>
@@ -37,6 +37,7 @@
#include "tpm_tis_spi.h"
#define MAX_SPI_FRAMESIZE 64
+#define SPI_HDRSIZE 4
/*
* TCG SPI flow control is documented in section 6.4 of the spec[1]. In short,
@@ -71,8 +72,74 @@ static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy,
return 0;
}
-int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *in, const u8 *out)
+/*
+ * Half duplex controller with support for TPM wait state detection like
+ * Tegra QSPI need CMD, ADDR & DATA sent in single message to manage HW flow
+ * control. Each phase sent in different transfer for controller to idenity
+ * phase.
+ */
+static int tpm_tis_spi_transfer_half(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *in, const u8 *out)
+{
+ struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+ struct spi_transfer spi_xfer[3];
+ struct spi_message m;
+ u8 transfer_len;
+ int ret;
+
+ while (len) {
+ transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
+
+ spi_message_init(&m);
+ phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
+ phy->iobuf[1] = 0xd4;
+ phy->iobuf[2] = addr >> 8;
+ phy->iobuf[3] = addr;
+
+ memset(&spi_xfer, 0, sizeof(spi_xfer));
+
+ spi_xfer[0].tx_buf = phy->iobuf;
+ spi_xfer[0].len = 1;
+ spi_message_add_tail(&spi_xfer[0], &m);
+
+ spi_xfer[1].tx_buf = phy->iobuf + 1;
+ spi_xfer[1].len = 3;
+ spi_message_add_tail(&spi_xfer[1], &m);
+
+ if (out) {
+ spi_xfer[2].tx_buf = &phy->iobuf[4];
+ spi_xfer[2].rx_buf = NULL;
+ memcpy(&phy->iobuf[4], out, transfer_len);
+ out += transfer_len;
+ }
+
+ if (in) {
+ spi_xfer[2].tx_buf = NULL;
+ spi_xfer[2].rx_buf = &phy->iobuf[4];
+ }
+
+ spi_xfer[2].len = transfer_len;
+ spi_message_add_tail(&spi_xfer[2], &m);
+
+ reinit_completion(&phy->ready);
+
+ ret = spi_sync(phy->spi_device, &m);
+ if (ret < 0)
+ return ret;
+
+ if (in) {
+ memcpy(in, &phy->iobuf[4], transfer_len);
+ in += transfer_len;
+ }
+
+ len -= transfer_len;
+ }
+
+ return ret;
+}
+
+static int tpm_tis_spi_transfer_full(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *in, const u8 *out)
{
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
int ret = 0;
@@ -80,7 +147,7 @@ int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
struct spi_transfer spi_xfer;
u8 transfer_len;
- spi_bus_lock(phy->spi_device->master);
+ spi_bus_lock(phy->spi_device->controller);
while (len) {
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
@@ -136,10 +203,36 @@ int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
}
exit:
- spi_bus_unlock(phy->spi_device->master);
+ if (ret < 0) {
+ /* Deactivate chip select */
+ memset(&spi_xfer, 0, sizeof(spi_xfer));
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ spi_sync_locked(phy->spi_device, &m);
+ }
+
+ spi_bus_unlock(phy->spi_device->controller);
return ret;
}
+int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *in, const u8 *out)
+{
+ struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+ struct spi_controller *ctlr = phy->spi_device->controller;
+
+ /*
+ * TPM flow control over SPI requires full duplex support.
+ * Send entire message to a half duplex controller to handle
+ * wait polling in controller.
+ * Set TPM HW flow control flag..
+ */
+ if (ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX)
+ return tpm_tis_spi_transfer_half(data, addr, len, in, out);
+ else
+ return tpm_tis_spi_transfer_full(data, addr, len, in, out);
+}
+
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
{
@@ -155,7 +248,7 @@ static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
int irq, const struct tpm_tis_phy_ops *phy_ops)
{
- phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
+ phy->iobuf = devm_kmalloc(&spi->dev, SPI_HDRSIZE + MAX_SPI_FRAMESIZE, GFP_KERNEL);
if (!phy->iobuf)
return -ENOMEM;
@@ -181,6 +274,9 @@ static int tpm_tis_spi_probe(struct spi_device *dev)
phy->flow_control = tpm_tis_spi_flow_control;
+ if (dev->controller->flags & SPI_CONTROLLER_HALF_DUPLEX)
+ dev->mode |= SPI_TPM_HW_FLOW;
+
/* If the SPI device has an IRQ then use that */
if (dev->irq > 0)
irq = dev->irq;
@@ -222,6 +318,7 @@ static void tpm_tis_spi_remove(struct spi_device *dev)
}
static const struct spi_device_id tpm_tis_spi_id[] = {
+ { "attpm20p", (unsigned long)tpm_tis_spi_probe },
{ "st33htpm-spi", (unsigned long)tpm_tis_spi_probe },
{ "slb9670", (unsigned long)tpm_tis_spi_probe },
{ "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe },
@@ -231,7 +328,8 @@ static const struct spi_device_id tpm_tis_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);
-static const struct of_device_id of_tis_spi_match[] = {
+static const struct of_device_id of_tis_spi_match[] __maybe_unused = {
+ { .compatible = "atmel,attpm20p", .data = tpm_tis_spi_probe },
{ .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe },
{ .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe },
{ .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe },
@@ -240,7 +338,7 @@ static const struct of_device_id of_tis_spi_match[] = {
};
MODULE_DEVICE_TABLE(of, of_tis_spi_match);
-static const struct acpi_device_id acpi_tis_spi_match[] = {
+static const struct acpi_device_id acpi_tis_spi_match[] __maybe_unused = {
{"SMO0768", 0},
{}
};
diff --git a/drivers/char/tpm/tpm_tis_synquacer.c b/drivers/char/tpm/tpm_tis_synquacer.c
index 679196c61401..4927714d277a 100644
--- a/drivers/char/tpm/tpm_tis_synquacer.c
+++ b/drivers/char/tpm/tpm_tis_synquacer.c
@@ -9,7 +9,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/kernel.h>
#include "tpm.h"
#include "tpm_tis_core.h"
@@ -127,14 +126,12 @@ static int tpm_tis_synquacer_probe(struct platform_device *pdev)
return tpm_tis_synquacer_init(&pdev->dev, &tpm_info);
}
-static int tpm_tis_synquacer_remove(struct platform_device *pdev)
+static void tpm_tis_synquacer_remove(struct platform_device *pdev)
{
struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
tpm_chip_unregister(chip);
tpm_tis_remove(chip);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -164,23 +161,7 @@ static struct platform_driver tis_synquacer_drv = {
},
};
-static int __init tpm_tis_synquacer_module_init(void)
-{
- int rc;
-
- rc = platform_driver_register(&tis_synquacer_drv);
- if (rc)
- return rc;
-
- return 0;
-}
-
-static void __exit tpm_tis_synquacer_module_exit(void)
-{
- platform_driver_unregister(&tis_synquacer_drv);
-}
+module_platform_driver(tis_synquacer_drv);
-module_init(tpm_tis_synquacer_module_init);
-module_exit(tpm_tis_synquacer_module_exit);
MODULE_DESCRIPTION("TPM MMIO Driver for Socionext SynQuacer platform");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
index 5c865987ba5c..0818bb517805 100644
--- a/drivers/char/tpm/tpm_vtpm_proxy.c
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -243,7 +243,6 @@ static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
static const struct file_operations vtpm_proxy_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = vtpm_proxy_fops_read,
.write = vtpm_proxy_fops_write,
.poll = vtpm_proxy_fops_poll,
@@ -322,12 +321,14 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip,
*
* @chip: tpm chip to use
* @buf: send buffer
+ * @bufsiz: size of the buffer
* @count: bytes to send
*
* Return:
* 0 in case of success, negative error value otherwise.
*/
-static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
+static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t count)
{
struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
@@ -683,37 +684,21 @@ static struct miscdevice vtpmx_miscdev = {
.fops = &vtpmx_fops,
};
-static int vtpmx_init(void)
-{
- return misc_register(&vtpmx_miscdev);
-}
-
-static void vtpmx_cleanup(void)
-{
- misc_deregister(&vtpmx_miscdev);
-}
-
static int __init vtpm_module_init(void)
{
int rc;
- rc = vtpmx_init();
- if (rc) {
- pr_err("couldn't create vtpmx device\n");
- return rc;
- }
-
workqueue = create_workqueue("tpm-vtpm");
if (!workqueue) {
pr_err("couldn't create workqueue\n");
- rc = -ENOMEM;
- goto err_vtpmx_cleanup;
+ return -ENOMEM;
}
- return 0;
-
-err_vtpmx_cleanup:
- vtpmx_cleanup();
+ rc = misc_register(&vtpmx_miscdev);
+ if (rc) {
+ pr_err("couldn't create vtpmx device\n");
+ destroy_workqueue(workqueue);
+ }
return rc;
}
@@ -721,13 +706,13 @@ err_vtpmx_cleanup:
static void __exit vtpm_module_exit(void)
{
destroy_workqueue(workqueue);
- vtpmx_cleanup();
+ misc_deregister(&vtpmx_miscdev);
}
module_init(vtpm_module_init);
module_exit(vtpm_module_exit);
-MODULE_AUTHOR("Stefan Berger (stefanb@us.ibm.com)");
+MODULE_AUTHOR("Stefan Berger <stefanb@us.ibm.com>");
MODULE_DESCRIPTION("vTPM Driver");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c
index eef0fb06ea83..c25df7ea064e 100644
--- a/drivers/char/tpm/tpmrm-dev.c
+++ b/drivers/char/tpm/tpmrm-dev.c
@@ -46,7 +46,6 @@ static int tpmrm_release(struct inode *inode, struct file *file)
const struct file_operations tpmrm_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.open = tpmrm_open,
.read = tpm_common_read,
.write = tpm_common_write,
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 379291826261..556bf2256716 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -131,7 +131,8 @@ static size_t shr_data_offset(struct vtpm_shared_page *shr)
return struct_size(shr, extra_pages, shr->nr_extra_pages);
}
-static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
+static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
+ size_t count)
{
struct tpm_private *priv = dev_get_drvdata(&chip->dev);
struct vtpm_shared_page *shr = priv->shr;
@@ -360,14 +361,13 @@ static int tpmfront_probe(struct xenbus_device *dev,
return tpm_chip_register(priv->chip);
}
-static int tpmfront_remove(struct xenbus_device *dev)
+static void tpmfront_remove(struct xenbus_device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
struct tpm_private *priv = dev_get_drvdata(&chip->dev);
tpm_chip_unregister(chip);
ring_free(priv);
dev_set_drvdata(&chip->dev, NULL);
- return 0;
}
static int tpmfront_resume(struct xenbus_device *dev)
diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c
index ed45d04905c2..d7f841ab4323 100644
--- a/drivers/char/ttyprintk.c
+++ b/drivers/char/ttyprintk.c
@@ -40,7 +40,7 @@ static struct ttyprintk_port tpk_port;
static int tpk_curr;
-static char tpk_buffer[TPK_STR_SIZE + 4];
+static u8 tpk_buffer[TPK_STR_SIZE + 4];
static void tpk_flush(void)
{
@@ -51,9 +51,9 @@ static void tpk_flush(void)
}
}
-static int tpk_printk(const unsigned char *buf, int count)
+static int tpk_printk(const u8 *buf, size_t count)
{
- int i;
+ size_t i;
for (i = 0; i < count; i++) {
if (tpk_curr >= TPK_STR_SIZE) {
@@ -103,8 +103,7 @@ static void tpk_close(struct tty_struct *tty, struct file *filp)
/*
* TTY operations write function.
*/
-static int tpk_write(struct tty_struct *tty,
- const unsigned char *buf, int count)
+static ssize_t tpk_write(struct tty_struct *tty, const u8 *buf, size_t count)
{
struct ttyprintk_port *tpkp = tty->driver_data;
unsigned long flags;
@@ -229,4 +228,5 @@ static void __exit ttyprintk_exit(void)
device_initcall(ttyprintk_init);
module_exit(ttyprintk_exit);
+MODULE_DESCRIPTION("TTY driver to output user messages via printk");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 9fa3c76a267f..088182e54deb 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -13,6 +13,7 @@
#include <linux/fs.h>
#include <linux/splice.h>
#include <linux/pagemap.h>
+#include <linux/idr.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/poll.h>
@@ -25,6 +26,7 @@
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/string_choices.h>
#include "../tty/hvc/hvc_console.h"
#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
@@ -39,31 +41,21 @@
* across multiple devices and multiple ports per device.
*/
struct ports_driver_data {
- /* Used for registering chardevs */
- struct class *class;
-
/* Used for exporting per-port information to debugfs */
struct dentry *debugfs_dir;
/* List of all the devices we're handling */
struct list_head portdevs;
- /*
- * This is used to keep track of the number of hvc consoles
- * spawned by this driver. This number is given as the first
- * argument to hvc_alloc(). To correctly map an initial
- * console spawned via hvc_instantiate to the console being
- * hooked up via hvc_alloc, we need to pass the same vtermno.
- *
- * We also just assume the first console being initialised was
- * the first one that got used as the initial console.
- */
- unsigned int next_vtermno;
-
/* All the console devices handled by this driver */
struct list_head consoles;
};
-static struct ports_driver_data pdrvdata = { .next_vtermno = 1};
+
+static struct ports_driver_data pdrvdata;
+
+static const struct class port_class = {
+ .name = "virtio-ports",
+};
static DEFINE_SPINLOCK(pdrvdata_lock);
static DECLARE_COMPLETION(early_console_added);
@@ -89,6 +81,8 @@ struct console {
u32 vtermno;
};
+static DEFINE_IDA(vtermno_ida);
+
struct port_buffer {
char *buf;
@@ -113,7 +107,7 @@ struct port_buffer {
unsigned int sgpages;
/* sg is used if spages > 0. sg must be the last in is struct */
- struct scatterlist sg[];
+ struct scatterlist sg[] __counted_by(sgpages);
};
/*
@@ -237,9 +231,6 @@ struct port {
bool guest_connected;
};
-/* This is the very early arch-specified put chars function. */
-static int (*early_put_chars)(u32, const char *, int);
-
static struct port *find_port_by_vtermno(u32 vtermno)
{
struct port *port;
@@ -660,7 +651,7 @@ done:
* Give out the data that's requested from the buffer that we have
* queued up.
*/
-static ssize_t fill_readbuf(struct port *port, char __user *out_buf,
+static ssize_t fill_readbuf(struct port *port, u8 __user *out_buf,
size_t out_count, bool to_user)
{
struct port_buffer *buf;
@@ -679,7 +670,7 @@ static ssize_t fill_readbuf(struct port *port, char __user *out_buf,
if (ret)
return -EFAULT;
} else {
- memcpy((__force char *)out_buf, buf->buf + buf->offset,
+ memcpy((__force u8 *)out_buf, buf->buf + buf->offset,
out_count);
}
@@ -893,9 +884,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
if (len + offset > PAGE_SIZE)
len = PAGE_SIZE - offset;
- src = kmap_atomic(buf->page);
+ src = kmap_local_page(buf->page);
memcpy(page_address(page) + offset, src + buf->offset, len);
- kunmap_atomic(src);
+ kunmap_local(src);
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
}
@@ -933,14 +924,14 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
pipe_lock(pipe);
ret = 0;
- if (pipe_empty(pipe->head, pipe->tail))
+ if (pipe_is_empty(pipe))
goto error_out;
ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
if (ret < 0)
goto error_out;
- occupancy = pipe_occupancy(pipe->head, pipe->tail);
+ occupancy = pipe_buf_usage(pipe);
buf = alloc_buf(port->portdev->vdev, 0, occupancy);
if (!buf) {
@@ -1103,7 +1094,6 @@ static const struct file_operations port_fops = {
.poll = port_fops_poll,
.release = port_fops_release,
.fasync = port_fops_fasync,
- .llseek = no_llseek,
};
/*
@@ -1114,16 +1104,13 @@ static const struct file_operations port_fops = {
* it to finish: inefficient in theory, but in practice
* implementations will do it immediately.
*/
-static int put_chars(u32 vtermno, const char *buf, int count)
+static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count)
{
struct port *port;
struct scatterlist sg[1];
void *data;
int ret;
- if (unlikely(early_put_chars))
- return early_put_chars(vtermno, buf, count);
-
port = find_port_by_vtermno(vtermno);
if (!port)
return -EPIPE;
@@ -1145,14 +1132,10 @@ static int put_chars(u32 vtermno, const char *buf, int count)
* We call out to fill_readbuf that gets us the required data from the
* buffers that are queued up.
*/
-static int get_chars(u32 vtermno, char *buf, int count)
+static ssize_t get_chars(u32 vtermno, u8 *buf, size_t count)
{
struct port *port;
- /* If we've not set up the port yet, we have no input to give. */
- if (unlikely(early_put_chars))
- return 0;
-
port = find_port_by_vtermno(vtermno);
if (!port)
return -EPIPE;
@@ -1160,7 +1143,7 @@ static int get_chars(u32 vtermno, char *buf, int count)
/* If we don't have an input queue yet, we can't get input. */
BUG_ON(!port->in_vq);
- return fill_readbuf(port, (__force char __user *)buf, count, false);
+ return fill_readbuf(port, (__force u8 __user *)buf, count, false);
}
static void resize_console(struct port *port)
@@ -1208,21 +1191,6 @@ static const struct hv_ops hv_ops = {
.notifier_hangup = notifier_del_vio,
};
-/*
- * Console drivers are initialized very early so boot messages can go
- * out, so we do things slightly differently from the generic virtio
- * initialization of the net and block drivers.
- *
- * At this stage, the console is output-only. It's too early to set
- * up a virtqueue, so we let the drivers do some boutique early-output
- * thing.
- */
-int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
-{
- early_put_chars = put_chars;
- return hvc_instantiate(0, 0, &hv_ops);
-}
-
static int init_port_console(struct port *port)
{
int ret;
@@ -1244,29 +1212,25 @@ static int init_port_console(struct port *port)
* pointers. The final argument is the output buffer size: we
* can do any size, so we put PAGE_SIZE here.
*/
- port->cons.vtermno = pdrvdata.next_vtermno;
+ ret = ida_alloc_min(&vtermno_ida, 1, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+ port->cons.vtermno = ret;
port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
if (IS_ERR(port->cons.hvc)) {
ret = PTR_ERR(port->cons.hvc);
dev_err(port->dev,
"error %d allocating hvc for port\n", ret);
port->cons.hvc = NULL;
+ ida_free(&vtermno_ida, port->cons.vtermno);
return ret;
}
spin_lock_irq(&pdrvdata_lock);
- pdrvdata.next_vtermno++;
list_add_tail(&port->cons.list, &pdrvdata.consoles);
spin_unlock_irq(&pdrvdata_lock);
port->guest_connected = true;
- /*
- * Start using the new console output if this is the first
- * console to come up.
- */
- if (early_put_chars)
- early_put_chars = NULL;
-
/* Notify host of port being opened */
send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
@@ -1306,8 +1270,7 @@ static int port_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "bytes_sent: %lu\n", port->stats.bytes_sent);
seq_printf(s, "bytes_received: %lu\n", port->stats.bytes_received);
seq_printf(s, "bytes_discarded: %lu\n", port->stats.bytes_discarded);
- seq_printf(s, "is_console: %s\n",
- is_console_port(port) ? "yes" : "no");
+ seq_printf(s, "is_console: %s\n", str_yes_no(is_console_port(port)));
seq_printf(s, "console_vtermno: %u\n", port->cons.vtermno);
return 0;
@@ -1358,7 +1321,6 @@ static void send_sigio_to_port(struct port *port)
static int add_port(struct ports_device *portdev, u32 id)
{
- char debugfs_name[16];
struct port *port;
dev_t devt;
int err;
@@ -1404,7 +1366,7 @@ static int add_port(struct ports_device *portdev, u32 id)
"Error %d adding cdev for port %u\n", err, id);
goto free_cdev;
}
- port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
+ port->dev = device_create(&port_class, &port->portdev->vdev->dev,
devt, port, "vport%up%u",
port->portdev->vdev->index, id);
if (IS_ERR(port->dev)) {
@@ -1461,16 +1423,14 @@ static int add_port(struct ports_device *portdev, u32 id)
* Finally, create the debugfs file that we can use to
* inspect a port's state at any time
*/
- snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u",
- port->portdev->vdev->index, id);
- port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
+ port->debugfs_file = debugfs_create_file(dev_name(port->dev), 0444,
pdrvdata.debugfs_dir,
port, &port_debugfs_fops);
return 0;
free_inbufs:
free_device:
- device_destroy(pdrvdata.class, port->dev->devt);
+ device_destroy(&port_class, port->dev->devt);
free_cdev:
cdev_del(port->cdev);
free_port:
@@ -1532,6 +1492,7 @@ static void unplug_port(struct port *port)
list_del(&port->cons.list);
spin_unlock_irq(&pdrvdata_lock);
hvc_remove(port->cons.hvc);
+ ida_free(&vtermno_ida, port->cons.vtermno);
}
remove_port_data(port);
@@ -1544,7 +1505,7 @@ static void unplug_port(struct port *port)
port->portdev = NULL;
sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
- device_destroy(pdrvdata.class, port->dev->devt);
+ device_destroy(&port_class, port->dev->devt);
cdev_del(port->cdev);
debugfs_remove(port->debugfs_file);
@@ -1615,8 +1576,8 @@ static void handle_control_message(struct virtio_device *vdev,
break;
case VIRTIO_CONSOLE_RESIZE: {
struct {
- __u16 rows;
- __u16 cols;
+ __virtio16 cols;
+ __virtio16 rows;
} size;
if (!is_console_port(port))
@@ -1624,7 +1585,8 @@ static void handle_control_message(struct virtio_device *vdev,
memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt),
sizeof(size));
- set_console_size(port, size.rows, size.cols);
+ set_console_size(port, virtio16_to_cpu(vdev, size.rows),
+ virtio16_to_cpu(vdev, size.cols));
port->cons.hvc->irq_requested = 1;
resize_console(port);
@@ -1670,9 +1632,8 @@ static void handle_control_message(struct virtio_device *vdev,
"Not enough space to store port name\n");
break;
}
- strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt),
- name_size - 1);
- port->name[name_size - 1] = 0;
+ strscpy(port->name, buf->buf + buf->offset + sizeof(*cpkt),
+ name_size);
/*
* Since we only have one sysfs attribute, 'name',
@@ -1840,8 +1801,7 @@ static void config_work_handler(struct work_struct *work)
static int init_vqs(struct ports_device *portdev)
{
- vq_callback_t **io_callbacks;
- char **io_names;
+ struct virtqueue_info *vqs_info;
struct virtqueue **vqs;
u32 i, j, nr_ports, nr_queues;
int err;
@@ -1850,15 +1810,12 @@ static int init_vqs(struct ports_device *portdev)
nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2;
vqs = kmalloc_array(nr_queues, sizeof(struct virtqueue *), GFP_KERNEL);
- io_callbacks = kmalloc_array(nr_queues, sizeof(vq_callback_t *),
- GFP_KERNEL);
- io_names = kmalloc_array(nr_queues, sizeof(char *), GFP_KERNEL);
+ vqs_info = kcalloc(nr_queues, sizeof(*vqs_info), GFP_KERNEL);
portdev->in_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *),
GFP_KERNEL);
portdev->out_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *),
GFP_KERNEL);
- if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs ||
- !portdev->out_vqs) {
+ if (!vqs || !vqs_info || !portdev->in_vqs || !portdev->out_vqs) {
err = -ENOMEM;
goto free;
}
@@ -1869,30 +1826,27 @@ static int init_vqs(struct ports_device *portdev)
* 0 before others.
*/
j = 0;
- io_callbacks[j] = in_intr;
- io_callbacks[j + 1] = out_intr;
- io_names[j] = "input";
- io_names[j + 1] = "output";
+ vqs_info[j].callback = in_intr;
+ vqs_info[j + 1].callback = out_intr;
+ vqs_info[j].name = "input";
+ vqs_info[j + 1].name = "output";
j += 2;
if (use_multiport(portdev)) {
- io_callbacks[j] = control_intr;
- io_callbacks[j + 1] = NULL;
- io_names[j] = "control-i";
- io_names[j + 1] = "control-o";
+ vqs_info[j].callback = control_intr;
+ vqs_info[j].name = "control-i";
+ vqs_info[j + 1].name = "control-o";
for (i = 1; i < nr_ports; i++) {
j += 2;
- io_callbacks[j] = in_intr;
- io_callbacks[j + 1] = out_intr;
- io_names[j] = "input";
- io_names[j + 1] = "output";
+ vqs_info[j].callback = in_intr;
+ vqs_info[j + 1].callback = out_intr;
+ vqs_info[j].name = "input";
+ vqs_info[j + 1].name = "output";
}
}
/* Find the queues. */
- err = virtio_find_vqs(portdev->vdev, nr_queues, vqs,
- io_callbacks,
- (const char **)io_names, NULL);
+ err = virtio_find_vqs(portdev->vdev, nr_queues, vqs, vqs_info, NULL);
if (err)
goto free;
@@ -1910,8 +1864,7 @@ static int init_vqs(struct ports_device *portdev)
portdev->out_vqs[i] = vqs[j + 1];
}
}
- kfree(io_names);
- kfree(io_callbacks);
+ kfree(vqs_info);
kfree(vqs);
return 0;
@@ -1919,8 +1872,7 @@ static int init_vqs(struct ports_device *portdev)
free:
kfree(portdev->out_vqs);
kfree(portdev->in_vqs);
- kfree(io_names);
- kfree(io_callbacks);
+ kfree(vqs_info);
kfree(vqs);
return err;
@@ -1940,6 +1892,7 @@ static void remove_vqs(struct ports_device *portdev)
flush_bufs(vq, true);
while ((buf = virtqueue_detach_unused_buf(vq)))
free_buf(buf, true);
+ cond_resched();
}
portdev->vdev->config->del_vqs(portdev->vdev);
kfree(portdev->in_vqs);
@@ -2002,7 +1955,6 @@ static int virtcons_probe(struct virtio_device *vdev)
struct ports_device *portdev;
int err;
bool multiport;
- bool early = early_put_chars != NULL;
/* We only need a config space if features are offered */
if (!vdev->config->get &&
@@ -2013,9 +1965,6 @@ static int virtcons_probe(struct virtio_device *vdev)
return -EINVAL;
}
- /* Ensure to read early_put_chars now */
- barrier();
-
portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
if (!portdev) {
err = -ENOMEM;
@@ -2055,25 +2004,27 @@ static int virtcons_probe(struct virtio_device *vdev)
multiport = true;
}
- err = init_vqs(portdev);
- if (err < 0) {
- dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
- goto free_chrdev;
- }
-
spin_lock_init(&portdev->ports_lock);
INIT_LIST_HEAD(&portdev->ports);
INIT_LIST_HEAD(&portdev->list);
- virtio_device_ready(portdev->vdev);
-
INIT_WORK(&portdev->config_work, &config_work_handler);
INIT_WORK(&portdev->control_work, &control_work_handler);
if (multiport) {
spin_lock_init(&portdev->c_ivq_lock);
spin_lock_init(&portdev->c_ovq_lock);
+ }
+
+ err = init_vqs(portdev);
+ if (err < 0) {
+ dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
+ goto free_chrdev;
+ }
+
+ virtio_device_ready(portdev->vdev);
+ if (multiport) {
err = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
if (err < 0) {
dev_err(&vdev->dev,
@@ -2103,18 +2054,6 @@ static int virtcons_probe(struct virtio_device *vdev)
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 1);
- /*
- * If there was an early virtio console, assume that there are no
- * other consoles. We need to wait until the hvc_alloc matches the
- * hvc_instantiate, otherwise tty_open will complain, resulting in
- * a "Warning: unable to open an initial console" boot failure.
- * Without multiport this is done in add_port above. With multiport
- * this might take some host<->guest communication - thus we have to
- * wait.
- */
- if (multiport && early)
- wait_for_completion(&early_console_added);
-
return 0;
free_chrdev:
@@ -2224,7 +2163,6 @@ static struct virtio_driver virtio_console = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
- .driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtcons_probe,
.remove = virtcons_remove,
@@ -2239,7 +2177,6 @@ static struct virtio_driver virtio_rproc_serial = {
.feature_table = rproc_serial_features,
.feature_table_size = ARRAY_SIZE(rproc_serial_features),
.driver.name = "virtio_rproc_serial",
- .driver.owner = THIS_MODULE,
.id_table = rproc_serial_id_table,
.probe = virtcons_probe,
.remove = virtcons_remove,
@@ -2249,12 +2186,9 @@ static int __init virtio_console_init(void)
{
int err;
- pdrvdata.class = class_create(THIS_MODULE, "virtio-ports");
- if (IS_ERR(pdrvdata.class)) {
- err = PTR_ERR(pdrvdata.class);
- pr_err("Error %d creating virtio-ports class\n", err);
+ err = class_register(&port_class);
+ if (err)
return err;
- }
pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
INIT_LIST_HEAD(&pdrvdata.consoles);
@@ -2276,7 +2210,7 @@ unregister:
unregister_virtio_driver(&virtio_console);
free:
debugfs_remove_recursive(pdrvdata.debugfs_dir);
- class_destroy(pdrvdata.class);
+ class_unregister(&port_class);
return err;
}
@@ -2287,7 +2221,7 @@ static void __exit virtio_console_fini(void)
unregister_virtio_driver(&virtio_console);
unregister_virtio_driver(&virtio_rproc_serial);
- class_destroy(pdrvdata.class);
+ class_unregister(&port_class);
debugfs_remove_recursive(pdrvdata.debugfs_dir);
}
module_init(virtio_console_init);
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 74a4928aea1d..34a345dc5e72 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -84,18 +84,13 @@
#include <linux/sysctl.h>
#include <linux/fs.h>
#include <linux/cdev.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
-#ifdef CONFIG_OF
-/* For open firmware. */
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#endif
-
#include "xilinx_hwicap.h"
#include "buffer_icap.h"
#include "fifo_icap.h"
@@ -113,7 +108,9 @@ static DEFINE_MUTEX(hwicap_mutex);
static bool probed_devices[HWICAP_DEVICES];
static struct mutex icap_sem;
-static struct class *icap_class;
+static const struct class icap_class = {
+ .name = "xilinx_config",
+};
#define UNIMPLEMENTED 0xFFFF
@@ -599,14 +596,14 @@ static const struct file_operations hwicap_fops = {
.llseek = noop_llseek,
};
-static int hwicap_setup(struct device *dev, int id,
- const struct resource *regs_res,
+static int hwicap_setup(struct platform_device *pdev, int id,
const struct hwicap_driver_config *config,
const struct config_registers *config_regs)
{
dev_t devt;
struct hwicap_drvdata *drvdata = NULL;
- int retval = 0;
+ struct device *dev = &pdev->dev;
+ int retval;
dev_info(dev, "Xilinx icap port driver\n");
@@ -634,72 +631,39 @@ static int hwicap_setup(struct device *dev, int id,
devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR + id);
- drvdata = kzalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL);
+ drvdata = devm_kzalloc(dev, sizeof(struct hwicap_drvdata), GFP_KERNEL);
if (!drvdata) {
retval = -ENOMEM;
- goto failed0;
- }
- dev_set_drvdata(dev, (void *)drvdata);
-
- if (!regs_res) {
- dev_err(dev, "Couldn't get registers resource\n");
- retval = -EFAULT;
- goto failed1;
+ goto failed;
}
+ dev_set_drvdata(dev, drvdata);
- drvdata->mem_start = regs_res->start;
- drvdata->mem_end = regs_res->end;
- drvdata->mem_size = resource_size(regs_res);
-
- if (!request_mem_region(drvdata->mem_start,
- drvdata->mem_size, DRIVER_NAME)) {
- dev_err(dev, "Couldn't lock memory region at %Lx\n",
- (unsigned long long) regs_res->start);
- retval = -EBUSY;
- goto failed1;
+ drvdata->base_address = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(drvdata->base_address)) {
+ retval = PTR_ERR(drvdata->base_address);
+ goto failed;
}
drvdata->devt = devt;
drvdata->dev = dev;
- drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size);
- if (!drvdata->base_address) {
- dev_err(dev, "ioremap() failed\n");
- retval = -ENOMEM;
- goto failed2;
- }
-
drvdata->config = config;
drvdata->config_regs = config_regs;
mutex_init(&drvdata->sem);
drvdata->is_open = 0;
- dev_info(dev, "ioremap %llx to %p with size %llx\n",
- (unsigned long long) drvdata->mem_start,
- drvdata->base_address,
- (unsigned long long) drvdata->mem_size);
-
cdev_init(&drvdata->cdev, &hwicap_fops);
drvdata->cdev.owner = THIS_MODULE;
retval = cdev_add(&drvdata->cdev, devt, 1);
if (retval) {
dev_err(dev, "cdev_add() failed\n");
- goto failed3;
+ goto failed;
}
- device_create(icap_class, dev, devt, NULL, "%s%d", DRIVER_NAME, id);
+ device_create(&icap_class, dev, devt, NULL, "%s%d", DRIVER_NAME, id);
return 0; /* success */
- failed3:
- iounmap(drvdata->base_address);
-
- failed2:
- release_mem_region(regs_res->start, drvdata->mem_size);
-
- failed1:
- kfree(drvdata);
-
- failed0:
+ failed:
mutex_lock(&icap_sem);
probed_devices[id] = 0;
mutex_unlock(&icap_sem);
@@ -721,53 +685,22 @@ static struct hwicap_driver_config fifo_icap_config = {
.reset = fifo_icap_reset,
};
-static int hwicap_remove(struct device *dev)
-{
- struct hwicap_drvdata *drvdata;
-
- drvdata = dev_get_drvdata(dev);
-
- if (!drvdata)
- return 0;
-
- device_destroy(icap_class, drvdata->devt);
- cdev_del(&drvdata->cdev);
- iounmap(drvdata->base_address);
- release_mem_region(drvdata->mem_start, drvdata->mem_size);
- kfree(drvdata);
-
- mutex_lock(&icap_sem);
- probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0;
- mutex_unlock(&icap_sem);
- return 0; /* success */
-}
-
-#ifdef CONFIG_OF
-static int hwicap_of_probe(struct platform_device *op,
- const struct hwicap_driver_config *config)
+static int hwicap_drv_probe(struct platform_device *pdev)
{
- struct resource res;
- const unsigned int *id;
- const char *family;
- int rc;
const struct config_registers *regs;
+ const struct hwicap_driver_config *config;
+ const char *family;
+ int id = -1;
+ config = device_get_match_data(&pdev->dev);
- rc = of_address_to_resource(op->dev.of_node, 0, &res);
- if (rc) {
- dev_err(&op->dev, "invalid address\n");
- return rc;
- }
-
- id = of_get_property(op->dev.of_node, "port-number", NULL);
+ of_property_read_u32(pdev->dev.of_node, "port-number", &id);
/* It's most likely that we're using V4, if the family is not
* specified
*/
regs = &v4_config_registers;
- family = of_get_property(op->dev.of_node, "xlnx,family", NULL);
-
- if (family) {
+ if (!of_property_read_string(pdev->dev.of_node, "xlnx,family", &family)) {
if (!strcmp(family, "virtex2p"))
regs = &v2_config_registers;
else if (!strcmp(family, "virtex4"))
@@ -777,60 +710,24 @@ static int hwicap_of_probe(struct platform_device *op,
else if (!strcmp(family, "virtex6"))
regs = &v6_config_registers;
}
- return hwicap_setup(&op->dev, id ? *id : -1, &res, config,
- regs);
-}
-#else
-static inline int hwicap_of_probe(struct platform_device *op,
- const struct hwicap_driver_config *config)
-{
- return -EINVAL;
+ return hwicap_setup(pdev, id, config, regs);
}
-#endif /* CONFIG_OF */
-static const struct of_device_id hwicap_of_match[];
-static int hwicap_drv_probe(struct platform_device *pdev)
+static void hwicap_drv_remove(struct platform_device *pdev)
{
- const struct of_device_id *match;
- struct resource *res;
- const struct config_registers *regs;
- const char *family;
-
- match = of_match_device(hwicap_of_match, &pdev->dev);
- if (match)
- return hwicap_of_probe(pdev, match->data);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- /* It's most likely that we're using V4, if the family is not
- * specified
- */
- regs = &v4_config_registers;
- family = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct hwicap_drvdata *drvdata;
- if (family) {
- if (!strcmp(family, "virtex2p"))
- regs = &v2_config_registers;
- else if (!strcmp(family, "virtex4"))
- regs = &v4_config_registers;
- else if (!strcmp(family, "virtex5"))
- regs = &v5_config_registers;
- else if (!strcmp(family, "virtex6"))
- regs = &v6_config_registers;
- }
+ drvdata = dev_get_drvdata(dev);
- return hwicap_setup(&pdev->dev, pdev->id, res,
- &buffer_icap_config, regs);
-}
+ device_destroy(&icap_class, drvdata->devt);
+ cdev_del(&drvdata->cdev);
-static int hwicap_drv_remove(struct platform_device *pdev)
-{
- return hwicap_remove(&pdev->dev);
+ mutex_lock(&icap_sem);
+ probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0;
+ mutex_unlock(&icap_sem);
}
-#ifdef CONFIG_OF
/* Match table for device tree binding */
static const struct of_device_id hwicap_of_match[] = {
{ .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config},
@@ -838,9 +735,6 @@ static const struct of_device_id hwicap_of_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, hwicap_of_match);
-#else
-#define hwicap_of_match NULL
-#endif
static struct platform_driver hwicap_platform_driver = {
.probe = hwicap_drv_probe,
@@ -856,7 +750,9 @@ static int __init hwicap_module_init(void)
dev_t devt;
int retval;
- icap_class = class_create(THIS_MODULE, "xilinx_config");
+ retval = class_register(&icap_class);
+ if (retval)
+ return retval;
mutex_init(&icap_sem);
devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR);
@@ -882,7 +778,7 @@ static void __exit hwicap_module_cleanup(void)
{
dev_t devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR);
- class_destroy(icap_class);
+ class_unregister(&icap_class);
platform_driver_unregister(&hwicap_platform_driver);
diff --git a/drivers/char/xillybus/Kconfig b/drivers/char/xillybus/Kconfig
index a8036dad437e..f51d533390a9 100644
--- a/drivers/char/xillybus/Kconfig
+++ b/drivers/char/xillybus/Kconfig
@@ -29,7 +29,7 @@ config XILLYBUS_PCIE
config XILLYBUS_OF
tristate "Xillybus over Device Tree"
- depends on OF && HAS_DMA
+ depends on OF && HAS_DMA && HAS_IOMEM
help
Set to M if you want Xillybus to find its resources from the
Open Firmware Flattened Device Tree. If the target is an embedded
diff --git a/drivers/char/xillybus/xillybus_class.c b/drivers/char/xillybus/xillybus_class.c
index 0f238648dcfe..c92a628e389e 100644
--- a/drivers/char/xillybus/xillybus_class.c
+++ b/drivers/char/xillybus/xillybus_class.c
@@ -23,7 +23,9 @@ MODULE_LICENSE("GPL v2");
static DEFINE_MUTEX(unit_mutex);
static LIST_HEAD(unit_list);
-static struct class *xillybus_class;
+static const struct class xillybus_class = {
+ .name = "xillybus",
+};
#define UNITNAMELEN 16
@@ -121,7 +123,7 @@ int xillybus_init_chrdev(struct device *dev,
len -= namelen + 1;
idt += namelen + 1;
- device = device_create(xillybus_class,
+ device = device_create(&xillybus_class,
NULL,
MKDEV(unit->major,
i + unit->lowest_minor),
@@ -152,7 +154,7 @@ int xillybus_init_chrdev(struct device *dev,
unroll_device_create:
for (i--; i >= 0; i--)
- device_destroy(xillybus_class, MKDEV(unit->major,
+ device_destroy(&xillybus_class, MKDEV(unit->major,
i + unit->lowest_minor));
cdev_del(unit->cdev);
@@ -193,7 +195,7 @@ void xillybus_cleanup_chrdev(void *private_data,
for (minor = unit->lowest_minor;
minor < (unit->lowest_minor + unit->num_nodes);
minor++)
- device_destroy(xillybus_class, MKDEV(unit->major, minor));
+ device_destroy(&xillybus_class, MKDEV(unit->major, minor));
cdev_del(unit->cdev);
@@ -227,33 +229,27 @@ int xillybus_find_inode(struct inode *inode,
break;
}
- mutex_unlock(&unit_mutex);
-
- if (!unit)
+ if (!unit) {
+ mutex_unlock(&unit_mutex);
return -ENODEV;
+ }
*private_data = unit->private_data;
*index = minor - unit->lowest_minor;
+ mutex_unlock(&unit_mutex);
return 0;
}
EXPORT_SYMBOL(xillybus_find_inode);
static int __init xillybus_class_init(void)
{
- xillybus_class = class_create(THIS_MODULE, "xillybus");
-
- if (IS_ERR(xillybus_class)) {
- pr_warn("Failed to register xillybus class\n");
-
- return PTR_ERR(xillybus_class);
- }
- return 0;
+ return class_register(&xillybus_class);
}
static void __exit xillybus_class_exit(void)
{
- class_destroy(xillybus_class);
+ class_unregister(&xillybus_class);
}
module_init(xillybus_class_init);
diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c
index 11b7c4749274..fc4e69b5cb6a 100644
--- a/drivers/char/xillybus/xillybus_core.c
+++ b/drivers/char/xillybus/xillybus_core.c
@@ -1184,8 +1184,7 @@ static int xillybus_flush(struct file *filp, fl_owner_t id)
static void xillybus_autoflush(struct work_struct *work)
{
- struct delayed_work *workitem = container_of(
- work, struct delayed_work, work);
+ struct delayed_work *workitem = to_delayed_work(work);
struct xilly_channel *channel = container_of(
workitem, struct xilly_channel, rd_workitem);
int rc;
@@ -1974,7 +1973,7 @@ EXPORT_SYMBOL(xillybus_endpoint_remove);
static int __init xillybus_init(void)
{
- xillybus_wq = alloc_workqueue(xillyname, 0, 0);
+ xillybus_wq = alloc_workqueue(xillyname, WQ_UNBOUND, 0);
if (!xillybus_wq)
return -ENOMEM;
diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c
index e5372e45d211..1a1e64133315 100644
--- a/drivers/char/xillybus/xillybus_of.c
+++ b/drivers/char/xillybus/xillybus_of.c
@@ -64,14 +64,12 @@ static int xilly_drv_probe(struct platform_device *op)
return xillybus_endpoint_discovery(endpoint);
}
-static int xilly_drv_remove(struct platform_device *op)
+static void xilly_drv_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct xilly_endpoint *endpoint = dev_get_drvdata(dev);
xillybus_endpoint_remove(endpoint);
-
- return 0;
}
static struct platform_driver xillybus_platform_driver = {
diff --git a/drivers/char/xillybus/xillyusb.c b/drivers/char/xillybus/xillyusb.c
index 39bcbfd908b4..386531474213 100644
--- a/drivers/char/xillybus/xillyusb.c
+++ b/drivers/char/xillybus/xillyusb.c
@@ -50,6 +50,7 @@ MODULE_LICENSE("GPL v2");
static const char xillyname[] = "xillyusb";
static unsigned int fifo_buf_order;
+static struct workqueue_struct *wakeup_wq;
#define USB_VENDOR_ID_XILINX 0x03fd
#define USB_VENDOR_ID_ALTERA 0x09fb
@@ -184,6 +185,14 @@ struct xillyusb_dev {
struct mutex process_in_mutex; /* synchronize wakeup_all() */
};
+/*
+ * kref_mutex is used in xillyusb_open() to prevent the xillyusb_dev
+ * struct from being freed during the gap between being found by
+ * xillybus_find_inode() and having its reference count incremented.
+ */
+
+static DEFINE_MUTEX(kref_mutex);
+
/* FPGA to host opcodes */
enum {
OPCODE_DATA = 0,
@@ -561,10 +570,6 @@ static void cleanup_dev(struct kref *kref)
* errors if executed. The mechanism relies on that xdev->error is assigned
* a non-zero value by report_io_error() prior to queueing wakeup_all(),
* which prevents bulk_in_work() from calling process_bulk_in().
- *
- * The fact that wakeup_all() and bulk_in_work() are queued on the same
- * workqueue makes their concurrent execution very unlikely, however the
- * kernel's API doesn't seem to ensure this strictly.
*/
static void wakeup_all(struct work_struct *work)
@@ -619,7 +624,7 @@ static void report_io_error(struct xillyusb_dev *xdev,
if (do_once) {
kref_get(&xdev->kref); /* xdev is used by work item */
- queue_work(xdev->workq, &xdev->wakeup_workitem);
+ queue_work(wakeup_wq, &xdev->wakeup_workitem);
}
}
@@ -1237,9 +1242,16 @@ static int xillyusb_open(struct inode *inode, struct file *filp)
int rc;
int index;
+ mutex_lock(&kref_mutex);
+
rc = xillybus_find_inode(inode, (void **)&xdev, &index);
- if (rc)
+ if (rc) {
+ mutex_unlock(&kref_mutex);
return rc;
+ }
+
+ kref_get(&xdev->kref);
+ mutex_unlock(&kref_mutex);
chan = &xdev->channels[index];
filp->private_data = chan;
@@ -1275,8 +1287,6 @@ static int xillyusb_open(struct inode *inode, struct file *filp)
((filp->f_mode & FMODE_WRITE) && chan->open_for_write))
goto unmutex_fail;
- kref_get(&xdev->kref);
-
if (filp->f_mode & FMODE_READ)
chan->open_for_read = 1;
@@ -1413,6 +1423,7 @@ unopen:
return rc;
unmutex_fail:
+ kref_put(&xdev->kref, cleanup_dev);
mutex_unlock(&chan->lock);
return rc;
}
@@ -1892,6 +1903,13 @@ static const struct file_operations xillyusb_fops = {
static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev)
{
+ struct usb_device *udev = xdev->udev;
+
+ /* Verify that device has the two fundamental bulk in/out endpoints */
+ if (usb_pipe_type_check(udev, usb_sndbulkpipe(udev, MSG_EP_NUM)) ||
+ usb_pipe_type_check(udev, usb_rcvbulkpipe(udev, IN_EP_NUM)))
+ return -ENODEV;
+
xdev->msg_ep = endpoint_alloc(xdev, MSG_EP_NUM | USB_DIR_OUT,
bulk_out_work, 1, 2);
if (!xdev->msg_ep)
@@ -1921,14 +1939,15 @@ static int setup_channels(struct xillyusb_dev *xdev,
__le16 *chandesc,
int num_channels)
{
- struct xillyusb_channel *chan;
+ struct usb_device *udev = xdev->udev;
+ struct xillyusb_channel *chan, *new_channels;
int i;
chan = kcalloc(num_channels, sizeof(*chan), GFP_KERNEL);
if (!chan)
return -ENOMEM;
- xdev->channels = chan;
+ new_channels = chan;
for (i = 0; i < num_channels; i++, chan++) {
unsigned int in_desc = le16_to_cpu(*chandesc++);
@@ -1957,6 +1976,15 @@ static int setup_channels(struct xillyusb_dev *xdev,
*/
if ((out_desc & 0x80) && i < 14) { /* Entry is valid */
+ if (usb_pipe_type_check(udev,
+ usb_sndbulkpipe(udev, i + 2))) {
+ dev_err(xdev->dev,
+ "Missing BULK OUT endpoint %d\n",
+ i + 2);
+ kfree(new_channels);
+ return -ENODEV;
+ }
+
chan->writable = 1;
chan->out_synchronous = !!(out_desc & 0x40);
chan->out_seekable = !!(out_desc & 0x20);
@@ -1966,6 +1994,7 @@ static int setup_channels(struct xillyusb_dev *xdev,
}
}
+ xdev->channels = new_channels;
return 0;
}
@@ -2082,9 +2111,11 @@ static int xillyusb_discovery(struct usb_interface *interface)
* just after responding with the IDT, there is no reason for any
* work item to be running now. To be sure that xdev->channels
* is updated on anything that might run in parallel, flush the
- * workqueue, which rarely does anything.
+ * device's workqueue and the wakeup work item. This rarely
+ * does anything.
*/
flush_workqueue(xdev->workq);
+ flush_work(&xdev->wakeup_workitem);
xdev->num_channels = num_channels;
@@ -2132,7 +2163,7 @@ static int xillyusb_probe(struct usb_interface *interface,
spin_lock_init(&xdev->error_lock);
xdev->in_counter = 0;
xdev->in_bytes_left = 0;
- xdev->workq = alloc_workqueue(xillyname, WQ_HIGHPRI, 0);
+ xdev->workq = alloc_workqueue(xillyname, WQ_HIGHPRI | WQ_UNBOUND, 0);
if (!xdev->workq) {
dev_err(&interface->dev, "Failed to allocate work queue\n");
@@ -2227,7 +2258,9 @@ static void xillyusb_disconnect(struct usb_interface *interface)
xdev->dev = NULL;
+ mutex_lock(&kref_mutex);
kref_put(&xdev->kref, cleanup_dev);
+ mutex_unlock(&kref_mutex);
}
static struct usb_driver xillyusb_driver = {
@@ -2242,6 +2275,10 @@ static int __init xillyusb_init(void)
{
int rc = 0;
+ wakeup_wq = alloc_workqueue(xillyname, WQ_UNBOUND, 0);
+ if (!wakeup_wq)
+ return -ENOMEM;
+
if (LOG2_INITIAL_FIFO_BUF_SIZE > PAGE_SHIFT)
fifo_buf_order = LOG2_INITIAL_FIFO_BUF_SIZE - PAGE_SHIFT;
else
@@ -2249,12 +2286,17 @@ static int __init xillyusb_init(void)
rc = usb_register(&xillyusb_driver);
+ if (rc)
+ destroy_workqueue(wakeup_wq);
+
return rc;
}
static void __exit xillyusb_exit(void)
{
usb_deregister(&xillyusb_driver);
+
+ destroy_workqueue(wakeup_wq);
}
module_init(xillyusb_init);
diff --git a/drivers/clk/.kunitconfig b/drivers/clk/.kunitconfig
index 2fbeb71316f8..08e26137f3d9 100644
--- a/drivers/clk/.kunitconfig
+++ b/drivers/clk/.kunitconfig
@@ -1,5 +1,9 @@
CONFIG_KUNIT=y
+CONFIG_OF=y
+CONFIG_OF_OVERLAY=y
CONFIG_COMMON_CLK=y
CONFIG_CLK_KUNIT_TEST=y
+CONFIG_CLK_FIXED_RATE_KUNIT_TEST=y
CONFIG_CLK_GATE_KUNIT_TEST=y
+CONFIG_CLK_FD_KUNIT_TEST=y
CONFIG_UML_PCI_OVER_VIRTIO=n
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 48f8f4221e21..3a1611008e48 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -23,7 +23,6 @@ menuconfig COMMON_CLK
depends on !HAVE_LEGACY_CLK
select HAVE_CLK_PREPARE
select HAVE_CLK
- select SRCU
select RATIONAL
help
The common clock framework is a single definition of struct
@@ -62,7 +61,6 @@ config LMK04832
config COMMON_CLK_APPLE_NCO
tristate "Clock driver for Apple SoC NCOs"
depends on ARCH_APPLE || COMPILE_TEST
- default ARCH_APPLE
help
This driver supports NCO (Numerically Controlled Oscillator) blocks
found on Apple SoCs such as t8103 (M1). The blocks are typically
@@ -83,16 +81,25 @@ config COMMON_CLK_MAX9485
config COMMON_CLK_RK808
tristate "Clock driver for RK805/RK808/RK809/RK817/RK818"
- depends on MFD_RK808
+ depends on MFD_RK8XX
help
This driver supports RK805, RK809 and RK817, RK808 and RK818 crystal oscillator clock.
These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each.
Clkout1 is always on, Clkout2 can off by control register.
+config COMMON_CLK_RP1
+ tristate "Raspberry Pi RP1-based clock support"
+ depends on MISC_RP1 || COMPILE_TEST
+ default MISC_RP1
+ help
+ Enable common clock framework support for Raspberry Pi RP1.
+ This multi-function device has 3 main PLLs and several clock
+ generators to drive the internal sub-peripherals.
+
config COMMON_CLK_HI655X
tristate "Clock driver for Hi655x" if EXPERT
depends on (MFD_HI655X_PMIC || COMPILE_TEST)
- depends on REGMAP
+ select REGMAP
default MFD_HI655X_PMIC
help
This driver supports the hi655x PMIC clock. This
@@ -219,6 +226,25 @@ config COMMON_CLK_EN7523
This driver provides the fixed clocks and gates present on Airoha
ARM silicon.
+config COMMON_CLK_EP93XX
+ tristate "Clock driver for Cirrus Logic ep93xx SoC"
+ depends on ARCH_EP93XX || COMPILE_TEST
+ select AUXILIARY_BUS
+ select REGMAP_MMIO
+ help
+ This driver supports the SoC clocks on the Cirrus Logic ep93xx.
+
+config COMMON_CLK_EYEQ
+ bool "Clock driver for the Mobileye EyeQ platform"
+ depends on MACH_EYEQ5 || MACH_EYEQ6H || COMPILE_TEST
+ select AUXILIARY_BUS
+ default MACH_EYEQ5 || MACH_EYEQ6H
+ help
+ This driver provides clocks found on Mobileye EyeQ5, EyeQ6L and Eye6H
+ SoCs. Controllers live in shared register regions called OLB. Driver
+ provides read-only PLLs, derived from the main crystal clock (which
+ must be constant). It also exposes some divider clocks.
+
config COMMON_CLK_FSL_FLEXSPI
tristate "Clock driver for FlexSPI on Layerscape SoCs"
depends on ARCH_LAYERSCAPE || COMPILE_TEST
@@ -249,10 +275,10 @@ config COMMON_CLK_GEMINI
platform, also known as SL3516 or CS3516.
config COMMON_CLK_LAN966X
- bool "Generic Clock Controller driver for LAN966X SoC"
+ tristate "Generic Clock Controller driver for LAN966X SoC"
depends on HAS_IOMEM
depends on OF
- depends on SOC_LAN966 || COMPILE_TEST
+ depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST
help
This driver provides support for Generic Clock Controller(GCK) on
LAN966X SoC. GCK generates and supplies clock to various peripherals
@@ -278,6 +304,15 @@ config COMMON_CLK_S2MPS11
clock. These multi-function devices have two (S2MPS14) or three
(S2MPS11, S5M8767) fixed-rate oscillators, clocked at 32KHz each.
+config CLK_TWL
+ tristate "Clock driver for the TWL PMIC family"
+ depends on TWL4030_CORE
+ help
+ Enable support for controlling the clock resources on TWL family
+ PMICs. These devices have some 32K clock outputs which can be
+ controlled by software. For now, the TWL6032 and TWL6030 clocks are
+ supported.
+
config CLK_TWL6040
tristate "External McPDM functional clock from twl6040"
depends on TWL6040_CORE
@@ -326,6 +361,24 @@ config COMMON_CLK_LOCHNAGAR
This driver supports the clocking features of the Cirrus Logic
Lochnagar audio development board.
+config COMMON_CLK_NPCM8XX
+ tristate "Clock driver for the NPCM8XX SoC Family"
+ depends on ARCH_NPCM || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ This driver supports the clocks on the Nuvoton BMC NPCM8XX SoC Family,
+ all the clocks are initialized by the bootloader, so this driver
+ allows only reading of current settings directly from the hardware.
+
+config COMMON_CLK_LOONGSON2
+ bool "Clock driver for Loongson-2 SoC"
+ depends on LOONGARCH || COMPILE_TEST
+ help
+ This driver provides support for clock controller on Loongson-2 SoC.
+ The clock controller can generates and supplies clock to various
+ peripherals within the SoC.
+ Say Y here to support Loongson-2 SoC clock driver.
+
config COMMON_CLK_NXP
def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
select REGMAP_MMIO if ARCH_LPC32XX
@@ -352,13 +405,6 @@ config COMMON_CLK_PXA
help
Support for the Marvell PXA SoC.
-config COMMON_CLK_OXNAS
- bool "Clock driver for the OXNAS SoC Family"
- depends on ARCH_OXNAS || COMPILE_TEST
- select MFD_SYSCON
- help
- Support for the OXNAS SoC Family clocks.
-
config COMMON_CLK_RS9_PCIE
tristate "Clock driver for Renesas 9-series PCIe clock generators"
depends on I2C
@@ -368,6 +414,24 @@ config COMMON_CLK_RS9_PCIE
This driver supports the Renesas 9-series PCIe clock generator
models 9FGV/9DBV/9DMV/9FGL/9DML/9QXL/9SQ.
+config COMMON_CLK_SI521XX
+ tristate "Clock driver for SkyWorks Si521xx PCIe clock generators"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
+ help
+ This driver supports the SkyWorks Si521xx PCIe clock generator
+ models Si52144/Si52146/Si52147.
+
+config COMMON_CLK_VC3
+ tristate "Clock driver for Renesas VersaClock 3 devices"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
+ help
+ This driver supports the Renesas VersaClock 3 programmable clock
+ generators.
+
config COMMON_CLK_VC5
tristate "Clock driver for IDT VersaClock 5,6 devices"
depends on I2C
@@ -377,15 +441,14 @@ config COMMON_CLK_VC5
This driver supports the IDT VersaClock 5 and VersaClock 6
programmable clock generators.
-config COMMON_CLK_STM32MP135
- def_bool COMMON_CLK && MACH_STM32MP13
- help
- Support for stm32mp135 SoC family clocks
-
-config COMMON_CLK_STM32MP157
- def_bool COMMON_CLK && MACH_STM32MP157
+config COMMON_CLK_VC7
+ tristate "Clock driver for Renesas Versaclock 7 devices"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
help
- Support for stm32mp157 SoC family clocks
+ Renesas Versaclock7 is a family of configurable clock generator
+ and jitter attenuator ICs with fractional and integer dividers.
config COMMON_CLK_STM32F
def_bool COMMON_CLK && (MACH_STM32F429 || MACH_STM32F469 || MACH_STM32F746)
@@ -418,16 +481,36 @@ config COMMON_CLK_BD718XX
config COMMON_CLK_FIXED_MMIO
bool "Clock driver for Memory Mapped Fixed values"
depends on COMMON_CLK && OF
+ depends on HAS_IOMEM
help
Support for Memory Mapped IO Fixed clocks
config COMMON_CLK_K210
bool "Clock driver for the Canaan Kendryte K210 SoC"
- depends on OF && RISCV && SOC_CANAAN
- default SOC_CANAAN
+ depends on OF && RISCV && SOC_CANAAN_K210
+ default SOC_CANAAN_K210
help
Support for the Canaan Kendryte K210 RISC-V SoC clocks.
+config COMMON_CLK_SP7021
+ tristate "Clock driver for Sunplus SP7021 SoC"
+ depends on SOC_SP7021 || COMPILE_TEST
+ default SOC_SP7021
+ help
+ This driver supports the Sunplus SP7021 SoC clocks.
+ It implements SP7021 PLLs/gate.
+ Not all features of the PLL are currently supported
+ by the driver.
+
+config COMMON_CLK_RPMI
+ tristate "Clock driver based on RISC-V RPMI"
+ depends on RISCV || COMPILE_TEST
+ depends on MAILBOX
+ default RISCV
+ help
+ Support for clocks based on the clock service group defined by
+ the RISC-V platform management interface (RPMI) specification.
+
source "drivers/clk/actions/Kconfig"
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/baikal-t1/Kconfig"
@@ -438,10 +521,12 @@ source "drivers/clk/imx/Kconfig"
source "drivers/clk/ingenic/Kconfig"
source "drivers/clk/keystone/Kconfig"
source "drivers/clk/mediatek/Kconfig"
+source "drivers/clk/mmp/Kconfig"
source "drivers/clk/meson/Kconfig"
source "drivers/clk/mstar/Kconfig"
source "drivers/clk/microchip/Kconfig"
source "drivers/clk/mvebu/Kconfig"
+source "drivers/clk/nuvoton/Kconfig"
source "drivers/clk/pistachio/Kconfig"
source "drivers/clk/qcom/Kconfig"
source "drivers/clk/ralink/Kconfig"
@@ -450,11 +535,15 @@ source "drivers/clk/rockchip/Kconfig"
source "drivers/clk/samsung/Kconfig"
source "drivers/clk/sifive/Kconfig"
source "drivers/clk/socfpga/Kconfig"
+source "drivers/clk/sophgo/Kconfig"
+source "drivers/clk/spacemit/Kconfig"
source "drivers/clk/sprd/Kconfig"
source "drivers/clk/starfive/Kconfig"
source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig"
+source "drivers/clk/thead/Kconfig"
+source "drivers/clk/stm32/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/visconti/Kconfig"
@@ -467,14 +556,31 @@ config CLK_KUNIT_TEST
tristate "Basic Clock Framework Kunit Tests" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
+ select DTC
help
Kunit tests for the common clock framework.
+config CLK_FIXED_RATE_KUNIT_TEST
+ tristate "Basic fixed rate clk type KUnit test" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ select DTC
+ help
+ KUnit tests for the basic fixed rate clk type.
+
config CLK_GATE_KUNIT_TEST
tristate "Basic gate type Kunit test" if !KUNIT_ALL_TESTS
depends on KUNIT
+ depends on !S390
default KUNIT_ALL_TESTS
help
Kunit test for the basic clk gate type.
+config CLK_FD_KUNIT_TEST
+ tristate "Basic fractional divider type Kunit test" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Kunit test for the clk-fractional-divider type.
+
endif
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5db170d38d2..61ec08404442 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -2,21 +2,46 @@
# common clock types
obj-$(CONFIG_HAVE_CLK) += clk-devres.o clk-bulk.o clkdev.o
obj-$(CONFIG_COMMON_CLK) += clk.o
-obj-$(CONFIG_CLK_KUNIT_TEST) += clk_test.o
+obj-$(CONFIG_CLK_KUNIT_TEST) += clk-test.o
+clk-test-y := clk_test.o \
+ kunit_clk_assigned_rates_u64_one.dtbo.o \
+ kunit_clk_assigned_rates_u64_one_consumer.dtbo.o \
+ kunit_clk_assigned_rates_u64_multiple.dtbo.o \
+ kunit_clk_assigned_rates_u64_multiple_consumer.dtbo.o \
+ kunit_clk_assigned_rates_multiple.dtbo.o \
+ kunit_clk_assigned_rates_multiple_consumer.dtbo.o \
+ kunit_clk_assigned_rates_null.dtbo.o \
+ kunit_clk_assigned_rates_null_consumer.dtbo.o \
+ kunit_clk_assigned_rates_one.dtbo.o \
+ kunit_clk_assigned_rates_one_consumer.dtbo.o \
+ kunit_clk_assigned_rates_without.dtbo.o \
+ kunit_clk_assigned_rates_without_consumer.dtbo.o \
+ kunit_clk_assigned_rates_zero.dtbo.o \
+ kunit_clk_assigned_rates_zero_consumer.dtbo.o \
+ kunit_clk_hw_get_dev_of_node.dtbo.o \
+ kunit_clk_parent_data_test.dtbo.o
obj-$(CONFIG_COMMON_CLK) += clk-divider.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
+obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST) += clk-fixed-rate-test.o
+clk-fixed-rate-test-y := clk-fixed-rate_test.o kunit_clk_fixed_rate_test.dtbo.o
obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_CLK_GATE_KUNIT_TEST) += clk-gate_test.o
obj-$(CONFIG_COMMON_CLK) += clk-multiplier.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
+obj-$(CONFIG_CLK_FD_KUNIT_TEST) += clk-fractional-divider_test.o
obj-$(CONFIG_COMMON_CLK) += clk-gpio.o
ifeq ($(CONFIG_OF), y)
obj-$(CONFIG_COMMON_CLK) += clk-conf.o
endif
+# KUnit specific helpers
+ifeq ($(CONFIG_COMMON_CLK), y)
+obj-$(CONFIG_KUNIT) += clk_kunit_helpers.o
+endif
+
# hardware specific clock types
# please keep this section sorted lexicographically by file path name
obj-$(CONFIG_COMMON_CLK_APPLE_NCO) += clk-apple-nco.o
@@ -29,8 +54,10 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o
+obj-$(CONFIG_COMMON_CLK_EP93XX) += clk-ep93xx.o
obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
obj-$(CONFIG_COMMON_CLK_EN7523) += clk-en7523.o
+obj-$(CONFIG_COMMON_CLK_EYEQ) += clk-eyeq.o
obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
obj-$(CONFIG_COMMON_CLK_FSL_FLEXSPI) += clk-fsl-flexspi.o
obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
@@ -43,19 +70,23 @@ obj-$(CONFIG_COMMON_CLK_K210) += clk-k210.o
obj-$(CONFIG_LMK04832) += clk-lmk04832.o
obj-$(CONFIG_COMMON_CLK_LAN966X) += clk-lan966x.o
obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o
+obj-$(CONFIG_MACH_LOONGSON32) += clk-loongson1.o
+obj-$(CONFIG_COMMON_CLK_LOONGSON2) += clk-loongson2.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o
obj-$(CONFIG_ARCH_MILBEAUT_M10V) += clk-milbeaut.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o
+obj-$(CONFIG_COMMON_CLK_NPCM8XX) += clk-npcm8xx.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
-obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o
obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-plldig.o
obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
+obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o
+obj-$(CONFIG_COMMON_CLK_RPMI) += clk-rpmi.o
obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
@@ -65,14 +96,18 @@ obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o
obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
+obj-$(CONFIG_COMMON_CLK_SP7021) += clk-sp7021.o
obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o
-obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o
obj-$(CONFIG_COMMON_CLK_TPS68470) += clk-tps68470.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
+obj-$(CONFIG_CLK_TWL) += clk-twl.o
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_COMMON_CLK_RS9_PCIE) += clk-renesas-pcie.o
+obj-$(CONFIG_COMMON_CLK_SI521XX) += clk-si521xx.o
+obj-$(CONFIG_COMMON_CLK_VC3) += clk-versaclock3.o
obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o
+obj-$(CONFIG_COMMON_CLK_VC7) += clk-versaclock7.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
@@ -90,9 +125,7 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-y += imgtec/
obj-y += imx/
obj-y += ingenic/
-obj-$(CONFIG_ARCH_K3) += keystone/
-obj-$(CONFIG_ARCH_KEYSTONE) += keystone/
-obj-$(CONFIG_MACH_LOONGSON32) += loongson1/
+obj-y += keystone/
obj-y += mediatek/
obj-$(CONFIG_ARCH_MESON) += meson/
obj-y += microchip/
@@ -102,6 +135,7 @@ endif
obj-y += mstar/
obj-y += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_ARCH_MA35) += nuvoton/
obj-$(CONFIG_COMMON_CLK_NXP) += nxp/
obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pistachio/
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
@@ -112,14 +146,17 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-y += socfpga/
+obj-y += sophgo/
+obj-y += spacemit/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-y += sprd/
obj-$(CONFIG_ARCH_STI) += st/
obj-$(CONFIG_ARCH_STM32) += stm32/
-obj-$(CONFIG_SOC_STARFIVE) += starfive/
+obj-y += starfive/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-y += sunxi-ng/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
+obj-$(CONFIG_ARCH_THEAD) += thead/
obj-y += ti/
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_U8500) += ux500/
diff --git a/drivers/clk/actions/owl-common.c b/drivers/clk/actions/owl-common.c
index 4de97cc7cb54..b3dded204dc5 100644
--- a/drivers/clk/actions/owl-common.c
+++ b/drivers/clk/actions/owl-common.c
@@ -8,8 +8,6 @@
// Copyright (c) 2018 Linaro Ltd.
// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -20,7 +18,6 @@ static const struct regmap_config owl_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x00cc,
- .fast_io = true,
};
static void owl_clk_set_regmap(const struct owl_clk_desc *desc,
@@ -43,10 +40,8 @@ int owl_clk_regmap_init(struct platform_device *pdev,
{
void __iomem *base;
struct regmap *regmap;
- struct resource *res;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/clk/actions/owl-common.h b/drivers/clk/actions/owl-common.h
index c000a431471e..5768a2e0f6a0 100644
--- a/drivers/clk/actions/owl-common.h
+++ b/drivers/clk/actions/owl-common.h
@@ -12,10 +12,10 @@
#define _OWL_COMMON_H_
#include <linux/clk-provider.h>
-#include <linux/of_platform.h>
#include <linux/regmap.h>
struct device_node;
+struct platform_device;
struct owl_clk_common {
struct regmap *regmap;
@@ -32,7 +32,7 @@ struct owl_clk_desc {
};
static inline struct owl_clk_common *
- hw_to_owl_clk_common(const struct clk_hw *hw)
+ hw_to_owl_clk_common(struct clk_hw *hw)
{
return container_of(hw, struct owl_clk_common, hw);
}
diff --git a/drivers/clk/actions/owl-composite.c b/drivers/clk/actions/owl-composite.c
index 101706e0c66f..00b74f8bc437 100644
--- a/drivers/clk/actions/owl-composite.c
+++ b/drivers/clk/actions/owl-composite.c
@@ -53,13 +53,19 @@ static int owl_comp_is_enabled(struct clk_hw *hw)
return owl_gate_clk_is_enabled(common, &comp->gate_hw);
}
-static long owl_comp_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int owl_comp_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct owl_composite *comp = hw_to_owl_comp(hw);
+ long rate;
- return owl_divider_helper_round_rate(&comp->common, &comp->rate.div_hw,
- rate, parent_rate);
+ rate = owl_divider_helper_round_rate(&comp->common, &comp->rate.div_hw,
+ req->rate, &req->best_parent_rate);
+ if (rate < 0)
+ return rate;
+
+ req->rate = rate;
+ return 0;
}
static unsigned long owl_comp_div_recalc_rate(struct clk_hw *hw,
@@ -80,14 +86,20 @@ static int owl_comp_div_set_rate(struct clk_hw *hw, unsigned long rate,
rate, parent_rate);
}
-static long owl_comp_fact_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int owl_comp_fact_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct owl_composite *comp = hw_to_owl_comp(hw);
+ long rate;
- return owl_factor_helper_round_rate(&comp->common,
- &comp->rate.factor_hw,
- rate, parent_rate);
+ rate = owl_factor_helper_round_rate(&comp->common,
+ &comp->rate.factor_hw,
+ req->rate, &req->best_parent_rate);
+ if (rate < 0)
+ return rate;
+
+ req->rate = rate;
+ return 0;
}
static unsigned long owl_comp_fact_recalc_rate(struct clk_hw *hw,
@@ -110,13 +122,13 @@ static int owl_comp_fact_set_rate(struct clk_hw *hw, unsigned long rate,
rate, parent_rate);
}
-static long owl_comp_fix_fact_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int owl_comp_fix_fact_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct owl_composite *comp = hw_to_owl_comp(hw);
struct clk_fixed_factor *fix_fact_hw = &comp->rate.fix_fact_hw;
- return comp->fix_fact_ops->round_rate(&fix_fact_hw->hw, rate, parent_rate);
+ return comp->fix_fact_ops->determine_rate(&fix_fact_hw->hw, req);
}
static unsigned long owl_comp_fix_fact_recalc_rate(struct clk_hw *hw,
@@ -152,7 +164,7 @@ const struct clk_ops owl_comp_div_ops = {
.is_enabled = owl_comp_is_enabled,
/* div_ops */
- .round_rate = owl_comp_div_round_rate,
+ .determine_rate = owl_comp_div_determine_rate,
.recalc_rate = owl_comp_div_recalc_rate,
.set_rate = owl_comp_div_set_rate,
};
@@ -169,7 +181,7 @@ const struct clk_ops owl_comp_fact_ops = {
.is_enabled = owl_comp_is_enabled,
/* fact_ops */
- .round_rate = owl_comp_fact_round_rate,
+ .determine_rate = owl_comp_fact_determine_rate,
.recalc_rate = owl_comp_fact_recalc_rate,
.set_rate = owl_comp_fact_set_rate,
};
@@ -181,7 +193,7 @@ const struct clk_ops owl_comp_fix_fact_ops = {
.is_enabled = owl_comp_is_enabled,
/* fix_fact_ops */
- .round_rate = owl_comp_fix_fact_round_rate,
+ .determine_rate = owl_comp_fix_fact_determine_rate,
.recalc_rate = owl_comp_fix_fact_recalc_rate,
.set_rate = owl_comp_fix_fact_set_rate,
};
@@ -189,6 +201,7 @@ const struct clk_ops owl_comp_fix_fact_ops = {
const struct clk_ops owl_comp_pass_ops = {
/* mux_ops */
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = owl_comp_get_parent,
.set_parent = owl_comp_set_parent,
diff --git a/drivers/clk/actions/owl-composite.h b/drivers/clk/actions/owl-composite.h
index bca38bf8f218..6d7c6f0c47c8 100644
--- a/drivers/clk/actions/owl-composite.h
+++ b/drivers/clk/actions/owl-composite.h
@@ -108,7 +108,7 @@ struct owl_composite {
}, \
}
-static inline struct owl_composite *hw_to_owl_comp(const struct clk_hw *hw)
+static inline struct owl_composite *hw_to_owl_comp(struct clk_hw *hw)
{
struct owl_clk_common *common = hw_to_owl_clk_common(hw);
diff --git a/drivers/clk/actions/owl-divider.c b/drivers/clk/actions/owl-divider.c
index cddac00fe324..118f1393c678 100644
--- a/drivers/clk/actions/owl-divider.c
+++ b/drivers/clk/actions/owl-divider.c
@@ -23,13 +23,16 @@ long owl_divider_helper_round_rate(struct owl_clk_common *common,
div_hw->div_flags);
}
-static long owl_divider_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int owl_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct owl_divider *div = hw_to_owl_divider(hw);
- return owl_divider_helper_round_rate(&div->common, &div->div_hw,
- rate, parent_rate);
+ req->rate = owl_divider_helper_round_rate(&div->common, &div->div_hw,
+ req->rate,
+ &req->best_parent_rate);
+
+ return 0;
}
unsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common,
@@ -89,6 +92,6 @@ static int owl_divider_set_rate(struct clk_hw *hw, unsigned long rate,
const struct clk_ops owl_divider_ops = {
.recalc_rate = owl_divider_recalc_rate,
- .round_rate = owl_divider_round_rate,
+ .determine_rate = owl_divider_determine_rate,
.set_rate = owl_divider_set_rate,
};
diff --git a/drivers/clk/actions/owl-divider.h b/drivers/clk/actions/owl-divider.h
index 083be6d80954..d76f58782c52 100644
--- a/drivers/clk/actions/owl-divider.h
+++ b/drivers/clk/actions/owl-divider.h
@@ -49,7 +49,7 @@ struct owl_divider {
}, \
}
-static inline struct owl_divider *hw_to_owl_divider(const struct clk_hw *hw)
+static inline struct owl_divider *hw_to_owl_divider(struct clk_hw *hw)
{
struct owl_clk_common *common = hw_to_owl_clk_common(hw);
diff --git a/drivers/clk/actions/owl-factor.c b/drivers/clk/actions/owl-factor.c
index 64f316cf7cfc..12f41f6bacd6 100644
--- a/drivers/clk/actions/owl-factor.c
+++ b/drivers/clk/actions/owl-factor.c
@@ -130,14 +130,16 @@ long owl_factor_helper_round_rate(struct owl_clk_common *common,
return *parent_rate * mul / div;
}
-static long owl_factor_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int owl_factor_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct owl_factor *factor = hw_to_owl_factor(hw);
struct owl_factor_hw *factor_hw = &factor->factor_hw;
- return owl_factor_helper_round_rate(&factor->common, factor_hw,
- rate, parent_rate);
+ req->rate = owl_factor_helper_round_rate(&factor->common, factor_hw,
+ req->rate, &req->best_parent_rate);
+
+ return 0;
}
unsigned long owl_factor_helper_recalc_rate(struct owl_clk_common *common,
@@ -214,7 +216,7 @@ static int owl_factor_set_rate(struct clk_hw *hw, unsigned long rate,
}
const struct clk_ops owl_factor_ops = {
- .round_rate = owl_factor_round_rate,
+ .determine_rate = owl_factor_determine_rate,
.recalc_rate = owl_factor_recalc_rate,
.set_rate = owl_factor_set_rate,
};
diff --git a/drivers/clk/actions/owl-factor.h b/drivers/clk/actions/owl-factor.h
index 04b89cbfdccb..24c704d40925 100644
--- a/drivers/clk/actions/owl-factor.h
+++ b/drivers/clk/actions/owl-factor.h
@@ -57,7 +57,7 @@ struct owl_factor {
#define div_mask(d) ((1 << ((d)->width)) - 1)
-static inline struct owl_factor *hw_to_owl_factor(const struct clk_hw *hw)
+static inline struct owl_factor *hw_to_owl_factor(struct clk_hw *hw)
{
struct owl_clk_common *common = hw_to_owl_clk_common(hw);
diff --git a/drivers/clk/actions/owl-gate.h b/drivers/clk/actions/owl-gate.h
index c2f161c93fda..ac458d4385ee 100644
--- a/drivers/clk/actions/owl-gate.h
+++ b/drivers/clk/actions/owl-gate.h
@@ -56,7 +56,7 @@ struct owl_gate {
}, \
} \
-static inline struct owl_gate *hw_to_owl_gate(const struct clk_hw *hw)
+static inline struct owl_gate *hw_to_owl_gate(struct clk_hw *hw)
{
struct owl_clk_common *common = hw_to_owl_clk_common(hw);
diff --git a/drivers/clk/actions/owl-mux.h b/drivers/clk/actions/owl-mux.h
index 53b9ab665294..dc0ecc2d5e10 100644
--- a/drivers/clk/actions/owl-mux.h
+++ b/drivers/clk/actions/owl-mux.h
@@ -44,7 +44,7 @@ struct owl_mux {
}, \
}
-static inline struct owl_mux *hw_to_owl_mux(const struct clk_hw *hw)
+static inline struct owl_mux *hw_to_owl_mux(struct clk_hw *hw)
{
struct owl_clk_common *common = hw_to_owl_clk_common(hw);
diff --git a/drivers/clk/actions/owl-pll.c b/drivers/clk/actions/owl-pll.c
index 155f313986b4..869690b79cc1 100644
--- a/drivers/clk/actions/owl-pll.c
+++ b/drivers/clk/actions/owl-pll.c
@@ -56,8 +56,8 @@ static const struct clk_pll_table *_get_pll_table(
return table;
}
-static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int owl_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct owl_pll *pll = hw_to_owl_pll(hw);
struct owl_pll_hw *pll_hw = &pll->pll_hw;
@@ -65,17 +65,24 @@ static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate,
u32 mul;
if (pll_hw->table) {
- clkt = _get_pll_table(pll_hw->table, rate);
- return clkt->rate;
+ clkt = _get_pll_table(pll_hw->table, req->rate);
+ req->rate = clkt->rate;
+
+ return 0;
}
/* fixed frequency */
- if (pll_hw->width == 0)
- return pll_hw->bfreq;
+ if (pll_hw->width == 0) {
+ req->rate = pll_hw->bfreq;
- mul = owl_pll_calculate_mul(pll_hw, rate);
+ return 0;
+ }
+
+ mul = owl_pll_calculate_mul(pll_hw, req->rate);
- return pll_hw->bfreq * mul;
+ req->rate = pll_hw->bfreq * mul;
+
+ return 0;
}
static unsigned long owl_pll_recalc_rate(struct clk_hw *hw,
@@ -188,7 +195,7 @@ const struct clk_ops owl_pll_ops = {
.enable = owl_pll_enable,
.disable = owl_pll_disable,
.is_enabled = owl_pll_is_enabled,
- .round_rate = owl_pll_round_rate,
+ .determine_rate = owl_pll_determine_rate,
.recalc_rate = owl_pll_recalc_rate,
.set_rate = owl_pll_set_rate,
};
diff --git a/drivers/clk/actions/owl-pll.h b/drivers/clk/actions/owl-pll.h
index 78e5fc360b03..58e19f1ade43 100644
--- a/drivers/clk/actions/owl-pll.h
+++ b/drivers/clk/actions/owl-pll.h
@@ -98,7 +98,7 @@ struct owl_pll {
#define mul_mask(m) ((1 << ((m)->width)) - 1)
-static inline struct owl_pll *hw_to_owl_pll(const struct clk_hw *hw)
+static inline struct owl_pll *hw_to_owl_pll(struct clk_hw *hw)
{
struct owl_clk_common *common = hw_to_owl_clk_common(hw);
diff --git a/drivers/clk/analogbits/Kconfig b/drivers/clk/analogbits/Kconfig
index 1e291b185438..7d73db0fcd49 100644
--- a/drivers/clk/analogbits/Kconfig
+++ b/drivers/clk/analogbits/Kconfig
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
config CLK_ANALOGBITS_WRPLL_CLN28HPC
- bool
+ tristate
diff --git a/drivers/clk/analogbits/wrpll-cln28hpc.c b/drivers/clk/analogbits/wrpll-cln28hpc.c
index 09ca82356399..9d178afc73bd 100644
--- a/drivers/clk/analogbits/wrpll-cln28hpc.c
+++ b/drivers/clk/analogbits/wrpll-cln28hpc.c
@@ -28,6 +28,7 @@
#include <linux/math64.h>
#include <linux/math.h>
#include <linux/minmax.h>
+#include <linux/module.h>
#include <linux/clk/analogbits-wrpll-cln28hpc.h>
@@ -291,7 +292,7 @@ int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
vco = vco_pre * f;
}
- delta = abs(target_rate - vco);
+ delta = abs(target_vco_rate - vco);
if (delta < best_delta) {
best_delta = delta;
best_r = r;
@@ -312,6 +313,7 @@ int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
return 0;
}
+EXPORT_SYMBOL_GPL(wrpll_configure_for_rate);
/**
* wrpll_calc_output_rate() - calculate the PLL's target output rate
@@ -349,6 +351,7 @@ unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,
return n;
}
+EXPORT_SYMBOL_GPL(wrpll_calc_output_rate);
/**
* wrpll_calc_max_lock_us() - return the time for the PLL to lock
@@ -366,3 +369,8 @@ unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c)
{
return MAX_LOCK_US;
}
+EXPORT_SYMBOL_GPL(wrpll_calc_max_lock_us);
+
+MODULE_AUTHOR("Paul Walmsley <paul.walmsley@sifive.com>");
+MODULE_DESCRIPTION("Analog Bits Wide-Range PLL library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 79301e1c1c36..9128a06b860d 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,7 +3,7 @@
# Makefile for at91 specific clk
#
-obj-y += pmc.o sckc.o dt-compat.o
+obj-y += pmc.o sckc.o
obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
obj-y += clk-system.o clk-peripheral.o clk-programmable.o
@@ -15,12 +15,14 @@ obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o
obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o
-obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o
-obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o
-obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o
-obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o
+obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o dt-compat.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o dt-compat.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o dt-compat.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o dt-compat.o
obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
-obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
-obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
-obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
+obj-$(CONFIG_SOC_SAM9X7) += sam9x7.o
+obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o dt-compat.o
+obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o dt-compat.o
+obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o dt-compat.o
+obj-$(CONFIG_SOC_SAMA7D65) += sama7d65.o
obj-$(CONFIG_SOC_SAMA7G5) += sama7g5.o
diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c
index b174f727a8ef..3f19e737ae4d 100644
--- a/drivers/clk/at91/at91rm9200.c
+++ b/drivers/clk/at91/at91rm9200.c
@@ -40,7 +40,7 @@ static const struct clk_pll_characteristics rm9200_pll_characteristics = {
};
static const struct sck at91rm9200_systemck[] = {
- { .n = "udpck", .p = "usbck", .id = 2 },
+ { .n = "udpck", .p = "usbck", .id = 1 },
{ .n = "uhpck", .p = "usbck", .id = 4 },
{ .n = "pck0", .p = "prog0", .id = 8 },
{ .n = "pck1", .p = "prog1", .id = 9 },
@@ -108,12 +108,12 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
- hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -140,7 +140,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91rm9200_master_layout,
&rm9200_mck_characteristics,
&rm9200_mck_lock);
@@ -148,7 +148,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91rm9200_master_layout,
&rm9200_mck_characteristics,
&rm9200_mck_lock, CLK_SET_RATE_GATE, 0);
@@ -171,7 +171,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 4, i,
+ parent_names, NULL, 4, i,
&at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -182,8 +182,8 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) {
hw = at91_clk_register_system(regmap, at91rm9200_systemck[i].n,
- at91rm9200_systemck[i].p,
- at91rm9200_systemck[i].id);
+ at91rm9200_systemck[i].p, NULL,
+ at91rm9200_systemck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -193,7 +193,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) {
hw = at91_clk_register_peripheral(regmap,
at91rm9200_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
at91rm9200_periphck[i].id);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c
index 11550e50cd9f..0799a13060ea 100644
--- a/drivers/clk/at91/at91sam9260.c
+++ b/drivers/clk/at91/at91sam9260.c
@@ -363,12 +363,12 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
- hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -416,7 +416,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91rm9200_master_layout,
data->mck_characteristics,
&at91sam9260_mck_lock);
@@ -424,7 +424,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91rm9200_master_layout,
data->mck_characteristics,
&at91sam9260_mck_lock,
@@ -448,7 +448,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 4, i,
+ parent_names, NULL, 4, i,
&at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -459,8 +459,8 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
for (i = 0; i < data->num_sck; i++) {
hw = at91_clk_register_system(regmap, data->sck[i].n,
- data->sck[i].p,
- data->sck[i].id);
+ data->sck[i].p, NULL,
+ data->sck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -470,7 +470,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
for (i = 0; i < data->num_pck; i++) {
hw = at91_clk_register_peripheral(regmap,
data->pck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
data->pck[i].id);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c
index 8c9344451f46..f45a7b80f7d8 100644
--- a/drivers/clk/at91/at91sam9g45.c
+++ b/drivers/clk/at91/at91sam9g45.c
@@ -40,9 +40,14 @@ static const struct clk_pll_characteristics plla_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9g45_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "pck0", .p = "prog0", .id = 8 },
{ .n = "pck1", .p = "prog1", .id = 9 },
@@ -118,12 +123,12 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
- hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -140,7 +145,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
at91sam9g45_pmc->chws[PMC_PLLACK] = hw;
- hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -151,7 +156,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91rm9200_master_layout,
&mck_characteristics,
&at91sam9g45_mck_lock);
@@ -159,7 +164,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91rm9200_master_layout,
&mck_characteristics,
&at91sam9g45_mck_lock,
@@ -186,7 +191,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, NULL, 5, i,
&at91sam9g45_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -197,8 +202,9 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9g45_systemck[i].n,
- at91sam9g45_systemck[i].p,
- at91sam9g45_systemck[i].id);
+ at91sam9g45_systemck[i].p, NULL,
+ at91sam9g45_systemck[i].id,
+ at91sam9g45_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -208,7 +214,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) {
hw = at91_clk_register_peripheral(regmap,
at91sam9g45_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
at91sam9g45_periphck[i].id);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
index 0bb19400d199..751786184ae2 100644
--- a/drivers/clk/at91/at91sam9n12.c
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -54,9 +54,14 @@ static const struct clk_pll_characteristics pllb_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9n12_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "lcdck", .p = "masterck_div", .id = 3 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "udpck", .p = "usbck", .id = 7 },
@@ -142,14 +147,14 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
if (IS_ERR(hw))
goto err_free;
@@ -178,7 +183,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
parent_names[2] = "plladivck";
parent_names[3] = "pllbck";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91sam9x5_master_layout,
&mck_characteristics,
&at91sam9n12_mck_lock);
@@ -186,7 +191,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91sam9x5_master_layout,
&mck_characteristics,
&at91sam9n12_mck_lock,
@@ -211,7 +216,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, NULL, 5, i,
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -222,8 +227,9 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9n12_systemck[i].n,
- at91sam9n12_systemck[i].p,
- at91sam9n12_systemck[i].id);
+ at91sam9n12_systemck[i].p, NULL,
+ at91sam9n12_systemck[i].id,
+ at91sam9n12_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -234,9 +240,9 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&at91sam9n12_pcr_layout,
at91sam9n12_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
at91sam9n12_periphck[i].id,
- &range, INT_MIN);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c
index b992137bab02..969f809e7d65 100644
--- a/drivers/clk/at91/at91sam9rl.c
+++ b/drivers/clk/at91/at91sam9rl.c
@@ -95,7 +95,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
if (!at91sam9rl_pmc)
return;
- hw = at91_clk_register_rm9200_main(regmap, "mainck", mainxtal_name);
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", mainxtal_name, NULL);
if (IS_ERR(hw))
goto err_free;
@@ -109,7 +109,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
at91sam9rl_pmc->chws[PMC_PLLACK] = hw;
- hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -120,7 +120,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
parent_names[2] = "pllack";
parent_names[3] = "utmick";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91rm9200_master_layout,
&sam9rl_mck_characteristics,
&sam9rl_mck_lock);
@@ -128,7 +128,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91rm9200_master_layout,
&sam9rl_mck_characteristics,
&sam9rl_mck_lock, CLK_SET_RATE_GATE, 0);
@@ -148,7 +148,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, NULL, 5, i,
&at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -159,8 +159,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9rl_systemck[i].n,
- at91sam9rl_systemck[i].p,
- at91sam9rl_systemck[i].id);
+ at91sam9rl_systemck[i].p, NULL,
+ at91sam9rl_systemck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -170,7 +170,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(at91sam9rl_periphck); i++) {
hw = at91_clk_register_peripheral(regmap,
at91sam9rl_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
at91sam9rl_periphck[i].id);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
index 3857db2e144b..3b801d12fac0 100644
--- a/drivers/clk/at91/at91sam9x5.c
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -41,9 +41,14 @@ static const struct clk_pll_characteristics plla_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9x5_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "smdck", .p = "smdclk", .id = 4 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "udpck", .p = "usbck", .id = 7 },
@@ -164,14 +169,14 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
if (IS_ERR(hw))
goto err_free;
@@ -188,7 +193,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
at91sam9x5_pmc->chws[PMC_PLLACK] = hw;
- hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -199,14 +204,14 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock);
if (IS_ERR(hw))
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE, 0);
@@ -236,7 +241,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, NULL, 5, i,
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -247,8 +252,9 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n,
- at91sam9x5_systemck[i].p,
- at91sam9x5_systemck[i].id);
+ at91sam9x5_systemck[i].p, NULL,
+ at91sam9x5_systemck[i].id,
+ at91sam9x5_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -256,7 +262,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
}
if (has_lcdck) {
- hw = at91_clk_register_system(regmap, "lcdck", "masterck_div", 3);
+ hw = at91_clk_register_system(regmap, "lcdck", "masterck_div",
+ NULL, 3, 0);
if (IS_ERR(hw))
goto err_free;
@@ -267,9 +274,9 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&at91sam9x5_pcr_layout,
at91sam9x5_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
at91sam9x5_periphck[i].id,
- &range, INT_MIN);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
@@ -280,9 +287,9 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&at91sam9x5_pcr_layout,
extra_pcks[i].n,
- "masterck_div",
+ "masterck_div", NULL,
extra_pcks[i].id,
- &range, INT_MIN);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
index a92da64c12e1..bf9b635ac9d6 100644
--- a/drivers/clk/at91/clk-audio-pll.c
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -270,8 +270,8 @@ static int clk_audio_pll_frac_determine_rate(struct clk_hw *hw,
return 0;
}
-static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_audio_pll_pad_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_hw *pclk = clk_hw_get_parent(hw);
long best_rate = -EINVAL;
@@ -283,7 +283,7 @@ static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
int best_diff = -1;
pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
- rate, *parent_rate);
+ req->rate, req->best_parent_rate);
/*
* Rate divisor is actually made of two different divisors, multiplied
@@ -304,12 +304,12 @@ static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
continue;
best_parent_rate = clk_hw_round_rate(pclk,
- rate * tmp_qd * div);
+ req->rate * tmp_qd * div);
tmp_rate = best_parent_rate / (div * tmp_qd);
- tmp_diff = abs(rate - tmp_rate);
+ tmp_diff = abs(req->rate - tmp_rate);
if (best_diff < 0 || best_diff > tmp_diff) {
- *parent_rate = best_parent_rate;
+ req->best_parent_rate = best_parent_rate;
best_rate = tmp_rate;
best_diff = tmp_diff;
}
@@ -318,11 +318,13 @@ static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
__func__, best_rate, best_parent_rate);
- return best_rate;
+ req->rate = best_rate;
+
+ return 0;
}
-static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_audio_pll_pmc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_hw *pclk = clk_hw_get_parent(hw);
long best_rate = -EINVAL;
@@ -333,20 +335,20 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
int best_diff = -1;
pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
- rate, *parent_rate);
+ req->rate, req->best_parent_rate);
- if (!rate)
+ if (!req->rate)
return 0;
best_parent_rate = clk_round_rate(pclk->clk, 1);
- div = max(best_parent_rate / rate, 1UL);
+ div = max(best_parent_rate / req->rate, 1UL);
for (; div <= AUDIO_PLL_QDPMC_MAX; div++) {
- best_parent_rate = clk_round_rate(pclk->clk, rate * div);
+ best_parent_rate = clk_round_rate(pclk->clk, req->rate * div);
tmp_rate = best_parent_rate / div;
- tmp_diff = abs(rate - tmp_rate);
+ tmp_diff = abs(req->rate - tmp_rate);
if (best_diff < 0 || best_diff > tmp_diff) {
- *parent_rate = best_parent_rate;
+ req->best_parent_rate = best_parent_rate;
best_rate = tmp_rate;
best_diff = tmp_diff;
tmp_qd = div;
@@ -356,9 +358,11 @@ static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
}
pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
- __func__, best_rate, *parent_rate, tmp_qd - 1);
+ __func__, best_rate, req->best_parent_rate, tmp_qd - 1);
+
+ req->rate = best_rate;
- return best_rate;
+ return 0;
}
static int clk_audio_pll_frac_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -436,7 +440,7 @@ static const struct clk_ops audio_pll_pad_ops = {
.enable = clk_audio_pll_pad_enable,
.disable = clk_audio_pll_pad_disable,
.recalc_rate = clk_audio_pll_pad_recalc_rate,
- .round_rate = clk_audio_pll_pad_round_rate,
+ .determine_rate = clk_audio_pll_pad_determine_rate,
.set_rate = clk_audio_pll_pad_set_rate,
};
@@ -444,7 +448,7 @@ static const struct clk_ops audio_pll_pmc_ops = {
.enable = clk_audio_pll_pmc_enable,
.disable = clk_audio_pll_pmc_disable,
.recalc_rate = clk_audio_pll_pmc_recalc_rate,
- .round_rate = clk_audio_pll_pmc_round_rate,
+ .determine_rate = clk_audio_pll_pmc_determine_rate,
.set_rate = clk_audio_pll_pmc_set_rate,
};
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index d429ba52a719..4b4edeecc889 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -136,7 +136,6 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
{
struct clk_generated *gck = to_clk_generated(hw);
struct clk_hw *parent = NULL;
- struct clk_rate_request req_parent = *req;
long best_rate = -EINVAL;
unsigned long min_rate, parent_rate;
int best_diff = -1;
@@ -192,7 +191,9 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
goto end;
for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
- req_parent.rate = req->rate * div;
+ struct clk_rate_request req_parent;
+
+ clk_hw_forward_rate_request(hw, req, parent, &req_parent, req->rate * div);
if (__clk_determine_rate(parent, &req_parent))
continue;
clk_generated_best_diff(req, parent, req_parent.rate, div,
@@ -318,22 +319,29 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
+ struct clk_hw **parent_hws,
u32 *mux_table, u8 num_parents, u8 id,
const struct clk_range *range,
int chg_pid)
{
struct clk_generated *gck;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
+ if (!(parent_names || parent_hws))
+ return ERR_PTR(-ENOMEM);
+
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &generated_ops;
- init.parent_names = parent_names;
+ if (parent_hws)
+ init.parent_hws = (const struct clk_hw **)parent_hws;
+ else
+ init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
if (chg_pid >= 0)
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
index 1e6c12eeda10..a9aa93b5a870 100644
--- a/drivers/clk/at91/clk-h32mx.c
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -40,21 +40,32 @@ static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_sama5d4_h32mx_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long div;
- if (rate > *parent_rate)
- return *parent_rate;
- div = *parent_rate / 2;
- if (rate < div)
- return div;
+ if (req->rate > req->best_parent_rate) {
+ req->rate = req->best_parent_rate;
- if (rate - div < *parent_rate - rate)
- return div;
+ return 0;
+ }
+ div = req->best_parent_rate / 2;
+ if (req->rate < div) {
+ req->rate = div;
+
+ return 0;
+ }
+
+ if (req->rate - div < req->best_parent_rate - req->rate) {
+ req->rate = div;
- return *parent_rate;
+ return 0;
+ }
+
+ req->rate = req->best_parent_rate;
+
+ return 0;
}
static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -77,7 +88,7 @@ static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops h32mx_ops = {
.recalc_rate = clk_sama5d4_h32mx_recalc_rate,
- .round_rate = clk_sama5d4_h32mx_round_rate,
+ .determine_rate = clk_sama5d4_h32mx_determine_rate,
.set_rate = clk_sama5d4_h32mx_set_rate,
};
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 8601b27c1ae0..9b462becc693 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -152,14 +152,15 @@ struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap,
const char *name,
const char *parent_name,
+ struct clk_parent_data *parent_data,
bool bypass)
{
struct clk_main_osc *osc;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
- if (!name || !parent_name)
+ if (!name || !(parent_name || parent_data))
return ERR_PTR(-EINVAL);
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
@@ -168,7 +169,10 @@ at91_clk_register_main_osc(struct regmap *regmap,
init.name = name;
init.ops = &main_osc_ops;
- init.parent_names = &parent_name;
+ if (parent_data)
+ init.parent_data = (const struct clk_parent_data *)parent_data;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = CLK_IGNORE_UNUSED;
@@ -397,17 +401,18 @@ static const struct clk_ops rm9200_main_ops = {
struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
- const char *parent_name)
+ const char *parent_name,
+ struct clk_hw *parent_hw)
{
struct clk_rm9200_main *clkmain;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
if (!name)
return ERR_PTR(-EINVAL);
- if (!parent_name)
+ if (!(parent_name || parent_hw))
return ERR_PTR(-EINVAL);
clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
@@ -416,7 +421,10 @@ at91_clk_register_rm9200_main(struct regmap *regmap,
init.name = name;
init.ops = &rm9200_main_ops;
- init.parent_names = &parent_name;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
@@ -533,6 +541,7 @@ static const struct clk_ops sam9x5_main_ops = {
.prepare = clk_sam9x5_main_prepare,
.is_prepared = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
.save_context = clk_sam9x5_main_save_context,
@@ -543,10 +552,11 @@ struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap,
const char *name,
const char **parent_names,
+ struct clk_hw **parent_hws,
int num_parents)
{
struct clk_sam9x5_main *clkmain;
- struct clk_init_data init;
+ struct clk_init_data init = {};
unsigned int status;
struct clk_hw *hw;
int ret;
@@ -554,7 +564,7 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
if (!name)
return ERR_PTR(-EINVAL);
- if (!parent_names || !num_parents)
+ if (!(parent_hws || parent_names) || !num_parents)
return ERR_PTR(-EINVAL);
clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
@@ -563,7 +573,10 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
init.name = name;
init.ops = &sam9x5_main_ops;
- init.parent_names = parent_names;
+ if (parent_hws)
+ init.parent_hws = (const struct clk_hw **)parent_hws;
+ else
+ init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_PARENT_GATE;
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 164e2959c7cf..d5ea2069ec83 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -20,7 +20,7 @@
#define PMC_MCR_CSS_SHIFT (16)
-#define MASTER_MAX_ID 4
+#define MASTER_MAX_ID 9
#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
@@ -473,18 +473,19 @@ static struct clk_hw * __init
at91_clk_register_master_internal(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names,
+ struct clk_hw **parent_hws,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
const struct clk_ops *ops, spinlock_t *lock, u32 flags)
{
struct clk_master *master;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
unsigned int mckr;
unsigned long irqflags;
int ret;
- if (!name || !num_parents || !parent_names || !lock)
+ if (!name || !num_parents || !(parent_names || parent_hws) || !lock)
return ERR_PTR(-EINVAL);
master = kzalloc(sizeof(*master), GFP_KERNEL);
@@ -493,7 +494,10 @@ at91_clk_register_master_internal(struct regmap *regmap,
init.name = name;
init.ops = ops;
- init.parent_names = parent_names;
+ if (parent_hws)
+ init.parent_hws = (const struct clk_hw **)parent_hws;
+ else
+ init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = flags;
@@ -527,12 +531,13 @@ struct clk_hw * __init
at91_clk_register_master_pres(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names,
+ struct clk_hw **parent_hws,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
spinlock_t *lock)
{
return at91_clk_register_master_internal(regmap, name, num_parents,
- parent_names, layout,
+ parent_names, parent_hws, layout,
characteristics,
&master_pres_ops,
lock, CLK_SET_RATE_GATE);
@@ -541,7 +546,7 @@ at91_clk_register_master_pres(struct regmap *regmap,
struct clk_hw * __init
at91_clk_register_master_div(struct regmap *regmap,
const char *name, const char *parent_name,
- const struct clk_master_layout *layout,
+ struct clk_hw *parent_hw, const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
spinlock_t *lock, u32 flags, u32 safe_div)
{
@@ -554,7 +559,8 @@ at91_clk_register_master_div(struct regmap *regmap,
ops = &master_div_ops_chg;
hw = at91_clk_register_master_internal(regmap, name, 1,
- &parent_name, layout,
+ parent_name ? &parent_name : NULL,
+ parent_hw ? &parent_hw : NULL, layout,
characteristics, ops,
lock, flags);
@@ -574,6 +580,9 @@ clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
{
struct clk_master *master = to_clk_master(hw);
+ if (master->div == MASTER_PRES_MAX)
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, 3);
+
return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
}
@@ -581,7 +590,6 @@ static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_master *master = to_clk_master(hw);
- struct clk_rate_request req_parent = *req;
struct clk_hw *parent;
long best_rate = LONG_MIN, best_diff = LONG_MIN;
unsigned long parent_rate;
@@ -618,11 +626,15 @@ static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
goto end;
for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
+ struct clk_rate_request req_parent;
+ unsigned long req_rate;
+
if (div == MASTER_PRES_MAX)
- req_parent.rate = req->rate * 3;
+ req_rate = req->rate * 3;
else
- req_parent.rate = req->rate << div;
+ req_rate = req->rate << div;
+ clk_hw_forward_rate_request(hw, req, parent, &req_parent, req_rate);
if (__clk_determine_rate(parent, &req_parent))
continue;
@@ -803,18 +815,19 @@ struct clk_hw * __init
at91_clk_sama7g5_register_master(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names,
+ struct clk_hw **parent_hws,
u32 *mux_table,
spinlock_t *lock, u8 id,
bool critical, int chg_pid)
{
struct clk_master *master;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
unsigned long flags;
unsigned int val;
int ret;
- if (!name || !num_parents || !parent_names || !mux_table ||
+ if (!name || !num_parents || !(parent_names || parent_hws) || !mux_table ||
!lock || id > MASTER_MAX_ID)
return ERR_PTR(-EINVAL);
@@ -824,7 +837,10 @@ at91_clk_sama7g5_register_master(struct regmap *regmap,
init.name = name;
init.ops = &sama7g5_master_ops;
- init.parent_names = parent_names;
+ if (parent_hws)
+ init.parent_hws = (const struct clk_hw **)parent_hws;
+ else
+ init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
if (chg_pid >= 0)
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index e14fa5ac734c..e7208c47268b 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -3,6 +3,7 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
@@ -97,14 +98,15 @@ static const struct clk_ops peripheral_ops = {
struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
- const char *parent_name, u32 id)
+ const char *parent_name, struct clk_hw *parent_hw,
+ u32 id)
{
struct clk_peripheral *periph;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
- if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
+ if (!name || !(parent_name || parent_hw) || id > PERIPHERAL_ID_MAX)
return ERR_PTR(-EINVAL);
periph = kzalloc(sizeof(*periph), GFP_KERNEL);
@@ -113,7 +115,10 @@ at91_clk_register_peripheral(struct regmap *regmap, const char *name,
init.name = name;
init.ops = &peripheral_ops;
- init.parent_names = &parent_name;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
@@ -269,15 +274,17 @@ static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
struct clk_hw *parent = clk_hw_get_parent(hw);
- struct clk_rate_request req_parent = *req;
unsigned long parent_rate = clk_hw_get_rate(parent);
unsigned long tmp_rate;
long best_rate = LONG_MIN;
long best_diff = LONG_MIN;
u32 shift;
- if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
- return parent_rate;
+ if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+ req->rate = parent_rate;
+
+ return 0;
+ }
/* Fist step: check the available dividers. */
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
@@ -302,8 +309,9 @@ static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
goto end;
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
- req_parent.rate = req->rate << shift;
+ struct clk_rate_request req_parent;
+ clk_hw_forward_rate_request(hw, req, parent, &req_parent, req->rate << shift);
if (__clk_determine_rate(parent, &req_parent))
continue;
@@ -328,50 +336,57 @@ end:
return 0;
}
-static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long *parent_rate)
+static int clk_sam9x5_peripheral_no_parent_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int shift = 0;
unsigned long best_rate;
unsigned long best_diff;
- unsigned long cur_rate = *parent_rate;
+ unsigned long cur_rate = req->best_parent_rate;
unsigned long cur_diff;
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
- if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
- return *parent_rate;
+ if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+ req->rate = req->best_parent_rate;
+
+ return 0;
+ }
if (periph->range.max) {
for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
- cur_rate = *parent_rate >> shift;
+ cur_rate = req->best_parent_rate >> shift;
if (cur_rate <= periph->range.max)
break;
}
}
- if (rate >= cur_rate)
- return cur_rate;
+ if (req->rate >= cur_rate) {
+ req->rate = cur_rate;
+
+ return 0;
+ }
- best_diff = cur_rate - rate;
+ best_diff = cur_rate - req->rate;
best_rate = cur_rate;
for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
- cur_rate = *parent_rate >> shift;
- if (cur_rate < rate)
- cur_diff = rate - cur_rate;
+ cur_rate = req->best_parent_rate >> shift;
+ if (cur_rate < req->rate)
+ cur_diff = req->rate - cur_rate;
else
- cur_diff = cur_rate - rate;
+ cur_diff = cur_rate - req->rate;
if (cur_diff < best_diff) {
best_diff = cur_diff;
best_rate = cur_rate;
}
- if (!best_diff || cur_rate < rate)
+ if (!best_diff || cur_rate < req->rate)
break;
}
- return best_rate;
+ req->rate = best_rate;
+
+ return 0;
}
static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
@@ -423,7 +438,7 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.disable = clk_sam9x5_peripheral_disable,
.is_enabled = clk_sam9x5_peripheral_is_enabled,
.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
- .round_rate = clk_sam9x5_peripheral_round_rate,
+ .determine_rate = clk_sam9x5_peripheral_no_parent_determine_rate,
.set_rate = clk_sam9x5_peripheral_set_rate,
.save_context = clk_sam9x5_peripheral_save_context,
.restore_context = clk_sam9x5_peripheral_restore_context,
@@ -444,15 +459,16 @@ struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
+ struct clk_hw *parent_hw,
u32 id, const struct clk_range *range,
- int chg_pid)
+ int chg_pid, unsigned long flags)
{
struct clk_sam9x5_peripheral *periph;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
- if (!name || !parent_name)
+ if (!name || !(parent_name || parent_hw))
return ERR_PTR(-EINVAL);
periph = kzalloc(sizeof(*periph), GFP_KERNEL);
@@ -460,14 +476,17 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
return ERR_PTR(-ENOMEM);
init.name = name;
- init.parent_names = &parent_name;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
+ init.flags = flags;
if (chg_pid < 0) {
- init.flags = 0;
init.ops = &sam9x5_peripheral_ops;
} else {
- init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT;
+ init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
init.ops = &sam9x5_peripheral_chg_ops;
}
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index 249d6a53cedf..5c5f7398effe 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -231,13 +231,15 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
return bestrate;
}
-static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_pll *pll = to_clk_pll(hw);
- return clk_pll_get_best_div_mul(pll, rate, *parent_rate,
- NULL, NULL, NULL);
+ req->rate = clk_pll_get_best_div_mul(pll, req->rate, req->best_parent_rate,
+ NULL, NULL, NULL);
+
+ return 0;
}
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -302,7 +304,7 @@ static const struct clk_ops pll_ops = {
.unprepare = clk_pll_unprepare,
.is_prepared = clk_pll_is_prepared,
.recalc_rate = clk_pll_recalc_rate,
- .round_rate = clk_pll_round_rate,
+ .determine_rate = clk_pll_determine_rate,
.set_rate = clk_pll_set_rate,
.save_context = clk_pll_save_context,
.restore_context = clk_pll_restore_context,
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
index ba3a1839a96d..3ac09fecc54e 100644
--- a/drivers/clk/at91/clk-plldiv.c
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -33,21 +33,33 @@ static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_plldiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long div;
- if (rate > *parent_rate)
- return *parent_rate;
- div = *parent_rate / 2;
- if (rate < div)
- return div;
+ if (req->rate > req->best_parent_rate) {
+ req->rate = req->best_parent_rate;
- if (rate - div < *parent_rate - rate)
- return div;
+ return 0;
+ }
+
+ div = req->best_parent_rate / 2;
+ if (req->rate < div) {
+ req->rate = div;
+
+ return 0;
+ }
+
+ if (req->rate - div < req->best_parent_rate - req->rate) {
+ req->rate = div;
- return *parent_rate;
+ return 0;
+ }
+
+ req->rate = req->best_parent_rate;
+
+ return 0;
}
static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -66,7 +78,7 @@ static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops plldiv_ops = {
.recalc_rate = clk_plldiv_recalc_rate,
- .round_rate = clk_plldiv_round_rate,
+ .determine_rate = clk_plldiv_determine_rate,
.set_rate = clk_plldiv_set_rate,
};
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 6c4b259d31d3..1195fb405503 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -215,16 +215,16 @@ static const struct clk_ops programmable_ops = {
struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
- u8 num_parents, u8 id,
+ struct clk_hw **parent_hws, u8 num_parents, u8 id,
const struct clk_programmable_layout *layout,
u32 *mux_table)
{
struct clk_programmable *prog;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
- if (id > PROG_ID_MAX)
+ if (id > PROG_ID_MAX || !(parent_names || parent_hws))
return ERR_PTR(-EINVAL);
prog = kzalloc(sizeof(*prog), GFP_KERNEL);
@@ -233,7 +233,10 @@ at91_clk_register_programmable(struct regmap *regmap,
init.name = name;
init.ops = &programmable_ops;
- init.parent_names = parent_names;
+ if (parent_hws)
+ init.parent_hws = (const struct clk_hw **)parent_hws;
+ else
+ init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index d757003004cb..3b965057ba0d 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -23,10 +23,7 @@
#define UPLL_DIV 2
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
-#define FCORE_MIN (600000000)
-#define FCORE_MAX (1200000000)
-
-#define PLL_MAX_ID 7
+#define PLL_MAX_ID 9
struct sam9x60_pll_core {
struct regmap *regmap;
@@ -76,9 +73,15 @@ static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ unsigned long freq;
- return parent_rate * (frac->mul + 1) +
+ freq = parent_rate * (frac->mul + 1) +
DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22));
+
+ if (core->layout->div2)
+ freq >>= 1;
+
+ return freq;
}
static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
@@ -90,8 +93,8 @@ static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
spin_lock_irqsave(core->lock, flags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
@@ -100,11 +103,8 @@ static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
(cmul == frac->mul && cfrac == frac->frac))
goto unlock;
- /* Recommended value for PMC_PLL_ACR */
- if (core->characteristics->upll)
- val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
- else
- val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
+ /* Load recommended value for PMC_PLL_ACR */
+ val = core->characteristics->acr;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);
regmap_write(regmap, AT91_PMC_PLL_CTRL1,
@@ -125,17 +125,17 @@ static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
udelay(10);
}
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
@@ -161,8 +161,8 @@ static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
spin_lock_irqsave(core->lock, flags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
@@ -170,9 +170,9 @@ static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
regmap_update_bits(regmap, AT91_PMC_PLL_ACR,
AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
spin_unlock_irqrestore(core->lock, flags);
}
@@ -194,7 +194,8 @@ static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
unsigned long nmul = 0;
unsigned long nfrac = 0;
- if (rate < FCORE_MIN || rate > FCORE_MAX)
+ if (rate < core->characteristics->core_output[0].min ||
+ rate > core->characteristics->core_output[0].max)
return -ERANGE;
/*
@@ -214,7 +215,8 @@ static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
}
/* Check if resulted rate is a valid. */
- if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+ if (tmprate < core->characteristics->core_output[0].min ||
+ tmprate > core->characteristics->core_output[0].max)
return -ERANGE;
if (update) {
@@ -225,12 +227,16 @@ static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
return tmprate;
}
-static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int sam9x60_frac_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false);
+ req->rate = sam9x60_frac_pll_compute_mul_frac(core, req->rate,
+ req->best_parent_rate,
+ false);
+
+ return 0;
}
static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -257,8 +263,8 @@ static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
spin_lock_irqsave(core->lock, irqflags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
- core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
@@ -270,18 +276,18 @@ static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
(frac->mul << core->layout->mul_shift) |
(frac->frac << core->layout->frac_shift));
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
AT91_PMC_PLL_CTRL0_ENLOCK |
AT91_PMC_PLL_CTRL0_ENPLL);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
@@ -316,7 +322,7 @@ static const struct clk_ops sam9x60_frac_pll_ops = {
.unprepare = sam9x60_frac_pll_unprepare,
.is_prepared = sam9x60_frac_pll_is_prepared,
.recalc_rate = sam9x60_frac_pll_recalc_rate,
- .round_rate = sam9x60_frac_pll_round_rate,
+ .determine_rate = sam9x60_frac_pll_determine_rate,
.set_rate = sam9x60_frac_pll_set_rate,
.save_context = sam9x60_frac_pll_save_context,
.restore_context = sam9x60_frac_pll_restore_context,
@@ -327,13 +333,16 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = {
.unprepare = sam9x60_frac_pll_unprepare,
.is_prepared = sam9x60_frac_pll_is_prepared,
.recalc_rate = sam9x60_frac_pll_recalc_rate,
- .round_rate = sam9x60_frac_pll_round_rate,
+ .determine_rate = sam9x60_frac_pll_determine_rate,
.set_rate = sam9x60_frac_pll_set_rate_chg,
.save_context = sam9x60_frac_pll_save_context,
.restore_context = sam9x60_frac_pll_restore_context,
};
-/* This function should be called with spinlock acquired. */
+/* This function should be called with spinlock acquired.
+ * Warning: this function must be called only if the same PLL ID was set in
+ * PLL_UPDT register previously.
+ */
static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
bool enable)
{
@@ -345,9 +354,9 @@ static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
core->layout->div_mask | ena_msk,
(div << core->layout->div_shift) | ena_val);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
@@ -361,8 +370,8 @@ static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
unsigned int val, cdiv;
spin_lock_irqsave(core->lock, flags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
@@ -393,15 +402,15 @@ static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
spin_lock_irqsave(core->lock, flags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
core->layout->endiv_mask, 0);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
- AT91_PMC_PLL_UPDT_UPDATE | core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
spin_unlock_irqrestore(core->lock, flags);
}
@@ -433,6 +442,12 @@ static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1));
}
+static unsigned long sam9x60_fixed_div_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate >> 1;
+}
+
static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core,
unsigned long *parent_rate,
unsigned long rate)
@@ -476,12 +491,15 @@ static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core,
return best_rate;
}
-static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int sam9x60_div_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- return sam9x60_div_pll_compute_div(core, parent_rate, rate);
+ req->rate = sam9x60_div_pll_compute_div(core, &req->best_parent_rate,
+ req->rate);
+
+ return 0;
}
static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -507,8 +525,8 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
spin_lock_irqsave(core->lock, irqflags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
- core->id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
@@ -563,8 +581,8 @@ static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier,
div->div = div->safe_div;
spin_lock_irqsave(core.lock, irqflags);
- regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
- core.id);
+ regmap_write_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core.id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
cdiv = (val & core.layout->div_mask) >> core.layout->div_shift;
@@ -590,7 +608,7 @@ static const struct clk_ops sam9x60_div_pll_ops = {
.unprepare = sam9x60_div_pll_unprepare,
.is_prepared = sam9x60_div_pll_is_prepared,
.recalc_rate = sam9x60_div_pll_recalc_rate,
- .round_rate = sam9x60_div_pll_round_rate,
+ .determine_rate = sam9x60_div_pll_determine_rate,
.set_rate = sam9x60_div_pll_set_rate,
.save_context = sam9x60_div_pll_save_context,
.restore_context = sam9x60_div_pll_restore_context,
@@ -601,12 +619,22 @@ static const struct clk_ops sam9x60_div_pll_ops_chg = {
.unprepare = sam9x60_div_pll_unprepare,
.is_prepared = sam9x60_div_pll_is_prepared,
.recalc_rate = sam9x60_div_pll_recalc_rate,
- .round_rate = sam9x60_div_pll_round_rate,
+ .determine_rate = sam9x60_div_pll_determine_rate,
.set_rate = sam9x60_div_pll_set_rate_chg,
.save_context = sam9x60_div_pll_save_context,
.restore_context = sam9x60_div_pll_restore_context,
};
+static const struct clk_ops sam9x60_fixed_div_pll_ops = {
+ .prepare = sam9x60_div_pll_prepare,
+ .unprepare = sam9x60_div_pll_unprepare,
+ .is_prepared = sam9x60_div_pll_is_prepared,
+ .recalc_rate = sam9x60_fixed_div_pll_recalc_rate,
+ .determine_rate = sam9x60_div_pll_determine_rate,
+ .save_context = sam9x60_div_pll_save_context,
+ .restore_context = sam9x60_div_pll_restore_context,
+};
+
struct clk_hw * __init
sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name,
@@ -616,7 +644,7 @@ sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
{
struct sam9x60_frac *frac;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
unsigned long parent_rate, irqflags;
unsigned int val;
int ret;
@@ -629,7 +657,10 @@ sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
return ERR_PTR(-ENOMEM);
init.name = name;
- init.parent_names = &parent_name;
+ if (parent_name)
+ init.parent_names = &parent_name;
+ else
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
init.num_parents = 1;
if (flags & CLK_SET_RATE_GATE)
init.ops = &sam9x60_frac_pll_ops;
@@ -666,9 +697,10 @@ sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
goto free;
}
- ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN,
+ ret = sam9x60_frac_pll_compute_mul_frac(&frac->core,
+ characteristics->core_output[0].min,
parent_rate, true);
- if (ret <= 0) {
+ if (ret < 0) {
hw = ERR_PTR(ret);
goto free;
}
@@ -692,14 +724,15 @@ free:
struct clk_hw * __init
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
- const char *name, const char *parent_name, u8 id,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, u32 flags,
u32 safe_div)
{
struct sam9x60_div *div;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
unsigned long irqflags;
unsigned int val;
int ret;
@@ -716,12 +749,19 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
return ERR_PTR(-ENOMEM);
init.name = name;
- init.parent_names = &parent_name;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
- if (flags & CLK_SET_RATE_GATE)
+
+ if (layout->div2)
+ init.ops = &sam9x60_fixed_div_pll_ops;
+ else if (flags & CLK_SET_RATE_GATE)
init.ops = &sam9x60_div_pll_ops;
else
init.ops = &sam9x60_div_pll_ops_chg;
+
init.flags = flags;
div->core.id = id;
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
index 160378438f1b..09c649c8598e 100644
--- a/drivers/clk/at91/clk-smd.c
+++ b/drivers/clk/at91/clk-smd.c
@@ -36,26 +36,31 @@ static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
return parent_rate / (smddiv + 1);
}
-static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int at91sam9x5_clk_smd_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long div;
unsigned long bestrate;
unsigned long tmp;
- if (rate >= *parent_rate)
- return *parent_rate;
+ if (req->rate >= req->best_parent_rate) {
+ req->rate = req->best_parent_rate;
+ return 0;
+ }
- div = *parent_rate / rate;
- if (div > SMD_MAX_DIV)
- return *parent_rate / (SMD_MAX_DIV + 1);
+ div = req->best_parent_rate / req->rate;
+ if (div > SMD_MAX_DIV) {
+ req->rate = req->best_parent_rate / (SMD_MAX_DIV + 1);
+ return 0;
+ }
- bestrate = *parent_rate / div;
- tmp = *parent_rate / (div + 1);
- if (bestrate - rate > rate - tmp)
+ bestrate = req->best_parent_rate / div;
+ tmp = req->best_parent_rate / (div + 1);
+ if (bestrate - req->rate > req->rate - tmp)
bestrate = tmp;
- return bestrate;
+ req->rate = bestrate;
+ return 0;
}
static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
@@ -98,7 +103,7 @@ static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops at91sam9x5_smd_ops = {
.recalc_rate = at91sam9x5_clk_smd_recalc_rate,
- .round_rate = at91sam9x5_clk_smd_round_rate,
+ .determine_rate = at91sam9x5_clk_smd_determine_rate,
.get_parent = at91sam9x5_clk_smd_get_parent,
.set_parent = at91sam9x5_clk_smd_set_parent,
.set_rate = at91sam9x5_clk_smd_set_rate,
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 80720fd1a9cf..90eed39d0785 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -105,14 +105,15 @@ static const struct clk_ops system_ops = {
struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
- const char *parent_name, u8 id)
+ const char *parent_name, struct clk_hw *parent_hw, u8 id,
+ unsigned long flags)
{
struct clk_system *sys;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
- if (!parent_name || id > SYSTEM_MAX_ID)
+ if (!(parent_name || parent_hw) || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
sys = kzalloc(sizeof(*sys), GFP_KERNEL);
@@ -121,9 +122,12 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
init.name = name;
init.ops = &system_ops;
- init.parent_names = &parent_name;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
- init.flags = CLK_SET_RATE_PARENT;
+ init.flags = CLK_SET_RATE_PARENT | flags;
sys->id = id;
sys->hw.init = &init;
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index b0696a928aa9..e906928cfbf0 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -319,8 +319,8 @@ static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int at91rm9200_clk_usb_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
struct clk_hw *parent = clk_hw_get_parent(hw);
@@ -336,25 +336,27 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
if (!usb->divisors[i])
continue;
- tmp_parent_rate = rate * usb->divisors[i];
+ tmp_parent_rate = req->rate * usb->divisors[i];
tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate);
tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
- if (tmprate < rate)
- tmpdiff = rate - tmprate;
+ if (tmprate < req->rate)
+ tmpdiff = req->rate - tmprate;
else
- tmpdiff = tmprate - rate;
+ tmpdiff = tmprate - req->rate;
if (bestdiff < 0 || bestdiff > tmpdiff) {
bestrate = tmprate;
bestdiff = tmpdiff;
- *parent_rate = tmp_parent_rate;
+ req->best_parent_rate = tmp_parent_rate;
}
if (!bestdiff)
break;
}
- return bestrate;
+ req->rate = bestrate;
+
+ return 0;
}
static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -384,7 +386,7 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops at91rm9200_usb_ops = {
.recalc_rate = at91rm9200_clk_usb_recalc_rate,
- .round_rate = at91rm9200_clk_usb_round_rate,
+ .determine_rate = at91rm9200_clk_usb_determine_rate,
.set_rate = at91rm9200_clk_usb_set_rate,
};
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index a22c10d9a1b9..b991180beea1 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -144,21 +144,28 @@ static struct clk_hw * __init
at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
struct regmap *regmap_sfr,
const char *name, const char *parent_name,
+ struct clk_hw *parent_hw,
const struct clk_ops *ops, unsigned long flags)
{
struct clk_utmi *utmi;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
+ if (!(parent_name || parent_hw))
+ return ERR_PTR(-EINVAL);
+
utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
if (!utmi)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = ops;
- init.parent_names = parent_name ? &parent_name : NULL;
- init.num_parents = parent_name ? 1 : 0;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
init.flags = flags;
utmi->hw.init = &init;
@@ -177,10 +184,11 @@ at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
- const char *name, const char *parent_name)
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw)
{
return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
- parent_name, &utmi_ops, CLK_SET_RATE_GATE);
+ parent_name, parent_hw, &utmi_ops, CLK_SET_RATE_GATE);
}
static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
@@ -279,8 +287,8 @@ static const struct clk_ops sama7g5_utmi_ops = {
struct clk_hw * __init
at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
- const char *parent_name)
+ const char *parent_name, struct clk_hw *parent_hw)
{
return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
- parent_name, &sama7g5_utmi_ops, 0);
+ parent_name, parent_hw, &sama7g5_utmi_ops, 0);
}
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
index 8ca8bcacf66d..f5a5f9ba7634 100644
--- a/drivers/clk/at91/dt-compat.c
+++ b/drivers/clk/at91/dt-compat.c
@@ -33,8 +33,11 @@ static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np)
const char *name = np->name;
const char *parent_name;
struct regmap *regmap;
+ struct device_node *parent_np;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -56,8 +59,11 @@ static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
const char *name = np->name;
const char *parent_name;
struct regmap *regmap;
+ struct device_node *parent_np;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -79,8 +85,11 @@ static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
const char *name = np->name;
const char *parent_name;
struct regmap *regmap;
+ struct device_node *parent_np;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -120,7 +129,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
struct clk_hw *hw;
unsigned int num_parents;
const char *parent_names[GENERATED_SOURCE_MAX];
- struct device_node *gcknp;
+ struct device_node *gcknp, *parent_np;
struct clk_range range = CLK_RANGE(0, 0);
struct regmap *regmap;
@@ -134,7 +143,9 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
if (!num || num > PERIPHERAL_MAX)
return;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -160,7 +171,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&dt_pcr_layout, name,
- parent_names, NULL,
+ parent_names, NULL, NULL,
num_parents, id, &range,
chg_pid);
if (IS_ERR(hw))
@@ -180,8 +191,11 @@ static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
const char *name = np->name;
const char *parent_name;
struct regmap *regmap;
+ struct device_node *parent_np;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -243,16 +257,19 @@ static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
const char *parent_name;
struct regmap *regmap;
bool bypass;
+ struct device_node *parent_np;
of_property_read_string(np, "clock-output-names", &name);
bypass = of_property_read_bool(np, "atmel,osc-bypass");
parent_name = of_clk_get_parent_name(np, 0);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
- hw = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
+ hw = at91_clk_register_main_osc(regmap, name, parent_name, NULL, bypass);
if (IS_ERR(hw))
return;
@@ -268,12 +285,15 @@ static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
u32 accuracy = 0;
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
of_property_read_string(np, "clock-output-names", &name);
of_property_read_u32(np, "clock-frequency", &frequency);
of_property_read_u32(np, "clock-accuracy", &accuracy);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -292,15 +312,18 @@ static void __init of_at91rm9200_clk_main_setup(struct device_node *np)
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
parent_name = of_clk_get_parent_name(np, 0);
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
- hw = at91_clk_register_rm9200_main(regmap, name, parent_name);
+ hw = at91_clk_register_rm9200_main(regmap, name, parent_name, NULL);
if (IS_ERR(hw))
return;
@@ -316,19 +339,22 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
unsigned int num_parents;
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
num_parents = of_clk_get_parent_count(np);
if (num_parents == 0 || num_parents > 2)
return;
of_clk_parent_fill(np, parent_names, num_parents);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
of_property_read_string(np, "clock-output-names", &name);
- hw = at91_clk_register_sam9x5_main(regmap, name, parent_names,
+ hw = at91_clk_register_sam9x5_main(regmap, name, parent_names, NULL,
num_parents);
if (IS_ERR(hw))
return;
@@ -373,6 +399,7 @@ of_at91_clk_master_setup(struct device_node *np,
const char *name = np->name;
struct clk_master_characteristics *characteristics;
struct regmap *regmap;
+ struct device_node *parent_np;
num_parents = of_clk_get_parent_count(np);
if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
@@ -386,17 +413,19 @@ of_at91_clk_master_setup(struct device_node *np,
if (!characteristics)
return;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
hw = at91_clk_register_master_pres(regmap, "masterck_pres", num_parents,
- parent_names, layout,
+ parent_names, NULL, layout,
characteristics, &mck_lock);
if (IS_ERR(hw))
goto out_free_characteristics;
- hw = at91_clk_register_master_div(regmap, name, "masterck_pres",
+ hw = at91_clk_register_master_div(regmap, name, "masterck_pres", NULL,
layout, characteristics,
&mck_lock, CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
@@ -433,6 +462,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
const char *name;
struct device_node *periphclknp;
struct regmap *regmap;
+ struct device_node *parent_np;
parent_name = of_clk_get_parent_name(np, 0);
if (!parent_name)
@@ -442,7 +472,9 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
if (!num || num > PERIPHERAL_MAX)
return;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -458,21 +490,32 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
if (type == PERIPHERAL_AT91RM9200) {
hw = at91_clk_register_peripheral(regmap, name,
- parent_name, id);
+ parent_name, NULL, id);
} else {
struct clk_range range = CLK_RANGE(0, 0);
+ unsigned long flags = 0;
of_at91_get_clk_range(periphclknp,
"atmel,clk-output-range",
&range);
+ /*
+ * mpddr_clk feed DDR controller and is enabled by
+ * bootloader thus we need to keep it enabled in case
+ * there is no Linux consumer for it.
+ */
+ if (!strcmp(periphclknp->name, "mpddr_clk"))
+ flags = CLK_IS_CRITICAL;
+
hw = at91_clk_register_sam9x5_peripheral(regmap,
&pmc_pcr_lock,
&dt_pcr_layout,
name,
parent_name,
+ NULL,
id, &range,
- INT_MIN);
+ INT_MIN,
+ flags);
}
if (IS_ERR(hw))
@@ -520,9 +563,10 @@ of_at91_clk_pll_get_characteristics(struct device_node *np)
if (num_cells < 2 || num_cells > 4)
return NULL;
- if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
+ num_output = of_property_count_u32_elems(np, "atmel,pll-clk-output-ranges");
+ if (num_output <= 0)
return NULL;
- num_output = tmp / (sizeof(u32) * num_cells);
+ num_output /= num_cells;
characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
if (!characteristics)
@@ -601,6 +645,7 @@ of_at91_clk_pll_setup(struct device_node *np,
struct regmap *regmap;
const char *parent_name;
const char *name = np->name;
+ struct device_node *parent_np;
struct clk_pll_characteristics *characteristics;
if (of_property_read_u32(np, "reg", &id))
@@ -610,7 +655,9 @@ of_at91_clk_pll_setup(struct device_node *np,
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -665,12 +712,15 @@ of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
parent_name = of_clk_get_parent_name(np, 0);
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -694,7 +744,7 @@ of_at91_clk_prog_setup(struct device_node *np,
unsigned int num_parents;
const char *parent_names[PROG_SOURCE_MAX];
const char *name;
- struct device_node *progclknp;
+ struct device_node *progclknp, *parent_np;
struct regmap *regmap;
num_parents = of_clk_get_parent_count(np);
@@ -707,7 +757,9 @@ of_at91_clk_prog_setup(struct device_node *np,
if (!num || num > (PROG_ID_MAX + 1))
return;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -719,7 +771,7 @@ of_at91_clk_prog_setup(struct device_node *np,
name = progclknp->name;
hw = at91_clk_register_programmable(regmap, name,
- parent_names, num_parents,
+ parent_names, NULL, num_parents,
id, layout, mux_table);
if (IS_ERR(hw))
continue;
@@ -756,13 +808,16 @@ static void __init of_at91sam9260_clk_slow_setup(struct device_node *np)
unsigned int num_parents;
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
num_parents = of_clk_get_parent_count(np);
if (num_parents != 2)
return;
of_clk_parent_fill(np, parent_names, num_parents);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -788,6 +843,7 @@ static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np)
const char *parent_names[SMD_SOURCE_MAX];
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
num_parents = of_clk_get_parent_count(np);
if (num_parents == 0 || num_parents > SMD_SOURCE_MAX)
@@ -797,7 +853,9 @@ static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np)
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -818,7 +876,7 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
u32 id;
struct clk_hw *hw;
const char *name;
- struct device_node *sysclknp;
+ struct device_node *sysclknp, *parent_np;
const char *parent_name;
struct regmap *regmap;
@@ -826,11 +884,15 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
if (num > (SYSTEM_MAX_ID + 1))
return;
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
for_each_child_of_node(np, sysclknp) {
+ unsigned long flags = 0;
+
if (of_property_read_u32(sysclknp, "reg", &id))
continue;
@@ -839,7 +901,16 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
parent_name = of_clk_get_parent_name(sysclknp, 0);
- hw = at91_clk_register_system(regmap, name, parent_name, id);
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus
+ * we need to keep it enabled in case there is no Linux consumer
+ * for it.
+ */
+ if (!strcmp(sysclknp->name, "ddrck"))
+ flags = CLK_IS_CRITICAL;
+
+ hw = at91_clk_register_system(regmap, name, parent_name, NULL,
+ id, flags);
if (IS_ERR(hw))
continue;
@@ -859,6 +930,7 @@ static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np)
const char *parent_names[USB_SOURCE_MAX];
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
num_parents = of_clk_get_parent_count(np);
if (num_parents == 0 || num_parents > USB_SOURCE_MAX)
@@ -868,7 +940,9 @@ static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np)
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -888,6 +962,7 @@ static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np)
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
+ struct device_node *parent_np;
parent_name = of_clk_get_parent_name(np, 0);
if (!parent_name)
@@ -895,7 +970,9 @@ static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np)
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
@@ -915,6 +992,7 @@ static void __init of_at91rm9200_clk_usb_setup(struct device_node *np)
const char *name = np->name;
u32 divisors[4] = {0, 0, 0, 0};
struct regmap *regmap;
+ struct device_node *parent_np;
parent_name = of_clk_get_parent_name(np, 0);
if (!parent_name)
@@ -926,7 +1004,9 @@ static void __init of_at91rm9200_clk_usb_setup(struct device_node *np)
of_property_read_string(np, "clock-output-names", &name);
- regmap = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap))
return;
hw = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
@@ -946,12 +1026,15 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
const char *parent_name;
const char *name = np->name;
struct regmap *regmap_pmc, *regmap_sfr;
+ struct device_node *parent_np;
parent_name = of_clk_get_parent_name(np, 0);
of_property_read_string(np, "clock-output-names", &name);
- regmap_pmc = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ regmap_pmc = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(regmap_pmc))
return;
@@ -973,7 +1056,7 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
regmap_sfr = NULL;
}
- hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name);
+ hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name, NULL);
if (IS_ERR(hw))
return;
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5aa9c1f1c886..2310f6f73162 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -115,7 +115,7 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
/* Address in SECURAM that say if we suspend to backup mode. */
static void __iomem *at91_pmc_backup_suspend;
-static int at91_pmc_suspend(void)
+static int at91_pmc_suspend(void *data)
{
unsigned int backup;
@@ -129,7 +129,7 @@ static int at91_pmc_suspend(void)
return clk_save_context();
}
-static void at91_pmc_resume(void)
+static void at91_pmc_resume(void *data)
{
unsigned int backup;
@@ -143,14 +143,19 @@ static void at91_pmc_resume(void)
clk_restore_context();
}
-static struct syscore_ops pmc_syscore_ops = {
+static const struct syscore_ops pmc_syscore_ops = {
.suspend = at91_pmc_suspend,
.resume = at91_pmc_resume,
};
+static struct syscore pmc_syscore = {
+ .ops = &pmc_syscore_ops,
+};
+
static const struct of_device_id pmc_dt_ids[] = {
{ .compatible = "atmel,sama5d2-pmc" },
{ .compatible = "microchip,sama7g5-pmc", },
+ { .compatible = "microchip,sama7d65-pmc", },
{ /* sentinel */ }
};
@@ -184,7 +189,7 @@ static int __init pmc_register_ops(void)
return -ENOMEM;
}
- register_syscore_ops(&pmc_syscore_ops);
+ register_syscore(&pmc_syscore);
return 0;
}
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index efe4975bddc3..543d7aee8d24 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -64,6 +64,7 @@ struct clk_pll_layout {
u8 frac_shift;
u8 div_shift;
u8 endiv_shift;
+ u8 div2;
};
extern const struct clk_pll_layout at91rm9200_pll_layout;
@@ -75,9 +76,11 @@ struct clk_pll_characteristics {
struct clk_range input;
int num_output;
const struct clk_range *output;
+ const struct clk_range *core_output;
u16 *icpll;
u8 *out;
u8 upll : 1;
+ u32 acr;
};
struct clk_programmable_layout {
@@ -114,11 +117,24 @@ struct at91_clk_pms {
unsigned int parent;
};
-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
-
#define ndck(a, s) (a[s - 1].id + 1)
#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)
+
+#define PMC_INIT_TABLE(_table, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) \
+ (_table)[_i] = _i; \
+ } while (0)
+
+#define PMC_FILL_TABLE(_to, _from, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) { \
+ (_to)[_i] = (_from)[_i]; \
+ } \
+ } while (0)
+
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
unsigned int nperiph, unsigned int ngck,
unsigned int npck);
@@ -144,7 +160,8 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u32 *mux_table, u8 num_parents, u8 id,
+ struct clk_hw **parent_hws, u32 *mux_table,
+ u8 num_parents, u8 id,
const struct clk_range *range, int chg_pid);
struct clk_hw * __init
@@ -161,25 +178,29 @@ at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name,
u32 frequency, u32 accuracy);
struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap, const char *name,
- const char *parent_name, bool bypass);
+ const char *parent_name,
+ struct clk_parent_data *parent_data, bool bypass);
struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
- const char *parent_name);
+ const char *parent_name,
+ struct clk_hw *parent_hw);
struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name,
- const char **parent_names, int num_parents);
+ const char **parent_names,
+ struct clk_hw **parent_hws, int num_parents);
struct clk_hw * __init
at91_clk_register_master_pres(struct regmap *regmap, const char *name,
int num_parents, const char **parent_names,
+ struct clk_hw **parent_hws,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
spinlock_t *lock);
struct clk_hw * __init
at91_clk_register_master_div(struct regmap *regmap, const char *name,
- const char *parent_names,
+ const char *parent_names, struct clk_hw *parent_hw,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
spinlock_t *lock, u32 flags, u32 safe_div);
@@ -187,19 +208,22 @@ at91_clk_register_master_div(struct regmap *regmap, const char *name,
struct clk_hw * __init
at91_clk_sama7g5_register_master(struct regmap *regmap,
const char *name, int num_parents,
- const char **parent_names, u32 *mux_table,
+ const char **parent_names,
+ struct clk_hw **parent_hws, u32 *mux_table,
spinlock_t *lock, u8 id, bool critical,
int chg_pid);
struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
- const char *parent_name, u32 id);
+ const char *parent_name, struct clk_hw *parent_hw,
+ u32 id);
struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
+ struct clk_hw *parent_hw,
u32 id, const struct clk_range *range,
- int chg_pid);
+ int chg_pid, unsigned long flags);
struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
@@ -212,7 +236,8 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name,
struct clk_hw * __init
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
- const char *name, const char *parent_name, u8 id,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, u32 flags,
u32 safe_div);
@@ -226,7 +251,8 @@ sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name,
- const char **parent_names, u8 num_parents, u8 id,
+ const char **parent_names, struct clk_hw **parent_hws,
+ u8 num_parents, u8 id,
const struct clk_programmable_layout *layout,
u32 *mux_table);
@@ -242,7 +268,8 @@ at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
- const char *parent_name, u8 id);
+ const char *parent_name, struct clk_hw *parent_hw,
+ u8 id, unsigned long flags);
struct clk_hw * __init
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
@@ -259,10 +286,11 @@ at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
- const char *name, const char *parent_name);
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw);
struct clk_hw * __init
at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
- const char *parent_name);
+ const char *parent_name, struct clk_hw *parent_hw);
#endif /* __PMC_H_ */
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 9ea4ce501bad..18baf4a256f4 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -26,10 +26,17 @@ static const struct clk_range plla_outputs[] = {
{ .min = 2343750, .max = 1200000000 },
};
+/* Fractional PLL core output range. */
+static const struct clk_range core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
static const struct clk_pll_characteristics plla_characteristics = {
.input = { .min = 12000000, .max = 48000000 },
.num_output = ARRAY_SIZE(plla_outputs),
.output = plla_outputs,
+ .core_output = core_outputs,
+ .acr = UL(0x00020010),
};
static const struct clk_range upll_outputs[] = {
@@ -40,7 +47,9 @@ static const struct clk_pll_characteristics upll_characteristics = {
.input = { .min = 12000000, .max = 48000000 },
.num_output = ARRAY_SIZE(upll_outputs),
.output = upll_outputs,
+ .core_output = core_outputs,
.upll = true,
+ .acr = UL(0x12023010), /* fIN = [18 MHz, 32 MHz]*/
};
static const struct clk_pll_layout pll_frac_layout = {
@@ -75,9 +84,14 @@ static const struct clk_pcr_layout sam9x60_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sam9x60_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "pck0", .p = "prog0", .id = 8 },
{ .n = "pck1", .p = "prog1", .id = 9 },
@@ -86,6 +100,7 @@ static const struct {
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sam9x60_periphck[] = {
{ .n = "pioA_clk", .id = 2, },
@@ -132,7 +147,11 @@ static const struct {
{ .n = "pioD_clk", .id = 44, },
{ .n = "tcb1_clk", .id = 45, },
{ .n = "dbgu_clk", .id = 47, },
- { .n = "mpddr_clk", .id = 49, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 49, .flags = CLK_IS_CRITICAL },
};
static const struct {
@@ -209,14 +228,14 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, 0);
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL, 0);
if (IS_ERR(hw))
goto err_free;
main_osc_hw = hw;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
if (IS_ERR(hw))
goto err_free;
@@ -236,7 +255,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
goto err_free;
hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
- "pllack_fracck", 0, &plla_characteristics,
+ "pllack_fracck", NULL, 0, &plla_characteristics,
&pll_div_layout,
/*
* This feeds CPU. It should not
@@ -256,7 +275,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
goto err_free;
hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
- "upllck_fracck", 1, &upll_characteristics,
+ "upllck_fracck", NULL, 1, &upll_characteristics,
&pll_div_layout,
CLK_SET_RATE_GATE |
CLK_SET_PARENT_GATE |
@@ -270,13 +289,13 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[1] = "mainck";
parent_names[2] = "pllack_divck";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
- parent_names, &sam9x60_master_layout,
+ parent_names, NULL, &sam9x60_master_layout,
&mck_characteristics, &mck_lock);
if (IS_ERR(hw))
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres", &sam9x60_master_layout,
+ "masterck_pres", NULL, &sam9x60_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
@@ -303,7 +322,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 6, i,
+ parent_names, NULL, 6, i,
&sam9x60_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -314,8 +333,9 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n,
- sam9x60_systemck[i].p,
- sam9x60_systemck[i].id);
+ sam9x60_systemck[i].p, NULL,
+ sam9x60_systemck[i].id,
+ sam9x60_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -326,9 +346,10 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
sam9x60_periphck[i].id,
- &range, INT_MIN);
+ &range, INT_MIN,
+ sam9x60_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -339,7 +360,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_gck[i].n,
- parent_names, NULL, 6,
+ parent_names, NULL, NULL, 6,
sam9x60_gck[i].id,
&sam9x60_gck[i].r, INT_MIN);
if (IS_ERR(hw))
diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c
new file mode 100644
index 000000000000..89868a0aeaba
--- /dev/null
+++ b/drivers/clk/at91/sam9x7.c
@@ -0,0 +1,952 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SAM9X7 PMC code.
+ *
+ * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Varshini Rajendran <varshini.rajendran@microchip.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(mck_lock);
+
+/**
+ * enum pll_ids - PLL clocks identifiers
+ * @PLL_ID_PLLA: PLLA identifier
+ * @PLL_ID_UPLL: UPLL identifier
+ * @PLL_ID_AUDIO: Audio PLL identifier
+ * @PLL_ID_LVDS: LVDS PLL identifier
+ * @PLL_ID_PLLA_DIV2: PLLA DIV2 identifier
+ * @PLL_ID_MAX: Max PLL Identifier
+ */
+enum pll_ids {
+ PLL_ID_PLLA,
+ PLL_ID_UPLL,
+ PLL_ID_AUDIO,
+ PLL_ID_LVDS,
+ PLL_ID_PLLA_DIV2,
+ PLL_ID_MAX,
+};
+
+/**
+ * enum pll_type - PLL type identifiers
+ * @PLL_TYPE_FRAC: fractional PLL identifier
+ * @PLL_TYPE_DIV: divider PLL identifier
+ */
+enum pll_type {
+ PLL_TYPE_FRAC,
+ PLL_TYPE_DIV,
+};
+
+static const struct clk_master_characteristics mck_characteristics = {
+ .output = { .min = 32000000, .max = 266666667 },
+ .divisors = { 1, 2, 4, 3, 5},
+ .have_div3_pres = 1,
+};
+
+static const struct clk_master_layout sam9x7_master_layout = {
+ .mask = 0x373,
+ .pres_shift = 4,
+ .offset = 0x28,
+};
+
+/* Fractional PLL core output range. */
+static const struct clk_range plla_core_outputs[] = {
+ { .min = 800000000, .max = 1600000000 },
+};
+
+static const struct clk_range upll_core_outputs[] = {
+ { .min = 600000000, .max = 960000000 },
+};
+
+static const struct clk_range lvdspll_core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+static const struct clk_range audiopll_core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+static const struct clk_range plladiv2_core_outputs[] = {
+ { .min = 800000000, .max = 1600000000 },
+};
+
+/* Fractional PLL output range. */
+static const struct clk_range plla_outputs[] = {
+ { .min = 400000000, .max = 800000000 },
+};
+
+static const struct clk_range upll_outputs[] = {
+ { .min = 300000000, .max = 480000000 },
+};
+
+static const struct clk_range lvdspll_outputs[] = {
+ { .min = 175000000, .max = 550000000 },
+};
+
+static const struct clk_range audiopll_outputs[] = {
+ { .min = 0, .max = 300000000 },
+};
+
+static const struct clk_range plladiv2_outputs[] = {
+ { .min = 200000000, .max = 400000000 },
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 20000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .core_output = plla_core_outputs,
+ .acr = UL(0x00020010), /* Old ACR_DEFAULT_PLLA value */
+};
+
+static const struct clk_pll_characteristics upll_characteristics = {
+ .input = { .min = 20000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(upll_outputs),
+ .output = upll_outputs,
+ .core_output = upll_core_outputs,
+ .upll = true,
+ .acr = UL(0x12023010), /* fIN=[20 MHz, 32 MHz] */
+};
+
+static const struct clk_pll_characteristics lvdspll_characteristics = {
+ .input = { .min = 20000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(lvdspll_outputs),
+ .output = lvdspll_outputs,
+ .core_output = lvdspll_core_outputs,
+ .acr = UL(0x12023010), /* fIN=[20 MHz, 32 MHz] */
+};
+
+static const struct clk_pll_characteristics audiopll_characteristics = {
+ .input = { .min = 20000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(audiopll_outputs),
+ .output = audiopll_outputs,
+ .core_output = audiopll_core_outputs,
+ .acr = UL(0x12023010), /* fIN=[20 MHz, 32 MHz] */
+};
+
+static const struct clk_pll_characteristics plladiv2_characteristics = {
+ .input = { .min = 20000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(plladiv2_outputs),
+ .output = plladiv2_outputs,
+ .core_output = plladiv2_core_outputs,
+ .acr = UL(0x00020010), /* Old ACR_DEFAULT_PLLA value */
+};
+
+/* Layout for fractional PLL ID PLLA. */
+static const struct clk_pll_layout plla_frac_layout = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+ .div2 = 1,
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_frac_layout = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+/* Layout for DIV PLLs. */
+static const struct clk_pll_layout pll_divpmc_layout = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
+/* Layout for DIV PLL ID PLLADIV2. */
+static const struct clk_pll_layout plladiv2_divpmc_layout = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+ .div2 = 1,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_divio_layout = {
+ .div_mask = GENMASK(19, 12),
+ .endiv_mask = BIT(30),
+ .div_shift = 12,
+ .endiv_shift = 30,
+};
+
+/*
+ * PLL clocks description
+ * @n: clock name
+ * @p: clock parent
+ * @l: clock layout
+ * @t: clock type
+ * @c: pll characteristics
+ * @f: clock flags
+ * @eid: export index in sam9x7->chws[] array
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ const struct clk_pll_layout *l;
+ u8 t;
+ const struct clk_pll_characteristics *c;
+ unsigned long f;
+ u8 eid;
+} sam9x7_plls[][3] = {
+ [PLL_ID_PLLA] = {
+ {
+ .n = "plla_fracck",
+ .p = "mainck",
+ .l = &plla_frac_layout,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds plla_divpmcck which feeds CPU. It should
+ * not be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .c = &plla_characteristics,
+ },
+
+ {
+ .n = "plla_divpmcck",
+ .p = "plla_fracck",
+ .l = &pll_divpmc_layout,
+ .t = PLL_TYPE_DIV,
+ /* This feeds CPU. It should not be disabled */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .eid = PMC_PLLACK,
+ .c = &plla_characteristics,
+ },
+ },
+
+ [PLL_ID_UPLL] = {
+ {
+ .n = "upll_fracck",
+ .p = "main_osc",
+ .l = &pll_frac_layout,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ .c = &upll_characteristics,
+ },
+
+ {
+ .n = "upll_divpmcck",
+ .p = "upll_fracck",
+ .l = &pll_divpmc_layout,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_UTMI,
+ .c = &upll_characteristics,
+ },
+ },
+
+ [PLL_ID_AUDIO] = {
+ {
+ .n = "audiopll_fracck",
+ .p = "main_osc",
+ .l = &pll_frac_layout,
+ .f = CLK_SET_RATE_GATE,
+ .c = &audiopll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ },
+
+ {
+ .n = "audiopll_divpmcck",
+ .p = "audiopll_fracck",
+ .l = &pll_divpmc_layout,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .c = &audiopll_characteristics,
+ .eid = PMC_AUDIOPMCPLL,
+ .t = PLL_TYPE_DIV,
+ },
+
+ {
+ .n = "audiopll_diviock",
+ .p = "audiopll_fracck",
+ .l = &pll_divio_layout,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .c = &audiopll_characteristics,
+ .eid = PMC_AUDIOIOPLL,
+ .t = PLL_TYPE_DIV,
+ },
+ },
+
+ [PLL_ID_LVDS] = {
+ {
+ .n = "lvdspll_fracck",
+ .p = "main_osc",
+ .l = &pll_frac_layout,
+ .f = CLK_SET_RATE_GATE,
+ .c = &lvdspll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ },
+
+ {
+ .n = "lvdspll_divpmcck",
+ .p = "lvdspll_fracck",
+ .l = &pll_divpmc_layout,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .c = &lvdspll_characteristics,
+ .eid = PMC_LVDSPLL,
+ .t = PLL_TYPE_DIV,
+ },
+ },
+
+ [PLL_ID_PLLA_DIV2] = {
+ {
+ .n = "plla_div2pmcck",
+ .p = "plla_fracck",
+ .l = &plladiv2_divpmc_layout,
+ /*
+ * This may feed critical parts of the system like timers.
+ * It should not be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .c = &plladiv2_characteristics,
+ .eid = PMC_PLLADIV2,
+ .t = PLL_TYPE_DIV,
+ },
+ },
+};
+
+static const struct clk_programmable_layout sam9x7_programmable_layout = {
+ .pres_mask = 0xff,
+ .pres_shift = 8,
+ .css_mask = 0x1f,
+ .have_slck_mck = 0,
+ .is_pres_direct = 1,
+};
+
+static const struct clk_pcr_layout sam9x7_pcr_layout = {
+ .offset = 0x88,
+ .cmd = BIT(31),
+ .gckcss_mask = GENMASK(12, 8),
+ .pid_mask = GENMASK(6, 0),
+};
+
+static const struct {
+ char *n;
+ char *p;
+ u8 id;
+ unsigned long flags;
+} sam9x7_systemck[] = {
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+};
+
+/*
+ * Peripheral clocks description
+ * @n: clock name
+ * @f: clock flags
+ * @id: peripheral id
+ */
+static const struct {
+ char *n;
+ unsigned long f;
+ u8 id;
+} sam9x7_periphck[] = {
+ { .n = "pioA_clk", .id = 2, },
+ { .n = "pioB_clk", .id = 3, },
+ { .n = "pioC_clk", .id = 4, },
+ { .n = "flex0_clk", .id = 5, },
+ { .n = "flex1_clk", .id = 6, },
+ { .n = "flex2_clk", .id = 7, },
+ { .n = "flex3_clk", .id = 8, },
+ { .n = "flex6_clk", .id = 9, },
+ { .n = "flex7_clk", .id = 10, },
+ { .n = "flex8_clk", .id = 11, },
+ { .n = "sdmmc0_clk", .id = 12, },
+ { .n = "flex4_clk", .id = 13, },
+ { .n = "flex5_clk", .id = 14, },
+ { .n = "flex9_clk", .id = 15, },
+ { .n = "flex10_clk", .id = 16, },
+ { .n = "tcb0_clk", .id = 17, },
+ { .n = "pwm_clk", .id = 18, },
+ { .n = "adc_clk", .id = 19, },
+ { .n = "dma0_clk", .id = 20, },
+ { .n = "uhphs_clk", .id = 22, },
+ { .n = "udphs_clk", .id = 23, },
+ { .n = "macb0_clk", .id = 24, },
+ { .n = "lcd_clk", .id = 25, },
+ { .n = "sdmmc1_clk", .id = 26, },
+ { .n = "ssc_clk", .id = 28, },
+ { .n = "can0_clk", .id = 29, },
+ { .n = "can1_clk", .id = 30, },
+ { .n = "flex11_clk", .id = 32, },
+ { .n = "flex12_clk", .id = 33, },
+ { .n = "i2s_clk", .id = 34, },
+ { .n = "qspi_clk", .id = 35, },
+ { .n = "gfx2d_clk", .id = 36, },
+ { .n = "pit64b0_clk", .id = 37, },
+ { .n = "trng_clk", .id = 38, },
+ { .n = "aes_clk", .id = 39, },
+ { .n = "tdes_clk", .id = 40, },
+ { .n = "sha_clk", .id = 41, },
+ { .n = "classd_clk", .id = 42, },
+ { .n = "isi_clk", .id = 43, },
+ { .n = "pioD_clk", .id = 44, },
+ { .n = "tcb1_clk", .id = 45, },
+ { .n = "dbgu_clk", .id = 47, },
+ { .n = "pmecc_clk", .id = 48, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 49, .f = CLK_IS_CRITICAL },
+ { .n = "csi2dc_clk", .id = 52, },
+ { .n = "csi4l_clk", .id = 53, },
+ { .n = "dsi4l_clk", .id = 54, },
+ { .n = "lvdsc_clk", .id = 56, },
+ { .n = "pit64b1_clk", .id = 58, },
+ { .n = "puf_clk", .id = 59, },
+ { .n = "gmactsu_clk", .id = 67, },
+};
+
+/*
+ * Generic clock description
+ * @n: clock name
+ * @pp: PLL parents
+ * @pp_mux_table: PLL parents mux table
+ * @r: clock output range
+ * @pp_chg_id: id in parent array of changeable PLL parent
+ * @pp_count: PLL parents count
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *pp[8];
+ const char pp_mux_table[8];
+ struct clk_range r;
+ int pp_chg_id;
+ u8 pp_count;
+ u8 id;
+} sam9x7_gck[] = {
+ {
+ .n = "flex0_gclk",
+ .id = 5,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex1_gclk",
+ .id = 6,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex2_gclk",
+ .id = 7,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex3_gclk",
+ .id = 8,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex6_gclk",
+ .id = 9,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex7_gclk",
+ .id = 10,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex8_gclk",
+ .id = 11,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "sdmmc0_gclk",
+ .id = 12,
+ .r = { .max = 105000000 },
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex4_gclk",
+ .id = 13,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex5_gclk",
+ .id = 14,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex9_gclk",
+ .id = 15,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex10_gclk",
+ .id = 16,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "tcb0_gclk",
+ .id = 17,
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "adc_gclk",
+ .id = 19,
+ .pp = { "upll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "lcd_gclk",
+ .id = 25,
+ .r = { .max = 75000000 },
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "sdmmc1_gclk",
+ .id = 26,
+ .r = { .max = 105000000 },
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "mcan0_gclk",
+ .id = 29,
+ .r = { .max = 80000000 },
+ .pp = { "upll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "mcan1_gclk",
+ .id = 30,
+ .r = { .max = 80000000 },
+ .pp = { "upll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex11_gclk",
+ .id = 32,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "flex12_gclk",
+ .id = 33,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "i2s_gclk",
+ .id = 34,
+ .r = { .max = 100000000 },
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "qspi_gclk",
+ .id = 35,
+ .r = { .max = 200000000 },
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "pit64b0_gclk",
+ .id = 37,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "classd_gclk",
+ .id = 42,
+ .r = { .max = 100000000 },
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "tcb1_gclk",
+ .id = 45,
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "dbgu_gclk",
+ .id = 47,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "mipiphy_gclk",
+ .id = 55,
+ .r = { .max = 27000000 },
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "pit64b1_gclk",
+ .id = 58,
+ .pp = { "plla_div2pmcck", },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN,
+ },
+
+ {
+ .n = "gmac_gclk",
+ .id = 67,
+ .pp = { "audiopll_divpmcck", "plla_div2pmcck", },
+ .pp_mux_table = { 6, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN,
+ },
+};
+
+static void __init sam9x7_pmc_setup(struct device_node *np)
+{
+ struct clk_range range = CLK_RANGE(0, 0);
+ const char *td_slck_name, *md_slck_name, *mainxtal_name;
+ struct pmc_data *sam9x7_pmc;
+ const char *parent_names[9];
+ void **clk_mux_buffer = NULL;
+ int clk_mux_buffer_size = 0;
+ struct clk_hw *main_osc_hw;
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ int i, j;
+
+ i = of_property_match_string(np, "clock-names", "td_slck");
+ if (i < 0)
+ return;
+
+ td_slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "md_slck");
+ if (i < 0)
+ return;
+
+ md_slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sam9x7_pmc = pmc_data_allocate(PMC_LVDSPLL + 1,
+ nck(sam9x7_systemck),
+ nck(sam9x7_periphck),
+ nck(sam9x7_gck), 8);
+ if (!sam9x7_pmc)
+ return;
+
+ clk_mux_buffer = kmalloc(sizeof(void *) *
+ (ARRAY_SIZE(sam9x7_gck)),
+ GFP_KERNEL);
+ if (!clk_mux_buffer)
+ goto err_free;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+ main_osc_hw = hw;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x7_pmc->chws[PMC_MAIN] = hw;
+
+ for (i = 0; i < PLL_ID_MAX; i++) {
+ for (j = 0; j < 3; j++) {
+ struct clk_hw *parent_hw;
+
+ if (!sam9x7_plls[i][j].n)
+ continue;
+
+ switch (sam9x7_plls[i][j].t) {
+ case PLL_TYPE_FRAC:
+ if (!strcmp(sam9x7_plls[i][j].p, "mainck"))
+ parent_hw = sam9x7_pmc->chws[PMC_MAIN];
+ else if (!strcmp(sam9x7_plls[i][j].p, "main_osc"))
+ parent_hw = main_osc_hw;
+ else
+ parent_hw = __clk_get_hw(of_clk_get_by_name
+ (np, sam9x7_plls[i][j].p));
+
+ hw = sam9x60_clk_register_frac_pll(regmap,
+ &pmc_pll_lock,
+ sam9x7_plls[i][j].n,
+ sam9x7_plls[i][j].p,
+ parent_hw, i,
+ sam9x7_plls[i][j].c,
+ sam9x7_plls[i][j].l,
+ sam9x7_plls[i][j].f);
+ break;
+
+ case PLL_TYPE_DIV:
+ hw = sam9x60_clk_register_div_pll(regmap,
+ &pmc_pll_lock,
+ sam9x7_plls[i][j].n,
+ sam9x7_plls[i][j].p, NULL, i,
+ sam9x7_plls[i][j].c,
+ sam9x7_plls[i][j].l,
+ sam9x7_plls[i][j].f, 0);
+ break;
+
+ default:
+ continue;
+ }
+
+ if (IS_ERR(hw))
+ goto err_free;
+
+ if (sam9x7_plls[i][j].eid)
+ sam9x7_pmc->chws[sam9x7_plls[i][j].eid] = hw;
+ }
+ }
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plla_divpmcck";
+ parent_names[3] = "upll_divpmcck";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names, NULL, &sam9x7_master_layout,
+ &mck_characteristics, &mck_lock);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_master_div(regmap, "masterck_div",
+ "masterck_pres", NULL, &sam9x7_master_layout,
+ &mck_characteristics, &mck_lock,
+ CLK_SET_RATE_GATE, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x7_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = "plla_divpmcck";
+ parent_names[1] = "upll_divpmcck";
+ parent_names[2] = "main_osc";
+ hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "masterck_div";
+ parent_names[4] = "plla_divpmcck";
+ parent_names[5] = "upll_divpmcck";
+ parent_names[6] = "audiopll_divpmcck";
+ for (i = 0; i < 2; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, NULL, 7, i,
+ &sam9x7_programmable_layout,
+ NULL);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x7_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sam9x7_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sam9x7_systemck[i].n,
+ sam9x7_systemck[i].p, NULL,
+ sam9x7_systemck[i].id,
+ sam9x7_systemck[i].flags);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x7_pmc->shws[sam9x7_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sam9x7_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &sam9x7_pcr_layout,
+ sam9x7_periphck[i].n,
+ "masterck_div", NULL,
+ sam9x7_periphck[i].id,
+ &range, INT_MIN,
+ sam9x7_periphck[i].f);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x7_pmc->phws[sam9x7_periphck[i].id] = hw;
+ }
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "masterck_div";
+ for (i = 0; i < ARRAY_SIZE(sam9x7_gck); i++) {
+ u8 num_parents = 4 + sam9x7_gck[i].pp_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ PMC_INIT_TABLE(mux_table, 4);
+ PMC_FILL_TABLE(&mux_table[4], sam9x7_gck[i].pp_mux_table,
+ sam9x7_gck[i].pp_count);
+ PMC_FILL_TABLE(&parent_names[4], sam9x7_gck[i].pp,
+ sam9x7_gck[i].pp_count);
+
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
+ &sam9x7_pcr_layout,
+ sam9x7_gck[i].n,
+ parent_names, NULL, mux_table,
+ num_parents,
+ sam9x7_gck[i].id,
+ &sam9x7_gck[i].r,
+ sam9x7_gck[i].pp_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x7_pmc->ghws[sam9x7_gck[i].id] = hw;
+ clk_mux_buffer[clk_mux_buffer_size++] = mux_table;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x7_pmc);
+ kfree(clk_mux_buffer);
+
+ return;
+
+err_free:
+ if (clk_mux_buffer) {
+ for (i = 0; i < clk_mux_buffer_size; i++)
+ kfree(clk_mux_buffer[i]);
+ kfree(clk_mux_buffer);
+ }
+ kfree(sam9x7_pmc);
+}
+
+/* Some clks are used for a clocksource */
+CLK_OF_DECLARE(sam9x7_pmc, "microchip,sam9x7-pmc", sam9x7_pmc_setup);
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index cfd0f5e23b99..c16594fce90c 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -40,9 +40,14 @@ static const struct clk_pcr_layout sama5d2_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sama5d2_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "lcdck", .p = "masterck_div", .id = 3 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "udpck", .p = "usbck", .id = 7 },
@@ -97,6 +102,7 @@ static const struct {
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sama5d2_periphck[] = {
{ .n = "dma0_clk", .id = 6, },
@@ -104,7 +110,11 @@ static const struct {
{ .n = "aes_clk", .id = 9, },
{ .n = "aesb_clk", .id = 10, },
{ .n = "sha_clk", .id = 12, },
- { .n = "mpddr_clk", .id = 13, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 13, .flags = CLK_IS_CRITICAL },
{ .n = "matrix0_clk", .id = 15, },
{ .n = "sdmmc0_hclk", .id = 31, },
{ .n = "sdmmc1_hclk", .id = 32, },
@@ -120,6 +130,16 @@ static const struct {
struct clk_range r;
int chg_pid;
} sama5d2_gck[] = {
+ { .n = "flx0_gclk", .id = 19, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx1_gclk", .id = 20, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx2_gclk", .id = 21, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx3_gclk", .id = 22, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx4_gclk", .id = 23, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart0_gclk", .id = 24, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart1_gclk", .id = 25, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart2_gclk", .id = 26, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart3_gclk", .id = 27, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart4_gclk", .id = 28, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
{ .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, },
{ .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, },
{ .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
@@ -182,14 +202,14 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
if (IS_ERR(hw))
goto err_free;
@@ -229,7 +249,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
if (IS_ERR(regmap_sfr))
regmap_sfr = NULL;
- hw = at91_clk_register_utmi(regmap, regmap_sfr, "utmick", "mainck");
+ hw = at91_clk_register_utmi(regmap, regmap_sfr, "utmick", "mainck", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -240,14 +260,14 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock);
if (IS_ERR(hw))
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE, 0);
@@ -280,7 +300,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 6, i,
+ parent_names, NULL, 6, i,
&sama5d2_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -291,8 +311,9 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d2_systemck[i].n,
- sama5d2_systemck[i].p,
- sama5d2_systemck[i].id);
+ sama5d2_systemck[i].p, NULL,
+ sama5d2_systemck[i].id,
+ sama5d2_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -303,9 +324,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
sama5d2_periphck[i].id,
- &range, INT_MIN);
+ &range, INT_MIN,
+ sama5d2_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -316,10 +338,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_periph32ck[i].n,
- "h32mxck",
+ "h32mxck", NULL,
sama5d2_periph32ck[i].id,
&sama5d2_periph32ck[i].r,
- INT_MIN);
+ INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
@@ -336,7 +358,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_gck[i].n,
- parent_names, NULL, 6,
+ parent_names, NULL, NULL, 6,
sama5d2_gck[i].id,
&sama5d2_gck[i].r,
sama5d2_gck[i].chg_pid);
diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
index 7e93c6edf305..522ce6031446 100644
--- a/drivers/clk/at91/sama5d3.c
+++ b/drivers/clk/at91/sama5d3.c
@@ -40,9 +40,14 @@ static const struct clk_pcr_layout sama5d3_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sama5d3_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "lcdck", .p = "masterck_div", .id = 3 },
{ .n = "smdck", .p = "smdclk", .id = 4 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
@@ -56,6 +61,7 @@ static const struct {
char *n;
u8 id;
struct clk_range r;
+ unsigned long flags;
} sama5d3_periphck[] = {
{ .n = "dbgu_clk", .id = 2, },
{ .n = "hsmc_clk", .id = 5, },
@@ -99,7 +105,11 @@ static const struct {
{ .n = "tdes_clk", .id = 44, },
{ .n = "trng_clk", .id = 45, },
{ .n = "fuse_clk", .id = 48, },
- { .n = "mpddr_clk", .id = 49, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 49, .flags = CLK_IS_CRITICAL },
};
static void __init sama5d3_pmc_setup(struct device_node *np)
@@ -140,14 +150,14 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
if (IS_ERR(hw))
goto err_free;
@@ -162,7 +172,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
sama5d3_pmc->chws[PMC_PLLACK] = hw;
- hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -173,14 +183,14 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock);
if (IS_ERR(hw))
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE, 0);
@@ -210,7 +220,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, NULL, 5, i,
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -221,8 +231,9 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d3_systemck[i].n,
- sama5d3_systemck[i].p,
- sama5d3_systemck[i].id);
+ sama5d3_systemck[i].p, NULL,
+ sama5d3_systemck[i].id,
+ sama5d3_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -233,10 +244,11 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d3_pcr_layout,
sama5d3_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
sama5d3_periphck[i].id,
&sama5d3_periphck[i].r,
- INT_MIN);
+ INT_MIN,
+ sama5d3_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
index 1a14a9bce308..160c0bddb6a3 100644
--- a/drivers/clk/at91/sama5d4.c
+++ b/drivers/clk/at91/sama5d4.c
@@ -39,9 +39,14 @@ static const struct clk_pcr_layout sama5d4_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sama5d4_systemck[] = {
- { .n = "ddrck", .p = "masterck_div", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "lcdck", .p = "masterck_div", .id = 3 },
{ .n = "smdck", .p = "smdclk", .id = 4 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
@@ -103,12 +108,17 @@ static const struct {
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sama5d4_periphck[] = {
{ .n = "dma0_clk", .id = 8 },
{ .n = "cpkcc_clk", .id = 10 },
{ .n = "aesb_clk", .id = 13 },
- { .n = "mpddr_clk", .id = 16 },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 16, .flags = CLK_IS_CRITICAL },
{ .n = "matrix0_clk", .id = 18 },
{ .n = "vdec_clk", .id = 19 },
{ .n = "dma1_clk", .id = 50 },
@@ -155,14 +165,14 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL,
bypass);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2);
if (IS_ERR(hw))
goto err_free;
@@ -177,7 +187,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_pmc->chws[PMC_PLLACK] = hw;
- hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck", NULL);
if (IS_ERR(hw))
goto err_free;
@@ -188,14 +198,14 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
parent_names[2] = "plladivck";
parent_names[3] = "utmick";
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
- parent_names,
+ parent_names, NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock);
if (IS_ERR(hw))
goto err_free;
hw = at91_clk_register_master_div(regmap, "masterck_div",
- "masterck_pres",
+ "masterck_pres", NULL,
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE, 0);
@@ -233,7 +243,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
- parent_names, 5, i,
+ parent_names, NULL, 5, i,
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
@@ -244,8 +254,9 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama5d4_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d4_systemck[i].n,
- sama5d4_systemck[i].p,
- sama5d4_systemck[i].id);
+ sama5d4_systemck[i].p, NULL,
+ sama5d4_systemck[i].id,
+ sama5d4_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -256,9 +267,10 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d4_pcr_layout,
sama5d4_periphck[i].n,
- "masterck_div",
+ "masterck_div", NULL,
sama5d4_periphck[i].id,
- &range, INT_MIN);
+ &range, INT_MIN,
+ sama5d4_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -269,9 +281,9 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d4_pcr_layout,
sama5d4_periph32ck[i].n,
- "h32mxck",
+ "h32mxck", NULL,
sama5d4_periph32ck[i].id,
- &range, INT_MIN);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/sama7d65.c b/drivers/clk/at91/sama7d65.c
new file mode 100644
index 000000000000..7dee2b160ffb
--- /dev/null
+++ b/drivers/clk/at91/sama7d65.c
@@ -0,0 +1,1379 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SAMA7D65 PMC code.
+ *
+ * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Ryan Wanner <ryan.wanner@microchip.com>
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(pmc_mck0_lock);
+static DEFINE_SPINLOCK(pmc_mckX_lock);
+
+#define PMC_INDEX_MAX 25
+
+/*
+ * PLL clocks identifiers
+ * @PLL_ID_CPU: CPU PLL identifier
+ * @PLL_ID_SYS: System PLL identifier
+ * @PLL_ID_DDR: DDR PLL identifier
+ * @PLL_ID_GPU: Graphics subsystem PLL identifier
+ * @PLL_ID_BAUD: Baud PLL identifier
+ * @PLL_ID_AUDIO: Audio PLL identifier
+ * @PLL_ID_ETH: Ethernet PLL identifier
+ * @PLL_ID_LVDS: LVDS PLL identifier
+ * @PLL_ID_USB: USB PLL identifier
+ */
+enum pll_ids {
+ PLL_ID_CPU,
+ PLL_ID_SYS,
+ PLL_ID_DDR,
+ PLL_ID_GPU,
+ PLL_ID_BAUD,
+ PLL_ID_AUDIO,
+ PLL_ID_ETH,
+ PLL_ID_LVDS,
+ PLL_ID_USB,
+ PLL_ID_MAX
+};
+
+/*
+ * PLL component identifier
+ * @PLL_COMPID_FRAC: Fractional PLL component identifier
+ * @PLL_COMPID_DIV0: 1st PLL divider component identifier
+ * @PLL_COMPID_DIV1: 2nd PLL divider component identifier
+ */
+enum pll_component_id {
+ PLL_COMPID_FRAC,
+ PLL_COMPID_DIV0,
+ PLL_COMPID_DIV1,
+ PLL_COMPID_MAX
+};
+
+/*
+ * PLL type identifiers
+ * @PLL_TYPE_FRAC: fractional PLL identifier
+ * @PLL_TYPE_DIV: divider PLL identifier
+ */
+enum pll_type {
+ PLL_TYPE_FRAC,
+ PLL_TYPE_DIV
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_layout_frac = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+/* Layout for DIVPMC dividers. */
+static const struct clk_pll_layout pll_layout_divpmc = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_layout_divio = {
+ .div_mask = GENMASK(19, 12),
+ .endiv_mask = BIT(30),
+ .div_shift = 12,
+ .endiv_shift = 30,
+};
+
+/*
+ * CPU PLL output range.
+ * Notice: The upper limit has been setup to 1000000002 due to hardware
+ * block which cannot output exactly 1GHz.
+ */
+static const struct clk_range cpu_pll_outputs[] = {
+ { .min = 2343750, .max = 1000000002 },
+};
+
+/* PLL output range. */
+static const struct clk_range pll_outputs[] = {
+ { .min = 2343750, .max = 1200000000 },
+};
+
+/*
+ * Min: fCOREPLLCK = 600 MHz, PMC_PLL_CTRL0.DIVPMC = 255
+ * Max: fCOREPLLCK = 800 MHz, PMC_PLL_CTRL0.DIVPMC = 0
+ */
+static const struct clk_range lvdspll_outputs[] = {
+ { .min = 16406250, .max = 800000000 },
+};
+
+static const struct clk_range upll_outputs[] = {
+ { .min = 480000000, .max = 480000000 },
+};
+
+/* Fractional PLL core output range. */
+static const struct clk_range core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+static const struct clk_range lvdspll_core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+static const struct clk_range upll_core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+/* CPU PLL characteristics. */
+static const struct clk_pll_characteristics cpu_pll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(cpu_pll_outputs),
+ .output = cpu_pll_outputs,
+ .core_output = core_outputs,
+ .acr = UL(0x00070010),
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics pll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(pll_outputs),
+ .output = pll_outputs,
+ .core_output = core_outputs,
+ .acr = UL(0x00070010),
+};
+
+static const struct clk_pll_characteristics lvdspll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(lvdspll_outputs),
+ .output = lvdspll_outputs,
+ .core_output = lvdspll_core_outputs,
+ .acr = UL(0x00070010),
+};
+
+static const struct clk_pll_characteristics upll_characteristics = {
+ .input = { .min = 20000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(upll_outputs),
+ .output = upll_outputs,
+ .core_output = upll_core_outputs,
+ .acr = UL(0x12020010),
+ .upll = true,
+};
+
+/*
+ * SAMA7D65 PLL possible parents
+ * @SAMA7D65_PLL_PARENT_MAINCK: MAINCK is PLL a parent
+ * @SAMA7D65_PLL_PARENT_MAIN_XTAL: MAIN XTAL is a PLL parent
+ * @SAMA7D65_PLL_PARENT_FRACCK: Frac PLL is a PLL parent (for PLL dividers)
+ */
+enum sama7d65_pll_parent {
+ SAMA7D65_PLL_PARENT_MAINCK,
+ SAMA7D65_PLL_PARENT_MAIN_XTAL,
+ SAMA7D65_PLL_PARENT_FRACCK
+};
+
+/*
+ * PLL clocks description
+ * @n: clock name
+ * @l: clock layout
+ * @c: clock characteristics
+ * @hw: pointer to clk_hw
+ * @t: clock type
+ * @f: clock flags
+ * @p: clock parent
+ * @eid: export index in sama7d65->chws[] array
+ * @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
+ * notification
+ */
+static struct sama7d65_pll {
+ const char *n;
+ const struct clk_pll_layout *l;
+ const struct clk_pll_characteristics *c;
+ struct clk_hw *hw;
+ unsigned long f;
+ enum sama7d65_pll_parent p;
+ u8 t;
+ u8 eid;
+ u8 safe_div;
+} sama7d65_plls[][PLL_COMPID_MAX] = {
+ [PLL_ID_CPU] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "cpupll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds cpupll_divpmcck which feeds CPU. It should
+ * not be disabled.
+ */
+ .f = CLK_IS_CRITICAL,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "cpupll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds CPU. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
+ .eid = PMC_CPUPLL,
+ /*
+ * Safe div=15 should be safe even for switching b/w 1GHz and
+ * 90MHz (frac pll might go up to 1.2GHz).
+ */
+ .safe_div = 15,
+ },
+ },
+
+ [PLL_ID_SYS] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "syspll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds syspll_divpmcck which may feed critical parts
+ * of the systems like timers. Therefore it should not be
+ * disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "syspll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /*
+ * This may feed critical parts of the systems like timers.
+ * Therefore it should not be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .eid = PMC_SYSPLL,
+ },
+ },
+
+ [PLL_ID_DDR] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "ddrpll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds ddrpll_divpmcck which feeds DDR. It should not
+ * be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "ddrpll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds DDR. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ },
+ },
+
+ [PLL_ID_GPU] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "gpupll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "gpupll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ },
+ },
+
+ [PLL_ID_BAUD] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "baudpll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "baudpll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_BAUDPLL,
+ },
+ },
+
+ [PLL_ID_AUDIO] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "audiopll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAIN_XTAL,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "audiopll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOPMCPLL,
+ },
+
+ [PLL_COMPID_DIV1] = {
+ .n = "audiopll_diviock",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divio,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOIOPLL,
+ },
+ },
+
+ [PLL_ID_ETH] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "ethpll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAIN_XTAL,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "ethpll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_ETHPLL,
+ },
+ },
+
+ [PLL_ID_LVDS] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "lvdspll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAIN_XTAL,
+ .l = &pll_layout_frac,
+ .c = &lvdspll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "lvdspll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &lvdspll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_LVDSPLL,
+ },
+ },
+
+ [PLL_ID_USB] = {
+ [PLL_COMPID_FRAC] = {
+ .n = "usbpll_fracck",
+ .p = SAMA7D65_PLL_PARENT_MAIN_XTAL,
+ .l = &pll_layout_frac,
+ .c = &upll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "usbpll_divpmcck",
+ .p = SAMA7D65_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &upll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_UTMI,
+ },
+ },
+};
+
+/* Used to create an array entry identifying a PLL by its components. */
+#define PLL_IDS_TO_ARR_ENTRY(_id, _comp) { PLL_ID_##_id, PLL_COMPID_##_comp}
+
+/*
+ * Master clock (MCK[0..9]) description
+ * @n: clock name
+ * @ep_chg_chg_id: index in parents array that specifies the changeable
+ * @ep: extra parents names array (entry formed by PLL components
+ * identifiers (see enum pll_component_id))
+ * @hw: pointer to clk_hw
+ * parent
+ * @ep_count: extra parents count
+ * @ep_mux_table: mux table for extra parents
+ * @id: clock id
+ * @eid: export index in sama7d65->chws[] array
+ * @c: true if clock is critical and cannot be disabled
+ */
+static struct {
+ const char *n;
+ struct {
+ int pll_id;
+ int pll_compid;
+ } ep[4];
+ struct clk_hw *hw;
+ int ep_chg_id;
+ u8 ep_count;
+ u8 ep_mux_table[4];
+ u8 id;
+ u8 eid;
+ u8 c;
+} sama7d65_mckx[] = {
+ { .n = "mck0", }, /* Dummy entry for MCK0 to store hw in probe. */
+ { .n = "mck1",
+ .id = 1,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .eid = PMC_MCK1,
+ .c = 1, },
+
+ { .n = "mck2",
+ .id = 2,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(DDR, DIV0), },
+ .ep_mux_table = { 5, 6, },
+ .ep_count = 2,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+
+ { .n = "mck3",
+ .id = 3,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(DDR, DIV0), },
+ .ep_mux_table = { 5, 6, },
+ .ep_count = 2,
+ .ep_chg_id = INT_MIN,
+ .eid = PMC_MCK3,
+ .c = 1, },
+
+ { .n = "mck4",
+ .id = 4,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+
+ { .n = "mck5",
+ .id = 5,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .eid = PMC_MCK5,
+ .c = 1, },
+
+ { .n = "mck6",
+ .id = 6,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_chg_id = INT_MIN,
+ .ep_count = 1,
+ .c = 1, },
+
+ { .n = "mck7",
+ .id = 7,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_chg_id = INT_MIN,
+ .ep_count = 1, },
+
+ { .n = "mck8",
+ .id = 8,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_chg_id = INT_MIN,
+ .ep_count = 1, },
+
+ { .n = "mck9",
+ .id = 9,
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .ep_mux_table = { 5, },
+ .ep_chg_id = INT_MIN,
+ .ep_count = 1, },
+};
+
+/*
+ * System clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ u8 id;
+} sama7d65_systemck[] = {
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "pck0", .p = "prog0", .id = 8, },
+ { .n = "pck1", .p = "prog1", .id = 9, },
+ { .n = "pck2", .p = "prog2", .id = 10, },
+ { .n = "pck3", .p = "prog3", .id = 11, },
+ { .n = "pck4", .p = "prog4", .id = 12, },
+ { .n = "pck5", .p = "prog5", .id = 13, },
+ { .n = "pck6", .p = "prog6", .id = 14, },
+ { .n = "pck7", .p = "prog7", .id = 15, },
+};
+
+/* Mux table for programmable clocks. */
+static u32 sama7d65_prog_mux_table[] = { 0, 1, 2, 5, 7, 8, 9, 10, 12 };
+
+/*
+ * Peripheral clock parent hw identifier (used to index in sama7d65_mckx[])
+ * @PCK_PARENT_HW_MCK0: pck parent hw identifier is MCK0
+ * @PCK_PARENT_HW_MCK1: pck parent hw identifier is MCK1
+ * @PCK_PARENT_HW_MCK2: pck parent hw identifier is MCK2
+ * @PCK_PARENT_HW_MCK3: pck parent hw identifier is MCK3
+ * @PCK_PARENT_HW_MCK4: pck parent hw identifier is MCK4
+ * @PCK_PARENT_HW_MCK5: pck parent hw identifier is MCK5
+ * @PCK_PARENT_HW_MCK6: pck parent hw identifier is MCK6
+ * @PCK_PARENT_HW_MCK7: pck parent hw identifier is MCK7
+ * @PCK_PARENT_HW_MCK8: pck parent hw identifier is MCK8
+ * @PCK_PARENT_HW_MCK9: pck parent hw identifier is MCK9
+ * @PCK_PARENT_HW_MAX: max identifier
+ */
+enum sama7d65_pck_parent_hw_id {
+ PCK_PARENT_HW_MCK0,
+ PCK_PARENT_HW_MCK1,
+ PCK_PARENT_HW_MCK2,
+ PCK_PARENT_HW_MCK3,
+ PCK_PARENT_HW_MCK4,
+ PCK_PARENT_HW_MCK5,
+ PCK_PARENT_HW_MCK6,
+ PCK_PARENT_HW_MCK7,
+ PCK_PARENT_HW_MCK8,
+ PCK_PARENT_HW_MCK9,
+ PCK_PARENT_HW_MAX
+};
+
+/*
+ * Peripheral clock description
+ * @n: clock name
+ * @p: clock parent hw id
+ * @r: clock range values
+ * @id: clock id
+ * @chgp: index in parent array of the changeable parent
+ */
+static struct {
+ const char *n;
+ enum sama7d65_pck_parent_hw_id p;
+ struct clk_range r;
+ u8 chgp;
+ u8 id;
+} sama7d65_periphck[] = {
+ { .n = "pioA_clk", .p = PCK_PARENT_HW_MCK0, .id = 10, },
+ { .n = "securam_clk", .p = PCK_PARENT_HW_MCK0, .id = 17, },
+ { .n = "sfr_clk", .p = PCK_PARENT_HW_MCK7, .id = 18, },
+ { .n = "hsmc_clk", .p = PCK_PARENT_HW_MCK5, .id = 20, },
+ { .n = "xdmac0_clk", .p = PCK_PARENT_HW_MCK6, .id = 21, },
+ { .n = "xdmac1_clk", .p = PCK_PARENT_HW_MCK6, .id = 22, },
+ { .n = "xdmac2_clk", .p = PCK_PARENT_HW_MCK1, .id = 23, },
+ { .n = "acc_clk", .p = PCK_PARENT_HW_MCK7, .id = 24, },
+ { .n = "aes_clk", .p = PCK_PARENT_HW_MCK6, .id = 26, },
+ { .n = "tzaesbasc_clk", .p = PCK_PARENT_HW_MCK8, .id = 27, },
+ { .n = "asrc_clk", .p = PCK_PARENT_HW_MCK9, .id = 29, .r = { .max = 200000000, }, },
+ { .n = "cpkcc_clk", .p = PCK_PARENT_HW_MCK0, .id = 30, },
+ { .n = "eic_clk", .p = PCK_PARENT_HW_MCK7, .id = 33, },
+ { .n = "flex0_clk", .p = PCK_PARENT_HW_MCK7, .id = 34, },
+ { .n = "flex1_clk", .p = PCK_PARENT_HW_MCK7, .id = 35, },
+ { .n = "flex2_clk", .p = PCK_PARENT_HW_MCK7, .id = 36, },
+ { .n = "flex3_clk", .p = PCK_PARENT_HW_MCK7, .id = 37, },
+ { .n = "flex4_clk", .p = PCK_PARENT_HW_MCK8, .id = 38, },
+ { .n = "flex5_clk", .p = PCK_PARENT_HW_MCK8, .id = 39, },
+ { .n = "flex6_clk", .p = PCK_PARENT_HW_MCK8, .id = 40, },
+ { .n = "flex7_clk", .p = PCK_PARENT_HW_MCK8, .id = 41, },
+ { .n = "flex8_clk", .p = PCK_PARENT_HW_MCK9, .id = 42, },
+ { .n = "flex9_clk", .p = PCK_PARENT_HW_MCK9, .id = 43, },
+ { .n = "flex10_clk", .p = PCK_PARENT_HW_MCK9, .id = 44, },
+ { .n = "gmac0_clk", .p = PCK_PARENT_HW_MCK6, .id = 46, },
+ { .n = "gmac1_clk", .p = PCK_PARENT_HW_MCK6, .id = 47, },
+ { .n = "gmac0_tsu_clk", .p = PCK_PARENT_HW_MCK1, .id = 49, },
+ { .n = "gmac1_tsu_clk", .p = PCK_PARENT_HW_MCK1, .id = 50, },
+ { .n = "icm_clk", .p = PCK_PARENT_HW_MCK5, .id = 53, },
+ { .n = "i2smcc0_clk", .p = PCK_PARENT_HW_MCK9, .id = 54, .r = { .max = 200000000, }, },
+ { .n = "i2smcc1_clk", .p = PCK_PARENT_HW_MCK9, .id = 55, .r = { .max = 200000000, }, },
+ { .n = "lcd_clk", .p = PCK_PARENT_HW_MCK3, .id = 56, },
+ { .n = "matrix_clk", .p = PCK_PARENT_HW_MCK5, .id = 57, },
+ { .n = "mcan0_clk", .p = PCK_PARENT_HW_MCK5, .id = 58, .r = { .max = 200000000, }, },
+ { .n = "mcan1_clk", .p = PCK_PARENT_HW_MCK5, .id = 59, .r = { .max = 200000000, }, },
+ { .n = "mcan2_clk", .p = PCK_PARENT_HW_MCK5, .id = 60, .r = { .max = 200000000, }, },
+ { .n = "mcan3_clk", .p = PCK_PARENT_HW_MCK5, .id = 61, .r = { .max = 200000000, }, },
+ { .n = "mcan4_clk", .p = PCK_PARENT_HW_MCK5, .id = 62, .r = { .max = 200000000, }, },
+ { .n = "pdmc0_clk", .p = PCK_PARENT_HW_MCK9, .id = 64, .r = { .max = 200000000, }, },
+ { .n = "pdmc1_clk", .p = PCK_PARENT_HW_MCK9, .id = 65, .r = { .max = 200000000, }, },
+ { .n = "pit64b0_clk", .p = PCK_PARENT_HW_MCK7, .id = 66, },
+ { .n = "pit64b1_clk", .p = PCK_PARENT_HW_MCK7, .id = 67, },
+ { .n = "pit64b2_clk", .p = PCK_PARENT_HW_MCK7, .id = 68, },
+ { .n = "pit64b3_clk", .p = PCK_PARENT_HW_MCK8, .id = 69, },
+ { .n = "pit64b4_clk", .p = PCK_PARENT_HW_MCK8, .id = 70, },
+ { .n = "pit64b5_clk", .p = PCK_PARENT_HW_MCK8, .id = 71, },
+ { .n = "pwm_clk", .p = PCK_PARENT_HW_MCK7, .id = 72, },
+ { .n = "qspi0_clk", .p = PCK_PARENT_HW_MCK5, .id = 73, },
+ { .n = "qspi1_clk", .p = PCK_PARENT_HW_MCK5, .id = 74, },
+ { .n = "sdmmc0_clk", .p = PCK_PARENT_HW_MCK1, .id = 75, },
+ { .n = "sdmmc1_clk", .p = PCK_PARENT_HW_MCK1, .id = 76, },
+ { .n = "sdmmc2_clk", .p = PCK_PARENT_HW_MCK1, .id = 77, },
+ { .n = "sha_clk", .p = PCK_PARENT_HW_MCK6, .id = 78, },
+ { .n = "spdifrx_clk", .p = PCK_PARENT_HW_MCK9, .id = 79, .r = { .max = 200000000, }, },
+ { .n = "spdiftx_clk", .p = PCK_PARENT_HW_MCK9, .id = 80, .r = { .max = 200000000, }, },
+ { .n = "ssc0_clk", .p = PCK_PARENT_HW_MCK7, .id = 81, .r = { .max = 200000000, }, },
+ { .n = "ssc1_clk", .p = PCK_PARENT_HW_MCK8, .id = 82, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch0_clk", .p = PCK_PARENT_HW_MCK8, .id = 83, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch1_clk", .p = PCK_PARENT_HW_MCK8, .id = 84, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch2_clk", .p = PCK_PARENT_HW_MCK8, .id = 85, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch0_clk", .p = PCK_PARENT_HW_MCK5, .id = 86, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch1_clk", .p = PCK_PARENT_HW_MCK5, .id = 87, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch2_clk", .p = PCK_PARENT_HW_MCK5, .id = 88, .r = { .max = 200000000, }, },
+ { .n = "tcpca_clk", .p = PCK_PARENT_HW_MCK5, .id = 89, },
+ { .n = "tcpcb_clk", .p = PCK_PARENT_HW_MCK5, .id = 90, },
+ { .n = "tdes_clk", .p = PCK_PARENT_HW_MCK6, .id = 91, },
+ { .n = "trng_clk", .p = PCK_PARENT_HW_MCK6, .id = 92, },
+ { .n = "udphsa_clk", .p = PCK_PARENT_HW_MCK5, .id = 99, },
+ { .n = "udphsb_clk", .p = PCK_PARENT_HW_MCK5, .id = 100, },
+ { .n = "uhphs_clk", .p = PCK_PARENT_HW_MCK5, .id = 101, },
+ { .n = "dsi_clk", .p = PCK_PARENT_HW_MCK3, .id = 103, },
+ { .n = "lvdsc_clk", .p = PCK_PARENT_HW_MCK3, .id = 104, },
+};
+
+/*
+ * Generic clock description
+ * @n: clock name
+ * @pp: PLL parents (entry formed by PLL components identifiers
+ * (see enum pll_component_id))
+ * @pp_mux_table: PLL parents mux table
+ * @r: clock output range
+ * @pp_chg_id: id in parent array of changeable PLL parent
+ * @pp_count: PLL parents count
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ struct {
+ int pll_id;
+ int pll_compid;
+ } pp[8];
+ const char pp_mux_table[8];
+ struct clk_range r;
+ int pp_chg_id;
+ u8 pp_count;
+ u8 id;
+} sama7d65_gck[] = {
+ { .n = "adc_gclk",
+ .id = 25,
+ .r = { .max = 100000000, },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 8, 9, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "asrc_gclk",
+ .id = 29,
+ .r = { .max = 200000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex0_gclk",
+ .id = 34,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = {8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex1_gclk",
+ .id = 35,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = {8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex2_gclk",
+ .id = 36,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = {8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex3_gclk",
+ .id = 37,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = {8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex4_gclk",
+ .id = 38,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex5_gclk",
+ .id = 39,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex6_gclk",
+ .id = 40,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex7_gclk",
+ .id = 41,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex8_gclk",
+ .id = 42,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex9_gclk",
+ .id = 43,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex10_gclk",
+ .id = 44,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 8, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_gclk",
+ .id = 46,
+ .r = { .max = 125000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "gmac1_gclk",
+ .id = 47,
+ .r = { .max = 125000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "gmac0_tsu_gclk",
+ .id = 49,
+ .r = { .max = 400000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = {10, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac1_tsu_gclk",
+ .id = 50,
+ .r = { .max = 400000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "i2smcc0_gclk",
+ .id = 54,
+ .r = { .max = 100000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "i2smcc1_gclk",
+ .id = 55,
+ .r = { .max = 100000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "lcdc_gclk",
+ .id = 56,
+ .r = { .max = 90000000 },
+ .pp_count = 0,
+ .pp_chg_id = INT_MIN,
+ },
+
+ { .n = "mcan0_gclk",
+ .id = 58,
+ .r = { .max = 80000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(USB, DIV0), },
+ .pp_mux_table = { 12 },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "mcan1_gclk",
+ .id = 59,
+ .r = { .max = 80000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(USB, DIV0), },
+ .pp_mux_table = { 12 },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "mcan2_gclk",
+ .id = 60,
+ .r = { .max = 80000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(USB, DIV0), },
+ .pp_mux_table = { 12 },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "mcan3_gclk",
+ .id = 61,
+ .r = { .max = 80000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(USB, DIV0), },
+ .pp_mux_table = { 12 },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "mcan4_gclk",
+ .id = 62,
+ .r = { .max = 80000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(USB, DIV0), },
+ .pp_mux_table = { 12 },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "pdmc0_gclk",
+ .id = 64,
+ .r = { .max = 80000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9 },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc1_gclk",
+ .id = 65,
+ .r = { .max = 80000000, },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b0_gclk",
+ .id = 66,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b1_gclk",
+ .id = 67,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b2_gclk",
+ .id = 68,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b3_gclk",
+ .id = 69,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = {8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b4_gclk",
+ .id = 70,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = {8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b5_gclk",
+ .id = 71,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = {8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi0_gclk",
+ .id = 73,
+ .r = { .max = 400000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi1_gclk",
+ .id = 74,
+ .r = { .max = 266000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "sdmmc0_gclk",
+ .id = 75,
+ .r = { .max = 208000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 10, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "sdmmc1_gclk",
+ .id = 76,
+ .r = { .max = 208000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 10, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "sdmmc2_gclk",
+ .id = 77,
+ .r = { .max = 208000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 10 },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "spdifrx_gclk",
+ .id = 79,
+ .r = { .max = 150000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "spdiftx_gclk",
+ .id = 80,
+ .r = { .max = 25000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcb0_ch0_gclk",
+ .id = 83,
+ .r = { .max = 34000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcb1_ch0_gclk",
+ .id = 86,
+ .r = { .max = 67000000 },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = { 8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "DSI_gclk",
+ .id = 103,
+ .r = {.max = 27000000},
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
+ .pp_mux_table = {5},
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "I3CC_gclk",
+ .id = 105,
+ .r = {.max = 125000000},
+ .pp = { PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
+ .pp_mux_table = {8, 9, 10, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+};
+
+/* MCK0 characteristics. */
+static const struct clk_master_characteristics mck0_characteristics = {
+ .output = { .min = 32768, .max = 200000000 },
+ .divisors = { 1, 2, 4, 3, 5 },
+ .have_div3_pres = 1,
+};
+
+/* MCK0 layout. */
+static const struct clk_master_layout mck0_layout = {
+ .mask = 0x773,
+ .pres_shift = 4,
+ .offset = 0x28,
+};
+
+/* Programmable clock layout. */
+static const struct clk_programmable_layout programmable_layout = {
+ .pres_mask = 0xff,
+ .pres_shift = 8,
+ .css_mask = 0x1f,
+ .have_slck_mck = 0,
+ .is_pres_direct = 1,
+};
+
+/* Peripheral clock layout. */
+static const struct clk_pcr_layout sama7d65_pcr_layout = {
+ .offset = 0x88,
+ .cmd = BIT(31),
+ .gckcss_mask = GENMASK(12, 8),
+ .pid_mask = GENMASK(6, 0),
+};
+
+static void __init sama7d65_pmc_setup(struct device_node *np)
+{
+ const char *main_xtal_name = "main_xtal";
+ struct pmc_data *sama7d65_pmc;
+ const char *parent_names[11];
+ void **alloc_mem = NULL;
+ int alloc_mem_size = 0;
+ struct regmap *regmap;
+ struct clk_hw *hw, *main_rc_hw, *main_osc_hw, *main_xtal_hw;
+ struct clk_hw *td_slck_hw, *md_slck_hw;
+ static struct clk_parent_data parent_data;
+ struct clk_hw *parent_hws[10];
+ bool bypass;
+ int i, j;
+
+ td_slck_hw = __clk_get_hw(of_clk_get_by_name(np, "td_slck"));
+ md_slck_hw = __clk_get_hw(of_clk_get_by_name(np, "md_slck"));
+ main_xtal_hw = __clk_get_hw(of_clk_get_by_name(np, main_xtal_name));
+
+ if (!td_slck_hw || !md_slck_hw || !main_xtal_hw)
+ return;
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sama7d65_pmc = pmc_data_allocate(PMC_INDEX_MAX,
+ nck(sama7d65_systemck),
+ nck(sama7d65_periphck),
+ nck(sama7d65_gck), 8);
+ if (!sama7d65_pmc)
+ return;
+
+ alloc_mem = kmalloc(sizeof(void *) *
+ (ARRAY_SIZE(sama7d65_mckx) + ARRAY_SIZE(sama7d65_gck)),
+ GFP_KERNEL);
+ if (!alloc_mem)
+ goto err_free;
+
+ main_rc_hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(main_rc_hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ parent_data.name = main_xtal_name;
+ parent_data.fw_name = main_xtal_name;
+ main_osc_hw = at91_clk_register_main_osc(regmap, "main_osc", NULL,
+ &parent_data, bypass);
+ if (IS_ERR(main_osc_hw))
+ goto err_free;
+
+ parent_hws[0] = main_rc_hw;
+ parent_hws[1] = main_osc_hw;
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", NULL, parent_hws, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_pmc->chws[PMC_MAIN] = hw;
+
+ for (i = 0; i < PLL_ID_MAX; i++) {
+ for (j = 0; j < PLL_COMPID_MAX; j++) {
+ struct clk_hw *parent_hw;
+
+ if (!sama7d65_plls[i][j].n)
+ continue;
+
+ switch (sama7d65_plls[i][j].t) {
+ case PLL_TYPE_FRAC:
+ switch (sama7d65_plls[i][j].p) {
+ case SAMA7D65_PLL_PARENT_MAINCK:
+ parent_hw = sama7d65_pmc->chws[PMC_MAIN];
+ break;
+ case SAMA7D65_PLL_PARENT_MAIN_XTAL:
+ parent_hw = main_xtal_hw;
+ break;
+ default:
+ /* Should not happen. */
+ parent_hw = NULL;
+ break;
+ }
+
+ hw = sam9x60_clk_register_frac_pll(regmap,
+ &pmc_pll_lock, sama7d65_plls[i][j].n,
+ NULL, parent_hw, i,
+ sama7d65_plls[i][j].c,
+ sama7d65_plls[i][j].l,
+ sama7d65_plls[i][j].f);
+ break;
+
+ case PLL_TYPE_DIV:
+ hw = sam9x60_clk_register_div_pll(regmap,
+ &pmc_pll_lock, sama7d65_plls[i][j].n,
+ NULL, sama7d65_plls[i][0].hw, i,
+ sama7d65_plls[i][j].c,
+ sama7d65_plls[i][j].l,
+ sama7d65_plls[i][j].f,
+ sama7d65_plls[i][j].safe_div);
+ break;
+
+ default:
+ continue;
+ }
+
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_plls[i][j].hw = hw;
+ if (sama7d65_plls[i][j].eid)
+ sama7d65_pmc->chws[sama7d65_plls[i][j].eid] = hw;
+ }
+ }
+
+ hw = at91_clk_register_master_div(regmap, "mck0", NULL,
+ sama7d65_plls[PLL_ID_CPU][1].hw,
+ &mck0_layout, &mck0_characteristics,
+ &pmc_mck0_lock, CLK_GET_RATE_NOCACHE, 5);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_pmc->chws[PMC_MCK] = hw;
+ sama7d65_mckx[PCK_PARENT_HW_MCK0].hw = hw;
+
+ parent_hws[0] = md_slck_hw;
+ parent_hws[1] = td_slck_hw;
+ parent_hws[2] = sama7d65_pmc->chws[PMC_MAIN];
+ for (i = PCK_PARENT_HW_MCK1; i < ARRAY_SIZE(sama7d65_mckx); i++) {
+ u8 num_parents = 3 + sama7d65_mckx[i].ep_count;
+ struct clk_hw *tmp_parent_hws[8];
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ alloc_mem[alloc_mem_size++] = mux_table;
+
+ PMC_INIT_TABLE(mux_table, 3);
+ PMC_FILL_TABLE(&mux_table[3], sama7d65_mckx[i].ep_mux_table,
+ sama7d65_mckx[i].ep_count);
+ for (j = 0; j < sama7d65_mckx[i].ep_count; j++) {
+ u8 pll_id = sama7d65_mckx[i].ep[j].pll_id;
+ u8 pll_compid = sama7d65_mckx[i].ep[j].pll_compid;
+
+ tmp_parent_hws[j] = sama7d65_plls[pll_id][pll_compid].hw;
+ }
+ PMC_FILL_TABLE(&parent_hws[3], tmp_parent_hws,
+ sama7d65_mckx[i].ep_count);
+
+ hw = at91_clk_sama7g5_register_master(regmap, sama7d65_mckx[i].n,
+ num_parents, NULL, parent_hws,
+ mux_table, &pmc_mckX_lock,
+ sama7d65_mckx[i].id,
+ sama7d65_mckx[i].c,
+ sama7d65_mckx[i].ep_chg_id);
+
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_mckx[i].hw = hw;
+ if (sama7d65_mckx[i].eid)
+ sama7d65_pmc->chws[sama7d65_mckx[i].eid] = hw;
+ }
+
+ parent_names[0] = "syspll_divpmcck";
+ parent_names[1] = "usbpll_divpmcck";
+ parent_names[2] = "main_osc";
+ hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_hws[0] = md_slck_hw;
+ parent_hws[1] = td_slck_hw;
+ parent_hws[2] = sama7d65_pmc->chws[PMC_MAIN];
+ parent_hws[3] = sama7d65_plls[PLL_ID_SYS][PLL_COMPID_DIV0].hw;
+ parent_hws[4] = sama7d65_plls[PLL_ID_DDR][PLL_COMPID_DIV0].hw;
+ parent_hws[5] = sama7d65_plls[PLL_ID_GPU][PLL_COMPID_DIV0].hw;
+ parent_hws[6] = sama7d65_plls[PLL_ID_BAUD][PLL_COMPID_DIV0].hw;
+ parent_hws[7] = sama7d65_plls[PLL_ID_AUDIO][PLL_COMPID_DIV0].hw;
+ parent_hws[8] = sama7d65_plls[PLL_ID_ETH][PLL_COMPID_DIV0].hw;
+
+ for (i = 0; i < 8; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name, NULL, parent_hws,
+ 9, i,
+ &programmable_layout,
+ sama7d65_prog_mux_table);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7d65_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama7d65_systemck[i].n,
+ sama7d65_systemck[i].p, NULL,
+ sama7d65_systemck[i].id, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_pmc->shws[sama7d65_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7d65_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &sama7d65_pcr_layout,
+ sama7d65_periphck[i].n,
+ NULL,
+ sama7d65_mckx[sama7d65_periphck[i].p].hw,
+ sama7d65_periphck[i].id,
+ &sama7d65_periphck[i].r,
+ sama7d65_periphck[i].chgp ? 0 :
+ INT_MIN, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_pmc->phws[sama7d65_periphck[i].id] = hw;
+ }
+
+ parent_hws[0] = md_slck_hw;
+ parent_hws[1] = td_slck_hw;
+ parent_hws[2] = sama7d65_pmc->chws[PMC_MAIN];
+ parent_hws[3] = sama7d65_pmc->chws[PMC_MCK1];
+ for (i = 0; i < ARRAY_SIZE(sama7d65_gck); i++) {
+ u8 num_parents = 4 + sama7d65_gck[i].pp_count;
+ struct clk_hw *tmp_parent_hws[8];
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ alloc_mem[alloc_mem_size++] = mux_table;
+
+ PMC_INIT_TABLE(mux_table, 4);
+ PMC_FILL_TABLE(&mux_table[4], sama7d65_gck[i].pp_mux_table,
+ sama7d65_gck[i].pp_count);
+ for (j = 0; j < sama7d65_gck[i].pp_count; j++) {
+ u8 pll_id = sama7d65_gck[i].pp[j].pll_id;
+ u8 pll_compid = sama7d65_gck[i].pp[j].pll_compid;
+
+ tmp_parent_hws[j] = sama7d65_plls[pll_id][pll_compid].hw;
+ }
+ PMC_FILL_TABLE(&parent_hws[4], tmp_parent_hws,
+ sama7d65_gck[i].pp_count);
+
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
+ &sama7d65_pcr_layout,
+ sama7d65_gck[i].n, NULL,
+ parent_hws, mux_table,
+ num_parents,
+ sama7d65_gck[i].id,
+ &sama7d65_gck[i].r,
+ sama7d65_gck[i].pp_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7d65_pmc->ghws[sama7d65_gck[i].id] = hw;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7d65_pmc);
+ kfree(alloc_mem);
+
+ return;
+
+err_free:
+ if (alloc_mem) {
+ for (i = 0; i < alloc_mem_size; i++)
+ kfree(alloc_mem[i]);
+ kfree(alloc_mem);
+ }
+
+ kfree(sama7d65_pmc);
+}
+
+/* Some clks are used for a clocksource */
+CLK_OF_DECLARE(sama7d65_pmc, "microchip,sama7d65-pmc", sama7d65_pmc_setup);
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
index 9a213ba9e58b..1340c2b00619 100644
--- a/drivers/clk/at91/sama7g5.c
+++ b/drivers/clk/at91/sama7g5.c
@@ -16,21 +16,6 @@
#include "pmc.h"
-#define SAMA7G5_INIT_TABLE(_table, _count) \
- do { \
- u8 _i; \
- for (_i = 0; _i < (_count); _i++) \
- (_table)[_i] = _i; \
- } while (0)
-
-#define SAMA7G5_FILL_TABLE(_to, _from, _count) \
- do { \
- u8 _i; \
- for (_i = 0; _i < (_count); _i++) { \
- (_to)[_i] = (_from)[_i]; \
- } \
- } while (0)
-
static DEFINE_SPINLOCK(pmc_pll_lock);
static DEFINE_SPINLOCK(pmc_mck0_lock);
static DEFINE_SPINLOCK(pmc_mckX_lock);
@@ -57,6 +42,19 @@ enum pll_ids {
};
/*
+ * PLL component identifier
+ * @PLL_COMPID_FRAC: Fractional PLL component identifier
+ * @PLL_COMPID_DIV0: 1st PLL divider component identifier
+ * @PLL_COMPID_DIV1: 2nd PLL divider component identifier
+ */
+enum pll_component_id {
+ PLL_COMPID_FRAC,
+ PLL_COMPID_DIV0,
+ PLL_COMPID_DIV1,
+ PLL_COMPID_MAX,
+};
+
+/*
* PLL type identifiers
* @PLL_TYPE_FRAC: fractional PLL identifier
* @PLL_TYPE_DIV: divider PLL identifier
@@ -104,11 +102,18 @@ static const struct clk_range pll_outputs[] = {
{ .min = 2343750, .max = 1200000000 },
};
+/* Fractional PLL core output range. */
+static const struct clk_range core_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
/* CPU PLL characteristics. */
static const struct clk_pll_characteristics cpu_pll_characteristics = {
.input = { .min = 12000000, .max = 50000000 },
.num_output = ARRAY_SIZE(cpu_pll_outputs),
.output = cpu_pll_outputs,
+ .core_output = core_outputs,
+ .acr = UL(0x00070010),
};
/* PLL characteristics. */
@@ -116,188 +121,238 @@ static const struct clk_pll_characteristics pll_characteristics = {
.input = { .min = 12000000, .max = 50000000 },
.num_output = ARRAY_SIZE(pll_outputs),
.output = pll_outputs,
+ .core_output = core_outputs,
+ .acr = UL(0x00070010),
+};
+
+/*
+ * SAMA7G5 PLL possible parents
+ * @SAMA7G5_PLL_PARENT_MAINCK: MAINCK is PLL a parent
+ * @SAMA7G5_PLL_PARENT_MAIN_XTAL: MAIN XTAL is a PLL parent
+ * @SAMA7G5_PLL_PARENT_FRACCK: Frac PLL is a PLL parent (for PLL dividers)
+ */
+enum sama7g5_pll_parent {
+ SAMA7G5_PLL_PARENT_MAINCK,
+ SAMA7G5_PLL_PARENT_MAIN_XTAL,
+ SAMA7G5_PLL_PARENT_FRACCK,
};
/*
* PLL clocks description
* @n: clock name
- * @p: clock parent
* @l: clock layout
* @c: clock characteristics
+ * @hw: pointer to clk_hw
* @t: clock type
* @f: clock flags
+ * @p: clock parent
* @eid: export index in sama7g5->chws[] array
* @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
* notification
*/
-static const struct {
+static struct sama7g5_pll {
const char *n;
- const char *p;
const struct clk_pll_layout *l;
const struct clk_pll_characteristics *c;
+ struct clk_hw *hw;
unsigned long f;
+ enum sama7g5_pll_parent p;
u8 t;
u8 eid;
u8 safe_div;
-} sama7g5_plls[][PLL_ID_MAX] = {
+} sama7g5_plls[][PLL_COMPID_MAX] = {
[PLL_ID_CPU] = {
- { .n = "cpupll_fracck",
- .p = "mainck",
- .l = &pll_layout_frac,
- .c = &cpu_pll_characteristics,
- .t = PLL_TYPE_FRAC,
- /*
- * This feeds cpupll_divpmcck which feeds CPU. It should
- * not be disabled.
- */
- .f = CLK_IS_CRITICAL, },
-
- { .n = "cpupll_divpmcck",
- .p = "cpupll_fracck",
- .l = &pll_layout_divpmc,
- .c = &cpu_pll_characteristics,
- .t = PLL_TYPE_DIV,
- /* This feeds CPU. It should not be disabled. */
- .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
- .eid = PMC_CPUPLL,
- /*
- * Safe div=15 should be safe even for switching b/w 1GHz and
- * 90MHz (frac pll might go up to 1.2GHz).
- */
- .safe_div = 15, },
+ [PLL_COMPID_FRAC] = {
+ .n = "cpupll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds cpupll_divpmcck which feeds CPU. It should
+ * not be disabled.
+ */
+ .f = CLK_IS_CRITICAL,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "cpupll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds CPU. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
+ .eid = PMC_CPUPLL,
+ /*
+ * Safe div=15 should be safe even for switching b/w 1GHz and
+ * 90MHz (frac pll might go up to 1.2GHz).
+ */
+ .safe_div = 15,
+ },
},
[PLL_ID_SYS] = {
- { .n = "syspll_fracck",
- .p = "mainck",
- .l = &pll_layout_frac,
- .c = &pll_characteristics,
- .t = PLL_TYPE_FRAC,
- /*
- * This feeds syspll_divpmcck which may feed critical parts
- * of the systems like timers. Therefore it should not be
- * disabled.
- */
- .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
-
- { .n = "syspll_divpmcck",
- .p = "syspll_fracck",
- .l = &pll_layout_divpmc,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- /*
- * This may feed critical parts of the systems like timers.
- * Therefore it should not be disabled.
- */
- .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
- .eid = PMC_SYSPLL, },
+ [PLL_COMPID_FRAC] = {
+ .n = "syspll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds syspll_divpmcck which may feed critical parts
+ * of the systems like timers. Therefore it should not be
+ * disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "syspll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /*
+ * This may feed critical parts of the systems like timers.
+ * Therefore it should not be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .eid = PMC_SYSPLL,
+ },
},
[PLL_ID_DDR] = {
- { .n = "ddrpll_fracck",
- .p = "mainck",
- .l = &pll_layout_frac,
- .c = &pll_characteristics,
- .t = PLL_TYPE_FRAC,
- /*
- * This feeds ddrpll_divpmcck which feeds DDR. It should not
- * be disabled.
- */
- .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
-
- { .n = "ddrpll_divpmcck",
- .p = "ddrpll_fracck",
- .l = &pll_layout_divpmc,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- /* This feeds DDR. It should not be disabled. */
- .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+ [PLL_COMPID_FRAC] = {
+ .n = "ddrpll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds ddrpll_divpmcck which feeds DDR. It should not
+ * be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "ddrpll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds DDR. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ },
},
[PLL_ID_IMG] = {
- { .n = "imgpll_fracck",
- .p = "mainck",
- .l = &pll_layout_frac,
- .c = &pll_characteristics,
- .t = PLL_TYPE_FRAC,
- .f = CLK_SET_RATE_GATE, },
-
- { .n = "imgpll_divpmcck",
- .p = "imgpll_fracck",
- .l = &pll_layout_divpmc,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT, },
+ [PLL_COMPID_FRAC] = {
+ .n = "imgpll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "imgpll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ },
},
[PLL_ID_BAUD] = {
- { .n = "baudpll_fracck",
- .p = "mainck",
- .l = &pll_layout_frac,
- .c = &pll_characteristics,
- .t = PLL_TYPE_FRAC,
- .f = CLK_SET_RATE_GATE, },
-
- { .n = "baudpll_divpmcck",
- .p = "baudpll_fracck",
- .l = &pll_layout_divpmc,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT, },
+ [PLL_COMPID_FRAC] = {
+ .n = "baudpll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAINCK,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "baudpll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ },
},
[PLL_ID_AUDIO] = {
- { .n = "audiopll_fracck",
- .p = "main_xtal",
- .l = &pll_layout_frac,
- .c = &pll_characteristics,
- .t = PLL_TYPE_FRAC,
- .f = CLK_SET_RATE_GATE, },
-
- { .n = "audiopll_divpmcck",
- .p = "audiopll_fracck",
- .l = &pll_layout_divpmc,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT,
- .eid = PMC_AUDIOPMCPLL, },
-
- { .n = "audiopll_diviock",
- .p = "audiopll_fracck",
- .l = &pll_layout_divio,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT,
- .eid = PMC_AUDIOIOPLL, },
+ [PLL_COMPID_FRAC] = {
+ .n = "audiopll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAIN_XTAL,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "audiopll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOPMCPLL,
+ },
+
+ [PLL_COMPID_DIV1] = {
+ .n = "audiopll_diviock",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divio,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOIOPLL,
+ },
},
[PLL_ID_ETH] = {
- { .n = "ethpll_fracck",
- .p = "main_xtal",
- .l = &pll_layout_frac,
- .c = &pll_characteristics,
- .t = PLL_TYPE_FRAC,
- .f = CLK_SET_RATE_GATE, },
-
- { .n = "ethpll_divpmcck",
- .p = "ethpll_fracck",
- .l = &pll_layout_divpmc,
- .c = &pll_characteristics,
- .t = PLL_TYPE_DIV,
- .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT, },
+ [PLL_COMPID_FRAC] = {
+ .n = "ethpll_fracck",
+ .p = SAMA7G5_PLL_PARENT_MAIN_XTAL,
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE,
+ },
+
+ [PLL_COMPID_DIV0] = {
+ .n = "ethpll_divpmcck",
+ .p = SAMA7G5_PLL_PARENT_FRACCK,
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ },
},
};
+/* Used to create an array entry identifying a PLL by its components. */
+#define PLL_IDS_TO_ARR_ENTRY(_id, _comp) { PLL_ID_##_id, PLL_COMPID_##_comp}
+
/*
* Master clock (MCK[1..4]) description
* @n: clock name
- * @ep: extra parents names array
- * @ep_chg_chg_id: index in parents array that specifies the changeable
+ * @ep: extra parents names array (entry formed by PLL components
+ * identifiers (see enum pll_component_id))
+ * @hw: pointer to clk_hw
+ * @ep_chg_id: index in parents array that specifies the changeable
* parent
* @ep_count: extra parents count
* @ep_mux_table: mux table for extra parents
@@ -305,9 +360,13 @@ static const struct {
* @eid: export index in sama7g5->chws[] array
* @c: true if clock is critical and cannot be disabled
*/
-static const struct {
+static struct {
const char *n;
- const char *ep[4];
+ struct {
+ int pll_id;
+ int pll_compid;
+ } ep[4];
+ struct clk_hw *hw;
int ep_chg_id;
u8 ep_count;
u8 ep_mux_table[4];
@@ -315,9 +374,10 @@ static const struct {
u8 eid;
u8 c;
} sama7g5_mckx[] = {
+ { .n = "mck0", }, /* Dummy entry for MCK0 to store hw in probe. */
{ .n = "mck1",
.id = 1,
- .ep = { "syspll_divpmcck", },
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
.ep_mux_table = { 5, },
.ep_count = 1,
.ep_chg_id = INT_MIN,
@@ -326,7 +386,7 @@ static const struct {
{ .n = "mck2",
.id = 2,
- .ep = { "ddrpll_divpmcck", },
+ .ep = { PLL_IDS_TO_ARR_ENTRY(DDR, DIV0), },
.ep_mux_table = { 6, },
.ep_count = 1,
.ep_chg_id = INT_MIN,
@@ -334,14 +394,15 @@ static const struct {
{ .n = "mck3",
.id = 3,
- .ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(DDR, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(IMG, DIV0), },
.ep_mux_table = { 5, 6, 7, },
.ep_count = 3,
.ep_chg_id = 5, },
{ .n = "mck4",
.id = 4,
- .ep = { "syspll_divpmcck", },
+ .ep = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), },
.ep_mux_table = { 5, },
.ep_count = 1,
.ep_chg_id = INT_MIN,
@@ -351,120 +412,137 @@ static const struct {
/*
* System clock description
* @n: clock name
- * @p: clock parent name
* @id: clock id
*/
static const struct {
const char *n;
- const char *p;
u8 id;
} sama7g5_systemck[] = {
- { .n = "pck0", .p = "prog0", .id = 8, },
- { .n = "pck1", .p = "prog1", .id = 9, },
- { .n = "pck2", .p = "prog2", .id = 10, },
- { .n = "pck3", .p = "prog3", .id = 11, },
- { .n = "pck4", .p = "prog4", .id = 12, },
- { .n = "pck5", .p = "prog5", .id = 13, },
- { .n = "pck6", .p = "prog6", .id = 14, },
- { .n = "pck7", .p = "prog7", .id = 15, },
+ { .n = "pck0", .id = 8, },
+ { .n = "pck1", .id = 9, },
+ { .n = "pck2", .id = 10, },
+ { .n = "pck3", .id = 11, },
+ { .n = "pck4", .id = 12, },
+ { .n = "pck5", .id = 13, },
+ { .n = "pck6", .id = 14, },
+ { .n = "pck7", .id = 15, },
};
/* Mux table for programmable clocks. */
static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 5, 6, 7, 8, 9, 10, };
/*
+ * Peripheral clock parent hw identifier (used to index in sama7g5_mckx[])
+ * @PCK_PARENT_HW_MCK0: pck parent hw identifier is MCK0
+ * @PCK_PARENT_HW_MCK1: pck parent hw identifier is MCK1
+ * @PCK_PARENT_HW_MCK2: pck parent hw identifier is MCK2
+ * @PCK_PARENT_HW_MCK3: pck parent hw identifier is MCK3
+ * @PCK_PARENT_HW_MCK4: pck parent hw identifier is MCK4
+ * @PCK_PARENT_HW_MAX: max identifier
+ */
+enum sama7g5_pck_parent_hw_id {
+ PCK_PARENT_HW_MCK0,
+ PCK_PARENT_HW_MCK1,
+ PCK_PARENT_HW_MCK2,
+ PCK_PARENT_HW_MCK3,
+ PCK_PARENT_HW_MCK4,
+ PCK_PARENT_HW_MAX,
+};
+
+/*
* Peripheral clock description
* @n: clock name
- * @p: clock parent name
+ * @p: clock parent hw id
* @r: clock range values
* @id: clock id
* @chgp: index in parent array of the changeable parent
*/
-static const struct {
+static struct {
const char *n;
- const char *p;
+ enum sama7g5_pck_parent_hw_id p;
struct clk_range r;
u8 chgp;
u8 id;
} sama7g5_periphck[] = {
- { .n = "pioA_clk", .p = "mck0", .id = 11, },
- { .n = "securam_clk", .p = "mck0", .id = 18, },
- { .n = "sfr_clk", .p = "mck1", .id = 19, },
- { .n = "hsmc_clk", .p = "mck1", .id = 21, },
- { .n = "xdmac0_clk", .p = "mck1", .id = 22, },
- { .n = "xdmac1_clk", .p = "mck1", .id = 23, },
- { .n = "xdmac2_clk", .p = "mck1", .id = 24, },
- { .n = "acc_clk", .p = "mck1", .id = 25, },
- { .n = "aes_clk", .p = "mck1", .id = 27, },
- { .n = "tzaesbasc_clk", .p = "mck1", .id = 28, },
- { .n = "asrc_clk", .p = "mck1", .id = 30, .r = { .max = 200000000, }, },
- { .n = "cpkcc_clk", .p = "mck0", .id = 32, },
- { .n = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, .chgp = 1, },
- { .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, .chgp = 1, },
- { .n = "eic_clk", .p = "mck1", .id = 37, },
- { .n = "flex0_clk", .p = "mck1", .id = 38, },
- { .n = "flex1_clk", .p = "mck1", .id = 39, },
- { .n = "flex2_clk", .p = "mck1", .id = 40, },
- { .n = "flex3_clk", .p = "mck1", .id = 41, },
- { .n = "flex4_clk", .p = "mck1", .id = 42, },
- { .n = "flex5_clk", .p = "mck1", .id = 43, },
- { .n = "flex6_clk", .p = "mck1", .id = 44, },
- { .n = "flex7_clk", .p = "mck1", .id = 45, },
- { .n = "flex8_clk", .p = "mck1", .id = 46, },
- { .n = "flex9_clk", .p = "mck1", .id = 47, },
- { .n = "flex10_clk", .p = "mck1", .id = 48, },
- { .n = "flex11_clk", .p = "mck1", .id = 49, },
- { .n = "gmac0_clk", .p = "mck1", .id = 51, },
- { .n = "gmac1_clk", .p = "mck1", .id = 52, },
- { .n = "icm_clk", .p = "mck1", .id = 55, },
- { .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, .chgp = 1, },
- { .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, },
- { .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, },
- { .n = "matrix_clk", .p = "mck1", .id = 60, },
- { .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, },
- { .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, },
- { .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, },
- { .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, },
- { .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, },
- { .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, },
- { .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, },
- { .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, },
- { .n = "pit64b0_clk", .p = "mck1", .id = 70, },
- { .n = "pit64b1_clk", .p = "mck1", .id = 71, },
- { .n = "pit64b2_clk", .p = "mck1", .id = 72, },
- { .n = "pit64b3_clk", .p = "mck1", .id = 73, },
- { .n = "pit64b4_clk", .p = "mck1", .id = 74, },
- { .n = "pit64b5_clk", .p = "mck1", .id = 75, },
- { .n = "pwm_clk", .p = "mck1", .id = 77, },
- { .n = "qspi0_clk", .p = "mck1", .id = 78, },
- { .n = "qspi1_clk", .p = "mck1", .id = 79, },
- { .n = "sdmmc0_clk", .p = "mck1", .id = 80, },
- { .n = "sdmmc1_clk", .p = "mck1", .id = 81, },
- { .n = "sdmmc2_clk", .p = "mck1", .id = 82, },
- { .n = "sha_clk", .p = "mck1", .id = 83, },
- { .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, },
- { .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, },
- { .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, },
- { .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, },
- { .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, },
- { .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, },
- { .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, },
- { .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, },
- { .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, },
- { .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, },
- { .n = "tcpca_clk", .p = "mck1", .id = 94, },
- { .n = "tcpcb_clk", .p = "mck1", .id = 95, },
- { .n = "tdes_clk", .p = "mck1", .id = 96, },
- { .n = "trng_clk", .p = "mck1", .id = 97, },
- { .n = "udphsa_clk", .p = "mck1", .id = 104, },
- { .n = "udphsb_clk", .p = "mck1", .id = 105, },
- { .n = "uhphs_clk", .p = "mck1", .id = 106, },
+ { .n = "pioA_clk", .p = PCK_PARENT_HW_MCK0, .id = 11, },
+ { .n = "securam_clk", .p = PCK_PARENT_HW_MCK0, .id = 18, },
+ { .n = "sfr_clk", .p = PCK_PARENT_HW_MCK1, .id = 19, },
+ { .n = "hsmc_clk", .p = PCK_PARENT_HW_MCK1, .id = 21, },
+ { .n = "xdmac0_clk", .p = PCK_PARENT_HW_MCK1, .id = 22, },
+ { .n = "xdmac1_clk", .p = PCK_PARENT_HW_MCK1, .id = 23, },
+ { .n = "xdmac2_clk", .p = PCK_PARENT_HW_MCK1, .id = 24, },
+ { .n = "acc_clk", .p = PCK_PARENT_HW_MCK1, .id = 25, },
+ { .n = "aes_clk", .p = PCK_PARENT_HW_MCK1, .id = 27, },
+ { .n = "tzaesbasc_clk", .p = PCK_PARENT_HW_MCK1, .id = 28, },
+ { .n = "asrc_clk", .p = PCK_PARENT_HW_MCK1, .id = 30, .r = { .max = 200000000, }, },
+ { .n = "cpkcc_clk", .p = PCK_PARENT_HW_MCK0, .id = 32, },
+ { .n = "csi_clk", .p = PCK_PARENT_HW_MCK3, .id = 33, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "csi2dc_clk", .p = PCK_PARENT_HW_MCK3, .id = 34, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "eic_clk", .p = PCK_PARENT_HW_MCK1, .id = 37, },
+ { .n = "flex0_clk", .p = PCK_PARENT_HW_MCK1, .id = 38, },
+ { .n = "flex1_clk", .p = PCK_PARENT_HW_MCK1, .id = 39, },
+ { .n = "flex2_clk", .p = PCK_PARENT_HW_MCK1, .id = 40, },
+ { .n = "flex3_clk", .p = PCK_PARENT_HW_MCK1, .id = 41, },
+ { .n = "flex4_clk", .p = PCK_PARENT_HW_MCK1, .id = 42, },
+ { .n = "flex5_clk", .p = PCK_PARENT_HW_MCK1, .id = 43, },
+ { .n = "flex6_clk", .p = PCK_PARENT_HW_MCK1, .id = 44, },
+ { .n = "flex7_clk", .p = PCK_PARENT_HW_MCK1, .id = 45, },
+ { .n = "flex8_clk", .p = PCK_PARENT_HW_MCK1, .id = 46, },
+ { .n = "flex9_clk", .p = PCK_PARENT_HW_MCK1, .id = 47, },
+ { .n = "flex10_clk", .p = PCK_PARENT_HW_MCK1, .id = 48, },
+ { .n = "flex11_clk", .p = PCK_PARENT_HW_MCK1, .id = 49, },
+ { .n = "gmac0_clk", .p = PCK_PARENT_HW_MCK1, .id = 51, },
+ { .n = "gmac1_clk", .p = PCK_PARENT_HW_MCK1, .id = 52, },
+ { .n = "icm_clk", .p = PCK_PARENT_HW_MCK1, .id = 55, },
+ { .n = "isc_clk", .p = PCK_PARENT_HW_MCK3, .id = 56, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "i2smcc0_clk", .p = PCK_PARENT_HW_MCK1, .id = 57, .r = { .max = 200000000, }, },
+ { .n = "i2smcc1_clk", .p = PCK_PARENT_HW_MCK1, .id = 58, .r = { .max = 200000000, }, },
+ { .n = "matrix_clk", .p = PCK_PARENT_HW_MCK1, .id = 60, },
+ { .n = "mcan0_clk", .p = PCK_PARENT_HW_MCK1, .id = 61, .r = { .max = 200000000, }, },
+ { .n = "mcan1_clk", .p = PCK_PARENT_HW_MCK1, .id = 62, .r = { .max = 200000000, }, },
+ { .n = "mcan2_clk", .p = PCK_PARENT_HW_MCK1, .id = 63, .r = { .max = 200000000, }, },
+ { .n = "mcan3_clk", .p = PCK_PARENT_HW_MCK1, .id = 64, .r = { .max = 200000000, }, },
+ { .n = "mcan4_clk", .p = PCK_PARENT_HW_MCK1, .id = 65, .r = { .max = 200000000, }, },
+ { .n = "mcan5_clk", .p = PCK_PARENT_HW_MCK1, .id = 66, .r = { .max = 200000000, }, },
+ { .n = "pdmc0_clk", .p = PCK_PARENT_HW_MCK1, .id = 68, .r = { .max = 200000000, }, },
+ { .n = "pdmc1_clk", .p = PCK_PARENT_HW_MCK1, .id = 69, .r = { .max = 200000000, }, },
+ { .n = "pit64b0_clk", .p = PCK_PARENT_HW_MCK1, .id = 70, },
+ { .n = "pit64b1_clk", .p = PCK_PARENT_HW_MCK1, .id = 71, },
+ { .n = "pit64b2_clk", .p = PCK_PARENT_HW_MCK1, .id = 72, },
+ { .n = "pit64b3_clk", .p = PCK_PARENT_HW_MCK1, .id = 73, },
+ { .n = "pit64b4_clk", .p = PCK_PARENT_HW_MCK1, .id = 74, },
+ { .n = "pit64b5_clk", .p = PCK_PARENT_HW_MCK1, .id = 75, },
+ { .n = "pwm_clk", .p = PCK_PARENT_HW_MCK1, .id = 77, },
+ { .n = "qspi0_clk", .p = PCK_PARENT_HW_MCK1, .id = 78, },
+ { .n = "qspi1_clk", .p = PCK_PARENT_HW_MCK1, .id = 79, },
+ { .n = "sdmmc0_clk", .p = PCK_PARENT_HW_MCK1, .id = 80, },
+ { .n = "sdmmc1_clk", .p = PCK_PARENT_HW_MCK1, .id = 81, },
+ { .n = "sdmmc2_clk", .p = PCK_PARENT_HW_MCK1, .id = 82, },
+ { .n = "sha_clk", .p = PCK_PARENT_HW_MCK1, .id = 83, },
+ { .n = "spdifrx_clk", .p = PCK_PARENT_HW_MCK1, .id = 84, .r = { .max = 200000000, }, },
+ { .n = "spdiftx_clk", .p = PCK_PARENT_HW_MCK1, .id = 85, .r = { .max = 200000000, }, },
+ { .n = "ssc0_clk", .p = PCK_PARENT_HW_MCK1, .id = 86, .r = { .max = 200000000, }, },
+ { .n = "ssc1_clk", .p = PCK_PARENT_HW_MCK1, .id = 87, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch0_clk", .p = PCK_PARENT_HW_MCK1, .id = 88, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch1_clk", .p = PCK_PARENT_HW_MCK1, .id = 89, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch2_clk", .p = PCK_PARENT_HW_MCK1, .id = 90, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch0_clk", .p = PCK_PARENT_HW_MCK1, .id = 91, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch1_clk", .p = PCK_PARENT_HW_MCK1, .id = 92, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch2_clk", .p = PCK_PARENT_HW_MCK1, .id = 93, .r = { .max = 200000000, }, },
+ { .n = "tcpca_clk", .p = PCK_PARENT_HW_MCK1, .id = 94, },
+ { .n = "tcpcb_clk", .p = PCK_PARENT_HW_MCK1, .id = 95, },
+ { .n = "tdes_clk", .p = PCK_PARENT_HW_MCK1, .id = 96, },
+ { .n = "trng_clk", .p = PCK_PARENT_HW_MCK1, .id = 97, },
+ { .n = "udphsa_clk", .p = PCK_PARENT_HW_MCK1, .id = 104, },
+ { .n = "udphsb_clk", .p = PCK_PARENT_HW_MCK1, .id = 105, },
+ { .n = "uhphs_clk", .p = PCK_PARENT_HW_MCK1, .id = 106, },
};
/*
* Generic clock description
* @n: clock name
- * @pp: PLL parents
+ * @pp: PLL parents (entry formed by PLL components identifiers
+ * (see enum pll_component_id))
* @pp_mux_table: PLL parents mux table
* @r: clock output range
* @pp_chg_id: id in parent array of changeable PLL parent
@@ -473,7 +551,10 @@ static const struct {
*/
static const struct {
const char *n;
- const char *pp[8];
+ struct {
+ int pll_id;
+ int pll_compid;
+ } pp[8];
const char pp_mux_table[8];
struct clk_range r;
int pp_chg_id;
@@ -483,7 +564,8 @@ static const struct {
{ .n = "adc_gclk",
.id = 26,
.r = { .max = 100000000, },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 7, 9, },
.pp_count = 3,
.pp_chg_id = INT_MIN, },
@@ -491,7 +573,7 @@ static const struct {
{ .n = "asrc_gclk",
.id = 30,
.r = { .max = 200000000 },
- .pp = { "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 9, },
.pp_count = 1,
.pp_chg_id = 3, },
@@ -499,7 +581,7 @@ static const struct {
{ .n = "csi_gclk",
.id = 33,
.r = { .max = 27000000 },
- .pp = { "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(DDR, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0), },
.pp_mux_table = { 6, 7, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -507,7 +589,7 @@ static const struct {
{ .n = "flex0_gclk",
.id = 38,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -515,7 +597,7 @@ static const struct {
{ .n = "flex1_gclk",
.id = 39,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -523,7 +605,7 @@ static const struct {
{ .n = "flex2_gclk",
.id = 40,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -531,7 +613,7 @@ static const struct {
{ .n = "flex3_gclk",
.id = 41,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -539,7 +621,7 @@ static const struct {
{ .n = "flex4_gclk",
.id = 42,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -547,7 +629,7 @@ static const struct {
{ .n = "flex5_gclk",
.id = 43,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -555,7 +637,7 @@ static const struct {
{ .n = "flex6_gclk",
.id = 44,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -563,7 +645,7 @@ static const struct {
{ .n = "flex7_gclk",
.id = 45,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -571,7 +653,7 @@ static const struct {
{ .n = "flex8_gclk",
.id = 46,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -579,7 +661,7 @@ static const struct {
{ .n = "flex9_gclk",
.id = 47,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -587,7 +669,7 @@ static const struct {
{ .n = "flex10_gclk",
.id = 48,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -595,7 +677,7 @@ static const struct {
{ .n = "flex11_gclk",
.id = 49,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -603,7 +685,7 @@ static const struct {
{ .n = "gmac0_gclk",
.id = 51,
.r = { .max = 125000000 },
- .pp = { "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 10, },
.pp_count = 1,
.pp_chg_id = 3, },
@@ -611,7 +693,7 @@ static const struct {
{ .n = "gmac1_gclk",
.id = 52,
.r = { .max = 50000000 },
- .pp = { "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 10, },
.pp_count = 1,
.pp_chg_id = INT_MIN, },
@@ -619,7 +701,7 @@ static const struct {
{ .n = "gmac0_tsu_gclk",
.id = 53,
.r = { .max = 300000000 },
- .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 9, 10, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -627,7 +709,7 @@ static const struct {
{ .n = "gmac1_tsu_gclk",
.id = 54,
.r = { .max = 300000000 },
- .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 9, 10, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -635,7 +717,7 @@ static const struct {
{ .n = "i2smcc0_gclk",
.id = 57,
.r = { .max = 100000000 },
- .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -643,7 +725,7 @@ static const struct {
{ .n = "i2smcc1_gclk",
.id = 58,
.r = { .max = 100000000 },
- .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -651,7 +733,7 @@ static const struct {
{ .n = "mcan0_gclk",
.id = 61,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -659,7 +741,7 @@ static const struct {
{ .n = "mcan1_gclk",
.id = 62,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -667,7 +749,7 @@ static const struct {
{ .n = "mcan2_gclk",
.id = 63,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -675,7 +757,7 @@ static const struct {
{ .n = "mcan3_gclk",
.id = 64,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -683,7 +765,7 @@ static const struct {
{ .n = "mcan4_gclk",
.id = 65,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -691,7 +773,7 @@ static const struct {
{ .n = "mcan5_gclk",
.id = 66,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -699,7 +781,7 @@ static const struct {
{ .n = "pdmc0_gclk",
.id = 68,
.r = { .max = 50000000 },
- .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -707,7 +789,7 @@ static const struct {
{ .n = "pdmc1_gclk",
.id = 69,
.r = { .max = 50000000, },
- .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -715,8 +797,9 @@ static const struct {
{ .n = "pit64b0_gclk",
.id = 70,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -724,8 +807,9 @@ static const struct {
{ .n = "pit64b1_gclk",
.id = 71,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -733,8 +817,9 @@ static const struct {
{ .n = "pit64b2_gclk",
.id = 72,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -742,8 +827,9 @@ static const struct {
{ .n = "pit64b3_gclk",
.id = 73,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -751,8 +837,9 @@ static const struct {
{ .n = "pit64b4_gclk",
.id = 74,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -760,8 +847,9 @@ static const struct {
{ .n = "pit64b5_gclk",
.id = 75,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -769,7 +857,7 @@ static const struct {
{ .n = "qspi0_gclk",
.id = 78,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -777,7 +865,7 @@ static const struct {
{ .n = "qspi1_gclk",
.id = 79,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
@@ -785,7 +873,7 @@ static const struct {
{ .n = "sdmmc0_gclk",
.id = 80,
.r = { .max = 208000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -793,7 +881,7 @@ static const struct {
{ .n = "sdmmc1_gclk",
.id = 81,
.r = { .max = 208000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -801,7 +889,7 @@ static const struct {
{ .n = "sdmmc2_gclk",
.id = 82,
.r = { .max = 208000000 },
- .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -809,7 +897,7 @@ static const struct {
{ .n = "spdifrx_gclk",
.id = 84,
.r = { .max = 150000000 },
- .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -817,7 +905,7 @@ static const struct {
{ .n = "spdiftx_gclk",
.id = 85,
.r = { .max = 25000000 },
- .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 4, },
@@ -825,8 +913,9 @@ static const struct {
{ .n = "tcb0_ch0_gclk",
.id = 88,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -834,8 +923,9 @@ static const struct {
{ .n = "tcb1_ch0_gclk",
.id = 91,
.r = { .max = 200000000 },
- .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
- "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(IMG, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(BAUD, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0),
+ PLL_IDS_TO_ARR_ENTRY(ETH, DIV0), },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
@@ -884,34 +974,25 @@ static const struct clk_pcr_layout sama7g5_pcr_layout = {
static void __init sama7g5_pmc_setup(struct device_node *np)
{
- const char *td_slck_name, *md_slck_name, *mainxtal_name;
+ const char *main_xtal_name = "main_xtal";
struct pmc_data *sama7g5_pmc;
- const char *parent_names[10];
void **alloc_mem = NULL;
int alloc_mem_size = 0;
struct regmap *regmap;
- struct clk_hw *hw;
+ struct clk_hw *hw, *main_rc_hw, *main_osc_hw, *main_xtal_hw;
+ struct clk_hw *td_slck_hw, *md_slck_hw;
+ static struct clk_parent_data parent_data;
+ struct clk_hw *parent_hws[10];
bool bypass;
int i, j;
- i = of_property_match_string(np, "clock-names", "td_slck");
- if (i < 0)
- return;
-
- td_slck_name = of_clk_get_parent_name(np, i);
-
- i = of_property_match_string(np, "clock-names", "md_slck");
- if (i < 0)
- return;
-
- md_slck_name = of_clk_get_parent_name(np, i);
+ td_slck_hw = __clk_get_hw(of_clk_get_by_name(np, "td_slck"));
+ md_slck_hw = __clk_get_hw(of_clk_get_by_name(np, "md_slck"));
+ main_xtal_hw = __clk_get_hw(of_clk_get_by_name(np, main_xtal_name));
- i = of_property_match_string(np, "clock-names", "main_xtal");
- if (i < 0)
+ if (!td_slck_hw || !md_slck_hw || !main_xtal_hw)
return;
- mainxtal_name = of_clk_get_parent_name(np, i);
-
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@@ -929,28 +1010,30 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
if (!alloc_mem)
goto err_free;
- hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
- 50000000);
- if (IS_ERR(hw))
+ main_rc_hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(main_rc_hw))
goto err_free;
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
- bypass);
- if (IS_ERR(hw))
+ parent_data.name = main_xtal_name;
+ parent_data.fw_name = main_xtal_name;
+ main_osc_hw = at91_clk_register_main_osc(regmap, "main_osc", NULL,
+ &parent_data, bypass);
+ if (IS_ERR(main_osc_hw))
goto err_free;
- parent_names[0] = "main_rc_osc";
- parent_names[1] = "main_osc";
- hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ parent_hws[0] = main_rc_hw;
+ parent_hws[1] = main_osc_hw;
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", NULL, parent_hws, 2);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->chws[PMC_MAIN] = hw;
for (i = 0; i < PLL_ID_MAX; i++) {
- for (j = 0; j < 3; j++) {
+ for (j = 0; j < PLL_COMPID_MAX; j++) {
struct clk_hw *parent_hw;
if (!sama7g5_plls[i][j].n)
@@ -958,15 +1041,22 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
switch (sama7g5_plls[i][j].t) {
case PLL_TYPE_FRAC:
- if (!strcmp(sama7g5_plls[i][j].p, "mainck"))
+ switch (sama7g5_plls[i][j].p) {
+ case SAMA7G5_PLL_PARENT_MAINCK:
parent_hw = sama7g5_pmc->chws[PMC_MAIN];
- else
- parent_hw = __clk_get_hw(of_clk_get_by_name(np,
- sama7g5_plls[i][j].p));
+ break;
+ case SAMA7G5_PLL_PARENT_MAIN_XTAL:
+ parent_hw = main_xtal_hw;
+ break;
+ default:
+ /* Should not happen. */
+ parent_hw = NULL;
+ break;
+ }
hw = sam9x60_clk_register_frac_pll(regmap,
&pmc_pll_lock, sama7g5_plls[i][j].n,
- sama7g5_plls[i][j].p, parent_hw, i,
+ NULL, parent_hw, i,
sama7g5_plls[i][j].c,
sama7g5_plls[i][j].l,
sama7g5_plls[i][j].f);
@@ -975,7 +1065,7 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
case PLL_TYPE_DIV:
hw = sam9x60_clk_register_div_pll(regmap,
&pmc_pll_lock, sama7g5_plls[i][j].n,
- sama7g5_plls[i][j].p, i,
+ NULL, sama7g5_plls[i][0].hw, i,
sama7g5_plls[i][j].c,
sama7g5_plls[i][j].l,
sama7g5_plls[i][j].f,
@@ -989,25 +1079,27 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
+ sama7g5_plls[i][j].hw = hw;
if (sama7g5_plls[i][j].eid)
sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw;
}
}
- parent_names[0] = "cpupll_divpmcck";
- hw = at91_clk_register_master_div(regmap, "mck0", "cpupll_divpmcck",
+ hw = at91_clk_register_master_div(regmap, "mck0", NULL,
+ sama7g5_plls[PLL_ID_CPU][1].hw,
&mck0_layout, &mck0_characteristics,
&pmc_mck0_lock, CLK_GET_RATE_NOCACHE, 5);
if (IS_ERR(hw))
goto err_free;
- sama7g5_pmc->chws[PMC_MCK] = hw;
+ sama7g5_mckx[PCK_PARENT_HW_MCK0].hw = sama7g5_pmc->chws[PMC_MCK] = hw;
- parent_names[0] = md_slck_name;
- parent_names[1] = td_slck_name;
- parent_names[2] = "mainck";
- for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
+ parent_hws[0] = md_slck_hw;
+ parent_hws[1] = td_slck_hw;
+ parent_hws[2] = sama7g5_pmc->chws[PMC_MAIN];
+ for (i = PCK_PARENT_HW_MCK1; i < ARRAY_SIZE(sama7g5_mckx); i++) {
u8 num_parents = 3 + sama7g5_mckx[i].ep_count;
+ struct clk_hw *tmp_parent_hws[8];
u32 *mux_table;
mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
@@ -1015,14 +1107,20 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
if (!mux_table)
goto err_free;
- SAMA7G5_INIT_TABLE(mux_table, 3);
- SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_mckx[i].ep_mux_table,
- sama7g5_mckx[i].ep_count);
- SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_mckx[i].ep,
- sama7g5_mckx[i].ep_count);
+ PMC_INIT_TABLE(mux_table, 3);
+ PMC_FILL_TABLE(&mux_table[3], sama7g5_mckx[i].ep_mux_table,
+ sama7g5_mckx[i].ep_count);
+ for (j = 0; j < sama7g5_mckx[i].ep_count; j++) {
+ u8 pll_id = sama7g5_mckx[i].ep[j].pll_id;
+ u8 pll_compid = sama7g5_mckx[i].ep[j].pll_compid;
+
+ tmp_parent_hws[j] = sama7g5_plls[pll_id][pll_compid].hw;
+ }
+ PMC_FILL_TABLE(&parent_hws[3], tmp_parent_hws,
+ sama7g5_mckx[i].ep_count);
hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n,
- num_parents, parent_names, mux_table,
+ num_parents, NULL, parent_hws, mux_table,
&pmc_mckX_lock, sama7g5_mckx[i].id,
sama7g5_mckx[i].c,
sama7g5_mckx[i].ep_chg_id);
@@ -1031,31 +1129,32 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
alloc_mem[alloc_mem_size++] = mux_table;
+ sama7g5_mckx[i].hw = hw;
if (sama7g5_mckx[i].eid)
sama7g5_pmc->chws[sama7g5_mckx[i].eid] = hw;
}
- hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal");
+ hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", NULL, main_xtal_hw);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->chws[PMC_UTMI] = hw;
- parent_names[0] = md_slck_name;
- parent_names[1] = td_slck_name;
- parent_names[2] = "mainck";
- parent_names[3] = "syspll_divpmcck";
- parent_names[4] = "ddrpll_divpmcck";
- parent_names[5] = "imgpll_divpmcck";
- parent_names[6] = "baudpll_divpmcck";
- parent_names[7] = "audiopll_divpmcck";
- parent_names[8] = "ethpll_divpmcck";
+ parent_hws[0] = md_slck_hw;
+ parent_hws[1] = td_slck_hw;
+ parent_hws[2] = sama7g5_pmc->chws[PMC_MAIN];
+ parent_hws[3] = sama7g5_plls[PLL_ID_SYS][PLL_COMPID_DIV0].hw;
+ parent_hws[4] = sama7g5_plls[PLL_ID_DDR][PLL_COMPID_DIV0].hw;
+ parent_hws[5] = sama7g5_plls[PLL_ID_IMG][PLL_COMPID_DIV0].hw;
+ parent_hws[6] = sama7g5_plls[PLL_ID_BAUD][PLL_COMPID_DIV0].hw;
+ parent_hws[7] = sama7g5_plls[PLL_ID_AUDIO][PLL_COMPID_DIV0].hw;
+ parent_hws[8] = sama7g5_plls[PLL_ID_ETH][PLL_COMPID_DIV0].hw;
for (i = 0; i < 8; i++) {
char name[6];
snprintf(name, sizeof(name), "prog%d", i);
- hw = at91_clk_register_programmable(regmap, name, parent_names,
+ hw = at91_clk_register_programmable(regmap, name, NULL, parent_hws,
9, i,
&programmable_layout,
sama7g5_prog_mux_table);
@@ -1067,8 +1166,8 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n,
- sama7g5_systemck[i].p,
- sama7g5_systemck[i].id);
+ NULL, sama7g5_pmc->pchws[i],
+ sama7g5_systemck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -1079,22 +1178,24 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama7g5_pcr_layout,
sama7g5_periphck[i].n,
- sama7g5_periphck[i].p,
+ NULL,
+ sama7g5_mckx[sama7g5_periphck[i].p].hw,
sama7g5_periphck[i].id,
&sama7g5_periphck[i].r,
sama7g5_periphck[i].chgp ? 0 :
- INT_MIN);
+ INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw;
}
- parent_names[0] = md_slck_name;
- parent_names[1] = td_slck_name;
- parent_names[2] = "mainck";
+ parent_hws[0] = md_slck_hw;
+ parent_hws[1] = td_slck_hw;
+ parent_hws[2] = sama7g5_pmc->chws[PMC_MAIN];
for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
u8 num_parents = 3 + sama7g5_gck[i].pp_count;
+ struct clk_hw *tmp_parent_hws[8];
u32 *mux_table;
mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
@@ -1102,16 +1203,22 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
if (!mux_table)
goto err_free;
- SAMA7G5_INIT_TABLE(mux_table, 3);
- SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_gck[i].pp_mux_table,
- sama7g5_gck[i].pp_count);
- SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_gck[i].pp,
- sama7g5_gck[i].pp_count);
+ PMC_INIT_TABLE(mux_table, 3);
+ PMC_FILL_TABLE(&mux_table[3], sama7g5_gck[i].pp_mux_table,
+ sama7g5_gck[i].pp_count);
+ for (j = 0; j < sama7g5_gck[i].pp_count; j++) {
+ u8 pll_id = sama7g5_gck[i].pp[j].pll_id;
+ u8 pll_compid = sama7g5_gck[i].pp[j].pll_compid;
+
+ tmp_parent_hws[j] = sama7g5_plls[pll_id][pll_compid].hw;
+ }
+ PMC_FILL_TABLE(&parent_hws[3], tmp_parent_hws,
+ sama7g5_gck[i].pp_count);
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama7g5_pcr_layout,
- sama7g5_gck[i].n,
- parent_names, mux_table,
+ sama7g5_gck[i].n, NULL,
+ parent_hws, mux_table,
num_parents,
sama7g5_gck[i].id,
&sama7g5_gck[i].r,
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index fdc9b669f8a7..021d1b412af4 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -12,6 +12,8 @@
#include <linux/of_address.h>
#include <linux/io.h>
+#include <dt-bindings/clock/at91.h>
+
#define SLOW_CLOCK_FREQ 32768
#define SLOWCK_SW_CYCLES 5
#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
@@ -117,17 +119,17 @@ static const struct clk_ops slow_osc_ops = {
static struct clk_hw * __init
at91_clk_register_slow_osc(void __iomem *sckcr,
const char *name,
- const char *parent_name,
+ const struct clk_parent_data *parent_data,
unsigned long startup,
bool bypass,
const struct clk_slow_bits *bits)
{
struct clk_slow_osc *osc;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
- if (!sckcr || !name || !parent_name)
+ if (!sckcr || !name || !parent_data)
return ERR_PTR(-EINVAL);
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
@@ -136,7 +138,7 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
init.name = name;
init.ops = &slow_osc_ops;
- init.parent_names = &parent_name;
+ init.parent_data = parent_data;
init.num_parents = 1;
init.flags = CLK_IGNORE_UNUSED;
@@ -310,6 +312,7 @@ static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
}
static const struct clk_ops sam9x5_slow_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_sam9x5_slow_set_parent,
.get_parent = clk_sam9x5_slow_get_parent,
};
@@ -317,16 +320,16 @@ static const struct clk_ops sam9x5_slow_ops = {
static struct clk_hw * __init
at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const char *name,
- const char **parent_names,
+ const struct clk_hw **parent_hws,
int num_parents,
const struct clk_slow_bits *bits)
{
struct clk_sam9x5_slow *slowck;
struct clk_hw *hw;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
- if (!sckcr || !name || !parent_names || !num_parents)
+ if (!sckcr || !name || !parent_hws || !num_parents)
return ERR_PTR(-EINVAL);
slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
@@ -335,7 +338,7 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
init.name = name;
init.ops = &sam9x5_slow_ops;
- init.parent_names = parent_names;
+ init.parent_hws = parent_hws;
init.num_parents = num_parents;
init.flags = 0;
@@ -366,18 +369,21 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
unsigned int rc_osc_startup_us,
const struct clk_slow_bits *bits)
{
- const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
void __iomem *regbase = of_iomap(np, 0);
struct device_node *child = NULL;
const char *xtal_name;
struct clk_hw *slow_rc, *slow_osc, *slowck;
+ static struct clk_parent_data parent_data = {
+ .name = "slow_xtal",
+ };
+ const struct clk_hw *parent_hws[2];
bool bypass;
int ret;
if (!regbase)
return;
- slow_rc = at91_clk_register_slow_rc_osc(regbase, parent_names[0],
+ slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc",
32768, 50000000,
rc_osc_startup_us, bits);
if (IS_ERR(slow_rc))
@@ -401,12 +407,16 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
if (!xtal_name)
goto unregister_slow_rc;
- slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
- xtal_name, 1200000, bypass, bits);
+ parent_data.fw_name = xtal_name;
+
+ slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
+ &parent_data, 1200000, bypass, bits);
if (IS_ERR(slow_osc))
goto unregister_slow_rc;
- slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names,
+ parent_hws[0] = slow_rc;
+ parent_hws[1] = slow_osc;
+ slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws,
2, bits);
if (IS_ERR(slowck))
goto unregister_slow_osc;
@@ -462,16 +472,19 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
struct clk_hw_onecell_data *clk_data;
- struct clk_hw *slow_rc, *slow_osc;
+ struct clk_hw *slow_rc, *slow_osc, *hw;
const char *xtal_name;
- const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
+ const struct clk_hw *parent_hws[2];
+ static struct clk_parent_data parent_data = {
+ .name = "slow_xtal",
+ };
bool bypass;
int ret;
if (!regbase)
return;
- slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
+ slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc",
NULL, 0, 32768,
93750000);
if (IS_ERR(slow_rc))
@@ -481,9 +494,10 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (!xtal_name)
goto unregister_slow_rc;
+ parent_data.fw_name = xtal_name;
bypass = of_property_read_bool(np, "atmel,osc-bypass");
- slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
- xtal_name, 5000000, bypass,
+ slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
+ &parent_data, 5000000, bypass,
&at91sam9x60_bits);
if (IS_ERR(slow_osc))
goto unregister_slow_rc;
@@ -494,17 +508,19 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
/* MD_SLCK and TD_SLCK. */
clk_data->num = 2;
- clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck",
- parent_names[0],
- 0, 32768);
- if (IS_ERR(clk_data->hws[0]))
+ hw = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck", slow_rc,
+ 0, 32768);
+ if (IS_ERR(hw))
goto clk_data_free;
+ clk_data->hws[SCKC_MD_SLCK] = hw;
- clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
- parent_names, 2,
- &at91sam9x60_bits);
- if (IS_ERR(clk_data->hws[1]))
+ parent_hws[0] = slow_rc;
+ parent_hws[1] = slow_osc;
+ hw = at91_clk_register_sam9x5_slow(regbase, "td_slck", parent_hws,
+ 2, &at91sam9x60_bits);
+ if (IS_ERR(hw))
goto unregister_md_slck;
+ clk_data->hws[SCKC_TD_SLCK] = hw;
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
if (WARN_ON(ret))
@@ -513,9 +529,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
return;
unregister_td_slck:
- at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
+ at91_clk_unregister_sam9x5_slow(clk_data->hws[SCKC_TD_SLCK]);
unregister_md_slck:
- clk_hw_unregister(clk_data->hws[0]);
+ clk_hw_unregister(clk_data->hws[SCKC_MD_SLCK]);
clk_data_free:
kfree(clk_data);
unregister_slow_osc:
@@ -572,30 +588,36 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
void __iomem *regbase = of_iomap(np, 0);
struct clk_hw *slow_rc, *slowck;
struct clk_sama5d4_slow_osc *osc;
- struct clk_init_data init;
+ struct clk_init_data init = {};
const char *xtal_name;
- const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
+ const struct clk_hw *parent_hws[2];
+ static struct clk_parent_data parent_data = {
+ .name = "slow_xtal",
+ };
int ret;
if (!regbase)
return;
slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
- parent_names[0],
+ "slow_rc_osc",
NULL, 0, 32768,
250000000);
if (IS_ERR(slow_rc))
return;
xtal_name = of_clk_get_parent_name(np, 0);
+ if (!xtal_name)
+ goto unregister_slow_rc;
+ parent_data.fw_name = xtal_name;
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
if (!osc)
goto unregister_slow_rc;
- init.name = parent_names[1];
+ init.name = "slow_osc";
init.ops = &sama5d4_slow_osc_ops;
- init.parent_names = &xtal_name;
+ init.parent_data = &parent_data;
init.num_parents = 1;
init.flags = CLK_IGNORE_UNUSED;
@@ -608,8 +630,10 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
if (ret)
goto free_slow_osc_data;
+ parent_hws[0] = slow_rc;
+ parent_hws[1] = &osc->hw;
slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
- parent_names, 2,
+ parent_hws, 2,
&at91sama5d4_bits);
if (IS_ERR(slowck))
goto unregister_slow_osc;
diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c
index e1fda6ad5cd5..6f3e1151b354 100644
--- a/drivers/clk/axs10x/i2s_pll_clock.c
+++ b/drivers/clk/axs10x/i2s_pll_clock.c
@@ -108,21 +108,21 @@ static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw,
return ((parent_rate / idiv) * fbdiv) / odiv;
}
-static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int i2s_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct i2s_pll_clk *clk = to_i2s_pll_clk(hw);
- const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate);
+ const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(req->best_parent_rate);
int i;
if (!pll_cfg) {
- dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
+ dev_err(clk->dev, "invalid parent rate=%ld\n", req->best_parent_rate);
return -EINVAL;
}
for (i = 0; pll_cfg[i].rate != 0; i++)
- if (pll_cfg[i].rate == rate)
- return rate;
+ if (pll_cfg[i].rate == req->rate)
+ return 0;
return -EINVAL;
}
@@ -156,7 +156,7 @@ static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops i2s_pll_ops = {
.recalc_rate = i2s_pll_recalc_rate,
- .round_rate = i2s_pll_round_rate,
+ .determine_rate = i2s_pll_determine_rate,
.set_rate = i2s_pll_set_rate,
};
@@ -198,10 +198,9 @@ static int i2s_pll_clk_probe(struct platform_device *pdev)
return of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
-static int i2s_pll_clk_remove(struct platform_device *pdev)
+static void i2s_pll_clk_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
- return 0;
}
static const struct of_device_id i2s_pll_clk_id[] = {
diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c
index 90fb0e6ff573..c7ca473ee76c 100644
--- a/drivers/clk/axs10x/pll_clock.c
+++ b/drivers/clk/axs10x/pll_clock.c
@@ -12,10 +12,9 @@
#include <linux/err.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
-#include <linux/of.h>
/* PLL registers addresses */
#define PLL_REG_IDIV 0x0
@@ -150,8 +149,8 @@ static unsigned long axs10x_pll_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long axs10x_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int axs10x_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
long best_rate;
@@ -164,11 +163,13 @@ static long axs10x_pll_round_rate(struct clk_hw *hw, unsigned long rate,
best_rate = pll_cfg[0].rate;
for (i = 1; pll_cfg[i].rate != 0; i++) {
- if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
+ if (abs(req->rate - pll_cfg[i].rate) < abs(req->rate - best_rate))
best_rate = pll_cfg[i].rate;
}
- return best_rate;
+ req->rate = best_rate;
+
+ return 0;
}
static int axs10x_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -209,7 +210,7 @@ static int axs10x_pll_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops axs10x_pll_ops = {
.recalc_rate = axs10x_pll_recalc_rate,
- .round_rate = axs10x_pll_round_rate,
+ .determine_rate = axs10x_pll_determine_rate,
.set_rate = axs10x_pll_set_rate,
};
@@ -253,14 +254,8 @@ static int axs10x_pll_clk_probe(struct platform_device *pdev)
return ret;
}
- return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
- &pll_clk->hw);
-}
-
-static int axs10x_pll_clk_remove(struct platform_device *pdev)
-{
- of_clk_del_provider(pdev->dev.of_node);
- return 0;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &pll_clk->hw);
}
static void __init of_axs10x_pll_clk_setup(struct device_node *node)
@@ -332,7 +327,6 @@ static struct platform_driver axs10x_pll_clk_driver = {
.of_match_table = axs10x_pll_clk_id,
},
.probe = axs10x_pll_clk_probe,
- .remove = axs10x_pll_clk_remove,
};
builtin_platform_driver(axs10x_pll_clk_driver);
diff --git a/drivers/clk/baikal-t1/Kconfig b/drivers/clk/baikal-t1/Kconfig
index 03102f1094bc..f0b186830324 100644
--- a/drivers/clk/baikal-t1/Kconfig
+++ b/drivers/clk/baikal-t1/Kconfig
@@ -29,7 +29,6 @@ config CLK_BT1_CCU_PLL
config CLK_BT1_CCU_DIV
bool "Baikal-T1 CCU Dividers support"
- select RESET_CONTROLLER
select MFD_SYSCON
default MIPS_BAIKAL_T1
help
@@ -39,4 +38,15 @@ config CLK_BT1_CCU_DIV
either gateable or ungateable. Some of the CCU dividers can be as well
used to reset the domains they're supplying clock to.
+config CLK_BT1_CCU_RST
+ bool "Baikal-T1 CCU Resets support"
+ select RESET_CONTROLLER
+ select MFD_SYSCON
+ default MIPS_BAIKAL_T1
+ help
+ Enable this to support the CCU reset blocks responsible for the
+ AXI-bus and some subsystems reset. These are mainly the
+ self-deasserted reset controls but there are several lines which
+ can be directly asserted/de-asserted (PCIe and DDR sub-domains).
+
endif
diff --git a/drivers/clk/baikal-t1/Makefile b/drivers/clk/baikal-t1/Makefile
index b3b9590b95ed..9c3637de9407 100644
--- a/drivers/clk/baikal-t1/Makefile
+++ b/drivers/clk/baikal-t1/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CLK_BT1_CCU_PLL) += ccu-pll.o clk-ccu-pll.o
obj-$(CONFIG_CLK_BT1_CCU_DIV) += ccu-div.o clk-ccu-div.o
+obj-$(CONFIG_CLK_BT1_CCU_RST) += ccu-rst.o
diff --git a/drivers/clk/baikal-t1/ccu-div.c b/drivers/clk/baikal-t1/ccu-div.c
index 4062092d67f9..849d1f55765f 100644
--- a/drivers/clk/baikal-t1/ccu-div.c
+++ b/drivers/clk/baikal-t1/ccu-div.c
@@ -34,9 +34,9 @@
#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
#define CCU_DIV_CTL_LOCK_SHIFTED BIT(27)
+#define CCU_DIV_CTL_GATE_REF_BUF BIT(28)
#define CCU_DIV_CTL_LOCK_NORMAL BIT(31)
-#define CCU_DIV_RST_DELAY_US 1
#define CCU_DIV_LOCK_CHECK_RETRIES 50
#define CCU_DIV_CLKDIV_MIN 0
@@ -170,6 +170,40 @@ static int ccu_div_gate_is_enabled(struct clk_hw *hw)
return !!(val & CCU_DIV_CTL_EN);
}
+static int ccu_div_buf_enable(struct clk_hw *hw)
+{
+ struct ccu_div *div = to_ccu_div(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&div->lock, flags);
+ regmap_update_bits(div->sys_regs, div->reg_ctl,
+ CCU_DIV_CTL_GATE_REF_BUF, 0);
+ spin_unlock_irqrestore(&div->lock, flags);
+
+ return 0;
+}
+
+static void ccu_div_buf_disable(struct clk_hw *hw)
+{
+ struct ccu_div *div = to_ccu_div(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&div->lock, flags);
+ regmap_update_bits(div->sys_regs, div->reg_ctl,
+ CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);
+ spin_unlock_irqrestore(&div->lock, flags);
+}
+
+static int ccu_div_buf_is_enabled(struct clk_hw *hw)
+{
+ struct ccu_div *div = to_ccu_div(hw);
+ u32 val = 0;
+
+ regmap_read(div->sys_regs, div->reg_ctl, &val);
+
+ return !(val & CCU_DIV_CTL_GATE_REF_BUF);
+}
+
static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@@ -194,15 +228,18 @@ static inline unsigned long ccu_div_var_calc_divider(unsigned long rate,
CCU_DIV_CLKDIV_MAX(mask));
}
-static long ccu_div_var_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int ccu_div_var_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct ccu_div *div = to_ccu_div(hw);
unsigned long divider;
- divider = ccu_div_var_calc_divider(rate, *parent_rate, div->mask);
+ divider = ccu_div_var_calc_divider(req->rate, req->best_parent_rate,
+ div->mask);
- return ccu_div_calc_freq(*parent_rate, divider);
+ req->rate = ccu_div_calc_freq(req->best_parent_rate, divider);
+
+ return 0;
}
/*
@@ -274,35 +311,19 @@ static unsigned long ccu_div_fixed_recalc_rate(struct clk_hw *hw,
return ccu_div_calc_freq(parent_rate, div->divider);
}
-static long ccu_div_fixed_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int ccu_div_fixed_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct ccu_div *div = to_ccu_div(hw);
- return ccu_div_calc_freq(*parent_rate, div->divider);
-}
+ req->rate = ccu_div_calc_freq(req->best_parent_rate, div->divider);
-static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
return 0;
}
-int ccu_div_reset_domain(struct ccu_div *div)
+static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- unsigned long flags;
-
- if (!div || !(div->features & CCU_DIV_RESET_DOMAIN))
- return -EINVAL;
-
- spin_lock_irqsave(&div->lock, flags);
- regmap_update_bits(div->sys_regs, div->reg_ctl,
- CCU_DIV_CTL_RST, CCU_DIV_CTL_RST);
- spin_unlock_irqrestore(&div->lock, flags);
-
- /* The next delay must be enough to cover all the resets. */
- udelay(CCU_DIV_RST_DELAY_US);
-
return 0;
}
@@ -323,6 +344,7 @@ static const struct ccu_div_dbgfs_bit ccu_div_bits[] = {
CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
+ CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF),
CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
};
@@ -441,6 +463,9 @@ static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)
continue;
}
+ if (!strcmp("div_buf", name))
+ continue;
+
bits[didx] = ccu_div_bits[bidx];
bits[didx].div = div;
@@ -477,6 +502,21 @@ static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)
&ccu_div_dbgfs_fixed_clkdiv_fops);
}
+static void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry)
+{
+ struct ccu_div *div = to_ccu_div(hw);
+ struct ccu_div_dbgfs_bit *bit;
+
+ bit = kmalloc(sizeof(*bit), GFP_KERNEL);
+ if (!bit)
+ return;
+
+ *bit = ccu_div_bits[3];
+ bit->div = div;
+ debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
+ &ccu_div_dbgfs_bit_fops);
+}
+
static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
{
struct ccu_div *div = to_ccu_div(hw);
@@ -489,6 +529,7 @@ static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
#define ccu_div_var_debug_init NULL
#define ccu_div_gate_debug_init NULL
+#define ccu_div_buf_debug_init NULL
#define ccu_div_fixed_debug_init NULL
#endif /* !CONFIG_DEBUG_FS */
@@ -498,14 +539,14 @@ static const struct clk_ops ccu_div_var_gate_to_set_ops = {
.disable = ccu_div_gate_disable,
.is_enabled = ccu_div_gate_is_enabled,
.recalc_rate = ccu_div_var_recalc_rate,
- .round_rate = ccu_div_var_round_rate,
+ .determine_rate = ccu_div_var_determine_rate,
.set_rate = ccu_div_var_set_rate_fast,
.debug_init = ccu_div_var_debug_init
};
static const struct clk_ops ccu_div_var_nogate_ops = {
.recalc_rate = ccu_div_var_recalc_rate,
- .round_rate = ccu_div_var_round_rate,
+ .determine_rate = ccu_div_var_determine_rate,
.set_rate = ccu_div_var_set_rate_slow,
.debug_init = ccu_div_var_debug_init
};
@@ -515,14 +556,21 @@ static const struct clk_ops ccu_div_gate_ops = {
.disable = ccu_div_gate_disable,
.is_enabled = ccu_div_gate_is_enabled,
.recalc_rate = ccu_div_fixed_recalc_rate,
- .round_rate = ccu_div_fixed_round_rate,
+ .determine_rate = ccu_div_fixed_determine_rate,
.set_rate = ccu_div_fixed_set_rate,
.debug_init = ccu_div_gate_debug_init
};
+static const struct clk_ops ccu_div_buf_ops = {
+ .enable = ccu_div_buf_enable,
+ .disable = ccu_div_buf_disable,
+ .is_enabled = ccu_div_buf_is_enabled,
+ .debug_init = ccu_div_buf_debug_init
+};
+
static const struct clk_ops ccu_div_fixed_ops = {
.recalc_rate = ccu_div_fixed_recalc_rate,
- .round_rate = ccu_div_fixed_round_rate,
+ .determine_rate = ccu_div_fixed_determine_rate,
.set_rate = ccu_div_fixed_set_rate,
.debug_init = ccu_div_fixed_debug_init
};
@@ -566,6 +614,8 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
} else if (div_init->type == CCU_DIV_GATE) {
hw_init.ops = &ccu_div_gate_ops;
div->divider = div_init->divider;
+ } else if (div_init->type == CCU_DIV_BUF) {
+ hw_init.ops = &ccu_div_buf_ops;
} else if (div_init->type == CCU_DIV_FIXED) {
hw_init.ops = &ccu_div_fixed_ops;
div->divider = div_init->divider;
@@ -579,6 +629,7 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
goto err_free_div;
}
parent_data.fw_name = div_init->parent_name;
+ parent_data.name = div_init->parent_name;
hw_init.parent_data = &parent_data;
hw_init.num_parents = 1;
diff --git a/drivers/clk/baikal-t1/ccu-div.h b/drivers/clk/baikal-t1/ccu-div.h
index 795665caefbd..76d8ee44d415 100644
--- a/drivers/clk/baikal-t1/ccu-div.h
+++ b/drivers/clk/baikal-t1/ccu-div.h
@@ -14,14 +14,25 @@
#include <linux/of.h>
/*
+ * CCU Divider private clock IDs
+ * @CCU_SYS_SATA_CLK: CCU SATA internal clock
+ * @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock
+ */
+#define CCU_SYS_SATA_CLK -1
+#define CCU_SYS_XGMAC_CLK -2
+
+/*
* CCU Divider private flags
+ * @CCU_DIV_BASIC: Basic divider clock required by the kernel as early as
+ * possible.
* @CCU_DIV_SKIP_ONE: Due to some reason divider can't be set to 1.
* It can be 0 though, which is functionally the same.
* @CCU_DIV_SKIP_ONE_TO_THREE: For some reason divider can't be within [1,3].
* It can be either 0 or greater than 3.
* @CCU_DIV_LOCK_SHIFTED: Find lock-bit at non-standard position.
- * @CCU_DIV_RESET_DOMAIN: Provide reset clock domain method.
+ * @CCU_DIV_RESET_DOMAIN: There is a clock domain reset handle.
*/
+#define CCU_DIV_BASIC BIT(0)
#define CCU_DIV_SKIP_ONE BIT(1)
#define CCU_DIV_SKIP_ONE_TO_THREE BIT(2)
#define CCU_DIV_LOCK_SHIFTED BIT(3)
@@ -31,11 +42,13 @@
* enum ccu_div_type - CCU Divider types
* @CCU_DIV_VAR: Clocks gate with variable divider.
* @CCU_DIV_GATE: Clocks gate with fixed divider.
+ * @CCU_DIV_BUF: Clock gate with no divider.
* @CCU_DIV_FIXED: Ungateable clock with fixed divider.
*/
enum ccu_div_type {
CCU_DIV_VAR,
CCU_DIV_GATE,
+ CCU_DIV_BUF,
CCU_DIV_FIXED
};
@@ -105,6 +118,4 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *init);
void ccu_div_hw_unregister(struct ccu_div *div);
-int ccu_div_reset_domain(struct ccu_div *div);
-
#endif /* __CLK_BT1_CCU_DIV_H__ */
diff --git a/drivers/clk/baikal-t1/ccu-pll.c b/drivers/clk/baikal-t1/ccu-pll.c
index 13ef28001439..357269f41cdc 100644
--- a/drivers/clk/baikal-t1/ccu-pll.c
+++ b/drivers/clk/baikal-t1/ccu-pll.c
@@ -228,14 +228,16 @@ static void ccu_pll_calc_factors(unsigned long rate, unsigned long parent_rate,
}
}
-static long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int ccu_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long nr = 1, nf = 1, od = 1;
- ccu_pll_calc_factors(rate, *parent_rate, &nr, &nf, &od);
+ ccu_pll_calc_factors(req->rate, req->best_parent_rate, &nr, &nf, &od);
- return ccu_pll_calc_freq(*parent_rate, nr, nf, od);
+ req->rate = ccu_pll_calc_freq(req->best_parent_rate, nr, nf, od);
+
+ return 0;
}
/*
@@ -481,7 +483,7 @@ static const struct clk_ops ccu_pll_gate_to_set_ops = {
.disable = ccu_pll_disable,
.is_enabled = ccu_pll_is_enabled,
.recalc_rate = ccu_pll_recalc_rate,
- .round_rate = ccu_pll_round_rate,
+ .determine_rate = ccu_pll_determine_rate,
.set_rate = ccu_pll_set_rate_norst,
.debug_init = ccu_pll_debug_init
};
@@ -491,7 +493,7 @@ static const struct clk_ops ccu_pll_straight_set_ops = {
.disable = ccu_pll_disable,
.is_enabled = ccu_pll_is_enabled,
.recalc_rate = ccu_pll_recalc_rate,
- .round_rate = ccu_pll_round_rate,
+ .determine_rate = ccu_pll_determine_rate,
.set_rate = ccu_pll_set_rate_reset,
.debug_init = ccu_pll_debug_init
};
diff --git a/drivers/clk/baikal-t1/ccu-pll.h b/drivers/clk/baikal-t1/ccu-pll.h
index 76cd9132a219..a71bfd7b90ec 100644
--- a/drivers/clk/baikal-t1/ccu-pll.h
+++ b/drivers/clk/baikal-t1/ccu-pll.h
@@ -14,6 +14,12 @@
#include <linux/of.h>
/*
+ * CCU PLL private flags
+ * @CCU_PLL_BASIC: Basic PLL required by the kernel as early as possible.
+ */
+#define CCU_PLL_BASIC BIT(0)
+
+/*
* struct ccu_pll_init_data - CCU PLL initialization data
* @id: Clock private identifier.
* @name: Clocks name.
@@ -22,6 +28,7 @@
* @sys_regs: Baikal-T1 System Controller registers map.
* @np: Pointer to the node describing the CCU PLLs.
* @flags: PLL clock flags.
+ * @features: PLL private features.
*/
struct ccu_pll_init_data {
unsigned int id;
@@ -31,6 +38,7 @@ struct ccu_pll_init_data {
struct regmap *sys_regs;
struct device_node *np;
unsigned long flags;
+ unsigned long features;
};
/*
diff --git a/drivers/clk/baikal-t1/ccu-rst.c b/drivers/clk/baikal-t1/ccu-rst.c
new file mode 100644
index 000000000000..40023ea67463
--- /dev/null
+++ b/drivers/clk/baikal-t1/ccu-rst.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 CCU Resets interface driver
+ */
+
+#define pr_fmt(fmt) "bt1-ccu-rst: " fmt
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/reset/bt1-ccu.h>
+
+#include "ccu-rst.h"
+
+#define CCU_AXI_MAIN_BASE 0x030
+#define CCU_AXI_DDR_BASE 0x034
+#define CCU_AXI_SATA_BASE 0x038
+#define CCU_AXI_GMAC0_BASE 0x03C
+#define CCU_AXI_GMAC1_BASE 0x040
+#define CCU_AXI_XGMAC_BASE 0x044
+#define CCU_AXI_PCIE_M_BASE 0x048
+#define CCU_AXI_PCIE_S_BASE 0x04C
+#define CCU_AXI_USB_BASE 0x050
+#define CCU_AXI_HWA_BASE 0x054
+#define CCU_AXI_SRAM_BASE 0x058
+
+#define CCU_SYS_DDR_BASE 0x02c
+#define CCU_SYS_SATA_REF_BASE 0x060
+#define CCU_SYS_APB_BASE 0x064
+#define CCU_SYS_PCIE_BASE 0x144
+
+#define CCU_RST_DELAY_US 1
+
+#define CCU_RST_TRIG(_base, _ofs) \
+ { \
+ .type = CCU_RST_TRIG, \
+ .base = _base, \
+ .mask = BIT(_ofs), \
+ }
+
+#define CCU_RST_DIR(_base, _ofs) \
+ { \
+ .type = CCU_RST_DIR, \
+ .base = _base, \
+ .mask = BIT(_ofs), \
+ }
+
+struct ccu_rst_info {
+ enum ccu_rst_type type;
+ unsigned int base;
+ unsigned int mask;
+};
+
+/*
+ * Each AXI-bus clock divider is equipped with the corresponding clock-consumer
+ * domain reset (it's self-deasserted reset control).
+ */
+static const struct ccu_rst_info axi_rst_info[] = {
+ [CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1),
+ [CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1),
+ [CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1),
+ [CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1),
+ [CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1),
+ [CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1),
+ [CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1),
+ [CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1),
+ [CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1),
+ [CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1),
+ [CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1),
+};
+
+/*
+ * SATA reference clock domain and APB-bus domain are connected with the
+ * sefl-deasserted reset control, which can be activated via the corresponding
+ * clock divider register. DDR and PCIe sub-domains can be reset with directly
+ * controlled reset signals. Resetting the DDR controller though won't end up
+ * well while the Linux kernel is working.
+ */
+static const struct ccu_rst_info sys_rst_info[] = {
+ [CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1),
+ [CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1),
+ [CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1),
+ [CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2),
+ [CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0),
+ [CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4),
+ [CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8),
+ [CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9),
+ [CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10),
+ [CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11),
+ [CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12),
+};
+
+static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx)
+{
+ struct ccu_rst *rst = to_ccu_rst(rcdev);
+ const struct ccu_rst_info *info = &rst->rsts_info[idx];
+
+ if (info->type != CCU_RST_TRIG)
+ return -EOPNOTSUPP;
+
+ regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask);
+
+ /* The next delay must be enough to cover all the resets. */
+ udelay(CCU_RST_DELAY_US);
+
+ return 0;
+}
+
+static int ccu_rst_set(struct reset_controller_dev *rcdev,
+ unsigned long idx, bool high)
+{
+ struct ccu_rst *rst = to_ccu_rst(rcdev);
+ const struct ccu_rst_info *info = &rst->rsts_info[idx];
+
+ if (info->type != CCU_RST_DIR)
+ return high ? -EOPNOTSUPP : 0;
+
+ return regmap_update_bits(rst->sys_regs, info->base,
+ info->mask, high ? info->mask : 0);
+}
+
+static int ccu_rst_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return ccu_rst_set(rcdev, idx, true);
+}
+
+static int ccu_rst_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return ccu_rst_set(rcdev, idx, false);
+}
+
+static int ccu_rst_status(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct ccu_rst *rst = to_ccu_rst(rcdev);
+ const struct ccu_rst_info *info = &rst->rsts_info[idx];
+ u32 val;
+
+ if (info->type != CCU_RST_DIR)
+ return -EOPNOTSUPP;
+
+ regmap_read(rst->sys_regs, info->base, &val);
+
+ return !!(val & info->mask);
+}
+
+static const struct reset_control_ops ccu_rst_ops = {
+ .reset = ccu_rst_reset,
+ .assert = ccu_rst_assert,
+ .deassert = ccu_rst_deassert,
+ .status = ccu_rst_status,
+};
+
+struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init)
+{
+ struct ccu_rst *rst;
+ int ret;
+
+ if (!rst_init)
+ return ERR_PTR(-EINVAL);
+
+ rst = kzalloc(sizeof(*rst), GFP_KERNEL);
+ if (!rst)
+ return ERR_PTR(-ENOMEM);
+
+ rst->sys_regs = rst_init->sys_regs;
+ if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) {
+ rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info);
+ rst->rsts_info = axi_rst_info;
+ } else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) {
+ rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info);
+ rst->rsts_info = sys_rst_info;
+ } else {
+ pr_err("Incompatible DT node '%s' specified\n",
+ of_node_full_name(rst_init->np));
+ ret = -EINVAL;
+ goto err_kfree_rst;
+ }
+
+ rst->rcdev.owner = THIS_MODULE;
+ rst->rcdev.ops = &ccu_rst_ops;
+ rst->rcdev.of_node = rst_init->np;
+
+ ret = reset_controller_register(&rst->rcdev);
+ if (ret) {
+ pr_err("Couldn't register '%s' reset controller\n",
+ of_node_full_name(rst_init->np));
+ goto err_kfree_rst;
+ }
+
+ return rst;
+
+err_kfree_rst:
+ kfree(rst);
+
+ return ERR_PTR(ret);
+}
+
+void ccu_rst_hw_unregister(struct ccu_rst *rst)
+{
+ reset_controller_unregister(&rst->rcdev);
+
+ kfree(rst);
+}
diff --git a/drivers/clk/baikal-t1/ccu-rst.h b/drivers/clk/baikal-t1/ccu-rst.h
new file mode 100644
index 000000000000..d6e8b2f671f4
--- /dev/null
+++ b/drivers/clk/baikal-t1/ccu-rst.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 CCU Resets interface driver
+ */
+#ifndef __CLK_BT1_CCU_RST_H__
+#define __CLK_BT1_CCU_RST_H__
+
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+struct ccu_rst_info;
+
+/*
+ * enum ccu_rst_type - CCU Reset types
+ * @CCU_RST_TRIG: Self-deasserted reset signal.
+ * @CCU_RST_DIR: Directly controlled reset signal.
+ */
+enum ccu_rst_type {
+ CCU_RST_TRIG,
+ CCU_RST_DIR,
+};
+
+/*
+ * struct ccu_rst_init_data - CCU Resets initialization data
+ * @sys_regs: Baikal-T1 System Controller registers map.
+ * @np: Pointer to the node with the System CCU block.
+ */
+struct ccu_rst_init_data {
+ struct regmap *sys_regs;
+ struct device_node *np;
+};
+
+/*
+ * struct ccu_rst - CCU Reset descriptor
+ * @rcdev: Reset controller descriptor.
+ * @sys_regs: Baikal-T1 System Controller registers map.
+ * @rsts_info: Reset flag info (base address and mask).
+ */
+struct ccu_rst {
+ struct reset_controller_dev rcdev;
+ struct regmap *sys_regs;
+ const struct ccu_rst_info *rsts_info;
+};
+#define to_ccu_rst(_rcdev) container_of(_rcdev, struct ccu_rst, rcdev)
+
+#ifdef CONFIG_CLK_BT1_CCU_RST
+
+struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init);
+
+void ccu_rst_hw_unregister(struct ccu_rst *rst);
+
+#else
+
+static inline
+struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init)
+{
+ return NULL;
+}
+
+static inline void ccu_rst_hw_unregister(struct ccu_rst *rst) {}
+
+#endif
+
+#endif /* __CLK_BT1_CCU_RST_H__ */
diff --git a/drivers/clk/baikal-t1/clk-ccu-div.c b/drivers/clk/baikal-t1/clk-ccu-div.c
index f141fda12b09..17d75e8e2e8f 100644
--- a/drivers/clk/baikal-t1/clk-ccu-div.c
+++ b/drivers/clk/baikal-t1/clk-ccu-div.c
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) "bt1-ccu-div: " fmt
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
@@ -19,14 +20,13 @@
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/ioport.h>
#include <linux/regmap.h>
#include <dt-bindings/clock/bt1-ccu.h>
-#include <dt-bindings/reset/bt1-ccu.h>
#include "ccu-div.h"
+#include "ccu-rst.h"
#define CCU_AXI_MAIN_BASE 0x030
#define CCU_AXI_DDR_BASE 0x034
@@ -76,6 +76,16 @@
.divider = _divider \
}
+#define CCU_DIV_BUF_INFO(_id, _name, _pname, _base, _flags) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .base = _base, \
+ .type = CCU_DIV_BUF, \
+ .flags = _flags \
+ }
+
#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \
{ \
.id = _id, \
@@ -85,12 +95,6 @@
.divider = _divider \
}
-#define CCU_DIV_RST_MAP(_rst_id, _clk_id) \
- { \
- .rst_id = _rst_id, \
- .clk_id = _clk_id \
- }
-
struct ccu_div_info {
unsigned int id;
const char *name;
@@ -105,11 +109,6 @@ struct ccu_div_info {
unsigned long features;
};
-struct ccu_div_rst_map {
- unsigned int rst_id;
- unsigned int clk_id;
-};
-
struct ccu_div_data {
struct device_node *np;
struct regmap *sys_regs;
@@ -118,11 +117,8 @@ struct ccu_div_data {
const struct ccu_div_info *divs_info;
struct ccu_div **divs;
- unsigned int rst_num;
- const struct ccu_div_rst_map *rst_map;
- struct reset_controller_dev rcdev;
+ struct ccu_rst *rsts;
};
-#define to_ccu_div_data(_rcdev) container_of(_rcdev, struct ccu_div_data, rcdev)
/*
* AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks
@@ -169,33 +165,22 @@ static const struct ccu_div_info axi_info[] = {
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN)
};
-static const struct ccu_div_rst_map axi_rst_map[] = {
- CCU_DIV_RST_MAP(CCU_AXI_MAIN_RST, CCU_AXI_MAIN_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_DDR_RST, CCU_AXI_DDR_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_SATA_RST, CCU_AXI_SATA_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_GMAC0_RST, CCU_AXI_GMAC0_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_GMAC1_RST, CCU_AXI_GMAC1_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_XGMAC_RST, CCU_AXI_XGMAC_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_PCIE_M_RST, CCU_AXI_PCIE_M_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_PCIE_S_RST, CCU_AXI_PCIE_S_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_USB_RST, CCU_AXI_USB_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_HWA_RST, CCU_AXI_HWA_CLK),
- CCU_DIV_RST_MAP(CCU_AXI_SRAM_RST, CCU_AXI_SRAM_CLK)
-};
-
/*
* APB-bus clock is marked as critical since it's a main communication bus
* for the SoC devices registers IO-operations.
*/
static const struct ccu_div_info sys_info[] = {
- CCU_DIV_VAR_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
+ CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk",
"sata_clk", CCU_SYS_SATA_REF_BASE, 4,
CLK_SET_RATE_GATE,
CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED |
CCU_DIV_RESET_DOMAIN),
+ CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
+ "sys_sata_clk", CCU_SYS_SATA_REF_BASE,
+ CLK_SET_RATE_PARENT),
CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
"pcie_clk", CCU_SYS_APB_BASE, 5,
- CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
+ CLK_IS_CRITICAL, CCU_DIV_BASIC | CCU_DIV_RESET_DOMAIN),
CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk",
"eth_clk", CCU_SYS_GMAC0_BASE, 5),
CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk",
@@ -204,10 +189,12 @@ static const struct ccu_div_info sys_info[] = {
"eth_clk", CCU_SYS_GMAC1_BASE, 5),
CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk",
"eth_clk", 10),
- CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
- "eth_clk", CCU_SYS_XGMAC_BASE, 8),
+ CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_CLK, "sys_xgmac_clk",
+ "eth_clk", CCU_SYS_XGMAC_BASE, 1),
+ CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
+ "sys_xgmac_clk", 8),
CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk",
- "eth_clk", 10),
+ "sys_xgmac_clk", 8),
CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk",
"eth_clk", CCU_SYS_USB_BASE, 10),
CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk",
@@ -227,74 +214,58 @@ static const struct ccu_div_info sys_info[] = {
"ref_clk", 25),
CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk",
"ref_clk", CCU_SYS_TIMER0_BASE, 17,
- CLK_SET_RATE_GATE, 0),
+ CLK_SET_RATE_GATE, CCU_DIV_BASIC),
CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk",
"ref_clk", CCU_SYS_TIMER1_BASE, 17,
- CLK_SET_RATE_GATE, 0),
+ CLK_SET_RATE_GATE, CCU_DIV_BASIC),
CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk",
"ref_clk", CCU_SYS_TIMER2_BASE, 17,
- CLK_SET_RATE_GATE, 0),
+ CLK_SET_RATE_GATE, CCU_DIV_BASIC),
CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk",
"eth_clk", CCU_SYS_WDT_BASE, 17,
CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE)
};
-static const struct ccu_div_rst_map sys_rst_map[] = {
- CCU_DIV_RST_MAP(CCU_SYS_SATA_REF_RST, CCU_SYS_SATA_REF_CLK),
- CCU_DIV_RST_MAP(CCU_SYS_APB_RST, CCU_SYS_APB_CLK),
-};
+static struct ccu_div_data *axi_data;
+static struct ccu_div_data *sys_data;
-static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data,
- unsigned int clk_id)
+static void ccu_div_set_data(struct ccu_div_data *data)
{
- struct ccu_div *div;
- int idx;
-
- for (idx = 0; idx < data->divs_num; ++idx) {
- div = data->divs[idx];
- if (div && div->id == clk_id)
- return div;
- }
-
- return ERR_PTR(-EINVAL);
+ struct device_node *np = data->np;
+
+ if (of_device_is_compatible(np, "baikal,bt1-ccu-axi"))
+ axi_data = data;
+ else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys"))
+ sys_data = data;
+ else
+ pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np));
}
-static int ccu_div_reset(struct reset_controller_dev *rcdev,
- unsigned long rst_id)
+static struct ccu_div_data *ccu_div_get_data(struct device_node *np)
{
- struct ccu_div_data *data = to_ccu_div_data(rcdev);
- const struct ccu_div_rst_map *map;
- struct ccu_div *div;
- int idx, ret;
+ if (of_device_is_compatible(np, "baikal,bt1-ccu-axi"))
+ return axi_data;
+ else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys"))
+ return sys_data;
- for (idx = 0, map = data->rst_map; idx < data->rst_num; ++idx, ++map) {
- if (map->rst_id == rst_id)
- break;
- }
- if (idx == data->rst_num) {
- pr_err("Invalid reset ID %lu specified\n", rst_id);
- return -EINVAL;
- }
+ pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np));
- div = ccu_div_find_desc(data, map->clk_id);
- if (IS_ERR(div)) {
- pr_err("Invalid clock ID %d in mapping\n", map->clk_id);
- return PTR_ERR(div);
- }
+ return NULL;
+}
- ret = ccu_div_reset_domain(div);
- if (ret) {
- pr_err("Reset isn't supported by divider %s\n",
- clk_hw_get_name(ccu_div_get_clk_hw(div)));
+static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data,
+ unsigned int clk_id)
+{
+ int idx;
+
+ for (idx = 0; idx < data->divs_num; ++idx) {
+ if (data->divs_info[idx].id == clk_id)
+ return data->divs[idx];
}
- return ret;
+ return ERR_PTR(-EINVAL);
}
-static const struct reset_control_ops ccu_div_rst_ops = {
- .reset = ccu_div_reset,
-};
-
static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
{
struct ccu_div_data *data;
@@ -308,13 +279,9 @@ static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) {
data->divs_num = ARRAY_SIZE(axi_info);
data->divs_info = axi_info;
- data->rst_num = ARRAY_SIZE(axi_rst_map);
- data->rst_map = axi_rst_map;
} else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) {
data->divs_num = ARRAY_SIZE(sys_info);
data->divs_info = sys_info;
- data->rst_num = ARRAY_SIZE(sys_rst_map);
- data->rst_map = sys_rst_map;
} else {
pr_err("Incompatible DT node '%s' specified\n",
of_node_full_name(np));
@@ -365,14 +332,16 @@ static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec,
clk_id = clkspec->args[0];
div = ccu_div_find_desc(data, clk_id);
if (IS_ERR(div)) {
- pr_info("Invalid clock ID %d specified\n", clk_id);
+ if (div != ERR_PTR(-EPROBE_DEFER))
+ pr_info("Invalid clock ID %d specified\n", clk_id);
+
return ERR_CAST(div);
}
return ccu_div_get_clk_hw(div);
}
-static int ccu_div_clk_register(struct ccu_div_data *data)
+static int ccu_div_clk_register(struct ccu_div_data *data, bool defer)
{
int idx, ret;
@@ -380,6 +349,13 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
const struct ccu_div_info *info = &data->divs_info[idx];
struct ccu_div_init_data init = {0};
+ if (!!(info->features & CCU_DIV_BASIC) ^ defer) {
+ if (!data->divs[idx])
+ data->divs[idx] = ERR_PTR(-EPROBE_DEFER);
+
+ continue;
+ }
+
init.id = info->id;
init.name = info->name;
init.parent_name = info->parent_name;
@@ -396,6 +372,9 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
init.base = info->base;
init.sys_regs = data->sys_regs;
init.divider = info->divider;
+ } else if (init.type == CCU_DIV_BUF) {
+ init.base = info->base;
+ init.sys_regs = data->sys_regs;
} else {
init.divider = info->divider;
}
@@ -409,49 +388,104 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
}
}
- ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data);
- if (ret) {
- pr_err("Couldn't register dividers '%s' clock provider\n",
- of_node_full_name(data->np));
- goto err_hw_unregister;
- }
-
return 0;
err_hw_unregister:
- for (--idx; idx >= 0; --idx)
+ for (--idx; idx >= 0; --idx) {
+ if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer)
+ continue;
+
ccu_div_hw_unregister(data->divs[idx]);
+ }
return ret;
}
-static void ccu_div_clk_unregister(struct ccu_div_data *data)
+static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer)
{
int idx;
- of_clk_del_provider(data->np);
+ /* Uninstall only the clocks registered on the specified stage */
+ for (idx = 0; idx < data->divs_num; ++idx) {
+ if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer)
+ continue;
- for (idx = 0; idx < data->divs_num; ++idx)
ccu_div_hw_unregister(data->divs[idx]);
+ }
}
-static int ccu_div_rst_register(struct ccu_div_data *data)
+static int ccu_div_of_register(struct ccu_div_data *data)
{
int ret;
- data->rcdev.ops = &ccu_div_rst_ops;
- data->rcdev.of_node = data->np;
- data->rcdev.nr_resets = data->rst_num;
+ ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data);
+ if (ret) {
+ pr_err("Couldn't register dividers '%s' clock provider\n",
+ of_node_full_name(data->np));
+ }
+
+ return ret;
+}
- ret = reset_controller_register(&data->rcdev);
- if (ret)
+static int ccu_div_rst_register(struct ccu_div_data *data)
+{
+ struct ccu_rst_init_data init = {0};
+
+ init.sys_regs = data->sys_regs;
+ init.np = data->np;
+
+ data->rsts = ccu_rst_hw_register(&init);
+ if (IS_ERR(data->rsts)) {
pr_err("Couldn't register divider '%s' reset controller\n",
of_node_full_name(data->np));
+ return PTR_ERR(data->rsts);
+ }
+
+ return 0;
+}
+
+static int ccu_div_probe(struct platform_device *pdev)
+{
+ struct ccu_div_data *data;
+ int ret;
+
+ data = ccu_div_get_data(dev_of_node(&pdev->dev));
+ if (!data)
+ return -EINVAL;
+
+ ret = ccu_div_clk_register(data, false);
+ if (ret)
+ return ret;
+
+ ret = ccu_div_rst_register(data);
+ if (ret)
+ goto err_clk_unregister;
+
+ return 0;
+
+err_clk_unregister:
+ ccu_div_clk_unregister(data, false);
return ret;
}
-static void ccu_div_init(struct device_node *np)
+static const struct of_device_id ccu_div_of_match[] = {
+ { .compatible = "baikal,bt1-ccu-axi" },
+ { .compatible = "baikal,bt1-ccu-sys" },
+ { }
+};
+
+static struct platform_driver ccu_div_driver = {
+ .probe = ccu_div_probe,
+ .driver = {
+ .name = "clk-ccu-div",
+ .of_match_table = ccu_div_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(ccu_div_driver);
+
+static __init void ccu_div_init(struct device_node *np)
{
struct ccu_div_data *data;
int ret;
@@ -464,22 +498,23 @@ static void ccu_div_init(struct device_node *np)
if (ret)
goto err_free_data;
- ret = ccu_div_clk_register(data);
+ ret = ccu_div_clk_register(data, true);
if (ret)
goto err_free_data;
- ret = ccu_div_rst_register(data);
+ ret = ccu_div_of_register(data);
if (ret)
goto err_clk_unregister;
+ ccu_div_set_data(data);
+
return;
err_clk_unregister:
- ccu_div_clk_unregister(data);
+ ccu_div_clk_unregister(data, true);
err_free_data:
ccu_div_free_data(data);
}
-
-CLK_OF_DECLARE(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
-CLK_OF_DECLARE(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);
+CLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
+CLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);
diff --git a/drivers/clk/baikal-t1/clk-ccu-pll.c b/drivers/clk/baikal-t1/clk-ccu-pll.c
index 2445d4b12baf..921b87024feb 100644
--- a/drivers/clk/baikal-t1/clk-ccu-pll.c
+++ b/drivers/clk/baikal-t1/clk-ccu-pll.c
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
@@ -31,13 +32,14 @@
#define CCU_PCIE_PLL_BASE 0x018
#define CCU_ETH_PLL_BASE 0x020
-#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags) \
- { \
- .id = _id, \
- .name = _name, \
- .parent_name = _pname, \
- .base = _base, \
- .flags = _flags \
+#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags, _features) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .base = _base, \
+ .flags = _flags, \
+ .features = _features, \
}
#define CCU_PLL_NUM ARRAY_SIZE(pll_info)
@@ -48,6 +50,7 @@ struct ccu_pll_info {
const char *parent_name;
unsigned int base;
unsigned long flags;
+ unsigned long features;
};
/*
@@ -61,15 +64,15 @@ struct ccu_pll_info {
*/
static const struct ccu_pll_info pll_info[] = {
CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL, CCU_PLL_BASIC),
CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE,
- CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0),
CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE,
- CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0),
CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL, CCU_PLL_BASIC),
CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE,
- CLK_IS_CRITICAL | CLK_SET_RATE_GATE)
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0)
};
struct ccu_pll_data {
@@ -78,16 +81,16 @@ struct ccu_pll_data {
struct ccu_pll *plls[CCU_PLL_NUM];
};
+static struct ccu_pll_data *pll_data;
+
static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data,
unsigned int clk_id)
{
- struct ccu_pll *pll;
int idx;
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
- pll = data->plls[idx];
- if (pll && pll->id == clk_id)
- return pll;
+ if (pll_info[idx].id == clk_id)
+ return data->plls[idx];
}
return ERR_PTR(-EINVAL);
@@ -133,14 +136,16 @@ static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec,
clk_id = clkspec->args[0];
pll = ccu_pll_find_desc(data, clk_id);
if (IS_ERR(pll)) {
- pr_info("Invalid PLL clock ID %d specified\n", clk_id);
+ if (pll != ERR_PTR(-EPROBE_DEFER))
+ pr_info("Invalid PLL clock ID %d specified\n", clk_id);
+
return ERR_CAST(pll);
}
return ccu_pll_get_clk_hw(pll);
}
-static int ccu_pll_clk_register(struct ccu_pll_data *data)
+static int ccu_pll_clk_register(struct ccu_pll_data *data, bool defer)
{
int idx, ret;
@@ -148,6 +153,14 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data)
const struct ccu_pll_info *info = &pll_info[idx];
struct ccu_pll_init_data init = {0};
+ /* Defer non-basic PLLs allocation for the probe stage */
+ if (!!(info->features & CCU_PLL_BASIC) ^ defer) {
+ if (!data->plls[idx])
+ data->plls[idx] = ERR_PTR(-EPROBE_DEFER);
+
+ continue;
+ }
+
init.id = info->id;
init.name = info->name;
init.parent_name = info->parent_name;
@@ -155,6 +168,7 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data)
init.sys_regs = data->sys_regs;
init.np = data->np;
init.flags = info->flags;
+ init.features = info->features;
data->plls[idx] = ccu_pll_hw_register(&init);
if (IS_ERR(data->plls[idx])) {
@@ -165,22 +179,70 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data)
}
}
+ return 0;
+
+err_hw_unregister:
+ for (--idx; idx >= 0; --idx) {
+ if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer)
+ continue;
+
+ ccu_pll_hw_unregister(data->plls[idx]);
+ }
+
+ return ret;
+}
+
+static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer)
+{
+ int idx;
+
+ /* Uninstall only the clocks registered on the specified stage */
+ for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
+ if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer)
+ continue;
+
+ ccu_pll_hw_unregister(data->plls[idx]);
+ }
+}
+
+static int ccu_pll_of_register(struct ccu_pll_data *data)
+{
+ int ret;
+
ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data);
if (ret) {
pr_err("Couldn't register PLL provider of '%s'\n",
of_node_full_name(data->np));
- goto err_hw_unregister;
}
- return 0;
+ return ret;
+}
-err_hw_unregister:
- for (--idx; idx >= 0; --idx)
- ccu_pll_hw_unregister(data->plls[idx]);
+static int ccu_pll_probe(struct platform_device *pdev)
+{
+ struct ccu_pll_data *data = pll_data;
- return ret;
+ if (!data)
+ return -EINVAL;
+
+ return ccu_pll_clk_register(data, false);
}
+static const struct of_device_id ccu_pll_of_match[] = {
+ { .compatible = "baikal,bt1-ccu-pll" },
+ { }
+};
+
+static struct platform_driver ccu_pll_driver = {
+ .probe = ccu_pll_probe,
+ .driver = {
+ .name = "clk-ccu-pll",
+ .of_match_table = ccu_pll_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(ccu_pll_driver);
+
static __init void ccu_pll_init(struct device_node *np)
{
struct ccu_pll_data *data;
@@ -194,13 +256,22 @@ static __init void ccu_pll_init(struct device_node *np)
if (ret)
goto err_free_data;
- ret = ccu_pll_clk_register(data);
+ ret = ccu_pll_clk_register(data, true);
if (ret)
goto err_free_data;
+ ret = ccu_pll_of_register(data);
+ if (ret)
+ goto err_clk_unregister;
+
+ pll_data = data;
+
return;
+err_clk_unregister:
+ ccu_pll_clk_unregister(data, true);
+
err_free_data:
ccu_pll_free_data(data);
}
-CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
+CLK_OF_DECLARE_DRIVER(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
index 77266afb1c79..a972d763eb77 100644
--- a/drivers/clk/bcm/Kconfig
+++ b/drivers/clk/bcm/Kconfig
@@ -37,6 +37,15 @@ config CLK_BCM_63XX_GATE
Enable common clock framework support for Broadcom BCM63xx DSL SoCs
based on the MIPS architecture
+config CLK_BCM63268_TIMER
+ bool "Broadcom BCM63268 timer clock and reset support"
+ depends on BMIPS_GENERIC || COMPILE_TEST
+ default BMIPS_GENERIC
+ select RESET_CONTROLLER
+ help
+ Enable timer clock and reset support for Broadcom BCM63268 DSL SoCs
+ based on the MIPS architecture.
+
config CLK_BCM_KONA
bool "Broadcom Kona CCU clock support"
depends on ARCH_BCM_MOBILE || COMPILE_TEST
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index edb66b44cb27..d0b6f4b1fb08 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o
obj-$(CONFIG_CLK_BCM_63XX_GATE) += clk-bcm63xx-gate.o
+obj-$(CONFIG_CLK_BCM63268_TIMER) += clk-bcm63268-timer.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
diff --git a/drivers/clk/bcm/clk-bcm2711-dvp.c b/drivers/clk/bcm/clk-bcm2711-dvp.c
index e63a42618ac2..e79720e85685 100644
--- a/drivers/clk/bcm/clk-bcm2711-dvp.c
+++ b/drivers/clk/bcm/clk-bcm2711-dvp.c
@@ -56,6 +56,8 @@ static int clk_dvp_probe(struct platform_device *pdev)
if (ret)
return ret;
+ data->num = NR_CLOCKS;
+
data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev,
"hdmi0-108MHz",
&clk_dvp_parent, 0,
@@ -76,7 +78,6 @@ static int clk_dvp_probe(struct platform_device *pdev)
goto unregister_clk0;
}
- data->num = NR_CLOCKS;
ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
data);
if (ret)
@@ -92,15 +93,13 @@ unregister_clk0:
return ret;
};
-static int clk_dvp_remove(struct platform_device *pdev)
+static void clk_dvp_remove(struct platform_device *pdev)
{
struct clk_dvp *dvp = platform_get_drvdata(pdev);
struct clk_hw_onecell_data *data = dvp->data;
clk_hw_unregister_gate(data->hws[1]);
clk_hw_unregister_gate(data->hws[0]);
-
- return 0;
}
static const struct of_device_id clk_dvp_dt_ids[] = {
@@ -111,7 +110,7 @@ MODULE_DEVICE_TABLE(of, clk_dvp_dt_ids);
static struct platform_driver clk_dvp_driver = {
.probe = clk_dvp_probe,
- .remove = clk_dvp_remove,
+ .remove = clk_dvp_remove,
.driver = {
.name = "brcm2711-dvp",
.of_match_table = clk_dvp_dt_ids,
diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c
index 290a2846a86b..0fafa5cba442 100644
--- a/drivers/clk/bcm/clk-bcm2835-aux.c
+++ b/drivers/clk/bcm/clk-bcm2835-aux.c
@@ -69,4 +69,3 @@ builtin_platform_driver(bcm2835_aux_clk_driver);
MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
MODULE_DESCRIPTION("BCM2835 auxiliary peripheral clock driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 48a1eb9f2d55..02215ea79403 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -30,8 +30,9 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/math.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <dt-bindings/clock/bcm2835.h>
@@ -502,6 +503,8 @@ struct bcm2835_clock_data {
bool low_jitter;
u32 tcnt_mux;
+
+ bool round_up;
};
struct bcm2835_gate_data {
@@ -567,18 +570,23 @@ static long bcm2835_pll_rate_from_divisors(unsigned long parent_rate,
return rate >> A2W_PLL_FRAC_BITS;
}
-static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int bcm2835_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
const struct bcm2835_pll_data *data = pll->data;
u32 ndiv, fdiv;
- rate = clamp(rate, data->min_rate, data->max_rate);
+ req->rate = clamp(req->rate, data->min_rate, data->max_rate);
+
+ bcm2835_pll_choose_ndiv_and_fdiv(req->rate, req->best_parent_rate,
+ &ndiv, &fdiv);
- bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv);
+ req->rate = bcm2835_pll_rate_from_divisors(req->best_parent_rate,
+ ndiv, fdiv,
+ 1);
- return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1);
+ return 0;
}
static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw,
@@ -780,7 +788,7 @@ static const struct clk_ops bcm2835_pll_clk_ops = {
.unprepare = bcm2835_pll_off,
.recalc_rate = bcm2835_pll_get_rate,
.set_rate = bcm2835_pll_set_rate,
- .round_rate = bcm2835_pll_round_rate,
+ .determine_rate = bcm2835_pll_determine_rate,
.debug_init = bcm2835_pll_debug_init,
};
@@ -966,9 +974,9 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw,
return div;
}
-static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
- unsigned long parent_rate,
- u32 div)
+static unsigned long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
+ unsigned long parent_rate,
+ u32 div)
{
const struct bcm2835_clock_data *data = clock->data;
u64 temp;
@@ -993,12 +1001,34 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
return temp;
}
+static unsigned long bcm2835_round_rate(unsigned long rate)
+{
+ unsigned long scaler;
+ unsigned long limit;
+
+ limit = rate / 100000;
+
+ scaler = 1;
+ while (scaler < limit)
+ scaler *= 10;
+
+ /*
+ * If increasing a clock by less than 0.1% changes it
+ * from ..999.. to ..000.., round up.
+ */
+ if ((rate + scaler - 1) / scaler % 1000 == 0)
+ rate = roundup(rate, scaler);
+
+ return rate;
+}
+
static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct bcm2835_cprman *cprman = clock->cprman;
const struct bcm2835_clock_data *data = clock->data;
+ unsigned long rate;
u32 div;
if (data->int_bits == 0 && data->frac_bits == 0)
@@ -1006,7 +1036,12 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
div = cprman_read(cprman, data->div_reg);
- return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
+ rate = bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
+
+ if (data->round_up)
+ rate = bcm2835_round_rate(rate);
+
+ return rate;
}
static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
@@ -1520,7 +1555,7 @@ static const char *const bcm2835_clock_osc_parents[] = {
.parents = bcm2835_clock_osc_parents, \
__VA_ARGS__)
-/* main peripherial parent mux */
+/* main peripheral parent mux */
static const char *const bcm2835_clock_per_parents[] = {
"gnd",
"xosc",
@@ -1784,7 +1819,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.load_mask = CM_PLLC_LOADPER,
.hold_mask = CM_PLLC_HOLDPER,
.fixed_divider = 1,
- .flags = CLK_SET_RATE_PARENT),
+ .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/*
* PLLD is the display PLL, used to drive DSI display panels.
@@ -2143,7 +2178,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_UARTDIV,
.int_bits = 10,
.frac_bits = 12,
- .tcnt_mux = 28),
+ .tcnt_mux = 28,
+ .round_up = true),
/* TV encoder clock. Only operating frequency is 108Mhz. */
[BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(
@@ -2319,4 +2355,3 @@ builtin_platform_driver(bcm2835_clk_driver);
MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
MODULE_DESCRIPTION("BCM2835 clock driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/clk/bcm/clk-bcm53573-ilp.c b/drivers/clk/bcm/clk-bcm53573-ilp.c
index 84f2af736ee8..b2fc05b60783 100644
--- a/drivers/clk/bcm/clk-bcm53573-ilp.c
+++ b/drivers/clk/bcm/clk-bcm53573-ilp.c
@@ -59,7 +59,7 @@ static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw,
/*
* At minimum we should loop for a bit to let hardware do the
* measurement. This isn't very accurate however, so for a better
- * precision lets try getting 20 different values for and use average.
+ * precision let's try getting 20 different values and use average.
*/
while (num < 20) {
regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val);
@@ -112,7 +112,7 @@ static void bcm53573_ilp_init(struct device_node *np)
goto err_free_ilp;
}
- ilp->regmap = syscon_node_to_regmap(of_get_parent(np));
+ ilp->regmap = syscon_node_to_regmap(np->parent);
if (IS_ERR(ilp->regmap)) {
err = PTR_ERR(ilp->regmap);
goto err_free_ilp;
diff --git a/drivers/clk/bcm/clk-bcm63268-timer.c b/drivers/clk/bcm/clk-bcm63268-timer.c
new file mode 100644
index 000000000000..463710d272a1
--- /dev/null
+++ b/drivers/clk/bcm/clk-bcm63268-timer.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BCM63268 Timer Clock and Reset Controller Driver
+ *
+ * Copyright (C) 2023 Álvaro Fernández Rojas <noltari@gmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/bcm63268-clock.h>
+
+#define BCM63268_TIMER_RESET_SLEEP_MIN_US 10000
+#define BCM63268_TIMER_RESET_SLEEP_MAX_US 20000
+
+struct bcm63268_tclkrst_hw {
+ void __iomem *regs;
+ spinlock_t lock;
+
+ struct reset_controller_dev rcdev;
+ struct clk_hw_onecell_data data;
+};
+
+struct bcm63268_tclk_table_entry {
+ const char * const name;
+ u8 bit;
+};
+
+static const struct bcm63268_tclk_table_entry bcm63268_timer_clocks[] = {
+ {
+ .name = "ephy1",
+ .bit = BCM63268_TCLK_EPHY1,
+ }, {
+ .name = "ephy2",
+ .bit = BCM63268_TCLK_EPHY2,
+ }, {
+ .name = "ephy3",
+ .bit = BCM63268_TCLK_EPHY3,
+ }, {
+ .name = "gphy1",
+ .bit = BCM63268_TCLK_GPHY1,
+ }, {
+ .name = "dsl",
+ .bit = BCM63268_TCLK_DSL,
+ }, {
+ .name = "wakeon_ephy",
+ .bit = BCM63268_TCLK_WAKEON_EPHY,
+ }, {
+ .name = "wakeon_dsl",
+ .bit = BCM63268_TCLK_WAKEON_DSL,
+ }, {
+ .name = "fap1_pll",
+ .bit = BCM63268_TCLK_FAP1,
+ }, {
+ .name = "fap2_pll",
+ .bit = BCM63268_TCLK_FAP2,
+ }, {
+ .name = "uto_50",
+ .bit = BCM63268_TCLK_UTO_50,
+ }, {
+ .name = "uto_extin",
+ .bit = BCM63268_TCLK_UTO_EXTIN,
+ }, {
+ .name = "usb_ref",
+ .bit = BCM63268_TCLK_USB_REF,
+ }, {
+ /* sentinel */
+ }
+};
+
+static inline struct bcm63268_tclkrst_hw *
+to_bcm63268_timer_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct bcm63268_tclkrst_hw, rcdev);
+}
+
+static int bcm63268_timer_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);
+ unsigned long flags;
+ uint32_t val;
+
+ spin_lock_irqsave(&reset->lock, flags);
+ val = __raw_readl(reset->regs);
+ if (assert)
+ val &= ~BIT(id);
+ else
+ val |= BIT(id);
+ __raw_writel(val, reset->regs);
+ spin_unlock_irqrestore(&reset->lock, flags);
+
+ return 0;
+}
+
+static int bcm63268_timer_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return bcm63268_timer_reset_update(rcdev, id, true);
+}
+
+static int bcm63268_timer_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return bcm63268_timer_reset_update(rcdev, id, false);
+}
+
+static int bcm63268_timer_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ bcm63268_timer_reset_update(rcdev, id, true);
+ usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,
+ BCM63268_TIMER_RESET_SLEEP_MAX_US);
+
+ bcm63268_timer_reset_update(rcdev, id, false);
+ /*
+ * Ensure component is taken out reset state by sleeping also after
+ * deasserting the reset. Otherwise, the component may not be ready
+ * for operation.
+ */
+ usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,
+ BCM63268_TIMER_RESET_SLEEP_MAX_US);
+
+ return 0;
+}
+
+static int bcm63268_timer_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);
+
+ return !(__raw_readl(reset->regs) & BIT(id));
+}
+
+static const struct reset_control_ops bcm63268_timer_reset_ops = {
+ .assert = bcm63268_timer_reset_assert,
+ .deassert = bcm63268_timer_reset_deassert,
+ .reset = bcm63268_timer_reset_reset,
+ .status = bcm63268_timer_reset_status,
+};
+
+static int bcm63268_tclk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct bcm63268_tclk_table_entry *entry;
+ struct bcm63268_tclkrst_hw *hw;
+ struct clk_hw *clk;
+ u8 maxbit = 0;
+ int i, ret;
+
+ for (entry = bcm63268_timer_clocks; entry->name; entry++)
+ maxbit = max(maxbit, entry->bit);
+ maxbit++;
+
+ hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),
+ GFP_KERNEL);
+ if (!hw)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hw);
+
+ spin_lock_init(&hw->lock);
+
+ hw->data.num = maxbit;
+ for (i = 0; i < maxbit; i++)
+ hw->data.hws[i] = ERR_PTR(-ENODEV);
+
+ hw->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hw->regs))
+ return PTR_ERR(hw->regs);
+
+ for (entry = bcm63268_timer_clocks; entry->name; entry++) {
+ clk = devm_clk_hw_register_gate(dev, entry->name, NULL, 0,
+ hw->regs, entry->bit,
+ CLK_GATE_BIG_ENDIAN,
+ &hw->lock);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ hw->data.hws[entry->bit] = clk;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &hw->data);
+ if (ret)
+ return ret;
+
+ hw->rcdev.of_node = dev->of_node;
+ hw->rcdev.ops = &bcm63268_timer_reset_ops;
+
+ ret = devm_reset_controller_register(dev, &hw->rcdev);
+ if (ret)
+ dev_err(dev, "Failed to register reset controller\n");
+
+ return 0;
+}
+
+static const struct of_device_id bcm63268_tclk_dt_ids[] = {
+ { .compatible = "brcm,bcm63268-timer-clocks" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver bcm63268_tclk = {
+ .probe = bcm63268_tclk_probe,
+ .driver = {
+ .name = "bcm63268-timer-clock",
+ .of_match_table = bcm63268_tclk_dt_ids,
+ },
+};
+builtin_platform_driver(bcm63268_tclk);
diff --git a/drivers/clk/bcm/clk-bcm63xx-gate.c b/drivers/clk/bcm/clk-bcm63xx-gate.c
index 89297c57881e..d6d857474436 100644
--- a/drivers/clk/bcm/clk-bcm63xx-gate.c
+++ b/drivers/clk/bcm/clk-bcm63xx-gate.c
@@ -3,7 +3,6 @@
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/bcm3368-clock.h>
@@ -541,7 +540,7 @@ out_err:
return ret;
}
-static int clk_bcm63xx_remove(struct platform_device *pdev)
+static void clk_bcm63xx_remove(struct platform_device *pdev)
{
struct clk_bcm63xx_hw *hw = platform_get_drvdata(pdev);
int i;
@@ -552,8 +551,6 @@ static int clk_bcm63xx_remove(struct platform_device *pdev)
if (!IS_ERR(hw->data.hws[i]))
clk_hw_unregister_gate(hw->data.hws[i]);
}
-
- return 0;
}
static const struct of_device_id clk_bcm63xx_dt_ids[] = {
diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c
index dcacf55c55ae..83ec13da9b2e 100644
--- a/drivers/clk/bcm/clk-iproc-asiu.c
+++ b/drivers/clk/bcm/clk-iproc-asiu.c
@@ -98,22 +98,27 @@ static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw,
return clk->rate;
}
-static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int iproc_asiu_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned int div;
- if (rate == 0 || *parent_rate == 0)
+ if (req->rate == 0 || req->best_parent_rate == 0)
return -EINVAL;
- if (rate == *parent_rate)
- return *parent_rate;
+ if (req->rate == req->best_parent_rate)
+ return 0;
- div = DIV_ROUND_CLOSEST(*parent_rate, rate);
- if (div < 2)
- return *parent_rate;
+ div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
+ if (div < 2) {
+ req->rate = req->best_parent_rate;
- return *parent_rate / div;
+ return 0;
+ }
+
+ req->rate = req->best_parent_rate / div;
+
+ return 0;
}
static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -168,7 +173,7 @@ static const struct clk_ops iproc_asiu_ops = {
.enable = iproc_asiu_clk_enable,
.disable = iproc_asiu_clk_disable,
.recalc_rate = iproc_asiu_clk_recalc_rate,
- .round_rate = iproc_asiu_clk_round_rate,
+ .determine_rate = iproc_asiu_clk_determine_rate,
.set_rate = iproc_asiu_clk_set_rate,
};
diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c
index 1a098db12062..680f9d8d357c 100644
--- a/drivers/clk/bcm/clk-iproc-pll.c
+++ b/drivers/clk/bcm/clk-iproc-pll.c
@@ -726,6 +726,7 @@ void iproc_pll_clk_setup(struct device_node *node,
const char *parent_name;
struct iproc_clk *iclk_array;
struct clk_hw_onecell_data *clk_data;
+ const char *clk_name;
if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl))
return;
@@ -773,7 +774,12 @@ void iproc_pll_clk_setup(struct device_node *node,
iclk = &iclk_array[0];
iclk->pll = pll;
- init.name = node->name;
+ ret = of_property_read_string_index(node, "clock-output-names",
+ 0, &clk_name);
+ if (WARN_ON(ret))
+ goto err_pll_register;
+
+ init.name = clk_name;
init.ops = &iproc_pll_ops;
init.flags = 0;
parent_name = of_clk_get_parent_name(node, 0);
@@ -793,13 +799,11 @@ void iproc_pll_clk_setup(struct device_node *node,
goto err_pll_register;
clk_data->hws[0] = &iclk->hw;
+ parent_name = clk_name;
/* now initialize and register all leaf clocks */
for (i = 1; i < num_clks; i++) {
- const char *clk_name;
-
memset(&init, 0, sizeof(init));
- parent_name = node->name;
ret = of_property_read_string_index(node, "clock-output-names",
i, &clk_name);
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
index ec5749e301ba..0171e6b2bfca 100644
--- a/drivers/clk/bcm/clk-kona.c
+++ b/drivers/clk/bcm/clk-kona.c
@@ -10,6 +10,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/clk-provider.h>
+#include <linux/string_choices.h>
/*
* "Policies" affect the frequencies of bus clocks provided by a
@@ -52,24 +53,6 @@ static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div)
return (u64)reg_div + ((u64)1 << div->u.s.frac_width);
}
-/*
- * Build a scaled divider value as close as possible to the
- * given whole part (div_value) and fractional part (expressed
- * in billionths).
- */
-u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths)
-{
- u64 combined;
-
- BUG_ON(!div_value);
- BUG_ON(billionths >= BILLION);
-
- combined = (u64)div_value * BILLION + billionths;
- combined <<= div->u.s.frac_width;
-
- return DIV_ROUND_CLOSEST_ULL(combined, BILLION);
-}
-
/* The scaled minimum divisor representable by a divider */
static inline u64
scaled_div_min(struct bcm_clk_div *div)
@@ -502,7 +485,7 @@ static int clk_gate(struct ccu_data *ccu, const char *name,
return 0;
pr_err("%s: failed to %s gate for %s\n", __func__,
- enable ? "enable" : "disable", name);
+ str_enable_disable(enable), name);
return -EIO;
}
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
index e09655024ac2..348a3454ce40 100644
--- a/drivers/clk/bcm/clk-kona.h
+++ b/drivers/clk/bcm/clk-kona.h
@@ -492,8 +492,6 @@ extern struct clk_ops kona_peri_clk_ops;
/* Externally visible functions */
extern u64 scaled_div_max(struct bcm_clk_div *div);
-extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
- u32 billionths);
extern void __init kona_dt_ccu_setup(struct ccu_data *ccu,
struct device_node *node);
diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c
index 876b37b8683c..1a9162f0ae31 100644
--- a/drivers/clk/bcm/clk-raspberrypi.c
+++ b/drivers/clk/bcm/clk-raspberrypi.c
@@ -18,24 +18,6 @@
#include <soc/bcm2835/raspberrypi-firmware.h>
-enum rpi_firmware_clk_id {
- RPI_FIRMWARE_EMMC_CLK_ID = 1,
- RPI_FIRMWARE_UART_CLK_ID,
- RPI_FIRMWARE_ARM_CLK_ID,
- RPI_FIRMWARE_CORE_CLK_ID,
- RPI_FIRMWARE_V3D_CLK_ID,
- RPI_FIRMWARE_H264_CLK_ID,
- RPI_FIRMWARE_ISP_CLK_ID,
- RPI_FIRMWARE_SDRAM_CLK_ID,
- RPI_FIRMWARE_PIXEL_CLK_ID,
- RPI_FIRMWARE_PWM_CLK_ID,
- RPI_FIRMWARE_HEVC_CLK_ID,
- RPI_FIRMWARE_EMMC2_CLK_ID,
- RPI_FIRMWARE_M2MC_CLK_ID,
- RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
- RPI_FIRMWARE_NUM_CLK_ID,
-};
-
static char *rpi_firmware_clk_names[] = {
[RPI_FIRMWARE_EMMC_CLK_ID] = "emmc",
[RPI_FIRMWARE_UART_CLK_ID] = "uart",
@@ -51,6 +33,8 @@ static char *rpi_firmware_clk_names[] = {
[RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2",
[RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc",
[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb",
+ [RPI_FIRMWARE_VEC_CLK_ID] = "vec",
+ [RPI_FIRMWARE_DISP_CLK_ID] = "disp",
};
#define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
@@ -73,11 +57,19 @@ struct raspberrypi_clk_data {
struct raspberrypi_clk *rpi;
};
+static inline
+const struct raspberrypi_clk_data *clk_hw_to_data(const struct clk_hw *hw)
+{
+ return container_of(hw, struct raspberrypi_clk_data, hw);
+}
+
struct raspberrypi_clk_variant {
bool export;
char *clkdev;
unsigned long min_rate;
bool minimize;
+ bool maximize;
+ u32 flags;
};
static struct raspberrypi_clk_variant
@@ -85,6 +77,7 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = {
[RPI_FIRMWARE_ARM_CLK_ID] = {
.export = true,
.clkdev = "cpu0",
+ .flags = CLK_IS_CRITICAL,
},
[RPI_FIRMWARE_CORE_CLK_ID] = {
.export = true,
@@ -100,6 +93,12 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = {
* always use the minimum the drivers will let us.
*/
.minimize = true,
+
+ /*
+ * It should never be disabled as it drives the bus for
+ * everything else.
+ */
+ .flags = CLK_IS_CRITICAL,
},
[RPI_FIRMWARE_M2MC_CLK_ID] = {
.export = true,
@@ -125,12 +124,46 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = {
* drivers will let us.
*/
.minimize = true,
+
+ /*
+ * As mentioned above, this clock is disabled during boot,
+ * the firmware will skip the HSM initialization, resulting
+ * in a bus lockup. Therefore, make sure it's enabled
+ * during boot, but after it, it can be enabled/disabled
+ * by the driver.
+ */
+ .flags = CLK_IGNORE_UNUSED,
},
[RPI_FIRMWARE_V3D_CLK_ID] = {
.export = true,
+ .maximize = true,
+ },
+ [RPI_FIRMWARE_PIXEL_CLK_ID] = {
+ .export = true,
+ .minimize = true,
+ .flags = CLK_IS_CRITICAL,
+ },
+ [RPI_FIRMWARE_HEVC_CLK_ID] = {
+ .export = true,
+ .minimize = true,
+ .flags = CLK_IS_CRITICAL,
+ },
+ [RPI_FIRMWARE_ISP_CLK_ID] = {
+ .export = true,
+ .minimize = true,
},
[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = {
.export = true,
+ .minimize = true,
+ .flags = CLK_IS_CRITICAL,
+ },
+ [RPI_FIRMWARE_VEC_CLK_ID] = {
+ .export = true,
+ .minimize = true,
+ },
+ [RPI_FIRMWARE_DISP_CLK_ID] = {
+ .export = true,
+ .minimize = true,
},
};
@@ -161,7 +194,6 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware,
struct raspberrypi_firmware_prop msg = {
.id = cpu_to_le32(data->id),
.val = cpu_to_le32(*val),
- .disable_turbo = cpu_to_le32(1),
};
int ret;
@@ -176,16 +208,18 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware,
static int raspberrypi_fw_is_prepared(struct clk_hw *hw)
{
- struct raspberrypi_clk_data *data =
- container_of(hw, struct raspberrypi_clk_data, hw);
+ const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk *rpi = data->rpi;
u32 val = 0;
int ret;
ret = raspberrypi_clock_property(rpi->firmware, data,
RPI_FIRMWARE_GET_CLOCK_STATE, &val);
- if (ret)
+ if (ret) {
+ dev_err_ratelimited(rpi->dev, "Failed to get %s state: %d\n",
+ clk_hw_get_name(hw), ret);
return 0;
+ }
return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT);
}
@@ -194,16 +228,18 @@ static int raspberrypi_fw_is_prepared(struct clk_hw *hw)
static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct raspberrypi_clk_data *data =
- container_of(hw, struct raspberrypi_clk_data, hw);
+ const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk *rpi = data->rpi;
u32 val = 0;
int ret;
ret = raspberrypi_clock_property(rpi->firmware, data,
RPI_FIRMWARE_GET_CLOCK_RATE, &val);
- if (ret)
+ if (ret) {
+ dev_err_ratelimited(rpi->dev, "Failed to get %s frequency: %d\n",
+ clk_hw_get_name(hw), ret);
return 0;
+ }
return val;
}
@@ -211,8 +247,7 @@ static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw,
static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct raspberrypi_clk_data *data =
- container_of(hw, struct raspberrypi_clk_data, hw);
+ const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk *rpi = data->rpi;
u32 _rate = rate;
int ret;
@@ -229,8 +264,7 @@ static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate,
static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
- struct raspberrypi_clk_data *data =
- container_of(hw, struct raspberrypi_clk_data, hw);
+ const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk_variant *variant = data->variant;
/*
@@ -252,7 +286,41 @@ static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw,
return 0;
}
+static int raspberrypi_fw_prepare(struct clk_hw *hw)
+{
+ const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
+ struct raspberrypi_clk *rpi = data->rpi;
+ u32 state = RPI_FIRMWARE_STATE_ENABLE_BIT;
+ int ret;
+
+ ret = raspberrypi_clock_property(rpi->firmware, data,
+ RPI_FIRMWARE_SET_CLOCK_STATE, &state);
+ if (ret)
+ dev_err_ratelimited(rpi->dev,
+ "Failed to set clock %s state to on: %d\n",
+ clk_hw_get_name(hw), ret);
+
+ return ret;
+}
+
+static void raspberrypi_fw_unprepare(struct clk_hw *hw)
+{
+ const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
+ struct raspberrypi_clk *rpi = data->rpi;
+ u32 state = 0;
+ int ret;
+
+ ret = raspberrypi_clock_property(rpi->firmware, data,
+ RPI_FIRMWARE_SET_CLOCK_STATE, &state);
+ if (ret)
+ dev_err_ratelimited(rpi->dev,
+ "Failed to set clock %s state to off: %d\n",
+ clk_hw_get_name(hw), ret);
+}
+
static const struct clk_ops raspberrypi_firmware_clk_ops = {
+ .prepare = raspberrypi_fw_prepare,
+ .unprepare = raspberrypi_fw_unprepare,
.is_prepared = raspberrypi_fw_is_prepared,
.recalc_rate = raspberrypi_fw_get_rate,
.determine_rate = raspberrypi_fw_dumb_determine_rate,
@@ -279,8 +347,10 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
init.name = devm_kasprintf(rpi->dev, GFP_KERNEL,
"fw-clk-%s",
rpi_firmware_clk_names[id]);
+ if (!init.name)
+ return ERR_PTR(-ENOMEM);
init.ops = &raspberrypi_firmware_clk_ops;
- init.flags = CLK_GET_RATE_NOCACHE;
+ init.flags = variant->flags | CLK_GET_RATE_NOCACHE;
data->hw.init = &init;
@@ -317,6 +387,9 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
}
}
+ if (variant->maximize)
+ variant->min_rate = max_rate;
+
if (variant->min_rate) {
unsigned long rate;
@@ -364,9 +437,9 @@ static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi,
while (clks->id) {
struct raspberrypi_clk_variant *variant;
- if (clks->id > RPI_FIRMWARE_NUM_CLK_ID) {
+ if (clks->id >= RPI_FIRMWARE_NUM_CLK_ID) {
dev_err(rpi->dev, "Unknown clock id: %u (max: %u)\n",
- clks->id, RPI_FIRMWARE_NUM_CLK_ID);
+ clks->id, RPI_FIRMWARE_NUM_CLK_ID - 1);
return -EINVAL;
}
@@ -379,8 +452,8 @@ static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi,
if (IS_ERR(hw))
return PTR_ERR(hw);
- data->hws[clks->id] = hw;
data->num = clks->id + 1;
+ data->hws[clks->id] = hw;
}
clks++;
@@ -447,13 +520,11 @@ static int raspberrypi_clk_probe(struct platform_device *pdev)
return 0;
}
-static int raspberrypi_clk_remove(struct platform_device *pdev)
+static void raspberrypi_clk_remove(struct platform_device *pdev)
{
struct raspberrypi_clk *rpi = platform_get_drvdata(pdev);
platform_device_unregister(rpi->cpufreq);
-
- return 0;
}
static const struct of_device_id raspberrypi_clk_match[] = {
@@ -475,4 +546,3 @@ module_platform_driver(raspberrypi_clk_driver);
MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
MODULE_DESCRIPTION("Raspberry Pi firmware clock driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:raspberrypi-clk");
diff --git a/drivers/clk/bcm/clk-sr.c b/drivers/clk/bcm/clk-sr.c
index 5db021685d6d..3b2cf397b789 100644
--- a/drivers/clk/bcm/clk-sr.c
+++ b/drivers/clk/bcm/clk-sr.c
@@ -5,7 +5,7 @@
#include <linux/err.h>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/bcm-sr.h>
diff --git a/drivers/clk/berlin/berlin2-avpll.c b/drivers/clk/berlin/berlin2-avpll.c
index aa89b4c9464e..79f3d37a0ee0 100644
--- a/drivers/clk/berlin/berlin2-avpll.c
+++ b/drivers/clk/berlin/berlin2-avpll.c
@@ -319,7 +319,7 @@ berlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
/*
* AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide.
- * AV2/AV3 form a fractional divider, where only specfic values for AV3
+ * AV2/AV3 form a fractional divider, where only specific values for AV3
* are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass.
*/
if (ch->index < 6) {
diff --git a/drivers/clk/berlin/berlin2-div.c b/drivers/clk/berlin/berlin2-div.c
index eb14a5bc0507..0a248bfe2193 100644
--- a/drivers/clk/berlin/berlin2-div.c
+++ b/drivers/clk/berlin/berlin2-div.c
@@ -210,6 +210,7 @@ static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
}
static const struct clk_ops berlin2_div_rate_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.recalc_rate = berlin2_div_recalc_rate,
};
diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c
index bccdfa00fd37..67a9edbba29c 100644
--- a/drivers/clk/berlin/bg2.c
+++ b/drivers/clk/berlin/bg2.c
@@ -500,12 +500,15 @@ static void __init berlin2_clock_setup(struct device_node *np)
int n, ret;
clk_data = kzalloc(struct_size(clk_data, hws, MAX_CLKS), GFP_KERNEL);
- if (!clk_data)
+ if (!clk_data) {
+ of_node_put(parent_np);
return;
+ }
clk_data->num = MAX_CLKS;
hws = clk_data->hws;
gbase = of_iomap(parent_np, 0);
+ of_node_put(parent_np);
if (!gbase)
return;
diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c
index e9518d35f262..dd2784bb75b6 100644
--- a/drivers/clk/berlin/bg2q.c
+++ b/drivers/clk/berlin/bg2q.c
@@ -286,19 +286,23 @@ static void __init berlin2q_clock_setup(struct device_node *np)
int n, ret;
clk_data = kzalloc(struct_size(clk_data, hws, MAX_CLKS), GFP_KERNEL);
- if (!clk_data)
+ if (!clk_data) {
+ of_node_put(parent_np);
return;
+ }
clk_data->num = MAX_CLKS;
hws = clk_data->hws;
gbase = of_iomap(parent_np, 0);
if (!gbase) {
+ of_node_put(parent_np);
pr_err("%pOF: Unable to map global base\n", np);
return;
}
/* BG2Q CPU PLL is not part of global registers */
cpupll_base = of_iomap(parent_np, 1);
+ of_node_put(parent_np);
if (!cpupll_base) {
pr_err("%pOF: Unable to map cpupll base\n", np);
iounmap(gbase);
diff --git a/drivers/clk/clk-apple-nco.c b/drivers/clk/clk-apple-nco.c
index 39472a51530a..d3ced4a0f029 100644
--- a/drivers/clk/clk-apple-nco.c
+++ b/drivers/clk/clk-apple-nco.c
@@ -212,13 +212,15 @@ static unsigned long applnco_recalc_rate(struct clk_hw *hw,
((u64) div) * incbase + inc1);
}
-static long applnco_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int applnco_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long lo = *parent_rate / (COARSE_DIV_OFFSET + LFSR_TBLSIZE) + 1;
- unsigned long hi = *parent_rate / COARSE_DIV_OFFSET;
+ unsigned long lo = req->best_parent_rate / (COARSE_DIV_OFFSET + LFSR_TBLSIZE) + 1;
+ unsigned long hi = req->best_parent_rate / COARSE_DIV_OFFSET;
- return clamp(rate, lo, hi);
+ req->rate = clamp(req->rate, lo, hi);
+
+ return 0;
}
static int applnco_enable(struct clk_hw *hw)
@@ -246,7 +248,7 @@ static void applnco_disable(struct clk_hw *hw)
static const struct clk_ops applnco_ops = {
.set_rate = applnco_set_rate,
.recalc_rate = applnco_recalc_rate,
- .round_rate = applnco_round_rate,
+ .determine_rate = applnco_determine_rate,
.enable = applnco_enable,
.disable = applnco_disable,
.is_enabled = applnco_is_enabled,
@@ -297,6 +299,9 @@ static int applnco_probe(struct platform_device *pdev)
memset(&init, 0, sizeof(init));
init.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"%s-%d", np->name, i);
+ if (!init.name)
+ return -ENOMEM;
+
init.ops = &applnco_ops;
init.parent_data = &pdata;
init.num_parents = 1;
diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c
index bacebd457e6f..595cfa533fb9 100644
--- a/drivers/clk/clk-asm9260.c
+++ b/drivers/clk/clk-asm9260.c
@@ -80,7 +80,7 @@ struct asm9260_mux_clock {
u8 mask;
u32 *table;
const char *name;
- const char **parent_names;
+ const struct clk_parent_data *parent_data;
u8 num_parents;
unsigned long offset;
unsigned long flags;
@@ -92,8 +92,8 @@ static const struct asm9260_div_clk asm9260_div_clks[] __initconst = {
{ CLKID_SYS_CPU, "cpu_div", "main_gate", HW_CPUCLKDIV },
{ CLKID_SYS_AHB, "ahb_div", "cpu_div", HW_SYSAHBCLKDIV },
- /* i2s has two deviders: one for only external mclk and internal
- * devider for all clks. */
+ /* i2s has two dividers: one for only external mclk and internal
+ * divider for all clks. */
{ CLKID_SYS_I2S0M, "i2s0m_div", "i2s0_mclk", HW_I2S0MCLKDIV },
{ CLKID_SYS_I2S1M, "i2s1m_div", "i2s1_mclk", HW_I2S1MCLKDIV },
{ CLKID_SYS_I2S0S, "i2s0s_div", "i2s0_gate", HW_I2S0SCLKDIV },
@@ -232,10 +232,10 @@ static const struct asm9260_gate_data asm9260_ahb_gates[] __initconst = {
HW_AHBCLKCTRL1, 16 },
};
-static const char __initdata *main_mux_p[] = { NULL, NULL };
-static const char __initdata *i2s0_mux_p[] = { NULL, NULL, "i2s0m_div"};
-static const char __initdata *i2s1_mux_p[] = { NULL, NULL, "i2s1m_div"};
-static const char __initdata *clkout_mux_p[] = { NULL, NULL, "rtc"};
+static struct clk_parent_data __initdata main_mux_p[] = { { .index = 0, }, { .name = "pll" } };
+static struct clk_parent_data __initdata i2s0_mux_p[] = { { .index = 0, }, { .name = "pll" }, { .name = "i2s0m_div"} };
+static struct clk_parent_data __initdata i2s1_mux_p[] = { { .index = 0, }, { .name = "pll" }, { .name = "i2s1m_div"} };
+static struct clk_parent_data __initdata clkout_mux_p[] = { { .index = 0, }, { .name = "pll" }, { .name = "rtc"} };
static u32 three_mux_table[] = {0, 1, 3};
static struct asm9260_mux_clock asm9260_mux_clks[] __initdata = {
@@ -255,9 +255,10 @@ static struct asm9260_mux_clock asm9260_mux_clks[] __initdata = {
static void __init asm9260_acc_init(struct device_node *np)
{
- struct clk_hw *hw;
+ struct clk_hw *pll_hw;
struct clk_hw **hws;
- const char *ref_clk, *pll_clk = "pll";
+ const char *pll_clk = "pll";
+ struct clk_parent_data pll_parent_data = { .index = 0 };
u32 rate;
int n;
@@ -274,21 +275,15 @@ static void __init asm9260_acc_init(struct device_node *np)
/* register pll */
rate = (ioread32(base + HW_SYSPLLCTRL) & 0xffff) * 1000000;
- /* TODO: Convert to DT parent scheme */
- ref_clk = of_clk_get_parent_name(np, 0);
- hw = __clk_hw_register_fixed_rate(NULL, NULL, pll_clk,
- ref_clk, NULL, NULL, 0, rate, 0,
- CLK_FIXED_RATE_PARENT_ACCURACY);
-
- if (IS_ERR(hw))
+ pll_hw = clk_hw_register_fixed_rate_parent_accuracy(NULL, pll_clk, &pll_parent_data,
+ 0, rate);
+ if (IS_ERR(pll_hw))
panic("%pOFn: can't register REFCLK. Check DT!", np);
for (n = 0; n < ARRAY_SIZE(asm9260_mux_clks); n++) {
const struct asm9260_mux_clock *mc = &asm9260_mux_clks[n];
- mc->parent_names[0] = ref_clk;
- mc->parent_names[1] = pll_clk;
- hw = clk_hw_register_mux_table(NULL, mc->name, mc->parent_names,
+ clk_hw_register_mux_table_parent_data(NULL, mc->name, mc->parent_data,
mc->num_parents, mc->flags, base + mc->offset,
0, mc->mask, 0, mc->table, &asm9260_clk_lock);
}
@@ -297,7 +292,7 @@ static void __init asm9260_acc_init(struct device_node *np)
for (n = 0; n < ARRAY_SIZE(asm9260_mux_gates); n++) {
const struct asm9260_gate_data *gd = &asm9260_mux_gates[n];
- hw = clk_hw_register_gate(NULL, gd->name,
+ clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags | CLK_SET_RATE_PARENT,
base + gd->reg, gd->bit_idx, 0, &asm9260_clk_lock);
}
diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 411ff5fb2c07..ff84191d0fe8 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -4,8 +4,8 @@
#define pr_fmt(fmt) "clk-aspeed: " fmt
#include <linux/mfd/syscon.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -701,6 +701,7 @@ static void __init aspeed_cc_init(struct device_node *np)
GFP_KERNEL);
if (!aspeed_clk_data)
return;
+ aspeed_clk_data->num = ASPEED_NUM_CLKS;
/*
* This way all clocks fetched before the platform device probes,
@@ -732,8 +733,6 @@ static void __init aspeed_cc_init(struct device_node *np)
aspeed_ast2500_cc(map);
else
pr_err("unknown platform, failed to add clocks\n");
-
- aspeed_clk_data->num = ASPEED_NUM_CLKS;
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, aspeed_clk_data);
if (ret)
pr_err("failed to add DT provider: %d\n", ret);
diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c
index 24dab2312bc6..114afc13d640 100644
--- a/drivers/clk/clk-ast2600.c
+++ b/drivers/clk/clk-ast2600.c
@@ -5,8 +5,8 @@
#define pr_fmt(fmt) "clk-ast2600: " fmt
#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -15,7 +15,11 @@
#include "clk-aspeed.h"
-#define ASPEED_G6_NUM_CLKS 71
+/*
+ * This includes the gates (configured from aspeed_g6_gates), plus the
+ * explicitly-configured clocks (ASPEED_CLK_HPLL and up).
+ */
+#define ASPEED_G6_NUM_CLKS 73
#define ASPEED_G6_SILICON_REV 0x014
#define CHIP_REVISION_ID GENMASK(23, 16)
@@ -32,6 +36,20 @@
#define ASPEED_G6_CLK_SELECTION1 0x300
#define ASPEED_G6_CLK_SELECTION2 0x304
#define ASPEED_G6_CLK_SELECTION4 0x310
+#define ASPEED_G6_CLK_SELECTION5 0x314
+#define I3C_CLK_SELECTION_SHIFT 31
+#define I3C_CLK_SELECTION BIT(31)
+#define I3C_CLK_SELECT_HCLK (0 << I3C_CLK_SELECTION_SHIFT)
+#define I3C_CLK_SELECT_APLL_DIV (1 << I3C_CLK_SELECTION_SHIFT)
+#define APLL_DIV_SELECTION_SHIFT 28
+#define APLL_DIV_SELECTION GENMASK(30, 28)
+#define APLL_DIV_2 (0b001 << APLL_DIV_SELECTION_SHIFT)
+#define APLL_DIV_3 (0b010 << APLL_DIV_SELECTION_SHIFT)
+#define APLL_DIV_4 (0b011 << APLL_DIV_SELECTION_SHIFT)
+#define APLL_DIV_5 (0b100 << APLL_DIV_SELECTION_SHIFT)
+#define APLL_DIV_6 (0b101 << APLL_DIV_SELECTION_SHIFT)
+#define APLL_DIV_7 (0b110 << APLL_DIV_SELECTION_SHIFT)
+#define APLL_DIV_8 (0b111 << APLL_DIV_SELECTION_SHIFT)
#define ASPEED_HPLL_PARAM 0x200
#define ASPEED_APLL_PARAM 0x210
@@ -55,6 +73,27 @@ static void __iomem *scu_g6_base;
static u8 soc_rev;
/*
+ * The majority of the clocks in the system are gates paired with a reset
+ * controller that holds the IP in reset; this is represented by the @reset_idx
+ * member of entries here.
+ *
+ * This borrows from clk_hw_register_gate, but registers two 'gates', one
+ * to control the clock enable register and the other to control the reset
+ * IP. This allows us to enforce the ordering:
+ *
+ * 1. Place IP in reset
+ * 2. Enable clock
+ * 3. Delay
+ * 4. Release reset
+ *
+ * Consequently, if reset_idx is set, reset control is implicit: the clock
+ * consumer does not need its own reset handling, as enabling the clock will
+ * also deassert reset.
+ *
+ * There are some gates that do not have an associated reset; these are
+ * handled by using -1 as the index for the reset, and the consumer must
+ * explicitly assert/deassert reset lines as required.
+ *
* Clocks marked with CLK_IS_CRITICAL:
*
* ref0 and ref1 are essential for the SoC to operate
@@ -97,14 +136,13 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
[ASPEED_CLK_GATE_LHCCLK] = { 37, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */
/* Reserved 38 RSA: no longer used */
/* Reserved 39 */
- [ASPEED_CLK_GATE_I3C0CLK] = { 40, 40, "i3c0clk-gate", NULL, 0 }, /* I3C0 */
- [ASPEED_CLK_GATE_I3C1CLK] = { 41, 41, "i3c1clk-gate", NULL, 0 }, /* I3C1 */
- [ASPEED_CLK_GATE_I3C2CLK] = { 42, 42, "i3c2clk-gate", NULL, 0 }, /* I3C2 */
- [ASPEED_CLK_GATE_I3C3CLK] = { 43, 43, "i3c3clk-gate", NULL, 0 }, /* I3C3 */
- [ASPEED_CLK_GATE_I3C4CLK] = { 44, 44, "i3c4clk-gate", NULL, 0 }, /* I3C4 */
- [ASPEED_CLK_GATE_I3C5CLK] = { 45, 45, "i3c5clk-gate", NULL, 0 }, /* I3C5 */
- [ASPEED_CLK_GATE_I3C6CLK] = { 46, 46, "i3c6clk-gate", NULL, 0 }, /* I3C6 */
- [ASPEED_CLK_GATE_I3C7CLK] = { 47, 47, "i3c7clk-gate", NULL, 0 }, /* I3C7 */
+ [ASPEED_CLK_GATE_I3C0CLK] = { 40, 40, "i3c0clk-gate", "i3cclk", 0 }, /* I3C0 */
+ [ASPEED_CLK_GATE_I3C1CLK] = { 41, 41, "i3c1clk-gate", "i3cclk", 0 }, /* I3C1 */
+ [ASPEED_CLK_GATE_I3C2CLK] = { 42, 42, "i3c2clk-gate", "i3cclk", 0 }, /* I3C2 */
+ [ASPEED_CLK_GATE_I3C3CLK] = { 43, 43, "i3c3clk-gate", "i3cclk", 0 }, /* I3C3 */
+ [ASPEED_CLK_GATE_I3C4CLK] = { 44, 44, "i3c4clk-gate", "i3cclk", 0 }, /* I3C4 */
+ [ASPEED_CLK_GATE_I3C5CLK] = { 45, 45, "i3c5clk-gate", "i3cclk", 0 }, /* I3C5 */
+ /* Reserved: 46 & 47 */
[ASPEED_CLK_GATE_UART1CLK] = { 48, -1, "uart1clk-gate", "uart", 0 }, /* UART1 */
[ASPEED_CLK_GATE_UART2CLK] = { 49, -1, "uart2clk-gate", "uart", 0 }, /* UART2 */
[ASPEED_CLK_GATE_UART3CLK] = { 50, -1, "uart3clk-gate", "uart", 0 }, /* UART3 */
@@ -119,7 +157,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
[ASPEED_CLK_GATE_UART11CLK] = { 59, -1, "uart11clk-gate", "uartx", 0 }, /* UART11 */
[ASPEED_CLK_GATE_UART12CLK] = { 60, -1, "uart12clk-gate", "uartx", 0 }, /* UART12 */
[ASPEED_CLK_GATE_UART13CLK] = { 61, -1, "uart13clk-gate", "uartx", 0 }, /* UART13 */
- [ASPEED_CLK_GATE_FSICLK] = { 62, 59, "fsiclk-gate", NULL, 0 }, /* FSI */
+ [ASPEED_CLK_GATE_FSICLK] = { 62, 59, "fsiclk-gate", "fsiclk", 0 }, /* FSI */
};
static const struct clk_div_table ast2600_eclk_div_table[] = {
@@ -622,7 +660,7 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev)
regmap_write(map, 0x308, 0x12000); /* 3x3 = 9 */
/* P-Bus (BCLK) clock divider */
- hw = clk_hw_register_divider_table(dev, "bclk", "hpll", 0,
+ hw = clk_hw_register_divider_table(dev, "bclk", "epll", 0,
scu_g6_base + ASPEED_G6_CLK_SELECTION1, 20, 3, 0,
ast2600_div_table,
&aspeed_g6_clk_lock);
@@ -652,6 +690,9 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev)
const struct aspeed_gate_data *gd = &aspeed_g6_gates[i];
u32 gate_flags;
+ if (!gd->name)
+ continue;
+
/*
* Special case: the USB port 1 clock (bit 14) is always
* working the opposite way from the other ones.
@@ -772,6 +813,17 @@ static void __init aspeed_g6_cc(struct regmap *map)
/* USB 2.0 port1 phy 40MHz clock */
hw = clk_hw_register_fixed_rate(NULL, "usb-phy-40m", NULL, 0, 40000000);
aspeed_g6_clk_data->hws[ASPEED_CLK_USBPHY_40M] = hw;
+
+ /* i3c clock: source from apll, divide by 8 */
+ regmap_update_bits(map, ASPEED_G6_CLK_SELECTION5,
+ I3C_CLK_SELECTION | APLL_DIV_SELECTION,
+ I3C_CLK_SELECT_APLL_DIV | APLL_DIV_8);
+
+ hw = clk_hw_register_fixed_factor(NULL, "i3cclk", "apll", 0, 1, 8);
+ aspeed_g6_clk_data->hws[ASPEED_CLK_I3C] = hw;
+
+ hw = clk_hw_register_fixed_factor(NULL, "fsiclk", "apll", 0, 1, 4);
+ aspeed_g6_clk_data->hws[ASPEED_CLK_FSI] = hw;
};
static void __init aspeed_g6_cc_init(struct device_node *np)
@@ -790,6 +842,7 @@ static void __init aspeed_g6_cc_init(struct device_node *np)
ASPEED_G6_NUM_CLKS), GFP_KERNEL);
if (!aspeed_g6_clk_data)
return;
+ aspeed_g6_clk_data->num = ASPEED_G6_NUM_CLKS;
/*
* This way all clocks fetched before the platform device probes,
@@ -811,7 +864,6 @@ static void __init aspeed_g6_cc_init(struct device_node *np)
}
aspeed_g6_cc(map);
- aspeed_g6_clk_data->num = ASPEED_G6_NUM_CLKS;
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, aspeed_g6_clk_data);
if (ret)
pr_err("failed to add DT provider: %d\n", ret);
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index ac6ff736ac8f..fa5ccef73e60 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -6,13 +6,17 @@
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
-#include <linux/platform_device.h>
+#include <linux/adi-axi-common.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
#include <linux/clk-provider.h>
-#include <linux/slab.h>
+#include <linux/err.h>
#include <linux/io.h>
-#include <linux/of.h>
#include <linux/module.h>
-#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
#define AXI_CLKGEN_V2_REG_RESET 0x40
#define AXI_CLKGEN_V2_REG_CLKSEL 0x44
@@ -27,6 +31,9 @@
#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
+#define ADI_CLKGEN_REG_FPGA_VOLTAGE 0x0140
+#define ADI_CLKGEN_INFO_FPGA_VOLTAGE(val) ((val) & GENMASK(15, 0))
+
#define MMCM_REG_CLKOUT5_2 0x07
#define MMCM_REG_CLKOUT0_1 0x08
#define MMCM_REG_CLKOUT0_2 0x09
@@ -89,7 +96,7 @@ static uint32_t axi_clkgen_lookup_filter(unsigned int m)
}
}
-static const uint32_t axi_clkgen_lock_table[] = {
+static const u32 axi_clkgen_lock_table[] = {
0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
@@ -101,7 +108,7 @@ static const uint32_t axi_clkgen_lock_table[] = {
0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
};
-static uint32_t axi_clkgen_lookup_lock(unsigned int m)
+static u32 axi_clkgen_lookup_lock(unsigned int m)
{
if (m < ARRAY_SIZE(axi_clkgen_lock_table))
return axi_clkgen_lock_table[m];
@@ -117,14 +124,15 @@ static const struct axi_clkgen_limits axi_clkgen_zynqmp_default_limits = {
static const struct axi_clkgen_limits axi_clkgen_zynq_default_limits = {
.fpfd_min = 10000,
- .fpfd_max = 300000,
+ .fpfd_max = 450000,
.fvco_min = 600000,
.fvco_max = 1200000,
};
static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits,
- unsigned long fin, unsigned long fout,
- unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
+ unsigned long fin, unsigned long fout,
+ unsigned int *best_d, unsigned int *best_m,
+ unsigned int *best_dout)
{
unsigned long d, d_min, d_max, _d_min, _d_max;
unsigned long m, m_min, m_max;
@@ -140,15 +148,15 @@ static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits,
*best_m = 0;
*best_dout = 0;
- d_min = max_t(unsigned long, DIV_ROUND_UP(fin, limits->fpfd_max), 1);
- d_max = min_t(unsigned long, fin / limits->fpfd_min, 80);
+ d_min = max(DIV_ROUND_UP(fin, limits->fpfd_max), 1);
+ d_max = min(fin / limits->fpfd_min, 80);
again:
fvco_min_fract = limits->fvco_min << fract_shift;
fvco_max_fract = limits->fvco_max << fract_shift;
- m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1);
- m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << fract_shift);
+ m_min = max(DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1);
+ m_max = min(fvco_max_fract * d_max / fin, 64 << fract_shift);
for (m = m_min; m <= m_max; m++) {
_d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract));
@@ -171,7 +179,7 @@ again:
}
}
- /* Lets see if we find a better setting in fractional mode */
+ /* Let's see if we find a better setting in fractional mode */
if (fract_shift == 0) {
fract_shift = 3;
goto again;
@@ -191,9 +199,9 @@ struct axi_clkgen_div_params {
};
static void axi_clkgen_calc_clk_params(unsigned int divider,
- unsigned int frac_divider, struct axi_clkgen_div_params *params)
+ unsigned int frac_divider,
+ struct axi_clkgen_div_params *params)
{
-
memset(params, 0x0, sizeof(*params));
if (divider == 1) {
@@ -221,7 +229,7 @@ static void axi_clkgen_calc_clk_params(unsigned int divider,
if (params->edge == 0 || frac_divider == 1)
params->low--;
if (((params->edge == 0) ^ (frac_divider == 1)) ||
- (divider == 2 && frac_divider == 1))
+ (divider == 2 && frac_divider == 1))
params->frac_wf_f = 1;
params->frac_phase = params->edge * 4 + frac_divider / 2;
@@ -229,13 +237,13 @@ static void axi_clkgen_calc_clk_params(unsigned int divider,
}
static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
- unsigned int reg, unsigned int val)
+ unsigned int reg, unsigned int val)
{
writel(val, axi_clkgen->base + reg);
}
static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
- unsigned int reg, unsigned int *val)
+ unsigned int reg, unsigned int *val)
{
*val = readl(axi_clkgen->base + reg);
}
@@ -256,7 +264,7 @@ static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
}
static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
- unsigned int reg, unsigned int *val)
+ unsigned int reg, unsigned int *val)
{
unsigned int reg_val;
int ret;
@@ -280,7 +288,8 @@ static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
}
static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
- unsigned int reg, unsigned int val, unsigned int mask)
+ unsigned int reg, unsigned int val,
+ unsigned int mask)
{
unsigned int reg_val = 0;
int ret;
@@ -301,8 +310,7 @@ static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
return 0;
}
-static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
- bool enable)
+static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, bool enable)
{
unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
@@ -318,31 +326,31 @@ static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
}
static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen,
- unsigned int reg1, unsigned int reg2, unsigned int reg3,
- struct axi_clkgen_div_params *params)
+ unsigned int reg1, unsigned int reg2,
+ unsigned int reg3,
+ struct axi_clkgen_div_params *params)
{
axi_clkgen_mmcm_write(axi_clkgen, reg1,
- (params->high << 6) | params->low, 0xefff);
+ (params->high << 6) | params->low, 0xefff);
axi_clkgen_mmcm_write(axi_clkgen, reg2,
- (params->frac << 12) | (params->frac_en << 11) |
- (params->frac_wf_r << 10) | (params->edge << 7) |
- (params->nocount << 6), 0x7fff);
+ (params->frac << 12) | (params->frac_en << 11) |
+ (params->frac_wf_r << 10) | (params->edge << 7) |
+ (params->nocount << 6), 0x7fff);
if (reg3 != 0) {
axi_clkgen_mmcm_write(axi_clkgen, reg3,
- (params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00);
+ (params->frac_phase << 11) | (params->frac_wf_f << 10),
+ 0x3c00);
}
}
-static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
- unsigned long rate, unsigned long parent_rate)
+static int axi_clkgen_set_rate(struct clk_hw *clk_hw, unsigned long rate,
+ unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
const struct axi_clkgen_limits *limits = &axi_clkgen->limits;
unsigned int d, m, dout;
struct axi_clkgen_div_params params;
- uint32_t power = 0;
- uint32_t filter;
- uint32_t lock;
+ u32 power = 0, filter, lock;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
@@ -362,49 +370,51 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, &params);
axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2,
- MMCM_REG_CLKOUT5_2, &params);
+ MMCM_REG_CLKOUT5_2, &params);
axi_clkgen_calc_clk_params(d, 0, &params);
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
- (params.edge << 13) | (params.nocount << 12) |
- (params.high << 6) | params.low, 0x3fff);
+ (params.edge << 13) | (params.nocount << 12) |
+ (params.high << 6) | params.low, 0x3fff);
axi_clkgen_calc_clk_params(m >> 3, m & 0x7, &params);
axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2,
- MMCM_REG_CLKOUT6_2, &params);
+ MMCM_REG_CLKOUT6_2, &params);
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
- (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
+ (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
- (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
+ (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
return 0;
}
-static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int axi_clkgen_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(hw);
const struct axi_clkgen_limits *limits = &axi_clkgen->limits;
unsigned int d, m, dout;
unsigned long long tmp;
- axi_clkgen_calc_params(limits, *parent_rate, rate, &d, &m, &dout);
+ axi_clkgen_calc_params(limits, req->best_parent_rate, req->rate,
+ &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
- tmp = (unsigned long long)*parent_rate * m;
+ tmp = (unsigned long long)req->best_parent_rate * m;
tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
- return min_t(unsigned long long, tmp, LONG_MAX);
+ req->rate = min_t(unsigned long long, tmp, LONG_MAX);
+ return 0;
}
static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen,
- unsigned int reg1, unsigned int reg2)
+ unsigned int reg1, unsigned int reg2)
{
unsigned int val1, val2;
unsigned int div;
@@ -431,7 +441,7 @@ static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen,
}
static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
- unsigned long parent_rate)
+ unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
unsigned int d, m, dout;
@@ -439,9 +449,9 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
unsigned int val;
dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1,
- MMCM_REG_CLKOUT0_2);
+ MMCM_REG_CLKOUT0_2);
m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1,
- MMCM_REG_CLK_FB2);
+ MMCM_REG_CLK_FB2);
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val);
if (val & MMCM_CLK_DIV_NOCOUNT)
@@ -493,9 +503,57 @@ static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw)
return parent;
}
+static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen,
+ struct device *dev)
+{
+ unsigned int tech, family, speed_grade, reg_value;
+
+ axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_INFO, &reg_value);
+ tech = ADI_AXI_INFO_FPGA_TECH(reg_value);
+ family = ADI_AXI_INFO_FPGA_FAMILY(reg_value);
+ speed_grade = ADI_AXI_INFO_FPGA_SPEED_GRADE(reg_value);
+
+ axi_clkgen->limits.fpfd_min = 10000;
+ axi_clkgen->limits.fvco_min = 600000;
+
+ switch (speed_grade) {
+ case ADI_AXI_FPGA_SPEED_1 ... ADI_AXI_FPGA_SPEED_1LV:
+ axi_clkgen->limits.fvco_max = 1200000;
+ axi_clkgen->limits.fpfd_max = 450000;
+ break;
+ case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2LV:
+ axi_clkgen->limits.fvco_max = 1440000;
+ axi_clkgen->limits.fpfd_max = 500000;
+ if (family == ADI_AXI_FPGA_FAMILY_KINTEX || family == ADI_AXI_FPGA_FAMILY_ARTIX) {
+ axi_clkgen_read(axi_clkgen, ADI_CLKGEN_REG_FPGA_VOLTAGE,
+ &reg_value);
+ if (ADI_CLKGEN_INFO_FPGA_VOLTAGE(reg_value) < 950) {
+ axi_clkgen->limits.fvco_max = 1200000;
+ axi_clkgen->limits.fpfd_max = 450000;
+ }
+ }
+ break;
+ case ADI_AXI_FPGA_SPEED_3:
+ axi_clkgen->limits.fvco_max = 1600000;
+ axi_clkgen->limits.fpfd_max = 550000;
+ break;
+ default:
+ return dev_err_probe(dev, -ENODEV, "Unknown speed grade %d\n",
+ speed_grade);
+ }
+
+ /* Overwrite vco limits for ultrascale+ */
+ if (tech == ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS) {
+ axi_clkgen->limits.fvco_max = 1600000;
+ axi_clkgen->limits.fvco_min = 800000;
+ }
+
+ return 0;
+}
+
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
- .round_rate = axi_clkgen_round_rate,
+ .determine_rate = axi_clkgen_determine_rate,
.set_rate = axi_clkgen_set_rate,
.enable = axi_clkgen_enable,
.disable = axi_clkgen_disable,
@@ -507,9 +565,11 @@ static int axi_clkgen_probe(struct platform_device *pdev)
{
const struct axi_clkgen_limits *dflt_limits;
struct axi_clkgen *axi_clkgen;
+ unsigned int pcore_version;
struct clk_init_data init;
const char *parent_names[2];
const char *clk_name;
+ struct clk *axi_clk;
unsigned int i;
int ret;
@@ -526,8 +586,24 @@ static int axi_clkgen_probe(struct platform_device *pdev)
return PTR_ERR(axi_clkgen->base);
init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
- if (init.num_parents < 1 || init.num_parents > 2)
- return -EINVAL;
+
+ axi_clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
+ if (!IS_ERR(axi_clk)) {
+ if (init.num_parents < 2 || init.num_parents > 3)
+ return -EINVAL;
+
+ init.num_parents -= 1;
+ } else {
+ /*
+ * Legacy... So that old DTs which do not have clock-names still
+ * work. In this case we don't explicitly enable the AXI bus
+ * clock.
+ */
+ if (PTR_ERR(axi_clk) != -ENOENT)
+ return PTR_ERR(axi_clk);
+ if (init.num_parents < 1 || init.num_parents > 2)
+ return -EINVAL;
+ }
for (i = 0; i < init.num_parents; i++) {
parent_names[i] = of_clk_get_parent_name(pdev->dev.of_node, i);
@@ -535,11 +611,20 @@ static int axi_clkgen_probe(struct platform_device *pdev)
return -EINVAL;
}
- memcpy(&axi_clkgen->limits, dflt_limits, sizeof(axi_clkgen->limits));
+ axi_clkgen_read(axi_clkgen, ADI_AXI_REG_VERSION, &pcore_version);
+
+ if (ADI_AXI_PCORE_VER_MAJOR(pcore_version) > 0x04) {
+ ret = axi_clkgen_setup_limits(axi_clkgen, &pdev->dev);
+ if (ret)
+ return ret;
+ } else {
+ memcpy(&axi_clkgen->limits, dflt_limits,
+ sizeof(axi_clkgen->limits));
+ }
clk_name = pdev->dev.of_node->name;
of_property_read_string(pdev->dev.of_node, "clock-output-names",
- &clk_name);
+ &clk_name);
init.name = clk_name;
init.ops = &axi_clkgen_ops;
@@ -553,15 +638,8 @@ static int axi_clkgen_probe(struct platform_device *pdev)
if (ret)
return ret;
- return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_simple_get,
- &axi_clkgen->clk_hw);
-}
-
-static int axi_clkgen_remove(struct platform_device *pdev)
-{
- of_clk_del_provider(pdev->dev.of_node);
-
- return 0;
+ return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
+ &axi_clkgen->clk_hw);
}
static const struct of_device_id axi_clkgen_ids[] = {
@@ -583,7 +661,6 @@ static struct platform_driver axi_clkgen_driver = {
.of_match_table = axi_clkgen_ids,
},
.probe = axi_clkgen_probe,
- .remove = axi_clkgen_remove,
};
module_platform_driver(axi_clkgen_driver);
diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c
index 07e80fe8c310..3823383f3fa6 100644
--- a/drivers/clk/clk-axm5516.c
+++ b/drivers/clk/clk-axm5516.c
@@ -529,7 +529,6 @@ static const struct regmap_config axmclk_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x1fffc,
- .fast_io = true,
};
static const struct of_device_id axmclk_match_table[] = {
@@ -541,14 +540,12 @@ MODULE_DEVICE_TABLE(of, axmclk_match_table);
static int axmclk_probe(struct platform_device *pdev)
{
void __iomem *base;
- struct resource *res;
int i, ret;
struct device *dev = &pdev->dev;
struct regmap *regmap;
size_t num_clks;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -569,18 +566,11 @@ static int axmclk_probe(struct platform_device *pdev)
return ret;
}
- return of_clk_add_hw_provider(dev->of_node, of_clk_axmclk_get, NULL);
-}
-
-static int axmclk_remove(struct platform_device *pdev)
-{
- of_clk_del_provider(pdev->dev.of_node);
- return 0;
+ return devm_of_clk_add_hw_provider(dev, of_clk_axmclk_get, NULL);
}
static struct platform_driver axmclk_driver = {
.probe = axmclk_probe,
- .remove = axmclk_remove,
.driver = {
.name = "clk-axm5516",
.of_match_table = axmclk_match_table,
diff --git a/drivers/clk/clk-bm1880.c b/drivers/clk/clk-bm1880.c
index fad78a22218e..dac190bc6e19 100644
--- a/drivers/clk/clk-bm1880.c
+++ b/drivers/clk/clk-bm1880.c
@@ -7,10 +7,10 @@
*/
#include <linux/clk-provider.h>
+#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -608,8 +608,8 @@ static unsigned long bm1880_clk_div_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long bm1880_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int bm1880_clk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw);
struct bm1880_div_clock *div = &div_hw->div;
@@ -621,13 +621,18 @@ static long bm1880_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
val = readl(reg_addr) >> div->shift;
val &= clk_div_mask(div->width);
- return divider_ro_round_rate(hw, rate, prate, div->table,
- div->width, div->flags,
- val);
+ req->rate = divider_ro_round_rate(hw, req->rate,
+ &req->best_parent_rate,
+ div->table,
+ div->width, div->flags, val);
+
+ return 0;
}
- return divider_round_rate(hw, rate, prate, div->table,
- div->width, div->flags);
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate,
+ div->table, div->width, div->flags);
+
+ return 0;
}
static int bm1880_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -665,7 +670,7 @@ static int bm1880_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops bm1880_clk_div_ops = {
.recalc_rate = bm1880_clk_div_recalc_rate,
- .round_rate = bm1880_clk_div_round_rate,
+ .determine_rate = bm1880_clk_div_determine_rate,
.set_rate = bm1880_clk_div_set_rate,
};
@@ -876,16 +881,13 @@ static int bm1880_clk_probe(struct platform_device *pdev)
struct bm1880_clock_data *clk_data;
void __iomem *pll_base, *sys_base;
struct device *dev = &pdev->dev;
- struct resource *res;
int num_clks, i;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pll_base = devm_ioremap_resource(&pdev->dev, res);
+ pll_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pll_base))
return PTR_ERR(pll_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- sys_base = devm_ioremap_resource(&pdev->dev, res);
+ sys_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(sys_base))
return PTR_ERR(sys_base);
@@ -949,4 +951,3 @@ module_platform_driver(bm1880_clk_driver);
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_DESCRIPTION("Clock driver for Bitmain BM1880 SoC");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c
index e9e16425c739..826b3ff99433 100644
--- a/drivers/clk/clk-bulk.c
+++ b/drivers/clk/clk-bulk.c
@@ -96,9 +96,9 @@ static int __clk_bulk_get(struct device *dev, int num_clks,
if (ret == -ENOENT && optional)
continue;
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get clk '%s': %d\n",
- clks[i].id, ret);
+ dev_err_probe(dev, ret,
+ "Failed to get clk '%s'\n",
+ clks[i].id);
goto err;
}
}
diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c
index 5467d941ddfd..a495d313b02f 100644
--- a/drivers/clk/clk-cdce706.c
+++ b/drivers/clk/clk-cdce706.c
@@ -155,6 +155,7 @@ static u8 cdce706_clkin_get_parent(struct clk_hw *hw)
}
static const struct clk_ops cdce706_clkin_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = cdce706_clkin_set_parent,
.get_parent = cdce706_clkin_get_parent,
};
@@ -182,8 +183,8 @@ static unsigned long cdce706_pll_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long cdce706_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cdce706_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
unsigned long mul, div;
@@ -191,9 +192,9 @@ static long cdce706_pll_round_rate(struct clk_hw *hw, unsigned long rate,
dev_dbg(&hwd->dev_data->client->dev,
"%s, rate: %lu, parent_rate: %lu\n",
- __func__, rate, *parent_rate);
+ __func__, req->rate, req->best_parent_rate);
- rational_best_approximation(rate, *parent_rate,
+ rational_best_approximation(req->rate, req->best_parent_rate,
CDCE706_PLL_N_MAX, CDCE706_PLL_M_MAX,
&mul, &div);
hwd->mul = mul;
@@ -203,9 +204,11 @@ static long cdce706_pll_round_rate(struct clk_hw *hw, unsigned long rate,
"%s, pll: %d, mul: %lu, div: %lu\n",
__func__, hwd->idx, mul, div);
- res = (u64)*parent_rate * hwd->mul;
+ res = (u64)req->best_parent_rate * hwd->mul;
do_div(res, hwd->div);
- return res;
+ req->rate = res;
+
+ return 0;
}
static int cdce706_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -250,7 +253,7 @@ static int cdce706_pll_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops cdce706_pll_ops = {
.recalc_rate = cdce706_pll_recalc_rate,
- .round_rate = cdce706_pll_round_rate,
+ .determine_rate = cdce706_pll_determine_rate,
.set_rate = cdce706_pll_set_rate,
};
@@ -287,18 +290,19 @@ static unsigned long cdce706_divider_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cdce706_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
struct cdce706_dev_data *cdce = hwd->dev_data;
+ unsigned long rate = req->rate;
unsigned long mul, div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, rate: %lu, parent_rate: %lu\n",
- __func__, rate, *parent_rate);
+ __func__, rate, req->best_parent_rate);
- rational_best_approximation(rate, *parent_rate,
+ rational_best_approximation(rate, req->best_parent_rate,
1, CDCE706_DIVIDER_DIVIDER_MAX,
&mul, &div);
if (!mul)
@@ -343,8 +347,8 @@ static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
dev_dbg(&hwd->dev_data->client->dev,
"%s, altering parent rate: %lu -> %lu\n",
- __func__, *parent_rate, rate * div);
- *parent_rate = rate * div;
+ __func__, req->best_parent_rate, rate * div);
+ req->best_parent_rate = rate * div;
}
hwd->div = div;
@@ -352,7 +356,8 @@ static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
"%s, divider: %d, div: %lu\n",
__func__, hwd->idx, div);
- return *parent_rate / div;
+ req->rate = req->best_parent_rate / div;
+ return 0;
}
static int cdce706_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -374,7 +379,7 @@ static const struct clk_ops cdce706_divider_ops = {
.set_parent = cdce706_divider_set_parent,
.get_parent = cdce706_divider_get_parent,
.recalc_rate = cdce706_divider_recalc_rate,
- .round_rate = cdce706_divider_round_rate,
+ .determine_rate = cdce706_divider_determine_rate,
.set_rate = cdce706_divider_set_rate,
};
@@ -420,11 +425,12 @@ static unsigned long cdce706_clkout_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long cdce706_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cdce706_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- *parent_rate = rate;
- return rate;
+ req->best_parent_rate = req->rate;
+
+ return 0;
}
static int cdce706_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -439,7 +445,7 @@ static const struct clk_ops cdce706_clkout_ops = {
.set_parent = cdce706_clkout_set_parent,
.get_parent = cdce706_clkout_get_parent,
.recalc_rate = cdce706_clkout_recalc_rate,
- .round_rate = cdce706_clkout_round_rate,
+ .determine_rate = cdce706_clkout_determine_rate,
.set_rate = cdce706_clkout_set_rate,
};
@@ -661,17 +667,10 @@ static int cdce706_probe(struct i2c_client *client)
ret = cdce706_register_clkouts(cdce);
if (ret < 0)
return ret;
- return of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce_get,
- cdce);
+ return devm_of_clk_add_hw_provider(&client->dev, of_clk_cdce_get,
+ cdce);
}
-static int cdce706_remove(struct i2c_client *client)
-{
- of_clk_del_provider(client->dev.of_node);
- return 0;
-}
-
-
#ifdef CONFIG_OF
static const struct of_device_id cdce706_dt_match[] = {
{ .compatible = "ti,cdce706" },
@@ -681,7 +680,7 @@ MODULE_DEVICE_TABLE(of, cdce706_dt_match);
#endif
static const struct i2c_device_id cdce706_id[] = {
- { "cdce706", 0 },
+ { "cdce706" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cdce706_id);
@@ -691,8 +690,7 @@ static struct i2c_driver cdce706_i2c_driver = {
.name = "cdce706",
.of_match_table = of_match_ptr(cdce706_dt_match),
},
- .probe_new = cdce706_probe,
- .remove = cdce706_remove,
+ .probe = cdce706_probe,
.id_table = cdce706_id,
};
module_i2c_driver(cdce706_i2c_driver);
diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c
index ef9a2d44e40c..0b2ad21e6e4d 100644
--- a/drivers/clk/clk-cdce925.c
+++ b/drivers/clk/clk-cdce925.c
@@ -25,25 +25,11 @@
* Model this as 2 PLL clocks which are parents to the outputs.
*/
-enum {
- CDCE913,
- CDCE925,
- CDCE937,
- CDCE949,
-};
-
struct clk_cdce925_chip_info {
int num_plls;
int num_outputs;
};
-static const struct clk_cdce925_chip_info clk_cdce925_chip_info_tbl[] = {
- [CDCE913] = { .num_plls = 1, .num_outputs = 3 },
- [CDCE925] = { .num_plls = 2, .num_outputs = 5 },
- [CDCE937] = { .num_plls = 3, .num_outputs = 7 },
- [CDCE949] = { .num_plls = 4, .num_outputs = 9 },
-};
-
#define MAX_NUMBER_OF_PLLS 4
#define MAX_NUMBER_OF_OUTPUTS 9
@@ -115,7 +101,6 @@ static void cdce925_pll_find_rate(unsigned long rate,
if (rate <= parent_rate) {
/* Can always deliver parent_rate in bypass mode */
- rate = parent_rate;
*n = 0;
*m = 0;
} else {
@@ -143,13 +128,15 @@ static void cdce925_pll_find_rate(unsigned long rate,
}
}
-static long cdce925_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cdce925_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
u16 n, m;
- cdce925_pll_find_rate(rate, *parent_rate, &n, &m);
- return (long)cdce925_pll_calculate_rate(*parent_rate, n, m);
+ cdce925_pll_find_rate(req->rate, req->best_parent_rate, &n, &m);
+ req->rate = (long)cdce925_pll_calculate_rate(req->best_parent_rate, n, m);
+
+ return 0;
}
static int cdce925_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -281,7 +268,7 @@ static const struct clk_ops cdce925_pll_ops = {
.prepare = cdce925_pll_prepare,
.unprepare = cdce925_pll_unprepare,
.recalc_rate = cdce925_pll_recalc_rate,
- .round_rate = cdce925_pll_round_rate,
+ .determine_rate = cdce925_pll_determine_rate,
.set_rate = cdce925_pll_set_rate,
};
@@ -435,20 +422,23 @@ static unsigned long cdce925_clk_best_parent_rate(
return rate * pdiv_best;
}
-static long cdce925_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cdce925_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long l_parent_rate = *parent_rate;
- u16 divider = cdce925_calc_divider(rate, l_parent_rate);
+ unsigned long l_parent_rate = req->best_parent_rate;
+ u16 divider = cdce925_calc_divider(req->rate, l_parent_rate);
- if (l_parent_rate / divider != rate) {
- l_parent_rate = cdce925_clk_best_parent_rate(hw, rate);
- divider = cdce925_calc_divider(rate, l_parent_rate);
- *parent_rate = l_parent_rate;
+ if (l_parent_rate / divider != req->rate) {
+ l_parent_rate = cdce925_clk_best_parent_rate(hw, req->rate);
+ divider = cdce925_calc_divider(req->rate, l_parent_rate);
+ req->best_parent_rate = l_parent_rate;
}
if (divider)
- return (long)(l_parent_rate / divider);
+ req->rate = (long)(l_parent_rate / divider);
+ else
+ req->rate = 0;
+
return 0;
}
@@ -466,7 +456,7 @@ static const struct clk_ops cdce925_clk_ops = {
.prepare = cdce925_clk_prepare,
.unprepare = cdce925_clk_unprepare,
.recalc_rate = cdce925_clk_recalc_rate,
- .round_rate = cdce925_clk_round_rate,
+ .determine_rate = cdce925_clk_determine_rate,
.set_rate = cdce925_clk_set_rate,
};
@@ -488,14 +478,17 @@ static u16 cdce925_y1_calc_divider(unsigned long rate,
return (u16)divider;
}
-static long cdce925_clk_y1_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cdce925_clk_y1_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long l_parent_rate = *parent_rate;
- u16 divider = cdce925_y1_calc_divider(rate, l_parent_rate);
+ unsigned long l_parent_rate = req->best_parent_rate;
+ u16 divider = cdce925_y1_calc_divider(req->rate, l_parent_rate);
if (divider)
- return (long)(l_parent_rate / divider);
+ req->rate = (long)(l_parent_rate / divider);
+ else
+ req->rate = 0;
+
return 0;
}
@@ -513,7 +506,7 @@ static const struct clk_ops cdce925_clk_y1_ops = {
.prepare = cdce925_clk_prepare,
.unprepare = cdce925_clk_unprepare,
.recalc_rate = cdce925_clk_recalc_rate,
- .round_rate = cdce925_clk_y1_round_rate,
+ .determine_rate = cdce925_clk_y1_determine_rate,
.set_rate = cdce925_clk_y1_set_rate,
};
@@ -603,51 +596,28 @@ of_clk_cdce925_get(struct of_phandle_args *clkspec, void *_data)
return &data->clk[idx].hw;
}
-static void cdce925_regulator_disable(void *regulator)
-{
- regulator_disable(regulator);
-}
-
static int cdce925_regulator_enable(struct device *dev, const char *name)
{
- struct regulator *regulator;
int err;
- regulator = devm_regulator_get(dev, name);
- if (IS_ERR(regulator))
- return PTR_ERR(regulator);
-
- err = regulator_enable(regulator);
- if (err) {
- dev_err(dev, "Failed to enable %s: %d\n", name, err);
- return err;
- }
+ err = devm_regulator_get_enable(dev, name);
+ if (err)
+ dev_err_probe(dev, err, "Failed to enable %s:\n", name);
- return devm_add_action_or_reset(dev, cdce925_regulator_disable,
- regulator);
+ return err;
}
/* The CDCE925 uses a funky way to read/write registers. Bulk mode is
* just weird, so just use the single byte mode exclusively. */
-static struct regmap_bus regmap_cdce925_bus = {
+static const struct regmap_bus regmap_cdce925_bus = {
.write = cdce925_regmap_i2c_write,
.read = cdce925_regmap_i2c_read,
};
-static const struct i2c_device_id cdce925_id[] = {
- { "cdce913", CDCE913 },
- { "cdce925", CDCE925 },
- { "cdce937", CDCE937 },
- { "cdce949", CDCE949 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, cdce925_id);
-
static int cdce925_probe(struct i2c_client *client)
{
struct clk_cdce925_chip *data;
struct device_node *node = client->dev.of_node;
- const struct i2c_device_id *id = i2c_match_id(cdce925_id, client);
const char *parent_name;
const char *pll_clk_name[MAX_NUMBER_OF_PLLS] = {NULL,};
struct clk_init_data init;
@@ -660,7 +630,7 @@ static int cdce925_probe(struct i2c_client *client)
.name = "configuration0",
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
dev_dbg(&client->dev, "%s\n", __func__);
@@ -678,7 +648,7 @@ static int cdce925_probe(struct i2c_client *client)
return -ENOMEM;
data->i2c_client = client;
- data->chip_info = &clk_cdce925_chip_info_tbl[id->driver_data];
+ data->chip_info = i2c_get_match_data(client);
config.max_register = CDCE925_OFFSET_PLL +
data->chip_info->num_plls * 0x10 - 1;
data->regmap = devm_regmap_init(&client->dev, &regmap_cdce925_bus,
@@ -714,6 +684,10 @@ static int cdce925_probe(struct i2c_client *client)
for (i = 0; i < data->chip_info->num_plls; ++i) {
pll_clk_name[i] = kasprintf(GFP_KERNEL, "%pOFn.pll%d",
client->dev.of_node, i);
+ if (!pll_clk_name[i]) {
+ err = -ENOMEM;
+ goto error;
+ }
init.name = pll_clk_name[i];
data->pll[i].chip = data;
data->pll[i].hw.init = &init;
@@ -755,6 +729,10 @@ static int cdce925_probe(struct i2c_client *client)
init.num_parents = 1;
init.parent_names = &parent_name; /* Mux Y1 to input */
init.name = kasprintf(GFP_KERNEL, "%pOFn.Y1", client->dev.of_node);
+ if (!init.name) {
+ err = -ENOMEM;
+ goto error;
+ }
data->clk[0].chip = data;
data->clk[0].hw.init = &init;
data->clk[0].index = 0;
@@ -773,6 +751,10 @@ static int cdce925_probe(struct i2c_client *client)
for (i = 1; i < data->chip_info->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%pOFn.Y%d",
client->dev.of_node, i+1);
+ if (!init.name) {
+ err = -ENOMEM;
+ goto error;
+ }
data->clk[i].chip = data;
data->clk[i].hw.init = &init;
data->clk[i].index = i;
@@ -823,21 +805,50 @@ error:
return err;
}
+static const struct clk_cdce925_chip_info clk_cdce913_info = {
+ .num_plls = 1,
+ .num_outputs = 3,
+};
+
+static const struct clk_cdce925_chip_info clk_cdce925_info = {
+ .num_plls = 2,
+ .num_outputs = 5,
+};
+
+static const struct clk_cdce925_chip_info clk_cdce937_info = {
+ .num_plls = 3,
+ .num_outputs = 7,
+};
+
+static const struct clk_cdce925_chip_info clk_cdce949_info = {
+ .num_plls = 4,
+ .num_outputs = 9,
+};
+
+static const struct i2c_device_id cdce925_id[] = {
+ { "cdce913", (kernel_ulong_t)&clk_cdce913_info },
+ { "cdce925", (kernel_ulong_t)&clk_cdce925_info },
+ { "cdce937", (kernel_ulong_t)&clk_cdce937_info },
+ { "cdce949", (kernel_ulong_t)&clk_cdce949_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cdce925_id);
+
static const struct of_device_id clk_cdce925_of_match[] = {
- { .compatible = "ti,cdce913" },
- { .compatible = "ti,cdce925" },
- { .compatible = "ti,cdce937" },
- { .compatible = "ti,cdce949" },
- { },
+ { .compatible = "ti,cdce913", .data = &clk_cdce913_info },
+ { .compatible = "ti,cdce925", .data = &clk_cdce925_info },
+ { .compatible = "ti,cdce937", .data = &clk_cdce937_info },
+ { .compatible = "ti,cdce949", .data = &clk_cdce949_info },
+ { }
};
MODULE_DEVICE_TABLE(of, clk_cdce925_of_match);
static struct i2c_driver cdce925_driver = {
.driver = {
.name = "cdce925",
- .of_match_table = of_match_ptr(clk_cdce925_of_match),
+ .of_match_table = clk_cdce925_of_match,
},
- .probe_new = cdce925_probe,
+ .probe = cdce925_probe,
.id_table = cdce925_id,
};
module_i2c_driver(cdce925_driver);
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c
index f8417ee2961a..402ab74d9bfb 100644
--- a/drivers/clk/clk-clps711x.c
+++ b/drivers/clk/clk-clps711x.c
@@ -99,7 +99,7 @@ static void __init clps711x_clk_init_dt(struct device_node *np)
*/
tmp &= ~(SYSCON1_TC1M | SYSCON1_TC1S);
/* Timer2 in prescale mode.
- * Value writen is automatically re-loaded when
+ * Value written is automatically re-loaded when
* the counter underflows.
*/
tmp |= SYSCON1_TC2M | SYSCON1_TC2S;
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index b9c5f904f535..66759fe28fad 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -85,10 +85,11 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
req->best_parent_hw = NULL;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
- struct clk_rate_request tmp_req = *req;
+ struct clk_rate_request tmp_req;
parent = clk_hw_get_parent(mux_hw);
+ clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate);
ret = clk_composite_determine_rate_for_parent(rate_hw,
&tmp_req,
parent,
@@ -104,12 +105,13 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
}
for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) {
- struct clk_rate_request tmp_req = *req;
+ struct clk_rate_request tmp_req;
parent = clk_hw_get_parent_by_index(mux_hw, i);
if (!parent)
continue;
+ clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate);
ret = clk_composite_determine_rate_for_parent(rate_hw,
&tmp_req,
parent,
@@ -117,7 +119,10 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
if (ret)
continue;
- rate_diff = abs(req->rate - tmp_req.rate);
+ if (req->rate >= tmp_req.rate)
+ rate_diff = req->rate - tmp_req.rate;
+ else
+ rate_diff = tmp_req.rate - req->rate;
if (!rate_diff || !req->best_parent_hw
|| best_rate_diff > rate_diff) {
diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c
index 2ef819606c41..303a0bb26e54 100644
--- a/drivers/clk/clk-conf.c
+++ b/drivers/clk/clk-conf.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/of.h>
#include <linux/printk.h>
+#include <linux/slab.h>
static int __set_clk_parents(struct device_node *node, bool clk_supplier)
{
@@ -33,9 +34,12 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
else
return rc;
}
- if (clkspec.np == node && !clk_supplier)
+ if (clkspec.np == node && !clk_supplier) {
+ of_node_put(clkspec.np);
return 0;
+ }
pclk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
if (IS_ERR(pclk)) {
if (PTR_ERR(pclk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get parent clock %d for %pOF\n",
@@ -48,10 +52,12 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier)
if (rc < 0)
goto err;
if (clkspec.np == node && !clk_supplier) {
+ of_node_put(clkspec.np);
rc = 0;
goto err;
}
clk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get assigned clock %d for %pOF\n",
@@ -76,13 +82,44 @@ err:
static int __set_clk_rates(struct device_node *node, bool clk_supplier)
{
struct of_phandle_args clkspec;
- struct property *prop;
- const __be32 *cur;
- int rc, index = 0;
+ int rc, count, count_64, index;
struct clk *clk;
- u32 rate;
+ u64 *rates_64 __free(kfree) = NULL;
+ u32 *rates __free(kfree) = NULL;
+
+ count = of_property_count_u32_elems(node, "assigned-clock-rates");
+ count_64 = of_property_count_u64_elems(node, "assigned-clock-rates-u64");
+ if (count_64 > 0) {
+ count = count_64;
+ rates_64 = kcalloc(count, sizeof(*rates_64), GFP_KERNEL);
+ if (!rates_64)
+ return -ENOMEM;
+
+ rc = of_property_read_u64_array(node,
+ "assigned-clock-rates-u64",
+ rates_64, count);
+ } else if (count > 0) {
+ rates = kcalloc(count, sizeof(*rates), GFP_KERNEL);
+ if (!rates)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(node, "assigned-clock-rates",
+ rates, count);
+ } else {
+ return 0;
+ }
+
+ if (rc)
+ return rc;
+
+ for (index = 0; index < count; index++) {
+ unsigned long rate;
+
+ if (rates_64)
+ rate = rates_64[index];
+ else
+ rate = rates[index];
- of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) {
if (rate) {
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
@@ -93,10 +130,13 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
else
return rc;
}
- if (clkspec.np == node && !clk_supplier)
+ if (clkspec.np == node && !clk_supplier) {
+ of_node_put(clkspec.np);
return 0;
+ }
clk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get clock %d for %pOF\n",
@@ -106,12 +146,11 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
rc = clk_set_rate(clk, rate);
if (rc < 0)
- pr_err("clk: couldn't set %s clk rate to %u (%d), current rate: %lu\n",
+ pr_err("clk: couldn't set %s clk rate to %lu (%d), current rate: %lu\n",
__clk_get_name(clk), rate, rc,
clk_get_rate(clk));
clk_put(clk);
}
- index++;
}
return 0;
}
diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index aa5c72bab83e..8800472ba63f 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -9,7 +9,7 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -305,15 +305,19 @@ static unsigned long cs2000_recalc_rate(struct clk_hw *hw,
return cs2000_ratio_to_rate(ratio, parent_rate, priv->lf_ratio);
}
-static long cs2000_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int cs2000_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct cs2000_priv *priv = hw_to_priv(hw);
u32 ratio;
- ratio = cs2000_rate_to_ratio(*parent_rate, rate, priv->lf_ratio);
+ ratio = cs2000_rate_to_ratio(req->best_parent_rate, req->rate,
+ priv->lf_ratio);
- return cs2000_ratio_to_rate(ratio, *parent_rate, priv->lf_ratio);
+ req->rate = cs2000_ratio_to_rate(ratio, req->best_parent_rate,
+ priv->lf_ratio);
+
+ return 0;
}
static int cs2000_select_ratio_mode(struct cs2000_priv *priv,
@@ -430,7 +434,7 @@ static u8 cs2000_get_parent(struct clk_hw *hw)
static const struct clk_ops cs2000_ops = {
.get_parent = cs2000_get_parent,
.recalc_rate = cs2000_recalc_rate,
- .round_rate = cs2000_round_rate,
+ .determine_rate = cs2000_determine_rate,
.set_rate = cs2000_set_rate,
.prepare = cs2000_enable,
.unprepare = cs2000_disable,
@@ -557,7 +561,7 @@ static int cs2000_version_print(struct cs2000_priv *priv)
return 0;
}
-static int cs2000_remove(struct i2c_client *client)
+static void cs2000_remove(struct i2c_client *client)
{
struct cs2000_priv *priv = i2c_get_clientdata(client);
struct device *dev = priv_to_dev(priv);
@@ -566,8 +570,6 @@ static int cs2000_remove(struct i2c_client *client)
of_clk_del_provider(np);
clk_hw_unregister(&priv->hw);
-
- return 0;
}
static int cs2000_probe(struct i2c_client *client)
@@ -624,7 +626,7 @@ static struct i2c_driver cs2000_driver = {
.pm = &cs2000_pm_ops,
.of_match_table = cs2000_of_match,
},
- .probe_new = cs2000_probe,
+ .probe = cs2000_probe,
.remove = cs2000_remove,
.id_table = cs2000_id,
};
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
index 4fb4fd4b06bd..5368d92d9b39 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -99,6 +99,34 @@ struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id)
}
EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled);
+struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev,
+ const char *id,
+ unsigned long rate)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = __devm_clk_get(dev, id, clk_get_optional, NULL,
+ clk_disable_unprepare);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+
+ ret = clk_set_rate(clk, rate);
+ if (ret)
+ goto out_put_clk;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_put_clk;
+
+ return clk;
+
+out_put_clk:
+ devm_clk_put(dev, clk);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled_with_rate);
+
struct clk_bulk_devres {
struct clk_bulk_data *clks;
int num_clks;
@@ -182,6 +210,47 @@ int __must_check devm_clk_bulk_get_all(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all);
+static void devm_clk_bulk_release_all_enable(struct device *dev, void *res)
+{
+ struct clk_bulk_devres *devres = res;
+
+ clk_bulk_disable_unprepare(devres->num_clks, devres->clks);
+ clk_bulk_put_all(devres->num_clks, devres->clks);
+}
+
+int __must_check devm_clk_bulk_get_all_enabled(struct device *dev,
+ struct clk_bulk_data **clks)
+{
+ struct clk_bulk_devres *devres;
+ int ret;
+
+ devres = devres_alloc(devm_clk_bulk_release_all_enable,
+ sizeof(*devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = clk_bulk_get_all(dev, &devres->clks);
+ if (ret > 0) {
+ *clks = devres->clks;
+ devres->num_clks = ret;
+ } else {
+ devres_free(devres);
+ return ret;
+ }
+
+ ret = clk_bulk_prepare_enable(devres->num_clks, *clks);
+ if (!ret) {
+ devres_add(dev, devres);
+ } else {
+ clk_bulk_put_all(devres->num_clks, devres->clks);
+ devres_free(devres);
+ return ret;
+ }
+
+ return devres->num_clks;
+}
+EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all_enabled);
+
static int devm_clk_match(struct device *dev, void *res, void *data)
{
struct clk **c = res;
@@ -205,18 +274,19 @@ EXPORT_SYMBOL(devm_clk_put);
struct clk *devm_get_clk_from_child(struct device *dev,
struct device_node *np, const char *con_id)
{
- struct clk **ptr, *clk;
+ struct devm_clk_state *state;
+ struct clk *clk;
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
+ state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL);
+ if (!state)
return ERR_PTR(-ENOMEM);
clk = of_clk_get_by_name(np, con_id);
if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
+ state->clk = clk;
+ devres_add(dev, state);
} else {
- devres_free(ptr);
+ devres_free(state);
}
return clk;
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index f6b2bf558486..2601b6155afb 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -72,6 +72,8 @@ static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
return clk_div_mask(width);
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << clk_div_mask(width);
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS)
+ return 2 * (clk_div_mask(width) + 1);
if (table)
return _get_table_maxdiv(table, width);
return clk_div_mask(width) + 1;
@@ -97,6 +99,8 @@ static unsigned int _get_div(const struct clk_div_table *table,
return 1 << val;
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return val ? val : clk_div_mask(width) + 1;
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS)
+ return 2 * (val + 1);
if (table)
return _get_table_div(table, val);
return val + 1;
@@ -122,6 +126,8 @@ static unsigned int _get_val(const struct clk_div_table *table,
return __ffs(div);
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
return (div == clk_div_mask(width) + 1) ? 0 : div;
+ if (flags & CLK_DIVIDER_EVEN_INTEGERS)
+ return (div >> 1) - 1;
if (table)
return _get_table_val(table, div);
return div - 1;
@@ -386,13 +392,13 @@ long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
const struct clk_div_table *table,
u8 width, unsigned long flags)
{
- struct clk_rate_request req = {
- .rate = rate,
- .best_parent_rate = *prate,
- .best_parent_hw = parent,
- };
+ struct clk_rate_request req;
int ret;
+ clk_hw_init_rate_request(hw, &req, rate);
+ req.best_parent_rate = *prate;
+ req.best_parent_hw = parent;
+
ret = divider_determine_rate(hw, &req, table, width, flags);
if (ret)
return ret;
@@ -408,13 +414,13 @@ long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
const struct clk_div_table *table, u8 width,
unsigned long flags, unsigned int val)
{
- struct clk_rate_request req = {
- .rate = rate,
- .best_parent_rate = *prate,
- .best_parent_hw = parent,
- };
+ struct clk_rate_request req;
int ret;
+ clk_hw_init_rate_request(hw, &req, rate);
+ req.best_parent_rate = *prate;
+ req.best_parent_hw = parent;
+
ret = divider_ro_determine_rate(hw, &req, table, width, flags, val);
if (ret)
return ret;
@@ -425,27 +431,6 @@ long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
}
EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent);
-static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
-{
- struct clk_divider *divider = to_clk_divider(hw);
-
- /* if read only, just return current value */
- if (divider->flags & CLK_DIVIDER_READ_ONLY) {
- u32 val;
-
- val = clk_div_readl(divider) >> divider->shift;
- val &= clk_div_mask(divider->width);
-
- return divider_ro_round_rate(hw, rate, prate, divider->table,
- divider->width, divider->flags,
- val);
- }
-
- return divider_round_rate(hw, rate, prate, divider->table,
- divider->width, divider->flags);
-}
-
static int clk_divider_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
@@ -521,7 +506,6 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
const struct clk_ops clk_divider_ops = {
.recalc_rate = clk_divider_recalc_rate,
- .round_rate = clk_divider_round_rate,
.determine_rate = clk_divider_determine_rate,
.set_rate = clk_divider_set_rate,
};
@@ -529,7 +513,6 @@ EXPORT_SYMBOL_GPL(clk_divider_ops);
const struct clk_ops clk_divider_ro_ops = {
.recalc_rate = clk_divider_recalc_rate,
- .round_rate = clk_divider_round_rate,
.determine_rate = clk_divider_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_divider_ro_ops);
@@ -538,7 +521,8 @@ struct clk_hw *__clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width,
+ unsigned long clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_divider *div;
@@ -610,8 +594,8 @@ EXPORT_SYMBOL_GPL(__clk_hw_register_divider);
struct clk *clk_register_divider_table(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
- spinlock_t *lock)
+ unsigned long clk_divider_flags,
+ const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_hw *hw;
@@ -664,7 +648,8 @@ struct clk_hw *__devm_clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width,
+ unsigned long clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_hw **ptr, *hw;
diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c
index 29f0126cbd05..08cc8e5acf43 100644
--- a/drivers/clk/clk-en7523.c
+++ b/drivers/clk/clk-en7523.c
@@ -2,16 +2,24 @@
#include <linux/delay.h>
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
#include <dt-bindings/clock/en7523-clk.h>
+#include <dt-bindings/reset/airoha,en7523-reset.h>
+#include <dt-bindings/reset/airoha,en7581-reset.h>
+
+#define RST_NR_PER_BANK 32
#define REG_PCI_CONTROL 0x88
#define REG_PCI_CONTROL_PERSTOUT BIT(29)
#define REG_PCI_CONTROL_PERSTOUT1 BIT(26)
+#define REG_PCI_CONTROL_REFCLK_EN0 BIT(23)
#define REG_PCI_CONTROL_REFCLK_EN1 BIT(22)
+#define REG_PCI_CONTROL_PERSTOUT2 BIT(16)
#define REG_GSW_CLK_DIV_SEL 0x1b4
#define REG_EMI_CLK_DIV_SEL 0x1b8
#define REG_BUS_CLK_DIV_SEL 0x1bc
@@ -19,10 +27,21 @@
#define REG_SPI_CLK_FREQ_SEL 0x1c8
#define REG_NPU_CLK_DIV_SEL 0x1fc
#define REG_CRYPTO_CLKSRC 0x200
-#define REG_RESET_CONTROL 0x834
+#define REG_RESET_CONTROL2 0x830
+#define REG_RESET2_CONTROL_PCIE2 BIT(27)
+#define REG_RESET_CONTROL1 0x834
#define REG_RESET_CONTROL_PCIEHB BIT(29)
#define REG_RESET_CONTROL_PCIE1 BIT(27)
#define REG_RESET_CONTROL_PCIE2 BIT(26)
+/* EN7581 */
+#define REG_NP_SCU_PCIC 0x88
+#define REG_NP_SCU_SSTR 0x9c
+#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13)
+#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11)
+#define REG_CRYPTO_CLKSRC2 0x20c
+
+#define REG_RST_CTRL2 0x830
+#define REG_RST_CTRL1 0x834
struct en_clk_desc {
int id;
@@ -41,6 +60,7 @@ struct en_clk_desc {
u8 div_shift;
u16 div_val0;
u8 div_step;
+ u8 div_offset;
};
struct en_clk_gate {
@@ -48,11 +68,31 @@ struct en_clk_gate {
struct clk_hw hw;
};
+struct en_rst_data {
+ const u16 *bank_ofs;
+ const u16 *idx_map;
+ void __iomem *base;
+ struct reset_controller_dev rcdev;
+};
+
+struct en_clk_soc_data {
+ u32 num_clocks;
+ const struct clk_ops pcie_ops;
+ int (*hw_init)(struct platform_device *pdev,
+ struct clk_hw_onecell_data *clk_data);
+};
+
static const u32 gsw_base[] = { 400000000, 500000000 };
static const u32 emi_base[] = { 333000000, 400000000 };
static const u32 bus_base[] = { 500000000, 540000000 };
static const u32 slic_base[] = { 100000000, 3125000 };
static const u32 npu_base[] = { 333000000, 400000000, 500000000 };
+/* EN7581 */
+static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 };
+static const u32 bus7581_base[] = { 600000000, 540000000 };
+static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 };
+static const u32 crypto_base[] = { 540000000, 480000000 };
+static const u32 emmc7581_base[] = { 200000000, 150000000 };
static const struct en_clk_desc en7523_base_clks[] = {
{
@@ -68,6 +108,7 @@ static const struct en_clk_desc en7523_base_clks[] = {
.div_bits = 3,
.div_shift = 0,
.div_step = 1,
+ .div_offset = 1,
}, {
.id = EN7523_CLK_EMI,
.name = "emi",
@@ -81,6 +122,7 @@ static const struct en_clk_desc en7523_base_clks[] = {
.div_bits = 3,
.div_shift = 0,
.div_step = 1,
+ .div_offset = 1,
}, {
.id = EN7523_CLK_BUS,
.name = "bus",
@@ -94,6 +136,7 @@ static const struct en_clk_desc en7523_base_clks[] = {
.div_bits = 3,
.div_shift = 0,
.div_step = 1,
+ .div_offset = 1,
}, {
.id = EN7523_CLK_SLIC,
.name = "slic",
@@ -134,32 +177,242 @@ static const struct en_clk_desc en7523_base_clks[] = {
.div_bits = 3,
.div_shift = 0,
.div_step = 1,
+ .div_offset = 1,
}, {
.id = EN7523_CLK_CRYPTO,
.name = "crypto",
.base_reg = REG_CRYPTO_CLKSRC,
.base_bits = 1,
- .base_shift = 8,
+ .base_shift = 0,
.base_values = emi_base,
.n_base_values = ARRAY_SIZE(emi_base),
}
};
-static const struct of_device_id of_match_clk_en7523[] = {
- { .compatible = "airoha,en7523-scu", },
- { /* sentinel */ }
+static const struct en_clk_desc en7581_base_clks[] = {
+ {
+ .id = EN7523_CLK_GSW,
+ .name = "gsw",
+
+ .base_reg = REG_GSW_CLK_DIV_SEL,
+ .base_bits = 1,
+ .base_shift = 8,
+ .base_values = gsw_base,
+ .n_base_values = ARRAY_SIZE(gsw_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ }, {
+ .id = EN7523_CLK_EMI,
+ .name = "emi",
+
+ .base_reg = REG_EMI_CLK_DIV_SEL,
+ .base_bits = 2,
+ .base_shift = 8,
+ .base_values = emi7581_base,
+ .n_base_values = ARRAY_SIZE(emi7581_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ }, {
+ .id = EN7523_CLK_BUS,
+ .name = "bus",
+
+ .base_reg = REG_BUS_CLK_DIV_SEL,
+ .base_bits = 1,
+ .base_shift = 8,
+ .base_values = bus7581_base,
+ .n_base_values = ARRAY_SIZE(bus7581_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ }, {
+ .id = EN7523_CLK_SLIC,
+ .name = "slic",
+
+ .base_reg = REG_SPI_CLK_FREQ_SEL,
+ .base_bits = 1,
+ .base_shift = 0,
+ .base_values = slic_base,
+ .n_base_values = ARRAY_SIZE(slic_base),
+
+ .div_reg = REG_SPI_CLK_DIV_SEL,
+ .div_bits = 5,
+ .div_shift = 24,
+ .div_val0 = 20,
+ .div_step = 2,
+ }, {
+ .id = EN7523_CLK_SPI,
+ .name = "spi",
+
+ .base_reg = REG_SPI_CLK_DIV_SEL,
+
+ .base_value = 400000000,
+
+ .div_bits = 5,
+ .div_shift = 8,
+ .div_val0 = 40,
+ .div_step = 2,
+ }, {
+ .id = EN7523_CLK_NPU,
+ .name = "npu",
+
+ .base_reg = REG_NPU_CLK_DIV_SEL,
+ .base_bits = 2,
+ .base_shift = 8,
+ .base_values = npu7581_base,
+ .n_base_values = ARRAY_SIZE(npu7581_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ }, {
+ .id = EN7523_CLK_CRYPTO,
+ .name = "crypto",
+
+ .base_reg = REG_CRYPTO_CLKSRC2,
+ .base_bits = 1,
+ .base_shift = 0,
+ .base_values = crypto_base,
+ .n_base_values = ARRAY_SIZE(crypto_base),
+ }, {
+ .id = EN7581_CLK_EMMC,
+ .name = "emmc",
+
+ .base_reg = REG_CRYPTO_CLKSRC2,
+ .base_bits = 1,
+ .base_shift = 12,
+ .base_values = emmc7581_base,
+ .n_base_values = ARRAY_SIZE(emmc7581_base),
+ }
};
-static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i)
-{
- const struct en_clk_desc *desc = &en7523_base_clks[i];
- u32 val;
+static const u16 en7581_rst_ofs[] = {
+ REG_RST_CTRL2,
+ REG_RST_CTRL1,
+};
+static const u16 en7523_rst_map[] = {
+ /* RST_CTRL2 */
+ [EN7523_XPON_PHY_RST] = 0,
+ [EN7523_XSI_MAC_RST] = 7,
+ [EN7523_XSI_PHY_RST] = 8,
+ [EN7523_NPU_RST] = 9,
+ [EN7523_I2S_RST] = 10,
+ [EN7523_TRNG_RST] = 11,
+ [EN7523_TRNG_MSTART_RST] = 12,
+ [EN7523_DUAL_HSI0_RST] = 13,
+ [EN7523_DUAL_HSI1_RST] = 14,
+ [EN7523_HSI_RST] = 15,
+ [EN7523_DUAL_HSI0_MAC_RST] = 16,
+ [EN7523_DUAL_HSI1_MAC_RST] = 17,
+ [EN7523_HSI_MAC_RST] = 18,
+ [EN7523_WDMA_RST] = 19,
+ [EN7523_WOE0_RST] = 20,
+ [EN7523_WOE1_RST] = 21,
+ [EN7523_HSDMA_RST] = 22,
+ [EN7523_I2C2RBUS_RST] = 23,
+ [EN7523_TDMA_RST] = 24,
+ /* RST_CTRL1 */
+ [EN7523_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0,
+ [EN7523_FE_PDMA_RST] = RST_NR_PER_BANK + 1,
+ [EN7523_FE_QDMA_RST] = RST_NR_PER_BANK + 2,
+ [EN7523_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4,
+ [EN7523_CRYPTO_RST] = RST_NR_PER_BANK + 6,
+ [EN7523_TIMER_RST] = RST_NR_PER_BANK + 8,
+ [EN7523_PCM1_RST] = RST_NR_PER_BANK + 11,
+ [EN7523_UART_RST] = RST_NR_PER_BANK + 12,
+ [EN7523_GPIO_RST] = RST_NR_PER_BANK + 13,
+ [EN7523_GDMA_RST] = RST_NR_PER_BANK + 14,
+ [EN7523_I2C_MASTER_RST] = RST_NR_PER_BANK + 16,
+ [EN7523_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17,
+ [EN7523_SFC_RST] = RST_NR_PER_BANK + 18,
+ [EN7523_UART2_RST] = RST_NR_PER_BANK + 19,
+ [EN7523_GDMP_RST] = RST_NR_PER_BANK + 20,
+ [EN7523_FE_RST] = RST_NR_PER_BANK + 21,
+ [EN7523_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22,
+ [EN7523_GSW_RST] = RST_NR_PER_BANK + 23,
+ [EN7523_SFC2_PCM_RST] = RST_NR_PER_BANK + 25,
+ [EN7523_PCIE0_RST] = RST_NR_PER_BANK + 26,
+ [EN7523_PCIE1_RST] = RST_NR_PER_BANK + 27,
+ [EN7523_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
+ [EN7523_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
+};
+
+static const u16 en7581_rst_map[] = {
+ /* RST_CTRL2 */
+ [EN7581_XPON_PHY_RST] = 0,
+ [EN7581_CPU_TIMER2_RST] = 2,
+ [EN7581_HSUART_RST] = 3,
+ [EN7581_UART4_RST] = 4,
+ [EN7581_UART5_RST] = 5,
+ [EN7581_I2C2_RST] = 6,
+ [EN7581_XSI_MAC_RST] = 7,
+ [EN7581_XSI_PHY_RST] = 8,
+ [EN7581_NPU_RST] = 9,
+ [EN7581_I2S_RST] = 10,
+ [EN7581_TRNG_RST] = 11,
+ [EN7581_TRNG_MSTART_RST] = 12,
+ [EN7581_DUAL_HSI0_RST] = 13,
+ [EN7581_DUAL_HSI1_RST] = 14,
+ [EN7581_HSI_RST] = 15,
+ [EN7581_DUAL_HSI0_MAC_RST] = 16,
+ [EN7581_DUAL_HSI1_MAC_RST] = 17,
+ [EN7581_HSI_MAC_RST] = 18,
+ [EN7581_WDMA_RST] = 19,
+ [EN7581_WOE0_RST] = 20,
+ [EN7581_WOE1_RST] = 21,
+ [EN7581_HSDMA_RST] = 22,
+ [EN7581_TDMA_RST] = 24,
+ [EN7581_EMMC_RST] = 25,
+ [EN7581_SOE_RST] = 26,
+ [EN7581_PCIE2_RST] = 27,
+ [EN7581_XFP_MAC_RST] = 28,
+ [EN7581_USB_HOST_P1_RST] = 29,
+ [EN7581_USB_HOST_P1_U3_PHY_RST] = 30,
+ /* RST_CTRL1 */
+ [EN7581_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0,
+ [EN7581_FE_PDMA_RST] = RST_NR_PER_BANK + 1,
+ [EN7581_FE_QDMA_RST] = RST_NR_PER_BANK + 2,
+ [EN7581_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4,
+ [EN7581_CRYPTO_RST] = RST_NR_PER_BANK + 6,
+ [EN7581_TIMER_RST] = RST_NR_PER_BANK + 8,
+ [EN7581_PCM1_RST] = RST_NR_PER_BANK + 11,
+ [EN7581_UART_RST] = RST_NR_PER_BANK + 12,
+ [EN7581_GPIO_RST] = RST_NR_PER_BANK + 13,
+ [EN7581_GDMA_RST] = RST_NR_PER_BANK + 14,
+ [EN7581_I2C_MASTER_RST] = RST_NR_PER_BANK + 16,
+ [EN7581_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17,
+ [EN7581_SFC_RST] = RST_NR_PER_BANK + 18,
+ [EN7581_UART2_RST] = RST_NR_PER_BANK + 19,
+ [EN7581_GDMP_RST] = RST_NR_PER_BANK + 20,
+ [EN7581_FE_RST] = RST_NR_PER_BANK + 21,
+ [EN7581_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22,
+ [EN7581_GSW_RST] = RST_NR_PER_BANK + 23,
+ [EN7581_SFC2_PCM_RST] = RST_NR_PER_BANK + 25,
+ [EN7581_PCIE0_RST] = RST_NR_PER_BANK + 26,
+ [EN7581_PCIE1_RST] = RST_NR_PER_BANK + 27,
+ [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28,
+ [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
+ [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
+};
+
+static int en7581_reset_register(struct device *dev, void __iomem *base,
+ const u16 *rst_map, int nr_resets);
+
+static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val)
+{
if (!desc->base_bits)
return desc->base_value;
- val = readl(base + desc->base_reg);
val >>= desc->base_shift;
val &= (1 << desc->base_bits) - 1;
@@ -169,23 +422,18 @@ static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i)
return desc->base_values[val];
}
-static u32 en7523_get_div(void __iomem *base, int i)
+static u32 en7523_get_div(const struct en_clk_desc *desc, u32 val)
{
- const struct en_clk_desc *desc = &en7523_base_clks[i];
- u32 reg, val;
-
if (!desc->div_bits)
return 1;
- reg = desc->div_reg ? desc->div_reg : desc->base_reg;
- val = readl(base + reg);
val >>= desc->div_shift;
val &= (1 << desc->div_bits) - 1;
if (!val && desc->div_val0)
return desc->div_val0;
- return (val + 1) * desc->div_step;
+ return (val + desc->div_offset) * desc->div_step;
}
static int en7523_pci_is_enabled(struct clk_hw *hw)
@@ -213,14 +461,14 @@ static int en7523_pci_prepare(struct clk_hw *hw)
usleep_range(1000, 2000);
/* Reset to default */
- val = readl(np_base + REG_RESET_CONTROL);
+ val = readl(np_base + REG_RESET_CONTROL1);
mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 |
REG_RESET_CONTROL_PCIEHB;
- writel(val & ~mask, np_base + REG_RESET_CONTROL);
+ writel(val & ~mask, np_base + REG_RESET_CONTROL1);
usleep_range(1000, 2000);
- writel(val | mask, np_base + REG_RESET_CONTROL);
+ writel(val | mask, np_base + REG_RESET_CONTROL1);
msleep(100);
- writel(val & ~mask, np_base + REG_RESET_CONTROL);
+ writel(val & ~mask, np_base + REG_RESET_CONTROL1);
usleep_range(5000, 10000);
/* Release device */
@@ -248,14 +496,10 @@ static void en7523_pci_unprepare(struct clk_hw *hw)
static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
void __iomem *np_base)
{
- static const struct clk_ops pcie_gate_ops = {
- .is_enabled = en7523_pci_is_enabled,
- .prepare = en7523_pci_prepare,
- .unprepare = en7523_pci_unprepare,
- };
+ const struct en_clk_soc_data *soc_data = device_get_match_data(dev);
struct clk_init_data init = {
.name = "pcie",
- .ops = &pcie_gate_ops,
+ .ops = &soc_data->pcie_ops,
};
struct en_clk_gate *cg;
@@ -265,7 +509,9 @@ static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
cg->base = np_base;
cg->hw.init = &init;
- en7523_pci_unprepare(&cg->hw);
+
+ if (init.ops->unprepare)
+ init.ops->unprepare(&cg->hw);
if (clk_hw_register(dev, &cg->hw))
return NULL;
@@ -273,6 +519,45 @@ static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
return &cg->hw;
}
+static int en7581_pci_is_enabled(struct clk_hw *hw)
+{
+ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ u32 val, mask;
+
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
+ val = readl(cg->base + REG_PCI_CONTROL);
+ return (val & mask) == mask;
+}
+
+static int en7581_pci_enable(struct clk_hw *hw)
+{
+ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ void __iomem *np_base = cg->base;
+ u32 val, mask;
+
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
+ REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
+ REG_PCI_CONTROL_PERSTOUT;
+ val = readl(np_base + REG_PCI_CONTROL);
+ writel(val | mask, np_base + REG_PCI_CONTROL);
+
+ return 0;
+}
+
+static void en7581_pci_disable(struct clk_hw *hw)
+{
+ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ void __iomem *np_base = cg->base;
+ u32 val, mask;
+
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
+ REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
+ REG_PCI_CONTROL_PERSTOUT;
+ val = readl(np_base + REG_PCI_CONTROL);
+ writel(val & ~mask, np_base + REG_PCI_CONTROL);
+ usleep_range(1000, 2000);
+}
+
static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data,
void __iomem *base, void __iomem *np_base)
{
@@ -282,9 +567,12 @@ static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_dat
for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) {
const struct en_clk_desc *desc = &en7523_base_clks[i];
+ u32 reg = desc->div_reg ? desc->div_reg : desc->base_reg;
+ u32 val = readl(base + desc->base_reg);
- rate = en7523_get_base_rate(base, i);
- rate /= en7523_get_div(base, i);
+ rate = en7523_get_base_rate(desc, val);
+ val = readl(base + reg);
+ rate /= en7523_get_div(desc, val);
hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate);
if (IS_ERR(hw)) {
@@ -298,16 +586,12 @@ static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_dat
hw = en7523_register_pcie_clk(dev, np_base);
clk_data->hws[EN7523_CLK_PCIE] = hw;
-
- clk_data->num = EN7523_NUM_CLOCKS;
}
-static int en7523_clk_probe(struct platform_device *pdev)
+static int en7523_clk_hw_init(struct platform_device *pdev,
+ struct clk_hw_onecell_data *clk_data)
{
- struct device_node *node = pdev->dev.of_node;
- struct clk_hw_onecell_data *clk_data;
void __iomem *base, *np_base;
- int r;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -317,23 +601,210 @@ static int en7523_clk_probe(struct platform_device *pdev)
if (IS_ERR(np_base))
return PTR_ERR(np_base);
+ en7523_register_clocks(&pdev->dev, clk_data, base, np_base);
+
+ return en7581_reset_register(&pdev->dev, np_base, en7523_rst_map,
+ ARRAY_SIZE(en7523_rst_map));
+}
+
+static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data,
+ struct regmap *map, void __iomem *base)
+{
+ struct clk_hw *hw;
+ u32 rate;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(en7581_base_clks); i++) {
+ const struct en_clk_desc *desc = &en7581_base_clks[i];
+ u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg;
+ int err;
+
+ err = regmap_read(map, desc->base_reg, &val);
+ if (err) {
+ pr_err("Failed reading fixed clk rate %s: %d\n",
+ desc->name, err);
+ continue;
+ }
+ rate = en7523_get_base_rate(desc, val);
+
+ err = regmap_read(map, reg, &val);
+ if (err) {
+ pr_err("Failed reading fixed clk div %s: %d\n",
+ desc->name, err);
+ continue;
+ }
+ rate /= en7523_get_div(desc, val);
+
+ hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register clk %s: %ld\n",
+ desc->name, PTR_ERR(hw));
+ continue;
+ }
+
+ clk_data->hws[desc->id] = hw;
+ }
+
+ hw = en7523_register_pcie_clk(dev, base);
+ clk_data->hws[EN7523_CLK_PCIE] = hw;
+}
+
+static int en7523_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
+ void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK];
+ u32 val;
+
+ val = readl(addr);
+ if (assert)
+ val |= BIT(id % RST_NR_PER_BANK);
+ else
+ val &= ~BIT(id % RST_NR_PER_BANK);
+ writel(val, addr);
+
+ return 0;
+}
+
+static int en7523_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return en7523_reset_update(rcdev, id, true);
+}
+
+static int en7523_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return en7523_reset_update(rcdev, id, false);
+}
+
+static int en7523_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
+ void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK];
+
+ return !!(readl(addr) & BIT(id % RST_NR_PER_BANK));
+}
+
+static int en7523_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
+
+ if (reset_spec->args[0] >= rcdev->nr_resets)
+ return -EINVAL;
+
+ return rst_data->idx_map[reset_spec->args[0]];
+}
+
+static const struct reset_control_ops en7581_reset_ops = {
+ .assert = en7523_reset_assert,
+ .deassert = en7523_reset_deassert,
+ .status = en7523_reset_status,
+};
+
+static int en7581_reset_register(struct device *dev, void __iomem *base,
+ const u16 *rst_map, int nr_resets)
+{
+ struct en_rst_data *rst_data;
+
+ rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL);
+ if (!rst_data)
+ return -ENOMEM;
+
+ rst_data->bank_ofs = en7581_rst_ofs;
+ rst_data->idx_map = rst_map;
+ rst_data->base = base;
+
+ rst_data->rcdev.nr_resets = nr_resets;
+ rst_data->rcdev.of_xlate = en7523_reset_xlate;
+ rst_data->rcdev.ops = &en7581_reset_ops;
+ rst_data->rcdev.of_node = dev->of_node;
+ rst_data->rcdev.of_reset_n_cells = 1;
+ rst_data->rcdev.owner = THIS_MODULE;
+ rst_data->rcdev.dev = dev;
+
+ return devm_reset_controller_register(dev, &rst_data->rcdev);
+}
+
+static int en7581_clk_hw_init(struct platform_device *pdev,
+ struct clk_hw_onecell_data *clk_data)
+{
+ struct regmap *map;
+ void __iomem *base;
+ u32 val;
+
+ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu");
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ en7581_register_clocks(&pdev->dev, clk_data, map, base);
+
+ val = readl(base + REG_NP_SCU_SSTR);
+ val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK);
+ writel(val, base + REG_NP_SCU_SSTR);
+ val = readl(base + REG_NP_SCU_PCIC);
+ writel(val | 3, base + REG_NP_SCU_PCIC);
+
+ return en7581_reset_register(&pdev->dev, base, en7581_rst_map,
+ ARRAY_SIZE(en7581_rst_map));
+}
+
+static int en7523_clk_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ const struct en_clk_soc_data *soc_data;
+ struct clk_hw_onecell_data *clk_data;
+ int r;
+
+ soc_data = device_get_match_data(&pdev->dev);
+
clk_data = devm_kzalloc(&pdev->dev,
- struct_size(clk_data, hws, EN7523_NUM_CLOCKS),
+ struct_size(clk_data, hws, soc_data->num_clocks),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
- en7523_register_clocks(&pdev->dev, clk_data, base, np_base);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ clk_data->num = soc_data->num_clocks;
+ r = soc_data->hw_init(pdev, clk_data);
if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
+ return r;
- return r;
+ return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
}
+static const struct en_clk_soc_data en7523_data = {
+ .num_clocks = ARRAY_SIZE(en7523_base_clks) + 1,
+ .pcie_ops = {
+ .is_enabled = en7523_pci_is_enabled,
+ .prepare = en7523_pci_prepare,
+ .unprepare = en7523_pci_unprepare,
+ },
+ .hw_init = en7523_clk_hw_init,
+};
+
+static const struct en_clk_soc_data en7581_data = {
+ /* We increment num_clocks by 1 to account for additional PCIe clock */
+ .num_clocks = ARRAY_SIZE(en7581_base_clks) + 1,
+ .pcie_ops = {
+ .is_enabled = en7581_pci_is_enabled,
+ .enable = en7581_pci_enable,
+ .disable = en7581_pci_disable,
+ },
+ .hw_init = en7581_clk_hw_init,
+};
+
+static const struct of_device_id of_match_clk_en7523[] = {
+ { .compatible = "airoha,en7523-scu", .data = &en7523_data },
+ { .compatible = "airoha,en7581-scu", .data = &en7581_data },
+ { /* sentinel */ }
+};
+
static struct platform_driver clk_en7523_drv = {
.probe = en7523_clk_probe,
.driver = {
diff --git a/drivers/clk/clk-ep93xx.c b/drivers/clk/clk-ep93xx.c
new file mode 100644
index 000000000000..972aadd11493
--- /dev/null
+++ b/drivers/clk/clk-ep93xx.c
@@ -0,0 +1,853 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Clock control for Cirrus EP93xx chips.
+ * Copyright (C) 2021 Nikita Shubin <nikita.shubin@maquefel.me>
+ *
+ * Based on a rewrite of arch/arm/mach-ep93xx/clock.c:
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ */
+#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt
+
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk-provider.h>
+#include <linux/math.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#include <linux/soc/cirrus/ep93xx.h>
+#include <dt-bindings/clock/cirrus,ep9301-syscon.h>
+
+#include <asm/div64.h>
+
+#define EP93XX_EXT_CLK_RATE 14745600
+#define EP93XX_EXT_RTC_RATE 32768
+
+#define EP93XX_SYSCON_POWER_STATE 0x00
+#define EP93XX_SYSCON_PWRCNT 0x04
+#define EP93XX_SYSCON_PWRCNT_UARTBAUD BIT(29)
+#define EP93XX_SYSCON_PWRCNT_USH_EN 28
+#define EP93XX_SYSCON_PWRCNT_DMA_M2M1 27
+#define EP93XX_SYSCON_PWRCNT_DMA_M2M0 26
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P8 25
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P9 24
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P6 23
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P7 22
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P4 21
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P5 20
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P2 19
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P3 18
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P0 17
+#define EP93XX_SYSCON_PWRCNT_DMA_M2P1 16
+#define EP93XX_SYSCON_CLKSET1 0x20
+#define EP93XX_SYSCON_CLKSET1_NBYP1 BIT(23)
+#define EP93XX_SYSCON_CLKSET2 0x24
+#define EP93XX_SYSCON_CLKSET2_NBYP2 BIT(19)
+#define EP93XX_SYSCON_CLKSET2_PLL2_EN BIT(18)
+#define EP93XX_SYSCON_DEVCFG 0x80
+#define EP93XX_SYSCON_DEVCFG_U3EN 24
+#define EP93XX_SYSCON_DEVCFG_U2EN 20
+#define EP93XX_SYSCON_DEVCFG_U1EN 18
+#define EP93XX_SYSCON_VIDCLKDIV 0x84
+#define EP93XX_SYSCON_CLKDIV_ENABLE 15
+#define EP93XX_SYSCON_CLKDIV_ESEL BIT(14)
+#define EP93XX_SYSCON_CLKDIV_PSEL BIT(13)
+#define EP93XX_SYSCON_CLKDIV_MASK GENMASK(14, 13)
+#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8
+#define EP93XX_SYSCON_I2SCLKDIV 0x8c
+#define EP93XX_SYSCON_I2SCLKDIV_SENA 31
+#define EP93XX_SYSCON_I2SCLKDIV_ORIDE BIT(29)
+#define EP93XX_SYSCON_I2SCLKDIV_SPOL BIT(19)
+#define EP93XX_SYSCON_KEYTCHCLKDIV 0x90
+#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN 31
+#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV 16
+#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN 15
+#define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV 0
+#define EP93XX_SYSCON_CHIPID 0x94
+#define EP93XX_SYSCON_CHIPID_ID 0x9213
+
+#define EP93XX_FIXED_CLK_COUNT 21
+
+static const char ep93xx_adc_divisors[] = { 16, 4 };
+static const char ep93xx_sclk_divisors[] = { 2, 4 };
+static const char ep93xx_lrclk_divisors[] = { 32, 64, 128 };
+
+struct ep93xx_clk {
+ struct clk_hw hw;
+ u16 idx;
+ u16 reg;
+ u32 mask;
+ u8 bit_idx;
+ u8 shift;
+ u8 width;
+ u8 num_div;
+ const char *div;
+};
+
+struct ep93xx_clk_priv {
+ spinlock_t lock;
+ struct ep93xx_regmap_adev *aux_dev;
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *map;
+ struct clk_hw *fixed[EP93XX_FIXED_CLK_COUNT];
+ struct ep93xx_clk reg[];
+};
+
+static struct ep93xx_clk *ep93xx_clk_from(struct clk_hw *hw)
+{
+ return container_of(hw, struct ep93xx_clk, hw);
+}
+
+static struct ep93xx_clk_priv *ep93xx_priv_from(struct ep93xx_clk *clk)
+{
+ return container_of(clk, struct ep93xx_clk_priv, reg[clk->idx]);
+}
+
+static void ep93xx_clk_write(struct ep93xx_clk_priv *priv, unsigned int reg, unsigned int val)
+{
+ struct ep93xx_regmap_adev *aux = priv->aux_dev;
+
+ aux->write(aux->map, aux->lock, reg, val);
+}
+
+static int ep93xx_clk_is_enabled(struct clk_hw *hw)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ u32 val;
+
+ regmap_read(priv->map, clk->reg, &val);
+
+ return !!(val & BIT(clk->bit_idx));
+}
+
+static int ep93xx_clk_enable(struct clk_hw *hw)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ u32 val;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ regmap_read(priv->map, clk->reg, &val);
+ val |= BIT(clk->bit_idx);
+
+ ep93xx_clk_write(priv, clk->reg, val);
+
+ return 0;
+}
+
+static void ep93xx_clk_disable(struct clk_hw *hw)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ u32 val;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ regmap_read(priv->map, clk->reg, &val);
+ val &= ~BIT(clk->bit_idx);
+
+ ep93xx_clk_write(priv, clk->reg, val);
+}
+
+static const struct clk_ops clk_ep93xx_gate_ops = {
+ .enable = ep93xx_clk_enable,
+ .disable = ep93xx_clk_disable,
+ .is_enabled = ep93xx_clk_is_enabled,
+};
+
+static int ep93xx_clk_register_gate(struct ep93xx_clk *clk,
+ const char *name,
+ struct clk_parent_data *parent_data,
+ unsigned long flags,
+ unsigned int reg,
+ u8 bit_idx)
+{
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ struct clk_init_data init = { };
+
+ init.name = name;
+ init.ops = &clk_ep93xx_gate_ops;
+ init.flags = flags;
+ init.parent_data = parent_data;
+ init.num_parents = 1;
+
+ clk->reg = reg;
+ clk->bit_idx = bit_idx;
+ clk->hw.init = &init;
+
+ return devm_clk_hw_register(priv->dev, &clk->hw);
+}
+
+static u8 ep93xx_mux_get_parent(struct clk_hw *hw)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ u32 val;
+
+ regmap_read(priv->map, clk->reg, &val);
+
+ val &= EP93XX_SYSCON_CLKDIV_MASK;
+
+ switch (val) {
+ case EP93XX_SYSCON_CLKDIV_ESEL:
+ return 1; /* PLL1 */
+ case EP93XX_SYSCON_CLKDIV_MASK:
+ return 2; /* PLL2 */
+ default:
+ return 0; /* XTALI */
+ };
+}
+
+static int ep93xx_mux_set_parent_lock(struct clk_hw *hw, u8 index)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ u32 val;
+
+ if (index >= 3)
+ return -EINVAL;
+
+ guard(spinlock_irqsave)(&priv->lock);
+
+ regmap_read(priv->map, clk->reg, &val);
+ val &= ~(EP93XX_SYSCON_CLKDIV_MASK);
+ val |= index > 0 ? EP93XX_SYSCON_CLKDIV_ESEL : 0;
+ val |= index > 1 ? EP93XX_SYSCON_CLKDIV_PSEL : 0;
+
+ ep93xx_clk_write(priv, clk->reg, val);
+
+ return 0;
+}
+
+static bool is_best(unsigned long rate, unsigned long now,
+ unsigned long best)
+{
+ return abs_diff(rate, now) < abs_diff(rate, best);
+}
+
+static int ep93xx_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned long best_rate = 0, actual_rate, mclk_rate;
+ unsigned long rate = req->rate;
+ struct clk_hw *parent_best = NULL;
+ unsigned long parent_rate_best;
+ unsigned long parent_rate;
+ int div, pdiv;
+ unsigned int i;
+
+ /*
+ * Try the two pll's and the external clock,
+ * because the valid predividers are 2, 2.5 and 3, we multiply
+ * all the clocks by 2 to avoid floating point math.
+ *
+ * This is based on the algorithm in the ep93xx raster guide:
+ * http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf
+ *
+ */
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+
+ parent_rate = clk_hw_get_rate(parent);
+ mclk_rate = parent_rate * 2;
+
+ /* Try each predivider value */
+ for (pdiv = 4; pdiv <= 6; pdiv++) {
+ div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv);
+ if (!in_range(div, 1, 127))
+ continue;
+
+ actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div);
+ if (is_best(rate, actual_rate, best_rate)) {
+ best_rate = actual_rate;
+ parent_rate_best = parent_rate;
+ parent_best = parent;
+ }
+ }
+ }
+
+ if (!parent_best)
+ return -EINVAL;
+
+ req->best_parent_rate = parent_rate_best;
+ req->best_parent_hw = parent_best;
+ req->rate = best_rate;
+
+ return 0;
+}
+
+static unsigned long ep93xx_ddiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ unsigned int pdiv, div;
+ u32 val;
+
+ regmap_read(priv->map, clk->reg, &val);
+ pdiv = (val >> EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) & GENMASK(1, 0);
+ div = val & GENMASK(6, 0);
+ if (!div)
+ return 0;
+
+ return DIV_ROUND_CLOSEST(parent_rate * 2, (pdiv + 3) * div);
+}
+
+static int ep93xx_ddiv_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ int pdiv, div, npdiv, ndiv;
+ unsigned long actual_rate, mclk_rate, rate_err = ULONG_MAX;
+ u32 val;
+
+ regmap_read(priv->map, clk->reg, &val);
+ mclk_rate = parent_rate * 2;
+
+ for (pdiv = 4; pdiv <= 6; pdiv++) {
+ div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv);
+ if (!in_range(div, 1, 127))
+ continue;
+
+ actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div);
+ if (abs(actual_rate - rate) < rate_err) {
+ npdiv = pdiv - 3;
+ ndiv = div;
+ rate_err = abs(actual_rate - rate);
+ }
+ }
+
+ if (rate_err == ULONG_MAX)
+ return -EINVAL;
+
+ /*
+ * Clear old dividers.
+ * Bit 7 is reserved bit in all ClkDiv registers.
+ */
+ val &= ~(GENMASK(9, 0) & ~BIT(7));
+
+ /* Set the new pdiv and div bits for the new clock rate */
+ val |= (npdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | ndiv;
+
+ ep93xx_clk_write(priv, clk->reg, val);
+
+ return 0;
+}
+
+static const struct clk_ops clk_ddiv_ops = {
+ .enable = ep93xx_clk_enable,
+ .disable = ep93xx_clk_disable,
+ .is_enabled = ep93xx_clk_is_enabled,
+ .get_parent = ep93xx_mux_get_parent,
+ .set_parent = ep93xx_mux_set_parent_lock,
+ .determine_rate = ep93xx_mux_determine_rate,
+ .recalc_rate = ep93xx_ddiv_recalc_rate,
+ .set_rate = ep93xx_ddiv_set_rate,
+};
+
+static int ep93xx_clk_register_ddiv(struct ep93xx_clk *clk,
+ const char *name,
+ struct clk_parent_data *parent_data,
+ u8 num_parents,
+ unsigned int reg,
+ u8 bit_idx)
+{
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ struct clk_init_data init = { };
+
+ init.name = name;
+ init.ops = &clk_ddiv_ops;
+ init.flags = 0;
+ init.parent_data = parent_data;
+ init.num_parents = num_parents;
+
+ clk->reg = reg;
+ clk->bit_idx = bit_idx;
+ clk->hw.init = &init;
+
+ return devm_clk_hw_register(priv->dev, &clk->hw);
+}
+
+static unsigned long ep93xx_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ u32 val;
+ u8 index;
+
+ regmap_read(priv->map, clk->reg, &val);
+ index = (val & clk->mask) >> clk->shift;
+ if (index >= clk->num_div)
+ return 0;
+
+ return DIV_ROUND_CLOSEST(parent_rate, clk->div[index]);
+}
+
+static int ep93xx_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ unsigned long best = 0, now;
+ unsigned int i;
+
+ for (i = 0; i < clk->num_div; i++) {
+ if (req->rate * clk->div[i] == req->best_parent_rate)
+ return 0;
+
+ now = DIV_ROUND_CLOSEST(req->best_parent_rate, clk->div[i]);
+ if (!best || is_best(req->rate, now, best))
+ best = now;
+ }
+
+ req->rate = best;
+
+ return 0;
+}
+
+static int ep93xx_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ep93xx_clk *clk = ep93xx_clk_from(hw);
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ unsigned int i;
+ u32 val;
+
+ regmap_read(priv->map, clk->reg, &val);
+ val &= ~clk->mask;
+ for (i = 0; i < clk->num_div; i++)
+ if (rate == DIV_ROUND_CLOSEST(parent_rate, clk->div[i]))
+ break;
+
+ if (i == clk->num_div)
+ return -EINVAL;
+
+ val |= i << clk->shift;
+
+ ep93xx_clk_write(priv, clk->reg, val);
+
+ return 0;
+}
+
+static const struct clk_ops ep93xx_div_ops = {
+ .enable = ep93xx_clk_enable,
+ .disable = ep93xx_clk_disable,
+ .is_enabled = ep93xx_clk_is_enabled,
+ .recalc_rate = ep93xx_div_recalc_rate,
+ .determine_rate = ep93xx_div_determine_rate,
+ .set_rate = ep93xx_div_set_rate,
+};
+
+static int ep93xx_register_div(struct ep93xx_clk *clk,
+ const char *name,
+ const struct clk_parent_data *parent_data,
+ unsigned int reg,
+ u8 enable_bit,
+ u8 shift,
+ u8 width,
+ const char *clk_divisors,
+ u8 num_div)
+{
+ struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
+ struct clk_init_data init = { };
+
+ init.name = name;
+ init.ops = &ep93xx_div_ops;
+ init.flags = 0;
+ init.parent_data = parent_data;
+ init.num_parents = 1;
+
+ clk->reg = reg;
+ clk->bit_idx = enable_bit;
+ clk->mask = GENMASK(shift + width - 1, shift);
+ clk->shift = shift;
+ clk->div = clk_divisors;
+ clk->num_div = num_div;
+ clk->hw.init = &init;
+
+ return devm_clk_hw_register(priv->dev, &clk->hw);
+}
+
+struct ep93xx_gate {
+ unsigned int idx;
+ unsigned int bit;
+ const char *name;
+};
+
+static const struct ep93xx_gate ep93xx_uarts[] = {
+ { EP93XX_CLK_UART1, EP93XX_SYSCON_DEVCFG_U1EN, "uart1" },
+ { EP93XX_CLK_UART2, EP93XX_SYSCON_DEVCFG_U2EN, "uart2" },
+ { EP93XX_CLK_UART3, EP93XX_SYSCON_DEVCFG_U3EN, "uart3" },
+};
+
+static int ep93xx_uart_clock_init(struct ep93xx_clk_priv *priv)
+{
+ struct clk_parent_data parent_data = { };
+ unsigned int i, idx, clk_uart_div;
+ struct ep93xx_clk *clk;
+ u32 val;
+ int ret;
+
+ regmap_read(priv->map, EP93XX_SYSCON_PWRCNT, &val);
+ if (val & EP93XX_SYSCON_PWRCNT_UARTBAUD)
+ clk_uart_div = 1;
+ else
+ clk_uart_div = 2;
+
+ priv->fixed[EP93XX_CLK_UART] =
+ devm_clk_hw_register_fixed_factor_index(priv->dev, "uart",
+ 0, /* XTALI external clock */
+ 0, 1, clk_uart_div);
+ parent_data.hw = priv->fixed[EP93XX_CLK_UART];
+
+ /* parenting uart gate clocks to uart clock */
+ for (i = 0; i < ARRAY_SIZE(ep93xx_uarts); i++) {
+ idx = ep93xx_uarts[i].idx - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ ret = ep93xx_clk_register_gate(clk,
+ ep93xx_uarts[i].name,
+ &parent_data, CLK_SET_RATE_PARENT,
+ EP93XX_SYSCON_DEVCFG,
+ ep93xx_uarts[i].bit);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "failed to register uart[%d] clock\n", i);
+ }
+
+ return 0;
+}
+
+static const struct ep93xx_gate ep93xx_dmas[] = {
+ { EP93XX_CLK_M2M0, EP93XX_SYSCON_PWRCNT_DMA_M2M0, "m2m0" },
+ { EP93XX_CLK_M2M1, EP93XX_SYSCON_PWRCNT_DMA_M2M1, "m2m1" },
+ { EP93XX_CLK_M2P0, EP93XX_SYSCON_PWRCNT_DMA_M2P0, "m2p0" },
+ { EP93XX_CLK_M2P1, EP93XX_SYSCON_PWRCNT_DMA_M2P1, "m2p1" },
+ { EP93XX_CLK_M2P2, EP93XX_SYSCON_PWRCNT_DMA_M2P2, "m2p2" },
+ { EP93XX_CLK_M2P3, EP93XX_SYSCON_PWRCNT_DMA_M2P3, "m2p3" },
+ { EP93XX_CLK_M2P4, EP93XX_SYSCON_PWRCNT_DMA_M2P4, "m2p4" },
+ { EP93XX_CLK_M2P5, EP93XX_SYSCON_PWRCNT_DMA_M2P5, "m2p5" },
+ { EP93XX_CLK_M2P6, EP93XX_SYSCON_PWRCNT_DMA_M2P6, "m2p6" },
+ { EP93XX_CLK_M2P7, EP93XX_SYSCON_PWRCNT_DMA_M2P7, "m2p7" },
+ { EP93XX_CLK_M2P8, EP93XX_SYSCON_PWRCNT_DMA_M2P8, "m2p8" },
+ { EP93XX_CLK_M2P9, EP93XX_SYSCON_PWRCNT_DMA_M2P9, "m2p9" },
+};
+
+static int ep93xx_dma_clock_init(struct ep93xx_clk_priv *priv)
+{
+ struct clk_parent_data parent_data = { };
+ unsigned int i, idx;
+
+ parent_data.hw = priv->fixed[EP93XX_CLK_HCLK];
+ for (i = 0; i < ARRAY_SIZE(ep93xx_dmas); i++) {
+ idx = ep93xx_dmas[i].idx;
+ priv->fixed[idx] = devm_clk_hw_register_gate_parent_data(priv->dev,
+ ep93xx_dmas[i].name,
+ &parent_data, 0,
+ priv->base + EP93XX_SYSCON_PWRCNT,
+ ep93xx_dmas[i].bit,
+ 0,
+ &priv->lock);
+ if (IS_ERR(priv->fixed[idx]))
+ return PTR_ERR(priv->fixed[idx]);
+ }
+
+ return 0;
+}
+
+static struct clk_hw *of_clk_ep93xx_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct ep93xx_clk_priv *priv = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx < EP93XX_CLK_UART1)
+ return priv->fixed[idx];
+
+ if (idx <= EP93XX_CLK_I2S_LRCLK)
+ return &priv->reg[idx - EP93XX_CLK_UART1].hw;
+
+ return ERR_PTR(-EINVAL);
+}
+
+/*
+ * PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) / 2^PS
+ */
+static unsigned long calc_pll_rate(u64 rate, u32 config_word)
+{
+ rate *= ((config_word >> 11) & GENMASK(4, 0)) + 1; /* X1FBD */
+ rate *= ((config_word >> 5) & GENMASK(5, 0)) + 1; /* X2FBD */
+ do_div(rate, (config_word & GENMASK(4, 0)) + 1); /* X2IPD */
+ rate >>= (config_word >> 16) & GENMASK(1, 0); /* PS */
+
+ return rate;
+}
+
+static int ep93xx_plls_init(struct ep93xx_clk_priv *priv)
+{
+ static const char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
+ static const char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
+ static const char pclk_divisors[] = { 1, 2, 4, 8 };
+ struct clk_parent_data xtali = { .index = 0 };
+ unsigned int clk_f_div, clk_h_div, clk_p_div;
+ unsigned long clk_pll1_rate, clk_pll2_rate;
+ struct device *dev = priv->dev;
+ struct clk_hw *hw, *pll1;
+ u32 value;
+
+ /* Determine the bootloader configured pll1 rate */
+ regmap_read(priv->map, EP93XX_SYSCON_CLKSET1, &value);
+
+ if (value & EP93XX_SYSCON_CLKSET1_NBYP1)
+ clk_pll1_rate = calc_pll_rate(EP93XX_EXT_CLK_RATE, value);
+ else
+ clk_pll1_rate = EP93XX_EXT_CLK_RATE;
+
+ pll1 = devm_clk_hw_register_fixed_rate_parent_data(dev, "pll1", &xtali,
+ 0, clk_pll1_rate);
+ if (IS_ERR(pll1))
+ return PTR_ERR(pll1);
+
+ priv->fixed[EP93XX_CLK_PLL1] = pll1;
+
+ /* Initialize the pll1 derived clocks */
+ clk_f_div = fclk_divisors[(value >> 25) & GENMASK(2, 0)];
+ clk_h_div = hclk_divisors[(value >> 20) & GENMASK(2, 0)];
+ clk_p_div = pclk_divisors[(value >> 18) & GENMASK(1, 0)];
+
+ hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "fclk", pll1, 0, 1, clk_f_div);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_FCLK] = hw;
+
+ hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "hclk", pll1, 0, 1, clk_h_div);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_HCLK] = hw;
+
+ hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "pclk", hw, 0, 1, clk_p_div);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_PCLK] = hw;
+
+ /* Determine the bootloader configured pll2 rate */
+ regmap_read(priv->map, EP93XX_SYSCON_CLKSET2, &value);
+ if (!(value & EP93XX_SYSCON_CLKSET2_NBYP2))
+ clk_pll2_rate = EP93XX_EXT_CLK_RATE;
+ else if (value & EP93XX_SYSCON_CLKSET2_PLL2_EN)
+ clk_pll2_rate = calc_pll_rate(EP93XX_EXT_CLK_RATE, value);
+ else
+ clk_pll2_rate = 0;
+
+ hw = devm_clk_hw_register_fixed_rate_parent_data(dev, "pll2", &xtali,
+ 0, clk_pll2_rate);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_PLL2] = hw;
+
+ return 0;
+}
+
+static int ep93xx_clk_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct ep93xx_regmap_adev *rdev = to_ep93xx_regmap_adev(adev);
+ struct clk_parent_data xtali = { .index = 0 };
+ struct clk_parent_data ddiv_pdata[3] = { };
+ unsigned int clk_spi_div, clk_usb_div;
+ struct clk_parent_data pdata = {};
+ struct device *dev = &adev->dev;
+ struct ep93xx_clk_priv *priv;
+ struct ep93xx_clk *clk;
+ struct clk_hw *hw;
+ unsigned int idx;
+ int ret;
+ u32 value;
+
+ priv = devm_kzalloc(dev, struct_size(priv, reg, 10), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+ priv->dev = dev;
+ priv->aux_dev = rdev;
+ priv->map = rdev->map;
+ priv->base = rdev->base;
+
+ ret = ep93xx_plls_init(priv);
+ if (ret)
+ return ret;
+
+ regmap_read(priv->map, EP93XX_SYSCON_CLKSET2, &value);
+ clk_usb_div = (value >> 28 & GENMASK(3, 0)) + 1;
+ hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "usb_clk",
+ priv->fixed[EP93XX_CLK_PLL2], 0, 1,
+ clk_usb_div);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_USB] = hw;
+
+ ret = ep93xx_uart_clock_init(priv);
+ if (ret)
+ return ret;
+
+ ret = ep93xx_dma_clock_init(priv);
+ if (ret)
+ return ret;
+
+ clk_spi_div = id->driver_data;
+ hw = devm_clk_hw_register_fixed_factor_index(dev, "ep93xx-spi.0",
+ 0, /* XTALI external clock */
+ 0, 1, clk_spi_div);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_SPI] = hw;
+
+ /* PWM clock */
+ hw = devm_clk_hw_register_fixed_factor_index(dev, "pwm_clk", 0, /* XTALI external clock */
+ 0, 1, 1);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_PWM] = hw;
+
+ /* USB clock */
+ pdata.hw = priv->fixed[EP93XX_CLK_USB];
+ hw = devm_clk_hw_register_gate_parent_data(priv->dev, "ohci-platform", &pdata,
+ 0, priv->base + EP93XX_SYSCON_PWRCNT,
+ EP93XX_SYSCON_PWRCNT_USH_EN, 0,
+ &priv->lock);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ priv->fixed[EP93XX_CLK_USB] = hw;
+
+ ddiv_pdata[0].index = 0; /* XTALI external clock */
+ ddiv_pdata[1].hw = priv->fixed[EP93XX_CLK_PLL1];
+ ddiv_pdata[2].hw = priv->fixed[EP93XX_CLK_PLL2];
+
+ /* touchscreen/ADC clock */
+ idx = EP93XX_CLK_ADC - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ ret = ep93xx_register_div(clk, "ep93xx-adc", &xtali,
+ EP93XX_SYSCON_KEYTCHCLKDIV,
+ EP93XX_SYSCON_KEYTCHCLKDIV_TSEN,
+ EP93XX_SYSCON_KEYTCHCLKDIV_ADIV,
+ 1,
+ ep93xx_adc_divisors,
+ ARRAY_SIZE(ep93xx_adc_divisors));
+
+
+ /* keypad clock */
+ idx = EP93XX_CLK_KEYPAD - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ ret = ep93xx_register_div(clk, "ep93xx-keypad", &xtali,
+ EP93XX_SYSCON_KEYTCHCLKDIV,
+ EP93XX_SYSCON_KEYTCHCLKDIV_KEN,
+ EP93XX_SYSCON_KEYTCHCLKDIV_KDIV,
+ 1,
+ ep93xx_adc_divisors,
+ ARRAY_SIZE(ep93xx_adc_divisors));
+
+ /*
+ * On reset PDIV and VDIV is set to zero, while PDIV zero
+ * means clock disable, VDIV shouldn't be zero.
+ * So we set both video and i2s dividers to minimum.
+ * ENA - Enable CLK divider.
+ * PDIV - 00 - Disable clock
+ * VDIV - at least 2
+ */
+
+ /* Check and enable video clk registers */
+ regmap_read(priv->map, EP93XX_SYSCON_VIDCLKDIV, &value);
+ value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2;
+ ep93xx_clk_write(priv, EP93XX_SYSCON_VIDCLKDIV, value);
+
+ /* Check and enable i2s clk registers */
+ regmap_read(priv->map, EP93XX_SYSCON_I2SCLKDIV, &value);
+ value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2;
+
+ /*
+ * Override the SAI_MSTR_CLK_CFG from the I2S block and use the
+ * I2SClkDiv Register settings. LRCLK transitions on the falling SCLK
+ * edge.
+ */
+ value |= EP93XX_SYSCON_I2SCLKDIV_ORIDE | EP93XX_SYSCON_I2SCLKDIV_SPOL;
+ ep93xx_clk_write(priv, EP93XX_SYSCON_I2SCLKDIV, value);
+
+ /* video clk */
+ idx = EP93XX_CLK_VIDEO - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ ret = ep93xx_clk_register_ddiv(clk, "ep93xx-fb",
+ ddiv_pdata, ARRAY_SIZE(ddiv_pdata),
+ EP93XX_SYSCON_VIDCLKDIV,
+ EP93XX_SYSCON_CLKDIV_ENABLE);
+
+ /* i2s clk */
+ idx = EP93XX_CLK_I2S_MCLK - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ ret = ep93xx_clk_register_ddiv(clk, "mclk",
+ ddiv_pdata, ARRAY_SIZE(ddiv_pdata),
+ EP93XX_SYSCON_I2SCLKDIV,
+ EP93XX_SYSCON_CLKDIV_ENABLE);
+
+ /* i2s sclk */
+ idx = EP93XX_CLK_I2S_SCLK - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ pdata.hw = &priv->reg[EP93XX_CLK_I2S_MCLK - EP93XX_CLK_UART1].hw;
+ ret = ep93xx_register_div(clk, "sclk", &pdata,
+ EP93XX_SYSCON_I2SCLKDIV,
+ EP93XX_SYSCON_I2SCLKDIV_SENA,
+ 16, /* EP93XX_I2SCLKDIV_SDIV_SHIFT */
+ 1, /* EP93XX_I2SCLKDIV_SDIV_WIDTH */
+ ep93xx_sclk_divisors,
+ ARRAY_SIZE(ep93xx_sclk_divisors));
+
+ /* i2s lrclk */
+ idx = EP93XX_CLK_I2S_LRCLK - EP93XX_CLK_UART1;
+ clk = &priv->reg[idx];
+ clk->idx = idx;
+ pdata.hw = &priv->reg[EP93XX_CLK_I2S_SCLK - EP93XX_CLK_UART1].hw;
+ ret = ep93xx_register_div(clk, "lrclk", &pdata,
+ EP93XX_SYSCON_I2SCLKDIV,
+ EP93XX_SYSCON_I2SCLKDIV_SENA,
+ 17, /* EP93XX_I2SCLKDIV_LRDIV32_SHIFT */
+ 2, /* EP93XX_I2SCLKDIV_LRDIV32_WIDTH */
+ ep93xx_lrclk_divisors,
+ ARRAY_SIZE(ep93xx_lrclk_divisors));
+
+ /* IrDa clk uses same pattern but no init code presents in original clock driver */
+ return devm_of_clk_add_hw_provider(priv->dev, of_clk_ep93xx_get, priv);
+}
+
+static const struct auxiliary_device_id ep93xx_clk_ids[] = {
+ { .name = "soc_ep93xx.clk-ep93xx", .driver_data = 2, },
+ { .name = "soc_ep93xx.clk-ep93xx.e2", .driver_data = 1, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, ep93xx_clk_ids);
+
+static struct auxiliary_driver ep93xx_clk_driver = {
+ .probe = ep93xx_clk_probe,
+ .id_table = ep93xx_clk_ids,
+};
+module_auxiliary_driver(ep93xx_clk_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>");
+MODULE_DESCRIPTION("Clock control for Cirrus EP93xx chips");
diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c
new file mode 100644
index 000000000000..ea1c3d78e7cd
--- /dev/null
+++ b/drivers/clk/clk-eyeq.c
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PLL clock driver for the Mobileye EyeQ5, EyeQ6L and EyeQ6H platforms.
+ *
+ * This controller handles:
+ * - Read-only PLLs, all derived from the same main crystal clock.
+ * - It also exposes divider clocks, those are children to PLLs.
+ * - Fixed factor clocks, children to PLLs.
+ *
+ * Parent clock is expected to be constant. This driver's registers live in a
+ * shared region called OLB. Some PLLs and fixed-factors are initialised early
+ * by of_clk_init(); if so, two clk providers are registered.
+ *
+ * We use eqc_ as prefix, as-in "EyeQ Clock", but way shorter.
+ *
+ * Copyright (C) 2024 Mobileye Vision Technologies Ltd.
+ */
+
+/*
+ * Set pr_fmt() for printing from eqc_early_init().
+ * It is called at of_clk_init() stage (read: really early).
+ */
+#define pr_fmt(fmt) "clk-eyeq: " fmt
+
+#include <linux/array_size.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io-64-nonatomic-hi-lo.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/mobileye,eyeq5-clk.h>
+
+/* In frac mode, it enables fractional noise canceling DAC. Else, no function. */
+#define PCSR0_DAC_EN BIT(0)
+/* Fractional or integer mode */
+#define PCSR0_DSM_EN BIT(1)
+#define PCSR0_PLL_EN BIT(2)
+/* All clocks output held at 0 */
+#define PCSR0_FOUTPOSTDIV_EN BIT(3)
+#define PCSR0_POST_DIV1 GENMASK(6, 4)
+#define PCSR0_POST_DIV2 GENMASK(9, 7)
+#define PCSR0_REF_DIV GENMASK(15, 10)
+#define PCSR0_INTIN GENMASK(27, 16)
+#define PCSR0_BYPASS BIT(28)
+/* Bits 30..29 are reserved */
+#define PCSR0_PLL_LOCKED BIT(31)
+
+#define PCSR1_RESET BIT(0)
+#define PCSR1_SSGC_DIV GENMASK(4, 1)
+/* Spread amplitude (% = 0.1 * SPREAD[4:0]) */
+#define PCSR1_SPREAD GENMASK(9, 5)
+#define PCSR1_DIS_SSCG BIT(10)
+/* Down-spread or center-spread */
+#define PCSR1_DOWN_SPREAD BIT(11)
+#define PCSR1_FRAC_IN GENMASK(31, 12)
+
+struct eqc_pll {
+ unsigned int index;
+ const char *name;
+ unsigned int reg64;
+};
+
+/*
+ * Divider clock. Divider is 2*(v+1), with v the register value.
+ * Min divider is 2, max is 2*(2^width).
+ */
+struct eqc_div {
+ unsigned int index;
+ const char *name;
+ unsigned int parent;
+ unsigned int reg;
+ u8 shift;
+ u8 width;
+};
+
+struct eqc_fixed_factor {
+ unsigned int index;
+ const char *name;
+ unsigned int mult;
+ unsigned int div;
+ unsigned int parent;
+};
+
+struct eqc_match_data {
+ unsigned int pll_count;
+ const struct eqc_pll *plls;
+
+ unsigned int div_count;
+ const struct eqc_div *divs;
+
+ unsigned int fixed_factor_count;
+ const struct eqc_fixed_factor *fixed_factors;
+
+ const char *reset_auxdev_name;
+ const char *pinctrl_auxdev_name;
+
+ unsigned int early_clk_count;
+};
+
+struct eqc_early_match_data {
+ unsigned int early_pll_count;
+ const struct eqc_pll *early_plls;
+
+ unsigned int early_fixed_factor_count;
+ const struct eqc_fixed_factor *early_fixed_factors;
+
+ /*
+ * We want our of_xlate callback to EPROBE_DEFER instead of dev_err()
+ * and EINVAL. For that, we must know the total clock count.
+ */
+ unsigned int late_clk_count;
+};
+
+/*
+ * Both factors (mult and div) must fit in 32 bits. When an operation overflows,
+ * this function throws away low bits so that factors still fit in 32 bits.
+ *
+ * Precision loss depends on amplitude of mult and div. Worst theoretical
+ * loss is: (UINT_MAX+1) / UINT_MAX - 1 = 2.3e-10.
+ * This is 1Hz every 4.3GHz.
+ */
+static void eqc_pll_downshift_factors(unsigned long *mult, unsigned long *div)
+{
+ unsigned long biggest;
+ unsigned int shift;
+
+ /* This function can be removed if mult/div switch to unsigned long. */
+ static_assert(sizeof_field(struct clk_fixed_factor, mult) == sizeof(unsigned int));
+ static_assert(sizeof_field(struct clk_fixed_factor, div) == sizeof(unsigned int));
+
+ /* No overflow, nothing to be done. */
+ if (*mult <= UINT_MAX && *div <= UINT_MAX)
+ return;
+
+ /*
+ * Compute the shift required to bring the biggest factor into unsigned
+ * int range. That is, shift its highest set bit to the unsigned int
+ * most significant bit.
+ */
+ biggest = max(*mult, *div);
+ shift = __fls(biggest) - (BITS_PER_BYTE * sizeof(unsigned int)) + 1;
+
+ *mult >>= shift;
+ *div >>= shift;
+}
+
+static int eqc_pll_parse_registers(u32 r0, u32 r1, unsigned long *mult,
+ unsigned long *div, unsigned long *acc)
+{
+ u32 spread;
+
+ if (r0 & PCSR0_BYPASS) {
+ *mult = 1;
+ *div = 1;
+ *acc = 0;
+ return 0;
+ }
+
+ if (!(r0 & PCSR0_PLL_LOCKED))
+ return -EINVAL;
+
+ *mult = FIELD_GET(PCSR0_INTIN, r0);
+ *div = FIELD_GET(PCSR0_REF_DIV, r0);
+ if (r0 & PCSR0_FOUTPOSTDIV_EN)
+ *div *= FIELD_GET(PCSR0_POST_DIV1, r0) * FIELD_GET(PCSR0_POST_DIV2, r0);
+
+ /* Fractional mode, in 2^20 (0x100000) parts. */
+ if (r0 & PCSR0_DSM_EN) {
+ *div *= (1ULL << 20);
+ *mult = *mult * (1ULL << 20) + FIELD_GET(PCSR1_FRAC_IN, r1);
+ }
+
+ if (!*mult || !*div)
+ return -EINVAL;
+
+ if (r1 & (PCSR1_RESET | PCSR1_DIS_SSCG)) {
+ *acc = 0;
+ return 0;
+ }
+
+ /*
+ * Spread spectrum.
+ *
+ * Spread is 1/1000 parts of frequency, accuracy is half of
+ * that. To get accuracy, convert to ppb (parts per billion).
+ *
+ * acc = spread * 1e6 / 2
+ * with acc in parts per billion and,
+ * spread in parts per thousand.
+ */
+ spread = FIELD_GET(PCSR1_SPREAD, r1);
+ *acc = spread * 500000;
+
+ if (r1 & PCSR1_DOWN_SPREAD) {
+ /*
+ * Downspreading: the central frequency is half a
+ * spread lower.
+ */
+ *mult *= 2000 - spread;
+ *div *= 2000;
+
+ /*
+ * Previous operation might overflow 32 bits. If it
+ * does, throw away the least amount of low bits.
+ */
+ eqc_pll_downshift_factors(mult, div);
+ }
+
+ return 0;
+}
+
+static void eqc_probe_init_plls(struct device *dev, const struct eqc_match_data *data,
+ void __iomem *base, struct clk_hw_onecell_data *cells)
+{
+ unsigned long mult, div, acc;
+ const struct eqc_pll *pll;
+ struct clk_hw *hw;
+ unsigned int i;
+ u32 r0, r1;
+ u64 val;
+ int ret;
+
+ for (i = 0; i < data->pll_count; i++) {
+ pll = &data->plls[i];
+
+ val = readq(base + pll->reg64);
+ r0 = val;
+ r1 = val >> 32;
+
+ ret = eqc_pll_parse_registers(r0, r1, &mult, &div, &acc);
+ if (ret) {
+ dev_warn(dev, "failed parsing state of %s\n", pll->name);
+ cells->hws[pll->index] = ERR_PTR(ret);
+ continue;
+ }
+
+ hw = clk_hw_register_fixed_factor_with_accuracy_fwname(dev,
+ dev->of_node, pll->name, "ref", 0, mult, div, acc);
+ cells->hws[pll->index] = hw;
+ if (IS_ERR(hw))
+ dev_warn(dev, "failed registering %s: %pe\n", pll->name, hw);
+ }
+}
+
+static void eqc_probe_init_divs(struct device *dev, const struct eqc_match_data *data,
+ void __iomem *base, struct clk_hw_onecell_data *cells)
+{
+ struct clk_parent_data parent_data = { };
+ const struct eqc_div *div;
+ struct clk_hw *parent;
+ void __iomem *reg;
+ struct clk_hw *hw;
+ unsigned int i;
+
+ for (i = 0; i < data->div_count; i++) {
+ div = &data->divs[i];
+ reg = base + div->reg;
+ parent = cells->hws[div->parent];
+
+ if (IS_ERR(parent)) {
+ /* Parent is in early clk provider. */
+ parent_data.index = div->parent;
+ parent_data.hw = NULL;
+ } else {
+ /* Avoid clock lookup when we already have the hw reference. */
+ parent_data.index = 0;
+ parent_data.hw = parent;
+ }
+
+ hw = clk_hw_register_divider_table_parent_data(dev, div->name,
+ &parent_data, 0, reg, div->shift, div->width,
+ CLK_DIVIDER_EVEN_INTEGERS, NULL, NULL);
+ cells->hws[div->index] = hw;
+ if (IS_ERR(hw))
+ dev_warn(dev, "failed registering %s: %pe\n",
+ div->name, hw);
+ }
+}
+
+static void eqc_probe_init_fixed_factors(struct device *dev,
+ const struct eqc_match_data *data,
+ struct clk_hw_onecell_data *cells)
+{
+ const struct eqc_fixed_factor *ff;
+ struct clk_hw *hw, *parent_hw;
+ unsigned int i;
+
+ for (i = 0; i < data->fixed_factor_count; i++) {
+ ff = &data->fixed_factors[i];
+ parent_hw = cells->hws[ff->parent];
+
+ if (IS_ERR(parent_hw)) {
+ /* Parent is in early clk provider. */
+ hw = clk_hw_register_fixed_factor_index(dev, ff->name,
+ ff->parent, 0, ff->mult, ff->div);
+ } else {
+ /* Avoid clock lookup when we already have the hw reference. */
+ hw = clk_hw_register_fixed_factor_parent_hw(dev, ff->name,
+ parent_hw, 0, ff->mult, ff->div);
+ }
+
+ cells->hws[ff->index] = hw;
+ if (IS_ERR(hw))
+ dev_warn(dev, "failed registering %s: %pe\n",
+ ff->name, hw);
+ }
+}
+
+static void eqc_auxdev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ kfree(adev);
+}
+
+static int eqc_auxdev_create(struct device *dev, void __iomem *base,
+ const char *name, u32 id)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->name = name;
+ adev->dev.parent = dev;
+ adev->dev.platform_data = (void __force *)base;
+ adev->dev.release = eqc_auxdev_release;
+ adev->id = id;
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(adev);
+ if (ret)
+ auxiliary_device_uninit(adev);
+
+ return ret;
+}
+
+static int eqc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct eqc_match_data *data;
+ struct clk_hw_onecell_data *cells;
+ unsigned int i, clk_count;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ data = device_get_match_data(dev);
+ if (!data)
+ return 0; /* No clocks nor auxdevs, we are done. */
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base)
+ return -ENOMEM;
+
+ /* Init optional reset auxiliary device. */
+ if (data->reset_auxdev_name) {
+ ret = eqc_auxdev_create(dev, base, data->reset_auxdev_name, 0);
+ if (ret)
+ dev_warn(dev, "failed creating auxiliary device %s.%s: %d\n",
+ KBUILD_MODNAME, data->reset_auxdev_name, ret);
+ }
+
+ /* Init optional pinctrl auxiliary device. */
+ if (data->pinctrl_auxdev_name) {
+ ret = eqc_auxdev_create(dev, base, data->pinctrl_auxdev_name, 0);
+ if (ret)
+ dev_warn(dev, "failed creating auxiliary device %s.%s: %d\n",
+ KBUILD_MODNAME, data->pinctrl_auxdev_name, ret);
+ }
+
+ if (data->pll_count + data->div_count + data->fixed_factor_count == 0)
+ return 0; /* Zero clocks, we are done. */
+
+ clk_count = data->pll_count + data->div_count +
+ data->fixed_factor_count + data->early_clk_count;
+ cells = kzalloc(struct_size(cells, hws, clk_count), GFP_KERNEL);
+ if (!cells)
+ return -ENOMEM;
+
+ cells->num = clk_count;
+
+ /* Early PLLs are marked as errors: the early provider will get queried. */
+ for (i = 0; i < clk_count; i++)
+ cells->hws[i] = ERR_PTR(-EINVAL);
+
+ eqc_probe_init_plls(dev, data, base, cells);
+
+ eqc_probe_init_divs(dev, data, base, cells);
+
+ eqc_probe_init_fixed_factors(dev, data, cells);
+
+ return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells);
+}
+
+/* Required early for GIC timer (pll-cpu) and UARTs (pll-per). */
+static const struct eqc_pll eqc_eyeq5_early_plls[] = {
+ { .index = EQ5C_PLL_CPU, .name = "pll-cpu", .reg64 = 0x02C },
+ { .index = EQ5C_PLL_PER, .name = "pll-per", .reg64 = 0x05C },
+};
+
+static const struct eqc_pll eqc_eyeq5_plls[] = {
+ { .index = EQ5C_PLL_VMP, .name = "pll-vmp", .reg64 = 0x034 },
+ { .index = EQ5C_PLL_PMA, .name = "pll-pma", .reg64 = 0x03C },
+ { .index = EQ5C_PLL_VDI, .name = "pll-vdi", .reg64 = 0x044 },
+ { .index = EQ5C_PLL_DDR0, .name = "pll-ddr0", .reg64 = 0x04C },
+ { .index = EQ5C_PLL_PCI, .name = "pll-pci", .reg64 = 0x054 },
+ { .index = EQ5C_PLL_PMAC, .name = "pll-pmac", .reg64 = 0x064 },
+ { .index = EQ5C_PLL_MPC, .name = "pll-mpc", .reg64 = 0x06C },
+ { .index = EQ5C_PLL_DDR1, .name = "pll-ddr1", .reg64 = 0x074 },
+};
+
+enum {
+ /*
+ * EQ5C_PLL_CPU children.
+ * EQ5C_PER_OCC_PCI is the last clock exposed in dt-bindings.
+ */
+ EQ5C_CPU_OCC = EQ5C_PER_OCC_PCI + 1,
+ EQ5C_CPU_SI_CSS0,
+ EQ5C_CPU_CPC,
+ EQ5C_CPU_CM,
+ EQ5C_CPU_MEM,
+ EQ5C_CPU_OCC_ISRAM,
+ EQ5C_CPU_ISRAM,
+ EQ5C_CPU_OCC_DBU,
+ EQ5C_CPU_SI_DBU_TP,
+
+ /*
+ * EQ5C_PLL_VDI children.
+ */
+ EQ5C_VDI_OCC_VDI,
+ EQ5C_VDI_VDI,
+ EQ5C_VDI_OCC_CAN_SER,
+ EQ5C_VDI_CAN_SER,
+ EQ5C_VDI_I2C_SER,
+
+ /*
+ * EQ5C_PLL_PER children.
+ */
+ EQ5C_PER_PERIPH,
+ EQ5C_PER_CAN,
+ EQ5C_PER_TIMER,
+ EQ5C_PER_CCF,
+ EQ5C_PER_OCC_MJPEG,
+ EQ5C_PER_HSM,
+ EQ5C_PER_MJPEG,
+ EQ5C_PER_FCMU_A,
+};
+
+static const struct eqc_fixed_factor eqc_eyeq5_early_fixed_factors[] = {
+ /* EQ5C_PLL_CPU children */
+ { EQ5C_CPU_OCC, "occ-cpu", 1, 1, EQ5C_PLL_CPU },
+ { EQ5C_CPU_SI_CSS0, "si-css0", 1, 1, EQ5C_CPU_OCC },
+ { EQ5C_CPU_CORE0, "core0", 1, 1, EQ5C_CPU_SI_CSS0 },
+ { EQ5C_CPU_CORE1, "core1", 1, 1, EQ5C_CPU_SI_CSS0 },
+ { EQ5C_CPU_CORE2, "core2", 1, 1, EQ5C_CPU_SI_CSS0 },
+ { EQ5C_CPU_CORE3, "core3", 1, 1, EQ5C_CPU_SI_CSS0 },
+
+ /* EQ5C_PLL_PER children */
+ { EQ5C_PER_OCC, "occ-periph", 1, 16, EQ5C_PLL_PER },
+ { EQ5C_PER_UART, "uart", 1, 1, EQ5C_PER_OCC },
+};
+
+static const struct eqc_fixed_factor eqc_eyeq5_fixed_factors[] = {
+ /* EQ5C_PLL_CPU children */
+ { EQ5C_CPU_CPC, "cpc", 1, 1, EQ5C_CPU_SI_CSS0 },
+ { EQ5C_CPU_CM, "cm", 1, 1, EQ5C_CPU_SI_CSS0 },
+ { EQ5C_CPU_MEM, "mem", 1, 1, EQ5C_CPU_SI_CSS0 },
+ { EQ5C_CPU_OCC_ISRAM, "occ-isram", 1, 2, EQ5C_PLL_CPU },
+ { EQ5C_CPU_ISRAM, "isram", 1, 1, EQ5C_CPU_OCC_ISRAM },
+ { EQ5C_CPU_OCC_DBU, "occ-dbu", 1, 10, EQ5C_PLL_CPU },
+ { EQ5C_CPU_SI_DBU_TP, "si-dbu-tp", 1, 1, EQ5C_CPU_OCC_DBU },
+
+ /* EQ5C_PLL_VDI children */
+ { EQ5C_VDI_OCC_VDI, "occ-vdi", 1, 2, EQ5C_PLL_VDI },
+ { EQ5C_VDI_VDI, "vdi", 1, 1, EQ5C_VDI_OCC_VDI },
+ { EQ5C_VDI_OCC_CAN_SER, "occ-can-ser", 1, 16, EQ5C_PLL_VDI },
+ { EQ5C_VDI_CAN_SER, "can-ser", 1, 1, EQ5C_VDI_OCC_CAN_SER },
+ { EQ5C_VDI_I2C_SER, "i2c-ser", 1, 20, EQ5C_PLL_VDI },
+
+ /* EQ5C_PLL_PER children */
+ { EQ5C_PER_PERIPH, "periph", 1, 1, EQ5C_PER_OCC },
+ { EQ5C_PER_CAN, "can", 1, 1, EQ5C_PER_OCC },
+ { EQ5C_PER_SPI, "spi", 1, 1, EQ5C_PER_OCC },
+ { EQ5C_PER_I2C, "i2c", 1, 1, EQ5C_PER_OCC },
+ { EQ5C_PER_TIMER, "timer", 1, 1, EQ5C_PER_OCC },
+ { EQ5C_PER_GPIO, "gpio", 1, 1, EQ5C_PER_OCC },
+ { EQ5C_PER_EMMC, "emmc-sys", 1, 10, EQ5C_PLL_PER },
+ { EQ5C_PER_CCF, "ccf-ctrl", 1, 4, EQ5C_PLL_PER },
+ { EQ5C_PER_OCC_MJPEG, "occ-mjpeg", 1, 2, EQ5C_PLL_PER },
+ { EQ5C_PER_HSM, "hsm", 1, 1, EQ5C_PER_OCC_MJPEG },
+ { EQ5C_PER_MJPEG, "mjpeg", 1, 1, EQ5C_PER_OCC_MJPEG },
+ { EQ5C_PER_FCMU_A, "fcmu-a", 1, 20, EQ5C_PLL_PER },
+ { EQ5C_PER_OCC_PCI, "occ-pci-sys", 1, 8, EQ5C_PLL_PER },
+};
+
+static const struct eqc_div eqc_eyeq5_divs[] = {
+ {
+ .index = EQ5C_DIV_OSPI,
+ .name = "div-ospi",
+ .parent = EQ5C_PLL_PER,
+ .reg = 0x11C,
+ .shift = 0,
+ .width = 4,
+ },
+};
+
+static const struct eqc_early_match_data eqc_eyeq5_early_match_data __initconst = {
+ .early_pll_count = ARRAY_SIZE(eqc_eyeq5_early_plls),
+ .early_plls = eqc_eyeq5_early_plls,
+
+ .early_fixed_factor_count = ARRAY_SIZE(eqc_eyeq5_early_fixed_factors),
+ .early_fixed_factors = eqc_eyeq5_early_fixed_factors,
+
+ .late_clk_count = ARRAY_SIZE(eqc_eyeq5_plls) + ARRAY_SIZE(eqc_eyeq5_divs) +
+ ARRAY_SIZE(eqc_eyeq5_fixed_factors),
+};
+
+static const struct eqc_match_data eqc_eyeq5_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq5_plls),
+ .plls = eqc_eyeq5_plls,
+
+ .div_count = ARRAY_SIZE(eqc_eyeq5_divs),
+ .divs = eqc_eyeq5_divs,
+
+ .fixed_factor_count = ARRAY_SIZE(eqc_eyeq5_fixed_factors),
+ .fixed_factors = eqc_eyeq5_fixed_factors,
+
+ .reset_auxdev_name = "reset",
+ .pinctrl_auxdev_name = "pinctrl",
+
+ .early_clk_count = ARRAY_SIZE(eqc_eyeq5_early_plls) +
+ ARRAY_SIZE(eqc_eyeq5_early_fixed_factors),
+};
+
+static const struct eqc_pll eqc_eyeq6l_plls[] = {
+ { .index = EQ6LC_PLL_DDR, .name = "pll-ddr", .reg64 = 0x02C },
+ { .index = EQ6LC_PLL_CPU, .name = "pll-cpu", .reg64 = 0x034 }, /* also acc */
+ { .index = EQ6LC_PLL_PER, .name = "pll-per", .reg64 = 0x03C },
+ { .index = EQ6LC_PLL_VDI, .name = "pll-vdi", .reg64 = 0x044 },
+};
+
+static const struct eqc_match_data eqc_eyeq6l_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq6l_plls),
+ .plls = eqc_eyeq6l_plls,
+
+ .reset_auxdev_name = "reset",
+};
+
+static const struct eqc_match_data eqc_eyeq6h_west_match_data = {
+ .reset_auxdev_name = "reset_west",
+};
+
+static const struct eqc_pll eqc_eyeq6h_east_plls[] = {
+ { .index = 0, .name = "pll-east", .reg64 = 0x074 },
+};
+
+static const struct eqc_match_data eqc_eyeq6h_east_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq6h_east_plls),
+ .plls = eqc_eyeq6h_east_plls,
+
+ .reset_auxdev_name = "reset_east",
+};
+
+static const struct eqc_pll eqc_eyeq6h_south_plls[] = {
+ { .index = EQ6HC_SOUTH_PLL_VDI, .name = "pll-vdi", .reg64 = 0x000 },
+ { .index = EQ6HC_SOUTH_PLL_PCIE, .name = "pll-pcie", .reg64 = 0x008 },
+ { .index = EQ6HC_SOUTH_PLL_PER, .name = "pll-per", .reg64 = 0x010 },
+ { .index = EQ6HC_SOUTH_PLL_ISP, .name = "pll-isp", .reg64 = 0x018 },
+};
+
+static const struct eqc_div eqc_eyeq6h_south_divs[] = {
+ {
+ .index = EQ6HC_SOUTH_DIV_EMMC,
+ .name = "div-emmc",
+ .parent = EQ6HC_SOUTH_PLL_PER,
+ .reg = 0x070,
+ .shift = 4,
+ .width = 4,
+ },
+ {
+ .index = EQ6HC_SOUTH_DIV_OSPI_REF,
+ .name = "div-ospi-ref",
+ .parent = EQ6HC_SOUTH_PLL_PER,
+ .reg = 0x090,
+ .shift = 4,
+ .width = 4,
+ },
+ {
+ .index = EQ6HC_SOUTH_DIV_OSPI_SYS,
+ .name = "div-ospi-sys",
+ .parent = EQ6HC_SOUTH_PLL_PER,
+ .reg = 0x090,
+ .shift = 8,
+ .width = 1,
+ },
+ {
+ .index = EQ6HC_SOUTH_DIV_TSU,
+ .name = "div-tsu",
+ .parent = EQ6HC_SOUTH_PLL_PCIE,
+ .reg = 0x098,
+ .shift = 4,
+ .width = 8,
+ },
+};
+
+static const struct eqc_match_data eqc_eyeq6h_south_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq6h_south_plls),
+ .plls = eqc_eyeq6h_south_plls,
+
+ .div_count = ARRAY_SIZE(eqc_eyeq6h_south_divs),
+ .divs = eqc_eyeq6h_south_divs,
+};
+
+static const struct eqc_pll eqc_eyeq6h_ddr0_plls[] = {
+ { .index = 0, .name = "pll-ddr0", .reg64 = 0x074 },
+};
+
+static const struct eqc_match_data eqc_eyeq6h_ddr0_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq6h_ddr0_plls),
+ .plls = eqc_eyeq6h_ddr0_plls,
+};
+
+static const struct eqc_pll eqc_eyeq6h_ddr1_plls[] = {
+ { .index = 0, .name = "pll-ddr1", .reg64 = 0x074 },
+};
+
+static const struct eqc_match_data eqc_eyeq6h_ddr1_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq6h_ddr1_plls),
+ .plls = eqc_eyeq6h_ddr1_plls,
+};
+
+static const struct eqc_pll eqc_eyeq6h_acc_plls[] = {
+ { .index = EQ6HC_ACC_PLL_XNN, .name = "pll-xnn", .reg64 = 0x040 },
+ { .index = EQ6HC_ACC_PLL_VMP, .name = "pll-vmp", .reg64 = 0x050 },
+ { .index = EQ6HC_ACC_PLL_PMA, .name = "pll-pma", .reg64 = 0x05C },
+ { .index = EQ6HC_ACC_PLL_MPC, .name = "pll-mpc", .reg64 = 0x068 },
+ { .index = EQ6HC_ACC_PLL_NOC, .name = "pll-noc", .reg64 = 0x070 },
+};
+
+static const struct eqc_match_data eqc_eyeq6h_acc_match_data = {
+ .pll_count = ARRAY_SIZE(eqc_eyeq6h_acc_plls),
+ .plls = eqc_eyeq6h_acc_plls,
+
+ .reset_auxdev_name = "reset_acc",
+};
+
+static const struct of_device_id eqc_match_table[] = {
+ { .compatible = "mobileye,eyeq5-olb", .data = &eqc_eyeq5_match_data },
+ { .compatible = "mobileye,eyeq6l-olb", .data = &eqc_eyeq6l_match_data },
+ { .compatible = "mobileye,eyeq6h-west-olb", .data = &eqc_eyeq6h_west_match_data },
+ { .compatible = "mobileye,eyeq6h-east-olb", .data = &eqc_eyeq6h_east_match_data },
+ { .compatible = "mobileye,eyeq6h-south-olb", .data = &eqc_eyeq6h_south_match_data },
+ { .compatible = "mobileye,eyeq6h-ddr0-olb", .data = &eqc_eyeq6h_ddr0_match_data },
+ { .compatible = "mobileye,eyeq6h-ddr1-olb", .data = &eqc_eyeq6h_ddr1_match_data },
+ { .compatible = "mobileye,eyeq6h-acc-olb", .data = &eqc_eyeq6h_acc_match_data },
+ {}
+};
+
+static struct platform_driver eqc_driver = {
+ .probe = eqc_probe,
+ .driver = {
+ .name = "clk-eyeq",
+ .of_match_table = eqc_match_table,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(eqc_driver);
+
+/* Required early for GIC timer. */
+static const struct eqc_pll eqc_eyeq6h_central_early_plls[] = {
+ { .index = EQ6HC_CENTRAL_PLL_CPU, .name = "pll-cpu", .reg64 = 0x02C },
+};
+
+static const struct eqc_fixed_factor eqc_eyeq6h_central_early_fixed_factors[] = {
+ { EQ6HC_CENTRAL_CPU_OCC, "occ-cpu", 1, 1, EQ6HC_CENTRAL_PLL_CPU },
+};
+
+static const struct eqc_early_match_data eqc_eyeq6h_central_early_match_data __initconst = {
+ .early_pll_count = ARRAY_SIZE(eqc_eyeq6h_central_early_plls),
+ .early_plls = eqc_eyeq6h_central_early_plls,
+
+ .early_fixed_factor_count = ARRAY_SIZE(eqc_eyeq6h_central_early_fixed_factors),
+ .early_fixed_factors = eqc_eyeq6h_central_early_fixed_factors,
+};
+
+/* Required early for UART. */
+static const struct eqc_pll eqc_eyeq6h_west_early_plls[] = {
+ { .index = EQ6HC_WEST_PLL_PER, .name = "pll-west", .reg64 = 0x074 },
+};
+
+static const struct eqc_fixed_factor eqc_eyeq6h_west_early_fixed_factors[] = {
+ { EQ6HC_WEST_PER_OCC, "west-per-occ", 1, 10, EQ6HC_WEST_PLL_PER },
+ { EQ6HC_WEST_PER_UART, "west-per-uart", 1, 1, EQ6HC_WEST_PER_OCC },
+};
+
+static const struct eqc_early_match_data eqc_eyeq6h_west_early_match_data __initconst = {
+ .early_pll_count = ARRAY_SIZE(eqc_eyeq6h_west_early_plls),
+ .early_plls = eqc_eyeq6h_west_early_plls,
+
+ .early_fixed_factor_count = ARRAY_SIZE(eqc_eyeq6h_west_early_fixed_factors),
+ .early_fixed_factors = eqc_eyeq6h_west_early_fixed_factors,
+};
+
+static void __init eqc_early_init(struct device_node *np,
+ const struct eqc_early_match_data *early_data)
+{
+ struct clk_hw_onecell_data *cells;
+ unsigned int i, clk_count;
+ void __iomem *base;
+ int ret;
+
+ clk_count = early_data->early_pll_count + early_data->early_fixed_factor_count +
+ early_data->late_clk_count;
+ cells = kzalloc(struct_size(cells, hws, clk_count), GFP_KERNEL);
+ if (!cells) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ cells->num = clk_count;
+
+ /*
+ * Mark all clocks as deferred; some are registered here, the rest at
+ * platform device probe.
+ *
+ * Once the platform device is probed, its provider will take priority
+ * when looking up clocks.
+ */
+ for (i = 0; i < clk_count; i++)
+ cells->hws[i] = ERR_PTR(-EPROBE_DEFER);
+
+ /* Offsets (reg64) of early PLLs are relative to OLB block. */
+ base = of_iomap(np, 0);
+ if (!base) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ for (i = 0; i < early_data->early_pll_count; i++) {
+ const struct eqc_pll *pll = &early_data->early_plls[i];
+ unsigned long mult, div, acc;
+ struct clk_hw *hw;
+ u32 r0, r1;
+ u64 val;
+
+ val = readq(base + pll->reg64);
+ r0 = val;
+ r1 = val >> 32;
+
+ ret = eqc_pll_parse_registers(r0, r1, &mult, &div, &acc);
+ if (ret) {
+ pr_err("failed parsing state of %s\n", pll->name);
+ goto err;
+ }
+
+ hw = clk_hw_register_fixed_factor_with_accuracy_fwname(NULL,
+ np, pll->name, "ref", 0, mult, div, acc);
+ cells->hws[pll->index] = hw;
+ if (IS_ERR(hw)) {
+ pr_err("failed registering %s: %pe\n", pll->name, hw);
+ ret = PTR_ERR(hw);
+ goto err;
+ }
+ }
+
+ for (i = 0; i < early_data->early_fixed_factor_count; i++) {
+ const struct eqc_fixed_factor *ff = &early_data->early_fixed_factors[i];
+ struct clk_hw *parent_hw = cells->hws[ff->parent];
+ struct clk_hw *hw;
+
+ hw = clk_hw_register_fixed_factor_parent_hw(NULL, ff->name,
+ parent_hw, 0, ff->mult, ff->div);
+ cells->hws[ff->index] = hw;
+ if (IS_ERR(hw)) {
+ pr_err("failed registering %s: %pe\n", ff->name, hw);
+ ret = PTR_ERR(hw);
+ goto err;
+ }
+ }
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells);
+ if (ret) {
+ pr_err("failed registering clk provider: %d\n", ret);
+ goto err;
+ }
+
+ return;
+
+err:
+ /*
+ * We are doomed. The system will not be able to boot.
+ *
+ * Let's still try to be good citizens by freeing resources and print
+ * a last error message that might help debugging.
+ */
+
+ pr_err("failed clk init: %d\n", ret);
+
+ if (cells) {
+ of_clk_del_provider(np);
+
+ for (i = 0; i < early_data->early_pll_count; i++) {
+ const struct eqc_pll *pll = &early_data->early_plls[i];
+ struct clk_hw *hw = cells->hws[pll->index];
+
+ if (!IS_ERR_OR_NULL(hw))
+ clk_hw_unregister_fixed_factor(hw);
+ }
+
+ kfree(cells);
+ }
+}
+
+static void __init eqc_eyeq5_early_init(struct device_node *np)
+{
+ eqc_early_init(np, &eqc_eyeq5_early_match_data);
+}
+CLK_OF_DECLARE_DRIVER(eqc_eyeq5, "mobileye,eyeq5-olb", eqc_eyeq5_early_init);
+
+static void __init eqc_eyeq6h_central_early_init(struct device_node *np)
+{
+ eqc_early_init(np, &eqc_eyeq6h_central_early_match_data);
+}
+CLK_OF_DECLARE_DRIVER(eqc_eyeq6h_central, "mobileye,eyeq6h-central-olb",
+ eqc_eyeq6h_central_early_init);
+
+static void __init eqc_eyeq6h_west_early_init(struct device_node *np)
+{
+ eqc_early_init(np, &eqc_eyeq6h_west_early_match_data);
+}
+CLK_OF_DECLARE_DRIVER(eqc_eyeq6h_west, "mobileye,eyeq6h-west-olb",
+ eqc_eyeq6h_west_early_init);
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index f734e34735a9..de658c9e4c53 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -30,19 +30,21 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
return (unsigned long)rate;
}
-static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_factor_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
unsigned long best_parent;
- best_parent = (rate / fix->mult) * fix->div;
- *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
+ best_parent = (req->rate / fix->mult) * fix->div;
+ req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
}
- return (*prate / fix->div) * fix->mult;
+ req->rate = (req->best_parent_rate / fix->div) * fix->mult;
+
+ return 0;
}
static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -50,17 +52,29 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
{
/*
* We must report success but we can do so unconditionally because
- * clk_factor_round_rate returns values that ensure this call is a
+ * clk_factor_determine_rate returns values that ensure this call is a
* nop.
*/
return 0;
}
+static unsigned long clk_factor_recalc_accuracy(struct clk_hw *hw,
+ unsigned long parent_accuracy)
+{
+ struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
+
+ if (fix->flags & CLK_FIXED_FACTOR_FIXED_ACCURACY)
+ return fix->acc;
+
+ return parent_accuracy;
+}
+
const struct clk_ops clk_fixed_factor_ops = {
- .round_rate = clk_factor_round_rate,
+ .determine_rate = clk_factor_determine_rate,
.set_rate = clk_factor_set_rate,
.recalc_rate = clk_factor_recalc_rate,
+ .recalc_accuracy = clk_factor_recalc_accuracy,
};
EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
@@ -79,13 +93,12 @@ static void devm_clk_hw_register_fixed_factor_release(struct device *dev, void *
static struct clk_hw *
__clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
const char *name, const char *parent_name,
- const struct clk_hw *parent_hw, int index,
+ const struct clk_hw *parent_hw, const struct clk_parent_data *pdata,
unsigned long flags, unsigned int mult, unsigned int div,
- bool devm)
+ unsigned long acc, unsigned int fixflags, bool devm)
{
struct clk_fixed_factor *fix;
struct clk_init_data init = { };
- struct clk_parent_data pdata = { .index = index };
struct clk_hw *hw;
int ret;
@@ -105,6 +118,8 @@ __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
fix->mult = mult;
fix->div = div;
fix->hw.init = &init;
+ fix->acc = acc;
+ fix->flags = fixflags;
init.name = name;
init.ops = &clk_fixed_factor_ops;
@@ -114,7 +129,7 @@ __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
else if (parent_hw)
init.parent_hws = &parent_hw;
else
- init.parent_data = &pdata;
+ init.parent_data = pdata;
init.num_parents = 1;
hw = &fix->hw;
@@ -151,8 +166,10 @@ struct clk_hw *devm_clk_hw_register_fixed_factor_index(struct device *dev,
const char *name, unsigned int index, unsigned long flags,
unsigned int mult, unsigned int div)
{
- return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, NULL, index,
- flags, mult, div, true);
+ const struct clk_parent_data pdata = { .index = index };
+
+ return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, NULL, &pdata,
+ flags, mult, div, 0, 0, true);
}
EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_index);
@@ -173,8 +190,10 @@ struct clk_hw *devm_clk_hw_register_fixed_factor_parent_hw(struct device *dev,
const char *name, const struct clk_hw *parent_hw,
unsigned long flags, unsigned int mult, unsigned int div)
{
+ const struct clk_parent_data pdata = { .index = -1 };
+
return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, parent_hw,
- -1, flags, mult, div, true);
+ &pdata, flags, mult, div, 0, 0, true);
}
EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_parent_hw);
@@ -182,9 +201,10 @@ struct clk_hw *clk_hw_register_fixed_factor_parent_hw(struct device *dev,
const char *name, const struct clk_hw *parent_hw,
unsigned long flags, unsigned int mult, unsigned int div)
{
- return __clk_hw_register_fixed_factor(dev, NULL, name, NULL,
- parent_hw, -1, flags, mult, div,
- false);
+ const struct clk_parent_data pdata = { .index = -1 };
+
+ return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, parent_hw,
+ &pdata, flags, mult, div, 0, 0, false);
}
EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_parent_hw);
@@ -192,11 +212,48 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
- return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL, -1,
- flags, mult, div, false);
+ const struct clk_parent_data pdata = { .index = -1 };
+
+ return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL,
+ &pdata, flags, mult, div, 0, 0, false);
}
EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
+struct clk_hw *clk_hw_register_fixed_factor_fwname(struct device *dev,
+ struct device_node *np, const char *name, const char *fw_name,
+ unsigned long flags, unsigned int mult, unsigned int div)
+{
+ const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name };
+
+ return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL,
+ &pdata, flags, mult, div, 0, 0, false);
+}
+EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_fwname);
+
+struct clk_hw *clk_hw_register_fixed_factor_with_accuracy_fwname(struct device *dev,
+ struct device_node *np, const char *name, const char *fw_name,
+ unsigned long flags, unsigned int mult, unsigned int div,
+ unsigned long acc)
+{
+ const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name };
+
+ return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL,
+ &pdata, flags, mult, div, acc,
+ CLK_FIXED_FACTOR_FIXED_ACCURACY, false);
+}
+EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_with_accuracy_fwname);
+
+struct clk_hw *clk_hw_register_fixed_factor_index(struct device *dev,
+ const char *name, unsigned int index, unsigned long flags,
+ unsigned int mult, unsigned int div)
+{
+ const struct clk_parent_data pdata = { .index = index };
+
+ return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, NULL, &pdata,
+ flags, mult, div, 0, 0, false);
+}
+EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_index);
+
struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
@@ -239,16 +296,43 @@ struct clk_hw *devm_clk_hw_register_fixed_factor(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
- return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL, -1,
- flags, mult, div, true);
+ const struct clk_parent_data pdata = { .index = -1 };
+
+ return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL,
+ &pdata, flags, mult, div, 0, 0, true);
}
EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor);
+struct clk_hw *devm_clk_hw_register_fixed_factor_fwname(struct device *dev,
+ struct device_node *np, const char *name, const char *fw_name,
+ unsigned long flags, unsigned int mult, unsigned int div)
+{
+ const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name };
+
+ return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL,
+ &pdata, flags, mult, div, 0, 0, true);
+}
+EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_fwname);
+
+struct clk_hw *devm_clk_hw_register_fixed_factor_with_accuracy_fwname(struct device *dev,
+ struct device_node *np, const char *name, const char *fw_name,
+ unsigned long flags, unsigned int mult, unsigned int div,
+ unsigned long acc)
+{
+ const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name };
+
+ return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL,
+ &pdata, flags, mult, div, acc,
+ CLK_FIXED_FACTOR_FIXED_ACCURACY, true);
+}
+EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_with_accuracy_fwname);
+
#ifdef CONFIG_OF
static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
{
struct clk_hw *hw;
const char *clk_name = node->name;
+ const struct clk_parent_data pdata = { .index = 0 };
u32 div, mult;
int ret;
@@ -266,8 +350,8 @@ static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
of_property_read_string(node, "clock-output-names", &clk_name);
- hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, NULL, 0,
- 0, mult, div, false);
+ hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, NULL,
+ &pdata, 0, mult, div, 0, 0, false);
if (IS_ERR(hw)) {
/*
* Clear OF_POPULATED flag so that clock registration can be
@@ -297,14 +381,12 @@ void __init of_fixed_factor_clk_setup(struct device_node *node)
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
of_fixed_factor_clk_setup);
-static int of_fixed_factor_clk_remove(struct platform_device *pdev)
+static void of_fixed_factor_clk_remove(struct platform_device *pdev)
{
struct clk_hw *clk = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
clk_hw_unregister_fixed_factor(clk);
-
- return 0;
}
static int of_fixed_factor_clk_probe(struct platform_device *pdev)
diff --git a/drivers/clk/clk-fixed-mmio.c b/drivers/clk/clk-fixed-mmio.c
index 5225d17d6b3f..3bfcf4cd98a2 100644
--- a/drivers/clk/clk-fixed-mmio.c
+++ b/drivers/clk/clk-fixed-mmio.c
@@ -71,14 +71,12 @@ static int of_fixed_mmio_clk_probe(struct platform_device *pdev)
return 0;
}
-static int of_fixed_mmio_clk_remove(struct platform_device *pdev)
+static void of_fixed_mmio_clk_remove(struct platform_device *pdev)
{
struct clk_hw *clk = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
clk_hw_unregister_fixed_rate(clk);
-
- return 0;
}
static const struct of_device_id of_fixed_mmio_clk_ids[] = {
@@ -99,4 +97,3 @@ module_platform_driver(of_fixed_mmio_clk_driver);
MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
MODULE_DESCRIPTION("Memory Mapped IO Fixed clock driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
index ac68a6b40f0e..6b4f76b9c4da 100644
--- a/drivers/clk/clk-fixed-rate.c
+++ b/drivers/clk/clk-fixed-rate.c
@@ -49,12 +49,24 @@ const struct clk_ops clk_fixed_rate_ops = {
};
EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
+static void devm_clk_hw_register_fixed_rate_release(struct device *dev, void *res)
+{
+ struct clk_fixed_rate *fix = res;
+
+ /*
+ * We can not use clk_hw_unregister_fixed_rate, since it will kfree()
+ * the hw, resulting in double free. Just unregister the hw and let
+ * devres code kfree() it.
+ */
+ clk_hw_unregister(&fix->hw);
+}
+
struct clk_hw *__clk_hw_register_fixed_rate(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy,
- unsigned long clk_fixed_flags)
+ unsigned long clk_fixed_flags, bool devm)
{
struct clk_fixed_rate *fixed;
struct clk_hw *hw;
@@ -62,7 +74,11 @@ struct clk_hw *__clk_hw_register_fixed_rate(struct device *dev,
int ret = -EINVAL;
/* allocate fixed-rate clock */
- fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
+ if (devm)
+ fixed = devres_alloc(devm_clk_hw_register_fixed_rate_release,
+ sizeof(*fixed), GFP_KERNEL);
+ else
+ fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
if (!fixed)
return ERR_PTR(-ENOMEM);
@@ -90,9 +106,13 @@ struct clk_hw *__clk_hw_register_fixed_rate(struct device *dev,
else
ret = of_clk_hw_register(np, hw);
if (ret) {
- kfree(fixed);
+ if (devm)
+ devres_free(fixed);
+ else
+ kfree(fixed);
hw = ERR_PTR(ret);
- }
+ } else if (devm)
+ devres_add(dev, fixed);
return hw;
}
@@ -176,14 +196,12 @@ void __init of_fixed_clk_setup(struct device_node *node)
}
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
-static int of_fixed_clk_remove(struct platform_device *pdev)
+static void of_fixed_clk_remove(struct platform_device *pdev)
{
struct clk_hw *hw = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
clk_hw_unregister_fixed_rate(hw);
-
- return 0;
}
static int of_fixed_clk_probe(struct platform_device *pdev)
diff --git a/drivers/clk/clk-fixed-rate_test.c b/drivers/clk/clk-fixed-rate_test.c
new file mode 100644
index 000000000000..0e04c10a21aa
--- /dev/null
+++ b/drivers/clk/clk-fixed-rate_test.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for clk fixed rate basic type
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <kunit/clk.h>
+#include <kunit/of.h>
+#include <kunit/platform_device.h>
+#include <kunit/resource.h>
+#include <kunit/test.h>
+
+#include "clk-fixed-rate_test.h"
+
+/**
+ * struct clk_hw_fixed_rate_kunit_params - Parameters to pass to __clk_hw_register_fixed_rate()
+ * @dev: device registering clk
+ * @np: device_node of device registering clk
+ * @name: name of clk
+ * @parent_name: parent name of clk
+ * @parent_hw: clk_hw pointer to parent of clk
+ * @parent_data: parent_data describing parent of clk
+ * @flags: clk framework flags
+ * @fixed_rate: frequency of clk
+ * @fixed_accuracy: accuracy of clk
+ * @clk_fixed_flags: fixed rate specific clk flags
+ */
+struct clk_hw_fixed_rate_kunit_params {
+ struct device *dev;
+ struct device_node *np;
+ const char *name;
+ const char *parent_name;
+ const struct clk_hw *parent_hw;
+ const struct clk_parent_data *parent_data;
+ unsigned long flags;
+ unsigned long fixed_rate;
+ unsigned long fixed_accuracy;
+ unsigned long clk_fixed_flags;
+};
+
+static int
+clk_hw_register_fixed_rate_kunit_init(struct kunit_resource *res, void *context)
+{
+ struct clk_hw_fixed_rate_kunit_params *params = context;
+ struct clk_hw *hw;
+
+ hw = __clk_hw_register_fixed_rate(params->dev, params->np,
+ params->name,
+ params->parent_name,
+ params->parent_hw,
+ params->parent_data,
+ params->flags,
+ params->fixed_rate,
+ params->fixed_accuracy,
+ params->clk_fixed_flags,
+ false);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ res->data = hw;
+
+ return 0;
+}
+
+static void clk_hw_register_fixed_rate_kunit_exit(struct kunit_resource *res)
+{
+ struct clk_hw *hw = res->data;
+
+ clk_hw_unregister_fixed_rate(hw);
+}
+
+/**
+ * clk_hw_register_fixed_rate_kunit() - Test managed __clk_hw_register_fixed_rate()
+ * @test: The test context
+ * @params: Arguments to __clk_hw_register_fixed_rate()
+ *
+ * Return: Registered fixed rate clk_hw or ERR_PTR on failure
+ */
+static struct clk_hw *
+clk_hw_register_fixed_rate_kunit(struct kunit *test,
+ struct clk_hw_fixed_rate_kunit_params *params)
+{
+ struct clk_hw *hw;
+
+ hw = kunit_alloc_resource(test,
+ clk_hw_register_fixed_rate_kunit_init,
+ clk_hw_register_fixed_rate_kunit_exit,
+ GFP_KERNEL, params);
+ if (!hw)
+ return ERR_PTR(-EINVAL);
+
+ return hw;
+}
+
+/**
+ * clk_hw_unregister_fixed_rate_kunit() - Test managed clk_hw_unregister_fixed_rate()
+ * @test: The test context
+ * @hw: fixed rate clk to unregister upon test completion
+ *
+ * Automatically unregister @hw when @test is complete via
+ * clk_hw_unregister_fixed_rate().
+ *
+ * Return: 0 on success or negative errno on failure
+ */
+static int clk_hw_unregister_fixed_rate_kunit(struct kunit *test, struct clk_hw *hw)
+{
+ if (!kunit_alloc_resource(test, NULL,
+ clk_hw_register_fixed_rate_kunit_exit,
+ GFP_KERNEL, hw))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * Test that clk_get_rate() on a fixed rate clk registered with
+ * clk_hw_register_fixed_rate() gets the proper frequency.
+ */
+static void clk_fixed_rate_rate_test(struct kunit *test)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+ const unsigned long fixed_rate = 230000;
+
+ hw = clk_hw_register_fixed_rate(NULL, "test-fixed-rate", NULL, 0, fixed_rate);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_unregister_fixed_rate_kunit(test, hw));
+
+ clk = clk_hw_get_clk_prepared_enabled_kunit(test, hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ KUNIT_EXPECT_EQ(test, fixed_rate, clk_get_rate(clk));
+}
+
+/*
+ * Test that clk_get_accuracy() on a fixed rate clk registered via
+ * clk_hw_register_fixed_rate_with_accuracy() gets the proper accuracy.
+ */
+static void clk_fixed_rate_accuracy_test(struct kunit *test)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+ const unsigned long fixed_accuracy = 5000;
+
+ hw = clk_hw_register_fixed_rate_with_accuracy(NULL, "test-fixed-rate",
+ NULL, 0, 0,
+ fixed_accuracy);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_unregister_fixed_rate_kunit(test, hw));
+
+ clk = clk_hw_get_clk_kunit(test, hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ KUNIT_EXPECT_EQ(test, fixed_accuracy, clk_get_accuracy(clk));
+}
+
+/* Test suite for a fixed rate clk without any parent */
+static struct kunit_case clk_fixed_rate_test_cases[] = {
+ KUNIT_CASE(clk_fixed_rate_rate_test),
+ KUNIT_CASE(clk_fixed_rate_accuracy_test),
+ {}
+};
+
+static struct kunit_suite clk_fixed_rate_suite = {
+ .name = "clk_fixed_rate",
+ .test_cases = clk_fixed_rate_test_cases,
+};
+
+/*
+ * Test that clk_get_parent() on a fixed rate clk gets the proper parent.
+ */
+static void clk_fixed_rate_parent_test(struct kunit *test)
+{
+ struct clk_hw *hw, *parent_hw;
+ struct clk *expected_parent, *actual_parent;
+ struct clk *clk;
+ const char *parent_name = "test-fixed-rate-parent";
+ struct clk_hw_fixed_rate_kunit_params parent_params = {
+ .name = parent_name,
+ };
+
+ parent_hw = clk_hw_register_fixed_rate_kunit(test, &parent_params);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
+ KUNIT_ASSERT_STREQ(test, parent_name, clk_hw_get_name(parent_hw));
+
+ expected_parent = clk_hw_get_clk_kunit(test, parent_hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_parent);
+
+ hw = clk_hw_register_fixed_rate(NULL, "test-fixed-rate", parent_name, 0, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_unregister_fixed_rate_kunit(test, hw));
+
+ clk = clk_hw_get_clk_kunit(test, hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ actual_parent = clk_get_parent(clk);
+ KUNIT_EXPECT_TRUE(test, clk_is_match(expected_parent, actual_parent));
+}
+
+/*
+ * Test that clk_get_rate() on a fixed rate clk ignores the parent rate.
+ */
+static void clk_fixed_rate_parent_rate_test(struct kunit *test)
+{
+ struct clk_hw *hw, *parent_hw;
+ struct clk *clk;
+ const unsigned long expected_rate = 1405;
+ const unsigned long parent_rate = 90402;
+ const char *parent_name = "test-fixed-rate-parent";
+ struct clk_hw_fixed_rate_kunit_params parent_params = {
+ .name = parent_name,
+ .fixed_rate = parent_rate,
+ };
+
+ parent_hw = clk_hw_register_fixed_rate_kunit(test, &parent_params);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
+ KUNIT_ASSERT_STREQ(test, parent_name, clk_hw_get_name(parent_hw));
+
+ hw = clk_hw_register_fixed_rate(NULL, "test-fixed-rate", parent_name, 0,
+ expected_rate);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_unregister_fixed_rate_kunit(test, hw));
+
+ clk = clk_hw_get_clk_prepared_enabled_kunit(test, hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ KUNIT_EXPECT_EQ(test, expected_rate, clk_get_rate(clk));
+}
+
+/*
+ * Test that clk_get_accuracy() on a fixed rate clk ignores the parent accuracy.
+ */
+static void clk_fixed_rate_parent_accuracy_test(struct kunit *test)
+{
+ struct clk_hw *hw, *parent_hw;
+ struct clk *clk;
+ const unsigned long expected_accuracy = 900;
+ const unsigned long parent_accuracy = 24000;
+ const char *parent_name = "test-fixed-rate-parent";
+ struct clk_hw_fixed_rate_kunit_params parent_params = {
+ .name = parent_name,
+ .fixed_accuracy = parent_accuracy,
+ };
+
+ parent_hw = clk_hw_register_fixed_rate_kunit(test, &parent_params);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
+ KUNIT_ASSERT_STREQ(test, parent_name, clk_hw_get_name(parent_hw));
+
+ hw = clk_hw_register_fixed_rate_with_accuracy(NULL, "test-fixed-rate",
+ parent_name, 0, 0,
+ expected_accuracy);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_unregister_fixed_rate_kunit(test, hw));
+
+ clk = clk_hw_get_clk_kunit(test, hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ KUNIT_EXPECT_EQ(test, expected_accuracy, clk_get_accuracy(clk));
+}
+
+/* Test suite for a fixed rate clk with a parent */
+static struct kunit_case clk_fixed_rate_parent_test_cases[] = {
+ KUNIT_CASE(clk_fixed_rate_parent_test),
+ KUNIT_CASE(clk_fixed_rate_parent_rate_test),
+ KUNIT_CASE(clk_fixed_rate_parent_accuracy_test),
+ {}
+};
+
+static struct kunit_suite clk_fixed_rate_parent_suite = {
+ .name = "clk_fixed_rate_parent",
+ .test_cases = clk_fixed_rate_parent_test_cases,
+};
+
+struct clk_fixed_rate_of_test_context {
+ struct device *dev;
+ struct platform_driver pdrv;
+ struct completion probed;
+};
+
+static inline struct clk_fixed_rate_of_test_context *
+pdev_to_clk_fixed_rate_of_test_context(struct platform_device *pdev)
+{
+ return container_of(to_platform_driver(pdev->dev.driver),
+ struct clk_fixed_rate_of_test_context,
+ pdrv);
+}
+
+/*
+ * Test that of_fixed_clk_setup() registers a fixed rate clk with the proper
+ * rate.
+ */
+static void clk_fixed_rate_of_probe_test(struct kunit *test)
+{
+ struct clk_fixed_rate_of_test_context *ctx = test->priv;
+ struct device *dev = ctx->dev;
+ struct clk *clk;
+
+ clk = clk_get_kunit(test, dev, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ KUNIT_ASSERT_EQ(test, 0, clk_prepare_enable_kunit(test, clk));
+ KUNIT_EXPECT_EQ(test, TEST_FIXED_FREQUENCY, clk_get_rate(clk));
+}
+
+/*
+ * Test that of_fixed_clk_setup() registers a fixed rate clk with the proper
+ * accuracy.
+ */
+static void clk_fixed_rate_of_accuracy_test(struct kunit *test)
+{
+ struct clk_fixed_rate_of_test_context *ctx = test->priv;
+ struct device *dev = ctx->dev;
+ struct clk *clk;
+
+ clk = clk_get_kunit(test, dev, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ KUNIT_EXPECT_EQ(test, TEST_FIXED_ACCURACY, clk_get_accuracy(clk));
+}
+
+static struct kunit_case clk_fixed_rate_of_cases[] = {
+ KUNIT_CASE(clk_fixed_rate_of_probe_test),
+ KUNIT_CASE(clk_fixed_rate_of_accuracy_test),
+ {}
+};
+
+static int clk_fixed_rate_of_test_probe(struct platform_device *pdev)
+{
+ struct clk_fixed_rate_of_test_context *ctx;
+
+ ctx = pdev_to_clk_fixed_rate_of_test_context(pdev);
+ ctx->dev = &pdev->dev;
+ complete(&ctx->probed);
+
+ return 0;
+}
+
+static int clk_fixed_rate_of_init(struct kunit *test)
+{
+ struct clk_fixed_rate_of_test_context *ctx;
+ static const struct of_device_id match_table[] = {
+ { .compatible = "test,single-clk-consumer" },
+ { }
+ };
+
+ KUNIT_ASSERT_EQ(test, 0, of_overlay_apply_kunit(test, kunit_clk_fixed_rate_test));
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ test->priv = ctx;
+
+ ctx->pdrv.probe = clk_fixed_rate_of_test_probe;
+ ctx->pdrv.driver.of_match_table = match_table;
+ ctx->pdrv.driver.name = __func__;
+ ctx->pdrv.driver.owner = THIS_MODULE;
+ init_completion(&ctx->probed);
+
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
+ KUNIT_ASSERT_NE(test, 0, wait_for_completion_timeout(&ctx->probed, HZ));
+
+ return 0;
+}
+
+static struct kunit_suite clk_fixed_rate_of_suite = {
+ .name = "clk_fixed_rate_of",
+ .init = clk_fixed_rate_of_init,
+ .test_cases = clk_fixed_rate_of_cases,
+};
+
+kunit_test_suites(
+ &clk_fixed_rate_suite,
+ &clk_fixed_rate_of_suite,
+ &clk_fixed_rate_parent_suite,
+);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test for clk fixed rate basic type");
diff --git a/drivers/clk/clk-fixed-rate_test.h b/drivers/clk/clk-fixed-rate_test.h
new file mode 100644
index 000000000000..e0d28e5b6081
--- /dev/null
+++ b/drivers/clk/clk-fixed-rate_test.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CLK_FIXED_RATE_TEST_H
+#define _CLK_FIXED_RATE_TEST_H
+
+#define TEST_FIXED_FREQUENCY 50000000
+#define TEST_FIXED_ACCURACY 300
+
+#endif
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index 8efa5142ff8c..cd36a6e27f25 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -38,12 +38,15 @@
* saturated values.
*/
-#include <linux/clk-provider.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
#include <linux/io.h>
+#include <linux/math.h>
#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/slab.h>
#include <linux/rational.h>
+#include <linux/slab.h>
+
+#include <linux/clk-provider.h>
#include "clk-fractional-divider.h"
@@ -63,14 +66,13 @@ static inline void clk_fd_writel(struct clk_fractional_divider *fd, u32 val)
writel(val, fd->reg);
}
-static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static void clk_fd_get_div(struct clk_hw *hw, struct u32_fract *fract)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long flags = 0;
unsigned long m, n;
+ u32 mmask, nmask;
u32 val;
- u64 ret;
if (fd->lock)
spin_lock_irqsave(fd->lock, flags);
@@ -84,19 +86,33 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
else
__release(fd->lock);
- m = (val & fd->mmask) >> fd->mshift;
- n = (val & fd->nmask) >> fd->nshift;
+ mmask = GENMASK(fd->mwidth - 1, 0) << fd->mshift;
+ nmask = GENMASK(fd->nwidth - 1, 0) << fd->nshift;
+
+ m = (val & mmask) >> fd->mshift;
+ n = (val & nmask) >> fd->nshift;
if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
m++;
n++;
}
- if (!n || !m)
+ fract->numerator = m;
+ fract->denominator = n;
+}
+
+static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct u32_fract fract;
+ u64 ret;
+
+ clk_fd_get_div(hw, &fract);
+
+ if (!fract.numerator || !fract.denominator)
return parent_rate;
- ret = (u64)parent_rate * m;
- do_div(ret, n);
+ ret = (u64)parent_rate * fract.numerator;
+ do_div(ret, fract.denominator);
return ret;
}
@@ -107,6 +123,7 @@ void clk_fractional_divider_general_approximation(struct clk_hw *hw,
unsigned long *m, unsigned long *n)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
+ unsigned long max_m, max_n;
/*
* Get rate closer to *parent_rate to guarantee there is no overflow
@@ -122,30 +139,44 @@ void clk_fractional_divider_general_approximation(struct clk_hw *hw,
rate <<= scale - fd->nwidth;
}
- rational_best_approximation(rate, *parent_rate,
- GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
- m, n);
+ if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
+ max_m = BIT(fd->mwidth);
+ max_n = BIT(fd->nwidth);
+ } else {
+ max_m = GENMASK(fd->mwidth - 1, 0);
+ max_n = GENMASK(fd->nwidth - 1, 0);
+ }
+
+ rational_best_approximation(rate, *parent_rate, max_m, max_n, m, n);
}
+EXPORT_SYMBOL_GPL(clk_fractional_divider_general_approximation);
-static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_fd_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long m, n;
u64 ret;
- if (!rate || (!clk_hw_can_set_rate_parent(hw) && rate >= *parent_rate))
- return *parent_rate;
+ if (!req->rate || (!clk_hw_can_set_rate_parent(hw) && req->rate >= req->best_parent_rate)) {
+ req->rate = req->best_parent_rate;
+
+ return 0;
+ }
if (fd->approximation)
- fd->approximation(hw, rate, parent_rate, &m, &n);
+ fd->approximation(hw, req->rate, &req->best_parent_rate, &m, &n);
else
- clk_fractional_divider_general_approximation(hw, rate, parent_rate, &m, &n);
+ clk_fractional_divider_general_approximation(hw, req->rate,
+ &req->best_parent_rate,
+ &m, &n);
- ret = (u64)*parent_rate * m;
+ ret = (u64)req->best_parent_rate * m;
do_div(ret, n);
- return ret;
+ req->rate = ret;
+
+ return 0;
}
static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -153,25 +184,34 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long flags = 0;
- unsigned long m, n;
+ unsigned long m, n, max_m, max_n;
+ u32 mmask, nmask;
u32 val;
- rational_best_approximation(rate, parent_rate,
- GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
- &m, &n);
+ if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
+ max_m = BIT(fd->mwidth);
+ max_n = BIT(fd->nwidth);
+ } else {
+ max_m = GENMASK(fd->mwidth - 1, 0);
+ max_n = GENMASK(fd->nwidth - 1, 0);
+ }
+ rational_best_approximation(rate, parent_rate, max_m, max_n, &m, &n);
if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
m--;
n--;
}
+ mmask = GENMASK(fd->mwidth - 1, 0) << fd->mshift;
+ nmask = GENMASK(fd->nwidth - 1, 0) << fd->nshift;
+
if (fd->lock)
spin_lock_irqsave(fd->lock, flags);
else
__acquire(fd->lock);
val = clk_fd_readl(fd);
- val &= ~(fd->mmask | fd->nmask);
+ val &= ~(mmask | nmask);
val |= (m << fd->mshift) | (n << fd->nshift);
clk_fd_writel(fd, val);
@@ -183,10 +223,45 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+static int clk_fd_numerator_get(void *hw, u64 *val)
+{
+ struct u32_fract fract;
+
+ clk_fd_get_div(hw, &fract);
+
+ *val = fract.numerator;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(clk_fd_numerator_fops, clk_fd_numerator_get, NULL, "%llu\n");
+
+static int clk_fd_denominator_get(void *hw, u64 *val)
+{
+ struct u32_fract fract;
+
+ clk_fd_get_div(hw, &fract);
+
+ *val = fract.denominator;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(clk_fd_denominator_fops, clk_fd_denominator_get, NULL, "%llu\n");
+
+static void clk_fd_debug_init(struct clk_hw *hw, struct dentry *dentry)
+{
+ debugfs_create_file("numerator", 0444, dentry, hw, &clk_fd_numerator_fops);
+ debugfs_create_file("denominator", 0444, dentry, hw, &clk_fd_denominator_fops);
+}
+#endif
+
const struct clk_ops clk_fractional_divider_ops = {
.recalc_rate = clk_fd_recalc_rate,
- .round_rate = clk_fd_round_rate,
+ .determine_rate = clk_fd_determine_rate,
.set_rate = clk_fd_set_rate,
+#ifdef CONFIG_DEBUG_FS
+ .debug_init = clk_fd_debug_init,
+#endif
};
EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
@@ -213,10 +288,8 @@ struct clk_hw *clk_hw_register_fractional_divider(struct device *dev,
fd->reg = reg;
fd->mshift = mshift;
fd->mwidth = mwidth;
- fd->mmask = GENMASK(mwidth - 1, 0) << mshift;
fd->nshift = nshift;
fd->nwidth = nwidth;
- fd->nmask = GENMASK(nwidth - 1, 0) << nshift;
fd->flags = clk_divider_flags;
fd->lock = lock;
fd->hw.init = &init;
diff --git a/drivers/clk/clk-fractional-divider_test.c b/drivers/clk/clk-fractional-divider_test.c
new file mode 100644
index 000000000000..25fa35d89c1a
--- /dev/null
+++ b/drivers/clk/clk-fractional-divider_test.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Kunit tests for clk fractional divider
+ */
+#include <linux/clk-provider.h>
+#include <kunit/test.h>
+
+#include "clk-fractional-divider.h"
+
+/*
+ * Test the maximum denominator case for fd clock without flags.
+ *
+ * Expect the highest possible denominator to be used in order to get as close as possible to the
+ * requested rate.
+ */
+static void clk_fd_test_approximation_max_denominator(struct kunit *test)
+{
+ struct clk_fractional_divider *fd;
+ unsigned long rate, parent_rate, parent_rate_before, m, n, max_n;
+
+ fd = kunit_kzalloc(test, sizeof(*fd), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fd);
+
+ fd->mwidth = 3;
+ fd->nwidth = 3;
+ max_n = 7;
+
+ rate = 240000000;
+ parent_rate = (max_n + 1) * rate; /* so that it exceeds the maximum divisor */
+ parent_rate_before = parent_rate;
+
+ clk_fractional_divider_general_approximation(&fd->hw, rate, &parent_rate, &m, &n);
+ KUNIT_ASSERT_EQ(test, parent_rate, parent_rate_before);
+
+ KUNIT_EXPECT_EQ(test, m, 1);
+ KUNIT_EXPECT_EQ(test, n, max_n);
+}
+
+/*
+ * Test the maximum numerator case for fd clock without flags.
+ *
+ * Expect the highest possible numerator to be used in order to get as close as possible to the
+ * requested rate.
+ */
+static void clk_fd_test_approximation_max_numerator(struct kunit *test)
+{
+ struct clk_fractional_divider *fd;
+ unsigned long rate, parent_rate, parent_rate_before, m, n, max_m;
+
+ fd = kunit_kzalloc(test, sizeof(*fd), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fd);
+
+ fd->mwidth = 3;
+ max_m = 7;
+ fd->nwidth = 3;
+
+ rate = 240000000;
+ parent_rate = rate / (max_m + 1); /* so that it exceeds the maximum numerator */
+ parent_rate_before = parent_rate;
+
+ clk_fractional_divider_general_approximation(&fd->hw, rate, &parent_rate, &m, &n);
+ KUNIT_ASSERT_EQ(test, parent_rate, parent_rate_before);
+
+ KUNIT_EXPECT_EQ(test, m, max_m);
+ KUNIT_EXPECT_EQ(test, n, 1);
+}
+
+/*
+ * Test the maximum denominator case for zero based fd clock.
+ *
+ * Expect the highest possible denominator to be used in order to get as close as possible to the
+ * requested rate.
+ */
+static void clk_fd_test_approximation_max_denominator_zero_based(struct kunit *test)
+{
+ struct clk_fractional_divider *fd;
+ unsigned long rate, parent_rate, parent_rate_before, m, n, max_n;
+
+ fd = kunit_kzalloc(test, sizeof(*fd), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fd);
+
+ fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
+ fd->mwidth = 3;
+ fd->nwidth = 3;
+ max_n = 8;
+
+ rate = 240000000;
+ parent_rate = (max_n + 1) * rate; /* so that it exceeds the maximum divisor */
+ parent_rate_before = parent_rate;
+
+ clk_fractional_divider_general_approximation(&fd->hw, rate, &parent_rate, &m, &n);
+ KUNIT_ASSERT_EQ(test, parent_rate, parent_rate_before);
+
+ KUNIT_EXPECT_EQ(test, m, 1);
+ KUNIT_EXPECT_EQ(test, n, max_n);
+}
+
+/*
+ * Test the maximum numerator case for zero based fd clock.
+ *
+ * Expect the highest possible numerator to be used in order to get as close as possible to the
+ * requested rate.
+ */
+static void clk_fd_test_approximation_max_numerator_zero_based(struct kunit *test)
+{
+ struct clk_fractional_divider *fd;
+ unsigned long rate, parent_rate, parent_rate_before, m, n, max_m;
+
+ fd = kunit_kzalloc(test, sizeof(*fd), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fd);
+
+ fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
+ fd->mwidth = 3;
+ max_m = 8;
+ fd->nwidth = 3;
+
+ rate = 240000000;
+ parent_rate = rate / (max_m + 1); /* so that it exceeds the maximum numerator */
+ parent_rate_before = parent_rate;
+
+ clk_fractional_divider_general_approximation(&fd->hw, rate, &parent_rate, &m, &n);
+ KUNIT_ASSERT_EQ(test, parent_rate, parent_rate_before);
+
+ KUNIT_EXPECT_EQ(test, m, max_m);
+ KUNIT_EXPECT_EQ(test, n, 1);
+}
+
+static struct kunit_case clk_fd_approximation_test_cases[] = {
+ KUNIT_CASE(clk_fd_test_approximation_max_denominator),
+ KUNIT_CASE(clk_fd_test_approximation_max_numerator),
+ KUNIT_CASE(clk_fd_test_approximation_max_denominator_zero_based),
+ KUNIT_CASE(clk_fd_test_approximation_max_numerator_zero_based),
+ {}
+};
+
+/*
+ * Test suite for clk_fractional_divider_general_approximation().
+ */
+static struct kunit_suite clk_fd_approximation_suite = {
+ .name = "clk-fd-approximation",
+ .test_cases = clk_fd_approximation_test_cases,
+};
+
+kunit_test_suites(
+ &clk_fd_approximation_suite
+);
+MODULE_DESCRIPTION("Kunit tests for clk fractional divider");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-fsl-sai.c b/drivers/clk/clk-fsl-sai.c
index 6238fcea0467..cba45e07562d 100644
--- a/drivers/clk/clk-fsl-sai.c
+++ b/drivers/clk/clk-fsl-sai.c
@@ -33,14 +33,12 @@ static int fsl_sai_clk_probe(struct platform_device *pdev)
struct clk_parent_data pdata = { .index = 0 };
void __iomem *base;
struct clk_hw *hw;
- struct resource *res;
sai_clk = devm_kzalloc(dev, sizeof(*sai_clk), GFP_KERNEL);
if (!sai_clk)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -88,5 +86,4 @@ module_platform_driver(fsl_sai_clk_driver);
MODULE_DESCRIPTION("Freescale SAI bitclock-as-a-clock driver");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
-MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:fsl-sai-clk");
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 64283807600b..4746f8219132 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -15,7 +15,7 @@
#include <linux/string.h>
/**
- * DOC: basic gatable clock which can gate and ungate it's ouput
+ * DOC: basic gateable clock which can gate and ungate its output
*
* Traits of this clock:
* prepare - clk_(un)prepare only ensures parent is (un)prepared
diff --git a/drivers/clk/clk-gate_test.c b/drivers/clk/clk-gate_test.c
index e136aaad48bf..e276cd974750 100644
--- a/drivers/clk/clk-gate_test.c
+++ b/drivers/clk/clk-gate_test.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Kunit test for clk gate basic type
+ * Kunit tests for clk gate
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
@@ -131,7 +131,7 @@ struct clk_gate_test_context {
void __iomem *fake_mem;
struct clk_hw *hw;
struct clk_hw *parent;
- u32 fake_reg; /* Keep at end, KASAN can detect out of bounds */
+ __le32 fake_reg; /* Keep at end, KASAN can detect out of bounds */
};
static struct clk_gate_test_context *clk_gate_test_alloc_ctx(struct kunit *test)
@@ -166,7 +166,7 @@ static void clk_gate_test_enable(struct kunit *test)
KUNIT_ASSERT_EQ(test, clk_prepare_enable(clk), 0);
- KUNIT_EXPECT_EQ(test, enable_val, ctx->fake_reg);
+ KUNIT_EXPECT_EQ(test, enable_val, le32_to_cpu(ctx->fake_reg));
KUNIT_EXPECT_TRUE(test, clk_hw_is_enabled(hw));
KUNIT_EXPECT_TRUE(test, clk_hw_is_prepared(hw));
KUNIT_EXPECT_TRUE(test, clk_hw_is_enabled(parent));
@@ -183,10 +183,10 @@ static void clk_gate_test_disable(struct kunit *test)
u32 disable_val = 0;
KUNIT_ASSERT_EQ(test, clk_prepare_enable(clk), 0);
- KUNIT_ASSERT_EQ(test, enable_val, ctx->fake_reg);
+ KUNIT_ASSERT_EQ(test, enable_val, le32_to_cpu(ctx->fake_reg));
clk_disable_unprepare(clk);
- KUNIT_EXPECT_EQ(test, disable_val, ctx->fake_reg);
+ KUNIT_EXPECT_EQ(test, disable_val, le32_to_cpu(ctx->fake_reg));
KUNIT_EXPECT_FALSE(test, clk_hw_is_enabled(hw));
KUNIT_EXPECT_FALSE(test, clk_hw_is_prepared(hw));
KUNIT_EXPECT_FALSE(test, clk_hw_is_enabled(parent));
@@ -246,7 +246,7 @@ static void clk_gate_test_invert_enable(struct kunit *test)
KUNIT_ASSERT_EQ(test, clk_prepare_enable(clk), 0);
- KUNIT_EXPECT_EQ(test, enable_val, ctx->fake_reg);
+ KUNIT_EXPECT_EQ(test, enable_val, le32_to_cpu(ctx->fake_reg));
KUNIT_EXPECT_TRUE(test, clk_hw_is_enabled(hw));
KUNIT_EXPECT_TRUE(test, clk_hw_is_prepared(hw));
KUNIT_EXPECT_TRUE(test, clk_hw_is_enabled(parent));
@@ -263,10 +263,10 @@ static void clk_gate_test_invert_disable(struct kunit *test)
u32 disable_val = BIT(15);
KUNIT_ASSERT_EQ(test, clk_prepare_enable(clk), 0);
- KUNIT_ASSERT_EQ(test, enable_val, ctx->fake_reg);
+ KUNIT_ASSERT_EQ(test, enable_val, le32_to_cpu(ctx->fake_reg));
clk_disable_unprepare(clk);
- KUNIT_EXPECT_EQ(test, disable_val, ctx->fake_reg);
+ KUNIT_EXPECT_EQ(test, disable_val, le32_to_cpu(ctx->fake_reg));
KUNIT_EXPECT_FALSE(test, clk_hw_is_enabled(hw));
KUNIT_EXPECT_FALSE(test, clk_hw_is_prepared(hw));
KUNIT_EXPECT_FALSE(test, clk_hw_is_enabled(parent));
@@ -290,7 +290,7 @@ static int clk_gate_test_invert_init(struct kunit *test)
2000000);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
- ctx->fake_reg = BIT(15); /* Default to off */
+ ctx->fake_reg = cpu_to_le32(BIT(15)); /* Default to off */
hw = clk_hw_register_gate_parent_hw(NULL, "test_gate", parent, 0,
ctx->fake_mem, 15,
CLK_GATE_SET_TO_DISABLE, NULL);
@@ -319,7 +319,7 @@ static void clk_gate_test_hiword_enable(struct kunit *test)
KUNIT_ASSERT_EQ(test, clk_prepare_enable(clk), 0);
- KUNIT_EXPECT_EQ(test, enable_val, ctx->fake_reg);
+ KUNIT_EXPECT_EQ(test, enable_val, le32_to_cpu(ctx->fake_reg));
KUNIT_EXPECT_TRUE(test, clk_hw_is_enabled(hw));
KUNIT_EXPECT_TRUE(test, clk_hw_is_prepared(hw));
KUNIT_EXPECT_TRUE(test, clk_hw_is_enabled(parent));
@@ -336,10 +336,10 @@ static void clk_gate_test_hiword_disable(struct kunit *test)
u32 disable_val = BIT(9 + 16);
KUNIT_ASSERT_EQ(test, clk_prepare_enable(clk), 0);
- KUNIT_ASSERT_EQ(test, enable_val, ctx->fake_reg);
+ KUNIT_ASSERT_EQ(test, enable_val, le32_to_cpu(ctx->fake_reg));
clk_disable_unprepare(clk);
- KUNIT_EXPECT_EQ(test, disable_val, ctx->fake_reg);
+ KUNIT_EXPECT_EQ(test, disable_val, le32_to_cpu(ctx->fake_reg));
KUNIT_EXPECT_FALSE(test, clk_hw_is_enabled(hw));
KUNIT_EXPECT_FALSE(test, clk_hw_is_prepared(hw));
KUNIT_EXPECT_FALSE(test, clk_hw_is_enabled(parent));
@@ -387,7 +387,7 @@ static void clk_gate_test_is_enabled(struct kunit *test)
struct clk_gate_test_context *ctx;
ctx = clk_gate_test_alloc_ctx(test);
- ctx->fake_reg = BIT(7);
+ ctx->fake_reg = cpu_to_le32(BIT(7));
hw = clk_hw_register_gate(NULL, "test_gate", NULL, 0, ctx->fake_mem, 7,
0, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
@@ -402,7 +402,7 @@ static void clk_gate_test_is_disabled(struct kunit *test)
struct clk_gate_test_context *ctx;
ctx = clk_gate_test_alloc_ctx(test);
- ctx->fake_reg = BIT(4);
+ ctx->fake_reg = cpu_to_le32(BIT(4));
hw = clk_hw_register_gate(NULL, "test_gate", NULL, 0, ctx->fake_mem, 7,
0, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
@@ -417,7 +417,7 @@ static void clk_gate_test_is_enabled_inverted(struct kunit *test)
struct clk_gate_test_context *ctx;
ctx = clk_gate_test_alloc_ctx(test);
- ctx->fake_reg = BIT(31);
+ ctx->fake_reg = cpu_to_le32(BIT(31));
hw = clk_hw_register_gate(NULL, "test_gate", NULL, 0, ctx->fake_mem, 2,
CLK_GATE_SET_TO_DISABLE, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
@@ -432,7 +432,7 @@ static void clk_gate_test_is_disabled_inverted(struct kunit *test)
struct clk_gate_test_context *ctx;
ctx = clk_gate_test_alloc_ctx(test);
- ctx->fake_reg = BIT(29);
+ ctx->fake_reg = cpu_to_le32(BIT(29));
hw = clk_hw_register_gate(NULL, "test_gate", NULL, 0, ctx->fake_mem, 29,
CLK_GATE_SET_TO_DISABLE, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
@@ -461,4 +461,5 @@ kunit_test_suites(
&clk_gate_test_hiword_suite,
&clk_gate_test_enabled_suite
);
+MODULE_DESCRIPTION("Kunit tests for clk gate");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clk-gemini.c b/drivers/clk/clk-gemini.c
index a23fa6d47ef1..e94589c38568 100644
--- a/drivers/clk/clk-gemini.c
+++ b/drivers/clk/clk-gemini.c
@@ -67,12 +67,10 @@ struct gemini_gate_data {
* struct clk_gemini_pci - Gemini PCI clock
* @hw: corresponding clock hardware entry
* @map: regmap to access the registers
- * @rate: current rate
*/
struct clk_gemini_pci {
struct clk_hw hw;
struct regmap *map;
- unsigned long rate;
};
/**
@@ -128,13 +126,16 @@ static unsigned long gemini_pci_recalc_rate(struct clk_hw *hw,
return 33000000;
}
-static long gemini_pci_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int gemini_pci_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
/* We support 33 and 66 MHz */
- if (rate < 48000000)
- return 33000000;
- return 66000000;
+ if (req->rate < 48000000)
+ req->rate = 33000000;
+ else
+ req->rate = 66000000;
+
+ return 0;
}
static int gemini_pci_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -181,7 +182,7 @@ static int gemini_pci_is_enabled(struct clk_hw *hw)
static const struct clk_ops gemini_pci_clk_ops = {
.recalc_rate = gemini_pci_recalc_rate,
- .round_rate = gemini_pci_round_rate,
+ .determine_rate = gemini_pci_determine_rate,
.set_rate = gemini_pci_set_rate,
.enable = gemini_pci_enable,
.disable = gemini_pci_disable,
@@ -276,7 +277,6 @@ static int gemini_clk_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
unsigned int mult, div;
- struct resource *res;
u32 val;
int ret;
int i;
@@ -286,8 +286,7 @@ static int gemini_clk_probe(struct platform_device *pdev)
return -ENOMEM;
/* Remap the system controller for the exclusive register */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -404,6 +403,7 @@ static void __init gemini_cc_init(struct device_node *np)
GFP_KERNEL);
if (!gemini_clk_data)
return;
+ gemini_clk_data->num = GEMINI_NUM_CLKS;
/*
* This way all clock fetched before the platform device probes,
@@ -457,7 +457,6 @@ static void __init gemini_cc_init(struct device_node *np)
gemini_clk_data->hws[GEMINI_CLK_APB] = hw;
/* Register the clocks to be accessed by the device tree */
- gemini_clk_data->num = GEMINI_NUM_CLKS;
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, gemini_clk_data);
}
CLK_OF_DECLARE_DRIVER(gemini_cc, "cortina,gemini-syscon", gemini_cc_init);
diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c
index 38755a241ab7..9099c57e2715 100644
--- a/drivers/clk/clk-gpio.c
+++ b/drivers/clk/clk-gpio.c
@@ -15,15 +15,17 @@
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
/**
* DOC: basic gpio gated clock which can be enabled and disabled
* with gpio output
* Traits of this clock:
- * prepare - clk_(un)prepare only ensures parent is (un)prepared
- * enable - clk_enable and clk_disable are functional & control gpio
+ * prepare - clk_(un)prepare are functional and control a gpio that can sleep
+ * enable - clk_enable and clk_disable are functional & control
+ * non-sleeping gpio
* rate - inherits rate from parent. No clk_set_rate support
* parent - fixed parent. No clk_set_parent support
*/
@@ -199,7 +201,6 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
struct gpio_desc *gpiod;
struct clk_hw *hw;
bool is_mux;
- int ret;
is_mux = of_device_is_compatible(node, "gpio-mux-clock");
@@ -211,17 +212,9 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
gpio_name = is_mux ? "select" : "enable";
gpiod = devm_gpiod_get(dev, gpio_name, GPIOD_OUT_LOW);
- if (IS_ERR(gpiod)) {
- ret = PTR_ERR(gpiod);
- if (ret == -EPROBE_DEFER)
- pr_debug("%pOFn: %s: GPIOs not yet available, retry later\n",
- node, __func__);
- else
- pr_err("%pOFn: %s: Can't get '%s' named GPIO property\n",
- node, __func__,
- gpio_name);
- return ret;
- }
+ if (IS_ERR(gpiod))
+ return dev_err_probe(dev, PTR_ERR(gpiod),
+ "Can't get '%s' named GPIO property\n", gpio_name);
if (is_mux)
hw = clk_hw_register_gpio_mux(dev, gpiod);
@@ -247,3 +240,187 @@ static struct platform_driver gpio_clk_driver = {
},
};
builtin_platform_driver(gpio_clk_driver);
+
+/**
+ * DOC: gated fixed clock, controlled with a gpio output and a regulator
+ * Traits of this clock:
+ * prepare - clk_prepare and clk_unprepare are function & control regulator
+ * optionally a gpio that can sleep
+ * enable - clk_enable and clk_disable are functional & control gpio
+ * rate - rate is fixed and set on clock registration
+ * parent - fixed clock is a root clock and has no parent
+ */
+
+/**
+ * struct clk_gated_fixed - Gateable fixed rate clock
+ * @clk_gpio: instance of clk_gpio for gate-gpio
+ * @supply: supply regulator
+ * @rate: fixed rate
+ */
+struct clk_gated_fixed {
+ struct clk_gpio clk_gpio;
+ struct regulator *supply;
+ unsigned long rate;
+};
+
+#define to_clk_gated_fixed(_clk_gpio) container_of(_clk_gpio, struct clk_gated_fixed, clk_gpio)
+
+static unsigned long clk_gated_fixed_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return to_clk_gated_fixed(to_clk_gpio(hw))->rate;
+}
+
+static int clk_gated_fixed_prepare(struct clk_hw *hw)
+{
+ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw));
+
+ if (!clk->supply)
+ return 0;
+
+ return regulator_enable(clk->supply);
+}
+
+static void clk_gated_fixed_unprepare(struct clk_hw *hw)
+{
+ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw));
+
+ if (!clk->supply)
+ return;
+
+ regulator_disable(clk->supply);
+}
+
+static int clk_gated_fixed_is_prepared(struct clk_hw *hw)
+{
+ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw));
+
+ if (!clk->supply)
+ return true;
+
+ return regulator_is_enabled(clk->supply);
+}
+
+/*
+ * Fixed gated clock with non-sleeping gpio.
+ *
+ * Prepare operation turns on the supply regulator
+ * and the enable operation switches the enable-gpio.
+ */
+static const struct clk_ops clk_gated_fixed_ops = {
+ .prepare = clk_gated_fixed_prepare,
+ .unprepare = clk_gated_fixed_unprepare,
+ .is_prepared = clk_gated_fixed_is_prepared,
+ .enable = clk_gpio_gate_enable,
+ .disable = clk_gpio_gate_disable,
+ .is_enabled = clk_gpio_gate_is_enabled,
+ .recalc_rate = clk_gated_fixed_recalc_rate,
+};
+
+static int clk_sleeping_gated_fixed_prepare(struct clk_hw *hw)
+{
+ int ret;
+
+ ret = clk_gated_fixed_prepare(hw);
+ if (ret)
+ return ret;
+
+ ret = clk_sleeping_gpio_gate_prepare(hw);
+ if (ret)
+ clk_gated_fixed_unprepare(hw);
+
+ return ret;
+}
+
+static void clk_sleeping_gated_fixed_unprepare(struct clk_hw *hw)
+{
+ clk_gated_fixed_unprepare(hw);
+ clk_sleeping_gpio_gate_unprepare(hw);
+}
+
+/*
+ * Fixed gated clock with non-sleeping gpio.
+ *
+ * Enabling the supply regulator and switching the enable-gpio happens
+ * both in the prepare step.
+ * is_prepared only needs to check the gpio state, as toggling the
+ * gpio is the last step when preparing.
+ */
+static const struct clk_ops clk_sleeping_gated_fixed_ops = {
+ .prepare = clk_sleeping_gated_fixed_prepare,
+ .unprepare = clk_sleeping_gated_fixed_unprepare,
+ .is_prepared = clk_sleeping_gpio_gate_is_prepared,
+ .recalc_rate = clk_gated_fixed_recalc_rate,
+};
+
+static int clk_gated_fixed_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct clk_gated_fixed *clk;
+ const struct clk_ops *ops;
+ const char *clk_name;
+ u32 rate;
+ int ret;
+
+ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return -ENOMEM;
+
+ ret = device_property_read_u32(dev, "clock-frequency", &rate);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clock-frequency\n");
+ clk->rate = rate;
+
+ ret = device_property_read_string(dev, "clock-output-names", &clk_name);
+ if (ret)
+ clk_name = fwnode_get_name(dev->fwnode);
+
+ clk->supply = devm_regulator_get_optional(dev, "vdd");
+ if (IS_ERR(clk->supply)) {
+ if (PTR_ERR(clk->supply) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(clk->supply),
+ "Failed to get regulator\n");
+ clk->supply = NULL;
+ }
+
+ clk->clk_gpio.gpiod = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(clk->clk_gpio.gpiod))
+ return dev_err_probe(dev, PTR_ERR(clk->clk_gpio.gpiod),
+ "Failed to get gpio\n");
+
+ if (gpiod_cansleep(clk->clk_gpio.gpiod))
+ ops = &clk_sleeping_gated_fixed_ops;
+ else
+ ops = &clk_gated_fixed_ops;
+
+ clk->clk_gpio.hw.init = CLK_HW_INIT_NO_PARENT(clk_name, ops, 0);
+
+ /* register the clock */
+ ret = devm_clk_hw_register(dev, &clk->clk_gpio.hw);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register clock\n");
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &clk->clk_gpio.hw);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register clock provider\n");
+
+ return 0;
+}
+
+static const struct of_device_id gated_fixed_clk_match_table[] = {
+ { .compatible = "gated-fixed-clock" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver gated_fixed_clk_driver = {
+ .probe = clk_gated_fixed_probe,
+ .driver = {
+ .name = "gated-fixed-clk",
+ .of_match_table = gated_fixed_clk_match_table,
+ },
+};
+builtin_platform_driver(gated_fixed_clk_driver);
diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c
index 2a0cea2946f9..cc583934ecf2 100644
--- a/drivers/clk/clk-highbank.c
+++ b/drivers/clk/clk-highbank.c
@@ -37,7 +37,6 @@
struct hb_clk {
struct clk_hw hw;
void __iomem *reg;
- char *parent_name;
};
#define to_hb_clk(p) container_of(p, struct hb_clk, hw)
@@ -131,15 +130,17 @@ static void clk_pll_calc(unsigned long rate, unsigned long ref_freq,
*pdivf = divf;
}
-static long clk_pll_round_rate(struct clk_hw *hwclk, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
u32 divq, divf;
- unsigned long ref_freq = *parent_rate;
+ unsigned long ref_freq = req->best_parent_rate;
- clk_pll_calc(rate, ref_freq, &divq, &divf);
+ clk_pll_calc(req->rate, ref_freq, &divq, &divf);
- return (ref_freq * (divf + 1)) / (1 << divq);
+ req->rate = (ref_freq * (divf + 1)) / (1 << divq);
+
+ return 0;
}
static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate,
@@ -186,7 +187,7 @@ static const struct clk_ops clk_pll_ops = {
.enable = clk_pll_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
- .round_rate = clk_pll_round_rate,
+ .determine_rate = clk_pll_determine_rate,
.set_rate = clk_pll_set_rate,
};
@@ -228,16 +229,18 @@ static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
return parent_rate / div;
}
-static long clk_periclk_round_rate(struct clk_hw *hwclk, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_periclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
u32 div;
- div = *parent_rate / rate;
+ div = req->best_parent_rate / req->rate;
div++;
div &= ~0x1;
- return *parent_rate / div;
+ req->rate = req->best_parent_rate / div;
+
+ return 0;
}
static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate,
@@ -256,7 +259,7 @@ static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate,
static const struct clk_ops periclk_ops = {
.recalc_rate = clk_periclk_recalc_rate,
- .round_rate = clk_periclk_round_rate,
+ .determine_rate = clk_periclk_determine_rate,
.set_rate = clk_periclk_set_rate,
};
diff --git a/drivers/clk/clk-hsdk-pll.c b/drivers/clk/clk-hsdk-pll.c
index 60007b508590..7d56a47c2aa7 100644
--- a/drivers/clk/clk-hsdk-pll.c
+++ b/drivers/clk/clk-hsdk-pll.c
@@ -12,7 +12,6 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -198,8 +197,8 @@ static unsigned long hsdk_pll_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long hsdk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int hsdk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int i;
unsigned long best_rate;
@@ -212,13 +211,15 @@ static long hsdk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
best_rate = pll_cfg[0].rate;
for (i = 1; pll_cfg[i].rate != 0; i++) {
- if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
+ if (abs(req->rate - pll_cfg[i].rate) < abs(req->rate - best_rate))
best_rate = pll_cfg[i].rate;
}
dev_dbg(clk->dev, "chosen best rate: %lu\n", best_rate);
- return best_rate;
+ req->rate = best_rate;
+
+ return 0;
}
static int hsdk_pll_comm_update_rate(struct hsdk_pll_clk *clk,
@@ -266,7 +267,7 @@ static int hsdk_pll_core_update_rate(struct hsdk_pll_clk *clk,
return -EINVAL;
/*
- * Program divider to div-by-1 if we succesfuly set core clock below
+ * Program divider to div-by-1 if we successfully set core clock below
* 500MHz threshold.
*/
if (rate <= CORE_IF_CLK_THRESHOLD_HZ)
@@ -297,14 +298,13 @@ static int hsdk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops hsdk_pll_ops = {
.recalc_rate = hsdk_pll_recalc_rate,
- .round_rate = hsdk_pll_round_rate,
+ .determine_rate = hsdk_pll_determine_rate,
.set_rate = hsdk_pll_set_rate,
};
static int hsdk_pll_clk_probe(struct platform_device *pdev)
{
int ret;
- struct resource *mem;
const char *parent_name;
unsigned int num_parents;
struct hsdk_pll_clk *pll_clk;
@@ -315,8 +315,7 @@ static int hsdk_pll_clk_probe(struct platform_device *pdev)
if (!pll_clk)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pll_clk->regs = devm_ioremap_resource(dev, mem);
+ pll_clk->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pll_clk->regs))
return PTR_ERR(pll_clk->regs);
@@ -346,14 +345,8 @@ static int hsdk_pll_clk_probe(struct platform_device *pdev)
return ret;
}
- return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
- &pll_clk->hw);
-}
-
-static int hsdk_pll_clk_remove(struct platform_device *pdev)
-{
- of_clk_del_provider(pdev->dev.of_node);
- return 0;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &pll_clk->hw);
}
static void __init of_hsdk_pll_clk_setup(struct device_node *node)
@@ -432,6 +425,5 @@ static struct platform_driver hsdk_pll_clk_driver = {
.of_match_table = hsdk_pll_clk_id,
},
.probe = hsdk_pll_clk_probe,
- .remove = hsdk_pll_clk_remove,
};
builtin_platform_driver(hsdk_pll_clk_driver);
diff --git a/drivers/clk/clk-k210.c b/drivers/clk/clk-k210.c
index 67a7cb3503c3..7b7329a907ab 100644
--- a/drivers/clk/clk-k210.c
+++ b/drivers/clk/clk-k210.c
@@ -11,7 +11,6 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_clk.h>
-#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/clk-provider.h>
#include <linux/bitfield.h>
@@ -495,7 +494,7 @@ static unsigned long k210_pll_get_rate(struct clk_hw *hw,
f = FIELD_GET(K210_PLL_CLKF, reg) + 1;
od = FIELD_GET(K210_PLL_CLKOD, reg) + 1;
- return (u64)parent_rate * f / (r * od);
+ return div_u64((u64)parent_rate * f, r * od);
}
static const struct clk_ops k210_pll_ops = {
@@ -537,6 +536,7 @@ static const struct clk_ops k210_pll2_ops = {
.disable = k210_pll_disable,
.is_enabled = k210_pll_is_enabled,
.recalc_rate = k210_pll_get_rate,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = k210_pll2_set_parent,
.get_parent = k210_pll2_get_parent,
};
@@ -635,6 +635,7 @@ static unsigned long k210_aclk_get_rate(struct clk_hw *hw,
}
static const struct clk_ops k210_aclk_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = k210_aclk_set_parent,
.get_parent = k210_aclk_get_parent,
.recalc_rate = k210_aclk_get_rate,
@@ -774,6 +775,7 @@ static unsigned long k210_clk_get_rate(struct clk_hw *hw,
static const struct clk_ops k210_clk_mux_ops = {
.enable = k210_clk_enable,
.disable = k210_clk_disable,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = k210_clk_set_parent,
.get_parent = k210_clk_get_parent,
.recalc_rate = k210_clk_get_rate,
diff --git a/drivers/clk/clk-lan966x.c b/drivers/clk/clk-lan966x.c
index 81cb90955d68..3c7a48c616bb 100644
--- a/drivers/clk/clk-lan966x.c
+++ b/drivers/clk/clk-lan966x.c
@@ -16,21 +16,26 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <dt-bindings/clock/microchip,lan966x.h>
-
#define GCK_ENA BIT(0)
#define GCK_SRC_SEL GENMASK(9, 8)
#define GCK_PRESCALER GENMASK(23, 16)
#define DIV_MAX 255
-static const char *clk_names[N_CLOCKS] = {
+static const char * const lan966x_clk_names[] = {
"qspi0", "qspi1", "qspi2", "sdmmc0",
"pi", "mcan0", "mcan1", "flexcom0",
"flexcom1", "flexcom2", "flexcom3",
"flexcom4", "timer1", "usb_refclk",
};
+static const char * const lan969x_clk_names[] = {
+ "qspi0", "qspi2", "sdmmc0", "sdmmc1",
+ "mcan0", "mcan1", "flexcom0",
+ "flexcom1", "flexcom2", "flexcom3",
+ "timer1", "usb_refclk",
+};
+
struct lan966x_gck {
struct clk_hw hw;
void __iomem *reg;
@@ -53,7 +58,7 @@ struct clk_gate_soc_desc {
int bit_idx;
};
-static const struct clk_gate_soc_desc clk_gate_desc[] = {
+static const struct clk_gate_soc_desc lan966x_clk_gate_desc[] = {
{ "uhphs", 11 },
{ "udphs", 10 },
{ "mcramc", 9 },
@@ -61,6 +66,37 @@ static const struct clk_gate_soc_desc clk_gate_desc[] = {
{ }
};
+static const struct clk_gate_soc_desc lan969x_clk_gate_desc[] = {
+ { "usb_drd", 10 },
+ { "mcramc", 9 },
+ { "hmatrix", 8 },
+ { }
+};
+
+struct lan966x_match_data {
+ char *name;
+ const char * const *clk_name;
+ const struct clk_gate_soc_desc *clk_gate_desc;
+ u8 num_generic_clks;
+ u8 num_total_clks;
+};
+
+static struct lan966x_match_data lan966x_desc = {
+ .name = "lan966x",
+ .clk_name = lan966x_clk_names,
+ .clk_gate_desc = lan966x_clk_gate_desc,
+ .num_total_clks = 18,
+ .num_generic_clks = 14,
+};
+
+static struct lan966x_match_data lan969x_desc = {
+ .name = "lan969x",
+ .clk_name = lan969x_clk_names,
+ .clk_gate_desc = lan969x_clk_gate_desc,
+ .num_total_clks = 15,
+ .num_generic_clks = 12,
+};
+
static DEFINE_SPINLOCK(clk_gate_lock);
static void __iomem *base;
@@ -103,22 +139,6 @@ static int lan966x_gck_set_rate(struct clk_hw *hw,
return 0;
}
-static long lan966x_gck_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
-{
- unsigned int div;
-
- if (rate == 0 || *parent_rate == 0)
- return -EINVAL;
-
- if (rate >= *parent_rate)
- return *parent_rate;
-
- div = DIV_ROUND_CLOSEST(*parent_rate, rate);
-
- return *parent_rate / div;
-}
-
static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@@ -177,7 +197,6 @@ static const struct clk_ops lan966x_gck_ops = {
.enable = lan966x_gck_enable,
.disable = lan966x_gck_disable,
.set_rate = lan966x_gck_set_rate,
- .round_rate = lan966x_gck_round_rate,
.recalc_rate = lan966x_gck_recalc_rate,
.determine_rate = lan966x_gck_determine_rate,
.set_parent = lan966x_gck_set_parent,
@@ -203,24 +222,26 @@ static struct clk_hw *lan966x_gck_clk_register(struct device *dev, int i)
};
static int lan966x_gate_clk_register(struct device *dev,
+ const struct lan966x_match_data *data,
struct clk_hw_onecell_data *hw_data,
void __iomem *gate_base)
{
- int i;
+ for (int i = data->num_generic_clks; i < data->num_total_clks; ++i) {
+ int idx = i - data->num_generic_clks;
+ const struct clk_gate_soc_desc *desc;
- for (i = GCK_GATE_UHPHS; i < N_CLOCKS; ++i) {
- int idx = i - GCK_GATE_UHPHS;
+ desc = &data->clk_gate_desc[idx];
hw_data->hws[i] =
- devm_clk_hw_register_gate(dev, clk_gate_desc[idx].name,
- "lan966x", 0, gate_base,
- clk_gate_desc[idx].bit_idx,
+ devm_clk_hw_register_gate(dev, desc->name,
+ data->name, 0, gate_base,
+ desc->bit_idx,
0, &clk_gate_lock);
if (IS_ERR(hw_data->hws[i]))
return dev_err_probe(dev, PTR_ERR(hw_data->hws[i]),
"failed to register %s clock\n",
- clk_gate_desc[idx].name);
+ desc->name);
}
return 0;
@@ -228,13 +249,18 @@ static int lan966x_gate_clk_register(struct device *dev,
static int lan966x_clk_probe(struct platform_device *pdev)
{
+ const struct lan966x_match_data *data;
struct clk_hw_onecell_data *hw_data;
struct device *dev = &pdev->dev;
void __iomem *gate_base;
struct resource *res;
int i, ret;
- hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, N_CLOCKS),
+ data = device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, data->num_total_clks),
GFP_KERNEL);
if (!hw_data)
return -ENOMEM;
@@ -245,10 +271,10 @@ static int lan966x_clk_probe(struct platform_device *pdev)
init.ops = &lan966x_gck_ops;
- hw_data->num = GCK_GATE_UHPHS;
+ hw_data->num = data->num_generic_clks;
- for (i = 0; i < GCK_GATE_UHPHS; i++) {
- init.name = clk_names[i];
+ for (i = 0; i < data->num_generic_clks; i++) {
+ init.name = data->clk_name[i];
hw_data->hws[i] = lan966x_gck_clk_register(dev, i);
if (IS_ERR(hw_data->hws[i])) {
dev_err(dev, "failed to register %s clock\n",
@@ -263,9 +289,9 @@ static int lan966x_clk_probe(struct platform_device *pdev)
if (IS_ERR(gate_base))
return PTR_ERR(gate_base);
- hw_data->num = N_CLOCKS;
+ hw_data->num = data->num_total_clks;
- ret = lan966x_gate_clk_register(dev, hw_data, gate_base);
+ ret = lan966x_gate_clk_register(dev, data, hw_data, gate_base);
if (ret)
return ret;
}
@@ -274,7 +300,8 @@ static int lan966x_clk_probe(struct platform_device *pdev)
}
static const struct of_device_id lan966x_clk_dt_ids[] = {
- { .compatible = "microchip,lan966x-gck", },
+ { .compatible = "microchip,lan966x-gck", .data = &lan966x_desc },
+ { .compatible = "microchip,lan9691-gck", .data = &lan969x_desc },
{ }
};
MODULE_DEVICE_TABLE(of, lan966x_clk_dt_ids);
@@ -286,7 +313,7 @@ static struct platform_driver lan966x_clk_driver = {
.of_match_table = lan966x_clk_dt_ids,
},
};
-builtin_platform_driver(lan966x_clk_driver);
+module_platform_driver(lan966x_clk_driver);
MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>");
MODULE_DESCRIPTION("LAN966X clock driver");
diff --git a/drivers/clk/clk-lmk04832.c b/drivers/clk/clk-lmk04832.c
index f416f8bc2898..b2107b31efa2 100644
--- a/drivers/clk/clk-lmk04832.c
+++ b/drivers/clk/clk-lmk04832.c
@@ -12,12 +12,10 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
-#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
-#include <linux/uaccess.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@@ -136,6 +134,11 @@
/* 0x14b - 0x152 Holdover */
/* 0x153 - 0x15f PLL1 Configuration */
+#define LMK04832_REG_PLL1_LD 0x15f
+#define LMK04832_BIT_PLL1_LD_MUX GENMASK(7, 3)
+#define LMK04832_VAL_PLL1_LD_MUX_SPI_RDBK 0x07
+#define LMK04832_BIT_PLL1_LD_TYPE GENMASK(2, 0)
+#define LMK04832_VAL_PLL1_LD_TYPE_OUT_PP 0x03
/* 0x160 - 0x16e PLL2 Configuration */
#define LMK04832_REG_PLL2_R_MSB 0x160
@@ -177,14 +180,14 @@ enum lmk04832_device_types {
};
/**
- * lmk04832_device_info - Holds static device information that is specific to
- * the chip revision
+ * struct lmk04832_device_info - Holds static device information that is
+ * specific to the chip revision
*
- * pid: Product Identifier
- * maskrev: IC version identifier
- * num_channels: Number of available output channels (clkout count)
- * vco0_range: {min, max} of the VCO0 operating range (in MHz)
- * vco1_range: {min, max} of the VCO1 operating range (in MHz)
+ * @pid: Product Identifier
+ * @maskrev: IC version identifier
+ * @num_channels: Number of available output channels (clkout count)
+ * @vco0_range: {min, max} of the VCO0 operating range (in MHz)
+ * @vco1_range: {min, max} of the VCO1 operating range (in MHz)
*/
struct lmk04832_device_info {
u16 pid;
@@ -208,6 +211,7 @@ enum lmk04832_rdbk_type {
RDBK_CLKIN_SEL0,
RDBK_CLKIN_SEL1,
RDBK_RESET,
+ RDBK_PLL1_LD,
};
struct lmk_dclk {
@@ -282,7 +286,7 @@ static bool lmk04832_regmap_rd_regs(struct device *dev, unsigned int reg)
default:
return false;
};
-};
+}
static bool lmk04832_regmap_wr_regs(struct device *dev, unsigned int reg)
{
@@ -305,7 +309,7 @@ static bool lmk04832_regmap_wr_regs(struct device *dev, unsigned int reg)
default:
return false;
};
-};
+}
static const struct regmap_config regmap_config = {
.name = "lmk04832",
@@ -371,7 +375,7 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
unsigned long prate)
{
struct lmk04832 *lmk = container_of(hw, struct lmk04832, vco);
- unsigned int pll2_p[] = {8, 2, 2, 3, 4, 5, 6, 7};
+ static const unsigned int pll2_p[] = {8, 2, 2, 3, 4, 5, 6, 7};
unsigned int pll2_n, p, pll2_r;
unsigned int pll2_misc;
unsigned long vco_rate;
@@ -403,7 +407,7 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
pll2_misc)) * pll2_n * pll2_p[p] / pll2_r;
return vco_rate;
-};
+}
/**
* lmk04832_check_vco_ranges - Check requested VCO frequency against VCO ranges
@@ -414,7 +418,7 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
* The LMK04832 has 2 internal VCO, each with independent operating ranges.
* Use the device_info structure to determine which VCO to use based on rate.
*
- * Returns VCO_MUX value or negative errno.
+ * Returns: VCO_MUX value or negative errno.
*/
static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate)
{
@@ -451,7 +455,7 @@ static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate)
*
* VCO = OSCin * 2 * PLL2_N * PLL2_P / PLL2_R
*
- * Returns vco rate or negative errno.
+ * Returns: vco rate or negative errno.
*/
static long lmk04832_calc_pll2_params(unsigned long prate, unsigned long rate,
unsigned int *n, unsigned int *p,
@@ -487,29 +491,34 @@ static long lmk04832_calc_pll2_params(unsigned long prate, unsigned long rate,
return DIV_ROUND_CLOSEST(prate * 2 * pll2_p * pll2_n, pll2_r);
}
-static long lmk04832_vco_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int lmk04832_vco_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct lmk04832 *lmk = container_of(hw, struct lmk04832, vco);
unsigned int n, p, r;
long vco_rate;
int ret;
- ret = lmk04832_check_vco_ranges(lmk, rate);
+ ret = lmk04832_check_vco_ranges(lmk, req->rate);
if (ret < 0)
return ret;
- vco_rate = lmk04832_calc_pll2_params(*prate, rate, &n, &p, &r);
+ vco_rate = lmk04832_calc_pll2_params(req->best_parent_rate, req->rate,
+ &n, &p, &r);
if (vco_rate < 0) {
dev_err(lmk->dev, "PLL2 parameters out of range\n");
- return vco_rate;
+ req->rate = vco_rate;
+
+ return 0;
}
- if (rate != vco_rate)
+ if (req->rate != vco_rate)
return -EINVAL;
- return vco_rate;
-};
+ req->rate = vco_rate;
+
+ return 0;
+}
static int lmk04832_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
@@ -568,14 +577,14 @@ static int lmk04832_vco_set_rate(struct clk_hw *hw, unsigned long rate,
return regmap_write(lmk->regmap, LMK04832_REG_PLL2_N_2,
FIELD_GET(0x0000ff, n));
-};
+}
static const struct clk_ops lmk04832_vco_ops = {
.is_enabled = lmk04832_vco_is_enabled,
.prepare = lmk04832_vco_prepare,
.unprepare = lmk04832_vco_unprepare,
.recalc_rate = lmk04832_vco_recalc_rate,
- .round_rate = lmk04832_vco_round_rate,
+ .determine_rate = lmk04832_vco_determine_rate,
.set_rate = lmk04832_vco_set_rate,
};
@@ -633,7 +642,7 @@ static int lmk04832_register_vco(struct lmk04832 *lmk)
static int lmk04832_clkout_set_ddly(struct lmk04832 *lmk, int id)
{
- int dclk_div_adj[] = {0, 0, -2, -2, 0, 3, -1, 0};
+ static const int dclk_div_adj[] = {0, 0, -2, -2, 0, 3, -1, 0};
unsigned int sclkx_y_ddly = 10;
unsigned int dclkx_y_ddly;
unsigned int dclkx_y_div;
@@ -884,25 +893,27 @@ static unsigned long lmk04832_sclk_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_CLOSEST(prate, sysref_div);
}
-static long lmk04832_sclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int lmk04832_sclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct lmk04832 *lmk = container_of(hw, struct lmk04832, sclk);
unsigned long sclk_rate;
unsigned int sysref_div;
- sysref_div = DIV_ROUND_CLOSEST(*prate, rate);
- sclk_rate = DIV_ROUND_CLOSEST(*prate, sysref_div);
+ sysref_div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
+ sclk_rate = DIV_ROUND_CLOSEST(req->best_parent_rate, sysref_div);
if (sysref_div < 0x07 || sysref_div > 0x1fff) {
dev_err(lmk->dev, "SYSREF divider out of range\n");
return -EINVAL;
}
- if (rate != sclk_rate)
+ if (req->rate != sclk_rate)
return -EINVAL;
- return sclk_rate;
+ req->rate = sclk_rate;
+
+ return 0;
}
static int lmk04832_sclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -941,7 +952,7 @@ static const struct clk_ops lmk04832_sclk_ops = {
.prepare = lmk04832_sclk_prepare,
.unprepare = lmk04832_sclk_unprepare,
.recalc_rate = lmk04832_sclk_recalc_rate,
- .round_rate = lmk04832_sclk_round_rate,
+ .determine_rate = lmk04832_sclk_determine_rate,
.set_rate = lmk04832_sclk_set_rate,
};
@@ -1063,29 +1074,31 @@ static unsigned long lmk04832_dclk_recalc_rate(struct clk_hw *hw,
rate = DIV_ROUND_CLOSEST(prate, dclk_div);
return rate;
-};
+}
-static long lmk04832_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int lmk04832_dclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct lmk_dclk *dclk = container_of(hw, struct lmk_dclk, hw);
struct lmk04832 *lmk = dclk->lmk;
unsigned long dclk_rate;
unsigned int dclk_div;
- dclk_div = DIV_ROUND_CLOSEST(*prate, rate);
- dclk_rate = DIV_ROUND_CLOSEST(*prate, dclk_div);
+ dclk_div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
+ dclk_rate = DIV_ROUND_CLOSEST(req->best_parent_rate, dclk_div);
if (dclk_div < 1 || dclk_div > 0x3ff) {
dev_err(lmk->dev, "%s_div out of range\n", clk_hw_get_name(hw));
return -EINVAL;
}
- if (rate != dclk_rate)
+ if (req->rate != dclk_rate)
return -EINVAL;
- return dclk_rate;
-};
+ req->rate = dclk_rate;
+
+ return 0;
+}
static int lmk04832_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
@@ -1147,14 +1160,14 @@ static int lmk04832_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
dev_err(lmk->dev, "SYNC sequence failed\n");
return ret;
-};
+}
static const struct clk_ops lmk04832_dclk_ops = {
.is_enabled = lmk04832_dclk_is_enabled,
.prepare = lmk04832_dclk_prepare,
.unprepare = lmk04832_dclk_unprepare,
.recalc_rate = lmk04832_dclk_recalc_rate,
- .round_rate = lmk04832_dclk_round_rate,
+ .determine_rate = lmk04832_dclk_determine_rate,
.set_rate = lmk04832_dclk_set_rate,
};
@@ -1281,6 +1294,7 @@ static const struct clk_ops lmk04832_clkout_ops = {
.is_enabled = lmk04832_clkout_is_enabled,
.prepare = lmk04832_clkout_prepare,
.unprepare = lmk04832_clkout_unprepare,
+ .determine_rate = __clk_mux_determine_rate,
.set_parent = lmk04832_clkout_set_parent,
.get_parent = lmk04832_clkout_get_parent,
};
@@ -1298,6 +1312,7 @@ static int lmk04832_register_clkout(struct lmk04832 *lmk, const int num)
sprintf(dclk_name, "lmk-dclk%02d_%02d", num, num + 1);
init.name = dclk_name;
parent_names[0] = clk_hw_get_name(&lmk->vco);
+ init.parent_names = parent_names;
init.ops = &lmk04832_dclk_ops;
init.flags = CLK_SET_RATE_PARENT;
init.num_parents = 1;
@@ -1346,6 +1361,10 @@ static int lmk04832_set_spi_rdbk(const struct lmk04832 *lmk, const int rdbk_pin)
{
int reg;
int ret;
+ int val = FIELD_PREP(LMK04832_BIT_CLKIN_SEL_MUX,
+ LMK04832_VAL_CLKIN_SEL_MUX_SPI_RDBK) |
+ FIELD_PREP(LMK04832_BIT_CLKIN_SEL_TYPE,
+ LMK04832_VAL_CLKIN_SEL_TYPE_OUT);
dev_info(lmk->dev, "setting up 4-wire mode\n");
ret = regmap_write(lmk->regmap, LMK04832_REG_RST3W,
@@ -1363,15 +1382,18 @@ static int lmk04832_set_spi_rdbk(const struct lmk04832 *lmk, const int rdbk_pin)
case RDBK_RESET:
reg = LMK04832_REG_CLKIN_RST;
break;
+ case RDBK_PLL1_LD:
+ reg = LMK04832_REG_PLL1_LD;
+ val = FIELD_PREP(LMK04832_BIT_PLL1_LD_MUX,
+ LMK04832_VAL_PLL1_LD_MUX_SPI_RDBK) |
+ FIELD_PREP(LMK04832_BIT_PLL1_LD_TYPE,
+ LMK04832_VAL_PLL1_LD_TYPE_OUT_PP);
+ break;
default:
return -EINVAL;
}
- return regmap_write(lmk->regmap, reg,
- FIELD_PREP(LMK04832_BIT_CLKIN_SEL_MUX,
- LMK04832_VAL_CLKIN_SEL_MUX_SPI_RDBK) |
- FIELD_PREP(LMK04832_BIT_CLKIN_SEL_TYPE,
- LMK04832_VAL_CLKIN_SEL_TYPE_OUT));
+ return regmap_write(lmk->regmap, reg, val);
}
static int lmk04832_probe(struct spi_device *spi)
@@ -1392,16 +1414,12 @@ static int lmk04832_probe(struct spi_device *spi)
lmk->dev = &spi->dev;
- lmk->oscin = devm_clk_get(lmk->dev, "oscin");
+ lmk->oscin = devm_clk_get_enabled(lmk->dev, "oscin");
if (IS_ERR(lmk->oscin)) {
dev_err(lmk->dev, "failed to get oscin clock\n");
return PTR_ERR(lmk->oscin);
}
- ret = clk_prepare_enable(lmk->oscin);
- if (ret)
- return ret;
-
lmk->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
GPIOD_OUT_LOW);
@@ -1409,14 +1427,14 @@ static int lmk04832_probe(struct spi_device *spi)
sizeof(struct lmk_dclk), GFP_KERNEL);
if (!lmk->dclk) {
ret = -ENOMEM;
- goto err_disable_oscin;
+ return ret;
}
lmk->clkout = devm_kcalloc(lmk->dev, info->num_channels,
sizeof(*lmk->clkout), GFP_KERNEL);
if (!lmk->clkout) {
ret = -ENOMEM;
- goto err_disable_oscin;
+ return ret;
}
lmk->clk_data = devm_kzalloc(lmk->dev, struct_size(lmk->clk_data, hws,
@@ -1424,7 +1442,7 @@ static int lmk04832_probe(struct spi_device *spi)
GFP_KERNEL);
if (!lmk->clk_data) {
ret = -ENOMEM;
- goto err_disable_oscin;
+ return ret;
}
device_property_read_u32(lmk->dev, "ti,vco-hz", &lmk->vco_rate);
@@ -1452,7 +1470,7 @@ static int lmk04832_probe(struct spi_device *spi)
dev_err(lmk->dev, "missing reg property in child: %s\n",
child->full_name);
of_node_put(child);
- goto err_disable_oscin;
+ return ret;
}
of_property_read_u32(child, "ti,clkout-fmt",
@@ -1473,7 +1491,7 @@ static int lmk04832_probe(struct spi_device *spi)
__func__, PTR_ERR(lmk->regmap));
ret = PTR_ERR(lmk->regmap);
- goto err_disable_oscin;
+ return ret;
}
regmap_write(lmk->regmap, LMK04832_REG_RST3W, LMK04832_BIT_RESET);
@@ -1483,7 +1501,7 @@ static int lmk04832_probe(struct spi_device *spi)
&rdbk_pin);
ret = lmk04832_set_spi_rdbk(lmk, rdbk_pin);
if (ret)
- goto err_disable_oscin;
+ return ret;
}
regmap_bulk_read(lmk->regmap, LMK04832_REG_ID_PROD_MSB, &tmp, 3);
@@ -1491,13 +1509,13 @@ static int lmk04832_probe(struct spi_device *spi)
dev_err(lmk->dev, "unsupported device type: pid 0x%04x, maskrev 0x%02x\n",
tmp[0] << 8 | tmp[1], tmp[2]);
ret = -EINVAL;
- goto err_disable_oscin;
+ return ret;
}
ret = lmk04832_register_vco(lmk);
if (ret) {
dev_err(lmk->dev, "failed to init device clock path\n");
- goto err_disable_oscin;
+ return ret;
}
if (lmk->vco_rate) {
@@ -1505,52 +1523,37 @@ static int lmk04832_probe(struct spi_device *spi)
ret = clk_set_rate(lmk->vco.clk, lmk->vco_rate);
if (ret) {
dev_err(lmk->dev, "failed to set VCO rate\n");
- goto err_disable_vco;
+ return ret;
}
}
ret = lmk04832_register_sclk(lmk);
if (ret) {
dev_err(lmk->dev, "failed to init SYNC/SYSREF clock path\n");
- goto err_disable_vco;
+ return ret;
}
for (i = 0; i < info->num_channels; i++) {
ret = lmk04832_register_clkout(lmk, i);
if (ret) {
dev_err(lmk->dev, "failed to register clk %d\n", i);
- goto err_disable_vco;
+ return ret;
}
}
lmk->clk_data->num = info->num_channels;
- ret = of_clk_add_hw_provider(lmk->dev->of_node, of_clk_hw_onecell_get,
- lmk->clk_data);
+ ret = devm_of_clk_add_hw_provider(lmk->dev, of_clk_hw_onecell_get,
+ lmk->clk_data);
if (ret) {
dev_err(lmk->dev, "failed to add provider (%d)\n", ret);
- goto err_disable_vco;
+ return ret;
}
spi_set_drvdata(spi, lmk);
return 0;
-
-err_disable_vco:
- clk_disable_unprepare(lmk->vco.clk);
-
-err_disable_oscin:
- clk_disable_unprepare(lmk->oscin);
-
- return ret;
}
-static void lmk04832_remove(struct spi_device *spi)
-{
- struct lmk04832 *lmk = spi_get_drvdata(spi);
-
- clk_disable_unprepare(lmk->oscin);
- of_clk_del_provider(spi->dev.of_node);
-}
static const struct spi_device_id lmk04832_id[] = {
{ "lmk04832", LMK04832 },
{}
@@ -1569,7 +1572,6 @@ static struct spi_driver lmk04832_driver = {
.of_match_table = lmk04832_of_id,
},
.probe = lmk04832_probe,
- .remove = lmk04832_remove,
.id_table = lmk04832_id,
};
module_spi_driver(lmk04832_driver);
diff --git a/drivers/clk/clk-lochnagar.c b/drivers/clk/clk-lochnagar.c
index 565bcd0cdde9..5561a2c66b69 100644
--- a/drivers/clk/clk-lochnagar.c
+++ b/drivers/clk/clk-lochnagar.c
@@ -12,14 +12,14 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/mfd/lochnagar1_regs.h>
#include <linux/mfd/lochnagar2_regs.h>
-#include <dt-bindings/clk/lochnagar.h>
+#include <dt-bindings/clock/lochnagar.h>
#define LOCHNAGAR_NUM_CLOCKS (LOCHNAGAR_SPDIF_CLKOUT + 1)
@@ -209,6 +209,7 @@ static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
static const struct clk_ops lochnagar_clk_ops = {
.prepare = lochnagar_clk_prepare,
.unprepare = lochnagar_clk_unprepare,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = lochnagar_clk_set_parent,
.get_parent = lochnagar_clk_get_parent,
};
@@ -241,22 +242,17 @@ static int lochnagar_clk_probe(struct platform_device *pdev)
};
struct device *dev = &pdev->dev;
struct lochnagar_clk_priv *priv;
- const struct of_device_id *of_id;
struct lochnagar_clk *lclk;
struct lochnagar_config *conf;
int ret, i;
- of_id = of_match_device(lochnagar_of_match, dev);
- if (!of_id)
- return -EINVAL;
-
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->regmap = dev_get_regmap(dev->parent, NULL);
- conf = (struct lochnagar_config *)of_id->data;
+ conf = (struct lochnagar_config *)device_get_match_data(dev);
memcpy(priv->lclks, conf->clks, sizeof(priv->lclks));
diff --git a/drivers/clk/clk-loongson1.c b/drivers/clk/clk-loongson1.c
new file mode 100644
index 000000000000..f9f060d08a5f
--- /dev/null
+++ b/drivers/clk/clk-loongson1.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Clock driver for Loongson-1 SoC
+ *
+ * Copyright (C) 2012-2023 Keguang Zhang <keguang.zhang@gmail.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/printk.h>
+
+#include <dt-bindings/clock/loongson,ls1x-clk.h>
+
+/* Loongson 1 Clock Register Definitions */
+#define CLK_PLL_FREQ 0x0
+#define CLK_PLL_DIV 0x4
+
+static DEFINE_SPINLOCK(ls1x_clk_div_lock);
+
+struct ls1x_clk_pll_data {
+ u32 fixed;
+ u8 shift;
+ u8 int_shift;
+ u8 int_width;
+ u8 frac_shift;
+ u8 frac_width;
+};
+
+struct ls1x_clk_div_data {
+ u8 shift;
+ u8 width;
+ unsigned long flags;
+ const struct clk_div_table *table;
+ u8 bypass_shift;
+ u8 bypass_inv;
+ spinlock_t *lock; /* protect access to DIV registers */
+};
+
+struct ls1x_clk {
+ void __iomem *reg;
+ unsigned int offset;
+ struct clk_hw hw;
+ const void *data;
+};
+
+#define to_ls1x_clk(_hw) container_of(_hw, struct ls1x_clk, hw)
+
+static inline unsigned long ls1x_pll_rate_part(unsigned int val,
+ unsigned int shift,
+ unsigned int width)
+{
+ return (val & GENMASK(shift + width, shift)) >> shift;
+}
+
+static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ls1x_clk *ls1x_clk = to_ls1x_clk(hw);
+ const struct ls1x_clk_pll_data *d = ls1x_clk->data;
+ u32 val, rate;
+
+ val = readl(ls1x_clk->reg);
+ rate = d->fixed;
+ rate += ls1x_pll_rate_part(val, d->int_shift, d->int_width);
+ if (d->frac_width)
+ rate += ls1x_pll_rate_part(val, d->frac_shift, d->frac_width);
+ rate *= parent_rate;
+ rate >>= d->shift;
+
+ return rate;
+}
+
+static const struct clk_ops ls1x_pll_clk_ops = {
+ .recalc_rate = ls1x_pll_recalc_rate,
+};
+
+static unsigned long ls1x_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ls1x_clk *ls1x_clk = to_ls1x_clk(hw);
+ const struct ls1x_clk_div_data *d = ls1x_clk->data;
+ unsigned int val;
+
+ val = readl(ls1x_clk->reg) >> d->shift;
+ val &= clk_div_mask(d->width);
+
+ return divider_recalc_rate(hw, parent_rate, val, d->table,
+ d->flags, d->width);
+}
+
+static int ls1x_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ls1x_clk *ls1x_clk = to_ls1x_clk(hw);
+ const struct ls1x_clk_div_data *d = ls1x_clk->data;
+
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate,
+ d->table, d->width, d->flags);
+
+ return 0;
+}
+
+static int ls1x_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ls1x_clk *ls1x_clk = to_ls1x_clk(hw);
+ const struct ls1x_clk_div_data *d = ls1x_clk->data;
+ int val, div_val;
+ unsigned long flags = 0;
+
+ div_val = divider_get_val(rate, parent_rate, d->table,
+ d->width, d->flags);
+ if (div_val < 0)
+ return div_val;
+
+ spin_lock_irqsave(d->lock, flags);
+
+ /* Bypass the clock */
+ val = readl(ls1x_clk->reg);
+ if (d->bypass_inv)
+ val &= ~BIT(d->bypass_shift);
+ else
+ val |= BIT(d->bypass_shift);
+ writel(val, ls1x_clk->reg);
+
+ val = readl(ls1x_clk->reg);
+ val &= ~(clk_div_mask(d->width) << d->shift);
+ val |= (u32)div_val << d->shift;
+ writel(val, ls1x_clk->reg);
+
+ /* Restore the clock */
+ val = readl(ls1x_clk->reg);
+ if (d->bypass_inv)
+ val |= BIT(d->bypass_shift);
+ else
+ val &= ~BIT(d->bypass_shift);
+ writel(val, ls1x_clk->reg);
+
+ spin_unlock_irqrestore(d->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops ls1x_clk_divider_ops = {
+ .recalc_rate = ls1x_divider_recalc_rate,
+ .determine_rate = ls1x_divider_determine_rate,
+ .set_rate = ls1x_divider_set_rate,
+};
+
+#define LS1X_CLK_PLL(_name, _offset, _fixed, _shift, \
+ f_shift, f_width, i_shift, i_width) \
+struct ls1x_clk _name = { \
+ .offset = (_offset), \
+ .data = &(const struct ls1x_clk_pll_data) { \
+ .fixed = (_fixed), \
+ .shift = (_shift), \
+ .int_shift = (i_shift), \
+ .int_width = (i_width), \
+ .frac_shift = (f_shift), \
+ .frac_width = (f_width), \
+ }, \
+ .hw.init = &(const struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &ls1x_pll_clk_ops, \
+ .parent_data = &(const struct clk_parent_data) { \
+ .fw_name = "xtal", \
+ .name = "xtal", \
+ .index = -1, \
+ }, \
+ .num_parents = 1, \
+ }, \
+}
+
+#define LS1X_CLK_DIV(_name, _pname, _offset, _shift, _width, \
+ _table, _bypass_shift, _bypass_inv, _flags) \
+struct ls1x_clk _name = { \
+ .offset = (_offset), \
+ .data = &(const struct ls1x_clk_div_data){ \
+ .shift = (_shift), \
+ .width = (_width), \
+ .table = (_table), \
+ .flags = (_flags), \
+ .bypass_shift = (_bypass_shift), \
+ .bypass_inv = (_bypass_inv), \
+ .lock = &ls1x_clk_div_lock, \
+ }, \
+ .hw.init = &(const struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &ls1x_clk_divider_ops, \
+ .parent_hws = (const struct clk_hw *[]) { _pname }, \
+ .num_parents = 1, \
+ .flags = CLK_GET_RATE_NOCACHE, \
+ }, \
+}
+
+static LS1X_CLK_PLL(ls1b_clk_pll, CLK_PLL_FREQ, 12, 1, 0, 5, 0, 0);
+static LS1X_CLK_DIV(ls1b_clk_cpu, &ls1b_clk_pll.hw, CLK_PLL_DIV,
+ 20, 4, NULL, 8, 0,
+ CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST);
+static LS1X_CLK_DIV(ls1b_clk_dc, &ls1b_clk_pll.hw, CLK_PLL_DIV,
+ 26, 4, NULL, 12, 0, CLK_DIVIDER_ONE_BASED);
+static LS1X_CLK_DIV(ls1b_clk_ahb, &ls1b_clk_pll.hw, CLK_PLL_DIV,
+ 14, 4, NULL, 10, 0, CLK_DIVIDER_ONE_BASED);
+static CLK_FIXED_FACTOR(ls1b_clk_apb, "ls1b_clk_apb", "ls1b_clk_ahb", 2, 1,
+ CLK_SET_RATE_PARENT);
+
+static struct clk_hw_onecell_data ls1b_clk_hw_data = {
+ .hws = {
+ [LS1X_CLKID_PLL] = &ls1b_clk_pll.hw,
+ [LS1X_CLKID_CPU] = &ls1b_clk_cpu.hw,
+ [LS1X_CLKID_DC] = &ls1b_clk_dc.hw,
+ [LS1X_CLKID_AHB] = &ls1b_clk_ahb.hw,
+ [LS1X_CLKID_APB] = &ls1b_clk_apb.hw,
+ },
+ .num = CLK_NR_CLKS,
+};
+
+static const struct clk_div_table ls1c_ahb_div_table[] = {
+ [0] = { .val = 0, .div = 2 },
+ [1] = { .val = 1, .div = 4 },
+ [2] = { .val = 2, .div = 3 },
+ [3] = { .val = 3, .div = 3 },
+ [4] = { /* sentinel */ }
+};
+
+static LS1X_CLK_PLL(ls1c_clk_pll, CLK_PLL_FREQ, 0, 2, 8, 8, 16, 8);
+static LS1X_CLK_DIV(ls1c_clk_cpu, &ls1c_clk_pll.hw, CLK_PLL_DIV,
+ 8, 7, NULL, 0, 1,
+ CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST);
+static LS1X_CLK_DIV(ls1c_clk_dc, &ls1c_clk_pll.hw, CLK_PLL_DIV,
+ 24, 7, NULL, 4, 1, CLK_DIVIDER_ONE_BASED);
+static LS1X_CLK_DIV(ls1c_clk_ahb, &ls1c_clk_cpu.hw, CLK_PLL_FREQ,
+ 0, 2, ls1c_ahb_div_table, 0, 0, CLK_DIVIDER_ALLOW_ZERO);
+static CLK_FIXED_FACTOR(ls1c_clk_apb, "ls1c_clk_apb", "ls1c_clk_ahb", 1, 1,
+ CLK_SET_RATE_PARENT);
+
+static struct clk_hw_onecell_data ls1c_clk_hw_data = {
+ .hws = {
+ [LS1X_CLKID_PLL] = &ls1c_clk_pll.hw,
+ [LS1X_CLKID_CPU] = &ls1c_clk_cpu.hw,
+ [LS1X_CLKID_DC] = &ls1c_clk_dc.hw,
+ [LS1X_CLKID_AHB] = &ls1c_clk_ahb.hw,
+ [LS1X_CLKID_APB] = &ls1c_clk_apb.hw,
+ },
+ .num = CLK_NR_CLKS,
+};
+
+static void __init ls1x_clk_init(struct device_node *np,
+ struct clk_hw_onecell_data *hw_data)
+{
+ struct ls1x_clk *ls1x_clk;
+ void __iomem *reg;
+ int i, ret;
+
+ reg = of_iomap(np, 0);
+ if (!reg) {
+ pr_err("Unable to map base for %pOF\n", np);
+ return;
+ }
+
+ for (i = 0; i < hw_data->num; i++) {
+ /* array might be sparse */
+ if (!hw_data->hws[i])
+ continue;
+
+ if (i != LS1X_CLKID_APB) {
+ ls1x_clk = to_ls1x_clk(hw_data->hws[i]);
+ ls1x_clk->reg = reg + ls1x_clk->offset;
+ }
+
+ ret = of_clk_hw_register(np, hw_data->hws[i]);
+ if (ret)
+ goto err;
+ }
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_data);
+ if (!ret)
+ return;
+
+err:
+ pr_err("Failed to register %pOF\n", np);
+
+ while (--i >= 0)
+ clk_hw_unregister(hw_data->hws[i]);
+
+ iounmap(reg);
+}
+
+static void __init ls1b_clk_init(struct device_node *np)
+{
+ return ls1x_clk_init(np, &ls1b_clk_hw_data);
+}
+
+static void __init ls1c_clk_init(struct device_node *np)
+{
+ return ls1x_clk_init(np, &ls1c_clk_hw_data);
+}
+
+CLK_OF_DECLARE(ls1b_clk, "loongson,ls1b-clk", ls1b_clk_init);
+CLK_OF_DECLARE(ls1c_clk, "loongson,ls1c-clk", ls1c_clk_init);
diff --git a/drivers/clk/clk-loongson2.c b/drivers/clk/clk-loongson2.c
new file mode 100644
index 000000000000..9c4c6c99db3e
--- /dev/null
+++ b/drivers/clk/clk-loongson2.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
+ * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <dt-bindings/clock/loongson,ls2k-clk.h>
+
+enum loongson2_clk_type {
+ CLK_TYPE_PLL,
+ CLK_TYPE_SCALE,
+ CLK_TYPE_DIVIDER,
+ CLK_TYPE_GATE,
+ CLK_TYPE_FIXED,
+ CLK_TYPE_NONE,
+};
+
+struct loongson2_clk_provider {
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t clk_lock; /* protect access to DIV registers */
+
+ /* Must be last --ends in a flexible-array member. */
+ struct clk_hw_onecell_data clk_data;
+};
+
+struct loongson2_clk_data {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 div_shift;
+ u8 div_width;
+ u8 mult_shift;
+ u8 mult_width;
+ u8 bit_idx;
+};
+
+struct loongson2_clk_board_info {
+ u8 id;
+ enum loongson2_clk_type type;
+ const char *name;
+ const char *parent_name;
+ unsigned long fixed_rate;
+ unsigned long flags;
+ u8 reg_offset;
+ u8 div_shift;
+ u8 div_width;
+ u8 mult_shift;
+ u8 mult_width;
+ u8 bit_idx;
+};
+
+#define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_DIVIDER, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .reg_offset = _offset, \
+ .div_shift = _dshift, \
+ .div_width = _dwidth, \
+ }
+
+#define CLK_PLL(_id, _name, _offset, _mshift, _mwidth, \
+ _dshift, _dwidth) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_PLL, \
+ .name = _name, \
+ .parent_name = NULL, \
+ .reg_offset = _offset, \
+ .mult_shift = _mshift, \
+ .mult_width = _mwidth, \
+ .div_shift = _dshift, \
+ .div_width = _dwidth, \
+ }
+
+#define CLK_SCALE(_id, _name, _pname, _offset, \
+ _dshift, _dwidth) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_SCALE, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .reg_offset = _offset, \
+ .div_shift = _dshift, \
+ .div_width = _dwidth, \
+ }
+
+#define CLK_SCALE_MODE(_id, _name, _pname, _offset, \
+ _dshift, _dwidth, _midx) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_SCALE, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .reg_offset = _offset, \
+ .div_shift = _dshift, \
+ .div_width = _dwidth, \
+ .bit_idx = _midx + 1, \
+ }
+
+#define CLK_GATE(_id, _name, _pname, _offset, _bidx) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_GATE, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .reg_offset = _offset, \
+ .bit_idx = _bidx, \
+ }
+
+#define CLK_GATE_FLAGS(_id, _name, _pname, _offset, _bidx, \
+ _flags) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_GATE, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .reg_offset = _offset, \
+ .bit_idx = _bidx, \
+ .flags = _flags \
+ }
+
+#define CLK_FIXED(_id, _name, _pname, _rate) \
+ { \
+ .id = _id, \
+ .type = CLK_TYPE_FIXED, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .fixed_rate = _rate, \
+ }
+
+static const struct loongson2_clk_board_info ls2k0300_clks[] = {
+ /* Reference Clock */
+ CLK_PLL(LS2K0300_NODE_PLL, "pll_node", 0x00, 15, 9, 8, 7),
+ CLK_PLL(LS2K0300_DDR_PLL, "pll_ddr", 0x08, 15, 9, 8, 7),
+ CLK_PLL(LS2K0300_PIX_PLL, "pll_pix", 0x10, 15, 9, 8, 7),
+ CLK_FIXED(LS2K0300_CLK_STABLE, "clk_stable", NULL, 100000000),
+ CLK_FIXED(LS2K0300_CLK_THSENS, "clk_thsens", NULL, 10000000),
+ /* Node PLL */
+ CLK_DIV(LS2K0300_CLK_NODE_DIV, "clk_node_div", "pll_node", 0x00, 24, 7),
+ CLK_DIV(LS2K0300_CLK_GMAC_DIV, "clk_gmac_div", "pll_node", 0x04, 0, 7),
+ CLK_DIV(LS2K0300_CLK_I2S_DIV, "clk_i2s_div", "pll_node", 0x04, 8, 7),
+ CLK_GATE(LS2K0300_CLK_NODE_PLL_GATE, "clk_node_pll_gate", "clk_node_div", 0x00, 0),
+ CLK_GATE(LS2K0300_CLK_GMAC_GATE, "clk_gmac_gate", "clk_gmac_div", 0x00, 1),
+ CLK_GATE(LS2K0300_CLK_I2S_GATE, "clk_i2s_gate", "clk_i2s_div", 0x00, 2),
+ CLK_GATE_FLAGS(LS2K0300_CLK_NODE_GATE, "clk_node_gate", "clk_node_scale", 0x24, 0,
+ CLK_IS_CRITICAL),
+ CLK_SCALE_MODE(LS2K0300_CLK_NODE_SCALE, "clk_node_scale", "clk_node_pll_gate", 0x20, 0, 3,
+ 3),
+ /* DDR PLL */
+ CLK_DIV(LS2K0300_CLK_DDR_DIV, "clk_ddr_div", "pll_ddr", 0x08, 24, 7),
+ CLK_DIV(LS2K0300_CLK_NET_DIV, "clk_net_div", "pll_ddr", 0x0c, 0, 7),
+ CLK_DIV(LS2K0300_CLK_DEV_DIV, "clk_dev_div", "pll_ddr", 0x0c, 8, 7),
+ CLK_GATE(LS2K0300_CLK_NET_GATE, "clk_net_gate", "clk_net_div", 0x08, 1),
+ CLK_GATE(LS2K0300_CLK_DEV_GATE, "clk_dev_gate", "clk_dev_div", 0x08, 2),
+ CLK_GATE_FLAGS(LS2K0300_CLK_DDR_GATE, "clk_ddr_gate", "clk_ddr_div", 0x08, 0,
+ CLK_IS_CRITICAL),
+ /* PIX PLL */
+ CLK_DIV(LS2K0300_CLK_PIX_DIV, "clk_pix_div", "pll_pix", 0x10, 24, 7),
+ CLK_DIV(LS2K0300_CLK_GMACBP_DIV, "clk_gmacbp_div", "pll_pix", 0x14, 0, 7),
+ CLK_GATE(LS2K0300_CLK_PIX_PLL_GATE, "clk_pix_pll_gate", "clk_pix_div", 0x10, 0),
+ CLK_GATE(LS2K0300_CLK_PIX_GATE, "clk_pix_gate", "clk_pix_scale", 0x24, 6),
+ CLK_GATE(LS2K0300_CLK_GMACBP_GATE, "clk_gmacbp_gate", "clk_gmacbp_div", 0x10, 1),
+ CLK_SCALE_MODE(LS2K0300_CLK_PIX_SCALE, "clk_pix_scale", "clk_pix_pll_gate", 0x20, 4, 3, 7),
+ /* clk_dev_gate */
+ CLK_DIV(LS2K0300_CLK_SDIO_SCALE, "clk_sdio_scale", "clk_dev_gate", 0x20, 24, 4),
+ CLK_GATE(LS2K0300_CLK_USB_GATE, "clk_usb_gate", "clk_usb_scale", 0x24, 2),
+ CLK_GATE(LS2K0300_CLK_SDIO_GATE, "clk_sdio_gate", "clk_sdio_scale", 0x24, 4),
+ CLK_GATE(LS2K0300_CLK_APB_GATE, "clk_apb_gate", "clk_apb_scale", 0x24, 3),
+ CLK_GATE_FLAGS(LS2K0300_CLK_BOOT_GATE, "clk_boot_gate", "clk_boot_scale", 0x24, 1,
+ CLK_IS_CRITICAL),
+ CLK_SCALE_MODE(LS2K0300_CLK_USB_SCALE, "clk_usb_scale", "clk_dev_gate", 0x20, 12, 3, 15),
+ CLK_SCALE_MODE(LS2K0300_CLK_APB_SCALE, "clk_apb_scale", "clk_dev_gate", 0x20, 16, 3, 19),
+ CLK_SCALE_MODE(LS2K0300_CLK_BOOT_SCALE, "clk_boot_scale", "clk_dev_gate", 0x20, 8, 3, 11),
+};
+
+static const struct loongson2_clk_board_info ls2k0500_clks[] = {
+ CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 16, 8, 8, 6),
+ CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x8, 16, 8, 8, 6),
+ CLK_PLL(LOONGSON2_DC_PLL, "pll_soc", 0x10, 16, 8, 8, 6),
+ CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x18, 16, 8, 8, 6),
+ CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x20, 16, 8, 8, 6),
+ CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0, 24, 6),
+ CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x8, 24, 6),
+ CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0xc, 8, 6),
+ CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_soc", 0x10, 24, 6),
+ CLK_DIV(LOONGSON2_DC_CLK, "clk_sb", "pll_soc", 0x14, 0, 6),
+ CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_soc", 0x14, 8, 6),
+ CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x18, 24, 6),
+ CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x20, 24, 6),
+ CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb", 0x28, 8, 3),
+ CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb", 0x28, 12, 3),
+ CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_sb", 0x28, 16, 3),
+ CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_sb", 0x28, 20, 3),
+ { /* Sentinel */ },
+};
+
+static const struct loongson2_clk_board_info ls2k1000_clks[] = {
+ CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 32, 10, 26, 6),
+ CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x10, 32, 10, 26, 6),
+ CLK_PLL(LOONGSON2_DC_PLL, "pll_dc", 0x20, 32, 10, 26, 6),
+ CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 32, 10, 26, 6),
+ CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 32, 10, 26, 6),
+ CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0x8, 0, 6),
+ CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x18, 0, 6),
+ CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_ddr", 0x18, 22, 6),
+ /*
+ * The hda clk divisor in the upper 32bits and the clk-prodiver
+ * layer code doesn't support 64bit io operation thus a conversion
+ * is required that subtract shift by 32 and add 4byte to the hda
+ * address
+ */
+ CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0x22, 12, 7),
+ CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "pll_dc", 0x28, 0, 6),
+ CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_dc", 0x28, 22, 6),
+ CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x38, 0, 6),
+ CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x38, 0, 6),
+ CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 8, 3),
+ CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
+ CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_gmac", 0x50, 16, 3),
+ CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_gmac", 0x50, 20, 3),
+ { /* Sentinel */ },
+};
+
+static const struct loongson2_clk_board_info ls2k2000_clks[] = {
+ CLK_PLL(LOONGSON2_DC_PLL, "pll_0", 0, 21, 9, 32, 6),
+ CLK_PLL(LOONGSON2_DDR_PLL, "pll_1", 0x10, 21, 9, 32, 6),
+ CLK_PLL(LOONGSON2_NODE_PLL, "pll_2", 0x20, 21, 9, 32, 6),
+ CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 21, 9, 32, 6),
+ CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 21, 9, 32, 6),
+ CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0", 0, 40),
+ CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0", 0, 41),
+ CLK_GATE(LOONGSON2_RIO_GATE, "rio_gate", "pll_0", 0, 42),
+ CLK_GATE(LOONGSON2_DC_GATE, "dc_gate", "pll_1", 0x10, 40),
+ CLK_GATE(LOONGSON2_DDR_GATE, "ddr_gate", "pll_1", 0x10, 41),
+ CLK_GATE(LOONGSON2_GPU_GATE, "gpu_gate", "pll_1", 0x10, 42),
+ CLK_GATE(LOONGSON2_HDA_GATE, "hda_gate", "pll_2", 0x20, 40),
+ CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2", 0x20, 41),
+ CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2", 0x20, 42),
+ CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),
+ CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),
+ CLK_DIV(LOONGSON2_OUT0_CLK, "clk_out0", "out0_gate", 0, 0, 6),
+ CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "gmac_gate", 0, 7, 6),
+ CLK_DIV(LOONGSON2_RIO_CLK, "clk_rio", "rio_gate", 0, 14, 6),
+ CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "dc_gate", 0x10, 0, 6),
+ CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "gpu_gate", 0x10, 7, 6),
+ CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "ddr_gate", 0x10, 14, 6),
+ CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "hda_gate", 0x20, 0, 6),
+ CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "node_gate", 0x20, 7, 6),
+ CLK_DIV(LOONGSON2_EMMC_CLK, "clk_emmc", "emmc_gate", 0x20, 14, 6),
+ CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x30, 0, 6),
+ CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x40, 0, 6),
+ CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0", 0x50, 12, 3),
+ CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_out0", 0x50, 16, 3),
+ CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_node", 0x50, 20, 3),
+ CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 23, 3),
+ CLK_SCALE(LOONGSON2_DES_CLK, "clk_des", "clk_node", 0x50, 40, 3),
+ CLK_SCALE(LOONGSON2_I2S_CLK, "clk_i2s", "clk_node", 0x50, 44, 3),
+ CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),
+ { /* Sentinel */ },
+};
+
+static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct loongson2_clk_data, hw);
+}
+
+static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
+{
+ return (val & GENMASK(shift + width - 1, shift)) >> shift;
+}
+
+static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u64 val, mult, div;
+ struct loongson2_clk_data *clk = to_loongson2_clk(hw);
+
+ val = readq(clk->reg);
+ mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
+ div = loongson2_rate_part(val, clk->div_shift, clk->div_width);
+
+ return div_u64((u64)parent_rate * mult, div);
+}
+
+static const struct clk_ops loongson2_pll_recalc_ops = {
+ .recalc_rate = loongson2_pll_recalc_rate,
+};
+
+static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u64 val, scale;
+ u32 mode = 0;
+ struct loongson2_clk_data *clk = to_loongson2_clk(hw);
+
+ val = readq(clk->reg);
+ scale = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
+
+ if (clk->bit_idx)
+ mode = val & BIT(clk->bit_idx - 1);
+
+ return mode == 0 ? div_u64((u64)parent_rate * scale, 8) :
+ div_u64((u64)parent_rate, scale);
+}
+
+static const struct clk_ops loongson2_freqscale_recalc_ops = {
+ .recalc_rate = loongson2_freqscale_recalc_rate,
+};
+
+static struct clk_hw *loongson2_clk_register(const char *parent,
+ struct loongson2_clk_provider *clp,
+ const struct loongson2_clk_board_info *cld,
+ const struct clk_ops *ops)
+{
+ int ret;
+ struct clk_hw *hw;
+ struct loongson2_clk_data *clk;
+ struct clk_init_data init = { };
+
+ clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = cld->name;
+ init.ops = ops;
+ init.flags = 0;
+ init.num_parents = 1;
+ init.parent_names = &parent;
+
+ clk->reg = clp->base + cld->reg_offset;
+ clk->div_shift = cld->div_shift;
+ clk->div_width = cld->div_width;
+ clk->mult_shift = cld->mult_shift;
+ clk->mult_width = cld->mult_width;
+ clk->bit_idx = cld->bit_idx;
+ clk->hw.init = &init;
+
+ hw = &clk->hw;
+ ret = devm_clk_hw_register(clp->dev, hw);
+ if (ret)
+ clk = ERR_PTR(ret);
+
+ return hw;
+}
+
+static int loongson2_clk_probe(struct platform_device *pdev)
+{
+ int i, clks_num = 0;
+ struct clk_hw *hw;
+ struct device *dev = &pdev->dev;
+ struct loongson2_clk_provider *clp;
+ const struct loongson2_clk_board_info *p, *data;
+ const char *refclk_name, *parent_name;
+
+ data = device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ refclk_name = of_clk_get_parent_name(dev->of_node, 0);
+ if (IS_ERR(refclk_name))
+ return dev_err_probe(dev, PTR_ERR(refclk_name),
+ "failed to get refclk name\n");
+
+ for (p = data; p->name; p++)
+ clks_num = max(clks_num, p->id + 1);
+
+ clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
+ GFP_KERNEL);
+ if (!clp)
+ return -ENOMEM;
+
+ clp->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(clp->base))
+ return PTR_ERR(clp->base);
+
+ spin_lock_init(&clp->clk_lock);
+ clp->clk_data.num = clks_num;
+ clp->dev = dev;
+
+ /* Avoid returning NULL for unused id */
+ memset_p((void **)clp->clk_data.hws, ERR_PTR(-ENOENT), clks_num);
+
+ for (i = 0; i < clks_num; i++) {
+ p = &data[i];
+ parent_name = p->parent_name ? p->parent_name : refclk_name;
+
+ switch (p->type) {
+ case CLK_TYPE_PLL:
+ hw = loongson2_clk_register(parent_name, clp, p,
+ &loongson2_pll_recalc_ops);
+ break;
+ case CLK_TYPE_SCALE:
+ hw = loongson2_clk_register(parent_name, clp, p,
+ &loongson2_freqscale_recalc_ops);
+ break;
+ case CLK_TYPE_DIVIDER:
+ hw = devm_clk_hw_register_divider(dev, p->name,
+ parent_name, 0,
+ clp->base + p->reg_offset,
+ p->div_shift, p->div_width,
+ CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ALLOW_ZERO,
+ &clp->clk_lock);
+ break;
+ case CLK_TYPE_GATE:
+ hw = devm_clk_hw_register_gate(dev, p->name, parent_name,
+ p->flags,
+ clp->base + p->reg_offset,
+ p->bit_idx, 0,
+ &clp->clk_lock);
+ break;
+ case CLK_TYPE_FIXED:
+ hw = devm_clk_hw_register_fixed_rate(dev, p->name, parent_name,
+ 0, p->fixed_rate);
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
+ }
+
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw),
+ "Register clk: %s, type: %u failed!\n",
+ p->name, p->type);
+
+ clp->clk_data.hws[p->id] = hw;
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
+}
+
+static const struct of_device_id loongson2_clk_match_table[] = {
+ { .compatible = "loongson,ls2k0300-clk", .data = &ls2k0300_clks },
+ { .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },
+ { .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
+ { .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },
+ { }
+};
+MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
+
+static struct platform_driver loongson2_clk_driver = {
+ .probe = loongson2_clk_probe,
+ .driver = {
+ .name = "loongson2-clk",
+ .of_match_table = loongson2_clk_match_table,
+ },
+};
+module_platform_driver(loongson2_clk_driver);
+
+MODULE_DESCRIPTION("Loongson2 clock driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-max9485.c b/drivers/clk/clk-max9485.c
index 5f85b0a32872..0515e3e41162 100644
--- a/drivers/clk/clk-max9485.c
+++ b/drivers/clk/clk-max9485.c
@@ -159,29 +159,32 @@ static unsigned long max9485_clkout_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long max9485_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int max9485_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
const struct max9485_rate *curr, *prev = NULL;
for (curr = max9485_rates; curr->out != 0; curr++) {
/* Exact matches */
- if (curr->out == rate)
- return rate;
+ if (curr->out == req->rate)
+ return 0;
/*
* Find the first entry that has a frequency higher than the
* requested one.
*/
- if (curr->out > rate) {
+ if (curr->out > req->rate) {
unsigned int mid;
/*
* If this is the first entry, clamp the value to the
* lowest possible frequency.
*/
- if (!prev)
- return curr->out;
+ if (!prev) {
+ req->rate = curr->out;
+
+ return 0;
+ }
/*
* Otherwise, determine whether the previous entry or
@@ -189,14 +192,18 @@ static long max9485_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
*/
mid = prev->out + ((curr->out - prev->out) / 2);
- return (mid > rate) ? prev->out : curr->out;
+ req->rate = mid > req->rate ? prev->out : curr->out;
+
+ return 0;
}
prev = curr;
}
/* If the last entry was still too high, clamp the value */
- return prev->out;
+ req->rate = prev->out;
+
+ return 0;
}
struct max9485_clk {
@@ -221,7 +228,7 @@ static const struct max9485_clk max9485_clks[MAX9485_NUM_CLKS] = {
.parent_index = -1,
.ops = {
.set_rate = max9485_clkout_set_rate,
- .round_rate = max9485_clkout_round_rate,
+ .determine_rate = max9485_clkout_determine_rate,
.recalc_rate = max9485_clkout_recalc_rate,
},
},
@@ -376,7 +383,7 @@ static struct i2c_driver max9485_driver = {
.pm = &max9485_pm_ops,
.of_match_table = max9485_dt_ids,
},
- .probe_new = max9485_i2c_probe,
+ .probe = max9485_i2c_probe,
.id_table = max9485_i2c_ids,
};
module_i2c_driver(max9485_driver);
diff --git a/drivers/clk/clk-milbeaut.c b/drivers/clk/clk-milbeaut.c
index 80b9d78493bc..b4f9b7143eaa 100644
--- a/drivers/clk/clk-milbeaut.c
+++ b/drivers/clk/clk-milbeaut.c
@@ -386,8 +386,8 @@ static unsigned long m10v_clk_divider_recalc_rate(struct clk_hw *hw,
divider->flags, divider->width);
}
-static long m10v_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int m10v_clk_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct m10v_clk_divider *divider = to_m10v_div(hw);
@@ -398,13 +398,19 @@ static long m10v_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
val = readl(divider->reg) >> divider->shift;
val &= clk_div_mask(divider->width);
- return divider_ro_round_rate(hw, rate, prate, divider->table,
- divider->width, divider->flags,
- val);
+ req->rate = divider_ro_round_rate(hw, req->rate,
+ &req->best_parent_rate,
+ divider->table,
+ divider->width,
+ divider->flags, val);
+
+ return 0;
}
- return divider_round_rate(hw, rate, prate, divider->table,
- divider->width, divider->flags);
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate,
+ divider->table, divider->width, divider->flags);
+
+ return 0;
}
static int m10v_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -450,7 +456,7 @@ static int m10v_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops m10v_clk_divider_ops = {
.recalc_rate = m10v_clk_divider_recalc_rate,
- .round_rate = m10v_clk_divider_round_rate,
+ .determine_rate = m10v_clk_divider_determine_rate,
.set_rate = m10v_clk_divider_set_rate,
};
@@ -560,14 +566,12 @@ static void m10v_reg_mux_pre(const struct m10v_clk_mux_factors *factors,
static int m10v_clk_probe(struct platform_device *pdev)
{
int id;
- struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *base;
const char *parent_name;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -620,6 +624,7 @@ static void __init m10v_cc_init(struct device_node *np)
if (!m10v_clk_data)
return;
+ m10v_clk_data->num = M10V_NUM_CLKS;
base = of_iomap(np, 0);
if (!base) {
@@ -656,8 +661,6 @@ static void __init m10v_cc_init(struct device_node *np)
base + CLKSEL(1), 0, 3, 0, rclk_table,
&m10v_crglock, NULL);
m10v_clk_data->hws[M10V_RCLK_ID] = hw;
-
- m10v_clk_data->num = M10V_NUM_CLKS;
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, m10v_clk_data);
}
CLK_OF_DECLARE_DRIVER(m10v_cc, "socionext,milbeaut-m10v-ccu", m10v_cc_init);
diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c
index e507aa958da9..6f2955d408b6 100644
--- a/drivers/clk/clk-multiplier.c
+++ b/drivers/clk/clk-multiplier.c
@@ -112,14 +112,16 @@ static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
return bestmult;
}
-static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_multiplier_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_multiplier *mult = to_clk_multiplier(hw);
- unsigned long factor = __bestmult(hw, rate, parent_rate,
+ unsigned long factor = __bestmult(hw, req->rate, &req->best_parent_rate,
mult->width, mult->flags);
- return *parent_rate * factor;
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
}
static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -150,7 +152,7 @@ static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
const struct clk_ops clk_multiplier_ops = {
.recalc_rate = clk_multiplier_recalc_rate,
- .round_rate = clk_multiplier_round_rate,
+ .determine_rate = clk_multiplier_determine_rate,
.set_rate = clk_multiplier_set_rate,
};
EXPORT_SYMBOL_GPL(clk_multiplier_ops);
diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c
index bad2677e11ae..fc0aeb4247f2 100644
--- a/drivers/clk/clk-nomadik.c
+++ b/drivers/clk/clk-nomadik.c
@@ -17,6 +17,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
+#include <linux/string_choices.h>
#include <linux/reboot.h>
/*
@@ -99,7 +100,7 @@ static void __init nomadik_src_init(void)
if (!src_base) {
pr_err("%s: must have src parent node with REGS (%pOFn)\n",
__func__, np);
- return;
+ goto out_put;
}
/* Set all timers to use the 2.4 MHz TIMCLK */
@@ -116,9 +117,9 @@ static void __init nomadik_src_init(void)
val = readl(src_base + SRC_XTALCR);
pr_info("SXTALO is %s\n",
- (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled");
+ str_disabled_enabled(val & SRC_XTALCR_SXTALDIS));
pr_info("MXTAL is %s\n",
- (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled");
+ str_enabled_disabled(val & SRC_XTALCR_MXTALSTAT));
if (of_property_read_bool(np, "disable-sxtalo")) {
/* The machine uses an external oscillator circuit */
val |= SRC_XTALCR_SXTALDIS;
@@ -132,10 +133,13 @@ static void __init nomadik_src_init(void)
}
writel(val, src_base + SRC_XTALCR);
register_reboot_notifier(&nomadik_clk_reboot_notifier);
+
+out_put:
+ of_node_put(np);
}
/**
- * struct clk_pll1 - Nomadik PLL1 clock
+ * struct clk_pll - Nomadik PLL clock
* @hw: corresponding clock hardware entry
* @id: PLL instance: 1 or 2
*/
diff --git a/drivers/clk/clk-npcm7xx.c b/drivers/clk/clk-npcm7xx.c
index e677bb5a784b..030186def9c6 100644
--- a/drivers/clk/clk-npcm7xx.c
+++ b/drivers/clk/clk-npcm7xx.c
@@ -129,20 +129,6 @@ npcm7xx_clk_register_pll(void __iomem *pllcon, const char *name,
#define NPCM7XX_SECCNT (0x68)
#define NPCM7XX_CNTR25M (0x6C)
-struct npcm7xx_clk_gate_data {
- u32 reg;
- u8 bit_idx;
- const char *name;
- const char *parent_name;
- unsigned long flags;
- /*
- * If this clock is exported via DT, set onecell_idx to constant
- * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for
- * this specific clock. Otherwise, set to -1.
- */
- int onecell_idx;
-};
-
struct npcm7xx_clk_mux_data {
u8 shift;
u8 mask;
@@ -160,21 +146,6 @@ struct npcm7xx_clk_mux_data {
};
-struct npcm7xx_clk_div_fixed_data {
- u8 mult;
- u8 div;
- const char *name;
- const char *parent_name;
- u8 clk_divider_flags;
- /*
- * If this clock is exported via DT, set onecell_idx to constant
- * defined in include/dt-bindings/clock/nuvoton, NPCM7XX-clock.h for
- * this specific clock. Otherwise, set to -1.
- */
- int onecell_idx;
-};
-
-
struct npcm7xx_clk_div_data {
u32 reg;
u8 shift;
@@ -539,7 +510,7 @@ static void __init npcm7xx_clk_init(struct device_node *clk_np)
return;
npcm7xx_init_fail:
- kfree(npcm7xx_clk_data->hws);
+ kfree(npcm7xx_clk_data);
npcm7xx_init_np_err:
iounmap(clk_base);
npcm7xx_init_error:
diff --git a/drivers/clk/clk-npcm8xx.c b/drivers/clk/clk-npcm8xx.c
new file mode 100644
index 000000000000..2138c011411d
--- /dev/null
+++ b/drivers/clk/clk-npcm8xx.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM8xx Clock Generator
+ * All the clocks are initialized by the bootloader, so this driver allows only
+ * reading of current settings directly from the hardware.
+ *
+ * Copyright (C) 2020 Nuvoton Technologies
+ * Author: Tomer Maimon <tomer.maimon@nuvoton.com>
+ */
+
+#define pr_fmt(fmt) "npcm8xx_clk: " fmt
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/nuvoton,npcm845-clk.h>
+#include <soc/nuvoton/clock-npcm8xx.h>
+
+/* npcm8xx clock registers*/
+#define NPCM8XX_CLKSEL 0x04
+#define NPCM8XX_CLKDIV1 0x08
+#define NPCM8XX_CLKDIV2 0x2C
+#define NPCM8XX_CLKDIV3 0x58
+#define NPCM8XX_CLKDIV4 0x7C
+#define NPCM8XX_PLLCON0 0x0C
+#define NPCM8XX_PLLCON1 0x10
+#define NPCM8XX_PLLCON2 0x54
+#define NPCM8XX_PLLCONG 0x60
+#define NPCM8XX_THRTL_CNT 0xC0
+
+#define PLLCON_LOKI BIT(31)
+#define PLLCON_LOKS BIT(30)
+#define PLLCON_FBDV GENMASK(27, 16)
+#define PLLCON_OTDV2 GENMASK(15, 13)
+#define PLLCON_PWDEN BIT(12)
+#define PLLCON_OTDV1 GENMASK(10, 8)
+#define PLLCON_INDV GENMASK(5, 0)
+
+static void __iomem *clk_base;
+
+struct npcm8xx_clk_pll {
+ void __iomem *pllcon;
+ unsigned int id;
+ const char *name;
+ unsigned long flags;
+ struct clk_hw hw;
+};
+
+#define to_npcm8xx_clk_pll(_hw) container_of(_hw, struct npcm8xx_clk_pll, hw)
+
+struct npcm8xx_clk_pll_data {
+ const char *name;
+ struct clk_parent_data parent;
+ unsigned int reg;
+ unsigned long flags;
+ struct clk_hw hw;
+};
+
+struct npcm8xx_clk_div_data {
+ u32 reg;
+ u8 shift;
+ u8 width;
+ const char *name;
+ const struct clk_hw *parent_hw;
+ unsigned long clk_divider_flags;
+ unsigned long flags;
+ int onecell_idx;
+ struct clk_hw hw;
+};
+
+struct npcm8xx_clk_mux_data {
+ u8 shift;
+ u32 mask;
+ const u32 *table;
+ const char *name;
+ const struct clk_parent_data *parent_data;
+ u8 num_parents;
+ unsigned long flags;
+ struct clk_hw hw;
+};
+
+static struct clk_hw hw_pll1_div2, hw_pll2_div2, hw_gfx_div2, hw_pre_clk;
+static struct npcm8xx_clk_pll_data npcm8xx_pll_clks[] = {
+ { "pll0", { .index = 0 }, NPCM8XX_PLLCON0, 0 },
+ { "pll1", { .index = 0 }, NPCM8XX_PLLCON1, 0 },
+ { "pll2", { .index = 0 }, NPCM8XX_PLLCON2, 0 },
+ { "pll_gfx", { .index = 0 }, NPCM8XX_PLLCONG, 0 },
+};
+
+static const u32 cpuck_mux_table[] = { 0, 1, 2, 7 };
+static const struct clk_parent_data cpuck_mux_parents[] = {
+ { .hw = &npcm8xx_pll_clks[0].hw },
+ { .hw = &npcm8xx_pll_clks[1].hw },
+ { .index = 0 },
+ { .hw = &npcm8xx_pll_clks[2].hw }
+};
+
+static const u32 pixcksel_mux_table[] = { 0, 2 };
+static const struct clk_parent_data pixcksel_mux_parents[] = {
+ { .hw = &npcm8xx_pll_clks[3].hw },
+ { .index = 0 }
+};
+
+static const u32 default_mux_table[] = { 0, 1, 2, 3 };
+static const struct clk_parent_data default_mux_parents[] = {
+ { .hw = &npcm8xx_pll_clks[0].hw },
+ { .hw = &npcm8xx_pll_clks[1].hw },
+ { .index = 0 },
+ { .hw = &hw_pll2_div2 }
+};
+
+static const u32 sucksel_mux_table[] = { 2, 3 };
+static const struct clk_parent_data sucksel_mux_parents[] = {
+ { .index = 0 },
+ { .hw = &hw_pll2_div2 }
+};
+
+static const u32 mccksel_mux_table[] = { 0, 2 };
+static const struct clk_parent_data mccksel_mux_parents[] = {
+ { .hw = &hw_pll1_div2 },
+ { .index = 0 }
+};
+
+static const u32 clkoutsel_mux_table[] = { 0, 1, 2, 3, 4 };
+static const struct clk_parent_data clkoutsel_mux_parents[] = {
+ { .hw = &npcm8xx_pll_clks[0].hw },
+ { .hw = &npcm8xx_pll_clks[1].hw },
+ { .index = 0 },
+ { .hw = &hw_gfx_div2 },
+ { .hw = &hw_pll2_div2 }
+};
+
+static const u32 gfxmsel_mux_table[] = { 2, 3 };
+static const struct clk_parent_data gfxmsel_mux_parents[] = {
+ { .index = 0 },
+ { .hw = &npcm8xx_pll_clks[2].hw }
+};
+
+static const u32 dvcssel_mux_table[] = { 2, 3 };
+static const struct clk_parent_data dvcssel_mux_parents[] = {
+ { .index = 0 },
+ { .hw = &npcm8xx_pll_clks[2].hw }
+};
+
+static const u32 default3_mux_table[] = { 0, 1, 2 };
+static const struct clk_parent_data default3_mux_parents[] = {
+ { .hw = &npcm8xx_pll_clks[0].hw },
+ { .hw = &npcm8xx_pll_clks[1].hw },
+ { .index = 0 }
+};
+
+static struct npcm8xx_clk_mux_data npcm8xx_muxes[] = {
+ { 0, 3, cpuck_mux_table, "cpu_mux", cpuck_mux_parents,
+ ARRAY_SIZE(cpuck_mux_parents), CLK_IS_CRITICAL },
+ { 4, 2, pixcksel_mux_table, "gfx_pixel_mux", pixcksel_mux_parents,
+ ARRAY_SIZE(pixcksel_mux_parents), 0 },
+ { 6, 2, default_mux_table, "sd_mux", default_mux_parents,
+ ARRAY_SIZE(default_mux_parents), 0 },
+ { 8, 2, default_mux_table, "uart_mux", default_mux_parents,
+ ARRAY_SIZE(default_mux_parents), 0 },
+ { 10, 2, sucksel_mux_table, "serial_usb_mux", sucksel_mux_parents,
+ ARRAY_SIZE(sucksel_mux_parents), 0 },
+ { 12, 2, mccksel_mux_table, "mc_mux", mccksel_mux_parents,
+ ARRAY_SIZE(mccksel_mux_parents), 0 },
+ { 14, 2, default_mux_table, "adc_mux", default_mux_parents,
+ ARRAY_SIZE(default_mux_parents), 0 },
+ { 16, 2, default_mux_table, "gfx_mux", default_mux_parents,
+ ARRAY_SIZE(default_mux_parents), 0 },
+ { 18, 3, clkoutsel_mux_table, "clkout_mux", clkoutsel_mux_parents,
+ ARRAY_SIZE(clkoutsel_mux_parents), 0 },
+ { 21, 2, gfxmsel_mux_table, "gfxm_mux", gfxmsel_mux_parents,
+ ARRAY_SIZE(gfxmsel_mux_parents), 0 },
+ { 23, 2, dvcssel_mux_table, "dvc_mux", dvcssel_mux_parents,
+ ARRAY_SIZE(dvcssel_mux_parents), 0 },
+ { 25, 2, default3_mux_table, "rg_mux", default3_mux_parents,
+ ARRAY_SIZE(default3_mux_parents), 0 },
+ { 27, 2, default3_mux_table, "rcp_mux", default3_mux_parents,
+ ARRAY_SIZE(default3_mux_parents), 0 },
+};
+
+/* configurable pre dividers: */
+static struct npcm8xx_clk_div_data npcm8xx_pre_divs[] = {
+ { NPCM8XX_CLKDIV1, 21, 5, "pre_adc", &npcm8xx_muxes[6].hw, CLK_DIVIDER_READ_ONLY, 0, -1 },
+ { NPCM8XX_CLKDIV1, 26, 2, "ahb", &hw_pre_clk, CLK_DIVIDER_READ_ONLY, CLK_IS_CRITICAL, NPCM8XX_CLK_AHB },
+};
+
+/* configurable dividers: */
+static struct npcm8xx_clk_div_data npcm8xx_divs[] = {
+ { NPCM8XX_CLKDIV1, 28, 3, "adc", &npcm8xx_pre_divs[0].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_ADC },
+ { NPCM8XX_CLKDIV1, 16, 5, "uart", &npcm8xx_muxes[3].hw, 0, 0, NPCM8XX_CLK_UART },
+ { NPCM8XX_CLKDIV1, 11, 5, "mmc", &npcm8xx_muxes[2].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_MMC },
+ { NPCM8XX_CLKDIV1, 6, 5, "spi3", &npcm8xx_pre_divs[1].hw, 0, 0, NPCM8XX_CLK_SPI3 },
+ { NPCM8XX_CLKDIV1, 2, 4, "pci", &npcm8xx_muxes[7].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_PCI },
+
+ { NPCM8XX_CLKDIV2, 30, 2, "apb4", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_APB4 },
+ { NPCM8XX_CLKDIV2, 28, 2, "apb3", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_APB3 },
+ { NPCM8XX_CLKDIV2, 26, 2, "apb2", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_APB2 },
+ { NPCM8XX_CLKDIV2, 24, 2, "apb1", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_APB1 },
+ { NPCM8XX_CLKDIV2, 22, 2, "apb5", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_APB5 },
+ { NPCM8XX_CLKDIV2, 16, 5, "clkout", &npcm8xx_muxes[8].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_CLKOUT },
+ { NPCM8XX_CLKDIV2, 13, 3, "gfx", &npcm8xx_muxes[7].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_GFX },
+ { NPCM8XX_CLKDIV2, 8, 5, "usb_bridge", &npcm8xx_muxes[4].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SU },
+ { NPCM8XX_CLKDIV2, 4, 4, "usb_host", &npcm8xx_muxes[4].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SU48 },
+ { NPCM8XX_CLKDIV2, 0, 4, "sdhc", &npcm8xx_muxes[2].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SDHC },
+
+ { NPCM8XX_CLKDIV3, 16, 8, "spi1", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI1 },
+ { NPCM8XX_CLKDIV3, 11, 5, "uart2", &npcm8xx_muxes[3].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_UART2 },
+ { NPCM8XX_CLKDIV3, 6, 5, "spi0", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI0 },
+ { NPCM8XX_CLKDIV3, 1, 5, "spix", &npcm8xx_pre_divs[1].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPIX },
+
+ { NPCM8XX_CLKDIV4, 28, 4, "rg", &npcm8xx_muxes[11].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RG },
+ { NPCM8XX_CLKDIV4, 12, 4, "rcp", &npcm8xx_muxes[12].hw, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RCP },
+
+ { NPCM8XX_THRTL_CNT, 0, 2, "th", &npcm8xx_muxes[0].hw, CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_TH },
+};
+
+static unsigned long npcm8xx_clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct npcm8xx_clk_pll *pll = to_npcm8xx_clk_pll(hw);
+ unsigned long fbdv, indv, otdv1, otdv2;
+ unsigned int val;
+ u64 ret;
+
+ if (parent_rate == 0) {
+ pr_debug("%s: parent rate is zero\n", __func__);
+ return 0;
+ }
+
+ val = readl_relaxed(pll->pllcon);
+
+ indv = FIELD_GET(PLLCON_INDV, val);
+ fbdv = FIELD_GET(PLLCON_FBDV, val);
+ otdv1 = FIELD_GET(PLLCON_OTDV1, val);
+ otdv2 = FIELD_GET(PLLCON_OTDV2, val);
+
+ ret = (u64)parent_rate * fbdv;
+ do_div(ret, indv * otdv1 * otdv2);
+
+ return ret;
+}
+
+static const struct clk_ops npcm8xx_clk_pll_ops = {
+ .recalc_rate = npcm8xx_clk_pll_recalc_rate,
+};
+
+static struct clk_hw *
+npcm8xx_clk_register_pll(struct device *dev, void __iomem *pllcon,
+ const char *name, const struct clk_parent_data *parent,
+ unsigned long flags)
+{
+ struct npcm8xx_clk_pll *pll;
+ struct clk_init_data init = {};
+ int ret;
+
+ pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &npcm8xx_clk_pll_ops;
+ init.parent_data = parent;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ pll->pllcon = pllcon;
+ pll->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &pll->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &pll->hw;
+}
+
+static DEFINE_SPINLOCK(npcm8xx_clk_lock);
+
+static int npcm8xx_clk_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct npcm_clock_adev *rdev = to_npcm_clock_adev(adev);
+ struct clk_hw_onecell_data *npcm8xx_clk_data;
+ struct device *dev = &adev->dev;
+ struct clk_hw *hw;
+ unsigned int i;
+
+ npcm8xx_clk_data = devm_kzalloc(dev, struct_size(npcm8xx_clk_data, hws,
+ NPCM8XX_NUM_CLOCKS),
+ GFP_KERNEL);
+ if (!npcm8xx_clk_data)
+ return -ENOMEM;
+
+ clk_base = rdev->base;
+
+ npcm8xx_clk_data->num = NPCM8XX_NUM_CLOCKS;
+
+ for (i = 0; i < NPCM8XX_NUM_CLOCKS; i++)
+ npcm8xx_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
+
+ /* Register plls */
+ for (i = 0; i < ARRAY_SIZE(npcm8xx_pll_clks); i++) {
+ struct npcm8xx_clk_pll_data *pll_clk = &npcm8xx_pll_clks[i];
+
+ hw = npcm8xx_clk_register_pll(dev, clk_base + pll_clk->reg,
+ pll_clk->name, &pll_clk->parent,
+ pll_clk->flags);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register pll\n");
+ pll_clk->hw = *hw;
+ }
+
+ /* Register fixed dividers */
+ hw = devm_clk_hw_register_fixed_factor(dev, "pll1_div2", "pll1", 0, 1, 2);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register fixed div\n");
+ hw_pll1_div2 = *hw;
+
+ hw = devm_clk_hw_register_fixed_factor(dev, "pll2_div2", "pll2", 0, 1, 2);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register pll2 div2\n");
+ hw_pll2_div2 = *hw;
+
+ hw = devm_clk_hw_register_fixed_factor(dev, "pll_gfx_div2", "pll_gfx", 0, 1, 2);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register gfx div2\n");
+ hw_gfx_div2 = *hw;
+
+ /* Register muxes */
+ for (i = 0; i < ARRAY_SIZE(npcm8xx_muxes); i++) {
+ struct npcm8xx_clk_mux_data *mux_data = &npcm8xx_muxes[i];
+
+ hw = devm_clk_hw_register_mux_parent_data_table(dev,
+ mux_data->name,
+ mux_data->parent_data,
+ mux_data->num_parents,
+ mux_data->flags,
+ clk_base + NPCM8XX_CLKSEL,
+ mux_data->shift,
+ mux_data->mask,
+ 0,
+ mux_data->table,
+ &npcm8xx_clk_lock);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register mux\n");
+ mux_data->hw = *hw;
+ }
+
+ hw = devm_clk_hw_register_fixed_factor(dev, "pre_clk", "cpu_mux", 0, 1, 2);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register pre clk div2\n");
+ hw_pre_clk = *hw;
+
+ hw = devm_clk_hw_register_fixed_factor(dev, "axi", "th", 0, 1, 2);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register axi div2\n");
+ npcm8xx_clk_data->hws[NPCM8XX_CLK_AXI] = hw;
+
+ hw = devm_clk_hw_register_fixed_factor(dev, "atb", "axi", 0, 1, 2);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register atb div2\n");
+ npcm8xx_clk_data->hws[NPCM8XX_CLK_ATB] = hw;
+
+ /* Register pre dividers */
+ for (i = 0; i < ARRAY_SIZE(npcm8xx_pre_divs); i++) {
+ struct npcm8xx_clk_div_data *div_data = &npcm8xx_pre_divs[i];
+
+ hw = devm_clk_hw_register_divider_parent_hw(dev, div_data->name,
+ div_data->parent_hw,
+ div_data->flags,
+ clk_base + div_data->reg,
+ div_data->shift,
+ div_data->width,
+ div_data->clk_divider_flags,
+ &npcm8xx_clk_lock);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register pre div\n");
+ div_data->hw = *hw;
+
+ if (div_data->onecell_idx >= 0)
+ npcm8xx_clk_data->hws[div_data->onecell_idx] = hw;
+ }
+
+ /* Register dividers */
+ for (i = 0; i < ARRAY_SIZE(npcm8xx_divs); i++) {
+ struct npcm8xx_clk_div_data *div_data = &npcm8xx_divs[i];
+
+ hw = devm_clk_hw_register_divider_parent_hw(dev, div_data->name,
+ div_data->parent_hw,
+ div_data->flags,
+ clk_base + div_data->reg,
+ div_data->shift,
+ div_data->width,
+ div_data->clk_divider_flags,
+ &npcm8xx_clk_lock);
+ if (IS_ERR(hw))
+ return dev_err_probe(dev, PTR_ERR(hw), "Can't register div\n");
+
+ if (div_data->onecell_idx >= 0)
+ npcm8xx_clk_data->hws[div_data->onecell_idx] = hw;
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ npcm8xx_clk_data);
+}
+
+static const struct auxiliary_device_id npcm8xx_clock_ids[] = {
+ {
+ .name = "reset_npcm.clk-npcm8xx",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, npcm8xx_clock_ids);
+
+static struct auxiliary_driver npcm8xx_clock_driver = {
+ .probe = npcm8xx_clk_probe,
+ .id_table = npcm8xx_clock_ids,
+};
+module_auxiliary_driver(npcm8xx_clock_driver);
+
+MODULE_DESCRIPTION("Clock driver for Nuvoton NPCM8XX BMC SoC");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c
deleted file mode 100644
index cda5e258355b..000000000000
--- a/drivers/clk/clk-oxnas.c
+++ /dev/null
@@ -1,249 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2010 Broadcom
- * Copyright (C) 2012 Stephen Warren
- * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/stringify.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-
-#include <dt-bindings/clock/oxsemi,ox810se.h>
-#include <dt-bindings/clock/oxsemi,ox820.h>
-
-/* Standard regmap gate clocks */
-struct clk_oxnas_gate {
- struct clk_hw hw;
- unsigned int bit;
- struct regmap *regmap;
-};
-
-struct oxnas_stdclk_data {
- struct clk_hw_onecell_data *onecell_data;
- struct clk_oxnas_gate **gates;
- unsigned int ngates;
- struct clk_oxnas_pll **plls;
- unsigned int nplls;
-};
-
-/* Regmap offsets */
-#define CLK_STAT_REGOFFSET 0x24
-#define CLK_SET_REGOFFSET 0x2c
-#define CLK_CLR_REGOFFSET 0x30
-
-static inline struct clk_oxnas_gate *to_clk_oxnas_gate(struct clk_hw *hw)
-{
- return container_of(hw, struct clk_oxnas_gate, hw);
-}
-
-static int oxnas_clk_gate_is_enabled(struct clk_hw *hw)
-{
- struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw);
- int ret;
- unsigned int val;
-
- ret = regmap_read(std->regmap, CLK_STAT_REGOFFSET, &val);
- if (ret < 0)
- return ret;
-
- return val & BIT(std->bit);
-}
-
-static int oxnas_clk_gate_enable(struct clk_hw *hw)
-{
- struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw);
-
- regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit));
-
- return 0;
-}
-
-static void oxnas_clk_gate_disable(struct clk_hw *hw)
-{
- struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw);
-
- regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit));
-}
-
-static const struct clk_ops oxnas_clk_gate_ops = {
- .enable = oxnas_clk_gate_enable,
- .disable = oxnas_clk_gate_disable,
- .is_enabled = oxnas_clk_gate_is_enabled,
-};
-
-static const char *const osc_parents[] = {
- "oscillator",
-};
-
-static const char *const eth_parents[] = {
- "gmacclk",
-};
-
-#define OXNAS_GATE(_name, _bit, _parents) \
-struct clk_oxnas_gate _name = { \
- .bit = (_bit), \
- .hw.init = &(struct clk_init_data) { \
- .name = #_name, \
- .ops = &oxnas_clk_gate_ops, \
- .parent_names = _parents, \
- .num_parents = ARRAY_SIZE(_parents), \
- .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
- }, \
-}
-
-static OXNAS_GATE(ox810se_leon, 0, osc_parents);
-static OXNAS_GATE(ox810se_dma_sgdma, 1, osc_parents);
-static OXNAS_GATE(ox810se_cipher, 2, osc_parents);
-static OXNAS_GATE(ox810se_sata, 4, osc_parents);
-static OXNAS_GATE(ox810se_audio, 5, osc_parents);
-static OXNAS_GATE(ox810se_usbmph, 6, osc_parents);
-static OXNAS_GATE(ox810se_etha, 7, eth_parents);
-static OXNAS_GATE(ox810se_pciea, 8, osc_parents);
-static OXNAS_GATE(ox810se_nand, 9, osc_parents);
-
-static struct clk_oxnas_gate *ox810se_gates[] = {
- &ox810se_leon,
- &ox810se_dma_sgdma,
- &ox810se_cipher,
- &ox810se_sata,
- &ox810se_audio,
- &ox810se_usbmph,
- &ox810se_etha,
- &ox810se_pciea,
- &ox810se_nand,
-};
-
-static OXNAS_GATE(ox820_leon, 0, osc_parents);
-static OXNAS_GATE(ox820_dma_sgdma, 1, osc_parents);
-static OXNAS_GATE(ox820_cipher, 2, osc_parents);
-static OXNAS_GATE(ox820_sd, 3, osc_parents);
-static OXNAS_GATE(ox820_sata, 4, osc_parents);
-static OXNAS_GATE(ox820_audio, 5, osc_parents);
-static OXNAS_GATE(ox820_usbmph, 6, osc_parents);
-static OXNAS_GATE(ox820_etha, 7, eth_parents);
-static OXNAS_GATE(ox820_pciea, 8, osc_parents);
-static OXNAS_GATE(ox820_nand, 9, osc_parents);
-static OXNAS_GATE(ox820_ethb, 10, eth_parents);
-static OXNAS_GATE(ox820_pcieb, 11, osc_parents);
-static OXNAS_GATE(ox820_ref600, 12, osc_parents);
-static OXNAS_GATE(ox820_usbdev, 13, osc_parents);
-
-static struct clk_oxnas_gate *ox820_gates[] = {
- &ox820_leon,
- &ox820_dma_sgdma,
- &ox820_cipher,
- &ox820_sd,
- &ox820_sata,
- &ox820_audio,
- &ox820_usbmph,
- &ox820_etha,
- &ox820_pciea,
- &ox820_nand,
- &ox820_etha,
- &ox820_pciea,
- &ox820_ref600,
- &ox820_usbdev,
-};
-
-static struct clk_hw_onecell_data ox810se_hw_onecell_data = {
- .hws = {
- [CLK_810_LEON] = &ox810se_leon.hw,
- [CLK_810_DMA_SGDMA] = &ox810se_dma_sgdma.hw,
- [CLK_810_CIPHER] = &ox810se_cipher.hw,
- [CLK_810_SATA] = &ox810se_sata.hw,
- [CLK_810_AUDIO] = &ox810se_audio.hw,
- [CLK_810_USBMPH] = &ox810se_usbmph.hw,
- [CLK_810_ETHA] = &ox810se_etha.hw,
- [CLK_810_PCIEA] = &ox810se_pciea.hw,
- [CLK_810_NAND] = &ox810se_nand.hw,
- },
- .num = ARRAY_SIZE(ox810se_gates),
-};
-
-static struct clk_hw_onecell_data ox820_hw_onecell_data = {
- .hws = {
- [CLK_820_LEON] = &ox820_leon.hw,
- [CLK_820_DMA_SGDMA] = &ox820_dma_sgdma.hw,
- [CLK_820_CIPHER] = &ox820_cipher.hw,
- [CLK_820_SD] = &ox820_sd.hw,
- [CLK_820_SATA] = &ox820_sata.hw,
- [CLK_820_AUDIO] = &ox820_audio.hw,
- [CLK_820_USBMPH] = &ox820_usbmph.hw,
- [CLK_820_ETHA] = &ox820_etha.hw,
- [CLK_820_PCIEA] = &ox820_pciea.hw,
- [CLK_820_NAND] = &ox820_nand.hw,
- [CLK_820_ETHB] = &ox820_ethb.hw,
- [CLK_820_PCIEB] = &ox820_pcieb.hw,
- [CLK_820_REF600] = &ox820_ref600.hw,
- [CLK_820_USBDEV] = &ox820_usbdev.hw,
- },
- .num = ARRAY_SIZE(ox820_gates),
-};
-
-static struct oxnas_stdclk_data ox810se_stdclk_data = {
- .onecell_data = &ox810se_hw_onecell_data,
- .gates = ox810se_gates,
- .ngates = ARRAY_SIZE(ox810se_gates),
-};
-
-static struct oxnas_stdclk_data ox820_stdclk_data = {
- .onecell_data = &ox820_hw_onecell_data,
- .gates = ox820_gates,
- .ngates = ARRAY_SIZE(ox820_gates),
-};
-
-static const struct of_device_id oxnas_stdclk_dt_ids[] = {
- { .compatible = "oxsemi,ox810se-stdclk", &ox810se_stdclk_data },
- { .compatible = "oxsemi,ox820-stdclk", &ox820_stdclk_data },
- { }
-};
-
-static int oxnas_stdclk_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- const struct oxnas_stdclk_data *data;
- struct regmap *regmap;
- int ret;
- int i;
-
- data = of_device_get_match_data(&pdev->dev);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap)) {
- dev_err(&pdev->dev, "failed to have parent regmap\n");
- return PTR_ERR(regmap);
- }
-
- for (i = 0 ; i < data->ngates ; ++i)
- data->gates[i]->regmap = regmap;
-
- for (i = 0; i < data->onecell_data->num; i++) {
- if (!data->onecell_data->hws[i])
- continue;
-
- ret = devm_clk_hw_register(&pdev->dev,
- data->onecell_data->hws[i]);
- if (ret)
- return ret;
- }
-
- return of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
- data->onecell_data);
-}
-
-static struct platform_driver oxnas_stdclk_driver = {
- .probe = oxnas_stdclk_probe,
- .driver = {
- .name = "oxnas-stdclk",
- .suppress_bind_attrs = true,
- .of_match_table = oxnas_stdclk_dt_ids,
- },
-};
-builtin_platform_driver(oxnas_stdclk_driver);
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
index b8c3d0da1918..39049f62dbbb 100644
--- a/drivers/clk/clk-palmas.c
+++ b/drivers/clk/clk-palmas.c
@@ -14,7 +14,6 @@
#include <linux/mfd/palmas.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -271,10 +270,9 @@ static int palmas_clks_probe(struct platform_device *pdev)
return ret;
}
-static int palmas_clks_remove(struct platform_device *pdev)
+static void palmas_clks_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
- return 0;
}
static struct platform_driver palmas_clks_driver = {
diff --git a/drivers/clk/clk-plldig.c b/drivers/clk/clk-plldig.c
index 25020164b89e..14357f3e4561 100644
--- a/drivers/clk/clk-plldig.c
+++ b/drivers/clk/clk-plldig.c
@@ -12,8 +12,6 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/bitfield.h>
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c
index da2c8eddfd9f..4709f0338e37 100644
--- a/drivers/clk/clk-pwm.c
+++ b/drivers/clk/clk-pwm.c
@@ -14,6 +14,7 @@
struct clk_pwm {
struct clk_hw hw;
struct pwm_device *pwm;
+ struct pwm_state state;
u32 fixed_rate;
};
@@ -22,11 +23,28 @@ static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw)
return container_of(hw, struct clk_pwm, hw);
}
+static int clk_pwm_enable(struct clk_hw *hw)
+{
+ struct clk_pwm *clk_pwm = to_clk_pwm(hw);
+
+ return pwm_apply_atomic(clk_pwm->pwm, &clk_pwm->state);
+}
+
+static void clk_pwm_disable(struct clk_hw *hw)
+{
+ struct clk_pwm *clk_pwm = to_clk_pwm(hw);
+ struct pwm_state state = clk_pwm->state;
+
+ state.enabled = false;
+
+ pwm_apply_atomic(clk_pwm->pwm, &state);
+}
+
static int clk_pwm_prepare(struct clk_hw *hw)
{
struct clk_pwm *clk_pwm = to_clk_pwm(hw);
- return pwm_enable(clk_pwm->pwm);
+ return pwm_apply_might_sleep(clk_pwm->pwm, &clk_pwm->state);
}
static void clk_pwm_unprepare(struct clk_hw *hw)
@@ -48,8 +66,11 @@ static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
{
struct clk_pwm *clk_pwm = to_clk_pwm(hw);
struct pwm_state state;
+ int ret;
- pwm_get_state(clk_pwm->pwm, &state);
+ ret = pwm_get_state_hw(clk_pwm->pwm, &state);
+ if (ret)
+ return ret;
duty->num = state.duty_cycle;
duty->den = state.period;
@@ -57,6 +78,13 @@ static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
return 0;
}
+static const struct clk_ops clk_pwm_ops_atomic = {
+ .enable = clk_pwm_enable,
+ .disable = clk_pwm_disable,
+ .recalc_rate = clk_pwm_recalc_rate,
+ .get_duty_cycle = clk_pwm_get_duty_cycle,
+};
+
static const struct clk_ops clk_pwm_ops = {
.prepare = clk_pwm_prepare,
.unprepare = clk_pwm_unprepare,
@@ -103,20 +131,19 @@ static int clk_pwm_probe(struct platform_device *pdev)
return -EINVAL;
}
- /*
- * FIXME: pwm_apply_args() should be removed when switching to the
- * atomic PWM API.
- */
- pwm_apply_args(pwm);
- ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period);
- if (ret < 0)
- return ret;
+ pwm_init_state(pwm, &clk_pwm->state);
+ pwm_set_relative_duty_cycle(&clk_pwm->state, 1, 2);
+ clk_pwm->state.enabled = true;
clk_name = node->name;
of_property_read_string(node, "clock-output-names", &clk_name);
init.name = clk_name;
- init.ops = &clk_pwm_ops;
+ if (pwm_might_sleep(pwm))
+ init.ops = &clk_pwm_ops;
+ else
+ init.ops = &clk_pwm_ops_atomic;
+
init.flags = 0;
init.num_parents = 0;
@@ -129,11 +156,9 @@ static int clk_pwm_probe(struct platform_device *pdev)
return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clk_pwm->hw);
}
-static int clk_pwm_remove(struct platform_device *pdev)
+static void clk_pwm_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
-
- return 0;
}
static const struct of_device_id clk_pwm_dt_ids[] = {
diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c
index 88898b97a443..a560edeb4b55 100644
--- a/drivers/clk/clk-qoriq.c
+++ b/drivers/clk/clk-qoriq.c
@@ -9,6 +9,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <dt-bindings/clock/fsl,qoriq-clockgen.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
@@ -17,8 +18,8 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#define PLL_DIV1 0
@@ -878,6 +879,7 @@ static u8 mux_get_parent(struct clk_hw *hw)
}
static const struct clk_ops cmux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = mux_get_parent,
.set_parent = mux_set_parent,
};
@@ -1063,8 +1065,10 @@ static void __init _clockgen_init(struct device_node *np, bool legacy);
*/
static void __init legacy_init_clockgen(struct device_node *np)
{
- if (!clockgen.node)
- _clockgen_init(of_get_parent(np), true);
+ if (!clockgen.node) {
+ struct device_node *parent_np __free(device_node) = of_get_parent(np);
+ _clockgen_init(parent_np, true);
+ }
}
/* Legacy node */
@@ -1159,6 +1163,7 @@ static struct clk * __init create_sysclk(const char *name)
sysclk = of_get_child_by_name(clockgen.node, "sysclk");
if (sysclk) {
clk = sysclk_from_fixed(sysclk, name);
+ of_node_put(sysclk);
if (!IS_ERR(clk))
return clk;
}
diff --git a/drivers/clk/clk-renesas-pcie.c b/drivers/clk/clk-renesas-pcie.c
index 4f5df1fc74b4..4c3a5e4eb77a 100644
--- a/drivers/clk/clk-renesas-pcie.c
+++ b/drivers/clk/clk-renesas-pcie.c
@@ -6,6 +6,8 @@
* - 9FGV/9DBV/9DMV/9FGL/9DML/9QXL/9SQ
* Currently supported:
* - 9FGV0241
+ * - 9FGV0441
+ * - 9FGV0841
*
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
*/
@@ -18,22 +20,20 @@
#include <linux/regmap.h>
#define RS9_REG_OE 0x0
-#define RS9_REG_OE_DIF_OE(n) BIT((n) + 1)
#define RS9_REG_SS 0x1
#define RS9_REG_SS_AMP_0V6 0x0
#define RS9_REG_SS_AMP_0V7 0x1
#define RS9_REG_SS_AMP_0V8 0x2
#define RS9_REG_SS_AMP_0V9 0x3
+#define RS9_REG_SS_AMP_DEFAULT RS9_REG_SS_AMP_0V8
#define RS9_REG_SS_AMP_MASK 0x3
#define RS9_REG_SS_SSC_100 0
#define RS9_REG_SS_SSC_M025 (1 << 3)
#define RS9_REG_SS_SSC_M050 (3 << 3)
+#define RS9_REG_SS_SSC_DEFAULT RS9_REG_SS_SSC_100
#define RS9_REG_SS_SSC_MASK (3 << 3)
#define RS9_REG_SS_SSC_LOCK BIT(5)
#define RS9_REG_SR 0x2
-#define RS9_REG_SR_2V0_DIF(n) 0
-#define RS9_REG_SR_3V0_DIF(n) BIT((n) + 1)
-#define RS9_REG_SR_DIF_MASK(n) BIT((n) + 1)
#define RS9_REG_REF 0x3
#define RS9_REG_REF_OE BIT(4)
#define RS9_REG_REF_OD BIT(5)
@@ -45,23 +45,26 @@
#define RS9_REG_DID 0x6
#define RS9_REG_BCP 0x7
-/* Supported Renesas 9-series models. */
-enum rs9_model {
- RENESAS_9FGV0241,
-};
+#define RS9_REG_VID_MASK GENMASK(3, 0)
+#define RS9_REG_VID_IDT 0x01
+
+#define RS9_REG_DID_TYPE_FGV (0x0 << RS9_REG_DID_TYPE_SHIFT)
+#define RS9_REG_DID_TYPE_DBV (0x1 << RS9_REG_DID_TYPE_SHIFT)
+#define RS9_REG_DID_TYPE_DMV (0x2 << RS9_REG_DID_TYPE_SHIFT)
+#define RS9_REG_DID_TYPE_SHIFT 0x6
/* Structure to describe features of a particular 9-series model */
struct rs9_chip_info {
- const enum rs9_model model;
unsigned int num_clks;
+ u8 outshift;
+ u8 did;
};
struct rs9_driver_data {
struct i2c_client *client;
struct regmap *regmap;
const struct rs9_chip_info *chip_info;
- struct clk *pin_xin;
- struct clk_hw *clk_dif[2];
+ struct clk_hw *clk_dif[4];
u8 pll_amplitude;
u8 pll_ssc;
u8 clk_dif_sr;
@@ -90,26 +93,90 @@ static const struct regmap_access_table rs9_writeable_table = {
.n_yes_ranges = ARRAY_SIZE(rs9_writeable_ranges),
};
+static int rs9_regmap_i2c_write(void *context,
+ unsigned int reg, unsigned int val)
+{
+ struct i2c_client *i2c = context;
+ const u8 data[3] = { reg, 1, val };
+ const int count = ARRAY_SIZE(data);
+ int ret;
+
+ ret = i2c_master_send(i2c, data, count);
+ if (ret == count)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int rs9_regmap_i2c_read(void *context,
+ unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *i2c = context;
+ struct i2c_msg xfer[2];
+ u8 txdata = reg;
+ u8 rxdata[2];
+ int ret;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = (void *)&txdata;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 2;
+ xfer[1].buf = (void *)rxdata;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ /*
+ * Byte 0 is transfer length, which is always 1 due
+ * to BCP register programming to 1 in rs9_probe(),
+ * ignore it and use data from Byte 1.
+ */
+ *val = rxdata[1];
+ return 0;
+}
+
static const struct regmap_config rs9_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_FLAT,
- .max_register = 0x8,
+ .max_register = RS9_REG_BCP,
+ .num_reg_defaults_raw = 0x8,
.rd_table = &rs9_readable_table,
.wr_table = &rs9_writeable_table,
+ .reg_write = rs9_regmap_i2c_write,
+ .reg_read = rs9_regmap_i2c_read,
};
+static u8 rs9_calc_dif(const struct rs9_driver_data *rs9, int idx)
+{
+ /*
+ * On 9FGV0241, the DIF OE0 is BIT(1) and DIF OE(1) is BIT(2),
+ * on 9FGV0441 and 9FGV0841 the DIF OE0 is BIT(0) and so on.
+ * Increment the index in the 9FGV0241 special case here.
+ */
+ return BIT(idx + rs9->chip_info->outshift);
+}
+
static int rs9_get_output_config(struct rs9_driver_data *rs9, int idx)
{
struct i2c_client *client = rs9->client;
+ u8 dif = rs9_calc_dif(rs9, idx);
unsigned char name[5] = "DIF0";
struct device_node *np;
int ret;
u32 sr;
/* Set defaults */
- rs9->clk_dif_sr &= ~RS9_REG_SR_DIF_MASK(idx);
- rs9->clk_dif_sr |= RS9_REG_SR_3V0_DIF(idx);
+ rs9->clk_dif_sr |= dif;
snprintf(name, 5, "DIF%d", idx);
np = of_get_child_by_name(client->dev.of_node, name);
@@ -121,11 +188,9 @@ static int rs9_get_output_config(struct rs9_driver_data *rs9, int idx)
of_node_put(np);
if (!ret) {
if (sr == 2000000) { /* 2V/ns */
- rs9->clk_dif_sr &= ~RS9_REG_SR_DIF_MASK(idx);
- rs9->clk_dif_sr |= RS9_REG_SR_2V0_DIF(idx);
+ rs9->clk_dif_sr &= ~dif;
} else if (sr == 3000000) { /* 3V/ns (default) */
- rs9->clk_dif_sr &= ~RS9_REG_SR_DIF_MASK(idx);
- rs9->clk_dif_sr |= RS9_REG_SR_3V0_DIF(idx);
+ rs9->clk_dif_sr |= dif;
} else
ret = dev_err_probe(&client->dev, -EINVAL,
"Invalid renesas,slew-rate value\n");
@@ -142,8 +207,8 @@ static int rs9_get_common_config(struct rs9_driver_data *rs9)
int ret;
/* Set defaults */
- rs9->pll_amplitude = RS9_REG_SS_AMP_0V7;
- rs9->pll_ssc = RS9_REG_SS_SSC_100;
+ rs9->pll_amplitude = RS9_REG_SS_AMP_DEFAULT;
+ rs9->pll_ssc = RS9_REG_SS_SSC_DEFAULT;
/* Output clock amplitude */
ret = of_property_read_u32(np, "renesas,out-amplitude-microvolt",
@@ -184,23 +249,25 @@ static void rs9_update_config(struct rs9_driver_data *rs9)
int i;
/* If amplitude is non-default, update it. */
- if (rs9->pll_amplitude != RS9_REG_SS_AMP_0V7) {
+ if (rs9->pll_amplitude != RS9_REG_SS_AMP_DEFAULT) {
regmap_update_bits(rs9->regmap, RS9_REG_SS, RS9_REG_SS_AMP_MASK,
rs9->pll_amplitude);
}
/* If SSC is non-default, update it. */
- if (rs9->pll_ssc != RS9_REG_SS_SSC_100) {
+ if (rs9->pll_ssc != RS9_REG_SS_SSC_DEFAULT) {
regmap_update_bits(rs9->regmap, RS9_REG_SS, RS9_REG_SS_SSC_MASK,
rs9->pll_ssc);
}
for (i = 0; i < rs9->chip_info->num_clks; i++) {
- if (rs9->clk_dif_sr & RS9_REG_SR_3V0_DIF(i))
+ u8 dif = rs9_calc_dif(rs9, i);
+
+ if (rs9->clk_dif_sr & dif)
continue;
- regmap_update_bits(rs9->regmap, RS9_REG_SR, RS9_REG_SR_3V0_DIF(i),
- rs9->clk_dif_sr & RS9_REG_SR_3V0_DIF(i));
+ regmap_update_bits(rs9->regmap, RS9_REG_SR, dif,
+ rs9->clk_dif_sr & dif);
}
}
@@ -217,6 +284,7 @@ static int rs9_probe(struct i2c_client *client)
{
unsigned char name[5] = "DIF0";
struct rs9_driver_data *rs9;
+ unsigned int vid, did;
struct clk_hw *hw;
int i, ret;
@@ -226,7 +294,7 @@ static int rs9_probe(struct i2c_client *client)
i2c_set_clientdata(client, rs9);
rs9->client = client;
- rs9->chip_info = device_get_match_data(&client->dev);
+ rs9->chip_info = i2c_get_match_data(client);
if (!rs9->chip_info)
return -EINVAL;
@@ -242,11 +310,32 @@ static int rs9_probe(struct i2c_client *client)
return ret;
}
- rs9->regmap = devm_regmap_init_i2c(client, &rs9_regmap_config);
+ rs9->regmap = devm_regmap_init(&client->dev, NULL,
+ client, &rs9_regmap_config);
if (IS_ERR(rs9->regmap))
return dev_err_probe(&client->dev, PTR_ERR(rs9->regmap),
"Failed to allocate register map\n");
+ /* Always read back 1 Byte via I2C */
+ ret = regmap_write(rs9->regmap, RS9_REG_BCP, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(rs9->regmap, RS9_REG_VID, &vid);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(rs9->regmap, RS9_REG_DID, &did);
+ if (ret < 0)
+ return ret;
+
+ vid &= RS9_REG_VID_MASK;
+ if (vid != RS9_REG_VID_IDT || did != rs9->chip_info->did)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Incorrect VID/DID: %#02x, %#02x. Expected %#02x, %#02x\n",
+ vid, did, RS9_REG_VID_IDT,
+ rs9->chip_info->did);
+
/* Register clock */
for (i = 0; i < rs9->chip_info->num_clks; i++) {
snprintf(name, 5, "DIF%d", i);
@@ -288,18 +377,35 @@ static int __maybe_unused rs9_resume(struct device *dev)
}
static const struct rs9_chip_info renesas_9fgv0241_info = {
- .model = RENESAS_9FGV0241,
.num_clks = 2,
+ .outshift = 1,
+ .did = RS9_REG_DID_TYPE_FGV | 0x02,
+};
+
+static const struct rs9_chip_info renesas_9fgv0441_info = {
+ .num_clks = 4,
+ .outshift = 0,
+ .did = RS9_REG_DID_TYPE_FGV | 0x04,
+};
+
+static const struct rs9_chip_info renesas_9fgv0841_info = {
+ .num_clks = 8,
+ .outshift = 0,
+ .did = RS9_REG_DID_TYPE_FGV | 0x08,
};
static const struct i2c_device_id rs9_id[] = {
- { "9fgv0241", .driver_data = RENESAS_9FGV0241 },
+ { "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info },
+ { "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info },
+ { "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, rs9_id);
static const struct of_device_id clk_rs9_of_match[] = {
{ .compatible = "renesas,9fgv0241", .data = &renesas_9fgv0241_info },
+ { .compatible = "renesas,9fgv0441", .data = &renesas_9fgv0441_info },
+ { .compatible = "renesas,9fgv0841", .data = &renesas_9fgv0841_info },
{ }
};
MODULE_DEVICE_TABLE(of, clk_rs9_of_match);
@@ -312,7 +418,7 @@ static struct i2c_driver rs9_driver = {
.pm = &rs9_pm_ops,
.of_match_table = clk_rs9_of_match,
},
- .probe_new = rs9_probe,
+ .probe = rs9_probe,
.id_table = rs9_id,
};
module_i2c_driver(rs9_driver);
diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c
index 32f833d732ed..f7412b137e5e 100644
--- a/drivers/clk/clk-rk808.c
+++ b/drivers/clk/clk-rk808.c
@@ -12,10 +12,9 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/mfd/rk808.h>
-#include <linux/i2c.h>
struct rk808_clkout {
- struct rk808 *rk808;
+ struct regmap *regmap;
struct clk_hw clkout1_hw;
struct clk_hw clkout2_hw;
};
@@ -31,9 +30,8 @@ static int rk808_clkout2_enable(struct clk_hw *hw, bool enable)
struct rk808_clkout *rk808_clkout = container_of(hw,
struct rk808_clkout,
clkout2_hw);
- struct rk808 *rk808 = rk808_clkout->rk808;
- return regmap_update_bits(rk808->regmap, RK808_CLK32OUT_REG,
+ return regmap_update_bits(rk808_clkout->regmap, RK808_CLK32OUT_REG,
CLK32KOUT2_EN, enable ? CLK32KOUT2_EN : 0);
}
@@ -52,10 +50,9 @@ static int rk808_clkout2_is_prepared(struct clk_hw *hw)
struct rk808_clkout *rk808_clkout = container_of(hw,
struct rk808_clkout,
clkout2_hw);
- struct rk808 *rk808 = rk808_clkout->rk808;
uint32_t val;
- int ret = regmap_read(rk808->regmap, RK808_CLK32OUT_REG, &val);
+ int ret = regmap_read(rk808_clkout->regmap, RK808_CLK32OUT_REG, &val);
if (ret < 0)
return ret;
@@ -93,9 +90,8 @@ static int rk817_clkout2_enable(struct clk_hw *hw, bool enable)
struct rk808_clkout *rk808_clkout = container_of(hw,
struct rk808_clkout,
clkout2_hw);
- struct rk808 *rk808 = rk808_clkout->rk808;
- return regmap_update_bits(rk808->regmap, RK817_SYS_CFG(1),
+ return regmap_update_bits(rk808_clkout->regmap, RK817_SYS_CFG(1),
RK817_CLK32KOUT2_EN,
enable ? RK817_CLK32KOUT2_EN : 0);
}
@@ -115,10 +111,9 @@ static int rk817_clkout2_is_prepared(struct clk_hw *hw)
struct rk808_clkout *rk808_clkout = container_of(hw,
struct rk808_clkout,
clkout2_hw);
- struct rk808 *rk808 = rk808_clkout->rk808;
unsigned int val;
- int ret = regmap_read(rk808->regmap, RK817_SYS_CFG(1), &val);
+ int ret = regmap_read(rk808_clkout->regmap, RK817_SYS_CFG(1), &val);
if (ret < 0)
return 0;
@@ -153,18 +148,21 @@ static const struct clk_ops *rkpmic_get_ops(long variant)
static int rk808_clkout_probe(struct platform_device *pdev)
{
struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
- struct i2c_client *client = rk808->i2c;
- struct device_node *node = client->dev.of_node;
+ struct device *dev = &pdev->dev;
struct clk_init_data init = {};
struct rk808_clkout *rk808_clkout;
int ret;
- rk808_clkout = devm_kzalloc(&client->dev,
+ dev->of_node = pdev->dev.parent->of_node;
+
+ rk808_clkout = devm_kzalloc(dev,
sizeof(*rk808_clkout), GFP_KERNEL);
if (!rk808_clkout)
return -ENOMEM;
- rk808_clkout->rk808 = rk808;
+ rk808_clkout->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!rk808_clkout->regmap)
+ return -ENODEV;
init.parent_names = NULL;
init.num_parents = 0;
@@ -173,10 +171,10 @@ static int rk808_clkout_probe(struct platform_device *pdev)
rk808_clkout->clkout1_hw.init = &init;
/* optional override of the clockname */
- of_property_read_string_index(node, "clock-output-names",
+ of_property_read_string_index(dev->of_node, "clock-output-names",
0, &init.name);
- ret = devm_clk_hw_register(&client->dev, &rk808_clkout->clkout1_hw);
+ ret = devm_clk_hw_register(dev, &rk808_clkout->clkout1_hw);
if (ret)
return ret;
@@ -185,10 +183,10 @@ static int rk808_clkout_probe(struct platform_device *pdev)
rk808_clkout->clkout2_hw.init = &init;
/* optional override of the clockname */
- of_property_read_string_index(node, "clock-output-names",
+ of_property_read_string_index(dev->of_node, "clock-output-names",
1, &init.name);
- ret = devm_clk_hw_register(&client->dev, &rk808_clkout->clkout2_hw);
+ ret = devm_clk_hw_register(dev, &rk808_clkout->clkout2_hw);
if (ret)
return ret;
diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c
new file mode 100644
index 000000000000..fd144755b879
--- /dev/null
+++ b/drivers/clk/clk-rp1.c
@@ -0,0 +1,2462 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Raspberry Pi Ltd.
+ *
+ * Clock driver for RP1 PCIe multifunction chip.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/units.h>
+
+#include <dt-bindings/clock/raspberrypi,rp1-clocks.h>
+
+#define PLL_SYS_OFFSET 0x08000
+#define PLL_SYS_CS (PLL_SYS_OFFSET + 0x00)
+#define PLL_SYS_PWR (PLL_SYS_OFFSET + 0x04)
+#define PLL_SYS_FBDIV_INT (PLL_SYS_OFFSET + 0x08)
+#define PLL_SYS_FBDIV_FRAC (PLL_SYS_OFFSET + 0x0c)
+#define PLL_SYS_PRIM (PLL_SYS_OFFSET + 0x10)
+#define PLL_SYS_SEC (PLL_SYS_OFFSET + 0x14)
+
+#define PLL_AUDIO_OFFSET 0x0c000
+#define PLL_AUDIO_CS (PLL_AUDIO_OFFSET + 0x00)
+#define PLL_AUDIO_PWR (PLL_AUDIO_OFFSET + 0x04)
+#define PLL_AUDIO_FBDIV_INT (PLL_AUDIO_OFFSET + 0x08)
+#define PLL_AUDIO_FBDIV_FRAC (PLL_AUDIO_OFFSET + 0x0c)
+#define PLL_AUDIO_PRIM (PLL_AUDIO_OFFSET + 0x10)
+#define PLL_AUDIO_SEC (PLL_AUDIO_OFFSET + 0x14)
+#define PLL_AUDIO_TERN (PLL_AUDIO_OFFSET + 0x18)
+
+#define PLL_VIDEO_OFFSET 0x10000
+#define PLL_VIDEO_CS (PLL_VIDEO_OFFSET + 0x00)
+#define PLL_VIDEO_PWR (PLL_VIDEO_OFFSET + 0x04)
+#define PLL_VIDEO_FBDIV_INT (PLL_VIDEO_OFFSET + 0x08)
+#define PLL_VIDEO_FBDIV_FRAC (PLL_VIDEO_OFFSET + 0x0c)
+#define PLL_VIDEO_PRIM (PLL_VIDEO_OFFSET + 0x10)
+#define PLL_VIDEO_SEC (PLL_VIDEO_OFFSET + 0x14)
+
+#define GPCLK_OE_CTRL 0x00000
+
+#define CLK_SYS_OFFSET 0x00014
+#define CLK_SYS_CTRL (CLK_SYS_OFFSET + 0x00)
+#define CLK_SYS_DIV_INT (CLK_SYS_OFFSET + 0x04)
+#define CLK_SYS_SEL (CLK_SYS_OFFSET + 0x0c)
+
+#define CLK_SLOW_OFFSET 0x00024
+#define CLK_SLOW_SYS_CTRL (CLK_SLOW_OFFSET + 0x00)
+#define CLK_SLOW_SYS_DIV_INT (CLK_SLOW_OFFSET + 0x04)
+#define CLK_SLOW_SYS_SEL (CLK_SLOW_OFFSET + 0x0c)
+
+#define CLK_DMA_OFFSET 0x00044
+#define CLK_DMA_CTRL (CLK_DMA_OFFSET + 0x00)
+#define CLK_DMA_DIV_INT (CLK_DMA_OFFSET + 0x04)
+#define CLK_DMA_SEL (CLK_DMA_OFFSET + 0x0c)
+
+#define CLK_UART_OFFSET 0x00054
+#define CLK_UART_CTRL (CLK_UART_OFFSET + 0x00)
+#define CLK_UART_DIV_INT (CLK_UART_OFFSET + 0x04)
+#define CLK_UART_SEL (CLK_UART_OFFSET + 0x0c)
+
+#define CLK_ETH_OFFSET 0x00064
+#define CLK_ETH_CTRL (CLK_ETH_OFFSET + 0x00)
+#define CLK_ETH_DIV_INT (CLK_ETH_OFFSET + 0x04)
+#define CLK_ETH_SEL (CLK_ETH_OFFSET + 0x0c)
+
+#define CLK_PWM0_OFFSET 0x00074
+#define CLK_PWM0_CTRL (CLK_PWM0_OFFSET + 0x00)
+#define CLK_PWM0_DIV_INT (CLK_PWM0_OFFSET + 0x04)
+#define CLK_PWM0_DIV_FRAC (CLK_PWM0_OFFSET + 0x08)
+#define CLK_PWM0_SEL (CLK_PWM0_OFFSET + 0x0c)
+
+#define CLK_PWM1_OFFSET 0x00084
+#define CLK_PWM1_CTRL (CLK_PWM1_OFFSET + 0x00)
+#define CLK_PWM1_DIV_INT (CLK_PWM1_OFFSET + 0x04)
+#define CLK_PWM1_DIV_FRAC (CLK_PWM1_OFFSET + 0x08)
+#define CLK_PWM1_SEL (CLK_PWM1_OFFSET + 0x0c)
+
+#define CLK_AUDIO_IN_OFFSET 0x00094
+#define CLK_AUDIO_IN_CTRL (CLK_AUDIO_IN_OFFSET + 0x00)
+#define CLK_AUDIO_IN_DIV_INT (CLK_AUDIO_IN_OFFSET + 0x04)
+#define CLK_AUDIO_IN_SEL (CLK_AUDIO_IN_OFFSET + 0x0c)
+
+#define CLK_AUDIO_OUT_OFFSET 0x000a4
+#define CLK_AUDIO_OUT_CTRL (CLK_AUDIO_OUT_OFFSET + 0x00)
+#define CLK_AUDIO_OUT_DIV_INT (CLK_AUDIO_OUT_OFFSET + 0x04)
+#define CLK_AUDIO_OUT_SEL (CLK_AUDIO_OUT_OFFSET + 0x0c)
+
+#define CLK_I2S_OFFSET 0x000b4
+#define CLK_I2S_CTRL (CLK_I2S_OFFSET + 0x00)
+#define CLK_I2S_DIV_INT (CLK_I2S_OFFSET + 0x04)
+#define CLK_I2S_SEL (CLK_I2S_OFFSET + 0x0c)
+
+#define CLK_MIPI0_CFG_OFFSET 0x000c4
+#define CLK_MIPI0_CFG_CTRL (CLK_MIPI0_CFG_OFFSET + 0x00)
+#define CLK_MIPI0_CFG_DIV_INT (CLK_MIPI0_CFG_OFFSET + 0x04)
+#define CLK_MIPI0_CFG_SEL (CLK_MIPI0_CFG_OFFSET + 0x0c)
+
+#define CLK_MIPI1_CFG_OFFSET 0x000d4
+#define CLK_MIPI1_CFG_CTRL (CLK_MIPI1_CFG_OFFSET + 0x00)
+#define CLK_MIPI1_CFG_DIV_INT (CLK_MIPI1_CFG_OFFSET + 0x04)
+#define CLK_MIPI1_CFG_SEL (CLK_MIPI1_CFG_OFFSET + 0x0c)
+
+#define CLK_PCIE_AUX_OFFSET 0x000e4
+#define CLK_PCIE_AUX_CTRL (CLK_PCIE_AUX_OFFSET + 0x00)
+#define CLK_PCIE_AUX_DIV_INT (CLK_PCIE_AUX_OFFSET + 0x04)
+#define CLK_PCIE_AUX_SEL (CLK_PCIE_AUX_OFFSET + 0x0c)
+
+#define CLK_USBH0_MICROFRAME_OFFSET 0x000f4
+#define CLK_USBH0_MICROFRAME_CTRL (CLK_USBH0_MICROFRAME_OFFSET + 0x00)
+#define CLK_USBH0_MICROFRAME_DIV_INT (CLK_USBH0_MICROFRAME_OFFSET + 0x04)
+#define CLK_USBH0_MICROFRAME_SEL (CLK_USBH0_MICROFRAME_OFFSET + 0x0c)
+
+#define CLK_USBH1_MICROFRAME_OFFSET 0x00104
+#define CLK_USBH1_MICROFRAME_CTRL (CLK_USBH1_MICROFRAME_OFFSET + 0x00)
+#define CLK_USBH1_MICROFRAME_DIV_INT (CLK_USBH1_MICROFRAME_OFFSET + 0x04)
+#define CLK_USBH1_MICROFRAME_SEL (CLK_USBH1_MICROFRAME_OFFSET + 0x0c)
+
+#define CLK_USBH0_SUSPEND_OFFSET 0x00114
+#define CLK_USBH0_SUSPEND_CTRL (CLK_USBH0_SUSPEND_OFFSET + 0x00)
+#define CLK_USBH0_SUSPEND_DIV_INT (CLK_USBH0_SUSPEND_OFFSET + 0x04)
+#define CLK_USBH0_SUSPEND_SEL (CLK_USBH0_SUSPEND_OFFSET + 0x0c)
+
+#define CLK_USBH1_SUSPEND_OFFSET 0x00124
+#define CLK_USBH1_SUSPEND_CTRL (CLK_USBH1_SUSPEND_OFFSET + 0x00)
+#define CLK_USBH1_SUSPEND_DIV_INT (CLK_USBH1_SUSPEND_OFFSET + 0x04)
+#define CLK_USBH1_SUSPEND_SEL (CLK_USBH1_SUSPEND_OFFSET + 0x0c)
+
+#define CLK_ETH_TSU_OFFSET 0x00134
+#define CLK_ETH_TSU_CTRL (CLK_ETH_TSU_OFFSET + 0x00)
+#define CLK_ETH_TSU_DIV_INT (CLK_ETH_TSU_OFFSET + 0x04)
+#define CLK_ETH_TSU_SEL (CLK_ETH_TSU_OFFSET + 0x0c)
+
+#define CLK_ADC_OFFSET 0x00144
+#define CLK_ADC_CTRL (CLK_ADC_OFFSET + 0x00)
+#define CLK_ADC_DIV_INT (CLK_ADC_OFFSET + 0x04)
+#define CLK_ADC_SEL (CLK_ADC_OFFSET + 0x0c)
+
+#define CLK_SDIO_TIMER_OFFSET 0x00154
+#define CLK_SDIO_TIMER_CTRL (CLK_SDIO_TIMER_OFFSET + 0x00)
+#define CLK_SDIO_TIMER_DIV_INT (CLK_SDIO_TIMER_OFFSET + 0x04)
+#define CLK_SDIO_TIMER_SEL (CLK_SDIO_TIMER_OFFSET + 0x0c)
+
+#define CLK_SDIO_ALT_SRC_OFFSET 0x00164
+#define CLK_SDIO_ALT_SRC_CTRL (CLK_SDIO_ALT_SRC_OFFSET + 0x00)
+#define CLK_SDIO_ALT_SRC_DIV_INT (CLK_SDIO_ALT_SRC_OFFSET + 0x04)
+#define CLK_SDIO_ALT_SRC_SEL (CLK_SDIO_ALT_SRC_OFFSET + 0x0c)
+
+#define CLK_GP0_OFFSET 0x00174
+#define CLK_GP0_CTRL (CLK_GP0_OFFSET + 0x00)
+#define CLK_GP0_DIV_INT (CLK_GP0_OFFSET + 0x04)
+#define CLK_GP0_DIV_FRAC (CLK_GP0_OFFSET + 0x08)
+#define CLK_GP0_SEL (CLK_GP0_OFFSET + 0x0c)
+
+#define CLK_GP1_OFFSET 0x00184
+#define CLK_GP1_CTRL (CLK_GP1_OFFSET + 0x00)
+#define CLK_GP1_DIV_INT (CLK_GP1_OFFSET + 0x04)
+#define CLK_GP1_DIV_FRAC (CLK_GP1_OFFSET + 0x08)
+#define CLK_GP1_SEL (CLK_GP1_OFFSET + 0x0c)
+
+#define CLK_GP2_OFFSET 0x00194
+#define CLK_GP2_CTRL (CLK_GP2_OFFSET + 0x00)
+#define CLK_GP2_DIV_INT (CLK_GP2_OFFSET + 0x04)
+#define CLK_GP2_DIV_FRAC (CLK_GP2_OFFSET + 0x08)
+#define CLK_GP2_SEL (CLK_GP2_OFFSET + 0x0c)
+
+#define CLK_GP3_OFFSET 0x001a4
+#define CLK_GP3_CTRL (CLK_GP3_OFFSET + 0x00)
+#define CLK_GP3_DIV_INT (CLK_GP3_OFFSET + 0x04)
+#define CLK_GP3_DIV_FRAC (CLK_GP3_OFFSET + 0x08)
+#define CLK_GP3_SEL (CLK_GP3_OFFSET + 0x0c)
+
+#define CLK_GP4_OFFSET 0x001b4
+#define CLK_GP4_CTRL (CLK_GP4_OFFSET + 0x00)
+#define CLK_GP4_DIV_INT (CLK_GP4_OFFSET + 0x04)
+#define CLK_GP4_DIV_FRAC (CLK_GP4_OFFSET + 0x08)
+#define CLK_GP4_SEL (CLK_GP4_OFFSET + 0x0c)
+
+#define CLK_GP5_OFFSET 0x001c4
+#define CLK_GP5_CTRL (CLK_GP5_OFFSET + 0x00)
+#define CLK_GP5_DIV_INT (CLK_GP5_OFFSET + 0x04)
+#define CLK_GP5_DIV_FRAC (CLK_GP5_OFFSET + 0x08)
+#define CLK_GP5_SEL (CLK_GP5_OFFSET + 0x0c)
+
+#define CLK_SYS_RESUS_CTRL 0x0020c
+
+#define CLK_SLOW_SYS_RESUS_CTRL 0x00214
+
+#define FC0_OFFSET 0x0021c
+#define FC0_REF_KHZ (FC0_OFFSET + 0x00)
+#define FC0_MIN_KHZ (FC0_OFFSET + 0x04)
+#define FC0_MAX_KHZ (FC0_OFFSET + 0x08)
+#define FC0_DELAY (FC0_OFFSET + 0x0c)
+#define FC0_INTERVAL (FC0_OFFSET + 0x10)
+#define FC0_SRC (FC0_OFFSET + 0x14)
+#define FC0_STATUS (FC0_OFFSET + 0x18)
+#define FC0_RESULT (FC0_OFFSET + 0x1c)
+#define FC_SIZE 0x20
+#define FC_COUNT 8
+#define FC_NUM(idx, off) ((idx) * 32 + (off))
+
+#define AUX_SEL 1
+
+#define VIDEO_CLOCKS_OFFSET 0x4000
+#define VIDEO_CLK_VEC_CTRL (VIDEO_CLOCKS_OFFSET + 0x0000)
+#define VIDEO_CLK_VEC_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0004)
+#define VIDEO_CLK_VEC_SEL (VIDEO_CLOCKS_OFFSET + 0x000c)
+#define VIDEO_CLK_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0010)
+#define VIDEO_CLK_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0014)
+#define VIDEO_CLK_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x001c)
+#define VIDEO_CLK_MIPI0_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0020)
+#define VIDEO_CLK_MIPI0_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0024)
+#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0028)
+#define VIDEO_CLK_MIPI0_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x002c)
+#define VIDEO_CLK_MIPI1_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0030)
+#define VIDEO_CLK_MIPI1_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0034)
+#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0038)
+#define VIDEO_CLK_MIPI1_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x003c)
+
+#define DIV_INT_8BIT_MAX GENMASK(7, 0) /* max divide for most clocks */
+#define DIV_INT_16BIT_MAX GENMASK(15, 0) /* max divide for GPx, PWM */
+#define DIV_INT_24BIT_MAX GENMASK(23, 0) /* max divide for CLK_SYS */
+
+#define FC0_STATUS_DONE BIT(4)
+#define FC0_STATUS_RUNNING BIT(8)
+#define FC0_RESULT_FRAC_SHIFT 5
+
+#define PLL_PRIM_DIV1_MASK GENMASK(18, 16)
+#define PLL_PRIM_DIV2_MASK GENMASK(14, 12)
+
+#define PLL_SEC_DIV_MASK GENMASK(12, 8)
+
+#define PLL_CS_LOCK BIT(31)
+#define PLL_CS_REFDIV_MASK BIT(1)
+
+#define PLL_PWR_PD BIT(0)
+#define PLL_PWR_DACPD BIT(1)
+#define PLL_PWR_DSMPD BIT(2)
+#define PLL_PWR_POSTDIVPD BIT(3)
+#define PLL_PWR_4PHASEPD BIT(4)
+#define PLL_PWR_VCOPD BIT(5)
+#define PLL_PWR_MASK GENMASK(5, 0)
+
+#define PLL_SEC_RST BIT(16)
+#define PLL_SEC_IMPL BIT(31)
+
+/* PLL phase output for both PRI and SEC */
+#define PLL_PH_EN BIT(4)
+#define PLL_PH_PHASE_SHIFT 0
+
+#define RP1_PLL_PHASE_0 0
+#define RP1_PLL_PHASE_90 1
+#define RP1_PLL_PHASE_180 2
+#define RP1_PLL_PHASE_270 3
+
+/* Clock fields for all clocks */
+#define CLK_CTRL_ENABLE BIT(11)
+#define CLK_CTRL_AUXSRC_MASK GENMASK(9, 5)
+#define CLK_CTRL_SRC_SHIFT 0
+#define CLK_DIV_FRAC_BITS 16
+
+#define LOCK_TIMEOUT_US 100000
+#define LOCK_POLL_DELAY_US 5
+
+#define MAX_CLK_PARENTS 16
+
+#define PLL_DIV_INVALID 19
+/*
+ * Secondary PLL channel output divider table.
+ * Divider values range from 8 to 19, where
+ * 19 means invalid.
+ */
+static const struct clk_div_table pll_sec_div_table[] = {
+ { 0x00, PLL_DIV_INVALID },
+ { 0x01, PLL_DIV_INVALID },
+ { 0x02, PLL_DIV_INVALID },
+ { 0x03, PLL_DIV_INVALID },
+ { 0x04, PLL_DIV_INVALID },
+ { 0x05, PLL_DIV_INVALID },
+ { 0x06, PLL_DIV_INVALID },
+ { 0x07, PLL_DIV_INVALID },
+ { 0x08, 8 },
+ { 0x09, 9 },
+ { 0x0a, 10 },
+ { 0x0b, 11 },
+ { 0x0c, 12 },
+ { 0x0d, 13 },
+ { 0x0e, 14 },
+ { 0x0f, 15 },
+ { 0x10, 16 },
+ { 0x11, 17 },
+ { 0x12, 18 },
+ { 0x13, PLL_DIV_INVALID },
+ { 0x14, PLL_DIV_INVALID },
+ { 0x15, PLL_DIV_INVALID },
+ { 0x16, PLL_DIV_INVALID },
+ { 0x17, PLL_DIV_INVALID },
+ { 0x18, PLL_DIV_INVALID },
+ { 0x19, PLL_DIV_INVALID },
+ { 0x1a, PLL_DIV_INVALID },
+ { 0x1b, PLL_DIV_INVALID },
+ { 0x1c, PLL_DIV_INVALID },
+ { 0x1d, PLL_DIV_INVALID },
+ { 0x1e, PLL_DIV_INVALID },
+ { 0x1f, PLL_DIV_INVALID },
+ { 0 }
+};
+
+struct rp1_clockman {
+ struct device *dev;
+ void __iomem *regs;
+ struct regmap *regmap;
+ spinlock_t regs_lock; /* spinlock for all clocks */
+
+ /* Must be last */
+ struct clk_hw_onecell_data onecell;
+};
+
+struct rp1_pll_core_data {
+ u32 cs_reg;
+ u32 pwr_reg;
+ u32 fbdiv_int_reg;
+ u32 fbdiv_frac_reg;
+ u32 fc0_src;
+};
+
+struct rp1_pll_data {
+ u32 ctrl_reg;
+ u32 fc0_src;
+};
+
+struct rp1_pll_ph_data {
+ unsigned int phase;
+ unsigned int fixed_divider;
+ u32 ph_reg;
+ u32 fc0_src;
+};
+
+struct rp1_pll_divider_data {
+ u32 sec_reg;
+ u32 fc0_src;
+};
+
+struct rp1_clock_data {
+ int num_std_parents;
+ int num_aux_parents;
+ u32 oe_mask;
+ u32 clk_src_mask;
+ u32 ctrl_reg;
+ u32 div_int_reg;
+ u32 div_frac_reg;
+ u32 sel_reg;
+ u32 div_int_max;
+ unsigned long max_freq;
+ u32 fc0_src;
+};
+
+struct rp1_clk_desc {
+ struct clk_hw *(*clk_register)(struct rp1_clockman *clockman,
+ struct rp1_clk_desc *desc);
+ const void *data;
+ struct clk_hw hw;
+ struct rp1_clockman *clockman;
+ unsigned long cached_rate;
+ struct clk_divider div;
+};
+
+static struct rp1_clk_desc *clk_audio_core;
+static struct rp1_clk_desc *clk_audio;
+static struct rp1_clk_desc *clk_i2s;
+static struct clk_hw *clk_xosc;
+
+static inline
+void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val)
+{
+ regmap_write(clockman->regmap, reg, val);
+}
+
+static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg)
+{
+ u32 val;
+
+ regmap_read(clockman->regmap, reg, &val);
+
+ return val;
+}
+
+static int rp1_pll_core_is_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_core->clockman;
+ const struct rp1_pll_core_data *data = pll_core->data;
+ u32 pwr = clockman_read(clockman, data->pwr_reg);
+
+ return (pwr & PLL_PWR_PD) || (pwr & PLL_PWR_POSTDIVPD);
+}
+
+static int rp1_pll_core_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_core->clockman;
+ const struct rp1_pll_core_data *data = pll_core->data;
+ u32 fbdiv_frac, val;
+ int ret;
+
+ spin_lock(&clockman->regs_lock);
+
+ if (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) {
+ /* Reset to a known state. */
+ clockman_write(clockman, data->pwr_reg, PLL_PWR_MASK);
+ clockman_write(clockman, data->fbdiv_int_reg, 20);
+ clockman_write(clockman, data->fbdiv_frac_reg, 0);
+ clockman_write(clockman, data->cs_reg, PLL_CS_REFDIV_MASK);
+ }
+
+ /* Come out of reset. */
+ fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg);
+ clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD);
+ spin_unlock(&clockman->regs_lock);
+
+ /* Wait for the PLL to lock. */
+ ret = regmap_read_poll_timeout(clockman->regmap, data->cs_reg, val,
+ val & PLL_CS_LOCK,
+ LOCK_POLL_DELAY_US, LOCK_TIMEOUT_US);
+ if (ret)
+ dev_err(clockman->dev, "%s: can't lock PLL\n",
+ clk_hw_get_name(hw));
+
+ return ret;
+}
+
+static void rp1_pll_core_off(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_core->clockman;
+ const struct rp1_pll_core_data *data = pll_core->data;
+
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->pwr_reg, 0);
+ spin_unlock(&clockman->regs_lock);
+}
+
+static inline unsigned long get_pll_core_divider(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate,
+ u32 *div_int, u32 *div_frac)
+{
+ u32 fbdiv_int, fbdiv_frac;
+ unsigned long calc_rate;
+ u64 shifted_fbdiv_int;
+ u64 div_fp64; /* 32.32 fixed point fraction. */
+
+ /* Factor of reference clock to VCO frequency. */
+ div_fp64 = (u64)(rate) << 32;
+ div_fp64 = DIV_ROUND_CLOSEST_ULL(div_fp64, parent_rate);
+
+ /* Round the fractional component at 24 bits. */
+ div_fp64 += 1 << (32 - 24 - 1);
+
+ fbdiv_int = div_fp64 >> 32;
+ fbdiv_frac = (div_fp64 >> (32 - 24)) & 0xffffff;
+
+ shifted_fbdiv_int = (u64)fbdiv_int << 24;
+ calc_rate = (u64)parent_rate * (shifted_fbdiv_int + fbdiv_frac);
+ calc_rate += BIT(23);
+ calc_rate >>= 24;
+
+ *div_int = fbdiv_int;
+ *div_frac = fbdiv_frac;
+
+ return calc_rate;
+}
+
+static int rp1_pll_core_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_core->clockman;
+ const struct rp1_pll_core_data *data = pll_core->data;
+ u32 fbdiv_int, fbdiv_frac;
+
+ /* Disable dividers to start with. */
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->fbdiv_int_reg, 0);
+ clockman_write(clockman, data->fbdiv_frac_reg, 0);
+ spin_unlock(&clockman->regs_lock);
+
+ get_pll_core_divider(hw, rate, parent_rate,
+ &fbdiv_int, &fbdiv_frac);
+
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD);
+ clockman_write(clockman, data->fbdiv_int_reg, fbdiv_int);
+ clockman_write(clockman, data->fbdiv_frac_reg, fbdiv_frac);
+ spin_unlock(&clockman->regs_lock);
+
+ /* Check that reference frequency is no greater than VCO / 16. */
+ if (WARN_ON_ONCE(parent_rate > (rate / 16)))
+ return -ERANGE;
+
+ spin_lock(&clockman->regs_lock);
+ /* Don't need to divide ref unless parent_rate > (output freq / 16) */
+ clockman_write(clockman, data->cs_reg,
+ clockman_read(clockman, data->cs_reg) |
+ PLL_CS_REFDIV_MASK);
+ spin_unlock(&clockman->regs_lock);
+
+ return 0;
+}
+
+static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rp1_clk_desc *pll_core = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_core->clockman;
+ const struct rp1_pll_core_data *data = pll_core->data;
+ u32 fbdiv_int, fbdiv_frac;
+ unsigned long calc_rate;
+ u64 shifted_fbdiv_int;
+
+ fbdiv_int = clockman_read(clockman, data->fbdiv_int_reg);
+ fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg);
+
+ shifted_fbdiv_int = (u64)fbdiv_int << 24;
+ calc_rate = (u64)parent_rate * (shifted_fbdiv_int + fbdiv_frac);
+ calc_rate += BIT(23);
+ calc_rate >>= 24;
+
+ return calc_rate;
+}
+
+static int rp1_pll_core_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ u32 fbdiv_int, fbdiv_frac;
+
+ req->rate = get_pll_core_divider(hw, req->rate, req->best_parent_rate,
+ &fbdiv_int,
+ &fbdiv_frac);
+
+ return 0;
+}
+
+static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate,
+ u32 *divider1, u32 *divider2)
+{
+ unsigned int div1, div2;
+ unsigned int best_div1 = 7, best_div2 = 7;
+ unsigned long best_rate_diff =
+ abs_diff(DIV_ROUND_CLOSEST(parent_rate, best_div1 * best_div2), rate);
+ unsigned long rate_diff, calc_rate;
+
+ for (div1 = 1; div1 <= 7; div1++) {
+ for (div2 = 1; div2 <= div1; div2++) {
+ calc_rate = DIV_ROUND_CLOSEST(parent_rate, div1 * div2);
+ rate_diff = abs_diff(calc_rate, rate);
+
+ if (calc_rate == rate) {
+ best_div1 = div1;
+ best_div2 = div2;
+ goto done;
+ } else if (rate_diff < best_rate_diff) {
+ best_div1 = div1;
+ best_div2 = div2;
+ best_rate_diff = rate_diff;
+ }
+ }
+ }
+
+done:
+ *divider1 = best_div1;
+ *divider2 = best_div2;
+}
+
+static int rp1_pll_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct rp1_clk_desc *pll = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll->clockman;
+ const struct rp1_pll_data *data = pll->data;
+
+ u32 prim, prim_div1, prim_div2;
+
+ get_pll_prim_dividers(rate, parent_rate, &prim_div1, &prim_div2);
+
+ spin_lock(&clockman->regs_lock);
+ prim = clockman_read(clockman, data->ctrl_reg);
+ prim &= ~PLL_PRIM_DIV1_MASK;
+ prim |= FIELD_PREP(PLL_PRIM_DIV1_MASK, prim_div1);
+ prim &= ~PLL_PRIM_DIV2_MASK;
+ prim |= FIELD_PREP(PLL_PRIM_DIV2_MASK, prim_div2);
+ clockman_write(clockman, data->ctrl_reg, prim);
+ spin_unlock(&clockman->regs_lock);
+
+ return 0;
+}
+
+static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rp1_clk_desc *pll = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll->clockman;
+ const struct rp1_pll_data *data = pll->data;
+ u32 prim, prim_div1, prim_div2;
+
+ prim = clockman_read(clockman, data->ctrl_reg);
+ prim_div1 = FIELD_GET(PLL_PRIM_DIV1_MASK, prim);
+ prim_div2 = FIELD_GET(PLL_PRIM_DIV2_MASK, prim);
+
+ if (!prim_div1 || !prim_div2) {
+ dev_err(clockman->dev, "%s: (%s) zero divider value\n",
+ __func__, clk_hw_get_name(hw));
+ return 0;
+ }
+
+ return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2);
+}
+
+static int rp1_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_hw *clk_audio_hw = &clk_audio->hw;
+ u32 div1, div2;
+
+ if (hw == clk_audio_hw && clk_audio->cached_rate == req->rate)
+ req->best_parent_rate = clk_audio_core->cached_rate;
+
+ get_pll_prim_dividers(req->rate, req->best_parent_rate, &div1, &div2);
+
+ req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, div1 * div2);
+
+ return 0;
+}
+
+static int rp1_pll_ph_is_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_ph->clockman;
+ const struct rp1_pll_ph_data *data = pll_ph->data;
+
+ return !!(clockman_read(clockman, data->ph_reg) & PLL_PH_EN);
+}
+
+static int rp1_pll_ph_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_ph->clockman;
+ const struct rp1_pll_ph_data *data = pll_ph->data;
+ u32 ph_reg;
+
+ spin_lock(&clockman->regs_lock);
+ ph_reg = clockman_read(clockman, data->ph_reg);
+ ph_reg |= data->phase << PLL_PH_PHASE_SHIFT;
+ ph_reg |= PLL_PH_EN;
+ clockman_write(clockman, data->ph_reg, ph_reg);
+ spin_unlock(&clockman->regs_lock);
+
+ return 0;
+}
+
+static void rp1_pll_ph_off(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = pll_ph->clockman;
+ const struct rp1_pll_ph_data *data = pll_ph->data;
+
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->ph_reg,
+ clockman_read(clockman, data->ph_reg) & ~PLL_PH_EN);
+ spin_unlock(&clockman->regs_lock);
+}
+
+static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw);
+ const struct rp1_pll_ph_data *data = pll_ph->data;
+
+ return parent_rate / data->fixed_divider;
+}
+
+static int rp1_pll_ph_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rp1_clk_desc *pll_ph = container_of(hw, struct rp1_clk_desc, hw);
+ const struct rp1_pll_ph_data *data = pll_ph->data;
+
+ req->rate = req->best_parent_rate / data->fixed_divider;
+
+ return 0;
+}
+
+static int rp1_pll_divider_is_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw);
+ struct rp1_clockman *clockman = divider->clockman;
+ const struct rp1_pll_data *data = divider->data;
+
+ return !(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_RST);
+}
+
+static int rp1_pll_divider_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw);
+ struct rp1_clockman *clockman = divider->clockman;
+ const struct rp1_pll_data *data = divider->data;
+
+ spin_lock(&clockman->regs_lock);
+ /* Check the implementation bit is set! */
+ WARN_ON(!(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_IMPL));
+ clockman_write(clockman, data->ctrl_reg,
+ clockman_read(clockman, data->ctrl_reg) & ~PLL_SEC_RST);
+ spin_unlock(&clockman->regs_lock);
+
+ return 0;
+}
+
+static void rp1_pll_divider_off(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw);
+ struct rp1_clockman *clockman = divider->clockman;
+ const struct rp1_pll_data *data = divider->data;
+
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->ctrl_reg,
+ clockman_read(clockman, data->ctrl_reg) | PLL_SEC_RST);
+ spin_unlock(&clockman->regs_lock);
+}
+
+static int rp1_pll_divider_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rp1_clk_desc *divider = container_of(hw, struct rp1_clk_desc, div.hw);
+ struct rp1_clockman *clockman = divider->clockman;
+ const struct rp1_pll_data *data = divider->data;
+ u32 div, sec;
+
+ div = DIV_ROUND_UP_ULL(parent_rate, rate);
+ div = clamp(div, 8u, 19u);
+
+ spin_lock(&clockman->regs_lock);
+ sec = clockman_read(clockman, data->ctrl_reg);
+ sec &= ~PLL_SEC_DIV_MASK;
+ sec |= FIELD_PREP(PLL_SEC_DIV_MASK, div);
+
+ /* Must keep the divider in reset to change the value. */
+ sec |= PLL_SEC_RST;
+ clockman_write(clockman, data->ctrl_reg, sec);
+
+ /* must sleep 10 pll vco cycles */
+ ndelay(div64_ul(10ULL * div * NSEC_PER_SEC, parent_rate));
+
+ sec &= ~PLL_SEC_RST;
+ clockman_write(clockman, data->ctrl_reg, sec);
+ spin_unlock(&clockman->regs_lock);
+
+ return 0;
+}
+
+static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return clk_divider_ops.recalc_rate(hw, parent_rate);
+}
+
+static int rp1_pll_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ req->rate = clk_divider_ops.determine_rate(hw, req);
+
+ return 0;
+}
+
+static int rp1_clock_is_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+
+ return !!(clockman_read(clockman, data->ctrl_reg) & CLK_CTRL_ENABLE);
+}
+
+static unsigned long rp1_clock_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+ u64 calc_rate;
+ u64 div;
+ u32 frac;
+
+ div = clockman_read(clockman, data->div_int_reg);
+ frac = (data->div_frac_reg != 0) ?
+ clockman_read(clockman, data->div_frac_reg) : 0;
+
+ /* If the integer portion of the divider is 0, treat it as 2^16 */
+ if (!div)
+ div = 1 << 16;
+
+ div = (div << CLK_DIV_FRAC_BITS) | (frac >> (32 - CLK_DIV_FRAC_BITS));
+
+ calc_rate = (u64)parent_rate << CLK_DIV_FRAC_BITS;
+ calc_rate = div64_u64(calc_rate, div);
+
+ return calc_rate;
+}
+
+static int rp1_clock_on(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->ctrl_reg,
+ clockman_read(clockman, data->ctrl_reg) | CLK_CTRL_ENABLE);
+ /* If this is a GPCLK, turn on the output-enable */
+ if (data->oe_mask)
+ clockman_write(clockman, GPCLK_OE_CTRL,
+ clockman_read(clockman, GPCLK_OE_CTRL) | data->oe_mask);
+ spin_unlock(&clockman->regs_lock);
+
+ return 0;
+}
+
+static void rp1_clock_off(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+
+ spin_lock(&clockman->regs_lock);
+ clockman_write(clockman, data->ctrl_reg,
+ clockman_read(clockman, data->ctrl_reg) & ~CLK_CTRL_ENABLE);
+ /* If this is a GPCLK, turn off the output-enable */
+ if (data->oe_mask)
+ clockman_write(clockman, GPCLK_OE_CTRL,
+ clockman_read(clockman, GPCLK_OE_CTRL) & ~data->oe_mask);
+ spin_unlock(&clockman->regs_lock);
+}
+
+static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate,
+ const struct rp1_clock_data *data)
+{
+ u64 div;
+
+ /*
+ * Due to earlier rounding, calculated parent_rate may differ from
+ * expected value. Don't fail on a small discrepancy near unity divide.
+ */
+ if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS))
+ return 0;
+
+ /*
+ * Always express div in fixed-point format for fractional division;
+ * If no fractional divider is present, the fraction part will be zero.
+ */
+ if (data->div_frac_reg) {
+ div = (u64)parent_rate << CLK_DIV_FRAC_BITS;
+ div = DIV_ROUND_CLOSEST_ULL(div, rate);
+ } else {
+ div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate);
+ div <<= CLK_DIV_FRAC_BITS;
+ }
+
+ div = clamp(div,
+ 1ull << CLK_DIV_FRAC_BITS,
+ (u64)data->div_int_max << CLK_DIV_FRAC_BITS);
+
+ return div;
+}
+
+static u8 rp1_clock_get_parent(struct clk_hw *hw)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+ u32 sel, ctrl;
+ u8 parent;
+
+ /* Sel is one-hot, so find the first bit set */
+ sel = clockman_read(clockman, data->sel_reg);
+ parent = ffs(sel) - 1;
+
+ /* sel == 0 implies the parent clock is not enabled yet. */
+ if (!sel) {
+ /* Read the clock src from the CTRL register instead */
+ ctrl = clockman_read(clockman, data->ctrl_reg);
+ parent = (ctrl & data->clk_src_mask) >> CLK_CTRL_SRC_SHIFT;
+ }
+
+ if (parent >= data->num_std_parents)
+ parent = AUX_SEL;
+
+ if (parent == AUX_SEL) {
+ /*
+ * Clock parent is an auxiliary source, so get the parent from
+ * the AUXSRC register field.
+ */
+ ctrl = clockman_read(clockman, data->ctrl_reg);
+ parent = FIELD_GET(CLK_CTRL_AUXSRC_MASK, ctrl);
+ parent += data->num_std_parents;
+ }
+
+ return parent;
+}
+
+static int rp1_clock_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+ u32 ctrl, sel;
+
+ spin_lock(&clockman->regs_lock);
+ ctrl = clockman_read(clockman, data->ctrl_reg);
+
+ if (index >= data->num_std_parents) {
+ /* This is an aux source request */
+ if (index >= data->num_std_parents + data->num_aux_parents) {
+ spin_unlock(&clockman->regs_lock);
+ return -EINVAL;
+ }
+
+ /* Select parent from aux list */
+ ctrl &= ~CLK_CTRL_AUXSRC_MASK;
+ ctrl |= FIELD_PREP(CLK_CTRL_AUXSRC_MASK, index - data->num_std_parents);
+ /* Set src to aux list */
+ ctrl &= ~data->clk_src_mask;
+ ctrl |= (AUX_SEL << CLK_CTRL_SRC_SHIFT) & data->clk_src_mask;
+ } else {
+ ctrl &= ~data->clk_src_mask;
+ ctrl |= (index << CLK_CTRL_SRC_SHIFT) & data->clk_src_mask;
+ }
+
+ clockman_write(clockman, data->ctrl_reg, ctrl);
+ spin_unlock(&clockman->regs_lock);
+
+ sel = rp1_clock_get_parent(hw);
+ if (sel != index)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rp1_clock_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate,
+ u8 parent)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ struct rp1_clockman *clockman = clock->clockman;
+ const struct rp1_clock_data *data = clock->data;
+ u32 div = rp1_clock_choose_div(rate, parent_rate, data);
+
+ spin_lock(&clockman->regs_lock);
+
+ clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS);
+ if (data->div_frac_reg)
+ clockman_write(clockman, data->div_frac_reg, div << (32 - CLK_DIV_FRAC_BITS));
+
+ spin_unlock(&clockman->regs_lock);
+
+ if (parent != 0xff)
+ return rp1_clock_set_parent(hw, parent);
+
+ return 0;
+}
+
+static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff);
+}
+
+static unsigned long calc_core_pll_rate(struct clk_hw *pll_hw,
+ unsigned long target_rate,
+ int *pdiv_prim, int *pdiv_clk)
+{
+ static const int prim_divs[] = {
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16,
+ 18, 20, 21, 24, 25, 28, 30, 35, 36, 42, 49,
+ };
+ const unsigned long xosc_rate = clk_hw_get_rate(clk_xosc);
+ const unsigned long core_min = xosc_rate * 16;
+ const unsigned long core_max = 2400000000;
+ int best_div_prim = 1, best_div_clk = 1;
+ unsigned long best_rate = core_max + 1;
+ unsigned long core_rate = 0;
+ int div_int, div_frac;
+ u64 div;
+ int i;
+
+ /* Given the target rate, choose a set of divisors/multipliers */
+ for (i = 0; i < ARRAY_SIZE(prim_divs); i++) {
+ int div_prim = prim_divs[i];
+ int div_clk;
+
+ for (div_clk = 1; div_clk <= 256; div_clk++) {
+ core_rate = target_rate * div_clk * div_prim;
+ if (core_rate >= core_min) {
+ if (core_rate < best_rate) {
+ best_rate = core_rate;
+ best_div_prim = div_prim;
+ best_div_clk = div_clk;
+ }
+ break;
+ }
+ }
+ }
+
+ if (best_rate < core_max) {
+ div = ((best_rate << 24) + xosc_rate / 2) / xosc_rate;
+ div_int = div >> 24;
+ div_frac = div % (1 << 24);
+ core_rate = (xosc_rate * ((div_int << 24) + div_frac) + (1 << 23)) >> 24;
+ } else {
+ core_rate = 0;
+ }
+
+ if (pdiv_prim)
+ *pdiv_prim = best_div_prim;
+ if (pdiv_clk)
+ *pdiv_clk = best_div_clk;
+
+ return core_rate;
+}
+
+static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
+ int parent_idx,
+ unsigned long rate,
+ unsigned long *prate,
+ unsigned long *calc_rate)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+ const struct rp1_clock_data *data = clock->data;
+ struct clk_hw *clk_audio_hw = &clk_audio->hw;
+ struct clk_hw *clk_i2s_hw = &clk_i2s->hw;
+ struct clk_hw *parent;
+ u32 div;
+ u64 tmp;
+
+ parent = clk_hw_get_parent_by_index(hw, parent_idx);
+
+ if (hw == clk_i2s_hw && clk_i2s->cached_rate == rate && parent == clk_audio_hw) {
+ *prate = clk_audio->cached_rate;
+ *calc_rate = rate;
+ return;
+ }
+
+ if (hw == clk_i2s_hw && parent == clk_audio_hw) {
+ unsigned long core_rate, audio_rate, i2s_rate;
+ int div_prim, div_clk;
+
+ core_rate = calc_core_pll_rate(parent, rate, &div_prim, &div_clk);
+ audio_rate = DIV_ROUND_CLOSEST(core_rate, div_prim);
+ i2s_rate = DIV_ROUND_CLOSEST(audio_rate, div_clk);
+ clk_audio_core->cached_rate = core_rate;
+ clk_audio->cached_rate = audio_rate;
+ clk_i2s->cached_rate = i2s_rate;
+ *prate = audio_rate;
+ *calc_rate = i2s_rate;
+ return;
+ }
+
+ *prate = clk_hw_get_rate(parent);
+ div = rp1_clock_choose_div(rate, *prate, data);
+
+ if (!div) {
+ *calc_rate = 0;
+ return;
+ }
+
+ /* Recalculate to account for rounding errors */
+ tmp = (u64)*prate << CLK_DIV_FRAC_BITS;
+ tmp = div_u64(tmp, div);
+
+ /*
+ * Prevent overclocks - if all parent choices result in
+ * a downstream clock in excess of the maximum, then the
+ * call to set the clock will fail.
+ */
+ if (tmp > data->max_freq)
+ *calc_rate = 0;
+ else
+ *calc_rate = tmp;
+}
+
+static int rp1_clock_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_hw *parent, *best_parent = NULL;
+ unsigned long best_rate = 0;
+ unsigned long best_prate = 0;
+ unsigned long best_rate_diff = ULONG_MAX;
+ unsigned long prate, calc_rate;
+ size_t i;
+
+ /*
+ * If the NO_REPARENT flag is set, try to use existing parent.
+ */
+ if ((clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)) {
+ i = rp1_clock_get_parent(hw);
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (parent) {
+ rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate,
+ &calc_rate);
+ if (calc_rate > 0) {
+ req->best_parent_hw = parent;
+ req->best_parent_rate = prate;
+ req->rate = calc_rate;
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * Select parent clock that results in the closest rate (lower or
+ * higher)
+ */
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (!parent)
+ continue;
+
+ rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate,
+ &calc_rate);
+
+ if (abs_diff(calc_rate, req->rate) < best_rate_diff) {
+ best_parent = parent;
+ best_prate = prate;
+ best_rate = calc_rate;
+ best_rate_diff = abs_diff(calc_rate, req->rate);
+
+ if (best_rate_diff == 0)
+ break;
+ }
+ }
+
+ if (best_rate == 0)
+ return -EINVAL;
+
+ req->best_parent_hw = best_parent;
+ req->best_parent_rate = best_prate;
+ req->rate = best_rate;
+
+ return 0;
+}
+
+static int rp1_varsrc_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+
+ /*
+ * "varsrc" exists purely to let clock dividers know the frequency
+ * of an externally-managed clock source (such as MIPI DSI byte-clock)
+ * which may change at run-time as a side-effect of some other driver.
+ */
+ clock->cached_rate = rate;
+ return 0;
+}
+
+static unsigned long rp1_varsrc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rp1_clk_desc *clock = container_of(hw, struct rp1_clk_desc, hw);
+
+ return clock->cached_rate;
+}
+
+static int rp1_varsrc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ return 0;
+}
+
+static const struct clk_ops rp1_pll_core_ops = {
+ .is_prepared = rp1_pll_core_is_on,
+ .prepare = rp1_pll_core_on,
+ .unprepare = rp1_pll_core_off,
+ .set_rate = rp1_pll_core_set_rate,
+ .recalc_rate = rp1_pll_core_recalc_rate,
+ .determine_rate = rp1_pll_core_determine_rate,
+};
+
+static const struct clk_ops rp1_pll_ops = {
+ .set_rate = rp1_pll_set_rate,
+ .recalc_rate = rp1_pll_recalc_rate,
+ .determine_rate = rp1_pll_determine_rate,
+};
+
+static const struct clk_ops rp1_pll_ph_ops = {
+ .is_prepared = rp1_pll_ph_is_on,
+ .prepare = rp1_pll_ph_on,
+ .unprepare = rp1_pll_ph_off,
+ .recalc_rate = rp1_pll_ph_recalc_rate,
+ .determine_rate = rp1_pll_ph_determine_rate,
+};
+
+static const struct clk_ops rp1_pll_divider_ops = {
+ .is_prepared = rp1_pll_divider_is_on,
+ .prepare = rp1_pll_divider_on,
+ .unprepare = rp1_pll_divider_off,
+ .set_rate = rp1_pll_divider_set_rate,
+ .recalc_rate = rp1_pll_divider_recalc_rate,
+ .determine_rate = rp1_pll_divider_determine_rate,
+};
+
+static const struct clk_ops rp1_clk_ops = {
+ .is_prepared = rp1_clock_is_on,
+ .prepare = rp1_clock_on,
+ .unprepare = rp1_clock_off,
+ .recalc_rate = rp1_clock_recalc_rate,
+ .get_parent = rp1_clock_get_parent,
+ .set_parent = rp1_clock_set_parent,
+ .set_rate_and_parent = rp1_clock_set_rate_and_parent,
+ .set_rate = rp1_clock_set_rate,
+ .determine_rate = rp1_clock_determine_rate,
+};
+
+static const struct clk_ops rp1_varsrc_ops = {
+ .set_rate = rp1_varsrc_set_rate,
+ .recalc_rate = rp1_varsrc_recalc_rate,
+ .determine_rate = rp1_varsrc_determine_rate,
+};
+
+static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman,
+ struct rp1_clk_desc *desc)
+{
+ int ret;
+
+ desc->clockman = clockman;
+
+ ret = devm_clk_hw_register(clockman->dev, &desc->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &desc->hw;
+}
+
+static struct clk_hw *rp1_register_pll_divider(struct rp1_clockman *clockman,
+ struct rp1_clk_desc *desc)
+{
+ const struct rp1_pll_data *divider_data = desc->data;
+ int ret;
+
+ desc->div.reg = clockman->regs + divider_data->ctrl_reg;
+ desc->div.shift = __ffs(PLL_SEC_DIV_MASK);
+ desc->div.width = __ffs(~(PLL_SEC_DIV_MASK >> desc->div.shift));
+ desc->div.flags = CLK_DIVIDER_ROUND_CLOSEST;
+ desc->div.lock = &clockman->regs_lock;
+ desc->div.hw.init = desc->hw.init;
+ desc->div.table = pll_sec_div_table;
+
+ desc->clockman = clockman;
+
+ ret = devm_clk_hw_register(clockman->dev, &desc->div.hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &desc->div.hw;
+}
+
+static struct clk_hw *rp1_register_clock(struct rp1_clockman *clockman,
+ struct rp1_clk_desc *desc)
+{
+ const struct rp1_clock_data *clock_data = desc->data;
+ int ret;
+
+ if (WARN_ON_ONCE(MAX_CLK_PARENTS <
+ clock_data->num_std_parents + clock_data->num_aux_parents))
+ return ERR_PTR(-EINVAL);
+
+ /* There must be a gap for the AUX selector */
+ if (WARN_ON_ONCE(clock_data->num_std_parents > AUX_SEL &&
+ desc->hw.init->parent_data[AUX_SEL].index != -1))
+ return ERR_PTR(-EINVAL);
+
+ desc->clockman = clockman;
+
+ ret = devm_clk_hw_register(clockman->dev, &desc->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &desc->hw;
+}
+
+/* Assignment helper macros for different clock types. */
+#define _REGISTER(f, ...) { .clk_register = f, __VA_ARGS__ }
+
+#define CLK_DATA(type, ...) .data = &(struct type) { __VA_ARGS__ }
+
+#define REGISTER_PLL(...) _REGISTER(&rp1_register_pll, \
+ __VA_ARGS__)
+
+#define REGISTER_PLL_DIV(...) _REGISTER(&rp1_register_pll_divider, \
+ __VA_ARGS__)
+
+#define REGISTER_CLK(...) _REGISTER(&rp1_register_clock, \
+ __VA_ARGS__)
+
+static struct rp1_clk_desc pll_sys_core_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_sys_core",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_pll_core_ops,
+ CLK_IS_CRITICAL
+ ),
+ CLK_DATA(rp1_pll_core_data,
+ .cs_reg = PLL_SYS_CS,
+ .pwr_reg = PLL_SYS_PWR,
+ .fbdiv_int_reg = PLL_SYS_FBDIV_INT,
+ .fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC,
+ )
+);
+
+static struct rp1_clk_desc pll_audio_core_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_audio_core",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_pll_core_ops,
+ CLK_IS_CRITICAL
+ ),
+ CLK_DATA(rp1_pll_core_data,
+ .cs_reg = PLL_AUDIO_CS,
+ .pwr_reg = PLL_AUDIO_PWR,
+ .fbdiv_int_reg = PLL_AUDIO_FBDIV_INT,
+ .fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC,
+ )
+);
+
+static struct rp1_clk_desc pll_video_core_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_video_core",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_pll_core_ops,
+ CLK_IS_CRITICAL
+ ),
+ CLK_DATA(rp1_pll_core_data,
+ .cs_reg = PLL_VIDEO_CS,
+ .pwr_reg = PLL_VIDEO_PWR,
+ .fbdiv_int_reg = PLL_VIDEO_FBDIV_INT,
+ .fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC,
+ )
+);
+
+static struct rp1_clk_desc pll_sys_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_sys",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_sys_core_desc.hw }
+ },
+ &rp1_pll_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_SYS_PRIM,
+ .fc0_src = FC_NUM(0, 2),
+ )
+);
+
+static struct rp1_clk_desc pll_audio_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_audio",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_audio_core_desc.hw }
+ },
+ &rp1_pll_ops,
+ CLK_SET_RATE_PARENT
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_AUDIO_PRIM,
+ .fc0_src = FC_NUM(4, 2),
+ )
+);
+
+static struct rp1_clk_desc pll_video_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_video",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_video_core_desc.hw }
+ },
+ &rp1_pll_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_VIDEO_PRIM,
+ .fc0_src = FC_NUM(3, 2),
+ )
+);
+
+static struct rp1_clk_desc pll_sys_sec_desc = REGISTER_PLL_DIV(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_sys_sec",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_sys_core_desc.hw }
+ },
+ &rp1_pll_divider_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_SYS_SEC,
+ .fc0_src = FC_NUM(2, 2),
+ )
+);
+
+static struct rp1_clk_desc pll_video_sec_desc = REGISTER_PLL_DIV(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_video_sec",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_video_core_desc.hw }
+ },
+ &rp1_pll_divider_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_VIDEO_SEC,
+ .fc0_src = FC_NUM(5, 3),
+ )
+);
+
+static const struct clk_parent_data clk_eth_tsu_parents[] = {
+ { .index = 0 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_eth_tsu_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_eth_tsu",
+ clk_eth_tsu_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 8,
+ .ctrl_reg = CLK_ETH_TSU_CTRL,
+ .div_int_reg = CLK_ETH_TSU_DIV_INT,
+ .sel_reg = CLK_ETH_TSU_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(5, 7),
+ )
+);
+
+static const struct clk_parent_data clk_eth_parents[] = {
+ { .hw = &pll_sys_sec_desc.div.hw },
+ { .hw = &pll_sys_desc.hw },
+ { .hw = &pll_video_sec_desc.hw },
+};
+
+static struct rp1_clk_desc clk_eth_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_eth",
+ clk_eth_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 3,
+ .ctrl_reg = CLK_ETH_CTRL,
+ .div_int_reg = CLK_ETH_DIV_INT,
+ .sel_reg = CLK_ETH_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 125 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(4, 6),
+ )
+);
+
+static const struct clk_parent_data clk_sys_parents[] = {
+ { .index = 0 },
+ { .index = -1 },
+ { .hw = &pll_sys_desc.hw },
+};
+
+static struct rp1_clk_desc clk_sys_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_sys",
+ clk_sys_parents,
+ &rp1_clk_ops,
+ CLK_IS_CRITICAL
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 3,
+ .num_aux_parents = 0,
+ .ctrl_reg = CLK_SYS_CTRL,
+ .div_int_reg = CLK_SYS_DIV_INT,
+ .sel_reg = CLK_SYS_SEL,
+ .div_int_max = DIV_INT_24BIT_MAX,
+ .max_freq = 200 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(0, 4),
+ .clk_src_mask = 0x3,
+ )
+);
+
+static struct rp1_clk_desc pll_sys_pri_ph_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_sys_pri_ph",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_sys_desc.hw }
+ },
+ &rp1_pll_ph_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_ph_data,
+ .ph_reg = PLL_SYS_PRIM,
+ .fixed_divider = 2,
+ .phase = RP1_PLL_PHASE_0,
+ .fc0_src = FC_NUM(1, 2),
+ )
+);
+
+static struct rp1_clk_desc pll_audio_pri_ph_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_audio_pri_ph",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_audio_desc.hw }
+ },
+ &rp1_pll_ph_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_ph_data,
+ .ph_reg = PLL_AUDIO_PRIM,
+ .fixed_divider = 2,
+ .phase = RP1_PLL_PHASE_0,
+ .fc0_src = FC_NUM(5, 1),
+ )
+);
+
+static struct rp1_clk_desc pll_video_pri_ph_desc = REGISTER_PLL(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_video_pri_ph",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_video_desc.hw }
+ },
+ &rp1_pll_ph_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_ph_data,
+ .ph_reg = PLL_VIDEO_PRIM,
+ .fixed_divider = 2,
+ .phase = RP1_PLL_PHASE_0,
+ .fc0_src = FC_NUM(4, 3),
+ )
+);
+
+static struct rp1_clk_desc pll_audio_sec_desc = REGISTER_PLL_DIV(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_audio_sec",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_audio_core_desc.hw }
+ },
+ &rp1_pll_divider_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_AUDIO_SEC,
+ .fc0_src = FC_NUM(6, 2),
+ )
+);
+
+static struct rp1_clk_desc pll_audio_tern_desc = REGISTER_PLL_DIV(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "pll_audio_tern",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_audio_core_desc.hw }
+ },
+ &rp1_pll_divider_ops,
+ 0
+ ),
+ CLK_DATA(rp1_pll_data,
+ .ctrl_reg = PLL_AUDIO_TERN,
+ .fc0_src = FC_NUM(6, 2),
+ )
+);
+
+static struct rp1_clk_desc clk_slow_sys_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_slow_sys",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_clk_ops,
+ CLK_IS_CRITICAL
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 1,
+ .num_aux_parents = 0,
+ .ctrl_reg = CLK_SLOW_SYS_CTRL,
+ .div_int_reg = CLK_SLOW_SYS_DIV_INT,
+ .sel_reg = CLK_SLOW_SYS_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(1, 4),
+ .clk_src_mask = 0x1,
+ )
+);
+
+static const struct clk_parent_data clk_dma_parents[] = {
+ { .hw = &pll_sys_pri_ph_desc.hw },
+ { .hw = &pll_video_desc.hw },
+ { .index = 0 },
+};
+
+static struct rp1_clk_desc clk_dma_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_dma",
+ clk_dma_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 3,
+ .ctrl_reg = CLK_DMA_CTRL,
+ .div_int_reg = CLK_DMA_DIV_INT,
+ .sel_reg = CLK_DMA_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(2, 2),
+ )
+);
+
+static const struct clk_parent_data clk_uart_parents[] = {
+ { .hw = &pll_sys_pri_ph_desc.hw },
+ { .hw = &pll_video_desc.hw },
+ { .index = 0 },
+};
+
+static struct rp1_clk_desc clk_uart_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_uart",
+ clk_uart_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 3,
+ .ctrl_reg = CLK_UART_CTRL,
+ .div_int_reg = CLK_UART_DIV_INT,
+ .sel_reg = CLK_UART_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(6, 7),
+ )
+);
+
+static const struct clk_parent_data clk_pwm0_parents[] = {
+ { .index = -1 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .index = 0 },
+};
+
+static struct rp1_clk_desc clk_pwm0_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_pwm0",
+ clk_pwm0_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 3,
+ .ctrl_reg = CLK_PWM0_CTRL,
+ .div_int_reg = CLK_PWM0_DIV_INT,
+ .div_frac_reg = CLK_PWM0_DIV_FRAC,
+ .sel_reg = CLK_PWM0_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 76800 * HZ_PER_KHZ,
+ .fc0_src = FC_NUM(0, 5),
+ )
+);
+
+static const struct clk_parent_data clk_pwm1_parents[] = {
+ { .index = -1 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .index = 0 },
+};
+
+static struct rp1_clk_desc clk_pwm1_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_pwm1",
+ clk_pwm1_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 3,
+ .ctrl_reg = CLK_PWM1_CTRL,
+ .div_int_reg = CLK_PWM1_DIV_INT,
+ .div_frac_reg = CLK_PWM1_DIV_FRAC,
+ .sel_reg = CLK_PWM1_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 76800 * HZ_PER_KHZ,
+ .fc0_src = FC_NUM(1, 5),
+ )
+);
+
+static const struct clk_parent_data clk_audio_in_parents[] = {
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .index = 0 },
+};
+
+static struct rp1_clk_desc clk_audio_in_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_audio_in",
+ clk_audio_in_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 5,
+ .ctrl_reg = CLK_AUDIO_IN_CTRL,
+ .div_int_reg = CLK_AUDIO_IN_DIV_INT,
+ .sel_reg = CLK_AUDIO_IN_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 76800 * HZ_PER_KHZ,
+ .fc0_src = FC_NUM(2, 5),
+ )
+);
+
+static const struct clk_parent_data clk_audio_out_parents[] = {
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .index = 0 },
+};
+
+static struct rp1_clk_desc clk_audio_out_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_audio_out",
+ clk_audio_out_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 4,
+ .ctrl_reg = CLK_AUDIO_OUT_CTRL,
+ .div_int_reg = CLK_AUDIO_OUT_DIV_INT,
+ .sel_reg = CLK_AUDIO_OUT_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 153600 * HZ_PER_KHZ,
+ .fc0_src = FC_NUM(3, 5),
+ )
+);
+
+static const struct clk_parent_data clk_i2s_parents[] = {
+ { .index = 0 },
+ { .hw = &pll_audio_desc.hw },
+ { .hw = &pll_audio_sec_desc.hw },
+};
+
+static struct rp1_clk_desc clk_i2s_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_i2s",
+ clk_i2s_parents,
+ &rp1_clk_ops,
+ CLK_SET_RATE_PARENT
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 3,
+ .ctrl_reg = CLK_I2S_CTRL,
+ .div_int_reg = CLK_I2S_DIV_INT,
+ .sel_reg = CLK_I2S_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(4, 4),
+ )
+);
+
+static struct rp1_clk_desc clk_mipi0_cfg_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_mipi0_cfg",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 1,
+ .ctrl_reg = CLK_MIPI0_CFG_CTRL,
+ .div_int_reg = CLK_MIPI0_CFG_DIV_INT,
+ .sel_reg = CLK_MIPI0_CFG_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(4, 5),
+ )
+);
+
+static struct rp1_clk_desc clk_mipi1_cfg_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_mipi1_cfg",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 1,
+ .ctrl_reg = CLK_MIPI1_CFG_CTRL,
+ .div_int_reg = CLK_MIPI1_CFG_DIV_INT,
+ .sel_reg = CLK_MIPI1_CFG_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(5, 6),
+ .clk_src_mask = 0x1,
+ )
+);
+
+static struct rp1_clk_desc clk_adc_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_adc",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 1,
+ .ctrl_reg = CLK_ADC_CTRL,
+ .div_int_reg = CLK_ADC_DIV_INT,
+ .sel_reg = CLK_ADC_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(5, 5),
+ )
+);
+
+static struct rp1_clk_desc clk_sdio_timer_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_sdio_timer",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 1,
+ .ctrl_reg = CLK_SDIO_TIMER_CTRL,
+ .div_int_reg = CLK_SDIO_TIMER_DIV_INT,
+ .sel_reg = CLK_SDIO_TIMER_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 50 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(3, 4),
+ )
+);
+
+static struct rp1_clk_desc clk_sdio_alt_src_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_sdio_alt_src",
+ (const struct clk_parent_data[]) {
+ { .hw = &pll_sys_desc.hw }
+ },
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 1,
+ .ctrl_reg = CLK_SDIO_ALT_SRC_CTRL,
+ .div_int_reg = CLK_SDIO_ALT_SRC_DIV_INT,
+ .sel_reg = CLK_SDIO_ALT_SRC_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 200 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(5, 4),
+ )
+);
+
+static const struct clk_parent_data clk_dpi_parents[] = {
+ { .hw = &pll_sys_desc.hw },
+ { .hw = &pll_video_sec_desc.hw },
+ { .hw = &pll_video_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_dpi_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_dpi",
+ clk_dpi_parents,
+ &rp1_clk_ops,
+ CLK_SET_RATE_NO_REPARENT /* Let DPI driver set parent */
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 8,
+ .ctrl_reg = VIDEO_CLK_DPI_CTRL,
+ .div_int_reg = VIDEO_CLK_DPI_DIV_INT,
+ .sel_reg = VIDEO_CLK_DPI_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 200 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(1, 6),
+ )
+);
+
+static const struct clk_parent_data clk_gp0_parents[] = {
+ { .index = 0 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_sys_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &clk_i2s_desc.hw },
+ { .hw = &clk_adc_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &clk_sys_desc.hw },
+};
+
+static struct rp1_clk_desc clk_gp0_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_gp0",
+ clk_gp0_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 16,
+ .oe_mask = BIT(0),
+ .ctrl_reg = CLK_GP0_CTRL,
+ .div_int_reg = CLK_GP0_DIV_INT,
+ .div_frac_reg = CLK_GP0_DIV_FRAC,
+ .sel_reg = CLK_GP0_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(0, 1),
+ )
+);
+
+static const struct clk_parent_data clk_gp1_parents[] = {
+ { .hw = &clk_sdio_timer_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_sys_pri_ph_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &clk_adc_desc.hw },
+ { .hw = &clk_dpi_desc.hw },
+ { .hw = &clk_pwm0_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_gp1_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_gp1",
+ clk_gp1_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 16,
+ .oe_mask = BIT(1),
+ .ctrl_reg = CLK_GP1_CTRL,
+ .div_int_reg = CLK_GP1_DIV_INT,
+ .div_frac_reg = CLK_GP1_DIV_FRAC,
+ .sel_reg = CLK_GP1_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(1, 1),
+ )
+);
+
+static struct rp1_clk_desc clksrc_mipi0_dsi_byteclk_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clksrc_mipi0_dsi_byteclk",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_varsrc_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 1,
+ .num_aux_parents = 0,
+ )
+);
+
+static struct rp1_clk_desc clksrc_mipi1_dsi_byteclk_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clksrc_mipi1_dsi_byteclk",
+ (const struct clk_parent_data[]) { { .index = 0 } },
+ &rp1_varsrc_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 1,
+ .num_aux_parents = 0,
+ )
+);
+
+static const struct clk_parent_data clk_mipi0_dpi_parents[] = {
+ { .hw = &pll_sys_desc.hw },
+ { .hw = &pll_video_sec_desc.hw },
+ { .hw = &pll_video_desc.hw },
+ { .hw = &clksrc_mipi0_dsi_byteclk_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_mipi0_dpi_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_mipi0_dpi",
+ clk_mipi0_dpi_parents,
+ &rp1_clk_ops,
+ CLK_SET_RATE_NO_REPARENT /* Let DSI driver set parent */
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 8,
+ .ctrl_reg = VIDEO_CLK_MIPI0_DPI_CTRL,
+ .div_int_reg = VIDEO_CLK_MIPI0_DPI_DIV_INT,
+ .div_frac_reg = VIDEO_CLK_MIPI0_DPI_DIV_FRAC,
+ .sel_reg = VIDEO_CLK_MIPI0_DPI_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 200 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(2, 6),
+ )
+);
+
+static const struct clk_parent_data clk_mipi1_dpi_parents[] = {
+ { .hw = &pll_sys_desc.hw },
+ { .hw = &pll_video_sec_desc.hw },
+ { .hw = &pll_video_desc.hw },
+ { .hw = &clksrc_mipi1_dsi_byteclk_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_mipi1_dpi_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_mipi1_dpi",
+ clk_mipi1_dpi_parents,
+ &rp1_clk_ops,
+ CLK_SET_RATE_NO_REPARENT /* Let DSI driver set parent */
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 8,
+ .ctrl_reg = VIDEO_CLK_MIPI1_DPI_CTRL,
+ .div_int_reg = VIDEO_CLK_MIPI1_DPI_DIV_INT,
+ .div_frac_reg = VIDEO_CLK_MIPI1_DPI_DIV_FRAC,
+ .sel_reg = VIDEO_CLK_MIPI1_DPI_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 200 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(3, 6),
+ )
+);
+
+static const struct clk_parent_data clk_gp2_parents[] = {
+ { .hw = &clk_sdio_alt_src_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_sys_sec_desc.hw },
+ { .index = -1 },
+ { .hw = &pll_video_desc.hw },
+ { .hw = &clk_audio_in_desc.hw },
+ { .hw = &clk_dpi_desc.hw },
+ { .hw = &clk_pwm0_desc.hw },
+ { .hw = &clk_pwm1_desc.hw },
+ { .hw = &clk_mipi0_dpi_desc.hw },
+ { .hw = &clk_mipi1_cfg_desc.hw },
+ { .hw = &clk_sys_desc.hw },
+};
+
+static struct rp1_clk_desc clk_gp2_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_gp2",
+ clk_gp2_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 16,
+ .oe_mask = BIT(2),
+ .ctrl_reg = CLK_GP2_CTRL,
+ .div_int_reg = CLK_GP2_DIV_INT,
+ .div_frac_reg = CLK_GP2_DIV_FRAC,
+ .sel_reg = CLK_GP2_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(2, 1),
+ )
+);
+
+static const struct clk_parent_data clk_gp3_parents[] = {
+ { .index = 0 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_video_pri_ph_desc.hw },
+ { .hw = &clk_audio_out_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &clk_mipi1_dpi_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_gp3_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_gp3",
+ clk_gp3_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 16,
+ .oe_mask = BIT(3),
+ .ctrl_reg = CLK_GP3_CTRL,
+ .div_int_reg = CLK_GP3_DIV_INT,
+ .div_frac_reg = CLK_GP3_DIV_FRAC,
+ .sel_reg = CLK_GP3_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(3, 1),
+ )
+);
+
+static const struct clk_parent_data clk_gp4_parents[] = {
+ { .index = 0 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &clk_mipi0_cfg_desc.hw },
+ { .hw = &clk_uart_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &clk_sys_desc.hw },
+};
+
+static struct rp1_clk_desc clk_gp4_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_gp4",
+ clk_gp4_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 16,
+ .oe_mask = BIT(4),
+ .ctrl_reg = CLK_GP4_CTRL,
+ .div_int_reg = CLK_GP4_DIV_INT,
+ .div_frac_reg = CLK_GP4_DIV_FRAC,
+ .sel_reg = CLK_GP4_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(4, 1),
+ )
+);
+
+static const struct clk_parent_data clk_vec_parents[] = {
+ { .hw = &pll_sys_pri_ph_desc.hw },
+ { .hw = &pll_video_sec_desc.hw },
+ { .hw = &pll_video_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_vec_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_vec",
+ clk_vec_parents,
+ &rp1_clk_ops,
+ CLK_SET_RATE_NO_REPARENT /* Let VEC driver set parent */
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 8,
+ .ctrl_reg = VIDEO_CLK_VEC_CTRL,
+ .div_int_reg = VIDEO_CLK_VEC_DIV_INT,
+ .sel_reg = VIDEO_CLK_VEC_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .max_freq = 108 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(0, 6),
+ )
+);
+
+static const struct clk_parent_data clk_gp5_parents[] = {
+ { .index = 0 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .hw = &pll_video_sec_desc.hw },
+ { .hw = &clk_eth_tsu_desc.hw },
+ { .index = -1 },
+ { .hw = &clk_vec_desc.hw },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct rp1_clk_desc clk_gp5_desc = REGISTER_CLK(
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(
+ "clk_gp5",
+ clk_gp5_parents,
+ &rp1_clk_ops,
+ 0
+ ),
+ CLK_DATA(rp1_clock_data,
+ .num_std_parents = 0,
+ .num_aux_parents = 16,
+ .oe_mask = BIT(5),
+ .ctrl_reg = CLK_GP5_CTRL,
+ .div_int_reg = CLK_GP5_DIV_INT,
+ .div_frac_reg = CLK_GP5_DIV_FRAC,
+ .sel_reg = CLK_GP5_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .max_freq = 100 * HZ_PER_MHZ,
+ .fc0_src = FC_NUM(5, 1),
+ )
+);
+
+static struct rp1_clk_desc *const clk_desc_array[] = {
+ [RP1_PLL_SYS_CORE] = &pll_sys_core_desc,
+ [RP1_PLL_AUDIO_CORE] = &pll_audio_core_desc,
+ [RP1_PLL_VIDEO_CORE] = &pll_video_core_desc,
+ [RP1_PLL_SYS] = &pll_sys_desc,
+ [RP1_CLK_ETH_TSU] = &clk_eth_tsu_desc,
+ [RP1_CLK_ETH] = &clk_eth_desc,
+ [RP1_CLK_SYS] = &clk_sys_desc,
+ [RP1_PLL_SYS_PRI_PH] = &pll_sys_pri_ph_desc,
+ [RP1_PLL_SYS_SEC] = &pll_sys_sec_desc,
+ [RP1_PLL_AUDIO] = &pll_audio_desc,
+ [RP1_PLL_VIDEO] = &pll_video_desc,
+ [RP1_PLL_AUDIO_PRI_PH] = &pll_audio_pri_ph_desc,
+ [RP1_PLL_VIDEO_PRI_PH] = &pll_video_pri_ph_desc,
+ [RP1_PLL_AUDIO_SEC] = &pll_audio_sec_desc,
+ [RP1_PLL_VIDEO_SEC] = &pll_video_sec_desc,
+ [RP1_PLL_AUDIO_TERN] = &pll_audio_tern_desc,
+ [RP1_CLK_SLOW_SYS] = &clk_slow_sys_desc,
+ [RP1_CLK_DMA] = &clk_dma_desc,
+ [RP1_CLK_UART] = &clk_uart_desc,
+ [RP1_CLK_PWM0] = &clk_pwm0_desc,
+ [RP1_CLK_PWM1] = &clk_pwm1_desc,
+ [RP1_CLK_AUDIO_IN] = &clk_audio_in_desc,
+ [RP1_CLK_AUDIO_OUT] = &clk_audio_out_desc,
+ [RP1_CLK_I2S] = &clk_i2s_desc,
+ [RP1_CLK_MIPI0_CFG] = &clk_mipi0_cfg_desc,
+ [RP1_CLK_MIPI1_CFG] = &clk_mipi1_cfg_desc,
+ [RP1_CLK_ADC] = &clk_adc_desc,
+ [RP1_CLK_SDIO_TIMER] = &clk_sdio_timer_desc,
+ [RP1_CLK_SDIO_ALT_SRC] = &clk_sdio_alt_src_desc,
+ [RP1_CLK_GP0] = &clk_gp0_desc,
+ [RP1_CLK_GP1] = &clk_gp1_desc,
+ [RP1_CLK_GP2] = &clk_gp2_desc,
+ [RP1_CLK_GP3] = &clk_gp3_desc,
+ [RP1_CLK_GP4] = &clk_gp4_desc,
+ [RP1_CLK_GP5] = &clk_gp5_desc,
+ [RP1_CLK_VEC] = &clk_vec_desc,
+ [RP1_CLK_DPI] = &clk_dpi_desc,
+ [RP1_CLK_MIPI0_DPI] = &clk_mipi0_dpi_desc,
+ [RP1_CLK_MIPI1_DPI] = &clk_mipi1_dpi_desc,
+ [RP1_CLK_MIPI0_DSI_BYTECLOCK] = &clksrc_mipi0_dsi_byteclk_desc,
+ [RP1_CLK_MIPI1_DSI_BYTECLOCK] = &clksrc_mipi1_dsi_byteclk_desc,
+};
+
+static const struct regmap_range rp1_reg_ranges[] = {
+ regmap_reg_range(PLL_SYS_CS, PLL_SYS_SEC),
+ regmap_reg_range(PLL_AUDIO_CS, PLL_AUDIO_TERN),
+ regmap_reg_range(PLL_VIDEO_CS, PLL_VIDEO_SEC),
+ regmap_reg_range(GPCLK_OE_CTRL, GPCLK_OE_CTRL),
+ regmap_reg_range(CLK_SYS_CTRL, CLK_SYS_DIV_INT),
+ regmap_reg_range(CLK_SYS_SEL, CLK_SYS_SEL),
+ regmap_reg_range(CLK_SLOW_SYS_CTRL, CLK_SLOW_SYS_DIV_INT),
+ regmap_reg_range(CLK_SLOW_SYS_SEL, CLK_SLOW_SYS_SEL),
+ regmap_reg_range(CLK_DMA_CTRL, CLK_DMA_DIV_INT),
+ regmap_reg_range(CLK_DMA_SEL, CLK_DMA_SEL),
+ regmap_reg_range(CLK_UART_CTRL, CLK_UART_DIV_INT),
+ regmap_reg_range(CLK_UART_SEL, CLK_UART_SEL),
+ regmap_reg_range(CLK_ETH_CTRL, CLK_ETH_DIV_INT),
+ regmap_reg_range(CLK_ETH_SEL, CLK_ETH_SEL),
+ regmap_reg_range(CLK_PWM0_CTRL, CLK_PWM0_SEL),
+ regmap_reg_range(CLK_PWM1_CTRL, CLK_PWM1_SEL),
+ regmap_reg_range(CLK_AUDIO_IN_CTRL, CLK_AUDIO_IN_DIV_INT),
+ regmap_reg_range(CLK_AUDIO_IN_SEL, CLK_AUDIO_IN_SEL),
+ regmap_reg_range(CLK_AUDIO_OUT_CTRL, CLK_AUDIO_OUT_DIV_INT),
+ regmap_reg_range(CLK_AUDIO_OUT_SEL, CLK_AUDIO_OUT_SEL),
+ regmap_reg_range(CLK_I2S_CTRL, CLK_I2S_DIV_INT),
+ regmap_reg_range(CLK_I2S_SEL, CLK_I2S_SEL),
+ regmap_reg_range(CLK_MIPI0_CFG_CTRL, CLK_MIPI0_CFG_DIV_INT),
+ regmap_reg_range(CLK_MIPI0_CFG_SEL, CLK_MIPI0_CFG_SEL),
+ regmap_reg_range(CLK_MIPI1_CFG_CTRL, CLK_MIPI1_CFG_DIV_INT),
+ regmap_reg_range(CLK_MIPI1_CFG_SEL, CLK_MIPI1_CFG_SEL),
+ regmap_reg_range(CLK_PCIE_AUX_CTRL, CLK_PCIE_AUX_DIV_INT),
+ regmap_reg_range(CLK_PCIE_AUX_SEL, CLK_PCIE_AUX_SEL),
+ regmap_reg_range(CLK_USBH0_MICROFRAME_CTRL, CLK_USBH0_MICROFRAME_DIV_INT),
+ regmap_reg_range(CLK_USBH0_MICROFRAME_SEL, CLK_USBH0_MICROFRAME_SEL),
+ regmap_reg_range(CLK_USBH1_MICROFRAME_CTRL, CLK_USBH1_MICROFRAME_DIV_INT),
+ regmap_reg_range(CLK_USBH1_MICROFRAME_SEL, CLK_USBH1_MICROFRAME_SEL),
+ regmap_reg_range(CLK_USBH0_SUSPEND_CTRL, CLK_USBH0_SUSPEND_DIV_INT),
+ regmap_reg_range(CLK_USBH0_SUSPEND_SEL, CLK_USBH0_SUSPEND_SEL),
+ regmap_reg_range(CLK_USBH1_SUSPEND_CTRL, CLK_USBH1_SUSPEND_DIV_INT),
+ regmap_reg_range(CLK_USBH1_SUSPEND_SEL, CLK_USBH1_SUSPEND_SEL),
+ regmap_reg_range(CLK_ETH_TSU_CTRL, CLK_ETH_TSU_DIV_INT),
+ regmap_reg_range(CLK_ETH_TSU_SEL, CLK_ETH_TSU_SEL),
+ regmap_reg_range(CLK_ADC_CTRL, CLK_ADC_DIV_INT),
+ regmap_reg_range(CLK_ADC_SEL, CLK_ADC_SEL),
+ regmap_reg_range(CLK_SDIO_TIMER_CTRL, CLK_SDIO_TIMER_DIV_INT),
+ regmap_reg_range(CLK_SDIO_TIMER_SEL, CLK_SDIO_TIMER_SEL),
+ regmap_reg_range(CLK_SDIO_ALT_SRC_CTRL, CLK_SDIO_ALT_SRC_DIV_INT),
+ regmap_reg_range(CLK_SDIO_ALT_SRC_SEL, CLK_SDIO_ALT_SRC_SEL),
+ regmap_reg_range(CLK_GP0_CTRL, CLK_GP0_SEL),
+ regmap_reg_range(CLK_GP1_CTRL, CLK_GP1_SEL),
+ regmap_reg_range(CLK_GP2_CTRL, CLK_GP2_SEL),
+ regmap_reg_range(CLK_GP3_CTRL, CLK_GP3_SEL),
+ regmap_reg_range(CLK_GP4_CTRL, CLK_GP4_SEL),
+ regmap_reg_range(CLK_GP5_CTRL, CLK_GP5_SEL),
+ regmap_reg_range(CLK_SYS_RESUS_CTRL, CLK_SYS_RESUS_CTRL),
+ regmap_reg_range(CLK_SLOW_SYS_RESUS_CTRL, CLK_SLOW_SYS_RESUS_CTRL),
+ regmap_reg_range(FC0_REF_KHZ, FC0_RESULT),
+ regmap_reg_range(VIDEO_CLK_VEC_CTRL, VIDEO_CLK_VEC_DIV_INT),
+ regmap_reg_range(VIDEO_CLK_VEC_SEL, VIDEO_CLK_DPI_DIV_INT),
+ regmap_reg_range(VIDEO_CLK_DPI_SEL, VIDEO_CLK_MIPI1_DPI_SEL),
+};
+
+static const struct regmap_access_table rp1_reg_table = {
+ .yes_ranges = rp1_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rp1_reg_ranges),
+};
+
+static const struct regmap_config rp1_clk_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = PLL_VIDEO_SEC,
+ .name = "rp1-clk",
+ .rd_table = &rp1_reg_table,
+ .disable_locking = true,
+};
+
+static int rp1_clk_probe(struct platform_device *pdev)
+{
+ const size_t asize = ARRAY_SIZE(clk_desc_array);
+ struct rp1_clk_desc *desc;
+ struct device *dev = &pdev->dev;
+ struct rp1_clockman *clockman;
+ struct clk_hw **hws;
+ unsigned int i;
+
+ clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize),
+ GFP_KERNEL);
+ if (!clockman)
+ return -ENOMEM;
+
+ spin_lock_init(&clockman->regs_lock);
+ clockman->dev = dev;
+
+ clockman->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(clockman->regs))
+ return PTR_ERR(clockman->regs);
+
+ clockman->regmap = devm_regmap_init_mmio(dev, clockman->regs,
+ &rp1_clk_regmap_cfg);
+ if (IS_ERR(clockman->regmap)) {
+ dev_err_probe(dev, PTR_ERR(clockman->regmap),
+ "could not init clock regmap\n");
+ return PTR_ERR(clockman->regmap);
+ }
+
+ clockman->onecell.num = asize;
+ hws = clockman->onecell.hws;
+
+ for (i = 0; i < asize; i++) {
+ desc = clk_desc_array[i];
+ if (desc && desc->clk_register && desc->data)
+ hws[i] = desc->clk_register(clockman, desc);
+ }
+
+ clk_audio_core = &pll_audio_core_desc;
+ clk_audio = &pll_audio_desc;
+ clk_i2s = &clk_i2s_desc;
+ clk_xosc = clk_hw_get_parent_by_index(&clk_i2s->hw, 0);
+
+ platform_set_drvdata(pdev, clockman);
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &clockman->onecell);
+}
+
+static const struct of_device_id rp1_clk_of_match[] = {
+ { .compatible = "raspberrypi,rp1-clocks" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rp1_clk_of_match);
+
+static struct platform_driver rp1_clk_driver = {
+ .driver = {
+ .name = "rp1-clk",
+ .of_match_table = rp1_clk_of_match,
+ },
+ .probe = rp1_clk_probe,
+};
+
+module_platform_driver(rp1_clk_driver);
+
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>");
+MODULE_DESCRIPTION("RP1 clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-rpmi.c b/drivers/clk/clk-rpmi.c
new file mode 100644
index 000000000000..921296aafa68
--- /dev/null
+++ b/drivers/clk/clk-rpmi.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RISC-V MPXY Based Clock Driver
+ *
+ * Copyright (C) 2025 Ventana Micro Systems Ltd.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/riscv-rpmi-message.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/wordpart.h>
+
+#define RPMI_CLK_DISCRETE_MAX_NUM_RATES 16
+#define RPMI_CLK_NAME_LEN 16
+
+#define to_rpmi_clk(clk) container_of(clk, struct rpmi_clk, hw)
+
+enum rpmi_clk_config {
+ RPMI_CLK_DISABLE = 0,
+ RPMI_CLK_ENABLE = 1,
+ RPMI_CLK_CONFIG_MAX_IDX
+};
+
+#define RPMI_CLK_TYPE_MASK GENMASK(1, 0)
+enum rpmi_clk_type {
+ RPMI_CLK_DISCRETE = 0,
+ RPMI_CLK_LINEAR = 1,
+ RPMI_CLK_TYPE_MAX_IDX
+};
+
+struct rpmi_clk_context {
+ struct device *dev;
+ struct mbox_chan *chan;
+ struct mbox_client client;
+ u32 max_msg_data_size;
+};
+
+/*
+ * rpmi_clk_rates represents the rates format
+ * as specified by the RPMI specification.
+ * No other data format (e.g., struct linear_range)
+ * is required to avoid to and from conversion.
+ */
+union rpmi_clk_rates {
+ u64 discrete[RPMI_CLK_DISCRETE_MAX_NUM_RATES];
+ struct {
+ u64 min;
+ u64 max;
+ u64 step;
+ } linear;
+};
+
+struct rpmi_clk {
+ struct rpmi_clk_context *context;
+ u32 id;
+ u32 num_rates;
+ u32 transition_latency;
+ enum rpmi_clk_type type;
+ union rpmi_clk_rates *rates;
+ char name[RPMI_CLK_NAME_LEN];
+ struct clk_hw hw;
+};
+
+struct rpmi_clk_rate_discrete {
+ __le32 lo;
+ __le32 hi;
+};
+
+struct rpmi_clk_rate_linear {
+ __le32 min_lo;
+ __le32 min_hi;
+ __le32 max_lo;
+ __le32 max_hi;
+ __le32 step_lo;
+ __le32 step_hi;
+};
+
+struct rpmi_get_num_clocks_rx {
+ __le32 status;
+ __le32 num_clocks;
+};
+
+struct rpmi_get_attrs_tx {
+ __le32 clkid;
+};
+
+struct rpmi_get_attrs_rx {
+ __le32 status;
+ __le32 flags;
+ __le32 num_rates;
+ __le32 transition_latency;
+ char name[RPMI_CLK_NAME_LEN];
+};
+
+struct rpmi_get_supp_rates_tx {
+ __le32 clkid;
+ __le32 clk_rate_idx;
+};
+
+struct rpmi_get_supp_rates_rx {
+ __le32 status;
+ __le32 flags;
+ __le32 remaining;
+ __le32 returned;
+ __le32 rates[];
+};
+
+struct rpmi_get_rate_tx {
+ __le32 clkid;
+};
+
+struct rpmi_get_rate_rx {
+ __le32 status;
+ __le32 lo;
+ __le32 hi;
+};
+
+struct rpmi_set_rate_tx {
+ __le32 clkid;
+ __le32 flags;
+ __le32 lo;
+ __le32 hi;
+};
+
+struct rpmi_set_rate_rx {
+ __le32 status;
+};
+
+struct rpmi_set_config_tx {
+ __le32 clkid;
+ __le32 config;
+};
+
+struct rpmi_set_config_rx {
+ __le32 status;
+};
+
+static inline u64 rpmi_clkrate_u64(u32 __hi, u32 __lo)
+{
+ return (((u64)(__hi) << 32) | (u32)(__lo));
+}
+
+static u32 rpmi_clk_get_num_clocks(struct rpmi_clk_context *context)
+{
+ struct rpmi_get_num_clocks_rx rx, *resp;
+ struct rpmi_mbox_message msg;
+ int ret;
+
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_NUM_CLOCKS,
+ NULL, 0, &rx, sizeof(rx));
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return 0;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp || resp->status)
+ return 0;
+
+ return le32_to_cpu(resp->num_clocks);
+}
+
+static int rpmi_clk_get_attrs(u32 clkid, struct rpmi_clk *rpmi_clk)
+{
+ struct rpmi_clk_context *context = rpmi_clk->context;
+ struct rpmi_mbox_message msg;
+ struct rpmi_get_attrs_tx tx;
+ struct rpmi_get_attrs_rx rx, *resp;
+ u8 format;
+ int ret;
+
+ tx.clkid = cpu_to_le32(clkid);
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_ATTRIBUTES,
+ &tx, sizeof(tx), &rx, sizeof(rx));
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return ret;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp)
+ return -EINVAL;
+ if (resp->status)
+ return rpmi_to_linux_error(le32_to_cpu(resp->status));
+
+ rpmi_clk->id = clkid;
+ rpmi_clk->num_rates = le32_to_cpu(resp->num_rates);
+ rpmi_clk->transition_latency = le32_to_cpu(resp->transition_latency);
+ strscpy(rpmi_clk->name, resp->name, RPMI_CLK_NAME_LEN);
+
+ format = le32_to_cpu(resp->flags) & RPMI_CLK_TYPE_MASK;
+ if (format >= RPMI_CLK_TYPE_MAX_IDX)
+ return -EINVAL;
+
+ rpmi_clk->type = format;
+
+ return 0;
+}
+
+static int rpmi_clk_get_supported_rates(u32 clkid, struct rpmi_clk *rpmi_clk)
+{
+ struct rpmi_clk_context *context = rpmi_clk->context;
+ struct rpmi_clk_rate_discrete *rate_discrete;
+ struct rpmi_clk_rate_linear *rate_linear;
+ struct rpmi_get_supp_rates_tx tx;
+ struct rpmi_get_supp_rates_rx *resp;
+ struct rpmi_mbox_message msg;
+ size_t clk_rate_idx;
+ int ret, rateidx, j;
+
+ tx.clkid = cpu_to_le32(clkid);
+ tx.clk_rate_idx = 0;
+
+ /*
+ * Make sure we allocate rx buffer sufficient to be accommodate all
+ * the rates sent in one RPMI message.
+ */
+ struct rpmi_get_supp_rates_rx *rx __free(kfree) =
+ kzalloc(context->max_msg_data_size, GFP_KERNEL);
+ if (!rx)
+ return -ENOMEM;
+
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_SUPPORTED_RATES,
+ &tx, sizeof(tx), rx, context->max_msg_data_size);
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return ret;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp)
+ return -EINVAL;
+ if (resp->status)
+ return rpmi_to_linux_error(le32_to_cpu(resp->status));
+ if (!le32_to_cpu(resp->returned))
+ return -EINVAL;
+
+ if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
+ rate_discrete = (struct rpmi_clk_rate_discrete *)resp->rates;
+
+ for (rateidx = 0; rateidx < le32_to_cpu(resp->returned); rateidx++) {
+ rpmi_clk->rates->discrete[rateidx] =
+ rpmi_clkrate_u64(le32_to_cpu(rate_discrete[rateidx].hi),
+ le32_to_cpu(rate_discrete[rateidx].lo));
+ }
+
+ /*
+ * Keep sending the request message until all
+ * the rates are received.
+ */
+ clk_rate_idx = 0;
+ while (le32_to_cpu(resp->remaining)) {
+ clk_rate_idx += le32_to_cpu(resp->returned);
+ tx.clk_rate_idx = cpu_to_le32(clk_rate_idx);
+
+ rpmi_mbox_init_send_with_response(&msg,
+ RPMI_CLK_SRV_GET_SUPPORTED_RATES,
+ &tx, sizeof(tx),
+ rx, context->max_msg_data_size);
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return ret;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp)
+ return -EINVAL;
+ if (resp->status)
+ return rpmi_to_linux_error(le32_to_cpu(resp->status));
+ if (!le32_to_cpu(resp->returned))
+ return -EINVAL;
+
+ for (j = 0; j < le32_to_cpu(resp->returned); j++) {
+ if (rateidx >= clk_rate_idx + le32_to_cpu(resp->returned))
+ break;
+ rpmi_clk->rates->discrete[rateidx++] =
+ rpmi_clkrate_u64(le32_to_cpu(rate_discrete[j].hi),
+ le32_to_cpu(rate_discrete[j].lo));
+ }
+ }
+ } else if (rpmi_clk->type == RPMI_CLK_LINEAR) {
+ rate_linear = (struct rpmi_clk_rate_linear *)resp->rates;
+
+ rpmi_clk->rates->linear.min = rpmi_clkrate_u64(le32_to_cpu(rate_linear->min_hi),
+ le32_to_cpu(rate_linear->min_lo));
+ rpmi_clk->rates->linear.max = rpmi_clkrate_u64(le32_to_cpu(rate_linear->max_hi),
+ le32_to_cpu(rate_linear->max_lo));
+ rpmi_clk->rates->linear.step = rpmi_clkrate_u64(le32_to_cpu(rate_linear->step_hi),
+ le32_to_cpu(rate_linear->step_lo));
+ }
+
+ return 0;
+}
+
+static unsigned long rpmi_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
+ struct rpmi_clk_context *context = rpmi_clk->context;
+ struct rpmi_mbox_message msg;
+ struct rpmi_get_rate_tx tx;
+ struct rpmi_get_rate_rx rx, *resp;
+ int ret;
+
+ tx.clkid = cpu_to_le32(rpmi_clk->id);
+
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_RATE,
+ &tx, sizeof(tx), &rx, sizeof(rx));
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return ret;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp)
+ return -EINVAL;
+ if (resp->status)
+ return rpmi_to_linux_error(le32_to_cpu(resp->status));
+
+ return rpmi_clkrate_u64(le32_to_cpu(resp->hi), le32_to_cpu(resp->lo));
+}
+
+static int rpmi_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
+ u64 fmin, fmax, ftmp;
+
+ /*
+ * Keep the requested rate if the clock format
+ * is of discrete type. Let the platform which
+ * is actually controlling the clock handle that.
+ */
+ if (rpmi_clk->type == RPMI_CLK_DISCRETE)
+ return 0;
+
+ fmin = rpmi_clk->rates->linear.min;
+ fmax = rpmi_clk->rates->linear.max;
+
+ if (req->rate <= fmin) {
+ req->rate = fmin;
+ return 0;
+ } else if (req->rate >= fmax) {
+ req->rate = fmax;
+ return 0;
+ }
+
+ ftmp = req->rate - fmin;
+ ftmp += rpmi_clk->rates->linear.step - 1;
+ do_div(ftmp, rpmi_clk->rates->linear.step);
+
+ req->rate = ftmp * rpmi_clk->rates->linear.step + fmin;
+
+ return 0;
+}
+
+static int rpmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
+ struct rpmi_clk_context *context = rpmi_clk->context;
+ struct rpmi_mbox_message msg;
+ struct rpmi_set_rate_tx tx;
+ struct rpmi_set_rate_rx rx, *resp;
+ int ret;
+
+ tx.clkid = cpu_to_le32(rpmi_clk->id);
+ tx.lo = cpu_to_le32(lower_32_bits(rate));
+ tx.hi = cpu_to_le32(upper_32_bits(rate));
+
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_RATE,
+ &tx, sizeof(tx), &rx, sizeof(rx));
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return ret;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp)
+ return -EINVAL;
+ if (resp->status)
+ return rpmi_to_linux_error(le32_to_cpu(resp->status));
+
+ return 0;
+}
+
+static int rpmi_clk_enable(struct clk_hw *hw)
+{
+ struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
+ struct rpmi_clk_context *context = rpmi_clk->context;
+ struct rpmi_mbox_message msg;
+ struct rpmi_set_config_tx tx;
+ struct rpmi_set_config_rx rx, *resp;
+ int ret;
+
+ tx.config = cpu_to_le32(RPMI_CLK_ENABLE);
+ tx.clkid = cpu_to_le32(rpmi_clk->id);
+
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_CONFIG,
+ &tx, sizeof(tx), &rx, sizeof(rx));
+
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return ret;
+
+ resp = rpmi_mbox_get_msg_response(&msg);
+ if (!resp)
+ return -EINVAL;
+ if (resp->status)
+ return rpmi_to_linux_error(le32_to_cpu(resp->status));
+
+ return 0;
+}
+
+static void rpmi_clk_disable(struct clk_hw *hw)
+{
+ struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
+ struct rpmi_clk_context *context = rpmi_clk->context;
+ struct rpmi_mbox_message msg;
+ struct rpmi_set_config_tx tx;
+ struct rpmi_set_config_rx rx;
+
+ tx.config = cpu_to_le32(RPMI_CLK_DISABLE);
+ tx.clkid = cpu_to_le32(rpmi_clk->id);
+
+ rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_CONFIG,
+ &tx, sizeof(tx), &rx, sizeof(rx));
+
+ rpmi_mbox_send_message(context->chan, &msg);
+}
+
+static const struct clk_ops rpmi_clk_ops = {
+ .recalc_rate = rpmi_clk_recalc_rate,
+ .determine_rate = rpmi_clk_determine_rate,
+ .set_rate = rpmi_clk_set_rate,
+ .prepare = rpmi_clk_enable,
+ .unprepare = rpmi_clk_disable,
+};
+
+static struct clk_hw *rpmi_clk_enumerate(struct rpmi_clk_context *context, u32 clkid)
+{
+ struct device *dev = context->dev;
+ unsigned long min_rate, max_rate;
+ union rpmi_clk_rates *rates;
+ struct rpmi_clk *rpmi_clk;
+ struct clk_init_data init = {};
+ struct clk_hw *clk_hw;
+ int ret;
+
+ rates = devm_kzalloc(dev, sizeof(*rates), GFP_KERNEL);
+ if (!rates)
+ return ERR_PTR(-ENOMEM);
+
+ rpmi_clk = devm_kzalloc(dev, sizeof(*rpmi_clk), GFP_KERNEL);
+ if (!rpmi_clk)
+ return ERR_PTR(-ENOMEM);
+
+ rpmi_clk->context = context;
+ rpmi_clk->rates = rates;
+
+ ret = rpmi_clk_get_attrs(clkid, rpmi_clk);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Failed to get clk-%u attributes\n",
+ clkid);
+
+ ret = rpmi_clk_get_supported_rates(clkid, rpmi_clk);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Get supported rates failed for clk-%u\n",
+ clkid);
+
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.num_parents = 0;
+ init.ops = &rpmi_clk_ops;
+ init.name = rpmi_clk->name;
+ clk_hw = &rpmi_clk->hw;
+ clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, clk_hw);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Unable to register clk-%u\n",
+ clkid);
+
+ if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
+ min_rate = rpmi_clk->rates->discrete[0];
+ max_rate = rpmi_clk->rates->discrete[rpmi_clk->num_rates - 1];
+ } else {
+ min_rate = rpmi_clk->rates->linear.min;
+ max_rate = rpmi_clk->rates->linear.max;
+ }
+
+ clk_hw_set_rate_range(clk_hw, min_rate, max_rate);
+
+ return clk_hw;
+}
+
+static void rpmi_clk_mbox_chan_release(void *data)
+{
+ struct mbox_chan *chan = data;
+
+ mbox_free_channel(chan);
+}
+
+static int rpmi_clk_probe(struct platform_device *pdev)
+{
+ int ret;
+ unsigned int num_clocks, i;
+ struct clk_hw_onecell_data *clk_data;
+ struct rpmi_clk_context *context;
+ struct rpmi_mbox_message msg;
+ struct clk_hw *hw_ptr;
+ struct device *dev = &pdev->dev;
+
+ context = devm_kzalloc(dev, sizeof(*context), GFP_KERNEL);
+ if (!context)
+ return -ENOMEM;
+ context->dev = dev;
+ platform_set_drvdata(pdev, context);
+
+ context->client.dev = context->dev;
+ context->client.rx_callback = NULL;
+ context->client.tx_block = false;
+ context->client.knows_txdone = true;
+ context->client.tx_tout = 0;
+
+ context->chan = mbox_request_channel(&context->client, 0);
+ if (IS_ERR(context->chan))
+ return PTR_ERR(context->chan);
+
+ ret = devm_add_action_or_reset(dev, rpmi_clk_mbox_chan_release, context->chan);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add rpmi mbox channel cleanup\n");
+
+ rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SPEC_VERSION);
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get spec version\n");
+ if (msg.attr.value < RPMI_MKVER(1, 0)) {
+ return dev_err_probe(dev, -EINVAL,
+ "msg protocol version mismatch, expected 0x%x, found 0x%x\n",
+ RPMI_MKVER(1, 0), msg.attr.value);
+ }
+
+ rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_ID);
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get service group ID\n");
+ if (msg.attr.value != RPMI_SRVGRP_CLOCK) {
+ return dev_err_probe(dev, -EINVAL,
+ "service group match failed, expected 0x%x, found 0x%x\n",
+ RPMI_SRVGRP_CLOCK, msg.attr.value);
+ }
+
+ rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_VERSION);
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get service group version\n");
+ if (msg.attr.value < RPMI_MKVER(1, 0)) {
+ return dev_err_probe(dev, -EINVAL,
+ "service group version failed, expected 0x%x, found 0x%x\n",
+ RPMI_MKVER(1, 0), msg.attr.value);
+ }
+
+ rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_MAX_MSG_DATA_SIZE);
+ ret = rpmi_mbox_send_message(context->chan, &msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get max message data size\n");
+
+ context->max_msg_data_size = msg.attr.value;
+ num_clocks = rpmi_clk_get_num_clocks(context);
+ if (!num_clocks)
+ return dev_err_probe(dev, -ENODEV, "No clocks found\n");
+
+ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_clocks),
+ GFP_KERNEL);
+ if (!clk_data)
+ return dev_err_probe(dev, -ENOMEM, "No memory for clock data\n");
+ clk_data->num = num_clocks;
+
+ for (i = 0; i < clk_data->num; i++) {
+ hw_ptr = rpmi_clk_enumerate(context, i);
+ if (IS_ERR(hw_ptr)) {
+ return dev_err_probe(dev, PTR_ERR(hw_ptr),
+ "Failed to register clk-%d\n", i);
+ }
+ clk_data->hws[i] = hw_ptr;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register clock HW provider\n");
+
+ return 0;
+}
+
+static const struct of_device_id rpmi_clk_of_match[] = {
+ { .compatible = "riscv,rpmi-clock" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rpmi_clk_of_match);
+
+static struct platform_driver rpmi_clk_driver = {
+ .driver = {
+ .name = "riscv-rpmi-clock",
+ .of_match_table = rpmi_clk_of_match,
+ },
+ .probe = rpmi_clk_probe,
+};
+module_platform_driver(rpmi_clk_driver);
+
+MODULE_AUTHOR("Rahul Pathak <rpathak@ventanamicro.com>");
+MODULE_DESCRIPTION("Clock Driver based on RPMI message protocol");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c
index a3e883a9f406..ff7ce12a5da6 100644
--- a/drivers/clk/clk-s2mps11.c
+++ b/drivers/clk/clk-s2mps11.c
@@ -11,6 +11,7 @@
#include <linux/regmap.h>
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
+#include <linux/mfd/samsung/s2mpg10.h>
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
@@ -137,7 +138,12 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
if (!clk_data)
return -ENOMEM;
+ clk_data->num = S2MPS11_CLKS_NUM;
+
switch (hwid) {
+ case S2MPG10:
+ s2mps11_reg = S2MPG10_PMIC_RTCBUF;
+ break;
case S2MPS11X:
s2mps11_reg = S2MPS11_REG_RTC_CTRL;
break;
@@ -186,7 +192,6 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
clk_data->hws[i] = &s2mps11_clks[i].hw;
}
- clk_data->num = S2MPS11_CLKS_NUM;
of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get,
clk_data);
@@ -202,7 +207,7 @@ err_reg:
return ret;
}
-static int s2mps11_clk_remove(struct platform_device *pdev)
+static void s2mps11_clk_remove(struct platform_device *pdev)
{
struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
int i;
@@ -217,11 +222,10 @@ static int s2mps11_clk_remove(struct platform_device *pdev)
continue;
clkdev_drop(s2mps11_clks[i].lookup);
}
-
- return 0;
}
static const struct platform_device_id s2mps11_clk_id[] = {
+ { "s2mpg10-clk", S2MPG10},
{ "s2mps11-clk", S2MPS11X},
{ "s2mps13-clk", S2MPS13X},
{ "s2mps14-clk", S2MPS14X},
@@ -236,12 +240,15 @@ MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
* through platform_device_id.
*
* However if device's DT node contains proper clock compatible and driver is
- * built as a module, then the *module* matching will be done trough DT aliases.
+ * built as a module, then the *module* matching will be done through DT aliases.
* This requires of_device_id table. In the same time this will not change the
* actual *device* matching so do not add .of_match_table.
*/
static const struct of_device_id s2mps11_dt_match[] __used = {
{
+ .compatible = "samsung,s2mpg10-clk",
+ .data = (void *)S2MPG10,
+ }, {
.compatible = "samsung,s2mps11-clk",
.data = (void *)S2MPS11X,
}, {
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index 2c7a830ce308..6b286ea6f121 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -2,9 +2,10 @@
/*
* System Control and Power Interface (SCMI) Protocol based clock driver
*
- * Copyright (C) 2018-2022 ARM Ltd.
+ * Copyright (C) 2018-2024 ARM Ltd.
*/
+#include <linux/bits.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -13,13 +14,29 @@
#include <linux/scmi_protocol.h>
#include <asm/div64.h>
+#define NOT_ATOMIC false
+#define ATOMIC true
+
+enum scmi_clk_feats {
+ SCMI_CLK_ATOMIC_SUPPORTED,
+ SCMI_CLK_STATE_CTRL_SUPPORTED,
+ SCMI_CLK_RATE_CTRL_SUPPORTED,
+ SCMI_CLK_PARENT_CTRL_SUPPORTED,
+ SCMI_CLK_DUTY_CYCLE_SUPPORTED,
+ SCMI_CLK_FEATS_COUNT
+};
+
+#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT)
+
static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
struct scmi_clk {
u32 id;
+ struct device *dev;
struct clk_hw hw;
const struct scmi_clock_info *info;
const struct scmi_protocol_handle *ph;
+ struct clk_parent_data *parent_data;
};
#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
@@ -37,8 +54,8 @@ static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int scmi_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
u64 fmin, fmax, ftmp;
struct scmi_clk *clk = to_scmi_clk(hw);
@@ -50,20 +67,27 @@ static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
* running at then.
*/
if (clk->info->rate_discrete)
- return rate;
+ return 0;
fmin = clk->info->range.min_rate;
fmax = clk->info->range.max_rate;
- if (rate <= fmin)
- return fmin;
- else if (rate >= fmax)
- return fmax;
+ if (req->rate <= fmin) {
+ req->rate = fmin;
+
+ return 0;
+ } else if (req->rate >= fmax) {
+ req->rate = fmax;
- ftmp = rate - fmin;
+ return 0;
+ }
+
+ ftmp = req->rate - fmin;
ftmp += clk->info->range.step_size - 1; /* to round up */
do_div(ftmp, clk->info->range.step_size);
- return ftmp * clk->info->range.step_size + fmin;
+ req->rate = ftmp * clk->info->range.step_size + fmin;
+
+ return 0;
}
static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -74,62 +98,124 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate);
}
+static int scmi_clk_set_parent(struct clk_hw *hw, u8 parent_index)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ return scmi_proto_clk_ops->parent_set(clk->ph, clk->id, parent_index);
+}
+
+static u8 scmi_clk_get_parent(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+ u32 parent_id, p_idx;
+ int ret;
+
+ ret = scmi_proto_clk_ops->parent_get(clk->ph, clk->id, &parent_id);
+ if (ret)
+ return 0;
+
+ for (p_idx = 0; p_idx < clk->info->num_parents; p_idx++) {
+ if (clk->parent_data[p_idx].index == parent_id)
+ break;
+ }
+
+ if (p_idx == clk->info->num_parents)
+ return 0;
+
+ return p_idx;
+}
+
static int scmi_clk_enable(struct clk_hw *hw)
{
struct scmi_clk *clk = to_scmi_clk(hw);
- return scmi_proto_clk_ops->enable(clk->ph, clk->id);
+ return scmi_proto_clk_ops->enable(clk->ph, clk->id, NOT_ATOMIC);
}
static void scmi_clk_disable(struct clk_hw *hw)
{
struct scmi_clk *clk = to_scmi_clk(hw);
- scmi_proto_clk_ops->disable(clk->ph, clk->id);
+ scmi_proto_clk_ops->disable(clk->ph, clk->id, NOT_ATOMIC);
}
static int scmi_clk_atomic_enable(struct clk_hw *hw)
{
struct scmi_clk *clk = to_scmi_clk(hw);
- return scmi_proto_clk_ops->enable_atomic(clk->ph, clk->id);
+ return scmi_proto_clk_ops->enable(clk->ph, clk->id, ATOMIC);
}
static void scmi_clk_atomic_disable(struct clk_hw *hw)
{
struct scmi_clk *clk = to_scmi_clk(hw);
- scmi_proto_clk_ops->disable_atomic(clk->ph, clk->id);
+ scmi_proto_clk_ops->disable(clk->ph, clk->id, ATOMIC);
}
-/*
- * We can provide enable/disable atomic callbacks only if the underlying SCMI
- * transport for an SCMI instance is configured to handle SCMI commands in an
- * atomic manner.
- *
- * When no SCMI atomic transport support is available we instead provide only
- * the prepare/unprepare API, as allowed by the clock framework when atomic
- * calls are not available.
- *
- * Two distinct sets of clk_ops are provided since we could have multiple SCMI
- * instances with different underlying transport quality, so they cannot be
- * shared.
- */
-static const struct clk_ops scmi_clk_ops = {
- .recalc_rate = scmi_clk_recalc_rate,
- .round_rate = scmi_clk_round_rate,
- .set_rate = scmi_clk_set_rate,
- .prepare = scmi_clk_enable,
- .unprepare = scmi_clk_disable,
-};
+static int __scmi_clk_is_enabled(struct clk_hw *hw, bool atomic)
+{
+ int ret;
+ bool enabled = false;
+ struct scmi_clk *clk = to_scmi_clk(hw);
-static const struct clk_ops scmi_atomic_clk_ops = {
- .recalc_rate = scmi_clk_recalc_rate,
- .round_rate = scmi_clk_round_rate,
- .set_rate = scmi_clk_set_rate,
- .enable = scmi_clk_atomic_enable,
- .disable = scmi_clk_atomic_disable,
-};
+ ret = scmi_proto_clk_ops->state_get(clk->ph, clk->id, &enabled, atomic);
+ if (ret)
+ dev_warn(clk->dev,
+ "Failed to get state for clock ID %d\n", clk->id);
+
+ return !!enabled;
+}
+
+static int scmi_clk_atomic_is_enabled(struct clk_hw *hw)
+{
+ return __scmi_clk_is_enabled(hw, ATOMIC);
+}
+
+static int scmi_clk_is_enabled(struct clk_hw *hw)
+{
+ return __scmi_clk_is_enabled(hw, NOT_ATOMIC);
+}
+
+static int scmi_clk_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
+{
+ int ret;
+ u32 val;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ ret = scmi_proto_clk_ops->config_oem_get(clk->ph, clk->id,
+ SCMI_CLOCK_CFG_DUTY_CYCLE,
+ &val, NULL, false);
+ if (!ret) {
+ duty->num = val;
+ duty->den = 100;
+ } else {
+ dev_warn(clk->dev,
+ "Failed to get duty cycle for clock ID %d\n", clk->id);
+ }
+
+ return ret;
+}
+
+static int scmi_clk_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
+{
+ int ret;
+ u32 val;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ /* SCMI OEM Duty Cycle is expressed as a percentage */
+ val = (duty->num * 100) / duty->den;
+ ret = scmi_proto_clk_ops->config_oem_set(clk->ph, clk->id,
+ SCMI_CLOCK_CFG_DUTY_CYCLE,
+ val, false);
+ if (ret)
+ dev_warn(clk->dev,
+ "Failed to set duty cycle(%u/%u) for clock ID %d\n",
+ duty->num, duty->den, clk->id);
+
+ return ret;
+}
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
const struct clk_ops *scmi_ops)
@@ -139,9 +225,10 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
struct clk_init_data init = {
.flags = CLK_GET_RATE_NOCACHE,
- .num_parents = 0,
+ .num_parents = sclk->info->num_parents,
.ops = scmi_ops,
.name = sclk->info->name,
+ .parent_data = sclk->parent_data,
};
sclk->hw.init = &init;
@@ -166,17 +253,162 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
return ret;
}
+/**
+ * scmi_clk_ops_alloc() - Alloc and configure clock operations
+ * @dev: A device reference for devres
+ * @feats_key: A bitmap representing the desired clk_ops capabilities
+ *
+ * Allocate and configure a proper set of clock operations depending on the
+ * specifically required SCMI clock features.
+ *
+ * Return: A pointer to the allocated and configured clk_ops on success,
+ * or NULL on allocation failure.
+ */
+static const struct clk_ops *
+scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
+{
+ struct clk_ops *ops;
+
+ ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return NULL;
+ /*
+ * We can provide enable/disable/is_enabled atomic callbacks only if the
+ * underlying SCMI transport for an SCMI instance is configured to
+ * handle SCMI commands in an atomic manner.
+ *
+ * When no SCMI atomic transport support is available we instead provide
+ * only the prepare/unprepare API, as allowed by the clock framework
+ * when atomic calls are not available.
+ */
+ if (feats_key & BIT(SCMI_CLK_STATE_CTRL_SUPPORTED)) {
+ if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
+ ops->enable = scmi_clk_atomic_enable;
+ ops->disable = scmi_clk_atomic_disable;
+ } else {
+ ops->prepare = scmi_clk_enable;
+ ops->unprepare = scmi_clk_disable;
+ }
+ }
+
+ if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED))
+ ops->is_enabled = scmi_clk_atomic_is_enabled;
+ else
+ ops->is_prepared = scmi_clk_is_enabled;
+
+ /* Rate ops */
+ ops->recalc_rate = scmi_clk_recalc_rate;
+ ops->determine_rate = scmi_clk_determine_rate;
+ if (feats_key & BIT(SCMI_CLK_RATE_CTRL_SUPPORTED))
+ ops->set_rate = scmi_clk_set_rate;
+
+ /* Parent ops */
+ ops->get_parent = scmi_clk_get_parent;
+ if (feats_key & BIT(SCMI_CLK_PARENT_CTRL_SUPPORTED))
+ ops->set_parent = scmi_clk_set_parent;
+
+ /* Duty cycle */
+ if (feats_key & BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED)) {
+ ops->get_duty_cycle = scmi_clk_get_duty_cycle;
+ ops->set_duty_cycle = scmi_clk_set_duty_cycle;
+ }
+
+ return ops;
+}
+
+/**
+ * scmi_clk_ops_select() - Select a proper set of clock operations
+ * @sclk: A reference to an SCMI clock descriptor
+ * @atomic_capable: A flag to indicate if atomic mode is supported by the
+ * transport
+ * @atomic_threshold_us: Platform atomic threshold value in microseconds:
+ * clk_ops are atomic when clock enable latency is less
+ * than this threshold
+ * @clk_ops_db: A reference to the array used as a database to store all the
+ * created clock operations combinations.
+ * @db_size: Maximum number of entries held by @clk_ops_db
+ *
+ * After having built a bitmap descriptor to represent the set of features
+ * needed by this SCMI clock, at first use it to lookup into the set of
+ * previously allocated clk_ops to check if a suitable combination of clock
+ * operations was already created; when no match is found allocate a brand new
+ * set of clk_ops satisfying the required combination of features and save it
+ * for future references.
+ *
+ * In this way only one set of clk_ops is ever created for each different
+ * combination that is effectively needed by a driver instance.
+ *
+ * Return: A pointer to the allocated and configured clk_ops on success, or
+ * NULL otherwise.
+ */
+static const struct clk_ops *
+scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
+ unsigned int atomic_threshold_us,
+ const struct clk_ops **clk_ops_db, size_t db_size)
+{
+ int ret;
+ u32 val;
+ const struct scmi_clock_info *ci = sclk->info;
+ unsigned int feats_key = 0;
+ const struct clk_ops *ops;
+
+ /*
+ * Note that when transport is atomic but SCMI protocol did not
+ * specify (or support) an enable_latency associated with a
+ * clock, we default to use atomic operations mode.
+ */
+ if (atomic_capable && ci->enable_latency <= atomic_threshold_us)
+ feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
+
+ if (!ci->state_ctrl_forbidden)
+ feats_key |= BIT(SCMI_CLK_STATE_CTRL_SUPPORTED);
+
+ if (!ci->rate_ctrl_forbidden)
+ feats_key |= BIT(SCMI_CLK_RATE_CTRL_SUPPORTED);
+
+ if (!ci->parent_ctrl_forbidden)
+ feats_key |= BIT(SCMI_CLK_PARENT_CTRL_SUPPORTED);
+
+ if (ci->extended_config) {
+ ret = scmi_proto_clk_ops->config_oem_get(sclk->ph, sclk->id,
+ SCMI_CLOCK_CFG_DUTY_CYCLE,
+ &val, NULL, false);
+ if (!ret)
+ feats_key |= BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED);
+ }
+
+ if (WARN_ON(feats_key >= db_size))
+ return NULL;
+
+ /* Lookup previously allocated ops */
+ ops = clk_ops_db[feats_key];
+ if (ops)
+ return ops;
+
+ /* Did not find a pre-allocated clock_ops */
+ ops = scmi_clk_ops_alloc(sclk->dev, feats_key);
+ if (!ops)
+ return NULL;
+
+ /* Store new ops combinations */
+ clk_ops_db[feats_key] = ops;
+
+ return ops;
+}
+
static int scmi_clocks_probe(struct scmi_device *sdev)
{
int idx, count, err;
- unsigned int atomic_threshold;
- bool is_atomic;
+ unsigned int atomic_threshold_us;
+ bool transport_is_atomic;
struct clk_hw **hws;
struct clk_hw_onecell_data *clk_data;
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
struct scmi_protocol_handle *ph;
+ const struct clk_ops *scmi_clk_ops_db[SCMI_MAX_CLK_OPS] = {};
+ struct scmi_clk *sclks;
if (!handle)
return -ENODEV;
@@ -200,47 +432,66 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
clk_data->num = count;
hws = clk_data->hws;
- is_atomic = handle->is_transport_atomic(handle, &atomic_threshold);
+ transport_is_atomic = handle->is_transport_atomic(handle,
+ &atomic_threshold_us);
+
+ sclks = devm_kcalloc(dev, count, sizeof(*sclks), GFP_KERNEL);
+ if (!sclks)
+ return -ENOMEM;
+
+ for (idx = 0; idx < count; idx++)
+ hws[idx] = &sclks[idx].hw;
for (idx = 0; idx < count; idx++) {
- struct scmi_clk *sclk;
+ struct scmi_clk *sclk = &sclks[idx];
const struct clk_ops *scmi_ops;
- sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
- if (!sclk)
- return -ENOMEM;
-
sclk->info = scmi_proto_clk_ops->info_get(ph, idx);
if (!sclk->info) {
dev_dbg(dev, "invalid clock info for idx %d\n", idx);
+ hws[idx] = NULL;
continue;
}
sclk->id = idx;
sclk->ph = ph;
+ sclk->dev = dev;
/*
- * Note that when transport is atomic but SCMI protocol did not
- * specify (or support) an enable_latency associated with a
- * clock, we default to use atomic operations mode.
+ * Note that the scmi_clk_ops_db is on the stack, not global,
+ * because it cannot be shared between multiple probe-sequences
+ * to avoid sharing the devm_ allocated clk_ops between multiple
+ * SCMI clk driver instances.
*/
- if (is_atomic &&
- sclk->info->enable_latency <= atomic_threshold)
- scmi_ops = &scmi_atomic_clk_ops;
- else
- scmi_ops = &scmi_clk_ops;
+ scmi_ops = scmi_clk_ops_select(sclk, transport_is_atomic,
+ atomic_threshold_us,
+ scmi_clk_ops_db,
+ ARRAY_SIZE(scmi_clk_ops_db));
+ if (!scmi_ops)
+ return -ENOMEM;
+
+ /* Initialize clock parent data. */
+ if (sclk->info->num_parents > 0) {
+ sclk->parent_data = devm_kcalloc(dev, sclk->info->num_parents,
+ sizeof(*sclk->parent_data), GFP_KERNEL);
+ if (!sclk->parent_data)
+ return -ENOMEM;
+
+ for (int i = 0; i < sclk->info->num_parents; i++) {
+ sclk->parent_data[i].index = sclk->info->parents[i];
+ sclk->parent_data[i].hw = hws[sclk->info->parents[i]];
+ }
+ }
err = scmi_clk_ops_init(dev, sclk, scmi_ops);
if (err) {
dev_err(dev, "failed to register clock %d\n", idx);
- devm_kfree(dev, sclk);
+ devm_kfree(dev, sclk->parent_data);
hws[idx] = NULL;
} else {
dev_dbg(dev, "Registered clock:%s%s\n",
sclk->info->name,
- scmi_ops == &scmi_atomic_clk_ops ?
- " (atomic ops)" : "");
- hws[idx] = &sclk->hw;
+ scmi_ops->enable ? " (atomic ops)" : "");
}
}
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
index a39af7616b13..0b592de7bdb2 100644
--- a/drivers/clk/clk-scpi.c
+++ b/drivers/clk/clk-scpi.c
@@ -10,7 +10,6 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/module.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/scpi_protocol.h>
@@ -33,8 +32,8 @@ static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
return clk->scpi_ops->clk_get_val(clk->id);
}
-static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int scpi_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
/*
* We can't figure out what rate it will be, so just return the
@@ -42,7 +41,7 @@ static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
* after the rate is set and we'll know what rate the clock is
* running at then.
*/
- return rate;
+ return 0;
}
static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -55,7 +54,7 @@ static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops scpi_clk_ops = {
.recalc_rate = scpi_clk_recalc_rate,
- .round_rate = scpi_clk_round_rate,
+ .determine_rate = scpi_clk_determine_rate,
.set_rate = scpi_clk_set_rate,
};
@@ -93,12 +92,14 @@ static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw,
return opp->freq;
}
-static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int scpi_dvfs_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct scpi_clk *clk = to_scpi_clk(hw);
- return __scpi_dvfs_round_rate(clk, rate);
+ req->rate = __scpi_dvfs_round_rate(clk, req->rate);
+
+ return 0;
}
static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate)
@@ -125,7 +126,7 @@ static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops scpi_dvfs_ops = {
.recalc_rate = scpi_dvfs_recalc_rate,
- .round_rate = scpi_dvfs_round_rate,
+ .determine_rate = scpi_dvfs_determine_rate,
.set_rate = scpi_dvfs_set_rate,
};
@@ -246,7 +247,7 @@ static int scpi_clk_add(struct device *dev, struct device_node *np,
return of_clk_add_hw_provider(np, scpi_of_clk_src_get, clk_data);
}
-static int scpi_clocks_remove(struct platform_device *pdev)
+static void scpi_clocks_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node;
@@ -258,7 +259,6 @@ static int scpi_clocks_remove(struct platform_device *pdev)
for_each_available_child_of_node(np, child)
of_clk_del_provider(np);
- return 0;
}
static int scpi_clocks_probe(struct platform_device *pdev)
diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c
index 4481c4303534..f61590d70575 100644
--- a/drivers/clk/clk-si514.c
+++ b/drivers/clk/clk-si514.c
@@ -227,20 +227,28 @@ static unsigned long si514_recalc_rate(struct clk_hw *hw,
return si514_calc_rate(&settings);
}
-static long si514_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si514_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_si514_muldiv settings;
int err;
- if (!rate)
+ if (!req->rate) {
+ req->rate = 0;
+
return 0;
+ }
- err = si514_calc_muldiv(&settings, rate);
- if (err)
- return err;
+ err = si514_calc_muldiv(&settings, req->rate);
+ if (err) {
+ req->rate = err;
- return si514_calc_rate(&settings);
+ return 0;
+ }
+
+ req->rate = si514_calc_rate(&settings);
+
+ return 0;
}
/*
@@ -289,7 +297,7 @@ static const struct clk_ops si514_clk_ops = {
.unprepare = si514_unprepare,
.is_prepared = si514_is_prepared,
.recalc_rate = si514_recalc_rate,
- .round_rate = si514_round_rate,
+ .determine_rate = si514_determine_rate,
.set_rate = si514_set_rate,
};
@@ -321,7 +329,7 @@ static bool si514_regmap_is_writeable(struct device *dev, unsigned int reg)
static const struct regmap_config si514_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = SI514_REG_CONTROL,
.writeable_reg = si514_regmap_is_writeable,
.volatile_reg = si514_regmap_is_volatile,
@@ -360,8 +368,8 @@ static int si514_probe(struct i2c_client *client)
dev_err(&client->dev, "clock registration failed\n");
return err;
}
- err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
- &data->hw);
+ err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get,
+ &data->hw);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
@@ -370,14 +378,8 @@ static int si514_probe(struct i2c_client *client)
return 0;
}
-static int si514_remove(struct i2c_client *client)
-{
- of_clk_del_provider(client->dev.of_node);
- return 0;
-}
-
static const struct i2c_device_id si514_id[] = {
- { "si514", 0 },
+ { "si514" },
{ }
};
MODULE_DEVICE_TABLE(i2c, si514_id);
@@ -393,8 +395,7 @@ static struct i2c_driver si514_driver = {
.name = "si514",
.of_match_table = clk_si514_of_match,
},
- .probe_new = si514_probe,
- .remove = si514_remove,
+ .probe = si514_probe,
.id_table = si514_id,
};
module_i2c_driver(si514_driver);
diff --git a/drivers/clk/clk-si521xx.c b/drivers/clk/clk-si521xx.c
new file mode 100644
index 000000000000..4ed4e1a5f4f2
--- /dev/null
+++ b/drivers/clk/clk-si521xx.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Skyworks Si521xx PCIe clock generator driver
+ *
+ * The following series can be supported:
+ * - Si52144 - 4x DIFF
+ * - Si52146 - 6x DIFF
+ * - Si52147 - 9x DIFF
+ * Currently tested:
+ * - Si52144
+ *
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitrev.h>
+#include <linux/clk-provider.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+/* OE1 and OE2 register */
+#define SI521XX_REG_OE(n) (((n) & 0x1) + 1)
+#define SI521XX_REG_ID 0x3
+#define SI521XX_REG_ID_PROG GENMASK(7, 4)
+#define SI521XX_REG_ID_VENDOR GENMASK(3, 0)
+#define SI521XX_REG_BC 0x4
+#define SI521XX_REG_DA 0x5
+#define SI521XX_REG_DA_AMP_SEL BIT(7)
+#define SI521XX_REG_DA_AMP_MASK GENMASK(6, 4)
+#define SI521XX_REG_DA_AMP_MIN 300000
+#define SI521XX_REG_DA_AMP_DEFAULT 800000
+#define SI521XX_REG_DA_AMP_MAX 1000000
+#define SI521XX_REG_DA_AMP_STEP 100000
+#define SI521XX_REG_DA_AMP(UV) \
+ FIELD_PREP(SI521XX_REG_DA_AMP_MASK, \
+ ((UV) - SI521XX_REG_DA_AMP_MIN) / SI521XX_REG_DA_AMP_STEP)
+#define SI521XX_REG_DA_UNKNOWN BIT(3) /* Always set */
+
+/* Count of populated OE bits in control register ref, 1 and 2 */
+#define SI521XX_OE_MAP(cr1, cr2) (((cr2) << 8) | (cr1))
+#define SI521XX_OE_MAP_GET_OE(oe, map) (((map) >> (((oe) - 1) * 8)) & 0xff)
+
+#define SI521XX_DIFF_MULT 4
+#define SI521XX_DIFF_DIV 1
+
+/* Supported Skyworks Si521xx models. */
+enum si521xx_model {
+ SI52144 = 0x44,
+ SI52146 = 0x46,
+ SI52147 = 0x47,
+};
+
+struct si521xx;
+
+struct si_clk {
+ struct clk_hw hw;
+ struct si521xx *si;
+ u8 reg;
+ u8 bit;
+};
+
+struct si521xx {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct si_clk clk_dif[9];
+ u16 chip_info;
+ u8 pll_amplitude;
+};
+
+/*
+ * Si521xx i2c regmap
+ */
+static const struct regmap_range si521xx_readable_ranges[] = {
+ regmap_reg_range(SI521XX_REG_OE(0), SI521XX_REG_DA),
+};
+
+static const struct regmap_access_table si521xx_readable_table = {
+ .yes_ranges = si521xx_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(si521xx_readable_ranges),
+};
+
+static const struct regmap_range si521xx_writeable_ranges[] = {
+ regmap_reg_range(SI521XX_REG_OE(0), SI521XX_REG_OE(1)),
+ regmap_reg_range(SI521XX_REG_BC, SI521XX_REG_DA),
+};
+
+static const struct regmap_access_table si521xx_writeable_table = {
+ .yes_ranges = si521xx_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(si521xx_writeable_ranges),
+};
+
+static int si521xx_regmap_i2c_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *i2c = context;
+ const u8 data[2] = { reg, val };
+ const int count = ARRAY_SIZE(data);
+ int ret;
+
+ ret = i2c_master_send(i2c, data, count);
+ if (ret == count)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int si521xx_regmap_i2c_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct i2c_client *i2c = context;
+ struct i2c_msg xfer[2];
+ u8 txdata = reg;
+ u8 rxdata[2];
+ int ret;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = (void *)&txdata;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 2;
+ xfer[1].buf = (void *)rxdata;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ /*
+ * Byte 0 is transfer length, which is always 1 due
+ * to BCP register programming to 1 in si521xx_probe(),
+ * ignore it and use data from Byte 1.
+ */
+ *val = rxdata[1];
+ return 0;
+}
+
+static const struct regmap_config si521xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = SI521XX_REG_DA,
+ .rd_table = &si521xx_readable_table,
+ .wr_table = &si521xx_writeable_table,
+ .reg_write = si521xx_regmap_i2c_write,
+ .reg_read = si521xx_regmap_i2c_read,
+};
+
+static unsigned long si521xx_diff_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long long rate;
+
+ rate = (unsigned long long)parent_rate * SI521XX_DIFF_MULT;
+ do_div(rate, SI521XX_DIFF_DIV);
+ return (unsigned long)rate;
+}
+
+static int si521xx_diff_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned long best_parent;
+
+ best_parent = (req->rate / SI521XX_DIFF_MULT) * SI521XX_DIFF_DIV;
+ req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
+
+ req->rate = (req->best_parent_rate / SI521XX_DIFF_DIV) * SI521XX_DIFF_MULT;
+
+ return 0;
+}
+
+static int si521xx_diff_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ /*
+ * We must report success but we can do so unconditionally because
+ * si521xx_diff_round_rate returns values that ensure this call is a
+ * nop.
+ */
+
+ return 0;
+}
+
+#define to_si521xx_clk(_hw) container_of(_hw, struct si_clk, hw)
+
+static int si521xx_diff_prepare(struct clk_hw *hw)
+{
+ struct si_clk *si_clk = to_si521xx_clk(hw);
+ struct si521xx *si = si_clk->si;
+
+ regmap_set_bits(si->regmap, SI521XX_REG_OE(si_clk->reg), si_clk->bit);
+
+ return 0;
+}
+
+static void si521xx_diff_unprepare(struct clk_hw *hw)
+{
+ struct si_clk *si_clk = to_si521xx_clk(hw);
+ struct si521xx *si = si_clk->si;
+
+ regmap_clear_bits(si->regmap, SI521XX_REG_OE(si_clk->reg), si_clk->bit);
+}
+
+static const struct clk_ops si521xx_diff_clk_ops = {
+ .determine_rate = si521xx_diff_determine_rate,
+ .set_rate = si521xx_diff_set_rate,
+ .recalc_rate = si521xx_diff_recalc_rate,
+ .prepare = si521xx_diff_prepare,
+ .unprepare = si521xx_diff_unprepare,
+};
+
+static int si521xx_get_common_config(struct si521xx *si)
+{
+ struct i2c_client *client = si->client;
+ struct device_node *np = client->dev.of_node;
+ unsigned int amp;
+ int ret;
+
+ /* Set defaults */
+ si->pll_amplitude = SI521XX_REG_DA_AMP(SI521XX_REG_DA_AMP_DEFAULT);
+
+ /* Output clock amplitude */
+ ret = of_property_read_u32(np, "skyworks,out-amplitude-microvolt",
+ &amp);
+ if (!ret) {
+ if (amp < SI521XX_REG_DA_AMP_MIN || amp > SI521XX_REG_DA_AMP_MAX ||
+ amp % SI521XX_REG_DA_AMP_STEP) {
+ return dev_err_probe(&client->dev, -EINVAL,
+ "Invalid skyworks,out-amplitude-microvolt value\n");
+ }
+ si->pll_amplitude = SI521XX_REG_DA_AMP(amp);
+ }
+
+ return 0;
+}
+
+static void si521xx_update_config(struct si521xx *si)
+{
+ /* If amplitude is non-default, update it. */
+ if (si->pll_amplitude == SI521XX_REG_DA_AMP(SI521XX_REG_DA_AMP_DEFAULT))
+ return;
+
+ regmap_update_bits(si->regmap, SI521XX_REG_DA,
+ SI521XX_REG_DA_AMP_MASK, si->pll_amplitude);
+}
+
+static void si521xx_diff_idx_to_reg_bit(const u16 chip_info, const int idx,
+ struct si_clk *clk)
+{
+ unsigned long mask;
+ int oe, b, ctr = 0;
+
+ for (oe = 1; oe <= 2; oe++) {
+ mask = bitrev8(SI521XX_OE_MAP_GET_OE(oe, chip_info));
+ for_each_set_bit(b, &mask, 8) {
+ if (ctr++ != idx)
+ continue;
+ clk->reg = SI521XX_REG_OE(oe);
+ clk->bit = 7 - b;
+ return;
+ }
+ }
+}
+
+static struct clk_hw *
+si521xx_of_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct si521xx *si = data;
+ unsigned int idx = clkspec->args[0];
+
+ return &si->clk_dif[idx].hw;
+}
+
+static int si521xx_probe(struct i2c_client *client)
+{
+ const u16 chip_info = (u16)(uintptr_t)i2c_get_match_data(client);
+ const struct clk_parent_data clk_parent_data = { .index = 0 };
+ const u8 data[3] = { SI521XX_REG_BC, 1, 1 };
+ unsigned char name[16] = "DIFF0";
+ struct clk_init_data init = {};
+ struct si521xx *si;
+ int i, ret;
+
+ if (!chip_info)
+ return -EINVAL;
+
+ si = devm_kzalloc(&client->dev, sizeof(*si), GFP_KERNEL);
+ if (!si)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, si);
+ si->client = client;
+
+ /* Fetch common configuration from DT (if specified) */
+ ret = si521xx_get_common_config(si);
+ if (ret)
+ return ret;
+
+ si->regmap = devm_regmap_init(&client->dev, NULL, client,
+ &si521xx_regmap_config);
+ if (IS_ERR(si->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(si->regmap),
+ "Failed to allocate register map\n");
+
+ /* Always read back 1 Byte via I2C */
+ ret = i2c_master_send(client, data, ARRAY_SIZE(data));
+ if (ret < 0)
+ return ret;
+
+ /* Register clock */
+ for (i = 0; i < hweight16(chip_info); i++) {
+ memset(&init, 0, sizeof(init));
+ snprintf(name, sizeof(name), "DIFF%d", i);
+ init.name = name;
+ init.ops = &si521xx_diff_clk_ops;
+ init.parent_data = &clk_parent_data;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+
+ si->clk_dif[i].hw.init = &init;
+ si->clk_dif[i].si = si;
+
+ si521xx_diff_idx_to_reg_bit(chip_info, i, &si->clk_dif[i]);
+
+ ret = devm_clk_hw_register(&client->dev, &si->clk_dif[i].hw);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(&client->dev, si521xx_of_clk_get, si);
+ if (!ret)
+ si521xx_update_config(si);
+
+ return ret;
+}
+
+static int __maybe_unused si521xx_suspend(struct device *dev)
+{
+ struct si521xx *si = dev_get_drvdata(dev);
+
+ regcache_cache_only(si->regmap, true);
+ regcache_mark_dirty(si->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused si521xx_resume(struct device *dev)
+{
+ struct si521xx *si = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(si->regmap, false);
+ ret = regcache_sync(si->regmap);
+ if (ret)
+ dev_err(dev, "Failed to restore register map: %d\n", ret);
+ return ret;
+}
+
+static const struct i2c_device_id si521xx_id[] = {
+ { "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) },
+ { "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) },
+ { "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si521xx_id);
+
+static const struct of_device_id clk_si521xx_of_match[] = {
+ { .compatible = "skyworks,si52144", .data = (void *)SI521XX_OE_MAP(0x5, 0xc0) },
+ { .compatible = "skyworks,si52146", .data = (void *)SI521XX_OE_MAP(0x15, 0xe0) },
+ { .compatible = "skyworks,si52147", .data = (void *)SI521XX_OE_MAP(0x15, 0xf8) },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clk_si521xx_of_match);
+
+static SIMPLE_DEV_PM_OPS(si521xx_pm_ops, si521xx_suspend, si521xx_resume);
+
+static struct i2c_driver si521xx_driver = {
+ .driver = {
+ .name = "clk-si521xx",
+ .pm = &si521xx_pm_ops,
+ .of_match_table = clk_si521xx_of_match,
+ },
+ .probe = si521xx_probe,
+ .id_table = si521xx_id,
+};
+module_i2c_driver(si521xx_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Skyworks Si521xx PCIe clock generator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c
index 4bca73212662..2499b771cd83 100644
--- a/drivers/clk/clk-si5341.c
+++ b/drivers/clk/clk-si5341.c
@@ -21,7 +21,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define SI5341_NUM_INPUTS 4
@@ -551,6 +551,7 @@ static int si5341_clk_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops si5341_clk_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = si5341_clk_set_parent,
.get_parent = si5341_clk_get_parent,
.recalc_rate = si5341_clk_recalc_rate,
@@ -662,8 +663,8 @@ static unsigned long si5341_synth_clk_recalc_rate(struct clk_hw *hw,
return f;
}
-static long si5341_synth_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si5341_synth_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
u64 f;
@@ -671,15 +672,21 @@ static long si5341_synth_clk_round_rate(struct clk_hw *hw, unsigned long rate,
/* The synthesizer accuracy is such that anything in range will work */
f = synth->data->freq_vco;
do_div(f, SI5341_SYNTH_N_MAX);
- if (rate < f)
- return f;
+ if (req->rate < f) {
+ req->rate = f;
+
+ return 0;
+ }
f = synth->data->freq_vco;
do_div(f, SI5341_SYNTH_N_MIN);
- if (rate > f)
- return f;
+ if (req->rate > f) {
+ req->rate = f;
+
+ return 0;
+ }
- return rate;
+ return 0;
}
static int si5341_synth_program(struct clk_si5341_synth *synth,
@@ -740,7 +747,7 @@ static const struct clk_ops si5341_synth_clk_ops = {
.prepare = si5341_synth_clk_prepare,
.unprepare = si5341_synth_clk_unprepare,
.recalc_rate = si5341_synth_clk_recalc_rate,
- .round_rate = si5341_synth_clk_round_rate,
+ .determine_rate = si5341_synth_clk_determine_rate,
.set_rate = si5341_synth_clk_set_rate,
};
@@ -827,19 +834,20 @@ static unsigned long si5341_output_clk_recalc_rate(struct clk_hw *hw,
return parent_rate / r_divider;
}
-static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si5341_output_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
+ unsigned long rate = req->rate;
unsigned long r;
if (!rate)
return 0;
- r = *parent_rate >> 1;
+ r = req->best_parent_rate >> 1;
/* If rate is an even divisor, no changes to parent required */
if (r && !(r % rate))
- return (long)rate;
+ return 0;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
if (rate > 200000000) {
@@ -849,14 +857,15 @@ static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate,
/* Take a parent frequency near 400 MHz */
r = (400000000u / rate) & ~1;
}
- *parent_rate = r * rate;
+ req->best_parent_rate = r * rate;
} else {
/* We cannot change our parent's rate, report what we can do */
r /= rate;
- rate = *parent_rate / (r << 1);
+ rate = req->best_parent_rate / (r << 1);
}
- return rate;
+ req->rate = rate;
+ return 0;
}
static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -892,10 +901,8 @@ static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate,
r[0] = r_div ? (r_div & 0xff) : 1;
r[1] = (r_div >> 8) & 0xff;
r[2] = (r_div >> 16) & 0xff;
- err = regmap_bulk_write(output->data->regmap,
+ return regmap_bulk_write(output->data->regmap,
SI5341_OUT_R_REG(output), r, 3);
-
- return 0;
}
static int si5341_output_reparent(struct clk_si5341_output *output, u8 index)
@@ -929,7 +936,7 @@ static const struct clk_ops si5341_output_clk_ops = {
.prepare = si5341_output_clk_prepare,
.unprepare = si5341_output_clk_unprepare,
.recalc_rate = si5341_output_clk_recalc_rate,
- .round_rate = si5341_output_clk_round_rate,
+ .determine_rate = si5341_output_clk_determine_rate,
.set_rate = si5341_output_clk_set_rate,
.set_parent = si5341_output_set_parent,
.get_parent = si5341_output_get_parent,
@@ -1257,7 +1264,7 @@ static int si5341_wait_device_ready(struct i2c_client *client)
static const struct regmap_config si5341_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.ranges = si5341_regmap_ranges,
.num_ranges = ARRAY_SIZE(si5341_regmap_ranges),
.max_register = SI5341_REGISTER_MAX,
@@ -1553,7 +1560,7 @@ static int si5341_probe(struct i2c_client *client)
struct clk_init_data init;
struct clk *input;
const char *root_clock_name;
- const char *synth_clock_names[SI5341_NUM_SYNTH];
+ const char *synth_clock_names[SI5341_NUM_SYNTH] = { NULL };
int err;
unsigned int i;
struct clk_si5341_output_config config[SI5341_MAX_NUM_OUTPUTS];
@@ -1697,6 +1704,10 @@ static int si5341_probe(struct i2c_client *client)
for (i = 0; i < data->num_synth; ++i) {
synth_clock_names[i] = devm_kasprintf(&client->dev, GFP_KERNEL,
"%s.N%u", client->dev.of_node->name, i);
+ if (!synth_clock_names[i]) {
+ err = -ENOMEM;
+ goto free_clk_names;
+ }
init.name = synth_clock_names[i];
data->synth[i].index = i;
data->synth[i].data = data;
@@ -1705,6 +1716,7 @@ static int si5341_probe(struct i2c_client *client)
if (err) {
dev_err(&client->dev,
"synth N%u registration failed\n", i);
+ goto free_clk_names;
}
}
@@ -1714,6 +1726,10 @@ static int si5341_probe(struct i2c_client *client)
for (i = 0; i < data->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%s.%d",
client->dev.of_node->name, i);
+ if (!init.name) {
+ err = -ENOMEM;
+ goto free_clk_names;
+ }
init.flags = config[i].synth_master ? CLK_SET_RATE_PARENT : 0;
data->clk[i].index = i;
data->clk[i].data = data;
@@ -1735,7 +1751,7 @@ static int si5341_probe(struct i2c_client *client)
if (err) {
dev_err(&client->dev,
"output %u registration failed\n", i);
- goto cleanup;
+ goto free_clk_names;
}
if (config[i].always_on)
clk_prepare(data->clk[i].hw.clk);
@@ -1745,7 +1761,7 @@ static int si5341_probe(struct i2c_client *client)
data);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
- goto cleanup;
+ goto free_clk_names;
}
if (initialization_required) {
@@ -1753,11 +1769,11 @@ static int si5341_probe(struct i2c_client *client)
regcache_cache_only(data->regmap, false);
err = regcache_sync(data->regmap);
if (err < 0)
- goto cleanup;
+ goto free_clk_names;
err = si5341_finalize_defaults(data);
if (err < 0)
- goto cleanup;
+ goto free_clk_names;
}
/* wait for device to report input clock present and PLL lock */
@@ -1766,37 +1782,36 @@ static int si5341_probe(struct i2c_client *client)
10000, 250000);
if (err) {
dev_err(&client->dev, "Error waiting for input clock or PLL lock\n");
- goto cleanup;
+ goto free_clk_names;
}
/* clear sticky alarm bits from initialization */
err = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0);
if (err) {
dev_err(&client->dev, "unable to clear sticky status\n");
- goto cleanup;
+ goto free_clk_names;
}
err = sysfs_create_files(&client->dev.kobj, si5341_attributes);
- if (err) {
+ if (err)
dev_err(&client->dev, "unable to create sysfs files\n");
- goto cleanup;
- }
+free_clk_names:
/* Free the names, clk framework makes copies */
for (i = 0; i < data->num_synth; ++i)
devm_kfree(&client->dev, (void *)synth_clock_names[i]);
- return 0;
-
cleanup:
- for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) {
- if (data->clk[i].vddo_reg)
- regulator_disable(data->clk[i].vddo_reg);
+ if (err) {
+ for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) {
+ if (data->clk[i].vddo_reg)
+ regulator_disable(data->clk[i].vddo_reg);
+ }
}
return err;
}
-static int si5341_remove(struct i2c_client *client)
+static void si5341_remove(struct i2c_client *client)
{
struct clk_si5341 *data = i2c_get_clientdata(client);
int i;
@@ -1807,8 +1822,6 @@ static int si5341_remove(struct i2c_client *client)
if (data->clk[i].vddo_reg)
regulator_disable(data->clk[i].vddo_reg);
}
-
- return 0;
}
static const struct i2c_device_id si5341_id[] = {
@@ -1836,7 +1849,7 @@ static struct i2c_driver si5341_driver = {
.name = "si5341",
.of_match_table = clk_si5341_of_match,
},
- .probe_new = si5341_probe,
+ .probe = si5341_probe,
.remove = si5341_remove,
.id_table = si5341_id,
};
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
index b9f088c4ba2f..e755db545e2e 100644
--- a/drivers/clk/clk-si5351.c
+++ b/drivers/clk/clk-si5351.c
@@ -21,7 +21,7 @@
#include <linux/errno.h>
#include <linux/rational.h>
#include <linux/i2c.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/platform_data/si5351.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -206,7 +206,7 @@ static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
static const struct regmap_config si5351_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = 187,
.writeable_reg = si5351_regmap_is_writeable,
.volatile_reg = si5351_regmap_is_volatile,
@@ -442,11 +442,12 @@ static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
return (unsigned long)rate;
}
-static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si5351_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
+ unsigned long rate = req->rate;
unsigned long rfrac, denom, a, b, c;
unsigned long long lltmp;
@@ -456,18 +457,18 @@ static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
rate = SI5351_PLL_VCO_MAX;
/* determine integer part of feedback equation */
- a = rate / *parent_rate;
+ a = rate / req->best_parent_rate;
if (a < SI5351_PLL_A_MIN)
- rate = *parent_rate * SI5351_PLL_A_MIN;
+ rate = req->best_parent_rate * SI5351_PLL_A_MIN;
if (a > SI5351_PLL_A_MAX)
- rate = *parent_rate * SI5351_PLL_A_MAX;
+ rate = req->best_parent_rate * SI5351_PLL_A_MAX;
/* find best approximation for b/c = fVCO mod fIN */
denom = 1000 * 1000;
- lltmp = rate % (*parent_rate);
+ lltmp = rate % (req->best_parent_rate);
lltmp *= denom;
- do_div(lltmp, *parent_rate);
+ do_div(lltmp, req->best_parent_rate);
rfrac = (unsigned long)lltmp;
b = 0;
@@ -484,19 +485,20 @@ static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
hwdata->params.p1 -= 512;
/* recalculate rate by fIN * (a + b/c) */
- lltmp = *parent_rate;
+ lltmp = req->best_parent_rate;
lltmp *= b;
do_div(lltmp, c);
rate = (unsigned long)lltmp;
- rate += *parent_rate * a;
+ rate += req->best_parent_rate * a;
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw), a, b, c,
- *parent_rate, rate);
+ req->best_parent_rate, rate);
- return rate;
+ req->rate = rate;
+ return 0;
}
static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -504,6 +506,8 @@ static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
+ struct si5351_platform_data *pdata =
+ hwdata->drvdata->client->dev.platform_data;
u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
SI5351_PLLB_PARAMETERS;
@@ -516,9 +520,10 @@ static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
(hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
/* Do a pll soft reset on the affected pll */
- si5351_reg_write(hwdata->drvdata, SI5351_PLL_RESET,
- hwdata->num == 0 ? SI5351_PLL_RESET_A :
- SI5351_PLL_RESET_B);
+ if (pdata->pll_reset[hwdata->num])
+ si5351_reg_write(hwdata->drvdata, SI5351_PLL_RESET,
+ hwdata->num == 0 ? SI5351_PLL_RESET_A :
+ SI5351_PLL_RESET_B);
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
@@ -533,7 +538,7 @@ static const struct clk_ops si5351_pll_ops = {
.set_parent = si5351_pll_set_parent,
.get_parent = si5351_pll_get_parent,
.recalc_rate = si5351_pll_recalc_rate,
- .round_rate = si5351_pll_round_rate,
+ .determine_rate = si5351_pll_determine_rate,
.set_rate = si5351_pll_set_rate,
};
@@ -640,16 +645,17 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
return (unsigned long)rate;
}
-static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si5351_msynth_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
+ unsigned long rate = req->rate;
unsigned long long lltmp;
unsigned long a, b, c;
int divby4;
- /* multisync6-7 can only handle freqencies < 150MHz */
+ /* multisync6-7 can only handle frequencies < 150MHz */
if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
rate = SI5351_MULTISYNTH67_MAX_FREQ;
@@ -679,10 +685,10 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
b = 0;
c = 1;
- *parent_rate = a * rate;
+ req->best_parent_rate = a * rate;
} else if (hwdata->num >= 6) {
/* determine the closest integer divider */
- a = DIV_ROUND_CLOSEST(*parent_rate, rate);
+ a = DIV_ROUND_CLOSEST(req->best_parent_rate, rate);
if (a < SI5351_MULTISYNTH_A_MIN)
a = SI5351_MULTISYNTH_A_MIN;
if (a > SI5351_MULTISYNTH67_A_MAX)
@@ -700,7 +706,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
}
/* determine integer part of divider equation */
- a = *parent_rate / rate;
+ a = req->best_parent_rate / rate;
if (a < SI5351_MULTISYNTH_A_MIN)
a = SI5351_MULTISYNTH_A_MIN;
if (a > SI5351_MULTISYNTH_A_MAX)
@@ -708,7 +714,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
/* find best approximation for b/c = fVCO mod fOUT */
denom = 1000 * 1000;
- lltmp = (*parent_rate) % rate;
+ lltmp = req->best_parent_rate % rate;
lltmp *= denom;
do_div(lltmp, rate);
rfrac = (unsigned long)lltmp;
@@ -722,7 +728,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
}
/* recalculate rate by fOUT = fIN / (a + b/c) */
- lltmp = *parent_rate;
+ lltmp = req->best_parent_rate;
lltmp *= c;
do_div(lltmp, a * c + b);
rate = (unsigned long)lltmp;
@@ -747,9 +753,11 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw), a, b, c, divby4,
- *parent_rate, rate);
+ req->best_parent_rate, rate);
- return rate;
+ req->rate = rate;
+
+ return 0;
}
static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -789,7 +797,7 @@ static const struct clk_ops si5351_msynth_ops = {
.set_parent = si5351_msynth_set_parent,
.get_parent = si5351_msynth_get_parent,
.recalc_rate = si5351_msynth_recalc_rate,
- .round_rate = si5351_msynth_round_rate,
+ .determine_rate = si5351_msynth_determine_rate,
.set_rate = si5351_msynth_set_rate,
};
@@ -1032,18 +1040,19 @@ static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
return parent_rate >> rdiv;
}
-static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si5351_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
+ unsigned long rate = req->rate;
unsigned char rdiv;
- /* clkout6/7 can only handle output freqencies < 150MHz */
+ /* clkout6/7 can only handle output frequencies < 150MHz */
if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
rate = SI5351_CLKOUT67_MAX_FREQ;
- /* clkout freqency is 8kHz - 160MHz */
+ /* clkout frequency is 8kHz - 160MHz */
if (rate > SI5351_CLKOUT_MAX_FREQ)
rate = SI5351_CLKOUT_MAX_FREQ;
if (rate < SI5351_CLKOUT_MIN_FREQ)
@@ -1058,13 +1067,13 @@ static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
rdiv += 1;
rate *= 2;
}
- *parent_rate = rate;
+ req->best_parent_rate = rate;
} else {
unsigned long new_rate, new_err, err;
/* round to closed rdiv */
rdiv = SI5351_OUTPUT_CLK_DIV_1;
- new_rate = *parent_rate;
+ new_rate = req->best_parent_rate;
err = abs(new_rate - rate);
do {
new_rate >>= 1;
@@ -1075,14 +1084,15 @@ static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
err = new_err;
} while (1);
}
- rate = *parent_rate >> rdiv;
+ rate = req->best_parent_rate >> rdiv;
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw), (1 << rdiv),
- *parent_rate, rate);
+ req->best_parent_rate, rate);
- return rate;
+ req->rate = rate;
+ return 0;
}
static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -1142,7 +1152,7 @@ static const struct clk_ops si5351_clkout_ops = {
.set_parent = si5351_clkout_set_parent,
.get_parent = si5351_clkout_get_parent,
.recalc_rate = si5351_clkout_recalc_rate,
- .round_rate = si5351_clkout_round_rate,
+ .determine_rate = si5351_clkout_determine_rate,
.set_rate = si5351_clkout_set_rate,
};
@@ -1165,8 +1175,8 @@ static int si5351_dt_parse(struct i2c_client *client,
{
struct device_node *child, *np = client->dev.of_node;
struct si5351_platform_data *pdata;
- struct property *prop;
- const __be32 *p;
+ u32 array[4];
+ int sz, i;
int num = 0;
u32 val;
@@ -1181,20 +1191,24 @@ static int si5351_dt_parse(struct i2c_client *client,
* property silabs,pll-source : <num src>, [<..>]
* allow to selectively set pll source
*/
- of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
+ sz = of_property_read_variable_u32_array(np, "silabs,pll-source", array, 2, 4);
+ sz = (sz == -EINVAL) ? 0 : sz; /* Missing property is OK */
+ if (sz < 0)
+ return dev_err_probe(&client->dev, sz, "invalid pll-source\n");
+ if (sz % 2)
+ return dev_err_probe(&client->dev, -EINVAL,
+ "missing pll-source for pll %d\n", array[sz - 1]);
+
+ for (i = 0; i < sz; i += 2) {
+ num = array[i];
+ val = array[i + 1];
+
if (num >= 2) {
dev_err(&client->dev,
"invalid pll %d on pll-source prop\n", num);
return -EINVAL;
}
- p = of_prop_next_u32(prop, p, &val);
- if (!p) {
- dev_err(&client->dev,
- "missing pll-source for pll %d\n", num);
- return -EINVAL;
- }
-
switch (val) {
case 0:
pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
@@ -1215,6 +1229,49 @@ static int si5351_dt_parse(struct i2c_client *client,
}
}
+ /*
+ * Parse PLL reset mode. For compatibility with older device trees, the
+ * default is to always reset a PLL after setting its rate.
+ */
+ pdata->pll_reset[0] = true;
+ pdata->pll_reset[1] = true;
+
+ sz = of_property_read_variable_u32_array(np, "silabs,pll-reset-mode", array, 2, 4);
+ sz = (sz == -EINVAL) ? 0 : sz; /* Missing property is OK */
+ if (sz < 0)
+ return dev_err_probe(&client->dev, sz, "invalid pll-reset-mode\n");
+ if (sz % 2)
+ return dev_err_probe(&client->dev, -EINVAL,
+ "missing pll-reset-mode for pll %d\n", array[sz - 1]);
+
+ for (i = 0; i < sz; i += 2) {
+ num = array[i];
+ val = array[i + 1];
+
+ if (num >= 2) {
+ dev_err(&client->dev,
+ "invalid pll %d on pll-reset-mode prop\n", num);
+ return -EINVAL;
+ }
+
+
+ switch (val) {
+ case 0:
+ /* Reset PLL whenever its rate is adjusted */
+ pdata->pll_reset[num] = true;
+ break;
+ case 1:
+ /* Don't reset PLL whenever its rate is adjusted */
+ pdata->pll_reset[num] = false;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid pll-reset-mode %d for pll %d\n", val,
+ num);
+ return -EINVAL;
+ }
+ }
+
/* per clkout properties */
for_each_child_of_node(np, child) {
if (of_property_read_u32(child, "reg", &num)) {
@@ -1378,8 +1435,7 @@ MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
static int si5351_i2c_probe(struct i2c_client *client)
{
- const struct i2c_device_id *id = i2c_match_id(si5351_i2c_ids, client);
- enum si5351_variant variant = (enum si5351_variant)id->driver_data;
+ enum si5351_variant variant;
struct si5351_platform_data *pdata;
struct si5351_driver_data *drvdata;
struct clk_init_data init;
@@ -1387,6 +1443,7 @@ static int si5351_i2c_probe(struct i2c_client *client)
u8 num_parents, num_clocks;
int ret, n;
+ variant = (enum si5351_variant)(uintptr_t)i2c_get_match_data(client);
ret = si5351_dt_parse(client, variant);
if (ret)
return ret;
@@ -1641,8 +1698,8 @@ static int si5351_i2c_probe(struct i2c_client *client)
}
}
- ret = of_clk_add_hw_provider(client->dev.of_node, si53351_of_clk_get,
- drvdata);
+ ret = devm_of_clk_add_hw_provider(&client->dev, si53351_of_clk_get,
+ drvdata);
if (ret) {
dev_err(&client->dev, "unable to add clk provider\n");
return ret;
@@ -1651,20 +1708,12 @@ static int si5351_i2c_probe(struct i2c_client *client)
return 0;
}
-static int si5351_i2c_remove(struct i2c_client *client)
-{
- of_clk_del_provider(client->dev.of_node);
-
- return 0;
-}
-
static struct i2c_driver si5351_driver = {
.driver = {
.name = "si5351",
.of_match_table = of_match_ptr(si5351_dt_ids),
},
- .probe_new = si5351_i2c_probe,
- .remove = si5351_i2c_remove,
+ .probe = si5351_i2c_probe,
.id_table = si5351_i2c_ids,
};
module_i2c_driver(si5351_driver);
diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c
index 089786907641..09c06ecec1a5 100644
--- a/drivers/clk/clk-si544.c
+++ b/drivers/clk/clk-si544.c
@@ -39,7 +39,7 @@
/* Max freq depends on speed grade */
#define SI544_MIN_FREQ 200000U
-/* Si544 Internal oscilator runs at 55.05 MHz */
+/* Si544 Internal oscillator runs at 55.05 MHz */
#define FXO 55050000U
/* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */
@@ -56,17 +56,11 @@
#define DELTA_M_FRAC_NUM 19
#define DELTA_M_FRAC_DEN 20000
-enum si544_speed_grade {
- si544a,
- si544b,
- si544c,
-};
-
struct clk_si544 {
struct clk_hw hw;
struct regmap *regmap;
struct i2c_client *i2c_client;
- enum si544_speed_grade speed_grade;
+ unsigned long max_freq;
};
#define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw)
@@ -196,24 +190,10 @@ static int si544_set_muldiv(struct clk_si544 *data,
static bool is_valid_frequency(const struct clk_si544 *data,
unsigned long frequency)
{
- unsigned long max_freq = 0;
-
if (frequency < SI544_MIN_FREQ)
return false;
- switch (data->speed_grade) {
- case si544a:
- max_freq = 1500000000;
- break;
- case si544b:
- max_freq = 800000000;
- break;
- case si544c:
- max_freq = 350000000;
- break;
- }
-
- return frequency <= max_freq;
+ return frequency <= data->max_freq;
}
/* Calculate divider settings for a given frequency */
@@ -327,16 +307,16 @@ static unsigned long si544_recalc_rate(struct clk_hw *hw,
return si544_calc_rate(&settings);
}
-static long si544_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si544_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_si544 *data = to_clk_si544(hw);
- if (!is_valid_frequency(data, rate))
+ if (!is_valid_frequency(data, req->rate))
return -EINVAL;
/* The accuracy is less than 1 Hz, so any rate is possible */
- return rate;
+ return 0;
}
/* Calculates the maximum "small" change, 950 * rate / 1000000 */
@@ -428,7 +408,7 @@ static const struct clk_ops si544_clk_ops = {
.unprepare = si544_unprepare,
.is_prepared = si544_is_prepared,
.recalc_rate = si544_recalc_rate,
- .round_rate = si544_round_rate,
+ .determine_rate = si544_determine_rate,
.set_rate = si544_set_rate,
};
@@ -446,24 +426,15 @@ static bool si544_regmap_is_volatile(struct device *dev, unsigned int reg)
static const struct regmap_config si544_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = SI544_REG_PAGE_SELECT,
.volatile_reg = si544_regmap_is_volatile,
};
-static const struct i2c_device_id si544_id[] = {
- { "si544a", si544a },
- { "si544b", si544b },
- { "si544c", si544c },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, si544_id);
-
static int si544_probe(struct i2c_client *client)
{
struct clk_si544 *data;
struct clk_init_data init;
- const struct i2c_device_id *id = i2c_match_id(si544_id, client);
int err;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
@@ -475,7 +446,7 @@ static int si544_probe(struct i2c_client *client)
init.num_parents = 0;
data->hw.init = &init;
data->i2c_client = client;
- data->speed_grade = id->driver_data;
+ data->max_freq = (uintptr_t)i2c_get_match_data(client);
if (of_property_read_string(client->dev.of_node, "clock-output-names",
&init.name))
@@ -507,11 +478,19 @@ static int si544_probe(struct i2c_client *client)
return 0;
}
+static const struct i2c_device_id si544_id[] = {
+ { "si544a", 1500000000 },
+ { "si544b", 800000000 },
+ { "si544c", 350000000 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si544_id);
+
static const struct of_device_id clk_si544_of_match[] = {
- { .compatible = "silabs,si544a" },
- { .compatible = "silabs,si544b" },
- { .compatible = "silabs,si544c" },
- { },
+ { .compatible = "silabs,si544a", .data = (void *)1500000000 },
+ { .compatible = "silabs,si544b", .data = (void *)800000000 },
+ { .compatible = "silabs,si544c", .data = (void *)350000000 },
+ { }
};
MODULE_DEVICE_TABLE(of, clk_si544_of_match);
@@ -520,7 +499,7 @@ static struct i2c_driver si544_driver = {
.name = "si544",
.of_match_table = clk_si544_of_match,
},
- .probe_new = si544_probe,
+ .probe = si544_probe,
.id_table = si544_id,
};
module_i2c_driver(si544_driver);
diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c
index 1ff8f32f734d..b0b1830dd430 100644
--- a/drivers/clk/clk-si570.c
+++ b/drivers/clk/clk-si570.c
@@ -50,11 +50,21 @@
#define SI570_FREEZE_DCO (1 << 4)
/**
+ * struct clk_si570_info:
+ * @max_freq: Maximum frequency for this device
+ * @has_temperature_stability: Device support temperature stability
+ */
+struct clk_si570_info {
+ u64 max_freq;
+ bool has_temperature_stability;
+};
+
+/**
* struct clk_si570:
* @hw: Clock hw struct
* @regmap: Device's regmap
- * @div_offset: Rgister offset for dividers
- * @max_freq: Maximum frequency for this device
+ * @div_offset: Register offset for dividers
+ * @info: Device info
* @fxtal: Factory xtal frequency
* @n1: Clock divider N1
* @hs_div: Clock divider HSDIV
@@ -66,7 +76,7 @@ struct clk_si570 {
struct clk_hw hw;
struct regmap *regmap;
unsigned int div_offset;
- u64 max_freq;
+ const struct clk_si570_info *info;
u64 fxtal;
unsigned int n1;
unsigned int hs_div;
@@ -76,11 +86,6 @@ struct clk_si570 {
};
#define to_clk_si570(_hw) container_of(_hw, struct clk_si570, hw)
-enum clk_si570_variant {
- si57x,
- si59x
-};
-
/**
* si570_get_divs() - Read clock dividers from HW
* @data: Pointer to struct clk_si570
@@ -176,7 +181,7 @@ static int si570_update_rfreq(struct clk_si570 *data)
}
/**
- * si570_calc_divs() - Caluclate clock dividers
+ * si570_calc_divs() - Calculate clock dividers
* @frequency: Target frequency
* @data: Driver data structure
* @out_rfreq: RFREG fractional multiplier (output)
@@ -241,34 +246,40 @@ static unsigned long si570_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long si570_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int si570_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int err;
u64 rfreq;
unsigned int n1, hs_div;
struct clk_si570 *data = to_clk_si570(hw);
- if (!rate)
+ if (!req->rate) {
+ req->rate = 0;
+
return 0;
+ }
- if (div64_u64(abs(rate - data->frequency) * 10000LL,
+ if (div64_u64(abs(req->rate - data->frequency) * 10000LL,
data->frequency) < 35) {
- rfreq = div64_u64((data->rfreq * rate) +
- div64_u64(data->frequency, 2), data->frequency);
+ rfreq = div64_u64((data->rfreq * req->rate) +
+ div64_u64(data->frequency, 2),
+ data->frequency);
n1 = data->n1;
hs_div = data->hs_div;
} else {
- err = si570_calc_divs(rate, data, &rfreq, &n1, &hs_div);
+ err = si570_calc_divs(req->rate, data, &rfreq, &n1, &hs_div);
if (err) {
dev_err(&data->i2c_client->dev,
"unable to round rate\n");
+ req->rate = 0;
+
return 0;
}
}
- return rate;
+ return 0;
}
/**
@@ -341,7 +352,7 @@ static int si570_set_rate(struct clk_hw *hw, unsigned long rate,
struct i2c_client *client = data->i2c_client;
int err;
- if (rate < SI570_MIN_FREQ || rate > data->max_freq) {
+ if (rate < SI570_MIN_FREQ || rate > data->info->max_freq) {
dev_err(&client->dev,
"requested frequency %lu Hz is out of range\n", rate);
return -EINVAL;
@@ -363,7 +374,7 @@ static int si570_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops si570_clk_ops = {
.recalc_rate = si570_recalc_rate,
- .round_rate = si570_round_rate,
+ .determine_rate = si570_determine_rate,
.set_rate = si570_set_rate,
};
@@ -392,30 +403,19 @@ static bool si570_regmap_is_writeable(struct device *dev, unsigned int reg)
static const struct regmap_config si570_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = 137,
.writeable_reg = si570_regmap_is_writeable,
.volatile_reg = si570_regmap_is_volatile,
};
-static const struct i2c_device_id si570_id[] = {
- { "si570", si57x },
- { "si571", si57x },
- { "si598", si59x },
- { "si599", si59x },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, si570_id);
-
static int si570_probe(struct i2c_client *client)
{
struct clk_si570 *data;
struct clk_init_data init;
- const struct i2c_device_id *id = i2c_match_id(si570_id, client);
u32 initial_fout, factory_fout, stability;
bool skip_recall;
int err;
- enum clk_si570_variant variant = id->driver_data;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -427,7 +427,8 @@ static int si570_probe(struct i2c_client *client)
data->hw.init = &init;
data->i2c_client = client;
- if (variant == si57x) {
+ data->info = i2c_get_match_data(client);
+ if (data->info->has_temperature_stability) {
err = of_property_read_u32(client->dev.of_node,
"temperature-stability", &stability);
if (err) {
@@ -438,10 +439,6 @@ static int si570_probe(struct i2c_client *client)
/* adjust register offsets for 7ppm devices */
if (stability == 7)
data->div_offset = SI570_DIV_OFFSET_7PPM;
-
- data->max_freq = SI570_MAX_FREQ;
- } else {
- data->max_freq = SI598_MAX_FREQ;
}
if (of_property_read_string(client->dev.of_node, "clock-output-names",
@@ -474,8 +471,8 @@ static int si570_probe(struct i2c_client *client)
dev_err(&client->dev, "clock registration failed\n");
return err;
}
- err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
- &data->hw);
+ err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get,
+ &data->hw);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
@@ -485,10 +482,8 @@ static int si570_probe(struct i2c_client *client)
if (!of_property_read_u32(client->dev.of_node, "clock-frequency",
&initial_fout)) {
err = clk_set_rate(data->hw.clk, initial_fout);
- if (err) {
- of_clk_del_provider(client->dev.of_node);
+ if (err)
return err;
- }
}
/* Display a message indicating that we've successfully registered */
@@ -498,18 +493,30 @@ static int si570_probe(struct i2c_client *client)
return 0;
}
-static int si570_remove(struct i2c_client *client)
-{
- of_clk_del_provider(client->dev.of_node);
- return 0;
-}
+static const struct clk_si570_info clk_si570_info = {
+ .max_freq = SI570_MAX_FREQ,
+ .has_temperature_stability = true,
+};
+
+static const struct clk_si570_info clk_si590_info = {
+ .max_freq = SI598_MAX_FREQ,
+};
+
+static const struct i2c_device_id si570_id[] = {
+ { "si570", (kernel_ulong_t)&clk_si570_info },
+ { "si571", (kernel_ulong_t)&clk_si570_info },
+ { "si598", (kernel_ulong_t)&clk_si590_info },
+ { "si599", (kernel_ulong_t)&clk_si590_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si570_id);
static const struct of_device_id clk_si570_of_match[] = {
- { .compatible = "silabs,si570" },
- { .compatible = "silabs,si571" },
- { .compatible = "silabs,si598" },
- { .compatible = "silabs,si599" },
- { },
+ { .compatible = "silabs,si570", .data = &clk_si570_info },
+ { .compatible = "silabs,si571", .data = &clk_si570_info },
+ { .compatible = "silabs,si598", .data = &clk_si590_info },
+ { .compatible = "silabs,si599", .data = &clk_si590_info },
+ { }
};
MODULE_DEVICE_TABLE(of, clk_si570_of_match);
@@ -518,8 +525,7 @@ static struct i2c_driver si570_driver = {
.name = "si570",
.of_match_table = clk_si570_of_match,
},
- .probe_new = si570_probe,
- .remove = si570_remove,
+ .probe = si570_probe,
.id_table = si570_id,
};
module_i2c_driver(si570_driver);
diff --git a/drivers/clk/clk-sp7021.c b/drivers/clk/clk-sp7021.c
new file mode 100644
index 000000000000..36528a71a2e6
--- /dev/null
+++ b/drivers/clk/clk-sp7021.c
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ * All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/bitfield.h>
+#include <linux/hw_bitfield.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/sunplus,sp7021-clkc.h>
+
+/* special div_width values for PLLTV/PLLA */
+#define DIV_TV 33
+#define DIV_A 34
+
+/* PLLTV parameters */
+enum {
+ SEL_FRA,
+ SDM_MOD,
+ PH_SEL,
+ NFRA,
+ DIVR,
+ DIVN,
+ DIVM,
+ P_MAX
+};
+
+#define MASK_SEL_FRA GENMASK(1, 1)
+#define MASK_SDM_MOD GENMASK(2, 2)
+#define MASK_PH_SEL GENMASK(4, 4)
+#define MASK_NFRA GENMASK(12, 6)
+#define MASK_DIVR GENMASK(8, 7)
+#define MASK_DIVN GENMASK(7, 0)
+#define MASK_DIVM GENMASK(14, 8)
+
+struct sp_pll {
+ struct clk_hw hw;
+ void __iomem *reg;
+ spinlock_t lock; /* lock for reg */
+ int div_shift;
+ int div_width;
+ int pd_bit; /* power down bit idx */
+ int bp_bit; /* bypass bit idx */
+ unsigned long brate; /* base rate, TODO: replace brate with muldiv */
+ u32 p[P_MAX]; /* for hold PLLTV/PLLA parameters */
+};
+
+#define to_sp_pll(_hw) container_of(_hw, struct sp_pll, hw)
+
+struct sp_clk_gate_info {
+ u16 reg; /* reg_index_shift */
+ u16 ext_parent; /* parent is extclk */
+};
+
+static const struct sp_clk_gate_info sp_clk_gates[] = {
+ { 0x02 },
+ { 0x05 },
+ { 0x06 },
+ { 0x07 },
+ { 0x09 },
+ { 0x0b, 1 },
+ { 0x0f, 1 },
+ { 0x14 },
+ { 0x15 },
+ { 0x16 },
+ { 0x17 },
+ { 0x18, 1 },
+ { 0x19, 1 },
+ { 0x1a, 1 },
+ { 0x1b, 1 },
+ { 0x1c, 1 },
+ { 0x1d, 1 },
+ { 0x1e },
+ { 0x1f, 1 },
+ { 0x20 },
+ { 0x21 },
+ { 0x22 },
+ { 0x23 },
+ { 0x24 },
+ { 0x25 },
+ { 0x26 },
+ { 0x2a },
+ { 0x2b },
+ { 0x2d },
+ { 0x2e },
+ { 0x30 },
+ { 0x31 },
+ { 0x32 },
+ { 0x33 },
+ { 0x3d },
+ { 0x3e },
+ { 0x3f },
+ { 0x42 },
+ { 0x44 },
+ { 0x4b },
+ { 0x4c },
+ { 0x4d },
+ { 0x4e },
+ { 0x4f },
+ { 0x50 },
+ { 0x55 },
+ { 0x60 },
+ { 0x61 },
+ { 0x6a },
+ { 0x73 },
+ { 0x86 },
+ { 0x8a },
+ { 0x8b },
+ { 0x8d },
+ { 0x8e },
+ { 0x8f },
+ { 0x90 },
+ { 0x92 },
+ { 0x93 },
+ { 0x95 },
+ { 0x96 },
+ { 0x97 },
+ { 0x98 },
+ { 0x99 },
+};
+
+#define _M 1000000UL
+#define F_27M (27 * _M)
+
+/*********************************** PLL_TV **********************************/
+
+/* TODO: set proper FVCO range */
+#define FVCO_MIN (100 * _M)
+#define FVCO_MAX (200 * _M)
+
+#define F_MIN (FVCO_MIN / 8)
+#define F_MAX (FVCO_MAX)
+
+static long plltv_integer_div(struct sp_pll *clk, unsigned long freq)
+{
+ /* valid m values: 27M must be divisible by m */
+ static const u32 m_table[] = {
+ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32
+ };
+ u32 m, n, r;
+ unsigned long fvco, nf;
+ long ret;
+
+ freq = clamp(freq, F_MIN, F_MAX);
+
+ /* DIVR 0~3 */
+ for (r = 0; r <= 3; r++) {
+ fvco = freq << r;
+ if (fvco <= FVCO_MAX)
+ break;
+ }
+
+ /* DIVM */
+ for (m = 0; m < ARRAY_SIZE(m_table); m++) {
+ nf = fvco * m_table[m];
+ n = nf / F_27M;
+ if ((n * F_27M) == nf)
+ break;
+ }
+ if (m >= ARRAY_SIZE(m_table)) {
+ ret = -EINVAL;
+ goto err_not_found;
+ }
+
+ /* save parameters */
+ clk->p[SEL_FRA] = 0;
+ clk->p[DIVR] = r;
+ clk->p[DIVN] = n;
+ clk->p[DIVM] = m_table[m];
+
+ return freq;
+
+err_not_found:
+ pr_err("%s: %s freq:%lu not found a valid setting\n",
+ __func__, clk_hw_get_name(&clk->hw), freq);
+
+ return ret;
+}
+
+/* parameters for PLLTV fractional divider */
+static const u32 pt[][5] = {
+ /* conventional fractional */
+ {
+ 1, /* factor */
+ 5, /* 5 * p0 (nint) */
+ 1, /* 1 * p0 */
+ F_27M, /* F_27M / p0 */
+ 1, /* p0 / p2 */
+ },
+ /* phase rotation */
+ {
+ 10, /* factor */
+ 54, /* 5.4 * p0 (nint) */
+ 2, /* 0.2 * p0 */
+ F_27M / 10, /* F_27M / p0 */
+ 5, /* p0 / p2 */
+ },
+};
+
+static const u32 sdm_mod_vals[] = { 91, 55 };
+
+static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
+{
+ u32 m, r;
+ u32 nint, nfra;
+ u32 df_quotient_min = 210000000;
+ u32 df_remainder_min = 0;
+ unsigned long fvco, nf, f, fout = 0;
+ int sdm, ph;
+
+ freq = clamp(freq, F_MIN, F_MAX);
+
+ /* DIVR 0~3 */
+ for (r = 0; r <= 3; r++) {
+ fvco = freq << r;
+ if (fvco <= FVCO_MAX)
+ break;
+ }
+ f = F_27M >> r;
+
+ /* PH_SEL */
+ for (ph = ARRAY_SIZE(pt) - 1; ph >= 0; ph--) {
+ const u32 *pp = pt[ph];
+
+ /* SDM_MOD */
+ for (sdm = 0; sdm < ARRAY_SIZE(sdm_mod_vals); sdm++) {
+ u32 mod = sdm_mod_vals[sdm];
+
+ /* DIVM 1~32 */
+ for (m = 1; m <= 32; m++) {
+ u32 df; /* diff freq */
+ u32 df_quotient, df_remainder;
+
+ nf = fvco * m;
+ nint = nf / pp[3];
+
+ if (nint < pp[1])
+ continue;
+ if (nint > pp[1])
+ break;
+
+ nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
+ if (nfra) {
+ u32 df0 = f * (nint + pp[2]) / pp[0];
+ u32 df1 = f * (mod - nfra) / mod / pp[4];
+
+ df = df0 - df1;
+ } else {
+ df = f * (nint) / pp[0];
+ }
+
+ df_quotient = df / m;
+ df_remainder = ((df % m) * 1000) / m;
+
+ if (freq > df_quotient) {
+ df_quotient = freq - df_quotient - 1;
+ df_remainder = 1000 - df_remainder;
+ } else {
+ df_quotient = df_quotient - freq;
+ }
+
+ if (df_quotient_min > df_quotient ||
+ (df_quotient_min == df_quotient &&
+ df_remainder_min > df_remainder)) {
+ /* found a closer freq, save parameters */
+ clk->p[SEL_FRA] = 1;
+ clk->p[SDM_MOD] = sdm;
+ clk->p[PH_SEL] = ph;
+ clk->p[NFRA] = nfra;
+ clk->p[DIVR] = r;
+ clk->p[DIVM] = m;
+
+ fout = df / m;
+ df_quotient_min = df_quotient;
+ df_remainder_min = df_remainder;
+ }
+ }
+ }
+ }
+
+ if (!fout) {
+ pr_err("%s: %s freq:%lu not found a valid setting\n",
+ __func__, clk_hw_get_name(&clk->hw), freq);
+ return -EINVAL;
+ }
+
+ return fout;
+}
+
+static long plltv_div(struct sp_pll *clk, unsigned long freq)
+{
+ if (freq % 100)
+ return plltv_fractional_div(clk, freq);
+
+ return plltv_integer_div(clk, freq);
+}
+
+static int plltv_set_rate(struct sp_pll *clk)
+{
+ unsigned long flags;
+ u32 r0, r1, r2;
+
+ r0 = BIT(clk->bp_bit + 16);
+ r0 |= FIELD_PREP_WM16(MASK_SEL_FRA, clk->p[SEL_FRA]);
+ r0 |= FIELD_PREP_WM16(MASK_SDM_MOD, clk->p[SDM_MOD]);
+ r0 |= FIELD_PREP_WM16(MASK_PH_SEL, clk->p[PH_SEL]);
+ r0 |= FIELD_PREP_WM16(MASK_NFRA, clk->p[NFRA]);
+
+ r1 = FIELD_PREP_WM16(MASK_DIVR, clk->p[DIVR]);
+
+ r2 = FIELD_PREP_WM16(MASK_DIVN, clk->p[DIVN] - 1);
+ r2 |= FIELD_PREP_WM16(MASK_DIVM, clk->p[DIVM] - 1);
+
+ spin_lock_irqsave(&clk->lock, flags);
+ writel(r0, clk->reg);
+ writel(r1, clk->reg + 4);
+ writel(r2, clk->reg + 8);
+ spin_unlock_irqrestore(&clk->lock, flags);
+
+ return 0;
+}
+
+/*********************************** PLL_A ***********************************/
+
+/* from Q628_PLLs_REG_setting.xlsx */
+static const struct {
+ u32 rate;
+ u32 regs[5];
+} pa[] = {
+ {
+ .rate = 135475200,
+ .regs = {
+ 0x4801,
+ 0x02df,
+ 0x248f,
+ 0x0211,
+ 0x33e9
+ }
+ },
+ {
+ .rate = 147456000,
+ .regs = {
+ 0x4801,
+ 0x1adf,
+ 0x2490,
+ 0x0349,
+ 0x33e9
+ }
+ },
+ {
+ .rate = 196608000,
+ .regs = {
+ 0x4801,
+ 0x42ef,
+ 0x2495,
+ 0x01c6,
+ 0x33e9
+ }
+ },
+};
+
+static int plla_set_rate(struct sp_pll *clk)
+{
+ const u32 *pp = pa[clk->p[0]].regs;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&clk->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(pa->regs); i++)
+ writel(0xffff0000 | pp[i], clk->reg + (i * 4));
+ spin_unlock_irqrestore(&clk->lock, flags);
+
+ return 0;
+}
+
+static long plla_round_rate(struct sp_pll *clk, unsigned long rate)
+{
+ int i = ARRAY_SIZE(pa);
+
+ while (--i) {
+ if (rate >= pa[i].rate)
+ break;
+ }
+ clk->p[0] = i;
+
+ return pa[i].rate;
+}
+
+/********************************** SP_PLL ***********************************/
+
+static long sp_pll_calc_div(struct sp_pll *clk, unsigned long rate)
+{
+ u32 fbdiv;
+ u32 max = 1 << clk->div_width;
+
+ fbdiv = DIV_ROUND_CLOSEST(rate, clk->brate);
+ if (fbdiv > max)
+ fbdiv = max;
+
+ return fbdiv;
+}
+
+static int sp_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct sp_pll *clk = to_sp_pll(hw);
+ long ret;
+
+ if (req->rate == req->best_parent_rate) {
+ ret = req->best_parent_rate; /* bypass */
+ } else if (clk->div_width == DIV_A) {
+ ret = plla_round_rate(clk, req->rate);
+ } else if (clk->div_width == DIV_TV) {
+ ret = plltv_div(clk, req->rate);
+ if (ret < 0)
+ ret = req->best_parent_rate;
+ } else {
+ ret = sp_pll_calc_div(clk, req->rate) * clk->brate;
+ }
+
+ req->rate = ret;
+
+ return 0;
+}
+
+static unsigned long sp_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct sp_pll *clk = to_sp_pll(hw);
+ u32 reg = readl(clk->reg);
+ unsigned long ret;
+
+ if (reg & BIT(clk->bp_bit)) {
+ ret = prate; /* bypass */
+ } else if (clk->div_width == DIV_A) {
+ ret = pa[clk->p[0]].rate;
+ } else if (clk->div_width == DIV_TV) {
+ u32 m, r, reg2;
+
+ r = FIELD_GET(MASK_DIVR, readl(clk->reg + 4));
+ reg2 = readl(clk->reg + 8);
+ m = FIELD_GET(MASK_DIVM, reg2) + 1;
+
+ if (reg & MASK_SEL_FRA) {
+ /* fractional divider */
+ u32 sdm = FIELD_GET(MASK_SDM_MOD, reg);
+ u32 ph = FIELD_GET(MASK_PH_SEL, reg);
+ u32 nfra = FIELD_GET(MASK_NFRA, reg);
+ const u32 *pp = pt[ph];
+ unsigned long r0, r1;
+
+ ret = prate >> r;
+ r0 = ret * (pp[1] + pp[2]) / pp[0];
+ r1 = ret * (sdm_mod_vals[sdm] - nfra) / sdm_mod_vals[sdm] / pp[4];
+ ret = (r0 - r1) / m;
+ } else {
+ /* integer divider */
+ u32 n = FIELD_GET(MASK_DIVN, reg2) + 1;
+
+ ret = (prate / m * n) >> r;
+ }
+ } else {
+ u32 fbdiv = ((reg >> clk->div_shift) & ((1 << clk->div_width) - 1)) + 1;
+
+ ret = clk->brate * fbdiv;
+ }
+
+ return ret;
+}
+
+static int sp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct sp_pll *clk = to_sp_pll(hw);
+ unsigned long flags;
+ u32 reg;
+
+ reg = BIT(clk->bp_bit + 16); /* HIWORD_MASK */
+
+ if (rate == prate) {
+ reg |= BIT(clk->bp_bit); /* bypass */
+ } else if (clk->div_width == DIV_A) {
+ return plla_set_rate(clk);
+ } else if (clk->div_width == DIV_TV) {
+ return plltv_set_rate(clk);
+ } else if (clk->div_width) {
+ u32 fbdiv = sp_pll_calc_div(clk, rate);
+ u32 mask = GENMASK(clk->div_shift + clk->div_width - 1, clk->div_shift);
+
+ reg |= mask << 16;
+ reg |= ((fbdiv - 1) << clk->div_shift) & mask;
+ }
+
+ spin_lock_irqsave(&clk->lock, flags);
+ writel(reg, clk->reg);
+ spin_unlock_irqrestore(&clk->lock, flags);
+
+ return 0;
+}
+
+static int sp_pll_enable(struct clk_hw *hw)
+{
+ struct sp_pll *clk = to_sp_pll(hw);
+
+ writel(BIT(clk->pd_bit + 16) | BIT(clk->pd_bit), clk->reg);
+
+ return 0;
+}
+
+static void sp_pll_disable(struct clk_hw *hw)
+{
+ struct sp_pll *clk = to_sp_pll(hw);
+
+ writel(BIT(clk->pd_bit + 16), clk->reg);
+}
+
+static int sp_pll_is_enabled(struct clk_hw *hw)
+{
+ struct sp_pll *clk = to_sp_pll(hw);
+
+ return readl(clk->reg) & BIT(clk->pd_bit);
+}
+
+static const struct clk_ops sp_pll_ops = {
+ .enable = sp_pll_enable,
+ .disable = sp_pll_disable,
+ .is_enabled = sp_pll_is_enabled,
+ .determine_rate = sp_pll_determine_rate,
+ .recalc_rate = sp_pll_recalc_rate,
+ .set_rate = sp_pll_set_rate
+};
+
+static const struct clk_ops sp_pll_sub_ops = {
+ .enable = sp_pll_enable,
+ .disable = sp_pll_disable,
+ .is_enabled = sp_pll_is_enabled,
+ .recalc_rate = sp_pll_recalc_rate,
+};
+
+static struct clk_hw *sp_pll_register(struct device *dev, const char *name,
+ const struct clk_parent_data *parent_data,
+ void __iomem *reg, int pd_bit, int bp_bit,
+ unsigned long brate, int shift, int width,
+ unsigned long flags)
+{
+ struct sp_pll *pll;
+ struct clk_hw *hw;
+ struct clk_init_data initd = {
+ .name = name,
+ .parent_data = parent_data,
+ .ops = (bp_bit >= 0) ? &sp_pll_ops : &sp_pll_sub_ops,
+ .num_parents = 1,
+ .flags = flags,
+ };
+ int ret;
+
+ pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->hw.init = &initd;
+ pll->reg = reg;
+ pll->pd_bit = pd_bit;
+ pll->bp_bit = bp_bit;
+ pll->brate = brate;
+ pll->div_shift = shift;
+ pll->div_width = width;
+ spin_lock_init(&pll->lock);
+
+ hw = &pll->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return hw;
+}
+
+#define PLLA_CTL (pll_base + 0x1c)
+#define PLLE_CTL (pll_base + 0x30)
+#define PLLF_CTL (pll_base + 0x34)
+#define PLLTV_CTL (pll_base + 0x38)
+
+static int sp7021_clk_probe(struct platform_device *pdev)
+{
+ static const u32 sp_clken[] = {
+ 0x67ef, 0x03ff, 0xff03, 0xfff0, 0x0004, /* G0.1~5 */
+ 0x0000, 0x8000, 0xffff, 0x0040, 0x0000, /* G0.6~10 */
+ };
+ static struct clk_parent_data pd_ext, pd_sys, pd_e;
+ struct device *dev = &pdev->dev;
+ void __iomem *clk_base, *pll_base, *sys_base;
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_hw **hws;
+ int i;
+
+ clk_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(clk_base))
+ return PTR_ERR(clk_base);
+ pll_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(pll_base))
+ return PTR_ERR(pll_base);
+ sys_base = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(sys_base))
+ return PTR_ERR(sys_base);
+
+ /* enable default clks */
+ for (i = 0; i < ARRAY_SIZE(sp_clken); i++)
+ writel((sp_clken[i] << 16) | sp_clken[i], clk_base + i * 4);
+
+ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, CLK_MAX),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+ clk_data->num = CLK_MAX;
+
+ hws = clk_data->hws;
+ pd_ext.index = 0;
+
+ /* PLLs */
+ hws[PLL_A] = sp_pll_register(dev, "plla", &pd_ext, PLLA_CTL,
+ 11, 12, 27000000, 0, DIV_A, 0);
+ if (IS_ERR(hws[PLL_A]))
+ return PTR_ERR(hws[PLL_A]);
+
+ hws[PLL_E] = sp_pll_register(dev, "plle", &pd_ext, PLLE_CTL,
+ 6, 2, 50000000, 0, 0, 0);
+ if (IS_ERR(hws[PLL_E]))
+ return PTR_ERR(hws[PLL_E]);
+ pd_e.hw = hws[PLL_E];
+ hws[PLL_E_2P5] = sp_pll_register(dev, "plle_2p5", &pd_e, PLLE_CTL,
+ 13, -1, 2500000, 0, 0, 0);
+ if (IS_ERR(hws[PLL_E_2P5]))
+ return PTR_ERR(hws[PLL_E_2P5]);
+ hws[PLL_E_25] = sp_pll_register(dev, "plle_25", &pd_e, PLLE_CTL,
+ 12, -1, 25000000, 0, 0, 0);
+ if (IS_ERR(hws[PLL_E_25]))
+ return PTR_ERR(hws[PLL_E_25]);
+ hws[PLL_E_112P5] = sp_pll_register(dev, "plle_112p5", &pd_e, PLLE_CTL,
+ 11, -1, 112500000, 0, 0, 0);
+ if (IS_ERR(hws[PLL_E_112P5]))
+ return PTR_ERR(hws[PLL_E_112P5]);
+
+ hws[PLL_F] = sp_pll_register(dev, "pllf", &pd_ext, PLLF_CTL,
+ 0, 10, 13500000, 1, 4, 0);
+ if (IS_ERR(hws[PLL_F]))
+ return PTR_ERR(hws[PLL_F]);
+
+ hws[PLL_TV] = sp_pll_register(dev, "plltv", &pd_ext, PLLTV_CTL,
+ 0, 15, 27000000, 0, DIV_TV, 0);
+ if (IS_ERR(hws[PLL_TV]))
+ return PTR_ERR(hws[PLL_TV]);
+ hws[PLL_TV_A] = devm_clk_hw_register_divider(dev, "plltv_a", "plltv", 0,
+ PLLTV_CTL + 4, 5, 1,
+ CLK_DIVIDER_POWER_OF_TWO,
+ &to_sp_pll(hws[PLL_TV])->lock);
+ if (IS_ERR(hws[PLL_TV_A]))
+ return PTR_ERR(hws[PLL_TV_A]);
+
+ /* system clock, should not be disabled */
+ hws[PLL_SYS] = sp_pll_register(dev, "pllsys", &pd_ext, sys_base,
+ 10, 9, 13500000, 0, 4, CLK_IS_CRITICAL);
+ if (IS_ERR(hws[PLL_SYS]))
+ return PTR_ERR(hws[PLL_SYS]);
+ pd_sys.hw = hws[PLL_SYS];
+
+ /* gates */
+ for (i = 0; i < ARRAY_SIZE(sp_clk_gates); i++) {
+ char name[10];
+ u32 j = sp_clk_gates[i].reg;
+ struct clk_parent_data *pd = sp_clk_gates[i].ext_parent ? &pd_ext : &pd_sys;
+
+ sprintf(name, "%02d_0x%02x", i, j);
+ hws[i] = devm_clk_hw_register_gate_parent_data(dev, name, pd, 0,
+ clk_base + (j >> 4) * 4,
+ j & 0x0f,
+ CLK_GATE_HIWORD_MASK,
+ NULL);
+ if (IS_ERR(hws[i]))
+ return PTR_ERR(hws[i]);
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+}
+
+static const struct of_device_id sp7021_clk_dt_ids[] = {
+ { .compatible = "sunplus,sp7021-clkc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sp7021_clk_dt_ids);
+
+static struct platform_driver sp7021_clk_driver = {
+ .probe = sp7021_clk_probe,
+ .driver = {
+ .name = "sp7021-clk",
+ .of_match_table = sp7021_clk_dt_ids,
+ },
+};
+module_platform_driver(sp7021_clk_driver);
+
+MODULE_AUTHOR("Sunplus Technology");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Clock driver for Sunplus SP7021 SoC");
diff --git a/drivers/clk/clk-sparx5.c b/drivers/clk/clk-sparx5.c
index 0fad0c1a0186..b2facc9c95d4 100644
--- a/drivers/clk/clk-sparx5.c
+++ b/drivers/clk/clk-sparx5.c
@@ -213,19 +213,21 @@ static unsigned long s5_pll_recalc_rate(struct clk_hw *hw,
return conf.freq;
}
-static long s5_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int s5_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct s5_pll_conf conf;
- return s5_calc_params(rate, *parent_rate, &conf);
+ req->rate = s5_calc_params(req->rate, req->best_parent_rate, &conf);
+
+ return 0;
}
static const struct clk_ops s5_pll_ops = {
.enable = s5_pll_enable,
.disable = s5_pll_disable,
.set_rate = s5_pll_set_rate,
- .round_rate = s5_pll_round_rate,
+ .determine_rate = s5_pll_determine_rate,
.recalc_rate = s5_pll_recalc_rate,
};
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 473dfe632cc5..b5d4d48432a0 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -5,6 +5,7 @@
* Inspired by clk-asm9260.c .
*/
+#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -18,7 +19,7 @@
#include <linux/mfd/syscon.h>
/*
- * Include list of clocks wich are not derived from system clock (SYSCLOCK)
+ * Include list of clocks which are not derived from system clock (SYSCLOCK)
* The index of these clocks is the secondary index of DT bindings
*
*/
@@ -34,11 +35,20 @@
#define STM32F4_RCC_APB2ENR 0x44
#define STM32F4_RCC_BDCR 0x70
#define STM32F4_RCC_CSR 0x74
+#define STM32F4_RCC_SSCGR 0x80
#define STM32F4_RCC_PLLI2SCFGR 0x84
#define STM32F4_RCC_PLLSAICFGR 0x88
#define STM32F4_RCC_DCKCFGR 0x8c
#define STM32F7_RCC_DCKCFGR2 0x90
+#define STM32F4_RCC_PLLCFGR_N_MASK GENMASK(14, 6)
+
+#define STM32F4_RCC_SSCGR_SSCGEN BIT(31)
+#define STM32F4_RCC_SSCGR_SPREADSEL BIT(30)
+#define STM32F4_RCC_SSCGR_RESERVED_MASK GENMASK(29, 28)
+#define STM32F4_RCC_SSCGR_INCSTEP_MASK GENMASK(27, 13)
+#define STM32F4_RCC_SSCGR_MODPER_MASK GENMASK(12, 0)
+
#define NONE -1
#define NO_IDX NONE
#define NO_MUX NONE
@@ -364,6 +374,16 @@ static const struct stm32f4_gate_data stm32f769_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 30, "mdio", "apb2_div" },
};
+enum stm32f4_pll_ssc_mod_type {
+ STM32F4_PLL_SSC_CENTER_SPREAD,
+ STM32F4_PLL_SSC_DOWN_SPREAD,
+};
+
+static const char * const stm32f4_ssc_mod_methods[] __initconst = {
+ [STM32F4_PLL_SSC_DOWN_SPREAD] = "down-spread",
+ [STM32F4_PLL_SSC_CENTER_SPREAD] = "center-spread",
+};
+
/*
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
* have gate bits associated with them. Its combined hweight is 71.
@@ -423,8 +443,8 @@ static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_apb_mul_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_apb_mul *am = to_clk_apb_mul(hw);
unsigned long mult = 1;
@@ -433,12 +453,14 @@ static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate,
mult = 2;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
- unsigned long best_parent = rate / mult;
+ unsigned long best_parent = req->rate / mult;
- *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
+ req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
}
- return *prate * mult;
+ req->rate = req->best_parent_rate * mult;
+
+ return 0;
}
static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -454,7 +476,7 @@ static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate,
}
static const struct clk_ops clk_apb_mul_factor_ops = {
- .round_rate = clk_apb_mul_round_rate,
+ .determine_rate = clk_apb_mul_determine_rate,
.set_rate = clk_apb_mul_set_rate,
.recalc_rate = clk_apb_mul_recalc_rate,
};
@@ -509,6 +531,12 @@ static const struct clk_div_table pll_divr_table[] = {
{ 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 }
};
+struct stm32f4_pll_ssc {
+ unsigned int mod_freq;
+ unsigned int mod_depth;
+ enum stm32f4_pll_ssc_mod_type mod_type;
+};
+
struct stm32f4_pll {
spinlock_t *lock;
struct clk_gate gate;
@@ -516,6 +544,8 @@ struct stm32f4_pll {
u8 bit_rdy_idx;
u8 status;
u8 n_start;
+ bool ssc_enable;
+ struct stm32f4_pll_ssc ssc_conf;
};
#define to_stm32f4_pll(_gate) container_of(_gate, struct stm32f4_pll, gate)
@@ -538,6 +568,7 @@ struct stm32f4_vco_data {
u8 offset;
u8 bit_idx;
u8 bit_rdy_idx;
+ bool sscg;
};
static const struct stm32f4_vco_data vco_data[] = {
@@ -632,28 +663,58 @@ static unsigned long stm32f4_pll_recalc(struct clk_hw *hw,
{
struct clk_gate *gate = to_clk_gate(hw);
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+ unsigned long val;
unsigned long n;
- n = (readl(base + pll->offset) >> 6) & 0x1ff;
+ val = readl(base + pll->offset);
+ n = FIELD_GET(STM32F4_RCC_PLLCFGR_N_MASK, val);
return parent_rate * n;
}
-static long stm32f4_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int stm32f4_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_gate *gate = to_clk_gate(hw);
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
unsigned long n;
- n = rate / *prate;
+ n = req->rate / req->best_parent_rate;
if (n < pll->n_start)
n = pll->n_start;
else if (n > 432)
n = 432;
- return *prate * n;
+ req->rate = req->best_parent_rate * n;
+
+ return 0;
+}
+
+static void stm32f4_pll_set_ssc(struct clk_hw *hw, unsigned long parent_rate,
+ unsigned int ndiv)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+ struct stm32f4_pll_ssc *ssc = &pll->ssc_conf;
+ u32 modeper, incstep;
+ u32 sscgr;
+
+ sscgr = readl(base + STM32F4_RCC_SSCGR);
+ /* reserved field must be kept at reset value */
+ sscgr &= STM32F4_RCC_SSCGR_RESERVED_MASK;
+
+ modeper = DIV_ROUND_CLOSEST(parent_rate, 4 * ssc->mod_freq);
+ incstep = DIV_ROUND_CLOSEST(((1 << 15) - 1) * ssc->mod_depth * ndiv,
+ 5 * 10000 * modeper);
+ sscgr |= STM32F4_RCC_SSCGR_SSCGEN |
+ FIELD_PREP(STM32F4_RCC_SSCGR_INCSTEP_MASK, incstep) |
+ FIELD_PREP(STM32F4_RCC_SSCGR_MODPER_MASK, modeper);
+
+ if (ssc->mod_type)
+ sscgr |= STM32F4_RCC_SSCGR_SPREADSEL;
+
+ writel(sscgr, base + STM32F4_RCC_SSCGR);
}
static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -673,9 +734,13 @@ static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
n = rate / parent_rate;
- val = readl(base + pll->offset) & ~(0x1ff << 6);
+ val = readl(base + pll->offset) & ~STM32F4_RCC_PLLCFGR_N_MASK;
+ val |= FIELD_PREP(STM32F4_RCC_PLLCFGR_N_MASK, n);
- writel(val | ((n & 0x1ff) << 6), base + pll->offset);
+ writel(val, base + pll->offset);
+
+ if (pll->ssc_enable)
+ stm32f4_pll_set_ssc(hw, parent_rate, n);
if (pll_state)
stm32f4_pll_enable(hw);
@@ -688,7 +753,7 @@ static const struct clk_ops stm32f4_pll_gate_ops = {
.disable = stm32f4_pll_disable,
.is_enabled = stm32f4_pll_is_enabled,
.recalc_rate = stm32f4_pll_recalc,
- .round_rate = stm32f4_pll_round_rate,
+ .determine_rate = stm32f4_pll_determine_rate,
.set_rate = stm32f4_pll_set_rate,
};
@@ -782,6 +847,84 @@ static struct clk_hw *clk_register_pll_div(const char *name,
return hw;
}
+static int __init stm32f4_pll_init_ssc(struct clk_hw *hw,
+ const struct stm32f4_pll_ssc *conf)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32f4_pll *pll = to_stm32f4_pll(gate);
+ struct clk_hw *parent;
+ unsigned long parent_rate;
+ int pll_state;
+ unsigned long n, val;
+
+ parent = clk_hw_get_parent(hw);
+ if (!parent) {
+ pr_err("%s: failed to get clock parent\n", __func__);
+ return -ENODEV;
+ }
+
+ parent_rate = clk_hw_get_rate(parent);
+
+ pll->ssc_enable = true;
+ memcpy(&pll->ssc_conf, conf, sizeof(pll->ssc_conf));
+
+ pll_state = stm32f4_pll_is_enabled(hw);
+
+ if (pll_state)
+ stm32f4_pll_disable(hw);
+
+ val = readl(base + pll->offset);
+ n = FIELD_GET(STM32F4_RCC_PLLCFGR_N_MASK, val);
+
+ pr_debug("%s: pll: %s, parent: %s, parent-rate: %lu, n: %lu\n",
+ __func__, clk_hw_get_name(hw), clk_hw_get_name(parent),
+ parent_rate, n);
+
+ stm32f4_pll_set_ssc(hw, parent_rate, n);
+
+ if (pll_state)
+ stm32f4_pll_enable(hw);
+
+ return 0;
+}
+
+static int __init stm32f4_pll_ssc_parse_dt(struct device_node *np,
+ struct stm32f4_pll_ssc *conf)
+{
+ int ret;
+
+ if (!conf)
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "st,ssc-modfreq-hz", &conf->mod_freq);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "st,ssc-moddepth-permyriad",
+ &conf->mod_depth);
+ if (ret) {
+ pr_err("%pOF: missing st,ssc-moddepth-permyriad\n", np);
+ return ret;
+ }
+
+ ret = fwnode_property_match_property_string(of_fwnode_handle(np),
+ "st,ssc-modmethod",
+ stm32f4_ssc_mod_methods,
+ ARRAY_SIZE(stm32f4_ssc_mod_methods));
+ if (ret < 0) {
+ pr_err("%pOF: failed to get st,ssc-modmethod\n", np);
+ return ret;
+ }
+
+ conf->mod_type = ret;
+
+ pr_debug("%pOF: SSCG settings: mod_freq: %d, mod_depth: %d mod_method: %s [%d]\n",
+ np, conf->mod_freq, conf->mod_depth,
+ stm32f4_ssc_mod_methods[ret], conf->mod_type);
+
+ return 0;
+}
+
static struct clk_hw *stm32f4_rcc_register_pll(const char *pllsrc,
const struct stm32f4_pll_data *data, spinlock_t *lock)
{
@@ -1045,6 +1188,7 @@ static int cclk_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops cclk_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = cclk_mux_get_parent,
.set_parent = cclk_mux_set_parent,
};
@@ -1688,7 +1832,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
const struct of_device_id *match;
const struct stm32f4_clk_data *data;
unsigned long pllm;
- struct clk_hw *pll_src_hw;
+ struct clk_hw *pll_src_hw, *pll_vco_hw;
+ struct stm32f4_pll_ssc ssc_conf;
base = of_iomap(np, 0);
if (!base) {
@@ -1747,8 +1892,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
clk_hw_register_fixed_factor(NULL, "vco_in", pll_src,
0, 1, pllm);
- stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
- &stm32f4_clk_lock);
+ pll_vco_hw = stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
+ &stm32f4_clk_lock);
clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in",
&data->pll_data[1], &stm32f4_clk_lock);
@@ -1893,6 +2038,9 @@ static void __init stm32f4_rcc_init(struct device_node *np)
of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
+ if (!stm32f4_pll_ssc_parse_dt(np, &ssc_conf))
+ stm32f4_pll_init_ssc(pll_vco_hw, &ssc_conf);
+
return;
fail:
kfree(clks);
diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c
index 1a701eada0c1..04c18a1d45d3 100644
--- a/drivers/clk/clk-stm32h7.c
+++ b/drivers/clk/clk-stm32h7.c
@@ -667,7 +667,6 @@ struct stm32_fractional_divider {
void __iomem *mreg;
u8 mshift;
u8 mwidth;
- u32 mmask;
void __iomem *nreg;
u8 nshift;
diff --git a/drivers/clk/clk-tps68470.c b/drivers/clk/clk-tps68470.c
index e5fbefd6ac2d..9511248c6bc9 100644
--- a/drivers/clk/clk-tps68470.c
+++ b/drivers/clk/clk-tps68470.c
@@ -146,12 +146,14 @@ static unsigned int tps68470_clk_cfg_lookup(unsigned long rate)
return best_idx;
}
-static long tps68470_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int tps68470_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned int idx = tps68470_clk_cfg_lookup(rate);
+ unsigned int idx = tps68470_clk_cfg_lookup(req->rate);
+
+ req->rate = clk_freqs[idx].freq;
- return clk_freqs[idx].freq;
+ return 0;
}
static int tps68470_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -186,7 +188,7 @@ static const struct clk_ops tps68470_clk_ops = {
.prepare = tps68470_clk_prepare,
.unprepare = tps68470_clk_unprepare,
.recalc_rate = tps68470_clk_recalc_rate,
- .round_rate = tps68470_clk_round_rate,
+ .determine_rate = tps68470_clk_determine_rate,
.set_rate = tps68470_clk_set_rate,
};
@@ -200,7 +202,9 @@ static int tps68470_clk_probe(struct platform_device *pdev)
.flags = CLK_SET_RATE_GATE,
};
struct tps68470_clkdata *tps68470_clkdata;
+ struct tps68470_clk_consumer *consumer;
int ret;
+ int i;
tps68470_clkdata = devm_kzalloc(&pdev->dev, sizeof(*tps68470_clkdata),
GFP_KERNEL);
@@ -223,10 +227,13 @@ static int tps68470_clk_probe(struct platform_device *pdev)
return ret;
if (pdata) {
- ret = devm_clk_hw_register_clkdev(&pdev->dev,
- &tps68470_clkdata->clkout_hw,
- pdata->consumer_con_id,
- pdata->consumer_dev_name);
+ for (i = 0; i < pdata->n_consumers; i++) {
+ consumer = &pdata->consumers[i];
+ ret = devm_clk_hw_register_clkdev(&pdev->dev,
+ &tps68470_clkdata->clkout_hw,
+ consumer->consumer_con_id,
+ consumer->consumer_dev_name);
+ }
}
return ret;
diff --git a/drivers/clk/clk-twl.c b/drivers/clk/clk-twl.c
new file mode 100644
index 000000000000..20bc3bf8fd62
--- /dev/null
+++ b/drivers/clk/clk-twl.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for twl device.
+ *
+ * inspired by the driver for the Palmas device
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/twl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define VREG_STATE 2
+#define VREG_GRP 0
+#define TWL6030_CFG_STATE_OFF 0x00
+#define TWL6030_CFG_STATE_ON 0x01
+#define TWL6030_CFG_STATE_MASK 0x03
+#define TWL6030_CFG_STATE_GRP_SHIFT 5
+#define TWL6030_CFG_STATE_APP_SHIFT 2
+#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT)
+#define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\
+ TWL6030_CFG_STATE_APP_SHIFT)
+#define P1_GRP BIT(0) /* processor power group */
+#define P2_GRP BIT(1)
+#define P3_GRP BIT(2)
+#define ALL_GRP (P1_GRP | P2_GRP | P3_GRP)
+
+enum twl_type {
+ TWL_TYPE_6030,
+ TWL_TYPE_6032,
+};
+
+struct twl_clock_info {
+ struct device *dev;
+ enum twl_type type;
+ u8 base;
+ struct clk_hw hw;
+};
+
+static inline int
+twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp,
+ unsigned int offset)
+{
+ u8 value;
+ int status;
+
+ status = twl_i2c_read_u8(slave_subgp, &value,
+ info->base + offset);
+ return (status < 0) ? status : value;
+}
+
+static inline int
+twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp,
+ unsigned int offset, u8 value)
+{
+ return twl_i2c_write_u8(slave_subgp, value,
+ info->base + offset);
+}
+
+static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw)
+{
+ return container_of(hw, struct twl_clock_info, hw);
+}
+
+static unsigned long twl_clks_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 32768;
+}
+
+static int twl6032_clks_prepare(struct clk_hw *hw)
+{
+ struct twl_clock_info *cinfo = to_twl_clks_info(hw);
+
+ if (cinfo->type == TWL_TYPE_6030) {
+ int grp;
+
+ grp = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_GRP);
+ if (grp < 0)
+ return grp;
+
+ return twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+ grp << TWL6030_CFG_STATE_GRP_SHIFT |
+ TWL6030_CFG_STATE_ON);
+ }
+
+ return twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+ TWL6030_CFG_STATE_ON);
+}
+
+static void twl6032_clks_unprepare(struct clk_hw *hw)
+{
+ struct twl_clock_info *cinfo = to_twl_clks_info(hw);
+ int ret;
+
+ if (cinfo->type == TWL_TYPE_6030)
+ ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+ ALL_GRP << TWL6030_CFG_STATE_GRP_SHIFT |
+ TWL6030_CFG_STATE_OFF);
+ else
+ ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+ TWL6030_CFG_STATE_OFF);
+
+ if (ret < 0)
+ dev_err(cinfo->dev, "clk unprepare failed\n");
+}
+
+static const struct clk_ops twl6032_clks_ops = {
+ .prepare = twl6032_clks_prepare,
+ .unprepare = twl6032_clks_unprepare,
+ .recalc_rate = twl_clks_recalc_rate,
+};
+
+struct twl_clks_data {
+ struct clk_init_data init;
+ u8 base;
+};
+
+static const struct twl_clks_data twl6032_clks[] = {
+ {
+ .init = {
+ .name = "clk32kg",
+ .ops = &twl6032_clks_ops,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .base = 0x8C,
+ },
+ {
+ .init = {
+ .name = "clk32kaudio",
+ .ops = &twl6032_clks_ops,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .base = 0x8F,
+ },
+ {
+ /* sentinel */
+ }
+};
+
+static int twl_clks_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ const struct twl_clks_data *hw_data;
+
+ struct twl_clock_info *cinfo;
+ int ret;
+ int i;
+ int count;
+
+ hw_data = twl6032_clks;
+ for (count = 0; hw_data[count].init.name; count++)
+ ;
+
+ clk_data = devm_kzalloc(&pdev->dev,
+ struct_size(clk_data, hws, count),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = count;
+ cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL);
+ if (!cinfo)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ cinfo[i].base = hw_data[i].base;
+ cinfo[i].dev = &pdev->dev;
+ cinfo[i].type = platform_get_device_id(pdev)->driver_data;
+ cinfo[i].hw.init = &hw_data[i].init;
+ ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "Fail to register clock %s\n",
+ hw_data[i].init.name);
+ }
+ clk_data->hws[i] = &cinfo[i].hw;
+ }
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev,
+ of_clk_hw_onecell_get, clk_data);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "Fail to add clock driver\n");
+
+ return 0;
+}
+
+static const struct platform_device_id twl_clks_id[] = {
+ {
+ .name = "twl6030-clk",
+ .driver_data = TWL_TYPE_6030,
+ }, {
+ .name = "twl6032-clk",
+ .driver_data = TWL_TYPE_6032,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, twl_clks_id);
+
+static struct platform_driver twl_clks_driver = {
+ .driver = {
+ .name = "twl-clk",
+ },
+ .probe = twl_clks_probe,
+ .id_table = twl_clks_id,
+};
+
+module_platform_driver(twl_clks_driver);
+
+MODULE_DESCRIPTION("Clock driver for TWL Series Devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-versaclock3.c b/drivers/clk/clk-versaclock3.c
new file mode 100644
index 000000000000..1849863dbd67
--- /dev/null
+++ b/drivers/clk/clk-versaclock3.c
@@ -0,0 +1,1171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Renesas Versaclock 3
+ *
+ * Copyright (C) 2023 Renesas Electronics Corp.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/i2c.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define NUM_CONFIG_REGISTERS 37
+
+#define VC3_GENERAL_CTR 0x0
+#define VC3_GENERAL_CTR_DIV1_SRC_SEL BIT(3)
+#define VC3_GENERAL_CTR_PLL3_REFIN_SEL BIT(2)
+
+#define VC3_PLL3_M_DIVIDER 0x3
+#define VC3_PLL3_M_DIV1 BIT(7)
+#define VC3_PLL3_M_DIV2 BIT(6)
+#define VC3_PLL3_M_DIV(n) ((n) & GENMASK(5, 0))
+
+#define VC3_PLL3_N_DIVIDER 0x4
+#define VC3_PLL3_LOOP_FILTER_N_DIV_MSB 0x5
+
+#define VC3_PLL3_CHARGE_PUMP_CTRL 0x6
+#define VC3_PLL3_CHARGE_PUMP_CTRL_OUTDIV3_SRC_SEL BIT(7)
+
+#define VC3_PLL1_CTRL_OUTDIV5 0x7
+#define VC3_PLL1_CTRL_OUTDIV5_PLL1_MDIV_DOUBLER BIT(7)
+
+#define VC3_PLL1_M_DIVIDER 0x8
+#define VC3_PLL1_M_DIV1 BIT(7)
+#define VC3_PLL1_M_DIV2 BIT(6)
+#define VC3_PLL1_M_DIV(n) ((n) & GENMASK(5, 0))
+
+#define VC3_PLL1_VCO_N_DIVIDER 0x9
+#define VC3_PLL1_LOOP_FILTER_N_DIV_MSB 0xa
+
+#define VC3_OUT_DIV1_DIV2_CTRL 0xf
+
+#define VC3_PLL2_FB_INT_DIV_MSB 0x10
+#define VC3_PLL2_FB_INT_DIV_LSB 0x11
+#define VC3_PLL2_FB_FRC_DIV_MSB 0x12
+#define VC3_PLL2_FB_FRC_DIV_LSB 0x13
+
+#define VC3_PLL2_M_DIVIDER 0x1a
+#define VC3_PLL2_MDIV_DOUBLER BIT(7)
+#define VC3_PLL2_M_DIV1 BIT(6)
+#define VC3_PLL2_M_DIV2 BIT(5)
+#define VC3_PLL2_M_DIV(n) ((n) & GENMASK(4, 0))
+
+#define VC3_OUT_DIV3_DIV4_CTRL 0x1b
+
+#define VC3_PLL_OP_CTRL 0x1c
+#define VC3_PLL_OP_CTRL_PLL2_REFIN_SEL 6
+
+#define VC3_OUTPUT_CTR 0x1d
+#define VC3_OUTPUT_CTR_DIV4_SRC_SEL BIT(3)
+
+#define VC3_SE2_CTRL_REG0 0x1f
+#define VC3_SE2_CTRL_REG0_SE2_CLK_SEL BIT(6)
+
+#define VC3_SE3_DIFF1_CTRL_REG 0x21
+#define VC3_SE3_DIFF1_CTRL_REG_SE3_CLK_SEL BIT(6)
+
+#define VC3_DIFF1_CTRL_REG 0x22
+#define VC3_DIFF1_CTRL_REG_DIFF1_CLK_SEL BIT(7)
+
+#define VC3_DIFF2_CTRL_REG 0x23
+#define VC3_DIFF2_CTRL_REG_DIFF2_CLK_SEL BIT(7)
+
+#define VC3_SE1_DIV4_CTRL 0x24
+#define VC3_SE1_DIV4_CTRL_SE1_CLK_SEL BIT(3)
+
+#define VC3_PLL1_VCO_MIN 300000000UL
+#define VC3_PLL1_VCO_MAX 600000000UL
+
+#define VC3_PLL3_VCO_MIN 300000000UL
+#define VC3_PLL3_VCO_MAX 800000000UL
+
+#define VC3_2_POW_16 (U16_MAX + 1)
+#define VC3_DIV_MASK(width) ((1 << (width)) - 1)
+
+enum vc3_pfd_mux {
+ VC3_PFD2_MUX,
+ VC3_PFD3_MUX,
+};
+
+enum vc3_pfd {
+ VC3_PFD1,
+ VC3_PFD2,
+ VC3_PFD3,
+};
+
+enum vc3_pll {
+ VC3_PLL1,
+ VC3_PLL2,
+ VC3_PLL3,
+};
+
+enum vc3_div_mux {
+ VC3_DIV1_MUX,
+ VC3_DIV3_MUX,
+ VC3_DIV4_MUX,
+};
+
+enum vc3_div {
+ VC3_DIV1,
+ VC3_DIV2,
+ VC3_DIV3,
+ VC3_DIV4,
+ VC3_DIV5,
+};
+
+enum vc3_clk {
+ VC3_REF,
+ VC3_SE1,
+ VC3_SE2,
+ VC3_SE3,
+ VC3_DIFF1,
+ VC3_DIFF2,
+};
+
+enum vc3_clk_mux {
+ VC3_SE1_MUX = VC3_SE1 - 1,
+ VC3_SE2_MUX = VC3_SE2 - 1,
+ VC3_SE3_MUX = VC3_SE3 - 1,
+ VC3_DIFF1_MUX = VC3_DIFF1 - 1,
+ VC3_DIFF2_MUX = VC3_DIFF2 - 1,
+};
+
+struct vc3_clk_data {
+ u8 offs;
+ u8 bitmsk;
+};
+
+struct vc3_pfd_data {
+ u8 num;
+ u8 offs;
+ u8 mdiv1_bitmsk;
+ u8 mdiv2_bitmsk;
+};
+
+struct vc3_vco {
+ unsigned long min;
+ unsigned long max;
+};
+
+struct vc3_pll_data {
+ struct vc3_vco vco;
+ u8 num;
+ u8 int_div_msb_offs;
+ u8 int_div_lsb_offs;
+};
+
+struct vc3_div_data {
+ const struct clk_div_table *table;
+ u8 offs;
+ u8 shift;
+ u8 width;
+ u8 flags;
+};
+
+struct vc3_hw_data {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ void *data;
+
+ u32 div_int;
+ u32 div_frc;
+};
+
+struct vc3_hw_cfg {
+ struct vc3_vco pll2_vco;
+ u32 se2_clk_sel_msk;
+};
+
+static const struct clk_div_table div1_divs[] = {
+ { .val = 0, .div = 1, }, { .val = 1, .div = 4, },
+ { .val = 2, .div = 5, }, { .val = 3, .div = 6, },
+ { .val = 4, .div = 2, }, { .val = 5, .div = 8, },
+ { .val = 6, .div = 10, }, { .val = 7, .div = 12, },
+ { .val = 8, .div = 4, }, { .val = 9, .div = 16, },
+ { .val = 10, .div = 20, }, { .val = 11, .div = 24, },
+ { .val = 12, .div = 8, }, { .val = 13, .div = 32, },
+ { .val = 14, .div = 40, }, { .val = 15, .div = 48, },
+ {}
+};
+
+static const struct clk_div_table div245_divs[] = {
+ { .val = 0, .div = 1, }, { .val = 1, .div = 3, },
+ { .val = 2, .div = 5, }, { .val = 3, .div = 10, },
+ { .val = 4, .div = 2, }, { .val = 5, .div = 6, },
+ { .val = 6, .div = 10, }, { .val = 7, .div = 20, },
+ { .val = 8, .div = 4, }, { .val = 9, .div = 12, },
+ { .val = 10, .div = 20, }, { .val = 11, .div = 40, },
+ { .val = 12, .div = 5, }, { .val = 13, .div = 15, },
+ { .val = 14, .div = 25, }, { .val = 15, .div = 50, },
+ {}
+};
+
+static const struct clk_div_table div3_divs[] = {
+ { .val = 0, .div = 1, }, { .val = 1, .div = 3, },
+ { .val = 2, .div = 5, }, { .val = 3, .div = 10, },
+ { .val = 4, .div = 2, }, { .val = 5, .div = 6, },
+ { .val = 6, .div = 10, }, { .val = 7, .div = 20, },
+ { .val = 8, .div = 4, }, { .val = 9, .div = 12, },
+ { .val = 10, .div = 20, }, { .val = 11, .div = 40, },
+ { .val = 12, .div = 8, }, { .val = 13, .div = 24, },
+ { .val = 14, .div = 40, }, { .val = 15, .div = 80, },
+ {}
+};
+
+static struct clk_hw *clk_out[6];
+
+static u8 vc3_pfd_mux_get_parent(struct clk_hw *hw)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_clk_data *pfd_mux = vc3->data;
+ u32 src;
+
+ regmap_read(vc3->regmap, pfd_mux->offs, &src);
+
+ return !!(src & pfd_mux->bitmsk);
+}
+
+static int vc3_pfd_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_clk_data *pfd_mux = vc3->data;
+
+ return regmap_update_bits(vc3->regmap, pfd_mux->offs, pfd_mux->bitmsk,
+ index ? pfd_mux->bitmsk : 0);
+}
+
+static const struct clk_ops vc3_pfd_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .set_parent = vc3_pfd_mux_set_parent,
+ .get_parent = vc3_pfd_mux_get_parent,
+};
+
+static unsigned long vc3_pfd_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_pfd_data *pfd = vc3->data;
+ unsigned int prediv, premul;
+ unsigned long rate;
+ u8 mdiv;
+
+ regmap_read(vc3->regmap, pfd->offs, &prediv);
+ if (pfd->num == VC3_PFD1) {
+ /* The bypass_prediv is set, PLL fed from Ref_in directly. */
+ if (prediv & pfd->mdiv1_bitmsk) {
+ /* check doubler is set or not */
+ regmap_read(vc3->regmap, VC3_PLL1_CTRL_OUTDIV5, &premul);
+ if (premul & VC3_PLL1_CTRL_OUTDIV5_PLL1_MDIV_DOUBLER)
+ parent_rate *= 2;
+ return parent_rate;
+ }
+ mdiv = VC3_PLL1_M_DIV(prediv);
+ } else if (pfd->num == VC3_PFD2) {
+ /* The bypass_prediv is set, PLL fed from Ref_in directly. */
+ if (prediv & pfd->mdiv1_bitmsk) {
+ regmap_read(vc3->regmap, VC3_PLL2_M_DIVIDER, &premul);
+ /* check doubler is set or not */
+ if (premul & VC3_PLL2_MDIV_DOUBLER)
+ parent_rate *= 2;
+ return parent_rate;
+ }
+
+ mdiv = VC3_PLL2_M_DIV(prediv);
+ } else {
+ /* The bypass_prediv is set, PLL fed from Ref_in directly. */
+ if (prediv & pfd->mdiv1_bitmsk)
+ return parent_rate;
+
+ mdiv = VC3_PLL3_M_DIV(prediv);
+ }
+
+ if (prediv & pfd->mdiv2_bitmsk)
+ rate = parent_rate / 2;
+ else
+ rate = parent_rate / mdiv;
+
+ return rate;
+}
+
+static int vc3_pfd_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_pfd_data *pfd = vc3->data;
+ unsigned long idiv;
+
+ /* PLL cannot operate with input clock above 50 MHz. */
+ if (req->rate > 50000000)
+ return -EINVAL;
+
+ /* CLKIN within range of PLL input, feed directly to PLL. */
+ if (req->best_parent_rate <= 50000000) {
+ req->rate = req->best_parent_rate;
+
+ return 0;
+ }
+
+ idiv = DIV_ROUND_UP(req->best_parent_rate, req->rate);
+ if (pfd->num == VC3_PFD1 || pfd->num == VC3_PFD3) {
+ if (idiv > 63)
+ return -EINVAL;
+ } else {
+ if (idiv > 31)
+ return -EINVAL;
+ }
+
+ req->rate = req->best_parent_rate / idiv;
+
+ return 0;
+}
+
+static int vc3_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_pfd_data *pfd = vc3->data;
+ unsigned long idiv;
+ u8 div;
+
+ /* CLKIN within range of PLL input, feed directly to PLL. */
+ if (parent_rate <= 50000000) {
+ regmap_update_bits(vc3->regmap, pfd->offs, pfd->mdiv1_bitmsk,
+ pfd->mdiv1_bitmsk);
+ regmap_update_bits(vc3->regmap, pfd->offs, pfd->mdiv2_bitmsk, 0);
+ return 0;
+ }
+
+ idiv = DIV_ROUND_UP(parent_rate, rate);
+ /* We have dedicated div-2 predivider. */
+ if (idiv == 2) {
+ regmap_update_bits(vc3->regmap, pfd->offs, pfd->mdiv2_bitmsk,
+ pfd->mdiv2_bitmsk);
+ regmap_update_bits(vc3->regmap, pfd->offs, pfd->mdiv1_bitmsk, 0);
+ } else {
+ if (pfd->num == VC3_PFD1)
+ div = VC3_PLL1_M_DIV(idiv);
+ else if (pfd->num == VC3_PFD2)
+ div = VC3_PLL2_M_DIV(idiv);
+ else
+ div = VC3_PLL3_M_DIV(idiv);
+
+ regmap_write(vc3->regmap, pfd->offs, div);
+ }
+
+ return 0;
+}
+
+static const struct clk_ops vc3_pfd_ops = {
+ .recalc_rate = vc3_pfd_recalc_rate,
+ .determine_rate = vc3_pfd_determine_rate,
+ .set_rate = vc3_pfd_set_rate,
+};
+
+static unsigned long vc3_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_pll_data *pll = vc3->data;
+ u32 div_int, div_frc, val;
+ unsigned long rate;
+
+ regmap_read(vc3->regmap, pll->int_div_msb_offs, &val);
+ div_int = (val & GENMASK(2, 0)) << 8;
+ regmap_read(vc3->regmap, pll->int_div_lsb_offs, &val);
+ div_int |= val;
+
+ if (pll->num == VC3_PLL2) {
+ regmap_read(vc3->regmap, VC3_PLL2_FB_FRC_DIV_MSB, &val);
+ div_frc = val << 8;
+ regmap_read(vc3->regmap, VC3_PLL2_FB_FRC_DIV_LSB, &val);
+ div_frc |= val;
+ rate = (parent_rate *
+ (div_int * VC3_2_POW_16 + div_frc) / VC3_2_POW_16);
+ } else {
+ rate = parent_rate * div_int;
+ }
+
+ return rate;
+}
+
+static int vc3_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_pll_data *pll = vc3->data;
+ u64 div_frc;
+
+ if (req->rate < pll->vco.min)
+ req->rate = pll->vco.min;
+ if (req->rate > pll->vco.max)
+ req->rate = pll->vco.max;
+
+ vc3->div_int = req->rate / req->best_parent_rate;
+
+ if (pll->num == VC3_PLL2) {
+ if (vc3->div_int > 0x7ff)
+ req->rate = req->best_parent_rate * 0x7ff;
+
+ /* Determine best fractional part, which is 16 bit wide */
+ div_frc = req->rate % req->best_parent_rate;
+ div_frc *= BIT(16) - 1;
+
+ vc3->div_frc = min_t(u64,
+ div64_ul(div_frc, req->best_parent_rate),
+ U16_MAX);
+ req->rate = (req->best_parent_rate *
+ (vc3->div_int * VC3_2_POW_16 + vc3->div_frc) / VC3_2_POW_16);
+ } else {
+ req->rate = req->best_parent_rate * vc3->div_int;
+ }
+
+ return 0;
+}
+
+static int vc3_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_pll_data *pll = vc3->data;
+ u32 val;
+
+ regmap_read(vc3->regmap, pll->int_div_msb_offs, &val);
+ val = (val & 0xf8) | ((vc3->div_int >> 8) & 0x7);
+ regmap_write(vc3->regmap, pll->int_div_msb_offs, val);
+ regmap_write(vc3->regmap, pll->int_div_lsb_offs, vc3->div_int & 0xff);
+
+ if (pll->num == VC3_PLL2) {
+ regmap_write(vc3->regmap, VC3_PLL2_FB_FRC_DIV_MSB,
+ vc3->div_frc >> 8);
+ regmap_write(vc3->regmap, VC3_PLL2_FB_FRC_DIV_LSB,
+ vc3->div_frc & 0xff);
+ }
+
+ return 0;
+}
+
+static const struct clk_ops vc3_pll_ops = {
+ .recalc_rate = vc3_pll_recalc_rate,
+ .determine_rate = vc3_pll_determine_rate,
+ .set_rate = vc3_pll_set_rate,
+};
+
+static u8 vc3_div_mux_get_parent(struct clk_hw *hw)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_clk_data *div_mux = vc3->data;
+ u32 src;
+
+ regmap_read(vc3->regmap, div_mux->offs, &src);
+
+ return !!(src & div_mux->bitmsk);
+}
+
+static int vc3_div_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_clk_data *div_mux = vc3->data;
+
+ return regmap_update_bits(vc3->regmap, div_mux->offs, div_mux->bitmsk,
+ index ? div_mux->bitmsk : 0);
+}
+
+static const struct clk_ops vc3_div_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .set_parent = vc3_div_mux_set_parent,
+ .get_parent = vc3_div_mux_get_parent,
+};
+
+static unsigned int vc3_get_div(const struct clk_div_table *table,
+ unsigned int val, unsigned long flag)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+
+ return 1;
+}
+
+static unsigned long vc3_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_div_data *div_data = vc3->data;
+ unsigned int val;
+
+ regmap_read(vc3->regmap, div_data->offs, &val);
+ val >>= div_data->shift;
+ val &= VC3_DIV_MASK(div_data->width);
+
+ return divider_recalc_rate(hw, parent_rate, val, div_data->table,
+ div_data->flags, div_data->width);
+}
+
+static int vc3_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_div_data *div_data = vc3->data;
+ unsigned int bestdiv;
+
+ /* if read only, just return current value */
+ if (div_data->flags & CLK_DIVIDER_READ_ONLY) {
+ regmap_read(vc3->regmap, div_data->offs, &bestdiv);
+ bestdiv >>= div_data->shift;
+ bestdiv &= VC3_DIV_MASK(div_data->width);
+ bestdiv = vc3_get_div(div_data->table, bestdiv, div_data->flags);
+ req->rate = DIV_ROUND_UP(req->best_parent_rate, bestdiv);
+
+ return 0;
+ }
+
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate,
+ div_data->table,
+ div_data->width, div_data->flags);
+
+ return 0;
+}
+
+static int vc3_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_div_data *div_data = vc3->data;
+ unsigned int value;
+
+ value = divider_get_val(rate, parent_rate, div_data->table,
+ div_data->width, div_data->flags);
+ return regmap_update_bits(vc3->regmap, div_data->offs,
+ VC3_DIV_MASK(div_data->width) << div_data->shift,
+ value << div_data->shift);
+}
+
+static const struct clk_ops vc3_div_ops = {
+ .recalc_rate = vc3_div_recalc_rate,
+ .determine_rate = vc3_div_determine_rate,
+ .set_rate = vc3_div_set_rate,
+};
+
+static int vc3_clk_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ int frc;
+
+ if (clk_mux_determine_rate_flags(hw, req, CLK_SET_RATE_PARENT)) {
+ /* The below check is equivalent to (best_parent_rate/rate) */
+ if (req->best_parent_rate >= req->rate) {
+ frc = DIV_ROUND_CLOSEST_ULL(req->best_parent_rate,
+ req->rate);
+ req->rate *= frc;
+ return clk_mux_determine_rate_flags(hw, req,
+ CLK_SET_RATE_PARENT);
+ }
+ }
+
+ return 0;
+}
+
+static u8 vc3_clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_clk_data *clk_mux = vc3->data;
+ u32 val;
+
+ regmap_read(vc3->regmap, clk_mux->offs, &val);
+
+ return !!(val & clk_mux->bitmsk);
+}
+
+static int vc3_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct vc3_hw_data *vc3 = container_of(hw, struct vc3_hw_data, hw);
+ const struct vc3_clk_data *clk_mux = vc3->data;
+
+ return regmap_update_bits(vc3->regmap, clk_mux->offs, clk_mux->bitmsk,
+ index ? clk_mux->bitmsk : 0);
+}
+
+static const struct clk_ops vc3_clk_mux_ops = {
+ .determine_rate = vc3_clk_mux_determine_rate,
+ .set_parent = vc3_clk_mux_set_parent,
+ .get_parent = vc3_clk_mux_get_parent,
+};
+
+static const struct regmap_config vc3_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .max_register = 0x24,
+};
+
+static struct vc3_hw_data clk_div[5];
+
+static const struct clk_parent_data pfd_mux_parent_data[] = {
+ { .index = 0, },
+ { .hw = &clk_div[VC3_DIV2].hw }
+};
+
+static struct vc3_hw_data clk_pfd_mux[] = {
+ [VC3_PFD2_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_PLL_OP_CTRL,
+ .bitmsk = BIT(VC3_PLL_OP_CTRL_PLL2_REFIN_SEL)
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pfd2_mux",
+ .ops = &vc3_pfd_mux_ops,
+ .parent_data = pfd_mux_parent_data,
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT
+ }
+ },
+ [VC3_PFD3_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_GENERAL_CTR,
+ .bitmsk = BIT(VC3_GENERAL_CTR_PLL3_REFIN_SEL)
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pfd3_mux",
+ .ops = &vc3_pfd_mux_ops,
+ .parent_data = pfd_mux_parent_data,
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT
+ }
+ }
+};
+
+static struct vc3_hw_data clk_pfd[] = {
+ [VC3_PFD1] = {
+ .data = &(struct vc3_pfd_data) {
+ .num = VC3_PFD1,
+ .offs = VC3_PLL1_M_DIVIDER,
+ .mdiv1_bitmsk = VC3_PLL1_M_DIV1,
+ .mdiv2_bitmsk = VC3_PLL1_M_DIV2
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pfd1",
+ .ops = &vc3_pfd_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .index = 0
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_PFD2] = {
+ .data = &(struct vc3_pfd_data) {
+ .num = VC3_PFD2,
+ .offs = VC3_PLL2_M_DIVIDER,
+ .mdiv1_bitmsk = VC3_PLL2_M_DIV1,
+ .mdiv2_bitmsk = VC3_PLL2_M_DIV2
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pfd2",
+ .ops = &vc3_pfd_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pfd_mux[VC3_PFD2_MUX].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_PFD3] = {
+ .data = &(struct vc3_pfd_data) {
+ .num = VC3_PFD3,
+ .offs = VC3_PLL3_M_DIVIDER,
+ .mdiv1_bitmsk = VC3_PLL3_M_DIV1,
+ .mdiv2_bitmsk = VC3_PLL3_M_DIV2
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pfd3",
+ .ops = &vc3_pfd_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pfd_mux[VC3_PFD3_MUX].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ }
+};
+
+static struct vc3_hw_data clk_pll[] = {
+ [VC3_PLL1] = {
+ .data = &(struct vc3_pll_data) {
+ .num = VC3_PLL1,
+ .int_div_msb_offs = VC3_PLL1_LOOP_FILTER_N_DIV_MSB,
+ .int_div_lsb_offs = VC3_PLL1_VCO_N_DIVIDER,
+ .vco = {
+ .min = VC3_PLL1_VCO_MIN,
+ .max = VC3_PLL1_VCO_MAX
+ }
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pll1",
+ .ops = &vc3_pll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pfd[VC3_PFD1].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_PLL2] = {
+ .data = &(struct vc3_pll_data) {
+ .num = VC3_PLL2,
+ .int_div_msb_offs = VC3_PLL2_FB_INT_DIV_MSB,
+ .int_div_lsb_offs = VC3_PLL2_FB_INT_DIV_LSB,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pll2",
+ .ops = &vc3_pll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pfd[VC3_PFD2].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_PLL3] = {
+ .data = &(struct vc3_pll_data) {
+ .num = VC3_PLL3,
+ .int_div_msb_offs = VC3_PLL3_LOOP_FILTER_N_DIV_MSB,
+ .int_div_lsb_offs = VC3_PLL3_N_DIVIDER,
+ .vco = {
+ .min = VC3_PLL3_VCO_MIN,
+ .max = VC3_PLL3_VCO_MAX
+ }
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pll3",
+ .ops = &vc3_pll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pfd[VC3_PFD3].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ }
+};
+
+static const struct clk_parent_data div_mux_parent_data[][2] = {
+ [VC3_DIV1_MUX] = {
+ { .hw = &clk_pll[VC3_PLL1].hw },
+ { .index = 0 }
+ },
+ [VC3_DIV3_MUX] = {
+ { .hw = &clk_pll[VC3_PLL2].hw },
+ { .hw = &clk_pll[VC3_PLL3].hw }
+ },
+ [VC3_DIV4_MUX] = {
+ { .hw = &clk_pll[VC3_PLL2].hw },
+ { .index = 0 }
+ }
+};
+
+static struct vc3_hw_data clk_div_mux[] = {
+ [VC3_DIV1_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_GENERAL_CTR,
+ .bitmsk = VC3_GENERAL_CTR_DIV1_SRC_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div1_mux",
+ .ops = &vc3_div_mux_ops,
+ .parent_data = div_mux_parent_data[VC3_DIV1_MUX],
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT
+ }
+ },
+ [VC3_DIV3_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_PLL3_CHARGE_PUMP_CTRL,
+ .bitmsk = VC3_PLL3_CHARGE_PUMP_CTRL_OUTDIV3_SRC_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div3_mux",
+ .ops = &vc3_div_mux_ops,
+ .parent_data = div_mux_parent_data[VC3_DIV3_MUX],
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT
+ }
+ },
+ [VC3_DIV4_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_OUTPUT_CTR,
+ .bitmsk = VC3_OUTPUT_CTR_DIV4_SRC_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div4_mux",
+ .ops = &vc3_div_mux_ops,
+ .parent_data = div_mux_parent_data[VC3_DIV4_MUX],
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT
+ }
+ }
+};
+
+static struct vc3_hw_data clk_div[] = {
+ [VC3_DIV1] = {
+ .data = &(struct vc3_div_data) {
+ .offs = VC3_OUT_DIV1_DIV2_CTRL,
+ .table = div1_divs,
+ .shift = 4,
+ .width = 4,
+ .flags = CLK_DIVIDER_READ_ONLY
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div1",
+ .ops = &vc3_div_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div_mux[VC3_DIV1_MUX].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_DIV2] = {
+ .data = &(struct vc3_div_data) {
+ .offs = VC3_OUT_DIV1_DIV2_CTRL,
+ .table = div245_divs,
+ .shift = 0,
+ .width = 4,
+ .flags = CLK_DIVIDER_READ_ONLY
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div2",
+ .ops = &vc3_div_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pll[VC3_PLL1].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_DIV3] = {
+ .data = &(struct vc3_div_data) {
+ .offs = VC3_OUT_DIV3_DIV4_CTRL,
+ .table = div3_divs,
+ .shift = 4,
+ .width = 4,
+ .flags = CLK_DIVIDER_READ_ONLY
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div3",
+ .ops = &vc3_div_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div_mux[VC3_DIV3_MUX].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_DIV4] = {
+ .data = &(struct vc3_div_data) {
+ .offs = VC3_OUT_DIV3_DIV4_CTRL,
+ .table = div245_divs,
+ .shift = 0,
+ .width = 4,
+ .flags = CLK_DIVIDER_READ_ONLY
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div4",
+ .ops = &vc3_div_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div_mux[VC3_DIV4_MUX].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_DIV5] = {
+ .data = &(struct vc3_div_data) {
+ .offs = VC3_PLL1_CTRL_OUTDIV5,
+ .table = div245_divs,
+ .shift = 0,
+ .width = 4,
+ .flags = CLK_DIVIDER_READ_ONLY
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "div5",
+ .ops = &vc3_div_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_pll[VC3_PLL3].hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ }
+};
+
+static struct vc3_hw_data clk_mux[] = {
+ [VC3_SE1_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_SE1_DIV4_CTRL,
+ .bitmsk = VC3_SE1_DIV4_CTRL_SE1_CLK_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "se1_mux",
+ .ops = &vc3_clk_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div[VC3_DIV5].hw,
+ &clk_div[VC3_DIV4].hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_SE2_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_SE2_CTRL_REG0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "se2_mux",
+ .ops = &vc3_clk_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div[VC3_DIV5].hw,
+ &clk_div[VC3_DIV4].hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_SE3_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_SE3_DIFF1_CTRL_REG,
+ .bitmsk = VC3_SE3_DIFF1_CTRL_REG_SE3_CLK_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "se3_mux",
+ .ops = &vc3_clk_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div[VC3_DIV2].hw,
+ &clk_div[VC3_DIV4].hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_DIFF1_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_DIFF1_CTRL_REG,
+ .bitmsk = VC3_DIFF1_CTRL_REG_DIFF1_CLK_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "diff1_mux",
+ .ops = &vc3_clk_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div[VC3_DIV1].hw,
+ &clk_div[VC3_DIV3].hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ },
+ [VC3_DIFF2_MUX] = {
+ .data = &(struct vc3_clk_data) {
+ .offs = VC3_DIFF2_CTRL_REG,
+ .bitmsk = VC3_DIFF2_CTRL_REG_DIFF2_CLK_SEL
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "diff2_mux",
+ .ops = &vc3_clk_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &clk_div[VC3_DIV1].hw,
+ &clk_div[VC3_DIV3].hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT
+ }
+ }
+};
+
+static struct clk_hw *vc3_of_clk_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ unsigned int idx = clkspec->args[0];
+ struct clk_hw **clkout_hw = data;
+
+ if (idx >= ARRAY_SIZE(clk_out)) {
+ pr_err("invalid clk index %u for provider %pOF\n", idx, clkspec->np);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return clkout_hw[idx];
+}
+
+static int vc3_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ u8 settings[NUM_CONFIG_REGISTERS];
+ const struct vc3_hw_cfg *data;
+ struct regmap *regmap;
+ const char *name;
+ int ret, i;
+
+ regmap = devm_regmap_init_i2c(client, &vc3_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to allocate register map\n");
+
+ ret = of_property_read_u8_array(dev->of_node, "renesas,settings",
+ settings, ARRAY_SIZE(settings));
+ if (!ret) {
+ /*
+ * A raw settings array was specified in the DT. Write the
+ * settings to the device immediately.
+ */
+ for (i = 0; i < NUM_CONFIG_REGISTERS; i++) {
+ ret = regmap_write(regmap, i, settings[i]);
+ if (ret) {
+ dev_err(dev, "error writing to chip (%i)\n", ret);
+ return ret;
+ }
+ }
+ } else if (ret == -EOVERFLOW) {
+ dev_err(&client->dev, "EOVERFLOW reg settings. ARRAY_SIZE: %zu\n",
+ ARRAY_SIZE(settings));
+ return ret;
+ }
+
+ /* Register pfd muxes */
+ for (i = 0; i < ARRAY_SIZE(clk_pfd_mux); i++) {
+ clk_pfd_mux[i].regmap = regmap;
+ ret = devm_clk_hw_register(dev, &clk_pfd_mux[i].hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s failed\n",
+ clk_pfd_mux[i].hw.init->name);
+ }
+
+ /* Register pfd's */
+ for (i = 0; i < ARRAY_SIZE(clk_pfd); i++) {
+ clk_pfd[i].regmap = regmap;
+ ret = devm_clk_hw_register(dev, &clk_pfd[i].hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s failed\n",
+ clk_pfd[i].hw.init->name);
+ }
+
+ data = i2c_get_match_data(client);
+
+ /* Register pll's */
+ for (i = 0; i < ARRAY_SIZE(clk_pll); i++) {
+ clk_pll[i].regmap = regmap;
+ if (i == VC3_PLL2) {
+ struct vc3_pll_data *pll_data = clk_pll[i].data;
+
+ pll_data->vco = data->pll2_vco;
+ }
+ ret = devm_clk_hw_register(dev, &clk_pll[i].hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s failed\n",
+ clk_pll[i].hw.init->name);
+ }
+
+ /* Register divider muxes */
+ for (i = 0; i < ARRAY_SIZE(clk_div_mux); i++) {
+ clk_div_mux[i].regmap = regmap;
+ ret = devm_clk_hw_register(dev, &clk_div_mux[i].hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s failed\n",
+ clk_div_mux[i].hw.init->name);
+ }
+
+ /* Register dividers */
+ for (i = 0; i < ARRAY_SIZE(clk_div); i++) {
+ clk_div[i].regmap = regmap;
+ ret = devm_clk_hw_register(dev, &clk_div[i].hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s failed\n",
+ clk_div[i].hw.init->name);
+ }
+
+ /* Register clk muxes */
+ for (i = 0; i < ARRAY_SIZE(clk_mux); i++) {
+ clk_mux[i].regmap = regmap;
+ if (i == VC3_SE2_MUX) {
+ struct vc3_clk_data *clk_data = clk_mux[i].data;
+
+ clk_data->bitmsk = data->se2_clk_sel_msk;
+ }
+ ret = devm_clk_hw_register(dev, &clk_mux[i].hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s failed\n",
+ clk_mux[i].hw.init->name);
+ }
+
+ /* Register clk outputs */
+ for (i = 0; i < ARRAY_SIZE(clk_out); i++) {
+ switch (i) {
+ case VC3_DIFF2:
+ name = "diff2";
+ break;
+ case VC3_DIFF1:
+ name = "diff1";
+ break;
+ case VC3_SE3:
+ name = "se3";
+ break;
+ case VC3_SE2:
+ name = "se2";
+ break;
+ case VC3_SE1:
+ name = "se1";
+ break;
+ case VC3_REF:
+ name = "ref";
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "invalid clk output %d\n", i);
+ }
+
+ if (i == VC3_REF)
+ clk_out[i] = devm_clk_hw_register_fixed_factor_index(dev,
+ name, 0, CLK_SET_RATE_PARENT, 1, 1);
+ else
+ clk_out[i] = devm_clk_hw_register_fixed_factor_parent_hw(dev,
+ name, &clk_mux[i - 1].hw, CLK_SET_RATE_PARENT, 1, 1);
+
+ if (IS_ERR(clk_out[i]))
+ return PTR_ERR(clk_out[i]);
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, vc3_of_clk_get, clk_out);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to add clk provider\n");
+
+ return ret;
+}
+
+static const struct vc3_hw_cfg vc3_5p = {
+ .pll2_vco = { .min = 400000000UL, .max = 1200000000UL },
+ .se2_clk_sel_msk = BIT(6),
+};
+
+static const struct vc3_hw_cfg vc3_5l = {
+ .pll2_vco = { .min = 30000000UL, .max = 130000000UL },
+ .se2_clk_sel_msk = BIT(0),
+};
+
+static const struct of_device_id dev_ids[] = {
+ { .compatible = "renesas,5p35023", .data = &vc3_5p },
+ { .compatible = "renesas,5l35023", .data = &vc3_5l },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dev_ids);
+
+static struct i2c_driver vc3_driver = {
+ .driver = {
+ .name = "vc3",
+ .of_match_table = of_match_ptr(dev_ids),
+ },
+ .probe = vc3_probe,
+};
+module_i2c_driver(vc3_driver);
+
+MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
+MODULE_DESCRIPTION("Renesas VersaClock 3 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c
index e7be3e54b9be..57228e88e81d 100644
--- a/drivers/clk/clk-versaclock5.c
+++ b/drivers/clk/clk-versaclock5.c
@@ -19,12 +19,11 @@
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/rational.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <dt-bindings/clk/versaclock.h>
+#include <dt-bindings/clock/versaclock.h>
/* VersaClock5 registers */
#define VC5_OTP_CONTROL 0x00
@@ -123,9 +122,8 @@
#define VC5_GLOBAL_REGISTER 0x76
#define VC5_GLOBAL_REGISTER_GLOBAL_RESET BIT(5)
-/* PLL/VCO runs between 2.5 GHz and 3.0 GHz */
+/* The minimum VCO frequency is 2.5 GHz. The maximum is variant specific. */
#define VC5_PLL_VCO_MIN 2500000000UL
-#define VC5_PLL_VCO_MAX 3000000000UL
/* VC5 Input mux settings */
#define VC5_MUX_IN_XIN BIT(0)
@@ -138,7 +136,7 @@
#define VC5_MAX_FOD_NUM 4
/* flags to describe chip features */
-/* chip has built-in oscilator */
+/* chip has built-in oscillator */
#define VC5_HAS_INTERNAL_XTAL BIT(0)
/* chip has PFD requency doubler */
#define VC5_HAS_PFD_FREQ_DBL BIT(1)
@@ -151,8 +149,10 @@ enum vc5_model {
IDT_VC5_5P49V5925,
IDT_VC5_5P49V5933,
IDT_VC5_5P49V5935,
+ IDT_VC6_5P49V60,
IDT_VC6_5P49V6901,
IDT_VC6_5P49V6965,
+ IDT_VC6_5P49V6975,
};
/* Structure to describe features of a particular VC5 model */
@@ -161,6 +161,7 @@ struct vc5_chip_info {
const unsigned int clk_fod_cnt;
const unsigned int clk_out_cnt;
const u32 flags;
+ const unsigned long vco_max;
};
struct vc5_driver_data;
@@ -216,7 +217,7 @@ static bool vc5_regmap_is_writeable(struct device *dev, unsigned int reg)
static const struct regmap_config vc5_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = 0x76,
.writeable_reg = vc5_regmap_is_writeable,
};
@@ -230,8 +231,12 @@ static unsigned char vc5_mux_get_parent(struct clk_hw *hw)
container_of(hw, struct vc5_driver_data, clk_mux);
const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN;
unsigned int src;
+ int ret;
+
+ ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src);
+ if (ret)
+ return 0;
- regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src);
src &= mask;
if (src == VC5_PRIM_SRC_SHDN_EN_XTAL)
@@ -276,6 +281,7 @@ static int vc5_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops vc5_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = vc5_mux_set_parent,
.get_parent = vc5_mux_get_parent,
};
@@ -286,19 +292,23 @@ static unsigned long vc5_dbl_recalc_rate(struct clk_hw *hw,
struct vc5_driver_data *vc5 =
container_of(hw, struct vc5_driver_data, clk_mul);
unsigned int premul;
+ int ret;
+
+ ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul);
+ if (ret)
+ return 0;
- regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul);
if (premul & VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ)
parent_rate *= 2;
return parent_rate;
}
-static long vc5_dbl_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int vc5_dbl_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- if ((*parent_rate == rate) || ((*parent_rate * 2) == rate))
- return rate;
+ if ((req->best_parent_rate == req->rate) || ((req->best_parent_rate * 2) == req->rate))
+ return 0;
else
return -EINVAL;
}
@@ -315,16 +325,14 @@ static int vc5_dbl_set_rate(struct clk_hw *hw, unsigned long rate,
else
mask = 0;
- regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN,
- VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ,
- mask);
-
- return 0;
+ return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN,
+ VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ,
+ mask);
}
static const struct clk_ops vc5_dbl_ops = {
.recalc_rate = vc5_dbl_recalc_rate,
- .round_rate = vc5_dbl_round_rate,
+ .determine_rate = vc5_dbl_determine_rate,
.set_rate = vc5_dbl_set_rate,
};
@@ -334,14 +342,19 @@ static unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw,
struct vc5_driver_data *vc5 =
container_of(hw, struct vc5_driver_data, clk_pfd);
unsigned int prediv, div;
+ int ret;
- regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv);
+ ret = regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv);
+ if (ret)
+ return 0;
/* The bypass_prediv is set, PLL fed from Ref_in directly. */
if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV)
return parent_rate;
- regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div);
+ ret = regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div);
+ if (ret)
+ return 0;
/* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */
if (div & VC5_REF_DIVIDER_SEL_PREDIV2)
@@ -350,24 +363,29 @@ static unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw,
return parent_rate / VC5_REF_DIVIDER_REF_DIV(div);
}
-static long vc5_pfd_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int vc5_pfd_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long idiv;
/* PLL cannot operate with input clock above 50 MHz. */
- if (rate > 50000000)
+ if (req->rate > 50000000)
return -EINVAL;
/* CLKIN within range of PLL input, feed directly to PLL. */
- if (*parent_rate <= 50000000)
- return *parent_rate;
+ if (req->best_parent_rate <= 50000000) {
+ req->rate = req->best_parent_rate;
- idiv = DIV_ROUND_UP(*parent_rate, rate);
+ return 0;
+ }
+
+ idiv = DIV_ROUND_UP(req->best_parent_rate, req->rate);
if (idiv > 127)
return -EINVAL;
- return *parent_rate / idiv;
+ req->rate = req->best_parent_rate / idiv;
+
+ return 0;
}
static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -376,15 +394,17 @@ static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
struct vc5_driver_data *vc5 =
container_of(hw, struct vc5_driver_data, clk_pfd);
unsigned long idiv;
+ int ret;
u8 div;
/* CLKIN within range of PLL input, feed directly to PLL. */
if (parent_rate <= 50000000) {
- regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
- VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV,
- VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
- regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00);
- return 0;
+ ret = regmap_set_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
+ VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00);
}
idiv = DIV_ROUND_UP(parent_rate, rate);
@@ -395,16 +415,17 @@ static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
else
div = VC5_REF_DIVIDER_REF_DIV(idiv);
- regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div);
- regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
- VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, 0);
+ ret = regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div);
+ if (ret)
+ return ret;
- return 0;
+ return regmap_clear_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
+ VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
}
static const struct clk_ops vc5_pfd_ops = {
.recalc_rate = vc5_pfd_recalc_rate,
- .round_rate = vc5_pfd_round_rate,
+ .determine_rate = vc5_pfd_determine_rate,
.set_rate = vc5_pfd_set_rate,
};
@@ -428,32 +449,32 @@ static unsigned long vc5_pll_recalc_rate(struct clk_hw *hw,
return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24);
}
-static long vc5_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int vc5_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
+ struct vc5_driver_data *vc5 = hwdata->vc5;
u32 div_int;
u64 div_frc;
- if (rate < VC5_PLL_VCO_MIN)
- rate = VC5_PLL_VCO_MIN;
- if (rate > VC5_PLL_VCO_MAX)
- rate = VC5_PLL_VCO_MAX;
+ req->rate = clamp(req->rate, VC5_PLL_VCO_MIN, vc5->chip_info->vco_max);
/* Determine integer part, which is 12 bit wide */
- div_int = rate / *parent_rate;
+ div_int = req->rate / req->best_parent_rate;
if (div_int > 0xfff)
- rate = *parent_rate * 0xfff;
+ req->rate = req->best_parent_rate * 0xfff;
/* Determine best fractional part, which is 24 bit wide */
- div_frc = rate % *parent_rate;
+ div_frc = req->rate % req->best_parent_rate;
div_frc *= BIT(24) - 1;
- do_div(div_frc, *parent_rate);
+ do_div(div_frc, req->best_parent_rate);
hwdata->div_int = div_int;
hwdata->div_frc = (u32)div_frc;
- return (*parent_rate * div_int) + ((*parent_rate * div_frc) >> 24);
+ req->rate = (req->best_parent_rate * div_int) + ((req->best_parent_rate * div_frc) >> 24);
+
+ return 0;
}
static int vc5_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -474,7 +495,7 @@ static int vc5_pll_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops vc5_pll_ops = {
.recalc_rate = vc5_pll_recalc_rate,
- .round_rate = vc5_pll_round_rate,
+ .determine_rate = vc5_pll_determine_rate,
.set_rate = vc5_pll_set_rate,
};
@@ -506,17 +527,17 @@ static unsigned long vc5_fod_recalc_rate(struct clk_hw *hw,
return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
}
-static long vc5_fod_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int vc5_fod_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw);
/* VCO frequency is divided by two before entering FOD */
- u32 f_in = *parent_rate / 2;
+ u32 f_in = req->best_parent_rate / 2;
u32 div_int;
u64 div_frc;
/* Determine integer part, which is 12 bit wide */
- div_int = f_in / rate;
+ div_int = f_in / req->rate;
/*
* WARNING: The clock chip does not output signal if the integer part
* of the divider is 0xfff and fractional part is non-zero.
@@ -524,18 +545,20 @@ static long vc5_fod_round_rate(struct clk_hw *hw, unsigned long rate,
*/
if (div_int > 0xffe) {
div_int = 0xffe;
- rate = f_in / div_int;
+ req->rate = f_in / div_int;
}
/* Determine best fractional part, which is 30 bit wide */
- div_frc = f_in % rate;
+ div_frc = f_in % req->rate;
div_frc <<= 24;
- do_div(div_frc, rate);
+ do_div(div_frc, req->rate);
hwdata->div_int = div_int;
hwdata->div_frc = (u32)div_frc;
- return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
+ req->rate = div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc);
+
+ return 0;
}
static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -551,9 +574,12 @@ static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
hwdata->div_int >> 4, hwdata->div_int << 4,
0
};
+ int ret;
- regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
- data, 14);
+ ret = regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
+ data, 14);
+ if (ret)
+ return ret;
/*
* Toggle magic bit in undocumented register for unknown reason.
@@ -561,17 +587,18 @@ static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
* datasheet somewhat implies this is needed, but the register
* and the bit is not documented.
*/
- regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
- VC5_GLOBAL_REGISTER_GLOBAL_RESET, 0);
- regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
- VC5_GLOBAL_REGISTER_GLOBAL_RESET,
- VC5_GLOBAL_REGISTER_GLOBAL_RESET);
- return 0;
+ ret = regmap_clear_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
+ VC5_GLOBAL_REGISTER_GLOBAL_RESET);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
+ VC5_GLOBAL_REGISTER_GLOBAL_RESET);
}
static const struct clk_ops vc5_fod_ops = {
.recalc_rate = vc5_fod_recalc_rate,
- .round_rate = vc5_fod_round_rate,
+ .determine_rate = vc5_fod_determine_rate,
.set_rate = vc5_fod_set_rate,
};
@@ -594,10 +621,9 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
* registers.
*/
if (vc5->chip_info->flags & VC5_HAS_BYPASS_SYNC_BIT) {
- ret = regmap_update_bits(vc5->regmap,
- VC5_RESERVED_X0(hwdata->num),
- VC5_RESERVED_X0_BYPASS_SYNC,
- VC5_RESERVED_X0_BYPASS_SYNC);
+ ret = regmap_set_bits(vc5->regmap,
+ VC5_RESERVED_X0(hwdata->num),
+ VC5_RESERVED_X0_BYPASS_SYNC);
if (ret)
return ret;
}
@@ -606,7 +632,10 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
* If the input mux is disabled, enable it first and
* select source from matching FOD.
*/
- regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
+ ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
+ if (ret)
+ return ret;
+
if ((src & mask) == 0) {
src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD;
ret = regmap_update_bits(vc5->regmap,
@@ -617,18 +646,22 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
}
/* Enable the clock buffer */
- regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
- VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
- VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
+ ret = regmap_set_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
+ VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
+ if (ret)
+ return ret;
+
if (hwdata->clk_output_cfg0_mask) {
dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n",
hwdata->num, hwdata->clk_output_cfg0_mask,
hwdata->clk_output_cfg0);
- regmap_update_bits(vc5->regmap,
- VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
- hwdata->clk_output_cfg0_mask,
- hwdata->clk_output_cfg0);
+ ret = regmap_update_bits(vc5->regmap,
+ VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
+ hwdata->clk_output_cfg0_mask,
+ hwdata->clk_output_cfg0);
+ if (ret)
+ return ret;
}
return 0;
@@ -640,8 +673,8 @@ static void vc5_clk_out_unprepare(struct clk_hw *hw)
struct vc5_driver_data *vc5 = hwdata->vc5;
/* Disable the clock buffer */
- regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
- VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0);
+ regmap_clear_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
+ VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
}
static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw)
@@ -656,8 +689,12 @@ static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw)
const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM |
VC5_OUT_DIV_CONTROL_SEL_EXT;
unsigned int src;
+ int ret;
+
+ ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
+ if (ret)
+ return 0;
- regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
src &= mask;
if (src == 0) /* Input mux set to DISABLED */
@@ -698,6 +735,7 @@ static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index)
static const struct clk_ops vc5_clk_out_ops = {
.prepare = vc5_clk_out_prepare,
.unprepare = vc5_clk_out_unprepare,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = vc5_clk_out_set_parent,
.get_parent = vc5_clk_out_get_parent,
};
@@ -725,6 +763,7 @@ static int vc5_map_index_to_output(const enum vc5_model model,
case IDT_VC5_5P49V5935:
case IDT_VC6_5P49V6901:
case IDT_VC6_5P49V6965:
+ case IDT_VC6_5P49V6975:
default:
return n;
}
@@ -819,22 +858,27 @@ static int vc5_update_cap_load(struct device_node *node, struct vc5_driver_data
{
u32 value;
int mapped_value;
+ int ret;
- if (!of_property_read_u32(node, "idt,xtal-load-femtofarads", &value)) {
- mapped_value = vc5_map_cap_value(value);
- if (mapped_value < 0)
- return mapped_value;
-
- /*
- * The mapped_value is really the high 6 bits of
- * VC5_XTAL_X1_LOAD_CAP and VC5_XTAL_X2_LOAD_CAP, so
- * shift the value 2 places.
- */
- regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03, mapped_value << 2);
- regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03, mapped_value << 2);
- }
+ if (of_property_read_u32(node, "idt,xtal-load-femtofarads", &value))
+ return 0;
- return 0;
+ mapped_value = vc5_map_cap_value(value);
+ if (mapped_value < 0)
+ return mapped_value;
+
+ /*
+ * The mapped_value is really the high 6 bits of
+ * VC5_XTAL_X1_LOAD_CAP and VC5_XTAL_X2_LOAD_CAP, so
+ * shift the value 2 places.
+ */
+ ret = regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03,
+ mapped_value << 2);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03,
+ mapped_value << 2);
}
static int vc5_update_slew(struct device_node *np_output,
@@ -920,7 +964,7 @@ static int vc5_probe(struct i2c_client *client)
i2c_set_clientdata(client, vc5);
vc5->client = client;
- vc5->chip_info = of_device_get_match_data(&client->dev);
+ vc5->chip_info = i2c_get_match_data(client);
vc5->pin_xin = devm_clk_get(&client->dev, "xin");
if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER)
@@ -956,7 +1000,10 @@ static int vc5_probe(struct i2c_client *client)
"could not read idt,output-enable-active\n");
}
- regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask, src_val);
+ ret = regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask,
+ src_val);
+ if (ret)
+ return ret;
/* Register clock input mux */
memset(&init, 0, sizeof(init));
@@ -992,6 +1039,11 @@ static int vc5_probe(struct i2c_client *client)
}
init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
+
init.ops = &vc5_mux_ops;
init.flags = 0;
init.parent_names = parent_names;
@@ -1006,6 +1058,10 @@ static int vc5_probe(struct i2c_client *client)
memset(&init, 0, sizeof(init));
init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl",
client->dev.of_node);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
init.ops = &vc5_dbl_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
@@ -1021,6 +1077,10 @@ static int vc5_probe(struct i2c_client *client)
/* Register PFD */
memset(&init, 0, sizeof(init));
init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
init.ops = &vc5_pfd_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
@@ -1038,6 +1098,10 @@ static int vc5_probe(struct i2c_client *client)
/* Register PLL */
memset(&init, 0, sizeof(init));
init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
init.ops = &vc5_pll_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
@@ -1057,6 +1121,10 @@ static int vc5_probe(struct i2c_client *client)
memset(&init, 0, sizeof(init));
init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d",
client->dev.of_node, idx);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
init.ops = &vc5_fod_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
@@ -1075,6 +1143,10 @@ static int vc5_probe(struct i2c_client *client)
memset(&init, 0, sizeof(init));
init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb",
client->dev.of_node);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
init.ops = &vc5_clk_out_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
@@ -1101,6 +1173,10 @@ static int vc5_probe(struct i2c_client *client)
memset(&init, 0, sizeof(init));
init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d",
client->dev.of_node, idx + 1);
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
init.ops = &vc5_clk_out_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
@@ -1138,7 +1214,7 @@ err_clk:
return ret;
}
-static int vc5_remove(struct i2c_client *client)
+static void vc5_remove(struct i2c_client *client)
{
struct vc5_driver_data *vc5 = i2c_get_clientdata(client);
@@ -1146,8 +1222,6 @@ static int vc5_remove(struct i2c_client *client)
if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)
clk_unregister_fixed_rate(vc5->pin_xin);
-
- return 0;
}
static int __maybe_unused vc5_suspend(struct device *dev)
@@ -1177,6 +1251,7 @@ static const struct vc5_chip_info idt_5p49v5923_info = {
.clk_fod_cnt = 2,
.clk_out_cnt = 3,
.flags = 0,
+ .vco_max = 3000000000UL,
};
static const struct vc5_chip_info idt_5p49v5925_info = {
@@ -1184,6 +1259,7 @@ static const struct vc5_chip_info idt_5p49v5925_info = {
.clk_fod_cnt = 4,
.clk_out_cnt = 5,
.flags = 0,
+ .vco_max = 3000000000UL,
};
static const struct vc5_chip_info idt_5p49v5933_info = {
@@ -1191,6 +1267,7 @@ static const struct vc5_chip_info idt_5p49v5933_info = {
.clk_fod_cnt = 2,
.clk_out_cnt = 3,
.flags = VC5_HAS_INTERNAL_XTAL,
+ .vco_max = 3000000000UL,
};
static const struct vc5_chip_info idt_5p49v5935_info = {
@@ -1198,13 +1275,23 @@ static const struct vc5_chip_info idt_5p49v5935_info = {
.clk_fod_cnt = 4,
.clk_out_cnt = 5,
.flags = VC5_HAS_INTERNAL_XTAL,
+ .vco_max = 3000000000UL,
+};
+
+static const struct vc5_chip_info idt_5p49v60_info = {
+ .model = IDT_VC6_5P49V60,
+ .clk_fod_cnt = 4,
+ .clk_out_cnt = 5,
+ .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT,
+ .vco_max = 2700000000UL,
};
static const struct vc5_chip_info idt_5p49v6901_info = {
.model = IDT_VC6_5P49V6901,
.clk_fod_cnt = 4,
.clk_out_cnt = 5,
- .flags = VC5_HAS_PFD_FREQ_DBL,
+ .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT,
+ .vco_max = 3000000000UL,
};
static const struct vc5_chip_info idt_5p49v6965_info = {
@@ -1212,15 +1299,26 @@ static const struct vc5_chip_info idt_5p49v6965_info = {
.clk_fod_cnt = 4,
.clk_out_cnt = 5,
.flags = VC5_HAS_BYPASS_SYNC_BIT,
+ .vco_max = 3000000000UL,
+};
+
+static const struct vc5_chip_info idt_5p49v6975_info = {
+ .model = IDT_VC6_5P49V6975,
+ .clk_fod_cnt = 4,
+ .clk_out_cnt = 5,
+ .flags = VC5_HAS_BYPASS_SYNC_BIT | VC5_HAS_INTERNAL_XTAL,
+ .vco_max = 3000000000UL,
};
static const struct i2c_device_id vc5_id[] = {
- { "5p49v5923", .driver_data = IDT_VC5_5P49V5923 },
- { "5p49v5925", .driver_data = IDT_VC5_5P49V5925 },
- { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 },
- { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 },
- { "5p49v6901", .driver_data = IDT_VC6_5P49V6901 },
- { "5p49v6965", .driver_data = IDT_VC6_5P49V6965 },
+ { "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info },
+ { "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info },
+ { "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info },
+ { "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info },
+ { "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info },
+ { "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info },
+ { "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info },
+ { "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, vc5_id);
@@ -1230,8 +1328,10 @@ static const struct of_device_id clk_vc5_of_match[] = {
{ .compatible = "idt,5p49v5925", .data = &idt_5p49v5925_info },
{ .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info },
{ .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info },
+ { .compatible = "idt,5p49v60", .data = &idt_5p49v60_info },
{ .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info },
{ .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info },
+ { .compatible = "idt,5p49v6975", .data = &idt_5p49v6975_info },
{ },
};
MODULE_DEVICE_TABLE(of, clk_vc5_of_match);
@@ -1244,7 +1344,7 @@ static struct i2c_driver vc5_driver = {
.pm = &vc5_pm_ops,
.of_match_table = clk_vc5_of_match,
},
- .probe_new = vc5_probe,
+ .probe = vc5_probe,
.remove = vc5_remove,
.id_table = vc5_id,
};
diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c
new file mode 100644
index 000000000000..adcc603e3259
--- /dev/null
+++ b/drivers/clk/clk-versaclock7.c
@@ -0,0 +1,1315 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common clock framework driver for the Versaclock7 family of timing devices.
+ *
+ * Copyright (c) 2022 Renesas Electronics Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/i2c.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/swab.h>
+
+/*
+ * 16-bit register address: the lower 8 bits of the register address come
+ * from the offset addr byte and the upper 8 bits come from the page register.
+ */
+#define VC7_PAGE_ADDR 0xFD
+#define VC7_PAGE_WINDOW 256
+#define VC7_MAX_REG 0x364
+
+/* Maximum number of banks supported by VC7 */
+#define VC7_NUM_BANKS 7
+
+/* Maximum number of FODs supported by VC7 */
+#define VC7_NUM_FOD 3
+
+/* Maximum number of IODs supported by VC7 */
+#define VC7_NUM_IOD 4
+
+/* Maximum number of outputs supported by VC7 */
+#define VC7_NUM_OUT 12
+
+/* VCO valid range is 9.5 GHz to 10.7 GHz */
+#define VC7_APLL_VCO_MIN 9500000000UL
+#define VC7_APLL_VCO_MAX 10700000000UL
+
+/* APLL denominator is fixed at 2^27 */
+#define VC7_APLL_DENOMINATOR_BITS 27
+
+/* FOD 1st stage denominator is fixed 2^34 */
+#define VC7_FOD_DENOMINATOR_BITS 34
+
+/* IOD can operate between 1kHz and 650MHz */
+#define VC7_IOD_RATE_MIN 1000UL
+#define VC7_IOD_RATE_MAX 650000000UL
+#define VC7_IOD_MIN_DIVISOR 14
+#define VC7_IOD_MAX_DIVISOR 0x1ffffff /* 25-bit */
+
+#define VC7_FOD_RATE_MIN 1000UL
+#define VC7_FOD_RATE_MAX 650000000UL
+#define VC7_FOD_1ST_STAGE_RATE_MIN 33000000UL /* 33 MHz */
+#define VC7_FOD_1ST_STAGE_RATE_MAX 650000000UL /* 650 MHz */
+#define VC7_FOD_1ST_INT_MAX 324
+#define VC7_FOD_2ND_INT_MIN 2
+#define VC7_FOD_2ND_INT_MAX 0x1ffff /* 17-bit */
+
+/* VC7 Registers */
+
+#define VC7_REG_XO_CNFG 0x2C
+#define VC7_REG_XO_CNFG_COUNT 4
+#define VC7_REG_XO_IB_H_DIV_SHIFT 24
+#define VC7_REG_XO_IB_H_DIV_MASK GENMASK(28, VC7_REG_XO_IB_H_DIV_SHIFT)
+
+#define VC7_REG_APLL_FB_DIV_FRAC 0x120
+#define VC7_REG_APLL_FB_DIV_FRAC_COUNT 4
+#define VC7_REG_APLL_FB_DIV_FRAC_MASK GENMASK(26, 0)
+
+#define VC7_REG_APLL_FB_DIV_INT 0x124
+#define VC7_REG_APLL_FB_DIV_INT_COUNT 2
+#define VC7_REG_APLL_FB_DIV_INT_MASK GENMASK(9, 0)
+
+#define VC7_REG_APLL_CNFG 0x127
+#define VC7_REG_APLL_EN_DOUBLER BIT(0)
+
+#define VC7_REG_OUT_BANK_CNFG(idx) (0x280 + (0x4 * (idx)))
+#define VC7_REG_OUTPUT_BANK_SRC_MASK GENMASK(2, 0)
+
+#define VC7_REG_FOD_INT_CNFG(idx) (0x1E0 + (0x10 * (idx)))
+#define VC7_REG_FOD_INT_CNFG_COUNT 8
+#define VC7_REG_FOD_1ST_INT_MASK GENMASK(8, 0)
+#define VC7_REG_FOD_2ND_INT_SHIFT 9
+#define VC7_REG_FOD_2ND_INT_MASK GENMASK(25, VC7_REG_FOD_2ND_INT_SHIFT)
+#define VC7_REG_FOD_FRAC_SHIFT 26
+#define VC7_REG_FOD_FRAC_MASK GENMASK_ULL(59, VC7_REG_FOD_FRAC_SHIFT)
+
+#define VC7_REG_IOD_INT_CNFG(idx) (0x1C0 + (0x8 * (idx)))
+#define VC7_REG_IOD_INT_CNFG_COUNT 4
+#define VC7_REG_IOD_INT_MASK GENMASK(24, 0)
+
+#define VC7_REG_ODRV_EN(idx) (0x240 + (0x4 * (idx)))
+#define VC7_REG_OUT_DIS BIT(0)
+
+struct vc7_driver_data;
+static const struct regmap_config vc7_regmap_config;
+
+/* Supported Renesas VC7 models */
+enum vc7_model {
+ VC7_RC21008A,
+};
+
+struct vc7_chip_info {
+ const enum vc7_model model;
+ const unsigned int banks[VC7_NUM_BANKS];
+ const unsigned int num_banks;
+ const unsigned int outputs[VC7_NUM_OUT];
+ const unsigned int num_outputs;
+};
+
+/*
+ * Changing the APLL frequency is currently not supported.
+ * The APLL will consist of an opaque block between the XO and FOD/IODs and
+ * its frequency will be computed based on the current state of the device.
+ */
+struct vc7_apll_data {
+ struct clk *clk;
+ struct vc7_driver_data *vc7;
+ u8 xo_ib_h_div;
+ u8 en_doubler;
+ u16 apll_fb_div_int;
+ u32 apll_fb_div_frac;
+};
+
+struct vc7_fod_data {
+ struct clk_hw hw;
+ struct vc7_driver_data *vc7;
+ unsigned int num;
+ u32 fod_1st_int;
+ u32 fod_2nd_int;
+ u64 fod_frac;
+};
+
+struct vc7_iod_data {
+ struct clk_hw hw;
+ struct vc7_driver_data *vc7;
+ unsigned int num;
+ u32 iod_int;
+};
+
+struct vc7_out_data {
+ struct clk_hw hw;
+ struct vc7_driver_data *vc7;
+ unsigned int num;
+ unsigned int out_dis;
+};
+
+struct vc7_driver_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ const struct vc7_chip_info *chip_info;
+
+ struct clk *pin_xin;
+ struct vc7_apll_data clk_apll;
+ struct vc7_fod_data clk_fod[VC7_NUM_FOD];
+ struct vc7_iod_data clk_iod[VC7_NUM_IOD];
+ struct vc7_out_data clk_out[VC7_NUM_OUT];
+};
+
+struct vc7_bank_src_map {
+ enum vc7_bank_src_type {
+ VC7_FOD,
+ VC7_IOD,
+ } type;
+ union _divider {
+ struct vc7_iod_data *iod;
+ struct vc7_fod_data *fod;
+ } src;
+};
+
+static struct clk_hw *vc7_of_clk_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct vc7_driver_data *vc7 = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= vc7->chip_info->num_outputs)
+ return ERR_PTR(-EINVAL);
+
+ return &vc7->clk_out[idx].hw;
+}
+
+static const unsigned int RC21008A_index_to_output_mapping[] = {
+ 1, 2, 3, 6, 7, 8, 10, 11
+};
+
+static int vc7_map_index_to_output(const enum vc7_model model, const unsigned int i)
+{
+ switch (model) {
+ case VC7_RC21008A:
+ return RC21008A_index_to_output_mapping[i];
+ default:
+ return i;
+ }
+}
+
+/* bank to output mapping, same across all variants */
+static const unsigned int output_bank_mapping[] = {
+ 0, /* Output 0 */
+ 1, /* Output 1 */
+ 2, /* Output 2 */
+ 2, /* Output 3 */
+ 3, /* Output 4 */
+ 3, /* Output 5 */
+ 3, /* Output 6 */
+ 3, /* Output 7 */
+ 4, /* Output 8 */
+ 4, /* Output 9 */
+ 5, /* Output 10 */
+ 6 /* Output 11 */
+};
+
+/**
+ * vc7_64_mul_64_to_128() - Multiply two u64 and return an unsigned 128-bit integer
+ * as an upper and lower part.
+ *
+ * @left: The left argument.
+ * @right: The right argument.
+ * @hi: The upper 64-bits of the 128-bit product.
+ * @lo: The lower 64-bits of the 128-bit product.
+ *
+ * From mul_64_64 in crypto/ecc.c:350 in the linux kernel, accessed in v5.17.2.
+ */
+static void vc7_64_mul_64_to_128(u64 left, u64 right, u64 *hi, u64 *lo)
+{
+ u64 a0 = left & 0xffffffffull;
+ u64 a1 = left >> 32;
+ u64 b0 = right & 0xffffffffull;
+ u64 b1 = right >> 32;
+ u64 m0 = a0 * b0;
+ u64 m1 = a0 * b1;
+ u64 m2 = a1 * b0;
+ u64 m3 = a1 * b1;
+
+ m2 += (m0 >> 32);
+ m2 += m1;
+
+ /* Overflow */
+ if (m2 < m1)
+ m3 += 0x100000000ull;
+
+ *lo = (m0 & 0xffffffffull) | (m2 << 32);
+ *hi = m3 + (m2 >> 32);
+}
+
+/**
+ * vc7_128_div_64_to_64() - Divides a 128-bit uint by a 64-bit divisor, return a 64-bit quotient.
+ *
+ * @numhi: The uppper 64-bits of the dividend.
+ * @numlo: The lower 64-bits of the dividend.
+ * @den: The denominator (divisor).
+ * @r: The remainder, pass NULL if the remainder is not needed.
+ *
+ * Originally from libdivide, modified to use kernel u64/u32 types.
+ *
+ * See https://github.com/ridiculousfish/libdivide/blob/master/libdivide.h#L471.
+ *
+ * Return: The 64-bit quotient of the division.
+ *
+ * In case of overflow of division by zero, max(u64) is returned.
+ */
+static u64 vc7_128_div_64_to_64(u64 numhi, u64 numlo, u64 den, u64 *r)
+{
+ /*
+ * We work in base 2**32.
+ * A uint32 holds a single digit. A uint64 holds two digits.
+ * Our numerator is conceptually [num3, num2, num1, num0].
+ * Our denominator is [den1, den0].
+ */
+ const u64 b = ((u64)1 << 32);
+
+ /* The high and low digits of our computed quotient. */
+ u32 q1, q0;
+
+ /* The normalization shift factor */
+ int shift;
+
+ /*
+ * The high and low digits of our denominator (after normalizing).
+ * Also the low 2 digits of our numerator (after normalizing).
+ */
+ u32 den1, den0, num1, num0;
+
+ /* A partial remainder; */
+ u64 rem;
+
+ /*
+ * The estimated quotient, and its corresponding remainder (unrelated
+ * to true remainder).
+ */
+ u64 qhat, rhat;
+
+ /* Variables used to correct the estimated quotient. */
+ u64 c1, c2;
+
+ /* Check for overflow and divide by 0. */
+ if (numhi >= den) {
+ if (r)
+ *r = ~0ull;
+ return ~0ull;
+ }
+
+ /*
+ * Determine the normalization factor. We multiply den by this, so that
+ * its leading digit is at least half b. In binary this means just
+ * shifting left by the number of leading zeros, so that there's a 1 in
+ * the MSB.
+ *
+ * We also shift numer by the same amount. This cannot overflow because
+ * numhi < den. The expression (-shift & 63) is the same as (64 -
+ * shift), except it avoids the UB of shifting by 64. The funny bitwise
+ * 'and' ensures that numlo does not get shifted into numhi if shift is
+ * 0. clang 11 has an x86 codegen bug here: see LLVM bug 50118. The
+ * sequence below avoids it.
+ */
+ shift = __builtin_clzll(den);
+ den <<= shift;
+ numhi <<= shift;
+ numhi |= (numlo >> (-shift & 63)) & (-(s64)shift >> 63);
+ numlo <<= shift;
+
+ /*
+ * Extract the low digits of the numerator and both digits of the
+ * denominator.
+ */
+ num1 = (u32)(numlo >> 32);
+ num0 = (u32)(numlo & 0xFFFFFFFFu);
+ den1 = (u32)(den >> 32);
+ den0 = (u32)(den & 0xFFFFFFFFu);
+
+ /*
+ * We wish to compute q1 = [n3 n2 n1] / [d1 d0].
+ * Estimate q1 as [n3 n2] / [d1], and then correct it.
+ * Note while qhat may be 2 digits, q1 is always 1 digit.
+ */
+ qhat = div64_u64_rem(numhi, den1, &rhat);
+ c1 = qhat * den0;
+ c2 = rhat * b + num1;
+ if (c1 > c2)
+ qhat -= (c1 - c2 > den) ? 2 : 1;
+ q1 = (u32)qhat;
+
+ /* Compute the true (partial) remainder. */
+ rem = numhi * b + num1 - q1 * den;
+
+ /*
+ * We wish to compute q0 = [rem1 rem0 n0] / [d1 d0].
+ * Estimate q0 as [rem1 rem0] / [d1] and correct it.
+ */
+ qhat = div64_u64_rem(rem, den1, &rhat);
+ c1 = qhat * den0;
+ c2 = rhat * b + num0;
+ if (c1 > c2)
+ qhat -= (c1 - c2 > den) ? 2 : 1;
+ q0 = (u32)qhat;
+
+ /* Return remainder if requested. */
+ if (r)
+ *r = (rem * b + num0 - q0 * den) >> shift;
+ return ((u64)q1 << 32) | q0;
+}
+
+static int vc7_get_bank_clk(struct vc7_driver_data *vc7,
+ unsigned int bank_idx,
+ unsigned int output_bank_src,
+ struct vc7_bank_src_map *map)
+{
+ /* Mapping from Table 38 in datasheet */
+ if (bank_idx == 0 || bank_idx == 1) {
+ switch (output_bank_src) {
+ case 0:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[0];
+ return 0;
+ case 1:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[1];
+ return 0;
+ case 4:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[0];
+ return 0;
+ case 5:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[1];
+ return 0;
+ default:
+ break;
+ }
+ } else if (bank_idx == 2) {
+ switch (output_bank_src) {
+ case 1:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[1];
+ return 0;
+ case 4:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[0];
+ return 0;
+ case 5:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[1];
+ return 0;
+ default:
+ break;
+ }
+ } else if (bank_idx == 3) {
+ switch (output_bank_src) {
+ case 4:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[0];
+ return 0;
+ case 5:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[1];
+ return 0;
+ case 6:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[2];
+ return 0;
+ default:
+ break;
+ }
+ } else if (bank_idx == 4) {
+ switch (output_bank_src) {
+ case 0:
+ /* CLKIN1 not supported in this driver */
+ break;
+ case 2:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[2];
+ return 0;
+ case 5:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[1];
+ return 0;
+ case 6:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[2];
+ return 0;
+ case 7:
+ /* CLKIN0 not supported in this driver */
+ break;
+ default:
+ break;
+ }
+ } else if (bank_idx == 5) {
+ switch (output_bank_src) {
+ case 0:
+ /* CLKIN1 not supported in this driver */
+ break;
+ case 1:
+ /* XIN_REFIN not supported in this driver */
+ break;
+ case 2:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[2];
+ return 0;
+ case 3:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[3];
+ return 0;
+ case 5:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[1];
+ return 0;
+ case 6:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[2];
+ return 0;
+ case 7:
+ /* CLKIN0 not supported in this driver */
+ break;
+ default:
+ break;
+ }
+ } else if (bank_idx == 6) {
+ switch (output_bank_src) {
+ case 0:
+ /* CLKIN1 not supported in this driver */
+ break;
+ case 2:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[2];
+ return 0;
+ case 3:
+ map->type = VC7_IOD,
+ map->src.iod = &vc7->clk_iod[3];
+ return 0;
+ case 5:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[1];
+ return 0;
+ case 6:
+ map->type = VC7_FOD,
+ map->src.fod = &vc7->clk_fod[2];
+ return 0;
+ case 7:
+ /* CLKIN0 not supported in this driver */
+ break;
+ default:
+ break;
+ }
+ }
+
+ pr_warn("bank_src%d = %d is not supported\n", bank_idx, output_bank_src);
+ return -1;
+}
+
+static int vc7_read_apll(struct vc7_driver_data *vc7)
+{
+ int err;
+ u32 val32;
+ u16 val16;
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_XO_CNFG,
+ (u32 *)&val32,
+ VC7_REG_XO_CNFG_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read XO_CNFG\n");
+ return err;
+ }
+
+ vc7->clk_apll.xo_ib_h_div = (val32 & VC7_REG_XO_IB_H_DIV_MASK)
+ >> VC7_REG_XO_IB_H_DIV_SHIFT;
+
+ err = regmap_read(vc7->regmap,
+ VC7_REG_APLL_CNFG,
+ &val32);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read APLL_CNFG\n");
+ return err;
+ }
+
+ vc7->clk_apll.en_doubler = val32 & VC7_REG_APLL_EN_DOUBLER;
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_APLL_FB_DIV_FRAC,
+ (u32 *)&val32,
+ VC7_REG_APLL_FB_DIV_FRAC_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read APLL_FB_DIV_FRAC\n");
+ return err;
+ }
+
+ vc7->clk_apll.apll_fb_div_frac = val32 & VC7_REG_APLL_FB_DIV_FRAC_MASK;
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_APLL_FB_DIV_INT,
+ (u16 *)&val16,
+ VC7_REG_APLL_FB_DIV_INT_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read APLL_FB_DIV_INT\n");
+ return err;
+ }
+
+ vc7->clk_apll.apll_fb_div_int = val16 & VC7_REG_APLL_FB_DIV_INT_MASK;
+
+ return 0;
+}
+
+static int vc7_read_fod(struct vc7_driver_data *vc7, unsigned int idx)
+{
+ int err;
+ u64 val;
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_FOD_INT_CNFG(idx),
+ (u64 *)&val,
+ VC7_REG_FOD_INT_CNFG_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read FOD%d\n", idx);
+ return err;
+ }
+
+ vc7->clk_fod[idx].fod_1st_int = (val & VC7_REG_FOD_1ST_INT_MASK);
+ vc7->clk_fod[idx].fod_2nd_int =
+ (val & VC7_REG_FOD_2ND_INT_MASK) >> VC7_REG_FOD_2ND_INT_SHIFT;
+ vc7->clk_fod[idx].fod_frac = (val & VC7_REG_FOD_FRAC_MASK)
+ >> VC7_REG_FOD_FRAC_SHIFT;
+
+ return 0;
+}
+
+static int vc7_write_fod(struct vc7_driver_data *vc7, unsigned int idx)
+{
+ int err;
+ u64 val;
+
+ /*
+ * FOD dividers are part of an atomic group where fod_1st_int,
+ * fod_2nd_int, and fod_frac must be written together. The new divider
+ * is applied when the MSB of fod_frac is written.
+ */
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_FOD_INT_CNFG(idx),
+ (u64 *)&val,
+ VC7_REG_FOD_INT_CNFG_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read FOD%d\n", idx);
+ return err;
+ }
+
+ val = u64_replace_bits(val,
+ vc7->clk_fod[idx].fod_1st_int,
+ VC7_REG_FOD_1ST_INT_MASK);
+ val = u64_replace_bits(val,
+ vc7->clk_fod[idx].fod_2nd_int,
+ VC7_REG_FOD_2ND_INT_MASK);
+ val = u64_replace_bits(val,
+ vc7->clk_fod[idx].fod_frac,
+ VC7_REG_FOD_FRAC_MASK);
+
+ err = regmap_bulk_write(vc7->regmap,
+ VC7_REG_FOD_INT_CNFG(idx),
+ (u64 *)&val,
+ sizeof(u64));
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to write FOD%d\n", idx);
+ return err;
+ }
+
+ return 0;
+}
+
+static int vc7_read_iod(struct vc7_driver_data *vc7, unsigned int idx)
+{
+ int err;
+ u32 val;
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_IOD_INT_CNFG(idx),
+ (u32 *)&val,
+ VC7_REG_IOD_INT_CNFG_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read IOD%d\n", idx);
+ return err;
+ }
+
+ vc7->clk_iod[idx].iod_int = (val & VC7_REG_IOD_INT_MASK);
+
+ return 0;
+}
+
+static int vc7_write_iod(struct vc7_driver_data *vc7, unsigned int idx)
+{
+ int err;
+ u32 val;
+
+ /*
+ * IOD divider field is atomic and all bits must be written.
+ * The new divider is applied when the MSB of iod_int is written.
+ */
+
+ err = regmap_bulk_read(vc7->regmap,
+ VC7_REG_IOD_INT_CNFG(idx),
+ (u32 *)&val,
+ VC7_REG_IOD_INT_CNFG_COUNT);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read IOD%d\n", idx);
+ return err;
+ }
+
+ val = u32_replace_bits(val,
+ vc7->clk_iod[idx].iod_int,
+ VC7_REG_IOD_INT_MASK);
+
+ err = regmap_bulk_write(vc7->regmap,
+ VC7_REG_IOD_INT_CNFG(idx),
+ (u32 *)&val,
+ sizeof(u32));
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to write IOD%d\n", idx);
+ return err;
+ }
+
+ return 0;
+}
+
+static int vc7_read_output(struct vc7_driver_data *vc7, unsigned int idx)
+{
+ int err;
+ unsigned int val, out_num;
+
+ out_num = vc7_map_index_to_output(vc7->chip_info->model, idx);
+ err = regmap_read(vc7->regmap,
+ VC7_REG_ODRV_EN(out_num),
+ &val);
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to read ODRV_EN[%d]\n", idx);
+ return err;
+ }
+
+ vc7->clk_out[idx].out_dis = val & VC7_REG_OUT_DIS;
+
+ return 0;
+}
+
+static int vc7_write_output(struct vc7_driver_data *vc7, unsigned int idx)
+{
+ int err;
+ unsigned int out_num;
+
+ out_num = vc7_map_index_to_output(vc7->chip_info->model, idx);
+ err = regmap_write_bits(vc7->regmap,
+ VC7_REG_ODRV_EN(out_num),
+ VC7_REG_OUT_DIS,
+ vc7->clk_out[idx].out_dis);
+
+ if (err) {
+ dev_err(&vc7->client->dev, "failed to write ODRV_EN[%d]\n", idx);
+ return err;
+ }
+
+ return 0;
+}
+
+static unsigned long vc7_get_apll_rate(struct vc7_driver_data *vc7)
+{
+ int err;
+ unsigned long xtal_rate;
+ u64 refin_div, apll_rate;
+
+ xtal_rate = clk_get_rate(vc7->pin_xin);
+ err = vc7_read_apll(vc7);
+ if (err) {
+ dev_err(&vc7->client->dev, "unable to read apll\n");
+ return err;
+ }
+
+ /* 0 is bypassed, 1 is reserved */
+ if (vc7->clk_apll.xo_ib_h_div < 2)
+ refin_div = xtal_rate;
+ else
+ refin_div = div64_u64(xtal_rate, vc7->clk_apll.xo_ib_h_div);
+
+ if (vc7->clk_apll.en_doubler)
+ refin_div *= 2;
+
+ /* divider = int + (frac / 2^27) */
+ apll_rate = (refin_div * vc7->clk_apll.apll_fb_div_int) +
+ ((refin_div * vc7->clk_apll.apll_fb_div_frac) >> VC7_APLL_DENOMINATOR_BITS);
+
+ pr_debug("%s - xo_ib_h_div: %u, apll_fb_div_int: %u, apll_fb_div_frac: %u\n",
+ __func__, vc7->clk_apll.xo_ib_h_div, vc7->clk_apll.apll_fb_div_int,
+ vc7->clk_apll.apll_fb_div_frac);
+ pr_debug("%s - refin_div: %llu, apll rate: %llu\n",
+ __func__, refin_div, apll_rate);
+
+ return apll_rate;
+}
+
+static void vc7_calc_iod_divider(unsigned long rate, unsigned long parent_rate,
+ u32 *divider)
+{
+ *divider = DIV_ROUND_UP(parent_rate, rate);
+ if (*divider < VC7_IOD_MIN_DIVISOR)
+ *divider = VC7_IOD_MIN_DIVISOR;
+ if (*divider > VC7_IOD_MAX_DIVISOR)
+ *divider = VC7_IOD_MAX_DIVISOR;
+}
+
+static void vc7_calc_fod_1st_stage(unsigned long rate, unsigned long parent_rate,
+ u32 *div_int, u64 *div_frac)
+{
+ u64 rem;
+
+ *div_int = (u32)div64_u64_rem(parent_rate, rate, &rem);
+ *div_frac = div64_u64(rem << VC7_FOD_DENOMINATOR_BITS, rate);
+}
+
+static unsigned long vc7_calc_fod_1st_stage_rate(unsigned long parent_rate,
+ u32 fod_1st_int, u64 fod_frac)
+{
+ u64 numer, denom, hi, lo, divisor;
+
+ numer = fod_frac;
+ denom = BIT_ULL(VC7_FOD_DENOMINATOR_BITS);
+
+ if (fod_frac) {
+ vc7_64_mul_64_to_128(parent_rate, denom, &hi, &lo);
+ divisor = ((u64)fod_1st_int * denom) + numer;
+ return vc7_128_div_64_to_64(hi, lo, divisor, NULL);
+ }
+
+ return div64_u64(parent_rate, fod_1st_int);
+}
+
+static unsigned long vc7_calc_fod_2nd_stage_rate(unsigned long parent_rate,
+ u32 fod_1st_int, u32 fod_2nd_int, u64 fod_frac)
+{
+ unsigned long fod_1st_stage_rate;
+
+ fod_1st_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, fod_1st_int, fod_frac);
+
+ if (fod_2nd_int < 2)
+ return fod_1st_stage_rate;
+
+ /*
+ * There is a div-by-2 preceding the 2nd stage integer divider
+ * (not shown on block diagram) so the actual 2nd stage integer
+ * divisor is 2 * N.
+ */
+ return div64_u64(fod_1st_stage_rate >> 1, fod_2nd_int);
+}
+
+static void vc7_calc_fod_divider(unsigned long rate, unsigned long parent_rate,
+ u32 *fod_1st_int, u32 *fod_2nd_int, u64 *fod_frac)
+{
+ unsigned int allow_frac, i, best_frac_i;
+ unsigned long first_stage_rate;
+
+ vc7_calc_fod_1st_stage(rate, parent_rate, fod_1st_int, fod_frac);
+ first_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, *fod_1st_int, *fod_frac);
+
+ *fod_2nd_int = 0;
+
+ /* Do we need the second stage integer divider? */
+ if (first_stage_rate < VC7_FOD_1ST_STAGE_RATE_MIN) {
+ allow_frac = 0;
+ best_frac_i = VC7_FOD_2ND_INT_MIN;
+
+ for (i = VC7_FOD_2ND_INT_MIN; i <= VC7_FOD_2ND_INT_MAX; i++) {
+ /*
+ * 1) There is a div-by-2 preceding the 2nd stage integer divider
+ * (not shown on block diagram) so the actual 2nd stage integer
+ * divisor is 2 * N.
+ * 2) Attempt to find an integer solution first. This means stepping
+ * through each 2nd stage integer and recalculating the 1st stage
+ * until the 1st stage frequency is out of bounds. If no integer
+ * solution is found, use the best fractional solution.
+ */
+ vc7_calc_fod_1st_stage(parent_rate, rate * 2 * i, fod_1st_int, fod_frac);
+ first_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate,
+ *fod_1st_int,
+ *fod_frac);
+
+ /* Remember the first viable fractional solution */
+ if (best_frac_i == VC7_FOD_2ND_INT_MIN &&
+ first_stage_rate > VC7_FOD_1ST_STAGE_RATE_MIN) {
+ best_frac_i = i;
+ }
+
+ /* Is the divider viable? Prefer integer solutions over fractional. */
+ if (*fod_1st_int < VC7_FOD_1ST_INT_MAX &&
+ first_stage_rate >= VC7_FOD_1ST_STAGE_RATE_MIN &&
+ (allow_frac || *fod_frac == 0)) {
+ *fod_2nd_int = i;
+ break;
+ }
+
+ /* Ran out of divisors or the 1st stage frequency is out of range */
+ if (i >= VC7_FOD_2ND_INT_MAX ||
+ first_stage_rate > VC7_FOD_1ST_STAGE_RATE_MAX) {
+ allow_frac = 1;
+ i = best_frac_i;
+
+ /* Restore the best frac and rerun the loop for the last time */
+ if (best_frac_i != VC7_FOD_2ND_INT_MIN)
+ i--;
+
+ continue;
+ }
+ }
+ }
+}
+
+static unsigned long vc7_fod_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw);
+ struct vc7_driver_data *vc7 = fod->vc7;
+ int err;
+ unsigned long fod_rate;
+
+ err = vc7_read_fod(vc7, fod->num);
+ if (err) {
+ dev_err(&vc7->client->dev, "error reading registers for %s\n",
+ clk_hw_get_name(hw));
+ return err;
+ }
+
+ pr_debug("%s - %s: parent_rate: %lu\n", __func__, clk_hw_get_name(hw), parent_rate);
+
+ fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate, fod->fod_1st_int,
+ fod->fod_2nd_int, fod->fod_frac);
+
+ pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n",
+ __func__, clk_hw_get_name(hw),
+ fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac);
+ pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), fod_rate);
+
+ return fod_rate;
+}
+
+static int vc7_fod_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw);
+ unsigned long fod_rate;
+
+ pr_debug("%s - %s: requested rate: %lu, parent_rate: %lu\n",
+ __func__, clk_hw_get_name(hw), req->rate, req->best_parent_rate);
+
+ vc7_calc_fod_divider(req->rate, req->best_parent_rate,
+ &fod->fod_1st_int, &fod->fod_2nd_int, &fod->fod_frac);
+ fod_rate = vc7_calc_fod_2nd_stage_rate(req->best_parent_rate, fod->fod_1st_int,
+ fod->fod_2nd_int, fod->fod_frac);
+
+ pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n",
+ __func__, clk_hw_get_name(hw),
+ fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac);
+ pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), fod_rate);
+
+ req->rate = fod_rate;
+
+ return 0;
+}
+
+static int vc7_fod_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
+{
+ struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw);
+ struct vc7_driver_data *vc7 = fod->vc7;
+ unsigned long fod_rate;
+
+ pr_debug("%s - %s: rate: %lu, parent_rate: %lu\n",
+ __func__, clk_hw_get_name(hw), rate, parent_rate);
+
+ if (rate < VC7_FOD_RATE_MIN || rate > VC7_FOD_RATE_MAX) {
+ dev_err(&vc7->client->dev,
+ "requested frequency %lu Hz for %s is out of range\n",
+ rate, clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
+ vc7_write_fod(vc7, fod->num);
+
+ fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate, fod->fod_1st_int,
+ fod->fod_2nd_int, fod->fod_frac);
+
+ pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n",
+ __func__, clk_hw_get_name(hw),
+ fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac);
+ pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), fod_rate);
+
+ return 0;
+}
+
+static const struct clk_ops vc7_fod_ops = {
+ .recalc_rate = vc7_fod_recalc_rate,
+ .determine_rate = vc7_fod_determine_rate,
+ .set_rate = vc7_fod_set_rate,
+};
+
+static unsigned long vc7_iod_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw);
+ struct vc7_driver_data *vc7 = iod->vc7;
+ int err;
+ unsigned long iod_rate;
+
+ err = vc7_read_iod(vc7, iod->num);
+ if (err) {
+ dev_err(&vc7->client->dev, "error reading registers for %s\n",
+ clk_hw_get_name(hw));
+ return err;
+ }
+
+ iod_rate = div64_u64(parent_rate, iod->iod_int);
+
+ pr_debug("%s - %s: iod_int: %u\n", __func__, clk_hw_get_name(hw), iod->iod_int);
+ pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), iod_rate);
+
+ return iod_rate;
+}
+
+static int vc7_iod_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw);
+ unsigned long iod_rate;
+
+ pr_debug("%s - %s: requested rate: %lu, parent_rate: %lu\n",
+ __func__, clk_hw_get_name(hw), req->rate, req->best_parent_rate);
+
+ vc7_calc_iod_divider(req->rate, req->best_parent_rate, &iod->iod_int);
+ iod_rate = div64_u64(req->best_parent_rate, iod->iod_int);
+
+ pr_debug("%s - %s: iod_int: %u\n", __func__, clk_hw_get_name(hw), iod->iod_int);
+ pr_debug("%s - %s rate: %ld\n", __func__, clk_hw_get_name(hw), iod_rate);
+
+ req->rate = iod_rate;
+
+ return 0;
+}
+
+static int vc7_iod_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
+{
+ struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw);
+ struct vc7_driver_data *vc7 = iod->vc7;
+ unsigned long iod_rate;
+
+ pr_debug("%s - %s: rate: %lu, parent_rate: %lu\n",
+ __func__, clk_hw_get_name(hw), rate, parent_rate);
+
+ if (rate < VC7_IOD_RATE_MIN || rate > VC7_IOD_RATE_MAX) {
+ dev_err(&vc7->client->dev,
+ "requested frequency %lu Hz for %s is out of range\n",
+ rate, clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
+ vc7_write_iod(vc7, iod->num);
+
+ iod_rate = div64_u64(parent_rate, iod->iod_int);
+
+ pr_debug("%s - %s: iod_int: %u\n", __func__, clk_hw_get_name(hw), iod->iod_int);
+ pr_debug("%s - %s rate: %ld\n", __func__, clk_hw_get_name(hw), iod_rate);
+
+ return 0;
+}
+
+static const struct clk_ops vc7_iod_ops = {
+ .recalc_rate = vc7_iod_recalc_rate,
+ .determine_rate = vc7_iod_determine_rate,
+ .set_rate = vc7_iod_set_rate,
+};
+
+static int vc7_clk_out_prepare(struct clk_hw *hw)
+{
+ struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw);
+ struct vc7_driver_data *vc7 = out->vc7;
+ int err;
+
+ out->out_dis = 0;
+
+ err = vc7_write_output(vc7, out->num);
+ if (err) {
+ dev_err(&vc7->client->dev, "error writing registers for %s\n",
+ clk_hw_get_name(hw));
+ return err;
+ }
+
+ pr_debug("%s - %s: clk prepared\n", __func__, clk_hw_get_name(hw));
+
+ return 0;
+}
+
+static void vc7_clk_out_unprepare(struct clk_hw *hw)
+{
+ struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw);
+ struct vc7_driver_data *vc7 = out->vc7;
+ int err;
+
+ out->out_dis = 1;
+
+ err = vc7_write_output(vc7, out->num);
+ if (err) {
+ dev_err(&vc7->client->dev, "error writing registers for %s\n",
+ clk_hw_get_name(hw));
+ return;
+ }
+
+ pr_debug("%s - %s: clk unprepared\n", __func__, clk_hw_get_name(hw));
+}
+
+static int vc7_clk_out_is_enabled(struct clk_hw *hw)
+{
+ struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw);
+ struct vc7_driver_data *vc7 = out->vc7;
+ int err, is_enabled;
+
+ err = vc7_read_output(vc7, out->num);
+ if (err) {
+ dev_err(&vc7->client->dev, "error reading registers for %s\n",
+ clk_hw_get_name(hw));
+ return err;
+ }
+
+ is_enabled = !out->out_dis;
+
+ pr_debug("%s - %s: is_enabled=%d\n", __func__, clk_hw_get_name(hw), is_enabled);
+
+ return is_enabled;
+}
+
+static const struct clk_ops vc7_clk_out_ops = {
+ .prepare = vc7_clk_out_prepare,
+ .unprepare = vc7_clk_out_unprepare,
+ .is_enabled = vc7_clk_out_is_enabled,
+};
+
+static int vc7_probe(struct i2c_client *client)
+{
+ struct vc7_driver_data *vc7;
+ struct clk_init_data clk_init;
+ struct vc7_bank_src_map bank_src_map;
+ const char *node_name, *apll_name;
+ const char *parent_names[1];
+ unsigned int i, val, bank_idx, out_num;
+ unsigned long apll_rate;
+ int ret;
+
+ vc7 = devm_kzalloc(&client->dev, sizeof(*vc7), GFP_KERNEL);
+ if (!vc7)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, vc7);
+ vc7->client = client;
+ vc7->chip_info = i2c_get_match_data(client);
+
+ vc7->pin_xin = devm_clk_get(&client->dev, "xin");
+ if (PTR_ERR(vc7->pin_xin) == -EPROBE_DEFER) {
+ return dev_err_probe(&client->dev, -EPROBE_DEFER,
+ "xin not specified\n");
+ }
+
+ vc7->regmap = devm_regmap_init_i2c(client, &vc7_regmap_config);
+ if (IS_ERR(vc7->regmap)) {
+ return dev_err_probe(&client->dev, PTR_ERR(vc7->regmap),
+ "failed to allocate register map\n");
+ }
+
+ if (of_property_read_string(client->dev.of_node, "clock-output-names",
+ &node_name))
+ node_name = client->dev.of_node->name;
+
+ /* Register APLL */
+ apll_rate = vc7_get_apll_rate(vc7);
+ apll_name = kasprintf(GFP_KERNEL, "%s_apll", node_name);
+ vc7->clk_apll.clk = clk_register_fixed_rate(&client->dev, apll_name,
+ __clk_get_name(vc7->pin_xin),
+ 0, apll_rate);
+ kfree(apll_name); /* ccf made a copy of the name */
+ if (IS_ERR(vc7->clk_apll.clk)) {
+ return dev_err_probe(&client->dev, PTR_ERR(vc7->clk_apll.clk),
+ "failed to register apll\n");
+ }
+
+ /* Register FODs */
+ for (i = 0; i < VC7_NUM_FOD; i++) {
+ memset(&clk_init, 0, sizeof(clk_init));
+ clk_init.name = kasprintf(GFP_KERNEL, "%s_fod%d", node_name, i);
+ clk_init.ops = &vc7_fod_ops;
+ clk_init.parent_names = parent_names;
+ parent_names[0] = __clk_get_name(vc7->clk_apll.clk);
+ clk_init.num_parents = 1;
+ vc7->clk_fod[i].num = i;
+ vc7->clk_fod[i].vc7 = vc7;
+ vc7->clk_fod[i].hw.init = &clk_init;
+ ret = devm_clk_hw_register(&client->dev, &vc7->clk_fod[i].hw);
+ if (ret)
+ goto err_clk_register;
+ kfree(clk_init.name); /* ccf made a copy of the name */
+ }
+
+ /* Register IODs */
+ for (i = 0; i < VC7_NUM_IOD; i++) {
+ memset(&clk_init, 0, sizeof(clk_init));
+ clk_init.name = kasprintf(GFP_KERNEL, "%s_iod%d", node_name, i);
+ clk_init.ops = &vc7_iod_ops;
+ clk_init.parent_names = parent_names;
+ parent_names[0] = __clk_get_name(vc7->clk_apll.clk);
+ clk_init.num_parents = 1;
+ vc7->clk_iod[i].num = i;
+ vc7->clk_iod[i].vc7 = vc7;
+ vc7->clk_iod[i].hw.init = &clk_init;
+ ret = devm_clk_hw_register(&client->dev, &vc7->clk_iod[i].hw);
+ if (ret)
+ goto err_clk_register;
+ kfree(clk_init.name); /* ccf made a copy of the name */
+ }
+
+ /* Register outputs */
+ for (i = 0; i < vc7->chip_info->num_outputs; i++) {
+ out_num = vc7_map_index_to_output(vc7->chip_info->model, i);
+
+ /*
+ * This driver does not support remapping FOD/IOD to banks.
+ * The device state is read and the driver is setup to match
+ * the device's existing mapping.
+ */
+ bank_idx = output_bank_mapping[out_num];
+
+ regmap_read(vc7->regmap, VC7_REG_OUT_BANK_CNFG(bank_idx), &val);
+ val &= VC7_REG_OUTPUT_BANK_SRC_MASK;
+
+ memset(&bank_src_map, 0, sizeof(bank_src_map));
+ ret = vc7_get_bank_clk(vc7, bank_idx, val, &bank_src_map);
+ if (ret) {
+ dev_err_probe(&client->dev, ret,
+ "unable to register output %d\n", i);
+ return ret;
+ }
+
+ switch (bank_src_map.type) {
+ case VC7_FOD:
+ parent_names[0] = clk_hw_get_name(&bank_src_map.src.fod->hw);
+ break;
+ case VC7_IOD:
+ parent_names[0] = clk_hw_get_name(&bank_src_map.src.iod->hw);
+ break;
+ }
+
+ memset(&clk_init, 0, sizeof(clk_init));
+ clk_init.name = kasprintf(GFP_KERNEL, "%s_out%d", node_name, i);
+ clk_init.ops = &vc7_clk_out_ops;
+ clk_init.flags = CLK_SET_RATE_PARENT;
+ clk_init.parent_names = parent_names;
+ clk_init.num_parents = 1;
+ vc7->clk_out[i].num = i;
+ vc7->clk_out[i].vc7 = vc7;
+ vc7->clk_out[i].hw.init = &clk_init;
+ ret = devm_clk_hw_register(&client->dev, &vc7->clk_out[i].hw);
+ if (ret)
+ goto err_clk_register;
+ kfree(clk_init.name); /* ccf made a copy of the name */
+ }
+
+ ret = of_clk_add_hw_provider(client->dev.of_node, vc7_of_clk_get, vc7);
+ if (ret) {
+ dev_err_probe(&client->dev, ret, "unable to add clk provider\n");
+ goto err_clk;
+ }
+
+ return ret;
+
+err_clk_register:
+ dev_err_probe(&client->dev, ret,
+ "unable to register %s\n", clk_init.name);
+ kfree(clk_init.name); /* ccf made a copy of the name */
+err_clk:
+ clk_unregister_fixed_rate(vc7->clk_apll.clk);
+ return ret;
+}
+
+static void vc7_remove(struct i2c_client *client)
+{
+ struct vc7_driver_data *vc7 = i2c_get_clientdata(client);
+
+ of_clk_del_provider(client->dev.of_node);
+ clk_unregister_fixed_rate(vc7->clk_apll.clk);
+}
+
+static bool vc7_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg == VC7_PAGE_ADDR)
+ return false;
+
+ return true;
+}
+
+static const struct vc7_chip_info vc7_rc21008a_info = {
+ .model = VC7_RC21008A,
+ .num_banks = 6,
+ .num_outputs = 8,
+};
+
+static const struct regmap_range_cfg vc7_range_cfg[] = {
+{
+ .range_min = 0,
+ .range_max = VC7_MAX_REG,
+ .selector_reg = VC7_PAGE_ADDR,
+ .selector_mask = 0xFF,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = VC7_PAGE_WINDOW,
+}};
+
+static const struct regmap_config vc7_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = VC7_MAX_REG,
+ .ranges = vc7_range_cfg,
+ .num_ranges = ARRAY_SIZE(vc7_range_cfg),
+ .volatile_reg = vc7_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .can_multi_write = true,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct i2c_device_id vc7_i2c_id[] = {
+ { "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, vc7_i2c_id);
+
+static const struct of_device_id vc7_of_match[] = {
+ { .compatible = "renesas,rc21008a", .data = &vc7_rc21008a_info },
+ {}
+};
+MODULE_DEVICE_TABLE(of, vc7_of_match);
+
+static struct i2c_driver vc7_i2c_driver = {
+ .driver = {
+ .name = "vc7",
+ .of_match_table = vc7_of_match,
+ },
+ .probe = vc7_probe,
+ .remove = vc7_remove,
+ .id_table = vc7_i2c_id,
+};
+module_i2c_driver(vc7_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alex Helms <alexander.helms.jy@renesas.com");
+MODULE_DESCRIPTION("Renesas Versaclock7 common clock framework driver");
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index 2a74a713ad59..eae5b3fbfb82 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -128,30 +128,31 @@ static unsigned long vt8500_dclk_recalc_rate(struct clk_hw *hw,
return parent_rate / div;
}
-static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int vt8500_dclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_device *cdev = to_clk_device(hw);
u32 divisor;
- if (rate == 0)
+ if (req->rate == 0)
return 0;
- divisor = *prate / rate;
+ divisor = req->best_parent_rate / req->rate;
/* If prate / rate would be decimal, incr the divisor */
- if (rate * divisor < *prate)
+ if (req->rate * divisor < req->best_parent_rate)
divisor++;
/*
* If this is a request for SDMMC we have to adjust the divisor
* when >31 to use the fixed predivisor
*/
- if ((cdev->div_mask == 0x3F) && (divisor > 31)) {
+ if ((cdev->div_mask == 0x3F) && (divisor > 31))
divisor = 64 * ((divisor / 64) + 1);
- }
- return *prate / divisor;
+ req->rate = req->best_parent_rate / divisor;
+
+ return 0;
}
static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -202,7 +203,7 @@ static const struct clk_ops vt8500_gated_clk_ops = {
};
static const struct clk_ops vt8500_divisor_clk_ops = {
- .round_rate = vt8500_dclk_round_rate,
+ .determine_rate = vt8500_dclk_determine_rate,
.set_rate = vt8500_dclk_set_rate,
.recalc_rate = vt8500_dclk_recalc_rate,
};
@@ -211,7 +212,7 @@ static const struct clk_ops vt8500_gated_divisor_clk_ops = {
.enable = vt8500_dclk_enable,
.disable = vt8500_dclk_disable,
.is_enabled = vt8500_dclk_is_enabled,
- .round_rate = vt8500_dclk_round_rate,
+ .determine_rate = vt8500_dclk_determine_rate,
.set_rate = vt8500_dclk_set_rate,
.recalc_rate = vt8500_dclk_recalc_rate,
};
@@ -594,8 +595,8 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int vtwm_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_pll *pll = to_clk_pll(hw);
u32 filter, mul, div1, div2;
@@ -604,33 +605,43 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
switch (pll->type) {
case PLL_TYPE_VT8500:
- ret = vt8500_find_pll_bits(rate, *prate, &mul, &div1);
+ ret = vt8500_find_pll_bits(req->rate, req->best_parent_rate,
+ &mul, &div1);
if (!ret)
- round_rate = VT8500_BITS_TO_FREQ(*prate, mul, div1);
+ round_rate = VT8500_BITS_TO_FREQ(req->best_parent_rate,
+ mul, div1);
break;
case PLL_TYPE_WM8650:
- ret = wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2);
+ ret = wm8650_find_pll_bits(req->rate, req->best_parent_rate,
+ &mul, &div1, &div2);
if (!ret)
- round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2);
+ round_rate = WM8650_BITS_TO_FREQ(req->best_parent_rate,
+ mul, div1, div2);
break;
case PLL_TYPE_WM8750:
- ret = wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
+ ret = wm8750_find_pll_bits(req->rate, req->best_parent_rate,
+ &filter, &mul, &div1, &div2);
if (!ret)
- round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
+ round_rate = WM8750_BITS_TO_FREQ(req->best_parent_rate,
+ mul, div1, div2);
break;
case PLL_TYPE_WM8850:
- ret = wm8850_find_pll_bits(rate, *prate, &mul, &div1, &div2);
+ ret = wm8850_find_pll_bits(req->rate, req->best_parent_rate,
+ &mul, &div1, &div2);
if (!ret)
- round_rate = WM8850_BITS_TO_FREQ(*prate, mul, div1, div2);
+ round_rate = WM8850_BITS_TO_FREQ(req->best_parent_rate,
+ mul, div1, div2);
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
if (ret)
- return ret;
+ req->rate = ret;
+ else
+ req->rate = round_rate;
- return round_rate;
+ return 0;
}
static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
@@ -665,7 +676,7 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
}
static const struct clk_ops vtwm_pll_ops = {
- .round_rate = vtwm_pll_round_rate,
+ .determine_rate = vtwm_pll_determine_rate,
.set_rate = vtwm_pll_set_rate,
.recalc_rate = vtwm_pll_recalc_rate,
};
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index ae6dd38ec053..263e927138c2 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -133,18 +133,20 @@ static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *unused)
+static int wm831x_fll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
int best = 0;
int i;
for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
- if (abs(wm831x_fll_auto_rates[i] - rate) <
- abs(wm831x_fll_auto_rates[best] - rate))
+ if (abs(wm831x_fll_auto_rates[i] - req->rate) <
+ abs(wm831x_fll_auto_rates[best] - req->rate))
best = i;
- return wm831x_fll_auto_rates[best];
+ req->rate = wm831x_fll_auto_rates[best];
+
+ return 0;
}
static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -214,7 +216,7 @@ static const struct clk_ops wm831x_fll_ops = {
.is_prepared = wm831x_fll_is_prepared,
.prepare = wm831x_fll_prepare,
.unprepare = wm831x_fll_unprepare,
- .round_rate = wm831x_fll_round_rate,
+ .determine_rate = wm831x_fll_determine_rate,
.recalc_rate = wm831x_fll_recalc_rate,
.set_rate = wm831x_fll_set_rate,
.get_parent = wm831x_fll_get_parent,
@@ -329,6 +331,7 @@ static const struct clk_ops wm831x_clkout_ops = {
.is_prepared = wm831x_clkout_is_prepared,
.prepare = wm831x_clkout_prepare,
.unprepare = wm831x_clkout_unprepare,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = wm831x_clkout_get_parent,
.set_parent = wm831x_clkout_set_parent,
};
diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c
index 857217cbcef8..92e39f3237c2 100644
--- a/drivers/clk/clk-xgene.c
+++ b/drivers/clk/clk-xgene.c
@@ -7,6 +7,7 @@
*/
#include <linux/module.h>
#include <linux/spinlock.h>
+#include <linux/string_choices.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/clkdev.h>
@@ -270,23 +271,28 @@ static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw,
return ret;
}
-static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int xgene_clk_pmd_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
u64 ret, scale;
- if (!rate || rate >= *parent_rate)
- return *parent_rate;
+ if (!req->rate || req->rate >= req->best_parent_rate) {
+ req->rate = req->best_parent_rate;
+
+ return 0;
+ }
/* freq = parent_rate * scaler / denom */
- ret = rate * fd->denom;
- scale = DIV_ROUND_UP_ULL(ret, *parent_rate);
+ ret = req->rate * fd->denom;
+ scale = DIV_ROUND_UP_ULL(ret, req->best_parent_rate);
- ret = (u64)*parent_rate * scale;
+ ret = (u64)req->best_parent_rate * scale;
do_div(ret, fd->denom);
- return ret;
+ req->rate = ret;
+
+ return 0;
}
static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -332,7 +338,7 @@ static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops xgene_clk_pmd_ops = {
.recalc_rate = xgene_clk_pmd_recalc_rate,
- .round_rate = xgene_clk_pmd_round_rate,
+ .determine_rate = xgene_clk_pmd_determine_rate,
.set_rate = xgene_clk_pmd_set_rate,
};
@@ -520,12 +526,11 @@ static int xgene_clk_is_enabled(struct clk_hw *hw)
data = xgene_clk_read(pclk->param.csr_reg +
pclk->param.reg_clk_offset);
pr_debug("%s clock is %s\n", clk_hw_get_name(hw),
- data & pclk->param.reg_clk_mask ? "enabled" :
- "disabled");
+ str_enabled_disabled(data & pclk->param.reg_clk_mask));
+ } else {
+ return 1;
}
- if (!pclk->param.csr_reg)
- return 1;
return data & pclk->param.reg_clk_mask ? 1 : 0;
}
@@ -593,23 +598,25 @@ static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate,
return parent_rate / divider_save;
}
-static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int xgene_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct xgene_clk *pclk = to_xgene_clk(hw);
- unsigned long parent_rate = *prate;
+ unsigned long parent_rate = req->best_parent_rate;
u32 divider;
if (pclk->param.divider_reg) {
/* Let's compute the divider */
- if (rate > parent_rate)
- rate = parent_rate;
- divider = parent_rate / rate; /* Rounded down */
+ if (req->rate > parent_rate)
+ req->rate = parent_rate;
+ divider = parent_rate / req->rate; /* Rounded down */
} else {
divider = 1;
}
- return parent_rate / divider;
+ req->rate = parent_rate / divider;
+
+ return 0;
}
static const struct clk_ops xgene_clk_ops = {
@@ -618,7 +625,7 @@ static const struct clk_ops xgene_clk_ops = {
.is_enabled = xgene_clk_is_enabled,
.recalc_rate = xgene_clk_recalc_rate,
.set_rate = xgene_clk_set_rate,
- .round_rate = xgene_clk_round_rate,
+ .determine_rate = xgene_clk_determine_rate,
};
static struct clk *xgene_register_clk(struct device *dev,
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index bd0b35cac83e..85d2f2481acf 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -6,21 +6,24 @@
* Standard functionality for the common clock API. See Documentation/driver-api/clk.rst
*/
+#include <linux/clk/clk-conf.h>
+#include <linux/clkdev.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
-#include <linux/clk/clk-conf.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
+#include <linux/device.h>
#include <linux/err.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
#include <linux/list.h>
-#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/device.h>
-#include <linux/init.h>
#include <linux/pm_runtime.h>
#include <linux/sched.h>
-#include <linux/clkdev.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/stringhash.h>
#include "clk.h"
@@ -33,10 +36,17 @@ static struct task_struct *enable_owner;
static int prepare_refcnt;
static int enable_refcnt;
+#define CLK_HASH_BITS 9
+static DEFINE_HASHTABLE(clk_hashtable, CLK_HASH_BITS);
+
static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
+/* List of registered clks that use runtime PM */
+static HLIST_HEAD(clk_rpm_list);
+static DEFINE_MUTEX(clk_rpm_list_lock);
+
static const struct hlist_head *all_lists[] = {
&clk_root_list,
&clk_orphan_list,
@@ -59,6 +69,7 @@ struct clk_core {
struct clk_hw *hw;
struct module *owner;
struct device *dev;
+ struct hlist_node rpm_node;
struct device_node *of_node;
struct clk_core *parent;
struct clk_parent_map *parents;
@@ -82,6 +93,7 @@ struct clk_core {
struct clk_duty duty;
struct hlist_head children;
struct hlist_node child_node;
+ struct hlist_node hashtable_node;
struct hlist_head clks;
unsigned int notifier_count;
#ifdef CONFIG_DEBUG_FS
@@ -122,6 +134,89 @@ static void clk_pm_runtime_put(struct clk_core *core)
pm_runtime_put_sync(core->dev);
}
+/**
+ * clk_pm_runtime_get_all() - Runtime "get" all clk provider devices
+ *
+ * Call clk_pm_runtime_get() on all runtime PM enabled clks in the clk tree so
+ * that disabling unused clks avoids a deadlock where a device is runtime PM
+ * resuming/suspending and the runtime PM callback is trying to grab the
+ * prepare_lock for something like clk_prepare_enable() while
+ * clk_disable_unused_subtree() holds the prepare_lock and is trying to runtime
+ * PM resume/suspend the device as well.
+ *
+ * Context: Acquires the 'clk_rpm_list_lock' and returns with the lock held on
+ * success. Otherwise the lock is released on failure.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int clk_pm_runtime_get_all(void)
+{
+ int ret;
+ struct clk_core *core, *failed;
+
+ /*
+ * Grab the list lock to prevent any new clks from being registered
+ * or unregistered until clk_pm_runtime_put_all().
+ */
+ mutex_lock(&clk_rpm_list_lock);
+
+ /*
+ * Runtime PM "get" all the devices that are needed for the clks
+ * currently registered. Do this without holding the prepare_lock, to
+ * avoid the deadlock.
+ */
+ hlist_for_each_entry(core, &clk_rpm_list, rpm_node) {
+ ret = clk_pm_runtime_get(core);
+ if (ret) {
+ failed = core;
+ pr_err("clk: Failed to runtime PM get '%s' for clk '%s'\n",
+ dev_name(failed->dev), failed->name);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ hlist_for_each_entry(core, &clk_rpm_list, rpm_node) {
+ if (core == failed)
+ break;
+
+ clk_pm_runtime_put(core);
+ }
+ mutex_unlock(&clk_rpm_list_lock);
+
+ return ret;
+}
+
+/**
+ * clk_pm_runtime_put_all() - Runtime "put" all clk provider devices
+ *
+ * Put the runtime PM references taken in clk_pm_runtime_get_all() and release
+ * the 'clk_rpm_list_lock'.
+ */
+static void clk_pm_runtime_put_all(void)
+{
+ struct clk_core *core;
+
+ hlist_for_each_entry(core, &clk_rpm_list, rpm_node)
+ clk_pm_runtime_put(core);
+ mutex_unlock(&clk_rpm_list_lock);
+}
+
+static void clk_pm_runtime_init(struct clk_core *core)
+{
+ struct device *dev = core->dev;
+
+ if (dev && pm_runtime_enabled(dev)) {
+ core->rpm_enabled = true;
+
+ mutex_lock(&clk_rpm_list_lock);
+ hlist_add_head(&core->rpm_node, &clk_rpm_list);
+ mutex_unlock(&clk_rpm_list_lock);
+ }
+}
+
/*** locking ***/
static void clk_prepare_lock(void)
{
@@ -244,6 +339,17 @@ static bool clk_core_is_enabled(struct clk_core *core)
}
}
+ /*
+ * This could be called with the enable lock held, or from atomic
+ * context. If the parent isn't enabled already, we can't do
+ * anything here. We can also assume this clock isn't enabled.
+ */
+ if ((core->flags & CLK_OPS_PARENT_ENABLE) && core->parent)
+ if (!clk_core_is_enabled(core->parent)) {
+ ret = false;
+ goto done;
+ }
+
ret = core->ops->is_enabled(core->hw);
done:
if (core->rpm_enabled)
@@ -266,6 +372,18 @@ const char *clk_hw_get_name(const struct clk_hw *hw)
}
EXPORT_SYMBOL_GPL(clk_hw_get_name);
+struct device *clk_hw_get_dev(const struct clk_hw *hw)
+{
+ return hw->core->dev;
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_dev);
+
+struct device_node *clk_hw_get_of_node(const struct clk_hw *hw)
+{
+ return hw->core->of_node;
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_of_node);
+
struct clk_hw *__clk_get_hw(struct clk *clk)
{
return !clk ? NULL : clk->core->hw;
@@ -284,45 +402,20 @@ struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw)
}
EXPORT_SYMBOL_GPL(clk_hw_get_parent);
-static struct clk_core *__clk_lookup_subtree(const char *name,
- struct clk_core *core)
-{
- struct clk_core *child;
- struct clk_core *ret;
-
- if (!strcmp(core->name, name))
- return core;
-
- hlist_for_each_entry(child, &core->children, child_node) {
- ret = __clk_lookup_subtree(name, child);
- if (ret)
- return ret;
- }
-
- return NULL;
-}
-
static struct clk_core *clk_core_lookup(const char *name)
{
- struct clk_core *root_clk;
- struct clk_core *ret;
+ struct clk_core *core;
+ u32 hash;
if (!name)
return NULL;
- /* search the 'proper' clk tree first */
- hlist_for_each_entry(root_clk, &clk_root_list, child_node) {
- ret = __clk_lookup_subtree(name, root_clk);
- if (ret)
- return ret;
- }
+ hash = full_name_hash(NULL, name, strlen(name));
- /* if not found, then search the orphan tree */
- hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) {
- ret = __clk_lookup_subtree(name, root_clk);
- if (ret)
- return ret;
- }
+ /* search the hashtable */
+ hash_for_each_possible(clk_hashtable, core, hashtable_node, hash)
+ if (!strcmp(core->name, name))
+ return core;
return NULL;
}
@@ -407,6 +500,9 @@ static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
if (IS_ERR(hw))
return ERR_CAST(hw);
+ if (!hw)
+ return NULL;
+
return hw->core;
}
@@ -506,12 +602,6 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
}
EXPORT_SYMBOL_GPL(clk_hw_is_prepared);
-bool clk_hw_rate_is_protected(const struct clk_hw *hw)
-{
- return clk_core_rate_is_protected(hw->core);
-}
-EXPORT_SYMBOL_GPL(clk_hw_rate_is_protected);
-
bool clk_hw_is_enabled(const struct clk_hw *hw)
{
return clk_core_is_enabled(hw->core);
@@ -536,6 +626,94 @@ static bool mux_is_better_rate(unsigned long rate, unsigned long now,
return now <= rate && now > best;
}
+static void clk_core_init_rate_req(struct clk_core * const core,
+ struct clk_rate_request *req,
+ unsigned long rate);
+
+static int clk_core_round_rate_nolock(struct clk_core *core,
+ struct clk_rate_request *req);
+
+static bool clk_core_has_parent(struct clk_core *core, const struct clk_core *parent)
+{
+ struct clk_core *tmp;
+ unsigned int i;
+
+ /* Optimize for the case where the parent is already the parent. */
+ if (core->parent == parent)
+ return true;
+
+ for (i = 0; i < core->num_parents; i++) {
+ tmp = clk_core_get_parent_by_index(core, i);
+ if (!tmp)
+ continue;
+
+ if (tmp == parent)
+ return true;
+ }
+
+ return false;
+}
+
+static void
+clk_core_forward_rate_req(struct clk_core *core,
+ const struct clk_rate_request *old_req,
+ struct clk_core *parent,
+ struct clk_rate_request *req,
+ unsigned long parent_rate)
+{
+ if (WARN_ON(!clk_core_has_parent(core, parent)))
+ return;
+
+ clk_core_init_rate_req(parent, req, parent_rate);
+
+ if (req->min_rate < old_req->min_rate)
+ req->min_rate = old_req->min_rate;
+
+ if (req->max_rate > old_req->max_rate)
+ req->max_rate = old_req->max_rate;
+}
+
+static int
+clk_core_determine_rate_no_reparent(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_core *core = hw->core;
+ struct clk_core *parent = core->parent;
+ unsigned long best;
+ int ret;
+
+ if (core->flags & CLK_SET_RATE_PARENT) {
+ struct clk_rate_request parent_req;
+
+ if (!parent) {
+ req->rate = 0;
+ return 0;
+ }
+
+ clk_core_forward_rate_req(core, req, parent, &parent_req,
+ req->rate);
+
+ trace_clk_rate_request_start(&parent_req);
+
+ ret = clk_core_round_rate_nolock(parent, &parent_req);
+ if (ret)
+ return ret;
+
+ trace_clk_rate_request_done(&parent_req);
+
+ best = parent_req.rate;
+ } else if (parent) {
+ best = clk_core_get_rate_nolock(parent);
+ } else {
+ best = clk_core_get_rate_nolock(core);
+ }
+
+ req->best_parent_rate = best;
+ req->rate = best;
+
+ return 0;
+}
+
int clk_mux_determine_rate_flags(struct clk_hw *hw,
struct clk_rate_request *req,
unsigned long flags)
@@ -543,56 +721,49 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw,
struct clk_core *core = hw->core, *parent, *best_parent = NULL;
int i, num_parents, ret;
unsigned long best = 0;
- struct clk_rate_request parent_req = *req;
/* if NO_REPARENT flag set, pass through to current parent */
- if (core->flags & CLK_SET_RATE_NO_REPARENT) {
- parent = core->parent;
- if (core->flags & CLK_SET_RATE_PARENT) {
- ret = __clk_determine_rate(parent ? parent->hw : NULL,
- &parent_req);
- if (ret)
- return ret;
-
- best = parent_req.rate;
- } else if (parent) {
- best = clk_core_get_rate_nolock(parent);
- } else {
- best = clk_core_get_rate_nolock(core);
- }
-
- goto out;
- }
+ if (core->flags & CLK_SET_RATE_NO_REPARENT)
+ return clk_core_determine_rate_no_reparent(hw, req);
/* find the parent that can provide the fastest rate <= rate */
num_parents = core->num_parents;
for (i = 0; i < num_parents; i++) {
+ unsigned long parent_rate;
+
parent = clk_core_get_parent_by_index(core, i);
if (!parent)
continue;
if (core->flags & CLK_SET_RATE_PARENT) {
- parent_req = *req;
- ret = __clk_determine_rate(parent->hw, &parent_req);
+ struct clk_rate_request parent_req;
+
+ clk_core_forward_rate_req(core, req, parent, &parent_req, req->rate);
+
+ trace_clk_rate_request_start(&parent_req);
+
+ ret = clk_core_round_rate_nolock(parent, &parent_req);
if (ret)
continue;
+
+ trace_clk_rate_request_done(&parent_req);
+
+ parent_rate = parent_req.rate;
} else {
- parent_req.rate = clk_core_get_rate_nolock(parent);
+ parent_rate = clk_core_get_rate_nolock(parent);
}
- if (mux_is_better_rate(req->rate, parent_req.rate,
+ if (mux_is_better_rate(req->rate, parent_rate,
best, flags)) {
best_parent = parent;
- best = parent_req.rate;
+ best = parent_rate;
}
}
if (!best_parent)
return -EINVAL;
-out:
- if (best_parent)
- req->best_parent_hw = best_parent->hw;
+ req->best_parent_hw = best_parent->hw;
req->best_parent_rate = best;
req->rate = best;
@@ -625,6 +796,22 @@ static void clk_core_get_boundaries(struct clk_core *core,
*max_rate = min(*max_rate, clk_user->max_rate);
}
+/*
+ * clk_hw_get_rate_range() - returns the clock rate range for a hw clk
+ * @hw: the hw clk we want to get the range from
+ * @min_rate: pointer to the variable that will hold the minimum
+ * @max_rate: pointer to the variable that will hold the maximum
+ *
+ * Fills the @min_rate and @max_rate variables with the minimum and
+ * maximum that clock can reach.
+ */
+void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate,
+ unsigned long *max_rate)
+{
+ clk_core_get_boundaries(hw->core, min_rate, max_rate);
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_rate_range);
+
static bool clk_core_check_boundaries(struct clk_core *core,
unsigned long min_rate,
unsigned long max_rate)
@@ -676,6 +863,25 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
}
EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
+/*
+ * clk_hw_determine_rate_no_reparent - clk_ops::determine_rate implementation for a clk that doesn't reparent
+ * @hw: mux type clk to determine rate on
+ * @req: rate request, also used to return preferred frequency
+ *
+ * Helper for finding best parent rate to provide a given frequency.
+ * This can be used directly as a determine_rate callback (e.g. for a
+ * mux), or from a more complex clock that may combine a mux with other
+ * operations.
+ *
+ * Returns: 0 on success, -EERROR value on error
+ */
+int clk_hw_determine_rate_no_reparent(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ return clk_core_determine_rate_no_reparent(hw, req);
+}
+EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent);
+
/*** clk api ***/
static void clk_core_rate_unprotect(struct clk_core *core)
@@ -812,6 +1018,25 @@ int clk_rate_exclusive_get(struct clk *clk)
}
EXPORT_SYMBOL_GPL(clk_rate_exclusive_get);
+static void devm_clk_rate_exclusive_put(void *data)
+{
+ struct clk *clk = data;
+
+ clk_rate_exclusive_put(clk);
+}
+
+int devm_clk_rate_exclusive_get(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_rate_exclusive_get(clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, devm_clk_rate_exclusive_put, clk);
+}
+EXPORT_SYMBOL_GPL(devm_clk_rate_exclusive_get);
+
static void clk_core_unprepare(struct clk_core *core)
{
lockdep_assert_held(&prepare_lock);
@@ -970,12 +1195,12 @@ static void clk_core_disable(struct clk_core *core)
if (--core->enable_count > 0)
return;
- trace_clk_disable_rcuidle(core);
+ trace_clk_disable(core);
if (core->ops->disable)
core->ops->disable(core->hw);
- trace_clk_disable_complete_rcuidle(core);
+ trace_clk_disable_complete(core);
clk_core_disable(core->parent);
}
@@ -1029,12 +1254,12 @@ static int clk_core_enable(struct clk_core *core)
if (ret)
return ret;
- trace_clk_enable_rcuidle(core);
+ trace_clk_enable(core);
if (core->ops->enable)
ret = core->ops->enable(core->hw);
- trace_clk_enable_complete_rcuidle(core);
+ trace_clk_enable_complete(core);
if (ret) {
clk_core_disable(core->parent);
@@ -1232,9 +1457,6 @@ static void __init clk_unprepare_unused_subtree(struct clk_core *core)
if (core->flags & CLK_IGNORE_UNUSED)
return;
- if (clk_pm_runtime_get(core))
- return;
-
if (clk_core_is_prepared(core)) {
trace_clk_unprepare(core);
if (core->ops->unprepare_unused)
@@ -1243,8 +1465,6 @@ static void __init clk_unprepare_unused_subtree(struct clk_core *core)
core->ops->unprepare(core->hw);
trace_clk_unprepare_complete(core);
}
-
- clk_pm_runtime_put(core);
}
static void __init clk_disable_unused_subtree(struct clk_core *core)
@@ -1260,9 +1480,6 @@ static void __init clk_disable_unused_subtree(struct clk_core *core)
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_prepare_enable(core->parent);
- if (clk_pm_runtime_get(core))
- goto unprepare_out;
-
flags = clk_enable_lock();
if (core->enable_count)
@@ -1287,8 +1504,6 @@ static void __init clk_disable_unused_subtree(struct clk_core *core)
unlock_out:
clk_enable_unlock(flags);
- clk_pm_runtime_put(core);
-unprepare_out:
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_disable_unprepare(core->parent);
}
@@ -1304,12 +1519,22 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup);
static int __init clk_disable_unused(void)
{
struct clk_core *core;
+ int ret;
if (clk_ignore_unused) {
pr_warn("clk: Not disabling unused clocks\n");
return 0;
}
+ pr_info("clk: Disabling unused clocks\n");
+
+ ret = clk_pm_runtime_get_all();
+ if (ret)
+ return ret;
+ /*
+ * Grab the prepare lock to keep the clk topology stable while iterating
+ * over clks.
+ */
clk_prepare_lock();
hlist_for_each_entry(core, &clk_root_list, child_node)
@@ -1326,6 +1551,8 @@ static int __init clk_disable_unused(void)
clk_prepare_unlock();
+ clk_pm_runtime_put_all();
+
return 0;
}
late_initcall_sync(clk_disable_unused);
@@ -1340,7 +1567,19 @@ static int clk_core_determine_round_nolock(struct clk_core *core,
if (!core)
return 0;
- req->rate = clamp(req->rate, req->min_rate, req->max_rate);
+ /*
+ * Some clock providers hand-craft their clk_rate_requests and
+ * might not fill min_rate and max_rate.
+ *
+ * If it's the case, clamping the rate is equivalent to setting
+ * the rate to 0 which is bad. Skip the clamping but complain so
+ * that it gets fixed, hopefully.
+ */
+ if (!req->min_rate && !req->max_rate)
+ pr_warn("%s: %s: clk_rate_request has initialized min or max rate.\n",
+ __func__, core->name);
+ else
+ req->rate = clamp(req->rate, req->min_rate, req->max_rate);
/*
* At this point, core protection will be disabled
@@ -1367,13 +1606,24 @@ static int clk_core_determine_round_nolock(struct clk_core *core,
}
static void clk_core_init_rate_req(struct clk_core * const core,
- struct clk_rate_request *req)
+ struct clk_rate_request *req,
+ unsigned long rate)
{
struct clk_core *parent;
- if (WARN_ON(!core || !req))
+ if (WARN_ON(!req))
return;
+ memset(req, 0, sizeof(*req));
+ req->max_rate = ULONG_MAX;
+
+ if (!core)
+ return;
+
+ req->core = core;
+ req->rate = rate;
+ clk_core_get_boundaries(core, &req->min_rate, &req->max_rate);
+
parent = core->parent;
if (parent) {
req->best_parent_hw = parent->hw;
@@ -1384,6 +1634,52 @@ static void clk_core_init_rate_req(struct clk_core * const core,
}
}
+/**
+ * clk_hw_init_rate_request - Initializes a clk_rate_request
+ * @hw: the clk for which we want to submit a rate request
+ * @req: the clk_rate_request structure we want to initialise
+ * @rate: the rate which is to be requested
+ *
+ * Initializes a clk_rate_request structure to submit to
+ * __clk_determine_rate() or similar functions.
+ */
+void clk_hw_init_rate_request(const struct clk_hw *hw,
+ struct clk_rate_request *req,
+ unsigned long rate)
+{
+ if (WARN_ON(!hw || !req))
+ return;
+
+ clk_core_init_rate_req(hw->core, req, rate);
+}
+EXPORT_SYMBOL_GPL(clk_hw_init_rate_request);
+
+/**
+ * clk_hw_forward_rate_request - Forwards a clk_rate_request to a clock's parent
+ * @hw: the original clock that got the rate request
+ * @old_req: the original clk_rate_request structure we want to forward
+ * @parent: the clk we want to forward @old_req to
+ * @req: the clk_rate_request structure we want to initialise
+ * @parent_rate: The rate which is to be requested to @parent
+ *
+ * Initializes a clk_rate_request structure to submit to a clock parent
+ * in __clk_determine_rate() or similar functions.
+ */
+void clk_hw_forward_rate_request(const struct clk_hw *hw,
+ const struct clk_rate_request *old_req,
+ const struct clk_hw *parent,
+ struct clk_rate_request *req,
+ unsigned long parent_rate)
+{
+ if (WARN_ON(!hw || !old_req || !parent || !req))
+ return;
+
+ clk_core_forward_rate_req(hw->core, old_req,
+ parent->core, req,
+ parent_rate);
+}
+EXPORT_SYMBOL_GPL(clk_hw_forward_rate_request);
+
static bool clk_core_can_round(struct clk_core * const core)
{
return core->ops->determine_rate || core->ops->round_rate;
@@ -1392,6 +1688,8 @@ static bool clk_core_can_round(struct clk_core * const core)
static int clk_core_round_rate_nolock(struct clk_core *core,
struct clk_rate_request *req)
{
+ int ret;
+
lockdep_assert_held(&prepare_lock);
if (!core) {
@@ -1399,12 +1697,27 @@ static int clk_core_round_rate_nolock(struct clk_core *core,
return 0;
}
- clk_core_init_rate_req(core, req);
-
if (clk_core_can_round(core))
return clk_core_determine_round_nolock(core, req);
- else if (core->flags & CLK_SET_RATE_PARENT)
- return clk_core_round_rate_nolock(core->parent, req);
+
+ if (core->flags & CLK_SET_RATE_PARENT) {
+ struct clk_rate_request parent_req;
+
+ clk_core_forward_rate_req(core, req, core->parent, &parent_req, req->rate);
+
+ trace_clk_rate_request_start(&parent_req);
+
+ ret = clk_core_round_rate_nolock(core->parent, &parent_req);
+ if (ret)
+ return ret;
+
+ trace_clk_rate_request_done(&parent_req);
+
+ req->best_parent_rate = parent_req.rate;
+ req->rate = parent_req.rate;
+
+ return 0;
+ }
req->rate = core->rate;
return 0;
@@ -1448,13 +1761,16 @@ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate)
int ret;
struct clk_rate_request req;
- clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
- req.rate = rate;
+ clk_core_init_rate_req(hw->core, &req, rate);
+
+ trace_clk_rate_request_start(&req);
ret = clk_core_round_rate_nolock(hw->core, &req);
if (ret)
return 0;
+ trace_clk_rate_request_done(&req);
+
return req.rate;
}
EXPORT_SYMBOL_GPL(clk_hw_round_rate);
@@ -1481,11 +1797,14 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
if (clk->exclusive_count)
clk_core_rate_unprotect(clk->core);
- clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
- req.rate = rate;
+ clk_core_init_rate_req(clk->core, &req, rate);
+
+ trace_clk_rate_request_start(&req);
ret = clk_core_round_rate_nolock(clk->core, &req);
+ trace_clk_rate_request_done(&req);
+
if (clk->exclusive_count)
clk_core_rate_protect(clk->core);
@@ -1611,6 +1930,7 @@ static unsigned long clk_recalc(struct clk_core *core,
/**
* __clk_recalc_rates
* @core: first clk in the subtree
+ * @update_req: Whether req_rate should be updated with the new rate
* @msg: notification type (see include/linux/clk.h)
*
* Walks the subtree of clks starting with clk and recalculates rates as it
@@ -1620,7 +1940,8 @@ static unsigned long clk_recalc(struct clk_core *core,
* clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
* if necessary.
*/
-static void __clk_recalc_rates(struct clk_core *core, unsigned long msg)
+static void __clk_recalc_rates(struct clk_core *core, bool update_req,
+ unsigned long msg)
{
unsigned long old_rate;
unsigned long parent_rate = 0;
@@ -1634,6 +1955,8 @@ static void __clk_recalc_rates(struct clk_core *core, unsigned long msg)
parent_rate = core->parent->rate;
core->rate = clk_recalc(core, parent_rate);
+ if (update_req)
+ core->req_rate = core->rate;
/*
* ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE
@@ -1643,13 +1966,13 @@ static void __clk_recalc_rates(struct clk_core *core, unsigned long msg)
__clk_notify(core, msg, old_rate, core->rate);
hlist_for_each_entry(child, &core->children, child_node)
- __clk_recalc_rates(child, msg);
+ __clk_recalc_rates(child, update_req, msg);
}
static unsigned long clk_core_get_rate_recalc(struct clk_core *core)
{
if (core && (core->flags & CLK_GET_RATE_NOCACHE))
- __clk_recalc_rates(core, 0);
+ __clk_recalc_rates(core, false, 0);
return clk_core_get_rate_nolock(core);
}
@@ -1659,8 +1982,9 @@ static unsigned long clk_core_get_rate_recalc(struct clk_core *core)
* @clk: the clk whose rate is being returned
*
* Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag
- * is set, which means a recalc_rate will be issued.
- * If clk is NULL then returns 0.
+ * is set, which means a recalc_rate will be issued. Can be called regardless of
+ * the clock enabledness. If clk is NULL, or if an error occurred, then returns
+ * 0.
*/
unsigned long clk_get_rate(struct clk *clk)
{
@@ -1864,6 +2188,7 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent,
flags = clk_enable_lock();
clk_reparent(core, old_parent);
clk_enable_unlock(flags);
+
__clk_set_parent_after(core, old_parent, parent);
return ret;
@@ -1952,7 +2277,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
unsigned long min_rate;
unsigned long max_rate;
int p_index = 0;
- long ret;
+ int ret;
/* sanity */
if (IS_ERR_OR_NULL(core))
@@ -1969,16 +2294,16 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
if (clk_core_can_round(core)) {
struct clk_rate_request req;
- req.rate = rate;
- req.min_rate = min_rate;
- req.max_rate = max_rate;
+ clk_core_init_rate_req(core, &req, rate);
- clk_core_init_rate_req(core, &req);
+ trace_clk_rate_request_start(&req);
ret = clk_core_determine_round_nolock(core, &req);
if (ret < 0)
return NULL;
+ trace_clk_rate_request_done(&req);
+
best_parent_rate = req.best_parent_rate;
new_rate = req.rate;
parent = req.best_parent_hw ? req.best_parent_hw->core : NULL;
@@ -2172,11 +2497,14 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
if (cnt < 0)
return cnt;
- clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
- req.rate = req_rate;
+ clk_core_init_rate_req(core, &req, req_rate);
+
+ trace_clk_rate_request_start(&req);
ret = clk_core_round_rate_nolock(core, &req);
+ trace_clk_rate_request_done(&req);
+
/* restore the protection */
clk_core_rate_restore_protect(core, cnt);
@@ -2188,7 +2516,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
{
struct clk_core *top, *fail_clk;
unsigned long rate;
- int ret = 0;
+ int ret;
if (!core)
return 0;
@@ -2324,19 +2652,15 @@ int clk_set_rate_exclusive(struct clk *clk, unsigned long rate)
}
EXPORT_SYMBOL_GPL(clk_set_rate_exclusive);
-/**
- * clk_set_rate_range - set a rate range for a clock source
- * @clk: clock source
- * @min: desired minimum clock rate in Hz, inclusive
- * @max: desired maximum clock rate in Hz, inclusive
- *
- * Returns success (0) or negative errno.
- */
-int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
+static int clk_set_rate_range_nolock(struct clk *clk,
+ unsigned long min,
+ unsigned long max)
{
int ret = 0;
unsigned long old_min, old_max, rate;
+ lockdep_assert_held(&prepare_lock);
+
if (!clk)
return 0;
@@ -2349,8 +2673,6 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
return -EINVAL;
}
- clk_prepare_lock();
-
if (clk->exclusive_count)
clk_core_rate_unprotect(clk->core);
@@ -2365,6 +2687,10 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
goto out;
}
+ rate = clk->core->req_rate;
+ if (clk->core->flags & CLK_GET_RATE_NOCACHE)
+ rate = clk_core_get_rate_recalc(clk->core);
+
/*
* Since the boundaries have been changed, let's give the
* opportunity to the provider to adjust the clock rate based on
@@ -2382,7 +2708,7 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
* - the determine_rate() callback does not really check for
* this corner case when determining the rate
*/
- rate = clamp(clk->core->req_rate, min, max);
+ rate = clamp(rate, min, max);
ret = clk_core_set_rate_nolock(clk->core, rate);
if (ret) {
/* rollback the changes */
@@ -2394,6 +2720,28 @@ out:
if (clk->exclusive_count)
clk_core_rate_protect(clk->core);
+ return ret;
+}
+
+/**
+ * clk_set_rate_range - set a rate range for a clock source
+ * @clk: clock source
+ * @min: desired minimum clock rate in Hz, inclusive
+ * @max: desired maximum clock rate in Hz, inclusive
+ *
+ * Return: 0 for success or negative errno on failure.
+ */
+int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
+{
+ int ret;
+
+ if (!clk)
+ return 0;
+
+ clk_prepare_lock();
+
+ ret = clk_set_rate_range_nolock(clk, min, max);
+
clk_prepare_unlock();
return ret;
@@ -2473,7 +2821,7 @@ static void clk_core_reparent(struct clk_core *core,
{
clk_reparent(core, new_parent);
__clk_recalc_accuracies(core);
- __clk_recalc_rates(core, POST_RATE_CHANGE);
+ __clk_recalc_rates(core, true, POST_RATE_CHANGE);
}
void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent)
@@ -2494,27 +2842,13 @@ void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent)
*
* Returns true if @parent is a possible parent for @clk, false otherwise.
*/
-bool clk_has_parent(struct clk *clk, struct clk *parent)
+bool clk_has_parent(const struct clk *clk, const struct clk *parent)
{
- struct clk_core *core, *parent_core;
- int i;
-
/* NULL clocks should be nops, so return success if either is NULL. */
if (!clk || !parent)
return true;
- core = clk->core;
- parent_core = parent->core;
-
- /* Optimize for the case where the parent is already the parent. */
- if (core->parent == parent_core)
- return true;
-
- for (i = 0; i < core->num_parents; i++)
- if (!strcmp(core->parents[i].name, parent_core->name))
- return true;
-
- return false;
+ return clk_core_has_parent(clk->core, parent->core);
}
EXPORT_SYMBOL_GPL(clk_has_parent);
@@ -2571,9 +2905,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
/* propagate rate an accuracy recalculation accordingly */
if (ret) {
- __clk_recalc_rates(core, ABORT_RATE_CHANGE);
+ __clk_recalc_rates(core, true, ABORT_RATE_CHANGE);
} else {
- __clk_recalc_rates(core, POST_RATE_CHANGE);
+ __clk_recalc_rates(core, true, POST_RATE_CHANGE);
__clk_recalc_accuracies(core);
}
@@ -2952,28 +3286,41 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
int level)
{
int phase;
+ struct clk *clk_user;
+ int multi_node = 0;
- seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu ",
+ seq_printf(s, "%*s%-*s %-7d %-8d %-8d %-11lu %-10lu ",
level * 3 + 1, "",
- 30 - level * 3, c->name,
+ 35 - level * 3, c->name,
c->enable_count, c->prepare_count, c->protect_count,
clk_core_get_rate_recalc(c),
clk_core_get_accuracy_recalc(c));
phase = clk_core_get_phase(c);
if (phase >= 0)
- seq_printf(s, "%5d", phase);
+ seq_printf(s, "%-5d", phase);
else
seq_puts(s, "-----");
- seq_printf(s, " %6d", clk_core_get_scaled_duty_cycle(c, 100000));
+ seq_printf(s, " %-6d", clk_core_get_scaled_duty_cycle(c, 100000));
if (c->ops->is_enabled)
- seq_printf(s, " %9c\n", clk_core_is_enabled(c) ? 'Y' : 'N');
+ seq_printf(s, " %5c ", clk_core_is_enabled(c) ? 'Y' : 'N');
else if (!c->ops->enable)
- seq_printf(s, " %9c\n", 'Y');
+ seq_printf(s, " %5c ", 'Y');
else
- seq_printf(s, " %9c\n", '?');
+ seq_printf(s, " %5c ", '?');
+
+ hlist_for_each_entry(clk_user, &c->clks, clks_node) {
+ seq_printf(s, "%*s%-*s %-25s\n",
+ level * 3 + 2 + 105 * multi_node, "",
+ 30,
+ clk_user->dev_id ? clk_user->dev_id : "deviceless",
+ clk_user->con_id ? clk_user->con_id : "no_connection_id");
+
+ multi_node = 1;
+ }
+
}
static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
@@ -2981,9 +3328,7 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
{
struct clk_core *child;
- clk_pm_runtime_get(c);
clk_summary_show_one(s, c, level);
- clk_pm_runtime_put(c);
hlist_for_each_entry(child, &c->children, child_node)
clk_summary_show_subtree(s, child, level + 1);
@@ -2992,11 +3337,16 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
static int clk_summary_show(struct seq_file *s, void *data)
{
struct clk_core *c;
- struct hlist_head **lists = (struct hlist_head **)s->private;
+ struct hlist_head **lists = s->private;
+ int ret;
- seq_puts(s, " enable prepare protect duty hardware\n");
- seq_puts(s, " clock count count count rate accuracy phase cycle enable\n");
- seq_puts(s, "-------------------------------------------------------------------------------------------------------\n");
+ seq_puts(s, " enable prepare protect duty hardware connection\n");
+ seq_puts(s, " clock count count count rate accuracy phase cycle enable consumer id\n");
+ seq_puts(s, "---------------------------------------------------------------------------------------------------------------------------------------------\n");
+
+ ret = clk_pm_runtime_get_all();
+ if (ret)
+ return ret;
clk_prepare_lock();
@@ -3005,6 +3355,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
clk_summary_show_subtree(s, c, 0);
clk_prepare_unlock();
+ clk_pm_runtime_put_all();
return 0;
}
@@ -3051,9 +3402,15 @@ static int clk_dump_show(struct seq_file *s, void *data)
{
struct clk_core *c;
bool first_node = true;
- struct hlist_head **lists = (struct hlist_head **)s->private;
+ struct hlist_head **lists = s->private;
+ int ret;
+
+ ret = clk_pm_runtime_get_all();
+ if (ret)
+ return ret;
seq_putc(s, '{');
+
clk_prepare_lock();
for (; *lists; lists++) {
@@ -3066,6 +3423,7 @@ static int clk_dump_show(struct seq_file *s, void *data)
}
clk_prepare_unlock();
+ clk_pm_runtime_put_all();
seq_puts(s, "}\n");
return 0;
@@ -3093,6 +3451,21 @@ static int clk_rate_set(void *data, u64 val)
#define clk_rate_mode 0644
+static int clk_phase_set(void *data, u64 val)
+{
+ struct clk_core *core = data;
+ int degrees = do_div(val, 360);
+ int ret;
+
+ clk_prepare_lock();
+ ret = clk_core_set_phase_nolock(core, degrees);
+ clk_prepare_unlock();
+
+ return ret;
+}
+
+#define clk_phase_mode 0644
+
static int clk_prepare_enable_set(void *data, u64 val)
{
struct clk_core *core = data;
@@ -3120,6 +3493,9 @@ DEFINE_DEBUGFS_ATTRIBUTE(clk_prepare_enable_fops, clk_prepare_enable_get,
#else
#define clk_rate_set NULL
#define clk_rate_mode 0444
+
+#define clk_phase_set NULL
+#define clk_phase_mode 0644
#endif
static int clk_rate_get(void *data, u64 *val)
@@ -3135,6 +3511,16 @@ static int clk_rate_get(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(clk_rate_fops, clk_rate_get, clk_rate_set, "%llu\n");
+static int clk_phase_get(void *data, u64 *val)
+{
+ struct clk_core *core = data;
+
+ *val = core->phase;
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(clk_phase_fops, clk_phase_get, clk_phase_set, "%llu\n");
+
static const struct {
unsigned long flag;
const char *name;
@@ -3180,6 +3566,7 @@ static void possible_parent_show(struct seq_file *s, struct clk_core *core,
unsigned int i, char terminator)
{
struct clk_core *parent;
+ const char *name = NULL;
/*
* Go through the following options to fetch a parent's name.
@@ -3194,18 +3581,20 @@ static void possible_parent_show(struct seq_file *s, struct clk_core *core,
* registered (yet).
*/
parent = clk_core_get_parent_by_index(core, i);
- if (parent)
+ if (parent) {
seq_puts(s, parent->name);
- else if (core->parents[i].name)
+ } else if (core->parents[i].name) {
seq_puts(s, core->parents[i].name);
- else if (core->parents[i].fw_name)
+ } else if (core->parents[i].fw_name) {
seq_printf(s, "<%s>(fw)", core->parents[i].fw_name);
- else if (core->parents[i].index >= 0)
- seq_puts(s,
- of_clk_get_parent_name(core->of_node,
- core->parents[i].index));
- else
- seq_puts(s, "(missing)");
+ } else {
+ if (core->parents[i].index >= 0)
+ name = of_clk_get_parent_name(core->of_node, core->parents[i].index);
+ if (!name)
+ name = "(missing)";
+
+ seq_puts(s, name);
+ }
seq_putc(s, terminator);
}
@@ -3325,7 +3714,8 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
debugfs_create_file("clk_min_rate", 0444, root, core, &clk_min_rate_fops);
debugfs_create_file("clk_max_rate", 0444, root, core, &clk_max_rate_fops);
debugfs_create_ulong("clk_accuracy", 0444, root, &core->accuracy);
- debugfs_create_u32("clk_phase", 0444, root, &core->phase);
+ debugfs_create_file("clk_phase", clk_phase_mode, root, core,
+ &clk_phase_fops);
debugfs_create_file("clk_flags", 0444, root, core, &clk_flags_fops);
debugfs_create_u32("clk_prepare_count", 0444, root, &core->prepare_count);
debugfs_create_u32("clk_enable_count", 0444, root, &core->enable_count);
@@ -3461,7 +3851,7 @@ static void clk_core_reparent_orphans_nolock(void)
/*
* We need to use __clk_set_parent_before() and _after() to
- * to properly migrate any prepare/enable count of the orphan
+ * properly migrate any prepare/enable count of the orphan
* clock. This is important for CLK_IS_CRITICAL clocks, which
* are enabled during init but might not have a parent yet.
*/
@@ -3470,7 +3860,7 @@ static void clk_core_reparent_orphans_nolock(void)
__clk_set_parent_before(orphan, parent);
__clk_set_parent_after(orphan, parent, NULL);
__clk_recalc_accuracies(orphan);
- __clk_recalc_rates(orphan, 0);
+ __clk_recalc_rates(orphan, true, 0);
/*
* __clk_init_parent() will set the initial req_rate to
@@ -3541,6 +3931,13 @@ static int __clk_core_init(struct clk_core *core)
goto out;
}
+ if (core->ops->set_parent && !core->ops->determine_rate) {
+ pr_err("%s: %s must implement .set_parent & .determine_rate\n",
+ __func__, core->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
if (core->num_parents > 1 && !core->ops->get_parent) {
pr_err("%s: %s must implement .get_parent as it has multi parents\n",
__func__, core->name);
@@ -3598,6 +3995,8 @@ static int __clk_core_init(struct clk_core *core)
hlist_add_head(&core->child_node, &clk_orphan_list);
core->orphan = true;
}
+ hash_add(clk_hashtable, &core->hashtable_node,
+ full_name_hash(NULL, core->name, strlen(core->name)));
/*
* Set clk's accuracy. The preferred method is to use
@@ -3670,13 +4069,11 @@ static int __clk_core_init(struct clk_core *core)
}
clk_core_reparent_orphans_nolock();
-
-
- kref_init(&core->ref);
out:
clk_pm_runtime_put(core);
unlock:
if (ret) {
+ hash_del(&core->hashtable_node);
hlist_del_init(&core->child_node);
core->hw->core = NULL;
}
@@ -3901,6 +4298,22 @@ static void clk_core_free_parent_map(struct clk_core *core)
kfree(core->parents);
}
+/* Free memory allocated for a struct clk_core */
+static void __clk_release(struct kref *ref)
+{
+ struct clk_core *core = container_of(ref, struct clk_core, ref);
+
+ if (core->rpm_enabled) {
+ mutex_lock(&clk_rpm_list_lock);
+ hlist_del(&core->rpm_node);
+ mutex_unlock(&clk_rpm_list_lock);
+ }
+
+ clk_core_free_parent_map(core);
+ kfree_const(core->name);
+ kfree(core);
+}
+
static struct clk *
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
{
@@ -3921,6 +4334,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
goto fail_out;
}
+ kref_init(&core->ref);
+
core->name = kstrdup_const(init->name, GFP_KERNEL);
if (!core->name) {
ret = -ENOMEM;
@@ -3933,9 +4348,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
}
core->ops = init->ops;
- if (dev && pm_runtime_enabled(dev))
- core->rpm_enabled = true;
core->dev = dev;
+ clk_pm_runtime_init(core);
core->of_node = np;
if (dev && dev->driver)
core->owner = dev->driver->owner;
@@ -3975,13 +4389,18 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
hw->clk = NULL;
fail_create_clk:
- clk_core_free_parent_map(core);
fail_parents:
fail_ops:
- kfree_const(core->name);
fail_name:
- kfree(core);
+ kref_put(&core->ref, __clk_release);
fail_out:
+ if (dev) {
+ dev_err_probe(dev, ret, "failed to register clk '%s' (%pS)\n",
+ init->name, hw);
+ } else {
+ pr_err("%pOF: error %pe: failed to register clk '%s' (%pS)\n",
+ np, ERR_PTR(ret), init->name, hw);
+ }
return ERR_PTR(ret);
}
@@ -4060,18 +4479,6 @@ int of_clk_hw_register(struct device_node *node, struct clk_hw *hw)
}
EXPORT_SYMBOL_GPL(of_clk_hw_register);
-/* Free memory allocated for a clock. */
-static void __clk_release(struct kref *ref)
-{
- struct clk_core *core = container_of(ref, struct clk_core, ref);
-
- lockdep_assert_held(&prepare_lock);
-
- clk_core_free_parent_map(core);
- kfree_const(core->name);
- kfree(core);
-}
-
/*
* Empty clk_ops for unregistered clocks. These are used temporarily
* after clk_unregister() was called on a clock and until last clock
@@ -4098,11 +4505,18 @@ static int clk_nodrv_set_parent(struct clk_hw *hw, u8 index)
return -ENXIO;
}
+static int clk_nodrv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ return -ENXIO;
+}
+
static const struct clk_ops clk_nodrv_ops = {
.enable = clk_nodrv_prepare_enable,
.disable = clk_nodrv_disable_unprepare,
.prepare = clk_nodrv_prepare_enable,
.unprepare = clk_nodrv_disable_unprepare,
+ .determine_rate = clk_nodrv_determine_rate,
.set_rate = clk_nodrv_set_rate,
.set_parent = clk_nodrv_set_parent,
};
@@ -4155,7 +4569,8 @@ void clk_unregister(struct clk *clk)
if (ops == &clk_nodrv_ops) {
pr_err("%s: unregistered clock: %s\n", __func__,
clk->core->name);
- goto unlock;
+ clk_prepare_unlock();
+ return;
}
/*
* Assign empty clock ops for consumers that might still hold
@@ -4180,6 +4595,7 @@ void clk_unregister(struct clk *clk)
clk_core_evict_parent_cache(clk->core);
+ hash_del(&clk->core->hashtable_node);
hlist_del_init(&clk->core->child_node);
if (clk->core->prepare_count)
@@ -4189,11 +4605,10 @@ void clk_unregister(struct clk *clk)
if (clk->core->protect_count)
pr_warn("%s: unregistering protected clock: %s\n",
__func__, clk->core->name);
+ clk_prepare_unlock();
kref_put(&clk->core->ref, __clk_release);
free_clk(clk);
-unlock:
- clk_prepare_unlock();
}
EXPORT_SYMBOL_GPL(clk_unregister);
@@ -4346,18 +4761,17 @@ void __clk_put(struct clk *clk)
clk->exclusive_count = 0;
}
- hlist_del(&clk->clks_node);
- if (clk->min_rate > clk->core->req_rate ||
- clk->max_rate < clk->core->req_rate)
- clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
+ clk_core_unlink_consumer(clk);
- owner = clk->core->owner;
- kref_put(&clk->core->ref, __clk_release);
+ /* If we had any boundaries on that clock, let's drop them. */
+ if (clk->min_rate > 0 || clk->max_rate < ULONG_MAX)
+ clk_set_rate_range_nolock(clk, 0, ULONG_MAX);
clk_prepare_unlock();
+ owner = clk->core->owner;
+ kref_put(&clk->core->ref, __clk_release);
module_put(owner);
-
free_clk(clk);
}
@@ -4491,6 +4905,7 @@ int devm_clk_notifier_register(struct device *dev, struct clk *clk,
if (!ret) {
devres->clk = clk;
devres->nb = nb;
+ devres_add(dev, devres);
} else {
devres_free(devres);
}
@@ -4678,8 +5093,8 @@ static struct device_node *get_clk_provider_node(struct device *dev)
np = dev->of_node;
parent_np = dev->parent ? dev->parent->of_node : NULL;
- if (!of_find_property(np, "#clock-cells", NULL))
- if (of_find_property(parent_np, "#clock-cells", NULL))
+ if (!of_property_present(np, "#clock-cells"))
+ if (of_property_present(parent_np, "#clock-cells"))
np = parent_np;
return np;
@@ -4750,32 +5165,6 @@ void of_clk_del_provider(struct device_node *np)
}
EXPORT_SYMBOL_GPL(of_clk_del_provider);
-static int devm_clk_provider_match(struct device *dev, void *res, void *data)
-{
- struct device_node **np = res;
-
- if (WARN_ON(!np || !*np))
- return 0;
-
- return *np == data;
-}
-
-/**
- * devm_of_clk_del_provider() - Remove clock provider registered using devm
- * @dev: Device to whose lifetime the clock provider was bound
- */
-void devm_of_clk_del_provider(struct device *dev)
-{
- int ret;
- struct device_node *np = get_clk_provider_node(dev);
-
- ret = devres_release(dev, devm_of_clk_release_provider,
- devm_clk_provider_match, np);
-
- WARN_ON(ret);
-}
-EXPORT_SYMBOL(devm_of_clk_del_provider);
-
/**
* of_parse_clkspec() - Parse a DT clock specifier for a given device node
* @np: device node to parse clock specifier from
@@ -4842,7 +5231,7 @@ static int of_parse_clkspec(const struct device_node *np, int index,
* clocks.
*/
np = np->parent;
- if (np && !of_get_property(np, "clock-ranges", NULL))
+ if (np && !of_property_present(np, "clock-ranges"))
break;
index = 0;
}
@@ -4874,6 +5263,10 @@ of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec)
if (!clkspec)
return ERR_PTR(-EINVAL);
+ /* Check if node in clkspec is in disabled/fail state */
+ if (!of_device_is_available(clkspec->np))
+ return ERR_PTR(-ENOENT);
+
mutex_lock(&of_clk_mutex);
list_for_each_entry(provider, &of_clk_providers, link) {
if (provider->node == clkspec->np) {
@@ -4974,9 +5367,8 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count);
const char *of_clk_get_parent_name(const struct device_node *np, int index)
{
struct of_phandle_args clkspec;
- struct property *prop;
const char *clk_name;
- const __be32 *vp;
+ bool found = false;
u32 pv;
int rc;
int count;
@@ -4993,16 +5385,19 @@ const char *of_clk_get_parent_name(const struct device_node *np, int index)
/* if there is an indices property, use it to transfer the index
* specified into an array offset for the clock-output-names property.
*/
- of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) {
+ of_property_for_each_u32(clkspec.np, "clock-indices", pv) {
if (index == pv) {
index = count;
+ found = true;
break;
}
count++;
}
/* We went off the end of 'clock-indices' without finding it */
- if (prop && !vp)
+ if (of_property_present(clkspec.np, "clock-indices") && !found) {
+ of_node_put(clkspec.np);
return NULL;
+ }
if (of_property_read_string_index(clkspec.np, "clock-output-names",
index,
@@ -5114,14 +5509,12 @@ static int parent_ready(struct device_node *np)
int of_clk_detect_critical(struct device_node *np, int index,
unsigned long *flags)
{
- struct property *prop;
- const __be32 *cur;
uint32_t idx;
if (!np || !flags)
return -EINVAL;
- of_property_for_each_u32(np, "clock-critical", prop, cur, idx)
+ of_property_for_each_u32(np, "clock-critical", idx)
if (index == idx)
*flags |= CLK_IS_CRITICAL;
diff --git a/drivers/clk/clk_kunit_helpers.c b/drivers/clk/clk_kunit_helpers.c
new file mode 100644
index 000000000000..68a28e70bb61
--- /dev/null
+++ b/drivers/clk/clk_kunit_helpers.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for clk providers and consumers
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <kunit/clk.h>
+#include <kunit/resource.h>
+
+KUNIT_DEFINE_ACTION_WRAPPER(clk_disable_unprepare_wrapper,
+ clk_disable_unprepare, struct clk *);
+/**
+ * clk_prepare_enable_kunit() - Test managed clk_prepare_enable()
+ * @test: The test context
+ * @clk: clk to prepare and enable
+ *
+ * Return: 0 on success, or negative errno on failure.
+ */
+int clk_prepare_enable_kunit(struct kunit *test, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, clk_disable_unprepare_wrapper,
+ clk);
+}
+EXPORT_SYMBOL_GPL(clk_prepare_enable_kunit);
+
+KUNIT_DEFINE_ACTION_WRAPPER(clk_put_wrapper, clk_put, struct clk *);
+
+static struct clk *__clk_get_kunit(struct kunit *test, struct clk *clk)
+{
+ int ret;
+
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = kunit_add_action_or_reset(test, clk_put_wrapper, clk);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return clk;
+}
+
+/**
+ * clk_get_kunit() - Test managed clk_get()
+ * @test: The test context
+ * @dev: device for clock "consumer"
+ * @con_id: clock consumer ID
+ *
+ * Just like clk_get(), except the clk is managed by the test case and is
+ * automatically put with clk_put() after the test case concludes.
+ *
+ * Return: new clk consumer or ERR_PTR on failure.
+ */
+struct clk *
+clk_get_kunit(struct kunit *test, struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+
+ return __clk_get_kunit(test, clk);
+}
+EXPORT_SYMBOL_GPL(clk_get_kunit);
+
+/**
+ * of_clk_get_kunit() - Test managed of_clk_get()
+ * @test: The test context
+ * @np: device_node for clock "consumer"
+ * @index: index in 'clocks' property of @np
+ *
+ * Just like of_clk_get(), except the clk is managed by the test case and is
+ * automatically put with clk_put() after the test case concludes.
+ *
+ * Return: new clk consumer or ERR_PTR on failure.
+ */
+struct clk *
+of_clk_get_kunit(struct kunit *test, struct device_node *np, int index)
+{
+ struct clk *clk;
+
+ clk = of_clk_get(np, index);
+
+ return __clk_get_kunit(test, clk);
+}
+EXPORT_SYMBOL_GPL(of_clk_get_kunit);
+
+/**
+ * clk_hw_get_clk_kunit() - Test managed clk_hw_get_clk()
+ * @test: The test context
+ * @hw: clk_hw associated with the clk being consumed
+ * @con_id: connection ID string on device
+ *
+ * Just like clk_hw_get_clk(), except the clk is managed by the test case and
+ * is automatically put with clk_put() after the test case concludes.
+ *
+ * Return: new clk consumer or ERR_PTR on failure.
+ */
+struct clk *
+clk_hw_get_clk_kunit(struct kunit *test, struct clk_hw *hw, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_hw_get_clk(hw, con_id);
+
+ return __clk_get_kunit(test, clk);
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_clk_kunit);
+
+/**
+ * clk_hw_get_clk_prepared_enabled_kunit() - Test managed clk_hw_get_clk() + clk_prepare_enable()
+ * @test: The test context
+ * @hw: clk_hw associated with the clk being consumed
+ * @con_id: connection ID string on device
+ *
+ * Just like
+ *
+ * .. code-block:: c
+ *
+ * struct clk *clk = clk_hw_get_clk(...);
+ * clk_prepare_enable(clk);
+ *
+ * except the clk is managed by the test case and is automatically disabled and
+ * unprepared with clk_disable_unprepare() and put with clk_put() after the
+ * test case concludes.
+ *
+ * Return: new clk consumer that is prepared and enabled or ERR_PTR on failure.
+ */
+struct clk *
+clk_hw_get_clk_prepared_enabled_kunit(struct kunit *test, struct clk_hw *hw,
+ const char *con_id)
+{
+ int ret;
+ struct clk *clk;
+
+ clk = clk_hw_get_clk_kunit(test, hw, con_id);
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = clk_prepare_enable_kunit(test, clk);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_clk_prepared_enabled_kunit);
+
+KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_wrapper,
+ clk_hw_unregister, struct clk_hw *);
+
+/**
+ * clk_hw_register_kunit() - Test managed clk_hw_register()
+ * @test: The test context
+ * @dev: device that is registering this clock
+ * @hw: link to hardware-specific clock data
+ *
+ * Just like clk_hw_register(), except the clk registration is managed by the
+ * test case and is automatically unregistered after the test case concludes.
+ *
+ * Return: 0 on success or a negative errno value on failure.
+ */
+int clk_hw_register_kunit(struct kunit *test, struct device *dev, struct clk_hw *hw)
+{
+ int ret;
+
+ ret = clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, clk_hw_unregister_wrapper, hw);
+}
+EXPORT_SYMBOL_GPL(clk_hw_register_kunit);
+
+/**
+ * of_clk_hw_register_kunit() - Test managed of_clk_hw_register()
+ * @test: The test context
+ * @node: device_node of device that is registering this clock
+ * @hw: link to hardware-specific clock data
+ *
+ * Just like of_clk_hw_register(), except the clk registration is managed by
+ * the test case and is automatically unregistered after the test case
+ * concludes.
+ *
+ * Return: 0 on success or a negative errno value on failure.
+ */
+int of_clk_hw_register_kunit(struct kunit *test, struct device_node *node, struct clk_hw *hw)
+{
+ int ret;
+
+ ret = of_clk_hw_register(node, hw);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, clk_hw_unregister_wrapper, hw);
+}
+EXPORT_SYMBOL_GPL(of_clk_hw_register_kunit);
+
+KUNIT_DEFINE_ACTION_WRAPPER(of_clk_del_provider_wrapper,
+ of_clk_del_provider, struct device_node *);
+
+/**
+ * of_clk_add_hw_provider_kunit() - Test managed of_clk_add_hw_provider()
+ * @test: The test context
+ * @np: Device node pointer associated with clock provider
+ * @get: Callback for decoding clk_hw
+ * @data: Context pointer for @get callback.
+ *
+ * Just like of_clk_add_hw_provider(), except the clk_hw provider is managed by
+ * the test case and is automatically unregistered after the test case
+ * concludes.
+ *
+ * Return: 0 on success or a negative errno value on failure.
+ */
+int of_clk_add_hw_provider_kunit(struct kunit *test, struct device_node *np,
+ struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data),
+ void *data)
+{
+ int ret;
+
+ ret = of_clk_add_hw_provider(np, get, data);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, of_clk_del_provider_wrapper, np);
+}
+EXPORT_SYMBOL_GPL(of_clk_add_hw_provider_kunit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit helpers for clk providers and consumers");
diff --git a/drivers/clk/clk_parent_data_test.h b/drivers/clk/clk_parent_data_test.h
new file mode 100644
index 000000000000..eedd53ae910d
--- /dev/null
+++ b/drivers/clk/clk_parent_data_test.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CLK_PARENT_DATA_TEST_H
+#define _CLK_PARENT_DATA_TEST_H
+
+#define CLK_PARENT_DATA_1MHZ_NAME "1mhz_fixed_legacy"
+#define CLK_PARENT_DATA_PARENT1 "parent_fwname"
+#define CLK_PARENT_DATA_PARENT2 "50"
+#define CLK_PARENT_DATA_50MHZ_NAME "50_clk"
+
+#endif
diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index 6731a822f4e3..a268d7b5d4cb 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -1,15 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Kunit test for clk rate management
+ * Kunit tests for clk framework
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/clk/clk-conf.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
/* Needed for clk_hw_get_clk() */
#include "clk.h"
+#include <kunit/clk.h>
+#include <kunit/of.h>
+#include <kunit/platform_device.h>
#include <kunit/test.h>
+#include "kunit_clk_assigned_rates.h"
+#include "clk_parent_data_test.h"
+
+static const struct clk_ops empty_clk_ops = { };
+
#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000)
#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000)
#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000)
@@ -104,10 +115,66 @@ static const struct clk_ops clk_dummy_minimize_rate_ops = {
};
static const struct clk_ops clk_dummy_single_parent_ops = {
+ /*
+ * FIXME: Even though we should probably be able to use
+ * __clk_mux_determine_rate() here, if we use it and call
+ * clk_round_rate() or clk_set_rate() with a rate lower than
+ * what all the parents can provide, it will return -EINVAL.
+ *
+ * This is due to the fact that it has the undocumented
+ * behaviour to always pick up the closest rate higher than the
+ * requested rate. If we get something lower, it thus considers
+ * that it's not acceptable and will return an error.
+ *
+ * It's somewhat inconsistent and creates a weird threshold
+ * between rates above the parent rate which would be rounded to
+ * what the parent can provide, but rates below will simply
+ * return an error.
+ */
+ .determine_rate = __clk_mux_determine_rate_closest,
.set_parent = clk_dummy_single_set_parent,
.get_parent = clk_dummy_single_get_parent,
};
+struct clk_multiple_parent_ctx {
+ struct clk_dummy_context parents_ctx[2];
+ struct clk_hw hw;
+ u8 current_parent;
+};
+
+static int clk_multiple_parents_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_multiple_parent_ctx *ctx =
+ container_of(hw, struct clk_multiple_parent_ctx, hw);
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ ctx->current_parent = index;
+
+ return 0;
+}
+
+static u8 clk_multiple_parents_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_multiple_parent_ctx *ctx =
+ container_of(hw, struct clk_multiple_parent_ctx, hw);
+
+ return ctx->current_parent;
+}
+
+static const struct clk_ops clk_multiple_parents_mux_ops = {
+ .get_parent = clk_multiple_parents_mux_get_parent,
+ .set_parent = clk_multiple_parents_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate_closest,
+};
+
+static const struct clk_ops clk_multiple_parents_no_reparent_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .get_parent = clk_multiple_parents_mux_get_parent,
+ .set_parent = clk_multiple_parents_mux_set_parent,
+};
+
static int clk_test_init_with_ops(struct kunit *test, const struct clk_ops *ops)
{
struct clk_dummy_context *ctx;
@@ -160,12 +227,14 @@ static void clk_test_get_rate(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, ctx->rate);
+
+ clk_put(clk);
}
/*
@@ -179,7 +248,7 @@ static void clk_test_set_get_rate(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -189,6 +258,8 @@ static void clk_test_set_get_rate(struct kunit *test)
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ clk_put(clk);
}
/*
@@ -202,7 +273,7 @@ static void clk_test_set_set_get_rate(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -216,18 +287,21 @@ static void clk_test_set_set_get_rate(struct kunit *test)
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
- * Test that clk_round_rate and clk_set_rate are consitent and will
+ * Test that clk_round_rate and clk_set_rate are consistent and will
* return the same frequency.
*/
static void clk_test_round_set_get_rate(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
- unsigned long rounded_rate, set_rate;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ unsigned long set_rate;
+ long rounded_rate;
rounded_rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1);
KUNIT_ASSERT_GT(test, rounded_rate, 0);
@@ -240,6 +314,8 @@ static void clk_test_round_set_get_rate(struct kunit *test)
set_rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, set_rate, 0);
KUNIT_EXPECT_EQ(test, rounded_rate, set_rate);
+
+ clk_put(clk);
}
static struct kunit_case clk_test_cases[] = {
@@ -250,6 +326,11 @@ static struct kunit_case clk_test_cases[] = {
{}
};
+/*
+ * Test suite for a basic rate clock, without any parent.
+ *
+ * These tests exercise the rate API with simple scenarios
+ */
static struct kunit_suite clk_test_suite = {
.name = "clk-test",
.init = clk_test_init,
@@ -257,11 +338,879 @@ static struct kunit_suite clk_test_suite = {
.test_cases = clk_test_cases,
};
+static int clk_uncached_test_init(struct kunit *test)
+{
+ struct clk_dummy_context *ctx;
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->rate = DUMMY_CLOCK_INIT_RATE;
+ ctx->hw.init = CLK_HW_INIT_NO_PARENT("test-clk",
+ &clk_dummy_rate_ops,
+ CLK_GET_RATE_NOCACHE);
+
+ ret = clk_hw_register(NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Test that for an uncached clock, the clock framework doesn't cache
+ * the rate and clk_get_rate() will return the underlying clock rate
+ * even if it changed.
+ */
+static void clk_test_uncached_get_rate(struct kunit *test)
+{
+ struct clk_dummy_context *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ unsigned long rate;
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);
+
+ /* We change the rate behind the clock framework's back */
+ ctx->rate = DUMMY_CLOCK_RATE_1;
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that for an uncached clock, clk_set_rate_range() will work
+ * properly if the rate hasn't changed.
+ */
+static void clk_test_uncached_set_range(struct kunit *test)
+{
+ struct clk_dummy_context *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ unsigned long rate;
+
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate_range(clk,
+ DUMMY_CLOCK_RATE_1,
+ DUMMY_CLOCK_RATE_2),
+ 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that for an uncached clock, clk_set_rate_range() will work
+ * properly if the rate has changed in hardware.
+ *
+ * In this case, it means that if the rate wasn't initially in the range
+ * we're trying to set, but got changed at some point into the range
+ * without the kernel knowing about it, its rate shouldn't be affected.
+ */
+static void clk_test_uncached_updated_rate_set_range(struct kunit *test)
+{
+ struct clk_dummy_context *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ unsigned long rate;
+
+ /* We change the rate behind the clock framework's back */
+ ctx->rate = DUMMY_CLOCK_RATE_1 + 1000;
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate_range(clk,
+ DUMMY_CLOCK_RATE_1,
+ DUMMY_CLOCK_RATE_2),
+ 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
+
+ clk_put(clk);
+}
+
+static struct kunit_case clk_uncached_test_cases[] = {
+ KUNIT_CASE(clk_test_uncached_get_rate),
+ KUNIT_CASE(clk_test_uncached_set_range),
+ KUNIT_CASE(clk_test_uncached_updated_rate_set_range),
+ {}
+};
+
+/*
+ * Test suite for a basic, uncached, rate clock, without any parent.
+ *
+ * These tests exercise the rate API with simple scenarios
+ */
+static struct kunit_suite clk_uncached_test_suite = {
+ .name = "clk-uncached-test",
+ .init = clk_uncached_test_init,
+ .exit = clk_test_exit,
+ .test_cases = clk_uncached_test_cases,
+};
+
+static int
+clk_multiple_parents_mux_test_init(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx;
+ const char *parents[2] = { "parent-0", "parent-1"};
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
+ ret = clk_hw_register_kunit(test, NULL, &ctx->parents_ctx[0].hw);
+ if (ret)
+ return ret;
+
+ ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
+ ret = clk_hw_register_kunit(test, NULL, &ctx->parents_ctx[1].hw);
+ if (ret)
+ return ret;
+
+ ctx->current_parent = 0;
+ ctx->hw.init = CLK_HW_INIT_PARENTS("test-mux", parents,
+ &clk_multiple_parents_mux_ops,
+ CLK_SET_RATE_PARENT);
+ ret = clk_hw_register_kunit(test, NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Test that for a clock with multiple parents, clk_get_parent()
+ * actually returns the current one.
+ */
+static void
+clk_test_multiple_parents_mux_get_parent(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent = clk_hw_get_clk(&ctx->parents_ctx[0].hw, NULL);
+
+ KUNIT_EXPECT_TRUE(test, clk_is_match(clk_get_parent(clk), parent));
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that for a clock with a multiple parents, clk_has_parent()
+ * actually reports all of them as parents.
+ */
+static void
+clk_test_multiple_parents_mux_has_parent(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[0].hw, NULL);
+ KUNIT_EXPECT_TRUE(test, clk_has_parent(clk, parent));
+ clk_put(parent);
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_EXPECT_TRUE(test, clk_has_parent(clk, parent));
+ clk_put(parent);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that for a clock with a multiple parents, if we set a range on
+ * that clock and the parent is changed, its rate after the reparenting
+ * is still within the range we asked for.
+ *
+ * FIXME: clk_set_parent() only does the reparenting but doesn't
+ * reevaluate whether the new clock rate is within its boundaries or
+ * not.
+ */
+static void
+clk_test_multiple_parents_mux_set_range_set_parent_get_rate(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk_kunit(test, hw, NULL);
+ struct clk *parent1, *parent2;
+ unsigned long rate;
+ int ret;
+
+ kunit_skip(test, "This needs to be fixed in the core.");
+
+ parent1 = clk_hw_get_clk_kunit(test, &ctx->parents_ctx[0].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent1);
+ KUNIT_ASSERT_TRUE(test, clk_is_match(clk_get_parent(clk), parent1));
+
+ parent2 = clk_hw_get_clk_kunit(test, &ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent2);
+
+ ret = clk_set_rate(parent1, DUMMY_CLOCK_RATE_1);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate(parent2, DUMMY_CLOCK_RATE_2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(clk,
+ DUMMY_CLOCK_RATE_1 - 1000,
+ DUMMY_CLOCK_RATE_1 + 1000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_parent(clk, parent2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 - 1000);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
+}
+
+static struct kunit_case clk_multiple_parents_mux_test_cases[] = {
+ KUNIT_CASE(clk_test_multiple_parents_mux_get_parent),
+ KUNIT_CASE(clk_test_multiple_parents_mux_has_parent),
+ KUNIT_CASE(clk_test_multiple_parents_mux_set_range_set_parent_get_rate),
+ {}
+};
+
+/*
+ * Test suite for a basic mux clock with two parents, with
+ * CLK_SET_RATE_PARENT on the child.
+ *
+ * These tests exercise the consumer API and check that the state of the
+ * child and parents are sane and consistent.
+ */
+static struct kunit_suite
+clk_multiple_parents_mux_test_suite = {
+ .name = "clk-multiple-parents-mux-test",
+ .init = clk_multiple_parents_mux_test_init,
+ .test_cases = clk_multiple_parents_mux_test_cases,
+};
+
+static int
+clk_orphan_transparent_multiple_parent_mux_test_init(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx;
+ const char *parents[2] = { "missing-parent", "proper-parent"};
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("proper-parent",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->parents_ctx[1].rate = DUMMY_CLOCK_INIT_RATE;
+ ret = clk_hw_register_kunit(test, NULL, &ctx->parents_ctx[1].hw);
+ if (ret)
+ return ret;
+
+ ctx->hw.init = CLK_HW_INIT_PARENTS("test-orphan-mux", parents,
+ &clk_multiple_parents_mux_ops,
+ CLK_SET_RATE_PARENT);
+ ret = clk_hw_register_kunit(test, NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Test that, for a mux whose current parent hasn't been registered yet and is
+ * thus orphan, clk_get_parent() will return NULL.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_get_parent(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+
+ KUNIT_EXPECT_PTR_EQ(test, clk_get_parent(clk), NULL);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux whose current parent hasn't been registered yet,
+ * calling clk_set_parent() to a valid parent will properly update the
+ * mux parent and its orphan status.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_parent(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent, *new_parent;
+ int ret;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_parent = clk_get_parent(clk);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+ KUNIT_EXPECT_TRUE(test, clk_is_match(parent, new_parent));
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux that started orphan but got switched to a valid
+ * parent, calling clk_drop_range() on the mux won't affect the parent
+ * rate.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_parent_drop_range(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ unsigned long parent_rate, new_parent_rate;
+ int ret;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, parent_rate, 0);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_drop_range(clk);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_parent_rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, new_parent_rate, 0);
+ KUNIT_EXPECT_EQ(test, parent_rate, new_parent_rate);
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux that started orphan but got switched to a valid
+ * parent, the rate of the mux and its new parent are consistent.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_parent_get_rate(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ unsigned long parent_rate, rate;
+ int ret;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, parent_rate, 0);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, parent_rate, rate);
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux that started orphan but got switched to a valid
+ * parent, calling clk_put() on the mux won't affect the parent rate.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_parent_put(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk *clk, *parent;
+ unsigned long parent_rate, new_parent_rate;
+ int ret;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ clk = clk_hw_get_clk(&ctx->hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, clk);
+
+ parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, parent_rate, 0);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ clk_put(clk);
+
+ new_parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, new_parent_rate, 0);
+ KUNIT_EXPECT_EQ(test, parent_rate, new_parent_rate);
+
+ clk_put(parent);
+}
+
+/*
+ * Test that, for a mux that started orphan but got switched to a valid
+ * parent, calling clk_set_rate_range() will affect the parent state if
+ * its rate is out of range.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ unsigned long rate;
+ int ret;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux that started orphan but got switched to a valid
+ * parent, calling clk_set_rate_range() won't affect the parent state if
+ * its rate is within range.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_untouched(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ unsigned long parent_rate, new_parent_rate;
+ int ret;
+
+ parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, parent_rate, 0);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(clk,
+ DUMMY_CLOCK_INIT_RATE - 1000,
+ DUMMY_CLOCK_INIT_RATE + 1000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, new_parent_rate, 0);
+ KUNIT_EXPECT_EQ(test, parent_rate, new_parent_rate);
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux whose current parent hasn't been registered yet,
+ * calling clk_set_rate_range() will succeed, and will be taken into
+ * account when rounding a rate.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ long rate;
+ int ret;
+
+ ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a mux that started orphan, was assigned and rate and
+ * then got switched to a valid parent, its rate is eventually within
+ * range.
+ *
+ * FIXME: Even though we update the rate as part of clk_set_parent(), we
+ * don't evaluate whether that new rate is within range and needs to be
+ * adjusted.
+ */
+static void
+clk_test_orphan_transparent_multiple_parent_mux_set_range_set_parent_get_rate(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk_kunit(test, hw, NULL);
+ struct clk *parent;
+ unsigned long rate;
+ int ret;
+
+ kunit_skip(test, "This needs to be fixed in the core.");
+
+ clk_hw_set_rate_range(hw, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
+
+ parent = clk_hw_get_clk_kunit(test, &ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ ret = clk_set_parent(clk, parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+}
+
+static struct kunit_case clk_orphan_transparent_multiple_parent_mux_test_cases[] = {
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_get_parent),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_drop_range),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_get_rate),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_put),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_untouched),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate),
+ KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_range_set_parent_get_rate),
+ {}
+};
+
+/*
+ * Test suite for a basic mux clock with two parents. The default parent
+ * isn't registered, only the second parent is. By default, the clock
+ * will thus be orphan.
+ *
+ * These tests exercise the behaviour of the consumer API when dealing
+ * with an orphan clock, and how we deal with the transition to a valid
+ * parent.
+ */
+static struct kunit_suite clk_orphan_transparent_multiple_parent_mux_test_suite = {
+ .name = "clk-orphan-transparent-multiple-parent-mux-test",
+ .init = clk_orphan_transparent_multiple_parent_mux_test_init,
+ .test_cases = clk_orphan_transparent_multiple_parent_mux_test_cases,
+};
+
struct clk_single_parent_ctx {
struct clk_dummy_context parent_ctx;
struct clk_hw hw;
};
+static int clk_single_parent_mux_test_init(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx;
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->parent_ctx.rate = DUMMY_CLOCK_INIT_RATE;
+ ctx->parent_ctx.hw.init =
+ CLK_HW_INIT_NO_PARENT("parent-clk",
+ &clk_dummy_rate_ops,
+ 0);
+
+ ret = clk_hw_register_kunit(test, NULL, &ctx->parent_ctx.hw);
+ if (ret)
+ return ret;
+
+ ctx->hw.init = CLK_HW_INIT("test-clk", "parent-clk",
+ &clk_dummy_single_parent_ops,
+ CLK_SET_RATE_PARENT);
+
+ ret = clk_hw_register_kunit(test, NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void
+clk_single_parent_mux_test_exit(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+
+ clk_hw_unregister(&ctx->hw);
+ clk_hw_unregister(&ctx->parent_ctx.hw);
+}
+
+/*
+ * Test that for a clock with a single parent, clk_get_parent() actually
+ * returns the parent.
+ */
+static void
+clk_test_single_parent_mux_get_parent(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent = clk_hw_get_clk(&ctx->parent_ctx.hw, NULL);
+
+ KUNIT_EXPECT_TRUE(test, clk_is_match(clk_get_parent(clk), parent));
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that for a clock with a single parent, clk_has_parent() actually
+ * reports it as a parent.
+ */
+static void
+clk_test_single_parent_mux_has_parent(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent = clk_hw_get_clk(&ctx->parent_ctx.hw, NULL);
+
+ KUNIT_EXPECT_TRUE(test, clk_has_parent(clk, parent));
+
+ clk_put(parent);
+ clk_put(clk);
+}
+
+/*
+ * Test that for a clock that can't modify its rate and with a single
+ * parent, if we set disjoints range on the parent and then the child,
+ * the second will return an error.
+ *
+ * FIXME: clk_set_rate_range() only considers the current clock when
+ * evaluating whether ranges are disjoints and not the upstream clocks
+ * ranges.
+ */
+static void
+clk_test_single_parent_mux_set_range_disjoint_child_last(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk_kunit(test, hw, NULL);
+ struct clk *parent;
+ int ret;
+
+ kunit_skip(test, "This needs to be fixed in the core.");
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ ret = clk_set_rate_range(parent, 1000, 2000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(clk, 3000, 4000);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that for a clock that can't modify its rate and with a single
+ * parent, if we set disjoints range on the child and then the parent,
+ * the second will return an error.
+ *
+ * FIXME: clk_set_rate_range() only considers the current clock when
+ * evaluating whether ranges are disjoints and not the downstream clocks
+ * ranges.
+ */
+static void
+clk_test_single_parent_mux_set_range_disjoint_parent_last(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk_kunit(test, hw, NULL);
+ struct clk *parent;
+ int ret;
+
+ kunit_skip(test, "This needs to be fixed in the core.");
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ ret = clk_set_rate_range(clk, 1000, 2000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(parent, 3000, 4000);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that for a clock that can't modify its rate and with a single
+ * parent, if we set a range on the parent and then call
+ * clk_round_rate(), the boundaries of the parent are taken into
+ * account.
+ */
+static void
+clk_test_single_parent_mux_set_range_round_rate_parent_only(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ long rate;
+ int ret;
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ ret = clk_set_rate_range(parent, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that for a clock that can't modify its rate and with a single
+ * parent, if we set a range on the parent and a more restrictive one on
+ * the child, and then call clk_round_rate(), the boundaries of the
+ * two clocks are taken into account.
+ */
+static void
+clk_test_single_parent_mux_set_range_round_rate_child_smaller(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ long rate;
+ int ret;
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ ret = clk_set_rate_range(parent, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1 + 1000, DUMMY_CLOCK_RATE_2 - 1000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
+
+ rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_2 + 1000);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that for a clock that can't modify its rate and with a single
+ * parent, if we set a range on the child and a more restrictive one on
+ * the parent, and then call clk_round_rate(), the boundaries of the
+ * two clocks are taken into account.
+ */
+static void
+clk_test_single_parent_mux_set_range_round_rate_parent_smaller(struct kunit *test)
+{
+ struct clk_single_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *parent;
+ long rate;
+ int ret;
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ ret = clk_set_rate_range(parent, DUMMY_CLOCK_RATE_1 + 1000, DUMMY_CLOCK_RATE_2 - 1000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1 - 1000);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
+
+ rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_2 + 1000);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1 + 1000);
+ KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2 - 1000);
+
+ clk_put(clk);
+}
+
+static struct kunit_case clk_single_parent_mux_test_cases[] = {
+ KUNIT_CASE(clk_test_single_parent_mux_get_parent),
+ KUNIT_CASE(clk_test_single_parent_mux_has_parent),
+ KUNIT_CASE(clk_test_single_parent_mux_set_range_disjoint_child_last),
+ KUNIT_CASE(clk_test_single_parent_mux_set_range_disjoint_parent_last),
+ KUNIT_CASE(clk_test_single_parent_mux_set_range_round_rate_child_smaller),
+ KUNIT_CASE(clk_test_single_parent_mux_set_range_round_rate_parent_only),
+ KUNIT_CASE(clk_test_single_parent_mux_set_range_round_rate_parent_smaller),
+ {}
+};
+
+/*
+ * Test suite for a basic mux clock with one parent, with
+ * CLK_SET_RATE_PARENT on the child.
+ *
+ * These tests exercise the consumer API and check that the state of the
+ * child and parent are sane and consistent.
+ */
+static struct kunit_suite
+clk_single_parent_mux_test_suite = {
+ .name = "clk-single-parent-mux-test",
+ .init = clk_single_parent_mux_test_init,
+ .test_cases = clk_single_parent_mux_test_cases,
+};
+
static int clk_orphan_transparent_single_parent_mux_test_init(struct kunit *test)
{
struct clk_single_parent_ctx *ctx;
@@ -298,23 +1247,18 @@ static int clk_orphan_transparent_single_parent_mux_test_init(struct kunit *test
return 0;
}
-static void clk_orphan_transparent_single_parent_mux_test_exit(struct kunit *test)
-{
- struct clk_single_parent_ctx *ctx = test->priv;
-
- clk_hw_unregister(&ctx->hw);
- clk_hw_unregister(&ctx->parent_ctx.hw);
-}
-
/*
* Test that a mux-only clock, with an initial rate within a range,
* will still have the same rate after the range has been enforced.
+ *
+ * See:
+ * https://lore.kernel.org/linux-clk/7720158d-10a7-a17b-73a4-a8615c9c6d5c@collabora.com/
*/
static void clk_test_orphan_transparent_parent_mux_set_range(struct kunit *test)
{
struct clk_single_parent_ctx *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate, new_rate;
rate = clk_get_rate(clk);
@@ -329,6 +1273,8 @@ static void clk_test_orphan_transparent_parent_mux_set_range(struct kunit *test)
new_rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, new_rate, 0);
KUNIT_EXPECT_EQ(test, rate, new_rate);
+
+ clk_put(clk);
}
static struct kunit_case clk_orphan_transparent_single_parent_mux_test_cases[] = {
@@ -336,13 +1282,151 @@ static struct kunit_case clk_orphan_transparent_single_parent_mux_test_cases[] =
{}
};
+/*
+ * Test suite for a basic mux clock with one parent. The parent is
+ * registered after its child. The clock will thus be an orphan when
+ * registered, but will no longer be when the tests run.
+ *
+ * These tests make sure a clock that used to be orphan has a sane,
+ * consistent, behaviour.
+ */
static struct kunit_suite clk_orphan_transparent_single_parent_test_suite = {
.name = "clk-orphan-transparent-single-parent-test",
.init = clk_orphan_transparent_single_parent_mux_test_init,
- .exit = clk_orphan_transparent_single_parent_mux_test_exit,
+ .exit = clk_single_parent_mux_test_exit,
.test_cases = clk_orphan_transparent_single_parent_mux_test_cases,
};
+struct clk_single_parent_two_lvl_ctx {
+ struct clk_dummy_context parent_parent_ctx;
+ struct clk_dummy_context parent_ctx;
+ struct clk_hw hw;
+};
+
+static int
+clk_orphan_two_level_root_last_test_init(struct kunit *test)
+{
+ struct clk_single_parent_two_lvl_ctx *ctx;
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->parent_ctx.hw.init =
+ CLK_HW_INIT("intermediate-parent",
+ "root-parent",
+ &clk_dummy_single_parent_ops,
+ CLK_SET_RATE_PARENT);
+ ret = clk_hw_register(NULL, &ctx->parent_ctx.hw);
+ if (ret)
+ return ret;
+
+ ctx->hw.init =
+ CLK_HW_INIT("test-clk", "intermediate-parent",
+ &clk_dummy_single_parent_ops,
+ CLK_SET_RATE_PARENT);
+ ret = clk_hw_register(NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ ctx->parent_parent_ctx.rate = DUMMY_CLOCK_INIT_RATE;
+ ctx->parent_parent_ctx.hw.init =
+ CLK_HW_INIT_NO_PARENT("root-parent",
+ &clk_dummy_rate_ops,
+ 0);
+ ret = clk_hw_register(NULL, &ctx->parent_parent_ctx.hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void
+clk_orphan_two_level_root_last_test_exit(struct kunit *test)
+{
+ struct clk_single_parent_two_lvl_ctx *ctx = test->priv;
+
+ clk_hw_unregister(&ctx->hw);
+ clk_hw_unregister(&ctx->parent_ctx.hw);
+ clk_hw_unregister(&ctx->parent_parent_ctx.hw);
+}
+
+/*
+ * Test that, for a clock whose parent used to be orphan, clk_get_rate()
+ * will return the proper rate.
+ */
+static void
+clk_orphan_two_level_root_last_test_get_rate(struct kunit *test)
+{
+ struct clk_single_parent_two_lvl_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ unsigned long rate;
+
+ rate = clk_get_rate(clk);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that, for a clock whose parent used to be orphan,
+ * clk_set_rate_range() won't affect its rate if it is already within
+ * range.
+ *
+ * See (for Exynos 4210):
+ * https://lore.kernel.org/linux-clk/366a0232-bb4a-c357-6aa8-636e398e05eb@samsung.com/
+ */
+static void
+clk_orphan_two_level_root_last_test_set_range(struct kunit *test)
+{
+ struct clk_single_parent_two_lvl_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ unsigned long rate;
+ int ret;
+
+ ret = clk_set_rate_range(clk,
+ DUMMY_CLOCK_INIT_RATE - 1000,
+ DUMMY_CLOCK_INIT_RATE + 1000);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_INIT_RATE);
+
+ clk_put(clk);
+}
+
+static struct kunit_case
+clk_orphan_two_level_root_last_test_cases[] = {
+ KUNIT_CASE(clk_orphan_two_level_root_last_test_get_rate),
+ KUNIT_CASE(clk_orphan_two_level_root_last_test_set_range),
+ {}
+};
+
+/*
+ * Test suite for a basic, transparent, clock with a parent that is also
+ * such a clock. The parent's parent is registered last, while the
+ * parent and its child are registered in that order. The intermediate
+ * and leaf clocks will thus be orphan when registered, but the leaf
+ * clock itself will always have its parent and will never be
+ * reparented. Indeed, it's only orphan because its parent is.
+ *
+ * These tests exercise the behaviour of the consumer API when dealing
+ * with an orphan clock, and how we deal with the transition to a valid
+ * parent.
+ */
+static struct kunit_suite
+clk_orphan_two_level_root_last_test_suite = {
+ .name = "clk-orphan-two-level-root-last-test",
+ .init = clk_orphan_two_level_root_last_test_init,
+ .exit = clk_orphan_two_level_root_last_test_exit,
+ .test_cases = clk_orphan_two_level_root_last_test_cases,
+};
+
/*
* Test that clk_set_rate_range won't return an error for a valid range
* and that it will make sure the rate of the clock is within the
@@ -352,7 +1436,7 @@ static void clk_range_test_set_range(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -365,6 +1449,8 @@ static void clk_range_test_set_range(struct kunit *test)
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
@@ -375,13 +1461,15 @@ static void clk_range_test_set_range_invalid(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
KUNIT_EXPECT_LT(test,
clk_set_rate_range(clk,
DUMMY_CLOCK_RATE_1 + 1000,
DUMMY_CLOCK_RATE_1),
0);
+
+ clk_put(clk);
}
/*
@@ -420,7 +1508,7 @@ static void clk_range_test_set_range_round_rate_lower(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
long rate;
KUNIT_ASSERT_EQ(test,
@@ -433,6 +1521,8 @@ static void clk_range_test_set_range_round_rate_lower(struct kunit *test)
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
@@ -443,7 +1533,7 @@ static void clk_range_test_set_range_set_rate_lower(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -460,6 +1550,8 @@ static void clk_range_test_set_range_set_rate_lower(struct kunit *test)
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
@@ -472,7 +1564,7 @@ static void clk_range_test_set_range_set_round_rate_consistent_lower(struct kuni
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
long rounded;
KUNIT_ASSERT_EQ(test,
@@ -489,6 +1581,8 @@ static void clk_range_test_set_range_set_round_rate_consistent_lower(struct kuni
0);
KUNIT_EXPECT_EQ(test, rounded, clk_get_rate(clk));
+
+ clk_put(clk);
}
/*
@@ -499,7 +1593,7 @@ static void clk_range_test_set_range_round_rate_higher(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
long rate;
KUNIT_ASSERT_EQ(test,
@@ -512,6 +1606,8 @@ static void clk_range_test_set_range_round_rate_higher(struct kunit *test)
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
@@ -522,7 +1618,7 @@ static void clk_range_test_set_range_set_rate_higher(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -539,6 +1635,8 @@ static void clk_range_test_set_range_set_rate_higher(struct kunit *test)
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_GE(test, rate, DUMMY_CLOCK_RATE_1);
KUNIT_EXPECT_LE(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
@@ -551,7 +1649,7 @@ static void clk_range_test_set_range_set_round_rate_consistent_higher(struct kun
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
long rounded;
KUNIT_ASSERT_EQ(test,
@@ -568,6 +1666,8 @@ static void clk_range_test_set_range_set_round_rate_consistent_higher(struct kun
0);
KUNIT_EXPECT_EQ(test, rounded, clk_get_rate(clk));
+
+ clk_put(clk);
}
/*
@@ -582,7 +1682,7 @@ static void clk_range_test_set_range_get_rate_raised(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -598,6 +1698,8 @@ static void clk_range_test_set_range_get_rate_raised(struct kunit *test)
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ clk_put(clk);
}
/*
@@ -612,7 +1714,7 @@ static void clk_range_test_set_range_get_rate_lowered(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -628,6 +1730,8 @@ static void clk_range_test_set_range_get_rate_lowered(struct kunit *test)
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
static struct kunit_case clk_range_test_cases[] = {
@@ -645,6 +1749,12 @@ static struct kunit_case clk_range_test_cases[] = {
{}
};
+/*
+ * Test suite for a basic rate clock, without any parent.
+ *
+ * These tests exercise the rate range API: clk_set_rate_range(),
+ * clk_set_min_rate(), clk_set_max_rate(), clk_drop_range().
+ */
static struct kunit_suite clk_range_test_suite = {
.name = "clk-range-test",
.init = clk_test_init,
@@ -664,7 +1774,7 @@ static void clk_range_test_set_range_rate_maximized(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -700,6 +1810,8 @@ static void clk_range_test_set_range_rate_maximized(struct kunit *test)
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(clk);
}
/*
@@ -714,7 +1826,7 @@ static void clk_range_test_multiple_set_range_rate_maximized(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *user1, *user2;
unsigned long rate;
@@ -758,14 +1870,79 @@ static void clk_range_test_multiple_set_range_rate_maximized(struct kunit *test)
clk_put(user2);
clk_put(user1);
+ clk_put(clk);
+}
+
+/*
+ * Test that if we have several subsequent calls to
+ * clk_set_rate_range(), across multiple users, the core will reevaluate
+ * whether a new rate is needed, including when a user drop its clock.
+ *
+ * With clk_dummy_maximize_rate_ops, this means that the rate will
+ * trail along the maximum as it evolves.
+ */
+static void clk_range_test_multiple_set_range_rate_put_maximized(struct kunit *test)
+{
+ struct clk_dummy_context *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *user1, *user2;
+ unsigned long rate;
+
+ user1 = clk_hw_get_clk(hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
+
+ user2 = clk_hw_get_clk(hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
+
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate(clk, DUMMY_CLOCK_RATE_2 + 1000),
+ 0);
+
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate_range(user1,
+ 0,
+ DUMMY_CLOCK_RATE_2),
+ 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
+
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate_range(user2,
+ 0,
+ DUMMY_CLOCK_RATE_1),
+ 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ clk_put(user2);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(user1);
+ clk_put(clk);
}
static struct kunit_case clk_range_maximize_test_cases[] = {
KUNIT_CASE(clk_range_test_set_range_rate_maximized),
KUNIT_CASE(clk_range_test_multiple_set_range_rate_maximized),
+ KUNIT_CASE(clk_range_test_multiple_set_range_rate_put_maximized),
{}
};
+/*
+ * Test suite for a basic rate clock, without any parent.
+ *
+ * These tests exercise the rate range API: clk_set_rate_range(),
+ * clk_set_min_rate(), clk_set_max_rate(), clk_drop_range(), with a
+ * driver that will always try to run at the highest possible rate.
+ */
static struct kunit_suite clk_range_maximize_test_suite = {
.name = "clk-range-maximize-test",
.init = clk_maximize_test_init,
@@ -785,7 +1962,7 @@ static void clk_range_test_set_range_rate_minimized(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
KUNIT_ASSERT_EQ(test,
@@ -821,6 +1998,8 @@ static void clk_range_test_set_range_rate_minimized(struct kunit *test)
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ clk_put(clk);
}
/*
@@ -835,7 +2014,7 @@ static void clk_range_test_multiple_set_range_rate_minimized(struct kunit *test)
{
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
- struct clk *clk = hw->clk;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *user1, *user2;
unsigned long rate;
@@ -875,14 +2054,75 @@ static void clk_range_test_multiple_set_range_rate_minimized(struct kunit *test)
clk_put(user2);
clk_put(user1);
+ clk_put(clk);
+}
+
+/*
+ * Test that if we have several subsequent calls to
+ * clk_set_rate_range(), across multiple users, the core will reevaluate
+ * whether a new rate is needed, including when a user drop its clock.
+ *
+ * With clk_dummy_minimize_rate_ops, this means that the rate will
+ * trail along the minimum as it evolves.
+ */
+static void clk_range_test_multiple_set_range_rate_put_minimized(struct kunit *test)
+{
+ struct clk_dummy_context *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *user1, *user2;
+ unsigned long rate;
+
+ user1 = clk_hw_get_clk(hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user1);
+
+ user2 = clk_hw_get_clk(hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, user2);
+
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate_range(user1,
+ DUMMY_CLOCK_RATE_1,
+ ULONG_MAX),
+ 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ KUNIT_ASSERT_EQ(test,
+ clk_set_rate_range(user2,
+ DUMMY_CLOCK_RATE_2,
+ ULONG_MAX),
+ 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(user2);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+
+ clk_put(user1);
+ clk_put(clk);
}
static struct kunit_case clk_range_minimize_test_cases[] = {
KUNIT_CASE(clk_range_test_set_range_rate_minimized),
KUNIT_CASE(clk_range_test_multiple_set_range_rate_minimized),
+ KUNIT_CASE(clk_range_test_multiple_set_range_rate_put_minimized),
{}
};
+/*
+ * Test suite for a basic rate clock, without any parent.
+ *
+ * These tests exercise the rate range API: clk_set_rate_range(),
+ * clk_set_min_rate(), clk_set_max_rate(), clk_drop_range(), with a
+ * driver that will always try to run at the lowest possible rate.
+ */
static struct kunit_suite clk_range_minimize_test_suite = {
.name = "clk-range-minimize-test",
.init = clk_minimize_test_init,
@@ -890,11 +2130,1436 @@ static struct kunit_suite clk_range_minimize_test_suite = {
.test_cases = clk_range_minimize_test_cases,
};
+struct clk_leaf_mux_ctx {
+ struct clk_multiple_parent_ctx mux_ctx;
+ struct clk_hw hw;
+ struct clk_hw parent;
+ struct clk_rate_request *req;
+ int (*determine_rate_func)(struct clk_hw *hw, struct clk_rate_request *req);
+};
+
+static int clk_leaf_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct clk_leaf_mux_ctx *ctx = container_of(hw, struct clk_leaf_mux_ctx, hw);
+ int ret;
+ struct clk_rate_request *parent_req = ctx->req;
+
+ clk_hw_forward_rate_request(hw, req, req->best_parent_hw, parent_req, req->rate);
+ ret = ctx->determine_rate_func(req->best_parent_hw, parent_req);
+ if (ret)
+ return ret;
+
+ req->rate = parent_req->rate;
+
+ return 0;
+}
+
+static const struct clk_ops clk_leaf_mux_set_rate_parent_ops = {
+ .determine_rate = clk_leaf_mux_determine_rate,
+ .set_parent = clk_dummy_single_set_parent,
+ .get_parent = clk_dummy_single_get_parent,
+};
+
+static int
+clk_leaf_mux_set_rate_parent_test_init(struct kunit *test)
+{
+ struct clk_leaf_mux_ctx *ctx;
+ const char *top_parents[2] = { "parent-0", "parent-1" };
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->mux_ctx.parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->mux_ctx.parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
+ ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[0].hw);
+ if (ret)
+ return ret;
+
+ ctx->mux_ctx.parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->mux_ctx.parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
+ ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[1].hw);
+ if (ret)
+ return ret;
+
+ ctx->mux_ctx.current_parent = 0;
+ ctx->mux_ctx.hw.init = CLK_HW_INIT_PARENTS("test-mux", top_parents,
+ &clk_multiple_parents_mux_ops,
+ 0);
+ ret = clk_hw_register(NULL, &ctx->mux_ctx.hw);
+ if (ret)
+ return ret;
+
+ ctx->parent.init = CLK_HW_INIT_HW("test-parent", &ctx->mux_ctx.hw,
+ &empty_clk_ops, CLK_SET_RATE_PARENT);
+ ret = clk_hw_register(NULL, &ctx->parent);
+ if (ret)
+ return ret;
+
+ ctx->hw.init = CLK_HW_INIT_HW("test-clock", &ctx->parent,
+ &clk_leaf_mux_set_rate_parent_ops,
+ CLK_SET_RATE_PARENT);
+ ret = clk_hw_register(NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void clk_leaf_mux_set_rate_parent_test_exit(struct kunit *test)
+{
+ struct clk_leaf_mux_ctx *ctx = test->priv;
+
+ clk_hw_unregister(&ctx->hw);
+ clk_hw_unregister(&ctx->parent);
+ clk_hw_unregister(&ctx->mux_ctx.hw);
+ clk_hw_unregister(&ctx->mux_ctx.parents_ctx[0].hw);
+ clk_hw_unregister(&ctx->mux_ctx.parents_ctx[1].hw);
+}
+
+struct clk_leaf_mux_set_rate_parent_determine_rate_test_case {
+ const char *desc;
+ int (*determine_rate_func)(struct clk_hw *hw, struct clk_rate_request *req);
+};
+
+static void
+clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc(
+ const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *t, char *desc)
+{
+ strcpy(desc, t->desc);
+}
+
+static const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case
+clk_leaf_mux_set_rate_parent_determine_rate_test_cases[] = {
+ {
+ /*
+ * Test that __clk_determine_rate() on the parent that can't
+ * change rate doesn't return a clk_rate_request structure with
+ * the best_parent_hw pointer pointing to the parent.
+ */
+ .desc = "clk_leaf_mux_set_rate_parent__clk_determine_rate_proper_parent",
+ .determine_rate_func = __clk_determine_rate,
+ },
+ {
+ /*
+ * Test that __clk_mux_determine_rate() on the parent that
+ * can't change rate doesn't return a clk_rate_request
+ * structure with the best_parent_hw pointer pointing to
+ * the parent.
+ */
+ .desc = "clk_leaf_mux_set_rate_parent__clk_mux_determine_rate_proper_parent",
+ .determine_rate_func = __clk_mux_determine_rate,
+ },
+ {
+ /*
+ * Test that __clk_mux_determine_rate_closest() on the parent
+ * that can't change rate doesn't return a clk_rate_request
+ * structure with the best_parent_hw pointer pointing to
+ * the parent.
+ */
+ .desc = "clk_leaf_mux_set_rate_parent__clk_mux_determine_rate_closest_proper_parent",
+ .determine_rate_func = __clk_mux_determine_rate_closest,
+ },
+ {
+ /*
+ * Test that clk_hw_determine_rate_no_reparent() on the parent
+ * that can't change rate doesn't return a clk_rate_request
+ * structure with the best_parent_hw pointer pointing to
+ * the parent.
+ */
+ .desc = "clk_leaf_mux_set_rate_parent_clk_hw_determine_rate_no_reparent_proper_parent",
+ .determine_rate_func = clk_hw_determine_rate_no_reparent,
+ },
+};
+
+KUNIT_ARRAY_PARAM(clk_leaf_mux_set_rate_parent_determine_rate_test,
+ clk_leaf_mux_set_rate_parent_determine_rate_test_cases,
+ clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc)
+
+/*
+ * Test that when a clk that can't change rate itself calls a function like
+ * __clk_determine_rate() on its parent it doesn't get back a clk_rate_request
+ * structure that has the best_parent_hw pointer point to the clk_hw passed
+ * into the determine rate function. See commit 262ca38f4b6e ("clk: Stop
+ * forwarding clk_rate_requests to the parent") for more background.
+ */
+static void clk_leaf_mux_set_rate_parent_determine_rate_test(struct kunit *test)
+{
+ struct clk_leaf_mux_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk_rate_request req;
+ unsigned long rate;
+ const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *test_param;
+
+ test_param = test->param_value;
+ ctx->determine_rate_func = test_param->determine_rate_func;
+
+ ctx->req = &req;
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_EQ(test, rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_ASSERT_EQ(test, DUMMY_CLOCK_RATE_2, clk_round_rate(clk, DUMMY_CLOCK_RATE_2));
+
+ KUNIT_EXPECT_EQ(test, req.rate, DUMMY_CLOCK_RATE_2);
+ KUNIT_EXPECT_EQ(test, req.best_parent_rate, DUMMY_CLOCK_RATE_2);
+ KUNIT_EXPECT_PTR_EQ(test, req.best_parent_hw, &ctx->mux_ctx.hw);
+
+ clk_put(clk);
+}
+
+static struct kunit_case clk_leaf_mux_set_rate_parent_test_cases[] = {
+ KUNIT_CASE_PARAM(clk_leaf_mux_set_rate_parent_determine_rate_test,
+ clk_leaf_mux_set_rate_parent_determine_rate_test_gen_params),
+ {}
+};
+
+/*
+ * Test suite for a clock whose parent is a pass-through clk whose parent is a
+ * mux with multiple parents. The leaf and pass-through clocks have the
+ * CLK_SET_RATE_PARENT flag, and will forward rate requests to the mux, which
+ * will then select which parent is the best fit for a given rate.
+ *
+ * These tests exercise the behaviour of muxes, and the proper selection
+ * of parents.
+ */
+static struct kunit_suite clk_leaf_mux_set_rate_parent_test_suite = {
+ .name = "clk-leaf-mux-set-rate-parent",
+ .init = clk_leaf_mux_set_rate_parent_test_init,
+ .exit = clk_leaf_mux_set_rate_parent_test_exit,
+ .test_cases = clk_leaf_mux_set_rate_parent_test_cases,
+};
+
+struct clk_mux_notifier_rate_change {
+ bool done;
+ unsigned long old_rate;
+ unsigned long new_rate;
+ wait_queue_head_t wq;
+};
+
+struct clk_mux_notifier_ctx {
+ struct clk_multiple_parent_ctx mux_ctx;
+ struct clk *clk;
+ struct notifier_block clk_nb;
+ struct clk_mux_notifier_rate_change pre_rate_change;
+ struct clk_mux_notifier_rate_change post_rate_change;
+};
+
+#define NOTIFIER_TIMEOUT_MS 100
+
+static int clk_mux_notifier_callback(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct clk_notifier_data *clk_data = data;
+ struct clk_mux_notifier_ctx *ctx = container_of(nb,
+ struct clk_mux_notifier_ctx,
+ clk_nb);
+
+ if (action & PRE_RATE_CHANGE) {
+ ctx->pre_rate_change.old_rate = clk_data->old_rate;
+ ctx->pre_rate_change.new_rate = clk_data->new_rate;
+ ctx->pre_rate_change.done = true;
+ wake_up_interruptible(&ctx->pre_rate_change.wq);
+ }
+
+ if (action & POST_RATE_CHANGE) {
+ ctx->post_rate_change.old_rate = clk_data->old_rate;
+ ctx->post_rate_change.new_rate = clk_data->new_rate;
+ ctx->post_rate_change.done = true;
+ wake_up_interruptible(&ctx->post_rate_change.wq);
+ }
+
+ return 0;
+}
+
+static int clk_mux_notifier_test_init(struct kunit *test)
+{
+ struct clk_mux_notifier_ctx *ctx;
+ const char *top_parents[2] = { "parent-0", "parent-1" };
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+ ctx->clk_nb.notifier_call = clk_mux_notifier_callback;
+ init_waitqueue_head(&ctx->pre_rate_change.wq);
+ init_waitqueue_head(&ctx->post_rate_change.wq);
+
+ ctx->mux_ctx.parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->mux_ctx.parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
+ ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[0].hw);
+ if (ret)
+ return ret;
+
+ ctx->mux_ctx.parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->mux_ctx.parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
+ ret = clk_hw_register(NULL, &ctx->mux_ctx.parents_ctx[1].hw);
+ if (ret)
+ return ret;
+
+ ctx->mux_ctx.current_parent = 0;
+ ctx->mux_ctx.hw.init = CLK_HW_INIT_PARENTS("test-mux", top_parents,
+ &clk_multiple_parents_mux_ops,
+ 0);
+ ret = clk_hw_register(NULL, &ctx->mux_ctx.hw);
+ if (ret)
+ return ret;
+
+ ctx->clk = clk_hw_get_clk(&ctx->mux_ctx.hw, NULL);
+ ret = clk_notifier_register(ctx->clk, &ctx->clk_nb);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void clk_mux_notifier_test_exit(struct kunit *test)
+{
+ struct clk_mux_notifier_ctx *ctx = test->priv;
+ struct clk *clk = ctx->clk;
+
+ clk_notifier_unregister(clk, &ctx->clk_nb);
+ clk_put(clk);
+
+ clk_hw_unregister(&ctx->mux_ctx.hw);
+ clk_hw_unregister(&ctx->mux_ctx.parents_ctx[0].hw);
+ clk_hw_unregister(&ctx->mux_ctx.parents_ctx[1].hw);
+}
+
+/*
+ * Test that if the we have a notifier registered on a mux, the core
+ * will notify us when we switch to another parent, and with the proper
+ * old and new rates.
+ */
+static void clk_mux_notifier_set_parent_test(struct kunit *test)
+{
+ struct clk_mux_notifier_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->mux_ctx.hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *new_parent = clk_hw_get_clk(&ctx->mux_ctx.parents_ctx[1].hw, NULL);
+ int ret;
+
+ ret = clk_set_parent(clk, new_parent);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = wait_event_interruptible_timeout(ctx->pre_rate_change.wq,
+ ctx->pre_rate_change.done,
+ msecs_to_jiffies(NOTIFIER_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, ctx->pre_rate_change.old_rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_EQ(test, ctx->pre_rate_change.new_rate, DUMMY_CLOCK_RATE_2);
+
+ ret = wait_event_interruptible_timeout(ctx->post_rate_change.wq,
+ ctx->post_rate_change.done,
+ msecs_to_jiffies(NOTIFIER_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, ctx->post_rate_change.old_rate, DUMMY_CLOCK_RATE_1);
+ KUNIT_EXPECT_EQ(test, ctx->post_rate_change.new_rate, DUMMY_CLOCK_RATE_2);
+
+ clk_put(new_parent);
+ clk_put(clk);
+}
+
+static struct kunit_case clk_mux_notifier_test_cases[] = {
+ KUNIT_CASE(clk_mux_notifier_set_parent_test),
+ {}
+};
+
+/*
+ * Test suite for a mux with multiple parents, and a notifier registered
+ * on the mux.
+ *
+ * These tests exercise the behaviour of notifiers.
+ */
+static struct kunit_suite clk_mux_notifier_test_suite = {
+ .name = "clk-mux-notifier",
+ .init = clk_mux_notifier_test_init,
+ .exit = clk_mux_notifier_test_exit,
+ .test_cases = clk_mux_notifier_test_cases,
+};
+
+static int
+clk_mux_no_reparent_test_init(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx;
+ const char *parents[2] = { "parent-0", "parent-1"};
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
+ ret = clk_hw_register(NULL, &ctx->parents_ctx[0].hw);
+ if (ret)
+ return ret;
+
+ ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
+ &clk_dummy_rate_ops,
+ 0);
+ ctx->parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
+ ret = clk_hw_register(NULL, &ctx->parents_ctx[1].hw);
+ if (ret)
+ return ret;
+
+ ctx->current_parent = 0;
+ ctx->hw.init = CLK_HW_INIT_PARENTS("test-mux", parents,
+ &clk_multiple_parents_no_reparent_mux_ops,
+ 0);
+ ret = clk_hw_register(NULL, &ctx->hw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void
+clk_mux_no_reparent_test_exit(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+
+ clk_hw_unregister(&ctx->hw);
+ clk_hw_unregister(&ctx->parents_ctx[0].hw);
+ clk_hw_unregister(&ctx->parents_ctx[1].hw);
+}
+
+/*
+ * Test that if the we have a mux that cannot change parent and we call
+ * clk_round_rate() on it with a rate that should cause it to change
+ * parent, it won't.
+ */
+static void clk_mux_no_reparent_round_rate(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *other_parent, *parent;
+ unsigned long other_parent_rate;
+ unsigned long parent_rate;
+ long rounded_rate;
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, parent_rate, 0);
+
+ other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent);
+ KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent));
+
+ other_parent_rate = clk_get_rate(other_parent);
+ KUNIT_ASSERT_GT(test, other_parent_rate, 0);
+ clk_put(other_parent);
+
+ rounded_rate = clk_round_rate(clk, other_parent_rate);
+ KUNIT_ASSERT_GT(test, rounded_rate, 0);
+ KUNIT_EXPECT_EQ(test, rounded_rate, parent_rate);
+
+ clk_put(clk);
+}
+
+/*
+ * Test that if the we have a mux that cannot change parent and we call
+ * clk_set_rate() on it with a rate that should cause it to change
+ * parent, it won't.
+ */
+static void clk_mux_no_reparent_set_rate(struct kunit *test)
+{
+ struct clk_multiple_parent_ctx *ctx = test->priv;
+ struct clk_hw *hw = &ctx->hw;
+ struct clk *clk = clk_hw_get_clk(hw, NULL);
+ struct clk *other_parent, *parent;
+ unsigned long other_parent_rate;
+ unsigned long parent_rate;
+ unsigned long rate;
+ int ret;
+
+ parent = clk_get_parent(clk);
+ KUNIT_ASSERT_PTR_NE(test, parent, NULL);
+
+ parent_rate = clk_get_rate(parent);
+ KUNIT_ASSERT_GT(test, parent_rate, 0);
+
+ other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent);
+ KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent));
+
+ other_parent_rate = clk_get_rate(other_parent);
+ KUNIT_ASSERT_GT(test, other_parent_rate, 0);
+ clk_put(other_parent);
+
+ ret = clk_set_rate(clk, other_parent_rate);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ rate = clk_get_rate(clk);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, rate, parent_rate);
+
+ clk_put(clk);
+}
+
+static struct kunit_case clk_mux_no_reparent_test_cases[] = {
+ KUNIT_CASE(clk_mux_no_reparent_round_rate),
+ KUNIT_CASE(clk_mux_no_reparent_set_rate),
+ {}
+};
+
+/*
+ * Test suite for a clock mux that isn't allowed to change parent, using
+ * the clk_hw_determine_rate_no_reparent() helper.
+ *
+ * These tests exercise that helper, and the proper selection of
+ * rates and parents.
+ */
+static struct kunit_suite clk_mux_no_reparent_test_suite = {
+ .name = "clk-mux-no-reparent",
+ .init = clk_mux_no_reparent_test_init,
+ .exit = clk_mux_no_reparent_test_exit,
+ .test_cases = clk_mux_no_reparent_test_cases,
+};
+
+struct clk_register_clk_parent_data_test_case {
+ const char *desc;
+ struct clk_parent_data pdata;
+};
+
+static void
+clk_register_clk_parent_data_test_case_to_desc(
+ const struct clk_register_clk_parent_data_test_case *t, char *desc)
+{
+ strcpy(desc, t->desc);
+}
+
+static const struct clk_register_clk_parent_data_test_case
+clk_register_clk_parent_data_of_cases[] = {
+ {
+ /*
+ * Test that a clk registered with a struct device_node can
+ * find a parent based on struct clk_parent_data::index.
+ */
+ .desc = "clk_parent_data_of_index_test",
+ .pdata.index = 0,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device_node can
+ * find a parent based on struct clk_parent_data::fwname.
+ */
+ .desc = "clk_parent_data_of_fwname_test",
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT1,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device_node can
+ * find a parent based on struct clk_parent_data::name.
+ */
+ .desc = "clk_parent_data_of_name_test",
+ /* The index must be negative to indicate firmware not used */
+ .pdata.index = -1,
+ .pdata.name = CLK_PARENT_DATA_1MHZ_NAME,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device_node can
+ * find a parent based on struct
+ * clk_parent_data::{fw_name,name}.
+ */
+ .desc = "clk_parent_data_of_fwname_name_test",
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT1,
+ .pdata.name = "not_matching",
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device_node can
+ * find a parent based on struct clk_parent_data::{index,name}.
+ * Index takes priority.
+ */
+ .desc = "clk_parent_data_of_index_name_priority_test",
+ .pdata.index = 0,
+ .pdata.name = "not_matching",
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device_node can
+ * find a parent based on struct
+ * clk_parent_data::{index,fwname,name}. The fw_name takes
+ * priority over index and name.
+ */
+ .desc = "clk_parent_data_of_index_fwname_name_priority_test",
+ .pdata.index = 1,
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT1,
+ .pdata.name = "not_matching",
+ },
+};
+
+KUNIT_ARRAY_PARAM(clk_register_clk_parent_data_of_test, clk_register_clk_parent_data_of_cases,
+ clk_register_clk_parent_data_test_case_to_desc)
+
+/**
+ * struct clk_register_clk_parent_data_of_ctx - Context for clk_parent_data OF tests
+ * @np: device node of clk under test
+ * @hw: clk_hw for clk under test
+ */
+struct clk_register_clk_parent_data_of_ctx {
+ struct device_node *np;
+ struct clk_hw hw;
+};
+
+static int clk_register_clk_parent_data_of_test_init(struct kunit *test)
+{
+ struct clk_register_clk_parent_data_of_ctx *ctx;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ of_overlay_apply_kunit(test, kunit_clk_parent_data_test));
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ test->priv = ctx;
+
+ ctx->np = of_find_compatible_node(NULL, NULL, "test,clk-parent-data");
+ if (!ctx->np)
+ return -ENODEV;
+
+ of_node_put_kunit(test, ctx->np);
+
+ return 0;
+}
+
+/*
+ * Test that a clk registered with a struct device_node can find a parent based on
+ * struct clk_parent_data when the hw member isn't set.
+ */
+static void clk_register_clk_parent_data_of_test(struct kunit *test)
+{
+ struct clk_register_clk_parent_data_of_ctx *ctx = test->priv;
+ struct clk_hw *parent_hw;
+ const struct clk_register_clk_parent_data_test_case *test_param;
+ struct clk_init_data init = { };
+ struct clk *expected_parent, *actual_parent;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->np);
+
+ expected_parent = of_clk_get_kunit(test, ctx->np, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_parent);
+
+ test_param = test->param_value;
+ init.parent_data = &test_param->pdata;
+ init.num_parents = 1;
+ init.name = "parent_data_of_test_clk";
+ init.ops = &clk_dummy_single_parent_ops;
+ ctx->hw.init = &init;
+ KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, ctx->np, &ctx->hw));
+
+ parent_hw = clk_hw_get_parent(&ctx->hw);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
+
+ actual_parent = clk_hw_get_clk_kunit(test, parent_hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, actual_parent);
+
+ KUNIT_EXPECT_TRUE(test, clk_is_match(expected_parent, actual_parent));
+}
+
+static struct kunit_case clk_register_clk_parent_data_of_test_cases[] = {
+ KUNIT_CASE_PARAM(clk_register_clk_parent_data_of_test,
+ clk_register_clk_parent_data_of_test_gen_params),
+ {}
+};
+
+/*
+ * Test suite for registering clks with struct clk_parent_data and a struct
+ * device_node.
+ */
+static struct kunit_suite clk_register_clk_parent_data_of_suite = {
+ .name = "clk_register_clk_parent_data_of",
+ .init = clk_register_clk_parent_data_of_test_init,
+ .test_cases = clk_register_clk_parent_data_of_test_cases,
+};
+
+/**
+ * struct platform_driver_dev_ctx - Context to stash platform device
+ * @dev: device under test
+ * @pdrv: driver to attach to find @dev
+ */
+struct platform_driver_dev_ctx {
+ struct device *dev;
+ struct platform_driver pdrv;
+};
+
+static inline struct platform_driver_dev_ctx *
+pdev_to_platform_driver_dev_ctx(struct platform_device *pdev)
+{
+ return container_of(to_platform_driver(pdev->dev.driver),
+ struct platform_driver_dev_ctx, pdrv);
+}
+
+static int kunit_platform_driver_dev_probe(struct platform_device *pdev)
+{
+ struct platform_driver_dev_ctx *ctx;
+
+ ctx = pdev_to_platform_driver_dev_ctx(pdev);
+ ctx->dev = &pdev->dev;
+
+ return 0;
+}
+
+static struct device *
+kunit_of_platform_driver_dev(struct kunit *test, const struct of_device_id *match_table)
+{
+ struct platform_driver_dev_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ctx->pdrv.probe = kunit_platform_driver_dev_probe;
+ ctx->pdrv.driver.of_match_table = match_table;
+ ctx->pdrv.driver.name = __func__;
+ ctx->pdrv.driver.owner = THIS_MODULE;
+
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
+
+ return ctx->dev;
+}
+
+static const struct clk_register_clk_parent_data_test_case
+clk_register_clk_parent_data_device_cases[] = {
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::index.
+ */
+ .desc = "clk_parent_data_device_index_test",
+ .pdata.index = 1,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::fwname.
+ */
+ .desc = "clk_parent_data_device_fwname_test",
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT2,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::name.
+ */
+ .desc = "clk_parent_data_device_name_test",
+ /* The index must be negative to indicate firmware not used */
+ .pdata.index = -1,
+ .pdata.name = CLK_PARENT_DATA_50MHZ_NAME,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::{fw_name,name}.
+ */
+ .desc = "clk_parent_data_device_fwname_name_test",
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT2,
+ .pdata.name = "not_matching",
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::{index,name}. Index
+ * takes priority.
+ */
+ .desc = "clk_parent_data_device_index_name_priority_test",
+ .pdata.index = 1,
+ .pdata.name = "not_matching",
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::{index,fwname,name}.
+ * The fw_name takes priority over index and name.
+ */
+ .desc = "clk_parent_data_device_index_fwname_name_priority_test",
+ .pdata.index = 0,
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT2,
+ .pdata.name = "not_matching",
+ },
+};
+
+KUNIT_ARRAY_PARAM(clk_register_clk_parent_data_device_test,
+ clk_register_clk_parent_data_device_cases,
+ clk_register_clk_parent_data_test_case_to_desc)
+
+/*
+ * Test that a clk registered with a struct device can find a parent based on
+ * struct clk_parent_data when the hw member isn't set.
+ */
+static void clk_register_clk_parent_data_device_test(struct kunit *test)
+{
+ struct device *dev;
+ struct clk_hw *hw;
+ const struct clk_register_clk_parent_data_test_case *test_param;
+ struct clk_hw *parent_hw;
+ struct clk_init_data init = { };
+ struct clk *expected_parent, *actual_parent;
+ static const struct of_device_id match_table[] = {
+ { .compatible = "test,clk-parent-data" },
+ { }
+ };
+
+ dev = kunit_of_platform_driver_dev(test, match_table);
+
+ expected_parent = clk_get_kunit(test, dev, "50");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_parent);
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ test_param = test->param_value;
+ init.parent_data = &test_param->pdata;
+ init.num_parents = 1;
+ init.name = "parent_data_device_test_clk";
+ init.ops = &clk_dummy_single_parent_ops;
+ hw->init = &init;
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw));
+
+ parent_hw = clk_hw_get_parent(hw);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
+
+ actual_parent = clk_hw_get_clk_kunit(test, parent_hw, __func__);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, actual_parent);
+
+ KUNIT_EXPECT_TRUE(test, clk_is_match(expected_parent, actual_parent));
+}
+
+static const struct clk_register_clk_parent_data_test_case
+clk_register_clk_parent_data_device_hw_cases[] = {
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw.
+ */
+ .desc = "clk_parent_data_device_hw_index_test",
+ /* The index must be negative to indicate firmware not used */
+ .pdata.index = -1,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw when
+ * struct clk_parent_data::fw_name is set.
+ */
+ .desc = "clk_parent_data_device_hw_fwname_test",
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT2,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw when struct
+ * clk_parent_data::name is set.
+ */
+ .desc = "clk_parent_data_device_hw_name_test",
+ /* The index must be negative to indicate firmware not used */
+ .pdata.index = -1,
+ .pdata.name = CLK_PARENT_DATA_50MHZ_NAME,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw when struct
+ * clk_parent_data::{fw_name,name} are set.
+ */
+ .desc = "clk_parent_data_device_hw_fwname_name_test",
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT2,
+ .pdata.name = "not_matching",
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw when struct
+ * clk_parent_data::index is set. The hw pointer takes
+ * priority.
+ */
+ .desc = "clk_parent_data_device_hw_index_priority_test",
+ .pdata.index = 0,
+ },
+ {
+ /*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw when
+ * struct clk_parent_data::{index,fwname,name} are set.
+ * The hw pointer takes priority over everything else.
+ */
+ .desc = "clk_parent_data_device_hw_index_fwname_name_priority_test",
+ .pdata.index = 0,
+ .pdata.fw_name = CLK_PARENT_DATA_PARENT2,
+ .pdata.name = "not_matching",
+ },
+};
+
+KUNIT_ARRAY_PARAM(clk_register_clk_parent_data_device_hw_test,
+ clk_register_clk_parent_data_device_hw_cases,
+ clk_register_clk_parent_data_test_case_to_desc)
+
+/*
+ * Test that a clk registered with a struct device can find a
+ * parent based on struct clk_parent_data::hw.
+ */
+static void clk_register_clk_parent_data_device_hw_test(struct kunit *test)
+{
+ struct device *dev;
+ struct clk_hw *hw;
+ const struct clk_register_clk_parent_data_test_case *test_param;
+ struct clk_dummy_context *parent;
+ struct clk_hw *parent_hw;
+ struct clk_parent_data pdata = { };
+ struct clk_init_data init = { };
+ static const struct of_device_id match_table[] = {
+ { .compatible = "test,clk-parent-data" },
+ { }
+ };
+
+ dev = kunit_of_platform_driver_dev(test, match_table);
+
+ parent = kunit_kzalloc(test, sizeof(*parent), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
+
+ parent_hw = &parent->hw;
+ parent_hw->init = CLK_HW_INIT_NO_PARENT("parent-clk",
+ &clk_dummy_rate_ops, 0);
+
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, parent_hw));
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ test_param = test->param_value;
+ memcpy(&pdata, &test_param->pdata, sizeof(pdata));
+ pdata.hw = parent_hw;
+ init.parent_data = &pdata;
+ init.num_parents = 1;
+ init.ops = &clk_dummy_single_parent_ops;
+ init.name = "parent_data_device_hw_test_clk";
+ hw->init = &init;
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw));
+
+ KUNIT_EXPECT_PTR_EQ(test, parent_hw, clk_hw_get_parent(hw));
+}
+
+static struct kunit_case clk_register_clk_parent_data_device_test_cases[] = {
+ KUNIT_CASE_PARAM(clk_register_clk_parent_data_device_test,
+ clk_register_clk_parent_data_device_test_gen_params),
+ KUNIT_CASE_PARAM(clk_register_clk_parent_data_device_hw_test,
+ clk_register_clk_parent_data_device_hw_test_gen_params),
+ {}
+};
+
+static int clk_register_clk_parent_data_device_init(struct kunit *test)
+{
+ KUNIT_ASSERT_EQ(test, 0,
+ of_overlay_apply_kunit(test, kunit_clk_parent_data_test));
+
+ return 0;
+}
+
+/*
+ * Test suite for registering clks with struct clk_parent_data and a struct
+ * device.
+ */
+static struct kunit_suite clk_register_clk_parent_data_device_suite = {
+ .name = "clk_register_clk_parent_data_device",
+ .init = clk_register_clk_parent_data_device_init,
+ .test_cases = clk_register_clk_parent_data_device_test_cases,
+};
+
+struct clk_assigned_rates_context {
+ struct clk_dummy_context clk0;
+ struct clk_dummy_context clk1;
+};
+
+/*
+ * struct clk_assigned_rates_test_param - Test parameters for clk_assigned_rates test
+ * @desc: Test description
+ * @overlay_begin: Pointer to start of DT overlay to apply for test
+ * @overlay_end: Pointer to end of DT overlay to apply for test
+ * @rate0: Initial rate of first clk
+ * @rate1: Initial rate of second clk
+ * @consumer_test: true if a consumer is being tested
+ */
+struct clk_assigned_rates_test_param {
+ const char *desc;
+ u8 *overlay_begin;
+ u8 *overlay_end;
+ unsigned long rate0;
+ unsigned long rate1;
+ bool consumer_test;
+};
+
+#define TEST_PARAM_OVERLAY(overlay_name) \
+ .overlay_begin = of_overlay_begin(overlay_name), \
+ .overlay_end = of_overlay_end(overlay_name)
+
+static void
+clk_assigned_rates_register_clk(struct kunit *test,
+ struct clk_dummy_context *ctx,
+ struct device_node *np, const char *name,
+ unsigned long rate)
+{
+ struct clk_init_data init = { };
+
+ init.name = name;
+ init.ops = &clk_dummy_rate_ops;
+ ctx->hw.init = &init;
+ ctx->rate = rate;
+
+ KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, np, &ctx->hw));
+ KUNIT_ASSERT_EQ(test, ctx->rate, rate);
+}
+
+/*
+ * Does most of the work of the test:
+ *
+ * 1. Apply the overlay to test
+ * 2. Register the clk or clks to test
+ * 3. Register the clk provider
+ * 4. Apply clk defaults to the consumer device if this is a consumer test
+ *
+ * The tests will set different test_param values to test different scenarios
+ * and validate that in their test functions.
+ */
+static int clk_assigned_rates_test_init(struct kunit *test)
+{
+ struct device_node *np, *consumer;
+ struct clk_hw_onecell_data *data;
+ struct clk_assigned_rates_context *ctx;
+ u32 clk_cells;
+ const struct clk_assigned_rates_test_param *test_param;
+
+ test_param = test->param_value;
+
+ KUNIT_ASSERT_EQ(test, 0, __of_overlay_apply_kunit(test,
+ test_param->overlay_begin,
+ test_param->overlay_end));
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test,
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL));
+ test->priv = ctx;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test,
+ np = of_find_compatible_node(NULL, NULL, "test,clk-assigned-rates"));
+ of_node_put_kunit(test, np);
+
+ KUNIT_ASSERT_EQ(test, 0, of_property_read_u32(np, "#clock-cells", &clk_cells));
+ /* Only support #clock-cells = <0> or <1> */
+ KUNIT_ASSERT_LT(test, clk_cells, 2);
+
+ clk_assigned_rates_register_clk(test, &ctx->clk0, np,
+ "test_assigned_rate0", test_param->rate0);
+ if (clk_cells == 0) {
+ KUNIT_ASSERT_EQ(test, 0,
+ of_clk_add_hw_provider_kunit(test, np, of_clk_hw_simple_get,
+ &ctx->clk0.hw));
+ } else if (clk_cells == 1) {
+ clk_assigned_rates_register_clk(test, &ctx->clk1, np,
+ "test_assigned_rate1", test_param->rate1);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test,
+ data = kunit_kzalloc(test, struct_size(data, hws, 2), GFP_KERNEL));
+ data->num = 2;
+ data->hws[0] = &ctx->clk0.hw;
+ data->hws[1] = &ctx->clk1.hw;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ of_clk_add_hw_provider_kunit(test, np, of_clk_hw_onecell_get, data));
+ }
+
+ /* Consumers are optional */
+ if (test_param->consumer_test) {
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test,
+ consumer = of_find_compatible_node(NULL, NULL, "test,clk-consumer"));
+ of_node_put_kunit(test, consumer);
+
+ KUNIT_ASSERT_EQ(test, 0, of_clk_set_defaults(consumer, false));
+ }
+
+ return 0;
+}
+
+static void clk_assigned_rates_assigns_one(struct kunit *test)
+{
+ struct clk_assigned_rates_context *ctx = test->priv;
+
+ KUNIT_EXPECT_EQ(test, ctx->clk0.rate, ASSIGNED_RATES_0_RATE);
+}
+
+static void clk_assigned_rates_assigns_multiple(struct kunit *test)
+{
+ struct clk_assigned_rates_context *ctx = test->priv;
+
+ KUNIT_EXPECT_EQ(test, ctx->clk0.rate, ASSIGNED_RATES_0_RATE);
+ KUNIT_EXPECT_EQ(test, ctx->clk1.rate, ASSIGNED_RATES_1_RATE);
+}
+
+static void clk_assigned_rates_skips(struct kunit *test)
+{
+ struct clk_assigned_rates_context *ctx = test->priv;
+ const struct clk_assigned_rates_test_param *test_param = test->param_value;
+
+ KUNIT_EXPECT_NE(test, ctx->clk0.rate, ASSIGNED_RATES_0_RATE);
+ KUNIT_EXPECT_EQ(test, ctx->clk0.rate, test_param->rate0);
+}
+
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_one);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_one_consumer);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_u64_one);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_u64_one_consumer);
+
+/* Test cases that assign one rate */
+static const struct clk_assigned_rates_test_param clk_assigned_rates_assigns_one_test_params[] = {
+ {
+ /*
+ * Test that a single cell assigned-clock-rates property
+ * assigns the rate when the property is in the provider.
+ */
+ .desc = "provider assigns",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_one),
+ },
+ {
+ /*
+ * Test that a single cell assigned-clock-rates property
+ * assigns the rate when the property is in the consumer.
+ */
+ .desc = "consumer assigns",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_one_consumer),
+ .consumer_test = true,
+ },
+ {
+ /*
+ * Test that a single cell assigned-clock-rates-u64 property
+ * assigns the rate when the property is in the provider.
+ */
+ .desc = "provider assigns u64",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_u64_one),
+ },
+ {
+ /*
+ * Test that a single cell assigned-clock-rates-u64 property
+ * assigns the rate when the property is in the consumer.
+ */
+ .desc = "consumer assigns u64",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_u64_one_consumer),
+ .consumer_test = true,
+ },
+};
+KUNIT_ARRAY_PARAM_DESC(clk_assigned_rates_assigns_one,
+ clk_assigned_rates_assigns_one_test_params, desc)
+
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_multiple);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_multiple_consumer);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_u64_multiple);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_u64_multiple_consumer);
+
+/* Test cases that assign multiple rates */
+static const struct clk_assigned_rates_test_param clk_assigned_rates_assigns_multiple_test_params[] = {
+ {
+ /*
+ * Test that a multiple cell assigned-clock-rates property
+ * assigns the rates when the property is in the provider.
+ */
+ .desc = "provider assigns",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_multiple),
+ },
+ {
+ /*
+ * Test that a multiple cell assigned-clock-rates property
+ * assigns the rates when the property is in the consumer.
+ */
+ .desc = "consumer assigns",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_multiple_consumer),
+ .consumer_test = true,
+ },
+ {
+ /*
+ * Test that a single cell assigned-clock-rates-u64 property
+ * assigns the rate when the property is in the provider.
+ */
+ .desc = "provider assigns u64",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_u64_multiple),
+ },
+ {
+ /*
+ * Test that a multiple cell assigned-clock-rates-u64 property
+ * assigns the rates when the property is in the consumer.
+ */
+ .desc = "consumer assigns u64",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_u64_multiple_consumer),
+ .consumer_test = true,
+ },
+};
+KUNIT_ARRAY_PARAM_DESC(clk_assigned_rates_assigns_multiple,
+ clk_assigned_rates_assigns_multiple_test_params,
+ desc)
+
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_without);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_without_consumer);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_zero);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_zero_consumer);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_null);
+OF_OVERLAY_DECLARE(kunit_clk_assigned_rates_null_consumer);
+
+/* Test cases that skip changing the rate due to malformed DT */
+static const struct clk_assigned_rates_test_param clk_assigned_rates_skips_test_params[] = {
+ {
+ /*
+ * Test that an assigned-clock-rates property without an assigned-clocks
+ * property fails when the property is in the provider.
+ */
+ .desc = "provider missing assigned-clocks",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_without),
+ .rate0 = 3000,
+ },
+ {
+ /*
+ * Test that an assigned-clock-rates property without an assigned-clocks
+ * property fails when the property is in the consumer.
+ */
+ .desc = "consumer missing assigned-clocks",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_without_consumer),
+ .rate0 = 3000,
+ .consumer_test = true,
+ },
+ {
+ /*
+ * Test that an assigned-clock-rates property of zero doesn't
+ * set a rate when the property is in the provider.
+ */
+ .desc = "provider assigned-clock-rates of zero",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_zero),
+ .rate0 = 3000,
+ },
+ {
+ /*
+ * Test that an assigned-clock-rates property of zero doesn't
+ * set a rate when the property is in the consumer.
+ */
+ .desc = "consumer assigned-clock-rates of zero",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_zero_consumer),
+ .rate0 = 3000,
+ .consumer_test = true,
+ },
+ {
+ /*
+ * Test that an assigned-clocks property with a null phandle
+ * doesn't set a rate when the property is in the provider.
+ */
+ .desc = "provider assigned-clocks null phandle",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_null),
+ .rate0 = 3000,
+ },
+ {
+ /*
+ * Test that an assigned-clocks property with a null phandle
+ * doesn't set a rate when the property is in the consumer.
+ */
+ .desc = "provider assigned-clocks null phandle",
+ TEST_PARAM_OVERLAY(kunit_clk_assigned_rates_null_consumer),
+ .rate0 = 3000,
+ .consumer_test = true,
+ },
+};
+KUNIT_ARRAY_PARAM_DESC(clk_assigned_rates_skips,
+ clk_assigned_rates_skips_test_params,
+ desc)
+
+static struct kunit_case clk_assigned_rates_test_cases[] = {
+ KUNIT_CASE_PARAM(clk_assigned_rates_assigns_one,
+ clk_assigned_rates_assigns_one_gen_params),
+ KUNIT_CASE_PARAM(clk_assigned_rates_assigns_multiple,
+ clk_assigned_rates_assigns_multiple_gen_params),
+ KUNIT_CASE_PARAM(clk_assigned_rates_skips,
+ clk_assigned_rates_skips_gen_params),
+ {}
+};
+
+/*
+ * Test suite for assigned-clock-rates{-u64} DT property.
+ */
+static struct kunit_suite clk_assigned_rates_suite = {
+ .name = "clk_assigned_rates",
+ .test_cases = clk_assigned_rates_test_cases,
+ .init = clk_assigned_rates_test_init,
+};
+
+static const struct clk_init_data clk_hw_get_dev_of_node_init_data = {
+ .name = "clk_hw_get_dev_of_node",
+ .ops = &empty_clk_ops,
+};
+
+/*
+ * Test that a clk registered with a struct device returns the device from
+ * clk_hw_get_dev() and the node from clk_hw_get_of_node()
+ */
+static void clk_hw_register_dev_get_dev_returns_dev(struct kunit *test)
+{
+ struct device *dev;
+ struct clk_hw *hw;
+ static const struct of_device_id match_table[] = {
+ { .compatible = "test,clk-hw-get-dev-of-node" },
+ { }
+ };
+
+ KUNIT_ASSERT_EQ(test, 0, of_overlay_apply_kunit(test, kunit_clk_hw_get_dev_of_node));
+
+ dev = kunit_of_platform_driver_dev(test, match_table);
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ hw->init = &clk_hw_get_dev_of_node_init_data;
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw));
+
+ KUNIT_EXPECT_PTR_EQ(test, dev, clk_hw_get_dev(hw));
+ KUNIT_EXPECT_PTR_EQ(test, dev_of_node(dev), clk_hw_get_of_node(hw));
+}
+
+/*
+ * Test that a clk registered with a struct device that's not associated with
+ * an OF node returns the device from clk_hw_get_dev() and NULL from
+ * clk_hw_get_of_node()
+ */
+static void clk_hw_register_dev_no_node_get_dev_returns_dev(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct device *dev;
+ struct clk_hw *hw;
+
+ pdev = kunit_platform_device_alloc(test, "clk_hw_register_dev_no_node", -1);
+ KUNIT_ASSERT_NOT_NULL(test, pdev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
+ dev = &pdev->dev;
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ hw->init = &clk_hw_get_dev_of_node_init_data;
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, dev, hw));
+
+ KUNIT_EXPECT_PTR_EQ(test, dev, clk_hw_get_dev(hw));
+ KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_of_node(hw));
+}
+
+/*
+ * Test that a clk registered without a struct device returns NULL from
+ * clk_hw_get_dev()
+ */
+static void clk_hw_register_NULL_get_dev_of_node_returns_NULL(struct kunit *test)
+{
+ struct clk_hw *hw;
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ hw->init = &clk_hw_get_dev_of_node_init_data;
+
+ KUNIT_ASSERT_EQ(test, 0, clk_hw_register_kunit(test, NULL, hw));
+
+ KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_dev(hw));
+ KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_of_node(hw));
+}
+
+/*
+ * Test that a clk registered with an of_node returns the node from
+ * clk_hw_get_of_node() and NULL from clk_hw_get_dev()
+ */
+static void of_clk_hw_register_node_get_of_node_returns_node(struct kunit *test)
+{
+ struct device_node *np;
+ struct clk_hw *hw;
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ KUNIT_ASSERT_EQ(test, 0, of_overlay_apply_kunit(test, kunit_clk_hw_get_dev_of_node));
+
+ np = of_find_compatible_node(NULL, NULL, "test,clk-hw-get-dev-of-node");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np);
+ of_node_put_kunit(test, np);
+
+ hw->init = &clk_hw_get_dev_of_node_init_data;
+ KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, np, hw));
+
+ KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_dev(hw));
+ KUNIT_EXPECT_PTR_EQ(test, np, clk_hw_get_of_node(hw));
+}
+
+/*
+ * Test that a clk registered without an of_node returns the node from
+ * clk_hw_get_of_node() and clk_hw_get_dev()
+ */
+static void of_clk_hw_register_NULL_get_of_node_returns_NULL(struct kunit *test)
+{
+ struct clk_hw *hw;
+
+ hw = kunit_kzalloc(test, sizeof(*hw), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hw);
+
+ hw->init = &clk_hw_get_dev_of_node_init_data;
+ KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, NULL, hw));
+
+ KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_dev(hw));
+ KUNIT_EXPECT_PTR_EQ(test, NULL, clk_hw_get_of_node(hw));
+}
+
+static struct kunit_case clk_hw_get_dev_of_node_test_cases[] = {
+ KUNIT_CASE(clk_hw_register_dev_get_dev_returns_dev),
+ KUNIT_CASE(clk_hw_register_dev_no_node_get_dev_returns_dev),
+ KUNIT_CASE(clk_hw_register_NULL_get_dev_of_node_returns_NULL),
+ KUNIT_CASE(of_clk_hw_register_node_get_of_node_returns_node),
+ KUNIT_CASE(of_clk_hw_register_NULL_get_of_node_returns_NULL),
+ {}
+};
+
+/*
+ * Test suite to verify clk_hw_get_dev() and clk_hw_get_of_node() when clk
+ * registered with clk_hw_register() and of_clk_hw_register()
+ */
+static struct kunit_suite clk_hw_get_dev_of_node_test_suite = {
+ .name = "clk_hw_get_dev_of_node_test_suite",
+ .test_cases = clk_hw_get_dev_of_node_test_cases,
+};
+
+
kunit_test_suites(
+ &clk_assigned_rates_suite,
+ &clk_hw_get_dev_of_node_test_suite,
+ &clk_leaf_mux_set_rate_parent_test_suite,
&clk_test_suite,
+ &clk_multiple_parents_mux_test_suite,
+ &clk_mux_no_reparent_test_suite,
+ &clk_mux_notifier_test_suite,
+ &clk_orphan_transparent_multiple_parent_mux_test_suite,
&clk_orphan_transparent_single_parent_test_suite,
+ &clk_orphan_two_level_root_last_test_suite,
&clk_range_test_suite,
&clk_range_maximize_test_suite,
- &clk_range_minimize_test_suite
+ &clk_range_minimize_test_suite,
+ &clk_register_clk_parent_data_of_suite,
+ &clk_register_clk_parent_data_device_suite,
+ &clk_single_parent_mux_test_suite,
+ &clk_uncached_test_suite,
);
+MODULE_DESCRIPTION("Kunit tests for clk framework");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 67f601a41023..e0bede6350e1 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -144,7 +144,7 @@ void clkdev_add_table(struct clk_lookup *cl, size_t num)
mutex_unlock(&clocks_mutex);
}
-#define MAX_DEV_ID 20
+#define MAX_DEV_ID 24
#define MAX_CON_ID 16
struct clk_lookup_alloc {
@@ -153,31 +153,69 @@ struct clk_lookup_alloc {
char con_id[MAX_CON_ID];
};
-static struct clk_lookup * __ref
+static __printf(3, 0) struct clk_lookup * __ref
vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup_alloc *cla;
+ struct va_format vaf;
+ const char *failure;
+ va_list ap_copy;
+ size_t max_size;
+ ssize_t res;
cla = kzalloc(sizeof(*cla), GFP_KERNEL);
if (!cla)
return NULL;
+ va_copy(ap_copy, ap);
+
cla->cl.clk_hw = hw;
if (con_id) {
- strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
+ res = strscpy(cla->con_id, con_id, sizeof(cla->con_id));
+ if (res < 0) {
+ max_size = sizeof(cla->con_id);
+ failure = "connection";
+ goto fail;
+ }
cla->cl.con_id = cla->con_id;
}
if (dev_fmt) {
- vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
+ res = vsnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
+ if (res >= sizeof(cla->dev_id)) {
+ max_size = sizeof(cla->dev_id);
+ failure = "device";
+ goto fail;
+ }
cla->cl.dev_id = cla->dev_id;
}
+ va_end(ap_copy);
+
+ return &cla->cl;
+
+fail:
+ if (dev_fmt)
+ vaf.fmt = dev_fmt;
+ else
+ vaf.fmt = "null-device";
+ vaf.va = &ap_copy;
+ pr_err("%pV:%s: %s ID is greater than %zu\n",
+ &vaf, con_id, failure, max_size);
+ va_end(ap_copy);
+
+ /*
+ * Don't fail in this case, but as the entry won't ever match just
+ * fill it with something that also won't match.
+ */
+ strscpy(cla->con_id, "bad", sizeof(cla->con_id));
+ strscpy(cla->dev_id, "bad", sizeof(cla->dev_id));
+
return &cla->cl;
}
-static struct clk_lookup *
+static __printf(3, 0) struct clk_lookup *
vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
@@ -265,9 +303,8 @@ void clkdev_drop(struct clk_lookup *cl)
}
EXPORT_SYMBOL(clkdev_drop);
-static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
- const char *con_id,
- const char *dev_id, ...)
+static __printf(3, 4) struct clk_lookup *
+__clk_register_clkdev(struct clk_hw *hw, const char *con_id, const char *dev_id, ...)
{
struct clk_lookup *cl;
va_list ap;
@@ -346,44 +383,10 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
}
EXPORT_SYMBOL(clk_hw_register_clkdev);
-static void devm_clkdev_release(struct device *dev, void *res)
-{
- clkdev_drop(*(struct clk_lookup **)res);
-}
-
-static int devm_clk_match_clkdev(struct device *dev, void *res, void *data)
-{
- struct clk_lookup **l = res;
-
- return *l == data;
-}
-
-/**
- * devm_clk_release_clkdev - Resource managed clkdev lookup release
- * @dev: device this lookup is bound
- * @con_id: connection ID string on device
- * @dev_id: format string describing device name
- *
- * Drop the clkdev lookup created with devm_clk_hw_register_clkdev.
- * Normally this function will not need to be called and the resource
- * management code will ensure that the resource is freed.
- */
-void devm_clk_release_clkdev(struct device *dev, const char *con_id,
- const char *dev_id)
+static void devm_clkdev_release(void *res)
{
- struct clk_lookup *cl;
- int rval;
-
- mutex_lock(&clocks_mutex);
- cl = clk_find(dev_id, con_id);
- mutex_unlock(&clocks_mutex);
-
- WARN_ON(!cl);
- rval = devres_release(dev, devm_clkdev_release,
- devm_clk_match_clkdev, cl);
- WARN_ON(rval);
+ clkdev_drop(res);
}
-EXPORT_SYMBOL(devm_clk_release_clkdev);
/**
* devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw
@@ -403,17 +406,13 @@ EXPORT_SYMBOL(devm_clk_release_clkdev);
int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw,
const char *con_id, const char *dev_id)
{
- int rval = -ENOMEM;
- struct clk_lookup **cl;
-
- cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL);
- if (cl) {
- rval = do_clk_register_clkdev(hw, cl, con_id, dev_id);
- if (!rval)
- devres_add(dev, cl);
- else
- devres_free(cl);
- }
- return rval;
+ struct clk_lookup *cl;
+ int rval;
+
+ rval = do_clk_register_clkdev(hw, &cl, con_id, dev_id);
+ if (rval)
+ return rval;
+
+ return devm_add_action_or_reset(dev, devm_clkdev_release, cl);
}
EXPORT_SYMBOL(devm_clk_hw_register_clkdev);
diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
index 11178b79b483..f9d5c9a392e4 100644
--- a/drivers/clk/davinci/Makefile
+++ b/drivers/clk/davinci/Makefile
@@ -4,18 +4,8 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx-cfgchip.o
obj-y += pll.o
-obj-$(CONFIG_ARCH_DAVINCI_DA830) += pll-da830.o
obj-$(CONFIG_ARCH_DAVINCI_DA850) += pll-da850.o
-obj-$(CONFIG_ARCH_DAVINCI_DM355) += pll-dm355.o
-obj-$(CONFIG_ARCH_DAVINCI_DM365) += pll-dm365.o
-obj-$(CONFIG_ARCH_DAVINCI_DM644x) += pll-dm644x.o
-obj-$(CONFIG_ARCH_DAVINCI_DM646x) += pll-dm646x.o
obj-y += psc.o
-obj-$(CONFIG_ARCH_DAVINCI_DA830) += psc-da830.o
obj-$(CONFIG_ARCH_DAVINCI_DA850) += psc-da850.o
-obj-$(CONFIG_ARCH_DAVINCI_DM355) += psc-dm355.o
-obj-$(CONFIG_ARCH_DAVINCI_DM365) += psc-dm365.o
-obj-$(CONFIG_ARCH_DAVINCI_DM644x) += psc-dm644x.o
-obj-$(CONFIG_ARCH_DAVINCI_DM646x) += psc-dm646x.o
endif
diff --git a/drivers/clk/davinci/da8xx-cfgchip.c b/drivers/clk/davinci/da8xx-cfgchip.c
index 77d18276bfe8..a5109fe8b16e 100644
--- a/drivers/clk/davinci/da8xx-cfgchip.c
+++ b/drivers/clk/davinci/da8xx-cfgchip.c
@@ -11,10 +11,10 @@
#include <linux/init.h>
#include <linux/mfd/da8xx-cfgchip.h>
#include <linux/mfd/syscon.h>
-#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_data/clk-da8xx-cfgchip.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -229,6 +229,7 @@ static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw)
}
static const struct clk_ops da8xx_cfgchip_mux_clk_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = da8xx_cfgchip_mux_clk_set_parent,
.get_parent = da8xx_cfgchip_mux_clk_get_parent,
};
@@ -461,10 +462,12 @@ static unsigned long da8xx_usb0_clk48_recalc_rate(struct clk_hw *hw,
return 48000000;
}
-static long da8xx_usb0_clk48_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int da8xx_usb0_clk48_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- return 48000000;
+ req->rate = 48000000;
+
+ return 0;
}
static int da8xx_usb0_clk48_set_parent(struct clk_hw *hw, u8 index)
@@ -493,7 +496,7 @@ static const struct clk_ops da8xx_usb0_clk48_ops = {
.disable = da8xx_usb0_clk48_disable,
.is_enabled = da8xx_usb0_clk48_is_enabled,
.recalc_rate = da8xx_usb0_clk48_recalc_rate,
- .round_rate = da8xx_usb0_clk48_round_rate,
+ .determine_rate = da8xx_usb0_clk48_determine_rate,
.set_parent = da8xx_usb0_clk48_set_parent,
.get_parent = da8xx_usb0_clk48_get_parent,
};
@@ -505,14 +508,12 @@ da8xx_cfgchip_register_usb0_clk48(struct device *dev,
const char * const parent_names[] = { "usb_refclkin", "pll0_auxclk" };
struct clk *fck_clk;
struct da8xx_usb0_clk48 *usb0;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
fck_clk = devm_clk_get(dev, "fck");
if (IS_ERR(fck_clk)) {
- if (PTR_ERR(fck_clk) != -EPROBE_DEFER)
- dev_err(dev, "Missing fck clock\n");
- return ERR_CAST(fck_clk);
+ return dev_err_cast_probe(dev, fck_clk, "Missing fck clock\n");
}
usb0 = devm_kzalloc(dev, sizeof(*usb0), GFP_KERNEL);
@@ -565,6 +566,7 @@ static u8 da8xx_usb1_clk48_get_parent(struct clk_hw *hw)
}
static const struct clk_ops da8xx_usb1_clk48_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = da8xx_usb1_clk48_set_parent,
.get_parent = da8xx_usb1_clk48_get_parent,
};
@@ -580,7 +582,7 @@ da8xx_cfgchip_register_usb1_clk48(struct device *dev,
{
const char * const parent_names[] = { "usb0_clk48", "usb_refclkin" };
struct da8xx_usb1_clk48 *usb1;
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
usb1 = devm_kzalloc(dev, sizeof(*usb1), GFP_KERNEL);
@@ -741,18 +743,14 @@ static int da8xx_cfgchip_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct da8xx_cfgchip_clk_platform_data *pdata = dev->platform_data;
- const struct of_device_id *of_id;
da8xx_cfgchip_init clk_init = NULL;
struct regmap *regmap = NULL;
- of_id = of_match_device(da8xx_cfgchip_of_match, dev);
- if (of_id) {
- struct device_node *parent;
+ clk_init = device_get_match_data(dev);
+ if (clk_init) {
+ struct device_node *parent __free(device_node) = of_get_parent(dev->of_node);
- clk_init = of_id->data;
- parent = of_get_parent(dev->of_node);
regmap = syscon_node_to_regmap(parent);
- of_node_put(parent);
} else if (pdev->id_entry && pdata) {
clk_init = (void *)pdev->id_entry->driver_data;
regmap = pdata->cfgchip;
diff --git a/drivers/clk/davinci/pll-da830.c b/drivers/clk/davinci/pll-da830.c
deleted file mode 100644
index 0a0d06fb25fd..000000000000
--- a/drivers/clk/davinci/pll-da830.c
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PLL clock descriptions for TI DA830/OMAP-L137/AM17XX
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clkdev.h>
-#include <linux/clk/davinci.h>
-#include <linux/bitops.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include "pll.h"
-
-static const struct davinci_pll_clk_info da830_pll_info = {
- .name = "pll0",
- .pllm_mask = GENMASK(4, 0),
- .pllm_min = 4,
- .pllm_max = 32,
- .pllout_min_rate = 300000000,
- .pllout_max_rate = 600000000,
- .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV,
-};
-
-/*
- * NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio",
- * meaning that we could change the divider as long as we keep the correct
- * ratio between all of the clocks, but we don't support that because there is
- * currently not a need for it.
- */
-
-SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV);
-SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0);
-SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV);
-SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0);
-SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_FIXED_DIV);
-SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0);
-
-int da830_pll_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- struct clk *clk;
-
- davinci_pll_clk_register(dev, &da830_pll_info, "ref_clk", base, cfgchip);
-
- clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base);
- clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc0");
- clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc1");
-
- clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base);
- clk_register_clkdev(clk, "pll0_sysclk3", "da830-psc0");
-
- clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base);
- clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc0");
- clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc1");
-
- clk = davinci_pll_sysclk_register(dev, &pll0_sysclk5, base);
- clk_register_clkdev(clk, "pll0_sysclk5", "da830-psc1");
-
- clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base);
- clk_register_clkdev(clk, "pll0_sysclk6", "da830-psc0");
-
- clk = davinci_pll_sysclk_register(dev, &pll0_sysclk7, base);
-
- clk = davinci_pll_auxclk_register(dev, "pll0_auxclk", base);
- clk_register_clkdev(clk, NULL, "i2c_davinci.1");
- clk_register_clkdev(clk, "timer0", NULL);
- clk_register_clkdev(clk, NULL, "davinci-wdt");
-
- return 0;
-}
diff --git a/drivers/clk/davinci/pll-dm355.c b/drivers/clk/davinci/pll-dm355.c
deleted file mode 100644
index 505aed80be9a..000000000000
--- a/drivers/clk/davinci/pll-dm355.c
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PLL clock descriptions for TI DM355
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/bitops.h>
-#include <linux/clk/davinci.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include "pll.h"
-
-static const struct davinci_pll_clk_info dm355_pll1_info = {
- .name = "pll1",
- .pllm_mask = GENMASK(7, 0),
- .pllm_min = 92,
- .pllm_max = 184,
- .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED |
- PLL_PREDIV_FIXED8 | PLL_HAS_POSTDIV |
- PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV,
-};
-
-SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
-SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
-SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(4, pll1_sysclk4, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-
-int dm355_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- struct clk *clk;
-
- davinci_pll_clk_register(dev, &dm355_pll1_info, "ref_clk", base, cfgchip);
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
- clk_register_clkdev(clk, "pll1_sysclk1", "dm355-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
- clk_register_clkdev(clk, "pll1_sysclk2", "dm355-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
- clk_register_clkdev(clk, "pll1_sysclk3", "dm355-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
- clk_register_clkdev(clk, "pll1_sysclk4", "dm355-psc");
-
- clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
- clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc");
-
- davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
-
- return 0;
-}
-
-static const struct davinci_pll_clk_info dm355_pll2_info = {
- .name = "pll2",
- .pllm_mask = GENMASK(7, 0),
- .pllm_min = 92,
- .pllm_max = 184,
- .flags = PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED | PLL_HAS_POSTDIV |
- PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV,
-};
-
-SYSCLK(1, pll2_sysclk1, pll2_pllen, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
-
-int dm355_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- davinci_pll_clk_register(dev, &dm355_pll2_info, "oscin", base, cfgchip);
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
-
- davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base);
-
- return 0;
-}
diff --git a/drivers/clk/davinci/pll-dm365.c b/drivers/clk/davinci/pll-dm365.c
deleted file mode 100644
index 2d29712753a3..000000000000
--- a/drivers/clk/davinci/pll-dm365.c
+++ /dev/null
@@ -1,146 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PLL clock descriptions for TI DM365
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/bitops.h>
-#include <linux/clkdev.h>
-#include <linux/clk/davinci.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "pll.h"
-
-#define OCSEL_OCSRC_ENABLE 0
-
-static const struct davinci_pll_clk_info dm365_pll1_info = {
- .name = "pll1",
- .pllm_mask = GENMASK(9, 0),
- .pllm_min = 1,
- .pllm_max = 1023,
- .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV |
- PLL_POSTDIV_ALWAYS_ENABLED | PLL_PLLM_2X,
-};
-
-SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(4, pll1_sysclk4, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(5, pll1_sysclk5, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(6, pll1_sysclk6, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(7, pll1_sysclk7, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(8, pll1_sysclk8, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(9, pll1_sysclk9, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-
-/*
- * This is a bit of a hack to make OCSEL[OCSRC] on DM365 look like OCSEL[OCSRC]
- * on DA850. On DM365, OCSEL[OCSRC] is just an enable/disable bit instead of a
- * multiplexer. By modeling it as a single parent mux clock, the clock code will
- * still do the right thing in this case.
- */
-static const char * const dm365_pll_obsclk_parent_names[] = {
- "oscin",
-};
-
-static u32 dm365_pll_obsclk_table[] = {
- OCSEL_OCSRC_ENABLE,
-};
-
-static const struct davinci_pll_obsclk_info dm365_pll1_obsclk_info = {
- .name = "pll1_obsclk",
- .parent_names = dm365_pll_obsclk_parent_names,
- .num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names),
- .table = dm365_pll_obsclk_table,
- .ocsrc_mask = BIT(4),
-};
-
-int dm365_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- struct clk *clk;
-
- davinci_pll_clk_register(dev, &dm365_pll1_info, "ref_clk", base, cfgchip);
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
- clk_register_clkdev(clk, "pll1_sysclk1", "dm365-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
- clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
- clk_register_clkdev(clk, "pll1_sysclk3", "dm365-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
- clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
- clk_register_clkdev(clk, "pll1_sysclk5", "dm365-psc");
-
- davinci_pll_sysclk_register(dev, &pll1_sysclk6, base);
-
- davinci_pll_sysclk_register(dev, &pll1_sysclk7, base);
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk8, base);
- clk_register_clkdev(clk, "pll1_sysclk8", "dm365-psc");
-
- davinci_pll_sysclk_register(dev, &pll1_sysclk9, base);
-
- clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
- clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc");
-
- davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
-
- davinci_pll_obsclk_register(dev, &dm365_pll1_obsclk_info, base);
-
- return 0;
-}
-
-static const struct davinci_pll_clk_info dm365_pll2_info = {
- .name = "pll2",
- .pllm_mask = GENMASK(9, 0),
- .pllm_min = 1,
- .pllm_max = 1023,
- .flags = PLL_HAS_PREDIV | PLL_HAS_POSTDIV | PLL_POSTDIV_ALWAYS_ENABLED |
- PLL_PLLM_2X,
-};
-
-SYSCLK(1, pll2_sysclk1, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(2, pll2_sysclk2, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(3, pll2_sysclk3, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(4, pll2_sysclk4, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-SYSCLK(5, pll2_sysclk5, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
-
-static const struct davinci_pll_obsclk_info dm365_pll2_obsclk_info = {
- .name = "pll2_obsclk",
- .parent_names = dm365_pll_obsclk_parent_names,
- .num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names),
- .table = dm365_pll_obsclk_table,
- .ocsrc_mask = BIT(4),
-};
-
-int dm365_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- struct clk *clk;
-
- davinci_pll_clk_register(dev, &dm365_pll2_info, "oscin", base, cfgchip);
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
-
- clk = davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
- clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc");
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk3, base);
-
- clk = davinci_pll_sysclk_register(dev, &pll2_sysclk4, base);
- clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc");
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk5, base);
-
- davinci_pll_auxclk_register(dev, "pll2_auxclk", base);
-
- davinci_pll_obsclk_register(dev, &dm365_pll2_obsclk_info, base);
-
- return 0;
-}
diff --git a/drivers/clk/davinci/pll-dm644x.c b/drivers/clk/davinci/pll-dm644x.c
deleted file mode 100644
index 7650fadfaac8..000000000000
--- a/drivers/clk/davinci/pll-dm644x.c
+++ /dev/null
@@ -1,81 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PLL clock descriptions for TI DM644X
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/bitops.h>
-#include <linux/clk/davinci.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include "pll.h"
-
-static const struct davinci_pll_clk_info dm644x_pll1_info = {
- .name = "pll1",
- .pllm_mask = GENMASK(4, 0),
- .pllm_min = 1,
- .pllm_max = 32,
- .pllout_min_rate = 400000000,
- .pllout_max_rate = 600000000, /* 810MHz @ 1.3V, -810 only */
- .flags = PLL_HAS_CLKMODE | PLL_HAS_POSTDIV,
-};
-
-SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-
-int dm644x_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- struct clk *clk;
-
- davinci_pll_clk_register(dev, &dm644x_pll1_info, "ref_clk", base, cfgchip);
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
- clk_register_clkdev(clk, "pll1_sysclk1", "dm644x-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
- clk_register_clkdev(clk, "pll1_sysclk2", "dm644x-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
- clk_register_clkdev(clk, "pll1_sysclk3", "dm644x-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
- clk_register_clkdev(clk, "pll1_sysclk5", "dm644x-psc");
-
- clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
- clk_register_clkdev(clk, "pll1_auxclk", "dm644x-psc");
-
- davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
-
- return 0;
-}
-
-static const struct davinci_pll_clk_info dm644x_pll2_info = {
- .name = "pll2",
- .pllm_mask = GENMASK(4, 0),
- .pllm_min = 1,
- .pllm_max = 32,
- .pllout_min_rate = 400000000,
- .pllout_max_rate = 900000000,
- .flags = PLL_HAS_POSTDIV | PLL_POSTDIV_FIXED_DIV,
-};
-
-SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0);
-SYSCLK(2, pll2_sysclk2, pll2_pllen, 4, 0);
-
-int dm644x_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- davinci_pll_clk_register(dev, &dm644x_pll2_info, "oscin", base, cfgchip);
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
-
- davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base);
-
- return 0;
-}
diff --git a/drivers/clk/davinci/pll-dm646x.c b/drivers/clk/davinci/pll-dm646x.c
deleted file mode 100644
index 26982970df0e..000000000000
--- a/drivers/clk/davinci/pll-dm646x.c
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PLL clock descriptions for TI DM646X
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clk/davinci.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include "pll.h"
-
-static const struct davinci_pll_clk_info dm646x_pll1_info = {
- .name = "pll1",
- .pllm_mask = GENMASK(4, 0),
- .pllm_min = 14,
- .pllm_max = 32,
- .flags = PLL_HAS_CLKMODE,
-};
-
-SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV);
-SYSCLK(4, pll1_sysclk4, pll1_pllen, 4, 0);
-SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, 0);
-SYSCLK(6, pll1_sysclk6, pll1_pllen, 4, 0);
-SYSCLK(8, pll1_sysclk8, pll1_pllen, 4, 0);
-SYSCLK(9, pll1_sysclk9, pll1_pllen, 4, 0);
-
-int dm646x_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- struct clk *clk;
-
- davinci_pll_clk_register(dev, &dm646x_pll1_info, "ref_clk", base, cfgchip);
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
- clk_register_clkdev(clk, "pll1_sysclk1", "dm646x-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
- clk_register_clkdev(clk, "pll1_sysclk2", "dm646x-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
- clk_register_clkdev(clk, "pll1_sysclk3", "dm646x-psc");
- clk_register_clkdev(clk, NULL, "davinci-wdt");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
- clk_register_clkdev(clk, "pll1_sysclk4", "dm646x-psc");
-
- clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
- clk_register_clkdev(clk, "pll1_sysclk5", "dm646x-psc");
-
- davinci_pll_sysclk_register(dev, &pll1_sysclk6, base);
-
- davinci_pll_sysclk_register(dev, &pll1_sysclk8, base);
-
- davinci_pll_sysclk_register(dev, &pll1_sysclk9, base);
-
- davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
-
- davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
-
- return 0;
-}
-
-static const struct davinci_pll_clk_info dm646x_pll2_info = {
- .name = "pll2",
- .pllm_mask = GENMASK(4, 0),
- .pllm_min = 14,
- .pllm_max = 32,
- .flags = 0,
-};
-
-SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, SYSCLK_ALWAYS_ENABLED);
-
-int dm646x_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip)
-{
- davinci_pll_clk_register(dev, &dm646x_pll2_info, "oscin", base, cfgchip);
-
- davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
-
- return 0;
-}
diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
index 0d750433eb42..bfb6bbdc036c 100644
--- a/drivers/clk/davinci/pll.c
+++ b/drivers/clk/davinci/pll.c
@@ -18,11 +18,9 @@
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/notifier.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/of.h>
-#include <linux/platform_data/clk-davinci-pll.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -98,7 +96,7 @@
* @hw: clk_hw for the pll
* @base: Base memory address
* @pllm_min: The minimum allowable PLLM[PLLM] value
- * @pllm_max: The maxiumum allowable PLLM[PLLM] value
+ * @pllm_max: The maximum allowable PLLM[PLLM] value
* @pllm_mask: Bitmask for PLLM[PLLM] value
*/
struct davinci_pll_clk {
@@ -765,13 +763,14 @@ int of_davinci_pll_init(struct device *dev, struct device_node *node,
return PTR_ERR(clk);
}
- child = of_get_child_by_name(node, "pllout");
- if (of_device_is_available(child))
+ child = of_get_available_child_by_name(node, "pllout");
+ if (child) {
of_clk_add_provider(child, of_clk_src_simple_get, clk);
- of_node_put(child);
+ of_node_put(child);
+ }
- child = of_get_child_by_name(node, "sysclk");
- if (of_device_is_available(child)) {
+ child = of_get_available_child_by_name(node, "sysclk");
+ if (child) {
struct clk_onecell_data *clk_data;
struct clk **clks;
int n_clks = max_sysclk_id + 1;
@@ -805,11 +804,11 @@ int of_davinci_pll_init(struct device *dev, struct device_node *node,
clks[(*div_info)->id] = clk;
}
of_clk_add_provider(child, of_clk_src_onecell_get, clk_data);
+ of_node_put(child);
}
- of_node_put(child);
- child = of_get_child_by_name(node, "auxclk");
- if (of_device_is_available(child)) {
+ child = of_get_available_child_by_name(node, "auxclk");
+ if (child) {
char child_name[MAX_NAME_SIZE];
snprintf(child_name, MAX_NAME_SIZE, "%s_auxclk", info->name);
@@ -820,11 +819,12 @@ int of_davinci_pll_init(struct device *dev, struct device_node *node,
child_name, PTR_ERR(clk));
else
of_clk_add_provider(child, of_clk_src_simple_get, clk);
+
+ of_node_put(child);
}
- of_node_put(child);
- child = of_get_child_by_name(node, "obsclk");
- if (of_device_is_available(child)) {
+ child = of_get_available_child_by_name(node, "obsclk");
+ if (child) {
if (obsclk_info)
clk = davinci_pll_obsclk_register(dev, obsclk_info, base);
else
@@ -835,69 +835,23 @@ int of_davinci_pll_init(struct device *dev, struct device_node *node,
PTR_ERR(clk));
else
of_clk_add_provider(child, of_clk_src_simple_get, clk);
+ of_node_put(child);
}
- of_node_put(child);
return 0;
}
-static struct davinci_pll_platform_data *davinci_pll_get_pdata(struct device *dev)
-{
- struct davinci_pll_platform_data *pdata = dev_get_platdata(dev);
-
- /*
- * Platform data is optional, so allocate a new struct if one was not
- * provided. For device tree, this will always be the case.
- */
- if (!pdata)
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
-
- /* for device tree, we need to fill in the struct */
- if (dev->of_node)
- pdata->cfgchip =
- syscon_regmap_lookup_by_compatible("ti,da830-cfgchip");
-
- return pdata;
-}
-
/* needed in early boot for clocksource/clockevent */
-#ifdef CONFIG_ARCH_DAVINCI_DA850
CLK_OF_DECLARE(da850_pll0, "ti,da850-pll0", of_da850_pll0_init);
-#endif
static const struct of_device_id davinci_pll_of_match[] = {
-#ifdef CONFIG_ARCH_DAVINCI_DA850
{ .compatible = "ti,da850-pll1", .data = of_da850_pll1_init },
-#endif
{ }
};
static const struct platform_device_id davinci_pll_id_table[] = {
-#ifdef CONFIG_ARCH_DAVINCI_DA830
- { .name = "da830-pll", .driver_data = (kernel_ulong_t)da830_pll_init },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DA850
{ .name = "da850-pll0", .driver_data = (kernel_ulong_t)da850_pll0_init },
{ .name = "da850-pll1", .driver_data = (kernel_ulong_t)da850_pll1_init },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM355
- { .name = "dm355-pll1", .driver_data = (kernel_ulong_t)dm355_pll1_init },
- { .name = "dm355-pll2", .driver_data = (kernel_ulong_t)dm355_pll2_init },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM365
- { .name = "dm365-pll1", .driver_data = (kernel_ulong_t)dm365_pll1_init },
- { .name = "dm365-pll2", .driver_data = (kernel_ulong_t)dm365_pll2_init },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM644x
- { .name = "dm644x-pll1", .driver_data = (kernel_ulong_t)dm644x_pll1_init },
- { .name = "dm644x-pll2", .driver_data = (kernel_ulong_t)dm644x_pll2_init },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM646x
- { .name = "dm646x-pll1", .driver_data = (kernel_ulong_t)dm646x_pll1_init },
- { .name = "dm646x-pll2", .driver_data = (kernel_ulong_t)dm646x_pll2_init },
-#endif
{ }
};
@@ -907,15 +861,12 @@ typedef int (*davinci_pll_init)(struct device *dev, void __iomem *base,
static int davinci_pll_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct davinci_pll_platform_data *pdata;
- const struct of_device_id *of_id;
davinci_pll_init pll_init = NULL;
+ struct regmap *cfgchip;
void __iomem *base;
- of_id = of_match_device(davinci_pll_of_match, dev);
- if (of_id)
- pll_init = of_id->data;
- else if (pdev->id_entry)
+ pll_init = device_get_match_data(dev);
+ if (!pll_init && pdev->id_entry)
pll_init = (void *)pdev->id_entry->driver_data;
if (!pll_init) {
@@ -923,17 +874,13 @@ static int davinci_pll_probe(struct platform_device *pdev)
return -EINVAL;
}
- pdata = davinci_pll_get_pdata(dev);
- if (!pdata) {
- dev_err(dev, "missing platform data\n");
- return -EINVAL;
- }
+ cfgchip = syscon_regmap_lookup_by_compatible("ti,da830-cfgchip");
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
- return pll_init(dev, base, pdata->cfgchip);
+ return pll_init(dev, base, cfgchip);
}
static struct platform_driver davinci_pll_driver = {
diff --git a/drivers/clk/davinci/pll.h b/drivers/clk/davinci/pll.h
index c2a453caa131..ad286ba4ce0c 100644
--- a/drivers/clk/davinci/pll.h
+++ b/drivers/clk/davinci/pll.h
@@ -80,7 +80,7 @@ static const struct davinci_pll_sysclk_info n = { \
* @name: The name of the clock
* @parent_names: Array of names of the parent clocks
* @num_parents: Length of @parent_names
- * @table: Array of values to write to OCSEL[OCSRC] cooresponding to
+ * @table: Array of values to write to OCSEL[OCSRC] corresponding to
* @parent_names
* @ocsrc_mask: Bitmask for OCSEL[OCSRC]
*/
@@ -122,19 +122,8 @@ int of_davinci_pll_init(struct device *dev, struct device_node *node,
/* Platform-specific callbacks */
-#ifdef CONFIG_ARCH_DAVINCI_DA850
int da850_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
void of_da850_pll0_init(struct device_node *node);
int of_da850_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM355
-int dm355_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM644x
-int dm644x_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM646x
-int dm646x_pll2_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
-#endif
#endif /* __CLK_DAVINCI_PLL_H___ */
diff --git a/drivers/clk/davinci/psc-da830.c b/drivers/clk/davinci/psc-da830.c
deleted file mode 100644
index 6481337382a6..000000000000
--- a/drivers/clk/davinci/psc-da830.c
+++ /dev/null
@@ -1,118 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PSC clock descriptions for TI DA830/OMAP-L137/AM17XX
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "psc.h"
-
-LPSC_CLKDEV1(aemif_clkdev, NULL, "ti-aemif");
-LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
-LPSC_CLKDEV1(mmcsd_clkdev, NULL, "da830-mmc.0");
-LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
-
-static const struct davinci_lpsc_clk_info da830_psc0_info[] = {
- LPSC(0, 0, tpcc, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(3, 0, aemif, pll0_sysclk3, aemif_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0),
- LPSC(5, 0, mmcsd, pll0_sysclk2, mmcsd_clkdev, 0),
- LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(8, 0, secu_mgr, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0),
- LPSC(10, 0, scr0_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(11, 0, scr1_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(12, 0, scr2_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(13, 0, pruss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(14, 0, arm, pll0_sysclk6, NULL, LPSC_ALWAYS_ENABLED),
- { }
-};
-
-static int da830_psc0_init(struct device *dev, void __iomem *base)
-{
- return davinci_psc_register_clocks(dev, da830_psc0_info, 16, base);
-}
-
-static struct clk_bulk_data da830_psc0_parent_clks[] = {
- { .id = "pll0_sysclk2" },
- { .id = "pll0_sysclk3" },
- { .id = "pll0_sysclk4" },
- { .id = "pll0_sysclk6" },
-};
-
-const struct davinci_psc_init_data da830_psc0_init_data = {
- .parent_clks = da830_psc0_parent_clks,
- .num_parent_clks = ARRAY_SIZE(da830_psc0_parent_clks),
- .psc_init = &da830_psc0_init,
-};
-
-LPSC_CLKDEV3(usb0_clkdev, "fck", "da830-usb-phy-clks",
- NULL, "musb-da8xx",
- NULL, "cppi41-dmaengine");
-LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx");
-/* REVISIT: gpio-davinci.c should be modified to drop con_id */
-LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
-LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
- "fck", "davinci_mdio.0");
-LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
-LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1");
-LPSC_CLKDEV1(mcasp2_clkdev, NULL, "davinci-mcasp.2");
-LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
-LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2");
-LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
-LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
-LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0");
-LPSC_CLKDEV2(pwm_clkdev, "fck", "ehrpwm.0",
- "fck", "ehrpwm.1");
-LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0",
- "fck", "ecap.1",
- "fck", "ecap.2");
-LPSC_CLKDEV2(eqep_clkdev, NULL, "eqep.0",
- NULL, "eqep.1");
-
-static const struct davinci_lpsc_clk_info da830_psc1_info[] = {
- LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0),
- LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0),
- LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0),
- LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0),
- LPSC(6, 0, emif3, pll0_sysclk5, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(7, 0, mcasp0, pll0_sysclk2, mcasp0_clkdev, 0),
- LPSC(8, 0, mcasp1, pll0_sysclk2, mcasp1_clkdev, 0),
- LPSC(9, 0, mcasp2, pll0_sysclk2, mcasp2_clkdev, 0),
- LPSC(10, 0, spi1, pll0_sysclk2, spi1_clkdev, 0),
- LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0),
- LPSC(12, 0, uart1, pll0_sysclk2, uart1_clkdev, 0),
- LPSC(13, 0, uart2, pll0_sysclk2, uart2_clkdev, 0),
- LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0),
- LPSC(17, 0, pwm, pll0_sysclk2, pwm_clkdev, 0),
- LPSC(20, 0, ecap, pll0_sysclk2, ecap_clkdev, 0),
- LPSC(21, 0, eqep, pll0_sysclk2, eqep_clkdev, 0),
- { }
-};
-
-static int da830_psc1_init(struct device *dev, void __iomem *base)
-{
- return davinci_psc_register_clocks(dev, da830_psc1_info, 32, base);
-}
-
-static struct clk_bulk_data da830_psc1_parent_clks[] = {
- { .id = "pll0_sysclk2" },
- { .id = "pll0_sysclk4" },
- { .id = "pll0_sysclk5" },
-};
-
-const struct davinci_psc_init_data da830_psc1_init_data = {
- .parent_clks = da830_psc1_parent_clks,
- .num_parent_clks = ARRAY_SIZE(da830_psc1_parent_clks),
- .psc_init = &da830_psc1_init,
-};
diff --git a/drivers/clk/davinci/psc-da850.c b/drivers/clk/davinci/psc-da850.c
index 5a18bca464cd..94081ab1e688 100644
--- a/drivers/clk/davinci/psc-da850.c
+++ b/drivers/clk/davinci/psc-da850.c
@@ -6,7 +6,6 @@
*/
#include <linux/clk-provider.h>
-#include <linux/reset-controller.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
@@ -66,14 +65,8 @@ LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0",
"fck", "ecap.1",
"fck", "ecap.2");
-static struct reset_control_lookup da850_psc0_reset_lookup_table[] = {
- RESET_LOOKUP("da850-psc0", 15, "davinci-rproc.0", NULL),
-};
-
static int da850_psc0_init(struct device *dev, void __iomem *base)
{
- reset_controller_add_lookup(da850_psc0_reset_lookup_table,
- ARRAY_SIZE(da850_psc0_reset_lookup_table));
return davinci_psc_register_clocks(dev, da850_psc0_info, 16, base);
}
diff --git a/drivers/clk/davinci/psc-dm355.c b/drivers/clk/davinci/psc-dm355.c
deleted file mode 100644
index ddd250107c4e..000000000000
--- a/drivers/clk/davinci/psc-dm355.c
+++ /dev/null
@@ -1,89 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PSC clock descriptions for TI DaVinci DM355
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clk/davinci.h>
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "psc.h"
-
-LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
-LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
-LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
-LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "dm6441-mmc.1");
-LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1");
-LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
-LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2");
-LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
-LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "dm6441-mmc.0");
-LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0");
-LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
-LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
-LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
-LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
-LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
-/* REVISIT: gpio-davinci.c should be modified to drop con_id */
-LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
-LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
-LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
-LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL);
-
-static const struct davinci_lpsc_clk_info dm355_psc_info[] = {
- LPSC(0, 0, vpss_master, pll1_sysclk4, vpss_master_clkdev, 0),
- LPSC(1, 0, vpss_slave, pll1_sysclk4, vpss_slave_clkdev, 0),
- LPSC(5, 0, timer3, pll1_auxclk, NULL, 0),
- LPSC(6, 0, spi1, pll1_sysclk2, spi1_clkdev, 0),
- LPSC(7, 0, mmcsd1, pll1_sysclk2, mmcsd1_clkdev, 0),
- LPSC(8, 0, asp1, pll1_sysclk2, mcbsp1_clkdev, 0),
- LPSC(9, 0, usb, pll1_sysclk2, usb_clkdev, 0),
- LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0),
- LPSC(11, 0, spi2, pll1_sysclk2, spi2_clkdev, 0),
- LPSC(12, 0, rto, pll1_auxclk, NULL, 0),
- LPSC(14, 0, aemif, pll1_sysclk2, aemif_clkdev, 0),
- LPSC(15, 0, mmcsd0, pll1_sysclk2, mmcsd0_clkdev, 0),
- LPSC(17, 0, asp0, pll1_sysclk2, mcbsp0_clkdev, 0),
- LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
- LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
- LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0),
- LPSC(21, 0, uart2, pll1_sysclk2, uart2_clkdev, 0),
- LPSC(22, 0, spi0, pll1_sysclk2, spi0_clkdev, 0),
- LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
- LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
- LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
- LPSC(26, 0, gpio, pll1_sysclk2, gpio_clkdev, 0),
- LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
- /* REVISIT: why can't this be disabled? */
- LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(31, 0, arm, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(40, 0, mjcp, pll1_sysclk1, NULL, 0),
- LPSC(41, 0, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0),
- { }
-};
-
-int dm355_psc_init(struct device *dev, void __iomem *base)
-{
- return davinci_psc_register_clocks(dev, dm355_psc_info, 42, base);
-}
-
-static struct clk_bulk_data dm355_psc_parent_clks[] = {
- { .id = "pll1_sysclk1" },
- { .id = "pll1_sysclk2" },
- { .id = "pll1_sysclk3" },
- { .id = "pll1_sysclk4" },
- { .id = "pll1_auxclk" },
-};
-
-const struct davinci_psc_init_data dm355_psc_init_data = {
- .parent_clks = dm355_psc_parent_clks,
- .num_parent_clks = ARRAY_SIZE(dm355_psc_parent_clks),
- .psc_init = &dm355_psc_init,
-};
diff --git a/drivers/clk/davinci/psc-dm365.c b/drivers/clk/davinci/psc-dm365.c
deleted file mode 100644
index c75424f4ea3b..000000000000
--- a/drivers/clk/davinci/psc-dm365.c
+++ /dev/null
@@ -1,111 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PSC clock descriptions for TI DaVinci DM365
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clk/davinci.h>
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "psc.h"
-
-LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
-LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
-LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1");
-LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp");
-LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
-LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2");
-LPSC_CLKDEV2(aemif_clkdev, "aemif", NULL,
- NULL, "ti-aemif");
-LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0");
-LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
-LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
-LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
-LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
-/* REVISIT: gpio-davinci.c should be modified to drop con_id */
-LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
-LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
-LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
-LPSC_CLKDEV1(spi3_clkdev, NULL, "spi_davinci.3");
-LPSC_CLKDEV1(spi4_clkdev, NULL, "spi_davinci.4");
-LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
- "fck", "davinci_mdio.0");
-LPSC_CLKDEV1(voice_codec_clkdev, NULL, "davinci_voicecodec");
-LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL);
-LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
-
-static const struct davinci_lpsc_clk_info dm365_psc_info[] = {
- LPSC(1, 0, vpss_slave, pll1_sysclk5, vpss_slave_clkdev, 0),
- LPSC(5, 0, timer3, pll1_auxclk, NULL, 0),
- LPSC(6, 0, spi1, pll1_sysclk4, spi1_clkdev, 0),
- LPSC(7, 0, mmcsd1, pll1_sysclk4, mmcsd1_clkdev, 0),
- LPSC(8, 0, asp0, pll1_sysclk4, asp0_clkdev, 0),
- LPSC(9, 0, usb, pll1_auxclk, usb_clkdev, 0),
- LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0),
- LPSC(11, 0, spi2, pll1_sysclk4, spi2_clkdev, 0),
- LPSC(12, 0, rto, pll1_sysclk4, NULL, 0),
- LPSC(14, 0, aemif, pll1_sysclk4, aemif_clkdev, 0),
- LPSC(15, 0, mmcsd0, pll1_sysclk8, mmcsd0_clkdev, 0),
- LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
- LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
- LPSC(20, 0, uart1, pll1_sysclk4, uart1_clkdev, 0),
- LPSC(22, 0, spi0, pll1_sysclk4, spi0_clkdev, 0),
- LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
- LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
- LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
- LPSC(26, 0, gpio, pll1_sysclk4, gpio_clkdev, 0),
- LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
- /* REVISIT: why can't this be disabled? */
- LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(31, 0, arm, pll2_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(38, 0, spi3, pll1_sysclk4, spi3_clkdev, 0),
- LPSC(39, 0, spi4, pll1_auxclk, spi4_clkdev, 0),
- LPSC(40, 0, emac, pll1_sysclk4, emac_clkdev, 0),
- /*
- * The TRM (ARM Subsystem User's Guide) shows two clocks input into
- * voice codec module (PLL2 SYSCLK4 with a DIV2 and PLL1 SYSCLK4). Its
- * not fully clear from documentation which clock should be considered
- * as parent for PSC. The clock chosen here is to maintain
- * compatibility with existing code in arch/arm/mach-davinci/dm365.c
- */
- LPSC(44, 0, voice_codec, pll2_sysclk4, voice_codec_clkdev, 0),
- /*
- * Its not fully clear from TRM (ARM Subsystem User's Guide) as to what
- * the parent of VPSS DAC LPSC should actually be. PLL1 SYSCLK3 feeds
- * into HDVICP and MJCP. The clock chosen here is to remain compatible
- * with code existing in arch/arm/mach-davinci/dm365.c
- */
- LPSC(46, 0, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0),
- LPSC(47, 0, vpss_master, pll1_sysclk5, vpss_master_clkdev, 0),
- LPSC(50, 0, mjcp, pll1_sysclk3, NULL, 0),
- { }
-};
-
-int dm365_psc_init(struct device *dev, void __iomem *base)
-{
- return davinci_psc_register_clocks(dev, dm365_psc_info, 52, base);
-}
-
-static struct clk_bulk_data dm365_psc_parent_clks[] = {
- { .id = "pll1_sysclk1" },
- { .id = "pll1_sysclk3" },
- { .id = "pll1_sysclk4" },
- { .id = "pll1_sysclk5" },
- { .id = "pll1_sysclk8" },
- { .id = "pll2_sysclk2" },
- { .id = "pll2_sysclk4" },
- { .id = "pll1_auxclk" },
-};
-
-const struct davinci_psc_init_data dm365_psc_init_data = {
- .parent_clks = dm365_psc_parent_clks,
- .num_parent_clks = ARRAY_SIZE(dm365_psc_parent_clks),
- .psc_init = &dm365_psc_init,
-};
diff --git a/drivers/clk/davinci/psc-dm644x.c b/drivers/clk/davinci/psc-dm644x.c
deleted file mode 100644
index 0cea6e0bd5f0..000000000000
--- a/drivers/clk/davinci/psc-dm644x.c
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PSC clock descriptions for TI DaVinci DM644x
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clk/davinci.h>
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "psc.h"
-
-LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
-LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
-LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
- "fck", "davinci_mdio.0");
-LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
-LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710");
-LPSC_CLKDEV2(aemif_clkdev, "aemif", NULL,
- NULL, "ti-aemif");
-LPSC_CLKDEV1(mmcsd_clkdev, NULL, "dm6441-mmc.0");
-LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp");
-LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
-LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
-LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
-LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
-/* REVISIT: gpio-davinci.c should be modified to drop con_id */
-LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
-LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
-LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
-
-static const struct davinci_lpsc_clk_info dm644x_psc_info[] = {
- LPSC(0, 0, vpss_master, pll1_sysclk3, vpss_master_clkdev, 0),
- LPSC(1, 0, vpss_slave, pll1_sysclk3, vpss_slave_clkdev, 0),
- LPSC(6, 0, emac, pll1_sysclk5, emac_clkdev, 0),
- LPSC(9, 0, usb, pll1_sysclk5, usb_clkdev, 0),
- LPSC(10, 0, ide, pll1_sysclk5, ide_clkdev, 0),
- LPSC(11, 0, vlynq, pll1_sysclk5, NULL, 0),
- LPSC(14, 0, aemif, pll1_sysclk5, aemif_clkdev, 0),
- LPSC(15, 0, mmcsd, pll1_sysclk5, mmcsd_clkdev, 0),
- LPSC(17, 0, asp0, pll1_sysclk5, asp0_clkdev, 0),
- LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
- LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
- LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0),
- LPSC(21, 0, uart2, pll1_auxclk, uart2_clkdev, 0),
- LPSC(22, 0, spi, pll1_sysclk5, NULL, 0),
- LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
- LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
- LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
- LPSC(26, 0, gpio, pll1_sysclk5, gpio_clkdev, 0),
- LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
- /* REVISIT: why can't this be disabled? */
- LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(31, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- /* REVISIT how to disable? */
- LPSC(39, 1, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
- /* REVISIT how to disable? */
- LPSC(40, 1, vicp, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- { }
-};
-
-int dm644x_psc_init(struct device *dev, void __iomem *base)
-{
- return davinci_psc_register_clocks(dev, dm644x_psc_info, 41, base);
-}
-
-static struct clk_bulk_data dm644x_psc_parent_clks[] = {
- { .id = "pll1_sysclk1" },
- { .id = "pll1_sysclk2" },
- { .id = "pll1_sysclk3" },
- { .id = "pll1_sysclk5" },
- { .id = "pll1_auxclk" },
-};
-
-const struct davinci_psc_init_data dm644x_psc_init_data = {
- .parent_clks = dm644x_psc_parent_clks,
- .num_parent_clks = ARRAY_SIZE(dm644x_psc_parent_clks),
- .psc_init = &dm644x_psc_init,
-};
diff --git a/drivers/clk/davinci/psc-dm646x.c b/drivers/clk/davinci/psc-dm646x.c
deleted file mode 100644
index 20012dc7471a..000000000000
--- a/drivers/clk/davinci/psc-dm646x.c
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PSC clock descriptions for TI DaVinci DM646x
- *
- * Copyright (C) 2018 David Lechner <david@lechnology.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clk/davinci.h>
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include "psc.h"
-
-LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710");
-LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
- "fck", "davinci_mdio.0");
-LPSC_CLKDEV2(aemif_clkdev, "aemif", NULL,
- NULL, "ti-aemif");
-LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
-LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1");
-LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
-LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
-LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
-LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
-/* REVISIT: gpio-davinci.c should be modified to drop con_id */
-LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
-LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
-
-static const struct davinci_lpsc_clk_info dm646x_psc_info[] = {
- LPSC(0, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- /* REVISIT how to disable? */
- LPSC(1, 0, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(4, 0, edma_cc, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(5, 0, edma_tc0, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(6, 0, edma_tc1, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(7, 0, edma_tc2, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(8, 0, edma_tc3, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(10, 0, ide, pll1_sysclk4, ide_clkdev, 0),
- LPSC(14, 0, emac, pll1_sysclk3, emac_clkdev, 0),
- LPSC(16, 0, vpif0, ref_clk, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(17, 0, vpif1, ref_clk, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(21, 0, aemif, pll1_sysclk3, aemif_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(22, 0, mcasp0, pll1_sysclk3, mcasp0_clkdev, 0),
- LPSC(23, 0, mcasp1, pll1_sysclk3, mcasp1_clkdev, 0),
- LPSC(26, 0, uart0, aux_clkin, uart0_clkdev, 0),
- LPSC(27, 0, uart1, aux_clkin, uart1_clkdev, 0),
- LPSC(28, 0, uart2, aux_clkin, uart2_clkdev, 0),
- /* REVIST: disabling hangs system */
- LPSC(29, 0, pwm0, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
- /* REVIST: disabling hangs system */
- LPSC(30, 0, pwm1, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
- LPSC(31, 0, i2c, pll1_sysclk3, i2c_clkdev, 0),
- LPSC(33, 0, gpio, pll1_sysclk3, gpio_clkdev, 0),
- LPSC(34, 0, timer0, pll1_sysclk3, timer0_clkdev, LPSC_ALWAYS_ENABLED),
- LPSC(35, 0, timer1, pll1_sysclk3, NULL, 0),
- { }
-};
-
-int dm646x_psc_init(struct device *dev, void __iomem *base)
-{
- return davinci_psc_register_clocks(dev, dm646x_psc_info, 46, base);
-}
-
-static struct clk_bulk_data dm646x_psc_parent_clks[] = {
- { .id = "ref_clk" },
- { .id = "aux_clkin" },
- { .id = "pll1_sysclk1" },
- { .id = "pll1_sysclk2" },
- { .id = "pll1_sysclk3" },
- { .id = "pll1_sysclk4" },
- { .id = "pll1_sysclk5" },
-};
-
-const struct davinci_psc_init_data dm646x_psc_init_data = {
- .parent_clks = dm646x_psc_parent_clks,
- .num_parent_clks = ARRAY_SIZE(dm646x_psc_parent_clks),
- .psc_init = &dm646x_psc_init,
-};
diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
index 7387e7f6276e..f3ee9397bb0c 100644
--- a/drivers/clk/davinci/psc.c
+++ b/drivers/clk/davinci/psc.c
@@ -18,10 +18,9 @@
#include <linux/clk/davinci.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/regmap.h>
@@ -278,6 +277,11 @@ davinci_lpsc_clk_register(struct device *dev, const char *name,
lpsc->pm_domain.name = devm_kasprintf(dev, GFP_KERNEL, "%s: %s",
best_dev_name(dev), name);
+ if (!lpsc->pm_domain.name) {
+ clk_hw_unregister(&lpsc->hw);
+ kfree(lpsc);
+ return ERR_PTR(-ENOMEM);
+ }
lpsc->pm_domain.attach_dev = davinci_psc_genpd_attach_dev;
lpsc->pm_domain.detach_dev = davinci_psc_genpd_detach_dev;
lpsc->pm_domain.flags = GENPD_FLAG_PM_CLK;
@@ -495,49 +499,26 @@ int of_davinci_psc_clk_init(struct device *dev,
}
static const struct of_device_id davinci_psc_of_match[] = {
-#ifdef CONFIG_ARCH_DAVINCI_DA850
{ .compatible = "ti,da850-psc0", .data = &of_da850_psc0_init_data },
{ .compatible = "ti,da850-psc1", .data = &of_da850_psc1_init_data },
-#endif
{ }
};
static const struct platform_device_id davinci_psc_id_table[] = {
-#ifdef CONFIG_ARCH_DAVINCI_DA830
- { .name = "da830-psc0", .driver_data = (kernel_ulong_t)&da830_psc0_init_data },
- { .name = "da830-psc1", .driver_data = (kernel_ulong_t)&da830_psc1_init_data },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DA850
{ .name = "da850-psc0", .driver_data = (kernel_ulong_t)&da850_psc0_init_data },
{ .name = "da850-psc1", .driver_data = (kernel_ulong_t)&da850_psc1_init_data },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM355
- { .name = "dm355-psc", .driver_data = (kernel_ulong_t)&dm355_psc_init_data },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM365
- { .name = "dm365-psc", .driver_data = (kernel_ulong_t)&dm365_psc_init_data },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM644x
- { .name = "dm644x-psc", .driver_data = (kernel_ulong_t)&dm644x_psc_init_data },
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM646x
- { .name = "dm646x-psc", .driver_data = (kernel_ulong_t)&dm646x_psc_init_data },
-#endif
{ }
};
static int davinci_psc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *of_id;
const struct davinci_psc_init_data *init_data = NULL;
void __iomem *base;
int ret;
- of_id = of_match_device(davinci_psc_of_match, dev);
- if (of_id)
- init_data = of_id->data;
- else if (pdev->id_entry)
+ init_data = device_get_match_data(dev);
+ if (!init_data && pdev->id_entry)
init_data = (void *)pdev->id_entry->driver_data;
if (!init_data) {
diff --git a/drivers/clk/davinci/psc.h b/drivers/clk/davinci/psc.h
index 69070f834391..742672843776 100644
--- a/drivers/clk/davinci/psc.h
+++ b/drivers/clk/davinci/psc.h
@@ -94,27 +94,9 @@ struct davinci_psc_init_data {
int (*psc_init)(struct device *dev, void __iomem *base);
};
-#ifdef CONFIG_ARCH_DAVINCI_DA830
-extern const struct davinci_psc_init_data da830_psc0_init_data;
-extern const struct davinci_psc_init_data da830_psc1_init_data;
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DA850
extern const struct davinci_psc_init_data da850_psc0_init_data;
extern const struct davinci_psc_init_data da850_psc1_init_data;
extern const struct davinci_psc_init_data of_da850_psc0_init_data;
extern const struct davinci_psc_init_data of_da850_psc1_init_data;
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM355
-extern const struct davinci_psc_init_data dm355_psc_init_data;
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM365
-extern const struct davinci_psc_init_data dm365_psc_init_data;
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM644x
-extern const struct davinci_psc_init_data dm644x_psc_init_data;
-#endif
-#ifdef CONFIG_ARCH_DAVINCI_DM646x
-extern const struct davinci_psc_init_data dm646x_psc_init_data;
-#endif
#endif /* __CLK_DAVINCI_PSC_H__ */
diff --git a/drivers/clk/hisilicon/clk-hi3519.c b/drivers/clk/hisilicon/clk-hi3519.c
index ad0c7f350cf0..0c50acd8543a 100644
--- a/drivers/clk/hisilicon/clk-hi3519.c
+++ b/drivers/clk/hisilicon/clk-hi3519.c
@@ -130,7 +130,7 @@ static void hi3519_clk_unregister(struct platform_device *pdev)
of_clk_del_provider(pdev->dev.of_node);
hisi_clk_unregister_gate(hi3519_gate_clks,
- ARRAY_SIZE(hi3519_mux_clks),
+ ARRAY_SIZE(hi3519_gate_clks),
crg->clk_data);
hisi_clk_unregister_mux(hi3519_mux_clks,
ARRAY_SIZE(hi3519_mux_clks),
@@ -162,13 +162,12 @@ static int hi3519_clk_probe(struct platform_device *pdev)
return 0;
}
-static int hi3519_clk_remove(struct platform_device *pdev)
+static void hi3519_clk_remove(struct platform_device *pdev)
{
struct hi3519_crg_data *crg = platform_get_drvdata(pdev);
hisi_reset_exit(crg->rstc);
hi3519_clk_unregister(pdev);
- return 0;
}
diff --git a/drivers/clk/hisilicon/clk-hi3559a.c b/drivers/clk/hisilicon/clk-hi3559a.c
index 9ea1a80acbe8..f297fb25c512 100644
--- a/drivers/clk/hisilicon/clk-hi3559a.c
+++ b/drivers/clk/hisilicon/clk-hi3559a.c
@@ -9,7 +9,7 @@
#include <linux/clk-provider.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -407,7 +407,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct hi3559av100_clk_pll *clk = to_pll_clk(hw);
- u64 frac_val, fbdiv_val, refdiv_val;
+ u64 frac_val, fbdiv_val;
u32 postdiv1_val, postdiv2_val;
u32 val;
u64 tmp, rate;
@@ -435,14 +435,13 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
val = readl_relaxed(clk->ctrl_reg2);
val = val >> clk->refdiv_shift;
val &= ((1 << clk->refdiv_width) - 1);
- refdiv_val = val;
/* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */
rate = 0;
tmp = 24000000 * fbdiv_val + (24000000 * frac_val) / (1 << 24);
rate += tmp;
- do_div(rate, refdiv_val);
- do_div(rate, postdiv1_val * postdiv2_val);
+ rate = div_u64(rate, val);
+ rate = div_u64(rate, postdiv1_val * postdiv2_val);
return rate;
}
@@ -461,8 +460,7 @@ static void hisi_clk_register_pll(struct hi3559av100_pll_clock *clks,
struct clk_init_data init;
int i;
- p_clk = devm_kzalloc(dev, sizeof(*p_clk) * nums, GFP_KERNEL);
-
+ p_clk = devm_kcalloc(dev, nums, sizeof(*p_clk), GFP_KERNEL);
if (!p_clk)
return;
@@ -491,7 +489,6 @@ static void hisi_clk_register_pll(struct hi3559av100_pll_clock *clks,
clk = clk_register(NULL, &p_clk->hw);
if (IS_ERR(clk)) {
- devm_kfree(dev, p_clk);
dev_err(dev, "%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
@@ -810,13 +807,12 @@ static int hi3559av100_crg_probe(struct platform_device *pdev)
return 0;
}
-static int hi3559av100_crg_remove(struct platform_device *pdev)
+static void hi3559av100_crg_remove(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
hisi_reset_exit(crg->rstc);
crg->funcs->unregister_clks(pdev);
- return 0;
}
static struct platform_driver hi3559av100_crg_driver = {
@@ -841,5 +837,4 @@ static void __exit hi3559av100_crg_exit(void)
module_exit(hi3559av100_crg_exit);
-MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("HiSilicon Hi3559AV100 CRG Driver");
diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
index a3d04c7c3da8..5d0226530fdb 100644
--- a/drivers/clk/hisilicon/clk-hi3620.c
+++ b/drivers/clk/hisilicon/clk-hi3620.c
@@ -14,7 +14,6 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <dt-bindings/clock/hi3620-clock.h>
@@ -467,8 +466,10 @@ static void __init hi3620_mmc_clk_init(struct device_node *node)
return;
clk_data->clks = kcalloc(num, sizeof(*clk_data->clks), GFP_KERNEL);
- if (!clk_data->clks)
+ if (!clk_data->clks) {
+ kfree(clk_data);
return;
+ }
for (i = 0; i < num; i++) {
struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
diff --git a/drivers/clk/hisilicon/clk-hi3660-stub.c b/drivers/clk/hisilicon/clk-hi3660-stub.c
index 3a653d54bee0..7c8b00ee6019 100644
--- a/drivers/clk/hisilicon/clk-hi3660-stub.c
+++ b/drivers/clk/hisilicon/clk-hi3660-stub.c
@@ -34,7 +34,7 @@
.num_parents = 0, \
.flags = CLK_GET_RATE_NOCACHE, \
}, \
- },
+ }
#define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw)
@@ -67,14 +67,14 @@ static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw,
return stub_clk->rate;
}
-static long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int hi3660_stub_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
/*
* LPM3 handles rate rounding so just return whatever
* rate is requested.
*/
- return rate;
+ return 0;
}
static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -97,15 +97,15 @@ static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops hi3660_stub_clk_ops = {
.recalc_rate = hi3660_stub_clk_recalc_rate,
- .round_rate = hi3660_stub_clk_round_rate,
+ .determine_rate = hi3660_stub_clk_determine_rate,
.set_rate = hi3660_stub_clk_set_rate,
};
static struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = {
- DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0")
- DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1")
- DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d")
- DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc")
+ DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0"),
+ DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1"),
+ DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d"),
+ DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc"),
};
static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec,
diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c
index 41f61726ab19..50f13dbb0e48 100644
--- a/drivers/clk/hisilicon/clk-hi3660.c
+++ b/drivers/clk/hisilicon/clk-hi3660.c
@@ -6,7 +6,7 @@
#include <dt-bindings/clock/hi3660-clock.h>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include "clk.h"
diff --git a/drivers/clk/hisilicon/clk-hi3670.c b/drivers/clk/hisilicon/clk-hi3670.c
index 4d05a71683a5..fa20ad144c8e 100644
--- a/drivers/clk/hisilicon/clk-hi3670.c
+++ b/drivers/clk/hisilicon/clk-hi3670.c
@@ -9,7 +9,7 @@
#include <dt-bindings/clock/hi3670-clock.h>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include "clk.h"
diff --git a/drivers/clk/hisilicon/clk-hi6220-stub.c b/drivers/clk/hisilicon/clk-hi6220-stub.c
index 4fdee4424d82..bf99cfafafa0 100644
--- a/drivers/clk/hisilicon/clk-hi6220-stub.c
+++ b/drivers/clk/hisilicon/clk-hi6220-stub.c
@@ -14,7 +14,7 @@
#include <linux/mfd/syscon.h>
#include <linux/mailbox_client.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
/* Stub clocks id */
@@ -161,11 +161,11 @@ static int hi6220_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
return ret;
}
-static long hi6220_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int hi6220_stub_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct hi6220_stub_clk *stub_clk = to_stub_clk(hw);
- unsigned long new_rate = rate / 1000; /* kHz */
+ unsigned long new_rate = req->rate / 1000; /* kHz */
switch (stub_clk->id) {
case HI6220_STUB_ACPU0:
@@ -181,12 +181,14 @@ static long hi6220_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate,
break;
}
- return new_rate;
+ req->rate = new_rate;
+
+ return 0;
}
static const struct clk_ops hi6220_stub_clk_ops = {
.recalc_rate = hi6220_stub_clk_recalc_rate,
- .round_rate = hi6220_stub_clk_round_rate,
+ .determine_rate = hi6220_stub_clk_determine_rate,
.set_rate = hi6220_stub_clk_set_rate,
};
diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c
index e7cdf72d4b06..c9d5a88da053 100644
--- a/drivers/clk/hisilicon/clk-hi6220.c
+++ b/drivers/clk/hisilicon/clk-hi6220.c
@@ -11,9 +11,6 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <dt-bindings/clock/hi6220-clock.h>
diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c
index 785b9faf3ea5..feb34e98af8c 100644
--- a/drivers/clk/hisilicon/clk-hip04.c
+++ b/drivers/clk/hisilicon/clk-hip04.c
@@ -11,9 +11,6 @@
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
#include <dt-bindings/clock/hip04-clock.h>
diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c
index 54d9fdc93599..09368fd32bef 100644
--- a/drivers/clk/hisilicon/clk.c
+++ b/drivers/clk/hisilicon/clk.c
@@ -16,7 +16,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk.h"
diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c
index 5348bafe694f..6bae18a84cb6 100644
--- a/drivers/clk/hisilicon/clkdivider-hi6220.c
+++ b/drivers/clk/hisilicon/clkdivider-hi6220.c
@@ -55,13 +55,15 @@ static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
}
-static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int hi6220_clkdiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
- return divider_round_rate(hw, rate, prate, dclk->table,
- dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate, dclk->table,
+ dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
+
+ return 0;
}
static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -93,7 +95,7 @@ static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops hi6220_clkdiv_ops = {
.recalc_rate = hi6220_clkdiv_recalc_rate,
- .round_rate = hi6220_clkdiv_round_rate,
+ .determine_rate = hi6220_clkdiv_determine_rate,
.set_rate = hi6220_clkdiv_set_rate,
};
diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c
index 90d858522967..21d4297f3225 100644
--- a/drivers/clk/hisilicon/clkgate-separated.c
+++ b/drivers/clk/hisilicon/clkgate-separated.c
@@ -17,9 +17,9 @@
#include "clk.h"
/* clock separated gate register offset */
-#define CLKGATE_SEPERATED_ENABLE 0x0
-#define CLKGATE_SEPERATED_DISABLE 0x4
-#define CLKGATE_SEPERATED_STATUS 0x8
+#define CLKGATE_SEPARATED_ENABLE 0x0
+#define CLKGATE_SEPARATED_DISABLE 0x4
+#define CLKGATE_SEPARATED_STATUS 0x8
struct clkgate_separated {
struct clk_hw hw;
@@ -40,7 +40,7 @@ static int clkgate_separated_enable(struct clk_hw *hw)
spin_lock_irqsave(sclk->lock, flags);
reg = BIT(sclk->bit_idx);
writel_relaxed(reg, sclk->enable);
- readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
+ readl_relaxed(sclk->enable + CLKGATE_SEPARATED_STATUS);
if (sclk->lock)
spin_unlock_irqrestore(sclk->lock, flags);
return 0;
@@ -56,8 +56,8 @@ static void clkgate_separated_disable(struct clk_hw *hw)
if (sclk->lock)
spin_lock_irqsave(sclk->lock, flags);
reg = BIT(sclk->bit_idx);
- writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE);
- readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
+ writel_relaxed(reg, sclk->enable + CLKGATE_SEPARATED_DISABLE);
+ readl_relaxed(sclk->enable + CLKGATE_SEPARATED_STATUS);
if (sclk->lock)
spin_unlock_irqrestore(sclk->lock, flags);
}
@@ -68,7 +68,7 @@ static int clkgate_separated_is_enabled(struct clk_hw *hw)
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
- reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
+ reg = readl_relaxed(sclk->enable + CLKGATE_SEPARATED_STATUS);
reg &= BIT(sclk->bit_idx);
return reg ? 1 : 0;
@@ -100,7 +100,7 @@ struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
- sclk->enable = reg + CLKGATE_SEPERATED_ENABLE;
+ sclk->enable = reg + CLKGATE_SEPARATED_ENABLE;
sclk->bit_idx = bit_idx;
sclk->flags = clk_gate_flags;
sclk->hw.init = &init;
diff --git a/drivers/clk/hisilicon/crg-hi3516cv300.c b/drivers/clk/hisilicon/crg-hi3516cv300.c
index 5d4e61c7a429..b66140f74c51 100644
--- a/drivers/clk/hisilicon/crg-hi3516cv300.c
+++ b/drivers/clk/hisilicon/crg-hi3516cv300.c
@@ -8,7 +8,7 @@
#include <dt-bindings/clock/hi3516cv300-clock.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include "clk.h"
#include "crg.h"
@@ -284,13 +284,12 @@ static int hi3516cv300_crg_probe(struct platform_device *pdev)
return 0;
}
-static int hi3516cv300_crg_remove(struct platform_device *pdev)
+static void hi3516cv300_crg_remove(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
hisi_reset_exit(crg->rstc);
crg->funcs->unregister_clks(pdev);
- return 0;
}
static struct platform_driver hi3516cv300_crg_driver = {
diff --git a/drivers/clk/hisilicon/crg-hi3798cv200.c b/drivers/clk/hisilicon/crg-hi3798cv200.c
index 08a19ba776e6..8eabd1cc229f 100644
--- a/drivers/clk/hisilicon/crg-hi3798cv200.c
+++ b/drivers/clk/hisilicon/crg-hi3798cv200.c
@@ -8,7 +8,7 @@
#include <dt-bindings/clock/histb-clock.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include "clk.h"
#include "crg.h"
@@ -367,13 +367,12 @@ static int hi3798cv200_crg_probe(struct platform_device *pdev)
return 0;
}
-static int hi3798cv200_crg_remove(struct platform_device *pdev)
+static void hi3798cv200_crg_remove(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
hisi_reset_exit(crg->rstc);
crg->funcs->unregister_clks(pdev);
- return 0;
}
static struct platform_driver hi3798cv200_crg_driver = {
diff --git a/drivers/clk/imgtec/clk-boston.c b/drivers/clk/imgtec/clk-boston.c
index b00cbd045af5..db96f8bea630 100644
--- a/drivers/clk/imgtec/clk-boston.c
+++ b/drivers/clk/imgtec/clk-boston.c
@@ -67,21 +67,21 @@ static void __init clk_boston_setup(struct device_node *np)
hw = clk_hw_register_fixed_rate(NULL, "input", NULL, 0, in_freq);
if (IS_ERR(hw)) {
- pr_err("failed to register input clock: %ld\n", PTR_ERR(hw));
+ pr_err("failed to register input clock: %pe\n", hw);
goto fail_input;
}
onecell->hws[BOSTON_CLK_INPUT] = hw;
hw = clk_hw_register_fixed_rate(NULL, "sys", "input", 0, sys_freq);
if (IS_ERR(hw)) {
- pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw));
+ pr_err("failed to register sys clock: %pe\n", hw);
goto fail_sys;
}
onecell->hws[BOSTON_CLK_SYS] = hw;
hw = clk_hw_register_fixed_rate(NULL, "cpu", "input", 0, cpu_freq);
if (IS_ERR(hw)) {
- pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw));
+ pr_err("failed to register cpu clock: %pe\n", hw);
goto fail_cpu;
}
onecell->hws[BOSTON_CLK_CPU] = hw;
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index 25785ec9c276..b292e7ca5c24 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -81,6 +81,7 @@ config CLK_IMX8MP
tristate "IMX8MP CCM Clock Driver"
depends on ARCH_MXC || COMPILE_TEST
select MXC_CLK
+ select AUXILIARY_BUS if RESET_CONTROLLER
help
Build the driver for i.MX8MP CCM Clock Driver
@@ -96,6 +97,7 @@ config CLK_IMX8QXP
depends on (ARCH_MXC && ARM64) || COMPILE_TEST
depends on IMX_SCU && HAVE_ARM_SMCCC
select MXC_CLK_SCU
+ select MXC_CLK
help
Build the driver for IMX8QXP SCU based clocks.
@@ -103,6 +105,7 @@ config CLK_IMX8ULP
tristate "IMX8ULP CCM Clock Driver"
depends on ARCH_MXC || COMPILE_TEST
select MXC_CLK
+ select AUXILIARY_BUS
help
Build the driver for i.MX8ULP CCM Clock Driver
@@ -113,9 +116,16 @@ config CLK_IMX93
help
Build the driver for i.MX93 CCM Clock Driver
+config CLK_IMX95_BLK_CTL
+ tristate "IMX95 Clock Driver for BLK CTL"
+ depends on ARCH_MXC || COMPILE_TEST
+ select MXC_CLK
+ help
+ Build the clock driver for i.MX95 BLK CTL
+
config CLK_IMXRT1050
tristate "IMXRT1050 CCM Clock Driver"
- depends on SOC_IMXRT
+ depends on SOC_IMXRT || COMPILE_TEST
select MXC_CLK
help
Build the driver for i.MXRT1050 CCM Clock Driver
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 88b9b9285d22..208b46873a18 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -12,6 +12,7 @@ mxc-clk-objs += clk-fixup-div.o
mxc-clk-objs += clk-fixup-mux.o
mxc-clk-objs += clk-frac-pll.o
mxc-clk-objs += clk-gate2.o
+mxc-clk-objs += clk-gate-93.o
mxc-clk-objs += clk-gate-exclusive.o
mxc-clk-objs += clk-pfd.o
mxc-clk-objs += clk-pfdv2.o
@@ -21,22 +22,26 @@ mxc-clk-objs += clk-pllv3.o
mxc-clk-objs += clk-pllv4.o
mxc-clk-objs += clk-pll14xx.o
mxc-clk-objs += clk-sscg-pll.o
+mxc-clk-objs += clk-gpr-mux.o
obj-$(CONFIG_MXC_CLK) += mxc-clk.o
obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o
-obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o
+obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-imx8mp-audiomix.o
obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
obj-$(CONFIG_CLK_IMX93) += clk-imx93.o
+obj-$(CONFIG_CLK_IMX95_BLK_CTL) += clk-imx95-blk-ctl.o
-obj-$(CONFIG_MXC_CLK_SCU) += clk-imx-scu.o clk-imx-lpcg-scu.o
+obj-$(CONFIG_MXC_CLK_SCU) += clk-imx-scu.o clk-imx-lpcg-scu.o clk-imx-acm.o
clk-imx-scu-$(CONFIG_CLK_IMX8QXP) += clk-scu.o clk-imx8qxp.o \
clk-imx8qxp-rsrc.o clk-imx8qm-rsrc.o \
clk-imx8dxl-rsrc.o
clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o
+clk-imx-acm-$(CONFIG_CLK_IMX8QXP) = clk-imx8-acm.o
obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o
+obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp-sim-lpav.o
obj-$(CONFIG_CLK_IMX1) += clk-imx1.o
obj-$(CONFIG_CLK_IMX25) += clk-imx25.o
diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c
index 6f17311647f3..eb27c6fee359 100644
--- a/drivers/clk/imx/clk-busy.c
+++ b/drivers/clk/imx/clk-busy.c
@@ -46,12 +46,12 @@ static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw,
return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate);
}
-static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_busy_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_busy_divider *busy = to_clk_busy_divider(hw);
- return busy->div_ops->round_rate(&busy->div.hw, rate, prate);
+ return busy->div_ops->determine_rate(&busy->div.hw, req);
}
static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -69,7 +69,7 @@ static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_busy_divider_ops = {
.recalc_rate = clk_busy_divider_recalc_rate,
- .round_rate = clk_busy_divider_round_rate,
+ .determine_rate = clk_busy_divider_determine_rate,
.set_rate = clk_busy_divider_set_rate,
};
@@ -148,6 +148,7 @@ static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_busy_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_busy_mux_get_parent,
.set_parent = clk_busy_mux_set_parent,
};
diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c
index 4eedd45dbaa8..37d2fc197be6 100644
--- a/drivers/clk/imx/clk-composite-7ulp.c
+++ b/drivers/clk/imx/clk-composite-7ulp.c
@@ -7,6 +7,7 @@
#include <linux/bits.h>
#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -14,15 +15,14 @@
#include "../clk-fractional-divider.h"
#include "clk.h"
+#define PCG_PR_MASK BIT(31)
#define PCG_PCS_SHIFT 24
#define PCG_PCS_MASK 0x7
#define PCG_CGC_SHIFT 30
#define PCG_FRAC_SHIFT 3
#define PCG_FRAC_WIDTH 1
-#define PCG_FRAC_MASK BIT(3)
#define PCG_PCD_SHIFT 0
#define PCG_PCD_WIDTH 3
-#define PCG_PCD_MASK 0x7
#define SW_RST BIT(28)
@@ -37,6 +37,9 @@ static int pcc_gate_enable(struct clk_hw *hw)
if (ret)
return ret;
+ /* Make sure the IP's clock is ready before release reset */
+ udelay(1);
+
spin_lock_irqsave(gate->lock, flags);
/*
* release the sw reset for peripherals associated with
@@ -48,6 +51,15 @@ static int pcc_gate_enable(struct clk_hw *hw)
spin_unlock_irqrestore(gate->lock, flags);
+ /*
+ * Read back the register to make sure the previous write has been
+ * done in the target HW register. For IP like GPU, after deassert
+ * the reset, need to wait for a while to make sure the sync reset
+ * is done
+ */
+ readl(gate->reg);
+ udelay(1);
+
return 0;
}
@@ -80,6 +92,12 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
struct clk_hw *hw;
u32 val;
+ val = readl(reg);
+ if (!(val & PCG_PR_MASK)) {
+ pr_info("PCC PR is 0 for clk:%s, bypass\n", name);
+ return NULL;
+ }
+
if (mux_present) {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
@@ -102,10 +120,8 @@ static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
fd->reg = reg;
fd->mshift = PCG_FRAC_SHIFT;
fd->mwidth = PCG_FRAC_WIDTH;
- fd->mmask = PCG_FRAC_MASK;
fd->nshift = PCG_PCD_SHIFT;
fd->nwidth = PCG_PCD_WIDTH;
- fd->nmask = PCG_PCD_MASK;
fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
if (has_swrst)
fd->lock = &imx_ccm_lock;
diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c
index cbf0d7955a00..1467d0a1b934 100644
--- a/drivers/clk/imx/clk-composite-8m.c
+++ b/drivers/clk/imx/clk-composite-8m.c
@@ -73,21 +73,6 @@ static int imx8m_clk_composite_compute_dividers(unsigned long rate,
return ret;
}
-static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long *prate)
-{
- int prediv_value;
- int div_value;
-
- imx8m_clk_composite_compute_dividers(rate, *prate,
- &prediv_value, &div_value);
- rate = DIV_ROUND_UP(*prate, prediv_value);
-
- return DIV_ROUND_UP(rate, div_value);
-
-}
-
static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
@@ -97,7 +82,7 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
int prediv_value;
int div_value;
int ret;
- u32 val;
+ u32 orig, val;
ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
&prediv_value, &div_value);
@@ -106,23 +91,55 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
spin_lock_irqsave(divider->lock, flags);
- val = readl(divider->reg);
- val &= ~((clk_div_mask(divider->width) << divider->shift) |
- (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
+ orig = readl(divider->reg);
+ val = orig & ~((clk_div_mask(divider->width) << divider->shift) |
+ (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
val |= (u32)(prediv_value - 1) << divider->shift;
val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
- writel(val, divider->reg);
+
+ if (val != orig)
+ writel(val, divider->reg);
spin_unlock_irqrestore(divider->lock, flags);
return ret;
}
+static int imx8m_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ int prediv_value;
+ int div_value;
+
+ /* if read only, just return current value */
+ if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+ u32 val;
+
+ val = readl(divider->reg);
+ prediv_value = val >> divider->shift;
+ prediv_value &= clk_div_mask(divider->width);
+ prediv_value++;
+
+ div_value = val >> PCG_DIV_SHIFT;
+ div_value &= clk_div_mask(PCG_DIV_WIDTH);
+ div_value++;
+
+ return divider_ro_determine_rate(hw, req, divider->table,
+ PCG_PREDIV_WIDTH + PCG_DIV_WIDTH,
+ divider->flags, prediv_value * div_value);
+ }
+
+ return divider_determine_rate(hw, req, divider->table,
+ PCG_PREDIV_WIDTH + PCG_DIV_WIDTH,
+ divider->flags);
+}
+
static const struct clk_ops imx8m_clk_composite_divider_ops = {
.recalc_rate = imx8m_clk_composite_divider_recalc_rate,
- .round_rate = imx8m_clk_composite_divider_round_rate,
.set_rate = imx8m_clk_composite_divider_set_rate,
+ .determine_rate = imx8m_divider_determine_rate,
};
static u8 imx8m_clk_composite_mux_get_parent(struct clk_hw *hw)
@@ -171,6 +188,34 @@ static const struct clk_ops imx8m_clk_composite_mux_ops = {
.determine_rate = imx8m_clk_composite_mux_determine_rate,
};
+static int imx8m_clk_composite_gate_enable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ val = readl(gate->reg);
+ val |= BIT(gate->bit_idx);
+ writel(val, gate->reg);
+
+ spin_unlock_irqrestore(gate->lock, flags);
+
+ return 0;
+}
+
+static void imx8m_clk_composite_gate_disable(struct clk_hw *hw)
+{
+ /* composite clk requires the disable hook */
+}
+
+static const struct clk_ops imx8m_clk_composite_gate_ops = {
+ .enable = imx8m_clk_composite_gate_enable,
+ .disable = imx8m_clk_composite_gate_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+
struct clk_hw *__imx8m_clk_hw_composite(const char *name,
const char * const *parent_names,
int num_parents, void __iomem *reg,
@@ -179,15 +224,16 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name,
{
struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
struct clk_hw *div_hw, *gate_hw = NULL;
- struct clk_divider *div = NULL;
+ struct clk_divider *div;
struct clk_gate *gate = NULL;
- struct clk_mux *mux = NULL;
+ struct clk_mux *mux;
const struct clk_ops *divider_ops;
const struct clk_ops *mux_ops;
+ const struct clk_ops *gate_ops;
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
- goto fail;
+ return ERR_CAST(hw);
mux_hw = &mux->hw;
mux->reg = reg;
@@ -197,7 +243,7 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name,
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
- goto fail;
+ goto free_mux;
div_hw = &div->hw;
div->reg = reg;
@@ -224,28 +270,32 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name,
div->flags = CLK_DIVIDER_ROUND_CLOSEST;
/* skip registering the gate ops if M4 is enabled */
- if (!mcore_booted) {
- gate = kzalloc(sizeof(*gate), GFP_KERNEL);
- if (!gate)
- goto fail;
-
- gate_hw = &gate->hw;
- gate->reg = reg;
- gate->bit_idx = PCG_CGC_SHIFT;
- gate->lock = &imx_ccm_lock;
- }
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ goto free_div;
+
+ gate_hw = &gate->hw;
+ gate->reg = reg;
+ gate->bit_idx = PCG_CGC_SHIFT;
+ gate->lock = &imx_ccm_lock;
+ if (!mcore_booted)
+ gate_ops = &clk_gate_ops;
+ else
+ gate_ops = &imx8m_clk_composite_gate_ops;
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
mux_hw, mux_ops, div_hw,
- divider_ops, gate_hw, &clk_gate_ops, flags);
+ divider_ops, gate_hw, gate_ops, flags);
if (IS_ERR(hw))
- goto fail;
+ goto free_gate;
return hw;
-fail:
+free_gate:
kfree(gate);
+free_div:
kfree(div);
+free_mux:
kfree(mux);
return ERR_CAST(hw);
}
diff --git a/drivers/clk/imx/clk-composite-93.c b/drivers/clk/imx/clk-composite-93.c
index b44619aa5ca5..513d74a39d3b 100644
--- a/drivers/clk/imx/clk-composite-93.c
+++ b/drivers/clk/imx/clk-composite-93.c
@@ -9,22 +9,180 @@
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/slab.h>
#include "clk.h"
+#define TIMEOUT_US 500U
+
#define CCM_DIV_SHIFT 0
#define CCM_DIV_WIDTH 8
#define CCM_MUX_SHIFT 8
#define CCM_MUX_MASK 3
#define CCM_OFF_SHIFT 24
+#define CCM_BUSY_SHIFT 28
+#define STAT_OFFSET 0x4
#define AUTHEN_OFFSET 0x30
#define TZ_NS_SHIFT 9
#define TZ_NS_MASK BIT(9)
+#define WHITE_LIST_SHIFT 16
+
+static int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg)
+{
+ int ret;
+ u32 val;
+
+ ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)),
+ 0, TIMEOUT_US);
+ if (ret)
+ pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw));
+
+ return ret;
+}
+
+static void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ unsigned long flags;
+ u32 reg;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+
+ reg = readl(gate->reg);
+
+ if (enable)
+ reg &= ~BIT(gate->bit_idx);
+ else
+ reg |= BIT(gate->bit_idx);
+
+ writel(reg, gate->reg);
+
+ imx93_clk_composite_wait_ready(hw, gate->reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int imx93_clk_composite_gate_enable(struct clk_hw *hw)
+{
+ imx93_clk_composite_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+static void imx93_clk_composite_gate_disable(struct clk_hw *hw)
+{
+ /*
+ * Skip disable the root clock gate if mcore enabled.
+ * The root clock may be used by the mcore.
+ */
+ if (mcore_booted)
+ return;
+
+ imx93_clk_composite_gate_endisable(hw, 0);
+}
+
+static const struct clk_ops imx93_clk_composite_gate_ops = {
+ .enable = imx93_clk_composite_gate_enable,
+ .disable = imx93_clk_composite_gate_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+
+static unsigned long
+imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ return clk_divider_ops.recalc_rate(hw, parent_rate);
+}
+
+static int
+imx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ return clk_divider_ops.determine_rate(hw, req);
+}
+
+static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ int value;
+ unsigned long flags = 0;
+ u32 val;
+ int ret;
+
+ value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags);
+ if (value < 0)
+ return value;
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+
+ val = readl(divider->reg);
+ val &= ~(clk_div_mask(divider->width) << divider->shift);
+ val |= (u32)value << divider->shift;
+ writel(val, divider->reg);
+
+ ret = imx93_clk_composite_wait_ready(hw, divider->reg);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return ret;
+}
+
+static const struct clk_ops imx93_clk_composite_divider_ops = {
+ .recalc_rate = imx93_clk_composite_divider_recalc_rate,
+ .determine_rate = imx93_clk_composite_divider_determine_rate,
+ .set_rate = imx93_clk_composite_divider_set_rate,
+};
+
+static u8 imx93_clk_composite_mux_get_parent(struct clk_hw *hw)
+{
+ return clk_mux_ops.get_parent(hw);
+}
+
+static int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
+ unsigned long flags = 0;
+ u32 reg;
+ int ret;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+ reg = readl(mux->reg);
+ reg &= ~(mux->mask << mux->shift);
+ val = val << mux->shift;
+ reg |= val;
+ writel(reg, mux->reg);
+
+ ret = imx93_clk_composite_wait_ready(hw, mux->reg);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return ret;
+}
+
+static int
+imx93_clk_composite_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ return clk_mux_ops.determine_rate(hw, req);
+}
+
+static const struct clk_ops imx93_clk_composite_mux_ops = {
+ .get_parent = imx93_clk_composite_mux_get_parent,
+ .set_parent = imx93_clk_composite_mux_set_parent,
+ .determine_rate = imx93_clk_composite_mux_determine_rate,
+};
+
struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *parent_names,
- int num_parents, void __iomem *reg,
+ int num_parents, void __iomem *reg, u32 domain_id,
unsigned long flags)
{
struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
@@ -33,6 +191,7 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p
struct clk_gate *gate = NULL;
struct clk_mux *mux = NULL;
bool clk_ro = false;
+ u32 authen;
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
@@ -55,7 +214,8 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p
div->lock = &imx_ccm_lock;
div->flags = CLK_DIVIDER_ROUND_CLOSEST;
- if (!(readl(reg + AUTHEN_OFFSET) & TZ_NS_MASK))
+ authen = readl(reg + AUTHEN_OFFSET);
+ if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
clk_ro = true;
if (clk_ro) {
@@ -74,9 +234,10 @@ struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *p
gate->flags = CLK_GATE_SET_TO_DISABLE;
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
- mux_hw, &clk_mux_ops, div_hw,
- &clk_divider_ops, gate_hw,
- &clk_gate_ops, flags | CLK_SET_RATE_NO_REPARENT);
+ mux_hw, &imx93_clk_composite_mux_ops, div_hw,
+ &imx93_clk_composite_divider_ops, gate_hw,
+ &imx93_clk_composite_gate_ops,
+ flags | CLK_SET_RATE_NO_REPARENT);
}
if (IS_ERR(hw))
diff --git a/drivers/clk/imx/clk-cpu.c b/drivers/clk/imx/clk-cpu.c
index cb6ca4cf0535..43637cb61693 100644
--- a/drivers/clk/imx/clk-cpu.c
+++ b/drivers/clk/imx/clk-cpu.c
@@ -30,12 +30,14 @@ static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw,
return clk_get_rate(cpu->div);
}
-static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_cpu_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_cpu *cpu = to_clk_cpu(hw);
- return clk_round_rate(cpu->pll, rate);
+ req->rate = clk_round_rate(cpu->pll, req->rate);
+
+ return 0;
}
static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -66,7 +68,7 @@ static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_cpu_ops = {
.recalc_rate = clk_cpu_recalc_rate,
- .round_rate = clk_cpu_round_rate,
+ .determine_rate = clk_cpu_determine_rate,
.set_rate = clk_cpu_set_rate,
};
diff --git a/drivers/clk/imx/clk-fixup-div.c b/drivers/clk/imx/clk-fixup-div.c
index 100ca828b052..aa6addbeb5a8 100644
--- a/drivers/clk/imx/clk-fixup-div.c
+++ b/drivers/clk/imx/clk-fixup-div.c
@@ -18,7 +18,7 @@
* @fixup: a hook to fixup the write value
*
* The imx fixup divider clock is a subclass of basic clk_divider
- * with an addtional fixup hook.
+ * with an additional fixup hook.
*/
struct clk_fixup_div {
struct clk_divider divider;
@@ -41,12 +41,12 @@ static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw,
return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate);
}
-static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_fixup_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
- return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate);
+ return fixup_div->ops->determine_rate(&fixup_div->divider.hw, req);
}
static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -81,7 +81,7 @@ static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_fixup_div_ops = {
.recalc_rate = clk_fixup_div_recalc_rate,
- .round_rate = clk_fixup_div_round_rate,
+ .determine_rate = clk_fixup_div_determine_rate,
.set_rate = clk_fixup_div_set_rate,
};
diff --git a/drivers/clk/imx/clk-fixup-mux.c b/drivers/clk/imx/clk-fixup-mux.c
index c82401570c84..418ac9fe2c26 100644
--- a/drivers/clk/imx/clk-fixup-mux.c
+++ b/drivers/clk/imx/clk-fixup-mux.c
@@ -17,7 +17,7 @@
* @fixup: a hook to fixup the write value
*
* The imx fixup multiplexer clock is a subclass of basic clk_mux
- * with an addtional fixup hook.
+ * with an additional fixup hook.
*/
struct clk_fixup_mux {
struct clk_mux mux;
@@ -60,6 +60,7 @@ static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_fixup_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_fixup_mux_get_parent,
.set_parent = clk_fixup_mux_set_parent,
};
diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c
index c703056fae85..eb668faaa38f 100644
--- a/drivers/clk/imx/clk-frac-pll.c
+++ b/drivers/clk/imx/clk-frac-pll.c
@@ -119,19 +119,19 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- u64 parent_rate = *prate;
+ u64 parent_rate = req->best_parent_rate;
u32 divff, divfi;
u64 temp64;
parent_rate *= 8;
- rate *= 2;
- temp64 = rate;
+ req->rate *= 2;
+ temp64 = req->rate;
do_div(temp64, parent_rate);
divfi = temp64;
- temp64 = rate - divfi * parent_rate;
+ temp64 = req->rate - divfi * parent_rate;
temp64 *= PLL_FRAC_DENOM;
do_div(temp64, parent_rate);
divff = temp64;
@@ -140,9 +140,11 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
temp64 *= divff;
do_div(temp64, PLL_FRAC_DENOM);
- rate = parent_rate * divfi + temp64;
+ req->rate = parent_rate * divfi + temp64;
+
+ req->rate = req->rate / 2;
- return rate / 2;
+ return 0;
}
/*
@@ -198,7 +200,7 @@ static const struct clk_ops clk_frac_pll_ops = {
.unprepare = clk_pll_unprepare,
.is_prepared = clk_pll_is_prepared,
.recalc_rate = clk_pll_recalc_rate,
- .round_rate = clk_pll_round_rate,
+ .determine_rate = clk_pll_determine_rate,
.set_rate = clk_pll_set_rate,
};
diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c
index a2aaa14fc1ae..090d60867250 100644
--- a/drivers/clk/imx/clk-fracn-gppll.c
+++ b/drivers/clk/imx/clk-fracn-gppll.c
@@ -15,6 +15,7 @@
#include "clk.h"
#define PLL_CTRL 0x0
+#define HW_CTRL_SEL BIT(16)
#define CLKMUX_BYPASS BIT(2)
#define CLKMUX_EN BIT(1)
#define POWERUP_MASK BIT(0)
@@ -52,26 +53,42 @@
.odiv = (_odiv), \
}
+#define PLL_FRACN_GP_INTEGER(_rate, _mfi, _rdiv, _odiv) \
+ { \
+ .rate = (_rate), \
+ .mfi = (_mfi), \
+ .mfn = 0, \
+ .mfd = 0, \
+ .rdiv = (_rdiv), \
+ .odiv = (_odiv), \
+ }
+
struct clk_fracn_gppll {
struct clk_hw hw;
void __iomem *base;
const struct imx_fracn_gppll_rate_table *rate_table;
int rate_count;
+ u32 flags;
};
/*
- * Fvco = Fref * (MFI + MFN / MFD)
- * Fout = Fvco / (rdiv * odiv)
+ * Fvco = (Fref / rdiv) * (MFI + MFN / MFD)
+ * Fout = Fvco / odiv
+ * The (Fref / rdiv) should be in range 20MHz to 40MHz
+ * The Fvco should be in range 2.5Ghz to 5Ghz
*/
static const struct imx_fracn_gppll_rate_table fracn_tbl[] = {
- PLL_FRACN_GP(650000000U, 81, 0, 1, 0, 3),
+ PLL_FRACN_GP(1039500000U, 173, 25, 100, 1, 4),
+ PLL_FRACN_GP(650000000U, 162, 50, 100, 0, 6),
PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8),
- PLL_FRACN_GP(560000000U, 70, 0, 1, 0, 3),
- PLL_FRACN_GP(498000000U, 83, 0, 1, 0, 4),
+ PLL_FRACN_GP(560000000U, 140, 0, 1, 0, 6),
+ PLL_FRACN_GP(519750000U, 173, 25, 100, 1, 8),
+ PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8),
PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6),
PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9),
- PLL_FRACN_GP(400000000U, 50, 0, 1, 0, 3),
- PLL_FRACN_GP(393216000U, 81, 92, 100, 0, 5)
+ PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12),
+ PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10),
+ PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12)
};
struct imx_fracn_gppll_clk imx_fracn_gppll = {
@@ -80,6 +97,25 @@ struct imx_fracn_gppll_clk imx_fracn_gppll = {
};
EXPORT_SYMBOL_GPL(imx_fracn_gppll);
+/*
+ * Fvco = (Fref / rdiv) * MFI
+ * Fout = Fvco / odiv
+ * The (Fref / rdiv) should be in range 20MHz to 40MHz
+ * The Fvco should be in range 2.5Ghz to 5Ghz
+ */
+static const struct imx_fracn_gppll_rate_table int_tbl[] = {
+ PLL_FRACN_GP_INTEGER(1700000000U, 141, 1, 2),
+ PLL_FRACN_GP_INTEGER(1400000000U, 175, 1, 3),
+ PLL_FRACN_GP_INTEGER(900000000U, 150, 1, 4),
+ PLL_FRACN_GP_INTEGER(800000000U, 200, 1, 6),
+};
+
+struct imx_fracn_gppll_clk imx_fracn_gppll_integer = {
+ .rate_table = int_tbl,
+ .rate_count = ARRAY_SIZE(int_tbl),
+};
+EXPORT_SYMBOL_GPL(imx_fracn_gppll_integer);
+
static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw)
{
return container_of(hw, struct clk_fracn_gppll, hw);
@@ -98,8 +134,8 @@ imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate)
return NULL;
}
-static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_fracn_gppll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw);
const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table;
@@ -107,11 +143,16 @@ static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate,
/* Assuming rate_table is in descending order */
for (i = 0; i < pll->rate_count; i++)
- if (rate >= rate_table[i].rate)
- return rate_table[i].rate;
+ if (req->rate >= rate_table[i].rate) {
+ req->rate = rate_table[i].rate;
+
+ return 0;
+ }
/* return minimum supported value */
- return rate_table[pll->rate_count - 1].rate;
+ req->rate = rate_table[pll->rate_count - 1].rate;
+
+ return 0;
}
static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
@@ -166,9 +207,15 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon
break;
}
- /* Fvco = Fref * (MFI + MFN / MFD) */
- fvco = fvco * mfi * mfd + fvco * mfn;
- do_div(fvco, mfd * rdiv * odiv);
+ if (pll->flags & CLK_FRACN_GPPLL_INTEGER) {
+ /* Fvco = (Fref / rdiv) * MFI */
+ fvco = fvco * mfi;
+ do_div(fvco, rdiv * odiv);
+ } else {
+ /* Fvco = (Fref / rdiv) * (MFI + MFN / MFD) */
+ fvco = fvco * mfi * mfd + fvco * mfn;
+ do_div(fvco, mfd * rdiv * odiv);
+ }
return (unsigned long)fvco;
}
@@ -191,6 +238,11 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
rate = imx_get_pll_settings(pll, drate);
+ /* Hardware control select disable. PLL is control by register */
+ tmp = readl_relaxed(pll->base + PLL_CTRL);
+ tmp &= ~HW_CTRL_SEL;
+ writel_relaxed(tmp, pll->base + PLL_CTRL);
+
/* Disable output */
tmp = readl_relaxed(pll->base + PLL_CTRL);
tmp &= ~CLKMUX_EN;
@@ -207,8 +259,12 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv |
FIELD_PREP(PLL_MFI_MASK, rate->mfi);
writel_relaxed(pll_div, pll->base + PLL_DIV);
- writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR);
- writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR);
+ readl(pll->base + PLL_DIV);
+ if (pll->flags & CLK_FRACN_GPPLL_FRACN) {
+ writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR);
+ writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR);
+ readl(pll->base + PLL_NUMERATOR);
+ }
/* Wait for 5us according to fracn mode pll doc */
udelay(5);
@@ -216,6 +272,7 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
/* Enable Powerup */
tmp |= POWERUP_MASK;
writel_relaxed(tmp, pll->base + PLL_CTRL);
+ readl(pll->base + PLL_CTRL);
/* Wait Lock */
ret = clk_fracn_gppll_wait_lock(pll);
@@ -244,19 +301,24 @@ static int clk_fracn_gppll_prepare(struct clk_hw *hw)
if (val & POWERUP_MASK)
return 0;
+ if (pll->flags & CLK_FRACN_GPPLL_FRACN)
+ writel_relaxed(readl_relaxed(pll->base + PLL_NUMERATOR),
+ pll->base + PLL_NUMERATOR);
+
val |= CLKMUX_BYPASS;
writel_relaxed(val, pll->base + PLL_CTRL);
val |= POWERUP_MASK;
writel_relaxed(val, pll->base + PLL_CTRL);
-
- val |= CLKMUX_EN;
- writel_relaxed(val, pll->base + PLL_CTRL);
+ readl(pll->base + PLL_CTRL);
ret = clk_fracn_gppll_wait_lock(pll);
if (ret)
return ret;
+ val |= CLKMUX_EN;
+ writel_relaxed(val, pll->base + PLL_CTRL);
+
val &= ~CLKMUX_BYPASS;
writel_relaxed(val, pll->base + PLL_CTRL);
@@ -288,12 +350,14 @@ static const struct clk_ops clk_fracn_gppll_ops = {
.unprepare = clk_fracn_gppll_unprepare,
.is_prepared = clk_fracn_gppll_is_prepared,
.recalc_rate = clk_fracn_gppll_recalc_rate,
- .round_rate = clk_fracn_gppll_round_rate,
+ .determine_rate = clk_fracn_gppll_determine_rate,
.set_rate = clk_fracn_gppll_set_rate,
};
-struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
- const struct imx_fracn_gppll_clk *pll_clk)
+static struct clk_hw *_imx_clk_fracn_gppll(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk,
+ u32 pll_flags)
{
struct clk_fracn_gppll *pll;
struct clk_hw *hw;
@@ -314,6 +378,7 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo
pll->hw.init = &init;
pll->rate_table = pll_clk->rate_table;
pll->rate_count = pll_clk->rate_count;
+ pll->flags = pll_flags;
hw = &pll->hw;
@@ -326,4 +391,18 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo
return hw;
}
+
+struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk)
+{
+ return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_FRACN);
+}
EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll);
+
+struct clk_hw *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk)
+{
+ return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_INTEGER);
+}
+EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll_integer);
diff --git a/drivers/clk/imx/clk-gate-93.c b/drivers/clk/imx/clk-gate-93.c
new file mode 100644
index 000000000000..ceb56b290394
--- /dev/null
+++ b/drivers/clk/imx/clk-gate-93.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define DIRECT_OFFSET 0x0
+
+/*
+ * 0b000 - LPCG will be OFF in any CPU mode.
+ * 0b100 - LPCG will be ON in any CPU mode.
+ */
+#define LPM_SETTING_OFF 0x0
+#define LPM_SETTING_ON 0x4
+
+#define LPM_CUR_OFFSET 0x1c
+
+#define AUTHEN_OFFSET 0x30
+#define CPULPM_EN BIT(2)
+#define TZ_NS_SHIFT 9
+#define TZ_NS_MASK BIT(9)
+
+#define WHITE_LIST_SHIFT 16
+
+struct imx93_clk_gate {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u32 bit_idx;
+ u32 val;
+ u32 mask;
+ spinlock_t *lock;
+ unsigned int *share_count;
+};
+
+#define to_imx93_clk_gate(_hw) container_of(_hw, struct imx93_clk_gate, hw)
+
+static void imx93_clk_gate_do_hardware(struct clk_hw *hw, bool enable)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ u32 val;
+
+ val = readl(gate->reg + AUTHEN_OFFSET);
+ if (val & CPULPM_EN) {
+ val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF;
+ writel(val, gate->reg + LPM_CUR_OFFSET);
+ } else {
+ val = readl(gate->reg + DIRECT_OFFSET);
+ val &= ~(gate->mask << gate->bit_idx);
+ if (enable)
+ val |= (gate->val & gate->mask) << gate->bit_idx;
+ writel(val, gate->reg + DIRECT_OFFSET);
+ }
+}
+
+static int imx93_clk_gate_enable(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (gate->share_count && (*gate->share_count)++ > 0)
+ goto out;
+
+ imx93_clk_gate_do_hardware(hw, true);
+out:
+ spin_unlock_irqrestore(gate->lock, flags);
+
+ return 0;
+}
+
+static void imx93_clk_gate_disable(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (gate->share_count) {
+ if (WARN_ON(*gate->share_count == 0))
+ goto out;
+ else if (--(*gate->share_count) > 0)
+ goto out;
+ }
+
+ imx93_clk_gate_do_hardware(hw, false);
+out:
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int imx93_clk_gate_reg_is_enabled(struct imx93_clk_gate *gate)
+{
+ u32 val = readl(gate->reg + AUTHEN_OFFSET);
+
+ if (val & CPULPM_EN) {
+ val = readl(gate->reg + LPM_CUR_OFFSET);
+ if (val == LPM_SETTING_ON)
+ return 1;
+ } else {
+ val = readl(gate->reg);
+ if (((val >> gate->bit_idx) & gate->mask) == gate->val)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int imx93_clk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ ret = imx93_clk_gate_reg_is_enabled(gate);
+
+ spin_unlock_irqrestore(gate->lock, flags);
+
+ return ret;
+}
+
+static void imx93_clk_gate_disable_unused(struct clk_hw *hw)
+{
+ struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (!gate->share_count || *gate->share_count == 0)
+ imx93_clk_gate_do_hardware(hw, false);
+
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static const struct clk_ops imx93_clk_gate_ops = {
+ .enable = imx93_clk_gate_enable,
+ .disable = imx93_clk_gate_disable,
+ .disable_unused = imx93_clk_gate_disable_unused,
+ .is_enabled = imx93_clk_gate_is_enabled,
+};
+
+static const struct clk_ops imx93_clk_gate_ro_ops = {
+ .is_enabled = imx93_clk_gate_is_enabled,
+};
+
+struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
+ u32 mask, u32 domain_id, unsigned int *share_count)
+{
+ struct imx93_clk_gate *gate;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+ u32 authen;
+
+ gate = kzalloc(sizeof(struct imx93_clk_gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = reg;
+ gate->lock = &imx_ccm_lock;
+ gate->bit_idx = bit_idx;
+ gate->val = val;
+ gate->mask = mask;
+ gate->share_count = share_count;
+
+ init.name = name;
+ init.ops = &imx93_clk_gate_ops;
+ init.flags = flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+
+ gate->hw.init = &init;
+ hw = &gate->hw;
+
+ authen = readl(reg + AUTHEN_OFFSET);
+ if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
+ init.ops = &imx93_clk_gate_ro_ops;
+
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(gate);
+ return ERR_PTR(ret);
+ }
+
+ return hw;
+}
+EXPORT_SYMBOL_GPL(imx93_clk_gate);
diff --git a/drivers/clk/imx/clk-gate-exclusive.c b/drivers/clk/imx/clk-gate-exclusive.c
index 77342893bb71..7017e9d4e188 100644
--- a/drivers/clk/imx/clk-gate-exclusive.c
+++ b/drivers/clk/imx/clk-gate-exclusive.c
@@ -18,7 +18,7 @@
* gate clock
*
* The imx exclusive gate clock is a subclass of basic clk_gate
- * with an addtional mask to indicate which other gate bits in the same
+ * with an additional mask to indicate which other gate bits in the same
* register is mutually exclusive to this gate clock.
*/
struct clk_gate_exclusive {
diff --git a/drivers/clk/imx/clk-gpr-mux.c b/drivers/clk/imx/clk-gpr-mux.c
new file mode 100644
index 000000000000..0e14b61cba84
--- /dev/null
+++ b/drivers/clk/imx/clk-gpr-mux.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ */
+
+#define pr_fmt(fmt) "imx:clk-gpr-mux: " fmt
+
+#include <linux/module.h>
+
+#include <linux/clk-provider.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "clk.h"
+
+struct imx_clk_gpr {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ u32 mask;
+ u32 reg;
+ const u32 *mux_table;
+};
+
+static struct imx_clk_gpr *to_imx_clk_gpr(struct clk_hw *hw)
+{
+ return container_of(hw, struct imx_clk_gpr, hw);
+}
+
+static u8 imx_clk_gpr_mux_get_parent(struct clk_hw *hw)
+{
+ struct imx_clk_gpr *priv = to_imx_clk_gpr(hw);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(priv->regmap, priv->reg, &val);
+ if (ret)
+ goto get_parent_err;
+
+ val &= priv->mask;
+
+ ret = clk_mux_val_to_index(hw, priv->mux_table, 0, val);
+ if (ret < 0)
+ goto get_parent_err;
+
+ return ret;
+
+get_parent_err:
+ pr_err("%s: failed to get parent (%pe)\n",
+ clk_hw_get_name(hw), ERR_PTR(ret));
+
+ /* return some realistic non negative value. Potentially we could
+ * give index to some dummy error parent.
+ */
+ return 0;
+}
+
+static int imx_clk_gpr_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct imx_clk_gpr *priv = to_imx_clk_gpr(hw);
+ unsigned int val = clk_mux_index_to_val(priv->mux_table, 0, index);
+
+ return regmap_update_bits(priv->regmap, priv->reg, priv->mask, val);
+}
+
+static const struct clk_ops imx_clk_gpr_mux_ops = {
+ .get_parent = imx_clk_gpr_mux_get_parent,
+ .set_parent = imx_clk_gpr_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+
+struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible,
+ u32 reg, const char **parent_names,
+ u8 num_parents, const u32 *mux_table, u32 mask)
+{
+ struct clk_init_data init = { };
+ struct imx_clk_gpr *priv;
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ int ret;
+
+ regmap = syscon_regmap_lookup_by_compatible(compatible);
+ if (IS_ERR(regmap)) {
+ pr_err("failed to find %s regmap\n", compatible);
+ return ERR_CAST(regmap);
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &imx_clk_gpr_mux_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ priv->hw.init = &init;
+ priv->regmap = regmap;
+ priv->mux_table = mux_table;
+ priv->reg = reg;
+ priv->mask = mask;
+
+ hw = &priv->hw;
+ ret = clk_hw_register(NULL, &priv->hw);
+ if (ret) {
+ kfree(priv);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
diff --git a/drivers/clk/imx/clk-imx1.c b/drivers/clk/imx/clk-imx1.c
index 22fc7491ba00..f6ea7e5052d5 100644
--- a/drivers/clk/imx/clk-imx1.c
+++ b/drivers/clk/imx/clk-imx1.c
@@ -10,7 +10,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/imx1-clock.h>
-#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
diff --git a/drivers/clk/imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c
index 66192fe0a898..c566be848c2d 100644
--- a/drivers/clk/imx/clk-imx25.c
+++ b/drivers/clk/imx/clk-imx25.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <soc/imx/revision.h>
#include "clk.h"
@@ -73,7 +74,7 @@ enum mx25_clks {
static struct clk *clk[clk_max];
-static int __init __mx25_clocks_init(void __iomem *ccm_base)
+static void __init __mx25_clocks_init(void __iomem *ccm_base)
{
BUG_ON(!ccm_base);
@@ -218,9 +219,9 @@ static int __init __mx25_clocks_init(void __iomem *ccm_base)
*/
clk_set_parent(clk[cko_sel], clk[ipg]);
- imx_register_uart_clocks(6);
+ imx_register_uart_clocks();
- return 0;
+ imx_print_silicon_rev("i.MX25", mx25_revision());
}
static void __init mx25_clocks_init_dt(struct device_node *np)
diff --git a/drivers/clk/imx/clk-imx27.c b/drivers/clk/imx/clk-imx27.c
index 56a5fc402b10..99618ded0939 100644
--- a/drivers/clk/imx/clk-imx27.c
+++ b/drivers/clk/imx/clk-imx27.c
@@ -8,7 +8,6 @@
#include <linux/of_address.h>
#include <dt-bindings/clock/imx27-clock.h>
#include <soc/imx/revision.h>
-#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
@@ -165,7 +164,7 @@ static void __init _mx27_clocks_init(unsigned long fref)
clk_prepare_enable(clk[IMX27_CLK_EMI_AHB_GATE]);
- imx_register_uart_clocks(7);
+ imx_register_uart_clocks();
imx_print_silicon_rev("i.MX27", mx27_revision());
}
diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c
index c44e18c6f63f..4c8d9ff0b2ad 100644
--- a/drivers/clk/imx/clk-imx31.c
+++ b/drivers/clk/imx/clk-imx31.c
@@ -11,7 +11,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <soc/imx/revision.h>
-#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c
index 0fe5ac210156..3b6fdb4e0be7 100644
--- a/drivers/clk/imx/clk-imx35.c
+++ b/drivers/clk/imx/clk-imx35.c
@@ -10,7 +10,6 @@
#include <linux/of.h>
#include <linux/err.h>
#include <soc/imx/revision.h>
-#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
@@ -235,7 +234,7 @@ static void __init _mx35_clocks_init(void)
*/
clk_prepare_enable(clk[scc_gate]);
- imx_register_uart_clocks(4);
+ imx_register_uart_clocks();
imx_print_silicon_rev("i.MX35", mx35_revision());
}
diff --git a/drivers/clk/imx/clk-imx5.c b/drivers/clk/imx/clk-imx5.c
index e4493846454d..9c5f489b3975 100644
--- a/drivers/clk/imx/clk-imx5.c
+++ b/drivers/clk/imx/clk-imx5.c
@@ -358,7 +358,7 @@ static void __init mx50_clocks_init(struct device_node *np)
r = clk_round_rate(clk[IMX5_CLK_USBOH3_PER_GATE], 54000000);
clk_set_rate(clk[IMX5_CLK_USBOH3_PER_GATE], r);
- imx_register_uart_clocks(5);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx50_ccm, "fsl,imx50-ccm", mx50_clocks_init);
@@ -454,7 +454,7 @@ static void __init mx51_clocks_init(struct device_node *np)
* longer supported. Set to one for better power saving.
*
* The effect of not setting these bits is that MIPI clocks can't be
- * enabled without the IPU clock being enabled aswell.
+ * enabled without the IPU clock being enabled as well.
*/
val = readl(MXC_CCM_CCDR);
val |= 1 << 18;
@@ -464,7 +464,7 @@ static void __init mx51_clocks_init(struct device_node *np)
val |= 1 << 23;
writel(val, MXC_CCM_CLPCR);
- imx_register_uart_clocks(3);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx51_ccm, "fsl,imx51-ccm", mx51_clocks_init);
@@ -609,6 +609,6 @@ static void __init mx53_clocks_init(struct device_node *np)
r = clk_round_rate(clk[IMX5_CLK_USBOH3_PER_GATE], 54000000);
clk_set_rate(clk[IMX5_CLK_USBOH3_PER_GATE], r);
- imx_register_uart_clocks(5);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx53_ccm, "fsl,imx53-ccm", mx53_clocks_init);
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index de36f58d551c..bf4c1d9c9928 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -12,6 +12,7 @@
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -115,6 +116,10 @@ static struct clk_div_table video_div_table[] = {
{ /* sentinel */ }
};
+static const char * enet_ref_sels[] = { "enet_ref", "enet_ref_pad", };
+static const u32 enet_ref_sels_table[] = { IMX6Q_GPR1_ENET_CLK_SEL_ANATOP, IMX6Q_GPR1_ENET_CLK_SEL_PAD };
+static const u32 enet_ref_sels_table_mask = IMX6Q_GPR1_ENET_CLK_SEL_ANATOP;
+
static unsigned int share_count_esai;
static unsigned int share_count_asrc;
static unsigned int share_count_ssi1;
@@ -908,6 +913,12 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0)
hws[IMX6QDL_CLK_GPT_3M] = hws[IMX6QDL_CLK_GPT_IPG_PER];
+ hws[IMX6QDL_CLK_ENET_REF_PAD] = imx6q_obtain_fixed_clk_hw(ccm_node, "enet_ref_pad", 0);
+
+ hws[IMX6QDL_CLK_ENET_REF_SEL] = imx_clk_gpr_mux("enet_ref_sel", "fsl,imx6q-iomuxc-gpr",
+ IOMUXC_GPR1, enet_ref_sels, ARRAY_SIZE(enet_ref_sels),
+ enet_ref_sels_table, enet_ref_sels_table_mask);
+
imx_check_clk_hws(hws, IMX6QDL_CLK_END);
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
@@ -974,6 +985,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
hws[IMX6QDL_CLK_PLL3_USB_OTG]->clk);
}
- imx_register_uart_clocks(2);
+ clk_set_parent(hws[IMX6QDL_CLK_ENET_REF_SEL]->clk, hws[IMX6QDL_CLK_ENET_REF]->clk);
+
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init);
diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c
index 277365970320..47b8667cfa3f 100644
--- a/drivers/clk/imx/clk-imx6sl.c
+++ b/drivers/clk/imx/clk-imx6sl.c
@@ -440,6 +440,6 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
clk_set_parent(hws[IMX6SL_CLK_LCDIF_AXI_SEL]->clk,
hws[IMX6SL_CLK_PLL2_PFD2]->clk);
- imx_register_uart_clocks(2);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx6sl, "fsl,imx6sl-ccm", imx6sl_clocks_init);
diff --git a/drivers/clk/imx/clk-imx6sll.c b/drivers/clk/imx/clk-imx6sll.c
index 31d777f30039..2fa70bf35e45 100644
--- a/drivers/clk/imx/clk-imx6sll.c
+++ b/drivers/clk/imx/clk-imx6sll.c
@@ -91,12 +91,12 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node)
hws[IMX6SLL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX6SLL_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil");
- hws[IMX6SLL_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc");
+ hws[IMX6SLL_CLK_CKIL] = imx_get_clk_hw_by_name(ccm_node, "ckil");
+ hws[IMX6SLL_CLK_OSC] = imx_get_clk_hw_by_name(ccm_node, "osc");
/* ipp_di clock is external input */
- hws[IMX6SLL_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0");
- hws[IMX6SLL_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1");
+ hws[IMX6SLL_CLK_IPP_DI0] = imx_get_clk_hw_by_name(ccm_node, "ipp_di0");
+ hws[IMX6SLL_CLK_IPP_DI1] = imx_get_clk_hw_by_name(ccm_node, "ipp_di1");
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sll-anatop");
base = of_iomap(np, 0);
@@ -340,7 +340,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node)
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
- imx_register_uart_clocks(5);
+ imx_register_uart_clocks();
/* Lower the AHB clock rate before changing the clock source. */
clk_set_rate(hws[IMX6SLL_CLK_AHB]->clk, 99000000);
diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c
index fc1bd23d4583..69f8f6f9ca49 100644
--- a/drivers/clk/imx/clk-imx6sx.c
+++ b/drivers/clk/imx/clk-imx6sx.c
@@ -121,6 +121,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
void __iomem *base;
+ bool lcdif1_assigned_clk;
clk_hw_data = kzalloc(struct_size(clk_hw_data, hws,
IMX6SX_CLK_CLK_END), GFP_KERNEL);
@@ -132,16 +133,16 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
hws[IMX6SX_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX6SX_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil");
- hws[IMX6SX_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc");
+ hws[IMX6SX_CLK_CKIL] = imx_get_clk_hw_by_name(ccm_node, "ckil");
+ hws[IMX6SX_CLK_OSC] = imx_get_clk_hw_by_name(ccm_node, "osc");
/* ipp_di clock is external input */
- hws[IMX6SX_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0");
- hws[IMX6SX_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1");
+ hws[IMX6SX_CLK_IPP_DI0] = imx_get_clk_hw_by_name(ccm_node, "ipp_di0");
+ hws[IMX6SX_CLK_IPP_DI1] = imx_get_clk_hw_by_name(ccm_node, "ipp_di1");
/* Clock source from external clock via CLK1/2 PAD */
- hws[IMX6SX_CLK_ANACLK1] = imx_obtain_fixed_clk_hw(ccm_node, "anaclk1");
- hws[IMX6SX_CLK_ANACLK2] = imx_obtain_fixed_clk_hw(ccm_node, "anaclk2");
+ hws[IMX6SX_CLK_ANACLK1] = imx_get_clk_hw_by_name(ccm_node, "anaclk1");
+ hws[IMX6SX_CLK_ANACLK2] = imx_get_clk_hw_by_name(ccm_node, "anaclk2");
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop");
base = of_iomap(np, 0);
@@ -280,13 +281,13 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
hws[IMX6SX_CLK_SSI3_SEL] = imx_clk_hw_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels));
hws[IMX6SX_CLK_SSI2_SEL] = imx_clk_hw_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels));
hws[IMX6SX_CLK_SSI1_SEL] = imx_clk_hw_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels));
- hws[IMX6SX_CLK_QSPI1_SEL] = imx_clk_hw_mux_flags("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels), CLK_SET_RATE_PARENT);
+ hws[IMX6SX_CLK_QSPI1_SEL] = imx_clk_hw_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels));
hws[IMX6SX_CLK_PERCLK_SEL] = imx_clk_hw_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels));
hws[IMX6SX_CLK_VID_SEL] = imx_clk_hw_mux("vid_sel", base + 0x20, 21, 3, vid_sels, ARRAY_SIZE(vid_sels));
hws[IMX6SX_CLK_ESAI_SEL] = imx_clk_hw_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels));
hws[IMX6SX_CLK_CAN_SEL] = imx_clk_hw_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels));
hws[IMX6SX_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels));
- hws[IMX6SX_CLK_QSPI2_SEL] = imx_clk_hw_mux_flags("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels), CLK_SET_RATE_PARENT);
+ hws[IMX6SX_CLK_QSPI2_SEL] = imx_clk_hw_mux("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels));
hws[IMX6SX_CLK_SPDIF_SEL] = imx_clk_hw_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels));
hws[IMX6SX_CLK_AUDIO_SEL] = imx_clk_hw_mux("audio_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels));
hws[IMX6SX_CLK_ENET_PRE_SEL] = imx_clk_hw_mux("enet_pre_sel", base + 0x34, 15, 3, enet_pre_sels, ARRAY_SIZE(enet_pre_sels));
@@ -302,10 +303,10 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
hws[IMX6SX_CLK_CKO2_SEL] = imx_clk_hw_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels));
hws[IMX6SX_CLK_CKO] = imx_clk_hw_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels));
- hws[IMX6SX_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT);
- hws[IMX6SX_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT);
- hws[IMX6SX_CLK_LDB_DI1_SEL] = imx_clk_hw_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di1_sels, ARRAY_SIZE(ldb_di1_sels), CLK_SET_RATE_PARENT);
- hws[IMX6SX_CLK_LDB_DI0_SEL] = imx_clk_hw_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels), CLK_SET_RATE_PARENT);
+ hws[IMX6SX_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels));
+ hws[IMX6SX_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels));
+ hws[IMX6SX_CLK_LDB_DI1_SEL] = imx_clk_hw_mux("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di1_sels, ARRAY_SIZE(ldb_di1_sels));
+ hws[IMX6SX_CLK_LDB_DI0_SEL] = imx_clk_hw_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels));
hws[IMX6SX_CLK_LCDIF1_PRE_SEL] = imx_clk_hw_mux_flags("lcdif1_pre_sel", base + 0x38, 15, 3, lcdif1_pre_sels, ARRAY_SIZE(lcdif1_pre_sels), CLK_SET_RATE_PARENT);
hws[IMX6SX_CLK_LCDIF1_SEL] = imx_clk_hw_mux_flags("lcdif1_sel", base + 0x38, 9, 3, lcdif1_sels, ARRAY_SIZE(lcdif1_sels), CLK_SET_RATE_PARENT);
@@ -498,9 +499,16 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clk_set_parent(hws[IMX6SX_CLK_EIM_SLOW_SEL]->clk, hws[IMX6SX_CLK_PLL2_PFD2]->clk);
clk_set_rate(hws[IMX6SX_CLK_EIM_SLOW]->clk, 132000000);
- /* set parent clock for LCDIF1 pixel clock */
- clk_set_parent(hws[IMX6SX_CLK_LCDIF1_PRE_SEL]->clk, hws[IMX6SX_CLK_PLL5_VIDEO_DIV]->clk);
- clk_set_parent(hws[IMX6SX_CLK_LCDIF1_SEL]->clk, hws[IMX6SX_CLK_LCDIF1_PODF]->clk);
+ np = of_find_node_by_path("/soc/bus@2200000/spba-bus@2240000/lcdif@2220000");
+ lcdif1_assigned_clk = of_find_property(np, "assigned-clock-parents", NULL);
+
+ /* Set parent clock for LCDIF1 pixel clock if not done via devicetree */
+ if (!lcdif1_assigned_clk) {
+ clk_set_parent(hws[IMX6SX_CLK_LCDIF1_PRE_SEL]->clk,
+ hws[IMX6SX_CLK_PLL5_VIDEO_DIV]->clk);
+ clk_set_parent(hws[IMX6SX_CLK_LCDIF1_SEL]->clk,
+ hws[IMX6SX_CLK_LCDIF1_PODF]->clk);
+ }
/* Set the parent clks of PCIe lvds1 and pcie_axi to be pcie ref, axi */
if (clk_set_parent(hws[IMX6SX_CLK_LVDS1_SEL]->clk, hws[IMX6SX_CLK_PCIE_REF_125M]->clk))
@@ -548,6 +556,6 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clk_set_parent(hws[IMX6SX_CLK_QSPI1_SEL]->clk, hws[IMX6SX_CLK_PLL2_BUS]->clk);
clk_set_parent(hws[IMX6SX_CLK_QSPI2_SEL]->clk, hws[IMX6SX_CLK_PLL2_BUS]->clk);
- imx_register_uart_clocks(2);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init);
diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c
index 520b100bff4b..05c7a82b751f 100644
--- a/drivers/clk/imx/clk-imx6ul.c
+++ b/drivers/clk/imx/clk-imx6ul.c
@@ -10,6 +10,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -94,6 +95,19 @@ static const struct clk_div_table video_div_table[] = {
{ }
};
+static const char * enet1_ref_sels[] = { "enet1_ref_125m", "enet1_ref_pad", "dummy", "dummy"};
+static const u32 enet1_ref_sels_table[] = { IMX6UL_GPR1_ENET1_TX_CLK_DIR,
+ IMX6UL_GPR1_ENET1_CLK_SEL, 0,
+ IMX6UL_GPR1_ENET1_TX_CLK_DIR | IMX6UL_GPR1_ENET1_CLK_SEL };
+static const u32 enet1_ref_sels_table_mask = IMX6UL_GPR1_ENET1_TX_CLK_DIR |
+ IMX6UL_GPR1_ENET1_CLK_SEL;
+static const char * enet2_ref_sels[] = { "enet2_ref_125m", "enet2_ref_pad", "dummy", "dummy"};
+static const u32 enet2_ref_sels_table[] = { IMX6UL_GPR1_ENET2_TX_CLK_DIR,
+ IMX6UL_GPR1_ENET2_CLK_SEL, 0,
+ IMX6UL_GPR1_ENET2_TX_CLK_DIR | IMX6UL_GPR1_ENET2_CLK_SEL };
+static const u32 enet2_ref_sels_table_mask = IMX6UL_GPR1_ENET2_TX_CLK_DIR |
+ IMX6UL_GPR1_ENET2_CLK_SEL;
+
static u32 share_count_asrc;
static u32 share_count_audio;
static u32 share_count_sai1;
@@ -126,12 +140,12 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6UL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX6UL_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil");
- hws[IMX6UL_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc");
+ hws[IMX6UL_CLK_CKIL] = imx_get_clk_hw_by_name(ccm_node, "ckil");
+ hws[IMX6UL_CLK_OSC] = imx_get_clk_hw_by_name(ccm_node, "osc");
/* ipp_di clock is external input */
- hws[IMX6UL_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0");
- hws[IMX6UL_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1");
+ hws[IMX6UL_CLK_IPP_DI0] = imx_get_clk_hw_by_name(ccm_node, "ipp_di0");
+ hws[IMX6UL_CLK_IPP_DI1] = imx_get_clk_hw_by_name(ccm_node, "ipp_di1");
np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-anatop");
base = of_iomap(np, 0);
@@ -176,7 +190,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6UL_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13);
hws[IMX6UL_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13);
hws[IMX6UL_CLK_PLL5_VIDEO] = imx_clk_hw_gate("pll5_video", "pll5_bypass", base + 0xa0, 13);
- hws[IMX6UL_CLK_PLL6_ENET] = imx_clk_hw_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13);
+ hws[IMX6UL_CLK_PLL6_ENET] = imx_clk_hw_fixed_factor("pll6_enet", "pll6_bypass", 1, 1);
hws[IMX6UL_CLK_PLL7_USB_HOST] = imx_clk_hw_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13);
/*
@@ -205,12 +219,13 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6UL_CLK_PLL3_PFD2] = imx_clk_hw_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2);
hws[IMX6UL_CLK_PLL3_PFD3] = imx_clk_hw_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3);
- hws[IMX6UL_CLK_ENET_REF] = clk_hw_register_divider_table(NULL, "enet_ref", "pll6_enet", 0,
+ hws[IMX6UL_CLK_ENET_REF] = clk_hw_register_divider_table(NULL, "enet1_ref", "pll6_enet", 0,
base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock);
hws[IMX6UL_CLK_ENET2_REF] = clk_hw_register_divider_table(NULL, "enet2_ref", "pll6_enet", 0,
base + 0xe0, 2, 2, 0, clk_enet_ref_table, &imx_ccm_lock);
- hws[IMX6UL_CLK_ENET2_REF_125M] = imx_clk_hw_gate("enet_ref_125m", "enet2_ref", base + 0xe0, 20);
+ hws[IMX6UL_CLK_ENET1_REF_125M] = imx_clk_hw_gate("enet1_ref_125m", "enet1_ref", base + 0xe0, 13);
+ hws[IMX6UL_CLK_ENET2_REF_125M] = imx_clk_hw_gate("enet2_ref_125m", "enet2_ref", base + 0xe0, 20);
hws[IMX6UL_CLK_ENET_PTP_REF] = imx_clk_hw_fixed_factor("enet_ptp_ref", "pll6_enet", 1, 20);
hws[IMX6UL_CLK_ENET_PTP] = imx_clk_hw_gate("enet_ptp", "enet_ptp_ref", base + 0xe0, 21);
@@ -471,6 +486,17 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
/* mask handshake of mmdc */
imx_mmdc_mask_handshake(base, 0);
+ hws[IMX6UL_CLK_ENET1_REF_PAD] = imx_obtain_fixed_of_clock(ccm_node, "enet1_ref_pad", 0);
+
+ hws[IMX6UL_CLK_ENET1_REF_SEL] = imx_clk_gpr_mux("enet1_ref_sel", "fsl,imx6ul-iomuxc-gpr",
+ IOMUXC_GPR1, enet1_ref_sels, ARRAY_SIZE(enet1_ref_sels),
+ enet1_ref_sels_table, enet1_ref_sels_table_mask);
+ hws[IMX6UL_CLK_ENET2_REF_PAD] = imx_obtain_fixed_of_clock(ccm_node, "enet2_ref_pad", 0);
+
+ hws[IMX6UL_CLK_ENET2_REF_SEL] = imx_clk_gpr_mux("enet2_ref_sel", "fsl,imx6ul-iomuxc-gpr",
+ IOMUXC_GPR1, enet2_ref_sels, ARRAY_SIZE(enet2_ref_sels),
+ enet2_ref_sels_table, enet2_ref_sels_table_mask);
+
imx_check_clk_hws(hws, IMX6UL_CLK_END);
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
@@ -515,6 +541,11 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clk_set_parent(hws[IMX6ULL_CLK_EPDC_PRE_SEL]->clk, hws[IMX6UL_CLK_PLL3_PFD2]->clk);
clk_set_parent(hws[IMX6UL_CLK_ENFC_SEL]->clk, hws[IMX6UL_CLK_PLL2_PFD2]->clk);
+
+ clk_set_parent(hws[IMX6UL_CLK_ENET1_REF_SEL]->clk, hws[IMX6UL_CLK_ENET1_REF_125M]->clk);
+ clk_set_parent(hws[IMX6UL_CLK_ENET2_REF_SEL]->clk, hws[IMX6UL_CLK_ENET2_REF_125M]->clk);
+
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx6ul, "fsl,imx6ul-ccm", imx6ul_clocks_init);
diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index cbf8131c63f7..99adc55e3f5d 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -391,8 +391,8 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
hws = clk_hw_data->hws;
hws[IMX7D_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX7D_OSC_24M_CLK] = imx_obtain_fixed_clk_hw(ccm_node, "osc");
- hws[IMX7D_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil");
+ hws[IMX7D_OSC_24M_CLK] = imx_get_clk_hw_by_name(ccm_node, "osc");
+ hws[IMX7D_CKIL] = imx_get_clk_hw_by_name(ccm_node, "ckil");
np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop");
base = of_iomap(np, 0);
@@ -498,14 +498,14 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
hws[IMX7D_ENET_AXI_ROOT_SRC] = imx_clk_hw_mux2_flags("enet_axi_src", base + 0x8900, 24, 3, enet_axi_sel, ARRAY_SIZE(enet_axi_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_NAND_USDHC_BUS_ROOT_SRC] = imx_clk_hw_mux2_flags("nand_usdhc_src", base + 0x8980, 24, 3, nand_usdhc_bus_sel, ARRAY_SIZE(nand_usdhc_bus_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_DRAM_PHYM_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_phym_src", base + 0x9800, 24, 1, dram_phym_sel, ARRAY_SIZE(dram_phym_sel), CLK_SET_PARENT_GATE);
- hws[IMX7D_DRAM_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_src", base + 0x9880, 24, 1, dram_sel, ARRAY_SIZE(dram_sel), CLK_SET_PARENT_GATE);
+ hws[IMX7D_DRAM_ROOT_SRC] = imx_clk_hw_mux2("dram_src", base + 0x9880, 24, 1, dram_sel, ARRAY_SIZE(dram_sel));
hws[IMX7D_DRAM_PHYM_ALT_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_phym_alt_src", base + 0xa000, 24, 3, dram_phym_alt_sel, ARRAY_SIZE(dram_phym_alt_sel), CLK_SET_PARENT_GATE);
- hws[IMX7D_DRAM_ALT_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_alt_src", base + 0xa080, 24, 3, dram_alt_sel, ARRAY_SIZE(dram_alt_sel), CLK_SET_PARENT_GATE);
+ hws[IMX7D_DRAM_ALT_ROOT_SRC] = imx_clk_hw_mux2("dram_alt_src", base + 0xa080, 24, 3, dram_alt_sel, ARRAY_SIZE(dram_alt_sel));
hws[IMX7D_USB_HSIC_ROOT_SRC] = imx_clk_hw_mux2_flags("usb_hsic_src", base + 0xa100, 24, 3, usb_hsic_sel, ARRAY_SIZE(usb_hsic_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_PCIE_CTRL_ROOT_SRC] = imx_clk_hw_mux2_flags("pcie_ctrl_src", base + 0xa180, 24, 3, pcie_ctrl_sel, ARRAY_SIZE(pcie_ctrl_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_PCIE_PHY_ROOT_SRC] = imx_clk_hw_mux2_flags("pcie_phy_src", base + 0xa200, 24, 3, pcie_phy_sel, ARRAY_SIZE(pcie_phy_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_EPDC_PIXEL_ROOT_SRC] = imx_clk_hw_mux2_flags("epdc_pixel_src", base + 0xa280, 24, 3, epdc_pixel_sel, ARRAY_SIZE(epdc_pixel_sel), CLK_SET_PARENT_GATE);
- hws[IMX7D_LCDIF_PIXEL_ROOT_SRC] = imx_clk_hw_mux2_flags("lcdif_pixel_src", base + 0xa300, 24, 3, lcdif_pixel_sel, ARRAY_SIZE(lcdif_pixel_sel), CLK_SET_PARENT_GATE);
+ hws[IMX7D_LCDIF_PIXEL_ROOT_SRC] = imx_clk_hw_mux2_flags("lcdif_pixel_src", base + 0xa300, 24, 3, lcdif_pixel_sel, ARRAY_SIZE(lcdif_pixel_sel), CLK_SET_PARENT_GATE | CLK_SET_RATE_PARENT);
hws[IMX7D_MIPI_DSI_ROOT_SRC] = imx_clk_hw_mux2_flags("mipi_dsi_src", base + 0xa380, 24, 3, mipi_dsi_sel, ARRAY_SIZE(mipi_dsi_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_MIPI_CSI_ROOT_SRC] = imx_clk_hw_mux2_flags("mipi_csi_src", base + 0xa400, 24, 3, mipi_csi_sel, ARRAY_SIZE(mipi_csi_sel), CLK_SET_PARENT_GATE);
hws[IMX7D_MIPI_DPHY_ROOT_SRC] = imx_clk_hw_mux2_flags("mipi_dphy_src", base + 0xa480, 24, 3, mipi_dphy_sel, ARRAY_SIZE(mipi_dphy_sel), CLK_SET_PARENT_GATE);
@@ -882,7 +882,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
hws[IMX7D_USB1_MAIN_480M_CLK] = imx_clk_hw_fixed_factor("pll_usb1_main_clk", "osc", 20, 1);
hws[IMX7D_USB_MAIN_480M_CLK] = imx_clk_hw_fixed_factor("pll_usb_main_clk", "osc", 20, 1);
- imx_register_uart_clocks(7);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx7d, "fsl,imx7d-ccm", imx7d_clocks_init);
diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c
index b6e45e77ee39..f4a48a42637f 100644
--- a/drivers/clk/imx/clk-imx7ulp.c
+++ b/drivers/clk/imx/clk-imx7ulp.c
@@ -59,11 +59,11 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np)
hws[IMX7ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX7ULP_CLK_ROSC] = imx_obtain_fixed_clk_hw(np, "rosc");
- hws[IMX7ULP_CLK_SOSC] = imx_obtain_fixed_clk_hw(np, "sosc");
- hws[IMX7ULP_CLK_SIRC] = imx_obtain_fixed_clk_hw(np, "sirc");
- hws[IMX7ULP_CLK_FIRC] = imx_obtain_fixed_clk_hw(np, "firc");
- hws[IMX7ULP_CLK_UPLL] = imx_obtain_fixed_clk_hw(np, "upll");
+ hws[IMX7ULP_CLK_ROSC] = imx_get_clk_hw_by_name(np, "rosc");
+ hws[IMX7ULP_CLK_SOSC] = imx_get_clk_hw_by_name(np, "sosc");
+ hws[IMX7ULP_CLK_SIRC] = imx_get_clk_hw_by_name(np, "sirc");
+ hws[IMX7ULP_CLK_FIRC] = imx_get_clk_hw_by_name(np, "firc");
+ hws[IMX7ULP_CLK_UPLL] = imx_get_clk_hw_by_name(np, "upll");
/* SCG1 */
base = of_iomap(np, 0);
@@ -176,7 +176,7 @@ static void __init imx7ulp_clk_pcc2_init(struct device_node *np)
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
- imx_register_uart_clocks(2);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx7ulp_clk_pcc2, "fsl,imx7ulp-pcc2", imx7ulp_clk_pcc2_init);
@@ -223,7 +223,7 @@ static void __init imx7ulp_clk_pcc3_init(struct device_node *np)
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
- imx_register_uart_clocks(7);
+ imx_register_uart_clocks();
}
CLK_OF_DECLARE(imx7ulp_clk_pcc3, "fsl,imx7ulp-pcc3", imx7ulp_clk_pcc3_init);
diff --git a/drivers/clk/imx/clk-imx8-acm.c b/drivers/clk/imx/clk-imx8-acm.c
new file mode 100644
index 000000000000..790f7e44b11e
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8-acm.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright 2023 NXP
+//
+
+#include <dt-bindings/clock/imx8-clock.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+/**
+ * struct clk_imx_acm_pm_domains - structure for multi power domain
+ * @pd_dev: power domain device
+ * @pd_dev_link: power domain device link
+ * @num_domains: power domain number
+ */
+struct clk_imx_acm_pm_domains {
+ struct device **pd_dev;
+ struct device_link **pd_dev_link;
+ int num_domains;
+};
+
+/**
+ * struct clk_imx8_acm_sel - for clock mux
+ * @name: clock name
+ * @clkid: clock id
+ * @parents: clock parents
+ * @num_parents: clock parents number
+ * @reg: register offset
+ * @shift: bit shift in register
+ * @width: bits width
+ */
+struct clk_imx8_acm_sel {
+ const char *name;
+ int clkid;
+ const struct clk_parent_data *parents; /* For mux */
+ int num_parents;
+ u32 reg;
+ u8 shift;
+ u8 width;
+};
+
+/**
+ * struct imx8_acm_soc_data - soc specific data
+ * @sels: pointer to struct clk_imx8_acm_sel
+ * @num_sels: numbers of items
+ * @mclk_sels: pointer to imx8qm/qxp/dxl_mclk_sels
+ */
+struct imx8_acm_soc_data {
+ struct clk_imx8_acm_sel *sels;
+ unsigned int num_sels;
+ struct clk_parent_data *mclk_sels;
+};
+
+/**
+ * struct imx8_acm_priv - private structure
+ * @dev_pm: multi power domain
+ * @soc_data: pointer to soc data
+ * @reg: base address of registers
+ * @regs: save registers for suspend
+ */
+struct imx8_acm_priv {
+ struct clk_imx_acm_pm_domains dev_pm;
+ const struct imx8_acm_soc_data *soc_data;
+ void __iomem *reg;
+ u32 regs[IMX_ADMA_ACM_CLK_END];
+};
+
+static const struct clk_parent_data imx8qm_aud_clk_sels[] = {
+ { .fw_name = "aud_rec_clk0_lpcg_clk" },
+ { .fw_name = "aud_rec_clk1_lpcg_clk" },
+ { .fw_name = "dummy" },
+ { .fw_name = "hdmi_rx_mclk" },
+ { .fw_name = "ext_aud_mclk0" },
+ { .fw_name = "ext_aud_mclk1" },
+ { .fw_name = "esai0_rx_clk" },
+ { .fw_name = "esai0_rx_hf_clk" },
+ { .fw_name = "esai0_tx_clk" },
+ { .fw_name = "esai0_tx_hf_clk" },
+ { .fw_name = "esai1_rx_clk" },
+ { .fw_name = "esai1_rx_hf_clk" },
+ { .fw_name = "esai1_tx_clk" },
+ { .fw_name = "esai1_tx_hf_clk" },
+ { .fw_name = "spdif0_rx" },
+ { .fw_name = "spdif1_rx" },
+ { .fw_name = "sai0_rx_bclk" },
+ { .fw_name = "sai0_tx_bclk" },
+ { .fw_name = "sai1_rx_bclk" },
+ { .fw_name = "sai1_tx_bclk" },
+ { .fw_name = "sai2_rx_bclk" },
+ { .fw_name = "sai3_rx_bclk" },
+ { .fw_name = "sai4_rx_bclk" },
+};
+
+static const struct clk_parent_data imx8qm_mclk_out_sels[] = {
+ { .fw_name = "aud_rec_clk0_lpcg_clk" },
+ { .fw_name = "aud_rec_clk1_lpcg_clk" },
+ { .fw_name = "dummy" },
+ { .fw_name = "hdmi_rx_mclk" },
+ { .fw_name = "spdif0_rx" },
+ { .fw_name = "spdif1_rx" },
+ { .fw_name = "sai4_rx_bclk" },
+ { .fw_name = "sai6_rx_bclk" },
+};
+
+#define ACM_AUD_CLK0_SEL_INDEX 2
+#define ACM_AUD_CLK1_SEL_INDEX 3
+
+static struct clk_parent_data imx8qm_mclk_sels[] = {
+ { .fw_name = "aud_pll_div_clk0_lpcg_clk" },
+ { .fw_name = "aud_pll_div_clk1_lpcg_clk" },
+ { }, /* clk_hw pointer of "acm_aud_clk0_sel" */
+ { }, /* clk_hw pointer of "acm_aud_clk1_sel" */
+};
+
+static const struct clk_parent_data imx8qm_asrc_mux_clk_sels[] = {
+ { .fw_name = "sai4_rx_bclk" },
+ { .fw_name = "sai5_tx_bclk" },
+ { .index = -1 },
+ { .fw_name = "dummy" },
+
+};
+
+static struct clk_imx8_acm_sel imx8qm_sels[] = {
+ { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8qm_aud_clk_sels, ARRAY_SIZE(imx8qm_aud_clk_sels), 0x000000, 0, 5 },
+ { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8qm_aud_clk_sels, ARRAY_SIZE(imx8qm_aud_clk_sels), 0x010000, 0, 5 },
+ { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8qm_mclk_out_sels, ARRAY_SIZE(imx8qm_mclk_out_sels), 0x020000, 0, 3 },
+ { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8qm_mclk_out_sels, ARRAY_SIZE(imx8qm_mclk_out_sels), 0x030000, 0, 3 },
+ { "acm_asrc0_mclk_sel", IMX_ADMA_ACM_ASRC0_MUX_CLK_SEL, imx8qm_asrc_mux_clk_sels, ARRAY_SIZE(imx8qm_asrc_mux_clk_sels), 0x040000, 0, 2 },
+ { "acm_esai0_mclk_sel", IMX_ADMA_ACM_ESAI0_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x060000, 0, 2 },
+ { "acm_esai1_mclk_sel", IMX_ADMA_ACM_ESAI1_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x070000, 0, 2 },
+ { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x0E0000, 0, 2 },
+ { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x0F0000, 0, 2 },
+ { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x100000, 0, 2 },
+ { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x110000, 0, 2 },
+ { "acm_sai4_mclk_sel", IMX_ADMA_ACM_SAI4_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x120000, 0, 2 },
+ { "acm_sai5_mclk_sel", IMX_ADMA_ACM_SAI5_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x130000, 0, 2 },
+ { "acm_sai6_mclk_sel", IMX_ADMA_ACM_SAI6_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x140000, 0, 2 },
+ { "acm_sai7_mclk_sel", IMX_ADMA_ACM_SAI7_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x150000, 0, 2 },
+ { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1A0000, 0, 2 },
+ { "acm_spdif1_mclk_sel", IMX_ADMA_ACM_SPDIF1_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1B0000, 0, 2 },
+ { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1C0000, 0, 2 },
+};
+
+static const struct clk_parent_data imx8qxp_aud_clk_sels[] = {
+ { .fw_name = "aud_rec_clk0_lpcg_clk" },
+ { .fw_name = "aud_rec_clk1_lpcg_clk" },
+ { .fw_name = "ext_aud_mclk0" },
+ { .fw_name = "ext_aud_mclk1" },
+ { .fw_name = "esai0_rx_clk" },
+ { .fw_name = "esai0_rx_hf_clk" },
+ { .fw_name = "esai0_tx_clk" },
+ { .fw_name = "esai0_tx_hf_clk" },
+ { .fw_name = "spdif0_rx" },
+ { .fw_name = "sai0_rx_bclk" },
+ { .fw_name = "sai0_tx_bclk" },
+ { .fw_name = "sai1_rx_bclk" },
+ { .fw_name = "sai1_tx_bclk" },
+ { .fw_name = "sai2_rx_bclk" },
+ { .fw_name = "sai3_rx_bclk" },
+};
+
+static const struct clk_parent_data imx8qxp_mclk_out_sels[] = {
+ { .fw_name = "aud_rec_clk0_lpcg_clk" },
+ { .fw_name = "aud_rec_clk1_lpcg_clk" },
+ { .index = -1 },
+ { .index = -1 },
+ { .fw_name = "spdif0_rx" },
+ { .index = -1 },
+ { .index = -1 },
+ { .fw_name = "sai4_rx_bclk" },
+};
+
+static struct clk_parent_data imx8qxp_mclk_sels[] = {
+ { .fw_name = "aud_pll_div_clk0_lpcg_clk" },
+ { .fw_name = "aud_pll_div_clk1_lpcg_clk" },
+ { }, /* clk_hw pointer of "acm_aud_clk0_sel" */
+ { }, /* clk_hw pointer of "acm_aud_clk1_sel" */
+};
+
+static struct clk_imx8_acm_sel imx8qxp_sels[] = {
+ { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8qxp_aud_clk_sels, ARRAY_SIZE(imx8qxp_aud_clk_sels), 0x000000, 0, 5 },
+ { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8qxp_aud_clk_sels, ARRAY_SIZE(imx8qxp_aud_clk_sels), 0x010000, 0, 5 },
+ { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8qxp_mclk_out_sels, ARRAY_SIZE(imx8qxp_mclk_out_sels), 0x020000, 0, 3 },
+ { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8qxp_mclk_out_sels, ARRAY_SIZE(imx8qxp_mclk_out_sels), 0x030000, 0, 3 },
+ { "acm_esai0_mclk_sel", IMX_ADMA_ACM_ESAI0_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x060000, 0, 2 },
+ { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x0E0000, 0, 2 },
+ { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x0F0000, 0, 2 },
+ { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x100000, 0, 2 },
+ { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x110000, 0, 2 },
+ { "acm_sai4_mclk_sel", IMX_ADMA_ACM_SAI4_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x140000, 0, 2 },
+ { "acm_sai5_mclk_sel", IMX_ADMA_ACM_SAI5_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x150000, 0, 2 },
+ { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x1A0000, 0, 2 },
+ { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x1C0000, 0, 2 },
+};
+
+static const struct clk_parent_data imx8dxl_aud_clk_sels[] = {
+ { .fw_name = "aud_rec_clk0_lpcg_clk" },
+ { .fw_name = "aud_rec_clk1_lpcg_clk" },
+ { .fw_name = "ext_aud_mclk0" },
+ { .fw_name = "ext_aud_mclk1" },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+ { .fw_name = "spdif0_rx" },
+ { .fw_name = "sai0_rx_bclk" },
+ { .fw_name = "sai0_tx_bclk" },
+ { .fw_name = "sai1_rx_bclk" },
+ { .fw_name = "sai1_tx_bclk" },
+ { .fw_name = "sai2_rx_bclk" },
+ { .fw_name = "sai3_rx_bclk" },
+};
+
+static const struct clk_parent_data imx8dxl_mclk_out_sels[] = {
+ { .fw_name = "aud_rec_clk0_lpcg_clk" },
+ { .fw_name = "aud_rec_clk1_lpcg_clk" },
+ { .index = -1 },
+ { .index = -1 },
+ { .fw_name = "spdif0_rx" },
+ { .index = -1 },
+ { .index = -1 },
+ { .index = -1 },
+};
+
+static struct clk_parent_data imx8dxl_mclk_sels[] = {
+ { .fw_name = "aud_pll_div_clk0_lpcg_clk" },
+ { .fw_name = "aud_pll_div_clk1_lpcg_clk" },
+ { }, /* clk_hw pointer of "acm_aud_clk0_sel" */
+ { }, /* clk_hw pointer of "acm_aud_clk1_sel" */
+};
+
+static struct clk_imx8_acm_sel imx8dxl_sels[] = {
+ { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x000000, 0, 5 },
+ { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x010000, 0, 5 },
+ { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x020000, 0, 3 },
+ { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x030000, 0, 3 },
+ { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0E0000, 0, 2 },
+ { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0F0000, 0, 2 },
+ { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x100000, 0, 2 },
+ { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x110000, 0, 2 },
+ { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x1A0000, 0, 2 },
+ { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x1C0000, 0, 2 },
+};
+
+/**
+ * clk_imx_acm_attach_pm_domains: attach multi power domains
+ * @dev: device pointer
+ * @dev_pm: power domains for device
+ */
+static int clk_imx_acm_attach_pm_domains(struct device *dev,
+ struct clk_imx_acm_pm_domains *dev_pm)
+{
+ int ret;
+ int i;
+
+ dev_pm->num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
+ "#power-domain-cells");
+ if (dev_pm->num_domains <= 1)
+ return 0;
+
+ dev_pm->pd_dev = devm_kmalloc_array(dev, dev_pm->num_domains,
+ sizeof(*dev_pm->pd_dev),
+ GFP_KERNEL);
+ if (!dev_pm->pd_dev)
+ return -ENOMEM;
+
+ dev_pm->pd_dev_link = devm_kmalloc_array(dev,
+ dev_pm->num_domains,
+ sizeof(*dev_pm->pd_dev_link),
+ GFP_KERNEL);
+ if (!dev_pm->pd_dev_link)
+ return -ENOMEM;
+
+ for (i = 0; i < dev_pm->num_domains; i++) {
+ dev_pm->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
+ if (IS_ERR(dev_pm->pd_dev[i])) {
+ ret = PTR_ERR(dev_pm->pd_dev[i]);
+ goto detach_pm;
+ }
+
+ dev_pm->pd_dev_link[i] = device_link_add(dev,
+ dev_pm->pd_dev[i],
+ DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!dev_pm->pd_dev_link[i]) {
+ dev_pm_domain_detach(dev_pm->pd_dev[i], false);
+ ret = -EINVAL;
+ goto detach_pm;
+ }
+ }
+ return 0;
+
+detach_pm:
+ while (--i >= 0) {
+ device_link_del(dev_pm->pd_dev_link[i]);
+ dev_pm_domain_detach(dev_pm->pd_dev[i], false);
+ }
+ return ret;
+}
+
+/**
+ * clk_imx_acm_detach_pm_domains: detach multi power domains
+ * @dev: deivice pointer
+ * @dev_pm: multi power domain for device
+ */
+static void clk_imx_acm_detach_pm_domains(struct device *dev,
+ struct clk_imx_acm_pm_domains *dev_pm)
+{
+ int i;
+
+ if (dev_pm->num_domains <= 1)
+ return;
+
+ for (i = 0; i < dev_pm->num_domains; i++) {
+ device_link_del(dev_pm->pd_dev_link[i]);
+ dev_pm_domain_detach(dev_pm->pd_dev[i], false);
+ }
+}
+
+static int imx8_acm_clk_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_hw_data;
+ struct device *dev = &pdev->dev;
+ struct clk_imx8_acm_sel *sels;
+ struct imx8_acm_priv *priv;
+ struct clk_hw **hws;
+ void __iomem *base;
+ int ret;
+ int i;
+
+ base = devm_of_iomap(dev, dev->of_node, 0, NULL);
+ if (WARN_ON(IS_ERR(base)))
+ return PTR_ERR(base);
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->reg = base;
+ priv->soc_data = of_device_get_match_data(dev);
+ platform_set_drvdata(pdev, priv);
+
+ clk_hw_data = devm_kzalloc(&pdev->dev, struct_size(clk_hw_data, hws, IMX_ADMA_ACM_CLK_END),
+ GFP_KERNEL);
+ if (!clk_hw_data)
+ return -ENOMEM;
+
+ clk_hw_data->num = IMX_ADMA_ACM_CLK_END;
+ hws = clk_hw_data->hws;
+
+ ret = clk_imx_acm_attach_pm_domains(&pdev->dev, &priv->dev_pm);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ sels = priv->soc_data->sels;
+ for (i = 0; i < priv->soc_data->num_sels; i++) {
+ hws[sels[i].clkid] = devm_clk_hw_register_mux_parent_data_table(dev,
+ sels[i].name, sels[i].parents,
+ sels[i].num_parents, 0,
+ base + sels[i].reg,
+ sels[i].shift, sels[i].width,
+ 0, NULL, NULL);
+ if (IS_ERR(hws[sels[i].clkid])) {
+ ret = PTR_ERR(hws[sels[i].clkid]);
+ imx_check_clk_hws(hws, IMX_ADMA_ACM_CLK_END);
+ goto err_clk_register;
+ }
+
+ /*
+ * The IMX_ADMA_ACM_AUD_CLK0_SEL and IMX_ADMA_ACM_AUD_CLK1_SEL are
+ * registered first. After registration, update the clk_hw pointer
+ * to imx8qm/qxp/dxl_mclk_sels structures.
+ */
+ if (sels[i].clkid == IMX_ADMA_ACM_AUD_CLK0_SEL)
+ priv->soc_data->mclk_sels[ACM_AUD_CLK0_SEL_INDEX].hw =
+ hws[IMX_ADMA_ACM_AUD_CLK0_SEL];
+ if (sels[i].clkid == IMX_ADMA_ACM_AUD_CLK1_SEL)
+ priv->soc_data->mclk_sels[ACM_AUD_CLK1_SEL_INDEX].hw =
+ hws[IMX_ADMA_ACM_AUD_CLK1_SEL];
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
+ if (ret < 0) {
+ dev_err(dev, "failed to register hws for ACM\n");
+ goto err_clk_register;
+ }
+
+ pm_runtime_put_sync(&pdev->dev);
+ return 0;
+
+err_clk_register:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ clk_imx_acm_detach_pm_domains(&pdev->dev, &priv->dev_pm);
+
+ return ret;
+}
+
+static void imx8_acm_clk_remove(struct platform_device *pdev)
+{
+ struct imx8_acm_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ clk_imx_acm_detach_pm_domains(&pdev->dev, &priv->dev_pm);
+}
+
+static const struct imx8_acm_soc_data imx8qm_acm_data = {
+ .sels = imx8qm_sels,
+ .num_sels = ARRAY_SIZE(imx8qm_sels),
+ .mclk_sels = imx8qm_mclk_sels,
+};
+
+static const struct imx8_acm_soc_data imx8qxp_acm_data = {
+ .sels = imx8qxp_sels,
+ .num_sels = ARRAY_SIZE(imx8qxp_sels),
+ .mclk_sels = imx8qxp_mclk_sels,
+};
+
+static const struct imx8_acm_soc_data imx8dxl_acm_data = {
+ .sels = imx8dxl_sels,
+ .num_sels = ARRAY_SIZE(imx8dxl_sels),
+ .mclk_sels = imx8dxl_mclk_sels,
+};
+
+static const struct of_device_id imx8_acm_match[] = {
+ { .compatible = "fsl,imx8qm-acm", .data = &imx8qm_acm_data },
+ { .compatible = "fsl,imx8qxp-acm", .data = &imx8qxp_acm_data },
+ { .compatible = "fsl,imx8dxl-acm", .data = &imx8dxl_acm_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8_acm_match);
+
+static int __maybe_unused imx8_acm_runtime_suspend(struct device *dev)
+{
+ struct imx8_acm_priv *priv = dev_get_drvdata(dev);
+ struct clk_imx8_acm_sel *sels;
+ int i;
+
+ sels = priv->soc_data->sels;
+
+ for (i = 0; i < priv->soc_data->num_sels; i++)
+ priv->regs[i] = readl_relaxed(priv->reg + sels[i].reg);
+
+ return 0;
+}
+
+static int __maybe_unused imx8_acm_runtime_resume(struct device *dev)
+{
+ struct imx8_acm_priv *priv = dev_get_drvdata(dev);
+ struct clk_imx8_acm_sel *sels;
+ int i;
+
+ sels = priv->soc_data->sels;
+
+ for (i = 0; i < priv->soc_data->num_sels; i++)
+ writel_relaxed(priv->regs[i], priv->reg + sels[i].reg);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx8_acm_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx8_acm_runtime_suspend,
+ imx8_acm_runtime_resume, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver imx8_acm_clk_driver = {
+ .driver = {
+ .name = "imx8-acm",
+ .of_match_table = imx8_acm_match,
+ .pm = &imx8_acm_pm_ops,
+ },
+ .probe = imx8_acm_clk_probe,
+ .remove = imx8_acm_clk_remove,
+};
+module_platform_driver(imx8_acm_clk_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_DESCRIPTION("Freescale i.MX8 Audio Clock Mux driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/imx/clk-imx8dxl-rsrc.c b/drivers/clk/imx/clk-imx8dxl-rsrc.c
index 69b7aa34fff5..0f940335d83c 100644
--- a/drivers/clk/imx/clk-imx8dxl-rsrc.c
+++ b/drivers/clk/imx/clk-imx8dxl-rsrc.c
@@ -47,11 +47,10 @@ static u32 imx8dxl_clk_scu_rsrc_table[] = {
IMX_SC_R_SDHC_2,
IMX_SC_R_ENET_0,
IMX_SC_R_ENET_1,
- IMX_SC_R_MLB_0,
IMX_SC_R_USB_1,
IMX_SC_R_NAND,
- IMX_SC_R_M4_0_I2C,
IMX_SC_R_M4_0_UART,
+ IMX_SC_R_M4_0_I2C,
IMX_SC_R_ELCDIF_PLL,
IMX_SC_R_AUDIO_PLL_0,
IMX_SC_R_AUDIO_PLL_1,
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
index b6d275855b36..342049b847b9 100644
--- a/drivers/clk/imx/clk-imx8mm.c
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -312,12 +312,12 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
hws = clk_hw_data->hws;
hws[IMX8MM_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX8MM_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m");
- hws[IMX8MM_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k");
- hws[IMX8MM_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1");
- hws[IMX8MM_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2");
- hws[IMX8MM_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3");
- hws[IMX8MM_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4");
+ hws[IMX8MM_CLK_24M] = imx_get_clk_hw_by_name(np, "osc_24m");
+ hws[IMX8MM_CLK_32K] = imx_get_clk_hw_by_name(np, "osc_32k");
+ hws[IMX8MM_CLK_EXT1] = imx_get_clk_hw_by_name(np, "clk_ext1");
+ hws[IMX8MM_CLK_EXT2] = imx_get_clk_hw_by_name(np, "clk_ext2");
+ hws[IMX8MM_CLK_EXT3] = imx_get_clk_hw_by_name(np, "clk_ext3");
+ hws[IMX8MM_CLK_EXT4] = imx_get_clk_hw_by_name(np, "clk_ext4");
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
base = of_iomap(np, 0);
@@ -432,7 +432,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
/* BUS */
hws[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800);
hws[IMX8MM_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mm_enet_axi_sels, base + 0x8880);
- hws[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900);
+ hws[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900);
hws[IMX8MM_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mm_vpu_bus_sels, base + 0x8980);
hws[IMX8MM_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mm_disp_axi_sels, base + 0x8a00);
hws[IMX8MM_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mm_disp_apb_sels, base + 0x8a80);
@@ -468,7 +468,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
hws[IMX8MM_CLK_PCIE1_PHY] = imx8m_clk_hw_composite("pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380);
hws[IMX8MM_CLK_PCIE1_AUX] = imx8m_clk_hw_composite("pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400);
hws[IMX8MM_CLK_DC_PIXEL] = imx8m_clk_hw_composite("dc_pixel", imx8mm_dc_pixel_sels, base + 0xa480);
- hws[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_hw_composite("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500);
+ hws[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_hw_composite_flags("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500, CLK_SET_RATE_PARENT);
hws[IMX8MM_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mm_sai1_sels, base + 0xa580);
hws[IMX8MM_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mm_sai2_sels, base + 0xa600);
hws[IMX8MM_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mm_sai3_sels, base + 0xa680);
@@ -609,7 +609,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
goto unregister_hws;
}
- imx_register_uart_clocks(4);
+ imx_register_uart_clocks();
return 0;
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c
index d37c45b676ab..ab77e148e70c 100644
--- a/drivers/clk/imx/clk-imx8mn.c
+++ b/drivers/clk/imx/clk-imx8mn.c
@@ -27,10 +27,10 @@ static u32 share_count_nand;
static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", };
static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
-static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
+static const char * const video_pll_bypass_sels[] = {"video_pll", "video_pll_ref_sel", };
static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", };
static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", };
-static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", };
+static const char * const m7_alt_pll_bypass_sels[] = {"m7_alt_pll", "m7_alt_pll_ref_sel", };
static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", };
static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", };
@@ -40,24 +40,24 @@ static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pl
static const char * const imx8mn_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", };
-static const char * const imx8mn_m7_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "vpu_pll_out",
- "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", };
+static const char * const imx8mn_m7_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "m7_alt_pll_out",
+ "sys_pll1_800m", "audio_pll1_out", "video_pll_out", "sys_pll3_out", };
static const char * const imx8mn_gpu_core_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m",
"sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out",
- "video_pll1_out", "audio_pll2_out", };
+ "video_pll_out", "audio_pll2_out", };
static const char * const imx8mn_gpu_shader_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m",
"sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out",
- "video_pll1_out", "audio_pll2_out", };
+ "video_pll_out", "audio_pll2_out", };
static const char * const imx8mn_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m",
"sys_pll2_250m", "sys_pll2_1000m", "audio_pll1_out",
- "video_pll1_out", "sys_pll1_100m",};
+ "video_pll_out", "sys_pll1_100m",};
static const char * const imx8mn_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out",
- "video_pll1_out", "sys_pll3_out", };
+ "video_pll_out", "sys_pll3_out", };
static const char * const imx8mn_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out",
@@ -77,23 +77,23 @@ static const char * const imx8mn_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "
static const char * const imx8mn_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out",
"sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out",
- "video_pll1_out", "audio_pll2_out", };
+ "video_pll_out", "audio_pll2_out", };
static const char * const imx8mn_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out",
"sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out",
- "video_pll1_out", "audio_pll2_out", };
+ "video_pll_out", "audio_pll2_out", };
static const char * const imx8mn_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out",
- "video_pll1_out", "audio_pll2_out", };
+ "video_pll_out", "audio_pll2_out", };
static const char * const imx8mn_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m",
"sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out",
- "audio_pll1_out", "video_pll1_out", };
+ "audio_pll1_out", "video_pll_out", };
static const char * const imx8mn_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m",
"sys_pll2_1000m", "sys_pll2_166m", "sys_pll3_out",
- "audio_pll1_out", "video_pll1_out", };
+ "audio_pll1_out", "video_pll_out", };
static const char * const imx8mn_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m",
"sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out",
@@ -103,49 +103,49 @@ static const char * const imx8mn_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mn_disp_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out",
+static const char * const imx8mn_disp_pixel_sels[] = {"osc_24m", "video_pll_out", "audio_pll2_out",
"audio_pll1_out", "sys_pll1_800m", "sys_pll2_1000m",
"sys_pll3_out", "clk_ext4", };
static const char * const imx8mn_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
- "clk_ext3", "clk_ext4", };
+ "video_pll_out", "sys_pll1_133m", "dummy",
+ "clk_ext2", "clk_ext3", };
static const char * const imx8mn_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
+ "video_pll_out", "sys_pll1_133m", "dummy",
"clk_ext3", "clk_ext4", };
static const char * const imx8mn_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
+ "video_pll_out", "sys_pll1_133m", "dummy",
"clk_ext2", "clk_ext3", };
static const char * const imx8mn_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
+ "video_pll_out", "sys_pll1_133m", "dummy",
"clk_ext3", "clk_ext4", };
static const char * const imx8mn_sai7_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
+ "video_pll_out", "sys_pll1_133m", "dummy",
"clk_ext3", "clk_ext4", };
static const char * const imx8mn_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
+ "video_pll_out", "sys_pll1_133m", "dummy",
"clk_ext2", "clk_ext3", };
static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m",
"sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out",
- "video_pll1_out", "clk_ext4", };
+ "video_pll_out", "clk_ext4", };
static const char * const imx8mn_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out",
"clk_ext1", "clk_ext2", "clk_ext3",
- "clk_ext4", "video_pll1_out", };
+ "clk_ext4", "video_pll_out", };
static const char * const imx8mn_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m",
- "sys_pll2_200m", "sys_pll2_500m", "video_pll1_out",
- "audio_pll2_out", };
+ "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out",
+ "video_pll_out", "audio_pll2_out", };
static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out",
"sys_pll1_400m", "audio_pll2_out", "sys_pll3_out",
- "sys_pll2_250m", "video_pll1_out", };
+ "sys_pll2_250m", "video_pll_out", };
static const char * const imx8mn_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m",
"sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m",
@@ -160,19 +160,19 @@ static const char * const imx8mn_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "s
"audio_pll2_out", "sys_pll1_100m", };
static const char * const imx8mn_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
- "sys_pll3_out", "audio_pll1_out", "video_pll1_out",
+ "sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
static const char * const imx8mn_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
- "sys_pll3_out", "audio_pll1_out", "video_pll1_out",
+ "sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
static const char * const imx8mn_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
- "sys_pll3_out", "audio_pll1_out", "video_pll1_out",
+ "sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
static const char * const imx8mn_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
- "sys_pll3_out", "audio_pll1_out", "video_pll1_out",
+ "sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
static const char * const imx8mn_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m",
@@ -213,63 +213,63 @@ static const char * const imx8mn_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "s
static const char * const imx8mn_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
- "sys_pll1_80m", "video_pll1_out", };
+ "sys_pll1_80m", "video_pll_out", };
static const char * const imx8mn_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
- "sys_pll1_80m", "video_pll1_out", };
+ "sys_pll1_80m", "video_pll_out", };
static const char * const imx8mn_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
- "sys_pll1_80m", "video_pll1_out", };
+ "sys_pll1_80m", "video_pll_out", };
static const char * const imx8mn_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
- "sys_pll1_80m", "video_pll1_out", };
+ "sys_pll1_80m", "video_pll_out", };
static const char * const imx8mn_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m",
- "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m",
+ "sys_pll1_40m", "video_pll_out", "sys_pll1_80m",
"audio_pll1_out", "clk_ext1", };
static const char * const imx8mn_gpt2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m",
- "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m",
+ "sys_pll1_40m", "video_pll_out", "sys_pll1_80m",
"audio_pll1_out", "clk_ext1", };
static const char * const imx8mn_gpt3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m",
- "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m",
+ "sys_pll1_40m", "video_pll_out", "sys_pll1_80m",
"audio_pll1_out", "clk_ext1", };
static const char * const imx8mn_gpt4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m",
- "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m",
+ "sys_pll1_40m", "video_pll_out", "sys_pll1_80m",
"audio_pll1_out", "clk_ext1", };
static const char * const imx8mn_gpt5_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m",
- "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m",
+ "sys_pll1_40m", "video_pll_out", "sys_pll1_80m",
"audio_pll1_out", "clk_ext1", };
static const char * const imx8mn_gpt6_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m",
- "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m",
+ "sys_pll1_40m", "video_pll_out", "sys_pll1_80m",
"audio_pll1_out", "clk_ext1", };
static const char * const imx8mn_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m",
- "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out",
+ "m7_alt_pll_out", "sys_pll2_125m", "sys_pll3_out",
"sys_pll1_80m", "sys_pll2_166m", };
-static const char * const imx8mn_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out",
+static const char * const imx8mn_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "m7_alt_pll_out",
"sys_pll3_out", "sys_pll2_200m", "sys_pll1_266m",
"sys_pll2_500m", "sys_pll1_100m", };
static const char * const imx8mn_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m",
"sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out",
- "audio_pll2_out", "video_pll1_out", };
+ "audio_pll2_out", "video_pll_out", };
static const char * const imx8mn_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m",
"sys_pll1_800m", "sys_pll2_1000m", "clk_ext2",
- "audio_pll2_out", "video_pll1_out", };
+ "audio_pll2_out", "video_pll_out", };
static const char * const imx8mn_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m",
"sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out",
- "audio_pll2_out", "video_pll1_out", };
+ "audio_pll2_out", "video_pll_out", };
static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
@@ -277,15 +277,15 @@ static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "s
static const char * const imx8mn_camera_pixel_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m",
"sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out",
- "audio_pll2_out", "video_pll1_out", };
+ "audio_pll2_out", "video_pll_out", };
static const char * const imx8mn_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m",
"sys_pll1_800m", "sys_pll2_1000m", "clk_ext2",
- "audio_pll2_out", "video_pll1_out", };
+ "audio_pll2_out", "video_pll_out", };
static const char * const imx8mn_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m",
"sys_pll1_800m", "sys_pll2_1000m", "clk_ext2",
- "audio_pll2_out", "video_pll1_out", };
+ "audio_pll2_out", "video_pll_out", };
static const char * const imx8mn_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m",
"sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out",
@@ -306,9 +306,9 @@ static const char * const imx8mn_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "du
"dummy", "sys_pll1_80m", };
static const char * const imx8mn_clko2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_400m",
"sys_pll2_166m", "sys_pll3_out", "audio_pll1_out",
- "video_pll1_out", "osc_32k", };
+ "video_pll_out", "osc_32k", };
-static const char * const clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll1_out",
+static const char * const clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll_out",
"dummy", "dummy", "gpu_pll_out", "dummy",
"arm_pll_out", "sys_pll1", "sys_pll2", "sys_pll3",
"dummy", "dummy", "osc_24m", "dummy", "osc_32k"};
@@ -323,7 +323,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
- clk_hw_data = kzalloc(struct_size(clk_hw_data, hws,
+ clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws,
IMX8MN_CLK_END), GFP_KERNEL);
if (WARN_ON(!clk_hw_data))
return -ENOMEM;
@@ -332,36 +332,36 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
hws = clk_hw_data->hws;
hws[IMX8MN_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX8MN_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m");
- hws[IMX8MN_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k");
- hws[IMX8MN_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1");
- hws[IMX8MN_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2");
- hws[IMX8MN_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3");
- hws[IMX8MN_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4");
+ hws[IMX8MN_CLK_24M] = imx_get_clk_hw_by_name(np, "osc_24m");
+ hws[IMX8MN_CLK_32K] = imx_get_clk_hw_by_name(np, "osc_32k");
+ hws[IMX8MN_CLK_EXT1] = imx_get_clk_hw_by_name(np, "clk_ext1");
+ hws[IMX8MN_CLK_EXT2] = imx_get_clk_hw_by_name(np, "clk_ext2");
+ hws[IMX8MN_CLK_EXT3] = imx_get_clk_hw_by_name(np, "clk_ext3");
+ hws[IMX8MN_CLK_EXT4] = imx_get_clk_hw_by_name(np, "clk_ext4");
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mn-anatop");
- base = of_iomap(np, 0);
+ base = devm_of_iomap(dev, np, 0, NULL);
of_node_put(np);
- if (WARN_ON(!base)) {
- ret = -ENOMEM;
+ if (WARN_ON(IS_ERR(base))) {
+ ret = PTR_ERR(base);
goto unregister_hws;
}
hws[IMX8MN_AUDIO_PLL1_REF_SEL] = imx_clk_hw_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MN_AUDIO_PLL2_REF_SEL] = imx_clk_hw_mux("audio_pll2_ref_sel", base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
- hws[IMX8MN_VIDEO_PLL1_REF_SEL] = imx_clk_hw_mux("video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ hws[IMX8MN_VIDEO_PLL_REF_SEL] = imx_clk_hw_mux("video_pll_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MN_DRAM_PLL_REF_SEL] = imx_clk_hw_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MN_GPU_PLL_REF_SEL] = imx_clk_hw_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
- hws[IMX8MN_VPU_PLL_REF_SEL] = imx_clk_hw_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
+ hws[IMX8MN_M7_ALT_PLL_REF_SEL] = imx_clk_hw_mux("m7_alt_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MN_ARM_PLL_REF_SEL] = imx_clk_hw_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MN_SYS_PLL3_REF_SEL] = imx_clk_hw_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MN_AUDIO_PLL1] = imx_clk_hw_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll);
hws[IMX8MN_AUDIO_PLL2] = imx_clk_hw_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll);
- hws[IMX8MN_VIDEO_PLL1] = imx_clk_hw_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll);
+ hws[IMX8MN_VIDEO_PLL] = imx_clk_hw_pll14xx("video_pll", "video_pll_ref_sel", base + 0x28, &imx_1443x_pll);
hws[IMX8MN_DRAM_PLL] = imx_clk_hw_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll);
hws[IMX8MN_GPU_PLL] = imx_clk_hw_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll);
- hws[IMX8MN_VPU_PLL] = imx_clk_hw_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll);
+ hws[IMX8MN_M7_ALT_PLL] = imx_clk_hw_pll14xx("m7_alt_pll", "m7_alt_pll_ref_sel", base + 0x74, &imx_1416x_pll);
hws[IMX8MN_ARM_PLL] = imx_clk_hw_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll);
hws[IMX8MN_SYS_PLL1] = imx_clk_hw_fixed("sys_pll1", 800000000);
hws[IMX8MN_SYS_PLL2] = imx_clk_hw_fixed("sys_pll2", 1000000000);
@@ -370,20 +370,20 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
/* PLL bypass out */
hws[IMX8MN_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX8MN_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux_flags("audio_pll2_bypass", base + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT);
- hws[IMX8MN_VIDEO_PLL1_BYPASS] = imx_clk_hw_mux_flags("video_pll1_bypass", base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT);
+ hws[IMX8MN_VIDEO_PLL_BYPASS] = imx_clk_hw_mux_flags("video_pll_bypass", base + 0x28, 16, 1, video_pll_bypass_sels, ARRAY_SIZE(video_pll_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX8MN_DRAM_PLL_BYPASS] = imx_clk_hw_mux_flags("dram_pll_bypass", base + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX8MN_GPU_PLL_BYPASS] = imx_clk_hw_mux_flags("gpu_pll_bypass", base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
- hws[IMX8MN_VPU_PLL_BYPASS] = imx_clk_hw_mux_flags("vpu_pll_bypass", base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
+ hws[IMX8MN_M7_ALT_PLL_BYPASS] = imx_clk_hw_mux_flags("m7_alt_pll_bypass", base + 0x74, 28, 1, m7_alt_pll_bypass_sels, ARRAY_SIZE(m7_alt_pll_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX8MN_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX8MN_SYS_PLL3_BYPASS] = imx_clk_hw_mux_flags("sys_pll3_bypass", base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT);
/* PLL out gate */
hws[IMX8MN_AUDIO_PLL1_OUT] = imx_clk_hw_gate("audio_pll1_out", "audio_pll1_bypass", base, 13);
hws[IMX8MN_AUDIO_PLL2_OUT] = imx_clk_hw_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x14, 13);
- hws[IMX8MN_VIDEO_PLL1_OUT] = imx_clk_hw_gate("video_pll1_out", "video_pll1_bypass", base + 0x28, 13);
+ hws[IMX8MN_VIDEO_PLL_OUT] = imx_clk_hw_gate("video_pll_out", "video_pll_bypass", base + 0x28, 13);
hws[IMX8MN_DRAM_PLL_OUT] = imx_clk_hw_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13);
hws[IMX8MN_GPU_PLL_OUT] = imx_clk_hw_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11);
- hws[IMX8MN_VPU_PLL_OUT] = imx_clk_hw_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 11);
+ hws[IMX8MN_M7_ALT_PLL_OUT] = imx_clk_hw_gate("m7_alt_pll_out", "m7_alt_pll_bypass", base + 0x74, 11);
hws[IMX8MN_ARM_PLL_OUT] = imx_clk_hw_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11);
hws[IMX8MN_SYS_PLL3_OUT] = imx_clk_hw_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11);
@@ -470,7 +470,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
hws[IMX8MN_CLK_DRAM_ALT] = imx8m_clk_hw_fw_managed_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000);
hws[IMX8MN_CLK_DRAM_APB] = imx8m_clk_hw_fw_managed_composite_critical("dram_apb", imx8mn_dram_apb_sels, base + 0xa080);
- hws[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_hw_composite("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500);
+ hws[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_hw_composite_flags("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500, CLK_SET_RATE_PARENT);
hws[IMX8MN_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mn_sai2_sels, base + 0xa600);
hws[IMX8MN_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mn_sai3_sels, base + 0xa680);
hws[IMX8MN_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mn_sai5_sels, base + 0xa780);
@@ -583,6 +583,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
hws[IMX8MN_CLK_SDMA2_ROOT] = imx_clk_hw_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0);
hws[IMX8MN_CLK_SDMA3_ROOT] = imx_clk_hw_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0);
hws[IMX8MN_CLK_SAI7_ROOT] = imx_clk_hw_gate2_shared2("sai7_root_clk", "sai7", base + 0x4650, 0, &share_count_sai7);
+ hws[IMX8MN_CLK_SAI7_IPG] = imx_clk_hw_gate2_shared2("sai7_ipg_clk", "ipg_audio_root", base + 0x4650, 0, &share_count_sai7);
hws[IMX8MN_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc_24m", 1, 8);
@@ -602,7 +603,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
goto unregister_hws;
}
- imx_register_uart_clocks(4);
+ imx_register_uart_clocks();
return 0;
diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-imx8mp-audiomix.c
new file mode 100644
index 000000000000..131702f2c9ec
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for i.MX8M Plus Audio BLK_CTRL
+ *
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/imx8mp-clock.h>
+
+#include "clk.h"
+
+#define CLKEN0 0x000
+#define CLKEN1 0x004
+#define EARC 0x200
+#define SAI1_MCLK_SEL 0x300
+#define SAI2_MCLK_SEL 0x304
+#define SAI3_MCLK_SEL 0x308
+#define SAI5_MCLK_SEL 0x30C
+#define SAI6_MCLK_SEL 0x310
+#define SAI7_MCLK_SEL 0x314
+#define PDM_SEL 0x318
+#define SAI_PLL_GNRL_CTL 0x400
+#define SAI_PLL_FDIVL_CTL0 0x404
+#define SAI_PLL_FDIVL_CTL1 0x408
+#define SAI_PLL_SSCG_CTL 0x40C
+#define SAI_PLL_MNIT_CTL 0x410
+#define IPG_LP_CTRL 0x504
+
+#define SAIn_MCLK1_PARENT(n) \
+static const struct clk_parent_data \
+clk_imx8mp_audiomix_sai##n##_mclk1_parents[] = { \
+ { \
+ .fw_name = "sai"__stringify(n), \
+ .name = "sai"__stringify(n) \
+ }, { \
+ .fw_name = "sai"__stringify(n)"_mclk", \
+ .name = "sai"__stringify(n)"_mclk" \
+ }, \
+}
+
+SAIn_MCLK1_PARENT(1);
+SAIn_MCLK1_PARENT(2);
+SAIn_MCLK1_PARENT(3);
+SAIn_MCLK1_PARENT(5);
+SAIn_MCLK1_PARENT(6);
+SAIn_MCLK1_PARENT(7);
+
+static const struct clk_parent_data clk_imx8mp_audiomix_sai_mclk2_parents[] = {
+ { .fw_name = "sai1", .name = "sai1" },
+ { .fw_name = "sai2", .name = "sai2" },
+ { .fw_name = "sai3", .name = "sai3" },
+ { .name = "dummy" },
+ { .fw_name = "sai5", .name = "sai5" },
+ { .fw_name = "sai6", .name = "sai6" },
+ { .fw_name = "sai7", .name = "sai7" },
+ { .fw_name = "sai1_mclk", .name = "sai1_mclk" },
+ { .fw_name = "sai2_mclk", .name = "sai2_mclk" },
+ { .fw_name = "sai3_mclk", .name = "sai3_mclk" },
+ { .name = "dummy" },
+ { .fw_name = "sai5_mclk", .name = "sai5_mclk" },
+ { .fw_name = "sai6_mclk", .name = "sai6_mclk" },
+ { .fw_name = "sai7_mclk", .name = "sai7_mclk" },
+ { .fw_name = "spdif_extclk", .name = "spdif_extclk" },
+ { .name = "dummy" },
+};
+
+static const struct clk_parent_data clk_imx8mp_audiomix_pdm_parents[] = {
+ { .fw_name = "pdm", .name = "pdm" },
+ { .name = "sai_pll_out_div2" },
+ { .fw_name = "sai1_mclk", .name = "sai1_mclk" },
+ { .name = "dummy" },
+};
+
+
+static const struct clk_parent_data clk_imx8mp_audiomix_pll_parents[] = {
+ { .fw_name = "osc_24m", .name = "osc_24m" },
+ { .name = "dummy" },
+ { .name = "dummy" },
+ { .name = "dummy" },
+};
+
+static const struct clk_parent_data clk_imx8mp_audiomix_pll_bypass_sels[] = {
+ { .fw_name = "sai_pll", .name = "sai_pll" },
+ { .fw_name = "sai_pll_ref_sel", .name = "sai_pll_ref_sel" },
+};
+
+#define CLK_GATE(gname, cname) \
+ { \
+ gname"_cg", \
+ IMX8MP_CLK_AUDIOMIX_##cname, \
+ { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \
+ CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \
+ 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \
+ }
+
+#define CLK_SAIn(n) \
+ { \
+ "sai"__stringify(n)"_mclk1_sel", \
+ IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1_SEL, {}, \
+ clk_imx8mp_audiomix_sai##n##_mclk1_parents, \
+ ARRAY_SIZE(clk_imx8mp_audiomix_sai##n##_mclk1_parents), \
+ SAI##n##_MCLK_SEL, 1, 0 \
+ }, { \
+ "sai"__stringify(n)"_mclk2_sel", \
+ IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2_SEL, {}, \
+ clk_imx8mp_audiomix_sai_mclk2_parents, \
+ ARRAY_SIZE(clk_imx8mp_audiomix_sai_mclk2_parents), \
+ SAI##n##_MCLK_SEL, 4, 1 \
+ }, { \
+ "sai"__stringify(n)"_ipg_cg", \
+ IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG, \
+ { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \
+ CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG \
+ }, { \
+ "sai"__stringify(n)"_mclk1_cg", \
+ IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1, \
+ { \
+ .fw_name = "sai"__stringify(n)"_mclk1_sel", \
+ .name = "sai"__stringify(n)"_mclk1_sel" \
+ }, NULL, 1, \
+ CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1 \
+ }, { \
+ "sai"__stringify(n)"_mclk2_cg", \
+ IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2, \
+ { \
+ .fw_name = "sai"__stringify(n)"_mclk2_sel", \
+ .name = "sai"__stringify(n)"_mclk2_sel" \
+ }, NULL, 1, \
+ CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2 \
+ }, { \
+ "sai"__stringify(n)"_mclk3_cg", \
+ IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3, \
+ { \
+ .fw_name = "sai_pll_out_div2", \
+ .name = "sai_pll_out_div2" \
+ }, NULL, 1, \
+ CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3 \
+ }
+
+#define CLK_PDM \
+ { \
+ "pdm_sel", IMX8MP_CLK_AUDIOMIX_PDM_SEL, {}, \
+ clk_imx8mp_audiomix_pdm_parents, \
+ ARRAY_SIZE(clk_imx8mp_audiomix_pdm_parents), \
+ PDM_SEL, 2, 0 \
+ }
+
+#define CLK_GATE_PARENT(gname, cname, pname) \
+ { \
+ gname"_cg", \
+ IMX8MP_CLK_AUDIOMIX_##cname, \
+ { .fw_name = pname, .name = pname }, NULL, 1, \
+ CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \
+ 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \
+ }
+
+struct clk_imx8mp_audiomix_sel {
+ const char *name;
+ int clkid;
+ const struct clk_parent_data parent; /* For gate */
+ const struct clk_parent_data *parents; /* For mux */
+ int num_parents;
+ u16 reg;
+ u8 width;
+ u8 shift;
+};
+
+static struct clk_imx8mp_audiomix_sel sels[] = {
+ CLK_GATE("asrc", ASRC_IPG),
+ CLK_GATE("pdm", PDM_IPG),
+ CLK_GATE("earc", EARC_IPG),
+ CLK_GATE_PARENT("ocrama", OCRAMA_IPG, "axi"),
+ CLK_GATE("aud2htx", AUD2HTX_IPG),
+ CLK_GATE_PARENT("earc_phy", EARC_PHY, "sai_pll_out_div2"),
+ CLK_GATE("sdma2", SDMA2_ROOT),
+ CLK_GATE("sdma3", SDMA3_ROOT),
+ CLK_GATE("spba2", SPBA2_ROOT),
+ CLK_GATE_PARENT("dsp", DSP_ROOT, "axi"),
+ CLK_GATE_PARENT("dspdbg", DSPDBG_ROOT, "axi"),
+ CLK_GATE("edma", EDMA_ROOT),
+ CLK_GATE_PARENT("audpll", AUDPLL_ROOT, "osc_24m"),
+ CLK_GATE("mu2", MU2_ROOT),
+ CLK_GATE("mu3", MU3_ROOT),
+ CLK_PDM,
+ CLK_SAIn(1),
+ CLK_SAIn(2),
+ CLK_SAIn(3),
+ CLK_SAIn(5),
+ CLK_SAIn(6),
+ CLK_SAIn(7)
+};
+
+static const u16 audiomix_regs[] = {
+ CLKEN0,
+ CLKEN1,
+ EARC,
+ SAI1_MCLK_SEL,
+ SAI2_MCLK_SEL,
+ SAI3_MCLK_SEL,
+ SAI5_MCLK_SEL,
+ SAI6_MCLK_SEL,
+ SAI7_MCLK_SEL,
+ PDM_SEL,
+ SAI_PLL_GNRL_CTL,
+ SAI_PLL_FDIVL_CTL0,
+ SAI_PLL_FDIVL_CTL1,
+ SAI_PLL_SSCG_CTL,
+ SAI_PLL_MNIT_CTL,
+ IPG_LP_CTRL,
+};
+
+struct clk_imx8mp_audiomix_priv {
+ void __iomem *base;
+ u32 regs_save[ARRAY_SIZE(audiomix_regs)];
+
+ /* Must be last */
+ struct clk_hw_onecell_data clk_data;
+};
+
+#if IS_ENABLED(CONFIG_RESET_CONTROLLER)
+
+static int clk_imx8mp_audiomix_reset_controller_register(struct device *dev,
+ struct clk_imx8mp_audiomix_priv *priv)
+{
+ struct auxiliary_device *adev;
+
+ if (!of_property_present(dev->of_node, "#reset-cells"))
+ return 0;
+
+ adev = devm_auxiliary_device_create(dev, "reset", NULL);
+ if (!adev)
+ return -ENODEV;
+
+ return 0;
+}
+
+#else /* !CONFIG_RESET_CONTROLLER */
+
+static int clk_imx8mp_audiomix_reset_controller_register(struct device *dev,
+ struct clk_imx8mp_audiomix_priv *priv)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_RESET_CONTROLLER */
+
+static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool save)
+{
+ struct clk_imx8mp_audiomix_priv *priv = dev_get_drvdata(dev);
+ void __iomem *base = priv->base;
+ int i;
+
+ if (save) {
+ for (i = 0; i < ARRAY_SIZE(audiomix_regs); i++)
+ priv->regs_save[i] = readl(base + audiomix_regs[i]);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(audiomix_regs); i++)
+ writel(priv->regs_save[i], base + audiomix_regs[i]);
+ }
+}
+
+static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
+{
+ struct clk_imx8mp_audiomix_priv *priv;
+ struct clk_hw_onecell_data *clk_hw_data;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ struct clk_hw *hw;
+ int i, ret;
+
+ priv = devm_kzalloc(dev,
+ struct_size(priv, clk_data.hws, IMX8MP_CLK_AUDIOMIX_END),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ clk_hw_data = &priv->clk_data;
+ clk_hw_data->num = IMX8MP_CLK_AUDIOMIX_END;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->base = base;
+ dev_set_drvdata(dev, priv);
+
+ /*
+ * pm_runtime_enable needs to be called before clk register.
+ * That is to make core->rpm_enabled to be true for clock
+ * usage.
+ */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ for (i = 0; i < ARRAY_SIZE(sels); i++) {
+ if (sels[i].num_parents == 1) {
+ hw = devm_clk_hw_register_gate_parent_data(dev,
+ sels[i].name, &sels[i].parent, CLK_SET_RATE_PARENT,
+ base + sels[i].reg, sels[i].shift, 0, NULL);
+ } else {
+ hw = devm_clk_hw_register_mux_parent_data_table(dev,
+ sels[i].name, sels[i].parents,
+ sels[i].num_parents, CLK_SET_RATE_PARENT,
+ base + sels[i].reg,
+ sels[i].shift, sels[i].width,
+ 0, NULL, NULL);
+ }
+
+ if (IS_ERR(hw)) {
+ ret = PTR_ERR(hw);
+ goto err_clk_register;
+ }
+
+ clk_hw_data->hws[sels[i].clkid] = hw;
+ }
+
+ /* SAI PLL */
+ hw = devm_clk_hw_register_mux_parent_data_table(dev,
+ "sai_pll_ref_sel", clk_imx8mp_audiomix_pll_parents,
+ ARRAY_SIZE(clk_imx8mp_audiomix_pll_parents),
+ CLK_SET_RATE_NO_REPARENT, base + SAI_PLL_GNRL_CTL,
+ 0, 2, 0, NULL, NULL);
+ clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw;
+
+ hw = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel",
+ base + 0x400, &imx_1443x_pll);
+ if (IS_ERR(hw)) {
+ ret = PTR_ERR(hw);
+ goto err_clk_register;
+ }
+ clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw;
+
+ hw = devm_clk_hw_register_mux_parent_data_table(dev,
+ "sai_pll_bypass", clk_imx8mp_audiomix_pll_bypass_sels,
+ ARRAY_SIZE(clk_imx8mp_audiomix_pll_bypass_sels),
+ CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+ base + SAI_PLL_GNRL_CTL, 16, 1, 0, NULL, NULL);
+ if (IS_ERR(hw)) {
+ ret = PTR_ERR(hw);
+ goto err_clk_register;
+ }
+
+ clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw;
+
+ hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass",
+ CLK_SET_RATE_PARENT,
+ base + SAI_PLL_GNRL_CTL, 13,
+ 0, NULL);
+ if (IS_ERR(hw)) {
+ ret = PTR_ERR(hw);
+ goto err_clk_register;
+ }
+ clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw;
+
+ hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2",
+ "sai_pll_out",
+ CLK_SET_RATE_PARENT, 1, 2);
+ if (IS_ERR(hw)) {
+ ret = PTR_ERR(hw);
+ goto err_clk_register;
+ }
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
+ clk_hw_data);
+ if (ret)
+ goto err_clk_register;
+
+ ret = clk_imx8mp_audiomix_reset_controller_register(dev, priv);
+ if (ret)
+ goto err_clk_register;
+
+ pm_runtime_put_sync(dev);
+ return 0;
+
+err_clk_register:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void clk_imx8mp_audiomix_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev)
+{
+ clk_imx8mp_audiomix_save_restore(dev, true);
+
+ return 0;
+}
+
+static int clk_imx8mp_audiomix_runtime_resume(struct device *dev)
+{
+ clk_imx8mp_audiomix_save_restore(dev, false);
+
+ return 0;
+}
+
+static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = {
+ RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend,
+ clk_imx8mp_audiomix_runtime_resume, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id clk_imx8mp_audiomix_of_match[] = {
+ { .compatible = "fsl,imx8mp-audio-blk-ctrl" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, clk_imx8mp_audiomix_of_match);
+
+static struct platform_driver clk_imx8mp_audiomix_driver = {
+ .probe = clk_imx8mp_audiomix_probe,
+ .remove = clk_imx8mp_audiomix_remove,
+ .driver = {
+ .name = "imx8mp-audio-blk-ctrl",
+ .of_match_table = clk_imx8mp_audiomix_of_match,
+ .pm = pm_ptr(&clk_imx8mp_audiomix_pm_ops),
+ },
+};
+
+module_platform_driver(clk_imx8mp_audiomix_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index e89db568f5a8..fe6dac70f1a1 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/units.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -17,6 +18,8 @@
static u32 share_count_nand;
static u32 share_count_media;
+static u32 share_count_usb;
+static u32 share_count_audio;
static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", };
static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", };
@@ -176,10 +179,6 @@ static const char * const imx8mp_sai3_sels[] = {"osc_24m", "audio_pll1_out", "au
"video_pll1_out", "sys_pll1_133m", "osc_hdmi",
"clk_ext3", "clk_ext4", };
-static const char * const imx8mp_sai4_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
- "video_pll1_out", "sys_pll1_133m", "osc_hdmi",
- "clk_ext1", "clk_ext2", };
-
static const char * const imx8mp_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out",
"video_pll1_out", "sys_pll1_133m", "osc_hdmi",
"clk_ext2", "clk_ext3", };
@@ -401,47 +400,185 @@ static const char * const imx8mp_dram_core_sels[] = {"dram_pll_out", "dram_alt_r
static const char * const imx8mp_clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll1_out",
"dummy", "dummy", "gpu_pll_out", "vpu_pll_out",
- "arm_pll_out", "sys_pll1", "sys_pll2", "sys_pll3",
- "dummy", "dummy", "osc_24m", "dummy", "osc_32k"};
+ "arm_pll_out", "sys_pll1_out", "sys_pll2_out",
+ "sys_pll3_out", "dummy", "dummy", "osc_24m",
+ "dummy", "osc_32k"};
static struct clk_hw **hws;
static struct clk_hw_onecell_data *clk_hw_data;
+struct imx8mp_clock_constraints {
+ unsigned int clkid;
+ u32 maxrate;
+};
+
+/*
+ * Below tables are taken from IMX8MPCEC Rev. 2.1, 07/2023
+ * Table 13. Maximum frequency of modules.
+ * Probable typos fixed are marked with a comment.
+ */
+static const struct imx8mp_clock_constraints imx8mp_clock_common_constraints[] = {
+ { IMX8MP_CLK_A53_DIV, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ENET_AXI, 266666667 }, /* Datasheet claims 266MHz */
+ { IMX8MP_CLK_NAND_USDHC_BUS, 266666667 }, /* Datasheet claims 266MHz */
+ { IMX8MP_CLK_MEDIA_APB, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_HDMI_APB, 133333333 }, /* Datasheet claims 133MHz */
+ { IMX8MP_CLK_ML_AXI, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_AHB, 133333333 },
+ { IMX8MP_CLK_IPG_ROOT, 66666667 },
+ { IMX8MP_CLK_AUDIO_AHB, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_DISP2_PIX, 170 * HZ_PER_MHZ },
+ { IMX8MP_CLK_DRAM_ALT, 666666667 },
+ { IMX8MP_CLK_DRAM_APB, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_CAN1, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_CAN2, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_PCIE_AUX, 10 * HZ_PER_MHZ },
+ { IMX8MP_CLK_I2C5, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_I2C6, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_SAI1, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_SAI2, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_SAI3, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_SAI5, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_SAI6, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_ENET_QOS, 125 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ENET_QOS_TIMER, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ENET_REF, 125 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ENET_TIMER, 125 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ENET_PHY_REF, 125 * HZ_PER_MHZ },
+ { IMX8MP_CLK_NAND, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_QSPI, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_USDHC1, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_USDHC2, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_I2C1, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_I2C2, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_I2C3, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_I2C4, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_UART1, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_UART2, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_UART3, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_UART4, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ECSPI1, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ECSPI2, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_PWM1, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_PWM2, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_PWM3, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_PWM4, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_GPT1, 100 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPT2, 100 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPT3, 100 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPT4, 100 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPT5, 100 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPT6, 100 * HZ_PER_MHZ },
+ { IMX8MP_CLK_WDOG, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_IPP_DO_CLKO1, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_IPP_DO_CLKO2, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_HDMI_REF_266M, 266 * HZ_PER_MHZ },
+ { IMX8MP_CLK_USDHC3, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_MIPI_PHY1_REF, 300 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_DISP1_PIX, 250 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_CAM2_PIX, 277 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_LDB, 595 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_MIPI_TEST_BYTE, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ECSPI3, 80 * HZ_PER_MHZ },
+ { IMX8MP_CLK_PDM, 200 * HZ_PER_MHZ },
+ { IMX8MP_CLK_SAI7, 66666667 }, /* Datasheet claims 66MHz */
+ { IMX8MP_CLK_MAIN_AXI, 400 * HZ_PER_MHZ },
+ { /* Sentinel */ }
+};
+
+static const struct imx8mp_clock_constraints imx8mp_clock_nominal_constraints[] = {
+ { IMX8MP_CLK_M7_CORE, 600 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ML_CORE, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU3D_CORE, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU3D_SHADER_CORE, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU2D_CORE, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_AUDIO_AXI_SRC, 600 * HZ_PER_MHZ },
+ { IMX8MP_CLK_HSIO_AXI, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_ISP, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_BUS, 600 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_AXI, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_HDMI_AXI, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU_AXI, 600 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU_AHB, 300 * HZ_PER_MHZ },
+ { IMX8MP_CLK_NOC, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_NOC_IO, 600 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ML_AHB, 300 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_G1, 600 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_G2, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_CAM1_PIX, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_VC8000E, 400 * HZ_PER_MHZ }, /* Datasheet claims 500MHz */
+ { IMX8MP_CLK_DRAM_CORE, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GIC, 400 * HZ_PER_MHZ },
+ { /* Sentinel */ }
+};
+
+static const struct imx8mp_clock_constraints imx8mp_clock_overdrive_constraints[] = {
+ { IMX8MP_CLK_M7_CORE, 800 * HZ_PER_MHZ},
+ { IMX8MP_CLK_ML_CORE, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU3D_CORE, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU3D_SHADER_CORE, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU2D_CORE, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_AUDIO_AXI_SRC, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_HSIO_AXI, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_ISP, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_BUS, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_AXI, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_HDMI_AXI, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU_AXI, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GPU_AHB, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_NOC, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_NOC_IO, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_ML_AHB, 400 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_G1, 800 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_G2, 700 * HZ_PER_MHZ },
+ { IMX8MP_CLK_MEDIA_CAM1_PIX, 500 * HZ_PER_MHZ },
+ { IMX8MP_CLK_VPU_VC8000E, 500 * HZ_PER_MHZ }, /* Datasheet claims 400MHz */
+ { IMX8MP_CLK_DRAM_CORE, 1000 * HZ_PER_MHZ },
+ { IMX8MP_CLK_GIC, 500 * HZ_PER_MHZ },
+ { /* Sentinel */ }
+};
+
+static void imx8mp_clocks_apply_constraints(const struct imx8mp_clock_constraints constraints[])
+{
+ const struct imx8mp_clock_constraints *constr;
+
+ for (constr = constraints; constr->clkid; constr++)
+ clk_hw_set_rate_range(hws[constr->clkid], 0, constr->maxrate);
+}
+
static int imx8mp_clocks_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np;
void __iomem *anatop_base, *ccm_base;
+ const char *opmode;
+ int err;
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mp-anatop");
- anatop_base = of_iomap(np, 0);
+ anatop_base = devm_of_iomap(dev, np, 0, NULL);
of_node_put(np);
- if (WARN_ON(!anatop_base))
- return -ENOMEM;
+ if (WARN_ON(IS_ERR(anatop_base)))
+ return PTR_ERR(anatop_base);
np = dev->of_node;
ccm_base = devm_platform_ioremap_resource(pdev, 0);
- if (WARN_ON(IS_ERR(ccm_base))) {
- iounmap(anatop_base);
+ if (WARN_ON(IS_ERR(ccm_base)))
return PTR_ERR(ccm_base);
- }
- clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, IMX8MP_CLK_END), GFP_KERNEL);
- if (WARN_ON(!clk_hw_data)) {
- iounmap(anatop_base);
+ clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX8MP_CLK_END), GFP_KERNEL);
+ if (WARN_ON(!clk_hw_data))
return -ENOMEM;
- }
clk_hw_data->num = IMX8MP_CLK_END;
hws = clk_hw_data->hws;
hws[IMX8MP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX8MP_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m");
- hws[IMX8MP_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k");
- hws[IMX8MP_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1");
- hws[IMX8MP_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2");
- hws[IMX8MP_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3");
- hws[IMX8MP_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4");
+ hws[IMX8MP_CLK_24M] = imx_get_clk_hw_by_name(np, "osc_24m");
+ hws[IMX8MP_CLK_32K] = imx_get_clk_hw_by_name(np, "osc_32k");
+ hws[IMX8MP_CLK_EXT1] = imx_get_clk_hw_by_name(np, "clk_ext1");
+ hws[IMX8MP_CLK_EXT2] = imx_get_clk_hw_by_name(np, "clk_ext2");
+ hws[IMX8MP_CLK_EXT3] = imx_get_clk_hw_by_name(np, "clk_ext3");
+ hws[IMX8MP_CLK_EXT4] = imx_get_clk_hw_by_name(np, "clk_ext4");
hws[IMX8MP_AUDIO_PLL1_REF_SEL] = imx_clk_hw_mux("audio_pll1_ref_sel", anatop_base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MP_AUDIO_PLL2_REF_SEL] = imx_clk_hw_mux("audio_pll2_ref_sel", anatop_base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
@@ -536,7 +673,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800);
hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880);
- hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900);
+ hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900);
hws[IMX8MP_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mp_vpu_bus_sels, ccm_base + 0x8980);
hws[IMX8MP_CLK_MEDIA_AXI] = imx8m_clk_hw_composite_bus("media_axi", imx8mp_media_axi_sels, ccm_base + 0x8a00);
hws[IMX8MP_CLK_MEDIA_APB] = imx8m_clk_hw_composite_bus("media_apb", imx8mp_media_apb_sels, ccm_base + 0x8a80);
@@ -552,12 +689,12 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
hws[IMX8MP_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb_root", imx8mp_ahb_sels, ccm_base + 0x9000);
hws[IMX8MP_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mp_audio_ahb_sels, ccm_base + 0x9100);
hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200);
- hws[IMX8MP_CLK_MEDIA_DISP2_PIX] = imx8m_clk_hw_composite("media_disp2_pix", imx8mp_media_disp_pix_sels, ccm_base + 0x9300);
+ hws[IMX8MP_CLK_MEDIA_DISP2_PIX] = imx8m_clk_hw_composite_bus_flags("media_disp2_pix", imx8mp_media_disp_pix_sels, ccm_base + 0x9300, CLK_SET_RATE_PARENT);
hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1);
- hws[IMX8MP_CLK_DRAM_ALT] = imx8m_clk_hw_composite("dram_alt", imx8mp_dram_alt_sels, ccm_base + 0xa000);
- hws[IMX8MP_CLK_DRAM_APB] = imx8m_clk_hw_composite_critical("dram_apb", imx8mp_dram_apb_sels, ccm_base + 0xa080);
+ hws[IMX8MP_CLK_DRAM_ALT] = imx8m_clk_hw_fw_managed_composite("dram_alt", imx8mp_dram_alt_sels, ccm_base + 0xa000);
+ hws[IMX8MP_CLK_DRAM_APB] = imx8m_clk_hw_fw_managed_composite_critical("dram_apb", imx8mp_dram_apb_sels, ccm_base + 0xa080);
hws[IMX8MP_CLK_VPU_G1] = imx8m_clk_hw_composite("vpu_g1", imx8mp_vpu_g1_sels, ccm_base + 0xa100);
hws[IMX8MP_CLK_VPU_G2] = imx8m_clk_hw_composite("vpu_g2", imx8mp_vpu_g2_sels, ccm_base + 0xa180);
hws[IMX8MP_CLK_CAN1] = imx8m_clk_hw_composite("can1", imx8mp_can1_sels, ccm_base + 0xa200);
@@ -568,7 +705,6 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
hws[IMX8MP_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mp_sai1_sels, ccm_base + 0xa580);
hws[IMX8MP_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mp_sai2_sels, ccm_base + 0xa600);
hws[IMX8MP_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mp_sai3_sels, ccm_base + 0xa680);
- hws[IMX8MP_CLK_SAI4] = imx8m_clk_hw_composite("sai4", imx8mp_sai4_sels, ccm_base + 0xa700);
hws[IMX8MP_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mp_sai5_sels, ccm_base + 0xa780);
hws[IMX8MP_CLK_SAI6] = imx8m_clk_hw_composite("sai6", imx8mp_sai6_sels, ccm_base + 0xa800);
hws[IMX8MP_CLK_ENET_QOS] = imx8m_clk_hw_composite("enet_qos", imx8mp_enet_qos_sels, ccm_base + 0xa880);
@@ -615,7 +751,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
hws[IMX8MP_CLK_USDHC3] = imx8m_clk_hw_composite("usdhc3", imx8mp_usdhc3_sels, ccm_base + 0xbc80);
hws[IMX8MP_CLK_MEDIA_CAM1_PIX] = imx8m_clk_hw_composite("media_cam1_pix", imx8mp_media_cam1_pix_sels, ccm_base + 0xbd00);
hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF] = imx8m_clk_hw_composite("media_mipi_phy1_ref", imx8mp_media_mipi_phy1_ref_sels, ccm_base + 0xbd80);
- hws[IMX8MP_CLK_MEDIA_DISP1_PIX] = imx8m_clk_hw_composite("media_disp1_pix", imx8mp_media_disp_pix_sels, ccm_base + 0xbe00);
+ hws[IMX8MP_CLK_MEDIA_DISP1_PIX] = imx8m_clk_hw_composite_bus_flags("media_disp1_pix", imx8mp_media_disp_pix_sels, ccm_base + 0xbe00, CLK_SET_RATE_PARENT);
hws[IMX8MP_CLK_MEDIA_CAM2_PIX] = imx8m_clk_hw_composite("media_cam2_pix", imx8mp_media_cam2_pix_sels, ccm_base + 0xbe80);
hws[IMX8MP_CLK_MEDIA_LDB] = imx8m_clk_hw_composite("media_ldb", imx8mp_media_ldb_sels, ccm_base + 0xbf00);
hws[IMX8MP_CLK_MEMREPAIR] = imx8m_clk_hw_composite_critical("mem_repair", imx8mp_memrepair_sels, ccm_base + 0xbf80);
@@ -665,15 +801,16 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
hws[IMX8MP_CLK_CAN1_ROOT] = imx_clk_hw_gate2("can1_root_clk", "can1", ccm_base + 0x4350, 0);
hws[IMX8MP_CLK_CAN2_ROOT] = imx_clk_hw_gate2("can2_root_clk", "can2", ccm_base + 0x4360, 0);
hws[IMX8MP_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_root_clk", "ipg_root", ccm_base + 0x43a0, 0);
- hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
hws[IMX8MP_CLK_SIM_ENET_ROOT] = imx_clk_hw_gate4("sim_enet_root_clk", "enet_axi", ccm_base + 0x4400, 0);
+ hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
hws[IMX8MP_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", ccm_base + 0x4450, 0);
hws[IMX8MP_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", ccm_base + 0x4460, 0);
hws[IMX8MP_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", ccm_base + 0x4490, 0);
hws[IMX8MP_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", ccm_base + 0x44a0, 0);
hws[IMX8MP_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", ccm_base + 0x44b0, 0);
hws[IMX8MP_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", ccm_base + 0x44c0, 0);
- hws[IMX8MP_CLK_USB_ROOT] = imx_clk_hw_gate4("usb_root_clk", "hsio_axi", ccm_base + 0x44d0, 0);
+ hws[IMX8MP_CLK_USB_ROOT] = imx_clk_hw_gate2_shared2("usb_root_clk", "hsio_axi", ccm_base + 0x44d0, 0, &share_count_usb);
+ hws[IMX8MP_CLK_USB_SUSP] = imx_clk_hw_gate2_shared2("usb_suspend_clk", "osc_32k", ccm_base + 0x44d0, 0, &share_count_usb);
hws[IMX8MP_CLK_USB_PHY_ROOT] = imx_clk_hw_gate4("usb_phy_root_clk", "usb_phy_ref", ccm_base + 0x44f0, 0);
hws[IMX8MP_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", ccm_base + 0x4510, 0);
hws[IMX8MP_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", ccm_base + 0x4520, 0);
@@ -693,13 +830,23 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
hws[IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp1_pix_root_clk", "media_disp1_pix", ccm_base + 0x45d0, 0, &share_count_media);
hws[IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp2_pix_root_clk", "media_disp2_pix", ccm_base + 0x45d0, 0, &share_count_media);
hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF_ROOT] = imx_clk_hw_gate2_shared2("media_mipi_phy1_ref_root", "media_mipi_phy1_ref", ccm_base + 0x45d0, 0, &share_count_media);
+ hws[IMX8MP_CLK_MEDIA_LDB_ROOT] = imx_clk_hw_gate2_shared2("media_ldb_root_clk", "media_ldb", ccm_base + 0x45d0, 0, &share_count_media);
hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp", ccm_base + 0x45d0, 0, &share_count_media);
hws[IMX8MP_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", ccm_base + 0x45e0, 0);
hws[IMX8MP_CLK_HDMI_ROOT] = imx_clk_hw_gate4("hdmi_root_clk", "hdmi_axi", ccm_base + 0x45f0, 0);
hws[IMX8MP_CLK_TSENSOR_ROOT] = imx_clk_hw_gate4("tsensor_root_clk", "ipg_root", ccm_base + 0x4620, 0);
hws[IMX8MP_CLK_VPU_ROOT] = imx_clk_hw_gate4("vpu_root_clk", "vpu_bus", ccm_base + 0x4630, 0);
- hws[IMX8MP_CLK_AUDIO_ROOT] = imx_clk_hw_gate4("audio_root_clk", "audio_ahb", ccm_base + 0x4650, 0);
+
+ hws[IMX8MP_CLK_AUDIO_AHB_ROOT] = imx_clk_hw_gate2_shared2("audio_ahb_root", "audio_ahb", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_AUDIO_AXI_ROOT] = imx_clk_hw_gate2_shared2("audio_axi_root", "audio_axi", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI1_ROOT] = imx_clk_hw_gate2_shared2("sai1_root", "sai1", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI2_ROOT] = imx_clk_hw_gate2_shared2("sai2_root", "sai2", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI3_ROOT] = imx_clk_hw_gate2_shared2("sai3_root", "sai3", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI5_ROOT] = imx_clk_hw_gate2_shared2("sai5_root", "sai5", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI6_ROOT] = imx_clk_hw_gate2_shared2("sai6_root", "sai6", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_SAI7_ROOT] = imx_clk_hw_gate2_shared2("sai7_root", "sai7", ccm_base + 0x4650, 0, &share_count_audio);
+ hws[IMX8MP_CLK_PDM_ROOT] = imx_clk_hw_gate2_shared2("pdm_root", "pdm", ccm_base + 0x4650, 0, &share_count_audio);
hws[IMX8MP_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core",
hws[IMX8MP_CLK_A53_CORE]->clk,
@@ -709,9 +856,24 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
imx_check_clk_hws(hws, IMX8MP_CLK_END);
- of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
+ imx8mp_clocks_apply_constraints(imx8mp_clock_common_constraints);
+
+ err = of_property_read_string(np, "fsl,operating-mode", &opmode);
+ if (!err) {
+ if (!strcmp(opmode, "nominal"))
+ imx8mp_clocks_apply_constraints(imx8mp_clock_nominal_constraints);
+ else if (!strcmp(opmode, "overdrive"))
+ imx8mp_clocks_apply_constraints(imx8mp_clock_overdrive_constraints);
+ }
+
+ err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
+ if (err < 0) {
+ dev_err(dev, "failed to register hws for i.MX8MP\n");
+ imx_unregister_hw_clocks(hws, IMX8MP_CLK_END);
+ return err;
+ }
- imx_register_uart_clocks(4);
+ imx_register_uart_clocks();
return 0;
}
diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
index 882dcad4817d..f70ed231b92d 100644
--- a/drivers/clk/imx/clk-imx8mq.c
+++ b/drivers/clk/imx/clk-imx8mq.c
@@ -288,8 +288,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
void __iomem *base;
int err;
- clk_hw_data = kzalloc(struct_size(clk_hw_data, hws,
- IMX8MQ_CLK_END), GFP_KERNEL);
+ clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX8MQ_CLK_END), GFP_KERNEL);
if (WARN_ON(!clk_hw_data))
return -ENOMEM;
@@ -297,19 +296,21 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
hws = clk_hw_data->hws;
hws[IMX8MQ_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- hws[IMX8MQ_CLK_32K] = imx_obtain_fixed_clk_hw(np, "ckil");
- hws[IMX8MQ_CLK_25M] = imx_obtain_fixed_clk_hw(np, "osc_25m");
- hws[IMX8MQ_CLK_27M] = imx_obtain_fixed_clk_hw(np, "osc_27m");
- hws[IMX8MQ_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1");
- hws[IMX8MQ_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2");
- hws[IMX8MQ_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3");
- hws[IMX8MQ_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4");
+ hws[IMX8MQ_CLK_32K] = imx_get_clk_hw_by_name(np, "ckil");
+ hws[IMX8MQ_CLK_25M] = imx_get_clk_hw_by_name(np, "osc_25m");
+ hws[IMX8MQ_CLK_27M] = imx_get_clk_hw_by_name(np, "osc_27m");
+ hws[IMX8MQ_CLK_EXT1] = imx_get_clk_hw_by_name(np, "clk_ext1");
+ hws[IMX8MQ_CLK_EXT2] = imx_get_clk_hw_by_name(np, "clk_ext2");
+ hws[IMX8MQ_CLK_EXT3] = imx_get_clk_hw_by_name(np, "clk_ext3");
+ hws[IMX8MQ_CLK_EXT4] = imx_get_clk_hw_by_name(np, "clk_ext4");
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-anatop");
- base = of_iomap(np, 0);
+ base = devm_of_iomap(dev, np, 0, NULL);
of_node_put(np);
- if (WARN_ON(!base))
- return -ENOMEM;
+ if (WARN_ON(IS_ERR(base))) {
+ err = PTR_ERR(base);
+ goto unregister_hws;
+ }
hws[IMX8MQ_ARM_PLL_REF_SEL] = imx_clk_hw_mux("arm_pll_ref_sel", base + 0x28, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
hws[IMX8MQ_GPU_PLL_REF_SEL] = imx_clk_hw_mux("gpu_pll_ref_sel", base + 0x18, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels));
@@ -395,8 +396,10 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
np = dev->of_node;
base = devm_platform_ioremap_resource(pdev, 0);
- if (WARN_ON(IS_ERR(base)))
- return PTR_ERR(base);
+ if (WARN_ON(IS_ERR(base))) {
+ err = PTR_ERR(base);
+ goto unregister_hws;
+ }
/* CORE */
hws[IMX8MQ_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mq_a53_sels, base + 0x8000);
@@ -601,7 +604,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
goto unregister_hws;
}
- imx_register_uart_clocks(4);
+ imx_register_uart_clocks();
return 0;
diff --git a/drivers/clk/imx/clk-imx8qm-rsrc.c b/drivers/clk/imx/clk-imx8qm-rsrc.c
index 87e0b6ac027e..dadfdd9a4541 100644
--- a/drivers/clk/imx/clk-imx8qm-rsrc.c
+++ b/drivers/clk/imx/clk-imx8qm-rsrc.c
@@ -43,6 +43,8 @@ static const u32 imx8qm_clk_scu_rsrc_table[] = {
IMX_SC_R_FTM_0,
IMX_SC_R_FTM_1,
IMX_SC_R_CAN_0,
+ IMX_SC_R_CAN_1,
+ IMX_SC_R_CAN_2,
IMX_SC_R_GPU_0_PID0,
IMX_SC_R_GPU_1_PID0,
IMX_SC_R_PWM_0,
@@ -65,7 +67,6 @@ static const u32 imx8qm_clk_scu_rsrc_table[] = {
IMX_SC_R_SDHC_2,
IMX_SC_R_ENET_0,
IMX_SC_R_ENET_1,
- IMX_SC_R_MLB_0,
IMX_SC_R_USB_2,
IMX_SC_R_NAND,
IMX_SC_R_LVDS_0,
@@ -79,8 +80,6 @@ static const u32 imx8qm_clk_scu_rsrc_table[] = {
IMX_SC_R_M4_0_I2C,
IMX_SC_R_M4_1_I2C,
IMX_SC_R_AUDIO_PLL_0,
- IMX_SC_R_VPU_UART,
- IMX_SC_R_VPUCORE,
IMX_SC_R_MIPI_0,
IMX_SC_R_MIPI_0_PWM_0,
IMX_SC_R_MIPI_0_I2C_0,
diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.c b/drivers/clk/imx/clk-imx8qxp-lpcg.c
index 5e31a6a24b3a..1dae3410ee99 100644
--- a/drivers/clk/imx/clk-imx8qxp-lpcg.c
+++ b/drivers/clk/imx/clk-imx8qxp-lpcg.c
@@ -9,8 +9,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
@@ -183,7 +181,6 @@ static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev,
unsigned int bit_offset[IMX_LPCG_MAX_CLKS];
struct clk_hw_onecell_data *clk_data;
struct clk_hw **clk_hws;
- struct resource *res;
void __iomem *base;
int count;
int idx;
@@ -193,8 +190,7 @@ static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev,
if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg"))
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -271,7 +267,6 @@ static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev,
if (ret)
goto unreg;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.h b/drivers/clk/imx/clk-imx8qxp-lpcg.h
index 2a37ce57c500..ebca8fa9268f 100644
--- a/drivers/clk/imx/clk-imx8qxp-lpcg.h
+++ b/drivers/clk/imx/clk-imx8qxp-lpcg.h
@@ -42,7 +42,6 @@
#define CONN_ENET_0_LPCG 0x30000
#define CONN_ENET_1_LPCG 0x40000
#define CONN_DTCP_LPCG 0x50000
-#define CONN_MLB_LPCG 0x60000
#define CONN_USB_2_LPCG 0x70000
#define CONN_USB_3_LPCG 0x80000
#define CONN_NAND_LPCG 0x90000
diff --git a/drivers/clk/imx/clk-imx8qxp-rsrc.c b/drivers/clk/imx/clk-imx8qxp-rsrc.c
index df09f2a7996d..585c425524a4 100644
--- a/drivers/clk/imx/clk-imx8qxp-rsrc.c
+++ b/drivers/clk/imx/clk-imx8qxp-rsrc.c
@@ -54,15 +54,17 @@ static const u32 imx8qxp_clk_scu_rsrc_table[] = {
IMX_SC_R_SDHC_2,
IMX_SC_R_ENET_0,
IMX_SC_R_ENET_1,
- IMX_SC_R_MLB_0,
IMX_SC_R_USB_2,
IMX_SC_R_NAND,
IMX_SC_R_LVDS_0,
IMX_SC_R_LVDS_1,
+ IMX_SC_R_M4_0_UART,
IMX_SC_R_M4_0_I2C,
IMX_SC_R_ELCDIF_PLL,
IMX_SC_R_AUDIO_PLL_0,
IMX_SC_R_PI_0,
+ IMX_SC_R_PI_0_PWM_0,
+ IMX_SC_R_PI_0_I2C_0,
IMX_SC_R_PI_0_PLL,
IMX_SC_R_MIPI_0,
IMX_SC_R_MIPI_0_PWM_0,
diff --git a/drivers/clk/imx/clk-imx8qxp.c b/drivers/clk/imx/clk-imx8qxp.c
index 546a3703bfeb..3ae162625bb1 100644
--- a/drivers/clk/imx/clk-imx8qxp.c
+++ b/drivers/clk/imx/clk-imx8qxp.c
@@ -9,7 +9,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -67,6 +66,22 @@ static const char * const lcd_pxl_sels[] = {
"lcd_pxl_bypass_div_clk",
};
+static const char *const lvds0_sels[] = {
+ "clk_dummy",
+ "clk_dummy",
+ "clk_dummy",
+ "clk_dummy",
+ "lvds0_bypass_clk",
+};
+
+static const char *const lvds1_sels[] = {
+ "clk_dummy",
+ "clk_dummy",
+ "clk_dummy",
+ "clk_dummy",
+ "lvds1_bypass_clk",
+};
+
static const char * const mipi_sels[] = {
"clk_dummy",
"clk_dummy",
@@ -75,6 +90,22 @@ static const char * const mipi_sels[] = {
"clk_dummy",
};
+static const char * const mipi0_phy_sels[] = {
+ "clk_dummy",
+ "clk_dummy",
+ "mipi_pll_div2_clk",
+ "clk_dummy",
+ "mipi0_bypass_clk",
+};
+
+static const char * const mipi1_phy_sels[] = {
+ "clk_dummy",
+ "clk_dummy",
+ "mipi_pll_div2_clk",
+ "clk_dummy",
+ "mipi1_bypass_clk",
+};
+
static const char * const lcd_sels[] = {
"clk_dummy",
"clk_dummy",
@@ -91,6 +122,11 @@ static const char * const pi_pll0_sels[] = {
"clk_dummy",
};
+static inline bool clk_on_imx8dxl(struct device_node *node)
+{
+ return of_device_is_compatible(node, "fsl,imx8dxl-clk");
+}
+
static int imx8qxp_clk_probe(struct platform_device *pdev)
{
struct device_node *ccm_node = pdev->dev.of_node;
@@ -148,10 +184,10 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
imx_clk_scu("adc0_clk", IMX_SC_R_ADC_0, IMX_SC_PM_CLK_PER);
imx_clk_scu("adc1_clk", IMX_SC_R_ADC_1, IMX_SC_PM_CLK_PER);
imx_clk_scu("pwm_clk", IMX_SC_R_LCD_0_PWM_0, IMX_SC_PM_CLK_PER);
+ imx_clk_scu("elcdif_pll", IMX_SC_R_ELCDIF_PLL, IMX_SC_PM_CLK_PLL);
imx_clk_scu2("lcd_clk", lcd_sels, ARRAY_SIZE(lcd_sels), IMX_SC_R_LCD_0, IMX_SC_PM_CLK_PER);
- imx_clk_scu2("lcd_pxl_clk", lcd_pxl_sels, ARRAY_SIZE(lcd_pxl_sels), IMX_SC_R_LCD_0, IMX_SC_PM_CLK_MISC0);
imx_clk_scu("lcd_pxl_bypass_div_clk", IMX_SC_R_LCD_0, IMX_SC_PM_CLK_BYPASS);
- imx_clk_scu("elcdif_pll", IMX_SC_R_ELCDIF_PLL, IMX_SC_PM_CLK_PLL);
+ imx_clk_scu2("lcd_pxl_clk", lcd_pxl_sels, ARRAY_SIZE(lcd_pxl_sels), IMX_SC_R_LCD_0, IMX_SC_PM_CLK_MISC0);
/* Audio SS */
imx_clk_scu("audio_pll0_clk", IMX_SC_R_AUDIO_PLL_0, IMX_SC_PM_CLK_PLL);
@@ -170,13 +206,15 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
imx_clk_mux_gpr_scu("enet0_rgmii_txc_sel", enet0_rgmii_txc_sels, ARRAY_SIZE(enet0_rgmii_txc_sels), IMX_SC_R_ENET_0, IMX_SC_C_TXCLK);
imx_clk_scu("enet0_bypass_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_BYPASS);
imx_clk_gate_gpr_scu("enet0_ref_50_clk", "clk_dummy", IMX_SC_R_ENET_0, IMX_SC_C_DISABLE_50, true);
- imx_clk_scu("enet0_rgmii_rx_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_MISC0);
+ if (!clk_on_imx8dxl(ccm_node)) {
+ imx_clk_scu("enet0_rgmii_rx_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_MISC0);
+ imx_clk_scu("enet1_rgmii_rx_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_MISC0);
+ }
imx_clk_scu("enet1_root_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_PER);
imx_clk_divider_gpr_scu("enet1_ref_div", "enet1_root_clk", IMX_SC_R_ENET_1, IMX_SC_C_CLKDIV);
imx_clk_mux_gpr_scu("enet1_rgmii_txc_sel", enet1_rgmii_txc_sels, ARRAY_SIZE(enet1_rgmii_txc_sels), IMX_SC_R_ENET_1, IMX_SC_C_TXCLK);
imx_clk_scu("enet1_bypass_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_BYPASS);
imx_clk_gate_gpr_scu("enet1_ref_50_clk", "clk_dummy", IMX_SC_R_ENET_1, IMX_SC_C_DISABLE_50, true);
- imx_clk_scu("enet1_rgmii_rx_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_MISC0);
imx_clk_scu("gpmi_io_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_MST_BUS);
imx_clk_scu("gpmi_bch_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_PER);
imx_clk_scu("usb3_aclk_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_PER);
@@ -184,42 +222,41 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
imx_clk_scu("usb3_lpm_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MISC);
/* Display controller SS */
- imx_clk_scu2("dc0_disp0_clk", dc0_sels, ARRAY_SIZE(dc0_sels), IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC0);
- imx_clk_scu2("dc0_disp1_clk", dc0_sels, ARRAY_SIZE(dc0_sels), IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC1);
imx_clk_scu("dc0_pll0_clk", IMX_SC_R_DC_0_PLL_0, IMX_SC_PM_CLK_PLL);
imx_clk_scu("dc0_pll1_clk", IMX_SC_R_DC_0_PLL_1, IMX_SC_PM_CLK_PLL);
imx_clk_scu("dc0_bypass0_clk", IMX_SC_R_DC_0_VIDEO0, IMX_SC_PM_CLK_BYPASS);
+ imx_clk_scu2("dc0_disp0_clk", dc0_sels, ARRAY_SIZE(dc0_sels), IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC0);
+ imx_clk_scu2("dc0_disp1_clk", dc0_sels, ARRAY_SIZE(dc0_sels), IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC1);
imx_clk_scu("dc0_bypass1_clk", IMX_SC_R_DC_0_VIDEO1, IMX_SC_PM_CLK_BYPASS);
- imx_clk_scu2("dc1_disp0_clk", dc1_sels, ARRAY_SIZE(dc1_sels), IMX_SC_R_DC_1, IMX_SC_PM_CLK_MISC0);
- imx_clk_scu2("dc1_disp1_clk", dc1_sels, ARRAY_SIZE(dc1_sels), IMX_SC_R_DC_1, IMX_SC_PM_CLK_MISC1);
imx_clk_scu("dc1_pll0_clk", IMX_SC_R_DC_1_PLL_0, IMX_SC_PM_CLK_PLL);
imx_clk_scu("dc1_pll1_clk", IMX_SC_R_DC_1_PLL_1, IMX_SC_PM_CLK_PLL);
imx_clk_scu("dc1_bypass0_clk", IMX_SC_R_DC_1_VIDEO0, IMX_SC_PM_CLK_BYPASS);
+ imx_clk_scu2("dc1_disp0_clk", dc1_sels, ARRAY_SIZE(dc1_sels), IMX_SC_R_DC_1, IMX_SC_PM_CLK_MISC0);
+ imx_clk_scu2("dc1_disp1_clk", dc1_sels, ARRAY_SIZE(dc1_sels), IMX_SC_R_DC_1, IMX_SC_PM_CLK_MISC1);
imx_clk_scu("dc1_bypass1_clk", IMX_SC_R_DC_1_VIDEO1, IMX_SC_PM_CLK_BYPASS);
/* MIPI-LVDS SS */
imx_clk_scu("mipi0_bypass_clk", IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_BYPASS);
- imx_clk_scu("mipi0_pixel_clk", IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_PER);
- imx_clk_scu("mipi0_lvds_pixel_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_MISC2);
- imx_clk_scu("mipi0_lvds_bypass_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_BYPASS);
- imx_clk_scu("mipi0_lvds_phy_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_MISC3);
+ imx_clk_scu2("mipi0_pixel_clk", mipi0_phy_sels, ARRAY_SIZE(mipi0_phy_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_PER);
+ imx_clk_scu("lvds0_bypass_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_BYPASS);
+ imx_clk_scu2("lvds0_pixel_clk", lvds0_sels, ARRAY_SIZE(lvds0_sels), IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_MISC2);
+ imx_clk_scu2("lvds0_phy_clk", lvds0_sels, ARRAY_SIZE(lvds0_sels), IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_MISC3);
imx_clk_scu2("mipi0_dsi_tx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_MST_BUS);
imx_clk_scu2("mipi0_dsi_rx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_SLV_BUS);
- imx_clk_scu2("mipi0_dsi_phy_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_PHY);
+ imx_clk_scu2("mipi0_dsi_phy_clk", mipi0_phy_sels, ARRAY_SIZE(mipi0_phy_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_PHY);
imx_clk_scu("mipi0_i2c0_clk", IMX_SC_R_MIPI_0_I2C_0, IMX_SC_PM_CLK_MISC2);
imx_clk_scu("mipi0_i2c1_clk", IMX_SC_R_MIPI_0_I2C_1, IMX_SC_PM_CLK_MISC2);
imx_clk_scu("mipi0_pwm0_clk", IMX_SC_R_MIPI_0_PWM_0, IMX_SC_PM_CLK_PER);
imx_clk_scu("mipi1_bypass_clk", IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_BYPASS);
- imx_clk_scu("mipi1_pixel_clk", IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_PER);
- imx_clk_scu("mipi1_lvds_pixel_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_MISC2);
- imx_clk_scu("mipi1_lvds_bypass_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_BYPASS);
- imx_clk_scu("mipi1_lvds_phy_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_MISC3);
-
+ imx_clk_scu2("mipi1_pixel_clk", mipi1_phy_sels, ARRAY_SIZE(mipi1_phy_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_PER);
+ imx_clk_scu("lvds1_bypass_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_BYPASS);
+ imx_clk_scu2("lvds1_pixel_clk", lvds1_sels, ARRAY_SIZE(lvds1_sels), IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_MISC2);
+ imx_clk_scu2("lvds1_phy_clk", lvds1_sels, ARRAY_SIZE(lvds1_sels), IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_MISC3);
imx_clk_scu2("mipi1_dsi_tx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_MST_BUS);
imx_clk_scu2("mipi1_dsi_rx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_SLV_BUS);
- imx_clk_scu2("mipi1_dsi_phy_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_PHY);
+ imx_clk_scu2("mipi1_dsi_phy_clk", mipi1_phy_sels, ARRAY_SIZE(mipi1_phy_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_PHY);
imx_clk_scu("mipi1_i2c0_clk", IMX_SC_R_MIPI_1_I2C_0, IMX_SC_PM_CLK_MISC2);
imx_clk_scu("mipi1_i2c1_clk", IMX_SC_R_MIPI_1_I2C_1, IMX_SC_PM_CLK_MISC2);
imx_clk_scu("mipi1_pwm0_clk", IMX_SC_R_MIPI_1_PWM_0, IMX_SC_PM_CLK_PER);
diff --git a/drivers/clk/imx/clk-imx8ulp-sim-lpav.c b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
new file mode 100644
index 000000000000..990c95b89b75
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <dt-bindings/clock/imx8ulp-clock.h>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define SYSCTRL0 0x8
+
+#define IMX8ULP_HIFI_CLK_GATE(gname, cname, pname, bidx) \
+ { \
+ .name = gname "_cg", \
+ .id = IMX8ULP_CLK_SIM_LPAV_HIFI_##cname, \
+ .parent = { .fw_name = pname }, \
+ .bit = bidx, \
+ }
+
+struct clk_imx8ulp_sim_lpav_data {
+ spinlock_t lock; /* shared by MUX, clock gate and reset */
+ unsigned long flags; /* for spinlock usage */
+ struct clk_hw_onecell_data clk_data; /* keep last */
+};
+
+struct clk_imx8ulp_sim_lpav_gate {
+ const char *name;
+ int id;
+ const struct clk_parent_data parent;
+ u8 bit;
+};
+
+static struct clk_imx8ulp_sim_lpav_gate gates[] = {
+ IMX8ULP_HIFI_CLK_GATE("hifi_core", CORE, "core", 17),
+ IMX8ULP_HIFI_CLK_GATE("hifi_pbclk", PBCLK, "bus", 18),
+ IMX8ULP_HIFI_CLK_GATE("hifi_plat", PLAT, "plat", 19)
+};
+
+static void clk_imx8ulp_sim_lpav_lock(void *arg) __acquires(&data->lock)
+{
+ struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
+
+ spin_lock_irqsave(&data->lock, data->flags);
+}
+
+static void clk_imx8ulp_sim_lpav_unlock(void *arg) __releases(&data->lock)
+{
+ struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
+
+ spin_unlock_irqrestore(&data->lock, data->flags);
+}
+
+static int clk_imx8ulp_sim_lpav_probe(struct platform_device *pdev)
+{
+ const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .lock = clk_imx8ulp_sim_lpav_lock,
+ .unlock = clk_imx8ulp_sim_lpav_unlock,
+ .lock_arg = &pdev->dev,
+ };
+ struct clk_imx8ulp_sim_lpav_data *data;
+ struct auxiliary_device *adev;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk_hw *hw;
+ int i, ret;
+
+ data = devm_kzalloc(&pdev->dev,
+ struct_size(data, clk_data.hws, ARRAY_SIZE(gates)),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, data);
+
+ /*
+ * this lock is used directly by the clock gate and indirectly
+ * by the reset and mux controller via the regmap API
+ */
+ spin_lock_init(&data->lock);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(base),
+ "failed to ioremap base\n");
+ /*
+ * although the clock gate doesn't use the regmap API to modify the
+ * registers, we still need the regmap because of the reset auxiliary
+ * driver and the MUX drivers, which use the parent device's regmap
+ */
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
+ "failed to initialize regmap\n");
+
+ data->clk_data.num = ARRAY_SIZE(gates);
+
+ for (i = 0; i < ARRAY_SIZE(gates); i++) {
+ hw = devm_clk_hw_register_gate_parent_data(&pdev->dev,
+ gates[i].name,
+ &gates[i].parent,
+ CLK_SET_RATE_PARENT,
+ base + SYSCTRL0,
+ gates[i].bit,
+ 0x0, &data->lock);
+ if (IS_ERR(hw))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hw),
+ "failed to register %s gate\n",
+ gates[i].name);
+
+ data->clk_data.hws[i] = hw;
+ }
+
+ adev = devm_auxiliary_device_create(&pdev->dev, "reset", NULL);
+ if (!adev)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "failed to register aux reset\n");
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev,
+ of_clk_hw_onecell_get,
+ &data->clk_data);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register clk hw provider\n");
+
+ /* used to probe MUX child device */
+ return devm_of_platform_populate(&pdev->dev);
+}
+
+static const struct of_device_id clk_imx8ulp_sim_lpav_of_match[] = {
+ { .compatible = "fsl,imx8ulp-sim-lpav" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clk_imx8ulp_sim_lpav_of_match);
+
+static struct platform_driver clk_imx8ulp_sim_lpav_driver = {
+ .probe = clk_imx8ulp_sim_lpav_probe,
+ .driver = {
+ .name = "clk-imx8ulp-sim-lpav",
+ .of_match_table = clk_imx8ulp_sim_lpav_of_match,
+ },
+};
+module_platform_driver(clk_imx8ulp_sim_lpav_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("i.MX8ULP LPAV System Integration Module (SIM) clock driver");
+MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>");
diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c
index 8eb1af2d6429..535b6364ca7e 100644
--- a/drivers/clk/imx/clk-imx8ulp.c
+++ b/drivers/clk/imx/clk-imx8ulp.c
@@ -7,7 +7,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
@@ -167,7 +167,7 @@ static int imx8ulp_clk_cgc1_init(struct platform_device *pdev)
clks[IMX8ULP_CLK_SPLL2_PRE_SEL] = imx_clk_hw_mux_flags("spll2_pre_sel", base + 0x510, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
clks[IMX8ULP_CLK_SPLL3_PRE_SEL] = imx_clk_hw_mux_flags("spll3_pre_sel", base + 0x610, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE);
- clks[IMX8ULP_CLK_SPLL2] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "spll2", "spll2_pre_sel", base + 0x500);
+ clks[IMX8ULP_CLK_SPLL2] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP_1GHZ, "spll2", "spll2_pre_sel", base + 0x500);
clks[IMX8ULP_CLK_SPLL3] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "spll3", "spll3_pre_sel", base + 0x600);
clks[IMX8ULP_CLK_SPLL3_VCODIV] = imx_clk_hw_divider("spll3_vcodiv", "spll3", base + 0x604, 0, 6);
@@ -198,10 +198,10 @@ static int imx8ulp_clk_cgc1_init(struct platform_device *pdev)
clks[IMX8ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x34, 28, 2, nic_sels, ARRAY_SIZE(nic_sels));
clks[IMX8ULP_CLK_NIC_AD_DIVPLAT] = imx_clk_hw_divider_flags("nic_ad_divplat", "nic_sel", base + 0x34, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
- clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
- clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
- clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "nic_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
- clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "nic_ad_divplat", base + 0x38, 0, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+ clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT);
+ clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT);
+ clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "xbar_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT);
+ clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "xbar_divbus", base + 0x38, 0, 6, CLK_SET_RATE_PARENT);
clks[IMX8ULP_CLK_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("sosc_div1_gate", "sosc", base + 0x108, 7);
clks[IMX8ULP_CLK_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("sosc_div2_gate", "sosc", base + 0x108, 15);
@@ -255,9 +255,9 @@ static int imx8ulp_clk_cgc2_init(struct platform_device *pdev)
clks[IMX8ULP_CLK_HIFI_DIVCORE] = imx_clk_hw_divider("hifi_core_div", "hifi_sel", base + 0x14, 21, 6);
clks[IMX8ULP_CLK_HIFI_DIVPLAT] = imx_clk_hw_divider("hifi_plat_div", "hifi_core_div", base + 0x14, 14, 6);
- clks[IMX8ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x40, 28, 3, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_PARENT_GATE);
- clks[IMX8ULP_CLK_DDR_DIV] = imx_clk_hw_divider_flags("ddr_div", "ddr_sel", base + 0x40, 21, 6, CLK_IS_CRITICAL);
- clks[IMX8ULP_CLK_LPAV_AXI_SEL] = imx_clk_hw_mux("lpav_sel", base + 0x3c, 28, 2, lpav_sels, ARRAY_SIZE(lpav_sels));
+ clks[IMX8ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x40, 28, 3, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_GET_RATE_NOCACHE);
+ clks[IMX8ULP_CLK_DDR_DIV] = imx_clk_hw_divider_flags("ddr_div", "ddr_sel", base + 0x40, 21, 6, CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
+ clks[IMX8ULP_CLK_LPAV_AXI_SEL] = imx_clk_hw_mux2("lpav_sel", base + 0x3c, 28, 2, lpav_sels, ARRAY_SIZE(lpav_sels));
clks[IMX8ULP_CLK_LPAV_AXI_DIV] = imx_clk_hw_divider_flags("lpav_axi_div", "lpav_sel", base + 0x3c, 21, 6, CLK_IS_CRITICAL);
clks[IMX8ULP_CLK_LPAV_AHB_DIV] = imx_clk_hw_divider_flags("lpav_ahb_div", "lpav_axi_div", base + 0x3c, 14, 6, CLK_IS_CRITICAL);
clks[IMX8ULP_CLK_LPAV_BUS_DIV] = imx_clk_hw_divider_flags("lpav_bus_div", "lpav_axi_div", base + 0x3c, 7, 6, CLK_IS_CRITICAL);
@@ -275,14 +275,14 @@ static int imx8ulp_clk_cgc2_init(struct platform_device *pdev)
clks[IMX8ULP_CLK_PLL4_PFD2_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd2_div2_gate", "pll4_pfd2", base + 0x60c, 15);
clks[IMX8ULP_CLK_PLL4_PFD3_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div1_gate", "pll4_pfd3", base + 0x60c, 23);
clks[IMX8ULP_CLK_PLL4_PFD3_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div2_gate", "pll4_pfd3", base + 0x60c, 31);
- clks[IMX8ULP_CLK_PLL4_PFD0_DIV1] = imx_clk_hw_divider("pll4_pfd0_div1", "pll4_pfd0_div1_gate", base + 0x608, 0, 6);
- clks[IMX8ULP_CLK_PLL4_PFD0_DIV2] = imx_clk_hw_divider("pll4_pfd0_div2", "pll4_pfd0_div2_gate", base + 0x608, 8, 6);
- clks[IMX8ULP_CLK_PLL4_PFD1_DIV1] = imx_clk_hw_divider("pll4_pfd1_div1", "pll4_pfd1_div1_gate", base + 0x608, 16, 6);
- clks[IMX8ULP_CLK_PLL4_PFD1_DIV2] = imx_clk_hw_divider("pll4_pfd1_div2", "pll4_pfd1_div2_gate", base + 0x608, 24, 6);
- clks[IMX8ULP_CLK_PLL4_PFD2_DIV1] = imx_clk_hw_divider("pll4_pfd2_div1", "pll4_pfd2_div1_gate", base + 0x60c, 0, 6);
- clks[IMX8ULP_CLK_PLL4_PFD2_DIV2] = imx_clk_hw_divider("pll4_pfd2_div2", "pll4_pfd2_div2_gate", base + 0x60c, 8, 6);
- clks[IMX8ULP_CLK_PLL4_PFD3_DIV1] = imx_clk_hw_divider("pll4_pfd3_div1", "pll4_pfd3_div1_gate", base + 0x60c, 16, 6);
- clks[IMX8ULP_CLK_PLL4_PFD3_DIV2] = imx_clk_hw_divider("pll4_pfd3_div2", "pll4_pfd3_div2_gate", base + 0x60c, 24, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD0_DIV1] = imx_clk_hw_divider_closest("pll4_pfd0_div1", "pll4_pfd0_div1_gate", base + 0x608, 0, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD0_DIV2] = imx_clk_hw_divider_closest("pll4_pfd0_div2", "pll4_pfd0_div2_gate", base + 0x608, 8, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD1_DIV1] = imx_clk_hw_divider_closest("pll4_pfd1_div1", "pll4_pfd1_div1_gate", base + 0x608, 16, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD1_DIV2] = imx_clk_hw_divider_closest("pll4_pfd1_div2", "pll4_pfd1_div2_gate", base + 0x608, 24, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD2_DIV1] = imx_clk_hw_divider_closest("pll4_pfd2_div1", "pll4_pfd2_div1_gate", base + 0x60c, 0, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD2_DIV2] = imx_clk_hw_divider_closest("pll4_pfd2_div2", "pll4_pfd2_div2_gate", base + 0x60c, 8, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD3_DIV1] = imx_clk_hw_divider_closest("pll4_pfd3_div1", "pll4_pfd3_div1_gate", base + 0x60c, 16, 6);
+ clks[IMX8ULP_CLK_PLL4_PFD3_DIV2] = imx_clk_hw_divider_closest("pll4_pfd3_div2", "pll4_pfd3_div2_gate", base + 0x60c, 24, 6);
clks[IMX8ULP_CLK_CGC2_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div1_gate", "sosc", base + 0x108, 7);
clks[IMX8ULP_CLK_CGC2_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div2_gate", "sosc", base + 0x108, 15);
@@ -333,7 +333,6 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev)
clks[IMX8ULP_CLK_WDOG4] = imx8ulp_clk_hw_composite("wdog4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xac, 1);
clks[IMX8ULP_CLK_LPIT1] = imx8ulp_clk_hw_composite("lpit1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xc8, 1);
clks[IMX8ULP_CLK_TPM4] = imx8ulp_clk_hw_composite("tpm4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xcc, 1);
- clks[IMX8ULP_CLK_TPM5] = imx8ulp_clk_hw_composite("tpm5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd0, 1);
clks[IMX8ULP_CLK_FLEXIO1] = imx8ulp_clk_hw_composite("flexio1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd4, 1);
clks[IMX8ULP_CLK_I3C2] = imx8ulp_clk_hw_composite("i3c2", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd8, 1);
clks[IMX8ULP_CLK_LPI2C4] = imx8ulp_clk_hw_composite("lpi2c4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xdc, 1);
@@ -376,8 +375,9 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev)
clks[IMX8ULP_CLK_DMA1_CH29] = imx_clk_hw_gate("pcc_dma1_ch29", "xbar_ad_divplat", base + 0x7c, 30);
clks[IMX8ULP_CLK_DMA1_CH30] = imx_clk_hw_gate("pcc_dma1_ch30", "xbar_ad_divplat", base + 0x80, 30);
clks[IMX8ULP_CLK_DMA1_CH31] = imx_clk_hw_gate("pcc_dma1_ch31", "xbar_ad_divplat", base + 0x84, 30);
- clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate("mu0_b", "xbar_ad_divplat", base + 0x88, 30);
+ clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate_flags("mu0_b", "xbar_ad_divplat", base + 0x88, 30, CLK_IS_CRITICAL);
clks[IMX8ULP_CLK_MU3_A] = imx_clk_hw_gate("mu3_a", "xbar_ad_divplat", base + 0x8c, 30);
+ clks[IMX8ULP_CLK_TPM5] = imx_clk_hw_gate_flags("tpm5", "sosc_div2", base + 0xd0, 30, CLK_IS_CRITICAL);
imx_check_clk_hws(clks, clk_data->num);
@@ -385,7 +385,7 @@ static int imx8ulp_clk_pcc3_init(struct platform_device *pdev)
if (ret)
return ret;
- imx_register_uart_clocks(1);
+ imx_register_uart_clocks();
/* register the pcc3 reset controller */
return imx8ulp_pcc_reset_init(pdev, base, pcc3_resets, ARRAY_SIZE(pcc3_resets));
diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c
index f5c9fa40491c..c5f358a75f30 100644
--- a/drivers/clk/imx/clk-imx93.c
+++ b/drivers/clk/imx/clk-imx93.c
@@ -15,6 +15,11 @@
#include "clk.h"
+#define IMX93_CLK_END 208
+
+#define PLAT_IMX93 BIT(0)
+#define PLAT_IMX91 BIT(1)
+
enum clk_sel {
LOW_SPEED_IO_SEL,
NON_IO_SEL,
@@ -28,6 +33,14 @@ enum clk_sel {
MAX_SEL
};
+static u32 share_count_sai1;
+static u32 share_count_sai2;
+static u32 share_count_sai3;
+static u32 share_count_mub;
+static u32 share_count_pdm;
+static u32 share_count_spdif;
+
+static const char * const a55_core_sels[] = {"a55_alt", "arm_pll"};
static const char *parent_names[MAX_SEL][4] = {
{"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "video_pll"},
{"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "sys_pll_pfd2_div2"},
@@ -46,26 +59,23 @@ static const struct imx93_clk_root {
u32 off;
enum clk_sel sel;
unsigned long flags;
+ unsigned long plat;
} root_array[] = {
/* a55/m33/bus critical clk for system run */
{ IMX93_CLK_A55_PERIPH, "a55_periph_root", 0x0000, FAST_SEL, CLK_IS_CRITICAL },
{ IMX93_CLK_A55_MTR_BUS, "a55_mtr_bus_root", 0x0080, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
- { IMX93_CLK_A55, "a55_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL },
+ { IMX93_CLK_A55, "a55_alt_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL },
{ IMX93_CLK_M33, "m33_root", 0x0180, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
{ IMX93_CLK_BUS_WAKEUP, "bus_wakeup_root", 0x0280, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
{ IMX93_CLK_BUS_AON, "bus_aon_root", 0x0300, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL },
{ IMX93_CLK_WAKEUP_AXI, "wakeup_axi_root", 0x0380, FAST_SEL, CLK_IS_CRITICAL },
{ IMX93_CLK_SWO_TRACE, "swo_trace_root", 0x0400, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_M33_SYSTICK, "m33_systick_root", 0x0480, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_FLEXIO1, "flexio1_root", 0x0500, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_FLEXIO2, "flexio2_root", 0x0580, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_LPIT1, "lpit1_root", 0x0600, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_LPIT2, "lpit2_root", 0x0680, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_M33_SYSTICK, "m33_systick_root", 0x0480, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_FLEXIO1, "flexio1_root", 0x0500, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_FLEXIO2, "flexio2_root", 0x0580, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
{ IMX93_CLK_LPTMR1, "lptmr1_root", 0x0700, LOW_SPEED_IO_SEL, },
{ IMX93_CLK_LPTMR2, "lptmr2_root", 0x0780, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_TPM1, "tpm1_root", 0x0800, TPM_SEL, },
{ IMX93_CLK_TPM2, "tpm2_root", 0x0880, TPM_SEL, },
- { IMX93_CLK_TPM3, "tpm3_root", 0x0900, TPM_SEL, },
{ IMX93_CLK_TPM4, "tpm4_root", 0x0980, TPM_SEL, },
{ IMX93_CLK_TPM5, "tpm5_root", 0x0a00, TPM_SEL, },
{ IMX93_CLK_TPM6, "tpm6_root", 0x0a80, TPM_SEL, },
@@ -108,19 +118,24 @@ static const struct imx93_clk_root {
{ IMX93_CLK_CCM_CKO2, "ccm_cko2_root", 0x1d00, CKO2_SEL, },
{ IMX93_CLK_CCM_CKO3, "ccm_cko3_root", 0x1d80, CKO1_SEL, },
{ IMX93_CLK_CCM_CKO4, "ccm_cko4_root", 0x1e00, CKO2_SEL, },
- { IMX93_CLK_HSIO, "hsio_root", 0x1e80, LOW_SPEED_IO_SEL, },
+ /*
+ * Critical because clk is used for handshake between HSIOMIX and NICMIX when
+ * NICMIX power down/on during system suspend/resume
+ */
+ { IMX93_CLK_HSIO, "hsio_root", 0x1e80, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL},
{ IMX93_CLK_HSIO_USB_TEST_60M, "hsio_usb_test_60m_root", 0x1f00, LOW_SPEED_IO_SEL, },
{ IMX93_CLK_HSIO_ACSCAN_80M, "hsio_acscan_80m_root", 0x1f80, LOW_SPEED_IO_SEL, },
{ IMX93_CLK_HSIO_ACSCAN_480M, "hsio_acscan_480m_root", 0x2000, MISC_SEL, },
- { IMX93_CLK_ML_APB, "ml_apb_root", 0x2180, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_ML, "ml_root", 0x2200, FAST_SEL, },
+ { IMX93_CLK_NIC_AXI, "nic_axi_root", 0x2080, FAST_SEL, CLK_IS_CRITICAL, },
+ { IMX93_CLK_ML_APB, "ml_apb_root", 0x2180, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_ML, "ml_root", 0x2200, FAST_SEL, 0, PLAT_IMX93, },
{ IMX93_CLK_MEDIA_AXI, "media_axi_root", 0x2280, FAST_SEL, },
{ IMX93_CLK_MEDIA_APB, "media_apb_root", 0x2300, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_MEDIA_LDB, "media_ldb_root", 0x2380, VIDEO_SEL, },
+ { IMX93_CLK_MEDIA_LDB, "media_ldb_root", 0x2380, VIDEO_SEL, 0, PLAT_IMX93, },
{ IMX93_CLK_MEDIA_DISP_PIX, "media_disp_pix_root", 0x2400, VIDEO_SEL, },
{ IMX93_CLK_CAM_PIX, "cam_pix_root", 0x2480, VIDEO_SEL, },
- { IMX93_CLK_MIPI_TEST_BYTE, "mipi_test_byte_root", 0x2500, VIDEO_SEL, },
- { IMX93_CLK_MIPI_PHY_CFG, "mipi_phy_cfg_root", 0x2580, VIDEO_SEL, },
+ { IMX93_CLK_MIPI_TEST_BYTE, "mipi_test_byte_root", 0x2500, VIDEO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_MIPI_PHY_CFG, "mipi_phy_cfg_root", 0x2580, VIDEO_SEL, 0, PLAT_IMX93, },
{ IMX93_CLK_ADC, "adc_root", 0x2700, LOW_SPEED_IO_SEL, },
{ IMX93_CLK_PDM, "pdm_root", 0x2780, AUDIO_SEL, },
{ IMX93_CLK_TSTMR1, "tstmr1_root", 0x2800, LOW_SPEED_IO_SEL, },
@@ -129,13 +144,16 @@ static const struct imx93_clk_root {
{ IMX93_CLK_MQS2, "mqs2_root", 0x2980, AUDIO_SEL, },
{ IMX93_CLK_AUDIO_XCVR, "audio_xcvr_root", 0x2a00, NON_IO_SEL, },
{ IMX93_CLK_SPDIF, "spdif_root", 0x2a80, AUDIO_SEL, },
- { IMX93_CLK_ENET, "enet_root", 0x2b00, NON_IO_SEL, },
- { IMX93_CLK_ENET_TIMER1, "enet_timer1_root", 0x2b80, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_ENET_TIMER2, "enet_timer2_root", 0x2c00, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_ENET_REF, "enet_ref_root", 0x2c80, NON_IO_SEL, },
- { IMX93_CLK_ENET_REF_PHY, "enet_ref_phy_root", 0x2d00, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_I3C1_SLOW, "i3c1_slow_root", 0x2d80, LOW_SPEED_IO_SEL, },
- { IMX93_CLK_I3C2_SLOW, "i3c2_slow_root", 0x2e00, LOW_SPEED_IO_SEL, },
+ { IMX93_CLK_ENET, "enet_root", 0x2b00, NON_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_ENET_TIMER1, "enet_timer1_root", 0x2b80, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_ENET_TIMER2, "enet_timer2_root", 0x2c00, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_ENET_REF, "enet_ref_root", 0x2c80, NON_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_ENET_REF_PHY, "enet_ref_phy_root", 0x2d00, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX91_CLK_ENET1_QOS_TSN, "enet1_qos_tsn_root", 0x2b00, NON_IO_SEL, 0, PLAT_IMX91, },
+ { IMX91_CLK_ENET_TIMER, "enet_timer_root", 0x2b80, LOW_SPEED_IO_SEL, 0, PLAT_IMX91, },
+ { IMX91_CLK_ENET2_REGULAR, "enet2_regular_root", 0x2c80, NON_IO_SEL, 0, PLAT_IMX91, },
+ { IMX93_CLK_I3C1_SLOW, "i3c1_slow_root", 0x2d80, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
+ { IMX93_CLK_I3C2_SLOW, "i3c2_slow_root", 0x2e00, LOW_SPEED_IO_SEL, 0, PLAT_IMX93, },
{ IMX93_CLK_USB_PHY_BURUNIN, "usb_phy_root", 0x2e80, LOW_SPEED_IO_SEL, },
{ IMX93_CLK_PAL_CAME_SCAN, "pal_came_scan_root", 0x2f00, MISC_SEL, }
};
@@ -146,8 +164,10 @@ static const struct imx93_clk_ccgr {
char *parent_name;
u32 off;
unsigned long flags;
+ u32 *shared_count;
+ unsigned long plat;
} ccgr_array[] = {
- { IMX93_CLK_A55_GATE, "a55", "a55_root", 0x8000, },
+ { IMX93_CLK_A55_GATE, "a55_alt", "a55_alt_root", 0x8000, },
/* M33 critical clk for system run */
{ IMX93_CLK_CM33_GATE, "cm33", "m33_root", 0x8040, CLK_IS_CRITICAL },
{ IMX93_CLK_ADC1_GATE, "adc1", "adc_root", 0x82c0, },
@@ -158,24 +178,26 @@ static const struct imx93_clk_ccgr {
{ IMX93_CLK_WDOG5_GATE, "wdog5", "osc_24m", 0x8400, },
{ IMX93_CLK_SEMA1_GATE, "sema1", "bus_aon_root", 0x8440, },
{ IMX93_CLK_SEMA2_GATE, "sema2", "bus_wakeup_root", 0x8480, },
- { IMX93_CLK_MU_A_GATE, "mu_a", "bus_aon_root", 0x84c0, },
- { IMX93_CLK_MU_B_GATE, "mu_b", "bus_aon_root", 0x8500, },
+ { IMX93_CLK_MU1_A_GATE, "mu1_a", "bus_aon_root", 0x84c0, CLK_IGNORE_UNUSED, NULL, PLAT_IMX93 },
+ { IMX93_CLK_MU2_A_GATE, "mu2_a", "bus_wakeup_root", 0x84c0, CLK_IGNORE_UNUSED, NULL, PLAT_IMX93 },
+ { IMX93_CLK_MU1_B_GATE, "mu1_b", "bus_aon_root", 0x8500, 0, &share_count_mub, PLAT_IMX93 },
+ { IMX93_CLK_MU2_B_GATE, "mu2_b", "bus_wakeup_root", 0x8500, 0, &share_count_mub, PLAT_IMX93 },
{ IMX93_CLK_EDMA1_GATE, "edma1", "m33_root", 0x8540, },
{ IMX93_CLK_EDMA2_GATE, "edma2", "wakeup_axi_root", 0x8580, },
- { IMX93_CLK_FLEXSPI1_GATE, "flexspi", "flexspi_root", 0x8640, },
+ { IMX93_CLK_FLEXSPI1_GATE, "flexspi1", "flexspi1_root", 0x8640, },
{ IMX93_CLK_GPIO1_GATE, "gpio1", "m33_root", 0x8880, },
{ IMX93_CLK_GPIO2_GATE, "gpio2", "bus_wakeup_root", 0x88c0, },
{ IMX93_CLK_GPIO3_GATE, "gpio3", "bus_wakeup_root", 0x8900, },
{ IMX93_CLK_GPIO4_GATE, "gpio4", "bus_wakeup_root", 0x8940, },
- { IMX93_CLK_FLEXIO1_GATE, "flexio1", "flexio1_root", 0x8980, },
- { IMX93_CLK_FLEXIO2_GATE, "flexio2", "flexio2_root", 0x89c0, },
- { IMX93_CLK_LPIT1_GATE, "lpit1", "lpit1_root", 0x8a00, },
- { IMX93_CLK_LPIT2_GATE, "lpit2", "lpit2_root", 0x8a40, },
+ { IMX93_CLK_FLEXIO1_GATE, "flexio1", "flexio1_root", 0x8980, 0, NULL, PLAT_IMX93},
+ { IMX93_CLK_FLEXIO2_GATE, "flexio2", "flexio2_root", 0x89c0, 0, NULL, PLAT_IMX93},
+ { IMX93_CLK_LPIT1_GATE, "lpit1", "bus_aon_root", 0x8a00, },
+ { IMX93_CLK_LPIT2_GATE, "lpit2", "bus_wakeup_root", 0x8a40, },
{ IMX93_CLK_LPTMR1_GATE, "lptmr1", "lptmr1_root", 0x8a80, },
{ IMX93_CLK_LPTMR2_GATE, "lptmr2", "lptmr2_root", 0x8ac0, },
- { IMX93_CLK_TPM1_GATE, "tpm1", "tpm1_root", 0x8b00, },
+ { IMX93_CLK_TPM1_GATE, "tpm1", "bus_aon_root", 0x8b00, },
{ IMX93_CLK_TPM2_GATE, "tpm2", "tpm2_root", 0x8b40, },
- { IMX93_CLK_TPM3_GATE, "tpm3", "tpm3_root", 0x8b80, },
+ { IMX93_CLK_TPM3_GATE, "tpm3", "bus_wakeup_root", 0x8b80, },
{ IMX93_CLK_TPM4_GATE, "tpm4", "tpm4_root", 0x8bc0, },
{ IMX93_CLK_TPM5_GATE, "tpm5", "tpm5_root", 0x8c00, },
{ IMX93_CLK_TPM6_GATE, "tpm6", "tpm6_root", 0x8c40, },
@@ -210,28 +232,36 @@ static const struct imx93_clk_ccgr {
{ IMX93_CLK_USDHC1_GATE, "usdhc1", "usdhc1_root", 0x9380, },
{ IMX93_CLK_USDHC2_GATE, "usdhc2", "usdhc2_root", 0x93c0, },
{ IMX93_CLK_USDHC3_GATE, "usdhc3", "usdhc3_root", 0x9400, },
- { IMX93_CLK_SAI1_GATE, "sai1", "sai1_root", 0x9440, },
- { IMX93_CLK_SAI2_GATE, "sai2", "sai2_root", 0x9480, },
- { IMX93_CLK_SAI3_GATE, "sai3", "sai3_root", 0x94c0, },
+ { IMX93_CLK_SAI1_GATE, "sai1", "sai1_root", 0x9440, 0, &share_count_sai1},
+ { IMX93_CLK_SAI1_IPG, "sai1_ipg_clk", "bus_aon_root", 0x9440, 0, &share_count_sai1},
+ { IMX93_CLK_SAI2_GATE, "sai2", "sai2_root", 0x9480, 0, &share_count_sai2},
+ { IMX93_CLK_SAI2_IPG, "sai2_ipg_clk", "bus_wakeup_root", 0x9480, 0, &share_count_sai2},
+ { IMX93_CLK_SAI3_GATE, "sai3", "sai3_root", 0x94c0, 0, &share_count_sai3},
+ { IMX93_CLK_SAI3_IPG, "sai3_ipg_clk", "bus_wakeup_root", 0x94c0, 0, &share_count_sai3},
{ IMX93_CLK_MIPI_CSI_GATE, "mipi_csi", "media_apb_root", 0x9580, },
- { IMX93_CLK_MIPI_DSI_GATE, "mipi_dsi", "media_apb_root", 0x95c0, },
- { IMX93_CLK_LVDS_GATE, "lvds", "media_ldb_root", 0x9600, },
+ { IMX93_CLK_MIPI_DSI_GATE, "mipi_dsi", "media_apb_root", 0x95c0, 0, NULL, PLAT_IMX93 },
+ { IMX93_CLK_LVDS_GATE, "lvds", "media_ldb_root", 0x9600, 0, NULL, PLAT_IMX93 },
{ IMX93_CLK_LCDIF_GATE, "lcdif", "media_apb_root", 0x9640, },
- { IMX93_CLK_PXP_GATE, "pxp", "media_apb_root", 0x9680, },
+ { IMX93_CLK_PXP_GATE, "pxp", "media_apb_root", 0x9680, 0, NULL, PLAT_IMX93 },
{ IMX93_CLK_ISI_GATE, "isi", "media_apb_root", 0x96c0, },
{ IMX93_CLK_NIC_MEDIA_GATE, "nic_media", "media_axi_root", 0x9700, },
{ IMX93_CLK_USB_CONTROLLER_GATE, "usb_controller", "hsio_root", 0x9a00, },
{ IMX93_CLK_USB_TEST_60M_GATE, "usb_test_60m", "hsio_usb_test_60m_root", 0x9a40, },
{ IMX93_CLK_HSIO_TROUT_24M_GATE, "hsio_trout_24m", "osc_24m", 0x9a80, },
- { IMX93_CLK_PDM_GATE, "pdm", "pdm_root", 0x9ac0, },
+ { IMX93_CLK_PDM_GATE, "pdm", "pdm_root", 0x9ac0, 0, &share_count_pdm},
+ { IMX93_CLK_PDM_IPG, "pdm_ipg_clk", "bus_aon_root", 0x9ac0, 0, &share_count_pdm},
{ IMX93_CLK_MQS1_GATE, "mqs1", "sai1_root", 0x9b00, },
{ IMX93_CLK_MQS2_GATE, "mqs2", "sai3_root", 0x9b40, },
{ IMX93_CLK_AUD_XCVR_GATE, "aud_xcvr", "audio_xcvr_root", 0x9b80, },
- { IMX93_CLK_SPDIF_GATE, "spdif", "spdif_root", 0x9c00, },
+ { IMX93_CLK_SPDIF_IPG, "spdif_ipg_clk", "bus_wakeup_root", 0x9c00, 0, &share_count_spdif},
+ { IMX93_CLK_SPDIF_GATE, "spdif", "spdif_root", 0x9c00, 0, &share_count_spdif},
{ IMX93_CLK_HSIO_32K_GATE, "hsio_32k", "osc_32k", 0x9dc0, },
- { IMX93_CLK_ENET1_GATE, "enet1", "enet_root", 0x9e00, },
- { IMX93_CLK_ENET_QOS_GATE, "enet_qos", "wakeup_axi_root", 0x9e40, },
- { IMX93_CLK_SYS_CNT_GATE, "sys_cnt", "osc_24m", 0x9e80, },
+ { IMX93_CLK_ENET1_GATE, "enet1", "wakeup_axi_root", 0x9e00, 0, NULL, PLAT_IMX93, },
+ { IMX93_CLK_ENET_QOS_GATE, "enet_qos", "wakeup_axi_root", 0x9e40, 0, NULL, PLAT_IMX93, },
+ { IMX91_CLK_ENET2_REGULAR_GATE, "enet2_regular", "wakeup_axi_root", 0x9e00, 0, NULL, PLAT_IMX91, },
+ { IMX91_CLK_ENET1_QOS_TSN_GATE, "enet1_qos_tsn", "wakeup_axi_root", 0x9e40, 0, NULL, PLAT_IMX91, },
+ /* Critical because clk accessed during CPU idle */
+ { IMX93_CLK_SYS_CNT_GATE, "sys_cnt", "osc_24m", 0x9e80, CLK_IS_CRITICAL},
{ IMX93_CLK_TSTMR1_GATE, "tstmr1", "bus_aon_root", 0x9ec0, },
{ IMX93_CLK_TSTMR2_GATE, "tstmr2", "bus_wakeup_root", 0x9f00, },
{ IMX93_CLK_TMC_GATE, "tmc", "osc_24m", 0x9f40, },
@@ -247,10 +277,11 @@ static int imx93_clocks_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
const struct imx93_clk_root *root;
const struct imx93_clk_ccgr *ccgr;
- void __iomem *base = NULL;
+ void __iomem *base, *anatop_base;
int i, ret;
+ const unsigned long plat = (unsigned long)device_get_match_data(&pdev->dev);
- clk_hw_data = kzalloc(struct_size(clk_hw_data, hws,
+ clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws,
IMX93_CLK_END), GFP_KERNEL);
if (!clk_hw_data)
return -ENOMEM;
@@ -259,9 +290,9 @@ static int imx93_clocks_probe(struct platform_device *pdev)
clks = clk_hw_data->hws;
clks[IMX93_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0);
- clks[IMX93_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m");
- clks[IMX93_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k");
- clks[IMX93_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1");
+ clks[IMX93_CLK_24M] = imx_get_clk_hw_by_name(np, "osc_24m");
+ clks[IMX93_CLK_32K] = imx_get_clk_hw_by_name(np, "osc_32k");
+ clks[IMX93_CLK_EXT1] = imx_get_clk_hw_by_name(np, "clk_ext1");
clks[IMX93_CLK_SYS_PLL_PFD0] = imx_clk_hw_fixed("sys_pll_pfd0", 1000000000);
clks[IMX93_CLK_SYS_PLL_PFD0_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd0_div2",
@@ -274,37 +305,54 @@ static int imx93_clocks_probe(struct platform_device *pdev)
"sys_pll_pfd2", 1, 2);
np = of_find_compatible_node(NULL, NULL, "fsl,imx93-anatop");
- base = of_iomap(np, 0);
+ anatop_base = devm_of_iomap(dev, np, 0, NULL);
of_node_put(np);
- if (WARN_ON(!base))
- return -ENOMEM;
+ if (WARN_ON(IS_ERR(anatop_base))) {
+ ret = PTR_ERR(anatop_base);
+ goto unregister_hws;
+ }
- clks[IMX93_CLK_AUDIO_PLL] = imx_clk_fracn_gppll("audio_pll", "osc_24m", base + 0x1200,
+ clks[IMX93_CLK_ARM_PLL] = imx_clk_fracn_gppll_integer("arm_pll", "osc_24m",
+ anatop_base + 0x1000,
+ &imx_fracn_gppll_integer);
+ clks[IMX93_CLK_AUDIO_PLL] = imx_clk_fracn_gppll("audio_pll", "osc_24m", anatop_base + 0x1200,
&imx_fracn_gppll);
- clks[IMX93_CLK_VIDEO_PLL] = imx_clk_fracn_gppll("video_pll", "osc_24m", base + 0x1400,
+ clks[IMX93_CLK_VIDEO_PLL] = imx_clk_fracn_gppll("video_pll", "osc_24m", anatop_base + 0x1400,
&imx_fracn_gppll);
np = dev->of_node;
base = devm_platform_ioremap_resource(pdev, 0);
- if (WARN_ON(IS_ERR(base)))
- return PTR_ERR(base);
+ if (WARN_ON(IS_ERR(base))) {
+ ret = PTR_ERR(base);
+ goto unregister_hws;
+ }
for (i = 0; i < ARRAY_SIZE(root_array); i++) {
root = &root_array[i];
- clks[root->clk] = imx93_clk_composite_flags(root->name,
- parent_names[root->sel],
- 4, base + root->off,
- root->flags);
+ if (!root->plat || root->plat & plat)
+ clks[root->clk] = imx93_clk_composite_flags(root->name,
+ parent_names[root->sel],
+ 4, base + root->off, 3,
+ root->flags);
}
for (i = 0; i < ARRAY_SIZE(ccgr_array); i++) {
ccgr = &ccgr_array[i];
- clks[ccgr->clk] = imx_clk_hw_gate4_flags(ccgr->name,
- ccgr->parent_name,
- base + ccgr->off, 0,
- ccgr->flags);
+ if (!ccgr->plat || ccgr->plat & plat)
+ clks[ccgr->clk] = imx93_clk_gate(NULL,
+ ccgr->name, ccgr->parent_name,
+ ccgr->flags, base + ccgr->off, 0, 1, 1, 3,
+ ccgr->shared_count);
}
+ clks[IMX93_CLK_A55_SEL] = imx_clk_hw_mux2("a55_sel", base + 0x4820, 0, 1, a55_core_sels,
+ ARRAY_SIZE(a55_core_sels));
+ clks[IMX93_CLK_A55_CORE] = imx_clk_hw_cpu("a55_core", "a55_sel",
+ clks[IMX93_CLK_A55_SEL]->clk,
+ clks[IMX93_CLK_A55_SEL]->clk,
+ clks[IMX93_CLK_ARM_PLL]->clk,
+ clks[IMX93_CLK_A55_GATE]->clk);
+
imx_check_clk_hws(clks, IMX93_CLK_END);
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
@@ -313,6 +361,8 @@ static int imx93_clocks_probe(struct platform_device *pdev)
goto unregister_hws;
}
+ imx_register_uart_clocks();
+
return 0;
unregister_hws:
@@ -322,7 +372,8 @@ unregister_hws:
}
static const struct of_device_id imx93_clk_of_match[] = {
- { .compatible = "fsl,imx93-ccm" },
+ { .compatible = "fsl,imx93-ccm", .data = (void *)PLAT_IMX93 },
+ { .compatible = "fsl,imx91-ccm", .data = (void *)PLAT_IMX91 },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx93_clk_of_match);
@@ -332,10 +383,12 @@ static struct platform_driver imx93_clk_driver = {
.driver = {
.name = "imx93-ccm",
.suppress_bind_attrs = true,
- .of_match_table = of_match_ptr(imx93_clk_of_match),
+ .of_match_table = imx93_clk_of_match,
},
};
module_platform_driver(imx93_clk_driver);
+module_param(mcore_booted, bool, 0444);
+MODULE_PARM_DESC(mcore_booted, "See Cortex-M core is booted or not");
MODULE_DESCRIPTION("NXP i.MX93 clock driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/imx/clk-imx95-blk-ctl.c b/drivers/clk/imx/clk-imx95-blk-ctl.c
new file mode 100644
index 000000000000..56bed4471995
--- /dev/null
+++ b/drivers/clk/imx/clk-imx95-blk-ctl.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024-2025 NXP
+ */
+
+#include <dt-bindings/clock/nxp,imx94-clock.h>
+#include <dt-bindings/clock/nxp,imx95-clock.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/pm_runtime.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+enum {
+ CLK_GATE,
+ CLK_DIVIDER,
+ CLK_MUX,
+};
+
+struct imx95_blk_ctl {
+ struct device *dev;
+ spinlock_t lock;
+ struct clk *clk_apb;
+
+ void __iomem *base;
+ /* clock gate register */
+ u32 clk_reg_restore;
+ const struct imx95_blk_ctl_dev_data *pdata;
+};
+
+struct imx95_blk_ctl_clk_dev_data {
+ const char *name;
+ const char * const *parent_names;
+ u32 num_parents;
+ u32 reg;
+ u32 bit_idx;
+ u32 bit_width;
+ u32 clk_type;
+ u32 flags;
+ u32 flags2;
+ u32 type;
+};
+
+struct imx95_blk_ctl_dev_data {
+ const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;
+ u32 num_clks;
+ bool rpm_enabled;
+ u32 clk_reg_offset;
+};
+
+static const struct imx95_blk_ctl_clk_dev_data vpublk_clk_dev_data[] = {
+ [IMX95_CLK_VPUBLK_WAVE] = {
+ .name = "vpublk_wave_vpu",
+ .parent_names = (const char *[]){ "vpu", },
+ .num_parents = 1,
+ .reg = 8,
+ .bit_idx = 0,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_VPUBLK_JPEG_ENC] = {
+ .name = "vpublk_jpeg_enc",
+ .parent_names = (const char *[]){ "vpujpeg", },
+ .num_parents = 1,
+ .reg = 8,
+ .bit_idx = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_VPUBLK_JPEG_DEC] = {
+ .name = "vpublk_jpeg_dec",
+ .parent_names = (const char *[]){ "vpujpeg", },
+ .num_parents = 1,
+ .reg = 8,
+ .bit_idx = 2,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ }
+};
+
+static const struct imx95_blk_ctl_dev_data vpublk_dev_data = {
+ .num_clks = ARRAY_SIZE(vpublk_clk_dev_data),
+ .clk_dev_data = vpublk_clk_dev_data,
+ .rpm_enabled = true,
+ .clk_reg_offset = 8,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data camblk_clk_dev_data[] = {
+ [IMX95_CLK_CAMBLK_CSI2_FOR0] = {
+ .name = "camblk_csi2_for0",
+ .parent_names = (const char *[]){ "camisi", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 0,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_CAMBLK_CSI2_FOR1] = {
+ .name = "camblk_csi2_for1",
+ .parent_names = (const char *[]){ "camisi", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_CAMBLK_ISP_AXI] = {
+ .name = "camblk_isp_axi",
+ .parent_names = (const char *[]){ "camaxi", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 4,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_CAMBLK_ISP_PIXEL] = {
+ .name = "camblk_isp_pixel",
+ .parent_names = (const char *[]){ "camisi", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 5,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_CAMBLK_ISP] = {
+ .name = "camblk_isp",
+ .parent_names = (const char *[]){ "camisi", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 6,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ }
+};
+
+static const struct imx95_blk_ctl_dev_data camblk_dev_data = {
+ .num_clks = ARRAY_SIZE(camblk_clk_dev_data),
+ .clk_dev_data = camblk_clk_dev_data,
+ .clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data imx95_lvds_clk_dev_data[] = {
+ [IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = {
+ .name = "ldb_phy_div",
+ .parent_names = (const char *[]){ "ldbpll", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 0,
+ .bit_width = 1,
+ .type = CLK_DIVIDER,
+ .flags2 = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {
+ .name = "lvds_ch0_gate",
+ .parent_names = (const char *[]){ "ldb_phy_div", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 1,
+ .bit_width = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = {
+ .name = "lvds_ch1_gate",
+ .parent_names = (const char *[]){ "ldb_phy_div", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 2,
+ .bit_width = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_DISPMIX_PIX_DI0_GATE] = {
+ .name = "lvds_di0_gate",
+ .parent_names = (const char *[]){ "ldb_pll_div7", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 3,
+ .bit_width = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+ [IMX95_CLK_DISPMIX_PIX_DI1_GATE] = {
+ .name = "lvds_di1_gate",
+ .parent_names = (const char *[]){ "ldb_pll_div7", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 4,
+ .bit_width = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+};
+
+static const struct imx95_blk_ctl_dev_data imx95_lvds_csr_dev_data = {
+ .num_clks = ARRAY_SIZE(imx95_lvds_clk_dev_data),
+ .clk_dev_data = imx95_lvds_clk_dev_data,
+ .clk_reg_offset = 0,
+};
+
+static const char * const imx95_disp_engine_parents[] = {
+ "videopll1", "dsi_pll", "ldb_pll_div7"
+};
+
+static const struct imx95_blk_ctl_clk_dev_data imx95_dispmix_csr_clk_dev_data[] = {
+ [IMX95_CLK_DISPMIX_ENG0_SEL] = {
+ .name = "disp_engine0_sel",
+ .parent_names = imx95_disp_engine_parents,
+ .num_parents = ARRAY_SIZE(imx95_disp_engine_parents),
+ .reg = 0,
+ .bit_idx = 0,
+ .bit_width = 2,
+ .type = CLK_MUX,
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+ },
+ [IMX95_CLK_DISPMIX_ENG1_SEL] = {
+ .name = "disp_engine1_sel",
+ .parent_names = imx95_disp_engine_parents,
+ .num_parents = ARRAY_SIZE(imx95_disp_engine_parents),
+ .reg = 0,
+ .bit_idx = 2,
+ .bit_width = 2,
+ .type = CLK_MUX,
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+ }
+};
+
+static const struct imx95_blk_ctl_dev_data imx95_dispmix_csr_dev_data = {
+ .num_clks = ARRAY_SIZE(imx95_dispmix_csr_clk_dev_data),
+ .clk_dev_data = imx95_dispmix_csr_clk_dev_data,
+ .clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data netxmix_clk_dev_data[] = {
+ [IMX95_CLK_NETCMIX_ENETC0_RMII] = {
+ .name = "enetc0_rmii_sel",
+ .parent_names = (const char *[]){"ext_enetref", "enetref"},
+ .num_parents = 2,
+ .reg = 4,
+ .bit_idx = 5,
+ .bit_width = 1,
+ .type = CLK_MUX,
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+ },
+ [IMX95_CLK_NETCMIX_ENETC1_RMII] = {
+ .name = "enetc1_rmii_sel",
+ .parent_names = (const char *[]){"ext_enetref", "enetref"},
+ .num_parents = 2,
+ .reg = 4,
+ .bit_idx = 10,
+ .bit_width = 1,
+ .type = CLK_MUX,
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct imx95_blk_ctl_dev_data netcmix_dev_data = {
+ .num_clks = ARRAY_SIZE(netxmix_clk_dev_data),
+ .clk_dev_data = netxmix_clk_dev_data,
+ .clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data hsio_blk_ctl_clk_dev_data[] = {
+ [0] = {
+ .name = "hsio_blk_ctl_clk",
+ .parent_names = (const char *[]){ "hsio_pll", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 6,
+ .bit_width = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ }
+};
+
+static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = {
+ .num_clks = 1,
+ .clk_dev_data = hsio_blk_ctl_clk_dev_data,
+ .clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data imx94_lvds_clk_dev_data[] = {
+ [IMX94_CLK_DISPMIX_LVDS_CLK_GATE] = {
+ .name = "lvds_clk_gate",
+ .parent_names = (const char *[]){ "ldbpll", },
+ .num_parents = 1,
+ .reg = 0,
+ .bit_idx = 1,
+ .bit_width = 1,
+ .type = CLK_GATE,
+ .flags = CLK_SET_RATE_PARENT,
+ .flags2 = CLK_GATE_SET_TO_DISABLE,
+ },
+};
+
+static const struct imx95_blk_ctl_dev_data imx94_lvds_csr_dev_data = {
+ .num_clks = ARRAY_SIZE(imx94_lvds_clk_dev_data),
+ .clk_dev_data = imx94_lvds_clk_dev_data,
+ .clk_reg_offset = 0,
+ .rpm_enabled = true,
+};
+
+static const char * const imx94_disp_engine_parents[] = {
+ "disppix", "ldb_pll_div7"
+};
+
+static const struct imx95_blk_ctl_clk_dev_data imx94_dispmix_csr_clk_dev_data[] = {
+ [IMX94_CLK_DISPMIX_CLK_SEL] = {
+ .name = "disp_clk_sel",
+ .parent_names = imx94_disp_engine_parents,
+ .num_parents = ARRAY_SIZE(imx94_disp_engine_parents),
+ .reg = 0,
+ .bit_idx = 1,
+ .bit_width = 1,
+ .type = CLK_MUX,
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct imx95_blk_ctl_dev_data imx94_dispmix_csr_dev_data = {
+ .num_clks = ARRAY_SIZE(imx94_dispmix_csr_clk_dev_data),
+ .clk_dev_data = imx94_dispmix_csr_clk_dev_data,
+ .clk_reg_offset = 0,
+ .rpm_enabled = true,
+};
+
+static int imx95_bc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx95_blk_ctl *bc;
+ struct clk_hw_onecell_data *clk_hw_data;
+ struct clk_hw **hws;
+ void __iomem *base;
+ int i, ret;
+
+ bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
+ if (!bc)
+ return -ENOMEM;
+ bc->dev = dev;
+ dev_set_drvdata(&pdev->dev, bc);
+
+ spin_lock_init(&bc->lock);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ bc->base = base;
+ bc->clk_apb = devm_clk_get(dev, NULL);
+ if (IS_ERR(bc->clk_apb))
+ return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n");
+
+ ret = clk_prepare_enable(bc->clk_apb);
+ if (ret) {
+ dev_err(dev, "failed to enable apb clock: %d\n", ret);
+ return ret;
+ }
+
+ bc->pdata = of_device_get_match_data(dev);
+ if (!bc->pdata)
+ return devm_of_platform_populate(dev);
+
+ clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc->pdata->num_clks),
+ GFP_KERNEL);
+ if (!clk_hw_data)
+ return -ENOMEM;
+
+ if (bc->pdata->rpm_enabled) {
+ devm_pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume_and_get(&pdev->dev);
+ }
+
+ clk_hw_data->num = bc->pdata->num_clks;
+ hws = clk_hw_data->hws;
+
+ for (i = 0; i < bc->pdata->num_clks; i++) {
+ const struct imx95_blk_ctl_clk_dev_data *data = &bc->pdata->clk_dev_data[i];
+ void __iomem *reg = base + data->reg;
+
+ if (data->type == CLK_MUX) {
+ hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names,
+ data->num_parents, data->flags, reg,
+ data->bit_idx, data->bit_width,
+ data->flags2, &bc->lock);
+ } else if (data->type == CLK_DIVIDER) {
+ hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0],
+ data->flags, reg, data->bit_idx,
+ data->bit_width, data->flags2, &bc->lock);
+ } else {
+ hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0],
+ data->flags, reg, data->bit_idx,
+ data->flags2, &bc->lock);
+ }
+ if (IS_ERR(hws[i])) {
+ ret = PTR_ERR(hws[i]);
+ dev_err(dev, "failed to register: %s:%d\n", data->name, ret);
+ goto cleanup;
+ }
+ }
+
+ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data);
+ if (ret)
+ goto cleanup;
+
+ ret = devm_of_platform_populate(dev);
+ if (ret) {
+ of_clk_del_provider(dev->of_node);
+ goto cleanup;
+ }
+
+ if (pm_runtime_enabled(bc->dev)) {
+ pm_runtime_put_sync(&pdev->dev);
+ clk_disable_unprepare(bc->clk_apb);
+ }
+
+ return 0;
+
+cleanup:
+ for (i = 0; i < bc->pdata->num_clks; i++) {
+ if (IS_ERR_OR_NULL(hws[i]))
+ continue;
+ clk_hw_unregister(hws[i]);
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int imx95_bc_runtime_suspend(struct device *dev)
+{
+ struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+
+ bc->clk_reg_restore = readl(bc->base + bc->pdata->clk_reg_offset);
+ clk_disable_unprepare(bc->clk_apb);
+
+ return 0;
+}
+
+static int imx95_bc_runtime_resume(struct device *dev)
+{
+ struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(bc->clk_apb);
+ if (ret)
+ return ret;
+
+ writel(bc->clk_reg_restore, bc->base + bc->pdata->clk_reg_offset);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int imx95_bc_suspend(struct device *dev)
+{
+ struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ bc->clk_reg_restore = readl(bc->base + bc->pdata->clk_reg_offset);
+ clk_disable_unprepare(bc->clk_apb);
+
+ return 0;
+}
+
+static int imx95_bc_resume(struct device *dev)
+{
+ struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+ int ret;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = clk_prepare_enable(bc->clk_apb);
+ if (ret)
+ return ret;
+
+ writel(bc->clk_reg_restore, bc->base + bc->pdata->clk_reg_offset);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops imx95_bc_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume)
+};
+
+static const struct of_device_id imx95_bc_of_match[] = {
+ { .compatible = "nxp,imx94-display-csr", .data = &imx94_dispmix_csr_dev_data },
+ { .compatible = "nxp,imx94-lvds-csr", .data = &imx94_lvds_csr_dev_data },
+ { .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data },
+ { .compatible = "nxp,imx95-display-master-csr", },
+ { .compatible = "nxp,imx95-display-csr", .data = &imx95_dispmix_csr_dev_data },
+ { .compatible = "nxp,imx95-lvds-csr", .data = &imx95_lvds_csr_dev_data },
+ { .compatible = "nxp,imx95-hsio-blk-ctl", .data = &hsio_blk_ctl_dev_data },
+ { .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data },
+ { .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data},
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
+
+static struct platform_driver imx95_bc_driver = {
+ .probe = imx95_bc_probe,
+ .driver = {
+ .name = "imx95-blk-ctl",
+ .of_match_table = imx95_bc_of_match,
+ .pm = &imx95_bc_pm_ops,
+ },
+};
+module_platform_driver(imx95_bc_driver);
+
+MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
+MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c
index 9539d35588ee..efd1ac9d8eeb 100644
--- a/drivers/clk/imx/clk-imxrt1050.c
+++ b/drivers/clk/imx/clk-imxrt1050.c
@@ -42,7 +42,7 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev)
struct device_node *anp;
int ret;
- clk_hw_data = kzalloc(struct_size(clk_hw_data, hws,
+ clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws,
IMXRT1050_CLK_END), GFP_KERNEL);
if (WARN_ON(!clk_hw_data))
return -ENOMEM;
@@ -50,13 +50,15 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev)
clk_hw_data->num = IMXRT1050_CLK_END;
hws = clk_hw_data->hws;
- hws[IMXRT1050_CLK_OSC] = imx_obtain_fixed_clk_hw(np, "osc");
+ hws[IMXRT1050_CLK_OSC] = imx_get_clk_hw_by_name(np, "osc");
anp = of_find_compatible_node(NULL, NULL, "fsl,imxrt-anatop");
- pll_base = of_iomap(anp, 0);
+ pll_base = devm_of_iomap(dev, anp, 0, NULL);
of_node_put(anp);
- if (WARN_ON(!pll_base))
- return -ENOMEM;
+ if (WARN_ON(IS_ERR(pll_base))) {
+ ret = PTR_ERR(pll_base);
+ goto unregister_hws;
+ }
/* Anatop clocks */
hws[IMXRT1050_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0UL);
@@ -104,8 +106,10 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev)
/* CCM clocks */
ccm_base = devm_platform_ioremap_resource(pdev, 0);
- if (WARN_ON(IS_ERR(ccm_base)))
- return PTR_ERR(ccm_base);
+ if (WARN_ON(IS_ERR(ccm_base))) {
+ ret = PTR_ERR(ccm_base);
+ goto unregister_hws;
+ }
hws[IMXRT1050_CLK_ARM_PODF] = imx_clk_hw_divider("arm_podf", "pll1_arm", ccm_base + 0x10, 0, 3);
hws[IMXRT1050_CLK_PRE_PERIPH_SEL] = imx_clk_hw_mux("pre_periph_sel", ccm_base + 0x18, 18, 2,
@@ -140,7 +144,8 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev)
hws[IMXRT1050_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", ccm_base + 0x80, 2);
hws[IMXRT1050_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", ccm_base + 0x80, 4);
hws[IMXRT1050_CLK_LPUART1] = imx_clk_hw_gate2("lpuart1", "lpuart_podf", ccm_base + 0x7c, 24);
- hws[IMXRT1050_CLK_LCDIF_APB] = imx_clk_hw_gate2("lcdif", "lcdif_podf", ccm_base + 0x74, 10);
+ hws[IMXRT1050_CLK_LCDIF_APB] = imx_clk_hw_gate2("lcdif", "lcdif_podf", ccm_base + 0x70, 28);
+ hws[IMXRT1050_CLK_LCDIF_PIX] = imx_clk_hw_gate2("lcdif_pix", "lcdif", ccm_base + 0x74, 10);
hws[IMXRT1050_CLK_DMA] = imx_clk_hw_gate("dma", "ipg", ccm_base + 0x7C, 6);
hws[IMXRT1050_CLK_DMA_MUX] = imx_clk_hw_gate("dmamux0", "ipg", ccm_base + 0x7C, 7);
imx_check_clk_hws(hws, IMXRT1050_CLK_END);
@@ -148,8 +153,12 @@ static int imxrt1050_clocks_probe(struct platform_device *pdev)
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
if (ret < 0) {
dev_err(dev, "Failed to register clks for i.MXRT1050.\n");
- imx_unregister_hw_clocks(hws, IMXRT1050_CLK_END);
+ goto unregister_hws;
}
+ return 0;
+
+unregister_hws:
+ imx_unregister_hw_clocks(hws, IMXRT1050_CLK_END);
return ret;
}
static const struct of_device_id imxrt1050_clk_of_match[] = {
@@ -166,3 +175,8 @@ static struct platform_driver imxrt1050_clk_driver = {
},
};
module_platform_driver(imxrt1050_clk_driver);
+
+MODULE_DESCRIPTION("NXP i.MX RT1050 clock driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jesse Taube <Mr.Bossman075@gmail.com>");
+MODULE_AUTHOR("Giulio Benetti <giulio.benetti@benettiengineering.com>");
diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c
index dd5abd09f3e2..6376557a3c3d 100644
--- a/drivers/clk/imx/clk-lpcg-scu.c
+++ b/drivers/clk/imx/clk-lpcg-scu.c
@@ -6,10 +6,12 @@
#include <linux/bits.h>
#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/units.h>
#include "clk-scu.h"
@@ -41,6 +43,29 @@ struct clk_lpcg_scu {
#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
+/* e10858 -LPCG clock gating register synchronization errata */
+static void lpcg_e10858_writel(unsigned long rate, void __iomem *reg, u32 val)
+{
+ writel(val, reg);
+
+ if (rate >= 24 * HZ_PER_MHZ || rate == 0) {
+ /*
+ * The time taken to access the LPCG registers from the AP core
+ * through the interconnect is longer than the minimum delay
+ * of 4 clock cycles required by the errata.
+ * Adding a readl will provide sufficient delay to prevent
+ * back-to-back writes.
+ */
+ readl(reg);
+ } else {
+ /*
+ * For clocks running below 24MHz, wait a minimum of
+ * 4 clock cycles.
+ */
+ ndelay(4 * (DIV_ROUND_UP(1000 * HZ_PER_MHZ, rate)));
+ }
+}
+
static int clk_lpcg_scu_enable(struct clk_hw *hw)
{
struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
@@ -57,7 +82,8 @@ static int clk_lpcg_scu_enable(struct clk_hw *hw)
val |= CLK_GATE_SCU_LPCG_HW_SEL;
reg |= val << clk->bit_idx;
- writel(reg, clk->reg);
+
+ lpcg_e10858_writel(clk_hw_get_rate(hw), clk->reg, reg);
spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
@@ -74,7 +100,7 @@ static void clk_lpcg_scu_disable(struct clk_hw *hw)
reg = readl_relaxed(clk->reg);
reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
- writel(reg, clk->reg);
+ lpcg_e10858_writel(clk_hw_get_rate(hw), clk->reg, reg);
spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
}
@@ -135,6 +161,9 @@ static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
{
struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
+ if (!strncmp("hdmi_lpcg", clk_hw_get_name(&clk->hw), strlen("hdmi_lpcg")))
+ return 0;
+
clk->state = readl_relaxed(clk->reg);
dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
@@ -145,13 +174,11 @@ static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
{
struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
- /*
- * FIXME: Sometimes writes don't work unless the CPU issues
- * them twice
- */
+ if (!strncmp("hdmi_lpcg", clk_hw_get_name(&clk->hw), strlen("hdmi_lpcg")))
+ return 0;
writel(clk->state, clk->reg);
- writel(clk->state, clk->reg);
+ lpcg_e10858_writel(0, clk->reg, clk->state);
dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
return 0;
diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c
index 5d2a9a3be95e..31220fa7882b 100644
--- a/drivers/clk/imx/clk-pfd.c
+++ b/drivers/clk/imx/clk-pfd.c
@@ -5,6 +5,7 @@
*/
#include <linux/clk-provider.h>
+#include <linux/export.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/err.h>
@@ -61,24 +62,26 @@ static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw,
return tmp;
}
-static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pfd_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- u64 tmp = *prate;
+ u64 tmp = req->best_parent_rate;
u8 frac;
- tmp = tmp * 18 + rate / 2;
- do_div(tmp, rate);
+ tmp = tmp * 18 + req->rate / 2;
+ do_div(tmp, req->rate);
frac = tmp;
if (frac < 12)
frac = 12;
else if (frac > 35)
frac = 35;
- tmp = *prate;
+ tmp = req->best_parent_rate;
tmp *= 18;
do_div(tmp, frac);
- return tmp;
+ req->rate = tmp;
+
+ return 0;
}
static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -116,7 +119,7 @@ static const struct clk_ops clk_pfd_ops = {
.enable = clk_pfd_enable,
.disable = clk_pfd_disable,
.recalc_rate = clk_pfd_recalc_rate,
- .round_rate = clk_pfd_round_rate,
+ .determine_rate = clk_pfd_determine_rate,
.set_rate = clk_pfd_set_rate,
.is_enabled = clk_pfd_is_enabled,
};
@@ -153,3 +156,4 @@ struct clk_hw *imx_clk_hw_pfd(const char *name, const char *parent_name,
return hw;
}
+EXPORT_SYMBOL_GPL(imx_clk_hw_pfd);
diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
index 1d0f79e9c346..36d0e80b55b8 100644
--- a/drivers/clk/imx/clk-pll14xx.c
+++ b/drivers/clk/imx/clk-pll14xx.c
@@ -54,7 +54,11 @@ static const struct imx_pll14xx_rate_table imx_pll1416x_tbl[] = {
PLL_1416X_RATE(800000000U, 200, 3, 1),
PLL_1416X_RATE(750000000U, 250, 2, 2),
PLL_1416X_RATE(700000000U, 350, 3, 2),
+ PLL_1416X_RATE(640000000U, 320, 3, 2),
PLL_1416X_RATE(600000000U, 300, 3, 2),
+ PLL_1416X_RATE(416000000U, 208, 3, 2),
+ PLL_1416X_RATE(320000000U, 160, 3, 2),
+ PLL_1416X_RATE(208000000U, 208, 3, 3),
};
static const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = {
@@ -62,8 +66,6 @@ static const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = {
PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
PLL_1443X_RATE(594000000U, 198, 2, 2, 0),
PLL_1443X_RATE(519750000U, 173, 2, 2, 16384),
- PLL_1443X_RATE(393216000U, 262, 2, 3, 9437),
- PLL_1443X_RATE(361267200U, 361, 3, 3, 17511),
};
struct imx_pll14xx_clk imx_1443x_pll = {
@@ -104,15 +106,15 @@ static const struct imx_pll14xx_rate_table *imx_get_pll_settings(
static long pll14xx_calc_rate(struct clk_pll14xx *pll, int mdiv, int pdiv,
int sdiv, int kdiv, unsigned long prate)
{
- u64 fvco = prate;
+ u64 fout = prate;
- /* fvco = (m * 65536 + k) * Fin / (p * 65536) */
- fvco *= (mdiv * 65536 + kdiv);
+ /* fout = (m * 65536 + k) * Fin / (p * 65536) / (1 << sdiv) */
+ fout *= (mdiv * 65536 + kdiv);
pdiv *= 65536;
- do_div(fvco, pdiv << sdiv);
+ do_div(fout, pdiv << sdiv);
- return fvco;
+ return fout;
}
static long pll1443x_calc_kdiv(int mdiv, int pdiv, int sdiv,
@@ -131,19 +133,19 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat
{
u32 pll_div_ctl0, pll_div_ctl1;
int mdiv, pdiv, sdiv, kdiv;
- long fvco, rate_min, rate_max, dist, best = LONG_MAX;
+ long fout, rate_min, rate_max, dist, best = LONG_MAX;
const struct imx_pll14xx_rate_table *tt;
/*
* Fractional PLL constrains:
*
- * a) 6MHz <= prate <= 25MHz
- * b) 1 <= p <= 63 (1 <= p <= 4 prate = 24MHz)
- * c) 64 <= m <= 1023
- * d) 0 <= s <= 6
- * e) -32768 <= k <= 32767
+ * a) 1 <= p <= 63
+ * b) 64 <= m <= 1023
+ * c) 0 <= s <= 6
+ * d) -32768 <= k <= 32767
*
* fvco = (m * 65536 + k) * prate / (p * 65536)
+ * fout = (m * 65536 + k) * prate / (p * 65536) / (1 << sdiv)
*/
/* First try if we can get the desired rate from one of the static entries */
@@ -174,8 +176,8 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat
pr_debug("%s: in=%ld, want=%ld Only adjust kdiv %ld -> %d\n",
clk_hw_get_name(&pll->hw), prate, rate,
FIELD_GET(KDIV_MASK, pll_div_ctl1), kdiv);
- fvco = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
- t->rate = (unsigned int)fvco;
+ fout = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
+ t->rate = (unsigned int)fout;
t->mdiv = mdiv;
t->pdiv = pdiv;
t->sdiv = sdiv;
@@ -184,20 +186,20 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat
}
/* Finally calculate best values */
- for (pdiv = 1; pdiv <= 7; pdiv++) {
+ for (pdiv = 1; pdiv <= 63; pdiv++) {
for (sdiv = 0; sdiv <= 6; sdiv++) {
/* calc mdiv = round(rate * pdiv * 2^sdiv) / prate) */
mdiv = DIV_ROUND_CLOSEST(rate * (pdiv << sdiv), prate);
mdiv = clamp(mdiv, 64, 1023);
kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate);
- fvco = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
+ fout = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
/* best match */
- dist = abs((long)rate - (long)fvco);
+ dist = abs((long)rate - (long)fout);
if (dist < best) {
best = dist;
- t->rate = (unsigned int)fvco;
+ t->rate = (unsigned int)fout;
t->mdiv = mdiv;
t->pdiv = pdiv;
t->sdiv = sdiv;
@@ -214,8 +216,8 @@ found:
t->mdiv, t->kdiv);
}
-static long clk_pll1416x_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pll1416x_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
@@ -223,22 +225,29 @@ static long clk_pll1416x_round_rate(struct clk_hw *hw, unsigned long rate,
/* Assuming rate_table is in descending order */
for (i = 0; i < pll->rate_count; i++)
- if (rate >= rate_table[i].rate)
- return rate_table[i].rate;
+ if (req->rate >= rate_table[i].rate) {
+ req->rate = rate_table[i].rate;
+
+ return 0;
+ }
/* return minimum supported value */
- return rate_table[pll->rate_count - 1].rate;
+ req->rate = rate_table[pll->rate_count - 1].rate;
+
+ return 0;
}
-static long clk_pll1443x_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pll1443x_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
struct imx_pll14xx_rate_table t;
- imx_pll14xx_calc_settings(pll, rate, *prate, &t);
+ imx_pll14xx_calc_settings(pll, req->rate, req->best_parent_rate, &t);
+
+ req->rate = t.rate;
- return t.rate;
+ return 0;
}
static unsigned long clk_pll14xx_recalc_rate(struct clk_hw *hw,
@@ -254,7 +263,7 @@ static unsigned long clk_pll14xx_recalc_rate(struct clk_hw *hw,
if (pll->type == PLL_1443X) {
pll_div_ctl1 = readl_relaxed(pll->base + DIV_CTL1);
- kdiv = FIELD_GET(KDIV_MASK, pll_div_ctl1);
+ kdiv = (s16)FIELD_GET(KDIV_MASK, pll_div_ctl1);
} else {
kdiv = 0;
}
@@ -468,7 +477,7 @@ static const struct clk_ops clk_pll1416x_ops = {
.unprepare = clk_pll14xx_unprepare,
.is_prepared = clk_pll14xx_is_prepared,
.recalc_rate = clk_pll14xx_recalc_rate,
- .round_rate = clk_pll1416x_round_rate,
+ .determine_rate = clk_pll1416x_determine_rate,
.set_rate = clk_pll1416x_set_rate,
};
@@ -481,7 +490,7 @@ static const struct clk_ops clk_pll1443x_ops = {
.unprepare = clk_pll14xx_unprepare,
.is_prepared = clk_pll14xx_is_prepared,
.recalc_rate = clk_pll14xx_recalc_rate,
- .round_rate = clk_pll1443x_round_rate,
+ .determine_rate = clk_pll1443x_determine_rate,
.set_rate = clk_pll1443x_set_rate,
};
diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c
index ff17f0664faa..bb497ad5e0ae 100644
--- a/drivers/clk/imx/clk-pllv2.c
+++ b/drivers/clk/imx/clk-pllv2.c
@@ -178,18 +178,25 @@ static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pllv2_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
u32 dp_op, dp_mfd, dp_mfn;
int ret;
- ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);
- if (ret)
- return ret;
+ ret = __clk_pllv2_set_rate(req->rate, req->best_parent_rate, &dp_op,
+ &dp_mfd, &dp_mfn);
+ if (ret) {
+ req->rate = ret;
- return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN,
- dp_op, dp_mfd, dp_mfn);
+ return 0;
+ }
+
+ req->rate = __clk_pllv2_recalc_rate(req->best_parent_rate,
+ MXC_PLL_DP_CTL_DPDCK0_2_EN, dp_op,
+ dp_mfd, dp_mfn);
+
+ return 0;
}
static int clk_pllv2_prepare(struct clk_hw *hw)
@@ -235,7 +242,7 @@ static const struct clk_ops clk_pllv2_ops = {
.prepare = clk_pllv2_prepare,
.unprepare = clk_pllv2_unprepare,
.recalc_rate = clk_pllv2_recalc_rate,
- .round_rate = clk_pllv2_round_rate,
+ .determine_rate = clk_pllv2_determine_rate,
.set_rate = clk_pllv2_set_rate,
};
diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
index eea32f87c60a..b99508367bcb 100644
--- a/drivers/clk/imx/clk-pllv3.c
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -6,6 +6,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/slab.h>
@@ -116,13 +117,14 @@ static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw,
return (div == 1) ? parent_rate * 22 : parent_rate * 20;
}
-static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pllv3_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long parent_rate = *prate;
+ unsigned long parent_rate = req->best_parent_rate;
- return (rate >= parent_rate * 22) ? parent_rate * 22 :
- parent_rate * 20;
+ req->rate = (req->rate >= parent_rate * 22) ? parent_rate * 22 : parent_rate * 20;
+
+ return 0;
}
static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -151,7 +153,7 @@ static const struct clk_ops clk_pllv3_ops = {
.unprepare = clk_pllv3_unprepare,
.is_prepared = clk_pllv3_is_prepared,
.recalc_rate = clk_pllv3_recalc_rate,
- .round_rate = clk_pllv3_round_rate,
+ .determine_rate = clk_pllv3_determine_rate,
.set_rate = clk_pllv3_set_rate,
};
@@ -164,21 +166,23 @@ static unsigned long clk_pllv3_sys_recalc_rate(struct clk_hw *hw,
return parent_rate * div / 2;
}
-static long clk_pllv3_sys_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pllv3_sys_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long parent_rate = *prate;
+ unsigned long parent_rate = req->best_parent_rate;
unsigned long min_rate = parent_rate * 54 / 2;
unsigned long max_rate = parent_rate * 108 / 2;
u32 div;
- if (rate > max_rate)
- rate = max_rate;
- else if (rate < min_rate)
- rate = min_rate;
- div = rate * 2 / parent_rate;
+ if (req->rate > max_rate)
+ req->rate = max_rate;
+ else if (req->rate < min_rate)
+ req->rate = min_rate;
+ div = req->rate * 2 / parent_rate;
- return parent_rate * div / 2;
+ req->rate = parent_rate * div / 2;
+
+ return 0;
}
static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -206,7 +210,7 @@ static const struct clk_ops clk_pllv3_sys_ops = {
.unprepare = clk_pllv3_unprepare,
.is_prepared = clk_pllv3_is_prepared,
.recalc_rate = clk_pllv3_sys_recalc_rate,
- .round_rate = clk_pllv3_sys_round_rate,
+ .determine_rate = clk_pllv3_sys_determine_rate,
.set_rate = clk_pllv3_sys_set_rate,
};
@@ -225,10 +229,10 @@ static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw,
return parent_rate * div + (unsigned long)temp64;
}
-static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pllv3_av_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long parent_rate = *prate;
+ unsigned long parent_rate = req->best_parent_rate;
unsigned long min_rate = parent_rate * 27;
unsigned long max_rate = parent_rate * 54;
u32 div;
@@ -236,16 +240,16 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate,
u32 max_mfd = 0x3FFFFFFF;
u64 temp64;
- if (rate > max_rate)
- rate = max_rate;
- else if (rate < min_rate)
- rate = min_rate;
+ if (req->rate > max_rate)
+ req->rate = max_rate;
+ else if (req->rate < min_rate)
+ req->rate = min_rate;
if (parent_rate <= max_mfd)
mfd = parent_rate;
- div = rate / parent_rate;
- temp64 = (u64) (rate - div * parent_rate);
+ div = req->rate / parent_rate;
+ temp64 = (u64) (req->rate - div * parent_rate);
temp64 *= mfd;
temp64 = div64_ul(temp64, parent_rate);
mfn = temp64;
@@ -254,7 +258,9 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate,
temp64 *= mfn;
do_div(temp64, mfd);
- return parent_rate * div + (unsigned long)temp64;
+ req->rate = parent_rate * div + (unsigned long)temp64;
+
+ return 0;
}
static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -295,7 +301,7 @@ static const struct clk_ops clk_pllv3_av_ops = {
.unprepare = clk_pllv3_unprepare,
.is_prepared = clk_pllv3_is_prepared,
.recalc_rate = clk_pllv3_av_recalc_rate,
- .round_rate = clk_pllv3_av_round_rate,
+ .determine_rate = clk_pllv3_av_determine_rate,
.set_rate = clk_pllv3_av_set_rate,
};
@@ -354,12 +360,15 @@ static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw,
return clk_pllv3_vf610_mf_to_rate(parent_rate, mf);
}
-static long clk_pllv3_vf610_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pllv3_vf610_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(*prate, rate);
+ struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(req->best_parent_rate,
+ req->rate);
+
+ req->rate = clk_pllv3_vf610_mf_to_rate(req->best_parent_rate, mf);
- return clk_pllv3_vf610_mf_to_rate(*prate, mf);
+ return 0;
}
static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -388,7 +397,7 @@ static const struct clk_ops clk_pllv3_vf610_ops = {
.unprepare = clk_pllv3_unprepare,
.is_prepared = clk_pllv3_is_prepared,
.recalc_rate = clk_pllv3_vf610_recalc_rate,
- .round_rate = clk_pllv3_vf610_round_rate,
+ .determine_rate = clk_pllv3_vf610_determine_rate,
.set_rate = clk_pllv3_vf610_set_rate,
};
@@ -486,3 +495,4 @@ struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name,
return hw;
}
+EXPORT_SYMBOL_GPL(imx_clk_hw_pllv3);
diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c
index 6e7e34571fc8..01d05b5d5438 100644
--- a/drivers/clk/imx/clk-pllv4.c
+++ b/drivers/clk/imx/clk-pllv4.c
@@ -44,11 +44,15 @@ struct clk_pllv4 {
u32 cfg_offset;
u32 num_offset;
u32 denom_offset;
+ bool use_mult_range;
};
/* Valid PLL MULT Table */
static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
+/* Valid PLL MULT range, (max, min) */
+static const int pllv4_mult_range[] = {54, 27};
+
#define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
#define LOCK_TIMEOUT_US USEC_PER_MSEC
@@ -91,33 +95,48 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
return (parent_rate * mult) + (u32)temp64;
}
-static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_pllv4_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long parent_rate = *prate;
+ struct clk_pllv4 *pll = to_clk_pllv4(hw);
+ unsigned long parent_rate = req->best_parent_rate;
unsigned long round_rate, i;
u32 mfn, mfd = DEFAULT_MFD;
bool found = false;
u64 temp64;
-
- for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
- round_rate = parent_rate * pllv4_mult_table[i];
- if (rate >= round_rate) {
+ u32 mult;
+
+ if (pll->use_mult_range) {
+ temp64 = (u64) req->rate;
+ do_div(temp64, parent_rate);
+ mult = temp64;
+ if (mult >= pllv4_mult_range[1] &&
+ mult <= pllv4_mult_range[0]) {
+ round_rate = parent_rate * mult;
found = true;
- break;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
+ round_rate = parent_rate * pllv4_mult_table[i];
+ if (req->rate >= round_rate) {
+ found = true;
+ break;
+ }
}
}
if (!found) {
pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
- clk_hw_get_name(hw), rate, parent_rate);
+ clk_hw_get_name(hw), req->rate, parent_rate);
+ req->rate = 0;
+
return 0;
}
if (parent_rate <= MAX_MFD)
mfd = parent_rate;
- temp64 = (u64)(rate - round_rate);
+ temp64 = (u64)(req->rate - round_rate);
temp64 *= mfd;
do_div(temp64, parent_rate);
mfn = temp64;
@@ -128,24 +147,35 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
* pair of mfn/mfd, we simply return the round_rate without using
* the frac part.
*/
- if (mfn >= mfd)
- return round_rate;
+ if (mfn >= mfd) {
+ req->rate = round_rate;
+
+ return 0;
+ }
temp64 = (u64)parent_rate;
temp64 *= mfn;
do_div(temp64, mfd);
- return round_rate + (u32)temp64;
+ req->rate = round_rate + (u32)temp64;
+
+ return 0;
}
-static bool clk_pllv4_is_valid_mult(unsigned int mult)
+static bool clk_pllv4_is_valid_mult(struct clk_pllv4 *pll, unsigned int mult)
{
int i;
/* check if mult is in valid MULT table */
- for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
- if (pllv4_mult_table[i] == mult)
+ if (pll->use_mult_range) {
+ if (mult >= pllv4_mult_range[1] &&
+ mult <= pllv4_mult_range[0])
return true;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
+ if (pllv4_mult_table[i] == mult)
+ return true;
+ }
}
return false;
@@ -160,7 +190,7 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
mult = rate / parent_rate;
- if (!clk_pllv4_is_valid_mult(mult))
+ if (!clk_pllv4_is_valid_mult(pll, mult))
return -EINVAL;
if (parent_rate <= MAX_MFD)
@@ -206,7 +236,7 @@ static void clk_pllv4_unprepare(struct clk_hw *hw)
static const struct clk_ops clk_pllv4_ops = {
.recalc_rate = clk_pllv4_recalc_rate,
- .round_rate = clk_pllv4_round_rate,
+ .determine_rate = clk_pllv4_determine_rate,
.set_rate = clk_pllv4_set_rate,
.prepare = clk_pllv4_prepare,
.unprepare = clk_pllv4_unprepare,
@@ -227,10 +257,13 @@ struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name,
pll->base = base;
- if (type == IMX_PLLV4_IMX8ULP) {
+ if (type == IMX_PLLV4_IMX8ULP ||
+ type == IMX_PLLV4_IMX8ULP_1GHZ) {
pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET;
pll->num_offset = IMX8ULP_PLL_NUM_OFFSET;
pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET;
+ if (type == IMX_PLLV4_IMX8ULP_1GHZ)
+ pll->use_mult_range = true;
} else {
pll->cfg_offset = PLL_CFG_OFFSET;
pll->num_offset = PLL_NUM_OFFSET;
diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c
index c56e406138db..34c9dc1fb20e 100644
--- a/drivers/clk/imx/clk-scu.c
+++ b/drivers/clk/imx/clk-scu.c
@@ -9,11 +9,13 @@
#include <linux/bsearch.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/firmware/imx/svc/rm.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <xen/xen.h>
#include "clk-scu.h"
@@ -251,21 +253,20 @@ static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
}
/*
- * clk_scu_round_rate - Round clock rate for a SCU clock
+ * clk_scu_determine_rate - Returns the closest rate for a SCU clock
* @hw: clock to round rate for
- * @rate: rate to round
- * @parent_rate: parent rate provided by common clock framework, not used
+ * @req: clock rate request
*
- * Returns the current clock rate, or zero in failure.
+ * Returns 0 on success, a negative error on failure
*/
-static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_scu_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
/*
* Assume we support all the requested rate and let the SCU firmware
* to handle the left work
*/
- return rate;
+ return 0;
}
static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
@@ -425,7 +426,7 @@ static void clk_scu_unprepare(struct clk_hw *hw)
static const struct clk_ops clk_scu_ops = {
.recalc_rate = clk_scu_recalc_rate,
- .round_rate = clk_scu_round_rate,
+ .determine_rate = clk_scu_determine_rate,
.set_rate = clk_scu_set_rate,
.get_parent = clk_scu_get_parent,
.set_parent = clk_scu_set_parent,
@@ -435,7 +436,7 @@ static const struct clk_ops clk_scu_ops = {
static const struct clk_ops clk_scu_cpu_ops = {
.recalc_rate = clk_scu_recalc_rate,
- .round_rate = clk_scu_round_rate,
+ .determine_rate = clk_scu_determine_rate,
.set_rate = clk_scu_atf_set_cpu_rate,
.prepare = clk_scu_prepare,
.unprepare = clk_scu_unprepare,
@@ -443,7 +444,7 @@ static const struct clk_ops clk_scu_cpu_ops = {
static const struct clk_ops clk_scu_pi_ops = {
.recalc_rate = clk_scu_recalc_rate,
- .round_rate = clk_scu_round_rate,
+ .determine_rate = clk_scu_determine_rate,
.set_rate = clk_scu_set_rate,
};
@@ -548,7 +549,6 @@ static int imx_clk_scu_probe(struct platform_device *pdev)
if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) ||
(clk->rsrc == IMX_SC_R_A72))) {
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
}
@@ -577,7 +577,7 @@ static int __maybe_unused imx_clk_scu_suspend(struct device *dev)
clk->rate = clk_scu_recalc_rate(&clk->hw, 0);
else
clk->rate = clk_hw_get_rate(&clk->hw);
- clk->is_enabled = clk_hw_is_enabled(&clk->hw);
+ clk->is_enabled = clk_hw_is_prepared(&clk->hw);
if (clk->parent)
dev_dbg(dev, "save parent %s idx %u\n", clk_hw_get_name(clk->parent),
@@ -653,6 +653,18 @@ static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id)
return of_genpd_add_device(&genpdspec, dev);
}
+static bool imx_clk_is_resource_owned(u32 rsrc)
+{
+ /*
+ * A-core resources are special. SCFW reports they are not "owned" by
+ * current partition but linux can still adjust them for cpufreq.
+ */
+ if (rsrc == IMX_SC_R_A53 || rsrc == IMX_SC_R_A72 || rsrc == IMX_SC_R_A35)
+ return true;
+
+ return imx_sc_rm_is_resource_owned(ccm_ipc_handle, rsrc);
+}
+
struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
const char * const *parents,
int num_parents, u32 rsrc_id, u8 clk_type)
@@ -670,6 +682,9 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
if (!imx_scu_clk_is_valid(rsrc_id))
return ERR_PTR(-EINVAL);
+ if (!imx_clk_is_resource_owned(rsrc_id))
+ return NULL;
+
pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
if (!pdev) {
pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n",
@@ -678,36 +693,38 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
}
ret = platform_device_add_data(pdev, &clk, sizeof(clk));
- if (ret) {
- platform_device_put(pdev);
- return ERR_PTR(ret);
- }
+ if (ret)
+ goto put_device;
ret = driver_set_override(&pdev->dev, &pdev->driver_override,
"imx-scu-clk", strlen("imx-scu-clk"));
- if (ret) {
- platform_device_put(pdev);
- return ERR_PTR(ret);
- }
+ if (ret)
+ goto put_device;
ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id);
if (ret)
pr_warn("%s: failed to attached the power domain %d\n",
name, ret);
- platform_device_add(pdev);
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto put_device;
- /* For API backwards compatiblilty, simply return NULL for success */
+ /* For API backwards compatibility, simply return NULL for success */
return NULL;
+
+put_device:
+ platform_device_put(pdev);
+ return ERR_PTR(ret);
}
void imx_clk_scu_unregister(void)
{
- struct imx_scu_clk_node *clk;
+ struct imx_scu_clk_node *clk, *n;
int i;
for (i = 0; i < IMX_SC_R_LAST; i++) {
- list_for_each_entry(clk, &imx_scu_clks[i], node) {
+ list_for_each_entry_safe(clk, n, &imx_scu_clks[i], node) {
clk_hw_unregister(clk->hw);
kfree(clk);
}
@@ -730,15 +747,15 @@ static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw,
return err ? 0 : rate;
}
-static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_gpr_div_scu_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- if (rate < *prate)
- rate = *prate / 2;
+ if (req->rate < req->best_parent_rate)
+ req->rate = req->best_parent_rate / 2;
else
- rate = *prate;
+ req->rate = req->best_parent_rate;
- return rate;
+ return 0;
}
static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -757,7 +774,7 @@ static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_gpr_div_scu_ops = {
.recalc_rate = clk_gpr_div_scu_recalc_rate,
- .round_rate = clk_gpr_div_scu_round_rate,
+ .determine_rate = clk_gpr_div_scu_determine_rate,
.set_rate = clk_gpr_div_scu_set_rate,
};
@@ -781,6 +798,7 @@ static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_gpr_mux_scu_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_gpr_mux_scu_get_parent,
.set_parent = clk_gpr_mux_scu_set_parent,
};
@@ -847,6 +865,11 @@ struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_na
return ERR_PTR(-EINVAL);
}
+ if (!imx_clk_is_resource_owned(rsrc_id)) {
+ kfree(clk_node);
+ return NULL;
+ }
+
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk) {
kfree(clk_node);
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 9e11f1c7c397..41eb38552a9c 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -139,7 +139,7 @@ static struct clk * __init vf610_get_fixed_clock(
return clk;
};
-static int vf610_clk_suspend(void)
+static int vf610_clk_suspend(void *data)
{
int i;
@@ -156,7 +156,7 @@ static int vf610_clk_suspend(void)
return 0;
}
-static void vf610_clk_resume(void)
+static void vf610_clk_resume(void *data)
{
int i;
@@ -171,11 +171,15 @@ static void vf610_clk_resume(void)
writel_relaxed(ccgr[i], CCM_CCGRx(i));
}
-static struct syscore_ops vf610_clk_syscore_ops = {
+static const struct syscore_ops vf610_clk_syscore_ops = {
.suspend = vf610_clk_suspend,
.resume = vf610_clk_resume,
};
+static struct syscore vf610_clk_syscore = {
+ .ops = &vf610_clk_syscore_ops,
+};
+
static void __init vf610_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
@@ -462,7 +466,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clk[clks_init_on[i]]);
- register_syscore_ops(&vf610_clk_syscore_ops);
+ register_syscore(&vf610_clk_syscore);
/* Add the clocks to provider list */
clk_data.clks = clk;
diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c
index 5582f18dd632..df83bd939492 100644
--- a/drivers/clk/imx/clk.c
+++ b/drivers/clk/imx/clk.c
@@ -20,14 +20,6 @@ EXPORT_SYMBOL_GPL(imx_ccm_lock);
bool mcore_booted;
EXPORT_SYMBOL_GPL(mcore_booted);
-void imx_unregister_clocks(struct clk *clks[], unsigned int count)
-{
- unsigned int i;
-
- for (i = 0; i < count; i++)
- clk_unregister(clks[i]);
-}
-
void imx_unregister_hw_clocks(struct clk_hw *hws[], unsigned int count)
{
unsigned int i;
@@ -110,8 +102,21 @@ struct clk_hw *imx_obtain_fixed_clock_hw(
return __clk_get_hw(clk);
}
-struct clk_hw * imx_obtain_fixed_clk_hw(struct device_node *np,
- const char *name)
+struct clk_hw *imx_obtain_fixed_of_clock(struct device_node *np,
+ const char *name, unsigned long rate)
+{
+ struct clk *clk = of_clk_get_by_name(np, name);
+ struct clk_hw *hw;
+
+ if (IS_ERR(clk))
+ hw = imx_obtain_fixed_clock_hw(name, rate);
+ else
+ hw = __clk_get_hw(clk);
+
+ return hw;
+}
+
+struct clk_hw *imx_get_clk_hw_by_name(struct device_node *np, const char *name)
{
struct clk *clk;
@@ -121,7 +126,7 @@ struct clk_hw * imx_obtain_fixed_clk_hw(struct device_node *np,
return __clk_get_hw(clk);
}
-EXPORT_SYMBOL_GPL(imx_obtain_fixed_clk_hw);
+EXPORT_SYMBOL_GPL(imx_get_clk_hw_by_name);
/*
* This fixups the register CCM_CSCMR1 write value.
@@ -166,8 +171,10 @@ __setup_param("earlycon", imx_keep_uart_earlycon,
__setup_param("earlyprintk", imx_keep_uart_earlyprintk,
imx_keep_uart_clocks_param, 0);
-void imx_register_uart_clocks(unsigned int clk_count)
+void imx_register_uart_clocks(void)
{
+ unsigned int num __maybe_unused;
+
imx_enabled_uart_clocks = 0;
/* i.MX boards use device trees now. For build tests without CONFIG_OF, do nothing */
@@ -175,14 +182,18 @@ void imx_register_uart_clocks(unsigned int clk_count)
if (imx_keep_uart_clocks) {
int i;
- imx_uart_clocks = kcalloc(clk_count, sizeof(struct clk *), GFP_KERNEL);
- if (!imx_uart_clocks)
+ num = of_clk_get_parent_count(of_stdout);
+ if (!num)
return;
if (!of_stdout)
return;
- for (i = 0; i < clk_count; i++) {
+ imx_uart_clocks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL);
+ if (!imx_uart_clocks)
+ return;
+
+ for (i = 0; i < num; i++) {
imx_uart_clocks[imx_enabled_uart_clocks] = of_clk_get(of_stdout, i);
/* Stop if there are no more of_stdout references */
@@ -206,12 +217,14 @@ static int __init imx_clk_disable_uart(void)
clk_disable_unprepare(imx_uart_clocks[i]);
clk_put(imx_uart_clocks[i]);
}
- kfree(imx_uart_clocks);
}
+ kfree(imx_uart_clocks);
+
return 0;
}
late_initcall_sync(imx_clk_disable_uart);
#endif
+MODULE_DESCRIPTION("Common clock support for NXP i.MX SoC family");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 5061a06468df..aa5202f284f3 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -12,14 +12,13 @@ extern bool mcore_booted;
void imx_check_clocks(struct clk *clks[], unsigned int count);
void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count);
#ifndef MODULE
-void imx_register_uart_clocks(unsigned int clk_count);
+void imx_register_uart_clocks(void);
#else
-static inline void imx_register_uart_clocks(unsigned int clk_count)
+static inline void imx_register_uart_clocks(void)
{
}
#endif
void imx_mmdc_mask_handshake(void __iomem *ccm_base, unsigned int chn);
-void imx_unregister_clocks(struct clk *clks[], unsigned int count);
void imx_unregister_hw_clocks(struct clk_hw *hws[], unsigned int count);
extern void imx_cscmr1_fixup(u32 *val);
@@ -46,6 +45,7 @@ enum imx_pll14xx_type {
enum imx_pllv4_type {
IMX_PLLV4_IMX7ULP,
IMX_PLLV4_IMX8ULP,
+ IMX_PLLV4_IMX8ULP_1GHZ,
};
enum imx_pfdv2_type {
@@ -73,6 +73,9 @@ extern struct imx_pll14xx_clk imx_1416x_pll;
extern struct imx_pll14xx_clk imx_1443x_pll;
extern struct imx_pll14xx_clk imx_1443x_dram_pll;
+#define CLK_FRACN_GPPLL_INTEGER BIT(0)
+#define CLK_FRACN_GPPLL_FRACN BIT(1)
+
/* NOTE: Rate table should be kept sorted in descending order. */
struct imx_fracn_gppll_rate_table {
unsigned int rate;
@@ -91,8 +94,12 @@ struct imx_fracn_gppll_clk {
struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
const struct imx_fracn_gppll_clk *pll_clk);
+struct clk_hw *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_fracn_gppll_clk *pll_clk);
extern struct imx_fracn_gppll_clk imx_fracn_gppll;
+extern struct imx_fracn_gppll_clk imx_fracn_gppll_integer;
#define imx_clk_cpu(name, parent_name, div, mux, pll, step) \
to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step))
@@ -153,9 +160,6 @@ extern struct imx_fracn_gppll_clk imx_fracn_gppll;
#define imx_clk_pllv2(name, parent, base) \
to_clk(imx_clk_hw_pllv2(name, parent, base))
-#define imx_clk_mux_flags(name, reg, shift, width, parents, num_parents, flags) \
- to_clk(imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags))
-
#define imx_clk_hw_gate(name, parent, reg, shift) \
imx_clk_hw_gate_flags(name, parent, reg, shift, 0)
@@ -288,8 +292,10 @@ struct clk * imx_obtain_fixed_clock(
struct clk_hw *imx_obtain_fixed_clock_hw(
const char *name, unsigned long rate);
-struct clk_hw *imx_obtain_fixed_clk_hw(struct device_node *np,
- const char *name);
+struct clk_hw *imx_obtain_fixed_of_clock(struct device_node *np,
+ const char *name, unsigned long rate);
+
+struct clk_hw *imx_get_clk_hw_by_name(struct device_node *np, const char *name);
struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent,
void __iomem *reg, u8 shift, u32 exclusive_mask);
@@ -347,6 +353,15 @@ static inline struct clk_hw *imx_clk_hw_fixed_factor(const char *name,
CLK_SET_RATE_PARENT, mult, div);
}
+static inline struct clk_hw *imx_clk_hw_divider_closest(const char *name,
+ const char *parent,
+ void __iomem *reg, u8 shift,
+ u8 width)
+{
+ return clk_hw_register_divider(NULL, name, parent, 0,
+ reg, shift, width, CLK_DIVIDER_ROUND_CLOSEST, &imx_ccm_lock);
+}
+
static inline struct clk_hw *__imx_clk_hw_divider(const char *name,
const char *parent,
void __iomem *reg, u8 shift,
@@ -415,6 +430,10 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name,
_imx8m_clk_hw_composite(name, parent_names, reg, \
0, IMX_COMPOSITE_CLK_FLAGS_DEFAULT)
+#define imx8m_clk_hw_composite_flags(name, parent_names, reg, flags) \
+ _imx8m_clk_hw_composite(name, parent_names, reg, \
+ 0, IMX_COMPOSITE_CLK_FLAGS_DEFAULT | flags)
+
#define imx8m_clk_hw_composite_critical(name, parent_names, reg) \
_imx8m_clk_hw_composite(name, parent_names, reg, \
0, IMX_COMPOSITE_CLK_FLAGS_CRITICAL)
@@ -423,6 +442,10 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name,
_imx8m_clk_hw_composite(name, parent_names, reg, \
IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_DEFAULT)
+#define imx8m_clk_hw_composite_bus_flags(name, parent_names, reg, flags) \
+ _imx8m_clk_hw_composite(name, parent_names, reg, \
+ IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_DEFAULT | flags)
+
#define imx8m_clk_hw_composite_bus_critical(name, parent_names, reg) \
_imx8m_clk_hw_composite(name, parent_names, reg, \
IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_CRITICAL)
@@ -445,13 +468,23 @@ struct clk_hw *imx93_clk_composite_flags(const char *name,
const char * const *parent_names,
int num_parents,
void __iomem *reg,
+ u32 domain_id,
unsigned long flags);
-#define imx93_clk_composite(name, parent_names, num_parents, reg) \
- imx93_clk_composite_flags(name, parent_names, num_parents, reg, \
+#define imx93_clk_composite(name, parent_names, num_parents, reg, domain_id) \
+ imx93_clk_composite_flags(name, parent_names, num_parents, reg, domain_id \
CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
+struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
+ u32 mask, u32 domain_id, unsigned int *share_count);
+
struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock);
+
+struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible,
+ u32 reg, const char **parent_names,
+ u8 num_parents, const u32 *mux_table, u32 mask);
+
#endif
diff --git a/drivers/clk/ingenic/Kconfig b/drivers/clk/ingenic/Kconfig
index 898f1bc478c9..f80ac4f2992b 100644
--- a/drivers/clk/ingenic/Kconfig
+++ b/drivers/clk/ingenic/Kconfig
@@ -15,6 +15,16 @@ config INGENIC_CGU_JZ4740
If building for a JZ4740 SoC, you want to say Y here.
+config INGENIC_CGU_JZ4755
+ bool "Ingenic JZ4755 CGU driver"
+ default MACH_JZ4755
+ select INGENIC_CGU_COMMON
+ help
+ Support the clocks provided by the CGU hardware on Ingenic JZ4755
+ and compatible SoCs.
+
+ If building for a JZ4755 SoC, you want to say Y here.
+
config INGENIC_CGU_JZ4725B
bool "Ingenic JZ4725B CGU driver"
default MACH_JZ4725B
diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile
index 9edfaf4610b9..81d8e23c2636 100644
--- a/drivers/clk/ingenic/Makefile
+++ b/drivers/clk/ingenic/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_INGENIC_CGU_COMMON) += cgu.o pm.o
obj-$(CONFIG_INGENIC_CGU_JZ4740) += jz4740-cgu.o
+obj-$(CONFIG_INGENIC_CGU_JZ4755) += jz4755-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4760) += jz4760-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c
index 861c50d6cb24..91e7ac0cc334 100644
--- a/drivers/clk/ingenic/cgu.c
+++ b/drivers/clk/ingenic/cgu.c
@@ -83,7 +83,7 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_pll_info *pll_info;
- unsigned m, n, od_enc, od;
+ unsigned m, n, od, od_enc = 0;
bool bypass;
u32 ctl;
@@ -96,8 +96,11 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
m += pll_info->m_offset;
n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0);
n += pll_info->n_offset;
- od_enc = ctl >> pll_info->od_shift;
- od_enc &= GENMASK(pll_info->od_bits - 1, 0);
+
+ if (pll_info->od_bits > 0) {
+ od_enc = ctl >> pll_info->od_shift;
+ od_enc &= GENMASK(pll_info->od_bits - 1, 0);
+ }
if (pll_info->bypass_bit >= 0) {
ctl = readl(cgu->base + pll_info->bypass_reg);
@@ -108,11 +111,15 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return parent_rate;
}
- for (od = 0; od < pll_info->od_max; od++) {
+ for (od = 0; od < pll_info->od_max; od++)
if (pll_info->od_encoding[od] == od_enc)
break;
- }
- BUG_ON(od == pll_info->od_max);
+
+ /* if od_max = 0, od_bits should be 0 and od is fixed to 1. */
+ if (pll_info->od_max == 0)
+ BUG_ON(pll_info->od_bits != 0);
+ else
+ BUG_ON(od == pll_info->od_max);
od++;
return div_u64((u64)parent_rate * m * pll_info->rate_multiplier,
@@ -167,14 +174,16 @@ ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
n * od);
}
-static long
-ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate,
- unsigned long *prate)
+static int ingenic_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
- return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL);
+ req->rate = ingenic_pll_calc(clk_info, req->rate, req->best_parent_rate,
+ NULL, NULL, NULL);
+
+ return 0;
}
static inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu,
@@ -182,6 +191,9 @@ static inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu,
{
u32 ctl;
+ if (pll_info->stable_bit < 0)
+ return 0;
+
return readl_poll_timeout(cgu->base + pll_info->reg, ctl,
ctl & BIT(pll_info->stable_bit),
0, 100 * USEC_PER_MSEC);
@@ -215,13 +227,18 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift);
ctl |= (n - pll_info->n_offset) << pll_info->n_shift;
- ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
- ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
+ if (pll_info->od_bits > 0) {
+ ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
+ ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
+ }
writel(ctl, cgu->base + pll_info->reg);
+ if (pll_info->set_rate_hook)
+ pll_info->set_rate_hook(pll_info, rate, parent_rate);
+
/* If the PLL is enabled, verify that it's stable */
- if (ctl & BIT(pll_info->enable_bit))
+ if (pll_info->enable_bit >= 0 && (ctl & BIT(pll_info->enable_bit)))
ret = ingenic_pll_check_stable(cgu, pll_info);
spin_unlock_irqrestore(&cgu->lock, flags);
@@ -239,6 +256,9 @@ static int ingenic_pll_enable(struct clk_hw *hw)
int ret;
u32 ctl;
+ if (pll_info->enable_bit < 0)
+ return 0;
+
spin_lock_irqsave(&cgu->lock, flags);
if (pll_info->bypass_bit >= 0) {
ctl = readl(cgu->base + pll_info->bypass_reg);
@@ -269,6 +289,9 @@ static void ingenic_pll_disable(struct clk_hw *hw)
unsigned long flags;
u32 ctl;
+ if (pll_info->enable_bit < 0)
+ return;
+
spin_lock_irqsave(&cgu->lock, flags);
ctl = readl(cgu->base + pll_info->reg);
@@ -286,6 +309,9 @@ static int ingenic_pll_is_enabled(struct clk_hw *hw)
const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
u32 ctl;
+ if (pll_info->enable_bit < 0)
+ return true;
+
ctl = readl(cgu->base + pll_info->reg);
return !!(ctl & BIT(pll_info->enable_bit));
@@ -293,7 +319,7 @@ static int ingenic_pll_is_enabled(struct clk_hw *hw)
static const struct clk_ops ingenic_pll_ops = {
.recalc_rate = ingenic_pll_recalc_rate,
- .round_rate = ingenic_pll_round_rate,
+ .determine_rate = ingenic_pll_determine_rate,
.set_rate = ingenic_pll_set_rate,
.enable = ingenic_pll_enable,
@@ -467,22 +493,23 @@ ingenic_clk_calc_div(struct clk_hw *hw,
return div;
}
-static long
-ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
- unsigned long *parent_rate)
+static int ingenic_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
unsigned int div = 1;
if (clk_info->type & CGU_CLK_DIV)
- div = ingenic_clk_calc_div(hw, clk_info, *parent_rate, req_rate);
+ div = ingenic_clk_calc_div(hw, clk_info, req->best_parent_rate,
+ req->rate);
else if (clk_info->type & CGU_CLK_FIXDIV)
div = clk_info->fixdiv.div;
else if (clk_hw_can_set_rate_parent(hw))
- *parent_rate = req_rate;
+ req->best_parent_rate = req->rate;
- return DIV_ROUND_UP(*parent_rate, div);
+ req->rate = DIV_ROUND_UP(req->best_parent_rate, div);
+ return 0;
}
static inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu,
@@ -602,7 +629,7 @@ static const struct clk_ops ingenic_clk_ops = {
.set_parent = ingenic_clk_set_parent,
.recalc_rate = ingenic_clk_recalc_rate,
- .round_rate = ingenic_clk_round_rate,
+ .determine_rate = ingenic_clk_determine_rate,
.set_rate = ingenic_clk_set_rate,
.enable = ingenic_clk_enable,
diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h
index 147b7df0d657..0d417d69dab7 100644
--- a/drivers/clk/ingenic/cgu.h
+++ b/drivers/clk/ingenic/cgu.h
@@ -33,7 +33,8 @@
* @od_shift: the number of bits to shift the post-VCO divider value by (ie.
* the index of the lowest bit of the post-VCO divider value in
* the PLL's control register)
- * @od_bits: the size of the post-VCO divider field in bits
+ * @od_bits: the size of the post-VCO divider field in bits, or 0 if no
+ * OD field exists (then the OD is fixed to 1)
* @od_max: the maximum post-VCO divider value
* @od_encoding: a pointer to an array mapping post-VCO divider values to
* their encoded values in the PLL control register, or -1 for
@@ -41,8 +42,12 @@
* @bypass_reg: the offset of the bypass control register within the CGU
* @bypass_bit: the index of the bypass bit in the PLL control register, or
* -1 if there is no bypass bit
- * @enable_bit: the index of the enable bit in the PLL control register
- * @stable_bit: the index of the stable bit in the PLL control register
+ * @enable_bit: the index of the enable bit in the PLL control register, or
+ * -1 if there is no enable bit (ie, the PLL is always on)
+ * @stable_bit: the index of the stable bit in the PLL control register, or
+ * -1 if there is no stable bit
+ * @set_rate_hook: hook called immediately after updating the CGU register,
+ * before releasing the spinlock
*/
struct ingenic_cgu_pll_info {
unsigned reg;
@@ -53,11 +58,13 @@ struct ingenic_cgu_pll_info {
u8 od_shift, od_bits, od_max;
unsigned bypass_reg;
s8 bypass_bit;
- u8 enable_bit;
- u8 stable_bit;
+ s8 enable_bit;
+ s8 stable_bit;
void (*calc_m_n_od)(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
unsigned int *m, unsigned int *n, unsigned int *od);
+ void (*set_rate_hook)(const struct ingenic_cgu_pll_info *pll_info,
+ unsigned long rate, unsigned long parent_rate);
};
/**
@@ -232,7 +239,7 @@ ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info,
*
* Register the clocks described by the CGU with the common clock framework.
*
- * Return: 0 on success or -errno if unsuccesful.
+ * Return: 0 on success or -errno if unsuccessful.
*/
int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu);
diff --git a/drivers/clk/ingenic/jz4725b-cgu.c b/drivers/clk/ingenic/jz4725b-cgu.c
index 590e9c85cb25..94cee44c854f 100644
--- a/drivers/clk/ingenic/jz4725b-cgu.c
+++ b/drivers/clk/ingenic/jz4725b-cgu.c
@@ -268,6 +268,6 @@ static void __init jz4725b_cgu_init(struct device_node *np)
if (retval)
pr_err("%s: failed to register CGU Clocks\n", __func__);
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-cgu", jz4725b_cgu_init);
diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c
index 3e0a30574ebb..2def3aedc8dd 100644
--- a/drivers/clk/ingenic/jz4740-cgu.c
+++ b/drivers/clk/ingenic/jz4740-cgu.c
@@ -266,6 +266,6 @@ static void __init jz4740_cgu_init(struct device_node *np)
if (retval)
pr_err("%s: failed to register CGU Clocks\n", __func__);
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);
diff --git a/drivers/clk/ingenic/jz4755-cgu.c b/drivers/clk/ingenic/jz4755-cgu.c
new file mode 100644
index 000000000000..17cf5dcaece9
--- /dev/null
+++ b/drivers/clk/ingenic/jz4755-cgu.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ingenic JZ4755 SoC CGU driver
+ * Heavily based on JZ4725b CGU driver
+ *
+ * Copyright (C) 2022 Siarhei Volkau
+ * Author: Siarhei Volkau <lis8215@gmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/ingenic,jz4755-cgu.h>
+
+#include "cgu.h"
+#include "pm.h"
+
+/* CGU register offsets */
+#define CGU_REG_CPCCR 0x00
+#define CGU_REG_CPPCR 0x10
+#define CGU_REG_CLKGR 0x20
+#define CGU_REG_OPCR 0x24
+#define CGU_REG_I2SCDR 0x60
+#define CGU_REG_LPCDR 0x64
+#define CGU_REG_MSCCDR 0x68
+#define CGU_REG_SSICDR 0x74
+#define CGU_REG_CIMCDR 0x7C
+
+static struct ingenic_cgu *cgu;
+
+static const s8 pll_od_encoding[4] = {
+ 0x0, 0x1, -1, 0x3,
+};
+
+static const u8 jz4755_cgu_cpccr_div_table[] = {
+ 1, 2, 3, 4, 6, 8,
+};
+
+static const u8 jz4755_cgu_pll_half_div_table[] = {
+ 2, 1,
+};
+
+static const struct ingenic_cgu_clk_info jz4755_cgu_clocks[] = {
+
+ /* External clocks */
+
+ [JZ4755_CLK_EXT] = { "ext", CGU_CLK_EXT },
+ [JZ4755_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
+
+ [JZ4755_CLK_PLL] = {
+ "pll", CGU_CLK_PLL,
+ .parents = { JZ4755_CLK_EXT, },
+ .pll = {
+ .reg = CGU_REG_CPPCR,
+ .rate_multiplier = 1,
+ .m_shift = 23,
+ .m_bits = 9,
+ .m_offset = 2,
+ .n_shift = 18,
+ .n_bits = 5,
+ .n_offset = 2,
+ .od_shift = 16,
+ .od_bits = 2,
+ .od_max = 4,
+ .od_encoding = pll_od_encoding,
+ .stable_bit = 10,
+ .bypass_reg = CGU_REG_CPPCR,
+ .bypass_bit = 9,
+ .enable_bit = 8,
+ },
+ },
+
+ /* Muxes & dividers */
+
+ [JZ4755_CLK_PLL_HALF] = {
+ "pll half", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, },
+ .div = {
+ CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,
+ jz4755_cgu_pll_half_div_table,
+ },
+ },
+
+ [JZ4755_CLK_EXT_HALF] = {
+ "ext half", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_EXT, },
+ .div = {
+ CGU_REG_CPCCR, 30, 1, 1, -1, -1, -1, 0,
+ NULL,
+ },
+ },
+
+ [JZ4755_CLK_CCLK] = {
+ "cclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, },
+ .div = {
+ CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_H0CLK] = {
+ "hclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, },
+ .div = {
+ CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_PCLK] = {
+ "pclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, },
+ .div = {
+ CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_MCLK] = {
+ "mclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, },
+ .div = {
+ CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_H1CLK] = {
+ "h1clk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, },
+ .div = {
+ CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_UDC] = {
+ "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, },
+ .mux = { CGU_REG_CPCCR, 29, 1 },
+ .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 10 },
+ },
+
+ [JZ4755_CLK_LCD] = {
+ "lcd", CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF, },
+ .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 9 },
+ },
+
+ [JZ4755_CLK_MMC] = {
+ "mmc", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL_HALF, },
+ .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
+ },
+
+ [JZ4755_CLK_I2S] = {
+ "i2s", CGU_CLK_MUX | CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, },
+ .mux = { CGU_REG_CPCCR, 31, 1 },
+ .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
+ },
+
+ [JZ4755_CLK_SPI] = {
+ "spi", CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF, },
+ .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 4 },
+ },
+
+ [JZ4755_CLK_TVE] = {
+ "tve", CGU_CLK_MUX | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_LCD, JZ4755_CLK_EXT, },
+ .mux = { CGU_REG_LPCDR, 31, 1 },
+ .gate = { CGU_REG_CLKGR, 18 },
+ },
+
+ [JZ4755_CLK_RTC] = {
+ "rtc", CGU_CLK_MUX | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT512, JZ4755_CLK_OSC32K, },
+ .mux = { CGU_REG_OPCR, 2, 1},
+ .gate = { CGU_REG_CLKGR, 2 },
+ },
+
+ [JZ4755_CLK_CIM] = {
+ "cim", CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF, },
+ .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 8 },
+ },
+
+ /* Gate-only clocks */
+
+ [JZ4755_CLK_UART0] = {
+ "uart0", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_CLKGR, 0 },
+ },
+
+ [JZ4755_CLK_UART1] = {
+ "uart1", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_CLKGR, 14 },
+ },
+
+ [JZ4755_CLK_UART2] = {
+ "uart2", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_CLKGR, 15 },
+ },
+
+ [JZ4755_CLK_ADC] = {
+ "adc", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_CLKGR, 7 },
+ },
+
+ [JZ4755_CLK_AIC] = {
+ "aic", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_CLKGR, 5 },
+ },
+
+ [JZ4755_CLK_I2C] = {
+ "i2c", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_CLKGR, 3 },
+ },
+
+ [JZ4755_CLK_BCH] = {
+ "bch", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 11 },
+ },
+
+ [JZ4755_CLK_TCU] = {
+ "tcu", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT, },
+ .gate = { CGU_REG_CLKGR, 1 },
+ },
+
+ [JZ4755_CLK_DMA] = {
+ "dma", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PCLK, },
+ .gate = { CGU_REG_CLKGR, 12 },
+ },
+
+ [JZ4755_CLK_MMC0] = {
+ "mmc0", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_MMC, },
+ .gate = { CGU_REG_CLKGR, 6 },
+ },
+
+ [JZ4755_CLK_MMC1] = {
+ "mmc1", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_MMC, },
+ .gate = { CGU_REG_CLKGR, 16 },
+ },
+
+ [JZ4755_CLK_AUX_CPU] = {
+ "aux_cpu", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 24 },
+ },
+
+ [JZ4755_CLK_AHB1] = {
+ "ahb1", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 23 },
+ },
+
+ [JZ4755_CLK_IDCT] = {
+ "idct", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 22 },
+ },
+
+ [JZ4755_CLK_DB] = {
+ "db", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 21 },
+ },
+
+ [JZ4755_CLK_ME] = {
+ "me", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 20 },
+ },
+
+ [JZ4755_CLK_MC] = {
+ "mc", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, },
+ .gate = { CGU_REG_CLKGR, 19 },
+ },
+
+ [JZ4755_CLK_TSSI] = {
+ "tssi", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF/* not sure */, },
+ .gate = { CGU_REG_CLKGR, 17 },
+ },
+
+ [JZ4755_CLK_IPU] = {
+ "ipu", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF/* not sure */, },
+ .gate = { CGU_REG_CLKGR, 13 },
+ },
+
+ [JZ4755_CLK_EXT512] = {
+ "ext/512", CGU_CLK_FIXDIV,
+ .parents = { JZ4755_CLK_EXT, },
+
+ .fixdiv = { 512 },
+ },
+
+ [JZ4755_CLK_UDC_PHY] = {
+ "udc_phy", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, },
+ .gate = { CGU_REG_OPCR, 6, true },
+ },
+};
+
+static void __init jz4755_cgu_init(struct device_node *np)
+{
+ int retval;
+
+ cgu = ingenic_cgu_new(jz4755_cgu_clocks,
+ ARRAY_SIZE(jz4755_cgu_clocks), np);
+ if (!cgu) {
+ pr_err("%s: failed to initialise CGU\n", __func__);
+ return;
+ }
+
+ retval = ingenic_cgu_register_clocks(cgu);
+ if (retval)
+ pr_err("%s: failed to register CGU Clocks\n", __func__);
+
+ ingenic_cgu_register_syscore(cgu);
+}
+/*
+ * CGU has some children devices, this is useful for probing children devices
+ * in the case where the device node is compatible with "simple-mfd".
+ */
+CLK_OF_DECLARE_DRIVER(jz4755_cgu, "ingenic,jz4755-cgu", jz4755_cgu_init);
diff --git a/drivers/clk/ingenic/jz4760-cgu.c b/drivers/clk/ingenic/jz4760-cgu.c
index ecd395ac8a28..372fe4b07992 100644
--- a/drivers/clk/ingenic/jz4760-cgu.c
+++ b/drivers/clk/ingenic/jz4760-cgu.c
@@ -58,7 +58,7 @@ jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
unsigned int *pm, unsigned int *pn, unsigned int *pod)
{
- unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 2;
+ unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1;
/* The frequency after the N divider must be between 1 and 50 MHz. */
n = parent_rate / (1 * MHZ);
@@ -66,19 +66,17 @@ jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
/* The N divider must be >= 2. */
n = clamp_val(n, 2, 1 << pll_info->n_bits);
- for (;; n >>= 1) {
- od = (unsigned int)-1;
+ rate /= MHZ;
+ parent_rate /= MHZ;
- do {
- m = (rate / MHZ) * (1 << ++od) * n / (parent_rate / MHZ);
- } while ((m > m_max || m & 1) && (od < 4));
-
- if (od < 4 && m >= 4 && m <= m_max)
- break;
+ for (m = m_max; m >= m_max && n >= 2; n--) {
+ m = rate * n / parent_rate;
+ od = m & 1;
+ m <<= od;
}
*pm = m;
- *pn = n;
+ *pn = n + 1;
*pod = 1 << od;
}
@@ -438,7 +436,7 @@ static void __init jz4760_cgu_init(struct device_node *np)
if (retval)
pr_err("%s: failed to register CGU Clocks\n", __func__);
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
/* We only probe via devicetree, no need for a platform driver */
diff --git a/drivers/clk/ingenic/jz4770-cgu.c b/drivers/clk/ingenic/jz4770-cgu.c
index 6ae1740367f9..58f1d3bad677 100644
--- a/drivers/clk/ingenic/jz4770-cgu.c
+++ b/drivers/clk/ingenic/jz4770-cgu.c
@@ -456,7 +456,7 @@ static void __init jz4770_cgu_init(struct device_node *np)
if (retval)
pr_err("%s: failed to register CGU Clocks\n", __func__);
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
/* We only probe via devicetree, no need for a platform driver */
diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c
index b1dadc0a5e75..1e88aef7ac0f 100644
--- a/drivers/clk/ingenic/jz4780-cgu.c
+++ b/drivers/clk/ingenic/jz4780-cgu.c
@@ -128,19 +128,19 @@ static unsigned long jz4780_otg_phy_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long jz4780_otg_phy_round_rate(struct clk_hw *hw, unsigned long req_rate,
- unsigned long *parent_rate)
+static int jz4780_otg_phy_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- if (req_rate < 15600000)
- return 12000000;
-
- if (req_rate < 21600000)
- return 19200000;
+ if (req->rate < 15600000)
+ req->rate = 12000000;
+ else if (req->rate < 21600000)
+ req->rate = 19200000;
+ else if (req->rate < 36000000)
+ req->rate = 24000000;
+ else
+ req->rate = 48000000;
- if (req_rate < 36000000)
- return 24000000;
-
- return 48000000;
+ return 0;
}
static int jz4780_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate,
@@ -212,7 +212,7 @@ static int jz4780_otg_phy_is_enabled(struct clk_hw *hw)
static const struct clk_ops jz4780_otg_phy_ops = {
.recalc_rate = jz4780_otg_phy_recalc_rate,
- .round_rate = jz4780_otg_phy_round_rate,
+ .determine_rate = jz4780_otg_phy_determine_rate,
.set_rate = jz4780_otg_phy_set_rate,
.enable = jz4780_otg_phy_enable,
@@ -803,6 +803,6 @@ static void __init jz4780_cgu_init(struct device_node *np)
return;
}
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
CLK_OF_DECLARE_DRIVER(jz4780_cgu, "ingenic,jz4780-cgu", jz4780_cgu_init);
diff --git a/drivers/clk/ingenic/pm.c b/drivers/clk/ingenic/pm.c
index 341752b640d2..206d5cf2872f 100644
--- a/drivers/clk/ingenic/pm.c
+++ b/drivers/clk/ingenic/pm.c
@@ -15,7 +15,7 @@
static void __iomem * __maybe_unused ingenic_cgu_base;
-static int __maybe_unused ingenic_cgu_pm_suspend(void)
+static int __maybe_unused ingenic_cgu_pm_suspend(void *data)
{
u32 val = readl(ingenic_cgu_base + CGU_REG_LCR);
@@ -24,22 +24,26 @@ static int __maybe_unused ingenic_cgu_pm_suspend(void)
return 0;
}
-static void __maybe_unused ingenic_cgu_pm_resume(void)
+static void __maybe_unused ingenic_cgu_pm_resume(void *data)
{
u32 val = readl(ingenic_cgu_base + CGU_REG_LCR);
writel(val & ~LCR_LOW_POWER_MODE, ingenic_cgu_base + CGU_REG_LCR);
}
-static struct syscore_ops __maybe_unused ingenic_cgu_pm_ops = {
+static const struct syscore_ops __maybe_unused ingenic_cgu_pm_ops = {
.suspend = ingenic_cgu_pm_suspend,
.resume = ingenic_cgu_pm_resume,
};
-void ingenic_cgu_register_syscore_ops(struct ingenic_cgu *cgu)
+static struct syscore __maybe_unused ingenic_cgu_pm = {
+ .ops = &ingenic_cgu_pm_ops,
+};
+
+void ingenic_cgu_register_syscore(struct ingenic_cgu *cgu)
{
if (IS_ENABLED(CONFIG_PM_SLEEP)) {
ingenic_cgu_base = cgu->base;
- register_syscore_ops(&ingenic_cgu_pm_ops);
+ register_syscore(&ingenic_cgu_pm);
}
}
diff --git a/drivers/clk/ingenic/pm.h b/drivers/clk/ingenic/pm.h
index fa7540407b6b..0dcb57dc64cb 100644
--- a/drivers/clk/ingenic/pm.h
+++ b/drivers/clk/ingenic/pm.h
@@ -7,6 +7,6 @@
struct ingenic_cgu;
-void ingenic_cgu_register_syscore_ops(struct ingenic_cgu *cgu);
+void ingenic_cgu_register_syscore(struct ingenic_cgu *cgu);
#endif /* DRIVERS_CLK_INGENIC_PM_H */
diff --git a/drivers/clk/ingenic/tcu.c b/drivers/clk/ingenic/tcu.c
index 201bf6e6b6e0..bc6a51da2072 100644
--- a/drivers/clk/ingenic/tcu.c
+++ b/drivers/clk/ingenic/tcu.c
@@ -101,15 +101,11 @@ static bool ingenic_tcu_enable_regs(struct clk_hw *hw)
bool enabled = false;
/*
- * If the SoC has no global TCU clock, we must ungate the channel's
- * clock to be able to access its registers.
- * If we have a TCU clock, it will be enabled automatically as it has
- * been attached to the regmap.
+ * According to the programming manual, a timer channel's registers can
+ * only be accessed when the channel's stop bit is clear.
*/
- if (!tcu->clk) {
- enabled = !!ingenic_tcu_is_enabled(hw);
- regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
- }
+ enabled = !!ingenic_tcu_is_enabled(hw);
+ regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));
return enabled;
}
@@ -120,8 +116,7 @@ static void ingenic_tcu_disable_regs(struct clk_hw *hw)
const struct ingenic_tcu_clk_info *info = tcu_clk->info;
struct ingenic_tcu *tcu = tcu_clk->tcu;
- if (!tcu->clk)
- regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
+ regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));
}
static u8 ingenic_tcu_get_parent(struct clk_hw *hw)
@@ -183,18 +178,21 @@ static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate)
return 5; /* /1024 divider */
}
-static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate,
- unsigned long *parent_rate)
+static int ingenic_tcu_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long rate = *parent_rate;
+ unsigned long rate = req->best_parent_rate;
u8 prescale;
- if (req_rate > rate)
- return rate;
+ if (req->rate > rate) {
+ req->rate = rate;
+ return 0;
+ }
- prescale = ingenic_tcu_get_prescale(rate, req_rate);
+ prescale = ingenic_tcu_get_prescale(rate, req->rate);
- return rate >> (prescale * 2);
+ req->rate = rate >> (prescale * 2);
+ return 0;
}
static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate,
@@ -224,7 +222,7 @@ static const struct clk_ops ingenic_tcu_clk_ops = {
.set_parent = ingenic_tcu_set_parent,
.recalc_rate = ingenic_tcu_recalc_rate,
- .round_rate = ingenic_tcu_round_rate,
+ .determine_rate = ingenic_tcu_determine_rate,
.set_rate = ingenic_tcu_set_rate,
.enable = ingenic_tcu_enable,
@@ -457,7 +455,7 @@ err_free_tcu:
return ret;
}
-static int __maybe_unused tcu_pm_suspend(void)
+static int __maybe_unused tcu_pm_suspend(void *data)
{
struct ingenic_tcu *tcu = ingenic_tcu;
@@ -467,7 +465,7 @@ static int __maybe_unused tcu_pm_suspend(void)
return 0;
}
-static void __maybe_unused tcu_pm_resume(void)
+static void __maybe_unused tcu_pm_resume(void *data)
{
struct ingenic_tcu *tcu = ingenic_tcu;
@@ -475,11 +473,15 @@ static void __maybe_unused tcu_pm_resume(void)
clk_enable(tcu->clk);
}
-static struct syscore_ops __maybe_unused tcu_pm_ops = {
+static const struct syscore_ops __maybe_unused tcu_pm_ops = {
.suspend = tcu_pm_suspend,
.resume = tcu_pm_resume,
};
+static struct syscore __maybe_unused tcu_pm = {
+ .ops = &tcu_pm_ops,
+};
+
static void __init ingenic_tcu_init(struct device_node *np)
{
int ret = ingenic_tcu_probe(np);
@@ -488,7 +490,7 @@ static void __init ingenic_tcu_init(struct device_node *np)
pr_crit("Failed to initialize TCU clocks: %d\n", ret);
if (IS_ENABLED(CONFIG_PM_SLEEP))
- register_syscore_ops(&tcu_pm_ops);
+ register_syscore(&tcu_pm);
}
CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);
diff --git a/drivers/clk/ingenic/x1000-cgu.c b/drivers/clk/ingenic/x1000-cgu.c
index b2ce3fb83f54..d89bdfb7c219 100644
--- a/drivers/clk/ingenic/x1000-cgu.c
+++ b/drivers/clk/ingenic/x1000-cgu.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/rational.h>
#include <dt-bindings/clock/ingenic,x1000-cgu.h>
@@ -83,16 +84,17 @@ static unsigned long x1000_otg_phy_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long x1000_otg_phy_round_rate(struct clk_hw *hw, unsigned long req_rate,
- unsigned long *parent_rate)
+static int x1000_otg_phy_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- if (req_rate < 18000000)
- return 12000000;
-
- if (req_rate < 36000000)
- return 24000000;
+ if (req->rate < 18000000)
+ req->rate = 12000000;
+ else if (req->rate < 36000000)
+ req->rate = 24000000;
+ else
+ req->rate = 48000000;
- return 48000000;
+ return 0;
}
static int x1000_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate,
@@ -160,7 +162,7 @@ static int x1000_usb_phy_is_enabled(struct clk_hw *hw)
static const struct clk_ops x1000_otg_phy_ops = {
.recalc_rate = x1000_otg_phy_recalc_rate,
- .round_rate = x1000_otg_phy_round_rate,
+ .determine_rate = x1000_otg_phy_determine_rate,
.set_rate = x1000_otg_phy_set_rate,
.enable = x1000_usb_phy_enable,
@@ -168,6 +170,38 @@ static const struct clk_ops x1000_otg_phy_ops = {
.is_enabled = x1000_usb_phy_is_enabled,
};
+static void
+x1000_i2spll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
+ unsigned long rate, unsigned long parent_rate,
+ unsigned int *pm, unsigned int *pn, unsigned int *pod)
+{
+ const unsigned long m_max = GENMASK(pll_info->m_bits - 1, 0);
+ const unsigned long n_max = GENMASK(pll_info->n_bits - 1, 0);
+ unsigned long m, n;
+
+ rational_best_approximation(rate, parent_rate, m_max, n_max, &m, &n);
+
+ /* n should not be less than 2*m */
+ if (n < 2 * m)
+ n = 2 * m;
+
+ *pm = m;
+ *pn = n;
+ *pod = 1;
+}
+
+static void
+x1000_i2spll_set_rate_hook(const struct ingenic_cgu_pll_info *pll_info,
+ unsigned long rate, unsigned long parent_rate)
+{
+ /*
+ * Writing 0 causes I2SCDR1.I2SDIV_D to be automatically recalculated
+ * based on the current value of I2SCDR.I2SDIV_N, which is needed for
+ * the divider to function correctly.
+ */
+ writel(0, cgu->base + CGU_REG_I2SCDR1);
+}
+
static const s8 pll_od_encoding[8] = {
0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
};
@@ -183,7 +217,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_APLL] = {
"apll", CGU_CLK_PLL,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.pll = {
.reg = CGU_REG_APLL,
.rate_multiplier = 1,
@@ -206,7 +240,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_MPLL] = {
"mpll", CGU_CLK_PLL,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.pll = {
.reg = CGU_REG_MPLL,
.rate_multiplier = 1,
@@ -256,7 +290,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
* system; mark it critical.
*/
.flags = CLK_IS_CRITICAL,
- .parents = { X1000_CLK_CPUMUX, -1, -1, -1 },
+ .parents = { X1000_CLK_CPUMUX },
.div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 },
.gate = { CGU_REG_CLKGR, 30 },
},
@@ -268,7 +302,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
* disabling it or any parent clocks will hang the system.
*/
.flags = CLK_IS_CRITICAL,
- .parents = { X1000_CLK_CPUMUX, -1, -1, -1 },
+ .parents = { X1000_CLK_CPUMUX },
.div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 },
},
@@ -287,13 +321,13 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_AHB2] = {
"ahb2", CGU_CLK_DIV,
- .parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 },
+ .parents = { X1000_CLK_AHB2PMUX },
.div = { CGU_REG_CPCCR, 12, 1, 4, 20, -1, -1 },
},
[X1000_CLK_PCLK] = {
"pclk", CGU_CLK_DIV | CGU_CLK_GATE,
- .parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 },
+ .parents = { X1000_CLK_AHB2PMUX },
.div = { CGU_REG_CPCCR, 16, 1, 4, 20, -1, -1 },
.gate = { CGU_REG_CLKGR, 28 },
},
@@ -319,6 +353,37 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
.gate = { CGU_REG_CLKGR, 25 },
},
+ [X1000_CLK_I2SPLLMUX] = {
+ "i2s_pll_mux", CGU_CLK_MUX,
+ .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
+ .mux = { CGU_REG_I2SCDR, 31, 1 },
+ },
+
+ [X1000_CLK_I2SPLL] = {
+ "i2s_pll", CGU_CLK_PLL,
+ .parents = { X1000_CLK_I2SPLLMUX },
+ .pll = {
+ .reg = CGU_REG_I2SCDR,
+ .rate_multiplier = 1,
+ .m_shift = 13,
+ .m_bits = 9,
+ .n_shift = 0,
+ .n_bits = 13,
+ .calc_m_n_od = x1000_i2spll_calc_m_n_od,
+ .set_rate_hook = x1000_i2spll_set_rate_hook,
+ },
+ },
+
+ [X1000_CLK_I2S] = {
+ "i2s", CGU_CLK_MUX,
+ .parents = { X1000_CLK_EXCLK, -1, -1, X1000_CLK_I2SPLL },
+ /*
+ * NOTE: the mux is at bit 30; bit 29 enables the M/N divider.
+ * Therefore, the divider is disabled when EXCLK is selected.
+ */
+ .mux = { CGU_REG_I2SCDR, 29, 2 },
+ },
+
[X1000_CLK_LCD] = {
"lcd", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
@@ -329,13 +394,13 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_MSCMUX] = {
"msc_mux", CGU_CLK_MUX,
- .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL},
+ .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
.mux = { CGU_REG_MSC0CDR, 31, 1 },
},
[X1000_CLK_MSC0] = {
"msc0", CGU_CLK_DIV | CGU_CLK_GATE,
- .parents = { X1000_CLK_MSCMUX, -1, -1, -1 },
+ .parents = { X1000_CLK_MSCMUX },
.div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR, 4 },
},
@@ -349,8 +414,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_OTG] = {
"otg", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
- .parents = { X1000_CLK_EXCLK, -1,
- X1000_CLK_APLL, X1000_CLK_MPLL },
+ .parents = { X1000_CLK_EXCLK, -1, X1000_CLK_APLL, X1000_CLK_MPLL },
.mux = { CGU_REG_USBCDR, 30, 2 },
.div = { CGU_REG_USBCDR, 0, 1, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR, 3 },
@@ -358,7 +422,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_SSIPLL] = {
"ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV,
- .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL, -1, -1 },
+ .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
.mux = { CGU_REG_SSICDR, 31, 1 },
.div = { CGU_REG_SSICDR, 0, 1, 8, 29, 28, 27 },
},
@@ -371,7 +435,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_SSIMUX] = {
"ssi_mux", CGU_CLK_MUX,
- .parents = { X1000_CLK_EXCLK, X1000_CLK_SSIPLL_DIV2, -1, -1 },
+ .parents = { X1000_CLK_EXCLK, X1000_CLK_SSIPLL_DIV2 },
.mux = { CGU_REG_SSICDR, 30, 1 },
},
@@ -392,79 +456,85 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_EMC] = {
"emc", CGU_CLK_GATE,
- .parents = { X1000_CLK_AHB2, -1, -1, -1 },
+ .parents = { X1000_CLK_AHB2 },
.gate = { CGU_REG_CLKGR, 0 },
},
[X1000_CLK_EFUSE] = {
"efuse", CGU_CLK_GATE,
- .parents = { X1000_CLK_AHB2, -1, -1, -1 },
+ .parents = { X1000_CLK_AHB2 },
.gate = { CGU_REG_CLKGR, 1 },
},
[X1000_CLK_SFC] = {
"sfc", CGU_CLK_GATE,
- .parents = { X1000_CLK_SSIPLL, -1, -1, -1 },
+ .parents = { X1000_CLK_SSIPLL },
.gate = { CGU_REG_CLKGR, 2 },
},
[X1000_CLK_I2C0] = {
"i2c0", CGU_CLK_GATE,
- .parents = { X1000_CLK_PCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_PCLK },
.gate = { CGU_REG_CLKGR, 7 },
},
[X1000_CLK_I2C1] = {
"i2c1", CGU_CLK_GATE,
- .parents = { X1000_CLK_PCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_PCLK },
.gate = { CGU_REG_CLKGR, 8 },
},
[X1000_CLK_I2C2] = {
"i2c2", CGU_CLK_GATE,
- .parents = { X1000_CLK_PCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_PCLK },
.gate = { CGU_REG_CLKGR, 9 },
},
+ [X1000_CLK_AIC] = {
+ "aic", CGU_CLK_GATE,
+ .parents = { X1000_CLK_EXCLK },
+ .gate = { CGU_REG_CLKGR, 11 },
+ },
+
[X1000_CLK_UART0] = {
"uart0", CGU_CLK_GATE,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 14 },
},
[X1000_CLK_UART1] = {
"uart1", CGU_CLK_GATE,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK},
.gate = { CGU_REG_CLKGR, 15 },
},
[X1000_CLK_UART2] = {
"uart2", CGU_CLK_GATE,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 16 },
},
[X1000_CLK_TCU] = {
"tcu", CGU_CLK_GATE,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 18 },
},
[X1000_CLK_SSI] = {
"ssi", CGU_CLK_GATE,
- .parents = { X1000_CLK_SSIMUX, -1, -1, -1 },
+ .parents = { X1000_CLK_SSIMUX },
.gate = { CGU_REG_CLKGR, 19 },
},
[X1000_CLK_OST] = {
"ost", CGU_CLK_GATE,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 20 },
},
[X1000_CLK_PDMA] = {
"pdma", CGU_CLK_GATE,
- .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+ .parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 21 },
},
};
@@ -486,7 +556,7 @@ static void __init x1000_cgu_init(struct device_node *np)
return;
}
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
/*
* CGU has some children devices, this is useful for probing children devices
diff --git a/drivers/clk/ingenic/x1830-cgu.c b/drivers/clk/ingenic/x1830-cgu.c
index 0fd46e50a513..acf856e5009e 100644
--- a/drivers/clk/ingenic/x1830-cgu.c
+++ b/drivers/clk/ingenic/x1830-cgu.c
@@ -463,7 +463,7 @@ static void __init x1830_cgu_init(struct device_node *np)
return;
}
- ingenic_cgu_register_syscore_ops(cgu);
+ ingenic_cgu_register_syscore(cgu);
}
/*
* CGU has some children devices, this is useful for probing children devices
diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c
index d59a7621bb20..6bbdd4705d71 100644
--- a/drivers/clk/keystone/pll.c
+++ b/drivers/clk/keystone/pll.c
@@ -209,7 +209,7 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl)
}
clk = clk_register_pll(NULL, node->name, parent_name, pll_data);
- if (clk) {
+ if (!IS_ERR_OR_NULL(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
return;
}
@@ -281,12 +281,13 @@ static void __init of_pll_div_clk_init(struct device_node *node)
clk = clk_register_divider(NULL, clk_name, parent_name, 0, reg, shift,
mask, 0, NULL);
- if (clk) {
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
- } else {
+ if (IS_ERR(clk)) {
pr_err("%s: error registering divider %s\n", __func__, clk_name);
iounmap(reg);
+ return;
}
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
CLK_OF_DECLARE(pll_divider_clock, "ti,keystone,pll-divider-clock", of_pll_div_clk_init);
@@ -328,10 +329,12 @@ static void __init of_pll_mux_clk_init(struct device_node *node)
clk = clk_register_mux(NULL, clk_name, (const char **)&parents,
ARRAY_SIZE(parents) , 0, reg, shift, mask,
0, NULL);
- if (clk)
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
- else
+ if (IS_ERR(clk)) {
pr_err("%s: error registering mux %s\n", __func__, clk_name);
+ return;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
CLK_OF_DECLARE(pll_mux_clock, "ti,keystone,pll-mux-clock", of_pll_mux_clk_init);
diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c
index d4b4e74e22da..9d5071223f4c 100644
--- a/drivers/clk/keystone/sci-clk.c
+++ b/drivers/clk/keystone/sci-clk.c
@@ -9,8 +9,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/soc/ti/ti_sci_protocol.h>
@@ -273,7 +272,7 @@ static const struct clk_ops sci_clk_ops = {
};
/**
- * _sci_clk_get - Gets a handle for an SCI clock
+ * _sci_clk_build - Gets a handle for an SCI clock
* @provider: Handle to SCI clock provider
* @sci_clk: Handle to the SCI clock to populate
*
@@ -294,6 +293,8 @@ static int _sci_clk_build(struct sci_clk_provider *provider,
name = kasprintf(GFP_KERNEL, "clk:%d:%d", sci_clk->dev_id,
sci_clk->clk_id);
+ if (!name)
+ return -ENOMEM;
init.name = name;
@@ -479,13 +480,10 @@ static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider)
num_clks++;
}
- provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk),
- GFP_KERNEL);
+ provider->clocks = devm_kmemdup_array(dev, clks, num_clks, sizeof(sci_clk), GFP_KERNEL);
if (!provider->clocks)
return -ENOMEM;
- memcpy(provider->clocks, clks, num_clks * sizeof(sci_clk));
-
provider->num_clocks = num_clks;
devm_kfree(dev, clks);
@@ -498,8 +496,8 @@ static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider)
static int _cmp_sci_clk_list(void *priv, const struct list_head *a,
const struct list_head *b)
{
- struct sci_clk *ca = container_of(a, struct sci_clk, node);
- struct sci_clk *cb = container_of(b, struct sci_clk, node);
+ const struct sci_clk *ca = container_of(a, struct sci_clk, node);
+ const struct sci_clk *cb = container_of(b, struct sci_clk, node);
return _cmp_sci_clk(ca, &cb);
}
@@ -515,6 +513,7 @@ static int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider)
struct sci_clk *sci_clk, *prev;
int num_clks = 0;
int num_parents;
+ bool state;
int clk_id;
const char * const clk_names[] = {
"clocks", "assigned-clocks", "assigned-clock-parents", NULL
@@ -585,6 +584,15 @@ static int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider)
clk_id = args.args[1] + 1;
while (num_parents--) {
+ /* Check if this clock id is valid */
+ ret = provider->ops->is_auto(provider->sci,
+ sci_clk->dev_id, clk_id, &state);
+
+ if (ret) {
+ clk_id++;
+ continue;
+ }
+
sci_clk = devm_kzalloc(dev,
sizeof(*sci_clk),
GFP_KERNEL);
@@ -689,11 +697,9 @@ static int ti_sci_clk_probe(struct platform_device *pdev)
* via common clock framework. Any memory allocated for the device will
* be free'd silently via the devm framework. Returns 0 always.
*/
-static int ti_sci_clk_remove(struct platform_device *pdev)
+static void ti_sci_clk_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
-
- return 0;
}
static struct platform_driver ti_sci_clk_driver = {
diff --git a/drivers/clk/keystone/syscon-clk.c b/drivers/clk/keystone/syscon-clk.c
index 19198325b909..ecf180a7949c 100644
--- a/drivers/clk/keystone/syscon-clk.c
+++ b/drivers/clk/keystone/syscon-clk.c
@@ -4,10 +4,12 @@
*/
#include <linux/clk-provider.h>
+#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
struct ti_syscon_gate_clk_priv {
struct clk_hw hw;
@@ -61,21 +63,31 @@ static const struct clk_ops ti_syscon_gate_clk_ops = {
static struct clk_hw
*ti_syscon_gate_clk_register(struct device *dev, struct regmap *regmap,
+ const char *parent_name,
const struct ti_syscon_gate_clk_data *data)
{
struct ti_syscon_gate_clk_priv *priv;
struct clk_init_data init;
+ char *name = NULL;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return ERR_PTR(-ENOMEM);
- init.name = data->name;
init.ops = &ti_syscon_gate_clk_ops;
- init.parent_names = NULL;
- init.num_parents = 0;
- init.flags = 0;
+ if (parent_name) {
+ name = kasprintf(GFP_KERNEL, "%s:%s", data->name, parent_name);
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+ } else {
+ init.name = data->name;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = 0;
+ }
priv->regmap = regmap;
priv->reg = data->offset;
@@ -83,36 +95,56 @@ static struct clk_hw
priv->hw.init = &init;
ret = devm_clk_hw_register(dev, &priv->hw);
+
+ if (name)
+ kfree(init.name);
+
if (ret)
return ERR_PTR(ret);
return &priv->hw;
}
+static const struct regmap_config ti_syscon_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
static int ti_syscon_gate_clk_probe(struct platform_device *pdev)
{
const struct ti_syscon_gate_clk_data *data, *p;
struct clk_hw_onecell_data *hw_data;
struct device *dev = &pdev->dev;
+ int num_clks, num_parents, i;
+ const char *parent_name;
struct regmap *regmap;
- int num_clks, i;
+ void __iomem *base;
data = device_get_match_data(dev);
if (!data)
return -EINVAL;
- regmap = syscon_node_to_regmap(dev->of_node);
- if (IS_ERR(regmap)) {
- if (PTR_ERR(regmap) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- dev_err(dev, "failed to find parent regmap\n");
- return PTR_ERR(regmap);
- }
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, &ti_syscon_regmap_cfg);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to get regmap\n");
num_clks = 0;
for (p = data; p->name; p++)
num_clks++;
+ num_parents = of_clk_get_parent_count(dev->of_node);
+ if (of_device_is_compatible(dev->of_node, "ti,am62-audio-refclk") &&
+ num_parents == 0) {
+ return dev_err_probe(dev, -EINVAL,
+ "must specify a parent clock\n");
+ }
+
hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks),
GFP_KERNEL);
if (!hw_data)
@@ -120,16 +152,20 @@ static int ti_syscon_gate_clk_probe(struct platform_device *pdev)
hw_data->num = num_clks;
+ parent_name = of_clk_get_parent_name(dev->of_node, 0);
for (i = 0; i < num_clks; i++) {
hw_data->hws[i] = ti_syscon_gate_clk_register(dev, regmap,
+ parent_name,
&data[i]);
if (IS_ERR(hw_data->hws[i]))
dev_warn(dev, "failed to register %s\n",
data[i].name);
}
- return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- hw_data);
+ if (num_clks == 1)
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ hw_data->hws[0]);
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
}
#define TI_SYSCON_CLK_GATE(_name, _offset, _bit_idx) \
@@ -169,6 +205,11 @@ static const struct ti_syscon_gate_clk_data am62_clk_data[] = {
{ /* Sentinel */ },
};
+static const struct ti_syscon_gate_clk_data am62_audio_clk_data[] = {
+ TI_SYSCON_CLK_GATE("audio_refclk", 0x0, 15),
+ { /* Sentinel */ },
+};
+
static const struct of_device_id ti_syscon_gate_clk_ids[] = {
{
.compatible = "ti,am654-ehrpwm-tbclk",
@@ -182,6 +223,10 @@ static const struct of_device_id ti_syscon_gate_clk_ids[] = {
.compatible = "ti,am62-epwm-tbclk",
.data = &am62_clk_data,
},
+ {
+ .compatible = "ti,am62-audio-refclk",
+ .data = &am62_audio_clk_data,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids);
diff --git a/drivers/clk/kunit_clk_assigned_rates.h b/drivers/clk/kunit_clk_assigned_rates.h
new file mode 100644
index 000000000000..df2d84dcaa93
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _KUNIT_CLK_ASSIGNED_RATES_H
+#define _KUNIT_CLK_ASSIGNED_RATES_H
+
+#define ASSIGNED_RATES_0_RATE 1600000
+#define ASSIGNED_RATES_1_RATE 9700000
+
+#endif
diff --git a/drivers/clk/kunit_clk_assigned_rates_multiple.dtso b/drivers/clk/kunit_clk_assigned_rates_multiple.dtso
new file mode 100644
index 000000000000..e600736e70f5
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_multiple.dtso
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <1>;
+ assigned-clocks = <&clk 0>,
+ <&clk 1>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>,
+ <ASSIGNED_RATES_1_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_multiple_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_multiple_consumer.dtso
new file mode 100644
index 000000000000..260aba458daf
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_multiple_consumer.dtso
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <1>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clocks = <&clk 0>,
+ <&clk 1>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>,
+ <ASSIGNED_RATES_1_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_null.dtso b/drivers/clk/kunit_clk_assigned_rates_null.dtso
new file mode 100644
index 000000000000..0b27b38a9130
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_null.dtso
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ assigned-clocks = <0>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_null_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_null_consumer.dtso
new file mode 100644
index 000000000000..99fb332ae83d
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_null_consumer.dtso
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clocks = <0>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_one.dtso b/drivers/clk/kunit_clk_assigned_rates_one.dtso
new file mode 100644
index 000000000000..dd95ec9b1cf9
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_one.dtso
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ assigned-clocks = <&clk>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_one_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_one_consumer.dtso
new file mode 100644
index 000000000000..a41dca806318
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_one_consumer.dtso
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clocks = <&clk>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso
new file mode 100644
index 000000000000..389b4e2eb7f7
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <1>;
+ assigned-clocks = <&clk 0>,
+ <&clk 1>;
+ assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>,
+ /bits/ 64 <ASSIGNED_RATES_1_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso
new file mode 100644
index 000000000000..3e117fd59b7d
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <1>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clocks = <&clk 0>,
+ <&clk 1>;
+ assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>,
+ /bits/ 64 <ASSIGNED_RATES_1_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso
new file mode 100644
index 000000000000..87041264e8f5
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ assigned-clocks = <&clk>;
+ assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso
new file mode 100644
index 000000000000..3259c003aec0
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clocks = <&clk>;
+ assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_without.dtso b/drivers/clk/kunit_clk_assigned_rates_without.dtso
new file mode 100644
index 000000000000..22d333495cf2
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_without.dtso
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_without_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_without_consumer.dtso
new file mode 100644
index 000000000000..75ac09140f83
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_without_consumer.dtso
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_assigned_rates.h"
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clock-rates = <ASSIGNED_RATES_0_RATE>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_zero.dtso b/drivers/clk/kunit_clk_assigned_rates_zero.dtso
new file mode 100644
index 000000000000..08e042c2eafe
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_zero.dtso
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ assigned-clocks = <&clk>;
+ assigned-clock-rates = <0>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_assigned_rates_zero_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_zero_consumer.dtso
new file mode 100644
index 000000000000..1d964672e855
--- /dev/null
+++ b/drivers/clk/kunit_clk_assigned_rates_zero_consumer.dtso
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ clk: kunit-clock {
+ compatible = "test,clk-assigned-rates";
+ #clock-cells = <0>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,clk-consumer";
+ assigned-clocks = <&clk>;
+ assigned-clock-rates = <0>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_fixed_rate_test.dtso b/drivers/clk/kunit_clk_fixed_rate_test.dtso
new file mode 100644
index 000000000000..d838ce766fa2
--- /dev/null
+++ b/drivers/clk/kunit_clk_fixed_rate_test.dtso
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "clk-fixed-rate_test.h"
+
+&{/} {
+ fixed_50MHz: kunit-clock {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <TEST_FIXED_FREQUENCY>;
+ clock-accuracy = <TEST_FIXED_ACCURACY>;
+ };
+
+ kunit-clock-consumer {
+ compatible = "test,single-clk-consumer";
+ clocks = <&fixed_50MHz>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_hw_get_dev_of_node.dtso b/drivers/clk/kunit_clk_hw_get_dev_of_node.dtso
new file mode 100644
index 000000000000..760717da3235
--- /dev/null
+++ b/drivers/clk/kunit_clk_hw_get_dev_of_node.dtso
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ kunit-clock-controller {
+ compatible = "test,clk-hw-get-dev-of-node";
+ #clock-cells = <0>;
+ };
+};
diff --git a/drivers/clk/kunit_clk_parent_data_test.dtso b/drivers/clk/kunit_clk_parent_data_test.dtso
new file mode 100644
index 000000000000..7d3ed9a5a2e8
--- /dev/null
+++ b/drivers/clk/kunit_clk_parent_data_test.dtso
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "clk_parent_data_test.h"
+
+&{/} {
+ fixed_50: kunit-clock-50MHz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <50000000>;
+ clock-output-names = CLK_PARENT_DATA_50MHZ_NAME;
+ };
+
+ fixed_parent: kunit-clock-1MHz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <1000000>;
+ clock-output-names = CLK_PARENT_DATA_1MHZ_NAME;
+ };
+
+ kunit-clock-controller {
+ compatible = "test,clk-parent-data";
+ clocks = <&fixed_parent>, <&fixed_50>;
+ clock-names = CLK_PARENT_DATA_PARENT1, CLK_PARENT_DATA_PARENT2;
+ #clock-cells = <1>;
+ };
+};
diff --git a/drivers/clk/loongson1/Makefile b/drivers/clk/loongson1/Makefile
deleted file mode 100644
index 251d0fe9dcd1..000000000000
--- a/drivers/clk/loongson1/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-y += clk.o
-obj-$(CONFIG_LOONGSON1_LS1B) += clk-loongson1b.o
-obj-$(CONFIG_LOONGSON1_LS1C) += clk-loongson1c.o
diff --git a/drivers/clk/loongson1/clk-loongson1b.c b/drivers/clk/loongson1/clk-loongson1b.c
deleted file mode 100644
index 13a2ca23a159..000000000000
--- a/drivers/clk/loongson1/clk-loongson1b.c
+++ /dev/null
@@ -1,118 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
- */
-
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/io.h>
-#include <linux/err.h>
-
-#include <loongson1.h>
-#include "clk.h"
-
-#define OSC (33 * 1000000)
-#define DIV_APB 2
-
-static DEFINE_SPINLOCK(_lock);
-
-static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- u32 pll, rate;
-
- pll = __raw_readl(LS1X_CLK_PLL_FREQ);
- rate = 12 + (pll & GENMASK(5, 0));
- rate *= OSC;
- rate >>= 1;
-
- return rate;
-}
-
-static const struct clk_ops ls1x_pll_clk_ops = {
- .recalc_rate = ls1x_pll_recalc_rate,
-};
-
-static const char *const cpu_parents[] = { "cpu_clk_div", "osc_clk", };
-static const char *const ahb_parents[] = { "ahb_clk_div", "osc_clk", };
-static const char *const dc_parents[] = { "dc_clk_div", "osc_clk", };
-
-void __init ls1x_clk_init(void)
-{
- struct clk_hw *hw;
-
- hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC);
- clk_hw_register_clkdev(hw, "osc_clk", NULL);
-
- /* clock derived from 33 MHz OSC clk */
- hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk",
- &ls1x_pll_clk_ops, 0);
- clk_hw_register_clkdev(hw, "pll_clk", NULL);
-
- /* clock derived from PLL clk */
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ CPU CLK
- * \___ PLL ___ CPU DIV ___| |
- * |_____|
- */
- hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk",
- CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
- DIV_CPU_SHIFT, DIV_CPU_WIDTH,
- CLK_DIVIDER_ONE_BASED |
- CLK_DIVIDER_ROUND_CLOSEST, &_lock);
- clk_hw_register_clkdev(hw, "cpu_clk_div", NULL);
- hw = clk_hw_register_mux(NULL, "cpu_clk", cpu_parents,
- ARRAY_SIZE(cpu_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock);
- clk_hw_register_clkdev(hw, "cpu_clk", NULL);
-
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ DC CLK
- * \___ PLL ___ DC DIV ___| |
- * |_____|
- */
- hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
- DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
- clk_hw_register_clkdev(hw, "dc_clk_div", NULL);
- hw = clk_hw_register_mux(NULL, "dc_clk", dc_parents,
- ARRAY_SIZE(dc_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock);
- clk_hw_register_clkdev(hw, "dc_clk", NULL);
-
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ DDR CLK
- * \___ PLL ___ DDR DIV ___| |
- * |_____|
- */
- hw = clk_hw_register_divider(NULL, "ahb_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
- DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED,
- &_lock);
- clk_hw_register_clkdev(hw, "ahb_clk_div", NULL);
- hw = clk_hw_register_mux(NULL, "ahb_clk", ahb_parents,
- ARRAY_SIZE(ahb_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock);
- clk_hw_register_clkdev(hw, "ahb_clk", NULL);
- clk_hw_register_clkdev(hw, "ls1x-dma", NULL);
- clk_hw_register_clkdev(hw, "stmmaceth", NULL);
-
- /* clock derived from AHB clk */
- /* APB clk is always half of the AHB clk */
- hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
- DIV_APB);
- clk_hw_register_clkdev(hw, "apb_clk", NULL);
- clk_hw_register_clkdev(hw, "ls1x-ac97", NULL);
- clk_hw_register_clkdev(hw, "ls1x-i2c", NULL);
- clk_hw_register_clkdev(hw, "ls1x-nand", NULL);
- clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL);
- clk_hw_register_clkdev(hw, "ls1x-spi", NULL);
- clk_hw_register_clkdev(hw, "ls1x-wdt", NULL);
- clk_hw_register_clkdev(hw, "serial8250", NULL);
-}
diff --git a/drivers/clk/loongson1/clk-loongson1c.c b/drivers/clk/loongson1/clk-loongson1c.c
deleted file mode 100644
index 1ebf740380ef..000000000000
--- a/drivers/clk/loongson1/clk-loongson1c.c
+++ /dev/null
@@ -1,95 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
- */
-
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/io.h>
-
-#include <loongson1.h>
-#include "clk.h"
-
-#define OSC (24 * 1000000)
-#define DIV_APB 1
-
-static DEFINE_SPINLOCK(_lock);
-
-static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- u32 pll, rate;
-
- pll = __raw_readl(LS1X_CLK_PLL_FREQ);
- rate = ((pll >> 8) & 0xff) + ((pll >> 16) & 0xff);
- rate *= OSC;
- rate >>= 2;
-
- return rate;
-}
-
-static const struct clk_ops ls1x_pll_clk_ops = {
- .recalc_rate = ls1x_pll_recalc_rate,
-};
-
-static const struct clk_div_table ahb_div_table[] = {
- [0] = { .val = 0, .div = 2 },
- [1] = { .val = 1, .div = 4 },
- [2] = { .val = 2, .div = 3 },
- [3] = { .val = 3, .div = 3 },
- [4] = { /* sentinel */ }
-};
-
-void __init ls1x_clk_init(void)
-{
- struct clk_hw *hw;
-
- hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC);
- clk_hw_register_clkdev(hw, "osc_clk", NULL);
-
- /* clock derived from 24 MHz OSC clk */
- hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk",
- &ls1x_pll_clk_ops, 0);
- clk_hw_register_clkdev(hw, "pll_clk", NULL);
-
- hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk",
- CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
- DIV_CPU_SHIFT, DIV_CPU_WIDTH,
- CLK_DIVIDER_ONE_BASED |
- CLK_DIVIDER_ROUND_CLOSEST, &_lock);
- clk_hw_register_clkdev(hw, "cpu_clk_div", NULL);
- hw = clk_hw_register_fixed_factor(NULL, "cpu_clk", "cpu_clk_div",
- 0, 1, 1);
- clk_hw_register_clkdev(hw, "cpu_clk", NULL);
-
- hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
- DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
- clk_hw_register_clkdev(hw, "dc_clk_div", NULL);
- hw = clk_hw_register_fixed_factor(NULL, "dc_clk", "dc_clk_div",
- 0, 1, 1);
- clk_hw_register_clkdev(hw, "dc_clk", NULL);
-
- hw = clk_hw_register_divider_table(NULL, "ahb_clk_div", "cpu_clk_div",
- 0, LS1X_CLK_PLL_FREQ, DIV_DDR_SHIFT,
- DIV_DDR_WIDTH, CLK_DIVIDER_ALLOW_ZERO,
- ahb_div_table, &_lock);
- clk_hw_register_clkdev(hw, "ahb_clk_div", NULL);
- hw = clk_hw_register_fixed_factor(NULL, "ahb_clk", "ahb_clk_div",
- 0, 1, 1);
- clk_hw_register_clkdev(hw, "ahb_clk", NULL);
- clk_hw_register_clkdev(hw, "ls1x-dma", NULL);
- clk_hw_register_clkdev(hw, "stmmaceth", NULL);
-
- /* clock derived from AHB clk */
- hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
- DIV_APB);
- clk_hw_register_clkdev(hw, "apb_clk", NULL);
- clk_hw_register_clkdev(hw, "ls1x-ac97", NULL);
- clk_hw_register_clkdev(hw, "ls1x-i2c", NULL);
- clk_hw_register_clkdev(hw, "ls1x-nand", NULL);
- clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL);
- clk_hw_register_clkdev(hw, "ls1x-spi", NULL);
- clk_hw_register_clkdev(hw, "ls1x-wdt", NULL);
- clk_hw_register_clkdev(hw, "serial8250", NULL);
-}
diff --git a/drivers/clk/loongson1/clk.c b/drivers/clk/loongson1/clk.c
deleted file mode 100644
index f336a3126d31..000000000000
--- a/drivers/clk/loongson1/clk.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/slab.h>
-
-#include "clk.h"
-
-struct clk_hw *__init clk_hw_register_pll(struct device *dev,
- const char *name,
- const char *parent_name,
- const struct clk_ops *ops,
- unsigned long flags)
-{
- int ret;
- struct clk_hw *hw;
- struct clk_init_data init;
-
- /* allocate the divider */
- hw = kzalloc(sizeof(*hw), GFP_KERNEL);
- if (!hw)
- return ERR_PTR(-ENOMEM);
-
- init.name = name;
- init.ops = ops;
- init.flags = flags;
- init.parent_names = parent_name ? &parent_name : NULL;
- init.num_parents = parent_name ? 1 : 0;
- hw->init = &init;
-
- /* register the clock */
- ret = clk_hw_register(dev, hw);
- if (ret) {
- kfree(hw);
- hw = ERR_PTR(ret);
- }
-
- return hw;
-}
diff --git a/drivers/clk/loongson1/clk.h b/drivers/clk/loongson1/clk.h
deleted file mode 100644
index 124642302b12..000000000000
--- a/drivers/clk/loongson1/clk.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
- */
-
-#ifndef __LOONGSON1_CLK_H
-#define __LOONGSON1_CLK_H
-
-struct clk_hw *clk_hw_register_pll(struct device *dev,
- const char *name,
- const char *parent_name,
- const struct clk_ops *ops,
- unsigned long flags);
-
-#endif /* __LOONGSON1_CLK_H */
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index d5936cfb3bee..0e8dd82aa84e 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -11,6 +11,13 @@ config COMMON_CLK_MEDIATEK
help
MediaTek SoCs' clock support.
+config COMMON_CLK_MEDIATEK_FHCTL
+ bool "clock driver for MediaTek FHCTL hardware control"
+ select COMMON_CLK_MEDIATEK
+ help
+ This driver supports MediaTek frequency hopping and
+ spread spectrum clocking features.
+
config COMMON_CLK_MT2701
bool "Clock driver for MediaTek MT2701"
depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
@@ -68,7 +75,7 @@ config COMMON_CLK_MT2701_G3DSYS
This driver supports MediaTek MT2701 g3dsys clocks.
config COMMON_CLK_MT2712
- bool "Clock driver for MediaTek MT2712"
+ tristate "Clock driver for MediaTek MT2712"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK && ARM64
@@ -76,47 +83,84 @@ config COMMON_CLK_MT2712
This driver supports MediaTek MT2712 basic clocks.
config COMMON_CLK_MT2712_BDPSYS
- bool "Clock driver for MediaTek MT2712 bdpsys"
+ tristate "Clock driver for MediaTek MT2712 bdpsys"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 bdpsys clocks.
config COMMON_CLK_MT2712_IMGSYS
- bool "Clock driver for MediaTek MT2712 imgsys"
+ tristate "Clock driver for MediaTek MT2712 imgsys"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 imgsys clocks.
config COMMON_CLK_MT2712_JPGDECSYS
- bool "Clock driver for MediaTek MT2712 jpgdecsys"
+ tristate "Clock driver for MediaTek MT2712 jpgdecsys"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 jpgdecsys clocks.
config COMMON_CLK_MT2712_MFGCFG
- bool "Clock driver for MediaTek MT2712 mfgcfg"
+ tristate "Clock driver for MediaTek MT2712 mfgcfg"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 mfgcfg clocks.
config COMMON_CLK_MT2712_MMSYS
- bool "Clock driver for MediaTek MT2712 mmsys"
+ tristate "Clock driver for MediaTek MT2712 mmsys"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 mmsys clocks.
config COMMON_CLK_MT2712_VDECSYS
- bool "Clock driver for MediaTek MT2712 vdecsys"
+ tristate "Clock driver for MediaTek MT2712 vdecsys"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 vdecsys clocks.
config COMMON_CLK_MT2712_VENCSYS
- bool "Clock driver for MediaTek MT2712 vencsys"
+ tristate "Clock driver for MediaTek MT2712 vencsys"
depends on COMMON_CLK_MT2712
help
This driver supports MediaTek MT2712 vencsys clocks.
+config COMMON_CLK_MT6735
+ tristate "Main clock drivers for MediaTek MT6735"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ help
+ This enables drivers for clocks and resets provided
+ by apmixedsys, topckgen, infracfg and pericfg on the
+ MediaTek MT6735 SoC.
+
+config COMMON_CLK_MT6735_IMGSYS
+ tristate "Clock driver for MediaTek MT6735 imgsys"
+ depends on COMMON_CLK_MT6735
+ help
+ This enables a driver for clocks provided by imgsys
+ on the MediaTek MT6735 SoC.
+
+config COMMON_CLK_MT6735_MFGCFG
+ tristate "Clock driver for MediaTek MT6735 mfgcfg"
+ depends on COMMON_CLK_MT6735
+ help
+ This enables a driver for clocks and resets provided
+ by mfgcfg on the MediaTek MT6735 SoC.
+
+config COMMON_CLK_MT6735_VDECSYS
+ tristate "Clock driver for MediaTek MT6735 vdecsys"
+ depends on COMMON_CLK_MT6735
+ help
+ This enables a driver for clocks and resets provided
+ by vdecsys on the MediaTek MT6735 SoC.
+
+config COMMON_CLK_MT6735_VENCSYS
+ tristate "Clock driver for MediaTek MT6735 vencsys"
+ depends on COMMON_CLK_MT6735
+ help
+ This enables a driver for clocks provided by vencsys
+ on the MediaTek MT6735 SoC.
+
config COMMON_CLK_MT6765
bool "Clock driver for MediaTek MT6765"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
@@ -126,79 +170,79 @@ config COMMON_CLK_MT6765
This driver supports MediaTek MT6765 basic clocks.
config COMMON_CLK_MT6765_AUDIOSYS
- bool "Clock driver for MediaTek MT6765 audiosys"
+ tristate "Clock driver for MediaTek MT6765 audiosys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 audiosys clocks.
config COMMON_CLK_MT6765_CAMSYS
- bool "Clock driver for MediaTek MT6765 camsys"
+ tristate "Clock driver for MediaTek MT6765 camsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 camsys clocks.
config COMMON_CLK_MT6765_GCESYS
- bool "Clock driver for MediaTek MT6765 gcesys"
+ tristate "Clock driver for MediaTek MT6765 gcesys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 gcesys clocks.
config COMMON_CLK_MT6765_MMSYS
- bool "Clock driver for MediaTek MT6765 mmsys"
+ tristate "Clock driver for MediaTek MT6765 mmsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mmsys clocks.
config COMMON_CLK_MT6765_IMGSYS
- bool "Clock driver for MediaTek MT6765 imgsys"
+ tristate "Clock driver for MediaTek MT6765 imgsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 imgsys clocks.
config COMMON_CLK_MT6765_VCODECSYS
- bool "Clock driver for MediaTek MT6765 vcodecsys"
+ tristate "Clock driver for MediaTek MT6765 vcodecsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 vcodecsys clocks.
config COMMON_CLK_MT6765_MFGSYS
- bool "Clock driver for MediaTek MT6765 mfgsys"
+ tristate "Clock driver for MediaTek MT6765 mfgsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mfgsys clocks.
config COMMON_CLK_MT6765_MIPI0ASYS
- bool "Clock driver for MediaTek MT6765 mipi0asys"
+ tristate "Clock driver for MediaTek MT6765 mipi0asys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mipi0asys clocks.
config COMMON_CLK_MT6765_MIPI0BSYS
- bool "Clock driver for MediaTek MT6765 mipi0bsys"
+ tristate "Clock driver for MediaTek MT6765 mipi0bsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mipi0bsys clocks.
config COMMON_CLK_MT6765_MIPI1ASYS
- bool "Clock driver for MediaTek MT6765 mipi1asys"
+ tristate "Clock driver for MediaTek MT6765 mipi1asys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mipi1asys clocks.
config COMMON_CLK_MT6765_MIPI1BSYS
- bool "Clock driver for MediaTek MT6765 mipi1bsys"
+ tristate "Clock driver for MediaTek MT6765 mipi1bsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mipi1bsys clocks.
config COMMON_CLK_MT6765_MIPI2ASYS
- bool "Clock driver for MediaTek MT6765 mipi2asys"
+ tristate "Clock driver for MediaTek MT6765 mipi2asys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mipi2asys clocks.
config COMMON_CLK_MT6765_MIPI2BSYS
- bool "Clock driver for MediaTek MT6765 mipi2bsys"
+ tristate "Clock driver for MediaTek MT6765 mipi2bsys"
depends on COMMON_CLK_MT6765
help
This driver supports MediaTek MT6765 mipi2bsys clocks.
@@ -259,6 +303,44 @@ config COMMON_CLK_MT6779_AUDSYS
help
This driver supports Mediatek MT6779 audsys clocks.
+config COMMON_CLK_MT6795
+ tristate "Clock driver for MediaTek MT6795"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
+ default ARCH_MEDIATEK
+ help
+ This driver supports MediaTek MT6795 basic clocks and clocks
+ required for various peripherals found on MediaTek.
+
+config COMMON_CLK_MT6795_MFGCFG
+ tristate "Clock driver for MediaTek MT6795 mfgcfg"
+ depends on COMMON_CLK_MT6795
+ default COMMON_CLK_MT6795
+ help
+ This driver supports MediaTek MT6795 mfgcfg clocks.
+
+config COMMON_CLK_MT6795_MMSYS
+ tristate "Clock driver for MediaTek MT6795 mmsys"
+ depends on COMMON_CLK_MT6795
+ default COMMON_CLK_MT6795
+ help
+ This driver supports MediaTek MT6795 mmsys clocks.
+
+config COMMON_CLK_MT6795_VDECSYS
+ tristate "Clock driver for MediaTek MT6795 VDECSYS"
+ depends on COMMON_CLK_MT6795
+ default COMMON_CLK_MT6795
+ help
+ This driver supports MediaTek MT6795 vdecsys clocks.
+
+config COMMON_CLK_MT6795_VENCSYS
+ tristate "Clock driver for MediaTek MT6795 VENCSYS"
+ depends on COMMON_CLK_MT6795
+ default COMMON_CLK_MT6795
+ help
+ This driver supports MediaTek MT6795 vencsys clocks.
+
config COMMON_CLK_MT6797
bool "Clock driver for MediaTek MT6797"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
@@ -268,31 +350,31 @@ config COMMON_CLK_MT6797
This driver supports MediaTek MT6797 basic clocks.
config COMMON_CLK_MT6797_MMSYS
- bool "Clock driver for MediaTek MT6797 mmsys"
+ tristate "Clock driver for MediaTek MT6797 mmsys"
depends on COMMON_CLK_MT6797
help
This driver supports MediaTek MT6797 mmsys clocks.
config COMMON_CLK_MT6797_IMGSYS
- bool "Clock driver for MediaTek MT6797 imgsys"
+ tristate "Clock driver for MediaTek MT6797 imgsys"
depends on COMMON_CLK_MT6797
help
This driver supports MediaTek MT6797 imgsys clocks.
config COMMON_CLK_MT6797_VDECSYS
- bool "Clock driver for MediaTek MT6797 vdecsys"
+ tristate "Clock driver for MediaTek MT6797 vdecsys"
depends on COMMON_CLK_MT6797
help
This driver supports MediaTek MT6797 vdecsys clocks.
config COMMON_CLK_MT6797_VENCSYS
- bool "Clock driver for MediaTek MT6797 vencsys"
+ tristate "Clock driver for MediaTek MT6797 vencsys"
depends on COMMON_CLK_MT6797
help
This driver supports MediaTek MT6797 vencsys clocks.
config COMMON_CLK_MT7622
- bool "Clock driver for MediaTek MT7622"
+ tristate "Clock driver for MediaTek MT7622"
depends on ARCH_MEDIATEK || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK
@@ -301,21 +383,21 @@ config COMMON_CLK_MT7622
required for various periperals found on MediaTek.
config COMMON_CLK_MT7622_ETHSYS
- bool "Clock driver for MediaTek MT7622 ETHSYS"
+ tristate "Clock driver for MediaTek MT7622 ETHSYS"
depends on COMMON_CLK_MT7622
help
This driver add support for clocks for Ethernet and SGMII
required on MediaTek MT7622 SoC.
config COMMON_CLK_MT7622_HIFSYS
- bool "Clock driver for MediaTek MT7622 HIFSYS"
+ tristate "Clock driver for MediaTek MT7622 HIFSYS"
depends on COMMON_CLK_MT7622
help
This driver supports MediaTek MT7622 HIFSYS clocks providing
to PCI-E and USB.
config COMMON_CLK_MT7622_AUDSYS
- bool "Clock driver for MediaTek MT7622 AUDSYS"
+ tristate "Clock driver for MediaTek MT7622 AUDSYS"
depends on COMMON_CLK_MT7622
help
This driver supports MediaTek MT7622 AUDSYS clocks providing
@@ -344,8 +426,25 @@ config COMMON_CLK_MT7629_HIFSYS
This driver supports MediaTek MT7629 HIFSYS clocks providing
to PCI-E and USB.
+config COMMON_CLK_MT7981
+ bool "Clock driver for MediaTek MT7981"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK
+ help
+ This driver supports MediaTek MT7981 basic clocks and clocks
+ required for various peripherals found on this SoC.
+
+config COMMON_CLK_MT7981_ETHSYS
+ tristate "Clock driver for MediaTek MT7981 ETHSYS"
+ depends on COMMON_CLK_MT7981
+ default COMMON_CLK_MT7981
+ help
+ This driver adds support for clocks for Ethernet and SGMII
+ required on MediaTek MT7981 SoC.
+
config COMMON_CLK_MT7986
- bool "Clock driver for MediaTek MT7986"
+ tristate "Clock driver for MediaTek MT7986"
depends on ARCH_MEDIATEK || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK
@@ -354,15 +453,24 @@ config COMMON_CLK_MT7986
required for various peripherals found on MediaTek.
config COMMON_CLK_MT7986_ETHSYS
- bool "Clock driver for MediaTek MT7986 ETHSYS"
+ tristate "Clock driver for MediaTek MT7986 ETHSYS"
depends on COMMON_CLK_MT7986
default COMMON_CLK_MT7986
help
This driver adds support for clocks for Ethernet and SGMII
required on MediaTek MT7986 SoC.
+config COMMON_CLK_MT7988
+ tristate "Clock driver for MediaTek MT7988"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK
+ help
+ This driver supports MediaTek MT7988 basic clocks and clocks
+ required for various periperals found on this SoC.
+
config COMMON_CLK_MT8135
- bool "Clock driver for MediaTek MT8135"
+ tristate "Clock driver for MediaTek MT8135"
depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK && ARM
@@ -370,7 +478,7 @@ config COMMON_CLK_MT8135
This driver supports MediaTek MT8135 clocks.
config COMMON_CLK_MT8167
- bool "Clock driver for MediaTek MT8167"
+ tristate "Clock driver for MediaTek MT8167"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK
@@ -378,57 +486,80 @@ config COMMON_CLK_MT8167
This driver supports MediaTek MT8167 basic clocks.
config COMMON_CLK_MT8167_AUDSYS
- bool "Clock driver for MediaTek MT8167 audsys"
+ tristate "Clock driver for MediaTek MT8167 audsys"
depends on COMMON_CLK_MT8167
default COMMON_CLK_MT8167
help
This driver supports MediaTek MT8167 audsys clocks.
config COMMON_CLK_MT8167_IMGSYS
- bool "Clock driver for MediaTek MT8167 imgsys"
+ tristate "Clock driver for MediaTek MT8167 imgsys"
depends on COMMON_CLK_MT8167
default COMMON_CLK_MT8167
help
This driver supports MediaTek MT8167 imgsys clocks.
config COMMON_CLK_MT8167_MFGCFG
- bool "Clock driver for MediaTek MT8167 mfgcfg"
+ tristate "Clock driver for MediaTek MT8167 mfgcfg"
depends on COMMON_CLK_MT8167
default COMMON_CLK_MT8167
help
This driver supports MediaTek MT8167 mfgcfg clocks.
config COMMON_CLK_MT8167_MMSYS
- bool "Clock driver for MediaTek MT8167 mmsys"
+ tristate "Clock driver for MediaTek MT8167 mmsys"
depends on COMMON_CLK_MT8167
default COMMON_CLK_MT8167
help
This driver supports MediaTek MT8167 mmsys clocks.
config COMMON_CLK_MT8167_VDECSYS
- bool "Clock driver for MediaTek MT8167 vdecsys"
+ tristate "Clock driver for MediaTek MT8167 vdecsys"
depends on COMMON_CLK_MT8167
default COMMON_CLK_MT8167
help
This driver supports MediaTek MT8167 vdecsys clocks.
config COMMON_CLK_MT8173
- bool "Clock driver for MediaTek MT8173"
- depends on ARCH_MEDIATEK || COMPILE_TEST
+ tristate "Clock driver for MediaTek MT8173"
+ depends on ARM64 || COMPILE_TEST
select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
default ARCH_MEDIATEK
help
- This driver supports MediaTek MT8173 clocks.
+ This driver supports MediaTek MT8173 basic clocks and clocks
+ required for various peripherals found on MediaTek.
+
+config COMMON_CLK_MT8173_IMGSYS
+ tristate "Clock driver for MediaTek MT8173 imgsys"
+ depends on COMMON_CLK_MT8173
+ default COMMON_CLK_MT8173
+ help
+ This driver supports MediaTek MT8173 imgsys clocks.
config COMMON_CLK_MT8173_MMSYS
- bool "Clock driver for MediaTek MT8173 mmsys"
+ tristate "Clock driver for MediaTek MT8173 mmsys"
+ depends on COMMON_CLK_MT8173
+ default COMMON_CLK_MT8173
+ help
+ This driver supports MediaTek MT8173 mmsys clocks.
+
+config COMMON_CLK_MT8173_VDECSYS
+ tristate "Clock driver for MediaTek MT8173 VDECSYS"
depends on COMMON_CLK_MT8173
default COMMON_CLK_MT8173
help
- This driver supports MediaTek MT8173 mmsys clocks.
+ This driver supports MediaTek MT8173 vdecsys clocks.
+
+config COMMON_CLK_MT8173_VENCSYS
+ tristate "Clock driver for MediaTek MT8173 VENCSYS"
+ depends on COMMON_CLK_MT8173
+ default COMMON_CLK_MT8173
+ help
+ This driver supports MediaTek MT8173 vencsys clocks.
config COMMON_CLK_MT8183
- bool "Clock driver for MediaTek MT8183"
+ tristate "Clock driver for MediaTek MT8183"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK && ARM64
@@ -436,167 +567,344 @@ config COMMON_CLK_MT8183
This driver supports MediaTek MT8183 basic clocks.
config COMMON_CLK_MT8183_AUDIOSYS
- bool "Clock driver for MediaTek MT8183 audiosys"
+ tristate "Clock driver for MediaTek MT8183 audiosys"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 audiosys clocks.
config COMMON_CLK_MT8183_CAMSYS
- bool "Clock driver for MediaTek MT8183 camsys"
+ tristate "Clock driver for MediaTek MT8183 camsys"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 camsys clocks.
config COMMON_CLK_MT8183_IMGSYS
- bool "Clock driver for MediaTek MT8183 imgsys"
+ tristate "Clock driver for MediaTek MT8183 imgsys"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 imgsys clocks.
config COMMON_CLK_MT8183_IPU_CORE0
- bool "Clock driver for MediaTek MT8183 ipu_core0"
+ tristate "Clock driver for MediaTek MT8183 ipu_core0"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 ipu_core0 clocks.
config COMMON_CLK_MT8183_IPU_CORE1
- bool "Clock driver for MediaTek MT8183 ipu_core1"
+ tristate "Clock driver for MediaTek MT8183 ipu_core1"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 ipu_core1 clocks.
config COMMON_CLK_MT8183_IPU_ADL
- bool "Clock driver for MediaTek MT8183 ipu_adl"
+ tristate "Clock driver for MediaTek MT8183 ipu_adl"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 ipu_adl clocks.
config COMMON_CLK_MT8183_IPU_CONN
- bool "Clock driver for MediaTek MT8183 ipu_conn"
+ tristate "Clock driver for MediaTek MT8183 ipu_conn"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 ipu_conn clocks.
config COMMON_CLK_MT8183_MFGCFG
- bool "Clock driver for MediaTek MT8183 mfgcfg"
+ tristate "Clock driver for MediaTek MT8183 mfgcfg"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 mfgcfg clocks.
config COMMON_CLK_MT8183_MMSYS
- bool "Clock driver for MediaTek MT8183 mmsys"
+ tristate "Clock driver for MediaTek MT8183 mmsys"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 mmsys clocks.
config COMMON_CLK_MT8183_VDECSYS
- bool "Clock driver for MediaTek MT8183 vdecsys"
+ tristate "Clock driver for MediaTek MT8183 vdecsys"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 vdecsys clocks.
config COMMON_CLK_MT8183_VENCSYS
- bool "Clock driver for MediaTek MT8183 vencsys"
+ tristate "Clock driver for MediaTek MT8183 vencsys"
depends on COMMON_CLK_MT8183
default COMMON_CLK_MT8183
help
This driver supports MediaTek MT8183 vencsys clocks.
config COMMON_CLK_MT8186
- bool "Clock driver for MediaTek MT8186"
+ tristate "Clock driver for MediaTek MT8186"
depends on ARM64 || COMPILE_TEST
select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
default ARCH_MEDIATEK
help
This driver supports MediaTek MT8186 clocks.
+config COMMON_CLK_MT8186_CAMSYS
+ tristate "Clock driver for MediaTek MT8186 camsys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 camsys and camsys_raw clocks.
+
+config COMMON_CLK_MT8186_IMGSYS
+ tristate "Clock driver for MediaTek MT8186 imgsys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 imgsys and imgsys2 clocks.
+
+config COMMON_CLK_MT8186_IPESYS
+ tristate "Clock driver for MediaTek MT8186 ipesys"
+ depends on COMMON_CLK_MT8186_IMGSYS
+ default COMMON_CLK_MT8186_IMGSYS
+ help
+ This driver supports MediaTek MT8186 ipesys clocks.
+
+config COMMON_CLK_MT8186_WPESYS
+ tristate "Clock driver for MediaTek MT8186 wpesys"
+ depends on COMMON_CLK_MT8186_IMGSYS
+ default COMMON_CLK_MT8186_IMGSYS
+ help
+ This driver supports MediaTek MT8186 Warp Engine clocks.
+
+config COMMON_CLK_MT8186_IMP_IIC_WRAP
+ tristate "Clock driver for MediaTek MT8186 imp_iic_wrap"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 imp_iic_wrap clocks.
+
+config COMMON_CLK_MT8186_MCUSYS
+ tristate "Clock driver for MediaTek MT8186 mcusys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 mcusys clocks.
+
+config COMMON_CLK_MT8186_MDPSYS
+ tristate "Clock driver for MediaTek MT8186 mdpsys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 mdpsys clocks.
+
+config COMMON_CLK_MT8186_MFGCFG
+ tristate "Clock driver for MediaTek MT8186 mfgcfg"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 mfgcfg clocks.
+
+config COMMON_CLK_MT8186_MMSYS
+ tristate "Clock driver for MediaTek MT8186 mmsys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 mmsys clocks.
+
+config COMMON_CLK_MT8186_VDECSYS
+ tristate "Clock driver for MediaTek MT8186 vdecsys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 vdecsys and vdecsys_soc clocks.
+
+config COMMON_CLK_MT8186_VENCSYS
+ tristate "Clock driver for MediaTek MT8186 vencsys"
+ depends on COMMON_CLK_MT8186
+ default COMMON_CLK_MT8186
+ help
+ This driver supports MediaTek MT8186 vencsys clocks.
+
+config COMMON_CLK_MT8188
+ tristate "Clock driver for MediaTek MT8188"
+ depends on ARM64 || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
+ default ARCH_MEDIATEK
+ help
+ This driver supports MediaTek MT8188 clocks.
+
+config COMMON_CLK_MT8188_ADSP_AUDIO26M
+ tristate "Clock driver for MediaTek MT8188 adsp audio26m"
+ depends on COMMON_CLK_MT8188
+ default COMMON_CLK_MT8188
+ help
+ This driver supports MediaTek MT8188 adsp audio26m clocks.
+
+config COMMON_CLK_MT8188_CAMSYS
+ tristate "Clock driver for MediaTek MT8188 camsys"
+ depends on COMMON_CLK_MT8188_VPPSYS
+ default COMMON_CLK_MT8188_VPPSYS
+ help
+ This driver supports MediaTek MT8188 camsys and camsys_raw clocks.
+
+config COMMON_CLK_MT8188_IMGSYS
+ tristate "Clock driver for MediaTek MT8188 imgsys"
+ depends on COMMON_CLK_MT8188_VPPSYS
+ default COMMON_CLK_MT8188_VPPSYS
+ help
+ This driver supports MediaTek MT8188 imgsys and imgsys2 clocks.
+
+config COMMON_CLK_MT8188_IMP_IIC_WRAP
+ tristate "Clock driver for MediaTek MT8188 imp_iic_wrap"
+ depends on COMMON_CLK_MT8188
+ default COMMON_CLK_MT8188
+ help
+ This driver supports MediaTek MT8188 I2C/I3C clocks.
+
+config COMMON_CLK_MT8188_IPESYS
+ tristate "Clock driver for MediaTek MT8188 ipesys"
+ depends on COMMON_CLK_MT8188_IMGSYS
+ default COMMON_CLK_MT8188_IMGSYS
+ help
+ This driver supports MediaTek MT8188 ipesys clocks.
+
+config COMMON_CLK_MT8188_MFGCFG
+ tristate "Clock driver for MediaTek MT8188 mfgcfg"
+ depends on COMMON_CLK_MT8188
+ default COMMON_CLK_MT8188
+ help
+ This driver supports MediaTek MT8188 mfgcfg clocks.
+
+config COMMON_CLK_MT8188_VDECSYS
+ tristate "Clock driver for MediaTek MT8188 vdecsys"
+ depends on COMMON_CLK_MT8188_VPPSYS
+ default COMMON_CLK_MT8188_VPPSYS
+ help
+ This driver supports MediaTek MT8188 vdecsys and vdecsys_soc clocks.
+
+config COMMON_CLK_MT8188_VDOSYS
+ tristate "Clock driver for MediaTek MT8188 vdosys"
+ depends on COMMON_CLK_MT8188
+ default COMMON_CLK_MT8188
+ help
+ This driver supports MediaTek MT8188 vdosys0/1 (multimedia) clocks.
+
+config COMMON_CLK_MT8188_VENCSYS
+ tristate "Clock driver for MediaTek MT8188 vencsys"
+ depends on COMMON_CLK_MT8188_VPPSYS
+ default COMMON_CLK_MT8188_VPPSYS
+ help
+ This driver supports MediaTek MT8188 vencsys clocks.
+
+config COMMON_CLK_MT8188_VPPSYS
+ tristate "Clock driver for MediaTek MT8188 vppsys"
+ depends on COMMON_CLK_MT8188
+ default COMMON_CLK_MT8188
+ help
+ This driver supports MediaTek MT8188 vppsys0/1 clocks.
+
+config COMMON_CLK_MT8188_WPESYS
+ tristate "Clock driver for MediaTek MT8188 wpesys"
+ depends on COMMON_CLK_MT8188_IMGSYS
+ default COMMON_CLK_MT8188_IMGSYS
+ help
+ This driver supports MediaTek MT8188 Warp Engine clocks.
+
config COMMON_CLK_MT8192
- bool "Clock driver for MediaTek MT8192"
+ tristate "Clock driver for MediaTek MT8192"
depends on ARM64 || COMPILE_TEST
select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
default ARM64
help
This driver supports MediaTek MT8192 basic clocks.
config COMMON_CLK_MT8192_AUDSYS
- bool "Clock driver for MediaTek MT8192 audsys"
+ tristate "Clock driver for MediaTek MT8192 audsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 audsys clocks.
config COMMON_CLK_MT8192_CAMSYS
- bool "Clock driver for MediaTek MT8192 camsys"
+ tristate "Clock driver for MediaTek MT8192 camsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 camsys and camsys_raw clocks.
config COMMON_CLK_MT8192_IMGSYS
- bool "Clock driver for MediaTek MT8192 imgsys"
+ tristate "Clock driver for MediaTek MT8192 imgsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 imgsys and imgsys2 clocks.
config COMMON_CLK_MT8192_IMP_IIC_WRAP
- bool "Clock driver for MediaTek MT8192 imp_iic_wrap"
+ tristate "Clock driver for MediaTek MT8192 imp_iic_wrap"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 imp_iic_wrap clocks.
config COMMON_CLK_MT8192_IPESYS
- bool "Clock driver for MediaTek MT8192 ipesys"
+ tristate "Clock driver for MediaTek MT8192 ipesys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 ipesys clocks.
config COMMON_CLK_MT8192_MDPSYS
- bool "Clock driver for MediaTek MT8192 mdpsys"
+ tristate "Clock driver for MediaTek MT8192 mdpsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 mdpsys clocks.
config COMMON_CLK_MT8192_MFGCFG
- bool "Clock driver for MediaTek MT8192 mfgcfg"
+ tristate "Clock driver for MediaTek MT8192 mfgcfg"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 mfgcfg clocks.
config COMMON_CLK_MT8192_MMSYS
- bool "Clock driver for MediaTek MT8192 mmsys"
+ tristate "Clock driver for MediaTek MT8192 mmsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 mmsys clocks.
config COMMON_CLK_MT8192_MSDC
- bool "Clock driver for MediaTek MT8192 msdc"
+ tristate "Clock driver for MediaTek MT8192 msdc"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 msdc and msdc_top clocks.
config COMMON_CLK_MT8192_SCP_ADSP
- bool "Clock driver for MediaTek MT8192 scp_adsp"
+ tristate "Clock driver for MediaTek MT8192 scp_adsp"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 scp_adsp clocks.
config COMMON_CLK_MT8192_VDECSYS
- bool "Clock driver for MediaTek MT8192 vdecsys"
+ tristate "Clock driver for MediaTek MT8192 vdecsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 vdecsys and vdecsys_soc clocks.
config COMMON_CLK_MT8192_VENCSYS
- bool "Clock driver for MediaTek MT8192 vencsys"
+ tristate "Clock driver for MediaTek MT8192 vencsys"
depends on COMMON_CLK_MT8192
+ default COMMON_CLK_MT8192
help
This driver supports MediaTek MT8192 vencsys clocks.
@@ -604,12 +912,219 @@ config COMMON_CLK_MT8195
bool "Clock driver for MediaTek MT8195"
depends on ARM64 || COMPILE_TEST
select COMMON_CLK_MEDIATEK
+ select COMMON_CLK_MEDIATEK_FHCTL
default ARCH_MEDIATEK
help
This driver supports MediaTek MT8195 clocks.
+config COMMON_CLK_MT8195_APUSYS
+ tristate "Clock driver for MediaTek MT8195 apusys"
+ depends on COMMON_CLK_MT8195
+ default COMMON_CLK_MT8195
+ help
+ This driver supports MediaTek MT8195 AI Processor Unit System clocks.
+
+config COMMON_CLK_MT8195_IMP_IIC_WRAP
+ tristate "Clock driver for MediaTek MT8195 imp_iic_wrap"
+ depends on COMMON_CLK_MT8195
+ default COMMON_CLK_MT8195
+ help
+ This driver supports MediaTek MT8195 I2C/I3C clocks.
+
+config COMMON_CLK_MT8195_MFGCFG
+ tristate "Clock driver for MediaTek MT8195 mfgcfg"
+ depends on COMMON_CLK_MT8195
+ default COMMON_CLK_MT8195
+ help
+ This driver supports MediaTek MT8195 mfgcfg clocks.
+
+config COMMON_CLK_MT8195_SCP_ADSP
+ tristate "Clock driver for MediaTek MT8195 scp_adsp"
+ depends on COMMON_CLK_MT8195
+ default COMMON_CLK_MT8195
+ help
+ This driver supports MediaTek MT8195 System Companion Processor
+ Audio DSP clocks.
+
+config COMMON_CLK_MT8195_VDOSYS
+ tristate "Clock driver for MediaTek MT8195 vdosys"
+ depends on COMMON_CLK_MT8195
+ default COMMON_CLK_MT8195
+ help
+ This driver supports MediaTek MT8195 vdosys0/1 (multimedia) clocks.
+
+config COMMON_CLK_MT8195_VPPSYS
+ tristate "Clock driver for MediaTek MT8195 vppsys"
+ depends on COMMON_CLK_MT8195
+ default COMMON_CLK_MT8195
+ help
+ This driver supports MediaTek MT8195 vppsys0/1 clocks.
+
+config COMMON_CLK_MT8195_CAMSYS
+ tristate "Clock driver for MediaTek MT8195 camsys"
+ depends on COMMON_CLK_MT8195_VPPSYS
+ default COMMON_CLK_MT8195_VPPSYS
+ help
+ This driver supports MediaTek MT8195 camsys and camsys_raw clocks.
+
+config COMMON_CLK_MT8195_IMGSYS
+ tristate "Clock driver for MediaTek MT8195 imgsys"
+ depends on COMMON_CLK_MT8195_VPPSYS
+ default COMMON_CLK_MT8195_VPPSYS
+ help
+ This driver supports MediaTek MT8195 imgsys and imgsys2 clocks.
+
+config COMMON_CLK_MT8195_IPESYS
+ tristate "Clock driver for MediaTek MT8195 ipesys"
+ depends on COMMON_CLK_MT8195_IMGSYS
+ default COMMON_CLK_MT8195_IMGSYS
+ help
+ This driver supports MediaTek MT8195 ipesys clocks.
+
+config COMMON_CLK_MT8195_WPESYS
+ tristate "Clock driver for MediaTek MT8195 wpesys"
+ depends on COMMON_CLK_MT8195_IMGSYS
+ default COMMON_CLK_MT8195_IMGSYS
+ help
+ This driver supports MediaTek MT8195 Warp Engine clocks.
+
+config COMMON_CLK_MT8195_VDECSYS
+ tristate "Clock driver for MediaTek MT8195 vdecsys"
+ depends on COMMON_CLK_MT8195_VPPSYS
+ default COMMON_CLK_MT8195_VPPSYS
+ help
+ This driver supports MediaTek MT8195 vdecsys and vdecsys_soc clocks.
+
+config COMMON_CLK_MT8195_VENCSYS
+ tristate "Clock driver for MediaTek MT8195 vencsys"
+ depends on COMMON_CLK_MT8195_VPPSYS
+ default COMMON_CLK_MT8195_VPPSYS
+ help
+ This driver supports MediaTek MT8195 vencsys clocks.
+
+config COMMON_CLK_MT8196
+ tristate "Clock driver for MediaTek MT8196"
+ depends on ARM64 || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK
+ help
+ This driver supports MediaTek MT8196 basic clocks.
+
+config COMMON_CLK_MT8196_IMP_IIC_WRAP
+ tristate "Clock driver for MediaTek MT8196 imp_iic_wrap"
+ depends on COMMON_CLK_MT8196
+ default COMMON_CLK_MT8196
+ help
+ This driver supports MediaTek MT8196 i2c clocks.
+
+config COMMON_CLK_MT8196_MCUSYS
+ tristate "Clock driver for MediaTek MT8196 mcusys"
+ depends on COMMON_CLK_MT8196
+ default COMMON_CLK_MT8196
+ help
+ This driver supports MediaTek MT8196 mcusys clocks.
+
+config COMMON_CLK_MT8196_MDPSYS
+ tristate "Clock driver for MediaTek MT8196 mdpsys"
+ depends on COMMON_CLK_MT8196
+ default COMMON_CLK_MT8196
+ help
+ This driver supports MediaTek MT8196 mdpsys clocks.
+
+config COMMON_CLK_MT8196_MFGCFG
+ tristate "Clock driver for MediaTek MT8196 mfgcfg"
+ depends on COMMON_CLK_MT8196
+ default m
+ help
+ This driver supports MediaTek MT8196 mfgcfg clocks.
+
+config COMMON_CLK_MT8196_MMSYS
+ tristate "Clock driver for MediaTek MT8196 mmsys"
+ depends on COMMON_CLK_MT8196
+ default m
+ help
+ This driver supports MediaTek MT8196 mmsys clocks.
+
+config COMMON_CLK_MT8196_PEXTPSYS
+ tristate "Clock driver for MediaTek MT8196 pextpsys"
+ depends on COMMON_CLK_MT8196
+ default COMMON_CLK_MT8196
+ help
+ This driver supports MediaTek MT8196 pextpsys clocks.
+
+config COMMON_CLK_MT8196_UFSSYS
+ tristate "Clock driver for MediaTek MT8196 ufssys"
+ depends on COMMON_CLK_MT8196
+ default COMMON_CLK_MT8196
+ help
+ This driver supports MediaTek MT8196 ufssys clocks.
+
+config COMMON_CLK_MT8196_VDECSYS
+ tristate "Clock driver for MediaTek MT8196 vdecsys"
+ depends on COMMON_CLK_MT8196
+ default m
+ help
+ This driver supports MediaTek MT8196 vdecsys clocks.
+
+config COMMON_CLK_MT8196_VENCSYS
+ tristate "Clock driver for MediaTek MT8196 vencsys"
+ depends on COMMON_CLK_MT8196
+ default m
+ help
+ This driver supports MediaTek MT8196 vencsys clocks.
+
+config COMMON_CLK_MT8365
+ tristate "Clock driver for MediaTek MT8365"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK && ARM64
+ help
+ This driver supports MediaTek MT8365 basic clocks.
+
+config COMMON_CLK_MT8365_APU
+ tristate "Clock driver for MediaTek MT8365 apu"
+ depends on COMMON_CLK_MT8365
+ default COMMON_CLK_MT8365
+ help
+ This driver supports MediaTek MT8365 apu clocks.
+
+config COMMON_CLK_MT8365_CAM
+ tristate "Clock driver for MediaTek MT8365 cam"
+ depends on COMMON_CLK_MT8365
+ default COMMON_CLK_MT8365
+ help
+ This driver supports MediaTek MT8365 cam clocks.
+
+config COMMON_CLK_MT8365_MFG
+ tristate "Clock driver for MediaTek MT8365 mfg"
+ depends on COMMON_CLK_MT8365
+ default COMMON_CLK_MT8365
+ help
+ This driver supports MediaTek MT8365 mfg clocks.
+
+config COMMON_CLK_MT8365_MMSYS
+ tristate "Clock driver for MediaTek MT8365 mmsys"
+ depends on COMMON_CLK_MT8365
+ default COMMON_CLK_MT8365
+ help
+ This driver supports MediaTek MT8365 mmsys clocks.
+
+config COMMON_CLK_MT8365_VDEC
+ tristate "Clock driver for MediaTek MT8365 vdec"
+ depends on COMMON_CLK_MT8365
+ default COMMON_CLK_MT8365
+ help
+ This driver supports MediaTek MT8365 vdec clocks.
+
+config COMMON_CLK_MT8365_VENC
+ tristate "Clock driver for MediaTek MT8365 venc"
+ depends on COMMON_CLK_MT8365
+ default COMMON_CLK_MT8365
+ help
+ This driver supports MediaTek MT8365 venc clocks.
+
config COMMON_CLK_MT8516
- bool "Clock driver for MediaTek MT8516"
+ tristate "Clock driver for MediaTek MT8516"
depends on ARCH_MEDIATEK || COMPILE_TEST
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK
@@ -617,7 +1132,7 @@ config COMMON_CLK_MT8516
This driver supports MediaTek MT8516 clocks.
config COMMON_CLK_MT8516_AUDSYS
- bool "Clock driver for MediaTek MT8516 audsys"
+ tristate "Clock driver for MediaTek MT8516 audsys"
depends on COMMON_CLK_MT8516
help
This driver supports MediaTek MT8516 audsys clocks.
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index caf2ce93d666..d8736a060dbd 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,6 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o reset.o clk-mux.o
+obj-$(CONFIG_COMMON_CLK_MEDIATEK_FHCTL) += clk-fhctl.o clk-pllfh.o
+obj-$(CONFIG_COMMON_CLK_MT6735) += clk-mt6735-apmixedsys.o clk-mt6735-infracfg.o clk-mt6735-pericfg.o clk-mt6735-topckgen.o
+obj-$(CONFIG_COMMON_CLK_MT6735_IMGSYS) += clk-mt6735-imgsys.o
+obj-$(CONFIG_COMMON_CLK_MT6735_MFGCFG) += clk-mt6735-mfgcfg.o
+obj-$(CONFIG_COMMON_CLK_MT6735_VDECSYS) += clk-mt6735-vdecsys.o
+obj-$(CONFIG_COMMON_CLK_MT6735_VENCSYS) += clk-mt6735-vencsys.o
obj-$(CONFIG_COMMON_CLK_MT6765) += clk-mt6765.o
obj-$(CONFIG_COMMON_CLK_MT6765_AUDIOSYS) += clk-mt6765-audio.o
obj-$(CONFIG_COMMON_CLK_MT6765_CAMSYS) += clk-mt6765-cam.o
@@ -17,6 +23,12 @@ obj-$(CONFIG_COMMON_CLK_MT6779_VDECSYS) += clk-mt6779-vdec.o
obj-$(CONFIG_COMMON_CLK_MT6779_VENCSYS) += clk-mt6779-venc.o
obj-$(CONFIG_COMMON_CLK_MT6779_MFGCFG) += clk-mt6779-mfg.o
obj-$(CONFIG_COMMON_CLK_MT6779_AUDSYS) += clk-mt6779-aud.o
+obj-$(CONFIG_COMMON_CLK_MT6795) += clk-mt6795-apmixedsys.o clk-mt6795-infracfg.o \
+ clk-mt6795-pericfg.o clk-mt6795-topckgen.o
+obj-$(CONFIG_COMMON_CLK_MT6795_MFGCFG) += clk-mt6795-mfg.o
+obj-$(CONFIG_COMMON_CLK_MT6795_MMSYS) += clk-mt6795-mm.o
+obj-$(CONFIG_COMMON_CLK_MT6795_VDECSYS) += clk-mt6795-vdecsys.o
+obj-$(CONFIG_COMMON_CLK_MT6795_VENCSYS) += clk-mt6795-vencsys.o
obj-$(CONFIG_COMMON_CLK_MT6797) += clk-mt6797.o
obj-$(CONFIG_COMMON_CLK_MT6797_IMGSYS) += clk-mt6797-img.o
obj-$(CONFIG_COMMON_CLK_MT6797_MMSYS) += clk-mt6797-mm.o
@@ -31,7 +43,7 @@ obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o
obj-$(CONFIG_COMMON_CLK_MT2701_IMGSYS) += clk-mt2701-img.o
obj-$(CONFIG_COMMON_CLK_MT2701_MMSYS) += clk-mt2701-mm.o
obj-$(CONFIG_COMMON_CLK_MT2701_VDECSYS) += clk-mt2701-vdec.o
-obj-$(CONFIG_COMMON_CLK_MT2712) += clk-mt2712.o
+obj-$(CONFIG_COMMON_CLK_MT2712) += clk-mt2712-apmixedsys.o clk-mt2712.o
obj-$(CONFIG_COMMON_CLK_MT2712_BDPSYS) += clk-mt2712-bdp.o
obj-$(CONFIG_COMMON_CLK_MT2712_IMGSYS) += clk-mt2712-img.o
obj-$(CONFIG_COMMON_CLK_MT2712_JPGDECSYS) += clk-mt2712-jpgdec.o
@@ -39,27 +51,41 @@ obj-$(CONFIG_COMMON_CLK_MT2712_MFGCFG) += clk-mt2712-mfg.o
obj-$(CONFIG_COMMON_CLK_MT2712_MMSYS) += clk-mt2712-mm.o
obj-$(CONFIG_COMMON_CLK_MT2712_VDECSYS) += clk-mt2712-vdec.o
obj-$(CONFIG_COMMON_CLK_MT2712_VENCSYS) += clk-mt2712-venc.o
-obj-$(CONFIG_COMMON_CLK_MT7622) += clk-mt7622.o
+obj-$(CONFIG_COMMON_CLK_MT7622) += clk-mt7622-apmixedsys.o clk-mt7622.o \
+ clk-mt7622-infracfg.o
obj-$(CONFIG_COMMON_CLK_MT7622_ETHSYS) += clk-mt7622-eth.o
obj-$(CONFIG_COMMON_CLK_MT7622_HIFSYS) += clk-mt7622-hif.o
obj-$(CONFIG_COMMON_CLK_MT7622_AUDSYS) += clk-mt7622-aud.o
obj-$(CONFIG_COMMON_CLK_MT7629) += clk-mt7629.o
obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o
obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o
+obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981-apmixed.o
+obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981-topckgen.o
+obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981-infracfg.o
+obj-$(CONFIG_COMMON_CLK_MT7981_ETHSYS) += clk-mt7981-eth.o
obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986-apmixed.o
obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986-topckgen.o
obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986-infracfg.o
obj-$(CONFIG_COMMON_CLK_MT7986_ETHSYS) += clk-mt7986-eth.o
-obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
-obj-$(CONFIG_COMMON_CLK_MT8167) += clk-mt8167.o
+obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988-apmixed.o
+obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988-topckgen.o
+obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988-infracfg.o
+obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988-eth.o
+obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988-xfipll.o
+obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135-apmixedsys.o clk-mt8135.o
+obj-$(CONFIG_COMMON_CLK_MT8167) += clk-mt8167-apmixedsys.o clk-mt8167.o
obj-$(CONFIG_COMMON_CLK_MT8167_AUDSYS) += clk-mt8167-aud.o
obj-$(CONFIG_COMMON_CLK_MT8167_IMGSYS) += clk-mt8167-img.o
obj-$(CONFIG_COMMON_CLK_MT8167_MFGCFG) += clk-mt8167-mfgcfg.o
obj-$(CONFIG_COMMON_CLK_MT8167_MMSYS) += clk-mt8167-mm.o
obj-$(CONFIG_COMMON_CLK_MT8167_VDECSYS) += clk-mt8167-vdec.o
-obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
+obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173-apmixedsys.o clk-mt8173-infracfg.o \
+ clk-mt8173-pericfg.o clk-mt8173-topckgen.o
+obj-$(CONFIG_COMMON_CLK_MT8173_IMGSYS) += clk-mt8173-img.o
obj-$(CONFIG_COMMON_CLK_MT8173_MMSYS) += clk-mt8173-mm.o
-obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183.o
+obj-$(CONFIG_COMMON_CLK_MT8173_VDECSYS) += clk-mt8173-vdecsys.o
+obj-$(CONFIG_COMMON_CLK_MT8173_VENCSYS) += clk-mt8173-vencsys.o
+obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183-apmixedsys.o clk-mt8183.o
obj-$(CONFIG_COMMON_CLK_MT8183_AUDIOSYS) += clk-mt8183-audio.o
obj-$(CONFIG_COMMON_CLK_MT8183_CAMSYS) += clk-mt8183-cam.o
obj-$(CONFIG_COMMON_CLK_MT8183_IMGSYS) += clk-mt8183-img.o
@@ -71,12 +97,33 @@ obj-$(CONFIG_COMMON_CLK_MT8183_MFGCFG) += clk-mt8183-mfgcfg.o
obj-$(CONFIG_COMMON_CLK_MT8183_MMSYS) += clk-mt8183-mm.o
obj-$(CONFIG_COMMON_CLK_MT8183_VDECSYS) += clk-mt8183-vdec.o
obj-$(CONFIG_COMMON_CLK_MT8183_VENCSYS) += clk-mt8183-venc.o
-obj-$(CONFIG_COMMON_CLK_MT8186) += clk-mt8186-mcu.o clk-mt8186-topckgen.o clk-mt8186-infra_ao.o \
- clk-mt8186-apmixedsys.o clk-mt8186-imp_iic_wrap.o \
- clk-mt8186-mfg.o clk-mt8186-mm.o clk-mt8186-wpe.o \
- clk-mt8186-img.o clk-mt8186-vdec.o clk-mt8186-venc.o \
- clk-mt8186-cam.o clk-mt8186-mdp.o clk-mt8186-ipe.o
-obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192.o
+obj-$(CONFIG_COMMON_CLK_MT8186) += clk-mt8186-apmixedsys.o clk-mt8186-topckgen.o \
+ clk-mt8186-infra_ao.o
+obj-$(CONFIG_COMMON_CLK_MT8186_CAMSYS) += clk-mt8186-cam.o
+obj-$(CONFIG_COMMON_CLK_MT8186_IMGSYS) += clk-mt8186-img.o
+obj-$(CONFIG_COMMON_CLK_MT8186_IMP_IIC_WRAP) += clk-mt8186-imp_iic_wrap.o
+obj-$(CONFIG_COMMON_CLK_MT8186_IPESYS) += clk-mt8186-ipe.o
+obj-$(CONFIG_COMMON_CLK_MT8186_MCUSYS) += clk-mt8186-mcu.o
+obj-$(CONFIG_COMMON_CLK_MT8186_MDPSYS) += clk-mt8186-mdp.o
+obj-$(CONFIG_COMMON_CLK_MT8186_MFGCFG) += clk-mt8186-mfg.o
+obj-$(CONFIG_COMMON_CLK_MT8186_MMSYS) += clk-mt8186-mm.o
+obj-$(CONFIG_COMMON_CLK_MT8186_VDECSYS) += clk-mt8186-vdec.o
+obj-$(CONFIG_COMMON_CLK_MT8186_VENCSYS) += clk-mt8186-venc.o
+obj-$(CONFIG_COMMON_CLK_MT8186_WPESYS) += clk-mt8186-wpe.o
+obj-$(CONFIG_COMMON_CLK_MT8188) += clk-mt8188-apmixedsys.o clk-mt8188-topckgen.o \
+ clk-mt8188-peri_ao.o clk-mt8188-infra_ao.o
+obj-$(CONFIG_COMMON_CLK_MT8188_ADSP_AUDIO26M) += clk-mt8188-adsp_audio26m.o
+obj-$(CONFIG_COMMON_CLK_MT8188_CAMSYS) += clk-mt8188-cam.o clk-mt8188-ccu.o
+obj-$(CONFIG_COMMON_CLK_MT8188_IMGSYS) += clk-mt8188-img.o
+obj-$(CONFIG_COMMON_CLK_MT8188_IMP_IIC_WRAP) += clk-mt8188-imp_iic_wrap.o
+obj-$(CONFIG_COMMON_CLK_MT8188_IPESYS) += clk-mt8188-ipe.o
+obj-$(CONFIG_COMMON_CLK_MT8188_MFGCFG) += clk-mt8188-mfg.o
+obj-$(CONFIG_COMMON_CLK_MT8188_VDECSYS) += clk-mt8188-vdec.o
+obj-$(CONFIG_COMMON_CLK_MT8188_VDOSYS) += clk-mt8188-vdo0.o clk-mt8188-vdo1.o
+obj-$(CONFIG_COMMON_CLK_MT8188_VENCSYS) += clk-mt8188-venc.o
+obj-$(CONFIG_COMMON_CLK_MT8188_VPPSYS) += clk-mt8188-vpp0.o clk-mt8188-vpp1.o
+obj-$(CONFIG_COMMON_CLK_MT8188_WPESYS) += clk-mt8188-wpe.o
+obj-$(CONFIG_COMMON_CLK_MT8192) += clk-mt8192-apmixedsys.o clk-mt8192.o
obj-$(CONFIG_COMMON_CLK_MT8192_AUDSYS) += clk-mt8192-aud.o
obj-$(CONFIG_COMMON_CLK_MT8192_CAMSYS) += clk-mt8192-cam.o
obj-$(CONFIG_COMMON_CLK_MT8192_IMGSYS) += clk-mt8192-img.o
@@ -90,12 +137,38 @@ obj-$(CONFIG_COMMON_CLK_MT8192_SCP_ADSP) += clk-mt8192-scp_adsp.o
obj-$(CONFIG_COMMON_CLK_MT8192_VDECSYS) += clk-mt8192-vdec.o
obj-$(CONFIG_COMMON_CLK_MT8192_VENCSYS) += clk-mt8192-venc.o
obj-$(CONFIG_COMMON_CLK_MT8195) += clk-mt8195-apmixedsys.o clk-mt8195-topckgen.o \
- clk-mt8195-peri_ao.o clk-mt8195-infra_ao.o \
- clk-mt8195-cam.o clk-mt8195-ccu.o clk-mt8195-img.o \
- clk-mt8195-ipe.o clk-mt8195-mfg.o clk-mt8195-scp_adsp.o \
- clk-mt8195-vdec.o clk-mt8195-vdo0.o clk-mt8195-vdo1.o \
- clk-mt8195-venc.o clk-mt8195-vpp0.o clk-mt8195-vpp1.o \
- clk-mt8195-wpe.o clk-mt8195-imp_iic_wrap.o \
- clk-mt8195-apusys_pll.o
-obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516.o
+ clk-mt8195-peri_ao.o clk-mt8195-infra_ao.o
+obj-$(CONFIG_COMMON_CLK_MT8195_APUSYS) += clk-mt8195-apusys_pll.o
+obj-$(CONFIG_COMMON_CLK_MT8195_CAMSYS) += clk-mt8195-cam.o clk-mt8195-ccu.o
+obj-$(CONFIG_COMMON_CLK_MT8195_IMGSYS) += clk-mt8195-img.o
+obj-$(CONFIG_COMMON_CLK_MT8195_IMP_IIC_WRAP) += clk-mt8195-imp_iic_wrap.o
+obj-$(CONFIG_COMMON_CLK_MT8195_IPESYS) += clk-mt8195-ipe.o
+obj-$(CONFIG_COMMON_CLK_MT8195_MFGCFG) += clk-mt8195-mfg.o
+obj-$(CONFIG_COMMON_CLK_MT8195_SCP_ADSP) += clk-mt8195-scp_adsp.o
+obj-$(CONFIG_COMMON_CLK_MT8195_VDECSYS) += clk-mt8195-vdec.o
+obj-$(CONFIG_COMMON_CLK_MT8195_VDOSYS) += clk-mt8195-vdo0.o clk-mt8195-vdo1.o
+obj-$(CONFIG_COMMON_CLK_MT8195_VENCSYS) += clk-mt8195-venc.o
+obj-$(CONFIG_COMMON_CLK_MT8195_VPPSYS) += clk-mt8195-vpp0.o clk-mt8195-vpp1.o
+obj-$(CONFIG_COMMON_CLK_MT8195_WPESYS) += clk-mt8195-wpe.o
+obj-$(CONFIG_COMMON_CLK_MT8196) += clk-mt8196-apmixedsys.o clk-mt8196-topckgen.o \
+ clk-mt8196-topckgen2.o clk-mt8196-vlpckgen.o \
+ clk-mt8196-peri_ao.o
+obj-$(CONFIG_COMMON_CLK_MT8196_IMP_IIC_WRAP) += clk-mt8196-imp_iic_wrap.o
+obj-$(CONFIG_COMMON_CLK_MT8196_MCUSYS) += clk-mt8196-mcu.o
+obj-$(CONFIG_COMMON_CLK_MT8196_MDPSYS) += clk-mt8196-mdpsys.o
+obj-$(CONFIG_COMMON_CLK_MT8196_MFGCFG) += clk-mt8196-mfg.o
+obj-$(CONFIG_COMMON_CLK_MT8196_MMSYS) += clk-mt8196-disp0.o clk-mt8196-disp1.o clk-mt8196-vdisp_ao.o \
+ clk-mt8196-ovl0.o clk-mt8196-ovl1.o
+obj-$(CONFIG_COMMON_CLK_MT8196_PEXTPSYS) += clk-mt8196-pextp.o
+obj-$(CONFIG_COMMON_CLK_MT8196_UFSSYS) += clk-mt8196-ufs_ao.o
+obj-$(CONFIG_COMMON_CLK_MT8196_VDECSYS) += clk-mt8196-vdec.o
+obj-$(CONFIG_COMMON_CLK_MT8196_VENCSYS) += clk-mt8196-venc.o
+obj-$(CONFIG_COMMON_CLK_MT8365) += clk-mt8365-apmixedsys.o clk-mt8365.o
+obj-$(CONFIG_COMMON_CLK_MT8365_APU) += clk-mt8365-apu.o
+obj-$(CONFIG_COMMON_CLK_MT8365_CAM) += clk-mt8365-cam.o
+obj-$(CONFIG_COMMON_CLK_MT8365_MFG) += clk-mt8365-mfg.o
+obj-$(CONFIG_COMMON_CLK_MT8365_MMSYS) += clk-mt8365-mm.o
+obj-$(CONFIG_COMMON_CLK_MT8365_VDEC) += clk-mt8365-vdec.o
+obj-$(CONFIG_COMMON_CLK_MT8365_VENC) += clk-mt8365-venc.o
+obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516-apmixedsys.o clk-mt8516.o
obj-$(CONFIG_COMMON_CLK_MT8516_AUDSYS) += clk-mt8516-aud.o
diff --git a/drivers/clk/mediatek/clk-apmixed.c b/drivers/clk/mediatek/clk-apmixed.c
index fc3d4146f482..60e34f124250 100644
--- a/drivers/clk/mediatek/clk-apmixed.c
+++ b/drivers/clk/mediatek/clk-apmixed.c
@@ -70,7 +70,7 @@ static const struct clk_ops mtk_ref2usb_tx_ops = {
.unprepare = mtk_ref2usb_tx_unprepare,
};
-struct clk_hw * __init mtk_clk_register_ref2usb_tx(const char *name,
+struct clk_hw *mtk_clk_register_ref2usb_tx(const char *name,
const char *parent_name, void __iomem *reg)
{
struct mtk_ref2usb_tx *tx;
@@ -98,5 +98,15 @@ struct clk_hw * __init mtk_clk_register_ref2usb_tx(const char *name,
return &tx->hw;
}
+EXPORT_SYMBOL_GPL(mtk_clk_register_ref2usb_tx);
+
+void mtk_clk_unregister_ref2usb_tx(struct clk_hw *hw)
+{
+ struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw);
+
+ clk_hw_unregister(hw);
+ kfree(tx);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_unregister_ref2usb_tx);
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-cpumux.c b/drivers/clk/mediatek/clk-cpumux.c
index 2b5d48591738..a03826db4dcb 100644
--- a/drivers/clk/mediatek/clk-cpumux.c
+++ b/drivers/clk/mediatek/clk-cpumux.c
@@ -53,12 +53,13 @@ static int clk_cpumux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_cpumux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_cpumux_get_parent,
.set_parent = clk_cpumux_set_parent,
};
static struct clk_hw *
-mtk_clk_register_cpumux(const struct mtk_composite *mux,
+mtk_clk_register_cpumux(struct device *dev, const struct mtk_composite *mux,
struct regmap *regmap)
{
struct mtk_clk_cpumux *cpumux;
@@ -81,7 +82,7 @@ mtk_clk_register_cpumux(const struct mtk_composite *mux,
cpumux->regmap = regmap;
cpumux->hw.init = &init;
- ret = clk_hw_register(NULL, &cpumux->hw);
+ ret = clk_hw_register(dev, &cpumux->hw);
if (ret) {
kfree(cpumux);
return ERR_PTR(ret);
@@ -102,7 +103,7 @@ static void mtk_clk_unregister_cpumux(struct clk_hw *hw)
kfree(cpumux);
}
-int mtk_clk_register_cpumuxes(struct device_node *node,
+int mtk_clk_register_cpumuxes(struct device *dev, struct device_node *node,
const struct mtk_composite *clks, int num,
struct clk_hw_onecell_data *clk_data)
{
@@ -125,7 +126,7 @@ int mtk_clk_register_cpumuxes(struct device_node *node,
continue;
}
- hw = mtk_clk_register_cpumux(mux, regmap);
+ hw = mtk_clk_register_cpumux(dev, mux, regmap);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", mux->name,
hw);
@@ -150,6 +151,7 @@ err:
return PTR_ERR(hw);
}
+EXPORT_SYMBOL_GPL(mtk_clk_register_cpumuxes);
void mtk_clk_unregister_cpumuxes(const struct mtk_composite *clks, int num,
struct clk_hw_onecell_data *clk_data)
@@ -166,5 +168,6 @@ void mtk_clk_unregister_cpumuxes(const struct mtk_composite *clks, int num,
clk_data->hws[mux->id] = ERR_PTR(-ENOENT);
}
}
+EXPORT_SYMBOL_GPL(mtk_clk_unregister_cpumuxes);
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-cpumux.h b/drivers/clk/mediatek/clk-cpumux.h
index 325adbef25d1..64e45c63b4a0 100644
--- a/drivers/clk/mediatek/clk-cpumux.h
+++ b/drivers/clk/mediatek/clk-cpumux.h
@@ -11,7 +11,7 @@ struct clk_hw_onecell_data;
struct device_node;
struct mtk_composite;
-int mtk_clk_register_cpumuxes(struct device_node *node,
+int mtk_clk_register_cpumuxes(struct device *dev, struct device_node *node,
const struct mtk_composite *clks, int num,
struct clk_hw_onecell_data *clk_data);
diff --git a/drivers/clk/mediatek/clk-fhctl.c b/drivers/clk/mediatek/clk-fhctl.c
new file mode 100644
index 000000000000..33b6ad8fdc2e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-fhctl.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <edward-jw.yang@mediatek.com>
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "clk-mtk.h"
+#include "clk-pllfh.h"
+#include "clk-fhctl.h"
+
+#define PERCENT_TO_DDSLMT(dds, percent_m10) \
+ ((((dds) * (percent_m10)) >> 5) / 100)
+
+static const struct fhctl_offset fhctl_offset_v1 = {
+ .offset_hp_en = 0x0,
+ .offset_clk_con = 0x4,
+ .offset_rst_con = 0x8,
+ .offset_slope0 = 0xc,
+ .offset_slope1 = 0x10,
+ .offset_cfg = 0x0,
+ .offset_updnlmt = 0x4,
+ .offset_dds = 0x8,
+ .offset_dvfs = 0xc,
+ .offset_mon = 0x10,
+};
+
+static const struct fhctl_offset fhctl_offset_v2 = {
+ .offset_hp_en = 0x0,
+ .offset_clk_con = 0x8,
+ .offset_rst_con = 0xc,
+ .offset_slope0 = 0x10,
+ .offset_slope1 = 0x14,
+ .offset_cfg = 0x0,
+ .offset_updnlmt = 0x4,
+ .offset_dds = 0x8,
+ .offset_dvfs = 0xc,
+ .offset_mon = 0x10,
+};
+
+const struct fhctl_offset *fhctl_get_offset_table(enum fhctl_variant v)
+{
+ switch (v) {
+ case FHCTL_PLLFH_V1:
+ return &fhctl_offset_v1;
+ case FHCTL_PLLFH_V2:
+ return &fhctl_offset_v2;
+ default:
+ return ERR_PTR(-EINVAL);
+ };
+}
+
+static void dump_hw(struct mtk_clk_pll *pll, struct fh_pll_regs *regs,
+ const struct fh_pll_data *data)
+{
+ pr_info("hp_en<%x>,clk_con<%x>,slope0<%x>,slope1<%x>\n",
+ readl(regs->reg_hp_en), readl(regs->reg_clk_con),
+ readl(regs->reg_slope0), readl(regs->reg_slope1));
+ pr_info("cfg<%x>,lmt<%x>,dds<%x>,dvfs<%x>,mon<%x>\n",
+ readl(regs->reg_cfg), readl(regs->reg_updnlmt),
+ readl(regs->reg_dds), readl(regs->reg_dvfs),
+ readl(regs->reg_mon));
+ pr_info("pcw<%x>\n", readl(pll->pcw_addr));
+}
+
+static int fhctl_set_ssc_regs(struct mtk_clk_pll *pll, struct fh_pll_regs *regs,
+ const struct fh_pll_data *data, u32 rate)
+{
+ u32 updnlmt_val, r;
+
+ writel((readl(regs->reg_cfg) & ~(data->frddsx_en)), regs->reg_cfg);
+ writel((readl(regs->reg_cfg) & ~(data->sfstrx_en)), regs->reg_cfg);
+ writel((readl(regs->reg_cfg) & ~(data->fhctlx_en)), regs->reg_cfg);
+
+ if (rate > 0) {
+ /* Set the relative parameter registers (dt/df/upbnd/downbnd) */
+ r = readl(regs->reg_cfg);
+ r &= ~(data->msk_frddsx_dys);
+ r |= (data->df_val << (ffs(data->msk_frddsx_dys) - 1));
+ writel(r, regs->reg_cfg);
+
+ r = readl(regs->reg_cfg);
+ r &= ~(data->msk_frddsx_dts);
+ r |= (data->dt_val << (ffs(data->msk_frddsx_dts) - 1));
+ writel(r, regs->reg_cfg);
+
+ writel((readl(pll->pcw_addr) & data->dds_mask) | data->tgl_org,
+ regs->reg_dds);
+
+ /* Calculate UPDNLMT */
+ updnlmt_val = PERCENT_TO_DDSLMT((readl(regs->reg_dds) &
+ data->dds_mask), rate) <<
+ data->updnlmt_shft;
+
+ writel(updnlmt_val, regs->reg_updnlmt);
+ writel(readl(regs->reg_hp_en) | BIT(data->fh_id),
+ regs->reg_hp_en);
+ /* Enable SSC */
+ writel(readl(regs->reg_cfg) | data->frddsx_en, regs->reg_cfg);
+ /* Enable Hopping control */
+ writel(readl(regs->reg_cfg) | data->fhctlx_en, regs->reg_cfg);
+
+ } else {
+ /* Switch to APMIXEDSYS control */
+ writel(readl(regs->reg_hp_en) & ~BIT(data->fh_id),
+ regs->reg_hp_en);
+ /* Wait for DDS to be stable */
+ udelay(30);
+ }
+
+ return 0;
+}
+
+static int hopping_hw_flow(struct mtk_clk_pll *pll, struct fh_pll_regs *regs,
+ const struct fh_pll_data *data,
+ struct fh_pll_state *state, unsigned int new_dds)
+{
+ u32 dds_mask = data->dds_mask;
+ u32 mon_dds = 0;
+ u32 con_pcw_tmp;
+ int ret;
+
+ if (state->ssc_rate)
+ fhctl_set_ssc_regs(pll, regs, data, 0);
+
+ writel((readl(pll->pcw_addr) & dds_mask) | data->tgl_org,
+ regs->reg_dds);
+
+ writel(readl(regs->reg_cfg) | data->sfstrx_en, regs->reg_cfg);
+ writel(readl(regs->reg_cfg) | data->fhctlx_en, regs->reg_cfg);
+ writel(data->slope0_value, regs->reg_slope0);
+ writel(data->slope1_value, regs->reg_slope1);
+
+ writel(readl(regs->reg_hp_en) | BIT(data->fh_id), regs->reg_hp_en);
+ writel((new_dds) | (data->dvfs_tri), regs->reg_dvfs);
+
+ /* Wait 1000 us until DDS stable */
+ ret = readl_poll_timeout_atomic(regs->reg_mon, mon_dds,
+ (mon_dds & dds_mask) == new_dds,
+ 10, 1000);
+ if (ret) {
+ pr_warn("%s: FHCTL hopping timeout\n", pll->data->name);
+ dump_hw(pll, regs, data);
+ }
+
+ con_pcw_tmp = readl(pll->pcw_addr) & (~dds_mask);
+ con_pcw_tmp = (con_pcw_tmp | (readl(regs->reg_mon) & dds_mask) |
+ data->pcwchg);
+
+ writel(con_pcw_tmp, pll->pcw_addr);
+ writel(readl(regs->reg_hp_en) & ~BIT(data->fh_id), regs->reg_hp_en);
+
+ if (state->ssc_rate)
+ fhctl_set_ssc_regs(pll, regs, data, state->ssc_rate);
+
+ return ret;
+}
+
+static unsigned int __get_postdiv(struct mtk_clk_pll *pll)
+{
+ unsigned int regval;
+
+ regval = readl(pll->pd_addr) >> pll->data->pd_shift;
+ regval &= POSTDIV_MASK;
+
+ return BIT(regval);
+}
+
+static void __set_postdiv(struct mtk_clk_pll *pll, unsigned int postdiv)
+{
+ unsigned int regval;
+
+ regval = readl(pll->pd_addr);
+ regval &= ~(POSTDIV_MASK << pll->data->pd_shift);
+ regval |= (ffs(postdiv) - 1) << pll->data->pd_shift;
+ writel(regval, pll->pd_addr);
+}
+
+static int fhctl_hopping(struct mtk_fh *fh, unsigned int new_dds,
+ unsigned int postdiv)
+{
+ const struct fh_pll_data *data = &fh->pllfh_data->data;
+ struct fh_pll_state *state = &fh->pllfh_data->state;
+ struct fh_pll_regs *regs = &fh->regs;
+ struct mtk_clk_pll *pll = &fh->clk_pll;
+ spinlock_t *lock = fh->lock;
+ unsigned int pll_postdiv;
+ unsigned long flags = 0;
+ int ret;
+
+ if (postdiv) {
+ pll_postdiv = __get_postdiv(pll);
+
+ if (postdiv > pll_postdiv)
+ __set_postdiv(pll, postdiv);
+ }
+
+ spin_lock_irqsave(lock, flags);
+
+ ret = hopping_hw_flow(pll, regs, data, state, new_dds);
+
+ spin_unlock_irqrestore(lock, flags);
+
+ if (postdiv && postdiv < pll_postdiv)
+ __set_postdiv(pll, postdiv);
+
+ return ret;
+}
+
+static int fhctl_ssc_enable(struct mtk_fh *fh, u32 rate)
+{
+ const struct fh_pll_data *data = &fh->pllfh_data->data;
+ struct fh_pll_state *state = &fh->pllfh_data->state;
+ struct fh_pll_regs *regs = &fh->regs;
+ struct mtk_clk_pll *pll = &fh->clk_pll;
+ spinlock_t *lock = fh->lock;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(lock, flags);
+
+ fhctl_set_ssc_regs(pll, regs, data, rate);
+ state->ssc_rate = rate;
+
+ spin_unlock_irqrestore(lock, flags);
+
+ return 0;
+}
+
+static const struct fh_operation fhctl_ops = {
+ .hopping = fhctl_hopping,
+ .ssc_enable = fhctl_ssc_enable,
+};
+
+const struct fh_operation *fhctl_get_ops(void)
+{
+ return &fhctl_ops;
+}
+
+void fhctl_hw_init(struct mtk_fh *fh)
+{
+ const struct fh_pll_data data = fh->pllfh_data->data;
+ struct fh_pll_state state = fh->pllfh_data->state;
+ struct fh_pll_regs regs = fh->regs;
+ u32 val;
+
+ /* initial hw register */
+ val = readl(regs.reg_clk_con) | BIT(data.fh_id);
+ writel(val, regs.reg_clk_con);
+
+ val = readl(regs.reg_rst_con) & ~BIT(data.fh_id);
+ writel(val, regs.reg_rst_con);
+ val = readl(regs.reg_rst_con) | BIT(data.fh_id);
+ writel(val, regs.reg_rst_con);
+
+ writel(0x0, regs.reg_cfg);
+ writel(0x0, regs.reg_updnlmt);
+ writel(0x0, regs.reg_dds);
+
+ /* enable ssc if needed */
+ if (state.ssc_rate)
+ fh->ops->ssc_enable(fh, state.ssc_rate);
+}
diff --git a/drivers/clk/mediatek/clk-fhctl.h b/drivers/clk/mediatek/clk-fhctl.h
new file mode 100644
index 000000000000..bfa6d281a3ee
--- /dev/null
+++ b/drivers/clk/mediatek/clk-fhctl.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <edward-jw.yang@mediatek.com>
+ */
+
+#ifndef __CLK_FHCTL_H
+#define __CLK_FHCTL_H
+
+#include "clk-pllfh.h"
+
+enum fhctl_variant {
+ FHCTL_PLLFH_V1,
+ FHCTL_PLLFH_V2,
+};
+
+struct fhctl_offset {
+ u32 offset_hp_en;
+ u32 offset_clk_con;
+ u32 offset_rst_con;
+ u32 offset_slope0;
+ u32 offset_slope1;
+ u32 offset_cfg;
+ u32 offset_updnlmt;
+ u32 offset_dds;
+ u32 offset_dvfs;
+ u32 offset_mon;
+};
+const struct fhctl_offset *fhctl_get_offset_table(enum fhctl_variant v);
+const struct fh_operation *fhctl_get_ops(void);
+void fhctl_hw_init(struct mtk_fh *fh);
+
+#endif
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
index 421806236228..f6b1429ff757 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -5,6 +5,7 @@
*/
#include <linux/clk-provider.h>
+#include <linux/dev_printk.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/printk.h>
@@ -12,15 +13,14 @@
#include <linux/slab.h>
#include <linux/types.h>
+#include "clk-mtk.h"
#include "clk-gate.h"
struct mtk_clk_gate {
struct clk_hw hw;
struct regmap *regmap;
- int set_ofs;
- int clr_ofs;
- int sta_ofs;
- u8 bit;
+ struct regmap *regmap_hwv;
+ const struct mtk_gate *gate;
};
static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw)
@@ -33,9 +33,9 @@ static u32 mtk_get_clockgating(struct clk_hw *hw)
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
u32 val;
- regmap_read(cg->regmap, cg->sta_ofs, &val);
+ regmap_read(cg->regmap, cg->gate->regs->sta_ofs, &val);
- return val & BIT(cg->bit);
+ return val & BIT(cg->gate->shift);
}
static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
@@ -52,28 +52,30 @@ static void mtk_cg_set_bit(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
- regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
+ regmap_write(cg->regmap, cg->gate->regs->set_ofs, BIT(cg->gate->shift));
}
static void mtk_cg_clr_bit(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
- regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
+ regmap_write(cg->regmap, cg->gate->regs->clr_ofs, BIT(cg->gate->shift));
}
static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
- regmap_set_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
+ regmap_set_bits(cg->regmap, cg->gate->regs->sta_ofs,
+ BIT(cg->gate->shift));
}
static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
- regmap_clear_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
+ regmap_clear_bits(cg->regmap, cg->gate->regs->sta_ofs,
+ BIT(cg->gate->shift));
}
static int mtk_cg_enable(struct clk_hw *hw)
@@ -100,6 +102,32 @@ static void mtk_cg_disable_inv(struct clk_hw *hw)
mtk_cg_clr_bit(hw);
}
+static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable)
+{
+ struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
+ u32 val;
+
+ regmap_write(cg->regmap_hwv,
+ enable ? cg->gate->hwv_regs->set_ofs :
+ cg->gate->hwv_regs->clr_ofs,
+ BIT(cg->gate->shift));
+
+ return regmap_read_poll_timeout_atomic(cg->regmap_hwv,
+ cg->gate->hwv_regs->sta_ofs, val,
+ val & BIT(cg->gate->shift), 0,
+ MTK_WAIT_HWV_DONE_US);
+}
+
+static int mtk_cg_hwv_enable(struct clk_hw *hw)
+{
+ return mtk_cg_hwv_set_en(hw, true);
+}
+
+static void mtk_cg_hwv_disable(struct clk_hw *hw)
+{
+ mtk_cg_hwv_set_en(hw, false);
+}
+
static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
{
mtk_cg_clr_bit_no_setclr(hw);
@@ -124,6 +152,15 @@ static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
mtk_cg_clr_bit_no_setclr(hw);
}
+static bool mtk_cg_uses_hwv(const struct clk_ops *ops)
+{
+ if (ops == &mtk_clk_gate_hwv_ops_setclr ||
+ ops == &mtk_clk_gate_hwv_ops_setclr_inv)
+ return true;
+
+ return false;
+}
+
const struct clk_ops mtk_clk_gate_ops_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable,
@@ -138,6 +175,20 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv);
+const struct clk_ops mtk_clk_gate_hwv_ops_setclr = {
+ .is_enabled = mtk_cg_bit_is_cleared,
+ .enable = mtk_cg_hwv_enable,
+ .disable = mtk_cg_hwv_disable,
+};
+EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr);
+
+const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = {
+ .is_enabled = mtk_cg_bit_is_set,
+ .enable = mtk_cg_hwv_enable,
+ .disable = mtk_cg_hwv_disable,
+};
+EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv);
+
const struct clk_ops mtk_clk_gate_ops_no_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable_no_setclr,
@@ -152,12 +203,10 @@ const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv);
-static struct clk_hw *mtk_clk_register_gate(const char *name,
- const char *parent_name,
- struct regmap *regmap, int set_ofs,
- int clr_ofs, int sta_ofs, u8 bit,
- const struct clk_ops *ops,
- unsigned long flags, struct device *dev)
+static struct clk_hw *mtk_clk_register_gate(struct device *dev,
+ const struct mtk_gate *gate,
+ struct regmap *regmap,
+ struct regmap *regmap_hwv)
{
struct mtk_clk_gate *cg;
int ret;
@@ -167,18 +216,19 @@ static struct clk_hw *mtk_clk_register_gate(const char *name,
if (!cg)
return ERR_PTR(-ENOMEM);
- init.name = name;
- init.flags = flags | CLK_SET_RATE_PARENT;
- init.parent_names = parent_name ? &parent_name : NULL;
- init.num_parents = parent_name ? 1 : 0;
- init.ops = ops;
+ init.name = gate->name;
+ init.flags = gate->flags | CLK_SET_RATE_PARENT;
+ init.parent_names = gate->parent_name ? &gate->parent_name : NULL;
+ init.num_parents = gate->parent_name ? 1 : 0;
+ init.ops = gate->ops;
+ if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv)
+ return dev_err_ptr_probe(
+ dev, -ENXIO,
+ "regmap not found for hardware voter clocks\n");
cg->regmap = regmap;
- cg->set_ofs = set_ofs;
- cg->clr_ofs = clr_ofs;
- cg->sta_ofs = sta_ofs;
- cg->bit = bit;
-
+ cg->regmap_hwv = regmap_hwv;
+ cg->gate = gate;
cg->hw.init = &init;
ret = clk_hw_register(dev, &cg->hw);
@@ -202,14 +252,14 @@ static void mtk_clk_unregister_gate(struct clk_hw *hw)
kfree(cg);
}
-int mtk_clk_register_gates_with_dev(struct device_node *node,
- const struct mtk_gate *clks, int num,
- struct clk_hw_onecell_data *clk_data,
- struct device *dev)
+int mtk_clk_register_gates(struct device *dev, struct device_node *node,
+ const struct mtk_gate *clks, int num,
+ struct clk_hw_onecell_data *clk_data)
{
int i;
struct clk_hw *hw;
struct regmap *regmap;
+ struct regmap *regmap_hwv;
if (!clk_data)
return -ENOMEM;
@@ -220,6 +270,12 @@ int mtk_clk_register_gates_with_dev(struct device_node *node,
return PTR_ERR(regmap);
}
+ regmap_hwv = mtk_clk_get_hwv_regmap(node);
+ if (IS_ERR(regmap_hwv))
+ return dev_err_probe(
+ dev, PTR_ERR(regmap_hwv),
+ "Cannot find hardware voter regmap for %pOF\n", node);
+
for (i = 0; i < num; i++) {
const struct mtk_gate *gate = &clks[i];
@@ -229,13 +285,7 @@ int mtk_clk_register_gates_with_dev(struct device_node *node,
continue;
}
- hw = mtk_clk_register_gate(gate->name, gate->parent_name,
- regmap,
- gate->regs->set_ofs,
- gate->regs->clr_ofs,
- gate->regs->sta_ofs,
- gate->shift, gate->ops,
- gate->flags, dev);
+ hw = mtk_clk_register_gate(dev, gate, regmap, regmap_hwv);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", gate->name,
@@ -261,13 +311,6 @@ err:
return PTR_ERR(hw);
}
-
-int mtk_clk_register_gates(struct device_node *node,
- const struct mtk_gate *clks, int num,
- struct clk_hw_onecell_data *clk_data)
-{
- return mtk_clk_register_gates_with_dev(node, clks, num, clk_data, NULL);
-}
EXPORT_SYMBOL_GPL(mtk_clk_register_gates);
void mtk_clk_unregister_gates(const struct mtk_gate *clks, int num,
diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
index d9897ef53528..4f05b9855dae 100644
--- a/drivers/clk/mediatek/clk-gate.h
+++ b/drivers/clk/mediatek/clk-gate.h
@@ -19,6 +19,8 @@ extern const struct clk_ops mtk_clk_gate_ops_setclr;
extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
extern const struct clk_ops mtk_clk_gate_ops_no_setclr;
extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv;
+extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr;
+extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv;
struct mtk_gate_regs {
u32 sta_ofs;
@@ -31,6 +33,7 @@ struct mtk_gate {
const char *name;
const char *parent_name;
const struct mtk_gate_regs *regs;
+ const struct mtk_gate_regs *hwv_regs;
int shift;
const struct clk_ops *ops;
unsigned long flags;
@@ -50,15 +53,10 @@ struct mtk_gate {
#define GATE_MTK(_id, _name, _parent, _regs, _shift, _ops) \
GATE_MTK_FLAGS(_id, _name, _parent, _regs, _shift, _ops, 0)
-int mtk_clk_register_gates(struct device_node *node,
+int mtk_clk_register_gates(struct device *dev, struct device_node *node,
const struct mtk_gate *clks, int num,
struct clk_hw_onecell_data *clk_data);
-int mtk_clk_register_gates_with_dev(struct device_node *node,
- const struct mtk_gate *clks, int num,
- struct clk_hw_onecell_data *clk_data,
- struct device *dev);
-
void mtk_clk_unregister_gates(const struct mtk_gate *clks, int num,
struct clk_hw_onecell_data *clk_data);
diff --git a/drivers/clk/mediatek/clk-mt2701-aud.c b/drivers/clk/mediatek/clk-mt2701-aud.c
index 6ba398eb7df9..e103121cf58e 100644
--- a/drivers/clk/mediatek/clk-mt2701-aud.c
+++ b/drivers/clk/mediatek/clk-mt2701-aud.c
@@ -7,7 +7,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -15,41 +15,17 @@
#include <dt-bindings/clock/mt2701-clk.h>
-#define GATE_AUDIO0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio2_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO3(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio3_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO3(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio3_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate_regs audio0_cg_regs = {
.set_ofs = 0x0,
@@ -76,12 +52,19 @@ static const struct mtk_gate_regs audio3_cg_regs = {
};
static const struct mtk_gate audio_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "aud_dummy"),
/* AUDIO0 */
GATE_AUDIO0(CLK_AUD_AFE, "audio_afe", "aud_intbus_sel", 2),
+ GATE_DUMMY(CLK_AUD_LRCK_DETECT, "audio_lrck_detect_dummy"),
+ GATE_DUMMY(CLK_AUD_I2S, "audio_i2c_dummy"),
+ GATE_DUMMY(CLK_AUD_APLL_TUNER, "audio_apll_tuner_dummy"),
GATE_AUDIO0(CLK_AUD_HDMI, "audio_hdmi", "audpll_sel", 20),
GATE_AUDIO0(CLK_AUD_SPDF, "audio_spdf", "audpll_sel", 21),
GATE_AUDIO0(CLK_AUD_SPDF2, "audio_spdf2", "audpll_sel", 22),
GATE_AUDIO0(CLK_AUD_APLL, "audio_apll", "audpll_sel", 23),
+ GATE_DUMMY(CLK_AUD_TML, "audio_tml_dummy"),
+ GATE_DUMMY(CLK_AUD_AHB_IDLE_EXT, "audio_ahb_idle_ext_dummy"),
+ GATE_DUMMY(CLK_AUD_AHB_IDLE_INT, "audio_ahb_idle_int_dummy"),
/* AUDIO1 */
GATE_AUDIO1(CLK_AUD_I2SIN1, "audio_i2sin1", "aud_mux1_sel", 0),
GATE_AUDIO1(CLK_AUD_I2SIN2, "audio_i2sin2", "aud_mux1_sel", 1),
@@ -99,10 +82,12 @@ static const struct mtk_gate audio_clks[] = {
GATE_AUDIO1(CLK_AUD_ASRCI2, "audio_asrci2", "asm_h_sel", 13),
GATE_AUDIO1(CLK_AUD_ASRCO1, "audio_asrco1", "asm_h_sel", 14),
GATE_AUDIO1(CLK_AUD_ASRCO2, "audio_asrco2", "asm_h_sel", 15),
+ GATE_DUMMY(CLK_AUD_HDMIRX, "audio_hdmirx_dummy"),
GATE_AUDIO1(CLK_AUD_INTDIR, "audio_intdir", "intdir_sel", 20),
GATE_AUDIO1(CLK_AUD_A1SYS, "audio_a1sys", "aud_mux1_sel", 21),
GATE_AUDIO1(CLK_AUD_A2SYS, "audio_a2sys", "aud_mux2_sel", 22),
GATE_AUDIO1(CLK_AUD_AFE_CONN, "audio_afe_conn", "aud_mux1_sel", 23),
+ GATE_DUMMY(CLK_AUD_AFE_PCMIF, "audio_afe_pcmif_dummy"),
GATE_AUDIO1(CLK_AUD_AFE_MRGIF, "audio_afe_mrgif", "aud_mux1_sel", 25),
/* AUDIO2 */
GATE_AUDIO2(CLK_AUD_MMIF_UL1, "audio_ul1", "aud_mux1_sel", 0),
@@ -123,6 +108,8 @@ static const struct mtk_gate audio_clks[] = {
GATE_AUDIO2(CLK_AUD_MMIF_AWB2, "audio_awb2", "aud_mux1_sel", 15),
GATE_AUDIO2(CLK_AUD_MMIF_DAI, "audio_dai", "aud_mux1_sel", 16),
/* AUDIO3 */
+ GATE_DUMMY(CLK_AUD_DMIC1, "audio_dmic1_dummy"),
+ GATE_DUMMY(CLK_AUD_DMIC2, "audio_dmic2_dummy"),
GATE_AUDIO3(CLK_AUD_ASRCI3, "audio_asrci3", "asm_h_sel", 2),
GATE_AUDIO3(CLK_AUD_ASRCI4, "audio_asrci4", "asm_h_sel", 3),
GATE_AUDIO3(CLK_AUD_ASRCI5, "audio_asrci5", "asm_h_sel", 4),
@@ -138,29 +125,28 @@ static const struct mtk_gate audio_clks[] = {
GATE_AUDIO3(CLK_AUD_MEM_ASRC5, "audio_mem_asrc5", "asm_h_sel", 14),
};
+static const struct mtk_clk_desc audio_desc = {
+ .clks = audio_clks,
+ .num_clks = ARRAY_SIZE(audio_clks),
+};
+
static const struct of_device_id of_match_clk_mt2701_aud[] = {
- { .compatible = "mediatek,mt2701-audsys", },
- {}
+ { .compatible = "mediatek,mt2701-audsys", .data = &audio_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_aud);
static int clk_mt2701_aud_probe(struct platform_device *pdev)
{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
int r;
- clk_data = mtk_alloc_clk_data(CLK_AUD_NR);
-
- mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ r = mtk_clk_simple_probe(pdev);
if (r) {
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
- goto err_clk_provider;
+ return r;
}
r = devm_of_platform_populate(&pdev->dev);
@@ -170,17 +156,25 @@ static int clk_mt2701_aud_probe(struct platform_device *pdev)
return 0;
err_plat_populate:
- of_clk_del_provider(node);
-err_clk_provider:
+ mtk_clk_simple_remove(pdev);
return r;
}
+static void clk_mt2701_aud_remove(struct platform_device *pdev)
+{
+ of_platform_depopulate(&pdev->dev);
+ mtk_clk_simple_remove(pdev);
+}
+
static struct platform_driver clk_mt2701_aud_drv = {
.probe = clk_mt2701_aud_probe,
+ .remove = clk_mt2701_aud_remove,
.driver = {
.name = "clk-mt2701-aud",
.of_match_table = of_match_clk_mt2701_aud,
},
};
+module_platform_driver(clk_mt2701_aud_drv);
-builtin_platform_driver(clk_mt2701_aud_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 audio clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-bdp.c b/drivers/clk/mediatek/clk-mt2701-bdp.c
index 662a8ab3fbb1..f11c7a4fa37b 100644
--- a/drivers/clk/mediatek/clk-mt2701-bdp.c
+++ b/drivers/clk/mediatek/clk-mt2701-bdp.c
@@ -24,25 +24,14 @@ static const struct mtk_gate_regs bdp1_cg_regs = {
.sta_ofs = 0x0110,
};
-#define GATE_BDP0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &bdp0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_BDP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &bdp0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
-#define GATE_BDP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &bdp1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_BDP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &bdp1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate bdp_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "bdp_dummy"),
GATE_BDP0(CLK_BDP_BRG_BA, "brg_baclk", "mm_sel", 0),
GATE_BDP0(CLK_BDP_BRG_DRAM, "brg_dram", "mm_sel", 1),
GATE_BDP0(CLK_BDP_LARB_DRAM, "larb_dram", "mm_sel", 2),
@@ -94,37 +83,30 @@ static const struct mtk_gate bdp_clks[] = {
GATE_BDP1(CLK_BDP_HDMI_MON, "hdmi_mon", "hdmi_0_pll340m", 16),
};
-static const struct of_device_id of_match_clk_mt2701_bdp[] = {
- { .compatible = "mediatek,mt2701-bdpsys", },
- {}
+static const struct mtk_clk_desc bdp_desc = {
+ .clks = bdp_clks,
+ .num_clks = ARRAY_SIZE(bdp_clks),
};
-static int clk_mt2701_bdp_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_BDP_NR);
-
- mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt2701_bdp[] = {
+ {
+ .compatible = "mediatek,mt2701-bdpsys",
+ .data = &bdp_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_bdp);
static struct platform_driver clk_mt2701_bdp_drv = {
- .probe = clk_mt2701_bdp_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2701-bdp",
.of_match_table = of_match_clk_mt2701_bdp,
},
};
+module_platform_driver(clk_mt2701_bdp_drv);
-builtin_platform_driver(clk_mt2701_bdp_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 BDP clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-eth.c b/drivers/clk/mediatek/clk-mt2701-eth.c
index edf1e2ed2b59..608252e73f24 100644
--- a/drivers/clk/mediatek/clk-mt2701-eth.c
+++ b/drivers/clk/mediatek/clk-mt2701-eth.c
@@ -16,16 +16,11 @@ static const struct mtk_gate_regs eth_cg_regs = {
.sta_ofs = 0x0030,
};
-#define GATE_ETH(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &eth_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_ETH(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &eth_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate eth_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "eth_dummy"),
GATE_ETH(CLK_ETHSYS_HSDMA, "hsdma_clk", "ethif_sel", 5),
GATE_ETH(CLK_ETHSYS_ESW, "esw_clk", "ethpll_500m_ck", 6),
GATE_ETH(CLK_ETHSYS_GP2, "gp2_clk", "trgpll", 7),
@@ -44,39 +39,27 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(rst_ofs),
};
-static const struct of_device_id of_match_clk_mt2701_eth[] = {
- { .compatible = "mediatek,mt2701-ethsys", },
- {}
+static const struct mtk_clk_desc eth_desc = {
+ .clks = eth_clks,
+ .num_clks = ARRAY_SIZE(eth_clks),
+ .rst_desc = &clk_rst_desc,
};
-static int clk_mt2701_eth_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_ETHSYS_NR);
-
- mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt2701_eth[] = {
+ { .compatible = "mediatek,mt2701-ethsys", .data = &eth_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_eth);
static struct platform_driver clk_mt2701_eth_drv = {
- .probe = clk_mt2701_eth_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2701-eth",
.of_match_table = of_match_clk_mt2701_eth,
},
};
+module_platform_driver(clk_mt2701_eth_drv);
-builtin_platform_driver(clk_mt2701_eth_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 Ethernet clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-g3d.c b/drivers/clk/mediatek/clk-mt2701-g3d.c
index 1458109d99d9..b3e18b6db75d 100644
--- a/drivers/clk/mediatek/clk-mt2701-g3d.c
+++ b/drivers/clk/mediatek/clk-mt2701-g3d.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -16,14 +14,8 @@
#include <dt-bindings/clock/mt2701-clk.h>
-#define GATE_G3D(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &g3d_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_G3D(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &g3d_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate_regs g3d_cg_regs = {
.sta_ofs = 0x0,
@@ -32,6 +24,7 @@ static const struct mtk_gate_regs g3d_cg_regs = {
};
static const struct mtk_gate g3d_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "g3d_dummy"),
GATE_G3D(CLK_G3DSYS_CORE, "g3d_core", "mfg_sel", 0),
};
@@ -43,61 +36,27 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(rst_ofs),
};
-static int clk_mt2701_g3dsys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_G3DSYS_NR);
-
- mtk_clk_register_gates(node, g3d_clks, ARRAY_SIZE(g3d_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return r;
-}
+static const struct mtk_clk_desc g3d_desc = {
+ .clks = g3d_clks,
+ .num_clks = ARRAY_SIZE(g3d_clks),
+ .rst_desc = &clk_rst_desc,
+};
static const struct of_device_id of_match_clk_mt2701_g3d[] = {
- {
- .compatible = "mediatek,mt2701-g3dsys",
- .data = clk_mt2701_g3dsys_init,
- }, {
- /* sentinel */
- }
+ { .compatible = "mediatek,mt2701-g3dsys", .data = &g3d_desc },
+ { /* sentinel */ }
};
-
-static int clk_mt2701_g3d_probe(struct platform_device *pdev)
-{
- int (*clk_init)(struct platform_device *);
- int r;
-
- clk_init = of_device_get_match_data(&pdev->dev);
- if (!clk_init)
- return -EINVAL;
-
- r = clk_init(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_g3d);
static struct platform_driver clk_mt2701_g3d_drv = {
- .probe = clk_mt2701_g3d_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2701-g3d",
.of_match_table = of_match_clk_mt2701_g3d,
},
};
+module_platform_driver(clk_mt2701_g3d_drv);
-builtin_platform_driver(clk_mt2701_g3d_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 GPU g3d clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-hif.c b/drivers/clk/mediatek/clk-mt2701-hif.c
index 434cbbe8c037..000e00576052 100644
--- a/drivers/clk/mediatek/clk-mt2701-hif.c
+++ b/drivers/clk/mediatek/clk-mt2701-hif.c
@@ -16,16 +16,11 @@ static const struct mtk_gate_regs hif_cg_regs = {
.sta_ofs = 0x0030,
};
-#define GATE_HIF(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &hif_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_HIF(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &hif_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate hif_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "hif_dummy"),
GATE_HIF(CLK_HIFSYS_USB0PHY, "usb0_phy_clk", "ethpll_500m_ck", 21),
GATE_HIF(CLK_HIFSYS_USB1PHY, "usb1_phy_clk", "ethpll_500m_ck", 22),
GATE_HIF(CLK_HIFSYS_PCIE0, "pcie0_clk", "ethpll_500m_ck", 24),
@@ -41,41 +36,27 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(rst_ofs),
};
-static const struct of_device_id of_match_clk_mt2701_hif[] = {
- { .compatible = "mediatek,mt2701-hifsys", },
- {}
+static const struct mtk_clk_desc hif_desc = {
+ .clks = hif_clks,
+ .num_clks = ARRAY_SIZE(hif_clks),
+ .rst_desc = &clk_rst_desc,
};
-static int clk_mt2701_hif_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_HIFSYS_NR);
-
- mtk_clk_register_gates(node, hif_clks, ARRAY_SIZE(hif_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r) {
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
- return r;
- }
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return 0;
-}
+static const struct of_device_id of_match_clk_mt2701_hif[] = {
+ { .compatible = "mediatek,mt2701-hifsys", .data = &hif_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_hif);
static struct platform_driver clk_mt2701_hif_drv = {
- .probe = clk_mt2701_hif_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2701-hif",
.of_match_table = of_match_clk_mt2701_hif,
},
};
+module_platform_driver(clk_mt2701_hif_drv);
-builtin_platform_driver(clk_mt2701_hif_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 HIFSYS clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-img.c b/drivers/clk/mediatek/clk-mt2701-img.c
index c4f3cd26df60..c158e54c4652 100644
--- a/drivers/clk/mediatek/clk-mt2701-img.c
+++ b/drivers/clk/mediatek/clk-mt2701-img.c
@@ -18,16 +18,11 @@ static const struct mtk_gate_regs img_cg_regs = {
.sta_ofs = 0x0000,
};
-#define GATE_IMG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &img_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IMG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate img_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "img_dummy"),
GATE_IMG(CLK_IMG_SMI_COMM, "img_smi_comm", "mm_sel", 0),
GATE_IMG(CLK_IMG_RESZ, "img_resz", "mm_sel", 1),
GATE_IMG(CLK_IMG_JPGDEC_SMI, "img_jpgdec_smi", "mm_sel", 5),
@@ -36,37 +31,30 @@ static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_VENC, "img_venc", "mm_sel", 9),
};
-static const struct of_device_id of_match_clk_mt2701_img[] = {
- { .compatible = "mediatek,mt2701-imgsys", },
- {}
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
};
-static int clk_mt2701_img_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt2701_img[] = {
+ {
+ .compatible = "mediatek,mt2701-imgsys",
+ .data = &img_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_img);
static struct platform_driver clk_mt2701_img_drv = {
- .probe = clk_mt2701_img_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2701-img",
.of_match_table = of_match_clk_mt2701_img,
},
};
+module_platform_driver(clk_mt2701_img_drv);
-builtin_platform_driver(clk_mt2701_img_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-mm.c b/drivers/clk/mediatek/clk-mt2701-mm.c
index 9ea7abad99d2..474d87d62e83 100644
--- a/drivers/clk/mediatek/clk-mt2701-mm.c
+++ b/drivers/clk/mediatek/clk-mt2701-mm.c
@@ -24,25 +24,14 @@ static const struct mtk_gate_regs disp1_cg_regs = {
.sta_ofs = 0x0110,
};
-#define GATE_DISP0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &disp0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_DISP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &disp0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_DISP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &disp1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_DISP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &disp1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mm_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "mm_dummy"),
GATE_DISP0(CLK_MM_SMI_COMMON, "mm_smi_comm", "mm_sel", 0),
GATE_DISP0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
GATE_DISP0(CLK_MM_CMDQ, "mm_cmdq", "mm_sel", 2),
@@ -79,32 +68,26 @@ static const struct mtk_gate mm_clks[] = {
GATE_DISP1(CLK_MM_TVE_FMM, "mm_tve_fmm", "mm_sel", 14),
};
-static int clk_mt2701_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR);
-
- mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return r;
-}
+static const struct platform_device_id clk_mt2701_mm_id_table[] = {
+ { .name = "clk-mt2701-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt2701_mm_id_table);
static struct platform_driver clk_mt2701_mm_drv = {
- .probe = clk_mt2701_mm_probe,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt2701-mm",
},
+ .id_table = clk_mt2701_mm_id_table,
};
+module_platform_driver(clk_mt2701_mm_drv);
-builtin_platform_driver(clk_mt2701_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 MultiMedia ddp clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701-vdec.c b/drivers/clk/mediatek/clk-mt2701-vdec.c
index a2f18117f27a..5299d92f3aba 100644
--- a/drivers/clk/mediatek/clk-mt2701-vdec.c
+++ b/drivers/clk/mediatek/clk-mt2701-vdec.c
@@ -24,60 +24,42 @@ static const struct mtk_gate_regs vdec1_cg_regs = {
.sta_ofs = 0x0008,
};
-#define GATE_VDEC0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VDEC0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
-#define GATE_VDEC1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VDEC1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate vdec_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "vdec_dummy"),
GATE_VDEC0(CLK_VDEC_CKGEN, "vdec_cken", "vdec_sel", 0),
GATE_VDEC1(CLK_VDEC_LARB, "vdec_larb_cken", "mm_sel", 0),
};
-static const struct of_device_id of_match_clk_mt2701_vdec[] = {
- { .compatible = "mediatek,mt2701-vdecsys", },
- {}
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
};
-static int clk_mt2701_vdec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_NR);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt2701_vdec[] = {
+ {
+ .compatible = "mediatek,mt2701-vdecsys",
+ .data = &vdec_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701_vdec);
static struct platform_driver clk_mt2701_vdec_drv = {
- .probe = clk_mt2701_vdec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2701-vdec",
.of_match_table = of_match_clk_mt2701_vdec,
},
};
+module_platform_driver(clk_mt2701_vdec_drv);
-builtin_platform_driver(clk_mt2701_vdec_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
index 9b442af37e67..1e88ad8b93f4 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
@@ -5,9 +5,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-cpumux.h"
@@ -636,14 +634,8 @@ static const struct mtk_gate_regs top_aud_cg_regs = {
.sta_ofs = 0x012C,
};
-#define GATE_TOP_AUD(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top_aud_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_TOP_AUD(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top_aud_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate top_clks[] = {
GATE_TOP_AUD(CLK_TOP_AUD_48K_TIMING, "a1sys_hp_ck", "aud_mux1_div",
@@ -669,13 +661,14 @@ static int mtk_topckgen_init(struct platform_device *pdev)
struct clk_hw_onecell_data *clk_data;
void __iomem *base;
struct device_node *node = pdev->dev.of_node;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
clk_data);
@@ -683,14 +676,15 @@ static int mtk_topckgen_init(struct platform_device *pdev)
mtk_clk_register_factors(top_fixed_divs, ARRAY_SIZE(top_fixed_divs),
clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes),
- base, &mt2701_clk_lock, clk_data);
+ mtk_clk_register_composites(&pdev->dev, top_muxes,
+ ARRAY_SIZE(top_muxes), base,
+ &mt2701_clk_lock, clk_data);
- mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs),
+ mtk_clk_register_dividers(&pdev->dev, top_adj_divs, ARRAY_SIZE(top_adj_divs),
base, &mt2701_clk_lock, clk_data);
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, top_clks,
+ ARRAY_SIZE(top_clks), clk_data);
return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
}
@@ -701,14 +695,8 @@ static const struct mtk_gate_regs infra_cg_regs = {
.sta_ofs = 0x0048,
};
-#define GATE_ICG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_ICG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate infra_clks[] = {
GATE_ICG(CLK_INFRA_DBG, "dbgclk", "axi_sel", 0),
@@ -761,6 +749,8 @@ static void __init mtk_infrasys_init_early(struct device_node *node)
if (!infra_clk_data) {
infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
+ if (!infra_clk_data)
+ return;
for (i = 0; i < CLK_INFRA_NR; i++)
infra_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
@@ -769,7 +759,7 @@ static void __init mtk_infrasys_init_early(struct device_node *node)
mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
infra_clk_data);
- mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
+ mtk_clk_register_cpumuxes(NULL, node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
infra_clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
@@ -788,6 +778,8 @@ static int mtk_infrasys_init(struct platform_device *pdev)
if (!infra_clk_data) {
infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
+ if (!infra_clk_data)
+ return -ENOMEM;
} else {
for (i = 0; i < CLK_INFRA_NR; i++) {
if (infra_clk_data->hws[i] == ERR_PTR(-EPROBE_DEFER))
@@ -795,8 +787,8 @@ static int mtk_infrasys_init(struct platform_device *pdev)
}
}
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- infra_clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, infra_clks,
+ ARRAY_SIZE(infra_clks), infra_clk_data);
mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
infra_clk_data);
@@ -822,23 +814,11 @@ static const struct mtk_gate_regs peri1_cg_regs = {
.sta_ofs = 0x001c,
};
-#define GATE_PERI0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_PERI0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_PERI1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_PERI1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate peri_clks[] = {
GATE_PERI0(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 31),
@@ -910,19 +890,21 @@ static int mtk_pericfg_init(struct platform_device *pdev)
void __iomem *base;
int r;
struct device_node *node = pdev->dev.of_node;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
clk_data = mtk_alloc_clk_data(CLK_PERI_NR);
+ if (!clk_data)
+ return -ENOMEM;
- mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, peri_clks,
+ ARRAY_SIZE(peri_clks), clk_data);
- mtk_clk_register_composites(peri_muxs, ARRAY_SIZE(peri_muxs), base,
- &mt2701_clk_lock, clk_data);
+ mtk_clk_register_composites(&pdev->dev, peri_muxs,
+ ARRAY_SIZE(peri_muxs), base,
+ &mt2701_clk_lock, clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
if (r)
@@ -1021,6 +1003,7 @@ static const struct of_device_id of_match_clk_mt2701[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2701);
static int clk_mt2701_probe(struct platform_device *pdev)
{
@@ -1054,3 +1037,6 @@ static int __init clk_mt2701_init(void)
}
arch_initcall(clk_mt2701_init);
+
+MODULE_DESCRIPTION("MediaTek MT2701 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-apmixedsys.c b/drivers/clk/mediatek/clk-mt2712-apmixedsys.c
new file mode 100644
index 000000000000..a60622d251ff
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2712-apmixedsys.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ * Weiyi Lu <weiyi.lu@mediatek.com>
+ * Copyright (c) 2023 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-pll.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mt2712-clk.h>
+
+#define MT2712_PLL_FMAX (3000UL * MHZ)
+
+#define CON0_MT2712_RST_BAR BIT(24)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift, \
+ _div_table) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT2712_RST_BAR, \
+ .fmax = MT2712_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _pcwbits, _pd_reg, _pd_shift, _tuner_reg, \
+ _tuner_en_reg, _tuner_en_bit, _pcw_reg, \
+ _pcw_shift, NULL)
+
+static const struct mtk_pll_div_table armca35pll_div_table[] = {
+ { .div = 0, .freq = MT2712_PLL_FMAX },
+ { .div = 1, .freq = 1202500000 },
+ { .div = 2, .freq = 500500000 },
+ { .div = 3, .freq = 315250000 },
+ { .div = 4, .freq = 157625000 },
+ { /* sentinel */ }
+};
+
+static const struct mtk_pll_div_table armca72pll_div_table[] = {
+ { .div = 0, .freq = MT2712_PLL_FMAX },
+ { .div = 1, .freq = 994500000 },
+ { .div = 2, .freq = 520000000 },
+ { .div = 3, .freq = 315250000 },
+ { .div = 4, .freq = 157625000 },
+ { /* sentinel */ }
+};
+
+static const struct mtk_pll_div_table mmpll_div_table[] = {
+ { .div = 0, .freq = MT2712_PLL_FMAX },
+ { .div = 1, .freq = 1001000000 },
+ { .div = 2, .freq = 601250000 },
+ { .div = 3, .freq = 250250000 },
+ { .div = 4, .freq = 125125000 },
+ { /* sentinel */ }
+};
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0230, 0x023C, 0xf0000100,
+ HAVE_RST_BAR, 31, 0x0230, 4, 0, 0, 0, 0x0234, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0240, 0x024C, 0xfe000100,
+ HAVE_RST_BAR, 31, 0x0240, 4, 0, 0, 0, 0x0244, 0),
+ PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x0320, 0x032C, 0xc0000100,
+ 0, 31, 0x0320, 4, 0, 0, 0, 0x0324, 0),
+ PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x0280, 0x028C, 0x00000100,
+ 0, 31, 0x0280, 4, 0, 0, 0, 0x0284, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x0330, 0x0340, 0x00000100,
+ 0, 31, 0x0330, 4, 0x0338, 0x0014, 0, 0x0334, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x0350, 0x0360, 0x00000100,
+ 0, 31, 0x0350, 4, 0x0358, 0x0014, 1, 0x0354, 0),
+ PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x0370, 0x037c, 0x00000100,
+ 0, 31, 0x0370, 4, 0, 0, 0, 0x0374, 0),
+ PLL(CLK_APMIXED_LVDSPLL2, "lvdspll2", 0x0390, 0x039C, 0x00000100,
+ 0, 31, 0x0390, 4, 0, 0, 0, 0x0394, 0),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0270, 0x027C, 0x00000100,
+ 0, 31, 0x0270, 4, 0, 0, 0, 0x0274, 0),
+ PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x0410, 0x041C, 0x00000100,
+ 0, 31, 0x0410, 4, 0, 0, 0, 0x0414, 0),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x0290, 0x029C, 0xc0000100,
+ 0, 31, 0x0290, 4, 0, 0, 0, 0x0294, 0),
+ PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0250, 0x0260, 0x00000100,
+ 0, 31, 0x0250, 4, 0, 0, 0, 0x0254, 0, mmpll_div_table),
+ PLL_B(CLK_APMIXED_ARMCA35PLL, "armca35pll", 0x0100, 0x0110, 0xf0000100,
+ HAVE_RST_BAR, 31, 0x0100, 4, 0, 0, 0, 0x0104, 0, armca35pll_div_table),
+ PLL_B(CLK_APMIXED_ARMCA72PLL, "armca72pll", 0x0210, 0x0220, 0x00000100,
+ 0, 31, 0x0210, 4, 0, 0, 0, 0x0214, 0, armca72pll_div_table),
+ PLL(CLK_APMIXED_ETHERPLL, "etherpll", 0x0300, 0x030C, 0xc0000100,
+ 0, 31, 0x0300, 4, 0, 0, 0, 0x0304, 0),
+};
+
+static int clk_mt2712_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (r)
+ goto free_clk_data;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r) {
+ dev_err(&pdev->dev, "Cannot register clock provider: %d\n", r);
+ goto unregister_plls;
+ }
+
+ return 0;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static void clk_mt2712_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static const struct of_device_id of_match_clk_mt2712_apmixed[] = {
+ { .compatible = "mediatek,mt2712-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_apmixed);
+
+static struct platform_driver clk_mt2712_apmixed_drv = {
+ .probe = clk_mt2712_apmixed_probe,
+ .remove = clk_mt2712_apmixed_remove,
+ .driver = {
+ .name = "clk-mt2712-apmixed",
+ .of_match_table = of_match_clk_mt2712_apmixed,
+ },
+};
+module_platform_driver(clk_mt2712_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT2712 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-bdp.c b/drivers/clk/mediatek/clk-mt2712-bdp.c
index 9acab4357133..c838311a0c51 100644
--- a/drivers/clk/mediatek/clk-mt2712-bdp.c
+++ b/drivers/clk/mediatek/clk-mt2712-bdp.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs bdp_cg_regs = {
.sta_ofs = 0x100,
};
-#define GATE_BDP(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &bdp_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_BDP(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &bdp_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate bdp_clks[] = {
GATE_BDP(CLK_BDP_BRIDGE_B, "bdp_bridge_b", "mm_sel", 0),
@@ -58,37 +52,30 @@ static const struct mtk_gate bdp_clks[] = {
GATE_BDP(CLK_BDP_TVD_CBUS, "bdp_tvd_cbus", "mm_sel", 30),
};
-static int clk_mt2712_bdp_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_BDP_NR_CLK);
-
- mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc bdp_desc = {
+ .clks = bdp_clks,
+ .num_clks = ARRAY_SIZE(bdp_clks),
+};
static const struct of_device_id of_match_clk_mt2712_bdp[] = {
- { .compatible = "mediatek,mt2712-bdpsys", },
- {}
+ {
+ .compatible = "mediatek,mt2712-bdpsys",
+ .data = &bdp_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_bdp);
static struct platform_driver clk_mt2712_bdp_drv = {
- .probe = clk_mt2712_bdp_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712-bdp",
.of_match_table = of_match_clk_mt2712_bdp,
},
};
+module_platform_driver(clk_mt2712_bdp_drv);
-builtin_platform_driver(clk_mt2712_bdp_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 BDP clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-img.c b/drivers/clk/mediatek/clk-mt2712-img.c
index 5cc143e65e42..bedebf86b0b5 100644
--- a/drivers/clk/mediatek/clk-mt2712-img.c
+++ b/drivers/clk/mediatek/clk-mt2712-img.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs img_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_IMG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &img_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_IMG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_SMI_LARB2, "img_smi_larb2", "mm_sel", 0),
@@ -36,37 +30,30 @@ static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_CAM_SV2_EN, "img_cam_sv2_en", "mm_sel", 11),
};
-static int clk_mt2712_img_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
+};
static const struct of_device_id of_match_clk_mt2712_img[] = {
- { .compatible = "mediatek,mt2712-imgsys", },
- {}
+ {
+ .compatible = "mediatek,mt2712-imgsys",
+ .data = &img_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_img);
static struct platform_driver clk_mt2712_img_drv = {
- .probe = clk_mt2712_img_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712-img",
.of_match_table = of_match_clk_mt2712_img,
},
};
+module_platform_driver(clk_mt2712_img_drv);
-builtin_platform_driver(clk_mt2712_img_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-jpgdec.c b/drivers/clk/mediatek/clk-mt2712-jpgdec.c
index 31fc30370d98..1a73474b2f99 100644
--- a/drivers/clk/mediatek/clk-mt2712-jpgdec.c
+++ b/drivers/clk/mediatek/clk-mt2712-jpgdec.c
@@ -18,51 +18,38 @@ static const struct mtk_gate_regs jpgdec_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_JPGDEC(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &jpgdec_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_JPGDEC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &jpgdec_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate jpgdec_clks[] = {
GATE_JPGDEC(CLK_JPGDEC_JPGDEC1, "jpgdec_jpgdec1", "jpgdec_sel", 0),
GATE_JPGDEC(CLK_JPGDEC_JPGDEC, "jpgdec_jpgdec", "jpgdec_sel", 4),
};
-static int clk_mt2712_jpgdec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_JPGDEC_NR_CLK);
-
- mtk_clk_register_gates(node, jpgdec_clks, ARRAY_SIZE(jpgdec_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc jpgdec_desc = {
+ .clks = jpgdec_clks,
+ .num_clks = ARRAY_SIZE(jpgdec_clks),
+};
static const struct of_device_id of_match_clk_mt2712_jpgdec[] = {
- { .compatible = "mediatek,mt2712-jpgdecsys", },
- {}
+ {
+ .compatible = "mediatek,mt2712-jpgdecsys",
+ .data = &jpgdec_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_jpgdec);
static struct platform_driver clk_mt2712_jpgdec_drv = {
- .probe = clk_mt2712_jpgdec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712-jpgdec",
.of_match_table = of_match_clk_mt2712_jpgdec,
},
};
+module_platform_driver(clk_mt2712_jpgdec_drv);
-builtin_platform_driver(clk_mt2712_jpgdec_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 JPEG Decoder clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-mfg.c b/drivers/clk/mediatek/clk-mt2712-mfg.c
index a4d09675bf18..c1bb45c7469e 100644
--- a/drivers/clk/mediatek/clk-mt2712-mfg.c
+++ b/drivers/clk/mediatek/clk-mt2712-mfg.c
@@ -18,50 +18,37 @@ static const struct mtk_gate_regs mfg_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_MFG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mfg_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mfg_clks[] = {
GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_sel", 0),
};
-static int clk_mt2712_mfg_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_MFG_NR_CLK);
-
- mtk_clk_register_gates(node, mfg_clks, ARRAY_SIZE(mfg_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc mfg_desc = {
+ .clks = mfg_clks,
+ .num_clks = ARRAY_SIZE(mfg_clks),
+};
static const struct of_device_id of_match_clk_mt2712_mfg[] = {
- { .compatible = "mediatek,mt2712-mfgcfg", },
- {}
+ {
+ .compatible = "mediatek,mt2712-mfgcfg",
+ .data = &mfg_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_mfg);
static struct platform_driver clk_mt2712_mfg_drv = {
- .probe = clk_mt2712_mfg_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712-mfg",
.of_match_table = of_match_clk_mt2712_mfg,
},
};
+module_platform_driver(clk_mt2712_mfg_drv);
-builtin_platform_driver(clk_mt2712_mfg_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-mm.c b/drivers/clk/mediatek/clk-mt2712-mm.c
index 7d44b09b8a0a..32ecb949f7eb 100644
--- a/drivers/clk/mediatek/clk-mt2712-mm.c
+++ b/drivers/clk/mediatek/clk-mt2712-mm.c
@@ -30,32 +30,14 @@ static const struct mtk_gate_regs mm2_cg_regs = {
.sta_ofs = 0x220,
};
-#define GATE_MM0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_MM1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_MM2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mm_clks[] = {
/* MM0 */
@@ -126,32 +108,26 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM2(CLK_MM_DSI3_DIGITAL, "mm_dsi3_digital", "dsi1_lntc", 6),
};
-static int clk_mt2712_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
-
- mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return r;
-}
+static const struct platform_device_id clk_mt2712_mm_id_table[] = {
+ { .name = "clk-mt2712-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt2712_mm_id_table);
static struct platform_driver clk_mt2712_mm_drv = {
- .probe = clk_mt2712_mm_probe,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt2712-mm",
},
+ .id_table = clk_mt2712_mm_id_table,
};
+module_platform_driver(clk_mt2712_mm_drv);
-builtin_platform_driver(clk_mt2712_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 MultiMedia ddp clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-vdec.c b/drivers/clk/mediatek/clk-mt2712-vdec.c
index af13f43dd831..a766342fbafa 100644
--- a/drivers/clk/mediatek/clk-mt2712-vdec.c
+++ b/drivers/clk/mediatek/clk-mt2712-vdec.c
@@ -24,23 +24,11 @@ static const struct mtk_gate_regs vdec1_cg_regs = {
.sta_ofs = 0x8,
};
-#define GATE_VDEC0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VDEC0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
-#define GATE_VDEC1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VDEC1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate vdec_clks[] = {
/* VDEC0 */
@@ -50,37 +38,30 @@ static const struct mtk_gate vdec_clks[] = {
GATE_VDEC1(CLK_VDEC_IMGRZ_CKEN, "vdec_imgrz_cken", "vdec_sel", 1),
};
-static int clk_mt2712_vdec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_NR_CLK);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
+};
static const struct of_device_id of_match_clk_mt2712_vdec[] = {
- { .compatible = "mediatek,mt2712-vdecsys", },
- {}
+ {
+ .compatible = "mediatek,mt2712-vdecsys",
+ .data = &vdec_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_vdec);
static struct platform_driver clk_mt2712_vdec_drv = {
- .probe = clk_mt2712_vdec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712-vdec",
.of_match_table = of_match_clk_mt2712_vdec,
},
};
+module_platform_driver(clk_mt2712_vdec_drv);
-builtin_platform_driver(clk_mt2712_vdec_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712-venc.c b/drivers/clk/mediatek/clk-mt2712-venc.c
index abc08a029753..fc193dc8e8f6 100644
--- a/drivers/clk/mediatek/clk-mt2712-venc.c
+++ b/drivers/clk/mediatek/clk-mt2712-venc.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs venc_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_VENC(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &venc_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VENC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate venc_clks[] = {
GATE_VENC(CLK_VENC_SMI_COMMON_CON, "venc_smi", "mm_sel", 0),
@@ -33,37 +27,30 @@ static const struct mtk_gate venc_clks[] = {
GATE_VENC(CLK_VENC_SMI_LARB6, "venc_smi_larb6", "jpgdec_sel", 12),
};
-static int clk_mt2712_venc_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK);
-
- mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
+};
static const struct of_device_id of_match_clk_mt2712_venc[] = {
- { .compatible = "mediatek,mt2712-vencsys", },
- {}
+ {
+ .compatible = "mediatek,mt2712-vencsys",
+ .data = &venc_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712_venc);
static struct platform_driver clk_mt2712_venc_drv = {
- .probe = clk_mt2712_venc_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712-venc",
.of_match_table = of_match_clk_mt2712_venc,
},
};
+module_platform_driver(clk_mt2712_venc_drv);
-builtin_platform_driver(clk_mt2712_venc_drv);
+MODULE_DESCRIPTION("MediaTek MT2712 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c
index 56980dd6c2ea..964c92130e3c 100644
--- a/drivers/clk/mediatek/clk-mt2712.c
+++ b/drivers/clk/mediatek/clk-mt2712.c
@@ -7,14 +7,11 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk-gate.h"
-#include "clk-pll.h"
#include "clk-mtk.h"
#include <dt-bindings/clock/mt2712-clk.h>
@@ -36,188 +33,96 @@ static const struct mtk_fixed_clk top_fixed_clks[] = {
FIXED_CLK(CLK_TOP_CVBSPLL, "cvbspll", NULL, 108000000),
};
-static const struct mtk_fixed_factor top_early_divs[] = {
- FACTOR(CLK_TOP_SYS_26M, "sys_26m", "clk26m", 1,
- 1),
- FACTOR(CLK_TOP_CLK26M_D2, "clk26m_d2", "sys_26m", 1,
- 2),
-};
-
static const struct mtk_fixed_factor top_divs[] = {
- FACTOR(CLK_TOP_ARMCA35PLL, "armca35pll_ck", "armca35pll", 1,
- 1),
- FACTOR(CLK_TOP_ARMCA35PLL_600M, "armca35pll_600m", "armca35pll_ck", 1,
- 2),
- FACTOR(CLK_TOP_ARMCA35PLL_400M, "armca35pll_400m", "armca35pll_ck", 1,
- 3),
- FACTOR(CLK_TOP_ARMCA72PLL, "armca72pll_ck", "armca72pll", 1,
- 1),
- FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1,
- 1),
- FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "syspll_ck", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1,
- 4),
- FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1,
- 8),
- FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1,
- 16),
- FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "syspll_ck", 1,
- 3),
- FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1,
- 4),
- FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "syspll_ck", 1,
- 5),
- FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1,
- 4),
- FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "syspll_ck", 1,
- 7),
- FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL, "univpll_ck", "univpll", 1,
- 1),
- FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_ck", 1,
- 7),
- FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_ck", 1,
- 26),
- FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univpll_ck", 1,
- 52),
- FACTOR(CLK_TOP_UNIVPLL_D104, "univpll_d104", "univpll_ck", 1,
- 104),
- FACTOR(CLK_TOP_UNIVPLL_D208, "univpll_d208", "univpll_ck", 1,
- 208),
- FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1,
- 8),
- FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_ck", 1,
- 3),
- FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1,
- 8),
- FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_ck", 1,
- 5),
- FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1,
- 8),
- FACTOR(CLK_TOP_F_MP0_PLL1, "f_mp0_pll1_ck", "univpll_d2", 1,
- 1),
- FACTOR(CLK_TOP_F_MP0_PLL2, "f_mp0_pll2_ck", "univpll1_d2", 1,
- 1),
- FACTOR(CLK_TOP_F_BIG_PLL1, "f_big_pll1_ck", "univpll_d2", 1,
- 1),
- FACTOR(CLK_TOP_F_BIG_PLL2, "f_big_pll2_ck", "univpll1_d2", 1,
- 1),
- FACTOR(CLK_TOP_F_BUS_PLL1, "f_bus_pll1_ck", "univpll_d2", 1,
- 1),
- FACTOR(CLK_TOP_F_BUS_PLL2, "f_bus_pll2_ck", "univpll1_d2", 1,
- 1),
- FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1,
- 1),
- FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1_ck", 1,
- 2),
- FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1_ck", 1,
- 4),
- FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1_ck", 1,
- 8),
- FACTOR(CLK_TOP_APLL1_D16, "apll1_d16", "apll1_ck", 1,
- 16),
- FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1,
- 1),
- FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2_ck", 1,
- 2),
- FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2_ck", 1,
- 4),
- FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2_ck", 1,
- 8),
- FACTOR(CLK_TOP_APLL2_D16, "apll2_d16", "apll2_ck", 1,
- 16),
- FACTOR(CLK_TOP_LVDSPLL, "lvdspll_ck", "lvdspll", 1,
- 1),
- FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll_ck", 1,
- 2),
- FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll_ck", 1,
- 4),
- FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll_ck", 1,
- 8),
- FACTOR(CLK_TOP_LVDSPLL2, "lvdspll2_ck", "lvdspll2", 1,
- 1),
- FACTOR(CLK_TOP_LVDSPLL2_D2, "lvdspll2_d2", "lvdspll2_ck", 1,
- 2),
- FACTOR(CLK_TOP_LVDSPLL2_D4, "lvdspll2_d4", "lvdspll2_ck", 1,
- 4),
- FACTOR(CLK_TOP_LVDSPLL2_D8, "lvdspll2_d8", "lvdspll2_ck", 1,
- 8),
- FACTOR(CLK_TOP_ETHERPLL_125M, "etherpll_125m", "etherpll", 1,
- 1),
- FACTOR(CLK_TOP_ETHERPLL_50M, "etherpll_50m", "etherpll", 1,
- 1),
- FACTOR(CLK_TOP_CVBS, "cvbs", "cvbspll", 1,
- 1),
- FACTOR(CLK_TOP_CVBS_D2, "cvbs_d2", "cvbs", 1,
- 2),
- FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1,
- 1),
- FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1,
- 1),
- FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1,
- 1),
- FACTOR(CLK_TOP_VCODECPLL_D2, "vcodecpll_d2", "vcodecpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll", 1,
- 1),
- FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_ck", 1,
- 4),
- FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_ck", 1,
- 8),
- FACTOR(CLK_TOP_TVDPLL_429M, "tvdpll_429m", "tvdpll", 1,
- 1),
- FACTOR(CLK_TOP_TVDPLL_429M_D2, "tvdpll_429m_d2", "tvdpll_429m", 1,
- 2),
- FACTOR(CLK_TOP_TVDPLL_429M_D4, "tvdpll_429m_d4", "tvdpll_429m", 1,
- 4),
- FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1,
- 1),
- FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll_ck", 1,
- 4),
- FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1,
- 1),
- FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2_ck", 1,
- 2),
- FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2_ck", 1,
- 4),
- FACTOR(CLK_TOP_D2A_ULCLK_6P5M, "d2a_ulclk_6p5m", "clk26m", 1,
- 4),
- FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1_ck", 1,
- 3),
- FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2_ck", 1,
- 3),
+ FACTOR(CLK_TOP_SYS_26M, "sys_26m", "clk26m", 1, 1),
+ FACTOR(CLK_TOP_CLK26M_D2, "clk26m_d2", "sys_26m", 1, 2),
+ FACTOR(CLK_TOP_ARMCA35PLL, "armca35pll_ck", "armca35pll", 1, 1),
+ FACTOR(CLK_TOP_ARMCA35PLL_600M, "armca35pll_600m", "armca35pll_ck", 1, 2),
+ FACTOR(CLK_TOP_ARMCA35PLL_400M, "armca35pll_400m", "armca35pll_ck", 1, 3),
+ FACTOR(CLK_TOP_ARMCA72PLL, "armca72pll_ck", "armca72pll", 1, 1),
+ FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1, 1),
+ FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "syspll_ck", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, 8),
+ FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, 16),
+ FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "syspll_ck", 1, 3),
+ FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "syspll_ck", 1, 5),
+ FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "syspll_ck", 1, 7),
+ FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL, "univpll_ck", "univpll", 1, 1),
+ FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_ck", 1, 7),
+ FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_ck", 1, 26),
+ FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univpll_ck", 1, 52),
+ FACTOR(CLK_TOP_UNIVPLL_D104, "univpll_d104", "univpll_ck", 1, 104),
+ FACTOR(CLK_TOP_UNIVPLL_D208, "univpll_d208", "univpll_ck", 1, 208),
+ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll_ck", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_ck", 1, 3),
+ FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_ck", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1, 8),
+ FACTOR(CLK_TOP_F_MP0_PLL1, "f_mp0_pll1_ck", "univpll_d2", 1, 1),
+ FACTOR(CLK_TOP_F_MP0_PLL2, "f_mp0_pll2_ck", "univpll1_d2", 1, 1),
+ FACTOR(CLK_TOP_F_BIG_PLL1, "f_big_pll1_ck", "univpll_d2", 1, 1),
+ FACTOR(CLK_TOP_F_BIG_PLL2, "f_big_pll2_ck", "univpll1_d2", 1, 1),
+ FACTOR(CLK_TOP_F_BUS_PLL1, "f_bus_pll1_ck", "univpll_d2", 1, 1),
+ FACTOR(CLK_TOP_F_BUS_PLL2, "f_bus_pll2_ck", "univpll1_d2", 1, 1),
+ FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
+ FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1_ck", 1, 2),
+ FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1_ck", 1, 4),
+ FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1_ck", 1, 8),
+ FACTOR(CLK_TOP_APLL1_D16, "apll1_d16", "apll1_ck", 1, 16),
+ FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1),
+ FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2_ck", 1, 2),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2_ck", 1, 4),
+ FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2_ck", 1, 8),
+ FACTOR(CLK_TOP_APLL2_D16, "apll2_d16", "apll2_ck", 1, 16),
+ FACTOR(CLK_TOP_LVDSPLL, "lvdspll_ck", "lvdspll", 1, 1),
+ FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll_ck", 1, 2),
+ FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll_ck", 1, 4),
+ FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll_ck", 1, 8),
+ FACTOR(CLK_TOP_LVDSPLL2, "lvdspll2_ck", "lvdspll2", 1, 1),
+ FACTOR(CLK_TOP_LVDSPLL2_D2, "lvdspll2_d2", "lvdspll2_ck", 1, 2),
+ FACTOR(CLK_TOP_LVDSPLL2_D4, "lvdspll2_d4", "lvdspll2_ck", 1, 4),
+ FACTOR(CLK_TOP_LVDSPLL2_D8, "lvdspll2_d8", "lvdspll2_ck", 1, 8),
+ FACTOR(CLK_TOP_ETHERPLL_125M, "etherpll_125m", "etherpll", 1, 1),
+ FACTOR(CLK_TOP_ETHERPLL_50M, "etherpll_50m", "etherpll", 1, 1),
+ FACTOR(CLK_TOP_CVBS, "cvbs", "cvbspll", 1, 1),
+ FACTOR(CLK_TOP_CVBS_D2, "cvbs_d2", "cvbs", 1, 2),
+ FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll_ck", 1, 2),
+ FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1),
+ FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll_ck", 1, 2),
+ FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 1),
+ FACTOR(CLK_TOP_VCODECPLL_D2, "vcodecpll_d2", "vcodecpll_ck", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll", 1, 1),
+ FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_ck", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_ck", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_ck", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL_429M, "tvdpll_429m", "tvdpll", 1, 1),
+ FACTOR(CLK_TOP_TVDPLL_429M_D2, "tvdpll_429m_d2", "tvdpll_429m", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL_429M_D4, "tvdpll_429m_d4", "tvdpll_429m", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll_ck", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll_ck", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2_ck", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2_ck", 1, 4),
+ FACTOR(CLK_TOP_D2A_ULCLK_6P5M, "d2a_ulclk_6p5m", "clk26m", 1, 4),
+ FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1_ck", 1, 3),
+ FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2_ck", 1, 3),
};
static const char * const axi_parents[] = {
@@ -737,169 +642,118 @@ static const char * const audull_vtx_parents[] = {
static struct mtk_composite top_muxes[] = {
/* CLK_CFG_0 */
MUX_GATE_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x040, 0, 3,
- 7, CLK_IS_CRITICAL),
+ 7, CLK_IS_CRITICAL),
MUX_GATE_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x040, 8, 1,
- 15, CLK_IS_CRITICAL),
- MUX_GATE(CLK_TOP_MM_SEL, "mm_sel",
- mm_parents, 0x040, 24, 3, 31),
+ 15, CLK_IS_CRITICAL),
+ MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x040, 24, 3, 31),
/* CLK_CFG_1 */
- MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel",
- pwm_parents, 0x050, 0, 2, 7),
- MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel",
- vdec_parents, 0x050, 8, 4, 15),
- MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel",
- venc_parents, 0x050, 16, 4, 23),
- MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel",
- mfg_parents, 0x050, 24, 4, 31),
+ MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x050, 0, 2, 7),
+ MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x050, 8, 4, 15),
+ MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x050, 16, 4, 23),
+ MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x050, 24, 4, 31),
/* CLK_CFG_2 */
- MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel",
- camtg_parents, 0x060, 0, 4, 7),
- MUX_GATE(CLK_TOP_UART_SEL, "uart_sel",
- uart_parents, 0x060, 8, 1, 15),
- MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel",
- spi_parents, 0x060, 16, 3, 23),
- MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel",
- usb20_parents, 0x060, 24, 2, 31),
+ MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x060, 0, 4, 7),
+ MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x060, 8, 1, 15),
+ MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x060, 16, 3, 23),
+ MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x060, 24, 2, 31),
/* CLK_CFG_3 */
- MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel",
- usb30_parents, 0x070, 0, 2, 7),
- MUX_GATE(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc50_0_h_sel",
- msdc50_0_h_parents, 0x070, 8, 3, 15),
- MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
- msdc50_0_parents, 0x070, 16, 4, 23),
- MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
- msdc30_1_parents, 0x070, 24, 3, 31),
+ MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x070, 0, 2, 7),
+ MUX_GATE(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc50_0_h_sel", msdc50_0_h_parents,
+ 0x070, 8, 3, 15),
+ MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents,
+ 0x070, 16, 4, 23),
+ MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents,
+ 0x070, 24, 3, 31),
/* CLK_CFG_4 */
- MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel",
- msdc30_1_parents, 0x080, 0, 3, 7),
- MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel",
- msdc30_3_parents, 0x080, 8, 4, 15),
- MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel",
- audio_parents, 0x080, 16, 2, 23),
- MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel",
- aud_intbus_parents, 0x080, 24, 3, 31),
+ MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_1_parents,
+ 0x080, 0, 3, 7),
+ MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents,
+ 0x080, 8, 4, 15),
+ MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents,
+ 0x080, 16, 2, 23),
+ MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents,
+ 0x080, 24, 3, 31),
/* CLK_CFG_5 */
- MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel",
- pmicspi_parents, 0x090, 0, 3, 7),
- MUX_GATE(CLK_TOP_DPILVDS1_SEL, "dpilvds1_sel",
- dpilvds1_parents, 0x090, 8, 3, 15),
- MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel",
- atb_parents, 0x090, 16, 2, 23),
- MUX_GATE(CLK_TOP_NR_SEL, "nr_sel",
- nr_parents, 0x090, 24, 3, 31),
+ MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x090, 0, 3, 7),
+ MUX_GATE(CLK_TOP_DPILVDS1_SEL, "dpilvds1_sel", dpilvds1_parents,
+ 0x090, 8, 3, 15),
+ MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x090, 16, 2, 23),
+ MUX_GATE(CLK_TOP_NR_SEL, "nr_sel", nr_parents, 0x090, 24, 3, 31),
/* CLK_CFG_6 */
- MUX_GATE(CLK_TOP_NFI2X_SEL, "nfi2x_sel",
- nfi2x_parents, 0x0a0, 0, 4, 7),
- MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel",
- irda_parents, 0x0a0, 8, 2, 15),
- MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel",
- cci400_parents, 0x0a0, 16, 3, 23),
- MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel",
- aud_1_parents, 0x0a0, 24, 2, 31),
+ MUX_GATE(CLK_TOP_NFI2X_SEL, "nfi2x_sel", nfi2x_parents, 0x0a0, 0, 4, 7),
+ MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x0a0, 8, 2, 15),
+ MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x0a0, 16, 3, 23),
+ MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x0a0, 24, 2, 31),
/* CLK_CFG_7 */
- MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel",
- aud_2_parents, 0x0b0, 0, 2, 7),
- MUX_GATE(CLK_TOP_MEM_MFG_IN_AS_SEL, "mem_mfg_sel",
- mem_mfg_parents, 0x0b0, 8, 2, 15),
- MUX_GATE(CLK_TOP_AXI_MFG_IN_AS_SEL, "axi_mfg_sel",
- axi_mfg_parents, 0x0b0, 16, 2, 23),
- MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel",
- scam_parents, 0x0b0, 24, 2, 31),
+ MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x0b0, 0, 2, 7),
+ MUX_GATE(CLK_TOP_MEM_MFG_IN_AS_SEL, "mem_mfg_sel", mem_mfg_parents,
+ 0x0b0, 8, 2, 15),
+ MUX_GATE(CLK_TOP_AXI_MFG_IN_AS_SEL, "axi_mfg_sel", axi_mfg_parents,
+ 0x0b0, 16, 2, 23),
+ MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x0b0, 24, 2, 31),
/* CLK_CFG_8 */
- MUX_GATE(CLK_TOP_NFIECC_SEL, "nfiecc_sel",
- nfiecc_parents, 0x0c0, 0, 3, 7),
- MUX_GATE(CLK_TOP_PE2_MAC_P0_SEL, "pe2_mac_p0_sel",
- pe2_mac_p0_parents, 0x0c0, 8, 3, 15),
- MUX_GATE(CLK_TOP_PE2_MAC_P1_SEL, "pe2_mac_p1_sel",
- pe2_mac_p0_parents, 0x0c0, 16, 3, 23),
- MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel",
- dpilvds_parents, 0x0c0, 24, 3, 31),
+ MUX_GATE(CLK_TOP_NFIECC_SEL, "nfiecc_sel", nfiecc_parents, 0x0c0, 0, 3, 7),
+ MUX_GATE(CLK_TOP_PE2_MAC_P0_SEL, "pe2_mac_p0_sel", pe2_mac_p0_parents,
+ 0x0c0, 8, 3, 15),
+ MUX_GATE(CLK_TOP_PE2_MAC_P1_SEL, "pe2_mac_p1_sel", pe2_mac_p0_parents,
+ 0x0c0, 16, 3, 23),
+ MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x0c0, 24, 3, 31),
/* CLK_CFG_9 */
- MUX_GATE(CLK_TOP_MSDC50_3_HCLK_SEL, "msdc50_3_h_sel",
- msdc50_0_h_parents, 0x0d0, 0, 3, 7),
- MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel",
- hdcp_parents, 0x0d0, 8, 2, 15),
- MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel",
- hdcp_24m_parents, 0x0d0, 16, 2, 23),
- MUX_GATE_FLAGS(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x0d0, 24, 2,
- 31, CLK_IS_CRITICAL),
+ MUX_GATE(CLK_TOP_MSDC50_3_HCLK_SEL, "msdc50_3_h_sel", msdc50_0_h_parents,
+ 0x0d0, 0, 3, 7),
+ MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x0d0, 8, 2, 15),
+ MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents,
+ 0x0d0, 16, 2, 23),
+ MUX_GATE_FLAGS(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents,
+ 0x0d0, 24, 2, 31, CLK_IS_CRITICAL),
/* CLK_CFG_10 */
- MUX_GATE(CLK_TOP_SPINOR_SEL, "spinor_sel",
- spinor_parents, 0x500, 0, 4, 7),
- MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel",
- apll_parents, 0x500, 8, 4, 15),
- MUX_GATE(CLK_TOP_APLL2_SEL, "apll2_sel",
- apll_parents, 0x500, 16, 4, 23),
- MUX_GATE(CLK_TOP_A1SYS_HP_SEL, "a1sys_hp_sel",
- a1sys_hp_parents, 0x500, 24, 3, 31),
+ MUX_GATE(CLK_TOP_SPINOR_SEL, "spinor_sel", spinor_parents, 0x500, 0, 4, 7),
+ MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x500, 8, 4, 15),
+ MUX_GATE(CLK_TOP_APLL2_SEL, "apll2_sel", apll_parents, 0x500, 16, 4, 23),
+ MUX_GATE(CLK_TOP_A1SYS_HP_SEL, "a1sys_hp_sel", a1sys_hp_parents,
+ 0x500, 24, 3, 31),
/* CLK_CFG_11 */
- MUX_GATE(CLK_TOP_A2SYS_HP_SEL, "a2sys_hp_sel",
- a2sys_hp_parents, 0x510, 0, 3, 7),
- MUX_GATE(CLK_TOP_ASM_L_SEL, "asm_l_sel",
- asm_l_parents, 0x510, 8, 2, 15),
- MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel",
- asm_l_parents, 0x510, 16, 2, 23),
- MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel",
- asm_l_parents, 0x510, 24, 2, 31),
+ MUX_GATE(CLK_TOP_A2SYS_HP_SEL, "a2sys_hp_sel", a2sys_hp_parents, 0x510, 0, 3, 7),
+ MUX_GATE(CLK_TOP_ASM_L_SEL, "asm_l_sel", asm_l_parents, 0x510, 8, 2, 15),
+ MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", asm_l_parents, 0x510, 16, 2, 23),
+ MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", asm_l_parents, 0x510, 24, 2, 31),
/* CLK_CFG_12 */
- MUX_GATE(CLK_TOP_I2SO1_SEL, "i2so1_sel",
- i2so1_parents, 0x520, 0, 2, 7),
- MUX_GATE(CLK_TOP_I2SO2_SEL, "i2so2_sel",
- i2so1_parents, 0x520, 8, 2, 15),
- MUX_GATE(CLK_TOP_I2SO3_SEL, "i2so3_sel",
- i2so1_parents, 0x520, 16, 2, 23),
- MUX_GATE(CLK_TOP_TDMO0_SEL, "tdmo0_sel",
- i2so1_parents, 0x520, 24, 2, 31),
+ MUX_GATE(CLK_TOP_I2SO1_SEL, "i2so1_sel", i2so1_parents, 0x520, 0, 2, 7),
+ MUX_GATE(CLK_TOP_I2SO2_SEL, "i2so2_sel", i2so1_parents, 0x520, 8, 2, 15),
+ MUX_GATE(CLK_TOP_I2SO3_SEL, "i2so3_sel", i2so1_parents, 0x520, 16, 2, 23),
+ MUX_GATE(CLK_TOP_TDMO0_SEL, "tdmo0_sel", i2so1_parents, 0x520, 24, 2, 31),
/* CLK_CFG_13 */
- MUX_GATE(CLK_TOP_TDMO1_SEL, "tdmo1_sel",
- i2so1_parents, 0x530, 0, 2, 7),
- MUX_GATE(CLK_TOP_I2SI1_SEL, "i2si1_sel",
- i2so1_parents, 0x530, 8, 2, 15),
- MUX_GATE(CLK_TOP_I2SI2_SEL, "i2si2_sel",
- i2so1_parents, 0x530, 16, 2, 23),
- MUX_GATE(CLK_TOP_I2SI3_SEL, "i2si3_sel",
- i2so1_parents, 0x530, 24, 2, 31),
+ MUX_GATE(CLK_TOP_TDMO1_SEL, "tdmo1_sel", i2so1_parents, 0x530, 0, 2, 7),
+ MUX_GATE(CLK_TOP_I2SI1_SEL, "i2si1_sel", i2so1_parents, 0x530, 8, 2, 15),
+ MUX_GATE(CLK_TOP_I2SI2_SEL, "i2si2_sel", i2so1_parents, 0x530, 16, 2, 23),
+ MUX_GATE(CLK_TOP_I2SI3_SEL, "i2si3_sel", i2so1_parents, 0x530, 24, 2, 31),
/* CLK_CFG_14 */
- MUX_GATE(CLK_TOP_ETHER_125M_SEL, "ether_125m_sel",
- ether_125m_parents, 0x540, 0, 2, 7),
- MUX_GATE(CLK_TOP_ETHER_50M_SEL, "ether_50m_sel",
- ether_50m_parents, 0x540, 8, 2, 15),
- MUX_GATE(CLK_TOP_JPGDEC_SEL, "jpgdec_sel",
- jpgdec_parents, 0x540, 16, 4, 23),
- MUX_GATE(CLK_TOP_SPISLV_SEL, "spislv_sel",
- spislv_parents, 0x540, 24, 3, 31),
+ MUX_GATE(CLK_TOP_ETHER_125M_SEL, "ether_125m_sel", ether_125m_parents,
+ 0x540, 0, 2, 7),
+ MUX_GATE(CLK_TOP_ETHER_50M_SEL, "ether_50m_sel", ether_50m_parents,
+ 0x540, 8, 2, 15),
+ MUX_GATE(CLK_TOP_JPGDEC_SEL, "jpgdec_sel", jpgdec_parents, 0x540, 16, 4, 23),
+ MUX_GATE(CLK_TOP_SPISLV_SEL, "spislv_sel", spislv_parents, 0x540, 24, 3, 31),
/* CLK_CFG_15 */
- MUX_GATE(CLK_TOP_ETHER_50M_RMII_SEL, "ether_sel",
- ether_parents, 0x550, 0, 2, 7),
- MUX_GATE(CLK_TOP_CAM2TG_SEL, "cam2tg_sel",
- camtg_parents, 0x550, 8, 4, 15),
- MUX_GATE(CLK_TOP_DI_SEL, "di_sel",
- di_parents, 0x550, 16, 3, 23),
- MUX_GATE(CLK_TOP_TVD_SEL, "tvd_sel",
- tvd_parents, 0x550, 24, 2, 31),
+ MUX_GATE(CLK_TOP_ETHER_50M_RMII_SEL, "ether_sel", ether_parents, 0x550, 0, 2, 7),
+ MUX_GATE(CLK_TOP_CAM2TG_SEL, "cam2tg_sel", camtg_parents, 0x550, 8, 4, 15),
+ MUX_GATE(CLK_TOP_DI_SEL, "di_sel", di_parents, 0x550, 16, 3, 23),
+ MUX_GATE(CLK_TOP_TVD_SEL, "tvd_sel", tvd_parents, 0x550, 24, 2, 31),
/* CLK_CFG_16 */
- MUX_GATE(CLK_TOP_I2C_SEL, "i2c_sel",
- i2c_parents, 0x560, 0, 3, 7),
- MUX_GATE(CLK_TOP_PWM_INFRA_SEL, "pwm_infra_sel",
- pwm_parents, 0x560, 8, 2, 15),
- MUX_GATE(CLK_TOP_MSDC0P_AES_SEL, "msdc0p_aes_sel",
- msdc0p_aes_parents, 0x560, 16, 2, 23),
- MUX_GATE(CLK_TOP_CMSYS_SEL, "cmsys_sel",
- cmsys_parents, 0x560, 24, 3, 31),
+ MUX_GATE(CLK_TOP_I2C_SEL, "i2c_sel", i2c_parents, 0x560, 0, 3, 7),
+ MUX_GATE(CLK_TOP_PWM_INFRA_SEL, "pwm_infra_sel", pwm_parents, 0x560, 8, 2, 15),
+ MUX_GATE(CLK_TOP_MSDC0P_AES_SEL, "msdc0p_aes_sel", msdc0p_aes_parents,
+ 0x560, 16, 2, 23),
+ MUX_GATE(CLK_TOP_CMSYS_SEL, "cmsys_sel", cmsys_parents, 0x560, 24, 3, 31),
/* CLK_CFG_17 */
- MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel",
- gcpu_parents, 0x570, 0, 3, 7),
+ MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x570, 0, 3, 7),
/* CLK_AUDDIV_4 */
- MUX(CLK_TOP_AUD_APLL1_SEL, "aud_apll1_sel",
- aud_apll1_parents, 0x134, 0, 1),
- MUX(CLK_TOP_AUD_APLL2_SEL, "aud_apll2_sel",
- aud_apll2_parents, 0x134, 1, 1),
- MUX(CLK_TOP_DA_AUDULL_VTX_6P5M_SEL, "audull_vtx_sel",
- audull_vtx_parents, 0x134, 31, 1),
- MUX(CLK_TOP_APLL1_REF_SEL, "apll1_ref_sel",
- apll1_ref_parents, 0x134, 4, 3),
- MUX(CLK_TOP_APLL2_REF_SEL, "apll2_ref_sel",
- apll1_ref_parents, 0x134, 7, 3),
+ MUX(CLK_TOP_AUD_APLL1_SEL, "aud_apll1_sel", aud_apll1_parents, 0x134, 0, 1),
+ MUX(CLK_TOP_AUD_APLL2_SEL, "aud_apll2_sel", aud_apll2_parents, 0x134, 1, 1),
+ MUX(CLK_TOP_DA_AUDULL_VTX_6P5M_SEL, "audull_vtx_sel", audull_vtx_parents,
+ 0x134, 31, 1),
+ MUX(CLK_TOP_APLL1_REF_SEL, "apll1_ref_sel", apll1_ref_parents, 0x134, 4, 3),
+ MUX(CLK_TOP_APLL2_REF_SEL, "apll2_ref_sel", apll1_ref_parents, 0x134, 7, 3),
};
static const char * const mcu_mp0_parents[] = {
@@ -926,13 +780,13 @@ static const char * const mcu_bus_parents[] = {
static struct mtk_composite mcu_muxes[] = {
/* mp0_pll_divider_cfg */
MUX_GATE_FLAGS(CLK_MCU_MP0_SEL, "mcu_mp0_sel", mcu_mp0_parents, 0x7A0,
- 9, 2, -1, CLK_IS_CRITICAL),
+ 9, 2, -1, CLK_IS_CRITICAL),
/* mp2_pll_divider_cfg */
MUX_GATE_FLAGS(CLK_MCU_MP2_SEL, "mcu_mp2_sel", mcu_mp2_parents, 0x7A8,
- 9, 2, -1, CLK_IS_CRITICAL),
+ 9, 2, -1, CLK_IS_CRITICAL),
/* bus_pll_divider_cfg */
MUX_GATE_FLAGS(CLK_MCU_BUS_SEL, "mcu_bus_sel", mcu_bus_parents, 0x7C0,
- 9, 2, -1, CLK_IS_CRITICAL),
+ 9, 2, -1, CLK_IS_CRITICAL),
};
static const struct mtk_clk_divider top_adj_divs[] = {
@@ -958,23 +812,11 @@ static const struct mtk_gate_regs top1_cg_regs = {
.sta_ofs = 0x424,
};
-#define GATE_TOP0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
-
-#define GATE_TOP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_TOP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
+
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate top_clks[] = {
/* TOP0 */
@@ -998,14 +840,8 @@ static const struct mtk_gate_regs infra_cg_regs = {
.sta_ofs = 0x48,
};
-#define GATE_INFRA(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_INFRA(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate infra_clks[] = {
GATE_INFRA(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0),
@@ -1035,227 +871,65 @@ static const struct mtk_gate_regs peri2_cg_regs = {
.sta_ofs = 0x42c,
};
-#define GATE_PERI0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_PERI1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_PERI2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_PERI0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_PERI1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_PERI2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri2_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate peri_clks[] = {
/* PERI0 */
- GATE_PERI0(CLK_PERI_NFI, "per_nfi",
- "axi_sel", 0),
- GATE_PERI0(CLK_PERI_THERM, "per_therm",
- "axi_sel", 1),
- GATE_PERI0(CLK_PERI_PWM0, "per_pwm0",
- "pwm_sel", 2),
- GATE_PERI0(CLK_PERI_PWM1, "per_pwm1",
- "pwm_sel", 3),
- GATE_PERI0(CLK_PERI_PWM2, "per_pwm2",
- "pwm_sel", 4),
- GATE_PERI0(CLK_PERI_PWM3, "per_pwm3",
- "pwm_sel", 5),
- GATE_PERI0(CLK_PERI_PWM4, "per_pwm4",
- "pwm_sel", 6),
- GATE_PERI0(CLK_PERI_PWM5, "per_pwm5",
- "pwm_sel", 7),
- GATE_PERI0(CLK_PERI_PWM6, "per_pwm6",
- "pwm_sel", 8),
- GATE_PERI0(CLK_PERI_PWM7, "per_pwm7",
- "pwm_sel", 9),
- GATE_PERI0(CLK_PERI_PWM, "per_pwm",
- "pwm_sel", 10),
- GATE_PERI0(CLK_PERI_AP_DMA, "per_ap_dma",
- "axi_sel", 13),
- GATE_PERI0(CLK_PERI_MSDC30_0, "per_msdc30_0",
- "msdc50_0_sel", 14),
- GATE_PERI0(CLK_PERI_MSDC30_1, "per_msdc30_1",
- "msdc30_1_sel", 15),
- GATE_PERI0(CLK_PERI_MSDC30_2, "per_msdc30_2",
- "msdc30_2_sel", 16),
- GATE_PERI0(CLK_PERI_MSDC30_3, "per_msdc30_3",
- "msdc30_3_sel", 17),
- GATE_PERI0(CLK_PERI_UART0, "per_uart0",
- "uart_sel", 20),
- GATE_PERI0(CLK_PERI_UART1, "per_uart1",
- "uart_sel", 21),
- GATE_PERI0(CLK_PERI_UART2, "per_uart2",
- "uart_sel", 22),
- GATE_PERI0(CLK_PERI_UART3, "per_uart3",
- "uart_sel", 23),
- GATE_PERI0(CLK_PERI_I2C0, "per_i2c0",
- "axi_sel", 24),
- GATE_PERI0(CLK_PERI_I2C1, "per_i2c1",
- "axi_sel", 25),
- GATE_PERI0(CLK_PERI_I2C2, "per_i2c2",
- "axi_sel", 26),
- GATE_PERI0(CLK_PERI_I2C3, "per_i2c3",
- "axi_sel", 27),
- GATE_PERI0(CLK_PERI_I2C4, "per_i2c4",
- "axi_sel", 28),
- GATE_PERI0(CLK_PERI_AUXADC, "per_auxadc",
- "ltepll_fs26m", 29),
- GATE_PERI0(CLK_PERI_SPI0, "per_spi0",
- "spi_sel", 30),
+ GATE_PERI0(CLK_PERI_NFI, "per_nfi", "axi_sel", 0),
+ GATE_PERI0(CLK_PERI_THERM, "per_therm", "axi_sel", 1),
+ GATE_PERI0(CLK_PERI_PWM0, "per_pwm0", "pwm_sel", 2),
+ GATE_PERI0(CLK_PERI_PWM1, "per_pwm1", "pwm_sel", 3),
+ GATE_PERI0(CLK_PERI_PWM2, "per_pwm2", "pwm_sel", 4),
+ GATE_PERI0(CLK_PERI_PWM3, "per_pwm3", "pwm_sel", 5),
+ GATE_PERI0(CLK_PERI_PWM4, "per_pwm4", "pwm_sel", 6),
+ GATE_PERI0(CLK_PERI_PWM5, "per_pwm5", "pwm_sel", 7),
+ GATE_PERI0(CLK_PERI_PWM6, "per_pwm6", "pwm_sel", 8),
+ GATE_PERI0(CLK_PERI_PWM7, "per_pwm7", "pwm_sel", 9),
+ GATE_PERI0(CLK_PERI_PWM, "per_pwm", "pwm_sel", 10),
+ GATE_PERI0(CLK_PERI_AP_DMA, "per_ap_dma", "axi_sel", 13),
+ GATE_PERI0(CLK_PERI_MSDC30_0, "per_msdc30_0", "msdc50_0_sel", 14),
+ GATE_PERI0(CLK_PERI_MSDC30_1, "per_msdc30_1", "msdc30_1_sel", 15),
+ GATE_PERI0(CLK_PERI_MSDC30_2, "per_msdc30_2", "msdc30_2_sel", 16),
+ GATE_PERI0(CLK_PERI_MSDC30_3, "per_msdc30_3", "msdc30_3_sel", 17),
+ GATE_PERI0(CLK_PERI_UART0, "per_uart0", "uart_sel", 20),
+ GATE_PERI0(CLK_PERI_UART1, "per_uart1", "uart_sel", 21),
+ GATE_PERI0(CLK_PERI_UART2, "per_uart2", "uart_sel", 22),
+ GATE_PERI0(CLK_PERI_UART3, "per_uart3", "uart_sel", 23),
+ GATE_PERI0(CLK_PERI_I2C0, "per_i2c0", "axi_sel", 24),
+ GATE_PERI0(CLK_PERI_I2C1, "per_i2c1", "axi_sel", 25),
+ GATE_PERI0(CLK_PERI_I2C2, "per_i2c2", "axi_sel", 26),
+ GATE_PERI0(CLK_PERI_I2C3, "per_i2c3", "axi_sel", 27),
+ GATE_PERI0(CLK_PERI_I2C4, "per_i2c4", "axi_sel", 28),
+ GATE_PERI0(CLK_PERI_AUXADC, "per_auxadc", "ltepll_fs26m", 29),
+ GATE_PERI0(CLK_PERI_SPI0, "per_spi0", "spi_sel", 30),
/* PERI1 */
- GATE_PERI1(CLK_PERI_SPI, "per_spi",
- "spinor_sel", 1),
- GATE_PERI1(CLK_PERI_I2C5, "per_i2c5",
- "axi_sel", 3),
- GATE_PERI1(CLK_PERI_SPI2, "per_spi2",
- "spi_sel", 5),
- GATE_PERI1(CLK_PERI_SPI3, "per_spi3",
- "spi_sel", 6),
- GATE_PERI1(CLK_PERI_SPI5, "per_spi5",
- "spi_sel", 8),
- GATE_PERI1(CLK_PERI_UART4, "per_uart4",
- "uart_sel", 9),
- GATE_PERI1(CLK_PERI_SFLASH, "per_sflash",
- "uart_sel", 11),
- GATE_PERI1(CLK_PERI_GMAC, "per_gmac",
- "uart_sel", 12),
- GATE_PERI1(CLK_PERI_PCIE0, "per_pcie0",
- "uart_sel", 14),
- GATE_PERI1(CLK_PERI_PCIE1, "per_pcie1",
- "uart_sel", 15),
- GATE_PERI1(CLK_PERI_GMAC_PCLK, "per_gmac_pclk",
- "uart_sel", 16),
+ GATE_PERI1(CLK_PERI_SPI, "per_spi", "spinor_sel", 1),
+ GATE_PERI1(CLK_PERI_I2C5, "per_i2c5", "axi_sel", 3),
+ GATE_PERI1(CLK_PERI_SPI2, "per_spi2", "spi_sel", 5),
+ GATE_PERI1(CLK_PERI_SPI3, "per_spi3", "spi_sel", 6),
+ GATE_PERI1(CLK_PERI_SPI5, "per_spi5", "spi_sel", 8),
+ GATE_PERI1(CLK_PERI_UART4, "per_uart4", "uart_sel", 9),
+ GATE_PERI1(CLK_PERI_SFLASH, "per_sflash", "uart_sel", 11),
+ GATE_PERI1(CLK_PERI_GMAC, "per_gmac", "uart_sel", 12),
+ GATE_PERI1(CLK_PERI_PCIE0, "per_pcie0", "uart_sel", 14),
+ GATE_PERI1(CLK_PERI_PCIE1, "per_pcie1", "uart_sel", 15),
+ GATE_PERI1(CLK_PERI_GMAC_PCLK, "per_gmac_pclk", "uart_sel", 16),
/* PERI2 */
- GATE_PERI2(CLK_PERI_MSDC50_0_EN, "per_msdc50_0_en",
- "msdc50_0_sel", 0),
- GATE_PERI2(CLK_PERI_MSDC30_1_EN, "per_msdc30_1_en",
- "msdc30_1_sel", 1),
- GATE_PERI2(CLK_PERI_MSDC30_2_EN, "per_msdc30_2_en",
- "msdc30_2_sel", 2),
- GATE_PERI2(CLK_PERI_MSDC30_3_EN, "per_msdc30_3_en",
- "msdc30_3_sel", 3),
- GATE_PERI2(CLK_PERI_MSDC50_0_HCLK_EN, "per_msdc50_0_h",
- "msdc50_0_h_sel", 4),
- GATE_PERI2(CLK_PERI_MSDC50_3_HCLK_EN, "per_msdc50_3_h",
- "msdc50_3_h_sel", 5),
- GATE_PERI2(CLK_PERI_MSDC30_0_QTR_EN, "per_msdc30_0_q",
- "axi_sel", 6),
- GATE_PERI2(CLK_PERI_MSDC30_3_QTR_EN, "per_msdc30_3_q",
- "mem_sel", 7),
-};
-
-#define MT2712_PLL_FMAX (3000UL * MHZ)
-
-#define CON0_MT2712_RST_BAR BIT(24)
-
-#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \
- _tuner_en_bit, _pcw_reg, _pcw_shift, \
- _div_table) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = CON0_MT2712_RST_BAR, \
- .fmax = MT2712_PLL_FMAX, \
- .pcwbits = _pcwbits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .tuner_en_reg = _tuner_en_reg, \
- .tuner_en_bit = _tuner_en_bit, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .div_table = _div_table, \
- }
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \
- _tuner_en_bit, _pcw_reg, _pcw_shift) \
- PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _pcwbits, _pd_reg, _pd_shift, _tuner_reg, \
- _tuner_en_reg, _tuner_en_bit, _pcw_reg, \
- _pcw_shift, NULL)
-
-static const struct mtk_pll_div_table armca35pll_div_table[] = {
- { .div = 0, .freq = MT2712_PLL_FMAX },
- { .div = 1, .freq = 1202500000 },
- { .div = 2, .freq = 500500000 },
- { .div = 3, .freq = 315250000 },
- { .div = 4, .freq = 157625000 },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_div_table armca72pll_div_table[] = {
- { .div = 0, .freq = MT2712_PLL_FMAX },
- { .div = 1, .freq = 994500000 },
- { .div = 2, .freq = 520000000 },
- { .div = 3, .freq = 315250000 },
- { .div = 4, .freq = 157625000 },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_div_table mmpll_div_table[] = {
- { .div = 0, .freq = MT2712_PLL_FMAX },
- { .div = 1, .freq = 1001000000 },
- { .div = 2, .freq = 601250000 },
- { .div = 3, .freq = 250250000 },
- { .div = 4, .freq = 125125000 },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0230, 0x023C, 0xf0000100,
- HAVE_RST_BAR, 31, 0x0230, 4, 0, 0, 0, 0x0234, 0),
- PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0240, 0x024C, 0xfe000100,
- HAVE_RST_BAR, 31, 0x0240, 4, 0, 0, 0, 0x0244, 0),
- PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x0320, 0x032C, 0xc0000100,
- 0, 31, 0x0320, 4, 0, 0, 0, 0x0324, 0),
- PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x0280, 0x028C, 0x00000100,
- 0, 31, 0x0280, 4, 0, 0, 0, 0x0284, 0),
- PLL(CLK_APMIXED_APLL1, "apll1", 0x0330, 0x0340, 0x00000100,
- 0, 31, 0x0330, 4, 0x0338, 0x0014, 0, 0x0334, 0),
- PLL(CLK_APMIXED_APLL2, "apll2", 0x0350, 0x0360, 0x00000100,
- 0, 31, 0x0350, 4, 0x0358, 0x0014, 1, 0x0354, 0),
- PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x0370, 0x037c, 0x00000100,
- 0, 31, 0x0370, 4, 0, 0, 0, 0x0374, 0),
- PLL(CLK_APMIXED_LVDSPLL2, "lvdspll2", 0x0390, 0x039C, 0x00000100,
- 0, 31, 0x0390, 4, 0, 0, 0, 0x0394, 0),
- PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0270, 0x027C, 0x00000100,
- 0, 31, 0x0270, 4, 0, 0, 0, 0x0274, 0),
- PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x0410, 0x041C, 0x00000100,
- 0, 31, 0x0410, 4, 0, 0, 0, 0x0414, 0),
- PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x0290, 0x029C, 0xc0000100,
- 0, 31, 0x0290, 4, 0, 0, 0, 0x0294, 0),
- PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0250, 0x0260, 0x00000100,
- 0, 31, 0x0250, 4, 0, 0, 0, 0x0254, 0,
- mmpll_div_table),
- PLL_B(CLK_APMIXED_ARMCA35PLL, "armca35pll", 0x0100, 0x0110, 0xf0000100,
- HAVE_RST_BAR, 31, 0x0100, 4, 0, 0, 0, 0x0104, 0,
- armca35pll_div_table),
- PLL_B(CLK_APMIXED_ARMCA72PLL, "armca72pll", 0x0210, 0x0220, 0x00000100,
- 0, 31, 0x0210, 4, 0, 0, 0, 0x0214, 0,
- armca72pll_div_table),
- PLL(CLK_APMIXED_ETHERPLL, "etherpll", 0x0300, 0x030C, 0xc0000100,
- 0, 31, 0x0300, 4, 0, 0, 0, 0x0304, 0),
+ GATE_PERI2(CLK_PERI_MSDC50_0_EN, "per_msdc50_0_en", "msdc50_0_sel", 0),
+ GATE_PERI2(CLK_PERI_MSDC30_1_EN, "per_msdc30_1_en", "msdc30_1_sel", 1),
+ GATE_PERI2(CLK_PERI_MSDC30_2_EN, "per_msdc30_2_en", "msdc30_2_sel", 2),
+ GATE_PERI2(CLK_PERI_MSDC30_3_EN, "per_msdc30_3_en", "msdc30_3_sel", 3),
+ GATE_PERI2(CLK_PERI_MSDC50_0_HCLK_EN, "per_msdc50_0_h", "msdc50_0_h_sel", 4),
+ GATE_PERI2(CLK_PERI_MSDC50_3_HCLK_EN, "per_msdc50_3_h", "msdc50_3_h_sel", 5),
+ GATE_PERI2(CLK_PERI_MSDC30_0_QTR_EN, "per_msdc30_0_q", "axi_sel", 6),
+ GATE_PERI2(CLK_PERI_MSDC30_3_QTR_EN, "per_msdc30_3_q", "mem_sel", 7),
};
static u16 infrasys_rst_ofs[] = { 0x30, 0x34, };
@@ -1276,213 +950,56 @@ static const struct mtk_clk_rst_desc clk_rst_desc[] = {
},
};
-static int clk_mt2712_apmixed_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
-
-static struct clk_hw_onecell_data *top_clk_data;
-
-static void clk_mt2712_top_init_early(struct device_node *node)
-{
- int r, i;
-
- if (!top_clk_data) {
- top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
-
- for (i = 0; i < CLK_TOP_NR_CLK; i++)
- top_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
- }
-
- mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs),
- top_clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, top_clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-
-CLK_OF_DECLARE_DRIVER(mt2712_topckgen, "mediatek,mt2712-topckgen",
- clk_mt2712_top_init_early);
-
-static int clk_mt2712_top_probe(struct platform_device *pdev)
-{
- int r, i;
- struct device_node *node = pdev->dev.of_node;
- void __iomem *base;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base)) {
- pr_err("%s(): ioremap failed\n", __func__);
- return PTR_ERR(base);
- }
-
- if (!top_clk_data) {
- top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
- } else {
- for (i = 0; i < CLK_TOP_NR_CLK; i++) {
- if (top_clk_data->hws[i] == ERR_PTR(-EPROBE_DEFER))
- top_clk_data->hws[i] = ERR_PTR(-ENOENT);
- }
- }
-
- mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
- top_clk_data);
- mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs),
- top_clk_data);
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt2712_clk_lock, top_clk_data);
- mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs), base,
- &mt2712_clk_lock, top_clk_data);
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
- top_clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, top_clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
-
-static int clk_mt2712_infra_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
-
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[0]);
-
- return r;
-}
-
-static int clk_mt2712_peri_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
-
- mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[1]);
-
- return r;
-}
-
-static int clk_mt2712_mcu_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
- void __iomem *base;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base)) {
- pr_err("%s(): ioremap failed\n", __func__);
- return PTR_ERR(base);
- }
-
- clk_data = mtk_alloc_clk_data(CLK_MCU_NR_CLK);
-
- mtk_clk_register_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), base,
- &mt2712_clk_lock, clk_data);
+static const struct mtk_clk_desc topck_desc = {
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .divider_clks = top_adj_divs,
+ .num_divider_clks = ARRAY_SIZE(top_adj_divs),
+ .clk_lock = &mt2712_clk_lock,
+};
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+static const struct mtk_clk_desc mcu_desc = {
+ .composite_clks = mcu_muxes,
+ .num_composite_clks = ARRAY_SIZE(mcu_muxes),
+ .clk_lock = &mt2712_clk_lock,
+};
- if (r != 0)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .rst_desc = &clk_rst_desc[0],
+};
- return r;
-}
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_clks,
+ .num_clks = ARRAY_SIZE(peri_clks),
+ .rst_desc = &clk_rst_desc[1],
+};
static const struct of_device_id of_match_clk_mt2712[] = {
- {
- .compatible = "mediatek,mt2712-apmixedsys",
- .data = clk_mt2712_apmixed_probe,
- }, {
- .compatible = "mediatek,mt2712-topckgen",
- .data = clk_mt2712_top_probe,
- }, {
- .compatible = "mediatek,mt2712-infracfg",
- .data = clk_mt2712_infra_probe,
- }, {
- .compatible = "mediatek,mt2712-pericfg",
- .data = clk_mt2712_peri_probe,
- }, {
- .compatible = "mediatek,mt2712-mcucfg",
- .data = clk_mt2712_mcu_probe,
- }, {
- /* sentinel */
- }
-};
-
-static int clk_mt2712_probe(struct platform_device *pdev)
-{
- int (*clk_probe)(struct platform_device *);
- int r;
-
- clk_probe = of_device_get_match_data(&pdev->dev);
- if (!clk_probe)
- return -EINVAL;
-
- r = clk_probe(pdev);
- if (r != 0)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+ { .compatible = "mediatek,mt2712-infracfg", .data = &infra_desc },
+ { .compatible = "mediatek,mt2712-mcucfg", .data = &mcu_desc },
+ { .compatible = "mediatek,mt2712-pericfg", .data = &peri_desc, },
+ { .compatible = "mediatek,mt2712-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt2712);
static struct platform_driver clk_mt2712_drv = {
- .probe = clk_mt2712_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt2712",
.of_match_table = of_match_clk_mt2712,
},
};
+module_platform_driver(clk_mt2712_drv);
-static int __init clk_mt2712_init(void)
-{
- return platform_driver_register(&clk_mt2712_drv);
-}
-
-arch_initcall(clk_mt2712_init);
+MODULE_DESCRIPTION("MediaTek MT2712 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
new file mode 100644
index 000000000000..e0949911e8f7
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-apmixedsys.h>
+
+#define AP_PLL_CON_5 0x014
+#define ARMPLL_CON0 0x200
+#define ARMPLL_CON1 0x204
+#define ARMPLL_PWR_CON0 0x20c
+#define MAINPLL_CON0 0x210
+#define MAINPLL_CON1 0x214
+#define MAINPLL_PWR_CON0 0x21c
+#define UNIVPLL_CON0 0x220
+#define UNIVPLL_CON1 0x224
+#define UNIVPLL_PWR_CON0 0x22c
+#define MMPLL_CON0 0x230
+#define MMPLL_CON1 0x234
+#define MMPLL_PWR_CON0 0x23c
+#define MSDCPLL_CON0 0x240
+#define MSDCPLL_CON1 0x244
+#define MSDCPLL_PWR_CON0 0x24c
+#define VENCPLL_CON0 0x250
+#define VENCPLL_CON1 0x254
+#define VENCPLL_PWR_CON0 0x25c
+#define TVDPLL_CON0 0x260
+#define TVDPLL_CON1 0x264
+#define TVDPLL_PWR_CON0 0x26c
+#define APLL1_CON0 0x270
+#define APLL1_CON1 0x274
+#define APLL1_CON2 0x278
+#define APLL1_PWR_CON0 0x280
+#define APLL2_CON0 0x284
+#define APLL2_CON1 0x288
+#define APLL2_CON2 0x28c
+#define APLL2_PWR_CON0 0x294
+
+#define CON0_RST_BAR BIT(24)
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _rst_bar_mask, \
+ _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcwbits, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = "clk26m", \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_chg_reg = _pcw_reg, \
+ .pcwbits = _pcwbits, \
+ .flags = _flags, \
+ }
+
+static const struct mtk_pll_data apmixedsys_plls[] = {
+ PLL(CLK_APMIXED_ARMPLL, "armpll", ARMPLL_CON0, ARMPLL_PWR_CON0, 0x00000001, 0, ARMPLL_CON1, 24, 0, 0, 0, ARMPLL_CON1, 21, PLL_AO),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", MAINPLL_CON0, MAINPLL_PWR_CON0, 0xf0000101, CON0_RST_BAR, MAINPLL_CON1, 24, 0, 0, 0, MAINPLL_CON1, 21, HAVE_RST_BAR),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", UNIVPLL_CON0, UNIVPLL_PWR_CON0, 0xfc000001, CON0_RST_BAR, UNIVPLL_CON1, 24, 0, 0, 0, UNIVPLL_CON1, 21, HAVE_RST_BAR),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", MMPLL_CON0, MMPLL_PWR_CON0, 0x00000001, 0, MMPLL_CON1, 24, 0, 0, 0, MMPLL_CON1, 21, 0),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", MSDCPLL_CON0, MSDCPLL_PWR_CON0, 0x00000001, 0, MSDCPLL_CON1, 24, 0, 0, 0, MSDCPLL_CON1, 21, 0),
+ PLL(CLK_APMIXED_VENCPLL, "vencpll", VENCPLL_CON0, VENCPLL_PWR_CON0, 0x00000001, CON0_RST_BAR, VENCPLL_CON1, 24, 0, 0, 0, VENCPLL_CON1, 21, HAVE_RST_BAR),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", TVDPLL_CON0, TVDPLL_PWR_CON0, 0x00000001, 0, TVDPLL_CON1, 24, 0, 0, 0, TVDPLL_CON1, 21, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", APLL1_CON0, APLL1_PWR_CON0, 0x00000001, 0, APLL1_CON0, 4, APLL1_CON2, AP_PLL_CON_5, 0, APLL1_CON1, 31, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", APLL2_CON0, APLL2_PWR_CON0, 0x00000001, 0, APLL2_CON0, 4, APLL2_CON2, AP_PLL_CON_5, 1, APLL2_CON1, 31, 0)
+};
+
+static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
+{
+ void __iomem *base;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct clk_hw_onecell_data *clk_data;
+ int ret;
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_devm_alloc_clk_data(&pdev->dev, ARRAY_SIZE(apmixedsys_plls));
+ if (!clk_data)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, clk_data);
+
+ ret = mtk_clk_register_plls(pdev->dev.of_node, apmixedsys_plls,
+ ARRAY_SIZE(apmixedsys_plls), clk_data);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register PLLs: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
+ clk_data);
+ if (ret)
+ dev_err(&pdev->dev,
+ "Failed to register clock provider: %d\n", ret);
+
+ return ret;
+}
+
+static void clk_mt6735_apmixed_remove(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls), clk_data);
+}
+
+static const struct of_device_id of_match_mt6735_apmixedsys[] = {
+ { .compatible = "mediatek,mt6735-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_mt6735_apmixedsys);
+
+static struct platform_driver clk_mt6735_apmixedsys = {
+ .probe = clk_mt6735_apmixed_probe,
+ .remove = clk_mt6735_apmixed_remove,
+ .driver = {
+ .name = "clk-mt6735-apmixedsys",
+ .of_match_table = of_match_mt6735_apmixedsys,
+ },
+};
+module_platform_driver(clk_mt6735_apmixedsys);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("MediaTek MT6735 apmixedsys clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-imgsys.c b/drivers/clk/mediatek/clk-mt6735-imgsys.c
new file mode 100644
index 000000000000..c564f8f72432
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-imgsys.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-imgsys.h>
+
+#define IMG_CG_CON 0x00
+#define IMG_CG_SET 0x04
+#define IMG_CG_CLR 0x08
+
+static struct mtk_gate_regs imgsys_cg_regs = {
+ .set_ofs = IMG_CG_SET,
+ .clr_ofs = IMG_CG_CLR,
+ .sta_ofs = IMG_CG_CON,
+};
+
+static const struct mtk_gate imgsys_gates[] = {
+ GATE_MTK(CLK_IMG_SMI_LARB2, "smi_larb2", "mm_sel", &imgsys_cg_regs, 0, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_CAM_SMI, "cam_smi", "mm_sel", &imgsys_cg_regs, 5, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_CAM_CAM, "cam_cam", "mm_sel", &imgsys_cg_regs, 6, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_SEN_TG, "sen_tg", "mm_sel", &imgsys_cg_regs, 7, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_SEN_CAM, "sen_cam", "mm_sel", &imgsys_cg_regs, 8, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_CAM_SV, "cam_sv", "mm_sel", &imgsys_cg_regs, 9, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_SUFOD, "sufod", "mm_sel", &imgsys_cg_regs, 10, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_IMG_FD, "fd", "mm_sel", &imgsys_cg_regs, 11, &mtk_clk_gate_ops_setclr),
+};
+
+static const struct mtk_clk_desc imgsys_clks = {
+ .clks = imgsys_gates,
+ .num_clks = ARRAY_SIZE(imgsys_gates),
+};
+
+static const struct of_device_id of_match_mt6735_imgsys[] = {
+ { .compatible = "mediatek,mt6735-imgsys", .data = &imgsys_clks },
+ { /* sentinel */ }
+};
+
+static struct platform_driver clk_mt6735_imgsys = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-imgsys",
+ .of_match_table = of_match_mt6735_imgsys,
+ },
+};
+module_platform_driver(clk_mt6735_imgsys);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("MediaTek MT6735 imgsys clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-infracfg.c b/drivers/clk/mediatek/clk-mt6735-infracfg.c
new file mode 100644
index 000000000000..c1171f903cfa
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-infracfg.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-infracfg.h>
+#include <dt-bindings/reset/mediatek,mt6735-infracfg.h>
+
+#define INFRA_RST0 0x30
+#define INFRA_GLOBALCON_PDN0 0x40
+#define INFRA_PDN1 0x44
+#define INFRA_PDN_STA 0x48
+
+#define RST_NR_PER_BANK 32
+
+static struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = INFRA_GLOBALCON_PDN0,
+ .clr_ofs = INFRA_PDN1,
+ .sta_ofs = INFRA_PDN_STA,
+};
+
+static const struct mtk_gate infracfg_gates[] = {
+ GATE_MTK(CLK_INFRA_DBG, "dbg", "axi_sel", &infra_cg_regs, 0, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_GCE, "gce", "axi_sel", &infra_cg_regs, 1, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_TRBG, "trbg", "axi_sel", &infra_cg_regs, 2, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_CPUM, "cpum", "axi_sel", &infra_cg_regs, 3, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_DEVAPC, "devapc", "axi_sel", &infra_cg_regs, 4, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_AUDIO, "audio", "aud_intbus_sel", &infra_cg_regs, 5, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_GCPU, "gcpu", "axi_sel", &infra_cg_regs, 6, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_L2C_SRAM, "l2csram", "axi_sel", &infra_cg_regs, 7, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_M4U, "m4u", "axi_sel", &infra_cg_regs, 8, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_CLDMA, "cldma", "axi_sel", &infra_cg_regs, 12, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_CONNMCU_BUS, "connmcu_bus", "axi_sel", &infra_cg_regs, 15, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_KP, "kp", "axi_sel", &infra_cg_regs, 16, &mtk_clk_gate_ops_setclr),
+ GATE_MTK_FLAGS(CLK_INFRA_APXGPT, "apxgpt", "axi_sel", &infra_cg_regs, 18, &mtk_clk_gate_ops_setclr, CLK_IS_CRITICAL),
+ GATE_MTK(CLK_INFRA_SEJ, "sej", "axi_sel", &infra_cg_regs, 19, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_CCIF0_AP, "ccif0ap", "axi_sel", &infra_cg_regs, 20, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_CCIF1_AP, "ccif1ap", "axi_sel", &infra_cg_regs, 21, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_PMIC_SPI, "pmicspi", "pmicspi_sel", &infra_cg_regs, 22, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_INFRA_PMIC_WRAP, "pmicwrap", "axi_sel", &infra_cg_regs, 23, &mtk_clk_gate_ops_setclr)
+};
+
+static u16 infracfg_rst_bank_ofs[] = { INFRA_RST0 };
+
+static u16 infracfg_rst_idx_map[] = {
+ [MT6735_INFRA_RST0_EMI_REG] = 0 * RST_NR_PER_BANK + 0,
+ [MT6735_INFRA_RST0_DRAMC0_AO] = 0 * RST_NR_PER_BANK + 1,
+ [MT6735_INFRA_RST0_AP_CIRQ_EINT] = 0 * RST_NR_PER_BANK + 3,
+ [MT6735_INFRA_RST0_APXGPT] = 0 * RST_NR_PER_BANK + 4,
+ [MT6735_INFRA_RST0_SCPSYS] = 0 * RST_NR_PER_BANK + 5,
+ [MT6735_INFRA_RST0_KP] = 0 * RST_NR_PER_BANK + 6,
+ [MT6735_INFRA_RST0_PMIC_WRAP] = 0 * RST_NR_PER_BANK + 7,
+ [MT6735_INFRA_RST0_CLDMA_AO_TOP] = 0 * RST_NR_PER_BANK + 8,
+ [MT6735_INFRA_RST0_USBSIF_TOP] = 0 * RST_NR_PER_BANK + 9,
+ [MT6735_INFRA_RST0_EMI] = 0 * RST_NR_PER_BANK + 16,
+ [MT6735_INFRA_RST0_CCIF] = 0 * RST_NR_PER_BANK + 17,
+ [MT6735_INFRA_RST0_DRAMC0] = 0 * RST_NR_PER_BANK + 18,
+ [MT6735_INFRA_RST0_EMI_AO_REG] = 0 * RST_NR_PER_BANK + 19,
+ [MT6735_INFRA_RST0_CCIF_AO] = 0 * RST_NR_PER_BANK + 20,
+ [MT6735_INFRA_RST0_TRNG] = 0 * RST_NR_PER_BANK + 21,
+ [MT6735_INFRA_RST0_SYS_CIRQ] = 0 * RST_NR_PER_BANK + 22,
+ [MT6735_INFRA_RST0_GCE] = 0 * RST_NR_PER_BANK + 23,
+ [MT6735_INFRA_RST0_M4U] = 0 * RST_NR_PER_BANK + 24,
+ [MT6735_INFRA_RST0_CCIF1] = 0 * RST_NR_PER_BANK + 25,
+ [MT6735_INFRA_RST0_CLDMA_TOP_PD] = 0 * RST_NR_PER_BANK + 26
+};
+
+static const struct mtk_clk_rst_desc infracfg_resets = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = infracfg_rst_bank_ofs,
+ .rst_bank_nr = ARRAY_SIZE(infracfg_rst_bank_ofs),
+ .rst_idx_map = infracfg_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(infracfg_rst_idx_map)
+};
+
+static const struct mtk_clk_desc infracfg_clks = {
+ .clks = infracfg_gates,
+ .num_clks = ARRAY_SIZE(infracfg_gates),
+
+ .rst_desc = &infracfg_resets
+};
+
+static const struct of_device_id of_match_mt6735_infracfg[] = {
+ { .compatible = "mediatek,mt6735-infracfg", .data = &infracfg_clks },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_mt6735_infracfg);
+
+static struct platform_driver clk_mt6735_infracfg = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-infracfg",
+ .of_match_table = of_match_mt6735_infracfg,
+ },
+};
+module_platform_driver(clk_mt6735_infracfg);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("MediaTek MT6735 infracfg clock and reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-mfgcfg.c b/drivers/clk/mediatek/clk-mt6735-mfgcfg.c
new file mode 100644
index 000000000000..1f5aedddf209
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-mfgcfg.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-mfgcfg.h>
+
+#define MFG_CG_CON 0x00
+#define MFG_CG_SET 0x04
+#define MFG_CG_CLR 0x08
+#define MFG_RESET 0x0c
+
+static struct mtk_gate_regs mfgcfg_cg_regs = {
+ .set_ofs = MFG_CG_SET,
+ .clr_ofs = MFG_CG_CLR,
+ .sta_ofs = MFG_CG_CON,
+};
+
+static const struct mtk_gate mfgcfg_gates[] = {
+ GATE_MTK(CLK_MFG_BG3D, "bg3d", "mfg_sel", &mfgcfg_cg_regs, 0, &mtk_clk_gate_ops_setclr),
+};
+
+static u16 mfgcfg_rst_ofs[] = { MFG_RESET };
+
+static const struct mtk_clk_rst_desc mfgcfg_resets = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = mfgcfg_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(mfgcfg_rst_ofs)
+};
+
+static const struct mtk_clk_desc mfgcfg_clks = {
+ .clks = mfgcfg_gates,
+ .num_clks = ARRAY_SIZE(mfgcfg_gates),
+
+ .rst_desc = &mfgcfg_resets
+};
+
+static const struct of_device_id of_match_mt6735_mfgcfg[] = {
+ { .compatible = "mediatek,mt6735-mfgcfg", .data = &mfgcfg_clks },
+ { /* sentinel */ }
+};
+
+static struct platform_driver clk_mt6735_mfgcfg = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-mfgcfg",
+ .of_match_table = of_match_mt6735_mfgcfg,
+ },
+};
+module_platform_driver(clk_mt6735_mfgcfg);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Mediatek MT6735 mfgcfg clock and reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-pericfg.c b/drivers/clk/mediatek/clk-mt6735-pericfg.c
new file mode 100644
index 000000000000..cbdf6d25c1b2
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-pericfg.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-pericfg.h>
+#include <dt-bindings/reset/mediatek,mt6735-pericfg.h>
+
+#define PERI_GLOBALCON_RST0 0x00
+#define PERI_GLOBALCON_RST1 0x04
+#define PERI_GLOBALCON_PDN0_SET 0x08
+#define PERI_GLOBALCON_PDN0_CLR 0x10
+#define PERI_GLOBALCON_PDN0_STA 0x18
+
+#define RST_NR_PER_BANK 32
+
+static struct mtk_gate_regs peri_cg_regs = {
+ .set_ofs = PERI_GLOBALCON_PDN0_SET,
+ .clr_ofs = PERI_GLOBALCON_PDN0_CLR,
+ .sta_ofs = PERI_GLOBALCON_PDN0_STA,
+};
+
+static const struct mtk_gate pericfg_gates[] = {
+ GATE_MTK(CLK_PERI_DISP_PWM, "disp_pwm", "disppwm_sel", &peri_cg_regs, 0, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_THERM, "therm", "axi_sel", &peri_cg_regs, 1, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM1, "pwm1", "axi_sel", &peri_cg_regs, 2, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM2, "pwm2", "axi_sel", &peri_cg_regs, 3, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM3, "pwm3", "axi_sel", &peri_cg_regs, 4, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM4, "pwm4", "axi_sel", &peri_cg_regs, 5, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM5, "pwm5", "axi_sel", &peri_cg_regs, 6, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM6, "pwm6", "axi_sel", &peri_cg_regs, 7, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM7, "pwm7", "axi_sel", &peri_cg_regs, 8, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_PWM, "pwm", "axi_sel", &peri_cg_regs, 9, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_USB0, "usb0", "usb20_sel", &peri_cg_regs, 10, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_IRDA, "irda", "irda_sel", &peri_cg_regs, 11, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_APDMA, "apdma", "axi_sel", &peri_cg_regs, 12, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_MSDC30_0, "msdc30_0", "msdc30_0_sel", &peri_cg_regs, 13, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_MSDC30_1, "msdc30_1", "msdc30_1_sel", &peri_cg_regs, 14, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_MSDC30_2, "msdc30_2", "msdc30_2_sel", &peri_cg_regs, 15, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_MSDC30_3, "msdc30_3", "msdc30_3_sel", &peri_cg_regs, 16, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_UART0, "uart0", "uart_sel", &peri_cg_regs, 17, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_UART1, "uart1", "uart_sel", &peri_cg_regs, 18, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_UART2, "uart2", "uart_sel", &peri_cg_regs, 19, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_UART3, "uart3", "uart_sel", &peri_cg_regs, 20, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_UART4, "uart4", "uart_sel", &peri_cg_regs, 21, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_BTIF, "btif", "axi_sel", &peri_cg_regs, 22, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_I2C0, "i2c0", "axi_sel", &peri_cg_regs, 23, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_I2C1, "i2c1", "axi_sel", &peri_cg_regs, 24, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_I2C2, "i2c2", "axi_sel", &peri_cg_regs, 25, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_I2C3, "i2c3", "axi_sel", &peri_cg_regs, 26, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_AUXADC, "auxadc", "axi_sel", &peri_cg_regs, 27, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_SPI0, "spi0", "spi_sel", &peri_cg_regs, 28, &mtk_clk_gate_ops_setclr),
+ GATE_MTK(CLK_PERI_IRTX, "irtx", "irtx_sel", &peri_cg_regs, 29, &mtk_clk_gate_ops_setclr)
+};
+
+static u16 pericfg_rst_bank_ofs[] = { PERI_GLOBALCON_RST0, PERI_GLOBALCON_RST1 };
+
+static u16 pericfg_rst_idx_map[] = {
+ [MT6735_PERI_RST0_UART0] = 0 * RST_NR_PER_BANK + 0,
+ [MT6735_PERI_RST0_UART1] = 0 * RST_NR_PER_BANK + 1,
+ [MT6735_PERI_RST0_UART2] = 0 * RST_NR_PER_BANK + 2,
+ [MT6735_PERI_RST0_UART3] = 0 * RST_NR_PER_BANK + 3,
+ [MT6735_PERI_RST0_UART4] = 0 * RST_NR_PER_BANK + 4,
+ [MT6735_PERI_RST0_BTIF] = 0 * RST_NR_PER_BANK + 6,
+ [MT6735_PERI_RST0_DISP_PWM_PERI] = 0 * RST_NR_PER_BANK + 7,
+ [MT6735_PERI_RST0_PWM] = 0 * RST_NR_PER_BANK + 8,
+ [MT6735_PERI_RST0_AUXADC] = 0 * RST_NR_PER_BANK + 10,
+ [MT6735_PERI_RST0_DMA] = 0 * RST_NR_PER_BANK + 11,
+ [MT6735_PERI_RST0_IRDA] = 0 * RST_NR_PER_BANK + 12,
+ [MT6735_PERI_RST0_IRTX] = 0 * RST_NR_PER_BANK + 13,
+ [MT6735_PERI_RST0_THERM] = 0 * RST_NR_PER_BANK + 16,
+ [MT6735_PERI_RST0_MSDC2] = 0 * RST_NR_PER_BANK + 17,
+ [MT6735_PERI_RST0_MSDC3] = 0 * RST_NR_PER_BANK + 18,
+ [MT6735_PERI_RST0_MSDC0] = 0 * RST_NR_PER_BANK + 19,
+ [MT6735_PERI_RST0_MSDC1] = 0 * RST_NR_PER_BANK + 20,
+ [MT6735_PERI_RST0_I2C0] = 0 * RST_NR_PER_BANK + 22,
+ [MT6735_PERI_RST0_I2C1] = 0 * RST_NR_PER_BANK + 23,
+ [MT6735_PERI_RST0_I2C2] = 0 * RST_NR_PER_BANK + 24,
+ [MT6735_PERI_RST0_I2C3] = 0 * RST_NR_PER_BANK + 25,
+ [MT6735_PERI_RST0_USB] = 0 * RST_NR_PER_BANK + 28,
+
+ [MT6735_PERI_RST1_SPI0] = 1 * RST_NR_PER_BANK + 1,
+};
+
+static const struct mtk_clk_rst_desc pericfg_resets = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = pericfg_rst_bank_ofs,
+ .rst_bank_nr = ARRAY_SIZE(pericfg_rst_bank_ofs),
+ .rst_idx_map = pericfg_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(pericfg_rst_idx_map)
+};
+
+static const struct mtk_clk_desc pericfg_clks = {
+ .clks = pericfg_gates,
+ .num_clks = ARRAY_SIZE(pericfg_gates),
+
+ .rst_desc = &pericfg_resets
+};
+
+static const struct of_device_id of_match_mt6735_pericfg[] = {
+ { .compatible = "mediatek,mt6735-pericfg", .data = &pericfg_clks },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_mt6735_pericfg);
+
+static struct platform_driver clk_mt6735_pericfg = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-pericfg",
+ .of_match_table = of_match_mt6735_pericfg,
+ },
+};
+module_platform_driver(clk_mt6735_pericfg);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("MediaTek MT6735 pericfg clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-topckgen.c b/drivers/clk/mediatek/clk-mt6735-topckgen.c
new file mode 100644
index 000000000000..2589ebfe2271
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-topckgen.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-topckgen.h>
+
+#define CLK_CFG_0 0x40
+#define CLK_CFG_0_SET 0x44
+#define CLK_CFG_0_CLR 0x48
+#define CLK_CFG_1 0x50
+#define CLK_CFG_1_SET 0x54
+#define CLK_CFG_1_CLR 0x58
+#define CLK_CFG_2 0x60
+#define CLK_CFG_2_SET 0x64
+#define CLK_CFG_2_CLR 0x68
+#define CLK_CFG_3 0x70
+#define CLK_CFG_3_SET 0x74
+#define CLK_CFG_3_CLR 0x78
+#define CLK_CFG_4 0x80
+#define CLK_CFG_4_SET 0x84
+#define CLK_CFG_4_CLR 0x88
+#define CLK_CFG_5 0x90
+#define CLK_CFG_5_SET 0x94
+#define CLK_CFG_5_CLR 0x98
+#define CLK_CFG_6 0xa0
+#define CLK_CFG_6_SET 0xa4
+#define CLK_CFG_6_CLR 0xa8
+#define CLK_CFG_7 0xb0
+#define CLK_CFG_7_SET 0xb4
+#define CLK_CFG_7_CLR 0xb8
+
+static DEFINE_SPINLOCK(mt6735_topckgen_lock);
+
+/* Some clocks with unknown details are modeled as fixed clocks */
+static const struct mtk_fixed_clk topckgen_fixed_clks[] = {
+ /*
+ * This clock is available as a parent option for multiple
+ * muxes and seems like an alternative name for clk26m at first,
+ * but it appears alongside it in several muxes which should
+ * mean it is a separate clock.
+ */
+ FIXED_CLK(CLK_TOP_AD_SYS_26M_CK, "ad_sys_26m_ck", "clk26m", 26 * MHZ),
+ /*
+ * This clock is the parent of DMPLL divisors. It might be MEMPLL
+ * or its parent, as DMPLL appears to be an alternative name for
+ * MEMPLL.
+ */
+ FIXED_CLK(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", NULL, 0),
+ /*
+ * DMPLL clock (dmpll_ck), controlled by DDRPHY.
+ */
+ FIXED_CLK(CLK_TOP_DMPLL, "dmpll", "clkph_mck_o", 0),
+ /*
+ * MIPI DPI clock. Parent option for dpi0_sel. Unknown parent.
+ */
+ FIXED_CLK(CLK_TOP_DPI_CK, "dpi_ck", NULL, 0),
+ /*
+ * This clock is a child of WHPLL which is controlled by
+ * the modem.
+ */
+ FIXED_CLK(CLK_TOP_WHPLL_AUDIO_CK, "whpll_audio_ck", NULL, 0)
+};
+
+static const struct mtk_fixed_factor topckgen_factors[] = {
+ FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3),
+ FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
+ FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "mainpll", 1, 8),
+ FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "mainpll", 1, 16),
+ FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll", 1, 26),
+ FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll", 1, 8),
+ FACTOR(CLK_TOP_MSDCPLL_D16, "msdcpll_d16", "msdcpll", 1, 16),
+ FACTOR(CLK_TOP_VENCPLL_D3, "vencpll_d3", "vencpll", 1, 3),
+ FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll", 1, 4),
+ FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2),
+ FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4),
+ FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8),
+ FACTOR(CLK_TOP_AD_SYS_26M_D2, "ad_sys_26m_d2", "clk26m", 1, 2)
+};
+
+static const char * const axi_sel_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll2_d2",
+ "dmpll",
+ "dmpll_d2"
+};
+
+static const char * const mem_sel_parents[] = {
+ "clk26m",
+ "dmpll"
+};
+
+static const char * const ddrphycfg_parents[] = {
+ "clk26m",
+ "syspll1_d8"
+};
+
+static const char * const mm_sel_parents[] = {
+ "clk26m",
+ "vencpll",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll2_d2",
+ "dmpll"
+};
+
+static const char * const pwm_sel_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "univpll3_d2",
+ "univpll1_d4"
+};
+
+static const char * const vdec_sel_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "syspll_d2",
+ "syspll2_d2",
+ "msdcpll_d2"
+};
+
+static const char * const mfg_sel_parents[] = {
+ "clk26m",
+ "mmpll",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "syspll_d3",
+ "syspll1_d2",
+ "syspll_d5",
+ "univpll_d3",
+ "univpll1_d2"
+};
+
+static const char * const camtg_sel_parents[] = {
+ "clk26m",
+ "univpll_d26",
+ "univpll2_d2",
+ "syspll3_d2",
+ "syspll3_d4",
+ "msdcpll_d4"
+};
+
+static const char * const uart_sel_parents[] = {
+ "clk26m",
+ "univpll2_d8"
+};
+
+static const char * const spi_sel_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "msdcpll_d8",
+ "syspll2_d4",
+ "syspll4_d2",
+ "univpll2_d4",
+ "univpll1_d8"
+};
+
+static const char * const usb20_sel_parents[] = {
+ "clk26m",
+ "univpll1_d8",
+ "univpll3_d4"
+};
+
+static const char * const msdc50_0_sel_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll2_d2",
+ "syspll4_d2",
+ "univpll_d5",
+ "univpll1_d4"
+};
+
+static const char * const msdc30_0_sel_parents[] = {
+ "clk26m",
+ "msdcpll",
+ "msdcpll_d2",
+ "msdcpll_d4",
+ "syspll2_d2",
+ "syspll1_d4",
+ "univpll1_d4",
+ "univpll_d3",
+ "univpll_d26",
+ "syspll2_d4",
+ "univpll_d2"
+};
+
+static const char * const msdc30_1_2_sel_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "syspll2_d2",
+ "syspll1_d4",
+ "univpll1_d4",
+ "univpll_d26",
+ "syspll2_d4"
+};
+
+static const char * const msdc30_3_sel_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "syspll2_d2",
+ "syspll1_d4",
+ "univpll1_d4",
+ "univpll_d26",
+ "msdcpll_d16",
+ "syspll2_d4"
+};
+
+static const char * const audio_sel_parents[] = {
+ "clk26m",
+ "syspll3_d4",
+ "syspll4_d4",
+ "syspll1_d16"
+};
+
+static const char * const aud_intbus_sel_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll4_d2",
+ "dmpll_d4"
+};
+
+static const char * const pmicspi_sel_parents[] = {
+ "clk26m",
+ "syspll1_d8",
+ "syspll3_d4",
+ "syspll1_d16",
+ "univpll3_d4",
+ "univpll_d26",
+ "dmpll_d4",
+ "dmpll_d8"
+};
+
+static const char * const scp_sel_parents[] = {
+ "clk26m",
+ "syspll1_d8",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const atb_sel_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll_d5",
+ "dmpll"
+};
+
+static const char * const dpi0_sel_parents[] = {
+ "clk26m",
+ "tvdpll",
+ "tvdpll_d2",
+ "tvdpll_d4",
+ "dpi_ck"
+};
+
+static const char * const scam_sel_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "univpll2_d4",
+ "vencpll_d3"
+};
+
+static const char * const mfg13m_sel_parents[] = {
+ "clk26m",
+ "ad_sys_26m_d2"
+};
+
+static const char * const aud_1_2_sel_parents[] = {
+ "clk26m",
+ "apll1"
+};
+
+static const char * const irda_sel_parents[] = {
+ "clk26m",
+ "univpll2_d4"
+};
+
+static const char * const irtx_sel_parents[] = {
+ "clk26m",
+ "ad_sys_26m_ck"
+};
+
+static const char * const disppwm_sel_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "syspll4_d2_d8",
+ "ad_sys_26m_ck"
+};
+
+static const struct mtk_mux topckgen_muxes[] = {
+ MUX_CLR_SET_UPD(CLK_TOP_AXI_SEL, "axi_sel", axi_sel_parents, CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR, 0, 3, 0, 0),
+ MUX_CLR_SET_UPD(CLK_TOP_MEM_SEL, "mem_sel", mem_sel_parents, CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR, 8, 1, 0, 0),
+ MUX_CLR_SET_UPD(CLK_TOP_DDRPHY_SEL, "ddrphycfg_sel", ddrphycfg_parents, CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR, 16, 1, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MM_SEL, "mm_sel", mm_sel_parents, CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR, 24, 3, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel", pwm_sel_parents, CLK_CFG_1, CLK_CFG_1_SET, CLK_CFG_1_CLR, 0, 2, 7, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_sel_parents, CLK_CFG_1, CLK_CFG_1_SET, CLK_CFG_1_CLR, 8, 3, 15, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_SEL, "mfg_sel", mfg_sel_parents, CLK_CFG_1, CLK_CFG_1_SET, CLK_CFG_1_CLR, 16, 4, 23, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_sel_parents, CLK_CFG_1, CLK_CFG_1_SET, CLK_CFG_1_CLR, 24, 3, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel", uart_sel_parents, CLK_CFG_2, CLK_CFG_2_SET, CLK_CFG_2_CLR, 0, 1, 7, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel", spi_sel_parents, CLK_CFG_2, CLK_CFG_2_SET, CLK_CFG_2_CLR, 8, 3, 15, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB20_SEL, "usb20_sel", usb20_sel_parents, CLK_CFG_2, CLK_CFG_2_SET, CLK_CFG_2_CLR, 16, 2, 23, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_sel_parents, CLK_CFG_2, CLK_CFG_2_SET, CLK_CFG_2_CLR, 24, 3, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_0_sel_parents, CLK_CFG_3, CLK_CFG_3_SET, CLK_CFG_3_CLR, 0, 4, 7, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_2_sel_parents, CLK_CFG_3, CLK_CFG_3_SET, CLK_CFG_3_CLR, 8, 3, 15, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_1_2_sel_parents, CLK_CFG_3, CLK_CFG_3_SET, CLK_CFG_3_CLR, 16, 3, 23, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_sel_parents, CLK_CFG_3, CLK_CFG_3_SET, CLK_CFG_3_CLR, 24, 4, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_SEL, "audio_sel", audio_sel_parents, CLK_CFG_4, CLK_CFG_4_SET, CLK_CFG_4_CLR, 0, 2, 7, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDINTBUS_SEL, "aud_intbus_sel", aud_intbus_sel_parents, CLK_CFG_4, CLK_CFG_4_SET, CLK_CFG_4_CLR, 8, 2, 15, 0, 0),
+ MUX_CLR_SET_UPD(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_sel_parents, CLK_CFG_4, CLK_CFG_4_SET, CLK_CFG_4_CLR, 16, 3, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SCP_SEL, "scp_sel", scp_sel_parents, CLK_CFG_4, CLK_CFG_4_SET, CLK_CFG_4_CLR, 24, 2, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ATB_SEL, "atb_sel", atb_sel_parents, CLK_CFG_5, CLK_CFG_5_SET, CLK_CFG_5_CLR, 0, 2, 7, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_sel_parents, CLK_CFG_5, CLK_CFG_5_SET, CLK_CFG_5_CLR, 8, 3, 15, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SCAM_SEL, "scam_sel", scam_sel_parents, CLK_CFG_5, CLK_CFG_5_SET, CLK_CFG_5_CLR, 16, 2, 23, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG13M_SEL, "mfg13m_sel", mfg13m_sel_parents, CLK_CFG_5, CLK_CFG_5_SET, CLK_CFG_5_CLR, 24, 1, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD1_SEL, "aud_1_sel", aud_1_2_sel_parents, CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR, 0, 1, 7, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD2_SEL, "aud_2_sel", aud_1_2_sel_parents, CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR, 8, 1, 15, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_IRDA_SEL, "irda_sel", irda_sel_parents, CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR, 16, 1, 23, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_IRTX_SEL, "irtx_sel", irtx_sel_parents, CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR, 24, 1, 31, 0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISPPWM_SEL, "disppwm_sel", disppwm_sel_parents, CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR, 0, 2, 7, 0, 0),
+};
+
+static const struct mtk_clk_desc topckgen_desc = {
+ .fixed_clks = topckgen_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(topckgen_fixed_clks),
+ .factor_clks = topckgen_factors,
+ .num_factor_clks = ARRAY_SIZE(topckgen_factors),
+ .mux_clks = topckgen_muxes,
+ .num_mux_clks = ARRAY_SIZE(topckgen_muxes),
+ .clk_lock = &mt6735_topckgen_lock,
+};
+
+static const struct of_device_id of_match_mt6735_topckgen[] = {
+ { .compatible = "mediatek,mt6735-topckgen", .data = &topckgen_desc},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_mt6735_topckgen);
+
+static struct platform_driver clk_mt6735_topckgen = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-topckgen",
+ .of_match_table = of_match_mt6735_topckgen,
+ },
+};
+module_platform_driver(clk_mt6735_topckgen);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("MediaTek MT6735 topckgen clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-vdecsys.c b/drivers/clk/mediatek/clk-mt6735-vdecsys.c
new file mode 100644
index 000000000000..8817085fc1db
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-vdecsys.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-vdecsys.h>
+#include <dt-bindings/reset/mediatek,mt6735-vdecsys.h>
+
+#define VDEC_CKEN_SET 0x00
+#define VDEC_CKEN_CLR 0x04
+#define SMI_LARB1_CKEN_SET 0x08
+#define SMI_LARB1_CKEN_CLR 0x0c
+#define VDEC_RESETB_CON 0x10
+#define SMI_LARB1_RESETB_CON 0x14
+
+#define RST_NR_PER_BANK 32
+
+static struct mtk_gate_regs vdec_cg_regs = {
+ .set_ofs = VDEC_CKEN_SET,
+ .clr_ofs = VDEC_CKEN_CLR,
+ .sta_ofs = VDEC_CKEN_SET,
+};
+
+static struct mtk_gate_regs smi_larb1_cg_regs = {
+ .set_ofs = SMI_LARB1_CKEN_SET,
+ .clr_ofs = SMI_LARB1_CKEN_CLR,
+ .sta_ofs = SMI_LARB1_CKEN_SET,
+};
+
+static const struct mtk_gate vdecsys_gates[] = {
+ GATE_MTK(CLK_VDEC_VDEC, "vdec", "vdec_sel", &vdec_cg_regs, 0, &mtk_clk_gate_ops_setclr_inv),
+ GATE_MTK(CLK_VDEC_SMI_LARB1, "smi_larb1", "vdec_sel", &smi_larb1_cg_regs, 0, &mtk_clk_gate_ops_setclr_inv),
+};
+
+static u16 vdecsys_rst_bank_ofs[] = { VDEC_RESETB_CON, SMI_LARB1_RESETB_CON };
+
+static u16 vdecsys_rst_idx_map[] = {
+ [MT6735_VDEC_RST0_VDEC] = 0 * RST_NR_PER_BANK + 0,
+ [MT6735_VDEC_RST1_SMI_LARB1] = 1 * RST_NR_PER_BANK + 0,
+};
+
+static const struct mtk_clk_rst_desc vdecsys_resets = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = vdecsys_rst_bank_ofs,
+ .rst_bank_nr = ARRAY_SIZE(vdecsys_rst_bank_ofs),
+ .rst_idx_map = vdecsys_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(vdecsys_rst_idx_map)
+};
+
+static const struct mtk_clk_desc vdecsys_clks = {
+ .clks = vdecsys_gates,
+ .num_clks = ARRAY_SIZE(vdecsys_gates),
+ .rst_desc = &vdecsys_resets
+};
+
+static const struct of_device_id of_match_mt6735_vdecsys[] = {
+ { .compatible = "mediatek,mt6735-vdecsys", .data = &vdecsys_clks },
+ { /* sentinel */ }
+};
+
+static struct platform_driver clk_mt6735_vdecsys = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-vdecsys",
+ .of_match_table = of_match_mt6735_vdecsys,
+ },
+};
+module_platform_driver(clk_mt6735_vdecsys);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("MediaTek MT6735 vdecsys clock and reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6735-vencsys.c b/drivers/clk/mediatek/clk-mt6735-vencsys.c
new file mode 100644
index 000000000000..8dec7f98492a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6735-vencsys.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#include <dt-bindings/clock/mediatek,mt6735-vencsys.h>
+
+#define VENC_CG_CON 0x00
+#define VENC_CG_SET 0x04
+#define VENC_CG_CLR 0x08
+
+static struct mtk_gate_regs venc_cg_regs = {
+ .set_ofs = VENC_CG_SET,
+ .clr_ofs = VENC_CG_CLR,
+ .sta_ofs = VENC_CG_CON,
+};
+
+static const struct mtk_gate vencsys_gates[] = {
+ GATE_MTK(CLK_VENC_SMI_LARB3, "smi_larb3", "mm_sel", &venc_cg_regs, 0, &mtk_clk_gate_ops_setclr_inv),
+ GATE_MTK(CLK_VENC_VENC, "venc", "mm_sel", &venc_cg_regs, 4, &mtk_clk_gate_ops_setclr_inv),
+ GATE_MTK(CLK_VENC_JPGENC, "jpgenc", "mm_sel", &venc_cg_regs, 8, &mtk_clk_gate_ops_setclr_inv),
+ GATE_MTK(CLK_VENC_JPGDEC, "jpgdec", "mm_sel", &venc_cg_regs, 12, &mtk_clk_gate_ops_setclr_inv),
+};
+
+static const struct mtk_clk_desc vencsys_clks = {
+ .clks = vencsys_gates,
+ .num_clks = ARRAY_SIZE(vencsys_gates),
+};
+
+static const struct of_device_id of_match_mt6735_vencsys[] = {
+ { .compatible = "mediatek,mt6735-vencsys", .data = &vencsys_clks },
+ { /* sentinel */ }
+};
+
+static struct platform_driver clk_mt6735_vencsys = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6735-vencsys",
+ .of_match_table = of_match_mt6735_vencsys,
+ },
+};
+module_platform_driver(clk_mt6735_vencsys);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Mediatek MT6735 vencsys clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765-audio.c b/drivers/clk/mediatek/clk-mt6765-audio.c
index 9c6e9caad597..2be1458087e6 100644
--- a/drivers/clk/mediatek/clk-mt6765-audio.c
+++ b/drivers/clk/mediatek/clk-mt6765-audio.c
@@ -24,23 +24,11 @@ static const struct mtk_gate_regs audio1_cg_regs = {
.sta_ofs = 0x4,
};
-#define GATE_AUDIO0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate audio_clks[] = {
/* AUDIO0 */
@@ -64,37 +52,30 @@ static const struct mtk_gate audio_clks[] = {
"audio_ck", 7),
};
-static int clk_mt6765_audio_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_AUDIO_NR_CLK);
-
- mtk_clk_register_gates(node, audio_clks,
- ARRAY_SIZE(audio_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc audio_desc = {
+ .clks = audio_clks,
+ .num_clks = ARRAY_SIZE(audio_clks),
+};
static const struct of_device_id of_match_clk_mt6765_audio[] = {
- { .compatible = "mediatek,mt6765-audsys", },
- {}
+ {
+ .compatible = "mediatek,mt6765-audsys",
+ .data = &audio_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765_audio);
static struct platform_driver clk_mt6765_audio_drv = {
- .probe = clk_mt6765_audio_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6765-audio",
.of_match_table = of_match_clk_mt6765_audio,
},
};
+module_platform_driver(clk_mt6765_audio_drv);
-builtin_platform_driver(clk_mt6765_audio_drv);
+MODULE_DESCRIPTION("MediaTek MT6765 audio clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765-cam.c b/drivers/clk/mediatek/clk-mt6765-cam.c
index 2586d3ac4cd4..2a7f30dc85bb 100644
--- a/drivers/clk/mediatek/clk-mt6765-cam.c
+++ b/drivers/clk/mediatek/clk-mt6765-cam.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs cam_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_CAM(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &cam_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_CAM(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &cam_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate cam_clks[] = {
GATE_CAM(CLK_CAM_LARB3, "cam_larb3", "mm_ck", 0),
@@ -39,36 +33,30 @@ static const struct mtk_gate cam_clks[] = {
GATE_CAM(CLK_CAM_CCU, "cam_ccu", "mm_ck", 12),
};
-static int clk_mt6765_cam_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_CAM_NR_CLK);
-
- mtk_clk_register_gates(node, cam_clks, ARRAY_SIZE(cam_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc cam_desc = {
+ .clks = cam_clks,
+ .num_clks = ARRAY_SIZE(cam_clks),
+};
static const struct of_device_id of_match_clk_mt6765_cam[] = {
- { .compatible = "mediatek,mt6765-camsys", },
- {}
+ {
+ .compatible = "mediatek,mt6765-camsys",
+ .data = &cam_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765_cam);
static struct platform_driver clk_mt6765_cam_drv = {
- .probe = clk_mt6765_cam_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6765-cam",
.of_match_table = of_match_clk_mt6765_cam,
},
};
+module_platform_driver(clk_mt6765_cam_drv);
-builtin_platform_driver(clk_mt6765_cam_drv);
+MODULE_DESCRIPTION("MediaTek MT6765 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765-img.c b/drivers/clk/mediatek/clk-mt6765-img.c
index 8cc95b98921e..ff857852cfb0 100644
--- a/drivers/clk/mediatek/clk-mt6765-img.c
+++ b/drivers/clk/mediatek/clk-mt6765-img.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs img_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_IMG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &img_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IMG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_LARB2, "img_larb2", "mm_ck", 0),
@@ -35,36 +29,30 @@ static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_RSC, "img_rsc", "mm_ck", 5),
};
-static int clk_mt6765_img_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
+};
static const struct of_device_id of_match_clk_mt6765_img[] = {
- { .compatible = "mediatek,mt6765-imgsys", },
- {}
+ {
+ .compatible = "mediatek,mt6765-imgsys",
+ .data = &img_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765_img);
static struct platform_driver clk_mt6765_img_drv = {
- .probe = clk_mt6765_img_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6765-img",
.of_match_table = of_match_clk_mt6765_img,
},
};
+module_platform_driver(clk_mt6765_img_drv);
-builtin_platform_driver(clk_mt6765_img_drv);
+MODULE_DESCRIPTION("MediaTek MT6765 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765-mipi0a.c b/drivers/clk/mediatek/clk-mt6765-mipi0a.c
index c816e26a95f9..8261dfd12a9a 100644
--- a/drivers/clk/mediatek/clk-mt6765-mipi0a.c
+++ b/drivers/clk/mediatek/clk-mt6765-mipi0a.c
@@ -18,51 +18,38 @@ static const struct mtk_gate_regs mipi0a_cg_regs = {
.sta_ofs = 0x80,
};
-#define GATE_MIPI0A(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mipi0a_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_MIPI0A(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mipi0a_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate mipi0a_clks[] = {
GATE_MIPI0A(CLK_MIPI0A_CSR_CSI_EN_0A,
"mipi0a_csr_0a", "f_fseninf_ck", 1),
};
-static int clk_mt6765_mipi0a_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_MIPI0A_NR_CLK);
-
- mtk_clk_register_gates(node, mipi0a_clks,
- ARRAY_SIZE(mipi0a_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc mipi0a_desc = {
+ .clks = mipi0a_clks,
+ .num_clks = ARRAY_SIZE(mipi0a_clks),
+};
static const struct of_device_id of_match_clk_mt6765_mipi0a[] = {
- { .compatible = "mediatek,mt6765-mipi0a", },
- {}
+ {
+ .compatible = "mediatek,mt6765-mipi0a",
+ .data = &mipi0a_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765_mipi0a);
static struct platform_driver clk_mt6765_mipi0a_drv = {
- .probe = clk_mt6765_mipi0a_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6765-mipi0a",
.of_match_table = of_match_clk_mt6765_mipi0a,
},
};
+module_platform_driver(clk_mt6765_mipi0a_drv);
-builtin_platform_driver(clk_mt6765_mipi0a_drv);
+MODULE_DESCRIPTION("MediaTek MT6765 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765-mm.c b/drivers/clk/mediatek/clk-mt6765-mm.c
index ee6d3b859a6c..e525919f9e81 100644
--- a/drivers/clk/mediatek/clk-mt6765-mm.c
+++ b/drivers/clk/mediatek/clk-mt6765-mm.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs mm_cg_regs = {
.sta_ofs = 0x100,
};
-#define GATE_MM(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mm_clks[] = {
/* MM */
@@ -61,36 +55,30 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM(CLK_MM_F26M_HRTWT, "mm_hrtwt", "f_f26m_ck", 29),
};
-static int clk_mt6765_mm_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
-
- mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
static const struct of_device_id of_match_clk_mt6765_mm[] = {
- { .compatible = "mediatek,mt6765-mmsys", },
- {}
+ {
+ .compatible = "mediatek,mt6765-mmsys",
+ .data = &mm_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765_mm);
static struct platform_driver clk_mt6765_mm_drv = {
- .probe = clk_mt6765_mm_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6765-mm",
.of_match_table = of_match_clk_mt6765_mm,
},
};
+module_platform_driver(clk_mt6765_mm_drv);
-builtin_platform_driver(clk_mt6765_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT6765 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765-vcodec.c b/drivers/clk/mediatek/clk-mt6765-vcodec.c
index d8045979d48a..f309d1090cda 100644
--- a/drivers/clk/mediatek/clk-mt6765-vcodec.c
+++ b/drivers/clk/mediatek/clk-mt6765-vcodec.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs venc_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_VENC(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &venc_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VENC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate venc_clks[] = {
GATE_VENC(CLK_VENC_SET0_LARB, "venc_set0_larb", "mm_ck", 0),
@@ -34,37 +28,30 @@ static const struct mtk_gate venc_clks[] = {
GATE_VENC(CLK_VENC_SET3_VDEC, "venc_set3_vdec", "mm_ck", 12),
};
-static int clk_mt6765_vcodec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK);
-
- mtk_clk_register_gates(node, venc_clks,
- ARRAY_SIZE(venc_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- return r;
-}
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
+};
static const struct of_device_id of_match_clk_mt6765_vcodec[] = {
- { .compatible = "mediatek,mt6765-vcodecsys", },
- {}
+ {
+ .compatible = "mediatek,mt6765-vcodecsys",
+ .data = &venc_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765_vcodec);
static struct platform_driver clk_mt6765_vcodec_drv = {
- .probe = clk_mt6765_vcodec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6765-vcodec",
.of_match_table = of_match_clk_mt6765_vcodec,
},
};
+module_platform_driver(clk_mt6765_vcodec_drv);
-builtin_platform_driver(clk_mt6765_vcodec_drv);
+MODULE_DESCRIPTION("MediaTek MT6765 Video Codec clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6765.c b/drivers/clk/mediatek/clk-mt6765.c
index e9b9e6729733..d53731e7933f 100644
--- a/drivers/clk/mediatek/clk-mt6765.c
+++ b/drivers/clk/mediatek/clk-mt6765.c
@@ -9,7 +9,7 @@
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/mfd/syscon.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-gate.h"
@@ -367,10 +367,12 @@ static const struct mtk_mux top_muxes[] = {
/* CLK_CFG_0 */
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR,
- 0, 2, 7, CLK_CFG_UPDATE, 0, CLK_IS_CRITICAL),
+ 0, 2, 7, CLK_CFG_UPDATE, 0,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents,
CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR,
- 8, 2, 15, CLK_CFG_UPDATE, 1, CLK_IS_CRITICAL),
+ 8, 2, 15, CLK_CFG_UPDATE, 1,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MM_SEL, "mm_sel", mm_parents, CLK_CFG_0,
CLK_CFG_0_SET, CLK_CFG_0_CLR, 16, 3, 23,
CLK_CFG_UPDATE, 2),
@@ -404,15 +406,15 @@ static const struct mtk_mux top_muxes[] = {
CLK_CFG_2_SET, CLK_CFG_2_CLR, 24, 2, 31,
CLK_CFG_UPDATE, 11),
/* CLK_CFG_3 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc5hclk",
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc5hclk",
msdc5hclk_parents, CLK_CFG_3, CLK_CFG_3_SET,
- CLK_CFG_3_CLR, 0, 2, 7, CLK_CFG_UPDATE, 12),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
+ CLK_CFG_3_CLR, 0, 2, 7, CLK_CFG_UPDATE, 12, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
msdc50_0_parents, CLK_CFG_3, CLK_CFG_3_SET,
- CLK_CFG_3_CLR, 8, 3, 15, CLK_CFG_UPDATE, 13),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
+ CLK_CFG_3_CLR, 8, 3, 15, CLK_CFG_UPDATE, 13, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
msdc30_1_parents, CLK_CFG_3, CLK_CFG_3_SET,
- CLK_CFG_3_CLR, 16, 3, 23, CLK_CFG_UPDATE, 14),
+ CLK_CFG_3_CLR, 16, 3, 23, CLK_CFG_UPDATE, 14, 0),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents,
CLK_CFG_3, CLK_CFG_3_SET, CLK_CFG_3_CLR,
24, 2, 31, CLK_CFG_UPDATE, 15),
@@ -459,7 +461,7 @@ static const struct mtk_mux top_muxes[] = {
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_PWRAP_ULPOSC_SEL, "ulposc_sel",
ulposc_parents, CLK_CFG_7, CLK_CFG_7_SET,
CLK_CFG_7_CLR, 0, 3, 7, CLK_CFG_UPDATE, 28,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTM_SEL, "camtm_sel", camtm_parents,
CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR, 8, 2, 15,
CLK_CFG_UPDATE, 29),
@@ -483,32 +485,14 @@ static const struct mtk_gate_regs top2_cg_regs = {
.sta_ofs = 0x320,
};
-#define GATE_TOP0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_TOP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_TOP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
-#define GATE_TOP2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_TOP2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top2_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate top_clks[] = {
/* TOP0 */
@@ -559,41 +543,17 @@ static const struct mtk_gate_regs ifr5_cg_regs = {
.sta_ofs = 0xc8,
};
-#define GATE_IFR2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &ifr2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IFR2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifr2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_IFR3(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &ifr3_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IFR3(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifr3_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_IFR4(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &ifr4_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IFR4(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifr4_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_IFR5(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &ifr5_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IFR5(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ifr5_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate ifr_clks[] = {
/* INFRA_TOPAXI */
@@ -674,14 +634,8 @@ static const struct mtk_gate_regs apmixed_cg_regs = {
.sta_ofs = 0x14,
};
-#define GATE_APMIXED(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &apmixed_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_APMIXED(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &apmixed_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate apmixed_clks[] = {
/* AUDIO0 */
@@ -777,19 +731,18 @@ static int clk_mt6765_apmixed_probe(struct platform_device *pdev)
int r;
struct device_node *node = pdev->dev.of_node;
void __iomem *base;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base)) {
- pr_err("%s(): ioremap failed\n", __func__);
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
return PTR_ERR(base);
- }
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
- mtk_clk_register_gates(node, apmixed_clks,
+ mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
ARRAY_SIZE(apmixed_clks), clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
@@ -812,24 +765,24 @@ static int clk_mt6765_top_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
void __iomem *base;
struct clk_hw_onecell_data *clk_data;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base)) {
- pr_err("%s(): ioremap failed\n", __func__);
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
return PTR_ERR(base);
- }
clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_fixed_clks(fixed_clks, ARRAY_SIZE(fixed_clks),
clk_data);
mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs),
clk_data);
- mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node,
+ mtk_clk_register_muxes(&pdev->dev, top_muxes,
+ ARRAY_SIZE(top_muxes), node,
&mt6765_clk_lock, clk_data);
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, top_clks,
+ ARRAY_SIZE(top_clks), clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
@@ -852,18 +805,17 @@ static int clk_mt6765_ifr_probe(struct platform_device *pdev)
int r;
struct device_node *node = pdev->dev.of_node;
void __iomem *base;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base)) {
- pr_err("%s(): ioremap failed\n", __func__);
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
return PTR_ERR(base);
- }
clk_data = mtk_alloc_clk_data(CLK_IFR_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
- mtk_clk_register_gates(node, ifr_clks, ARRAY_SIZE(ifr_clks),
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, ifr_clks,
+ ARRAY_SIZE(ifr_clks), clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
if (r)
@@ -887,6 +839,7 @@ static const struct of_device_id of_match_clk_mt6765[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6765);
static int clk_mt6765_probe(struct platform_device *pdev)
{
@@ -920,3 +873,6 @@ static int __init clk_mt6765_init(void)
}
arch_initcall(clk_mt6765_init);
+
+MODULE_DESCRIPTION("MediaTek MT6765 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-aud.c b/drivers/clk/mediatek/clk-mt6779-aud.c
index 97e44abb7e87..8ed318bd7765 100644
--- a/drivers/clk/mediatek/clk-mt6779-aud.c
+++ b/drivers/clk/mediatek/clk-mt6779-aud.c
@@ -6,9 +6,7 @@
#include <linux/module.h>
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -89,31 +87,30 @@ static const struct mtk_gate audio_clks[] = {
"audio_h_sel", 31),
};
-static const struct of_device_id of_match_clk_mt6779_aud[] = {
- { .compatible = "mediatek,mt6779-audio", },
- {}
+static const struct mtk_clk_desc audio_desc = {
+ .clks = audio_clks,
+ .num_clks = ARRAY_SIZE(audio_clks),
};
-static int clk_mt6779_aud_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_AUD_NR_CLK);
-
- mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct of_device_id of_match_clk_mt6779_aud[] = {
+ {
+ .compatible = "mediatek,mt6779-audio",
+ .data = &audio_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_aud);
static struct platform_driver clk_mt6779_aud_drv = {
- .probe = clk_mt6779_aud_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-aud",
.of_match_table = of_match_clk_mt6779_aud,
},
};
-
module_platform_driver(clk_mt6779_aud_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 audio clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-cam.c b/drivers/clk/mediatek/clk-mt6779-cam.c
index 9c5117aae146..f397b55606de 100644
--- a/drivers/clk/mediatek/clk-mt6779-cam.c
+++ b/drivers/clk/mediatek/clk-mt6779-cam.c
@@ -38,31 +38,30 @@ static const struct mtk_gate cam_clks[] = {
GATE_CAM(CLK_CAM_FAKE_ENG, "camsys_fake_eng", "cam_sel", 14),
};
-static const struct of_device_id of_match_clk_mt6779_cam[] = {
- { .compatible = "mediatek,mt6779-camsys", },
- {}
+static const struct mtk_clk_desc cam_desc = {
+ .clks = cam_clks,
+ .num_clks = ARRAY_SIZE(cam_clks),
};
-static int clk_mt6779_cam_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_CAM_NR_CLK);
-
- mtk_clk_register_gates(node, cam_clks, ARRAY_SIZE(cam_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct of_device_id of_match_clk_mt6779_cam[] = {
+ {
+ .compatible = "mediatek,mt6779-camsys",
+ .data = &cam_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_cam);
static struct platform_driver clk_mt6779_cam_drv = {
- .probe = clk_mt6779_cam_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-cam",
.of_match_table = of_match_clk_mt6779_cam,
},
};
-
module_platform_driver(clk_mt6779_cam_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 Camera clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-img.c b/drivers/clk/mediatek/clk-mt6779-img.c
index 801271477d46..474a59a4ca9e 100644
--- a/drivers/clk/mediatek/clk-mt6779-img.c
+++ b/drivers/clk/mediatek/clk-mt6779-img.c
@@ -30,26 +30,24 @@ static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_WPE_A, "imgsys_wpe_a", "img_sel", 7),
};
-static const struct of_device_id of_match_clk_mt6779_img[] = {
- { .compatible = "mediatek,mt6779-imgsys", },
- {}
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
};
-static int clk_mt6779_img_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct of_device_id of_match_clk_mt6779_img[] = {
+ {
+ .compatible = "mediatek,mt6779-imgsys",
+ .data = &img_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_img);
static struct platform_driver clk_mt6779_img_drv = {
- .probe = clk_mt6779_img_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-img",
.of_match_table = of_match_clk_mt6779_img,
@@ -57,4 +55,6 @@ static struct platform_driver clk_mt6779_img_drv = {
};
module_platform_driver(clk_mt6779_img_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 imgsys clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-ipe.c b/drivers/clk/mediatek/clk-mt6779-ipe.c
index f67814ca7dfb..c2314654f43a 100644
--- a/drivers/clk/mediatek/clk-mt6779-ipe.c
+++ b/drivers/clk/mediatek/clk-mt6779-ipe.c
@@ -32,26 +32,24 @@ static const struct mtk_gate ipe_clks[] = {
GATE_IPE(CLK_IPE_DPE, "ipe_dpe", "ipe_sel", 6),
};
-static const struct of_device_id of_match_clk_mt6779_ipe[] = {
- { .compatible = "mediatek,mt6779-ipesys", },
- {}
+static const struct mtk_clk_desc ipe_desc = {
+ .clks = ipe_clks,
+ .num_clks = ARRAY_SIZE(ipe_clks),
};
-static int clk_mt6779_ipe_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IPE_NR_CLK);
-
- mtk_clk_register_gates(node, ipe_clks, ARRAY_SIZE(ipe_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct of_device_id of_match_clk_mt6779_ipe[] = {
+ {
+ .compatible = "mediatek,mt6779-ipesys",
+ .data = &ipe_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_ipe);
static struct platform_driver clk_mt6779_ipe_drv = {
- .probe = clk_mt6779_ipe_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-ipe",
.of_match_table = of_match_clk_mt6779_ipe,
@@ -59,4 +57,6 @@ static struct platform_driver clk_mt6779_ipe_drv = {
};
module_platform_driver(clk_mt6779_ipe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 Image Processing Engine clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-mfg.c b/drivers/clk/mediatek/clk-mt6779-mfg.c
index fc7387b59758..21793cb6e6e3 100644
--- a/drivers/clk/mediatek/clk-mt6779-mfg.c
+++ b/drivers/clk/mediatek/clk-mt6779-mfg.c
@@ -27,26 +27,24 @@ static const struct mtk_gate mfg_clks[] = {
GATE_MFG(CLK_MFGCFG_BG3D, "mfg_bg3d", "mfg_sel", 0),
};
-static int clk_mt6779_mfg_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_MFGCFG_NR_CLK);
-
- mtk_clk_register_gates(node, mfg_clks, ARRAY_SIZE(mfg_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc mfg_desc = {
+ .clks = mfg_clks,
+ .num_clks = ARRAY_SIZE(mfg_clks),
+};
static const struct of_device_id of_match_clk_mt6779_mfg[] = {
- { .compatible = "mediatek,mt6779-mfgcfg", },
- {}
+ {
+ .compatible = "mediatek,mt6779-mfgcfg",
+ .data = &mfg_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_mfg);
static struct platform_driver clk_mt6779_mfg_drv = {
- .probe = clk_mt6779_mfg_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-mfg",
.of_match_table = of_match_clk_mt6779_mfg,
@@ -54,4 +52,6 @@ static struct platform_driver clk_mt6779_mfg_drv = {
};
module_platform_driver(clk_mt6779_mfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 GPU mfg clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-mm.c b/drivers/clk/mediatek/clk-mt6779-mm.c
index eda8cbee3d23..30bbab308388 100644
--- a/drivers/clk/mediatek/clk-mt6779-mm.c
+++ b/drivers/clk/mediatek/clk-mt6779-mm.c
@@ -85,26 +85,27 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM1(CLK_MM_DISP_OVL_FBDC, "mm_disp_ovl_fbdc", "mm_sel", 16),
};
-static int clk_mt6779_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
-
- mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
- clk_data);
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct platform_device_id clk_mt6779_mm_id_table[] = {
+ { .name = "clk-mt6779-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt6779_mm_id_table);
static struct platform_driver clk_mt6779_mm_drv = {
- .probe = clk_mt6779_mm_probe,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt6779-mm",
},
+ .id_table = clk_mt6779_mm_id_table,
};
module_platform_driver(clk_mt6779_mm_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 MultiMedia mdp/ddp clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-vdec.c b/drivers/clk/mediatek/clk-mt6779-vdec.c
index 7e195b082e86..458d012f023c 100644
--- a/drivers/clk/mediatek/clk-mt6779-vdec.c
+++ b/drivers/clk/mediatek/clk-mt6779-vdec.c
@@ -39,26 +39,24 @@ static const struct mtk_gate vdec_clks[] = {
GATE_VDEC1_I(CLK_VDEC_LARB1, "vdec_larb1_cken", "vdec_sel", 0),
};
-static const struct of_device_id of_match_clk_mt6779_vdec[] = {
- { .compatible = "mediatek,mt6779-vdecsys", },
- {}
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
};
-static int clk_mt6779_vdec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_GCON_NR_CLK);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct of_device_id of_match_clk_mt6779_vdec[] = {
+ {
+ .compatible = "mediatek,mt6779-vdecsys",
+ .data = &vdec_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_vdec);
static struct platform_driver clk_mt6779_vdec_drv = {
- .probe = clk_mt6779_vdec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-vdec",
.of_match_table = of_match_clk_mt6779_vdec,
@@ -66,4 +64,6 @@ static struct platform_driver clk_mt6779_vdec_drv = {
};
module_platform_driver(clk_mt6779_vdec_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 Video Decoders clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779-venc.c b/drivers/clk/mediatek/clk-mt6779-venc.c
index 573efa87c9bd..70cebc274031 100644
--- a/drivers/clk/mediatek/clk-mt6779-venc.c
+++ b/drivers/clk/mediatek/clk-mt6779-venc.c
@@ -30,26 +30,24 @@ static const struct mtk_gate venc_clks[] = {
GATE_VENC_I(CLK_VENC_GCON_GALS, "venc_gals", "venc_sel", 28),
};
-static const struct of_device_id of_match_clk_mt6779_venc[] = {
- { .compatible = "mediatek,mt6779-vencsys", },
- {}
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
};
-static int clk_mt6779_venc_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VENC_GCON_NR_CLK);
-
- mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct of_device_id of_match_clk_mt6779_venc[] = {
+ {
+ .compatible = "mediatek,mt6779-vencsys",
+ .data = &venc_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779_venc);
static struct platform_driver clk_mt6779_venc_drv = {
- .probe = clk_mt6779_venc_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6779-venc",
.of_match_table = of_match_clk_mt6779_venc,
@@ -57,4 +55,6 @@ static struct platform_driver clk_mt6779_venc_drv = {
};
module_platform_driver(clk_mt6779_venc_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6779 Video Encoders clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6779.c b/drivers/clk/mediatek/clk-mt6779.c
index 0d0a90ee5eb2..86732f5acf93 100644
--- a/drivers/clk/mediatek/clk-mt6779.c
+++ b/drivers/clk/mediatek/clk-mt6779.c
@@ -6,8 +6,6 @@
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "clk-gate.h"
@@ -640,7 +638,7 @@ static const struct mtk_mux top_muxes[] = {
/* CLK_CFG_0 */
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI, "axi_sel", axi_parents,
0x20, 0x24, 0x28, 0, 2, 7,
- 0x004, 0, CLK_IS_CRITICAL),
+ 0x004, 0, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MM, "mm_sel", mm_parents,
0x20, 0x24, 0x28, 8, 3, 15, 0x004, 1),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SCP, "scp_sel", scp_parents,
@@ -687,16 +685,16 @@ static const struct mtk_mux top_muxes[] = {
0x70, 0x74, 0x78, 0, 1, 7, 0x004, 20),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI, "spi_sel", spi_parents,
0x70, 0x74, 0x78, 8, 2, 15, 0x004, 21),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_HCLK, "msdc50_hclk_sel",
- msdc50_hclk_parents, 0x70, 0x74, 0x78,
- 16, 2, 23, 0x004, 22),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0, "msdc50_0_sel",
- msdc50_0_parents, 0x70, 0x74, 0x78,
- 24, 3, 31, 0x004, 23),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_HCLK, "msdc50_hclk_sel",
+ msdc50_hclk_parents, 0x70, 0x74, 0x78,
+ 16, 2, 23, 0x004, 22, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0, "msdc50_0_sel",
+ msdc50_0_parents, 0x70, 0x74, 0x78,
+ 24, 3, 31, 0x004, 23, 0),
/* CLK_CFG_6 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1, "msdc30_1_sel",
- msdc30_1_parents, 0x80, 0x84, 0x88,
- 0, 3, 7, 0x004, 24),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1, "msdc30_1_sel",
+ msdc30_1_parents, 0x80, 0x84, 0x88,
+ 0, 3, 7, 0x004, 24, 0),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD, "audio_sel", audio_parents,
0x80, 0x84, 0x88, 8, 2, 15, 0x004, 25),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_INTBUS, "aud_intbus_sel",
@@ -710,7 +708,7 @@ static const struct mtk_mux top_muxes[] = {
0x90, 0x94, 0x98, 0, 2, 7, 0x004, 28),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SSPM, "sspm_sel", sspm_parents,
0x90, 0x94, 0x98, 8, 3, 15,
- 0x004, 29, CLK_IS_CRITICAL),
+ 0x004, 29, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_DPI0, "dpi0_sel", dpi0_parents,
0x90, 0x94, 0x98, 16, 3, 23, 0x004, 30),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SCAM, "scam_sel", scam_parents,
@@ -727,7 +725,7 @@ static const struct mtk_mux top_muxes[] = {
16, 2, 23, 0x008, 3),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SPM, "spm_sel", spm_parents,
0xa0, 0xa4, 0xa8, 24, 2, 31,
- 0x008, 4, CLK_IS_CRITICAL),
+ 0x008, 4, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/* CLK_CFG_9 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C, "i2c_sel", i2c_parents,
0xb0, 0xb4, 0xb8, 0, 2, 7, 0x008, 5),
@@ -880,6 +878,7 @@ static const struct mtk_gate_regs infra3_cg_regs = {
&mtk_clk_gate_ops_setclr)
static const struct mtk_gate infra_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "ifa_dummy"),
/* INFRA0 */
GATE_INFRA0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr",
"axi_sel", 0),
@@ -1218,10 +1217,12 @@ static int clk_mt6779_apmixed_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
- mtk_clk_register_gates(node, apmixed_clks,
+ mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
ARRAY_SIZE(apmixed_clks), clk_data);
return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
@@ -1238,33 +1239,25 @@ static int clk_mt6779_top_probe(struct platform_device *pdev)
return PTR_ERR(base);
clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
clk_data);
mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes),
- node, &mt6779_clk_lock, clk_data);
+ mtk_clk_register_muxes(&pdev->dev, top_muxes,
+ ARRAY_SIZE(top_muxes), node,
+ &mt6779_clk_lock, clk_data);
- mtk_clk_register_composites(top_aud_muxes, ARRAY_SIZE(top_aud_muxes),
- base, &mt6779_clk_lock, clk_data);
+ mtk_clk_register_composites(&pdev->dev, top_aud_muxes,
+ ARRAY_SIZE(top_aud_muxes), base,
+ &mt6779_clk_lock, clk_data);
- mtk_clk_register_composites(top_aud_divs, ARRAY_SIZE(top_aud_divs),
- base, &mt6779_clk_lock, clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
-
-static int clk_mt6779_infra_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
-
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
+ mtk_clk_register_composites(&pdev->dev, top_aud_divs,
+ ARRAY_SIZE(top_aud_divs), base,
+ &mt6779_clk_lock, clk_data);
return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
}
@@ -1277,9 +1270,6 @@ static const struct of_device_id of_match_clk_mt6779[] = {
.compatible = "mediatek,mt6779-topckgen",
.data = clk_mt6779_top_probe,
}, {
- .compatible = "mediatek,mt6779-infracfg_ao",
- .data = clk_mt6779_infra_probe,
- }, {
/* sentinel */
}
};
@@ -1302,6 +1292,26 @@ static int clk_mt6779_probe(struct platform_device *pdev)
return r;
}
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+};
+
+static const struct of_device_id of_match_clk_mt6779_infra[] = {
+ { .compatible = "mediatek,mt6779-infracfg_ao", .data = &infra_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6779);
+
+static struct platform_driver clk_mt6779_infra_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6779-infra",
+ .of_match_table = of_match_clk_mt6779_infra,
+ },
+};
+
static struct platform_driver clk_mt6779_drv = {
.probe = clk_mt6779_probe,
.driver = {
@@ -1312,8 +1322,14 @@ static struct platform_driver clk_mt6779_drv = {
static int __init clk_mt6779_init(void)
{
- return platform_driver_register(&clk_mt6779_drv);
+ int ret = platform_driver_register(&clk_mt6779_drv);
+
+ if (ret)
+ return ret;
+ return platform_driver_register(&clk_mt6779_infra_drv);
}
arch_initcall(clk_mt6779_init);
+
+MODULE_DESCRIPTION("MediaTek MT6779 main clocks driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-apmixedsys.c b/drivers/clk/mediatek/clk-mt6795-apmixedsys.c
new file mode 100644
index 000000000000..91665d7f125e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-apmixedsys.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-fhctl.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-pllfh.h"
+
+#define REG_REF2USB 0x8
+#define REG_AP_PLL_CON7 0x1c
+ #define MD1_MTCMOS_OFF BIT(0)
+ #define MD1_MEM_OFF BIT(1)
+ #define MD1_CLK_OFF BIT(4)
+ #define MD1_ISO_OFF BIT(8)
+
+#define MT6795_PLL_FMAX (3000UL * MHZ)
+#define MT6795_CON0_EN BIT(0)
+#define MT6795_CON0_RST_BAR BIT(24)
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = MT6795_CON0_EN | _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = MT6795_CON0_RST_BAR, \
+ .fmax = MT6795_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = NULL, \
+ .pll_en_bit = 0, \
+ }
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMCA53PLL, "armca53pll", 0x200, 0x20c, 0, PLL_AO,
+ 21, 0x204, 24, 0x0, 0x204, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR,
+ 21, 0x220, 4, 0x0, 0x224, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000101, HAVE_RST_BAR,
+ 7, 0x230, 4, 0x0, 0x234, 14),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0, 0, 21, 0x244, 24, 0x0, 0x244, 0),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0, 0, 21, 0x250, 4, 0x0, 0x254, 0),
+ PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0, 0, 21, 0x260, 4, 0x0, 0x264, 0),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0, 0, 21, 0x270, 4, 0x0, 0x274, 0),
+ PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0, 0, 21, 0x280, 4, 0x0, 0x284, 0),
+ PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0, 0, 21, 0x290, 4, 0x0, 0x294, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0, 0, 31, 0x2a0, 4, 0x2a8, 0x2a4, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0, 0, 31, 0x2b4, 4, 0x2bc, 0x2b8, 0),
+};
+
+enum fh_pll_id {
+ FH_CA53PLL_LL,
+ FH_CA53PLL_BL,
+ FH_MAINPLL,
+ FH_MPLL,
+ FH_MSDCPLL,
+ FH_MMPLL,
+ FH_VENCPLL,
+ FH_TVDPLL,
+ FH_VCODECPLL,
+ FH_NR_FH,
+};
+
+#define _FH(_pllid, _fhid, _slope, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fh_ver = FHCTL_PLLFH_V1, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = _slope, \
+ .slope1_value = _slope, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+#define FH(_pllid, _fhid, _offset) _FH(_pllid, _fhid, 0x6003c97, _offset)
+#define FH_M(_pllid, _fhid, _offset) _FH(_pllid, _fhid, 0x6000140, _offset)
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_ARMCA53PLL, FH_CA53PLL_BL, 0x38),
+ FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x60),
+ FH_M(CLK_APMIXED_MPLL, FH_MPLL, 0x74),
+ FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x88),
+ FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0x9c),
+ FH(CLK_APMIXED_VENCPLL, FH_VENCPLL, 0xb0),
+ FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0xc4),
+ FH(CLK_APMIXED_VCODECPLL, FH_VCODECPLL, 0xd8),
+};
+
+static void clk_mt6795_apmixed_setup_md1(void __iomem *base)
+{
+ void __iomem *reg = base + REG_AP_PLL_CON7;
+
+ /* Turn on MD1 internal clock */
+ writel(readl(reg) & ~MD1_CLK_OFF, reg);
+
+ /* Unlock MD1's MTCMOS power path */
+ writel(readl(reg) & ~MD1_MTCMOS_OFF, reg);
+
+ /* Turn on ISO */
+ writel(readl(reg) & ~MD1_ISO_OFF, reg);
+
+ /* Turn on memory */
+ writel(readl(reg) & ~MD1_MEM_OFF, reg);
+}
+
+static const struct of_device_id of_match_clk_mt6795_apmixed[] = {
+ { .compatible = "mediatek,mt6795-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_apmixed);
+
+static int clk_mt6795_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ const u8 *fhctl_node = "mediatek,mt6795-fhctl";
+ void __iomem *base;
+ struct clk_hw *hw;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+ ret = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
+ if (ret)
+ goto free_clk_data;
+
+ hw = mtk_clk_register_ref2usb_tx("ref2usb_tx", "clk26m", base + REG_REF2USB);
+ if (IS_ERR(hw)) {
+ ret = PTR_ERR(hw);
+ dev_err(dev, "Failed to register ref2usb_tx: %d\n", ret);
+ goto unregister_plls;
+ }
+ clk_data->hws[CLK_APMIXED_REF2USB_TX] = hw;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret) {
+ dev_err(dev, "Cannot register clock provider: %d\n", ret);
+ goto unregister_ref2usb;
+ }
+
+ /* Setup MD1 to avoid random crashes */
+ dev_dbg(dev, "Performing initial setup for MD1\n");
+ clk_mt6795_apmixed_setup_md1(base);
+
+ return 0;
+
+unregister_ref2usb:
+ mtk_clk_unregister_ref2usb_tx(clk_data->hws[CLK_APMIXED_REF2USB_TX]);
+unregister_plls:
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+ return ret;
+}
+
+static void clk_mt6795_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_ref2usb_tx(clk_data->hws[CLK_APMIXED_REF2USB_TX]);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt6795_apmixed_drv = {
+ .probe = clk_mt6795_apmixed_probe,
+ .remove = clk_mt6795_apmixed_remove,
+ .driver = {
+ .name = "clk-mt6795-apmixed",
+ .of_match_table = of_match_clk_mt6795_apmixed,
+ },
+};
+module_platform_driver(clk_mt6795_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 apmixed clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-infracfg.c b/drivers/clk/mediatek/clk-mt6795-infracfg.c
new file mode 100644
index 000000000000..e4559569f5b0
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-infracfg.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <dt-bindings/reset/mediatek,mt6795-resets.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-cpumux.h"
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "reset.h"
+
+#define GATE_ICG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, \
+ _shift, &mtk_clk_gate_ops_no_setclr)
+
+static const struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = 0x0040,
+ .clr_ofs = 0x0044,
+ .sta_ofs = 0x0048,
+};
+
+static const char * const ca53_c0_parents[] = {
+ "clk26m",
+ "armca53pll",
+ "mainpll",
+ "univpll"
+};
+
+static const char * const ca53_c1_parents[] = {
+ "clk26m",
+ "armca53pll",
+ "mainpll",
+ "univpll"
+};
+
+static const struct mtk_composite cpu_muxes[] = {
+ MUX(CLK_INFRA_CA53_C0_SEL, "infra_ca53_c0_sel", ca53_c0_parents, 0x00, 0, 2),
+ MUX(CLK_INFRA_CA53_C1_SEL, "infra_ca53_c1_sel", ca53_c1_parents, 0x00, 2, 2),
+};
+
+static const struct mtk_gate infra_gates[] = {
+ GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0),
+ GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1),
+ GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5),
+ GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6),
+ GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7),
+ GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8),
+ GATE_ICG(CLK_INFRA_MD1MCU, "infra_md1mcu", "clk26m", 9),
+ GATE_ICG(CLK_INFRA_MD1BUS, "infra_md1bus", "axi_sel", 10),
+ GATE_ICG(CLK_INFRA_MD1DBB, "infra_dbb", "axi_sel", 11),
+ GATE_ICG(CLK_INFRA_DEVICE_APC, "infra_devapc", "clk26m", 12),
+ GATE_ICG(CLK_INFRA_TRNG, "infra_trng", "axi_sel", 13),
+ GATE_ICG(CLK_INFRA_MD1LTE, "infra_md1lte", "axi_sel", 14),
+ GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "cpum_ck", 15),
+ GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16),
+};
+
+static u16 infra_ao_rst_ofs[] = { 0x30, 0x34 };
+
+static u16 infra_ao_idx_map[] = {
+ [MT6795_INFRA_RST0_SCPSYS_RST] = 0 * RST_NR_PER_BANK + 5,
+ [MT6795_INFRA_RST0_PMIC_WRAP_RST] = 0 * RST_NR_PER_BANK + 7,
+ [MT6795_INFRA_RST1_MIPI_DSI_RST] = 1 * RST_NR_PER_BANK + 4,
+ [MT6795_INFRA_RST1_MIPI_CSI_RST] = 1 * RST_NR_PER_BANK + 7,
+ [MT6795_INFRA_RST1_MM_IOMMU_RST] = 1 * RST_NR_PER_BANK + 15,
+};
+
+static const struct mtk_clk_rst_desc clk_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = infra_ao_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(infra_ao_rst_ofs),
+ .rst_idx_map = infra_ao_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(infra_ao_idx_map),
+};
+
+static const struct of_device_id of_match_clk_mt6795_infracfg[] = {
+ { .compatible = "mediatek,mt6795-infracfg" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_infracfg);
+
+static int clk_mt6795_infracfg_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
+ if (ret)
+ goto free_clk_data;
+
+ ret = mtk_clk_register_gates(&pdev->dev, node, infra_gates,
+ ARRAY_SIZE(infra_gates), clk_data);
+ if (ret)
+ goto free_clk_data;
+
+ ret = mtk_clk_register_cpumuxes(&pdev->dev, node, cpu_muxes,
+ ARRAY_SIZE(cpu_muxes), clk_data);
+ if (ret)
+ goto unregister_gates;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_cpumuxes;
+
+ return 0;
+
+unregister_cpumuxes:
+ mtk_clk_unregister_cpumuxes(cpu_muxes, ARRAY_SIZE(cpu_muxes), clk_data);
+unregister_gates:
+ mtk_clk_unregister_gates(infra_gates, ARRAY_SIZE(infra_gates), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+ return ret;
+}
+
+static void clk_mt6795_infracfg_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_cpumuxes(cpu_muxes, ARRAY_SIZE(cpu_muxes), clk_data);
+ mtk_clk_unregister_gates(infra_gates, ARRAY_SIZE(infra_gates), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt6795_infracfg_drv = {
+ .driver = {
+ .name = "clk-mt6795-infracfg",
+ .of_match_table = of_match_clk_mt6795_infracfg,
+ },
+ .probe = clk_mt6795_infracfg_probe,
+ .remove = clk_mt6795_infracfg_remove,
+};
+module_platform_driver(clk_mt6795_infracfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-mfg.c b/drivers/clk/mediatek/clk-mt6795-mfg.c
new file mode 100644
index 000000000000..1d658bb19e82
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-mfg.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mfg_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate mfg_clks[] = {
+ GATE_MFG(CLK_MFG_BAXI, "mfg_baxi", "axi_mfg_in_sel", 0),
+ GATE_MFG(CLK_MFG_BMEM, "mfg_bmem", "mem_mfg_in_sel", 1),
+ GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_sel", 2),
+ GATE_MFG(CLK_MFG_B26M, "mfg_b26m", "clk26m", 3),
+};
+
+static const struct mtk_clk_desc mfg_desc = {
+ .clks = mfg_clks,
+ .num_clks = ARRAY_SIZE(mfg_clks),
+};
+
+static const struct of_device_id of_match_clk_mt6795_mfg[] = {
+ { .compatible = "mediatek,mt6795-mfgcfg", .data = &mfg_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_mfg);
+
+static struct platform_driver clk_mt6795_mfg_drv = {
+ .driver = {
+ .name = "clk-mt6795-mfg",
+ .of_match_table = of_match_clk_mt6795_mfg,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt6795_mfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-mm.c b/drivers/clk/mediatek/clk-mt6795-mm.c
new file mode 100644
index 000000000000..733d0e2021fc
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-mm.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#define GATE_MM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_MM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate_regs mm0_cg_regs = {
+ .set_ofs = 0x0104,
+ .clr_ofs = 0x0108,
+ .sta_ofs = 0x0100,
+};
+
+static const struct mtk_gate_regs mm1_cg_regs = {
+ .set_ofs = 0x0114,
+ .clr_ofs = 0x0118,
+ .sta_ofs = 0x0110,
+};
+
+static const struct mtk_gate mm_gates[] = {
+ /* MM0 */
+ GATE_MM0(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 0),
+ GATE_MM0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
+ GATE_MM0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 2),
+ GATE_MM0(CLK_MM_MDP_RDMA0, "mm_mdp_rdma0", "mm_sel", 3),
+ GATE_MM0(CLK_MM_MDP_RDMA1, "mm_mdp_rdma1", "mm_sel", 4),
+ GATE_MM0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 5),
+ GATE_MM0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 6),
+ GATE_MM0(CLK_MM_MDP_RSZ2, "mm_mdp_rsz2", "mm_sel", 7),
+ GATE_MM0(CLK_MM_MDP_TDSHP0, "mm_mdp_tdshp0", "mm_sel", 8),
+ GATE_MM0(CLK_MM_MDP_TDSHP1, "mm_mdp_tdshp1", "mm_sel", 9),
+ GATE_MM0(CLK_MM_MDP_CROP, "mm_mdp_crop", "mm_sel", 10),
+ GATE_MM0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11),
+ GATE_MM0(CLK_MM_MDP_WROT0, "mm_mdp_wrot0", "mm_sel", 12),
+ GATE_MM0(CLK_MM_MDP_WROT1, "mm_mdp_wrot1", "mm_sel", 13),
+ GATE_MM0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 14),
+ GATE_MM0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "clk32k", 15),
+ GATE_MM0(CLK_MM_DISP_OVL0, "mm_disp_ovl0", "mm_sel", 16),
+ GATE_MM0(CLK_MM_DISP_OVL1, "mm_disp_ovl1", "mm_sel", 17),
+ GATE_MM0(CLK_MM_DISP_RDMA0, "mm_disp_rdma0", "mm_sel", 18),
+ GATE_MM0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19),
+ GATE_MM0(CLK_MM_DISP_RDMA2, "mm_disp_rdma2", "mm_sel", 20),
+ GATE_MM0(CLK_MM_DISP_WDMA0, "mm_disp_wdma0", "mm_sel", 21),
+ GATE_MM0(CLK_MM_DISP_WDMA1, "mm_disp_wdma1", "mm_sel", 22),
+ GATE_MM0(CLK_MM_DISP_COLOR0, "mm_disp_color0", "mm_sel", 23),
+ GATE_MM0(CLK_MM_DISP_COLOR1, "mm_disp_color1", "mm_sel", 24),
+ GATE_MM0(CLK_MM_DISP_AAL, "mm_disp_aal", "mm_sel", 25),
+ GATE_MM0(CLK_MM_DISP_GAMMA, "mm_disp_gamma", "mm_sel", 26),
+ GATE_MM0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 27),
+ GATE_MM0(CLK_MM_DISP_SPLIT0, "mm_disp_split0", "mm_sel", 28),
+ GATE_MM0(CLK_MM_DISP_SPLIT1, "mm_disp_split1", "mm_sel", 29),
+ GATE_MM0(CLK_MM_DISP_MERGE, "mm_disp_merge", "mm_sel", 30),
+ GATE_MM0(CLK_MM_DISP_OD, "mm_disp_od", "mm_sel", 31),
+
+ /* MM1 */
+ GATE_MM1(CLK_MM_DISP_PWM0MM, "mm_disp_pwm0mm", "mm_sel", 0),
+ GATE_MM1(CLK_MM_DISP_PWM026M, "mm_disp_pwm026m", "pwm_sel", 1),
+ GATE_MM1(CLK_MM_DISP_PWM1MM, "mm_disp_pwm1mm", "mm_sel", 2),
+ GATE_MM1(CLK_MM_DISP_PWM126M, "mm_disp_pwm126m", "pwm_sel", 3),
+ GATE_MM1(CLK_MM_DSI0_ENGINE, "mm_dsi0_engine", "mm_sel", 4),
+ GATE_MM1(CLK_MM_DSI0_DIGITAL, "mm_dsi0_digital", "dsi0_dig", 5),
+ GATE_MM1(CLK_MM_DSI1_ENGINE, "mm_dsi1_engine", "mm_sel", 6),
+ GATE_MM1(CLK_MM_DSI1_DIGITAL, "mm_dsi1_digital", "dsi1_dig", 7),
+ GATE_MM1(CLK_MM_DPI_PIXEL, "mm_dpi_pixel", "dpi0_sel", 8),
+ GATE_MM1(CLK_MM_DPI_ENGINE, "mm_dpi_engine", "mm_sel", 9),
+};
+
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_gates,
+ .num_clks = ARRAY_SIZE(mm_gates),
+};
+
+static const struct platform_device_id clk_mt6795_mm_id_table[] = {
+ { .name = "clk-mt6795-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt6795_mm_id_table);
+
+static struct platform_driver clk_mt6795_mm_drv = {
+ .driver = {
+ .name = "clk-mt6795-mm",
+ },
+ .id_table = clk_mt6795_mm_id_table,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+};
+module_platform_driver(clk_mt6795_mm_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 MMSYS clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-pericfg.c b/drivers/clk/mediatek/clk-mt6795-pericfg.c
new file mode 100644
index 000000000000..d48240eb2a67
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-pericfg.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <dt-bindings/reset/mediatek,mt6795-resets.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "reset.h"
+
+#define GATE_PERI(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr)
+
+static DEFINE_SPINLOCK(mt6795_peri_clk_lock);
+
+static const struct mtk_gate_regs peri_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x0010,
+ .sta_ofs = 0x0018,
+};
+
+static const char * const uart_ck_sel_parents[] = {
+ "clk26m",
+ "uart_sel",
+};
+
+static const struct mtk_composite peri_clks[] = {
+ MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
+ MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
+ MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
+ MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1),
+};
+
+static const struct mtk_gate peri_gates[] = {
+ GATE_PERI(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0),
+ GATE_PERI(CLK_PERI_THERM, "peri_therm", "axi_sel", 1),
+ GATE_PERI(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2),
+ GATE_PERI(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3),
+ GATE_PERI(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4),
+ GATE_PERI(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5),
+ GATE_PERI(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6),
+ GATE_PERI(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7),
+ GATE_PERI(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8),
+ GATE_PERI(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9),
+ GATE_PERI(CLK_PERI_USB0, "peri_usb0", "usb30_sel", 10),
+ GATE_PERI(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11),
+ GATE_PERI(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12),
+ GATE_PERI(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13),
+ GATE_PERI(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14),
+ GATE_PERI(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15),
+ GATE_PERI(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16),
+ GATE_PERI(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17),
+ GATE_PERI(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18),
+ GATE_PERI(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19),
+ GATE_PERI(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20),
+ GATE_PERI(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21),
+ GATE_PERI(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22),
+ GATE_PERI(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23),
+ GATE_PERI(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24),
+ GATE_PERI(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25),
+ GATE_PERI(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26),
+ GATE_PERI(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27),
+ GATE_PERI(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28),
+ GATE_PERI(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29),
+};
+
+static u16 peri_rst_ofs[] = { 0x0 };
+
+static u16 peri_idx_map[] = {
+ [MT6795_PERI_NFI_SW_RST] = 14,
+ [MT6795_PERI_THERM_SW_RST] = 16,
+ [MT6795_PERI_MSDC1_SW_RST] = 20,
+};
+
+static const struct mtk_clk_rst_desc clk_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = peri_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(peri_rst_ofs),
+ .rst_idx_map = peri_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(peri_idx_map),
+};
+
+static const struct of_device_id of_match_clk_mt6795_pericfg[] = {
+ { .compatible = "mediatek,mt6795-pericfg" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_pericfg);
+
+static int clk_mt6795_pericfg_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
+ if (ret)
+ goto free_clk_data;
+
+ ret = mtk_clk_register_gates(&pdev->dev, node, peri_gates,
+ ARRAY_SIZE(peri_gates), clk_data);
+ if (ret)
+ goto free_clk_data;
+
+ ret = mtk_clk_register_composites(&pdev->dev, peri_clks,
+ ARRAY_SIZE(peri_clks), base,
+ &mt6795_peri_clk_lock, clk_data);
+ if (ret)
+ goto unregister_gates;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_composites;
+
+ return 0;
+
+unregister_composites:
+ mtk_clk_unregister_composites(peri_clks, ARRAY_SIZE(peri_clks), clk_data);
+unregister_gates:
+ mtk_clk_unregister_gates(peri_gates, ARRAY_SIZE(peri_gates), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+ return ret;
+}
+
+static void clk_mt6795_pericfg_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_composites(peri_clks, ARRAY_SIZE(peri_clks), clk_data);
+ mtk_clk_unregister_gates(peri_gates, ARRAY_SIZE(peri_gates), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt6795_pericfg_drv = {
+ .driver = {
+ .name = "clk-mt6795-pericfg",
+ .of_match_table = of_match_clk_mt6795_pericfg,
+ },
+ .probe = clk_mt6795_pericfg_probe,
+ .remove = clk_mt6795_pericfg_remove,
+};
+module_platform_driver(clk_mt6795_pericfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 pericfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-topckgen.c b/drivers/clk/mediatek/clk-mt6795-topckgen.c
new file mode 100644
index 000000000000..9c6d63a80b19
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-topckgen.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+/*
+ * For some clocks, we don't care what their actual rates are. And these
+ * clocks may change their rate on different products or different scenarios.
+ * So we model these clocks' rate as 0, to denote it's not an actual rate.
+ */
+#define DUMMY_RATE 0
+
+#define TOP_MUX_GATE_NOSR(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) \
+ MUX_GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _reg, \
+ (_reg + 0x4), (_reg + 0x8), _shift, _width, \
+ _gate, 0, -1, _flags)
+
+#define TOP_MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) \
+ TOP_MUX_GATE_NOSR(_id, _name, _parents, _reg, _shift, _width, \
+ _gate, CLK_SET_RATE_PARENT | _flags)
+
+static DEFINE_SPINLOCK(mt6795_top_clk_lock);
+
+static const char * const aud_1_parents[] = {
+ "clk26m",
+ "apll1_ck",
+ "univpll2_d4",
+ "univpll2_d8"
+};
+
+static const char * const aud_2_parents[] = {
+ "clk26m",
+ "apll2_ck",
+ "univpll2_d4",
+ "univpll2_d8"
+};
+
+static const char * const aud_intbus_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll4_d2",
+ "univpll3_d2",
+ "univpll2_d8",
+ "dmpll_d4",
+ "dmpll_d8"
+};
+
+static const char * const audio_parents[] = {
+ "clk26m",
+ "syspll3_d4",
+ "syspll4_d4",
+ "syspll1_d16"
+};
+
+static const char * const axi_mfg_in_parents[] = {
+ "clk26m",
+ "axi_sel",
+ "dmpll_d2"
+};
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll2_d2",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const camtg_parents[] = {
+ "clk26m",
+ "univpll_d26",
+ "univpll2_d2",
+ "syspll3_d2",
+ "syspll3_d4",
+ "univpll1_d4",
+ "dmpll_d8"
+};
+
+static const char * const cci400_parents[] = {
+ "clk26m",
+ "vencpll_ck",
+ "clk26m",
+ "clk26m",
+ "univpll_d2",
+ "syspll_d2",
+ "msdcpll_ck",
+ "dmpll_ck"
+};
+
+static const char * const ddrphycfg_parents[] = {
+ "clk26m",
+ "syspll1_d8"
+};
+
+static const char * const dpi0_parents[] = {
+ "clk26m",
+ "tvdpll_d2",
+ "tvdpll_d4",
+ "clk26m",
+ "clk26m",
+ "tvdpll_d8",
+ "tvdpll_d16"
+};
+
+static const char * const i2s0_m_ck_parents[] = {
+ "apll1_div1",
+ "apll2_div1"
+};
+
+static const char * const i2s1_m_ck_parents[] = {
+ "apll1_div2",
+ "apll2_div2"
+};
+
+static const char * const i2s2_m_ck_parents[] = {
+ "apll1_div3",
+ "apll2_div3"
+};
+
+static const char * const i2s3_m_ck_parents[] = {
+ "apll1_div4",
+ "apll2_div4"
+};
+
+static const char * const i2s3_b_ck_parents[] = {
+ "apll1_div5",
+ "apll2_div5"
+};
+
+static const char * const irda_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "syspll2_d4",
+ "dmpll_d8",
+};
+
+static const char * const mem_mfg_in_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "dmpll_ck"
+};
+
+static const char * const mem_parents[] = {
+ "clk26m",
+ "dmpll_ck"
+};
+
+static const char * const mfg_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "dmpll_ck",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "syspll_d3",
+ "syspll1_d2",
+ "syspll_d5",
+ "univpll_d3",
+ "univpll1_d2",
+ "univpll_d5",
+ "univpll2_d2"
+};
+
+static const char * const mm_parents[] = {
+ "clk26m",
+ "vencpll_d2",
+ "syspll_d3",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll1_d2",
+ "univpll2_d2",
+ "dmpll_d2"
+};
+
+static const char * const mjc_parents[] = {
+ "clk26m",
+ "univpll_d3",
+ "vcodecpll_ck",
+ "tvdpll_445p5m",
+ "vencpll_d2",
+ "syspll_d3",
+ "univpll1_d2",
+ "syspll_d5",
+ "syspll1_d2",
+ "univpll_d5",
+ "univpll2_d2",
+ "dmpll_ck"
+};
+
+static const char * const msdc50_0_h_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll2_d2",
+ "syspll4_d2",
+ "univpll_d5",
+ "univpll1_d4"
+};
+
+static const char * const msdc50_0_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "msdcpll_d2",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "msdcpll_d4",
+ "vencpll_d4",
+ "tvdpll_ck",
+ "univpll_d2",
+ "univpll1_d2",
+ "mmpll_ck"
+};
+
+static const char * const msdc30_1_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "univpll_d7",
+ "vencpll_d4"
+};
+
+static const char * const msdc30_2_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "univpll_d7",
+ "vencpll_d2"
+};
+
+static const char * const msdc30_3_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "univpll_d7",
+ "vencpll_d4"
+};
+
+static const char * const pmicspi_parents[] = {
+ "clk26m",
+ "syspll1_d8",
+ "syspll3_d4",
+ "syspll1_d16",
+ "univpll3_d4",
+ "univpll_d26",
+ "dmpll_d8",
+ "dmpll_d16"
+};
+
+static const char * const pwm_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "univpll3_d2",
+ "univpll1_d4"
+};
+
+static const char * const scam_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "univpll2_d4",
+ "dmpll_d4"
+};
+
+static const char * const scp_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "univpll_d5",
+ "syspll_d5",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const spi_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "syspll1_d4",
+ "syspll4_d2",
+ "univpll3_d2",
+ "univpll2_d4",
+ "univpll1_d8"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll2_d8"
+};
+
+static const char * const usb20_parents[] = {
+ "clk26m",
+ "univpll1_d8",
+ "univpll3_d4"
+};
+
+static const char * const usb30_parents[] = {
+ "clk26m",
+ "univpll3_d2",
+ "usb_syspll_125m",
+ "univpll2_d4"
+};
+
+static const char * const vdec_parents[] = {
+ "clk26m",
+ "vcodecpll_ck",
+ "tvdpll_445p5m",
+ "univpll_d3",
+ "vencpll_d2",
+ "syspll_d3",
+ "univpll1_d2",
+ "mmpll_d2",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const venc_parents[] = {
+ "clk26m",
+ "vcodecpll_ck",
+ "tvdpll_445p5m",
+ "univpll_d3",
+ "vencpll_d2",
+ "syspll_d3",
+ "univpll1_d2",
+ "univpll2_d2",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const struct mtk_fixed_clk fixed_clks[] = {
+ FIXED_CLK(CLK_TOP_ADSYS_26M, "adsys_26m", "clk26m", 26 * MHZ),
+ FIXED_CLK(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk26m", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk26m", 125 * MHZ),
+ FIXED_CLK(CLK_TOP_DSI0_DIG, "dsi0_dig", "clk26m", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_DSI1_DIG, "dsi1_dig", "clk26m", DUMMY_RATE),
+};
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_ARMCA53PLL_754M, "armca53pll_754m", "clk26m", 1, 2),
+ FACTOR(CLK_TOP_ARMCA53PLL_502M, "armca53pll_502m", "clk26m", 1, 3),
+
+ FACTOR_FLAGS(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7, 0),
+
+ FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3),
+
+ FACTOR_FLAGS(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26, 0),
+
+ FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1),
+ FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793),
+ FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1),
+
+ FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "clk26m", 1, 2),
+ FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "clk26m", 1, 3),
+
+ FACTOR(CLK_TOP_ARMCA53PLL_D2, "armca53pll_d2", "clk26m", 1, 1),
+ FACTOR(CLK_TOP_ARMCA53PLL_D3, "armca53pll_d3", "clk26m", 1, 1),
+
+ FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
+ FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1),
+
+ FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1),
+ FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2),
+ FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4),
+ FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8),
+ FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16),
+
+ FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
+
+ FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4),
+
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4, 0),
+
+ FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1),
+ FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16),
+
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2, 0),
+
+ FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3),
+ FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4),
+
+ FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1),
+ FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2),
+ FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4),
+};
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ TOP_MUX_GATE_NOSR(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+ 0x40, 0, 3, 7, CLK_IS_CRITICAL),
+ TOP_MUX_GATE_NOSR(CLK_TOP_MEM_SEL, "mem_sel", mem_parents,
+ 0x40, 8, 1, 15, CLK_IS_CRITICAL),
+ TOP_MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents,
+ 0x40, 16, 1, 23, CLK_IS_CRITICAL),
+ TOP_MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x40, 24, 3, 31, 0),
+ /* CLK_CFG_1 */
+ TOP_MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x50, 0, 2, 7, 0),
+ TOP_MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x50, 8, 4, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x50, 16, 4, 23, 0),
+ TOP_MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x50, 24, 4, 31, 0),
+ /* CLK_CFG_2 */
+ TOP_MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x60, 0, 3, 7, 0),
+ TOP_MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x60, 8, 1, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x60, 16, 3, 23, 0),
+ TOP_MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x60, 24, 2, 31, 0),
+ /* CLK_CFG_3 */
+ TOP_MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x70, 0, 2, 7, 0),
+ TOP_MUX_GATE(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents,
+ 0x70, 8, 3, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x70, 16, 4, 23, 0),
+ TOP_MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x70, 24, 3, 31, 0),
+ /* CLK_CFG_4 */
+ TOP_MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x80, 0, 3, 7, 0),
+ TOP_MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x80, 8, 3, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x80, 16, 2, 23, 0),
+ TOP_MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents,
+ 0x80, 24, 3, 31, 0),
+ /* CLK_CFG_5 */
+ TOP_MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x90, 0, 3, 5, 0),
+ TOP_MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x90, 8, 3, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_MJC_SEL, "mjc_sel", mjc_parents, 0x90, 24, 4, 31, 0),
+ /* CLK_CFG_6 */
+ /*
+ * The dpi0_sel clock should not propagate rate changes to its parent
+ * clock so the dpi driver can have full control over PLL and divider.
+ */
+ TOP_MUX_GATE_NOSR(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0xa0, 0, 3, 7, 0),
+ TOP_MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0xa0, 8, 2, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents,
+ 0xa0, 16, 3, 23, CLK_IS_CRITICAL),
+ TOP_MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0xa0, 24, 2, 31, 0),
+ /* CLK_CFG_7 */
+ TOP_MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0xb0, 0, 2, 7, 0),
+ TOP_MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents,
+ 0xb0, 8, 2, 15, 0),
+ TOP_MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents,
+ 0xb0, 16, 2, 23, 0),
+ TOP_MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0xb0, 24, 2, 31, 0),
+};
+
+static struct mtk_composite top_aud_divs[] = {
+ MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1),
+ MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1),
+ MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1),
+ MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1),
+ MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1),
+
+ DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24),
+ DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0),
+ DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8),
+ DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16),
+ DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24),
+ DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0),
+
+ DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28),
+ DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0),
+ DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8),
+ DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16),
+ DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24),
+ DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_aud_divs,
+ .num_composite_clks = ARRAY_SIZE(top_aud_divs),
+ .clk_lock = &mt6795_top_clk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt6795_topckgen[] = {
+ { .compatible = "mediatek,mt6795-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_topckgen);
+
+static struct platform_driver clk_mt6795_topckgen_drv = {
+ .driver = {
+ .name = "clk-mt6795-topckgen",
+ .of_match_table = of_match_clk_mt6795_topckgen,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt6795_topckgen_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 topckgen clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-vdecsys.c b/drivers/clk/mediatek/clk-mt6795-vdecsys.c
new file mode 100644
index 000000000000..f2968f859dca
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-vdecsys.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#define GATE_VDEC(_id, _name, _parent, _regs) \
+ GATE_MTK(_id, _name, _parent, _regs, 0, \
+ &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate_regs vdec0_cg_regs = {
+ .set_ofs = 0x0000,
+ .clr_ofs = 0x0004,
+ .sta_ofs = 0x0000,
+};
+
+static const struct mtk_gate_regs vdec1_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x000c,
+ .sta_ofs = 0x0008,
+};
+
+static const struct mtk_gate vdec_clks[] = {
+ GATE_VDEC(CLK_VDEC_CKEN, "vdec_cken", "vdec_sel", &vdec0_cg_regs),
+ GATE_VDEC(CLK_VDEC_LARB_CKEN, "vdec_larb_cken", "mm_sel", &vdec1_cg_regs),
+};
+
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
+};
+
+static const struct of_device_id of_match_clk_mt6795_vdecsys[] = {
+ { .compatible = "mediatek,mt6795-vdecsys", .data = &vdec_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_vdecsys);
+
+static struct platform_driver clk_mt6795_vdecsys_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt6795-vdecsys",
+ .of_match_table = of_match_clk_mt6795_vdecsys,
+ },
+};
+module_platform_driver(clk_mt6795_vdecsys_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 vdecsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6795-vencsys.c b/drivers/clk/mediatek/clk-mt6795-vencsys.c
new file mode 100644
index 000000000000..2f8d48da1a85
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6795-vencsys.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt6795-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs venc_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_VENC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate venc_clks[] = {
+ GATE_VENC(CLK_VENC_LARB, "venc_larb", "venc_sel", 0),
+ GATE_VENC(CLK_VENC_VENC, "venc_venc", "venc_sel", 4),
+ GATE_VENC(CLK_VENC_JPGENC, "venc_jpgenc", "venc_sel", 8),
+ GATE_VENC(CLK_VENC_JPGDEC, "venc_jpgdec", "venc_sel", 12),
+};
+
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
+};
+
+static const struct of_device_id of_match_clk_mt6795_vencsys[] = {
+ { .compatible = "mediatek,mt6795-vencsys", .data = &venc_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_vencsys);
+
+static struct platform_driver clk_mt6795_vencsys_drv = {
+ .driver = {
+ .name = "clk-mt6795-vencsys",
+ .of_match_table = of_match_clk_mt6795_vencsys,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt6795_vencsys_drv);
+
+MODULE_DESCRIPTION("MediaTek MT6795 vdecsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6797-img.c b/drivers/clk/mediatek/clk-mt6797-img.c
index 25d17db13bac..338c69234f24 100644
--- a/drivers/clk/mediatek/clk-mt6797-img.c
+++ b/drivers/clk/mediatek/clk-mt6797-img.c
@@ -16,14 +16,8 @@ static const struct mtk_gate_regs img_cg_regs = {
.sta_ofs = 0x0000,
};
-#define GATE_IMG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &img_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IMG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_FDVT, "img_fdvt", "mm_sel", 11),
@@ -32,37 +26,30 @@ static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_LARB6, "img_larb6", "mm_sel", 0),
};
-static const struct of_device_id of_match_clk_mt6797_img[] = {
- { .compatible = "mediatek,mt6797-imgsys", },
- {}
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
};
-static int clk_mt6797_img_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt6797_img[] = {
+ {
+ .compatible = "mediatek,mt6797-imgsys",
+ .data = &img_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6797_img);
static struct platform_driver clk_mt6797_img_drv = {
- .probe = clk_mt6797_img_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6797-img",
.of_match_table = of_match_clk_mt6797_img,
},
};
+module_platform_driver(clk_mt6797_img_drv);
-builtin_platform_driver(clk_mt6797_img_drv);
+MODULE_DESCRIPTION("MediaTek MT6797 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6797-mm.c b/drivers/clk/mediatek/clk-mt6797-mm.c
index 0846011fc894..ddb40b8a1a7d 100644
--- a/drivers/clk/mediatek/clk-mt6797-mm.c
+++ b/drivers/clk/mediatek/clk-mt6797-mm.c
@@ -23,23 +23,11 @@ static const struct mtk_gate_regs mm1_cg_regs = {
.sta_ofs = 0x0110,
};
-#define GATE_MM0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
-}
+#define GATE_MM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_MM1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
-}
+#define GATE_MM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mm_clks[] = {
GATE_MM0(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 0),
@@ -92,32 +80,26 @@ static const struct mtk_gate mm_clks[] = {
"clk26m", 3),
};
-static int clk_mt6797_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR);
-
- mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return r;
-}
+static const struct platform_device_id clk_mt6797_mm_id_table[] = {
+ { .name = "clk-mt6797-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt6797_mm_id_table);
static struct platform_driver clk_mt6797_mm_drv = {
- .probe = clk_mt6797_mm_probe,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt6797-mm",
},
+ .id_table = clk_mt6797_mm_id_table,
};
+module_platform_driver(clk_mt6797_mm_drv);
-builtin_platform_driver(clk_mt6797_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT6797 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6797-vdec.c b/drivers/clk/mediatek/clk-mt6797-vdec.c
index de857894e033..d832f48123f5 100644
--- a/drivers/clk/mediatek/clk-mt6797-vdec.c
+++ b/drivers/clk/mediatek/clk-mt6797-vdec.c
@@ -24,23 +24,11 @@ static const struct mtk_gate_regs vdec1_cg_regs = {
.sta_ofs = 0x0008,
};
-#define GATE_VDEC0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
-}
+#define GATE_VDEC0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
-#define GATE_VDEC1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
-}
+#define GATE_VDEC1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate vdec_clks[] = {
GATE_VDEC0(CLK_VDEC_CKEN_ENG, "vdec_cken_eng", "vdec_sel", 8),
@@ -49,37 +37,30 @@ static const struct mtk_gate vdec_clks[] = {
GATE_VDEC1(CLK_VDEC_LARB1_CKEN, "vdec_larb1_cken", "mm_sel", 0),
};
-static const struct of_device_id of_match_clk_mt6797_vdec[] = {
- { .compatible = "mediatek,mt6797-vdecsys", },
- {}
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
};
-static int clk_mt6797_vdec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_NR);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt6797_vdec[] = {
+ {
+ .compatible = "mediatek,mt6797-vdecsys",
+ .data = &vdec_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6797_vdec);
static struct platform_driver clk_mt6797_vdec_drv = {
- .probe = clk_mt6797_vdec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6797-vdec",
.of_match_table = of_match_clk_mt6797_vdec,
},
};
+module_platform_driver(clk_mt6797_vdec_drv);
-builtin_platform_driver(clk_mt6797_vdec_drv);
+MODULE_DESCRIPTION("MediaTek MT6797 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6797-venc.c b/drivers/clk/mediatek/clk-mt6797-venc.c
index 78b7ed55f979..fd4446f4a9d7 100644
--- a/drivers/clk/mediatek/clk-mt6797-venc.c
+++ b/drivers/clk/mediatek/clk-mt6797-venc.c
@@ -18,14 +18,8 @@ static const struct mtk_gate_regs venc_cg_regs = {
.sta_ofs = 0x0000,
};
-#define GATE_VENC(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &venc_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VENC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
static const struct mtk_gate venc_clks[] = {
GATE_VENC(CLK_VENC_0, "venc_0", "mm_sel", 0),
@@ -34,37 +28,30 @@ static const struct mtk_gate venc_clks[] = {
GATE_VENC(CLK_VENC_3, "venc_3", "venc_sel", 12),
};
-static const struct of_device_id of_match_clk_mt6797_venc[] = {
- { .compatible = "mediatek,mt6797-vencsys", },
- {}
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
};
-static int clk_mt6797_venc_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VENC_NR);
-
- mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+static const struct of_device_id of_match_clk_mt6797_venc[] = {
+ {
+ .compatible = "mediatek,mt6797-vencsys",
+ .data = &venc_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6797_venc);
static struct platform_driver clk_mt6797_venc_drv = {
- .probe = clk_mt6797_venc_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt6797-venc",
.of_match_table = of_match_clk_mt6797_venc,
},
};
+module_platform_driver(clk_mt6797_venc_drv);
-builtin_platform_driver(clk_mt6797_venc_drv);
+MODULE_DESCRIPTION("MediaTek MT6797 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt6797.c b/drivers/clk/mediatek/clk-mt6797.c
index b89f325a4b9b..fb59e71af58e 100644
--- a/drivers/clk/mediatek/clk-mt6797.c
+++ b/drivers/clk/mediatek/clk-mt6797.c
@@ -5,8 +5,6 @@
*/
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "clk-gate.h"
@@ -392,11 +390,14 @@ static int mtk_topckgen_init(struct platform_device *pdev)
return PTR_ERR(base);
clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_factors(top_fixed_divs, ARRAY_SIZE(top_fixed_divs),
clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
+ mtk_clk_register_composites(&pdev->dev, top_muxes,
+ ARRAY_SIZE(top_muxes), base,
&mt6797_clk_lock, clk_data);
return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
@@ -420,40 +421,22 @@ static const struct mtk_gate_regs infra2_cg_regs = {
.sta_ofs = 0x00b0,
};
-#define GATE_ICG0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
-}
+#define GATE_ICG0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_ICG1(_id, _name, _parent, _shift) \
- GATE_ICG1_FLAGS(_id, _name, _parent, _shift, 0)
+#define GATE_ICG1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_ICG1_FLAGS(_id, _name, _parent, _shift, _flags) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- .flags = _flags, \
-}
+#define GATE_ICG1_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra1_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flags)
-#define GATE_ICG2(_id, _name, _parent, _shift) \
- GATE_ICG2_FLAGS(_id, _name, _parent, _shift, 0)
+#define GATE_ICG2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_ICG2_FLAGS(_id, _name, _parent, _shift, _flags) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- .flags = _flags, \
-}
+#define GATE_ICG2_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra2_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flags)
/*
* Clock gates dramc and dramc_b are needed by the DRAM controller.
@@ -564,6 +547,8 @@ static void mtk_infrasys_init_early(struct device_node *node)
if (!infra_clk_data) {
infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
+ if (!infra_clk_data)
+ return;
for (i = 0; i < CLK_INFRA_NR; i++)
infra_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
@@ -589,6 +574,8 @@ static int mtk_infrasys_init(struct platform_device *pdev)
if (!infra_clk_data) {
infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
+ if (!infra_clk_data)
+ return -ENOMEM;
} else {
for (i = 0; i < CLK_INFRA_NR; i++) {
if (infra_clk_data->hws[i] == ERR_PTR(-EPROBE_DEFER))
@@ -596,8 +583,8 @@ static int mtk_infrasys_init(struct platform_device *pdev)
}
}
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- infra_clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, infra_clks,
+ ARRAY_SIZE(infra_clks), infra_clk_data);
mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
infra_clk_data);
@@ -687,6 +674,7 @@ static const struct of_device_id of_match_clk_mt6797[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt6797);
static int clk_mt6797_probe(struct platform_device *pdev)
{
@@ -720,3 +708,6 @@ static int __init clk_mt6797_init(void)
}
arch_initcall(clk_mt6797_init);
+
+MODULE_DESCRIPTION("MediaTek MT6797 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7622-apmixedsys.c b/drivers/clk/mediatek/clk-mt7622-apmixedsys.c
new file mode 100644
index 000000000000..2350592d9a93
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7622-apmixedsys.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ * Copyright (c) 2023 Collabora, Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#define MT7622_PLL_FMAX (2500UL * MHZ)
+#define CON0_MT7622_RST_BAR BIT(27)
+
+#define PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,\
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift, _div_table, _parent_name) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT7622_RST_BAR, \
+ .fmax = MT7622_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = _div_table, \
+ .parent_name = _parent_name, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift) \
+ PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,\
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
+ NULL, "clkxtal")
+
+static const struct mtk_gate_regs apmixed_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x8,
+};
+
+#define GATE_APMIXED_AO(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &apmixed_cg_regs, _shift, \
+ &mtk_clk_gate_ops_no_setclr_inv, CLK_IS_CRITICAL)
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0,
+ PLL_AO, 21, 0x0204, 24, 0, 0x0204, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0210, 0x021C, 0,
+ HAVE_RST_BAR, 21, 0x0214, 24, 0, 0x0214, 0),
+ PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x0220, 0x022C, 0,
+ HAVE_RST_BAR, 7, 0x0224, 24, 0, 0x0224, 14),
+ PLL(CLK_APMIXED_ETH1PLL, "eth1pll", 0x0300, 0x0310, 0,
+ 0, 21, 0x0300, 1, 0, 0x0304, 0),
+ PLL(CLK_APMIXED_ETH2PLL, "eth2pll", 0x0314, 0x0320, 0,
+ 0, 21, 0x0314, 1, 0, 0x0318, 0),
+ PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x0324, 0x0330, 0,
+ 0, 31, 0x0324, 1, 0, 0x0328, 0),
+ PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x0334, 0x0340, 0,
+ 0, 31, 0x0334, 1, 0, 0x0338, 0),
+ PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x0344, 0x0354, 0,
+ 0, 21, 0x0344, 1, 0, 0x0348, 0),
+ PLL(CLK_APMIXED_SGMIPLL, "sgmipll", 0x0358, 0x0368, 0,
+ 0, 21, 0x0358, 1, 0, 0x035C, 0),
+};
+
+static const struct mtk_gate apmixed_clks[] = {
+ GATE_APMIXED_AO(CLK_APMIXED_MAIN_CORE_EN, "main_core_en", "mainpll", 5),
+};
+
+static int clk_mt7622_apmixed_probe(struct platform_device *pdev)
+{
+ void __iomem *base;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_devm_alloc_clk_data(dev, CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (ret)
+ return ret;
+
+ ret = mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
+ ARRAY_SIZE(apmixed_clks), clk_data);
+ if (ret)
+ goto unregister_plls;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_gates;
+
+ return 0;
+
+unregister_gates:
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+
+ return ret;
+}
+
+static void clk_mt7622_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+}
+
+static const struct of_device_id of_match_clk_mt7622_apmixed[] = {
+ { .compatible = "mediatek,mt7622-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7622_apmixed);
+
+static struct platform_driver clk_mt7622_apmixed_drv = {
+ .probe = clk_mt7622_apmixed_probe,
+ .remove = clk_mt7622_apmixed_remove,
+ .driver = {
+ .name = "clk-mt7622-apmixed",
+ .of_match_table = of_match_clk_mt7622_apmixed,
+ },
+};
+module_platform_driver(clk_mt7622_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT7622 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7622-aud.c b/drivers/clk/mediatek/clk-mt7622-aud.c
index 9f2e5aa7b5d9..a4ea5e20efa2 100644
--- a/drivers/clk/mediatek/clk-mt7622-aud.c
+++ b/drivers/clk/mediatek/clk-mt7622-aud.c
@@ -8,7 +8,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -16,41 +16,17 @@
#include <dt-bindings/clock/mt7622-clk.h>
-#define GATE_AUDIO0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio2_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-#define GATE_AUDIO3(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &audio3_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUDIO3(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &audio3_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
static const struct mtk_gate_regs audio0_cg_regs = {
.set_ofs = 0x0,
@@ -99,6 +75,7 @@ static const struct mtk_gate audio_clks[] = {
GATE_AUDIO1(CLK_AUDIO_A1SYS, "audio_a1sys", "a1sys_hp_sel", 21),
GATE_AUDIO1(CLK_AUDIO_A2SYS, "audio_a2sys", "a2sys_hp_sel", 22),
GATE_AUDIO1(CLK_AUDIO_AFE_CONN, "audio_afe_conn", "a1sys_hp_sel", 23),
+ GATE_AUDIO1(CLK_AUDIO_AFE_MRGIF, "audio_afe_mrgif", "aud_mux1_sel", 25),
/* AUDIO2 */
GATE_AUDIO2(CLK_AUDIO_UL1, "audio_ul1", "a1sys_hp_sel", 0),
GATE_AUDIO2(CLK_AUDIO_UL2, "audio_ul2", "a1sys_hp_sel", 1),
@@ -130,24 +107,22 @@ static const struct mtk_gate audio_clks[] = {
GATE_AUDIO3(CLK_AUDIO_MEM_ASRC5, "audio_mem_asrc5", "asm_h_sel", 14),
};
-static int clk_mt7622_audiosys_init(struct platform_device *pdev)
+static const struct mtk_clk_desc audio_desc = {
+ .clks = audio_clks,
+ .num_clks = ARRAY_SIZE(audio_clks),
+};
+
+static int clk_mt7622_aud_probe(struct platform_device *pdev)
{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
int r;
- clk_data = mtk_alloc_clk_data(CLK_AUDIO_NR_CLK);
-
- mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ r = mtk_clk_simple_probe(pdev);
if (r) {
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
- goto err_clk_provider;
+ return r;
}
r = devm_of_platform_populate(&pdev->dev);
@@ -157,44 +132,31 @@ static int clk_mt7622_audiosys_init(struct platform_device *pdev)
return 0;
err_plat_populate:
- of_clk_del_provider(node);
-err_clk_provider:
+ mtk_clk_simple_remove(pdev);
return r;
}
-static const struct of_device_id of_match_clk_mt7622_aud[] = {
- {
- .compatible = "mediatek,mt7622-audsys",
- .data = clk_mt7622_audiosys_init,
- }, {
- /* sentinel */
- }
-};
-
-static int clk_mt7622_aud_probe(struct platform_device *pdev)
+static void clk_mt7622_aud_remove(struct platform_device *pdev)
{
- int (*clk_init)(struct platform_device *);
- int r;
-
- clk_init = of_device_get_match_data(&pdev->dev);
- if (!clk_init)
- return -EINVAL;
-
- r = clk_init(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
+ of_platform_depopulate(&pdev->dev);
+ mtk_clk_simple_remove(pdev);
}
+static const struct of_device_id of_match_clk_mt7622_aud[] = {
+ { .compatible = "mediatek,mt7622-audsys", .data = &audio_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7622_aud);
+
static struct platform_driver clk_mt7622_aud_drv = {
.probe = clk_mt7622_aud_probe,
+ .remove = clk_mt7622_aud_remove,
.driver = {
.name = "clk-mt7622-aud",
.of_match_table = of_match_clk_mt7622_aud,
},
};
+module_platform_driver(clk_mt7622_aud_drv);
-builtin_platform_driver(clk_mt7622_aud_drv);
+MODULE_DESCRIPTION("MediaTek MT7622 audio clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7622-eth.c b/drivers/clk/mediatek/clk-mt7622-eth.c
index 43de0477d5d9..1c1033a92c46 100644
--- a/drivers/clk/mediatek/clk-mt7622-eth.c
+++ b/drivers/clk/mediatek/clk-mt7622-eth.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -16,14 +14,8 @@
#include <dt-bindings/clock/mt7622-clk.h>
-#define GATE_ETH(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &eth_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_ETH(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &eth_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate_regs eth_cg_regs = {
.set_ofs = 0x30,
@@ -45,14 +37,8 @@ static const struct mtk_gate_regs sgmii_cg_regs = {
.sta_ofs = 0xE4,
};
-#define GATE_SGMII(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &sgmii_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_SGMII(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &sgmii_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate sgmii_clks[] = {
GATE_SGMII(CLK_SGMII_TX250M_EN, "sgmii_tx250m_en",
@@ -73,84 +59,33 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(rst_ofs),
};
-static int clk_mt7622_ethsys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_ETH_NR_CLK);
-
- mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return r;
-}
-
-static int clk_mt7622_sgmiisys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_SGMII_NR_CLK);
-
- mtk_clk_register_gates(node, sgmii_clks, ARRAY_SIZE(sgmii_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
+static const struct mtk_clk_desc eth_desc = {
+ .clks = eth_clks,
+ .num_clks = ARRAY_SIZE(eth_clks),
+ .rst_desc = &clk_rst_desc,
+};
- return r;
-}
+static const struct mtk_clk_desc sgmii_desc = {
+ .clks = sgmii_clks,
+ .num_clks = ARRAY_SIZE(sgmii_clks),
+};
static const struct of_device_id of_match_clk_mt7622_eth[] = {
- {
- .compatible = "mediatek,mt7622-ethsys",
- .data = clk_mt7622_ethsys_init,
- }, {
- .compatible = "mediatek,mt7622-sgmiisys",
- .data = clk_mt7622_sgmiisys_init,
- }, {
- /* sentinel */
- }
+ { .compatible = "mediatek,mt7622-ethsys", .data = &eth_desc },
+ { .compatible = "mediatek,mt7622-sgmiisys", .data = &sgmii_desc },
+ { /* sentinel */ }
};
-
-static int clk_mt7622_eth_probe(struct platform_device *pdev)
-{
- int (*clk_init)(struct platform_device *);
- int r;
-
- clk_init = of_device_get_match_data(&pdev->dev);
- if (!clk_init)
- return -EINVAL;
-
- r = clk_init(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7622_eth);
static struct platform_driver clk_mt7622_eth_drv = {
- .probe = clk_mt7622_eth_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt7622-eth",
.of_match_table = of_match_clk_mt7622_eth,
},
};
+module_platform_driver(clk_mt7622_eth_drv);
-builtin_platform_driver(clk_mt7622_eth_drv);
+MODULE_DESCRIPTION("MediaTek MT7622 Ethernet clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7622-hif.c b/drivers/clk/mediatek/clk-mt7622-hif.c
index 67e96231dd25..5bcfe12c4fd0 100644
--- a/drivers/clk/mediatek/clk-mt7622-hif.c
+++ b/drivers/clk/mediatek/clk-mt7622-hif.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -16,23 +14,11 @@
#include <dt-bindings/clock/mt7622-clk.h>
-#define GATE_PCIE(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &pcie_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
-
-#define GATE_SSUSB(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &ssusb_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_PCIE(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &pcie_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_SSUSB(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ssusb_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate_regs pcie_cg_regs = {
.set_ofs = 0x30,
@@ -84,86 +70,34 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(rst_ofs),
};
-static int clk_mt7622_ssusbsys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_SSUSB_NR_CLK);
-
- mtk_clk_register_gates(node, ssusb_clks, ARRAY_SIZE(ssusb_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return r;
-}
-
-static int clk_mt7622_pciesys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_PCIE_NR_CLK);
-
- mtk_clk_register_gates(node, pcie_clks, ARRAY_SIZE(pcie_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
+static const struct mtk_clk_desc ssusb_desc = {
+ .clks = ssusb_clks,
+ .num_clks = ARRAY_SIZE(ssusb_clks),
+ .rst_desc = &clk_rst_desc,
+};
- return r;
-}
+static const struct mtk_clk_desc pcie_desc = {
+ .clks = pcie_clks,
+ .num_clks = ARRAY_SIZE(pcie_clks),
+ .rst_desc = &clk_rst_desc,
+};
static const struct of_device_id of_match_clk_mt7622_hif[] = {
- {
- .compatible = "mediatek,mt7622-pciesys",
- .data = clk_mt7622_pciesys_init,
- }, {
- .compatible = "mediatek,mt7622-ssusbsys",
- .data = clk_mt7622_ssusbsys_init,
- }, {
- /* sentinel */
- }
+ { .compatible = "mediatek,mt7622-pciesys", .data = &pcie_desc },
+ { .compatible = "mediatek,mt7622-ssusbsys", .data = &ssusb_desc },
+ { /* sentinel */ }
};
-
-static int clk_mt7622_hif_probe(struct platform_device *pdev)
-{
- int (*clk_init)(struct platform_device *);
- int r;
-
- clk_init = of_device_get_match_data(&pdev->dev);
- if (!clk_init)
- return -EINVAL;
-
- r = clk_init(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7622_hif);
static struct platform_driver clk_mt7622_hif_drv = {
- .probe = clk_mt7622_hif_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt7622-hif",
.of_match_table = of_match_clk_mt7622_hif,
},
};
+module_platform_driver(clk_mt7622_hif_drv);
-builtin_platform_driver(clk_mt7622_hif_drv);
+MODULE_DESCRIPTION("MediaTek MT7622 HIF clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7622-infracfg.c b/drivers/clk/mediatek/clk-mt7622-infracfg.c
new file mode 100644
index 000000000000..cfdf3b07c3e0
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7622-infracfg.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ * Copyright (c) 2023 Collabora, Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt7622-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "clk-cpumux.h"
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "reset.h"
+
+#define GATE_INFRA(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = 0x40,
+ .clr_ofs = 0x44,
+ .sta_ofs = 0x48,
+};
+
+static const char * const infra_mux1_parents[] = {
+ "clkxtal",
+ "armpll",
+ "main_core_en",
+ "armpll"
+};
+
+static const struct mtk_composite cpu_muxes[] = {
+ MUX(CLK_INFRA_MUX1_SEL, "infra_mux1_sel", infra_mux1_parents, 0x000, 2, 2),
+};
+
+static const struct mtk_gate infra_clks[] = {
+ GATE_INFRA(CLK_INFRA_DBGCLK_PD, "infra_dbgclk_pd", "axi_sel", 0),
+ GATE_INFRA(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 2),
+ GATE_INFRA(CLK_INFRA_AUDIO_PD, "infra_audio_pd", "aud_intbus_sel", 5),
+ GATE_INFRA(CLK_INFRA_IRRX_PD, "infra_irrx_pd", "irrx_sel", 16),
+ GATE_INFRA(CLK_INFRA_APXGPT_PD, "infra_apxgpt_pd", "f10m_ref_sel", 18),
+ GATE_INFRA(CLK_INFRA_PMIC_PD, "infra_pmic_pd", "pmicspi_sel", 22),
+};
+
+static u16 infrasys_rst_ofs[] = { 0x30 };
+
+static const struct mtk_clk_rst_desc clk_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = infrasys_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs),
+};
+
+static const struct of_device_id of_match_clk_mt7622_infracfg[] = {
+ { .compatible = "mediatek,mt7622-infracfg" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7622_infracfg);
+
+static int clk_mt7622_infracfg_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
+ if (ret)
+ goto free_clk_data;
+
+ ret = mtk_clk_register_gates(&pdev->dev, node, infra_clks,
+ ARRAY_SIZE(infra_clks), clk_data);
+ if (ret)
+ goto free_clk_data;
+
+ ret = mtk_clk_register_cpumuxes(&pdev->dev, node, cpu_muxes,
+ ARRAY_SIZE(cpu_muxes), clk_data);
+ if (ret)
+ goto unregister_gates;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_cpumuxes;
+
+ return 0;
+
+unregister_cpumuxes:
+ mtk_clk_unregister_cpumuxes(cpu_muxes, ARRAY_SIZE(cpu_muxes), clk_data);
+unregister_gates:
+ mtk_clk_unregister_gates(infra_clks, ARRAY_SIZE(infra_clks), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+ return ret;
+}
+
+static void clk_mt7622_infracfg_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_cpumuxes(cpu_muxes, ARRAY_SIZE(cpu_muxes), clk_data);
+ mtk_clk_unregister_gates(infra_clks, ARRAY_SIZE(infra_clks), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt7622_infracfg_drv = {
+ .driver = {
+ .name = "clk-mt7622-infracfg",
+ .of_match_table = of_match_clk_mt7622_infracfg,
+ },
+ .probe = clk_mt7622_infracfg_probe,
+ .remove = clk_mt7622_infracfg_remove,
+};
+module_platform_driver(clk_mt7622_infracfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7622 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7622.c b/drivers/clk/mediatek/clk-mt7622.c
index 3b55f8641fae..f62b03abab4f 100644
--- a/drivers/clk/mediatek/clk-mt7622.c
+++ b/drivers/clk/mediatek/clk-mt7622.c
@@ -6,112 +6,33 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-cpumux.h"
#include "clk-gate.h"
#include "clk-mtk.h"
-#include "clk-pll.h"
#include <dt-bindings/clock/mt7622-clk.h>
#include <linux/clk.h> /* for consumer */
-#define MT7622_PLL_FMAX (2500UL * MHZ)
-#define CON0_MT7622_RST_BAR BIT(27)
-
-#define PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,\
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift, _div_table, _parent_name) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = CON0_MT7622_RST_BAR, \
- .fmax = MT7622_PLL_FMAX, \
- .pcwbits = _pcwbits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .div_table = _div_table, \
- .parent_name = _parent_name, \
- }
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift) \
- PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,\
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
- NULL, "clkxtal")
-
-#define GATE_APMIXED(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &apmixed_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
-
-#define GATE_INFRA(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
-
-#define GATE_TOP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
-
-#define GATE_PERI0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_PERI1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_TOP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-static DEFINE_SPINLOCK(mt7622_clk_lock);
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-static const char * const infra_mux1_parents[] = {
- "clkxtal",
- "armpll",
- "main_core_en",
- "armpll"
-};
+#define GATE_PERI0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_PERI0_AO(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &peri0_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_IS_CRITICAL)
+
+#define GATE_PERI1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static DEFINE_SPINLOCK(mt7622_clk_lock);
static const char * const axi_parents[] = {
"clkxtal",
@@ -292,18 +213,6 @@ static const char * const peribus_ck_parents[] = {
"syspll1_d4"
};
-static const struct mtk_gate_regs apmixed_cg_regs = {
- .set_ofs = 0x8,
- .clr_ofs = 0x8,
- .sta_ofs = 0x8,
-};
-
-static const struct mtk_gate_regs infra_cg_regs = {
- .set_ofs = 0x40,
- .clr_ofs = 0x44,
- .sta_ofs = 0x48,
-};
-
static const struct mtk_gate_regs top0_cg_regs = {
.set_ofs = 0x120,
.clr_ofs = 0x120,
@@ -328,40 +237,6 @@ static const struct mtk_gate_regs peri1_cg_regs = {
.sta_ofs = 0x1C,
};
-static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0,
- PLL_AO, 21, 0x0204, 24, 0, 0x0204, 0),
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0210, 0x021C, 0,
- HAVE_RST_BAR, 21, 0x0214, 24, 0, 0x0214, 0),
- PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x0220, 0x022C, 0,
- HAVE_RST_BAR, 7, 0x0224, 24, 0, 0x0224, 14),
- PLL(CLK_APMIXED_ETH1PLL, "eth1pll", 0x0300, 0x0310, 0,
- 0, 21, 0x0300, 1, 0, 0x0304, 0),
- PLL(CLK_APMIXED_ETH2PLL, "eth2pll", 0x0314, 0x0320, 0,
- 0, 21, 0x0314, 1, 0, 0x0318, 0),
- PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x0324, 0x0330, 0,
- 0, 31, 0x0324, 1, 0, 0x0328, 0),
- PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x0334, 0x0340, 0,
- 0, 31, 0x0334, 1, 0, 0x0338, 0),
- PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x0344, 0x0354, 0,
- 0, 21, 0x0344, 1, 0, 0x0348, 0),
- PLL(CLK_APMIXED_SGMIPLL, "sgmipll", 0x0358, 0x0368, 0,
- 0, 21, 0x0358, 1, 0, 0x035C, 0),
-};
-
-static const struct mtk_gate apmixed_clks[] = {
- GATE_APMIXED(CLK_APMIXED_MAIN_CORE_EN, "main_core_en", "mainpll", 5),
-};
-
-static const struct mtk_gate infra_clks[] = {
- GATE_INFRA(CLK_INFRA_DBGCLK_PD, "infra_dbgclk_pd", "axi_sel", 0),
- GATE_INFRA(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 2),
- GATE_INFRA(CLK_INFRA_AUDIO_PD, "infra_audio_pd", "aud_intbus_sel", 5),
- GATE_INFRA(CLK_INFRA_IRRX_PD, "infra_irrx_pd", "irrx_sel", 16),
- GATE_INFRA(CLK_INFRA_APXGPT_PD, "infra_apxgpt_pd", "f10m_ref_sel", 18),
- GATE_INFRA(CLK_INFRA_PMIC_PD, "infra_pmic_pd", "pmicspi_sel", 22),
-};
-
static const struct mtk_fixed_clk top_fixed_clks[] = {
FIXED_CLK(CLK_TOP_TO_U2_PHY, "to_u2_phy", "clkxtal",
31250000),
@@ -485,7 +360,7 @@ static const struct mtk_gate peri_clks[] = {
GATE_PERI0(CLK_PERI_AP_DMA_PD, "peri_ap_dma_pd", "axi_sel", 12),
GATE_PERI0(CLK_PERI_MSDC30_0_PD, "peri_msdc30_0", "msdc30_0_sel", 13),
GATE_PERI0(CLK_PERI_MSDC30_1_PD, "peri_msdc30_1", "msdc30_1_sel", 14),
- GATE_PERI0(CLK_PERI_UART0_PD, "peri_uart0_pd", "axi_sel", 17),
+ GATE_PERI0_AO(CLK_PERI_UART0_PD, "peri_uart0_pd", "axi_sel", 17),
GATE_PERI0(CLK_PERI_UART1_PD, "peri_uart1_pd", "axi_sel", 18),
GATE_PERI0(CLK_PERI_UART2_PD, "peri_uart2_pd", "axi_sel", 19),
GATE_PERI0(CLK_PERI_UART3_PD, "peri_uart3_pd", "axi_sel", 20),
@@ -506,19 +381,14 @@ static const struct mtk_gate peri_clks[] = {
GATE_PERI1(CLK_PERI_IRTX_PD, "peri_irtx_pd", "irtx_sel", 2),
};
-static struct mtk_composite infra_muxes[] = {
- MUX(CLK_INFRA_MUX1_SEL, "infra_mux1_sel", infra_mux1_parents,
- 0x000, 2, 2),
-};
-
static struct mtk_composite top_muxes[] = {
/* CLK_CFG_0 */
- MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
- 0x040, 0, 3, 7),
- MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents,
- 0x040, 8, 1, 15),
- MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents,
- 0x040, 16, 1, 23),
+ MUX_GATE_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+ 0x040, 0, 3, 7, CLK_IS_CRITICAL),
+ MUX_GATE_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents,
+ 0x040, 8, 1, 15, CLK_IS_CRITICAL),
+ MUX_GATE_FLAGS(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents,
+ 0x040, 16, 1, 23, CLK_IS_CRITICAL),
MUX_GATE(CLK_TOP_ETH_SEL, "eth_sel", eth_parents,
0x040, 24, 3, 31),
@@ -610,180 +480,53 @@ static struct mtk_composite peri_muxes[] = {
MUX(CLK_PERIBUS_SEL, "peribus_ck_sel", peribus_ck_parents, 0x05C, 0, 1),
};
-static u16 infrasys_rst_ofs[] = { 0x30, };
static u16 pericfg_rst_ofs[] = { 0x0, 0x4, };
-static const struct mtk_clk_rst_desc clk_rst_desc[] = {
- /* infrasys */
- {
- .version = MTK_RST_SIMPLE,
- .rst_bank_ofs = infrasys_rst_ofs,
- .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs),
- },
- /* pericfg */
- {
- .version = MTK_RST_SIMPLE,
- .rst_bank_ofs = pericfg_rst_ofs,
- .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs),
- },
+static const struct mtk_clk_rst_desc clk_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = pericfg_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs),
};
-static int mtk_topckgen_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- struct device_node *node = pdev->dev.of_node;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
-
- mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
- clk_data);
-
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs),
- clk_data);
-
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes),
- base, &mt7622_clk_lock, clk_data);
-
- mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs),
- base, &mt7622_clk_lock, clk_data);
-
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
- clk_data);
-
- clk_prepare_enable(clk_data->hws[CLK_TOP_AXI_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_MEM_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_DDRPHYCFG_SEL]->clk);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
-
-static int mtk_infrasys_init(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
-
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
-
- mtk_clk_register_cpumuxes(node, infra_muxes, ARRAY_SIZE(infra_muxes),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
- clk_data);
- if (r)
- return r;
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[0]);
-
- return 0;
-}
-
-static int mtk_apmixedsys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls),
- clk_data);
-
- mtk_clk_register_gates(node, apmixed_clks,
- ARRAY_SIZE(apmixed_clks), clk_data);
-
- clk_prepare_enable(clk_data->hws[CLK_APMIXED_ARMPLL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_APMIXED_MAIN_CORE_EN]->clk);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
-
-static int mtk_pericfg_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- int r;
- struct device_node *node = pdev->dev.of_node;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
-
- mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
- clk_data);
-
- mtk_clk_register_composites(peri_muxes, ARRAY_SIZE(peri_muxes), base,
- &mt7622_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- return r;
-
- clk_prepare_enable(clk_data->hws[CLK_PERI_UART0_PD]->clk);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[1]);
+static const struct mtk_clk_desc topck_desc = {
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .divider_clks = top_adj_divs,
+ .num_divider_clks = ARRAY_SIZE(top_adj_divs),
+ .clk_lock = &mt7622_clk_lock,
+};
- return 0;
-}
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_clks,
+ .num_clks = ARRAY_SIZE(peri_clks),
+ .composite_clks = peri_muxes,
+ .num_composite_clks = ARRAY_SIZE(peri_muxes),
+ .rst_desc = &clk_rst_desc,
+ .clk_lock = &mt7622_clk_lock,
+};
static const struct of_device_id of_match_clk_mt7622[] = {
- {
- .compatible = "mediatek,mt7622-apmixedsys",
- .data = mtk_apmixedsys_init,
- }, {
- .compatible = "mediatek,mt7622-infracfg",
- .data = mtk_infrasys_init,
- }, {
- .compatible = "mediatek,mt7622-topckgen",
- .data = mtk_topckgen_init,
- }, {
- .compatible = "mediatek,mt7622-pericfg",
- .data = mtk_pericfg_init,
- }, {
- /* sentinel */
- }
-};
-
-static int clk_mt7622_probe(struct platform_device *pdev)
-{
- int (*clk_init)(struct platform_device *);
- int r;
-
- clk_init = of_device_get_match_data(&pdev->dev);
- if (!clk_init)
- return -EINVAL;
-
- r = clk_init(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+ { .compatible = "mediatek,mt7622-topckgen", .data = &topck_desc },
+ { .compatible = "mediatek,mt7622-pericfg", .data = &peri_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7622);
static struct platform_driver clk_mt7622_drv = {
- .probe = clk_mt7622_probe,
.driver = {
.name = "clk-mt7622",
.of_match_table = of_match_clk_mt7622,
},
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
};
+module_platform_driver(clk_mt7622_drv)
-static int clk_mt7622_init(void)
-{
- return platform_driver_register(&clk_mt7622_drv);
-}
-
-arch_initcall(clk_mt7622_init);
+MODULE_DESCRIPTION("MediaTek MT7622 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7629-eth.c b/drivers/clk/mediatek/clk-mt7629-eth.c
index 282dd6559465..29ea93abaac5 100644
--- a/drivers/clk/mediatek/clk-mt7629-eth.c
+++ b/drivers/clk/mediatek/clk-mt7629-eth.c
@@ -7,8 +7,6 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -16,14 +14,8 @@
#include <dt-bindings/clock/mt7629-clk.h>
-#define GATE_ETH(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &eth_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_ETH(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &eth_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate_regs eth_cg_regs = {
.set_ofs = 0x30,
@@ -45,14 +37,8 @@ static const struct mtk_gate_regs sgmii_cg_regs = {
.sta_ofs = 0xE4,
};
-#define GATE_SGMII(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &sgmii_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_SGMII(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &sgmii_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate sgmii_clks[2][4] = {
{
@@ -91,8 +77,11 @@ static int clk_mt7629_ethsys_init(struct platform_device *pdev)
int r;
clk_data = mtk_alloc_clk_data(CLK_ETH_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
- mtk_clk_register_gates(node, eth_clks, CLK_ETH_NR_CLK, clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, eth_clks,
+ CLK_ETH_NR_CLK, clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
if (r)
@@ -113,9 +102,11 @@ static int clk_mt7629_sgmiisys_init(struct platform_device *pdev)
int r;
clk_data = mtk_alloc_clk_data(CLK_SGMII_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
- mtk_clk_register_gates(node, sgmii_clks[id++], CLK_SGMII_NR_CLK,
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, sgmii_clks[id++],
+ CLK_SGMII_NR_CLK, clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
if (r)
@@ -137,6 +128,7 @@ static const struct of_device_id of_match_clk_mt7629_eth[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7629_eth);
static int clk_mt7629_eth_probe(struct platform_device *pdev)
{
@@ -165,3 +157,6 @@ static struct platform_driver clk_mt7629_eth_drv = {
};
builtin_platform_driver(clk_mt7629_eth_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7629 Ethernet clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7629-hif.c b/drivers/clk/mediatek/clk-mt7629-hif.c
index 0c8b9e139789..3fdc2d7d4274 100644
--- a/drivers/clk/mediatek/clk-mt7629-hif.c
+++ b/drivers/clk/mediatek/clk-mt7629-hif.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -16,23 +14,11 @@
#include <dt-bindings/clock/mt7629-clk.h>
-#define GATE_PCIE(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &pcie_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
-
-#define GATE_SSUSB(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &ssusb_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_PCIE(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &pcie_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_SSUSB(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ssusb_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
static const struct mtk_gate_regs pcie_cg_regs = {
.set_ofs = 0x30,
@@ -79,86 +65,34 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(rst_ofs),
};
-static int clk_mt7629_ssusbsys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_SSUSB_NR_CLK);
-
- mtk_clk_register_gates(node, ssusb_clks, ARRAY_SIZE(ssusb_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return r;
-}
-
-static int clk_mt7629_pciesys_init(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_PCIE_NR_CLK);
-
- mtk_clk_register_gates(node, pcie_clks, ARRAY_SIZE(pcie_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
+static const struct mtk_clk_desc ssusb_desc = {
+ .clks = ssusb_clks,
+ .num_clks = ARRAY_SIZE(ssusb_clks),
+ .rst_desc = &clk_rst_desc,
+};
- return r;
-}
+static const struct mtk_clk_desc pcie_desc = {
+ .clks = pcie_clks,
+ .num_clks = ARRAY_SIZE(pcie_clks),
+ .rst_desc = &clk_rst_desc,
+};
static const struct of_device_id of_match_clk_mt7629_hif[] = {
- {
- .compatible = "mediatek,mt7629-pciesys",
- .data = clk_mt7629_pciesys_init,
- }, {
- .compatible = "mediatek,mt7629-ssusbsys",
- .data = clk_mt7629_ssusbsys_init,
- }, {
- /* sentinel */
- }
+ { .compatible = "mediatek,mt7629-pciesys", .data = &pcie_desc },
+ { .compatible = "mediatek,mt7629-ssusbsys", .data = &ssusb_desc },
+ { /* sentinel */ }
};
-
-static int clk_mt7629_hif_probe(struct platform_device *pdev)
-{
- int (*clk_init)(struct platform_device *);
- int r;
-
- clk_init = of_device_get_match_data(&pdev->dev);
- if (!clk_init)
- return -EINVAL;
-
- r = clk_init(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7629_hif);
static struct platform_driver clk_mt7629_hif_drv = {
- .probe = clk_mt7629_hif_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt7629-hif",
.of_match_table = of_match_clk_mt7629_hif,
},
};
+module_platform_driver(clk_mt7629_hif_drv);
-builtin_platform_driver(clk_mt7629_hif_drv);
+MODULE_DESCRIPTION("MediaTek MT2701 HIF clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7629.c b/drivers/clk/mediatek/clk-mt7629.c
index e4a08c811adc..baf94e7bea37 100644
--- a/drivers/clk/mediatek/clk-mt7629.c
+++ b/drivers/clk/mediatek/clk-mt7629.c
@@ -8,8 +8,6 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "clk-cpumux.h"
@@ -50,41 +48,17 @@
_pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
NULL, "clk20m")
-#define GATE_APMIXED(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &apmixed_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_APMIXED(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &apmixed_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
-#define GATE_INFRA(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_INFRA(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_PERI0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_PERI0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_PERI1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_PERI1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static DEFINE_SPINLOCK(mt7629_clk_lock);
@@ -581,6 +555,8 @@ static int mtk_topckgen_init(struct platform_device *pdev)
return PTR_ERR(base);
clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
clk_data);
@@ -588,8 +564,9 @@ static int mtk_topckgen_init(struct platform_device *pdev)
mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs),
clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes),
- base, &mt7629_clk_lock, clk_data);
+ mtk_clk_register_composites(&pdev->dev, top_muxes,
+ ARRAY_SIZE(top_muxes), base,
+ &mt7629_clk_lock, clk_data);
clk_prepare_enable(clk_data->hws[CLK_TOP_AXI_SEL]->clk);
clk_prepare_enable(clk_data->hws[CLK_TOP_MEM_SEL]->clk);
@@ -604,12 +581,14 @@ static int mtk_infrasys_init(struct platform_device *pdev)
struct clk_hw_onecell_data *clk_data;
clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, infra_clks,
+ ARRAY_SIZE(infra_clks), clk_data);
- mtk_clk_register_cpumuxes(node, infra_muxes, ARRAY_SIZE(infra_muxes),
- clk_data);
+ mtk_clk_register_cpumuxes(&pdev->dev, node, infra_muxes,
+ ARRAY_SIZE(infra_muxes), clk_data);
return of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
clk_data);
@@ -627,11 +606,14 @@ static int mtk_pericfg_init(struct platform_device *pdev)
return PTR_ERR(base);
clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
- mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
- clk_data);
+ mtk_clk_register_gates(&pdev->dev, node, peri_clks,
+ ARRAY_SIZE(peri_clks), clk_data);
- mtk_clk_register_composites(peri_muxes, ARRAY_SIZE(peri_muxes), base,
+ mtk_clk_register_composites(&pdev->dev, peri_muxes,
+ ARRAY_SIZE(peri_muxes), base,
&mt7629_clk_lock, clk_data);
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
@@ -655,7 +637,7 @@ static int mtk_apmixedsys_init(struct platform_device *pdev)
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls),
clk_data);
- mtk_clk_register_gates(node, apmixed_clks,
+ mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
ARRAY_SIZE(apmixed_clks), clk_data);
clk_prepare_enable(clk_data->hws[CLK_APMIXED_ARMPLL]->clk);
@@ -682,6 +664,7 @@ static const struct of_device_id of_match_clk_mt7629[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7629);
static int clk_mt7629_probe(struct platform_device *pdev)
{
@@ -715,3 +698,6 @@ static int clk_mt7629_init(void)
}
arch_initcall(clk_mt7629_init);
+
+MODULE_DESCRIPTION("MediaTek MT7629 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7981-apmixed.c b/drivers/clk/mediatek/clk-mt7981-apmixed.c
new file mode 100644
index 000000000000..e8211eb4e09e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7981-apmixed.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Wenzhen Yu <wenzhen.yu@mediatek.com>
+ * Author: Jianhui Zhao <zhaojh329@gmail.com>
+ * Author: Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-mux.h"
+#include "clk-pll.h"
+
+#include <dt-bindings/clock/mediatek,mt7981-clk.h>
+#include <linux/clk.h>
+
+#define MT7981_PLL_FMAX (2500UL * MHZ)
+#define CON0_MT7981_RST_BAR BIT(27)
+
+#define PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
+ _div_table, _parent_name) \
+ { \
+ .id = _id, .name = _name, .reg = _reg, .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, .flags = _flags, \
+ .rst_bar_mask = CON0_MT7981_RST_BAR, .fmax = MT7981_PLL_FMAX, \
+ .pcwbits = _pcwbits, .pd_reg = _pd_reg, .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, .div_table = _div_table, \
+ .parent_name = _parent_name, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, \
+ _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) \
+ PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, NULL, \
+ "clkxtal")
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x00000001, PLL_AO,
+ 32, 0x0200, 4, 0, 0x0204, 0),
+ PLL(CLK_APMIXED_NET2PLL, "net2pll", 0x0210, 0x021C, 0x00000001, 0, 32,
+ 0x0210, 4, 0, 0x0214, 0),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0220, 0x022C, 0x00000001, 0, 32,
+ 0x0220, 4, 0, 0x0224, 0),
+ PLL(CLK_APMIXED_SGMPLL, "sgmpll", 0x0230, 0x023C, 0x00000001, 0, 32,
+ 0x0230, 4, 0, 0x0234, 0),
+ PLL(CLK_APMIXED_WEDMCUPLL, "wedmcupll", 0x0240, 0x024C, 0x00000001, 0, 32,
+ 0x0240, 4, 0, 0x0244, 0),
+ PLL(CLK_APMIXED_NET1PLL, "net1pll", 0x0250, 0x025C, 0x00000001, 0, 32,
+ 0x0250, 4, 0, 0x0254, 0),
+ PLL(CLK_APMIXED_MPLL, "mpll", 0x0260, 0x0270, 0x00000001, 0, 32,
+ 0x0260, 4, 0, 0x0264, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x0278, 0x0288, 0x00000001, 0, 32,
+ 0x0278, 4, 0, 0x027C, 0),
+};
+
+static const struct of_device_id of_match_clk_mt7981_apmixed[] = {
+ { .compatible = "mediatek,mt7981-apmixedsys", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7981_apmixed);
+
+static int clk_mt7981_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ int r;
+
+ clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls));
+ if (!clk_data)
+ return -ENOMEM;
+
+ mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r) {
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+ goto free_apmixed_data;
+ }
+ return r;
+
+free_apmixed_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static struct platform_driver clk_mt7981_apmixed_drv = {
+ .probe = clk_mt7981_apmixed_probe,
+ .driver = {
+ .name = "clk-mt7981-apmixed",
+ .of_match_table = of_match_clk_mt7981_apmixed,
+ },
+};
+builtin_platform_driver(clk_mt7981_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7981 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7981-eth.c b/drivers/clk/mediatek/clk-mt7981-eth.c
new file mode 100644
index 000000000000..906aec9ddff5
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7981-eth.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Wenzhen Yu <wenzhen.yu@mediatek.com>
+ * Author: Jianhui Zhao <zhaojh329@gmail.com>
+ * Author: Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mediatek,mt7981-clk.h>
+
+static const struct mtk_gate_regs sgmii0_cg_regs = {
+ .set_ofs = 0xE4,
+ .clr_ofs = 0xE4,
+ .sta_ofs = 0xE4,
+};
+
+#define GATE_SGMII0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &sgmii0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate sgmii0_clks[] __initconst = {
+ GATE_SGMII0(CLK_SGM0_TX_EN, "sgm0_tx_en", "usb_tx250m", 2),
+ GATE_SGMII0(CLK_SGM0_RX_EN, "sgm0_rx_en", "usb_eq_rx250m", 3),
+ GATE_SGMII0(CLK_SGM0_CK0_EN, "sgm0_ck0_en", "usb_ln0", 4),
+ GATE_SGMII0(CLK_SGM0_CDR_CK0_EN, "sgm0_cdr_ck0_en", "usb_cdr", 5),
+};
+
+static const struct mtk_gate_regs sgmii1_cg_regs = {
+ .set_ofs = 0xE4,
+ .clr_ofs = 0xE4,
+ .sta_ofs = 0xE4,
+};
+
+#define GATE_SGMII1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &sgmii1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate sgmii1_clks[] __initconst = {
+ GATE_SGMII1(CLK_SGM1_TX_EN, "sgm1_tx_en", "usb_tx250m", 2),
+ GATE_SGMII1(CLK_SGM1_RX_EN, "sgm1_rx_en", "usb_eq_rx250m", 3),
+ GATE_SGMII1(CLK_SGM1_CK1_EN, "sgm1_ck1_en", "usb_ln0", 4),
+ GATE_SGMII1(CLK_SGM1_CDR_CK1_EN, "sgm1_cdr_ck1_en", "usb_cdr", 5),
+};
+
+static const struct mtk_gate_regs eth_cg_regs = {
+ .set_ofs = 0x30,
+ .clr_ofs = 0x30,
+ .sta_ofs = 0x30,
+};
+
+#define GATE_ETH(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &eth_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate eth_clks[] __initconst = {
+ GATE_ETH(CLK_ETH_FE_EN, "eth_fe_en", "netsys_2x", 6),
+ GATE_ETH(CLK_ETH_GP2_EN, "eth_gp2_en", "sgm_325m", 7),
+ GATE_ETH(CLK_ETH_GP1_EN, "eth_gp1_en", "sgm_325m", 8),
+ GATE_ETH(CLK_ETH_WOCPU0_EN, "eth_wocpu0_en", "netsys_wed_mcu", 15),
+};
+
+static const struct mtk_clk_desc eth_desc = {
+ .clks = eth_clks,
+ .num_clks = ARRAY_SIZE(eth_clks),
+};
+
+static const struct mtk_clk_desc sgmii0_desc = {
+ .clks = sgmii0_clks,
+ .num_clks = ARRAY_SIZE(sgmii0_clks),
+};
+
+static const struct mtk_clk_desc sgmii1_desc = {
+ .clks = sgmii1_clks,
+ .num_clks = ARRAY_SIZE(sgmii1_clks),
+};
+
+static const struct of_device_id of_match_clk_mt7981_eth[] = {
+ { .compatible = "mediatek,mt7981-ethsys", .data = &eth_desc },
+ { .compatible = "mediatek,mt7981-sgmiisys_0", .data = &sgmii0_desc },
+ { .compatible = "mediatek,mt7981-sgmiisys_1", .data = &sgmii1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7981_eth);
+
+static struct platform_driver clk_mt7981_eth_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt7981-eth",
+ .of_match_table = of_match_clk_mt7981_eth,
+ },
+};
+module_platform_driver(clk_mt7981_eth_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7981 Ethernet clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7981-infracfg.c b/drivers/clk/mediatek/clk-mt7981-infracfg.c
new file mode 100644
index 000000000000..0487b6bb80ae
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7981-infracfg.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Wenzhen Yu <wenzhen.yu@mediatek.com>
+ * Author: Jianhui Zhao <zhaojh329@gmail.com>
+ * Author: Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "clk-mux.h"
+
+#include <dt-bindings/clock/mediatek,mt7981-clk.h>
+#include <linux/clk.h>
+
+static DEFINE_SPINLOCK(mt7981_clk_lock);
+
+static const struct mtk_fixed_factor infra_divs[] = {
+ FACTOR(CLK_INFRA_66M_MCK, "infra_66m_mck", "sysaxi_sel", 1, 2),
+};
+
+static const char *const infra_uart_parent[] __initconst = { "csw_f26m_sel",
+ "uart_sel" };
+
+static const char *const infra_spi0_parents[] __initconst = { "i2c_sel",
+ "spi_sel" };
+
+static const char *const infra_spi1_parents[] __initconst = { "i2c_sel",
+ "spim_mst_sel" };
+
+static const char *const infra_pwm1_parents[] __initconst = { "pwm_sel" };
+
+static const char *const infra_pwm_bsel_parents[] __initconst = {
+ "cb_rtc_32p7k", "csw_f26m_sel", "infra_66m_mck", "pwm_sel"
+};
+
+static const char *const infra_pcie_parents[] __initconst = {
+ "cb_rtc_32p7k", "csw_f26m_sel", "cb_cksq_40m", "pextp_tl_ck_sel"
+};
+
+static const struct mtk_mux infra_muxes[] = {
+ /* MODULE_CLK_SEL_0 */
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_UART0_SEL, "infra_uart0_sel",
+ infra_uart_parent, 0x0018, 0x0010, 0x0014, 0, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_UART1_SEL, "infra_uart1_sel",
+ infra_uart_parent, 0x0018, 0x0010, 0x0014, 1, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_UART2_SEL, "infra_uart2_sel",
+ infra_uart_parent, 0x0018, 0x0010, 0x0014, 2, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_SPI0_SEL, "infra_spi0_sel",
+ infra_spi0_parents, 0x0018, 0x0010, 0x0014, 4, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_SPI1_SEL, "infra_spi1_sel",
+ infra_spi1_parents, 0x0018, 0x0010, 0x0014, 5, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_SPI2_SEL, "infra_spi2_sel",
+ infra_spi0_parents, 0x0018, 0x0010, 0x0014, 6, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM1_SEL, "infra_pwm1_sel",
+ infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 9, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM2_SEL, "infra_pwm2_sel",
+ infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 11, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM3_SEL, "infra_pwm3_sel",
+ infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 15, 1,
+ -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_BSEL, "infra_pwm_bsel",
+ infra_pwm_bsel_parents, 0x0018, 0x0010, 0x0014, 13,
+ 2, -1, -1, -1),
+ /* MODULE_CLK_SEL_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_SEL, "infra_pcie_sel",
+ infra_pcie_parents, 0x0028, 0x0020, 0x0024, 0, 2,
+ -1, -1, -1),
+};
+
+static const struct mtk_gate_regs infra0_cg_regs = {
+ .set_ofs = 0x40,
+ .clr_ofs = 0x44,
+ .sta_ofs = 0x48,
+};
+
+static const struct mtk_gate_regs infra1_cg_regs = {
+ .set_ofs = 0x50,
+ .clr_ofs = 0x54,
+ .sta_ofs = 0x58,
+};
+
+static const struct mtk_gate_regs infra2_cg_regs = {
+ .set_ofs = 0x60,
+ .clr_ofs = 0x64,
+ .sta_ofs = 0x68,
+};
+
+#define GATE_INFRA0(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, .name = _name, .parent_name = _parent, \
+ .regs = &infra0_cg_regs, .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_INFRA1(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, .name = _name, .parent_name = _parent, \
+ .regs = &infra1_cg_regs, .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_INFRA2(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, .name = _name, .parent_name = _parent, \
+ .regs = &infra2_cg_regs, .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate infra_clks[] = {
+ /* INFRA0 */
+ GATE_INFRA0(CLK_INFRA_GPT_STA, "infra_gpt_sta", "infra_66m_mck", 0),
+ GATE_INFRA0(CLK_INFRA_PWM_HCK, "infra_pwm_hck", "infra_66m_mck", 1),
+ GATE_INFRA0(CLK_INFRA_PWM_STA, "infra_pwm_sta", "infra_pwm_bsel", 2),
+ GATE_INFRA0(CLK_INFRA_PWM1_CK, "infra_pwm1", "infra_pwm1_sel", 3),
+ GATE_INFRA0(CLK_INFRA_PWM2_CK, "infra_pwm2", "infra_pwm2_sel", 4),
+ GATE_INFRA0(CLK_INFRA_CQ_DMA_CK, "infra_cq_dma", "sysaxi", 6),
+
+ GATE_INFRA0(CLK_INFRA_AUD_BUS_CK, "infra_aud_bus", "sysaxi", 8),
+ GATE_INFRA0(CLK_INFRA_AUD_26M_CK, "infra_aud_26m", "csw_f26m_sel", 9),
+ GATE_INFRA0(CLK_INFRA_AUD_L_CK, "infra_aud_l", "aud_l", 10),
+ GATE_INFRA0(CLK_INFRA_AUD_AUD_CK, "infra_aud_aud", "a1sys", 11),
+ GATE_INFRA0(CLK_INFRA_AUD_EG2_CK, "infra_aud_eg2", "a_tuner", 13),
+ GATE_INFRA0(CLK_INFRA_DRAMC_26M_CK, "infra_dramc_26m", "csw_f26m_sel",
+ 14),
+ GATE_INFRA0(CLK_INFRA_DBG_CK, "infra_dbg", "infra_66m_mck", 15),
+ GATE_INFRA0(CLK_INFRA_AP_DMA_CK, "infra_ap_dma", "infra_66m_mck", 16),
+ GATE_INFRA0(CLK_INFRA_SEJ_CK, "infra_sej", "infra_66m_mck", 24),
+ GATE_INFRA0(CLK_INFRA_SEJ_13M_CK, "infra_sej_13m", "csw_f26m_sel", 25),
+ GATE_INFRA0(CLK_INFRA_PWM3_CK, "infra_pwm3", "infra_pwm3_sel", 27),
+ /* INFRA1 */
+ GATE_INFRA1(CLK_INFRA_THERM_CK, "infra_therm", "csw_f26m_sel", 0),
+ GATE_INFRA1(CLK_INFRA_I2C0_CK, "infra_i2c0", "i2c_bck", 1),
+ GATE_INFRA1(CLK_INFRA_UART0_CK, "infra_uart0", "infra_uart0_sel", 2),
+ GATE_INFRA1(CLK_INFRA_UART1_CK, "infra_uart1", "infra_uart1_sel", 3),
+ GATE_INFRA1(CLK_INFRA_UART2_CK, "infra_uart2", "infra_uart2_sel", 4),
+ GATE_INFRA1(CLK_INFRA_SPI2_CK, "infra_spi2", "infra_spi2_sel", 6),
+ GATE_INFRA1(CLK_INFRA_SPI2_HCK_CK, "infra_spi2_hck", "infra_66m_mck", 7),
+ GATE_INFRA1(CLK_INFRA_NFI1_CK, "infra_nfi1", "nfi1x", 8),
+ GATE_INFRA1(CLK_INFRA_SPINFI1_CK, "infra_spinfi1", "spinfi_bck", 9),
+ GATE_INFRA1(CLK_INFRA_NFI_HCK_CK, "infra_nfi_hck", "infra_66m_mck", 10),
+ GATE_INFRA1(CLK_INFRA_SPI0_CK, "infra_spi0", "infra_spi0_sel", 11),
+ GATE_INFRA1(CLK_INFRA_SPI1_CK, "infra_spi1", "infra_spi1_sel", 12),
+ GATE_INFRA1(CLK_INFRA_SPI0_HCK_CK, "infra_spi0_hck", "infra_66m_mck",
+ 13),
+ GATE_INFRA1(CLK_INFRA_SPI1_HCK_CK, "infra_spi1_hck", "infra_66m_mck",
+ 14),
+ GATE_INFRA1(CLK_INFRA_FRTC_CK, "infra_frtc", "cb_rtc_32k", 15),
+ GATE_INFRA1(CLK_INFRA_MSDC_CK, "infra_msdc", "emmc_400m", 16),
+ GATE_INFRA1(CLK_INFRA_MSDC_HCK_CK, "infra_msdc_hck", "emmc_208m", 17),
+ GATE_INFRA1(CLK_INFRA_MSDC_133M_CK, "infra_msdc_133m", "sysaxi", 18),
+ GATE_INFRA1(CLK_INFRA_MSDC_66M_CK, "infra_msdc_66m", "sysaxi", 19),
+ GATE_INFRA1(CLK_INFRA_ADC_26M_CK, "infra_adc_26m", "infra_adc_frc", 20),
+ GATE_INFRA1(CLK_INFRA_ADC_FRC_CK, "infra_adc_frc", "csw_f26m", 21),
+ GATE_INFRA1(CLK_INFRA_FBIST2FPC_CK, "infra_fbist2fpc", "nfi1x", 23),
+ GATE_INFRA1(CLK_INFRA_I2C_MCK_CK, "infra_i2c_mck", "sysaxi", 25),
+ GATE_INFRA1(CLK_INFRA_I2C_PCK_CK, "infra_i2c_pck", "infra_66m_mck", 26),
+ /* INFRA2 */
+ GATE_INFRA2(CLK_INFRA_IUSB_133_CK, "infra_iusb_133", "sysaxi", 0),
+ GATE_INFRA2(CLK_INFRA_IUSB_66M_CK, "infra_iusb_66m", "sysaxi", 1),
+ GATE_INFRA2(CLK_INFRA_IUSB_SYS_CK, "infra_iusb_sys", "u2u3_sys", 2),
+ GATE_INFRA2(CLK_INFRA_IUSB_CK, "infra_iusb", "u2u3_ref", 3),
+ GATE_INFRA2(CLK_INFRA_IPCIE_CK, "infra_ipcie", "pextp_tl", 12),
+ GATE_INFRA2(CLK_INFRA_IPCIE_PIPE_CK, "infra_ipcie_pipe", "cb_cksq_40m",
+ 13),
+ GATE_INFRA2(CLK_INFRA_IPCIER_CK, "infra_ipcier", "csw_f26m", 14),
+ GATE_INFRA2(CLK_INFRA_IPCIEB_CK, "infra_ipcieb", "sysaxi", 15),
+};
+
+static const struct mtk_clk_desc infracfg_desc = {
+ .factor_clks = infra_divs,
+ .num_factor_clks = ARRAY_SIZE(infra_divs),
+ .mux_clks = infra_muxes,
+ .num_mux_clks = ARRAY_SIZE(infra_muxes),
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .clk_lock = &mt7981_clk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt7981_infracfg[] = {
+ { .compatible = "mediatek,mt7981-infracfg", .data = &infracfg_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7981_infracfg);
+
+static struct platform_driver clk_mt7981_infracfg_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt7981-infracfg",
+ .of_match_table = of_match_clk_mt7981_infracfg,
+ },
+};
+module_platform_driver(clk_mt7981_infracfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7981 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7981-topckgen.c b/drivers/clk/mediatek/clk-mt7981-topckgen.c
new file mode 100644
index 000000000000..1943f11e47c1
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7981-topckgen.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Wenzhen Yu <wenzhen.yu@mediatek.com>
+ * Author: Jianhui Zhao <zhaojh329@gmail.com>
+ */
+
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "clk-mux.h"
+
+#include <dt-bindings/clock/mediatek,mt7981-clk.h>
+#include <linux/clk.h>
+
+static DEFINE_SPINLOCK(mt7981_clk_lock);
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_CB_CKSQ_40M, "cb_cksq_40m", "clkxtal", 1, 1),
+ FACTOR(CLK_TOP_CB_M_416M, "cb_m_416m", "mpll", 1, 1),
+ FACTOR(CLK_TOP_CB_M_D2, "cb_m_d2", "mpll", 1, 2),
+ FACTOR(CLK_TOP_CB_M_D3, "cb_m_d3", "mpll", 1, 3),
+ FACTOR(CLK_TOP_M_D3_D2, "m_d3_d2", "mpll", 1, 2),
+ FACTOR(CLK_TOP_CB_M_D4, "cb_m_d4", "mpll", 1, 4),
+ FACTOR(CLK_TOP_CB_M_D8, "cb_m_d8", "mpll", 1, 8),
+ FACTOR(CLK_TOP_M_D8_D2, "m_d8_d2", "mpll", 1, 16),
+ FACTOR(CLK_TOP_CB_MM_720M, "cb_mm_720m", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_CB_MM_D2, "cb_mm_d2", "mmpll", 1, 2),
+ FACTOR(CLK_TOP_CB_MM_D3, "cb_mm_d3", "mmpll", 1, 3),
+ FACTOR(CLK_TOP_CB_MM_D3_D5, "cb_mm_d3_d5", "mmpll", 1, 15),
+ FACTOR(CLK_TOP_CB_MM_D4, "cb_mm_d4", "mmpll", 1, 4),
+ FACTOR(CLK_TOP_CB_MM_D6, "cb_mm_d6", "mmpll", 1, 6),
+ FACTOR(CLK_TOP_MM_D6_D2, "mm_d6_d2", "mmpll", 1, 12),
+ FACTOR(CLK_TOP_CB_MM_D8, "cb_mm_d8", "mmpll", 1, 8),
+ FACTOR(CLK_TOP_CB_APLL2_196M, "cb_apll2_196m", "apll2", 1, 1),
+ FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2", 1, 2),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4),
+ FACTOR(CLK_TOP_NET1_2500M, "net1_2500m", "net1pll", 1, 1),
+ FACTOR(CLK_TOP_CB_NET1_D4, "cb_net1_d4", "net1pll", 1, 4),
+ FACTOR(CLK_TOP_CB_NET1_D5, "cb_net1_d5", "net1pll", 1, 5),
+ FACTOR(CLK_TOP_NET1_D5_D2, "net1_d5_d2", "net1pll", 1, 10),
+ FACTOR(CLK_TOP_NET1_D5_D4, "net1_d5_d4", "net1pll", 1, 20),
+ FACTOR(CLK_TOP_CB_NET1_D8, "cb_net1_d8", "net1pll", 1, 8),
+ FACTOR(CLK_TOP_NET1_D8_D2, "net1_d8_d2", "net1pll", 1, 16),
+ FACTOR(CLK_TOP_NET1_D8_D4, "net1_d8_d4", "net1pll", 1, 32),
+ FACTOR(CLK_TOP_CB_NET2_800M, "cb_net2_800m", "net2pll", 1, 1),
+ FACTOR(CLK_TOP_CB_NET2_D2, "cb_net2_d2", "net2pll", 1, 2),
+ FACTOR(CLK_TOP_CB_NET2_D4, "cb_net2_d4", "net2pll", 1, 4),
+ FACTOR(CLK_TOP_NET2_D4_D2, "net2_d4_d2", "net2pll", 1, 8),
+ FACTOR(CLK_TOP_NET2_D4_D4, "net2_d4_d4", "net2pll", 1, 16),
+ FACTOR(CLK_TOP_CB_NET2_D6, "cb_net2_d6", "net2pll", 1, 6),
+ FACTOR(CLK_TOP_CB_WEDMCU_208M, "cb_wedmcu_208m", "wedmcupll", 1, 1),
+ FACTOR(CLK_TOP_CB_SGM_325M, "cb_sgm_325m", "sgmpll", 1, 1),
+ FACTOR(CLK_TOP_CKSQ_40M_D2, "cksq_40m_d2", "cb_cksq_40m", 1, 2),
+ FACTOR(CLK_TOP_CB_RTC_32K, "cb_rtc_32k", "cb_cksq_40m", 1, 1250),
+ FACTOR(CLK_TOP_CB_RTC_32P7K, "cb_rtc_32p7k", "cb_cksq_40m", 1, 1220),
+ FACTOR(CLK_TOP_USB_TX250M, "usb_tx250m", "cb_cksq_40m", 1, 1),
+ FACTOR(CLK_TOP_FAUD, "faud", "aud_sel", 1, 1),
+ FACTOR(CLK_TOP_NFI1X, "nfi1x", "nfi1x_sel", 1, 1),
+ FACTOR(CLK_TOP_USB_EQ_RX250M, "usb_eq_rx250m", "cb_cksq_40m", 1, 1),
+ FACTOR(CLK_TOP_USB_CDR_CK, "usb_cdr", "cb_cksq_40m", 1, 1),
+ FACTOR(CLK_TOP_USB_LN0_CK, "usb_ln0", "cb_cksq_40m", 1, 1),
+ FACTOR(CLK_TOP_SPINFI_BCK, "spinfi_bck", "spinfi_sel", 1, 1),
+ FACTOR(CLK_TOP_SPI, "spi", "spi_sel", 1, 1),
+ FACTOR(CLK_TOP_SPIM_MST, "spim_mst", "spim_mst_sel", 1, 1),
+ FACTOR(CLK_TOP_UART_BCK, "uart_bck", "uart_sel", 1, 1),
+ FACTOR(CLK_TOP_PWM_BCK, "pwm_bck", "pwm_sel", 1, 1),
+ FACTOR(CLK_TOP_I2C_BCK, "i2c_bck", "i2c_sel", 1, 1),
+ FACTOR(CLK_TOP_PEXTP_TL, "pextp_tl", "pextp_tl_ck_sel", 1, 1),
+ FACTOR(CLK_TOP_EMMC_208M, "emmc_208m", "emmc_208m_sel", 1, 1),
+ FACTOR(CLK_TOP_EMMC_400M, "emmc_400m", "emmc_400m_sel", 1, 1),
+ FACTOR(CLK_TOP_DRAMC_REF, "dramc_ref", "dramc_sel", 1, 1),
+ FACTOR(CLK_TOP_DRAMC_MD32, "dramc_md32", "dramc_md32_sel", 1, 1),
+ FACTOR(CLK_TOP_SYSAXI, "sysaxi", "sysaxi_sel", 1, 1),
+ FACTOR(CLK_TOP_SYSAPB, "sysapb", "sysapb_sel", 1, 1),
+ FACTOR(CLK_TOP_ARM_DB_MAIN, "arm_db_main", "arm_db_main_sel", 1, 1),
+ FACTOR(CLK_TOP_AP2CNN_HOST, "ap2cnn_host", "ap2cnn_host_sel", 1, 1),
+ FACTOR(CLK_TOP_NETSYS, "netsys", "netsys_sel", 1, 1),
+ FACTOR(CLK_TOP_NETSYS_500M, "netsys_500m", "netsys_500m_sel", 1, 1),
+ FACTOR(CLK_TOP_NETSYS_WED_MCU, "netsys_wed_mcu", "netsys_mcu_sel", 1, 1),
+ FACTOR(CLK_TOP_NETSYS_2X, "netsys_2x", "netsys_2x_sel", 1, 1),
+ FACTOR(CLK_TOP_SGM_325M, "sgm_325m", "sgm_325m_sel", 1, 1),
+ FACTOR(CLK_TOP_SGM_REG, "sgm_reg", "sgm_reg_sel", 1, 1),
+ FACTOR(CLK_TOP_F26M, "csw_f26m", "csw_f26m_sel", 1, 1),
+ FACTOR(CLK_TOP_EIP97B, "eip97b", "eip97b_sel", 1, 1),
+ FACTOR(CLK_TOP_USB3_PHY, "usb3_phy", "usb3_phy_sel", 1, 1),
+ FACTOR(CLK_TOP_AUD, "aud", "faud", 1, 1),
+ FACTOR(CLK_TOP_A1SYS, "a1sys", "a1sys_sel", 1, 1),
+ FACTOR(CLK_TOP_AUD_L, "aud_l", "aud_l_sel", 1, 1),
+ FACTOR(CLK_TOP_A_TUNER, "a_tuner", "a_tuner_sel", 1, 1),
+ FACTOR(CLK_TOP_U2U3_REF, "u2u3_ref", "u2u3_sel", 1, 1),
+ FACTOR(CLK_TOP_U2U3_SYS, "u2u3_sys", "u2u3_sys_sel", 1, 1),
+ FACTOR(CLK_TOP_U2U3_XHCI, "u2u3_xhci", "u2u3_xhci_sel", 1, 1),
+ FACTOR(CLK_TOP_USB_FRMCNT, "usb_frmcnt", "usb_frmcnt_sel", 1, 1),
+};
+
+static const char * const nfi1x_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_mm_d4",
+ "net1_d8_d2",
+ "cb_net2_d6",
+ "cb_m_d4",
+ "cb_mm_d8",
+ "net1_d8_d4",
+ "cb_m_d8"
+};
+
+static const char * const spinfi_parents[] __initconst = {
+ "cksq_40m_d2",
+ "cb_cksq_40m",
+ "net1_d5_d4",
+ "cb_m_d4",
+ "cb_mm_d8",
+ "net1_d8_d4",
+ "mm_d6_d2",
+ "cb_m_d8"
+};
+
+static const char * const spi_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_m_d2",
+ "cb_mm_d4",
+ "net1_d8_d2",
+ "cb_net2_d6",
+ "net1_d5_d4",
+ "cb_m_d4",
+ "net1_d8_d4"
+};
+
+static const char * const uart_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_m_d8",
+ "m_d8_d2"
+};
+
+static const char * const pwm_parents[] __initconst = {
+ "cb_cksq_40m",
+ "net1_d8_d2",
+ "net1_d5_d4",
+ "cb_m_d4",
+ "m_d8_d2",
+ "cb_rtc_32k"
+};
+
+static const char * const i2c_parents[] __initconst = {
+ "cb_cksq_40m",
+ "net1_d5_d4",
+ "cb_m_d4",
+ "net1_d8_d4"
+};
+
+static const char * const pextp_tl_ck_parents[] __initconst = {
+ "cb_cksq_40m",
+ "net1_d5_d4",
+ "cb_m_d4",
+ "cb_rtc_32k"
+};
+
+static const char * const emmc_208m_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_m_d2",
+ "cb_net2_d4",
+ "cb_apll2_196m",
+ "cb_mm_d4",
+ "net1_d8_d2",
+ "cb_mm_d6"
+};
+
+static const char * const emmc_400m_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_net2_d2",
+ "cb_mm_d2",
+ "cb_net2_d2"
+};
+
+static const char * const csw_f26m_parents[] __initconst = {
+ "cksq_40m_d2",
+ "m_d8_d2"
+};
+
+static const char * const dramc_md32_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_m_d2",
+ "cb_wedmcu_208m"
+};
+
+static const char * const sysaxi_parents[] __initconst = {
+ "cb_cksq_40m",
+ "net1_d8_d2"
+};
+
+static const char * const sysapb_parents[] __initconst = {
+ "cb_cksq_40m",
+ "m_d3_d2"
+};
+
+static const char * const arm_db_main_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_net2_d6"
+};
+
+static const char * const ap2cnn_host_parents[] __initconst = {
+ "cb_cksq_40m",
+ "net1_d8_d4"
+};
+
+static const char * const netsys_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_mm_d2"
+};
+
+static const char * const netsys_500m_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_net1_d5"
+};
+
+static const char * const netsys_mcu_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_mm_720m",
+ "cb_net1_d4",
+ "cb_net1_d5",
+ "cb_m_416m"
+};
+
+static const char * const netsys_2x_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_net2_800m",
+ "cb_mm_720m"
+};
+
+static const char * const sgm_325m_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_sgm_325m"
+};
+
+static const char * const sgm_reg_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_net2_d4"
+};
+
+static const char * const eip97b_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_net1_d5",
+ "cb_m_416m",
+ "cb_mm_d2",
+ "net1_d5_d2"
+};
+
+static const char * const aud_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_apll2_196m"
+};
+
+static const char * const a1sys_parents[] __initconst = {
+ "cb_cksq_40m",
+ "apll2_d4"
+};
+
+static const char * const aud_l_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_apll2_196m",
+ "m_d8_d2"
+};
+
+static const char * const a_tuner_parents[] __initconst = {
+ "cb_cksq_40m",
+ "apll2_d4",
+ "m_d8_d2"
+};
+
+static const char * const u2u3_parents[] __initconst = {
+ "cb_cksq_40m",
+ "m_d8_d2"
+};
+
+static const char * const u2u3_sys_parents[] __initconst = {
+ "cb_cksq_40m",
+ "net1_d5_d4"
+};
+
+static const char * const usb_frmcnt_parents[] __initconst = {
+ "cb_cksq_40m",
+ "cb_mm_d3_d5"
+};
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NFI1X_SEL, "nfi1x_sel", nfi1x_parents,
+ 0x000, 0x004, 0x008, 0, 3, 7, 0x1C0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPINFI_SEL, "spinfi_sel", spinfi_parents,
+ 0x000, 0x004, 0x008, 8, 3, 15, 0x1C0, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel", spi_parents,
+ 0x000, 0x004, 0x008, 16, 3, 23, 0x1C0, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPIM_MST_SEL, "spim_mst_sel", spi_parents,
+ 0x000, 0x004, 0x008, 24, 3, 31, 0x1C0, 3),
+ /* CLK_CFG_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel", uart_parents,
+ 0x010, 0x014, 0x018, 0, 2, 7, 0x1C0, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents,
+ 0x010, 0x014, 0x018, 8, 3, 15, 0x1C0, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C_SEL, "i2c_sel", i2c_parents,
+ 0x010, 0x014, 0x018, 16, 2, 23, 0x1C0, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_TL_SEL, "pextp_tl_ck_sel",
+ pextp_tl_ck_parents, 0x010, 0x014, 0x018, 24, 2, 31,
+ 0x1C0, 7),
+ /* CLK_CFG_2 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_EMMC_208M_SEL, "emmc_208m_sel",
+ emmc_208m_parents, 0x020, 0x024, 0x028, 0, 3, 7,
+ 0x1C0, 8, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_EMMC_400M_SEL, "emmc_400m_sel",
+ emmc_400m_parents, 0x020, 0x024, 0x028, 8, 2, 15,
+ 0x1C0, 9, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_F26M_SEL, "csw_f26m_sel",
+ csw_f26m_parents, 0x020, 0x024, 0x028, 16, 1, 23,
+ 0x1C0, 10,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_SEL, "dramc_sel",
+ csw_f26m_parents, 0x020, 0x024, 0x028, 24, 1,
+ 31, 0x1C0, 11,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ /* CLK_CFG_3 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel",
+ dramc_md32_parents, 0x030, 0x034, 0x038, 0, 2,
+ 7, 0x1C0, 12,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAXI_SEL, "sysaxi_sel",
+ sysaxi_parents, 0x030, 0x034, 0x038, 8, 1, 15,
+ 0x1C0, 13,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAPB_SEL, "sysapb_sel",
+ sysapb_parents, 0x030, 0x034, 0x038, 16, 1,
+ 23, 0x1C0, 14,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ARM_DB_MAIN_SEL, "arm_db_main_sel",
+ arm_db_main_parents, 0x030, 0x034, 0x038, 24, 1, 31,
+ 0x1C0, 15),
+ /* CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AP2CNN_HOST_SEL, "ap2cnn_host_sel",
+ ap2cnn_host_parents, 0x040, 0x044, 0x048, 0, 1, 7,
+ 0x1C0, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_SEL, "netsys_sel", netsys_parents,
+ 0x040, 0x044, 0x048, 8, 1, 15, 0x1C0, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_500M_SEL, "netsys_500m_sel",
+ netsys_500m_parents, 0x040, 0x044, 0x048, 16, 1, 23,
+ 0x1C0, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_MCU_SEL, "netsys_mcu_sel",
+ netsys_mcu_parents, 0x040, 0x044, 0x048, 24, 3, 31,
+ 0x1C0, 19),
+ /* CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_2X_SEL, "netsys_2x_sel",
+ netsys_2x_parents, 0x050, 0x054, 0x058, 0, 2, 7,
+ 0x1C0, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_325M_SEL, "sgm_325m_sel",
+ sgm_325m_parents, 0x050, 0x054, 0x058, 8, 1, 15,
+ 0x1C0, 21),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SGM_REG_SEL, "sgm_reg_sel", sgm_reg_parents,
+ 0x050, 0x054, 0x058, 16, 1, 23, 0x1C0, 22,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_EIP97B_SEL, "eip97b_sel", eip97b_parents,
+ 0x050, 0x054, 0x058, 24, 3, 31, 0x1C0, 23),
+ /* CLK_CFG_6 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB3_PHY_SEL, "usb3_phy_sel",
+ csw_f26m_parents, 0x060, 0x064, 0x068, 0, 1,
+ 7, 0x1C0, 24),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_SEL, "aud_sel", aud_parents, 0x060,
+ 0x064, 0x068, 8, 1, 15, 0x1C0, 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A1SYS_SEL, "a1sys_sel", a1sys_parents,
+ 0x060, 0x064, 0x068, 16, 1, 23, 0x1C0, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_L_SEL, "aud_l_sel", aud_l_parents,
+ 0x060, 0x064, 0x068, 24, 2, 31, 0x1C0, 27),
+ /* CLK_CFG_7 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A_TUNER_SEL, "a_tuner_sel",
+ a_tuner_parents, 0x070, 0x074, 0x078, 0, 2, 7,
+ 0x1C0, 28),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_U2U3_SEL, "u2u3_sel", u2u3_parents, 0x070,
+ 0x074, 0x078, 8, 1, 15, 0x1C0, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_U2U3_SYS_SEL, "u2u3_sys_sel",
+ u2u3_sys_parents, 0x070, 0x074, 0x078, 16, 1, 23,
+ 0x1C0, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_U2U3_XHCI_SEL, "u2u3_xhci_sel",
+ u2u3_sys_parents, 0x070, 0x074, 0x078, 24, 1, 31,
+ 0x1C4, 0),
+ /* CLK_CFG_8 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_FRMCNT_SEL, "usb_frmcnt_sel",
+ usb_frmcnt_parents, 0x080, 0x084, 0x088, 0, 1, 7,
+ 0x1C4, 1),
+};
+
+static struct mtk_composite top_aud_divs[] = {
+ DIV_GATE(CLK_TOP_AUD_I2S_M, "aud_i2s_m", "aud",
+ 0x0420, 0, 0x0420, 8, 8),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_aud_divs,
+ .num_composite_clks = ARRAY_SIZE(top_aud_divs),
+ .clk_lock = &mt7981_clk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt7981_topckgen[] = {
+ { .compatible = "mediatek,mt7981-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7981_topckgen);
+
+static struct platform_driver clk_mt7981_topckgen_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt7981-topckgen",
+ .of_match_table = of_match_clk_mt7981_topckgen,
+ },
+};
+module_platform_driver(clk_mt7981_topckgen_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7981 top clock generators driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7986-apmixed.c b/drivers/clk/mediatek/clk-mt7986-apmixed.c
index 62080ee4dbe3..93751abe6be8 100644
--- a/drivers/clk/mediatek/clk-mt7986-apmixed.c
+++ b/drivers/clk/mediatek/clk-mt7986-apmixed.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-gate.h"
@@ -42,7 +40,7 @@
"clkxtal")
static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x0, 0, 32,
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x0, PLL_AO, 32,
0x0200, 4, 0, 0x0204, 0),
PLL(CLK_APMIXED_NET2PLL, "net2pll", 0x0210, 0x021C, 0x0, 0, 32,
0x0210, 4, 0, 0x0214, 0),
@@ -62,8 +60,9 @@ static const struct mtk_pll_data plls[] = {
static const struct of_device_id of_match_clk_mt7986_apmixed[] = {
{ .compatible = "mediatek,mt7986-apmixedsys", },
- {}
+ { }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7986_apmixed);
static int clk_mt7986_apmixed_probe(struct platform_device *pdev)
{
@@ -77,8 +76,6 @@ static int clk_mt7986_apmixed_probe(struct platform_device *pdev)
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
- clk_prepare_enable(clk_data->hws[CLK_APMIXED_ARMPLL]->clk);
-
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
if (r) {
pr_err("%s(): could not register clock provider: %d\n",
@@ -100,3 +97,6 @@ static struct platform_driver clk_mt7986_apmixed_drv = {
},
};
builtin_platform_driver(clk_mt7986_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7986 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7986-eth.c b/drivers/clk/mediatek/clk-mt7986-eth.c
index 7868c0728e96..4514d42c0829 100644
--- a/drivers/clk/mediatek/clk-mt7986-eth.c
+++ b/drivers/clk/mediatek/clk-mt7986-eth.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -22,14 +20,10 @@ static const struct mtk_gate_regs sgmii0_cg_regs = {
.sta_ofs = 0xe4,
};
-#define GATE_SGMII0(_id, _name, _parent, _shift) \
- { \
- .id = _id, .name = _name, .parent_name = _parent, \
- .regs = &sgmii0_cg_regs, .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_SGMII0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &sgmii0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
-static const struct mtk_gate sgmii0_clks[] __initconst = {
+static const struct mtk_gate sgmii0_clks[] = {
GATE_SGMII0(CLK_SGMII0_TX250M_EN, "sgmii0_tx250m_en", "top_xtal", 2),
GATE_SGMII0(CLK_SGMII0_RX250M_EN, "sgmii0_rx250m_en", "top_xtal", 3),
GATE_SGMII0(CLK_SGMII0_CDR_REF, "sgmii0_cdr_ref", "top_xtal", 4),
@@ -42,14 +36,10 @@ static const struct mtk_gate_regs sgmii1_cg_regs = {
.sta_ofs = 0xe4,
};
-#define GATE_SGMII1(_id, _name, _parent, _shift) \
- { \
- .id = _id, .name = _name, .parent_name = _parent, \
- .regs = &sgmii1_cg_regs, .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_SGMII1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &sgmii1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
-static const struct mtk_gate sgmii1_clks[] __initconst = {
+static const struct mtk_gate sgmii1_clks[] = {
GATE_SGMII1(CLK_SGMII1_TX250M_EN, "sgmii1_tx250m_en", "top_xtal", 2),
GATE_SGMII1(CLK_SGMII1_RX250M_EN, "sgmii1_rx250m_en", "top_xtal", 3),
GATE_SGMII1(CLK_SGMII1_CDR_REF, "sgmii1_cdr_ref", "top_xtal", 4),
@@ -62,14 +52,10 @@ static const struct mtk_gate_regs eth_cg_regs = {
.sta_ofs = 0x30,
};
-#define GATE_ETH(_id, _name, _parent, _shift) \
- { \
- .id = _id, .name = _name, .parent_name = _parent, \
- .regs = &eth_cg_regs, .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr_inv, \
- }
+#define GATE_ETH(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &eth_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
-static const struct mtk_gate eth_clks[] __initconst = {
+static const struct mtk_gate eth_clks[] = {
GATE_ETH(CLK_ETH_FE_EN, "eth_fe_en", "netsys_2x_sel", 6),
GATE_ETH(CLK_ETH_GP2_EN, "eth_gp2_en", "sgm_325m_sel", 7),
GATE_ETH(CLK_ETH_GP1_EN, "eth_gp1_en", "sgm_325m_sel", 8),
@@ -77,56 +63,38 @@ static const struct mtk_gate eth_clks[] __initconst = {
GATE_ETH(CLK_ETH_WOCPU0_EN, "eth_wocpu0_en", "netsys_mcu_sel", 15),
};
-static void __init mtk_sgmiisys_0_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(ARRAY_SIZE(sgmii0_clks));
-
- mtk_clk_register_gates(node, sgmii0_clks, ARRAY_SIZE(sgmii0_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_sgmiisys_0, "mediatek,mt7986-sgmiisys_0",
- mtk_sgmiisys_0_init);
-
-static void __init mtk_sgmiisys_1_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(ARRAY_SIZE(sgmii1_clks));
-
- mtk_clk_register_gates(node, sgmii1_clks, ARRAY_SIZE(sgmii1_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_sgmiisys_1, "mediatek,mt7986-sgmiisys_1",
- mtk_sgmiisys_1_init);
-
-static void __init mtk_ethsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
+static const struct mtk_clk_desc eth_desc = {
+ .clks = eth_clks,
+ .num_clks = ARRAY_SIZE(eth_clks),
+};
- clk_data = mtk_alloc_clk_data(ARRAY_SIZE(eth_clks));
+static const struct mtk_clk_desc sgmii0_desc = {
+ .clks = sgmii0_clks,
+ .num_clks = ARRAY_SIZE(sgmii0_clks),
+};
- mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks), clk_data);
+static const struct mtk_clk_desc sgmii1_desc = {
+ .clks = sgmii1_clks,
+ .num_clks = ARRAY_SIZE(sgmii1_clks),
+};
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+static const struct of_device_id of_match_clk_mt7986_eth[] = {
+ { .compatible = "mediatek,mt7986-ethsys", .data = &eth_desc },
+ { .compatible = "mediatek,mt7986-sgmiisys_0", .data = &sgmii0_desc },
+ { .compatible = "mediatek,mt7986-sgmiisys_1", .data = &sgmii1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7986_eth);
+
+static struct platform_driver clk_mt7986_eth_drv = {
+ .driver = {
+ .name = "clk-mt7986-eth",
+ .of_match_table = of_match_clk_mt7986_eth,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt7986_eth_drv);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt7986-ethsys", mtk_ethsys_init);
+MODULE_DESCRIPTION("MediaTek MT7986 Ethernet clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7986-infracfg.c b/drivers/clk/mediatek/clk-mt7986-infracfg.c
index d90727a53283..732c65e616de 100644
--- a/drivers/clk/mediatek/clk-mt7986-infracfg.c
+++ b/drivers/clk/mediatek/clk-mt7986-infracfg.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
@@ -87,26 +85,14 @@ static const struct mtk_gate_regs infra2_cg_regs = {
.sta_ofs = 0x68,
};
-#define GATE_INFRA0(_id, _name, _parent, _shift) \
- { \
- .id = _id, .name = _name, .parent_name = _parent, \
- .regs = &infra0_cg_regs, .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_INFRA1(_id, _name, _parent, _shift) \
- { \
- .id = _id, .name = _name, .parent_name = _parent, \
- .regs = &infra1_cg_regs, .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_INFRA2(_id, _name, _parent, _shift) \
- { \
- .id = _id, .name = _name, .parent_name = _parent, \
- .regs = &infra2_cg_regs, .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_INFRA0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_INFRA1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_INFRA2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate infra_clks[] = {
/* INFRA0 */
@@ -153,7 +139,7 @@ static const struct mtk_gate infra_clks[] = {
18),
GATE_INFRA1(CLK_INFRA_MSDC_66M_CK, "infra_msdc_66m", "infra_sysaxi_d2",
19),
- GATE_INFRA1(CLK_INFRA_ADC_26M_CK, "infra_adc_26m", "csw_f26m_sel", 20),
+ GATE_INFRA1(CLK_INFRA_ADC_26M_CK, "infra_adc_26m", "infra_adc_frc", 20),
GATE_INFRA1(CLK_INFRA_ADC_FRC_CK, "infra_adc_frc", "csw_f26m_sel", 21),
GATE_INFRA1(CLK_INFRA_FBIST2FPC_CK, "infra_fbist2fpc", "nfi1x_sel", 23),
/* INFRA2 */
@@ -169,56 +155,31 @@ static const struct mtk_gate infra_clks[] = {
GATE_INFRA2(CLK_INFRA_IPCIEB_CK, "infra_ipcieb", "sysaxi_sel", 15),
};
-static int clk_mt7986_infracfg_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
- void __iomem *base;
- int nr = ARRAY_SIZE(infra_divs) + ARRAY_SIZE(infra_muxes) +
- ARRAY_SIZE(infra_clks);
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return -ENOMEM;
- }
-
- clk_data = mtk_alloc_clk_data(nr);
-
- if (!clk_data)
- return -ENOMEM;
-
- mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data);
- mtk_clk_register_muxes(infra_muxes, ARRAY_SIZE(infra_muxes), node,
- &mt7986_clk_lock, clk_data);
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r) {
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
- goto free_infracfg_data;
- }
- return r;
-
-free_infracfg_data:
- mtk_free_clk_data(clk_data);
- return r;
-
-}
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .factor_clks = infra_divs,
+ .num_factor_clks = ARRAY_SIZE(infra_divs),
+ .mux_clks = infra_muxes,
+ .num_mux_clks = ARRAY_SIZE(infra_muxes),
+ .clk_lock = &mt7986_clk_lock,
+};
static const struct of_device_id of_match_clk_mt7986_infracfg[] = {
- { .compatible = "mediatek,mt7986-infracfg", },
- {}
+ { .compatible = "mediatek,mt7986-infracfg", .data = &infra_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7986_infracfg);
static struct platform_driver clk_mt7986_infracfg_drv = {
- .probe = clk_mt7986_infracfg_probe,
.driver = {
.name = "clk-mt7986-infracfg",
.of_match_table = of_match_clk_mt7986_infracfg,
},
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
};
-builtin_platform_driver(clk_mt7986_infracfg_drv);
+module_platform_driver(clk_mt7986_infracfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7986 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7986-topckgen.c b/drivers/clk/mediatek/clk-mt7986-topckgen.c
index de5121cf2877..2dd30da306d9 100644
--- a/drivers/clk/mediatek/clk-mt7986-topckgen.c
+++ b/drivers/clk/mediatek/clk-mt7986-topckgen.c
@@ -6,9 +6,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
@@ -193,25 +191,32 @@ static const struct mtk_mux top_muxes[] = {
pextp_tl_ck_parents, 0x010, 0x014, 0x018, 24, 2,
31, 0x1C0, 7),
/* CLK_CFG_2 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_EMMC_250M_SEL, "emmc_250m_sel",
- emmc_250m_parents, 0x020, 0x024, 0x028, 0, 1, 7,
- 0x1C0, 8),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_EMMC_416M_SEL, "emmc_416m_sel",
- emmc_416m_parents, 0x020, 0x024, 0x028, 8, 1, 15,
- 0x1C0, 9),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_EMMC_250M_SEL, "emmc_250m_sel",
+ emmc_250m_parents, 0x020, 0x024, 0x028, 0, 1, 7,
+ 0x1C0, 8, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_EMMC_416M_SEL, "emmc_416m_sel",
+ emmc_416m_parents, 0x020, 0x024, 0x028, 8, 1, 15,
+ 0x1C0, 9, 0),
MUX_GATE_CLR_SET_UPD(CLK_TOP_F_26M_ADC_SEL, "f_26m_adc_sel",
f_26m_adc_parents, 0x020, 0x024, 0x028, 16, 1, 23,
0x1C0, 10),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_DRAMC_SEL, "dramc_sel", f_26m_adc_parents,
- 0x020, 0x024, 0x028, 24, 1, 31, 0x1C0, 11),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_SEL, "dramc_sel",
+ f_26m_adc_parents, 0x020, 0x024, 0x028,
+ 24, 1, 31, 0x1C0, 11,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/* CLK_CFG_3 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel",
- dramc_md32_parents, 0x030, 0x034, 0x038, 0, 1, 7,
- 0x1C0, 12),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_SYSAXI_SEL, "sysaxi_sel", sysaxi_parents,
- 0x030, 0x034, 0x038, 8, 2, 15, 0x1C0, 13),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_SYSAPB_SEL, "sysapb_sel", sysapb_parents,
- 0x030, 0x034, 0x038, 16, 2, 23, 0x1C0, 14),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel",
+ dramc_md32_parents, 0x030, 0x034, 0x038,
+ 0, 1, 7, 0x1C0, 12,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAXI_SEL, "sysaxi_sel",
+ sysaxi_parents, 0x030, 0x034, 0x038,
+ 8, 2, 15, 0x1C0, 13,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAPB_SEL, "sysapb_sel",
+ sysapb_parents, 0x030, 0x034, 0x038,
+ 16, 2, 23, 0x1C0, 14,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_ARM_DB_MAIN_SEL, "arm_db_main_sel",
arm_db_main_parents, 0x030, 0x034, 0x038, 24, 1,
31, 0x1C0, 15),
@@ -234,9 +239,10 @@ static const struct mtk_mux top_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_325M_SEL, "sgm_325m_sel",
sgm_325m_parents, 0x050, 0x054, 0x058, 8, 1, 15,
0x1C0, 21),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_REG_SEL, "sgm_reg_sel",
- sgm_reg_parents, 0x050, 0x054, 0x058, 16, 1, 23,
- 0x1C0, 22),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SGM_REG_SEL, "sgm_reg_sel",
+ sgm_reg_parents, 0x050, 0x054, 0x058,
+ 16, 1, 23, 0x1C0, 22,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_A1SYS_SEL, "a1sys_sel", a1sys_parents,
0x050, 0x054, 0x058, 24, 1, 31, 0x1C0, 23),
/* CLK_CFG_6 */
@@ -252,9 +258,10 @@ static const struct mtk_mux top_muxes[] = {
f_26m_adc_parents, 0x060, 0x064, 0x068, 24, 1, 31,
0x1C0, 27),
/* CLK_CFG_7 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_F26M_SEL, "csw_f26m_sel",
- f_26m_adc_parents, 0x070, 0x074, 0x078, 0, 1, 7,
- 0x1C0, 28),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_F26M_SEL, "csw_f26m_sel",
+ f_26m_adc_parents, 0x070, 0x074, 0x078,
+ 0, 1, 7, 0x1C0, 28,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_L_SEL, "aud_l_sel", aud_l_parents,
0x070, 0x074, 0x078, 8, 2, 15, 0x1C0, 29),
MUX_GATE_CLR_SET_UPD(CLK_TOP_A_TUNER_SEL, "a_tuner_sel",
@@ -281,62 +288,31 @@ static const struct mtk_mux top_muxes[] = {
0x1C4, 5),
};
-static int clk_mt7986_topckgen_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
- void __iomem *base;
- int nr = ARRAY_SIZE(top_fixed_clks) + ARRAY_SIZE(top_divs) +
- ARRAY_SIZE(top_muxes);
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return -ENOMEM;
- }
-
- clk_data = mtk_alloc_clk_data(nr);
- if (!clk_data)
- return -ENOMEM;
-
- mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
- clk_data);
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node,
- &mt7986_clk_lock, clk_data);
-
- clk_prepare_enable(clk_data->hws[CLK_TOP_SYSAXI_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_SYSAPB_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_DRAMC_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_DRAMC_MD32_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_F26M_SEL]->clk);
- clk_prepare_enable(clk_data->hws[CLK_TOP_SGM_REG_SEL]->clk);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r) {
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
- goto free_topckgen_data;
- }
- return r;
-
-free_topckgen_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .clk_lock = &mt7986_clk_lock,
+};
static const struct of_device_id of_match_clk_mt7986_topckgen[] = {
- { .compatible = "mediatek,mt7986-topckgen", },
- {}
+ { .compatible = "mediatek,mt7986-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7986_topckgen);
static struct platform_driver clk_mt7986_topckgen_drv = {
- .probe = clk_mt7986_topckgen_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt7986-topckgen",
.of_match_table = of_match_clk_mt7986_topckgen,
},
};
-builtin_platform_driver(clk_mt7986_topckgen_drv);
+module_platform_driver(clk_mt7986_topckgen_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7986 top clock generators driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7988-apmixed.c b/drivers/clk/mediatek/clk-mt7988-apmixed.c
new file mode 100644
index 000000000000..63d33a78cb48
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7988-apmixed.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Xiufeng Li <Xiufeng.Li@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "clk-mux.h"
+#include "clk-pll.h"
+#include <dt-bindings/clock/mediatek,mt7988-clk.h>
+
+#define MT7988_PLL_FMAX (2500UL * MHZ)
+#define MT7988_PCW_CHG_BIT 2
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _rst_bar_mask, _pcwbits, _pd_reg, \
+ _pd_shift, _tuner_reg, _tuner_en_reg, _tuner_en_bit, _pcw_reg, _pcw_shift, \
+ _pcw_chg_reg) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = BIT(_rst_bar_mask), \
+ .fmax = MT7988_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcw_chg_reg = _pcw_chg_reg, \
+ .pcw_chg_bit = MT7988_PCW_CHG_BIT, \
+ .parent_name = "clkxtal", \
+ }
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_NETSYSPLL, "netsyspll", 0x0104, 0x0110, 0x00000001, 0, 0, 32, 0x0104, 4, 0,
+ 0, 0, 0x0108, 0, 0x0104),
+ PLL(CLK_APMIXED_MPLL, "mpll", 0x0114, 0x0120, 0xff000001, HAVE_RST_BAR, 23, 32, 0x0114, 4,
+ 0, 0, 0, 0x0118, 0, 0x0114),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0124, 0x0130, 0xff000001, HAVE_RST_BAR, 23, 32, 0x0124, 4,
+ 0, 0, 0, 0x0128, 0, 0x0124),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x0134, 0x0140, 0x00000001, 0, 0, 32, 0x0134, 4, 0x0704,
+ 0x0700, 1, 0x0138, 0, 0x0134),
+ PLL(CLK_APMIXED_NET1PLL, "net1pll", 0x0144, 0x0150, 0xff000001, HAVE_RST_BAR, 23, 32,
+ 0x0144, 4, 0, 0, 0, 0x0148, 0, 0x0144),
+ PLL(CLK_APMIXED_NET2PLL, "net2pll", 0x0154, 0x0160, 0xff000001, (HAVE_RST_BAR | PLL_AO), 23,
+ 32, 0x0154, 4, 0, 0, 0, 0x0158, 0, 0x0154),
+ PLL(CLK_APMIXED_WEDMCUPLL, "wedmcupll", 0x0164, 0x0170, 0x00000001, 0, 0, 32, 0x0164, 4, 0,
+ 0, 0, 0x0168, 0, 0x0164),
+ PLL(CLK_APMIXED_SGMPLL, "sgmpll", 0x0174, 0x0180, 0x00000001, 0, 0, 32, 0x0174, 4, 0, 0, 0,
+ 0x0178, 0, 0x0174),
+ PLL(CLK_APMIXED_ARM_B, "arm_b", 0x0204, 0x0210, 0xff000001, (HAVE_RST_BAR | PLL_AO), 23, 32,
+ 0x0204, 4, 0, 0, 0, 0x0208, 0, 0x0204),
+ PLL(CLK_APMIXED_CCIPLL2_B, "ccipll2_b", 0x0214, 0x0220, 0xff000001, HAVE_RST_BAR, 23, 32,
+ 0x0214, 4, 0, 0, 0, 0x0218, 0, 0x0214),
+ PLL(CLK_APMIXED_USXGMIIPLL, "usxgmiipll", 0x0304, 0x0310, 0xff000001, HAVE_RST_BAR, 23, 32,
+ 0x0304, 4, 0, 0, 0, 0x0308, 0, 0x0304),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0314, 0x0320, 0x00000001, 0, 0, 32, 0x0314, 4, 0, 0,
+ 0, 0x0318, 0, 0x0314),
+};
+
+static const struct of_device_id of_match_clk_mt7988_apmixed[] = {
+ { .compatible = "mediatek,mt7988-apmixedsys" },
+ { /* sentinel */ }
+};
+
+static int clk_mt7988_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ int r;
+
+ clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls));
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (r)
+ goto free_apmixed_data;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ return r;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+free_apmixed_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static struct platform_driver clk_mt7988_apmixed_drv = {
+ .probe = clk_mt7988_apmixed_probe,
+ .driver = {
+ .name = "clk-mt7988-apmixed",
+ .of_match_table = of_match_clk_mt7988_apmixed,
+ },
+};
+builtin_platform_driver(clk_mt7988_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7988 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7988-eth.c b/drivers/clk/mediatek/clk-mt7988-eth.c
new file mode 100644
index 000000000000..7d9463688be2
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7988-eth.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Xiufeng Li <Xiufeng.Li@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "reset.h"
+#include <dt-bindings/clock/mediatek,mt7988-clk.h>
+#include <dt-bindings/reset/mediatek,mt7988-resets.h>
+
+static const struct mtk_gate_regs ethdma_cg_regs = {
+ .set_ofs = 0x30,
+ .clr_ofs = 0x30,
+ .sta_ofs = 0x30,
+};
+
+#define GATE_ETHDMA(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ethdma_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate ethdma_clks[] = {
+ GATE_ETHDMA(CLK_ETHDMA_XGP1_EN, "ethdma_xgp1_en", "top_xtal", 0),
+ GATE_ETHDMA(CLK_ETHDMA_XGP2_EN, "ethdma_xgp2_en", "top_xtal", 1),
+ GATE_ETHDMA(CLK_ETHDMA_XGP3_EN, "ethdma_xgp3_en", "top_xtal", 2),
+ GATE_ETHDMA(CLK_ETHDMA_FE_EN, "ethdma_fe_en", "netsys_2x_sel", 6),
+ GATE_ETHDMA(CLK_ETHDMA_GP2_EN, "ethdma_gp2_en", "top_xtal", 7),
+ GATE_ETHDMA(CLK_ETHDMA_GP1_EN, "ethdma_gp1_en", "top_xtal", 8),
+ GATE_ETHDMA(CLK_ETHDMA_GP3_EN, "ethdma_gp3_en", "top_xtal", 10),
+ GATE_ETHDMA(CLK_ETHDMA_ESW_EN, "ethdma_esw_en", "netsys_gsw_sel", 16),
+ GATE_ETHDMA(CLK_ETHDMA_CRYPT0_EN, "ethdma_crypt0_en", "eip197_sel", 29),
+};
+
+static const struct mtk_clk_desc ethdma_desc = {
+ .clks = ethdma_clks,
+ .num_clks = ARRAY_SIZE(ethdma_clks),
+};
+
+static const struct mtk_gate_regs sgmii_cg_regs = {
+ .set_ofs = 0xe4,
+ .clr_ofs = 0xe4,
+ .sta_ofs = 0xe4,
+};
+
+#define GATE_SGMII(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &sgmii_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate sgmii0_clks[] = {
+ GATE_SGMII(CLK_SGM0_TX_EN, "sgm0_tx_en", "top_xtal", 2),
+ GATE_SGMII(CLK_SGM0_RX_EN, "sgm0_rx_en", "top_xtal", 3),
+};
+
+static const struct mtk_clk_desc sgmii0_desc = {
+ .clks = sgmii0_clks,
+ .num_clks = ARRAY_SIZE(sgmii0_clks),
+};
+
+static const struct mtk_gate sgmii1_clks[] = {
+ GATE_SGMII(CLK_SGM1_TX_EN, "sgm1_tx_en", "top_xtal", 2),
+ GATE_SGMII(CLK_SGM1_RX_EN, "sgm1_rx_en", "top_xtal", 3),
+};
+
+static const struct mtk_clk_desc sgmii1_desc = {
+ .clks = sgmii1_clks,
+ .num_clks = ARRAY_SIZE(sgmii1_clks),
+};
+
+static const struct mtk_gate_regs ethwarp_cg_regs = {
+ .set_ofs = 0x14,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x14,
+};
+
+#define GATE_ETHWARP(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ethwarp_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate ethwarp_clks[] = {
+ GATE_ETHWARP(CLK_ETHWARP_WOCPU2_EN, "ethwarp_wocpu2_en", "netsys_mcu_sel", 13),
+ GATE_ETHWARP(CLK_ETHWARP_WOCPU1_EN, "ethwarp_wocpu1_en", "netsys_mcu_sel", 14),
+ GATE_ETHWARP(CLK_ETHWARP_WOCPU0_EN, "ethwarp_wocpu0_en", "netsys_mcu_sel", 15),
+};
+
+static u16 ethwarp_rst_ofs[] = { 0x8 };
+
+static u16 ethwarp_idx_map[] = {
+ [MT7988_ETHWARP_RST_SWITCH] = 9,
+};
+
+static const struct mtk_clk_rst_desc ethwarp_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = ethwarp_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(ethwarp_rst_ofs),
+ .rst_idx_map = ethwarp_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(ethwarp_idx_map),
+};
+
+static const struct mtk_clk_desc ethwarp_desc = {
+ .clks = ethwarp_clks,
+ .num_clks = ARRAY_SIZE(ethwarp_clks),
+ .rst_desc = &ethwarp_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt7988_eth[] = {
+ { .compatible = "mediatek,mt7988-ethsys", .data = &ethdma_desc },
+ { .compatible = "mediatek,mt7988-sgmiisys0", .data = &sgmii0_desc },
+ { .compatible = "mediatek,mt7988-sgmiisys1", .data = &sgmii1_desc },
+ { .compatible = "mediatek,mt7988-ethwarp", .data = &ethwarp_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7988_eth);
+
+static struct platform_driver clk_mt7988_eth_drv = {
+ .driver = {
+ .name = "clk-mt7988-eth",
+ .of_match_table = of_match_clk_mt7988_eth,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt7988_eth_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7988 Ethernet clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7988-infracfg.c b/drivers/clk/mediatek/clk-mt7988-infracfg.c
new file mode 100644
index 000000000000..ef8267319d91
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7988-infracfg.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Xiufeng Li <Xiufeng.Li@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "clk-mux.h"
+#include <dt-bindings/clock/mediatek,mt7988-clk.h>
+#include <dt-bindings/reset/mediatek,mt7988-resets.h>
+
+#define MT7988_INFRA_RST0_SET_OFFSET 0x70
+#define MT7988_INFRA_RST1_SET_OFFSET 0x80
+
+static DEFINE_SPINLOCK(mt7988_clk_lock);
+
+static const char *const infra_mux_uart0_parents[] __initconst = { "csw_infra_f26m_sel",
+ "uart_sel" };
+
+static const char *const infra_mux_uart1_parents[] __initconst = { "csw_infra_f26m_sel",
+ "uart_sel" };
+
+static const char *const infra_mux_uart2_parents[] __initconst = { "csw_infra_f26m_sel",
+ "uart_sel" };
+
+static const char *const infra_mux_spi0_parents[] __initconst = { "i2c_sel", "spi_sel" };
+
+static const char *const infra_mux_spi1_parents[] __initconst = { "i2c_sel", "spim_mst_sel" };
+
+static const char *const infra_pwm_bck_parents[] __initconst = { "top_rtc_32p7k",
+ "csw_infra_f26m_sel", "sysaxi_sel",
+ "pwm_sel" };
+
+static const char *const infra_pcie_gfmux_tl_ck_o_p0_parents[] __initconst = {
+ "top_rtc_32p7k", "csw_infra_f26m_sel", "csw_infra_f26m_sel", "pextp_tl_sel"
+};
+
+static const char *const infra_pcie_gfmux_tl_ck_o_p1_parents[] __initconst = {
+ "top_rtc_32p7k", "csw_infra_f26m_sel", "csw_infra_f26m_sel", "pextp_tl_p1_sel"
+};
+
+static const char *const infra_pcie_gfmux_tl_ck_o_p2_parents[] __initconst = {
+ "top_rtc_32p7k", "csw_infra_f26m_sel", "csw_infra_f26m_sel", "pextp_tl_p2_sel"
+};
+
+static const char *const infra_pcie_gfmux_tl_ck_o_p3_parents[] __initconst = {
+ "top_rtc_32p7k", "csw_infra_f26m_sel", "csw_infra_f26m_sel", "pextp_tl_p3_sel"
+};
+
+static const struct mtk_mux infra_muxes[] = {
+ /* MODULE_CLK_SEL_0 */
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART0_SEL, "infra_mux_uart0_sel",
+ infra_mux_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART1_SEL, "infra_mux_uart1_sel",
+ infra_mux_uart1_parents, 0x0018, 0x0010, 0x0014, 1, 1, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART2_SEL, "infra_mux_uart2_sel",
+ infra_mux_uart2_parents, 0x0018, 0x0010, 0x0014, 2, 1, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI0_SEL, "infra_mux_spi0_sel", infra_mux_spi0_parents,
+ 0x0018, 0x0010, 0x0014, 4, 1, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI1_SEL, "infra_mux_spi1_sel", infra_mux_spi1_parents,
+ 0x0018, 0x0010, 0x0014, 5, 1, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI2_SEL, "infra_mux_spi2_sel", infra_mux_spi0_parents,
+ 0x0018, 0x0010, 0x0014, 6, 1, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_SEL, "infra_pwm_sel", infra_pwm_bck_parents, 0x0018,
+ 0x0010, 0x0014, 14, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK1_SEL, "infra_pwm_ck1_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 16, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK2_SEL, "infra_pwm_ck2_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 18, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK3_SEL, "infra_pwm_ck3_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 20, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK4_SEL, "infra_pwm_ck4_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 22, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK5_SEL, "infra_pwm_ck5_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 24, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK6_SEL, "infra_pwm_ck6_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 26, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK7_SEL, "infra_pwm_ck7_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 28, 2, -1, -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK8_SEL, "infra_pwm_ck8_sel", infra_pwm_bck_parents,
+ 0x0018, 0x0010, 0x0014, 30, 2, -1, -1, -1),
+ /* MODULE_CLK_SEL_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P0_SEL, "infra_pcie_gfmux_tl_o_p0_sel",
+ infra_pcie_gfmux_tl_ck_o_p0_parents, 0x0028, 0x0020, 0x0024, 0, 2, -1,
+ -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P1_SEL, "infra_pcie_gfmux_tl_o_p1_sel",
+ infra_pcie_gfmux_tl_ck_o_p1_parents, 0x0028, 0x0020, 0x0024, 2, 2, -1,
+ -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P2_SEL, "infra_pcie_gfmux_tl_o_p2_sel",
+ infra_pcie_gfmux_tl_ck_o_p2_parents, 0x0028, 0x0020, 0x0024, 4, 2, -1,
+ -1, -1),
+ MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P3_SEL, "infra_pcie_gfmux_tl_o_p3_sel",
+ infra_pcie_gfmux_tl_ck_o_p3_parents, 0x0028, 0x0020, 0x0024, 6, 2, -1,
+ -1, -1),
+};
+
+static const struct mtk_gate_regs infra0_cg_regs = {
+ .set_ofs = 0x10,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x18,
+};
+
+static const struct mtk_gate_regs infra1_cg_regs = {
+ .set_ofs = 0x40,
+ .clr_ofs = 0x44,
+ .sta_ofs = 0x48,
+};
+
+static const struct mtk_gate_regs infra2_cg_regs = {
+ .set_ofs = 0x50,
+ .clr_ofs = 0x54,
+ .sta_ofs = 0x58,
+};
+
+static const struct mtk_gate_regs infra3_cg_regs = {
+ .set_ofs = 0x60,
+ .clr_ofs = 0x64,
+ .sta_ofs = 0x68,
+};
+
+#define GATE_INFRA0_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra0_cg_regs, _shift, &mtk_clk_gate_ops_setclr, \
+ _flags)
+
+#define GATE_INFRA1_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra1_cg_regs, _shift, &mtk_clk_gate_ops_setclr, \
+ _flags)
+
+#define GATE_INFRA2_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra2_cg_regs, _shift, &mtk_clk_gate_ops_setclr, \
+ _flags)
+
+#define GATE_INFRA3_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra3_cg_regs, _shift, &mtk_clk_gate_ops_setclr, \
+ _flags)
+
+#define GATE_INFRA0(_id, _name, _parent, _shift) GATE_INFRA0_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_INFRA1(_id, _name, _parent, _shift) GATE_INFRA1_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_INFRA2(_id, _name, _parent, _shift) GATE_INFRA2_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_INFRA3(_id, _name, _parent, _shift) GATE_INFRA3_FLAGS(_id, _name, _parent, _shift, 0)
+
+static const struct mtk_gate infra_clks[] = {
+ /* INFRA0 */
+ GATE_INFRA0(CLK_INFRA_PCIE_PERI_26M_CK_P0, "infra_pcie_peri_ck_26m_ck_p0",
+ "csw_infra_f26m_sel", 7),
+ GATE_INFRA0(CLK_INFRA_PCIE_PERI_26M_CK_P1, "infra_pcie_peri_ck_26m_ck_p1",
+ "csw_infra_f26m_sel", 8),
+ GATE_INFRA0(CLK_INFRA_PCIE_PERI_26M_CK_P2, "infra_pcie_peri_ck_26m_ck_p2",
+ "infra_pcie_peri_ck_26m_ck_p3", 9),
+ GATE_INFRA0(CLK_INFRA_PCIE_PERI_26M_CK_P3, "infra_pcie_peri_ck_26m_ck_p3",
+ "csw_infra_f26m_sel", 10),
+ /* INFRA1 */
+ GATE_INFRA1(CLK_INFRA_66M_GPT_BCK, "infra_hf_66m_gpt_bck", "sysaxi_sel", 0),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_HCK, "infra_hf_66m_pwm_hck", "sysaxi_sel", 1),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_BCK, "infra_hf_66m_pwm_bck", "infra_pwm_sel", 2),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK1, "infra_hf_66m_pwm_ck1", "infra_pwm_ck1_sel", 3),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK2, "infra_hf_66m_pwm_ck2", "infra_pwm_ck2_sel", 4),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK3, "infra_hf_66m_pwm_ck3", "infra_pwm_ck3_sel", 5),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK4, "infra_hf_66m_pwm_ck4", "infra_pwm_ck4_sel", 6),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK5, "infra_hf_66m_pwm_ck5", "infra_pwm_ck5_sel", 7),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK6, "infra_hf_66m_pwm_ck6", "infra_pwm_ck6_sel", 8),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK7, "infra_hf_66m_pwm_ck7", "infra_pwm_ck7_sel", 9),
+ GATE_INFRA1(CLK_INFRA_66M_PWM_CK8, "infra_hf_66m_pwm_ck8", "infra_pwm_ck8_sel", 10),
+ GATE_INFRA1(CLK_INFRA_133M_CQDMA_BCK, "infra_hf_133m_cqdma_bck", "sysaxi_sel", 12),
+ GATE_INFRA1(CLK_INFRA_66M_AUD_SLV_BCK, "infra_66m_aud_slv_bck", "sysaxi_sel", 13),
+ GATE_INFRA1(CLK_INFRA_AUD_26M, "infra_f_faud_26m", "csw_infra_f26m_sel", 14),
+ GATE_INFRA1(CLK_INFRA_AUD_L, "infra_f_faud_l", "aud_l_sel", 15),
+ GATE_INFRA1(CLK_INFRA_AUD_AUD, "infra_f_aud_aud", "a1sys_sel", 16),
+ GATE_INFRA1(CLK_INFRA_AUD_EG2, "infra_f_faud_eg2", "a_tuner_sel", 18),
+ GATE_INFRA1_FLAGS(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m", "csw_infra_f26m_sel", 19,
+ CLK_IS_CRITICAL),
+ /* JTAG */
+ GATE_INFRA1_FLAGS(CLK_INFRA_133M_DBG_ACKM, "infra_hf_133m_dbg_ackm", "sysaxi_sel", 20,
+ CLK_IS_CRITICAL),
+ GATE_INFRA1(CLK_INFRA_66M_AP_DMA_BCK, "infra_66m_ap_dma_bck", "sysaxi_sel", 21),
+ GATE_INFRA1(CLK_INFRA_66M_SEJ_BCK, "infra_hf_66m_sej_bck", "sysaxi_sel", 29),
+ GATE_INFRA1(CLK_INFRA_PRE_CK_SEJ_F13M, "infra_pre_ck_sej_f13m", "csw_infra_f26m_sel", 30),
+ /* INFRA2 */
+ GATE_INFRA2(CLK_INFRA_26M_THERM_SYSTEM, "infra_hf_26m_therm_system", "csw_infra_f26m_sel",
+ 0),
+ GATE_INFRA2(CLK_INFRA_I2C_BCK, "infra_i2c_bck", "i2c_sel", 1),
+ GATE_INFRA2(CLK_INFRA_52M_UART0_CK, "infra_f_52m_uart0", "infra_mux_uart0_sel", 3),
+ GATE_INFRA2(CLK_INFRA_52M_UART1_CK, "infra_f_52m_uart1", "infra_mux_uart1_sel", 4),
+ GATE_INFRA2(CLK_INFRA_52M_UART2_CK, "infra_f_52m_uart2", "infra_mux_uart2_sel", 5),
+ GATE_INFRA2(CLK_INFRA_NFI, "infra_f_fnfi", "nfi1x_sel", 9),
+ GATE_INFRA2(CLK_INFRA_SPINFI, "infra_f_fspinfi", "spinfi_sel", 10),
+ GATE_INFRA2_FLAGS(CLK_INFRA_66M_NFI_HCK, "infra_hf_66m_nfi_hck", "sysaxi_sel", 11,
+ CLK_IS_CRITICAL),
+ GATE_INFRA2_FLAGS(CLK_INFRA_104M_SPI0, "infra_hf_104m_spi0", "infra_mux_spi0_sel", 12,
+ CLK_IS_CRITICAL),
+ GATE_INFRA2(CLK_INFRA_104M_SPI1, "infra_hf_104m_spi1", "infra_mux_spi1_sel", 13),
+ GATE_INFRA2(CLK_INFRA_104M_SPI2_BCK, "infra_hf_104m_spi2_bck", "infra_mux_spi2_sel", 14),
+ GATE_INFRA2_FLAGS(CLK_INFRA_66M_SPI0_HCK, "infra_hf_66m_spi0_hck", "sysaxi_sel", 15,
+ CLK_IS_CRITICAL),
+ GATE_INFRA2(CLK_INFRA_66M_SPI1_HCK, "infra_hf_66m_spi1_hck", "sysaxi_sel", 16),
+ GATE_INFRA2(CLK_INFRA_66M_SPI2_HCK, "infra_hf_66m_spi2_hck", "sysaxi_sel", 17),
+ GATE_INFRA2(CLK_INFRA_66M_FLASHIF_AXI, "infra_hf_66m_flashif_axi", "sysaxi_sel", 18),
+ GATE_INFRA2_FLAGS(CLK_INFRA_RTC, "infra_f_frtc", "top_rtc_32k", 19, CLK_IS_CRITICAL),
+ GATE_INFRA2(CLK_INFRA_26M_ADC_BCK, "infra_f_26m_adc_bck", "csw_infra_f26m_sel", 20),
+ GATE_INFRA2(CLK_INFRA_RC_ADC, "infra_f_frc_adc", "infra_f_26m_adc_bck", 21),
+ GATE_INFRA2(CLK_INFRA_MSDC400, "infra_f_fmsdc400", "emmc_400m_sel", 22),
+ GATE_INFRA2(CLK_INFRA_MSDC2_HCK, "infra_f_fmsdc2_hck", "emmc_250m_sel", 23),
+ GATE_INFRA2(CLK_INFRA_133M_MSDC_0_HCK, "infra_hf_133m_msdc_0_hck", "sysaxi_sel", 24),
+ GATE_INFRA2(CLK_INFRA_66M_MSDC_0_HCK, "infra_66m_msdc_0_hck", "sysaxi_sel", 25),
+ GATE_INFRA2(CLK_INFRA_133M_CPUM_BCK, "infra_hf_133m_cpum_bck", "sysaxi_sel", 26),
+ GATE_INFRA2(CLK_INFRA_BIST2FPC, "infra_hf_fbist2fpc", "nfi1x_sel", 27),
+ GATE_INFRA2(CLK_INFRA_I2C_X16W_MCK_CK_P1, "infra_hf_i2c_x16w_mck_ck_p1", "sysaxi_sel", 29),
+ GATE_INFRA2(CLK_INFRA_I2C_X16W_PCK_CK_P1, "infra_hf_i2c_x16w_pck_ck_p1", "sysaxi_sel", 31),
+ /* INFRA3 */
+ GATE_INFRA3(CLK_INFRA_133M_USB_HCK, "infra_133m_usb_hck", "sysaxi_sel", 0),
+ GATE_INFRA3(CLK_INFRA_133M_USB_HCK_CK_P1, "infra_133m_usb_hck_ck_p1", "sysaxi_sel", 1),
+ GATE_INFRA3(CLK_INFRA_66M_USB_HCK, "infra_66m_usb_hck", "sysaxi_sel", 2),
+ GATE_INFRA3(CLK_INFRA_66M_USB_HCK_CK_P1, "infra_66m_usb_hck_ck_p1", "sysaxi_sel", 3),
+ GATE_INFRA3(CLK_INFRA_USB_SYS, "infra_usb_sys", "usb_sys_sel", 4),
+ GATE_INFRA3(CLK_INFRA_USB_SYS_CK_P1, "infra_usb_sys_ck_p1", "usb_sys_p1_sel", 5),
+ GATE_INFRA3(CLK_INFRA_USB_REF, "infra_usb_ref", "top_xtal", 6),
+ GATE_INFRA3(CLK_INFRA_USB_CK_P1, "infra_usb_ck_p1", "top_xtal", 7),
+ GATE_INFRA3_FLAGS(CLK_INFRA_USB_FRMCNT, "infra_usb_frmcnt", "usb_frmcnt_sel", 8,
+ CLK_IS_CRITICAL),
+ GATE_INFRA3_FLAGS(CLK_INFRA_USB_FRMCNT_CK_P1, "infra_usb_frmcnt_ck_p1", "usb_frmcnt_p1_sel",
+ 9, CLK_IS_CRITICAL),
+ GATE_INFRA3(CLK_INFRA_USB_PIPE, "infra_usb_pipe", "sspxtp_sel", 10),
+ GATE_INFRA3(CLK_INFRA_USB_PIPE_CK_P1, "infra_usb_pipe_ck_p1", "usb_phy_sel", 11),
+ GATE_INFRA3(CLK_INFRA_USB_UTMI, "infra_usb_utmi", "top_xtal", 12),
+ GATE_INFRA3(CLK_INFRA_USB_UTMI_CK_P1, "infra_usb_utmi_ck_p1", "top_xtal", 13),
+ GATE_INFRA3(CLK_INFRA_USB_XHCI, "infra_usb_xhci", "usb_xhci_sel", 14),
+ GATE_INFRA3(CLK_INFRA_USB_XHCI_CK_P1, "infra_usb_xhci_ck_p1", "usb_xhci_p1_sel", 15),
+ GATE_INFRA3(CLK_INFRA_PCIE_GFMUX_TL_P0, "infra_pcie_gfmux_tl_ck_p0",
+ "infra_pcie_gfmux_tl_o_p0_sel", 20),
+ GATE_INFRA3(CLK_INFRA_PCIE_GFMUX_TL_P1, "infra_pcie_gfmux_tl_ck_p1",
+ "infra_pcie_gfmux_tl_o_p1_sel", 21),
+ GATE_INFRA3(CLK_INFRA_PCIE_GFMUX_TL_P2, "infra_pcie_gfmux_tl_ck_p2",
+ "infra_pcie_gfmux_tl_o_p2_sel", 22),
+ GATE_INFRA3(CLK_INFRA_PCIE_GFMUX_TL_P3, "infra_pcie_gfmux_tl_ck_p3",
+ "infra_pcie_gfmux_tl_o_p3_sel", 23),
+ GATE_INFRA3(CLK_INFRA_PCIE_PIPE_P0, "infra_pcie_pipe_ck_p0", "top_xtal", 24),
+ GATE_INFRA3(CLK_INFRA_PCIE_PIPE_P1, "infra_pcie_pipe_ck_p1", "top_xtal", 25),
+ GATE_INFRA3(CLK_INFRA_PCIE_PIPE_P2, "infra_pcie_pipe_ck_p2", "top_xtal", 26),
+ GATE_INFRA3(CLK_INFRA_PCIE_PIPE_P3, "infra_pcie_pipe_ck_p3", "top_xtal", 27),
+ GATE_INFRA3(CLK_INFRA_133M_PCIE_CK_P0, "infra_133m_pcie_ck_p0", "sysaxi_sel", 28),
+ GATE_INFRA3(CLK_INFRA_133M_PCIE_CK_P1, "infra_133m_pcie_ck_p1", "sysaxi_sel", 29),
+ GATE_INFRA3(CLK_INFRA_133M_PCIE_CK_P2, "infra_133m_pcie_ck_p2", "sysaxi_sel", 30),
+ GATE_INFRA3(CLK_INFRA_133M_PCIE_CK_P3, "infra_133m_pcie_ck_p3", "sysaxi_sel", 31),
+};
+
+static u16 infra_rst_ofs[] = {
+ MT7988_INFRA_RST0_SET_OFFSET,
+ MT7988_INFRA_RST1_SET_OFFSET,
+};
+
+static u16 infra_idx_map[] = {
+ [MT7988_INFRA_RST0_PEXTP_MAC_SWRST] = 0 * RST_NR_PER_BANK + 6,
+ [MT7988_INFRA_RST1_THERM_CTRL_SWRST] = 1 * RST_NR_PER_BANK + 9,
+};
+
+static struct mtk_clk_rst_desc infra_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = infra_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(infra_rst_ofs),
+ .rst_idx_map = infra_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(infra_idx_map),
+};
+
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .mux_clks = infra_muxes,
+ .num_mux_clks = ARRAY_SIZE(infra_muxes),
+ .clk_lock = &mt7988_clk_lock,
+ .rst_desc = &infra_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt7988_infracfg[] = {
+ { .compatible = "mediatek,mt7988-infracfg", .data = &infra_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7988_infracfg);
+
+static struct platform_driver clk_mt7988_infracfg_drv = {
+ .driver = {
+ .name = "clk-mt7988-infracfg",
+ .of_match_table = of_match_clk_mt7988_infracfg,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt7988_infracfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7988 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7988-topckgen.c b/drivers/clk/mediatek/clk-mt7988-topckgen.c
new file mode 100644
index 000000000000..50e02cc7a214
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7988-topckgen.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Sam Shih <sam.shih@mediatek.com>
+ * Author: Xiufeng Li <Xiufeng.Li@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "clk-mux.h"
+#include <dt-bindings/clock/mediatek,mt7988-clk.h>
+
+static DEFINE_SPINLOCK(mt7988_clk_lock);
+
+static const struct mtk_fixed_clk top_fixed_clks[] = {
+ FIXED_CLK(CLK_TOP_XTAL, "top_xtal", "clkxtal", 40000000),
+};
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_XTAL_D2, "top_xtal_d2", "top_xtal", 1, 2),
+ FACTOR(CLK_TOP_RTC_32K, "top_rtc_32k", "top_xtal", 1, 1250),
+ FACTOR(CLK_TOP_RTC_32P7K, "top_rtc_32p7k", "top_xtal", 1, 1220),
+ FACTOR(CLK_TOP_MPLL_D2, "mpll_d2", "mpll", 1, 2),
+ FACTOR(CLK_TOP_MPLL_D3_D2, "mpll_d3_d2", "mpll", 1, 2),
+ FACTOR(CLK_TOP_MPLL_D4, "mpll_d4", "mpll", 1, 4),
+ FACTOR(CLK_TOP_MPLL_D8, "mpll_d8", "mpll", 1, 8),
+ FACTOR(CLK_TOP_MPLL_D8_D2, "mpll_d8_d2", "mpll", 1, 16),
+ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
+ FACTOR(CLK_TOP_MMPLL_D3_D5, "mmpll_d3_d5", "mmpll", 1, 15),
+ FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D6_D2, "mmpll_d6_d2", "mmpll", 1, 12),
+ FACTOR(CLK_TOP_MMPLL_D8, "mmpll_d8", "mmpll", 1, 8),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4),
+ FACTOR(CLK_TOP_NET1PLL_D4, "net1pll_d4", "net1pll", 1, 4),
+ FACTOR(CLK_TOP_NET1PLL_D5, "net1pll_d5", "net1pll", 1, 5),
+ FACTOR(CLK_TOP_NET1PLL_D5_D2, "net1pll_d5_d2", "net1pll", 1, 10),
+ FACTOR(CLK_TOP_NET1PLL_D5_D4, "net1pll_d5_d4", "net1pll", 1, 20),
+ FACTOR(CLK_TOP_NET1PLL_D8, "net1pll_d8", "net1pll", 1, 8),
+ FACTOR(CLK_TOP_NET1PLL_D8_D2, "net1pll_d8_d2", "net1pll", 1, 16),
+ FACTOR(CLK_TOP_NET1PLL_D8_D4, "net1pll_d8_d4", "net1pll", 1, 32),
+ FACTOR(CLK_TOP_NET1PLL_D8_D8, "net1pll_d8_d8", "net1pll", 1, 64),
+ FACTOR(CLK_TOP_NET1PLL_D8_D16, "net1pll_d8_d16", "net1pll", 1, 128),
+ FACTOR(CLK_TOP_NET2PLL_D2, "net2pll_d2", "net2pll", 1, 2),
+ FACTOR(CLK_TOP_NET2PLL_D4, "net2pll_d4", "net2pll", 1, 4),
+ FACTOR(CLK_TOP_NET2PLL_D4_D4, "net2pll_d4_d4", "net2pll", 1, 16),
+ FACTOR(CLK_TOP_NET2PLL_D4_D8, "net2pll_d4_d8", "net2pll", 1, 32),
+ FACTOR(CLK_TOP_NET2PLL_D6, "net2pll_d6", "net2pll", 1, 6),
+ FACTOR(CLK_TOP_NET2PLL_D8, "net2pll_d8", "net2pll", 1, 8),
+};
+
+static const char *const netsys_parents[] = { "top_xtal", "net2pll_d2", "mmpll_d2" };
+static const char *const netsys_500m_parents[] = { "top_xtal", "net1pll_d5", "net1pll_d5_d2" };
+static const char *const netsys_2x_parents[] = { "top_xtal", "net2pll", "mmpll" };
+static const char *const netsys_gsw_parents[] = { "top_xtal", "net1pll_d4", "net1pll_d5" };
+static const char *const eth_gmii_parents[] = { "top_xtal", "net1pll_d5_d4" };
+static const char *const netsys_mcu_parents[] = { "top_xtal", "net2pll", "mmpll",
+ "net1pll_d4", "net1pll_d5", "mpll" };
+static const char *const eip197_parents[] = { "top_xtal", "netsyspll", "net2pll",
+ "mmpll", "net1pll_d4", "net1pll_d5" };
+static const char *const axi_infra_parents[] = { "top_xtal", "net1pll_d8_d2" };
+static const char *const uart_parents[] = { "top_xtal", "mpll_d8", "mpll_d8_d2" };
+static const char *const emmc_250m_parents[] = { "top_xtal", "net1pll_d5_d2", "mmpll_d4" };
+static const char *const emmc_400m_parents[] = { "top_xtal", "msdcpll", "mmpll_d2",
+ "mpll_d2", "mmpll_d4", "net1pll_d8_d2" };
+static const char *const spi_parents[] = { "top_xtal", "mpll_d2", "mmpll_d4",
+ "net1pll_d8_d2", "net2pll_d6", "net1pll_d5_d4",
+ "mpll_d4", "net1pll_d8_d4" };
+static const char *const nfi1x_parents[] = { "top_xtal", "mmpll_d4", "net1pll_d8_d2", "net2pll_d6",
+ "mpll_d4", "mmpll_d8", "net1pll_d8_d4", "mpll_d8" };
+static const char *const spinfi_parents[] = { "top_xtal_d2", "top_xtal", "net1pll_d5_d4",
+ "mpll_d4", "mmpll_d8", "net1pll_d8_d4",
+ "mmpll_d6_d2", "mpll_d8" };
+static const char *const pwm_parents[] = { "top_xtal", "net1pll_d8_d2", "net1pll_d5_d4",
+ "mpll_d4", "mpll_d8_d2", "top_rtc_32k" };
+static const char *const i2c_parents[] = { "top_xtal", "net1pll_d5_d4", "mpll_d4",
+ "net1pll_d8_d4" };
+static const char *const pcie_mbist_250m_parents[] = { "top_xtal", "net1pll_d5_d2" };
+static const char *const pextp_tl_ck_parents[] = { "top_xtal", "net2pll_d6", "mmpll_d8",
+ "mpll_d8_d2", "top_rtc_32k" };
+static const char *const usb_frmcnt_parents[] = { "top_xtal", "mmpll_d3_d5" };
+static const char *const aud_parents[] = { "top_xtal", "apll2" };
+static const char *const a1sys_parents[] = { "top_xtal", "apll2_d4" };
+static const char *const aud_l_parents[] = { "top_xtal", "apll2", "mpll_d8_d2" };
+static const char *const sspxtp_parents[] = { "top_xtal_d2", "mpll_d8_d2" };
+static const char *const usxgmii_sbus_0_parents[] = { "top_xtal", "net1pll_d8_d4" };
+static const char *const sgm_0_parents[] = { "top_xtal", "sgmpll" };
+static const char *const sysapb_parents[] = { "top_xtal", "mpll_d3_d2" };
+static const char *const eth_refck_50m_parents[] = { "top_xtal", "net2pll_d4_d4" };
+static const char *const eth_sys_200m_parents[] = { "top_xtal", "net2pll_d4" };
+static const char *const eth_xgmii_parents[] = { "top_xtal_d2", "net1pll_d8_d8", "net1pll_d8_d16" };
+static const char *const bus_tops_parents[] = { "top_xtal", "net1pll_d5", "net2pll_d2" };
+static const char *const npu_tops_parents[] = { "top_xtal", "net2pll" };
+static const char *const dramc_md32_parents[] = { "top_xtal", "mpll_d2", "wedmcupll" };
+static const char *const da_xtp_glb_p0_parents[] = { "top_xtal", "net2pll_d8" };
+static const char *const mcusys_backup_625m_parents[] = { "top_xtal", "net1pll_d4" };
+static const char *const macsec_parents[] = { "top_xtal", "sgmpll", "net1pll_d8" };
+static const char *const netsys_tops_400m_parents[] = { "top_xtal", "net2pll_d2" };
+static const char *const eth_mii_parents[] = { "top_xtal_d2", "net2pll_d4_d8" };
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_SEL, "netsys_sel", netsys_parents, 0x000, 0x004, 0x008,
+ 0, 2, 7, 0x1c0, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_500M_SEL, "netsys_500m_sel", netsys_500m_parents, 0x000,
+ 0x004, 0x008, 8, 2, 15, 0x1C0, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_2X_SEL, "netsys_2x_sel", netsys_2x_parents, 0x000,
+ 0x004, 0x008, 16, 2, 23, 0x1C0, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_GSW_SEL, "netsys_gsw_sel", netsys_gsw_parents, 0x000,
+ 0x004, 0x008, 24, 2, 31, 0x1C0, 3),
+ /* CLK_CFG_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_GMII_SEL, "eth_gmii_sel", eth_gmii_parents, 0x010, 0x014,
+ 0x018, 0, 1, 7, 0x1C0, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_MCU_SEL, "netsys_mcu_sel", netsys_mcu_parents, 0x010,
+ 0x014, 0x018, 8, 3, 15, 0x1C0, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_PAO_2X_SEL, "netsys_pao_2x_sel", netsys_mcu_parents,
+ 0x010, 0x014, 0x018, 16, 3, 23, 0x1C0, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_EIP197_SEL, "eip197_sel", eip197_parents, 0x010, 0x014, 0x018,
+ 24, 3, 31, 0x1c0, 7),
+ /* CLK_CFG_2 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI_INFRA_SEL, "axi_infra_sel", axi_infra_parents, 0x020,
+ 0x024, 0x028, 0, 1, 7, 0x1C0, 8, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x020, 0x024, 0x028, 8, 2,
+ 15, 0x1c0, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_EMMC_250M_SEL, "emmc_250m_sel", emmc_250m_parents, 0x020,
+ 0x024, 0x028, 16, 2, 23, 0x1C0, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_EMMC_400M_SEL, "emmc_400m_sel", emmc_400m_parents, 0x020,
+ 0x024, 0x028, 24, 3, 31, 0x1C0, 11),
+ /* CLK_CFG_3 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x030, 0x034, 0x038, 0, 3, 7,
+ 0x1c0, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPIM_MST_SEL, "spim_mst_sel", spi_parents, 0x030, 0x034, 0x038,
+ 8, 3, 15, 0x1c0, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NFI1X_SEL, "nfi1x_sel", nfi1x_parents, 0x030, 0x034, 0x038, 16,
+ 3, 23, 0x1c0, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPINFI_SEL, "spinfi_sel", spinfi_parents, 0x030, 0x034, 0x038,
+ 24, 3, 31, 0x1c0, 15),
+ /* CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x040, 0x044, 0x048, 0, 3, 7,
+ 0x1c0, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C_SEL, "i2c_sel", i2c_parents, 0x040, 0x044, 0x048, 8, 2, 15,
+ 0x1c0, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PCIE_MBIST_250M_SEL, "pcie_mbist_250m_sel",
+ pcie_mbist_250m_parents, 0x040, 0x044, 0x048, 16, 1, 23, 0x1C0, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_TL_SEL, "pextp_tl_sel", pextp_tl_ck_parents, 0x040,
+ 0x044, 0x048, 24, 3, 31, 0x1C0, 19),
+ /* CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_TL_P1_SEL, "pextp_tl_p1_sel", pextp_tl_ck_parents, 0x050,
+ 0x054, 0x058, 0, 3, 7, 0x1C0, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_TL_P2_SEL, "pextp_tl_p2_sel", pextp_tl_ck_parents, 0x050,
+ 0x054, 0x058, 8, 3, 15, 0x1C0, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_TL_P3_SEL, "pextp_tl_p3_sel", pextp_tl_ck_parents, 0x050,
+ 0x054, 0x058, 16, 3, 23, 0x1C0, 22),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_SYS_SEL, "usb_sys_sel", eth_gmii_parents, 0x050, 0x054,
+ 0x058, 24, 1, 31, 0x1C0, 23),
+ /* CLK_CFG_6 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_SYS_P1_SEL, "usb_sys_p1_sel", eth_gmii_parents, 0x060,
+ 0x064, 0x068, 0, 1, 7, 0x1C0, 24),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_SEL, "usb_xhci_sel", eth_gmii_parents, 0x060, 0x064,
+ 0x068, 8, 1, 15, 0x1C0, 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_XHCI_P1_SEL, "usb_xhci_p1_sel", eth_gmii_parents, 0x060,
+ 0x064, 0x068, 16, 1, 23, 0x1C0, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_FRMCNT_SEL, "usb_frmcnt_sel", usb_frmcnt_parents, 0x060,
+ 0x064, 0x068, 24, 1, 31, 0x1C0, 27),
+ /* CLK_CFG_7 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_FRMCNT_P1_SEL, "usb_frmcnt_p1_sel", usb_frmcnt_parents,
+ 0x070, 0x074, 0x078, 0, 1, 7, 0x1C0, 28),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_SEL, "aud_sel", aud_parents, 0x070, 0x074, 0x078, 8, 1, 15,
+ 0x1c0, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A1SYS_SEL, "a1sys_sel", a1sys_parents, 0x070, 0x074, 0x078, 16,
+ 1, 23, 0x1c0, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_L_SEL, "aud_l_sel", aud_l_parents, 0x070, 0x074, 0x078, 24,
+ 2, 31, 0x1c4, 0),
+ /* CLK_CFG_8 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A_TUNER_SEL, "a_tuner_sel", a1sys_parents, 0x080, 0x084, 0x088,
+ 0, 1, 7, 0x1c4, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSPXTP_SEL, "sspxtp_sel", sspxtp_parents, 0x080, 0x084, 0x088,
+ 8, 1, 15, 0x1c4, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_PHY_SEL, "usb_phy_sel", sspxtp_parents, 0x080, 0x084,
+ 0x088, 16, 1, 23, 0x1c4, 3),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USXGMII_SBUS_0_SEL, "usxgmii_sbus_0_sel",
+ usxgmii_sbus_0_parents, 0x080, 0x084, 0x088, 24, 1, 31, 0x1C4, 4),
+ /* CLK_CFG_9 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USXGMII_SBUS_1_SEL, "usxgmii_sbus_1_sel",
+ usxgmii_sbus_0_parents, 0x090, 0x094, 0x098, 0, 1, 7, 0x1C4, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_0_SEL, "sgm_0_sel", sgm_0_parents, 0x090, 0x094, 0x098, 8,
+ 1, 15, 0x1c4, 6),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SGM_SBUS_0_SEL, "sgm_sbus_0_sel", usxgmii_sbus_0_parents,
+ 0x090, 0x094, 0x098, 16, 1, 23, 0x1C4, 7, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_1_SEL, "sgm_1_sel", sgm_0_parents, 0x090, 0x094, 0x098, 24,
+ 1, 31, 0x1c4, 8),
+ /* CLK_CFG_10 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SGM_SBUS_1_SEL, "sgm_sbus_1_sel", usxgmii_sbus_0_parents,
+ 0x0a0, 0x0a4, 0x0a8, 0, 1, 7, 0x1C4, 9, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_XFI_PHY_0_XTAL_SEL, "xfi_phy_0_xtal_sel", sspxtp_parents,
+ 0x0a0, 0x0a4, 0x0a8, 8, 1, 15, 0x1C4, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_XFI_PHY_1_XTAL_SEL, "xfi_phy_1_xtal_sel", sspxtp_parents,
+ 0x0a0, 0x0a4, 0x0a8, 16, 1, 23, 0x1C4, 11),
+ /* CLK_CFG_11 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAXI_SEL, "sysaxi_sel", axi_infra_parents, 0x0a0,
+ 0x0a4, 0x0a8, 24, 1, 31, 0x1C4, 12, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAPB_SEL, "sysapb_sel", sysapb_parents, 0x0b0, 0x0b4,
+ 0x0b8, 0, 1, 7, 0x1c4, 13, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_REFCK_50M_SEL, "eth_refck_50m_sel", eth_refck_50m_parents,
+ 0x0b0, 0x0b4, 0x0b8, 8, 1, 15, 0x1C4, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_SYS_200M_SEL, "eth_sys_200m_sel", eth_sys_200m_parents,
+ 0x0b0, 0x0b4, 0x0b8, 16, 1, 23, 0x1C4, 15),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_SYS_SEL, "eth_sys_sel", pcie_mbist_250m_parents, 0x0b0,
+ 0x0b4, 0x0b8, 24, 1, 31, 0x1C4, 16),
+ /* CLK_CFG_12 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_XGMII_SEL, "eth_xgmii_sel", eth_xgmii_parents, 0x0c0,
+ 0x0c4, 0x0c8, 0, 2, 7, 0x1C4, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_BUS_TOPS_SEL, "bus_tops_sel", bus_tops_parents, 0x0c0, 0x0c4,
+ 0x0c8, 8, 2, 15, 0x1C4, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NPU_TOPS_SEL, "npu_tops_sel", npu_tops_parents, 0x0c0, 0x0c4,
+ 0x0c8, 16, 1, 23, 0x1C4, 19),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_SEL, "dramc_sel", sspxtp_parents, 0x0c0, 0x0c4,
+ 0x0c8, 24, 1, 31, 0x1C4, 20, CLK_IS_CRITICAL),
+ /* CLK_CFG_13 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel", dramc_md32_parents,
+ 0x0d0, 0x0d4, 0x0d8, 0, 2, 7, 0x1C4, 21, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_INFRA_F26M_SEL, "csw_infra_f26m_sel", sspxtp_parents,
+ 0x0d0, 0x0d4, 0x0d8, 8, 1, 15, 0x1C4, 22, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_P0_SEL, "pextp_p0_sel", sspxtp_parents, 0x0d0, 0x0d4,
+ 0x0d8, 16, 1, 23, 0x1C4, 23),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_P1_SEL, "pextp_p1_sel", sspxtp_parents, 0x0d0, 0x0d4,
+ 0x0d8, 24, 1, 31, 0x1C4, 24),
+ /* CLK_CFG_14 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_P2_SEL, "pextp_p2_sel", sspxtp_parents, 0x0e0, 0x0e4,
+ 0x0e8, 0, 1, 7, 0x1C4, 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_P3_SEL, "pextp_p3_sel", sspxtp_parents, 0x0e0, 0x0e4,
+ 0x0e8, 8, 1, 15, 0x1C4, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DA_XTP_GLB_P0_SEL, "da_xtp_glb_p0_sel", da_xtp_glb_p0_parents,
+ 0x0e0, 0x0e4, 0x0e8, 16, 1, 23, 0x1C4, 27),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DA_XTP_GLB_P1_SEL, "da_xtp_glb_p1_sel", da_xtp_glb_p0_parents,
+ 0x0e0, 0x0e4, 0x0e8, 24, 1, 31, 0x1C4, 28),
+ /* CLK_CFG_15 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DA_XTP_GLB_P2_SEL, "da_xtp_glb_p2_sel", da_xtp_glb_p0_parents,
+ 0x0f0, 0x0f4, 0x0f8, 0, 1, 7, 0x1C4, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DA_XTP_GLB_P3_SEL, "da_xtp_glb_p3_sel", da_xtp_glb_p0_parents,
+ 0x0f0, 0x0f4, 0x0f8, 8, 1, 15, 0x1C4, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CKM_SEL, "ckm_sel", sspxtp_parents, 0x0F0, 0x0f4, 0x0f8, 16, 1,
+ 23, 0x1c8, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DA_SEL, "da_sel", sspxtp_parents, 0x0f0, 0x0f4, 0x0f8, 24, 1,
+ 31, 0x1C8, 1),
+ /* CLK_CFG_16 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_SEL, "pextp_sel", sspxtp_parents, 0x0100, 0x104, 0x108,
+ 0, 1, 7, 0x1c8, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_TOPS_P2_26M_SEL, "tops_p2_26m_sel", sspxtp_parents, 0x0100,
+ 0x104, 0x108, 8, 1, 15, 0x1C8, 3),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MCUSYS_BACKUP_625M_SEL, "mcusys_backup_625m_sel",
+ mcusys_backup_625m_parents, 0x0100, 0x104, 0x108, 16, 1, 23, 0x1C8, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_SYNC_250M_SEL, "netsys_sync_250m_sel",
+ pcie_mbist_250m_parents, 0x0100, 0x104, 0x108, 24, 1, 31, 0x1c8, 5),
+ /* CLK_CFG_17 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MACSEC_SEL, "macsec_sel", macsec_parents, 0x0110, 0x114, 0x118,
+ 0, 2, 7, 0x1c8, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_TOPS_400M_SEL, "netsys_tops_400m_sel",
+ netsys_tops_400m_parents, 0x0110, 0x114, 0x118, 8, 1, 15, 0x1c8, 7),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_PPEFB_250M_SEL, "netsys_ppefb_250m_sel",
+ pcie_mbist_250m_parents, 0x0110, 0x114, 0x118, 16, 1, 23, 0x1c8, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_WARP_SEL, "netsys_warp_sel", netsys_parents, 0x0110,
+ 0x114, 0x118, 24, 2, 31, 0x1C8, 9),
+ /* CLK_CFG_18 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_MII_SEL, "eth_mii_sel", eth_mii_parents, 0x0120, 0x124,
+ 0x128, 0, 1, 7, 0x1c8, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NPU_SEL, "ck_npu_sel", netsys_2x_parents, 0x0120, 0x124, 0x128,
+ 8, 2, 15, 0x1c8, 11),
+};
+
+static const struct mtk_composite top_aud_divs[] = {
+ DIV_GATE(CLK_TOP_AUD_I2S_M, "aud_i2s_m", "aud_sel", 0x0420, 0, 0x0420, 8, 8),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_aud_divs,
+ .num_composite_clks = ARRAY_SIZE(top_aud_divs),
+ .clk_lock = &mt7988_clk_lock,
+};
+
+static const char *const mcu_bus_div_parents[] = { "top_xtal", "ccipll2_b", "net1pll_d4" };
+
+static const char *const mcu_arm_div_parents[] = { "top_xtal", "arm_b", "net1pll_d4" };
+
+static struct mtk_composite mcu_muxes[] = {
+ /* bus_pll_divider_cfg */
+ MUX_GATE_FLAGS(CLK_MCU_BUS_DIV_SEL, "mcu_bus_div_sel", mcu_bus_div_parents, 0x7C0, 9, 2, -1,
+ CLK_IS_CRITICAL),
+ /* mp2_pll_divider_cfg */
+ MUX_GATE_FLAGS(CLK_MCU_ARM_DIV_SEL, "mcu_arm_div_sel", mcu_arm_div_parents, 0x7A8, 9, 2, -1,
+ CLK_IS_CRITICAL),
+};
+
+static const struct mtk_clk_desc mcusys_desc = {
+ .composite_clks = mcu_muxes,
+ .num_composite_clks = ARRAY_SIZE(mcu_muxes),
+};
+
+static const struct of_device_id of_match_clk_mt7988_topckgen[] = {
+ { .compatible = "mediatek,mt7988-topckgen", .data = &topck_desc },
+ { .compatible = "mediatek,mt7988-mcusys", .data = &mcusys_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7988_topckgen);
+
+static struct platform_driver clk_mt7988_topckgen_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt7988-topckgen",
+ .of_match_table = of_match_clk_mt7988_topckgen,
+ },
+};
+module_platform_driver(clk_mt7988_topckgen_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7988 top clock generators driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt7988-xfipll.c b/drivers/clk/mediatek/clk-mt7988-xfipll.c
new file mode 100644
index 000000000000..f941e4d3ef28
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt7988-xfipll.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include <dt-bindings/clock/mediatek,mt7988-clk.h>
+
+/* Register to control USXGMII XFI PLL analog */
+#define XFI_PLL_ANA_GLB8 0x108
+#define RG_XFI_PLL_ANA_SWWA 0x02283248
+
+static const struct mtk_gate_regs xfipll_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x8,
+};
+
+#define GATE_XFIPLL(_id, _name, _parent, _shift) \
+ { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &xfipll_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_fixed_factor xfipll_divs[] = {
+ FACTOR(CLK_XFIPLL_PLL, "xfipll_pll", "top_xtal", 125, 32),
+};
+
+static const struct mtk_gate xfipll_clks[] = {
+ GATE_XFIPLL(CLK_XFIPLL_PLL_EN, "xfipll_pll_en", "xfipll_pll", 31),
+};
+
+static const struct mtk_clk_desc xfipll_desc = {
+ .clks = xfipll_clks,
+ .num_clks = ARRAY_SIZE(xfipll_clks),
+ .factor_clks = xfipll_divs,
+ .num_factor_clks = ARRAY_SIZE(xfipll_divs),
+};
+
+static int clk_mt7988_xfipll_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base = of_iomap(node, 0);
+
+ if (!base)
+ return -ENOMEM;
+
+ /* Apply software workaround for USXGMII PLL TCL issue */
+ writel(RG_XFI_PLL_ANA_SWWA, base + XFI_PLL_ANA_GLB8);
+ iounmap(base);
+
+ return mtk_clk_simple_probe(pdev);
+};
+
+static const struct of_device_id of_match_clk_mt7988_xfipll[] = {
+ { .compatible = "mediatek,mt7988-xfi-pll", .data = &xfipll_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt7988_xfipll);
+
+static struct platform_driver clk_mt7988_xfipll_drv = {
+ .driver = {
+ .name = "clk-mt7988-xfipll",
+ .of_match_table = of_match_clk_mt7988_xfipll,
+ },
+ .probe = clk_mt7988_xfipll_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt7988_xfipll_drv);
+
+MODULE_DESCRIPTION("MediaTek MT7988 XFI PLL clock driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8135-apmixedsys.c b/drivers/clk/mediatek/clk-mt8135-apmixedsys.c
new file mode 100644
index 000000000000..bdadc35c64cb
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8135-apmixedsys.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * James Liao <jamesjj.liao@mediatek.com>
+ * Copyright (c) 2023 Collabora, Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8135-clk.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#define MT8135_PLL_FMAX (2000 * MHZ)
+#define CON0_MT8135_RST_BAR BIT(27)
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT8135_RST_BAR, \
+ .fmax = MT8135_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ }
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000000, 0, 21, 0x204, 24, 0x0, 0x204, 0),
+ PLL(CLK_APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000000, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000000, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000000, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000000, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000000, 0, 21, 0x278, 6, 0x0, 0x27c, 0),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000000, 0, 31, 0x294, 6, 0x0, 0x298, 0),
+ PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000000, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0),
+ PLL(CLK_APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000000, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0),
+ PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000000, 0, 21, 0x2b0, 6, 0x0, 0x308, 0),
+};
+
+static int clk_mt8135_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ int ret;
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (ret)
+ goto free_clk_data;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_plls;
+
+ return 0;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+
+ return ret;
+}
+
+static void clk_mt8135_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static const struct of_device_id of_match_clk_mt8135_apmixed[] = {
+ { .compatible = "mediatek,mt8135-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8135_apmixed);
+
+static struct platform_driver clk_mt8135_apmixed_drv = {
+ .probe = clk_mt8135_apmixed_probe,
+ .remove = clk_mt8135_apmixed_remove,
+ .driver = {
+ .name = "clk-mt8135-apmixed",
+ .of_match_table = of_match_clk_mt8135_apmixed,
+ },
+};
+module_platform_driver(clk_mt8135_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT8135 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c
index b68888a034c4..084e48a554c2 100644
--- a/drivers/clk/mediatek/clk-mt8135.c
+++ b/drivers/clk/mediatek/clk-mt8135.c
@@ -5,8 +5,10 @@
*/
#include <linux/clk.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mfd/syscon.h>
#include <dt-bindings/clock/mt8135-clk.h>
@@ -17,14 +19,13 @@
static DEFINE_SPINLOCK(mt8135_clk_lock);
-static const struct mtk_fixed_factor root_clk_alias[] __initconst = {
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_DUMMY, "top_divs_dummy", "clk_null", 1, 1),
FACTOR(CLK_TOP_DSI0_LNTC_DSICLK, "dsi0_lntc_dsiclk", "clk_null", 1, 1),
FACTOR(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_clkdig_cts", "clk_null", 1, 1),
FACTOR(CLK_TOP_CLKPH_MCK, "clkph_mck", "clk_null", 1, 1),
FACTOR(CLK_TOP_CPUM_TCK_IN, "cpum_tck_in", "clk_null", 1, 1),
-};
-static const struct mtk_fixed_factor top_divs[] __initconst = {
FACTOR(CLK_TOP_MAINPLL_806M, "mainpll_806m", "mainpll", 1, 2),
FACTOR(CLK_TOP_MAINPLL_537P3M, "mainpll_537p3m", "mainpll", 1, 3),
FACTOR(CLK_TOP_MAINPLL_322P4M, "mainpll_322p4m", "mainpll", 1, 5),
@@ -100,7 +101,7 @@ static const struct mtk_fixed_factor top_divs[] __initconst = {
FACTOR(CLK_TOP_MEMPLL_MCK_D4, "mempll_mck_d4", "clkph_mck", 1, 4),
};
-static const char * const axi_parents[] __initconst = {
+static const char * const axi_parents[] = {
"clk26m",
"syspll_d3",
"syspll_d4",
@@ -110,7 +111,7 @@ static const char * const axi_parents[] __initconst = {
"syspll_d3p5"
};
-static const char * const smi_parents[] __initconst = {
+static const char * const smi_parents[] = {
"clk26m",
"clkph_mck",
"syspll_d2p5",
@@ -128,7 +129,7 @@ static const char * const smi_parents[] __initconst = {
"lvdspll"
};
-static const char * const mfg_parents[] __initconst = {
+static const char * const mfg_parents[] = {
"clk26m",
"univpll1_d4",
"syspll_d2",
@@ -144,13 +145,13 @@ static const char * const mfg_parents[] __initconst = {
"mmpll_d7"
};
-static const char * const irda_parents[] __initconst = {
+static const char * const irda_parents[] = {
"clk26m",
"univpll2_d8",
"univpll1_d6"
};
-static const char * const cam_parents[] __initconst = {
+static const char * const cam_parents[] = {
"clk26m",
"syspll_d3",
"syspll_d3p5",
@@ -161,13 +162,13 @@ static const char * const cam_parents[] __initconst = {
"univpll1_d4"
};
-static const char * const aud_intbus_parents[] __initconst = {
+static const char * const aud_intbus_parents[] = {
"clk26m",
"syspll_d6",
"univpll_d10"
};
-static const char * const jpg_parents[] __initconst = {
+static const char * const jpg_parents[] = {
"clk26m",
"syspll_d5",
"syspll_d4",
@@ -177,7 +178,7 @@ static const char * const jpg_parents[] __initconst = {
"univpll_d5"
};
-static const char * const disp_parents[] __initconst = {
+static const char * const disp_parents[] = {
"clk26m",
"syspll_d3p5",
"syspll_d3",
@@ -188,7 +189,7 @@ static const char * const disp_parents[] __initconst = {
"vdecpll"
};
-static const char * const msdc30_parents[] __initconst = {
+static const char * const msdc30_parents[] = {
"clk26m",
"syspll_d6",
"syspll_d5",
@@ -197,13 +198,13 @@ static const char * const msdc30_parents[] __initconst = {
"msdcpll"
};
-static const char * const usb20_parents[] __initconst = {
+static const char * const usb20_parents[] = {
"clk26m",
"univpll2_d6",
"univpll1_d10"
};
-static const char * const venc_parents[] __initconst = {
+static const char * const venc_parents[] = {
"clk26m",
"syspll_d3",
"syspll_d8",
@@ -214,7 +215,7 @@ static const char * const venc_parents[] __initconst = {
"mmpll_d6"
};
-static const char * const spi_parents[] __initconst = {
+static const char * const spi_parents[] = {
"clk26m",
"syspll_d6",
"syspll_d8",
@@ -223,17 +224,17 @@ static const char * const spi_parents[] __initconst = {
"univpll1_d8"
};
-static const char * const uart_parents[] __initconst = {
+static const char * const uart_parents[] = {
"clk26m",
"univpll2_d8"
};
-static const char * const mem_parents[] __initconst = {
+static const char * const mem_parents[] = {
"clk26m",
"clkph_mck"
};
-static const char * const camtg_parents[] __initconst = {
+static const char * const camtg_parents[] = {
"clk26m",
"univpll_d26",
"univpll1_d6",
@@ -241,12 +242,12 @@ static const char * const camtg_parents[] __initconst = {
"syspll_d8"
};
-static const char * const audio_parents[] __initconst = {
+static const char * const audio_parents[] = {
"clk26m",
"syspll_d24"
};
-static const char * const fix_parents[] __initconst = {
+static const char * const fix_parents[] = {
"rtc32k",
"clk26m",
"univpll_d5",
@@ -257,7 +258,7 @@ static const char * const fix_parents[] __initconst = {
"univpll1_d8"
};
-static const char * const vdec_parents[] __initconst = {
+static const char * const vdec_parents[] = {
"clk26m",
"vdecpll",
"clkph_mck",
@@ -276,13 +277,13 @@ static const char * const vdec_parents[] __initconst = {
"lvdspll"
};
-static const char * const ddrphycfg_parents[] __initconst = {
+static const char * const ddrphycfg_parents[] = {
"clk26m",
"axi_sel",
"syspll_d12"
};
-static const char * const dpilvds_parents[] __initconst = {
+static const char * const dpilvds_parents[] = {
"clk26m",
"lvdspll",
"lvdspll_d2",
@@ -290,7 +291,7 @@ static const char * const dpilvds_parents[] __initconst = {
"lvdspll_d8"
};
-static const char * const pmicspi_parents[] __initconst = {
+static const char * const pmicspi_parents[] = {
"clk26m",
"univpll2_d6",
"syspll_d8",
@@ -301,14 +302,14 @@ static const char * const pmicspi_parents[] __initconst = {
"syspll_d24"
};
-static const char * const smi_mfg_as_parents[] __initconst = {
+static const char * const smi_mfg_as_parents[] = {
"clk26m",
"smi_sel",
"mfg_sel",
"mem_sel"
};
-static const char * const gcpu_parents[] __initconst = {
+static const char * const gcpu_parents[] = {
"clk26m",
"syspll_d4",
"univpll_d7",
@@ -316,14 +317,14 @@ static const char * const gcpu_parents[] __initconst = {
"syspll_d6"
};
-static const char * const dpi1_parents[] __initconst = {
+static const char * const dpi1_parents[] = {
"clk26m",
"tvhdmi_h_ck",
"tvhdmi_d2",
"tvhdmi_d4"
};
-static const char * const cci_parents[] __initconst = {
+static const char * const cci_parents[] = {
"clk26m",
"mainpll_537p3m",
"univpll_d3",
@@ -332,7 +333,7 @@ static const char * const cci_parents[] __initconst = {
"syspll_d5"
};
-static const char * const apll_parents[] __initconst = {
+static const char * const apll_parents[] = {
"clk26m",
"apll_ck",
"apll_d4",
@@ -341,14 +342,14 @@ static const char * const apll_parents[] __initconst = {
"apll_d24"
};
-static const char * const hdmipll_parents[] __initconst = {
+static const char * const hdmipll_parents[] = {
"clk26m",
"hdmitx_clkdig_cts",
"hdmitx_clkdig_d2",
"hdmitx_clkdig_d3"
};
-static const struct mtk_composite top_muxes[] __initconst = {
+static const struct mtk_composite top_muxes[] = {
/* CLK_CFG_0 */
MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
0x0140, 0, 3, INVALID_MUX_GATE_BIT),
@@ -390,7 +391,7 @@ static const struct mtk_composite top_muxes[] __initconst = {
MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0164, 24, 3, 31),
/* CLK_CFG_9 */
MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0168, 0, 2, 7),
- MUX_GATE(CLK_TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15),
+ MUX_GATE_FLAGS(CLK_TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15, CLK_IS_CRITICAL),
MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x0168, 16, 3, 23),
MUX_GATE(CLK_TOP_HDMIPLL_SEL, "hdmipll_sel", hdmipll_parents, 0x0168, 24, 2, 31),
};
@@ -401,23 +402,22 @@ static const struct mtk_gate_regs infra_cg_regs = {
.sta_ofs = 0x0048,
};
-#define GATE_ICG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_ICG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_ICG_AO(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_IS_CRITICAL)
-static const struct mtk_gate infra_clks[] __initconst = {
+static const struct mtk_gate infra_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "infra_dummy"),
GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23),
GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22),
GATE_ICG(CLK_INFRA_CCIF1_AP_CTRL, "ccif1_ap_ctrl", "axi_sel", 21),
GATE_ICG(CLK_INFRA_CCIF0_AP_CTRL, "ccif0_ap_ctrl", "axi_sel", 20),
GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16),
GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15),
- GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8),
+ GATE_ICG_AO(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8),
GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7),
GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6),
GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5),
@@ -438,25 +438,14 @@ static const struct mtk_gate_regs peri1_cg_regs = {
.sta_ofs = 0x001c,
};
-#define GATE_PERI0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_PERI0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_PERI1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_PERI1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-static const struct mtk_gate peri_gates[] __initconst = {
+static const struct mtk_gate peri_gates[] = {
+ GATE_DUMMY(CLK_DUMMY, "peri_dummy"),
/* PERI0 */
GATE_PERI0(CLK_PERI_I2C5, "i2c5_ck", "axi_sel", 31),
GATE_PERI0(CLK_PERI_I2C4, "i2c4_ck", "axi_sel", 30),
@@ -502,12 +491,12 @@ static const struct mtk_gate peri_gates[] __initconst = {
GATE_PERI1(CLK_PERI_I2C6, "i2c6_ck", "axi_sel", 0),
};
-static const char * const uart_ck_sel_parents[] __initconst = {
+static const char * const uart_ck_sel_parents[] = {
"clk26m",
"uart_sel",
};
-static const struct mtk_composite peri_clks[] __initconst = {
+static const struct mtk_composite peri_clks[] = {
MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
@@ -532,125 +521,46 @@ static const struct mtk_clk_rst_desc clk_rst_desc[] = {
}
};
-static void __init mtk_topckgen_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- int r;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
-
- mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data);
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt8135_clk_lock, clk_data);
-
- clk_prepare_enable(clk_data->hws[CLK_TOP_CCI_SEL]->clk);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init);
-
-static void __init mtk_infrasys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
-
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
-
- clk_prepare_enable(clk_data->hws[CLK_INFRA_M4U]->clk);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .rst_desc = &clk_rst_desc[0],
+};
- mtk_register_reset_controller(node, &clk_rst_desc[0]);
-}
-CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init);
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_gates,
+ .num_clks = ARRAY_SIZE(peri_gates),
+ .composite_clks = peri_clks,
+ .num_composite_clks = ARRAY_SIZE(peri_clks),
+ .clk_lock = &mt8135_clk_lock,
+ .rst_desc = &clk_rst_desc[1],
+};
-static void __init mtk_pericfg_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- void __iomem *base;
+static const struct mtk_clk_desc topck_desc = {
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .clk_lock = &mt8135_clk_lock,
+};
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
+static const struct of_device_id of_match_clk_mt8135[] = {
+ { .compatible = "mediatek,mt8135-infracfg", .data = &infra_desc },
+ { .compatible = "mediatek,mt8135-pericfg", .data = &peri_desc },
+ { .compatible = "mediatek,mt8135-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8135);
- clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
-
- mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates),
- clk_data);
- mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base,
- &mt8135_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_register_reset_controller(node, &clk_rst_desc[1]);
-}
-CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init);
-
-#define MT8135_PLL_FMAX (2000 * MHZ)
-#define CON0_MT8135_RST_BAR BIT(27)
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = CON0_MT8135_RST_BAR, \
- .fmax = MT8135_PLL_FMAX, \
- .pcwbits = _pcwbits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- }
+static struct platform_driver clk_mt8135_drv = {
+ .driver = {
+ .name = "clk-mt8135",
+ .of_match_table = of_match_clk_mt8135,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt8135_drv);
-static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000000, 0, 21, 0x204, 24, 0x0, 0x204, 0),
- PLL(CLK_APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000000, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0),
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000000, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0),
- PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000000, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9),
- PLL(CLK_APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000000, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0),
- PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000000, 0, 21, 0x278, 6, 0x0, 0x27c, 0),
- PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000000, 0, 31, 0x294, 6, 0x0, 0x298, 0),
- PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000000, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0),
- PLL(CLK_APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000000, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0),
- PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000000, 0, 21, 0x2b0, 6, 0x0, 0x308, 0),
-};
-
-static void __init mtk_apmixedsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
-
- clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
- if (!clk_data)
- return;
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
-}
-CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys",
- mtk_apmixedsys_init);
+MODULE_DESCRIPTION("MediaTek MT8135 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167-apmixedsys.c b/drivers/clk/mediatek/clk-mt8167-apmixedsys.c
new file mode 100644
index 000000000000..adf576786696
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8167-apmixedsys.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ * Copyright (c) 2020 BayLibre, SAS
+ * Copyright (c) 2023 Collabora, Ltd.
+ */
+
+#include <dt-bindings/clock/mt8167-clk.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-pll.h"
+#include "clk-mtk.h"
+
+static DEFINE_SPINLOCK(mt8167_apmixed_clk_lock);
+
+#define MT8167_PLL_FMAX (2500UL * MHZ)
+
+#define CON0_MT8167_RST_BAR BIT(27)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift, _div_table) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT8167_RST_BAR, \
+ .fmax = MT8167_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
+ NULL)
+
+static const struct mtk_pll_div_table mmpll_div_table[] = {
+ { .div = 0, .freq = MT8167_PLL_FMAX },
+ { .div = 1, .freq = 1000000000 },
+ { .div = 2, .freq = 604500000 },
+ { .div = 3, .freq = 253500000 },
+ { .div = 4, .freq = 126750000 },
+ { /* sentinel */ }
+};
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0100, 0x0110, 0, 0,
+ 21, 0x0104, 24, 0, 0x0104, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0120, 0x0130, 0,
+ HAVE_RST_BAR, 21, 0x0124, 24, 0, 0x0124, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0140, 0x0150, 0x30000000,
+ HAVE_RST_BAR, 7, 0x0144, 24, 0, 0x0144, 0),
+ PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0160, 0x0170, 0, 0,
+ 21, 0x0164, 24, 0, 0x0164, 0, mmpll_div_table),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x0180, 0x0190, 0, 0,
+ 31, 0x0180, 1, 0x0194, 0x0184, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x01A0, 0x01B0, 0, 0,
+ 31, 0x01A0, 1, 0x01B4, 0x01A4, 0),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x01C0, 0x01D0, 0, 0,
+ 21, 0x01C4, 24, 0, 0x01C4, 0),
+ PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x01E0, 0x01F0, 0, 0,
+ 21, 0x01E4, 24, 0, 0x01E4, 0),
+};
+
+#define DIV_ADJ_FLAG(_id, _name, _parent, _reg, _shift, _width, _flag) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .div_reg = _reg, \
+ .div_shift = _shift, \
+ .div_width = _width, \
+ .clk_divider_flags = _flag, \
+}
+
+static const struct mtk_clk_divider adj_divs[] = {
+ DIV_ADJ_FLAG(CLK_APMIXED_HDMI_REF, "hdmi_ref", "tvdpll",
+ 0x1c4, 24, 3, CLK_DIVIDER_POWER_OF_TWO),
+};
+
+static int clk_mt8167_apmixed_probe(struct platform_device *pdev)
+{
+ void __iomem *base;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_devm_alloc_clk_data(dev, MT8167_CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (ret)
+ return ret;
+
+ ret = mtk_clk_register_dividers(dev, adj_divs, ARRAY_SIZE(adj_divs), base,
+ &mt8167_apmixed_clk_lock, clk_data);
+ if (ret)
+ goto unregister_plls;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_dividers;
+
+ return 0;
+
+unregister_dividers:
+ mtk_clk_unregister_dividers(adj_divs, ARRAY_SIZE(adj_divs), clk_data);
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+
+ return ret;
+}
+
+static const struct of_device_id of_match_clk_mt8167_apmixed[] = {
+ { .compatible = "mediatek,mt8167-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8167_apmixed);
+
+static struct platform_driver clk_mt8167_apmixed_drv = {
+ .probe = clk_mt8167_apmixed_probe,
+ .driver = {
+ .name = "clk-mt8167-apmixed",
+ .of_match_table = of_match_clk_mt8167_apmixed,
+ },
+};
+builtin_platform_driver(clk_mt8167_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT8167 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167-aud.c b/drivers/clk/mediatek/clk-mt8167-aud.c
index ce1ae8d243c3..d6cff4bdf4cb 100644
--- a/drivers/clk/mediatek/clk-mt8167-aud.c
+++ b/drivers/clk/mediatek/clk-mt8167-aud.c
@@ -7,9 +7,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -23,16 +21,11 @@ static const struct mtk_gate_regs aud_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_AUD(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &aud_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUD(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &aud_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-static const struct mtk_gate aud_clks[] __initconst = {
+
+static const struct mtk_gate aud_clks[] = {
GATE_AUD(CLK_AUD_AFE, "aud_afe", "clk26m_ck", 2),
GATE_AUD(CLK_AUD_I2S, "aud_i2s", "i2s_infra_bck", 6),
GATE_AUD(CLK_AUD_22M, "aud_22m", "rg_aud_engen1", 8),
@@ -48,19 +41,26 @@ static const struct mtk_gate aud_clks[] __initconst = {
GATE_AUD(CLK_AUD_TML, "aud_tml", "aud_afe", 27),
};
-static void __init mtk_audsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_AUD_NR_CLK);
+static const struct mtk_clk_desc aud_desc = {
+ .clks = aud_clks,
+ .num_clks = ARRAY_SIZE(aud_clks),
+};
- mtk_clk_register_gates(node, aud_clks, ARRAY_SIZE(aud_clks), clk_data);
+static const struct of_device_id of_match_clk_mt8167_audsys[] = {
+ { .compatible = "mediatek,mt8167-audsys", .data = &aud_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8167_audsys);
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static struct platform_driver clk_mt8167_audsys_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8167-audsys",
+ .of_match_table = of_match_clk_mt8167_audsys,
+ },
+};
+module_platform_driver(clk_mt8167_audsys_drv);
-}
-CLK_OF_DECLARE(mtk_audsys, "mediatek,mt8167-audsys", mtk_audsys_init);
+MODULE_DESCRIPTION("MediaTek MT8167 audio clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167-img.c b/drivers/clk/mediatek/clk-mt8167-img.c
index e359e563d2b7..42d38ae94b69 100644
--- a/drivers/clk/mediatek/clk-mt8167-img.c
+++ b/drivers/clk/mediatek/clk-mt8167-img.c
@@ -7,9 +7,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -23,16 +21,10 @@ static const struct mtk_gate_regs img_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_IMG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &img_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_IMG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-static const struct mtk_gate img_clks[] __initconst = {
+static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_LARB1_SMI, "img_larb1_smi", "smi_mm", 0),
GATE_IMG(CLK_IMG_CAM_SMI, "img_cam_smi", "smi_mm", 5),
GATE_IMG(CLK_IMG_CAM_CAM, "img_cam_cam", "smi_mm", 6),
@@ -41,20 +33,26 @@ static const struct mtk_gate img_clks[] __initconst = {
GATE_IMG(CLK_IMG_VENC, "img_venc", "smi_mm", 9),
};
-static void __init mtk_imgsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
+};
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static const struct of_device_id of_match_clk_mt8167_imgsys[] = {
+ { .compatible = "mediatek,mt8167-imgsys", .data = &img_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8167_imgsys);
+
+static struct platform_driver clk_mt8167_imgsys_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8167-imgsys",
+ .of_match_table = of_match_clk_mt8167_imgsys,
+ },
+};
+module_platform_driver(clk_mt8167_imgsys_drv);
-}
-CLK_OF_DECLARE(mtk_imgsys, "mediatek,mt8167-imgsys", mtk_imgsys_init);
+MODULE_DESCRIPTION("MediaTek MT8167 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167-mfgcfg.c b/drivers/clk/mediatek/clk-mt8167-mfgcfg.c
index 4fd82fe87d6e..1ef37a3e6851 100644
--- a/drivers/clk/mediatek/clk-mt8167-mfgcfg.c
+++ b/drivers/clk/mediatek/clk-mt8167-mfgcfg.c
@@ -7,9 +7,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -23,36 +21,36 @@ static const struct mtk_gate_regs mfg_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_MFG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mfg_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-static const struct mtk_gate mfg_clks[] __initconst = {
+static const struct mtk_gate mfg_clks[] = {
GATE_MFG(CLK_MFG_BAXI, "mfg_baxi", "ahb_infra_sel", 0),
GATE_MFG(CLK_MFG_BMEM, "mfg_bmem", "gfmux_emi1x_sel", 1),
GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_mm", 2),
GATE_MFG(CLK_MFG_B26M, "mfg_b26m", "clk26m_ck", 3),
};
-static void __init mtk_mfgcfg_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_MFG_NR_CLK);
-
- mtk_clk_register_gates(node, mfg_clks, ARRAY_SIZE(mfg_clks), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+static const struct mtk_clk_desc mfg_desc = {
+ .clks = mfg_clks,
+ .num_clks = ARRAY_SIZE(mfg_clks),
+};
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static const struct of_device_id of_match_clk_mt8167_mfgcfg[] = {
+ { .compatible = "mediatek,mt8167-mfgcfg", .data = &mfg_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8167_mfgcfg);
+
+static struct platform_driver clk_mt8167_mfgcfg_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8167-mfgcfg",
+ .of_match_table = of_match_clk_mt8167_mfgcfg,
+ },
+};
+module_platform_driver(clk_mt8167_mfgcfg_drv);
-}
-CLK_OF_DECLARE(mtk_mfgcfg, "mediatek,mt8167-mfgcfg", mtk_mfgcfg_init);
+MODULE_DESCRIPTION("MediaTek MT8167 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167-mm.c b/drivers/clk/mediatek/clk-mt8167-mm.c
index 73910060577f..cef66ee836f3 100644
--- a/drivers/clk/mediatek/clk-mt8167-mm.c
+++ b/drivers/clk/mediatek/clk-mt8167-mm.c
@@ -7,9 +7,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -29,23 +27,11 @@ static const struct mtk_gate_regs mm1_cg_regs = {
.sta_ofs = 0x110,
};
-#define GATE_MM0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_MM1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mm_clks[] = {
/* MM0 */
@@ -86,47 +72,26 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM1(CLK_MM_HDMI_PLL, "mm_hdmi_pll", "hdmtx_dig_cts", 21),
};
-struct clk_mt8167_mm_driver_data {
- const struct mtk_gate *gates_clk;
- int gates_num;
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
};
-static const struct clk_mt8167_mm_driver_data mt8167_mmsys_driver_data = {
- .gates_clk = mm_clks,
- .gates_num = ARRAY_SIZE(mm_clks),
+static const struct platform_device_id clk_mt8167_mm_id_table[] = {
+ { .name = "clk-mt8167-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(platform, clk_mt8167_mm_id_table);
-static int clk_mt8167_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- const struct clk_mt8167_mm_driver_data *data;
- struct clk_hw_onecell_data *clk_data;
- int ret;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- data = &mt8167_mmsys_driver_data;
-
- ret = mtk_clk_register_gates(node, data->gates_clk, data->gates_num,
- clk_data);
- if (ret)
- return ret;
-
- ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static struct platform_driver clk_mt8173_mm_drv = {
+static struct platform_driver clk_mt8167_mm_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8167-mm",
},
- .probe = clk_mt8167_mm_probe,
+ .id_table = clk_mt8167_mm_id_table,
};
+module_platform_driver(clk_mt8167_mm_drv);
-builtin_platform_driver(clk_mt8173_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT8167 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167-vdec.c b/drivers/clk/mediatek/clk-mt8167-vdec.c
index ee4fffb6859d..e3769bc556a9 100644
--- a/drivers/clk/mediatek/clk-mt8167-vdec.c
+++ b/drivers/clk/mediatek/clk-mt8167-vdec.c
@@ -7,9 +7,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -29,45 +27,39 @@ static const struct mtk_gate_regs vdec1_cg_regs = {
.sta_ofs = 0x8,
};
-#define GATE_VDEC0_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VDEC0_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
-#define GATE_VDEC1_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
+#define GATE_VDEC1_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
-static const struct mtk_gate vdec_clks[] __initconst = {
+static const struct mtk_gate vdec_clks[] = {
/* VDEC0 */
GATE_VDEC0_I(CLK_VDEC_CKEN, "vdec_cken", "rg_vdec", 0),
/* VDEC1 */
GATE_VDEC1_I(CLK_VDEC_LARB1_CKEN, "vdec_larb1_cken", "smi_mm", 0),
};
-static void __init mtk_vdecsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_NR_CLK);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks), clk_data);
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
+};
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+static const struct of_device_id of_match_clk_mt8167_vdec[] = {
+ { .compatible = "mediatek,mt8167-vdecsys", .data = &vdec_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8167_vdec);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static struct platform_driver clk_mt8167_vdec_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8167-vdecsys",
+ .of_match_table = of_match_clk_mt8167_vdec,
+ },
+};
+module_platform_driver(clk_mt8167_vdec_drv);
-}
-CLK_OF_DECLARE(mtk_vdecsys, "mediatek,mt8167-vdecsys", mtk_vdecsys_init);
+MODULE_DESCRIPTION("MediaTek MT8167 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8167.c b/drivers/clk/mediatek/clk-mt8167.c
index f900ac4bf7b8..c64d918c37de 100644
--- a/drivers/clk/mediatek/clk-mt8167.c
+++ b/drivers/clk/mediatek/clk-mt8167.c
@@ -11,16 +11,16 @@
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
#include "clk-gate.h"
#include "clk-mtk.h"
-#include "clk-pll.h"
#include <dt-bindings/clock/mt8167-clk.h>
static DEFINE_SPINLOCK(mt8167_clk_lock);
-static const struct mtk_fixed_clk fixed_clks[] __initconst = {
+static const struct mtk_fixed_clk fixed_clks[] = {
FIXED_CLK(CLK_TOP_CLK_NULL, "clk_null", NULL, 0),
FIXED_CLK(CLK_TOP_I2S_INFRA_BCK, "i2s_infra_bck", "clk_null", 26000000),
FIXED_CLK(CLK_TOP_MEMPLL, "mempll", "clk26m", 800000000),
@@ -29,7 +29,7 @@ static const struct mtk_fixed_clk fixed_clks[] __initconst = {
FIXED_CLK(CLK_TOP_LVDSTX_CLKDIG_CTS, "lvdstx_dig_cts", "clk26m", 52500000),
};
-static const struct mtk_fixed_factor top_divs[] __initconst = {
+static const struct mtk_fixed_factor top_divs[] = {
FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "mempll", 1, 1),
FACTOR(CLK_TOP_MAINPLL_D2, "mainpll_d2", "mainpll", 1, 2),
FACTOR(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4),
@@ -85,22 +85,22 @@ static const struct mtk_fixed_factor top_divs[] __initconst = {
FACTOR(CLK_TOP_ETH_D2, "eth_d2_ck", "eth_sel", 1, 2),
};
-static const char * const uart0_parents[] __initconst = {
+static const char * const uart0_parents[] = {
"clk26m_ck",
"univpll_d24"
};
-static const char * const gfmux_emi1x_parents[] __initconst = {
+static const char * const gfmux_emi1x_parents[] = {
"clk26m_ck",
"dmpll_ck"
};
-static const char * const emi_ddrphy_parents[] __initconst = {
+static const char * const emi_ddrphy_parents[] = {
"gfmux_emi1x_sel",
"gfmux_emi1x_sel"
};
-static const char * const ahb_infra_parents[] __initconst = {
+static const char * const ahb_infra_parents[] = {
"clk_null",
"clk26m_ck",
"mainpll_d11",
@@ -116,7 +116,7 @@ static const char * const ahb_infra_parents[] __initconst = {
"mainpll_d10"
};
-static const char * const csw_mux_mfg_parents[] __initconst = {
+static const char * const csw_mux_mfg_parents[] = {
"clk_null",
"clk_null",
"univpll_d3",
@@ -127,7 +127,7 @@ static const char * const csw_mux_mfg_parents[] __initconst = {
"mmpll380m"
};
-static const char * const msdc0_parents[] __initconst = {
+static const char * const msdc0_parents[] = {
"clk26m_ck",
"univpll_d6",
"mainpll_d8",
@@ -138,7 +138,7 @@ static const char * const msdc0_parents[] __initconst = {
"mmpll_d2"
};
-static const char * const camtg_mm_parents[] __initconst = {
+static const char * const camtg_mm_parents[] = {
"clk_null",
"clk26m_ck",
"usb_phy48m_ck",
@@ -146,17 +146,17 @@ static const char * const camtg_mm_parents[] __initconst = {
"univpll_d6"
};
-static const char * const pwm_mm_parents[] __initconst = {
+static const char * const pwm_mm_parents[] = {
"clk26m_ck",
"univpll_d12"
};
-static const char * const uart1_parents[] __initconst = {
+static const char * const uart1_parents[] = {
"clk26m_ck",
"univpll_d24"
};
-static const char * const msdc1_parents[] __initconst = {
+static const char * const msdc1_parents[] = {
"clk26m_ck",
"univpll_d6",
"mainpll_d8",
@@ -167,24 +167,24 @@ static const char * const msdc1_parents[] __initconst = {
"mmpll_d2"
};
-static const char * const spm_52m_parents[] __initconst = {
+static const char * const spm_52m_parents[] = {
"clk26m_ck",
"univpll_d24"
};
-static const char * const pmicspi_parents[] __initconst = {
+static const char * const pmicspi_parents[] = {
"univpll_d20",
"usb_phy48m_ck",
"univpll_d16",
"clk26m_ck"
};
-static const char * const qaxi_aud26m_parents[] __initconst = {
+static const char * const qaxi_aud26m_parents[] = {
"clk26m_ck",
"ahb_infra_sel"
};
-static const char * const aud_intbus_parents[] __initconst = {
+static const char * const aud_intbus_parents[] = {
"clk_null",
"clk26m_ck",
"mainpll_d22",
@@ -192,7 +192,7 @@ static const char * const aud_intbus_parents[] __initconst = {
"mainpll_d11"
};
-static const char * const nfi2x_pad_parents[] __initconst = {
+static const char * const nfi2x_pad_parents[] = {
"clk_null",
"clk_null",
"clk_null",
@@ -280,12 +280,12 @@ static const char * const nfi2x_pad_parents[] __initconst = {
"mainpll_d5"
};
-static const char * const nfi1x_pad_parents[] __initconst = {
+static const char * const nfi1x_pad_parents[] = {
"ahb_infra_sel",
"nfi1x_ck"
};
-static const char * const mfg_mm_parents[] __initconst = {
+static const char * const mfg_mm_parents[] = {
"clk_null",
"clk_null",
"clk_null",
@@ -325,12 +325,12 @@ static const char * const mfg_mm_parents[] __initconst = {
"mainpll_d14"
};
-static const char * const ddrphycfg_parents[] __initconst = {
+static const char * const ddrphycfg_parents[] = {
"clk26m_ck",
"mainpll_d16"
};
-static const char * const smi_mm_parents[] __initconst = {
+static const char * const smi_mm_parents[] = {
"clk26m_ck",
"clk_null",
"clk_null",
@@ -346,7 +346,7 @@ static const char * const smi_mm_parents[] __initconst = {
"mainpll_d14"
};
-static const char * const usb_78m_parents[] __initconst = {
+static const char * const usb_78m_parents[] = {
"clk_null",
"clk26m_ck",
"univpll_d16",
@@ -354,7 +354,7 @@ static const char * const usb_78m_parents[] __initconst = {
"mainpll_d20"
};
-static const char * const scam_mm_parents[] __initconst = {
+static const char * const scam_mm_parents[] = {
"clk_null",
"clk26m_ck",
"mainpll_d14",
@@ -362,7 +362,7 @@ static const char * const scam_mm_parents[] __initconst = {
"mainpll_d12"
};
-static const char * const spinor_parents[] __initconst = {
+static const char * const spinor_parents[] = {
"clk26m_d2",
"clk26m_ck",
"mainpll_d40",
@@ -373,7 +373,7 @@ static const char * const spinor_parents[] __initconst = {
"univpll_d12"
};
-static const char * const msdc2_parents[] __initconst = {
+static const char * const msdc2_parents[] = {
"clk26m_ck",
"univpll_d6",
"mainpll_d8",
@@ -384,7 +384,7 @@ static const char * const msdc2_parents[] __initconst = {
"mmpll_d2"
};
-static const char * const eth_parents[] __initconst = {
+static const char * const eth_parents[] = {
"clk26m_ck",
"mainpll_d40",
"univpll_d24",
@@ -392,7 +392,7 @@ static const char * const eth_parents[] __initconst = {
"mainpll_d20"
};
-static const char * const vdec_mm_parents[] __initconst = {
+static const char * const vdec_mm_parents[] = {
"clk26m_ck",
"univpll_d4",
"mainpll_d4",
@@ -401,7 +401,7 @@ static const char * const vdec_mm_parents[] __initconst = {
"mainpll_d6"
};
-static const char * const dpi0_mm_parents[] __initconst = {
+static const char * const dpi0_mm_parents[] = {
"clk26m_ck",
"lvdspll_ck",
"lvdspll_d2",
@@ -409,7 +409,7 @@ static const char * const dpi0_mm_parents[] __initconst = {
"lvdspll_d8"
};
-static const char * const dpi1_mm_parents[] __initconst = {
+static const char * const dpi1_mm_parents[] = {
"clk26m_ck",
"tvdpll_d2",
"tvdpll_d4",
@@ -417,85 +417,85 @@ static const char * const dpi1_mm_parents[] __initconst = {
"tvdpll_d16"
};
-static const char * const axi_mfg_in_parents[] __initconst = {
+static const char * const axi_mfg_in_parents[] = {
"clk26m_ck",
"mainpll_d11",
"univpll_d24",
"mmpll380m"
};
-static const char * const slow_mfg_parents[] __initconst = {
+static const char * const slow_mfg_parents[] = {
"clk26m_ck",
"univpll_d12",
"univpll_d24"
};
-static const char * const aud1_parents[] __initconst = {
+static const char * const aud1_parents[] = {
"clk26m_ck",
"apll1_ck"
};
-static const char * const aud2_parents[] __initconst = {
+static const char * const aud2_parents[] = {
"clk26m_ck",
"apll2_ck"
};
-static const char * const aud_engen1_parents[] __initconst = {
+static const char * const aud_engen1_parents[] = {
"clk26m_ck",
"rg_apll1_d2_en",
"rg_apll1_d4_en",
"rg_apll1_d8_en"
};
-static const char * const aud_engen2_parents[] __initconst = {
+static const char * const aud_engen2_parents[] = {
"clk26m_ck",
"rg_apll2_d2_en",
"rg_apll2_d4_en",
"rg_apll2_d8_en"
};
-static const char * const i2c_parents[] __initconst = {
+static const char * const i2c_parents[] = {
"clk26m_ck",
"univpll_d20",
"univpll_d16",
"univpll_d12"
};
-static const char * const aud_i2s0_m_parents[] __initconst = {
+static const char * const aud_i2s0_m_parents[] = {
"rg_aud1",
"rg_aud2"
};
-static const char * const pwm_parents[] __initconst = {
+static const char * const pwm_parents[] = {
"clk26m_ck",
"univpll_d12"
};
-static const char * const spi_parents[] __initconst = {
+static const char * const spi_parents[] = {
"clk26m_ck",
"univpll_d12",
"univpll_d8",
"univpll_d6"
};
-static const char * const aud_spdifin_parents[] __initconst = {
+static const char * const aud_spdifin_parents[] = {
"clk26m_ck",
"univpll_d2"
};
-static const char * const uart2_parents[] __initconst = {
+static const char * const uart2_parents[] = {
"clk26m_ck",
"univpll_d24"
};
-static const char * const bsi_parents[] __initconst = {
+static const char * const bsi_parents[] = {
"clk26m_ck",
"mainpll_d10",
"mainpll_d12",
"mainpll_d20"
};
-static const char * const dbg_atclk_parents[] __initconst = {
+static const char * const dbg_atclk_parents[] = {
"clk_null",
"clk26m_ck",
"mainpll_d5",
@@ -503,7 +503,7 @@ static const char * const dbg_atclk_parents[] __initconst = {
"univpll_d5"
};
-static const char * const csw_nfiecc_parents[] __initconst = {
+static const char * const csw_nfiecc_parents[] = {
"clk_null",
"mainpll_d7",
"mainpll_d6",
@@ -511,7 +511,7 @@ static const char * const csw_nfiecc_parents[] __initconst = {
"mainpll_d5"
};
-static const char * const nfiecc_parents[] __initconst = {
+static const char * const nfiecc_parents[] = {
"clk_null",
"nfi2x_pad_sel",
"mainpll_d4",
@@ -625,24 +625,24 @@ static struct mtk_composite top_muxes[] __initdata = {
0x07c, 13, 3),
};
-static const char * const ifr_mux1_parents[] __initconst = {
+static const char * const ifr_mux1_parents[] = {
"clk26m_ck",
"armpll",
"univpll",
"mainpll_d2"
};
-static const char * const ifr_eth_25m_parents[] __initconst = {
+static const char * const ifr_eth_25m_parents[] = {
"eth_d2_ck",
"rg_eth"
};
-static const char * const ifr_i2c0_parents[] __initconst = {
+static const char * const ifr_i2c0_parents[] = {
"ahb_infra_d2",
"rg_i2c"
};
-static const struct mtk_composite ifr_muxes[] __initconst = {
+static const struct mtk_composite ifr_muxes[] = {
MUX(CLK_IFR_MUX1_SEL, "ifr_mux1_sel", ifr_mux1_parents, 0x000,
2, 2),
MUX(CLK_IFR_ETH_25M_SEL, "ifr_eth_25m_sel", ifr_eth_25m_parents, 0x080,
@@ -685,21 +685,6 @@ static const struct mtk_clk_divider top_adj_divs[] = {
0x0078, 0, 8),
};
-#define DIV_ADJ_FLAG(_id, _name, _parent, _reg, _shift, _width, _flag) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .div_reg = _reg, \
- .div_shift = _shift, \
- .div_width = _width, \
- .clk_divider_flags = _flag, \
-}
-
-static const struct mtk_clk_divider apmixed_adj_divs[] = {
- DIV_ADJ_FLAG(CLK_APMIXED_HDMI_REF, "hdmi_ref", "tvdpll",
- 0x1c4, 24, 3, CLK_DIVIDER_POWER_OF_TWO),
-};
-
static const struct mtk_gate_regs top0_cg_regs = {
.set_ofs = 0x50,
.clr_ofs = 0x80,
@@ -736,79 +721,31 @@ static const struct mtk_gate_regs top5_cg_regs = {
.sta_ofs = 0x44,
};
-#define GATE_TOP0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP0_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-#define GATE_TOP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP2_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-#define GATE_TOP3(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top3_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP4_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top4_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-#define GATE_TOP5(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top5_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
-
-static const struct mtk_gate top_clks[] __initconst = {
+#define GATE_TOP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP0_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP2_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top2_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_TOP3(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top3_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP4_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top4_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_TOP5(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top5_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
+
+static const struct mtk_gate top_clks[] = {
/* TOP0 */
GATE_TOP0(CLK_TOP_PWM_MM, "pwm_mm", "pwm_mm_sel", 0),
GATE_TOP0(CLK_TOP_CAM_MM, "cam_mm", "camtg_mm_sel", 1),
@@ -921,143 +858,42 @@ static const struct mtk_gate top_clks[] __initconst = {
GATE_TOP5(CLK_TOP_APLL12_DIV6, "apll12_div6", "apll12_ck_div6", 8),
};
-static void __init mtk_topckgen_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- void __iomem *base;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(MT8167_CLK_TOP_NR_CLK);
-
- mtk_clk_register_fixed_clks(fixed_clks, ARRAY_SIZE(fixed_clks),
- clk_data);
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), clk_data);
-
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt8167_clk_lock, clk_data);
- mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs),
- base, &mt8167_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8167-topckgen", mtk_topckgen_init);
-
-static void __init mtk_infracfg_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- void __iomem *base;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(CLK_IFR_NR_CLK);
-
- mtk_clk_register_composites(ifr_muxes, ARRAY_SIZE(ifr_muxes), base,
- &mt8167_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_infracfg, "mediatek,mt8167-infracfg", mtk_infracfg_init);
-
-#define MT8167_PLL_FMAX (2500UL * MHZ)
-
-#define CON0_MT8167_RST_BAR BIT(27)
-
-#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift, _div_table) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = CON0_MT8167_RST_BAR, \
- .fmax = MT8167_PLL_FMAX, \
- .pcwbits = _pcwbits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .div_table = _div_table, \
- }
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift) \
- PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
- NULL)
-
-static const struct mtk_pll_div_table mmpll_div_table[] = {
- { .div = 0, .freq = MT8167_PLL_FMAX },
- { .div = 1, .freq = 1000000000 },
- { .div = 2, .freq = 604500000 },
- { .div = 3, .freq = 253500000 },
- { .div = 4, .freq = 126750000 },
- { } /* sentinel */
+static const struct mtk_clk_desc topck_desc = {
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .fixed_clks = fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .divider_clks = top_adj_divs,
+ .num_divider_clks = ARRAY_SIZE(top_adj_divs),
+ .clk_lock = &mt8167_clk_lock,
};
-static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0100, 0x0110, 0, 0,
- 21, 0x0104, 24, 0, 0x0104, 0),
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0120, 0x0130, 0,
- HAVE_RST_BAR, 21, 0x0124, 24, 0, 0x0124, 0),
- PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0140, 0x0150, 0x30000000,
- HAVE_RST_BAR, 7, 0x0144, 24, 0, 0x0144, 0),
- PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0160, 0x0170, 0, 0,
- 21, 0x0164, 24, 0, 0x0164, 0, mmpll_div_table),
- PLL(CLK_APMIXED_APLL1, "apll1", 0x0180, 0x0190, 0, 0,
- 31, 0x0180, 1, 0x0194, 0x0184, 0),
- PLL(CLK_APMIXED_APLL2, "apll2", 0x01A0, 0x01B0, 0, 0,
- 31, 0x01A0, 1, 0x01B4, 0x01A4, 0),
- PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x01C0, 0x01D0, 0, 0,
- 21, 0x01C4, 24, 0, 0x01C4, 0),
- PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x01E0, 0x01F0, 0, 0,
- 21, 0x01E4, 24, 0, 0x01E4, 0),
+static const struct mtk_clk_desc infra_desc = {
+ .composite_clks = ifr_muxes,
+ .num_composite_clks = ARRAY_SIZE(ifr_muxes),
+ .clk_lock = &mt8167_clk_lock,
};
-static void __init mtk_apmixedsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- int r;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(MT8167_CLK_APMIXED_NR_CLK);
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
- mtk_clk_register_dividers(apmixed_adj_divs, ARRAY_SIZE(apmixed_adj_divs),
- base, &mt8167_clk_lock, clk_data);
+static const struct of_device_id of_match_clk_mt8167[] = {
+ { .compatible = "mediatek,mt8167-topckgen", .data = &topck_desc },
+ { .compatible = "mediatek,mt8167-infracfg", .data = &infra_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8167);
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static struct platform_driver clk_mt8167_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8167",
+ .of_match_table = of_match_clk_mt8167,
+ },
+};
+module_platform_driver(clk_mt8167_drv);
-}
-CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8167-apmixedsys",
- mtk_apmixedsys_init);
+MODULE_DESCRIPTION("MediaTek MTì8167 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-apmixedsys.c b/drivers/clk/mediatek/clk-mt8173-apmixedsys.c
new file mode 100644
index 000000000000..95385bb67d55
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-apmixedsys.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-fhctl.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-pllfh.h"
+
+#define REGOFF_REF2USB 0x8
+#define REGOFF_HDMI_REF 0x40
+
+#define MT8173_PLL_FMAX (3000UL * MHZ)
+
+#define CON0_MT8173_RST_BAR BIT(24)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift, _div_table) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT8173_RST_BAR, \
+ .fmax = MT8173_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
+ NULL)
+
+static const struct mtk_pll_div_table mmpll_div_table[] = {
+ { .div = 0, .freq = MT8173_PLL_FMAX },
+ { .div = 1, .freq = 1000000000 },
+ { .div = 2, .freq = 702000000 },
+ { .div = 3, .freq = 253500000 },
+ { .div = 4, .freq = 126750000 },
+ { } /* sentinel */
+};
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0, PLL_AO,
+ 21, 0x204, 24, 0x0, 0x204, 0),
+ PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0, PLL_AO,
+ 21, 0x214, 24, 0x0, 0x214, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000100, HAVE_RST_BAR, 21,
+ 0x220, 4, 0x0, 0x224, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000000, HAVE_RST_BAR, 7,
+ 0x230, 4, 0x0, 0x234, 14),
+ PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0, 0, 21, 0x244, 24, 0x0,
+ 0x244, 0, mmpll_div_table),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0, 0, 21, 0x250, 4, 0x0, 0x254, 0),
+ PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0, 0, 21, 0x260, 4, 0x0, 0x264, 0),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0, 0, 21, 0x270, 4, 0x0, 0x274, 0),
+ PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0, 0, 21, 0x280, 4, 0x0, 0x284, 0),
+ PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0, 0, 21, 0x290, 4, 0x0, 0x294, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0),
+ PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0),
+ PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0),
+};
+
+enum fh_pll_id {
+ FH_ARMCA7PLL,
+ FH_ARMCA15PLL,
+ FH_MAINPLL,
+ FH_MPLL,
+ FH_MSDCPLL,
+ FH_MMPLL,
+ FH_VENCPLL,
+ FH_TVDPLL,
+ FH_VCODECPLL,
+ FH_LVDSPLL,
+ FH_MSDC2PLL,
+ FH_NR_FH,
+};
+
+#define FH(_pllid, _fhid, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fh_ver = FHCTL_PLLFH_V1, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = 0x6003c97, \
+ .slope1_value = 0x6003c97, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_ARMCA7PLL, FH_ARMCA7PLL, 0x38),
+ FH(CLK_APMIXED_ARMCA15PLL, FH_ARMCA15PLL, 0x4c),
+ FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x60),
+ FH(CLK_APMIXED_MPLL, FH_MPLL, 0x74),
+ FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x88),
+ FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0x9c),
+ FH(CLK_APMIXED_VENCPLL, FH_VENCPLL, 0xb0),
+ FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0xc4),
+ FH(CLK_APMIXED_VCODECPLL, FH_VCODECPLL, 0xd8),
+ FH(CLK_APMIXED_LVDSPLL, FH_LVDSPLL, 0xec),
+ FH(CLK_APMIXED_MSDCPLL2, FH_MSDC2PLL, 0x100),
+};
+
+static const struct of_device_id of_match_clk_mt8173_apmixed[] = {
+ { .compatible = "mediatek,mt8173-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_apmixed);
+
+static int clk_mt8173_apmixed_probe(struct platform_device *pdev)
+{
+ const u8 *fhctl_node = "mediatek,mt8173-fhctl";
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data;
+ void __iomem *base;
+ struct clk_hw *hw;
+ int r;
+
+ base = of_iomap(node, 0);
+ if (!base)
+ return -ENOMEM;
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (IS_ERR_OR_NULL(clk_data)) {
+ r = -ENOMEM;
+ goto unmap_io;
+ }
+
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+ r = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
+ if (r)
+ goto free_clk_data;
+
+ hw = mtk_clk_register_ref2usb_tx("ref2usb_tx", "clk26m", base + REGOFF_REF2USB);
+ if (IS_ERR(hw)) {
+ r = PTR_ERR(hw);
+ dev_err(&pdev->dev, "Failed to register ref2usb_tx: %d\n", r);
+ goto unregister_plls;
+ }
+ clk_data->hws[CLK_APMIXED_REF2USB_TX] = hw;
+
+ hw = devm_clk_hw_register_divider(&pdev->dev, "hdmi_ref", "tvdpll_594m", 0,
+ base + REGOFF_HDMI_REF, 16, 3,
+ CLK_DIVIDER_POWER_OF_TWO, NULL);
+ clk_data->hws[CLK_APMIXED_HDMI_REF] = hw;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_ref2usb;
+
+ return 0;
+
+unregister_ref2usb:
+ mtk_clk_unregister_ref2usb_tx(clk_data->hws[CLK_APMIXED_REF2USB_TX]);
+unregister_plls:
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+unmap_io:
+ iounmap(base);
+ return r;
+}
+
+static void clk_mt8173_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_ref2usb_tx(clk_data->hws[CLK_APMIXED_REF2USB_TX]);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8173_apmixed_drv = {
+ .probe = clk_mt8173_apmixed_probe,
+ .remove = clk_mt8173_apmixed_remove,
+ .driver = {
+ .name = "clk-mt8173-apmixed",
+ .of_match_table = of_match_clk_mt8173_apmixed,
+ },
+};
+module_platform_driver(clk_mt8173_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 apmixed clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-img.c b/drivers/clk/mediatek/clk-mt8173-img.c
new file mode 100644
index 000000000000..6db2b9ab2bc9
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-img.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs img_cg_regs = {
+ .set_ofs = 0x0004,
+ .clr_ofs = 0x0008,
+ .sta_ofs = 0x0000,
+};
+
+#define GATE_IMG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate img_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "img_dummy"),
+ GATE_IMG(CLK_IMG_LARB2_SMI, "img_larb2_smi", "mm_sel", 0),
+ GATE_IMG(CLK_IMG_CAM_SMI, "img_cam_smi", "mm_sel", 5),
+ GATE_IMG(CLK_IMG_CAM_CAM, "img_cam_cam", "mm_sel", 6),
+ GATE_IMG(CLK_IMG_SEN_TG, "img_sen_tg", "camtg_sel", 7),
+ GATE_IMG(CLK_IMG_SEN_CAM, "img_sen_cam", "mm_sel", 8),
+ GATE_IMG(CLK_IMG_CAM_SV, "img_cam_sv", "mm_sel", 9),
+ GATE_IMG(CLK_IMG_FD, "img_fd", "mm_sel", 11),
+};
+
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8173_imgsys[] = {
+ { .compatible = "mediatek,mt8173-imgsys", .data = &img_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_imgsys);
+
+static struct platform_driver clk_mt8173_vdecsys_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8173-imgsys",
+ .of_match_table = of_match_clk_mt8173_imgsys,
+ },
+};
+module_platform_driver(clk_mt8173_vdecsys_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 vdecsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-infracfg.c b/drivers/clk/mediatek/clk-mt8173-infracfg.c
new file mode 100644
index 000000000000..fa2d1d557e04
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-infracfg.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-cpumux.h"
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "reset.h"
+
+#define GATE_ICG(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr)
+
+static struct clk_hw_onecell_data *infra_clk_data;
+
+static const struct mtk_gate_regs infra_cg_regs = {
+ .set_ofs = 0x0040,
+ .clr_ofs = 0x0044,
+ .sta_ofs = 0x0048,
+};
+
+static const char * const ca53_parents[] __initconst = {
+ "clk26m",
+ "armca7pll",
+ "mainpll",
+ "univpll"
+};
+
+static const char * const ca72_parents[] __initconst = {
+ "clk26m",
+ "armca15pll",
+ "mainpll",
+ "univpll"
+};
+
+static const struct mtk_composite cpu_muxes[] = {
+ MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2),
+ MUX(CLK_INFRA_CA72SEL, "infra_ca72_sel", ca72_parents, 0x0000, 2, 2),
+};
+
+static const struct mtk_fixed_factor infra_early_divs[] = {
+ FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2),
+};
+
+static const struct mtk_gate infra_gates[] = {
+ GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0),
+ GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1),
+ GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5),
+ GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6),
+ GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7),
+ GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8),
+ GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "cpum_ck", 15),
+ GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16),
+ GATE_ICG(CLK_INFRA_CEC, "infra_cec", "clk26m", 18),
+ GATE_ICG(CLK_INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22),
+ GATE_ICG(CLK_INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23),
+};
+
+static u16 infrasys_rst_ofs[] = { 0x30, 0x34 };
+
+static const struct mtk_clk_rst_desc clk_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = infrasys_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs),
+};
+
+static const struct of_device_id of_match_clk_mt8173_infracfg[] = {
+ { .compatible = "mediatek,mt8173-infracfg" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_infracfg);
+
+static void clk_mt8173_infra_init_early(struct device_node *node)
+{
+ int i;
+
+ infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
+ if (!infra_clk_data)
+ return;
+
+ for (i = 0; i < CLK_INFRA_NR_CLK; i++)
+ infra_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
+
+ mtk_clk_register_factors(infra_early_divs,
+ ARRAY_SIZE(infra_early_divs), infra_clk_data);
+
+ of_clk_add_hw_provider(node, of_clk_hw_onecell_get, infra_clk_data);
+}
+CLK_OF_DECLARE_DRIVER(mtk_infrasys, "mediatek,mt8173-infracfg",
+ clk_mt8173_infra_init_early);
+
+static int clk_mt8173_infracfg_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ int r, i;
+
+ if (!infra_clk_data) {
+ infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
+ if (!infra_clk_data)
+ return -ENOMEM;
+ } else {
+ for (i = 0; i < CLK_INFRA_NR_CLK; i++)
+ if (infra_clk_data->hws[i] == ERR_PTR(-EPROBE_DEFER))
+ infra_clk_data->hws[i] = ERR_PTR(-ENOENT);
+ }
+
+ r = mtk_clk_register_gates(&pdev->dev, node, infra_gates,
+ ARRAY_SIZE(infra_gates), infra_clk_data);
+ if (r)
+ return r;
+
+ r = mtk_clk_register_cpumuxes(&pdev->dev, node, cpu_muxes,
+ ARRAY_SIZE(cpu_muxes), infra_clk_data);
+ if (r)
+ goto unregister_gates;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, infra_clk_data);
+ if (r)
+ goto unregister_cpumuxes;
+
+ r = mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
+ if (r)
+ goto unregister_clk_hw;
+
+ return 0;
+
+unregister_clk_hw:
+ of_clk_del_provider(node);
+unregister_cpumuxes:
+ mtk_clk_unregister_cpumuxes(cpu_muxes, ARRAY_SIZE(cpu_muxes), infra_clk_data);
+unregister_gates:
+ mtk_clk_unregister_gates(infra_gates, ARRAY_SIZE(infra_gates), infra_clk_data);
+ return r;
+}
+
+static void clk_mt8173_infracfg_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_cpumuxes(cpu_muxes, ARRAY_SIZE(cpu_muxes), clk_data);
+ mtk_clk_unregister_gates(infra_gates, ARRAY_SIZE(infra_gates), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8173_infracfg_drv = {
+ .driver = {
+ .name = "clk-mt8173-infracfg",
+ .of_match_table = of_match_clk_mt8173_infracfg,
+ },
+ .probe = clk_mt8173_infracfg_probe,
+ .remove = clk_mt8173_infracfg_remove,
+};
+module_platform_driver(clk_mt8173_infracfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-mm.c b/drivers/clk/mediatek/clk-mt8173-mm.c
index 8abf42c2030c..26d27250b914 100644
--- a/drivers/clk/mediatek/clk-mt8173-mm.c
+++ b/drivers/clk/mediatek/clk-mt8173-mm.c
@@ -5,7 +5,7 @@
*/
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-gate.h"
@@ -25,25 +25,14 @@ static const struct mtk_gate_regs mm1_cg_regs = {
.sta_ofs = 0x0110,
};
-#define GATE_MM0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
-#define GATE_MM1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &mm1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
+#define GATE_MM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate mt8173_mm_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "mm_dummy"),
/* MM0 */
GATE_MM0(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 0),
GATE_MM0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
@@ -100,47 +89,26 @@ static const struct mtk_gate mt8173_mm_clks[] = {
GATE_MM1(CLK_MM_HDMI_HDCP24M, "mm_hdmi_hdcp24m", "hdcp_24m_sel", 20),
};
-struct clk_mt8173_mm_driver_data {
- const struct mtk_gate *gates_clk;
- int gates_num;
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mt8173_mm_clks,
+ .num_clks = ARRAY_SIZE(mt8173_mm_clks),
};
-static const struct clk_mt8173_mm_driver_data mt8173_mmsys_driver_data = {
- .gates_clk = mt8173_mm_clks,
- .gates_num = ARRAY_SIZE(mt8173_mm_clks),
+static const struct platform_device_id clk_mt8173_mm_id_table[] = {
+ { .name = "clk-mt8173-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
};
-
-static int clk_mt8173_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- const struct clk_mt8173_mm_driver_data *data;
- struct clk_hw_onecell_data *clk_data;
- int ret;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- data = &mt8173_mmsys_driver_data;
-
- ret = mtk_clk_register_gates(node, data->gates_clk, data->gates_num,
- clk_data);
- if (ret)
- return ret;
-
- ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (ret)
- return ret;
-
- return 0;
-}
+MODULE_DEVICE_TABLE(platform, clk_mt8173_mm_id_table);
static struct platform_driver clk_mt8173_mm_drv = {
.driver = {
.name = "clk-mt8173-mm",
},
- .probe = clk_mt8173_mm_probe,
+ .id_table = clk_mt8173_mm_id_table,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
};
+module_platform_driver(clk_mt8173_mm_drv);
-builtin_platform_driver(clk_mt8173_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT8173 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-pericfg.c b/drivers/clk/mediatek/clk-mt8173-pericfg.c
new file mode 100644
index 000000000000..bebda74d0f43
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-pericfg.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "reset.h"
+
+#define GATE_PERI0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri0_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_PERI1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri1_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr)
+
+static DEFINE_SPINLOCK(mt8173_clk_lock);
+
+static const struct mtk_gate_regs peri0_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x0010,
+ .sta_ofs = 0x0018,
+};
+
+static const struct mtk_gate_regs peri1_cg_regs = {
+ .set_ofs = 0x000c,
+ .clr_ofs = 0x0014,
+ .sta_ofs = 0x001c,
+};
+
+static const char * const uart_ck_sel_parents[] = {
+ "clk26m",
+ "uart_sel",
+};
+
+static const struct mtk_composite peri_clks[] = {
+ MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
+ MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
+ MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
+ MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1),
+};
+
+static const struct mtk_gate peri_gates[] = {
+ GATE_DUMMY(CLK_DUMMY, "peri_gate_dummy"),
+ /* PERI0 */
+ GATE_PERI0(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0),
+ GATE_PERI0(CLK_PERI_THERM, "peri_therm", "axi_sel", 1),
+ GATE_PERI0(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2),
+ GATE_PERI0(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3),
+ GATE_PERI0(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4),
+ GATE_PERI0(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5),
+ GATE_PERI0(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6),
+ GATE_PERI0(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7),
+ GATE_PERI0(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8),
+ GATE_PERI0(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9),
+ GATE_PERI0(CLK_PERI_USB0, "peri_usb0", "usb20_sel", 10),
+ GATE_PERI0(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11),
+ GATE_PERI0(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12),
+ GATE_PERI0(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13),
+ GATE_PERI0(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14),
+ GATE_PERI0(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15),
+ GATE_PERI0(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16),
+ GATE_PERI0(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17),
+ GATE_PERI0(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18),
+ GATE_PERI0(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19),
+ GATE_PERI0(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20),
+ GATE_PERI0(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21),
+ GATE_PERI0(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22),
+ GATE_PERI0(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23),
+ GATE_PERI0(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24),
+ GATE_PERI0(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25),
+ GATE_PERI0(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26),
+ GATE_PERI0(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27),
+ GATE_PERI0(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28),
+ GATE_PERI0(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29),
+ GATE_PERI0(CLK_PERI_I2C5, "peri_i2c5", "axi_sel", 30),
+ GATE_PERI0(CLK_PERI_NFIECC, "peri_nfiecc", "axi_sel", 31),
+ /* PERI1 */
+ GATE_PERI1(CLK_PERI_SPI, "peri_spi", "spi_sel", 0),
+ GATE_PERI1(CLK_PERI_IRRX, "peri_irrx", "spi_sel", 1),
+ GATE_PERI1(CLK_PERI_I2C6, "peri_i2c6", "axi_sel", 2),
+};
+
+static u16 pericfg_rst_ofs[] = { 0x0, 0x4 };
+
+static const struct mtk_clk_rst_desc clk_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = pericfg_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs),
+};
+
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_gates,
+ .num_clks = ARRAY_SIZE(peri_gates),
+ .composite_clks = peri_clks,
+ .num_composite_clks = ARRAY_SIZE(peri_clks),
+ .clk_lock = &mt8173_clk_lock,
+ .rst_desc = &clk_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8173_pericfg[] = {
+ { .compatible = "mediatek,mt8173-pericfg", .data = &peri_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_pericfg);
+
+static struct platform_driver clk_mt8173_pericfg_drv = {
+ .driver = {
+ .name = "clk-mt8173-pericfg",
+ .of_match_table = of_match_clk_mt8173_pericfg,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt8173_pericfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 pericfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-topckgen.c b/drivers/clk/mediatek/clk-mt8173-topckgen.c
new file mode 100644
index 000000000000..42c37541cebb
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-topckgen.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+/*
+ * For some clocks, we don't care what their actual rates are. And these
+ * clocks may change their rate on different products or different scenarios.
+ * So we model these clocks' rate as 0, to denote it's not an actual rate.
+ */
+#define DUMMY_RATE 0
+
+#define TOP_MUX_GATE_NOSR(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) \
+ MUX_GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _reg, \
+ (_reg + 0x4), (_reg + 0x8), _shift, _width, \
+ _gate, 0, -1, _flags)
+
+#define TOP_MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) \
+ TOP_MUX_GATE_NOSR(_id, _name, _parents, _reg, _shift, _width, \
+ _gate, CLK_SET_RATE_PARENT | _flags)
+
+static DEFINE_SPINLOCK(mt8173_top_clk_lock);
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll2_d2",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const mem_parents[] = {
+ "clk26m",
+ "dmpll_ck"
+};
+
+static const char * const ddrphycfg_parents[] = {
+ "clk26m",
+ "syspll1_d8"
+};
+
+static const char * const mm_parents[] = {
+ "clk26m",
+ "vencpll_d2",
+ "main_h364m",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll1_d2",
+ "univpll2_d2",
+ "dmpll_d2"
+};
+
+static const char * const pwm_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "univpll3_d2",
+ "univpll1_d4"
+};
+
+static const char * const vdec_parents[] = {
+ "clk26m",
+ "vcodecpll_ck",
+ "tvdpll_445p5m",
+ "univpll_d3",
+ "vencpll_d2",
+ "syspll_d3",
+ "univpll1_d2",
+ "mmpll_d2",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const venc_parents[] = {
+ "clk26m",
+ "vcodecpll_ck",
+ "tvdpll_445p5m",
+ "univpll_d3",
+ "vencpll_d2",
+ "syspll_d3",
+ "univpll1_d2",
+ "univpll2_d2",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const mfg_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "dmpll_ck",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "clk26m",
+ "syspll_d3",
+ "syspll1_d2",
+ "syspll_d5",
+ "univpll_d3",
+ "univpll1_d2",
+ "univpll_d5",
+ "univpll2_d2"
+};
+
+static const char * const camtg_parents[] = {
+ "clk26m",
+ "univpll_d26",
+ "univpll2_d2",
+ "syspll3_d2",
+ "syspll3_d4",
+ "univpll1_d4"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll2_d8"
+};
+
+static const char * const spi_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "syspll1_d4",
+ "syspll4_d2",
+ "univpll3_d2",
+ "univpll2_d4",
+ "univpll1_d8"
+};
+
+static const char * const usb20_parents[] = {
+ "clk26m",
+ "univpll1_d8",
+ "univpll3_d4"
+};
+
+static const char * const usb30_parents[] = {
+ "clk26m",
+ "univpll3_d2",
+ "usb_syspll_125m",
+ "univpll2_d4"
+};
+
+static const char * const msdc50_0_h_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll2_d2",
+ "syspll4_d2",
+ "univpll_d5",
+ "univpll1_d4"
+};
+
+static const char * const msdc50_0_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "msdcpll_d2",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "msdcpll_d4",
+ "vencpll_d4",
+ "tvdpll_ck",
+ "univpll_d2",
+ "univpll1_d2",
+ "mmpll_ck",
+ "msdcpll2_ck",
+ "msdcpll2_d2",
+ "msdcpll2_d4"
+};
+
+static const char * const msdc30_1_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "univpll_d7",
+ "vencpll_d4"
+};
+
+static const char * const msdc30_2_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "msdcpll_d4",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "univpll_d7",
+ "vencpll_d2"
+};
+
+static const char * const msdc30_3_parents[] = {
+ "clk26m",
+ "msdcpll2_ck",
+ "msdcpll2_d2",
+ "univpll2_d2",
+ "msdcpll2_d4",
+ "msdcpll_d4",
+ "univpll1_d4",
+ "syspll2_d2",
+ "syspll_d7",
+ "univpll_d7",
+ "vencpll_d4",
+ "msdcpll_ck",
+ "msdcpll_d2",
+ "msdcpll_d4"
+};
+
+static const char * const audio_parents[] = {
+ "clk26m",
+ "syspll3_d4",
+ "syspll4_d4",
+ "syspll1_d16"
+};
+
+static const char * const aud_intbus_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll4_d2",
+ "univpll3_d2",
+ "univpll2_d8",
+ "dmpll_d4",
+ "dmpll_d8"
+};
+
+static const char * const pmicspi_parents[] = {
+ "clk26m",
+ "syspll1_d8",
+ "syspll3_d4",
+ "syspll1_d16",
+ "univpll3_d4",
+ "univpll_d26",
+ "dmpll_d8",
+ "dmpll_d16"
+};
+
+static const char * const scp_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "univpll_d5",
+ "syspll_d5",
+ "dmpll_d2",
+ "dmpll_d4"
+};
+
+static const char * const atb_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "univpll_d5",
+ "dmpll_d2"
+};
+
+static const char * const venc_lt_parents[] = {
+ "clk26m",
+ "univpll_d3",
+ "vcodecpll_ck",
+ "tvdpll_445p5m",
+ "vencpll_d2",
+ "syspll_d3",
+ "univpll1_d2",
+ "univpll2_d2",
+ "syspll1_d2",
+ "univpll_d5",
+ "vcodecpll_370p5",
+ "dmpll_ck"
+};
+
+static const char * const dpi0_parents[] = {
+ "clk26m",
+ "tvdpll_d2",
+ "tvdpll_d4",
+ "clk26m",
+ "clk26m",
+ "tvdpll_d8",
+ "tvdpll_d16"
+};
+
+static const char * const irda_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "syspll2_d4"
+};
+
+static const char * const cci400_parents[] = {
+ "clk26m",
+ "vencpll_ck",
+ "armca7pll_754m",
+ "armca7pll_502m",
+ "univpll_d2",
+ "syspll_d2",
+ "msdcpll_ck",
+ "dmpll_ck"
+};
+
+static const char * const aud_1_parents[] = {
+ "clk26m",
+ "apll1_ck",
+ "univpll2_d4",
+ "univpll2_d8"
+};
+
+static const char * const aud_2_parents[] = {
+ "clk26m",
+ "apll2_ck",
+ "univpll2_d4",
+ "univpll2_d8"
+};
+
+static const char * const mem_mfg_in_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "dmpll_ck",
+ "clk26m"
+};
+
+static const char * const axi_mfg_in_parents[] = {
+ "clk26m",
+ "axi_sel",
+ "dmpll_d2"
+};
+
+static const char * const scam_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "univpll2_d4",
+ "dmpll_d4"
+};
+
+static const char * const spinfi_ifr_parents[] = {
+ "clk26m",
+ "univpll2_d8",
+ "univpll3_d4",
+ "syspll4_d2",
+ "univpll2_d4",
+ "univpll3_d2",
+ "syspll1_d4",
+ "univpll1_d4"
+};
+
+static const char * const hdmi_parents[] = {
+ "clk26m",
+ "hdmitx_dig_cts",
+ "hdmitxpll_d2",
+ "hdmitxpll_d3"
+};
+
+static const char * const dpilvds_parents[] = {
+ "clk26m",
+ "lvdspll",
+ "lvdspll_d2",
+ "lvdspll_d4",
+ "lvdspll_d8",
+ "fpc_ck"
+};
+
+static const char * const msdc50_2_h_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll2_d2",
+ "syspll4_d2",
+ "univpll_d5",
+ "univpll1_d4"
+};
+
+static const char * const hdcp_parents[] = {
+ "clk26m",
+ "syspll4_d2",
+ "syspll3_d4",
+ "univpll2_d4"
+};
+
+static const char * const hdcp_24m_parents[] = {
+ "clk26m",
+ "univpll_d26",
+ "univpll_d52",
+ "univpll2_d8"
+};
+
+static const char * const rtc_parents[] = {
+ "clkrtc_int",
+ "clkrtc_ext",
+ "clk26m",
+ "univpll3_d8"
+};
+
+static const char * const i2s0_m_ck_parents[] = {
+ "apll1_div1",
+ "apll2_div1"
+};
+
+static const char * const i2s1_m_ck_parents[] = {
+ "apll1_div2",
+ "apll2_div2"
+};
+
+static const char * const i2s2_m_ck_parents[] = {
+ "apll1_div3",
+ "apll2_div3"
+};
+
+static const char * const i2s3_m_ck_parents[] = {
+ "apll1_div4",
+ "apll2_div4"
+};
+
+static const char * const i2s3_b_ck_parents[] = {
+ "apll1_div5",
+ "apll2_div5"
+};
+
+static const struct mtk_fixed_clk fixed_clks[] = {
+ FIXED_CLK(CLK_DUMMY, "topck_dummy", "clk26m", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk26m", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk26m", 125 * MHZ),
+ FIXED_CLK(CLK_TOP_DSI0_DIG, "dsi0_dig", "clk26m", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_DSI1_DIG, "dsi1_dig", "clk26m", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_LVDS_PXL, "lvds_pxl", "lvdspll", DUMMY_RATE),
+ FIXED_CLK(CLK_TOP_LVDS_CTS, "lvds_cts", "lvdspll", DUMMY_RATE),
+};
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2),
+ FACTOR(CLK_TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3),
+
+ FACTOR_FLAGS(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7, 0),
+
+ FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3),
+
+ FACTOR_FLAGS(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26, 0),
+
+ FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1),
+ FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793),
+ FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1),
+
+ FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2),
+ FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3),
+
+ FACTOR(CLK_TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1),
+ FACTOR(CLK_TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1),
+
+ FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
+ FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1),
+
+ FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1),
+ FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2),
+ FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4),
+ FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8),
+ FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16),
+
+ FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2),
+ FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4),
+ FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8),
+
+ FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
+
+ FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4),
+
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4, 0),
+
+ FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1),
+ FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16),
+
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2, 0),
+
+ FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3),
+ FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4),
+
+ FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1),
+ FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2),
+ FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4),
+};
+
+static const struct mtk_composite top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3),
+ MUX_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_FLAGS(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel",
+ ddrphycfg_parents, 0x0040, 16, 1, 23,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31),
+ /* CLK_CFG_1 */
+ MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7),
+ MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15),
+ MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23),
+ MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31),
+ /* CLK_CFG_2 */
+ MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7),
+ MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15),
+ MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23),
+ MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31),
+ /* CLK_CFG_3 */
+ MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7),
+ MUX_GATE_FLAGS(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents,
+ 0x0070, 8, 3, 15, 0),
+ MUX_GATE_FLAGS(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents,
+ 0x0070, 16, 4, 23, 0),
+ MUX_GATE_FLAGS(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents,
+ 0x0070, 24, 3, 31, 0),
+ /* CLK_CFG_4 */
+ MUX_GATE_FLAGS(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents,
+ 0x0080, 0, 3, 7, 0),
+ MUX_GATE_FLAGS(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents,
+ 0x0080, 8, 4, 15, 0),
+ MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents,
+ 0x0080, 16, 2, 23),
+ MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents,
+ 0x0080, 24, 3, 31),
+ /* CLK_CFG_5 */
+ MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents,
+ 0x0090, 0, 3, 7 /* 7:5 */),
+ MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15),
+ MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23),
+ MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents,
+ 0x0090, 24, 4, 31),
+ /* CLK_CFG_6 */
+ /*
+ * The dpi0_sel clock should not propagate rate changes to its parent
+ * clock so the dpi driver can have full control over PLL and divider.
+ */
+ MUX_GATE_FLAGS(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents,
+ 0x00a0, 0, 3, 7, 0),
+ MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15),
+ MUX_GATE_FLAGS(CLK_TOP_CCI400_SEL, "cci400_sel",
+ cci400_parents, 0x00a0, 16, 3, 23,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31),
+ /* CLK_CFG_7 */
+ MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7),
+ MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents,
+ 0x00b0, 8, 2, 15),
+ MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents,
+ 0x00b0, 16, 2, 23),
+ MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31),
+ /* CLK_CFG_12 */
+ MUX_GATE(CLK_TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents,
+ 0x00c0, 0, 3, 7),
+ MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15),
+ MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents,
+ 0x00c0, 24, 3, 31),
+ /* CLK_CFG_13 */
+ MUX_GATE_FLAGS(CLK_TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents,
+ 0x00d0, 0, 3, 7, 0),
+ MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15),
+ MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents,
+ 0x00d0, 16, 2, 23),
+ MUX_FLAGS(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+
+ DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24),
+ DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0),
+ DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8),
+ DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16),
+ DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24),
+ DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0),
+
+ DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28),
+ DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0),
+ DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8),
+ DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16),
+ DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24),
+ DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4),
+
+ MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1),
+ MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1),
+ MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1),
+ MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1),
+ MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .clk_lock = &mt8173_top_clk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt8173_topckgen[] = {
+ { .compatible = "mediatek,mt8173-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_topckgen);
+
+static struct platform_driver clk_mt8173_topckgen_drv = {
+ .driver = {
+ .name = "clk-mt8173-topckgen",
+ .of_match_table = of_match_clk_mt8173_topckgen,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt8173_topckgen_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 topckgen clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-vdecsys.c b/drivers/clk/mediatek/clk-mt8173-vdecsys.c
new file mode 100644
index 000000000000..625ca0b09cc2
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-vdecsys.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#define GATE_VDEC(_id, _name, _parent, _regs) \
+ GATE_MTK(_id, _name, _parent, _regs, 0, \
+ &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate_regs vdec0_cg_regs = {
+ .set_ofs = 0x0000,
+ .clr_ofs = 0x0004,
+ .sta_ofs = 0x0000,
+};
+
+static const struct mtk_gate_regs vdec1_cg_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x000c,
+ .sta_ofs = 0x0008,
+};
+
+static const struct mtk_gate vdec_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "vdec_dummy"),
+ GATE_VDEC(CLK_VDEC_CKEN, "vdec_cken", "vdec_sel", &vdec0_cg_regs),
+ GATE_VDEC(CLK_VDEC_LARB_CKEN, "vdec_larb_cken", "mm_sel", &vdec1_cg_regs),
+};
+
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8173_vdecsys[] = {
+ { .compatible = "mediatek,mt8173-vdecsys", .data = &vdec_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_vdecsys);
+
+static struct platform_driver clk_mt8173_vdecsys_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8173-vdecsys",
+ .of_match_table = of_match_clk_mt8173_vdecsys,
+ },
+};
+module_platform_driver(clk_mt8173_vdecsys_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 vdecsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173-vencsys.c b/drivers/clk/mediatek/clk-mt8173-vencsys.c
new file mode 100644
index 000000000000..87755dd1a337
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8173-vencsys.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2022 Collabora Ltd.
+ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs venc_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_VENC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate venc_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "venc_dummy"),
+ GATE_VENC(CLK_VENC_CKE0, "venc_cke0", "mm_sel", 0),
+ GATE_VENC(CLK_VENC_CKE1, "venc_cke1", "venc_sel", 4),
+ GATE_VENC(CLK_VENC_CKE2, "venc_cke2", "venc_sel", 8),
+ GATE_VENC(CLK_VENC_CKE3, "venc_cke3", "venc_sel", 12),
+};
+
+static const struct mtk_gate venclt_clks[] = {
+ GATE_DUMMY(CLK_DUMMY, "venclt_dummy"),
+ GATE_VENC(CLK_VENCLT_CKE0, "venclt_cke0", "mm_sel", 0),
+ GATE_VENC(CLK_VENCLT_CKE1, "venclt_cke1", "venclt_sel", 4),
+};
+
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
+};
+
+static const struct mtk_clk_desc venc_lt_desc = {
+ .clks = venclt_clks,
+ .num_clks = ARRAY_SIZE(venclt_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8173_vencsys[] = {
+ { .compatible = "mediatek,mt8173-vencsys", .data = &venc_desc },
+ { .compatible = "mediatek,mt8173-vencltsys", .data = &venc_lt_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_vencsys);
+
+static struct platform_driver clk_mt8173_vencsys_drv = {
+ .driver = {
+ .name = "clk-mt8173-vencsys",
+ .of_match_table = of_match_clk_mt8173_vencsys,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt8173_vencsys_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8173 vencsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
deleted file mode 100644
index b8529ee7199d..000000000000
--- a/drivers/clk/mediatek/clk-mt8173.c
+++ /dev/null
@@ -1,1125 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2014 MediaTek Inc.
- * Author: James Liao <jamesjj.liao@mediatek.com>
- */
-
-#include <linux/clk.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-
-#include "clk-cpumux.h"
-#include "clk-gate.h"
-#include "clk-mtk.h"
-#include "clk-pll.h"
-
-#include <dt-bindings/clock/mt8173-clk.h>
-
-/*
- * For some clocks, we don't care what their actual rates are. And these
- * clocks may change their rate on different products or different scenarios.
- * So we model these clocks' rate as 0, to denote it's not an actual rate.
- */
-#define DUMMY_RATE 0
-
-static DEFINE_SPINLOCK(mt8173_clk_lock);
-
-static const struct mtk_fixed_clk fixed_clks[] __initconst = {
- FIXED_CLK(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk26m", DUMMY_RATE),
- FIXED_CLK(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk26m", 125 * MHZ),
- FIXED_CLK(CLK_TOP_DSI0_DIG, "dsi0_dig", "clk26m", DUMMY_RATE),
- FIXED_CLK(CLK_TOP_DSI1_DIG, "dsi1_dig", "clk26m", DUMMY_RATE),
- FIXED_CLK(CLK_TOP_LVDS_PXL, "lvds_pxl", "lvdspll", DUMMY_RATE),
- FIXED_CLK(CLK_TOP_LVDS_CTS, "lvds_cts", "lvdspll", DUMMY_RATE),
-};
-
-static const struct mtk_fixed_factor top_divs[] __initconst = {
- FACTOR(CLK_TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2),
- FACTOR(CLK_TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3),
-
- FACTOR(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2),
- FACTOR(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3),
- FACTOR(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5),
- FACTOR(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7),
-
- FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4),
- FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3),
-
- FACTOR(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2),
- FACTOR(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3),
- FACTOR(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5),
- FACTOR(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7),
- FACTOR(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26),
-
- FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1),
- FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793),
- FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1),
-
- FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2),
- FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3),
-
- FACTOR(CLK_TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1),
- FACTOR(CLK_TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1),
-
- FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
- FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1),
-
- FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1),
- FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2),
- FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4),
- FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8),
- FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16),
-
- FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2),
- FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4),
- FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8),
-
- FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
- FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
-
- FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
- FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
- FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
- FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1),
- FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2),
- FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4),
-
- FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1),
- FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2),
- FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4),
- FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8),
- FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16),
- FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1),
- FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2),
- FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4),
- FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1),
- FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2),
- FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4),
- FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1),
- FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2),
- FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4),
-
- FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1),
- FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2),
- FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4),
- FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8),
- FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16),
-
- FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1),
- FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1),
- FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1),
- FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1),
- FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1),
- FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2),
-
- FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3),
- FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4),
-
- FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1),
- FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2),
- FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4),
-};
-
-static const char * const axi_parents[] __initconst = {
- "clk26m",
- "syspll1_d2",
- "syspll_d5",
- "syspll1_d4",
- "univpll_d5",
- "univpll2_d2",
- "dmpll_d2",
- "dmpll_d4"
-};
-
-static const char * const mem_parents[] __initconst = {
- "clk26m",
- "dmpll_ck"
-};
-
-static const char * const ddrphycfg_parents[] __initconst = {
- "clk26m",
- "syspll1_d8"
-};
-
-static const char * const mm_parents[] __initconst = {
- "clk26m",
- "vencpll_d2",
- "main_h364m",
- "syspll1_d2",
- "syspll_d5",
- "syspll1_d4",
- "univpll1_d2",
- "univpll2_d2",
- "dmpll_d2"
-};
-
-static const char * const pwm_parents[] __initconst = {
- "clk26m",
- "univpll2_d4",
- "univpll3_d2",
- "univpll1_d4"
-};
-
-static const char * const vdec_parents[] __initconst = {
- "clk26m",
- "vcodecpll_ck",
- "tvdpll_445p5m",
- "univpll_d3",
- "vencpll_d2",
- "syspll_d3",
- "univpll1_d2",
- "mmpll_d2",
- "dmpll_d2",
- "dmpll_d4"
-};
-
-static const char * const venc_parents[] __initconst = {
- "clk26m",
- "vcodecpll_ck",
- "tvdpll_445p5m",
- "univpll_d3",
- "vencpll_d2",
- "syspll_d3",
- "univpll1_d2",
- "univpll2_d2",
- "dmpll_d2",
- "dmpll_d4"
-};
-
-static const char * const mfg_parents[] __initconst = {
- "clk26m",
- "mmpll_ck",
- "dmpll_ck",
- "clk26m",
- "clk26m",
- "clk26m",
- "clk26m",
- "clk26m",
- "clk26m",
- "syspll_d3",
- "syspll1_d2",
- "syspll_d5",
- "univpll_d3",
- "univpll1_d2",
- "univpll_d5",
- "univpll2_d2"
-};
-
-static const char * const camtg_parents[] __initconst = {
- "clk26m",
- "univpll_d26",
- "univpll2_d2",
- "syspll3_d2",
- "syspll3_d4",
- "univpll1_d4"
-};
-
-static const char * const uart_parents[] __initconst = {
- "clk26m",
- "univpll2_d8"
-};
-
-static const char * const spi_parents[] __initconst = {
- "clk26m",
- "syspll3_d2",
- "syspll1_d4",
- "syspll4_d2",
- "univpll3_d2",
- "univpll2_d4",
- "univpll1_d8"
-};
-
-static const char * const usb20_parents[] __initconst = {
- "clk26m",
- "univpll1_d8",
- "univpll3_d4"
-};
-
-static const char * const usb30_parents[] __initconst = {
- "clk26m",
- "univpll3_d2",
- "usb_syspll_125m",
- "univpll2_d4"
-};
-
-static const char * const msdc50_0_h_parents[] __initconst = {
- "clk26m",
- "syspll1_d2",
- "syspll2_d2",
- "syspll4_d2",
- "univpll_d5",
- "univpll1_d4"
-};
-
-static const char * const msdc50_0_parents[] __initconst = {
- "clk26m",
- "msdcpll_ck",
- "msdcpll_d2",
- "univpll1_d4",
- "syspll2_d2",
- "syspll_d7",
- "msdcpll_d4",
- "vencpll_d4",
- "tvdpll_ck",
- "univpll_d2",
- "univpll1_d2",
- "mmpll_ck",
- "msdcpll2_ck",
- "msdcpll2_d2",
- "msdcpll2_d4"
-};
-
-static const char * const msdc30_1_parents[] __initconst = {
- "clk26m",
- "univpll2_d2",
- "msdcpll_d4",
- "univpll1_d4",
- "syspll2_d2",
- "syspll_d7",
- "univpll_d7",
- "vencpll_d4"
-};
-
-static const char * const msdc30_2_parents[] __initconst = {
- "clk26m",
- "univpll2_d2",
- "msdcpll_d4",
- "univpll1_d4",
- "syspll2_d2",
- "syspll_d7",
- "univpll_d7",
- "vencpll_d2"
-};
-
-static const char * const msdc30_3_parents[] __initconst = {
- "clk26m",
- "msdcpll2_ck",
- "msdcpll2_d2",
- "univpll2_d2",
- "msdcpll2_d4",
- "msdcpll_d4",
- "univpll1_d4",
- "syspll2_d2",
- "syspll_d7",
- "univpll_d7",
- "vencpll_d4",
- "msdcpll_ck",
- "msdcpll_d2",
- "msdcpll_d4"
-};
-
-static const char * const audio_parents[] __initconst = {
- "clk26m",
- "syspll3_d4",
- "syspll4_d4",
- "syspll1_d16"
-};
-
-static const char * const aud_intbus_parents[] __initconst = {
- "clk26m",
- "syspll1_d4",
- "syspll4_d2",
- "univpll3_d2",
- "univpll2_d8",
- "dmpll_d4",
- "dmpll_d8"
-};
-
-static const char * const pmicspi_parents[] __initconst = {
- "clk26m",
- "syspll1_d8",
- "syspll3_d4",
- "syspll1_d16",
- "univpll3_d4",
- "univpll_d26",
- "dmpll_d8",
- "dmpll_d16"
-};
-
-static const char * const scp_parents[] __initconst = {
- "clk26m",
- "syspll1_d2",
- "univpll_d5",
- "syspll_d5",
- "dmpll_d2",
- "dmpll_d4"
-};
-
-static const char * const atb_parents[] __initconst = {
- "clk26m",
- "syspll1_d2",
- "univpll_d5",
- "dmpll_d2"
-};
-
-static const char * const venc_lt_parents[] __initconst = {
- "clk26m",
- "univpll_d3",
- "vcodecpll_ck",
- "tvdpll_445p5m",
- "vencpll_d2",
- "syspll_d3",
- "univpll1_d2",
- "univpll2_d2",
- "syspll1_d2",
- "univpll_d5",
- "vcodecpll_370p5",
- "dmpll_ck"
-};
-
-static const char * const dpi0_parents[] __initconst = {
- "clk26m",
- "tvdpll_d2",
- "tvdpll_d4",
- "clk26m",
- "clk26m",
- "tvdpll_d8",
- "tvdpll_d16"
-};
-
-static const char * const irda_parents[] __initconst = {
- "clk26m",
- "univpll2_d4",
- "syspll2_d4"
-};
-
-static const char * const cci400_parents[] __initconst = {
- "clk26m",
- "vencpll_ck",
- "armca7pll_754m",
- "armca7pll_502m",
- "univpll_d2",
- "syspll_d2",
- "msdcpll_ck",
- "dmpll_ck"
-};
-
-static const char * const aud_1_parents[] __initconst = {
- "clk26m",
- "apll1_ck",
- "univpll2_d4",
- "univpll2_d8"
-};
-
-static const char * const aud_2_parents[] __initconst = {
- "clk26m",
- "apll2_ck",
- "univpll2_d4",
- "univpll2_d8"
-};
-
-static const char * const mem_mfg_in_parents[] __initconst = {
- "clk26m",
- "mmpll_ck",
- "dmpll_ck",
- "clk26m"
-};
-
-static const char * const axi_mfg_in_parents[] __initconst = {
- "clk26m",
- "axi_sel",
- "dmpll_d2"
-};
-
-static const char * const scam_parents[] __initconst = {
- "clk26m",
- "syspll3_d2",
- "univpll2_d4",
- "dmpll_d4"
-};
-
-static const char * const spinfi_ifr_parents[] __initconst = {
- "clk26m",
- "univpll2_d8",
- "univpll3_d4",
- "syspll4_d2",
- "univpll2_d4",
- "univpll3_d2",
- "syspll1_d4",
- "univpll1_d4"
-};
-
-static const char * const hdmi_parents[] __initconst = {
- "clk26m",
- "hdmitx_dig_cts",
- "hdmitxpll_d2",
- "hdmitxpll_d3"
-};
-
-static const char * const dpilvds_parents[] __initconst = {
- "clk26m",
- "lvdspll",
- "lvdspll_d2",
- "lvdspll_d4",
- "lvdspll_d8",
- "fpc_ck"
-};
-
-static const char * const msdc50_2_h_parents[] __initconst = {
- "clk26m",
- "syspll1_d2",
- "syspll2_d2",
- "syspll4_d2",
- "univpll_d5",
- "univpll1_d4"
-};
-
-static const char * const hdcp_parents[] __initconst = {
- "clk26m",
- "syspll4_d2",
- "syspll3_d4",
- "univpll2_d4"
-};
-
-static const char * const hdcp_24m_parents[] __initconst = {
- "clk26m",
- "univpll_d26",
- "univpll_d52",
- "univpll2_d8"
-};
-
-static const char * const rtc_parents[] __initconst = {
- "clkrtc_int",
- "clkrtc_ext",
- "clk26m",
- "univpll3_d8"
-};
-
-static const char * const i2s0_m_ck_parents[] __initconst = {
- "apll1_div1",
- "apll2_div1"
-};
-
-static const char * const i2s1_m_ck_parents[] __initconst = {
- "apll1_div2",
- "apll2_div2"
-};
-
-static const char * const i2s2_m_ck_parents[] __initconst = {
- "apll1_div3",
- "apll2_div3"
-};
-
-static const char * const i2s3_m_ck_parents[] __initconst = {
- "apll1_div4",
- "apll2_div4"
-};
-
-static const char * const i2s3_b_ck_parents[] __initconst = {
- "apll1_div5",
- "apll2_div5"
-};
-
-static const char * const ca53_parents[] __initconst = {
- "clk26m",
- "armca7pll",
- "mainpll",
- "univpll"
-};
-
-static const char * const ca72_parents[] __initconst = {
- "clk26m",
- "armca15pll",
- "mainpll",
- "univpll"
-};
-
-static const struct mtk_composite cpu_muxes[] __initconst = {
- MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2),
- MUX(CLK_INFRA_CA72SEL, "infra_ca72_sel", ca72_parents, 0x0000, 2, 2),
-};
-
-static const struct mtk_composite top_muxes[] __initconst = {
- /* CLK_CFG_0 */
- MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3),
- MUX(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1),
- MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23),
- MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31),
- /* CLK_CFG_1 */
- MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7),
- MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15),
- MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23),
- MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31),
- /* CLK_CFG_2 */
- MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7),
- MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15),
- MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23),
- MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31),
- /* CLK_CFG_3 */
- MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7),
- MUX_GATE(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents, 0x0070, 8, 3, 15),
- MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x0070, 16, 4, 23),
- MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x0070, 24, 3, 31),
- /* CLK_CFG_4 */
- MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x0080, 0, 3, 7),
- MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x0080, 8, 4, 15),
- MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0080, 16, 2, 23),
- MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0080, 24, 3, 31),
- /* CLK_CFG_5 */
- MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0090, 0, 3, 7 /* 7:5 */),
- MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15),
- MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23),
- MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31),
- /* CLK_CFG_6 */
- /*
- * The dpi0_sel clock should not propagate rate changes to its parent
- * clock so the dpi driver can have full control over PLL and divider.
- */
- MUX_GATE_FLAGS(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7, 0),
- MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15),
- MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23),
- MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31),
- /* CLK_CFG_7 */
- MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7),
- MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents, 0x00b0, 8, 2, 15),
- MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents, 0x00b0, 16, 2, 23),
- MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31),
- /* CLK_CFG_12 */
- MUX_GATE(CLK_TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents, 0x00c0, 0, 3, 7),
- MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15),
- MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x00c0, 24, 3, 31),
- /* CLK_CFG_13 */
- MUX_GATE(CLK_TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents, 0x00d0, 0, 3, 7),
- MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15),
- MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents, 0x00d0, 16, 2, 23),
- MUX(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2),
-
- DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24),
- DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0),
- DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8),
- DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16),
- DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24),
- DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0),
-
- DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28),
- DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0),
- DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8),
- DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16),
- DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24),
- DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4),
-
- MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1),
- MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1),
- MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1),
- MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1),
- MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1),
-};
-
-static const struct mtk_gate_regs infra_cg_regs __initconst = {
- .set_ofs = 0x0040,
- .clr_ofs = 0x0044,
- .sta_ofs = 0x0048,
-};
-
-#define GATE_ICG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &infra_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-static const struct mtk_gate infra_clks[] __initconst = {
- GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0),
- GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1),
- GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5),
- GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6),
- GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7),
- GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8),
- GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "cpum_ck", 15),
- GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16),
- GATE_ICG(CLK_INFRA_CEC, "infra_cec", "clk26m", 18),
- GATE_ICG(CLK_INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22),
- GATE_ICG(CLK_INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23),
-};
-
-static const struct mtk_fixed_factor infra_divs[] __initconst = {
- FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2),
-};
-
-static const struct mtk_gate_regs peri0_cg_regs __initconst = {
- .set_ofs = 0x0008,
- .clr_ofs = 0x0010,
- .sta_ofs = 0x0018,
-};
-
-static const struct mtk_gate_regs peri1_cg_regs __initconst = {
- .set_ofs = 0x000c,
- .clr_ofs = 0x0014,
- .sta_ofs = 0x001c,
-};
-
-#define GATE_PERI0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_PERI1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &peri1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-static const struct mtk_gate peri_gates[] __initconst = {
- /* PERI0 */
- GATE_PERI0(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0),
- GATE_PERI0(CLK_PERI_THERM, "peri_therm", "axi_sel", 1),
- GATE_PERI0(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2),
- GATE_PERI0(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3),
- GATE_PERI0(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4),
- GATE_PERI0(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5),
- GATE_PERI0(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6),
- GATE_PERI0(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7),
- GATE_PERI0(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8),
- GATE_PERI0(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9),
- GATE_PERI0(CLK_PERI_USB0, "peri_usb0", "usb20_sel", 10),
- GATE_PERI0(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11),
- GATE_PERI0(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12),
- GATE_PERI0(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13),
- GATE_PERI0(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14),
- GATE_PERI0(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15),
- GATE_PERI0(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16),
- GATE_PERI0(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17),
- GATE_PERI0(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18),
- GATE_PERI0(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19),
- GATE_PERI0(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20),
- GATE_PERI0(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21),
- GATE_PERI0(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22),
- GATE_PERI0(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23),
- GATE_PERI0(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24),
- GATE_PERI0(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25),
- GATE_PERI0(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26),
- GATE_PERI0(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27),
- GATE_PERI0(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28),
- GATE_PERI0(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29),
- GATE_PERI0(CLK_PERI_I2C5, "peri_i2c5", "axi_sel", 30),
- GATE_PERI0(CLK_PERI_NFIECC, "peri_nfiecc", "axi_sel", 31),
- /* PERI1 */
- GATE_PERI1(CLK_PERI_SPI, "peri_spi", "spi_sel", 0),
- GATE_PERI1(CLK_PERI_IRRX, "peri_irrx", "spi_sel", 1),
- GATE_PERI1(CLK_PERI_I2C6, "peri_i2c6", "axi_sel", 2),
-};
-
-static const char * const uart_ck_sel_parents[] __initconst = {
- "clk26m",
- "uart_sel",
-};
-
-static const struct mtk_composite peri_clks[] __initconst = {
- MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
- MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
- MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
- MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1),
-};
-
-static const struct mtk_gate_regs cg_regs_4_8_0 __initconst = {
- .set_ofs = 0x0004,
- .clr_ofs = 0x0008,
- .sta_ofs = 0x0000,
-};
-
-#define GATE_IMG(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &cg_regs_4_8_0, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-static const struct mtk_gate img_clks[] __initconst = {
- GATE_IMG(CLK_IMG_LARB2_SMI, "img_larb2_smi", "mm_sel", 0),
- GATE_IMG(CLK_IMG_CAM_SMI, "img_cam_smi", "mm_sel", 5),
- GATE_IMG(CLK_IMG_CAM_CAM, "img_cam_cam", "mm_sel", 6),
- GATE_IMG(CLK_IMG_SEN_TG, "img_sen_tg", "camtg_sel", 7),
- GATE_IMG(CLK_IMG_SEN_CAM, "img_sen_cam", "mm_sel", 8),
- GATE_IMG(CLK_IMG_CAM_SV, "img_cam_sv", "mm_sel", 9),
- GATE_IMG(CLK_IMG_FD, "img_fd", "mm_sel", 11),
-};
-
-static const struct mtk_gate_regs vdec0_cg_regs __initconst = {
- .set_ofs = 0x0000,
- .clr_ofs = 0x0004,
- .sta_ofs = 0x0000,
-};
-
-static const struct mtk_gate_regs vdec1_cg_regs __initconst = {
- .set_ofs = 0x0008,
- .clr_ofs = 0x000c,
- .sta_ofs = 0x0008,
-};
-
-#define GATE_VDEC0(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec0_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-#define GATE_VDEC1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &vdec1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-static const struct mtk_gate vdec_clks[] __initconst = {
- GATE_VDEC0(CLK_VDEC_CKEN, "vdec_cken", "vdec_sel", 0),
- GATE_VDEC1(CLK_VDEC_LARB_CKEN, "vdec_larb_cken", "mm_sel", 0),
-};
-
-#define GATE_VENC(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &cg_regs_4_8_0, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-static const struct mtk_gate venc_clks[] __initconst = {
- GATE_VENC(CLK_VENC_CKE0, "venc_cke0", "mm_sel", 0),
- GATE_VENC(CLK_VENC_CKE1, "venc_cke1", "venc_sel", 4),
- GATE_VENC(CLK_VENC_CKE2, "venc_cke2", "venc_sel", 8),
- GATE_VENC(CLK_VENC_CKE3, "venc_cke3", "venc_sel", 12),
-};
-
-#define GATE_VENCLT(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &cg_regs_4_8_0, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-static const struct mtk_gate venclt_clks[] __initconst = {
- GATE_VENCLT(CLK_VENCLT_CKE0, "venclt_cke0", "mm_sel", 0),
- GATE_VENCLT(CLK_VENCLT_CKE1, "venclt_cke1", "venclt_sel", 4),
-};
-
-static u16 infrasys_rst_ofs[] = { 0x30, 0x34, };
-static u16 pericfg_rst_ofs[] = { 0x0, 0x4, };
-
-static const struct mtk_clk_rst_desc clk_rst_desc[] = {
- /* infrasys */
- {
- .version = MTK_RST_SIMPLE,
- .rst_bank_ofs = infrasys_rst_ofs,
- .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs),
- },
- /* pericfg */
- {
- .version = MTK_RST_SIMPLE,
- .rst_bank_ofs = pericfg_rst_ofs,
- .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs),
- }
-};
-
-static struct clk_hw_onecell_data *mt8173_top_clk_data __initdata;
-static struct clk_hw_onecell_data *mt8173_pll_clk_data __initdata;
-
-static void __init mtk_clk_enable_critical(void)
-{
- if (!mt8173_top_clk_data || !mt8173_pll_clk_data)
- return;
-
- clk_prepare_enable(mt8173_pll_clk_data->hws[CLK_APMIXED_ARMCA15PLL]->clk);
- clk_prepare_enable(mt8173_pll_clk_data->hws[CLK_APMIXED_ARMCA7PLL]->clk);
- clk_prepare_enable(mt8173_top_clk_data->hws[CLK_TOP_MEM_SEL]->clk);
- clk_prepare_enable(mt8173_top_clk_data->hws[CLK_TOP_DDRPHYCFG_SEL]->clk);
- clk_prepare_enable(mt8173_top_clk_data->hws[CLK_TOP_CCI400_SEL]->clk);
- clk_prepare_enable(mt8173_top_clk_data->hws[CLK_TOP_RTC_SEL]->clk);
-}
-
-static void __init mtk_topckgen_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- int r;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- mt8173_top_clk_data = clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
-
- mtk_clk_register_fixed_clks(fixed_clks, ARRAY_SIZE(fixed_clks), clk_data);
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt8173_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_clk_enable_critical();
-}
-CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init);
-
-static void __init mtk_infrasys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
-
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
- mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data);
-
- mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_register_reset_controller(node, &clk_rst_desc[0]);
-}
-CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init);
-
-static void __init mtk_pericfg_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- void __iomem *base;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
-
- mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates),
- clk_data);
- mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base,
- &mt8173_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_register_reset_controller(node, &clk_rst_desc[1]);
-}
-CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init);
-
-struct mtk_clk_usb {
- int id;
- const char *name;
- const char *parent;
- u32 reg_ofs;
-};
-
-#define APMIXED_USB(_id, _name, _parent, _reg_ofs) { \
- .id = _id, \
- .name = _name, \
- .parent = _parent, \
- .reg_ofs = _reg_ofs, \
- }
-
-static const struct mtk_clk_usb apmixed_usb[] __initconst = {
- APMIXED_USB(CLK_APMIXED_REF2USB_TX, "ref2usb_tx", "clk26m", 0x8),
-};
-
-#define MT8173_PLL_FMAX (3000UL * MHZ)
-
-#define CON0_MT8173_RST_BAR BIT(24)
-
-#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift, _div_table) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = CON0_MT8173_RST_BAR, \
- .fmax = MT8173_PLL_FMAX, \
- .pcwbits = _pcwbits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .div_table = _div_table, \
- }
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift) \
- PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
- NULL)
-
-static const struct mtk_pll_div_table mmpll_div_table[] = {
- { .div = 0, .freq = MT8173_PLL_FMAX },
- { .div = 1, .freq = 1000000000 },
- { .div = 2, .freq = 702000000 },
- { .div = 3, .freq = 253500000 },
- { .div = 4, .freq = 126750000 },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0, 0, 21, 0x204, 24, 0x0, 0x204, 0),
- PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0, 0, 21, 0x214, 24, 0x0, 0x214, 0),
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000100, HAVE_RST_BAR, 21, 0x220, 4, 0x0, 0x224, 0),
- PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000000, HAVE_RST_BAR, 7, 0x230, 4, 0x0, 0x234, 14),
- PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0, 0, 21, 0x244, 24, 0x0, 0x244, 0, mmpll_div_table),
- PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0, 0, 21, 0x250, 4, 0x0, 0x254, 0),
- PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0, 0, 21, 0x260, 4, 0x0, 0x264, 0),
- PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0, 0, 21, 0x270, 4, 0x0, 0x274, 0),
- PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0, 0, 21, 0x280, 4, 0x0, 0x284, 0),
- PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0, 0, 21, 0x290, 4, 0x0, 0x294, 0),
- PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0),
- PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0),
- PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0),
- PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0),
-};
-
-static void __init mtk_apmixedsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- struct clk_hw *hw;
- int r, i;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- mt8173_pll_clk_data = clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
- if (!clk_data) {
- iounmap(base);
- return;
- }
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
-
- for (i = 0; i < ARRAY_SIZE(apmixed_usb); i++) {
- const struct mtk_clk_usb *cku = &apmixed_usb[i];
-
- hw = mtk_clk_register_ref2usb_tx(cku->name, cku->parent, base + cku->reg_ofs);
- if (IS_ERR(hw)) {
- pr_err("Failed to register clk %s: %ld\n", cku->name, PTR_ERR(hw));
- continue;
- }
-
- clk_data->hws[cku->id] = hw;
- }
-
- hw = clk_hw_register_divider(NULL, "hdmi_ref", "tvdpll_594m", 0,
- base + 0x40, 16, 3, CLK_DIVIDER_POWER_OF_TWO,
- NULL);
- clk_data->hws[CLK_APMIXED_HDMI_REF] = hw;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-
- mtk_clk_enable_critical();
-}
-CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys",
- mtk_apmixedsys_init);
-
-static void __init mtk_imgsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_imgsys, "mediatek,mt8173-imgsys", mtk_imgsys_init);
-
-static void __init mtk_vdecsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_NR_CLK);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_vdecsys, "mediatek,mt8173-vdecsys", mtk_vdecsys_init);
-
-static void __init mtk_vencsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK);
-
- mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_vencsys, "mediatek,mt8173-vencsys", mtk_vencsys_init);
-
-static void __init mtk_vencltsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_VENCLT_NR_CLK);
-
- mtk_clk_register_gates(node, venclt_clks, ARRAY_SIZE(venclt_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_vencltsys, "mediatek,mt8173-vencltsys", mtk_vencltsys_init);
diff --git a/drivers/clk/mediatek/clk-mt8183-apmixedsys.c b/drivers/clk/mediatek/clk-mt8183-apmixedsys.c
new file mode 100644
index 000000000000..551adbfd7ac9
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8183-apmixedsys.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Weiyi Lu <weiyi.lu@mediatek.com>
+ * Copyright (c) 2023 Collabora, Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8183-clk.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+static const struct mtk_gate_regs apmixed_cg_regs = {
+ .set_ofs = 0x20,
+ .clr_ofs = 0x20,
+ .sta_ofs = 0x20,
+};
+
+#define GATE_APMIXED_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &apmixed_cg_regs, \
+ _shift, &mtk_clk_gate_ops_no_setclr_inv, _flags)
+
+#define GATE_APMIXED(_id, _name, _parent, _shift) \
+ GATE_APMIXED_FLAGS(_id, _name, _parent, _shift, 0)
+
+/*
+ * CRITICAL CLOCK:
+ * apmixed_appll26m is the toppest clock gate of all PLLs.
+ */
+static const struct mtk_gate apmixed_clks[] = {
+ /* AUDIO0 */
+ GATE_APMIXED(CLK_APMIXED_SSUSB_26M, "apmixed_ssusb26m", "f_f26m_ck", 4),
+ GATE_APMIXED_FLAGS(CLK_APMIXED_APPLL_26M, "apmixed_appll26m",
+ "f_f26m_ck", 5, CLK_IS_CRITICAL),
+ GATE_APMIXED(CLK_APMIXED_MIPIC0_26M, "apmixed_mipic026m", "f_f26m_ck", 6),
+ GATE_APMIXED(CLK_APMIXED_MDPLLGP_26M, "apmixed_mdpll26m", "f_f26m_ck", 7),
+ GATE_APMIXED(CLK_APMIXED_MMSYS_26M, "apmixed_mmsys26m", "f_f26m_ck", 8),
+ GATE_APMIXED(CLK_APMIXED_UFS_26M, "apmixed_ufs26m", "f_f26m_ck", 9),
+ GATE_APMIXED(CLK_APMIXED_MIPIC1_26M, "apmixed_mipic126m", "f_f26m_ck", 11),
+ GATE_APMIXED(CLK_APMIXED_MEMPLL_26M, "apmixed_mempll26m", "f_f26m_ck", 13),
+ GATE_APMIXED(CLK_APMIXED_CLKSQ_LVPLL_26M, "apmixed_lvpll26m", "f_f26m_ck", 14),
+ GATE_APMIXED(CLK_APMIXED_MIPID0_26M, "apmixed_mipid026m", "f_f26m_ck", 16),
+ GATE_APMIXED(CLK_APMIXED_MIPID1_26M, "apmixed_mipid126m", "f_f26m_ck", 17),
+};
+
+#define MT8183_PLL_FMAX (3800UL * MHZ)
+#define MT8183_PLL_FMIN (1500UL * MHZ)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \
+ _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift, \
+ _pcw_chg_reg, _div_table) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8183_PLL_FMAX, \
+ .fmin = MT8183_PLL_FMIN, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = _pcwibits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcw_chg_reg = _pcw_chg_reg, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \
+ _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift, \
+ _pcw_chg_reg) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \
+ _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift, \
+ _pcw_chg_reg, NULL)
+
+static const struct mtk_pll_div_table armpll_div_table[] = {
+ { .div = 0, .freq = MT8183_PLL_FMAX },
+ { .div = 1, .freq = 1500 * MHZ },
+ { .div = 2, .freq = 750 * MHZ },
+ { .div = 3, .freq = 375 * MHZ },
+ { .div = 4, .freq = 187500000 },
+ { /* sentinel */ }
+};
+
+static const struct mtk_pll_div_table mfgpll_div_table[] = {
+ { .div = 0, .freq = MT8183_PLL_FMAX },
+ { .div = 1, .freq = 1600 * MHZ },
+ { .div = 2, .freq = 800 * MHZ },
+ { .div = 3, .freq = 400 * MHZ },
+ { .div = 4, .freq = 200 * MHZ },
+ { /* sentinel */ }
+};
+
+static const struct mtk_pll_data plls[] = {
+ PLL_B(CLK_APMIXED_ARMPLL_LL, "armpll_ll", 0x0200, 0x020C, 0,
+ HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0204, 24, 0x0, 0x0, 0,
+ 0x0204, 0, 0, armpll_div_table),
+ PLL_B(CLK_APMIXED_ARMPLL_L, "armpll_l", 0x0210, 0x021C, 0,
+ HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0214, 24, 0x0, 0x0, 0,
+ 0x0214, 0, 0, armpll_div_table),
+ PLL(CLK_APMIXED_CCIPLL, "ccipll", 0x0290, 0x029C, 0,
+ HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0294, 24, 0x0, 0x0, 0,
+ 0x0294, 0, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0220, 0x022C, 0,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0224, 24, 0x0, 0x0, 0,
+ 0x0224, 0, 0),
+ PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x0230, 0x023C, 0,
+ HAVE_RST_BAR, BIT(24), 22, 8, 0x0234, 24, 0x0, 0x0, 0,
+ 0x0234, 0, 0),
+ PLL_B(CLK_APMIXED_MFGPLL, "mfgpll", 0x0240, 0x024C, 0,
+ 0, 0, 22, 8, 0x0244, 24, 0x0, 0x0, 0, 0x0244, 0, 0,
+ mfgpll_div_table),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0250, 0x025C, 0,
+ 0, 0, 22, 8, 0x0254, 24, 0x0, 0x0, 0, 0x0254, 0, 0),
+ PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x0260, 0x026C, 0,
+ 0, 0, 22, 8, 0x0264, 24, 0x0, 0x0, 0, 0x0264, 0, 0),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0270, 0x027C, 0,
+ HAVE_RST_BAR, BIT(23), 22, 8, 0x0274, 24, 0x0, 0x0, 0,
+ 0x0274, 0, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x02A0, 0x02B0, 0,
+ 0, 0, 32, 8, 0x02A0, 1, 0x02A8, 0x0014, 0, 0x02A4, 0, 0x02A0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x02b4, 0x02c4, 0,
+ 0, 0, 32, 8, 0x02B4, 1, 0x02BC, 0x0014, 1, 0x02B8, 0, 0x02B4),
+};
+
+static int clk_mt8183_apmixed_probe(struct platform_device *pdev)
+{
+ void __iomem *base;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_devm_alloc_clk_data(dev, CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (ret)
+ return ret;
+
+ ret = mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
+ ARRAY_SIZE(apmixed_clks), clk_data);
+ if (ret)
+ goto unregister_plls;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_gates;
+
+ return 0;
+
+unregister_gates:
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+
+ return ret;
+}
+
+static const struct of_device_id of_match_clk_mt8183_apmixed[] = {
+ { .compatible = "mediatek,mt8183-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_apmixed);
+
+static struct platform_driver clk_mt8183_apmixed_drv = {
+ .probe = clk_mt8183_apmixed_probe,
+ .driver = {
+ .name = "clk-mt8183-apmixed",
+ .of_match_table = of_match_clk_mt8183_apmixed,
+ },
+};
+builtin_platform_driver(clk_mt8183_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT8183 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-audio.c b/drivers/clk/mediatek/clk-mt8183-audio.c
index b2d7746eddbe..011d329ad30e 100644
--- a/drivers/clk/mediatek/clk-mt8183-audio.c
+++ b/drivers/clk/mediatek/clk-mt8183-audio.c
@@ -67,39 +67,47 @@ static const struct mtk_gate audio_clks[] = {
20),
};
+static const struct mtk_clk_desc audio_desc = {
+ .clks = audio_clks,
+ .num_clks = ARRAY_SIZE(audio_clks),
+};
+
static int clk_mt8183_audio_probe(struct platform_device *pdev)
{
- struct clk_hw_onecell_data *clk_data;
int r;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_AUDIO_NR_CLK);
-
- mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks),
- clk_data);
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ r = mtk_clk_simple_probe(pdev);
if (r)
return r;
r = devm_of_platform_populate(&pdev->dev);
if (r)
- of_clk_del_provider(node);
+ mtk_clk_simple_remove(pdev);
return r;
}
+static void clk_mt8183_audio_remove(struct platform_device *pdev)
+{
+ of_platform_depopulate(&pdev->dev);
+ mtk_clk_simple_remove(pdev);
+}
+
static const struct of_device_id of_match_clk_mt8183_audio[] = {
- { .compatible = "mediatek,mt8183-audiosys", },
- {}
+ { .compatible = "mediatek,mt8183-audiosys", .data = &audio_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_audio);
static struct platform_driver clk_mt8183_audio_drv = {
.probe = clk_mt8183_audio_probe,
+ .remove = clk_mt8183_audio_remove,
.driver = {
.name = "clk-mt8183-audio",
.of_match_table = of_match_clk_mt8183_audio,
},
};
+module_platform_driver(clk_mt8183_audio_drv);
-builtin_platform_driver(clk_mt8183_audio_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 audio clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-cam.c b/drivers/clk/mediatek/clk-mt8183-cam.c
index fcc598a45165..c7642085f8de 100644
--- a/drivers/clk/mediatek/clk-mt8183-cam.c
+++ b/drivers/clk/mediatek/clk-mt8183-cam.c
@@ -34,30 +34,30 @@ static const struct mtk_gate cam_clks[] = {
GATE_CAM(CLK_CAM_CCU, "cam_ccu", "cam_sel", 12),
};
-static int clk_mt8183_cam_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_CAM_NR_CLK);
-
- mtk_clk_register_gates(node, cam_clks, ARRAY_SIZE(cam_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc cam_desc = {
+ .clks = cam_clks,
+ .num_clks = ARRAY_SIZE(cam_clks),
+};
static const struct of_device_id of_match_clk_mt8183_cam[] = {
- { .compatible = "mediatek,mt8183-camsys", },
- {}
+ {
+ .compatible = "mediatek,mt8183-camsys",
+ .data = &cam_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_cam);
static struct platform_driver clk_mt8183_cam_drv = {
- .probe = clk_mt8183_cam_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-cam",
.of_match_table = of_match_clk_mt8183_cam,
},
};
+module_platform_driver(clk_mt8183_cam_drv);
-builtin_platform_driver(clk_mt8183_cam_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-img.c b/drivers/clk/mediatek/clk-mt8183-img.c
index eb2def2cf0ae..ee92459c74ca 100644
--- a/drivers/clk/mediatek/clk-mt8183-img.c
+++ b/drivers/clk/mediatek/clk-mt8183-img.c
@@ -34,30 +34,30 @@ static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_OWE, "img_owe", "img_sel", 9),
};
-static int clk_mt8183_img_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
-
- mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc img_desc = {
+ .clks = img_clks,
+ .num_clks = ARRAY_SIZE(img_clks),
+};
static const struct of_device_id of_match_clk_mt8183_img[] = {
- { .compatible = "mediatek,mt8183-imgsys", },
- {}
+ {
+ .compatible = "mediatek,mt8183-imgsys",
+ .data = &img_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_img);
static struct platform_driver clk_mt8183_img_drv = {
- .probe = clk_mt8183_img_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-img",
.of_match_table = of_match_clk_mt8183_img,
},
};
+module_platform_driver(clk_mt8183_img_drv);
-builtin_platform_driver(clk_mt8183_img_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-ipu0.c b/drivers/clk/mediatek/clk-mt8183-ipu0.c
index b30fc9f47518..6831747f123b 100644
--- a/drivers/clk/mediatek/clk-mt8183-ipu0.c
+++ b/drivers/clk/mediatek/clk-mt8183-ipu0.c
@@ -27,30 +27,30 @@ static const struct mtk_gate ipu_core0_clks[] = {
GATE_IPU_CORE0(CLK_IPU_CORE0_IPU, "ipu_core0_ipu", "dsp_sel", 2),
};
-static int clk_mt8183_ipu_core0_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IPU_CORE0_NR_CLK);
-
- mtk_clk_register_gates(node, ipu_core0_clks, ARRAY_SIZE(ipu_core0_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc ipu_core0_desc = {
+ .clks = ipu_core0_clks,
+ .num_clks = ARRAY_SIZE(ipu_core0_clks),
+};
static const struct of_device_id of_match_clk_mt8183_ipu_core0[] = {
- { .compatible = "mediatek,mt8183-ipu_core0", },
- {}
+ {
+ .compatible = "mediatek,mt8183-ipu_core0",
+ .data = &ipu_core0_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_ipu_core0);
static struct platform_driver clk_mt8183_ipu_core0_drv = {
- .probe = clk_mt8183_ipu_core0_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-ipu_core0",
.of_match_table = of_match_clk_mt8183_ipu_core0,
},
};
+module_platform_driver(clk_mt8183_ipu_core0_drv);
-builtin_platform_driver(clk_mt8183_ipu_core0_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Pri. Image Processing Unit clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-ipu1.c b/drivers/clk/mediatek/clk-mt8183-ipu1.c
index b378957e11d0..ecf434432e7b 100644
--- a/drivers/clk/mediatek/clk-mt8183-ipu1.c
+++ b/drivers/clk/mediatek/clk-mt8183-ipu1.c
@@ -27,30 +27,30 @@ static const struct mtk_gate ipu_core1_clks[] = {
GATE_IPU_CORE1(CLK_IPU_CORE1_IPU, "ipu_core1_ipu", "dsp_sel", 2),
};
-static int clk_mt8183_ipu_core1_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IPU_CORE1_NR_CLK);
-
- mtk_clk_register_gates(node, ipu_core1_clks, ARRAY_SIZE(ipu_core1_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc ipu_core1_desc = {
+ .clks = ipu_core1_clks,
+ .num_clks = ARRAY_SIZE(ipu_core1_clks),
+};
static const struct of_device_id of_match_clk_mt8183_ipu_core1[] = {
- { .compatible = "mediatek,mt8183-ipu_core1", },
- {}
+ {
+ .compatible = "mediatek,mt8183-ipu_core1",
+ .data = &ipu_core1_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_ipu_core1);
static struct platform_driver clk_mt8183_ipu_core1_drv = {
- .probe = clk_mt8183_ipu_core1_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-ipu_core1",
.of_match_table = of_match_clk_mt8183_ipu_core1,
},
};
+module_platform_driver(clk_mt8183_ipu_core1_drv);
-builtin_platform_driver(clk_mt8183_ipu_core1_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Sec. Image Processing Unit clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-ipu_adl.c b/drivers/clk/mediatek/clk-mt8183-ipu_adl.c
index 941b43ac8bec..c1a770ba3245 100644
--- a/drivers/clk/mediatek/clk-mt8183-ipu_adl.c
+++ b/drivers/clk/mediatek/clk-mt8183-ipu_adl.c
@@ -25,30 +25,30 @@ static const struct mtk_gate ipu_adl_clks[] = {
GATE_IPU_ADL_I(CLK_IPU_ADL_CABGEN, "ipu_adl_cabgen", "dsp_sel", 24),
};
-static int clk_mt8183_ipu_adl_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IPU_ADL_NR_CLK);
-
- mtk_clk_register_gates(node, ipu_adl_clks, ARRAY_SIZE(ipu_adl_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc ipu_adl_desc = {
+ .clks = ipu_adl_clks,
+ .num_clks = ARRAY_SIZE(ipu_adl_clks),
+};
static const struct of_device_id of_match_clk_mt8183_ipu_adl[] = {
- { .compatible = "mediatek,mt8183-ipu_adl", },
- {}
+ {
+ .compatible = "mediatek,mt8183-ipu_adl",
+ .data = &ipu_adl_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_ipu_adl);
static struct platform_driver clk_mt8183_ipu_adl_drv = {
- .probe = clk_mt8183_ipu_adl_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-ipu_adl",
.of_match_table = of_match_clk_mt8183_ipu_adl,
},
};
+module_platform_driver(clk_mt8183_ipu_adl_drv);
-builtin_platform_driver(clk_mt8183_ipu_adl_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Image Processing Unit ADL driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-ipu_conn.c b/drivers/clk/mediatek/clk-mt8183-ipu_conn.c
index ae82c2e17110..f0e72e6edb7a 100644
--- a/drivers/clk/mediatek/clk-mt8183-ipu_conn.c
+++ b/drivers/clk/mediatek/clk-mt8183-ipu_conn.c
@@ -94,30 +94,30 @@ static const struct mtk_gate ipu_conn_clks[] = {
"ipu_conn_cab3to1_slice", "dsp1_sel", 17),
};
-static int clk_mt8183_ipu_conn_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_IPU_CONN_NR_CLK);
-
- mtk_clk_register_gates(node, ipu_conn_clks, ARRAY_SIZE(ipu_conn_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc ipu_conn_desc = {
+ .clks = ipu_conn_clks,
+ .num_clks = ARRAY_SIZE(ipu_conn_clks),
+};
static const struct of_device_id of_match_clk_mt8183_ipu_conn[] = {
- { .compatible = "mediatek,mt8183-ipu_conn", },
- {}
+ {
+ .compatible = "mediatek,mt8183-ipu_conn",
+ .data = &ipu_conn_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_ipu_conn);
static struct platform_driver clk_mt8183_ipu_conn_drv = {
- .probe = clk_mt8183_ipu_conn_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-ipu_conn",
.of_match_table = of_match_clk_mt8183_ipu_conn,
},
};
+module_platform_driver(clk_mt8183_ipu_conn_drv);
-builtin_platform_driver(clk_mt8183_ipu_conn_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Image Processing Unit Bus clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-mfgcfg.c b/drivers/clk/mediatek/clk-mt8183-mfgcfg.c
index d774edaf760b..be44889783ff 100644
--- a/drivers/clk/mediatek/clk-mt8183-mfgcfg.c
+++ b/drivers/clk/mediatek/clk-mt8183-mfgcfg.c
@@ -18,40 +18,39 @@ static const struct mtk_gate_regs mfg_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_MFG(_id, _name, _parent, _shift) \
- GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, \
- &mtk_clk_gate_ops_setclr)
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_SET_RATE_PARENT)
static const struct mtk_gate mfg_clks[] = {
GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_sel", 0)
};
-static int clk_mt8183_mfg_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- pm_runtime_enable(&pdev->dev);
-
- clk_data = mtk_alloc_clk_data(CLK_MFG_NR_CLK);
-
- mtk_clk_register_gates_with_dev(node, mfg_clks, ARRAY_SIZE(mfg_clks),
- clk_data, &pdev->dev);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc mfg_desc = {
+ .clks = mfg_clks,
+ .num_clks = ARRAY_SIZE(mfg_clks),
+ .need_runtime_pm = true,
+};
static const struct of_device_id of_match_clk_mt8183_mfg[] = {
- { .compatible = "mediatek,mt8183-mfgcfg", },
- {}
+ {
+ .compatible = "mediatek,mt8183-mfgcfg",
+ .data = &mfg_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_mfg);
static struct platform_driver clk_mt8183_mfg_drv = {
- .probe = clk_mt8183_mfg_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-mfg",
.of_match_table = of_match_clk_mt8183_mfg,
},
};
+module_platform_driver(clk_mt8183_mfg_drv);
-builtin_platform_driver(clk_mt8183_mfg_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-mm.c b/drivers/clk/mediatek/clk-mt8183-mm.c
index 11ecc6fb0065..0f132f05fa8b 100644
--- a/drivers/clk/mediatek/clk-mt8183-mm.c
+++ b/drivers/clk/mediatek/clk-mt8183-mm.c
@@ -82,25 +82,26 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM1(CLK_MM_DBI_IF, "mm_dbi_if", "dpi0_sel", 13),
};
-static int clk_mt8183_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
-
- mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
- clk_data);
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct platform_device_id clk_mt8183_mm_id_table[] = {
+ { .name = "clk-mt8183-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8183_mm_id_table);
static struct platform_driver clk_mt8183_mm_drv = {
- .probe = clk_mt8183_mm_probe,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8183-mm",
},
+ .id_table = clk_mt8183_mm_id_table,
};
+module_platform_driver(clk_mt8183_mm_drv);
-builtin_platform_driver(clk_mt8183_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-vdec.c b/drivers/clk/mediatek/clk-mt8183-vdec.c
index 0548cde159d0..43bf34077b16 100644
--- a/drivers/clk/mediatek/clk-mt8183-vdec.c
+++ b/drivers/clk/mediatek/clk-mt8183-vdec.c
@@ -38,30 +38,30 @@ static const struct mtk_gate vdec_clks[] = {
GATE_VDEC1_I(CLK_VDEC_LARB1, "vdec_larb1", "mm_sel", 0),
};
-static int clk_mt8183_vdec_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VDEC_NR_CLK);
-
- mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
+};
static const struct of_device_id of_match_clk_mt8183_vdec[] = {
- { .compatible = "mediatek,mt8183-vdecsys", },
- {}
+ {
+ .compatible = "mediatek,mt8183-vdecsys",
+ .data = &vdec_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_vdec);
static struct platform_driver clk_mt8183_vdec_drv = {
- .probe = clk_mt8183_vdec_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-vdec",
.of_match_table = of_match_clk_mt8183_vdec,
},
};
+module_platform_driver(clk_mt8183_vdec_drv);
-builtin_platform_driver(clk_mt8183_vdec_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183-venc.c b/drivers/clk/mediatek/clk-mt8183-venc.c
index f86ec607d87a..c3d99b3b8ff7 100644
--- a/drivers/clk/mediatek/clk-mt8183-venc.c
+++ b/drivers/clk/mediatek/clk-mt8183-venc.c
@@ -30,30 +30,30 @@ static const struct mtk_gate venc_clks[] = {
"mm_sel", 8),
};
-static int clk_mt8183_venc_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK);
-
- mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
+};
static const struct of_device_id of_match_clk_mt8183_venc[] = {
- { .compatible = "mediatek,mt8183-vencsys", },
- {}
+ {
+ .compatible = "mediatek,mt8183-vencsys",
+ .data = &venc_desc,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_venc);
static struct platform_driver clk_mt8183_venc_drv = {
- .probe = clk_mt8183_venc_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183-venc",
.of_match_table = of_match_clk_mt8183_venc,
},
};
+module_platform_driver(clk_mt8183_venc_drv);
-builtin_platform_driver(clk_mt8183_venc_drv);
+MODULE_DESCRIPTION("MediaTek MT8183 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8183.c b/drivers/clk/mediatek/clk-mt8183.c
index 8512101e1189..aa7cc7709b2d 100644
--- a/drivers/clk/mediatek/clk-mt8183.c
+++ b/drivers/clk/mediatek/clk-mt8183.c
@@ -5,16 +5,13 @@
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk-gate.h"
#include "clk-mtk.h"
#include "clk-mux.h"
-#include "clk-pll.h"
#include <dt-bindings/clock/mt8183-clk.h>
@@ -26,155 +23,86 @@ static const struct mtk_fixed_clk top_fixed_clks[] = {
FIXED_CLK(CLK_TOP_UNIVP_192M, "univpll_192m", "univpll", 192000000),
};
-static const struct mtk_fixed_factor top_early_divs[] = {
- FACTOR(CLK_TOP_CLK13M, "clk13m", "clk26m", 1, 2),
-};
-
+/*
+ * To retain compatibility with older devicetrees, we keep CLK_TOP_CLK13M
+ * valid, but renamed from "clk13m" (defined as fixed clock in the new
+ * devicetrees) to "clk26m_d2", satisfying the older clock assignments.
+ * This means that on new devicetrees "clk26m_d2" is unused.
+ */
static const struct mtk_fixed_factor top_divs[] = {
- FACTOR(CLK_TOP_F26M_CK_D2, "csw_f26m_ck_d2", "clk26m", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL_CK, "syspll_ck", "mainpll", 1,
- 1),
- FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "syspll_ck", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL_D2_D2, "syspll_d2_d2", "syspll_d2", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL_D2_D4, "syspll_d2_d4", "syspll_d2", 1,
- 4),
- FACTOR(CLK_TOP_SYSPLL_D2_D8, "syspll_d2_d8", "syspll_d2", 1,
- 8),
- FACTOR(CLK_TOP_SYSPLL_D2_D16, "syspll_d2_d16", "syspll_d2", 1,
- 16),
- FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1,
- 3),
- FACTOR(CLK_TOP_SYSPLL_D3_D2, "syspll_d3_d2", "syspll_d3", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL_D3_D4, "syspll_d3_d4", "syspll_d3", 1,
- 4),
- FACTOR(CLK_TOP_SYSPLL_D3_D8, "syspll_d3_d8", "syspll_d3", 1,
- 8),
- FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1,
- 5),
- FACTOR(CLK_TOP_SYSPLL_D5_D2, "syspll_d5_d2", "syspll_d5", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL_D5_D4, "syspll_d5_d4", "syspll_d5", 1,
- 4),
- FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1,
- 7),
- FACTOR(CLK_TOP_SYSPLL_D7_D2, "syspll_d7_d2", "syspll_d7", 1,
- 2),
- FACTOR(CLK_TOP_SYSPLL_D7_D4, "syspll_d7_d4", "syspll_d7", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL_CK, "univpll_ck", "univpll", 1,
- 1),
- FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL_D2_D2, "univpll_d2_d2", "univpll_d2", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL_D2_D4, "univpll_d2_d4", "univpll_d2", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL_D2_D8, "univpll_d2_d8", "univpll_d2", 1,
- 8),
- FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1,
- 3),
- FACTOR(CLK_TOP_UNIVPLL_D3_D2, "univpll_d3_d2", "univpll_d3", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL_D3_D4, "univpll_d3_d4", "univpll_d3", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL_D3_D8, "univpll_d3_d8", "univpll_d3", 1,
- 8),
- FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1,
- 5),
- FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1,
- 4),
- FACTOR(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1,
- 8),
- FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1,
- 7),
- FACTOR(CLK_TOP_UNIVP_192M_CK, "univ_192m_ck", "univpll_192m", 1,
- 1),
- FACTOR(CLK_TOP_UNIVP_192M_D2, "univ_192m_d2", "univ_192m_ck", 1,
- 2),
- FACTOR(CLK_TOP_UNIVP_192M_D4, "univ_192m_d4", "univ_192m_ck", 1,
- 4),
- FACTOR(CLK_TOP_UNIVP_192M_D8, "univ_192m_d8", "univ_192m_ck", 1,
- 8),
- FACTOR(CLK_TOP_UNIVP_192M_D16, "univ_192m_d16", "univ_192m_ck", 1,
- 16),
- FACTOR(CLK_TOP_UNIVP_192M_D32, "univ_192m_d32", "univ_192m_ck", 1,
- 32),
- FACTOR(CLK_TOP_APLL1_CK, "apll1_ck", "apll1", 1,
- 1),
- FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1", 1,
- 2),
- FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1,
- 4),
- FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1", 1,
- 8),
- FACTOR(CLK_TOP_APLL2_CK, "apll2_ck", "apll2", 1,
- 1),
- FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2", 1,
- 2),
- FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1,
- 4),
- FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2", 1,
- 8),
- FACTOR(CLK_TOP_TVDPLL_CK, "tvdpll_ck", "tvdpll", 1,
- 1),
- FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_ck", 1,
- 2),
- FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll", 1,
- 4),
- FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll", 1,
- 8),
- FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll", 1,
- 16),
- FACTOR(CLK_TOP_MMPLL_CK, "mmpll_ck", "mmpll", 1,
- 1),
- FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll", 1,
- 4),
- FACTOR(CLK_TOP_MMPLL_D4_D2, "mmpll_d4_d2", "mmpll_d4", 1,
- 2),
- FACTOR(CLK_TOP_MMPLL_D4_D4, "mmpll_d4_d4", "mmpll_d4", 1,
- 4),
- FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1,
- 5),
- FACTOR(CLK_TOP_MMPLL_D5_D2, "mmpll_d5_d2", "mmpll_d5", 1,
- 2),
- FACTOR(CLK_TOP_MMPLL_D5_D4, "mmpll_d5_d4", "mmpll_d5", 1,
- 4),
- FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll", 1,
- 6),
- FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1,
- 7),
- FACTOR(CLK_TOP_MFGPLL_CK, "mfgpll_ck", "mfgpll", 1,
- 1),
- FACTOR(CLK_TOP_MSDCPLL_CK, "msdcpll_ck", "msdcpll", 1,
- 1),
- FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1,
- 2),
- FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1,
- 4),
- FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll", 1,
- 8),
- FACTOR(CLK_TOP_MSDCPLL_D16, "msdcpll_d16", "msdcpll", 1,
- 16),
- FACTOR(CLK_TOP_AD_OSC_CK, "ad_osc_ck", "osc", 1,
- 1),
- FACTOR(CLK_TOP_OSC_D2, "osc_d2", "osc", 1,
- 2),
- FACTOR(CLK_TOP_OSC_D4, "osc_d4", "osc", 1,
- 4),
- FACTOR(CLK_TOP_OSC_D8, "osc_d8", "osc", 1,
- 8),
- FACTOR(CLK_TOP_OSC_D16, "osc_d16", "osc", 1,
- 16),
- FACTOR(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1,
- 2),
- FACTOR(CLK_TOP_UNIVPLL_D3_D16, "univpll_d3_d16", "univpll_d3", 1,
- 16),
+ FACTOR(CLK_TOP_CLK13M, "clk26m_d2", "clk26m", 1, 2),
+ FACTOR(CLK_TOP_F26M_CK_D2, "csw_f26m_ck_d2", "clk26m", 1, 2),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_CK, "syspll_ck", "mainpll", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2, "syspll_d2", "syspll_ck", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2_D2, "syspll_d2_d2", "syspll_d2", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2_D4, "syspll_d2_d4", "syspll_d2", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2_D8, "syspll_d2_d8", "syspll_d2", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D2_D16, "syspll_d2_d16", "syspll_d2", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D3_D2, "syspll_d3_d2", "syspll_d3", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D3_D4, "syspll_d3_d4", "syspll_d3", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D3_D8, "syspll_d3_d8", "syspll_d3", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D5_D2, "syspll_d5_d2", "syspll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D5_D4, "syspll_d5_d4", "syspll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D7_D2, "syspll_d7_d2", "syspll_d7", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_SYSPLL_D7_D4, "syspll_d7_d4", "syspll_d7", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_CK, "univpll_ck", "univpll", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll_ck", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2_D2, "univpll_d2_d2", "univpll_d2", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2_D4, "univpll_d2_d4", "univpll_d2", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2_D8, "univpll_d2_d8", "univpll_d2", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D2, "univpll_d3_d2", "univpll_d3", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D4, "univpll_d3_d4", "univpll_d3", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D8, "univpll_d3_d8", "univpll_d3", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVP_192M_CK, "univ_192m_ck", "univpll_192m", 1, 1, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVP_192M_D2, "univ_192m_d2", "univ_192m_ck", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVP_192M_D4, "univ_192m_d4", "univ_192m_ck", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVP_192M_D8, "univ_192m_d8", "univ_192m_ck", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVP_192M_D16, "univ_192m_d16", "univ_192m_ck", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVP_192M_D32, "univ_192m_d32", "univ_192m_ck", 1, 32, 0),
+ FACTOR(CLK_TOP_APLL1_CK, "apll1_ck", "apll1", 1, 1),
+ FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1", 1, 2),
+ FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1, 4),
+ FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1", 1, 8),
+ FACTOR(CLK_TOP_APLL2_CK, "apll2_ck", "apll2", 1, 1),
+ FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2", 1, 2),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4),
+ FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL_CK, "tvdpll_ck", "tvdpll", 1, 1),
+ FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_ck", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll", 1, 16),
+ FACTOR(CLK_TOP_MMPLL_CK, "mmpll_ck", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D4_D2, "mmpll_d4_d2", "mmpll_d4", 1, 2),
+ FACTOR(CLK_TOP_MMPLL_D4_D4, "mmpll_d4_d4", "mmpll_d4", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5),
+ FACTOR(CLK_TOP_MMPLL_D5_D2, "mmpll_d5_d2", "mmpll_d5", 1, 2),
+ FACTOR(CLK_TOP_MMPLL_D5_D4, "mmpll_d5_d4", "mmpll_d5", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll", 1, 6),
+ FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7),
+ FACTOR(CLK_TOP_MFGPLL_CK, "mfgpll_ck", "mfgpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_CK, "msdcpll_ck", "msdcpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
+ FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll", 1, 8),
+ FACTOR(CLK_TOP_MSDCPLL_D16, "msdcpll_d16", "msdcpll", 1, 16),
+ FACTOR(CLK_TOP_AD_OSC_CK, "ad_osc_ck", "osc", 1, 1),
+ FACTOR(CLK_TOP_OSC_D2, "osc_d2", "osc", 1, 2),
+ FACTOR(CLK_TOP_OSC_D4, "osc_d4", "osc", 1, 4),
+ FACTOR(CLK_TOP_OSC_D8, "osc_d8", "osc", 1, 8),
+ FACTOR(CLK_TOP_OSC_D16, "osc_d16", "osc", 1, 16),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D16, "univpll_d3_d16", "univpll_d3", 1, 16, 0),
};
static const char * const axi_parents[] = {
@@ -398,6 +326,14 @@ static const char * const atb_parents[] = {
"syspll_d5"
};
+static const char * const sspm_parents[] = {
+ "clk26m",
+ "univpll_d2_d4",
+ "syspll_d2_d2",
+ "univpll_d2_d2",
+ "syspll_d3"
+};
+
static const char * const dpi0_parents[] = {
"clk26m",
"tvdpll_d2",
@@ -521,138 +457,102 @@ static const char * const aud_2_parents[] = {
static const struct mtk_mux top_muxes[] = {
/* CLK_CFG_0 */
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_AXI, "axi_sel",
- axi_parents, 0x40,
- 0x44, 0x48, 0, 2, 7, 0x004, 0, CLK_IS_CRITICAL),
+ axi_parents, 0x40, 0x44, 0x48, 0, 2, 7, 0x004, 0,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_MM, "mm_sel",
- mm_parents, 0x40,
- 0x44, 0x48, 8, 3, 15, 0x004, 1),
+ mm_parents, 0x40, 0x44, 0x48, 8, 3, 15, 0x004, 1),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_IMG, "img_sel",
- img_parents, 0x40,
- 0x44, 0x48, 16, 3, 23, 0x004, 2),
+ img_parents, 0x40, 0x44, 0x48, 16, 3, 23, 0x004, 2),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_CAM, "cam_sel",
- cam_parents, 0x40,
- 0x44, 0x48, 24, 4, 31, 0x004, 3),
+ cam_parents, 0x40, 0x44, 0x48, 24, 4, 31, 0x004, 3),
/* CLK_CFG_1 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DSP, "dsp_sel",
- dsp_parents, 0x50,
- 0x54, 0x58, 0, 4, 7, 0x004, 4),
+ dsp_parents, 0x50, 0x54, 0x58, 0, 4, 7, 0x004, 4),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DSP1, "dsp1_sel",
- dsp1_parents, 0x50,
- 0x54, 0x58, 8, 4, 15, 0x004, 5),
+ dsp1_parents, 0x50, 0x54, 0x58, 8, 4, 15, 0x004, 5),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DSP2, "dsp2_sel",
- dsp2_parents, 0x50,
- 0x54, 0x58, 16, 4, 23, 0x004, 6),
+ dsp2_parents, 0x50, 0x54, 0x58, 16, 4, 23, 0x004, 6),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_IPU_IF, "ipu_if_sel",
- ipu_if_parents, 0x50,
- 0x54, 0x58, 24, 4, 31, 0x004, 7),
+ ipu_if_parents, 0x50, 0x54, 0x58, 24, 4, 31, 0x004, 7),
/* CLK_CFG_2 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_MFG, "mfg_sel",
- mfg_parents, 0x60,
- 0x64, 0x68, 0, 2, 7, 0x004, 8),
+ mfg_parents, 0x60, 0x64, 0x68, 0, 2, 7, 0x004, 8),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_F52M_MFG, "f52m_mfg_sel",
- f52m_mfg_parents, 0x60,
- 0x64, 0x68, 8, 2, 15, 0x004, 9),
+ f52m_mfg_parents, 0x60, 0x64, 0x68, 8, 2, 15, 0x004, 9),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_CAMTG, "camtg_sel",
- camtg_parents, 0x60,
- 0x64, 0x68, 16, 3, 23, 0x004, 10),
+ camtg_parents, 0x60, 0x64, 0x68, 16, 3, 23, 0x004, 10),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_CAMTG2, "camtg2_sel",
- camtg2_parents, 0x60,
- 0x64, 0x68, 24, 3, 31, 0x004, 11),
+ camtg2_parents, 0x60, 0x64, 0x68, 24, 3, 31, 0x004, 11),
/* CLK_CFG_3 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_CAMTG3, "camtg3_sel",
- camtg3_parents, 0x70,
- 0x74, 0x78, 0, 3, 7, 0x004, 12),
+ camtg3_parents, 0x70, 0x74, 0x78, 0, 3, 7, 0x004, 12),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_CAMTG4, "camtg4_sel",
- camtg4_parents, 0x70,
- 0x74, 0x78, 8, 3, 15, 0x004, 13),
+ camtg4_parents, 0x70, 0x74, 0x78, 8, 3, 15, 0x004, 13),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_UART, "uart_sel",
- uart_parents, 0x70,
- 0x74, 0x78, 16, 1, 23, 0x004, 14),
+ uart_parents, 0x70, 0x74, 0x78, 16, 1, 23, 0x004, 14),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_SPI, "spi_sel",
- spi_parents, 0x70,
- 0x74, 0x78, 24, 2, 31, 0x004, 15),
+ spi_parents, 0x70, 0x74, 0x78, 24, 2, 31, 0x004, 15),
/* CLK_CFG_4 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_MSDC50_0_HCLK, "msdc50_hclk_sel",
- msdc50_hclk_parents, 0x80,
- 0x84, 0x88, 0, 2, 7, 0x004, 16),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_MSDC50_0, "msdc50_0_sel",
- msdc50_0_parents, 0x80,
- 0x84, 0x88, 8, 3, 15, 0x004, 17),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_MSDC30_1, "msdc30_1_sel",
- msdc30_1_parents, 0x80,
- 0x84, 0x88, 16, 3, 23, 0x004, 18),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_MSDC30_2, "msdc30_2_sel",
- msdc30_2_parents, 0x80,
- 0x84, 0x88, 24, 3, 31, 0x004, 19),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_MSDC50_0_HCLK, "msdc50_hclk_sel",
+ msdc50_hclk_parents, 0x80, 0x84, 0x88, 0, 2, 7, 0x004, 16, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_MSDC50_0, "msdc50_0_sel",
+ msdc50_0_parents, 0x80, 0x84, 0x88, 8, 3, 15, 0x004, 17, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_MSDC30_1, "msdc30_1_sel",
+ msdc30_1_parents, 0x80, 0x84, 0x88, 16, 3, 23, 0x004, 18, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_MSDC30_2, "msdc30_2_sel",
+ msdc30_2_parents, 0x80, 0x84, 0x88, 24, 3, 31, 0x004, 19, 0),
/* CLK_CFG_5 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_AUDIO, "audio_sel",
- audio_parents, 0x90,
- 0x94, 0x98, 0, 2, 7, 0x004, 20),
+ audio_parents, 0x90, 0x94, 0x98, 0, 2, 7, 0x004, 20),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_AUD_INTBUS, "aud_intbus_sel",
- aud_intbus_parents, 0x90,
- 0x94, 0x98, 8, 2, 15, 0x004, 21),
+ aud_intbus_parents, 0x90, 0x94, 0x98, 8, 2, 15, 0x004, 21),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_PMICSPI, "pmicspi_sel",
- pmicspi_parents, 0x90,
- 0x94, 0x98, 16, 2, 23, 0x004, 22),
+ pmicspi_parents, 0x90, 0x94, 0x98, 16, 2, 23, 0x004, 22),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_FPWRAP_ULPOSC, "fpwrap_ulposc_sel",
- fpwrap_ulposc_parents, 0x90,
- 0x94, 0x98, 24, 2, 31, 0x004, 23),
+ fpwrap_ulposc_parents, 0x90, 0x94, 0x98, 24, 2, 31, 0x004, 23),
/* CLK_CFG_6 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_ATB, "atb_sel",
- atb_parents, 0xa0,
- 0xa4, 0xa8, 0, 2, 7, 0x004, 24),
+ atb_parents, 0xa0, 0xa4, 0xa8, 0, 2, 7, 0x004, 24),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_SSPM, "sspm_sel",
+ sspm_parents, 0xa0, 0xa4, 0xa8, 8, 3, 15, 0x004, 25,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DPI0, "dpi0_sel",
- dpi0_parents, 0xa0,
- 0xa4, 0xa8, 16, 4, 23, 0x004, 26),
+ dpi0_parents, 0xa0, 0xa4, 0xa8, 16, 4, 23, 0x004, 26),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_SCAM, "scam_sel",
- scam_parents, 0xa0,
- 0xa4, 0xa8, 24, 1, 31, 0x004, 27),
+ scam_parents, 0xa0, 0xa4, 0xa8, 24, 1, 31, 0x004, 27),
/* CLK_CFG_7 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DISP_PWM, "disppwm_sel",
- disppwm_parents, 0xb0,
- 0xb4, 0xb8, 0, 3, 7, 0x004, 28),
+ disppwm_parents, 0xb0, 0xb4, 0xb8, 0, 3, 7, 0x004, 28),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_USB_TOP, "usb_top_sel",
- usb_top_parents, 0xb0,
- 0xb4, 0xb8, 8, 2, 15, 0x004, 29),
+ usb_top_parents, 0xb0, 0xb4, 0xb8, 8, 2, 15, 0x004, 29),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_SSUSB_TOP_XHCI, "ssusb_top_xhci_sel",
- ssusb_top_xhci_parents, 0xb0,
- 0xb4, 0xb8, 16, 2, 23, 0x004, 30),
+ ssusb_top_xhci_parents, 0xb0, 0xb4, 0xb8, 16, 2, 23, 0x004, 30),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_SPM, "spm_sel",
- spm_parents, 0xb0,
- 0xb4, 0xb8, 24, 1, 31, 0x008, 0, CLK_IS_CRITICAL),
+ spm_parents, 0xb0, 0xb4, 0xb8, 24, 1, 31, 0x008, 0,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/* CLK_CFG_8 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_I2C, "i2c_sel",
- i2c_parents, 0xc0,
- 0xc4, 0xc8, 0, 2, 7, 0x008, 1),
+ i2c_parents, 0xc0, 0xc4, 0xc8, 0, 2, 7, 0x008, 1),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_SCP, "scp_sel",
- scp_parents, 0xc0,
- 0xc4, 0xc8, 8, 3, 15, 0x008, 2),
+ scp_parents, 0xc0, 0xc4, 0xc8, 8, 3, 15, 0x008, 2),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_SENINF, "seninf_sel",
- seninf_parents, 0xc0,
- 0xc4, 0xc8, 16, 2, 23, 0x008, 3),
+ seninf_parents, 0xc0, 0xc4, 0xc8, 16, 2, 23, 0x008, 3),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DXCC, "dxcc_sel",
- dxcc_parents, 0xc0,
- 0xc4, 0xc8, 24, 2, 31, 0x008, 4),
+ dxcc_parents, 0xc0, 0xc4, 0xc8, 24, 2, 31, 0x008, 4),
/* CLK_CFG_9 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_AUD_ENG1, "aud_eng1_sel",
- aud_engen1_parents, 0xd0,
- 0xd4, 0xd8, 0, 2, 7, 0x008, 5),
+ aud_engen1_parents, 0xd0, 0xd4, 0xd8, 0, 2, 7, 0x008, 5),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_AUD_ENG2, "aud_eng2_sel",
- aud_engen2_parents, 0xd0,
- 0xd4, 0xd8, 8, 2, 15, 0x008, 6),
+ aud_engen2_parents, 0xd0, 0xd4, 0xd8, 8, 2, 15, 0x008, 6),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_FAES_UFSFDE, "faes_ufsfde_sel",
- faes_ufsfde_parents, 0xd0,
- 0xd4, 0xd8, 16, 3, 23, 0x008, 7),
+ faes_ufsfde_parents, 0xd0, 0xd4, 0xd8, 16, 3, 23, 0x008, 7),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_FUFS, "fufs_sel",
- fufs_parents, 0xd0,
- 0xd4, 0xd8, 24, 2, 31, 0x008, 8),
+ fufs_parents, 0xd0, 0xd4, 0xd8, 24, 2, 31, 0x008, 8),
/* CLK_CFG_10 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_AUD_1, "aud_1_sel",
- aud_1_parents, 0xe0,
- 0xe4, 0xe8, 0, 1, 7, 0x008, 9),
+ aud_1_parents, 0xe0, 0xe4, 0xe8, 0, 1, 7, 0x008, 9),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_AUD_2, "aud_2_sel",
- aud_2_parents, 0xe0,
- 0xe4, 0xe8, 8, 1, 15, 0x008, 10),
+ aud_2_parents, 0xe0, 0xe4, 0xe8, 8, 1, 15, 0x008, 10),
};
static const char * const apll_i2s0_parents[] = {
@@ -685,21 +585,6 @@ static const char * const apll_i2s5_parents[] = {
"aud_2_sel"
};
-static struct mtk_composite top_aud_muxes[] = {
- MUX(CLK_TOP_MUX_APLL_I2S0, "apll_i2s0_sel", apll_i2s0_parents,
- 0x320, 8, 1),
- MUX(CLK_TOP_MUX_APLL_I2S1, "apll_i2s1_sel", apll_i2s1_parents,
- 0x320, 9, 1),
- MUX(CLK_TOP_MUX_APLL_I2S2, "apll_i2s2_sel", apll_i2s2_parents,
- 0x320, 10, 1),
- MUX(CLK_TOP_MUX_APLL_I2S3, "apll_i2s3_sel", apll_i2s3_parents,
- 0x320, 11, 1),
- MUX(CLK_TOP_MUX_APLL_I2S4, "apll_i2s4_sel", apll_i2s4_parents,
- 0x320, 12, 1),
- MUX(CLK_TOP_MUX_APLL_I2S5, "apll_i2s5_sel", apll_i2s5_parents,
- 0x328, 20, 1),
-};
-
static const char * const mcu_mp0_parents[] = {
"clk26m",
"armpll_ll",
@@ -730,19 +615,19 @@ static struct mtk_composite mcu_muxes[] = {
MUX(CLK_MCU_BUS_SEL, "mcu_bus_sel", mcu_bus_parents, 0x7C0, 9, 2),
};
-static struct mtk_composite top_aud_divs[] = {
- DIV_GATE(CLK_TOP_APLL12_DIV0, "apll12_div0", "apll_i2s0_sel",
- 0x320, 2, 0x324, 8, 0),
- DIV_GATE(CLK_TOP_APLL12_DIV1, "apll12_div1", "apll_i2s1_sel",
- 0x320, 3, 0x324, 8, 8),
- DIV_GATE(CLK_TOP_APLL12_DIV2, "apll12_div2", "apll_i2s2_sel",
- 0x320, 4, 0x324, 8, 16),
- DIV_GATE(CLK_TOP_APLL12_DIV3, "apll12_div3", "apll_i2s3_sel",
- 0x320, 5, 0x324, 8, 24),
- DIV_GATE(CLK_TOP_APLL12_DIV4, "apll12_div4", "apll_i2s4_sel",
- 0x320, 6, 0x328, 8, 0),
- DIV_GATE(CLK_TOP_APLL12_DIVB, "apll12_divb", "apll12_div4",
- 0x320, 7, 0x328, 8, 8),
+static struct mtk_composite top_aud_comp[] = {
+ MUX(CLK_TOP_MUX_APLL_I2S0, "apll_i2s0_sel", apll_i2s0_parents, 0x320, 8, 1),
+ MUX(CLK_TOP_MUX_APLL_I2S1, "apll_i2s1_sel", apll_i2s1_parents, 0x320, 9, 1),
+ MUX(CLK_TOP_MUX_APLL_I2S2, "apll_i2s2_sel", apll_i2s2_parents, 0x320, 10, 1),
+ MUX(CLK_TOP_MUX_APLL_I2S3, "apll_i2s3_sel", apll_i2s3_parents, 0x320, 11, 1),
+ MUX(CLK_TOP_MUX_APLL_I2S4, "apll_i2s4_sel", apll_i2s4_parents, 0x320, 12, 1),
+ MUX(CLK_TOP_MUX_APLL_I2S5, "apll_i2s5_sel", apll_i2s5_parents, 0x328, 20, 1),
+ DIV_GATE(CLK_TOP_APLL12_DIV0, "apll12_div0", "apll_i2s0_sel", 0x320, 2, 0x324, 8, 0),
+ DIV_GATE(CLK_TOP_APLL12_DIV1, "apll12_div1", "apll_i2s1_sel", 0x320, 3, 0x324, 8, 8),
+ DIV_GATE(CLK_TOP_APLL12_DIV2, "apll12_div2", "apll_i2s2_sel", 0x320, 4, 0x324, 8, 16),
+ DIV_GATE(CLK_TOP_APLL12_DIV3, "apll12_div3", "apll_i2s3_sel", 0x320, 5, 0x324, 8, 24),
+ DIV_GATE(CLK_TOP_APLL12_DIV4, "apll12_div4", "apll_i2s4_sel", 0x320, 6, 0x328, 8, 0),
+ DIV_GATE(CLK_TOP_APLL12_DIVB, "apll12_divb", "apll12_div4", 0x320, 7, 0x328, 8, 8),
};
static const struct mtk_gate_regs top_cg_regs = {
@@ -797,209 +682,128 @@ static const struct mtk_gate_regs infra3_cg_regs = {
GATE_MTK(_id, _name, _parent, &infra2_cg_regs, _shift, \
&mtk_clk_gate_ops_setclr)
+#define GATE_INFRA2_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra2_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr, _flag)
+
#define GATE_INFRA3(_id, _name, _parent, _shift) \
GATE_MTK(_id, _name, _parent, &infra3_cg_regs, _shift, \
&mtk_clk_gate_ops_setclr)
+#define GATE_INFRA3_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra3_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr, _flag)
+
static const struct mtk_gate infra_clks[] = {
/* INFRA0 */
- GATE_INFRA0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr",
- "axi_sel", 0),
- GATE_INFRA0(CLK_INFRA_PMIC_AP, "infra_pmic_ap",
- "axi_sel", 1),
- GATE_INFRA0(CLK_INFRA_PMIC_MD, "infra_pmic_md",
- "axi_sel", 2),
- GATE_INFRA0(CLK_INFRA_PMIC_CONN, "infra_pmic_conn",
- "axi_sel", 3),
- GATE_INFRA0(CLK_INFRA_SCPSYS, "infra_scp",
- "scp_sel", 4),
- GATE_INFRA0(CLK_INFRA_SEJ, "infra_sej",
- "f_f26m_ck", 5),
- GATE_INFRA0(CLK_INFRA_APXGPT, "infra_apxgpt",
- "axi_sel", 6),
- GATE_INFRA0(CLK_INFRA_ICUSB, "infra_icusb",
- "axi_sel", 8),
- GATE_INFRA0(CLK_INFRA_GCE, "infra_gce",
- "axi_sel", 9),
- GATE_INFRA0(CLK_INFRA_THERM, "infra_therm",
- "axi_sel", 10),
- GATE_INFRA0(CLK_INFRA_I2C0, "infra_i2c0",
- "i2c_sel", 11),
- GATE_INFRA0(CLK_INFRA_I2C1, "infra_i2c1",
- "i2c_sel", 12),
- GATE_INFRA0(CLK_INFRA_I2C2, "infra_i2c2",
- "i2c_sel", 13),
- GATE_INFRA0(CLK_INFRA_I2C3, "infra_i2c3",
- "i2c_sel", 14),
- GATE_INFRA0(CLK_INFRA_PWM_HCLK, "infra_pwm_hclk",
- "axi_sel", 15),
- GATE_INFRA0(CLK_INFRA_PWM1, "infra_pwm1",
- "i2c_sel", 16),
- GATE_INFRA0(CLK_INFRA_PWM2, "infra_pwm2",
- "i2c_sel", 17),
- GATE_INFRA0(CLK_INFRA_PWM3, "infra_pwm3",
- "i2c_sel", 18),
- GATE_INFRA0(CLK_INFRA_PWM4, "infra_pwm4",
- "i2c_sel", 19),
- GATE_INFRA0(CLK_INFRA_PWM, "infra_pwm",
- "i2c_sel", 21),
- GATE_INFRA0(CLK_INFRA_UART0, "infra_uart0",
- "uart_sel", 22),
- GATE_INFRA0(CLK_INFRA_UART1, "infra_uart1",
- "uart_sel", 23),
- GATE_INFRA0(CLK_INFRA_UART2, "infra_uart2",
- "uart_sel", 24),
- GATE_INFRA0(CLK_INFRA_UART3, "infra_uart3",
- "uart_sel", 25),
- GATE_INFRA0(CLK_INFRA_GCE_26M, "infra_gce_26m",
- "axi_sel", 27),
- GATE_INFRA0(CLK_INFRA_CQ_DMA_FPC, "infra_cqdma_fpc",
- "axi_sel", 28),
- GATE_INFRA0(CLK_INFRA_BTIF, "infra_btif",
- "axi_sel", 31),
+ GATE_INFRA0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr", "axi_sel", 0),
+ GATE_INFRA0(CLK_INFRA_PMIC_AP, "infra_pmic_ap", "axi_sel", 1),
+ GATE_INFRA0(CLK_INFRA_PMIC_MD, "infra_pmic_md", "axi_sel", 2),
+ GATE_INFRA0(CLK_INFRA_PMIC_CONN, "infra_pmic_conn", "axi_sel", 3),
+ GATE_INFRA0(CLK_INFRA_SCPSYS, "infra_scp", "scp_sel", 4),
+ GATE_INFRA0(CLK_INFRA_SEJ, "infra_sej", "f_f26m_ck", 5),
+ GATE_INFRA0(CLK_INFRA_APXGPT, "infra_apxgpt", "axi_sel", 6),
+ GATE_INFRA0(CLK_INFRA_ICUSB, "infra_icusb", "axi_sel", 8),
+ GATE_INFRA0(CLK_INFRA_GCE, "infra_gce", "axi_sel", 9),
+ GATE_INFRA0(CLK_INFRA_THERM, "infra_therm", "axi_sel", 10),
+ GATE_INFRA0(CLK_INFRA_I2C0, "infra_i2c0", "i2c_sel", 11),
+ GATE_INFRA0(CLK_INFRA_I2C1, "infra_i2c1", "i2c_sel", 12),
+ GATE_INFRA0(CLK_INFRA_I2C2, "infra_i2c2", "i2c_sel", 13),
+ GATE_INFRA0(CLK_INFRA_I2C3, "infra_i2c3", "i2c_sel", 14),
+ GATE_INFRA0(CLK_INFRA_PWM_HCLK, "infra_pwm_hclk", "axi_sel", 15),
+ GATE_INFRA0(CLK_INFRA_PWM1, "infra_pwm1", "i2c_sel", 16),
+ GATE_INFRA0(CLK_INFRA_PWM2, "infra_pwm2", "i2c_sel", 17),
+ GATE_INFRA0(CLK_INFRA_PWM3, "infra_pwm3", "i2c_sel", 18),
+ GATE_INFRA0(CLK_INFRA_PWM4, "infra_pwm4", "i2c_sel", 19),
+ GATE_INFRA0(CLK_INFRA_PWM, "infra_pwm", "i2c_sel", 21),
+ GATE_INFRA0(CLK_INFRA_UART0, "infra_uart0", "uart_sel", 22),
+ GATE_INFRA0(CLK_INFRA_UART1, "infra_uart1", "uart_sel", 23),
+ GATE_INFRA0(CLK_INFRA_UART2, "infra_uart2", "uart_sel", 24),
+ GATE_INFRA0(CLK_INFRA_UART3, "infra_uart3", "uart_sel", 25),
+ GATE_INFRA0(CLK_INFRA_GCE_26M, "infra_gce_26m", "axi_sel", 27),
+ GATE_INFRA0(CLK_INFRA_CQ_DMA_FPC, "infra_cqdma_fpc", "axi_sel", 28),
+ GATE_INFRA0(CLK_INFRA_BTIF, "infra_btif", "axi_sel", 31),
/* INFRA1 */
- GATE_INFRA1(CLK_INFRA_SPI0, "infra_spi0",
- "spi_sel", 1),
- GATE_INFRA1(CLK_INFRA_MSDC0, "infra_msdc0",
- "msdc50_hclk_sel", 2),
- GATE_INFRA1(CLK_INFRA_MSDC1, "infra_msdc1",
- "axi_sel", 4),
- GATE_INFRA1(CLK_INFRA_MSDC2, "infra_msdc2",
- "axi_sel", 5),
- GATE_INFRA1(CLK_INFRA_MSDC0_SCK, "infra_msdc0_sck",
- "msdc50_0_sel", 6),
- GATE_INFRA1(CLK_INFRA_DVFSRC, "infra_dvfsrc",
- "f_f26m_ck", 7),
- GATE_INFRA1(CLK_INFRA_GCPU, "infra_gcpu",
- "axi_sel", 8),
- GATE_INFRA1(CLK_INFRA_TRNG, "infra_trng",
- "axi_sel", 9),
- GATE_INFRA1(CLK_INFRA_AUXADC, "infra_auxadc",
- "f_f26m_ck", 10),
- GATE_INFRA1(CLK_INFRA_CPUM, "infra_cpum",
- "axi_sel", 11),
- GATE_INFRA1(CLK_INFRA_CCIF1_AP, "infra_ccif1_ap",
- "axi_sel", 12),
- GATE_INFRA1(CLK_INFRA_CCIF1_MD, "infra_ccif1_md",
- "axi_sel", 13),
- GATE_INFRA1(CLK_INFRA_AUXADC_MD, "infra_auxadc_md",
- "f_f26m_ck", 14),
- GATE_INFRA1(CLK_INFRA_MSDC1_SCK, "infra_msdc1_sck",
- "msdc30_1_sel", 16),
- GATE_INFRA1(CLK_INFRA_MSDC2_SCK, "infra_msdc2_sck",
- "msdc30_2_sel", 17),
- GATE_INFRA1(CLK_INFRA_AP_DMA, "infra_apdma",
- "axi_sel", 18),
- GATE_INFRA1(CLK_INFRA_XIU, "infra_xiu",
- "axi_sel", 19),
- GATE_INFRA1(CLK_INFRA_DEVICE_APC, "infra_device_apc",
- "axi_sel", 20),
- GATE_INFRA1(CLK_INFRA_CCIF_AP, "infra_ccif_ap",
- "axi_sel", 23),
- GATE_INFRA1(CLK_INFRA_DEBUGSYS, "infra_debugsys",
- "axi_sel", 24),
- GATE_INFRA1(CLK_INFRA_AUDIO, "infra_audio",
- "axi_sel", 25),
- GATE_INFRA1(CLK_INFRA_CCIF_MD, "infra_ccif_md",
- "axi_sel", 26),
- GATE_INFRA1(CLK_INFRA_DXCC_SEC_CORE, "infra_dxcc_sec_core",
- "dxcc_sel", 27),
- GATE_INFRA1(CLK_INFRA_DXCC_AO, "infra_dxcc_ao",
- "dxcc_sel", 28),
- GATE_INFRA1(CLK_INFRA_DEVMPU_BCLK, "infra_devmpu_bclk",
- "axi_sel", 30),
- GATE_INFRA1(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m",
- "f_f26m_ck", 31),
+ GATE_INFRA1(CLK_INFRA_SPI0, "infra_spi0", "spi_sel", 1),
+ GATE_INFRA1(CLK_INFRA_MSDC0, "infra_msdc0", "msdc50_hclk_sel", 2),
+ GATE_INFRA1(CLK_INFRA_MSDC1, "infra_msdc1", "axi_sel", 4),
+ GATE_INFRA1(CLK_INFRA_MSDC2, "infra_msdc2", "axi_sel", 5),
+ GATE_INFRA1(CLK_INFRA_MSDC0_SCK, "infra_msdc0_sck", "msdc50_0_sel", 6),
+ GATE_INFRA1(CLK_INFRA_DVFSRC, "infra_dvfsrc", "f_f26m_ck", 7),
+ GATE_INFRA1(CLK_INFRA_GCPU, "infra_gcpu", "axi_sel", 8),
+ GATE_INFRA1(CLK_INFRA_TRNG, "infra_trng", "axi_sel", 9),
+ GATE_INFRA1(CLK_INFRA_AUXADC, "infra_auxadc", "f_f26m_ck", 10),
+ GATE_INFRA1(CLK_INFRA_CPUM, "infra_cpum", "axi_sel", 11),
+ GATE_INFRA1(CLK_INFRA_CCIF1_AP, "infra_ccif1_ap", "axi_sel", 12),
+ GATE_INFRA1(CLK_INFRA_CCIF1_MD, "infra_ccif1_md", "axi_sel", 13),
+ GATE_INFRA1(CLK_INFRA_AUXADC_MD, "infra_auxadc_md", "f_f26m_ck", 14),
+ GATE_INFRA1(CLK_INFRA_MSDC1_SCK, "infra_msdc1_sck", "msdc30_1_sel", 16),
+ GATE_INFRA1(CLK_INFRA_MSDC2_SCK, "infra_msdc2_sck", "msdc30_2_sel", 17),
+ GATE_INFRA1(CLK_INFRA_AP_DMA, "infra_apdma", "axi_sel", 18),
+ GATE_INFRA1(CLK_INFRA_XIU, "infra_xiu", "axi_sel", 19),
+ GATE_INFRA1(CLK_INFRA_DEVICE_APC, "infra_device_apc", "axi_sel", 20),
+ GATE_INFRA1(CLK_INFRA_CCIF_AP, "infra_ccif_ap", "axi_sel", 23),
+ GATE_INFRA1(CLK_INFRA_DEBUGSYS, "infra_debugsys", "axi_sel", 24),
+ GATE_INFRA1(CLK_INFRA_AUDIO, "infra_audio", "axi_sel", 25),
+ GATE_INFRA1(CLK_INFRA_CCIF_MD, "infra_ccif_md", "axi_sel", 26),
+ GATE_INFRA1(CLK_INFRA_DXCC_SEC_CORE, "infra_dxcc_sec_core", "dxcc_sel", 27),
+ GATE_INFRA1(CLK_INFRA_DXCC_AO, "infra_dxcc_ao", "dxcc_sel", 28),
+ GATE_INFRA1(CLK_INFRA_DEVMPU_BCLK, "infra_devmpu_bclk", "axi_sel", 30),
+ GATE_INFRA1(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m", "f_f26m_ck", 31),
/* INFRA2 */
- GATE_INFRA2(CLK_INFRA_IRTX, "infra_irtx",
- "f_f26m_ck", 0),
- GATE_INFRA2(CLK_INFRA_USB, "infra_usb",
- "usb_top_sel", 1),
- GATE_INFRA2(CLK_INFRA_DISP_PWM, "infra_disppwm",
- "axi_sel", 2),
- GATE_INFRA2(CLK_INFRA_CLDMA_BCLK, "infra_cldma_bclk",
- "axi_sel", 3),
- GATE_INFRA2(CLK_INFRA_AUDIO_26M_BCLK, "infra_audio_26m_bclk",
- "f_f26m_ck", 4),
- GATE_INFRA2(CLK_INFRA_SPI1, "infra_spi1",
- "spi_sel", 6),
- GATE_INFRA2(CLK_INFRA_I2C4, "infra_i2c4",
- "i2c_sel", 7),
- GATE_INFRA2(CLK_INFRA_MODEM_TEMP_SHARE, "infra_md_tmp_share",
- "f_f26m_ck", 8),
- GATE_INFRA2(CLK_INFRA_SPI2, "infra_spi2",
- "spi_sel", 9),
- GATE_INFRA2(CLK_INFRA_SPI3, "infra_spi3",
- "spi_sel", 10),
- GATE_INFRA2(CLK_INFRA_UNIPRO_SCK, "infra_unipro_sck",
- "ssusb_top_xhci_sel", 11),
- GATE_INFRA2(CLK_INFRA_UNIPRO_TICK, "infra_unipro_tick",
- "fufs_sel", 12),
- GATE_INFRA2(CLK_INFRA_UFS_MP_SAP_BCLK, "infra_ufs_mp_sap_bck",
- "fufs_sel", 13),
- GATE_INFRA2(CLK_INFRA_MD32_BCLK, "infra_md32_bclk",
- "axi_sel", 14),
- GATE_INFRA2(CLK_INFRA_UNIPRO_MBIST, "infra_unipro_mbist",
- "axi_sel", 16),
- GATE_INFRA2(CLK_INFRA_I2C5, "infra_i2c5",
- "i2c_sel", 18),
- GATE_INFRA2(CLK_INFRA_I2C5_ARBITER, "infra_i2c5_arbiter",
- "i2c_sel", 19),
- GATE_INFRA2(CLK_INFRA_I2C5_IMM, "infra_i2c5_imm",
- "i2c_sel", 20),
- GATE_INFRA2(CLK_INFRA_I2C1_ARBITER, "infra_i2c1_arbiter",
- "i2c_sel", 21),
- GATE_INFRA2(CLK_INFRA_I2C1_IMM, "infra_i2c1_imm",
- "i2c_sel", 22),
- GATE_INFRA2(CLK_INFRA_I2C2_ARBITER, "infra_i2c2_arbiter",
- "i2c_sel", 23),
- GATE_INFRA2(CLK_INFRA_I2C2_IMM, "infra_i2c2_imm",
- "i2c_sel", 24),
- GATE_INFRA2(CLK_INFRA_SPI4, "infra_spi4",
- "spi_sel", 25),
- GATE_INFRA2(CLK_INFRA_SPI5, "infra_spi5",
- "spi_sel", 26),
- GATE_INFRA2(CLK_INFRA_CQ_DMA, "infra_cqdma",
- "axi_sel", 27),
- GATE_INFRA2(CLK_INFRA_UFS, "infra_ufs",
- "fufs_sel", 28),
- GATE_INFRA2(CLK_INFRA_AES_UFSFDE, "infra_aes_ufsfde",
- "faes_ufsfde_sel", 29),
- GATE_INFRA2(CLK_INFRA_UFS_TICK, "infra_ufs_tick",
- "fufs_sel", 30),
+ GATE_INFRA2(CLK_INFRA_IRTX, "infra_irtx", "f_f26m_ck", 0),
+ GATE_INFRA2(CLK_INFRA_USB, "infra_usb", "usb_top_sel", 1),
+ GATE_INFRA2(CLK_INFRA_DISP_PWM, "infra_disppwm", "axi_sel", 2),
+ GATE_INFRA2(CLK_INFRA_CLDMA_BCLK, "infra_cldma_bclk", "axi_sel", 3),
+ GATE_INFRA2(CLK_INFRA_AUDIO_26M_BCLK, "infra_audio_26m_bclk", "f_f26m_ck", 4),
+ GATE_INFRA2(CLK_INFRA_SPI1, "infra_spi1", "spi_sel", 6),
+ GATE_INFRA2(CLK_INFRA_I2C4, "infra_i2c4", "i2c_sel", 7),
+ GATE_INFRA2(CLK_INFRA_MODEM_TEMP_SHARE, "infra_md_tmp_share", "f_f26m_ck", 8),
+ GATE_INFRA2(CLK_INFRA_SPI2, "infra_spi2", "spi_sel", 9),
+ GATE_INFRA2(CLK_INFRA_SPI3, "infra_spi3", "spi_sel", 10),
+ GATE_INFRA2(CLK_INFRA_UNIPRO_SCK, "infra_unipro_sck", "ssusb_top_xhci_sel", 11),
+ GATE_INFRA2(CLK_INFRA_UNIPRO_TICK, "infra_unipro_tick", "fufs_sel", 12),
+ GATE_INFRA2(CLK_INFRA_UFS_MP_SAP_BCLK, "infra_ufs_mp_sap_bck", "fufs_sel", 13),
+ GATE_INFRA2(CLK_INFRA_MD32_BCLK, "infra_md32_bclk", "axi_sel", 14),
+ /* infra_sspm is main clock in co-processor, should not be closed in Linux. */
+ GATE_INFRA2_FLAGS(CLK_INFRA_SSPM, "infra_sspm", "sspm_sel", 15, CLK_IS_CRITICAL),
+ GATE_INFRA2(CLK_INFRA_UNIPRO_MBIST, "infra_unipro_mbist", "axi_sel", 16),
+ /* infra_sspm_bus_hclk is main clock in co-processor, should not be closed in Linux. */
+ GATE_INFRA2_FLAGS(CLK_INFRA_SSPM_BUS_HCLK, "infra_sspm_bus_hclk", "axi_sel", 17, CLK_IS_CRITICAL),
+ GATE_INFRA2(CLK_INFRA_I2C5, "infra_i2c5", "i2c_sel", 18),
+ GATE_INFRA2(CLK_INFRA_I2C5_ARBITER, "infra_i2c5_arbiter", "i2c_sel", 19),
+ GATE_INFRA2(CLK_INFRA_I2C5_IMM, "infra_i2c5_imm", "i2c_sel", 20),
+ GATE_INFRA2(CLK_INFRA_I2C1_ARBITER, "infra_i2c1_arbiter", "i2c_sel", 21),
+ GATE_INFRA2(CLK_INFRA_I2C1_IMM, "infra_i2c1_imm", "i2c_sel", 22),
+ GATE_INFRA2(CLK_INFRA_I2C2_ARBITER, "infra_i2c2_arbiter", "i2c_sel", 23),
+ GATE_INFRA2(CLK_INFRA_I2C2_IMM, "infra_i2c2_imm", "i2c_sel", 24),
+ GATE_INFRA2(CLK_INFRA_SPI4, "infra_spi4", "spi_sel", 25),
+ GATE_INFRA2(CLK_INFRA_SPI5, "infra_spi5", "spi_sel", 26),
+ GATE_INFRA2(CLK_INFRA_CQ_DMA, "infra_cqdma", "axi_sel", 27),
+ GATE_INFRA2(CLK_INFRA_UFS, "infra_ufs", "fufs_sel", 28),
+ GATE_INFRA2(CLK_INFRA_AES_UFSFDE, "infra_aes_ufsfde", "faes_ufsfde_sel", 29),
+ GATE_INFRA2(CLK_INFRA_UFS_TICK, "infra_ufs_tick", "fufs_sel", 30),
/* INFRA3 */
- GATE_INFRA3(CLK_INFRA_MSDC0_SELF, "infra_msdc0_self",
- "msdc50_0_sel", 0),
- GATE_INFRA3(CLK_INFRA_MSDC1_SELF, "infra_msdc1_self",
- "msdc50_0_sel", 1),
- GATE_INFRA3(CLK_INFRA_MSDC2_SELF, "infra_msdc2_self",
- "msdc50_0_sel", 2),
- GATE_INFRA3(CLK_INFRA_UFS_AXI, "infra_ufs_axi",
- "axi_sel", 5),
- GATE_INFRA3(CLK_INFRA_I2C6, "infra_i2c6",
- "i2c_sel", 6),
- GATE_INFRA3(CLK_INFRA_AP_MSDC0, "infra_ap_msdc0",
- "msdc50_hclk_sel", 7),
- GATE_INFRA3(CLK_INFRA_MD_MSDC0, "infra_md_msdc0",
- "msdc50_hclk_sel", 8),
- GATE_INFRA3(CLK_INFRA_CCIF2_AP, "infra_ccif2_ap",
- "axi_sel", 16),
- GATE_INFRA3(CLK_INFRA_CCIF2_MD, "infra_ccif2_md",
- "axi_sel", 17),
- GATE_INFRA3(CLK_INFRA_CCIF3_AP, "infra_ccif3_ap",
- "axi_sel", 18),
- GATE_INFRA3(CLK_INFRA_CCIF3_MD, "infra_ccif3_md",
- "axi_sel", 19),
- GATE_INFRA3(CLK_INFRA_SEJ_F13M, "infra_sej_f13m",
- "f_f26m_ck", 20),
- GATE_INFRA3(CLK_INFRA_AES_BCLK, "infra_aes_bclk",
- "axi_sel", 21),
- GATE_INFRA3(CLK_INFRA_I2C7, "infra_i2c7",
- "i2c_sel", 22),
- GATE_INFRA3(CLK_INFRA_I2C8, "infra_i2c8",
- "i2c_sel", 23),
- GATE_INFRA3(CLK_INFRA_FBIST2FPC, "infra_fbist2fpc",
- "msdc50_0_sel", 24),
+ GATE_INFRA3(CLK_INFRA_MSDC0_SELF, "infra_msdc0_self", "msdc50_0_sel", 0),
+ GATE_INFRA3(CLK_INFRA_MSDC1_SELF, "infra_msdc1_self", "msdc50_0_sel", 1),
+ GATE_INFRA3(CLK_INFRA_MSDC2_SELF, "infra_msdc2_self", "msdc50_0_sel", 2),
+ /* infra_sspm_26m_self is main clock in co-processor, should not be closed in Linux. */
+ GATE_INFRA3_FLAGS(CLK_INFRA_SSPM_26M_SELF, "infra_sspm_26m_self", "f_f26m_ck", 3, CLK_IS_CRITICAL),
+ /* infra_sspm_32k_self is main clock in co-processor, should not be closed in Linux. */
+ GATE_INFRA3_FLAGS(CLK_INFRA_SSPM_32K_SELF, "infra_sspm_32k_self", "clk32k", 4, CLK_IS_CRITICAL),
+ GATE_INFRA3(CLK_INFRA_UFS_AXI, "infra_ufs_axi", "axi_sel", 5),
+ GATE_INFRA3(CLK_INFRA_I2C6, "infra_i2c6", "i2c_sel", 6),
+ GATE_INFRA3(CLK_INFRA_AP_MSDC0, "infra_ap_msdc0", "msdc50_hclk_sel", 7),
+ GATE_INFRA3(CLK_INFRA_MD_MSDC0, "infra_md_msdc0", "msdc50_hclk_sel", 8),
+ GATE_INFRA3(CLK_INFRA_CCIF2_AP, "infra_ccif2_ap", "axi_sel", 16),
+ GATE_INFRA3(CLK_INFRA_CCIF2_MD, "infra_ccif2_md", "axi_sel", 17),
+ GATE_INFRA3(CLK_INFRA_CCIF3_AP, "infra_ccif3_ap", "axi_sel", 18),
+ GATE_INFRA3(CLK_INFRA_CCIF3_MD, "infra_ccif3_md", "axi_sel", 19),
+ GATE_INFRA3(CLK_INFRA_SEJ_F13M, "infra_sej_f13m", "f_f26m_ck", 20),
+ GATE_INFRA3(CLK_INFRA_AES_BCLK, "infra_aes_bclk", "axi_sel", 21),
+ GATE_INFRA3(CLK_INFRA_I2C7, "infra_i2c7", "i2c_sel", 22),
+ GATE_INFRA3(CLK_INFRA_I2C8, "infra_i2c8", "i2c_sel", 23),
+ GATE_INFRA3(CLK_INFRA_FBIST2FPC, "infra_fbist2fpc", "msdc50_0_sel", 24),
};
static const struct mtk_gate_regs peri_cg_regs = {
@@ -1016,140 +820,6 @@ static const struct mtk_gate peri_clks[] = {
GATE_PERI(CLK_PERI_AXI, "peri_axi", "axi_sel", 31),
};
-static const struct mtk_gate_regs apmixed_cg_regs = {
- .set_ofs = 0x20,
- .clr_ofs = 0x20,
- .sta_ofs = 0x20,
-};
-
-#define GATE_APMIXED_FLAGS(_id, _name, _parent, _shift, _flags) \
- GATE_MTK_FLAGS(_id, _name, _parent, &apmixed_cg_regs, \
- _shift, &mtk_clk_gate_ops_no_setclr_inv, _flags)
-
-#define GATE_APMIXED(_id, _name, _parent, _shift) \
- GATE_APMIXED_FLAGS(_id, _name, _parent, _shift, 0)
-
-/*
- * CRITICAL CLOCK:
- * apmixed_appll26m is the toppest clock gate of all PLLs.
- */
-static const struct mtk_gate apmixed_clks[] = {
- /* AUDIO0 */
- GATE_APMIXED(CLK_APMIXED_SSUSB_26M, "apmixed_ssusb26m",
- "f_f26m_ck", 4),
- GATE_APMIXED_FLAGS(CLK_APMIXED_APPLL_26M, "apmixed_appll26m",
- "f_f26m_ck", 5, CLK_IS_CRITICAL),
- GATE_APMIXED(CLK_APMIXED_MIPIC0_26M, "apmixed_mipic026m",
- "f_f26m_ck", 6),
- GATE_APMIXED(CLK_APMIXED_MDPLLGP_26M, "apmixed_mdpll26m",
- "f_f26m_ck", 7),
- GATE_APMIXED(CLK_APMIXED_MMSYS_26M, "apmixed_mmsys26m",
- "f_f26m_ck", 8),
- GATE_APMIXED(CLK_APMIXED_UFS_26M, "apmixed_ufs26m",
- "f_f26m_ck", 9),
- GATE_APMIXED(CLK_APMIXED_MIPIC1_26M, "apmixed_mipic126m",
- "f_f26m_ck", 11),
- GATE_APMIXED(CLK_APMIXED_MEMPLL_26M, "apmixed_mempll26m",
- "f_f26m_ck", 13),
- GATE_APMIXED(CLK_APMIXED_CLKSQ_LVPLL_26M, "apmixed_lvpll26m",
- "f_f26m_ck", 14),
- GATE_APMIXED(CLK_APMIXED_MIPID0_26M, "apmixed_mipid026m",
- "f_f26m_ck", 16),
- GATE_APMIXED(CLK_APMIXED_MIPID1_26M, "apmixed_mipid126m",
- "f_f26m_ck", 17),
-};
-
-#define MT8183_PLL_FMAX (3800UL * MHZ)
-#define MT8183_PLL_FMIN (1500UL * MHZ)
-
-#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \
- _pd_shift, _tuner_reg, _tuner_en_reg, \
- _tuner_en_bit, _pcw_reg, _pcw_shift, \
- _pcw_chg_reg, _div_table) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = _rst_bar_mask, \
- .fmax = MT8183_PLL_FMAX, \
- .fmin = MT8183_PLL_FMIN, \
- .pcwbits = _pcwbits, \
- .pcwibits = _pcwibits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .tuner_en_reg = _tuner_en_reg, \
- .tuner_en_bit = _tuner_en_bit, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .pcw_chg_reg = _pcw_chg_reg, \
- .div_table = _div_table, \
- }
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \
- _pd_shift, _tuner_reg, _tuner_en_reg, \
- _tuner_en_bit, _pcw_reg, _pcw_shift, \
- _pcw_chg_reg) \
- PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \
- _pd_shift, _tuner_reg, _tuner_en_reg, \
- _tuner_en_bit, _pcw_reg, _pcw_shift, \
- _pcw_chg_reg, NULL)
-
-static const struct mtk_pll_div_table armpll_div_table[] = {
- { .div = 0, .freq = MT8183_PLL_FMAX },
- { .div = 1, .freq = 1500 * MHZ },
- { .div = 2, .freq = 750 * MHZ },
- { .div = 3, .freq = 375 * MHZ },
- { .div = 4, .freq = 187500000 },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_div_table mfgpll_div_table[] = {
- { .div = 0, .freq = MT8183_PLL_FMAX },
- { .div = 1, .freq = 1600 * MHZ },
- { .div = 2, .freq = 800 * MHZ },
- { .div = 3, .freq = 400 * MHZ },
- { .div = 4, .freq = 200 * MHZ },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_data plls[] = {
- PLL_B(CLK_APMIXED_ARMPLL_LL, "armpll_ll", 0x0200, 0x020C, 0,
- HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0204, 24, 0x0, 0x0, 0,
- 0x0204, 0, 0, armpll_div_table),
- PLL_B(CLK_APMIXED_ARMPLL_L, "armpll_l", 0x0210, 0x021C, 0,
- HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0214, 24, 0x0, 0x0, 0,
- 0x0214, 0, 0, armpll_div_table),
- PLL(CLK_APMIXED_CCIPLL, "ccipll", 0x0290, 0x029C, 0,
- HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0294, 24, 0x0, 0x0, 0,
- 0x0294, 0, 0),
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0220, 0x022C, 0,
- HAVE_RST_BAR, BIT(24), 22, 8, 0x0224, 24, 0x0, 0x0, 0,
- 0x0224, 0, 0),
- PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x0230, 0x023C, 0,
- HAVE_RST_BAR, BIT(24), 22, 8, 0x0234, 24, 0x0, 0x0, 0,
- 0x0234, 0, 0),
- PLL_B(CLK_APMIXED_MFGPLL, "mfgpll", 0x0240, 0x024C, 0,
- 0, 0, 22, 8, 0x0244, 24, 0x0, 0x0, 0, 0x0244, 0, 0,
- mfgpll_div_table),
- PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0250, 0x025C, 0,
- 0, 0, 22, 8, 0x0254, 24, 0x0, 0x0, 0, 0x0254, 0, 0),
- PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x0260, 0x026C, 0,
- 0, 0, 22, 8, 0x0264, 24, 0x0, 0x0, 0, 0x0264, 0, 0),
- PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0270, 0x027C, 0,
- HAVE_RST_BAR, BIT(23), 22, 8, 0x0274, 24, 0x0, 0x0, 0,
- 0x0274, 0, 0),
- PLL(CLK_APMIXED_APLL1, "apll1", 0x02A0, 0x02B0, 0,
- 0, 0, 32, 8, 0x02A0, 1, 0x02A8, 0x0014, 0, 0x02A4, 0, 0x02A0),
- PLL(CLK_APMIXED_APLL2, "apll2", 0x02b4, 0x02c4, 0,
- 0, 0, 32, 8, 0x02B4, 1, 0x02BC, 0x0014, 1, 0x02B8, 0, 0x02B4),
-};
-
static u16 infra_rst_ofs[] = {
INFRA_RST0_SET_OFFSET,
INFRA_RST1_SET_OFFSET,
@@ -1163,179 +833,79 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_bank_nr = ARRAY_SIZE(infra_rst_ofs),
};
-static int clk_mt8183_apmixed_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
-
- mtk_clk_register_gates(node, apmixed_clks, ARRAY_SIZE(apmixed_clks),
- clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
-
-static struct clk_hw_onecell_data *top_clk_data;
-
-static void clk_mt8183_top_init_early(struct device_node *node)
+/* Register mux notifier for MFG mux */
+static int clk_mt8183_reg_mfg_mux_notifier(struct device *dev, struct clk *clk)
{
+ struct mtk_mux_nb *mfg_mux_nb;
int i;
- top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
-
- for (i = 0; i < CLK_TOP_NR_CLK; i++)
- top_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
-
- mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs),
- top_clk_data);
-
- of_clk_add_hw_provider(node, of_clk_hw_onecell_get, top_clk_data);
-}
-
-CLK_OF_DECLARE_DRIVER(mt8183_topckgen, "mediatek,mt8183-topckgen",
- clk_mt8183_top_init_early);
-
-static int clk_mt8183_top_probe(struct platform_device *pdev)
-{
- void __iomem *base;
- struct device_node *node = pdev->dev.of_node;
+ mfg_mux_nb = devm_kzalloc(dev, sizeof(*mfg_mux_nb), GFP_KERNEL);
+ if (!mfg_mux_nb)
+ return -ENOMEM;
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
- top_clk_data);
-
- mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs),
- top_clk_data);
-
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
-
- mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes),
- node, &mt8183_clk_lock, top_clk_data);
-
- mtk_clk_register_composites(top_aud_muxes, ARRAY_SIZE(top_aud_muxes),
- base, &mt8183_clk_lock, top_clk_data);
-
- mtk_clk_register_composites(top_aud_divs, ARRAY_SIZE(top_aud_divs),
- base, &mt8183_clk_lock, top_clk_data);
-
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
- top_clk_data);
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
- top_clk_data);
-}
-
-static int clk_mt8183_infra_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
-
- mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
- clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r) {
- dev_err(&pdev->dev,
- "%s(): could not register clock provider: %d\n",
- __func__, r);
- return r;
- }
-
- mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
-
- return r;
-}
-
-static int clk_mt8183_peri_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
-
- clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
+ for (i = 0; i < ARRAY_SIZE(top_muxes); i++)
+ if (top_muxes[i].id == CLK_TOP_MUX_MFG)
+ break;
+ if (i == ARRAY_SIZE(top_muxes))
+ return -EINVAL;
- mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
- clk_data);
+ mfg_mux_nb->ops = top_muxes[i].ops;
+ mfg_mux_nb->bypass_index = 0; /* Bypass to 26M crystal */
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ return devm_mtk_clk_mux_notifier_register(dev, clk, mfg_mux_nb);
}
-static int clk_mt8183_mcu_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- void __iomem *base;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .rst_desc = &clk_rst_desc,
+};
- clk_data = mtk_alloc_clk_data(CLK_MCU_NR_CLK);
+static const struct mtk_clk_desc mcu_desc = {
+ .composite_clks = mcu_muxes,
+ .num_composite_clks = ARRAY_SIZE(mcu_muxes),
+ .clk_lock = &mt8183_clk_lock,
+};
- mtk_clk_register_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), base,
- &mt8183_clk_lock, clk_data);
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_clks,
+ .num_clks = ARRAY_SIZE(peri_clks),
+};
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_aud_comp,
+ .num_composite_clks = ARRAY_SIZE(top_aud_comp),
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .clk_lock = &mt8183_clk_lock,
+ .clk_notifier_func = clk_mt8183_reg_mfg_mux_notifier,
+ .mfg_clk_idx = CLK_TOP_MUX_MFG,
+};
static const struct of_device_id of_match_clk_mt8183[] = {
- {
- .compatible = "mediatek,mt8183-apmixedsys",
- .data = clk_mt8183_apmixed_probe,
- }, {
- .compatible = "mediatek,mt8183-topckgen",
- .data = clk_mt8183_top_probe,
- }, {
- .compatible = "mediatek,mt8183-infracfg",
- .data = clk_mt8183_infra_probe,
- }, {
- .compatible = "mediatek,mt8183-pericfg",
- .data = clk_mt8183_peri_probe,
- }, {
- .compatible = "mediatek,mt8183-mcucfg",
- .data = clk_mt8183_mcu_probe,
- }, {
- /* sentinel */
- }
-};
-
-static int clk_mt8183_probe(struct platform_device *pdev)
-{
- int (*clk_probe)(struct platform_device *pdev);
- int r;
-
- clk_probe = of_device_get_match_data(&pdev->dev);
- if (!clk_probe)
- return -EINVAL;
-
- r = clk_probe(pdev);
- if (r)
- dev_err(&pdev->dev,
- "could not register clock provider: %s: %d\n",
- pdev->name, r);
-
- return r;
-}
+ { .compatible = "mediatek,mt8183-infracfg", .data = &infra_desc },
+ { .compatible = "mediatek,mt8183-mcucfg", .data = &mcu_desc },
+ { .compatible = "mediatek,mt8183-pericfg", .data = &peri_desc, },
+ { .compatible = "mediatek,mt8183-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8183);
static struct platform_driver clk_mt8183_drv = {
- .probe = clk_mt8183_probe,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8183",
.of_match_table = of_match_clk_mt8183,
},
};
+module_platform_driver(clk_mt8183_drv)
-static int __init clk_mt8183_init(void)
-{
- return platform_driver_register(&clk_mt8183_drv);
-}
-
-arch_initcall(clk_mt8183_init);
+MODULE_DESCRIPTION("MediaTek MT8183 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-apmixedsys.c b/drivers/clk/mediatek/clk-mt8186-apmixedsys.c
index e692a2a67ce1..4b2b16578232 100644
--- a/drivers/clk/mediatek/clk-mt8186-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt8186-apmixedsys.c
@@ -7,8 +7,10 @@
#include <linux/platform_device.h>
#include <dt-bindings/clock/mt8186-clk.h>
+#include "clk-fhctl.h"
#include "clk-mtk.h"
#include "clk-pll.h"
+#include "clk-pllfh.h"
#define MT8186_PLL_FMAX (3800UL * MHZ)
#define MT8186_PLL_FMIN (1500UL * MHZ)
@@ -76,22 +78,81 @@ static const struct mtk_pll_data plls[] = {
0, 0, 32, 0x034C, 24, 0x0044, 0x000C, 5, 0x0350),
};
+enum fh_pll_id {
+ FH_ARMPLL_LL,
+ FH_ARMPLL_BL,
+ FH_CCIPLL,
+ FH_MAINPLL,
+ FH_MMPLL,
+ FH_TVDPLL,
+ FH_RESERVE6,
+ FH_ADSPPLL,
+ FH_MFGPLL,
+ FH_NNAPLL,
+ FH_NNA2PLL,
+ FH_MSDCPLL,
+ FH_RESERVE12,
+ FH_NR_FH,
+};
+
+#define FH(_pllid, _fhid, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fh_ver = FHCTL_PLLFH_V2, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = 0x6003c97, \
+ .slope1_value = 0x6003c97, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_ARMPLL_LL, FH_ARMPLL_LL, 0x003C),
+ FH(CLK_APMIXED_ARMPLL_BL, FH_ARMPLL_BL, 0x0050),
+ FH(CLK_APMIXED_CCIPLL, FH_CCIPLL, 0x0064),
+ FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x0078),
+ FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0x008C),
+ FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0x00A0),
+ FH(CLK_APMIXED_ADSPPLL, FH_ADSPPLL, 0x00C8),
+ FH(CLK_APMIXED_MFGPLL, FH_MFGPLL, 0x00DC),
+ FH(CLK_APMIXED_NNAPLL, FH_NNAPLL, 0x00F0),
+ FH(CLK_APMIXED_NNA2PLL, FH_NNA2PLL, 0x0104),
+ FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x0118),
+};
+
static const struct of_device_id of_match_clk_mt8186_apmixed[] = {
{ .compatible = "mediatek,mt8186-apmixedsys", },
{}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_apmixed);
static int clk_mt8186_apmixed_probe(struct platform_device *pdev)
{
struct clk_hw_onecell_data *clk_data;
struct device_node *node = pdev->dev.of_node;
+ const u8 *fhctl_node = "mediatek,mt8186-fhctl";
int r;
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
if (!clk_data)
return -ENOMEM;
- r = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+
+ r = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
if (r)
goto free_apmixed_data;
@@ -104,22 +165,22 @@ static int clk_mt8186_apmixed_probe(struct platform_device *pdev)
return r;
unregister_plls:
- mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
free_apmixed_data:
mtk_free_clk_data(clk_data);
return r;
}
-static int clk_mt8186_apmixed_remove(struct platform_device *pdev)
+static void clk_mt8186_apmixed_remove(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
of_clk_del_provider(node);
- mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
mtk_free_clk_data(clk_data);
-
- return 0;
}
static struct platform_driver clk_mt8186_apmixed_drv = {
@@ -130,4 +191,7 @@ static struct platform_driver clk_mt8186_apmixed_drv = {
.of_match_table = of_match_clk_mt8186_apmixed,
},
};
-builtin_platform_driver(clk_mt8186_apmixed_drv);
+module_platform_driver(clk_mt8186_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-cam.c b/drivers/clk/mediatek/clk-mt8186-cam.c
index 9ec345a2ce66..2ddd5f90377f 100644
--- a/drivers/clk/mediatek/clk-mt8186-cam.c
+++ b/drivers/clk/mediatek/clk-mt8186-cam.c
@@ -78,6 +78,7 @@ static const struct of_device_id of_match_clk_mt8186_cam[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_cam);
static struct platform_driver clk_mt8186_cam_drv = {
.probe = mtk_clk_simple_probe,
@@ -87,4 +88,7 @@ static struct platform_driver clk_mt8186_cam_drv = {
.of_match_table = of_match_clk_mt8186_cam,
},
};
-builtin_platform_driver(clk_mt8186_cam_drv);
+module_platform_driver(clk_mt8186_cam_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-img.c b/drivers/clk/mediatek/clk-mt8186-img.c
index 08a625475aee..5e466e1f5f44 100644
--- a/drivers/clk/mediatek/clk-mt8186-img.c
+++ b/drivers/clk/mediatek/clk-mt8186-img.c
@@ -56,6 +56,7 @@ static const struct of_device_id of_match_clk_mt8186_img[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_img);
static struct platform_driver clk_mt8186_img_drv = {
.probe = mtk_clk_simple_probe,
@@ -65,4 +66,7 @@ static struct platform_driver clk_mt8186_img_drv = {
.of_match_table = of_match_clk_mt8186_img,
},
};
-builtin_platform_driver(clk_mt8186_img_drv);
+module_platform_driver(clk_mt8186_img_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-imp_iic_wrap.c b/drivers/clk/mediatek/clk-mt8186-imp_iic_wrap.c
index 47f2e480a05e..75abb871044c 100644
--- a/drivers/clk/mediatek/clk-mt8186-imp_iic_wrap.c
+++ b/drivers/clk/mediatek/clk-mt8186-imp_iic_wrap.c
@@ -55,6 +55,7 @@ static const struct of_device_id of_match_clk_mt8186_imp_iic_wrap[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_imp_iic_wrap);
static struct platform_driver clk_mt8186_imp_iic_wrap_drv = {
.probe = mtk_clk_simple_probe,
@@ -64,4 +65,7 @@ static struct platform_driver clk_mt8186_imp_iic_wrap_drv = {
.of_match_table = of_match_clk_mt8186_imp_iic_wrap,
},
};
-builtin_platform_driver(clk_mt8186_imp_iic_wrap_drv);
+module_platform_driver(clk_mt8186_imp_iic_wrap_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 I2C Wrapper clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-infra_ao.c b/drivers/clk/mediatek/clk-mt8186-infra_ao.c
index df2a6bd1aefa..8d9d86a510ff 100644
--- a/drivers/clk/mediatek/clk-mt8186-infra_ao.c
+++ b/drivers/clk/mediatek/clk-mt8186-infra_ao.c
@@ -227,6 +227,7 @@ static const struct of_device_id of_match_clk_mt8186_infra_ao[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_infra_ao);
static struct platform_driver clk_mt8186_infra_ao_drv = {
.probe = mtk_clk_simple_probe,
@@ -236,4 +237,7 @@ static struct platform_driver clk_mt8186_infra_ao_drv = {
.of_match_table = of_match_clk_mt8186_infra_ao,
},
};
-builtin_platform_driver(clk_mt8186_infra_ao_drv);
+module_platform_driver(clk_mt8186_infra_ao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-ipe.c b/drivers/clk/mediatek/clk-mt8186-ipe.c
index 8fca148effa6..f66a0aeaa6b3 100644
--- a/drivers/clk/mediatek/clk-mt8186-ipe.c
+++ b/drivers/clk/mediatek/clk-mt8186-ipe.c
@@ -43,6 +43,7 @@ static const struct of_device_id of_match_clk_mt8186_ipe[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_ipe);
static struct platform_driver clk_mt8186_ipe_drv = {
.probe = mtk_clk_simple_probe,
@@ -52,4 +53,7 @@ static struct platform_driver clk_mt8186_ipe_drv = {
.of_match_table = of_match_clk_mt8186_ipe,
},
};
-builtin_platform_driver(clk_mt8186_ipe_drv);
+module_platform_driver(clk_mt8186_ipe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 Image Processing Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-mcu.c b/drivers/clk/mediatek/clk-mt8186-mcu.c
index dfc305c1fc5d..d1640e4dc2ad 100644
--- a/drivers/clk/mediatek/clk-mt8186-mcu.c
+++ b/drivers/clk/mediatek/clk-mt8186-mcu.c
@@ -43,66 +43,26 @@ static struct mtk_composite mcu_muxes[] = {
MUX(CLK_MCU_ARMPLL_BUS_SEL, "mcu_armpll_bus_sel", mcu_armpll_bus_parents, 0x2E0, 9, 2),
};
-static const struct of_device_id of_match_clk_mt8186_mcu[] = {
- { .compatible = "mediatek,mt8186-mcusys", },
- {}
+static const struct mtk_clk_desc mcu_desc = {
+ .composite_clks = mcu_muxes,
+ .num_composite_clks = ARRAY_SIZE(mcu_muxes),
};
-static int clk_mt8186_mcu_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
- void __iomem *base;
-
- clk_data = mtk_alloc_clk_data(CLK_MCU_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base)) {
- r = PTR_ERR(base);
- goto free_mcu_data;
- }
-
- r = mtk_clk_register_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), base,
- NULL, clk_data);
- if (r)
- goto free_mcu_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto unregister_composite_muxes;
-
- platform_set_drvdata(pdev, clk_data);
-
- return r;
-
-unregister_composite_muxes:
- mtk_clk_unregister_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), clk_data);
-free_mcu_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
-
-static int clk_mt8186_mcu_remove(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
- struct device_node *node = pdev->dev.of_node;
-
- of_clk_del_provider(node);
- mtk_clk_unregister_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), clk_data);
- mtk_free_clk_data(clk_data);
-
- return 0;
-}
+static const struct of_device_id of_match_clk_mt8186_mcu[] = {
+ { .compatible = "mediatek,mt8186-mcusys", .data = &mcu_desc },
+ { /* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_mcu);
static struct platform_driver clk_mt8186_mcu_drv = {
- .probe = clk_mt8186_mcu_probe,
- .remove = clk_mt8186_mcu_remove,
.driver = {
.name = "clk-mt8186-mcu",
.of_match_table = of_match_clk_mt8186_mcu,
},
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
};
-builtin_platform_driver(clk_mt8186_mcu_drv);
+module_platform_driver(clk_mt8186_mcu_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 mcusys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-mdp.c b/drivers/clk/mediatek/clk-mt8186-mdp.c
index 05174088ef20..01561cf902c4 100644
--- a/drivers/clk/mediatek/clk-mt8186-mdp.c
+++ b/drivers/clk/mediatek/clk-mt8186-mdp.c
@@ -68,6 +68,7 @@ static const struct of_device_id of_match_clk_mt8186_mdp[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_mdp);
static struct platform_driver clk_mt8186_mdp_drv = {
.probe = mtk_clk_simple_probe,
@@ -77,4 +78,7 @@ static struct platform_driver clk_mt8186_mdp_drv = {
.of_match_table = of_match_clk_mt8186_mdp,
},
};
-builtin_platform_driver(clk_mt8186_mdp_drv);
+module_platform_driver(clk_mt8186_mdp_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 Multimedia Data Path clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-mfg.c b/drivers/clk/mediatek/clk-mt8186-mfg.c
index f1f92216f894..3f21b1f222e1 100644
--- a/drivers/clk/mediatek/clk-mt8186-mfg.c
+++ b/drivers/clk/mediatek/clk-mt8186-mfg.c
@@ -16,8 +16,9 @@ static const struct mtk_gate_regs mfg_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_MFG(_id, _name, _parent, _shift) \
- GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_SET_RATE_PARENT)
static const struct mtk_gate mfg_clks[] = {
GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "top_mfg", 0),
@@ -36,6 +37,7 @@ static const struct of_device_id of_match_clk_mt8186_mfg[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_mfg);
static struct platform_driver clk_mt8186_mfg_drv = {
.probe = mtk_clk_simple_probe,
@@ -45,4 +47,7 @@ static struct platform_driver clk_mt8186_mfg_drv = {
.of_match_table = of_match_clk_mt8186_mfg,
},
};
-builtin_platform_driver(clk_mt8186_mfg_drv);
+module_platform_driver(clk_mt8186_mfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-mm.c b/drivers/clk/mediatek/clk-mt8186-mm.c
index 1d33be407947..fc8488c44866 100644
--- a/drivers/clk/mediatek/clk-mt8186-mm.c
+++ b/drivers/clk/mediatek/clk-mt8186-mm.c
@@ -58,54 +58,26 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM1(CLK_MM_DISP_26M, "mm_disp_26m_ck", "top_disp", 10),
};
-static int clk_mt8186_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- r = mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), clk_data);
- if (r)
- goto free_mm_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto unregister_gates;
-
- platform_set_drvdata(pdev, clk_data);
-
- return r;
-
-unregister_gates:
- mtk_clk_unregister_gates(mm_clks, ARRAY_SIZE(mm_clks), clk_data);
-free_mm_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
-
-static int clk_mt8186_mm_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
-
- of_clk_del_provider(node);
- mtk_clk_unregister_gates(mm_clks, ARRAY_SIZE(mm_clks), clk_data);
- mtk_free_clk_data(clk_data);
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return 0;
-}
+static const struct platform_device_id clk_mt8186_mm_id_table[] = {
+ { .name = "clk-mt8186-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8186_mm_id_table);
static struct platform_driver clk_mt8186_mm_drv = {
- .probe = clk_mt8186_mm_probe,
- .remove = clk_mt8186_mm_remove,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8186-mm",
},
+ .id_table = clk_mt8186_mm_id_table,
};
-builtin_platform_driver(clk_mt8186_mm_drv);
+module_platform_driver(clk_mt8186_mm_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-topckgen.c b/drivers/clk/mediatek/clk-mt8186-topckgen.c
index d7f2c4663c85..14f1cbdbbd13 100644
--- a/drivers/clk/mediatek/clk-mt8186-topckgen.c
+++ b/drivers/clk/mediatek/clk-mt8186-topckgen.c
@@ -19,37 +19,37 @@ static const struct mtk_fixed_clk top_fixed_clks[] = {
};
static const struct mtk_fixed_factor top_divs[] = {
- FACTOR(CLK_TOP_MAINPLL_D2, "mainpll_d2", "mainpll", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D2_D2, "mainpll_d2_d2", "mainpll_d2", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D2_D4, "mainpll_d2_d4", "mainpll_d2", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D2_D16, "mainpll_d2_d16", "mainpll_d2", 1, 16),
- FACTOR(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3),
- FACTOR(CLK_TOP_MAINPLL_D3_D2, "mainpll_d3_d2", "mainpll_d3", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D3_D4, "mainpll_d3_d4", "mainpll_d3", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5),
- FACTOR(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7),
- FACTOR(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D2_D2, "univpll_d2_d2", "univpll_d2", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D2_D4, "univpll_d2_d4", "univpll_d2", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
- FACTOR(CLK_TOP_UNIVPLL_D3_D2, "univpll_d3_d2", "univpll_d3", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D3_D4, "univpll_d3_d4", "univpll_d3", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D3_D8, "univpll_d3_d8", "univpll_d3", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D3_D32, "univpll_d3_d32", "univpll_d3", 1, 32),
- FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
- FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
- FACTOR(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univ2pll", 1, 13),
- FACTOR(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16),
- FACTOR(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D2, "mainpll_d2", "mainpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D2_D2, "mainpll_d2_d2", "mainpll_d2", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D2_D4, "mainpll_d2_d4", "mainpll_d2", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D2_D16, "mainpll_d2_d16", "mainpll_d2", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D3_D2, "mainpll_d3_d2", "mainpll_d3", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D3_D4, "mainpll_d3_d4", "mainpll_d3", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2_D2, "univpll_d2_d2", "univpll_d2", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2_D4, "univpll_d2_d4", "univpll_d2", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D2, "univpll_d3_d2", "univpll_d3", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D4, "univpll_d3_d4", "univpll_d3", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D8, "univpll_d3_d8", "univpll_d3", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3_D32, "univpll_d3_d32", "univpll_d3", 1, 32, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univ2pll", 1, 13, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32, 0),
FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1", 1, 2),
FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1, 4),
FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1", 1, 8),
@@ -504,10 +504,10 @@ static const struct mtk_mux top_mtk_muxes[] = {
*/
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI, "top_axi", axi_parents,
0x0040, 0x0044, 0x0048, 0, 2, 7, 0x0004, 0,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SCP, "top_scp", scp_parents,
0x0040, 0x0044, 0x0048, 8, 3, 15, 0x0004, 1,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG, "top_mfg",
mfg_parents, 0x0040, 0x0044, 0x0048, 16, 2, 23, 0x0004, 2),
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG, "top_camtg",
@@ -531,12 +531,12 @@ static const struct mtk_mux top_mtk_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI, "top_spi",
spi_parents, 0x0060, 0x0064, 0x0068, 24, 3, 31, 0x0004, 11),
/* CLK_CFG_3 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_HCLK, "top_msdc5hclk",
- msdc5hclk_parents, 0x0070, 0x0074, 0x0078, 0, 2, 7, 0x0004, 12),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0, "top_msdc50_0",
- msdc50_0_parents, 0x0070, 0x0074, 0x0078, 8, 3, 15, 0x0004, 13),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1, "top_msdc30_1",
- msdc30_1_parents, 0x0070, 0x0074, 0x0078, 16, 3, 23, 0x0004, 14),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_HCLK, "top_msdc5hclk",
+ msdc5hclk_parents, 0x0070, 0x0074, 0x0078, 0, 2, 7, 0x0004, 12, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0, "top_msdc50_0",
+ msdc50_0_parents, 0x0070, 0x0074, 0x0078, 8, 3, 15, 0x0004, 13, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1, "top_msdc30_1",
+ msdc30_1_parents, 0x0070, 0x0074, 0x0078, 16, 3, 23, 0x0004, 14, 0),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO, "top_audio",
audio_parents, 0x0070, 0x0074, 0x0078, 24, 2, 31, 0x0004, 15),
/* CLK_CFG_4 */
@@ -559,7 +559,7 @@ static const struct mtk_mux top_mtk_muxes[] = {
disp_pwm_parents, 0x0090, 0x0094, 0x0098, 8, 3, 15, 0x0004, 21),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SSPM, "top_sspm", sspm_parents,
0x0090, 0x0094, 0x0098, 16, 3, 23, 0x0004, 22,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_DXCC, "top_dxcc",
dxcc_parents, 0x0090, 0x0094, 0x0098, 24, 2, 31, 0x0004, 23),
/*
@@ -570,10 +570,10 @@ static const struct mtk_mux top_mtk_muxes[] = {
usb_parents, 0x00a0, 0x00a4, 0x00a8, 0, 2, 7, 0x0004, 24),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SRCK, "top_srck", srck_parents,
0x00a0, 0x00a4, 0x00a8, 8, 2, 15, 0x0004, 25,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SPM, "top_spm", spm_parents,
0x00a0, 0x00a4, 0x00a8, 16, 2, 23, 0x0004, 26,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C, "top_i2c",
i2c_parents, 0x00a0, 0x00a4, 0x00a8, 24, 2, 31, 0x0004, 27),
/* CLK_CFG_7 */
@@ -627,7 +627,7 @@ static const struct mtk_mux top_mtk_muxes[] = {
*/
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DVFSRC, "top_dvfsrc", dvfsrc_parents,
0x0100, 0x0104, 0x0108, 0, 1, 7, 0x0008, 17,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_DSI_OCC, "top_dsi_occ",
dsi_occ_parents, 0x0100, 0x0104, 0x0108, 8, 2, 15, 0x0008, 18),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SPMI_MST, "top_spmi_mst",
@@ -669,9 +669,6 @@ static struct mtk_composite top_muxes[] = {
MUX(CLK_TOP_APLL_I2S4_MCK_SEL, "apll_i2s4_mck_sel", apll_mck_parents, 0x0320, 19, 1),
MUX(CLK_TOP_APLL_TDMOUT_MCK_SEL, "apll_tdmout_mck_sel", apll_mck_parents,
0x0320, 20, 1),
-};
-
-static const struct mtk_composite top_adj_divs[] = {
DIV_GATE(CLK_TOP_APLL12_CK_DIV0, "apll12_div0", "apll_i2s0_mck_sel",
0x0320, 0, 0x0328, 8, 0),
DIV_GATE(CLK_TOP_APLL12_CK_DIV1, "apll12_div1", "apll_i2s1_mck_sel",
@@ -684,97 +681,57 @@ static const struct mtk_composite top_adj_divs[] = {
0x0320, 4, 0x0334, 8, 0),
};
-static const struct of_device_id of_match_clk_mt8186_topck[] = {
- { .compatible = "mediatek,mt8186-topckgen", },
- {}
-};
-
-static int clk_mt8186_topck_probe(struct platform_device *pdev)
+/* Register mux notifier for MFG mux */
+static int clk_mt8186_reg_mfg_mux_notifier(struct device *dev, struct clk *clk)
{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
- void __iomem *base;
+ struct mtk_mux_nb *mfg_mux_nb;
+ int i;
- clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
- if (!clk_data)
+ mfg_mux_nb = devm_kzalloc(dev, sizeof(*mfg_mux_nb), GFP_KERNEL);
+ if (!mfg_mux_nb)
return -ENOMEM;
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base)) {
- r = PTR_ERR(base);
- goto free_top_data;
- }
-
- r = mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
- clk_data);
- if (r)
- goto free_top_data;
-
- r = mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- if (r)
- goto unregister_fixed_clks;
-
- r = mtk_clk_register_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), node,
- &mt8186_clk_lock, clk_data);
- if (r)
- goto unregister_factors;
-
- r = mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt8186_clk_lock, clk_data);
- if (r)
- goto unregister_muxes;
-
- r = mtk_clk_register_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), base,
- &mt8186_clk_lock, clk_data);
- if (r)
- goto unregister_composite_muxes;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto unregister_composite_divs;
-
- platform_set_drvdata(pdev, clk_data);
-
- return r;
-
-unregister_composite_divs:
- mtk_clk_unregister_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), clk_data);
-unregister_composite_muxes:
- mtk_clk_unregister_composites(top_muxes, ARRAY_SIZE(top_muxes), clk_data);
-unregister_muxes:
- mtk_clk_unregister_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), clk_data);
-unregister_factors:
- mtk_clk_unregister_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
-unregister_fixed_clks:
- mtk_clk_unregister_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), clk_data);
-free_top_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
+ for (i = 0; i < ARRAY_SIZE(top_mtk_muxes); i++)
+ if (top_mtk_muxes[i].id == CLK_TOP_MFG)
+ break;
+ if (i == ARRAY_SIZE(top_mtk_muxes))
+ return -EINVAL;
-static int clk_mt8186_topck_remove(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
- struct device_node *node = pdev->dev.of_node;
-
- of_clk_del_provider(node);
- mtk_clk_unregister_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), clk_data);
- mtk_clk_unregister_composites(top_muxes, ARRAY_SIZE(top_muxes), clk_data);
- mtk_clk_unregister_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), clk_data);
- mtk_clk_unregister_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_unregister_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), clk_data);
- mtk_free_clk_data(clk_data);
-
- return 0;
+ mfg_mux_nb->ops = top_mtk_muxes[i].ops;
+ mfg_mux_nb->bypass_index = 0; /* Bypass to 26M crystal */
+
+ return devm_mtk_clk_mux_notifier_register(dev, clk, mfg_mux_nb);
}
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_mtk_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_mtk_muxes),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .clk_lock = &mt8186_clk_lock,
+ .clk_notifier_func = clk_mt8186_reg_mfg_mux_notifier,
+ .mfg_clk_idx = CLK_TOP_MFG,
+};
+
+static const struct of_device_id of_match_clk_mt8186_topck[] = {
+ { .compatible = "mediatek,mt8186-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_topck);
+
static struct platform_driver clk_mt8186_topck_drv = {
- .probe = clk_mt8186_topck_probe,
- .remove = clk_mt8186_topck_remove,
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8186-topck",
.of_match_table = of_match_clk_mt8186_topck,
},
};
-builtin_platform_driver(clk_mt8186_topck_drv);
+module_platform_driver(clk_mt8186_topck_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 top clock generators driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-vdec.c b/drivers/clk/mediatek/clk-mt8186-vdec.c
index 5ad7e1ae0bac..522b8c952969 100644
--- a/drivers/clk/mediatek/clk-mt8186-vdec.c
+++ b/drivers/clk/mediatek/clk-mt8186-vdec.c
@@ -76,6 +76,7 @@ static const struct of_device_id of_match_clk_mt8186_vdec[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_vdec);
static struct platform_driver clk_mt8186_vdec_drv = {
.probe = mtk_clk_simple_probe,
@@ -85,4 +86,7 @@ static struct platform_driver clk_mt8186_vdec_drv = {
.of_match_table = of_match_clk_mt8186_vdec,
},
};
-builtin_platform_driver(clk_mt8186_vdec_drv);
+module_platform_driver(clk_mt8186_vdec_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-venc.c b/drivers/clk/mediatek/clk-mt8186-venc.c
index f5519f794c45..c0c98bc75112 100644
--- a/drivers/clk/mediatek/clk-mt8186-venc.c
+++ b/drivers/clk/mediatek/clk-mt8186-venc.c
@@ -39,6 +39,7 @@ static const struct of_device_id of_match_clk_mt8186_venc[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_venc);
static struct platform_driver clk_mt8186_venc_drv = {
.probe = mtk_clk_simple_probe,
@@ -48,4 +49,7 @@ static struct platform_driver clk_mt8186_venc_drv = {
.of_match_table = of_match_clk_mt8186_venc,
},
};
-builtin_platform_driver(clk_mt8186_venc_drv);
+module_platform_driver(clk_mt8186_venc_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8186-wpe.c b/drivers/clk/mediatek/clk-mt8186-wpe.c
index 8db3e9178a1e..babd7b2778c2 100644
--- a/drivers/clk/mediatek/clk-mt8186-wpe.c
+++ b/drivers/clk/mediatek/clk-mt8186-wpe.c
@@ -39,6 +39,7 @@ static const struct of_device_id of_match_clk_mt8186_wpe[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8186_wpe);
static struct platform_driver clk_mt8186_wpe_drv = {
.probe = mtk_clk_simple_probe,
@@ -48,4 +49,7 @@ static struct platform_driver clk_mt8186_wpe_drv = {
.of_match_table = of_match_clk_mt8186_wpe,
},
};
-builtin_platform_driver(clk_mt8186_wpe_drv);
+module_platform_driver(clk_mt8186_wpe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 Warp Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-adsp_audio26m.c b/drivers/clk/mediatek/clk-mt8188-adsp_audio26m.c
new file mode 100644
index 000000000000..dcde2187d24a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-adsp_audio26m.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs adsp_audio26m_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x80,
+ .sta_ofs = 0x80,
+};
+
+#define GATE_ADSP_FLAGS(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &adsp_audio26m_cg_regs, _shift, \
+ &mtk_clk_gate_ops_no_setclr)
+
+static const struct mtk_gate adsp_audio26m_clks[] = {
+ GATE_ADSP_FLAGS(CLK_AUDIODSP_AUDIO26M, "audiodsp_audio26m", "clk26m", 3),
+};
+
+static const struct mtk_clk_desc adsp_audio26m_desc = {
+ .clks = adsp_audio26m_clks,
+ .num_clks = ARRAY_SIZE(adsp_audio26m_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_adsp_audio26m[] = {
+ { .compatible = "mediatek,mt8188-adsp-audio26m", .data = &adsp_audio26m_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_adsp_audio26m);
+
+static struct platform_driver clk_mt8188_adsp_audio26m_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-adsp_audio26m",
+ .of_match_table = of_match_clk_mt8188_adsp_audio26m,
+ },
+};
+module_platform_driver(clk_mt8188_adsp_audio26m_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 AudioDSP clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-apmixedsys.c b/drivers/clk/mediatek/clk-mt8188-apmixedsys.c
new file mode 100644
index 000000000000..21d7a9a2ab1a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-apmixedsys.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+static const struct mtk_gate_regs apmixed_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x8,
+};
+
+#define GATE_APMIXED(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &apmixed_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate apmixed_clks[] = {
+ GATE_APMIXED(CLK_APMIXED_PLL_SSUSB26M_EN, "pll_ssusb26m_en", "clk26m", 1),
+};
+
+#define MT8188_PLL_FMAX (3800UL * MHZ)
+#define MT8188_PLL_FMIN (1500UL * MHZ)
+#define MT8188_INTEGER_BITS 8
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
+ _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, _pcw_chg_reg, \
+ _en_reg, _pll_en_bit) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8188_PLL_FMAX, \
+ .fmin = MT8188_PLL_FMIN, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8188_INTEGER_BITS, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcw_chg_reg = _pcw_chg_reg, \
+ .en_reg = _en_reg, \
+ .pll_en_bit = _pll_en_bit, \
+ }
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ETHPLL, "ethpll", 0x044C, 0x0458, 0,
+ 0, 0, 22, 0x0450, 24, 0, 0, 0, 0x0450, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0514, 0x0520, 0,
+ 0, 0, 22, 0x0518, 24, 0, 0, 0, 0x0518, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_TVDPLL1, "tvdpll1", 0x0524, 0x0530, 0,
+ 0, 0, 22, 0x0528, 24, 0, 0, 0, 0x0528, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_TVDPLL2, "tvdpll2", 0x0534, 0x0540, 0,
+ 0, 0, 22, 0x0538, 24, 0, 0, 0, 0x0538, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0544, 0x0550, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x0548, 24, 0, 0, 0, 0x0548, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x045C, 0x0468, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x0460, 24, 0, 0, 0, 0x0460, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_IMGPLL, "imgpll", 0x0554, 0x0560, 0,
+ 0, 0, 22, 0x0558, 24, 0, 0, 0, 0x0558, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0504, 0x0510, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x0508, 24, 0, 0, 0, 0x0508, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_ADSPPLL, "adsppll", 0x042C, 0x0438, 0,
+ 0, 0, 22, 0x0430, 24, 0, 0, 0, 0x0430, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x0304, 0x0314, 0,
+ 0, 0, 32, 0x0308, 24, 0x0034, 0x0000, 12, 0x030C, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x0318, 0x0328, 0,
+ 0, 0, 32, 0x031C, 24, 0x0038, 0x0000, 13, 0x0320, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_APLL3, "apll3", 0x032C, 0x033C, 0,
+ 0, 0, 32, 0x0330, 24, 0x003C, 0x0000, 14, 0x0334, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_APLL4, "apll4", 0x0404, 0x0414, 0,
+ 0, 0, 32, 0x0408, 24, 0x0040, 0x0000, 15, 0x040C, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_APLL5, "apll5", 0x0418, 0x0428, 0,
+ 0, 0, 32, 0x041C, 24, 0x0044, 0x0000, 16, 0x0420, 0, 0, 0, 9),
+ PLL(CLK_APMIXED_MFGPLL, "mfgpll", 0x0340, 0x034C, 0,
+ 0, 0, 22, 0x0344, 24, 0, 0, 0, 0x0344, 0, 0, 0, 9),
+};
+
+static const struct of_device_id of_match_clk_mt8188_apmixed[] = {
+ { .compatible = "mediatek,mt8188-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_apmixed);
+
+static int clk_mt8188_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ int r;
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (r)
+ goto free_apmixed_data;
+
+ r = mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
+ ARRAY_SIZE(apmixed_clks), clk_data);
+ if (r)
+ goto unregister_plls;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_gates;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ return 0;
+
+unregister_gates:
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+free_apmixed_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static void clk_mt8188_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8188_apmixed_drv = {
+ .probe = clk_mt8188_apmixed_probe,
+ .remove = clk_mt8188_apmixed_remove,
+ .driver = {
+ .name = "clk-mt8188-apmixed",
+ .of_match_table = of_match_clk_mt8188_apmixed,
+ },
+};
+module_platform_driver(clk_mt8188_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-cam.c b/drivers/clk/mediatek/clk-mt8188-cam.c
new file mode 100644
index 000000000000..9b029fdd584e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-cam.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs cam_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_CAM(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &cam_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define CAM_SYS_SMI_LARB_RST_OFF (0xA0)
+
+static const struct mtk_gate cam_main_clks[] = {
+ GATE_CAM(CLK_CAM_MAIN_LARB13, "cam_main_larb13", "top_cam", 0),
+ GATE_CAM(CLK_CAM_MAIN_LARB14, "cam_main_larb14", "top_cam", 1),
+ GATE_CAM(CLK_CAM_MAIN_CAM, "cam_main_cam", "top_cam", 2),
+ GATE_CAM(CLK_CAM_MAIN_CAM_SUBA, "cam_main_cam_suba", "top_cam", 3),
+ GATE_CAM(CLK_CAM_MAIN_CAM_SUBB, "cam_main_cam_subb", "top_cam", 4),
+ GATE_CAM(CLK_CAM_MAIN_CAMTG, "cam_main_camtg", "top_cam", 7),
+ GATE_CAM(CLK_CAM_MAIN_SENINF, "cam_main_seninf", "top_cam", 8),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVA, "cam_main_gcamsva", "top_cam", 9),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVB, "cam_main_gcamsvb", "top_cam", 10),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVC, "cam_main_gcamsvc", "top_cam", 11),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVD, "cam_main_gcamsvd", "top_cam", 12),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVE, "cam_main_gcamsve", "top_cam", 13),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVF, "cam_main_gcamsvf", "top_cam", 14),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVG, "cam_main_gcamsvg", "top_cam", 15),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVH, "cam_main_gcamsvh", "top_cam", 16),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVI, "cam_main_gcamsvi", "top_cam", 17),
+ GATE_CAM(CLK_CAM_MAIN_GCAMSVJ, "cam_main_gcamsvj", "top_cam", 18),
+ GATE_CAM(CLK_CAM_MAIN_CAMSV_TOP, "cam_main_camsv", "top_cam", 19),
+ GATE_CAM(CLK_CAM_MAIN_CAMSV_CQ_A, "cam_main_camsv_cq_a", "top_cam", 20),
+ GATE_CAM(CLK_CAM_MAIN_CAMSV_CQ_B, "cam_main_camsv_cq_b", "top_cam", 21),
+ GATE_CAM(CLK_CAM_MAIN_CAMSV_CQ_C, "cam_main_camsv_cq_c", "top_cam", 22),
+ GATE_CAM(CLK_CAM_MAIN_FAKE_ENG, "cam_main_fake_eng", "top_cam", 28),
+ GATE_CAM(CLK_CAM_MAIN_CAM2MM0_GALS, "cam_main_cam2mm0_gals", "top_cam", 29),
+ GATE_CAM(CLK_CAM_MAIN_CAM2MM1_GALS, "cam_main_cam2mm1_gals", "top_cam", 30),
+ GATE_CAM(CLK_CAM_MAIN_CAM2SYS_GALS, "cam_main_cam2sys_gals", "top_cam", 31),
+};
+
+static const struct mtk_gate cam_rawa_clks[] = {
+ GATE_CAM(CLK_CAM_RAWA_LARBX, "cam_rawa_larbx", "top_cam", 0),
+ GATE_CAM(CLK_CAM_RAWA_CAM, "cam_rawa_cam", "top_cam", 1),
+ GATE_CAM(CLK_CAM_RAWA_CAMTG, "cam_rawa_camtg", "top_cam", 2),
+};
+
+static const struct mtk_gate cam_rawb_clks[] = {
+ GATE_CAM(CLK_CAM_RAWB_LARBX, "cam_rawb_larbx", "top_cam", 0),
+ GATE_CAM(CLK_CAM_RAWB_CAM, "cam_rawb_cam", "top_cam", 1),
+ GATE_CAM(CLK_CAM_RAWB_CAMTG, "cam_rawb_camtg", "top_cam", 2),
+};
+
+static const struct mtk_gate cam_yuva_clks[] = {
+ GATE_CAM(CLK_CAM_YUVA_LARBX, "cam_yuva_larbx", "top_cam", 0),
+ GATE_CAM(CLK_CAM_YUVA_CAM, "cam_yuva_cam", "top_cam", 1),
+ GATE_CAM(CLK_CAM_YUVA_CAMTG, "cam_yuva_camtg", "top_cam", 2),
+};
+
+static const struct mtk_gate cam_yuvb_clks[] = {
+ GATE_CAM(CLK_CAM_YUVB_LARBX, "cam_yuvb_larbx", "top_cam", 0),
+ GATE_CAM(CLK_CAM_YUVB_CAM, "cam_yuvb_cam", "top_cam", 1),
+ GATE_CAM(CLK_CAM_YUVB_CAMTG, "cam_yuvb_camtg", "top_cam", 2),
+};
+
+/* Reset for SMI larb 16a/16b/17a/17b */
+static u16 cam_sys_rst_ofs[] = {
+ CAM_SYS_SMI_LARB_RST_OFF,
+};
+
+static const struct mtk_clk_rst_desc cam_sys_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = cam_sys_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(cam_sys_rst_ofs),
+};
+
+static const struct mtk_clk_desc cam_main_desc = {
+ .clks = cam_main_clks,
+ .num_clks = ARRAY_SIZE(cam_main_clks),
+};
+
+static const struct mtk_clk_desc cam_rawa_desc = {
+ .clks = cam_rawa_clks,
+ .num_clks = ARRAY_SIZE(cam_rawa_clks),
+ .rst_desc = &cam_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc cam_rawb_desc = {
+ .clks = cam_rawb_clks,
+ .num_clks = ARRAY_SIZE(cam_rawb_clks),
+ .rst_desc = &cam_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc cam_yuva_desc = {
+ .clks = cam_yuva_clks,
+ .num_clks = ARRAY_SIZE(cam_yuva_clks),
+ .rst_desc = &cam_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc cam_yuvb_desc = {
+ .clks = cam_yuvb_clks,
+ .num_clks = ARRAY_SIZE(cam_yuvb_clks),
+ .rst_desc = &cam_sys_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8188_cam[] = {
+ { .compatible = "mediatek,mt8188-camsys", .data = &cam_main_desc },
+ { .compatible = "mediatek,mt8188-camsys-rawa", .data = &cam_rawa_desc },
+ { .compatible = "mediatek,mt8188-camsys-rawb", .data = &cam_rawb_desc },
+ { .compatible = "mediatek,mt8188-camsys-yuva", .data = &cam_yuva_desc },
+ { .compatible = "mediatek,mt8188-camsys-yuvb", .data = &cam_yuvb_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_cam);
+
+static struct platform_driver clk_mt8188_cam_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-cam",
+ .of_match_table = of_match_clk_mt8188_cam,
+ },
+};
+module_platform_driver(clk_mt8188_cam_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-ccu.c b/drivers/clk/mediatek/clk-mt8188-ccu.c
new file mode 100644
index 000000000000..1566fc437ea3
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-ccu.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs ccu_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_CCU(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ccu_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate ccu_clks[] = {
+ GATE_CCU(CLK_CCU_LARB27, "ccu_larb27", "top_ccu", 0),
+ GATE_CCU(CLK_CCU_AHB, "ccu_ahb", "top_ccu", 1),
+ GATE_CCU(CLK_CCU_CCU0, "ccu_ccu0", "top_ccu", 2),
+};
+
+static const struct mtk_clk_desc ccu_desc = {
+ .clks = ccu_clks,
+ .num_clks = ARRAY_SIZE(ccu_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_ccu[] = {
+ { .compatible = "mediatek,mt8188-ccusys", .data = &ccu_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_ccu);
+
+static struct platform_driver clk_mt8188_ccu_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-ccu",
+ .of_match_table = of_match_clk_mt8188_ccu,
+ },
+};
+module_platform_driver(clk_mt8188_ccu_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Camera Control Unit clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-img.c b/drivers/clk/mediatek/clk-mt8188-img.c
new file mode 100644
index 000000000000..d44bfbd8308a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-img.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs imgsys_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IMGSYS(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &imgsys_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define IMG_SYS_SMI_LARB_RST_OFF (0xC)
+
+static const struct mtk_gate imgsys_main_clks[] = {
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_LARB9, "imgsys_main_larb9", "top_img", 0),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_TRAW0, "imgsys_main_traw0", "top_img", 1),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_TRAW1, "imgsys_main_traw1", "top_img", 2),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_VCORE_GALS, "imgsys_main_vcore_gals", "top_img", 3),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_DIP0, "imgsys_main_dip0", "top_img", 8),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_WPE0, "imgsys_main_wpe0", "top_img", 9),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_IPE, "imgsys_main_ipe", "top_img", 10),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_WPE1, "imgsys_main_wpe1", "top_img", 12),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_WPE2, "imgsys_main_wpe2", "top_img", 13),
+ GATE_IMGSYS(CLK_IMGSYS_MAIN_GALS, "imgsys_main_gals", "top_img", 31),
+};
+
+static const struct mtk_gate imgsys_wpe1_clks[] = {
+ GATE_IMGSYS(CLK_IMGSYS_WPE1_LARB11, "imgsys_wpe1_larb11", "top_img", 0),
+ GATE_IMGSYS(CLK_IMGSYS_WPE1, "imgsys_wpe1", "top_img", 1),
+};
+
+static const struct mtk_gate imgsys_wpe2_clks[] = {
+ GATE_IMGSYS(CLK_IMGSYS_WPE2_LARB11, "imgsys_wpe2_larb11", "top_img", 0),
+ GATE_IMGSYS(CLK_IMGSYS_WPE2, "imgsys_wpe2", "top_img", 1),
+};
+
+static const struct mtk_gate imgsys_wpe3_clks[] = {
+ GATE_IMGSYS(CLK_IMGSYS_WPE3_LARB11, "imgsys_wpe3_larb11", "top_img", 0),
+ GATE_IMGSYS(CLK_IMGSYS_WPE3, "imgsys_wpe3", "top_img", 1),
+};
+
+static const struct mtk_gate imgsys1_dip_top_clks[] = {
+ GATE_IMGSYS(CLK_IMGSYS1_DIP_TOP_LARB10, "imgsys1_dip_larb10", "top_img", 0),
+ GATE_IMGSYS(CLK_IMGSYS1_DIP_TOP_DIP_TOP, "imgsys1_dip_dip_top", "top_img", 1),
+};
+
+static const struct mtk_gate imgsys1_dip_nr_clks[] = {
+ GATE_IMGSYS(CLK_IMGSYS1_DIP_NR_LARB15, "imgsys1_dip_nr_larb15", "top_img", 0),
+ GATE_IMGSYS(CLK_IMGSYS1_DIP_NR_DIP_NR, "imgsys1_dip_nr_dip_nr", "top_img", 1),
+};
+
+/* Reset for SMI larb 10/11a/11b/11c/15 */
+static u16 img_sys_rst_ofs[] = {
+ IMG_SYS_SMI_LARB_RST_OFF,
+};
+
+static const struct mtk_clk_rst_desc img_sys_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = img_sys_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(img_sys_rst_ofs),
+};
+
+static const struct mtk_clk_desc imgsys_main_desc = {
+ .clks = imgsys_main_clks,
+ .num_clks = ARRAY_SIZE(imgsys_main_clks),
+};
+
+static const struct mtk_clk_desc imgsys_wpe1_desc = {
+ .clks = imgsys_wpe1_clks,
+ .num_clks = ARRAY_SIZE(imgsys_wpe1_clks),
+ .rst_desc = &img_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc imgsys_wpe2_desc = {
+ .clks = imgsys_wpe2_clks,
+ .num_clks = ARRAY_SIZE(imgsys_wpe2_clks),
+ .rst_desc = &img_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc imgsys_wpe3_desc = {
+ .clks = imgsys_wpe3_clks,
+ .num_clks = ARRAY_SIZE(imgsys_wpe3_clks),
+ .rst_desc = &img_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc imgsys1_dip_top_desc = {
+ .clks = imgsys1_dip_top_clks,
+ .num_clks = ARRAY_SIZE(imgsys1_dip_top_clks),
+ .rst_desc = &img_sys_rst_desc,
+};
+
+static const struct mtk_clk_desc imgsys1_dip_nr_desc = {
+ .clks = imgsys1_dip_nr_clks,
+ .num_clks = ARRAY_SIZE(imgsys1_dip_nr_clks),
+ .rst_desc = &img_sys_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8188_imgsys_main[] = {
+ { .compatible = "mediatek,mt8188-imgsys", .data = &imgsys_main_desc },
+ { .compatible = "mediatek,mt8188-imgsys-wpe1", .data = &imgsys_wpe1_desc },
+ { .compatible = "mediatek,mt8188-imgsys-wpe2", .data = &imgsys_wpe2_desc },
+ { .compatible = "mediatek,mt8188-imgsys-wpe3", .data = &imgsys_wpe3_desc },
+ { .compatible = "mediatek,mt8188-imgsys1-dip-top", .data = &imgsys1_dip_top_desc },
+ { .compatible = "mediatek,mt8188-imgsys1-dip-nr", .data = &imgsys1_dip_nr_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_imgsys_main);
+
+static struct platform_driver clk_mt8188_imgsys_main_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-imgsys_main",
+ .of_match_table = of_match_clk_mt8188_imgsys_main,
+ },
+};
+module_platform_driver(clk_mt8188_imgsys_main_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-imp_iic_wrap.c b/drivers/clk/mediatek/clk-mt8188-imp_iic_wrap.c
new file mode 100644
index 000000000000..14a4b575b583
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-imp_iic_wrap.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs imp_iic_wrap_cg_regs = {
+ .set_ofs = 0xe08,
+ .clr_ofs = 0xe04,
+ .sta_ofs = 0xe00,
+};
+
+#define GATE_IMP_IIC_WRAP(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &imp_iic_wrap_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_OPS_PARENT_ENABLE)
+
+static const struct mtk_gate imp_iic_wrap_c_clks[] = {
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_C_AP_CLOCK_I2C0,
+ "imp_iic_wrap_c_ap_clock_i2c0", "top_i2c", 0),
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_C_AP_CLOCK_I2C2,
+ "imp_iic_wrap_c_ap_clock_i2c2", "top_i2c", 1),
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_C_AP_CLOCK_I2C3,
+ "imp_iic_wrap_c_ap_clock_i2c3", "top_i2c", 2),
+};
+
+static const struct mtk_gate imp_iic_wrap_w_clks[] = {
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_W_AP_CLOCK_I2C1,
+ "imp_iic_wrap_w_ap_clock_i2c1", "top_i2c", 0),
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_W_AP_CLOCK_I2C4,
+ "imp_iic_wrap_w_ap_clock_i2c4", "top_i2c", 1),
+};
+
+static const struct mtk_gate imp_iic_wrap_en_clks[] = {
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_EN_AP_CLOCK_I2C5,
+ "imp_iic_wrap_en_ap_clock_i2c5", "top_i2c", 0),
+ GATE_IMP_IIC_WRAP(CLK_IMP_IIC_WRAP_EN_AP_CLOCK_I2C6,
+ "imp_iic_wrap_en_ap_clock_i2c6", "top_i2c", 1),
+};
+
+static const struct mtk_clk_desc imp_iic_wrap_c_desc = {
+ .clks = imp_iic_wrap_c_clks,
+ .num_clks = ARRAY_SIZE(imp_iic_wrap_c_clks),
+};
+
+static const struct mtk_clk_desc imp_iic_wrap_w_desc = {
+ .clks = imp_iic_wrap_w_clks,
+ .num_clks = ARRAY_SIZE(imp_iic_wrap_w_clks),
+};
+
+static const struct mtk_clk_desc imp_iic_wrap_en_desc = {
+ .clks = imp_iic_wrap_en_clks,
+ .num_clks = ARRAY_SIZE(imp_iic_wrap_en_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_imp_iic_wrap[] = {
+ { .compatible = "mediatek,mt8188-imp-iic-wrap-c", .data = &imp_iic_wrap_c_desc },
+ { .compatible = "mediatek,mt8188-imp-iic-wrap-w", .data = &imp_iic_wrap_w_desc },
+ { .compatible = "mediatek,mt8188-imp-iic-wrap-en", .data = &imp_iic_wrap_en_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_imp_iic_wrap);
+
+static struct platform_driver clk_mt8188_imp_iic_wrap_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-imp_iic_wrap",
+ .of_match_table = of_match_clk_mt8188_imp_iic_wrap,
+ },
+};
+
+module_platform_driver(clk_mt8188_imp_iic_wrap_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 I2C Wrapper clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-infra_ao.c b/drivers/clk/mediatek/clk-mt8188-infra_ao.c
new file mode 100644
index 000000000000..b9bc8fcc2ade
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-infra_ao.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <dt-bindings/reset/mt8188-resets.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs infra_ao0_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x84,
+ .sta_ofs = 0x90,
+};
+
+static const struct mtk_gate_regs infra_ao1_cg_regs = {
+ .set_ofs = 0x88,
+ .clr_ofs = 0x8c,
+ .sta_ofs = 0x94,
+};
+
+static const struct mtk_gate_regs infra_ao2_cg_regs = {
+ .set_ofs = 0xa4,
+ .clr_ofs = 0xa8,
+ .sta_ofs = 0xac,
+};
+
+static const struct mtk_gate_regs infra_ao3_cg_regs = {
+ .set_ofs = 0xc0,
+ .clr_ofs = 0xc4,
+ .sta_ofs = 0xc8,
+};
+
+static const struct mtk_gate_regs infra_ao4_cg_regs = {
+ .set_ofs = 0xe0,
+ .clr_ofs = 0xe4,
+ .sta_ofs = 0xe8,
+};
+
+#define GATE_INFRA_AO0_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao0_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flag)
+
+#define GATE_INFRA_AO0(_id, _name, _parent, _shift) \
+ GATE_INFRA_AO0_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_INFRA_AO1_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao1_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flag)
+
+#define GATE_INFRA_AO1(_id, _name, _parent, _shift) \
+ GATE_INFRA_AO1_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_INFRA_AO2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &infra_ao2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_INFRA_AO2_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao2_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flag)
+
+#define GATE_INFRA_AO3_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao3_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flag)
+
+#define GATE_INFRA_AO3(_id, _name, _parent, _shift) \
+ GATE_INFRA_AO3_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_INFRA_AO4_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao4_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flag)
+
+#define GATE_INFRA_AO4(_id, _name, _parent, _shift) \
+ GATE_INFRA_AO4_FLAGS(_id, _name, _parent, _shift, 0)
+
+static const struct mtk_gate infra_ao_clks[] = {
+ /* INFRA_AO0 */
+ GATE_INFRA_AO0(CLK_INFRA_AO_PMIC_TMR, "infra_ao_pmic_tmr", "top_pwrap_ulposc", 0),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PMIC_AP, "infra_ao_pmic_ap", "top_pwrap_ulposc", 1),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PMIC_MD, "infra_ao_pmic_md", "top_pwrap_ulposc", 2),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PMIC_CONN, "infra_ao_pmic_conn", "top_pwrap_ulposc", 3),
+ /* infra_ao_sej is main clock is for secure engine with JTAG support */
+ GATE_INFRA_AO0_FLAGS(CLK_INFRA_AO_SEJ, "infra_ao_sej", "top_axi", 5, CLK_IS_CRITICAL),
+ GATE_INFRA_AO0(CLK_INFRA_AO_APXGPT, "infra_ao_apxgpt", "top_axi", 6),
+ GATE_INFRA_AO0(CLK_INFRA_AO_GCE, "infra_ao_gce", "top_axi", 8),
+ GATE_INFRA_AO0(CLK_INFRA_AO_GCE2, "infra_ao_gce2", "top_axi", 9),
+ GATE_INFRA_AO0(CLK_INFRA_AO_THERM, "infra_ao_therm", "top_axi", 10),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PWM_HCLK, "infra_ao_pwm_h", "top_axi", 15),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PWM1, "infra_ao_pwm1", "top_pwm", 16),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PWM2, "infra_ao_pwm2", "top_pwm", 17),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PWM3, "infra_ao_pwm3", "top_pwm", 18),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PWM4, "infra_ao_pwm4", "top_pwm", 19),
+ GATE_INFRA_AO0(CLK_INFRA_AO_PWM, "infra_ao_pwm", "top_pwm", 21),
+ GATE_INFRA_AO0(CLK_INFRA_AO_UART0, "infra_ao_uart0", "top_uart", 22),
+ GATE_INFRA_AO0(CLK_INFRA_AO_UART1, "infra_ao_uart1", "top_uart", 23),
+ GATE_INFRA_AO0(CLK_INFRA_AO_UART2, "infra_ao_uart2", "top_uart", 24),
+ GATE_INFRA_AO0(CLK_INFRA_AO_UART3, "infra_ao_uart3", "top_uart", 25),
+ GATE_INFRA_AO0(CLK_INFRA_AO_UART4, "infra_ao_uart4", "top_uart", 26),
+ GATE_INFRA_AO0(CLK_INFRA_AO_GCE_26M, "infra_ao_gce_26m", "clk26m", 27),
+ GATE_INFRA_AO0(CLK_INFRA_AO_CQ_DMA_FPC, "infra_ao_dma", "pad_fpc_ck", 28),
+ GATE_INFRA_AO0(CLK_INFRA_AO_UART5, "infra_ao_uart5", "top_uart", 29),
+ /* INFRA_AO1 */
+ GATE_INFRA_AO1(CLK_INFRA_AO_HDMI_26M, "infra_ao_hdmi_26m", "clk26m", 0),
+ GATE_INFRA_AO1(CLK_INFRA_AO_SPI0, "infra_ao_spi0", "top_spi", 1),
+ GATE_INFRA_AO1(CLK_INFRA_AO_MSDC0, "infra_ao_msdc0", "top_msdc5hclk", 2),
+ GATE_INFRA_AO1(CLK_INFRA_AO_MSDC1, "infra_ao_msdc1", "top_axi", 4),
+ GATE_INFRA_AO1(CLK_INFRA_AO_MSDC2, "infra_ao_msdc2", "top_axi", 5),
+ GATE_INFRA_AO1(CLK_INFRA_AO_MSDC0_SRC, "infra_ao_msdc0_clk", "top_msdc50_0", 6),
+ /* infra_ao_dvfsrc is for internal DVFS usage, should not be handled by Linux. */
+ GATE_INFRA_AO1_FLAGS(CLK_INFRA_AO_DVFSRC, "infra_ao_dvfsrc",
+ "clk26m", 7, CLK_IS_CRITICAL),
+ GATE_INFRA_AO1(CLK_INFRA_AO_TRNG, "infra_ao_trng", "top_axi", 9),
+ GATE_INFRA_AO1(CLK_INFRA_AO_AUXADC, "infra_ao_auxadc", "clk26m", 10),
+ GATE_INFRA_AO1(CLK_INFRA_AO_CPUM, "infra_ao_cpum", "top_axi", 11),
+ GATE_INFRA_AO1(CLK_INFRA_AO_HDMI_32K, "infra_ao_hdmi_32k", "clk32k", 12),
+ GATE_INFRA_AO1(CLK_INFRA_AO_CEC_66M_HCLK, "infra_ao_cec_66m_hclk", "top_axi", 13),
+ GATE_INFRA_AO1(CLK_INFRA_AO_PCIE_TL_26M, "infra_ao_pcie_tl_26m", "clk26m", 15),
+ GATE_INFRA_AO1(CLK_INFRA_AO_MSDC1_SRC, "infra_ao_msdc1_clk", "top_msdc30_1", 16),
+ GATE_INFRA_AO1(CLK_INFRA_AO_CEC_66M_BCLK, "infra_ao_cec_66m_bclk", "top_axi", 17),
+ GATE_INFRA_AO1(CLK_INFRA_AO_PCIE_TL_96M, "infra_ao_pcie_tl_96m", "top_tl", 18),
+ /* infra_ao_dapc is for device access permission control module */
+ GATE_INFRA_AO1_FLAGS(CLK_INFRA_AO_DEVICE_APC, "infra_ao_dapc",
+ "top_axi", 20, CLK_IS_CRITICAL),
+ GATE_INFRA_AO1(CLK_INFRA_AO_ECC_66M_HCLK, "infra_ao_ecc_66m_hclk", "top_axi", 23),
+ GATE_INFRA_AO1(CLK_INFRA_AO_DEBUGSYS, "infra_ao_debugsys", "top_axi", 24),
+ GATE_INFRA_AO1(CLK_INFRA_AO_AUDIO, "infra_ao_audio", "top_axi", 25),
+ GATE_INFRA_AO1(CLK_INFRA_AO_PCIE_TL_32K, "infra_ao_pcie_tl_32k", "clk32k", 26),
+ GATE_INFRA_AO1(CLK_INFRA_AO_DBG_TRACE, "infra_ao_dbg_trace", "top_axi", 29),
+ GATE_INFRA_AO1(CLK_INFRA_AO_DRAMC_F26M, "infra_ao_dramc26", "clk26m", 31),
+ /* INFRA_AO2 */
+ GATE_INFRA_AO2(CLK_INFRA_AO_IRTX, "infra_ao_irtx", "top_axi", 0),
+ GATE_INFRA_AO2(CLK_INFRA_AO_DISP_PWM, "infra_ao_disp_pwm", "top_disp_pwm0", 2),
+ GATE_INFRA_AO2(CLK_INFRA_AO_CLDMA_BCLK, "infra_ao_cldmabclk", "top_axi", 3),
+ GATE_INFRA_AO2(CLK_INFRA_AO_AUDIO_26M_BCLK, "infra_ao_audio26m", "clk26m", 4),
+ GATE_INFRA_AO2(CLK_INFRA_AO_SPI1, "infra_ao_spi1", "top_spi", 6),
+ GATE_INFRA_AO2(CLK_INFRA_AO_SPI2, "infra_ao_spi2", "top_spi", 9),
+ GATE_INFRA_AO2(CLK_INFRA_AO_SPI3, "infra_ao_spi3", "top_spi", 10),
+ GATE_INFRA_AO2_FLAGS(CLK_INFRA_AO_FSSPM, "infra_ao_fsspm",
+ "top_sspm", 15, CLK_IS_CRITICAL),
+ GATE_INFRA_AO2_FLAGS(CLK_INFRA_AO_SSPM_BUS_HCLK, "infra_ao_sspm_hclk",
+ "top_axi", 17, CLK_IS_CRITICAL),
+ GATE_INFRA_AO2(CLK_INFRA_AO_APDMA_BCLK, "infra_ao_apdma_bclk", "top_axi", 18),
+ GATE_INFRA_AO2(CLK_INFRA_AO_SPI4, "infra_ao_spi4", "top_spi", 25),
+ GATE_INFRA_AO2(CLK_INFRA_AO_SPI5, "infra_ao_spi5", "top_spi", 26),
+ GATE_INFRA_AO2(CLK_INFRA_AO_CQ_DMA, "infra_ao_cq_dma", "top_axi", 27),
+ /* INFRA_AO3 */
+ GATE_INFRA_AO3(CLK_INFRA_AO_MSDC0_SELF, "infra_ao_msdc0sf", "top_msdc50_0", 0),
+ GATE_INFRA_AO3(CLK_INFRA_AO_MSDC1_SELF, "infra_ao_msdc1sf", "top_msdc50_0", 1),
+ GATE_INFRA_AO3(CLK_INFRA_AO_MSDC2_SELF, "infra_ao_msdc2sf", "top_msdc50_0", 2),
+ GATE_INFRA_AO3(CLK_INFRA_AO_I2S_DMA, "infra_ao_i2s_dma", "top_axi", 5),
+ GATE_INFRA_AO3(CLK_INFRA_AO_AP_MSDC0, "infra_ao_ap_msdc0", "top_msdc50_0", 7),
+ GATE_INFRA_AO3(CLK_INFRA_AO_MD_MSDC0, "infra_ao_md_msdc0", "top_msdc50_0", 8),
+ GATE_INFRA_AO3(CLK_INFRA_AO_MSDC30_2, "infra_ao_msdc30_2", "top_msdc30_2", 9),
+ GATE_INFRA_AO3(CLK_INFRA_AO_GCPU, "infra_ao_gcpu", "top_gcpu", 10),
+ GATE_INFRA_AO3(CLK_INFRA_AO_PCIE_PERI_26M, "infra_ao_pcie_peri_26m", "clk26m", 15),
+ GATE_INFRA_AO3(CLK_INFRA_AO_GCPU_66M_BCLK, "infra_ao_gcpu_66m_bclk", "top_axi", 16),
+ GATE_INFRA_AO3(CLK_INFRA_AO_GCPU_133M_BCLK, "infra_ao_gcpu_133m_bclk", "top_axi", 17),
+ GATE_INFRA_AO3(CLK_INFRA_AO_DISP_PWM1, "infra_ao_disp_pwm1", "top_disp_pwm1", 20),
+ GATE_INFRA_AO3(CLK_INFRA_AO_FBIST2FPC, "infra_ao_fbist2fpc", "top_msdc50_0", 24),
+ /* infra_ao_dapc_sync is for device access permission control module */
+ GATE_INFRA_AO3_FLAGS(CLK_INFRA_AO_DEVICE_APC_SYNC, "infra_ao_dapc_sync",
+ "top_axi", 25, CLK_IS_CRITICAL),
+ GATE_INFRA_AO3(CLK_INFRA_AO_PCIE_P1_PERI_26M, "infra_ao_pcie_p1_peri_26m", "clk26m", 26),
+ /* INFRA_AO4 */
+ /* infra_ao_133m_mclk_set/infra_ao_66m_mclk_set are main clocks of peripheral */
+ GATE_INFRA_AO4_FLAGS(CLK_INFRA_AO_133M_MCLK_CK, "infra_ao_133m_mclk_set",
+ "top_axi", 0, CLK_IS_CRITICAL),
+ GATE_INFRA_AO4_FLAGS(CLK_INFRA_AO_66M_MCLK_CK, "infra_ao_66m_mclk_set",
+ "top_axi", 1, CLK_IS_CRITICAL),
+ GATE_INFRA_AO4(CLK_INFRA_AO_PCIE_PL_P_250M_P0, "infra_ao_pcie_pl_p_250m_p0",
+ "pextp_pipe", 7),
+ GATE_INFRA_AO4(CLK_INFRA_AO_RG_AES_MSDCFDE_CK_0P,
+ "infra_ao_aes_msdcfde_0p", "top_aes_msdcfde", 18),
+};
+
+static u16 infra_ao_rst_ofs[] = {
+ INFRA_RST0_SET_OFFSET,
+ INFRA_RST1_SET_OFFSET,
+ INFRA_RST2_SET_OFFSET,
+ INFRA_RST3_SET_OFFSET,
+ INFRA_RST4_SET_OFFSET,
+};
+
+static u16 infra_ao_idx_map[] = {
+ [MT8188_INFRA_RST1_THERMAL_MCU_RST] = 1 * RST_NR_PER_BANK + 2,
+ [MT8188_INFRA_RST1_THERMAL_CTRL_RST] = 1 * RST_NR_PER_BANK + 4,
+ [MT8188_INFRA_RST3_PTP_CTRL_RST] = 3 * RST_NR_PER_BANK + 5,
+};
+
+static const struct mtk_clk_rst_desc infra_ao_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = infra_ao_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(infra_ao_rst_ofs),
+ .rst_idx_map = infra_ao_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(infra_ao_idx_map),
+};
+
+static const struct mtk_clk_desc infra_ao_desc = {
+ .clks = infra_ao_clks,
+ .num_clks = ARRAY_SIZE(infra_ao_clks),
+ .rst_desc = &infra_ao_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8188_infra_ao[] = {
+ { .compatible = "mediatek,mt8188-infracfg-ao", .data = &infra_ao_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_infra_ao);
+
+static struct platform_driver clk_mt8188_infra_ao_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-infra_ao",
+ .of_match_table = of_match_clk_mt8188_infra_ao,
+ },
+};
+module_platform_driver(clk_mt8188_infra_ao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-ipe.c b/drivers/clk/mediatek/clk-mt8188-ipe.c
new file mode 100644
index 000000000000..70a011c1f9ce
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-ipe.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs ipe_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IPE(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &ipe_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define IPE_SYS_SMI_LARB_RST_OFF (0xC)
+
+static const struct mtk_gate ipe_clks[] = {
+ GATE_IPE(CLK_IPE_DPE, "ipe_dpe", "top_ipe", 0),
+ GATE_IPE(CLK_IPE_FDVT, "ipe_fdvt", "top_ipe", 1),
+ GATE_IPE(CLK_IPE_ME, "ipe_me", "top_ipe", 2),
+ GATE_IPE(CLK_IPESYS_TOP, "ipesys_top", "top_ipe", 3),
+ GATE_IPE(CLK_IPE_SMI_LARB12, "ipe_smi_larb12", "top_ipe", 4),
+};
+
+/* Reset for SMI larb 12 */
+static u16 ipe_sys_rst_ofs[] = {
+ IPE_SYS_SMI_LARB_RST_OFF,
+};
+
+static const struct mtk_clk_rst_desc ipe_sys_rst_desc = {
+ .version = MTK_RST_SIMPLE,
+ .rst_bank_ofs = ipe_sys_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(ipe_sys_rst_ofs),
+};
+
+static const struct mtk_clk_desc ipe_desc = {
+ .clks = ipe_clks,
+ .num_clks = ARRAY_SIZE(ipe_clks),
+ .rst_desc = &ipe_sys_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8188_ipe[] = {
+ { .compatible = "mediatek,mt8188-ipesys", .data = &ipe_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_ipe);
+
+static struct platform_driver clk_mt8188_ipe_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-ipe",
+ .of_match_table = of_match_clk_mt8188_ipe,
+ },
+};
+
+module_platform_driver(clk_mt8188_ipe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Image Processing Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-mfg.c b/drivers/clk/mediatek/clk-mt8188-mfg.c
new file mode 100644
index 000000000000..2ddfb1a3de47
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-mfg.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mfgcfg_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &mfgcfg_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, CLK_SET_RATE_PARENT)
+
+static const struct mtk_gate mfgcfg_clks[] = {
+ GATE_MFG(CLK_MFGCFG_BG3D, "mfgcfg_bg3d", "mfg_ck_fast_ref", 0),
+};
+
+static const struct mtk_clk_desc mfgcfg_desc = {
+ .clks = mfgcfg_clks,
+ .num_clks = ARRAY_SIZE(mfgcfg_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_mfgcfg[] = {
+ { .compatible = "mediatek,mt8188-mfgcfg", .data = &mfgcfg_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_mfgcfg);
+
+static struct platform_driver clk_mt8188_mfgcfg_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-mfgcfg",
+ .of_match_table = of_match_clk_mt8188_mfgcfg,
+ },
+};
+
+module_platform_driver(clk_mt8188_mfgcfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8186 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-peri_ao.c b/drivers/clk/mediatek/clk-mt8188-peri_ao.c
new file mode 100644
index 000000000000..639865335fc8
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-peri_ao.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs peri_ao_cg_regs = {
+ .set_ofs = 0x10,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x18,
+};
+
+#define GATE_PERI_AO(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &peri_ao_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate peri_ao_clks[] = {
+ GATE_PERI_AO(CLK_PERI_AO_ETHERNET, "peri_ao_ethernet", "top_axi", 0),
+ GATE_PERI_AO(CLK_PERI_AO_ETHERNET_BUS, "peri_ao_ethernet_bus", "top_axi", 1),
+ GATE_PERI_AO(CLK_PERI_AO_FLASHIF_BUS, "peri_ao_flashif_bus", "top_axi", 3),
+ GATE_PERI_AO(CLK_PERI_AO_FLASHIF_26M, "peri_ao_flashif_26m", "clk26m", 4),
+ GATE_PERI_AO(CLK_PERI_AO_FLASHIFLASHCK, "peri_ao_flashiflashck", "top_spinor", 5),
+ GATE_PERI_AO(CLK_PERI_AO_SSUSB_2P_BUS, "peri_ao_ssusb_2p_bus", "top_usb_top_2p", 9),
+ GATE_PERI_AO(CLK_PERI_AO_SSUSB_2P_XHCI, "peri_ao_ssusb_2p_xhci", "top_ssusb_xhci_2p", 10),
+ GATE_PERI_AO(CLK_PERI_AO_SSUSB_3P_BUS, "peri_ao_ssusb_3p_bus", "top_usb_top_3p", 11),
+ GATE_PERI_AO(CLK_PERI_AO_SSUSB_3P_XHCI, "peri_ao_ssusb_3p_xhci", "top_ssusb_xhci_3p", 12),
+ GATE_PERI_AO(CLK_PERI_AO_SSUSB_BUS, "peri_ao_ssusb_bus", "top_usb_top", 13),
+ GATE_PERI_AO(CLK_PERI_AO_SSUSB_XHCI, "peri_ao_ssusb_xhci", "top_ssusb_xhci", 14),
+ GATE_PERI_AO(CLK_PERI_AO_ETHERNET_MAC, "peri_ao_ethernet_mac_clk", "top_snps_eth_250m", 16),
+ GATE_PERI_AO(CLK_PERI_AO_PCIE_P0_FMEM, "peri_ao_pcie_p0_fmem", "hd_466m_fmem_ck", 24),
+};
+
+static const struct mtk_clk_desc peri_ao_desc = {
+ .clks = peri_ao_clks,
+ .num_clks = ARRAY_SIZE(peri_ao_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_peri_ao[] = {
+ { .compatible = "mediatek,mt8188-pericfg-ao", .data = &peri_ao_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_peri_ao);
+
+static struct platform_driver clk_mt8188_peri_ao_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-peri_ao",
+ .of_match_table = of_match_clk_mt8188_peri_ao,
+ },
+};
+module_platform_driver(clk_mt8188_peri_ao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 pericfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-topckgen.c b/drivers/clk/mediatek/clk-mt8188-topckgen.c
new file mode 100644
index 000000000000..6b07abe9a8f5
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-topckgen.c
@@ -0,0 +1,1362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+static DEFINE_SPINLOCK(mt8188_clk_lock);
+
+static const struct mtk_fixed_clk top_fixed_clks[] = {
+ FIXED_CLK(CLK_TOP_ULPOSC1, "ulposc_ck1", NULL, 260000000),
+ FIXED_CLK(CLK_TOP_MPHONE_SLAVE_BCK, "mphone_slave_bck", NULL, 49152000),
+ FIXED_CLK(CLK_TOP_PAD_FPC, "pad_fpc_ck", NULL, 50000000),
+ FIXED_CLK(CLK_TOP_466M_FMEM, "hd_466m_fmem_ck", NULL, 533000000),
+ FIXED_CLK(CLK_TOP_PEXTP_PIPE, "pextp_pipe", NULL, 250000000),
+ FIXED_CLK(CLK_TOP_DSI_PHY, "dsi_phy", NULL, 500000000),
+};
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3),
+ FACTOR(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll_d4", 1, 2),
+ FACTOR(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll_d4", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll_d4", 1, 8),
+ FACTOR(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5),
+ FACTOR(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2),
+ FACTOR(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll_d5", 1, 8),
+ FACTOR(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6),
+ FACTOR(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll_d6", 1, 2),
+ FACTOR(CLK_TOP_MAINPLL_D6_D4, "mainpll_d6_d4", "mainpll_d6", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D6_D8, "mainpll_d6_d8", "mainpll_d6", 1, 8),
+ FACTOR(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7),
+ FACTOR(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2),
+ FACTOR(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll_d7", 1, 8),
+ FACTOR(CLK_TOP_MAINPLL_D9, "mainpll_d9", "mainpll", 1, 9),
+ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
+ FACTOR(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll_d4", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll_d4", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll_d4", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll_d6", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll_d6", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll_d6", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
+ FACTOR(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D10, "univpll_192m_d10", "univpll_192m", 1, 10),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32),
+ FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1", 1, 3),
+ FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1, 4),
+ FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2", 1, 3),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4),
+ FACTOR(CLK_TOP_APLL3_D4, "apll3_d4", "apll3", 1, 4),
+ FACTOR(CLK_TOP_APLL4_D4, "apll4_d4", "apll4", 1, 4),
+ FACTOR(CLK_TOP_APLL5_D4, "apll5_d4", "apll5", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D4_D2, "mmpll_d4_d2", "mmpll_d4", 1, 2),
+ FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5),
+ FACTOR(CLK_TOP_MMPLL_D5_D2, "mmpll_d5_d2", "mmpll_d5", 1, 2),
+ FACTOR(CLK_TOP_MMPLL_D5_D4, "mmpll_d5_d4", "mmpll_d5", 1, 4),
+ FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll", 1, 6),
+ FACTOR(CLK_TOP_MMPLL_D6_D2, "mmpll_d6_d2", "mmpll_d6", 1, 2),
+ FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7),
+ FACTOR(CLK_TOP_MMPLL_D9, "mmpll_d9", "mmpll", 1, 9),
+ FACTOR(CLK_TOP_TVDPLL1_D2, "tvdpll1_d2", "tvdpll1", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL1_D4, "tvdpll1_d4", "tvdpll1", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL1_D8, "tvdpll1_d8", "tvdpll1", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL1_D16, "tvdpll1_d16", "tvdpll1", 1, 16),
+ FACTOR(CLK_TOP_TVDPLL2_D2, "tvdpll2_d2", "tvdpll2", 1, 2),
+ FACTOR(CLK_TOP_TVDPLL2_D4, "tvdpll2_d4", "tvdpll2", 1, 4),
+ FACTOR(CLK_TOP_TVDPLL2_D8, "tvdpll2_d8", "tvdpll2", 1, 8),
+ FACTOR(CLK_TOP_TVDPLL2_D16, "tvdpll2_d16", "tvdpll2", 1, 16),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D16, "msdcpll_d16", "msdcpll", 1, 16),
+ FACTOR(CLK_TOP_ETHPLL_D2, "ethpll_d2", "ethpll", 1, 2),
+ FACTOR(CLK_TOP_ETHPLL_D4, "ethpll_d4", "ethpll", 1, 4),
+ FACTOR(CLK_TOP_ETHPLL_D8, "ethpll_d8", "ethpll", 1, 8),
+ FACTOR(CLK_TOP_ETHPLL_D10, "ethpll_d10", "ethpll", 1, 10),
+ FACTOR(CLK_TOP_ADSPPLL_D2, "adsppll_d2", "adsppll", 1, 2),
+ FACTOR(CLK_TOP_ADSPPLL_D4, "adsppll_d4", "adsppll", 1, 4),
+ FACTOR(CLK_TOP_ADSPPLL_D8, "adsppll_d8", "adsppll", 1, 8),
+ FACTOR(CLK_TOP_ULPOSC1_D2, "ulposc1_d2", "ulposc_ck1", 1, 2),
+ FACTOR(CLK_TOP_ULPOSC1_D4, "ulposc1_d4", "ulposc_ck1", 1, 4),
+ FACTOR(CLK_TOP_ULPOSC1_D8, "ulposc1_d8", "ulposc_ck1", 1, 8),
+ FACTOR(CLK_TOP_ULPOSC1_D7, "ulposc1_d7", "ulposc_ck1", 1, 7),
+ FACTOR(CLK_TOP_ULPOSC1_D10, "ulposc1_d10", "ulposc_ck1", 1, 10),
+ FACTOR(CLK_TOP_ULPOSC1_D16, "ulposc1_d16", "ulposc_ck1", 1, 16),
+};
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2",
+ "mainpll_d4_d2",
+ "mainpll_d5_d2",
+ "mainpll_d6_d2",
+ "ulposc1_d4"
+};
+
+static const char * const spm_parents[] = {
+ "clk26m",
+ "ulposc1_d10",
+ "mainpll_d7_d4",
+ "clk32k"
+};
+
+static const char * const scp_parents[] = {
+ "clk26m",
+ "univpll_d4",
+ "mainpll_d6",
+ "univpll_d6",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "univpll_d3",
+ "mainpll_d3"
+};
+
+static const char * const bus_aximem_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2",
+ "mainpll_d4_d2",
+ "mainpll_d5_d2",
+ "mainpll_d6"
+};
+
+static const char * const vpp_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d5_d2",
+ "mmpll_d6_d2",
+ "univpll_d5_d2",
+ "univpll_d4_d2",
+ "mmpll_d4_d2",
+ "mmpll_d7",
+ "univpll_d6",
+ "mainpll_d4",
+ "mmpll_d5",
+ "tvdpll1",
+ "tvdpll2",
+ "univpll_d4",
+ "mmpll_d4"
+};
+
+static const char * const ethdr_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d5_d2",
+ "mmpll_d6_d2",
+ "univpll_d5_d2",
+ "univpll_d4_d2",
+ "mmpll_d4_d2",
+ "mmpll_d7",
+ "univpll_d6",
+ "mainpll_d4",
+ "mmpll_d5_d4",
+ "tvdpll1",
+ "tvdpll2",
+ "univpll_d4",
+ "mmpll_d4"
+};
+
+static const char * const ipe_parents[] = {
+ "clk26m",
+ "imgpll",
+ "mainpll_d4",
+ "mmpll_d6",
+ "univpll_d6",
+ "mainpll_d6",
+ "mmpll_d4_d2",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "mmpll_d6_d2",
+ "univpll_d5_d2",
+ "mainpll_d7"
+};
+
+static const char * const cam_parents[] = {
+ "clk26m",
+ "tvdpll1",
+ "mainpll_d4",
+ "mmpll_d4",
+ "univpll_d4",
+ "univpll_d5",
+ "univpll_d6",
+ "mmpll_d7",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "imgpll"
+};
+
+static const char * const ccu_parents[] = {
+ "clk26m",
+ "univpll_d6",
+ "mainpll_d4_d2",
+ "mainpll_d4",
+ "univpll_d5",
+ "mainpll_d6",
+ "mmpll_d6",
+ "mmpll_d7",
+ "univpll_d4_d2",
+ "univpll_d7"
+};
+
+static const char * const ccu_ahb_parents[] = {
+ "clk26m",
+ "univpll_d6",
+ "mainpll_d4_d2",
+ "mainpll_d4",
+ "univpll_d5",
+ "mainpll_d6",
+ "mmpll_d6",
+ "mmpll_d7",
+ "univpll_d4_d2",
+ "univpll_d7"
+};
+
+static const char * const img_parents[] = {
+ "clk26m",
+ "imgpll",
+ "univpll_d4",
+ "mainpll_d4",
+ "univpll_d5",
+ "mmpll_d6",
+ "mmpll_d7",
+ "univpll_d6",
+ "mainpll_d6",
+ "mmpll_d4_d2",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "univpll_d5_d2"
+};
+
+static const char * const camtm_parents[] = {
+ "clk26m",
+ "univpll_d4_d4",
+ "univpll_d6_d2",
+ "univpll_d6_d4"
+};
+
+static const char * const dsp_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d4_d2",
+ "univpll_d5",
+ "univpll_d4",
+ "mmpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp1_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d5",
+ "mmpll_d5",
+ "univpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp2_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d5",
+ "mmpll_d5",
+ "univpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp3_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d5",
+ "mmpll_d5",
+ "univpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp4_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d4_d2",
+ "mainpll_d4",
+ "univpll_d4",
+ "mmpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp5_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d4_d2",
+ "mainpll_d4",
+ "univpll_d4",
+ "mmpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp6_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d4_d2",
+ "mainpll_d4",
+ "univpll_d4",
+ "mmpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+static const char * const dsp7_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d4_d2",
+ "univpll_d5",
+ "univpll_d4",
+ "mmpll_d4",
+ "mainpll_d3",
+ "univpll_d3"
+};
+
+/*
+ * MFG can be also parented to "univpll_d6" and "univpll_d7":
+ * these have been removed from the parents list to let us
+ * achieve GPU DVFS without any special clock handlers.
+ */
+static const char * const mfg_core_tmp_parents[] = {
+ "clk26m",
+ "mainpll_d5_d2"
+};
+
+static const char * const camtg_parents[] = {
+ "clk26m",
+ "univpll_192m_d8",
+ "univpll_d6_d8",
+ "univpll_192m_d4",
+ "univpll_192m_d10",
+ "clk13m",
+ "univpll_192m_d16",
+ "univpll_192m_d32"
+};
+
+static const char * const camtg2_parents[] = {
+ "clk26m",
+ "univpll_192m_d8",
+ "univpll_d6_d8",
+ "univpll_192m_d4",
+ "univpll_192m_d10",
+ "clk13m",
+ "univpll_192m_d16",
+ "univpll_192m_d32"
+};
+
+static const char * const camtg3_parents[] = {
+ "clk26m",
+ "univpll_192m_d8",
+ "univpll_d6_d8",
+ "univpll_192m_d4",
+ "univpll_192m_d10",
+ "clk13m",
+ "univpll_192m_d16",
+ "univpll_192m_d32"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll_d6_d8"
+};
+
+static const char * const spi_parents[] = {
+ "clk26m",
+ "mainpll_d5_d4",
+ "mainpll_d6_d4",
+ "univpll_d6_d4",
+ "univpll_d6_d2",
+ "mainpll_d6_d2",
+ "mainpll_d4_d4",
+ "univpll_d5_d4"
+};
+
+static const char * const msdc5hclk_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "mainpll_d6_d2"
+};
+
+static const char * const msdc50_0_parents[] = {
+ "clk26m",
+ "msdcpll",
+ "msdcpll_d2",
+ "univpll_d4_d4",
+ "mainpll_d6_d2",
+ "univpll_d4_d2"
+};
+
+static const char * const msdc30_1_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d6_d2",
+ "mainpll_d7_d2",
+ "msdcpll_d2"
+};
+
+static const char * const msdc30_2_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "mainpll_d6_d2",
+ "mainpll_d7_d2",
+ "msdcpll_d2"
+};
+
+static const char * const intdir_parents[] = {
+ "clk26m",
+ "univpll_d6",
+ "mainpll_d4",
+ "univpll_d4"
+};
+
+static const char * const aud_intbus_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d7_d4"
+};
+
+static const char * const audio_h_parents[] = {
+ "clk26m",
+ "univpll_d7",
+ "apll1",
+ "apll2"
+};
+
+static const char * const pwrap_ulposc_parents[] = {
+ "clk26m",
+ "ulposc1_d10",
+ "ulposc1_d7",
+ "ulposc1_d8",
+ "ulposc1_d16",
+ "mainpll_d4_d8",
+ "univpll_d5_d8",
+ "tvdpll1_d16"
+};
+
+static const char * const atb_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const sspm_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2",
+ "mainpll_d6_d2",
+ "mainpll_d5_d2",
+ "mainpll_d9",
+ "mainpll_d4_d2"
+};
+
+/*
+ * Both DP/eDP can be parented to TVDPLL1 and TVDPLL2, but we force using
+ * TVDPLL1 on eDP and TVDPLL2 on DP to avoid changing the "other" PLL rate
+ * in dual output case, which would lead to corruption of functionality loss.
+ */
+static const char * const dp_parents[] = {
+ "clk26m",
+ "tvdpll2_d2",
+ "tvdpll2_d4",
+ "tvdpll2_d8",
+ "tvdpll2_d16"
+};
+static const u8 dp_parents_idx[] = { 0, 2, 4, 6, 8 };
+
+static const char * const edp_parents[] = {
+ "clk26m",
+ "tvdpll1_d2",
+ "tvdpll1_d4",
+ "tvdpll1_d8",
+ "tvdpll1_d16"
+};
+static const u8 edp_parents_idx[] = { 0, 1, 3, 5, 7 };
+
+static const char * const dpi_parents[] = {
+ "clk26m",
+ "tvdpll1_d2",
+ "tvdpll2_d2",
+ "tvdpll1_d4",
+ "tvdpll2_d4",
+ "tvdpll1_d8",
+ "tvdpll2_d8",
+ "tvdpll1_d16",
+ "tvdpll2_d16"
+};
+
+static const char * const disp_pwm0_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "ulposc1_d2",
+ "ulposc1_d4",
+ "ulposc1_d16",
+ "ethpll_d4"
+};
+
+static const char * const disp_pwm1_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "ulposc1_d2",
+ "ulposc1_d4",
+ "ulposc1_d16"
+};
+
+static const char * const usb_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const ssusb_xhci_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const usb_2p_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const ssusb_xhci_2p_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const usb_3p_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const ssusb_xhci_3p_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "univpll_d6_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const i2c_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "univpll_d5_d4"
+};
+
+static const char * const seninf_parents[] = {
+ "clk26m",
+ "univpll_d4_d4",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d7",
+ "univpll_d6",
+ "mmpll_d6",
+ "univpll_d5"
+};
+
+static const char * const seninf1_parents[] = {
+ "clk26m",
+ "univpll_d4_d4",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d7",
+ "univpll_d6",
+ "mmpll_d6",
+ "univpll_d5"
+};
+
+static const char * const gcpu_parents[] = {
+ "clk26m",
+ "mainpll_d6",
+ "univpll_d4_d2",
+ "mmpll_d5_d2",
+ "univpll_d5_d2"
+};
+
+static const char * const venc_parents[] = {
+ "clk26m",
+ "mmpll_d4_d2",
+ "mainpll_d6",
+ "univpll_d4_d2",
+ "mainpll_d4_d2",
+ "univpll_d6",
+ "mmpll_d6",
+ "mainpll_d5_d2",
+ "mainpll_d6_d2",
+ "mmpll_d9",
+ "univpll_d4_d4",
+ "mainpll_d4",
+ "univpll_d4",
+ "univpll_d5",
+ "univpll_d5_d2",
+ "mainpll_d5"
+};
+
+static const char * const vdec_parents[] = {
+ "clk26m",
+ "mainpll_d5_d2",
+ "mmpll_d6_d2",
+ "univpll_d5_d2",
+ "univpll_d4_d2",
+ "mmpll_d4_d2",
+ "univpll_d6",
+ "mainpll_d5",
+ "univpll_d5",
+ "mmpll_d6",
+ "mainpll_d4",
+ "tvdpll2",
+ "univpll_d4",
+ "imgpll",
+ "univpll_d6_d2",
+ "mmpll_d9"
+};
+
+static const char * const pwm_parents[] = {
+ "clk32k",
+ "clk26m",
+ "univpll_d4_d8",
+ "univpll_d6_d4"
+};
+
+static const char * const mcupm_parents[] = {
+ "clk26m",
+ "mainpll_d6_d2",
+ "mainpll_d7_d4"
+};
+
+static const char * const spmi_p_mst_parents[] = {
+ "clk26m",
+ "clk13m",
+ "ulposc1_d8",
+ "ulposc1_d10",
+ "ulposc1_d16",
+ "ulposc1_d7",
+ "clk32k",
+ "mainpll_d7_d8",
+ "mainpll_d6_d8",
+ "mainpll_d5_d8"
+};
+
+static const char * const spmi_m_mst_parents[] = {
+ "clk26m",
+ "clk13m",
+ "ulposc1_d8",
+ "ulposc1_d10",
+ "ulposc1_d16",
+ "ulposc1_d7",
+ "clk32k",
+ "mainpll_d7_d8",
+ "mainpll_d6_d8",
+ "mainpll_d5_d8"
+};
+
+static const char * const dvfsrc_parents[] = {
+ "clk26m",
+ "ulposc1_d10",
+ "univpll_d6_d8",
+ "msdcpll_d16"
+};
+
+static const char * const tl_parents[] = {
+ "clk26m",
+ "univpll_d5_d4",
+ "mainpll_d4_d4"
+};
+
+static const char * const aes_msdcfde_parents[] = {
+ "clk26m",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d4_d4",
+ "univpll_d4_d2",
+ "univpll_d6"
+};
+
+static const char * const dsi_occ_parents[] = {
+ "clk26m",
+ "univpll_d6_d2",
+ "univpll_d5_d2",
+ "univpll_d4_d2"
+};
+
+static const char * const wpe_vpp_parents[] = {
+ "clk26m",
+ "mainpll_d5_d2",
+ "mmpll_d6_d2",
+ "univpll_d5_d2",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "mmpll_d4_d2",
+ "mainpll_d6",
+ "mmpll_d7",
+ "univpll_d6",
+ "mainpll_d5",
+ "univpll_d5",
+ "mainpll_d4",
+ "tvdpll1",
+ "univpll_d4"
+};
+
+static const char * const hdcp_parents[] = {
+ "clk26m",
+ "univpll_d4_d8",
+ "mainpll_d5_d8",
+ "univpll_d6_d4"
+};
+
+static const char * const hdcp_24m_parents[] = {
+ "clk26m",
+ "univpll_192m_d4",
+ "univpll_192m_d8",
+ "univpll_d6_d8"
+};
+
+static const char * const hdmi_apb_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "msdcpll_d2"
+};
+
+static const char * const snps_eth_250m_parents[] = {
+ "clk26m",
+ "ethpll_d2"
+};
+
+static const char * const snps_eth_62p4m_ptp_parents[] = {
+ "apll2_d3",
+ "apll1_d3",
+ "clk26m",
+ "ethpll_d8"
+};
+
+static const char * const snps_eth_50m_rmii_parents[] = {
+ "clk26m",
+ "ethpll_d10"
+};
+
+static const char * const adsp_parents[] = {
+ "clk26m",
+ "clk13m",
+ "mainpll_d6",
+ "mainpll_d5_d2",
+ "univpll_d4_d4",
+ "univpll_d4",
+ "ulposc1_d2",
+ "ulposc1_ck1",
+ "adsppll",
+ "adsppll_d2",
+ "adsppll_d4",
+ "adsppll_d8"
+};
+
+static const char * const audio_local_bus_parents[] = {
+ "clk26m",
+ "clk13m",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d4",
+ "univpll_d6",
+ "ulposc1_ck1",
+ "ulposc1_d4",
+ "ulposc1_d2"
+};
+
+static const char * const asm_h_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "univpll_d6_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const asm_l_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "univpll_d6_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const apll1_parents[] = {
+ "clk26m",
+ "apll1_d4"
+};
+
+static const char * const apll2_parents[] = {
+ "clk26m",
+ "apll2_d4"
+};
+
+static const char * const apll3_parents[] = {
+ "clk26m",
+ "apll3_d4"
+};
+
+static const char * const apll4_parents[] = {
+ "clk26m",
+ "apll4_d4"
+};
+
+static const char * const apll5_parents[] = {
+ "clk26m",
+ "apll5_d4"
+};
+
+static const char * const i2so1_parents[] = {
+ "clk26m",
+ "apll1",
+ "apll2",
+ "apll3",
+ "apll4",
+ "apll5"
+};
+
+static const char * const i2so2_parents[] = {
+ "clk26m",
+ "apll1",
+ "apll2",
+ "apll3",
+ "apll4",
+ "apll5"
+};
+
+static const char * const i2si1_parents[] = {
+ "clk26m",
+ "apll1",
+ "apll2",
+ "apll3",
+ "apll4",
+ "apll5"
+};
+
+static const char * const i2si2_parents[] = {
+ "clk26m",
+ "apll1",
+ "apll2",
+ "apll3",
+ "apll4",
+ "apll5"
+};
+
+static const char * const dptx_parents[] = {
+ "clk26m",
+ "apll1",
+ "apll2",
+ "apll3",
+ "apll4",
+ "apll5"
+};
+
+static const char * const aud_iec_parents[] = {
+ "clk26m",
+ "apll1",
+ "apll2",
+ "apll3",
+ "apll4",
+ "apll5"
+};
+
+static const char * const a1sys_hp_parents[] = {
+ "clk26m",
+ "apll1_d4"
+};
+
+static const char * const a2sys_parents[] = {
+ "clk26m",
+ "apll2_d4"
+};
+
+static const char * const a3sys_parents[] = {
+ "clk26m",
+ "apll3_d4",
+ "apll4_d4",
+ "apll5_d4"
+};
+
+static const char * const a4sys_parents[] = {
+ "clk26m",
+ "apll3_d4",
+ "apll4_d4",
+ "apll5_d4"
+};
+
+static const char * const ecc_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "univpll_d6"
+};
+
+static const char * const spinor_parents[] = {
+ "clk26m",
+ "clk13m",
+ "mainpll_d7_d8",
+ "univpll_d6_d8"
+};
+
+static const char * const ulposc_parents[] = {
+ "ulposc_ck1",
+ "ethpll_d2",
+ "mainpll_d4_d2",
+ "ethpll_d10"
+};
+
+static const char * const srck_parents[] = {
+ "ulposc1_d10",
+ "clk26m"
+};
+
+static const char * const mfg_fast_ref_parents[] = {
+ "top_mfg_core_tmp",
+ "mfgpll"
+};
+
+static const struct mtk_mux top_mtk_muxes[] = {
+ /*
+ * CLK_CFG_0
+ * axi_sel and bus_aximem_sel are bus clocks, should not be closed by Linux.
+ * spm_sel and scp_sel are main clocks in always-on co-processor.
+ */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI, "top_axi", axi_parents,
+ 0x020, 0x024, 0x028, 0, 4, 7, 0x04, 0,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SPM, "top_spm", spm_parents,
+ 0x020, 0x024, 0x028, 8, 4, 15, 0x04, 1,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SCP, "top_scp", scp_parents,
+ 0x020, 0x024, 0x028, 16, 4, 23, 0x04, 2,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_BUS_AXIMEM, "top_bus_aximem", bus_aximem_parents,
+ 0x020, 0x024, 0x028, 24, 4, 31, 0x04, 3,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ /* CLK_CFG_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VPP, "top_vpp",
+ vpp_parents, 0x02C, 0x030, 0x034, 0, 4, 7, 0x04, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETHDR, "top_ethdr",
+ ethdr_parents, 0x02C, 0x030, 0x034, 8, 4, 15, 0x04, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_IPE, "top_ipe",
+ ipe_parents, 0x02C, 0x030, 0x034, 16, 4, 23, 0x04, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAM, "top_cam",
+ cam_parents, 0x02C, 0x030, 0x034, 24, 4, 31, 0x04, 7),
+ /* CLK_CFG_2 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CCU, "top_ccu",
+ ccu_parents, 0x038, 0x03C, 0x040, 0, 4, 7, 0x04, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CCU_AHB, "top_ccu_ahb",
+ ccu_ahb_parents, 0x038, 0x03C, 0x040, 8, 4, 15, 0x04, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_IMG, "top_img",
+ img_parents, 0x038, 0x03C, 0x040, 16, 4, 23, 0x04, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTM, "top_camtm",
+ camtm_parents, 0x038, 0x03C, 0x040, 24, 4, 31, 0x04, 11),
+ /* CLK_CFG_3 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP, "top_dsp",
+ dsp_parents, 0x044, 0x048, 0x04C, 0, 4, 7, 0x04, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP1, "top_dsp1",
+ dsp1_parents, 0x044, 0x048, 0x04C, 8, 4, 15, 0x04, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP2, "top_dsp2",
+ dsp2_parents, 0x044, 0x048, 0x04C, 16, 4, 23, 0x04, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP3, "top_dsp3",
+ dsp3_parents, 0x044, 0x048, 0x04C, 24, 4, 31, 0x04, 15),
+ /* CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP4, "top_dsp4",
+ dsp4_parents, 0x050, 0x054, 0x058, 0, 4, 7, 0x04, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP5, "top_dsp5",
+ dsp5_parents, 0x050, 0x054, 0x058, 8, 4, 15, 0x04, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP6, "top_dsp6",
+ dsp6_parents, 0x050, 0x054, 0x058, 16, 4, 23, 0x04, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP7, "top_dsp7",
+ dsp7_parents, 0x050, 0x054, 0x058, 24, 4, 31, 0x04, 19),
+ /* CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_CORE_TMP, "top_mfg_core_tmp",
+ mfg_core_tmp_parents, 0x05C, 0x060, 0x064, 0, 4, 7, 0x04, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG, "top_camtg",
+ camtg_parents, 0x05C, 0x060, 0x064, 8, 4, 15, 0x04, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG2, "top_camtg2",
+ camtg2_parents, 0x05C, 0x060, 0x064, 16, 4, 23, 0x04, 22),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG3, "top_camtg3",
+ camtg3_parents, 0x05C, 0x060, 0x064, 24, 4, 31, 0x04, 23),
+ /* CLK_CFG_6 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART, "top_uart",
+ uart_parents, 0x068, 0x06C, 0x070, 0, 4, 7, 0x04, 24),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI, "top_spi",
+ spi_parents, 0x068, 0x06C, 0x070, 8, 4, 15, 0x04, 25),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_HCLK, "top_msdc5hclk",
+ msdc5hclk_parents, 0x068, 0x06C, 0x070, 16, 4, 23, 0x04, 26, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0, "top_msdc50_0",
+ msdc50_0_parents, 0x068, 0x06C, 0x070, 24, 4, 31, 0x04, 27, 0),
+ /* CLK_CFG_7 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1, "top_msdc30_1",
+ msdc30_1_parents, 0x074, 0x078, 0x07C, 0, 4, 7, 0x04, 28, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_2, "top_msdc30_2",
+ msdc30_2_parents, 0x074, 0x078, 0x07C, 8, 4, 15, 0x04, 29, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_INTDIR, "top_intdir",
+ intdir_parents, 0x074, 0x078, 0x07C, 16, 4, 23, 0x04, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_INTBUS, "top_aud_intbus",
+ aud_intbus_parents, 0x074, 0x078, 0x07C, 24, 4, 31, 0x04, 31),
+ /* CLK_CFG_8 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_H, "top_audio_h",
+ audio_h_parents, 0x080, 0x084, 0x088, 0, 4, 7, 0x08, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWRAP_ULPOSC, "top_pwrap_ulposc",
+ pwrap_ulposc_parents, 0x080, 0x084, 0x088, 8, 4, 15, 0x08, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ATB, "top_atb",
+ atb_parents, 0x080, 0x084, 0x088, 16, 4, 23, 0x08, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSPM, "top_sspm",
+ sspm_parents, 0x080, 0x084, 0x088, 24, 4, 31, 0x08, 3),
+ /* CLK_CFG_9 */
+ MUX_GATE_CLR_SET_UPD_INDEXED(CLK_TOP_DP, "top_dp",
+ dp_parents, dp_parents_idx, 0x08C, 0x090, 0x094,
+ 0, 4, 7, 0x08, 4),
+ MUX_GATE_CLR_SET_UPD_INDEXED(CLK_TOP_EDP, "top_edp",
+ edp_parents, edp_parents_idx, 0x08C, 0x090, 0x094,
+ 8, 4, 15, 0x08, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DPI, "top_dpi",
+ dpi_parents, 0x08C, 0x090, 0x094, 16, 4, 23, 0x08, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_PWM0, "top_disp_pwm0",
+ disp_pwm0_parents, 0x08C, 0x090, 0x094, 24, 4, 31, 0x08, 7),
+ /* CLK_CFG_10 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_PWM1, "top_disp_pwm1",
+ disp_pwm1_parents, 0x098, 0x09C, 0x0A0, 0, 4, 7, 0x08, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP, "top_usb_top",
+ usb_parents, 0x098, 0x09C, 0x0A0, 8, 4, 15, 0x08, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSUSB_XHCI, "top_ssusb_xhci",
+ ssusb_xhci_parents, 0x098, 0x09C, 0x0A0, 16, 4, 23, 0x08, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_2P, "top_usb_top_2p",
+ usb_2p_parents, 0x098, 0x09C, 0x0A0, 24, 4, 31, 0x08, 11),
+ /* CLK_CFG_11 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSUSB_XHCI_2P, "top_ssusb_xhci_2p",
+ ssusb_xhci_2p_parents, 0x0A4, 0x0A8, 0x0AC, 0, 4, 7, 0x08, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_3P, "top_usb_top_3p",
+ usb_3p_parents, 0x0A4, 0x0A8, 0x0AC, 8, 4, 15, 0x08, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSUSB_XHCI_3P, "top_ssusb_xhci_3p",
+ ssusb_xhci_3p_parents, 0x0A4, 0x0A8, 0x0AC, 16, 4, 23, 0x08, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C, "top_i2c",
+ i2c_parents, 0x0A4, 0x0A8, 0x0AC, 24, 4, 31, 0x08, 15),
+ /* CLK_CFG_12 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF, "top_seninf",
+ seninf_parents, 0x0B0, 0x0B4, 0x0B8, 0, 4, 7, 0x08, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF1, "top_seninf1",
+ seninf1_parents, 0x0B0, 0x0B4, 0x0B8, 8, 4, 15, 0x08, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_GCPU, "top_gcpu",
+ gcpu_parents, 0x0B0, 0x0B4, 0x0B8, 16, 4, 23, 0x08, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VENC, "top_venc",
+ venc_parents, 0x0B0, 0x0B4, 0x0B8, 24, 4, 31, 0x08, 19),
+ /*
+ * CLK_CFG_13
+ * top_mcupm is main clock in co-processor, should not be handled by Linux.
+ */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_VDEC, "top_vdec",
+ vdec_parents, 0x0BC, 0x0C0, 0x0C4, 0, 4, 7, 0x08, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM, "top_pwm",
+ pwm_parents, 0x0BC, 0x0C0, 0x0C4, 8, 4, 15, 0x08, 21),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MCUPM, "top_mcupm", mcupm_parents,
+ 0x0BC, 0x0C0, 0x0C4, 16, 4, 23, 0x08, 22,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPMI_P_MST, "top_spmi_p_mst",
+ spmi_p_mst_parents, 0x0BC, 0x0C0, 0x0C4, 24, 4, 31, 0x08, 23),
+ /*
+ * CLK_CFG_14
+ * dvfsrc_sel is for internal DVFS usage, should not be handled by Linux.
+ */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPMI_M_MST, "top_spmi_m_mst",
+ spmi_m_mst_parents, 0x0C8, 0x0CC, 0x0D0, 0, 4, 7, 0x08, 24),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DVFSRC, "top_dvfsrc", dvfsrc_parents,
+ 0x0C8, 0x0CC, 0x0D0, 8, 4, 15, 0x08, 25,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_TL, "top_tl",
+ tl_parents, 0x0C8, 0x0CC, 0x0D0, 16, 4, 23, 0x08, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AES_MSDCFDE, "top_aes_msdcfde",
+ aes_msdcfde_parents, 0x0C8, 0x0CC, 0x0D0, 24, 4, 31, 0x08, 27),
+ /* CLK_CFG_15 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSI_OCC, "top_dsi_occ",
+ dsi_occ_parents, 0x0D4, 0x0D8, 0x0DC, 0, 4, 7, 0x08, 28),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_WPE_VPP, "top_wpe_vpp",
+ wpe_vpp_parents, 0x0D4, 0x0D8, 0x0DC, 8, 4, 15, 0x08, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_HDCP, "top_hdcp",
+ hdcp_parents, 0x0D4, 0x0D8, 0x0DC, 16, 4, 23, 0x08, 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_HDCP_24M, "top_hdcp_24m",
+ hdcp_24m_parents, 0x0D4, 0x0D8, 0x0DC, 24, 4, 31, 0x08, 31),
+ /* CLK_CFG_16 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_HDMI_APB, "top_hdmi_apb",
+ hdmi_apb_parents, 0x0E0, 0x0E4, 0x0E8, 0, 4, 7, 0x0C, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SNPS_ETH_250M, "top_snps_eth_250m",
+ snps_eth_250m_parents, 0x0E0, 0x0E4, 0x0E8, 8, 4, 15, 0x0C, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SNPS_ETH_62P4M_PTP, "top_snps_eth_62p4m_ptp",
+ snps_eth_62p4m_ptp_parents, 0x0E0, 0x0E4, 0x0E8, 16, 4, 23, 0x0C, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SNPS_ETH_50M_RMII, "snps_eth_50m_rmii",
+ snps_eth_50m_rmii_parents, 0x0E0, 0x0E4, 0x0E8, 24, 4, 31, 0x0C, 3),
+ /* CLK_CFG_17 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ADSP, "top_adsp",
+ adsp_parents, 0x0EC, 0x0F0, 0x0F4, 0, 4, 7, 0x0C, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_LOCAL_BUS, "top_audio_local_bus",
+ audio_local_bus_parents, 0x0EC, 0x0F0, 0x0F4, 8, 4, 15, 0x0C, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ASM_H, "top_asm_h",
+ asm_h_parents, 0x0EC, 0x0F0, 0x0F4, 16, 4, 23, 0x0C, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ASM_L, "top_asm_l",
+ asm_l_parents, 0x0EC, 0x0F0, 0x0F4, 24, 4, 31, 0x0C, 7),
+ /* CLK_CFG_18 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APLL1, "top_apll1",
+ apll1_parents, 0x0F8, 0x0FC, 0x100, 0, 4, 7, 0x0C, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APLL2, "top_apll2",
+ apll2_parents, 0x0F8, 0x0FC, 0x100, 8, 4, 15, 0x0C, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APLL3, "top_apll3",
+ apll3_parents, 0x0F8, 0x0FC, 0x100, 16, 4, 23, 0x0C, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APLL4, "top_apll4",
+ apll4_parents, 0x0F8, 0x0FC, 0x100, 24, 4, 31, 0x0C, 11),
+ /* CLK_CFG_19 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APLL5, "top_apll5",
+ apll5_parents, 0x0104, 0x0108, 0x010C, 0, 4, 7, 0x0C, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2SO1, "top_i2so1",
+ i2so1_parents, 0x0104, 0x0108, 0x010C, 8, 4, 15, 0x0C, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2SO2, "top_i2so2",
+ i2so2_parents, 0x0104, 0x0108, 0x010C, 16, 4, 23, 0x0C, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2SI1, "top_i2si1",
+ i2si1_parents, 0x0104, 0x0108, 0x010C, 24, 4, 31, 0x0C, 15),
+ /* CLK_CFG_20 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2SI2, "top_i2si2",
+ i2si2_parents, 0x0110, 0x0114, 0x0118, 0, 4, 7, 0x0C, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DPTX, "top_dptx",
+ dptx_parents, 0x0110, 0x0114, 0x0118, 8, 4, 15, 0x0C, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_IEC, "top_aud_iec",
+ aud_iec_parents, 0x0110, 0x0114, 0x0118, 16, 4, 23, 0x0C, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A1SYS_HP, "top_a1sys_hp",
+ a1sys_hp_parents, 0x0110, 0x0114, 0x0118, 24, 4, 31, 0x0C, 19),
+ /* CLK_CFG_21 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A2SYS, "top_a2sys",
+ a2sys_parents, 0x011C, 0x0120, 0x0124, 0, 4, 7, 0x0C, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A3SYS, "top_a3sys",
+ a3sys_parents, 0x011C, 0x0120, 0x0124, 8, 4, 15, 0x0C, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_A4SYS, "top_a4sys",
+ a4sys_parents, 0x011C, 0x0120, 0x0124, 16, 4, 23, 0x0C, 22),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ECC, "top_ecc",
+ ecc_parents, 0x011C, 0x0120, 0x0124, 24, 4, 31, 0x0C, 23),
+ /*
+ * CLK_CFG_22
+ * top_ulposc/top_srck are clock source of always on co-processor,
+ * should not be closed by Linux.
+ */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPINOR, "top_spinor",
+ spinor_parents, 0x0128, 0x012C, 0x0130, 0, 4, 7, 0x0C, 24),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_ULPOSC, "top_ulposc", ulposc_parents,
+ 0x0128, 0x012C, 0x0130, 8, 4, 15, 0x0C, 25,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SRCK, "top_srck", srck_parents,
+ 0x0128, 0x012C, 0x0130, 16, 4, 23, 0x0C, 26,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+};
+
+static const struct mtk_composite top_adj_divs[] = {
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV0, "apll12_div0", "top_i2si1", 0x0320, 0, 0x0328, 8, 0),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV1, "apll12_div1", "top_i2si2", 0x0320, 1, 0x0328, 8, 8),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV2, "apll12_div2", "top_i2so1", 0x0320, 2, 0x0328, 8, 16),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV3, "apll12_div3", "top_i2so2", 0x0320, 3, 0x0328, 8, 24),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV4, "apll12_div4", "top_aud_iec", 0x0320, 4, 0x0334, 8, 0),
+ DIV_GATE(CLK_TOP_APLL12_CK_DIV9, "apll12_div9", "top_dptx", 0x0320, 9, 0x0338, 8, 8),
+};
+static const struct mtk_gate_regs top0_cg_regs = {
+ .set_ofs = 0x238,
+ .clr_ofs = 0x238,
+ .sta_ofs = 0x238,
+};
+
+static const struct mtk_gate_regs top1_cg_regs = {
+ .set_ofs = 0x250,
+ .clr_ofs = 0x250,
+ .sta_ofs = 0x250,
+};
+
+#define GATE_TOP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate top_clks[] = {
+ /* TOP0 */
+ GATE_TOP0(CLK_TOP_CFGREG_CLOCK_EN_VPP0, "cfgreg_clock_vpp0", "top_vpp", 0),
+ GATE_TOP0(CLK_TOP_CFGREG_CLOCK_EN_VPP1, "cfgreg_clock_vpp1", "top_vpp", 1),
+ GATE_TOP0(CLK_TOP_CFGREG_CLOCK_EN_VDO0, "cfgreg_clock_vdo0", "top_vpp", 2),
+ GATE_TOP0(CLK_TOP_CFGREG_CLOCK_EN_VDO1, "cfgreg_clock_vdo1", "top_vpp", 3),
+ GATE_TOP0(CLK_TOP_CFGREG_CLOCK_ISP_AXI_GALS, "cfgreg_clock_isp_axi_gals", "top_vpp", 4),
+ GATE_TOP0(CLK_TOP_CFGREG_F26M_VPP0, "cfgreg_f26m_vpp0", "clk26m", 5),
+ GATE_TOP0(CLK_TOP_CFGREG_F26M_VPP1, "cfgreg_f26m_vpp1", "clk26m", 6),
+ GATE_TOP0(CLK_TOP_CFGREG_F26M_VDO0, "cfgreg_f26m_vdo0", "clk26m", 7),
+ GATE_TOP0(CLK_TOP_CFGREG_F26M_VDO1, "cfgreg_f26m_vdo1", "clk26m", 8),
+ GATE_TOP0(CLK_TOP_CFGREG_AUD_F26M_AUD, "cfgreg_aud_f26m_aud", "clk26m", 9),
+ GATE_TOP0(CLK_TOP_CFGREG_UNIPLL_SES, "cfgreg_unipll_ses", "univpll_d2", 15),
+ GATE_TOP0(CLK_TOP_CFGREG_F_PCIE_PHY_REF, "cfgreg_f_pcie_phy_ref", "clk26m", 18),
+ /* TOP1 */
+ GATE_TOP1(CLK_TOP_SSUSB_TOP_REF, "ssusb_ref", "clk26m", 0),
+ GATE_TOP1(CLK_TOP_SSUSB_PHY_REF, "ssusb_phy_ref", "clk26m", 1),
+ GATE_TOP1(CLK_TOP_SSUSB_TOP_P1_REF, "ssusb_p1_ref", "clk26m", 2),
+ GATE_TOP1(CLK_TOP_SSUSB_PHY_P1_REF, "ssusb_phy_p1_ref", "clk26m", 3),
+ GATE_TOP1(CLK_TOP_SSUSB_TOP_P2_REF, "ssusb_p2_ref", "clk26m", 4),
+ GATE_TOP1(CLK_TOP_SSUSB_PHY_P2_REF, "ssusb_phy_p2_ref", "clk26m", 5),
+ GATE_TOP1(CLK_TOP_SSUSB_TOP_P3_REF, "ssusb_p3_ref", "clk26m", 6),
+ GATE_TOP1(CLK_TOP_SSUSB_PHY_P3_REF, "ssusb_phy_p3_ref", "clk26m", 7),
+};
+
+static const struct of_device_id of_match_clk_mt8188_topck[] = {
+ { .compatible = "mediatek,mt8188-topckgen" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_topck);
+
+/* Register mux notifier for MFG mux */
+static int clk_mt8188_reg_mfg_mux_notifier(struct device *dev, struct clk *clk)
+{
+ struct mtk_mux_nb *mfg_mux_nb;
+
+ mfg_mux_nb = devm_kzalloc(dev, sizeof(*mfg_mux_nb), GFP_KERNEL);
+ if (!mfg_mux_nb)
+ return -ENOMEM;
+
+ mfg_mux_nb->ops = &clk_mux_ops;
+ mfg_mux_nb->bypass_index = 0; /* Bypass to TOP_MFG_CORE_TMP */
+
+ return devm_mtk_clk_mux_notifier_register(dev, clk, mfg_mux_nb);
+}
+
+static int clk_mt8188_topck_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *top_clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw *hw;
+ int r;
+ void __iomem *base;
+
+ top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
+ if (!top_clk_data)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ r = PTR_ERR(base);
+ goto free_top_data;
+ }
+
+ r = mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
+ top_clk_data);
+ if (r)
+ goto free_top_data;
+
+ r = mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
+ if (r)
+ goto unregister_fixed_clks;
+
+ r = mtk_clk_register_muxes(&pdev->dev, top_mtk_muxes,
+ ARRAY_SIZE(top_mtk_muxes), node,
+ &mt8188_clk_lock, top_clk_data);
+ if (r)
+ goto unregister_factors;
+
+ hw = devm_clk_hw_register_mux(&pdev->dev, "mfg_ck_fast_ref", mfg_fast_ref_parents,
+ ARRAY_SIZE(mfg_fast_ref_parents), CLK_SET_RATE_PARENT,
+ (base + 0x250), 8, 1, 0, &mt8188_clk_lock);
+ if (IS_ERR(hw)) {
+ r = PTR_ERR(hw);
+ goto unregister_muxes;
+ }
+ top_clk_data->hws[CLK_TOP_MFG_CK_FAST_REF] = hw;
+
+ r = clk_mt8188_reg_mfg_mux_notifier(&pdev->dev,
+ top_clk_data->hws[CLK_TOP_MFG_CK_FAST_REF]->clk);
+ if (r)
+ goto unregister_muxes;
+
+ r = mtk_clk_register_composites(&pdev->dev, top_adj_divs,
+ ARRAY_SIZE(top_adj_divs), base,
+ &mt8188_clk_lock, top_clk_data);
+ if (r)
+ goto unregister_muxes;
+
+ r = mtk_clk_register_gates(&pdev->dev, node, top_clks,
+ ARRAY_SIZE(top_clks), top_clk_data);
+ if (r)
+ goto unregister_composite_divs;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, top_clk_data);
+ if (r)
+ goto unregister_gates;
+
+ platform_set_drvdata(pdev, top_clk_data);
+
+ return r;
+
+unregister_gates:
+ mtk_clk_unregister_gates(top_clks, ARRAY_SIZE(top_clks), top_clk_data);
+unregister_composite_divs:
+ mtk_clk_unregister_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), top_clk_data);
+unregister_muxes:
+ mtk_clk_unregister_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), top_clk_data);
+unregister_factors:
+ mtk_clk_unregister_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
+unregister_fixed_clks:
+ mtk_clk_unregister_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), top_clk_data);
+free_top_data:
+ mtk_free_clk_data(top_clk_data);
+ return r;
+}
+
+static void clk_mt8188_topck_remove(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *top_clk_data = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_gates(top_clks, ARRAY_SIZE(top_clks), top_clk_data);
+ mtk_clk_unregister_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), top_clk_data);
+ mtk_clk_unregister_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), top_clk_data);
+ mtk_clk_unregister_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
+ mtk_clk_unregister_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), top_clk_data);
+ mtk_free_clk_data(top_clk_data);
+}
+
+static struct platform_driver clk_mt8188_topck_drv = {
+ .probe = clk_mt8188_topck_probe,
+ .remove = clk_mt8188_topck_remove,
+ .driver = {
+ .name = "clk-mt8188-topck",
+ .of_match_table = of_match_clk_mt8188_topck,
+ },
+};
+module_platform_driver(clk_mt8188_topck_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 top clock generators driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-vdec.c b/drivers/clk/mediatek/clk-mt8188-vdec.c
new file mode 100644
index 000000000000..f48f0716d7c2
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-vdec.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vdec0_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs vdec1_cg_regs = {
+ .set_ofs = 0x200,
+ .clr_ofs = 0x204,
+ .sta_ofs = 0x200,
+};
+
+static const struct mtk_gate_regs vdec2_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x8,
+};
+
+#define GATE_VDEC0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec0_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_VDEC1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_VDEC2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec2_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate vdec1_clks[] = {
+ /* VDEC1_0 */
+ GATE_VDEC0(CLK_VDEC1_SOC_VDEC, "vdec1_soc_vdec", "top_vdec", 0),
+ GATE_VDEC0(CLK_VDEC1_SOC_VDEC_ACTIVE, "vdec1_soc_vdec_active", "top_vdec", 4),
+ GATE_VDEC0(CLK_VDEC1_SOC_VDEC_ENG, "vdec1_soc_vdec_eng", "top_vdec", 8),
+ /* VDEC1_1 */
+ GATE_VDEC1(CLK_VDEC1_SOC_LAT, "vdec1_soc_lat", "top_vdec", 0),
+ GATE_VDEC1(CLK_VDEC1_SOC_LAT_ACTIVE, "vdec1_soc_lat_active", "top_vdec", 4),
+ GATE_VDEC1(CLK_VDEC1_SOC_LAT_ENG, "vdec1_soc_lat_eng", "top_vdec", 8),
+ /* VDEC1_2 */
+ GATE_VDEC2(CLK_VDEC1_SOC_LARB1, "vdec1_soc_larb1", "top_vdec", 0),
+};
+
+static const struct mtk_gate vdec2_clks[] = {
+ /* VDEC2_0 */
+ GATE_VDEC0(CLK_VDEC2_VDEC, "vdec2_vdec", "top_vdec", 0),
+ GATE_VDEC0(CLK_VDEC2_VDEC_ACTIVE, "vdec2_vdec_active", "top_vdec", 4),
+ GATE_VDEC0(CLK_VDEC2_VDEC_ENG, "vdec2_vdec_eng", "top_vdec", 8),
+ /* VDEC2_1 */
+ GATE_VDEC1(CLK_VDEC2_LAT, "vdec2_lat", "top_vdec", 0),
+ /* VDEC2_2 */
+ GATE_VDEC2(CLK_VDEC2_LARB1, "vdec2_larb1", "top_vdec", 0),
+};
+
+static const struct mtk_clk_desc vdec1_desc = {
+ .clks = vdec1_clks,
+ .num_clks = ARRAY_SIZE(vdec1_clks),
+};
+
+static const struct mtk_clk_desc vdec2_desc = {
+ .clks = vdec2_clks,
+ .num_clks = ARRAY_SIZE(vdec2_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_vdec[] = {
+ { .compatible = "mediatek,mt8188-vdecsys-soc", .data = &vdec1_desc },
+ { .compatible = "mediatek,mt8188-vdecsys", .data = &vdec2_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_vdec);
+
+static struct platform_driver clk_mt8188_vdec_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-vdec",
+ .of_match_table = of_match_clk_mt8188_vdec,
+ },
+};
+
+module_platform_driver(clk_mt8188_vdec_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-vdo0.c b/drivers/clk/mediatek/clk-mt8188-vdo0.c
new file mode 100644
index 000000000000..017d6662589b
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-vdo0.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vdo0_0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs vdo0_1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs vdo0_2_cg_regs = {
+ .set_ofs = 0x124,
+ .clr_ofs = 0x128,
+ .sta_ofs = 0x120,
+};
+
+#define GATE_VDO0_0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo0_0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO0_1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo0_1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO0_2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo0_2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO0_2_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &vdo0_2_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flags)
+
+static const struct mtk_gate vdo0_clks[] = {
+ /* VDO0_0 */
+ GATE_VDO0_0(CLK_VDO0_DISP_OVL0, "vdo0_disp_ovl0", "top_vpp", 0),
+ GATE_VDO0_0(CLK_VDO0_FAKE_ENG0, "vdo0_fake_eng0", "top_vpp", 2),
+ GATE_VDO0_0(CLK_VDO0_DISP_CCORR0, "vdo0_disp_ccorr0", "top_vpp", 4),
+ GATE_VDO0_0(CLK_VDO0_DISP_MUTEX0, "vdo0_disp_mutex0", "top_vpp", 6),
+ GATE_VDO0_0(CLK_VDO0_DISP_GAMMA0, "vdo0_disp_gamma0", "top_vpp", 8),
+ GATE_VDO0_0(CLK_VDO0_DISP_DITHER0, "vdo0_disp_dither0", "top_vpp", 10),
+ GATE_VDO0_0(CLK_VDO0_DISP_WDMA0, "vdo0_disp_wdma0", "top_vpp", 17),
+ GATE_VDO0_0(CLK_VDO0_DISP_RDMA0, "vdo0_disp_rdma0", "top_vpp", 19),
+ GATE_VDO0_0(CLK_VDO0_DSI0, "vdo0_dsi0", "top_vpp", 21),
+ GATE_VDO0_0(CLK_VDO0_DSI1, "vdo0_dsi1", "top_vpp", 22),
+ GATE_VDO0_0(CLK_VDO0_DSC_WRAP0, "vdo0_dsc_wrap0", "top_vpp", 23),
+ GATE_VDO0_0(CLK_VDO0_VPP_MERGE0, "vdo0_vpp_merge0", "top_vpp", 24),
+ GATE_VDO0_0(CLK_VDO0_DP_INTF0, "vdo0_dp_intf0", "top_vpp", 25),
+ GATE_VDO0_0(CLK_VDO0_DISP_AAL0, "vdo0_disp_aal0", "top_vpp", 26),
+ GATE_VDO0_0(CLK_VDO0_INLINEROT0, "vdo0_inlinerot0", "top_vpp", 27),
+ GATE_VDO0_0(CLK_VDO0_APB_BUS, "vdo0_apb_bus", "top_vpp", 28),
+ GATE_VDO0_0(CLK_VDO0_DISP_COLOR0, "vdo0_disp_color0", "top_vpp", 29),
+ GATE_VDO0_0(CLK_VDO0_MDP_WROT0, "vdo0_mdp_wrot0", "top_vpp", 30),
+ GATE_VDO0_0(CLK_VDO0_DISP_RSZ0, "vdo0_disp_rsz0", "top_vpp", 31),
+ /* VDO0_1 */
+ GATE_VDO0_1(CLK_VDO0_DISP_POSTMASK0, "vdo0_disp_postmask0", "top_vpp", 0),
+ GATE_VDO0_1(CLK_VDO0_FAKE_ENG1, "vdo0_fake_eng1", "top_vpp", 1),
+ GATE_VDO0_1(CLK_VDO0_DL_ASYNC2, "vdo0_dl_async2", "top_vpp", 5),
+ GATE_VDO0_1(CLK_VDO0_DL_RELAY3, "vdo0_dl_relay3", "top_vpp", 6),
+ GATE_VDO0_1(CLK_VDO0_DL_RELAY4, "vdo0_dl_relay4", "top_vpp", 7),
+ GATE_VDO0_1(CLK_VDO0_SMI_GALS, "vdo0_smi_gals", "top_vpp", 10),
+ GATE_VDO0_1(CLK_VDO0_SMI_COMMON, "vdo0_smi_common", "top_vpp", 11),
+ GATE_VDO0_1(CLK_VDO0_SMI_EMI, "vdo0_smi_emi", "top_vpp", 12),
+ GATE_VDO0_1(CLK_VDO0_SMI_IOMMU, "vdo0_smi_iommu", "top_vpp", 13),
+ GATE_VDO0_1(CLK_VDO0_SMI_LARB, "vdo0_smi_larb", "top_vpp", 14),
+ GATE_VDO0_1(CLK_VDO0_SMI_RSI, "vdo0_smi_rsi", "top_vpp", 15),
+ /* VDO0_2 */
+ GATE_VDO0_2(CLK_VDO0_DSI0_DSI, "vdo0_dsi0_dsi", "top_dsi_occ", 0),
+ GATE_VDO0_2(CLK_VDO0_DSI1_DSI, "vdo0_dsi1_dsi", "top_dsi_occ", 8),
+ GATE_VDO0_2_FLAGS(CLK_VDO0_DP_INTF0_DP_INTF, "vdo0_dp_intf0_dp_intf",
+ "top_edp", 16, CLK_SET_RATE_PARENT),
+};
+
+static const struct mtk_clk_desc vdo0_desc = {
+ .clks = vdo0_clks,
+ .num_clks = ARRAY_SIZE(vdo0_clks),
+};
+
+static const struct platform_device_id clk_mt8188_vdo0_id_table[] = {
+ { .name = "clk-mt8188-vdo0", .driver_data = (kernel_ulong_t)&vdo0_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8188_vdo0_id_table);
+
+static struct platform_driver clk_mt8188_vdo0_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8188-vdo0",
+ },
+ .id_table = clk_mt8188_vdo0_id_table,
+};
+module_platform_driver(clk_mt8188_vdo0_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Video Output 0 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-vdo1.c b/drivers/clk/mediatek/clk-mt8188-vdo1.c
new file mode 100644
index 000000000000..f715d45e545e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-vdo1.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vdo1_0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs vdo1_1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs vdo1_2_cg_regs = {
+ .set_ofs = 0x124,
+ .clr_ofs = 0x128,
+ .sta_ofs = 0x120,
+};
+
+static const struct mtk_gate_regs vdo1_3_cg_regs = {
+ .set_ofs = 0x134,
+ .clr_ofs = 0x138,
+ .sta_ofs = 0x130,
+};
+
+static const struct mtk_gate_regs vdo1_4_cg_regs = {
+ .set_ofs = 0x144,
+ .clr_ofs = 0x148,
+ .sta_ofs = 0x140,
+};
+
+static const struct mtk_gate_regs vdo1_5_cg_regs = {
+ .set_ofs = 0x400,
+ .clr_ofs = 0x400,
+ .sta_ofs = 0x400,
+};
+
+#define GATE_VDO1_0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO1_1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO1_2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO1_3(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_3_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO1_3_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &vdo1_3_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flags)
+
+#define GATE_VDO1_4(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_4_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VDO1_5(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_5_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate vdo1_clks[] = {
+ /* VDO1_0 */
+ GATE_VDO1_0(CLK_VDO1_SMI_LARB2, "vdo1_smi_larb2", "top_vpp", 0),
+ GATE_VDO1_0(CLK_VDO1_SMI_LARB3, "vdo1_smi_larb3", "top_vpp", 1),
+ GATE_VDO1_0(CLK_VDO1_GALS, "vdo1_gals", "top_vpp", 2),
+ GATE_VDO1_0(CLK_VDO1_FAKE_ENG0, "vdo1_fake_eng0", "top_vpp", 3),
+ GATE_VDO1_0(CLK_VDO1_FAKE_ENG1, "vdo1_fake_eng1", "top_vpp", 4),
+ GATE_VDO1_0(CLK_VDO1_MDP_RDMA0, "vdo1_mdp_rdma0", "top_vpp", 5),
+ GATE_VDO1_0(CLK_VDO1_MDP_RDMA1, "vdo1_mdp_rdma1", "top_vpp", 6),
+ GATE_VDO1_0(CLK_VDO1_MDP_RDMA2, "vdo1_mdp_rdma2", "top_vpp", 7),
+ GATE_VDO1_0(CLK_VDO1_MDP_RDMA3, "vdo1_mdp_rdma3", "top_vpp", 8),
+ GATE_VDO1_0(CLK_VDO1_VPP_MERGE0, "vdo1_vpp_merge0", "top_vpp", 9),
+ GATE_VDO1_0(CLK_VDO1_VPP_MERGE1, "vdo1_vpp_merge1", "top_vpp", 10),
+ GATE_VDO1_0(CLK_VDO1_VPP_MERGE2, "vdo1_vpp_merge2", "top_vpp", 11),
+ /* VDO1_1 */
+ GATE_VDO1_1(CLK_VDO1_VPP_MERGE3, "vdo1_vpp_merge3", "top_vpp", 0),
+ GATE_VDO1_1(CLK_VDO1_VPP_MERGE4, "vdo1_vpp_merge4", "top_vpp", 1),
+ GATE_VDO1_1(CLK_VDO1_VPP2_TO_VDO1_DL_ASYNC, "vdo1_vpp2_to_vdo1_dl_async", "top_vpp", 2),
+ GATE_VDO1_1(CLK_VDO1_VPP3_TO_VDO1_DL_ASYNC, "vdo1_vpp3_to_vdo1_dl_async", "top_vpp", 3),
+ GATE_VDO1_1(CLK_VDO1_DISP_MUTEX, "vdo1_disp_mutex", "top_vpp", 4),
+ GATE_VDO1_1(CLK_VDO1_MDP_RDMA4, "vdo1_mdp_rdma4", "top_vpp", 5),
+ GATE_VDO1_1(CLK_VDO1_MDP_RDMA5, "vdo1_mdp_rdma5", "top_vpp", 6),
+ GATE_VDO1_1(CLK_VDO1_MDP_RDMA6, "vdo1_mdp_rdma6", "top_vpp", 7),
+ GATE_VDO1_1(CLK_VDO1_MDP_RDMA7, "vdo1_mdp_rdma7", "top_vpp", 8),
+ GATE_VDO1_1(CLK_VDO1_DP_INTF0_MMCK, "vdo1_dp_intf0_mmck", "top_vpp", 9),
+ GATE_VDO1_1(CLK_VDO1_DPI0_MM, "vdo1_dpi0_mm_ck", "top_vpp", 10),
+ GATE_VDO1_1(CLK_VDO1_DPI1_MM, "vdo1_dpi1_mm_ck", "top_vpp", 11),
+ GATE_VDO1_1(CLK_VDO1_MERGE0_DL_ASYNC, "vdo1_merge0_dl_async", "top_vpp", 13),
+ GATE_VDO1_1(CLK_VDO1_MERGE1_DL_ASYNC, "vdo1_merge1_dl_async", "top_vpp", 14),
+ GATE_VDO1_1(CLK_VDO1_MERGE2_DL_ASYNC, "vdo1_merge2_dl_async", "top_vpp", 15),
+ GATE_VDO1_1(CLK_VDO1_MERGE3_DL_ASYNC, "vdo1_merge3_dl_async", "top_vpp", 16),
+ GATE_VDO1_1(CLK_VDO1_MERGE4_DL_ASYNC, "vdo1_merge4_dl_async", "top_vpp", 17),
+ GATE_VDO1_1(CLK_VDO1_DSC_VDO1_DL_ASYNC, "vdo1_dsc_vdo1_dl_async", "top_vpp", 18),
+ GATE_VDO1_1(CLK_VDO1_MERGE_VDO1_DL_ASYNC, "vdo1_merge_vdo1_dl_async", "top_vpp", 19),
+ GATE_VDO1_1(CLK_VDO1_PADDING0, "vdo1_padding0", "top_vpp", 20),
+ GATE_VDO1_1(CLK_VDO1_PADDING1, "vdo1_padding1", "top_vpp", 21),
+ GATE_VDO1_1(CLK_VDO1_PADDING2, "vdo1_padding2", "top_vpp", 22),
+ GATE_VDO1_1(CLK_VDO1_PADDING3, "vdo1_padding3", "top_vpp", 23),
+ GATE_VDO1_1(CLK_VDO1_PADDING4, "vdo1_padding4", "top_vpp", 24),
+ GATE_VDO1_1(CLK_VDO1_PADDING5, "vdo1_padding5", "top_vpp", 25),
+ GATE_VDO1_1(CLK_VDO1_PADDING6, "vdo1_padding6", "top_vpp", 26),
+ GATE_VDO1_1(CLK_VDO1_PADDING7, "vdo1_padding7", "top_vpp", 27),
+ GATE_VDO1_1(CLK_VDO1_DISP_RSZ0, "vdo1_disp_rsz0", "top_vpp", 28),
+ GATE_VDO1_1(CLK_VDO1_DISP_RSZ1, "vdo1_disp_rsz1", "top_vpp", 29),
+ GATE_VDO1_1(CLK_VDO1_DISP_RSZ2, "vdo1_disp_rsz2", "top_vpp", 30),
+ GATE_VDO1_1(CLK_VDO1_DISP_RSZ3, "vdo1_disp_rsz3", "top_vpp", 31),
+ /* VDO1_2 */
+ GATE_VDO1_2(CLK_VDO1_HDR_VDO_FE0, "vdo1_hdr_vdo_fe0", "top_vpp", 0),
+ GATE_VDO1_2(CLK_VDO1_HDR_GFX_FE0, "vdo1_hdr_gfx_fe0", "top_vpp", 1),
+ GATE_VDO1_2(CLK_VDO1_HDR_VDO_BE, "vdo1_hdr_vdo_be", "top_vpp", 2),
+ GATE_VDO1_2(CLK_VDO1_HDR_VDO_FE1, "vdo1_hdr_vdo_fe1", "top_vpp", 16),
+ GATE_VDO1_2(CLK_VDO1_HDR_GFX_FE1, "vdo1_hdr_gfx_fe1", "top_vpp", 17),
+ GATE_VDO1_2(CLK_VDO1_DISP_MIXER, "vdo1_disp_mixer", "top_vpp", 18),
+ GATE_VDO1_2(CLK_VDO1_HDR_VDO_FE0_DL_ASYNC, "vdo1_hdr_vdo_fe0_dl_async", "top_vpp", 19),
+ GATE_VDO1_2(CLK_VDO1_HDR_VDO_FE1_DL_ASYNC, "vdo1_hdr_vdo_fe1_dl_async", "top_vpp", 20),
+ GATE_VDO1_2(CLK_VDO1_HDR_GFX_FE0_DL_ASYNC, "vdo1_hdr_gfx_fe0_dl_async", "top_vpp", 21),
+ GATE_VDO1_2(CLK_VDO1_HDR_GFX_FE1_DL_ASYNC, "vdo1_hdr_gfx_fe1_dl_async", "top_vpp", 22),
+ GATE_VDO1_2(CLK_VDO1_HDR_VDO_BE_DL_ASYNC, "vdo1_hdr_vdo_be_dl_async", "top_vpp", 23),
+ /* VDO1_3 */
+ GATE_VDO1_3(CLK_VDO1_DPI0, "vdo1_dpi0_ck", "top_vpp", 0),
+ GATE_VDO1_3(CLK_VDO1_DISP_MONITOR_DPI0, "vdo1_disp_monitor_dpi0_ck", "top_vpp", 1),
+ GATE_VDO1_3(CLK_VDO1_DPI1, "vdo1_dpi1_ck", "top_vpp", 8),
+ GATE_VDO1_3(CLK_VDO1_DISP_MONITOR_DPI1, "vdo1_disp_monitor_dpi1_ck", "top_vpp", 9),
+ GATE_VDO1_3_FLAGS(CLK_VDO1_DPINTF, "vdo1_dpintf", "top_dp", 16, CLK_SET_RATE_PARENT),
+ GATE_VDO1_3(CLK_VDO1_DISP_MONITOR_DPINTF, "vdo1_disp_monitor_dpintf_ck", "top_vpp", 17),
+ /* VDO1_4 */
+ GATE_VDO1_4(CLK_VDO1_26M_SLOW, "vdo1_26m_slow_ck", "clk26m", 8),
+ /* VDO1_5 */
+ GATE_VDO1_5(CLK_VDO1_DPI1_HDMI, "vdo1_dpi1_hdmi", "hdmi_txpll", 0),
+};
+
+static const struct mtk_clk_desc vdo1_desc = {
+ .clks = vdo1_clks,
+ .num_clks = ARRAY_SIZE(vdo1_clks),
+};
+
+static const struct platform_device_id clk_mt8188_vdo1_id_table[] = {
+ { .name = "clk-mt8188-vdo1", .driver_data = (kernel_ulong_t)&vdo1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8188_vdo1_id_table);
+
+static struct platform_driver clk_mt8188_vdo1_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8188-vdo1",
+ },
+ .id_table = clk_mt8188_vdo1_id_table,
+};
+module_platform_driver(clk_mt8188_vdo1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Video Output 1 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-venc.c b/drivers/clk/mediatek/clk-mt8188-venc.c
new file mode 100644
index 000000000000..01e971545506
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-venc.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs venc1_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_VENC1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc1_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate venc1_clks[] = {
+ GATE_VENC1(CLK_VENC1_LARB, "venc1_larb", "top_venc", 0),
+ GATE_VENC1(CLK_VENC1_VENC, "venc1_venc", "top_venc", 4),
+ GATE_VENC1(CLK_VENC1_JPGENC, "venc1_jpgenc", "top_venc", 8),
+ GATE_VENC1(CLK_VENC1_JPGDEC, "venc1_jpgdec", "top_venc", 12),
+ GATE_VENC1(CLK_VENC1_JPGDEC_C1, "venc1_jpgdec_c1", "top_venc", 16),
+ GATE_VENC1(CLK_VENC1_GALS, "venc1_gals", "top_venc", 28),
+ GATE_VENC1(CLK_VENC1_GALS_SRAM, "venc1_gals_sram", "top_venc", 31),
+};
+
+static const struct mtk_clk_desc venc1_desc = {
+ .clks = venc1_clks,
+ .num_clks = ARRAY_SIZE(venc1_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_venc1[] = {
+ { .compatible = "mediatek,mt8188-vencsys", .data = &venc1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_venc1);
+
+static struct platform_driver clk_mt8188_venc1_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-venc1",
+ .of_match_table = of_match_clk_mt8188_venc1,
+ },
+};
+module_platform_driver(clk_mt8188_venc1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-vpp0.c b/drivers/clk/mediatek/clk-mt8188-vpp0.c
new file mode 100644
index 000000000000..cd2579b7b9c3
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-vpp0.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vpp0_0_cg_regs = {
+ .set_ofs = 0x24,
+ .clr_ofs = 0x28,
+ .sta_ofs = 0x20,
+};
+
+static const struct mtk_gate_regs vpp0_1_cg_regs = {
+ .set_ofs = 0x30,
+ .clr_ofs = 0x34,
+ .sta_ofs = 0x2c,
+};
+
+static const struct mtk_gate_regs vpp0_2_cg_regs = {
+ .set_ofs = 0x3c,
+ .clr_ofs = 0x40,
+ .sta_ofs = 0x38,
+};
+
+#define GATE_VPP0_0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vpp0_0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VPP0_1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vpp0_1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VPP0_2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vpp0_2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate vpp0_clks[] = {
+ /* VPP0_0 */
+ GATE_VPP0_0(CLK_VPP0_MDP_FG, "vpp0_mdp_fg", "top_vpp", 1),
+ GATE_VPP0_0(CLK_VPP0_STITCH, "vpp0_stitch", "top_vpp", 2),
+ GATE_VPP0_0(CLK_VPP0_PADDING, "vpp0_padding", "top_vpp", 7),
+ GATE_VPP0_0(CLK_VPP0_MDP_TCC, "vpp0_mdp_tcc", "top_vpp", 8),
+ GATE_VPP0_0(CLK_VPP0_WARP0_ASYNC_TX, "vpp0_warp0_async_tx", "top_vpp", 10),
+ GATE_VPP0_0(CLK_VPP0_WARP1_ASYNC_TX, "vpp0_warp1_async_tx", "top_vpp", 11),
+ GATE_VPP0_0(CLK_VPP0_MUTEX, "vpp0_mutex", "top_vpp", 13),
+ GATE_VPP0_0(CLK_VPP02VPP1_RELAY, "vpp02vpp1_relay", "top_vpp", 14),
+ GATE_VPP0_0(CLK_VPP0_VPP12VPP0_ASYNC, "vpp0_vpp12vpp0_async", "top_vpp", 15),
+ GATE_VPP0_0(CLK_VPP0_MMSYSRAM_TOP, "vpp0_mmsysram_top", "top_vpp", 16),
+ GATE_VPP0_0(CLK_VPP0_MDP_AAL, "vpp0_mdp_aal", "top_vpp", 17),
+ GATE_VPP0_0(CLK_VPP0_MDP_RSZ, "vpp0_mdp_rsz", "top_vpp", 18),
+ /* VPP0_1 */
+ GATE_VPP0_1(CLK_VPP0_SMI_COMMON_MMSRAM, "vpp0_smi_common_mmsram", "top_vpp", 0),
+ GATE_VPP0_1(CLK_VPP0_GALS_VDO0_LARB0_MMSRAM, "vpp0_gals_vdo0_larb0_mmsram", "top_vpp", 1),
+ GATE_VPP0_1(CLK_VPP0_GALS_VDO0_LARB1_MMSRAM, "vpp0_gals_vdo0_larb1_mmsram", "top_vpp", 2),
+ GATE_VPP0_1(CLK_VPP0_GALS_VENCSYS_MMSRAM, "vpp0_gals_vencsys_mmsram", "top_vpp", 3),
+ GATE_VPP0_1(CLK_VPP0_GALS_VENCSYS_CORE1_MMSRAM,
+ "vpp0_gals_vencsys_core1_mmsram", "top_vpp", 4),
+ GATE_VPP0_1(CLK_VPP0_GALS_INFRA_MMSRAM, "vpp0_gals_infra_mmsram", "top_vpp", 5),
+ GATE_VPP0_1(CLK_VPP0_GALS_CAMSYS_MMSRAM, "vpp0_gals_camsys_mmsram", "top_vpp", 6),
+ GATE_VPP0_1(CLK_VPP0_GALS_VPP1_LARB5_MMSRAM, "vpp0_gals_vpp1_larb5_mmsram", "top_vpp", 7),
+ GATE_VPP0_1(CLK_VPP0_GALS_VPP1_LARB6_MMSRAM, "vpp0_gals_vpp1_larb6_mmsram", "top_vpp", 8),
+ GATE_VPP0_1(CLK_VPP0_SMI_REORDER_MMSRAM, "vpp0_smi_reorder_mmsram", "top_vpp", 9),
+ GATE_VPP0_1(CLK_VPP0_SMI_IOMMU, "vpp0_smi_iommu", "top_vpp", 10),
+ GATE_VPP0_1(CLK_VPP0_GALS_IMGSYS_CAMSYS, "vpp0_gals_imgsys_camsys", "top_vpp", 11),
+ GATE_VPP0_1(CLK_VPP0_MDP_RDMA, "vpp0_mdp_rdma", "top_vpp", 12),
+ GATE_VPP0_1(CLK_VPP0_MDP_WROT, "vpp0_mdp_wrot", "top_vpp", 13),
+ GATE_VPP0_1(CLK_VPP0_GALS_EMI0_EMI1, "vpp0_gals_emi0_emi1", "top_vpp", 16),
+ GATE_VPP0_1(CLK_VPP0_SMI_SUB_COMMON_REORDER, "vpp0_smi_sub_common_reorder", "top_vpp", 17),
+ GATE_VPP0_1(CLK_VPP0_SMI_RSI, "vpp0_smi_rsi", "top_vpp", 18),
+ GATE_VPP0_1(CLK_VPP0_SMI_COMMON_LARB4, "vpp0_smi_common_larb4", "top_vpp", 19),
+ GATE_VPP0_1(CLK_VPP0_GALS_VDEC_VDEC_CORE1, "vpp0_gals_vdec_vdec_core1", "top_vpp", 20),
+ GATE_VPP0_1(CLK_VPP0_GALS_VPP1_WPESYS, "vpp0_gals_vpp1_wpesys", "top_vpp", 21),
+ GATE_VPP0_1(CLK_VPP0_GALS_VDO0_VDO1_VENCSYS_CORE1,
+ "vpp0_gals_vdo0_vdo1_vencsys_core1", "top_vpp", 22),
+ GATE_VPP0_1(CLK_VPP0_FAKE_ENG, "vpp0_fake_eng", "top_vpp", 23),
+ GATE_VPP0_1(CLK_VPP0_MDP_HDR, "vpp0_mdp_hdr", "top_vpp", 24),
+ GATE_VPP0_1(CLK_VPP0_MDP_TDSHP, "vpp0_mdp_tdshp", "top_vpp", 25),
+ GATE_VPP0_1(CLK_VPP0_MDP_COLOR, "vpp0_mdp_color", "top_vpp", 26),
+ GATE_VPP0_1(CLK_VPP0_MDP_OVL, "vpp0_mdp_ovl", "top_vpp", 27),
+ GATE_VPP0_1(CLK_VPP0_DSIP_RDMA, "vpp0_dsip_rdma", "top_vpp", 28),
+ GATE_VPP0_1(CLK_VPP0_DISP_WDMA, "vpp0_disp_wdma", "top_vpp", 29),
+ GATE_VPP0_1(CLK_VPP0_MDP_HMS, "vpp0_mdp_hms", "top_vpp", 30),
+ /* VPP0_2 */
+ GATE_VPP0_2(CLK_VPP0_WARP0_RELAY, "vpp0_warp0_relay", "top_wpe_vpp", 0),
+ GATE_VPP0_2(CLK_VPP0_WARP0_ASYNC, "vpp0_warp0_async", "top_wpe_vpp", 1),
+ GATE_VPP0_2(CLK_VPP0_WARP1_RELAY, "vpp0_warp1_relay", "top_wpe_vpp", 2),
+ GATE_VPP0_2(CLK_VPP0_WARP1_ASYNC, "vpp0_warp1_async", "top_wpe_vpp", 3),
+};
+
+static const struct mtk_clk_desc vpp0_desc = {
+ .clks = vpp0_clks,
+ .num_clks = ARRAY_SIZE(vpp0_clks),
+};
+
+static const struct platform_device_id clk_mt8188_vpp0_id_table[] = {
+ { .name = "clk-mt8188-vpp0", .driver_data = (kernel_ulong_t)&vpp0_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8188_vpp0_id_table);
+
+static struct platform_driver clk_mt8188_vpp0_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8188-vpp0",
+ },
+ .id_table = clk_mt8188_vpp0_id_table,
+};
+module_platform_driver(clk_mt8188_vpp0_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Video Processing Pipe 0 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-vpp1.c b/drivers/clk/mediatek/clk-mt8188-vpp1.c
new file mode 100644
index 000000000000..0e1bd8306e8a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-vpp1.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vpp1_0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs vpp1_1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+#define GATE_VPP1_0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vpp1_0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_VPP1_1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vpp1_1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate vpp1_clks[] = {
+ /* VPP1_0 */
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_OVL, "vpp1_svpp1_mdp_ovl", "top_vpp", 0),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_TCC, "vpp1_svpp1_mdp_tcc", "top_vpp", 1),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_WROT, "vpp1_svpp1_mdp_wrot", "top_vpp", 2),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_VPP_PAD, "vpp1_svpp1_vpp_pad", "top_vpp", 3),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_MDP_WROT, "vpp1_svpp2_mdp_wrot", "top_vpp", 4),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_VPP_PAD, "vpp1_svpp2_vpp_pad", "top_vpp", 5),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_MDP_WROT, "vpp1_svpp3_mdp_wrot", "top_vpp", 6),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_VPP_PAD, "vpp1_svpp3_vpp_pad", "top_vpp", 7),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_RDMA, "vpp1_svpp1_mdp_rdma", "top_vpp", 8),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_FG, "vpp1_svpp1_mdp_fg", "top_vpp", 9),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_MDP_RDMA, "vpp1_svpp2_mdp_rdma", "top_vpp", 10),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_MDP_FG, "vpp1_svpp2_mdp_fg", "top_vpp", 11),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_MDP_RDMA, "vpp1_svpp3_mdp_rdma", "top_vpp", 12),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_MDP_FG, "vpp1_svpp3_mdp_fg", "top_vpp", 13),
+ GATE_VPP1_0(CLK_VPP1_VPP_SPLIT, "vpp1_vpp_split", "top_vpp", 14),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_VDO0_DL_RELAY, "vpp1_svpp2_vdo0_dl_relay", "top_vpp", 15),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_RSZ, "vpp1_svpp1_mdp_rsz", "top_vpp", 16),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_TDSHP, "vpp1_svpp1_mdp_tdshp", "top_vpp", 17),
+ GATE_VPP1_0(CLK_VPP1_SVPP1_MDP_COLOR, "vpp1_svpp1_mdp_color", "top_vpp", 18),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_VDO1_DL_RELAY, "vpp1_svpp3_vdo1_dl_relay", "top_vpp", 19),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_MDP_RSZ, "vpp1_svpp2_mdp_rsz", "top_vpp", 20),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_VPP_MERGE, "vpp1_svpp2_vpp_merge", "top_vpp", 21),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_MDP_TDSHP, "vpp1_svpp2_mdp_tdshp", "top_vpp", 22),
+ GATE_VPP1_0(CLK_VPP1_SVPP2_MDP_COLOR, "vpp1_svpp2_mdp_color", "top_vpp", 23),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_MDP_RSZ, "vpp1_svpp3_mdp_rsz", "top_vpp", 24),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_VPP_MERGE, "vpp1_svpp3_vpp_merge", "top_vpp", 25),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_MDP_TDSHP, "vpp1_svpp3_mdp_tdshp", "top_vpp", 26),
+ GATE_VPP1_0(CLK_VPP1_SVPP3_MDP_COLOR, "vpp1_svpp3_mdp_color", "top_vpp", 27),
+ GATE_VPP1_0(CLK_VPP1_GALS5, "vpp1_gals5", "top_vpp", 28),
+ GATE_VPP1_0(CLK_VPP1_GALS6, "vpp1_gals6", "top_vpp", 29),
+ GATE_VPP1_0(CLK_VPP1_LARB5, "vpp1_larb5", "top_vpp", 30),
+ GATE_VPP1_0(CLK_VPP1_LARB6, "vpp1_larb6", "top_vpp", 31),
+ /* VPP1_1 */
+ GATE_VPP1_1(CLK_VPP1_SVPP1_MDP_HDR, "vpp1_svpp1_mdp_hdr", "top_vpp", 0),
+ GATE_VPP1_1(CLK_VPP1_SVPP1_MDP_AAL, "vpp1_svpp1_mdp_aal", "top_vpp", 1),
+ GATE_VPP1_1(CLK_VPP1_SVPP2_MDP_HDR, "vpp1_svpp2_mdp_hdr", "top_vpp", 2),
+ GATE_VPP1_1(CLK_VPP1_SVPP2_MDP_AAL, "vpp1_svpp2_mdp_aal", "top_vpp", 3),
+ GATE_VPP1_1(CLK_VPP1_SVPP3_MDP_HDR, "vpp1_svpp3_mdp_hdr", "top_vpp", 4),
+ GATE_VPP1_1(CLK_VPP1_SVPP3_MDP_AAL, "vpp1_svpp3_mdp_aal", "top_vpp", 5),
+ GATE_VPP1_1(CLK_VPP1_DISP_MUTEX, "vpp1_disp_mutex", "top_vpp", 7),
+ GATE_VPP1_1(CLK_VPP1_SVPP2_VDO1_DL_RELAY, "vpp1_svpp2_vdo1_dl_relay", "top_vpp", 8),
+ GATE_VPP1_1(CLK_VPP1_SVPP3_VDO0_DL_RELAY, "vpp1_svpp3_vdo0_dl_relay", "top_vpp", 9),
+ GATE_VPP1_1(CLK_VPP1_VPP0_DL_ASYNC, "vpp1_vpp0_dl_async", "top_vpp", 10),
+ GATE_VPP1_1(CLK_VPP1_VPP0_DL1_RELAY, "vpp1_vpp0_dl1_relay", "top_vpp", 11),
+ GATE_VPP1_1(CLK_VPP1_LARB5_FAKE_ENG, "vpp1_larb5_fake_eng", "top_vpp", 12),
+ GATE_VPP1_1(CLK_VPP1_LARB6_FAKE_ENG, "vpp1_larb6_fake_eng", "top_vpp", 13),
+ GATE_VPP1_1(CLK_VPP1_HDMI_META, "vpp1_hdmi_meta", "top_vpp", 16),
+ GATE_VPP1_1(CLK_VPP1_VPP_SPLIT_HDMI, "vpp1_vpp_split_hdmi", "top_vpp", 17),
+ GATE_VPP1_1(CLK_VPP1_DGI_IN, "vpp1_dgi_in", "top_vpp", 18),
+ GATE_VPP1_1(CLK_VPP1_DGI_OUT, "vpp1_dgi_out", "top_vpp", 19),
+ GATE_VPP1_1(CLK_VPP1_VPP_SPLIT_DGI, "vpp1_vpp_split_dgi", "top_vpp", 20),
+ GATE_VPP1_1(CLK_VPP1_DL_CON_OCC, "vpp1_dl_con_occ", "top_vpp", 21),
+ GATE_VPP1_1(CLK_VPP1_VPP_SPLIT_26M, "vpp1_vpp_split_26m", "top_vpp", 26),
+};
+
+static const struct mtk_clk_desc vpp1_desc = {
+ .clks = vpp1_clks,
+ .num_clks = ARRAY_SIZE(vpp1_clks),
+};
+
+static const struct platform_device_id clk_mt8188_vpp1_id_table[] = {
+ { .name = "clk-mt8188-vpp1", .driver_data = (kernel_ulong_t)&vpp1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8188_vpp1_id_table);
+
+static struct platform_driver clk_mt8188_vpp1_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8188-vpp1",
+ },
+ .id_table = clk_mt8188_vpp1_id_table,
+};
+module_platform_driver(clk_mt8188_vpp1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Video Processing Pipe 1 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8188-wpe.c b/drivers/clk/mediatek/clk-mt8188-wpe.c
new file mode 100644
index 000000000000..d709bb1ee1d6
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8188-wpe.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Garmin Chang <garmin.chang@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/mediatek,mt8188-clk.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs wpe_top_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x0,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs wpe_vpp0_0_cg_regs = {
+ .set_ofs = 0x58,
+ .clr_ofs = 0x58,
+ .sta_ofs = 0x58,
+};
+
+static const struct mtk_gate_regs wpe_vpp0_1_cg_regs = {
+ .set_ofs = 0x5c,
+ .clr_ofs = 0x5c,
+ .sta_ofs = 0x5c,
+};
+
+#define GATE_WPE_TOP(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &wpe_top_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_WPE_VPP0_0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &wpe_vpp0_0_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_WPE_VPP0_1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &wpe_vpp0_1_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate wpe_top_clks[] = {
+ GATE_WPE_TOP(CLK_WPE_TOP_WPE_VPP0, "wpe_wpe_vpp0", "top_wpe_vpp", 16),
+ GATE_WPE_TOP(CLK_WPE_TOP_SMI_LARB7, "wpe_smi_larb7", "top_wpe_vpp", 18),
+ GATE_WPE_TOP(CLK_WPE_TOP_WPESYS_EVENT_TX, "wpe_wpesys_event_tx", "top_wpe_vpp", 20),
+ GATE_WPE_TOP(CLK_WPE_TOP_SMI_LARB7_PCLK_EN, "wpe_smi_larb7_p_en", "top_wpe_vpp", 24),
+};
+
+static const struct mtk_gate wpe_vpp0_clks[] = {
+ /* WPE_VPP00 */
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_VGEN, "wpe_vpp0_vgen", "top_img", 0),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_EXT, "wpe_vpp0_ext", "top_img", 1),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_VFC, "wpe_vpp0_vfc", "top_img", 2),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH0_TOP, "wpe_vpp0_cach0_top", "top_img", 3),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH0_DMA, "wpe_vpp0_cach0_dma", "top_img", 4),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH1_TOP, "wpe_vpp0_cach1_top", "top_img", 5),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH1_DMA, "wpe_vpp0_cach1_dma", "top_img", 6),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH2_TOP, "wpe_vpp0_cach2_top", "top_img", 7),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH2_DMA, "wpe_vpp0_cach2_dma", "top_img", 8),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH3_TOP, "wpe_vpp0_cach3_top", "top_img", 9),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_CACH3_DMA, "wpe_vpp0_cach3_dma", "top_img", 10),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_PSP, "wpe_vpp0_psp", "top_img", 11),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_PSP2, "wpe_vpp0_psp2", "top_img", 12),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_SYNC, "wpe_vpp0_sync", "top_img", 13),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_C24, "wpe_vpp0_c24", "top_img", 14),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_MDP_CROP, "wpe_vpp0_mdp_crop", "top_img", 15),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_ISP_CROP, "wpe_vpp0_isp_crop", "top_img", 16),
+ GATE_WPE_VPP0_0(CLK_WPE_VPP0_TOP, "wpe_vpp0_top", "top_img", 17),
+ /* WPE_VPP0_1 */
+ GATE_WPE_VPP0_1(CLK_WPE_VPP0_VECI, "wpe_vpp0_veci", "top_img", 0),
+ GATE_WPE_VPP0_1(CLK_WPE_VPP0_VEC2I, "wpe_vpp0_vec2i", "top_img", 1),
+ GATE_WPE_VPP0_1(CLK_WPE_VPP0_VEC3I, "wpe_vpp0_vec3i", "top_img", 2),
+ GATE_WPE_VPP0_1(CLK_WPE_VPP0_WPEO, "wpe_vpp0_wpeo", "top_img", 3),
+ GATE_WPE_VPP0_1(CLK_WPE_VPP0_MSKO, "wpe_vpp0_msko", "top_img", 4),
+};
+
+static const struct mtk_clk_desc wpe_top_desc = {
+ .clks = wpe_top_clks,
+ .num_clks = ARRAY_SIZE(wpe_top_clks),
+};
+
+static const struct mtk_clk_desc wpe_vpp0_desc = {
+ .clks = wpe_vpp0_clks,
+ .num_clks = ARRAY_SIZE(wpe_vpp0_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8188_wpe[] = {
+ { .compatible = "mediatek,mt8188-wpesys", .data = &wpe_top_desc },
+ { .compatible = "mediatek,mt8188-wpesys-vpp0", .data = &wpe_vpp0_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8188_wpe);
+
+static struct platform_driver clk_mt8188_wpe_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8188-wpe",
+ .of_match_table = of_match_clk_mt8188_wpe,
+ },
+};
+module_platform_driver(clk_mt8188_wpe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8188 Warp Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-apmixedsys.c b/drivers/clk/mediatek/clk-mt8192-apmixedsys.c
new file mode 100644
index 000000000000..0b66a27e4d5a
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8192-apmixedsys.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Chun-Jie Chen <chun-jie.chen@mediatek.com>
+ * Copyright (c) 2023 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8192-clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-fhctl.h"
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-pll.h"
+#include "clk-pllfh.h"
+
+static const struct mtk_gate_regs apmixed_cg_regs = {
+ .set_ofs = 0x14,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x14,
+};
+
+#define GATE_APMIXED(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &apmixed_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate apmixed_clks[] = {
+ GATE_APMIXED(CLK_APMIXED_MIPID26M, "mipid26m", "clk26m", 16),
+};
+
+#define MT8192_PLL_FMAX (3800UL * MHZ)
+#define MT8192_PLL_FMIN (1500UL * MHZ)
+#define MT8192_INTEGER_BITS 8
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
+ _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, _pcw_chg_reg, \
+ _en_reg, _pll_en_bit) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8192_PLL_FMAX, \
+ .fmin = MT8192_PLL_FMIN, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8192_INTEGER_BITS, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcw_chg_reg = _pcw_chg_reg, \
+ .en_reg = _en_reg, \
+ .pll_en_bit = _pll_en_bit, \
+ }
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
+ _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift) \
+ PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
+ _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, 0, 0, 0)
+
+static const struct mtk_pll_data plls[] = {
+ PLL_B(CLK_APMIXED_MAINPLL, "mainpll", 0x0340, 0x034c, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x0344, 24, 0, 0, 0, 0x0344, 0),
+ PLL_B(CLK_APMIXED_UNIVPLL, "univpll", 0x0308, 0x0314, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x030c, 24, 0, 0, 0, 0x030c, 0),
+ PLL(CLK_APMIXED_USBPLL, "usbpll", 0x03c4, 0x03cc, 0x00000000,
+ 0, 0, 22, 0x03c4, 24, 0, 0, 0, 0x03c4, 0, 0x03c4, 0x03cc, 2),
+ PLL_B(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0350, 0x035c, 0x00000000,
+ 0, 0, 22, 0x0354, 24, 0, 0, 0, 0x0354, 0),
+ PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0360, 0x036c, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x0364, 24, 0, 0, 0, 0x0364, 0),
+ PLL_B(CLK_APMIXED_ADSPPLL, "adsppll", 0x0370, 0x037c, 0xff000000,
+ HAVE_RST_BAR, BIT(23), 22, 0x0374, 24, 0, 0, 0, 0x0374, 0),
+ PLL_B(CLK_APMIXED_MFGPLL, "mfgpll", 0x0268, 0x0274, 0x00000000,
+ 0, 0, 22, 0x026c, 24, 0, 0, 0, 0x026c, 0),
+ PLL_B(CLK_APMIXED_TVDPLL, "tvdpll", 0x0380, 0x038c, 0x00000000,
+ 0, 0, 22, 0x0384, 24, 0, 0, 0, 0x0384, 0),
+ PLL_B(CLK_APMIXED_APLL1, "apll1", 0x0318, 0x0328, 0x00000000,
+ 0, 0, 32, 0x031c, 24, 0x0040, 0x000c, 0, 0x0320, 0),
+ PLL_B(CLK_APMIXED_APLL2, "apll2", 0x032c, 0x033c, 0x00000000,
+ 0, 0, 32, 0x0330, 24, 0, 0, 0, 0x0334, 0),
+};
+
+enum fh_pll_id {
+ FH_ARMPLL_LL,
+ FH_ARMPLL_BL0,
+ FH_ARMPLL_BL1,
+ FH_ARMPLL_BL2,
+ FH_ARMPLL_BL3,
+ FH_CCIPLL,
+ FH_MFGPLL,
+ FH_MEMPLL,
+ FH_MPLL,
+ FH_MMPLL,
+ FH_MAINPLL,
+ FH_MSDCPLL,
+ FH_ADSPPLL,
+ FH_APUPLL,
+ FH_TVDPLL,
+ FH_NR_FH,
+};
+
+#define FH(_pllid, _fhid, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fh_ver = FHCTL_PLLFH_V2, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = 0x6003c97, \
+ .slope1_value = 0x6003c97, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_MFGPLL, FH_MFGPLL, 0xb4),
+ FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0xf0),
+ FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x104),
+ FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x118),
+ FH(CLK_APMIXED_ADSPPLL, FH_ADSPPLL, 0x12c),
+ FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0x154),
+};
+
+static const struct of_device_id of_match_clk_mt8192_apmixed[] = {
+ { .compatible = "mediatek,mt8192-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_apmixed);
+
+static int clk_mt8192_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ const u8 *fhctl_node = "mediatek,mt8192-fhctl";
+ int r;
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+
+ r = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
+ if (r)
+ goto free_clk_data;
+
+ r = mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
+ ARRAY_SIZE(apmixed_clks), clk_data);
+ if (r)
+ goto unregister_plls;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_gates;
+
+ return r;
+
+unregister_gates:
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+unregister_plls:
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static void clk_mt8192_apmixed_remove(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8192_apmixed_drv = {
+ .driver = {
+ .name = "clk-mt8192-apmixed",
+ .of_match_table = of_match_clk_mt8192_apmixed,
+ },
+ .probe = clk_mt8192_apmixed_probe,
+ .remove = clk_mt8192_apmixed_remove,
+};
+module_platform_driver(clk_mt8192_apmixed_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 apmixed clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-aud.c b/drivers/clk/mediatek/clk-mt8192-aud.c
index 8c989bffd8c7..f3ebf8713fbb 100644
--- a/drivers/clk/mediatek/clk-mt8192-aud.c
+++ b/drivers/clk/mediatek/clk-mt8192-aud.c
@@ -77,42 +77,47 @@ static const struct mtk_gate aud_clks[] = {
GATE_AUD2(CLK_AUD_I2S9_B, "aud_i2s9_b", "audio_sel", 4),
};
+static const struct mtk_clk_desc aud_desc = {
+ .clks = aud_clks,
+ .num_clks = ARRAY_SIZE(aud_clks),
+};
+
static int clk_mt8192_aud_probe(struct platform_device *pdev)
{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
int r;
- clk_data = mtk_alloc_clk_data(CLK_AUD_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- r = mtk_clk_register_gates(node, aud_clks, ARRAY_SIZE(aud_clks), clk_data);
- if (r)
- return r;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ r = mtk_clk_simple_probe(pdev);
if (r)
return r;
r = devm_of_platform_populate(&pdev->dev);
if (r)
- of_clk_del_provider(node);
+ mtk_clk_simple_remove(pdev);
return r;
}
+static void clk_mt8192_aud_remove(struct platform_device *pdev)
+{
+ of_platform_depopulate(&pdev->dev);
+ mtk_clk_simple_remove(pdev);
+}
+
static const struct of_device_id of_match_clk_mt8192_aud[] = {
- { .compatible = "mediatek,mt8192-audsys", },
- {}
+ { .compatible = "mediatek,mt8192-audsys", .data = &aud_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_aud);
static struct platform_driver clk_mt8192_aud_drv = {
.probe = clk_mt8192_aud_probe,
+ .remove = clk_mt8192_aud_remove,
.driver = {
.name = "clk-mt8192-aud",
.of_match_table = of_match_clk_mt8192_aud,
},
};
+module_platform_driver(clk_mt8192_aud_drv);
-builtin_platform_driver(clk_mt8192_aud_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 audio clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-cam.c b/drivers/clk/mediatek/clk-mt8192-cam.c
index fc74cd80b4b0..891d2f88d9cf 100644
--- a/drivers/clk/mediatek/clk-mt8192-cam.c
+++ b/drivers/clk/mediatek/clk-mt8192-cam.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -95,13 +95,17 @@ static const struct of_device_id of_match_clk_mt8192_cam[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_cam);
static struct platform_driver clk_mt8192_cam_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-cam",
.of_match_table = of_match_clk_mt8192_cam,
},
};
+module_platform_driver(clk_mt8192_cam_drv);
-builtin_platform_driver(clk_mt8192_cam_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-img.c b/drivers/clk/mediatek/clk-mt8192-img.c
index 7ce3abe42577..c08e831125a5 100644
--- a/drivers/clk/mediatek/clk-mt8192-img.c
+++ b/drivers/clk/mediatek/clk-mt8192-img.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -58,13 +58,17 @@ static const struct of_device_id of_match_clk_mt8192_img[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_img);
static struct platform_driver clk_mt8192_img_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-img",
.of_match_table = of_match_clk_mt8192_img,
},
};
+module_platform_driver(clk_mt8192_img_drv);
-builtin_platform_driver(clk_mt8192_img_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-imp_iic_wrap.c b/drivers/clk/mediatek/clk-mt8192-imp_iic_wrap.c
index 700356ac6a58..0f9530d9263c 100644
--- a/drivers/clk/mediatek/clk-mt8192-imp_iic_wrap.c
+++ b/drivers/clk/mediatek/clk-mt8192-imp_iic_wrap.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -107,13 +107,17 @@ static const struct of_device_id of_match_clk_mt8192_imp_iic_wrap[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_imp_iic_wrap);
static struct platform_driver clk_mt8192_imp_iic_wrap_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-imp_iic_wrap",
.of_match_table = of_match_clk_mt8192_imp_iic_wrap,
},
};
+module_platform_driver(clk_mt8192_imp_iic_wrap_drv);
-builtin_platform_driver(clk_mt8192_imp_iic_wrap_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 I2C Wrapper clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-ipe.c b/drivers/clk/mediatek/clk-mt8192-ipe.c
index 730d91b64b3f..c932b8b20edc 100644
--- a/drivers/clk/mediatek/clk-mt8192-ipe.c
+++ b/drivers/clk/mediatek/clk-mt8192-ipe.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -45,13 +45,17 @@ static const struct of_device_id of_match_clk_mt8192_ipe[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_ipe);
static struct platform_driver clk_mt8192_ipe_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-ipe",
.of_match_table = of_match_clk_mt8192_ipe,
},
};
+module_platform_driver(clk_mt8192_ipe_drv);
-builtin_platform_driver(clk_mt8192_ipe_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 Image Processing Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-mdp.c b/drivers/clk/mediatek/clk-mt8192-mdp.c
index 93c87ae2f332..30334ebca864 100644
--- a/drivers/clk/mediatek/clk-mt8192-mdp.c
+++ b/drivers/clk/mediatek/clk-mt8192-mdp.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -70,13 +70,17 @@ static const struct of_device_id of_match_clk_mt8192_mdp[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_mdp);
static struct platform_driver clk_mt8192_mdp_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-mdp",
.of_match_table = of_match_clk_mt8192_mdp,
},
};
+module_platform_driver(clk_mt8192_mdp_drv);
-builtin_platform_driver(clk_mt8192_mdp_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 Multimedia Data Path clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-mfg.c b/drivers/clk/mediatek/clk-mt8192-mfg.c
index 3bbc7469f0e4..9d176659e8a2 100644
--- a/drivers/clk/mediatek/clk-mt8192-mfg.c
+++ b/drivers/clk/mediatek/clk-mt8192-mfg.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -18,8 +18,10 @@ static const struct mtk_gate_regs mfg_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_MFG(_id, _name, _parent, _shift) \
- GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+#define GATE_MFG(_id, _name, _parent, _shift) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr, \
+ CLK_SET_RATE_PARENT)
static const struct mtk_gate mfg_clks[] = {
GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_pll_sel", 0),
@@ -38,13 +40,17 @@ static const struct of_device_id of_match_clk_mt8192_mfg[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_mfg);
static struct platform_driver clk_mt8192_mfg_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-mfg",
.of_match_table = of_match_clk_mt8192_mfg,
},
};
+module_platform_driver(clk_mt8192_mfg_drv);
-builtin_platform_driver(clk_mt8192_mfg_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-mm.c b/drivers/clk/mediatek/clk-mt8192-mm.c
index 1be3ff4d407d..bda4406e1304 100644
--- a/drivers/clk/mediatek/clk-mt8192-mm.c
+++ b/drivers/clk/mediatek/clk-mt8192-mm.c
@@ -80,29 +80,26 @@ static const struct mtk_gate mm_clks[] = {
GATE_MM2(CLK_MM_32KHZ, "mm_32khz", "clk32k", 25),
};
-static int clk_mt8192_mm_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- r = mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), clk_data);
- if (r)
- return r;
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
-}
+static const struct platform_device_id clk_mt8192_mm_id_table[] = {
+ { .name = "clk-mt8192-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8192_mm_id_table);
static struct platform_driver clk_mt8192_mm_drv = {
- .probe = clk_mt8192_mm_probe,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8192-mm",
},
+ .id_table = clk_mt8192_mm_id_table,
};
+module_platform_driver(clk_mt8192_mm_drv);
-builtin_platform_driver(clk_mt8192_mm_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-msdc.c b/drivers/clk/mediatek/clk-mt8192-msdc.c
index 635f7a0b629a..04a66220f269 100644
--- a/drivers/clk/mediatek/clk-mt8192-msdc.c
+++ b/drivers/clk/mediatek/clk-mt8192-msdc.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -52,13 +52,17 @@ static const struct of_device_id of_match_clk_mt8192_msdc[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_msdc);
static struct platform_driver clk_mt8192_msdc_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-msdc",
.of_match_table = of_match_clk_mt8192_msdc,
},
};
+module_platform_driver(clk_mt8192_msdc_drv);
-builtin_platform_driver(clk_mt8192_msdc_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 MMC/SD Controller clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-scp_adsp.c b/drivers/clk/mediatek/clk-mt8192-scp_adsp.c
index 58725d79dd13..f9e4c16573e2 100644
--- a/drivers/clk/mediatek/clk-mt8192-scp_adsp.c
+++ b/drivers/clk/mediatek/clk-mt8192-scp_adsp.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -38,13 +38,17 @@ static const struct of_device_id of_match_clk_mt8192_scp_adsp[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_scp_adsp);
static struct platform_driver clk_mt8192_scp_adsp_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-scp_adsp",
.of_match_table = of_match_clk_mt8192_scp_adsp,
},
};
+module_platform_driver(clk_mt8192_scp_adsp_drv);
-builtin_platform_driver(clk_mt8192_scp_adsp_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 SCP AudioDSP clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-vdec.c b/drivers/clk/mediatek/clk-mt8192-vdec.c
index b1d95cfbf22a..9c10161807b2 100644
--- a/drivers/clk/mediatek/clk-mt8192-vdec.c
+++ b/drivers/clk/mediatek/clk-mt8192-vdec.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -82,13 +82,17 @@ static const struct of_device_id of_match_clk_mt8192_vdec[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_vdec);
static struct platform_driver clk_mt8192_vdec_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-vdec",
.of_match_table = of_match_clk_mt8192_vdec,
},
};
+module_platform_driver(clk_mt8192_vdec_drv);
-builtin_platform_driver(clk_mt8192_vdec_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192-venc.c b/drivers/clk/mediatek/clk-mt8192-venc.c
index c0d867bff09e..0b01e2b7f036 100644
--- a/drivers/clk/mediatek/clk-mt8192-venc.c
+++ b/drivers/clk/mediatek/clk-mt8192-venc.c
@@ -4,7 +4,7 @@
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
#include <linux/clk-provider.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -41,13 +41,17 @@ static const struct of_device_id of_match_clk_mt8192_venc[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192_venc);
static struct platform_driver clk_mt8192_venc_drv = {
.probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
.driver = {
.name = "clk-mt8192-venc",
.of_match_table = of_match_clk_mt8192_venc,
},
};
+module_platform_driver(clk_mt8192_venc_drv);
-builtin_platform_driver(clk_mt8192_venc_drv);
+MODULE_DESCRIPTION("MediaTek MT8192 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c
index ebbd2798d9a3..50b43807c60c 100644
--- a/drivers/clk/mediatek/clk-mt8192.c
+++ b/drivers/clk/mediatek/clk-mt8192.c
@@ -6,16 +6,13 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk-gate.h"
#include "clk-mtk.h"
#include "clk-mux.h"
-#include "clk-pll.h"
#include <dt-bindings/clock/mt8192-clk.h>
#include <dt-bindings/reset/mt8192-resets.h>
@@ -26,43 +23,39 @@ static const struct mtk_fixed_clk top_fixed_clks[] = {
FIXED_CLK(CLK_TOP_ULPOSC, "ulposc", NULL, 260000000),
};
-static const struct mtk_fixed_factor top_early_divs[] = {
- FACTOR(CLK_TOP_CSW_F26M_D2, "csw_f26m_d2", "clk26m", 1, 2),
-};
-
static const struct mtk_fixed_factor top_divs[] = {
- FACTOR(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3),
- FACTOR(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll_d4", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll_d4", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll_d4", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D4_D16, "mainpll_d4_d16", "mainpll_d4", 1, 16),
- FACTOR(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5),
- FACTOR(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll_d5", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6),
- FACTOR(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll_d6", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D6_D4, "mainpll_d6_d4", "mainpll_d6", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7),
- FACTOR(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll_d7", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
- FACTOR(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll_d4", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll_d4", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll_d4", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
- FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6),
- FACTOR(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll_d6", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll_d6", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll_d6", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D6_D16, "univpll_d6_d16", "univpll_d6", 1, 16),
- FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll_d4", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll_d4", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll_d4", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D16, "mainpll_d4_d16", "mainpll_d4", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll_d5", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll_d6", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6_D4, "mainpll_d6_d4", "mainpll_d6", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll_d7", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll_d4", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll_d4", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll_d4", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll_d6", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll_d6", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll_d6", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D16, "univpll_d6_d16", "univpll_d6", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7, 0),
FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1", 1, 2),
FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1, 4),
@@ -95,13 +88,14 @@ static const struct mtk_fixed_factor top_divs[] = {
FACTOR(CLK_TOP_OSC_D10, "osc_d10", "ulposc", 1, 10),
FACTOR(CLK_TOP_OSC_D16, "osc_d16", "ulposc", 1, 16),
FACTOR(CLK_TOP_OSC_D20, "osc_d20", "ulposc", 1, 20),
+ FACTOR(CLK_TOP_CSW_F26M_D2, "csw_f26m_d2", "clk26m", 1, 2),
FACTOR(CLK_TOP_ADSPPLL, "adsppll_ck", "adsppll", 1, 1),
- FACTOR(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13),
- FACTOR(CLK_TOP_UNIVPLL_192M_D2, "univpll_192m_d2", "univpll_192m", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16),
- FACTOR(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D2, "univpll_192m_d2", "univpll_192m", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32, 0),
};
static const char * const axi_parents[] = {
@@ -167,22 +161,7 @@ static const char * const mdp_parents[] = {
"mmpll_d5_d2"
};
-static const char * const img1_parents[] = {
- "clk26m",
- "univpll_d4",
- "tvdpll_ck",
- "mainpll_d4",
- "univpll_d5",
- "mmpll_d6",
- "univpll_d6",
- "mainpll_d6",
- "mmpll_d4_d2",
- "mainpll_d4_d2",
- "mmpll_d6_d2",
- "mmpll_d5_d2"
-};
-
-static const char * const img2_parents[] = {
+static const char * const img_parents[] = {
"clk26m",
"univpll_d4",
"tvdpll_ck",
@@ -280,61 +259,6 @@ static const char * const camtg_parents[] = {
"univpll_192m_d32"
};
-static const char * const camtg2_parents[] = {
- "clk26m",
- "univpll_192m_d8",
- "univpll_d6_d8",
- "univpll_192m_d4",
- "univpll_d6_d16",
- "csw_f26m_d2",
- "univpll_192m_d16",
- "univpll_192m_d32"
-};
-
-static const char * const camtg3_parents[] = {
- "clk26m",
- "univpll_192m_d8",
- "univpll_d6_d8",
- "univpll_192m_d4",
- "univpll_d6_d16",
- "csw_f26m_d2",
- "univpll_192m_d16",
- "univpll_192m_d32"
-};
-
-static const char * const camtg4_parents[] = {
- "clk26m",
- "univpll_192m_d8",
- "univpll_d6_d8",
- "univpll_192m_d4",
- "univpll_d6_d16",
- "csw_f26m_d2",
- "univpll_192m_d16",
- "univpll_192m_d32"
-};
-
-static const char * const camtg5_parents[] = {
- "clk26m",
- "univpll_192m_d8",
- "univpll_d6_d8",
- "univpll_192m_d4",
- "univpll_d6_d16",
- "csw_f26m_d2",
- "univpll_192m_d16",
- "univpll_192m_d32"
-};
-
-static const char * const camtg6_parents[] = {
- "clk26m",
- "univpll_192m_d8",
- "univpll_d6_d8",
- "univpll_192m_d4",
- "univpll_d6_d16",
- "csw_f26m_d2",
- "univpll_192m_d16",
- "univpll_192m_d32"
-};
-
static const char * const uart_parents[] = {
"clk26m",
"univpll_d6_d8"
@@ -362,15 +286,7 @@ static const char * const msdc50_0_parents[] = {
"univpll_d4_d2"
};
-static const char * const msdc30_1_parents[] = {
- "clk26m",
- "univpll_d6_d2",
- "mainpll_d6_d2",
- "mainpll_d7_d2",
- "msdcpll_d2"
-};
-
-static const char * const msdc30_2_parents[] = {
+static const char * const msdc30_parents[] = {
"clk26m",
"univpll_d6_d2",
"mainpll_d6_d2",
@@ -457,39 +373,6 @@ static const char * const seninf_parents[] = {
"univpll_d5"
};
-static const char * const seninf1_parents[] = {
- "clk26m",
- "univpll_d4_d4",
- "univpll_d6_d2",
- "univpll_d4_d2",
- "univpll_d7",
- "univpll_d6",
- "mmpll_d6",
- "univpll_d5"
-};
-
-static const char * const seninf2_parents[] = {
- "clk26m",
- "univpll_d4_d4",
- "univpll_d6_d2",
- "univpll_d4_d2",
- "univpll_d7",
- "univpll_d6",
- "mmpll_d6",
- "univpll_d5"
-};
-
-static const char * const seninf3_parents[] = {
- "clk26m",
- "univpll_d4_d4",
- "univpll_d6_d2",
- "univpll_d4_d2",
- "univpll_d7",
- "univpll_d6",
- "mmpll_d6",
- "univpll_d5"
-};
-
static const char * const tl_parents[] = {
"clk26m",
"univpll_192m_d2",
@@ -649,52 +532,7 @@ static const char * const sflash_parents[] = {
"univpll_d5_d8"
};
-static const char * const apll_i2s0_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s1_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s2_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s3_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s4_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s5_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s6_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s7_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s8_m_parents[] = {
- "aud_1_sel",
- "aud_2_sel"
-};
-
-static const char * const apll_i2s9_m_parents[] = {
+static const char * const apll_i2s_m_parents[] = {
"aud_1_sel",
"aud_2_sel"
};
@@ -709,24 +547,24 @@ static const struct mtk_mux top_mtk_muxes[] = {
/* CLK_CFG_0 */
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI_SEL, "axi_sel",
axi_parents, 0x010, 0x014, 0x018, 0, 3, 7, 0x004, 0,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SPM_SEL, "spm_sel",
spm_parents, 0x010, 0x014, 0x018, 8, 2, 15, 0x004, 1,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SCP_SEL, "scp_sel",
scp_parents, 0x010, 0x014, 0x018, 16, 3, 23, 0x004, 2),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_BUS_AXIMEM_SEL, "bus_aximem_sel",
bus_aximem_parents, 0x010, 0x014, 0x018, 24, 3, 31, 0x004, 3,
- CLK_IS_CRITICAL),
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/* CLK_CFG_1 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_SEL, "disp_sel",
disp_parents, 0x020, 0x024, 0x028, 0, 4, 7, 0x004, 4),
MUX_GATE_CLR_SET_UPD(CLK_TOP_MDP_SEL, "mdp_sel",
mdp_parents, 0x020, 0x024, 0x028, 8, 4, 15, 0x004, 5),
MUX_GATE_CLR_SET_UPD(CLK_TOP_IMG1_SEL, "img1_sel",
- img1_parents, 0x020, 0x024, 0x028, 16, 4, 23, 0x004, 6),
+ img_parents, 0x020, 0x024, 0x028, 16, 4, 23, 0x004, 6),
MUX_GATE_CLR_SET_UPD(CLK_TOP_IMG2_SEL, "img2_sel",
- img2_parents, 0x020, 0x024, 0x028, 24, 4, 31, 0x004, 7),
+ img_parents, 0x020, 0x024, 0x028, 24, 4, 31, 0x004, 7),
/* CLK_CFG_2 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_IPE_SEL, "ipe_sel",
ipe_parents, 0x030, 0x034, 0x038, 0, 4, 7, 0x004, 8),
@@ -747,29 +585,30 @@ static const struct mtk_mux top_mtk_muxes[] = {
camtg_parents, 0x050, 0x054, 0x058, 24, 3, 31, 0x004, 19),
/* CLK_CFG_5 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG2_SEL, "camtg2_sel",
- camtg2_parents, 0x060, 0x064, 0x068, 0, 3, 7, 0x004, 20),
+ camtg_parents, 0x060, 0x064, 0x068, 0, 3, 7, 0x004, 20),
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG3_SEL, "camtg3_sel",
- camtg3_parents, 0x060, 0x064, 0x068, 8, 3, 15, 0x004, 21),
+ camtg_parents, 0x060, 0x064, 0x068, 8, 3, 15, 0x004, 21),
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG4_SEL, "camtg4_sel",
- camtg4_parents, 0x060, 0x064, 0x068, 16, 3, 23, 0x004, 22),
+ camtg_parents, 0x060, 0x064, 0x068, 16, 3, 23, 0x004, 22),
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG5_SEL, "camtg5_sel",
- camtg5_parents, 0x060, 0x064, 0x068, 24, 3, 31, 0x004, 23),
+ camtg_parents, 0x060, 0x064, 0x068, 24, 3, 31, 0x004, 23),
/* CLK_CFG_6 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG6_SEL, "camtg6_sel",
- camtg6_parents, 0x070, 0x074, 0x078, 0, 3, 7, 0x004, 24),
+ camtg_parents, 0x070, 0x074, 0x078, 0, 3, 7, 0x004, 24),
MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel",
uart_parents, 0x070, 0x074, 0x078, 8, 1, 15, 0x004, 25),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel",
spi_parents, 0x070, 0x074, 0x078, 16, 2, 23, 0x004, 26),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel",
- msdc50_0_h_parents, 0x070, 0x074, 0x078, 24, 2, 31, 0x004, 27),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel",
+ msdc50_0_h_parents, 0x070, 0x074, 0x078, 24, 2,
+ 31, 0x004, 27, 0),
/* CLK_CFG_7 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
- msdc50_0_parents, 0x080, 0x084, 0x088, 0, 3, 7, 0x004, 28),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
- msdc30_1_parents, 0x080, 0x084, 0x088, 8, 3, 15, 0x004, 29),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel",
- msdc30_2_parents, 0x080, 0x084, 0x088, 16, 3, 23, 0x004, 30),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
+ msdc50_0_parents, 0x080, 0x084, 0x088, 0, 3, 7, 0x004, 28, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
+ msdc30_parents, 0x080, 0x084, 0x088, 8, 3, 15, 0x004, 29, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel",
+ msdc30_parents, 0x080, 0x084, 0x088, 16, 3, 23, 0x004, 30, 0),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_SEL, "audio_sel",
audio_parents, 0x080, 0x084, 0x088, 24, 2, 31, 0x008, 0),
/* CLK_CFG_8 */
@@ -796,12 +635,12 @@ static const struct mtk_mux top_mtk_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF_SEL, "seninf_sel",
seninf_parents, 0x0b0, 0x0b4, 0x0b8, 16, 3, 23, 0x008, 11),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF1_SEL, "seninf1_sel",
- seninf1_parents, 0x0b0, 0x0b4, 0x0b8, 24, 3, 31, 0x008, 12),
+ seninf_parents, 0x0b0, 0x0b4, 0x0b8, 24, 3, 31, 0x008, 12),
/* CLK_CFG_11 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF2_SEL, "seninf2_sel",
- seninf2_parents, 0x0c0, 0x0c4, 0x0c8, 0, 3, 7, 0x008, 13),
+ seninf_parents, 0x0c0, 0x0c4, 0x0c8, 0, 3, 7, 0x008, 13),
MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF3_SEL, "seninf3_sel",
- seninf3_parents, 0x0c0, 0x0c4, 0x0c8, 8, 3, 15, 0x008, 14),
+ seninf_parents, 0x0c0, 0x0c4, 0x0c8, 8, 3, 15, 0x008, 14),
MUX_GATE_CLR_SET_UPD(CLK_TOP_TL_SEL, "tl_sel",
tl_parents, 0x0c0, 0x0c4, 0x0c8, 16, 2, 23, 0x008, 15),
MUX_GATE_CLR_SET_UPD(CLK_TOP_DXCC_SEL, "dxcc_sel",
@@ -847,19 +686,17 @@ static const struct mtk_mux top_mtk_muxes[] = {
static struct mtk_composite top_muxes[] = {
/* CLK_AUDDIV_0 */
- MUX(CLK_TOP_APLL_I2S0_M_SEL, "apll_i2s0_m_sel", apll_i2s0_m_parents, 0x320, 16, 1),
- MUX(CLK_TOP_APLL_I2S1_M_SEL, "apll_i2s1_m_sel", apll_i2s1_m_parents, 0x320, 17, 1),
- MUX(CLK_TOP_APLL_I2S2_M_SEL, "apll_i2s2_m_sel", apll_i2s2_m_parents, 0x320, 18, 1),
- MUX(CLK_TOP_APLL_I2S3_M_SEL, "apll_i2s3_m_sel", apll_i2s3_m_parents, 0x320, 19, 1),
- MUX(CLK_TOP_APLL_I2S4_M_SEL, "apll_i2s4_m_sel", apll_i2s4_m_parents, 0x320, 20, 1),
- MUX(CLK_TOP_APLL_I2S5_M_SEL, "apll_i2s5_m_sel", apll_i2s5_m_parents, 0x320, 21, 1),
- MUX(CLK_TOP_APLL_I2S6_M_SEL, "apll_i2s6_m_sel", apll_i2s6_m_parents, 0x320, 22, 1),
- MUX(CLK_TOP_APLL_I2S7_M_SEL, "apll_i2s7_m_sel", apll_i2s7_m_parents, 0x320, 23, 1),
- MUX(CLK_TOP_APLL_I2S8_M_SEL, "apll_i2s8_m_sel", apll_i2s8_m_parents, 0x320, 24, 1),
- MUX(CLK_TOP_APLL_I2S9_M_SEL, "apll_i2s9_m_sel", apll_i2s9_m_parents, 0x320, 25, 1),
-};
-
-static const struct mtk_composite top_adj_divs[] = {
+ MUX(CLK_TOP_APLL_I2S0_M_SEL, "apll_i2s0_m_sel", apll_i2s_m_parents, 0x320, 16, 1),
+ MUX(CLK_TOP_APLL_I2S1_M_SEL, "apll_i2s1_m_sel", apll_i2s_m_parents, 0x320, 17, 1),
+ MUX(CLK_TOP_APLL_I2S2_M_SEL, "apll_i2s2_m_sel", apll_i2s_m_parents, 0x320, 18, 1),
+ MUX(CLK_TOP_APLL_I2S3_M_SEL, "apll_i2s3_m_sel", apll_i2s_m_parents, 0x320, 19, 1),
+ MUX(CLK_TOP_APLL_I2S4_M_SEL, "apll_i2s4_m_sel", apll_i2s_m_parents, 0x320, 20, 1),
+ MUX(CLK_TOP_APLL_I2S5_M_SEL, "apll_i2s5_m_sel", apll_i2s_m_parents, 0x320, 21, 1),
+ MUX(CLK_TOP_APLL_I2S6_M_SEL, "apll_i2s6_m_sel", apll_i2s_m_parents, 0x320, 22, 1),
+ MUX(CLK_TOP_APLL_I2S7_M_SEL, "apll_i2s7_m_sel", apll_i2s_m_parents, 0x320, 23, 1),
+ MUX(CLK_TOP_APLL_I2S8_M_SEL, "apll_i2s8_m_sel", apll_i2s_m_parents, 0x320, 24, 1),
+ MUX(CLK_TOP_APLL_I2S9_M_SEL, "apll_i2s9_m_sel", apll_i2s_m_parents, 0x320, 25, 1),
+ /* APLL_DIV */
DIV_GATE(CLK_TOP_APLL12_DIV0, "apll12_div0", "apll_i2s0_m_sel", 0x320, 0, 0x328, 8, 0),
DIV_GATE(CLK_TOP_APLL12_DIV1, "apll12_div1", "apll_i2s1_m_sel", 0x320, 1, 0x328, 8, 8),
DIV_GATE(CLK_TOP_APLL12_DIV2, "apll12_div2", "apll_i2s2_m_sel", 0x320, 2, 0x328, 8, 16),
@@ -873,19 +710,6 @@ static const struct mtk_composite top_adj_divs[] = {
DIV_GATE(CLK_TOP_APLL12_DIV9, "apll12_div9", "apll_i2s9_m_sel", 0x320, 10, 0x338, 8, 16),
};
-static const struct mtk_gate_regs apmixed_cg_regs = {
- .set_ofs = 0x14,
- .clr_ofs = 0x14,
- .sta_ofs = 0x14,
-};
-
-#define GATE_APMIXED(_id, _name, _parent, _shift) \
- GATE_MTK(_id, _name, _parent, &apmixed_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
-
-static const struct mtk_gate apmixed_clks[] = {
- GATE_APMIXED(CLK_APMIXED_MIPID26M, "mipid26m", "clk26m", 16),
-};
-
static const struct mtk_gate_regs infra0_cg_regs = {
.set_ofs = 0x80,
.clr_ofs = 0x84,
@@ -1139,243 +963,72 @@ static const struct mtk_clk_rst_desc clk_rst_desc = {
.rst_idx_map_nr = ARRAY_SIZE(infra_ao_idx_map),
};
-#define MT8192_PLL_FMAX (3800UL * MHZ)
-#define MT8192_PLL_FMIN (1500UL * MHZ)
-#define MT8192_INTEGER_BITS 8
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
- _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
- _pcw_reg, _pcw_shift, _pcw_chg_reg, \
- _en_reg, _pll_en_bit) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = _rst_bar_mask, \
- .fmax = MT8192_PLL_FMAX, \
- .fmin = MT8192_PLL_FMIN, \
- .pcwbits = _pcwbits, \
- .pcwibits = MT8192_INTEGER_BITS, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .tuner_en_reg = _tuner_en_reg, \
- .tuner_en_bit = _tuner_en_bit, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .pcw_chg_reg = _pcw_chg_reg, \
- .en_reg = _en_reg, \
- .pll_en_bit = _pll_en_bit, \
- }
-
-#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
- _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
- _pcw_reg, _pcw_shift) \
- PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
- _rst_bar_mask, _pcwbits, _pd_reg, _pd_shift, \
- _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
- _pcw_reg, _pcw_shift, 0, 0, 0)
-
-static const struct mtk_pll_data plls[] = {
- PLL_B(CLK_APMIXED_MAINPLL, "mainpll", 0x0340, 0x034c, 0xff000000,
- HAVE_RST_BAR, BIT(23), 22, 0x0344, 24, 0, 0, 0, 0x0344, 0),
- PLL_B(CLK_APMIXED_UNIVPLL, "univpll", 0x0308, 0x0314, 0xff000000,
- HAVE_RST_BAR, BIT(23), 22, 0x030c, 24, 0, 0, 0, 0x030c, 0),
- PLL(CLK_APMIXED_USBPLL, "usbpll", 0x03c4, 0x03cc, 0x00000000,
- 0, 0, 22, 0x03c4, 24, 0, 0, 0, 0x03c4, 0, 0x03c4, 0x03cc, 2),
- PLL_B(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0350, 0x035c, 0x00000000,
- 0, 0, 22, 0x0354, 24, 0, 0, 0, 0x0354, 0),
- PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0360, 0x036c, 0xff000000,
- HAVE_RST_BAR, BIT(23), 22, 0x0364, 24, 0, 0, 0, 0x0364, 0),
- PLL_B(CLK_APMIXED_ADSPPLL, "adsppll", 0x0370, 0x037c, 0xff000000,
- HAVE_RST_BAR, BIT(23), 22, 0x0374, 24, 0, 0, 0, 0x0374, 0),
- PLL_B(CLK_APMIXED_MFGPLL, "mfgpll", 0x0268, 0x0274, 0x00000000,
- 0, 0, 22, 0x026c, 24, 0, 0, 0, 0x026c, 0),
- PLL_B(CLK_APMIXED_TVDPLL, "tvdpll", 0x0380, 0x038c, 0x00000000,
- 0, 0, 22, 0x0384, 24, 0, 0, 0, 0x0384, 0),
- PLL_B(CLK_APMIXED_APLL1, "apll1", 0x0318, 0x0328, 0x00000000,
- 0, 0, 32, 0x031c, 24, 0x0040, 0x000c, 0, 0x0320, 0),
- PLL_B(CLK_APMIXED_APLL2, "apll2", 0x032c, 0x033c, 0x00000000,
- 0, 0, 32, 0x0330, 24, 0, 0, 0, 0x0334, 0),
-};
-
-static struct clk_hw_onecell_data *top_clk_data;
-
-static void clk_mt8192_top_init_early(struct device_node *node)
+/* Register mux notifier for MFG mux */
+static int clk_mt8192_reg_mfg_mux_notifier(struct device *dev, struct clk *clk)
{
+ struct mtk_mux_nb *mfg_mux_nb;
int i;
- top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
- if (!top_clk_data)
- return;
-
- for (i = 0; i < CLK_TOP_NR_CLK; i++)
- top_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER);
-
- mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs), top_clk_data);
-
- of_clk_add_hw_provider(node, of_clk_hw_onecell_get, top_clk_data);
-}
-
-CLK_OF_DECLARE_DRIVER(mt8192_topckgen, "mediatek,mt8192-topckgen",
- clk_mt8192_top_init_early);
-
-static int clk_mt8192_top_probe(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- int r;
- void __iomem *base;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), top_clk_data);
- mtk_clk_register_factors(top_early_divs, ARRAY_SIZE(top_early_divs), top_clk_data);
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
- mtk_clk_register_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), node, &mt8192_clk_lock,
- top_clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, &mt8192_clk_lock,
- top_clk_data);
- mtk_clk_register_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), base, &mt8192_clk_lock,
- top_clk_data);
- r = mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), top_clk_data);
- if (r)
- return r;
-
- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
- top_clk_data);
-}
-
-static int clk_mt8192_infra_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
- if (!clk_data)
+ mfg_mux_nb = devm_kzalloc(dev, sizeof(*mfg_mux_nb), GFP_KERNEL);
+ if (!mfg_mux_nb)
return -ENOMEM;
- r = mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), clk_data);
- if (r)
- goto free_clk_data;
-
- r = mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc);
- if (r)
- goto free_clk_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto free_clk_data;
-
- return r;
-
-free_clk_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
-
-static int clk_mt8192_peri_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- r = mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), clk_data);
- if (r)
- goto free_clk_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto free_clk_data;
+ for (i = 0; i < ARRAY_SIZE(top_mtk_muxes); i++)
+ if (top_mtk_muxes[i].id == CLK_TOP_MFG_PLL_SEL)
+ break;
+ if (i == ARRAY_SIZE(top_mtk_muxes))
+ return -EINVAL;
- return r;
+ mfg_mux_nb->ops = top_mtk_muxes[i].ops;
+ mfg_mux_nb->bypass_index = 0; /* Bypass to 26M crystal */
-free_clk_data:
- mtk_free_clk_data(clk_data);
- return r;
+ return devm_mtk_clk_mux_notifier_register(dev, clk, mfg_mux_nb);
}
-static int clk_mt8192_apmixed_probe(struct platform_device *pdev)
-{
- struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
- r = mtk_clk_register_gates(node, apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
- if (r)
- goto free_clk_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto free_clk_data;
+static const struct mtk_clk_desc infra_desc = {
+ .clks = infra_clks,
+ .num_clks = ARRAY_SIZE(infra_clks),
+ .rst_desc = &clk_rst_desc,
+};
- return r;
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_clks,
+ .num_clks = ARRAY_SIZE(peri_clks),
+};
-free_clk_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
+static const struct mtk_clk_desc topck_desc = {
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_mtk_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_mtk_muxes),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .clk_lock = &mt8192_clk_lock,
+ .clk_notifier_func = clk_mt8192_reg_mfg_mux_notifier,
+ .mfg_clk_idx = CLK_TOP_MFG_PLL_SEL,
+};
static const struct of_device_id of_match_clk_mt8192[] = {
- {
- .compatible = "mediatek,mt8192-apmixedsys",
- .data = clk_mt8192_apmixed_probe,
- }, {
- .compatible = "mediatek,mt8192-topckgen",
- .data = clk_mt8192_top_probe,
- }, {
- .compatible = "mediatek,mt8192-infracfg",
- .data = clk_mt8192_infra_probe,
- }, {
- .compatible = "mediatek,mt8192-pericfg",
- .data = clk_mt8192_peri_probe,
- }, {
- /* sentinel */
- }
-};
-
-static int clk_mt8192_probe(struct platform_device *pdev)
-{
- int (*clk_probe)(struct platform_device *pdev);
- int r;
-
- clk_probe = of_device_get_match_data(&pdev->dev);
- if (!clk_probe)
- return -EINVAL;
-
- r = clk_probe(pdev);
- if (r)
- dev_err(&pdev->dev, "could not register clock provider: %s: %d\n", pdev->name, r);
-
- return r;
-}
+ { .compatible = "mediatek,mt8192-infracfg", .data = &infra_desc },
+ { .compatible = "mediatek,mt8192-pericfg", .data = &peri_desc },
+ { .compatible = "mediatek,mt8192-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8192);
static struct platform_driver clk_mt8192_drv = {
- .probe = clk_mt8192_probe,
.driver = {
.name = "clk-mt8192",
.of_match_table = of_match_clk_mt8192,
},
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
};
+module_platform_driver(clk_mt8192_drv);
-static int __init clk_mt8192_init(void)
-{
- return platform_driver_register(&clk_mt8192_drv);
-}
-
-arch_initcall(clk_mt8192_init);
+MODULE_DESCRIPTION("MediaTek MT8192 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-apmixedsys.c b/drivers/clk/mediatek/clk-mt8195-apmixedsys.c
index 0dfed6ec4d15..282a3137dc89 100644
--- a/drivers/clk/mediatek/clk-mt8195-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt8195-apmixedsys.c
@@ -3,12 +3,14 @@
// Copyright (c) 2021 MediaTek Inc.
// Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
+#include "clk-fhctl.h"
#include "clk-gate.h"
#include "clk-mtk.h"
#include "clk-pll.h"
+#include "clk-pllfh.h"
#include <dt-bindings/clock/mt8195-clk.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
static const struct mtk_gate_regs apmixed_cg_regs = {
@@ -105,26 +107,87 @@ static const struct mtk_pll_data plls[] = {
0, 0, 22, 0x0158, 24, 0, 0, 0, 0x0158, 0, 0x0158, 0, 9),
};
+enum fh_pll_id {
+ FH_ARMPLL_LL,
+ FH_ARMPLL_BL,
+ FH_MEMPLL,
+ FH_ADSPPLL,
+ FH_NNAPLL,
+ FH_CCIPLL,
+ FH_MFGPLL,
+ FH_TVDPLL2,
+ FH_MPLL,
+ FH_MMPLL,
+ FH_MAINPLL,
+ FH_MSDCPLL,
+ FH_IMGPLL,
+ FH_VDECPLL,
+ FH_TVDPLL1,
+ FH_NR_FH,
+};
+
+#define FH(_pllid, _fhid, _offset) { \
+ .data = { \
+ .pll_id = _pllid, \
+ .fh_id = _fhid, \
+ .fh_ver = FHCTL_PLLFH_V2, \
+ .fhx_offset = _offset, \
+ .dds_mask = GENMASK(21, 0), \
+ .slope0_value = 0x6003c97, \
+ .slope1_value = 0x6003c97, \
+ .sfstrx_en = BIT(2), \
+ .frddsx_en = BIT(1), \
+ .fhctlx_en = BIT(0), \
+ .tgl_org = BIT(31), \
+ .dvfs_tri = BIT(31), \
+ .pcwchg = BIT(31), \
+ .dt_val = 0x0, \
+ .df_val = 0x9, \
+ .updnlmt_shft = 16, \
+ .msk_frddsx_dys = GENMASK(23, 20), \
+ .msk_frddsx_dts = GENMASK(19, 16), \
+ }, \
+ }
+
+static struct mtk_pllfh_data pllfhs[] = {
+ FH(CLK_APMIXED_ADSPPLL, FH_ADSPPLL, 0x78),
+ FH(CLK_APMIXED_NNAPLL, FH_NNAPLL, 0x8c),
+ FH(CLK_APMIXED_MFGPLL, FH_MFGPLL, 0xb4),
+ FH(CLK_APMIXED_TVDPLL2, FH_TVDPLL2, 0xc8),
+ FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0xf0),
+ FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x104),
+ FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x118),
+ FH(CLK_APMIXED_IMGPLL, FH_IMGPLL, 0x12c),
+ FH(CLK_APMIXED_VDECPLL, FH_VDECPLL, 0x140),
+ FH(CLK_APMIXED_TVDPLL2, FH_TVDPLL1, 0x154),
+};
+
static const struct of_device_id of_match_clk_mt8195_apmixed[] = {
{ .compatible = "mediatek,mt8195-apmixedsys", },
{}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_apmixed);
static int clk_mt8195_apmixed_probe(struct platform_device *pdev)
{
struct clk_hw_onecell_data *clk_data;
struct device_node *node = pdev->dev.of_node;
+ const u8 *fhctl_node = "mediatek,mt8195-fhctl";
int r;
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
if (!clk_data)
return -ENOMEM;
- r = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ fhctl_parse_dt(fhctl_node, pllfhs, ARRAY_SIZE(pllfhs));
+
+ r = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls),
+ pllfhs, ARRAY_SIZE(pllfhs), clk_data);
if (r)
goto free_apmixed_data;
- r = mtk_clk_register_gates(node, apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
+ r = mtk_clk_register_gates(&pdev->dev, node, apmixed_clks,
+ ARRAY_SIZE(apmixed_clks), clk_data);
if (r)
goto unregister_plls;
@@ -139,23 +202,23 @@ static int clk_mt8195_apmixed_probe(struct platform_device *pdev)
unregister_gates:
mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
unregister_plls:
- mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
free_apmixed_data:
mtk_free_clk_data(clk_data);
return r;
}
-static int clk_mt8195_apmixed_remove(struct platform_device *pdev)
+static void clk_mt8195_apmixed_remove(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
of_clk_del_provider(node);
mtk_clk_unregister_gates(apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data);
- mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+ mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs,
+ ARRAY_SIZE(pllfhs), clk_data);
mtk_free_clk_data(clk_data);
-
- return 0;
}
static struct platform_driver clk_mt8195_apmixed_drv = {
@@ -166,4 +229,7 @@ static struct platform_driver clk_mt8195_apmixed_drv = {
.of_match_table = of_match_clk_mt8195_apmixed,
},
};
-builtin_platform_driver(clk_mt8195_apmixed_drv);
+module_platform_driver(clk_mt8195_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-apusys_pll.c b/drivers/clk/mediatek/clk-mt8195-apusys_pll.c
index 0b52f6a009c4..8b45a3fad02f 100644
--- a/drivers/clk/mediatek/clk-mt8195-apusys_pll.c
+++ b/drivers/clk/mediatek/clk-mt8195-apusys_pll.c
@@ -85,7 +85,7 @@ free_apusys_pll_data:
return r;
}
-static int clk_mt8195_apusys_pll_remove(struct platform_device *pdev)
+static void clk_mt8195_apusys_pll_remove(struct platform_device *pdev)
{
struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
@@ -93,14 +93,13 @@ static int clk_mt8195_apusys_pll_remove(struct platform_device *pdev)
of_clk_del_provider(node);
mtk_clk_unregister_plls(apusys_plls, ARRAY_SIZE(apusys_plls), clk_data);
mtk_free_clk_data(clk_data);
-
- return 0;
}
static const struct of_device_id of_match_clk_mt8195_apusys_pll[] = {
{ .compatible = "mediatek,mt8195-apusys_pll", },
{}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_apusys_pll);
static struct platform_driver clk_mt8195_apusys_pll_drv = {
.probe = clk_mt8195_apusys_pll_probe,
@@ -110,4 +109,7 @@ static struct platform_driver clk_mt8195_apusys_pll_drv = {
.of_match_table = of_match_clk_mt8195_apusys_pll,
},
};
-builtin_platform_driver(clk_mt8195_apusys_pll_drv);
+module_platform_driver(clk_mt8195_apusys_pll_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 AI Processing Unit PLL clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-cam.c b/drivers/clk/mediatek/clk-mt8195-cam.c
index e4d00fe6e757..02cb20c2948b 100644
--- a/drivers/clk/mediatek/clk-mt8195-cam.c
+++ b/drivers/clk/mediatek/clk-mt8195-cam.c
@@ -131,6 +131,7 @@ static const struct of_device_id of_match_clk_mt8195_cam[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_cam);
static struct platform_driver clk_mt8195_cam_drv = {
.probe = mtk_clk_simple_probe,
@@ -140,4 +141,7 @@ static struct platform_driver clk_mt8195_cam_drv = {
.of_match_table = of_match_clk_mt8195_cam,
},
};
-builtin_platform_driver(clk_mt8195_cam_drv);
+module_platform_driver(clk_mt8195_cam_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-ccu.c b/drivers/clk/mediatek/clk-mt8195-ccu.c
index 4e326b6301ba..22cd1cb070f1 100644
--- a/drivers/clk/mediatek/clk-mt8195-ccu.c
+++ b/drivers/clk/mediatek/clk-mt8195-ccu.c
@@ -39,6 +39,7 @@ static const struct of_device_id of_match_clk_mt8195_ccu[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_ccu);
static struct platform_driver clk_mt8195_ccu_drv = {
.probe = mtk_clk_simple_probe,
@@ -48,4 +49,7 @@ static struct platform_driver clk_mt8195_ccu_drv = {
.of_match_table = of_match_clk_mt8195_ccu,
},
};
-builtin_platform_driver(clk_mt8195_ccu_drv);
+module_platform_driver(clk_mt8195_ccu_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Camera Control Unit clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-img.c b/drivers/clk/mediatek/clk-mt8195-img.c
index 12f5c436d075..11beba4b2ac2 100644
--- a/drivers/clk/mediatek/clk-mt8195-img.c
+++ b/drivers/clk/mediatek/clk-mt8195-img.c
@@ -85,6 +85,7 @@ static const struct of_device_id of_match_clk_mt8195_img[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_img);
static struct platform_driver clk_mt8195_img_drv = {
.probe = mtk_clk_simple_probe,
@@ -94,4 +95,7 @@ static struct platform_driver clk_mt8195_img_drv = {
.of_match_table = of_match_clk_mt8195_img,
},
};
-builtin_platform_driver(clk_mt8195_img_drv);
+module_platform_driver(clk_mt8195_img_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 imgsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c b/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c
index fbc809d05072..8711b18b1576 100644
--- a/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c
+++ b/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c
@@ -55,6 +55,7 @@ static const struct of_device_id of_match_clk_mt8195_imp_iic_wrap[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_imp_iic_wrap);
static struct platform_driver clk_mt8195_imp_iic_wrap_drv = {
.probe = mtk_clk_simple_probe,
@@ -64,4 +65,7 @@ static struct platform_driver clk_mt8195_imp_iic_wrap_drv = {
.of_match_table = of_match_clk_mt8195_imp_iic_wrap,
},
};
-builtin_platform_driver(clk_mt8195_imp_iic_wrap_drv);
+module_platform_driver(clk_mt8195_imp_iic_wrap_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 I2C Wrapper clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-infra_ao.c b/drivers/clk/mediatek/clk-mt8195-infra_ao.c
index 97657f255618..ad47fdb23460 100644
--- a/drivers/clk/mediatek/clk-mt8195-infra_ao.c
+++ b/drivers/clk/mediatek/clk-mt8195-infra_ao.c
@@ -55,8 +55,12 @@ static const struct mtk_gate_regs infra_ao4_cg_regs = {
#define GATE_INFRA_AO1(_id, _name, _parent, _shift) \
GATE_INFRA_AO1_FLAGS(_id, _name, _parent, _shift, 0)
+#define GATE_INFRA_AO2_FLAGS(_id, _name, _parent, _shift, _flag) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao2_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flag)
+
#define GATE_INFRA_AO2(_id, _name, _parent, _shift) \
- GATE_MTK(_id, _name, _parent, &infra_ao2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+ GATE_INFRA_AO2_FLAGS(_id, _name, _parent, _shift, 0)
#define GATE_INFRA_AO3_FLAGS(_id, _name, _parent, _shift, _flag) \
GATE_MTK_FLAGS(_id, _name, _parent, &infra_ao3_cg_regs, _shift, \
@@ -99,7 +103,7 @@ static const struct mtk_gate infra_ao_clks[] = {
GATE_INFRA_AO0(CLK_INFRA_AO_CQ_DMA_FPC, "infra_ao_cq_dma_fpc", "fpc", 28),
GATE_INFRA_AO0(CLK_INFRA_AO_UART5, "infra_ao_uart5", "top_uart", 29),
/* INFRA_AO1 */
- GATE_INFRA_AO1(CLK_INFRA_AO_HDMI_26M, "infra_ao_hdmi_26m", "clk26m", 0),
+ GATE_INFRA_AO1(CLK_INFRA_AO_HDMI_26M, "infra_ao_hdmi_26m", "top_hdmi_xtal", 0),
GATE_INFRA_AO1(CLK_INFRA_AO_SPI0, "infra_ao_spi0", "top_spi", 1),
GATE_INFRA_AO1(CLK_INFRA_AO_MSDC0, "infra_ao_msdc0", "top_msdc50_0_hclk", 2),
GATE_INFRA_AO1(CLK_INFRA_AO_MSDC1, "infra_ao_msdc1", "top_axi", 4),
@@ -136,8 +140,11 @@ static const struct mtk_gate infra_ao_clks[] = {
GATE_INFRA_AO2(CLK_INFRA_AO_UNIPRO_SYS, "infra_ao_unipro_sys", "top_ufs", 11),
GATE_INFRA_AO2(CLK_INFRA_AO_UNIPRO_TICK, "infra_ao_unipro_tick", "top_ufs_tick1us", 12),
GATE_INFRA_AO2(CLK_INFRA_AO_UFS_MP_SAP_B, "infra_ao_ufs_mp_sap_b", "top_ufs_mp_sap_cfg", 13),
- GATE_INFRA_AO2(CLK_INFRA_AO_PWRMCU, "infra_ao_pwrmcu", "top_pwrmcu", 15),
- GATE_INFRA_AO2(CLK_INFRA_AO_PWRMCU_BUS_H, "infra_ao_pwrmcu_bus_h", "top_axi", 17),
+ /* pwrmcu is used by ATF for platform PM: clocks must never be disabled by the kernel */
+ GATE_INFRA_AO2_FLAGS(CLK_INFRA_AO_PWRMCU, "infra_ao_pwrmcu", "top_pwrmcu", 15,
+ CLK_IS_CRITICAL),
+ GATE_INFRA_AO2_FLAGS(CLK_INFRA_AO_PWRMCU_BUS_H, "infra_ao_pwrmcu_bus_h", "top_axi", 17,
+ CLK_IS_CRITICAL),
GATE_INFRA_AO2(CLK_INFRA_AO_APDMA_B, "infra_ao_apdma_b", "top_axi", 18),
GATE_INFRA_AO2(CLK_INFRA_AO_SPI4, "infra_ao_spi4", "top_spi", 25),
GATE_INFRA_AO2(CLK_INFRA_AO_SPI5, "infra_ao_spi5", "top_spi", 26),
@@ -193,6 +200,9 @@ static u16 infra_ao_rst_ofs[] = {
static u16 infra_ao_idx_map[] = {
[MT8195_INFRA_RST0_THERM_CTRL_SWRST] = 0 * RST_NR_PER_BANK + 0,
+ [MT8195_INFRA_RST2_USBSIF_P1_SWRST] = 2 * RST_NR_PER_BANK + 18,
+ [MT8195_INFRA_RST2_PCIE_P0_SWRST] = 2 * RST_NR_PER_BANK + 26,
+ [MT8195_INFRA_RST2_PCIE_P1_SWRST] = 2 * RST_NR_PER_BANK + 27,
[MT8195_INFRA_RST3_THERM_CTRL_PTP_SWRST] = 3 * RST_NR_PER_BANK + 5,
[MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST] = 4 * RST_NR_PER_BANK + 10,
};
@@ -219,6 +229,7 @@ static const struct of_device_id of_match_clk_mt8195_infra_ao[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_infra_ao);
static struct platform_driver clk_mt8195_infra_ao_drv = {
.probe = mtk_clk_simple_probe,
@@ -228,4 +239,7 @@ static struct platform_driver clk_mt8195_infra_ao_drv = {
.of_match_table = of_match_clk_mt8195_infra_ao,
},
};
-builtin_platform_driver(clk_mt8195_infra_ao_drv);
+module_platform_driver(clk_mt8195_infra_ao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 infracfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-ipe.c b/drivers/clk/mediatek/clk-mt8195-ipe.c
index b0d745cf7752..b1af00348a86 100644
--- a/drivers/clk/mediatek/clk-mt8195-ipe.c
+++ b/drivers/clk/mediatek/clk-mt8195-ipe.c
@@ -40,6 +40,7 @@ static const struct of_device_id of_match_clk_mt8195_ipe[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_ipe);
static struct platform_driver clk_mt8195_ipe_drv = {
.probe = mtk_clk_simple_probe,
@@ -49,4 +50,7 @@ static struct platform_driver clk_mt8195_ipe_drv = {
.of_match_table = of_match_clk_mt8195_ipe,
},
};
-builtin_platform_driver(clk_mt8195_ipe_drv);
+module_platform_driver(clk_mt8195_ipe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Image Processing Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-mfg.c b/drivers/clk/mediatek/clk-mt8195-mfg.c
index 9411c556a5a9..07c358db1af9 100644
--- a/drivers/clk/mediatek/clk-mt8195-mfg.c
+++ b/drivers/clk/mediatek/clk-mt8195-mfg.c
@@ -17,10 +17,12 @@ static const struct mtk_gate_regs mfg_cg_regs = {
};
#define GATE_MFG(_id, _name, _parent, _shift) \
- GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+ GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, \
+ _shift, &mtk_clk_gate_ops_setclr, \
+ CLK_SET_RATE_PARENT)
static const struct mtk_gate mfg_clks[] = {
- GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "top_mfg_core_tmp", 0),
+ GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_ck_fast_ref", 0),
};
static const struct mtk_clk_desc mfg_desc = {
@@ -36,6 +38,7 @@ static const struct of_device_id of_match_clk_mt8195_mfg[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_mfg);
static struct platform_driver clk_mt8195_mfg_drv = {
.probe = mtk_clk_simple_probe,
@@ -45,4 +48,7 @@ static struct platform_driver clk_mt8195_mfg_drv = {
.of_match_table = of_match_clk_mt8195_mfg,
},
};
-builtin_platform_driver(clk_mt8195_mfg_drv);
+module_platform_driver(clk_mt8195_mfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-peri_ao.c b/drivers/clk/mediatek/clk-mt8195-peri_ao.c
index 2f6b3bb657db..b743eb60a30b 100644
--- a/drivers/clk/mediatek/clk-mt8195-peri_ao.c
+++ b/drivers/clk/mediatek/clk-mt8195-peri_ao.c
@@ -51,6 +51,7 @@ static const struct of_device_id of_match_clk_mt8195_peri_ao[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_peri_ao);
static struct platform_driver clk_mt8195_peri_ao_drv = {
.probe = mtk_clk_simple_probe,
@@ -60,4 +61,7 @@ static struct platform_driver clk_mt8195_peri_ao_drv = {
.of_match_table = of_match_clk_mt8195_peri_ao,
},
};
-builtin_platform_driver(clk_mt8195_peri_ao_drv);
+module_platform_driver(clk_mt8195_peri_ao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 pericfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-scp_adsp.c b/drivers/clk/mediatek/clk-mt8195-scp_adsp.c
index e16c383f631b..bc73fccd0515 100644
--- a/drivers/clk/mediatek/clk-mt8195-scp_adsp.c
+++ b/drivers/clk/mediatek/clk-mt8195-scp_adsp.c
@@ -36,6 +36,7 @@ static const struct of_device_id of_match_clk_mt8195_scp_adsp[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_scp_adsp);
static struct platform_driver clk_mt8195_scp_adsp_drv = {
.probe = mtk_clk_simple_probe,
@@ -45,4 +46,7 @@ static struct platform_driver clk_mt8195_scp_adsp_drv = {
.of_match_table = of_match_clk_mt8195_scp_adsp,
},
};
-builtin_platform_driver(clk_mt8195_scp_adsp_drv);
+module_platform_driver(clk_mt8195_scp_adsp_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 SCP AudioDSP clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-topckgen.c b/drivers/clk/mediatek/clk-mt8195-topckgen.c
index ec70e1f65eaf..b1f44b873354 100644
--- a/drivers/clk/mediatek/clk-mt8195-topckgen.c
+++ b/drivers/clk/mediatek/clk-mt8195-topckgen.c
@@ -8,7 +8,7 @@
#include "clk-mux.h"
#include <dt-bindings/clock/mt8195-clk.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
static DEFINE_SPINLOCK(mt8195_clk_lock);
@@ -35,45 +35,45 @@ static const struct mtk_fixed_factor top_divs[] = {
FACTOR(CLK_TOP_IN_DGI_D4, "in_dgi_d4", "in_dgi", 1, 4),
FACTOR(CLK_TOP_IN_DGI_D6, "in_dgi_d6", "in_dgi", 1, 6),
FACTOR(CLK_TOP_IN_DGI_D8, "in_dgi_d8", "in_dgi", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3),
- FACTOR(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll_d4", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll_d4", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll_d4", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5),
- FACTOR(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll_d5", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6),
- FACTOR(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll_d6", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D6_D4, "mainpll_d6_d4", "mainpll_d6", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D6_D8, "mainpll_d6_d8", "mainpll_d6", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7),
- FACTOR(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2),
- FACTOR(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4),
- FACTOR(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll_d7", 1, 8),
- FACTOR(CLK_TOP_MAINPLL_D9, "mainpll_d9", "mainpll", 1, 9),
- FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
- FACTOR(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll_d4", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll_d4", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll_d4", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
- FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6),
- FACTOR(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll_d6", 1, 2),
- FACTOR(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll_d6", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll_d6", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_D6_D16, "univpll_d6_d16", "univpll_d6", 1, 16),
- FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
- FACTOR(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13),
- FACTOR(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4),
- FACTOR(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8),
- FACTOR(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16),
- FACTOR(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll_d4", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll_d4", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll_d4", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll_d5", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll_d6", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6_D4, "mainpll_d6_d4", "mainpll_d6", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D6_D8, "mainpll_d6_d8", "mainpll_d6", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll_d7", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll_d7", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll_d7", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_MAINPLL_D9, "mainpll_d9", "mainpll", 1, 9, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll_d4", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll_d4", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll_d4", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll_d5", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll_d5", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D5_D8, "univpll_d5_d8", "univpll_d5", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll_d6", 1, 2, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll_d6", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll_d6", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D6_D16, "univpll_d6_d16", "univpll_d6", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll_192m", 1, 4, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll_192m", 1, 8, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll_192m", 1, 16, 0),
+ FACTOR_FLAGS(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll_192m", 1, 32, 0),
FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1", 1, 3),
FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1", 1, 4),
FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2", 1, 3),
@@ -298,11 +298,14 @@ static const char * const ipu_if_parents[] = {
"mmpll_d4"
};
+/*
+ * MFG can be also parented to "univpll_d6" and "univpll_d7":
+ * these have been removed from the parents list to let us
+ * achieve GPU DVFS without any special clock handlers.
+ */
static const char * const mfg_parents[] = {
"clk26m",
- "mainpll_d5_d2",
- "univpll_d6",
- "univpll_d7"
+ "mainpll_d5_d2"
};
static const char * const camtg_parents[] = {
@@ -412,17 +415,28 @@ static const char * const pwrmcu_parents[] = {
"mainpll_d4_d2"
};
+/*
+ * Both DP/eDP can be parented to TVDPLL1 and TVDPLL2, but we force using
+ * TVDPLL1 on eDP and TVDPLL2 on DP to avoid changing the "other" PLL rate
+ * in dual output case, which would lead to corruption of functionality loss.
+ */
static const char * const dp_parents[] = {
"clk26m",
- "tvdpll1_d2",
"tvdpll2_d2",
- "tvdpll1_d4",
"tvdpll2_d4",
- "tvdpll1_d8",
"tvdpll2_d8",
- "tvdpll1_d16",
"tvdpll2_d16"
};
+static const u8 dp_parents_idx[] = { 0, 2, 4, 6, 8 };
+
+static const char * const edp_parents[] = {
+ "clk26m",
+ "tvdpll1_d2",
+ "tvdpll1_d4",
+ "tvdpll1_d8",
+ "tvdpll1_d16"
+};
+static const u8 edp_parents_idx[] = { 0, 1, 3, 5, 7 };
static const char * const disp_pwm_parents[] = {
"clk26m",
@@ -859,13 +873,17 @@ static const struct mtk_mux top_mtk_muxes[] = {
* top_spm and top_scp are main clocks in always-on co-processor.
*/
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI, "top_axi",
- axi_parents, 0x020, 0x024, 0x028, 0, 3, 7, 0x04, 0, CLK_IS_CRITICAL),
+ axi_parents, 0x020, 0x024, 0x028, 0, 3, 7, 0x04, 0,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SPM, "top_spm",
- spm_parents, 0x020, 0x024, 0x028, 8, 2, 15, 0x04, 1, CLK_IS_CRITICAL),
+ spm_parents, 0x020, 0x024, 0x028, 8, 2, 15, 0x04, 1,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SCP, "top_scp",
- scp_parents, 0x020, 0x024, 0x028, 16, 3, 23, 0x04, 2, CLK_IS_CRITICAL),
+ scp_parents, 0x020, 0x024, 0x028, 16, 3, 23, 0x04, 2,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_BUS_AXIMEM, "top_bus_aximem",
- bus_aximem_parents, 0x020, 0x024, 0x028, 24, 3, 31, 0x04, 3, CLK_IS_CRITICAL),
+ bus_aximem_parents, 0x020, 0x024, 0x028, 24, 3, 31, 0x04, 3,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/* CLK_CFG_1 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_VPP, "top_vpp",
vpp_parents, 0x02C, 0x030, 0x034, 0, 4, 7, 0x04, 4),
@@ -923,15 +941,15 @@ static const struct mtk_mux top_mtk_muxes[] = {
/* CLK_CFG_7 */
MUX_GATE_CLR_SET_UPD(CLK_TOP_SPIS, "top_spis",
spis_parents, 0x074, 0x078, 0x07C, 0, 3, 7, 0x04, 28),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_HCLK, "top_msdc50_0_hclk",
- msdc50_0_h_parents, 0x074, 0x078, 0x07C, 8, 2, 15, 0x04, 29),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0, "top_msdc50_0",
- msdc50_0_parents, 0x074, 0x078, 0x07C, 16, 3, 23, 0x04, 30),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1, "top_msdc30_1",
- msdc30_parents, 0x074, 0x078, 0x07C, 24, 3, 31, 0x04, 31),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_HCLK, "top_msdc50_0_hclk",
+ msdc50_0_h_parents, 0x074, 0x078, 0x07C, 8, 2, 15, 0x04, 29, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0, "top_msdc50_0",
+ msdc50_0_parents, 0x074, 0x078, 0x07C, 16, 3, 23, 0x04, 30, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1, "top_msdc30_1",
+ msdc30_parents, 0x074, 0x078, 0x07C, 24, 3, 31, 0x04, 31, 0),
/* CLK_CFG_8 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_2, "top_msdc30_2",
- msdc30_parents, 0x080, 0x084, 0x088, 0, 3, 7, 0x08, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_2, "top_msdc30_2",
+ msdc30_parents, 0x080, 0x084, 0x088, 0, 3, 7, 0x08, 0, 0),
MUX_GATE_CLR_SET_UPD(CLK_TOP_INTDIR, "top_intdir",
intdir_parents, 0x080, 0x084, 0x088, 8, 2, 15, 0x08, 1),
MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_INTBUS, "top_aud_intbus",
@@ -948,12 +966,13 @@ static const struct mtk_mux top_mtk_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_ATB, "top_atb",
atb_parents, 0x08C, 0x090, 0x094, 8, 2, 15, 0x08, 5),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_PWRMCU, "top_pwrmcu",
- pwrmcu_parents, 0x08C, 0x090, 0x094, 16, 3, 23, 0x08, 6, CLK_IS_CRITICAL),
- MUX_GATE_CLR_SET_UPD(CLK_TOP_DP, "top_dp",
- dp_parents, 0x08C, 0x090, 0x094, 24, 4, 31, 0x08, 7),
+ pwrmcu_parents, 0x08C, 0x090, 0x094, 16, 3, 23, 0x08, 6,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD_INDEXED(CLK_TOP_DP, "top_dp",
+ dp_parents, dp_parents_idx, 0x08C, 0x090, 0x094, 24, 4, 31, 0x08, 7),
/* CLK_CFG_10 */
- MUX_GATE_CLR_SET_UPD(CLK_TOP_EDP, "top_edp",
- dp_parents, 0x098, 0x09C, 0x0A0, 0, 4, 7, 0x08, 8),
+ MUX_GATE_CLR_SET_UPD_INDEXED(CLK_TOP_EDP, "top_edp",
+ edp_parents, edp_parents_idx, 0x098, 0x09C, 0x0A0, 0, 4, 7, 0x08, 8),
MUX_GATE_CLR_SET_UPD(CLK_TOP_DPI, "top_dpi",
dp_parents, 0x098, 0x09C, 0x0A0, 8, 4, 15, 0x08, 9),
MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_PWM0, "top_disp_pwm0",
@@ -1017,7 +1036,8 @@ static const struct mtk_mux top_mtk_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM, "top_pwm",
pwm_parents, 0x0E0, 0x0E4, 0x0E8, 16, 1, 23, 0x0C, 2),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MCUPM, "top_mcupm",
- mcupm_parents, 0x0E0, 0x0E4, 0x0E8, 24, 2, 31, 0x0C, 3, CLK_IS_CRITICAL),
+ mcupm_parents, 0x0E0, 0x0E4, 0x0E8, 24, 2, 31, 0x0C, 3,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/*
* CLK_CFG_17
* top_dvfsrc is for internal DVFS usage, should not be handled by Linux.
@@ -1027,7 +1047,8 @@ static const struct mtk_mux top_mtk_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_SPMI_M_MST, "top_spmi_m_mst",
spmi_parents, 0x0EC, 0x0F0, 0x0F4, 8, 4, 15, 0x0C, 5),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DVFSRC, "top_dvfsrc",
- dvfsrc_parents, 0x0EC, 0x0F0, 0x0F4, 16, 2, 23, 0x0C, 6, CLK_IS_CRITICAL),
+ dvfsrc_parents, 0x0EC, 0x0F0, 0x0F4, 16, 2, 23, 0x0C, 6,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD(CLK_TOP_TL, "top_tl",
tl_parents, 0x0EC, 0x0F0, 0x0F4, 24, 2, 31, 0x0C, 7),
/* CLK_CFG_18 */
@@ -1138,22 +1159,20 @@ static const struct mtk_mux top_mtk_muxes[] = {
MUX_GATE_CLR_SET_UPD(CLK_TOP_DVIO_DGI_REF, "top_dvio_dgi_ref",
dvio_dgi_ref_parents, 0x017C, 0x0180, 0x0184, 0, 3, 7, 0x010, 20),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_ULPOSC, "top_ulposc",
- ulposc_parents, 0x017C, 0x0180, 0x0184, 8, 2, 15, 0x010, 21, CLK_IS_CRITICAL),
+ ulposc_parents, 0x017C, 0x0180, 0x0184, 8, 2, 15, 0x010, 21,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_ULPOSC_CORE, "top_ulposc_core",
- ulposc_core_parents, 0x017C, 0x0180, 0x0184, 16, 2, 23, 0x010, 22, CLK_IS_CRITICAL),
+ ulposc_core_parents, 0x017C, 0x0180, 0x0184, 16, 2, 23, 0x010, 22,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SRCK, "top_srck",
- srck_parents, 0x017C, 0x0180, 0x0184, 24, 1, 31, 0x010, 23, CLK_IS_CRITICAL),
+ srck_parents, 0x017C, 0x0180, 0x0184, 24, 1, 31, 0x010, 23,
+ CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/*
* the clocks in CLK_CFG_30 ~ 37 are backup clock source, no need to handled
* by Linux.
*/
};
-static struct mtk_composite top_muxes[] = {
- /* CLK_MISC_CFG_3 */
- MUX(CLK_TOP_MFG_CK_FAST_REF, "mfg_ck_fast_ref", mfg_fast_parents, 0x0250, 8, 1),
-};
-
static const struct mtk_composite top_adj_divs[] = {
DIV_GATE(CLK_TOP_APLL12_DIV0, "apll12_div0", "top_i2si1_mck", 0x0320, 0, 0x0328, 8, 0),
DIV_GATE(CLK_TOP_APLL12_DIV1, "apll12_div1", "top_i2si2_mck", 0x0320, 1, 0x0328, 8, 8),
@@ -1221,11 +1240,28 @@ static const struct of_device_id of_match_clk_mt8195_topck[] = {
{ .compatible = "mediatek,mt8195-topckgen", },
{}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_topck);
+
+/* Register mux notifier for MFG mux */
+static int clk_mt8195_reg_mfg_mux_notifier(struct device *dev, struct clk *clk)
+{
+ struct mtk_mux_nb *mfg_mux_nb;
+
+ mfg_mux_nb = devm_kzalloc(dev, sizeof(*mfg_mux_nb), GFP_KERNEL);
+ if (!mfg_mux_nb)
+ return -ENOMEM;
+
+ mfg_mux_nb->ops = &clk_mux_ops;
+ mfg_mux_nb->bypass_index = 0; /* Bypass to TOP_MFG_CORE_TMP */
+
+ return devm_mtk_clk_mux_notifier_register(dev, clk, mfg_mux_nb);
+}
static int clk_mt8195_topck_probe(struct platform_device *pdev)
{
struct clk_hw_onecell_data *top_clk_data;
struct device_node *node = pdev->dev.of_node;
+ struct clk_hw *hw;
int r;
void __iomem *base;
@@ -1248,22 +1284,34 @@ static int clk_mt8195_topck_probe(struct platform_device *pdev)
if (r)
goto unregister_fixed_clks;
- r = mtk_clk_register_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), node,
+ r = mtk_clk_register_muxes(&pdev->dev, top_mtk_muxes,
+ ARRAY_SIZE(top_mtk_muxes), node,
&mt8195_clk_lock, top_clk_data);
if (r)
goto unregister_factors;
- r = mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt8195_clk_lock, top_clk_data);
+ hw = devm_clk_hw_register_mux(&pdev->dev, "mfg_ck_fast_ref", mfg_fast_parents,
+ ARRAY_SIZE(mfg_fast_parents), CLK_SET_RATE_PARENT,
+ (base + 0x250), 8, 1, 0, &mt8195_clk_lock);
+ if (IS_ERR(hw)) {
+ r = PTR_ERR(hw);
+ goto unregister_muxes;
+ }
+ top_clk_data->hws[CLK_TOP_MFG_CK_FAST_REF] = hw;
+
+ r = clk_mt8195_reg_mfg_mux_notifier(&pdev->dev,
+ top_clk_data->hws[CLK_TOP_MFG_CK_FAST_REF]->clk);
if (r)
goto unregister_muxes;
- r = mtk_clk_register_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), base,
+ r = mtk_clk_register_composites(&pdev->dev, top_adj_divs,
+ ARRAY_SIZE(top_adj_divs), base,
&mt8195_clk_lock, top_clk_data);
if (r)
- goto unregister_composite_muxes;
+ goto unregister_muxes;
- r = mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), top_clk_data);
+ r = mtk_clk_register_gates(&pdev->dev, node, top_clks,
+ ARRAY_SIZE(top_clks), top_clk_data);
if (r)
goto unregister_composite_divs;
@@ -1279,8 +1327,6 @@ unregister_gates:
mtk_clk_unregister_gates(top_clks, ARRAY_SIZE(top_clks), top_clk_data);
unregister_composite_divs:
mtk_clk_unregister_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), top_clk_data);
-unregister_composite_muxes:
- mtk_clk_unregister_composites(top_muxes, ARRAY_SIZE(top_muxes), top_clk_data);
unregister_muxes:
mtk_clk_unregister_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), top_clk_data);
unregister_factors:
@@ -1292,7 +1338,7 @@ free_top_data:
return r;
}
-static int clk_mt8195_topck_remove(struct platform_device *pdev)
+static void clk_mt8195_topck_remove(struct platform_device *pdev)
{
struct clk_hw_onecell_data *top_clk_data = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
@@ -1300,13 +1346,10 @@ static int clk_mt8195_topck_remove(struct platform_device *pdev)
of_clk_del_provider(node);
mtk_clk_unregister_gates(top_clks, ARRAY_SIZE(top_clks), top_clk_data);
mtk_clk_unregister_composites(top_adj_divs, ARRAY_SIZE(top_adj_divs), top_clk_data);
- mtk_clk_unregister_composites(top_muxes, ARRAY_SIZE(top_muxes), top_clk_data);
mtk_clk_unregister_muxes(top_mtk_muxes, ARRAY_SIZE(top_mtk_muxes), top_clk_data);
mtk_clk_unregister_factors(top_divs, ARRAY_SIZE(top_divs), top_clk_data);
mtk_clk_unregister_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), top_clk_data);
mtk_free_clk_data(top_clk_data);
-
- return 0;
}
static struct platform_driver clk_mt8195_topck_drv = {
@@ -1317,4 +1360,7 @@ static struct platform_driver clk_mt8195_topck_drv = {
.of_match_table = of_match_clk_mt8195_topck,
},
};
-builtin_platform_driver(clk_mt8195_topck_drv);
+module_platform_driver(clk_mt8195_topck_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 top clock generators driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-vdec.c b/drivers/clk/mediatek/clk-mt8195-vdec.c
index a1446b666385..0bad706047c9 100644
--- a/drivers/clk/mediatek/clk-mt8195-vdec.c
+++ b/drivers/clk/mediatek/clk-mt8195-vdec.c
@@ -93,6 +93,7 @@ static const struct of_device_id of_match_clk_mt8195_vdec[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_vdec);
static struct platform_driver clk_mt8195_vdec_drv = {
.probe = mtk_clk_simple_probe,
@@ -102,4 +103,7 @@ static struct platform_driver clk_mt8195_vdec_drv = {
.of_match_table = of_match_clk_mt8195_vdec,
},
};
-builtin_platform_driver(clk_mt8195_vdec_drv);
+module_platform_driver(clk_mt8195_vdec_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-vdo0.c b/drivers/clk/mediatek/clk-mt8195-vdo0.c
index 261a7f76dd3c..581d99f8c254 100644
--- a/drivers/clk/mediatek/clk-mt8195-vdo0.c
+++ b/drivers/clk/mediatek/clk-mt8195-vdo0.c
@@ -37,6 +37,10 @@ static const struct mtk_gate_regs vdo0_2_cg_regs = {
#define GATE_VDO0_2(_id, _name, _parent, _shift) \
GATE_MTK(_id, _name, _parent, &vdo0_2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+#define GATE_VDO0_2_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &vdo0_2_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flags)
+
static const struct mtk_gate vdo0_clks[] = {
/* VDO0_0 */
GATE_VDO0_0(CLK_VDO0_DISP_OVL0, "vdo0_disp_ovl0", "top_vpp", 0),
@@ -85,57 +89,30 @@ static const struct mtk_gate vdo0_clks[] = {
/* VDO0_2 */
GATE_VDO0_2(CLK_VDO0_DSI0_DSI, "vdo0_dsi0_dsi", "top_dsi_occ", 0),
GATE_VDO0_2(CLK_VDO0_DSI1_DSI, "vdo0_dsi1_dsi", "top_dsi_occ", 8),
- GATE_VDO0_2(CLK_VDO0_DP_INTF0_DP_INTF, "vdo0_dp_intf0_dp_intf", "top_edp", 16),
+ GATE_VDO0_2_FLAGS(CLK_VDO0_DP_INTF0_DP_INTF, "vdo0_dp_intf0_dp_intf",
+ "top_edp", 16, CLK_SET_RATE_PARENT),
};
-static int clk_mt8195_vdo0_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_VDO0_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- r = mtk_clk_register_gates(node, vdo0_clks, ARRAY_SIZE(vdo0_clks), clk_data);
- if (r)
- goto free_vdo0_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto unregister_gates;
-
- platform_set_drvdata(pdev, clk_data);
-
- return r;
-
-unregister_gates:
- mtk_clk_unregister_gates(vdo0_clks, ARRAY_SIZE(vdo0_clks), clk_data);
-free_vdo0_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
-
-static int clk_mt8195_vdo0_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
-
- of_clk_del_provider(node);
- mtk_clk_unregister_gates(vdo0_clks, ARRAY_SIZE(vdo0_clks), clk_data);
- mtk_free_clk_data(clk_data);
+static const struct mtk_clk_desc vdo0_desc = {
+ .clks = vdo0_clks,
+ .num_clks = ARRAY_SIZE(vdo0_clks),
+};
- return 0;
-}
+static const struct platform_device_id clk_mt8195_vdo0_id_table[] = {
+ { .name = "clk-mt8195-vdo0", .driver_data = (kernel_ulong_t)&vdo0_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8195_vdo0_id_table);
static struct platform_driver clk_mt8195_vdo0_drv = {
- .probe = clk_mt8195_vdo0_probe,
- .remove = clk_mt8195_vdo0_remove,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8195-vdo0",
},
+ .id_table = clk_mt8195_vdo0_id_table,
};
-builtin_platform_driver(clk_mt8195_vdo0_drv);
+module_platform_driver(clk_mt8195_vdo0_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Video Output 0 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-vdo1.c b/drivers/clk/mediatek/clk-mt8195-vdo1.c
index 3378487d2c90..7f8b1a8967bd 100644
--- a/drivers/clk/mediatek/clk-mt8195-vdo1.c
+++ b/drivers/clk/mediatek/clk-mt8195-vdo1.c
@@ -34,6 +34,12 @@ static const struct mtk_gate_regs vdo1_3_cg_regs = {
.sta_ofs = 0x140,
};
+static const struct mtk_gate_regs vdo1_4_cg_regs = {
+ .set_ofs = 0x400,
+ .clr_ofs = 0x400,
+ .sta_ofs = 0x400,
+};
+
#define GATE_VDO1_0(_id, _name, _parent, _shift) \
GATE_MTK(_id, _name, _parent, &vdo1_0_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
@@ -43,9 +49,16 @@ static const struct mtk_gate_regs vdo1_3_cg_regs = {
#define GATE_VDO1_2(_id, _name, _parent, _shift) \
GATE_MTK(_id, _name, _parent, &vdo1_2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+#define GATE_VDO1_2_FLAGS(_id, _name, _parent, _shift, _flags) \
+ GATE_MTK_FLAGS(_id, _name, _parent, &vdo1_2_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr, _flags)
+
#define GATE_VDO1_3(_id, _name, _parent, _shift) \
GATE_MTK(_id, _name, _parent, &vdo1_3_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+#define GATE_VDO1_4(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdo1_4_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
static const struct mtk_gate vdo1_clks[] = {
/* VDO1_0 */
GATE_VDO1_0(CLK_VDO1_SMI_LARB2, "vdo1_smi_larb2", "top_vpp", 0),
@@ -99,60 +112,34 @@ static const struct mtk_gate vdo1_clks[] = {
GATE_VDO1_2(CLK_VDO1_DISP_MONITOR_DPI0, "vdo1_disp_monitor_dpi0", "top_vpp", 1),
GATE_VDO1_2(CLK_VDO1_DPI1, "vdo1_dpi1", "top_vpp", 8),
GATE_VDO1_2(CLK_VDO1_DISP_MONITOR_DPI1, "vdo1_disp_monitor_dpi1", "top_vpp", 9),
- GATE_VDO1_2(CLK_VDO1_DPINTF, "vdo1_dpintf", "top_vpp", 16),
+ GATE_VDO1_2_FLAGS(CLK_VDO1_DPINTF, "vdo1_dpintf", "top_dp", 16, CLK_SET_RATE_PARENT),
GATE_VDO1_2(CLK_VDO1_DISP_MONITOR_DPINTF, "vdo1_disp_monitor_dpintf", "top_vpp", 17),
/* VDO1_3 */
GATE_VDO1_3(CLK_VDO1_26M_SLOW, "vdo1_26m_slow", "clk26m", 8),
+ /* VDO1_4 */
+ GATE_VDO1_4(CLK_VDO1_DPI1_HDMI, "vdo1_dpi1_hdmi", "hdmi_txpll", 0),
};
-static int clk_mt8195_vdo1_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_VDO1_NR_CLK);
- if (!clk_data)
- return -ENOMEM;
-
- r = mtk_clk_register_gates(node, vdo1_clks, ARRAY_SIZE(vdo1_clks), clk_data);
- if (r)
- goto free_vdo1_data;
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- goto unregister_gates;
-
- platform_set_drvdata(pdev, clk_data);
-
- return r;
-
-unregister_gates:
- mtk_clk_unregister_gates(vdo1_clks, ARRAY_SIZE(vdo1_clks), clk_data);
-free_vdo1_data:
- mtk_free_clk_data(clk_data);
- return r;
-}
-
-static int clk_mt8195_vdo1_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *node = dev->parent->of_node;
- struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
-
- of_clk_del_provider(node);
- mtk_clk_unregister_gates(vdo1_clks, ARRAY_SIZE(vdo1_clks), clk_data);
- mtk_free_clk_data(clk_data);
+static const struct mtk_clk_desc vdo1_desc = {
+ .clks = vdo1_clks,
+ .num_clks = ARRAY_SIZE(vdo1_clks),
+};
- return 0;
-}
+static const struct platform_device_id clk_mt8195_vdo1_id_table[] = {
+ { .name = "clk-mt8195-vdo1", .driver_data = (kernel_ulong_t)&vdo1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8195_vdo1_id_table);
static struct platform_driver clk_mt8195_vdo1_drv = {
- .probe = clk_mt8195_vdo1_probe,
- .remove = clk_mt8195_vdo1_remove,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8195-vdo1",
},
+ .id_table = clk_mt8195_vdo1_id_table,
};
-builtin_platform_driver(clk_mt8195_vdo1_drv);
+module_platform_driver(clk_mt8195_vdo1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Video Output 1 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-venc.c b/drivers/clk/mediatek/clk-mt8195-venc.c
index 622f57804f96..3b52ff025d5e 100644
--- a/drivers/clk/mediatek/clk-mt8195-venc.c
+++ b/drivers/clk/mediatek/clk-mt8195-venc.c
@@ -58,6 +58,7 @@ static const struct of_device_id of_match_clk_mt8195_venc[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_venc);
static struct platform_driver clk_mt8195_venc_drv = {
.probe = mtk_clk_simple_probe,
@@ -67,4 +68,7 @@ static struct platform_driver clk_mt8195_venc_drv = {
.of_match_table = of_match_clk_mt8195_venc,
},
};
-builtin_platform_driver(clk_mt8195_venc_drv);
+module_platform_driver(clk_mt8195_venc_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-vpp0.c b/drivers/clk/mediatek/clk-mt8195-vpp0.c
index bf2939c3a023..0e3e1dd7977c 100644
--- a/drivers/clk/mediatek/clk-mt8195-vpp0.c
+++ b/drivers/clk/mediatek/clk-mt8195-vpp0.c
@@ -91,21 +91,21 @@ static const struct mtk_clk_desc vpp0_desc = {
.num_clks = ARRAY_SIZE(vpp0_clks),
};
-static const struct of_device_id of_match_clk_mt8195_vpp0[] = {
- {
- .compatible = "mediatek,mt8195-vppsys0",
- .data = &vpp0_desc,
- }, {
- /* sentinel */
- }
+static const struct platform_device_id clk_mt8195_vpp0_id_table[] = {
+ { .name = "clk-mt8195-vpp0", .driver_data = (kernel_ulong_t)&vpp0_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(platform, clk_mt8195_vpp0_id_table);
static struct platform_driver clk_mt8195_vpp0_drv = {
- .probe = mtk_clk_simple_probe,
- .remove = mtk_clk_simple_remove,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8195-vpp0",
- .of_match_table = of_match_clk_mt8195_vpp0,
},
+ .id_table = clk_mt8195_vpp0_id_table,
};
-builtin_platform_driver(clk_mt8195_vpp0_drv);
+module_platform_driver(clk_mt8195_vpp0_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Video Processing Pipe 0 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-vpp1.c b/drivers/clk/mediatek/clk-mt8195-vpp1.c
index ffd52c762890..fb7b7aef0bba 100644
--- a/drivers/clk/mediatek/clk-mt8195-vpp1.c
+++ b/drivers/clk/mediatek/clk-mt8195-vpp1.c
@@ -89,21 +89,21 @@ static const struct mtk_clk_desc vpp1_desc = {
.num_clks = ARRAY_SIZE(vpp1_clks),
};
-static const struct of_device_id of_match_clk_mt8195_vpp1[] = {
- {
- .compatible = "mediatek,mt8195-vppsys1",
- .data = &vpp1_desc,
- }, {
- /* sentinel */
- }
+static const struct platform_device_id clk_mt8195_vpp1_id_table[] = {
+ { .name = "clk-mt8195-vpp1", .driver_data = (kernel_ulong_t)&vpp1_desc },
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(platform, clk_mt8195_vpp1_id_table);
static struct platform_driver clk_mt8195_vpp1_drv = {
- .probe = mtk_clk_simple_probe,
- .remove = mtk_clk_simple_remove,
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
.driver = {
.name = "clk-mt8195-vpp1",
- .of_match_table = of_match_clk_mt8195_vpp1,
},
+ .id_table = clk_mt8195_vpp1_id_table,
};
-builtin_platform_driver(clk_mt8195_vpp1_drv);
+module_platform_driver(clk_mt8195_vpp1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Video Processing Pipe 1 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8195-wpe.c b/drivers/clk/mediatek/clk-mt8195-wpe.c
index b483fab10e18..315b93bbfcdc 100644
--- a/drivers/clk/mediatek/clk-mt8195-wpe.c
+++ b/drivers/clk/mediatek/clk-mt8195-wpe.c
@@ -132,6 +132,7 @@ static const struct of_device_id of_match_clk_mt8195_wpe[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_wpe);
static struct platform_driver clk_mt8195_wpe_drv = {
.probe = mtk_clk_simple_probe,
@@ -141,4 +142,7 @@ static struct platform_driver clk_mt8195_wpe_drv = {
.of_match_table = of_match_clk_mt8195_wpe,
},
};
-builtin_platform_driver(clk_mt8195_wpe_drv);
+module_platform_driver(clk_mt8195_wpe_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8195 Warp Engine clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-apmixedsys.c b/drivers/clk/mediatek/clk-mt8196-apmixedsys.c
new file mode 100644
index 000000000000..617f5449b88b
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-apmixedsys.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+/* APMIXEDSYS PLL control register offsets */
+#define MAINPLL_CON0 0x250
+#define MAINPLL_CON1 0x254
+#define UNIVPLL_CON0 0x264
+#define UNIVPLL_CON1 0x268
+#define MSDCPLL_CON0 0x278
+#define MSDCPLL_CON1 0x27c
+#define ADSPPLL_CON0 0x28c
+#define ADSPPLL_CON1 0x290
+#define EMIPLL_CON0 0x2a0
+#define EMIPLL_CON1 0x2a4
+#define EMIPLL2_CON0 0x2b4
+#define EMIPLL2_CON1 0x2b8
+#define NET1PLL_CON0 0x2c8
+#define NET1PLL_CON1 0x2cc
+#define SGMIIPLL_CON0 0x2dc
+#define SGMIIPLL_CON1 0x2e0
+
+/* APMIXEDSYS_GP2 PLL control register offsets*/
+#define MAINPLL2_CON0 0x250
+#define MAINPLL2_CON1 0x254
+#define UNIVPLL2_CON0 0x264
+#define UNIVPLL2_CON1 0x268
+#define MMPLL2_CON0 0x278
+#define MMPLL2_CON1 0x27c
+#define IMGPLL_CON0 0x28c
+#define IMGPLL_CON1 0x290
+#define TVDPLL1_CON0 0x2a0
+#define TVDPLL1_CON1 0x2a4
+#define TVDPLL2_CON0 0x2b4
+#define TVDPLL2_CON1 0x2b8
+#define TVDPLL3_CON0 0x2c8
+#define TVDPLL3_CON1 0x2cc
+
+#define PLLEN_ALL 0x080
+#define PLLEN_ALL_SET 0x084
+#define PLLEN_ALL_CLR 0x088
+
+#define FENC_STATUS_CON0 0x03c
+
+#define MT8196_PLL_FMAX (3800UL * MHZ)
+#define MT8196_PLL_FMIN (1500UL * MHZ)
+#define MT8196_INTEGER_BITS 8
+
+#define PLL_FENC(_id, _name, _reg, _fenc_sta_ofs, _fenc_sta_bit,\
+ _flags, _pd_reg, _pd_shift, \
+ _pcw_reg, _pcw_shift, _pcwbits, \
+ _pll_en_bit) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .fenc_sta_ofs = _fenc_sta_ofs, \
+ .fenc_sta_bit = _fenc_sta_bit, \
+ .flags = _flags, \
+ .fmax = MT8196_PLL_FMAX, \
+ .fmin = MT8196_PLL_FMIN, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8196_INTEGER_BITS, \
+ .en_reg = PLLEN_ALL, \
+ .en_set_reg = PLLEN_ALL_SET, \
+ .en_clr_reg = PLLEN_ALL_CLR, \
+ .pll_en_bit = _pll_en_bit, \
+ .ops = &mtk_pll_fenc_clr_set_ops, \
+}
+
+struct mtk_pll_desc {
+ const struct mtk_pll_data *clks;
+ size_t num_clks;
+};
+
+static const struct mtk_pll_data apmixed_plls[] = {
+ PLL_FENC(CLK_APMIXED_MAINPLL, "mainpll", MAINPLL_CON0, FENC_STATUS_CON0,
+ 7, PLL_AO, MAINPLL_CON1, 24, MAINPLL_CON1, 0, 22, 0),
+ PLL_FENC(CLK_APMIXED_UNIVPLL, "univpll", UNIVPLL_CON0, FENC_STATUS_CON0,
+ 6, 0, UNIVPLL_CON1, 24, UNIVPLL_CON1, 0, 22, 1),
+ PLL_FENC(CLK_APMIXED_MSDCPLL, "msdcpll", MSDCPLL_CON0, FENC_STATUS_CON0,
+ 5, 0, MSDCPLL_CON1, 24, MSDCPLL_CON1, 0, 22, 2),
+ PLL_FENC(CLK_APMIXED_ADSPPLL, "adsppll", ADSPPLL_CON0, FENC_STATUS_CON0,
+ 4, 0, ADSPPLL_CON1, 24, ADSPPLL_CON1, 0, 22, 3),
+ PLL_FENC(CLK_APMIXED_EMIPLL, "emipll", EMIPLL_CON0, FENC_STATUS_CON0, 3,
+ PLL_AO, EMIPLL_CON1, 24, EMIPLL_CON1, 0, 22, 4),
+ PLL_FENC(CLK_APMIXED_EMIPLL2, "emipll2", EMIPLL2_CON0, FENC_STATUS_CON0,
+ 2, PLL_AO, EMIPLL2_CON1, 24, EMIPLL2_CON1, 0, 22, 5),
+ PLL_FENC(CLK_APMIXED_NET1PLL, "net1pll", NET1PLL_CON0, FENC_STATUS_CON0,
+ 1, 0, NET1PLL_CON1, 24, NET1PLL_CON1, 0, 22, 6),
+ PLL_FENC(CLK_APMIXED_SGMIIPLL, "sgmiipll", SGMIIPLL_CON0, FENC_STATUS_CON0,
+ 0, 0, SGMIIPLL_CON1, 24, SGMIIPLL_CON1, 0, 22, 7),
+};
+
+static const struct mtk_pll_desc apmixed_desc = {
+ .clks = apmixed_plls,
+ .num_clks = ARRAY_SIZE(apmixed_plls),
+};
+
+static const struct mtk_pll_data apmixed2_plls[] = {
+ PLL_FENC(CLK_APMIXED2_MAINPLL2, "mainpll2", MAINPLL2_CON0, FENC_STATUS_CON0,
+ 6, 0, MAINPLL2_CON1, 24, MAINPLL2_CON1, 0, 22, 0),
+ PLL_FENC(CLK_APMIXED2_UNIVPLL2, "univpll2", UNIVPLL2_CON0, FENC_STATUS_CON0,
+ 5, 0, UNIVPLL2_CON1, 24, UNIVPLL2_CON1, 0, 22, 1),
+ PLL_FENC(CLK_APMIXED2_MMPLL2, "mmpll2", MMPLL2_CON0, FENC_STATUS_CON0,
+ 4, 0, MMPLL2_CON1, 24, MMPLL2_CON1, 0, 22, 2),
+ PLL_FENC(CLK_APMIXED2_IMGPLL, "imgpll", IMGPLL_CON0, FENC_STATUS_CON0,
+ 3, 0, IMGPLL_CON1, 24, IMGPLL_CON1, 0, 22, 3),
+ PLL_FENC(CLK_APMIXED2_TVDPLL1, "tvdpll1", TVDPLL1_CON0, FENC_STATUS_CON0,
+ 2, 0, TVDPLL1_CON1, 24, TVDPLL1_CON1, 0, 22, 4),
+ PLL_FENC(CLK_APMIXED2_TVDPLL2, "tvdpll2", TVDPLL2_CON0, FENC_STATUS_CON0,
+ 1, 0, TVDPLL2_CON1, 24, TVDPLL2_CON1, 0, 22, 5),
+ PLL_FENC(CLK_APMIXED2_TVDPLL3, "tvdpll3", TVDPLL3_CON0, FENC_STATUS_CON0,
+ 0, 0, TVDPLL3_CON1, 24, TVDPLL3_CON1, 0, 22, 6),
+};
+
+static const struct mtk_pll_desc apmixed2_desc = {
+ .clks = apmixed2_plls,
+ .num_clks = ARRAY_SIZE(apmixed2_plls),
+};
+
+static int clk_mt8196_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ const struct mtk_pll_desc *mcd;
+ int r;
+
+ mcd = device_get_match_data(&pdev->dev);
+ if (!mcd)
+ return -EINVAL;
+
+ clk_data = mtk_alloc_clk_data(mcd->num_clks);
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_plls(node, mcd->clks, mcd->num_clks, clk_data);
+ if (r)
+ goto free_apmixed_data;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ return r;
+
+unregister_plls:
+ mtk_clk_unregister_plls(mcd->clks, mcd->num_clks, clk_data);
+free_apmixed_data:
+ mtk_free_clk_data(clk_data);
+ return r;
+}
+
+static void clk_mt8196_apmixed_remove(struct platform_device *pdev)
+{
+ const struct mtk_pll_desc *mcd = device_get_match_data(&pdev->dev);
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(mcd->clks, mcd->num_clks, clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static const struct of_device_id of_match_clk_mt8196_apmixed[] = {
+ { .compatible = "mediatek,mt8196-apmixedsys", .data = &apmixed_desc },
+ { .compatible = "mediatek,mt8196-apmixedsys-gp2",
+ .data = &apmixed2_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_apmixed);
+
+static struct platform_driver clk_mt8196_apmixed_drv = {
+ .probe = clk_mt8196_apmixed_probe,
+ .remove = clk_mt8196_apmixed_remove,
+ .driver = {
+ .name = "clk-mt8196-apmixed",
+ .of_match_table = of_match_clk_mt8196_apmixed,
+ },
+};
+module_platform_driver(clk_mt8196_apmixed_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-disp0.c b/drivers/clk/mediatek/clk-mt8196-disp0.c
new file mode 100644
index 000000000000..9474aad26e92
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-disp0.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mm0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs mm0_hwv_regs = {
+ .set_ofs = 0x0020,
+ .clr_ofs = 0x0024,
+ .sta_ofs = 0x2c10,
+};
+
+static const struct mtk_gate_regs mm1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs mm1_hwv_regs = {
+ .set_ofs = 0x0028,
+ .clr_ofs = 0x002c,
+ .sta_ofs = 0x2c14,
+};
+
+#define GATE_MM0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm0_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr,\
+ }
+
+#define GATE_HWV_MM0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm0_cg_regs, \
+ .hwv_regs = &mm0_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE \
+ }
+
+#define GATE_MM1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm1_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr,\
+ }
+
+#define GATE_HWV_MM1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm1_cg_regs, \
+ .hwv_regs = &mm1_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+static const struct mtk_gate mm_clks[] = {
+ /* MM0 */
+ GATE_HWV_MM0(CLK_MM_CONFIG, "mm_config", "disp", 0),
+ GATE_HWV_MM0(CLK_MM_DISP_MUTEX0, "mm_disp_mutex0", "disp", 1),
+ GATE_HWV_MM0(CLK_MM_DISP_AAL0, "mm_disp_aal0", "disp", 2),
+ GATE_HWV_MM0(CLK_MM_DISP_AAL1, "mm_disp_aal1", "disp", 3),
+ GATE_MM0(CLK_MM_DISP_C3D0, "mm_disp_c3d0", "disp", 4),
+ GATE_MM0(CLK_MM_DISP_C3D1, "mm_disp_c3d1", "disp", 5),
+ GATE_MM0(CLK_MM_DISP_C3D2, "mm_disp_c3d2", "disp", 6),
+ GATE_MM0(CLK_MM_DISP_C3D3, "mm_disp_c3d3", "disp", 7),
+ GATE_MM0(CLK_MM_DISP_CCORR0, "mm_disp_ccorr0", "disp", 8),
+ GATE_MM0(CLK_MM_DISP_CCORR1, "mm_disp_ccorr1", "disp", 9),
+ GATE_MM0(CLK_MM_DISP_CCORR2, "mm_disp_ccorr2", "disp", 10),
+ GATE_MM0(CLK_MM_DISP_CCORR3, "mm_disp_ccorr3", "disp", 11),
+ GATE_MM0(CLK_MM_DISP_CHIST0, "mm_disp_chist0", "disp", 12),
+ GATE_MM0(CLK_MM_DISP_CHIST1, "mm_disp_chist1", "disp", 13),
+ GATE_MM0(CLK_MM_DISP_COLOR0, "mm_disp_color0", "disp", 14),
+ GATE_MM0(CLK_MM_DISP_COLOR1, "mm_disp_color1", "disp", 15),
+ GATE_MM0(CLK_MM_DISP_DITHER0, "mm_disp_dither0", "disp", 16),
+ GATE_MM0(CLK_MM_DISP_DITHER1, "mm_disp_dither1", "disp", 17),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC0, "mm_disp_dli_async0", "disp", 18),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC1, "mm_disp_dli_async1", "disp", 19),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC2, "mm_disp_dli_async2", "disp", 20),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC3, "mm_disp_dli_async3", "disp", 21),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC4, "mm_disp_dli_async4", "disp", 22),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC5, "mm_disp_dli_async5", "disp", 23),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC6, "mm_disp_dli_async6", "disp", 24),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC7, "mm_disp_dli_async7", "disp", 25),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC8, "mm_disp_dli_async8", "disp", 26),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC9, "mm_disp_dli_async9", "disp", 27),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC10, "mm_disp_dli_async10", "disp", 28),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC11, "mm_disp_dli_async11", "disp", 29),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC12, "mm_disp_dli_async12", "disp", 30),
+ GATE_HWV_MM0(CLK_MM_DISP_DLI_ASYNC13, "mm_disp_dli_async13", "disp", 31),
+ /* MM1 */
+ GATE_HWV_MM1(CLK_MM_DISP_DLI_ASYNC14, "mm_disp_dli_async14", "disp", 0),
+ GATE_HWV_MM1(CLK_MM_DISP_DLI_ASYNC15, "mm_disp_dli_async15", "disp", 1),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC0, "mm_disp_dlo_async0", "disp", 2),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC1, "mm_disp_dlo_async1", "disp", 3),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC2, "mm_disp_dlo_async2", "disp", 4),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC3, "mm_disp_dlo_async3", "disp", 5),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC4, "mm_disp_dlo_async4", "disp", 6),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC5, "mm_disp_dlo_async5", "disp", 7),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC6, "mm_disp_dlo_async6", "disp", 8),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC7, "mm_disp_dlo_async7", "disp", 9),
+ GATE_HWV_MM1(CLK_MM_DISP_DLO_ASYNC8, "mm_disp_dlo_async8", "disp", 10),
+ GATE_MM1(CLK_MM_DISP_GAMMA0, "mm_disp_gamma0", "disp", 11),
+ GATE_MM1(CLK_MM_DISP_GAMMA1, "mm_disp_gamma1", "disp", 12),
+ GATE_MM1(CLK_MM_MDP_AAL0, "mm_mdp_aal0", "disp", 13),
+ GATE_MM1(CLK_MM_MDP_AAL1, "mm_mdp_aal1", "disp", 14),
+ GATE_HWV_MM1(CLK_MM_MDP_RDMA0, "mm_mdp_rdma0", "disp", 15),
+ GATE_HWV_MM1(CLK_MM_DISP_POSTMASK0, "mm_disp_postmask0", "disp", 16),
+ GATE_HWV_MM1(CLK_MM_DISP_POSTMASK1, "mm_disp_postmask1", "disp", 17),
+ GATE_HWV_MM1(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "disp", 18),
+ GATE_HWV_MM1(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "disp", 19),
+ GATE_HWV_MM1(CLK_MM_DISP_SPR0, "mm_disp_spr0", "disp", 20),
+ GATE_MM1(CLK_MM_DISP_TDSHP0, "mm_disp_tdshp0", "disp", 21),
+ GATE_MM1(CLK_MM_DISP_TDSHP1, "mm_disp_tdshp1", "disp", 22),
+ GATE_HWV_MM1(CLK_MM_DISP_WDMA0, "mm_disp_wdma0", "disp", 23),
+ GATE_HWV_MM1(CLK_MM_DISP_Y2R0, "mm_disp_y2r0", "disp", 24),
+ GATE_HWV_MM1(CLK_MM_SMI_SUB_COMM0, "mm_ssc", "disp", 25),
+ GATE_HWV_MM1(CLK_MM_DISP_FAKE_ENG0, "mm_disp_fake_eng0", "disp", 26),
+};
+
+static const struct mtk_clk_desc mm_mcd = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
+
+static const struct platform_device_id clk_mt8196_disp0_id_table[] = {
+ { .name = "clk-mt8196-disp0", .driver_data = (kernel_ulong_t)&mm_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8196_disp0_id_table);
+
+static struct platform_driver clk_mt8196_disp0_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8196-disp0",
+ },
+ .id_table = clk_mt8196_disp0_id_table,
+};
+module_platform_driver(clk_mt8196_disp0_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 disp0 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-disp1.c b/drivers/clk/mediatek/clk-mt8196-disp1.c
new file mode 100644
index 000000000000..3bbec79a7010
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-disp1.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mm10_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs mm10_hwv_regs = {
+ .set_ofs = 0x0010,
+ .clr_ofs = 0x0014,
+ .sta_ofs = 0x2c08,
+};
+
+static const struct mtk_gate_regs mm11_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs mm11_hwv_regs = {
+ .set_ofs = 0x0018,
+ .clr_ofs = 0x001c,
+ .sta_ofs = 0x2c0c,
+};
+
+#define GATE_MM10(_id, _name, _parent, _shift) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm10_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr,\
+ }
+
+#define GATE_HWV_MM10(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm10_cg_regs, \
+ .hwv_regs = &mm10_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_MM11(_id, _name, _parent, _shift) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm11_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr,\
+ }
+
+#define GATE_HWV_MM11(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm11_cg_regs, \
+ .hwv_regs = &mm11_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ }
+
+static const struct mtk_gate mm1_clks[] = {
+ /* MM10 */
+ GATE_HWV_MM10(CLK_MM1_DISPSYS1_CONFIG, "mm1_dispsys1_config", "disp", 0),
+ GATE_HWV_MM10(CLK_MM1_DISPSYS1_S_CONFIG, "mm1_dispsys1_s_config", "disp", 1),
+ GATE_HWV_MM10(CLK_MM1_DISP_MUTEX0, "mm1_disp_mutex0", "disp", 2),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC20, "mm1_disp_dli_async20", "disp", 3),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC21, "mm1_disp_dli_async21", "disp", 4),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC22, "mm1_disp_dli_async22", "disp", 5),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC23, "mm1_disp_dli_async23", "disp", 6),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC24, "mm1_disp_dli_async24", "disp", 7),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC25, "mm1_disp_dli_async25", "disp", 8),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC26, "mm1_disp_dli_async26", "disp", 9),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC27, "mm1_disp_dli_async27", "disp", 10),
+ GATE_HWV_MM10(CLK_MM1_DISP_DLI_ASYNC28, "mm1_disp_dli_async28", "disp", 11),
+ GATE_HWV_MM10(CLK_MM1_DISP_RELAY0, "mm1_disp_relay0", "disp", 12),
+ GATE_HWV_MM10(CLK_MM1_DISP_RELAY1, "mm1_disp_relay1", "disp", 13),
+ GATE_HWV_MM10(CLK_MM1_DISP_RELAY2, "mm1_disp_relay2", "disp", 14),
+ GATE_HWV_MM10(CLK_MM1_DISP_RELAY3, "mm1_disp_relay3", "disp", 15),
+ GATE_HWV_MM10(CLK_MM1_DISP_DP_INTF0, "mm1_DP_CLK", "disp", 16),
+ GATE_HWV_MM10(CLK_MM1_DISP_DP_INTF1, "mm1_disp_dp_intf1", "disp", 17),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSC_WRAP0, "mm1_disp_dsc_wrap0", "disp", 18),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSC_WRAP1, "mm1_disp_dsc_wrap1", "disp", 19),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSC_WRAP2, "mm1_disp_dsc_wrap2", "disp", 20),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSC_WRAP3, "mm1_disp_dsc_wrap3", "disp", 21),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSI0, "mm1_CLK0", "disp", 22),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSI1, "mm1_CLK1", "disp", 23),
+ GATE_HWV_MM10(CLK_MM1_DISP_DSI2, "mm1_CLK2", "disp", 24),
+ GATE_HWV_MM10(CLK_MM1_DISP_DVO0, "mm1_disp_dvo0", "disp", 25),
+ GATE_HWV_MM10(CLK_MM1_DISP_GDMA0, "mm1_disp_gdma0", "disp", 26),
+ GATE_HWV_MM10(CLK_MM1_DISP_MERGE0, "mm1_disp_merge0", "disp", 27),
+ GATE_HWV_MM10(CLK_MM1_DISP_MERGE1, "mm1_disp_merge1", "disp", 28),
+ GATE_HWV_MM10(CLK_MM1_DISP_MERGE2, "mm1_disp_merge2", "disp", 29),
+ GATE_HWV_MM10(CLK_MM1_DISP_ODDMR0, "mm1_disp_oddmr0", "disp", 30),
+ GATE_HWV_MM10(CLK_MM1_DISP_POSTALIGN0, "mm1_disp_postalign0", "disp", 31),
+ /* MM11 */
+ GATE_HWV_MM11(CLK_MM1_DISP_DITHER2, "mm1_disp_dither2", "disp", 0),
+ GATE_HWV_MM11(CLK_MM1_DISP_R2Y0, "mm1_disp_r2y0", "disp", 1),
+ GATE_HWV_MM11(CLK_MM1_DISP_SPLITTER0, "mm1_disp_splitter0", "disp", 2),
+ GATE_HWV_MM11(CLK_MM1_DISP_SPLITTER1, "mm1_disp_splitter1", "disp", 3),
+ GATE_HWV_MM11(CLK_MM1_DISP_SPLITTER2, "mm1_disp_splitter2", "disp", 4),
+ GATE_HWV_MM11(CLK_MM1_DISP_SPLITTER3, "mm1_disp_splitter3", "disp", 5),
+ GATE_HWV_MM11(CLK_MM1_DISP_VDCM0, "mm1_disp_vdcm0", "disp", 6),
+ GATE_HWV_MM11(CLK_MM1_DISP_WDMA1, "mm1_disp_wdma1", "disp", 7),
+ GATE_HWV_MM11(CLK_MM1_DISP_WDMA2, "mm1_disp_wdma2", "disp", 8),
+ GATE_HWV_MM11(CLK_MM1_DISP_WDMA3, "mm1_disp_wdma3", "disp", 9),
+ GATE_HWV_MM11(CLK_MM1_DISP_WDMA4, "mm1_disp_wdma4", "disp", 10),
+ GATE_HWV_MM11(CLK_MM1_MDP_RDMA1, "mm1_mdp_rdma1", "disp", 11),
+ GATE_HWV_MM11(CLK_MM1_SMI_LARB0, "mm1_smi_larb0", "disp", 12),
+ GATE_HWV_MM11(CLK_MM1_MOD1, "mm1_mod1", "clk26m", 13),
+ GATE_HWV_MM11(CLK_MM1_MOD2, "mm1_mod2", "clk26m", 14),
+ GATE_HWV_MM11(CLK_MM1_MOD3, "mm1_mod3", "clk26m", 15),
+ GATE_HWV_MM11(CLK_MM1_MOD4, "mm1_mod4", "dp0", 16),
+ GATE_HWV_MM11(CLK_MM1_MOD5, "mm1_mod5", "dp1", 17),
+ GATE_HWV_MM11(CLK_MM1_MOD6, "mm1_mod6", "dp1", 18),
+ GATE_HWV_MM11(CLK_MM1_CG0, "mm1_cg0", "disp", 20),
+ GATE_HWV_MM11(CLK_MM1_CG1, "mm1_cg1", "disp", 21),
+ GATE_HWV_MM11(CLK_MM1_CG2, "mm1_cg2", "disp", 22),
+ GATE_HWV_MM11(CLK_MM1_CG3, "mm1_cg3", "disp", 23),
+ GATE_HWV_MM11(CLK_MM1_CG4, "mm1_cg4", "disp", 24),
+ GATE_HWV_MM11(CLK_MM1_CG5, "mm1_cg5", "disp", 25),
+ GATE_HWV_MM11(CLK_MM1_CG6, "mm1_cg6", "disp", 26),
+ GATE_HWV_MM11(CLK_MM1_CG7, "mm1_cg7", "disp", 27),
+ GATE_HWV_MM11(CLK_MM1_F26M, "mm1_f26m_ck", "clk26m", 28),
+};
+
+static const struct mtk_clk_desc mm1_mcd = {
+ .clks = mm1_clks,
+ .num_clks = ARRAY_SIZE(mm1_clks),
+};
+
+static const struct platform_device_id clk_mt8196_disp1_id_table[] = {
+ { .name = "clk-mt8196-disp1", .driver_data = (kernel_ulong_t)&mm1_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8196_disp1_id_table);
+
+static struct platform_driver clk_mt8196_disp1_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8196-disp1",
+ },
+ .id_table = clk_mt8196_disp1_id_table,
+};
+module_platform_driver(clk_mt8196_disp1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 disp1 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-imp_iic_wrap.c b/drivers/clk/mediatek/clk-mt8196-imp_iic_wrap.c
new file mode 100644
index 000000000000..a63241671650
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-imp_iic_wrap.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs imp_cg_regs = {
+ .set_ofs = 0xe08,
+ .clr_ofs = 0xe04,
+ .sta_ofs = 0xe00,
+};
+
+#define GATE_IMP(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &imp_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate impc_clks[] = {
+ GATE_IMP(CLK_IMPC_I2C11, "impc_i2c11", "i2c_p", 0),
+ GATE_IMP(CLK_IMPC_I2C12, "impc_i2c12", "i2c_p", 1),
+ GATE_IMP(CLK_IMPC_I2C13, "impc_i2c13", "i2c_p", 2),
+ GATE_IMP(CLK_IMPC_I2C14, "impc_i2c14", "i2c_p", 3),
+};
+
+static const struct mtk_clk_desc impc_mcd = {
+ .clks = impc_clks,
+ .num_clks = ARRAY_SIZE(impc_clks),
+};
+
+static const struct mtk_gate impe_clks[] = {
+ GATE_IMP(CLK_IMPE_I2C5, "impe_i2c5", "i2c_east", 0),
+};
+
+static const struct mtk_clk_desc impe_mcd = {
+ .clks = impe_clks,
+ .num_clks = ARRAY_SIZE(impe_clks),
+};
+
+static const struct mtk_gate_regs impn_hwv_regs = {
+ .set_ofs = 0x0000,
+ .clr_ofs = 0x0004,
+ .sta_ofs = 0x2c00,
+};
+
+#define GATE_HWV_IMPN(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &imp_cg_regs, \
+ .hwv_regs = &impn_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+static const struct mtk_gate impn_clks[] = {
+ GATE_IMP(CLK_IMPN_I2C1, "impn_i2c1", "i2c_north", 0),
+ GATE_IMP(CLK_IMPN_I2C2, "impn_i2c2", "i2c_north", 1),
+ GATE_IMP(CLK_IMPN_I2C4, "impn_i2c4", "i2c_north", 2),
+ GATE_HWV_IMPN(CLK_IMPN_I2C7, "impn_i2c7", "i2c_north", 3),
+ GATE_IMP(CLK_IMPN_I2C8, "impn_i2c8", "i2c_north", 4),
+ GATE_IMP(CLK_IMPN_I2C9, "impn_i2c9", "i2c_north", 5),
+};
+
+static const struct mtk_clk_desc impn_mcd = {
+ .clks = impn_clks,
+ .num_clks = ARRAY_SIZE(impn_clks),
+};
+
+static const struct mtk_gate impw_clks[] = {
+ GATE_IMP(CLK_IMPW_I2C0, "impw_i2c0", "i2c_west", 0),
+ GATE_IMP(CLK_IMPW_I2C3, "impw_i2c3", "i2c_west", 1),
+ GATE_IMP(CLK_IMPW_I2C6, "impw_i2c6", "i2c_west", 2),
+ GATE_IMP(CLK_IMPW_I2C10, "impw_i2c10", "i2c_west", 3),
+};
+
+static const struct mtk_clk_desc impw_mcd = {
+ .clks = impw_clks,
+ .num_clks = ARRAY_SIZE(impw_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8196_imp_iic_wrap[] = {
+ { .compatible = "mediatek,mt8196-imp-iic-wrap-c", .data = &impc_mcd },
+ { .compatible = "mediatek,mt8196-imp-iic-wrap-e", .data = &impe_mcd },
+ { .compatible = "mediatek,mt8196-imp-iic-wrap-n", .data = &impn_mcd },
+ { .compatible = "mediatek,mt8196-imp-iic-wrap-w", .data = &impw_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_imp_iic_wrap);
+
+static struct platform_driver clk_mt8196_imp_iic_wrap_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-imp_iic_wrap",
+ .of_match_table = of_match_clk_mt8196_imp_iic_wrap,
+ },
+};
+module_platform_driver(clk_mt8196_imp_iic_wrap_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 I2C Wrapper clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-mcu.c b/drivers/clk/mediatek/clk-mt8196-mcu.c
new file mode 100644
index 000000000000..5cbcc411ae73
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-mcu.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#define ARMPLL_LL_CON0 0x008
+#define ARMPLL_LL_CON1 0x00c
+#define ARMPLL_LL_CON2 0x010
+#define ARMPLL_LL_CON3 0x014
+#define ARMPLL_BL_CON0 0x008
+#define ARMPLL_BL_CON1 0x00c
+#define ARMPLL_BL_CON2 0x010
+#define ARMPLL_BL_CON3 0x014
+#define ARMPLL_B_CON0 0x008
+#define ARMPLL_B_CON1 0x00c
+#define ARMPLL_B_CON2 0x010
+#define ARMPLL_B_CON3 0x014
+#define CCIPLL_CON0 0x008
+#define CCIPLL_CON1 0x00c
+#define CCIPLL_CON2 0x010
+#define CCIPLL_CON3 0x014
+#define PTPPLL_CON0 0x008
+#define PTPPLL_CON1 0x00c
+#define PTPPLL_CON2 0x010
+#define PTPPLL_CON3 0x014
+
+#define MT8196_PLL_FMAX (3800UL * MHZ)
+#define MT8196_PLL_FMIN (1500UL * MHZ)
+#define MT8196_INTEGER_BITS 8
+
+#define PLL(_id, _name, _reg, _en_reg, _en_mask, _pll_en_bit, \
+ _flags, _rst_bar_mask, \
+ _pd_reg, _pd_shift, _tuner_reg, \
+ _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, _pcwbits) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .en_reg = _en_reg, \
+ .en_mask = _en_mask, \
+ .pll_en_bit = _pll_en_bit, \
+ .flags = _flags, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8196_PLL_FMAX, \
+ .fmin = MT8196_PLL_FMIN, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8196_INTEGER_BITS, \
+ }
+
+static const struct mtk_pll_data cpu_bl_plls[] = {
+ PLL(CLK_CPBL_ARMPLL_BL, "armpll-bl", ARMPLL_BL_CON0, ARMPLL_BL_CON0, 0,
+ 0, PLL_AO, BIT(0), ARMPLL_BL_CON1, 24, 0, 0, 0, ARMPLL_BL_CON1, 0, 22),
+};
+
+static const struct mtk_pll_data cpu_b_plls[] = {
+ PLL(CLK_CPB_ARMPLL_B, "armpll-b", ARMPLL_B_CON0, ARMPLL_B_CON0, 0, 0,
+ PLL_AO, BIT(0), ARMPLL_B_CON1, 24, 0, 0, 0, ARMPLL_B_CON1, 0, 22),
+};
+
+static const struct mtk_pll_data cpu_ll_plls[] = {
+ PLL(CLK_CPLL_ARMPLL_LL, "armpll-ll", ARMPLL_LL_CON0, ARMPLL_LL_CON0, 0,
+ 0, PLL_AO, BIT(0), ARMPLL_LL_CON1, 24, 0, 0, 0, ARMPLL_LL_CON1, 0, 22),
+};
+
+static const struct mtk_pll_data cci_plls[] = {
+ PLL(CLK_CCIPLL, "ccipll", CCIPLL_CON0, CCIPLL_CON0, 0, 0, PLL_AO,
+ BIT(0), CCIPLL_CON1, 24, 0, 0, 0, CCIPLL_CON1, 0, 22),
+};
+
+static const struct mtk_pll_data ptp_plls[] = {
+ PLL(CLK_PTPPLL, "ptppll", PTPPLL_CON0, PTPPLL_CON0, 0, 0, PLL_AO,
+ BIT(0), PTPPLL_CON1, 24, 0, 0, 0, PTPPLL_CON1, 0, 22),
+};
+
+static const struct of_device_id of_match_clk_mt8196_mcu[] = {
+ { .compatible = "mediatek,mt8196-armpll-bl-pll-ctrl",
+ .data = &cpu_bl_plls },
+ { .compatible = "mediatek,mt8196-armpll-b-pll-ctrl",
+ .data = &cpu_b_plls },
+ { .compatible = "mediatek,mt8196-armpll-ll-pll-ctrl",
+ .data = &cpu_ll_plls },
+ { .compatible = "mediatek,mt8196-ccipll-pll-ctrl", .data = &cci_plls },
+ { .compatible = "mediatek,mt8196-ptppll-pll-ctrl", .data = &ptp_plls },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_mcu);
+
+static int clk_mt8196_mcu_probe(struct platform_device *pdev)
+{
+ const struct mtk_pll_data *plls;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ const int num_plls = 1;
+ int r;
+
+ plls = of_device_get_match_data(&pdev->dev);
+ if (!plls)
+ return -EINVAL;
+
+ clk_data = mtk_alloc_clk_data(num_plls);
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_plls(node, plls, num_plls, clk_data);
+ if (r)
+ goto free_clk_data;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ return r;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, num_plls, clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+
+ return r;
+}
+
+static void clk_mt8196_mcu_remove(struct platform_device *pdev)
+{
+ const struct mtk_pll_data *plls = of_device_get_match_data(&pdev->dev);
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(plls, 1, clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8196_mcu_drv = {
+ .probe = clk_mt8196_mcu_probe,
+ .remove = clk_mt8196_mcu_remove,
+ .driver = {
+ .name = "clk-mt8196-mcu",
+ .of_match_table = of_match_clk_mt8196_mcu,
+ },
+};
+module_platform_driver(clk_mt8196_mcu_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 mcusys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-mdpsys.c b/drivers/clk/mediatek/clk-mt8196-mdpsys.c
new file mode 100644
index 000000000000..7667d88f0eb0
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-mdpsys.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mdp0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs mdp1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs mdp2_cg_regs = {
+ .set_ofs = 0x124,
+ .clr_ofs = 0x128,
+ .sta_ofs = 0x120,
+};
+
+#define GATE_MDP0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mdp0_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_MDP1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mdp1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_MDP2(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mdp2_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate mdp1_clks[] = {
+ /* MDP1-0 */
+ GATE_MDP0(CLK_MDP1_MDP_MUTEX0, "mdp1_mdp_mutex0", "mdp", 0),
+ GATE_MDP0(CLK_MDP1_SMI0, "mdp1_smi0", "mdp", 1),
+ GATE_MDP0(CLK_MDP1_APB_BUS, "mdp1_apb_bus", "mdp", 2),
+ GATE_MDP0(CLK_MDP1_MDP_RDMA0, "mdp1_mdp_rdma0", "mdp", 3),
+ GATE_MDP0(CLK_MDP1_MDP_RDMA1, "mdp1_mdp_rdma1", "mdp", 4),
+ GATE_MDP0(CLK_MDP1_MDP_RDMA2, "mdp1_mdp_rdma2", "mdp", 5),
+ GATE_MDP0(CLK_MDP1_MDP_BIRSZ0, "mdp1_mdp_birsz0", "mdp", 6),
+ GATE_MDP0(CLK_MDP1_MDP_HDR0, "mdp1_mdp_hdr0", "mdp", 7),
+ GATE_MDP0(CLK_MDP1_MDP_AAL0, "mdp1_mdp_aal0", "mdp", 8),
+ GATE_MDP0(CLK_MDP1_MDP_RSZ0, "mdp1_mdp_rsz0", "mdp", 9),
+ GATE_MDP0(CLK_MDP1_MDP_RSZ2, "mdp1_mdp_rsz2", "mdp", 10),
+ GATE_MDP0(CLK_MDP1_MDP_TDSHP0, "mdp1_mdp_tdshp0", "mdp", 11),
+ GATE_MDP0(CLK_MDP1_MDP_COLOR0, "mdp1_mdp_color0", "mdp", 12),
+ GATE_MDP0(CLK_MDP1_MDP_WROT0, "mdp1_mdp_wrot0", "mdp", 13),
+ GATE_MDP0(CLK_MDP1_MDP_WROT1, "mdp1_mdp_wrot1", "mdp", 14),
+ GATE_MDP0(CLK_MDP1_MDP_WROT2, "mdp1_mdp_wrot2", "mdp", 15),
+ GATE_MDP0(CLK_MDP1_MDP_FAKE_ENG0, "mdp1_mdp_fake_eng0", "mdp", 16),
+ GATE_MDP0(CLK_MDP1_APB_DB, "mdp1_apb_db", "mdp", 17),
+ GATE_MDP0(CLK_MDP1_MDP_DLI_ASYNC0, "mdp1_mdp_dli_async0", "mdp", 18),
+ GATE_MDP0(CLK_MDP1_MDP_DLI_ASYNC1, "mdp1_mdp_dli_async1", "mdp", 19),
+ GATE_MDP0(CLK_MDP1_MDP_DLO_ASYNC0, "mdp1_mdp_dlo_async0", "mdp", 20),
+ GATE_MDP0(CLK_MDP1_MDP_DLO_ASYNC1, "mdp1_mdp_dlo_async1", "mdp", 21),
+ GATE_MDP0(CLK_MDP1_MDP_DLI_ASYNC2, "mdp1_mdp_dli_async2", "mdp", 22),
+ GATE_MDP0(CLK_MDP1_MDP_DLO_ASYNC2, "mdp1_mdp_dlo_async2", "mdp", 23),
+ GATE_MDP0(CLK_MDP1_MDP_DLO_ASYNC3, "mdp1_mdp_dlo_async3", "mdp", 24),
+ GATE_MDP0(CLK_MDP1_IMG_DL_ASYNC0, "mdp1_img_dl_async0", "mdp", 25),
+ GATE_MDP0(CLK_MDP1_MDP_RROT0, "mdp1_mdp_rrot0", "mdp", 26),
+ GATE_MDP0(CLK_MDP1_MDP_MERGE0, "mdp1_mdp_merge0", "mdp", 27),
+ GATE_MDP0(CLK_MDP1_MDP_C3D0, "mdp1_mdp_c3d0", "mdp", 28),
+ GATE_MDP0(CLK_MDP1_MDP_FG0, "mdp1_mdp_fg0", "mdp", 29),
+ GATE_MDP0(CLK_MDP1_MDP_CLA2, "mdp1_mdp_cla2", "mdp", 30),
+ GATE_MDP0(CLK_MDP1_MDP_DLO_ASYNC4, "mdp1_mdp_dlo_async4", "mdp", 31),
+ /* MDP1-1 */
+ GATE_MDP1(CLK_MDP1_VPP_RSZ0, "mdp1_vpp_rsz0", "mdp", 0),
+ GATE_MDP1(CLK_MDP1_VPP_RSZ1, "mdp1_vpp_rsz1", "mdp", 1),
+ GATE_MDP1(CLK_MDP1_MDP_DLO_ASYNC5, "mdp1_mdp_dlo_async5", "mdp", 2),
+ GATE_MDP1(CLK_MDP1_IMG0, "mdp1_img0", "mdp", 3),
+ GATE_MDP1(CLK_MDP1_F26M, "mdp1_f26m", "clk26m", 27),
+ /* MDP1-2 */
+ GATE_MDP2(CLK_MDP1_IMG_DL_RELAY0, "mdp1_img_dl_relay0", "mdp", 0),
+ GATE_MDP2(CLK_MDP1_IMG_DL_RELAY1, "mdp1_img_dl_relay1", "mdp", 8),
+};
+
+static const struct mtk_clk_desc mdp1_mcd = {
+ .clks = mdp1_clks,
+ .num_clks = ARRAY_SIZE(mdp1_clks),
+ .need_runtime_pm = true,
+};
+
+
+static const struct mtk_gate mdp_clks[] = {
+ /* MDP0 */
+ GATE_MDP0(CLK_MDP_MDP_MUTEX0, "mdp_mdp_mutex0", "mdp", 0),
+ GATE_MDP0(CLK_MDP_SMI0, "mdp_smi0", "mdp", 1),
+ GATE_MDP0(CLK_MDP_APB_BUS, "mdp_apb_bus", "mdp", 2),
+ GATE_MDP0(CLK_MDP_MDP_RDMA0, "mdp_mdp_rdma0", "mdp", 3),
+ GATE_MDP0(CLK_MDP_MDP_RDMA1, "mdp_mdp_rdma1", "mdp", 4),
+ GATE_MDP0(CLK_MDP_MDP_RDMA2, "mdp_mdp_rdma2", "mdp", 5),
+ GATE_MDP0(CLK_MDP_MDP_BIRSZ0, "mdp_mdp_birsz0", "mdp", 6),
+ GATE_MDP0(CLK_MDP_MDP_HDR0, "mdp_mdp_hdr0", "mdp", 7),
+ GATE_MDP0(CLK_MDP_MDP_AAL0, "mdp_mdp_aal0", "mdp", 8),
+ GATE_MDP0(CLK_MDP_MDP_RSZ0, "mdp_mdp_rsz0", "mdp", 9),
+ GATE_MDP0(CLK_MDP_MDP_RSZ2, "mdp_mdp_rsz2", "mdp", 10),
+ GATE_MDP0(CLK_MDP_MDP_TDSHP0, "mdp_mdp_tdshp0", "mdp", 11),
+ GATE_MDP0(CLK_MDP_MDP_COLOR0, "mdp_mdp_color0", "mdp", 12),
+ GATE_MDP0(CLK_MDP_MDP_WROT0, "mdp_mdp_wrot0", "mdp", 13),
+ GATE_MDP0(CLK_MDP_MDP_WROT1, "mdp_mdp_wrot1", "mdp", 14),
+ GATE_MDP0(CLK_MDP_MDP_WROT2, "mdp_mdp_wrot2", "mdp", 15),
+ GATE_MDP0(CLK_MDP_MDP_FAKE_ENG0, "mdp_mdp_fake_eng0", "mdp", 16),
+ GATE_MDP0(CLK_MDP_APB_DB, "mdp_apb_db", "mdp", 17),
+ GATE_MDP0(CLK_MDP_MDP_DLI_ASYNC0, "mdp_mdp_dli_async0", "mdp", 18),
+ GATE_MDP0(CLK_MDP_MDP_DLI_ASYNC1, "mdp_mdp_dli_async1", "mdp", 19),
+ GATE_MDP0(CLK_MDP_MDP_DLO_ASYNC0, "mdp_mdp_dlo_async0", "mdp", 20),
+ GATE_MDP0(CLK_MDP_MDP_DLO_ASYNC1, "mdp_mdp_dlo_async1", "mdp", 21),
+ GATE_MDP0(CLK_MDP_MDP_DLI_ASYNC2, "mdp_mdp_dli_async2", "mdp", 22),
+ GATE_MDP0(CLK_MDP_MDP_DLO_ASYNC2, "mdp_mdp_dlo_async2", "mdp", 23),
+ GATE_MDP0(CLK_MDP_MDP_DLO_ASYNC3, "mdp_mdp_dlo_async3", "mdp", 24),
+ GATE_MDP0(CLK_MDP_IMG_DL_ASYNC0, "mdp_img_dl_async0", "mdp", 25),
+ GATE_MDP0(CLK_MDP_MDP_RROT0, "mdp_mdp_rrot0", "mdp", 26),
+ GATE_MDP0(CLK_MDP_MDP_MERGE0, "mdp_mdp_merge0", "mdp", 27),
+ GATE_MDP0(CLK_MDP_MDP_C3D0, "mdp_mdp_c3d0", "mdp", 28),
+ GATE_MDP0(CLK_MDP_MDP_FG0, "mdp_mdp_fg0", "mdp", 29),
+ GATE_MDP0(CLK_MDP_MDP_CLA2, "mdp_mdp_cla2", "mdp", 30),
+ GATE_MDP0(CLK_MDP_MDP_DLO_ASYNC4, "mdp_mdp_dlo_async4", "mdp", 31),
+ /* MDP1 */
+ GATE_MDP1(CLK_MDP_VPP_RSZ0, "mdp_vpp_rsz0", "mdp", 0),
+ GATE_MDP1(CLK_MDP_VPP_RSZ1, "mdp_vpp_rsz1", "mdp", 1),
+ GATE_MDP1(CLK_MDP_MDP_DLO_ASYNC5, "mdp_mdp_dlo_async5", "mdp", 2),
+ GATE_MDP1(CLK_MDP_IMG0, "mdp_img0", "mdp", 3),
+ GATE_MDP1(CLK_MDP_F26M, "mdp_f26m", "clk26m", 27),
+ /* MDP2 */
+ GATE_MDP2(CLK_MDP_IMG_DL_RELAY0, "mdp_img_dl_relay0", "mdp", 0),
+ GATE_MDP2(CLK_MDP_IMG_DL_RELAY1, "mdp_img_dl_relay1", "mdp", 8),
+};
+
+static const struct mtk_clk_desc mdp_mcd = {
+ .clks = mdp_clks,
+ .num_clks = ARRAY_SIZE(mdp_clks),
+ .need_runtime_pm = true,
+};
+
+static const struct of_device_id of_match_clk_mt8196_mdpsys[] = {
+ { .compatible = "mediatek,mt8196-mdpsys0", .data = &mdp_mcd },
+ { .compatible = "mediatek,mt8196-mdpsys1", .data = &mdp1_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_mdpsys);
+
+static struct platform_driver clk_mt8196_mdpsys_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-mdpsys",
+ .of_match_table = of_match_clk_mt8196_mdpsys,
+ },
+};
+module_platform_driver(clk_mt8196_mdpsys_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 Multimedia Data Path clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-mfg.c b/drivers/clk/mediatek/clk-mt8196-mfg.c
new file mode 100644
index 000000000000..ae1eb9de79ae
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-mfg.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#define MFGPLL_CON0 0x008
+#define MFGPLL_CON1 0x00c
+#define MFGPLL_CON2 0x010
+#define MFGPLL_CON3 0x014
+#define MFGPLL_SC0_CON0 0x008
+#define MFGPLL_SC0_CON1 0x00c
+#define MFGPLL_SC0_CON2 0x010
+#define MFGPLL_SC0_CON3 0x014
+#define MFGPLL_SC1_CON0 0x008
+#define MFGPLL_SC1_CON1 0x00c
+#define MFGPLL_SC1_CON2 0x010
+#define MFGPLL_SC1_CON3 0x014
+
+#define MT8196_PLL_FMAX (3800UL * MHZ)
+#define MT8196_PLL_FMIN (1500UL * MHZ)
+#define MT8196_INTEGER_BITS 8
+
+#define PLL(_id, _name, _reg, _en_reg, _en_mask, _pll_en_bit, \
+ _flags, _rst_bar_mask, \
+ _pd_reg, _pd_shift, _tuner_reg, \
+ _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, _pcwbits) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .en_reg = _en_reg, \
+ .en_mask = _en_mask, \
+ .pll_en_bit = _pll_en_bit, \
+ .flags = _flags, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8196_PLL_FMAX, \
+ .fmin = MT8196_PLL_FMIN, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8196_INTEGER_BITS, \
+ }
+
+static const struct mtk_pll_data mfg_ao_plls[] = {
+ PLL(CLK_MFG_AO_MFGPLL, "mfgpll", MFGPLL_CON0, MFGPLL_CON0, 0, 0, 0,
+ BIT(0), MFGPLL_CON1, 24, 0, 0, 0,
+ MFGPLL_CON1, 0, 22),
+};
+
+static const struct mtk_pll_data mfgsc0_ao_plls[] = {
+ PLL(CLK_MFGSC0_AO_MFGPLL_SC0, "mfgpll-sc0", MFGPLL_SC0_CON0,
+ MFGPLL_SC0_CON0, 0, 0, 0, BIT(0), MFGPLL_SC0_CON1, 24, 0, 0, 0,
+ MFGPLL_SC0_CON1, 0, 22),
+};
+
+static const struct mtk_pll_data mfgsc1_ao_plls[] = {
+ PLL(CLK_MFGSC1_AO_MFGPLL_SC1, "mfgpll-sc1", MFGPLL_SC1_CON0,
+ MFGPLL_SC1_CON0, 0, 0, 0, BIT(0), MFGPLL_SC1_CON1, 24, 0, 0, 0,
+ MFGPLL_SC1_CON1, 0, 22),
+};
+
+static const struct of_device_id of_match_clk_mt8196_mfg[] = {
+ { .compatible = "mediatek,mt8196-mfgpll-pll-ctrl",
+ .data = &mfg_ao_plls },
+ { .compatible = "mediatek,mt8196-mfgpll-sc0-pll-ctrl",
+ .data = &mfgsc0_ao_plls },
+ { .compatible = "mediatek,mt8196-mfgpll-sc1-pll-ctrl",
+ .data = &mfgsc1_ao_plls },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_mfg);
+
+static int clk_mt8196_mfg_probe(struct platform_device *pdev)
+{
+ const struct mtk_pll_data *plls;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ const int num_plls = 1;
+ int r;
+
+ plls = of_device_get_match_data(&pdev->dev);
+ if (!plls)
+ return -EINVAL;
+
+ clk_data = mtk_alloc_clk_data(num_plls);
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_plls(node, plls, num_plls, clk_data);
+ if (r)
+ goto free_clk_data;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ return r;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, num_plls, clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+
+ return r;
+}
+
+static void clk_mt8196_mfg_remove(struct platform_device *pdev)
+{
+ const struct mtk_pll_data *plls = of_device_get_match_data(&pdev->dev);
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(plls, 1, clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static struct platform_driver clk_mt8196_mfg_drv = {
+ .probe = clk_mt8196_mfg_probe,
+ .remove = clk_mt8196_mfg_remove,
+ .driver = {
+ .name = "clk-mt8196-mfg",
+ .of_match_table = of_match_clk_mt8196_mfg,
+ },
+};
+module_platform_driver(clk_mt8196_mfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-ovl0.c b/drivers/clk/mediatek/clk-mt8196-ovl0.c
new file mode 100644
index 000000000000..d4affd14d2c4
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-ovl0.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs ovl0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs ovl0_hwv_regs = {
+ .set_ofs = 0x0060,
+ .clr_ofs = 0x0064,
+ .sta_ofs = 0x2c30,
+};
+
+static const struct mtk_gate_regs ovl1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs ovl1_hwv_regs = {
+ .set_ofs = 0x0068,
+ .clr_ofs = 0x006c,
+ .sta_ofs = 0x2c34,
+};
+
+#define GATE_HWV_OVL0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ovl0_cg_regs, \
+ .hwv_regs = &ovl0_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_OVL1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ovl1_cg_regs, \
+ .hwv_regs = &ovl1_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+static const struct mtk_gate ovl_clks[] = {
+ /* OVL0 */
+ GATE_HWV_OVL0(CLK_OVLSYS_CONFIG, "ovlsys_config", "disp", 0),
+ GATE_HWV_OVL0(CLK_OVL_FAKE_ENG0, "ovl_fake_eng0", "disp", 1),
+ GATE_HWV_OVL0(CLK_OVL_FAKE_ENG1, "ovl_fake_eng1", "disp", 2),
+ GATE_HWV_OVL0(CLK_OVL_MUTEX0, "ovl_mutex0", "disp", 3),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA0, "ovl_exdma0", "disp", 4),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA1, "ovl_exdma1", "disp", 5),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA2, "ovl_exdma2", "disp", 6),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA3, "ovl_exdma3", "disp", 7),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA4, "ovl_exdma4", "disp", 8),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA5, "ovl_exdma5", "disp", 9),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA6, "ovl_exdma6", "disp", 10),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA7, "ovl_exdma7", "disp", 11),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA8, "ovl_exdma8", "disp", 12),
+ GATE_HWV_OVL0(CLK_OVL_EXDMA9, "ovl_exdma9", "disp", 13),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER0, "ovl_blender0", "disp", 14),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER1, "ovl_blender1", "disp", 15),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER2, "ovl_blender2", "disp", 16),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER3, "ovl_blender3", "disp", 17),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER4, "ovl_blender4", "disp", 18),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER5, "ovl_blender5", "disp", 19),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER6, "ovl_blender6", "disp", 20),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER7, "ovl_blender7", "disp", 21),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER8, "ovl_blender8", "disp", 22),
+ GATE_HWV_OVL0(CLK_OVL_BLENDER9, "ovl_blender9", "disp", 23),
+ GATE_HWV_OVL0(CLK_OVL_OUTPROC0, "ovl_outproc0", "disp", 24),
+ GATE_HWV_OVL0(CLK_OVL_OUTPROC1, "ovl_outproc1", "disp", 25),
+ GATE_HWV_OVL0(CLK_OVL_OUTPROC2, "ovl_outproc2", "disp", 26),
+ GATE_HWV_OVL0(CLK_OVL_OUTPROC3, "ovl_outproc3", "disp", 27),
+ GATE_HWV_OVL0(CLK_OVL_OUTPROC4, "ovl_outproc4", "disp", 28),
+ GATE_HWV_OVL0(CLK_OVL_OUTPROC5, "ovl_outproc5", "disp", 29),
+ GATE_HWV_OVL0(CLK_OVL_MDP_RSZ0, "ovl_mdp_rsz0", "disp", 30),
+ GATE_HWV_OVL0(CLK_OVL_MDP_RSZ1, "ovl_mdp_rsz1", "disp", 31),
+ /* OVL1 */
+ GATE_HWV_OVL1(CLK_OVL_DISP_WDMA0, "ovl_disp_wdma0", "disp", 0),
+ GATE_HWV_OVL1(CLK_OVL_DISP_WDMA1, "ovl_disp_wdma1", "disp", 1),
+ GATE_HWV_OVL1(CLK_OVL_UFBC_WDMA0, "ovl_ufbc_wdma0", "disp", 2),
+ GATE_HWV_OVL1(CLK_OVL_MDP_RDMA0, "ovl_mdp_rdma0", "disp", 3),
+ GATE_HWV_OVL1(CLK_OVL_MDP_RDMA1, "ovl_mdp_rdma1", "disp", 4),
+ GATE_HWV_OVL1(CLK_OVL_BWM0, "ovl_bwm0", "disp", 5),
+ GATE_HWV_OVL1(CLK_OVL_DLI0, "ovl_dli0", "disp", 6),
+ GATE_HWV_OVL1(CLK_OVL_DLI1, "ovl_dli1", "disp", 7),
+ GATE_HWV_OVL1(CLK_OVL_DLI2, "ovl_dli2", "disp", 8),
+ GATE_HWV_OVL1(CLK_OVL_DLI3, "ovl_dli3", "disp", 9),
+ GATE_HWV_OVL1(CLK_OVL_DLI4, "ovl_dli4", "disp", 10),
+ GATE_HWV_OVL1(CLK_OVL_DLI5, "ovl_dli5", "disp", 11),
+ GATE_HWV_OVL1(CLK_OVL_DLI6, "ovl_dli6", "disp", 12),
+ GATE_HWV_OVL1(CLK_OVL_DLI7, "ovl_dli7", "disp", 13),
+ GATE_HWV_OVL1(CLK_OVL_DLI8, "ovl_dli8", "disp", 14),
+ GATE_HWV_OVL1(CLK_OVL_DLO0, "ovl_dlo0", "disp", 15),
+ GATE_HWV_OVL1(CLK_OVL_DLO1, "ovl_dlo1", "disp", 16),
+ GATE_HWV_OVL1(CLK_OVL_DLO2, "ovl_dlo2", "disp", 17),
+ GATE_HWV_OVL1(CLK_OVL_DLO3, "ovl_dlo3", "disp", 18),
+ GATE_HWV_OVL1(CLK_OVL_DLO4, "ovl_dlo4", "disp", 19),
+ GATE_HWV_OVL1(CLK_OVL_DLO5, "ovl_dlo5", "disp", 20),
+ GATE_HWV_OVL1(CLK_OVL_DLO6, "ovl_dlo6", "disp", 21),
+ GATE_HWV_OVL1(CLK_OVL_DLO7, "ovl_dlo7", "disp", 22),
+ GATE_HWV_OVL1(CLK_OVL_DLO8, "ovl_dlo8", "disp", 23),
+ GATE_HWV_OVL1(CLK_OVL_DLO9, "ovl_dlo9", "disp", 24),
+ GATE_HWV_OVL1(CLK_OVL_DLO10, "ovl_dlo10", "disp", 25),
+ GATE_HWV_OVL1(CLK_OVL_DLO11, "ovl_dlo11", "disp", 26),
+ GATE_HWV_OVL1(CLK_OVL_DLO12, "ovl_dlo12", "disp", 27),
+ GATE_HWV_OVL1(CLK_OVLSYS_RELAY0, "ovlsys_relay0", "disp", 28),
+ GATE_HWV_OVL1(CLK_OVL_INLINEROT0, "ovl_inlinerot0", "disp", 29),
+ GATE_HWV_OVL1(CLK_OVL_SMI, "ovl_smi", "disp", 30),
+};
+
+static const struct mtk_clk_desc ovl_mcd = {
+ .clks = ovl_clks,
+ .num_clks = ARRAY_SIZE(ovl_clks),
+};
+
+static const struct platform_device_id clk_mt8196_ovl0_id_table[] = {
+ { .name = "clk-mt8196-ovl0", .driver_data = (kernel_ulong_t)&ovl_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8196_ovl0_id_table);
+
+static struct platform_driver clk_mt8196_ovl0_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8196-ovl0",
+ },
+ .id_table = clk_mt8196_ovl0_id_table,
+};
+module_platform_driver(clk_mt8196_ovl0_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 ovl0 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-ovl1.c b/drivers/clk/mediatek/clk-mt8196-ovl1.c
new file mode 100644
index 000000000000..c8843d0d3ede
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-ovl1.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs ovl10_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs ovl10_hwv_regs = {
+ .set_ofs = 0x0050,
+ .clr_ofs = 0x0054,
+ .sta_ofs = 0x2c28,
+};
+
+static const struct mtk_gate_regs ovl11_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+static const struct mtk_gate_regs ovl11_hwv_regs = {
+ .set_ofs = 0x0058,
+ .clr_ofs = 0x005c,
+ .sta_ofs = 0x2c2c,
+};
+
+#define GATE_HWV_OVL10(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ovl10_cg_regs, \
+ .hwv_regs = &ovl10_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_OVL11(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ovl11_cg_regs, \
+ .hwv_regs = &ovl11_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+static const struct mtk_gate ovl1_clks[] = {
+ /* OVL10 */
+ GATE_HWV_OVL10(CLK_OVL1_OVLSYS_CONFIG, "ovl1_ovlsys_config", "disp", 0),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_FAKE_ENG0, "ovl1_ovl_fake_eng0", "disp", 1),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_FAKE_ENG1, "ovl1_ovl_fake_eng1", "disp", 2),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_MUTEX0, "ovl1_ovl_mutex0", "disp", 3),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA0, "ovl1_ovl_exdma0", "disp", 4),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA1, "ovl1_ovl_exdma1", "disp", 5),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA2, "ovl1_ovl_exdma2", "disp", 6),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA3, "ovl1_ovl_exdma3", "disp", 7),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA4, "ovl1_ovl_exdma4", "disp", 8),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA5, "ovl1_ovl_exdma5", "disp", 9),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA6, "ovl1_ovl_exdma6", "disp", 10),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA7, "ovl1_ovl_exdma7", "disp", 11),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA8, "ovl1_ovl_exdma8", "disp", 12),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_EXDMA9, "ovl1_ovl_exdma9", "disp", 13),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER0, "ovl1_ovl_blender0", "disp", 14),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER1, "ovl1_ovl_blender1", "disp", 15),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER2, "ovl1_ovl_blender2", "disp", 16),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER3, "ovl1_ovl_blender3", "disp", 17),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER4, "ovl1_ovl_blender4", "disp", 18),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER5, "ovl1_ovl_blender5", "disp", 19),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER6, "ovl1_ovl_blender6", "disp", 20),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER7, "ovl1_ovl_blender7", "disp", 21),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER8, "ovl1_ovl_blender8", "disp", 22),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_BLENDER9, "ovl1_ovl_blender9", "disp", 23),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_OUTPROC0, "ovl1_ovl_outproc0", "disp", 24),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_OUTPROC1, "ovl1_ovl_outproc1", "disp", 25),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_OUTPROC2, "ovl1_ovl_outproc2", "disp", 26),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_OUTPROC3, "ovl1_ovl_outproc3", "disp", 27),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_OUTPROC4, "ovl1_ovl_outproc4", "disp", 28),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_OUTPROC5, "ovl1_ovl_outproc5", "disp", 29),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_MDP_RSZ0, "ovl1_ovl_mdp_rsz0", "disp", 30),
+ GATE_HWV_OVL10(CLK_OVL1_OVL_MDP_RSZ1, "ovl1_ovl_mdp_rsz1", "disp", 31),
+ /* OVL11 */
+ GATE_HWV_OVL11(CLK_OVL1_OVL_DISP_WDMA0, "ovl1_ovl_disp_wdma0", "disp", 0),
+ GATE_HWV_OVL11(CLK_OVL1_OVL_DISP_WDMA1, "ovl1_ovl_disp_wdma1", "disp", 1),
+ GATE_HWV_OVL11(CLK_OVL1_OVL_UFBC_WDMA0, "ovl1_ovl_ufbc_wdma0", "disp", 2),
+ GATE_HWV_OVL11(CLK_OVL1_OVL_MDP_RDMA0, "ovl1_ovl_mdp_rdma0", "disp", 3),
+ GATE_HWV_OVL11(CLK_OVL1_OVL_MDP_RDMA1, "ovl1_ovl_mdp_rdma1", "disp", 4),
+ GATE_HWV_OVL11(CLK_OVL1_OVL_BWM0, "ovl1_ovl_bwm0", "disp", 5),
+ GATE_HWV_OVL11(CLK_OVL1_DLI0, "ovl1_dli0", "disp", 6),
+ GATE_HWV_OVL11(CLK_OVL1_DLI1, "ovl1_dli1", "disp", 7),
+ GATE_HWV_OVL11(CLK_OVL1_DLI2, "ovl1_dli2", "disp", 8),
+ GATE_HWV_OVL11(CLK_OVL1_DLI3, "ovl1_dli3", "disp", 9),
+ GATE_HWV_OVL11(CLK_OVL1_DLI4, "ovl1_dli4", "disp", 10),
+ GATE_HWV_OVL11(CLK_OVL1_DLI5, "ovl1_dli5", "disp", 11),
+ GATE_HWV_OVL11(CLK_OVL1_DLI6, "ovl1_dli6", "disp", 12),
+ GATE_HWV_OVL11(CLK_OVL1_DLI7, "ovl1_dli7", "disp", 13),
+ GATE_HWV_OVL11(CLK_OVL1_DLI8, "ovl1_dli8", "disp", 14),
+ GATE_HWV_OVL11(CLK_OVL1_DLO0, "ovl1_dlo0", "disp", 15),
+ GATE_HWV_OVL11(CLK_OVL1_DLO1, "ovl1_dlo1", "disp", 16),
+ GATE_HWV_OVL11(CLK_OVL1_DLO2, "ovl1_dlo2", "disp", 17),
+ GATE_HWV_OVL11(CLK_OVL1_DLO3, "ovl1_dlo3", "disp", 18),
+ GATE_HWV_OVL11(CLK_OVL1_DLO4, "ovl1_dlo4", "disp", 19),
+ GATE_HWV_OVL11(CLK_OVL1_DLO5, "ovl1_dlo5", "disp", 20),
+ GATE_HWV_OVL11(CLK_OVL1_DLO6, "ovl1_dlo6", "disp", 21),
+ GATE_HWV_OVL11(CLK_OVL1_DLO7, "ovl1_dlo7", "disp", 22),
+ GATE_HWV_OVL11(CLK_OVL1_DLO8, "ovl1_dlo8", "disp", 23),
+ GATE_HWV_OVL11(CLK_OVL1_DLO9, "ovl1_dlo9", "disp", 24),
+ GATE_HWV_OVL11(CLK_OVL1_DLO10, "ovl1_dlo10", "disp", 25),
+ GATE_HWV_OVL11(CLK_OVL1_DLO11, "ovl1_dlo11", "disp", 26),
+ GATE_HWV_OVL11(CLK_OVL1_DLO12, "ovl1_dlo12", "disp", 27),
+ GATE_HWV_OVL11(CLK_OVL1_OVLSYS_RELAY0, "ovl1_ovlsys_relay0", "disp", 28),
+ GATE_HWV_OVL11(CLK_OVL1_OVL_INLINEROT0, "ovl1_ovl_inlinerot0", "disp", 29),
+ GATE_HWV_OVL11(CLK_OVL1_SMI, "ovl1_smi", "disp", 30),
+};
+
+static const struct mtk_clk_desc ovl1_mcd = {
+ .clks = ovl1_clks,
+ .num_clks = ARRAY_SIZE(ovl1_clks),
+};
+
+static const struct platform_device_id clk_mt8196_ovl1_id_table[] = {
+ { .name = "clk-mt8196-ovl1", .driver_data = (kernel_ulong_t)&ovl1_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8196_ovl1_id_table);
+
+static struct platform_driver clk_mt8196_ovl1_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8196-ovl1",
+ },
+ .id_table = clk_mt8196_ovl1_id_table,
+};
+module_platform_driver(clk_mt8196_ovl1_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 ovl1 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-peri_ao.c b/drivers/clk/mediatek/clk-mt8196-peri_ao.c
new file mode 100644
index 000000000000..f227a86c5d60
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-peri_ao.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs peri_ao0_cg_regs = {
+ .set_ofs = 0x24,
+ .clr_ofs = 0x28,
+ .sta_ofs = 0x10,
+};
+
+static const struct mtk_gate_regs peri_ao1_cg_regs = {
+ .set_ofs = 0x2c,
+ .clr_ofs = 0x30,
+ .sta_ofs = 0x14,
+};
+
+static const struct mtk_gate_regs peri_ao1_hwv_regs = {
+ .set_ofs = 0x0008,
+ .clr_ofs = 0x000c,
+ .sta_ofs = 0x2c04,
+};
+
+static const struct mtk_gate_regs peri_ao2_cg_regs = {
+ .set_ofs = 0x34,
+ .clr_ofs = 0x38,
+ .sta_ofs = 0x18,
+};
+
+#define GATE_PERI_AO0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &peri_ao0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_PERI_AO1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &peri_ao1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_HWV_PERI_AO1(_id, _name, _parent, _shift) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &peri_ao1_cg_regs, \
+ .hwv_regs = &peri_ao1_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ }
+
+#define GATE_PERI_AO2(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &peri_ao2_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate peri_ao_clks[] = {
+ /* PERI_AO0 */
+ GATE_PERI_AO0(CLK_PERI_AO_UART0_BCLK, "peri_ao_uart0_bclk", "uart", 0),
+ GATE_PERI_AO0(CLK_PERI_AO_UART1_BCLK, "peri_ao_uart1_bclk", "uart", 1),
+ GATE_PERI_AO0(CLK_PERI_AO_UART2_BCLK, "peri_ao_uart2_bclk", "uart", 2),
+ GATE_PERI_AO0(CLK_PERI_AO_UART3_BCLK, "peri_ao_uart3_bclk", "uart", 3),
+ GATE_PERI_AO0(CLK_PERI_AO_UART4_BCLK, "peri_ao_uart4_bclk", "uart", 4),
+ GATE_PERI_AO0(CLK_PERI_AO_UART5_BCLK, "peri_ao_uart5_bclk", "uart", 5),
+ GATE_PERI_AO0(CLK_PERI_AO_PWM_X16W_HCLK, "peri_ao_pwm_x16w", "p_axi", 12),
+ GATE_PERI_AO0(CLK_PERI_AO_PWM_X16W_BCLK, "peri_ao_pwm_x16w_bclk", "pwm", 13),
+ GATE_PERI_AO0(CLK_PERI_AO_PWM_PWM_BCLK0, "peri_ao_pwm_pwm_bclk0", "pwm", 14),
+ GATE_PERI_AO0(CLK_PERI_AO_PWM_PWM_BCLK1, "peri_ao_pwm_pwm_bclk1", "pwm", 15),
+ GATE_PERI_AO0(CLK_PERI_AO_PWM_PWM_BCLK2, "peri_ao_pwm_pwm_bclk2", "pwm", 16),
+ GATE_PERI_AO0(CLK_PERI_AO_PWM_PWM_BCLK3, "peri_ao_pwm_pwm_bclk3", "pwm", 17),
+ /* PERI_AO1 */
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI0_BCLK, "peri_ao_spi0_bclk", "spi0_b", 0),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI1_BCLK, "peri_ao_spi1_bclk", "spi1_b", 2),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI2_BCLK, "peri_ao_spi2_bclk", "spi2_b", 3),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI3_BCLK, "peri_ao_spi3_bclk", "spi3_b", 4),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI4_BCLK, "peri_ao_spi4_bclk", "spi4_b", 5),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI5_BCLK, "peri_ao_spi5_bclk", "spi5_b", 6),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI6_BCLK, "peri_ao_spi6_bclk", "spi6_b", 7),
+ GATE_HWV_PERI_AO1(CLK_PERI_AO_SPI7_BCLK, "peri_ao_spi7_bclk", "spi7_b", 8),
+ GATE_PERI_AO1(CLK_PERI_AO_FLASHIF_FLASH, "peri_ao_flashif_flash", "peri_ao_flashif_27m",
+ 18),
+ GATE_PERI_AO1(CLK_PERI_AO_FLASHIF_27M, "peri_ao_flashif_27m", "sflash", 19),
+ GATE_PERI_AO1(CLK_PERI_AO_FLASHIF_DRAM, "peri_ao_flashif_dram", "p_axi", 20),
+ GATE_PERI_AO1(CLK_PERI_AO_FLASHIF_AXI, "peri_ao_flashif_axi", "peri_ao_flashif_dram", 21),
+ GATE_PERI_AO1(CLK_PERI_AO_FLASHIF_BCLK, "peri_ao_flashif_bclk", "p_axi", 22),
+ GATE_PERI_AO1(CLK_PERI_AO_AP_DMA_X32W_BCLK, "peri_ao_ap_dma_x32w_bclk", "p_axi", 26),
+ /* PERI_AO2 */
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC1_MSDC_SRC, "peri_ao_msdc1_msdc_src", "msdc30_1", 1),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC1_HCLK, "peri_ao_msdc1", "peri_ao_msdc1_axi", 2),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC1_AXI, "peri_ao_msdc1_axi", "p_axi", 3),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC1_HCLK_WRAP, "peri_ao_msdc1_h_wrap", "peri_ao_msdc1", 4),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC2_MSDC_SRC, "peri_ao_msdc2_msdc_src", "msdc30_2", 10),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC2_HCLK, "peri_ao_msdc2", "peri_ao_msdc2_axi", 11),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC2_AXI, "peri_ao_msdc2_axi", "p_axi", 12),
+ GATE_PERI_AO2(CLK_PERI_AO_MSDC2_HCLK_WRAP, "peri_ao_msdc2_h_wrap", "peri_ao_msdc2", 13),
+};
+
+static const struct mtk_clk_desc peri_ao_mcd = {
+ .clks = peri_ao_clks,
+ .num_clks = ARRAY_SIZE(peri_ao_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8196_peri_ao[] = {
+ { .compatible = "mediatek,mt8196-pericfg-ao", .data = &peri_ao_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_peri_ao);
+
+static struct platform_driver clk_mt8196_peri_ao_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-peri-ao",
+ .of_match_table = of_match_clk_mt8196_peri_ao,
+ },
+};
+
+MODULE_DESCRIPTION("MediaTek MT8196 pericfg_ao clock controller driver");
+module_platform_driver(clk_mt8196_peri_ao_drv);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-pextp.c b/drivers/clk/mediatek/clk-mt8196-pextp.c
new file mode 100644
index 000000000000..3e505ecc4b6e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-pextp.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+#include <dt-bindings/reset/mediatek,mt8196-resets.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "reset.h"
+
+#define MT8196_PEXTP_RST0_SET_OFFSET 0x8
+
+static const struct mtk_gate_regs pext_cg_regs = {
+ .set_ofs = 0x18,
+ .clr_ofs = 0x1c,
+ .sta_ofs = 0x14,
+};
+
+#define GATE_PEXT(_id, _name, _parent, _shift) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &pext_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr,\
+ }
+
+static const struct mtk_gate pext_clks[] = {
+ GATE_PEXT(CLK_PEXT_PEXTP_MAC_P0_TL, "pext_pm0_tl", "tl", 0),
+ GATE_PEXT(CLK_PEXT_PEXTP_MAC_P0_REF, "pext_pm0_ref", "clk26m", 1),
+ GATE_PEXT(CLK_PEXT_PEXTP_PHY_P0_MCU_BUS, "pext_pp0_mcu_bus", "clk26m", 6),
+ GATE_PEXT(CLK_PEXT_PEXTP_PHY_P0_PEXTP_REF, "pext_pp0_pextp_ref", "clk26m", 7),
+ GATE_PEXT(CLK_PEXT_PEXTP_MAC_P0_AXI_250, "pext_pm0_axi_250", "ufs_pexpt0_mem_sub", 12),
+ GATE_PEXT(CLK_PEXT_PEXTP_MAC_P0_AHB_APB, "pext_pm0_ahb_apb", "ufs_pextp0_axi", 13),
+ GATE_PEXT(CLK_PEXT_PEXTP_MAC_P0_PL_P, "pext_pm0_pl_p", "clk26m", 14),
+ GATE_PEXT(CLK_PEXT_PEXTP_VLP_AO_P0_LP, "pext_pextp_vlp_ao_p0_lp", "clk26m", 19),
+};
+
+static u16 pext_rst_ofs[] = { MT8196_PEXTP_RST0_SET_OFFSET };
+
+static u16 pext_rst_idx_map[] = {
+ [MT8196_PEXTP0_RST0_PCIE0_MAC] = 0,
+ [MT8196_PEXTP0_RST0_PCIE0_PHY] = 1,
+};
+
+static const struct mtk_clk_rst_desc pext_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = pext_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(pext_rst_ofs),
+ .rst_idx_map = pext_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(pext_rst_idx_map),
+};
+
+static const struct mtk_clk_desc pext_mcd = {
+ .clks = pext_clks,
+ .num_clks = ARRAY_SIZE(pext_clks),
+ .rst_desc = &pext_rst_desc,
+};
+
+static const struct mtk_gate pext1_clks[] = {
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P1_TL, "pext1_pm1_tl", "tl_p1", 0),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P1_REF, "pext1_pm1_ref", "clk26m", 1),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P2_TL, "pext1_pm2_tl", "tl_p2", 2),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P2_REF, "pext1_pm2_ref", "clk26m", 3),
+ GATE_PEXT(CLK_PEXT1_PEXTP_PHY_P1_MCU_BUS, "pext1_pp1_mcu_bus", "clk26m", 8),
+ GATE_PEXT(CLK_PEXT1_PEXTP_PHY_P1_PEXTP_REF, "pext1_pp1_pextp_ref", "clk26m", 9),
+ GATE_PEXT(CLK_PEXT1_PEXTP_PHY_P2_MCU_BUS, "pext1_pp2_mcu_bus", "clk26m", 10),
+ GATE_PEXT(CLK_PEXT1_PEXTP_PHY_P2_PEXTP_REF, "pext1_pp2_pextp_ref", "clk26m", 11),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P1_AXI_250, "pext1_pm1_axi_250",
+ "pextp1_usb_axi", 16),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P1_AHB_APB, "pext1_pm1_ahb_apb",
+ "pextp1_usb_mem_sub", 17),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P1_PL_P, "pext1_pm1_pl_p", "clk26m", 18),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P2_AXI_250, "pext1_pm2_axi_250",
+ "pextp1_usb_axi", 19),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P2_AHB_APB, "pext1_pm2_ahb_apb",
+ "pextp1_usb_mem_sub", 20),
+ GATE_PEXT(CLK_PEXT1_PEXTP_MAC_P2_PL_P, "pext1_pm2_pl_p", "clk26m", 21),
+ GATE_PEXT(CLK_PEXT1_PEXTP_VLP_AO_P1_LP, "pext1_pextp_vlp_ao_p1_lp", "clk26m", 26),
+ GATE_PEXT(CLK_PEXT1_PEXTP_VLP_AO_P2_LP, "pext1_pextp_vlp_ao_p2_lp", "clk26m", 27),
+};
+
+static u16 pext1_rst_idx_map[] = {
+ [MT8196_PEXTP1_RST0_PCIE1_MAC] = 0,
+ [MT8196_PEXTP1_RST0_PCIE1_PHY] = 1,
+ [MT8196_PEXTP1_RST0_PCIE2_MAC] = 8,
+ [MT8196_PEXTP1_RST0_PCIE2_PHY] = 9,
+};
+
+static const struct mtk_clk_rst_desc pext1_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = pext_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(pext_rst_ofs),
+ .rst_idx_map = pext1_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(pext1_rst_idx_map),
+};
+
+static const struct mtk_clk_desc pext1_mcd = {
+ .clks = pext1_clks,
+ .num_clks = ARRAY_SIZE(pext1_clks),
+ .rst_desc = &pext1_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8196_pextp[] = {
+ { .compatible = "mediatek,mt8196-pextp0cfg-ao", .data = &pext_mcd },
+ { .compatible = "mediatek,mt8196-pextp1cfg-ao", .data = &pext1_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_pextp);
+
+static struct platform_driver clk_mt8196_pextp_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-pextp",
+ .of_match_table = of_match_clk_mt8196_pextp,
+ },
+};
+
+module_platform_driver(clk_mt8196_pextp_drv);
+MODULE_DESCRIPTION("MediaTek MT8196 PCIe transmit phy clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-topckgen.c b/drivers/clk/mediatek/clk-mt8196-topckgen.c
new file mode 100644
index 000000000000..6ace11ef6b69
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-topckgen.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+/* MUX SEL REG */
+#define CLK_CFG_UPDATE 0x0004
+#define CLK_CFG_UPDATE1 0x0008
+#define CLK_CFG_UPDATE2 0x000c
+#define CLK_CFG_0 0x0010
+#define CLK_CFG_0_SET 0x0014
+#define CLK_CFG_0_CLR 0x0018
+#define CLK_CFG_1 0x0020
+#define CLK_CFG_1_SET 0x0024
+#define CLK_CFG_1_CLR 0x0028
+#define CLK_CFG_2 0x0030
+#define CLK_CFG_2_SET 0x0034
+#define CLK_CFG_2_CLR 0x0038
+#define CLK_CFG_3 0x0040
+#define CLK_CFG_3_SET 0x0044
+#define CLK_CFG_3_CLR 0x0048
+#define CLK_CFG_4 0x0050
+#define CLK_CFG_4_SET 0x0054
+#define CLK_CFG_4_CLR 0x0058
+#define CLK_CFG_5 0x0060
+#define CLK_CFG_5_SET 0x0064
+#define CLK_CFG_5_CLR 0x0068
+#define CLK_CFG_6 0x0070
+#define CLK_CFG_6_SET 0x0074
+#define CLK_CFG_6_CLR 0x0078
+#define CLK_CFG_7 0x0080
+#define CLK_CFG_7_SET 0x0084
+#define CLK_CFG_7_CLR 0x0088
+#define CLK_CFG_8 0x0090
+#define CLK_CFG_8_SET 0x0094
+#define CLK_CFG_8_CLR 0x0098
+#define CLK_CFG_9 0x00a0
+#define CLK_CFG_9_SET 0x00a4
+#define CLK_CFG_9_CLR 0x00a8
+#define CLK_CFG_10 0x00b0
+#define CLK_CFG_10_SET 0x00b4
+#define CLK_CFG_10_CLR 0x00b8
+#define CLK_CFG_11 0x00c0
+#define CLK_CFG_11_SET 0x00c4
+#define CLK_CFG_11_CLR 0x00c8
+#define CLK_CFG_12 0x00d0
+#define CLK_CFG_12_SET 0x00d4
+#define CLK_CFG_12_CLR 0x00d8
+#define CLK_CFG_13 0x00e0
+#define CLK_CFG_13_SET 0x00e4
+#define CLK_CFG_13_CLR 0x00e8
+#define CLK_CFG_14 0x00f0
+#define CLK_CFG_14_SET 0x00f4
+#define CLK_CFG_14_CLR 0x00f8
+#define CLK_CFG_15 0x0100
+#define CLK_CFG_15_SET 0x0104
+#define CLK_CFG_15_CLR 0x0108
+#define CLK_CFG_16 0x0110
+#define CLK_CFG_16_SET 0x0114
+#define CLK_CFG_16_CLR 0x0118
+#define CLK_CFG_17 0x0120
+#define CLK_CFG_17_SET 0x0124
+#define CLK_CFG_17_CLR 0x0128
+#define CLK_CFG_18 0x0130
+#define CLK_CFG_18_SET 0x0134
+#define CLK_CFG_18_CLR 0x0138
+#define CLK_CFG_19 0x0140
+#define CLK_CFG_19_SET 0x0144
+#define CLK_CFG_19_CLR 0x0148
+#define CLK_AUDDIV_0 0x020c
+#define CLK_FENC_STATUS_MON_0 0x0270
+#define CLK_FENC_STATUS_MON_1 0x0274
+#define CLK_FENC_STATUS_MON_2 0x0278
+
+/* MUX SHIFT */
+#define TOP_MUX_AXI_SHIFT 0
+#define TOP_MUX_MEM_SUB_SHIFT 1
+#define TOP_MUX_IO_NOC_SHIFT 2
+#define TOP_MUX_PERI_AXI_SHIFT 3
+#define TOP_MUX_UFS_PEXTP0_AXI_SHIFT 4
+#define TOP_MUX_PEXTP1_USB_AXI_SHIFT 5
+#define TOP_MUX_PERI_FMEM_SUB_SHIFT 6
+#define TOP_MUX_UFS_PEXPT0_MEM_SUB_SHIFT 7
+#define TOP_MUX_PEXTP1_USB_MEM_SUB_SHIFT 8
+#define TOP_MUX_PERI_NOC_SHIFT 9
+#define TOP_MUX_EMI_N_SHIFT 10
+#define TOP_MUX_EMI_S_SHIFT 11
+#define TOP_MUX_AP2CONN_HOST_SHIFT 14
+#define TOP_MUX_ATB_SHIFT 15
+#define TOP_MUX_CIRQ_SHIFT 16
+#define TOP_MUX_PBUS_156M_SHIFT 17
+#define TOP_MUX_EFUSE_SHIFT 20
+#define TOP_MUX_MCU_L3GIC_SHIFT 21
+#define TOP_MUX_MCU_INFRA_SHIFT 22
+#define TOP_MUX_DSP_SHIFT 23
+#define TOP_MUX_MFG_REF_SHIFT 24
+#define TOP_MUX_MFG_EB_SHIFT 26
+#define TOP_MUX_UART_SHIFT 27
+#define TOP_MUX_SPI0_BCLK_SHIFT 28
+#define TOP_MUX_SPI1_BCLK_SHIFT 29
+#define TOP_MUX_SPI2_BCLK_SHIFT 30
+#define TOP_MUX_SPI3_BCLK_SHIFT 0
+#define TOP_MUX_SPI4_BCLK_SHIFT 1
+#define TOP_MUX_SPI5_BCLK_SHIFT 2
+#define TOP_MUX_SPI6_BCLK_SHIFT 3
+#define TOP_MUX_SPI7_BCLK_SHIFT 4
+#define TOP_MUX_MSDC30_1_SHIFT 7
+#define TOP_MUX_MSDC30_2_SHIFT 8
+#define TOP_MUX_DISP_PWM_SHIFT 9
+#define TOP_MUX_USB_TOP_1P_SHIFT 10
+#define TOP_MUX_SSUSB_XHCI_1P_SHIFT 11
+#define TOP_MUX_SSUSB_FMCNT_P1_SHIFT 12
+#define TOP_MUX_I2C_PERI_SHIFT 13
+#define TOP_MUX_I2C_EAST_SHIFT 14
+#define TOP_MUX_I2C_WEST_SHIFT 15
+#define TOP_MUX_I2C_NORTH_SHIFT 16
+#define TOP_MUX_AES_UFSFDE_SHIFT 17
+#define TOP_MUX_UFS_SHIFT 18
+#define TOP_MUX_AUD_1_SHIFT 21
+#define TOP_MUX_AUD_2_SHIFT 22
+#define TOP_MUX_ADSP_SHIFT 23
+#define TOP_MUX_ADSP_UARTHUB_B_SHIFT 24
+#define TOP_MUX_DPMAIF_MAIN_SHIFT 25
+#define TOP_MUX_PWM_SHIFT 26
+#define TOP_MUX_MCUPM_SHIFT 27
+#define TOP_MUX_SFLASH_SHIFT 28
+#define TOP_MUX_IPSEAST_SHIFT 29
+#define TOP_MUX_TL_SHIFT 0
+#define TOP_MUX_TL_P1_SHIFT 1
+#define TOP_MUX_TL_P2_SHIFT 2
+#define TOP_MUX_EMI_INTERFACE_546_SHIFT 3
+#define TOP_MUX_SDF_SHIFT 4
+#define TOP_MUX_UARTHUB_BCLK_SHIFT 5
+#define TOP_MUX_DPSW_CMP_26M_SHIFT 6
+#define TOP_MUX_SMAPCK_SHIFT 7
+#define TOP_MUX_SSR_PKA_SHIFT 8
+#define TOP_MUX_SSR_DMA_SHIFT 9
+#define TOP_MUX_SSR_KDF_SHIFT 10
+#define TOP_MUX_SSR_RNG_SHIFT 11
+#define TOP_MUX_SPU0_SHIFT 12
+#define TOP_MUX_SPU1_SHIFT 13
+#define TOP_MUX_DXCC_SHIFT 14
+
+/* CKSTA REG */
+#define CKSTA_REG 0x01c8
+#define CKSTA_REG1 0x01cc
+#define CKSTA_REG2 0x01d0
+
+/* DIVIDER REG */
+#define CLK_AUDDIV_2 0x0214
+#define CLK_AUDDIV_3 0x0220
+#define CLK_AUDDIV_4 0x0224
+#define CLK_AUDDIV_5 0x0228
+
+/* HW Voter REG */
+#define HWV_CG_0_SET 0x0000
+#define HWV_CG_0_CLR 0x0004
+#define HWV_CG_0_DONE 0x2c00
+#define HWV_CG_1_SET 0x0008
+#define HWV_CG_1_CLR 0x000c
+#define HWV_CG_1_DONE 0x2c04
+#define HWV_CG_2_SET 0x0010
+#define HWV_CG_2_CLR 0x0014
+#define HWV_CG_2_DONE 0x2c08
+#define HWV_CG_3_SET 0x0018
+#define HWV_CG_3_CLR 0x001c
+#define HWV_CG_3_DONE 0x2c0c
+#define HWV_CG_4_SET 0x0020
+#define HWV_CG_4_CLR 0x0024
+#define HWV_CG_4_DONE 0x2c10
+#define HWV_CG_5_SET 0x0028
+#define HWV_CG_5_CLR 0x002c
+#define HWV_CG_5_DONE 0x2c14
+#define HWV_CG_6_SET 0x0030
+#define HWV_CG_6_CLR 0x0034
+#define HWV_CG_6_DONE 0x2c18
+#define HWV_CG_7_SET 0x0038
+#define HWV_CG_7_CLR 0x003c
+#define HWV_CG_7_DONE 0x2c1c
+#define HWV_CG_8_SET 0x0040
+#define HWV_CG_8_CLR 0x0044
+#define HWV_CG_8_DONE 0x2c20
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_MAINPLL_D3, "mainpll_d3", "mainpll", 1, 3),
+ FACTOR(CLK_TOP_MAINPLL_D4, "mainpll_d4", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_MAINPLL_D4_D2, "mainpll_d4_d2", "mainpll", 1, 8),
+ FACTOR(CLK_TOP_MAINPLL_D4_D4, "mainpll_d4_d4", "mainpll", 1, 16),
+ FACTOR(CLK_TOP_MAINPLL_D4_D8, "mainpll_d4_d8", "mainpll", 1, 32),
+ FACTOR(CLK_TOP_MAINPLL_D5, "mainpll_d5", "mainpll", 1, 5),
+ FACTOR(CLK_TOP_MAINPLL_D5_D2, "mainpll_d5_d2", "mainpll", 1, 10),
+ FACTOR(CLK_TOP_MAINPLL_D5_D4, "mainpll_d5_d4", "mainpll", 1, 20),
+ FACTOR(CLK_TOP_MAINPLL_D5_D8, "mainpll_d5_d8", "mainpll", 1, 40),
+ FACTOR(CLK_TOP_MAINPLL_D6, "mainpll_d6", "mainpll", 1, 6),
+ FACTOR(CLK_TOP_MAINPLL_D6_D2, "mainpll_d6_d2", "mainpll", 1, 12),
+ FACTOR(CLK_TOP_MAINPLL_D7, "mainpll_d7", "mainpll", 1, 7),
+ FACTOR(CLK_TOP_MAINPLL_D7_D2, "mainpll_d7_d2", "mainpll", 1, 14),
+ FACTOR(CLK_TOP_MAINPLL_D7_D4, "mainpll_d7_d4", "mainpll", 1, 28),
+ FACTOR(CLK_TOP_MAINPLL_D7_D8, "mainpll_d7_d8", "mainpll", 1, 56),
+ FACTOR(CLK_TOP_MAINPLL_D9, "mainpll_d9", "mainpll", 1, 9),
+ FACTOR(CLK_TOP_UNIVPLL_D4, "univpll_d4", "univpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D2, "univpll_d4_d2", "univpll", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D4, "univpll_d4_d4", "univpll", 1, 16),
+ FACTOR(CLK_TOP_UNIVPLL_D4_D8, "univpll_d4_d8", "univpll", 1, 32),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D2, "univpll_d5_d2", "univpll", 1, 10),
+ FACTOR(CLK_TOP_UNIVPLL_D5_D4, "univpll_d5_d4", "univpll", 1, 20),
+ FACTOR(CLK_TOP_UNIVPLL_D6, "univpll_d6", "univpll", 1, 6),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D2, "univpll_d6_d2", "univpll", 1, 12),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D4, "univpll_d6_d4", "univpll", 1, 24),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D8, "univpll_d6_d8", "univpll", 1, 48),
+ FACTOR(CLK_TOP_UNIVPLL_D6_D16, "univpll_d6_d16", "univpll", 1, 96),
+ FACTOR(CLK_TOP_UNIVPLL_192M, "univpll_192m", "univpll", 1, 13),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D4, "univpll_192m_d4", "univpll", 1, 52),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D8, "univpll_192m_d8", "univpll", 1, 104),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D16, "univpll_192m_d16", "univpll", 1, 208),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D32, "univpll_192m_d32", "univpll", 1, 416),
+ FACTOR(CLK_TOP_UNIVPLL_192M_D10, "univpll_192m_d10", "univpll", 1, 130),
+ FACTOR(CLK_TOP_TVDPLL1_D2, "tvdpll1_d2", "tvdpll1", 1, 2),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_OSC_D2, "osc_d2", "ulposc", 1, 2),
+ FACTOR(CLK_TOP_OSC_D3, "osc_d3", "ulposc", 1, 3),
+ FACTOR(CLK_TOP_OSC_D4, "osc_d4", "ulposc", 1, 4),
+ FACTOR(CLK_TOP_OSC_D5, "osc_d5", "ulposc", 1, 5),
+ FACTOR(CLK_TOP_OSC_D7, "osc_d7", "ulposc", 1, 7),
+ FACTOR(CLK_TOP_OSC_D8, "osc_d8", "ulposc", 1, 8),
+ FACTOR(CLK_TOP_OSC_D10, "osc_d10", "ulposc", 1, 10),
+ FACTOR(CLK_TOP_OSC_D14, "osc_d14", "ulposc", 1, 14),
+ FACTOR(CLK_TOP_OSC_D20, "osc_d20", "ulposc", 1, 20),
+ FACTOR(CLK_TOP_OSC_D32, "osc_d32", "ulposc", 1, 32),
+ FACTOR(CLK_TOP_OSC_D40, "osc_d40", "ulposc", 1, 40),
+};
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d8",
+ "osc_d4",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const mem_sub_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d4",
+ "univpll_d4_d4",
+ "osc_d3",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d5",
+ "univpll_d5",
+ "mainpll_d4",
+ "mainpll_d3"
+};
+
+static const char * const io_noc_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d8",
+ "osc_d4",
+ "mainpll_d6_d2",
+ "mainpll_d9"
+};
+
+static const char * const shared_axi_parents[] = {
+ "clk26m",
+ "mainpll_d7_d8",
+ "mainpll_d5_d8",
+ "osc_d8",
+ "mainpll_d7_d4",
+ "mainpll_d5_d4",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const shared_sub_parents[] = {
+ "clk26m",
+ "mainpll_d5_d8",
+ "mainpll_d5_d4",
+ "osc_d4",
+ "univpll_d4_d4",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d5",
+ "univpll_d5",
+ "mainpll_d4"
+};
+
+static const char * const p_noc_parents[] = {
+ "clk26m",
+ "mainpll_d5_d8",
+ "mainpll_d5_d4",
+ "osc_d4",
+ "univpll_d4_d4",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d5",
+ "univpll_d5",
+ "mainpll_d4",
+ "mainpll_d3"
+};
+
+static const char * const emi_parents[] = {
+ "clk26m",
+ "osc_d4",
+ "mainpll_d5_d8",
+ "mainpll_d5_d4",
+ "mainpll_d4_d4",
+ "emipll1_ck"
+};
+
+static const char * const ap2conn_host_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4"
+};
+
+static const char * const atb_parents[] = {
+ "clk26m",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6"
+};
+
+static const char * const cirq_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d7_d4"
+};
+
+static const char * const pbus_156m_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2",
+ "osc_d2",
+ "mainpll_d7"
+};
+
+static const char * const efuse_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const mcu_l3gic_parents[] = {
+ "clk26m",
+ "osc_d8",
+ "mainpll_d4_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const mcu_infra_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d7_d2",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d9",
+ "mainpll_d6"
+};
+
+static const char * const dsp_parents[] = {
+ "clk26m",
+ "osc_d5",
+ "osc_d4",
+ "osc_d3",
+ "univpll_d6_d2",
+ "osc_d2",
+ "univpll_d5",
+ "osc"
+};
+
+static const char * const mfg_ref_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2"
+};
+
+static const char * const mfg_eb_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2",
+ "mainpll_d6_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll_d6_d8",
+ "univpll_d6_d4",
+ "univpll_d6_d2"
+};
+
+static const char * const spi_b_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "univpll_d5_d4",
+ "mainpll_d4_d4",
+ "univpll_d4_d4",
+ "mainpll_d6_d2",
+ "univpll_192m",
+ "univpll_d6_d2"
+};
+
+static const char * const msdc30_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "mainpll_d6_d2",
+ "univpll_d6_d2",
+ "msdcpll_d2"
+};
+
+static const char * const disp_pwm_parents[] = {
+ "clk26m",
+ "osc_d32",
+ "osc_d8",
+ "univpll_d6_d4",
+ "univpll_d5_d4",
+ "osc_d4",
+ "mainpll_d4_d4"
+};
+
+static const char * const usb_1p_parents[] = {
+ "clk26m",
+ "univpll_d5_d4"
+};
+
+static const char * const usb_fmcnt_p1_parents[] = {
+ "clk26m",
+ "univpll_192m_d4"
+};
+
+static const char * const i2c_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "univpll_d5_d4",
+ "mainpll_d4_d4",
+ "univpll_d5_d2"
+};
+
+static const char * const aes_ufsfde_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d6",
+ "mainpll_d4"
+};
+
+static const char * const ufs_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "univpll_d6_d2",
+ "mainpll_d4_d2",
+ "univpll_d6",
+ "mainpll_d5",
+ "univpll_d5"
+};
+
+static const char * const aud_1_parents[] = {
+ "clk26m",
+ "vlp_apll1"
+};
+
+static const char * const aud_2_parents[] = {
+ "clk26m",
+ "vlp_apll2"
+};
+
+static const char * const adsp_parents[] = {
+ "clk26m",
+ "adsppll"
+};
+
+static const char * const adsp_uarthub_b_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "univpll_d6_d2"
+};
+
+static const char * const dpmaif_main_parents[] = {
+ "clk26m",
+ "univpll_d4_d4",
+ "univpll_d5_d2",
+ "mainpll_d4_d2",
+ "univpll_d4_d2",
+ "mainpll_d6",
+ "univpll_d6",
+ "mainpll_d5",
+ "univpll_d5"
+};
+
+static const char * const pwm_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4",
+ "univpll_d4_d8"
+};
+
+static const char * const mcupm_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2",
+ "mainpll_d6_d2",
+ "univpll_d6_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const ipseast_parents[] = {
+ "clk26m",
+ "mainpll_d6",
+ "mainpll_d5",
+ "mainpll_d4",
+ "mainpll_d3"
+};
+
+static const char * const tl_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4",
+ "mainpll_d4_d4",
+ "mainpll_d5_d2"
+};
+
+static const char * const md_emi_parents[] = {
+ "clk26m",
+ "mainpll_d4"
+};
+
+static const char * const sdf_parents[] = {
+ "clk26m",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2",
+ "mainpll_d6",
+ "mainpll_d4",
+ "univpll_d4"
+};
+
+static const char * const uarthub_b_parents[] = {
+ "clk26m",
+ "univpll_d6_d4",
+ "univpll_d6_d2"
+};
+
+static const char * const dpsw_cmp_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const smapparents[] = {
+ "clk26m",
+ "mainpll_d4_d8"
+};
+
+static const char * const ssr_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const char * const ssr_kdf_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7"
+};
+
+static const char * const ssr_rng_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d5_d2",
+ "mainpll_d4_d2"
+};
+
+static const char * const spu_parents[] = {
+ "clk26m",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const char * const dxcc_parents[] = {
+ "clk26m",
+ "mainpll_d4_d8",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2"
+};
+
+static const char * const apll_m_parents[] = {
+ "aud_1",
+ "aud_2"
+};
+
+static const char * const sflash_parents[] = {
+ "clk26m",
+ "mainpll_d7_d8",
+ "univpll_d6_d8"
+};
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX_CLR_SET_UPD(CLK_TOP_AXI, "axi",
+ axi_parents, CLK_CFG_0, CLK_CFG_0_SET,
+ CLK_CFG_0_CLR, 0, 3,
+ CLK_CFG_UPDATE, TOP_MUX_AXI_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_MEM_SUB, "mem_sub",
+ mem_sub_parents, CLK_CFG_0, CLK_CFG_0_SET,
+ CLK_CFG_0_CLR, 8, 4,
+ CLK_CFG_UPDATE, TOP_MUX_MEM_SUB_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_IO_NOC, "io_noc",
+ io_noc_parents, CLK_CFG_0, CLK_CFG_0_SET,
+ CLK_CFG_0_CLR, 16, 3,
+ CLK_CFG_UPDATE, TOP_MUX_IO_NOC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_P_AXI, "p_axi",
+ shared_axi_parents, CLK_CFG_0, CLK_CFG_0_SET,
+ CLK_CFG_0_CLR, 24, 3,
+ CLK_CFG_UPDATE, TOP_MUX_PERI_AXI_SHIFT),
+ /* CLK_CFG_1 */
+ MUX_CLR_SET_UPD(CLK_TOP_UFS_PEXTP0_AXI, "ufs_pextp0_axi",
+ shared_axi_parents, CLK_CFG_1, CLK_CFG_1_SET,
+ CLK_CFG_1_CLR, 0, 3,
+ CLK_CFG_UPDATE, TOP_MUX_UFS_PEXTP0_AXI_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_PEXTP1_USB_AXI, "pextp1_usb_axi",
+ shared_axi_parents, CLK_CFG_1, CLK_CFG_1_SET,
+ CLK_CFG_1_CLR, 8, 3,
+ CLK_CFG_UPDATE, TOP_MUX_PEXTP1_USB_AXI_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_P_FMEM_SUB, "p_fmem_sub",
+ shared_sub_parents, CLK_CFG_1, CLK_CFG_1_SET,
+ CLK_CFG_1_CLR, 16, 4,
+ CLK_CFG_UPDATE, TOP_MUX_PERI_FMEM_SUB_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_PEXPT0_MEM_SUB, "ufs_pexpt0_mem_sub",
+ shared_sub_parents, CLK_CFG_1, CLK_CFG_1_SET,
+ CLK_CFG_1_CLR, 24, 4,
+ CLK_CFG_UPDATE, TOP_MUX_UFS_PEXPT0_MEM_SUB_SHIFT),
+ /* CLK_CFG_2 */
+ MUX_CLR_SET_UPD(CLK_TOP_PEXTP1_USB_MEM_SUB, "pextp1_usb_mem_sub",
+ shared_sub_parents, CLK_CFG_2, CLK_CFG_2_SET,
+ CLK_CFG_2_CLR, 0, 4,
+ CLK_CFG_UPDATE, TOP_MUX_PEXTP1_USB_MEM_SUB_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_P_NOC, "p_noc",
+ p_noc_parents, CLK_CFG_2, CLK_CFG_2_SET,
+ CLK_CFG_2_CLR, 8, 4,
+ CLK_CFG_UPDATE, TOP_MUX_PERI_NOC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_EMI_N, "emi_n",
+ emi_parents, CLK_CFG_2, CLK_CFG_2_SET,
+ CLK_CFG_2_CLR, 16, 3,
+ CLK_CFG_UPDATE, TOP_MUX_EMI_N_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_EMI_S, "emi_s",
+ emi_parents, CLK_CFG_2, CLK_CFG_2_SET,
+ CLK_CFG_2_CLR, 24, 3,
+ CLK_CFG_UPDATE, TOP_MUX_EMI_S_SHIFT),
+ /* CLK_CFG_3 */
+ MUX_CLR_SET_UPD(CLK_TOP_AP2CONN_HOST, "ap2conn_host",
+ ap2conn_host_parents, CLK_CFG_3, CLK_CFG_3_SET,
+ CLK_CFG_3_CLR, 16, 1,
+ CLK_CFG_UPDATE, TOP_MUX_AP2CONN_HOST_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_ATB, "atb",
+ atb_parents, CLK_CFG_3, CLK_CFG_3_SET,
+ CLK_CFG_3_CLR, 24, 2,
+ CLK_CFG_UPDATE, TOP_MUX_ATB_SHIFT),
+ /* CLK_CFG_4 */
+ MUX_CLR_SET_UPD(CLK_TOP_CIRQ, "cirq",
+ cirq_parents, CLK_CFG_4, CLK_CFG_4_SET,
+ CLK_CFG_4_CLR, 0, 2,
+ CLK_CFG_UPDATE, TOP_MUX_CIRQ_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_PBUS_156M, "pbus_156m",
+ pbus_156m_parents, CLK_CFG_4, CLK_CFG_4_SET,
+ CLK_CFG_4_CLR, 8, 2,
+ CLK_CFG_UPDATE, TOP_MUX_PBUS_156M_SHIFT),
+ /* CLK_CFG_5 */
+ MUX_CLR_SET_UPD(CLK_TOP_EFUSE, "efuse",
+ efuse_parents, CLK_CFG_5, CLK_CFG_5_SET,
+ CLK_CFG_5_CLR, 0, 1,
+ CLK_CFG_UPDATE, TOP_MUX_EFUSE_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_MCL3GIC, "mcu_l3gic",
+ mcu_l3gic_parents, CLK_CFG_5, CLK_CFG_5_SET,
+ CLK_CFG_5_CLR, 8, 2,
+ CLK_CFG_UPDATE, TOP_MUX_MCU_L3GIC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_MCINFRA, "mcu_infra",
+ mcu_infra_parents, CLK_CFG_5, CLK_CFG_5_SET,
+ CLK_CFG_5_CLR, 16, 3,
+ CLK_CFG_UPDATE, TOP_MUX_MCU_INFRA_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_DSP, "dsp",
+ dsp_parents, CLK_CFG_5, CLK_CFG_5_SET,
+ CLK_CFG_5_CLR, 24, 3,
+ CLK_CFG_UPDATE, TOP_MUX_DSP_SHIFT),
+ /* CLK_CFG_6 */
+ MUX_GATE_FENC_CLR_SET_UPD_FLAGS(CLK_TOP_MFG_REF, "mfg_ref", mfg_ref_parents,
+ NULL, ARRAY_SIZE(mfg_ref_parents),
+ CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR,
+ 0, 1, 7, CLK_CFG_UPDATE, TOP_MUX_MFG_REF_SHIFT,
+ CLK_FENC_STATUS_MON_0, 7, CLK_IGNORE_UNUSED),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_EB, "mfg_eb",
+ mfg_eb_parents, CLK_CFG_6, CLK_CFG_6_SET,
+ CLK_CFG_6_CLR, 16, 2,
+ 23, CLK_CFG_UPDATE, TOP_MUX_MFG_EB_SHIFT),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_UART, "uart", uart_parents,
+ CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR,
+ HWV_CG_3_DONE, HWV_CG_3_SET, HWV_CG_3_CLR,
+ 24, 2, 31, CLK_CFG_UPDATE, TOP_MUX_UART_SHIFT,
+ CLK_FENC_STATUS_MON_0, 4),
+ /* CLK_CFG_7 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI0_BCLK, "spi0_b", spi_b_parents,
+ CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR,
+ HWV_CG_4_DONE, HWV_CG_4_SET, HWV_CG_4_CLR,
+ 0, 3, 7, CLK_CFG_UPDATE, TOP_MUX_SPI0_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_0, 3),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI1_BCLK, "spi1_b", spi_b_parents,
+ CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR,
+ HWV_CG_4_DONE, HWV_CG_4_SET, HWV_CG_4_CLR,
+ 8, 3, 15, CLK_CFG_UPDATE, TOP_MUX_SPI1_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_0, 2),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI2_BCLK, "spi2_b", spi_b_parents,
+ CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR,
+ HWV_CG_4_DONE, HWV_CG_4_SET, HWV_CG_4_CLR,
+ 16, 3, 23, CLK_CFG_UPDATE, TOP_MUX_SPI2_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_0, 1),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI3_BCLK, "spi3_b", spi_b_parents,
+ CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR,
+ HWV_CG_4_DONE, HWV_CG_4_SET, HWV_CG_4_CLR,
+ 24, 3, 31, CLK_CFG_UPDATE1, TOP_MUX_SPI3_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_0, 0),
+ /* CLK_CFG_8 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI4_BCLK, "spi4_b", spi_b_parents,
+ CLK_CFG_8, CLK_CFG_8_SET, CLK_CFG_8_CLR,
+ HWV_CG_5_DONE, HWV_CG_5_SET, HWV_CG_5_CLR,
+ 0, 3, 7, CLK_CFG_UPDATE1, TOP_MUX_SPI4_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_1, 31),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI5_BCLK, "spi5_b", spi_b_parents,
+ CLK_CFG_8, CLK_CFG_8_SET, CLK_CFG_8_CLR,
+ HWV_CG_5_DONE, HWV_CG_5_SET, HWV_CG_5_CLR,
+ 8, 3, 15, CLK_CFG_UPDATE1, TOP_MUX_SPI5_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_1, 30),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI6_BCLK, "spi6_b", spi_b_parents,
+ CLK_CFG_8, CLK_CFG_8_SET, CLK_CFG_8_CLR,
+ HWV_CG_5_DONE, HWV_CG_5_SET, HWV_CG_5_CLR,
+ 16, 3, 23, CLK_CFG_UPDATE1, TOP_MUX_SPI6_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_1, 29),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_SPI7_BCLK, "spi7_b", spi_b_parents,
+ CLK_CFG_8, CLK_CFG_8_SET, CLK_CFG_8_CLR,
+ HWV_CG_5_DONE, HWV_CG_5_SET, HWV_CG_5_CLR,
+ 24, 3, 31, CLK_CFG_UPDATE1, TOP_MUX_SPI7_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_1, 28),
+ /* CLK_CFG_9 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_MSDC30_1, "msdc30_1", msdc30_parents,
+ CLK_CFG_9, CLK_CFG_9_SET, CLK_CFG_9_CLR,
+ 16, 3, 23, CLK_CFG_UPDATE1, TOP_MUX_MSDC30_1_SHIFT,
+ CLK_FENC_STATUS_MON_1, 25),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_MSDC30_2, "msdc30_2", msdc30_parents,
+ CLK_CFG_9, CLK_CFG_9_SET, CLK_CFG_9_CLR,
+ 24, 3, 31, CLK_CFG_UPDATE1, TOP_MUX_MSDC30_2_SHIFT,
+ CLK_FENC_STATUS_MON_1, 24),
+ /* CLK_CFG_10 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_DISP_PWM, "disp_pwm", disp_pwm_parents,
+ CLK_CFG_10, CLK_CFG_10_SET, CLK_CFG_10_CLR,
+ 0, 3, 7, CLK_CFG_UPDATE1, TOP_MUX_DISP_PWM_SHIFT,
+ CLK_FENC_STATUS_MON_1, 23),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_USB_TOP_1P, "usb_1p", usb_1p_parents,
+ CLK_CFG_10, CLK_CFG_10_SET, CLK_CFG_10_CLR,
+ 8, 1, 15, CLK_CFG_UPDATE1, TOP_MUX_USB_TOP_1P_SHIFT,
+ CLK_FENC_STATUS_MON_1, 22),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_USB_XHCI_1P, "usb_xhci_1p", usb_1p_parents,
+ CLK_CFG_10, CLK_CFG_10_SET, CLK_CFG_10_CLR,
+ 16, 1, 23, CLK_CFG_UPDATE1, TOP_MUX_SSUSB_XHCI_1P_SHIFT,
+ CLK_FENC_STATUS_MON_1, 21),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_USB_FMCNT_P1, "usb_fmcnt_p1", usb_fmcnt_p1_parents,
+ CLK_CFG_10, CLK_CFG_10_SET, CLK_CFG_10_CLR,
+ 24, 1, 31, CLK_CFG_UPDATE1, TOP_MUX_SSUSB_FMCNT_P1_SHIFT,
+ CLK_FENC_STATUS_MON_1, 20),
+ /* CLK_CFG_11 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_I2C_P, "i2c_p", i2c_parents,
+ CLK_CFG_11, CLK_CFG_11_SET, CLK_CFG_11_CLR,
+ 0, 3, 7, CLK_CFG_UPDATE1, TOP_MUX_I2C_PERI_SHIFT,
+ CLK_FENC_STATUS_MON_1, 19),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_I2C_EAST, "i2c_east", i2c_parents,
+ CLK_CFG_11, CLK_CFG_11_SET, CLK_CFG_11_CLR,
+ 8, 3, 15, CLK_CFG_UPDATE1, TOP_MUX_I2C_EAST_SHIFT,
+ CLK_FENC_STATUS_MON_1, 18),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_I2C_WEST, "i2c_west", i2c_parents,
+ CLK_CFG_11, CLK_CFG_11_SET, CLK_CFG_11_CLR,
+ 16, 3, 23, CLK_CFG_UPDATE1, TOP_MUX_I2C_WEST_SHIFT,
+ CLK_FENC_STATUS_MON_1, 17),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_I2C_NORTH, "i2c_north", i2c_parents,
+ CLK_CFG_11, CLK_CFG_11_SET, CLK_CFG_11_CLR,
+ HWV_CG_6_DONE, HWV_CG_6_SET, HWV_CG_6_CLR,
+ 24, 3, 31, CLK_CFG_UPDATE1, TOP_MUX_I2C_NORTH_SHIFT,
+ CLK_FENC_STATUS_MON_1, 16),
+ /* CLK_CFG_12 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_AES_UFSFDE, "aes_ufsfde", aes_ufsfde_parents,
+ CLK_CFG_12, CLK_CFG_12_SET, CLK_CFG_12_CLR,
+ 0, 3, 7, CLK_CFG_UPDATE1, TOP_MUX_AES_UFSFDE_SHIFT,
+ CLK_FENC_STATUS_MON_1, 15),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_UFS, "ufs", ufs_parents,
+ CLK_CFG_12, CLK_CFG_12_SET, CLK_CFG_12_CLR,
+ 8, 3, 15, CLK_CFG_UPDATE1, TOP_MUX_UFS_SHIFT,
+ CLK_FENC_STATUS_MON_1, 14),
+ /* CLK_CFG_13 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_AUD_1, "aud_1", aud_1_parents,
+ CLK_CFG_13, CLK_CFG_13_SET, CLK_CFG_13_CLR,
+ 0, 1, 7, CLK_CFG_UPDATE1, TOP_MUX_AUD_1_SHIFT,
+ CLK_FENC_STATUS_MON_1, 11),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_AUD_2, "aud_2", aud_2_parents,
+ CLK_CFG_13, CLK_CFG_13_SET, CLK_CFG_13_CLR,
+ 8, 1, 15, CLK_CFG_UPDATE1, TOP_MUX_AUD_2_SHIFT,
+ CLK_FENC_STATUS_MON_1, 10),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_ADSP, "adsp", adsp_parents,
+ CLK_CFG_13, CLK_CFG_13_SET, CLK_CFG_13_CLR,
+ 16, 1, 23, CLK_CFG_UPDATE1, TOP_MUX_ADSP_SHIFT,
+ CLK_FENC_STATUS_MON_1, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ADSP_UARTHUB_B, "adsp_uarthub_b",
+ adsp_uarthub_b_parents, CLK_CFG_13, CLK_CFG_13_SET,
+ CLK_CFG_13_CLR, 24, 2, 31,
+ CLK_CFG_UPDATE1, TOP_MUX_ADSP_UARTHUB_B_SHIFT),
+ /* CLK_CFG_14 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_DPMAIF_MAIN, "dpmaif_main", dpmaif_main_parents,
+ CLK_CFG_14, CLK_CFG_14_SET, CLK_CFG_14_CLR,
+ 0, 4, 7, CLK_CFG_UPDATE1, TOP_MUX_DPMAIF_MAIN_SHIFT,
+ CLK_FENC_STATUS_MON_1, 7),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_PWM, "pwm", pwm_parents,
+ CLK_CFG_14, CLK_CFG_14_SET, CLK_CFG_14_CLR,
+ 8, 2, 15, CLK_CFG_UPDATE1, TOP_MUX_PWM_SHIFT,
+ CLK_FENC_STATUS_MON_1, 6),
+ MUX_CLR_SET_UPD(CLK_TOP_MCUPM, "mcupm",
+ mcupm_parents, CLK_CFG_14, CLK_CFG_14_SET,
+ CLK_CFG_14_CLR, 16, 3,
+ CLK_CFG_UPDATE1, TOP_MUX_MCUPM_SHIFT),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_SFLASH, "sflash", sflash_parents,
+ CLK_CFG_14, CLK_CFG_14_SET, CLK_CFG_14_CLR,
+ 24, 2, 31, CLK_CFG_UPDATE1, TOP_MUX_SFLASH_SHIFT,
+ CLK_FENC_STATUS_MON_1, 4),
+ /* CLK_CFG_15 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_IPSEAST, "ipseast", ipseast_parents,
+ CLK_CFG_15, CLK_CFG_15_SET, CLK_CFG_15_CLR,
+ 0, 3, 7, CLK_CFG_UPDATE1, TOP_MUX_IPSEAST_SHIFT,
+ CLK_FENC_STATUS_MON_1, 3),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_TL, "tl", tl_parents,
+ CLK_CFG_15, CLK_CFG_15_SET, CLK_CFG_15_CLR,
+ 16, 2, 23, CLK_CFG_UPDATE2, TOP_MUX_TL_SHIFT,
+ CLK_FENC_STATUS_MON_1, 1),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_TL_P1, "tl_p1", tl_parents,
+ CLK_CFG_15, CLK_CFG_15_SET, CLK_CFG_15_CLR,
+ 24, 2, 31, CLK_CFG_UPDATE2, TOP_MUX_TL_P1_SHIFT,
+ CLK_FENC_STATUS_MON_1, 0),
+ /* CLK_CFG_16 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP_TL_P2, "tl_p2", tl_parents,
+ CLK_CFG_16, CLK_CFG_16_SET, CLK_CFG_16_CLR,
+ 0, 2, 7, CLK_CFG_UPDATE2, TOP_MUX_TL_P2_SHIFT,
+ CLK_FENC_STATUS_MON_2, 31),
+ MUX_CLR_SET_UPD(CLK_TOP_EMI_INTERFACE_546, "emi_interface_546",
+ md_emi_parents, CLK_CFG_16, CLK_CFG_16_SET,
+ CLK_CFG_16_CLR, 8, 1,
+ CLK_CFG_UPDATE2, TOP_MUX_EMI_INTERFACE_546_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SDF, "sdf",
+ sdf_parents, CLK_CFG_16, CLK_CFG_16_SET,
+ CLK_CFG_16_CLR, 16, 3,
+ CLK_CFG_UPDATE2, TOP_MUX_SDF_SHIFT),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP_UARTHUB_BCLK, "uarthub_b", uarthub_b_parents,
+ CLK_CFG_16, CLK_CFG_16_SET, CLK_CFG_16_CLR,
+ HWV_CG_7_DONE, HWV_CG_7_SET, HWV_CG_7_CLR,
+ 24, 2, 31, CLK_CFG_UPDATE2, TOP_MUX_UARTHUB_BCLK_SHIFT,
+ CLK_FENC_STATUS_MON_2, 28),
+ /* CLK_CFG_17 */
+ MUX_CLR_SET_UPD(CLK_TOP_DPSW_CMP_26M, "dpsw_cmp_26m",
+ dpsw_cmp_26m_parents, CLK_CFG_17, CLK_CFG_17_SET,
+ CLK_CFG_17_CLR, 0, 1,
+ CLK_CFG_UPDATE2, TOP_MUX_DPSW_CMP_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SMAP, "smap",
+ smapparents, CLK_CFG_17, CLK_CFG_17_SET,
+ CLK_CFG_17_CLR, 8, 1,
+ CLK_CFG_UPDATE2, TOP_MUX_SMAPCK_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SSR_PKA, "ssr_pka",
+ ssr_parents, CLK_CFG_17, CLK_CFG_17_SET,
+ CLK_CFG_17_CLR, 16, 3,
+ CLK_CFG_UPDATE2, TOP_MUX_SSR_PKA_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SSR_DMA, "ssr_dma",
+ ssr_parents, CLK_CFG_17, CLK_CFG_17_SET,
+ CLK_CFG_17_CLR, 24, 3,
+ CLK_CFG_UPDATE2, TOP_MUX_SSR_DMA_SHIFT),
+ /* CLK_CFG_18 */
+ MUX_CLR_SET_UPD(CLK_TOP_SSR_KDF, "ssr_kdf",
+ ssr_kdf_parents, CLK_CFG_18, CLK_CFG_18_SET,
+ CLK_CFG_18_CLR, 0, 2,
+ CLK_CFG_UPDATE2, TOP_MUX_SSR_KDF_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SSR_RNG, "ssr_rng",
+ ssr_rng_parents, CLK_CFG_18, CLK_CFG_18_SET,
+ CLK_CFG_18_CLR, 8, 2,
+ CLK_CFG_UPDATE2, TOP_MUX_SSR_RNG_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SPU0, "spu0",
+ spu_parents, CLK_CFG_18, CLK_CFG_18_SET,
+ CLK_CFG_18_CLR, 16, 3,
+ CLK_CFG_UPDATE2, TOP_MUX_SPU0_SHIFT),
+ MUX_CLR_SET_UPD(CLK_TOP_SPU1, "spu1",
+ spu_parents, CLK_CFG_18, CLK_CFG_18_SET,
+ CLK_CFG_18_CLR, 24, 3,
+ CLK_CFG_UPDATE2, TOP_MUX_SPU1_SHIFT),
+ /* CLK_CFG_19 */
+ MUX_CLR_SET_UPD(CLK_TOP_DXCC, "dxcc",
+ dxcc_parents, CLK_CFG_19, CLK_CFG_19_SET,
+ CLK_CFG_19_CLR, 0, 2,
+ CLK_CFG_UPDATE2, TOP_MUX_DXCC_SHIFT),
+};
+
+static const struct mtk_composite top_aud_divs[] = {
+ /* CLK_AUDDIV_2 */
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SIN0, "apll_i2sin0_m", apll_m_parents,
+ CLK_AUDDIV_0, 16, 1, CLK_AUDDIV_2, 0, 8, CLK_AUDDIV_0, 0),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SIN1, "apll_i2sin1_m", apll_m_parents,
+ CLK_AUDDIV_0, 17, 1, CLK_AUDDIV_2, 8, 8, CLK_AUDDIV_0, 1),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SIN2, "apll_i2sin2_m", apll_m_parents,
+ CLK_AUDDIV_0, 18, 1, CLK_AUDDIV_2, 16, 8, CLK_AUDDIV_0, 2),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SIN3, "apll_i2sin3_m", apll_m_parents,
+ CLK_AUDDIV_0, 19, 1, CLK_AUDDIV_2, 24, 8, CLK_AUDDIV_0, 3),
+ /* CLK_AUDDIV_3 */
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SIN4, "apll_i2sin4_m", apll_m_parents,
+ CLK_AUDDIV_0, 20, 1, CLK_AUDDIV_3, 0, 8, CLK_AUDDIV_0, 4),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SIN6, "apll_i2sin6_m", apll_m_parents,
+ CLK_AUDDIV_0, 21, 1, CLK_AUDDIV_3, 8, 8, CLK_AUDDIV_0, 5),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SOUT0, "apll_i2sout0_m", apll_m_parents,
+ CLK_AUDDIV_0, 22, 1, CLK_AUDDIV_3, 16, 8, CLK_AUDDIV_0, 6),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SOUT1, "apll_i2sout1_m", apll_m_parents,
+ CLK_AUDDIV_0, 23, 1, CLK_AUDDIV_3, 24, 8, CLK_AUDDIV_0, 7),
+ /* CLK_AUDDIV_4 */
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SOUT2, "apll_i2sout2_m", apll_m_parents,
+ CLK_AUDDIV_0, 24, 1, CLK_AUDDIV_4, 0, 8, CLK_AUDDIV_0, 8),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SOUT3, "apll_i2sout3_m", apll_m_parents,
+ CLK_AUDDIV_0, 25, 1, CLK_AUDDIV_4, 8, 8, CLK_AUDDIV_0, 9),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SOUT4, "apll_i2sout4_m", apll_m_parents,
+ CLK_AUDDIV_0, 26, 1, CLK_AUDDIV_4, 16, 8, CLK_AUDDIV_0, 10),
+ MUX_DIV_GATE(CLK_TOP_APLL_I2SOUT6, "apll_i2sout6_m", apll_m_parents,
+ CLK_AUDDIV_0, 27, 1, CLK_AUDDIV_4, 24, 8, CLK_AUDDIV_0, 11),
+ /* CLK_AUDDIV_5 */
+ MUX_DIV_GATE(CLK_TOP_APLL_FMI2S, "apll_fmi2s_m", apll_m_parents,
+ CLK_AUDDIV_0, 28, 1, CLK_AUDDIV_5, 0, 8, CLK_AUDDIV_0, 12),
+ MUX(CLK_TOP_APLL_TDMOUT, "apll_tdmout_m",
+ apll_m_parents, CLK_AUDDIV_0, 29, 1),
+ DIV_GATE(CLK_TOP_APLL12_DIV_TDMOUT_M, "apll12_div_tdmout_m",
+ "apll_tdmout_m", CLK_AUDDIV_0,
+ 13, CLK_AUDDIV_5, 8, 8),
+ DIV_GATE(CLK_TOP_APLL12_DIV_TDMOUT_B, "apll12_div_tdmout_b",
+ "apll_tdmout_m", CLK_AUDDIV_0,
+ 14, CLK_AUDDIV_5, 8, 16),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_aud_divs,
+ .num_composite_clks = ARRAY_SIZE(top_aud_divs)
+};
+
+static const struct of_device_id of_match_clk_mt8196_ck[] = {
+ { .compatible = "mediatek,mt8196-topckgen", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_ck);
+
+static struct platform_driver clk_mt8196_topck_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-topck",
+ .of_match_table = of_match_clk_mt8196_ck,
+ },
+};
+
+MODULE_DESCRIPTION("MediaTek MT8196 top clock generators driver");
+module_platform_driver(clk_mt8196_topck_drv);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-topckgen2.c b/drivers/clk/mediatek/clk-mt8196-topckgen2.c
new file mode 100644
index 000000000000..6df93d7fbf91
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-topckgen2.c
@@ -0,0 +1,568 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+/* MUX SEL REG */
+#define CKSYS2_CLK_CFG_UPDATE 0x0004
+#define CKSYS2_CLK_CFG_0 0x0010
+#define CKSYS2_CLK_CFG_0_SET 0x0014
+#define CKSYS2_CLK_CFG_0_CLR 0x0018
+#define CKSYS2_CLK_CFG_1 0x0020
+#define CKSYS2_CLK_CFG_1_SET 0x0024
+#define CKSYS2_CLK_CFG_1_CLR 0x0028
+#define CKSYS2_CLK_CFG_2 0x0030
+#define CKSYS2_CLK_CFG_2_SET 0x0034
+#define CKSYS2_CLK_CFG_2_CLR 0x0038
+#define CKSYS2_CLK_CFG_3 0x0040
+#define CKSYS2_CLK_CFG_3_SET 0x0044
+#define CKSYS2_CLK_CFG_3_CLR 0x0048
+#define CKSYS2_CLK_CFG_4 0x0050
+#define CKSYS2_CLK_CFG_4_SET 0x0054
+#define CKSYS2_CLK_CFG_4_CLR 0x0058
+#define CKSYS2_CLK_CFG_5 0x0060
+#define CKSYS2_CLK_CFG_5_SET 0x0064
+#define CKSYS2_CLK_CFG_5_CLR 0x0068
+#define CKSYS2_CLK_CFG_6 0x0070
+#define CKSYS2_CLK_CFG_6_SET 0x0074
+#define CKSYS2_CLK_CFG_6_CLR 0x0078
+#define CKSYS2_CLK_FENC_STATUS_MON_0 0x0174
+
+/* MUX SHIFT */
+#define TOP_MUX_SENINF0_SHIFT 0
+#define TOP_MUX_SENINF1_SHIFT 1
+#define TOP_MUX_SENINF2_SHIFT 2
+#define TOP_MUX_SENINF3_SHIFT 3
+#define TOP_MUX_SENINF4_SHIFT 4
+#define TOP_MUX_SENINF5_SHIFT 5
+#define TOP_MUX_IMG1_SHIFT 6
+#define TOP_MUX_IPE_SHIFT 7
+#define TOP_MUX_CAM_SHIFT 8
+#define TOP_MUX_CAMTM_SHIFT 9
+#define TOP_MUX_DPE_SHIFT 10
+#define TOP_MUX_VDEC_SHIFT 11
+#define TOP_MUX_CCUSYS_SHIFT 12
+#define TOP_MUX_CCUTM_SHIFT 13
+#define TOP_MUX_VENC_SHIFT 14
+#define TOP_MUX_DVO_SHIFT 15
+#define TOP_MUX_DVO_FAVT_SHIFT 16
+#define TOP_MUX_DP1_SHIFT 17
+#define TOP_MUX_DP0_SHIFT 18
+#define TOP_MUX_DISP_SHIFT 19
+#define TOP_MUX_MDP_SHIFT 20
+#define TOP_MUX_MMINFRA_SHIFT 21
+#define TOP_MUX_MMINFRA_SNOC_SHIFT 22
+#define TOP_MUX_MMUP_SHIFT 23
+#define TOP_MUX_MMINFRA_AO_SHIFT 26
+
+/* HW Voter REG */
+#define HWV_CG_30_SET 0x0058
+#define HWV_CG_30_CLR 0x005c
+#define HWV_CG_30_DONE 0x2c2c
+
+#define MM_HWV_CG_30_SET 0x00f0
+#define MM_HWV_CG_30_CLR 0x00f4
+#define MM_HWV_CG_30_DONE 0x2c78
+#define MM_HWV_CG_31_SET 0x00f8
+#define MM_HWV_CG_31_CLR 0x00fc
+#define MM_HWV_CG_31_DONE 0x2c7c
+#define MM_HWV_CG_32_SET 0x0100
+#define MM_HWV_CG_32_CLR 0x0104
+#define MM_HWV_CG_32_DONE 0x2c80
+#define MM_HWV_CG_33_SET 0x0108
+#define MM_HWV_CG_33_CLR 0x010c
+#define MM_HWV_CG_33_DONE 0x2c84
+#define MM_HWV_CG_34_SET 0x0110
+#define MM_HWV_CG_34_CLR 0x0114
+#define MM_HWV_CG_34_DONE 0x2c88
+#define MM_HWV_CG_35_SET 0x0118
+#define MM_HWV_CG_35_CLR 0x011c
+#define MM_HWV_CG_35_DONE 0x2c8c
+#define MM_HWV_CG_36_SET 0x0120
+#define MM_HWV_CG_36_CLR 0x0124
+#define MM_HWV_CG_36_DONE 0x2c90
+#define MM_HWV_MUX_UPDATE_31_0 0x0240
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP2_MAINPLL2_D2, "mainpll2_d2", "mainpll2", 1, 2),
+ FACTOR(CLK_TOP2_MAINPLL2_D3, "mainpll2_d3", "mainpll2", 1, 3),
+ FACTOR(CLK_TOP2_MAINPLL2_D4, "mainpll2_d4", "mainpll2", 1, 4),
+ FACTOR(CLK_TOP2_MAINPLL2_D4_D2, "mainpll2_d4_d2", "mainpll2", 1, 8),
+ FACTOR(CLK_TOP2_MAINPLL2_D4_D4, "mainpll2_d4_d4", "mainpll2", 1, 16),
+ FACTOR(CLK_TOP2_MAINPLL2_D5, "mainpll2_d5", "mainpll2", 1, 5),
+ FACTOR(CLK_TOP2_MAINPLL2_D5_D2, "mainpll2_d5_d2", "mainpll2", 1, 10),
+ FACTOR(CLK_TOP2_MAINPLL2_D6, "mainpll2_d6", "mainpll2", 1, 6),
+ FACTOR(CLK_TOP2_MAINPLL2_D6_D2, "mainpll2_d6_d2", "mainpll2", 1, 12),
+ FACTOR(CLK_TOP2_MAINPLL2_D7, "mainpll2_d7", "mainpll2", 1, 7),
+ FACTOR(CLK_TOP2_MAINPLL2_D7_D2, "mainpll2_d7_d2", "mainpll2", 1, 14),
+ FACTOR(CLK_TOP2_MAINPLL2_D9, "mainpll2_d9", "mainpll2", 1, 9),
+ FACTOR(CLK_TOP2_UNIVPLL2_D3, "univpll2_d3", "univpll2", 1, 3),
+ FACTOR(CLK_TOP2_UNIVPLL2_D4, "univpll2_d4", "univpll2", 1, 4),
+ FACTOR(CLK_TOP2_UNIVPLL2_D4_D2, "univpll2_d4_d2", "univpll2", 1, 8),
+ FACTOR(CLK_TOP2_UNIVPLL2_D5, "univpll2_d5", "univpll2", 1, 5),
+ FACTOR(CLK_TOP2_UNIVPLL2_D5_D2, "univpll2_d5_d2", "univpll2", 1, 10),
+ FACTOR(CLK_TOP2_UNIVPLL2_D6, "univpll2_d6", "univpll2", 1, 6),
+ FACTOR(CLK_TOP2_UNIVPLL2_D6_D2, "univpll2_d6_d2", "univpll2", 1, 12),
+ FACTOR(CLK_TOP2_UNIVPLL2_D6_D4, "univpll2_d6_d4", "univpll2", 1, 24),
+ FACTOR(CLK_TOP2_UNIVPLL2_D7, "univpll2_d7", "univpll2", 1, 7),
+ FACTOR(CLK_TOP2_IMGPLL_D2, "imgpll_d2", "imgpll", 1, 2),
+ FACTOR(CLK_TOP2_IMGPLL_D4, "imgpll_d4", "imgpll", 1, 4),
+ FACTOR(CLK_TOP2_IMGPLL_D5, "imgpll_d5", "imgpll", 1, 5),
+ FACTOR(CLK_TOP2_IMGPLL_D5_D2, "imgpll_d5_d2", "imgpll", 1, 10),
+ FACTOR(CLK_TOP2_MMPLL2_D3, "mmpll2_d3", "mmpll2", 1, 3),
+ FACTOR(CLK_TOP2_MMPLL2_D4, "mmpll2_d4", "mmpll2", 1, 4),
+ FACTOR(CLK_TOP2_MMPLL2_D4_D2, "mmpll2_d4_d2", "mmpll2", 1, 8),
+ FACTOR(CLK_TOP2_MMPLL2_D5, "mmpll2_d5", "mmpll2", 1, 5),
+ FACTOR(CLK_TOP2_MMPLL2_D5_D2, "mmpll2_d5_d2", "mmpll2", 1, 10),
+ FACTOR(CLK_TOP2_MMPLL2_D6, "mmpll2_d6", "mmpll2", 1, 6),
+ FACTOR(CLK_TOP2_MMPLL2_D6_D2, "mmpll2_d6_d2", "mmpll2", 1, 12),
+ FACTOR(CLK_TOP2_MMPLL2_D7, "mmpll2_d7", "mmpll2", 1, 7),
+ FACTOR(CLK_TOP2_MMPLL2_D9, "mmpll2_d9", "mmpll2", 1, 9),
+ FACTOR(CLK_TOP2_TVDPLL1_D4, "tvdpll1_d4", "tvdpll1", 1, 4),
+ FACTOR(CLK_TOP2_TVDPLL1_D8, "tvdpll1_d8", "tvdpll1", 1, 8),
+ FACTOR(CLK_TOP2_TVDPLL1_D16, "tvdpll1_d16", "tvdpll1", 1, 16),
+ FACTOR(CLK_TOP2_TVDPLL2_D2, "tvdpll2_d2", "tvdpll2", 1, 2),
+ FACTOR(CLK_TOP2_TVDPLL2_D4, "tvdpll2_d4", "tvdpll2", 1, 4),
+ FACTOR(CLK_TOP2_TVDPLL2_D8, "tvdpll2_d8", "tvdpll2", 1, 8),
+ FACTOR(CLK_TOP2_TVDPLL2_D16, "tvdpll2_d16", "tvdpll2", 92, 1473),
+ FACTOR(CLK_TOP2_TVDPLL3_D2, "tvdpll3_d2", "tvdpll3", 1, 2),
+ FACTOR(CLK_TOP2_TVDPLL3_D4, "tvdpll3_d4", "tvdpll3", 1, 4),
+ FACTOR(CLK_TOP2_TVDPLL3_D8, "tvdpll3_d8", "tvdpll3", 1, 8),
+ FACTOR(CLK_TOP2_TVDPLL3_D16, "tvdpll3_d16", "tvdpll3", 92, 1473),
+};
+
+static const char * const seninf_parents[] = {
+ "clk26m",
+ "ck_osc_d10",
+ "ck_osc_d8",
+ "ck_osc_d5",
+ "ck_osc_d4",
+ "univpll2_d6_d2",
+ "mainpll2_d9",
+ "ck_osc_d2",
+ "mainpll2_d4_d2",
+ "univpll2_d4_d2",
+ "mmpll2_d4_d2",
+ "univpll2_d7",
+ "mainpll2_d6",
+ "mmpll2_d7",
+ "univpll2_d6",
+ "univpll2_d5"
+};
+
+static const char * const img1_parents[] = {
+ "clk26m",
+ "ck_osc_d4",
+ "ck_osc_d3",
+ "mmpll2_d6_d2",
+ "ck_osc_d2",
+ "imgpll_d5_d2",
+ "mmpll2_d5_d2",
+ "univpll2_d4_d2",
+ "mmpll2_d4_d2",
+ "mmpll2_d7",
+ "univpll2_d6",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "mmpll2_d5",
+ "univpll2_d4",
+ "imgpll_d4"
+};
+
+static const char * const ipe_parents[] = {
+ "clk26m",
+ "ck_osc_d4",
+ "ck_osc_d3",
+ "ck_osc_d2",
+ "univpll2_d6",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "imgpll_d5",
+ "ck_mainpll_d4",
+ "mmpll2_d5",
+ "imgpll_d4"
+};
+
+static const char * const cam_parents[] = {
+ "clk26m",
+ "ck_osc_d10",
+ "ck_osc_d4",
+ "ck_osc_d3",
+ "ck_osc_d2",
+ "mmpll2_d5_d2",
+ "univpll2_d4_d2",
+ "univpll2_d7",
+ "mmpll2_d7",
+ "univpll2_d6",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "mmpll2_d5",
+ "univpll2_d4",
+ "imgpll_d4",
+ "mmpll2_d4"
+};
+
+static const char * const camtm_parents[] = {
+ "clk26m",
+ "univpll2_d6_d4",
+ "ck_osc_d4",
+ "ck_osc_d3",
+ "univpll2_d6_d2"
+};
+
+static const char * const dpe_parents[] = {
+ "clk26m",
+ "mmpll2_d5_d2",
+ "univpll2_d4_d2",
+ "mmpll2_d7",
+ "univpll2_d6",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "mmpll2_d5",
+ "imgpll_d4",
+ "mmpll2_d4"
+};
+
+static const char * const vdec_parents[] = {
+ "clk26m",
+ "ck_mainpll_d5_d2",
+ "mainpll2_d4_d4",
+ "mainpll2_d7_d2",
+ "mainpll2_d6_d2",
+ "mainpll2_d5_d2",
+ "mainpll2_d9",
+ "mainpll2_d4_d2",
+ "mainpll2_d7",
+ "mainpll2_d6",
+ "univpll2_d6",
+ "mainpll2_d5",
+ "mainpll2_d4",
+ "imgpll_d2"
+};
+
+static const char * const ccusys_parents[] = {
+ "clk26m",
+ "ck_osc_d4",
+ "ck_osc_d3",
+ "ck_osc_d2",
+ "mmpll2_d5_d2",
+ "univpll2_d4_d2",
+ "mmpll2_d7",
+ "univpll2_d6",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "mainpll2_d4",
+ "mainpll2_d3",
+ "univpll2_d3"
+};
+
+static const char * const ccutm_parents[] = {
+ "clk26m",
+ "univpll2_d6_d4",
+ "ck_osc_d4",
+ "ck_osc_d3",
+ "univpll2_d6_d2"
+};
+
+static const char * const venc_parents[] = {
+ "clk26m",
+ "mainpll2_d5_d2",
+ "univpll2_d5_d2",
+ "mainpll2_d4_d2",
+ "mmpll2_d9",
+ "univpll2_d4_d2",
+ "mmpll2_d4_d2",
+ "mainpll2_d6",
+ "univpll2_d6",
+ "mainpll2_d5",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "mainpll2_d4",
+ "univpll2_d4",
+ "univpll2_d3"
+};
+
+static const char * const dp1_parents[] = {
+ "clk26m",
+ "tvdpll2_d16",
+ "tvdpll2_d8",
+ "tvdpll2_d4",
+ "tvdpll2_d2"
+};
+
+static const char * const dp0_parents[] = {
+ "clk26m",
+ "tvdpll1_d16",
+ "tvdpll1_d8",
+ "tvdpll1_d4",
+ "ck_tvdpll1_d2"
+};
+
+static const char * const disp_parents[] = {
+ "clk26m",
+ "ck_mainpll_d5_d2",
+ "ck_mainpll_d4_d2",
+ "ck_mainpll_d6",
+ "mainpll2_d5",
+ "mmpll2_d6",
+ "mainpll2_d4",
+ "univpll2_d4",
+ "mainpll2_d3"
+};
+
+static const char * const mdp_parents[] = {
+ "clk26m",
+ "ck_mainpll_d5_d2",
+ "mainpll2_d5_d2",
+ "mmpll2_d6_d2",
+ "mainpll2_d9",
+ "mainpll2_d4_d2",
+ "mainpll2_d7",
+ "mainpll2_d6",
+ "mainpll2_d5",
+ "mmpll2_d6",
+ "mainpll2_d4",
+ "univpll2_d4",
+ "mainpll2_d3"
+};
+
+static const char * const mminfra_parents[] = {
+ "clk26m",
+ "ck_osc_d4",
+ "ck_mainpll_d7_d2",
+ "ck_mainpll_d5_d2",
+ "ck_mainpll_d9",
+ "mmpll2_d6_d2",
+ "mainpll2_d4_d2",
+ "ck_mainpll_d6",
+ "univpll2_d6",
+ "mainpll2_d5",
+ "mmpll2_d6",
+ "univpll2_d5",
+ "mainpll2_d4",
+ "univpll2_d4",
+ "mainpll2_d3",
+ "univpll2_d3"
+};
+
+static const char * const mminfra_snoc_parents[] = {
+ "clk26m",
+ "ck_osc_d4",
+ "ck_mainpll_d7_d2",
+ "ck_mainpll_d9",
+ "ck_mainpll_d7",
+ "ck_mainpll_d6",
+ "mmpll2_d4_d2",
+ "ck_mainpll_d5",
+ "ck_mainpll_d4",
+ "univpll2_d4",
+ "mmpll2_d4",
+ "mainpll2_d3",
+ "univpll2_d3",
+ "mmpll2_d3",
+ "mainpll2_d2"
+};
+
+static const char * const mmup_parents[] = {
+ "clk26m",
+ "mainpll2_d6",
+ "mainpll2_d5",
+ "ck_osc_d2",
+ "ck_osc",
+ "ck_mainpll_d4",
+ "univpll2_d4",
+ "mainpll2_d3"
+};
+
+static const char * const mminfra_ao_parents[] = {
+ "clk26m",
+ "ck_osc_d4",
+ "ck_mainpll_d3"
+};
+
+static const char * const dvo_parents[] = {
+ "clk26m",
+ "tvdpll3_d16",
+ "tvdpll3_d8",
+ "tvdpll3_d4",
+ "tvdpll3_d2"
+};
+
+static const char * const dvo_favt_parents[] = {
+ "clk26m",
+ "tvdpll3_d16",
+ "tvdpll3_d8",
+ "tvdpll3_d4",
+ "vlp_apll1",
+ "vlp_apll2",
+ "tvdpll3_d2"
+};
+
+static const struct mtk_mux top_muxes[] = {
+ /* CKSYS2_CLK_CFG_0 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_SENINF0, "seninf0", seninf_parents,
+ CKSYS2_CLK_CFG_0, CKSYS2_CLK_CFG_0_SET, CKSYS2_CLK_CFG_0_CLR,
+ MM_HWV_CG_30_DONE, MM_HWV_CG_30_SET, MM_HWV_CG_30_CLR,
+ 0, 4, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_SENINF0_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 31),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_SENINF1, "seninf1", seninf_parents,
+ CKSYS2_CLK_CFG_0, CKSYS2_CLK_CFG_0_SET, CKSYS2_CLK_CFG_0_CLR,
+ MM_HWV_CG_30_DONE, MM_HWV_CG_30_SET, MM_HWV_CG_30_CLR,
+ 8, 4, 15, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_SENINF1_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 30),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_SENINF2, "seninf2", seninf_parents,
+ CKSYS2_CLK_CFG_0, CKSYS2_CLK_CFG_0_SET, CKSYS2_CLK_CFG_0_CLR,
+ MM_HWV_CG_30_DONE, MM_HWV_CG_30_SET, MM_HWV_CG_30_CLR,
+ 16, 4, 23, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_SENINF2_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 29),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_SENINF3, "seninf3", seninf_parents,
+ CKSYS2_CLK_CFG_0, CKSYS2_CLK_CFG_0_SET, CKSYS2_CLK_CFG_0_CLR,
+ MM_HWV_CG_30_DONE, MM_HWV_CG_30_SET, MM_HWV_CG_30_CLR,
+ 24, 4, 31, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_SENINF3_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 28),
+ /* CKSYS2_CLK_CFG_1 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_SENINF4, "seninf4", seninf_parents,
+ CKSYS2_CLK_CFG_1, CKSYS2_CLK_CFG_1_SET, CKSYS2_CLK_CFG_1_CLR,
+ MM_HWV_CG_31_DONE, MM_HWV_CG_31_SET, MM_HWV_CG_31_CLR,
+ 0, 4, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_SENINF4_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 27),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_SENINF5, "seninf5", seninf_parents,
+ CKSYS2_CLK_CFG_1, CKSYS2_CLK_CFG_1_SET, CKSYS2_CLK_CFG_1_CLR,
+ MM_HWV_CG_31_DONE, MM_HWV_CG_31_SET, MM_HWV_CG_31_CLR,
+ 8, 4, 15, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_SENINF5_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 26),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_IMG1, "img1", img1_parents,
+ CKSYS2_CLK_CFG_1, CKSYS2_CLK_CFG_1_SET, CKSYS2_CLK_CFG_1_CLR,
+ MM_HWV_CG_31_DONE, MM_HWV_CG_31_SET, MM_HWV_CG_31_CLR,
+ 16, 4, 23, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_IMG1_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 25),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_IPE, "ipe", ipe_parents,
+ CKSYS2_CLK_CFG_1, CKSYS2_CLK_CFG_1_SET, CKSYS2_CLK_CFG_1_CLR,
+ MM_HWV_CG_31_DONE, MM_HWV_CG_31_SET, MM_HWV_CG_31_CLR,
+ 24, 4, 31, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_IPE_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 24),
+ /* CKSYS2_CLK_CFG_2 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_CAM, "cam", cam_parents,
+ CKSYS2_CLK_CFG_2, CKSYS2_CLK_CFG_2_SET, CKSYS2_CLK_CFG_2_CLR,
+ MM_HWV_CG_32_DONE, MM_HWV_CG_32_SET, MM_HWV_CG_32_CLR,
+ 0, 4, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_CAM_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 23),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_CAMTM, "camtm", camtm_parents,
+ CKSYS2_CLK_CFG_2, CKSYS2_CLK_CFG_2_SET, CKSYS2_CLK_CFG_2_CLR,
+ MM_HWV_CG_32_DONE, MM_HWV_CG_32_SET, MM_HWV_CG_32_CLR,
+ 8, 3, 15, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_CAMTM_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 22),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_DPE, "dpe", dpe_parents,
+ CKSYS2_CLK_CFG_2, CKSYS2_CLK_CFG_2_SET, CKSYS2_CLK_CFG_2_CLR,
+ MM_HWV_CG_32_DONE, MM_HWV_CG_32_SET, MM_HWV_CG_32_CLR,
+ 16, 4, 23, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_DPE_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 21),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_VDEC, "vdec", vdec_parents,
+ CKSYS2_CLK_CFG_2, CKSYS2_CLK_CFG_2_SET, CKSYS2_CLK_CFG_2_CLR,
+ MM_HWV_CG_32_DONE, MM_HWV_CG_32_SET, MM_HWV_CG_32_CLR,
+ 24, 4, 31, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_VDEC_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 20),
+ /* CKSYS2_CLK_CFG_3 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_CCUSYS, "ccusys", ccusys_parents,
+ CKSYS2_CLK_CFG_3, CKSYS2_CLK_CFG_3_SET, CKSYS2_CLK_CFG_3_CLR,
+ MM_HWV_CG_33_DONE, MM_HWV_CG_33_SET, MM_HWV_CG_33_CLR,
+ 0, 4, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_CCUSYS_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 19),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_CCUTM, "ccutm", ccutm_parents,
+ CKSYS2_CLK_CFG_3, CKSYS2_CLK_CFG_3_SET, CKSYS2_CLK_CFG_3_CLR,
+ MM_HWV_CG_33_DONE, MM_HWV_CG_33_SET, MM_HWV_CG_33_CLR,
+ 8, 3, 15, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_CCUTM_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 18),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_VENC, "venc", venc_parents,
+ CKSYS2_CLK_CFG_3, CKSYS2_CLK_CFG_3_SET, CKSYS2_CLK_CFG_3_CLR,
+ MM_HWV_CG_33_DONE, MM_HWV_CG_33_SET, MM_HWV_CG_33_CLR,
+ 16, 4, 23, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_VENC_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 17),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP2_DVO, "dvo", dvo_parents,
+ CKSYS2_CLK_CFG_3, CKSYS2_CLK_CFG_3_SET, CKSYS2_CLK_CFG_3_CLR,
+ 24, 3, 31, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_DVO_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 16),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP2_DVO_FAVT, "dvo_favt", dvo_favt_parents,
+ CKSYS2_CLK_CFG_4, CKSYS2_CLK_CFG_4_SET, CKSYS2_CLK_CFG_4_CLR,
+ 0, 3, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_DVO_FAVT_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 15),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP2_DP1, "dp1", dp1_parents,
+ CKSYS2_CLK_CFG_4, CKSYS2_CLK_CFG_4_SET, CKSYS2_CLK_CFG_4_CLR,
+ 8, 3, 15, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_DP1_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 14),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP2_DP0, "dp0", dp0_parents,
+ CKSYS2_CLK_CFG_4, CKSYS2_CLK_CFG_4_SET, CKSYS2_CLK_CFG_4_CLR,
+ 16, 3, 23, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_DP0_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 13),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_DISP, "disp", disp_parents,
+ CKSYS2_CLK_CFG_4, CKSYS2_CLK_CFG_4_SET, CKSYS2_CLK_CFG_4_CLR,
+ MM_HWV_CG_34_DONE, MM_HWV_CG_34_SET, MM_HWV_CG_34_CLR,
+ 24, 4, 31, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_DISP_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 12),
+ /* CKSYS2_CLK_CFG_5 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_MDP, "mdp", mdp_parents,
+ CKSYS2_CLK_CFG_5, CKSYS2_CLK_CFG_5_SET, CKSYS2_CLK_CFG_5_CLR,
+ MM_HWV_CG_35_DONE, MM_HWV_CG_35_SET, MM_HWV_CG_35_CLR,
+ 0, 4, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_MDP_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 11),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_MMINFRA, "mminfra", mminfra_parents,
+ CKSYS2_CLK_CFG_5, CKSYS2_CLK_CFG_5_SET, CKSYS2_CLK_CFG_5_CLR,
+ MM_HWV_CG_35_DONE, MM_HWV_CG_35_SET, MM_HWV_CG_35_CLR,
+ 8, 4, 15, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_MMINFRA_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 10),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_MMINFRA_SNOC, "mminfra_snoc", mminfra_snoc_parents,
+ CKSYS2_CLK_CFG_5, CKSYS2_CLK_CFG_5_SET, CKSYS2_CLK_CFG_5_CLR,
+ MM_HWV_CG_35_DONE, MM_HWV_CG_35_SET, MM_HWV_CG_35_CLR,
+ 16, 4, 23, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_MMINFRA_SNOC_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 9),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_TOP2_MMUP, "mmup", mmup_parents,
+ CKSYS2_CLK_CFG_5, CKSYS2_CLK_CFG_5_SET, CKSYS2_CLK_CFG_5_CLR,
+ 24, 3, 31, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_MMUP_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 8),
+ /* CKSYS2_CLK_CFG_6 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_TOP2_MMINFRA_AO, "mminfra_ao", mminfra_ao_parents,
+ CKSYS2_CLK_CFG_6, CKSYS2_CLK_CFG_6_SET, CKSYS2_CLK_CFG_6_CLR,
+ MM_HWV_CG_36_DONE, MM_HWV_CG_36_SET, MM_HWV_CG_36_CLR,
+ 16, 2, 7, CKSYS2_CLK_CFG_UPDATE, TOP_MUX_MMINFRA_AO_SHIFT,
+ CKSYS2_CLK_FENC_STATUS_MON_0, 5),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+};
+
+static const struct of_device_id of_match_clk_mt8196_ck[] = {
+ { .compatible = "mediatek,mt8196-topckgen-gp2", .data = &topck_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_ck);
+
+static struct platform_driver clk_mt8196_topck_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-topck2",
+ .of_match_table = of_match_clk_mt8196_ck,
+ },
+};
+
+MODULE_DESCRIPTION("MediaTek MT8196 GP2 top clock generators driver");
+module_platform_driver(clk_mt8196_topck_drv);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-ufs_ao.c b/drivers/clk/mediatek/clk-mt8196-ufs_ao.c
new file mode 100644
index 000000000000..0c04717b7b4b
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-ufs_ao.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+#include <dt-bindings/reset/mediatek,mt8196-resets.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+#define MT8196_UFSAO_RST0_SET_OFFSET 0x48
+#define MT8196_UFSAO_RST1_SET_OFFSET 0x148
+
+static const struct mtk_gate_regs ufsao0_cg_regs = {
+ .set_ofs = 0x108,
+ .clr_ofs = 0x10c,
+ .sta_ofs = 0x104,
+};
+
+static const struct mtk_gate_regs ufsao1_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_UFSAO0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ufsao0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_UFSAO1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ufsao1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate ufsao_clks[] = {
+ /* UFSAO0 */
+ GATE_UFSAO0(CLK_UFSAO_UFSHCI_UFS, "ufsao_ufshci_ufs", "ufs", 0),
+ GATE_UFSAO0(CLK_UFSAO_UFSHCI_AES, "ufsao_ufshci_aes", "aes_ufsfde", 1),
+ /* UFSAO1 */
+ GATE_UFSAO1(CLK_UFSAO_UNIPRO_TX_SYM, "ufsao_unipro_tx_sym", "clk26m", 0),
+ GATE_UFSAO1(CLK_UFSAO_UNIPRO_RX_SYM0, "ufsao_unipro_rx_sym0", "clk26m", 1),
+ GATE_UFSAO1(CLK_UFSAO_UNIPRO_RX_SYM1, "ufsao_unipro_rx_sym1", "clk26m", 2),
+ GATE_UFSAO1(CLK_UFSAO_UNIPRO_SYS, "ufsao_unipro_sys", "ufs", 3),
+ GATE_UFSAO1(CLK_UFSAO_UNIPRO_SAP, "ufsao_unipro_sap", "clk26m", 4),
+ GATE_UFSAO1(CLK_UFSAO_PHY_SAP, "ufsao_phy_sap", "clk26m", 8),
+};
+
+static u16 ufsao_rst_ofs[] = {
+ MT8196_UFSAO_RST0_SET_OFFSET,
+ MT8196_UFSAO_RST1_SET_OFFSET
+};
+
+static u16 ufsao_rst_idx_map[] = {
+ [MT8196_UFSAO_RST0_UFS_MPHY] = 8,
+ [MT8196_UFSAO_RST1_UFS_UNIPRO] = 1 * RST_NR_PER_BANK + 0,
+ [MT8196_UFSAO_RST1_UFS_CRYPTO] = 1 * RST_NR_PER_BANK + 1,
+ [MT8196_UFSAO_RST1_UFSHCI] = 1 * RST_NR_PER_BANK + 2,
+};
+
+static const struct mtk_clk_rst_desc ufsao_rst_desc = {
+ .version = MTK_RST_SET_CLR,
+ .rst_bank_ofs = ufsao_rst_ofs,
+ .rst_bank_nr = ARRAY_SIZE(ufsao_rst_ofs),
+ .rst_idx_map = ufsao_rst_idx_map,
+ .rst_idx_map_nr = ARRAY_SIZE(ufsao_rst_idx_map),
+};
+
+static const struct mtk_clk_desc ufsao_mcd = {
+ .clks = ufsao_clks,
+ .num_clks = ARRAY_SIZE(ufsao_clks),
+ .rst_desc = &ufsao_rst_desc,
+};
+
+static const struct of_device_id of_match_clk_mt8196_ufs_ao[] = {
+ { .compatible = "mediatek,mt8196-ufscfg-ao", .data = &ufsao_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_ufs_ao);
+
+static struct platform_driver clk_mt8196_ufs_ao_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-ufs-ao",
+ .of_match_table = of_match_clk_mt8196_ufs_ao,
+ },
+};
+
+module_platform_driver(clk_mt8196_ufs_ao_drv);
+MODULE_DESCRIPTION("MediaTek MT8196 ufs_ao clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-vdec.c b/drivers/clk/mediatek/clk-mt8196-vdec.c
new file mode 100644
index 000000000000..f8dcd84a2b58
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-vdec.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vde20_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs vde20_hwv_regs = {
+ .set_ofs = 0x0088,
+ .clr_ofs = 0x008c,
+ .sta_ofs = 0x2c44,
+};
+
+static const struct mtk_gate_regs vde21_cg_regs = {
+ .set_ofs = 0x200,
+ .clr_ofs = 0x204,
+ .sta_ofs = 0x200,
+};
+
+static const struct mtk_gate_regs vde21_hwv_regs = {
+ .set_ofs = 0x0080,
+ .clr_ofs = 0x0084,
+ .sta_ofs = 0x2c40,
+};
+
+static const struct mtk_gate_regs vde22_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x8,
+};
+
+static const struct mtk_gate_regs vde22_hwv_regs = {
+ .set_ofs = 0x0078,
+ .clr_ofs = 0x007c,
+ .sta_ofs = 0x2c3c,
+};
+
+#define GATE_HWV_VDE20(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde20_cg_regs, \
+ .hwv_regs = &vde20_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VDE21(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde21_cg_regs, \
+ .hwv_regs = &vde21_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VDE22(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde22_cg_regs, \
+ .hwv_regs = &vde22_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE | \
+ CLK_IGNORE_UNUSED, \
+ }
+
+static const struct mtk_gate vde2_clks[] = {
+ /* VDE20 */
+ GATE_HWV_VDE20(CLK_VDE2_VDEC_CKEN, "vde2_vdec_cken", "vdec", 0),
+ GATE_HWV_VDE20(CLK_VDE2_VDEC_ACTIVE, "vde2_vdec_active", "vdec", 4),
+ GATE_HWV_VDE20(CLK_VDE2_VDEC_CKEN_ENG, "vde2_vdec_cken_eng", "vdec", 8),
+ /* VDE21 */
+ GATE_HWV_VDE21(CLK_VDE2_LAT_CKEN, "vde2_lat_cken", "vdec", 0),
+ GATE_HWV_VDE21(CLK_VDE2_LAT_ACTIVE, "vde2_lat_active", "vdec", 4),
+ GATE_HWV_VDE21(CLK_VDE2_LAT_CKEN_ENG, "vde2_lat_cken_eng", "vdec", 8),
+ /* VDE22 */
+ GATE_HWV_VDE22(CLK_VDE2_LARB1_CKEN, "vde2_larb1_cken", "vdec", 0),
+};
+
+static const struct mtk_clk_desc vde2_mcd = {
+ .clks = vde2_clks,
+ .num_clks = ARRAY_SIZE(vde2_clks),
+ .need_runtime_pm = true,
+};
+
+static const struct mtk_gate_regs vde10_hwv_regs = {
+ .set_ofs = 0x00a0,
+ .clr_ofs = 0x00a4,
+ .sta_ofs = 0x2c50,
+};
+
+static const struct mtk_gate_regs vde11_cg_regs = {
+ .set_ofs = 0x1e0,
+ .clr_ofs = 0x1e0,
+ .sta_ofs = 0x1e0,
+};
+
+static const struct mtk_gate_regs vde11_hwv_regs = {
+ .set_ofs = 0x00b0,
+ .clr_ofs = 0x00b4,
+ .sta_ofs = 0x2c58,
+};
+
+static const struct mtk_gate_regs vde12_cg_regs = {
+ .set_ofs = 0x1ec,
+ .clr_ofs = 0x1ec,
+ .sta_ofs = 0x1ec,
+};
+
+static const struct mtk_gate_regs vde12_hwv_regs = {
+ .set_ofs = 0x00a8,
+ .clr_ofs = 0x00ac,
+ .sta_ofs = 0x2c54,
+};
+
+static const struct mtk_gate_regs vde13_cg_regs = {
+ .set_ofs = 0x200,
+ .clr_ofs = 0x204,
+ .sta_ofs = 0x200,
+};
+
+static const struct mtk_gate_regs vde13_hwv_regs = {
+ .set_ofs = 0x0098,
+ .clr_ofs = 0x009c,
+ .sta_ofs = 0x2c4c,
+};
+
+static const struct mtk_gate_regs vde14_hwv_regs = {
+ .set_ofs = 0x0090,
+ .clr_ofs = 0x0094,
+ .sta_ofs = 0x2c48,
+};
+
+#define GATE_HWV_VDE10(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde20_cg_regs, \
+ .hwv_regs = &vde10_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VDE11(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde11_cg_regs, \
+ .hwv_regs = &vde11_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VDE12(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde12_cg_regs, \
+ .hwv_regs = &vde12_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv, \
+ .flags = CLK_OPS_PARENT_ENABLE \
+ }
+
+#define GATE_HWV_VDE13(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde13_cg_regs, \
+ .hwv_regs = &vde13_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VDE14(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &vde22_cg_regs, \
+ .hwv_regs = &vde14_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE | \
+ CLK_IGNORE_UNUSED, \
+ }
+
+static const struct mtk_gate vde1_clks[] = {
+ /* VDE10 */
+ GATE_HWV_VDE10(CLK_VDE1_VDEC_CKEN, "vde1_vdec_cken", "vdec", 0),
+ GATE_HWV_VDE10(CLK_VDE1_VDEC_ACTIVE, "vde1_vdec_active", "vdec", 4),
+ GATE_HWV_VDE10(CLK_VDE1_VDEC_CKEN_ENG, "vde1_vdec_cken_eng", "vdec", 8),
+ /* VDE11 */
+ GATE_HWV_VDE11(CLK_VDE1_VDEC_SOC_IPS_EN, "vde1_vdec_soc_ips_en", "vdec", 0),
+ /* VDE12 */
+ GATE_HWV_VDE12(CLK_VDE1_VDEC_SOC_APTV_EN, "vde1_aptv_en", "ck_tck_26m_mx9_ck", 0),
+ GATE_HWV_VDE12(CLK_VDE1_VDEC_SOC_APTV_TOP_EN, "vde1_aptv_topen", "ck_tck_26m_mx9_ck", 1),
+ /* VDE13 */
+ GATE_HWV_VDE13(CLK_VDE1_LAT_CKEN, "vde1_lat_cken", "vdec", 0),
+ GATE_HWV_VDE13(CLK_VDE1_LAT_ACTIVE, "vde1_lat_active", "vdec", 4),
+ GATE_HWV_VDE13(CLK_VDE1_LAT_CKEN_ENG, "vde1_lat_cken_eng", "vdec", 8),
+ /* VDE14 */
+ GATE_HWV_VDE14(CLK_VDE1_LARB1_CKEN, "vde1_larb1_cken", "vdec", 0),
+};
+
+static const struct mtk_clk_desc vde1_mcd = {
+ .clks = vde1_clks,
+ .num_clks = ARRAY_SIZE(vde1_clks),
+ .need_runtime_pm = true,
+};
+
+static const struct of_device_id of_match_clk_mt8196_vdec[] = {
+ { .compatible = "mediatek,mt8196-vdecsys", .data = &vde2_mcd },
+ { .compatible = "mediatek,mt8196-vdecsys-soc", .data = &vde1_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vdec);
+
+static struct platform_driver clk_mt8196_vdec_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-vdec",
+ .of_match_table = of_match_clk_mt8196_vdec,
+ },
+};
+module_platform_driver(clk_mt8196_vdec_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-vdisp_ao.c b/drivers/clk/mediatek/clk-mt8196-vdisp_ao.c
new file mode 100644
index 000000000000..fddb69d1c3eb
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-vdisp_ao.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mm_v_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs mm_v_hwv_regs = {
+ .set_ofs = 0x0030,
+ .clr_ofs = 0x0034,
+ .sta_ofs = 0x2c18,
+};
+
+#define GATE_MM_AO_V(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm_v_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE | \
+ CLK_IS_CRITICAL, \
+ }
+
+#define GATE_HWV_MM_V(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm_v_cg_regs, \
+ .hwv_regs = &mm_v_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+static const struct mtk_gate mm_v_clks[] = {
+ GATE_HWV_MM_V(CLK_MM_V_DISP_VDISP_AO_CONFIG, "mm_v_disp_vdisp_ao_config", "disp", 0),
+ GATE_HWV_MM_V(CLK_MM_V_DISP_DPC, "mm_v_disp_dpc", "disp", 16),
+ GATE_MM_AO_V(CLK_MM_V_SMI_SUB_SOMM0, "mm_v_smi_sub_somm0", "disp", 2),
+};
+
+static const struct mtk_clk_desc mm_v_mcd = {
+ .clks = mm_v_clks,
+ .num_clks = ARRAY_SIZE(mm_v_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8196_vdisp_ao[] = {
+ { .compatible = "mediatek,mt8196-vdisp-ao", .data = &mm_v_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vdisp_ao);
+
+static struct platform_driver clk_mt8196_vdisp_ao_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8196-vdisp-ao",
+ .of_match_table = of_match_clk_mt8196_vdisp_ao,
+ },
+};
+module_platform_driver(clk_mt8196_vdisp_ao_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 vdisp_ao clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-venc.c b/drivers/clk/mediatek/clk-mt8196-venc.c
new file mode 100644
index 000000000000..13e2e36e945f
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-venc.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs ven10_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs ven10_hwv_regs = {
+ .set_ofs = 0x00b8,
+ .clr_ofs = 0x00bc,
+ .sta_ofs = 0x2c5c,
+};
+
+static const struct mtk_gate_regs ven11_cg_regs = {
+ .set_ofs = 0x10,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x10,
+};
+
+static const struct mtk_gate_regs ven11_hwv_regs = {
+ .set_ofs = 0x00c0,
+ .clr_ofs = 0x00c4,
+ .sta_ofs = 0x2c60,
+};
+
+#define GATE_VEN10(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven10_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr_inv, \
+ }
+
+#define GATE_HWV_VEN10_FLAGS(_id, _name, _parent, _shift, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven10_cg_regs, \
+ .hwv_regs = &ven10_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv, \
+ .flags = (_flags) | \
+ CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VEN10(_id, _name, _parent, _shift) \
+ GATE_HWV_VEN10_FLAGS(_id, _name, _parent, _shift, 0)
+
+#define GATE_HWV_VEN11(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven11_cg_regs, \
+ .hwv_regs = &ven11_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE \
+ }
+
+static const struct mtk_gate ven1_clks[] = {
+ /* VEN10 */
+ GATE_HWV_VEN10(CLK_VEN1_CKE0_LARB, "ven1_larb", "venc", 0),
+ GATE_HWV_VEN10(CLK_VEN1_CKE1_VENC, "ven1_venc", "venc", 4),
+ GATE_VEN10(CLK_VEN1_CKE2_JPGENC, "ven1_jpgenc", "venc", 8),
+ GATE_VEN10(CLK_VEN1_CKE3_JPGDEC, "ven1_jpgdec", "venc", 12),
+ GATE_VEN10(CLK_VEN1_CKE4_JPGDEC_C1, "ven1_jpgdec_c1", "venc", 16),
+ GATE_HWV_VEN10(CLK_VEN1_CKE5_GALS, "ven1_gals", "venc", 28),
+ GATE_HWV_VEN10(CLK_VEN1_CKE29_VENC_ADAB_CTRL, "ven1_venc_adab_ctrl",
+ "venc", 29),
+ GATE_HWV_VEN10_FLAGS(CLK_VEN1_CKE29_VENC_XPC_CTRL,
+ "ven1_venc_xpc_ctrl", "venc", 30,
+ CLK_IGNORE_UNUSED),
+ GATE_HWV_VEN10(CLK_VEN1_CKE6_GALS_SRAM, "ven1_gals_sram", "venc", 31),
+ /* VEN11 */
+ GATE_HWV_VEN11(CLK_VEN1_RES_FLAT, "ven1_res_flat", "venc", 0),
+};
+
+static const struct mtk_clk_desc ven1_mcd = {
+ .clks = ven1_clks,
+ .num_clks = ARRAY_SIZE(ven1_clks),
+ .need_runtime_pm = true,
+};
+
+static const struct mtk_gate_regs ven20_hwv_regs = {
+ .set_ofs = 0x00c8,
+ .clr_ofs = 0x00cc,
+ .sta_ofs = 0x2c64,
+};
+
+static const struct mtk_gate_regs ven21_hwv_regs = {
+ .set_ofs = 0x00d0,
+ .clr_ofs = 0x00d4,
+ .sta_ofs = 0x2c68,
+};
+
+#define GATE_VEN20(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven10_cg_regs, \
+ .shift = _shift, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ .ops = &mtk_clk_gate_ops_setclr_inv, \
+ }
+
+#define GATE_HWV_VEN20(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven10_cg_regs, \
+ .hwv_regs = &ven20_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VEN21(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven11_cg_regs, \
+ .hwv_regs = &ven21_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE \
+ }
+
+static const struct mtk_gate ven2_clks[] = {
+ /* VEN20 */
+ GATE_HWV_VEN20(CLK_VEN2_CKE0_LARB, "ven2_larb", "venc", 0),
+ GATE_HWV_VEN20(CLK_VEN2_CKE1_VENC, "ven2_venc", "venc", 4),
+ GATE_VEN20(CLK_VEN2_CKE2_JPGENC, "ven2_jpgenc", "venc", 8),
+ GATE_VEN20(CLK_VEN2_CKE3_JPGDEC, "ven2_jpgdec", "venc", 12),
+ GATE_HWV_VEN20(CLK_VEN2_CKE5_GALS, "ven2_gals", "venc", 28),
+ GATE_HWV_VEN20(CLK_VEN2_CKE29_VENC_XPC_CTRL, "ven2_venc_xpc_ctrl", "venc", 30),
+ GATE_HWV_VEN20(CLK_VEN2_CKE6_GALS_SRAM, "ven2_gals_sram", "venc", 31),
+ /* VEN21 */
+ GATE_HWV_VEN21(CLK_VEN2_RES_FLAT, "ven2_res_flat", "venc", 0),
+};
+
+static const struct mtk_clk_desc ven2_mcd = {
+ .clks = ven2_clks,
+ .num_clks = ARRAY_SIZE(ven2_clks),
+ .need_runtime_pm = true,
+};
+
+static const struct mtk_gate_regs ven_c20_hwv_regs = {
+ .set_ofs = 0x00d8,
+ .clr_ofs = 0x00dc,
+ .sta_ofs = 0x2c6c,
+};
+
+static const struct mtk_gate_regs ven_c21_hwv_regs = {
+ .set_ofs = 0x00e0,
+ .clr_ofs = 0x00e4,
+ .sta_ofs = 0x2c70,
+};
+
+#define GATE_HWV_VEN_C20(_id, _name, _parent, _shift) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven10_cg_regs, \
+ .hwv_regs = &ven_c20_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr_inv,\
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+#define GATE_HWV_VEN_C21(_id, _name, _parent, _shift) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ven11_cg_regs, \
+ .hwv_regs = &ven_c21_hwv_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_hwv_ops_setclr, \
+ .flags = CLK_OPS_PARENT_ENABLE, \
+ }
+
+static const struct mtk_gate ven_c2_clks[] = {
+ /* VEN_C20 */
+ GATE_HWV_VEN_C20(CLK_VEN_C2_CKE0_LARB, "ven_c2_larb", "venc", 0),
+ GATE_HWV_VEN_C20(CLK_VEN_C2_CKE1_VENC, "ven_c2_venc", "venc", 4),
+ GATE_HWV_VEN_C20(CLK_VEN_C2_CKE5_GALS, "ven_c2_gals", "venc", 28),
+ GATE_HWV_VEN_C20(CLK_VEN_C2_CKE29_VENC_XPC_CTRL, "ven_c2_venc_xpc_ctrl",
+ "venc", 30),
+ GATE_HWV_VEN_C20(CLK_VEN_C2_CKE6_GALS_SRAM, "ven_c2_gals_sram", "venc", 31),
+ /* VEN_C21 */
+ GATE_HWV_VEN_C21(CLK_VEN_C2_RES_FLAT, "ven_c2_res_flat", "venc", 0),
+};
+
+static const struct mtk_clk_desc ven_c2_mcd = {
+ .clks = ven_c2_clks,
+ .num_clks = ARRAY_SIZE(ven_c2_clks),
+ .need_runtime_pm = true,
+};
+
+static const struct of_device_id of_match_clk_mt8196_venc[] = {
+ { .compatible = "mediatek,mt8196-vencsys", .data = &ven1_mcd },
+ { .compatible = "mediatek,mt8196-vencsys-c1", .data = &ven2_mcd },
+ { .compatible = "mediatek,mt8196-vencsys-c2", .data = &ven_c2_mcd },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_venc);
+
+static struct platform_driver clk_mt8196_venc_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8196-venc",
+ .of_match_table = of_match_clk_mt8196_venc,
+ },
+};
+module_platform_driver(clk_mt8196_venc_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8196 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c
new file mode 100644
index 000000000000..d59a8a9d9855
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+#include "clk-pll.h"
+
+/* MUX SEL REG */
+#define VLP_CLK_CFG_UPDATE 0x0004
+#define VLP_CLK_CFG_UPDATE1 0x0008
+#define VLP_CLK_CFG_0 0x0010
+#define VLP_CLK_CFG_0_SET 0x0014
+#define VLP_CLK_CFG_0_CLR 0x0018
+#define VLP_CLK_CFG_1 0x0020
+#define VLP_CLK_CFG_1_SET 0x0024
+#define VLP_CLK_CFG_1_CLR 0x0028
+#define VLP_CLK_CFG_2 0x0030
+#define VLP_CLK_CFG_2_SET 0x0034
+#define VLP_CLK_CFG_2_CLR 0x0038
+#define VLP_CLK_CFG_3 0x0040
+#define VLP_CLK_CFG_3_SET 0x0044
+#define VLP_CLK_CFG_3_CLR 0x0048
+#define VLP_CLK_CFG_4 0x0050
+#define VLP_CLK_CFG_4_SET 0x0054
+#define VLP_CLK_CFG_4_CLR 0x0058
+#define VLP_CLK_CFG_5 0x0060
+#define VLP_CLK_CFG_5_SET 0x0064
+#define VLP_CLK_CFG_5_CLR 0x0068
+#define VLP_CLK_CFG_6 0x0070
+#define VLP_CLK_CFG_6_SET 0x0074
+#define VLP_CLK_CFG_6_CLR 0x0078
+#define VLP_CLK_CFG_7 0x0080
+#define VLP_CLK_CFG_7_SET 0x0084
+#define VLP_CLK_CFG_7_CLR 0x0088
+#define VLP_CLK_CFG_8 0x0090
+#define VLP_CLK_CFG_8_SET 0x0094
+#define VLP_CLK_CFG_8_CLR 0x0098
+#define VLP_CLK_CFG_9 0x00a0
+#define VLP_CLK_CFG_9_SET 0x00a4
+#define VLP_CLK_CFG_9_CLR 0x00a8
+#define VLP_CLK_CFG_10 0x00b0
+#define VLP_CLK_CFG_10_SET 0x00b4
+#define VLP_CLK_CFG_10_CLR 0x00b8
+#define VLP_OCIC_FENC_STATUS_MON_0 0x039c
+#define VLP_OCIC_FENC_STATUS_MON_1 0x03a0
+
+/* MUX SHIFT */
+#define TOP_MUX_SCP_SHIFT 0
+#define TOP_MUX_SCP_SPI_SHIFT 1
+#define TOP_MUX_SCP_IIC_SHIFT 2
+#define TOP_MUX_SCP_IIC_HS_SHIFT 3
+#define TOP_MUX_PWRAP_ULPOSC_SHIFT 4
+#define TOP_MUX_SPMI_M_TIA_32K_SHIFT 5
+#define TOP_MUX_APXGPT_26M_B_SHIFT 6
+#define TOP_MUX_DPSW_SHIFT 7
+#define TOP_MUX_DPSW_CENTRAL_SHIFT 8
+#define TOP_MUX_SPMI_M_MST_SHIFT 9
+#define TOP_MUX_DVFSRC_SHIFT 10
+#define TOP_MUX_PWM_VLP_SHIFT 11
+#define TOP_MUX_AXI_VLP_SHIFT 12
+#define TOP_MUX_SYSTIMER_26M_SHIFT 13
+#define TOP_MUX_SSPM_SHIFT 14
+#define TOP_MUX_SRCK_SHIFT 15
+#define TOP_MUX_CAMTG0_SHIFT 16
+#define TOP_MUX_CAMTG1_SHIFT 17
+#define TOP_MUX_CAMTG2_SHIFT 18
+#define TOP_MUX_CAMTG3_SHIFT 19
+#define TOP_MUX_CAMTG4_SHIFT 20
+#define TOP_MUX_CAMTG5_SHIFT 21
+#define TOP_MUX_CAMTG6_SHIFT 22
+#define TOP_MUX_CAMTG7_SHIFT 23
+#define TOP_MUX_SSPM_26M_SHIFT 25
+#define TOP_MUX_ULPOSC_SSPM_SHIFT 26
+#define TOP_MUX_VLP_PBUS_26M_SHIFT 27
+#define TOP_MUX_DEBUG_ERR_FLAG_VLP_26M_SHIFT 28
+#define TOP_MUX_DPMSRDMA_SHIFT 29
+#define TOP_MUX_VLP_PBUS_156M_SHIFT 30
+#define TOP_MUX_SPM_SHIFT 0
+#define TOP_MUX_MMINFRA_VLP_SHIFT 1
+#define TOP_MUX_USB_TOP_SHIFT 2
+#define TOP_MUX_SSUSB_XHCI_SHIFT 3
+#define TOP_MUX_NOC_VLP_SHIFT 4
+#define TOP_MUX_AUDIO_H_SHIFT 5
+#define TOP_MUX_AUD_ENGEN1_SHIFT 6
+#define TOP_MUX_AUD_ENGEN2_SHIFT 7
+#define TOP_MUX_AUD_INTBUS_SHIFT 8
+#define TOP_MUX_SPU_VLP_26M_SHIFT 9
+#define TOP_MUX_SPU0_VLP_SHIFT 10
+#define TOP_MUX_SPU1_VLP_SHIFT 11
+
+/* CKSTA REG */
+#define VLP_CKSTA_REG0 0x0250
+#define VLP_CKSTA_REG1 0x0254
+
+/* HW Voter REG */
+#define HWV_CG_9_SET 0x0048
+#define HWV_CG_9_CLR 0x004c
+#define HWV_CG_9_DONE 0x2c24
+#define HWV_CG_10_SET 0x0050
+#define HWV_CG_10_CLR 0x0054
+#define HWV_CG_10_DONE 0x2c28
+
+/* PLL REG */
+#define VLP_AP_PLL_CON3 0x264
+#define VLP_APLL1_TUNER_CON0 0x2a4
+#define VLP_APLL2_TUNER_CON0 0x2a8
+#define VLP_APLL1_CON0 0x274
+#define VLP_APLL1_CON1 0x278
+#define VLP_APLL1_CON2 0x27c
+#define VLP_APLL1_CON3 0x280
+#define VLP_APLL2_CON0 0x28c
+#define VLP_APLL2_CON1 0x290
+#define VLP_APLL2_CON2 0x294
+#define VLP_APLL2_CON3 0x298
+
+/* vlp apll1 tuner default value*/
+#define VLP_APLL1_TUNER_CON0_VALUE 0x6f28bd4d
+/* vlp apll2 tuner default value + 1*/
+#define VLP_APLL2_TUNER_CON0_VALUE 0x78fd5265
+
+#define VLP_PLLEN_ALL 0x080
+#define VLP_PLLEN_ALL_SET 0x084
+#define VLP_PLLEN_ALL_CLR 0x088
+
+#define MT8196_PLL_FMAX (3800UL * MHZ)
+#define MT8196_PLL_FMIN (1500UL * MHZ)
+#define MT8196_INTEGER_BITS 8
+
+#define PLL_FENC(_id, _name, _reg, _fenc_sta_ofs, _fenc_sta_bit,\
+ _flags, _pd_reg, _pd_shift, \
+ _pcw_reg, _pcw_shift, _pcwbits, \
+ _pll_en_bit) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .fenc_sta_ofs = _fenc_sta_ofs, \
+ .fenc_sta_bit = _fenc_sta_bit, \
+ .flags = _flags, \
+ .fmax = MT8196_PLL_FMAX, \
+ .fmin = MT8196_PLL_FMIN, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8196_INTEGER_BITS, \
+ .en_reg = VLP_PLLEN_ALL, \
+ .en_set_reg = VLP_PLLEN_ALL_SET, \
+ .en_clr_reg = VLP_PLLEN_ALL_CLR, \
+ .pll_en_bit = _pll_en_bit, \
+ .ops = &mtk_pll_fenc_clr_set_ops, \
+}
+
+static DEFINE_SPINLOCK(mt8196_clk_vlp_lock);
+
+static const struct mtk_fixed_factor vlp_divs[] = {
+ FACTOR(CLK_VLP_CLK26M, "vlp_clk26m", "clk26m", 1, 1),
+ FACTOR(CLK_VLP_APLL1_D4, "apll1_d4", "vlp_apll1", 1, 4),
+ FACTOR(CLK_VLP_APLL1_D8, "apll1_d8", "vlp_apll1", 1, 8),
+ FACTOR(CLK_VLP_APLL2_D4, "apll2_d4", "vlp_apll2", 1, 4),
+ FACTOR(CLK_VLP_APLL2_D8, "apll2_d8", "vlp_apll2", 1, 8),
+};
+
+static const char * const vlp_scp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d6",
+ "mainpll_d4",
+ "mainpll_d3",
+ "vlp_apll1"
+};
+
+static const char * const vlp_scp_spi_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d7_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const vlp_scp_iic_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d5_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_scp_iic_hs_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d5_d4",
+ "mainpll_d7_d2",
+ "mainpll_d7"
+};
+
+static const char * const vlp_pwrap_ulposc_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d14",
+ "osc_d10"
+};
+
+static const char * const vlp_spmi_32k_parents[] = {
+ "clk26m",
+ "clk32k",
+ "osc_d20",
+ "osc_d14",
+ "osc_d10"
+};
+
+static const char * const vlp_apxgpt_26m_b_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_dpsw_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "osc_d7",
+ "mainpll_d7_d4"
+};
+
+static const char * const vlp_dpsw_central_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "osc_d7",
+ "mainpll_d7_d4"
+};
+
+static const char * const vlp_spmi_m_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d14",
+ "osc_d10"
+};
+
+static const char * const vlp_dvfsrc_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_pwm_vlp_parents[] = {
+ "clk26m",
+ "clk32k",
+ "osc_d20",
+ "osc_d8",
+ "mainpll_d4_d8"
+};
+
+static const char * const vlp_axi_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d7_d4",
+ "osc_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_systimer_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_sspm_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d5_d2",
+ "osc_d2",
+ "mainpll_d6"
+};
+
+static const char * const vlp_srck_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_camtg0_1_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "ulposc3",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg2_7_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_sspm_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_ulposc_sspm_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "mainpll_d4_d2"
+};
+
+static const char * const vlp_vlp_pbus_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_debug_err_flag_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_dpmsrdma_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_vlp_pbus_156m_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "mainpll_d7_d2",
+ "mainpll_d7"
+};
+
+static const char * const vlp_spm_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4"
+};
+
+static const char * const vlp_mminfra_parents[] = {
+ "clk26m",
+ "osc_d4",
+ "mainpll_d3"
+};
+
+static const char * const vlp_usb_parents[] = {
+ "clk26m",
+ "mainpll_d9"
+};
+
+static const char * const vlp_noc_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d9"
+};
+
+static const char * const vlp_audio_h_parents[] = {
+ "vlp_clk26m",
+ "vlp_apll1",
+ "vlp_apll2"
+};
+
+static const char * const vlp_aud_engen1_parents[] = {
+ "vlp_clk26m",
+ "apll1_d8",
+ "apll1_d4"
+};
+
+static const char * const vlp_aud_engen2_parents[] = {
+ "vlp_clk26m",
+ "apll2_d8",
+ "apll2_d4"
+};
+
+static const char * const vlp_aud_intbus_parents[] = {
+ "vlp_clk26m",
+ "mainpll_d7_d4",
+ "mainpll_d4_d4"
+};
+
+static const u8 vlp_aud_parent_index[] = { 1, 2, 3 };
+
+static const char * const vlp_spvlp_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_spu0_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const char * const vlp_spu1_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const struct mtk_mux vlp_muxes[] = {
+ /* VLP_CLK_CFG_0 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_SCP, "vlp_scp", vlp_scp_parents,
+ VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET, VLP_CLK_CFG_0_CLR,
+ 0, 3, 7, VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 31),
+ MUX_CLR_SET_UPD(CLK_VLP_SCP_SPI, "vlp_scp_spi",
+ vlp_scp_spi_parents, VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET,
+ VLP_CLK_CFG_0_CLR, 8, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_SPI_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SCP_IIC, "vlp_scp_iic",
+ vlp_scp_iic_parents, VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET,
+ VLP_CLK_CFG_0_CLR, 16, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_IIC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SCP_IIC_HS, "vlp_scp_iic_hs",
+ vlp_scp_iic_hs_parents, VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET,
+ VLP_CLK_CFG_0_CLR, 24, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_IIC_HS_SHIFT),
+ /* VLP_CLK_CFG_1 */
+ MUX_CLR_SET_UPD(CLK_VLP_PWRAP_ULPOSC, "vlp_pwrap_ulposc",
+ vlp_pwrap_ulposc_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 0, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_PWRAP_ULPOSC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPMI_M_TIA_32K, "vlp_spmi_32k",
+ vlp_spmi_32k_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 8, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SPMI_M_TIA_32K_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_APXGPT_26M_B, "vlp_apxgpt_26m_b",
+ vlp_apxgpt_26m_b_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 16, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_APXGPT_26M_B_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_DPSW, "vlp_dpsw",
+ vlp_dpsw_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 24, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DPSW_SHIFT),
+ /* VLP_CLK_CFG_2 */
+ MUX_CLR_SET_UPD(CLK_VLP_DPSW_CENTRAL, "vlp_dpsw_central",
+ vlp_dpsw_central_parents, VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET,
+ VLP_CLK_CFG_2_CLR, 0, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DPSW_CENTRAL_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPMI_M_MST, "vlp_spmi_m",
+ vlp_spmi_m_parents, VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET,
+ VLP_CLK_CFG_2_CLR, 8, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SPMI_M_MST_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_DVFSRC, "vlp_dvfsrc",
+ vlp_dvfsrc_parents, VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET,
+ VLP_CLK_CFG_2_CLR, 16, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DVFSRC_SHIFT),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_PWM_VLP, "vlp_pwm_vlp", vlp_pwm_vlp_parents,
+ VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET, VLP_CLK_CFG_2_CLR,
+ 24, 3, 31, VLP_CLK_CFG_UPDATE, TOP_MUX_PWM_VLP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 20),
+ /* VLP_CLK_CFG_3 */
+ MUX_CLR_SET_UPD(CLK_VLP_AXI_VLP, "vlp_axi_vlp",
+ vlp_axi_vlp_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 0, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_AXI_VLP_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SYSTIMER_26M, "vlp_systimer_26m",
+ vlp_systimer_26m_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 8, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SYSTIMER_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SSPM, "vlp_sspm",
+ vlp_sspm_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 16, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SSPM_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SRCK, "vlp_srck",
+ vlp_srck_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 24, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SRCK_SHIFT),
+ /* VLP_CLK_CFG_4 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG0, "vlp_camtg0", vlp_camtg0_1_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 0, 4, 7, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG0_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 15),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG1, "vlp_camtg1", vlp_camtg0_1_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 8, 4, 15, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG1_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 14),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG2, "vlp_camtg2", vlp_camtg2_7_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 16, 4, 23, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG2_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 13),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG3, "vlp_camtg3", vlp_camtg2_7_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 24, 4, 31, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG3_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 12),
+ /* VLP_CLK_CFG_5 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG4, "vlp_camtg4", vlp_camtg2_7_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 0, 4, 7, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG4_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 11),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG5, "vlp_camtg5", vlp_camtg2_7_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 8, 4, 15, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG5_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 10),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG6, "vlp_camtg6", vlp_camtg2_7_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 16, 4, 23, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG6_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 9),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG7, "vlp_camtg7", vlp_camtg2_7_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 24, 4, 31, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG7_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 8),
+ /* VLP_CLK_CFG_6 */
+ MUX_CLR_SET_UPD(CLK_VLP_SSPM_26M, "vlp_sspm_26m",
+ vlp_sspm_26m_parents, VLP_CLK_CFG_6, VLP_CLK_CFG_6_SET,
+ VLP_CLK_CFG_6_CLR, 8, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SSPM_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_ULPOSC_SSPM, "vlp_ulposc_sspm",
+ vlp_ulposc_sspm_parents, VLP_CLK_CFG_6, VLP_CLK_CFG_6_SET,
+ VLP_CLK_CFG_6_CLR, 16, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_ULPOSC_SSPM_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_VLP_PBUS_26M, "vlp_vlp_pbus_26m",
+ vlp_vlp_pbus_26m_parents, VLP_CLK_CFG_6, VLP_CLK_CFG_6_SET,
+ VLP_CLK_CFG_6_CLR, 24, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_VLP_PBUS_26M_SHIFT),
+ /* VLP_CLK_CFG_7 */
+ MUX_CLR_SET_UPD(CLK_VLP_DEBUG_ERR_FLAG, "vlp_debug_err_flag",
+ vlp_debug_err_flag_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 0, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DEBUG_ERR_FLAG_VLP_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_DPMSRDMA, "vlp_dpmsrdma",
+ vlp_dpmsrdma_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 8, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DPMSRDMA_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_VLP_PBUS_156M, "vlp_vlp_pbus_156m",
+ vlp_vlp_pbus_156m_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 16, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_VLP_PBUS_156M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPM, "vlp_spm",
+ vlp_spm_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 24, 1,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPM_SHIFT),
+ /* VLP_CLK_CFG_8 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_MMINFRA, "vlp_mminfra", vlp_mminfra_parents,
+ VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET, VLP_CLK_CFG_8_CLR,
+ 0, 2, 7, VLP_CLK_CFG_UPDATE1, TOP_MUX_MMINFRA_VLP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 31),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_USB_TOP, "vlp_usb", vlp_usb_parents,
+ VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET, VLP_CLK_CFG_8_CLR,
+ 8, 1, 15, VLP_CLK_CFG_UPDATE1, TOP_MUX_USB_TOP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 30),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_USB_XHCI, "vlp_usb_xhci", vlp_usb_parents,
+ VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET, VLP_CLK_CFG_8_CLR,
+ 16, 1, 23, VLP_CLK_CFG_UPDATE1, TOP_MUX_SSUSB_XHCI_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 29),
+ MUX_CLR_SET_UPD(CLK_VLP_NOC_VLP, "vlp_noc_vlp",
+ vlp_noc_vlp_parents, VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET,
+ VLP_CLK_CFG_8_CLR, 24, 2,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_NOC_VLP_SHIFT),
+ /* VLP_CLK_CFG_9 */
+ MUX_GATE_FENC_CLR_SET_UPD_INDEXED(CLK_VLP_AUDIO_H, "vlp_audio_h",
+ vlp_audio_h_parents, vlp_aud_parent_index,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 0, 2, 7, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUDIO_H_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 27),
+ MUX_GATE_FENC_CLR_SET_UPD_INDEXED(CLK_VLP_AUD_ENGEN1, "vlp_aud_engen1",
+ vlp_aud_engen1_parents, vlp_aud_parent_index,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 8, 2, 15, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUD_ENGEN1_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 26),
+ MUX_GATE_FENC_CLR_SET_UPD_INDEXED(CLK_VLP_AUD_ENGEN2, "vlp_aud_engen2",
+ vlp_aud_engen2_parents, vlp_aud_parent_index,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 16, 2, 23, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUD_ENGEN2_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 25),
+ MUX_GATE_FENC_CLR_SET_UPD_INDEXED(CLK_VLP_AUD_INTBUS, "vlp_aud_intbus",
+ vlp_aud_intbus_parents, vlp_aud_parent_index,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 24, 2, 31, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUD_INTBUS_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 24),
+ /* VLP_CLK_CFG_10 */
+ MUX_CLR_SET_UPD(CLK_VLP_SPVLP_26M, "vlp_spvlp_26m",
+ vlp_spvlp_26m_parents, VLP_CLK_CFG_10, VLP_CLK_CFG_10_SET,
+ VLP_CLK_CFG_10_CLR, 0, 1,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPU_VLP_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPU0_VLP, "vlp_spu0_vlp",
+ vlp_spu0_vlp_parents, VLP_CLK_CFG_10, VLP_CLK_CFG_10_SET,
+ VLP_CLK_CFG_10_CLR, 8, 3,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPU0_VLP_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPU1_VLP, "vlp_spu1_vlp",
+ vlp_spu1_vlp_parents, VLP_CLK_CFG_10, VLP_CLK_CFG_10_SET,
+ VLP_CLK_CFG_10_CLR, 16, 3,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPU1_VLP_SHIFT),
+};
+
+static const struct mtk_pll_data vlp_plls[] = {
+ PLL_FENC(CLK_VLP_APLL1, "vlp_apll1", VLP_APLL1_CON0, 0x0358, 1, 0,
+ VLP_APLL1_CON1, 24, VLP_APLL1_CON2, 0, 32, 0),
+ PLL_FENC(CLK_VLP_APLL2, "vlp_apll2", VLP_APLL2_CON0, 0x0358, 0, 0,
+ VLP_APLL2_CON1, 24, VLP_APLL2_CON2, 0, 32, 1),
+};
+
+static const struct regmap_config vlpckgen_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x1000,
+ .fast_io = true,
+};
+
+static int clk_mt8196_vlp_probe(struct platform_device *pdev)
+{
+ static void __iomem *base;
+ struct clk_hw_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+
+ clk_data = mtk_alloc_clk_data(ARRAY_SIZE(vlp_muxes) +
+ ARRAY_SIZE(vlp_plls) +
+ ARRAY_SIZE(vlp_divs));
+ if (!clk_data)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, &vlpckgen_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ r = mtk_clk_register_factors(vlp_divs, ARRAY_SIZE(vlp_divs), clk_data);
+ if (r)
+ goto free_clk_data;
+
+ r = mtk_clk_register_muxes(&pdev->dev, vlp_muxes, ARRAY_SIZE(vlp_muxes),
+ node, &mt8196_clk_vlp_lock, clk_data);
+ if (r)
+ goto unregister_factors;
+
+ r = mtk_clk_register_plls(node, vlp_plls, ARRAY_SIZE(vlp_plls),
+ clk_data);
+ if (r)
+ goto unregister_muxes;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ /* Initialize APLL tuner registers */
+ regmap_write(regmap, VLP_APLL1_TUNER_CON0, VLP_APLL1_TUNER_CON0_VALUE);
+ regmap_write(regmap, VLP_APLL2_TUNER_CON0, VLP_APLL2_TUNER_CON0_VALUE);
+
+ return r;
+
+unregister_plls:
+ mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data);
+unregister_muxes:
+ mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data);
+unregister_factors:
+ mtk_clk_unregister_factors(vlp_divs, ARRAY_SIZE(vlp_divs), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+
+ return r;
+}
+
+static void clk_mt8196_vlp_remove(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data);
+ mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data);
+ mtk_clk_unregister_factors(vlp_divs, ARRAY_SIZE(vlp_divs), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static const struct of_device_id of_match_clk_mt8196_vlp_ck[] = {
+ { .compatible = "mediatek,mt8196-vlpckgen" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vlp_ck);
+
+static struct platform_driver clk_mt8196_vlp_drv = {
+ .probe = clk_mt8196_vlp_probe,
+ .remove = clk_mt8196_vlp_remove,
+ .driver = {
+ .name = "clk-mt8196-vlpck",
+ .of_match_table = of_match_clk_mt8196_vlp_ck,
+ },
+};
+
+MODULE_DESCRIPTION("MediaTek MT8196 VLP clock generator driver");
+module_platform_driver(clk_mt8196_vlp_drv);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-apmixedsys.c b/drivers/clk/mediatek/clk-mt8365-apmixedsys.c
new file mode 100644
index 000000000000..f41b991a0178
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-apmixedsys.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Copyright (c) 2023 Collabora Ltd.
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-pll.h"
+#include "clk-mtk.h"
+
+#define MT8365_PLL_FMAX (3800UL * MHZ)
+#define MT8365_PLL_FMIN (1500UL * MHZ)
+#define CON0_MT8365_RST_BAR BIT(23)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \
+ _tuner_en_bit, _pcw_reg, _pcw_shift, _div_table, \
+ _rst_bar_mask, _pcw_chg_reg) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = _rst_bar_mask, \
+ .fmax = MT8365_PLL_FMAX, \
+ .fmin = MT8365_PLL_FMIN, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = 8, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcw_chg_reg = _pcw_chg_reg, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, \
+ _tuner_en_reg, _tuner_en_bit, _pcw_reg, \
+ _pcw_shift, _rst_bar_mask, _pcw_chg_reg) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _pcwbits, _pd_reg, _pd_shift, \
+ _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, NULL, _rst_bar_mask, \
+ _pcw_chg_reg) \
+
+static const struct mtk_pll_div_table armpll_div_table[] = {
+ { .div = 0, .freq = MT8365_PLL_FMAX },
+ { .div = 1, .freq = 1500 * MHZ },
+ { .div = 2, .freq = 750 * MHZ },
+ { .div = 3, .freq = 375 * MHZ },
+ { .div = 4, .freq = 182500000 },
+ { } /* sentinel */
+};
+
+static const struct mtk_pll_div_table mfgpll_div_table[] = {
+ { .div = 0, .freq = MT8365_PLL_FMAX },
+ { .div = 1, .freq = 1600 * MHZ },
+ { .div = 2, .freq = 800 * MHZ },
+ { .div = 3, .freq = 400 * MHZ },
+ { .div = 4, .freq = 200 * MHZ },
+ { } /* sentinel */
+};
+
+static const struct mtk_pll_div_table dsppll_div_table[] = {
+ { .div = 0, .freq = MT8365_PLL_FMAX },
+ { .div = 1, .freq = 1600 * MHZ },
+ { .div = 2, .freq = 600 * MHZ },
+ { .div = 3, .freq = 400 * MHZ },
+ { .div = 4, .freq = 200 * MHZ },
+ { } /* sentinel */
+};
+
+static const struct mtk_pll_data plls[] = {
+ PLL_B(CLK_APMIXED_ARMPLL, "armpll", 0x030C, 0x0318, 0x00000001, PLL_AO,
+ 22, 0x0310, 24, 0, 0, 0, 0x0310, 0, armpll_div_table, 0, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0228, 0x0234, 0xFF000001,
+ HAVE_RST_BAR, 22, 0x022C, 24, 0, 0, 0, 0x022C, 0, CON0_MT8365_RST_BAR, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll2", 0x0208, 0x0214, 0xFF000001,
+ HAVE_RST_BAR, 22, 0x020C, 24, 0, 0, 0, 0x020C, 0, CON0_MT8365_RST_BAR, 0),
+ PLL_B(CLK_APMIXED_MFGPLL, "mfgpll", 0x0218, 0x0224, 0x00000001, 0, 22,
+ 0x021C, 24, 0, 0, 0, 0x021C, 0, mfgpll_div_table, 0, 0),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0350, 0x035C, 0x00000001, 0, 22,
+ 0x0354, 24, 0, 0, 0, 0x0354, 0, 0, 0),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0330, 0x033C, 0x00000001, 0, 22,
+ 0x0334, 24, 0, 0, 0, 0x0334, 0, 0, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x031C, 0x032C, 0x00000001, 0, 32,
+ 0x0320, 24, 0x0040, 0x000C, 0, 0x0324, 0, 0, 0x0320),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x0360, 0x0370, 0x00000001, 0, 32,
+ 0x0364, 24, 0x004C, 0x000C, 5, 0x0368, 0, 0, 0x0364),
+ PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x0374, 0x0380, 0x00000001, 0, 22,
+ 0x0378, 24, 0, 0, 0, 0x0378, 0, 0, 0),
+ PLL_B(CLK_APMIXED_DSPPLL, "dsppll", 0x0390, 0x039C, 0x00000001, 0, 22,
+ 0x0394, 24, 0, 0, 0, 0x0394, 0, dsppll_div_table, 0, 0),
+ PLL(CLK_APMIXED_APUPLL, "apupll", 0x03A0, 0x03AC, 0x00000001, 0, 22,
+ 0x03A4, 24, 0, 0, 0, 0x03A4, 0, 0, 0),
+};
+
+static int clk_mt8365_apmixed_probe(struct platform_device *pdev)
+{
+ void __iomem *base;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct clk_hw *hw;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_devm_alloc_clk_data(dev, CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ hw = devm_clk_hw_register_gate(dev, "univ_en", "univpll2", 0,
+ base + 0x204, 0, 0, NULL);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+ clk_data->hws[CLK_APMIXED_UNIV_EN] = hw;
+
+ hw = devm_clk_hw_register_gate(dev, "usb20_en", "univ_en", 0,
+ base + 0x204, 1, 0, NULL);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+ clk_data->hws[CLK_APMIXED_USB20_EN] = hw;
+
+ ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_plls;
+
+ return 0;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+
+ return ret;
+}
+
+static const struct of_device_id of_match_clk_mt8365_apmixed[] = {
+ { .compatible = "mediatek,mt8365-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365_apmixed);
+
+static struct platform_driver clk_mt8365_apmixed_drv = {
+ .probe = clk_mt8365_apmixed_probe,
+ .driver = {
+ .name = "clk-mt8365-apmixed",
+ .of_match_table = of_match_clk_mt8365_apmixed,
+ },
+};
+builtin_platform_driver(clk_mt8365_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT8365 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-apu.c b/drivers/clk/mediatek/clk-mt8365-apu.c
new file mode 100644
index 000000000000..2583c4704ffa
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-apu.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs apu_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_APU(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &apu_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate apu_clks[] = {
+ GATE_APU(CLK_APU_AHB, "apu_ahb", "ifr_apu_axi", 5),
+ GATE_APU(CLK_APU_EDMA, "apu_edma", "apu_sel", 4),
+ GATE_APU(CLK_APU_IF_CK, "apu_if_ck", "apu_if_sel", 3),
+ GATE_APU(CLK_APU_JTAG, "apu_jtag", "clk26m", 2),
+ GATE_APU(CLK_APU_AXI, "apu_axi", "apu_sel", 1),
+ GATE_APU(CLK_APU_IPU_CK, "apu_ck", "apu_sel", 0),
+};
+
+static const struct mtk_clk_desc apu_desc = {
+ .clks = apu_clks,
+ .num_clks = ARRAY_SIZE(apu_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8365_apu[] = {
+ {
+ .compatible = "mediatek,mt8365-apu",
+ .data = &apu_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365_apu);
+
+static struct platform_driver clk_mt8365_apu_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8365-apu",
+ .of_match_table = of_match_clk_mt8365_apu,
+ },
+};
+module_platform_driver(clk_mt8365_apu_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 AI Processing Unit clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-cam.c b/drivers/clk/mediatek/clk-mt8365-cam.c
new file mode 100644
index 000000000000..89d2bd50263b
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-cam.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs cam_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_CAM(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &cam_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate cam_clks[] = {
+ GATE_CAM(CLK_CAM_LARB2, "cam_larb2", "mm_sel", 0),
+ GATE_CAM(CLK_CAM, "cam", "mm_sel", 6),
+ GATE_CAM(CLK_CAMTG, "camtg", "mm_sel", 7),
+ GATE_CAM(CLK_CAM_SENIF, "cam_senif", "mm_sel", 8),
+ GATE_CAM(CLK_CAMSV0, "camsv0", "mm_sel", 9),
+ GATE_CAM(CLK_CAMSV1, "camsv1", "mm_sel", 10),
+ GATE_CAM(CLK_CAM_FDVT, "cam_fdvt", "mm_sel", 11),
+ GATE_CAM(CLK_CAM_WPE, "cam_wpe", "mm_sel", 12),
+};
+
+static const struct mtk_clk_desc cam_desc = {
+ .clks = cam_clks,
+ .num_clks = ARRAY_SIZE(cam_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8365_cam[] = {
+ {
+ .compatible = "mediatek,mt8365-imgsys",
+ .data = &cam_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365_cam);
+
+static struct platform_driver clk_mt8365_cam_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8365-cam",
+ .of_match_table = of_match_clk_mt8365_cam,
+ },
+};
+module_platform_driver(clk_mt8365_cam_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 Camera clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-mfg.c b/drivers/clk/mediatek/clk-mt8365-mfg.c
new file mode 100644
index 000000000000..41bcd389119c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-mfg.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mfg0_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs mfg1_cg_regs = {
+ .set_ofs = 0x280,
+ .clr_ofs = 0x280,
+ .sta_ofs = 0x280,
+};
+
+#define GATE_MFG0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mfg0_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr)
+
+#define GATE_MFG1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mfg1_cg_regs, _shift, \
+ &mtk_clk_gate_ops_no_setclr)
+
+static const struct mtk_gate mfg_clks[] = {
+ /* MFG0 */
+ GATE_MFG0(CLK_MFG_BG3D, "mfg_bg3d", "mfg_sel", 0),
+ /* MFG1 */
+ GATE_MFG1(CLK_MFG_MBIST_DIAG, "mfg_mbist_diag", "mbist_diag_sel", 24),
+};
+
+static const struct mtk_clk_desc mfg_desc = {
+ .clks = mfg_clks,
+ .num_clks = ARRAY_SIZE(mfg_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8365_mfg[] = {
+ {
+ .compatible = "mediatek,mt8365-mfgcfg",
+ .data = &mfg_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365_mfg);
+
+static struct platform_driver clk_mt8365_mfg_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8365-mfg",
+ .of_match_table = of_match_clk_mt8365_mfg,
+ },
+};
+module_platform_driver(clk_mt8365_mfg_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 GPU mfg clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-mm.c b/drivers/clk/mediatek/clk-mt8365-mm.c
new file mode 100644
index 000000000000..56fb2a43ecd0
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-mm.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre, SAS
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs mm0_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+static const struct mtk_gate_regs mm1_cg_regs = {
+ .set_ofs = 0x114,
+ .clr_ofs = 0x118,
+ .sta_ofs = 0x110,
+};
+
+#define GATE_MM0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm0_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr)
+
+#define GATE_MM1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &mm1_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr)
+
+static const struct mtk_gate mm_clks[] = {
+ /* MM0 */
+ GATE_MM0(CLK_MM_MM_MDP_RDMA0, "mm_mdp_rdma0", "mm_sel", 0),
+ GATE_MM0(CLK_MM_MM_MDP_CCORR0, "mm_mdp_ccorr0", "mm_sel", 1),
+ GATE_MM0(CLK_MM_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 2),
+ GATE_MM0(CLK_MM_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 3),
+ GATE_MM0(CLK_MM_MM_MDP_TDSHP0, "mm_mdp_tdshp0", "mm_sel", 4),
+ GATE_MM0(CLK_MM_MM_MDP_WROT0, "mm_mdp_wrot0", "mm_sel", 5),
+ GATE_MM0(CLK_MM_MM_MDP_WDMA0, "mm_mdp_wdma0", "mm_sel", 6),
+ GATE_MM0(CLK_MM_MM_DISP_OVL0, "mm_disp_ovl0", "mm_sel", 7),
+ GATE_MM0(CLK_MM_MM_DISP_OVL0_2L, "mm_disp_ovl0_2l", "mm_sel", 8),
+ GATE_MM0(CLK_MM_MM_DISP_RSZ0, "mm_disp_rsz0", "mm_sel", 9),
+ GATE_MM0(CLK_MM_MM_DISP_RDMA0, "mm_disp_rdma0", "mm_sel", 10),
+ GATE_MM0(CLK_MM_MM_DISP_WDMA0, "mm_disp_wdma0", "mm_sel", 11),
+ GATE_MM0(CLK_MM_MM_DISP_COLOR0, "mm_disp_color0", "mm_sel", 12),
+ GATE_MM0(CLK_MM_MM_DISP_CCORR0, "mm_disp_ccorr0", "mm_sel", 13),
+ GATE_MM0(CLK_MM_MM_DISP_AAL0, "mm_disp_aal0", "mm_sel", 14),
+ GATE_MM0(CLK_MM_MM_DISP_GAMMA0, "mm_disp_gamma0", "mm_sel", 15),
+ GATE_MM0(CLK_MM_MM_DISP_DITHER0, "mm_disp_dither0", "mm_sel", 16),
+ GATE_MM0(CLK_MM_MM_DSI0, "mm_dsi0", "mm_sel", 17),
+ GATE_MM0(CLK_MM_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 18),
+ GATE_MM0(CLK_MM_MM_MDP_RDMA1, "mm_mdp_rdma1", "mm_sel", 19),
+ GATE_MM0(CLK_MM_DPI0_DPI0, "mm_dpi0_dpi0", "dpi0_sel", 20),
+ GATE_MM0(CLK_MM_MM_FAKE, "mm_fake", "mm_sel", 21),
+ GATE_MM0(CLK_MM_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 22),
+ GATE_MM0(CLK_MM_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 23),
+ GATE_MM0(CLK_MM_MM_SMI_COMM0, "mm_smi_comm0", "mm_sel", 24),
+ GATE_MM0(CLK_MM_MM_SMI_COMM1, "mm_smi_comm1", "mm_sel", 25),
+ GATE_MM0(CLK_MM_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 26),
+ GATE_MM0(CLK_MM_MM_SMI_IMG, "mm_smi_img", "mm_sel", 27),
+ GATE_MM0(CLK_MM_MM_SMI_CAM, "mm_smi_cam", "mm_sel", 28),
+ GATE_MM0(CLK_MM_IMG_IMG_DL_RELAY, "mm_dl_relay", "mm_sel", 29),
+ GATE_MM0(CLK_MM_IMG_IMG_DL_ASYNC_TOP, "mm_dl_async_top", "mm_sel", 30),
+ GATE_MM0(CLK_MM_DSI0_DIG_DSI, "mm_dsi0_dig_dsi", "dsi0_lntc_dsick", 31),
+ /* MM1 */
+ GATE_MM1(CLK_MM_26M_HRTWT, "mm_f26m_hrtwt", "clk26m", 0),
+ GATE_MM1(CLK_MM_MM_DPI0, "mm_dpi0", "mm_sel", 1),
+ GATE_MM1(CLK_MM_LVDSTX_PXL, "mm_flvdstx_pxl", "vpll_dpix", 2),
+ GATE_MM1(CLK_MM_LVDSTX_CTS, "mm_flvdstx_cts", "lvdstx_dig_cts", 3),
+};
+
+static const struct mtk_clk_desc mm_desc = {
+ .clks = mm_clks,
+ .num_clks = ARRAY_SIZE(mm_clks),
+};
+
+static const struct platform_device_id clk_mt8365_mm_id_table[] = {
+ { .name = "clk-mt8365-mm", .driver_data = (kernel_ulong_t)&mm_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, clk_mt8365_mm_id_table);
+
+static struct platform_driver clk_mt8365_mm_drv = {
+ .probe = mtk_clk_pdev_probe,
+ .remove = mtk_clk_pdev_remove,
+ .driver = {
+ .name = "clk-mt8365-mm",
+ },
+ .id_table = clk_mt8365_mm_id_table,
+};
+module_platform_driver(clk_mt8365_mm_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 MultiMedia clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-vdec.c b/drivers/clk/mediatek/clk-mt8365-vdec.c
new file mode 100644
index 000000000000..f5d0518bc2e0
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-vdec.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs vdec0_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs vdec1_cg_regs = {
+ .set_ofs = 0x8,
+ .clr_ofs = 0xc,
+ .sta_ofs = 0x8,
+};
+
+#define GATE_VDEC0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec0_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_VDEC1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &vdec1_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate vdec_clks[] = {
+ /* VDEC0 */
+ GATE_VDEC0(CLK_VDEC_VDEC, "vdec_fvdec_ck", "mm_sel", 0),
+ /* VDEC1 */
+ GATE_VDEC1(CLK_VDEC_LARB1, "vdec_flarb1_ck", "mm_sel", 0),
+};
+
+static const struct mtk_clk_desc vdec_desc = {
+ .clks = vdec_clks,
+ .num_clks = ARRAY_SIZE(vdec_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8365_vdec[] = {
+ {
+ .compatible = "mediatek,mt8365-vdecsys",
+ .data = &vdec_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365_vdec);
+
+static struct platform_driver clk_mt8365_vdec_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8365-vdec",
+ .of_match_table = of_match_clk_mt8365_vdec,
+ },
+};
+module_platform_driver(clk_mt8365_vdec_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 Video Decoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365-venc.c b/drivers/clk/mediatek/clk-mt8365-venc.c
new file mode 100644
index 000000000000..35abd908537c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365-venc.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+
+static const struct mtk_gate_regs venc_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_VENC(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &venc_cg_regs, _shift, \
+ &mtk_clk_gate_ops_setclr_inv)
+
+static const struct mtk_gate venc_clks[] = {
+ /* VENC */
+ GATE_VENC(CLK_VENC, "venc_fvenc_ck", "mm_sel", 4),
+ GATE_VENC(CLK_VENC_JPGENC, "venc_jpgenc_ck", "mm_sel", 8),
+};
+
+static const struct mtk_clk_desc venc_desc = {
+ .clks = venc_clks,
+ .num_clks = ARRAY_SIZE(venc_clks),
+};
+
+static const struct of_device_id of_match_clk_mt8365_venc[] = {
+ {
+ .compatible = "mediatek,mt8365-vencsys",
+ .data = &venc_desc,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365_venc);
+
+static struct platform_driver clk_mt8365_venc_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8365-venc",
+ .of_match_table = of_match_clk_mt8365_venc,
+ },
+};
+module_platform_driver(clk_mt8365_venc_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 Video Encoders clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8365.c b/drivers/clk/mediatek/clk-mt8365.c
new file mode 100644
index 000000000000..e7952121112e
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8365.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ * Copyright (C) 2023 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mediatek,mt8365-clk.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk-gate.h"
+#include "clk-mtk.h"
+#include "clk-mux.h"
+
+static DEFINE_SPINLOCK(mt8365_clk_lock);
+
+static const struct mtk_fixed_clk top_fixed_clks[] = {
+ FIXED_CLK(CLK_TOP_CLK_NULL, "clk_null", NULL, 0),
+ FIXED_CLK(CLK_TOP_I2S0_BCK, "i2s0_bck", NULL, 26000000),
+ FIXED_CLK(CLK_TOP_DSI0_LNTC_DSICK, "dsi0_lntc_dsick", "clk26m",
+ 75000000),
+ FIXED_CLK(CLK_TOP_VPLL_DPIX, "vpll_dpix", "clk26m", 75000000),
+ FIXED_CLK(CLK_TOP_LVDSTX_CLKDIG_CTS, "lvdstx_dig_cts", "clk26m",
+ 52500000),
+};
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_SYS_26M_D2, "sys_26m_d2", "clk26m", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "mainpll", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "mainpll", 1, 8),
+ FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "mainpll", 1, 16),
+ FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "mainpll", 1, 32),
+ FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3),
+ FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "mainpll", 1, 6),
+ FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "mainpll", 1, 12),
+ FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "mainpll", 1, 24),
+ FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
+ FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "mainpll", 1, 10),
+ FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "mainpll", 1, 20),
+ FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7),
+ FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "mainpll", 1, 14),
+ FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "mainpll", 1, 28),
+ FACTOR(CLK_TOP_UNIVPLL, "univpll", "univ_en", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
+ FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll", 1, 6),
+ FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll", 1, 12),
+ FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll", 1, 24),
+ FACTOR(CLK_TOP_UNIVPLL2_D32, "univpll2_d32", "univpll", 1, 96),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll", 1, 10),
+ FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll", 1, 20),
+ FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
+ FACTOR(CLK_TOP_MFGPLL, "mfgpll_ck", "mfgpll", 1, 1),
+ FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2),
+ FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4),
+ FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8),
+ FACTOR(CLK_TOP_LVDSPLL_D16, "lvdspll_d16", "lvdspll", 1, 16),
+ FACTOR(CLK_TOP_USB20_192M, "usb20_192m_ck", "usb20_en", 1, 13),
+ FACTOR(CLK_TOP_USB20_192M_D4, "usb20_192m_d4", "usb20_192m_ck", 1, 4),
+ FACTOR(CLK_TOP_USB20_192M_D8, "usb20_192m_d8", "usb20_192m_ck", 1, 8),
+ FACTOR(CLK_TOP_USB20_192M_D16, "usb20_192m_d16", "usb20_192m_ck",
+ 1, 16),
+ FACTOR(CLK_TOP_USB20_192M_D32, "usb20_192m_d32", "usb20_192m_ck",
+ 1, 32),
+ FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
+ FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1_ck", 1, 2),
+ FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1_ck", 1, 4),
+ FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1_ck", 1, 8),
+ FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1),
+ FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2_ck", 1, 2),
+ FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2_ck", 1, 4),
+ FACTOR(CLK_TOP_APLL2_D8, "apll2_d8", "apll2_ck", 1, 8),
+ FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+ FACTOR(CLK_TOP_DSPPLL, "dsppll_ck", "dsppll", 1, 1),
+ FACTOR(CLK_TOP_DSPPLL_D2, "dsppll_d2", "dsppll", 1, 2),
+ FACTOR(CLK_TOP_DSPPLL_D4, "dsppll_d4", "dsppll", 1, 4),
+ FACTOR(CLK_TOP_DSPPLL_D8, "dsppll_d8", "dsppll", 1, 8),
+ FACTOR(CLK_TOP_APUPLL, "apupll_ck", "apupll", 1, 1),
+ FACTOR(CLK_TOP_CLK26M_D52, "clk26m_d52", "clk26m", 1, 52),
+};
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "syspll_d7",
+ "syspll1_d4",
+ "syspll3_d2"
+};
+
+static const char * const mem_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "syspll_d3",
+ "syspll1_d2"
+};
+
+static const char * const mm_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll1_d2",
+ "mmpll_d2"
+};
+
+static const char * const scp_parents[] = {
+ "clk26m",
+ "syspll4_d2",
+ "univpll2_d2",
+ "syspll1_d2",
+ "univpll1_d2",
+ "syspll_d3",
+ "univpll_d3"
+};
+
+static const char * const mfg_parents[] = {
+ "clk26m",
+ "mfgpll_ck",
+ "syspll_d3",
+ "univpll_d3"
+};
+
+static const char * const atb_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll1_d2"
+};
+
+static const char * const camtg_parents[] = {
+ "clk26m",
+ "usb20_192m_d8",
+ "univpll2_d8",
+ "usb20_192m_d4",
+ "univpll2_d32",
+ "usb20_192m_d16",
+ "usb20_192m_d32"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll2_d8"
+};
+
+static const char * const spi_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "univpll2_d4",
+ "univpll2_d8"
+};
+
+static const char * const msdc50_0_hc_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "univpll1_d4",
+ "syspll2_d2"
+};
+
+static const char * const msdc50_0_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "univpll1_d2",
+ "syspll1_d2",
+ "univpll_d5",
+ "syspll2_d2",
+ "univpll1_d4",
+ "syspll4_d2"
+};
+
+static const char * const msdc50_2_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "univpll_d3",
+ "univpll1_d2",
+ "syspll1_d2",
+ "univpll2_d2",
+ "syspll2_d2",
+ "univpll1_d4"
+};
+
+static const char * const msdc30_1_parents[] = {
+ "clk26m",
+ "msdcpll_d2",
+ "univpll2_d2",
+ "syspll2_d2",
+ "univpll1_d4",
+ "syspll1_d4",
+ "syspll2_d4",
+ "univpll2_d8"
+};
+
+static const char * const audio_parents[] = {
+ "clk26m",
+ "syspll3_d4",
+ "syspll4_d4",
+ "syspll1_d16"
+};
+
+static const char * const aud_intbus_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll4_d2"
+};
+
+static const char * const aud_1_parents[] = {
+ "clk26m",
+ "apll1_ck"
+};
+
+static const char * const aud_2_parents[] = {
+ "clk26m",
+ "apll2_ck"
+};
+
+static const char * const aud_engen1_parents[] = {
+ "clk26m",
+ "apll1_d2",
+ "apll1_d4",
+ "apll1_d8"
+};
+
+static const char * const aud_engen2_parents[] = {
+ "clk26m",
+ "apll2_d2",
+ "apll2_d4",
+ "apll2_d8"
+};
+
+static const char * const aud_spdif_parents[] = {
+ "clk26m",
+ "univpll_d2"
+};
+
+static const char * const disp_pwm_parents[] = {
+ "clk26m",
+ "univpll2_d4"
+};
+
+static const char * const dxcc_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll1_d4",
+ "syspll1_d8"
+};
+
+static const char * const ssusb_sys_parents[] = {
+ "clk26m",
+ "univpll3_d4",
+ "univpll2_d4",
+ "univpll3_d2"
+};
+
+static const char * const spm_parents[] = {
+ "clk26m",
+ "syspll1_d8"
+};
+
+static const char * const i2c_parents[] = {
+ "clk26m",
+ "univpll3_d4",
+ "univpll3_d2",
+ "syspll1_d8",
+ "syspll2_d8"
+};
+
+static const char * const pwm_parents[] = {
+ "clk26m",
+ "univpll3_d4",
+ "syspll1_d8"
+};
+
+static const char * const senif_parents[] = {
+ "clk26m",
+ "univpll1_d4",
+ "univpll1_d2",
+ "univpll2_d2"
+};
+
+static const char * const aes_fde_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "univpll_d3",
+ "univpll2_d2",
+ "univpll1_d2",
+ "syspll1_d2"
+};
+
+static const char * const dpi0_parents[] = {
+ "clk26m",
+ "lvdspll_d2",
+ "lvdspll_d4",
+ "lvdspll_d8",
+ "lvdspll_d16"
+};
+
+static const char * const dsp_parents[] = {
+ "clk26m",
+ "sys_26m_d2",
+ "dsppll_ck",
+ "dsppll_d2",
+ "dsppll_d4",
+ "dsppll_d8"
+};
+
+static const char * const nfi2x_parents[] = {
+ "clk26m",
+ "syspll2_d2",
+ "syspll_d7",
+ "syspll_d3",
+ "syspll2_d4",
+ "msdcpll_d2",
+ "univpll1_d2",
+ "univpll_d5"
+};
+
+static const char * const nfiecc_parents[] = {
+ "clk26m",
+ "syspll4_d2",
+ "univpll2_d4",
+ "syspll_d7",
+ "univpll1_d2",
+ "syspll1_d2",
+ "univpll2_d2",
+ "syspll_d5"
+};
+
+static const char * const ecc_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "univpll1_d2",
+ "univpll_d3",
+ "syspll_d2"
+};
+
+static const char * const eth_parents[] = {
+ "clk26m",
+ "univpll2_d8",
+ "syspll4_d4",
+ "syspll1_d8",
+ "syspll4_d2"
+};
+
+static const char * const gcpu_parents[] = {
+ "clk26m",
+ "univpll_d3",
+ "univpll2_d2",
+ "syspll_d3",
+ "syspll2_d2"
+};
+
+static const char * const gcpu_cpm_parents[] = {
+ "clk26m",
+ "univpll2_d2",
+ "syspll2_d2"
+};
+
+static const char * const apu_parents[] = {
+ "clk26m",
+ "univpll_d2",
+ "apupll_ck",
+ "mmpll_ck",
+ "syspll_d3",
+ "univpll1_d2",
+ "syspll1_d2",
+ "syspll1_d4"
+};
+
+static const char * const mbist_diag_parents[] = {
+ "clk26m",
+ "syspll4_d4",
+ "univpll2_d8"
+};
+
+static const char * const apll_i2s_parents[] = {
+ "aud_1_sel",
+ "aud_2_sel"
+};
+
+static struct mtk_composite top_misc_muxes[] = {
+ /* CLK_CFG_11 */
+ MUX_GATE(CLK_TOP_MBIST_DIAG_SEL, "mbist_diag_sel", mbist_diag_parents,
+ 0x0ec, 0, 2, 7),
+ /* Audio MUX */
+ MUX(CLK_TOP_APLL_I2S0_SEL, "apll_i2s0_sel", apll_i2s_parents, 0x0320, 11, 1),
+ MUX(CLK_TOP_APLL_I2S1_SEL, "apll_i2s1_sel", apll_i2s_parents, 0x0320, 12, 1),
+ MUX(CLK_TOP_APLL_I2S2_SEL, "apll_i2s2_sel", apll_i2s_parents, 0x0320, 13, 1),
+ MUX(CLK_TOP_APLL_I2S3_SEL, "apll_i2s3_sel", apll_i2s_parents, 0x0320, 14, 1),
+ MUX(CLK_TOP_APLL_TDMOUT_SEL, "apll_tdmout_sel", apll_i2s_parents, 0x0320, 15, 1),
+ MUX(CLK_TOP_APLL_TDMIN_SEL, "apll_tdmin_sel", apll_i2s_parents, 0x0320, 16, 1),
+ MUX(CLK_TOP_APLL_SPDIF_SEL, "apll_spdif_sel", apll_i2s_parents, 0x0320, 17, 1),
+};
+
+#define CLK_CFG_UPDATE 0x004
+#define CLK_CFG_UPDATE1 0x008
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+ 0x040, 0x044, 0x048, 0, 2, 7, CLK_CFG_UPDATE,
+ 0, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x040,
+ 0x044, 0x048, 8, 2, 15, CLK_CFG_UPDATE, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x040, 0x044,
+ 0x048, 16, 3, 23, CLK_CFG_UPDATE, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x040,
+ 0x044, 0x048, 24, 3, 31, CLK_CFG_UPDATE, 3),
+ /* CLK_CFG_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x050,
+ 0x054, 0x058, 0, 2, 7, CLK_CFG_UPDATE, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x050,
+ 0x054, 0x058, 8, 2, 15, CLK_CFG_UPDATE, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents,
+ 0x050, 0x054, 0x058, 16, 3, 23, CLK_CFG_UPDATE, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG1_SEL, "camtg1_sel", camtg_parents,
+ 0x050, 0x054, 0x058, 24, 3, 31, CLK_CFG_UPDATE, 7),
+ /* CLK_CFG_2 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x060,
+ 0x064, 0x068, 0, 1, 7, CLK_CFG_UPDATE, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x060,
+ 0x064, 0x068, 8, 2, 15, CLK_CFG_UPDATE, 9),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_HC_SEL, "msdc50_0_hc_sel",
+ msdc50_0_hc_parents, 0x060, 0x064, 0x068, 16, 2,
+ 23, CLK_CFG_UPDATE, 10, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC2_2_HC_SEL, "msdc2_2_hc_sel",
+ msdc50_0_hc_parents, 0x060, 0x064, 0x068, 24, 2,
+ 31, CLK_CFG_UPDATE, 11, 0),
+ /* CLK_CFG_3 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
+ msdc50_0_parents, 0x070, 0x074, 0x078, 0, 3, 7,
+ CLK_CFG_UPDATE, 12, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC50_2_SEL, "msdc50_2_sel",
+ msdc50_2_parents, 0x070, 0x074, 0x078, 8, 3, 15,
+ CLK_CFG_UPDATE, 13, 0),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
+ msdc30_1_parents, 0x070, 0x074, 0x078, 16, 3, 23,
+ CLK_CFG_UPDATE, 14, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents,
+ 0x070, 0x074, 0x078, 24, 2, 31, CLK_CFG_UPDATE,
+ 15),
+ /* CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel",
+ aud_intbus_parents, 0x080, 0x084, 0x088, 0, 2, 7,
+ CLK_CFG_UPDATE, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents,
+ 0x080, 0x084, 0x088, 8, 1, 15, CLK_CFG_UPDATE, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents,
+ 0x080, 0x084, 0x088, 16, 1, 23, CLK_CFG_UPDATE,
+ 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_ENGEN1_SEL, "aud_engen1_sel",
+ aud_engen1_parents, 0x080, 0x084, 0x088, 24, 2, 31,
+ CLK_CFG_UPDATE, 19),
+ /* CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_ENGEN2_SEL, "aud_engen2_sel",
+ aud_engen2_parents, 0x090, 0x094, 0x098, 0, 2, 7,
+ CLK_CFG_UPDATE, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_SPDIF_SEL, "aud_spdif_sel",
+ aud_spdif_parents, 0x090, 0x094, 0x098, 8, 1, 15,
+ CLK_CFG_UPDATE, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_PWM_SEL, "disp_pwm_sel",
+ disp_pwm_parents, 0x090, 0x094, 0x098, 16, 2, 23,
+ CLK_CFG_UPDATE, 22),
+ /* CLK_CFG_6 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DXCC_SEL, "dxcc_sel", dxcc_parents,
+ 0x0a0, 0x0a4, 0x0a8, 0, 2, 7, CLK_CFG_UPDATE,
+ 24, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSUSB_SYS_SEL, "ssusb_sys_sel",
+ ssusb_sys_parents, 0x0a0, 0x0a4, 0x0a8, 8, 2, 15,
+ CLK_CFG_UPDATE, 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSUSB_XHCI_SEL, "ssusb_xhci_sel",
+ ssusb_sys_parents, 0x0a0, 0x0a4, 0x0a8, 16, 2, 23,
+ CLK_CFG_UPDATE, 26),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SPM_SEL, "spm_sel", spm_parents,
+ 0x0a0, 0x0a4, 0x0a8, 24, 1, 31, CLK_CFG_UPDATE,
+ 27, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
+ /* CLK_CFG_7 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C_SEL, "i2c_sel", i2c_parents, 0x0b0,
+ 0x0b4, 0x0b8, 0, 3, 7, CLK_CFG_UPDATE, 28),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0b0,
+ 0x0b4, 0x0b8, 8, 2, 15, CLK_CFG_UPDATE, 29),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SENIF_SEL, "senif_sel", senif_parents,
+ 0x0b0, 0x0b4, 0x0b8, 16, 2, 23, CLK_CFG_UPDATE,
+ 30),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AES_FDE_SEL, "aes_fde_sel",
+ aes_fde_parents, 0x0b0, 0x0b4, 0x0b8, 24, 3, 31,
+ CLK_CFG_UPDATE, 31),
+ /* CLK_CFG_8 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTM_SEL, "camtm_sel", senif_parents,
+ 0x0c0, 0x0c4, 0x0c8, 0, 2, 7, CLK_CFG_UPDATE1, 0),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x0c0,
+ 0x0c4, 0x0c8, 8, 3, 15, CLK_CFG_UPDATE1, 1),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi0_parents, 0x0c0,
+ 0x0c4, 0x0c8, 16, 3, 23, CLK_CFG_UPDATE1, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP_SEL, "dsp_sel", dsp_parents, 0x0c0,
+ 0x0c4, 0x0c8, 24, 3, 31, CLK_CFG_UPDATE1, 3),
+ /* CLK_CFG_9 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NFI2X_SEL, "nfi2x_sel", nfi2x_parents,
+ 0x0d0, 0x0d4, 0x0d8, 0, 3, 7, CLK_CFG_UPDATE1, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_NFIECC_SEL, "nfiecc_sel", nfiecc_parents,
+ 0x0d0, 0x0d4, 0x0d8, 8, 3, 15, CLK_CFG_UPDATE1, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ECC_SEL, "ecc_sel", ecc_parents, 0x0d0,
+ 0x0d4, 0x0d8, 16, 3, 23, CLK_CFG_UPDATE1, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ETH_SEL, "eth_sel", eth_parents, 0x0d0,
+ 0x0d4, 0x0d8, 24, 3, 31, CLK_CFG_UPDATE1, 7),
+ /* CLK_CFG_10 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0e0,
+ 0x0e4, 0x0e8, 0, 3, 7, CLK_CFG_UPDATE1, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_GCPU_CPM_SEL, "gcpu_cpm_sel",
+ gcpu_cpm_parents, 0x0e0, 0x0e4, 0x0e8, 8, 2, 15,
+ CLK_CFG_UPDATE1, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APU_SEL, "apu_sel", apu_parents, 0x0e0,
+ 0x0e4, 0x0e8, 16, 3, 23, CLK_CFG_UPDATE1, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_APU_IF_SEL, "apu_if_sel", apu_parents,
+ 0x0e0, 0x0e4, 0x0e8, 24, 3, 31, CLK_CFG_UPDATE1,
+ 11),
+};
+
+static const char * const mcu_bus_parents[] = {
+ "clk26m",
+ "armpll",
+ "mainpll",
+ "univpll_d2"
+};
+
+static struct mtk_composite mcu_muxes[] = {
+ /* bus_pll_divider_cfg */
+ MUX_GATE_FLAGS(CLK_MCU_BUS_SEL, "mcu_bus_sel", mcu_bus_parents, 0x7C0,
+ 9, 2, -1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
+};
+
+#define DIV_ADJ_F(_id, _name, _parent, _reg, _shift, _width, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .div_reg = _reg, \
+ .div_shift = _shift, \
+ .div_width = _width, \
+ .clk_divider_flags = _flags, \
+}
+
+static const struct mtk_clk_divider top_adj_divs[] = {
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV0, "apll12_ck_div0", "apll_i2s0_sel",
+ 0x324, 0, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV1, "apll12_ck_div1", "apll_i2s1_sel",
+ 0x324, 8, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV2, "apll12_ck_div2", "apll_i2s2_sel",
+ 0x324, 16, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV3, "apll12_ck_div3", "apll_i2s3_sel",
+ 0x324, 24, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV4, "apll12_ck_div4", "apll_tdmout_sel",
+ 0x328, 0, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV4B, "apll12_ck_div4b", "apll_tdmout_sel",
+ 0x328, 8, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV5, "apll12_ck_div5", "apll_tdmin_sel",
+ 0x328, 16, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV5B, "apll12_ck_div5b", "apll_tdmin_sel",
+ 0x328, 24, 8, CLK_DIVIDER_ROUND_CLOSEST),
+ DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV6, "apll12_ck_div6", "apll_spdif_sel",
+ 0x32c, 0, 8, CLK_DIVIDER_ROUND_CLOSEST),
+};
+
+static const struct mtk_gate_regs top0_cg_regs = {
+ .set_ofs = 0,
+ .clr_ofs = 0,
+ .sta_ofs = 0,
+};
+
+static const struct mtk_gate_regs top1_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x104,
+ .sta_ofs = 0x104,
+};
+
+static const struct mtk_gate_regs top2_cg_regs = {
+ .set_ofs = 0x320,
+ .clr_ofs = 0x320,
+ .sta_ofs = 0x320,
+};
+
+#define GATE_TOP0(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top0_cg_regs, \
+ _shift, &mtk_clk_gate_ops_no_setclr)
+
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, \
+ _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+#define GATE_TOP2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top2_cg_regs, \
+ _shift, &mtk_clk_gate_ops_no_setclr_inv)
+
+static const struct mtk_gate top_clk_gates[] = {
+ GATE_TOP0(CLK_TOP_CONN_32K, "conn_32k", "clk32k", 10),
+ GATE_TOP0(CLK_TOP_CONN_26M, "conn_26m", "clk26m", 11),
+ GATE_TOP0(CLK_TOP_DSP_32K, "dsp_32k", "clk32k", 16),
+ GATE_TOP0(CLK_TOP_DSP_26M, "dsp_26m", "clk26m", 17),
+ GATE_TOP1(CLK_TOP_USB20_48M_EN, "usb20_48m_en", "usb20_192m_d4", 8),
+ GATE_TOP1(CLK_TOP_UNIVPLL_48M_EN, "univpll_48m_en", "usb20_192m_d4", 9),
+ GATE_TOP1(CLK_TOP_LVDSTX_CLKDIG_EN, "lvdstx_dig_en", "lvdstx_dig_cts", 20),
+ GATE_TOP1(CLK_TOP_VPLL_DPIX_EN, "vpll_dpix_en", "vpll_dpix", 21),
+ GATE_TOP1(CLK_TOP_SSUSB_TOP_CK_EN, "ssusb_top_ck_en", NULL, 22),
+ GATE_TOP1(CLK_TOP_SSUSB_PHY_CK_EN, "ssusb_phy_ck_en", NULL, 23),
+ GATE_TOP2(CLK_TOP_AUD_I2S0_M, "aud_i2s0_m_ck", "apll12_ck_div0", 0),
+ GATE_TOP2(CLK_TOP_AUD_I2S1_M, "aud_i2s1_m_ck", "apll12_ck_div1", 1),
+ GATE_TOP2(CLK_TOP_AUD_I2S2_M, "aud_i2s2_m_ck", "apll12_ck_div2", 2),
+ GATE_TOP2(CLK_TOP_AUD_I2S3_M, "aud_i2s3_m_ck", "apll12_ck_div3", 3),
+ GATE_TOP2(CLK_TOP_AUD_TDMOUT_M, "aud_tdmout_m_ck", "apll12_ck_div4", 4),
+ GATE_TOP2(CLK_TOP_AUD_TDMOUT_B, "aud_tdmout_b_ck", "apll12_ck_div4b", 5),
+ GATE_TOP2(CLK_TOP_AUD_TDMIN_M, "aud_tdmin_m_ck", "apll12_ck_div5", 6),
+ GATE_TOP2(CLK_TOP_AUD_TDMIN_B, "aud_tdmin_b_ck", "apll12_ck_div5b", 7),
+ GATE_TOP2(CLK_TOP_AUD_SPDIF_M, "aud_spdif_m_ck", "apll12_ck_div6", 8),
+};
+
+static const struct mtk_gate_regs ifr2_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x84,
+ .sta_ofs = 0x90,
+};
+
+static const struct mtk_gate_regs ifr3_cg_regs = {
+ .set_ofs = 0x88,
+ .clr_ofs = 0x8c,
+ .sta_ofs = 0x94,
+};
+
+static const struct mtk_gate_regs ifr4_cg_regs = {
+ .set_ofs = 0xa4,
+ .clr_ofs = 0xa8,
+ .sta_ofs = 0xac,
+};
+
+static const struct mtk_gate_regs ifr5_cg_regs = {
+ .set_ofs = 0xc0,
+ .clr_ofs = 0xc4,
+ .sta_ofs = 0xc8,
+};
+
+static const struct mtk_gate_regs ifr6_cg_regs = {
+ .set_ofs = 0xd0,
+ .clr_ofs = 0xd4,
+ .sta_ofs = 0xd8,
+};
+
+#define GATE_IFRX(_id, _name, _parent, _shift, _regs) \
+ GATE_MTK(_id, _name, _parent, _regs, _shift, \
+ &mtk_clk_gate_ops_setclr)
+
+#define GATE_IFR2(_id, _name, _parent, _shift) \
+ GATE_IFRX(_id, _name, _parent, _shift, &ifr2_cg_regs)
+
+#define GATE_IFR3(_id, _name, _parent, _shift) \
+ GATE_IFRX(_id, _name, _parent, _shift, &ifr3_cg_regs)
+
+#define GATE_IFR4(_id, _name, _parent, _shift) \
+ GATE_IFRX(_id, _name, _parent, _shift, &ifr4_cg_regs)
+
+#define GATE_IFR5(_id, _name, _parent, _shift) \
+ GATE_IFRX(_id, _name, _parent, _shift, &ifr5_cg_regs)
+
+#define GATE_IFR6(_id, _name, _parent, _shift) \
+ GATE_IFRX(_id, _name, _parent, _shift, &ifr6_cg_regs)
+
+static const struct mtk_gate ifr_clks[] = {
+ /* IFR2 */
+ GATE_IFR2(CLK_IFR_PMIC_TMR, "ifr_pmic_tmr", "clk26m", 0),
+ GATE_IFR2(CLK_IFR_PMIC_AP, "ifr_pmic_ap", "clk26m", 1),
+ GATE_IFR2(CLK_IFR_PMIC_MD, "ifr_pmic_md", "clk26m", 2),
+ GATE_IFR2(CLK_IFR_PMIC_CONN, "ifr_pmic_conn", "clk26m", 3),
+ GATE_IFR2(CLK_IFR_ICUSB, "ifr_icusb", "axi_sel", 8),
+ GATE_IFR2(CLK_IFR_GCE, "ifr_gce", "axi_sel", 9),
+ GATE_IFR2(CLK_IFR_THERM, "ifr_therm", "axi_sel", 10),
+ GATE_IFR2(CLK_IFR_PWM_HCLK, "ifr_pwm_hclk", "axi_sel", 15),
+ GATE_IFR2(CLK_IFR_PWM1, "ifr_pwm1", "pwm_sel", 16),
+ GATE_IFR2(CLK_IFR_PWM2, "ifr_pwm2", "pwm_sel", 17),
+ GATE_IFR2(CLK_IFR_PWM3, "ifr_pwm3", "pwm_sel", 18),
+ GATE_IFR2(CLK_IFR_PWM4, "ifr_pwm4", "pwm_sel", 19),
+ GATE_IFR2(CLK_IFR_PWM5, "ifr_pwm5", "pwm_sel", 20),
+ GATE_IFR2(CLK_IFR_PWM, "ifr_pwm", "pwm_sel", 21),
+ GATE_IFR2(CLK_IFR_UART0, "ifr_uart0", "uart_sel", 22),
+ GATE_IFR2(CLK_IFR_UART1, "ifr_uart1", "uart_sel", 23),
+ GATE_IFR2(CLK_IFR_UART2, "ifr_uart2", "uart_sel", 24),
+ GATE_IFR2(CLK_IFR_DSP_UART, "ifr_dsp_uart", "uart_sel", 26),
+ GATE_IFR2(CLK_IFR_GCE_26M, "ifr_gce_26m", "clk26m", 27),
+ GATE_IFR2(CLK_IFR_CQ_DMA_FPC, "ifr_cq_dma_fpc", "axi_sel", 28),
+ GATE_IFR2(CLK_IFR_BTIF, "ifr_btif", "axi_sel", 31),
+ /* IFR3 */
+ GATE_IFR3(CLK_IFR_SPI0, "ifr_spi0", "spi_sel", 1),
+ GATE_IFR3(CLK_IFR_MSDC0_HCLK, "ifr_msdc0", "msdc50_0_hc_sel", 2),
+ GATE_IFR3(CLK_IFR_MSDC2_HCLK, "ifr_msdc2", "msdc2_2_hc_sel", 3),
+ GATE_IFR3(CLK_IFR_MSDC1_HCLK, "ifr_msdc1", "axi_sel", 4),
+ GATE_IFR3(CLK_IFR_DVFSRC, "ifr_dvfsrc", "clk26m", 7),
+ GATE_IFR3(CLK_IFR_GCPU, "ifr_gcpu", "axi_sel", 8),
+ GATE_IFR3(CLK_IFR_TRNG, "ifr_trng", "axi_sel", 9),
+ GATE_IFR3(CLK_IFR_AUXADC, "ifr_auxadc", "clk26m", 10),
+ GATE_IFR3(CLK_IFR_CPUM, "ifr_cpum", "clk26m", 11),
+ GATE_IFR3(CLK_IFR_AUXADC_MD, "ifr_auxadc_md", "clk26m", 14),
+ GATE_IFR3(CLK_IFR_AP_DMA, "ifr_ap_dma", "axi_sel", 18),
+ GATE_IFR3(CLK_IFR_DEBUGSYS, "ifr_debugsys", "axi_sel", 24),
+ GATE_IFR3(CLK_IFR_AUDIO, "ifr_audio", "axi_sel", 25),
+ /* IFR4 */
+ GATE_IFR4(CLK_IFR_PWM_FBCLK6, "ifr_pwm_fbclk6", "pwm_sel", 0),
+ GATE_IFR4(CLK_IFR_DISP_PWM, "ifr_disp_pwm", "disp_pwm_sel", 2),
+ GATE_IFR4(CLK_IFR_AUD_26M_BK, "ifr_aud_26m_bk", "clk26m", 4),
+ GATE_IFR4(CLK_IFR_CQ_DMA, "ifr_cq_dma", "axi_sel", 27),
+ /* IFR5 */
+ GATE_IFR5(CLK_IFR_MSDC0_SF, "ifr_msdc0_sf", "msdc50_0_sel", 0),
+ GATE_IFR5(CLK_IFR_MSDC1_SF, "ifr_msdc1_sf", "msdc50_0_sel", 1),
+ GATE_IFR5(CLK_IFR_MSDC2_SF, "ifr_msdc2_sf", "msdc50_0_sel", 2),
+ GATE_IFR5(CLK_IFR_AP_MSDC0, "ifr_ap_msdc0", "msdc50_0_sel", 7),
+ GATE_IFR5(CLK_IFR_MD_MSDC0, "ifr_md_msdc0", "msdc50_0_sel", 8),
+ GATE_IFR5(CLK_IFR_MSDC0_SRC, "ifr_msdc0_src", "msdc50_0_sel", 9),
+ GATE_IFR5(CLK_IFR_MSDC1_SRC, "ifr_msdc1_src", "msdc30_1_sel", 10),
+ GATE_IFR5(CLK_IFR_MSDC2_SRC, "ifr_msdc2_src", "msdc50_2_sel", 11),
+ GATE_IFR5(CLK_IFR_PWRAP_TMR, "ifr_pwrap_tmr", "clk26m", 12),
+ GATE_IFR5(CLK_IFR_PWRAP_SPI, "ifr_pwrap_spi", "clk26m", 13),
+ GATE_IFR5(CLK_IFR_PWRAP_SYS, "ifr_pwrap_sys", "clk26m", 14),
+ GATE_MTK_FLAGS(CLK_IFR_MCU_PM_BK, "ifr_mcu_pm_bk", NULL, &ifr5_cg_regs,
+ 17, &mtk_clk_gate_ops_setclr, CLK_IGNORE_UNUSED),
+ GATE_IFR5(CLK_IFR_IRRX_26M, "ifr_irrx_26m", "clk26m", 22),
+ GATE_IFR5(CLK_IFR_IRRX_32K, "ifr_irrx_32k", "clk32k", 23),
+ GATE_IFR5(CLK_IFR_I2C0_AXI, "ifr_i2c0_axi", "i2c_sel", 24),
+ GATE_IFR5(CLK_IFR_I2C1_AXI, "ifr_i2c1_axi", "i2c_sel", 25),
+ GATE_IFR5(CLK_IFR_I2C2_AXI, "ifr_i2c2_axi", "i2c_sel", 26),
+ GATE_IFR5(CLK_IFR_I2C3_AXI, "ifr_i2c3_axi", "i2c_sel", 27),
+ GATE_IFR5(CLK_IFR_NIC_AXI, "ifr_nic_axi", "axi_sel", 28),
+ GATE_IFR5(CLK_IFR_NIC_SLV_AXI, "ifr_nic_slv_axi", "axi_sel", 29),
+ GATE_IFR5(CLK_IFR_APU_AXI, "ifr_apu_axi", "axi_sel", 30),
+ /* IFR6 */
+ GATE_IFR6(CLK_IFR_NFIECC, "ifr_nfiecc", "nfiecc_sel", 0),
+ GATE_IFR6(CLK_IFR_NFI1X_BK, "ifr_nfi1x_bk", "nfi2x_sel", 1),
+ GATE_IFR6(CLK_IFR_NFIECC_BK, "ifr_nfiecc_bk", "nfi2x_sel", 2),
+ GATE_IFR6(CLK_IFR_NFI_BK, "ifr_nfi_bk", "axi_sel", 3),
+ GATE_IFR6(CLK_IFR_MSDC2_AP_BK, "ifr_msdc2_ap_bk", "axi_sel", 4),
+ GATE_IFR6(CLK_IFR_MSDC2_MD_BK, "ifr_msdc2_md_bk", "axi_sel", 5),
+ GATE_IFR6(CLK_IFR_MSDC2_BK, "ifr_msdc2_bk", "axi_sel", 6),
+ GATE_IFR6(CLK_IFR_SUSB_133_BK, "ifr_susb_133_bk", "axi_sel", 7),
+ GATE_IFR6(CLK_IFR_SUSB_66_BK, "ifr_susb_66_bk", "axi_sel", 8),
+ GATE_IFR6(CLK_IFR_SSUSB_SYS, "ifr_ssusb_sys", "ssusb_sys_sel", 9),
+ GATE_IFR6(CLK_IFR_SSUSB_REF, "ifr_ssusb_ref", "ssusb_sys_sel", 10),
+ GATE_IFR6(CLK_IFR_SSUSB_XHCI, "ifr_ssusb_xhci", "ssusb_xhci_sel", 11),
+};
+
+static const struct mtk_gate_regs peri_cg_regs = {
+ .set_ofs = 0x20c,
+ .clr_ofs = 0x20c,
+ .sta_ofs = 0x20c,
+};
+
+static const struct mtk_gate peri_clks[] = {
+ GATE_MTK(CLK_PERIAXI, "periaxi", "axi_sel", &peri_cg_regs, 31,
+ &mtk_clk_gate_ops_no_setclr),
+};
+
+static const struct mtk_clk_desc topck_desc = {
+ .clks = top_clk_gates,
+ .num_clks = ARRAY_SIZE(top_clk_gates),
+ .fixed_clks = top_fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(top_fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .mux_clks = top_muxes,
+ .num_mux_clks = ARRAY_SIZE(top_muxes),
+ .composite_clks = top_misc_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_misc_muxes),
+ .divider_clks = top_adj_divs,
+ .num_divider_clks = ARRAY_SIZE(top_adj_divs),
+ .clk_lock = &mt8365_clk_lock,
+};
+
+static const struct mtk_clk_desc infra_desc = {
+ .clks = ifr_clks,
+ .num_clks = ARRAY_SIZE(ifr_clks),
+};
+
+static const struct mtk_clk_desc peri_desc = {
+ .clks = peri_clks,
+ .num_clks = ARRAY_SIZE(peri_clks),
+};
+
+static const struct mtk_clk_desc mcu_desc = {
+ .composite_clks = mcu_muxes,
+ .num_composite_clks = ARRAY_SIZE(mcu_muxes),
+ .clk_lock = &mt8365_clk_lock,
+};
+
+static const struct of_device_id of_match_clk_mt8365[] = {
+ { .compatible = "mediatek,mt8365-topckgen", .data = &topck_desc },
+ { .compatible = "mediatek,mt8365-infracfg", .data = &infra_desc },
+ { .compatible = "mediatek,mt8365-pericfg", .data = &peri_desc },
+ { .compatible = "mediatek,mt8365-mcucfg", .data = &mcu_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8365);
+
+static struct platform_driver clk_mt8365_drv = {
+ .driver = {
+ .name = "clk-mt8365",
+ .of_match_table = of_match_clk_mt8365,
+ },
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+};
+module_platform_driver(clk_mt8365_drv);
+
+MODULE_DESCRIPTION("MediaTek MT8365 main clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8516-apmixedsys.c b/drivers/clk/mediatek/clk-mt8516-apmixedsys.c
new file mode 100644
index 000000000000..edd9174d2f2f
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8516-apmixedsys.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * James Liao <jamesjj.liao@mediatek.com>
+ * Fabien Parent <fparent@baylibre.com>
+ *
+ * Copyright (c) 2023 Collabora, Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <dt-bindings/clock/mt8516-clk.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-pll.h"
+
+#define MT8516_PLL_FMAX (1502UL * MHZ)
+
+#define CON0_MT8516_RST_BAR BIT(27)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift, _div_table) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT8516_RST_BAR, \
+ .fmax = MT8516_PLL_FMAX, \
+ .pcwbits = _pcwbits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
+ _pcw_shift) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
+ NULL)
+
+static const struct mtk_pll_div_table mmpll_div_table[] = {
+ { .div = 0, .freq = MT8516_PLL_FMAX },
+ { .div = 1, .freq = 1000000000 },
+ { .div = 2, .freq = 604500000 },
+ { .div = 3, .freq = 253500000 },
+ { .div = 4, .freq = 126750000 },
+ { } /* sentinel */
+};
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0100, 0x0110, 0, 0,
+ 21, 0x0104, 24, 0, 0x0104, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0120, 0x0130, 0,
+ HAVE_RST_BAR, 21, 0x0124, 24, 0, 0x0124, 0),
+ PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0140, 0x0150, 0x30000000,
+ HAVE_RST_BAR, 7, 0x0144, 24, 0, 0x0144, 0),
+ PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0160, 0x0170, 0, 0,
+ 21, 0x0164, 24, 0, 0x0164, 0, mmpll_div_table),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x0180, 0x0190, 0, 0,
+ 31, 0x0180, 1, 0x0194, 0x0184, 0),
+ PLL(CLK_APMIXED_APLL2, "apll2", 0x01A0, 0x01B0, 0, 0,
+ 31, 0x01A0, 1, 0x01B4, 0x01A4, 0),
+};
+
+static int clk_mt8516_apmixed_probe(struct platform_device *pdev)
+{
+ void __iomem *base;
+ struct clk_hw_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_data = mtk_devm_alloc_clk_data(dev, CLK_APMIXED_NR_CLK);
+ if (!clk_data)
+ return -ENOMEM;
+
+ ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ goto unregister_plls;
+
+ return 0;
+
+unregister_plls:
+ mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data);
+
+ return ret;
+}
+
+static const struct of_device_id of_match_clk_mt8516_apmixed[] = {
+ { .compatible = "mediatek,mt8516-apmixedsys" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8516_apmixed);
+
+static struct platform_driver clk_mt8516_apmixed_drv = {
+ .probe = clk_mt8516_apmixed_probe,
+ .driver = {
+ .name = "clk-mt8516-apmixed",
+ .of_match_table = of_match_clk_mt8516_apmixed,
+ },
+};
+builtin_platform_driver(clk_mt8516_apmixed_drv)
+
+MODULE_DESCRIPTION("MediaTek MT8516 apmixedsys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8516-aud.c b/drivers/clk/mediatek/clk-mt8516-aud.c
index 90f48068a8de..6227635fd5a1 100644
--- a/drivers/clk/mediatek/clk-mt8516-aud.c
+++ b/drivers/clk/mediatek/clk-mt8516-aud.c
@@ -3,12 +3,11 @@
* Copyright (c) 2019 MediaTek Inc.
* Author: James Liao <jamesjj.liao@mediatek.com>
* Fabien Parent <fparent@baylibre.com>
+ * Copyright (c) 2023 Collabora Ltd.
*/
#include <linux/clk-provider.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
@@ -22,16 +21,10 @@ static const struct mtk_gate_regs aud_cg_regs = {
.sta_ofs = 0x0,
};
-#define GATE_AUD(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &aud_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_AUD(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &aud_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr)
-static const struct mtk_gate aud_clks[] __initconst = {
+static const struct mtk_gate aud_clks[] = {
GATE_AUD(CLK_AUD_AFE, "aud_afe", "clk26m_ck", 2),
GATE_AUD(CLK_AUD_I2S, "aud_i2s", "i2s_infra_bck", 6),
GATE_AUD(CLK_AUD_22M, "aud_22m", "rg_aud_engen1", 8),
@@ -47,19 +40,26 @@ static const struct mtk_gate aud_clks[] __initconst = {
GATE_AUD(CLK_AUD_TML, "aud_tml", "aud_afe", 27),
};
-static void __init mtk_audsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
-
- clk_data = mtk_alloc_clk_data(CLK_AUD_NR_CLK);
+static const struct mtk_clk_desc aud_desc = {
+ .clks = aud_clks,
+ .num_clks = ARRAY_SIZE(aud_clks),
+};
- mtk_clk_register_gates(node, aud_clks, ARRAY_SIZE(aud_clks), clk_data);
+static const struct of_device_id of_match_clk_mt8516_aud[] = {
+ { .compatible = "mediatek,mt8516-audsys", .data = &aud_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8516_aud);
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static struct platform_driver clk_mt8516_aud_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8516-aud",
+ .of_match_table = of_match_clk_mt8516_aud,
+ },
+};
+module_platform_driver(clk_mt8516_aud_drv);
-}
-CLK_OF_DECLARE(mtk_audsys, "mediatek,mt8516-audsys", mtk_audsys_init);
+MODULE_DESCRIPTION("MediaTek MT8516 audiosys clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mt8516.c b/drivers/clk/mediatek/clk-mt8516.c
index b96db88893e2..21eb052b0a53 100644
--- a/drivers/clk/mediatek/clk-mt8516.c
+++ b/drivers/clk/mediatek/clk-mt8516.c
@@ -3,6 +3,7 @@
* Copyright (c) 2019 MediaTek Inc.
* Author: James Liao <jamesjj.liao@mediatek.com>
* Fabien Parent <fparent@baylibre.com>
+ * Copyright (c) 2023 Collabora Ltd.
*/
#include <linux/delay.h>
@@ -10,10 +11,10 @@
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
#include "clk-gate.h"
#include "clk-mtk.h"
-#include "clk-pll.h"
#include <dt-bindings/clock/mt8516-clk.h>
@@ -525,59 +526,23 @@ static const struct mtk_gate_regs top5_cg_regs = {
.sta_ofs = 0x44,
};
-#define GATE_TOP1(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top1_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP2(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP2_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top2_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-#define GATE_TOP3(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top3_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr, \
- }
-
-#define GATE_TOP4_I(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top4_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_setclr_inv, \
- }
-
-#define GATE_TOP5(_id, _name, _parent, _shift) { \
- .id = _id, \
- .name = _name, \
- .parent_name = _parent, \
- .regs = &top5_cg_regs, \
- .shift = _shift, \
- .ops = &mtk_clk_gate_ops_no_setclr, \
- }
+#define GATE_TOP1(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top1_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP2(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top2_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP2_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top2_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_TOP3(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top3_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
+
+#define GATE_TOP4_I(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top4_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv)
+
+#define GATE_TOP5(_id, _name, _parent, _shift) \
+ GATE_MTK(_id, _name, _parent, &top5_cg_regs, _shift, &mtk_clk_gate_ops_setclr)
static const struct mtk_gate top_clks[] __initconst = {
/* TOP1 */
@@ -675,137 +640,42 @@ static const struct mtk_gate top_clks[] __initconst = {
GATE_TOP5(CLK_TOP_APLL12_DIV6, "apll12_div6", "apll12_ck_div6", 8),
};
-static void __init mtk_topckgen_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- void __iomem *base;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
-
- mtk_clk_register_fixed_clks(fixed_clks, ARRAY_SIZE(fixed_clks),
- clk_data);
- mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), clk_data);
-
- mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
- mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
- &mt8516_clk_lock, clk_data);
- mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs),
- base, &mt8516_clk_lock, clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8516-topckgen", mtk_topckgen_init);
-
-static void __init mtk_infracfg_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- int r;
- void __iomem *base;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
+static const struct mtk_clk_desc topck_desc = {
+ .clks = top_clks,
+ .num_clks = ARRAY_SIZE(top_clks),
+ .fixed_clks = fixed_clks,
+ .num_fixed_clks = ARRAY_SIZE(fixed_clks),
+ .factor_clks = top_divs,
+ .num_factor_clks = ARRAY_SIZE(top_divs),
+ .composite_clks = top_muxes,
+ .num_composite_clks = ARRAY_SIZE(top_muxes),
+ .divider_clks = top_adj_divs,
+ .num_divider_clks = ARRAY_SIZE(top_adj_divs),
+ .clk_lock = &mt8516_clk_lock,
+};
- clk_data = mtk_alloc_clk_data(CLK_IFR_NR_CLK);
+static const struct mtk_clk_desc infra_desc = {
+ .composite_clks = ifr_muxes,
+ .num_composite_clks = ARRAY_SIZE(ifr_muxes),
+ .clk_lock = &mt8516_clk_lock,
+};
- mtk_clk_register_composites(ifr_muxes, ARRAY_SIZE(ifr_muxes), base,
- &mt8516_clk_lock, clk_data);
+static const struct of_device_id of_match_clk_mt8516[] = {
+ { .compatible = "mediatek,mt8516-topckgen", .data = &topck_desc },
+ { .compatible = "mediatek,mt8516-infracfg", .data = &infra_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8516);
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
-}
-CLK_OF_DECLARE(mtk_infracfg, "mediatek,mt8516-infracfg", mtk_infracfg_init);
-
-#define MT8516_PLL_FMAX (1502UL * MHZ)
-
-#define CON0_MT8516_RST_BAR BIT(27)
-
-#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift, _div_table) { \
- .id = _id, \
- .name = _name, \
- .reg = _reg, \
- .pwr_reg = _pwr_reg, \
- .en_mask = _en_mask, \
- .flags = _flags, \
- .rst_bar_mask = CON0_MT8516_RST_BAR, \
- .fmax = MT8516_PLL_FMAX, \
- .pcwbits = _pcwbits, \
- .pd_reg = _pd_reg, \
- .pd_shift = _pd_shift, \
- .tuner_reg = _tuner_reg, \
- .pcw_reg = _pcw_reg, \
- .pcw_shift = _pcw_shift, \
- .div_table = _div_table, \
- }
-
-#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \
- _pcw_shift) \
- PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
- _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
- NULL)
-
-static const struct mtk_pll_div_table mmpll_div_table[] = {
- { .div = 0, .freq = MT8516_PLL_FMAX },
- { .div = 1, .freq = 1000000000 },
- { .div = 2, .freq = 604500000 },
- { .div = 3, .freq = 253500000 },
- { .div = 4, .freq = 126750000 },
- { } /* sentinel */
-};
-
-static const struct mtk_pll_data plls[] = {
- PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0100, 0x0110, 0, 0,
- 21, 0x0104, 24, 0, 0x0104, 0),
- PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0120, 0x0130, 0,
- HAVE_RST_BAR, 21, 0x0124, 24, 0, 0x0124, 0),
- PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0140, 0x0150, 0x30000000,
- HAVE_RST_BAR, 7, 0x0144, 24, 0, 0x0144, 0),
- PLL_B(CLK_APMIXED_MMPLL, "mmpll", 0x0160, 0x0170, 0, 0,
- 21, 0x0164, 24, 0, 0x0164, 0, mmpll_div_table),
- PLL(CLK_APMIXED_APLL1, "apll1", 0x0180, 0x0190, 0, 0,
- 31, 0x0180, 1, 0x0194, 0x0184, 0),
- PLL(CLK_APMIXED_APLL2, "apll2", 0x01A0, 0x01B0, 0, 0,
- 31, 0x01A0, 1, 0x01B4, 0x01A4, 0),
-};
-
-static void __init mtk_apmixedsys_init(struct device_node *node)
-{
- struct clk_hw_onecell_data *clk_data;
- void __iomem *base;
- int r;
-
- base = of_iomap(node, 0);
- if (!base) {
- pr_err("%s(): ioremap failed\n", __func__);
- return;
- }
-
- clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
-
- mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
-
- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
- if (r)
- pr_err("%s(): could not register clock provider: %d\n",
- __func__, r);
+static struct platform_driver clk_mt8516_drv = {
+ .probe = mtk_clk_simple_probe,
+ .remove = mtk_clk_simple_remove,
+ .driver = {
+ .name = "clk-mt8516",
+ .of_match_table = of_match_clk_mt8516,
+ },
+};
+module_platform_driver(clk_mt8516_drv);
-}
-CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8516-apmixedsys",
- mtk_apmixedsys_init);
+MODULE_DESCRIPTION("MediaTek MT8516 clocks driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 05a188c62119..19cd27941747 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -11,26 +11,67 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "clk-mtk.h"
#include "clk-gate.h"
+#include "clk-mux.h"
-struct clk_hw_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
+const struct mtk_gate_regs cg_regs_dummy = { 0, 0, 0 };
+EXPORT_SYMBOL_GPL(cg_regs_dummy);
+
+static int mtk_clk_dummy_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+
+static void mtk_clk_dummy_disable(struct clk_hw *hw) { }
+
+const struct clk_ops mtk_clk_dummy_ops = {
+ .enable = mtk_clk_dummy_enable,
+ .disable = mtk_clk_dummy_disable,
+};
+EXPORT_SYMBOL_GPL(mtk_clk_dummy_ops);
+
+static void mtk_init_clk_data(struct clk_hw_onecell_data *clk_data,
+ unsigned int clk_num)
{
int i;
+
+ clk_data->num = clk_num;
+
+ for (i = 0; i < clk_num; i++)
+ clk_data->hws[i] = ERR_PTR(-ENOENT);
+}
+
+struct clk_hw_onecell_data *mtk_devm_alloc_clk_data(struct device *dev,
+ unsigned int clk_num)
+{
struct clk_hw_onecell_data *clk_data;
- clk_data = kzalloc(struct_size(clk_data, hws, clk_num), GFP_KERNEL);
+ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, clk_num),
+ GFP_KERNEL);
if (!clk_data)
return NULL;
- clk_data->num = clk_num;
+ mtk_init_clk_data(clk_data, clk_num);
- for (i = 0; i < clk_num; i++)
- clk_data->hws[i] = ERR_PTR(-ENOENT);
+ return clk_data;
+}
+EXPORT_SYMBOL_GPL(mtk_devm_alloc_clk_data);
+
+struct clk_hw_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
+{
+ struct clk_hw_onecell_data *clk_data;
+
+ clk_data = kzalloc(struct_size(clk_data, hws, clk_num), GFP_KERNEL);
+ if (!clk_data)
+ return NULL;
+
+ mtk_init_clk_data(clk_data, clk_num);
return clk_data;
}
@@ -80,7 +121,7 @@ err:
if (IS_ERR_OR_NULL(clk_data->hws[rc->id]))
continue;
- clk_unregister_fixed_rate(clk_data->hws[rc->id]->clk);
+ clk_hw_unregister_fixed_rate(clk_data->hws[rc->id]);
clk_data->hws[rc->id] = ERR_PTR(-ENOENT);
}
@@ -102,7 +143,7 @@ void mtk_clk_unregister_fixed_clks(const struct mtk_fixed_clk *clks, int num,
if (IS_ERR_OR_NULL(clk_data->hws[rc->id]))
continue;
- clk_unregister_fixed_rate(clk_data->hws[rc->id]->clk);
+ clk_hw_unregister_fixed_rate(clk_data->hws[rc->id]);
clk_data->hws[rc->id] = ERR_PTR(-ENOENT);
}
}
@@ -126,7 +167,7 @@ int mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num,
}
hw = clk_hw_register_fixed_factor(NULL, ff->name, ff->parent_name,
- CLK_SET_RATE_PARENT, ff->mult, ff->div);
+ ff->flags, ff->mult, ff->div);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", ff->name,
@@ -146,7 +187,7 @@ err:
if (IS_ERR_OR_NULL(clk_data->hws[ff->id]))
continue;
- clk_unregister_fixed_factor(clk_data->hws[ff->id]->clk);
+ clk_hw_unregister_fixed_factor(clk_data->hws[ff->id]);
clk_data->hws[ff->id] = ERR_PTR(-ENOENT);
}
@@ -168,14 +209,14 @@ void mtk_clk_unregister_factors(const struct mtk_fixed_factor *clks, int num,
if (IS_ERR_OR_NULL(clk_data->hws[ff->id]))
continue;
- clk_unregister_fixed_factor(clk_data->hws[ff->id]->clk);
+ clk_hw_unregister_fixed_factor(clk_data->hws[ff->id]);
clk_data->hws[ff->id] = ERR_PTR(-ENOENT);
}
}
EXPORT_SYMBOL_GPL(mtk_clk_unregister_factors);
-static struct clk_hw *mtk_clk_register_composite(const struct mtk_composite *mc,
- void __iomem *base, spinlock_t *lock)
+static struct clk_hw *mtk_clk_register_composite(struct device *dev,
+ const struct mtk_composite *mc, void __iomem *base, spinlock_t *lock)
{
struct clk_hw *hw;
struct clk_mux *mux = NULL;
@@ -241,7 +282,7 @@ static struct clk_hw *mtk_clk_register_composite(const struct mtk_composite *mc,
div_ops = &clk_divider_ops;
}
- hw = clk_hw_register_composite(NULL, mc->name, parent_names, num_parents,
+ hw = clk_hw_register_composite(dev, mc->name, parent_names, num_parents,
mux_hw, mux_ops,
div_hw, div_ops,
gate_hw, gate_ops,
@@ -285,7 +326,8 @@ static void mtk_clk_unregister_composite(struct clk_hw *hw)
kfree(mux);
}
-int mtk_clk_register_composites(const struct mtk_composite *mcs, int num,
+int mtk_clk_register_composites(struct device *dev,
+ const struct mtk_composite *mcs, int num,
void __iomem *base, spinlock_t *lock,
struct clk_hw_onecell_data *clk_data)
{
@@ -304,7 +346,7 @@ int mtk_clk_register_composites(const struct mtk_composite *mcs, int num,
continue;
}
- hw = mtk_clk_register_composite(mc, base, lock);
+ hw = mtk_clk_register_composite(dev, mc, base, lock);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", mc->name,
@@ -352,7 +394,8 @@ void mtk_clk_unregister_composites(const struct mtk_composite *mcs, int num,
}
EXPORT_SYMBOL_GPL(mtk_clk_unregister_composites);
-int mtk_clk_register_dividers(const struct mtk_clk_divider *mcds, int num,
+int mtk_clk_register_dividers(struct device *dev,
+ const struct mtk_clk_divider *mcds, int num,
void __iomem *base, spinlock_t *lock,
struct clk_hw_onecell_data *clk_data)
{
@@ -371,7 +414,7 @@ int mtk_clk_register_dividers(const struct mtk_clk_divider *mcds, int num,
continue;
}
- hw = clk_hw_register_divider(NULL, mcd->name, mcd->parent_name,
+ hw = clk_hw_register_divider(dev, mcd->name, mcd->parent_name,
mcd->flags, base + mcd->div_reg, mcd->div_shift,
mcd->div_width, mcd->clk_divider_flags, lock);
@@ -393,12 +436,13 @@ err:
if (IS_ERR_OR_NULL(clk_data->hws[mcd->id]))
continue;
- mtk_clk_unregister_composite(clk_data->hws[mcd->id]);
+ clk_hw_unregister_divider(clk_data->hws[mcd->id]);
clk_data->hws[mcd->id] = ERR_PTR(-ENOENT);
}
return PTR_ERR(hw);
}
+EXPORT_SYMBOL_GPL(mtk_clk_register_dividers);
void mtk_clk_unregister_dividers(const struct mtk_clk_divider *mcds, int num,
struct clk_hw_onecell_data *clk_data)
@@ -414,29 +458,121 @@ void mtk_clk_unregister_dividers(const struct mtk_clk_divider *mcds, int num,
if (IS_ERR_OR_NULL(clk_data->hws[mcd->id]))
continue;
- clk_unregister_divider(clk_data->hws[mcd->id]->clk);
+ clk_hw_unregister_divider(clk_data->hws[mcd->id]);
clk_data->hws[mcd->id] = ERR_PTR(-ENOENT);
}
}
+EXPORT_SYMBOL_GPL(mtk_clk_unregister_dividers);
-int mtk_clk_simple_probe(struct platform_device *pdev)
+static int __mtk_clk_simple_probe(struct platform_device *pdev,
+ struct device_node *node)
{
+ const struct platform_device_id *id;
const struct mtk_clk_desc *mcd;
struct clk_hw_onecell_data *clk_data;
- struct device_node *node = pdev->dev.of_node;
- int r;
+ void __iomem *base = NULL;
+ int num_clks, r;
+
+ mcd = device_get_match_data(&pdev->dev);
+ if (!mcd) {
+ /* Clock driver wasn't registered from devicetree */
+ id = platform_get_device_id(pdev);
+ if (id)
+ mcd = (const struct mtk_clk_desc *)id->driver_data;
+
+ if (!mcd)
+ return -EINVAL;
+ }
- mcd = of_device_get_match_data(&pdev->dev);
- if (!mcd)
- return -EINVAL;
+ /* Composite and divider clocks needs us to pass iomem pointer */
+ if (mcd->composite_clks || mcd->divider_clks) {
+ if (!mcd->shared_io)
+ base = devm_platform_ioremap_resource(pdev, 0);
+ else
+ base = of_iomap(node, 0);
- clk_data = mtk_alloc_clk_data(mcd->num_clks);
- if (!clk_data)
- return -ENOMEM;
+ if (IS_ERR_OR_NULL(base))
+ return IS_ERR(base) ? PTR_ERR(base) : -ENOMEM;
+ }
- r = mtk_clk_register_gates(node, mcd->clks, mcd->num_clks, clk_data);
- if (r)
- goto free_data;
+
+ if (mcd->need_runtime_pm) {
+ devm_pm_runtime_enable(&pdev->dev);
+ /*
+ * Do a pm_runtime_resume_and_get() to workaround a possible
+ * deadlock between clk_register() and the genpd framework.
+ */
+ r = pm_runtime_resume_and_get(&pdev->dev);
+ if (r)
+ return r;
+ }
+
+ /* Calculate how many clk_hw_onecell_data entries to allocate */
+ num_clks = mcd->num_clks + mcd->num_composite_clks;
+ num_clks += mcd->num_fixed_clks + mcd->num_factor_clks;
+ num_clks += mcd->num_mux_clks + mcd->num_divider_clks;
+
+ clk_data = mtk_alloc_clk_data(num_clks);
+ if (!clk_data) {
+ r = -ENOMEM;
+ goto free_base;
+ }
+
+ if (mcd->fixed_clks) {
+ r = mtk_clk_register_fixed_clks(mcd->fixed_clks,
+ mcd->num_fixed_clks, clk_data);
+ if (r)
+ goto free_data;
+ }
+
+ if (mcd->factor_clks) {
+ r = mtk_clk_register_factors(mcd->factor_clks,
+ mcd->num_factor_clks, clk_data);
+ if (r)
+ goto unregister_fixed_clks;
+ }
+
+ if (mcd->mux_clks) {
+ r = mtk_clk_register_muxes(&pdev->dev, mcd->mux_clks,
+ mcd->num_mux_clks, node,
+ mcd->clk_lock, clk_data);
+ if (r)
+ goto unregister_factors;
+ }
+
+ if (mcd->composite_clks) {
+ /* We don't check composite_lock because it's optional */
+ r = mtk_clk_register_composites(&pdev->dev,
+ mcd->composite_clks,
+ mcd->num_composite_clks,
+ base, mcd->clk_lock, clk_data);
+ if (r)
+ goto unregister_muxes;
+ }
+
+ if (mcd->divider_clks) {
+ r = mtk_clk_register_dividers(&pdev->dev,
+ mcd->divider_clks,
+ mcd->num_divider_clks,
+ base, mcd->clk_lock, clk_data);
+ if (r)
+ goto unregister_composites;
+ }
+
+ if (mcd->clks) {
+ r = mtk_clk_register_gates(&pdev->dev, node, mcd->clks,
+ mcd->num_clks, clk_data);
+ if (r)
+ goto unregister_dividers;
+ }
+
+ if (mcd->clk_notifier_func) {
+ struct clk *mfg_mux = clk_data->hws[mcd->mfg_clk_idx]->clk;
+
+ r = mcd->clk_notifier_func(&pdev->dev, mfg_mux);
+ if (r)
+ goto unregister_clks;
+ }
r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
if (r)
@@ -451,26 +587,118 @@ int mtk_clk_simple_probe(struct platform_device *pdev)
goto unregister_clks;
}
+ if (mcd->need_runtime_pm)
+ pm_runtime_put(&pdev->dev);
+
return r;
unregister_clks:
- mtk_clk_unregister_gates(mcd->clks, mcd->num_clks, clk_data);
+ if (mcd->clks)
+ mtk_clk_unregister_gates(mcd->clks, mcd->num_clks, clk_data);
+unregister_dividers:
+ if (mcd->divider_clks)
+ mtk_clk_unregister_dividers(mcd->divider_clks,
+ mcd->num_divider_clks, clk_data);
+unregister_composites:
+ if (mcd->composite_clks)
+ mtk_clk_unregister_composites(mcd->composite_clks,
+ mcd->num_composite_clks, clk_data);
+unregister_muxes:
+ if (mcd->mux_clks)
+ mtk_clk_unregister_muxes(mcd->mux_clks,
+ mcd->num_mux_clks, clk_data);
+unregister_factors:
+ if (mcd->factor_clks)
+ mtk_clk_unregister_factors(mcd->factor_clks,
+ mcd->num_factor_clks, clk_data);
+unregister_fixed_clks:
+ if (mcd->fixed_clks)
+ mtk_clk_unregister_fixed_clks(mcd->fixed_clks,
+ mcd->num_fixed_clks, clk_data);
free_data:
mtk_free_clk_data(clk_data);
+free_base:
+ if (mcd->shared_io && base)
+ iounmap(base);
+
+ if (mcd->need_runtime_pm)
+ pm_runtime_put(&pdev->dev);
return r;
}
-int mtk_clk_simple_remove(struct platform_device *pdev)
+static void __mtk_clk_simple_remove(struct platform_device *pdev,
+ struct device_node *node)
{
- const struct mtk_clk_desc *mcd = of_device_get_match_data(&pdev->dev);
struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
- struct device_node *node = pdev->dev.of_node;
+ const struct mtk_clk_desc *mcd = device_get_match_data(&pdev->dev);
of_clk_del_provider(node);
- mtk_clk_unregister_gates(mcd->clks, mcd->num_clks, clk_data);
+ if (mcd->clks)
+ mtk_clk_unregister_gates(mcd->clks, mcd->num_clks, clk_data);
+ if (mcd->divider_clks)
+ mtk_clk_unregister_dividers(mcd->divider_clks,
+ mcd->num_divider_clks, clk_data);
+ if (mcd->composite_clks)
+ mtk_clk_unregister_composites(mcd->composite_clks,
+ mcd->num_composite_clks, clk_data);
+ if (mcd->mux_clks)
+ mtk_clk_unregister_muxes(mcd->mux_clks,
+ mcd->num_mux_clks, clk_data);
+ if (mcd->factor_clks)
+ mtk_clk_unregister_factors(mcd->factor_clks,
+ mcd->num_factor_clks, clk_data);
+ if (mcd->fixed_clks)
+ mtk_clk_unregister_fixed_clks(mcd->fixed_clks,
+ mcd->num_fixed_clks, clk_data);
mtk_free_clk_data(clk_data);
+}
- return 0;
+int mtk_clk_pdev_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->parent->of_node;
+
+ return __mtk_clk_simple_probe(pdev, node);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_pdev_probe);
+
+int mtk_clk_simple_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+
+ return __mtk_clk_simple_probe(pdev, node);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_simple_probe);
+
+void mtk_clk_pdev_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->parent->of_node;
+
+ __mtk_clk_simple_remove(pdev, node);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_pdev_remove);
+
+void mtk_clk_simple_remove(struct platform_device *pdev)
+{
+ __mtk_clk_simple_remove(pdev, pdev->dev.of_node);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_simple_remove);
+
+struct regmap *mtk_clk_get_hwv_regmap(struct device_node *node)
+{
+ struct device_node *hwv_node;
+ struct regmap *regmap_hwv;
+
+ hwv_node = of_parse_phandle(node, "mediatek,hardware-voter", 0);
+ if (!hwv_node)
+ return NULL;
+
+ regmap_hwv = device_node_to_regmap(hwv_node);
+ of_node_put(hwv_node);
+
+ return regmap_hwv;
}
+EXPORT_SYMBOL_GPL(mtk_clk_get_hwv_regmap);
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
index 1b95c484d5aa..5417b9264e6d 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -20,8 +20,29 @@
#define MHZ (1000 * 1000)
+#define MTK_WAIT_HWV_DONE_US 30
+
struct platform_device;
+/*
+ * We need the clock IDs to start from zero but to maintain devicetree
+ * backwards compatibility we can't change bindings to start from zero.
+ * Only a few platforms are affected, so we solve issues given by the
+ * commonized MTK clocks probe function(s) by adding a dummy clock at
+ * the beginning where needed.
+ */
+#define CLK_DUMMY 0
+
+extern const struct clk_ops mtk_clk_dummy_ops;
+extern const struct mtk_gate_regs cg_regs_dummy;
+
+#define GATE_DUMMY(_id, _name) { \
+ .id = _id, \
+ .name = _name, \
+ .regs = &cg_regs_dummy, \
+ .ops = &mtk_clk_dummy_ops, \
+ }
+
struct mtk_fixed_clk {
int id;
const char *name;
@@ -47,16 +68,21 @@ struct mtk_fixed_factor {
const char *parent_name;
int mult;
int div;
+ unsigned long flags;
};
-#define FACTOR(_id, _name, _parent, _mult, _div) { \
+#define FACTOR_FLAGS(_id, _name, _parent, _mult, _div, _fl) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.mult = _mult, \
.div = _div, \
+ .flags = _fl, \
}
+#define FACTOR(_id, _name, _parent, _mult, _div) \
+ FACTOR_FLAGS(_id, _name, _parent, _mult, _div, CLK_SET_RATE_PARENT)
+
int mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num,
struct clk_hw_onecell_data *clk_data);
void mtk_clk_unregister_factors(const struct mtk_fixed_factor *clks, int num,
@@ -149,7 +175,27 @@ struct mtk_composite {
.flags = 0, \
}
-int mtk_clk_register_composites(const struct mtk_composite *mcs, int num,
+#define MUX_DIV_GATE(_id, _name, _parents, \
+ _mux_reg, _mux_shift, _mux_width, \
+ _div_reg, _div_shift, _div_width, \
+ _gate_reg, _gate_shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_names = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .mux_reg = _mux_reg, \
+ .mux_shift = _mux_shift, \
+ .mux_width = _mux_width, \
+ .divider_reg = _div_reg, \
+ .divider_shift = _div_shift, \
+ .divider_width = _div_width, \
+ .gate_reg = _gate_reg, \
+ .gate_shift = _gate_shift, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }
+
+int mtk_clk_register_composites(struct device *dev,
+ const struct mtk_composite *mcs, int num,
void __iomem *base, spinlock_t *lock,
struct clk_hw_onecell_data *clk_data);
void mtk_clk_unregister_composites(const struct mtk_composite *mcs, int num,
@@ -177,25 +223,49 @@ struct mtk_clk_divider {
.div_width = _width, \
}
-int mtk_clk_register_dividers(const struct mtk_clk_divider *mcds, int num,
+int mtk_clk_register_dividers(struct device *dev,
+ const struct mtk_clk_divider *mcds, int num,
void __iomem *base, spinlock_t *lock,
struct clk_hw_onecell_data *clk_data);
void mtk_clk_unregister_dividers(const struct mtk_clk_divider *mcds, int num,
struct clk_hw_onecell_data *clk_data);
struct clk_hw_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
+struct clk_hw_onecell_data *mtk_devm_alloc_clk_data(struct device *dev,
+ unsigned int clk_num);
void mtk_free_clk_data(struct clk_hw_onecell_data *clk_data);
struct clk_hw *mtk_clk_register_ref2usb_tx(const char *name,
const char *parent_name, void __iomem *reg);
+void mtk_clk_unregister_ref2usb_tx(struct clk_hw *hw);
struct mtk_clk_desc {
const struct mtk_gate *clks;
size_t num_clks;
+ const struct mtk_composite *composite_clks;
+ size_t num_composite_clks;
+ const struct mtk_clk_divider *divider_clks;
+ size_t num_divider_clks;
+ const struct mtk_fixed_clk *fixed_clks;
+ size_t num_fixed_clks;
+ const struct mtk_fixed_factor *factor_clks;
+ size_t num_factor_clks;
+ const struct mtk_mux *mux_clks;
+ size_t num_mux_clks;
const struct mtk_clk_rst_desc *rst_desc;
+ spinlock_t *clk_lock;
+ bool shared_io;
+
+ int (*clk_notifier_func)(struct device *dev, struct clk *clk);
+ unsigned int mfg_clk_idx;
+
+ bool need_runtime_pm;
};
+int mtk_clk_pdev_probe(struct platform_device *pdev);
+void mtk_clk_pdev_remove(struct platform_device *pdev);
int mtk_clk_simple_probe(struct platform_device *pdev);
-int mtk_clk_simple_remove(struct platform_device *pdev);
+void mtk_clk_simple_remove(struct platform_device *pdev);
+struct regmap *mtk_clk_get_hwv_regmap(struct device_node *node);
#endif /* __DRV_CLK_MTK_H */
diff --git a/drivers/clk/mediatek/clk-mux.c b/drivers/clk/mediatek/clk-mux.c
index cd5f9fd8cb98..c5af6dc078a3 100644
--- a/drivers/clk/mediatek/clk-mux.c
+++ b/drivers/clk/mediatek/clk-mux.c
@@ -4,9 +4,11 @@
* Author: Owen Chen <owen.chen@mediatek.com>
*/
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/compiler_types.h>
#include <linux/container_of.h>
+#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -14,11 +16,15 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include "clk-mtk.h"
#include "clk-mux.h"
+#define MTK_WAIT_FENC_DONE_US 30
+
struct mtk_clk_mux {
struct clk_hw hw;
struct regmap *regmap;
+ struct regmap *regmap_hwv;
const struct mtk_mux *data;
spinlock_t *lock;
bool reparent;
@@ -29,6 +35,33 @@ static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
return container_of(hw, struct mtk_clk_mux, hw);
}
+static int mtk_clk_mux_fenc_enable_setclr(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ unsigned long flags;
+ u32 val;
+ int ret;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+ else
+ __acquire(mux->lock);
+
+ regmap_write(mux->regmap, mux->data->clr_ofs,
+ BIT(mux->data->gate_shift));
+
+ ret = regmap_read_poll_timeout_atomic(mux->regmap, mux->data->fenc_sta_mon_ofs,
+ val, val & BIT(mux->data->fenc_shift), 1,
+ MTK_WAIT_FENC_DONE_US);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+ else
+ __release(mux->lock);
+
+ return ret;
+}
+
static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -69,6 +102,16 @@ static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
BIT(mux->data->gate_shift));
}
+static int mtk_clk_mux_fenc_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val;
+
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
+
+ return !!(val & BIT(mux->data->fenc_shift));
+}
+
static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -79,6 +122,41 @@ static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
return (val & BIT(mux->data->gate_shift)) == 0;
}
+static int mtk_clk_mux_hwv_fenc_enable(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val;
+ int ret;
+
+ regmap_write(mux->regmap_hwv, mux->data->hwv_set_ofs,
+ BIT(mux->data->gate_shift));
+
+ ret = regmap_read_poll_timeout_atomic(mux->regmap_hwv, mux->data->hwv_sta_ofs,
+ val, val & BIT(mux->data->gate_shift), 0,
+ MTK_WAIT_HWV_DONE_US);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout_atomic(mux->regmap, mux->data->fenc_sta_mon_ofs,
+ val, val & BIT(mux->data->fenc_shift), 1,
+ MTK_WAIT_FENC_DONE_US);
+
+ return ret;
+}
+
+static void mtk_clk_mux_hwv_disable(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val;
+
+ regmap_write(mux->regmap_hwv, mux->data->hwv_clr_ofs,
+ BIT(mux->data->gate_shift));
+
+ regmap_read_poll_timeout_atomic(mux->regmap_hwv, mux->data->hwv_sta_ofs,
+ val, (val & BIT(mux->data->gate_shift)),
+ 0, MTK_WAIT_HWV_DONE_US);
+}
+
static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -88,6 +166,17 @@ static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
regmap_read(mux->regmap, mux->data->mux_ofs, &val);
val = (val >> mux->data->mux_shift) & mask;
+ if (mux->data->parent_index) {
+ int i;
+
+ for (i = 0; i < mux->data->num_parents; i++)
+ if (mux->data->parent_index[i] == val)
+ return i;
+
+ /* Not found: return an impossible index to generate error */
+ return mux->data->num_parents + 1;
+ }
+
return val;
}
@@ -103,6 +192,9 @@ static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
else
__acquire(mux->lock);
+ if (mux->data->parent_index)
+ index = mux->data->parent_index[index];
+
regmap_read(mux->regmap, mux->data->mux_ofs, &orig);
val = (orig & ~(mask << mux->data->mux_shift))
| (index << mux->data->mux_shift);
@@ -128,9 +220,24 @@ static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
return 0;
}
+static int mtk_clk_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ return clk_mux_determine_rate_flags(hw, req, 0);
+}
+
+static bool mtk_clk_mux_uses_hwv(const struct clk_ops *ops)
+{
+ if (ops == &mtk_mux_gate_hwv_fenc_clr_set_upd_ops)
+ return true;
+
+ return false;
+}
+
const struct clk_ops mtk_mux_clr_set_upd_ops = {
.get_parent = mtk_clk_mux_get_parent,
.set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
};
EXPORT_SYMBOL_GPL(mtk_mux_clr_set_upd_ops);
@@ -140,12 +247,35 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops = {
.is_enabled = mtk_clk_mux_is_enabled,
.get_parent = mtk_clk_mux_get_parent,
.set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
};
EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);
-static struct clk_hw *mtk_clk_register_mux(const struct mtk_mux *mux,
- struct regmap *regmap,
- spinlock_t *lock)
+const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops = {
+ .enable = mtk_clk_mux_fenc_enable_setclr,
+ .disable = mtk_clk_mux_disable_setclr,
+ .is_enabled = mtk_clk_mux_fenc_is_enabled,
+ .get_parent = mtk_clk_mux_get_parent,
+ .set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_gate_fenc_clr_set_upd_ops);
+
+const struct clk_ops mtk_mux_gate_hwv_fenc_clr_set_upd_ops = {
+ .enable = mtk_clk_mux_hwv_fenc_enable,
+ .disable = mtk_clk_mux_hwv_disable,
+ .is_enabled = mtk_clk_mux_fenc_is_enabled,
+ .get_parent = mtk_clk_mux_get_parent,
+ .set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_gate_hwv_fenc_clr_set_upd_ops);
+
+static struct clk_hw *mtk_clk_register_mux(struct device *dev,
+ const struct mtk_mux *mux,
+ struct regmap *regmap,
+ struct regmap *regmap_hwv,
+ spinlock_t *lock)
{
struct mtk_clk_mux *clk_mux;
struct clk_init_data init = {};
@@ -156,17 +286,22 @@ static struct clk_hw *mtk_clk_register_mux(const struct mtk_mux *mux,
return ERR_PTR(-ENOMEM);
init.name = mux->name;
- init.flags = mux->flags | CLK_SET_RATE_PARENT;
+ init.flags = mux->flags;
init.parent_names = mux->parent_names;
init.num_parents = mux->num_parents;
init.ops = mux->ops;
+ if (mtk_clk_mux_uses_hwv(init.ops) && !regmap_hwv)
+ return dev_err_ptr_probe(
+ dev, -ENXIO,
+ "regmap not found for hardware voter clocks\n");
clk_mux->regmap = regmap;
+ clk_mux->regmap_hwv = regmap_hwv;
clk_mux->data = mux;
clk_mux->lock = lock;
clk_mux->hw.init = &init;
- ret = clk_hw_register(NULL, &clk_mux->hw);
+ ret = clk_hw_register(dev, &clk_mux->hw);
if (ret) {
kfree(clk_mux);
return ERR_PTR(ret);
@@ -187,12 +322,14 @@ static void mtk_clk_unregister_mux(struct clk_hw *hw)
kfree(mux);
}
-int mtk_clk_register_muxes(const struct mtk_mux *muxes,
+int mtk_clk_register_muxes(struct device *dev,
+ const struct mtk_mux *muxes,
int num, struct device_node *node,
spinlock_t *lock,
struct clk_hw_onecell_data *clk_data)
{
struct regmap *regmap;
+ struct regmap *regmap_hwv;
struct clk_hw *hw;
int i;
@@ -202,6 +339,12 @@ int mtk_clk_register_muxes(const struct mtk_mux *muxes,
return PTR_ERR(regmap);
}
+ regmap_hwv = mtk_clk_get_hwv_regmap(node);
+ if (IS_ERR(regmap_hwv))
+ return dev_err_probe(
+ dev, PTR_ERR(regmap_hwv),
+ "Cannot find hardware voter regmap for %pOF\n", node);
+
for (i = 0; i < num; i++) {
const struct mtk_mux *mux = &muxes[i];
@@ -211,7 +354,7 @@ int mtk_clk_register_muxes(const struct mtk_mux *muxes,
continue;
}
- hw = mtk_clk_register_mux(mux, regmap, lock);
+ hw = mtk_clk_register_mux(dev, mux, regmap, regmap_hwv, lock);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", mux->name,
@@ -259,4 +402,41 @@ void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num,
}
EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes);
+/*
+ * This clock notifier is called when the frequency of the parent
+ * PLL clock is to be changed. The idea is to switch the parent to a
+ * stable clock, such as the main oscillator, while the PLL frequency
+ * stabilizes.
+ */
+static int mtk_clk_mux_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *_data)
+{
+ struct clk_notifier_data *data = _data;
+ struct clk_hw *hw = __clk_get_hw(data->clk);
+ struct mtk_mux_nb *mux_nb = to_mtk_mux_nb(nb);
+ int ret = 0;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ mux_nb->original_index = mux_nb->ops->get_parent(hw);
+ ret = mux_nb->ops->set_parent(hw, mux_nb->bypass_index);
+ break;
+ case POST_RATE_CHANGE:
+ case ABORT_RATE_CHANGE:
+ ret = mux_nb->ops->set_parent(hw, mux_nb->original_index);
+ break;
+ }
+
+ return notifier_from_errno(ret);
+}
+
+int devm_mtk_clk_mux_notifier_register(struct device *dev, struct clk *clk,
+ struct mtk_mux_nb *mux_nb)
+{
+ mux_nb->nb.notifier_call = mtk_clk_mux_notifier_cb;
+
+ return devm_clk_notifier_register(dev, clk, &mux_nb->nb);
+}
+EXPORT_SYMBOL_GPL(devm_mtk_clk_mux_notifier_register);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/clk-mux.h b/drivers/clk/mediatek/clk-mux.h
index 6539c58f5d7d..151e56dcf884 100644
--- a/drivers/clk/mediatek/clk-mux.h
+++ b/drivers/clk/mediatek/clk-mux.h
@@ -7,18 +7,21 @@
#ifndef __DRV_CLK_MTK_MUX_H
#define __DRV_CLK_MTK_MUX_H
+#include <linux/notifier.h>
#include <linux/spinlock.h>
#include <linux/types.h>
struct clk;
struct clk_hw_onecell_data;
struct clk_ops;
+struct device;
struct device_node;
struct mtk_mux {
int id;
const char *name;
const char * const *parent_names;
+ const u8 *parent_index;
unsigned int flags;
u32 mux_ofs;
@@ -26,18 +29,25 @@ struct mtk_mux {
u32 clr_ofs;
u32 upd_ofs;
+ u32 hwv_set_ofs;
+ u32 hwv_clr_ofs;
+ u32 hwv_sta_ofs;
+ u32 fenc_sta_mon_ofs;
+
u8 mux_shift;
u8 mux_width;
u8 gate_shift;
s8 upd_shift;
+ u8 fenc_shift;
const struct clk_ops *ops;
signed char num_parents;
};
-#define GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _mux_ofs, \
- _mux_set_ofs, _mux_clr_ofs, _shift, _width, \
- _gate, _upd_ofs, _upd, _flags, _ops) { \
+#define __GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _paridx, \
+ _num_parents, _mux_ofs, _mux_set_ofs, \
+ _mux_clr_ofs, _shift, _width, _gate, _upd_ofs, \
+ _upd, _flags, _ops) { \
.id = _id, \
.name = _name, \
.mux_ofs = _mux_ofs, \
@@ -49,13 +59,32 @@ struct mtk_mux {
.gate_shift = _gate, \
.upd_shift = _upd, \
.parent_names = _parents, \
- .num_parents = ARRAY_SIZE(_parents), \
+ .parent_index = _paridx, \
+ .num_parents = _num_parents, \
.flags = _flags, \
.ops = &_ops, \
}
+#define GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _mux_ofs, \
+ _mux_set_ofs, _mux_clr_ofs, _shift, _width, \
+ _gate, _upd_ofs, _upd, _flags, _ops) \
+ __GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ NULL, ARRAY_SIZE(_parents), _mux_ofs, \
+ _mux_set_ofs, _mux_clr_ofs, _shift, _width, \
+ _gate, _upd_ofs, _upd, _flags, _ops) \
+
+#define GATE_CLR_SET_UPD_FLAGS_INDEXED(_id, _name, _parents, _paridx, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, _shift, \
+ _width, _gate, _upd_ofs, _upd, _flags, _ops) \
+ __GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ _paridx, ARRAY_SIZE(_paridx), _mux_ofs, \
+ _mux_set_ofs, _mux_clr_ofs, _shift, _width, \
+ _gate, _upd_ofs, _upd, _flags, _ops) \
+
extern const struct clk_ops mtk_mux_clr_set_upd_ops;
extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
+extern const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops;
+extern const struct clk_ops mtk_mux_gate_hwv_fenc_clr_set_upd_ops;
#define MUX_GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _mux_ofs, \
_mux_set_ofs, _mux_clr_ofs, _shift, _width, \
@@ -65,6 +94,14 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
_gate, _upd_ofs, _upd, _flags, \
mtk_mux_gate_clr_set_upd_ops)
+#define MUX_GATE_CLR_SET_UPD_FLAGS_INDEXED(_id, _name, _parents, \
+ _paridx, _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, _flags) \
+ GATE_CLR_SET_UPD_FLAGS_INDEXED(_id, _name, _parents, \
+ _paridx, _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, _flags, \
+ mtk_mux_gate_clr_set_upd_ops)
+
#define MUX_GATE_CLR_SET_UPD(_id, _name, _parents, _mux_ofs, \
_mux_set_ofs, _mux_clr_ofs, _shift, _width, \
_gate, _upd_ofs, _upd) \
@@ -73,6 +110,14 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
_width, _gate, _upd_ofs, _upd, \
CLK_SET_RATE_PARENT)
+#define MUX_GATE_CLR_SET_UPD_INDEXED(_id, _name, _parents, _paridx, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, _shift, \
+ _width, _gate, _upd_ofs, _upd) \
+ MUX_GATE_CLR_SET_UPD_FLAGS_INDEXED(_id, _name, \
+ _parents, _paridx, _mux_ofs, _mux_set_ofs, \
+ _mux_clr_ofs, _shift, _width, _gate, _upd_ofs, \
+ _upd, CLK_SET_RATE_PARENT)
+
#define MUX_CLR_SET_UPD(_id, _name, _parents, _mux_ofs, \
_mux_set_ofs, _mux_clr_ofs, _shift, _width, \
_upd_ofs, _upd) \
@@ -81,7 +126,87 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
0, _upd_ofs, _upd, CLK_SET_RATE_PARENT, \
mtk_mux_clr_set_upd_ops)
-int mtk_clk_register_muxes(const struct mtk_mux *muxes,
+#define MUX_GATE_HWV_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .mux_ofs = _mux_ofs, \
+ .set_ofs = _mux_set_ofs, \
+ .clr_ofs = _mux_clr_ofs, \
+ .hwv_sta_ofs = _hwv_sta_ofs, \
+ .hwv_set_ofs = _hwv_set_ofs, \
+ .hwv_clr_ofs = _hwv_clr_ofs, \
+ .upd_ofs = _upd_ofs, \
+ .fenc_sta_mon_ofs = _fenc_sta_mon_ofs, \
+ .mux_shift = _shift, \
+ .mux_width = _width, \
+ .gate_shift = _gate, \
+ .upd_shift = _upd, \
+ .fenc_shift = _fenc, \
+ .parent_names = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .flags = _flags, \
+ .ops = &mtk_mux_gate_hwv_fenc_clr_set_upd_ops, \
+ }
+
+#define MUX_GATE_HWV_FENC_CLR_SET_UPD(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc) \
+ MUX_GATE_HWV_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, 0)
+
+#define MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, _paridx, \
+ _num_parents, _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .mux_ofs = _mux_ofs, \
+ .set_ofs = _mux_set_ofs, \
+ .clr_ofs = _mux_clr_ofs, \
+ .upd_ofs = _upd_ofs, \
+ .fenc_sta_mon_ofs = _fenc_sta_mon_ofs, \
+ .mux_shift = _shift, \
+ .mux_width = _width, \
+ .gate_shift = _gate, \
+ .upd_shift = _upd, \
+ .fenc_shift = _fenc, \
+ .parent_names = _parents, \
+ .parent_index = _paridx, \
+ .num_parents = _num_parents, \
+ .flags = _flags, \
+ .ops = &mtk_mux_gate_fenc_clr_set_upd_ops, \
+ }
+
+#define MUX_GATE_FENC_CLR_SET_UPD(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc) \
+ MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ NULL, ARRAY_SIZE(_parents), _mux_ofs, \
+ _mux_set_ofs, _mux_clr_ofs, _shift, \
+ _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, 0)
+
+#define MUX_GATE_FENC_CLR_SET_UPD_INDEXED(_id, _name, _parents, _paridx, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc) \
+ MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, _paridx, \
+ ARRAY_SIZE(_paridx), _mux_ofs, _mux_set_ofs, \
+ _mux_clr_ofs, _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, 0)
+
+int mtk_clk_register_muxes(struct device *dev,
+ const struct mtk_mux *muxes,
int num, struct device_node *node,
spinlock_t *lock,
struct clk_hw_onecell_data *clk_data);
@@ -89,4 +214,17 @@ int mtk_clk_register_muxes(const struct mtk_mux *muxes,
void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num,
struct clk_hw_onecell_data *clk_data);
+struct mtk_mux_nb {
+ struct notifier_block nb;
+ const struct clk_ops *ops;
+
+ u8 bypass_index; /* Which parent to temporarily use */
+ u8 original_index; /* Set by notifier callback */
+};
+
+#define to_mtk_mux_nb(_nb) container_of(_nb, struct mtk_mux_nb, nb)
+
+int devm_mtk_clk_mux_notifier_register(struct device *dev, struct clk *clk,
+ struct mtk_mux_nb *mux_nb);
+
#endif /* __DRV_CLK_MTK_MUX_H */
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
index 54e6cfd29dfc..cd2b6ce551c6 100644
--- a/drivers/clk/mediatek/clk-pll.c
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -23,45 +23,25 @@
#define CON0_BASE_EN BIT(0)
#define CON0_PWR_ON BIT(0)
#define CON0_ISO_EN BIT(1)
-#define PCW_CHG_MASK BIT(31)
+#define PCW_CHG_BIT 31
#define AUDPLL_TUNER_EN BIT(31)
-#define POSTDIV_MASK 0x7
-
/* default 7 bits integer, can be overridden with pcwibits. */
#define INTEGER_BITS 7
-/*
- * MediaTek PLLs are configured through their pcw value. The pcw value describes
- * a divider in the PLL feedback loop which consists of 7 bits for the integer
- * part and the remaining bits (if present) for the fractional part. Also they
- * have a 3 bit power-of-two post divider.
- */
-
-struct mtk_clk_pll {
- struct clk_hw hw;
- void __iomem *base_addr;
- void __iomem *pd_addr;
- void __iomem *pwr_addr;
- void __iomem *tuner_addr;
- void __iomem *tuner_en_addr;
- void __iomem *pcw_addr;
- void __iomem *pcw_chg_addr;
- void __iomem *en_addr;
- const struct mtk_pll_data *data;
-};
-
-static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
+int mtk_pll_is_prepared(struct clk_hw *hw)
{
- return container_of(hw, struct mtk_clk_pll, hw);
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ return (readl(pll->en_addr) & BIT(pll->data->pll_en_bit)) != 0;
}
-static int mtk_pll_is_prepared(struct clk_hw *hw)
+static int mtk_pll_fenc_is_prepared(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
- return (readl(pll->en_addr) & BIT(pll->data->pll_en_bit)) != 0;
+ return !!(readl(pll->fenc_addr) & BIT(pll->data->fenc_sta_bit));
}
static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin,
@@ -141,7 +121,8 @@ static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
pll->data->pcw_shift);
val |= pcw << pll->data->pcw_shift;
writel(val, pll->pcw_addr);
- chg = readl(pll->pcw_chg_addr) | PCW_CHG_MASK;
+ chg = readl(pll->pcw_chg_addr) |
+ BIT(pll->data->pcw_chg_bit ? : PCW_CHG_BIT);
writel(chg, pll->pcw_chg_addr);
if (pll->tuner_addr)
writel(val + 1, pll->tuner_addr);
@@ -161,8 +142,8 @@ static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
* @fin: The input frequency
*
*/
-static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
- u32 freq, u32 fin)
+void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
+ u32 freq, u32 fin)
{
unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000 * MHZ);
const struct mtk_pll_div_table *div_table = pll->data->div_table;
@@ -198,8 +179,8 @@ static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
*pcw = (u32)_pcw;
}
-static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 pcw = 0;
@@ -211,8 +192,7 @@ static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 postdiv;
@@ -227,19 +207,22 @@ static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
}
-static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+int mtk_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 pcw = 0;
int postdiv;
- mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate);
+ mtk_pll_calc_values(pll, &pcw, &postdiv, req->rate,
+ req->best_parent_rate);
- return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
+ req->rate = __mtk_pll_recalc_rate(pll, req->best_parent_rate, pcw,
+ postdiv);
+
+ return 0;
}
-static int mtk_pll_prepare(struct clk_hw *hw)
+int mtk_pll_prepare(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 r;
@@ -273,7 +256,7 @@ static int mtk_pll_prepare(struct clk_hw *hw)
return 0;
}
-static void mtk_pll_unprepare(struct clk_hw *hw)
+void mtk_pll_unprepare(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 r;
@@ -301,27 +284,53 @@ static void mtk_pll_unprepare(struct clk_hw *hw)
writel(r, pll->pwr_addr);
}
-static const struct clk_ops mtk_pll_ops = {
+static int mtk_pll_prepare_setclr(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ writel(BIT(pll->data->pll_en_bit), pll->en_set_addr);
+
+ /* Wait 20us after enable for the PLL to stabilize */
+ udelay(20);
+
+ return 0;
+}
+
+static void mtk_pll_unprepare_setclr(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ writel(BIT(pll->data->pll_en_bit), pll->en_clr_addr);
+}
+
+const struct clk_ops mtk_pll_ops = {
.is_prepared = mtk_pll_is_prepared,
.prepare = mtk_pll_prepare,
.unprepare = mtk_pll_unprepare,
.recalc_rate = mtk_pll_recalc_rate,
- .round_rate = mtk_pll_round_rate,
+ .determine_rate = mtk_pll_determine_rate,
.set_rate = mtk_pll_set_rate,
};
-static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
- void __iomem *base)
+const struct clk_ops mtk_pll_fenc_clr_set_ops = {
+ .is_prepared = mtk_pll_fenc_is_prepared,
+ .prepare = mtk_pll_prepare_setclr,
+ .unprepare = mtk_pll_unprepare_setclr,
+ .recalc_rate = mtk_pll_recalc_rate,
+ .determine_rate = mtk_pll_determine_rate,
+ .set_rate = mtk_pll_set_rate,
+};
+EXPORT_SYMBOL_GPL(mtk_pll_fenc_clr_set_ops);
+
+struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
+ const struct mtk_pll_data *data,
+ void __iomem *base,
+ const struct clk_ops *pll_ops)
{
- struct mtk_clk_pll *pll;
struct clk_init_data init = {};
int ret;
const char *parent_name = "clk26m";
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return ERR_PTR(-ENOMEM);
-
pll->base_addr = base + data->reg;
pll->pwr_addr = base + data->pwr_reg;
pll->pd_addr = base + data->pd_reg;
@@ -338,12 +347,18 @@ static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
pll->en_addr = base + data->en_reg;
else
pll->en_addr = pll->base_addr + REG_CON0;
+ if (data->en_set_reg)
+ pll->en_set_addr = base + data->en_set_reg;
+ if (data->en_clr_reg)
+ pll->en_clr_addr = base + data->en_clr_reg;
pll->hw.init = &init;
pll->data = data;
+ pll->fenc_addr = base + data->fenc_sta_ofs;
+
init.name = data->name;
init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
- init.ops = &mtk_pll_ops;
+ init.ops = pll_ops;
if (data->parent_name)
init.parent_names = &data->parent_name;
else
@@ -352,15 +367,31 @@ static struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
ret = clk_hw_register(NULL, &pll->hw);
- if (ret) {
- kfree(pll);
+ if (ret)
return ERR_PTR(ret);
- }
return &pll->hw;
}
-static void mtk_clk_unregister_pll(struct clk_hw *hw)
+struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
+ void __iomem *base)
+{
+ struct mtk_clk_pll *pll;
+ struct clk_hw *hw;
+ const struct clk_ops *pll_ops = data->ops ? data->ops : &mtk_pll_ops;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ hw = mtk_clk_register_pll_ops(pll, data, base, pll_ops);
+ if (IS_ERR(hw))
+ kfree(pll);
+
+ return hw;
+}
+
+void mtk_clk_unregister_pll(struct clk_hw *hw)
{
struct mtk_clk_pll *pll;
@@ -423,8 +454,8 @@ err:
}
EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
-static __iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
- const struct mtk_pll_data *data)
+__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
+ const struct mtk_pll_data *data)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h
index fe3199715688..d71c150ce83e 100644
--- a/drivers/clk/mediatek/clk-pll.h
+++ b/drivers/clk/mediatek/clk-pll.h
@@ -7,6 +7,7 @@
#ifndef __DRV_CLK_MTK_PLL_H
#define __DRV_CLK_MTK_PLL_H
+#include <linux/clk-provider.h>
#include <linux/types.h>
struct clk_ops;
@@ -20,6 +21,7 @@ struct mtk_pll_div_table {
#define HAVE_RST_BAR BIT(0)
#define PLL_AO BIT(1)
+#define POSTDIV_MASK GENMASK(2, 0)
struct mtk_pll_data {
int id;
@@ -27,6 +29,7 @@ struct mtk_pll_data {
u32 reg;
u32 pwr_reg;
u32 en_mask;
+ u32 fenc_sta_ofs;
u32 pd_reg;
u32 tuner_reg;
u32 tuner_en_reg;
@@ -45,7 +48,34 @@ struct mtk_pll_data {
const struct mtk_pll_div_table *div_table;
const char *parent_name;
u32 en_reg;
+ u32 en_set_reg;
+ u32 en_clr_reg;
u8 pll_en_bit; /* Assume 0, indicates BIT(0) by default */
+ u8 pcw_chg_bit;
+ u8 fenc_sta_bit;
+};
+
+/*
+ * MediaTek PLLs are configured through their pcw value. The pcw value describes
+ * a divider in the PLL feedback loop which consists of 7 bits for the integer
+ * part and the remaining bits (if present) for the fractional part. Also they
+ * have a 3 bit power-of-two post divider.
+ */
+
+struct mtk_clk_pll {
+ struct clk_hw hw;
+ void __iomem *base_addr;
+ void __iomem *pd_addr;
+ void __iomem *pwr_addr;
+ void __iomem *tuner_addr;
+ void __iomem *tuner_en_addr;
+ void __iomem *pcw_addr;
+ void __iomem *pcw_chg_addr;
+ void __iomem *en_addr;
+ void __iomem *en_set_addr;
+ void __iomem *en_clr_addr;
+ void __iomem *fenc_addr;
+ const struct mtk_pll_data *data;
};
int mtk_clk_register_plls(struct device_node *node,
@@ -54,4 +84,37 @@ int mtk_clk_register_plls(struct device_node *node,
void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls,
struct clk_hw_onecell_data *clk_data);
+extern const struct clk_ops mtk_pll_ops;
+extern const struct clk_ops mtk_pll_fenc_clr_set_ops;
+
+static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
+{
+ return container_of(hw, struct mtk_clk_pll, hw);
+}
+
+int mtk_pll_is_prepared(struct clk_hw *hw);
+
+int mtk_pll_prepare(struct clk_hw *hw);
+
+void mtk_pll_unprepare(struct clk_hw *hw);
+
+unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate);
+
+void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
+ u32 freq, u32 fin);
+int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
+int mtk_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req);
+
+struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
+ const struct mtk_pll_data *data,
+ void __iomem *base,
+ const struct clk_ops *pll_ops);
+struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
+ void __iomem *base);
+void mtk_clk_unregister_pll(struct clk_hw *hw);
+
+__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
+ const struct mtk_pll_data *data);
+
#endif /* __DRV_CLK_MTK_PLL_H */
diff --git a/drivers/clk/mediatek/clk-pllfh.c b/drivers/clk/mediatek/clk-pllfh.c
new file mode 100644
index 000000000000..83630ee07ee9
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pllfh.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <edward-jw.yang@mediatek.com>
+ */
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+
+#include "clk-mtk.h"
+#include "clk-pllfh.h"
+#include "clk-fhctl.h"
+
+static DEFINE_SPINLOCK(pllfh_lock);
+
+inline struct mtk_fh *to_mtk_fh(struct clk_hw *hw)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+ return container_of(pll, struct mtk_fh, clk_pll);
+}
+
+static int mtk_fhctl_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+ struct mtk_fh *fh = to_mtk_fh(hw);
+ u32 pcw = 0;
+ u32 postdiv;
+
+ mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate);
+
+ return fh->ops->hopping(fh, pcw, postdiv);
+}
+
+static const struct clk_ops mtk_pllfh_ops = {
+ .is_prepared = mtk_pll_is_prepared,
+ .prepare = mtk_pll_prepare,
+ .unprepare = mtk_pll_unprepare,
+ .recalc_rate = mtk_pll_recalc_rate,
+ .determine_rate = mtk_pll_determine_rate,
+ .set_rate = mtk_fhctl_set_rate,
+};
+
+static struct mtk_pllfh_data *get_pllfh_by_id(struct mtk_pllfh_data *pllfhs,
+ int num_fhs, int pll_id)
+{
+ int i;
+
+ for (i = 0; i < num_fhs; i++)
+ if (pllfhs[i].data.pll_id == pll_id)
+ return &pllfhs[i];
+
+ return NULL;
+}
+
+void fhctl_parse_dt(const u8 *compatible_node, struct mtk_pllfh_data *pllfhs,
+ int num_fhs)
+{
+ void __iomem *base;
+ struct device_node *node;
+ u32 num_clocks, pll_id, ssc_rate;
+ int offset, i;
+
+ node = of_find_compatible_node(NULL, NULL, compatible_node);
+ if (!node) {
+ pr_warn("cannot find \"%s\"\n", compatible_node);
+ return;
+ }
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ goto out_node_put;
+ }
+
+ num_clocks = of_clk_get_parent_count(node);
+ if (!num_clocks) {
+ pr_err("%s(): failed to get clocks property\n", __func__);
+ goto err;
+ }
+
+ for (i = 0; i < num_clocks; i++) {
+ struct mtk_pllfh_data *pllfh;
+
+ offset = i * 2;
+
+ of_property_read_u32_index(node, "clocks", offset + 1, &pll_id);
+ of_property_read_u32_index(node,
+ "mediatek,hopping-ssc-percent",
+ i, &ssc_rate);
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll_id);
+ if (!pllfh)
+ continue;
+
+ pllfh->state.fh_enable = 1;
+ pllfh->state.ssc_rate = ssc_rate;
+ pllfh->state.base = base;
+ }
+
+out_node_put:
+ of_node_put(node);
+ return;
+err:
+ iounmap(base);
+ goto out_node_put;
+}
+EXPORT_SYMBOL_GPL(fhctl_parse_dt);
+
+static int pllfh_init(struct mtk_fh *fh, struct mtk_pllfh_data *pllfh_data)
+{
+ struct fh_pll_regs *regs = &fh->regs;
+ const struct fhctl_offset *offset;
+ void __iomem *base = pllfh_data->state.base;
+ void __iomem *fhx_base = base + pllfh_data->data.fhx_offset;
+
+ offset = fhctl_get_offset_table(pllfh_data->data.fh_ver);
+ if (IS_ERR(offset))
+ return PTR_ERR(offset);
+
+ regs->reg_hp_en = base + offset->offset_hp_en;
+ regs->reg_clk_con = base + offset->offset_clk_con;
+ regs->reg_rst_con = base + offset->offset_rst_con;
+ regs->reg_slope0 = base + offset->offset_slope0;
+ regs->reg_slope1 = base + offset->offset_slope1;
+
+ regs->reg_cfg = fhx_base + offset->offset_cfg;
+ regs->reg_updnlmt = fhx_base + offset->offset_updnlmt;
+ regs->reg_dds = fhx_base + offset->offset_dds;
+ regs->reg_dvfs = fhx_base + offset->offset_dvfs;
+ regs->reg_mon = fhx_base + offset->offset_mon;
+
+ fh->pllfh_data = pllfh_data;
+ fh->lock = &pllfh_lock;
+
+ fh->ops = fhctl_get_ops();
+
+ return 0;
+}
+
+static bool fhctl_is_supported_and_enabled(const struct mtk_pllfh_data *pllfh)
+{
+ return pllfh && (pllfh->state.fh_enable == 1);
+}
+
+static struct clk_hw *
+mtk_clk_register_pllfh(const struct mtk_pll_data *pll_data,
+ struct mtk_pllfh_data *pllfh_data, void __iomem *base)
+{
+ struct clk_hw *hw;
+ struct mtk_fh *fh;
+ int ret;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh)
+ return ERR_PTR(-ENOMEM);
+
+ ret = pllfh_init(fh, pllfh_data);
+ if (ret) {
+ hw = ERR_PTR(ret);
+ goto out;
+ }
+
+ hw = mtk_clk_register_pll_ops(&fh->clk_pll, pll_data, base,
+ &mtk_pllfh_ops);
+
+ if (IS_ERR(hw))
+ goto out;
+
+ fhctl_hw_init(fh);
+
+out:
+ if (IS_ERR(hw))
+ kfree(fh);
+
+ return hw;
+}
+
+static void mtk_clk_unregister_pllfh(struct clk_hw *hw)
+{
+ struct mtk_fh *fh;
+
+ if (!hw)
+ return;
+
+ fh = to_mtk_fh(hw);
+
+ clk_hw_unregister(hw);
+ kfree(fh);
+}
+
+int mtk_clk_register_pllfhs(struct device_node *node,
+ const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_fhs,
+ struct clk_hw_onecell_data *clk_data)
+{
+ void __iomem *base;
+ int i;
+ struct clk_hw *hw;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_plls; i++) {
+ const struct mtk_pll_data *pll = &plls[i];
+ struct mtk_pllfh_data *pllfh;
+ bool use_fhctl;
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id);
+ use_fhctl = fhctl_is_supported_and_enabled(pllfh);
+
+ if (use_fhctl)
+ hw = mtk_clk_register_pllfh(pll, pllfh, base);
+ else
+ hw = mtk_clk_register_pll(pll, base);
+
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register %s clk %s: %ld\n",
+ use_fhctl ? "fhpll" : "pll", pll->name,
+ PTR_ERR(hw));
+ goto err;
+ }
+
+ clk_data->hws[pll->id] = hw;
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0) {
+ const struct mtk_pll_data *pll = &plls[i];
+ struct mtk_pllfh_data *pllfh;
+ bool use_fhctl;
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id);
+ use_fhctl = fhctl_is_supported_and_enabled(pllfh);
+
+ if (use_fhctl)
+ mtk_clk_unregister_pllfh(clk_data->hws[pll->id]);
+ else
+ mtk_clk_unregister_pll(clk_data->hws[pll->id]);
+
+ clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
+ }
+
+ iounmap(base);
+
+ return PTR_ERR(hw);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_register_pllfhs);
+
+void mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_fhs,
+ struct clk_hw_onecell_data *clk_data)
+{
+ void __iomem *base = NULL, *fhctl_base = NULL;
+ int i;
+
+ if (!clk_data)
+ return;
+
+ for (i = num_plls; i > 0; i--) {
+ const struct mtk_pll_data *pll = &plls[i - 1];
+ struct mtk_pllfh_data *pllfh;
+ bool use_fhctl;
+
+ if (IS_ERR_OR_NULL(clk_data->hws[pll->id]))
+ continue;
+
+ pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id);
+ use_fhctl = fhctl_is_supported_and_enabled(pllfh);
+
+ if (use_fhctl) {
+ fhctl_base = pllfh->state.base;
+ mtk_clk_unregister_pllfh(clk_data->hws[pll->id]);
+ } else {
+ base = mtk_clk_pll_get_base(clk_data->hws[pll->id],
+ pll);
+ mtk_clk_unregister_pll(clk_data->hws[pll->id]);
+ }
+
+ clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
+ }
+
+ if (fhctl_base)
+ iounmap(fhctl_base);
+
+ iounmap(base);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_unregister_pllfhs);
diff --git a/drivers/clk/mediatek/clk-pllfh.h b/drivers/clk/mediatek/clk-pllfh.h
new file mode 100644
index 000000000000..5f419c2ec01f
--- /dev/null
+++ b/drivers/clk/mediatek/clk-pllfh.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Edward-JW Yang <edward-jw.yang@mediatek.com>
+ */
+
+#ifndef __CLK_PLLFH_H
+#define __CLK_PLLFH_H
+
+#include "clk-pll.h"
+
+struct fh_pll_state {
+ void __iomem *base;
+ u32 fh_enable;
+ u32 ssc_rate;
+};
+
+struct fh_pll_data {
+ int pll_id;
+ int fh_id;
+ int fh_ver;
+ u32 fhx_offset;
+ u32 dds_mask;
+ u32 slope0_value;
+ u32 slope1_value;
+ u32 sfstrx_en;
+ u32 frddsx_en;
+ u32 fhctlx_en;
+ u32 tgl_org;
+ u32 dvfs_tri;
+ u32 pcwchg;
+ u32 dt_val;
+ u32 df_val;
+ u32 updnlmt_shft;
+ u32 msk_frddsx_dys;
+ u32 msk_frddsx_dts;
+};
+
+struct mtk_pllfh_data {
+ struct fh_pll_state state;
+ const struct fh_pll_data data;
+};
+
+struct fh_pll_regs {
+ void __iomem *reg_hp_en;
+ void __iomem *reg_clk_con;
+ void __iomem *reg_rst_con;
+ void __iomem *reg_slope0;
+ void __iomem *reg_slope1;
+ void __iomem *reg_cfg;
+ void __iomem *reg_updnlmt;
+ void __iomem *reg_dds;
+ void __iomem *reg_dvfs;
+ void __iomem *reg_mon;
+};
+
+struct mtk_fh {
+ struct mtk_clk_pll clk_pll;
+ struct fh_pll_regs regs;
+ struct mtk_pllfh_data *pllfh_data;
+ const struct fh_operation *ops;
+ spinlock_t *lock;
+};
+
+struct fh_operation {
+ int (*hopping)(struct mtk_fh *fh, unsigned int new_dds,
+ unsigned int postdiv);
+ int (*ssc_enable)(struct mtk_fh *fh, u32 rate);
+};
+
+int mtk_clk_register_pllfhs(struct device_node *node,
+ const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_pllfhs,
+ struct clk_hw_onecell_data *clk_data);
+
+void mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls,
+ struct mtk_pllfh_data *pllfhs, int num_fhs,
+ struct clk_hw_onecell_data *clk_data);
+
+void fhctl_parse_dt(const u8 *compatible_node, struct mtk_pllfh_data *pllfhs,
+ int num_pllfhs);
+
+#endif /* __CLK_PLLFH_H */
diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c
index 179505549a7c..2e3303975096 100644
--- a/drivers/clk/mediatek/reset.c
+++ b/drivers/clk/mediatek/reset.c
@@ -110,65 +110,6 @@ static int reset_xlate(struct reset_controller_dev *rcdev,
return data->desc->rst_idx_map[reset_spec->args[0]];
}
-int mtk_register_reset_controller(struct device_node *np,
- const struct mtk_clk_rst_desc *desc)
-{
- struct regmap *regmap;
- const struct reset_control_ops *rcops = NULL;
- struct mtk_clk_rst_data *data;
- int ret;
-
- if (!desc) {
- pr_err("mtk clock reset desc is NULL\n");
- return -EINVAL;
- }
-
- switch (desc->version) {
- case MTK_RST_SIMPLE:
- rcops = &mtk_reset_ops;
- break;
- case MTK_RST_SET_CLR:
- rcops = &mtk_reset_ops_set_clr;
- break;
- default:
- pr_err("Unknown reset version %d\n", desc->version);
- return -EINVAL;
- }
-
- regmap = device_node_to_regmap(np);
- if (IS_ERR(regmap)) {
- pr_err("Cannot find regmap for %pOF: %pe\n", np, regmap);
- return -EINVAL;
- }
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->desc = desc;
- data->regmap = regmap;
- data->rcdev.owner = THIS_MODULE;
- data->rcdev.ops = rcops;
- data->rcdev.of_node = np;
-
- if (data->desc->rst_idx_map_nr > 0) {
- data->rcdev.of_reset_n_cells = 1;
- data->rcdev.nr_resets = desc->rst_idx_map_nr;
- data->rcdev.of_xlate = reset_xlate;
- } else {
- data->rcdev.nr_resets = desc->rst_bank_nr * RST_NR_PER_BANK;
- }
-
- ret = reset_controller_register(&data->rcdev);
- if (ret) {
- pr_err("could not register reset controller: %d\n", ret);
- kfree(data);
- return ret;
- }
-
- return 0;
-}
-
int mtk_register_reset_controller_with_dev(struct device *dev,
const struct mtk_clk_rst_desc *desc)
{
@@ -198,7 +139,7 @@ int mtk_register_reset_controller_with_dev(struct device *dev,
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap)) {
dev_err(dev, "Cannot find regmap %pe\n", regmap);
- return -EINVAL;
+ return PTR_ERR(regmap);
}
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -228,5 +169,6 @@ int mtk_register_reset_controller_with_dev(struct device *dev,
return 0;
}
+EXPORT_SYMBOL_GPL(mtk_register_reset_controller_with_dev);
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mediatek/reset.h b/drivers/clk/mediatek/reset.h
index 6a58a3d59165..562ffd290a22 100644
--- a/drivers/clk/mediatek/reset.h
+++ b/drivers/clk/mediatek/reset.h
@@ -60,16 +60,6 @@ struct mtk_clk_rst_data {
};
/**
- * mtk_register_reset_controller - Register MediaTek clock reset controller
- * @np: Pointer to device node.
- * @desc: Constant pointer to description of clock reset.
- *
- * Return: 0 on success and errorno otherwise.
- */
-int mtk_register_reset_controller(struct device_node *np,
- const struct mtk_clk_rst_desc *desc);
-
-/**
* mtk_register_reset_controller - Register mediatek clock reset controller with device
* @np: Pointer to device.
* @desc: Constant pointer to description of clock reset.
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index fc002c155bc3..71481607a6d5 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -5,6 +5,7 @@ menu "Clock support for Amlogic platforms"
config COMMON_CLK_MESON_REGMAP
tristate
select REGMAP
+ select MFD_SYSCON
config COMMON_CLK_MESON_DUALDIV
tristate
@@ -30,14 +31,20 @@ config COMMON_CLK_MESON_VID_PLL_DIV
tristate
select COMMON_CLK_MESON_REGMAP
-config COMMON_CLK_MESON_AO_CLKC
+config COMMON_CLK_MESON_VCLK
tristate
select COMMON_CLK_MESON_REGMAP
- select RESET_CONTROLLER
-config COMMON_CLK_MESON_EE_CLKC
+config COMMON_CLK_MESON_CLKC_UTILS
+ select REGMAP
+ select MFD_SYSCON
+ tristate
+
+config COMMON_CLK_MESON_AO_CLKC
tristate
select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select RESET_CONTROLLER
config COMMON_CLK_MESON_CPU_DYNDIV
tristate
@@ -46,8 +53,9 @@ config COMMON_CLK_MESON_CPU_DYNDIV
config COMMON_CLK_MESON8B
bool "Meson8 SoC Clock controller support"
depends on ARM
- default y
+ default ARCH_MESON
select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select MFD_SYSCON
@@ -60,14 +68,14 @@ config COMMON_CLK_MESON8B
config COMMON_CLK_GXBB
tristate "GXBB and GXL SoC clock controllers support"
depends on ARM64
- default y
+ default ARCH_MESON
select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
select COMMON_CLK_MESON_DUALDIV
select COMMON_CLK_MESON_VID_PLL_DIV
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select COMMON_CLK_MESON_AO_CLKC
- select COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on AmLogic S905 devices, aka gxbb.
@@ -76,13 +84,13 @@ config COMMON_CLK_GXBB
config COMMON_CLK_AXG
tristate "AXG SoC clock controllers support"
depends on ARM64
- default y
+ default ARCH_MESON
select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
select COMMON_CLK_MESON_DUALDIV
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select COMMON_CLK_MESON_AO_CLKC
- select COMMON_CLK_MESON_EE_CLKC
select MFD_SYSCON
help
Support for the clock controller on AmLogic A113D devices, aka axg.
@@ -94,25 +102,103 @@ config COMMON_CLK_AXG_AUDIO
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_PHASE
select COMMON_CLK_MESON_SCLK_DIV
+ select COMMON_CLK_MESON_CLKC_UTILS
select REGMAP_MMIO
+ select AUXILIARY_BUS
+ imply RESET_MESON_AUX
help
Support for the audio clock controller on AmLogic A113D devices,
aka axg, Say Y if you want audio subsystem to work.
+config COMMON_CLK_A1_PLL
+ tristate "Amlogic A1 SoC PLL controller support"
+ depends on ARM64
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_PLL
+ help
+ Support for the PLL clock controller on Amlogic A113L based
+ device, A1 SoC Family. Say Y if you want A1 PLL clock controller
+ to work.
+
+config COMMON_CLK_A1_PERIPHERALS
+ tristate "Amlogic A1 SoC Peripherals clock controller support"
+ depends on ARM64
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
+ help
+ Support for the Peripherals clock controller on Amlogic A113L based
+ device, A1 SoC Family. Say Y if you want A1 Peripherals clock
+ controller to work.
+
+config COMMON_CLK_C3_PLL
+ tristate "Amlogic C3 PLL clock controller"
+ depends on ARM64
+ default ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_PLL
+ select COMMON_CLK_MESON_CLKC_UTILS
+ imply COMMON_CLK_SCMI
+ help
+ Support for the PLL clock controller on Amlogic C302X and C308L devices,
+ AKA C3. Say Y if you want the board to work, because PLLs are the parent
+ of most peripherals.
+
+config COMMON_CLK_C3_PERIPHERALS
+ tristate "Amlogic C3 peripherals clock controller"
+ depends on ARM64
+ default ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_CLKC_UTILS
+ imply COMMON_CLK_SCMI
+ imply COMMON_CLK_C3_PLL
+ help
+ Support for the Peripherals clock controller on Amlogic C302X and
+ C308L devices, AKA C3. Say Y if you want the peripherals clock to
+ work.
+
config COMMON_CLK_G12A
tristate "G12 and SM1 SoC clock controllers support"
depends on ARM64
- default y
+ default ARCH_MESON
select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
select COMMON_CLK_MESON_DUALDIV
select COMMON_CLK_MESON_MPLL
select COMMON_CLK_MESON_PLL
select COMMON_CLK_MESON_AO_CLKC
- select COMMON_CLK_MESON_EE_CLKC
select COMMON_CLK_MESON_CPU_DYNDIV
select COMMON_CLK_MESON_VID_PLL_DIV
+ select COMMON_CLK_MESON_VCLK
select MFD_SYSCON
help
Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
devices, aka g12a. Say Y if you want peripherals to work.
+
+config COMMON_CLK_S4_PLL
+ tristate "S4 SoC PLL clock controllers support"
+ depends on ARM64
+ default ARCH_MESON
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_MPLL
+ select COMMON_CLK_MESON_PLL
+ select COMMON_CLK_MESON_REGMAP
+ help
+ Support for the PLL clock controller on Amlogic S805X2 and S905Y4 devices,
+ AKA S4. Say Y if you want the board to work, because PLLs are the parent of
+ most peripherals.
+
+config COMMON_CLK_S4_PERIPHERALS
+ tristate "S4 SoC peripherals clock controllers support"
+ depends on ARM64
+ default ARCH_MESON
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_VID_PLL_DIV
+ help
+ Support for the peripherals clock controller on Amlogic S805X2 and S905Y4
+ devices, AKA S4. Say Y if you want S4 peripherals clock controller to work.
endmenu
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 6eca2a406ee3..c6998e752c68 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -1,21 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-only
# Amlogic clock drivers
+obj-$(CONFIG_COMMON_CLK_MESON_CLKC_UTILS) += meson-clkc-utils.o
obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON_CPU_DYNDIV) += clk-cpu-dyndiv.o
obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o
-obj-$(CONFIG_COMMON_CLK_MESON_EE_CLKC) += meson-eeclk.o
obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o
obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o
obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
+obj-$(CONFIG_COMMON_CLK_MESON_VCLK) += vclk.o
# Amlogic Clock controllers
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
+obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
+obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
+obj-$(CONFIG_COMMON_CLK_C3_PLL) += c3-pll.o
+obj-$(CONFIG_COMMON_CLK_C3_PERIPHERALS) += c3-peripherals.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
+obj-$(CONFIG_COMMON_CLK_S4_PLL) += s4-pll.o
+obj-$(CONFIG_COMMON_CLK_S4_PERIPHERALS) += s4-peripherals.o
diff --git a/drivers/clk/meson/a1-peripherals.c b/drivers/clk/meson/a1-peripherals.c
new file mode 100644
index 000000000000..5e0d58c01405
--- /dev/null
+++ b/drivers/clk/meson/a1-peripherals.c
@@ -0,0 +1,2100 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ * Author: Jian Hu <jian.hu@amlogic.com>
+ *
+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
+ * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include "clk-dualdiv.h"
+#include "clk-regmap.h"
+#include "meson-clkc-utils.h"
+
+#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
+
+#define SYS_OSCIN_CTRL 0x0
+#define RTC_BY_OSCIN_CTRL0 0x4
+#define RTC_BY_OSCIN_CTRL1 0x8
+#define RTC_CTRL 0xc
+#define SYS_CLK_CTRL0 0x10
+#define SYS_CLK_EN0 0x1c
+#define SYS_CLK_EN1 0x20
+#define AXI_CLK_EN 0x24
+#define DSPA_CLK_EN 0x28
+#define DSPB_CLK_EN 0x2c
+#define DSPA_CLK_CTRL0 0x30
+#define DSPB_CLK_CTRL0 0x34
+#define CLK12_24_CTRL 0x38
+#define GEN_CLK_CTRL 0x3c
+#define SAR_ADC_CLK_CTRL 0xc0
+#define PWM_CLK_AB_CTRL 0xc4
+#define PWM_CLK_CD_CTRL 0xc8
+#define PWM_CLK_EF_CTRL 0xcc
+#define SPICC_CLK_CTRL 0xd0
+#define TS_CLK_CTRL 0xd4
+#define SPIFC_CLK_CTRL 0xd8
+#define USB_BUSCLK_CTRL 0xdc
+#define SD_EMMC_CLK_CTRL 0xe0
+#define CECA_CLK_CTRL0 0xe4
+#define CECA_CLK_CTRL1 0xe8
+#define CECB_CLK_CTRL0 0xec
+#define CECB_CLK_CTRL1 0xf0
+#define PSRAM_CLK_CTRL 0xf4
+#define DMC_CLK_CTRL 0xf8
+
+static struct clk_regmap a1_xtal_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "xtal_in",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fixpll_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fixpll_in",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_usb_phy_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_phy_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_usb_ctrl_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_ctrl_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_hifipll_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hifipll_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_syspll_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 5,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "syspll_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_dds_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_OSCIN_CTRL,
+ .bit_idx = 6,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dds_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_rtc_32k_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param a1_32k_div_table[] = {
+ {
+ .dual = 1,
+ .n1 = 733,
+ .m1 = 8,
+ .n2 = 732,
+ .m2 = 11,
+ },
+ {}
+};
+
+static struct clk_regmap a1_rtc_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = RTC_BY_OSCIN_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = RTC_BY_OSCIN_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = a1_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_rtc_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_rtc_32k_xtal = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = RTC_BY_OSCIN_CTRL1,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_xtal",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_rtc_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_rtc_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = RTC_CTRL,
+ .mask = 0x3,
+ .shift = 0,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_rtc_32k_xtal.hw,
+ &a1_rtc_32k_div.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_rtc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_rtc_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static u32 a1_sys_parents_val_table[] = { 0, 1, 2, 3, 7 };
+static const struct clk_parent_data a1_sys_parents[] = {
+ { .fw_name = "xtal" },
+ { .fw_name = "fclk_div2" },
+ { .fw_name = "fclk_div3" },
+ { .fw_name = "fclk_div5" },
+ { .hw = &a1_rtc.hw },
+};
+
+static struct clk_regmap a1_sys_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SYS_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = a1_sys_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_b_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = a1_sys_parents,
+ .num_parents = ARRAY_SIZE(a1_sys_parents),
+ },
+};
+
+static struct clk_regmap a1_sys_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SYS_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_b_div",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sys_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_sys_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sys_b",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sys_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_sys_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SYS_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = a1_sys_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_a_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = a1_sys_parents,
+ .num_parents = ARRAY_SIZE(a1_sys_parents),
+ },
+};
+
+static struct clk_regmap a1_sys_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SYS_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_a_div",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sys_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_sys_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SYS_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sys_a",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sys_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_sys = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SYS_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sys_a.hw,
+ &a1_sys_b.hw,
+ },
+ .num_parents = 2,
+ /*
+ * This clock is used by APB bus which is set in boot ROM code
+ * and is required by the platform to operate correctly.
+ * Until the following condition are met, we need this clock to
+ * be marked as critical:
+ * a) Mark the clock used by a firmware resource, if possible
+ * b) CCF has a clock hand-off mechanism to make the sure the
+ * clock stays on until the proper driver comes along
+ */
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ },
+};
+
+static u32 a1_dsp_parents_val_table[] = { 0, 1, 2, 3, 4, 7 };
+static const struct clk_parent_data a1_dsp_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "hifi_pll", },
+ { .hw = &a1_rtc.hw },
+};
+
+static struct clk_regmap a1_dspa_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = a1_dsp_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_a_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_dsp_parents,
+ .num_parents = ARRAY_SIZE(a1_dsp_parents),
+ },
+};
+
+static struct clk_regmap a1_dspa_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPA_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspa_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspa_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = a1_dsp_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_b_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_dsp_parents,
+ .num_parents = ARRAY_SIZE(a1_dsp_parents),
+ },
+};
+
+static struct clk_regmap a1_dspa_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPA_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspa_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspa_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_a.hw,
+ &a1_dspa_b.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspa_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_EN,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspa_en_nic = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPA_CLK_EN,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspa_en_nic",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspa_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPB_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = a1_dsp_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_a_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_dsp_parents,
+ .num_parents = ARRAY_SIZE(a1_dsp_parents),
+ },
+};
+
+static struct clk_regmap a1_dspb_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPB_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPB_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = a1_dsp_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_b_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_dsp_parents,
+ .num_parents = ARRAY_SIZE(a1_dsp_parents),
+ },
+};
+
+static struct clk_regmap a1_dspb_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DSPB_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPB_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspb_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_a.hw,
+ &a1_dspb_b.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_EN,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dspb_en_nic = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DSPB_CLK_EN,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dspb_en_nic",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dspb_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_24m = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "24m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor a1_24m_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "24m_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_24m.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_12m = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 10,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "12m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_24m_div2.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fclk_div2_divn_pre = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLK12_24_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_divn_pre",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fclk_div2",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fclk_div2_divn = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 12,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_divn",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fclk_div2_divn_pre.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * the index 2 is sys_pll_div16, it will be implemented in the CPU clock driver,
+ * the index 4 is the clock measurement source, it's not supported yet
+ */
+static u32 a1_gen_parents_val_table[] = { 0, 1, 3, 5, 6, 7, 8 };
+static const struct clk_parent_data a1_gen_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &a1_rtc.hw },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+};
+
+static struct clk_regmap a1_gen_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = GEN_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 12,
+ .table = a1_gen_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gen_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_gen_parents,
+ .num_parents = ARRAY_SIZE(a1_gen_parents),
+ /*
+ * The GEN clock can be connected to an external pad, so it
+ * may be set up directly from the device tree. Additionally,
+ * the GEN clock can be inherited from a more accurate RTC
+ * clock, so in certain situations, it may be necessary
+ * to freeze its parent.
+ */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap a1_gen_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = GEN_CLK_CTRL,
+ .shift = 0,
+ .width = 11,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gen_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_gen_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_gen = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = GEN_CLK_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gen",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_gen_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_saradc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SAR_ADC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", },
+ { .hw = &a1_sys.hw, },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap a1_saradc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SAR_ADC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_saradc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_saradc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SAR_ADC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "saradc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_saradc_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a1_pwm_abcd_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &a1_sys.hw },
+ { .hw = &a1_rtc.hw },
+};
+
+static struct clk_regmap a1_pwm_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .mask = 0x1,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_a_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(a1_pwm_abcd_parents),
+ },
+};
+
+static struct clk_regmap a1_pwm_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_a_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_a_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_a",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_a_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .mask = 0x1,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_b_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(a1_pwm_abcd_parents),
+ },
+};
+
+static struct clk_regmap a1_pwm_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .shift = 16,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_b_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_b_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_AB_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_b",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_b_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_c_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .mask = 0x1,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_c_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(a1_pwm_abcd_parents),
+ },
+};
+
+static struct clk_regmap a1_pwm_c_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_c_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_c_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_c = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_c",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_c_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_d_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .mask = 0x1,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_d_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_pwm_abcd_parents,
+ .num_parents = ARRAY_SIZE(a1_pwm_abcd_parents),
+ },
+};
+
+static struct clk_regmap a1_pwm_d_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .shift = 16,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_d_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_d_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_d = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_CD_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_d",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_d_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a1_pwm_ef_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &a1_sys.hw },
+ { .fw_name = "fclk_div5", },
+ { .hw = &a1_rtc.hw },
+};
+
+static struct clk_regmap a1_pwm_e_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_e_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_pwm_ef_parents,
+ .num_parents = ARRAY_SIZE(a1_pwm_ef_parents),
+ },
+};
+
+static struct clk_regmap a1_pwm_e_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_e_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_e_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_e = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_e",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_e_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_f_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .mask = 0x3,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_f_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_pwm_ef_parents,
+ .num_parents = ARRAY_SIZE(a1_pwm_ef_parents),
+ },
+};
+
+static struct clk_regmap a1_pwm_f_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .shift = 16,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pwm_f_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_f_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_pwm_f = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PWM_CLK_EF_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "pwm_f",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_pwm_f_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * spicc clk
+ * fdiv2 |\ |\ _____
+ * ---------| |---DIV--| | | | spicc out
+ * ---------| | | |-----|GATE |---------
+ * ..... |/ | / |_____|
+ * --------------------|/
+ * 24M
+ */
+static const struct clk_parent_data a1_spi_parents[] = {
+ { .fw_name = "fclk_div2"},
+ { .fw_name = "fclk_div3"},
+ { .fw_name = "fclk_div5"},
+ { .fw_name = "hifi_pll" },
+};
+
+static struct clk_regmap a1_spicc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPICC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_spi_parents,
+ .num_parents = ARRAY_SIZE(a1_spi_parents),
+ },
+};
+
+static struct clk_regmap a1_spicc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SPICC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_spicc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_spicc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPICC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a1_spicc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_spicc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SPICC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "spicc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_spicc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_ts_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = TS_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ts_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_ts = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = TS_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ts",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_ts_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_spifc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPIFC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spifc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_spi_parents,
+ .num_parents = ARRAY_SIZE(a1_spi_parents),
+ },
+};
+
+static struct clk_regmap a1_spifc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SPIFC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spifc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_spifc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_spifc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SPIFC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spifc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a1_spifc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_spifc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SPIFC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "spifc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_spifc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a1_usb_bus_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &a1_sys.hw },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+};
+
+static struct clk_regmap a1_usb_bus_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = USB_BUSCLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_bus_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_usb_bus_parents,
+ .num_parents = ARRAY_SIZE(a1_usb_bus_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_usb_bus_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = USB_BUSCLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_bus_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_usb_bus_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_usb_bus = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = USB_BUSCLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_bus",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_usb_bus_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a1_sd_emmc_parents[] = {
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "hifi_pll", },
+};
+
+static struct clk_regmap a1_sd_emmc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_sd_emmc_parents,
+ .num_parents = ARRAY_SIZE(a1_sd_emmc_parents),
+ },
+};
+
+static struct clk_regmap a1_sd_emmc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sd_emmc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_sd_emmc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a1_sd_emmc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_sd_emmc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = SD_EMMC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_sd_emmc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_psram_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PSRAM_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "psram_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_sd_emmc_parents,
+ .num_parents = ARRAY_SIZE(a1_sd_emmc_parents),
+ },
+};
+
+static struct clk_regmap a1_psram_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = PSRAM_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "psram_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_psram_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_psram_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = PSRAM_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "psram_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a1_psram_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_psram = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = PSRAM_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "psram",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_psram_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dmc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DMC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dmc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a1_sd_emmc_parents,
+ .num_parents = ARRAY_SIZE(a1_sd_emmc_parents),
+ },
+};
+
+static struct clk_regmap a1_dmc_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = DMC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dmc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dmc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dmc_sel2 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DMC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dmc_sel2",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a1_dmc_div.hw },
+ { .fw_name = "xtal", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_dmc = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = DMC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "dmc",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_dmc_sel2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_ceca_32k_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECA_CLK_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ceca_32k_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_ceca_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CECA_CLK_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CECA_CLK_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CECA_CLK_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CECA_CLK_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CECA_CLK_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = a1_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_ceca_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_ceca_32k_sel_pre = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECA_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_sel_pre",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_ceca_32k_div.hw,
+ &a1_ceca_32k_in.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_ceca_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECA_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 31,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_ceca_32k_sel_pre.hw,
+ &a1_rtc.hw,
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap a1_ceca_32k_out = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECA_CLK_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_out",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_ceca_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_cecb_32k_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECB_CLK_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cecb_32k_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_cecb_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CECB_CLK_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CECB_CLK_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CECB_CLK_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CECB_CLK_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CECB_CLK_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = a1_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_cecb_32k_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_cecb_32k_sel_pre = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECB_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_sel_pre",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_cecb_32k_div.hw,
+ &a1_cecb_32k_in.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a1_cecb_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CECB_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 31,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_cecb_32k_sel_pre.hw,
+ &a1_rtc.hw,
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap a1_cecb_32k_out = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CECB_CLK_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_out",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_cecb_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a1_pclk_parents = { .hw = &a1_sys.hw };
+
+#define A1_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(a1_##_name, _reg, _bit, &a1_pclk_parents, _flags)
+
+/*
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
+ */
+static A1_PCLK(clktree, SYS_CLK_EN0, 0, CLK_IGNORE_UNUSED);
+static A1_PCLK(reset_ctrl, SYS_CLK_EN0, 1, CLK_IGNORE_UNUSED);
+static A1_PCLK(analog_ctrl, SYS_CLK_EN0, 2, CLK_IGNORE_UNUSED);
+static A1_PCLK(pwr_ctrl, SYS_CLK_EN0, 3, CLK_IGNORE_UNUSED);
+static A1_PCLK(pad_ctrl, SYS_CLK_EN0, 4, CLK_IGNORE_UNUSED);
+static A1_PCLK(sys_ctrl, SYS_CLK_EN0, 5, CLK_IGNORE_UNUSED);
+static A1_PCLK(temp_sensor, SYS_CLK_EN0, 6, CLK_IGNORE_UNUSED);
+static A1_PCLK(am2axi_dev, SYS_CLK_EN0, 7, CLK_IGNORE_UNUSED);
+static A1_PCLK(spicc_b, SYS_CLK_EN0, 8, CLK_IGNORE_UNUSED);
+static A1_PCLK(spicc_a, SYS_CLK_EN0, 9, CLK_IGNORE_UNUSED);
+static A1_PCLK(msr, SYS_CLK_EN0, 10, CLK_IGNORE_UNUSED);
+static A1_PCLK(audio, SYS_CLK_EN0, 11, CLK_IGNORE_UNUSED);
+static A1_PCLK(jtag_ctrl, SYS_CLK_EN0, 12, CLK_IGNORE_UNUSED);
+static A1_PCLK(saradc_en, SYS_CLK_EN0, 13, CLK_IGNORE_UNUSED);
+static A1_PCLK(pwm_ef, SYS_CLK_EN0, 14, CLK_IGNORE_UNUSED);
+static A1_PCLK(pwm_cd, SYS_CLK_EN0, 15, CLK_IGNORE_UNUSED);
+static A1_PCLK(pwm_ab, SYS_CLK_EN0, 16, CLK_IGNORE_UNUSED);
+static A1_PCLK(cec, SYS_CLK_EN0, 17, CLK_IGNORE_UNUSED);
+static A1_PCLK(i2c_s, SYS_CLK_EN0, 18, CLK_IGNORE_UNUSED);
+static A1_PCLK(ir_ctrl, SYS_CLK_EN0, 19, CLK_IGNORE_UNUSED);
+static A1_PCLK(i2c_m_d, SYS_CLK_EN0, 20, CLK_IGNORE_UNUSED);
+static A1_PCLK(i2c_m_c, SYS_CLK_EN0, 21, CLK_IGNORE_UNUSED);
+static A1_PCLK(i2c_m_b, SYS_CLK_EN0, 22, CLK_IGNORE_UNUSED);
+static A1_PCLK(i2c_m_a, SYS_CLK_EN0, 23, CLK_IGNORE_UNUSED);
+static A1_PCLK(acodec, SYS_CLK_EN0, 24, CLK_IGNORE_UNUSED);
+static A1_PCLK(otp, SYS_CLK_EN0, 25, CLK_IGNORE_UNUSED);
+static A1_PCLK(sd_emmc_a, SYS_CLK_EN0, 26, CLK_IGNORE_UNUSED);
+static A1_PCLK(usb_phy, SYS_CLK_EN0, 27, CLK_IGNORE_UNUSED);
+static A1_PCLK(usb_ctrl, SYS_CLK_EN0, 28, CLK_IGNORE_UNUSED);
+static A1_PCLK(sys_dspb, SYS_CLK_EN0, 29, CLK_IGNORE_UNUSED);
+static A1_PCLK(sys_dspa, SYS_CLK_EN0, 30, CLK_IGNORE_UNUSED);
+static A1_PCLK(dma, SYS_CLK_EN0, 31, CLK_IGNORE_UNUSED);
+
+static A1_PCLK(irq_ctrl, SYS_CLK_EN1, 0, CLK_IGNORE_UNUSED);
+static A1_PCLK(nic, SYS_CLK_EN1, 1, CLK_IGNORE_UNUSED);
+static A1_PCLK(gic, SYS_CLK_EN1, 2, CLK_IGNORE_UNUSED);
+static A1_PCLK(uart_c, SYS_CLK_EN1, 3, CLK_IGNORE_UNUSED);
+static A1_PCLK(uart_b, SYS_CLK_EN1, 4, CLK_IGNORE_UNUSED);
+static A1_PCLK(uart_a, SYS_CLK_EN1, 5, CLK_IGNORE_UNUSED);
+static A1_PCLK(sys_psram, SYS_CLK_EN1, 6, CLK_IGNORE_UNUSED);
+static A1_PCLK(rsa, SYS_CLK_EN1, 8, CLK_IGNORE_UNUSED);
+static A1_PCLK(coresight, SYS_CLK_EN1, 9, CLK_IGNORE_UNUSED);
+
+static A1_PCLK(am2axi_vad, AXI_CLK_EN, 0, CLK_IGNORE_UNUSED);
+static A1_PCLK(audio_vad, AXI_CLK_EN, 1, CLK_IGNORE_UNUSED);
+static A1_PCLK(axi_dmc, AXI_CLK_EN, 3, CLK_IGNORE_UNUSED);
+static A1_PCLK(axi_psram, AXI_CLK_EN, 4, CLK_IGNORE_UNUSED);
+static A1_PCLK(ramb, AXI_CLK_EN, 5, CLK_IGNORE_UNUSED);
+static A1_PCLK(rama, AXI_CLK_EN, 6, CLK_IGNORE_UNUSED);
+static A1_PCLK(axi_spifc, AXI_CLK_EN, 7, CLK_IGNORE_UNUSED);
+static A1_PCLK(axi_nic, AXI_CLK_EN, 8, CLK_IGNORE_UNUSED);
+static A1_PCLK(axi_dma, AXI_CLK_EN, 9, CLK_IGNORE_UNUSED);
+static A1_PCLK(cpu_ctrl, AXI_CLK_EN, 10, CLK_IGNORE_UNUSED);
+static A1_PCLK(rom, AXI_CLK_EN, 11, CLK_IGNORE_UNUSED);
+static A1_PCLK(prod_i2c, AXI_CLK_EN, 12, CLK_IGNORE_UNUSED);
+
+/* Array of all clocks registered by this provider */
+static struct clk_hw *a1_peripherals_hw_clks[] = {
+ [CLKID_XTAL_IN] = &a1_xtal_in.hw,
+ [CLKID_FIXPLL_IN] = &a1_fixpll_in.hw,
+ [CLKID_USB_PHY_IN] = &a1_usb_phy_in.hw,
+ [CLKID_USB_CTRL_IN] = &a1_usb_ctrl_in.hw,
+ [CLKID_HIFIPLL_IN] = &a1_hifipll_in.hw,
+ [CLKID_SYSPLL_IN] = &a1_syspll_in.hw,
+ [CLKID_DDS_IN] = &a1_dds_in.hw,
+ [CLKID_SYS] = &a1_sys.hw,
+ [CLKID_CLKTREE] = &a1_clktree.hw,
+ [CLKID_RESET_CTRL] = &a1_reset_ctrl.hw,
+ [CLKID_ANALOG_CTRL] = &a1_analog_ctrl.hw,
+ [CLKID_PWR_CTRL] = &a1_pwr_ctrl.hw,
+ [CLKID_PAD_CTRL] = &a1_pad_ctrl.hw,
+ [CLKID_SYS_CTRL] = &a1_sys_ctrl.hw,
+ [CLKID_TEMP_SENSOR] = &a1_temp_sensor.hw,
+ [CLKID_AM2AXI_DIV] = &a1_am2axi_dev.hw,
+ [CLKID_SPICC_B] = &a1_spicc_b.hw,
+ [CLKID_SPICC_A] = &a1_spicc_a.hw,
+ [CLKID_MSR] = &a1_msr.hw,
+ [CLKID_AUDIO] = &a1_audio.hw,
+ [CLKID_JTAG_CTRL] = &a1_jtag_ctrl.hw,
+ [CLKID_SARADC_EN] = &a1_saradc_en.hw,
+ [CLKID_PWM_EF] = &a1_pwm_ef.hw,
+ [CLKID_PWM_CD] = &a1_pwm_cd.hw,
+ [CLKID_PWM_AB] = &a1_pwm_ab.hw,
+ [CLKID_CEC] = &a1_cec.hw,
+ [CLKID_I2C_S] = &a1_i2c_s.hw,
+ [CLKID_IR_CTRL] = &a1_ir_ctrl.hw,
+ [CLKID_I2C_M_D] = &a1_i2c_m_d.hw,
+ [CLKID_I2C_M_C] = &a1_i2c_m_c.hw,
+ [CLKID_I2C_M_B] = &a1_i2c_m_b.hw,
+ [CLKID_I2C_M_A] = &a1_i2c_m_a.hw,
+ [CLKID_ACODEC] = &a1_acodec.hw,
+ [CLKID_OTP] = &a1_otp.hw,
+ [CLKID_SD_EMMC_A] = &a1_sd_emmc_a.hw,
+ [CLKID_USB_PHY] = &a1_usb_phy.hw,
+ [CLKID_USB_CTRL] = &a1_usb_ctrl.hw,
+ [CLKID_SYS_DSPB] = &a1_sys_dspb.hw,
+ [CLKID_SYS_DSPA] = &a1_sys_dspa.hw,
+ [CLKID_DMA] = &a1_dma.hw,
+ [CLKID_IRQ_CTRL] = &a1_irq_ctrl.hw,
+ [CLKID_NIC] = &a1_nic.hw,
+ [CLKID_GIC] = &a1_gic.hw,
+ [CLKID_UART_C] = &a1_uart_c.hw,
+ [CLKID_UART_B] = &a1_uart_b.hw,
+ [CLKID_UART_A] = &a1_uart_a.hw,
+ [CLKID_SYS_PSRAM] = &a1_sys_psram.hw,
+ [CLKID_RSA] = &a1_rsa.hw,
+ [CLKID_CORESIGHT] = &a1_coresight.hw,
+ [CLKID_AM2AXI_VAD] = &a1_am2axi_vad.hw,
+ [CLKID_AUDIO_VAD] = &a1_audio_vad.hw,
+ [CLKID_AXI_DMC] = &a1_axi_dmc.hw,
+ [CLKID_AXI_PSRAM] = &a1_axi_psram.hw,
+ [CLKID_RAMB] = &a1_ramb.hw,
+ [CLKID_RAMA] = &a1_rama.hw,
+ [CLKID_AXI_SPIFC] = &a1_axi_spifc.hw,
+ [CLKID_AXI_NIC] = &a1_axi_nic.hw,
+ [CLKID_AXI_DMA] = &a1_axi_dma.hw,
+ [CLKID_CPU_CTRL] = &a1_cpu_ctrl.hw,
+ [CLKID_ROM] = &a1_rom.hw,
+ [CLKID_PROC_I2C] = &a1_prod_i2c.hw,
+ [CLKID_DSPA_SEL] = &a1_dspa_sel.hw,
+ [CLKID_DSPB_SEL] = &a1_dspb_sel.hw,
+ [CLKID_DSPA_EN] = &a1_dspa_en.hw,
+ [CLKID_DSPA_EN_NIC] = &a1_dspa_en_nic.hw,
+ [CLKID_DSPB_EN] = &a1_dspb_en.hw,
+ [CLKID_DSPB_EN_NIC] = &a1_dspb_en_nic.hw,
+ [CLKID_RTC] = &a1_rtc.hw,
+ [CLKID_CECA_32K] = &a1_ceca_32k_out.hw,
+ [CLKID_CECB_32K] = &a1_cecb_32k_out.hw,
+ [CLKID_24M] = &a1_24m.hw,
+ [CLKID_12M] = &a1_12m.hw,
+ [CLKID_FCLK_DIV2_DIVN] = &a1_fclk_div2_divn.hw,
+ [CLKID_GEN] = &a1_gen.hw,
+ [CLKID_SARADC_SEL] = &a1_saradc_sel.hw,
+ [CLKID_SARADC] = &a1_saradc.hw,
+ [CLKID_PWM_A] = &a1_pwm_a.hw,
+ [CLKID_PWM_B] = &a1_pwm_b.hw,
+ [CLKID_PWM_C] = &a1_pwm_c.hw,
+ [CLKID_PWM_D] = &a1_pwm_d.hw,
+ [CLKID_PWM_E] = &a1_pwm_e.hw,
+ [CLKID_PWM_F] = &a1_pwm_f.hw,
+ [CLKID_SPICC] = &a1_spicc.hw,
+ [CLKID_TS] = &a1_ts.hw,
+ [CLKID_SPIFC] = &a1_spifc.hw,
+ [CLKID_USB_BUS] = &a1_usb_bus.hw,
+ [CLKID_SD_EMMC] = &a1_sd_emmc.hw,
+ [CLKID_PSRAM] = &a1_psram.hw,
+ [CLKID_DMC] = &a1_dmc.hw,
+ [CLKID_SYS_A_SEL] = &a1_sys_a_sel.hw,
+ [CLKID_SYS_A_DIV] = &a1_sys_a_div.hw,
+ [CLKID_SYS_A] = &a1_sys_a.hw,
+ [CLKID_SYS_B_SEL] = &a1_sys_b_sel.hw,
+ [CLKID_SYS_B_DIV] = &a1_sys_b_div.hw,
+ [CLKID_SYS_B] = &a1_sys_b.hw,
+ [CLKID_DSPA_A_SEL] = &a1_dspa_a_sel.hw,
+ [CLKID_DSPA_A_DIV] = &a1_dspa_a_div.hw,
+ [CLKID_DSPA_A] = &a1_dspa_a.hw,
+ [CLKID_DSPA_B_SEL] = &a1_dspa_b_sel.hw,
+ [CLKID_DSPA_B_DIV] = &a1_dspa_b_div.hw,
+ [CLKID_DSPA_B] = &a1_dspa_b.hw,
+ [CLKID_DSPB_A_SEL] = &a1_dspb_a_sel.hw,
+ [CLKID_DSPB_A_DIV] = &a1_dspb_a_div.hw,
+ [CLKID_DSPB_A] = &a1_dspb_a.hw,
+ [CLKID_DSPB_B_SEL] = &a1_dspb_b_sel.hw,
+ [CLKID_DSPB_B_DIV] = &a1_dspb_b_div.hw,
+ [CLKID_DSPB_B] = &a1_dspb_b.hw,
+ [CLKID_RTC_32K_IN] = &a1_rtc_32k_in.hw,
+ [CLKID_RTC_32K_DIV] = &a1_rtc_32k_div.hw,
+ [CLKID_RTC_32K_XTAL] = &a1_rtc_32k_xtal.hw,
+ [CLKID_RTC_32K_SEL] = &a1_rtc_32k_sel.hw,
+ [CLKID_CECB_32K_IN] = &a1_cecb_32k_in.hw,
+ [CLKID_CECB_32K_DIV] = &a1_cecb_32k_div.hw,
+ [CLKID_CECB_32K_SEL_PRE] = &a1_cecb_32k_sel_pre.hw,
+ [CLKID_CECB_32K_SEL] = &a1_cecb_32k_sel.hw,
+ [CLKID_CECA_32K_IN] = &a1_ceca_32k_in.hw,
+ [CLKID_CECA_32K_DIV] = &a1_ceca_32k_div.hw,
+ [CLKID_CECA_32K_SEL_PRE] = &a1_ceca_32k_sel_pre.hw,
+ [CLKID_CECA_32K_SEL] = &a1_ceca_32k_sel.hw,
+ [CLKID_DIV2_PRE] = &a1_fclk_div2_divn_pre.hw,
+ [CLKID_24M_DIV2] = &a1_24m_div2.hw,
+ [CLKID_GEN_SEL] = &a1_gen_sel.hw,
+ [CLKID_GEN_DIV] = &a1_gen_div.hw,
+ [CLKID_SARADC_DIV] = &a1_saradc_div.hw,
+ [CLKID_PWM_A_SEL] = &a1_pwm_a_sel.hw,
+ [CLKID_PWM_A_DIV] = &a1_pwm_a_div.hw,
+ [CLKID_PWM_B_SEL] = &a1_pwm_b_sel.hw,
+ [CLKID_PWM_B_DIV] = &a1_pwm_b_div.hw,
+ [CLKID_PWM_C_SEL] = &a1_pwm_c_sel.hw,
+ [CLKID_PWM_C_DIV] = &a1_pwm_c_div.hw,
+ [CLKID_PWM_D_SEL] = &a1_pwm_d_sel.hw,
+ [CLKID_PWM_D_DIV] = &a1_pwm_d_div.hw,
+ [CLKID_PWM_E_SEL] = &a1_pwm_e_sel.hw,
+ [CLKID_PWM_E_DIV] = &a1_pwm_e_div.hw,
+ [CLKID_PWM_F_SEL] = &a1_pwm_f_sel.hw,
+ [CLKID_PWM_F_DIV] = &a1_pwm_f_div.hw,
+ [CLKID_SPICC_SEL] = &a1_spicc_sel.hw,
+ [CLKID_SPICC_DIV] = &a1_spicc_div.hw,
+ [CLKID_SPICC_SEL2] = &a1_spicc_sel2.hw,
+ [CLKID_TS_DIV] = &a1_ts_div.hw,
+ [CLKID_SPIFC_SEL] = &a1_spifc_sel.hw,
+ [CLKID_SPIFC_DIV] = &a1_spifc_div.hw,
+ [CLKID_SPIFC_SEL2] = &a1_spifc_sel2.hw,
+ [CLKID_USB_BUS_SEL] = &a1_usb_bus_sel.hw,
+ [CLKID_USB_BUS_DIV] = &a1_usb_bus_div.hw,
+ [CLKID_SD_EMMC_SEL] = &a1_sd_emmc_sel.hw,
+ [CLKID_SD_EMMC_DIV] = &a1_sd_emmc_div.hw,
+ [CLKID_SD_EMMC_SEL2] = &a1_sd_emmc_sel2.hw,
+ [CLKID_PSRAM_SEL] = &a1_psram_sel.hw,
+ [CLKID_PSRAM_DIV] = &a1_psram_div.hw,
+ [CLKID_PSRAM_SEL2] = &a1_psram_sel2.hw,
+ [CLKID_DMC_SEL] = &a1_dmc_sel.hw,
+ [CLKID_DMC_DIV] = &a1_dmc_div.hw,
+ [CLKID_DMC_SEL2] = &a1_dmc_sel2.hw,
+};
+
+static const struct meson_clkc_data a1_peripherals_clkc_data = {
+ .hw_clks = {
+ .hws = a1_peripherals_hw_clks,
+ .num = ARRAY_SIZE(a1_peripherals_hw_clks),
+ },
+};
+
+static const struct of_device_id a1_peripherals_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,a1-peripherals-clkc",
+ .data = &a1_peripherals_clkc_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, a1_peripherals_clkc_match_table);
+
+static struct platform_driver a1_peripherals_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "a1-peripherals-clkc",
+ .of_match_table = a1_peripherals_clkc_match_table,
+ },
+};
+module_platform_driver(a1_peripherals_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic A1 Peripherals Clock Controller driver");
+MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
+MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
new file mode 100644
index 000000000000..1f82e9c7c14e
--- /dev/null
+++ b/drivers/clk/meson/a1-pll.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ * Author: Jian Hu <jian.hu@amlogic.com>
+ *
+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
+ * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include "clk-pll.h"
+#include "clk-regmap.h"
+#include "meson-clkc-utils.h"
+
+#define ANACTRL_FIXPLL_CTRL0 0x0
+#define ANACTRL_FIXPLL_CTRL1 0x4
+#define ANACTRL_FIXPLL_STS 0x14
+#define ANACTRL_HIFIPLL_CTRL0 0xc0
+#define ANACTRL_HIFIPLL_CTRL1 0xc4
+#define ANACTRL_HIFIPLL_CTRL2 0xc8
+#define ANACTRL_HIFIPLL_CTRL3 0xcc
+#define ANACTRL_HIFIPLL_CTRL4 0xd0
+#define ANACTRL_HIFIPLL_STS 0xd4
+
+#include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
+
+static struct clk_regmap a1_fixed_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = ANACTRL_FIXPLL_CTRL1,
+ .shift = 0,
+ .width = 19,
+ },
+ .l = {
+ .reg_off = ANACTRL_FIXPLL_STS,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fixed_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fixpll_in",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fixed_pll = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL0,
+ .bit_idx = 20,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fixed_pll",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct pll_mult_range a1_hifi_pll_range = {
+ .min = 32,
+ .max = 64,
+};
+
+static const struct reg_sequence a1_hifi_pll_init_regs[] = {
+ { .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x01800000 },
+ { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00001100 },
+ { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x100a1100 },
+ { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x00302000 },
+ { .reg = ANACTRL_HIFIPLL_CTRL0, .def = 0x01f18000 },
+};
+
+static struct clk_regmap a1_hifi_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL1,
+ .shift = 0,
+ .width = 19,
+ },
+ .l = {
+ .reg_off = ANACTRL_HIFIPLL_STS,
+ .shift = 31,
+ .width = 1,
+ },
+ .current_en = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 26,
+ .width = 1,
+ },
+ .l_detect = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL2,
+ .shift = 6,
+ .width = 1,
+ },
+ .range = &a1_hifi_pll_range,
+ .init_regs = a1_hifi_pll_init_regs,
+ .init_count = ARRAY_SIZE(a1_hifi_pll_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "hifipll_in",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor a1_fclk_div2_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fixed_pll.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL0,
+ .bit_idx = 21,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fclk_div2_div.hw
+ },
+ .num_parents = 1,
+ /*
+ * This clock is used by DDR clock in BL2 firmware
+ * and is required by the platform to operate correctly.
+ * Until the following condition are met, we need this clock to
+ * be marked as critical:
+ * a) Mark the clock used by a firmware resource, if possible
+ * b) CCF has a clock hand-off mechanism to make the sure the
+ * clock stays on until the proper driver comes along
+ */
+ .flags = CLK_IS_CRITICAL,
+ },
+};
+
+static struct clk_fixed_factor a1_fclk_div3_div = {
+ .mult = 1,
+ .div = 3,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fixed_pll.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL0,
+ .bit_idx = 22,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fclk_div3_div.hw
+ },
+ .num_parents = 1,
+ /*
+ * This clock is used by APB bus which is set in boot ROM code
+ * and is required by the platform to operate correctly.
+ */
+ .flags = CLK_IS_CRITICAL,
+ },
+};
+
+static struct clk_fixed_factor a1_fclk_div5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fixed_pll.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL0,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fclk_div5_div.hw
+ },
+ .num_parents = 1,
+ /*
+ * This clock is used by AXI bus which setted in Romcode
+ * and is required by the platform to operate correctly.
+ */
+ .flags = CLK_IS_CRITICAL,
+ },
+};
+
+static struct clk_fixed_factor a1_fclk_div7_div = {
+ .mult = 1,
+ .div = 7,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fixed_pll.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a1_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL0,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a1_fclk_div7_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+/* Array of all clocks registered by this provider */
+static struct clk_hw *a1_pll_hw_clks[] = {
+ [CLKID_FIXED_PLL_DCO] = &a1_fixed_pll_dco.hw,
+ [CLKID_FIXED_PLL] = &a1_fixed_pll.hw,
+ [CLKID_FCLK_DIV2_DIV] = &a1_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &a1_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &a1_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &a1_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV2] = &a1_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &a1_fclk_div3.hw,
+ [CLKID_FCLK_DIV5] = &a1_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &a1_fclk_div7.hw,
+ [CLKID_HIFI_PLL] = &a1_hifi_pll.hw,
+};
+
+static const struct meson_clkc_data a1_pll_clkc_data = {
+ .hw_clks = {
+ .hws = a1_pll_hw_clks,
+ .num = ARRAY_SIZE(a1_pll_hw_clks),
+ },
+};
+
+static const struct of_device_id a1_pll_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,a1-pll-clkc",
+ .data = &a1_pll_clkc_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);
+
+static struct platform_driver a1_pll_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "a1-pll-clkc",
+ .of_match_table = a1_pll_clkc_match_table,
+ },
+};
+module_platform_driver(a1_pll_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic A1 PLL Clock Controller driver");
+MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
+MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c
index af6db437bcd8..902fbd34039c 100644
--- a/drivers/clk/meson/axg-aoclk.c
+++ b/drivers/clk/meson/axg-aoclk.c
@@ -14,11 +14,13 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include "meson-aoclk.h"
-#include "axg-aoclk.h"
#include "clk-regmap.h"
#include "clk-dualdiv.h"
+#include <dt-bindings/clock/axg-aoclkc.h>
+#include <dt-bindings/reset/axg-aoclkc.h>
+
/*
* AO Configuration Clock registers offsets
* Register offsets from the data sheet must be multiplied by 4.
@@ -32,32 +34,21 @@
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
-#define AXG_AO_GATE(_name, _bit) \
-static struct clk_regmap axg_aoclk_##_name = { \
- .data = &(struct clk_regmap_gate_data) { \
- .offset = (AO_RTI_GEN_CNTL_REG0), \
- .bit_idx = (_bit), \
- }, \
- .hw.init = &(struct clk_init_data) { \
- .name = "axg_ao_" #_name, \
- .ops = &clk_regmap_gate_ops, \
- .parent_data = &(const struct clk_parent_data) { \
- .fw_name = "mpeg-clk", \
- }, \
- .num_parents = 1, \
- .flags = CLK_IGNORE_UNUSED, \
- }, \
-}
+static const struct clk_parent_data axg_ao_pclk_parents = { .fw_name = "mpeg-clk" };
+
+#define AXG_AO_GATE(_name, _bit, _flags) \
+ MESON_PCLK(axg_ao_##_name, AO_RTI_GEN_CNTL_REG0, _bit, \
+ &axg_ao_pclk_parents, _flags)
-AXG_AO_GATE(remote, 0);
-AXG_AO_GATE(i2c_master, 1);
-AXG_AO_GATE(i2c_slave, 2);
-AXG_AO_GATE(uart1, 3);
-AXG_AO_GATE(uart2, 5);
-AXG_AO_GATE(ir_blaster, 6);
-AXG_AO_GATE(saradc, 7);
+static AXG_AO_GATE(remote, 0, CLK_IGNORE_UNUSED);
+static AXG_AO_GATE(i2c_master, 1, CLK_IGNORE_UNUSED);
+static AXG_AO_GATE(i2c_slave, 2, CLK_IGNORE_UNUSED);
+static AXG_AO_GATE(uart1, 3, CLK_IGNORE_UNUSED);
+static AXG_AO_GATE(uart2, 5, CLK_IGNORE_UNUSED);
+static AXG_AO_GATE(ir_blaster, 6, CLK_IGNORE_UNUSED);
+static AXG_AO_GATE(saradc, 7, CLK_IGNORE_UNUSED);
-static struct clk_regmap axg_aoclk_cts_oscin = {
+static struct clk_regmap axg_ao_cts_oscin = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTI_PWR_CNTL_REG0,
.bit_idx = 14,
@@ -72,7 +63,7 @@ static struct clk_regmap axg_aoclk_cts_oscin = {
},
};
-static struct clk_regmap axg_aoclk_32k_pre = {
+static struct clk_regmap axg_ao_32k_pre = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 31,
@@ -81,7 +72,7 @@ static struct clk_regmap axg_aoclk_32k_pre = {
.name = "axg_ao_32k_pre",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_aoclk_cts_oscin.hw
+ &axg_ao_cts_oscin.hw
},
.num_parents = 1,
},
@@ -97,7 +88,7 @@ static const struct meson_clk_dualdiv_param axg_32k_div_table[] = {
}, {}
};
-static struct clk_regmap axg_aoclk_32k_div = {
+static struct clk_regmap axg_ao_32k_div = {
.data = &(struct meson_clk_dualdiv_data){
.n1 = {
.reg_off = AO_RTC_ALT_CLK_CNTL0,
@@ -130,13 +121,13 @@ static struct clk_regmap axg_aoclk_32k_div = {
.name = "axg_ao_32k_div",
.ops = &meson_clk_dualdiv_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_aoclk_32k_pre.hw
+ &axg_ao_32k_pre.hw
},
.num_parents = 1,
},
};
-static struct clk_regmap axg_aoclk_32k_sel = {
+static struct clk_regmap axg_ao_32k_sel = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTC_ALT_CLK_CNTL1,
.mask = 0x1,
@@ -147,15 +138,15 @@ static struct clk_regmap axg_aoclk_32k_sel = {
.name = "axg_ao_32k_sel",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_aoclk_32k_div.hw,
- &axg_aoclk_32k_pre.hw,
+ &axg_ao_32k_div.hw,
+ &axg_ao_32k_pre.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap axg_aoclk_32k = {
+static struct clk_regmap axg_ao_32k = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 30,
@@ -164,14 +155,14 @@ static struct clk_regmap axg_aoclk_32k = {
.name = "axg_ao_32k",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_aoclk_32k_sel.hw
+ &axg_ao_32k_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap axg_aoclk_cts_rtc_oscin = {
+static struct clk_regmap axg_ao_cts_rtc_oscin = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
@@ -182,7 +173,7 @@ static struct clk_regmap axg_aoclk_cts_rtc_oscin = {
.name = "axg_ao_cts_rtc_oscin",
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
- { .hw = &axg_aoclk_32k.hw },
+ { .hw = &axg_ao_32k.hw },
{ .fw_name = "ext_32k-0", },
},
.num_parents = 2,
@@ -190,7 +181,7 @@ static struct clk_regmap axg_aoclk_cts_rtc_oscin = {
},
};
-static struct clk_regmap axg_aoclk_clk81 = {
+static struct clk_regmap axg_ao_clk81 = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
@@ -198,68 +189,74 @@ static struct clk_regmap axg_aoclk_clk81 = {
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
+ /*
+ * NOTE: this is one of the infamous clock the pwm driver
+ * can request directly by its global name. It's wrong but
+ * there is not much we can do about it until the support
+ * for the old pwm bindings is dropped
+ */
.name = "axg_ao_clk81",
.ops = &clk_regmap_mux_ro_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "mpeg-clk", },
- { .hw = &axg_aoclk_cts_rtc_oscin.hw },
+ { .hw = &axg_ao_cts_rtc_oscin.hw },
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap axg_aoclk_saradc_mux = {
+static struct clk_regmap axg_ao_saradc_mux = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_SAR_CLK,
.mask = 0x3,
.shift = 9,
},
.hw.init = &(struct clk_init_data){
- .name = "axg_ao_saradc_mux",
+ .name = "ao_saradc_mux",
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
- { .hw = &axg_aoclk_clk81.hw },
+ { .hw = &axg_ao_clk81.hw },
},
.num_parents = 2,
},
};
-static struct clk_regmap axg_aoclk_saradc_div = {
+static struct clk_regmap axg_ao_saradc_div = {
.data = &(struct clk_regmap_div_data) {
.offset = AO_SAR_CLK,
.shift = 0,
.width = 8,
},
.hw.init = &(struct clk_init_data){
- .name = "axg_ao_saradc_div",
+ .name = "ao_saradc_div",
.ops = &clk_regmap_divider_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_aoclk_saradc_mux.hw
+ &axg_ao_saradc_mux.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap axg_aoclk_saradc_gate = {
+static struct clk_regmap axg_ao_saradc_gate = {
.data = &(struct clk_regmap_gate_data) {
.offset = AO_SAR_CLK,
.bit_idx = 8,
},
.hw.init = &(struct clk_init_data){
- .name = "axg_ao_saradc_gate",
+ .name = "ao_saradc_gate",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_aoclk_saradc_div.hw
+ &axg_ao_saradc_div.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static const unsigned int axg_aoclk_reset[] = {
+static const unsigned int axg_ao_reset[] = {
[RESET_AO_REMOTE] = 16,
[RESET_AO_I2C_MASTER] = 18,
[RESET_AO_I2C_SLAVE] = 19,
@@ -268,74 +265,56 @@ static const unsigned int axg_aoclk_reset[] = {
[RESET_AO_IR_BLASTER] = 23,
};
-static struct clk_regmap *axg_aoclk_regmap[] = {
- &axg_aoclk_remote,
- &axg_aoclk_i2c_master,
- &axg_aoclk_i2c_slave,
- &axg_aoclk_uart1,
- &axg_aoclk_uart2,
- &axg_aoclk_ir_blaster,
- &axg_aoclk_saradc,
- &axg_aoclk_cts_oscin,
- &axg_aoclk_32k_pre,
- &axg_aoclk_32k_div,
- &axg_aoclk_32k_sel,
- &axg_aoclk_32k,
- &axg_aoclk_cts_rtc_oscin,
- &axg_aoclk_clk81,
- &axg_aoclk_saradc_mux,
- &axg_aoclk_saradc_div,
- &axg_aoclk_saradc_gate,
+static struct clk_hw *axg_ao_hw_clks[] = {
+ [CLKID_AO_REMOTE] = &axg_ao_remote.hw,
+ [CLKID_AO_I2C_MASTER] = &axg_ao_i2c_master.hw,
+ [CLKID_AO_I2C_SLAVE] = &axg_ao_i2c_slave.hw,
+ [CLKID_AO_UART1] = &axg_ao_uart1.hw,
+ [CLKID_AO_UART2] = &axg_ao_uart2.hw,
+ [CLKID_AO_IR_BLASTER] = &axg_ao_ir_blaster.hw,
+ [CLKID_AO_SAR_ADC] = &axg_ao_saradc.hw,
+ [CLKID_AO_CLK81] = &axg_ao_clk81.hw,
+ [CLKID_AO_SAR_ADC_SEL] = &axg_ao_saradc_mux.hw,
+ [CLKID_AO_SAR_ADC_DIV] = &axg_ao_saradc_div.hw,
+ [CLKID_AO_SAR_ADC_CLK] = &axg_ao_saradc_gate.hw,
+ [CLKID_AO_CTS_OSCIN] = &axg_ao_cts_oscin.hw,
+ [CLKID_AO_32K_PRE] = &axg_ao_32k_pre.hw,
+ [CLKID_AO_32K_DIV] = &axg_ao_32k_div.hw,
+ [CLKID_AO_32K_SEL] = &axg_ao_32k_sel.hw,
+ [CLKID_AO_32K] = &axg_ao_32k.hw,
+ [CLKID_AO_CTS_RTC_OSCIN] = &axg_ao_cts_rtc_oscin.hw,
};
-static const struct clk_hw_onecell_data axg_aoclk_onecell_data = {
- .hws = {
- [CLKID_AO_REMOTE] = &axg_aoclk_remote.hw,
- [CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master.hw,
- [CLKID_AO_I2C_SLAVE] = &axg_aoclk_i2c_slave.hw,
- [CLKID_AO_UART1] = &axg_aoclk_uart1.hw,
- [CLKID_AO_UART2] = &axg_aoclk_uart2.hw,
- [CLKID_AO_IR_BLASTER] = &axg_aoclk_ir_blaster.hw,
- [CLKID_AO_SAR_ADC] = &axg_aoclk_saradc.hw,
- [CLKID_AO_CLK81] = &axg_aoclk_clk81.hw,
- [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux.hw,
- [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div.hw,
- [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate.hw,
- [CLKID_AO_CTS_OSCIN] = &axg_aoclk_cts_oscin.hw,
- [CLKID_AO_32K_PRE] = &axg_aoclk_32k_pre.hw,
- [CLKID_AO_32K_DIV] = &axg_aoclk_32k_div.hw,
- [CLKID_AO_32K_SEL] = &axg_aoclk_32k_sel.hw,
- [CLKID_AO_32K] = &axg_aoclk_32k.hw,
- [CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw,
- },
- .num = NR_CLKS,
-};
-
-static const struct meson_aoclk_data axg_aoclkc_data = {
+static const struct meson_aoclk_data axg_ao_clkc_data = {
.reset_reg = AO_RTI_GEN_CNTL_REG0,
- .num_reset = ARRAY_SIZE(axg_aoclk_reset),
- .reset = axg_aoclk_reset,
- .num_clks = ARRAY_SIZE(axg_aoclk_regmap),
- .clks = axg_aoclk_regmap,
- .hw_data = &axg_aoclk_onecell_data,
+ .num_reset = ARRAY_SIZE(axg_ao_reset),
+ .reset = axg_ao_reset,
+ .clkc_data = {
+ .hw_clks = {
+ .hws = axg_ao_hw_clks,
+ .num = ARRAY_SIZE(axg_ao_hw_clks),
+ },
+ },
};
-static const struct of_device_id axg_aoclkc_match_table[] = {
+static const struct of_device_id axg_ao_clkc_match_table[] = {
{
.compatible = "amlogic,meson-axg-aoclkc",
- .data = &axg_aoclkc_data,
+ .data = &axg_ao_clkc_data.clkc_data,
},
{ }
};
-MODULE_DEVICE_TABLE(of, axg_aoclkc_match_table);
+MODULE_DEVICE_TABLE(of, axg_ao_clkc_match_table);
-static struct platform_driver axg_aoclkc_driver = {
+static struct platform_driver axg_ao_clkc_driver = {
.probe = meson_aoclkc_probe,
.driver = {
- .name = "axg-aoclkc",
- .of_match_table = axg_aoclkc_match_table,
+ .name = "axg-ao-clkc",
+ .of_match_table = axg_ao_clkc_match_table,
},
};
+module_platform_driver(axg_ao_clkc_driver);
-module_platform_driver(axg_aoclkc_driver);
-MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Amlogic AXG Always-ON Clock Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/axg-aoclk.h b/drivers/clk/meson/axg-aoclk.h
deleted file mode 100644
index 3cc27e85170f..000000000000
--- a/drivers/clk/meson/axg-aoclk.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
-/*
- * Copyright (c) 2017 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- *
- * Copyright (c) 2018 Amlogic, inc.
- * Author: Qiufang Dai <qiufang.dai@amlogic.com>
- */
-
-#ifndef __AXG_AOCLKC_H
-#define __AXG_AOCLKC_H
-
-#define NR_CLKS 17
-
-#include <dt-bindings/clock/axg-aoclkc.h>
-#include <dt-bindings/reset/axg-aoclkc.h>
-
-#endif /* __AXG_AOCLKC_H */
diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c
index 5016682e47c8..fd7eca652261 100644
--- a/drivers/clk/meson/axg-audio.c
+++ b/drivers/clk/meson/axg-audio.c
@@ -4,22 +4,79 @@
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
+#include <linux/auxiliary_bus.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
-#include <linux/of_device.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
-#include <linux/reset-controller.h>
#include <linux/slab.h>
-#include "axg-audio.h"
+#include "meson-clkc-utils.h"
#include "clk-regmap.h"
#include "clk-phase.h"
#include "sclk-div.h"
+#include <dt-bindings/clock/axg-audio-clkc.h>
+
+/* Audio clock register offsets */
+#define AUDIO_CLK_GATE_EN 0x000
+#define AUDIO_MCLK_A_CTRL 0x004
+#define AUDIO_MCLK_B_CTRL 0x008
+#define AUDIO_MCLK_C_CTRL 0x00C
+#define AUDIO_MCLK_D_CTRL 0x010
+#define AUDIO_MCLK_E_CTRL 0x014
+#define AUDIO_MCLK_F_CTRL 0x018
+#define AUDIO_MST_PAD_CTRL0 0x01c
+#define AUDIO_MST_PAD_CTRL1 0x020
+#define AUDIO_SW_RESET 0x024
+#define AUDIO_MST_A_SCLK_CTRL0 0x040
+#define AUDIO_MST_A_SCLK_CTRL1 0x044
+#define AUDIO_MST_B_SCLK_CTRL0 0x048
+#define AUDIO_MST_B_SCLK_CTRL1 0x04C
+#define AUDIO_MST_C_SCLK_CTRL0 0x050
+#define AUDIO_MST_C_SCLK_CTRL1 0x054
+#define AUDIO_MST_D_SCLK_CTRL0 0x058
+#define AUDIO_MST_D_SCLK_CTRL1 0x05C
+#define AUDIO_MST_E_SCLK_CTRL0 0x060
+#define AUDIO_MST_E_SCLK_CTRL1 0x064
+#define AUDIO_MST_F_SCLK_CTRL0 0x068
+#define AUDIO_MST_F_SCLK_CTRL1 0x06C
+#define AUDIO_CLK_TDMIN_A_CTRL 0x080
+#define AUDIO_CLK_TDMIN_B_CTRL 0x084
+#define AUDIO_CLK_TDMIN_C_CTRL 0x088
+#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
+#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
+#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
+#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
+#define AUDIO_CLK_SPDIFIN_CTRL 0x09C
+#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
+#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
+#define AUDIO_CLK_LOCKER_CTRL 0x0A8
+#define AUDIO_CLK_PDMIN_CTRL0 0x0AC
+#define AUDIO_CLK_PDMIN_CTRL1 0x0B0
+#define AUDIO_CLK_SPDIFOUT_B_CTRL 0x0B4
+
+/* SM1 introduce new register and some shifts :( */
+#define AUDIO_CLK_GATE_EN1 0x004
+#define AUDIO_SM1_MCLK_A_CTRL 0x008
+#define AUDIO_SM1_MCLK_B_CTRL 0x00C
+#define AUDIO_SM1_MCLK_C_CTRL 0x010
+#define AUDIO_SM1_MCLK_D_CTRL 0x014
+#define AUDIO_SM1_MCLK_E_CTRL 0x018
+#define AUDIO_SM1_MCLK_F_CTRL 0x01C
+#define AUDIO_SM1_MST_PAD_CTRL0 0x020
+#define AUDIO_SM1_MST_PAD_CTRL1 0x024
+#define AUDIO_SM1_SW_RESET0 0x028
+#define AUDIO_SM1_SW_RESET1 0x02C
+#define AUDIO_CLK81_CTRL 0x030
+#define AUDIO_CLK81_EN 0x034
+#define AUDIO_EARCRX_CMDC_CLK_CTRL 0x0D0
+#define AUDIO_EARCRX_DMAC_CLK_CTRL 0x0D4
+
#define AUD_GATE(_name, _reg, _bit, _pname, _iflags) { \
.data = &(struct clk_regmap_gate_data){ \
.offset = (_reg), \
@@ -750,6 +807,9 @@ static struct clk_regmap toddr_d =
AUD_PCLK_GATE(toddr_d, AUDIO_CLK_GATE_EN1, 1);
static struct clk_regmap loopback_b =
AUD_PCLK_GATE(loopback_b, AUDIO_CLK_GATE_EN1, 2);
+static struct clk_regmap earcrx =
+ AUD_PCLK_GATE(earcrx, AUDIO_CLK_GATE_EN1, 6);
+
static struct clk_regmap sm1_mst_a_mclk_sel =
AUD_MST_MCLK_MUX(mst_a_mclk, AUDIO_SM1_MCLK_A_CTRL);
@@ -763,6 +823,10 @@ static struct clk_regmap sm1_mst_e_mclk_sel =
AUD_MST_MCLK_MUX(mst_e_mclk, AUDIO_SM1_MCLK_E_CTRL);
static struct clk_regmap sm1_mst_f_mclk_sel =
AUD_MST_MCLK_MUX(mst_f_mclk, AUDIO_SM1_MCLK_F_CTRL);
+static struct clk_regmap sm1_earcrx_cmdc_clk_sel =
+ AUD_MST_MCLK_MUX(earcrx_cmdc_clk, AUDIO_EARCRX_CMDC_CLK_CTRL);
+static struct clk_regmap sm1_earcrx_dmac_clk_sel =
+ AUD_MST_MCLK_MUX(earcrx_dmac_clk, AUDIO_EARCRX_DMAC_CLK_CTRL);
static struct clk_regmap sm1_mst_a_mclk_div =
AUD_MST_MCLK_DIV(mst_a_mclk, AUDIO_SM1_MCLK_A_CTRL);
@@ -776,6 +840,11 @@ static struct clk_regmap sm1_mst_e_mclk_div =
AUD_MST_MCLK_DIV(mst_e_mclk, AUDIO_SM1_MCLK_E_CTRL);
static struct clk_regmap sm1_mst_f_mclk_div =
AUD_MST_MCLK_DIV(mst_f_mclk, AUDIO_SM1_MCLK_F_CTRL);
+static struct clk_regmap sm1_earcrx_cmdc_clk_div =
+ AUD_MST_MCLK_DIV(earcrx_cmdc_clk, AUDIO_EARCRX_CMDC_CLK_CTRL);
+static struct clk_regmap sm1_earcrx_dmac_clk_div =
+ AUD_MST_MCLK_DIV(earcrx_dmac_clk, AUDIO_EARCRX_DMAC_CLK_CTRL);
+
static struct clk_regmap sm1_mst_a_mclk =
AUD_MST_MCLK_GATE(mst_a_mclk, AUDIO_SM1_MCLK_A_CTRL);
@@ -789,6 +858,10 @@ static struct clk_regmap sm1_mst_e_mclk =
AUD_MST_MCLK_GATE(mst_e_mclk, AUDIO_SM1_MCLK_E_CTRL);
static struct clk_regmap sm1_mst_f_mclk =
AUD_MST_MCLK_GATE(mst_f_mclk, AUDIO_SM1_MCLK_F_CTRL);
+static struct clk_regmap sm1_earcrx_cmdc_clk =
+ AUD_MST_MCLK_GATE(earcrx_cmdc_clk, AUDIO_EARCRX_CMDC_CLK_CTRL);
+static struct clk_regmap sm1_earcrx_dmac_clk =
+ AUD_MST_MCLK_GATE(earcrx_dmac_clk, AUDIO_EARCRX_DMAC_CLK_CTRL);
static struct clk_regmap sm1_tdm_mclk_pad_0 = AUD_TDM_PAD_CTRL(
tdm_mclk_pad_0, AUDIO_SM1_MST_PAD_CTRL0, 0, mclk_pad_ctrl_parent_data);
@@ -811,950 +884,450 @@ static struct clk_regmap sm1_tdm_sclk_pad_2 = AUD_TDM_PAD_CTRL(
* Array of all clocks provided by this provider
* The input clocks of the controller will be populated at runtime
*/
-static struct clk_hw_onecell_data axg_audio_hw_onecell_data = {
- .hws = {
- [AUD_CLKID_DDR_ARB] = &ddr_arb.hw,
- [AUD_CLKID_PDM] = &pdm.hw,
- [AUD_CLKID_TDMIN_A] = &tdmin_a.hw,
- [AUD_CLKID_TDMIN_B] = &tdmin_b.hw,
- [AUD_CLKID_TDMIN_C] = &tdmin_c.hw,
- [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw,
- [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw,
- [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw,
- [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw,
- [AUD_CLKID_FRDDR_A] = &frddr_a.hw,
- [AUD_CLKID_FRDDR_B] = &frddr_b.hw,
- [AUD_CLKID_FRDDR_C] = &frddr_c.hw,
- [AUD_CLKID_TODDR_A] = &toddr_a.hw,
- [AUD_CLKID_TODDR_B] = &toddr_b.hw,
- [AUD_CLKID_TODDR_C] = &toddr_c.hw,
- [AUD_CLKID_LOOPBACK] = &loopback.hw,
- [AUD_CLKID_SPDIFIN] = &spdifin.hw,
- [AUD_CLKID_SPDIFOUT] = &spdifout.hw,
- [AUD_CLKID_RESAMPLE] = &resample.hw,
- [AUD_CLKID_POWER_DETECT] = &power_detect.hw,
- [AUD_CLKID_MST_A_MCLK_SEL] = &mst_a_mclk_sel.hw,
- [AUD_CLKID_MST_B_MCLK_SEL] = &mst_b_mclk_sel.hw,
- [AUD_CLKID_MST_C_MCLK_SEL] = &mst_c_mclk_sel.hw,
- [AUD_CLKID_MST_D_MCLK_SEL] = &mst_d_mclk_sel.hw,
- [AUD_CLKID_MST_E_MCLK_SEL] = &mst_e_mclk_sel.hw,
- [AUD_CLKID_MST_F_MCLK_SEL] = &mst_f_mclk_sel.hw,
- [AUD_CLKID_MST_A_MCLK_DIV] = &mst_a_mclk_div.hw,
- [AUD_CLKID_MST_B_MCLK_DIV] = &mst_b_mclk_div.hw,
- [AUD_CLKID_MST_C_MCLK_DIV] = &mst_c_mclk_div.hw,
- [AUD_CLKID_MST_D_MCLK_DIV] = &mst_d_mclk_div.hw,
- [AUD_CLKID_MST_E_MCLK_DIV] = &mst_e_mclk_div.hw,
- [AUD_CLKID_MST_F_MCLK_DIV] = &mst_f_mclk_div.hw,
- [AUD_CLKID_MST_A_MCLK] = &mst_a_mclk.hw,
- [AUD_CLKID_MST_B_MCLK] = &mst_b_mclk.hw,
- [AUD_CLKID_MST_C_MCLK] = &mst_c_mclk.hw,
- [AUD_CLKID_MST_D_MCLK] = &mst_d_mclk.hw,
- [AUD_CLKID_MST_E_MCLK] = &mst_e_mclk.hw,
- [AUD_CLKID_MST_F_MCLK] = &mst_f_mclk.hw,
- [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw,
- [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw,
- [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw,
- [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw,
- [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw,
- [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw,
- [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw,
- [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw,
- [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw,
- [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw,
- [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw,
- [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw,
- [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw,
- [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw,
- [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw,
- [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw,
- [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw,
- [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw,
- [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw,
- [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw,
- [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw,
- [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw,
- [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw,
- [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw,
- [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw,
- [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw,
- [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw,
- [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw,
- [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw,
- [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw,
- [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw,
- [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw,
- [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw,
- [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw,
- [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw,
- [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw,
- [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw,
- [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw,
- [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw,
- [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw,
- [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw,
- [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw,
- [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw,
- [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw,
- [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw,
- [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw,
- [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw,
- [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw,
- [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw,
- [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw,
- [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw,
- [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw,
- [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw,
- [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw,
- [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw,
- [AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw,
- [AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw,
- [AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw,
- [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw,
- [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw,
- [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw,
- [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw,
- [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw,
- [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw,
- [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw,
- [AUD_CLKID_TOP] = &axg_aud_top,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
+static struct clk_hw *axg_audio_hw_clks[] = {
+ [AUD_CLKID_DDR_ARB] = &ddr_arb.hw,
+ [AUD_CLKID_PDM] = &pdm.hw,
+ [AUD_CLKID_TDMIN_A] = &tdmin_a.hw,
+ [AUD_CLKID_TDMIN_B] = &tdmin_b.hw,
+ [AUD_CLKID_TDMIN_C] = &tdmin_c.hw,
+ [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw,
+ [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw,
+ [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw,
+ [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw,
+ [AUD_CLKID_FRDDR_A] = &frddr_a.hw,
+ [AUD_CLKID_FRDDR_B] = &frddr_b.hw,
+ [AUD_CLKID_FRDDR_C] = &frddr_c.hw,
+ [AUD_CLKID_TODDR_A] = &toddr_a.hw,
+ [AUD_CLKID_TODDR_B] = &toddr_b.hw,
+ [AUD_CLKID_TODDR_C] = &toddr_c.hw,
+ [AUD_CLKID_LOOPBACK] = &loopback.hw,
+ [AUD_CLKID_SPDIFIN] = &spdifin.hw,
+ [AUD_CLKID_SPDIFOUT] = &spdifout.hw,
+ [AUD_CLKID_RESAMPLE] = &resample.hw,
+ [AUD_CLKID_POWER_DETECT] = &power_detect.hw,
+ [AUD_CLKID_MST_A_MCLK_SEL] = &mst_a_mclk_sel.hw,
+ [AUD_CLKID_MST_B_MCLK_SEL] = &mst_b_mclk_sel.hw,
+ [AUD_CLKID_MST_C_MCLK_SEL] = &mst_c_mclk_sel.hw,
+ [AUD_CLKID_MST_D_MCLK_SEL] = &mst_d_mclk_sel.hw,
+ [AUD_CLKID_MST_E_MCLK_SEL] = &mst_e_mclk_sel.hw,
+ [AUD_CLKID_MST_F_MCLK_SEL] = &mst_f_mclk_sel.hw,
+ [AUD_CLKID_MST_A_MCLK_DIV] = &mst_a_mclk_div.hw,
+ [AUD_CLKID_MST_B_MCLK_DIV] = &mst_b_mclk_div.hw,
+ [AUD_CLKID_MST_C_MCLK_DIV] = &mst_c_mclk_div.hw,
+ [AUD_CLKID_MST_D_MCLK_DIV] = &mst_d_mclk_div.hw,
+ [AUD_CLKID_MST_E_MCLK_DIV] = &mst_e_mclk_div.hw,
+ [AUD_CLKID_MST_F_MCLK_DIV] = &mst_f_mclk_div.hw,
+ [AUD_CLKID_MST_A_MCLK] = &mst_a_mclk.hw,
+ [AUD_CLKID_MST_B_MCLK] = &mst_b_mclk.hw,
+ [AUD_CLKID_MST_C_MCLK] = &mst_c_mclk.hw,
+ [AUD_CLKID_MST_D_MCLK] = &mst_d_mclk.hw,
+ [AUD_CLKID_MST_E_MCLK] = &mst_e_mclk.hw,
+ [AUD_CLKID_MST_F_MCLK] = &mst_f_mclk.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw,
+ [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw,
+ [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw,
+ [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw,
+ [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw,
+ [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw,
+ [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw,
+ [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw,
+ [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw,
+ [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw,
+ [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw,
+ [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw,
+ [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw,
+ [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw,
+ [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw,
+ [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw,
+ [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw,
+ [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw,
+ [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw,
+ [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw,
+ [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw,
+ [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw,
+ [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw,
+ [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw,
+ [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw,
+ [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw,
+ [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw,
+ [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw,
+ [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw,
+ [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw,
+ [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw,
+ [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw,
+ [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw,
+ [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw,
+ [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw,
+ [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw,
+ [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw,
+ [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw,
+ [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw,
+ [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw,
+ [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw,
+ [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw,
+ [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw,
+ [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw,
+ [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw,
+ [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw,
+ [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw,
+ [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw,
+ [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw,
+ [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw,
+ [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw,
+ [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw,
+ [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw,
+ [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw,
+ [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw,
+ [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw,
+ [AUD_CLKID_TOP] = &axg_aud_top,
};
/*
* Array of all G12A clocks provided by this provider
* The input clocks of the controller will be populated at runtime
*/
-static struct clk_hw_onecell_data g12a_audio_hw_onecell_data = {
- .hws = {
- [AUD_CLKID_DDR_ARB] = &ddr_arb.hw,
- [AUD_CLKID_PDM] = &pdm.hw,
- [AUD_CLKID_TDMIN_A] = &tdmin_a.hw,
- [AUD_CLKID_TDMIN_B] = &tdmin_b.hw,
- [AUD_CLKID_TDMIN_C] = &tdmin_c.hw,
- [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw,
- [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw,
- [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw,
- [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw,
- [AUD_CLKID_FRDDR_A] = &frddr_a.hw,
- [AUD_CLKID_FRDDR_B] = &frddr_b.hw,
- [AUD_CLKID_FRDDR_C] = &frddr_c.hw,
- [AUD_CLKID_TODDR_A] = &toddr_a.hw,
- [AUD_CLKID_TODDR_B] = &toddr_b.hw,
- [AUD_CLKID_TODDR_C] = &toddr_c.hw,
- [AUD_CLKID_LOOPBACK] = &loopback.hw,
- [AUD_CLKID_SPDIFIN] = &spdifin.hw,
- [AUD_CLKID_SPDIFOUT] = &spdifout.hw,
- [AUD_CLKID_RESAMPLE] = &resample.hw,
- [AUD_CLKID_POWER_DETECT] = &power_detect.hw,
- [AUD_CLKID_SPDIFOUT_B] = &spdifout_b.hw,
- [AUD_CLKID_MST_A_MCLK_SEL] = &mst_a_mclk_sel.hw,
- [AUD_CLKID_MST_B_MCLK_SEL] = &mst_b_mclk_sel.hw,
- [AUD_CLKID_MST_C_MCLK_SEL] = &mst_c_mclk_sel.hw,
- [AUD_CLKID_MST_D_MCLK_SEL] = &mst_d_mclk_sel.hw,
- [AUD_CLKID_MST_E_MCLK_SEL] = &mst_e_mclk_sel.hw,
- [AUD_CLKID_MST_F_MCLK_SEL] = &mst_f_mclk_sel.hw,
- [AUD_CLKID_MST_A_MCLK_DIV] = &mst_a_mclk_div.hw,
- [AUD_CLKID_MST_B_MCLK_DIV] = &mst_b_mclk_div.hw,
- [AUD_CLKID_MST_C_MCLK_DIV] = &mst_c_mclk_div.hw,
- [AUD_CLKID_MST_D_MCLK_DIV] = &mst_d_mclk_div.hw,
- [AUD_CLKID_MST_E_MCLK_DIV] = &mst_e_mclk_div.hw,
- [AUD_CLKID_MST_F_MCLK_DIV] = &mst_f_mclk_div.hw,
- [AUD_CLKID_MST_A_MCLK] = &mst_a_mclk.hw,
- [AUD_CLKID_MST_B_MCLK] = &mst_b_mclk.hw,
- [AUD_CLKID_MST_C_MCLK] = &mst_c_mclk.hw,
- [AUD_CLKID_MST_D_MCLK] = &mst_d_mclk.hw,
- [AUD_CLKID_MST_E_MCLK] = &mst_e_mclk.hw,
- [AUD_CLKID_MST_F_MCLK] = &mst_f_mclk.hw,
- [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw,
- [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw,
- [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw,
- [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &spdifout_b_clk_sel.hw,
- [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &spdifout_b_clk_div.hw,
- [AUD_CLKID_SPDIFOUT_B_CLK] = &spdifout_b_clk.hw,
- [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw,
- [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw,
- [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw,
- [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw,
- [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw,
- [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw,
- [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw,
- [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw,
- [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw,
- [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw,
- [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw,
- [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw,
- [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw,
- [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw,
- [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw,
- [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw,
- [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw,
- [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw,
- [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw,
- [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw,
- [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw,
- [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw,
- [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw,
- [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw,
- [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw,
- [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw,
- [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw,
- [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw,
- [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw,
- [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw,
- [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw,
- [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw,
- [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw,
- [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw,
- [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw,
- [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw,
- [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw,
- [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw,
- [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw,
- [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw,
- [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw,
- [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw,
- [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw,
- [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw,
- [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw,
- [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw,
- [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw,
- [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw,
- [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw,
- [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw,
- [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw,
- [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw,
- [AUD_CLKID_TDMOUT_A_SCLK] = &g12a_tdmout_a_sclk.hw,
- [AUD_CLKID_TDMOUT_B_SCLK] = &g12a_tdmout_b_sclk.hw,
- [AUD_CLKID_TDMOUT_C_SCLK] = &g12a_tdmout_c_sclk.hw,
- [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw,
- [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw,
- [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw,
- [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw,
- [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw,
- [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw,
- [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw,
- [AUD_CLKID_TDM_MCLK_PAD0] = &g12a_tdm_mclk_pad_0.hw,
- [AUD_CLKID_TDM_MCLK_PAD1] = &g12a_tdm_mclk_pad_1.hw,
- [AUD_CLKID_TDM_LRCLK_PAD0] = &g12a_tdm_lrclk_pad_0.hw,
- [AUD_CLKID_TDM_LRCLK_PAD1] = &g12a_tdm_lrclk_pad_1.hw,
- [AUD_CLKID_TDM_LRCLK_PAD2] = &g12a_tdm_lrclk_pad_2.hw,
- [AUD_CLKID_TDM_SCLK_PAD0] = &g12a_tdm_sclk_pad_0.hw,
- [AUD_CLKID_TDM_SCLK_PAD1] = &g12a_tdm_sclk_pad_1.hw,
- [AUD_CLKID_TDM_SCLK_PAD2] = &g12a_tdm_sclk_pad_2.hw,
- [AUD_CLKID_TOP] = &axg_aud_top,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
+static struct clk_hw *g12a_audio_hw_clks[] = {
+ [AUD_CLKID_DDR_ARB] = &ddr_arb.hw,
+ [AUD_CLKID_PDM] = &pdm.hw,
+ [AUD_CLKID_TDMIN_A] = &tdmin_a.hw,
+ [AUD_CLKID_TDMIN_B] = &tdmin_b.hw,
+ [AUD_CLKID_TDMIN_C] = &tdmin_c.hw,
+ [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw,
+ [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw,
+ [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw,
+ [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw,
+ [AUD_CLKID_FRDDR_A] = &frddr_a.hw,
+ [AUD_CLKID_FRDDR_B] = &frddr_b.hw,
+ [AUD_CLKID_FRDDR_C] = &frddr_c.hw,
+ [AUD_CLKID_TODDR_A] = &toddr_a.hw,
+ [AUD_CLKID_TODDR_B] = &toddr_b.hw,
+ [AUD_CLKID_TODDR_C] = &toddr_c.hw,
+ [AUD_CLKID_LOOPBACK] = &loopback.hw,
+ [AUD_CLKID_SPDIFIN] = &spdifin.hw,
+ [AUD_CLKID_SPDIFOUT] = &spdifout.hw,
+ [AUD_CLKID_RESAMPLE] = &resample.hw,
+ [AUD_CLKID_POWER_DETECT] = &power_detect.hw,
+ [AUD_CLKID_SPDIFOUT_B] = &spdifout_b.hw,
+ [AUD_CLKID_MST_A_MCLK_SEL] = &mst_a_mclk_sel.hw,
+ [AUD_CLKID_MST_B_MCLK_SEL] = &mst_b_mclk_sel.hw,
+ [AUD_CLKID_MST_C_MCLK_SEL] = &mst_c_mclk_sel.hw,
+ [AUD_CLKID_MST_D_MCLK_SEL] = &mst_d_mclk_sel.hw,
+ [AUD_CLKID_MST_E_MCLK_SEL] = &mst_e_mclk_sel.hw,
+ [AUD_CLKID_MST_F_MCLK_SEL] = &mst_f_mclk_sel.hw,
+ [AUD_CLKID_MST_A_MCLK_DIV] = &mst_a_mclk_div.hw,
+ [AUD_CLKID_MST_B_MCLK_DIV] = &mst_b_mclk_div.hw,
+ [AUD_CLKID_MST_C_MCLK_DIV] = &mst_c_mclk_div.hw,
+ [AUD_CLKID_MST_D_MCLK_DIV] = &mst_d_mclk_div.hw,
+ [AUD_CLKID_MST_E_MCLK_DIV] = &mst_e_mclk_div.hw,
+ [AUD_CLKID_MST_F_MCLK_DIV] = &mst_f_mclk_div.hw,
+ [AUD_CLKID_MST_A_MCLK] = &mst_a_mclk.hw,
+ [AUD_CLKID_MST_B_MCLK] = &mst_b_mclk.hw,
+ [AUD_CLKID_MST_C_MCLK] = &mst_c_mclk.hw,
+ [AUD_CLKID_MST_D_MCLK] = &mst_d_mclk.hw,
+ [AUD_CLKID_MST_E_MCLK] = &mst_e_mclk.hw,
+ [AUD_CLKID_MST_F_MCLK] = &mst_f_mclk.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw,
+ [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw,
+ [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &spdifout_b_clk_sel.hw,
+ [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &spdifout_b_clk_div.hw,
+ [AUD_CLKID_SPDIFOUT_B_CLK] = &spdifout_b_clk.hw,
+ [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw,
+ [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw,
+ [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw,
+ [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw,
+ [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw,
+ [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw,
+ [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw,
+ [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw,
+ [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw,
+ [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw,
+ [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw,
+ [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw,
+ [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw,
+ [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw,
+ [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw,
+ [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw,
+ [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw,
+ [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw,
+ [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw,
+ [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw,
+ [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw,
+ [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw,
+ [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw,
+ [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw,
+ [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw,
+ [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw,
+ [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw,
+ [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw,
+ [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw,
+ [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw,
+ [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw,
+ [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw,
+ [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw,
+ [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw,
+ [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw,
+ [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw,
+ [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw,
+ [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw,
+ [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw,
+ [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw,
+ [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw,
+ [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw,
+ [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw,
+ [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw,
+ [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw,
+ [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw,
+ [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK] = &g12a_tdmout_a_sclk.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK] = &g12a_tdmout_b_sclk.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK] = &g12a_tdmout_c_sclk.hw,
+ [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw,
+ [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw,
+ [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw,
+ [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw,
+ [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw,
+ [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw,
+ [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw,
+ [AUD_CLKID_TDM_MCLK_PAD0] = &g12a_tdm_mclk_pad_0.hw,
+ [AUD_CLKID_TDM_MCLK_PAD1] = &g12a_tdm_mclk_pad_1.hw,
+ [AUD_CLKID_TDM_LRCLK_PAD0] = &g12a_tdm_lrclk_pad_0.hw,
+ [AUD_CLKID_TDM_LRCLK_PAD1] = &g12a_tdm_lrclk_pad_1.hw,
+ [AUD_CLKID_TDM_LRCLK_PAD2] = &g12a_tdm_lrclk_pad_2.hw,
+ [AUD_CLKID_TDM_SCLK_PAD0] = &g12a_tdm_sclk_pad_0.hw,
+ [AUD_CLKID_TDM_SCLK_PAD1] = &g12a_tdm_sclk_pad_1.hw,
+ [AUD_CLKID_TDM_SCLK_PAD2] = &g12a_tdm_sclk_pad_2.hw,
+ [AUD_CLKID_TOP] = &axg_aud_top,
};
/*
* Array of all SM1 clocks provided by this provider
* The input clocks of the controller will be populated at runtime
*/
-static struct clk_hw_onecell_data sm1_audio_hw_onecell_data = {
- .hws = {
- [AUD_CLKID_DDR_ARB] = &ddr_arb.hw,
- [AUD_CLKID_PDM] = &pdm.hw,
- [AUD_CLKID_TDMIN_A] = &tdmin_a.hw,
- [AUD_CLKID_TDMIN_B] = &tdmin_b.hw,
- [AUD_CLKID_TDMIN_C] = &tdmin_c.hw,
- [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw,
- [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw,
- [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw,
- [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw,
- [AUD_CLKID_FRDDR_A] = &frddr_a.hw,
- [AUD_CLKID_FRDDR_B] = &frddr_b.hw,
- [AUD_CLKID_FRDDR_C] = &frddr_c.hw,
- [AUD_CLKID_TODDR_A] = &toddr_a.hw,
- [AUD_CLKID_TODDR_B] = &toddr_b.hw,
- [AUD_CLKID_TODDR_C] = &toddr_c.hw,
- [AUD_CLKID_LOOPBACK] = &loopback.hw,
- [AUD_CLKID_SPDIFIN] = &spdifin.hw,
- [AUD_CLKID_SPDIFOUT] = &spdifout.hw,
- [AUD_CLKID_RESAMPLE] = &resample.hw,
- [AUD_CLKID_SPDIFOUT_B] = &spdifout_b.hw,
- [AUD_CLKID_MST_A_MCLK_SEL] = &sm1_mst_a_mclk_sel.hw,
- [AUD_CLKID_MST_B_MCLK_SEL] = &sm1_mst_b_mclk_sel.hw,
- [AUD_CLKID_MST_C_MCLK_SEL] = &sm1_mst_c_mclk_sel.hw,
- [AUD_CLKID_MST_D_MCLK_SEL] = &sm1_mst_d_mclk_sel.hw,
- [AUD_CLKID_MST_E_MCLK_SEL] = &sm1_mst_e_mclk_sel.hw,
- [AUD_CLKID_MST_F_MCLK_SEL] = &sm1_mst_f_mclk_sel.hw,
- [AUD_CLKID_MST_A_MCLK_DIV] = &sm1_mst_a_mclk_div.hw,
- [AUD_CLKID_MST_B_MCLK_DIV] = &sm1_mst_b_mclk_div.hw,
- [AUD_CLKID_MST_C_MCLK_DIV] = &sm1_mst_c_mclk_div.hw,
- [AUD_CLKID_MST_D_MCLK_DIV] = &sm1_mst_d_mclk_div.hw,
- [AUD_CLKID_MST_E_MCLK_DIV] = &sm1_mst_e_mclk_div.hw,
- [AUD_CLKID_MST_F_MCLK_DIV] = &sm1_mst_f_mclk_div.hw,
- [AUD_CLKID_MST_A_MCLK] = &sm1_mst_a_mclk.hw,
- [AUD_CLKID_MST_B_MCLK] = &sm1_mst_b_mclk.hw,
- [AUD_CLKID_MST_C_MCLK] = &sm1_mst_c_mclk.hw,
- [AUD_CLKID_MST_D_MCLK] = &sm1_mst_d_mclk.hw,
- [AUD_CLKID_MST_E_MCLK] = &sm1_mst_e_mclk.hw,
- [AUD_CLKID_MST_F_MCLK] = &sm1_mst_f_mclk.hw,
- [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw,
- [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw,
- [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw,
- [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &spdifout_b_clk_sel.hw,
- [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &spdifout_b_clk_div.hw,
- [AUD_CLKID_SPDIFOUT_B_CLK] = &spdifout_b_clk.hw,
- [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw,
- [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw,
- [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw,
- [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw,
- [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw,
- [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw,
- [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw,
- [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw,
- [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw,
- [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw,
- [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw,
- [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw,
- [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw,
- [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw,
- [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw,
- [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw,
- [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw,
- [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw,
- [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw,
- [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw,
- [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw,
- [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw,
- [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw,
- [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw,
- [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw,
- [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw,
- [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw,
- [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw,
- [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw,
- [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw,
- [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw,
- [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw,
- [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw,
- [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw,
- [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw,
- [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw,
- [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw,
- [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw,
- [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw,
- [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw,
- [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw,
- [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw,
- [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw,
- [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw,
- [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw,
- [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw,
- [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw,
- [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw,
- [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw,
- [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw,
- [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw,
- [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw,
- [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw,
- [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw,
- [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw,
- [AUD_CLKID_TDMOUT_A_SCLK] = &g12a_tdmout_a_sclk.hw,
- [AUD_CLKID_TDMOUT_B_SCLK] = &g12a_tdmout_b_sclk.hw,
- [AUD_CLKID_TDMOUT_C_SCLK] = &g12a_tdmout_c_sclk.hw,
- [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw,
- [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw,
- [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw,
- [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw,
- [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw,
- [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw,
- [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw,
- [AUD_CLKID_TDM_MCLK_PAD0] = &sm1_tdm_mclk_pad_0.hw,
- [AUD_CLKID_TDM_MCLK_PAD1] = &sm1_tdm_mclk_pad_1.hw,
- [AUD_CLKID_TDM_LRCLK_PAD0] = &sm1_tdm_lrclk_pad_0.hw,
- [AUD_CLKID_TDM_LRCLK_PAD1] = &sm1_tdm_lrclk_pad_1.hw,
- [AUD_CLKID_TDM_LRCLK_PAD2] = &sm1_tdm_lrclk_pad_2.hw,
- [AUD_CLKID_TDM_SCLK_PAD0] = &sm1_tdm_sclk_pad_0.hw,
- [AUD_CLKID_TDM_SCLK_PAD1] = &sm1_tdm_sclk_pad_1.hw,
- [AUD_CLKID_TDM_SCLK_PAD2] = &sm1_tdm_sclk_pad_2.hw,
- [AUD_CLKID_TOP] = &sm1_aud_top.hw,
- [AUD_CLKID_TORAM] = &toram.hw,
- [AUD_CLKID_EQDRC] = &eqdrc.hw,
- [AUD_CLKID_RESAMPLE_B] = &resample_b.hw,
- [AUD_CLKID_TOVAD] = &tovad.hw,
- [AUD_CLKID_LOCKER] = &locker.hw,
- [AUD_CLKID_SPDIFIN_LB] = &spdifin_lb.hw,
- [AUD_CLKID_FRDDR_D] = &frddr_d.hw,
- [AUD_CLKID_TODDR_D] = &toddr_d.hw,
- [AUD_CLKID_LOOPBACK_B] = &loopback_b.hw,
- [AUD_CLKID_CLK81_EN] = &sm1_clk81_en.hw,
- [AUD_CLKID_SYSCLK_A_DIV] = &sm1_sysclk_a_div.hw,
- [AUD_CLKID_SYSCLK_A_EN] = &sm1_sysclk_a_en.hw,
- [AUD_CLKID_SYSCLK_B_DIV] = &sm1_sysclk_b_div.hw,
- [AUD_CLKID_SYSCLK_B_EN] = &sm1_sysclk_b_en.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-
-/* Convenience table to populate regmap in .probe(). */
-static struct clk_regmap *const axg_clk_regmaps[] = {
- &ddr_arb,
- &pdm,
- &tdmin_a,
- &tdmin_b,
- &tdmin_c,
- &tdmin_lb,
- &tdmout_a,
- &tdmout_b,
- &tdmout_c,
- &frddr_a,
- &frddr_b,
- &frddr_c,
- &toddr_a,
- &toddr_b,
- &toddr_c,
- &loopback,
- &spdifin,
- &spdifout,
- &resample,
- &power_detect,
- &mst_a_mclk_sel,
- &mst_b_mclk_sel,
- &mst_c_mclk_sel,
- &mst_d_mclk_sel,
- &mst_e_mclk_sel,
- &mst_f_mclk_sel,
- &mst_a_mclk_div,
- &mst_b_mclk_div,
- &mst_c_mclk_div,
- &mst_d_mclk_div,
- &mst_e_mclk_div,
- &mst_f_mclk_div,
- &mst_a_mclk,
- &mst_b_mclk,
- &mst_c_mclk,
- &mst_d_mclk,
- &mst_e_mclk,
- &mst_f_mclk,
- &spdifout_clk_sel,
- &spdifout_clk_div,
- &spdifout_clk,
- &spdifin_clk_sel,
- &spdifin_clk_div,
- &spdifin_clk,
- &pdm_dclk_sel,
- &pdm_dclk_div,
- &pdm_dclk,
- &pdm_sysclk_sel,
- &pdm_sysclk_div,
- &pdm_sysclk,
- &mst_a_sclk_pre_en,
- &mst_b_sclk_pre_en,
- &mst_c_sclk_pre_en,
- &mst_d_sclk_pre_en,
- &mst_e_sclk_pre_en,
- &mst_f_sclk_pre_en,
- &mst_a_sclk_div,
- &mst_b_sclk_div,
- &mst_c_sclk_div,
- &mst_d_sclk_div,
- &mst_e_sclk_div,
- &mst_f_sclk_div,
- &mst_a_sclk_post_en,
- &mst_b_sclk_post_en,
- &mst_c_sclk_post_en,
- &mst_d_sclk_post_en,
- &mst_e_sclk_post_en,
- &mst_f_sclk_post_en,
- &mst_a_sclk,
- &mst_b_sclk,
- &mst_c_sclk,
- &mst_d_sclk,
- &mst_e_sclk,
- &mst_f_sclk,
- &mst_a_lrclk_div,
- &mst_b_lrclk_div,
- &mst_c_lrclk_div,
- &mst_d_lrclk_div,
- &mst_e_lrclk_div,
- &mst_f_lrclk_div,
- &mst_a_lrclk,
- &mst_b_lrclk,
- &mst_c_lrclk,
- &mst_d_lrclk,
- &mst_e_lrclk,
- &mst_f_lrclk,
- &tdmin_a_sclk_sel,
- &tdmin_b_sclk_sel,
- &tdmin_c_sclk_sel,
- &tdmin_lb_sclk_sel,
- &tdmout_a_sclk_sel,
- &tdmout_b_sclk_sel,
- &tdmout_c_sclk_sel,
- &tdmin_a_sclk_pre_en,
- &tdmin_b_sclk_pre_en,
- &tdmin_c_sclk_pre_en,
- &tdmin_lb_sclk_pre_en,
- &tdmout_a_sclk_pre_en,
- &tdmout_b_sclk_pre_en,
- &tdmout_c_sclk_pre_en,
- &tdmin_a_sclk_post_en,
- &tdmin_b_sclk_post_en,
- &tdmin_c_sclk_post_en,
- &tdmin_lb_sclk_post_en,
- &tdmout_a_sclk_post_en,
- &tdmout_b_sclk_post_en,
- &tdmout_c_sclk_post_en,
- &tdmin_a_sclk,
- &tdmin_b_sclk,
- &tdmin_c_sclk,
- &tdmin_lb_sclk,
- &axg_tdmout_a_sclk,
- &axg_tdmout_b_sclk,
- &axg_tdmout_c_sclk,
- &tdmin_a_lrclk,
- &tdmin_b_lrclk,
- &tdmin_c_lrclk,
- &tdmin_lb_lrclk,
- &tdmout_a_lrclk,
- &tdmout_b_lrclk,
- &tdmout_c_lrclk,
-};
-
-static struct clk_regmap *const g12a_clk_regmaps[] = {
- &ddr_arb,
- &pdm,
- &tdmin_a,
- &tdmin_b,
- &tdmin_c,
- &tdmin_lb,
- &tdmout_a,
- &tdmout_b,
- &tdmout_c,
- &frddr_a,
- &frddr_b,
- &frddr_c,
- &toddr_a,
- &toddr_b,
- &toddr_c,
- &loopback,
- &spdifin,
- &spdifout,
- &resample,
- &power_detect,
- &spdifout_b,
- &mst_a_mclk_sel,
- &mst_b_mclk_sel,
- &mst_c_mclk_sel,
- &mst_d_mclk_sel,
- &mst_e_mclk_sel,
- &mst_f_mclk_sel,
- &mst_a_mclk_div,
- &mst_b_mclk_div,
- &mst_c_mclk_div,
- &mst_d_mclk_div,
- &mst_e_mclk_div,
- &mst_f_mclk_div,
- &mst_a_mclk,
- &mst_b_mclk,
- &mst_c_mclk,
- &mst_d_mclk,
- &mst_e_mclk,
- &mst_f_mclk,
- &spdifout_clk_sel,
- &spdifout_clk_div,
- &spdifout_clk,
- &spdifin_clk_sel,
- &spdifin_clk_div,
- &spdifin_clk,
- &pdm_dclk_sel,
- &pdm_dclk_div,
- &pdm_dclk,
- &pdm_sysclk_sel,
- &pdm_sysclk_div,
- &pdm_sysclk,
- &mst_a_sclk_pre_en,
- &mst_b_sclk_pre_en,
- &mst_c_sclk_pre_en,
- &mst_d_sclk_pre_en,
- &mst_e_sclk_pre_en,
- &mst_f_sclk_pre_en,
- &mst_a_sclk_div,
- &mst_b_sclk_div,
- &mst_c_sclk_div,
- &mst_d_sclk_div,
- &mst_e_sclk_div,
- &mst_f_sclk_div,
- &mst_a_sclk_post_en,
- &mst_b_sclk_post_en,
- &mst_c_sclk_post_en,
- &mst_d_sclk_post_en,
- &mst_e_sclk_post_en,
- &mst_f_sclk_post_en,
- &mst_a_sclk,
- &mst_b_sclk,
- &mst_c_sclk,
- &mst_d_sclk,
- &mst_e_sclk,
- &mst_f_sclk,
- &mst_a_lrclk_div,
- &mst_b_lrclk_div,
- &mst_c_lrclk_div,
- &mst_d_lrclk_div,
- &mst_e_lrclk_div,
- &mst_f_lrclk_div,
- &mst_a_lrclk,
- &mst_b_lrclk,
- &mst_c_lrclk,
- &mst_d_lrclk,
- &mst_e_lrclk,
- &mst_f_lrclk,
- &tdmin_a_sclk_sel,
- &tdmin_b_sclk_sel,
- &tdmin_c_sclk_sel,
- &tdmin_lb_sclk_sel,
- &tdmout_a_sclk_sel,
- &tdmout_b_sclk_sel,
- &tdmout_c_sclk_sel,
- &tdmin_a_sclk_pre_en,
- &tdmin_b_sclk_pre_en,
- &tdmin_c_sclk_pre_en,
- &tdmin_lb_sclk_pre_en,
- &tdmout_a_sclk_pre_en,
- &tdmout_b_sclk_pre_en,
- &tdmout_c_sclk_pre_en,
- &tdmin_a_sclk_post_en,
- &tdmin_b_sclk_post_en,
- &tdmin_c_sclk_post_en,
- &tdmin_lb_sclk_post_en,
- &tdmout_a_sclk_post_en,
- &tdmout_b_sclk_post_en,
- &tdmout_c_sclk_post_en,
- &tdmin_a_sclk,
- &tdmin_b_sclk,
- &tdmin_c_sclk,
- &tdmin_lb_sclk,
- &g12a_tdmout_a_sclk,
- &g12a_tdmout_b_sclk,
- &g12a_tdmout_c_sclk,
- &tdmin_a_lrclk,
- &tdmin_b_lrclk,
- &tdmin_c_lrclk,
- &tdmin_lb_lrclk,
- &tdmout_a_lrclk,
- &tdmout_b_lrclk,
- &tdmout_c_lrclk,
- &spdifout_b_clk_sel,
- &spdifout_b_clk_div,
- &spdifout_b_clk,
- &g12a_tdm_mclk_pad_0,
- &g12a_tdm_mclk_pad_1,
- &g12a_tdm_lrclk_pad_0,
- &g12a_tdm_lrclk_pad_1,
- &g12a_tdm_lrclk_pad_2,
- &g12a_tdm_sclk_pad_0,
- &g12a_tdm_sclk_pad_1,
- &g12a_tdm_sclk_pad_2,
- &toram,
- &eqdrc,
-};
-
-static struct clk_regmap *const sm1_clk_regmaps[] = {
- &ddr_arb,
- &pdm,
- &tdmin_a,
- &tdmin_b,
- &tdmin_c,
- &tdmin_lb,
- &tdmout_a,
- &tdmout_b,
- &tdmout_c,
- &frddr_a,
- &frddr_b,
- &frddr_c,
- &toddr_a,
- &toddr_b,
- &toddr_c,
- &loopback,
- &spdifin,
- &spdifout,
- &resample,
- &spdifout_b,
- &sm1_mst_a_mclk_sel,
- &sm1_mst_b_mclk_sel,
- &sm1_mst_c_mclk_sel,
- &sm1_mst_d_mclk_sel,
- &sm1_mst_e_mclk_sel,
- &sm1_mst_f_mclk_sel,
- &sm1_mst_a_mclk_div,
- &sm1_mst_b_mclk_div,
- &sm1_mst_c_mclk_div,
- &sm1_mst_d_mclk_div,
- &sm1_mst_e_mclk_div,
- &sm1_mst_f_mclk_div,
- &sm1_mst_a_mclk,
- &sm1_mst_b_mclk,
- &sm1_mst_c_mclk,
- &sm1_mst_d_mclk,
- &sm1_mst_e_mclk,
- &sm1_mst_f_mclk,
- &spdifout_clk_sel,
- &spdifout_clk_div,
- &spdifout_clk,
- &spdifin_clk_sel,
- &spdifin_clk_div,
- &spdifin_clk,
- &pdm_dclk_sel,
- &pdm_dclk_div,
- &pdm_dclk,
- &pdm_sysclk_sel,
- &pdm_sysclk_div,
- &pdm_sysclk,
- &mst_a_sclk_pre_en,
- &mst_b_sclk_pre_en,
- &mst_c_sclk_pre_en,
- &mst_d_sclk_pre_en,
- &mst_e_sclk_pre_en,
- &mst_f_sclk_pre_en,
- &mst_a_sclk_div,
- &mst_b_sclk_div,
- &mst_c_sclk_div,
- &mst_d_sclk_div,
- &mst_e_sclk_div,
- &mst_f_sclk_div,
- &mst_a_sclk_post_en,
- &mst_b_sclk_post_en,
- &mst_c_sclk_post_en,
- &mst_d_sclk_post_en,
- &mst_e_sclk_post_en,
- &mst_f_sclk_post_en,
- &mst_a_sclk,
- &mst_b_sclk,
- &mst_c_sclk,
- &mst_d_sclk,
- &mst_e_sclk,
- &mst_f_sclk,
- &mst_a_lrclk_div,
- &mst_b_lrclk_div,
- &mst_c_lrclk_div,
- &mst_d_lrclk_div,
- &mst_e_lrclk_div,
- &mst_f_lrclk_div,
- &mst_a_lrclk,
- &mst_b_lrclk,
- &mst_c_lrclk,
- &mst_d_lrclk,
- &mst_e_lrclk,
- &mst_f_lrclk,
- &tdmin_a_sclk_sel,
- &tdmin_b_sclk_sel,
- &tdmin_c_sclk_sel,
- &tdmin_lb_sclk_sel,
- &tdmout_a_sclk_sel,
- &tdmout_b_sclk_sel,
- &tdmout_c_sclk_sel,
- &tdmin_a_sclk_pre_en,
- &tdmin_b_sclk_pre_en,
- &tdmin_c_sclk_pre_en,
- &tdmin_lb_sclk_pre_en,
- &tdmout_a_sclk_pre_en,
- &tdmout_b_sclk_pre_en,
- &tdmout_c_sclk_pre_en,
- &tdmin_a_sclk_post_en,
- &tdmin_b_sclk_post_en,
- &tdmin_c_sclk_post_en,
- &tdmin_lb_sclk_post_en,
- &tdmout_a_sclk_post_en,
- &tdmout_b_sclk_post_en,
- &tdmout_c_sclk_post_en,
- &tdmin_a_sclk,
- &tdmin_b_sclk,
- &tdmin_c_sclk,
- &tdmin_lb_sclk,
- &g12a_tdmout_a_sclk,
- &g12a_tdmout_b_sclk,
- &g12a_tdmout_c_sclk,
- &tdmin_a_lrclk,
- &tdmin_b_lrclk,
- &tdmin_c_lrclk,
- &tdmin_lb_lrclk,
- &tdmout_a_lrclk,
- &tdmout_b_lrclk,
- &tdmout_c_lrclk,
- &spdifout_b_clk_sel,
- &spdifout_b_clk_div,
- &spdifout_b_clk,
- &sm1_tdm_mclk_pad_0,
- &sm1_tdm_mclk_pad_1,
- &sm1_tdm_lrclk_pad_0,
- &sm1_tdm_lrclk_pad_1,
- &sm1_tdm_lrclk_pad_2,
- &sm1_tdm_sclk_pad_0,
- &sm1_tdm_sclk_pad_1,
- &sm1_tdm_sclk_pad_2,
- &sm1_aud_top,
- &toram,
- &eqdrc,
- &resample_b,
- &tovad,
- &locker,
- &spdifin_lb,
- &frddr_d,
- &toddr_d,
- &loopback_b,
- &sm1_clk81_en,
- &sm1_sysclk_a_div,
- &sm1_sysclk_a_en,
- &sm1_sysclk_b_div,
- &sm1_sysclk_b_en,
-};
-
-struct axg_audio_reset_data {
- struct reset_controller_dev rstc;
- struct regmap *map;
- unsigned int offset;
-};
-
-static void axg_audio_reset_reg_and_bit(struct axg_audio_reset_data *rst,
- unsigned long id,
- unsigned int *reg,
- unsigned int *bit)
-{
- unsigned int stride = regmap_get_reg_stride(rst->map);
-
- *reg = (id / (stride * BITS_PER_BYTE)) * stride;
- *reg += rst->offset;
- *bit = id % (stride * BITS_PER_BYTE);
-}
-
-static int axg_audio_reset_update(struct reset_controller_dev *rcdev,
- unsigned long id, bool assert)
-{
- struct axg_audio_reset_data *rst =
- container_of(rcdev, struct axg_audio_reset_data, rstc);
- unsigned int offset, bit;
-
- axg_audio_reset_reg_and_bit(rst, id, &offset, &bit);
-
- regmap_update_bits(rst->map, offset, BIT(bit),
- assert ? BIT(bit) : 0);
-
- return 0;
-}
-
-static int axg_audio_reset_status(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- struct axg_audio_reset_data *rst =
- container_of(rcdev, struct axg_audio_reset_data, rstc);
- unsigned int val, offset, bit;
-
- axg_audio_reset_reg_and_bit(rst, id, &offset, &bit);
-
- regmap_read(rst->map, offset, &val);
-
- return !!(val & BIT(bit));
-}
-
-static int axg_audio_reset_assert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return axg_audio_reset_update(rcdev, id, true);
-}
-
-static int axg_audio_reset_deassert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return axg_audio_reset_update(rcdev, id, false);
-}
-
-static int axg_audio_reset_toggle(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- int ret;
-
- ret = axg_audio_reset_assert(rcdev, id);
- if (ret)
- return ret;
-
- return axg_audio_reset_deassert(rcdev, id);
-}
-
-static const struct reset_control_ops axg_audio_rstc_ops = {
- .assert = axg_audio_reset_assert,
- .deassert = axg_audio_reset_deassert,
- .reset = axg_audio_reset_toggle,
- .status = axg_audio_reset_status,
+static struct clk_hw *sm1_audio_hw_clks[] = {
+ [AUD_CLKID_DDR_ARB] = &ddr_arb.hw,
+ [AUD_CLKID_PDM] = &pdm.hw,
+ [AUD_CLKID_TDMIN_A] = &tdmin_a.hw,
+ [AUD_CLKID_TDMIN_B] = &tdmin_b.hw,
+ [AUD_CLKID_TDMIN_C] = &tdmin_c.hw,
+ [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw,
+ [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw,
+ [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw,
+ [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw,
+ [AUD_CLKID_FRDDR_A] = &frddr_a.hw,
+ [AUD_CLKID_FRDDR_B] = &frddr_b.hw,
+ [AUD_CLKID_FRDDR_C] = &frddr_c.hw,
+ [AUD_CLKID_TODDR_A] = &toddr_a.hw,
+ [AUD_CLKID_TODDR_B] = &toddr_b.hw,
+ [AUD_CLKID_TODDR_C] = &toddr_c.hw,
+ [AUD_CLKID_LOOPBACK] = &loopback.hw,
+ [AUD_CLKID_SPDIFIN] = &spdifin.hw,
+ [AUD_CLKID_SPDIFOUT] = &spdifout.hw,
+ [AUD_CLKID_RESAMPLE] = &resample.hw,
+ [AUD_CLKID_SPDIFOUT_B] = &spdifout_b.hw,
+ [AUD_CLKID_MST_A_MCLK_SEL] = &sm1_mst_a_mclk_sel.hw,
+ [AUD_CLKID_MST_B_MCLK_SEL] = &sm1_mst_b_mclk_sel.hw,
+ [AUD_CLKID_MST_C_MCLK_SEL] = &sm1_mst_c_mclk_sel.hw,
+ [AUD_CLKID_MST_D_MCLK_SEL] = &sm1_mst_d_mclk_sel.hw,
+ [AUD_CLKID_MST_E_MCLK_SEL] = &sm1_mst_e_mclk_sel.hw,
+ [AUD_CLKID_MST_F_MCLK_SEL] = &sm1_mst_f_mclk_sel.hw,
+ [AUD_CLKID_MST_A_MCLK_DIV] = &sm1_mst_a_mclk_div.hw,
+ [AUD_CLKID_MST_B_MCLK_DIV] = &sm1_mst_b_mclk_div.hw,
+ [AUD_CLKID_MST_C_MCLK_DIV] = &sm1_mst_c_mclk_div.hw,
+ [AUD_CLKID_MST_D_MCLK_DIV] = &sm1_mst_d_mclk_div.hw,
+ [AUD_CLKID_MST_E_MCLK_DIV] = &sm1_mst_e_mclk_div.hw,
+ [AUD_CLKID_MST_F_MCLK_DIV] = &sm1_mst_f_mclk_div.hw,
+ [AUD_CLKID_MST_A_MCLK] = &sm1_mst_a_mclk.hw,
+ [AUD_CLKID_MST_B_MCLK] = &sm1_mst_b_mclk.hw,
+ [AUD_CLKID_MST_C_MCLK] = &sm1_mst_c_mclk.hw,
+ [AUD_CLKID_MST_D_MCLK] = &sm1_mst_d_mclk.hw,
+ [AUD_CLKID_MST_E_MCLK] = &sm1_mst_e_mclk.hw,
+ [AUD_CLKID_MST_F_MCLK] = &sm1_mst_f_mclk.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw,
+ [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw,
+ [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &spdifout_b_clk_sel.hw,
+ [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &spdifout_b_clk_div.hw,
+ [AUD_CLKID_SPDIFOUT_B_CLK] = &spdifout_b_clk.hw,
+ [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw,
+ [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw,
+ [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw,
+ [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw,
+ [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw,
+ [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw,
+ [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw,
+ [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw,
+ [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw,
+ [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw,
+ [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw,
+ [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw,
+ [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw,
+ [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw,
+ [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw,
+ [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw,
+ [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw,
+ [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw,
+ [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw,
+ [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw,
+ [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw,
+ [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw,
+ [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw,
+ [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw,
+ [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw,
+ [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw,
+ [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw,
+ [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw,
+ [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw,
+ [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw,
+ [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw,
+ [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw,
+ [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw,
+ [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw,
+ [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw,
+ [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw,
+ [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw,
+ [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw,
+ [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw,
+ [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw,
+ [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw,
+ [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw,
+ [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw,
+ [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw,
+ [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw,
+ [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw,
+ [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK] = &g12a_tdmout_a_sclk.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK] = &g12a_tdmout_b_sclk.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK] = &g12a_tdmout_c_sclk.hw,
+ [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw,
+ [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw,
+ [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw,
+ [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw,
+ [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw,
+ [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw,
+ [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw,
+ [AUD_CLKID_TDM_MCLK_PAD0] = &sm1_tdm_mclk_pad_0.hw,
+ [AUD_CLKID_TDM_MCLK_PAD1] = &sm1_tdm_mclk_pad_1.hw,
+ [AUD_CLKID_TDM_LRCLK_PAD0] = &sm1_tdm_lrclk_pad_0.hw,
+ [AUD_CLKID_TDM_LRCLK_PAD1] = &sm1_tdm_lrclk_pad_1.hw,
+ [AUD_CLKID_TDM_LRCLK_PAD2] = &sm1_tdm_lrclk_pad_2.hw,
+ [AUD_CLKID_TDM_SCLK_PAD0] = &sm1_tdm_sclk_pad_0.hw,
+ [AUD_CLKID_TDM_SCLK_PAD1] = &sm1_tdm_sclk_pad_1.hw,
+ [AUD_CLKID_TDM_SCLK_PAD2] = &sm1_tdm_sclk_pad_2.hw,
+ [AUD_CLKID_TOP] = &sm1_aud_top.hw,
+ [AUD_CLKID_TORAM] = &toram.hw,
+ [AUD_CLKID_EQDRC] = &eqdrc.hw,
+ [AUD_CLKID_RESAMPLE_B] = &resample_b.hw,
+ [AUD_CLKID_TOVAD] = &tovad.hw,
+ [AUD_CLKID_LOCKER] = &locker.hw,
+ [AUD_CLKID_SPDIFIN_LB] = &spdifin_lb.hw,
+ [AUD_CLKID_FRDDR_D] = &frddr_d.hw,
+ [AUD_CLKID_TODDR_D] = &toddr_d.hw,
+ [AUD_CLKID_LOOPBACK_B] = &loopback_b.hw,
+ [AUD_CLKID_CLK81_EN] = &sm1_clk81_en.hw,
+ [AUD_CLKID_SYSCLK_A_DIV] = &sm1_sysclk_a_div.hw,
+ [AUD_CLKID_SYSCLK_A_EN] = &sm1_sysclk_a_en.hw,
+ [AUD_CLKID_SYSCLK_B_DIV] = &sm1_sysclk_b_div.hw,
+ [AUD_CLKID_SYSCLK_B_EN] = &sm1_sysclk_b_en.hw,
+ [AUD_CLKID_EARCRX] = &earcrx.hw,
+ [AUD_CLKID_EARCRX_CMDC_SEL] = &sm1_earcrx_cmdc_clk_sel.hw,
+ [AUD_CLKID_EARCRX_CMDC_DIV] = &sm1_earcrx_cmdc_clk_div.hw,
+ [AUD_CLKID_EARCRX_CMDC] = &sm1_earcrx_cmdc_clk.hw,
+ [AUD_CLKID_EARCRX_DMAC_SEL] = &sm1_earcrx_dmac_clk_sel.hw,
+ [AUD_CLKID_EARCRX_DMAC_DIV] = &sm1_earcrx_dmac_clk_div.hw,
+ [AUD_CLKID_EARCRX_DMAC] = &sm1_earcrx_dmac_clk.hw,
};
-static const struct regmap_config axg_audio_regmap_cfg = {
+static struct regmap_config axg_audio_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
- .max_register = AUDIO_CLK_SPDIFOUT_B_CTRL,
};
struct audioclk_data {
- struct clk_regmap *const *regmap_clks;
- unsigned int regmap_clk_num;
- struct clk_hw_onecell_data *hw_onecell_data;
- unsigned int reset_offset;
- unsigned int reset_num;
+ struct meson_clk_hw_data hw_clks;
+ const char *rst_drvname;
+ unsigned int max_register;
};
static int axg_audio_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct audioclk_data *data;
- struct axg_audio_reset_data *rst;
+ struct auxiliary_device *auxdev;
struct regmap *map;
void __iomem *regs;
struct clk_hw *hw;
@@ -1769,6 +1342,7 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
if (IS_ERR(regs))
return PTR_ERR(regs);
+ axg_audio_regmap_cfg.max_register = data->max_register;
map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg);
if (IS_ERR(map)) {
dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map));
@@ -1786,15 +1360,11 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
return ret;
}
- /* Populate regmap for the regmap backed clocks */
- for (i = 0; i < data->regmap_clk_num; i++)
- data->regmap_clks[i]->map = map;
-
/* Take care to skip the registered input clocks */
- for (i = AUD_CLKID_DDR_ARB; i < data->hw_onecell_data->num; i++) {
+ for (i = AUD_CLKID_DDR_ARB; i < data->hw_clks.num; i++) {
const char *name;
- hw = data->hw_onecell_data->hws[i];
+ hw = data->hw_clks.hws[i];
/* array might be sparse */
if (!hw)
continue;
@@ -1808,49 +1378,45 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
}
}
- ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- data->hw_onecell_data);
+ ret = devm_of_clk_add_hw_provider(dev, meson_clk_hw_get, (void *)&data->hw_clks);
if (ret)
return ret;
- /* Stop here if there is no reset */
- if (!data->reset_num)
- return 0;
-
- rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
- if (!rst)
- return -ENOMEM;
-
- rst->map = map;
- rst->offset = data->reset_offset;
- rst->rstc.nr_resets = data->reset_num;
- rst->rstc.ops = &axg_audio_rstc_ops;
- rst->rstc.of_node = dev->of_node;
- rst->rstc.owner = THIS_MODULE;
+ /* Register auxiliary reset driver when applicable */
+ if (data->rst_drvname) {
+ auxdev = __devm_auxiliary_device_create(dev, dev->driver->name,
+ data->rst_drvname, NULL, 0);
+ if (!auxdev)
+ return -ENODEV;
+ }
- return devm_reset_controller_register(dev, &rst->rstc);
+ return 0;
}
static const struct audioclk_data axg_audioclk_data = {
- .regmap_clks = axg_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps),
- .hw_onecell_data = &axg_audio_hw_onecell_data,
+ .hw_clks = {
+ .hws = axg_audio_hw_clks,
+ .num = ARRAY_SIZE(axg_audio_hw_clks),
+ },
+ .max_register = AUDIO_CLK_PDMIN_CTRL1,
};
static const struct audioclk_data g12a_audioclk_data = {
- .regmap_clks = g12a_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
- .hw_onecell_data = &g12a_audio_hw_onecell_data,
- .reset_offset = AUDIO_SW_RESET,
- .reset_num = 26,
+ .hw_clks = {
+ .hws = g12a_audio_hw_clks,
+ .num = ARRAY_SIZE(g12a_audio_hw_clks),
+ },
+ .rst_drvname = "rst-g12a",
+ .max_register = AUDIO_CLK_SPDIFOUT_B_CTRL,
};
static const struct audioclk_data sm1_audioclk_data = {
- .regmap_clks = sm1_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(sm1_clk_regmaps),
- .hw_onecell_data = &sm1_audio_hw_onecell_data,
- .reset_offset = AUDIO_SM1_SW_RESET0,
- .reset_num = 39,
+ .hw_clks = {
+ .hws = sm1_audio_hw_clks,
+ .num = ARRAY_SIZE(sm1_audio_hw_clks),
+ },
+ .rst_drvname = "rst-sm1",
+ .max_register = AUDIO_EARCRX_DMAC_CLK_CTRL,
};
static const struct of_device_id clkc_match_table[] = {
@@ -1878,4 +1444,5 @@ module_platform_driver(axg_audio_driver);
MODULE_DESCRIPTION("Amlogic AXG/G12A/SM1 Audio Clock driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h
deleted file mode 100644
index fd65a7d0704b..000000000000
--- a/drivers/clk/meson/axg-audio.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
-/*
- * Copyright (c) 2018 BayLibre, SAS.
- * Author: Jerome Brunet <jbrunet@baylibre.com>
- */
-
-#ifndef __AXG_AUDIO_CLKC_H
-#define __AXG_AUDIO_CLKC_H
-
-/*
- * Audio Clock register offsets
- *
- * Register offsets from the datasheet must be multiplied by 4 before
- * to get the right offset
- */
-#define AUDIO_CLK_GATE_EN 0x000
-#define AUDIO_MCLK_A_CTRL 0x004
-#define AUDIO_MCLK_B_CTRL 0x008
-#define AUDIO_MCLK_C_CTRL 0x00C
-#define AUDIO_MCLK_D_CTRL 0x010
-#define AUDIO_MCLK_E_CTRL 0x014
-#define AUDIO_MCLK_F_CTRL 0x018
-#define AUDIO_MST_PAD_CTRL0 0x01c
-#define AUDIO_MST_PAD_CTRL1 0x020
-#define AUDIO_SW_RESET 0x024
-#define AUDIO_MST_A_SCLK_CTRL0 0x040
-#define AUDIO_MST_A_SCLK_CTRL1 0x044
-#define AUDIO_MST_B_SCLK_CTRL0 0x048
-#define AUDIO_MST_B_SCLK_CTRL1 0x04C
-#define AUDIO_MST_C_SCLK_CTRL0 0x050
-#define AUDIO_MST_C_SCLK_CTRL1 0x054
-#define AUDIO_MST_D_SCLK_CTRL0 0x058
-#define AUDIO_MST_D_SCLK_CTRL1 0x05C
-#define AUDIO_MST_E_SCLK_CTRL0 0x060
-#define AUDIO_MST_E_SCLK_CTRL1 0x064
-#define AUDIO_MST_F_SCLK_CTRL0 0x068
-#define AUDIO_MST_F_SCLK_CTRL1 0x06C
-#define AUDIO_CLK_TDMIN_A_CTRL 0x080
-#define AUDIO_CLK_TDMIN_B_CTRL 0x084
-#define AUDIO_CLK_TDMIN_C_CTRL 0x088
-#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
-#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
-#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
-#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
-#define AUDIO_CLK_SPDIFIN_CTRL 0x09C
-#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
-#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
-#define AUDIO_CLK_LOCKER_CTRL 0x0A8
-#define AUDIO_CLK_PDMIN_CTRL0 0x0AC
-#define AUDIO_CLK_PDMIN_CTRL1 0x0B0
-#define AUDIO_CLK_SPDIFOUT_B_CTRL 0x0B4
-
-/* SM1 introduce new register and some shifts :( */
-#define AUDIO_CLK_GATE_EN1 0x004
-#define AUDIO_SM1_MCLK_A_CTRL 0x008
-#define AUDIO_SM1_MCLK_B_CTRL 0x00C
-#define AUDIO_SM1_MCLK_C_CTRL 0x010
-#define AUDIO_SM1_MCLK_D_CTRL 0x014
-#define AUDIO_SM1_MCLK_E_CTRL 0x018
-#define AUDIO_SM1_MCLK_F_CTRL 0x01C
-#define AUDIO_SM1_MST_PAD_CTRL0 0x020
-#define AUDIO_SM1_MST_PAD_CTRL1 0x024
-#define AUDIO_SM1_SW_RESET0 0x028
-#define AUDIO_SM1_SW_RESET1 0x02C
-#define AUDIO_CLK81_CTRL 0x030
-#define AUDIO_CLK81_EN 0x034
-/*
- * CLKID index values
- * These indices are entirely contrived and do not map onto the hardware.
- */
-
-#define AUD_CLKID_MST_A_MCLK_SEL 59
-#define AUD_CLKID_MST_B_MCLK_SEL 60
-#define AUD_CLKID_MST_C_MCLK_SEL 61
-#define AUD_CLKID_MST_D_MCLK_SEL 62
-#define AUD_CLKID_MST_E_MCLK_SEL 63
-#define AUD_CLKID_MST_F_MCLK_SEL 64
-#define AUD_CLKID_MST_A_MCLK_DIV 65
-#define AUD_CLKID_MST_B_MCLK_DIV 66
-#define AUD_CLKID_MST_C_MCLK_DIV 67
-#define AUD_CLKID_MST_D_MCLK_DIV 68
-#define AUD_CLKID_MST_E_MCLK_DIV 69
-#define AUD_CLKID_MST_F_MCLK_DIV 70
-#define AUD_CLKID_SPDIFOUT_CLK_SEL 71
-#define AUD_CLKID_SPDIFOUT_CLK_DIV 72
-#define AUD_CLKID_SPDIFIN_CLK_SEL 73
-#define AUD_CLKID_SPDIFIN_CLK_DIV 74
-#define AUD_CLKID_PDM_DCLK_SEL 75
-#define AUD_CLKID_PDM_DCLK_DIV 76
-#define AUD_CLKID_PDM_SYSCLK_SEL 77
-#define AUD_CLKID_PDM_SYSCLK_DIV 78
-#define AUD_CLKID_MST_A_SCLK_PRE_EN 92
-#define AUD_CLKID_MST_B_SCLK_PRE_EN 93
-#define AUD_CLKID_MST_C_SCLK_PRE_EN 94
-#define AUD_CLKID_MST_D_SCLK_PRE_EN 95
-#define AUD_CLKID_MST_E_SCLK_PRE_EN 96
-#define AUD_CLKID_MST_F_SCLK_PRE_EN 97
-#define AUD_CLKID_MST_A_SCLK_DIV 98
-#define AUD_CLKID_MST_B_SCLK_DIV 99
-#define AUD_CLKID_MST_C_SCLK_DIV 100
-#define AUD_CLKID_MST_D_SCLK_DIV 101
-#define AUD_CLKID_MST_E_SCLK_DIV 102
-#define AUD_CLKID_MST_F_SCLK_DIV 103
-#define AUD_CLKID_MST_A_SCLK_POST_EN 104
-#define AUD_CLKID_MST_B_SCLK_POST_EN 105
-#define AUD_CLKID_MST_C_SCLK_POST_EN 106
-#define AUD_CLKID_MST_D_SCLK_POST_EN 107
-#define AUD_CLKID_MST_E_SCLK_POST_EN 108
-#define AUD_CLKID_MST_F_SCLK_POST_EN 109
-#define AUD_CLKID_MST_A_LRCLK_DIV 110
-#define AUD_CLKID_MST_B_LRCLK_DIV 111
-#define AUD_CLKID_MST_C_LRCLK_DIV 112
-#define AUD_CLKID_MST_D_LRCLK_DIV 113
-#define AUD_CLKID_MST_E_LRCLK_DIV 114
-#define AUD_CLKID_MST_F_LRCLK_DIV 115
-#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 137
-#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 138
-#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN 139
-#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 140
-#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 141
-#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 142
-#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN 143
-#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 144
-#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 145
-#define AUD_CLKID_TDMIN_C_SCLK_POST_EN 146
-#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 147
-#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 148
-#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 149
-#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150
-#define AUD_CLKID_SPDIFOUT_B_CLK_SEL 153
-#define AUD_CLKID_SPDIFOUT_B_CLK_DIV 154
-#define AUD_CLKID_CLK81_EN 173
-#define AUD_CLKID_SYSCLK_A_DIV 174
-#define AUD_CLKID_SYSCLK_B_DIV 175
-#define AUD_CLKID_SYSCLK_A_EN 176
-#define AUD_CLKID_SYSCLK_B_EN 177
-
-/* include the CLKIDs which are part of the DT bindings */
-#include <dt-bindings/clock/axg-audio-clkc.h>
-
-#define NR_CLKS 178
-
-#endif /*__AXG_AUDIO_CLKC_H */
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index 2ad3801398dc..0a25c649ef1d 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -11,17 +11,102 @@
#include <linux/clk-provider.h>
#include <linux/init.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-mpll.h"
-#include "axg.h"
-#include "meson-eeclk.h"
-
-static DEFINE_SPINLOCK(meson_clk_lock);
+#include "meson-clkc-utils.h"
+
+#include <dt-bindings/clock/axg-clkc.h>
+
+#define HHI_GP0_PLL_CNTL 0x40
+#define HHI_GP0_PLL_CNTL2 0x44
+#define HHI_GP0_PLL_CNTL3 0x48
+#define HHI_GP0_PLL_CNTL4 0x4c
+#define HHI_GP0_PLL_CNTL5 0x50
+#define HHI_GP0_PLL_STS 0x54
+#define HHI_GP0_PLL_CNTL1 0x58
+#define HHI_HIFI_PLL_CNTL 0x80
+#define HHI_HIFI_PLL_CNTL2 0x84
+#define HHI_HIFI_PLL_CNTL3 0x88
+#define HHI_HIFI_PLL_CNTL4 0x8C
+#define HHI_HIFI_PLL_CNTL5 0x90
+#define HHI_HIFI_PLL_STS 0x94
+#define HHI_HIFI_PLL_CNTL1 0x98
+
+#define HHI_XTAL_DIVN_CNTL 0xbc
+#define HHI_GCLK2_MPEG0 0xc0
+#define HHI_GCLK2_MPEG1 0xc4
+#define HHI_GCLK2_MPEG2 0xc8
+#define HHI_GCLK2_OTHER 0xd0
+#define HHI_GCLK2_AO 0xd4
+#define HHI_PCIE_PLL_CNTL 0xd8
+#define HHI_PCIE_PLL_CNTL1 0xdC
+#define HHI_PCIE_PLL_CNTL2 0xe0
+#define HHI_PCIE_PLL_CNTL3 0xe4
+#define HHI_PCIE_PLL_CNTL4 0xe8
+#define HHI_PCIE_PLL_CNTL5 0xec
+#define HHI_PCIE_PLL_CNTL6 0xf0
+#define HHI_PCIE_PLL_STS 0xf4
+
+#define HHI_MEM_PD_REG0 0x100
+#define HHI_VPU_MEM_PD_REG0 0x104
+#define HHI_VIID_CLK_DIV 0x128
+#define HHI_VIID_CLK_CNTL 0x12c
+
+#define HHI_GCLK_MPEG0 0x140
+#define HHI_GCLK_MPEG1 0x144
+#define HHI_GCLK_MPEG2 0x148
+#define HHI_GCLK_OTHER 0x150
+#define HHI_GCLK_AO 0x154
+#define HHI_SYS_CPU_CLK_CNTL1 0x15c
+#define HHI_SYS_CPU_RESET_CNTL 0x160
+#define HHI_VID_CLK_DIV 0x164
+#define HHI_SPICC_HCLK_CNTL 0x168
+
+#define HHI_MPEG_CLK_CNTL 0x174
+#define HHI_VID_CLK_CNTL 0x17c
+#define HHI_TS_CLK_CNTL 0x190
+#define HHI_VID_CLK_CNTL2 0x194
+#define HHI_SYS_CPU_CLK_CNTL0 0x19c
+#define HHI_VID_PLL_CLK_DIV 0x1a0
+#define HHI_VPU_CLK_CNTL 0x1bC
+
+#define HHI_VAPBCLK_CNTL 0x1F4
+
+#define HHI_GEN_CLK_CNTL 0x228
+
+#define HHI_VDIN_MEAS_CLK_CNTL 0x250
+#define HHI_NAND_CLK_CNTL 0x25C
+#define HHI_SD_EMMC_CLK_CNTL 0x264
+
+#define HHI_MPLL_CNTL 0x280
+#define HHI_MPLL_CNTL2 0x284
+#define HHI_MPLL_CNTL3 0x288
+#define HHI_MPLL_CNTL4 0x28C
+#define HHI_MPLL_CNTL5 0x290
+#define HHI_MPLL_CNTL6 0x294
+#define HHI_MPLL_CNTL7 0x298
+#define HHI_MPLL_CNTL8 0x29C
+#define HHI_MPLL_CNTL9 0x2A0
+#define HHI_MPLL_CNTL10 0x2A4
+
+#define HHI_MPLL3_CNTL0 0x2E0
+#define HHI_MPLL3_CNTL1 0x2E4
+#define HHI_PLL_TOP_MISC 0x2E8
+
+#define HHI_SYS_PLL_CNTL1 0x2FC
+#define HHI_SYS_PLL_CNTL 0x300
+#define HHI_SYS_PLL_CNTL2 0x304
+#define HHI_SYS_PLL_CNTL3 0x308
+#define HHI_SYS_PLL_CNTL4 0x30c
+#define HHI_SYS_PLL_CNTL5 0x310
+#define HHI_SYS_PLL_STS 0x314
+#define HHI_DPLL_TOP_I 0x318
+#define HHI_DPLL_TOP2_I 0x31C
static struct clk_regmap axg_fixed_pll_dco = {
.data = &(struct meson_clk_pll_data){
@@ -248,7 +333,7 @@ static struct clk_regmap axg_gp0_pll = {
},
};
-static const struct reg_sequence axg_hifi_init_regs[] = {
+static const struct reg_sequence axg_hifi_pll_init_regs[] = {
{ .reg = HHI_HIFI_PLL_CNTL1, .def = 0xc084b000 },
{ .reg = HHI_HIFI_PLL_CNTL2, .def = 0xb75020be },
{ .reg = HHI_HIFI_PLL_CNTL3, .def = 0x0a6a3a88 },
@@ -289,8 +374,8 @@ static struct clk_regmap axg_hifi_pll_dco = {
.width = 1,
},
.table = axg_gp0_pll_params_table,
- .init_regs = axg_hifi_init_regs,
- .init_count = ARRAY_SIZE(axg_hifi_init_regs),
+ .init_regs = axg_hifi_pll_init_regs,
+ .init_count = ARRAY_SIZE(axg_hifi_pll_init_regs),
.flags = CLK_MESON_PLL_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
@@ -504,7 +589,6 @@ static struct clk_regmap axg_mpll0_div = {
.shift = 0,
.width = 1,
},
- .lock = &meson_clk_lock,
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
@@ -555,7 +639,6 @@ static struct clk_regmap axg_mpll1_div = {
.shift = 1,
.width = 1,
},
- .lock = &meson_clk_lock,
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
@@ -611,7 +694,6 @@ static struct clk_regmap axg_mpll2_div = {
.shift = 2,
.width = 1,
},
- .lock = &meson_clk_lock,
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
@@ -662,7 +744,6 @@ static struct clk_regmap axg_mpll3_div = {
.shift = 3,
.width = 1,
},
- .lock = &meson_clk_lock,
.flags = CLK_MESON_MPLL_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
@@ -699,7 +780,7 @@ static const struct pll_params_table axg_pcie_pll_params_table[] = {
{ /* sentinel */ },
};
-static const struct reg_sequence axg_pcie_init_regs[] = {
+static const struct reg_sequence axg_pcie_pll_init_regs[] = {
{ .reg = HHI_PCIE_PLL_CNTL1, .def = 0x0084a2aa },
{ .reg = HHI_PCIE_PLL_CNTL2, .def = 0xb75020be },
{ .reg = HHI_PCIE_PLL_CNTL3, .def = 0x0a47488e },
@@ -742,8 +823,8 @@ static struct clk_regmap axg_pcie_pll_dco = {
.width = 1,
},
.table = axg_pcie_pll_params_table,
- .init_regs = axg_pcie_init_regs,
- .init_count = ARRAY_SIZE(axg_pcie_init_regs),
+ .init_regs = axg_pcie_pll_init_regs,
+ .init_count = ARRAY_SIZE(axg_pcie_pll_init_regs),
},
.hw.init = &(struct clk_init_data){
.name = "pcie_pll_dco",
@@ -854,8 +935,9 @@ static struct clk_regmap axg_pcie_cml_en1 = {
},
};
-static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
-static const struct clk_parent_data clk81_parent_data[] = {
+/* clk81 is often referred as "mpeg_clk" */
+static u32 clk81_parents_val_table[] = { 0, 2, 3, 4, 5, 6, 7 };
+static const struct clk_parent_data clk81_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &axg_fclk_div7.hw },
{ .hw = &axg_mpll1.hw },
@@ -865,32 +947,32 @@ static const struct clk_parent_data clk81_parent_data[] = {
{ .hw = &axg_fclk_div5.hw },
};
-static struct clk_regmap axg_mpeg_clk_sel = {
+static struct clk_regmap axg_clk81_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MPEG_CLK_CNTL,
.mask = 0x7,
.shift = 12,
- .table = mux_table_clk81,
+ .table = clk81_parents_val_table,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_sel",
+ .name = "clk81_sel",
.ops = &clk_regmap_mux_ro_ops,
- .parent_data = clk81_parent_data,
- .num_parents = ARRAY_SIZE(clk81_parent_data),
+ .parent_data = clk81_parents,
+ .num_parents = ARRAY_SIZE(clk81_parents),
},
};
-static struct clk_regmap axg_mpeg_clk_div = {
+static struct clk_regmap axg_clk81_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_MPEG_CLK_CNTL,
.shift = 0,
.width = 7,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_div",
+ .name = "clk81_div",
.ops = &clk_regmap_divider_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_mpeg_clk_sel.hw
+ &axg_clk81_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -906,14 +988,14 @@ static struct clk_regmap axg_clk81 = {
.name = "clk81",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &axg_mpeg_clk_div.hw
+ &axg_clk81_div.hw
},
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
},
};
-static const struct clk_parent_data axg_sd_emmc_clk0_parent_data[] = {
+static const struct clk_parent_data axg_sd_emmc_clk0_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &axg_fclk_div2.hw },
{ .hw = &axg_fclk_div3.hw },
@@ -922,7 +1004,7 @@ static const struct clk_parent_data axg_sd_emmc_clk0_parent_data[] = {
/*
* Following these parent clocks, we should also have had mpll2, mpll3
* and gp0_pll but these clocks are too precious to be used here. All
- * the necessary rates for MMC and NAND operation can be acheived using
+ * the necessary rates for MMC and NAND operation can be achieved using
* xtal or fclk_div clocks
*/
};
@@ -937,8 +1019,8 @@ static struct clk_regmap axg_sd_emmc_b_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = axg_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(axg_sd_emmc_clk0_parent_data),
+ .parent_data = axg_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(axg_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -987,8 +1069,8 @@ static struct clk_regmap axg_sd_emmc_c_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = axg_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(axg_sd_emmc_clk0_parent_data),
+ .parent_data = axg_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(axg_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1029,7 +1111,7 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = {
/* VPU Clock */
-static const struct clk_hw *axg_vpu_parent_hws[] = {
+static const struct clk_hw *axg_vpu_parents[] = {
&axg_fclk_div4.hw,
&axg_fclk_div3.hw,
&axg_fclk_div5.hw,
@@ -1045,8 +1127,8 @@ static struct clk_regmap axg_vpu_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(axg_vpu_parent_hws),
+ .parent_hws = axg_vpu_parents,
+ .num_parents = ARRAY_SIZE(axg_vpu_parents),
/* We need a specific parent for VPU clock source, let it be set in DT */
.flags = CLK_SET_RATE_NO_REPARENT,
},
@@ -1094,8 +1176,8 @@ static struct clk_regmap axg_vpu_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(axg_vpu_parent_hws),
+ .parent_hws = axg_vpu_parents,
+ .num_parents = ARRAY_SIZE(axg_vpu_parents),
/* We need a specific parent for VPU clock source, let it be set in DT */
.flags = CLK_SET_RATE_NO_REPARENT,
},
@@ -1163,8 +1245,8 @@ static struct clk_regmap axg_vapb_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "vapb_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(axg_vpu_parent_hws),
+ .parent_hws = axg_vpu_parents,
+ .num_parents = ARRAY_SIZE(axg_vpu_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1211,8 +1293,8 @@ static struct clk_regmap axg_vapb_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vapb_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(axg_vpu_parent_hws),
+ .parent_hws = axg_vpu_parents,
+ .num_parents = ARRAY_SIZE(axg_vpu_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1284,7 +1366,7 @@ static struct clk_regmap axg_vapb = {
/* Video Clocks */
-static const struct clk_hw *axg_vclk_parent_hws[] = {
+static const struct clk_hw *axg_vclk_parents[] = {
&axg_gp0_pll.hw,
&axg_fclk_div4.hw,
&axg_fclk_div3.hw,
@@ -1303,8 +1385,8 @@ static struct clk_regmap axg_vclk_sel = {
.hw.init = &(struct clk_init_data){
.name = "vclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_vclk_parent_hws,
- .num_parents = ARRAY_SIZE(axg_vclk_parent_hws),
+ .parent_hws = axg_vclk_parents,
+ .num_parents = ARRAY_SIZE(axg_vclk_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -1318,8 +1400,8 @@ static struct clk_regmap axg_vclk2_sel = {
.hw.init = &(struct clk_init_data){
.name = "vclk2_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_vclk_parent_hws,
- .num_parents = ARRAY_SIZE(axg_vclk_parent_hws),
+ .parent_hws = axg_vclk_parents,
+ .num_parents = ARRAY_SIZE(axg_vclk_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -1658,8 +1740,8 @@ static struct clk_fixed_factor axg_vclk2_div12 = {
},
};
-static u32 mux_table_cts_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
-static const struct clk_hw *axg_cts_parent_hws[] = {
+static u32 axg_cts_encl_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *axg_cts_encl_parents[] = {
&axg_vclk_div1.hw,
&axg_vclk_div2.hw,
&axg_vclk_div4.hw,
@@ -1677,13 +1759,13 @@ static struct clk_regmap axg_cts_encl_sel = {
.offset = HHI_VIID_CLK_DIV,
.mask = 0xf,
.shift = 12,
- .table = mux_table_cts_sel,
+ .table = axg_cts_encl_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_encl_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = axg_cts_parent_hws,
- .num_parents = ARRAY_SIZE(axg_cts_parent_hws),
+ .parent_hws = axg_cts_encl_parents,
+ .num_parents = ARRAY_SIZE(axg_cts_encl_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -1706,8 +1788,8 @@ static struct clk_regmap axg_cts_encl = {
/* MIPI DSI Host Clock */
-static u32 mux_table_axg_vdin_meas[] = { 0, 1, 2, 3, 6, 7 };
-static const struct clk_parent_data axg_vdin_meas_parent_data[] = {
+static u32 axg_vdin_meas_parents_val_table[] = { 0, 1, 2, 3, 6, 7 };
+static const struct clk_parent_data axg_vdin_meas_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &axg_fclk_div4.hw },
{ .hw = &axg_fclk_div3.hw },
@@ -1722,13 +1804,13 @@ static struct clk_regmap axg_vdin_meas_sel = {
.mask = 0x7,
.shift = 21,
.flags = CLK_MUX_ROUND_CLOSEST,
- .table = mux_table_axg_vdin_meas,
+ .table = axg_vdin_meas_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "vdin_meas_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = axg_vdin_meas_parent_data,
- .num_parents = ARRAY_SIZE(axg_vdin_meas_parent_data),
+ .parent_data = axg_vdin_meas_parents,
+ .num_parents = ARRAY_SIZE(axg_vdin_meas_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1764,9 +1846,8 @@ static struct clk_regmap axg_vdin_meas = {
},
};
-static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
- 9, 10, 11, 13, 14, };
-static const struct clk_parent_data gen_clk_parent_data[] = {
+static u32 gen_clk_parents_val_table[] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, };
+static const struct clk_parent_data gen_clk_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &axg_hifi_pll.hw },
{ .hw = &axg_mpll0.hw },
@@ -1785,7 +1866,7 @@ static struct clk_regmap axg_gen_clk_sel = {
.offset = HHI_GEN_CLK_CNTL,
.mask = 0xf,
.shift = 12,
- .table = mux_table_gen_clk,
+ .table = gen_clk_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk_sel",
@@ -1796,8 +1877,8 @@ static struct clk_regmap axg_gen_clk_sel = {
* hifi_pll, mpll0, mpll1, mpll2, mpll3, fdiv4,
* fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll
*/
- .parent_data = gen_clk_parent_data,
- .num_parents = ARRAY_SIZE(gen_clk_parent_data),
+ .parent_data = gen_clk_parents,
+ .num_parents = ARRAY_SIZE(gen_clk_parents),
},
};
@@ -1834,352 +1915,235 @@ static struct clk_regmap axg_gen_clk = {
},
};
-#define MESON_GATE(_name, _reg, _bit) \
- MESON_PCLK(_name, _reg, _bit, &axg_clk81.hw)
-
-/* Everything Else (EE) domain gates */
-static MESON_GATE(axg_ddr, HHI_GCLK_MPEG0, 0);
-static MESON_GATE(axg_audio_locker, HHI_GCLK_MPEG0, 2);
-static MESON_GATE(axg_mipi_dsi_host, HHI_GCLK_MPEG0, 3);
-static MESON_GATE(axg_isa, HHI_GCLK_MPEG0, 5);
-static MESON_GATE(axg_pl301, HHI_GCLK_MPEG0, 6);
-static MESON_GATE(axg_periphs, HHI_GCLK_MPEG0, 7);
-static MESON_GATE(axg_spicc_0, HHI_GCLK_MPEG0, 8);
-static MESON_GATE(axg_i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(axg_rng0, HHI_GCLK_MPEG0, 12);
-static MESON_GATE(axg_uart0, HHI_GCLK_MPEG0, 13);
-static MESON_GATE(axg_mipi_dsi_phy, HHI_GCLK_MPEG0, 14);
-static MESON_GATE(axg_spicc_1, HHI_GCLK_MPEG0, 15);
-static MESON_GATE(axg_pcie_a, HHI_GCLK_MPEG0, 16);
-static MESON_GATE(axg_pcie_b, HHI_GCLK_MPEG0, 17);
-static MESON_GATE(axg_hiu_reg, HHI_GCLK_MPEG0, 19);
-static MESON_GATE(axg_assist_misc, HHI_GCLK_MPEG0, 23);
-static MESON_GATE(axg_emmc_b, HHI_GCLK_MPEG0, 25);
-static MESON_GATE(axg_emmc_c, HHI_GCLK_MPEG0, 26);
-static MESON_GATE(axg_dma, HHI_GCLK_MPEG0, 27);
-static MESON_GATE(axg_spi, HHI_GCLK_MPEG0, 30);
-
-static MESON_GATE(axg_audio, HHI_GCLK_MPEG1, 0);
-static MESON_GATE(axg_eth_core, HHI_GCLK_MPEG1, 3);
-static MESON_GATE(axg_uart1, HHI_GCLK_MPEG1, 16);
-static MESON_GATE(axg_g2d, HHI_GCLK_MPEG1, 20);
-static MESON_GATE(axg_usb0, HHI_GCLK_MPEG1, 21);
-static MESON_GATE(axg_usb1, HHI_GCLK_MPEG1, 22);
-static MESON_GATE(axg_reset, HHI_GCLK_MPEG1, 23);
-static MESON_GATE(axg_usb_general, HHI_GCLK_MPEG1, 26);
-static MESON_GATE(axg_ahb_arb0, HHI_GCLK_MPEG1, 29);
-static MESON_GATE(axg_efuse, HHI_GCLK_MPEG1, 30);
-static MESON_GATE(axg_boot_rom, HHI_GCLK_MPEG1, 31);
-
-static MESON_GATE(axg_ahb_data_bus, HHI_GCLK_MPEG2, 1);
-static MESON_GATE(axg_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
-static MESON_GATE(axg_usb1_to_ddr, HHI_GCLK_MPEG2, 8);
-static MESON_GATE(axg_usb0_to_ddr, HHI_GCLK_MPEG2, 9);
-static MESON_GATE(axg_mmc_pclk, HHI_GCLK_MPEG2, 11);
-static MESON_GATE(axg_vpu_intr, HHI_GCLK_MPEG2, 25);
-static MESON_GATE(axg_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
-static MESON_GATE(axg_gic, HHI_GCLK_MPEG2, 30);
+static const struct clk_parent_data axg_pclk_parents = { .hw = &axg_clk81.hw };
+
+#define AXG_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(axg_##_name, _reg, _bit, &axg_pclk_parents, _flags)
+
+/*
+ * Everything Else (EE) domain gates
+ *
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
+ */
+static AXG_PCLK(ddr, HHI_GCLK_MPEG0, 0, CLK_IGNORE_UNUSED);
+static AXG_PCLK(audio_locker, HHI_GCLK_MPEG0, 2, CLK_IGNORE_UNUSED);
+static AXG_PCLK(mipi_dsi_host, HHI_GCLK_MPEG0, 3, CLK_IGNORE_UNUSED);
+static AXG_PCLK(isa, HHI_GCLK_MPEG0, 5, CLK_IGNORE_UNUSED);
+static AXG_PCLK(pl301, HHI_GCLK_MPEG0, 6, CLK_IGNORE_UNUSED);
+static AXG_PCLK(periphs, HHI_GCLK_MPEG0, 7, CLK_IGNORE_UNUSED);
+static AXG_PCLK(spicc_0, HHI_GCLK_MPEG0, 8, CLK_IGNORE_UNUSED);
+static AXG_PCLK(i2c, HHI_GCLK_MPEG0, 9, CLK_IGNORE_UNUSED);
+static AXG_PCLK(rng0, HHI_GCLK_MPEG0, 12, CLK_IGNORE_UNUSED);
+static AXG_PCLK(uart0, HHI_GCLK_MPEG0, 13, CLK_IGNORE_UNUSED);
+static AXG_PCLK(mipi_dsi_phy, HHI_GCLK_MPEG0, 14, CLK_IGNORE_UNUSED);
+static AXG_PCLK(spicc_1, HHI_GCLK_MPEG0, 15, CLK_IGNORE_UNUSED);
+static AXG_PCLK(pcie_a, HHI_GCLK_MPEG0, 16, CLK_IGNORE_UNUSED);
+static AXG_PCLK(pcie_b, HHI_GCLK_MPEG0, 17, CLK_IGNORE_UNUSED);
+static AXG_PCLK(hiu_reg, HHI_GCLK_MPEG0, 19, CLK_IGNORE_UNUSED);
+static AXG_PCLK(assist_misc, HHI_GCLK_MPEG0, 23, CLK_IGNORE_UNUSED);
+static AXG_PCLK(emmc_b, HHI_GCLK_MPEG0, 25, CLK_IGNORE_UNUSED);
+static AXG_PCLK(emmc_c, HHI_GCLK_MPEG0, 26, CLK_IGNORE_UNUSED);
+static AXG_PCLK(dma, HHI_GCLK_MPEG0, 27, CLK_IGNORE_UNUSED);
+static AXG_PCLK(spi, HHI_GCLK_MPEG0, 30, CLK_IGNORE_UNUSED);
+
+static AXG_PCLK(audio, HHI_GCLK_MPEG1, 0, CLK_IGNORE_UNUSED);
+static AXG_PCLK(eth_core, HHI_GCLK_MPEG1, 3, CLK_IGNORE_UNUSED);
+static AXG_PCLK(uart1, HHI_GCLK_MPEG1, 16, CLK_IGNORE_UNUSED);
+static AXG_PCLK(g2d, HHI_GCLK_MPEG1, 20, CLK_IGNORE_UNUSED);
+static AXG_PCLK(usb0, HHI_GCLK_MPEG1, 21, CLK_IGNORE_UNUSED);
+static AXG_PCLK(usb1, HHI_GCLK_MPEG1, 22, CLK_IGNORE_UNUSED);
+static AXG_PCLK(reset, HHI_GCLK_MPEG1, 23, CLK_IGNORE_UNUSED);
+static AXG_PCLK(usb_general, HHI_GCLK_MPEG1, 26, CLK_IGNORE_UNUSED);
+static AXG_PCLK(ahb_arb0, HHI_GCLK_MPEG1, 29, CLK_IGNORE_UNUSED);
+static AXG_PCLK(efuse, HHI_GCLK_MPEG1, 30, CLK_IGNORE_UNUSED);
+static AXG_PCLK(boot_rom, HHI_GCLK_MPEG1, 31, CLK_IGNORE_UNUSED);
+
+static AXG_PCLK(ahb_data_bus, HHI_GCLK_MPEG2, 1, CLK_IGNORE_UNUSED);
+static AXG_PCLK(ahb_ctrl_bus, HHI_GCLK_MPEG2, 2, CLK_IGNORE_UNUSED);
+static AXG_PCLK(usb1_to_ddr, HHI_GCLK_MPEG2, 8, CLK_IGNORE_UNUSED);
+static AXG_PCLK(usb0_to_ddr, HHI_GCLK_MPEG2, 9, CLK_IGNORE_UNUSED);
+static AXG_PCLK(mmc_pclk, HHI_GCLK_MPEG2, 11, CLK_IGNORE_UNUSED);
+static AXG_PCLK(vpu_intr, HHI_GCLK_MPEG2, 25, CLK_IGNORE_UNUSED);
+static AXG_PCLK(sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26, CLK_IGNORE_UNUSED);
+static AXG_PCLK(gic, HHI_GCLK_MPEG2, 30, CLK_IGNORE_UNUSED);
/* Always On (AO) domain gates */
-static MESON_GATE(axg_ao_media_cpu, HHI_GCLK_AO, 0);
-static MESON_GATE(axg_ao_ahb_sram, HHI_GCLK_AO, 1);
-static MESON_GATE(axg_ao_ahb_bus, HHI_GCLK_AO, 2);
-static MESON_GATE(axg_ao_iface, HHI_GCLK_AO, 3);
-static MESON_GATE(axg_ao_i2c, HHI_GCLK_AO, 4);
+static AXG_PCLK(ao_media_cpu, HHI_GCLK_AO, 0, CLK_IGNORE_UNUSED);
+static AXG_PCLK(ao_ahb_sram, HHI_GCLK_AO, 1, CLK_IGNORE_UNUSED);
+static AXG_PCLK(ao_ahb_bus, HHI_GCLK_AO, 2, CLK_IGNORE_UNUSED);
+static AXG_PCLK(ao_iface, HHI_GCLK_AO, 3, CLK_IGNORE_UNUSED);
+static AXG_PCLK(ao_i2c, HHI_GCLK_AO, 4, CLK_IGNORE_UNUSED);
/* Array of all clocks provided by this provider */
-static struct clk_hw_onecell_data axg_hw_onecell_data = {
- .hws = {
- [CLKID_SYS_PLL] = &axg_sys_pll.hw,
- [CLKID_FIXED_PLL] = &axg_fixed_pll.hw,
- [CLKID_FCLK_DIV2] = &axg_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &axg_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &axg_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &axg_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &axg_fclk_div7.hw,
- [CLKID_GP0_PLL] = &axg_gp0_pll.hw,
- [CLKID_MPEG_SEL] = &axg_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &axg_mpeg_clk_div.hw,
- [CLKID_CLK81] = &axg_clk81.hw,
- [CLKID_MPLL0] = &axg_mpll0.hw,
- [CLKID_MPLL1] = &axg_mpll1.hw,
- [CLKID_MPLL2] = &axg_mpll2.hw,
- [CLKID_MPLL3] = &axg_mpll3.hw,
- [CLKID_DDR] = &axg_ddr.hw,
- [CLKID_AUDIO_LOCKER] = &axg_audio_locker.hw,
- [CLKID_MIPI_DSI_HOST] = &axg_mipi_dsi_host.hw,
- [CLKID_ISA] = &axg_isa.hw,
- [CLKID_PL301] = &axg_pl301.hw,
- [CLKID_PERIPHS] = &axg_periphs.hw,
- [CLKID_SPICC0] = &axg_spicc_0.hw,
- [CLKID_I2C] = &axg_i2c.hw,
- [CLKID_RNG0] = &axg_rng0.hw,
- [CLKID_UART0] = &axg_uart0.hw,
- [CLKID_MIPI_DSI_PHY] = &axg_mipi_dsi_phy.hw,
- [CLKID_SPICC1] = &axg_spicc_1.hw,
- [CLKID_PCIE_A] = &axg_pcie_a.hw,
- [CLKID_PCIE_B] = &axg_pcie_b.hw,
- [CLKID_HIU_IFACE] = &axg_hiu_reg.hw,
- [CLKID_ASSIST_MISC] = &axg_assist_misc.hw,
- [CLKID_SD_EMMC_B] = &axg_emmc_b.hw,
- [CLKID_SD_EMMC_C] = &axg_emmc_c.hw,
- [CLKID_DMA] = &axg_dma.hw,
- [CLKID_SPI] = &axg_spi.hw,
- [CLKID_AUDIO] = &axg_audio.hw,
- [CLKID_ETH] = &axg_eth_core.hw,
- [CLKID_UART1] = &axg_uart1.hw,
- [CLKID_G2D] = &axg_g2d.hw,
- [CLKID_USB0] = &axg_usb0.hw,
- [CLKID_USB1] = &axg_usb1.hw,
- [CLKID_RESET] = &axg_reset.hw,
- [CLKID_USB] = &axg_usb_general.hw,
- [CLKID_AHB_ARB0] = &axg_ahb_arb0.hw,
- [CLKID_EFUSE] = &axg_efuse.hw,
- [CLKID_BOOT_ROM] = &axg_boot_rom.hw,
- [CLKID_AHB_DATA_BUS] = &axg_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &axg_ahb_ctrl_bus.hw,
- [CLKID_USB1_DDR_BRIDGE] = &axg_usb1_to_ddr.hw,
- [CLKID_USB0_DDR_BRIDGE] = &axg_usb0_to_ddr.hw,
- [CLKID_MMC_PCLK] = &axg_mmc_pclk.hw,
- [CLKID_VPU_INTR] = &axg_vpu_intr.hw,
- [CLKID_SEC_AHB_AHB3_BRIDGE] = &axg_sec_ahb_ahb3_bridge.hw,
- [CLKID_GIC] = &axg_gic.hw,
- [CLKID_AO_MEDIA_CPU] = &axg_ao_media_cpu.hw,
- [CLKID_AO_AHB_SRAM] = &axg_ao_ahb_sram.hw,
- [CLKID_AO_AHB_BUS] = &axg_ao_ahb_bus.hw,
- [CLKID_AO_IFACE] = &axg_ao_iface.hw,
- [CLKID_AO_I2C] = &axg_ao_i2c.hw,
- [CLKID_SD_EMMC_B_CLK0_SEL] = &axg_sd_emmc_b_clk0_sel.hw,
- [CLKID_SD_EMMC_B_CLK0_DIV] = &axg_sd_emmc_b_clk0_div.hw,
- [CLKID_SD_EMMC_B_CLK0] = &axg_sd_emmc_b_clk0.hw,
- [CLKID_SD_EMMC_C_CLK0_SEL] = &axg_sd_emmc_c_clk0_sel.hw,
- [CLKID_SD_EMMC_C_CLK0_DIV] = &axg_sd_emmc_c_clk0_div.hw,
- [CLKID_SD_EMMC_C_CLK0] = &axg_sd_emmc_c_clk0.hw,
- [CLKID_MPLL0_DIV] = &axg_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &axg_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &axg_mpll2_div.hw,
- [CLKID_MPLL3_DIV] = &axg_mpll3_div.hw,
- [CLKID_HIFI_PLL] = &axg_hifi_pll.hw,
- [CLKID_MPLL_PREDIV] = &axg_mpll_prediv.hw,
- [CLKID_FCLK_DIV2_DIV] = &axg_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &axg_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &axg_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &axg_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &axg_fclk_div7_div.hw,
- [CLKID_PCIE_PLL] = &axg_pcie_pll.hw,
- [CLKID_PCIE_MUX] = &axg_pcie_mux.hw,
- [CLKID_PCIE_REF] = &axg_pcie_ref.hw,
- [CLKID_PCIE_CML_EN0] = &axg_pcie_cml_en0.hw,
- [CLKID_PCIE_CML_EN1] = &axg_pcie_cml_en1.hw,
- [CLKID_GEN_CLK_SEL] = &axg_gen_clk_sel.hw,
- [CLKID_GEN_CLK_DIV] = &axg_gen_clk_div.hw,
- [CLKID_GEN_CLK] = &axg_gen_clk.hw,
- [CLKID_SYS_PLL_DCO] = &axg_sys_pll_dco.hw,
- [CLKID_FIXED_PLL_DCO] = &axg_fixed_pll_dco.hw,
- [CLKID_GP0_PLL_DCO] = &axg_gp0_pll_dco.hw,
- [CLKID_HIFI_PLL_DCO] = &axg_hifi_pll_dco.hw,
- [CLKID_PCIE_PLL_DCO] = &axg_pcie_pll_dco.hw,
- [CLKID_PCIE_PLL_OD] = &axg_pcie_pll_od.hw,
- [CLKID_VPU_0_DIV] = &axg_vpu_0_div.hw,
- [CLKID_VPU_0_SEL] = &axg_vpu_0_sel.hw,
- [CLKID_VPU_0] = &axg_vpu_0.hw,
- [CLKID_VPU_1_DIV] = &axg_vpu_1_div.hw,
- [CLKID_VPU_1_SEL] = &axg_vpu_1_sel.hw,
- [CLKID_VPU_1] = &axg_vpu_1.hw,
- [CLKID_VPU] = &axg_vpu.hw,
- [CLKID_VAPB_0_DIV] = &axg_vapb_0_div.hw,
- [CLKID_VAPB_0_SEL] = &axg_vapb_0_sel.hw,
- [CLKID_VAPB_0] = &axg_vapb_0.hw,
- [CLKID_VAPB_1_DIV] = &axg_vapb_1_div.hw,
- [CLKID_VAPB_1_SEL] = &axg_vapb_1_sel.hw,
- [CLKID_VAPB_1] = &axg_vapb_1.hw,
- [CLKID_VAPB_SEL] = &axg_vapb_sel.hw,
- [CLKID_VAPB] = &axg_vapb.hw,
- [CLKID_VCLK] = &axg_vclk.hw,
- [CLKID_VCLK2] = &axg_vclk2.hw,
- [CLKID_VCLK_SEL] = &axg_vclk_sel.hw,
- [CLKID_VCLK2_SEL] = &axg_vclk2_sel.hw,
- [CLKID_VCLK_INPUT] = &axg_vclk_input.hw,
- [CLKID_VCLK2_INPUT] = &axg_vclk2_input.hw,
- [CLKID_VCLK_DIV] = &axg_vclk_div.hw,
- [CLKID_VCLK2_DIV] = &axg_vclk2_div.hw,
- [CLKID_VCLK_DIV2_EN] = &axg_vclk_div2_en.hw,
- [CLKID_VCLK_DIV4_EN] = &axg_vclk_div4_en.hw,
- [CLKID_VCLK_DIV6_EN] = &axg_vclk_div6_en.hw,
- [CLKID_VCLK_DIV12_EN] = &axg_vclk_div12_en.hw,
- [CLKID_VCLK2_DIV2_EN] = &axg_vclk2_div2_en.hw,
- [CLKID_VCLK2_DIV4_EN] = &axg_vclk2_div4_en.hw,
- [CLKID_VCLK2_DIV6_EN] = &axg_vclk2_div6_en.hw,
- [CLKID_VCLK2_DIV12_EN] = &axg_vclk2_div12_en.hw,
- [CLKID_VCLK_DIV1] = &axg_vclk_div1.hw,
- [CLKID_VCLK_DIV2] = &axg_vclk_div2.hw,
- [CLKID_VCLK_DIV4] = &axg_vclk_div4.hw,
- [CLKID_VCLK_DIV6] = &axg_vclk_div6.hw,
- [CLKID_VCLK_DIV12] = &axg_vclk_div12.hw,
- [CLKID_VCLK2_DIV1] = &axg_vclk2_div1.hw,
- [CLKID_VCLK2_DIV2] = &axg_vclk2_div2.hw,
- [CLKID_VCLK2_DIV4] = &axg_vclk2_div4.hw,
- [CLKID_VCLK2_DIV6] = &axg_vclk2_div6.hw,
- [CLKID_VCLK2_DIV12] = &axg_vclk2_div12.hw,
- [CLKID_CTS_ENCL_SEL] = &axg_cts_encl_sel.hw,
- [CLKID_CTS_ENCL] = &axg_cts_encl.hw,
- [CLKID_VDIN_MEAS_SEL] = &axg_vdin_meas_sel.hw,
- [CLKID_VDIN_MEAS_DIV] = &axg_vdin_meas_div.hw,
- [CLKID_VDIN_MEAS] = &axg_vdin_meas.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-/* Convenience table to populate regmap in .probe */
-static struct clk_regmap *const axg_clk_regmaps[] = {
- &axg_clk81,
- &axg_ddr,
- &axg_audio_locker,
- &axg_mipi_dsi_host,
- &axg_isa,
- &axg_pl301,
- &axg_periphs,
- &axg_spicc_0,
- &axg_i2c,
- &axg_rng0,
- &axg_uart0,
- &axg_mipi_dsi_phy,
- &axg_spicc_1,
- &axg_pcie_a,
- &axg_pcie_b,
- &axg_hiu_reg,
- &axg_assist_misc,
- &axg_emmc_b,
- &axg_emmc_c,
- &axg_dma,
- &axg_spi,
- &axg_audio,
- &axg_eth_core,
- &axg_uart1,
- &axg_g2d,
- &axg_usb0,
- &axg_usb1,
- &axg_reset,
- &axg_usb_general,
- &axg_ahb_arb0,
- &axg_efuse,
- &axg_boot_rom,
- &axg_ahb_data_bus,
- &axg_ahb_ctrl_bus,
- &axg_usb1_to_ddr,
- &axg_usb0_to_ddr,
- &axg_mmc_pclk,
- &axg_vpu_intr,
- &axg_sec_ahb_ahb3_bridge,
- &axg_gic,
- &axg_ao_media_cpu,
- &axg_ao_ahb_sram,
- &axg_ao_ahb_bus,
- &axg_ao_iface,
- &axg_ao_i2c,
- &axg_sd_emmc_b_clk0,
- &axg_sd_emmc_c_clk0,
- &axg_mpeg_clk_div,
- &axg_sd_emmc_b_clk0_div,
- &axg_sd_emmc_c_clk0_div,
- &axg_mpeg_clk_sel,
- &axg_sd_emmc_b_clk0_sel,
- &axg_sd_emmc_c_clk0_sel,
- &axg_mpll0,
- &axg_mpll1,
- &axg_mpll2,
- &axg_mpll3,
- &axg_mpll0_div,
- &axg_mpll1_div,
- &axg_mpll2_div,
- &axg_mpll3_div,
- &axg_fixed_pll,
- &axg_sys_pll,
- &axg_gp0_pll,
- &axg_hifi_pll,
- &axg_mpll_prediv,
- &axg_fclk_div2,
- &axg_fclk_div3,
- &axg_fclk_div4,
- &axg_fclk_div5,
- &axg_fclk_div7,
- &axg_pcie_pll_dco,
- &axg_pcie_pll_od,
- &axg_pcie_pll,
- &axg_pcie_mux,
- &axg_pcie_ref,
- &axg_pcie_cml_en0,
- &axg_pcie_cml_en1,
- &axg_gen_clk_sel,
- &axg_gen_clk_div,
- &axg_gen_clk,
- &axg_fixed_pll_dco,
- &axg_sys_pll_dco,
- &axg_gp0_pll_dco,
- &axg_hifi_pll_dco,
- &axg_pcie_pll_dco,
- &axg_pcie_pll_od,
- &axg_vpu_0_div,
- &axg_vpu_0_sel,
- &axg_vpu_0,
- &axg_vpu_1_div,
- &axg_vpu_1_sel,
- &axg_vpu_1,
- &axg_vpu,
- &axg_vapb_0_div,
- &axg_vapb_0_sel,
- &axg_vapb_0,
- &axg_vapb_1_div,
- &axg_vapb_1_sel,
- &axg_vapb_1,
- &axg_vapb_sel,
- &axg_vapb,
- &axg_vclk,
- &axg_vclk2,
- &axg_vclk_sel,
- &axg_vclk2_sel,
- &axg_vclk_input,
- &axg_vclk2_input,
- &axg_vclk_div,
- &axg_vclk2_div,
- &axg_vclk_div2_en,
- &axg_vclk_div4_en,
- &axg_vclk_div6_en,
- &axg_vclk_div12_en,
- &axg_vclk2_div2_en,
- &axg_vclk2_div4_en,
- &axg_vclk2_div6_en,
- &axg_vclk2_div12_en,
- &axg_cts_encl_sel,
- &axg_cts_encl,
- &axg_vdin_meas_sel,
- &axg_vdin_meas_div,
- &axg_vdin_meas,
-};
-
-static const struct meson_eeclkc_data axg_clkc_data = {
- .regmap_clks = axg_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps),
- .hw_onecell_data = &axg_hw_onecell_data,
-};
-
-
-static const struct of_device_id clkc_match_table[] = {
+static struct clk_hw *axg_hw_clks[] = {
+ [CLKID_SYS_PLL] = &axg_sys_pll.hw,
+ [CLKID_FIXED_PLL] = &axg_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &axg_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &axg_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &axg_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &axg_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &axg_fclk_div7.hw,
+ [CLKID_GP0_PLL] = &axg_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &axg_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &axg_clk81_div.hw,
+ [CLKID_CLK81] = &axg_clk81.hw,
+ [CLKID_MPLL0] = &axg_mpll0.hw,
+ [CLKID_MPLL1] = &axg_mpll1.hw,
+ [CLKID_MPLL2] = &axg_mpll2.hw,
+ [CLKID_MPLL3] = &axg_mpll3.hw,
+ [CLKID_DDR] = &axg_ddr.hw,
+ [CLKID_AUDIO_LOCKER] = &axg_audio_locker.hw,
+ [CLKID_MIPI_DSI_HOST] = &axg_mipi_dsi_host.hw,
+ [CLKID_ISA] = &axg_isa.hw,
+ [CLKID_PL301] = &axg_pl301.hw,
+ [CLKID_PERIPHS] = &axg_periphs.hw,
+ [CLKID_SPICC0] = &axg_spicc_0.hw,
+ [CLKID_I2C] = &axg_i2c.hw,
+ [CLKID_RNG0] = &axg_rng0.hw,
+ [CLKID_UART0] = &axg_uart0.hw,
+ [CLKID_MIPI_DSI_PHY] = &axg_mipi_dsi_phy.hw,
+ [CLKID_SPICC1] = &axg_spicc_1.hw,
+ [CLKID_PCIE_A] = &axg_pcie_a.hw,
+ [CLKID_PCIE_B] = &axg_pcie_b.hw,
+ [CLKID_HIU_IFACE] = &axg_hiu_reg.hw,
+ [CLKID_ASSIST_MISC] = &axg_assist_misc.hw,
+ [CLKID_SD_EMMC_B] = &axg_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &axg_emmc_c.hw,
+ [CLKID_DMA] = &axg_dma.hw,
+ [CLKID_SPI] = &axg_spi.hw,
+ [CLKID_AUDIO] = &axg_audio.hw,
+ [CLKID_ETH] = &axg_eth_core.hw,
+ [CLKID_UART1] = &axg_uart1.hw,
+ [CLKID_G2D] = &axg_g2d.hw,
+ [CLKID_USB0] = &axg_usb0.hw,
+ [CLKID_USB1] = &axg_usb1.hw,
+ [CLKID_RESET] = &axg_reset.hw,
+ [CLKID_USB] = &axg_usb_general.hw,
+ [CLKID_AHB_ARB0] = &axg_ahb_arb0.hw,
+ [CLKID_EFUSE] = &axg_efuse.hw,
+ [CLKID_BOOT_ROM] = &axg_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &axg_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &axg_ahb_ctrl_bus.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &axg_usb1_to_ddr.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &axg_usb0_to_ddr.hw,
+ [CLKID_MMC_PCLK] = &axg_mmc_pclk.hw,
+ [CLKID_VPU_INTR] = &axg_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &axg_sec_ahb_ahb3_bridge.hw,
+ [CLKID_GIC] = &axg_gic.hw,
+ [CLKID_AO_MEDIA_CPU] = &axg_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &axg_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &axg_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &axg_ao_iface.hw,
+ [CLKID_AO_I2C] = &axg_ao_i2c.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &axg_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &axg_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &axg_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &axg_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &axg_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &axg_sd_emmc_c_clk0.hw,
+ [CLKID_MPLL0_DIV] = &axg_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &axg_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &axg_mpll2_div.hw,
+ [CLKID_MPLL3_DIV] = &axg_mpll3_div.hw,
+ [CLKID_HIFI_PLL] = &axg_hifi_pll.hw,
+ [CLKID_MPLL_PREDIV] = &axg_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &axg_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &axg_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &axg_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &axg_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &axg_fclk_div7_div.hw,
+ [CLKID_PCIE_PLL] = &axg_pcie_pll.hw,
+ [CLKID_PCIE_MUX] = &axg_pcie_mux.hw,
+ [CLKID_PCIE_REF] = &axg_pcie_ref.hw,
+ [CLKID_PCIE_CML_EN0] = &axg_pcie_cml_en0.hw,
+ [CLKID_PCIE_CML_EN1] = &axg_pcie_cml_en1.hw,
+ [CLKID_GEN_CLK_SEL] = &axg_gen_clk_sel.hw,
+ [CLKID_GEN_CLK_DIV] = &axg_gen_clk_div.hw,
+ [CLKID_GEN_CLK] = &axg_gen_clk.hw,
+ [CLKID_SYS_PLL_DCO] = &axg_sys_pll_dco.hw,
+ [CLKID_FIXED_PLL_DCO] = &axg_fixed_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &axg_gp0_pll_dco.hw,
+ [CLKID_HIFI_PLL_DCO] = &axg_hifi_pll_dco.hw,
+ [CLKID_PCIE_PLL_DCO] = &axg_pcie_pll_dco.hw,
+ [CLKID_PCIE_PLL_OD] = &axg_pcie_pll_od.hw,
+ [CLKID_VPU_0_DIV] = &axg_vpu_0_div.hw,
+ [CLKID_VPU_0_SEL] = &axg_vpu_0_sel.hw,
+ [CLKID_VPU_0] = &axg_vpu_0.hw,
+ [CLKID_VPU_1_DIV] = &axg_vpu_1_div.hw,
+ [CLKID_VPU_1_SEL] = &axg_vpu_1_sel.hw,
+ [CLKID_VPU_1] = &axg_vpu_1.hw,
+ [CLKID_VPU] = &axg_vpu.hw,
+ [CLKID_VAPB_0_DIV] = &axg_vapb_0_div.hw,
+ [CLKID_VAPB_0_SEL] = &axg_vapb_0_sel.hw,
+ [CLKID_VAPB_0] = &axg_vapb_0.hw,
+ [CLKID_VAPB_1_DIV] = &axg_vapb_1_div.hw,
+ [CLKID_VAPB_1_SEL] = &axg_vapb_1_sel.hw,
+ [CLKID_VAPB_1] = &axg_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &axg_vapb_sel.hw,
+ [CLKID_VAPB] = &axg_vapb.hw,
+ [CLKID_VCLK] = &axg_vclk.hw,
+ [CLKID_VCLK2] = &axg_vclk2.hw,
+ [CLKID_VCLK_SEL] = &axg_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &axg_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &axg_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &axg_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &axg_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &axg_vclk2_div.hw,
+ [CLKID_VCLK_DIV2_EN] = &axg_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV4_EN] = &axg_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV6_EN] = &axg_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV12_EN] = &axg_vclk_div12_en.hw,
+ [CLKID_VCLK2_DIV2_EN] = &axg_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV4_EN] = &axg_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV6_EN] = &axg_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV12_EN] = &axg_vclk2_div12_en.hw,
+ [CLKID_VCLK_DIV1] = &axg_vclk_div1.hw,
+ [CLKID_VCLK_DIV2] = &axg_vclk_div2.hw,
+ [CLKID_VCLK_DIV4] = &axg_vclk_div4.hw,
+ [CLKID_VCLK_DIV6] = &axg_vclk_div6.hw,
+ [CLKID_VCLK_DIV12] = &axg_vclk_div12.hw,
+ [CLKID_VCLK2_DIV1] = &axg_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2] = &axg_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4] = &axg_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6] = &axg_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12] = &axg_vclk2_div12.hw,
+ [CLKID_CTS_ENCL_SEL] = &axg_cts_encl_sel.hw,
+ [CLKID_CTS_ENCL] = &axg_cts_encl.hw,
+ [CLKID_VDIN_MEAS_SEL] = &axg_vdin_meas_sel.hw,
+ [CLKID_VDIN_MEAS_DIV] = &axg_vdin_meas_div.hw,
+ [CLKID_VDIN_MEAS] = &axg_vdin_meas.hw,
+};
+
+static const struct meson_clkc_data axg_clkc_data = {
+ .hw_clks = {
+ .hws = axg_hw_clks,
+ .num = ARRAY_SIZE(axg_hw_clks),
+ },
+};
+
+static const struct of_device_id axg_clkc_match_table[] = {
{ .compatible = "amlogic,axg-clkc", .data = &axg_clkc_data },
{}
};
-MODULE_DEVICE_TABLE(of, clkc_match_table);
+MODULE_DEVICE_TABLE(of, axg_clkc_match_table);
-static struct platform_driver axg_driver = {
- .probe = meson_eeclkc_probe,
+static struct platform_driver axg_clkc_driver = {
+ .probe = meson_clkc_syscon_probe,
.driver = {
.name = "axg-clkc",
- .of_match_table = clkc_match_table,
+ .of_match_table = axg_clkc_match_table,
},
};
+module_platform_driver(axg_clkc_driver);
-module_platform_driver(axg_driver);
-MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Amlogic AXG Main Clock Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/axg.h b/drivers/clk/meson/axg.h
deleted file mode 100644
index 23ea87964af2..000000000000
--- a/drivers/clk/meson/axg.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
-/*
- * Copyright (c) 2016 AmLogic, Inc.
- * Author: Michael Turquette <mturquette@baylibre.com>
- *
- * Copyright (c) 2017 Amlogic, inc.
- * Author: Qiufang Dai <qiufang.dai@amlogic.com>
- *
- */
-#ifndef __AXG_H
-#define __AXG_H
-
-/*
- * Clock controller register offsets
- *
- * Register offsets from the data sheet must be multiplied by 4 before
- * adding them to the base address to get the right value.
- */
-#define HHI_GP0_PLL_CNTL 0x40
-#define HHI_GP0_PLL_CNTL2 0x44
-#define HHI_GP0_PLL_CNTL3 0x48
-#define HHI_GP0_PLL_CNTL4 0x4c
-#define HHI_GP0_PLL_CNTL5 0x50
-#define HHI_GP0_PLL_STS 0x54
-#define HHI_GP0_PLL_CNTL1 0x58
-#define HHI_HIFI_PLL_CNTL 0x80
-#define HHI_HIFI_PLL_CNTL2 0x84
-#define HHI_HIFI_PLL_CNTL3 0x88
-#define HHI_HIFI_PLL_CNTL4 0x8C
-#define HHI_HIFI_PLL_CNTL5 0x90
-#define HHI_HIFI_PLL_STS 0x94
-#define HHI_HIFI_PLL_CNTL1 0x98
-
-#define HHI_XTAL_DIVN_CNTL 0xbc
-#define HHI_GCLK2_MPEG0 0xc0
-#define HHI_GCLK2_MPEG1 0xc4
-#define HHI_GCLK2_MPEG2 0xc8
-#define HHI_GCLK2_OTHER 0xd0
-#define HHI_GCLK2_AO 0xd4
-#define HHI_PCIE_PLL_CNTL 0xd8
-#define HHI_PCIE_PLL_CNTL1 0xdC
-#define HHI_PCIE_PLL_CNTL2 0xe0
-#define HHI_PCIE_PLL_CNTL3 0xe4
-#define HHI_PCIE_PLL_CNTL4 0xe8
-#define HHI_PCIE_PLL_CNTL5 0xec
-#define HHI_PCIE_PLL_CNTL6 0xf0
-#define HHI_PCIE_PLL_STS 0xf4
-
-#define HHI_MEM_PD_REG0 0x100
-#define HHI_VPU_MEM_PD_REG0 0x104
-#define HHI_VIID_CLK_DIV 0x128
-#define HHI_VIID_CLK_CNTL 0x12c
-
-#define HHI_GCLK_MPEG0 0x140
-#define HHI_GCLK_MPEG1 0x144
-#define HHI_GCLK_MPEG2 0x148
-#define HHI_GCLK_OTHER 0x150
-#define HHI_GCLK_AO 0x154
-#define HHI_SYS_CPU_CLK_CNTL1 0x15c
-#define HHI_SYS_CPU_RESET_CNTL 0x160
-#define HHI_VID_CLK_DIV 0x164
-#define HHI_SPICC_HCLK_CNTL 0x168
-
-#define HHI_MPEG_CLK_CNTL 0x174
-#define HHI_VID_CLK_CNTL 0x17c
-#define HHI_TS_CLK_CNTL 0x190
-#define HHI_VID_CLK_CNTL2 0x194
-#define HHI_SYS_CPU_CLK_CNTL0 0x19c
-#define HHI_VID_PLL_CLK_DIV 0x1a0
-#define HHI_VPU_CLK_CNTL 0x1bC
-
-#define HHI_VAPBCLK_CNTL 0x1F4
-
-#define HHI_GEN_CLK_CNTL 0x228
-
-#define HHI_VDIN_MEAS_CLK_CNTL 0x250
-#define HHI_NAND_CLK_CNTL 0x25C
-#define HHI_SD_EMMC_CLK_CNTL 0x264
-
-#define HHI_MPLL_CNTL 0x280
-#define HHI_MPLL_CNTL2 0x284
-#define HHI_MPLL_CNTL3 0x288
-#define HHI_MPLL_CNTL4 0x28C
-#define HHI_MPLL_CNTL5 0x290
-#define HHI_MPLL_CNTL6 0x294
-#define HHI_MPLL_CNTL7 0x298
-#define HHI_MPLL_CNTL8 0x29C
-#define HHI_MPLL_CNTL9 0x2A0
-#define HHI_MPLL_CNTL10 0x2A4
-
-#define HHI_MPLL3_CNTL0 0x2E0
-#define HHI_MPLL3_CNTL1 0x2E4
-#define HHI_PLL_TOP_MISC 0x2E8
-
-#define HHI_SYS_PLL_CNTL1 0x2FC
-#define HHI_SYS_PLL_CNTL 0x300
-#define HHI_SYS_PLL_CNTL2 0x304
-#define HHI_SYS_PLL_CNTL3 0x308
-#define HHI_SYS_PLL_CNTL4 0x30c
-#define HHI_SYS_PLL_CNTL5 0x310
-#define HHI_SYS_PLL_STS 0x314
-#define HHI_DPLL_TOP_I 0x318
-#define HHI_DPLL_TOP2_I 0x31C
-
-/*
- * CLKID index values
- *
- * These indices are entirely contrived and do not map onto the hardware.
- * It has now been decided to expose everything by default in the DT header:
- * include/dt-bindings/clock/axg-clkc.h. Only the clocks ids we don't want
- * to expose, such as the internal muxes and dividers of composite clocks,
- * will remain defined here.
- */
-#define CLKID_MPEG_SEL 8
-#define CLKID_MPEG_DIV 9
-#define CLKID_SD_EMMC_B_CLK0_SEL 61
-#define CLKID_SD_EMMC_B_CLK0_DIV 62
-#define CLKID_SD_EMMC_C_CLK0_SEL 63
-#define CLKID_SD_EMMC_C_CLK0_DIV 64
-#define CLKID_MPLL0_DIV 65
-#define CLKID_MPLL1_DIV 66
-#define CLKID_MPLL2_DIV 67
-#define CLKID_MPLL3_DIV 68
-#define CLKID_MPLL_PREDIV 70
-#define CLKID_FCLK_DIV2_DIV 71
-#define CLKID_FCLK_DIV3_DIV 72
-#define CLKID_FCLK_DIV4_DIV 73
-#define CLKID_FCLK_DIV5_DIV 74
-#define CLKID_FCLK_DIV7_DIV 75
-#define CLKID_PCIE_PLL 76
-#define CLKID_PCIE_MUX 77
-#define CLKID_PCIE_REF 78
-#define CLKID_GEN_CLK_SEL 82
-#define CLKID_GEN_CLK_DIV 83
-#define CLKID_SYS_PLL_DCO 85
-#define CLKID_FIXED_PLL_DCO 86
-#define CLKID_GP0_PLL_DCO 87
-#define CLKID_HIFI_PLL_DCO 88
-#define CLKID_PCIE_PLL_DCO 89
-#define CLKID_PCIE_PLL_OD 90
-#define CLKID_VPU_0_DIV 91
-#define CLKID_VPU_1_DIV 94
-#define CLKID_VAPB_0_DIV 98
-#define CLKID_VAPB_1_DIV 101
-#define CLKID_VCLK_SEL 108
-#define CLKID_VCLK2_SEL 109
-#define CLKID_VCLK_INPUT 110
-#define CLKID_VCLK2_INPUT 111
-#define CLKID_VCLK_DIV 112
-#define CLKID_VCLK2_DIV 113
-#define CLKID_VCLK_DIV2_EN 114
-#define CLKID_VCLK_DIV4_EN 115
-#define CLKID_VCLK_DIV6_EN 116
-#define CLKID_VCLK_DIV12_EN 117
-#define CLKID_VCLK2_DIV2_EN 118
-#define CLKID_VCLK2_DIV4_EN 119
-#define CLKID_VCLK2_DIV6_EN 120
-#define CLKID_VCLK2_DIV12_EN 121
-#define CLKID_CTS_ENCL_SEL 132
-#define CLKID_VDIN_MEAS_SEL 134
-#define CLKID_VDIN_MEAS_DIV 135
-
-#define NR_CLKS 137
-
-/* include the CLKIDs that have been made part of the DT binding */
-#include <dt-bindings/clock/axg-clkc.h>
-
-#endif /* __AXG_H */
diff --git a/drivers/clk/meson/c3-peripherals.c b/drivers/clk/meson/c3-peripherals.c
new file mode 100644
index 000000000000..b158756cfee4
--- /dev/null
+++ b/drivers/clk/meson/c3-peripherals.c
@@ -0,0 +1,1128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Amlogic C3 Peripherals Clock Controller Driver
+ *
+ * Copyright (c) 2023 Amlogic, inc.
+ * Author: Chuan Liu <chuan.liu@amlogic.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+#include "meson-clkc-utils.h"
+#include <dt-bindings/clock/amlogic,c3-peripherals-clkc.h>
+
+#define RTC_BY_OSCIN_CTRL0 0x8
+#define RTC_BY_OSCIN_CTRL1 0xc
+#define RTC_CTRL 0x10
+#define SYS_CLK_EN0_REG0 0x44
+#define SYS_CLK_EN0_REG1 0x48
+#define SYS_CLK_EN0_REG2 0x4c
+#define CLK12_24_CTRL 0xa8
+#define AXI_CLK_EN0 0xac
+#define VDIN_MEAS_CLK_CTRL 0xf8
+#define VAPB_CLK_CTRL 0xfc
+#define MIPIDSI_PHY_CLK_CTRL 0x104
+#define GE2D_CLK_CTRL 0x10c
+#define ISP0_CLK_CTRL 0x110
+#define DEWARPA_CLK_CTRL 0x114
+#define VOUTENC_CLK_CTRL 0x118
+#define VDEC_CLK_CTRL 0x140
+#define VDEC3_CLK_CTRL 0x148
+#define TS_CLK_CTRL 0x158
+#define ETH_CLK_CTRL 0x164
+#define NAND_CLK_CTRL 0x168
+#define SD_EMMC_CLK_CTRL 0x16c
+#define SPICC_CLK_CTRL 0x174
+#define GEN_CLK_CTRL 0x178
+#define SAR_CLK_CTRL0 0x17c
+#define PWM_CLK_AB_CTRL 0x180
+#define PWM_CLK_CD_CTRL 0x184
+#define PWM_CLK_EF_CTRL 0x188
+#define PWM_CLK_GH_CTRL 0x18c
+#define PWM_CLK_IJ_CTRL 0x190
+#define PWM_CLK_KL_CTRL 0x194
+#define PWM_CLK_MN_CTRL 0x198
+#define VC9000E_CLK_CTRL 0x19c
+#define SPIFC_CLK_CTRL 0x1a0
+#define NNA_CLK_CTRL 0x220
+
+#define C3_COMP_SEL(_name, _reg, _shift, _mask, _pdata) \
+ MESON_COMP_SEL(c3_, _name, _reg, _shift, _mask, _pdata, NULL, 0, 0)
+
+#define C3_COMP_DIV(_name, _reg, _shift, _width) \
+ MESON_COMP_DIV(c3_, _name, _reg, _shift, _width, 0, CLK_SET_RATE_PARENT)
+
+#define C3_COMP_GATE(_name, _reg, _bit) \
+ MESON_COMP_GATE(c3_, _name, _reg, _bit, CLK_SET_RATE_PARENT)
+
+static struct clk_regmap c3_rtc_xtal_clkin = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_xtal_clkin",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "oscin",
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param c3_rtc_32k_div_table[] = {
+ { 733, 732, 8, 11, 1 },
+ { /* sentinel */ }
+};
+
+static struct clk_regmap c3_rtc_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data) {
+ .n1 = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = RTC_BY_OSCIN_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = RTC_BY_OSCIN_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = RTC_BY_OSCIN_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = c3_rtc_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_rtc_xtal_clkin.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct clk_parent_data c3_rtc_32k_parents[] = {
+ { .hw = &c3_rtc_32k_div.hw },
+ { .hw = &c3_rtc_xtal_clkin.hw }
+};
+
+static struct clk_regmap c3_rtc_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = RTC_BY_OSCIN_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = c3_rtc_32k_parents,
+ .num_parents = ARRAY_SIZE(c3_rtc_32k_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_rtc_32k = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_rtc_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_rtc_clk_parents[] = {
+ { .fw_name = "oscin" },
+ { .hw = &c3_rtc_32k.hw },
+ { .fw_name = "pad_osc" }
+};
+
+static struct clk_regmap c3_rtc_clk = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = RTC_CTRL,
+ .mask = 0x3,
+ .shift = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_clk",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = c3_rtc_clk_parents,
+ .num_parents = ARRAY_SIZE(c3_rtc_clk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_sys_pclk_parents = { .fw_name = "sysclk" };
+
+#define C3_SYS_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(c3_##_name, _reg, _bit, &c3_sys_pclk_parents, _flags)
+
+#define C3_SYS_PCLK_RO(_name, _reg, _bit) \
+ MESON_PCLK_RO(c3_##_name, _reg, _bit, &c3_sys_pclk_parents, 0)
+
+static C3_SYS_PCLK(sys_reset_ctrl, SYS_CLK_EN0_REG0, 1, 0);
+static C3_SYS_PCLK(sys_pwr_ctrl, SYS_CLK_EN0_REG0, 3, 0);
+static C3_SYS_PCLK(sys_pad_ctrl, SYS_CLK_EN0_REG0, 4, 0);
+static C3_SYS_PCLK(sys_ctrl, SYS_CLK_EN0_REG0, 5, 0);
+static C3_SYS_PCLK(sys_ts_pll, SYS_CLK_EN0_REG0, 6, 0);
+
+/*
+ * NOTE: sys_dev_arb provides the clock to the ETH and SPICC arbiters that
+ * access the AXI bus.
+ */
+static C3_SYS_PCLK(sys_dev_arb, SYS_CLK_EN0_REG0, 7, 0);
+
+/*
+ * FIXME: sys_mmc_pclk provides the clock for the DDR PHY, DDR will only be
+ * initialized in bl2, and this clock should not be touched in linux.
+ */
+static C3_SYS_PCLK_RO(sys_mmc_pclk, SYS_CLK_EN0_REG0, 8);
+
+/*
+ * NOTE: sys_cpu_ctrl provides the clock for CPU controller. After clock is
+ * disabled, cpu_clk and other key CPU-related configurations cannot take effect.
+ */
+static C3_SYS_PCLK(sys_cpu_ctrl, SYS_CLK_EN0_REG0, 11, CLK_IS_CRITICAL);
+static C3_SYS_PCLK(sys_jtag_ctrl, SYS_CLK_EN0_REG0, 12, 0);
+static C3_SYS_PCLK(sys_ir_ctrl, SYS_CLK_EN0_REG0, 13, 0);
+
+/*
+ * NOTE: sys_irq_ctrl provides the clock for IRQ controller. The IRQ controller
+ * collects and distributes the interrupt signal to the GIC, PWR_CTRL, and
+ * AOCPU. If the clock is disabled, interrupt-related functions will occurs an
+ * exception.
+ */
+static C3_SYS_PCLK(sys_irq_ctrl, SYS_CLK_EN0_REG0, 14, CLK_IS_CRITICAL);
+static C3_SYS_PCLK(sys_msr_clk, SYS_CLK_EN0_REG0, 15, 0);
+static C3_SYS_PCLK(sys_rom, SYS_CLK_EN0_REG0, 16, 0);
+static C3_SYS_PCLK(sys_uart_f, SYS_CLK_EN0_REG0, 17, 0);
+static C3_SYS_PCLK(sys_cpu_apb, SYS_CLK_EN0_REG0, 18, 0);
+static C3_SYS_PCLK(sys_rsa, SYS_CLK_EN0_REG0, 19, 0);
+static C3_SYS_PCLK(sys_sar_adc, SYS_CLK_EN0_REG0, 20, 0);
+static C3_SYS_PCLK(sys_startup, SYS_CLK_EN0_REG0, 21, 0);
+static C3_SYS_PCLK(sys_secure, SYS_CLK_EN0_REG0, 22, 0);
+static C3_SYS_PCLK(sys_spifc, SYS_CLK_EN0_REG0, 23, 0);
+static C3_SYS_PCLK(sys_nna, SYS_CLK_EN0_REG0, 25, 0);
+static C3_SYS_PCLK(sys_eth_mac, SYS_CLK_EN0_REG0, 26, 0);
+
+/*
+ * FIXME: sys_gic provides the clock for GIC(Generic Interrupt Controller).
+ * After clock is disabled, The GIC cannot work properly. At present, the driver
+ * used by our GIC is the public driver in kernel, and there is no management
+ * clock in the driver.
+ */
+static C3_SYS_PCLK(sys_gic, SYS_CLK_EN0_REG0, 27, CLK_IS_CRITICAL);
+static C3_SYS_PCLK(sys_rama, SYS_CLK_EN0_REG0, 28, 0);
+
+/*
+ * NOTE: sys_big_nic provides the clock to the control bus of the NIC(Network
+ * Interface Controller) between multiple devices(CPU, DDR, RAM, ROM, GIC,
+ * SPIFC, CAPU, JTAG, EMMC, SDIO, sec_top, USB, Audio, ETH, SPICC) in the
+ * system. After clock is disabled, The NIC cannot work.
+ */
+static C3_SYS_PCLK(sys_big_nic, SYS_CLK_EN0_REG0, 29, CLK_IS_CRITICAL);
+static C3_SYS_PCLK(sys_ramb, SYS_CLK_EN0_REG0, 30, 0);
+static C3_SYS_PCLK(sys_audio_pclk, SYS_CLK_EN0_REG0, 31, 0);
+static C3_SYS_PCLK(sys_pwm_kl, SYS_CLK_EN0_REG1, 0, 0);
+static C3_SYS_PCLK(sys_pwm_ij, SYS_CLK_EN0_REG1, 1, 0);
+static C3_SYS_PCLK(sys_usb, SYS_CLK_EN0_REG1, 2, 0);
+static C3_SYS_PCLK(sys_sd_emmc_a, SYS_CLK_EN0_REG1, 3, 0);
+static C3_SYS_PCLK(sys_sd_emmc_c, SYS_CLK_EN0_REG1, 4, 0);
+static C3_SYS_PCLK(sys_pwm_ab, SYS_CLK_EN0_REG1, 5, 0);
+static C3_SYS_PCLK(sys_pwm_cd, SYS_CLK_EN0_REG1, 6, 0);
+static C3_SYS_PCLK(sys_pwm_ef, SYS_CLK_EN0_REG1, 7, 0);
+static C3_SYS_PCLK(sys_pwm_gh, SYS_CLK_EN0_REG1, 8, 0);
+static C3_SYS_PCLK(sys_spicc_1, SYS_CLK_EN0_REG1, 9, 0);
+static C3_SYS_PCLK(sys_spicc_0, SYS_CLK_EN0_REG1, 10, 0);
+static C3_SYS_PCLK(sys_uart_a, SYS_CLK_EN0_REG1, 11, 0);
+static C3_SYS_PCLK(sys_uart_b, SYS_CLK_EN0_REG1, 12, 0);
+static C3_SYS_PCLK(sys_uart_c, SYS_CLK_EN0_REG1, 13, 0);
+static C3_SYS_PCLK(sys_uart_d, SYS_CLK_EN0_REG1, 14, 0);
+static C3_SYS_PCLK(sys_uart_e, SYS_CLK_EN0_REG1, 15, 0);
+static C3_SYS_PCLK(sys_i2c_m_a, SYS_CLK_EN0_REG1, 16, 0);
+static C3_SYS_PCLK(sys_i2c_m_b, SYS_CLK_EN0_REG1, 17, 0);
+static C3_SYS_PCLK(sys_i2c_m_c, SYS_CLK_EN0_REG1, 18, 0);
+static C3_SYS_PCLK(sys_i2c_m_d, SYS_CLK_EN0_REG1, 19, 0);
+static C3_SYS_PCLK(sys_i2c_s_a, SYS_CLK_EN0_REG1, 20, 0);
+static C3_SYS_PCLK(sys_rtc, SYS_CLK_EN0_REG1, 21, 0);
+static C3_SYS_PCLK(sys_ge2d, SYS_CLK_EN0_REG1, 22, 0);
+static C3_SYS_PCLK(sys_isp, SYS_CLK_EN0_REG1, 23, 0);
+static C3_SYS_PCLK(sys_gpv_isp_nic, SYS_CLK_EN0_REG1, 24, 0);
+static C3_SYS_PCLK(sys_gpv_cve_nic, SYS_CLK_EN0_REG1, 25, 0);
+static C3_SYS_PCLK(sys_mipi_dsi_host, SYS_CLK_EN0_REG1, 26, 0);
+static C3_SYS_PCLK(sys_mipi_dsi_phy, SYS_CLK_EN0_REG1, 27, 0);
+static C3_SYS_PCLK(sys_eth_phy, SYS_CLK_EN0_REG1, 28, 0);
+static C3_SYS_PCLK(sys_acodec, SYS_CLK_EN0_REG1, 29, 0);
+static C3_SYS_PCLK(sys_dwap, SYS_CLK_EN0_REG1, 30, 0);
+static C3_SYS_PCLK(sys_dos, SYS_CLK_EN0_REG1, 31, 0);
+static C3_SYS_PCLK(sys_cve, SYS_CLK_EN0_REG2, 0, 0);
+static C3_SYS_PCLK(sys_vout, SYS_CLK_EN0_REG2, 1, 0);
+static C3_SYS_PCLK(sys_vc9000e, SYS_CLK_EN0_REG2, 2, 0);
+static C3_SYS_PCLK(sys_pwm_mn, SYS_CLK_EN0_REG2, 3, 0);
+static C3_SYS_PCLK(sys_sd_emmc_b, SYS_CLK_EN0_REG2, 4, 0);
+
+static const struct clk_parent_data c3_axi_pclk_parents = { .fw_name = "axiclk" };
+
+#define C3_AXI_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(c3_##_name, _reg, _bit, &c3_axi_pclk_parents, _flags)
+
+/*
+ * NOTE: axi_sys_nic provides the clock to the AXI bus of the system NIC. After
+ * clock is disabled, The NIC cannot work.
+ */
+static C3_AXI_PCLK(axi_sys_nic, AXI_CLK_EN0, 2, CLK_IS_CRITICAL);
+static C3_AXI_PCLK(axi_isp_nic, AXI_CLK_EN0, 3, 0);
+static C3_AXI_PCLK(axi_cve_nic, AXI_CLK_EN0, 4, 0);
+static C3_AXI_PCLK(axi_ramb, AXI_CLK_EN0, 5, 0);
+static C3_AXI_PCLK(axi_rama, AXI_CLK_EN0, 6, 0);
+
+/*
+ * NOTE: axi_cpu_dmc provides the clock to the AXI bus where the CPU accesses
+ * the DDR. After clock is disabled, The CPU will not have access to the DDR.
+ */
+static C3_AXI_PCLK(axi_cpu_dmc, AXI_CLK_EN0, 7, CLK_IS_CRITICAL);
+static C3_AXI_PCLK(axi_nic, AXI_CLK_EN0, 8, 0);
+static C3_AXI_PCLK(axi_dma, AXI_CLK_EN0, 9, 0);
+
+/*
+ * NOTE: axi_mux_nic provides the clock to the NIC's AXI bus for NN(Neural
+ * Network) and other devices(CPU, EMMC, SDIO, sec_top, USB, Audio, ETH, SPICC)
+ * to access RAM space.
+ */
+static C3_AXI_PCLK(axi_mux_nic, AXI_CLK_EN0, 10, 0);
+static C3_AXI_PCLK(axi_cve, AXI_CLK_EN0, 12, 0);
+
+/*
+ * NOTE: axi_dev1_dmc provides the clock for the peripherals(EMMC, SDIO,
+ * sec_top, USB, Audio, ETH, SPICC) to access the AXI bus of the DDR.
+ */
+static C3_AXI_PCLK(axi_dev1_dmc, AXI_CLK_EN0, 13, 0);
+static C3_AXI_PCLK(axi_dev0_dmc, AXI_CLK_EN0, 14, 0);
+static C3_AXI_PCLK(axi_dsp_dmc, AXI_CLK_EN0, 15, 0);
+
+/*
+ * clk_12_24m model
+ *
+ * |------| |-----| clk_12m_24m |-----|
+ * xtal---->| gate |---->| div |------------>| pad |
+ * |------| |-----| |-----|
+ */
+static struct clk_regmap c3_clk_12_24m_in = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "clk_12_24m_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal_24m",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_clk_12_24m = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = CLK12_24_CTRL,
+ .shift = 10,
+ .width = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "clk_12_24m",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_clk_12_24m_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+/* Fix me: set value 0 will div by 2 like value 1 */
+static struct clk_regmap c3_fclk_25m_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = CLK12_24_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_25m_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_25m = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 12,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_25m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_25m_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * Channel 3(ddr_dpll_pt_clk) is manged by the DDR module; channel 12(cts_msr_clk)
+ * is manged by clock measures module. Their hardware are out of clock tree.
+ * Channel 4 8 9 10 11 13 14 15 16 18 are not connected.
+ */
+static u32 c3_gen_parents_val_table[] = { 0, 1, 2, 5, 6, 7, 17, 19, 20, 21, 22, 23, 24};
+static const struct clk_parent_data c3_gen_parents[] = {
+ { .fw_name = "oscin" },
+ { .hw = &c3_rtc_clk.hw },
+ { .fw_name = "sysplldiv16" },
+ { .fw_name = "gp0" },
+ { .fw_name = "gp1" },
+ { .fw_name = "hifi" },
+ { .fw_name = "cpudiv16" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv7" }
+};
+
+static struct clk_regmap c3_gen_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = GEN_CLK_CTRL,
+ .mask = 0x1f,
+ .shift = 12,
+ .table = c3_gen_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gen_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = c3_gen_parents,
+ .num_parents = ARRAY_SIZE(c3_gen_parents),
+ },
+};
+
+static struct clk_regmap c3_gen_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = GEN_CLK_CTRL,
+ .shift = 0,
+ .width = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gen_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_gen_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_gen = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = GEN_CLK_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gen",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_gen_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_saradc_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "sysclk" }
+};
+
+static C3_COMP_SEL(saradc, SAR_CLK_CTRL0, 9, 0x1, c3_saradc_parents);
+static C3_COMP_DIV(saradc, SAR_CLK_CTRL0, 0, 8);
+static C3_COMP_GATE(saradc, SAR_CLK_CTRL0, 8);
+
+static const struct clk_parent_data c3_pwm_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "gp1" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv3" }
+};
+
+static C3_COMP_SEL(pwm_a, PWM_CLK_AB_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_a, PWM_CLK_AB_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_a, PWM_CLK_AB_CTRL, 8);
+
+static C3_COMP_SEL(pwm_b, PWM_CLK_AB_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_b, PWM_CLK_AB_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_b, PWM_CLK_AB_CTRL, 24);
+
+static C3_COMP_SEL(pwm_c, PWM_CLK_CD_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_c, PWM_CLK_CD_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_c, PWM_CLK_CD_CTRL, 8);
+
+static C3_COMP_SEL(pwm_d, PWM_CLK_CD_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_d, PWM_CLK_CD_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_d, PWM_CLK_CD_CTRL, 24);
+
+static C3_COMP_SEL(pwm_e, PWM_CLK_EF_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_e, PWM_CLK_EF_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_e, PWM_CLK_EF_CTRL, 8);
+
+static C3_COMP_SEL(pwm_f, PWM_CLK_EF_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_f, PWM_CLK_EF_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_f, PWM_CLK_EF_CTRL, 24);
+
+static C3_COMP_SEL(pwm_g, PWM_CLK_GH_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_g, PWM_CLK_GH_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_g, PWM_CLK_GH_CTRL, 8);
+
+static C3_COMP_SEL(pwm_h, PWM_CLK_GH_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_h, PWM_CLK_GH_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_h, PWM_CLK_GH_CTRL, 24);
+
+static C3_COMP_SEL(pwm_i, PWM_CLK_IJ_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_i, PWM_CLK_IJ_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_i, PWM_CLK_IJ_CTRL, 8);
+
+static C3_COMP_SEL(pwm_j, PWM_CLK_IJ_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_j, PWM_CLK_IJ_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_j, PWM_CLK_IJ_CTRL, 24);
+
+static C3_COMP_SEL(pwm_k, PWM_CLK_KL_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_k, PWM_CLK_KL_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_k, PWM_CLK_KL_CTRL, 8);
+
+static C3_COMP_SEL(pwm_l, PWM_CLK_KL_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_l, PWM_CLK_KL_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_l, PWM_CLK_KL_CTRL, 24);
+
+static C3_COMP_SEL(pwm_m, PWM_CLK_MN_CTRL, 9, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_m, PWM_CLK_MN_CTRL, 0, 8);
+static C3_COMP_GATE(pwm_m, PWM_CLK_MN_CTRL, 8);
+
+static C3_COMP_SEL(pwm_n, PWM_CLK_MN_CTRL, 25, 0x3, c3_pwm_parents);
+static C3_COMP_DIV(pwm_n, PWM_CLK_MN_CTRL, 16, 8);
+static C3_COMP_GATE(pwm_n, PWM_CLK_MN_CTRL, 24);
+
+static const struct clk_parent_data c3_spicc_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "sysclk" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv7" },
+ { .fw_name = "gp1" }
+};
+
+static C3_COMP_SEL(spicc_a, SPICC_CLK_CTRL, 7, 0x7, c3_spicc_parents);
+static C3_COMP_DIV(spicc_a, SPICC_CLK_CTRL, 0, 6);
+static C3_COMP_GATE(spicc_a, SPICC_CLK_CTRL, 6);
+
+static C3_COMP_SEL(spicc_b, SPICC_CLK_CTRL, 23, 0x7, c3_spicc_parents);
+static C3_COMP_DIV(spicc_b, SPICC_CLK_CTRL, 16, 6);
+static C3_COMP_GATE(spicc_b, SPICC_CLK_CTRL, 22);
+
+static const struct clk_parent_data c3_spifc_parents[] = {
+ { .fw_name = "gp0" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "hifi" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv7" }
+};
+
+static C3_COMP_SEL(spifc, SPIFC_CLK_CTRL, 9, 0x7, c3_spifc_parents);
+static C3_COMP_DIV(spifc, SPIFC_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(spifc, SPIFC_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_sd_emmc_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "hifi" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "gp1" },
+ { .fw_name = "gp0" }
+};
+
+static C3_COMP_SEL(sd_emmc_a, SD_EMMC_CLK_CTRL, 9, 0x7, c3_sd_emmc_parents);
+static C3_COMP_DIV(sd_emmc_a, SD_EMMC_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(sd_emmc_a, SD_EMMC_CLK_CTRL, 7);
+
+static C3_COMP_SEL(sd_emmc_b, SD_EMMC_CLK_CTRL, 25, 0x7, c3_sd_emmc_parents);
+static C3_COMP_DIV(sd_emmc_b, SD_EMMC_CLK_CTRL, 16, 7);
+static C3_COMP_GATE(sd_emmc_b, SD_EMMC_CLK_CTRL, 23);
+
+static C3_COMP_SEL(sd_emmc_c, NAND_CLK_CTRL, 9, 0x7, c3_sd_emmc_parents);
+static C3_COMP_DIV(sd_emmc_c, NAND_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(sd_emmc_c, NAND_CLK_CTRL, 7);
+
+static struct clk_regmap c3_ts_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = TS_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ts_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "oscin",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_ts = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = TS_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ts",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_ts_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_eth_parents = {
+ .fw_name = "fdiv2",
+};
+
+static struct clk_fixed_factor c3_eth_125m_div = {
+ .mult = 1,
+ .div = 8,
+ .hw.init = &(struct clk_init_data) {
+ .name = "eth_125m_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &c3_eth_parents,
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_eth_125m = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ETH_CLK_CTRL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "eth_125m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_eth_125m_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_eth_rmii_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ETH_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "eth_rmii_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &c3_eth_parents,
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_eth_rmii = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ETH_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "eth_rmii",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_eth_rmii_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_mipi_dsi_meas_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp1" },
+ { .fw_name = "gp0" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv7" }
+};
+
+static C3_COMP_SEL(mipi_dsi_meas, VDIN_MEAS_CLK_CTRL, 21, 0x7, c3_mipi_dsi_meas_parents);
+static C3_COMP_DIV(mipi_dsi_meas, VDIN_MEAS_CLK_CTRL, 12, 7);
+static C3_COMP_GATE(mipi_dsi_meas, VDIN_MEAS_CLK_CTRL, 20);
+
+static const struct clk_parent_data c3_dsi_phy_parents[] = {
+ { .fw_name = "gp1" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv7" }
+};
+
+static C3_COMP_SEL(dsi_phy, MIPIDSI_PHY_CLK_CTRL, 12, 0x7, c3_dsi_phy_parents);
+static C3_COMP_DIV(dsi_phy, MIPIDSI_PHY_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(dsi_phy, MIPIDSI_PHY_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_vout_mclk_parents[] = {
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp1" },
+ { .fw_name = "fdiv7" }
+};
+
+static C3_COMP_SEL(vout_mclk, VOUTENC_CLK_CTRL, 9, 0x7, c3_vout_mclk_parents);
+static C3_COMP_DIV(vout_mclk, VOUTENC_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(vout_mclk, VOUTENC_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_vout_enc_parents[] = {
+ { .fw_name = "gp1" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv7" }
+};
+
+static C3_COMP_SEL(vout_enc, VOUTENC_CLK_CTRL, 25, 0x7, c3_vout_enc_parents);
+static C3_COMP_DIV(vout_enc, VOUTENC_CLK_CTRL, 16, 7);
+static C3_COMP_GATE(vout_enc, VOUTENC_CLK_CTRL, 24);
+
+static const struct clk_parent_data c3_hcodec_pre_parents[] = {
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv7" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp0" },
+ { .fw_name = "oscin" }
+};
+
+static C3_COMP_SEL(hcodec_0, VDEC_CLK_CTRL, 9, 0x7, c3_hcodec_pre_parents);
+static C3_COMP_DIV(hcodec_0, VDEC_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(hcodec_0, VDEC_CLK_CTRL, 8);
+
+static C3_COMP_SEL(hcodec_1, VDEC3_CLK_CTRL, 9, 0x7, c3_hcodec_pre_parents);
+static C3_COMP_DIV(hcodec_1, VDEC3_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(hcodec_1, VDEC3_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_hcodec_parents[] = {
+ { .hw = &c3_hcodec_0.hw },
+ { .hw = &c3_hcodec_1.hw }
+};
+
+static struct clk_regmap c3_hcodec = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = VDEC3_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hcodec",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = c3_hcodec_parents,
+ .num_parents = ARRAY_SIZE(c3_hcodec_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_vc9000e_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv7" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp0" }
+};
+
+static C3_COMP_SEL(vc9000e_aclk, VC9000E_CLK_CTRL, 9, 0x7, c3_vc9000e_parents);
+static C3_COMP_DIV(vc9000e_aclk, VC9000E_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(vc9000e_aclk, VC9000E_CLK_CTRL, 8);
+
+static C3_COMP_SEL(vc9000e_core, VC9000E_CLK_CTRL, 25, 0x7, c3_vc9000e_parents);
+static C3_COMP_DIV(vc9000e_core, VC9000E_CLK_CTRL, 16, 7);
+static C3_COMP_GATE(vc9000e_core, VC9000E_CLK_CTRL, 24);
+
+static const struct clk_parent_data c3_csi_phy_parents[] = {
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp1" },
+ { .fw_name = "oscin" }
+};
+
+static C3_COMP_SEL(csi_phy0, ISP0_CLK_CTRL, 25, 0x7, c3_csi_phy_parents);
+static C3_COMP_DIV(csi_phy0, ISP0_CLK_CTRL, 16, 7);
+static C3_COMP_GATE(csi_phy0, ISP0_CLK_CTRL, 24);
+
+static const struct clk_parent_data c3_dewarpa_parents[] = {
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp1" },
+ { .fw_name = "fdiv7" }
+};
+
+static C3_COMP_SEL(dewarpa, DEWARPA_CLK_CTRL, 9, 0x7, c3_dewarpa_parents);
+static C3_COMP_DIV(dewarpa, DEWARPA_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(dewarpa, DEWARPA_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_isp_parents[] = {
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp1" },
+ { .fw_name = "oscin" }
+};
+
+static C3_COMP_SEL(isp0, ISP0_CLK_CTRL, 9, 0x7, c3_isp_parents);
+static C3_COMP_DIV(isp0, ISP0_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(isp0, ISP0_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_nna_core_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "gp1" },
+ { .fw_name = "hifi" }
+};
+
+static C3_COMP_SEL(nna_core, NNA_CLK_CTRL, 9, 0x7, c3_nna_core_parents);
+static C3_COMP_DIV(nna_core, NNA_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(nna_core, NNA_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_ge2d_parents[] = {
+ { .fw_name = "oscin" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "hifi" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .hw = &c3_rtc_clk.hw }
+};
+
+static C3_COMP_SEL(ge2d, GE2D_CLK_CTRL, 9, 0x7, c3_ge2d_parents);
+static C3_COMP_DIV(ge2d, GE2D_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(ge2d, GE2D_CLK_CTRL, 8);
+
+static const struct clk_parent_data c3_vapb_parents[] = {
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi" },
+ { .fw_name = "gp1" },
+ { .fw_name = "oscin" },
+};
+
+static C3_COMP_SEL(vapb, VAPB_CLK_CTRL, 9, 0x7, c3_vapb_parents);
+static C3_COMP_DIV(vapb, VAPB_CLK_CTRL, 0, 7);
+static C3_COMP_GATE(vapb, VAPB_CLK_CTRL, 8);
+
+static struct clk_hw *c3_peripherals_hw_clks[] = {
+ [CLKID_RTC_XTAL_CLKIN] = &c3_rtc_xtal_clkin.hw,
+ [CLKID_RTC_32K_DIV] = &c3_rtc_32k_div.hw,
+ [CLKID_RTC_32K_MUX] = &c3_rtc_32k_sel.hw,
+ [CLKID_RTC_32K] = &c3_rtc_32k.hw,
+ [CLKID_RTC_CLK] = &c3_rtc_clk.hw,
+ [CLKID_SYS_RESET_CTRL] = &c3_sys_reset_ctrl.hw,
+ [CLKID_SYS_PWR_CTRL] = &c3_sys_pwr_ctrl.hw,
+ [CLKID_SYS_PAD_CTRL] = &c3_sys_pad_ctrl.hw,
+ [CLKID_SYS_CTRL] = &c3_sys_ctrl.hw,
+ [CLKID_SYS_TS_PLL] = &c3_sys_ts_pll.hw,
+ [CLKID_SYS_DEV_ARB] = &c3_sys_dev_arb.hw,
+ [CLKID_SYS_MMC_PCLK] = &c3_sys_mmc_pclk.hw,
+ [CLKID_SYS_CPU_CTRL] = &c3_sys_cpu_ctrl.hw,
+ [CLKID_SYS_JTAG_CTRL] = &c3_sys_jtag_ctrl.hw,
+ [CLKID_SYS_IR_CTRL] = &c3_sys_ir_ctrl.hw,
+ [CLKID_SYS_IRQ_CTRL] = &c3_sys_irq_ctrl.hw,
+ [CLKID_SYS_MSR_CLK] = &c3_sys_msr_clk.hw,
+ [CLKID_SYS_ROM] = &c3_sys_rom.hw,
+ [CLKID_SYS_UART_F] = &c3_sys_uart_f.hw,
+ [CLKID_SYS_CPU_ARB] = &c3_sys_cpu_apb.hw,
+ [CLKID_SYS_RSA] = &c3_sys_rsa.hw,
+ [CLKID_SYS_SAR_ADC] = &c3_sys_sar_adc.hw,
+ [CLKID_SYS_STARTUP] = &c3_sys_startup.hw,
+ [CLKID_SYS_SECURE] = &c3_sys_secure.hw,
+ [CLKID_SYS_SPIFC] = &c3_sys_spifc.hw,
+ [CLKID_SYS_NNA] = &c3_sys_nna.hw,
+ [CLKID_SYS_ETH_MAC] = &c3_sys_eth_mac.hw,
+ [CLKID_SYS_GIC] = &c3_sys_gic.hw,
+ [CLKID_SYS_RAMA] = &c3_sys_rama.hw,
+ [CLKID_SYS_BIG_NIC] = &c3_sys_big_nic.hw,
+ [CLKID_SYS_RAMB] = &c3_sys_ramb.hw,
+ [CLKID_SYS_AUDIO_PCLK] = &c3_sys_audio_pclk.hw,
+ [CLKID_SYS_PWM_KL] = &c3_sys_pwm_kl.hw,
+ [CLKID_SYS_PWM_IJ] = &c3_sys_pwm_ij.hw,
+ [CLKID_SYS_USB] = &c3_sys_usb.hw,
+ [CLKID_SYS_SD_EMMC_A] = &c3_sys_sd_emmc_a.hw,
+ [CLKID_SYS_SD_EMMC_C] = &c3_sys_sd_emmc_c.hw,
+ [CLKID_SYS_PWM_AB] = &c3_sys_pwm_ab.hw,
+ [CLKID_SYS_PWM_CD] = &c3_sys_pwm_cd.hw,
+ [CLKID_SYS_PWM_EF] = &c3_sys_pwm_ef.hw,
+ [CLKID_SYS_PWM_GH] = &c3_sys_pwm_gh.hw,
+ [CLKID_SYS_SPICC_1] = &c3_sys_spicc_1.hw,
+ [CLKID_SYS_SPICC_0] = &c3_sys_spicc_0.hw,
+ [CLKID_SYS_UART_A] = &c3_sys_uart_a.hw,
+ [CLKID_SYS_UART_B] = &c3_sys_uart_b.hw,
+ [CLKID_SYS_UART_C] = &c3_sys_uart_c.hw,
+ [CLKID_SYS_UART_D] = &c3_sys_uart_d.hw,
+ [CLKID_SYS_UART_E] = &c3_sys_uart_e.hw,
+ [CLKID_SYS_I2C_M_A] = &c3_sys_i2c_m_a.hw,
+ [CLKID_SYS_I2C_M_B] = &c3_sys_i2c_m_b.hw,
+ [CLKID_SYS_I2C_M_C] = &c3_sys_i2c_m_c.hw,
+ [CLKID_SYS_I2C_M_D] = &c3_sys_i2c_m_d.hw,
+ [CLKID_SYS_I2S_S_A] = &c3_sys_i2c_s_a.hw,
+ [CLKID_SYS_RTC] = &c3_sys_rtc.hw,
+ [CLKID_SYS_GE2D] = &c3_sys_ge2d.hw,
+ [CLKID_SYS_ISP] = &c3_sys_isp.hw,
+ [CLKID_SYS_GPV_ISP_NIC] = &c3_sys_gpv_isp_nic.hw,
+ [CLKID_SYS_GPV_CVE_NIC] = &c3_sys_gpv_cve_nic.hw,
+ [CLKID_SYS_MIPI_DSI_HOST] = &c3_sys_mipi_dsi_host.hw,
+ [CLKID_SYS_MIPI_DSI_PHY] = &c3_sys_mipi_dsi_phy.hw,
+ [CLKID_SYS_ETH_PHY] = &c3_sys_eth_phy.hw,
+ [CLKID_SYS_ACODEC] = &c3_sys_acodec.hw,
+ [CLKID_SYS_DWAP] = &c3_sys_dwap.hw,
+ [CLKID_SYS_DOS] = &c3_sys_dos.hw,
+ [CLKID_SYS_CVE] = &c3_sys_cve.hw,
+ [CLKID_SYS_VOUT] = &c3_sys_vout.hw,
+ [CLKID_SYS_VC9000E] = &c3_sys_vc9000e.hw,
+ [CLKID_SYS_PWM_MN] = &c3_sys_pwm_mn.hw,
+ [CLKID_SYS_SD_EMMC_B] = &c3_sys_sd_emmc_b.hw,
+ [CLKID_AXI_SYS_NIC] = &c3_axi_sys_nic.hw,
+ [CLKID_AXI_ISP_NIC] = &c3_axi_isp_nic.hw,
+ [CLKID_AXI_CVE_NIC] = &c3_axi_cve_nic.hw,
+ [CLKID_AXI_RAMB] = &c3_axi_ramb.hw,
+ [CLKID_AXI_RAMA] = &c3_axi_rama.hw,
+ [CLKID_AXI_CPU_DMC] = &c3_axi_cpu_dmc.hw,
+ [CLKID_AXI_NIC] = &c3_axi_nic.hw,
+ [CLKID_AXI_DMA] = &c3_axi_dma.hw,
+ [CLKID_AXI_MUX_NIC] = &c3_axi_mux_nic.hw,
+ [CLKID_AXI_CVE] = &c3_axi_cve.hw,
+ [CLKID_AXI_DEV1_DMC] = &c3_axi_dev1_dmc.hw,
+ [CLKID_AXI_DEV0_DMC] = &c3_axi_dev0_dmc.hw,
+ [CLKID_AXI_DSP_DMC] = &c3_axi_dsp_dmc.hw,
+ [CLKID_12_24M_IN] = &c3_clk_12_24m_in.hw,
+ [CLKID_12M_24M] = &c3_clk_12_24m.hw,
+ [CLKID_FCLK_25M_DIV] = &c3_fclk_25m_div.hw,
+ [CLKID_FCLK_25M] = &c3_fclk_25m.hw,
+ [CLKID_GEN_SEL] = &c3_gen_sel.hw,
+ [CLKID_GEN_DIV] = &c3_gen_div.hw,
+ [CLKID_GEN] = &c3_gen.hw,
+ [CLKID_SARADC_SEL] = &c3_saradc_sel.hw,
+ [CLKID_SARADC_DIV] = &c3_saradc_div.hw,
+ [CLKID_SARADC] = &c3_saradc.hw,
+ [CLKID_PWM_A_SEL] = &c3_pwm_a_sel.hw,
+ [CLKID_PWM_A_DIV] = &c3_pwm_a_div.hw,
+ [CLKID_PWM_A] = &c3_pwm_a.hw,
+ [CLKID_PWM_B_SEL] = &c3_pwm_b_sel.hw,
+ [CLKID_PWM_B_DIV] = &c3_pwm_b_div.hw,
+ [CLKID_PWM_B] = &c3_pwm_b.hw,
+ [CLKID_PWM_C_SEL] = &c3_pwm_c_sel.hw,
+ [CLKID_PWM_C_DIV] = &c3_pwm_c_div.hw,
+ [CLKID_PWM_C] = &c3_pwm_c.hw,
+ [CLKID_PWM_D_SEL] = &c3_pwm_d_sel.hw,
+ [CLKID_PWM_D_DIV] = &c3_pwm_d_div.hw,
+ [CLKID_PWM_D] = &c3_pwm_d.hw,
+ [CLKID_PWM_E_SEL] = &c3_pwm_e_sel.hw,
+ [CLKID_PWM_E_DIV] = &c3_pwm_e_div.hw,
+ [CLKID_PWM_E] = &c3_pwm_e.hw,
+ [CLKID_PWM_F_SEL] = &c3_pwm_f_sel.hw,
+ [CLKID_PWM_F_DIV] = &c3_pwm_f_div.hw,
+ [CLKID_PWM_F] = &c3_pwm_f.hw,
+ [CLKID_PWM_G_SEL] = &c3_pwm_g_sel.hw,
+ [CLKID_PWM_G_DIV] = &c3_pwm_g_div.hw,
+ [CLKID_PWM_G] = &c3_pwm_g.hw,
+ [CLKID_PWM_H_SEL] = &c3_pwm_h_sel.hw,
+ [CLKID_PWM_H_DIV] = &c3_pwm_h_div.hw,
+ [CLKID_PWM_H] = &c3_pwm_h.hw,
+ [CLKID_PWM_I_SEL] = &c3_pwm_i_sel.hw,
+ [CLKID_PWM_I_DIV] = &c3_pwm_i_div.hw,
+ [CLKID_PWM_I] = &c3_pwm_i.hw,
+ [CLKID_PWM_J_SEL] = &c3_pwm_j_sel.hw,
+ [CLKID_PWM_J_DIV] = &c3_pwm_j_div.hw,
+ [CLKID_PWM_J] = &c3_pwm_j.hw,
+ [CLKID_PWM_K_SEL] = &c3_pwm_k_sel.hw,
+ [CLKID_PWM_K_DIV] = &c3_pwm_k_div.hw,
+ [CLKID_PWM_K] = &c3_pwm_k.hw,
+ [CLKID_PWM_L_SEL] = &c3_pwm_l_sel.hw,
+ [CLKID_PWM_L_DIV] = &c3_pwm_l_div.hw,
+ [CLKID_PWM_L] = &c3_pwm_l.hw,
+ [CLKID_PWM_M_SEL] = &c3_pwm_m_sel.hw,
+ [CLKID_PWM_M_DIV] = &c3_pwm_m_div.hw,
+ [CLKID_PWM_M] = &c3_pwm_m.hw,
+ [CLKID_PWM_N_SEL] = &c3_pwm_n_sel.hw,
+ [CLKID_PWM_N_DIV] = &c3_pwm_n_div.hw,
+ [CLKID_PWM_N] = &c3_pwm_n.hw,
+ [CLKID_SPICC_A_SEL] = &c3_spicc_a_sel.hw,
+ [CLKID_SPICC_A_DIV] = &c3_spicc_a_div.hw,
+ [CLKID_SPICC_A] = &c3_spicc_a.hw,
+ [CLKID_SPICC_B_SEL] = &c3_spicc_b_sel.hw,
+ [CLKID_SPICC_B_DIV] = &c3_spicc_b_div.hw,
+ [CLKID_SPICC_B] = &c3_spicc_b.hw,
+ [CLKID_SPIFC_SEL] = &c3_spifc_sel.hw,
+ [CLKID_SPIFC_DIV] = &c3_spifc_div.hw,
+ [CLKID_SPIFC] = &c3_spifc.hw,
+ [CLKID_SD_EMMC_A_SEL] = &c3_sd_emmc_a_sel.hw,
+ [CLKID_SD_EMMC_A_DIV] = &c3_sd_emmc_a_div.hw,
+ [CLKID_SD_EMMC_A] = &c3_sd_emmc_a.hw,
+ [CLKID_SD_EMMC_B_SEL] = &c3_sd_emmc_b_sel.hw,
+ [CLKID_SD_EMMC_B_DIV] = &c3_sd_emmc_b_div.hw,
+ [CLKID_SD_EMMC_B] = &c3_sd_emmc_b.hw,
+ [CLKID_SD_EMMC_C_SEL] = &c3_sd_emmc_c_sel.hw,
+ [CLKID_SD_EMMC_C_DIV] = &c3_sd_emmc_c_div.hw,
+ [CLKID_SD_EMMC_C] = &c3_sd_emmc_c.hw,
+ [CLKID_TS_DIV] = &c3_ts_div.hw,
+ [CLKID_TS] = &c3_ts.hw,
+ [CLKID_ETH_125M_DIV] = &c3_eth_125m_div.hw,
+ [CLKID_ETH_125M] = &c3_eth_125m.hw,
+ [CLKID_ETH_RMII_DIV] = &c3_eth_rmii_div.hw,
+ [CLKID_ETH_RMII] = &c3_eth_rmii.hw,
+ [CLKID_MIPI_DSI_MEAS_SEL] = &c3_mipi_dsi_meas_sel.hw,
+ [CLKID_MIPI_DSI_MEAS_DIV] = &c3_mipi_dsi_meas_div.hw,
+ [CLKID_MIPI_DSI_MEAS] = &c3_mipi_dsi_meas.hw,
+ [CLKID_DSI_PHY_SEL] = &c3_dsi_phy_sel.hw,
+ [CLKID_DSI_PHY_DIV] = &c3_dsi_phy_div.hw,
+ [CLKID_DSI_PHY] = &c3_dsi_phy.hw,
+ [CLKID_VOUT_MCLK_SEL] = &c3_vout_mclk_sel.hw,
+ [CLKID_VOUT_MCLK_DIV] = &c3_vout_mclk_div.hw,
+ [CLKID_VOUT_MCLK] = &c3_vout_mclk.hw,
+ [CLKID_VOUT_ENC_SEL] = &c3_vout_enc_sel.hw,
+ [CLKID_VOUT_ENC_DIV] = &c3_vout_enc_div.hw,
+ [CLKID_VOUT_ENC] = &c3_vout_enc.hw,
+ [CLKID_HCODEC_0_SEL] = &c3_hcodec_0_sel.hw,
+ [CLKID_HCODEC_0_DIV] = &c3_hcodec_0_div.hw,
+ [CLKID_HCODEC_0] = &c3_hcodec_0.hw,
+ [CLKID_HCODEC_1_SEL] = &c3_hcodec_1_sel.hw,
+ [CLKID_HCODEC_1_DIV] = &c3_hcodec_1_div.hw,
+ [CLKID_HCODEC_1] = &c3_hcodec_1.hw,
+ [CLKID_HCODEC] = &c3_hcodec.hw,
+ [CLKID_VC9000E_ACLK_SEL] = &c3_vc9000e_aclk_sel.hw,
+ [CLKID_VC9000E_ACLK_DIV] = &c3_vc9000e_aclk_div.hw,
+ [CLKID_VC9000E_ACLK] = &c3_vc9000e_aclk.hw,
+ [CLKID_VC9000E_CORE_SEL] = &c3_vc9000e_core_sel.hw,
+ [CLKID_VC9000E_CORE_DIV] = &c3_vc9000e_core_div.hw,
+ [CLKID_VC9000E_CORE] = &c3_vc9000e_core.hw,
+ [CLKID_CSI_PHY0_SEL] = &c3_csi_phy0_sel.hw,
+ [CLKID_CSI_PHY0_DIV] = &c3_csi_phy0_div.hw,
+ [CLKID_CSI_PHY0] = &c3_csi_phy0.hw,
+ [CLKID_DEWARPA_SEL] = &c3_dewarpa_sel.hw,
+ [CLKID_DEWARPA_DIV] = &c3_dewarpa_div.hw,
+ [CLKID_DEWARPA] = &c3_dewarpa.hw,
+ [CLKID_ISP0_SEL] = &c3_isp0_sel.hw,
+ [CLKID_ISP0_DIV] = &c3_isp0_div.hw,
+ [CLKID_ISP0] = &c3_isp0.hw,
+ [CLKID_NNA_CORE_SEL] = &c3_nna_core_sel.hw,
+ [CLKID_NNA_CORE_DIV] = &c3_nna_core_div.hw,
+ [CLKID_NNA_CORE] = &c3_nna_core.hw,
+ [CLKID_GE2D_SEL] = &c3_ge2d_sel.hw,
+ [CLKID_GE2D_DIV] = &c3_ge2d_div.hw,
+ [CLKID_GE2D] = &c3_ge2d.hw,
+ [CLKID_VAPB_SEL] = &c3_vapb_sel.hw,
+ [CLKID_VAPB_DIV] = &c3_vapb_div.hw,
+ [CLKID_VAPB] = &c3_vapb.hw,
+};
+
+static const struct meson_clkc_data c3_peripherals_clkc_data = {
+ .hw_clks = {
+ .hws = c3_peripherals_hw_clks,
+ .num = ARRAY_SIZE(c3_peripherals_hw_clks),
+ },
+};
+
+static const struct of_device_id c3_peripherals_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,c3-peripherals-clkc",
+ .data = &c3_peripherals_clkc_data,
+ },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, c3_peripherals_clkc_match_table);
+
+static struct platform_driver c3_peripherals_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "c3-peripherals-clkc",
+ .of_match_table = c3_peripherals_clkc_match_table,
+ },
+};
+module_platform_driver(c3_peripherals_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic C3 Peripherals Clock Controller driver");
+MODULE_AUTHOR("Chuan Liu <chuan.liu@amlogic.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/c3-pll.c b/drivers/clk/meson/c3-pll.c
new file mode 100644
index 000000000000..dd047d17488c
--- /dev/null
+++ b/drivers/clk/meson/c3-pll.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Amlogic C3 PLL Controller Driver
+ *
+ * Copyright (c) 2023 Amlogic, inc.
+ * Author: Chuan Liu <chuan.liu@amlogic.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "meson-clkc-utils.h"
+#include <dt-bindings/clock/amlogic,c3-pll-clkc.h>
+
+#define ANACTRL_FIXPLL_CTRL4 0x50
+#define ANACTRL_GP0PLL_CTRL0 0x80
+#define ANACTRL_GP0PLL_CTRL1 0x84
+#define ANACTRL_GP0PLL_CTRL2 0x88
+#define ANACTRL_GP0PLL_CTRL3 0x8c
+#define ANACTRL_GP0PLL_CTRL4 0x90
+#define ANACTRL_GP0PLL_CTRL5 0x94
+#define ANACTRL_GP0PLL_CTRL6 0x98
+#define ANACTRL_HIFIPLL_CTRL0 0x100
+#define ANACTRL_HIFIPLL_CTRL1 0x104
+#define ANACTRL_HIFIPLL_CTRL2 0x108
+#define ANACTRL_HIFIPLL_CTRL3 0x10c
+#define ANACTRL_HIFIPLL_CTRL4 0x110
+#define ANACTRL_HIFIPLL_CTRL5 0x114
+#define ANACTRL_HIFIPLL_CTRL6 0x118
+#define ANACTRL_MPLL_CTRL0 0x180
+#define ANACTRL_MPLL_CTRL1 0x184
+#define ANACTRL_MPLL_CTRL2 0x188
+#define ANACTRL_MPLL_CTRL3 0x18c
+#define ANACTRL_MPLL_CTRL4 0x190
+
+static struct clk_regmap c3_fclk_50m_en = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_50m_en",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_50m = {
+ .mult = 1,
+ .div = 40,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_50m",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_50m_en.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_div2_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div2_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_div2_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_div2p5_div = {
+ .mult = 2,
+ .div = 5,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div2p5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_div2p5 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div2p5",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_div2p5_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_div3_div = {
+ .mult = 1,
+ .div = 3,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div3_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 20,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_div3_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_div4_div = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div4_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 21,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_div4_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_div5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 22,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_div5_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor c3_fclk_div7_div = {
+ .mult = 1,
+ .div = 7,
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div7_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fix"
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_FIXPLL_CTRL4,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_fclk_div7_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct reg_sequence c3_gp0_pll_init_regs[] = {
+ { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x0 },
+ { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 },
+ { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 },
+ { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x3927200a },
+ { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 },
+};
+
+static const struct pll_mult_range c3_gp0_pll_mult_range = {
+ .min = 125,
+ .max = 250,
+};
+
+static struct clk_regmap c3_gp0_pll_dco = {
+ .data = &(struct meson_clk_pll_data) {
+ .en = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 0,
+ .width = 9,
+ },
+ .frac = {
+ .reg_off = ANACTRL_GP0PLL_CTRL1,
+ .shift = 0,
+ .width = 19,
+ },
+ .n = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &c3_gp0_pll_mult_range,
+ .init_regs = c3_gp0_pll_init_regs,
+ .init_count = ARRAY_SIZE(c3_gp0_pll_init_regs),
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gp0_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "top",
+ },
+ .num_parents = 1,
+ },
+};
+
+/* The maximum frequency divider supports is 32, not 128(2^7) */
+static const struct clk_div_table c3_gp0_pll_od_table[] = {
+ { 0, 1 },
+ { 1, 2 },
+ { 2, 4 },
+ { 3, 8 },
+ { 4, 16 },
+ { 5, 32 },
+ { /* sentinel */ }
+};
+
+static struct clk_regmap c3_gp0_pll = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ANACTRL_GP0PLL_CTRL0,
+ .shift = 16,
+ .width = 3,
+ .table = c3_gp0_pll_od_table,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gp0_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_gp0_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct reg_sequence c3_hifi_pll_init_regs[] = {
+ { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x0 },
+ { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 },
+ { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 },
+ { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x3927200a },
+ { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 },
+};
+
+static struct clk_regmap c3_hifi_pll_dco = {
+ .data = &(struct meson_clk_pll_data) {
+ .en = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .frac = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL1,
+ .shift = 0,
+ .width = 19,
+ },
+ .n = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &c3_gp0_pll_mult_range,
+ .init_regs = c3_hifi_pll_init_regs,
+ .init_count = ARRAY_SIZE(c3_hifi_pll_init_regs),
+ .frac_max = 100000,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hifi_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "top",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap c3_hifi_pll = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 16,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hifi_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_hifi_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct reg_sequence c3_mclk_pll_init_regs[] = {
+ { .reg = ANACTRL_MPLL_CTRL1, .def = 0x1420500f },
+ { .reg = ANACTRL_MPLL_CTRL2, .def = 0x00023041 },
+ { .reg = ANACTRL_MPLL_CTRL3, .def = 0x18180000 },
+ { .reg = ANACTRL_MPLL_CTRL2, .def = 0x00023001 }
+};
+
+static const struct pll_mult_range c3_mclk_pll_mult_range = {
+ .min = 67,
+ .max = 133,
+};
+
+static struct clk_regmap c3_mclk_pll_dco = {
+ .data = &(struct meson_clk_pll_data) {
+ .en = {
+ .reg_off = ANACTRL_MPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_MPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = ANACTRL_MPLL_CTRL0,
+ .shift = 16,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = ANACTRL_MPLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_MPLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &c3_mclk_pll_mult_range,
+ .init_regs = c3_mclk_pll_init_regs,
+ .init_count = ARRAY_SIZE(c3_mclk_pll_init_regs),
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "mclk",
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct clk_div_table c3_mpll_pll_od_table[] = {
+ { 0, 1 },
+ { 1, 2 },
+ { 2, 4 },
+ { 3, 8 },
+ { 4, 16 },
+ { /* sentinel */ }
+};
+
+static struct clk_regmap c3_mclk_pll_od = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ANACTRL_MPLL_CTRL0,
+ .shift = 12,
+ .width = 3,
+ .table = c3_mpll_pll_od_table,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk_pll_od",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk_pll_dco.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* both value 0 and 1 gives divide the input rate by one */
+static struct clk_regmap c3_mclk_pll = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .shift = 16,
+ .width = 5,
+ .flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk_pll_od.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data c3_mclk_parents[] = {
+ { .hw = &c3_mclk_pll.hw },
+ { .fw_name = "mclk" },
+ { .hw = &c3_fclk_50m.hw }
+};
+
+static struct clk_regmap c3_mclk0_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .mask = 0x3,
+ .shift = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = c3_mclk_parents,
+ .num_parents = ARRAY_SIZE(c3_mclk_parents),
+ },
+};
+
+static struct clk_regmap c3_mclk0_div_en = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk0_div_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_mclk0_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .shift = 2,
+ .width = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk0_div_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_mclk0 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_mclk1_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .mask = 0x3,
+ .shift = 12,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = c3_mclk_parents,
+ .num_parents = ARRAY_SIZE(c3_mclk_parents),
+ },
+};
+
+static struct clk_regmap c3_mclk1_div_en = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .bit_idx = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk1_div_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_mclk1_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .shift = 10,
+ .width = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk1_div_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap c3_mclk1 = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ANACTRL_MPLL_CTRL4,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mclk1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &c3_mclk1_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_hw *c3_pll_hw_clks[] = {
+ [CLKID_FCLK_50M_EN] = &c3_fclk_50m_en.hw,
+ [CLKID_FCLK_50M] = &c3_fclk_50m.hw,
+ [CLKID_FCLK_DIV2_DIV] = &c3_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV2] = &c3_fclk_div2.hw,
+ [CLKID_FCLK_DIV2P5_DIV] = &c3_fclk_div2p5_div.hw,
+ [CLKID_FCLK_DIV2P5] = &c3_fclk_div2p5.hw,
+ [CLKID_FCLK_DIV3_DIV] = &c3_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV3] = &c3_fclk_div3.hw,
+ [CLKID_FCLK_DIV4_DIV] = &c3_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV4] = &c3_fclk_div4.hw,
+ [CLKID_FCLK_DIV5_DIV] = &c3_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV5] = &c3_fclk_div5.hw,
+ [CLKID_FCLK_DIV7_DIV] = &c3_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV7] = &c3_fclk_div7.hw,
+ [CLKID_GP0_PLL_DCO] = &c3_gp0_pll_dco.hw,
+ [CLKID_GP0_PLL] = &c3_gp0_pll.hw,
+ [CLKID_HIFI_PLL_DCO] = &c3_hifi_pll_dco.hw,
+ [CLKID_HIFI_PLL] = &c3_hifi_pll.hw,
+ [CLKID_MCLK_PLL_DCO] = &c3_mclk_pll_dco.hw,
+ [CLKID_MCLK_PLL_OD] = &c3_mclk_pll_od.hw,
+ [CLKID_MCLK_PLL] = &c3_mclk_pll.hw,
+ [CLKID_MCLK0_SEL] = &c3_mclk0_sel.hw,
+ [CLKID_MCLK0_SEL_EN] = &c3_mclk0_div_en.hw,
+ [CLKID_MCLK0_DIV] = &c3_mclk0_div.hw,
+ [CLKID_MCLK0] = &c3_mclk0.hw,
+ [CLKID_MCLK1_SEL] = &c3_mclk1_sel.hw,
+ [CLKID_MCLK1_SEL_EN] = &c3_mclk1_div_en.hw,
+ [CLKID_MCLK1_DIV] = &c3_mclk1_div.hw,
+ [CLKID_MCLK1] = &c3_mclk1.hw
+};
+
+static const struct meson_clkc_data c3_pll_clkc_data = {
+ .hw_clks = {
+ .hws = c3_pll_hw_clks,
+ .num = ARRAY_SIZE(c3_pll_hw_clks),
+ },
+};
+
+static const struct of_device_id c3_pll_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,c3-pll-clkc",
+ .data = &c3_pll_clkc_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, c3_pll_clkc_match_table);
+
+static struct platform_driver c3_pll_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "c3-pll-clkc",
+ .of_match_table = c3_pll_clkc_match_table,
+ },
+};
+module_platform_driver(c3_pll_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic C3 PLL Clock Controller driver");
+MODULE_AUTHOR("Chuan Liu <chuan.liu@amlogic.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-cpu-dyndiv.c b/drivers/clk/meson/clk-cpu-dyndiv.c
index 36976927fe82..83aedbfd2891 100644
--- a/drivers/clk/meson/clk-cpu-dyndiv.c
+++ b/drivers/clk/meson/clk-cpu-dyndiv.c
@@ -27,14 +27,13 @@ static unsigned long meson_clk_cpu_dyndiv_recalc_rate(struct clk_hw *hw,
NULL, 0, data->div.width);
}
-static long meson_clk_cpu_dyndiv_round_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long *prate)
+static int meson_clk_cpu_dyndiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_cpu_dyndiv_data *data = meson_clk_cpu_dyndiv_data(clk);
- return divider_round_rate(hw, rate, prate, NULL, data->div.width, 0);
+ return divider_determine_rate(hw, req, NULL, data->div.width, 0);
}
static int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -62,12 +61,14 @@ static int meson_clk_cpu_dyndiv_set_rate(struct clk_hw *hw, unsigned long rate,
};
const struct clk_ops meson_clk_cpu_dyndiv_ops = {
+ .init = clk_regmap_init,
.recalc_rate = meson_clk_cpu_dyndiv_recalc_rate,
- .round_rate = meson_clk_cpu_dyndiv_round_rate,
+ .determine_rate = meson_clk_cpu_dyndiv_determine_rate,
.set_rate = meson_clk_cpu_dyndiv_set_rate,
};
-EXPORT_SYMBOL_GPL(meson_clk_cpu_dyndiv_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_cpu_dyndiv_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic CPU Dynamic Clock divider");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-dualdiv.c b/drivers/clk/meson/clk-dualdiv.c
index c5ca23a5e3e8..787df6cdf841 100644
--- a/drivers/clk/meson/clk-dualdiv.c
+++ b/drivers/clk/meson/clk-dualdiv.c
@@ -86,18 +86,23 @@ __dualdiv_get_setting(unsigned long rate, unsigned long parent_rate,
return (struct meson_clk_dualdiv_param *)&table[best_i];
}
-static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int meson_clk_dualdiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk);
- const struct meson_clk_dualdiv_param *setting =
- __dualdiv_get_setting(rate, *parent_rate, dualdiv);
+ const struct meson_clk_dualdiv_param *setting;
- if (!setting)
- return meson_clk_dualdiv_recalc_rate(hw, *parent_rate);
+ setting = __dualdiv_get_setting(req->rate, req->best_parent_rate,
+ dualdiv);
+ if (setting)
+ req->rate = __dualdiv_param_to_rate(req->best_parent_rate,
+ setting);
+ else
+ req->rate = meson_clk_dualdiv_recalc_rate(hw,
+ req->best_parent_rate);
- return __dualdiv_param_to_rate(*parent_rate, setting);
+ return 0;
}
static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -121,18 +126,21 @@ static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate,
}
const struct clk_ops meson_clk_dualdiv_ops = {
+ .init = clk_regmap_init,
.recalc_rate = meson_clk_dualdiv_recalc_rate,
- .round_rate = meson_clk_dualdiv_round_rate,
+ .determine_rate = meson_clk_dualdiv_determine_rate,
.set_rate = meson_clk_dualdiv_set_rate,
};
-EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_dualdiv_ops, "CLK_MESON");
const struct clk_ops meson_clk_dualdiv_ro_ops = {
+ .init = clk_regmap_init,
.recalc_rate = meson_clk_dualdiv_recalc_rate,
};
-EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_dualdiv_ro_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic dual divider driver");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c
index fc9df4860872..7f8dada66e16 100644
--- a/drivers/clk/meson/clk-mpll.c
+++ b/drivers/clk/meson/clk-mpll.c
@@ -87,16 +87,22 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
return rate < 0 ? 0 : rate;
}
-static long mpll_round_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long *parent_rate)
+static int mpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
unsigned int sdm, n2;
+ long rate;
+
+ params_from_rate(req->rate, req->best_parent_rate, &sdm, &n2,
+ mpll->flags);
+
+ rate = rate_from_params(req->best_parent_rate, sdm, n2);
+ if (rate < 0)
+ return rate;
- params_from_rate(rate, *parent_rate, &sdm, &n2, mpll->flags);
- return rate_from_params(*parent_rate, sdm, n2);
+ req->rate = rate;
+ return 0;
}
static int mpll_set_rate(struct clk_hw *hw,
@@ -106,26 +112,15 @@ static int mpll_set_rate(struct clk_hw *hw,
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
unsigned int sdm, n2;
- unsigned long flags = 0;
params_from_rate(rate, parent_rate, &sdm, &n2, mpll->flags);
- if (mpll->lock)
- spin_lock_irqsave(mpll->lock, flags);
- else
- __acquire(mpll->lock);
-
/* Set the fractional part */
meson_parm_write(clk->map, &mpll->sdm, sdm);
/* Set the integer divider part */
meson_parm_write(clk->map, &mpll->n2, n2);
- if (mpll->lock)
- spin_unlock_irqrestore(mpll->lock, flags);
- else
- __release(mpll->lock);
-
return 0;
}
@@ -133,6 +128,11 @@ static int mpll_init(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
+ int ret;
+
+ ret = clk_regmap_init(hw);
+ if (ret)
+ return ret;
if (mpll->init_count)
regmap_multi_reg_write(clk->map, mpll->init_regs,
@@ -156,19 +156,21 @@ static int mpll_init(struct clk_hw *hw)
}
const struct clk_ops meson_clk_mpll_ro_ops = {
+ .init = clk_regmap_init,
.recalc_rate = mpll_recalc_rate,
- .round_rate = mpll_round_rate,
+ .determine_rate = mpll_determine_rate,
};
-EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_mpll_ro_ops, "CLK_MESON");
const struct clk_ops meson_clk_mpll_ops = {
.recalc_rate = mpll_recalc_rate,
- .round_rate = mpll_round_rate,
+ .determine_rate = mpll_determine_rate,
.set_rate = mpll_set_rate,
.init = mpll_init,
};
-EXPORT_SYMBOL_GPL(meson_clk_mpll_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_mpll_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic MPLL driver");
MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-mpll.h b/drivers/clk/meson/clk-mpll.h
index a991d568c43a..4ffd3aeef799 100644
--- a/drivers/clk/meson/clk-mpll.h
+++ b/drivers/clk/meson/clk-mpll.h
@@ -20,7 +20,6 @@ struct meson_clk_mpll_data {
struct parm misc;
const struct reg_sequence *init_regs;
unsigned int init_count;
- spinlock_t *lock;
u8 flags;
};
diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c
index a6763439f7d2..58dd982e6878 100644
--- a/drivers/clk/meson/clk-phase.c
+++ b/drivers/clk/meson/clk-phase.c
@@ -58,10 +58,11 @@ static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
}
const struct clk_ops meson_clk_phase_ops = {
+ .init = clk_regmap_init,
.get_phase = meson_clk_phase_get_phase,
.set_phase = meson_clk_phase_set_phase,
};
-EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_phase_ops, "CLK_MESON");
/*
* This is a special clock for the audio controller.
@@ -83,6 +84,11 @@ static int meson_clk_triphase_sync(struct clk_hw *hw)
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
unsigned int val;
+ int ret;
+
+ ret = clk_regmap_init(hw);
+ if (ret)
+ return ret;
/* Get phase 0 and sync it to phase 1 and 2 */
val = meson_parm_read(clk->map, &tph->ph0);
@@ -123,7 +129,7 @@ const struct clk_ops meson_clk_triphase_ops = {
.get_phase = meson_clk_triphase_get_phase,
.set_phase = meson_clk_triphase_set_phase,
};
-EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_triphase_ops, "CLK_MESON");
/*
* This is a special clock for the audio controller.
@@ -142,6 +148,11 @@ static int meson_sclk_ws_inv_sync(struct clk_hw *hw)
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
unsigned int val;
+ int ret;
+
+ ret = clk_regmap_init(hw);
+ if (ret)
+ return ret;
/* Get phase and sync the inverted value to ws */
val = meson_parm_read(clk->map, &tph->ph);
@@ -178,9 +189,9 @@ const struct clk_ops meson_sclk_ws_inv_ops = {
.get_phase = meson_sclk_ws_inv_get_phase,
.set_phase = meson_sclk_ws_inv_set_phase,
};
-EXPORT_SYMBOL_GPL(meson_sclk_ws_inv_ops);
-
+EXPORT_SYMBOL_NS_GPL(meson_sclk_ws_inv_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic phase driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 9e55617bc3b4..1ea6579a760f 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -32,7 +32,6 @@
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/module.h>
-#include <linux/rational.h>
#include "clk-regmap.h"
#include "clk-pll.h"
@@ -58,12 +57,13 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
struct meson_clk_pll_data *pll)
{
u64 rate = (u64)parent_rate * m;
+ unsigned int frac_max = pll->frac_max ? pll->frac_max :
+ (1 << pll->frac.width);
if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
u64 frac_rate = (u64)parent_rate * frac;
- rate += DIV_ROUND_UP_ULL(frac_rate,
- (1 << pll->frac.width));
+ rate += DIV_ROUND_UP_ULL(frac_rate, frac_max);
}
return DIV_ROUND_UP_ULL(rate, n);
@@ -101,7 +101,8 @@ static unsigned int __pll_params_with_frac(unsigned long rate,
unsigned int n,
struct meson_clk_pll_data *pll)
{
- unsigned int frac_max = (1 << pll->frac.width);
+ unsigned int frac_max = pll->frac_max ? pll->frac_max :
+ (1 << pll->frac.width);
u64 val = (u64)rate * n;
/* Bail out if we are already over the requested rate */
@@ -277,55 +278,79 @@ static int meson_clk_pll_wait_lock(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
- int delay = 24000000;
+ int delay = 5000;
do {
- /* Is the clock locked now ? */
+ /* Is the clock locked now ? Time out after 100ms. */
if (meson_parm_read(clk->map, &pll->l))
return 0;
- delay--;
- } while (delay > 0);
+ udelay(20);
+ } while (--delay);
return -ETIMEDOUT;
}
-static int meson_clk_pll_init(struct clk_hw *hw)
+static int meson_clk_pll_is_enabled(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
- if (pll->init_count) {
- meson_parm_write(clk->map, &pll->rst, 1);
- regmap_multi_reg_write(clk->map, pll->init_regs,
- pll->init_count);
- meson_parm_write(clk->map, &pll->rst, 0);
- }
+ if (MESON_PARM_APPLICABLE(&pll->rst) &&
+ meson_parm_read(clk->map, &pll->rst))
+ return 0;
- return 0;
+ if (!meson_parm_read(clk->map, &pll->en) ||
+ !meson_parm_read(clk->map, &pll->l))
+ return 0;
+
+ return 1;
}
-static int meson_clk_pll_is_enabled(struct clk_hw *hw)
+static int meson_clk_pll_init(struct clk_hw *hw)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ int ret;
- if (meson_parm_read(clk->map, &pll->rst) ||
- !meson_parm_read(clk->map, &pll->en) ||
- !meson_parm_read(clk->map, &pll->l))
+ ret = clk_regmap_init(hw);
+ if (ret)
+ return ret;
+
+ /*
+ * Keep the clock running, which was already initialized and enabled
+ * from the bootloader stage, to avoid any glitches.
+ */
+ if ((pll->flags & CLK_MESON_PLL_NOINIT_ENABLED) &&
+ meson_clk_pll_is_enabled(hw))
return 0;
- return 1;
+ if (pll->init_count) {
+ if (MESON_PARM_APPLICABLE(&pll->rst))
+ meson_parm_write(clk->map, &pll->rst, 1);
+
+ regmap_multi_reg_write(clk->map, pll->init_regs,
+ pll->init_count);
+
+ if (MESON_PARM_APPLICABLE(&pll->rst))
+ meson_parm_write(clk->map, &pll->rst, 0);
+ }
+
+ return 0;
}
static int meson_clk_pcie_pll_enable(struct clk_hw *hw)
{
- meson_clk_pll_init(hw);
+ int retries = 10;
- if (meson_clk_pll_wait_lock(hw))
- return -EIO;
+ do {
+ meson_clk_pll_init(hw);
+ if (!meson_clk_pll_wait_lock(hw))
+ return 0;
+ pr_info("Retry enabling PCIe PLL clock\n");
+ } while (--retries);
- return 0;
+ return -EIO;
}
static int meson_clk_pll_enable(struct clk_hw *hw)
@@ -338,13 +363,34 @@ static int meson_clk_pll_enable(struct clk_hw *hw)
return 0;
/* Make sure the pll is in reset */
- meson_parm_write(clk->map, &pll->rst, 1);
+ if (MESON_PARM_APPLICABLE(&pll->rst))
+ meson_parm_write(clk->map, &pll->rst, 1);
/* Enable the pll */
meson_parm_write(clk->map, &pll->en, 1);
/* Take the pll out reset */
- meson_parm_write(clk->map, &pll->rst, 0);
+ if (MESON_PARM_APPLICABLE(&pll->rst))
+ meson_parm_write(clk->map, &pll->rst, 0);
+
+ /*
+ * Compared with the previous SoCs, self-adaption current module
+ * is newly added for A1, keep the new power-on sequence to enable the
+ * PLL. The sequence is:
+ * 1. enable the pll, delay for 10us
+ * 2. enable the pll self-adaption current module, delay for 40us
+ * 3. enable the lock detect module
+ */
+ if (MESON_PARM_APPLICABLE(&pll->current_en)) {
+ udelay(10);
+ meson_parm_write(clk->map, &pll->current_en, 1);
+ udelay(40);
+ }
+
+ if (MESON_PARM_APPLICABLE(&pll->l_detect)) {
+ meson_parm_write(clk->map, &pll->l_detect, 1);
+ meson_parm_write(clk->map, &pll->l_detect, 0);
+ }
if (meson_clk_pll_wait_lock(hw))
return -EIO;
@@ -358,10 +404,15 @@ static void meson_clk_pll_disable(struct clk_hw *hw)
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
/* Put the pll is in reset */
- meson_parm_write(clk->map, &pll->rst, 1);
+ if (MESON_PARM_APPLICABLE(&pll->rst))
+ meson_parm_write(clk->map, &pll->rst, 1);
/* Disable the pll */
meson_parm_write(clk->map, &pll->en, 0);
+
+ /* Disable PLL internal self-adaption current module */
+ if (MESON_PARM_APPLICABLE(&pll->current_en))
+ meson_parm_write(clk->map, &pll->current_en, 0);
}
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -400,8 +451,8 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
ret = meson_clk_pll_enable(hw);
if (ret) {
- pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
- __func__, old_rate);
+ pr_warn("%s: pll %s didn't lock, trying to set old rate %lu\n",
+ __func__, clk_hw_get_name(hw), old_rate);
/*
* FIXME: Do we really need/want this HACK ?
* It looks unsafe. what happens if the clock gets into a
@@ -422,13 +473,14 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
* the other ops except set_rate since the rate is fixed.
*/
const struct clk_ops meson_clk_pcie_pll_ops = {
+ .init = clk_regmap_init,
.recalc_rate = meson_clk_pll_recalc_rate,
.determine_rate = meson_clk_pll_determine_rate,
.is_enabled = meson_clk_pll_is_enabled,
.enable = meson_clk_pcie_pll_enable,
.disable = meson_clk_pll_disable
};
-EXPORT_SYMBOL_GPL(meson_clk_pcie_pll_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_pcie_pll_ops, "CLK_MESON");
const struct clk_ops meson_clk_pll_ops = {
.init = meson_clk_pll_init,
@@ -439,15 +491,17 @@ const struct clk_ops meson_clk_pll_ops = {
.enable = meson_clk_pll_enable,
.disable = meson_clk_pll_disable
};
-EXPORT_SYMBOL_GPL(meson_clk_pll_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_pll_ops, "CLK_MESON");
const struct clk_ops meson_clk_pll_ro_ops = {
+ .init = clk_regmap_init,
.recalc_rate = meson_clk_pll_recalc_rate,
.is_enabled = meson_clk_pll_is_enabled,
};
-EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops);
+EXPORT_SYMBOL_NS_GPL(meson_clk_pll_ro_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic PLL driver");
MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h
index 367efd0f6410..949157fb7bf5 100644
--- a/drivers/clk/meson/clk-pll.h
+++ b/drivers/clk/meson/clk-pll.h
@@ -28,6 +28,7 @@ struct pll_mult_range {
}
#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
+#define CLK_MESON_PLL_NOINIT_ENABLED BIT(1)
struct meson_clk_pll_data {
struct parm en;
@@ -36,10 +37,13 @@ struct meson_clk_pll_data {
struct parm frac;
struct parm l;
struct parm rst;
+ struct parm current_en;
+ struct parm l_detect;
const struct reg_sequence *init_regs;
unsigned int init_count;
const struct pll_params_table *table;
const struct pll_mult_range *range;
+ unsigned int frac_max;
u8 flags;
};
diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c
index 8ad8977cf1c2..1ed56fe63cae 100644
--- a/drivers/clk/meson/clk-regmap.c
+++ b/drivers/clk/meson/clk-regmap.c
@@ -4,9 +4,52 @@
* Author: Jerome Brunet <jbrunet@baylibre.com>
*/
+#include <linux/device.h>
#include <linux/module.h>
+#include <linux/mfd/syscon.h>
#include "clk-regmap.h"
+int clk_regmap_init(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct device_node *np, *parent_np;
+ struct device *dev;
+
+ /* Allow regmap to be preset as it was historically done */
+ if (clk->map)
+ return 0;
+
+ /*
+ * FIXME: what follows couples the controller implementation
+ * and clk_regmap clock type. This situation is not desirable
+ * but temporary, until the controller is able to register
+ * a hook to initialize a clock type
+ */
+
+ /* Check the usual dev enabled controller with an basic IO regmap */
+ dev = clk_hw_get_dev(hw);
+ if (dev) {
+ clk->map = dev_get_regmap(dev, NULL);
+ if (clk->map)
+ return 0;
+ }
+
+ /* Move on to early and syscon based controllers */
+ np = clk_hw_get_of_node(hw);
+ if (np) {
+ parent_np = of_get_parent(np);
+ clk->map = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+
+ if (!IS_ERR_OR_NULL(clk->map))
+ return 0;
+ }
+
+ /* Bail out if regmap can't be found */
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(clk_regmap_init, "CLK_MESON");
+
static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
{
struct clk_regmap *clk = to_clk_regmap(hw);
@@ -45,16 +88,18 @@ static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
}
const struct clk_ops clk_regmap_gate_ops = {
+ .init = clk_regmap_init,
.enable = clk_regmap_gate_enable,
.disable = clk_regmap_gate_disable,
.is_enabled = clk_regmap_gate_is_enabled,
};
-EXPORT_SYMBOL_GPL(clk_regmap_gate_ops);
+EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ops, "CLK_MESON");
const struct clk_ops clk_regmap_gate_ro_ops = {
+ .init = clk_regmap_init,
.is_enabled = clk_regmap_gate_is_enabled,
};
-EXPORT_SYMBOL_GPL(clk_regmap_gate_ro_ops);
+EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ro_ops, "CLK_MESON");
static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw,
unsigned long prate)
@@ -121,17 +166,19 @@ static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,
/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */
const struct clk_ops clk_regmap_divider_ops = {
+ .init = clk_regmap_init,
.recalc_rate = clk_regmap_div_recalc_rate,
.determine_rate = clk_regmap_div_determine_rate,
.set_rate = clk_regmap_div_set_rate,
};
-EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
+EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ops, "CLK_MESON");
const struct clk_ops clk_regmap_divider_ro_ops = {
+ .init = clk_regmap_init,
.recalc_rate = clk_regmap_div_recalc_rate,
.determine_rate = clk_regmap_div_determine_rate,
};
-EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops);
+EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ro_ops, "CLK_MESON");
static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
{
@@ -170,17 +217,20 @@ static int clk_regmap_mux_determine_rate(struct clk_hw *hw,
}
const struct clk_ops clk_regmap_mux_ops = {
+ .init = clk_regmap_init,
.get_parent = clk_regmap_mux_get_parent,
.set_parent = clk_regmap_mux_set_parent,
.determine_rate = clk_regmap_mux_determine_rate,
};
-EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
+EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ops, "CLK_MESON");
const struct clk_ops clk_regmap_mux_ro_ops = {
+ .init = clk_regmap_init,
.get_parent = clk_regmap_mux_get_parent,
};
-EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
+EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ro_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h
index e365312da54e..8e5c39b023e1 100644
--- a/drivers/clk/meson/clk-regmap.h
+++ b/drivers/clk/meson/clk-regmap.h
@@ -7,6 +7,7 @@
#ifndef __CLK_REGMAP_H
#define __CLK_REGMAP_H
+#include <linux/device.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
@@ -31,6 +32,9 @@ static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw)
return container_of(hw, struct clk_regmap, hw);
}
+/* clk_regmap init op to get and cache regmap from the controllers */
+int clk_regmap_init(struct clk_hw *hw);
+
/**
* struct clk_regmap_gate_data - regmap backed gate specific data
*
@@ -114,24 +118,4 @@ clk_get_regmap_mux_data(struct clk_regmap *clk)
extern const struct clk_ops clk_regmap_mux_ops;
extern const struct clk_ops clk_regmap_mux_ro_ops;
-#define __MESON_PCLK(_name, _reg, _bit, _ops, _pname) \
-struct clk_regmap _name = { \
- .data = &(struct clk_regmap_gate_data){ \
- .offset = (_reg), \
- .bit_idx = (_bit), \
- }, \
- .hw.init = &(struct clk_init_data) { \
- .name = #_name, \
- .ops = _ops, \
- .parent_hws = (const struct clk_hw *[]) { _pname }, \
- .num_parents = 1, \
- .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
- }, \
-}
-
-#define MESON_PCLK(_name, _reg, _bit, _pname) \
- __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ops, _pname)
-
-#define MESON_PCLK_RO(_name, _reg, _bit, _pname) \
- __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ro_ops, _pname)
#endif /* __CLK_REGMAP_H */
diff --git a/drivers/clk/meson/g12a-aoclk.c b/drivers/clk/meson/g12a-aoclk.c
index b52990e574d2..96981da271fa 100644
--- a/drivers/clk/meson/g12a-aoclk.c
+++ b/drivers/clk/meson/g12a-aoclk.c
@@ -14,11 +14,13 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include "meson-aoclk.h"
-#include "g12a-aoclk.h"
#include "clk-regmap.h"
#include "clk-dualdiv.h"
+#include <dt-bindings/clock/g12a-aoclkc.h>
+#include <dt-bindings/reset/g12a-aoclkc.h>
+
/*
* AO Configuration Clock registers offsets
* Register offsets from the data sheet must be multiplied by 4.
@@ -35,46 +37,38 @@
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
+static const struct clk_parent_data g12a_ao_pclk_parents = { .fw_name = "mpeg-clk" };
+
+#define G12A_AO_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(g12a_ao_##_name, _reg, _bit, &g12a_ao_pclk_parents, _flags)
+
/*
- * Like every other peripheral clock gate in Amlogic Clock drivers,
- * we are using CLK_IGNORE_UNUSED here, so we keep the state of the
- * bootloader. The goal is to remove this flag at some point.
- * Actually removing it will require some extensive test to be done safely.
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
*/
-#define AXG_AO_GATE(_name, _reg, _bit) \
-static struct clk_regmap g12a_aoclk_##_name = { \
- .data = &(struct clk_regmap_gate_data) { \
- .offset = (_reg), \
- .bit_idx = (_bit), \
- }, \
- .hw.init = &(struct clk_init_data) { \
- .name = "g12a_ao_" #_name, \
- .ops = &clk_regmap_gate_ops, \
- .parent_data = &(const struct clk_parent_data) { \
- .fw_name = "mpeg-clk", \
- }, \
- .num_parents = 1, \
- .flags = CLK_IGNORE_UNUSED, \
- }, \
-}
+static G12A_AO_PCLK(ahb, AO_CLK_GATE0, 0, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(ir_in, AO_CLK_GATE0, 1, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(i2c_m0, AO_CLK_GATE0, 2, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(i2c_s0, AO_CLK_GATE0, 3, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(uart, AO_CLK_GATE0, 4, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(prod_i2c, AO_CLK_GATE0, 5, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(uart2, AO_CLK_GATE0, 6, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(ir_out, AO_CLK_GATE0, 7, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(saradc, AO_CLK_GATE0, 8, CLK_IGNORE_UNUSED);
-AXG_AO_GATE(ahb, AO_CLK_GATE0, 0);
-AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1);
-AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2);
-AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3);
-AXG_AO_GATE(uart, AO_CLK_GATE0, 4);
-AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5);
-AXG_AO_GATE(uart2, AO_CLK_GATE0, 6);
-AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7);
-AXG_AO_GATE(saradc, AO_CLK_GATE0, 8);
-AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0);
-AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1);
-AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2);
-AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3);
-AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4);
-AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5);
+static G12A_AO_PCLK(mailbox, AO_CLK_GATE0_SP, 0, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(m3, AO_CLK_GATE0_SP, 1, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(ahb_sram, AO_CLK_GATE0_SP, 2, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(rti, AO_CLK_GATE0_SP, 3, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(m4_fclk, AO_CLK_GATE0_SP, 4, CLK_IGNORE_UNUSED);
+static G12A_AO_PCLK(m4_hclk, AO_CLK_GATE0_SP, 5, CLK_IGNORE_UNUSED);
-static struct clk_regmap g12a_aoclk_cts_oscin = {
+static struct clk_regmap g12a_ao_cts_oscin = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTI_PWR_CNTL_REG0,
.bit_idx = 14,
@@ -101,22 +95,22 @@ static const struct meson_clk_dualdiv_param g12a_32k_div_table[] = {
/* 32k_by_oscin clock */
-static struct clk_regmap g12a_aoclk_32k_by_oscin_pre = {
+static struct clk_regmap g12a_ao_32k_by_oscin_pre = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 31,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_32k_by_oscin_pre",
+ .name = "ao_32k_by_oscin_pre",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_cts_oscin.hw
+ &g12a_ao_cts_oscin.hw
},
.num_parents = 1,
},
};
-static struct clk_regmap g12a_aoclk_32k_by_oscin_div = {
+static struct clk_regmap g12a_ao_32k_by_oscin_div = {
.data = &(struct meson_clk_dualdiv_data){
.n1 = {
.reg_off = AO_RTC_ALT_CLK_CNTL0,
@@ -146,16 +140,16 @@ static struct clk_regmap g12a_aoclk_32k_by_oscin_div = {
.table = g12a_32k_div_table,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_32k_by_oscin_div",
+ .name = "ao_32k_by_oscin_div",
.ops = &meson_clk_dualdiv_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_32k_by_oscin_pre.hw
+ &g12a_ao_32k_by_oscin_pre.hw
},
.num_parents = 1,
},
};
-static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = {
+static struct clk_regmap g12a_ao_32k_by_oscin_sel = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTC_ALT_CLK_CNTL1,
.mask = 0x1,
@@ -163,27 +157,27 @@ static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = {
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_32k_by_oscin_sel",
+ .name = "ao_32k_by_oscin_sel",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_32k_by_oscin_div.hw,
- &g12a_aoclk_32k_by_oscin_pre.hw,
+ &g12a_ao_32k_by_oscin_div.hw,
+ &g12a_ao_32k_by_oscin_pre.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap g12a_aoclk_32k_by_oscin = {
+static struct clk_regmap g12a_ao_32k_by_oscin = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 30,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_32k_by_oscin",
+ .name = "ao_32k_by_oscin",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_32k_by_oscin_sel.hw
+ &g12a_ao_32k_by_oscin_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -192,22 +186,22 @@ static struct clk_regmap g12a_aoclk_32k_by_oscin = {
/* cec clock */
-static struct clk_regmap g12a_aoclk_cec_pre = {
+static struct clk_regmap g12a_ao_cec_pre = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_CEC_CLK_CNTL_REG0,
.bit_idx = 31,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_cec_pre",
+ .name = "ao_cec_pre",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_cts_oscin.hw
+ &g12a_ao_cts_oscin.hw
},
.num_parents = 1,
},
};
-static struct clk_regmap g12a_aoclk_cec_div = {
+static struct clk_regmap g12a_ao_cec_div = {
.data = &(struct meson_clk_dualdiv_data){
.n1 = {
.reg_off = AO_CEC_CLK_CNTL_REG0,
@@ -237,16 +231,16 @@ static struct clk_regmap g12a_aoclk_cec_div = {
.table = g12a_32k_div_table,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_cec_div",
+ .name = "ao_cec_div",
.ops = &meson_clk_dualdiv_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_cec_pre.hw
+ &g12a_ao_cec_pre.hw
},
.num_parents = 1,
},
};
-static struct clk_regmap g12a_aoclk_cec_sel = {
+static struct clk_regmap g12a_ao_cec_sel = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_CEC_CLK_CNTL_REG1,
.mask = 0x1,
@@ -254,34 +248,34 @@ static struct clk_regmap g12a_aoclk_cec_sel = {
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_cec_sel",
+ .name = "ao_cec_sel",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_cec_div.hw,
- &g12a_aoclk_cec_pre.hw,
+ &g12a_ao_cec_div.hw,
+ &g12a_ao_cec_pre.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap g12a_aoclk_cec = {
+static struct clk_regmap g12a_ao_cec = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_CEC_CLK_CNTL_REG0,
.bit_idx = 30,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_cec",
+ .name = "ao_cec",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_cec_sel.hw
+ &g12a_ao_cec_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap g12a_aoclk_cts_rtc_oscin = {
+static struct clk_regmap g12a_ao_cts_rtc_oscin = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
@@ -289,10 +283,10 @@ static struct clk_regmap g12a_aoclk_cts_rtc_oscin = {
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_cts_rtc_oscin",
+ .name = "ao_cts_rtc_oscin",
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
- { .hw = &g12a_aoclk_32k_by_oscin.hw },
+ { .hw = &g12a_ao_32k_by_oscin.hw },
{ .fw_name = "ext-32k-0", },
},
.num_parents = 2,
@@ -300,7 +294,7 @@ static struct clk_regmap g12a_aoclk_cts_rtc_oscin = {
},
};
-static struct clk_regmap g12a_aoclk_clk81 = {
+static struct clk_regmap g12a_ao_clk81 = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
@@ -308,68 +302,74 @@ static struct clk_regmap g12a_aoclk_clk81 = {
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
+ /*
+ * NOTE: this is one of the infamous clock the pwm driver
+ * can request directly by its global name. It's wrong but
+ * there is not much we can do about it until the support
+ * for the old pwm bindings is dropped
+ */
.name = "g12a_ao_clk81",
.ops = &clk_regmap_mux_ro_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "mpeg-clk", },
- { .hw = &g12a_aoclk_cts_rtc_oscin.hw },
+ { .hw = &g12a_ao_cts_rtc_oscin.hw },
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap g12a_aoclk_saradc_mux = {
+static struct clk_regmap g12a_ao_saradc_mux = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_SAR_CLK,
.mask = 0x3,
.shift = 9,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_saradc_mux",
+ .name = "ao_saradc_mux",
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
- { .hw = &g12a_aoclk_clk81.hw },
+ { .hw = &g12a_ao_clk81.hw },
},
.num_parents = 2,
},
};
-static struct clk_regmap g12a_aoclk_saradc_div = {
+static struct clk_regmap g12a_ao_saradc_div = {
.data = &(struct clk_regmap_div_data) {
.offset = AO_SAR_CLK,
.shift = 0,
.width = 8,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_saradc_div",
+ .name = "ao_saradc_div",
.ops = &clk_regmap_divider_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_saradc_mux.hw
+ &g12a_ao_saradc_mux.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap g12a_aoclk_saradc_gate = {
+static struct clk_regmap g12a_ao_saradc_gate = {
.data = &(struct clk_regmap_gate_data) {
.offset = AO_SAR_CLK,
.bit_idx = 8,
},
.hw.init = &(struct clk_init_data){
- .name = "g12a_ao_saradc_gate",
+ .name = "ao_saradc_gate",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_aoclk_saradc_div.hw
+ &g12a_ao_saradc_div.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static const unsigned int g12a_aoclk_reset[] = {
+static const unsigned int g12a_ao_reset[] = {
[RESET_AO_IR_IN] = 16,
[RESET_AO_UART] = 17,
[RESET_AO_I2C_M] = 18,
@@ -379,98 +379,68 @@ static const unsigned int g12a_aoclk_reset[] = {
[RESET_AO_IR_OUT] = 23,
};
-static struct clk_regmap *g12a_aoclk_regmap[] = {
- &g12a_aoclk_ahb,
- &g12a_aoclk_ir_in,
- &g12a_aoclk_i2c_m0,
- &g12a_aoclk_i2c_s0,
- &g12a_aoclk_uart,
- &g12a_aoclk_prod_i2c,
- &g12a_aoclk_uart2,
- &g12a_aoclk_ir_out,
- &g12a_aoclk_saradc,
- &g12a_aoclk_mailbox,
- &g12a_aoclk_m3,
- &g12a_aoclk_ahb_sram,
- &g12a_aoclk_rti,
- &g12a_aoclk_m4_fclk,
- &g12a_aoclk_m4_hclk,
- &g12a_aoclk_cts_oscin,
- &g12a_aoclk_32k_by_oscin_pre,
- &g12a_aoclk_32k_by_oscin_div,
- &g12a_aoclk_32k_by_oscin_sel,
- &g12a_aoclk_32k_by_oscin,
- &g12a_aoclk_cec_pre,
- &g12a_aoclk_cec_div,
- &g12a_aoclk_cec_sel,
- &g12a_aoclk_cec,
- &g12a_aoclk_cts_rtc_oscin,
- &g12a_aoclk_clk81,
- &g12a_aoclk_saradc_mux,
- &g12a_aoclk_saradc_div,
- &g12a_aoclk_saradc_gate,
-};
-
-static const struct clk_hw_onecell_data g12a_aoclk_onecell_data = {
- .hws = {
- [CLKID_AO_AHB] = &g12a_aoclk_ahb.hw,
- [CLKID_AO_IR_IN] = &g12a_aoclk_ir_in.hw,
- [CLKID_AO_I2C_M0] = &g12a_aoclk_i2c_m0.hw,
- [CLKID_AO_I2C_S0] = &g12a_aoclk_i2c_s0.hw,
- [CLKID_AO_UART] = &g12a_aoclk_uart.hw,
- [CLKID_AO_PROD_I2C] = &g12a_aoclk_prod_i2c.hw,
- [CLKID_AO_UART2] = &g12a_aoclk_uart2.hw,
- [CLKID_AO_IR_OUT] = &g12a_aoclk_ir_out.hw,
- [CLKID_AO_SAR_ADC] = &g12a_aoclk_saradc.hw,
- [CLKID_AO_MAILBOX] = &g12a_aoclk_mailbox.hw,
- [CLKID_AO_M3] = &g12a_aoclk_m3.hw,
- [CLKID_AO_AHB_SRAM] = &g12a_aoclk_ahb_sram.hw,
- [CLKID_AO_RTI] = &g12a_aoclk_rti.hw,
- [CLKID_AO_M4_FCLK] = &g12a_aoclk_m4_fclk.hw,
- [CLKID_AO_M4_HCLK] = &g12a_aoclk_m4_hclk.hw,
- [CLKID_AO_CLK81] = &g12a_aoclk_clk81.hw,
- [CLKID_AO_SAR_ADC_SEL] = &g12a_aoclk_saradc_mux.hw,
- [CLKID_AO_SAR_ADC_DIV] = &g12a_aoclk_saradc_div.hw,
- [CLKID_AO_SAR_ADC_CLK] = &g12a_aoclk_saradc_gate.hw,
- [CLKID_AO_CTS_OSCIN] = &g12a_aoclk_cts_oscin.hw,
- [CLKID_AO_32K_PRE] = &g12a_aoclk_32k_by_oscin_pre.hw,
- [CLKID_AO_32K_DIV] = &g12a_aoclk_32k_by_oscin_div.hw,
- [CLKID_AO_32K_SEL] = &g12a_aoclk_32k_by_oscin_sel.hw,
- [CLKID_AO_32K] = &g12a_aoclk_32k_by_oscin.hw,
- [CLKID_AO_CEC_PRE] = &g12a_aoclk_cec_pre.hw,
- [CLKID_AO_CEC_DIV] = &g12a_aoclk_cec_div.hw,
- [CLKID_AO_CEC_SEL] = &g12a_aoclk_cec_sel.hw,
- [CLKID_AO_CEC] = &g12a_aoclk_cec.hw,
- [CLKID_AO_CTS_RTC_OSCIN] = &g12a_aoclk_cts_rtc_oscin.hw,
- },
- .num = NR_CLKS,
+static struct clk_hw *g12a_ao_hw_clks[] = {
+ [CLKID_AO_AHB] = &g12a_ao_ahb.hw,
+ [CLKID_AO_IR_IN] = &g12a_ao_ir_in.hw,
+ [CLKID_AO_I2C_M0] = &g12a_ao_i2c_m0.hw,
+ [CLKID_AO_I2C_S0] = &g12a_ao_i2c_s0.hw,
+ [CLKID_AO_UART] = &g12a_ao_uart.hw,
+ [CLKID_AO_PROD_I2C] = &g12a_ao_prod_i2c.hw,
+ [CLKID_AO_UART2] = &g12a_ao_uart2.hw,
+ [CLKID_AO_IR_OUT] = &g12a_ao_ir_out.hw,
+ [CLKID_AO_SAR_ADC] = &g12a_ao_saradc.hw,
+ [CLKID_AO_MAILBOX] = &g12a_ao_mailbox.hw,
+ [CLKID_AO_M3] = &g12a_ao_m3.hw,
+ [CLKID_AO_AHB_SRAM] = &g12a_ao_ahb_sram.hw,
+ [CLKID_AO_RTI] = &g12a_ao_rti.hw,
+ [CLKID_AO_M4_FCLK] = &g12a_ao_m4_fclk.hw,
+ [CLKID_AO_M4_HCLK] = &g12a_ao_m4_hclk.hw,
+ [CLKID_AO_CLK81] = &g12a_ao_clk81.hw,
+ [CLKID_AO_SAR_ADC_SEL] = &g12a_ao_saradc_mux.hw,
+ [CLKID_AO_SAR_ADC_DIV] = &g12a_ao_saradc_div.hw,
+ [CLKID_AO_SAR_ADC_CLK] = &g12a_ao_saradc_gate.hw,
+ [CLKID_AO_CTS_OSCIN] = &g12a_ao_cts_oscin.hw,
+ [CLKID_AO_32K_PRE] = &g12a_ao_32k_by_oscin_pre.hw,
+ [CLKID_AO_32K_DIV] = &g12a_ao_32k_by_oscin_div.hw,
+ [CLKID_AO_32K_SEL] = &g12a_ao_32k_by_oscin_sel.hw,
+ [CLKID_AO_32K] = &g12a_ao_32k_by_oscin.hw,
+ [CLKID_AO_CEC_PRE] = &g12a_ao_cec_pre.hw,
+ [CLKID_AO_CEC_DIV] = &g12a_ao_cec_div.hw,
+ [CLKID_AO_CEC_SEL] = &g12a_ao_cec_sel.hw,
+ [CLKID_AO_CEC] = &g12a_ao_cec.hw,
+ [CLKID_AO_CTS_RTC_OSCIN] = &g12a_ao_cts_rtc_oscin.hw,
};
-static const struct meson_aoclk_data g12a_aoclkc_data = {
+static const struct meson_aoclk_data g12a_ao_clkc_data = {
.reset_reg = AO_RTI_GEN_CNTL_REG0,
- .num_reset = ARRAY_SIZE(g12a_aoclk_reset),
- .reset = g12a_aoclk_reset,
- .num_clks = ARRAY_SIZE(g12a_aoclk_regmap),
- .clks = g12a_aoclk_regmap,
- .hw_data = &g12a_aoclk_onecell_data,
+ .num_reset = ARRAY_SIZE(g12a_ao_reset),
+ .reset = g12a_ao_reset,
+ .clkc_data = {
+ .hw_clks = {
+ .hws = g12a_ao_hw_clks,
+ .num = ARRAY_SIZE(g12a_ao_hw_clks),
+ },
+ },
};
-static const struct of_device_id g12a_aoclkc_match_table[] = {
+static const struct of_device_id g12a_ao_clkc_match_table[] = {
{
.compatible = "amlogic,meson-g12a-aoclkc",
- .data = &g12a_aoclkc_data,
+ .data = &g12a_ao_clkc_data.clkc_data,
},
{ }
};
-MODULE_DEVICE_TABLE(of, g12a_aoclkc_match_table);
+MODULE_DEVICE_TABLE(of, g12a_ao_clkc_match_table);
-static struct platform_driver g12a_aoclkc_driver = {
+static struct platform_driver g12a_ao_clkc_driver = {
.probe = meson_aoclkc_probe,
.driver = {
.name = "g12a-aoclkc",
- .of_match_table = g12a_aoclkc_match_table,
+ .of_match_table = g12a_ao_clkc_match_table,
},
};
+module_platform_driver(g12a_ao_clkc_driver);
-module_platform_driver(g12a_aoclkc_driver);
-MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Amlogic G12A Always-ON Clock Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/g12a-aoclk.h b/drivers/clk/meson/g12a-aoclk.h
deleted file mode 100644
index a67c8a7cd7c4..000000000000
--- a/drivers/clk/meson/g12a-aoclk.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
-/*
- * Copyright (c) 2019 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- */
-
-#ifndef __G12A_AOCLKC_H
-#define __G12A_AOCLKC_H
-
-/*
- * CLKID index values
- *
- * These indices are entirely contrived and do not map onto the hardware.
- * It has now been decided to expose everything by default in the DT header:
- * include/dt-bindings/clock/g12a-aoclkc.h. Only the clocks ids we don't want
- * to expose, such as the internal muxes and dividers of composite clocks,
- * will remain defined here.
- */
-#define CLKID_AO_SAR_ADC_DIV 17
-#define CLKID_AO_32K_PRE 20
-#define CLKID_AO_32K_DIV 21
-#define CLKID_AO_32K_SEL 22
-#define CLKID_AO_CEC_PRE 24
-#define CLKID_AO_CEC_DIV 25
-#define CLKID_AO_CEC_SEL 26
-
-#define NR_CLKS 29
-
-#include <dt-bindings/clock/g12a-aoclkc.h>
-#include <dt-bindings/reset/g12a-aoclkc.h>
-
-#endif /* __G12A_AOCLKC_H */
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index 310accf94830..185b6348251d 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -12,7 +12,7 @@
#include <linux/clk-provider.h>
#include <linux/init.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/module.h>
@@ -22,10 +22,120 @@
#include "clk-regmap.h"
#include "clk-cpu-dyndiv.h"
#include "vid-pll-div.h"
-#include "meson-eeclk.h"
-#include "g12a.h"
-
-static DEFINE_SPINLOCK(meson_clk_lock);
+#include "vclk.h"
+#include "meson-clkc-utils.h"
+
+#include <dt-bindings/clock/g12a-clkc.h>
+
+#define HHI_MIPI_CNTL0 0x000
+#define HHI_MIPI_CNTL1 0x004
+#define HHI_MIPI_CNTL2 0x008
+#define HHI_MIPI_STS 0x00c
+#define HHI_GP0_PLL_CNTL0 0x040
+#define HHI_GP0_PLL_CNTL1 0x044
+#define HHI_GP0_PLL_CNTL2 0x048
+#define HHI_GP0_PLL_CNTL3 0x04c
+#define HHI_GP0_PLL_CNTL4 0x050
+#define HHI_GP0_PLL_CNTL5 0x054
+#define HHI_GP0_PLL_CNTL6 0x058
+#define HHI_GP0_PLL_STS 0x05c
+#define HHI_GP1_PLL_CNTL0 0x060
+#define HHI_GP1_PLL_CNTL1 0x064
+#define HHI_GP1_PLL_CNTL2 0x068
+#define HHI_GP1_PLL_CNTL3 0x06c
+#define HHI_GP1_PLL_CNTL4 0x070
+#define HHI_GP1_PLL_CNTL5 0x074
+#define HHI_GP1_PLL_CNTL6 0x078
+#define HHI_GP1_PLL_STS 0x07c
+#define HHI_PCIE_PLL_CNTL0 0x098
+#define HHI_PCIE_PLL_CNTL1 0x09c
+#define HHI_PCIE_PLL_CNTL2 0x0a0
+#define HHI_PCIE_PLL_CNTL3 0x0a4
+#define HHI_PCIE_PLL_CNTL4 0x0a8
+#define HHI_PCIE_PLL_CNTL5 0x0ac
+#define HHI_PCIE_PLL_STS 0x0b8
+#define HHI_HIFI_PLL_CNTL0 0x0d8
+#define HHI_HIFI_PLL_CNTL1 0x0dc
+#define HHI_HIFI_PLL_CNTL2 0x0e0
+#define HHI_HIFI_PLL_CNTL3 0x0e4
+#define HHI_HIFI_PLL_CNTL4 0x0e8
+#define HHI_HIFI_PLL_CNTL5 0x0ec
+#define HHI_HIFI_PLL_CNTL6 0x0f0
+#define HHI_VIID_CLK_DIV 0x128
+#define HHI_VIID_CLK_CNTL 0x12c
+#define HHI_GCLK_MPEG0 0x140
+#define HHI_GCLK_MPEG1 0x144
+#define HHI_GCLK_MPEG2 0x148
+#define HHI_GCLK_OTHER 0x150
+#define HHI_GCLK_OTHER2 0x154
+#define HHI_SYS_CPU_CLK_CNTL1 0x15c
+#define HHI_VID_CLK_DIV 0x164
+#define HHI_MPEG_CLK_CNTL 0x174
+#define HHI_AUD_CLK_CNTL 0x178
+#define HHI_VID_CLK_CNTL 0x17c
+#define HHI_TS_CLK_CNTL 0x190
+#define HHI_VID_CLK_CNTL2 0x194
+#define HHI_SYS_CPU_CLK_CNTL0 0x19c
+#define HHI_VID_PLL_CLK_DIV 0x1a0
+#define HHI_MALI_CLK_CNTL 0x1b0
+#define HHI_VPU_CLKC_CNTL 0x1b4
+#define HHI_VPU_CLK_CNTL 0x1bc
+#define HHI_ISP_CLK_CNTL 0x1c0
+#define HHI_NNA_CLK_CNTL 0x1c8
+#define HHI_HDMI_CLK_CNTL 0x1cc
+#define HHI_VDEC_CLK_CNTL 0x1e0
+#define HHI_VDEC2_CLK_CNTL 0x1e4
+#define HHI_VDEC3_CLK_CNTL 0x1e8
+#define HHI_VDEC4_CLK_CNTL 0x1ec
+#define HHI_HDCP22_CLK_CNTL 0x1f0
+#define HHI_VAPBCLK_CNTL 0x1f4
+#define HHI_SYS_CPUB_CLK_CNTL1 0x200
+#define HHI_SYS_CPUB_CLK_CNTL 0x208
+#define HHI_VPU_CLKB_CNTL 0x20c
+#define HHI_SYS_CPU_CLK_CNTL2 0x210
+#define HHI_SYS_CPU_CLK_CNTL3 0x214
+#define HHI_SYS_CPU_CLK_CNTL4 0x218
+#define HHI_SYS_CPU_CLK_CNTL5 0x21c
+#define HHI_SYS_CPU_CLK_CNTL6 0x220
+#define HHI_GEN_CLK_CNTL 0x228
+#define HHI_VDIN_MEAS_CLK_CNTL 0x250
+#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254
+#define HHI_NAND_CLK_CNTL 0x25c
+#define HHI_SD_EMMC_CLK_CNTL 0x264
+#define HHI_MPLL_CNTL0 0x278
+#define HHI_MPLL_CNTL1 0x27c
+#define HHI_MPLL_CNTL2 0x280
+#define HHI_MPLL_CNTL3 0x284
+#define HHI_MPLL_CNTL4 0x288
+#define HHI_MPLL_CNTL5 0x28c
+#define HHI_MPLL_CNTL6 0x290
+#define HHI_MPLL_CNTL7 0x294
+#define HHI_MPLL_CNTL8 0x298
+#define HHI_FIX_PLL_CNTL0 0x2a0
+#define HHI_FIX_PLL_CNTL1 0x2a4
+#define HHI_FIX_PLL_CNTL3 0x2ac
+#define HHI_SYS_PLL_CNTL0 0x2f4
+#define HHI_SYS_PLL_CNTL1 0x2f8
+#define HHI_SYS_PLL_CNTL2 0x2fc
+#define HHI_SYS_PLL_CNTL3 0x300
+#define HHI_SYS_PLL_CNTL4 0x304
+#define HHI_SYS_PLL_CNTL5 0x308
+#define HHI_SYS_PLL_CNTL6 0x30c
+#define HHI_HDMI_PLL_CNTL0 0x320
+#define HHI_HDMI_PLL_CNTL1 0x324
+#define HHI_HDMI_PLL_CNTL2 0x328
+#define HHI_HDMI_PLL_CNTL3 0x32c
+#define HHI_HDMI_PLL_CNTL4 0x330
+#define HHI_HDMI_PLL_CNTL5 0x334
+#define HHI_HDMI_PLL_CNTL6 0x338
+#define HHI_SPICC_CLK_CNTL 0x3dc
+#define HHI_SYS1_PLL_CNTL0 0x380
+#define HHI_SYS1_PLL_CNTL1 0x384
+#define HHI_SYS1_PLL_CNTL2 0x388
+#define HHI_SYS1_PLL_CNTL3 0x38c
+#define HHI_SYS1_PLL_CNTL4 0x390
+#define HHI_SYS1_PLL_CNTL5 0x394
+#define HHI_SYS1_PLL_CNTL6 0x398
static struct clk_regmap g12a_fixed_pll_dco = {
.data = &(struct meson_clk_pll_data){
@@ -276,6 +386,451 @@ static struct clk_fixed_factor g12b_sys1_pll_div16 = {
},
};
+static const struct pll_mult_range g12a_gp0_pll_mult_range = {
+ .min = 125,
+ .max = 255,
+};
+
+/*
+ * Internal gp0 pll emulation configuration parameters
+ */
+static const struct reg_sequence g12a_gp0_pll_init_regs[] = {
+ { .reg = HHI_GP0_PLL_CNTL1, .def = 0x00000000 },
+ { .reg = HHI_GP0_PLL_CNTL2, .def = 0x00000000 },
+ { .reg = HHI_GP0_PLL_CNTL3, .def = 0x48681c00 },
+ { .reg = HHI_GP0_PLL_CNTL4, .def = 0x33771290 },
+ { .reg = HHI_GP0_PLL_CNTL5, .def = 0x39272000 },
+ { .reg = HHI_GP0_PLL_CNTL6, .def = 0x56540000 },
+};
+
+static struct clk_regmap g12a_gp0_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_GP0_PLL_CNTL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_GP0_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &g12a_gp0_pll_mult_range,
+ .init_regs = g12a_gp0_pll_init_regs,
+ .init_count = ARRAY_SIZE(g12a_gp0_pll_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_gp0_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_GP0_PLL_CNTL0,
+ .shift = 16,
+ .width = 3,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_gp0_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sm1_gp1_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_GP1_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_GP1_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_GP1_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_GP1_PLL_CNTL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = HHI_GP1_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_GP1_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp1_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ /* This clock feeds the DSU, avoid disabling it */
+ .flags = CLK_IS_CRITICAL,
+ },
+};
+
+static struct clk_regmap sm1_gp1_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_GP1_PLL_CNTL0,
+ .shift = 16,
+ .width = 3,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp1_pll",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &sm1_gp1_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+/*
+ * Internal hifi pll emulation configuration parameters
+ */
+static const struct reg_sequence g12a_hifi_pll_init_regs[] = {
+ { .reg = HHI_HIFI_PLL_CNTL1, .def = 0x00000000 },
+ { .reg = HHI_HIFI_PLL_CNTL2, .def = 0x00000000 },
+ { .reg = HHI_HIFI_PLL_CNTL3, .def = 0x6a285c00 },
+ { .reg = HHI_HIFI_PLL_CNTL4, .def = 0x65771290 },
+ { .reg = HHI_HIFI_PLL_CNTL5, .def = 0x39272000 },
+ { .reg = HHI_HIFI_PLL_CNTL6, .def = 0x56540000 },
+};
+
+static struct clk_regmap g12a_hifi_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_HIFI_PLL_CNTL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HIFI_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &g12a_gp0_pll_mult_range,
+ .init_regs = g12a_hifi_pll_init_regs,
+ .init_count = ARRAY_SIZE(g12a_hifi_pll_init_regs),
+ .flags = CLK_MESON_PLL_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_hifi_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HIFI_PLL_CNTL0,
+ .shift = 16,
+ .width = 2,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_hifi_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * The Meson G12A PCIE PLL is fined tuned to deliver a very precise
+ * 100MHz reference clock for the PCIe Analog PHY, and thus requires
+ * a strict register sequence to enable the PLL.
+ */
+static const struct reg_sequence g12a_pcie_pll_init_regs[] = {
+ { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x20090496 },
+ { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x30090496 },
+ { .reg = HHI_PCIE_PLL_CNTL1, .def = 0x00000000 },
+ { .reg = HHI_PCIE_PLL_CNTL2, .def = 0x00001100 },
+ { .reg = HHI_PCIE_PLL_CNTL3, .def = 0x10058e00 },
+ { .reg = HHI_PCIE_PLL_CNTL4, .def = 0x000100c0 },
+ { .reg = HHI_PCIE_PLL_CNTL5, .def = 0x68000048 },
+ { .reg = HHI_PCIE_PLL_CNTL5, .def = 0x68000068, .delay_us = 20 },
+ { .reg = HHI_PCIE_PLL_CNTL4, .def = 0x008100c0, .delay_us = 10 },
+ { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x34090496 },
+ { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x14090496, .delay_us = 10 },
+ { .reg = HHI_PCIE_PLL_CNTL2, .def = 0x00001000 },
+};
+
+/* Keep a single entry table for recalc/round_rate() ops */
+static const struct pll_params_table g12a_pcie_pll_table[] = {
+ PLL_PARAMS(150, 1),
+ {0, 0},
+};
+
+static struct clk_regmap g12a_pcie_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_PCIE_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_PCIE_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_PCIE_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_PCIE_PLL_CNTL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .l = {
+ .reg_off = HHI_PCIE_PLL_CNTL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_PCIE_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .table = g12a_pcie_pll_table,
+ .init_regs = g12a_pcie_pll_init_regs,
+ .init_count = ARRAY_SIZE(g12a_pcie_pll_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pcie_pll_dco",
+ .ops = &meson_clk_pcie_pll_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_pcie_pll_dco_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "pcie_pll_dco_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_pcie_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_pcie_pll_od = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_PCIE_PLL_CNTL0,
+ .shift = 16,
+ .width = 5,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST |
+ CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ALLOW_ZERO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "pcie_pll_od",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_pcie_pll_dco_div2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor g12a_pcie_pll = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "pcie_pll_pll",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_pcie_pll_od.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_HDMI_PLL_CNTL1,
+ .shift = 0,
+ .width = 16,
+ },
+ .l = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 30,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HDMI_PLL_CNTL0,
+ .shift = 29,
+ .width = 1,
+ },
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ /*
+ * Display directly handle hdmi pll registers ATM, we need
+ * NOCACHE to keep our view of the clock as accurate as possible
+ */
+ .flags = CLK_GET_RATE_NOCACHE,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll_od = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_PLL_CNTL0,
+ .shift = 16,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_od",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_hdmi_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll_od2 = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_PLL_CNTL0,
+ .shift = 18,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_od2",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_hdmi_pll_od.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12a_hdmi_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_HDMI_PLL_CNTL0,
+ .shift = 20,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_hdmi_pll_od2.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+ },
+};
+
static struct clk_fixed_factor g12a_fclk_div2_div = {
.mult = 1,
.div = 2,
@@ -349,36 +904,166 @@ static struct clk_regmap g12a_fclk_div3 = {
},
};
-/* Datasheet names this field as "premux0" */
-static struct clk_regmap g12a_cpu_clk_premux0 = {
+
+static struct clk_fixed_factor g12a_fclk_div4_div = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &g12a_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 21,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fclk_div4_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &g12a_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 22,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fclk_div5_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div7_div = {
+ .mult = 1,
+ .div = 7,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &g12a_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fclk_div7_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_fclk_div2p5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2p5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_fclk_div2p5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_FIX_PLL_CNTL1,
+ .bit_idx = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2p5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fclk_div2p5_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor g12a_mpll_50m_div = {
+ .mult = 1,
+ .div = 80,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_50m_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap g12a_mpll_50m = {
.data = &(struct clk_regmap_mux_data){
- .offset = HHI_SYS_CPU_CLK_CNTL0,
- .mask = 0x3,
- .shift = 0,
- .flags = CLK_MUX_ROUND_CLOSEST,
+ .offset = HHI_FIX_PLL_CNTL3,
+ .mask = 0x1,
+ .shift = 5,
},
.hw.init = &(struct clk_init_data){
- .name = "cpu_clk_dyn0_sel",
- .ops = &clk_regmap_mux_ops,
+ .name = "mpll_50m",
+ .ops = &clk_regmap_mux_ro_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
- { .hw = &g12a_fclk_div2.hw },
- { .hw = &g12a_fclk_div3.hw },
+ { .hw = &g12a_mpll_50m_div.hw },
},
- .num_parents = 3,
- .flags = CLK_SET_RATE_PARENT,
+ .num_parents = 2,
},
};
-/* Datasheet names this field as "premux1" */
-static struct clk_regmap g12a_cpu_clk_premux1 = {
+static struct clk_fixed_factor g12a_mpll_prediv = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_prediv",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+/* Datasheet names this field as "premux0" */
+static struct clk_regmap g12a_cpu_clk_dyn0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL0,
.mask = 0x3,
- .shift = 16,
+ .shift = 0,
+ .flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
- .name = "cpu_clk_dyn1_sel",
+ .name = "cpu_clk_dyn0_sel",
.ops = &clk_regmap_mux_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "xtal", },
@@ -386,13 +1071,12 @@ static struct clk_regmap g12a_cpu_clk_premux1 = {
{ .hw = &g12a_fclk_div3.hw },
},
.num_parents = 3,
- /* This sub-tree is used a parking clock */
- .flags = CLK_SET_RATE_NO_REPARENT
+ .flags = CLK_SET_RATE_PARENT,
},
};
/* Datasheet names this field as "mux0_divn_tcnt" */
-static struct clk_regmap g12a_cpu_clk_mux0_div = {
+static struct clk_regmap g12a_cpu_clk_dyn0_div = {
.data = &(struct meson_clk_cpu_dyndiv_data){
.div = {
.reg_off = HHI_SYS_CPU_CLK_CNTL0,
@@ -409,7 +1093,7 @@ static struct clk_regmap g12a_cpu_clk_mux0_div = {
.name = "cpu_clk_dyn0_div",
.ops = &meson_clk_cpu_dyndiv_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_cpu_clk_premux0.hw
+ &g12a_cpu_clk_dyn0_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -417,7 +1101,7 @@ static struct clk_regmap g12a_cpu_clk_mux0_div = {
};
/* Datasheet names this field as "postmux0" */
-static struct clk_regmap g12a_cpu_clk_postmux0 = {
+static struct clk_regmap g12a_cpu_clk_dyn0 = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL0,
.mask = 0x1,
@@ -428,16 +1112,37 @@ static struct clk_regmap g12a_cpu_clk_postmux0 = {
.name = "cpu_clk_dyn0",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_cpu_clk_premux0.hw,
- &g12a_cpu_clk_mux0_div.hw,
+ &g12a_cpu_clk_dyn0_sel.hw,
+ &g12a_cpu_clk_dyn0_div.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
+/* Datasheet names this field as "premux1" */
+static struct clk_regmap g12a_cpu_clk_dyn1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SYS_CPU_CLK_CNTL0,
+ .mask = 0x3,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_clk_dyn1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", },
+ { .hw = &g12a_fclk_div2.hw },
+ { .hw = &g12a_fclk_div3.hw },
+ },
+ .num_parents = 3,
+ /* This sub-tree is used a parking clock */
+ .flags = CLK_SET_RATE_NO_REPARENT
+ },
+};
+
/* Datasheet names this field as "Mux1_divn_tcnt" */
-static struct clk_regmap g12a_cpu_clk_mux1_div = {
+static struct clk_regmap g12a_cpu_clk_dyn1_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_SYS_CPU_CLK_CNTL0,
.shift = 20,
@@ -447,14 +1152,14 @@ static struct clk_regmap g12a_cpu_clk_mux1_div = {
.name = "cpu_clk_dyn1_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_cpu_clk_premux1.hw
+ &g12a_cpu_clk_dyn1_sel.hw
},
.num_parents = 1,
},
};
/* Datasheet names this field as "postmux1" */
-static struct clk_regmap g12a_cpu_clk_postmux1 = {
+static struct clk_regmap g12a_cpu_clk_dyn1 = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL0,
.mask = 0x1,
@@ -464,8 +1169,8 @@ static struct clk_regmap g12a_cpu_clk_postmux1 = {
.name = "cpu_clk_dyn1",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_cpu_clk_premux1.hw,
- &g12a_cpu_clk_mux1_div.hw,
+ &g12a_cpu_clk_dyn1_sel.hw,
+ &g12a_cpu_clk_dyn1_div.hw,
},
.num_parents = 2,
/* This sub-tree is used a parking clock */
@@ -485,8 +1190,8 @@ static struct clk_regmap g12a_cpu_clk_dyn = {
.name = "cpu_clk_dyn",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_cpu_clk_postmux0.hw,
- &g12a_cpu_clk_postmux1.hw,
+ &g12a_cpu_clk_dyn0.hw,
+ &g12a_cpu_clk_dyn1.hw,
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
@@ -534,7 +1239,7 @@ static struct clk_regmap g12b_cpu_clk = {
};
/* Datasheet names this field as "premux0" */
-static struct clk_regmap g12b_cpub_clk_premux0 = {
+static struct clk_regmap g12b_cpub_clk_dyn0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPUB_CLK_CNTL,
.mask = 0x3,
@@ -555,7 +1260,7 @@ static struct clk_regmap g12b_cpub_clk_premux0 = {
};
/* Datasheet names this field as "mux0_divn_tcnt" */
-static struct clk_regmap g12b_cpub_clk_mux0_div = {
+static struct clk_regmap g12b_cpub_clk_dyn0_div = {
.data = &(struct meson_clk_cpu_dyndiv_data){
.div = {
.reg_off = HHI_SYS_CPUB_CLK_CNTL,
@@ -572,7 +1277,7 @@ static struct clk_regmap g12b_cpub_clk_mux0_div = {
.name = "cpub_clk_dyn0_div",
.ops = &meson_clk_cpu_dyndiv_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_premux0.hw
+ &g12b_cpub_clk_dyn0_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -580,7 +1285,7 @@ static struct clk_regmap g12b_cpub_clk_mux0_div = {
};
/* Datasheet names this field as "postmux0" */
-static struct clk_regmap g12b_cpub_clk_postmux0 = {
+static struct clk_regmap g12b_cpub_clk_dyn0 = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPUB_CLK_CNTL,
.mask = 0x1,
@@ -591,8 +1296,8 @@ static struct clk_regmap g12b_cpub_clk_postmux0 = {
.name = "cpub_clk_dyn0",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_premux0.hw,
- &g12b_cpub_clk_mux0_div.hw
+ &g12b_cpub_clk_dyn0_sel.hw,
+ &g12b_cpub_clk_dyn0_div.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
@@ -600,7 +1305,7 @@ static struct clk_regmap g12b_cpub_clk_postmux0 = {
};
/* Datasheet names this field as "premux1" */
-static struct clk_regmap g12b_cpub_clk_premux1 = {
+static struct clk_regmap g12b_cpub_clk_dyn1_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPUB_CLK_CNTL,
.mask = 0x3,
@@ -621,7 +1326,7 @@ static struct clk_regmap g12b_cpub_clk_premux1 = {
};
/* Datasheet names this field as "Mux1_divn_tcnt" */
-static struct clk_regmap g12b_cpub_clk_mux1_div = {
+static struct clk_regmap g12b_cpub_clk_dyn1_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_SYS_CPUB_CLK_CNTL,
.shift = 20,
@@ -631,14 +1336,14 @@ static struct clk_regmap g12b_cpub_clk_mux1_div = {
.name = "cpub_clk_dyn1_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_premux1.hw
+ &g12b_cpub_clk_dyn1_sel.hw
},
.num_parents = 1,
},
};
/* Datasheet names this field as "postmux1" */
-static struct clk_regmap g12b_cpub_clk_postmux1 = {
+static struct clk_regmap g12b_cpub_clk_dyn1 = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPUB_CLK_CNTL,
.mask = 0x1,
@@ -648,8 +1353,8 @@ static struct clk_regmap g12b_cpub_clk_postmux1 = {
.name = "cpub_clk_dyn1",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_premux1.hw,
- &g12b_cpub_clk_mux1_div.hw
+ &g12b_cpub_clk_dyn1_sel.hw,
+ &g12b_cpub_clk_dyn1_div.hw
},
.num_parents = 2,
/* This sub-tree is used a parking clock */
@@ -669,8 +1374,8 @@ static struct clk_regmap g12b_cpub_clk_dyn = {
.name = "cpub_clk_dyn",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_postmux0.hw,
- &g12b_cpub_clk_postmux1.hw
+ &g12b_cpub_clk_dyn0.hw,
+ &g12b_cpub_clk_dyn1.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
@@ -697,10 +1402,8 @@ static struct clk_regmap g12b_cpub_clk = {
},
};
-static struct clk_regmap sm1_gp1_pll;
-
/* Datasheet names this field as "premux0" */
-static struct clk_regmap sm1_dsu_clk_premux0 = {
+static struct clk_regmap sm1_dsu_clk_dyn0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL5,
.mask = 0x3,
@@ -719,28 +1422,8 @@ static struct clk_regmap sm1_dsu_clk_premux0 = {
},
};
-/* Datasheet names this field as "premux1" */
-static struct clk_regmap sm1_dsu_clk_premux1 = {
- .data = &(struct clk_regmap_mux_data){
- .offset = HHI_SYS_CPU_CLK_CNTL5,
- .mask = 0x3,
- .shift = 16,
- },
- .hw.init = &(struct clk_init_data){
- .name = "dsu_clk_dyn1_sel",
- .ops = &clk_regmap_mux_ro_ops,
- .parent_data = (const struct clk_parent_data []) {
- { .fw_name = "xtal", },
- { .hw = &g12a_fclk_div2.hw },
- { .hw = &g12a_fclk_div3.hw },
- { .hw = &sm1_gp1_pll.hw },
- },
- .num_parents = 4,
- },
-};
-
/* Datasheet names this field as "Mux0_divn_tcnt" */
-static struct clk_regmap sm1_dsu_clk_mux0_div = {
+static struct clk_regmap sm1_dsu_clk_dyn0_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_SYS_CPU_CLK_CNTL5,
.shift = 4,
@@ -750,14 +1433,14 @@ static struct clk_regmap sm1_dsu_clk_mux0_div = {
.name = "dsu_clk_dyn0_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &sm1_dsu_clk_premux0.hw
+ &sm1_dsu_clk_dyn0_sel.hw
},
.num_parents = 1,
},
};
/* Datasheet names this field as "postmux0" */
-static struct clk_regmap sm1_dsu_clk_postmux0 = {
+static struct clk_regmap sm1_dsu_clk_dyn0 = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL5,
.mask = 0x1,
@@ -767,15 +1450,35 @@ static struct clk_regmap sm1_dsu_clk_postmux0 = {
.name = "dsu_clk_dyn0",
.ops = &clk_regmap_mux_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &sm1_dsu_clk_premux0.hw,
- &sm1_dsu_clk_mux0_div.hw,
+ &sm1_dsu_clk_dyn0_sel.hw,
+ &sm1_dsu_clk_dyn0_div.hw,
},
.num_parents = 2,
},
};
+/* Datasheet names this field as "premux1" */
+static struct clk_regmap sm1_dsu_clk_dyn1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SYS_CPU_CLK_CNTL5,
+ .mask = 0x3,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dsu_clk_dyn1_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", },
+ { .hw = &g12a_fclk_div2.hw },
+ { .hw = &g12a_fclk_div3.hw },
+ { .hw = &sm1_gp1_pll.hw },
+ },
+ .num_parents = 4,
+ },
+};
+
/* Datasheet names this field as "Mux1_divn_tcnt" */
-static struct clk_regmap sm1_dsu_clk_mux1_div = {
+static struct clk_regmap sm1_dsu_clk_dyn1_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_SYS_CPU_CLK_CNTL5,
.shift = 20,
@@ -785,14 +1488,14 @@ static struct clk_regmap sm1_dsu_clk_mux1_div = {
.name = "dsu_clk_dyn1_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &sm1_dsu_clk_premux1.hw
+ &sm1_dsu_clk_dyn1_sel.hw
},
.num_parents = 1,
},
};
/* Datasheet names this field as "postmux1" */
-static struct clk_regmap sm1_dsu_clk_postmux1 = {
+static struct clk_regmap sm1_dsu_clk_dyn1 = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL5,
.mask = 0x1,
@@ -802,8 +1505,8 @@ static struct clk_regmap sm1_dsu_clk_postmux1 = {
.name = "dsu_clk_dyn1",
.ops = &clk_regmap_mux_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &sm1_dsu_clk_premux1.hw,
- &sm1_dsu_clk_mux1_div.hw,
+ &sm1_dsu_clk_dyn1_sel.hw,
+ &sm1_dsu_clk_dyn1_div.hw,
},
.num_parents = 2,
},
@@ -820,8 +1523,8 @@ static struct clk_regmap sm1_dsu_clk_dyn = {
.name = "dsu_clk_dyn",
.ops = &clk_regmap_mux_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &sm1_dsu_clk_postmux0.hw,
- &sm1_dsu_clk_postmux1.hw,
+ &sm1_dsu_clk_dyn0.hw,
+ &sm1_dsu_clk_dyn1.hw,
},
.num_parents = 2,
},
@@ -933,7 +1636,7 @@ static struct notifier_block g12a_cpu_clk_mux_nb = {
.notifier_call = g12a_cpu_clk_mux_notifier_cb,
};
-struct g12a_cpu_clk_postmux_nb_data {
+struct g12a_cpu_clk_dyn_nb_data {
struct notifier_block nb;
struct clk_hw *xtal;
struct clk_hw *cpu_clk_dyn;
@@ -942,33 +1645,33 @@ struct g12a_cpu_clk_postmux_nb_data {
struct clk_hw *cpu_clk_premux1;
};
-static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
- unsigned long event, void *data)
+static int g12a_cpu_clk_dyn_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
{
- struct g12a_cpu_clk_postmux_nb_data *nb_data =
- container_of(nb, struct g12a_cpu_clk_postmux_nb_data, nb);
+ struct g12a_cpu_clk_dyn_nb_data *nb_data =
+ container_of(nb, struct g12a_cpu_clk_dyn_nb_data, nb);
switch (event) {
case PRE_RATE_CHANGE:
/*
- * This notifier means cpu_clk_postmux0 clock will be changed
+ * This notifier means cpu_clk_dyn0 clock will be changed
* to feed cpu_clk, this is the current path :
* cpu_clk
* \- cpu_clk_dyn
- * \- cpu_clk_postmux0
- * \- cpu_clk_muxX_div
- * \- cpu_clk_premux0
+ * \- cpu_clk_dyn0
+ * \- cpu_clk_dyn0_div
+ * \- cpu_clk_dyn0_sel
* \- fclk_div3 or fclk_div2
* OR
- * \- cpu_clk_premux0
+ * \- cpu_clk_dyn0_sel
* \- fclk_div3 or fclk_div2
*/
- /* Setup cpu_clk_premux1 to xtal */
+ /* Setup cpu_clk_dyn1_sel to xtal */
clk_hw_set_parent(nb_data->cpu_clk_premux1,
nb_data->xtal);
- /* Setup cpu_clk_postmux1 to bypass divider */
+ /* Setup cpu_clk_dyn1 to bypass divider */
clk_hw_set_parent(nb_data->cpu_clk_postmux1,
nb_data->cpu_clk_premux1);
@@ -980,8 +1683,8 @@ static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
* Now, cpu_clk is 24MHz in the current path :
* cpu_clk
* \- cpu_clk_dyn
- * \- cpu_clk_postmux1
- * \- cpu_clk_premux1
+ * \- cpu_clk_dyn1
+ * \- cpu_clk_dyn1_sel
* \- xtal
*/
@@ -991,8 +1694,8 @@ static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
case POST_RATE_CHANGE:
/*
- * The cpu_clk_postmux0 has ben updated, now switch back
- * cpu_clk_dyn to cpu_clk_postmux0 and take the changes
+ * The cpu_clk_dyn0 has ben updated, now switch back
+ * cpu_clk_dyn to cpu_clk_dyn0 and take the changes
* in account.
*/
@@ -1004,12 +1707,12 @@ static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
* new path :
* cpu_clk
* \- cpu_clk_dyn
- * \- cpu_clk_postmux0
- * \- cpu_clk_muxX_div
- * \- cpu_clk_premux0
+ * \- cpu_clk_dyn0
+ * \- cpu_clk_dyn0_div
+ * \- cpu_clk_dyn0_sel
* \- fclk_div3 or fclk_div2
* OR
- * \- cpu_clk_premux0
+ * \- cpu_clk_dyn0_sel
* \- fclk_div3 or fclk_div2
*/
@@ -1022,20 +1725,20 @@ static int g12a_cpu_clk_postmux_notifier_cb(struct notifier_block *nb,
}
}
-static struct g12a_cpu_clk_postmux_nb_data g12a_cpu_clk_postmux0_nb_data = {
+static struct g12a_cpu_clk_dyn_nb_data g12a_cpu_clk_dyn0_nb_data = {
.cpu_clk_dyn = &g12a_cpu_clk_dyn.hw,
- .cpu_clk_postmux0 = &g12a_cpu_clk_postmux0.hw,
- .cpu_clk_postmux1 = &g12a_cpu_clk_postmux1.hw,
- .cpu_clk_premux1 = &g12a_cpu_clk_premux1.hw,
- .nb.notifier_call = g12a_cpu_clk_postmux_notifier_cb,
+ .cpu_clk_postmux0 = &g12a_cpu_clk_dyn0.hw,
+ .cpu_clk_postmux1 = &g12a_cpu_clk_dyn1.hw,
+ .cpu_clk_premux1 = &g12a_cpu_clk_dyn1_sel.hw,
+ .nb.notifier_call = g12a_cpu_clk_dyn_notifier_cb,
};
-static struct g12a_cpu_clk_postmux_nb_data g12b_cpub_clk_postmux0_nb_data = {
+static struct g12a_cpu_clk_dyn_nb_data g12b_cpub_clk_dyn0_nb_data = {
.cpu_clk_dyn = &g12b_cpub_clk_dyn.hw,
- .cpu_clk_postmux0 = &g12b_cpub_clk_postmux0.hw,
- .cpu_clk_postmux1 = &g12b_cpub_clk_postmux1.hw,
- .cpu_clk_premux1 = &g12b_cpub_clk_premux1.hw,
- .nb.notifier_call = g12a_cpu_clk_postmux_notifier_cb,
+ .cpu_clk_postmux0 = &g12b_cpub_clk_dyn0.hw,
+ .cpu_clk_postmux1 = &g12b_cpub_clk_dyn1.hw,
+ .cpu_clk_premux1 = &g12b_cpub_clk_dyn1_sel.hw,
+ .nb.notifier_call = g12a_cpu_clk_dyn_notifier_cb,
};
struct g12a_sys_pll_nb_data {
@@ -1136,8 +1839,18 @@ static struct clk_regmap g12a_cpu_clk_div16_en = {
.hw.init = &(struct clk_init_data) {
.name = "cpu_clk_div16_en",
.ops = &clk_regmap_gate_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_cpu_clk.hw
+ .parent_data = &(const struct clk_parent_data) {
+ /*
+ * Note:
+ * G12A and G12B have different cpu clocks (with
+ * different struct clk_hw). We fallback to the global
+ * naming string mechanism so this clock picks
+ * up the appropriate one. Same goes for the other
+ * clock using cpu cluster A clock output and present
+ * on both G12 variant.
+ */
+ .name = "cpu_clk",
+ .index = -1,
},
.num_parents = 1,
/*
@@ -1202,7 +1915,10 @@ static struct clk_regmap g12a_cpu_clk_apb_div = {
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_apb_div",
.ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) { &g12a_cpu_clk.hw },
+ .parent_data = &(const struct clk_parent_data) {
+ .name = "cpu_clk",
+ .index = -1,
+ },
.num_parents = 1,
},
};
@@ -1236,7 +1952,10 @@ static struct clk_regmap g12a_cpu_clk_atb_div = {
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_atb_div",
.ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) { &g12a_cpu_clk.hw },
+ .parent_data = &(const struct clk_parent_data) {
+ .name = "cpu_clk",
+ .index = -1,
+ },
.num_parents = 1,
},
};
@@ -1270,7 +1989,10 @@ static struct clk_regmap g12a_cpu_clk_axi_div = {
.hw.init = &(struct clk_init_data){
.name = "cpu_clk_axi_div",
.ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) { &g12a_cpu_clk.hw },
+ .parent_data = &(const struct clk_parent_data) {
+ .name = "cpu_clk",
+ .index = -1,
+ },
.num_parents = 1,
},
};
@@ -1305,13 +2027,6 @@ static struct clk_regmap g12a_cpu_clk_trace_div = {
.name = "cpu_clk_trace_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_data = &(const struct clk_parent_data) {
- /*
- * Note:
- * G12A and G12B have different cpu_clks (with
- * different struct clk_hw). We fallback to the global
- * naming string mechanism so cpu_clk_trace_div picks
- * up the appropriate one.
- */
.name = "cpu_clk",
.index = -1,
},
@@ -1429,27 +2144,29 @@ static struct clk_fixed_factor g12b_cpub_clk_div8 = {
},
};
-static u32 mux_table_cpub[] = { 1, 2, 3, 4, 5, 6, 7 };
+static u32 g12b_cpub_clk_if_parents_val_table[] = { 1, 2, 3, 4, 5, 6, 7 };
+static const struct clk_hw *g12b_cpub_clk_if_parents[] = {
+ &g12b_cpub_clk_div2.hw,
+ &g12b_cpub_clk_div3.hw,
+ &g12b_cpub_clk_div4.hw,
+ &g12b_cpub_clk_div5.hw,
+ &g12b_cpub_clk_div6.hw,
+ &g12b_cpub_clk_div7.hw,
+ &g12b_cpub_clk_div8.hw,
+};
+
static struct clk_regmap g12b_cpub_clk_apb_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPUB_CLK_CNTL1,
.mask = 7,
.shift = 3,
- .table = mux_table_cpub,
+ .table = g12b_cpub_clk_if_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_apb_sel",
.ops = &clk_regmap_mux_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_div2.hw,
- &g12b_cpub_clk_div3.hw,
- &g12b_cpub_clk_div4.hw,
- &g12b_cpub_clk_div5.hw,
- &g12b_cpub_clk_div6.hw,
- &g12b_cpub_clk_div7.hw,
- &g12b_cpub_clk_div8.hw
- },
- .num_parents = 7,
+ .parent_hws = g12b_cpub_clk_if_parents,
+ .num_parents = ARRAY_SIZE(g12b_cpub_clk_if_parents),
},
};
@@ -1478,21 +2195,13 @@ static struct clk_regmap g12b_cpub_clk_atb_sel = {
.offset = HHI_SYS_CPUB_CLK_CNTL1,
.mask = 7,
.shift = 6,
- .table = mux_table_cpub,
+ .table = g12b_cpub_clk_if_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_atb_sel",
.ops = &clk_regmap_mux_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_div2.hw,
- &g12b_cpub_clk_div3.hw,
- &g12b_cpub_clk_div4.hw,
- &g12b_cpub_clk_div5.hw,
- &g12b_cpub_clk_div6.hw,
- &g12b_cpub_clk_div7.hw,
- &g12b_cpub_clk_div8.hw
- },
- .num_parents = 7,
+ .parent_hws = g12b_cpub_clk_if_parents,
+ .num_parents = ARRAY_SIZE(g12b_cpub_clk_if_parents),
},
};
@@ -1521,21 +2230,13 @@ static struct clk_regmap g12b_cpub_clk_axi_sel = {
.offset = HHI_SYS_CPUB_CLK_CNTL1,
.mask = 7,
.shift = 9,
- .table = mux_table_cpub,
+ .table = g12b_cpub_clk_if_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_axi_sel",
.ops = &clk_regmap_mux_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_div2.hw,
- &g12b_cpub_clk_div3.hw,
- &g12b_cpub_clk_div4.hw,
- &g12b_cpub_clk_div5.hw,
- &g12b_cpub_clk_div6.hw,
- &g12b_cpub_clk_div7.hw,
- &g12b_cpub_clk_div8.hw
- },
- .num_parents = 7,
+ .parent_hws = g12b_cpub_clk_if_parents,
+ .num_parents = ARRAY_SIZE(g12b_cpub_clk_if_parents),
},
};
@@ -1564,21 +2265,13 @@ static struct clk_regmap g12b_cpub_clk_trace_sel = {
.offset = HHI_SYS_CPUB_CLK_CNTL1,
.mask = 7,
.shift = 20,
- .table = mux_table_cpub,
+ .table = g12b_cpub_clk_if_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cpub_clk_trace_sel",
.ops = &clk_regmap_mux_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12b_cpub_clk_div2.hw,
- &g12b_cpub_clk_div3.hw,
- &g12b_cpub_clk_div4.hw,
- &g12b_cpub_clk_div5.hw,
- &g12b_cpub_clk_div6.hw,
- &g12b_cpub_clk_div7.hw,
- &g12b_cpub_clk_div8.hw
- },
- .num_parents = 7,
+ .parent_hws = g12b_cpub_clk_if_parents,
+ .num_parents = ARRAY_SIZE(g12b_cpub_clk_if_parents),
},
};
@@ -1602,600 +2295,6 @@ static struct clk_regmap g12b_cpub_clk_trace = {
},
};
-static const struct pll_mult_range g12a_gp0_pll_mult_range = {
- .min = 125,
- .max = 255,
-};
-
-/*
- * Internal gp0 pll emulation configuration parameters
- */
-static const struct reg_sequence g12a_gp0_init_regs[] = {
- { .reg = HHI_GP0_PLL_CNTL1, .def = 0x00000000 },
- { .reg = HHI_GP0_PLL_CNTL2, .def = 0x00000000 },
- { .reg = HHI_GP0_PLL_CNTL3, .def = 0x48681c00 },
- { .reg = HHI_GP0_PLL_CNTL4, .def = 0x33771290 },
- { .reg = HHI_GP0_PLL_CNTL5, .def = 0x39272000 },
- { .reg = HHI_GP0_PLL_CNTL6, .def = 0x56540000 },
-};
-
-static struct clk_regmap g12a_gp0_pll_dco = {
- .data = &(struct meson_clk_pll_data){
- .en = {
- .reg_off = HHI_GP0_PLL_CNTL0,
- .shift = 28,
- .width = 1,
- },
- .m = {
- .reg_off = HHI_GP0_PLL_CNTL0,
- .shift = 0,
- .width = 8,
- },
- .n = {
- .reg_off = HHI_GP0_PLL_CNTL0,
- .shift = 10,
- .width = 5,
- },
- .frac = {
- .reg_off = HHI_GP0_PLL_CNTL1,
- .shift = 0,
- .width = 17,
- },
- .l = {
- .reg_off = HHI_GP0_PLL_CNTL0,
- .shift = 31,
- .width = 1,
- },
- .rst = {
- .reg_off = HHI_GP0_PLL_CNTL0,
- .shift = 29,
- .width = 1,
- },
- .range = &g12a_gp0_pll_mult_range,
- .init_regs = g12a_gp0_init_regs,
- .init_count = ARRAY_SIZE(g12a_gp0_init_regs),
- },
- .hw.init = &(struct clk_init_data){
- .name = "gp0_pll_dco",
- .ops = &meson_clk_pll_ops,
- .parent_data = &(const struct clk_parent_data) {
- .fw_name = "xtal",
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_gp0_pll = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_GP0_PLL_CNTL0,
- .shift = 16,
- .width = 3,
- .flags = (CLK_DIVIDER_POWER_OF_TWO |
- CLK_DIVIDER_ROUND_CLOSEST),
- },
- .hw.init = &(struct clk_init_data){
- .name = "gp0_pll",
- .ops = &clk_regmap_divider_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_gp0_pll_dco.hw
- },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_regmap sm1_gp1_pll_dco = {
- .data = &(struct meson_clk_pll_data){
- .en = {
- .reg_off = HHI_GP1_PLL_CNTL0,
- .shift = 28,
- .width = 1,
- },
- .m = {
- .reg_off = HHI_GP1_PLL_CNTL0,
- .shift = 0,
- .width = 8,
- },
- .n = {
- .reg_off = HHI_GP1_PLL_CNTL0,
- .shift = 10,
- .width = 5,
- },
- .frac = {
- .reg_off = HHI_GP1_PLL_CNTL1,
- .shift = 0,
- .width = 17,
- },
- .l = {
- .reg_off = HHI_GP1_PLL_CNTL0,
- .shift = 31,
- .width = 1,
- },
- .rst = {
- .reg_off = HHI_GP1_PLL_CNTL0,
- .shift = 29,
- .width = 1,
- },
- },
- .hw.init = &(struct clk_init_data){
- .name = "gp1_pll_dco",
- .ops = &meson_clk_pll_ro_ops,
- .parent_data = &(const struct clk_parent_data) {
- .fw_name = "xtal",
- },
- .num_parents = 1,
- /* This clock feeds the DSU, avoid disabling it */
- .flags = CLK_IS_CRITICAL,
- },
-};
-
-static struct clk_regmap sm1_gp1_pll = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_GP1_PLL_CNTL0,
- .shift = 16,
- .width = 3,
- .flags = (CLK_DIVIDER_POWER_OF_TWO |
- CLK_DIVIDER_ROUND_CLOSEST),
- },
- .hw.init = &(struct clk_init_data){
- .name = "gp1_pll",
- .ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &sm1_gp1_pll_dco.hw
- },
- .num_parents = 1,
- },
-};
-
-/*
- * Internal hifi pll emulation configuration parameters
- */
-static const struct reg_sequence g12a_hifi_init_regs[] = {
- { .reg = HHI_HIFI_PLL_CNTL1, .def = 0x00000000 },
- { .reg = HHI_HIFI_PLL_CNTL2, .def = 0x00000000 },
- { .reg = HHI_HIFI_PLL_CNTL3, .def = 0x6a285c00 },
- { .reg = HHI_HIFI_PLL_CNTL4, .def = 0x65771290 },
- { .reg = HHI_HIFI_PLL_CNTL5, .def = 0x39272000 },
- { .reg = HHI_HIFI_PLL_CNTL6, .def = 0x56540000 },
-};
-
-static struct clk_regmap g12a_hifi_pll_dco = {
- .data = &(struct meson_clk_pll_data){
- .en = {
- .reg_off = HHI_HIFI_PLL_CNTL0,
- .shift = 28,
- .width = 1,
- },
- .m = {
- .reg_off = HHI_HIFI_PLL_CNTL0,
- .shift = 0,
- .width = 8,
- },
- .n = {
- .reg_off = HHI_HIFI_PLL_CNTL0,
- .shift = 10,
- .width = 5,
- },
- .frac = {
- .reg_off = HHI_HIFI_PLL_CNTL1,
- .shift = 0,
- .width = 17,
- },
- .l = {
- .reg_off = HHI_HIFI_PLL_CNTL0,
- .shift = 31,
- .width = 1,
- },
- .rst = {
- .reg_off = HHI_HIFI_PLL_CNTL0,
- .shift = 29,
- .width = 1,
- },
- .range = &g12a_gp0_pll_mult_range,
- .init_regs = g12a_hifi_init_regs,
- .init_count = ARRAY_SIZE(g12a_hifi_init_regs),
- .flags = CLK_MESON_PLL_ROUND_CLOSEST,
- },
- .hw.init = &(struct clk_init_data){
- .name = "hifi_pll_dco",
- .ops = &meson_clk_pll_ops,
- .parent_data = &(const struct clk_parent_data) {
- .fw_name = "xtal",
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_hifi_pll = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_HIFI_PLL_CNTL0,
- .shift = 16,
- .width = 2,
- .flags = (CLK_DIVIDER_POWER_OF_TWO |
- CLK_DIVIDER_ROUND_CLOSEST),
- },
- .hw.init = &(struct clk_init_data){
- .name = "hifi_pll",
- .ops = &clk_regmap_divider_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_hifi_pll_dco.hw
- },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- },
-};
-
-/*
- * The Meson G12A PCIE PLL is fined tuned to deliver a very precise
- * 100MHz reference clock for the PCIe Analog PHY, and thus requires
- * a strict register sequence to enable the PLL.
- */
-static const struct reg_sequence g12a_pcie_pll_init_regs[] = {
- { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x20090496 },
- { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x30090496 },
- { .reg = HHI_PCIE_PLL_CNTL1, .def = 0x00000000 },
- { .reg = HHI_PCIE_PLL_CNTL2, .def = 0x00001100 },
- { .reg = HHI_PCIE_PLL_CNTL3, .def = 0x10058e00 },
- { .reg = HHI_PCIE_PLL_CNTL4, .def = 0x000100c0 },
- { .reg = HHI_PCIE_PLL_CNTL5, .def = 0x68000048 },
- { .reg = HHI_PCIE_PLL_CNTL5, .def = 0x68000068, .delay_us = 20 },
- { .reg = HHI_PCIE_PLL_CNTL4, .def = 0x008100c0, .delay_us = 10 },
- { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x34090496 },
- { .reg = HHI_PCIE_PLL_CNTL0, .def = 0x14090496, .delay_us = 10 },
- { .reg = HHI_PCIE_PLL_CNTL2, .def = 0x00001000 },
-};
-
-/* Keep a single entry table for recalc/round_rate() ops */
-static const struct pll_params_table g12a_pcie_pll_table[] = {
- PLL_PARAMS(150, 1),
- {0, 0},
-};
-
-static struct clk_regmap g12a_pcie_pll_dco = {
- .data = &(struct meson_clk_pll_data){
- .en = {
- .reg_off = HHI_PCIE_PLL_CNTL0,
- .shift = 28,
- .width = 1,
- },
- .m = {
- .reg_off = HHI_PCIE_PLL_CNTL0,
- .shift = 0,
- .width = 8,
- },
- .n = {
- .reg_off = HHI_PCIE_PLL_CNTL0,
- .shift = 10,
- .width = 5,
- },
- .frac = {
- .reg_off = HHI_PCIE_PLL_CNTL1,
- .shift = 0,
- .width = 12,
- },
- .l = {
- .reg_off = HHI_PCIE_PLL_CNTL0,
- .shift = 31,
- .width = 1,
- },
- .rst = {
- .reg_off = HHI_PCIE_PLL_CNTL0,
- .shift = 29,
- .width = 1,
- },
- .table = g12a_pcie_pll_table,
- .init_regs = g12a_pcie_pll_init_regs,
- .init_count = ARRAY_SIZE(g12a_pcie_pll_init_regs),
- },
- .hw.init = &(struct clk_init_data){
- .name = "pcie_pll_dco",
- .ops = &meson_clk_pcie_pll_ops,
- .parent_data = &(const struct clk_parent_data) {
- .fw_name = "xtal",
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_fixed_factor g12a_pcie_pll_dco_div2 = {
- .mult = 1,
- .div = 2,
- .hw.init = &(struct clk_init_data){
- .name = "pcie_pll_dco_div2",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_pcie_pll_dco.hw
- },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_regmap g12a_pcie_pll_od = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_PCIE_PLL_CNTL0,
- .shift = 16,
- .width = 5,
- .flags = CLK_DIVIDER_ROUND_CLOSEST |
- CLK_DIVIDER_ONE_BASED |
- CLK_DIVIDER_ALLOW_ZERO,
- },
- .hw.init = &(struct clk_init_data){
- .name = "pcie_pll_od",
- .ops = &clk_regmap_divider_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_pcie_pll_dco_div2.hw
- },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_fixed_factor g12a_pcie_pll = {
- .mult = 1,
- .div = 2,
- .hw.init = &(struct clk_init_data){
- .name = "pcie_pll_pll",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_pcie_pll_od.hw
- },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_regmap g12a_hdmi_pll_dco = {
- .data = &(struct meson_clk_pll_data){
- .en = {
- .reg_off = HHI_HDMI_PLL_CNTL0,
- .shift = 28,
- .width = 1,
- },
- .m = {
- .reg_off = HHI_HDMI_PLL_CNTL0,
- .shift = 0,
- .width = 8,
- },
- .n = {
- .reg_off = HHI_HDMI_PLL_CNTL0,
- .shift = 10,
- .width = 5,
- },
- .frac = {
- .reg_off = HHI_HDMI_PLL_CNTL1,
- .shift = 0,
- .width = 16,
- },
- .l = {
- .reg_off = HHI_HDMI_PLL_CNTL0,
- .shift = 30,
- .width = 1,
- },
- .rst = {
- .reg_off = HHI_HDMI_PLL_CNTL0,
- .shift = 29,
- .width = 1,
- },
- },
- .hw.init = &(struct clk_init_data){
- .name = "hdmi_pll_dco",
- .ops = &meson_clk_pll_ro_ops,
- .parent_data = &(const struct clk_parent_data) {
- .fw_name = "xtal",
- },
- .num_parents = 1,
- /*
- * Display directly handle hdmi pll registers ATM, we need
- * NOCACHE to keep our view of the clock as accurate as possible
- */
- .flags = CLK_GET_RATE_NOCACHE,
- },
-};
-
-static struct clk_regmap g12a_hdmi_pll_od = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_HDMI_PLL_CNTL0,
- .shift = 16,
- .width = 2,
- .flags = CLK_DIVIDER_POWER_OF_TWO,
- },
- .hw.init = &(struct clk_init_data){
- .name = "hdmi_pll_od",
- .ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_hdmi_pll_dco.hw
- },
- .num_parents = 1,
- .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_regmap g12a_hdmi_pll_od2 = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_HDMI_PLL_CNTL0,
- .shift = 18,
- .width = 2,
- .flags = CLK_DIVIDER_POWER_OF_TWO,
- },
- .hw.init = &(struct clk_init_data){
- .name = "hdmi_pll_od2",
- .ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_hdmi_pll_od.hw
- },
- .num_parents = 1,
- .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_regmap g12a_hdmi_pll = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_HDMI_PLL_CNTL0,
- .shift = 20,
- .width = 2,
- .flags = CLK_DIVIDER_POWER_OF_TWO,
- },
- .hw.init = &(struct clk_init_data){
- .name = "hdmi_pll",
- .ops = &clk_regmap_divider_ro_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_hdmi_pll_od2.hw
- },
- .num_parents = 1,
- .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
- },
-};
-
-static struct clk_fixed_factor g12a_fclk_div4_div = {
- .mult = 1,
- .div = 4,
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div4_div",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) { &g12a_fixed_pll.hw },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_fclk_div4 = {
- .data = &(struct clk_regmap_gate_data){
- .offset = HHI_FIX_PLL_CNTL1,
- .bit_idx = 21,
- },
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div4",
- .ops = &clk_regmap_gate_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fclk_div4_div.hw
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_fixed_factor g12a_fclk_div5_div = {
- .mult = 1,
- .div = 5,
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div5_div",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) { &g12a_fixed_pll.hw },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_fclk_div5 = {
- .data = &(struct clk_regmap_gate_data){
- .offset = HHI_FIX_PLL_CNTL1,
- .bit_idx = 22,
- },
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div5",
- .ops = &clk_regmap_gate_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fclk_div5_div.hw
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_fixed_factor g12a_fclk_div7_div = {
- .mult = 1,
- .div = 7,
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div7_div",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) { &g12a_fixed_pll.hw },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_fclk_div7 = {
- .data = &(struct clk_regmap_gate_data){
- .offset = HHI_FIX_PLL_CNTL1,
- .bit_idx = 23,
- },
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div7",
- .ops = &clk_regmap_gate_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fclk_div7_div.hw
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_fixed_factor g12a_fclk_div2p5_div = {
- .mult = 1,
- .div = 5,
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div2p5_div",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fixed_pll_dco.hw
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_fclk_div2p5 = {
- .data = &(struct clk_regmap_gate_data){
- .offset = HHI_FIX_PLL_CNTL1,
- .bit_idx = 25,
- },
- .hw.init = &(struct clk_init_data){
- .name = "fclk_div2p5",
- .ops = &clk_regmap_gate_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fclk_div2p5_div.hw
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_fixed_factor g12a_mpll_50m_div = {
- .mult = 1,
- .div = 80,
- .hw.init = &(struct clk_init_data){
- .name = "mpll_50m_div",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fixed_pll_dco.hw
- },
- .num_parents = 1,
- },
-};
-
-static struct clk_regmap g12a_mpll_50m = {
- .data = &(struct clk_regmap_mux_data){
- .offset = HHI_FIX_PLL_CNTL3,
- .mask = 0x1,
- .shift = 5,
- },
- .hw.init = &(struct clk_init_data){
- .name = "mpll_50m",
- .ops = &clk_regmap_mux_ro_ops,
- .parent_data = (const struct clk_parent_data []) {
- { .fw_name = "xtal", },
- { .hw = &g12a_mpll_50m_div.hw },
- },
- .num_parents = 2,
- },
-};
-
-static struct clk_fixed_factor g12a_mpll_prediv = {
- .mult = 1,
- .div = 2,
- .hw.init = &(struct clk_init_data){
- .name = "mpll_prediv",
- .ops = &clk_fixed_factor_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &g12a_fixed_pll_dco.hw
- },
- .num_parents = 1,
- },
-};
-
static const struct reg_sequence g12a_mpll0_init_regs[] = {
{ .reg = HHI_MPLL_CNTL2, .def = 0x40000033 },
};
@@ -2222,7 +2321,6 @@ static struct clk_regmap g12a_mpll0_div = {
.shift = 29,
.width = 1,
},
- .lock = &meson_clk_lock,
.init_regs = g12a_mpll0_init_regs,
.init_count = ARRAY_SIZE(g12a_mpll0_init_regs),
},
@@ -2276,7 +2374,6 @@ static struct clk_regmap g12a_mpll1_div = {
.shift = 29,
.width = 1,
},
- .lock = &meson_clk_lock,
.init_regs = g12a_mpll1_init_regs,
.init_count = ARRAY_SIZE(g12a_mpll1_init_regs),
},
@@ -2330,7 +2427,6 @@ static struct clk_regmap g12a_mpll2_div = {
.shift = 29,
.width = 1,
},
- .lock = &meson_clk_lock,
.init_regs = g12a_mpll2_init_regs,
.init_count = ARRAY_SIZE(g12a_mpll2_init_regs),
},
@@ -2384,7 +2480,6 @@ static struct clk_regmap g12a_mpll3_div = {
.shift = 29,
.width = 1,
},
- .lock = &meson_clk_lock,
.init_regs = g12a_mpll3_init_regs,
.init_count = ARRAY_SIZE(g12a_mpll3_init_regs),
},
@@ -2412,8 +2507,9 @@ static struct clk_regmap g12a_mpll3 = {
},
};
-static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
-static const struct clk_parent_data clk81_parent_data[] = {
+/* clk81 is often referred as "mpeg_clk" */
+static u32 g12a_clk81_parents_val_table[] = { 0, 2, 3, 4, 5, 6, 7 };
+static const struct clk_parent_data g12a_clk81_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div7.hw },
{ .hw = &g12a_mpll1.hw },
@@ -2423,32 +2519,32 @@ static const struct clk_parent_data clk81_parent_data[] = {
{ .hw = &g12a_fclk_div5.hw },
};
-static struct clk_regmap g12a_mpeg_clk_sel = {
+static struct clk_regmap g12a_clk81_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MPEG_CLK_CNTL,
.mask = 0x7,
.shift = 12,
- .table = mux_table_clk81,
+ .table = g12a_clk81_parents_val_table,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_sel",
+ .name = "clk81_sel",
.ops = &clk_regmap_mux_ro_ops,
- .parent_data = clk81_parent_data,
- .num_parents = ARRAY_SIZE(clk81_parent_data),
+ .parent_data = g12a_clk81_parents,
+ .num_parents = ARRAY_SIZE(g12a_clk81_parents),
},
};
-static struct clk_regmap g12a_mpeg_clk_div = {
+static struct clk_regmap g12a_clk81_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_MPEG_CLK_CNTL,
.shift = 0,
.width = 7,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_div",
+ .name = "clk81_div",
.ops = &clk_regmap_divider_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_mpeg_clk_sel.hw
+ &g12a_clk81_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -2464,14 +2560,14 @@ static struct clk_regmap g12a_clk81 = {
.name = "clk81",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &g12a_mpeg_clk_div.hw
+ &g12a_clk81_div.hw
},
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
},
};
-static const struct clk_parent_data g12a_sd_emmc_clk0_parent_data[] = {
+static const struct clk_parent_data g12a_sd_emmc_clk0_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div2.hw },
{ .hw = &g12a_fclk_div3.hw },
@@ -2480,7 +2576,7 @@ static const struct clk_parent_data g12a_sd_emmc_clk0_parent_data[] = {
/*
* Following these parent clocks, we should also have had mpll2, mpll3
* and gp0_pll but these clocks are too precious to be used here. All
- * the necessary rates for MMC and NAND operation can be acheived using
+ * the necessary rates for MMC and NAND operation can be achieved using
* g12a_ee_core or fclk_div clocks
*/
};
@@ -2495,8 +2591,8 @@ static struct clk_regmap g12a_sd_emmc_a_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_a_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = g12a_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_data),
+ .parent_data = g12a_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2544,8 +2640,8 @@ static struct clk_regmap g12a_sd_emmc_b_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = g12a_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_data),
+ .parent_data = g12a_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2593,8 +2689,8 @@ static struct clk_regmap g12a_sd_emmc_c_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = g12a_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_data),
+ .parent_data = g12a_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2656,7 +2752,7 @@ static struct clk_regmap g12a_vid_pll_div = {
},
};
-static const struct clk_hw *g12a_vid_pll_parent_hws[] = {
+static const struct clk_hw *g12a_vid_pll_parents[] = {
&g12a_vid_pll_div.hw,
&g12a_hdmi_pll.hw,
};
@@ -2674,8 +2770,8 @@ static struct clk_regmap g12a_vid_pll_sel = {
* bit 18 selects from 2 possible parents:
* vid_pll_div or hdmi_pll
*/
- .parent_hws = g12a_vid_pll_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vid_pll_parent_hws),
+ .parent_hws = g12a_vid_pll_parents,
+ .num_parents = ARRAY_SIZE(g12a_vid_pll_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -2698,7 +2794,7 @@ static struct clk_regmap g12a_vid_pll = {
/* VPU Clock */
-static const struct clk_hw *g12a_vpu_parent_hws[] = {
+static const struct clk_hw *g12a_vpu_parents[] = {
&g12a_fclk_div3.hw,
&g12a_fclk_div4.hw,
&g12a_fclk_div5.hw,
@@ -2718,8 +2814,8 @@ static struct clk_regmap g12a_vpu_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vpu_parent_hws),
+ .parent_hws = g12a_vpu_parents,
+ .num_parents = ARRAY_SIZE(g12a_vpu_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -2762,8 +2858,8 @@ static struct clk_regmap g12a_vpu_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vpu_parent_hws),
+ .parent_hws = g12a_vpu_parents,
+ .num_parents = ARRAY_SIZE(g12a_vpu_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -2821,7 +2917,7 @@ static struct clk_regmap g12a_vpu = {
/* VDEC clocks */
-static const struct clk_hw *g12a_vdec_parent_hws[] = {
+static const struct clk_hw *g12a_vdec_parents[] = {
&g12a_fclk_div2p5.hw,
&g12a_fclk_div3.hw,
&g12a_fclk_div4.hw,
@@ -2841,8 +2937,8 @@ static struct clk_regmap g12a_vdec_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vdec_parent_hws),
+ .parent_hws = g12a_vdec_parents,
+ .num_parents = ARRAY_SIZE(g12a_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2891,8 +2987,8 @@ static struct clk_regmap g12a_vdec_hevcf_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_hevcf_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vdec_parent_hws),
+ .parent_hws = g12a_vdec_parents,
+ .num_parents = ARRAY_SIZE(g12a_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2941,8 +3037,8 @@ static struct clk_regmap g12a_vdec_hevc_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_hevc_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vdec_parent_hws),
+ .parent_hws = g12a_vdec_parents,
+ .num_parents = ARRAY_SIZE(g12a_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2983,7 +3079,7 @@ static struct clk_regmap g12a_vdec_hevc = {
/* VAPB Clock */
-static const struct clk_hw *g12a_vapb_parent_hws[] = {
+static const struct clk_hw *g12a_vapb_parents[] = {
&g12a_fclk_div4.hw,
&g12a_fclk_div3.hw,
&g12a_fclk_div5.hw,
@@ -3003,8 +3099,8 @@ static struct clk_regmap g12a_vapb_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "vapb_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vapb_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vapb_parent_hws),
+ .parent_hws = g12a_vapb_parents,
+ .num_parents = ARRAY_SIZE(g12a_vapb_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -3051,8 +3147,8 @@ static struct clk_regmap g12a_vapb_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vapb_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vapb_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vapb_parent_hws),
+ .parent_hws = g12a_vapb_parents,
+ .num_parents = ARRAY_SIZE(g12a_vapb_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -3126,7 +3222,7 @@ static struct clk_regmap g12a_vapb = {
},
};
-static const struct clk_hw *g12a_vclk_parent_hws[] = {
+static const struct clk_hw *g12a_vclk_parents[] = {
&g12a_vid_pll.hw,
&g12a_gp0_pll.hw,
&g12a_hifi_pll.hw,
@@ -3146,8 +3242,8 @@ static struct clk_regmap g12a_vclk_sel = {
.hw.init = &(struct clk_init_data){
.name = "vclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vclk_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
+ .parent_hws = g12a_vclk_parents,
+ .num_parents = ARRAY_SIZE(g12a_vclk_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -3161,9 +3257,9 @@ static struct clk_regmap g12a_vclk2_sel = {
.hw.init = &(struct clk_init_data){
.name = "vclk2_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_vclk_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
- .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ .parent_hws = g12a_vclk_parents,
+ .num_parents = ARRAY_SIZE(g12a_vclk_parents),
+ .flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -3191,7 +3287,6 @@ static struct clk_regmap g12a_vclk2_input = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
@@ -3213,19 +3308,32 @@ static struct clk_regmap g12a_vclk_div = {
};
static struct clk_regmap g12a_vclk2_div = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_VIID_CLK_DIV,
- .shift = 0,
- .width = 8,
+ .data = &(struct meson_vclk_div_data){
+ .div = {
+ .reg_off = HHI_VIID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .enable = {
+ .reg_off = HHI_VIID_CLK_DIV,
+ .shift = 16,
+ .width = 1,
+ },
+ .reset = {
+ .reg_off = HHI_VIID_CLK_DIV,
+ .shift = 17,
+ .width = 1,
+ },
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "vclk2_div",
- .ops = &clk_regmap_divider_ops,
+ .ops = &meson_vclk_div_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_vclk2_input.hw
},
.num_parents = 1,
- .flags = CLK_GET_RATE_NOCACHE,
+ .flags = CLK_SET_RATE_GATE,
},
};
@@ -3244,16 +3352,24 @@ static struct clk_regmap g12a_vclk = {
};
static struct clk_regmap g12a_vclk2 = {
- .data = &(struct clk_regmap_gate_data){
- .offset = HHI_VIID_CLK_CNTL,
- .bit_idx = 19,
+ .data = &(struct meson_vclk_gate_data){
+ .enable = {
+ .reg_off = HHI_VIID_CLK_CNTL,
+ .shift = 19,
+ .width = 1,
+ },
+ .reset = {
+ .reg_off = HHI_VIID_CLK_CNTL,
+ .shift = 15,
+ .width = 1,
+ },
},
.hw.init = &(struct clk_init_data) {
.name = "vclk2",
- .ops = &clk_regmap_gate_ops,
+ .ops = &meson_vclk_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3337,7 +3453,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3351,7 +3467,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3365,7 +3481,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3379,7 +3495,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3393,7 +3509,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3459,6 +3575,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 = {
&g12a_vclk2_div2_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3472,6 +3589,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 = {
&g12a_vclk2_div4_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3485,6 +3603,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 = {
&g12a_vclk2_div6_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3498,11 +3617,12 @@ static struct clk_fixed_factor g12a_vclk2_div12 = {
&g12a_vclk2_div12_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
-static u32 mux_table_cts_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
-static const struct clk_hw *g12a_cts_parent_hws[] = {
+static u32 g12a_cts_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *g12a_cts_parents[] = {
&g12a_vclk_div1.hw,
&g12a_vclk_div2.hw,
&g12a_vclk_div4.hw,
@@ -3520,13 +3640,13 @@ static struct clk_regmap g12a_cts_enci_sel = {
.offset = HHI_VID_CLK_DIV,
.mask = 0xf,
.shift = 28,
- .table = mux_table_cts_sel,
+ .table = g12a_cts_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_enci_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_cts_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
+ .parent_hws = g12a_cts_parents,
+ .num_parents = ARRAY_SIZE(g12a_cts_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -3536,36 +3656,52 @@ static struct clk_regmap g12a_cts_encp_sel = {
.offset = HHI_VID_CLK_DIV,
.mask = 0xf,
.shift = 20,
- .table = mux_table_cts_sel,
+ .table = g12a_cts_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_encp_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_cts_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
+ .parent_hws = g12a_cts_parents,
+ .num_parents = ARRAY_SIZE(g12a_cts_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
+static struct clk_regmap g12a_cts_encl_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 12,
+ .table = g12a_cts_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_encl_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = g12a_cts_parents,
+ .num_parents = ARRAY_SIZE(g12a_cts_parents),
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
static struct clk_regmap g12a_cts_vdac_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VIID_CLK_DIV,
.mask = 0xf,
.shift = 28,
- .table = mux_table_cts_sel,
+ .table = g12a_cts_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_vdac_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_cts_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
+ .parent_hws = g12a_cts_parents,
+ .num_parents = ARRAY_SIZE(g12a_cts_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
/* TOFIX: add support for cts_tcon */
-static u32 mux_table_hdmi_tx_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
-static const struct clk_hw *g12a_cts_hdmi_tx_parent_hws[] = {
+static u32 g12a_hdmi_tx_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *g12a_hdmi_tx_parents[] = {
&g12a_vclk_div1.hw,
&g12a_vclk_div2.hw,
&g12a_vclk_div4.hw,
@@ -3583,13 +3719,13 @@ static struct clk_regmap g12a_hdmi_tx_sel = {
.offset = HHI_HDMI_CLK_CNTL,
.mask = 0xf,
.shift = 16,
- .table = mux_table_hdmi_tx_sel,
+ .table = g12a_hdmi_tx_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "hdmi_tx_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_cts_hdmi_tx_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_cts_hdmi_tx_parent_hws),
+ .parent_hws = g12a_hdmi_tx_parents,
+ .num_parents = ARRAY_SIZE(g12a_hdmi_tx_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -3626,6 +3762,22 @@ static struct clk_regmap g12a_cts_encp = {
},
};
+static struct clk_regmap g12a_cts_encl = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL2,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_encl",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_cts_encl_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
static struct clk_regmap g12a_cts_vdac = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL2,
@@ -3660,7 +3812,7 @@ static struct clk_regmap g12a_hdmi_tx = {
/* MIPI DSI Host Clocks */
-static const struct clk_hw *g12a_mipi_dsi_pxclk_parent_hws[] = {
+static const struct clk_hw *g12a_mipi_dsi_pxclk_parents[] = {
&g12a_vid_pll.hw,
&g12a_gp0_pll.hw,
&g12a_hifi_pll.hw,
@@ -3681,17 +3833,28 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel = {
.hw.init = &(struct clk_init_data){
.name = "mipi_dsi_pxclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
- .num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
- .flags = CLK_SET_RATE_NO_REPARENT,
+ .parent_hws = g12a_mipi_dsi_pxclk_parents,
+ .num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parents),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
},
};
+/*
+ * FIXME: Force as bypass by forcing a single /1 table entry, and doesn't on boot value
+ * when setting a clock with this node in the clock path, but doesn't guarantee the divider
+ * is at /1 at boot until a rate is set.
+ */
+static const struct clk_div_table g12a_mipi_dsi_pxclk_div_table[] = {
+ { .val = 0, .div = 1 },
+ { /* sentinel */ },
+};
+
static struct clk_regmap g12a_mipi_dsi_pxclk_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_MIPIDSI_PHY_CLK_CNTL,
.shift = 0,
.width = 7,
+ .table = g12a_mipi_dsi_pxclk_div_table,
},
.hw.init = &(struct clk_init_data){
.name = "mipi_dsi_pxclk_div",
@@ -3720,9 +3883,69 @@ static struct clk_regmap g12a_mipi_dsi_pxclk = {
},
};
+/* MIPI ISP Clocks */
+
+static const struct clk_parent_data g12b_mipi_isp_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &g12a_gp0_pll.hw },
+ { .hw = &g12a_hifi_pll.hw },
+ { .hw = &g12a_fclk_div2p5.hw },
+ { .hw = &g12a_fclk_div3.hw },
+ { .hw = &g12a_fclk_div4.hw },
+ { .hw = &g12a_fclk_div5.hw },
+ { .hw = &g12a_fclk_div7.hw },
+};
+
+static struct clk_regmap g12b_mipi_isp_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_ISP_CLK_CNTL,
+ .mask = 7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mipi_isp_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = g12b_mipi_isp_parents,
+ .num_parents = ARRAY_SIZE(g12b_mipi_isp_parents),
+ },
+};
+
+static struct clk_regmap g12b_mipi_isp_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_ISP_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mipi_isp_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12b_mipi_isp_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap g12b_mipi_isp = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_ISP_CLK_CNTL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "mipi_isp",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12b_mipi_isp_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
/* HDMI Clocks */
-static const struct clk_parent_data g12a_hdmi_parent_data[] = {
+static const struct clk_parent_data g12a_hdmi_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &g12a_fclk_div4.hw },
{ .hw = &g12a_fclk_div3.hw },
@@ -3739,8 +3962,8 @@ static struct clk_regmap g12a_hdmi_sel = {
.hw.init = &(struct clk_init_data){
.name = "hdmi_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = g12a_hdmi_parent_data,
- .num_parents = ARRAY_SIZE(g12a_hdmi_parent_data),
+ .parent_data = g12a_hdmi_parents,
+ .num_parents = ARRAY_SIZE(g12a_hdmi_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -3780,7 +4003,7 @@ static struct clk_regmap g12a_hdmi = {
* mux because it does top-to-bottom updates the each clock tree and
* switches to the "inactive" one when CLK_SET_RATE_GATE is set.
*/
-static const struct clk_parent_data g12a_mali_0_1_parent_data[] = {
+static const struct clk_parent_data g12a_mali_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &g12a_gp0_pll.hw },
{ .hw = &g12a_hifi_pll.hw },
@@ -3800,8 +4023,8 @@ static struct clk_regmap g12a_mali_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "mali_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = g12a_mali_0_1_parent_data,
- .num_parents = 8,
+ .parent_data = g12a_mali_parents,
+ .num_parents = ARRAY_SIZE(g12a_mali_parents),
/*
* Don't request the parent to change the rate because
* all GPU frequencies can be derived from the fclk_*
@@ -3854,8 +4077,8 @@ static struct clk_regmap g12a_mali_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "mali_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = g12a_mali_0_1_parent_data,
- .num_parents = 8,
+ .parent_data = g12a_mali_parents,
+ .num_parents = ARRAY_SIZE(g12a_mali_parents),
/*
* Don't request the parent to change the rate because
* all GPU frequencies can be derived from the fclk_*
@@ -3899,11 +4122,6 @@ static struct clk_regmap g12a_mali_1 = {
},
};
-static const struct clk_hw *g12a_mali_parent_hws[] = {
- &g12a_mali_0.hw,
- &g12a_mali_1.hw,
-};
-
static struct clk_regmap g12a_mali = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MALI_CLK_CNTL,
@@ -3913,7 +4131,10 @@ static struct clk_regmap g12a_mali = {
.hw.init = &(struct clk_init_data){
.name = "mali",
.ops = &clk_regmap_mux_ops,
- .parent_hws = g12a_mali_parent_hws,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_mali_0.hw,
+ &g12a_mali_1.hw,
+ },
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
@@ -3952,11 +4173,12 @@ static struct clk_regmap g12a_ts = {
/* SPICC SCLK source clock */
-static const struct clk_parent_data spicc_sclk_parent_data[] = {
+static const struct clk_parent_data g12a_spicc_sclk_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &g12a_clk81.hw },
{ .hw = &g12a_fclk_div4.hw },
{ .hw = &g12a_fclk_div3.hw },
+ { .hw = &g12a_fclk_div2.hw },
{ .hw = &g12a_fclk_div5.hw },
{ .hw = &g12a_fclk_div7.hw },
};
@@ -3970,8 +4192,8 @@ static struct clk_regmap g12a_spicc0_sclk_sel = {
.hw.init = &(struct clk_init_data){
.name = "spicc0_sclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = spicc_sclk_parent_data,
- .num_parents = ARRAY_SIZE(spicc_sclk_parent_data),
+ .parent_data = g12a_spicc_sclk_parents,
+ .num_parents = ARRAY_SIZE(g12a_spicc_sclk_parents),
},
};
@@ -4017,8 +4239,8 @@ static struct clk_regmap g12a_spicc1_sclk_sel = {
.hw.init = &(struct clk_init_data){
.name = "spicc1_sclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = spicc_sclk_parent_data,
- .num_parents = ARRAY_SIZE(spicc_sclk_parent_data),
+ .parent_data = g12a_spicc_sclk_parents,
+ .num_parents = ARRAY_SIZE(g12a_spicc_sclk_parents),
},
};
@@ -4057,7 +4279,7 @@ static struct clk_regmap g12a_spicc1_sclk = {
/* Neural Network Accelerator source clock */
-static const struct clk_parent_data nna_clk_parent_data[] = {
+static const struct clk_parent_data sm1_nna_clk_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &g12a_gp0_pll.hw, },
{ .hw = &g12a_hifi_pll.hw, },
@@ -4077,8 +4299,8 @@ static struct clk_regmap sm1_nna_axi_clk_sel = {
.hw.init = &(struct clk_init_data){
.name = "nna_axi_clk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = nna_clk_parent_data,
- .num_parents = ARRAY_SIZE(nna_clk_parent_data),
+ .parent_data = sm1_nna_clk_parents,
+ .num_parents = ARRAY_SIZE(sm1_nna_clk_parents),
},
};
@@ -4124,8 +4346,8 @@ static struct clk_regmap sm1_nna_core_clk_sel = {
.hw.init = &(struct clk_init_data){
.name = "nna_core_clk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = nna_clk_parent_data,
- .num_parents = ARRAY_SIZE(nna_clk_parent_data),
+ .parent_data = sm1_nna_clk_parents,
+ .num_parents = ARRAY_SIZE(sm1_nna_clk_parents),
},
};
@@ -4162,1075 +4384,843 @@ static struct clk_regmap sm1_nna_core_clk = {
},
};
-#define MESON_GATE(_name, _reg, _bit) \
- MESON_PCLK(_name, _reg, _bit, &g12a_clk81.hw)
-
-#define MESON_GATE_RO(_name, _reg, _bit) \
- MESON_PCLK_RO(_name, _reg, _bit, &g12a_clk81.hw)
-
-/* Everything Else (EE) domain gates */
-static MESON_GATE(g12a_ddr, HHI_GCLK_MPEG0, 0);
-static MESON_GATE(g12a_dos, HHI_GCLK_MPEG0, 1);
-static MESON_GATE(g12a_audio_locker, HHI_GCLK_MPEG0, 2);
-static MESON_GATE(g12a_mipi_dsi_host, HHI_GCLK_MPEG0, 3);
-static MESON_GATE(g12a_eth_phy, HHI_GCLK_MPEG0, 4);
-static MESON_GATE(g12a_isa, HHI_GCLK_MPEG0, 5);
-static MESON_GATE(g12a_pl301, HHI_GCLK_MPEG0, 6);
-static MESON_GATE(g12a_periphs, HHI_GCLK_MPEG0, 7);
-static MESON_GATE(g12a_spicc_0, HHI_GCLK_MPEG0, 8);
-static MESON_GATE(g12a_i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(g12a_sana, HHI_GCLK_MPEG0, 10);
-static MESON_GATE(g12a_sd, HHI_GCLK_MPEG0, 11);
-static MESON_GATE(g12a_rng0, HHI_GCLK_MPEG0, 12);
-static MESON_GATE(g12a_uart0, HHI_GCLK_MPEG0, 13);
-static MESON_GATE(g12a_spicc_1, HHI_GCLK_MPEG0, 14);
-static MESON_GATE(g12a_hiu_reg, HHI_GCLK_MPEG0, 19);
-static MESON_GATE(g12a_mipi_dsi_phy, HHI_GCLK_MPEG0, 20);
-static MESON_GATE(g12a_assist_misc, HHI_GCLK_MPEG0, 23);
-static MESON_GATE(g12a_emmc_a, HHI_GCLK_MPEG0, 4);
-static MESON_GATE(g12a_emmc_b, HHI_GCLK_MPEG0, 25);
-static MESON_GATE(g12a_emmc_c, HHI_GCLK_MPEG0, 26);
-static MESON_GATE(g12a_audio_codec, HHI_GCLK_MPEG0, 28);
-
-static MESON_GATE(g12a_audio, HHI_GCLK_MPEG1, 0);
-static MESON_GATE(g12a_eth_core, HHI_GCLK_MPEG1, 3);
-static MESON_GATE(g12a_demux, HHI_GCLK_MPEG1, 4);
-static MESON_GATE(g12a_audio_ififo, HHI_GCLK_MPEG1, 11);
-static MESON_GATE(g12a_adc, HHI_GCLK_MPEG1, 13);
-static MESON_GATE(g12a_uart1, HHI_GCLK_MPEG1, 16);
-static MESON_GATE(g12a_g2d, HHI_GCLK_MPEG1, 20);
-static MESON_GATE(g12a_reset, HHI_GCLK_MPEG1, 23);
-static MESON_GATE(g12a_pcie_comb, HHI_GCLK_MPEG1, 24);
-static MESON_GATE(g12a_parser, HHI_GCLK_MPEG1, 25);
-static MESON_GATE(g12a_usb_general, HHI_GCLK_MPEG1, 26);
-static MESON_GATE(g12a_pcie_phy, HHI_GCLK_MPEG1, 27);
-static MESON_GATE(g12a_ahb_arb0, HHI_GCLK_MPEG1, 29);
-
-static MESON_GATE(g12a_ahb_data_bus, HHI_GCLK_MPEG2, 1);
-static MESON_GATE(g12a_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
-static MESON_GATE(g12a_htx_hdcp22, HHI_GCLK_MPEG2, 3);
-static MESON_GATE(g12a_htx_pclk, HHI_GCLK_MPEG2, 4);
-static MESON_GATE(g12a_bt656, HHI_GCLK_MPEG2, 6);
-static MESON_GATE(g12a_usb1_to_ddr, HHI_GCLK_MPEG2, 8);
-static MESON_GATE(g12a_mmc_pclk, HHI_GCLK_MPEG2, 11);
-static MESON_GATE(g12a_uart2, HHI_GCLK_MPEG2, 15);
-static MESON_GATE(g12a_vpu_intr, HHI_GCLK_MPEG2, 25);
-static MESON_GATE(g12a_gic, HHI_GCLK_MPEG2, 30);
-
-static MESON_GATE(g12a_vclk2_venci0, HHI_GCLK_OTHER, 1);
-static MESON_GATE(g12a_vclk2_venci1, HHI_GCLK_OTHER, 2);
-static MESON_GATE(g12a_vclk2_vencp0, HHI_GCLK_OTHER, 3);
-static MESON_GATE(g12a_vclk2_vencp1, HHI_GCLK_OTHER, 4);
-static MESON_GATE(g12a_vclk2_venct0, HHI_GCLK_OTHER, 5);
-static MESON_GATE(g12a_vclk2_venct1, HHI_GCLK_OTHER, 6);
-static MESON_GATE(g12a_vclk2_other, HHI_GCLK_OTHER, 7);
-static MESON_GATE(g12a_vclk2_enci, HHI_GCLK_OTHER, 8);
-static MESON_GATE(g12a_vclk2_encp, HHI_GCLK_OTHER, 9);
-static MESON_GATE(g12a_dac_clk, HHI_GCLK_OTHER, 10);
-static MESON_GATE(g12a_aoclk_gate, HHI_GCLK_OTHER, 14);
-static MESON_GATE(g12a_iec958_gate, HHI_GCLK_OTHER, 16);
-static MESON_GATE(g12a_enc480p, HHI_GCLK_OTHER, 20);
-static MESON_GATE(g12a_rng1, HHI_GCLK_OTHER, 21);
-static MESON_GATE(g12a_vclk2_enct, HHI_GCLK_OTHER, 22);
-static MESON_GATE(g12a_vclk2_encl, HHI_GCLK_OTHER, 23);
-static MESON_GATE(g12a_vclk2_venclmmc, HHI_GCLK_OTHER, 24);
-static MESON_GATE(g12a_vclk2_vencl, HHI_GCLK_OTHER, 25);
-static MESON_GATE(g12a_vclk2_other1, HHI_GCLK_OTHER, 26);
-
-static MESON_GATE_RO(g12a_dma, HHI_GCLK_OTHER2, 0);
-static MESON_GATE_RO(g12a_efuse, HHI_GCLK_OTHER2, 1);
-static MESON_GATE_RO(g12a_rom_boot, HHI_GCLK_OTHER2, 2);
-static MESON_GATE_RO(g12a_reset_sec, HHI_GCLK_OTHER2, 3);
-static MESON_GATE_RO(g12a_sec_ahb_apb3, HHI_GCLK_OTHER2, 4);
+static const struct clk_parent_data g12a_pclk_parents = { .hw = &g12a_clk81.hw };
+
+#define G12A_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(_name, _reg, _bit, &g12a_pclk_parents, _flags)
+
+#define G12A_PCLK_RO(_name, _reg, _bit, _flags) \
+ MESON_PCLK_RO(_name, _reg, _bit, &g12a_pclk_parents, _flags)
+
+/*
+ * Everything Else (EE) domain gates
+ *
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
+ */
+static G12A_PCLK(g12a_ddr, HHI_GCLK_MPEG0, 0, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_dos, HHI_GCLK_MPEG0, 1, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_audio_locker, HHI_GCLK_MPEG0, 2, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_mipi_dsi_host, HHI_GCLK_MPEG0, 3, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_eth_phy, HHI_GCLK_MPEG0, 4, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_isa, HHI_GCLK_MPEG0, 5, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_pl301, HHI_GCLK_MPEG0, 6, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_periphs, HHI_GCLK_MPEG0, 7, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_spicc_0, HHI_GCLK_MPEG0, 8, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_i2c, HHI_GCLK_MPEG0, 9, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_sana, HHI_GCLK_MPEG0, 10, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_sd, HHI_GCLK_MPEG0, 11, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_rng0, HHI_GCLK_MPEG0, 12, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_uart0, HHI_GCLK_MPEG0, 13, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_spicc_1, HHI_GCLK_MPEG0, 14, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_hiu_reg, HHI_GCLK_MPEG0, 19, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_mipi_dsi_phy, HHI_GCLK_MPEG0, 20, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_assist_misc, HHI_GCLK_MPEG0, 23, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_emmc_a, HHI_GCLK_MPEG0, 24, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_emmc_b, HHI_GCLK_MPEG0, 25, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_emmc_c, HHI_GCLK_MPEG0, 26, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_audio_codec, HHI_GCLK_MPEG0, 28, CLK_IGNORE_UNUSED);
+
+static G12A_PCLK(g12a_audio, HHI_GCLK_MPEG1, 0, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_eth_core, HHI_GCLK_MPEG1, 3, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_demux, HHI_GCLK_MPEG1, 4, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_audio_ififo, HHI_GCLK_MPEG1, 11, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_adc, HHI_GCLK_MPEG1, 13, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_uart1, HHI_GCLK_MPEG1, 16, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_g2d, HHI_GCLK_MPEG1, 20, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_reset, HHI_GCLK_MPEG1, 23, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_pcie_comb, HHI_GCLK_MPEG1, 24, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_parser, HHI_GCLK_MPEG1, 25, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_usb_general, HHI_GCLK_MPEG1, 26, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_pcie_phy, HHI_GCLK_MPEG1, 27, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_ahb_arb0, HHI_GCLK_MPEG1, 29, CLK_IGNORE_UNUSED);
+
+static G12A_PCLK(g12a_ahb_data_bus, HHI_GCLK_MPEG2, 1, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_htx_hdcp22, HHI_GCLK_MPEG2, 3, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_htx_pclk, HHI_GCLK_MPEG2, 4, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_bt656, HHI_GCLK_MPEG2, 6, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_usb1_to_ddr, HHI_GCLK_MPEG2, 8, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12b_mipi_isp_gate, HHI_GCLK_MPEG2, 17, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_mmc_pclk, HHI_GCLK_MPEG2, 11, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_uart2, HHI_GCLK_MPEG2, 15, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vpu_intr, HHI_GCLK_MPEG2, 25, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12b_csi_phy1, HHI_GCLK_MPEG2, 28, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12b_csi_phy0, HHI_GCLK_MPEG2, 29, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_gic, HHI_GCLK_MPEG2, 30, CLK_IGNORE_UNUSED);
+
+static G12A_PCLK(g12a_vclk2_venci0, HHI_GCLK_OTHER, 1, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_venci1, HHI_GCLK_OTHER, 2, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_vencp0, HHI_GCLK_OTHER, 3, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_vencp1, HHI_GCLK_OTHER, 4, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_venct0, HHI_GCLK_OTHER, 5, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_venct1, HHI_GCLK_OTHER, 6, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_other, HHI_GCLK_OTHER, 7, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_enci, HHI_GCLK_OTHER, 8, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_encp, HHI_GCLK_OTHER, 9, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_dac_clk, HHI_GCLK_OTHER, 10, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_aoclk_gate, HHI_GCLK_OTHER, 14, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_iec958_gate, HHI_GCLK_OTHER, 16, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_enc480p, HHI_GCLK_OTHER, 20, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_rng1, HHI_GCLK_OTHER, 21, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_enct, HHI_GCLK_OTHER, 22, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_encl, HHI_GCLK_OTHER, 23, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_venclmmc, HHI_GCLK_OTHER, 24, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_vencl, HHI_GCLK_OTHER, 25, CLK_IGNORE_UNUSED);
+static G12A_PCLK(g12a_vclk2_other1, HHI_GCLK_OTHER, 26, CLK_IGNORE_UNUSED);
+
+static G12A_PCLK_RO(g12a_dma, HHI_GCLK_OTHER2, 0, 0);
+static G12A_PCLK_RO(g12a_efuse, HHI_GCLK_OTHER2, 1, 0);
+static G12A_PCLK_RO(g12a_rom_boot, HHI_GCLK_OTHER2, 2, 0);
+static G12A_PCLK_RO(g12a_reset_sec, HHI_GCLK_OTHER2, 3, 0);
+static G12A_PCLK_RO(g12a_sec_ahb_apb3, HHI_GCLK_OTHER2, 4, 0);
/* Array of all clocks provided by this provider */
-static struct clk_hw_onecell_data g12a_hw_onecell_data = {
- .hws = {
- [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
- [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
- [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
- [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
- [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
- [CLKID_MPEG_SEL] = &g12a_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &g12a_mpeg_clk_div.hw,
- [CLKID_CLK81] = &g12a_clk81.hw,
- [CLKID_MPLL0] = &g12a_mpll0.hw,
- [CLKID_MPLL1] = &g12a_mpll1.hw,
- [CLKID_MPLL2] = &g12a_mpll2.hw,
- [CLKID_MPLL3] = &g12a_mpll3.hw,
- [CLKID_DDR] = &g12a_ddr.hw,
- [CLKID_DOS] = &g12a_dos.hw,
- [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
- [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
- [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
- [CLKID_ISA] = &g12a_isa.hw,
- [CLKID_PL301] = &g12a_pl301.hw,
- [CLKID_PERIPHS] = &g12a_periphs.hw,
- [CLKID_SPICC0] = &g12a_spicc_0.hw,
- [CLKID_I2C] = &g12a_i2c.hw,
- [CLKID_SANA] = &g12a_sana.hw,
- [CLKID_SD] = &g12a_sd.hw,
- [CLKID_RNG0] = &g12a_rng0.hw,
- [CLKID_UART0] = &g12a_uart0.hw,
- [CLKID_SPICC1] = &g12a_spicc_1.hw,
- [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
- [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
- [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
- [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
- [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
- [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
- [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
- [CLKID_AUDIO] = &g12a_audio.hw,
- [CLKID_ETH] = &g12a_eth_core.hw,
- [CLKID_DEMUX] = &g12a_demux.hw,
- [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
- [CLKID_ADC] = &g12a_adc.hw,
- [CLKID_UART1] = &g12a_uart1.hw,
- [CLKID_G2D] = &g12a_g2d.hw,
- [CLKID_RESET] = &g12a_reset.hw,
- [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
- [CLKID_PARSER] = &g12a_parser.hw,
- [CLKID_USB] = &g12a_usb_general.hw,
- [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
- [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
- [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
- [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
- [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
- [CLKID_BT656] = &g12a_bt656.hw,
- [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
- [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
- [CLKID_UART2] = &g12a_uart2.hw,
- [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
- [CLKID_GIC] = &g12a_gic.hw,
- [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
- [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
- [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
- [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
- [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
- [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
- [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
- [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
- [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
- [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
- [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
- [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
- [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
- [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
- [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
- [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
- [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
- [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
- [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
- [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
- [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
- [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
- [CLKID_IEC958] = &g12a_iec958_gate.hw,
- [CLKID_ENC480P] = &g12a_enc480p.hw,
- [CLKID_RNG1] = &g12a_rng1.hw,
- [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
- [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
- [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
- [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
- [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
- [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
- [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
- [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
- [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
- [CLKID_DMA] = &g12a_dma.hw,
- [CLKID_EFUSE] = &g12a_efuse.hw,
- [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
- [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
- [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
- [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
- [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
- [CLKID_VPU_0] = &g12a_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
- [CLKID_VPU_1] = &g12a_vpu_1.hw,
- [CLKID_VPU] = &g12a_vpu.hw,
- [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
- [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
- [CLKID_VAPB_0] = &g12a_vapb_0.hw,
- [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
- [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
- [CLKID_VAPB_1] = &g12a_vapb_1.hw,
- [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
- [CLKID_VAPB] = &g12a_vapb.hw,
- [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
- [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
- [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
- [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
- [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
- [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
- [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
- [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
- [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
- [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
- [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
- [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
- [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
- [CLKID_VCLK] = &g12a_vclk.hw,
- [CLKID_VCLK2] = &g12a_vclk2.hw,
- [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
- [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
- [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
- [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
- [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
- [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
- [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
- [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
- [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
- [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
- [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
- [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
- [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
- [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
- [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
- [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
- [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
- [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
- [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
- [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
- [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
- [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
- [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
- [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
- [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
- [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
- [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
- [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
- [CLKID_HDMI] = &g12a_hdmi.hw,
- [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
- [CLKID_MALI_0] = &g12a_mali_0.hw,
- [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
- [CLKID_MALI_1] = &g12a_mali_1.hw,
- [CLKID_MALI] = &g12a_mali.hw,
- [CLKID_MPLL_50M_DIV] = &g12a_mpll_50m_div.hw,
- [CLKID_MPLL_50M] = &g12a_mpll_50m.hw,
- [CLKID_SYS_PLL_DIV16_EN] = &g12a_sys_pll_div16_en.hw,
- [CLKID_SYS_PLL_DIV16] = &g12a_sys_pll_div16.hw,
- [CLKID_CPU_CLK_DYN0_SEL] = &g12a_cpu_clk_premux0.hw,
- [CLKID_CPU_CLK_DYN0_DIV] = &g12a_cpu_clk_mux0_div.hw,
- [CLKID_CPU_CLK_DYN0] = &g12a_cpu_clk_postmux0.hw,
- [CLKID_CPU_CLK_DYN1_SEL] = &g12a_cpu_clk_premux1.hw,
- [CLKID_CPU_CLK_DYN1_DIV] = &g12a_cpu_clk_mux1_div.hw,
- [CLKID_CPU_CLK_DYN1] = &g12a_cpu_clk_postmux1.hw,
- [CLKID_CPU_CLK_DYN] = &g12a_cpu_clk_dyn.hw,
- [CLKID_CPU_CLK] = &g12a_cpu_clk.hw,
- [CLKID_CPU_CLK_DIV16_EN] = &g12a_cpu_clk_div16_en.hw,
- [CLKID_CPU_CLK_DIV16] = &g12a_cpu_clk_div16.hw,
- [CLKID_CPU_CLK_APB_DIV] = &g12a_cpu_clk_apb_div.hw,
- [CLKID_CPU_CLK_APB] = &g12a_cpu_clk_apb.hw,
- [CLKID_CPU_CLK_ATB_DIV] = &g12a_cpu_clk_atb_div.hw,
- [CLKID_CPU_CLK_ATB] = &g12a_cpu_clk_atb.hw,
- [CLKID_CPU_CLK_AXI_DIV] = &g12a_cpu_clk_axi_div.hw,
- [CLKID_CPU_CLK_AXI] = &g12a_cpu_clk_axi.hw,
- [CLKID_CPU_CLK_TRACE_DIV] = &g12a_cpu_clk_trace_div.hw,
- [CLKID_CPU_CLK_TRACE] = &g12a_cpu_clk_trace.hw,
- [CLKID_PCIE_PLL_DCO] = &g12a_pcie_pll_dco.hw,
- [CLKID_PCIE_PLL_DCO_DIV2] = &g12a_pcie_pll_dco_div2.hw,
- [CLKID_PCIE_PLL_OD] = &g12a_pcie_pll_od.hw,
- [CLKID_PCIE_PLL] = &g12a_pcie_pll.hw,
- [CLKID_VDEC_1_SEL] = &g12a_vdec_1_sel.hw,
- [CLKID_VDEC_1_DIV] = &g12a_vdec_1_div.hw,
- [CLKID_VDEC_1] = &g12a_vdec_1.hw,
- [CLKID_VDEC_HEVC_SEL] = &g12a_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &g12a_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC] = &g12a_vdec_hevc.hw,
- [CLKID_VDEC_HEVCF_SEL] = &g12a_vdec_hevcf_sel.hw,
- [CLKID_VDEC_HEVCF_DIV] = &g12a_vdec_hevcf_div.hw,
- [CLKID_VDEC_HEVCF] = &g12a_vdec_hevcf.hw,
- [CLKID_TS_DIV] = &g12a_ts_div.hw,
- [CLKID_TS] = &g12a_ts.hw,
- [CLKID_SPICC0_SCLK_SEL] = &g12a_spicc0_sclk_sel.hw,
- [CLKID_SPICC0_SCLK_DIV] = &g12a_spicc0_sclk_div.hw,
- [CLKID_SPICC0_SCLK] = &g12a_spicc0_sclk.hw,
- [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw,
- [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw,
- [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw,
- [CLKID_MIPI_DSI_PXCLK_SEL] = &g12a_mipi_dsi_pxclk_sel.hw,
- [CLKID_MIPI_DSI_PXCLK_DIV] = &g12a_mipi_dsi_pxclk_div.hw,
- [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-static struct clk_hw_onecell_data g12b_hw_onecell_data = {
- .hws = {
- [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
- [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
- [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
- [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
- [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
- [CLKID_MPEG_SEL] = &g12a_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &g12a_mpeg_clk_div.hw,
- [CLKID_CLK81] = &g12a_clk81.hw,
- [CLKID_MPLL0] = &g12a_mpll0.hw,
- [CLKID_MPLL1] = &g12a_mpll1.hw,
- [CLKID_MPLL2] = &g12a_mpll2.hw,
- [CLKID_MPLL3] = &g12a_mpll3.hw,
- [CLKID_DDR] = &g12a_ddr.hw,
- [CLKID_DOS] = &g12a_dos.hw,
- [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
- [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
- [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
- [CLKID_ISA] = &g12a_isa.hw,
- [CLKID_PL301] = &g12a_pl301.hw,
- [CLKID_PERIPHS] = &g12a_periphs.hw,
- [CLKID_SPICC0] = &g12a_spicc_0.hw,
- [CLKID_I2C] = &g12a_i2c.hw,
- [CLKID_SANA] = &g12a_sana.hw,
- [CLKID_SD] = &g12a_sd.hw,
- [CLKID_RNG0] = &g12a_rng0.hw,
- [CLKID_UART0] = &g12a_uart0.hw,
- [CLKID_SPICC1] = &g12a_spicc_1.hw,
- [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
- [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
- [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
- [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
- [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
- [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
- [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
- [CLKID_AUDIO] = &g12a_audio.hw,
- [CLKID_ETH] = &g12a_eth_core.hw,
- [CLKID_DEMUX] = &g12a_demux.hw,
- [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
- [CLKID_ADC] = &g12a_adc.hw,
- [CLKID_UART1] = &g12a_uart1.hw,
- [CLKID_G2D] = &g12a_g2d.hw,
- [CLKID_RESET] = &g12a_reset.hw,
- [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
- [CLKID_PARSER] = &g12a_parser.hw,
- [CLKID_USB] = &g12a_usb_general.hw,
- [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
- [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
- [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
- [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
- [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
- [CLKID_BT656] = &g12a_bt656.hw,
- [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
- [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
- [CLKID_UART2] = &g12a_uart2.hw,
- [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
- [CLKID_GIC] = &g12a_gic.hw,
- [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
- [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
- [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
- [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
- [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
- [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
- [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
- [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
- [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
- [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
- [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
- [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
- [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
- [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
- [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
- [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
- [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
- [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
- [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
- [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
- [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
- [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
- [CLKID_IEC958] = &g12a_iec958_gate.hw,
- [CLKID_ENC480P] = &g12a_enc480p.hw,
- [CLKID_RNG1] = &g12a_rng1.hw,
- [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
- [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
- [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
- [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
- [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
- [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
- [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
- [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
- [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
- [CLKID_DMA] = &g12a_dma.hw,
- [CLKID_EFUSE] = &g12a_efuse.hw,
- [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
- [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
- [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
- [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
- [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
- [CLKID_VPU_0] = &g12a_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
- [CLKID_VPU_1] = &g12a_vpu_1.hw,
- [CLKID_VPU] = &g12a_vpu.hw,
- [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
- [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
- [CLKID_VAPB_0] = &g12a_vapb_0.hw,
- [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
- [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
- [CLKID_VAPB_1] = &g12a_vapb_1.hw,
- [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
- [CLKID_VAPB] = &g12a_vapb.hw,
- [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
- [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
- [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
- [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
- [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
- [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
- [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
- [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
- [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
- [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
- [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
- [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
- [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
- [CLKID_VCLK] = &g12a_vclk.hw,
- [CLKID_VCLK2] = &g12a_vclk2.hw,
- [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
- [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
- [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
- [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
- [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
- [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
- [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
- [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
- [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
- [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
- [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
- [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
- [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
- [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
- [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
- [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
- [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
- [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
- [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
- [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
- [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
- [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
- [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
- [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
- [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
- [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
- [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
- [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
- [CLKID_HDMI] = &g12a_hdmi.hw,
- [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
- [CLKID_MALI_0] = &g12a_mali_0.hw,
- [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
- [CLKID_MALI_1] = &g12a_mali_1.hw,
- [CLKID_MALI] = &g12a_mali.hw,
- [CLKID_MPLL_50M_DIV] = &g12a_mpll_50m_div.hw,
- [CLKID_MPLL_50M] = &g12a_mpll_50m.hw,
- [CLKID_SYS_PLL_DIV16_EN] = &g12a_sys_pll_div16_en.hw,
- [CLKID_SYS_PLL_DIV16] = &g12a_sys_pll_div16.hw,
- [CLKID_CPU_CLK_DYN0_SEL] = &g12a_cpu_clk_premux0.hw,
- [CLKID_CPU_CLK_DYN0_DIV] = &g12a_cpu_clk_mux0_div.hw,
- [CLKID_CPU_CLK_DYN0] = &g12a_cpu_clk_postmux0.hw,
- [CLKID_CPU_CLK_DYN1_SEL] = &g12a_cpu_clk_premux1.hw,
- [CLKID_CPU_CLK_DYN1_DIV] = &g12a_cpu_clk_mux1_div.hw,
- [CLKID_CPU_CLK_DYN1] = &g12a_cpu_clk_postmux1.hw,
- [CLKID_CPU_CLK_DYN] = &g12a_cpu_clk_dyn.hw,
- [CLKID_CPU_CLK] = &g12b_cpu_clk.hw,
- [CLKID_CPU_CLK_DIV16_EN] = &g12a_cpu_clk_div16_en.hw,
- [CLKID_CPU_CLK_DIV16] = &g12a_cpu_clk_div16.hw,
- [CLKID_CPU_CLK_APB_DIV] = &g12a_cpu_clk_apb_div.hw,
- [CLKID_CPU_CLK_APB] = &g12a_cpu_clk_apb.hw,
- [CLKID_CPU_CLK_ATB_DIV] = &g12a_cpu_clk_atb_div.hw,
- [CLKID_CPU_CLK_ATB] = &g12a_cpu_clk_atb.hw,
- [CLKID_CPU_CLK_AXI_DIV] = &g12a_cpu_clk_axi_div.hw,
- [CLKID_CPU_CLK_AXI] = &g12a_cpu_clk_axi.hw,
- [CLKID_CPU_CLK_TRACE_DIV] = &g12a_cpu_clk_trace_div.hw,
- [CLKID_CPU_CLK_TRACE] = &g12a_cpu_clk_trace.hw,
- [CLKID_PCIE_PLL_DCO] = &g12a_pcie_pll_dco.hw,
- [CLKID_PCIE_PLL_DCO_DIV2] = &g12a_pcie_pll_dco_div2.hw,
- [CLKID_PCIE_PLL_OD] = &g12a_pcie_pll_od.hw,
- [CLKID_PCIE_PLL] = &g12a_pcie_pll.hw,
- [CLKID_VDEC_1_SEL] = &g12a_vdec_1_sel.hw,
- [CLKID_VDEC_1_DIV] = &g12a_vdec_1_div.hw,
- [CLKID_VDEC_1] = &g12a_vdec_1.hw,
- [CLKID_VDEC_HEVC_SEL] = &g12a_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &g12a_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC] = &g12a_vdec_hevc.hw,
- [CLKID_VDEC_HEVCF_SEL] = &g12a_vdec_hevcf_sel.hw,
- [CLKID_VDEC_HEVCF_DIV] = &g12a_vdec_hevcf_div.hw,
- [CLKID_VDEC_HEVCF] = &g12a_vdec_hevcf.hw,
- [CLKID_TS_DIV] = &g12a_ts_div.hw,
- [CLKID_TS] = &g12a_ts.hw,
- [CLKID_SYS1_PLL_DCO] = &g12b_sys1_pll_dco.hw,
- [CLKID_SYS1_PLL] = &g12b_sys1_pll.hw,
- [CLKID_SYS1_PLL_DIV16_EN] = &g12b_sys1_pll_div16_en.hw,
- [CLKID_SYS1_PLL_DIV16] = &g12b_sys1_pll_div16.hw,
- [CLKID_CPUB_CLK_DYN0_SEL] = &g12b_cpub_clk_premux0.hw,
- [CLKID_CPUB_CLK_DYN0_DIV] = &g12b_cpub_clk_mux0_div.hw,
- [CLKID_CPUB_CLK_DYN0] = &g12b_cpub_clk_postmux0.hw,
- [CLKID_CPUB_CLK_DYN1_SEL] = &g12b_cpub_clk_premux1.hw,
- [CLKID_CPUB_CLK_DYN1_DIV] = &g12b_cpub_clk_mux1_div.hw,
- [CLKID_CPUB_CLK_DYN1] = &g12b_cpub_clk_postmux1.hw,
- [CLKID_CPUB_CLK_DYN] = &g12b_cpub_clk_dyn.hw,
- [CLKID_CPUB_CLK] = &g12b_cpub_clk.hw,
- [CLKID_CPUB_CLK_DIV16_EN] = &g12b_cpub_clk_div16_en.hw,
- [CLKID_CPUB_CLK_DIV16] = &g12b_cpub_clk_div16.hw,
- [CLKID_CPUB_CLK_DIV2] = &g12b_cpub_clk_div2.hw,
- [CLKID_CPUB_CLK_DIV3] = &g12b_cpub_clk_div3.hw,
- [CLKID_CPUB_CLK_DIV4] = &g12b_cpub_clk_div4.hw,
- [CLKID_CPUB_CLK_DIV5] = &g12b_cpub_clk_div5.hw,
- [CLKID_CPUB_CLK_DIV6] = &g12b_cpub_clk_div6.hw,
- [CLKID_CPUB_CLK_DIV7] = &g12b_cpub_clk_div7.hw,
- [CLKID_CPUB_CLK_DIV8] = &g12b_cpub_clk_div8.hw,
- [CLKID_CPUB_CLK_APB_SEL] = &g12b_cpub_clk_apb_sel.hw,
- [CLKID_CPUB_CLK_APB] = &g12b_cpub_clk_apb.hw,
- [CLKID_CPUB_CLK_ATB_SEL] = &g12b_cpub_clk_atb_sel.hw,
- [CLKID_CPUB_CLK_ATB] = &g12b_cpub_clk_atb.hw,
- [CLKID_CPUB_CLK_AXI_SEL] = &g12b_cpub_clk_axi_sel.hw,
- [CLKID_CPUB_CLK_AXI] = &g12b_cpub_clk_axi.hw,
- [CLKID_CPUB_CLK_TRACE_SEL] = &g12b_cpub_clk_trace_sel.hw,
- [CLKID_CPUB_CLK_TRACE] = &g12b_cpub_clk_trace.hw,
- [CLKID_SPICC0_SCLK_SEL] = &g12a_spicc0_sclk_sel.hw,
- [CLKID_SPICC0_SCLK_DIV] = &g12a_spicc0_sclk_div.hw,
- [CLKID_SPICC0_SCLK] = &g12a_spicc0_sclk.hw,
- [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw,
- [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw,
- [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw,
- [CLKID_NNA_AXI_CLK_SEL] = &sm1_nna_axi_clk_sel.hw,
- [CLKID_NNA_AXI_CLK_DIV] = &sm1_nna_axi_clk_div.hw,
- [CLKID_NNA_AXI_CLK] = &sm1_nna_axi_clk.hw,
- [CLKID_NNA_CORE_CLK_SEL] = &sm1_nna_core_clk_sel.hw,
- [CLKID_NNA_CORE_CLK_DIV] = &sm1_nna_core_clk_div.hw,
- [CLKID_NNA_CORE_CLK] = &sm1_nna_core_clk.hw,
- [CLKID_MIPI_DSI_PXCLK_SEL] = &g12a_mipi_dsi_pxclk_sel.hw,
- [CLKID_MIPI_DSI_PXCLK_DIV] = &g12a_mipi_dsi_pxclk_div.hw,
- [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-static struct clk_hw_onecell_data sm1_hw_onecell_data = {
- .hws = {
- [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
- [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
- [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
- [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
- [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
- [CLKID_MPEG_SEL] = &g12a_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &g12a_mpeg_clk_div.hw,
- [CLKID_CLK81] = &g12a_clk81.hw,
- [CLKID_MPLL0] = &g12a_mpll0.hw,
- [CLKID_MPLL1] = &g12a_mpll1.hw,
- [CLKID_MPLL2] = &g12a_mpll2.hw,
- [CLKID_MPLL3] = &g12a_mpll3.hw,
- [CLKID_DDR] = &g12a_ddr.hw,
- [CLKID_DOS] = &g12a_dos.hw,
- [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
- [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
- [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
- [CLKID_ISA] = &g12a_isa.hw,
- [CLKID_PL301] = &g12a_pl301.hw,
- [CLKID_PERIPHS] = &g12a_periphs.hw,
- [CLKID_SPICC0] = &g12a_spicc_0.hw,
- [CLKID_I2C] = &g12a_i2c.hw,
- [CLKID_SANA] = &g12a_sana.hw,
- [CLKID_SD] = &g12a_sd.hw,
- [CLKID_RNG0] = &g12a_rng0.hw,
- [CLKID_UART0] = &g12a_uart0.hw,
- [CLKID_SPICC1] = &g12a_spicc_1.hw,
- [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
- [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
- [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
- [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
- [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
- [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
- [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
- [CLKID_AUDIO] = &g12a_audio.hw,
- [CLKID_ETH] = &g12a_eth_core.hw,
- [CLKID_DEMUX] = &g12a_demux.hw,
- [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
- [CLKID_ADC] = &g12a_adc.hw,
- [CLKID_UART1] = &g12a_uart1.hw,
- [CLKID_G2D] = &g12a_g2d.hw,
- [CLKID_RESET] = &g12a_reset.hw,
- [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
- [CLKID_PARSER] = &g12a_parser.hw,
- [CLKID_USB] = &g12a_usb_general.hw,
- [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
- [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
- [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
- [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
- [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
- [CLKID_BT656] = &g12a_bt656.hw,
- [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
- [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
- [CLKID_UART2] = &g12a_uart2.hw,
- [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
- [CLKID_GIC] = &g12a_gic.hw,
- [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
- [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
- [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
- [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
- [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
- [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
- [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
- [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
- [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
- [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
- [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
- [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
- [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
- [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
- [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
- [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
- [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
- [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
- [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
- [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
- [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
- [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
- [CLKID_IEC958] = &g12a_iec958_gate.hw,
- [CLKID_ENC480P] = &g12a_enc480p.hw,
- [CLKID_RNG1] = &g12a_rng1.hw,
- [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
- [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
- [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
- [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
- [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
- [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
- [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
- [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
- [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
- [CLKID_DMA] = &g12a_dma.hw,
- [CLKID_EFUSE] = &g12a_efuse.hw,
- [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
- [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
- [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
- [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
- [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
- [CLKID_VPU_0] = &g12a_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
- [CLKID_VPU_1] = &g12a_vpu_1.hw,
- [CLKID_VPU] = &g12a_vpu.hw,
- [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
- [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
- [CLKID_VAPB_0] = &g12a_vapb_0.hw,
- [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
- [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
- [CLKID_VAPB_1] = &g12a_vapb_1.hw,
- [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
- [CLKID_VAPB] = &g12a_vapb.hw,
- [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
- [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
- [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
- [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
- [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
- [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
- [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
- [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
- [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
- [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
- [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
- [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
- [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
- [CLKID_VCLK] = &g12a_vclk.hw,
- [CLKID_VCLK2] = &g12a_vclk2.hw,
- [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
- [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
- [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
- [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
- [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
- [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
- [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
- [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
- [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
- [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
- [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
- [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
- [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
- [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
- [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
- [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
- [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
- [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
- [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
- [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
- [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
- [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
- [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
- [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
- [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
- [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
- [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
- [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
- [CLKID_HDMI] = &g12a_hdmi.hw,
- [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
- [CLKID_MALI_0] = &g12a_mali_0.hw,
- [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
- [CLKID_MALI_1] = &g12a_mali_1.hw,
- [CLKID_MALI] = &g12a_mali.hw,
- [CLKID_MPLL_50M_DIV] = &g12a_mpll_50m_div.hw,
- [CLKID_MPLL_50M] = &g12a_mpll_50m.hw,
- [CLKID_SYS_PLL_DIV16_EN] = &g12a_sys_pll_div16_en.hw,
- [CLKID_SYS_PLL_DIV16] = &g12a_sys_pll_div16.hw,
- [CLKID_CPU_CLK_DYN0_SEL] = &g12a_cpu_clk_premux0.hw,
- [CLKID_CPU_CLK_DYN0_DIV] = &g12a_cpu_clk_mux0_div.hw,
- [CLKID_CPU_CLK_DYN0] = &g12a_cpu_clk_postmux0.hw,
- [CLKID_CPU_CLK_DYN1_SEL] = &g12a_cpu_clk_premux1.hw,
- [CLKID_CPU_CLK_DYN1_DIV] = &g12a_cpu_clk_mux1_div.hw,
- [CLKID_CPU_CLK_DYN1] = &g12a_cpu_clk_postmux1.hw,
- [CLKID_CPU_CLK_DYN] = &g12a_cpu_clk_dyn.hw,
- [CLKID_CPU_CLK] = &g12a_cpu_clk.hw,
- [CLKID_CPU_CLK_DIV16_EN] = &g12a_cpu_clk_div16_en.hw,
- [CLKID_CPU_CLK_DIV16] = &g12a_cpu_clk_div16.hw,
- [CLKID_CPU_CLK_APB_DIV] = &g12a_cpu_clk_apb_div.hw,
- [CLKID_CPU_CLK_APB] = &g12a_cpu_clk_apb.hw,
- [CLKID_CPU_CLK_ATB_DIV] = &g12a_cpu_clk_atb_div.hw,
- [CLKID_CPU_CLK_ATB] = &g12a_cpu_clk_atb.hw,
- [CLKID_CPU_CLK_AXI_DIV] = &g12a_cpu_clk_axi_div.hw,
- [CLKID_CPU_CLK_AXI] = &g12a_cpu_clk_axi.hw,
- [CLKID_CPU_CLK_TRACE_DIV] = &g12a_cpu_clk_trace_div.hw,
- [CLKID_CPU_CLK_TRACE] = &g12a_cpu_clk_trace.hw,
- [CLKID_PCIE_PLL_DCO] = &g12a_pcie_pll_dco.hw,
- [CLKID_PCIE_PLL_DCO_DIV2] = &g12a_pcie_pll_dco_div2.hw,
- [CLKID_PCIE_PLL_OD] = &g12a_pcie_pll_od.hw,
- [CLKID_PCIE_PLL] = &g12a_pcie_pll.hw,
- [CLKID_VDEC_1_SEL] = &g12a_vdec_1_sel.hw,
- [CLKID_VDEC_1_DIV] = &g12a_vdec_1_div.hw,
- [CLKID_VDEC_1] = &g12a_vdec_1.hw,
- [CLKID_VDEC_HEVC_SEL] = &g12a_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &g12a_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC] = &g12a_vdec_hevc.hw,
- [CLKID_VDEC_HEVCF_SEL] = &g12a_vdec_hevcf_sel.hw,
- [CLKID_VDEC_HEVCF_DIV] = &g12a_vdec_hevcf_div.hw,
- [CLKID_VDEC_HEVCF] = &g12a_vdec_hevcf.hw,
- [CLKID_TS_DIV] = &g12a_ts_div.hw,
- [CLKID_TS] = &g12a_ts.hw,
- [CLKID_GP1_PLL_DCO] = &sm1_gp1_pll_dco.hw,
- [CLKID_GP1_PLL] = &sm1_gp1_pll.hw,
- [CLKID_DSU_CLK_DYN0_SEL] = &sm1_dsu_clk_premux0.hw,
- [CLKID_DSU_CLK_DYN0_DIV] = &sm1_dsu_clk_premux1.hw,
- [CLKID_DSU_CLK_DYN0] = &sm1_dsu_clk_mux0_div.hw,
- [CLKID_DSU_CLK_DYN1_SEL] = &sm1_dsu_clk_postmux0.hw,
- [CLKID_DSU_CLK_DYN1_DIV] = &sm1_dsu_clk_mux1_div.hw,
- [CLKID_DSU_CLK_DYN1] = &sm1_dsu_clk_postmux1.hw,
- [CLKID_DSU_CLK_DYN] = &sm1_dsu_clk_dyn.hw,
- [CLKID_DSU_CLK_FINAL] = &sm1_dsu_final_clk.hw,
- [CLKID_DSU_CLK] = &sm1_dsu_clk.hw,
- [CLKID_CPU1_CLK] = &sm1_cpu1_clk.hw,
- [CLKID_CPU2_CLK] = &sm1_cpu2_clk.hw,
- [CLKID_CPU3_CLK] = &sm1_cpu3_clk.hw,
- [CLKID_SPICC0_SCLK_SEL] = &g12a_spicc0_sclk_sel.hw,
- [CLKID_SPICC0_SCLK_DIV] = &g12a_spicc0_sclk_div.hw,
- [CLKID_SPICC0_SCLK] = &g12a_spicc0_sclk.hw,
- [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw,
- [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw,
- [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw,
- [CLKID_NNA_AXI_CLK_SEL] = &sm1_nna_axi_clk_sel.hw,
- [CLKID_NNA_AXI_CLK_DIV] = &sm1_nna_axi_clk_div.hw,
- [CLKID_NNA_AXI_CLK] = &sm1_nna_axi_clk.hw,
- [CLKID_NNA_CORE_CLK_SEL] = &sm1_nna_core_clk_sel.hw,
- [CLKID_NNA_CORE_CLK_DIV] = &sm1_nna_core_clk_div.hw,
- [CLKID_NNA_CORE_CLK] = &sm1_nna_core_clk.hw,
- [CLKID_MIPI_DSI_PXCLK_SEL] = &g12a_mipi_dsi_pxclk_sel.hw,
- [CLKID_MIPI_DSI_PXCLK_DIV] = &g12a_mipi_dsi_pxclk_div.hw,
- [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-/* Convenience table to populate regmap in .probe */
-static struct clk_regmap *const g12a_clk_regmaps[] = {
- &g12a_clk81,
- &g12a_dos,
- &g12a_ddr,
- &g12a_audio_locker,
- &g12a_mipi_dsi_host,
- &g12a_eth_phy,
- &g12a_isa,
- &g12a_pl301,
- &g12a_periphs,
- &g12a_spicc_0,
- &g12a_i2c,
- &g12a_sana,
- &g12a_sd,
- &g12a_rng0,
- &g12a_uart0,
- &g12a_spicc_1,
- &g12a_hiu_reg,
- &g12a_mipi_dsi_phy,
- &g12a_assist_misc,
- &g12a_emmc_a,
- &g12a_emmc_b,
- &g12a_emmc_c,
- &g12a_audio_codec,
- &g12a_audio,
- &g12a_eth_core,
- &g12a_demux,
- &g12a_audio_ififo,
- &g12a_adc,
- &g12a_uart1,
- &g12a_g2d,
- &g12a_reset,
- &g12a_pcie_comb,
- &g12a_parser,
- &g12a_usb_general,
- &g12a_pcie_phy,
- &g12a_ahb_arb0,
- &g12a_ahb_data_bus,
- &g12a_ahb_ctrl_bus,
- &g12a_htx_hdcp22,
- &g12a_htx_pclk,
- &g12a_bt656,
- &g12a_usb1_to_ddr,
- &g12a_mmc_pclk,
- &g12a_uart2,
- &g12a_vpu_intr,
- &g12a_gic,
- &g12a_sd_emmc_a_clk0,
- &g12a_sd_emmc_b_clk0,
- &g12a_sd_emmc_c_clk0,
- &g12a_mpeg_clk_div,
- &g12a_sd_emmc_a_clk0_div,
- &g12a_sd_emmc_b_clk0_div,
- &g12a_sd_emmc_c_clk0_div,
- &g12a_mpeg_clk_sel,
- &g12a_sd_emmc_a_clk0_sel,
- &g12a_sd_emmc_b_clk0_sel,
- &g12a_sd_emmc_c_clk0_sel,
- &g12a_mpll0,
- &g12a_mpll1,
- &g12a_mpll2,
- &g12a_mpll3,
- &g12a_mpll0_div,
- &g12a_mpll1_div,
- &g12a_mpll2_div,
- &g12a_mpll3_div,
- &g12a_fixed_pll,
- &g12a_sys_pll,
- &g12a_gp0_pll,
- &g12a_hifi_pll,
- &g12a_vclk2_venci0,
- &g12a_vclk2_venci1,
- &g12a_vclk2_vencp0,
- &g12a_vclk2_vencp1,
- &g12a_vclk2_venct0,
- &g12a_vclk2_venct1,
- &g12a_vclk2_other,
- &g12a_vclk2_enci,
- &g12a_vclk2_encp,
- &g12a_dac_clk,
- &g12a_aoclk_gate,
- &g12a_iec958_gate,
- &g12a_enc480p,
- &g12a_rng1,
- &g12a_vclk2_enct,
- &g12a_vclk2_encl,
- &g12a_vclk2_venclmmc,
- &g12a_vclk2_vencl,
- &g12a_vclk2_other1,
- &g12a_fixed_pll_dco,
- &g12a_sys_pll_dco,
- &g12a_gp0_pll_dco,
- &g12a_hifi_pll_dco,
- &g12a_fclk_div2,
- &g12a_fclk_div3,
- &g12a_fclk_div4,
- &g12a_fclk_div5,
- &g12a_fclk_div7,
- &g12a_fclk_div2p5,
- &g12a_dma,
- &g12a_efuse,
- &g12a_rom_boot,
- &g12a_reset_sec,
- &g12a_sec_ahb_apb3,
- &g12a_vpu_0_sel,
- &g12a_vpu_0_div,
- &g12a_vpu_0,
- &g12a_vpu_1_sel,
- &g12a_vpu_1_div,
- &g12a_vpu_1,
- &g12a_vpu,
- &g12a_vapb_0_sel,
- &g12a_vapb_0_div,
- &g12a_vapb_0,
- &g12a_vapb_1_sel,
- &g12a_vapb_1_div,
- &g12a_vapb_1,
- &g12a_vapb_sel,
- &g12a_vapb,
- &g12a_hdmi_pll_dco,
- &g12a_hdmi_pll_od,
- &g12a_hdmi_pll_od2,
- &g12a_hdmi_pll,
- &g12a_vid_pll_div,
- &g12a_vid_pll_sel,
- &g12a_vid_pll,
- &g12a_vclk_sel,
- &g12a_vclk2_sel,
- &g12a_vclk_input,
- &g12a_vclk2_input,
- &g12a_vclk_div,
- &g12a_vclk2_div,
- &g12a_vclk,
- &g12a_vclk2,
- &g12a_vclk_div1,
- &g12a_vclk_div2_en,
- &g12a_vclk_div4_en,
- &g12a_vclk_div6_en,
- &g12a_vclk_div12_en,
- &g12a_vclk2_div1,
- &g12a_vclk2_div2_en,
- &g12a_vclk2_div4_en,
- &g12a_vclk2_div6_en,
- &g12a_vclk2_div12_en,
- &g12a_cts_enci_sel,
- &g12a_cts_encp_sel,
- &g12a_cts_vdac_sel,
- &g12a_hdmi_tx_sel,
- &g12a_cts_enci,
- &g12a_cts_encp,
- &g12a_cts_vdac,
- &g12a_hdmi_tx,
- &g12a_hdmi_sel,
- &g12a_hdmi_div,
- &g12a_hdmi,
- &g12a_mali_0_sel,
- &g12a_mali_0_div,
- &g12a_mali_0,
- &g12a_mali_1_sel,
- &g12a_mali_1_div,
- &g12a_mali_1,
- &g12a_mali,
- &g12a_mpll_50m,
- &g12a_sys_pll_div16_en,
- &g12a_cpu_clk_premux0,
- &g12a_cpu_clk_mux0_div,
- &g12a_cpu_clk_postmux0,
- &g12a_cpu_clk_premux1,
- &g12a_cpu_clk_mux1_div,
- &g12a_cpu_clk_postmux1,
- &g12a_cpu_clk_dyn,
- &g12a_cpu_clk,
- &g12a_cpu_clk_div16_en,
- &g12a_cpu_clk_apb_div,
- &g12a_cpu_clk_apb,
- &g12a_cpu_clk_atb_div,
- &g12a_cpu_clk_atb,
- &g12a_cpu_clk_axi_div,
- &g12a_cpu_clk_axi,
- &g12a_cpu_clk_trace_div,
- &g12a_cpu_clk_trace,
- &g12a_pcie_pll_od,
- &g12a_pcie_pll_dco,
- &g12a_vdec_1_sel,
- &g12a_vdec_1_div,
- &g12a_vdec_1,
- &g12a_vdec_hevc_sel,
- &g12a_vdec_hevc_div,
- &g12a_vdec_hevc,
- &g12a_vdec_hevcf_sel,
- &g12a_vdec_hevcf_div,
- &g12a_vdec_hevcf,
- &g12a_ts_div,
- &g12a_ts,
- &g12b_cpu_clk,
- &g12b_sys1_pll_dco,
- &g12b_sys1_pll,
- &g12b_sys1_pll_div16_en,
- &g12b_cpub_clk_premux0,
- &g12b_cpub_clk_mux0_div,
- &g12b_cpub_clk_postmux0,
- &g12b_cpub_clk_premux1,
- &g12b_cpub_clk_mux1_div,
- &g12b_cpub_clk_postmux1,
- &g12b_cpub_clk_dyn,
- &g12b_cpub_clk,
- &g12b_cpub_clk_div16_en,
- &g12b_cpub_clk_apb_sel,
- &g12b_cpub_clk_apb,
- &g12b_cpub_clk_atb_sel,
- &g12b_cpub_clk_atb,
- &g12b_cpub_clk_axi_sel,
- &g12b_cpub_clk_axi,
- &g12b_cpub_clk_trace_sel,
- &g12b_cpub_clk_trace,
- &sm1_gp1_pll_dco,
- &sm1_gp1_pll,
- &sm1_dsu_clk_premux0,
- &sm1_dsu_clk_premux1,
- &sm1_dsu_clk_mux0_div,
- &sm1_dsu_clk_postmux0,
- &sm1_dsu_clk_mux1_div,
- &sm1_dsu_clk_postmux1,
- &sm1_dsu_clk_dyn,
- &sm1_dsu_final_clk,
- &sm1_dsu_clk,
- &sm1_cpu1_clk,
- &sm1_cpu2_clk,
- &sm1_cpu3_clk,
- &g12a_spicc0_sclk_sel,
- &g12a_spicc0_sclk_div,
- &g12a_spicc0_sclk,
- &g12a_spicc1_sclk_sel,
- &g12a_spicc1_sclk_div,
- &g12a_spicc1_sclk,
- &sm1_nna_axi_clk_sel,
- &sm1_nna_axi_clk_div,
- &sm1_nna_axi_clk,
- &sm1_nna_core_clk_sel,
- &sm1_nna_core_clk_div,
- &sm1_nna_core_clk,
- &g12a_mipi_dsi_pxclk_sel,
- &g12a_mipi_dsi_pxclk_div,
- &g12a_mipi_dsi_pxclk,
+static struct clk_hw *g12a_hw_clks[] = {
+ [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
+ [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
+ [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
+ [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &g12a_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &g12a_clk81_div.hw,
+ [CLKID_CLK81] = &g12a_clk81.hw,
+ [CLKID_MPLL0] = &g12a_mpll0.hw,
+ [CLKID_MPLL1] = &g12a_mpll1.hw,
+ [CLKID_MPLL2] = &g12a_mpll2.hw,
+ [CLKID_MPLL3] = &g12a_mpll3.hw,
+ [CLKID_DDR] = &g12a_ddr.hw,
+ [CLKID_DOS] = &g12a_dos.hw,
+ [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
+ [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
+ [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
+ [CLKID_ISA] = &g12a_isa.hw,
+ [CLKID_PL301] = &g12a_pl301.hw,
+ [CLKID_PERIPHS] = &g12a_periphs.hw,
+ [CLKID_SPICC0] = &g12a_spicc_0.hw,
+ [CLKID_I2C] = &g12a_i2c.hw,
+ [CLKID_SANA] = &g12a_sana.hw,
+ [CLKID_SD] = &g12a_sd.hw,
+ [CLKID_RNG0] = &g12a_rng0.hw,
+ [CLKID_UART0] = &g12a_uart0.hw,
+ [CLKID_SPICC1] = &g12a_spicc_1.hw,
+ [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
+ [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
+ [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
+ [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
+ [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
+ [CLKID_AUDIO] = &g12a_audio.hw,
+ [CLKID_ETH] = &g12a_eth_core.hw,
+ [CLKID_DEMUX] = &g12a_demux.hw,
+ [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
+ [CLKID_ADC] = &g12a_adc.hw,
+ [CLKID_UART1] = &g12a_uart1.hw,
+ [CLKID_G2D] = &g12a_g2d.hw,
+ [CLKID_RESET] = &g12a_reset.hw,
+ [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
+ [CLKID_PARSER] = &g12a_parser.hw,
+ [CLKID_USB] = &g12a_usb_general.hw,
+ [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
+ [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
+ [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
+ [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
+ [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
+ [CLKID_BT656] = &g12a_bt656.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
+ [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
+ [CLKID_UART2] = &g12a_uart2.hw,
+ [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
+ [CLKID_GIC] = &g12a_gic.hw,
+ [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
+ [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
+ [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
+ [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
+ [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
+ [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
+ [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
+ [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
+ [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
+ [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
+ [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
+ [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
+ [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
+ [CLKID_IEC958] = &g12a_iec958_gate.hw,
+ [CLKID_ENC480P] = &g12a_enc480p.hw,
+ [CLKID_RNG1] = &g12a_rng1.hw,
+ [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
+ [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
+ [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
+ [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
+ [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
+ [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
+ [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
+ [CLKID_DMA] = &g12a_dma.hw,
+ [CLKID_EFUSE] = &g12a_efuse.hw,
+ [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
+ [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
+ [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
+ [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
+ [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
+ [CLKID_VPU_0] = &g12a_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
+ [CLKID_VPU_1] = &g12a_vpu_1.hw,
+ [CLKID_VPU] = &g12a_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &g12a_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &g12a_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
+ [CLKID_VAPB] = &g12a_vapb.hw,
+ [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
+ [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
+ [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
+ [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
+ [CLKID_VCLK] = &g12a_vclk.hw,
+ [CLKID_VCLK2] = &g12a_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
+ [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
+ [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
+ [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
+ [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
+ [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
+ [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
+ [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
+ [CLKID_HDMI] = &g12a_hdmi.hw,
+ [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
+ [CLKID_MALI_0] = &g12a_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
+ [CLKID_MALI_1] = &g12a_mali_1.hw,
+ [CLKID_MALI] = &g12a_mali.hw,
+ [CLKID_MPLL_50M_DIV] = &g12a_mpll_50m_div.hw,
+ [CLKID_MPLL_50M] = &g12a_mpll_50m.hw,
+ [CLKID_SYS_PLL_DIV16_EN] = &g12a_sys_pll_div16_en.hw,
+ [CLKID_SYS_PLL_DIV16] = &g12a_sys_pll_div16.hw,
+ [CLKID_CPU_CLK_DYN0_SEL] = &g12a_cpu_clk_dyn0_sel.hw,
+ [CLKID_CPU_CLK_DYN0_DIV] = &g12a_cpu_clk_dyn0_div.hw,
+ [CLKID_CPU_CLK_DYN0] = &g12a_cpu_clk_dyn0.hw,
+ [CLKID_CPU_CLK_DYN1_SEL] = &g12a_cpu_clk_dyn1_sel.hw,
+ [CLKID_CPU_CLK_DYN1_DIV] = &g12a_cpu_clk_dyn1_div.hw,
+ [CLKID_CPU_CLK_DYN1] = &g12a_cpu_clk_dyn1.hw,
+ [CLKID_CPU_CLK_DYN] = &g12a_cpu_clk_dyn.hw,
+ [CLKID_CPU_CLK] = &g12a_cpu_clk.hw,
+ [CLKID_CPU_CLK_DIV16_EN] = &g12a_cpu_clk_div16_en.hw,
+ [CLKID_CPU_CLK_DIV16] = &g12a_cpu_clk_div16.hw,
+ [CLKID_CPU_CLK_APB_DIV] = &g12a_cpu_clk_apb_div.hw,
+ [CLKID_CPU_CLK_APB] = &g12a_cpu_clk_apb.hw,
+ [CLKID_CPU_CLK_ATB_DIV] = &g12a_cpu_clk_atb_div.hw,
+ [CLKID_CPU_CLK_ATB] = &g12a_cpu_clk_atb.hw,
+ [CLKID_CPU_CLK_AXI_DIV] = &g12a_cpu_clk_axi_div.hw,
+ [CLKID_CPU_CLK_AXI] = &g12a_cpu_clk_axi.hw,
+ [CLKID_CPU_CLK_TRACE_DIV] = &g12a_cpu_clk_trace_div.hw,
+ [CLKID_CPU_CLK_TRACE] = &g12a_cpu_clk_trace.hw,
+ [CLKID_PCIE_PLL_DCO] = &g12a_pcie_pll_dco.hw,
+ [CLKID_PCIE_PLL_DCO_DIV2] = &g12a_pcie_pll_dco_div2.hw,
+ [CLKID_PCIE_PLL_OD] = &g12a_pcie_pll_od.hw,
+ [CLKID_PCIE_PLL] = &g12a_pcie_pll.hw,
+ [CLKID_VDEC_1_SEL] = &g12a_vdec_1_sel.hw,
+ [CLKID_VDEC_1_DIV] = &g12a_vdec_1_div.hw,
+ [CLKID_VDEC_1] = &g12a_vdec_1.hw,
+ [CLKID_VDEC_HEVC_SEL] = &g12a_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &g12a_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC] = &g12a_vdec_hevc.hw,
+ [CLKID_VDEC_HEVCF_SEL] = &g12a_vdec_hevcf_sel.hw,
+ [CLKID_VDEC_HEVCF_DIV] = &g12a_vdec_hevcf_div.hw,
+ [CLKID_VDEC_HEVCF] = &g12a_vdec_hevcf.hw,
+ [CLKID_TS_DIV] = &g12a_ts_div.hw,
+ [CLKID_TS] = &g12a_ts.hw,
+ [CLKID_SPICC0_SCLK_SEL] = &g12a_spicc0_sclk_sel.hw,
+ [CLKID_SPICC0_SCLK_DIV] = &g12a_spicc0_sclk_div.hw,
+ [CLKID_SPICC0_SCLK] = &g12a_spicc0_sclk.hw,
+ [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw,
+ [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw,
+ [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw,
+ [CLKID_MIPI_DSI_PXCLK_SEL] = &g12a_mipi_dsi_pxclk_sel.hw,
+ [CLKID_MIPI_DSI_PXCLK_DIV] = &g12a_mipi_dsi_pxclk_div.hw,
+ [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw,
+};
+
+static struct clk_hw *g12b_hw_clks[] = {
+ [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
+ [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
+ [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
+ [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &g12a_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &g12a_clk81_div.hw,
+ [CLKID_CLK81] = &g12a_clk81.hw,
+ [CLKID_MPLL0] = &g12a_mpll0.hw,
+ [CLKID_MPLL1] = &g12a_mpll1.hw,
+ [CLKID_MPLL2] = &g12a_mpll2.hw,
+ [CLKID_MPLL3] = &g12a_mpll3.hw,
+ [CLKID_DDR] = &g12a_ddr.hw,
+ [CLKID_DOS] = &g12a_dos.hw,
+ [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
+ [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
+ [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
+ [CLKID_ISA] = &g12a_isa.hw,
+ [CLKID_PL301] = &g12a_pl301.hw,
+ [CLKID_PERIPHS] = &g12a_periphs.hw,
+ [CLKID_SPICC0] = &g12a_spicc_0.hw,
+ [CLKID_I2C] = &g12a_i2c.hw,
+ [CLKID_SANA] = &g12a_sana.hw,
+ [CLKID_SD] = &g12a_sd.hw,
+ [CLKID_RNG0] = &g12a_rng0.hw,
+ [CLKID_UART0] = &g12a_uart0.hw,
+ [CLKID_SPICC1] = &g12a_spicc_1.hw,
+ [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
+ [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
+ [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
+ [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
+ [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
+ [CLKID_AUDIO] = &g12a_audio.hw,
+ [CLKID_ETH] = &g12a_eth_core.hw,
+ [CLKID_DEMUX] = &g12a_demux.hw,
+ [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
+ [CLKID_ADC] = &g12a_adc.hw,
+ [CLKID_UART1] = &g12a_uart1.hw,
+ [CLKID_G2D] = &g12a_g2d.hw,
+ [CLKID_RESET] = &g12a_reset.hw,
+ [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
+ [CLKID_PARSER] = &g12a_parser.hw,
+ [CLKID_USB] = &g12a_usb_general.hw,
+ [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
+ [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
+ [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
+ [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
+ [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
+ [CLKID_BT656] = &g12a_bt656.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
+ [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
+ [CLKID_UART2] = &g12a_uart2.hw,
+ [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
+ [CLKID_GIC] = &g12a_gic.hw,
+ [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
+ [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
+ [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
+ [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
+ [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
+ [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
+ [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
+ [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
+ [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
+ [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
+ [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
+ [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
+ [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
+ [CLKID_IEC958] = &g12a_iec958_gate.hw,
+ [CLKID_ENC480P] = &g12a_enc480p.hw,
+ [CLKID_RNG1] = &g12a_rng1.hw,
+ [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
+ [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
+ [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
+ [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
+ [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
+ [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
+ [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
+ [CLKID_DMA] = &g12a_dma.hw,
+ [CLKID_EFUSE] = &g12a_efuse.hw,
+ [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
+ [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
+ [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
+ [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
+ [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
+ [CLKID_VPU_0] = &g12a_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
+ [CLKID_VPU_1] = &g12a_vpu_1.hw,
+ [CLKID_VPU] = &g12a_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &g12a_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &g12a_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
+ [CLKID_VAPB] = &g12a_vapb.hw,
+ [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
+ [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
+ [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
+ [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
+ [CLKID_VCLK] = &g12a_vclk.hw,
+ [CLKID_VCLK2] = &g12a_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
+ [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
+ [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
+ [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
+ [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
+ [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
+ [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
+ [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
+ [CLKID_HDMI] = &g12a_hdmi.hw,
+ [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
+ [CLKID_MALI_0] = &g12a_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
+ [CLKID_MALI_1] = &g12a_mali_1.hw,
+ [CLKID_MALI] = &g12a_mali.hw,
+ [CLKID_MPLL_50M_DIV] = &g12a_mpll_50m_div.hw,
+ [CLKID_MPLL_50M] = &g12a_mpll_50m.hw,
+ [CLKID_SYS_PLL_DIV16_EN] = &g12a_sys_pll_div16_en.hw,
+ [CLKID_SYS_PLL_DIV16] = &g12a_sys_pll_div16.hw,
+ [CLKID_CPU_CLK_DYN0_SEL] = &g12a_cpu_clk_dyn0_sel.hw,
+ [CLKID_CPU_CLK_DYN0_DIV] = &g12a_cpu_clk_dyn0_div.hw,
+ [CLKID_CPU_CLK_DYN0] = &g12a_cpu_clk_dyn0.hw,
+ [CLKID_CPU_CLK_DYN1_SEL] = &g12a_cpu_clk_dyn1_sel.hw,
+ [CLKID_CPU_CLK_DYN1_DIV] = &g12a_cpu_clk_dyn1_div.hw,
+ [CLKID_CPU_CLK_DYN1] = &g12a_cpu_clk_dyn1.hw,
+ [CLKID_CPU_CLK_DYN] = &g12a_cpu_clk_dyn.hw,
+ [CLKID_CPU_CLK] = &g12b_cpu_clk.hw,
+ [CLKID_CPU_CLK_DIV16_EN] = &g12a_cpu_clk_div16_en.hw,
+ [CLKID_CPU_CLK_DIV16] = &g12a_cpu_clk_div16.hw,
+ [CLKID_CPU_CLK_APB_DIV] = &g12a_cpu_clk_apb_div.hw,
+ [CLKID_CPU_CLK_APB] = &g12a_cpu_clk_apb.hw,
+ [CLKID_CPU_CLK_ATB_DIV] = &g12a_cpu_clk_atb_div.hw,
+ [CLKID_CPU_CLK_ATB] = &g12a_cpu_clk_atb.hw,
+ [CLKID_CPU_CLK_AXI_DIV] = &g12a_cpu_clk_axi_div.hw,
+ [CLKID_CPU_CLK_AXI] = &g12a_cpu_clk_axi.hw,
+ [CLKID_CPU_CLK_TRACE_DIV] = &g12a_cpu_clk_trace_div.hw,
+ [CLKID_CPU_CLK_TRACE] = &g12a_cpu_clk_trace.hw,
+ [CLKID_PCIE_PLL_DCO] = &g12a_pcie_pll_dco.hw,
+ [CLKID_PCIE_PLL_DCO_DIV2] = &g12a_pcie_pll_dco_div2.hw,
+ [CLKID_PCIE_PLL_OD] = &g12a_pcie_pll_od.hw,
+ [CLKID_PCIE_PLL] = &g12a_pcie_pll.hw,
+ [CLKID_VDEC_1_SEL] = &g12a_vdec_1_sel.hw,
+ [CLKID_VDEC_1_DIV] = &g12a_vdec_1_div.hw,
+ [CLKID_VDEC_1] = &g12a_vdec_1.hw,
+ [CLKID_VDEC_HEVC_SEL] = &g12a_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &g12a_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC] = &g12a_vdec_hevc.hw,
+ [CLKID_VDEC_HEVCF_SEL] = &g12a_vdec_hevcf_sel.hw,
+ [CLKID_VDEC_HEVCF_DIV] = &g12a_vdec_hevcf_div.hw,
+ [CLKID_VDEC_HEVCF] = &g12a_vdec_hevcf.hw,
+ [CLKID_TS_DIV] = &g12a_ts_div.hw,
+ [CLKID_TS] = &g12a_ts.hw,
+ [CLKID_SYS1_PLL_DCO] = &g12b_sys1_pll_dco.hw,
+ [CLKID_SYS1_PLL] = &g12b_sys1_pll.hw,
+ [CLKID_SYS1_PLL_DIV16_EN] = &g12b_sys1_pll_div16_en.hw,
+ [CLKID_SYS1_PLL_DIV16] = &g12b_sys1_pll_div16.hw,
+ [CLKID_CPUB_CLK_DYN0_SEL] = &g12b_cpub_clk_dyn0_sel.hw,
+ [CLKID_CPUB_CLK_DYN0_DIV] = &g12b_cpub_clk_dyn0_div.hw,
+ [CLKID_CPUB_CLK_DYN0] = &g12b_cpub_clk_dyn0.hw,
+ [CLKID_CPUB_CLK_DYN1_SEL] = &g12b_cpub_clk_dyn1_sel.hw,
+ [CLKID_CPUB_CLK_DYN1_DIV] = &g12b_cpub_clk_dyn1_div.hw,
+ [CLKID_CPUB_CLK_DYN1] = &g12b_cpub_clk_dyn1.hw,
+ [CLKID_CPUB_CLK_DYN] = &g12b_cpub_clk_dyn.hw,
+ [CLKID_CPUB_CLK] = &g12b_cpub_clk.hw,
+ [CLKID_CPUB_CLK_DIV16_EN] = &g12b_cpub_clk_div16_en.hw,
+ [CLKID_CPUB_CLK_DIV16] = &g12b_cpub_clk_div16.hw,
+ [CLKID_CPUB_CLK_DIV2] = &g12b_cpub_clk_div2.hw,
+ [CLKID_CPUB_CLK_DIV3] = &g12b_cpub_clk_div3.hw,
+ [CLKID_CPUB_CLK_DIV4] = &g12b_cpub_clk_div4.hw,
+ [CLKID_CPUB_CLK_DIV5] = &g12b_cpub_clk_div5.hw,
+ [CLKID_CPUB_CLK_DIV6] = &g12b_cpub_clk_div6.hw,
+ [CLKID_CPUB_CLK_DIV7] = &g12b_cpub_clk_div7.hw,
+ [CLKID_CPUB_CLK_DIV8] = &g12b_cpub_clk_div8.hw,
+ [CLKID_CPUB_CLK_APB_SEL] = &g12b_cpub_clk_apb_sel.hw,
+ [CLKID_CPUB_CLK_APB] = &g12b_cpub_clk_apb.hw,
+ [CLKID_CPUB_CLK_ATB_SEL] = &g12b_cpub_clk_atb_sel.hw,
+ [CLKID_CPUB_CLK_ATB] = &g12b_cpub_clk_atb.hw,
+ [CLKID_CPUB_CLK_AXI_SEL] = &g12b_cpub_clk_axi_sel.hw,
+ [CLKID_CPUB_CLK_AXI] = &g12b_cpub_clk_axi.hw,
+ [CLKID_CPUB_CLK_TRACE_SEL] = &g12b_cpub_clk_trace_sel.hw,
+ [CLKID_CPUB_CLK_TRACE] = &g12b_cpub_clk_trace.hw,
+ [CLKID_SPICC0_SCLK_SEL] = &g12a_spicc0_sclk_sel.hw,
+ [CLKID_SPICC0_SCLK_DIV] = &g12a_spicc0_sclk_div.hw,
+ [CLKID_SPICC0_SCLK] = &g12a_spicc0_sclk.hw,
+ [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw,
+ [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw,
+ [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw,
+ [CLKID_NNA_AXI_CLK_SEL] = &sm1_nna_axi_clk_sel.hw,
+ [CLKID_NNA_AXI_CLK_DIV] = &sm1_nna_axi_clk_div.hw,
+ [CLKID_NNA_AXI_CLK] = &sm1_nna_axi_clk.hw,
+ [CLKID_NNA_CORE_CLK_SEL] = &sm1_nna_core_clk_sel.hw,
+ [CLKID_NNA_CORE_CLK_DIV] = &sm1_nna_core_clk_div.hw,
+ [CLKID_NNA_CORE_CLK] = &sm1_nna_core_clk.hw,
+ [CLKID_MIPI_DSI_PXCLK_SEL] = &g12a_mipi_dsi_pxclk_sel.hw,
+ [CLKID_MIPI_DSI_PXCLK_DIV] = &g12a_mipi_dsi_pxclk_div.hw,
+ [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw,
+ [CLKID_MIPI_ISP_SEL] = &g12b_mipi_isp_sel.hw,
+ [CLKID_MIPI_ISP_DIV] = &g12b_mipi_isp_div.hw,
+ [CLKID_MIPI_ISP] = &g12b_mipi_isp.hw,
+ [CLKID_MIPI_ISP_GATE] = &g12b_mipi_isp_gate.hw,
+ [CLKID_MIPI_ISP_CSI_PHY0] = &g12b_csi_phy0.hw,
+ [CLKID_MIPI_ISP_CSI_PHY1] = &g12b_csi_phy1.hw,
+};
+
+static struct clk_hw *sm1_hw_clks[] = {
+ [CLKID_SYS_PLL] = &g12a_sys_pll.hw,
+ [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw,
+ [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw,
+ [CLKID_GP0_PLL] = &g12a_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &g12a_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &g12a_clk81_div.hw,
+ [CLKID_CLK81] = &g12a_clk81.hw,
+ [CLKID_MPLL0] = &g12a_mpll0.hw,
+ [CLKID_MPLL1] = &g12a_mpll1.hw,
+ [CLKID_MPLL2] = &g12a_mpll2.hw,
+ [CLKID_MPLL3] = &g12a_mpll3.hw,
+ [CLKID_DDR] = &g12a_ddr.hw,
+ [CLKID_DOS] = &g12a_dos.hw,
+ [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw,
+ [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw,
+ [CLKID_ETH_PHY] = &g12a_eth_phy.hw,
+ [CLKID_ISA] = &g12a_isa.hw,
+ [CLKID_PL301] = &g12a_pl301.hw,
+ [CLKID_PERIPHS] = &g12a_periphs.hw,
+ [CLKID_SPICC0] = &g12a_spicc_0.hw,
+ [CLKID_I2C] = &g12a_i2c.hw,
+ [CLKID_SANA] = &g12a_sana.hw,
+ [CLKID_SD] = &g12a_sd.hw,
+ [CLKID_RNG0] = &g12a_rng0.hw,
+ [CLKID_UART0] = &g12a_uart0.hw,
+ [CLKID_SPICC1] = &g12a_spicc_1.hw,
+ [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw,
+ [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw,
+ [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw,
+ [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw,
+ [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw,
+ [CLKID_AUDIO] = &g12a_audio.hw,
+ [CLKID_ETH] = &g12a_eth_core.hw,
+ [CLKID_DEMUX] = &g12a_demux.hw,
+ [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw,
+ [CLKID_ADC] = &g12a_adc.hw,
+ [CLKID_UART1] = &g12a_uart1.hw,
+ [CLKID_G2D] = &g12a_g2d.hw,
+ [CLKID_RESET] = &g12a_reset.hw,
+ [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw,
+ [CLKID_PARSER] = &g12a_parser.hw,
+ [CLKID_USB] = &g12a_usb_general.hw,
+ [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw,
+ [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw,
+ [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw,
+ [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw,
+ [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw,
+ [CLKID_BT656] = &g12a_bt656.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw,
+ [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw,
+ [CLKID_UART2] = &g12a_uart2.hw,
+ [CLKID_VPU_INTR] = &g12a_vpu_intr.hw,
+ [CLKID_GIC] = &g12a_gic.hw,
+ [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw,
+ [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw,
+ [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw,
+ [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw,
+ [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw,
+ [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw,
+ [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw,
+ [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw,
+ [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw,
+ [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw,
+ [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw,
+ [CLKID_DAC_CLK] = &g12a_dac_clk.hw,
+ [CLKID_AOCLK] = &g12a_aoclk_gate.hw,
+ [CLKID_IEC958] = &g12a_iec958_gate.hw,
+ [CLKID_ENC480P] = &g12a_enc480p.hw,
+ [CLKID_RNG1] = &g12a_rng1.hw,
+ [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw,
+ [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw,
+ [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw,
+ [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw,
+ [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw,
+ [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw,
+ [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw,
+ [CLKID_DMA] = &g12a_dma.hw,
+ [CLKID_EFUSE] = &g12a_efuse.hw,
+ [CLKID_ROM_BOOT] = &g12a_rom_boot.hw,
+ [CLKID_RESET_SEC] = &g12a_reset_sec.hw,
+ [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw,
+ [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw,
+ [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw,
+ [CLKID_VPU_0] = &g12a_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw,
+ [CLKID_VPU_1] = &g12a_vpu_1.hw,
+ [CLKID_VPU] = &g12a_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &g12a_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &g12a_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw,
+ [CLKID_VAPB] = &g12a_vapb.hw,
+ [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw,
+ [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw,
+ [CLKID_VID_PLL] = &g12a_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw,
+ [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &g12a_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw,
+ [CLKID_VCLK] = &g12a_vclk.hw,
+ [CLKID_VCLK2] = &g12a_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw,
+ [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw,
+ [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw,
+ [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw,
+ [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw,
+ [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw,
+ [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
+ [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw,
+ [CLKID_HDMI] = &g12a_hdmi.hw,
+ [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw,
+ [CLKID_MALI_0] = &g12a_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw,
+ [CLKID_MALI_1] = &g12a_mali_1.hw,
+ [CLKID_MALI] = &g12a_mali.hw,
+ [CLKID_MPLL_50M_DIV] = &g12a_mpll_50m_div.hw,
+ [CLKID_MPLL_50M] = &g12a_mpll_50m.hw,
+ [CLKID_SYS_PLL_DIV16_EN] = &g12a_sys_pll_div16_en.hw,
+ [CLKID_SYS_PLL_DIV16] = &g12a_sys_pll_div16.hw,
+ [CLKID_CPU_CLK_DYN0_SEL] = &g12a_cpu_clk_dyn0_sel.hw,
+ [CLKID_CPU_CLK_DYN0_DIV] = &g12a_cpu_clk_dyn0_div.hw,
+ [CLKID_CPU_CLK_DYN0] = &g12a_cpu_clk_dyn0.hw,
+ [CLKID_CPU_CLK_DYN1_SEL] = &g12a_cpu_clk_dyn1_sel.hw,
+ [CLKID_CPU_CLK_DYN1_DIV] = &g12a_cpu_clk_dyn1_div.hw,
+ [CLKID_CPU_CLK_DYN1] = &g12a_cpu_clk_dyn1.hw,
+ [CLKID_CPU_CLK_DYN] = &g12a_cpu_clk_dyn.hw,
+ [CLKID_CPU_CLK] = &g12a_cpu_clk.hw,
+ [CLKID_CPU_CLK_DIV16_EN] = &g12a_cpu_clk_div16_en.hw,
+ [CLKID_CPU_CLK_DIV16] = &g12a_cpu_clk_div16.hw,
+ [CLKID_CPU_CLK_APB_DIV] = &g12a_cpu_clk_apb_div.hw,
+ [CLKID_CPU_CLK_APB] = &g12a_cpu_clk_apb.hw,
+ [CLKID_CPU_CLK_ATB_DIV] = &g12a_cpu_clk_atb_div.hw,
+ [CLKID_CPU_CLK_ATB] = &g12a_cpu_clk_atb.hw,
+ [CLKID_CPU_CLK_AXI_DIV] = &g12a_cpu_clk_axi_div.hw,
+ [CLKID_CPU_CLK_AXI] = &g12a_cpu_clk_axi.hw,
+ [CLKID_CPU_CLK_TRACE_DIV] = &g12a_cpu_clk_trace_div.hw,
+ [CLKID_CPU_CLK_TRACE] = &g12a_cpu_clk_trace.hw,
+ [CLKID_PCIE_PLL_DCO] = &g12a_pcie_pll_dco.hw,
+ [CLKID_PCIE_PLL_DCO_DIV2] = &g12a_pcie_pll_dco_div2.hw,
+ [CLKID_PCIE_PLL_OD] = &g12a_pcie_pll_od.hw,
+ [CLKID_PCIE_PLL] = &g12a_pcie_pll.hw,
+ [CLKID_VDEC_1_SEL] = &g12a_vdec_1_sel.hw,
+ [CLKID_VDEC_1_DIV] = &g12a_vdec_1_div.hw,
+ [CLKID_VDEC_1] = &g12a_vdec_1.hw,
+ [CLKID_VDEC_HEVC_SEL] = &g12a_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &g12a_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC] = &g12a_vdec_hevc.hw,
+ [CLKID_VDEC_HEVCF_SEL] = &g12a_vdec_hevcf_sel.hw,
+ [CLKID_VDEC_HEVCF_DIV] = &g12a_vdec_hevcf_div.hw,
+ [CLKID_VDEC_HEVCF] = &g12a_vdec_hevcf.hw,
+ [CLKID_TS_DIV] = &g12a_ts_div.hw,
+ [CLKID_TS] = &g12a_ts.hw,
+ [CLKID_GP1_PLL_DCO] = &sm1_gp1_pll_dco.hw,
+ [CLKID_GP1_PLL] = &sm1_gp1_pll.hw,
+ [CLKID_DSU_CLK_DYN0_SEL] = &sm1_dsu_clk_dyn0_sel.hw,
+ [CLKID_DSU_CLK_DYN0_DIV] = &sm1_dsu_clk_dyn0_div.hw,
+ [CLKID_DSU_CLK_DYN0] = &sm1_dsu_clk_dyn0.hw,
+ [CLKID_DSU_CLK_DYN1_SEL] = &sm1_dsu_clk_dyn1_sel.hw,
+ [CLKID_DSU_CLK_DYN1_DIV] = &sm1_dsu_clk_dyn1_div.hw,
+ [CLKID_DSU_CLK_DYN1] = &sm1_dsu_clk_dyn1.hw,
+ [CLKID_DSU_CLK_DYN] = &sm1_dsu_clk_dyn.hw,
+ [CLKID_DSU_CLK_FINAL] = &sm1_dsu_final_clk.hw,
+ [CLKID_DSU_CLK] = &sm1_dsu_clk.hw,
+ [CLKID_CPU1_CLK] = &sm1_cpu1_clk.hw,
+ [CLKID_CPU2_CLK] = &sm1_cpu2_clk.hw,
+ [CLKID_CPU3_CLK] = &sm1_cpu3_clk.hw,
+ [CLKID_SPICC0_SCLK_SEL] = &g12a_spicc0_sclk_sel.hw,
+ [CLKID_SPICC0_SCLK_DIV] = &g12a_spicc0_sclk_div.hw,
+ [CLKID_SPICC0_SCLK] = &g12a_spicc0_sclk.hw,
+ [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw,
+ [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw,
+ [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw,
+ [CLKID_NNA_AXI_CLK_SEL] = &sm1_nna_axi_clk_sel.hw,
+ [CLKID_NNA_AXI_CLK_DIV] = &sm1_nna_axi_clk_div.hw,
+ [CLKID_NNA_AXI_CLK] = &sm1_nna_axi_clk.hw,
+ [CLKID_NNA_CORE_CLK_SEL] = &sm1_nna_core_clk_sel.hw,
+ [CLKID_NNA_CORE_CLK_DIV] = &sm1_nna_core_clk_div.hw,
+ [CLKID_NNA_CORE_CLK] = &sm1_nna_core_clk.hw,
+ [CLKID_MIPI_DSI_PXCLK_SEL] = &g12a_mipi_dsi_pxclk_sel.hw,
+ [CLKID_MIPI_DSI_PXCLK_DIV] = &g12a_mipi_dsi_pxclk_div.hw,
+ [CLKID_MIPI_DSI_PXCLK] = &g12a_mipi_dsi_pxclk.hw,
};
static const struct reg_sequence g12a_init_regs[] = {
@@ -5239,8 +5229,7 @@ static const struct reg_sequence g12a_init_regs[] = {
#define DVFS_CON_ID "dvfs"
-static int meson_g12a_dvfs_setup_common(struct device *dev,
- struct clk_hw **hws)
+static int g12a_dvfs_setup_common(struct device *dev, struct clk_hw **hws)
{
struct clk *notifier_clk;
struct clk_hw *xtal;
@@ -5249,13 +5238,13 @@ static int meson_g12a_dvfs_setup_common(struct device *dev,
xtal = clk_hw_get_parent_by_index(hws[CLKID_CPU_CLK_DYN1_SEL], 0);
/* Setup clock notifier for cpu_clk_postmux0 */
- g12a_cpu_clk_postmux0_nb_data.xtal = xtal;
- notifier_clk = devm_clk_hw_get_clk(dev, &g12a_cpu_clk_postmux0.hw,
+ g12a_cpu_clk_dyn0_nb_data.xtal = xtal;
+ notifier_clk = devm_clk_hw_get_clk(dev, &g12a_cpu_clk_dyn0.hw,
DVFS_CON_ID);
ret = devm_clk_notifier_register(dev, notifier_clk,
- &g12a_cpu_clk_postmux0_nb_data.nb);
+ &g12a_cpu_clk_dyn0_nb_data.nb);
if (ret) {
- dev_err(dev, "failed to register the cpu_clk_postmux0 notifier\n");
+ dev_err(dev, "failed to register the cpu_clk_dyn0 notifier\n");
return ret;
}
@@ -5272,15 +5261,15 @@ static int meson_g12a_dvfs_setup_common(struct device *dev,
return 0;
}
-static int meson_g12b_dvfs_setup(struct platform_device *pdev)
+static int g12b_dvfs_setup(struct platform_device *pdev)
{
- struct clk_hw **hws = g12b_hw_onecell_data.hws;
+ struct clk_hw **hws = g12b_hw_clks;
struct device *dev = &pdev->dev;
struct clk *notifier_clk;
struct clk_hw *xtal;
int ret;
- ret = meson_g12a_dvfs_setup_common(dev, hws);
+ ret = g12a_dvfs_setup_common(dev, hws);
if (ret)
return ret;
@@ -5309,18 +5298,19 @@ static int meson_g12b_dvfs_setup(struct platform_device *pdev)
/* Add notifiers for the second CPU cluster */
/* Setup clock notifier for cpub_clk_postmux0 */
- g12b_cpub_clk_postmux0_nb_data.xtal = xtal;
- notifier_clk = devm_clk_hw_get_clk(dev, &g12b_cpub_clk_postmux0.hw,
+ g12b_cpub_clk_dyn0_nb_data.xtal = xtal;
+ notifier_clk = devm_clk_hw_get_clk(dev, &g12b_cpub_clk_dyn0.hw,
DVFS_CON_ID);
ret = devm_clk_notifier_register(dev, notifier_clk,
- &g12b_cpub_clk_postmux0_nb_data.nb);
+ &g12b_cpub_clk_dyn0_nb_data.nb);
if (ret) {
- dev_err(dev, "failed to register the cpub_clk_postmux0 notifier\n");
+ dev_err(dev, "failed to register the cpub_clk_dyn0 notifier\n");
return ret;
}
/* Setup clock notifier for cpub_clk_dyn mux */
- notifier_clk = devm_clk_hw_get_clk(dev, &g12b_cpub_clk_dyn.hw, "dvfs");
+ notifier_clk = devm_clk_hw_get_clk(dev, &g12b_cpub_clk_dyn.hw,
+ DVFS_CON_ID);
ret = devm_clk_notifier_register(dev, notifier_clk,
&g12a_cpu_clk_mux_nb);
if (ret) {
@@ -5349,14 +5339,14 @@ static int meson_g12b_dvfs_setup(struct platform_device *pdev)
return 0;
}
-static int meson_g12a_dvfs_setup(struct platform_device *pdev)
+static int g12a_dvfs_setup(struct platform_device *pdev)
{
- struct clk_hw **hws = g12a_hw_onecell_data.hws;
+ struct clk_hw **hws = g12a_hw_clks;
struct device *dev = &pdev->dev;
struct clk *notifier_clk;
int ret;
- ret = meson_g12a_dvfs_setup_common(dev, hws);
+ ret = g12a_dvfs_setup_common(dev, hws);
if (ret)
return ret;
@@ -5381,27 +5371,27 @@ static int meson_g12a_dvfs_setup(struct platform_device *pdev)
return 0;
}
-struct meson_g12a_data {
- const struct meson_eeclkc_data eeclkc_data;
+struct g12a_clkc_data {
+ const struct meson_clkc_data clkc_data;
int (*dvfs_setup)(struct platform_device *pdev);
};
-static int meson_g12a_probe(struct platform_device *pdev)
+static int g12a_clkc_probe(struct platform_device *pdev)
{
- const struct meson_eeclkc_data *eeclkc_data;
- const struct meson_g12a_data *g12a_data;
+ const struct meson_clkc_data *clkc_data;
+ const struct g12a_clkc_data *g12a_data;
int ret;
- eeclkc_data = of_device_get_match_data(&pdev->dev);
- if (!eeclkc_data)
+ clkc_data = of_device_get_match_data(&pdev->dev);
+ if (!clkc_data)
return -EINVAL;
- ret = meson_eeclkc_probe(pdev);
+ ret = meson_clkc_syscon_probe(pdev);
if (ret)
return ret;
- g12a_data = container_of(eeclkc_data, struct meson_g12a_data,
- eeclkc_data);
+ g12a_data = container_of(clkc_data, struct g12a_clkc_data,
+ clkc_data);
if (g12a_data->dvfs_setup)
return g12a_data->dvfs_setup(pdev);
@@ -5409,59 +5399,64 @@ static int meson_g12a_probe(struct platform_device *pdev)
return 0;
}
-static const struct meson_g12a_data g12a_clkc_data = {
- .eeclkc_data = {
- .regmap_clks = g12a_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
- .hw_onecell_data = &g12a_hw_onecell_data,
+static const struct g12a_clkc_data g12a_clkc_data = {
+ .clkc_data = {
+ .hw_clks = {
+ .hws = g12a_hw_clks,
+ .num = ARRAY_SIZE(g12a_hw_clks),
+ },
.init_regs = g12a_init_regs,
.init_count = ARRAY_SIZE(g12a_init_regs),
},
- .dvfs_setup = meson_g12a_dvfs_setup,
+ .dvfs_setup = g12a_dvfs_setup,
};
-static const struct meson_g12a_data g12b_clkc_data = {
- .eeclkc_data = {
- .regmap_clks = g12a_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
- .hw_onecell_data = &g12b_hw_onecell_data,
+static const struct g12a_clkc_data g12b_clkc_data = {
+ .clkc_data = {
+ .hw_clks = {
+ .hws = g12b_hw_clks,
+ .num = ARRAY_SIZE(g12b_hw_clks),
+ },
},
- .dvfs_setup = meson_g12b_dvfs_setup,
+ .dvfs_setup = g12b_dvfs_setup,
};
-static const struct meson_g12a_data sm1_clkc_data = {
- .eeclkc_data = {
- .regmap_clks = g12a_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps),
- .hw_onecell_data = &sm1_hw_onecell_data,
+static const struct g12a_clkc_data sm1_clkc_data = {
+ .clkc_data = {
+ .hw_clks = {
+ .hws = sm1_hw_clks,
+ .num = ARRAY_SIZE(sm1_hw_clks),
+ },
},
- .dvfs_setup = meson_g12a_dvfs_setup,
+ .dvfs_setup = g12a_dvfs_setup,
};
-static const struct of_device_id clkc_match_table[] = {
+static const struct of_device_id g12a_clkc_match_table[] = {
{
.compatible = "amlogic,g12a-clkc",
- .data = &g12a_clkc_data.eeclkc_data
+ .data = &g12a_clkc_data.clkc_data
},
{
.compatible = "amlogic,g12b-clkc",
- .data = &g12b_clkc_data.eeclkc_data
+ .data = &g12b_clkc_data.clkc_data
},
{
.compatible = "amlogic,sm1-clkc",
- .data = &sm1_clkc_data.eeclkc_data
+ .data = &sm1_clkc_data.clkc_data
},
{}
};
-MODULE_DEVICE_TABLE(of, clkc_match_table);
+MODULE_DEVICE_TABLE(of, g12a_clkc_match_table);
-static struct platform_driver g12a_driver = {
- .probe = meson_g12a_probe,
+static struct platform_driver g12a_clkc_driver = {
+ .probe = g12a_clkc_probe,
.driver = {
.name = "g12a-clkc",
- .of_match_table = clkc_match_table,
+ .of_match_table = g12a_clkc_match_table,
},
};
+module_platform_driver(g12a_clkc_driver);
-module_platform_driver(g12a_driver);
-MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Amlogic G12/SM1 Main Clock Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/g12a.h b/drivers/clk/meson/g12a.h
deleted file mode 100644
index a97613df38b3..000000000000
--- a/drivers/clk/meson/g12a.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
-/*
- * Copyright (c) 2016 Amlogic, Inc.
- * Author: Michael Turquette <mturquette@baylibre.com>
- *
- * Copyright (c) 2018 Amlogic, inc.
- * Author: Qiufang Dai <qiufang.dai@amlogic.com>
- * Author: Jian Hu <jian.hu@amlogic.com>
- *
- */
-#ifndef __G12A_H
-#define __G12A_H
-
-/*
- * Clock controller register offsets
- *
- * Register offsets from the data sheet must be multiplied by 4 before
- * adding them to the base address to get the right value.
- */
-#define HHI_MIPI_CNTL0 0x000
-#define HHI_MIPI_CNTL1 0x004
-#define HHI_MIPI_CNTL2 0x008
-#define HHI_MIPI_STS 0x00C
-#define HHI_GP0_PLL_CNTL0 0x040
-#define HHI_GP0_PLL_CNTL1 0x044
-#define HHI_GP0_PLL_CNTL2 0x048
-#define HHI_GP0_PLL_CNTL3 0x04C
-#define HHI_GP0_PLL_CNTL4 0x050
-#define HHI_GP0_PLL_CNTL5 0x054
-#define HHI_GP0_PLL_CNTL6 0x058
-#define HHI_GP0_PLL_STS 0x05C
-#define HHI_GP1_PLL_CNTL0 0x060
-#define HHI_GP1_PLL_CNTL1 0x064
-#define HHI_GP1_PLL_CNTL2 0x068
-#define HHI_GP1_PLL_CNTL3 0x06C
-#define HHI_GP1_PLL_CNTL4 0x070
-#define HHI_GP1_PLL_CNTL5 0x074
-#define HHI_GP1_PLL_CNTL6 0x078
-#define HHI_GP1_PLL_STS 0x07C
-#define HHI_PCIE_PLL_CNTL0 0x098
-#define HHI_PCIE_PLL_CNTL1 0x09C
-#define HHI_PCIE_PLL_CNTL2 0x0A0
-#define HHI_PCIE_PLL_CNTL3 0x0A4
-#define HHI_PCIE_PLL_CNTL4 0x0A8
-#define HHI_PCIE_PLL_CNTL5 0x0AC
-#define HHI_PCIE_PLL_STS 0x0B8
-#define HHI_HIFI_PLL_CNTL0 0x0D8
-#define HHI_HIFI_PLL_CNTL1 0x0DC
-#define HHI_HIFI_PLL_CNTL2 0x0E0
-#define HHI_HIFI_PLL_CNTL3 0x0E4
-#define HHI_HIFI_PLL_CNTL4 0x0E8
-#define HHI_HIFI_PLL_CNTL5 0x0EC
-#define HHI_HIFI_PLL_CNTL6 0x0F0
-#define HHI_VIID_CLK_DIV 0x128
-#define HHI_VIID_CLK_CNTL 0x12C
-#define HHI_GCLK_MPEG0 0x140
-#define HHI_GCLK_MPEG1 0x144
-#define HHI_GCLK_MPEG2 0x148
-#define HHI_GCLK_OTHER 0x150
-#define HHI_GCLK_OTHER2 0x154
-#define HHI_SYS_CPU_CLK_CNTL1 0x15c
-#define HHI_VID_CLK_DIV 0x164
-#define HHI_MPEG_CLK_CNTL 0x174
-#define HHI_AUD_CLK_CNTL 0x178
-#define HHI_VID_CLK_CNTL 0x17c
-#define HHI_TS_CLK_CNTL 0x190
-#define HHI_VID_CLK_CNTL2 0x194
-#define HHI_SYS_CPU_CLK_CNTL0 0x19c
-#define HHI_VID_PLL_CLK_DIV 0x1A0
-#define HHI_MALI_CLK_CNTL 0x1b0
-#define HHI_VPU_CLKC_CNTL 0x1b4
-#define HHI_VPU_CLK_CNTL 0x1bC
-#define HHI_NNA_CLK_CNTL 0x1C8
-#define HHI_HDMI_CLK_CNTL 0x1CC
-#define HHI_VDEC_CLK_CNTL 0x1E0
-#define HHI_VDEC2_CLK_CNTL 0x1E4
-#define HHI_VDEC3_CLK_CNTL 0x1E8
-#define HHI_VDEC4_CLK_CNTL 0x1EC
-#define HHI_HDCP22_CLK_CNTL 0x1F0
-#define HHI_VAPBCLK_CNTL 0x1F4
-#define HHI_SYS_CPUB_CLK_CNTL1 0x200
-#define HHI_SYS_CPUB_CLK_CNTL 0x208
-#define HHI_VPU_CLKB_CNTL 0x20C
-#define HHI_SYS_CPU_CLK_CNTL2 0x210
-#define HHI_SYS_CPU_CLK_CNTL3 0x214
-#define HHI_SYS_CPU_CLK_CNTL4 0x218
-#define HHI_SYS_CPU_CLK_CNTL5 0x21c
-#define HHI_SYS_CPU_CLK_CNTL6 0x220
-#define HHI_GEN_CLK_CNTL 0x228
-#define HHI_VDIN_MEAS_CLK_CNTL 0x250
-#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254
-#define HHI_NAND_CLK_CNTL 0x25C
-#define HHI_SD_EMMC_CLK_CNTL 0x264
-#define HHI_MPLL_CNTL0 0x278
-#define HHI_MPLL_CNTL1 0x27C
-#define HHI_MPLL_CNTL2 0x280
-#define HHI_MPLL_CNTL3 0x284
-#define HHI_MPLL_CNTL4 0x288
-#define HHI_MPLL_CNTL5 0x28c
-#define HHI_MPLL_CNTL6 0x290
-#define HHI_MPLL_CNTL7 0x294
-#define HHI_MPLL_CNTL8 0x298
-#define HHI_FIX_PLL_CNTL0 0x2A0
-#define HHI_FIX_PLL_CNTL1 0x2A4
-#define HHI_FIX_PLL_CNTL3 0x2AC
-#define HHI_SYS_PLL_CNTL0 0x2f4
-#define HHI_SYS_PLL_CNTL1 0x2f8
-#define HHI_SYS_PLL_CNTL2 0x2fc
-#define HHI_SYS_PLL_CNTL3 0x300
-#define HHI_SYS_PLL_CNTL4 0x304
-#define HHI_SYS_PLL_CNTL5 0x308
-#define HHI_SYS_PLL_CNTL6 0x30c
-#define HHI_HDMI_PLL_CNTL0 0x320
-#define HHI_HDMI_PLL_CNTL1 0x324
-#define HHI_HDMI_PLL_CNTL2 0x328
-#define HHI_HDMI_PLL_CNTL3 0x32c
-#define HHI_HDMI_PLL_CNTL4 0x330
-#define HHI_HDMI_PLL_CNTL5 0x334
-#define HHI_HDMI_PLL_CNTL6 0x338
-#define HHI_SPICC_CLK_CNTL 0x3dc
-#define HHI_SYS1_PLL_CNTL0 0x380
-#define HHI_SYS1_PLL_CNTL1 0x384
-#define HHI_SYS1_PLL_CNTL2 0x388
-#define HHI_SYS1_PLL_CNTL3 0x38c
-#define HHI_SYS1_PLL_CNTL4 0x390
-#define HHI_SYS1_PLL_CNTL5 0x394
-#define HHI_SYS1_PLL_CNTL6 0x398
-
-/*
- * CLKID index values
- *
- * These indices are entirely contrived and do not map onto the hardware.
- * It has now been decided to expose everything by default in the DT header:
- * include/dt-bindings/clock/g12a-clkc.h. Only the clocks ids we don't want
- * to expose, such as the internal muxes and dividers of composite clocks,
- * will remain defined here.
- */
-#define CLKID_MPEG_SEL 8
-#define CLKID_MPEG_DIV 9
-#define CLKID_SD_EMMC_A_CLK0_SEL 63
-#define CLKID_SD_EMMC_A_CLK0_DIV 64
-#define CLKID_SD_EMMC_B_CLK0_SEL 65
-#define CLKID_SD_EMMC_B_CLK0_DIV 66
-#define CLKID_SD_EMMC_C_CLK0_SEL 67
-#define CLKID_SD_EMMC_C_CLK0_DIV 68
-#define CLKID_MPLL0_DIV 69
-#define CLKID_MPLL1_DIV 70
-#define CLKID_MPLL2_DIV 71
-#define CLKID_MPLL3_DIV 72
-#define CLKID_MPLL_PREDIV 73
-#define CLKID_FCLK_DIV2_DIV 75
-#define CLKID_FCLK_DIV3_DIV 76
-#define CLKID_FCLK_DIV4_DIV 77
-#define CLKID_FCLK_DIV5_DIV 78
-#define CLKID_FCLK_DIV7_DIV 79
-#define CLKID_FCLK_DIV2P5_DIV 100
-#define CLKID_FIXED_PLL_DCO 101
-#define CLKID_SYS_PLL_DCO 102
-#define CLKID_GP0_PLL_DCO 103
-#define CLKID_HIFI_PLL_DCO 104
-#define CLKID_VPU_0_DIV 111
-#define CLKID_VPU_1_DIV 114
-#define CLKID_VAPB_0_DIV 118
-#define CLKID_VAPB_1_DIV 121
-#define CLKID_HDMI_PLL_DCO 125
-#define CLKID_HDMI_PLL_OD 126
-#define CLKID_HDMI_PLL_OD2 127
-#define CLKID_VID_PLL_SEL 130
-#define CLKID_VID_PLL_DIV 131
-#define CLKID_VCLK_SEL 132
-#define CLKID_VCLK2_SEL 133
-#define CLKID_VCLK_INPUT 134
-#define CLKID_VCLK2_INPUT 135
-#define CLKID_VCLK_DIV 136
-#define CLKID_VCLK2_DIV 137
-#define CLKID_VCLK_DIV2_EN 140
-#define CLKID_VCLK_DIV4_EN 141
-#define CLKID_VCLK_DIV6_EN 142
-#define CLKID_VCLK_DIV12_EN 143
-#define CLKID_VCLK2_DIV2_EN 144
-#define CLKID_VCLK2_DIV4_EN 145
-#define CLKID_VCLK2_DIV6_EN 146
-#define CLKID_VCLK2_DIV12_EN 147
-#define CLKID_CTS_ENCI_SEL 158
-#define CLKID_CTS_ENCP_SEL 159
-#define CLKID_CTS_VDAC_SEL 160
-#define CLKID_HDMI_TX_SEL 161
-#define CLKID_HDMI_SEL 166
-#define CLKID_HDMI_DIV 167
-#define CLKID_MALI_0_DIV 170
-#define CLKID_MALI_1_DIV 173
-#define CLKID_MPLL_50M_DIV 176
-#define CLKID_SYS_PLL_DIV16_EN 178
-#define CLKID_SYS_PLL_DIV16 179
-#define CLKID_CPU_CLK_DYN0_SEL 180
-#define CLKID_CPU_CLK_DYN0_DIV 181
-#define CLKID_CPU_CLK_DYN0 182
-#define CLKID_CPU_CLK_DYN1_SEL 183
-#define CLKID_CPU_CLK_DYN1_DIV 184
-#define CLKID_CPU_CLK_DYN1 185
-#define CLKID_CPU_CLK_DYN 186
-#define CLKID_CPU_CLK_DIV16_EN 188
-#define CLKID_CPU_CLK_DIV16 189
-#define CLKID_CPU_CLK_APB_DIV 190
-#define CLKID_CPU_CLK_APB 191
-#define CLKID_CPU_CLK_ATB_DIV 192
-#define CLKID_CPU_CLK_ATB 193
-#define CLKID_CPU_CLK_AXI_DIV 194
-#define CLKID_CPU_CLK_AXI 195
-#define CLKID_CPU_CLK_TRACE_DIV 196
-#define CLKID_CPU_CLK_TRACE 197
-#define CLKID_PCIE_PLL_DCO 198
-#define CLKID_PCIE_PLL_DCO_DIV2 199
-#define CLKID_PCIE_PLL_OD 200
-#define CLKID_VDEC_1_SEL 202
-#define CLKID_VDEC_1_DIV 203
-#define CLKID_VDEC_HEVC_SEL 205
-#define CLKID_VDEC_HEVC_DIV 206
-#define CLKID_VDEC_HEVCF_SEL 208
-#define CLKID_VDEC_HEVCF_DIV 209
-#define CLKID_TS_DIV 211
-#define CLKID_SYS1_PLL_DCO 213
-#define CLKID_SYS1_PLL 214
-#define CLKID_SYS1_PLL_DIV16_EN 215
-#define CLKID_SYS1_PLL_DIV16 216
-#define CLKID_CPUB_CLK_DYN0_SEL 217
-#define CLKID_CPUB_CLK_DYN0_DIV 218
-#define CLKID_CPUB_CLK_DYN0 219
-#define CLKID_CPUB_CLK_DYN1_SEL 220
-#define CLKID_CPUB_CLK_DYN1_DIV 221
-#define CLKID_CPUB_CLK_DYN1 222
-#define CLKID_CPUB_CLK_DYN 223
-#define CLKID_CPUB_CLK_DIV16_EN 225
-#define CLKID_CPUB_CLK_DIV16 226
-#define CLKID_CPUB_CLK_DIV2 227
-#define CLKID_CPUB_CLK_DIV3 228
-#define CLKID_CPUB_CLK_DIV4 229
-#define CLKID_CPUB_CLK_DIV5 230
-#define CLKID_CPUB_CLK_DIV6 231
-#define CLKID_CPUB_CLK_DIV7 232
-#define CLKID_CPUB_CLK_DIV8 233
-#define CLKID_CPUB_CLK_APB_SEL 234
-#define CLKID_CPUB_CLK_APB 235
-#define CLKID_CPUB_CLK_ATB_SEL 236
-#define CLKID_CPUB_CLK_ATB 237
-#define CLKID_CPUB_CLK_AXI_SEL 238
-#define CLKID_CPUB_CLK_AXI 239
-#define CLKID_CPUB_CLK_TRACE_SEL 240
-#define CLKID_CPUB_CLK_TRACE 241
-#define CLKID_GP1_PLL_DCO 242
-#define CLKID_DSU_CLK_DYN0_SEL 244
-#define CLKID_DSU_CLK_DYN0_DIV 245
-#define CLKID_DSU_CLK_DYN0 246
-#define CLKID_DSU_CLK_DYN1_SEL 247
-#define CLKID_DSU_CLK_DYN1_DIV 248
-#define CLKID_DSU_CLK_DYN1 249
-#define CLKID_DSU_CLK_DYN 250
-#define CLKID_DSU_CLK_FINAL 251
-#define CLKID_SPICC0_SCLK_SEL 256
-#define CLKID_SPICC0_SCLK_DIV 257
-#define CLKID_SPICC1_SCLK_SEL 259
-#define CLKID_SPICC1_SCLK_DIV 260
-#define CLKID_NNA_AXI_CLK_SEL 262
-#define CLKID_NNA_AXI_CLK_DIV 263
-#define CLKID_NNA_CORE_CLK_SEL 265
-#define CLKID_NNA_CORE_CLK_DIV 266
-#define CLKID_MIPI_DSI_PXCLK_DIV 268
-
-#define NR_CLKS 271
-
-/* include the CLKIDs that have been made part of the DT binding */
-#include <dt-bindings/clock/g12a-clkc.h>
-
-#endif /* __G12A_H */
diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c
index fce95cf89836..c7dfb3a06cb5 100644
--- a/drivers/clk/meson/gxbb-aoclk.c
+++ b/drivers/clk/meson/gxbb-aoclk.c
@@ -7,11 +7,13 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include "meson-aoclk.h"
-#include "gxbb-aoclk.h"
#include "clk-regmap.h"
#include "clk-dualdiv.h"
+#include <dt-bindings/clock/gxbb-aoclkc.h>
+#include <dt-bindings/reset/gxbb-aoclkc.h>
+
/* AO Configuration Clock registers offsets */
#define AO_RTI_PWR_CNTL_REG1 0x0c
#define AO_RTI_PWR_CNTL_REG0 0x10
@@ -21,31 +23,20 @@
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
-#define GXBB_AO_GATE(_name, _bit) \
-static struct clk_regmap _name##_ao = { \
- .data = &(struct clk_regmap_gate_data) { \
- .offset = AO_RTI_GEN_CNTL_REG0, \
- .bit_idx = (_bit), \
- }, \
- .hw.init = &(struct clk_init_data) { \
- .name = #_name "_ao", \
- .ops = &clk_regmap_gate_ops, \
- .parent_data = &(const struct clk_parent_data) { \
- .fw_name = "mpeg-clk", \
- }, \
- .num_parents = 1, \
- .flags = CLK_IGNORE_UNUSED, \
- }, \
-}
+static const struct clk_parent_data gxbb_ao_pclk_parents = { .fw_name = "mpeg-clk" };
+
+#define GXBB_AO_PCLK(_name, _bit, _flags) \
+ MESON_PCLK(gxbb_ao_##_name, AO_RTI_GEN_CNTL_REG0, _bit, \
+ &gxbb_ao_pclk_parents, _flags)
-GXBB_AO_GATE(remote, 0);
-GXBB_AO_GATE(i2c_master, 1);
-GXBB_AO_GATE(i2c_slave, 2);
-GXBB_AO_GATE(uart1, 3);
-GXBB_AO_GATE(uart2, 5);
-GXBB_AO_GATE(ir_blaster, 6);
+static GXBB_AO_PCLK(remote, 0, CLK_IGNORE_UNUSED);
+static GXBB_AO_PCLK(i2c_master, 1, CLK_IGNORE_UNUSED);
+static GXBB_AO_PCLK(i2c_slave, 2, CLK_IGNORE_UNUSED);
+static GXBB_AO_PCLK(uart1, 3, CLK_IGNORE_UNUSED);
+static GXBB_AO_PCLK(uart2, 5, CLK_IGNORE_UNUSED);
+static GXBB_AO_PCLK(ir_blaster, 6, CLK_IGNORE_UNUSED);
-static struct clk_regmap ao_cts_oscin = {
+static struct clk_regmap gxbb_ao_cts_oscin = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTI_PWR_CNTL_REG0,
.bit_idx = 6,
@@ -60,7 +51,7 @@ static struct clk_regmap ao_cts_oscin = {
},
};
-static struct clk_regmap ao_32k_pre = {
+static struct clk_regmap gxbb_ao_32k_pre = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 31,
@@ -68,7 +59,7 @@ static struct clk_regmap ao_32k_pre = {
.hw.init = &(struct clk_init_data){
.name = "ao_32k_pre",
.ops = &clk_regmap_gate_ops,
- .parent_hws = (const struct clk_hw *[]) { &ao_cts_oscin.hw },
+ .parent_hws = (const struct clk_hw *[]) { &gxbb_ao_cts_oscin.hw },
.num_parents = 1,
},
};
@@ -83,7 +74,7 @@ static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = {
}, {}
};
-static struct clk_regmap ao_32k_div = {
+static struct clk_regmap gxbb_ao_32k_div = {
.data = &(struct meson_clk_dualdiv_data){
.n1 = {
.reg_off = AO_RTC_ALT_CLK_CNTL0,
@@ -115,12 +106,12 @@ static struct clk_regmap ao_32k_div = {
.hw.init = &(struct clk_init_data){
.name = "ao_32k_div",
.ops = &meson_clk_dualdiv_ops,
- .parent_hws = (const struct clk_hw *[]) { &ao_32k_pre.hw },
+ .parent_hws = (const struct clk_hw *[]) { &gxbb_ao_32k_pre.hw },
.num_parents = 1,
},
};
-static struct clk_regmap ao_32k_sel = {
+static struct clk_regmap gxbb_ao_32k_sel = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTC_ALT_CLK_CNTL1,
.mask = 0x1,
@@ -131,15 +122,15 @@ static struct clk_regmap ao_32k_sel = {
.name = "ao_32k_sel",
.ops = &clk_regmap_mux_ops,
.parent_hws = (const struct clk_hw *[]) {
- &ao_32k_div.hw,
- &ao_32k_pre.hw
+ &gxbb_ao_32k_div.hw,
+ &gxbb_ao_32k_pre.hw
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap ao_32k = {
+static struct clk_regmap gxbb_ao_32k = {
.data = &(struct clk_regmap_gate_data){
.offset = AO_RTC_ALT_CLK_CNTL0,
.bit_idx = 30,
@@ -147,13 +138,13 @@ static struct clk_regmap ao_32k = {
.hw.init = &(struct clk_init_data){
.name = "ao_32k",
.ops = &clk_regmap_gate_ops,
- .parent_hws = (const struct clk_hw *[]) { &ao_32k_sel.hw },
+ .parent_hws = (const struct clk_hw *[]) { &gxbb_ao_32k_sel.hw },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap ao_cts_rtc_oscin = {
+static struct clk_regmap gxbb_ao_cts_rtc_oscin = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x7,
@@ -168,14 +159,14 @@ static struct clk_regmap ao_cts_rtc_oscin = {
{ .fw_name = "ext-32k-0", },
{ .fw_name = "ext-32k-1", },
{ .fw_name = "ext-32k-2", },
- { .hw = &ao_32k.hw },
+ { .hw = &gxbb_ao_32k.hw },
},
.num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap ao_clk81 = {
+static struct clk_regmap gxbb_ao_clk81 = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_RTI_PWR_CNTL_REG0,
.mask = 0x1,
@@ -187,14 +178,14 @@ static struct clk_regmap ao_clk81 = {
.ops = &clk_regmap_mux_ro_ops,
.parent_data = (const struct clk_parent_data []) {
{ .fw_name = "mpeg-clk", },
- { .hw = &ao_cts_rtc_oscin.hw },
+ { .hw = &gxbb_ao_cts_rtc_oscin.hw },
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap ao_cts_cec = {
+static struct clk_regmap gxbb_ao_cts_cec = {
.data = &(struct clk_regmap_mux_data) {
.offset = AO_CRT_CLK_CNTL1,
.mask = 0x1,
@@ -219,14 +210,14 @@ static struct clk_regmap ao_cts_cec = {
*/
.parent_data = (const struct clk_parent_data []) {
{ .name = "fixme", .index = -1, },
- { .hw = &ao_cts_rtc_oscin.hw },
+ { .hw = &gxbb_ao_cts_rtc_oscin.hw },
},
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
-static const unsigned int gxbb_aoclk_reset[] = {
+static const unsigned int gxbb_ao_reset[] = {
[RESET_AO_REMOTE] = 16,
[RESET_AO_I2C_MASTER] = 18,
[RESET_AO_I2C_SLAVE] = 19,
@@ -235,67 +226,53 @@ static const unsigned int gxbb_aoclk_reset[] = {
[RESET_AO_IR_BLASTER] = 23,
};
-static struct clk_regmap *gxbb_aoclk[] = {
- &remote_ao,
- &i2c_master_ao,
- &i2c_slave_ao,
- &uart1_ao,
- &uart2_ao,
- &ir_blaster_ao,
- &ao_cts_oscin,
- &ao_32k_pre,
- &ao_32k_div,
- &ao_32k_sel,
- &ao_32k,
- &ao_cts_rtc_oscin,
- &ao_clk81,
- &ao_cts_cec,
-};
-
-static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = {
- .hws = {
- [CLKID_AO_REMOTE] = &remote_ao.hw,
- [CLKID_AO_I2C_MASTER] = &i2c_master_ao.hw,
- [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao.hw,
- [CLKID_AO_UART1] = &uart1_ao.hw,
- [CLKID_AO_UART2] = &uart2_ao.hw,
- [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw,
- [CLKID_AO_CEC_32K] = &ao_cts_cec.hw,
- [CLKID_AO_CTS_OSCIN] = &ao_cts_oscin.hw,
- [CLKID_AO_32K_PRE] = &ao_32k_pre.hw,
- [CLKID_AO_32K_DIV] = &ao_32k_div.hw,
- [CLKID_AO_32K_SEL] = &ao_32k_sel.hw,
- [CLKID_AO_32K] = &ao_32k.hw,
- [CLKID_AO_CTS_RTC_OSCIN] = &ao_cts_rtc_oscin.hw,
- [CLKID_AO_CLK81] = &ao_clk81.hw,
- },
- .num = NR_CLKS,
+static struct clk_hw *gxbb_ao_hw_clks[] = {
+ [CLKID_AO_REMOTE] = &gxbb_ao_remote.hw,
+ [CLKID_AO_I2C_MASTER] = &gxbb_ao_i2c_master.hw,
+ [CLKID_AO_I2C_SLAVE] = &gxbb_ao_i2c_slave.hw,
+ [CLKID_AO_UART1] = &gxbb_ao_uart1.hw,
+ [CLKID_AO_UART2] = &gxbb_ao_uart2.hw,
+ [CLKID_AO_IR_BLASTER] = &gxbb_ao_ir_blaster.hw,
+ [CLKID_AO_CEC_32K] = &gxbb_ao_cts_cec.hw,
+ [CLKID_AO_CTS_OSCIN] = &gxbb_ao_cts_oscin.hw,
+ [CLKID_AO_32K_PRE] = &gxbb_ao_32k_pre.hw,
+ [CLKID_AO_32K_DIV] = &gxbb_ao_32k_div.hw,
+ [CLKID_AO_32K_SEL] = &gxbb_ao_32k_sel.hw,
+ [CLKID_AO_32K] = &gxbb_ao_32k.hw,
+ [CLKID_AO_CTS_RTC_OSCIN] = &gxbb_ao_cts_rtc_oscin.hw,
+ [CLKID_AO_CLK81] = &gxbb_ao_clk81.hw,
};
-static const struct meson_aoclk_data gxbb_aoclkc_data = {
+static const struct meson_aoclk_data gxbb_ao_clkc_data = {
.reset_reg = AO_RTI_GEN_CNTL_REG0,
- .num_reset = ARRAY_SIZE(gxbb_aoclk_reset),
- .reset = gxbb_aoclk_reset,
- .num_clks = ARRAY_SIZE(gxbb_aoclk),
- .clks = gxbb_aoclk,
- .hw_data = &gxbb_aoclk_onecell_data,
+ .num_reset = ARRAY_SIZE(gxbb_ao_reset),
+ .reset = gxbb_ao_reset,
+ .clkc_data = {
+ .hw_clks = {
+ .hws = gxbb_ao_hw_clks,
+ .num = ARRAY_SIZE(gxbb_ao_hw_clks),
+ },
+ },
};
-static const struct of_device_id gxbb_aoclkc_match_table[] = {
+static const struct of_device_id gxbb_ao_clkc_match_table[] = {
{
.compatible = "amlogic,meson-gx-aoclkc",
- .data = &gxbb_aoclkc_data,
+ .data = &gxbb_ao_clkc_data.clkc_data,
},
{ }
};
-MODULE_DEVICE_TABLE(of, gxbb_aoclkc_match_table);
+MODULE_DEVICE_TABLE(of, gxbb_ao_clkc_match_table);
-static struct platform_driver gxbb_aoclkc_driver = {
+static struct platform_driver gxbb_ao_clkc_driver = {
.probe = meson_aoclkc_probe,
.driver = {
.name = "gxbb-aoclkc",
- .of_match_table = gxbb_aoclkc_match_table,
+ .of_match_table = gxbb_ao_clkc_match_table,
},
};
-module_platform_driver(gxbb_aoclkc_driver);
-MODULE_LICENSE("GPL v2");
+module_platform_driver(gxbb_ao_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic GXBB Always-ON Clock Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h
deleted file mode 100644
index 1db16f9b37d4..000000000000
--- a/drivers/clk/meson/gxbb-aoclk.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Copyright (c) 2017 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- */
-
-#ifndef __GXBB_AOCLKC_H
-#define __GXBB_AOCLKC_H
-
-#define NR_CLKS 14
-
-#include <dt-bindings/clock/gxbb-aoclkc.h>
-#include <dt-bindings/reset/gxbb-aoclkc.h>
-
-#endif /* __GXBB_AOCLKC_H */
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 608e0e8ca49a..5a229c4ffae1 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -6,82 +6,115 @@
#include <linux/clk-provider.h>
#include <linux/init.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/module.h>
-#include "gxbb.h"
#include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-mpll.h"
-#include "meson-eeclk.h"
+#include "meson-clkc-utils.h"
#include "vid-pll-div.h"
-static DEFINE_SPINLOCK(meson_clk_lock);
-
-static const struct pll_params_table gxbb_gp0_pll_params_table[] = {
- PLL_PARAMS(32, 1),
- PLL_PARAMS(33, 1),
- PLL_PARAMS(34, 1),
- PLL_PARAMS(35, 1),
- PLL_PARAMS(36, 1),
- PLL_PARAMS(37, 1),
- PLL_PARAMS(38, 1),
- PLL_PARAMS(39, 1),
- PLL_PARAMS(40, 1),
- PLL_PARAMS(41, 1),
- PLL_PARAMS(42, 1),
- PLL_PARAMS(43, 1),
- PLL_PARAMS(44, 1),
- PLL_PARAMS(45, 1),
- PLL_PARAMS(46, 1),
- PLL_PARAMS(47, 1),
- PLL_PARAMS(48, 1),
- PLL_PARAMS(49, 1),
- PLL_PARAMS(50, 1),
- PLL_PARAMS(51, 1),
- PLL_PARAMS(52, 1),
- PLL_PARAMS(53, 1),
- PLL_PARAMS(54, 1),
- PLL_PARAMS(55, 1),
- PLL_PARAMS(56, 1),
- PLL_PARAMS(57, 1),
- PLL_PARAMS(58, 1),
- PLL_PARAMS(59, 1),
- PLL_PARAMS(60, 1),
- PLL_PARAMS(61, 1),
- PLL_PARAMS(62, 1),
- { /* sentinel */ },
-};
-
-static const struct pll_params_table gxl_gp0_pll_params_table[] = {
- PLL_PARAMS(42, 1),
- PLL_PARAMS(43, 1),
- PLL_PARAMS(44, 1),
- PLL_PARAMS(45, 1),
- PLL_PARAMS(46, 1),
- PLL_PARAMS(47, 1),
- PLL_PARAMS(48, 1),
- PLL_PARAMS(49, 1),
- PLL_PARAMS(50, 1),
- PLL_PARAMS(51, 1),
- PLL_PARAMS(52, 1),
- PLL_PARAMS(53, 1),
- PLL_PARAMS(54, 1),
- PLL_PARAMS(55, 1),
- PLL_PARAMS(56, 1),
- PLL_PARAMS(57, 1),
- PLL_PARAMS(58, 1),
- PLL_PARAMS(59, 1),
- PLL_PARAMS(60, 1),
- PLL_PARAMS(61, 1),
- PLL_PARAMS(62, 1),
- PLL_PARAMS(63, 1),
- PLL_PARAMS(64, 1),
- PLL_PARAMS(65, 1),
- PLL_PARAMS(66, 1),
- { /* sentinel */ },
-};
+#include <dt-bindings/clock/gxbb-clkc.h>
+
+#define SCR 0x2c
+#define TIMEOUT_VALUE 0x3c
+
+#define HHI_GP0_PLL_CNTL 0x40
+#define HHI_GP0_PLL_CNTL2 0x44
+#define HHI_GP0_PLL_CNTL3 0x48
+#define HHI_GP0_PLL_CNTL4 0x4c
+#define HHI_GP0_PLL_CNTL5 0x50
+#define HHI_GP0_PLL_CNTL1 0x58
+
+#define HHI_XTAL_DIVN_CNTL 0xbc
+#define HHI_TIMER90K 0xec
+
+#define HHI_MEM_PD_REG0 0x100
+#define HHI_MEM_PD_REG1 0x104
+#define HHI_VPU_MEM_PD_REG1 0x108
+#define HHI_VIID_CLK_DIV 0x128
+#define HHI_VIID_CLK_CNTL 0x12c
+
+#define HHI_GCLK_MPEG0 0x140
+#define HHI_GCLK_MPEG1 0x144
+#define HHI_GCLK_MPEG2 0x148
+#define HHI_GCLK_OTHER 0x150
+#define HHI_GCLK_AO 0x154
+#define HHI_SYS_OSCIN_CNTL 0x158
+#define HHI_SYS_CPU_CLK_CNTL1 0x15c
+#define HHI_SYS_CPU_RESET_CNTL 0x160
+#define HHI_VID_CLK_DIV 0x164
+
+#define HHI_MPEG_CLK_CNTL 0x174
+#define HHI_AUD_CLK_CNTL 0x178
+#define HHI_VID_CLK_CNTL 0x17c
+#define HHI_AUD_CLK_CNTL2 0x190
+#define HHI_VID_CLK_CNTL2 0x194
+#define HHI_SYS_CPU_CLK_CNTL0 0x19c
+#define HHI_VID_PLL_CLK_DIV 0x1a0
+#define HHI_AUD_CLK_CNTL3 0x1a4
+#define HHI_MALI_CLK_CNTL 0x1b0
+#define HHI_VPU_CLK_CNTL 0x1bc
+
+#define HHI_HDMI_CLK_CNTL 0x1cc
+#define HHI_VDEC_CLK_CNTL 0x1e0
+#define HHI_VDEC2_CLK_CNTL 0x1e4
+#define HHI_VDEC3_CLK_CNTL 0x1e8
+#define HHI_VDEC4_CLK_CNTL 0x1ec
+#define HHI_HDCP22_CLK_CNTL 0x1f0
+#define HHI_VAPBCLK_CNTL 0x1f4
+
+#define HHI_VPU_CLKB_CNTL 0x20c
+#define HHI_USB_CLK_CNTL 0x220
+#define HHI_32K_CLK_CNTL 0x224
+#define HHI_GEN_CLK_CNTL 0x228
+
+#define HHI_PCM_CLK_CNTL 0x258
+#define HHI_NAND_CLK_CNTL 0x25c
+#define HHI_SD_EMMC_CLK_CNTL 0x264
+
+#define HHI_MPLL_CNTL 0x280
+#define HHI_MPLL_CNTL2 0x284
+#define HHI_MPLL_CNTL3 0x288
+#define HHI_MPLL_CNTL4 0x28c
+#define HHI_MPLL_CNTL5 0x290
+#define HHI_MPLL_CNTL6 0x294
+#define HHI_MPLL_CNTL7 0x298
+#define HHI_MPLL_CNTL8 0x29c
+#define HHI_MPLL_CNTL9 0x2a0
+#define HHI_MPLL_CNTL10 0x2a4
+
+#define HHI_MPLL3_CNTL0 0x2e0
+#define HHI_MPLL3_CNTL1 0x2e4
+#define HHI_VDAC_CNTL0 0x2f4
+#define HHI_VDAC_CNTL1 0x2f8
+
+#define HHI_SYS_PLL_CNTL 0x300
+#define HHI_SYS_PLL_CNTL2 0x304
+#define HHI_SYS_PLL_CNTL3 0x308
+#define HHI_SYS_PLL_CNTL4 0x30c
+#define HHI_SYS_PLL_CNTL5 0x310
+#define HHI_DPLL_TOP_I 0x318
+#define HHI_DPLL_TOP2_I 0x31c
+#define HHI_HDMI_PLL_CNTL 0x320
+#define HHI_HDMI_PLL_CNTL2 0x324
+#define HHI_HDMI_PLL_CNTL3 0x328
+#define HHI_HDMI_PLL_CNTL4 0x32c
+#define HHI_HDMI_PLL_CNTL5 0x330
+#define HHI_HDMI_PLL_CNTL6 0x334
+#define HHI_HDMI_PLL_CNTL_I 0x338
+#define HHI_HDMI_PLL_CNTL7 0x33c
+
+#define HHI_HDMI_PHY_CNTL0 0x3a0
+#define HHI_HDMI_PHY_CNTL1 0x3a4
+#define HHI_HDMI_PHY_CNTL2 0x3a8
+#define HHI_HDMI_PHY_CNTL3 0x3ac
+
+#define HHI_VID_LOCK_CLK_CNTL 0x3c8
+#define HHI_BT656_CLK_CNTL 0x3d4
+#define HHI_SAR_CLK_CNTL 0x3d8
static struct clk_regmap gxbb_fixed_pll_dco = {
.data = &(struct meson_clk_pll_data){
@@ -426,7 +459,42 @@ static struct clk_regmap gxbb_sys_pll = {
},
};
-static const struct reg_sequence gxbb_gp0_init_regs[] = {
+static const struct pll_params_table gxbb_gp0_pll_params_table[] = {
+ PLL_PARAMS(32, 1),
+ PLL_PARAMS(33, 1),
+ PLL_PARAMS(34, 1),
+ PLL_PARAMS(35, 1),
+ PLL_PARAMS(36, 1),
+ PLL_PARAMS(37, 1),
+ PLL_PARAMS(38, 1),
+ PLL_PARAMS(39, 1),
+ PLL_PARAMS(40, 1),
+ PLL_PARAMS(41, 1),
+ PLL_PARAMS(42, 1),
+ PLL_PARAMS(43, 1),
+ PLL_PARAMS(44, 1),
+ PLL_PARAMS(45, 1),
+ PLL_PARAMS(46, 1),
+ PLL_PARAMS(47, 1),
+ PLL_PARAMS(48, 1),
+ PLL_PARAMS(49, 1),
+ PLL_PARAMS(50, 1),
+ PLL_PARAMS(51, 1),
+ PLL_PARAMS(52, 1),
+ PLL_PARAMS(53, 1),
+ PLL_PARAMS(54, 1),
+ PLL_PARAMS(55, 1),
+ PLL_PARAMS(56, 1),
+ PLL_PARAMS(57, 1),
+ PLL_PARAMS(58, 1),
+ PLL_PARAMS(59, 1),
+ PLL_PARAMS(60, 1),
+ PLL_PARAMS(61, 1),
+ PLL_PARAMS(62, 1),
+ { /* sentinel */ },
+};
+
+static const struct reg_sequence gxbb_gp0_pll_init_regs[] = {
{ .reg = HHI_GP0_PLL_CNTL2, .def = 0x69c80000 },
{ .reg = HHI_GP0_PLL_CNTL3, .def = 0x0a5590c4 },
{ .reg = HHI_GP0_PLL_CNTL4, .def = 0x0000500d },
@@ -460,8 +528,8 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
.width = 1,
},
.table = gxbb_gp0_pll_params_table,
- .init_regs = gxbb_gp0_init_regs,
- .init_count = ARRAY_SIZE(gxbb_gp0_init_regs),
+ .init_regs = gxbb_gp0_pll_init_regs,
+ .init_count = ARRAY_SIZE(gxbb_gp0_pll_init_regs),
},
.hw.init = &(struct clk_init_data){
.name = "gp0_pll_dco",
@@ -473,7 +541,36 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
},
};
-static const struct reg_sequence gxl_gp0_init_regs[] = {
+static const struct pll_params_table gxl_gp0_pll_params_table[] = {
+ PLL_PARAMS(42, 1),
+ PLL_PARAMS(43, 1),
+ PLL_PARAMS(44, 1),
+ PLL_PARAMS(45, 1),
+ PLL_PARAMS(46, 1),
+ PLL_PARAMS(47, 1),
+ PLL_PARAMS(48, 1),
+ PLL_PARAMS(49, 1),
+ PLL_PARAMS(50, 1),
+ PLL_PARAMS(51, 1),
+ PLL_PARAMS(52, 1),
+ PLL_PARAMS(53, 1),
+ PLL_PARAMS(54, 1),
+ PLL_PARAMS(55, 1),
+ PLL_PARAMS(56, 1),
+ PLL_PARAMS(57, 1),
+ PLL_PARAMS(58, 1),
+ PLL_PARAMS(59, 1),
+ PLL_PARAMS(60, 1),
+ PLL_PARAMS(61, 1),
+ PLL_PARAMS(62, 1),
+ PLL_PARAMS(63, 1),
+ PLL_PARAMS(64, 1),
+ PLL_PARAMS(65, 1),
+ PLL_PARAMS(66, 1),
+ { /* sentinel */ },
+};
+
+static const struct reg_sequence gxl_gp0_pll_init_regs[] = {
{ .reg = HHI_GP0_PLL_CNTL1, .def = 0xc084b000 },
{ .reg = HHI_GP0_PLL_CNTL2, .def = 0xb75020be },
{ .reg = HHI_GP0_PLL_CNTL3, .def = 0x0a59a288 },
@@ -514,8 +611,8 @@ static struct clk_regmap gxl_gp0_pll_dco = {
.width = 1,
},
.table = gxl_gp0_pll_params_table,
- .init_regs = gxl_gp0_init_regs,
- .init_count = ARRAY_SIZE(gxl_gp0_init_regs),
+ .init_regs = gxl_gp0_pll_init_regs,
+ .init_count = ARRAY_SIZE(gxl_gp0_pll_init_regs),
},
.hw.init = &(struct clk_init_data){
.name = "gp0_pll_dco",
@@ -729,7 +826,6 @@ static struct clk_regmap gxbb_mpll0_div = {
.shift = 16,
.width = 9,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll0_div",
@@ -758,7 +854,6 @@ static struct clk_regmap gxl_mpll0_div = {
.shift = 16,
.width = 9,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll0_div",
@@ -810,7 +905,6 @@ static struct clk_regmap gxbb_mpll1_div = {
.shift = 16,
.width = 9,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll1_div",
@@ -853,7 +947,6 @@ static struct clk_regmap gxbb_mpll2_div = {
.shift = 16,
.width = 9,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll2_div",
@@ -879,8 +972,9 @@ static struct clk_regmap gxbb_mpll2 = {
},
};
-static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
-static const struct clk_parent_data clk81_parent_data[] = {
+/* clk81 is often referred as "mpeg_clk" */
+static u32 clk81_parents_val_table[] = { 0, 2, 3, 4, 5, 6, 7 };
+static const struct clk_parent_data clk81_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &gxbb_fclk_div7.hw },
{ .hw = &gxbb_mpll1.hw },
@@ -890,37 +984,37 @@ static const struct clk_parent_data clk81_parent_data[] = {
{ .hw = &gxbb_fclk_div5.hw },
};
-static struct clk_regmap gxbb_mpeg_clk_sel = {
+static struct clk_regmap gxbb_clk81_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MPEG_CLK_CNTL,
.mask = 0x7,
.shift = 12,
- .table = mux_table_clk81,
+ .table = clk81_parents_val_table,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_sel",
+ .name = "clk81_sel",
.ops = &clk_regmap_mux_ro_ops,
/*
* bits 14:12 selects from 8 possible parents:
* xtal, 1'b0 (wtf), fclk_div7, mpll_clkout1, mpll_clkout2,
* fclk_div4, fclk_div3, fclk_div5
*/
- .parent_data = clk81_parent_data,
- .num_parents = ARRAY_SIZE(clk81_parent_data),
+ .parent_data = clk81_parents,
+ .num_parents = ARRAY_SIZE(clk81_parents),
},
};
-static struct clk_regmap gxbb_mpeg_clk_div = {
+static struct clk_regmap gxbb_clk81_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_MPEG_CLK_CNTL,
.shift = 0,
.width = 7,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_div",
+ .name = "clk81_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &gxbb_mpeg_clk_sel.hw
+ &gxbb_clk81_sel.hw
},
.num_parents = 1,
},
@@ -936,7 +1030,7 @@ static struct clk_regmap gxbb_clk81 = {
.name = "clk81",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &gxbb_mpeg_clk_div.hw
+ &gxbb_clk81_div.hw
},
.num_parents = 1,
.flags = CLK_IS_CRITICAL,
@@ -1001,7 +1095,7 @@ static struct clk_regmap gxbb_sar_adc_clk = {
* switches to the "inactive" one when CLK_SET_RATE_GATE is set.
*/
-static const struct clk_parent_data gxbb_mali_0_1_parent_data[] = {
+static const struct clk_parent_data gxbb_mali_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &gxbb_gp0_pll.hw },
{ .hw = &gxbb_mpll2.hw },
@@ -1021,8 +1115,8 @@ static struct clk_regmap gxbb_mali_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "mali_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_mali_0_1_parent_data,
- .num_parents = 8,
+ .parent_data = gxbb_mali_parents,
+ .num_parents = ARRAY_SIZE(gxbb_mali_parents),
/*
* Don't request the parent to change the rate because
* all GPU frequencies can be derived from the fclk_*
@@ -1075,8 +1169,8 @@ static struct clk_regmap gxbb_mali_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "mali_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_mali_0_1_parent_data,
- .num_parents = 8,
+ .parent_data = gxbb_mali_parents,
+ .num_parents = ARRAY_SIZE(gxbb_mali_parents),
/*
* Don't request the parent to change the rate because
* all GPU frequencies can be derived from the fclk_*
@@ -1120,11 +1214,6 @@ static struct clk_regmap gxbb_mali_1 = {
},
};
-static const struct clk_hw *gxbb_mali_parent_hws[] = {
- &gxbb_mali_0.hw,
- &gxbb_mali_1.hw,
-};
-
static struct clk_regmap gxbb_mali = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MALI_CLK_CNTL,
@@ -1134,29 +1223,35 @@ static struct clk_regmap gxbb_mali = {
.hw.init = &(struct clk_init_data){
.name = "mali",
.ops = &clk_regmap_mux_ops,
- .parent_hws = gxbb_mali_parent_hws,
+ .parent_hws = (const struct clk_hw *[]) {
+ &gxbb_mali_0.hw,
+ &gxbb_mali_1.hw,
+ },
.num_parents = 2,
.flags = CLK_SET_RATE_PARENT,
},
};
+static u32 gxbb_cts_mclk_parents_val_table[] = { 1, 2, 3 };
+static const struct clk_hw *gxbb_cts_mclk_parents[] = {
+ &gxbb_mpll0.hw,
+ &gxbb_mpll1.hw,
+ &gxbb_mpll2.hw,
+};
+
static struct clk_regmap gxbb_cts_amclk_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_AUD_CLK_CNTL,
.mask = 0x3,
.shift = 9,
- .table = (u32[]){ 1, 2, 3 },
+ .table = gxbb_cts_mclk_parents_val_table,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "cts_amclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &gxbb_mpll0.hw,
- &gxbb_mpll1.hw,
- &gxbb_mpll2.hw,
- },
- .num_parents = 3,
+ .parent_hws = gxbb_cts_mclk_parents,
+ .num_parents = ARRAY_SIZE(gxbb_cts_mclk_parents),
},
};
@@ -1199,18 +1294,14 @@ static struct clk_regmap gxbb_cts_mclk_i958_sel = {
.offset = HHI_AUD_CLK_CNTL2,
.mask = 0x3,
.shift = 25,
- .table = (u32[]){ 1, 2, 3 },
+ .table = gxbb_cts_mclk_parents_val_table,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data) {
.name = "cts_mclk_i958_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &gxbb_mpll0.hw,
- &gxbb_mpll1.hw,
- &gxbb_mpll2.hw,
- },
- .num_parents = 3,
+ .parent_hws = gxbb_cts_mclk_parents,
+ .num_parents = ARRAY_SIZE(gxbb_cts_mclk_parents),
},
};
@@ -1270,14 +1361,13 @@ static struct clk_regmap gxbb_cts_i958 = {
},
};
-static const struct clk_parent_data gxbb_32k_clk_parent_data[] = {
+/*
+ * This table skips a clock named 'cts_slow_oscin' in the documentation
+ * This clock does not exist yet in this controller or the AO one
+ */
+static u32 gxbb_32k_clk_parents_val_table[] = { 0, 2, 3 };
+static const struct clk_parent_data gxbb_32k_clk_parents[] = {
{ .fw_name = "xtal", },
- /*
- * FIXME: This clock is provided by the ao clock controller but the
- * clock is not yet part of the binding of this controller, so string
- * name must be use to set this parent.
- */
- { .name = "cts_slow_oscin", .index = -1 },
{ .hw = &gxbb_fclk_div3.hw },
{ .hw = &gxbb_fclk_div5.hw },
};
@@ -1287,11 +1377,12 @@ static struct clk_regmap gxbb_32k_clk_sel = {
.offset = HHI_32K_CLK_CNTL,
.mask = 0x3,
.shift = 16,
- },
+ .table = gxbb_32k_clk_parents_val_table,
+ },
.hw.init = &(struct clk_init_data){
.name = "32k_clk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_32k_clk_parent_data,
+ .parent_data = gxbb_32k_clk_parents,
.num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
},
@@ -1310,7 +1401,7 @@ static struct clk_regmap gxbb_32k_clk_div = {
&gxbb_32k_clk_sel.hw
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -1330,7 +1421,7 @@ static struct clk_regmap gxbb_32k_clk = {
},
};
-static const struct clk_parent_data gxbb_sd_emmc_clk0_parent_data[] = {
+static const struct clk_parent_data gxbb_sd_emmc_clk0_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &gxbb_fclk_div2.hw },
{ .hw = &gxbb_fclk_div3.hw },
@@ -1339,7 +1430,7 @@ static const struct clk_parent_data gxbb_sd_emmc_clk0_parent_data[] = {
/*
* Following these parent clocks, we should also have had mpll2, mpll3
* and gp0_pll but these clocks are too precious to be used here. All
- * the necessary rates for MMC and NAND operation can be acheived using
+ * the necessary rates for MMC and NAND operation can be achieved using
* xtal or fclk_div clocks
*/
};
@@ -1354,8 +1445,8 @@ static struct clk_regmap gxbb_sd_emmc_a_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_a_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_data),
+ .parent_data = gxbb_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1404,8 +1495,8 @@ static struct clk_regmap gxbb_sd_emmc_b_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_data),
+ .parent_data = gxbb_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1454,8 +1545,8 @@ static struct clk_regmap gxbb_sd_emmc_c_clk0_sel = {
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_sd_emmc_clk0_parent_data,
- .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_data),
+ .parent_data = gxbb_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1496,7 +1587,7 @@ static struct clk_regmap gxbb_sd_emmc_c_clk0 = {
/* VPU Clock */
-static const struct clk_hw *gxbb_vpu_parent_hws[] = {
+static const struct clk_hw *gxbb_vpu_parents[] = {
&gxbb_fclk_div4.hw,
&gxbb_fclk_div3.hw,
&gxbb_fclk_div5.hw,
@@ -1516,8 +1607,8 @@ static struct clk_regmap gxbb_vpu_0_sel = {
* bits 9:10 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
*/
- .parent_hws = gxbb_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vpu_parent_hws),
+ .parent_hws = gxbb_vpu_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vpu_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1564,8 +1655,8 @@ static struct clk_regmap gxbb_vpu_1_sel = {
* bits 25:26 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
*/
- .parent_hws = gxbb_vpu_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vpu_parent_hws),
+ .parent_hws = gxbb_vpu_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vpu_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1623,7 +1714,7 @@ static struct clk_regmap gxbb_vpu = {
/* VAPB Clock */
-static const struct clk_hw *gxbb_vapb_parent_hws[] = {
+static const struct clk_hw *gxbb_vapb_parents[] = {
&gxbb_fclk_div4.hw,
&gxbb_fclk_div3.hw,
&gxbb_fclk_div5.hw,
@@ -1643,8 +1734,8 @@ static struct clk_regmap gxbb_vapb_0_sel = {
* bits 9:10 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
*/
- .parent_hws = gxbb_vapb_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vapb_parent_hws),
+ .parent_hws = gxbb_vapb_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vapb_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1695,8 +1786,8 @@ static struct clk_regmap gxbb_vapb_1_sel = {
* bits 25:26 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
*/
- .parent_hws = gxbb_vapb_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vapb_parent_hws),
+ .parent_hws = gxbb_vapb_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vapb_parents),
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1804,7 +1895,7 @@ static struct clk_regmap gxbb_vid_pll_div = {
},
};
-static const struct clk_parent_data gxbb_vid_pll_parent_data[] = {
+static const struct clk_parent_data gxbb_vid_pll_parents[] = {
{ .hw = &gxbb_vid_pll_div.hw },
/*
* Note:
@@ -1829,8 +1920,8 @@ static struct clk_regmap gxbb_vid_pll_sel = {
* bit 18 selects from 2 possible parents:
* vid_pll_div or hdmi_pll
*/
- .parent_data = gxbb_vid_pll_parent_data,
- .num_parents = ARRAY_SIZE(gxbb_vid_pll_parent_data),
+ .parent_data = gxbb_vid_pll_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vid_pll_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -1851,7 +1942,7 @@ static struct clk_regmap gxbb_vid_pll = {
},
};
-static const struct clk_hw *gxbb_vclk_parent_hws[] = {
+static const struct clk_hw *gxbb_vclk_parents[] = {
&gxbb_vid_pll.hw,
&gxbb_fclk_div4.hw,
&gxbb_fclk_div3.hw,
@@ -1875,8 +1966,8 @@ static struct clk_regmap gxbb_vclk_sel = {
* vid_pll, fclk_div4, fclk_div3, fclk_div5,
* vid_pll, fclk_div7, mp1
*/
- .parent_hws = gxbb_vclk_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vclk_parent_hws),
+ .parent_hws = gxbb_vclk_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vclk_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -1895,8 +1986,8 @@ static struct clk_regmap gxbb_vclk2_sel = {
* vid_pll, fclk_div4, fclk_div3, fclk_div5,
* vid_pll, fclk_div7, mp1
*/
- .parent_hws = gxbb_vclk_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vclk_parent_hws),
+ .parent_hws = gxbb_vclk_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vclk_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -2235,8 +2326,8 @@ static struct clk_fixed_factor gxbb_vclk2_div12 = {
},
};
-static u32 mux_table_cts_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
-static const struct clk_hw *gxbb_cts_parent_hws[] = {
+static u32 gxbb_cts_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *gxbb_cts_parents[] = {
&gxbb_vclk_div1.hw,
&gxbb_vclk_div2.hw,
&gxbb_vclk_div4.hw,
@@ -2254,13 +2345,13 @@ static struct clk_regmap gxbb_cts_enci_sel = {
.offset = HHI_VID_CLK_DIV,
.mask = 0xf,
.shift = 28,
- .table = mux_table_cts_sel,
+ .table = gxbb_cts_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_enci_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = gxbb_cts_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_cts_parent_hws),
+ .parent_hws = gxbb_cts_parents,
+ .num_parents = ARRAY_SIZE(gxbb_cts_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -2270,13 +2361,13 @@ static struct clk_regmap gxbb_cts_encp_sel = {
.offset = HHI_VID_CLK_DIV,
.mask = 0xf,
.shift = 20,
- .table = mux_table_cts_sel,
+ .table = gxbb_cts_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_encp_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = gxbb_cts_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_cts_parent_hws),
+ .parent_hws = gxbb_cts_parents,
+ .num_parents = ARRAY_SIZE(gxbb_cts_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -2286,50 +2377,13 @@ static struct clk_regmap gxbb_cts_vdac_sel = {
.offset = HHI_VIID_CLK_DIV,
.mask = 0xf,
.shift = 28,
- .table = mux_table_cts_sel,
+ .table = gxbb_cts_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cts_vdac_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = gxbb_cts_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_cts_parent_hws),
- .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
- },
-};
-
-/* TOFIX: add support for cts_tcon */
-static u32 mux_table_hdmi_tx_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
-static const struct clk_hw *gxbb_cts_hdmi_tx_parent_hws[] = {
- &gxbb_vclk_div1.hw,
- &gxbb_vclk_div2.hw,
- &gxbb_vclk_div4.hw,
- &gxbb_vclk_div6.hw,
- &gxbb_vclk_div12.hw,
- &gxbb_vclk2_div1.hw,
- &gxbb_vclk2_div2.hw,
- &gxbb_vclk2_div4.hw,
- &gxbb_vclk2_div6.hw,
- &gxbb_vclk2_div12.hw,
-};
-
-static struct clk_regmap gxbb_hdmi_tx_sel = {
- .data = &(struct clk_regmap_mux_data){
- .offset = HHI_HDMI_CLK_CNTL,
- .mask = 0xf,
- .shift = 16,
- .table = mux_table_hdmi_tx_sel,
- },
- .hw.init = &(struct clk_init_data){
- .name = "hdmi_tx_sel",
- .ops = &clk_regmap_mux_ops,
- /*
- * bits 31:28 selects from 12 possible parents:
- * vclk_div1, vclk_div2, vclk_div4, vclk_div6, vclk_div12
- * vclk2_div1, vclk2_div2, vclk2_div4, vclk2_div6, vclk2_div12,
- * cts_tcon
- */
- .parent_hws = gxbb_cts_hdmi_tx_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_cts_hdmi_tx_parent_hws),
+ .parent_hws = gxbb_cts_parents,
+ .num_parents = ARRAY_SIZE(gxbb_cts_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -2382,6 +2436,43 @@ static struct clk_regmap gxbb_cts_vdac = {
},
};
+/* TOFIX: add support for cts_tcon */
+static u32 gxbb_hdmi_tx_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *gxbb_hdmi_tx_parents[] = {
+ &gxbb_vclk_div1.hw,
+ &gxbb_vclk_div2.hw,
+ &gxbb_vclk_div4.hw,
+ &gxbb_vclk_div6.hw,
+ &gxbb_vclk_div12.hw,
+ &gxbb_vclk2_div1.hw,
+ &gxbb_vclk2_div2.hw,
+ &gxbb_vclk2_div4.hw,
+ &gxbb_vclk2_div6.hw,
+ &gxbb_vclk2_div12.hw,
+};
+
+static struct clk_regmap gxbb_hdmi_tx_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_HDMI_CLK_CNTL,
+ .mask = 0xf,
+ .shift = 16,
+ .table = gxbb_hdmi_tx_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_tx_sel",
+ .ops = &clk_regmap_mux_ops,
+ /*
+ * bits 31:28 selects from 12 possible parents:
+ * vclk_div1, vclk_div2, vclk_div4, vclk_div6, vclk_div12
+ * vclk2_div1, vclk2_div2, vclk2_div4, vclk2_div6, vclk2_div12,
+ * cts_tcon
+ */
+ .parent_hws = gxbb_hdmi_tx_parents,
+ .num_parents = ARRAY_SIZE(gxbb_hdmi_tx_parents),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
static struct clk_regmap gxbb_hdmi_tx = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL2,
@@ -2400,7 +2491,7 @@ static struct clk_regmap gxbb_hdmi_tx = {
/* HDMI Clocks */
-static const struct clk_parent_data gxbb_hdmi_parent_data[] = {
+static const struct clk_parent_data gxbb_hdmi_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &gxbb_fclk_div4.hw },
{ .hw = &gxbb_fclk_div3.hw },
@@ -2417,8 +2508,8 @@ static struct clk_regmap gxbb_hdmi_sel = {
.hw.init = &(struct clk_init_data){
.name = "hdmi_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = gxbb_hdmi_parent_data,
- .num_parents = ARRAY_SIZE(gxbb_hdmi_parent_data),
+ .parent_data = gxbb_hdmi_parents,
+ .num_parents = ARRAY_SIZE(gxbb_hdmi_parents),
.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
},
};
@@ -2454,7 +2545,7 @@ static struct clk_regmap gxbb_hdmi = {
/* VDEC clocks */
-static const struct clk_hw *gxbb_vdec_parent_hws[] = {
+static const struct clk_hw *gxbb_vdec_parents[] = {
&gxbb_fclk_div4.hw,
&gxbb_fclk_div3.hw,
&gxbb_fclk_div5.hw,
@@ -2471,8 +2562,8 @@ static struct clk_regmap gxbb_vdec_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = gxbb_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vdec_parent_hws),
+ .parent_hws = gxbb_vdec_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2521,8 +2612,8 @@ static struct clk_regmap gxbb_vdec_hevc_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_hevc_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = gxbb_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(gxbb_vdec_parent_hws),
+ .parent_hws = gxbb_vdec_parents,
+ .num_parents = ARRAY_SIZE(gxbb_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2561,9 +2652,8 @@ static struct clk_regmap gxbb_vdec_hevc = {
},
};
-static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8,
- 9, 10, 11, 13, 14, };
-static const struct clk_parent_data gen_clk_parent_data[] = {
+static u32 gxbb_gen_clk_parents_val_table[] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, };
+static const struct clk_parent_data gxbb_gen_clk_parents[] = {
{ .fw_name = "xtal", },
{ .hw = &gxbb_vdec_1.hw },
{ .hw = &gxbb_vdec_hevc.hw },
@@ -2582,7 +2672,7 @@ static struct clk_regmap gxbb_gen_clk_sel = {
.offset = HHI_GEN_CLK_CNTL,
.mask = 0xf,
.shift = 12,
- .table = mux_table_gen_clk,
+ .table = gxbb_gen_clk_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "gen_clk_sel",
@@ -2593,8 +2683,8 @@ static struct clk_regmap gxbb_gen_clk_sel = {
* vid_pll, vid2_pll (hevc), mpll0, mpll1, mpll2, fdiv4,
* fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll
*/
- .parent_data = gen_clk_parent_data,
- .num_parents = ARRAY_SIZE(gen_clk_parent_data),
+ .parent_data = gxbb_gen_clk_parents,
+ .num_parents = ARRAY_SIZE(gxbb_gen_clk_parents),
},
};
@@ -2631,942 +2721,567 @@ static struct clk_regmap gxbb_gen_clk = {
},
};
-#define MESON_GATE(_name, _reg, _bit) \
- MESON_PCLK(_name, _reg, _bit, &gxbb_clk81.hw)
-
-/* Everything Else (EE) domain gates */
-static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0);
-static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1);
-static MESON_GATE(gxbb_isa, HHI_GCLK_MPEG0, 5);
-static MESON_GATE(gxbb_pl301, HHI_GCLK_MPEG0, 6);
-static MESON_GATE(gxbb_periphs, HHI_GCLK_MPEG0, 7);
-static MESON_GATE(gxbb_spicc, HHI_GCLK_MPEG0, 8);
-static MESON_GATE(gxbb_i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG0, 10);
-static MESON_GATE(gxbb_smart_card, HHI_GCLK_MPEG0, 11);
-static MESON_GATE(gxbb_rng0, HHI_GCLK_MPEG0, 12);
-static MESON_GATE(gxbb_uart0, HHI_GCLK_MPEG0, 13);
-static MESON_GATE(gxbb_sdhc, HHI_GCLK_MPEG0, 14);
-static MESON_GATE(gxbb_stream, HHI_GCLK_MPEG0, 15);
-static MESON_GATE(gxbb_async_fifo, HHI_GCLK_MPEG0, 16);
-static MESON_GATE(gxbb_sdio, HHI_GCLK_MPEG0, 17);
-static MESON_GATE(gxbb_abuf, HHI_GCLK_MPEG0, 18);
-static MESON_GATE(gxbb_hiu_iface, HHI_GCLK_MPEG0, 19);
-static MESON_GATE(gxbb_assist_misc, HHI_GCLK_MPEG0, 23);
-static MESON_GATE(gxbb_emmc_a, HHI_GCLK_MPEG0, 24);
-static MESON_GATE(gxbb_emmc_b, HHI_GCLK_MPEG0, 25);
-static MESON_GATE(gxbb_emmc_c, HHI_GCLK_MPEG0, 26);
-static MESON_GATE(gxl_acodec, HHI_GCLK_MPEG0, 28);
-static MESON_GATE(gxbb_spi, HHI_GCLK_MPEG0, 30);
-
-static MESON_GATE(gxbb_i2s_spdif, HHI_GCLK_MPEG1, 2);
-static MESON_GATE(gxbb_eth, HHI_GCLK_MPEG1, 3);
-static MESON_GATE(gxbb_demux, HHI_GCLK_MPEG1, 4);
-static MESON_GATE(gxbb_blkmv, HHI_GCLK_MPEG1, 14);
-static MESON_GATE(gxbb_aiu, HHI_GCLK_MPEG1, 15);
-static MESON_GATE(gxbb_uart1, HHI_GCLK_MPEG1, 16);
-static MESON_GATE(gxbb_g2d, HHI_GCLK_MPEG1, 20);
-static MESON_GATE(gxbb_usb0, HHI_GCLK_MPEG1, 21);
-static MESON_GATE(gxbb_usb1, HHI_GCLK_MPEG1, 22);
-static MESON_GATE(gxbb_reset, HHI_GCLK_MPEG1, 23);
-static MESON_GATE(gxbb_nand, HHI_GCLK_MPEG1, 24);
-static MESON_GATE(gxbb_dos_parser, HHI_GCLK_MPEG1, 25);
-static MESON_GATE(gxbb_usb, HHI_GCLK_MPEG1, 26);
-static MESON_GATE(gxbb_vdin1, HHI_GCLK_MPEG1, 28);
-static MESON_GATE(gxbb_ahb_arb0, HHI_GCLK_MPEG1, 29);
-static MESON_GATE(gxbb_efuse, HHI_GCLK_MPEG1, 30);
-static MESON_GATE(gxbb_boot_rom, HHI_GCLK_MPEG1, 31);
-
-static MESON_GATE(gxbb_ahb_data_bus, HHI_GCLK_MPEG2, 1);
-static MESON_GATE(gxbb_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
-static MESON_GATE(gxbb_hdmi_intr_sync, HHI_GCLK_MPEG2, 3);
-static MESON_GATE(gxbb_hdmi_pclk, HHI_GCLK_MPEG2, 4);
-static MESON_GATE(gxbb_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8);
-static MESON_GATE(gxbb_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9);
-static MESON_GATE(gxbb_mmc_pclk, HHI_GCLK_MPEG2, 11);
-static MESON_GATE(gxbb_dvin, HHI_GCLK_MPEG2, 12);
-static MESON_GATE(gxbb_uart2, HHI_GCLK_MPEG2, 15);
-static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG2, 22);
-static MESON_GATE(gxbb_vpu_intr, HHI_GCLK_MPEG2, 25);
-static MESON_GATE(gxbb_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
-static MESON_GATE(gxbb_clk81_a53, HHI_GCLK_MPEG2, 29);
-
-static MESON_GATE(gxbb_vclk2_venci0, HHI_GCLK_OTHER, 1);
-static MESON_GATE(gxbb_vclk2_venci1, HHI_GCLK_OTHER, 2);
-static MESON_GATE(gxbb_vclk2_vencp0, HHI_GCLK_OTHER, 3);
-static MESON_GATE(gxbb_vclk2_vencp1, HHI_GCLK_OTHER, 4);
-static MESON_GATE(gxbb_gclk_venci_int0, HHI_GCLK_OTHER, 8);
-static MESON_GATE(gxbb_gclk_vencp_int, HHI_GCLK_OTHER, 9);
-static MESON_GATE(gxbb_dac_clk, HHI_GCLK_OTHER, 10);
-static MESON_GATE(gxbb_aoclk_gate, HHI_GCLK_OTHER, 14);
-static MESON_GATE(gxbb_iec958_gate, HHI_GCLK_OTHER, 16);
-static MESON_GATE(gxbb_enc480p, HHI_GCLK_OTHER, 20);
-static MESON_GATE(gxbb_rng1, HHI_GCLK_OTHER, 21);
-static MESON_GATE(gxbb_gclk_venci_int1, HHI_GCLK_OTHER, 22);
-static MESON_GATE(gxbb_vclk2_venclmcc, HHI_GCLK_OTHER, 24);
-static MESON_GATE(gxbb_vclk2_vencl, HHI_GCLK_OTHER, 25);
-static MESON_GATE(gxbb_vclk_other, HHI_GCLK_OTHER, 26);
-static MESON_GATE(gxbb_edp, HHI_GCLK_OTHER, 31);
+static const struct clk_parent_data gxbb_pclk_parents = { .hw = &gxbb_clk81.hw };
+
+#define GXBB_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(_name, _reg, _bit, &gxbb_pclk_parents, _flags)
+
+/*
+ * Everything Else (EE) domain gates
+ *
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
+ */
+static GXBB_PCLK(gxbb_ddr, HHI_GCLK_MPEG0, 0, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_dos, HHI_GCLK_MPEG0, 1, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_isa, HHI_GCLK_MPEG0, 5, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_pl301, HHI_GCLK_MPEG0, 6, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_periphs, HHI_GCLK_MPEG0, 7, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_spicc, HHI_GCLK_MPEG0, 8, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_i2c, HHI_GCLK_MPEG0, 9, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_sana, HHI_GCLK_MPEG0, 10, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_smart_card, HHI_GCLK_MPEG0, 11, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_rng0, HHI_GCLK_MPEG0, 12, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_uart0, HHI_GCLK_MPEG0, 13, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_sdhc, HHI_GCLK_MPEG0, 14, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_stream, HHI_GCLK_MPEG0, 15, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_async_fifo, HHI_GCLK_MPEG0, 16, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_sdio, HHI_GCLK_MPEG0, 17, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_abuf, HHI_GCLK_MPEG0, 18, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_hiu_iface, HHI_GCLK_MPEG0, 19, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_assist_misc, HHI_GCLK_MPEG0, 23, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_emmc_a, HHI_GCLK_MPEG0, 24, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_emmc_b, HHI_GCLK_MPEG0, 25, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_emmc_c, HHI_GCLK_MPEG0, 26, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxl_acodec, HHI_GCLK_MPEG0, 28, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_spi, HHI_GCLK_MPEG0, 30, CLK_IGNORE_UNUSED);
+
+static GXBB_PCLK(gxbb_i2s_spdif, HHI_GCLK_MPEG1, 2, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_eth, HHI_GCLK_MPEG1, 3, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_demux, HHI_GCLK_MPEG1, 4, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_blkmv, HHI_GCLK_MPEG1, 14, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_aiu, HHI_GCLK_MPEG1, 15, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_uart1, HHI_GCLK_MPEG1, 16, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_g2d, HHI_GCLK_MPEG1, 20, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_usb0, HHI_GCLK_MPEG1, 21, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_usb1, HHI_GCLK_MPEG1, 22, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_reset, HHI_GCLK_MPEG1, 23, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_nand, HHI_GCLK_MPEG1, 24, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_dos_parser, HHI_GCLK_MPEG1, 25, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_usb, HHI_GCLK_MPEG1, 26, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vdin1, HHI_GCLK_MPEG1, 28, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_ahb_arb0, HHI_GCLK_MPEG1, 29, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_efuse, HHI_GCLK_MPEG1, 30, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_boot_rom, HHI_GCLK_MPEG1, 31, CLK_IGNORE_UNUSED);
+
+static GXBB_PCLK(gxbb_ahb_data_bus, HHI_GCLK_MPEG2, 1, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_hdmi_intr_sync, HHI_GCLK_MPEG2, 3, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_hdmi_pclk, HHI_GCLK_MPEG2, 4, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_mmc_pclk, HHI_GCLK_MPEG2, 11, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_dvin, HHI_GCLK_MPEG2, 12, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_uart2, HHI_GCLK_MPEG2, 15, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_sar_adc, HHI_GCLK_MPEG2, 22, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vpu_intr, HHI_GCLK_MPEG2, 25, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_clk81_a53, HHI_GCLK_MPEG2, 29, CLK_IGNORE_UNUSED);
+
+static GXBB_PCLK(gxbb_vclk2_venci0, HHI_GCLK_OTHER, 1, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vclk2_venci1, HHI_GCLK_OTHER, 2, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vclk2_vencp0, HHI_GCLK_OTHER, 3, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vclk2_vencp1, HHI_GCLK_OTHER, 4, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_gclk_venci_int0, HHI_GCLK_OTHER, 8, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_gclk_vencp_int, HHI_GCLK_OTHER, 9, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_dac_clk, HHI_GCLK_OTHER, 10, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_aoclk_gate, HHI_GCLK_OTHER, 14, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_iec958_gate, HHI_GCLK_OTHER, 16, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_enc480p, HHI_GCLK_OTHER, 20, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_rng1, HHI_GCLK_OTHER, 21, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_gclk_venci_int1, HHI_GCLK_OTHER, 22, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vclk2_venclmcc, HHI_GCLK_OTHER, 24, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vclk2_vencl, HHI_GCLK_OTHER, 25, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_vclk_other, HHI_GCLK_OTHER, 26, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_edp, HHI_GCLK_OTHER, 31, CLK_IGNORE_UNUSED);
/* Always On (AO) domain gates */
-static MESON_GATE(gxbb_ao_media_cpu, HHI_GCLK_AO, 0);
-static MESON_GATE(gxbb_ao_ahb_sram, HHI_GCLK_AO, 1);
-static MESON_GATE(gxbb_ao_ahb_bus, HHI_GCLK_AO, 2);
-static MESON_GATE(gxbb_ao_iface, HHI_GCLK_AO, 3);
-static MESON_GATE(gxbb_ao_i2c, HHI_GCLK_AO, 4);
+static GXBB_PCLK(gxbb_ao_media_cpu, HHI_GCLK_AO, 0, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_ao_ahb_sram, HHI_GCLK_AO, 1, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_ao_ahb_bus, HHI_GCLK_AO, 2, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_ao_iface, HHI_GCLK_AO, 3, CLK_IGNORE_UNUSED);
+static GXBB_PCLK(gxbb_ao_i2c, HHI_GCLK_AO, 4, CLK_IGNORE_UNUSED);
/* AIU gates */
-static MESON_PCLK(gxbb_aiu_glue, HHI_GCLK_MPEG1, 6, &gxbb_aiu.hw);
-static MESON_PCLK(gxbb_iec958, HHI_GCLK_MPEG1, 7, &gxbb_aiu_glue.hw);
-static MESON_PCLK(gxbb_i2s_out, HHI_GCLK_MPEG1, 8, &gxbb_aiu_glue.hw);
-static MESON_PCLK(gxbb_amclk, HHI_GCLK_MPEG1, 9, &gxbb_aiu_glue.hw);
-static MESON_PCLK(gxbb_aififo2, HHI_GCLK_MPEG1, 10, &gxbb_aiu_glue.hw);
-static MESON_PCLK(gxbb_mixer, HHI_GCLK_MPEG1, 11, &gxbb_aiu_glue.hw);
-static MESON_PCLK(gxbb_mixer_iface, HHI_GCLK_MPEG1, 12, &gxbb_aiu_glue.hw);
-static MESON_PCLK(gxbb_adc, HHI_GCLK_MPEG1, 13, &gxbb_aiu_glue.hw);
+static const struct clk_parent_data gxbb_aiu_glue_parents = { .hw = &gxbb_aiu.hw };
+static MESON_PCLK(gxbb_aiu_glue, HHI_GCLK_MPEG1, 6, &gxbb_aiu_glue_parents, CLK_IGNORE_UNUSED);
+
+static const struct clk_parent_data gxbb_aiu_pclk_parents = { .hw = &gxbb_aiu_glue.hw };
+#define GXBB_AIU_PCLK(_name, _bit, _flags) \
+ MESON_PCLK(_name, HHI_GCLK_MPEG1, _bit, &gxbb_aiu_pclk_parents, _flags)
+
+static GXBB_AIU_PCLK(gxbb_iec958, 7, CLK_IGNORE_UNUSED);
+static GXBB_AIU_PCLK(gxbb_i2s_out, 8, CLK_IGNORE_UNUSED);
+static GXBB_AIU_PCLK(gxbb_amclk, 9, CLK_IGNORE_UNUSED);
+static GXBB_AIU_PCLK(gxbb_aififo2, 10, CLK_IGNORE_UNUSED);
+static GXBB_AIU_PCLK(gxbb_mixer, 11, CLK_IGNORE_UNUSED);
+static GXBB_AIU_PCLK(gxbb_mixer_iface, 12, CLK_IGNORE_UNUSED);
+static GXBB_AIU_PCLK(gxbb_adc, 13, CLK_IGNORE_UNUSED);
/* Array of all clocks provided by this provider */
-static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
- .hws = {
- [CLKID_SYS_PLL] = &gxbb_sys_pll.hw,
- [CLKID_HDMI_PLL] = &gxbb_hdmi_pll.hw,
- [CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw,
- [CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &gxbb_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &gxbb_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &gxbb_fclk_div7.hw,
- [CLKID_GP0_PLL] = &gxbb_gp0_pll.hw,
- [CLKID_MPEG_SEL] = &gxbb_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &gxbb_mpeg_clk_div.hw,
- [CLKID_CLK81] = &gxbb_clk81.hw,
- [CLKID_MPLL0] = &gxbb_mpll0.hw,
- [CLKID_MPLL1] = &gxbb_mpll1.hw,
- [CLKID_MPLL2] = &gxbb_mpll2.hw,
- [CLKID_DDR] = &gxbb_ddr.hw,
- [CLKID_DOS] = &gxbb_dos.hw,
- [CLKID_ISA] = &gxbb_isa.hw,
- [CLKID_PL301] = &gxbb_pl301.hw,
- [CLKID_PERIPHS] = &gxbb_periphs.hw,
- [CLKID_SPICC] = &gxbb_spicc.hw,
- [CLKID_I2C] = &gxbb_i2c.hw,
- [CLKID_SAR_ADC] = &gxbb_sar_adc.hw,
- [CLKID_SMART_CARD] = &gxbb_smart_card.hw,
- [CLKID_RNG0] = &gxbb_rng0.hw,
- [CLKID_UART0] = &gxbb_uart0.hw,
- [CLKID_SDHC] = &gxbb_sdhc.hw,
- [CLKID_STREAM] = &gxbb_stream.hw,
- [CLKID_ASYNC_FIFO] = &gxbb_async_fifo.hw,
- [CLKID_SDIO] = &gxbb_sdio.hw,
- [CLKID_ABUF] = &gxbb_abuf.hw,
- [CLKID_HIU_IFACE] = &gxbb_hiu_iface.hw,
- [CLKID_ASSIST_MISC] = &gxbb_assist_misc.hw,
- [CLKID_SPI] = &gxbb_spi.hw,
- [CLKID_I2S_SPDIF] = &gxbb_i2s_spdif.hw,
- [CLKID_ETH] = &gxbb_eth.hw,
- [CLKID_DEMUX] = &gxbb_demux.hw,
- [CLKID_AIU_GLUE] = &gxbb_aiu_glue.hw,
- [CLKID_IEC958] = &gxbb_iec958.hw,
- [CLKID_I2S_OUT] = &gxbb_i2s_out.hw,
- [CLKID_AMCLK] = &gxbb_amclk.hw,
- [CLKID_AIFIFO2] = &gxbb_aififo2.hw,
- [CLKID_MIXER] = &gxbb_mixer.hw,
- [CLKID_MIXER_IFACE] = &gxbb_mixer_iface.hw,
- [CLKID_ADC] = &gxbb_adc.hw,
- [CLKID_BLKMV] = &gxbb_blkmv.hw,
- [CLKID_AIU] = &gxbb_aiu.hw,
- [CLKID_UART1] = &gxbb_uart1.hw,
- [CLKID_G2D] = &gxbb_g2d.hw,
- [CLKID_USB0] = &gxbb_usb0.hw,
- [CLKID_USB1] = &gxbb_usb1.hw,
- [CLKID_RESET] = &gxbb_reset.hw,
- [CLKID_NAND] = &gxbb_nand.hw,
- [CLKID_DOS_PARSER] = &gxbb_dos_parser.hw,
- [CLKID_USB] = &gxbb_usb.hw,
- [CLKID_VDIN1] = &gxbb_vdin1.hw,
- [CLKID_AHB_ARB0] = &gxbb_ahb_arb0.hw,
- [CLKID_EFUSE] = &gxbb_efuse.hw,
- [CLKID_BOOT_ROM] = &gxbb_boot_rom.hw,
- [CLKID_AHB_DATA_BUS] = &gxbb_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &gxbb_ahb_ctrl_bus.hw,
- [CLKID_HDMI_INTR_SYNC] = &gxbb_hdmi_intr_sync.hw,
- [CLKID_HDMI_PCLK] = &gxbb_hdmi_pclk.hw,
- [CLKID_USB1_DDR_BRIDGE] = &gxbb_usb1_ddr_bridge.hw,
- [CLKID_USB0_DDR_BRIDGE] = &gxbb_usb0_ddr_bridge.hw,
- [CLKID_MMC_PCLK] = &gxbb_mmc_pclk.hw,
- [CLKID_DVIN] = &gxbb_dvin.hw,
- [CLKID_UART2] = &gxbb_uart2.hw,
- [CLKID_SANA] = &gxbb_sana.hw,
- [CLKID_VPU_INTR] = &gxbb_vpu_intr.hw,
- [CLKID_SEC_AHB_AHB3_BRIDGE] = &gxbb_sec_ahb_ahb3_bridge.hw,
- [CLKID_CLK81_A53] = &gxbb_clk81_a53.hw,
- [CLKID_VCLK2_VENCI0] = &gxbb_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &gxbb_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &gxbb_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &gxbb_vclk2_vencp1.hw,
- [CLKID_GCLK_VENCI_INT0] = &gxbb_gclk_venci_int0.hw,
- [CLKID_GCLK_VENCI_INT] = &gxbb_gclk_vencp_int.hw,
- [CLKID_DAC_CLK] = &gxbb_dac_clk.hw,
- [CLKID_AOCLK_GATE] = &gxbb_aoclk_gate.hw,
- [CLKID_IEC958_GATE] = &gxbb_iec958_gate.hw,
- [CLKID_ENC480P] = &gxbb_enc480p.hw,
- [CLKID_RNG1] = &gxbb_rng1.hw,
- [CLKID_GCLK_VENCI_INT1] = &gxbb_gclk_venci_int1.hw,
- [CLKID_VCLK2_VENCLMCC] = &gxbb_vclk2_venclmcc.hw,
- [CLKID_VCLK2_VENCL] = &gxbb_vclk2_vencl.hw,
- [CLKID_VCLK_OTHER] = &gxbb_vclk_other.hw,
- [CLKID_EDP] = &gxbb_edp.hw,
- [CLKID_AO_MEDIA_CPU] = &gxbb_ao_media_cpu.hw,
- [CLKID_AO_AHB_SRAM] = &gxbb_ao_ahb_sram.hw,
- [CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw,
- [CLKID_AO_IFACE] = &gxbb_ao_iface.hw,
- [CLKID_AO_I2C] = &gxbb_ao_i2c.hw,
- [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw,
- [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw,
- [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw,
- [CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw,
- [CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw,
- [CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw,
- [CLKID_MALI_0_SEL] = &gxbb_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &gxbb_mali_0_div.hw,
- [CLKID_MALI_0] = &gxbb_mali_0.hw,
- [CLKID_MALI_1_SEL] = &gxbb_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &gxbb_mali_1_div.hw,
- [CLKID_MALI_1] = &gxbb_mali_1.hw,
- [CLKID_MALI] = &gxbb_mali.hw,
- [CLKID_CTS_AMCLK] = &gxbb_cts_amclk.hw,
- [CLKID_CTS_AMCLK_SEL] = &gxbb_cts_amclk_sel.hw,
- [CLKID_CTS_AMCLK_DIV] = &gxbb_cts_amclk_div.hw,
- [CLKID_CTS_MCLK_I958] = &gxbb_cts_mclk_i958.hw,
- [CLKID_CTS_MCLK_I958_SEL] = &gxbb_cts_mclk_i958_sel.hw,
- [CLKID_CTS_MCLK_I958_DIV] = &gxbb_cts_mclk_i958_div.hw,
- [CLKID_CTS_I958] = &gxbb_cts_i958.hw,
- [CLKID_32K_CLK] = &gxbb_32k_clk.hw,
- [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw,
- [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw,
- [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw,
- [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw,
- [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw,
- [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw,
- [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw,
- [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw,
- [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw,
- [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw,
- [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw,
- [CLKID_VPU_0_SEL] = &gxbb_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &gxbb_vpu_0_div.hw,
- [CLKID_VPU_0] = &gxbb_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &gxbb_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &gxbb_vpu_1_div.hw,
- [CLKID_VPU_1] = &gxbb_vpu_1.hw,
- [CLKID_VPU] = &gxbb_vpu.hw,
- [CLKID_VAPB_0_SEL] = &gxbb_vapb_0_sel.hw,
- [CLKID_VAPB_0_DIV] = &gxbb_vapb_0_div.hw,
- [CLKID_VAPB_0] = &gxbb_vapb_0.hw,
- [CLKID_VAPB_1_SEL] = &gxbb_vapb_1_sel.hw,
- [CLKID_VAPB_1_DIV] = &gxbb_vapb_1_div.hw,
- [CLKID_VAPB_1] = &gxbb_vapb_1.hw,
- [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw,
- [CLKID_VAPB] = &gxbb_vapb.hw,
- [CLKID_HDMI_PLL_PRE_MULT] = &gxbb_hdmi_pll_pre_mult.hw,
- [CLKID_MPLL0_DIV] = &gxbb_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &gxbb_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &gxbb_mpll2_div.hw,
- [CLKID_MPLL_PREDIV] = &gxbb_mpll_prediv.hw,
- [CLKID_FCLK_DIV2_DIV] = &gxbb_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &gxbb_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &gxbb_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &gxbb_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &gxbb_fclk_div7_div.hw,
- [CLKID_VDEC_1_SEL] = &gxbb_vdec_1_sel.hw,
- [CLKID_VDEC_1_DIV] = &gxbb_vdec_1_div.hw,
- [CLKID_VDEC_1] = &gxbb_vdec_1.hw,
- [CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
- [CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
- [CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
- [CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
- [CLKID_FIXED_PLL_DCO] = &gxbb_fixed_pll_dco.hw,
- [CLKID_HDMI_PLL_DCO] = &gxbb_hdmi_pll_dco.hw,
- [CLKID_HDMI_PLL_OD] = &gxbb_hdmi_pll_od.hw,
- [CLKID_HDMI_PLL_OD2] = &gxbb_hdmi_pll_od2.hw,
- [CLKID_SYS_PLL_DCO] = &gxbb_sys_pll_dco.hw,
- [CLKID_GP0_PLL_DCO] = &gxbb_gp0_pll_dco.hw,
- [CLKID_VID_PLL_DIV] = &gxbb_vid_pll_div.hw,
- [CLKID_VID_PLL_SEL] = &gxbb_vid_pll_sel.hw,
- [CLKID_VID_PLL] = &gxbb_vid_pll.hw,
- [CLKID_VCLK_SEL] = &gxbb_vclk_sel.hw,
- [CLKID_VCLK2_SEL] = &gxbb_vclk2_sel.hw,
- [CLKID_VCLK_INPUT] = &gxbb_vclk_input.hw,
- [CLKID_VCLK2_INPUT] = &gxbb_vclk2_input.hw,
- [CLKID_VCLK_DIV] = &gxbb_vclk_div.hw,
- [CLKID_VCLK2_DIV] = &gxbb_vclk2_div.hw,
- [CLKID_VCLK] = &gxbb_vclk.hw,
- [CLKID_VCLK2] = &gxbb_vclk2.hw,
- [CLKID_VCLK_DIV1] = &gxbb_vclk_div1.hw,
- [CLKID_VCLK_DIV2_EN] = &gxbb_vclk_div2_en.hw,
- [CLKID_VCLK_DIV2] = &gxbb_vclk_div2.hw,
- [CLKID_VCLK_DIV4_EN] = &gxbb_vclk_div4_en.hw,
- [CLKID_VCLK_DIV4] = &gxbb_vclk_div4.hw,
- [CLKID_VCLK_DIV6_EN] = &gxbb_vclk_div6_en.hw,
- [CLKID_VCLK_DIV6] = &gxbb_vclk_div6.hw,
- [CLKID_VCLK_DIV12_EN] = &gxbb_vclk_div12_en.hw,
- [CLKID_VCLK_DIV12] = &gxbb_vclk_div12.hw,
- [CLKID_VCLK2_DIV1] = &gxbb_vclk2_div1.hw,
- [CLKID_VCLK2_DIV2_EN] = &gxbb_vclk2_div2_en.hw,
- [CLKID_VCLK2_DIV2] = &gxbb_vclk2_div2.hw,
- [CLKID_VCLK2_DIV4_EN] = &gxbb_vclk2_div4_en.hw,
- [CLKID_VCLK2_DIV4] = &gxbb_vclk2_div4.hw,
- [CLKID_VCLK2_DIV6_EN] = &gxbb_vclk2_div6_en.hw,
- [CLKID_VCLK2_DIV6] = &gxbb_vclk2_div6.hw,
- [CLKID_VCLK2_DIV12_EN] = &gxbb_vclk2_div12_en.hw,
- [CLKID_VCLK2_DIV12] = &gxbb_vclk2_div12.hw,
- [CLKID_CTS_ENCI_SEL] = &gxbb_cts_enci_sel.hw,
- [CLKID_CTS_ENCP_SEL] = &gxbb_cts_encp_sel.hw,
- [CLKID_CTS_VDAC_SEL] = &gxbb_cts_vdac_sel.hw,
- [CLKID_HDMI_TX_SEL] = &gxbb_hdmi_tx_sel.hw,
- [CLKID_CTS_ENCI] = &gxbb_cts_enci.hw,
- [CLKID_CTS_ENCP] = &gxbb_cts_encp.hw,
- [CLKID_CTS_VDAC] = &gxbb_cts_vdac.hw,
- [CLKID_HDMI_TX] = &gxbb_hdmi_tx.hw,
- [CLKID_HDMI_SEL] = &gxbb_hdmi_sel.hw,
- [CLKID_HDMI_DIV] = &gxbb_hdmi_div.hw,
- [CLKID_HDMI] = &gxbb_hdmi.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-static struct clk_hw_onecell_data gxl_hw_onecell_data = {
- .hws = {
- [CLKID_SYS_PLL] = &gxbb_sys_pll.hw,
- [CLKID_HDMI_PLL] = &gxl_hdmi_pll.hw,
- [CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw,
- [CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &gxbb_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &gxbb_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &gxbb_fclk_div7.hw,
- [CLKID_GP0_PLL] = &gxbb_gp0_pll.hw,
- [CLKID_MPEG_SEL] = &gxbb_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &gxbb_mpeg_clk_div.hw,
- [CLKID_CLK81] = &gxbb_clk81.hw,
- [CLKID_MPLL0] = &gxbb_mpll0.hw,
- [CLKID_MPLL1] = &gxbb_mpll1.hw,
- [CLKID_MPLL2] = &gxbb_mpll2.hw,
- [CLKID_DDR] = &gxbb_ddr.hw,
- [CLKID_DOS] = &gxbb_dos.hw,
- [CLKID_ISA] = &gxbb_isa.hw,
- [CLKID_PL301] = &gxbb_pl301.hw,
- [CLKID_PERIPHS] = &gxbb_periphs.hw,
- [CLKID_SPICC] = &gxbb_spicc.hw,
- [CLKID_I2C] = &gxbb_i2c.hw,
- [CLKID_SAR_ADC] = &gxbb_sar_adc.hw,
- [CLKID_SMART_CARD] = &gxbb_smart_card.hw,
- [CLKID_RNG0] = &gxbb_rng0.hw,
- [CLKID_UART0] = &gxbb_uart0.hw,
- [CLKID_SDHC] = &gxbb_sdhc.hw,
- [CLKID_STREAM] = &gxbb_stream.hw,
- [CLKID_ASYNC_FIFO] = &gxbb_async_fifo.hw,
- [CLKID_SDIO] = &gxbb_sdio.hw,
- [CLKID_ABUF] = &gxbb_abuf.hw,
- [CLKID_HIU_IFACE] = &gxbb_hiu_iface.hw,
- [CLKID_ASSIST_MISC] = &gxbb_assist_misc.hw,
- [CLKID_SPI] = &gxbb_spi.hw,
- [CLKID_I2S_SPDIF] = &gxbb_i2s_spdif.hw,
- [CLKID_ETH] = &gxbb_eth.hw,
- [CLKID_DEMUX] = &gxbb_demux.hw,
- [CLKID_AIU_GLUE] = &gxbb_aiu_glue.hw,
- [CLKID_IEC958] = &gxbb_iec958.hw,
- [CLKID_I2S_OUT] = &gxbb_i2s_out.hw,
- [CLKID_AMCLK] = &gxbb_amclk.hw,
- [CLKID_AIFIFO2] = &gxbb_aififo2.hw,
- [CLKID_MIXER] = &gxbb_mixer.hw,
- [CLKID_MIXER_IFACE] = &gxbb_mixer_iface.hw,
- [CLKID_ADC] = &gxbb_adc.hw,
- [CLKID_BLKMV] = &gxbb_blkmv.hw,
- [CLKID_AIU] = &gxbb_aiu.hw,
- [CLKID_UART1] = &gxbb_uart1.hw,
- [CLKID_G2D] = &gxbb_g2d.hw,
- [CLKID_USB0] = &gxbb_usb0.hw,
- [CLKID_USB1] = &gxbb_usb1.hw,
- [CLKID_RESET] = &gxbb_reset.hw,
- [CLKID_NAND] = &gxbb_nand.hw,
- [CLKID_DOS_PARSER] = &gxbb_dos_parser.hw,
- [CLKID_USB] = &gxbb_usb.hw,
- [CLKID_VDIN1] = &gxbb_vdin1.hw,
- [CLKID_AHB_ARB0] = &gxbb_ahb_arb0.hw,
- [CLKID_EFUSE] = &gxbb_efuse.hw,
- [CLKID_BOOT_ROM] = &gxbb_boot_rom.hw,
- [CLKID_AHB_DATA_BUS] = &gxbb_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &gxbb_ahb_ctrl_bus.hw,
- [CLKID_HDMI_INTR_SYNC] = &gxbb_hdmi_intr_sync.hw,
- [CLKID_HDMI_PCLK] = &gxbb_hdmi_pclk.hw,
- [CLKID_USB1_DDR_BRIDGE] = &gxbb_usb1_ddr_bridge.hw,
- [CLKID_USB0_DDR_BRIDGE] = &gxbb_usb0_ddr_bridge.hw,
- [CLKID_MMC_PCLK] = &gxbb_mmc_pclk.hw,
- [CLKID_DVIN] = &gxbb_dvin.hw,
- [CLKID_UART2] = &gxbb_uart2.hw,
- [CLKID_SANA] = &gxbb_sana.hw,
- [CLKID_VPU_INTR] = &gxbb_vpu_intr.hw,
- [CLKID_SEC_AHB_AHB3_BRIDGE] = &gxbb_sec_ahb_ahb3_bridge.hw,
- [CLKID_CLK81_A53] = &gxbb_clk81_a53.hw,
- [CLKID_VCLK2_VENCI0] = &gxbb_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &gxbb_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &gxbb_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &gxbb_vclk2_vencp1.hw,
- [CLKID_GCLK_VENCI_INT0] = &gxbb_gclk_venci_int0.hw,
- [CLKID_GCLK_VENCI_INT] = &gxbb_gclk_vencp_int.hw,
- [CLKID_DAC_CLK] = &gxbb_dac_clk.hw,
- [CLKID_AOCLK_GATE] = &gxbb_aoclk_gate.hw,
- [CLKID_IEC958_GATE] = &gxbb_iec958_gate.hw,
- [CLKID_ENC480P] = &gxbb_enc480p.hw,
- [CLKID_RNG1] = &gxbb_rng1.hw,
- [CLKID_GCLK_VENCI_INT1] = &gxbb_gclk_venci_int1.hw,
- [CLKID_VCLK2_VENCLMCC] = &gxbb_vclk2_venclmcc.hw,
- [CLKID_VCLK2_VENCL] = &gxbb_vclk2_vencl.hw,
- [CLKID_VCLK_OTHER] = &gxbb_vclk_other.hw,
- [CLKID_EDP] = &gxbb_edp.hw,
- [CLKID_AO_MEDIA_CPU] = &gxbb_ao_media_cpu.hw,
- [CLKID_AO_AHB_SRAM] = &gxbb_ao_ahb_sram.hw,
- [CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw,
- [CLKID_AO_IFACE] = &gxbb_ao_iface.hw,
- [CLKID_AO_I2C] = &gxbb_ao_i2c.hw,
- [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw,
- [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw,
- [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw,
- [CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw,
- [CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw,
- [CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw,
- [CLKID_MALI_0_SEL] = &gxbb_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &gxbb_mali_0_div.hw,
- [CLKID_MALI_0] = &gxbb_mali_0.hw,
- [CLKID_MALI_1_SEL] = &gxbb_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &gxbb_mali_1_div.hw,
- [CLKID_MALI_1] = &gxbb_mali_1.hw,
- [CLKID_MALI] = &gxbb_mali.hw,
- [CLKID_CTS_AMCLK] = &gxbb_cts_amclk.hw,
- [CLKID_CTS_AMCLK_SEL] = &gxbb_cts_amclk_sel.hw,
- [CLKID_CTS_AMCLK_DIV] = &gxbb_cts_amclk_div.hw,
- [CLKID_CTS_MCLK_I958] = &gxbb_cts_mclk_i958.hw,
- [CLKID_CTS_MCLK_I958_SEL] = &gxbb_cts_mclk_i958_sel.hw,
- [CLKID_CTS_MCLK_I958_DIV] = &gxbb_cts_mclk_i958_div.hw,
- [CLKID_CTS_I958] = &gxbb_cts_i958.hw,
- [CLKID_32K_CLK] = &gxbb_32k_clk.hw,
- [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw,
- [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw,
- [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw,
- [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw,
- [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw,
- [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw,
- [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw,
- [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw,
- [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw,
- [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw,
- [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw,
- [CLKID_VPU_0_SEL] = &gxbb_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &gxbb_vpu_0_div.hw,
- [CLKID_VPU_0] = &gxbb_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &gxbb_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &gxbb_vpu_1_div.hw,
- [CLKID_VPU_1] = &gxbb_vpu_1.hw,
- [CLKID_VPU] = &gxbb_vpu.hw,
- [CLKID_VAPB_0_SEL] = &gxbb_vapb_0_sel.hw,
- [CLKID_VAPB_0_DIV] = &gxbb_vapb_0_div.hw,
- [CLKID_VAPB_0] = &gxbb_vapb_0.hw,
- [CLKID_VAPB_1_SEL] = &gxbb_vapb_1_sel.hw,
- [CLKID_VAPB_1_DIV] = &gxbb_vapb_1_div.hw,
- [CLKID_VAPB_1] = &gxbb_vapb_1.hw,
- [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw,
- [CLKID_VAPB] = &gxbb_vapb.hw,
- [CLKID_MPLL0_DIV] = &gxl_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &gxbb_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &gxbb_mpll2_div.hw,
- [CLKID_MPLL_PREDIV] = &gxbb_mpll_prediv.hw,
- [CLKID_FCLK_DIV2_DIV] = &gxbb_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &gxbb_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &gxbb_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &gxbb_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &gxbb_fclk_div7_div.hw,
- [CLKID_VDEC_1_SEL] = &gxbb_vdec_1_sel.hw,
- [CLKID_VDEC_1_DIV] = &gxbb_vdec_1_div.hw,
- [CLKID_VDEC_1] = &gxbb_vdec_1.hw,
- [CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
- [CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
- [CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
- [CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
- [CLKID_FIXED_PLL_DCO] = &gxbb_fixed_pll_dco.hw,
- [CLKID_HDMI_PLL_DCO] = &gxl_hdmi_pll_dco.hw,
- [CLKID_HDMI_PLL_OD] = &gxl_hdmi_pll_od.hw,
- [CLKID_HDMI_PLL_OD2] = &gxl_hdmi_pll_od2.hw,
- [CLKID_SYS_PLL_DCO] = &gxbb_sys_pll_dco.hw,
- [CLKID_GP0_PLL_DCO] = &gxl_gp0_pll_dco.hw,
- [CLKID_VID_PLL_DIV] = &gxbb_vid_pll_div.hw,
- [CLKID_VID_PLL_SEL] = &gxbb_vid_pll_sel.hw,
- [CLKID_VID_PLL] = &gxbb_vid_pll.hw,
- [CLKID_VCLK_SEL] = &gxbb_vclk_sel.hw,
- [CLKID_VCLK2_SEL] = &gxbb_vclk2_sel.hw,
- [CLKID_VCLK_INPUT] = &gxbb_vclk_input.hw,
- [CLKID_VCLK2_INPUT] = &gxbb_vclk2_input.hw,
- [CLKID_VCLK_DIV] = &gxbb_vclk_div.hw,
- [CLKID_VCLK2_DIV] = &gxbb_vclk2_div.hw,
- [CLKID_VCLK] = &gxbb_vclk.hw,
- [CLKID_VCLK2] = &gxbb_vclk2.hw,
- [CLKID_VCLK_DIV1] = &gxbb_vclk_div1.hw,
- [CLKID_VCLK_DIV2_EN] = &gxbb_vclk_div2_en.hw,
- [CLKID_VCLK_DIV2] = &gxbb_vclk_div2.hw,
- [CLKID_VCLK_DIV4_EN] = &gxbb_vclk_div4_en.hw,
- [CLKID_VCLK_DIV4] = &gxbb_vclk_div4.hw,
- [CLKID_VCLK_DIV6_EN] = &gxbb_vclk_div6_en.hw,
- [CLKID_VCLK_DIV6] = &gxbb_vclk_div6.hw,
- [CLKID_VCLK_DIV12_EN] = &gxbb_vclk_div12_en.hw,
- [CLKID_VCLK_DIV12] = &gxbb_vclk_div12.hw,
- [CLKID_VCLK2_DIV1] = &gxbb_vclk2_div1.hw,
- [CLKID_VCLK2_DIV2_EN] = &gxbb_vclk2_div2_en.hw,
- [CLKID_VCLK2_DIV2] = &gxbb_vclk2_div2.hw,
- [CLKID_VCLK2_DIV4_EN] = &gxbb_vclk2_div4_en.hw,
- [CLKID_VCLK2_DIV4] = &gxbb_vclk2_div4.hw,
- [CLKID_VCLK2_DIV6_EN] = &gxbb_vclk2_div6_en.hw,
- [CLKID_VCLK2_DIV6] = &gxbb_vclk2_div6.hw,
- [CLKID_VCLK2_DIV12_EN] = &gxbb_vclk2_div12_en.hw,
- [CLKID_VCLK2_DIV12] = &gxbb_vclk2_div12.hw,
- [CLKID_CTS_ENCI_SEL] = &gxbb_cts_enci_sel.hw,
- [CLKID_CTS_ENCP_SEL] = &gxbb_cts_encp_sel.hw,
- [CLKID_CTS_VDAC_SEL] = &gxbb_cts_vdac_sel.hw,
- [CLKID_HDMI_TX_SEL] = &gxbb_hdmi_tx_sel.hw,
- [CLKID_CTS_ENCI] = &gxbb_cts_enci.hw,
- [CLKID_CTS_ENCP] = &gxbb_cts_encp.hw,
- [CLKID_CTS_VDAC] = &gxbb_cts_vdac.hw,
- [CLKID_HDMI_TX] = &gxbb_hdmi_tx.hw,
- [CLKID_HDMI_SEL] = &gxbb_hdmi_sel.hw,
- [CLKID_HDMI_DIV] = &gxbb_hdmi_div.hw,
- [CLKID_HDMI] = &gxbb_hdmi.hw,
- [CLKID_ACODEC] = &gxl_acodec.hw,
- [NR_CLKS] = NULL,
- },
- .num = NR_CLKS,
-};
-
-static struct clk_regmap *const gxbb_clk_regmaps[] = {
- &gxbb_clk81,
- &gxbb_ddr,
- &gxbb_dos,
- &gxbb_isa,
- &gxbb_pl301,
- &gxbb_periphs,
- &gxbb_spicc,
- &gxbb_i2c,
- &gxbb_sar_adc,
- &gxbb_smart_card,
- &gxbb_rng0,
- &gxbb_uart0,
- &gxbb_sdhc,
- &gxbb_stream,
- &gxbb_async_fifo,
- &gxbb_sdio,
- &gxbb_abuf,
- &gxbb_hiu_iface,
- &gxbb_assist_misc,
- &gxbb_spi,
- &gxbb_i2s_spdif,
- &gxbb_eth,
- &gxbb_demux,
- &gxbb_aiu_glue,
- &gxbb_iec958,
- &gxbb_i2s_out,
- &gxbb_amclk,
- &gxbb_aififo2,
- &gxbb_mixer,
- &gxbb_mixer_iface,
- &gxbb_adc,
- &gxbb_blkmv,
- &gxbb_aiu,
- &gxbb_uart1,
- &gxbb_g2d,
- &gxbb_usb0,
- &gxbb_usb1,
- &gxbb_reset,
- &gxbb_nand,
- &gxbb_dos_parser,
- &gxbb_usb,
- &gxbb_vdin1,
- &gxbb_ahb_arb0,
- &gxbb_efuse,
- &gxbb_boot_rom,
- &gxbb_ahb_data_bus,
- &gxbb_ahb_ctrl_bus,
- &gxbb_hdmi_intr_sync,
- &gxbb_hdmi_pclk,
- &gxbb_usb1_ddr_bridge,
- &gxbb_usb0_ddr_bridge,
- &gxbb_mmc_pclk,
- &gxbb_dvin,
- &gxbb_uart2,
- &gxbb_sana,
- &gxbb_vpu_intr,
- &gxbb_sec_ahb_ahb3_bridge,
- &gxbb_clk81_a53,
- &gxbb_vclk2_venci0,
- &gxbb_vclk2_venci1,
- &gxbb_vclk2_vencp0,
- &gxbb_vclk2_vencp1,
- &gxbb_gclk_venci_int0,
- &gxbb_gclk_vencp_int,
- &gxbb_dac_clk,
- &gxbb_aoclk_gate,
- &gxbb_iec958_gate,
- &gxbb_enc480p,
- &gxbb_rng1,
- &gxbb_gclk_venci_int1,
- &gxbb_vclk2_venclmcc,
- &gxbb_vclk2_vencl,
- &gxbb_vclk_other,
- &gxbb_edp,
- &gxbb_ao_media_cpu,
- &gxbb_ao_ahb_sram,
- &gxbb_ao_ahb_bus,
- &gxbb_ao_iface,
- &gxbb_ao_i2c,
- &gxbb_emmc_a,
- &gxbb_emmc_b,
- &gxbb_emmc_c,
- &gxbb_sar_adc_clk,
- &gxbb_mali_0,
- &gxbb_mali_1,
- &gxbb_cts_amclk,
- &gxbb_cts_mclk_i958,
- &gxbb_32k_clk,
- &gxbb_sd_emmc_a_clk0,
- &gxbb_sd_emmc_b_clk0,
- &gxbb_sd_emmc_c_clk0,
- &gxbb_vpu_0,
- &gxbb_vpu_1,
- &gxbb_vapb_0,
- &gxbb_vapb_1,
- &gxbb_vapb,
- &gxbb_mpeg_clk_div,
- &gxbb_sar_adc_clk_div,
- &gxbb_mali_0_div,
- &gxbb_mali_1_div,
- &gxbb_cts_mclk_i958_div,
- &gxbb_32k_clk_div,
- &gxbb_sd_emmc_a_clk0_div,
- &gxbb_sd_emmc_b_clk0_div,
- &gxbb_sd_emmc_c_clk0_div,
- &gxbb_vpu_0_div,
- &gxbb_vpu_1_div,
- &gxbb_vapb_0_div,
- &gxbb_vapb_1_div,
- &gxbb_mpeg_clk_sel,
- &gxbb_sar_adc_clk_sel,
- &gxbb_mali_0_sel,
- &gxbb_mali_1_sel,
- &gxbb_mali,
- &gxbb_cts_amclk_sel,
- &gxbb_cts_mclk_i958_sel,
- &gxbb_cts_i958,
- &gxbb_32k_clk_sel,
- &gxbb_sd_emmc_a_clk0_sel,
- &gxbb_sd_emmc_b_clk0_sel,
- &gxbb_sd_emmc_c_clk0_sel,
- &gxbb_vpu_0_sel,
- &gxbb_vpu_1_sel,
- &gxbb_vpu,
- &gxbb_vapb_0_sel,
- &gxbb_vapb_1_sel,
- &gxbb_vapb_sel,
- &gxbb_mpll0,
- &gxbb_mpll1,
- &gxbb_mpll2,
- &gxbb_mpll0_div,
- &gxbb_mpll1_div,
- &gxbb_mpll2_div,
- &gxbb_cts_amclk_div,
- &gxbb_fixed_pll,
- &gxbb_sys_pll,
- &gxbb_mpll_prediv,
- &gxbb_fclk_div2,
- &gxbb_fclk_div3,
- &gxbb_fclk_div4,
- &gxbb_fclk_div5,
- &gxbb_fclk_div7,
- &gxbb_vdec_1_sel,
- &gxbb_vdec_1_div,
- &gxbb_vdec_1,
- &gxbb_vdec_hevc_sel,
- &gxbb_vdec_hevc_div,
- &gxbb_vdec_hevc,
- &gxbb_gen_clk_sel,
- &gxbb_gen_clk_div,
- &gxbb_gen_clk,
- &gxbb_fixed_pll_dco,
- &gxbb_sys_pll_dco,
- &gxbb_gp0_pll,
- &gxbb_vid_pll,
- &gxbb_vid_pll_sel,
- &gxbb_vid_pll_div,
- &gxbb_vclk,
- &gxbb_vclk_sel,
- &gxbb_vclk_div,
- &gxbb_vclk_input,
- &gxbb_vclk_div1,
- &gxbb_vclk_div2_en,
- &gxbb_vclk_div4_en,
- &gxbb_vclk_div6_en,
- &gxbb_vclk_div12_en,
- &gxbb_vclk2,
- &gxbb_vclk2_sel,
- &gxbb_vclk2_div,
- &gxbb_vclk2_input,
- &gxbb_vclk2_div1,
- &gxbb_vclk2_div2_en,
- &gxbb_vclk2_div4_en,
- &gxbb_vclk2_div6_en,
- &gxbb_vclk2_div12_en,
- &gxbb_cts_enci,
- &gxbb_cts_enci_sel,
- &gxbb_cts_encp,
- &gxbb_cts_encp_sel,
- &gxbb_cts_vdac,
- &gxbb_cts_vdac_sel,
- &gxbb_hdmi_tx,
- &gxbb_hdmi_tx_sel,
- &gxbb_hdmi_sel,
- &gxbb_hdmi_div,
- &gxbb_hdmi,
- &gxbb_gp0_pll_dco,
- &gxbb_hdmi_pll,
- &gxbb_hdmi_pll_od,
- &gxbb_hdmi_pll_od2,
- &gxbb_hdmi_pll_dco,
-};
-
-static struct clk_regmap *const gxl_clk_regmaps[] = {
- &gxbb_clk81,
- &gxbb_ddr,
- &gxbb_dos,
- &gxbb_isa,
- &gxbb_pl301,
- &gxbb_periphs,
- &gxbb_spicc,
- &gxbb_i2c,
- &gxbb_sar_adc,
- &gxbb_smart_card,
- &gxbb_rng0,
- &gxbb_uart0,
- &gxbb_sdhc,
- &gxbb_stream,
- &gxbb_async_fifo,
- &gxbb_sdio,
- &gxbb_abuf,
- &gxbb_hiu_iface,
- &gxbb_assist_misc,
- &gxbb_spi,
- &gxbb_i2s_spdif,
- &gxbb_eth,
- &gxbb_demux,
- &gxbb_aiu_glue,
- &gxbb_iec958,
- &gxbb_i2s_out,
- &gxbb_amclk,
- &gxbb_aififo2,
- &gxbb_mixer,
- &gxbb_mixer_iface,
- &gxbb_adc,
- &gxbb_blkmv,
- &gxbb_aiu,
- &gxbb_uart1,
- &gxbb_g2d,
- &gxbb_usb0,
- &gxbb_usb1,
- &gxbb_reset,
- &gxbb_nand,
- &gxbb_dos_parser,
- &gxbb_usb,
- &gxbb_vdin1,
- &gxbb_ahb_arb0,
- &gxbb_efuse,
- &gxbb_boot_rom,
- &gxbb_ahb_data_bus,
- &gxbb_ahb_ctrl_bus,
- &gxbb_hdmi_intr_sync,
- &gxbb_hdmi_pclk,
- &gxbb_usb1_ddr_bridge,
- &gxbb_usb0_ddr_bridge,
- &gxbb_mmc_pclk,
- &gxbb_dvin,
- &gxbb_uart2,
- &gxbb_sana,
- &gxbb_vpu_intr,
- &gxbb_sec_ahb_ahb3_bridge,
- &gxbb_clk81_a53,
- &gxbb_vclk2_venci0,
- &gxbb_vclk2_venci1,
- &gxbb_vclk2_vencp0,
- &gxbb_vclk2_vencp1,
- &gxbb_gclk_venci_int0,
- &gxbb_gclk_vencp_int,
- &gxbb_dac_clk,
- &gxbb_aoclk_gate,
- &gxbb_iec958_gate,
- &gxbb_enc480p,
- &gxbb_rng1,
- &gxbb_gclk_venci_int1,
- &gxbb_vclk2_venclmcc,
- &gxbb_vclk2_vencl,
- &gxbb_vclk_other,
- &gxbb_edp,
- &gxbb_ao_media_cpu,
- &gxbb_ao_ahb_sram,
- &gxbb_ao_ahb_bus,
- &gxbb_ao_iface,
- &gxbb_ao_i2c,
- &gxbb_emmc_a,
- &gxbb_emmc_b,
- &gxbb_emmc_c,
- &gxbb_sar_adc_clk,
- &gxbb_mali_0,
- &gxbb_mali_1,
- &gxbb_cts_amclk,
- &gxbb_cts_mclk_i958,
- &gxbb_32k_clk,
- &gxbb_sd_emmc_a_clk0,
- &gxbb_sd_emmc_b_clk0,
- &gxbb_sd_emmc_c_clk0,
- &gxbb_vpu_0,
- &gxbb_vpu_1,
- &gxbb_vapb_0,
- &gxbb_vapb_1,
- &gxbb_vapb,
- &gxbb_mpeg_clk_div,
- &gxbb_sar_adc_clk_div,
- &gxbb_mali_0_div,
- &gxbb_mali_1_div,
- &gxbb_cts_mclk_i958_div,
- &gxbb_32k_clk_div,
- &gxbb_sd_emmc_a_clk0_div,
- &gxbb_sd_emmc_b_clk0_div,
- &gxbb_sd_emmc_c_clk0_div,
- &gxbb_vpu_0_div,
- &gxbb_vpu_1_div,
- &gxbb_vapb_0_div,
- &gxbb_vapb_1_div,
- &gxbb_mpeg_clk_sel,
- &gxbb_sar_adc_clk_sel,
- &gxbb_mali_0_sel,
- &gxbb_mali_1_sel,
- &gxbb_mali,
- &gxbb_cts_amclk_sel,
- &gxbb_cts_mclk_i958_sel,
- &gxbb_cts_i958,
- &gxbb_32k_clk_sel,
- &gxbb_sd_emmc_a_clk0_sel,
- &gxbb_sd_emmc_b_clk0_sel,
- &gxbb_sd_emmc_c_clk0_sel,
- &gxbb_vpu_0_sel,
- &gxbb_vpu_1_sel,
- &gxbb_vpu,
- &gxbb_vapb_0_sel,
- &gxbb_vapb_1_sel,
- &gxbb_vapb_sel,
- &gxbb_mpll0,
- &gxbb_mpll1,
- &gxbb_mpll2,
- &gxl_mpll0_div,
- &gxbb_mpll1_div,
- &gxbb_mpll2_div,
- &gxbb_cts_amclk_div,
- &gxbb_fixed_pll,
- &gxbb_sys_pll,
- &gxbb_mpll_prediv,
- &gxbb_fclk_div2,
- &gxbb_fclk_div3,
- &gxbb_fclk_div4,
- &gxbb_fclk_div5,
- &gxbb_fclk_div7,
- &gxbb_vdec_1_sel,
- &gxbb_vdec_1_div,
- &gxbb_vdec_1,
- &gxbb_vdec_hevc_sel,
- &gxbb_vdec_hevc_div,
- &gxbb_vdec_hevc,
- &gxbb_gen_clk_sel,
- &gxbb_gen_clk_div,
- &gxbb_gen_clk,
- &gxbb_fixed_pll_dco,
- &gxbb_sys_pll_dco,
- &gxbb_gp0_pll,
- &gxbb_vid_pll,
- &gxbb_vid_pll_sel,
- &gxbb_vid_pll_div,
- &gxbb_vclk,
- &gxbb_vclk_sel,
- &gxbb_vclk_div,
- &gxbb_vclk_input,
- &gxbb_vclk_div1,
- &gxbb_vclk_div2_en,
- &gxbb_vclk_div4_en,
- &gxbb_vclk_div6_en,
- &gxbb_vclk_div12_en,
- &gxbb_vclk2,
- &gxbb_vclk2_sel,
- &gxbb_vclk2_div,
- &gxbb_vclk2_input,
- &gxbb_vclk2_div1,
- &gxbb_vclk2_div2_en,
- &gxbb_vclk2_div4_en,
- &gxbb_vclk2_div6_en,
- &gxbb_vclk2_div12_en,
- &gxbb_cts_enci,
- &gxbb_cts_enci_sel,
- &gxbb_cts_encp,
- &gxbb_cts_encp_sel,
- &gxbb_cts_vdac,
- &gxbb_cts_vdac_sel,
- &gxbb_hdmi_tx,
- &gxbb_hdmi_tx_sel,
- &gxbb_hdmi_sel,
- &gxbb_hdmi_div,
- &gxbb_hdmi,
- &gxl_gp0_pll_dco,
- &gxl_hdmi_pll,
- &gxl_hdmi_pll_od,
- &gxl_hdmi_pll_od2,
- &gxl_hdmi_pll_dco,
- &gxl_acodec,
-};
-
-static const struct meson_eeclkc_data gxbb_clkc_data = {
- .regmap_clks = gxbb_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(gxbb_clk_regmaps),
- .hw_onecell_data = &gxbb_hw_onecell_data,
-};
-
-static const struct meson_eeclkc_data gxl_clkc_data = {
- .regmap_clks = gxl_clk_regmaps,
- .regmap_clk_num = ARRAY_SIZE(gxl_clk_regmaps),
- .hw_onecell_data = &gxl_hw_onecell_data,
-};
-
-static const struct of_device_id clkc_match_table[] = {
+static struct clk_hw *gxbb_hw_clks[] = {
+ [CLKID_SYS_PLL] = &gxbb_sys_pll.hw,
+ [CLKID_HDMI_PLL] = &gxbb_hdmi_pll.hw,
+ [CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &gxbb_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &gxbb_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &gxbb_fclk_div7.hw,
+ [CLKID_GP0_PLL] = &gxbb_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &gxbb_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &gxbb_clk81_div.hw,
+ [CLKID_CLK81] = &gxbb_clk81.hw,
+ [CLKID_MPLL0] = &gxbb_mpll0.hw,
+ [CLKID_MPLL1] = &gxbb_mpll1.hw,
+ [CLKID_MPLL2] = &gxbb_mpll2.hw,
+ [CLKID_DDR] = &gxbb_ddr.hw,
+ [CLKID_DOS] = &gxbb_dos.hw,
+ [CLKID_ISA] = &gxbb_isa.hw,
+ [CLKID_PL301] = &gxbb_pl301.hw,
+ [CLKID_PERIPHS] = &gxbb_periphs.hw,
+ [CLKID_SPICC] = &gxbb_spicc.hw,
+ [CLKID_I2C] = &gxbb_i2c.hw,
+ [CLKID_SAR_ADC] = &gxbb_sar_adc.hw,
+ [CLKID_SMART_CARD] = &gxbb_smart_card.hw,
+ [CLKID_RNG0] = &gxbb_rng0.hw,
+ [CLKID_UART0] = &gxbb_uart0.hw,
+ [CLKID_SDHC] = &gxbb_sdhc.hw,
+ [CLKID_STREAM] = &gxbb_stream.hw,
+ [CLKID_ASYNC_FIFO] = &gxbb_async_fifo.hw,
+ [CLKID_SDIO] = &gxbb_sdio.hw,
+ [CLKID_ABUF] = &gxbb_abuf.hw,
+ [CLKID_HIU_IFACE] = &gxbb_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &gxbb_assist_misc.hw,
+ [CLKID_SPI] = &gxbb_spi.hw,
+ [CLKID_I2S_SPDIF] = &gxbb_i2s_spdif.hw,
+ [CLKID_ETH] = &gxbb_eth.hw,
+ [CLKID_DEMUX] = &gxbb_demux.hw,
+ [CLKID_AIU_GLUE] = &gxbb_aiu_glue.hw,
+ [CLKID_IEC958] = &gxbb_iec958.hw,
+ [CLKID_I2S_OUT] = &gxbb_i2s_out.hw,
+ [CLKID_AMCLK] = &gxbb_amclk.hw,
+ [CLKID_AIFIFO2] = &gxbb_aififo2.hw,
+ [CLKID_MIXER] = &gxbb_mixer.hw,
+ [CLKID_MIXER_IFACE] = &gxbb_mixer_iface.hw,
+ [CLKID_ADC] = &gxbb_adc.hw,
+ [CLKID_BLKMV] = &gxbb_blkmv.hw,
+ [CLKID_AIU] = &gxbb_aiu.hw,
+ [CLKID_UART1] = &gxbb_uart1.hw,
+ [CLKID_G2D] = &gxbb_g2d.hw,
+ [CLKID_USB0] = &gxbb_usb0.hw,
+ [CLKID_USB1] = &gxbb_usb1.hw,
+ [CLKID_RESET] = &gxbb_reset.hw,
+ [CLKID_NAND] = &gxbb_nand.hw,
+ [CLKID_DOS_PARSER] = &gxbb_dos_parser.hw,
+ [CLKID_USB] = &gxbb_usb.hw,
+ [CLKID_VDIN1] = &gxbb_vdin1.hw,
+ [CLKID_AHB_ARB0] = &gxbb_ahb_arb0.hw,
+ [CLKID_EFUSE] = &gxbb_efuse.hw,
+ [CLKID_BOOT_ROM] = &gxbb_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &gxbb_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &gxbb_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &gxbb_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &gxbb_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &gxbb_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &gxbb_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &gxbb_mmc_pclk.hw,
+ [CLKID_DVIN] = &gxbb_dvin.hw,
+ [CLKID_UART2] = &gxbb_uart2.hw,
+ [CLKID_SANA] = &gxbb_sana.hw,
+ [CLKID_VPU_INTR] = &gxbb_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &gxbb_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A53] = &gxbb_clk81_a53.hw,
+ [CLKID_VCLK2_VENCI0] = &gxbb_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &gxbb_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &gxbb_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &gxbb_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT0] = &gxbb_gclk_venci_int0.hw,
+ [CLKID_GCLK_VENCI_INT] = &gxbb_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &gxbb_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &gxbb_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &gxbb_iec958_gate.hw,
+ [CLKID_ENC480P] = &gxbb_enc480p.hw,
+ [CLKID_RNG1] = &gxbb_rng1.hw,
+ [CLKID_GCLK_VENCI_INT1] = &gxbb_gclk_venci_int1.hw,
+ [CLKID_VCLK2_VENCLMCC] = &gxbb_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &gxbb_vclk2_vencl.hw,
+ [CLKID_VCLK_OTHER] = &gxbb_vclk_other.hw,
+ [CLKID_EDP] = &gxbb_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &gxbb_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &gxbb_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &gxbb_ao_iface.hw,
+ [CLKID_AO_I2C] = &gxbb_ao_i2c.hw,
+ [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw,
+ [CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw,
+ [CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw,
+ [CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw,
+ [CLKID_MALI_0_SEL] = &gxbb_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &gxbb_mali_0_div.hw,
+ [CLKID_MALI_0] = &gxbb_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &gxbb_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &gxbb_mali_1_div.hw,
+ [CLKID_MALI_1] = &gxbb_mali_1.hw,
+ [CLKID_MALI] = &gxbb_mali.hw,
+ [CLKID_CTS_AMCLK] = &gxbb_cts_amclk.hw,
+ [CLKID_CTS_AMCLK_SEL] = &gxbb_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &gxbb_cts_amclk_div.hw,
+ [CLKID_CTS_MCLK_I958] = &gxbb_cts_mclk_i958.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &gxbb_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &gxbb_cts_mclk_i958_div.hw,
+ [CLKID_CTS_I958] = &gxbb_cts_i958.hw,
+ [CLKID_32K_CLK] = &gxbb_32k_clk.hw,
+ [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw,
+ [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw,
+ [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw,
+ [CLKID_VPU_0_SEL] = &gxbb_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &gxbb_vpu_0_div.hw,
+ [CLKID_VPU_0] = &gxbb_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &gxbb_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &gxbb_vpu_1_div.hw,
+ [CLKID_VPU_1] = &gxbb_vpu_1.hw,
+ [CLKID_VPU] = &gxbb_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &gxbb_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &gxbb_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &gxbb_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &gxbb_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &gxbb_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &gxbb_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw,
+ [CLKID_VAPB] = &gxbb_vapb.hw,
+ [CLKID_HDMI_PLL_PRE_MULT] = &gxbb_hdmi_pll_pre_mult.hw,
+ [CLKID_MPLL0_DIV] = &gxbb_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &gxbb_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &gxbb_mpll2_div.hw,
+ [CLKID_MPLL_PREDIV] = &gxbb_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &gxbb_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &gxbb_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &gxbb_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &gxbb_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &gxbb_fclk_div7_div.hw,
+ [CLKID_VDEC_1_SEL] = &gxbb_vdec_1_sel.hw,
+ [CLKID_VDEC_1_DIV] = &gxbb_vdec_1_div.hw,
+ [CLKID_VDEC_1] = &gxbb_vdec_1.hw,
+ [CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
+ [CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
+ [CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
+ [CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
+ [CLKID_FIXED_PLL_DCO] = &gxbb_fixed_pll_dco.hw,
+ [CLKID_HDMI_PLL_DCO] = &gxbb_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &gxbb_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL_OD2] = &gxbb_hdmi_pll_od2.hw,
+ [CLKID_SYS_PLL_DCO] = &gxbb_sys_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &gxbb_gp0_pll_dco.hw,
+ [CLKID_VID_PLL_DIV] = &gxbb_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &gxbb_vid_pll_sel.hw,
+ [CLKID_VID_PLL] = &gxbb_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &gxbb_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &gxbb_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &gxbb_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &gxbb_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &gxbb_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &gxbb_vclk2_div.hw,
+ [CLKID_VCLK] = &gxbb_vclk.hw,
+ [CLKID_VCLK2] = &gxbb_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &gxbb_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &gxbb_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV2] = &gxbb_vclk_div2.hw,
+ [CLKID_VCLK_DIV4_EN] = &gxbb_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV4] = &gxbb_vclk_div4.hw,
+ [CLKID_VCLK_DIV6_EN] = &gxbb_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV6] = &gxbb_vclk_div6.hw,
+ [CLKID_VCLK_DIV12_EN] = &gxbb_vclk_div12_en.hw,
+ [CLKID_VCLK_DIV12] = &gxbb_vclk_div12.hw,
+ [CLKID_VCLK2_DIV1] = &gxbb_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &gxbb_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV2] = &gxbb_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4_EN] = &gxbb_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV4] = &gxbb_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6_EN] = &gxbb_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV6] = &gxbb_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12_EN] = &gxbb_vclk2_div12_en.hw,
+ [CLKID_VCLK2_DIV12] = &gxbb_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &gxbb_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &gxbb_cts_encp_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &gxbb_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &gxbb_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &gxbb_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &gxbb_cts_encp.hw,
+ [CLKID_CTS_VDAC] = &gxbb_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &gxbb_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &gxbb_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &gxbb_hdmi_div.hw,
+ [CLKID_HDMI] = &gxbb_hdmi.hw,
+};
+
+static struct clk_hw *gxl_hw_clks[] = {
+ [CLKID_SYS_PLL] = &gxbb_sys_pll.hw,
+ [CLKID_HDMI_PLL] = &gxl_hdmi_pll.hw,
+ [CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw,
+ [CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &gxbb_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &gxbb_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &gxbb_fclk_div7.hw,
+ [CLKID_GP0_PLL] = &gxbb_gp0_pll.hw,
+ [CLKID_MPEG_SEL] = &gxbb_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &gxbb_clk81_div.hw,
+ [CLKID_CLK81] = &gxbb_clk81.hw,
+ [CLKID_MPLL0] = &gxbb_mpll0.hw,
+ [CLKID_MPLL1] = &gxbb_mpll1.hw,
+ [CLKID_MPLL2] = &gxbb_mpll2.hw,
+ [CLKID_DDR] = &gxbb_ddr.hw,
+ [CLKID_DOS] = &gxbb_dos.hw,
+ [CLKID_ISA] = &gxbb_isa.hw,
+ [CLKID_PL301] = &gxbb_pl301.hw,
+ [CLKID_PERIPHS] = &gxbb_periphs.hw,
+ [CLKID_SPICC] = &gxbb_spicc.hw,
+ [CLKID_I2C] = &gxbb_i2c.hw,
+ [CLKID_SAR_ADC] = &gxbb_sar_adc.hw,
+ [CLKID_SMART_CARD] = &gxbb_smart_card.hw,
+ [CLKID_RNG0] = &gxbb_rng0.hw,
+ [CLKID_UART0] = &gxbb_uart0.hw,
+ [CLKID_SDHC] = &gxbb_sdhc.hw,
+ [CLKID_STREAM] = &gxbb_stream.hw,
+ [CLKID_ASYNC_FIFO] = &gxbb_async_fifo.hw,
+ [CLKID_SDIO] = &gxbb_sdio.hw,
+ [CLKID_ABUF] = &gxbb_abuf.hw,
+ [CLKID_HIU_IFACE] = &gxbb_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &gxbb_assist_misc.hw,
+ [CLKID_SPI] = &gxbb_spi.hw,
+ [CLKID_I2S_SPDIF] = &gxbb_i2s_spdif.hw,
+ [CLKID_ETH] = &gxbb_eth.hw,
+ [CLKID_DEMUX] = &gxbb_demux.hw,
+ [CLKID_AIU_GLUE] = &gxbb_aiu_glue.hw,
+ [CLKID_IEC958] = &gxbb_iec958.hw,
+ [CLKID_I2S_OUT] = &gxbb_i2s_out.hw,
+ [CLKID_AMCLK] = &gxbb_amclk.hw,
+ [CLKID_AIFIFO2] = &gxbb_aififo2.hw,
+ [CLKID_MIXER] = &gxbb_mixer.hw,
+ [CLKID_MIXER_IFACE] = &gxbb_mixer_iface.hw,
+ [CLKID_ADC] = &gxbb_adc.hw,
+ [CLKID_BLKMV] = &gxbb_blkmv.hw,
+ [CLKID_AIU] = &gxbb_aiu.hw,
+ [CLKID_UART1] = &gxbb_uart1.hw,
+ [CLKID_G2D] = &gxbb_g2d.hw,
+ [CLKID_USB0] = &gxbb_usb0.hw,
+ [CLKID_USB1] = &gxbb_usb1.hw,
+ [CLKID_RESET] = &gxbb_reset.hw,
+ [CLKID_NAND] = &gxbb_nand.hw,
+ [CLKID_DOS_PARSER] = &gxbb_dos_parser.hw,
+ [CLKID_USB] = &gxbb_usb.hw,
+ [CLKID_VDIN1] = &gxbb_vdin1.hw,
+ [CLKID_AHB_ARB0] = &gxbb_ahb_arb0.hw,
+ [CLKID_EFUSE] = &gxbb_efuse.hw,
+ [CLKID_BOOT_ROM] = &gxbb_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &gxbb_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &gxbb_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &gxbb_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &gxbb_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &gxbb_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &gxbb_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &gxbb_mmc_pclk.hw,
+ [CLKID_DVIN] = &gxbb_dvin.hw,
+ [CLKID_UART2] = &gxbb_uart2.hw,
+ [CLKID_SANA] = &gxbb_sana.hw,
+ [CLKID_VPU_INTR] = &gxbb_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &gxbb_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A53] = &gxbb_clk81_a53.hw,
+ [CLKID_VCLK2_VENCI0] = &gxbb_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &gxbb_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &gxbb_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &gxbb_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT0] = &gxbb_gclk_venci_int0.hw,
+ [CLKID_GCLK_VENCI_INT] = &gxbb_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &gxbb_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &gxbb_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &gxbb_iec958_gate.hw,
+ [CLKID_ENC480P] = &gxbb_enc480p.hw,
+ [CLKID_RNG1] = &gxbb_rng1.hw,
+ [CLKID_GCLK_VENCI_INT1] = &gxbb_gclk_venci_int1.hw,
+ [CLKID_VCLK2_VENCLMCC] = &gxbb_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &gxbb_vclk2_vencl.hw,
+ [CLKID_VCLK_OTHER] = &gxbb_vclk_other.hw,
+ [CLKID_EDP] = &gxbb_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &gxbb_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &gxbb_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &gxbb_ao_iface.hw,
+ [CLKID_AO_I2C] = &gxbb_ao_i2c.hw,
+ [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw,
+ [CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw,
+ [CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw,
+ [CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw,
+ [CLKID_MALI_0_SEL] = &gxbb_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &gxbb_mali_0_div.hw,
+ [CLKID_MALI_0] = &gxbb_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &gxbb_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &gxbb_mali_1_div.hw,
+ [CLKID_MALI_1] = &gxbb_mali_1.hw,
+ [CLKID_MALI] = &gxbb_mali.hw,
+ [CLKID_CTS_AMCLK] = &gxbb_cts_amclk.hw,
+ [CLKID_CTS_AMCLK_SEL] = &gxbb_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &gxbb_cts_amclk_div.hw,
+ [CLKID_CTS_MCLK_I958] = &gxbb_cts_mclk_i958.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &gxbb_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &gxbb_cts_mclk_i958_div.hw,
+ [CLKID_CTS_I958] = &gxbb_cts_i958.hw,
+ [CLKID_32K_CLK] = &gxbb_32k_clk.hw,
+ [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw,
+ [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw,
+ [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw,
+ [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw,
+ [CLKID_VPU_0_SEL] = &gxbb_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &gxbb_vpu_0_div.hw,
+ [CLKID_VPU_0] = &gxbb_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &gxbb_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &gxbb_vpu_1_div.hw,
+ [CLKID_VPU_1] = &gxbb_vpu_1.hw,
+ [CLKID_VPU] = &gxbb_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &gxbb_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &gxbb_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &gxbb_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &gxbb_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &gxbb_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &gxbb_vapb_1.hw,
+ [CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw,
+ [CLKID_VAPB] = &gxbb_vapb.hw,
+ [CLKID_MPLL0_DIV] = &gxl_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &gxbb_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &gxbb_mpll2_div.hw,
+ [CLKID_MPLL_PREDIV] = &gxbb_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &gxbb_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &gxbb_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &gxbb_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &gxbb_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &gxbb_fclk_div7_div.hw,
+ [CLKID_VDEC_1_SEL] = &gxbb_vdec_1_sel.hw,
+ [CLKID_VDEC_1_DIV] = &gxbb_vdec_1_div.hw,
+ [CLKID_VDEC_1] = &gxbb_vdec_1.hw,
+ [CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw,
+ [CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw,
+ [CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw,
+ [CLKID_GEN_CLK] = &gxbb_gen_clk.hw,
+ [CLKID_FIXED_PLL_DCO] = &gxbb_fixed_pll_dco.hw,
+ [CLKID_HDMI_PLL_DCO] = &gxl_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &gxl_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL_OD2] = &gxl_hdmi_pll_od2.hw,
+ [CLKID_SYS_PLL_DCO] = &gxbb_sys_pll_dco.hw,
+ [CLKID_GP0_PLL_DCO] = &gxl_gp0_pll_dco.hw,
+ [CLKID_VID_PLL_DIV] = &gxbb_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &gxbb_vid_pll_sel.hw,
+ [CLKID_VID_PLL] = &gxbb_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &gxbb_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &gxbb_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &gxbb_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &gxbb_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &gxbb_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &gxbb_vclk2_div.hw,
+ [CLKID_VCLK] = &gxbb_vclk.hw,
+ [CLKID_VCLK2] = &gxbb_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &gxbb_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &gxbb_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV2] = &gxbb_vclk_div2.hw,
+ [CLKID_VCLK_DIV4_EN] = &gxbb_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV4] = &gxbb_vclk_div4.hw,
+ [CLKID_VCLK_DIV6_EN] = &gxbb_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV6] = &gxbb_vclk_div6.hw,
+ [CLKID_VCLK_DIV12_EN] = &gxbb_vclk_div12_en.hw,
+ [CLKID_VCLK_DIV12] = &gxbb_vclk_div12.hw,
+ [CLKID_VCLK2_DIV1] = &gxbb_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &gxbb_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV2] = &gxbb_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4_EN] = &gxbb_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV4] = &gxbb_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6_EN] = &gxbb_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV6] = &gxbb_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12_EN] = &gxbb_vclk2_div12_en.hw,
+ [CLKID_VCLK2_DIV12] = &gxbb_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &gxbb_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &gxbb_cts_encp_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &gxbb_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &gxbb_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &gxbb_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &gxbb_cts_encp.hw,
+ [CLKID_CTS_VDAC] = &gxbb_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &gxbb_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &gxbb_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &gxbb_hdmi_div.hw,
+ [CLKID_HDMI] = &gxbb_hdmi.hw,
+ [CLKID_ACODEC] = &gxl_acodec.hw,
+};
+
+static const struct meson_clkc_data gxbb_clkc_data = {
+ .hw_clks = {
+ .hws = gxbb_hw_clks,
+ .num = ARRAY_SIZE(gxbb_hw_clks),
+ },
+};
+
+static const struct meson_clkc_data gxl_clkc_data = {
+ .hw_clks = {
+ .hws = gxl_hw_clks,
+ .num = ARRAY_SIZE(gxl_hw_clks),
+ },
+};
+
+static const struct of_device_id gxbb_clkc_match_table[] = {
{ .compatible = "amlogic,gxbb-clkc", .data = &gxbb_clkc_data },
{ .compatible = "amlogic,gxl-clkc", .data = &gxl_clkc_data },
{},
};
-MODULE_DEVICE_TABLE(of, clkc_match_table);
+MODULE_DEVICE_TABLE(of, gxbb_clkc_match_table);
-static struct platform_driver gxbb_driver = {
- .probe = meson_eeclkc_probe,
+static struct platform_driver gxbb_clkc_driver = {
+ .probe = meson_clkc_syscon_probe,
.driver = {
.name = "gxbb-clkc",
- .of_match_table = clkc_match_table,
+ .of_match_table = gxbb_clkc_match_table,
},
};
+module_platform_driver(gxbb_clkc_driver);
-module_platform_driver(gxbb_driver);
-MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Amlogic GXBB Main Clock Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h
deleted file mode 100644
index 1ee8cb7e2f5a..000000000000
--- a/drivers/clk/meson/gxbb.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
-/*
- * Copyright (c) 2016 AmLogic, Inc.
- * Author: Michael Turquette <mturquette@baylibre.com>
- */
-
-#ifndef __GXBB_H
-#define __GXBB_H
-
-/*
- * Clock controller register offsets
- *
- * Register offsets from the data sheet are listed in comment blocks below.
- * Those offsets must be multiplied by 4 before adding them to the base address
- * to get the right value
- */
-#define SCR 0x2C /* 0x0b offset in data sheet */
-#define TIMEOUT_VALUE 0x3c /* 0x0f offset in data sheet */
-
-#define HHI_GP0_PLL_CNTL 0x40 /* 0x10 offset in data sheet */
-#define HHI_GP0_PLL_CNTL2 0x44 /* 0x11 offset in data sheet */
-#define HHI_GP0_PLL_CNTL3 0x48 /* 0x12 offset in data sheet */
-#define HHI_GP0_PLL_CNTL4 0x4c /* 0x13 offset in data sheet */
-#define HHI_GP0_PLL_CNTL5 0x50 /* 0x14 offset in data sheet */
-#define HHI_GP0_PLL_CNTL1 0x58 /* 0x16 offset in data sheet */
-
-#define HHI_XTAL_DIVN_CNTL 0xbc /* 0x2f offset in data sheet */
-#define HHI_TIMER90K 0xec /* 0x3b offset in data sheet */
-
-#define HHI_MEM_PD_REG0 0x100 /* 0x40 offset in data sheet */
-#define HHI_MEM_PD_REG1 0x104 /* 0x41 offset in data sheet */
-#define HHI_VPU_MEM_PD_REG1 0x108 /* 0x42 offset in data sheet */
-#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */
-#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */
-
-#define HHI_GCLK_MPEG0 0x140 /* 0x50 offset in data sheet */
-#define HHI_GCLK_MPEG1 0x144 /* 0x51 offset in data sheet */
-#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */
-#define HHI_GCLK_OTHER 0x150 /* 0x54 offset in data sheet */
-#define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */
-#define HHI_SYS_OSCIN_CNTL 0x158 /* 0x56 offset in data sheet */
-#define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */
-#define HHI_SYS_CPU_RESET_CNTL 0x160 /* 0x58 offset in data sheet */
-#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */
-
-#define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */
-#define HHI_AUD_CLK_CNTL 0x178 /* 0x5e offset in data sheet */
-#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */
-#define HHI_AUD_CLK_CNTL2 0x190 /* 0x64 offset in data sheet */
-#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */
-#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */
-#define HHI_VID_PLL_CLK_DIV 0x1a0 /* 0x68 offset in data sheet */
-#define HHI_AUD_CLK_CNTL3 0x1a4 /* 0x69 offset in data sheet */
-#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */
-#define HHI_VPU_CLK_CNTL 0x1bC /* 0x6f offset in data sheet */
-
-#define HHI_HDMI_CLK_CNTL 0x1CC /* 0x73 offset in data sheet */
-#define HHI_VDEC_CLK_CNTL 0x1E0 /* 0x78 offset in data sheet */
-#define HHI_VDEC2_CLK_CNTL 0x1E4 /* 0x79 offset in data sheet */
-#define HHI_VDEC3_CLK_CNTL 0x1E8 /* 0x7a offset in data sheet */
-#define HHI_VDEC4_CLK_CNTL 0x1EC /* 0x7b offset in data sheet */
-#define HHI_HDCP22_CLK_CNTL 0x1F0 /* 0x7c offset in data sheet */
-#define HHI_VAPBCLK_CNTL 0x1F4 /* 0x7d offset in data sheet */
-
-#define HHI_VPU_CLKB_CNTL 0x20C /* 0x83 offset in data sheet */
-#define HHI_USB_CLK_CNTL 0x220 /* 0x88 offset in data sheet */
-#define HHI_32K_CLK_CNTL 0x224 /* 0x89 offset in data sheet */
-#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */
-
-#define HHI_PCM_CLK_CNTL 0x258 /* 0x96 offset in data sheet */
-#define HHI_NAND_CLK_CNTL 0x25C /* 0x97 offset in data sheet */
-#define HHI_SD_EMMC_CLK_CNTL 0x264 /* 0x99 offset in data sheet */
-
-#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */
-#define HHI_MPLL_CNTL2 0x284 /* 0xa1 offset in data sheet */
-#define HHI_MPLL_CNTL3 0x288 /* 0xa2 offset in data sheet */
-#define HHI_MPLL_CNTL4 0x28C /* 0xa3 offset in data sheet */
-#define HHI_MPLL_CNTL5 0x290 /* 0xa4 offset in data sheet */
-#define HHI_MPLL_CNTL6 0x294 /* 0xa5 offset in data sheet */
-#define HHI_MPLL_CNTL7 0x298 /* MP0, 0xa6 offset in data sheet */
-#define HHI_MPLL_CNTL8 0x29C /* MP1, 0xa7 offset in data sheet */
-#define HHI_MPLL_CNTL9 0x2A0 /* MP2, 0xa8 offset in data sheet */
-#define HHI_MPLL_CNTL10 0x2A4 /* MP2, 0xa9 offset in data sheet */
-
-#define HHI_MPLL3_CNTL0 0x2E0 /* 0xb8 offset in data sheet */
-#define HHI_MPLL3_CNTL1 0x2E4 /* 0xb9 offset in data sheet */
-#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
-#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
-
-#define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */
-#define HHI_SYS_PLL_CNTL2 0x304 /* 0xc1 offset in data sheet */
-#define HHI_SYS_PLL_CNTL3 0x308 /* 0xc2 offset in data sheet */
-#define HHI_SYS_PLL_CNTL4 0x30c /* 0xc3 offset in data sheet */
-#define HHI_SYS_PLL_CNTL5 0x310 /* 0xc4 offset in data sheet */
-#define HHI_DPLL_TOP_I 0x318 /* 0xc6 offset in data sheet */
-#define HHI_DPLL_TOP2_I 0x31C /* 0xc7 offset in data sheet */
-#define HHI_HDMI_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */
-#define HHI_HDMI_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */
-#define HHI_HDMI_PLL_CNTL3 0x328 /* 0xca offset in data sheet */
-#define HHI_HDMI_PLL_CNTL4 0x32C /* 0xcb offset in data sheet */
-#define HHI_HDMI_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */
-#define HHI_HDMI_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */
-#define HHI_HDMI_PLL_CNTL_I 0x338 /* 0xce offset in data sheet */
-#define HHI_HDMI_PLL_CNTL7 0x33C /* 0xcf offset in data sheet */
-
-#define HHI_HDMI_PHY_CNTL0 0x3A0 /* 0xe8 offset in data sheet */
-#define HHI_HDMI_PHY_CNTL1 0x3A4 /* 0xe9 offset in data sheet */
-#define HHI_HDMI_PHY_CNTL2 0x3A8 /* 0xea offset in data sheet */
-#define HHI_HDMI_PHY_CNTL3 0x3AC /* 0xeb offset in data sheet */
-
-#define HHI_VID_LOCK_CLK_CNTL 0x3C8 /* 0xf2 offset in data sheet */
-#define HHI_BT656_CLK_CNTL 0x3D4 /* 0xf5 offset in data sheet */
-#define HHI_SAR_CLK_CNTL 0x3D8 /* 0xf6 offset in data sheet */
-
-/*
- * CLKID index values
- *
- * These indices are entirely contrived and do not map onto the hardware.
- * It has now been decided to expose everything by default in the DT header:
- * include/dt-bindings/clock/gxbb-clkc.h. Only the clocks ids we don't want
- * to expose, such as the internal muxes and dividers of composite clocks,
- * will remain defined here.
- */
-/* ID 1 is unused (it was used by the non-existing CLKID_CPUCLK before) */
-#define CLKID_MPEG_SEL 10
-#define CLKID_MPEG_DIV 11
-#define CLKID_SAR_ADC_DIV 99
-#define CLKID_MALI_0_DIV 101
-#define CLKID_MALI_1_DIV 104
-#define CLKID_CTS_AMCLK_SEL 108
-#define CLKID_CTS_AMCLK_DIV 109
-#define CLKID_CTS_MCLK_I958_SEL 111
-#define CLKID_CTS_MCLK_I958_DIV 112
-#define CLKID_32K_CLK_SEL 115
-#define CLKID_32K_CLK_DIV 116
-#define CLKID_SD_EMMC_A_CLK0_SEL 117
-#define CLKID_SD_EMMC_A_CLK0_DIV 118
-#define CLKID_SD_EMMC_B_CLK0_SEL 120
-#define CLKID_SD_EMMC_B_CLK0_DIV 121
-#define CLKID_SD_EMMC_C_CLK0_SEL 123
-#define CLKID_SD_EMMC_C_CLK0_DIV 124
-#define CLKID_VPU_0_DIV 127
-#define CLKID_VPU_1_DIV 130
-#define CLKID_VAPB_0_DIV 134
-#define CLKID_VAPB_1_DIV 137
-#define CLKID_HDMI_PLL_PRE_MULT 141
-#define CLKID_MPLL0_DIV 142
-#define CLKID_MPLL1_DIV 143
-#define CLKID_MPLL2_DIV 144
-#define CLKID_MPLL_PREDIV 145
-#define CLKID_FCLK_DIV2_DIV 146
-#define CLKID_FCLK_DIV3_DIV 147
-#define CLKID_FCLK_DIV4_DIV 148
-#define CLKID_FCLK_DIV5_DIV 149
-#define CLKID_FCLK_DIV7_DIV 150
-#define CLKID_VDEC_1_SEL 151
-#define CLKID_VDEC_1_DIV 152
-#define CLKID_VDEC_HEVC_SEL 154
-#define CLKID_VDEC_HEVC_DIV 155
-#define CLKID_GEN_CLK_SEL 157
-#define CLKID_GEN_CLK_DIV 158
-#define CLKID_FIXED_PLL_DCO 160
-#define CLKID_HDMI_PLL_DCO 161
-#define CLKID_HDMI_PLL_OD 162
-#define CLKID_HDMI_PLL_OD2 163
-#define CLKID_SYS_PLL_DCO 164
-#define CLKID_GP0_PLL_DCO 165
-#define CLKID_VID_PLL_SEL 167
-#define CLKID_VID_PLL_DIV 168
-#define CLKID_VCLK_SEL 169
-#define CLKID_VCLK2_SEL 170
-#define CLKID_VCLK_INPUT 171
-#define CLKID_VCLK2_INPUT 172
-#define CLKID_VCLK_DIV 173
-#define CLKID_VCLK2_DIV 174
-#define CLKID_VCLK_DIV2_EN 177
-#define CLKID_VCLK_DIV4_EN 178
-#define CLKID_VCLK_DIV6_EN 179
-#define CLKID_VCLK_DIV12_EN 180
-#define CLKID_VCLK2_DIV2_EN 181
-#define CLKID_VCLK2_DIV4_EN 182
-#define CLKID_VCLK2_DIV6_EN 183
-#define CLKID_VCLK2_DIV12_EN 184
-#define CLKID_CTS_ENCI_SEL 195
-#define CLKID_CTS_ENCP_SEL 196
-#define CLKID_CTS_VDAC_SEL 197
-#define CLKID_HDMI_TX_SEL 198
-#define CLKID_HDMI_SEL 203
-#define CLKID_HDMI_DIV 204
-
-#define NR_CLKS 207
-
-/* include the CLKIDs that have been made part of the DT binding */
-#include <dt-bindings/clock/gxbb-clkc.h>
-
-#endif /* __GXBB_H */
diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c
index 27cd2c1f3f61..8f6bdea18119 100644
--- a/drivers/clk/meson/meson-aoclk.c
+++ b/drivers/clk/meson/meson-aoclk.c
@@ -13,11 +13,12 @@
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/mfd/syscon.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "meson-aoclk.h"
+#include "clk-regmap.h"
static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev,
unsigned long id)
@@ -36,20 +37,31 @@ static const struct reset_control_ops meson_aoclk_reset_ops = {
int meson_aoclkc_probe(struct platform_device *pdev)
{
struct meson_aoclk_reset_controller *rstc;
- struct meson_aoclk_data *data;
+ const struct meson_clkc_data *clkc_data;
+ const struct meson_aoclk_data *data;
struct device *dev = &pdev->dev;
+ struct device_node *np;
struct regmap *regmap;
- int ret, clkid;
+ int ret;
- data = (struct meson_aoclk_data *) of_device_get_match_data(dev);
- if (!data)
- return -ENODEV;
+ clkc_data = of_device_get_match_data(dev);
+ if (!clkc_data)
+ return -EINVAL;
+
+ ret = meson_clkc_syscon_probe(pdev);
+ if (ret)
+ return ret;
+
+ data = container_of(clkc_data, struct meson_aoclk_data,
+ clkc_data);
rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL);
if (!rstc)
return -ENOMEM;
- regmap = syscon_node_to_regmap(of_get_parent(dev->of_node));
+ np = of_get_parent(dev->of_node);
+ regmap = syscon_node_to_regmap(np);
+ of_node_put(np);
if (IS_ERR(regmap)) {
dev_err(dev, "failed to get regmap\n");
return PTR_ERR(regmap);
@@ -67,24 +79,10 @@ int meson_aoclkc_probe(struct platform_device *pdev)
return ret;
}
- /* Populate regmap */
- for (clkid = 0; clkid < data->num_clks; clkid++)
- data->clks[clkid]->map = regmap;
-
- /* Register all clks */
- for (clkid = 0; clkid < data->hw_data->num; clkid++) {
- if (!data->hw_data->hws[clkid])
- continue;
-
- ret = devm_clk_hw_register(dev, data->hw_data->hws[clkid]);
- if (ret) {
- dev_err(dev, "Clock registration failed\n");
- return ret;
- }
- }
-
- return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- (void *) data->hw_data);
+ return 0;
}
-EXPORT_SYMBOL_GPL(meson_aoclkc_probe);
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_NS_GPL(meson_aoclkc_probe, "CLK_MESON");
+
+MODULE_DESCRIPTION("Amlogic Always-ON Clock Controller helpers");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/meson-aoclk.h b/drivers/clk/meson/meson-aoclk.h
index 605b43855a69..2c83e73d3a77 100644
--- a/drivers/clk/meson/meson-aoclk.h
+++ b/drivers/clk/meson/meson-aoclk.h
@@ -17,14 +17,13 @@
#include <linux/reset-controller.h>
#include "clk-regmap.h"
+#include "meson-clkc-utils.h"
struct meson_aoclk_data {
+ const struct meson_clkc_data clkc_data;
const unsigned int reset_reg;
const int num_reset;
const unsigned int *reset;
- const int num_clks;
- struct clk_regmap **clks;
- const struct clk_hw_onecell_data *hw_data;
};
struct meson_aoclk_reset_controller {
diff --git a/drivers/clk/meson/meson-clkc-utils.c b/drivers/clk/meson/meson-clkc-utils.c
new file mode 100644
index 000000000000..870f50548e26
--- /dev/null
+++ b/drivers/clk/meson/meson-clkc-utils.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "meson-clkc-utils.h"
+
+struct clk_hw *meson_clk_hw_get(struct of_phandle_args *clkspec, void *clk_hw_data)
+{
+ const struct meson_clk_hw_data *data = clk_hw_data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= data->num) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return data->hws[idx];
+}
+EXPORT_SYMBOL_NS_GPL(meson_clk_hw_get, "CLK_MESON");
+
+static int meson_clkc_init(struct device *dev, struct regmap *map)
+{
+ const struct meson_clkc_data *data;
+ struct clk_hw *hw;
+ int ret, i;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ if (data->init_count)
+ regmap_multi_reg_write(map, data->init_regs, data->init_count);
+
+ for (i = 0; i < data->hw_clks.num; i++) {
+ hw = data->hw_clks.hws[i];
+
+ /* array might be sparse */
+ if (!hw)
+ continue;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret) {
+ dev_err(dev, "registering %s clock failed\n",
+ hw->init->name);
+ return ret;
+ }
+ }
+
+ return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get, (void *)&data->hw_clks);
+}
+
+int meson_clkc_syscon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np;
+ struct regmap *map;
+
+ np = of_get_parent(dev->of_node);
+ map = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to get parent syscon regmap\n");
+ return PTR_ERR(map);
+ }
+
+ return meson_clkc_init(dev, map);
+}
+EXPORT_SYMBOL_NS_GPL(meson_clkc_syscon_probe, "CLK_MESON");
+
+int meson_clkc_mmio_probe(struct platform_device *pdev)
+{
+ const struct meson_clkc_data *data;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *base;
+ struct regmap *map;
+ struct regmap_config regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap_cfg.max_register = resource_size(res) - regmap_cfg.reg_stride;
+
+ map = devm_regmap_init_mmio(dev, base, &regmap_cfg);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ return meson_clkc_init(dev, map);
+}
+EXPORT_SYMBOL_NS_GPL(meson_clkc_mmio_probe, "CLK_MESON");
+
+MODULE_DESCRIPTION("Amlogic Clock Controller Utilities");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/meson-clkc-utils.h b/drivers/clk/meson/meson-clkc-utils.h
new file mode 100644
index 000000000000..ddadf14b4923
--- /dev/null
+++ b/drivers/clk/meson/meson-clkc-utils.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org>
+ */
+
+#ifndef __MESON_CLKC_UTILS_H__
+#define __MESON_CLKC_UTILS_H__
+
+#include <linux/of_device.h>
+#include <linux/clk-provider.h>
+
+struct platform_device;
+
+struct meson_clk_hw_data {
+ struct clk_hw **hws;
+ unsigned int num;
+};
+
+struct clk_hw *meson_clk_hw_get(struct of_phandle_args *clkspec, void *clk_hw_data);
+
+struct meson_clkc_data {
+ const struct reg_sequence *init_regs;
+ unsigned int init_count;
+ struct meson_clk_hw_data hw_clks;
+};
+
+int meson_clkc_syscon_probe(struct platform_device *pdev);
+int meson_clkc_mmio_probe(struct platform_device *pdev);
+
+#define __MESON_PCLK(_name, _reg, _bit, _ops, _pdata, _flags) \
+struct clk_regmap _name = { \
+ .data = &(struct clk_regmap_gate_data) { \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = _ops, \
+ .parent_data = (_pdata), \
+ .num_parents = 1, \
+ .flags = (_flags), \
+ }, \
+}
+
+#define MESON_PCLK(_name, _reg, _bit, _pdata, _flags) \
+ __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ops, _pdata, _flags)
+
+#define MESON_PCLK_RO(_name, _reg, _bit, _pdata, _flags) \
+ __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ro_ops, _pdata, _flags)
+
+/* Helpers for the usual sel/div/gate composite clocks */
+#define MESON_COMP_SEL(_prefix, _name, _reg, _shift, _mask, _pdata, \
+ _table, _dflags, _iflags) \
+struct clk_regmap _prefix##_name##_sel = { \
+ .data = &(struct clk_regmap_mux_data) { \
+ .offset = (_reg), \
+ .mask = (_mask), \
+ .shift = (_shift), \
+ .flags = (_dflags), \
+ .table = (_table), \
+ }, \
+ .hw.init = &(struct clk_init_data){ \
+ .name = #_name "_sel", \
+ .ops = &clk_regmap_mux_ops, \
+ .parent_data = _pdata, \
+ .num_parents = ARRAY_SIZE(_pdata), \
+ .flags = (_iflags), \
+ }, \
+}
+
+#define MESON_COMP_DIV(_prefix, _name, _reg, _shift, _width, \
+ _dflags, _iflags) \
+struct clk_regmap _prefix##_name##_div = { \
+ .data = &(struct clk_regmap_div_data) { \
+ .offset = (_reg), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .flags = (_dflags), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name "_div", \
+ .ops = &clk_regmap_divider_ops, \
+ .parent_hws = (const struct clk_hw *[]) { \
+ &_prefix##_name##_sel.hw \
+ }, \
+ .num_parents = 1, \
+ .flags = (_iflags), \
+ }, \
+}
+
+#define MESON_COMP_GATE(_prefix, _name, _reg, _bit, _iflags) \
+struct clk_regmap _prefix##_name = { \
+ .data = &(struct clk_regmap_gate_data) { \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &clk_regmap_gate_ops, \
+ .parent_hws = (const struct clk_hw *[]) { \
+ &_prefix##_name##_div.hw \
+ }, \
+ .num_parents = 1, \
+ .flags = (_iflags), \
+ }, \
+}
+
+#endif
diff --git a/drivers/clk/meson/meson-eeclk.c b/drivers/clk/meson/meson-eeclk.c
deleted file mode 100644
index 8d5a5dab955a..000000000000
--- a/drivers/clk/meson/meson-eeclk.c
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2019 BayLibre, SAS.
- * Author: Jerome Brunet <jbrunet@baylibre.com>
- */
-
-#include <linux/clk-provider.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
-#include <linux/module.h>
-
-#include "clk-regmap.h"
-#include "meson-eeclk.h"
-
-int meson_eeclkc_probe(struct platform_device *pdev)
-{
- const struct meson_eeclkc_data *data;
- struct device *dev = &pdev->dev;
- struct regmap *map;
- int ret, i;
-
- data = of_device_get_match_data(dev);
- if (!data)
- return -EINVAL;
-
- /* Get the hhi system controller node */
- map = syscon_node_to_regmap(of_get_parent(dev->of_node));
- if (IS_ERR(map)) {
- dev_err(dev,
- "failed to get HHI regmap\n");
- return PTR_ERR(map);
- }
-
- if (data->init_count)
- regmap_multi_reg_write(map, data->init_regs, data->init_count);
-
- /* Populate regmap for the regmap backed clocks */
- for (i = 0; i < data->regmap_clk_num; i++)
- data->regmap_clks[i]->map = map;
-
- for (i = 0; i < data->hw_onecell_data->num; i++) {
- /* array might be sparse */
- if (!data->hw_onecell_data->hws[i])
- continue;
-
- ret = devm_clk_hw_register(dev, data->hw_onecell_data->hws[i]);
- if (ret) {
- dev_err(dev, "Clock registration failed\n");
- return ret;
- }
- }
-
- return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- data->hw_onecell_data);
-}
-EXPORT_SYMBOL_GPL(meson_eeclkc_probe);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/meson-eeclk.h b/drivers/clk/meson/meson-eeclk.h
deleted file mode 100644
index 77316207bde1..000000000000
--- a/drivers/clk/meson/meson-eeclk.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2019 BayLibre, SAS.
- * Author: Jerome Brunet <jbrunet@baylibre.com>
- */
-
-#ifndef __MESON_CLKC_H
-#define __MESON_CLKC_H
-
-#include <linux/clk-provider.h>
-#include "clk-regmap.h"
-
-struct platform_device;
-
-struct meson_eeclkc_data {
- struct clk_regmap *const *regmap_clks;
- unsigned int regmap_clk_num;
- const struct reg_sequence *init_regs;
- unsigned int init_count;
- struct clk_hw_onecell_data *hw_onecell_data;
-};
-
-int meson_eeclkc_probe(struct platform_device *pdev);
-
-#endif /* __MESON_CLKC_H */
diff --git a/drivers/clk/meson/meson8-ddr.c b/drivers/clk/meson/meson8-ddr.c
index 4b73ea244b63..0f93774f7371 100644
--- a/drivers/clk/meson/meson8-ddr.c
+++ b/drivers/clk/meson/meson8-ddr.c
@@ -12,6 +12,7 @@
#include "clk-regmap.h"
#include "clk-pll.h"
+#include "meson-clkc-utils.h"
#define AM_DDR_PLL_CNTL 0x00
#define AM_DDR_PLL_CNTL1 0x04
@@ -77,69 +78,31 @@ static struct clk_regmap meson8_ddr_pll = {
},
};
-static struct clk_hw_onecell_data meson8_ddr_clk_hw_onecell_data = {
- .hws = {
- [DDR_CLKID_DDR_PLL_DCO] = &meson8_ddr_pll_dco.hw,
- [DDR_CLKID_DDR_PLL] = &meson8_ddr_pll.hw,
- },
- .num = 2,
-};
-
-static struct clk_regmap *const meson8_ddr_clk_regmaps[] = {
- &meson8_ddr_pll_dco,
- &meson8_ddr_pll,
+static struct clk_hw *meson8_ddr_hw_clks[] = {
+ [DDR_CLKID_DDR_PLL_DCO] = &meson8_ddr_pll_dco.hw,
+ [DDR_CLKID_DDR_PLL] = &meson8_ddr_pll.hw,
};
-static const struct regmap_config meson8_ddr_clkc_regmap_config = {
- .reg_bits = 8,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = DDR_CLK_STS,
+static const struct meson_clkc_data meson8_ddr_clkc_data = {
+ .hw_clks = {
+ .hws = meson8_ddr_hw_clks,
+ .num = ARRAY_SIZE(meson8_ddr_hw_clks),
+ },
};
-static int meson8_ddr_clkc_probe(struct platform_device *pdev)
-{
- struct regmap *regmap;
- void __iomem *base;
- struct clk_hw *hw;
- int ret, i;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &meson8_ddr_clkc_regmap_config);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- /* Populate regmap */
- for (i = 0; i < ARRAY_SIZE(meson8_ddr_clk_regmaps); i++)
- meson8_ddr_clk_regmaps[i]->map = regmap;
-
- /* Register all clks */
- for (i = 0; i < meson8_ddr_clk_hw_onecell_data.num; i++) {
- hw = meson8_ddr_clk_hw_onecell_data.hws[i];
-
- ret = devm_clk_hw_register(&pdev->dev, hw);
- if (ret) {
- dev_err(&pdev->dev, "Clock registration failed\n");
- return ret;
- }
- }
-
- return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
- &meson8_ddr_clk_hw_onecell_data);
-}
-
static const struct of_device_id meson8_ddr_clkc_match_table[] = {
- { .compatible = "amlogic,meson8-ddr-clkc" },
- { .compatible = "amlogic,meson8b-ddr-clkc" },
+ {
+ .compatible = "amlogic,meson8-ddr-clkc",
+ .data = &meson8_ddr_clkc_data,
+ }, {
+ .compatible = "amlogic,meson8b-ddr-clkc",
+ .data = &meson8_ddr_clkc_data,
+ },
{ /* sentinel */ }
};
static struct platform_driver meson8_ddr_clkc_driver = {
- .probe = meson8_ddr_clkc_probe,
+ .probe = meson_clkc_mmio_probe,
.driver = {
.name = "meson8-ddr-clkc",
.of_match_table = meson8_ddr_clkc_match_table,
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 8f3b7a94a667..95d0b9cbd904 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -16,12 +16,79 @@
#include <linux/slab.h>
#include <linux/regmap.h>
-#include "meson8b.h"
#include "clk-regmap.h"
+#include "meson-clkc-utils.h"
#include "clk-pll.h"
#include "clk-mpll.h"
-static DEFINE_SPINLOCK(meson_clk_lock);
+#include <dt-bindings/clock/meson8b-clkc.h>
+#include <dt-bindings/reset/amlogic,meson8b-clkc-reset.h>
+
+/*
+ * Clock controller register offsets
+ *
+ * Register offsets from the HardKernel[0] data sheet must be multiplied
+ * by 4 before adding them to the base address to get the right value
+ *
+ * [0] https://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf
+ */
+#define HHI_GP_PLL_CNTL 0x40
+#define HHI_GP_PLL_CNTL2 0x44
+#define HHI_GP_PLL_CNTL3 0x48
+#define HHI_GP_PLL_CNTL4 0x4C
+#define HHI_GP_PLL_CNTL5 0x50
+#define HHI_VIID_CLK_DIV 0x128
+#define HHI_VIID_CLK_CNTL 0x12c
+#define HHI_GCLK_MPEG0 0x140
+#define HHI_GCLK_MPEG1 0x144
+#define HHI_GCLK_MPEG2 0x148
+#define HHI_GCLK_OTHER 0x150
+#define HHI_GCLK_AO 0x154
+#define HHI_SYS_CPU_CLK_CNTL1 0x15c
+#define HHI_VID_CLK_DIV 0x164
+#define HHI_MPEG_CLK_CNTL 0x174
+#define HHI_AUD_CLK_CNTL 0x178
+#define HHI_VID_CLK_CNTL 0x17c
+#define HHI_AUD_CLK_CNTL2 0x190
+#define HHI_VID_CLK_CNTL2 0x194
+#define HHI_VID_DIVIDER_CNTL 0x198
+#define HHI_SYS_CPU_CLK_CNTL0 0x19c
+#define HHI_MALI_CLK_CNTL 0x1b0
+#define HHI_VPU_CLK_CNTL 0x1bc
+#define HHI_HDMI_CLK_CNTL 0x1cc
+#define HHI_VDEC_CLK_CNTL 0x1e0
+#define HHI_VDEC2_CLK_CNTL 0x1e4
+#define HHI_VDEC3_CLK_CNTL 0x1e8
+#define HHI_NAND_CLK_CNTL 0x25c
+#define HHI_MPLL_CNTL 0x280
+#define HHI_SYS_PLL_CNTL 0x300
+#define HHI_VID_PLL_CNTL 0x320
+#define HHI_VID_PLL_CNTL2 0x324
+#define HHI_VID_PLL_CNTL3 0x328
+#define HHI_VID_PLL_CNTL4 0x32c
+#define HHI_VID_PLL_CNTL5 0x330
+#define HHI_VID_PLL_CNTL6 0x334
+#define HHI_VID2_PLL_CNTL 0x380
+#define HHI_VID2_PLL_CNTL2 0x384
+#define HHI_VID2_PLL_CNTL3 0x388
+#define HHI_VID2_PLL_CNTL4 0x38c
+#define HHI_VID2_PLL_CNTL5 0x390
+#define HHI_VID2_PLL_CNTL6 0x394
+
+/*
+ * MPLL register offeset taken from the S905 datasheet. Vendor kernel source
+ * confirm these are the same for the S805.
+ */
+#define HHI_MPLL_CNTL 0x280
+#define HHI_MPLL_CNTL2 0x284
+#define HHI_MPLL_CNTL3 0x288
+#define HHI_MPLL_CNTL4 0x28c
+#define HHI_MPLL_CNTL5 0x290
+#define HHI_MPLL_CNTL6 0x294
+#define HHI_MPLL_CNTL7 0x298
+#define HHI_MPLL_CNTL8 0x29c
+#define HHI_MPLL_CNTL9 0x2a0
+#define HHI_MPLL_CNTL10 0x2a4
struct meson8b_clk_reset {
struct reset_controller_dev reset;
@@ -147,7 +214,7 @@ static const struct reg_sequence meson8b_hdmi_pll_init_regs[] = {
{ .reg = HHI_VID2_PLL_CNTL2, .def = 0x0430a800 },
};
-static const struct pll_params_table hdmi_pll_params_table[] = {
+static const struct pll_params_table meson8b_hdmi_pll_params_table[] = {
PLL_PARAMS(40, 1),
PLL_PARAMS(42, 1),
PLL_PARAMS(44, 1),
@@ -200,7 +267,7 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
.shift = 29,
.width = 1,
},
- .table = hdmi_pll_params_table,
+ .table = meson8b_hdmi_pll_params_table,
.init_regs = meson8b_hdmi_pll_init_regs,
.init_count = ARRAY_SIZE(meson8b_hdmi_pll_init_regs),
},
@@ -488,7 +555,6 @@ static struct clk_regmap meson8b_mpll0_div = {
.shift = 25,
.width = 1,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll0_div",
@@ -533,7 +599,6 @@ static struct clk_regmap meson8b_mpll1_div = {
.shift = 16,
.width = 9,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll1_div",
@@ -578,7 +643,6 @@ static struct clk_regmap meson8b_mpll2_div = {
.shift = 16,
.width = 9,
},
- .lock = &meson_clk_lock,
},
.hw.init = &(struct clk_init_data){
.name = "mpll2_div",
@@ -606,16 +670,17 @@ static struct clk_regmap meson8b_mpll2 = {
},
};
-static u32 mux_table_clk81[] = { 6, 5, 7 };
-static struct clk_regmap meson8b_mpeg_clk_sel = {
+/* clk81 is often referred as "mpeg_clk" */
+static u32 meson8b_clk81_parents_val_table[] = { 6, 5, 7 };
+static struct clk_regmap meson8b_clk81_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MPEG_CLK_CNTL,
.mask = 0x7,
.shift = 12,
- .table = mux_table_clk81,
+ .table = meson8b_clk81_parents_val_table,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_sel",
+ .name = "clk81_sel",
.ops = &clk_regmap_mux_ro_ops,
/*
* FIXME bits 14:12 selects from 8 possible parents:
@@ -631,17 +696,17 @@ static struct clk_regmap meson8b_mpeg_clk_sel = {
},
};
-static struct clk_regmap meson8b_mpeg_clk_div = {
+static struct clk_regmap meson8b_clk81_div = {
.data = &(struct clk_regmap_div_data){
.offset = HHI_MPEG_CLK_CNTL,
.shift = 0,
.width = 7,
},
.hw.init = &(struct clk_init_data){
- .name = "mpeg_clk_div",
+ .name = "clk81_div",
.ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_mpeg_clk_sel.hw
+ &meson8b_clk81_sel.hw
},
.num_parents = 1,
},
@@ -656,7 +721,7 @@ static struct clk_regmap meson8b_clk81 = {
.name = "clk81",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_mpeg_clk_div.hw
+ &meson8b_clk81_div.hw
},
.num_parents = 1,
.flags = CLK_IS_CRITICAL,
@@ -710,7 +775,7 @@ static struct clk_fixed_factor meson8b_cpu_in_div3 = {
},
};
-static const struct clk_div_table cpu_scale_table[] = {
+static const struct clk_div_table meson8b_cpu_scale_div_table[] = {
{ .val = 1, .div = 4 },
{ .val = 2, .div = 6 },
{ .val = 3, .div = 8 },
@@ -727,7 +792,7 @@ static struct clk_regmap meson8b_cpu_scale_div = {
.offset = HHI_SYS_CPU_CLK_CNTL1,
.shift = 20,
.width = 10,
- .table = cpu_scale_table,
+ .table = meson8b_cpu_scale_div_table,
.flags = CLK_DIVIDER_ALLOW_ZERO,
},
.hw.init = &(struct clk_init_data){
@@ -741,13 +806,13 @@ static struct clk_regmap meson8b_cpu_scale_div = {
},
};
-static u32 mux_table_cpu_scale_out_sel[] = { 0, 1, 3 };
+static u32 meson8b_cpu_scale_out_parents_val_table[] = { 0, 1, 3 };
static struct clk_regmap meson8b_cpu_scale_out_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL0,
.mask = 0x3,
.shift = 2,
- .table = mux_table_cpu_scale_out_sel,
+ .table = meson8b_cpu_scale_out_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "cpu_scale_out_sel",
@@ -829,13 +894,13 @@ static struct clk_regmap meson8b_nand_clk_div = {
},
};
-static struct clk_regmap meson8b_nand_clk_gate = {
+static struct clk_regmap meson8b_nand_clk = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_NAND_CLK_CNTL,
.bit_idx = 8,
},
.hw.init = &(struct clk_init_data){
- .name = "nand_clk_gate",
+ .name = "nand_clk",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_nand_clk_div.hw
@@ -936,160 +1001,137 @@ static struct clk_fixed_factor meson8b_cpu_clk_div8 = {
},
};
-static u32 mux_table_apb[] = { 1, 2, 3, 4, 5, 6, 7 };
-static struct clk_regmap meson8b_apb_clk_sel = {
+static u32 meson8b_cpu_if_parents_val_table[] = { 1, 2, 3, 4, 5, 6, 7 };
+static const struct clk_hw *meson8b_cpu_if_parents[] = {
+ &meson8b_cpu_clk_div2.hw,
+ &meson8b_cpu_clk_div3.hw,
+ &meson8b_cpu_clk_div4.hw,
+ &meson8b_cpu_clk_div5.hw,
+ &meson8b_cpu_clk_div6.hw,
+ &meson8b_cpu_clk_div7.hw,
+ &meson8b_cpu_clk_div8.hw,
+};
+
+static struct clk_regmap meson8b_apb_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.mask = 0x7,
.shift = 3,
- .table = mux_table_apb,
+ .table = meson8b_cpu_if_parents_val_table,
},
.hw.init = &(struct clk_init_data){
- .name = "apb_clk_sel",
+ .name = "apb_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &meson8b_cpu_clk_div2.hw,
- &meson8b_cpu_clk_div3.hw,
- &meson8b_cpu_clk_div4.hw,
- &meson8b_cpu_clk_div5.hw,
- &meson8b_cpu_clk_div6.hw,
- &meson8b_cpu_clk_div7.hw,
- &meson8b_cpu_clk_div8.hw,
- },
- .num_parents = 7,
+ .parent_hws = meson8b_cpu_if_parents,
+ .num_parents = ARRAY_SIZE(meson8b_cpu_if_parents),
},
};
-static struct clk_regmap meson8b_apb_clk_gate = {
+static struct clk_regmap meson8b_apb = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.bit_idx = 16,
.flags = CLK_GATE_SET_TO_DISABLE,
},
.hw.init = &(struct clk_init_data){
- .name = "apb_clk_dis",
+ .name = "apb",
.ops = &clk_regmap_gate_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_apb_clk_sel.hw
+ &meson8b_apb_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap meson8b_periph_clk_sel = {
+static struct clk_regmap meson8b_periph_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.mask = 0x7,
.shift = 6,
},
.hw.init = &(struct clk_init_data){
- .name = "periph_clk_sel",
+ .name = "periph_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &meson8b_cpu_clk_div2.hw,
- &meson8b_cpu_clk_div3.hw,
- &meson8b_cpu_clk_div4.hw,
- &meson8b_cpu_clk_div5.hw,
- &meson8b_cpu_clk_div6.hw,
- &meson8b_cpu_clk_div7.hw,
- &meson8b_cpu_clk_div8.hw,
- },
- .num_parents = 7,
+ .parent_hws = meson8b_cpu_if_parents,
+ .num_parents = ARRAY_SIZE(meson8b_cpu_if_parents),
},
};
-static struct clk_regmap meson8b_periph_clk_gate = {
+static struct clk_regmap meson8b_periph = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.bit_idx = 17,
.flags = CLK_GATE_SET_TO_DISABLE,
},
.hw.init = &(struct clk_init_data){
- .name = "periph_clk_dis",
+ .name = "periph",
.ops = &clk_regmap_gate_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_periph_clk_sel.hw
+ &meson8b_periph_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static u32 mux_table_axi[] = { 1, 2, 3, 4, 5, 6, 7 };
-static struct clk_regmap meson8b_axi_clk_sel = {
+static struct clk_regmap meson8b_axi_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.mask = 0x7,
.shift = 9,
- .table = mux_table_axi,
+ .table = meson8b_cpu_if_parents_val_table,
},
.hw.init = &(struct clk_init_data){
- .name = "axi_clk_sel",
+ .name = "axi_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &meson8b_cpu_clk_div2.hw,
- &meson8b_cpu_clk_div3.hw,
- &meson8b_cpu_clk_div4.hw,
- &meson8b_cpu_clk_div5.hw,
- &meson8b_cpu_clk_div6.hw,
- &meson8b_cpu_clk_div7.hw,
- &meson8b_cpu_clk_div8.hw,
- },
- .num_parents = 7,
+ .parent_hws = meson8b_cpu_if_parents,
+ .num_parents = ARRAY_SIZE(meson8b_cpu_if_parents),
},
};
-static struct clk_regmap meson8b_axi_clk_gate = {
+static struct clk_regmap meson8b_axi = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.bit_idx = 18,
.flags = CLK_GATE_SET_TO_DISABLE,
},
.hw.init = &(struct clk_init_data){
- .name = "axi_clk_dis",
+ .name = "axi",
.ops = &clk_regmap_gate_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_axi_clk_sel.hw
+ &meson8b_axi_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap meson8b_l2_dram_clk_sel = {
+static struct clk_regmap meson8b_l2_dram_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.mask = 0x7,
.shift = 12,
},
.hw.init = &(struct clk_init_data){
- .name = "l2_dram_clk_sel",
+ .name = "l2_dram_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = (const struct clk_hw *[]) {
- &meson8b_cpu_clk_div2.hw,
- &meson8b_cpu_clk_div3.hw,
- &meson8b_cpu_clk_div4.hw,
- &meson8b_cpu_clk_div5.hw,
- &meson8b_cpu_clk_div6.hw,
- &meson8b_cpu_clk_div7.hw,
- &meson8b_cpu_clk_div8.hw,
- },
- .num_parents = 7,
+ .parent_hws = meson8b_cpu_if_parents,
+ .num_parents = ARRAY_SIZE(meson8b_cpu_if_parents),
},
};
-static struct clk_regmap meson8b_l2_dram_clk_gate = {
+static struct clk_regmap meson8b_l2_dram = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_SYS_CPU_CLK_CNTL1,
.bit_idx = 19,
.flags = CLK_GATE_SET_TO_DISABLE,
},
.hw.init = &(struct clk_init_data){
- .name = "l2_dram_clk_dis",
+ .name = "l2_dram",
.ops = &clk_regmap_gate_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_l2_dram_clk_sel.hw
+ &meson8b_l2_dram_sel.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -1222,7 +1264,7 @@ static struct clk_regmap meson8b_vid_pll_final_div = {
},
};
-static const struct clk_hw *meson8b_vclk_mux_parent_hws[] = {
+static const struct clk_hw *meson8b_vclk_parents[] = {
&meson8b_vid_pll_final_div.hw,
&meson8b_fclk_div4.hw,
&meson8b_fclk_div3.hw,
@@ -1241,8 +1283,8 @@ static struct clk_regmap meson8b_vclk_in_sel = {
.hw.init = &(struct clk_init_data){
.name = "vclk_in_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk_mux_parent_hws),
+ .parent_hws = meson8b_vclk_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_parents),
.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
},
};
@@ -1279,13 +1321,13 @@ static struct clk_regmap meson8b_vclk_en = {
},
};
-static struct clk_regmap meson8b_vclk_div1_gate = {
+static struct clk_regmap meson8b_vclk_div1 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL,
.bit_idx = 0,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk_div1_en",
+ .name = "vclk_div1",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_en.hw
@@ -1299,7 +1341,7 @@ static struct clk_fixed_factor meson8b_vclk_div2_div = {
.mult = 1,
.div = 2,
.hw.init = &(struct clk_init_data){
- .name = "vclk_div2",
+ .name = "vclk_div2_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_en.hw
@@ -1309,13 +1351,13 @@ static struct clk_fixed_factor meson8b_vclk_div2_div = {
}
};
-static struct clk_regmap meson8b_vclk_div2_div_gate = {
+static struct clk_regmap meson8b_vclk_div2 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL,
.bit_idx = 1,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk_div2_en",
+ .name = "vclk_div2",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_div2_div.hw
@@ -1329,7 +1371,7 @@ static struct clk_fixed_factor meson8b_vclk_div4_div = {
.mult = 1,
.div = 4,
.hw.init = &(struct clk_init_data){
- .name = "vclk_div4",
+ .name = "vclk_div4_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_en.hw
@@ -1339,13 +1381,13 @@ static struct clk_fixed_factor meson8b_vclk_div4_div = {
}
};
-static struct clk_regmap meson8b_vclk_div4_div_gate = {
+static struct clk_regmap meson8b_vclk_div4 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL,
.bit_idx = 2,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk_div4_en",
+ .name = "vclk_div4",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_div4_div.hw
@@ -1359,7 +1401,7 @@ static struct clk_fixed_factor meson8b_vclk_div6_div = {
.mult = 1,
.div = 6,
.hw.init = &(struct clk_init_data){
- .name = "vclk_div6",
+ .name = "vclk_div6_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_en.hw
@@ -1369,13 +1411,13 @@ static struct clk_fixed_factor meson8b_vclk_div6_div = {
}
};
-static struct clk_regmap meson8b_vclk_div6_div_gate = {
+static struct clk_regmap meson8b_vclk_div6 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL,
.bit_idx = 3,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk_div6_en",
+ .name = "vclk_div6",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_div6_div.hw
@@ -1389,7 +1431,7 @@ static struct clk_fixed_factor meson8b_vclk_div12_div = {
.mult = 1,
.div = 12,
.hw.init = &(struct clk_init_data){
- .name = "vclk_div12",
+ .name = "vclk_div12_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_en.hw
@@ -1399,13 +1441,13 @@ static struct clk_fixed_factor meson8b_vclk_div12_div = {
}
};
-static struct clk_regmap meson8b_vclk_div12_div_gate = {
+static struct clk_regmap meson8b_vclk_div12 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL,
.bit_idx = 4,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk_div12_en",
+ .name = "vclk_div12",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk_div12_div.hw
@@ -1424,13 +1466,13 @@ static struct clk_regmap meson8b_vclk2_in_sel = {
.hw.init = &(struct clk_init_data){
.name = "vclk2_in_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk_mux_parent_hws),
+ .parent_hws = meson8b_vclk_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_parents),
.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
},
};
-static struct clk_regmap meson8b_vclk2_clk_in_en = {
+static struct clk_regmap meson8b_vclk2_in_en = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 16,
@@ -1446,7 +1488,7 @@ static struct clk_regmap meson8b_vclk2_clk_in_en = {
},
};
-static struct clk_regmap meson8b_vclk2_clk_en = {
+static struct clk_regmap meson8b_vclk2_en = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 19,
@@ -1455,23 +1497,23 @@ static struct clk_regmap meson8b_vclk2_clk_en = {
.name = "vclk2_en",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_vclk2_clk_in_en.hw
+ &meson8b_vclk2_in_en.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_regmap meson8b_vclk2_div1_gate = {
+static struct clk_regmap meson8b_vclk2_div1 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 0,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div1_en",
+ .name = "vclk2_div1",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_vclk2_clk_en.hw
+ &meson8b_vclk2_en.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -1482,23 +1524,23 @@ static struct clk_fixed_factor meson8b_vclk2_div2_div = {
.mult = 1,
.div = 2,
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div2",
+ .name = "vclk2_div2_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_vclk2_clk_en.hw
+ &meson8b_vclk2_en.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
}
};
-static struct clk_regmap meson8b_vclk2_div2_div_gate = {
+static struct clk_regmap meson8b_vclk2_div2 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 1,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div2_en",
+ .name = "vclk2_div2",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk2_div2_div.hw
@@ -1512,23 +1554,23 @@ static struct clk_fixed_factor meson8b_vclk2_div4_div = {
.mult = 1,
.div = 4,
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div4",
+ .name = "vclk2_div4_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_vclk2_clk_en.hw
+ &meson8b_vclk2_en.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
}
};
-static struct clk_regmap meson8b_vclk2_div4_div_gate = {
+static struct clk_regmap meson8b_vclk2_div4 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 2,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div4_en",
+ .name = "vclk2_div4",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk2_div4_div.hw
@@ -1542,23 +1584,23 @@ static struct clk_fixed_factor meson8b_vclk2_div6_div = {
.mult = 1,
.div = 6,
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div6",
+ .name = "vclk2_div6_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_vclk2_clk_en.hw
+ &meson8b_vclk2_en.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
}
};
-static struct clk_regmap meson8b_vclk2_div6_div_gate = {
+static struct clk_regmap meson8b_vclk2_div6 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 3,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div6_en",
+ .name = "vclk2_div6",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk2_div6_div.hw
@@ -1572,23 +1614,23 @@ static struct clk_fixed_factor meson8b_vclk2_div12_div = {
.mult = 1,
.div = 12,
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div12",
+ .name = "vclk2_div12_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
- &meson8b_vclk2_clk_en.hw
+ &meson8b_vclk2_en.hw
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
}
};
-static struct clk_regmap meson8b_vclk2_div12_div_gate = {
+static struct clk_regmap meson8b_vclk2_div12 = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VIID_CLK_DIV,
.bit_idx = 4,
},
.hw.init = &(struct clk_init_data){
- .name = "vclk2_div12_en",
+ .name = "vclk2_div12",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&meson8b_vclk2_div12_div.hw
@@ -1598,12 +1640,12 @@ static struct clk_regmap meson8b_vclk2_div12_div_gate = {
},
};
-static const struct clk_hw *meson8b_vclk_enc_mux_parent_hws[] = {
- &meson8b_vclk_div1_gate.hw,
- &meson8b_vclk_div2_div_gate.hw,
- &meson8b_vclk_div4_div_gate.hw,
- &meson8b_vclk_div6_div_gate.hw,
- &meson8b_vclk_div12_div_gate.hw,
+static const struct clk_hw *meson8b_vclk_enc_parents[] = {
+ &meson8b_vclk_div1.hw,
+ &meson8b_vclk_div2.hw,
+ &meson8b_vclk_div4.hw,
+ &meson8b_vclk_div6.hw,
+ &meson8b_vclk_div12.hw,
};
static struct clk_regmap meson8b_cts_enct_sel = {
@@ -1615,8 +1657,8 @@ static struct clk_regmap meson8b_cts_enct_sel = {
.hw.init = &(struct clk_init_data){
.name = "cts_enct_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk_enc_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk_enc_mux_parent_hws),
+ .parent_hws = meson8b_vclk_enc_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_enc_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1646,8 +1688,8 @@ static struct clk_regmap meson8b_cts_encp_sel = {
.hw.init = &(struct clk_init_data){
.name = "cts_encp_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk_enc_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk_enc_mux_parent_hws),
+ .parent_hws = meson8b_vclk_enc_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_enc_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1677,8 +1719,8 @@ static struct clk_regmap meson8b_cts_enci_sel = {
.hw.init = &(struct clk_init_data){
.name = "cts_enci_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk_enc_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk_enc_mux_parent_hws),
+ .parent_hws = meson8b_vclk_enc_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_enc_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1708,8 +1750,8 @@ static struct clk_regmap meson8b_hdmi_tx_pixel_sel = {
.hw.init = &(struct clk_init_data){
.name = "hdmi_tx_pixel_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk_enc_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk_enc_mux_parent_hws),
+ .parent_hws = meson8b_vclk_enc_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_enc_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1730,14 +1772,6 @@ static struct clk_regmap meson8b_hdmi_tx_pixel = {
},
};
-static const struct clk_hw *meson8b_vclk2_enc_mux_parent_hws[] = {
- &meson8b_vclk2_div1_gate.hw,
- &meson8b_vclk2_div2_div_gate.hw,
- &meson8b_vclk2_div4_div_gate.hw,
- &meson8b_vclk2_div6_div_gate.hw,
- &meson8b_vclk2_div12_div_gate.hw,
-};
-
static struct clk_regmap meson8b_cts_encl_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VIID_CLK_DIV,
@@ -1747,8 +1781,8 @@ static struct clk_regmap meson8b_cts_encl_sel = {
.hw.init = &(struct clk_init_data){
.name = "cts_encl_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk2_enc_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk2_enc_mux_parent_hws),
+ .parent_hws = meson8b_vclk_enc_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_enc_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1778,8 +1812,8 @@ static struct clk_regmap meson8b_cts_vdac0_sel = {
.hw.init = &(struct clk_init_data){
.name = "cts_vdac0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vclk2_enc_mux_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vclk2_enc_mux_parent_hws),
+ .parent_hws = meson8b_vclk_enc_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vclk_enc_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -1862,7 +1896,8 @@ static struct clk_regmap meson8b_hdmi_sys = {
* CLK_SET_RATE_GATE is set.
* Meson8 only has mali_0 and no glitch-free mux.
*/
-static const struct clk_parent_data meson8b_mali_0_1_parent_data[] = {
+static u32 meson8b_mali_parents_val_table[] = { 0, 2, 3, 4, 5, 6, 7 };
+static const struct clk_parent_data meson8b_mali_parents[] = {
{ .fw_name = "xtal", .name = "xtal", .index = -1, },
{ .hw = &meson8b_mpll2.hw, },
{ .hw = &meson8b_mpll1.hw, },
@@ -1872,20 +1907,18 @@ static const struct clk_parent_data meson8b_mali_0_1_parent_data[] = {
{ .hw = &meson8b_fclk_div5.hw, },
};
-static u32 meson8b_mali_0_1_mux_table[] = { 0, 2, 3, 4, 5, 6, 7 };
-
static struct clk_regmap meson8b_mali_0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_MALI_CLK_CNTL,
.mask = 0x7,
.shift = 9,
- .table = meson8b_mali_0_1_mux_table,
+ .table = meson8b_mali_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "mali_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = meson8b_mali_0_1_parent_data,
- .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_data),
+ .parent_data = meson8b_mali_parents,
+ .num_parents = ARRAY_SIZE(meson8b_mali_parents),
/*
* Don't propagate rate changes up because the only changeable
* parents are mpll1 and mpll2 but we need those for audio and
@@ -1934,13 +1967,13 @@ static struct clk_regmap meson8b_mali_1_sel = {
.offset = HHI_MALI_CLK_CNTL,
.mask = 0x7,
.shift = 25,
- .table = meson8b_mali_0_1_mux_table,
+ .table = meson8b_mali_parents_val_table,
},
.hw.init = &(struct clk_init_data){
.name = "mali_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_data = meson8b_mali_0_1_parent_data,
- .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_data),
+ .parent_data = meson8b_mali_parents,
+ .num_parents = ARRAY_SIZE(meson8b_mali_parents),
/*
* Don't propagate rate changes up because the only changeable
* parents are mpll1 and mpll2 but we need those for audio and
@@ -2075,20 +2108,13 @@ static struct clk_regmap meson8m2_gp_pll = {
},
};
-static const struct clk_hw *meson8b_vpu_0_1_parent_hws[] = {
+static const struct clk_hw *meson8b_vpu_parents[] = {
&meson8b_fclk_div4.hw,
&meson8b_fclk_div3.hw,
&meson8b_fclk_div5.hw,
&meson8b_fclk_div7.hw,
};
-static const struct clk_hw *mmeson8m2_vpu_0_1_parent_hws[] = {
- &meson8b_fclk_div4.hw,
- &meson8b_fclk_div3.hw,
- &meson8b_fclk_div5.hw,
- &meson8m2_gp_pll.hw,
-};
-
static struct clk_regmap meson8b_vpu_0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VPU_CLK_CNTL,
@@ -2098,12 +2124,19 @@ static struct clk_regmap meson8b_vpu_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vpu_0_1_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vpu_0_1_parent_hws),
+ .parent_hws = meson8b_vpu_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vpu_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
+static const struct clk_hw *mmeson8m2_vpu_parents[] = {
+ &meson8b_fclk_div4.hw,
+ &meson8b_fclk_div3.hw,
+ &meson8b_fclk_div5.hw,
+ &meson8m2_gp_pll.hw,
+};
+
static struct clk_regmap meson8m2_vpu_0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VPU_CLK_CNTL,
@@ -2113,8 +2146,8 @@ static struct clk_regmap meson8m2_vpu_0_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_0_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = mmeson8m2_vpu_0_1_parent_hws,
- .num_parents = ARRAY_SIZE(mmeson8m2_vpu_0_1_parent_hws),
+ .parent_hws = mmeson8m2_vpu_parents,
+ .num_parents = ARRAY_SIZE(mmeson8m2_vpu_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2169,8 +2202,8 @@ static struct clk_regmap meson8b_vpu_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vpu_0_1_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vpu_0_1_parent_hws),
+ .parent_hws = meson8b_vpu_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vpu_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2184,8 +2217,8 @@ static struct clk_regmap meson8m2_vpu_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vpu_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = mmeson8m2_vpu_0_1_parent_hws,
- .num_parents = ARRAY_SIZE(mmeson8m2_vpu_0_1_parent_hws),
+ .parent_hws = mmeson8m2_vpu_parents,
+ .num_parents = ARRAY_SIZE(mmeson8m2_vpu_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2257,7 +2290,7 @@ static struct clk_regmap meson8b_vpu = {
},
};
-static const struct clk_hw *meson8b_vdec_parent_hws[] = {
+static const struct clk_hw *meson8b_vdec_parents[] = {
&meson8b_fclk_div4.hw,
&meson8b_fclk_div3.hw,
&meson8b_fclk_div5.hw,
@@ -2276,8 +2309,8 @@ static struct clk_regmap meson8b_vdec_1_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_1_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vdec_parent_hws),
+ .parent_hws = meson8b_vdec_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2379,8 +2412,8 @@ static struct clk_regmap meson8b_vdec_hcodec_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_hcodec_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vdec_parent_hws),
+ .parent_hws = meson8b_vdec_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2429,8 +2462,8 @@ static struct clk_regmap meson8b_vdec_2_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_2_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vdec_parent_hws),
+ .parent_hws = meson8b_vdec_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2479,8 +2512,8 @@ static struct clk_regmap meson8b_vdec_hevc_sel = {
.hw.init = &(struct clk_init_data){
.name = "vdec_hevc_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_vdec_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_vdec_parent_hws),
+ .parent_hws = meson8b_vdec_parents,
+ .num_parents = ARRAY_SIZE(meson8b_vdec_parents),
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -2539,27 +2572,26 @@ static struct clk_regmap meson8b_vdec_hevc = {
};
/* TODO: the clock at index 0 is "DDR_PLL" which we don't support yet */
-static const struct clk_hw *meson8b_cts_amclk_parent_hws[] = {
+static u32 meson8b_cts_mclk_parents_val_table[] = { 1, 2, 3 };
+static const struct clk_hw *meson8b_cts_mclk_parents[] = {
&meson8b_mpll0.hw,
&meson8b_mpll1.hw,
&meson8b_mpll2.hw
};
-static u32 meson8b_cts_amclk_mux_table[] = { 1, 2, 3 };
-
static struct clk_regmap meson8b_cts_amclk_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_AUD_CLK_CNTL,
.mask = 0x3,
.shift = 9,
- .table = meson8b_cts_amclk_mux_table,
+ .table = meson8b_cts_mclk_parents_val_table,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "cts_amclk_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_cts_amclk_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_cts_amclk_parent_hws),
+ .parent_hws = meson8b_cts_mclk_parents,
+ .num_parents = ARRAY_SIZE(meson8b_cts_mclk_parents),
},
};
@@ -2597,28 +2629,19 @@ static struct clk_regmap meson8b_cts_amclk = {
},
};
-/* TODO: the clock at index 0 is "DDR_PLL" which we don't support yet */
-static const struct clk_hw *meson8b_cts_mclk_i958_parent_hws[] = {
- &meson8b_mpll0.hw,
- &meson8b_mpll1.hw,
- &meson8b_mpll2.hw
-};
-
-static u32 meson8b_cts_mclk_i958_mux_table[] = { 1, 2, 3 };
-
static struct clk_regmap meson8b_cts_mclk_i958_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_AUD_CLK_CNTL2,
.mask = 0x3,
.shift = 25,
- .table = meson8b_cts_mclk_i958_mux_table,
+ .table = meson8b_cts_mclk_parents_val_table,
.flags = CLK_MUX_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data) {
.name = "cts_mclk_i958_sel",
.ops = &clk_regmap_mux_ops,
- .parent_hws = meson8b_cts_mclk_i958_parent_hws,
- .num_parents = ARRAY_SIZE(meson8b_cts_mclk_i958_parent_hws),
+ .parent_hws = meson8b_cts_mclk_parents,
+ .num_parents = ARRAY_SIZE(meson8b_cts_mclk_parents),
},
};
@@ -2678,942 +2701,749 @@ static struct clk_regmap meson8b_cts_i958 = {
},
};
-#define MESON_GATE(_name, _reg, _bit) \
- MESON_PCLK(_name, _reg, _bit, &meson8b_clk81.hw)
-
-/* Everything Else (EE) domain gates */
-
-static MESON_GATE(meson8b_ddr, HHI_GCLK_MPEG0, 0);
-static MESON_GATE(meson8b_dos, HHI_GCLK_MPEG0, 1);
-static MESON_GATE(meson8b_isa, HHI_GCLK_MPEG0, 5);
-static MESON_GATE(meson8b_pl301, HHI_GCLK_MPEG0, 6);
-static MESON_GATE(meson8b_periphs, HHI_GCLK_MPEG0, 7);
-static MESON_GATE(meson8b_spicc, HHI_GCLK_MPEG0, 8);
-static MESON_GATE(meson8b_i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(meson8b_sar_adc, HHI_GCLK_MPEG0, 10);
-static MESON_GATE(meson8b_smart_card, HHI_GCLK_MPEG0, 11);
-static MESON_GATE(meson8b_rng0, HHI_GCLK_MPEG0, 12);
-static MESON_GATE(meson8b_uart0, HHI_GCLK_MPEG0, 13);
-static MESON_GATE(meson8b_sdhc, HHI_GCLK_MPEG0, 14);
-static MESON_GATE(meson8b_stream, HHI_GCLK_MPEG0, 15);
-static MESON_GATE(meson8b_async_fifo, HHI_GCLK_MPEG0, 16);
-static MESON_GATE(meson8b_sdio, HHI_GCLK_MPEG0, 17);
-static MESON_GATE(meson8b_abuf, HHI_GCLK_MPEG0, 18);
-static MESON_GATE(meson8b_hiu_iface, HHI_GCLK_MPEG0, 19);
-static MESON_GATE(meson8b_assist_misc, HHI_GCLK_MPEG0, 23);
-static MESON_GATE(meson8b_spi, HHI_GCLK_MPEG0, 30);
-
-static MESON_GATE(meson8b_i2s_spdif, HHI_GCLK_MPEG1, 2);
-static MESON_GATE(meson8b_eth, HHI_GCLK_MPEG1, 3);
-static MESON_GATE(meson8b_demux, HHI_GCLK_MPEG1, 4);
-static MESON_GATE(meson8b_blkmv, HHI_GCLK_MPEG1, 14);
-static MESON_GATE(meson8b_aiu, HHI_GCLK_MPEG1, 15);
-static MESON_GATE(meson8b_uart1, HHI_GCLK_MPEG1, 16);
-static MESON_GATE(meson8b_g2d, HHI_GCLK_MPEG1, 20);
-static MESON_GATE(meson8b_usb0, HHI_GCLK_MPEG1, 21);
-static MESON_GATE(meson8b_usb1, HHI_GCLK_MPEG1, 22);
-static MESON_GATE(meson8b_reset, HHI_GCLK_MPEG1, 23);
-static MESON_GATE(meson8b_nand, HHI_GCLK_MPEG1, 24);
-static MESON_GATE(meson8b_dos_parser, HHI_GCLK_MPEG1, 25);
-static MESON_GATE(meson8b_usb, HHI_GCLK_MPEG1, 26);
-static MESON_GATE(meson8b_vdin1, HHI_GCLK_MPEG1, 28);
-static MESON_GATE(meson8b_ahb_arb0, HHI_GCLK_MPEG1, 29);
-static MESON_GATE(meson8b_efuse, HHI_GCLK_MPEG1, 30);
-static MESON_GATE(meson8b_boot_rom, HHI_GCLK_MPEG1, 31);
-
-static MESON_GATE(meson8b_ahb_data_bus, HHI_GCLK_MPEG2, 1);
-static MESON_GATE(meson8b_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
-static MESON_GATE(meson8b_hdmi_intr_sync, HHI_GCLK_MPEG2, 3);
-static MESON_GATE(meson8b_hdmi_pclk, HHI_GCLK_MPEG2, 4);
-static MESON_GATE(meson8b_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8);
-static MESON_GATE(meson8b_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9);
-static MESON_GATE(meson8b_mmc_pclk, HHI_GCLK_MPEG2, 11);
-static MESON_GATE(meson8b_dvin, HHI_GCLK_MPEG2, 12);
-static MESON_GATE(meson8b_uart2, HHI_GCLK_MPEG2, 15);
-static MESON_GATE(meson8b_sana, HHI_GCLK_MPEG2, 22);
-static MESON_GATE(meson8b_vpu_intr, HHI_GCLK_MPEG2, 25);
-static MESON_GATE(meson8b_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
-static MESON_GATE(meson8b_clk81_a9, HHI_GCLK_MPEG2, 29);
-
-static MESON_GATE(meson8b_vclk2_venci0, HHI_GCLK_OTHER, 1);
-static MESON_GATE(meson8b_vclk2_venci1, HHI_GCLK_OTHER, 2);
-static MESON_GATE(meson8b_vclk2_vencp0, HHI_GCLK_OTHER, 3);
-static MESON_GATE(meson8b_vclk2_vencp1, HHI_GCLK_OTHER, 4);
-static MESON_GATE(meson8b_gclk_venci_int, HHI_GCLK_OTHER, 8);
-static MESON_GATE(meson8b_gclk_vencp_int, HHI_GCLK_OTHER, 9);
-static MESON_GATE(meson8b_dac_clk, HHI_GCLK_OTHER, 10);
-static MESON_GATE(meson8b_aoclk_gate, HHI_GCLK_OTHER, 14);
-static MESON_GATE(meson8b_iec958_gate, HHI_GCLK_OTHER, 16);
-static MESON_GATE(meson8b_enc480p, HHI_GCLK_OTHER, 20);
-static MESON_GATE(meson8b_rng1, HHI_GCLK_OTHER, 21);
-static MESON_GATE(meson8b_gclk_vencl_int, HHI_GCLK_OTHER, 22);
-static MESON_GATE(meson8b_vclk2_venclmcc, HHI_GCLK_OTHER, 24);
-static MESON_GATE(meson8b_vclk2_vencl, HHI_GCLK_OTHER, 25);
-static MESON_GATE(meson8b_vclk2_other, HHI_GCLK_OTHER, 26);
-static MESON_GATE(meson8b_edp, HHI_GCLK_OTHER, 31);
+static const struct clk_parent_data meson8b_pclk_parents = { .hw = &meson8b_clk81.hw };
+
+#define MESON8B_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(_name, _reg, _bit, &meson8b_pclk_parents, _flags)
+
+/*
+ * Everything Else (EE) domain gates
+ *
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
+ */
+static MESON8B_PCLK(meson8b_ddr, HHI_GCLK_MPEG0, 0, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_dos, HHI_GCLK_MPEG0, 1, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_isa, HHI_GCLK_MPEG0, 5, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_pl301, HHI_GCLK_MPEG0, 6, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_periphs, HHI_GCLK_MPEG0, 7, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_spicc, HHI_GCLK_MPEG0, 8, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_i2c, HHI_GCLK_MPEG0, 9, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_sar_adc, HHI_GCLK_MPEG0, 10, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_smart_card, HHI_GCLK_MPEG0, 11, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_rng0, HHI_GCLK_MPEG0, 12, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_uart0, HHI_GCLK_MPEG0, 13, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_sdhc, HHI_GCLK_MPEG0, 14, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_stream, HHI_GCLK_MPEG0, 15, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_async_fifo, HHI_GCLK_MPEG0, 16, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_sdio, HHI_GCLK_MPEG0, 17, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_abuf, HHI_GCLK_MPEG0, 18, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_hiu_iface, HHI_GCLK_MPEG0, 19, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_assist_misc, HHI_GCLK_MPEG0, 23, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_spi, HHI_GCLK_MPEG0, 30, CLK_IGNORE_UNUSED);
+
+static MESON8B_PCLK(meson8b_i2s_spdif, HHI_GCLK_MPEG1, 2, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_eth, HHI_GCLK_MPEG1, 3, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_demux, HHI_GCLK_MPEG1, 4, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_blkmv, HHI_GCLK_MPEG1, 14, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_aiu, HHI_GCLK_MPEG1, 15, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_uart1, HHI_GCLK_MPEG1, 16, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_g2d, HHI_GCLK_MPEG1, 20, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_usb0, HHI_GCLK_MPEG1, 21, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_usb1, HHI_GCLK_MPEG1, 22, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_reset, HHI_GCLK_MPEG1, 23, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_nand, HHI_GCLK_MPEG1, 24, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_dos_parser, HHI_GCLK_MPEG1, 25, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_usb, HHI_GCLK_MPEG1, 26, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vdin1, HHI_GCLK_MPEG1, 28, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_ahb_arb0, HHI_GCLK_MPEG1, 29, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_efuse, HHI_GCLK_MPEG1, 30, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_boot_rom, HHI_GCLK_MPEG1, 31, CLK_IGNORE_UNUSED);
+
+static MESON8B_PCLK(meson8b_ahb_data_bus, HHI_GCLK_MPEG2, 1, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_hdmi_intr_sync, HHI_GCLK_MPEG2, 3, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_hdmi_pclk, HHI_GCLK_MPEG2, 4, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_mmc_pclk, HHI_GCLK_MPEG2, 11, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_dvin, HHI_GCLK_MPEG2, 12, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_uart2, HHI_GCLK_MPEG2, 15, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_sana, HHI_GCLK_MPEG2, 22, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vpu_intr, HHI_GCLK_MPEG2, 25, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_clk81_a9, HHI_GCLK_MPEG2, 29, CLK_IGNORE_UNUSED);
+
+static MESON8B_PCLK(meson8b_vclk2_venci0, HHI_GCLK_OTHER, 1, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vclk2_venci1, HHI_GCLK_OTHER, 2, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vclk2_vencp0, HHI_GCLK_OTHER, 3, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vclk2_vencp1, HHI_GCLK_OTHER, 4, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_gclk_venci_int, HHI_GCLK_OTHER, 8, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_gclk_vencp_int, HHI_GCLK_OTHER, 9, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_dac_clk, HHI_GCLK_OTHER, 10, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_aoclk_gate, HHI_GCLK_OTHER, 14, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_iec958_gate, HHI_GCLK_OTHER, 16, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_enc480p, HHI_GCLK_OTHER, 20, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_rng1, HHI_GCLK_OTHER, 21, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_gclk_vencl_int, HHI_GCLK_OTHER, 22, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vclk2_venclmcc, HHI_GCLK_OTHER, 24, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vclk2_vencl, HHI_GCLK_OTHER, 25, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_vclk2_other, HHI_GCLK_OTHER, 26, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_edp, HHI_GCLK_OTHER, 31, CLK_IGNORE_UNUSED);
/* AIU gates */
-#define MESON_AIU_GLUE_GATE(_name, _reg, _bit) \
- MESON_PCLK(_name, _reg, _bit, &meson8b_aiu_glue.hw)
-
-static MESON_PCLK(meson8b_aiu_glue, HHI_GCLK_MPEG1, 6, &meson8b_aiu.hw);
-static MESON_AIU_GLUE_GATE(meson8b_iec958, HHI_GCLK_MPEG1, 7);
-static MESON_AIU_GLUE_GATE(meson8b_i2s_out, HHI_GCLK_MPEG1, 8);
-static MESON_AIU_GLUE_GATE(meson8b_amclk, HHI_GCLK_MPEG1, 9);
-static MESON_AIU_GLUE_GATE(meson8b_aififo2, HHI_GCLK_MPEG1, 10);
-static MESON_AIU_GLUE_GATE(meson8b_mixer, HHI_GCLK_MPEG1, 11);
-static MESON_AIU_GLUE_GATE(meson8b_mixer_iface, HHI_GCLK_MPEG1, 12);
-static MESON_AIU_GLUE_GATE(meson8b_adc, HHI_GCLK_MPEG1, 13);
+static const struct clk_parent_data meson8b_aiu_glue_parents = { .hw = &meson8b_aiu.hw };
+static MESON_PCLK(meson8b_aiu_glue, HHI_GCLK_MPEG1, 6,
+ &meson8b_aiu_glue_parents, CLK_IGNORE_UNUSED);
+
+static const struct clk_parent_data meson8b_aiu_pclk_parents = { .hw = &meson8b_aiu_glue.hw };
+#define MESON8B_AIU_PCLK(_name, _bit, _flags) \
+ MESON_PCLK(_name, HHI_GCLK_MPEG1, _bit, &meson8b_aiu_pclk_parents, _flags)
+
+static MESON8B_AIU_PCLK(meson8b_iec958, 7, CLK_IGNORE_UNUSED);
+static MESON8B_AIU_PCLK(meson8b_i2s_out, 8, CLK_IGNORE_UNUSED);
+static MESON8B_AIU_PCLK(meson8b_amclk, 9, CLK_IGNORE_UNUSED);
+static MESON8B_AIU_PCLK(meson8b_aififo2, 10, CLK_IGNORE_UNUSED);
+static MESON8B_AIU_PCLK(meson8b_mixer, 11, CLK_IGNORE_UNUSED);
+static MESON8B_AIU_PCLK(meson8b_mixer_iface, 12, CLK_IGNORE_UNUSED);
+static MESON8B_AIU_PCLK(meson8b_adc, 13, CLK_IGNORE_UNUSED);
/* Always On (AO) domain gates */
-static MESON_GATE(meson8b_ao_media_cpu, HHI_GCLK_AO, 0);
-static MESON_GATE(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1);
-static MESON_GATE(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2);
-static MESON_GATE(meson8b_ao_iface, HHI_GCLK_AO, 3);
-
-static struct clk_hw_onecell_data meson8_hw_onecell_data = {
- .hws = {
- [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
- [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
- [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
- [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
- [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
- [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw,
- [CLKID_CLK81] = &meson8b_clk81.hw,
- [CLKID_DDR] = &meson8b_ddr.hw,
- [CLKID_DOS] = &meson8b_dos.hw,
- [CLKID_ISA] = &meson8b_isa.hw,
- [CLKID_PL301] = &meson8b_pl301.hw,
- [CLKID_PERIPHS] = &meson8b_periphs.hw,
- [CLKID_SPICC] = &meson8b_spicc.hw,
- [CLKID_I2C] = &meson8b_i2c.hw,
- [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
- [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
- [CLKID_RNG0] = &meson8b_rng0.hw,
- [CLKID_UART0] = &meson8b_uart0.hw,
- [CLKID_SDHC] = &meson8b_sdhc.hw,
- [CLKID_STREAM] = &meson8b_stream.hw,
- [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
- [CLKID_SDIO] = &meson8b_sdio.hw,
- [CLKID_ABUF] = &meson8b_abuf.hw,
- [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
- [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
- [CLKID_SPI] = &meson8b_spi.hw,
- [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
- [CLKID_ETH] = &meson8b_eth.hw,
- [CLKID_DEMUX] = &meson8b_demux.hw,
- [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
- [CLKID_IEC958] = &meson8b_iec958.hw,
- [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
- [CLKID_AMCLK] = &meson8b_amclk.hw,
- [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
- [CLKID_MIXER] = &meson8b_mixer.hw,
- [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
- [CLKID_ADC] = &meson8b_adc.hw,
- [CLKID_BLKMV] = &meson8b_blkmv.hw,
- [CLKID_AIU] = &meson8b_aiu.hw,
- [CLKID_UART1] = &meson8b_uart1.hw,
- [CLKID_G2D] = &meson8b_g2d.hw,
- [CLKID_USB0] = &meson8b_usb0.hw,
- [CLKID_USB1] = &meson8b_usb1.hw,
- [CLKID_RESET] = &meson8b_reset.hw,
- [CLKID_NAND] = &meson8b_nand.hw,
- [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
- [CLKID_USB] = &meson8b_usb.hw,
- [CLKID_VDIN1] = &meson8b_vdin1.hw,
- [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
- [CLKID_EFUSE] = &meson8b_efuse.hw,
- [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
- [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
- [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
- [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
- [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
- [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
- [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
- [CLKID_DVIN] = &meson8b_dvin.hw,
- [CLKID_UART2] = &meson8b_uart2.hw,
- [CLKID_SANA] = &meson8b_sana.hw,
- [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
- [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
- [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
- [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
- [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
- [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
- [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
- [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
- [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
- [CLKID_ENC480P] = &meson8b_enc480p.hw,
- [CLKID_RNG1] = &meson8b_rng1.hw,
- [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
- [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
- [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
- [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
- [CLKID_EDP] = &meson8b_edp.hw,
- [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
- [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
- [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
- [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
- [CLKID_MPLL0] = &meson8b_mpll0.hw,
- [CLKID_MPLL1] = &meson8b_mpll1.hw,
- [CLKID_MPLL2] = &meson8b_mpll2.hw,
- [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
- [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
- [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
- [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
- [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
- [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
- [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
- [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
- [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
- [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
- [CLKID_NAND_CLK] = &meson8b_nand_clk_gate.hw,
- [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
- [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
- [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
- [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
- [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
- [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
- [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
- [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
- [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
- [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
- [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw,
- [CLKID_APB] = &meson8b_apb_clk_gate.hw,
- [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw,
- [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw,
- [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw,
- [CLKID_AXI] = &meson8b_axi_clk_gate.hw,
- [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_clk_sel.hw,
- [CLKID_L2_DRAM] = &meson8b_l2_dram_clk_gate.hw,
- [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
- [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
- [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
- [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
- [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
- [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
- [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
- [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
- [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
- [CLKID_VCLK_EN] = &meson8b_vclk_en.hw,
- [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw,
- [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
- [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw,
- [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
- [CLKID_VCLK_DIV4] = &meson8b_vclk_div4_div_gate.hw,
- [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
- [CLKID_VCLK_DIV6] = &meson8b_vclk_div6_div_gate.hw,
- [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
- [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw,
- [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
- [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw,
- [CLKID_VCLK2_EN] = &meson8b_vclk2_clk_en.hw,
- [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw,
- [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
- [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw,
- [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
- [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4_div_gate.hw,
- [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
- [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6_div_gate.hw,
- [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
- [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12_div_gate.hw,
- [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
- [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
- [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
- [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
- [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
- [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
- [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
- [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
- [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
- [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
- [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
- [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
- [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
- [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
- [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
- [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
- [CLKID_MALI] = &meson8b_mali_0.hw,
- [CLKID_VPU_0_SEL] = &meson8b_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &meson8b_vpu_0_div.hw,
- [CLKID_VPU] = &meson8b_vpu_0.hw,
- [CLKID_VDEC_1_SEL] = &meson8b_vdec_1_sel.hw,
- [CLKID_VDEC_1_1_DIV] = &meson8b_vdec_1_1_div.hw,
- [CLKID_VDEC_1] = &meson8b_vdec_1_1.hw,
- [CLKID_VDEC_HCODEC_SEL] = &meson8b_vdec_hcodec_sel.hw,
- [CLKID_VDEC_HCODEC_DIV] = &meson8b_vdec_hcodec_div.hw,
- [CLKID_VDEC_HCODEC] = &meson8b_vdec_hcodec.hw,
- [CLKID_VDEC_2_SEL] = &meson8b_vdec_2_sel.hw,
- [CLKID_VDEC_2_DIV] = &meson8b_vdec_2_div.hw,
- [CLKID_VDEC_2] = &meson8b_vdec_2.hw,
- [CLKID_VDEC_HEVC_SEL] = &meson8b_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &meson8b_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC_EN] = &meson8b_vdec_hevc_en.hw,
- [CLKID_VDEC_HEVC] = &meson8b_vdec_hevc.hw,
- [CLKID_CTS_AMCLK_SEL] = &meson8b_cts_amclk_sel.hw,
- [CLKID_CTS_AMCLK_DIV] = &meson8b_cts_amclk_div.hw,
- [CLKID_CTS_AMCLK] = &meson8b_cts_amclk.hw,
- [CLKID_CTS_MCLK_I958_SEL] = &meson8b_cts_mclk_i958_sel.hw,
- [CLKID_CTS_MCLK_I958_DIV] = &meson8b_cts_mclk_i958_div.hw,
- [CLKID_CTS_MCLK_I958] = &meson8b_cts_mclk_i958.hw,
- [CLKID_CTS_I958] = &meson8b_cts_i958.hw,
- [CLKID_VID_PLL_LVDS_EN] = &meson8b_vid_pll_lvds_en.hw,
- [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw,
- [CLK_NR_CLKS] = NULL,
- },
- .num = CLK_NR_CLKS,
-};
-
-static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
- .hws = {
- [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
- [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
- [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
- [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
- [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
- [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw,
- [CLKID_CLK81] = &meson8b_clk81.hw,
- [CLKID_DDR] = &meson8b_ddr.hw,
- [CLKID_DOS] = &meson8b_dos.hw,
- [CLKID_ISA] = &meson8b_isa.hw,
- [CLKID_PL301] = &meson8b_pl301.hw,
- [CLKID_PERIPHS] = &meson8b_periphs.hw,
- [CLKID_SPICC] = &meson8b_spicc.hw,
- [CLKID_I2C] = &meson8b_i2c.hw,
- [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
- [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
- [CLKID_RNG0] = &meson8b_rng0.hw,
- [CLKID_UART0] = &meson8b_uart0.hw,
- [CLKID_SDHC] = &meson8b_sdhc.hw,
- [CLKID_STREAM] = &meson8b_stream.hw,
- [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
- [CLKID_SDIO] = &meson8b_sdio.hw,
- [CLKID_ABUF] = &meson8b_abuf.hw,
- [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
- [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
- [CLKID_SPI] = &meson8b_spi.hw,
- [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
- [CLKID_ETH] = &meson8b_eth.hw,
- [CLKID_DEMUX] = &meson8b_demux.hw,
- [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
- [CLKID_IEC958] = &meson8b_iec958.hw,
- [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
- [CLKID_AMCLK] = &meson8b_amclk.hw,
- [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
- [CLKID_MIXER] = &meson8b_mixer.hw,
- [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
- [CLKID_ADC] = &meson8b_adc.hw,
- [CLKID_BLKMV] = &meson8b_blkmv.hw,
- [CLKID_AIU] = &meson8b_aiu.hw,
- [CLKID_UART1] = &meson8b_uart1.hw,
- [CLKID_G2D] = &meson8b_g2d.hw,
- [CLKID_USB0] = &meson8b_usb0.hw,
- [CLKID_USB1] = &meson8b_usb1.hw,
- [CLKID_RESET] = &meson8b_reset.hw,
- [CLKID_NAND] = &meson8b_nand.hw,
- [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
- [CLKID_USB] = &meson8b_usb.hw,
- [CLKID_VDIN1] = &meson8b_vdin1.hw,
- [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
- [CLKID_EFUSE] = &meson8b_efuse.hw,
- [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
- [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
- [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
- [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
- [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
- [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
- [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
- [CLKID_DVIN] = &meson8b_dvin.hw,
- [CLKID_UART2] = &meson8b_uart2.hw,
- [CLKID_SANA] = &meson8b_sana.hw,
- [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
- [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
- [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
- [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
- [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
- [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
- [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
- [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
- [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
- [CLKID_ENC480P] = &meson8b_enc480p.hw,
- [CLKID_RNG1] = &meson8b_rng1.hw,
- [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
- [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
- [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
- [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
- [CLKID_EDP] = &meson8b_edp.hw,
- [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
- [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
- [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
- [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
- [CLKID_MPLL0] = &meson8b_mpll0.hw,
- [CLKID_MPLL1] = &meson8b_mpll1.hw,
- [CLKID_MPLL2] = &meson8b_mpll2.hw,
- [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
- [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
- [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
- [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
- [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
- [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
- [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
- [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
- [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
- [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
- [CLKID_NAND_CLK] = &meson8b_nand_clk_gate.hw,
- [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
- [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
- [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
- [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
- [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
- [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
- [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
- [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
- [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
- [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
- [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw,
- [CLKID_APB] = &meson8b_apb_clk_gate.hw,
- [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw,
- [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw,
- [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw,
- [CLKID_AXI] = &meson8b_axi_clk_gate.hw,
- [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_clk_sel.hw,
- [CLKID_L2_DRAM] = &meson8b_l2_dram_clk_gate.hw,
- [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
- [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
- [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
- [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
- [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
- [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
- [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
- [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
- [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
- [CLKID_VCLK_EN] = &meson8b_vclk_en.hw,
- [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw,
- [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
- [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw,
- [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
- [CLKID_VCLK_DIV4] = &meson8b_vclk_div4_div_gate.hw,
- [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
- [CLKID_VCLK_DIV6] = &meson8b_vclk_div6_div_gate.hw,
- [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
- [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw,
- [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
- [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw,
- [CLKID_VCLK2_EN] = &meson8b_vclk2_clk_en.hw,
- [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw,
- [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
- [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw,
- [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
- [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4_div_gate.hw,
- [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
- [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6_div_gate.hw,
- [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
- [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12_div_gate.hw,
- [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
- [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
- [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
- [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
- [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
- [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
- [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
- [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
- [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
- [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
- [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
- [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
- [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
- [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
- [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
- [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
- [CLKID_MALI_0] = &meson8b_mali_0.hw,
- [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw,
- [CLKID_MALI_1] = &meson8b_mali_1.hw,
- [CLKID_MALI] = &meson8b_mali.hw,
- [CLKID_VPU_0_SEL] = &meson8b_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &meson8b_vpu_0_div.hw,
- [CLKID_VPU_0] = &meson8b_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &meson8b_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &meson8b_vpu_1_div.hw,
- [CLKID_VPU_1] = &meson8b_vpu_1.hw,
- [CLKID_VPU] = &meson8b_vpu.hw,
- [CLKID_VDEC_1_SEL] = &meson8b_vdec_1_sel.hw,
- [CLKID_VDEC_1_1_DIV] = &meson8b_vdec_1_1_div.hw,
- [CLKID_VDEC_1_1] = &meson8b_vdec_1_1.hw,
- [CLKID_VDEC_1_2_DIV] = &meson8b_vdec_1_2_div.hw,
- [CLKID_VDEC_1_2] = &meson8b_vdec_1_2.hw,
- [CLKID_VDEC_1] = &meson8b_vdec_1.hw,
- [CLKID_VDEC_HCODEC_SEL] = &meson8b_vdec_hcodec_sel.hw,
- [CLKID_VDEC_HCODEC_DIV] = &meson8b_vdec_hcodec_div.hw,
- [CLKID_VDEC_HCODEC] = &meson8b_vdec_hcodec.hw,
- [CLKID_VDEC_2_SEL] = &meson8b_vdec_2_sel.hw,
- [CLKID_VDEC_2_DIV] = &meson8b_vdec_2_div.hw,
- [CLKID_VDEC_2] = &meson8b_vdec_2.hw,
- [CLKID_VDEC_HEVC_SEL] = &meson8b_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &meson8b_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC_EN] = &meson8b_vdec_hevc_en.hw,
- [CLKID_VDEC_HEVC] = &meson8b_vdec_hevc.hw,
- [CLKID_CTS_AMCLK_SEL] = &meson8b_cts_amclk_sel.hw,
- [CLKID_CTS_AMCLK_DIV] = &meson8b_cts_amclk_div.hw,
- [CLKID_CTS_AMCLK] = &meson8b_cts_amclk.hw,
- [CLKID_CTS_MCLK_I958_SEL] = &meson8b_cts_mclk_i958_sel.hw,
- [CLKID_CTS_MCLK_I958_DIV] = &meson8b_cts_mclk_i958_div.hw,
- [CLKID_CTS_MCLK_I958] = &meson8b_cts_mclk_i958.hw,
- [CLKID_CTS_I958] = &meson8b_cts_i958.hw,
- [CLKID_VID_PLL_LVDS_EN] = &meson8b_vid_pll_lvds_en.hw,
- [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw,
- [CLK_NR_CLKS] = NULL,
- },
- .num = CLK_NR_CLKS,
-};
-
-static struct clk_hw_onecell_data meson8m2_hw_onecell_data = {
- .hws = {
- [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
- [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
- [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
- [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
- [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
- [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
- [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
- [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
- [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
- [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw,
- [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw,
- [CLKID_CLK81] = &meson8b_clk81.hw,
- [CLKID_DDR] = &meson8b_ddr.hw,
- [CLKID_DOS] = &meson8b_dos.hw,
- [CLKID_ISA] = &meson8b_isa.hw,
- [CLKID_PL301] = &meson8b_pl301.hw,
- [CLKID_PERIPHS] = &meson8b_periphs.hw,
- [CLKID_SPICC] = &meson8b_spicc.hw,
- [CLKID_I2C] = &meson8b_i2c.hw,
- [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
- [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
- [CLKID_RNG0] = &meson8b_rng0.hw,
- [CLKID_UART0] = &meson8b_uart0.hw,
- [CLKID_SDHC] = &meson8b_sdhc.hw,
- [CLKID_STREAM] = &meson8b_stream.hw,
- [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
- [CLKID_SDIO] = &meson8b_sdio.hw,
- [CLKID_ABUF] = &meson8b_abuf.hw,
- [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
- [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
- [CLKID_SPI] = &meson8b_spi.hw,
- [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
- [CLKID_ETH] = &meson8b_eth.hw,
- [CLKID_DEMUX] = &meson8b_demux.hw,
- [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
- [CLKID_IEC958] = &meson8b_iec958.hw,
- [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
- [CLKID_AMCLK] = &meson8b_amclk.hw,
- [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
- [CLKID_MIXER] = &meson8b_mixer.hw,
- [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
- [CLKID_ADC] = &meson8b_adc.hw,
- [CLKID_BLKMV] = &meson8b_blkmv.hw,
- [CLKID_AIU] = &meson8b_aiu.hw,
- [CLKID_UART1] = &meson8b_uart1.hw,
- [CLKID_G2D] = &meson8b_g2d.hw,
- [CLKID_USB0] = &meson8b_usb0.hw,
- [CLKID_USB1] = &meson8b_usb1.hw,
- [CLKID_RESET] = &meson8b_reset.hw,
- [CLKID_NAND] = &meson8b_nand.hw,
- [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
- [CLKID_USB] = &meson8b_usb.hw,
- [CLKID_VDIN1] = &meson8b_vdin1.hw,
- [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
- [CLKID_EFUSE] = &meson8b_efuse.hw,
- [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
- [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
- [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
- [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
- [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
- [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
- [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
- [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
- [CLKID_DVIN] = &meson8b_dvin.hw,
- [CLKID_UART2] = &meson8b_uart2.hw,
- [CLKID_SANA] = &meson8b_sana.hw,
- [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
- [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
- [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
- [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
- [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
- [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
- [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
- [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
- [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
- [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
- [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
- [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
- [CLKID_ENC480P] = &meson8b_enc480p.hw,
- [CLKID_RNG1] = &meson8b_rng1.hw,
- [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
- [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
- [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
- [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
- [CLKID_EDP] = &meson8b_edp.hw,
- [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
- [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
- [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
- [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
- [CLKID_MPLL0] = &meson8b_mpll0.hw,
- [CLKID_MPLL1] = &meson8b_mpll1.hw,
- [CLKID_MPLL2] = &meson8b_mpll2.hw,
- [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
- [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
- [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
- [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
- [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
- [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
- [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
- [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
- [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
- [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
- [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
- [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
- [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
- [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
- [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
- [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
- [CLKID_NAND_CLK] = &meson8b_nand_clk_gate.hw,
- [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
- [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
- [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
- [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
- [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
- [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
- [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
- [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
- [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
- [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
- [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw,
- [CLKID_APB] = &meson8b_apb_clk_gate.hw,
- [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw,
- [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw,
- [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw,
- [CLKID_AXI] = &meson8b_axi_clk_gate.hw,
- [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_clk_sel.hw,
- [CLKID_L2_DRAM] = &meson8b_l2_dram_clk_gate.hw,
- [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
- [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
- [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
- [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
- [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
- [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
- [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
- [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
- [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
- [CLKID_VCLK_EN] = &meson8b_vclk_en.hw,
- [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw,
- [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
- [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw,
- [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
- [CLKID_VCLK_DIV4] = &meson8b_vclk_div4_div_gate.hw,
- [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
- [CLKID_VCLK_DIV6] = &meson8b_vclk_div6_div_gate.hw,
- [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
- [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw,
- [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
- [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw,
- [CLKID_VCLK2_EN] = &meson8b_vclk2_clk_en.hw,
- [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw,
- [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
- [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw,
- [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
- [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4_div_gate.hw,
- [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
- [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6_div_gate.hw,
- [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
- [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12_div_gate.hw,
- [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
- [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
- [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
- [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
- [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
- [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
- [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
- [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
- [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
- [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
- [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
- [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
- [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
- [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
- [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
- [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
- [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
- [CLKID_MALI_0] = &meson8b_mali_0.hw,
- [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw,
- [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw,
- [CLKID_MALI_1] = &meson8b_mali_1.hw,
- [CLKID_MALI] = &meson8b_mali.hw,
- [CLKID_GP_PLL_DCO] = &meson8m2_gp_pll_dco.hw,
- [CLKID_GP_PLL] = &meson8m2_gp_pll.hw,
- [CLKID_VPU_0_SEL] = &meson8m2_vpu_0_sel.hw,
- [CLKID_VPU_0_DIV] = &meson8b_vpu_0_div.hw,
- [CLKID_VPU_0] = &meson8b_vpu_0.hw,
- [CLKID_VPU_1_SEL] = &meson8m2_vpu_1_sel.hw,
- [CLKID_VPU_1_DIV] = &meson8b_vpu_1_div.hw,
- [CLKID_VPU_1] = &meson8b_vpu_1.hw,
- [CLKID_VPU] = &meson8b_vpu.hw,
- [CLKID_VDEC_1_SEL] = &meson8b_vdec_1_sel.hw,
- [CLKID_VDEC_1_1_DIV] = &meson8b_vdec_1_1_div.hw,
- [CLKID_VDEC_1_1] = &meson8b_vdec_1_1.hw,
- [CLKID_VDEC_1_2_DIV] = &meson8b_vdec_1_2_div.hw,
- [CLKID_VDEC_1_2] = &meson8b_vdec_1_2.hw,
- [CLKID_VDEC_1] = &meson8b_vdec_1.hw,
- [CLKID_VDEC_HCODEC_SEL] = &meson8b_vdec_hcodec_sel.hw,
- [CLKID_VDEC_HCODEC_DIV] = &meson8b_vdec_hcodec_div.hw,
- [CLKID_VDEC_HCODEC] = &meson8b_vdec_hcodec.hw,
- [CLKID_VDEC_2_SEL] = &meson8b_vdec_2_sel.hw,
- [CLKID_VDEC_2_DIV] = &meson8b_vdec_2_div.hw,
- [CLKID_VDEC_2] = &meson8b_vdec_2.hw,
- [CLKID_VDEC_HEVC_SEL] = &meson8b_vdec_hevc_sel.hw,
- [CLKID_VDEC_HEVC_DIV] = &meson8b_vdec_hevc_div.hw,
- [CLKID_VDEC_HEVC_EN] = &meson8b_vdec_hevc_en.hw,
- [CLKID_VDEC_HEVC] = &meson8b_vdec_hevc.hw,
- [CLKID_CTS_AMCLK_SEL] = &meson8b_cts_amclk_sel.hw,
- [CLKID_CTS_AMCLK_DIV] = &meson8b_cts_amclk_div.hw,
- [CLKID_CTS_AMCLK] = &meson8b_cts_amclk.hw,
- [CLKID_CTS_MCLK_I958_SEL] = &meson8b_cts_mclk_i958_sel.hw,
- [CLKID_CTS_MCLK_I958_DIV] = &meson8b_cts_mclk_i958_div.hw,
- [CLKID_CTS_MCLK_I958] = &meson8b_cts_mclk_i958.hw,
- [CLKID_CTS_I958] = &meson8b_cts_i958.hw,
- [CLKID_VID_PLL_LVDS_EN] = &meson8b_vid_pll_lvds_en.hw,
- [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw,
- [CLK_NR_CLKS] = NULL,
- },
- .num = CLK_NR_CLKS,
-};
-
-static struct clk_regmap *const meson8b_clk_regmaps[] = {
- &meson8b_clk81,
- &meson8b_ddr,
- &meson8b_dos,
- &meson8b_isa,
- &meson8b_pl301,
- &meson8b_periphs,
- &meson8b_spicc,
- &meson8b_i2c,
- &meson8b_sar_adc,
- &meson8b_smart_card,
- &meson8b_rng0,
- &meson8b_uart0,
- &meson8b_sdhc,
- &meson8b_stream,
- &meson8b_async_fifo,
- &meson8b_sdio,
- &meson8b_abuf,
- &meson8b_hiu_iface,
- &meson8b_assist_misc,
- &meson8b_spi,
- &meson8b_i2s_spdif,
- &meson8b_eth,
- &meson8b_demux,
- &meson8b_aiu_glue,
- &meson8b_iec958,
- &meson8b_i2s_out,
- &meson8b_amclk,
- &meson8b_aififo2,
- &meson8b_mixer,
- &meson8b_mixer_iface,
- &meson8b_adc,
- &meson8b_blkmv,
- &meson8b_aiu,
- &meson8b_uart1,
- &meson8b_g2d,
- &meson8b_usb0,
- &meson8b_usb1,
- &meson8b_reset,
- &meson8b_nand,
- &meson8b_dos_parser,
- &meson8b_usb,
- &meson8b_vdin1,
- &meson8b_ahb_arb0,
- &meson8b_efuse,
- &meson8b_boot_rom,
- &meson8b_ahb_data_bus,
- &meson8b_ahb_ctrl_bus,
- &meson8b_hdmi_intr_sync,
- &meson8b_hdmi_pclk,
- &meson8b_usb1_ddr_bridge,
- &meson8b_usb0_ddr_bridge,
- &meson8b_mmc_pclk,
- &meson8b_dvin,
- &meson8b_uart2,
- &meson8b_sana,
- &meson8b_vpu_intr,
- &meson8b_sec_ahb_ahb3_bridge,
- &meson8b_clk81_a9,
- &meson8b_vclk2_venci0,
- &meson8b_vclk2_venci1,
- &meson8b_vclk2_vencp0,
- &meson8b_vclk2_vencp1,
- &meson8b_gclk_venci_int,
- &meson8b_gclk_vencp_int,
- &meson8b_dac_clk,
- &meson8b_aoclk_gate,
- &meson8b_iec958_gate,
- &meson8b_enc480p,
- &meson8b_rng1,
- &meson8b_gclk_vencl_int,
- &meson8b_vclk2_venclmcc,
- &meson8b_vclk2_vencl,
- &meson8b_vclk2_other,
- &meson8b_edp,
- &meson8b_ao_media_cpu,
- &meson8b_ao_ahb_sram,
- &meson8b_ao_ahb_bus,
- &meson8b_ao_iface,
- &meson8b_mpeg_clk_div,
- &meson8b_mpeg_clk_sel,
- &meson8b_mpll0,
- &meson8b_mpll1,
- &meson8b_mpll2,
- &meson8b_mpll0_div,
- &meson8b_mpll1_div,
- &meson8b_mpll2_div,
- &meson8b_fixed_pll,
- &meson8b_sys_pll,
- &meson8b_cpu_in_sel,
- &meson8b_cpu_scale_div,
- &meson8b_cpu_scale_out_sel,
- &meson8b_cpu_clk,
- &meson8b_mpll_prediv,
- &meson8b_fclk_div2,
- &meson8b_fclk_div3,
- &meson8b_fclk_div4,
- &meson8b_fclk_div5,
- &meson8b_fclk_div7,
- &meson8b_nand_clk_sel,
- &meson8b_nand_clk_div,
- &meson8b_nand_clk_gate,
- &meson8b_fixed_pll_dco,
- &meson8b_hdmi_pll_dco,
- &meson8b_sys_pll_dco,
- &meson8b_apb_clk_sel,
- &meson8b_apb_clk_gate,
- &meson8b_periph_clk_sel,
- &meson8b_periph_clk_gate,
- &meson8b_axi_clk_sel,
- &meson8b_axi_clk_gate,
- &meson8b_l2_dram_clk_sel,
- &meson8b_l2_dram_clk_gate,
- &meson8b_hdmi_pll_lvds_out,
- &meson8b_hdmi_pll_hdmi_out,
- &meson8b_vid_pll_in_sel,
- &meson8b_vid_pll_in_en,
- &meson8b_vid_pll_pre_div,
- &meson8b_vid_pll_post_div,
- &meson8b_vid_pll,
- &meson8b_vid_pll_final_div,
- &meson8b_vclk_in_sel,
- &meson8b_vclk_in_en,
- &meson8b_vclk_en,
- &meson8b_vclk_div1_gate,
- &meson8b_vclk_div2_div_gate,
- &meson8b_vclk_div4_div_gate,
- &meson8b_vclk_div6_div_gate,
- &meson8b_vclk_div12_div_gate,
- &meson8b_vclk2_in_sel,
- &meson8b_vclk2_clk_in_en,
- &meson8b_vclk2_clk_en,
- &meson8b_vclk2_div1_gate,
- &meson8b_vclk2_div2_div_gate,
- &meson8b_vclk2_div4_div_gate,
- &meson8b_vclk2_div6_div_gate,
- &meson8b_vclk2_div12_div_gate,
- &meson8b_cts_enct_sel,
- &meson8b_cts_enct,
- &meson8b_cts_encp_sel,
- &meson8b_cts_encp,
- &meson8b_cts_enci_sel,
- &meson8b_cts_enci,
- &meson8b_hdmi_tx_pixel_sel,
- &meson8b_hdmi_tx_pixel,
- &meson8b_cts_encl_sel,
- &meson8b_cts_encl,
- &meson8b_cts_vdac0_sel,
- &meson8b_cts_vdac0,
- &meson8b_hdmi_sys_sel,
- &meson8b_hdmi_sys_div,
- &meson8b_hdmi_sys,
- &meson8b_mali_0_sel,
- &meson8b_mali_0_div,
- &meson8b_mali_0,
- &meson8b_mali_1_sel,
- &meson8b_mali_1_div,
- &meson8b_mali_1,
- &meson8b_mali,
- &meson8m2_gp_pll_dco,
- &meson8m2_gp_pll,
- &meson8b_vpu_0_sel,
- &meson8m2_vpu_0_sel,
- &meson8b_vpu_0_div,
- &meson8b_vpu_0,
- &meson8b_vpu_1_sel,
- &meson8m2_vpu_1_sel,
- &meson8b_vpu_1_div,
- &meson8b_vpu_1,
- &meson8b_vpu,
- &meson8b_vdec_1_sel,
- &meson8b_vdec_1_1_div,
- &meson8b_vdec_1_1,
- &meson8b_vdec_1_2_div,
- &meson8b_vdec_1_2,
- &meson8b_vdec_1,
- &meson8b_vdec_hcodec_sel,
- &meson8b_vdec_hcodec_div,
- &meson8b_vdec_hcodec,
- &meson8b_vdec_2_sel,
- &meson8b_vdec_2_div,
- &meson8b_vdec_2,
- &meson8b_vdec_hevc_sel,
- &meson8b_vdec_hevc_div,
- &meson8b_vdec_hevc_en,
- &meson8b_vdec_hevc,
- &meson8b_cts_amclk,
- &meson8b_cts_amclk_sel,
- &meson8b_cts_amclk_div,
- &meson8b_cts_mclk_i958_sel,
- &meson8b_cts_mclk_i958_div,
- &meson8b_cts_mclk_i958,
- &meson8b_cts_i958,
- &meson8b_vid_pll_lvds_en,
+static MESON8B_PCLK(meson8b_ao_media_cpu, HHI_GCLK_AO, 0, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2, CLK_IGNORE_UNUSED);
+static MESON8B_PCLK(meson8b_ao_iface, HHI_GCLK_AO, 3, CLK_IGNORE_UNUSED);
+
+static struct clk_hw *meson8_hw_clks[] = {
+ [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
+ [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
+ [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
+ [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
+ [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
+ [CLKID_MPEG_SEL] = &meson8b_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &meson8b_clk81_div.hw,
+ [CLKID_CLK81] = &meson8b_clk81.hw,
+ [CLKID_DDR] = &meson8b_ddr.hw,
+ [CLKID_DOS] = &meson8b_dos.hw,
+ [CLKID_ISA] = &meson8b_isa.hw,
+ [CLKID_PL301] = &meson8b_pl301.hw,
+ [CLKID_PERIPHS] = &meson8b_periphs.hw,
+ [CLKID_SPICC] = &meson8b_spicc.hw,
+ [CLKID_I2C] = &meson8b_i2c.hw,
+ [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
+ [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
+ [CLKID_RNG0] = &meson8b_rng0.hw,
+ [CLKID_UART0] = &meson8b_uart0.hw,
+ [CLKID_SDHC] = &meson8b_sdhc.hw,
+ [CLKID_STREAM] = &meson8b_stream.hw,
+ [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
+ [CLKID_SDIO] = &meson8b_sdio.hw,
+ [CLKID_ABUF] = &meson8b_abuf.hw,
+ [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
+ [CLKID_SPI] = &meson8b_spi.hw,
+ [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
+ [CLKID_ETH] = &meson8b_eth.hw,
+ [CLKID_DEMUX] = &meson8b_demux.hw,
+ [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
+ [CLKID_IEC958] = &meson8b_iec958.hw,
+ [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
+ [CLKID_AMCLK] = &meson8b_amclk.hw,
+ [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
+ [CLKID_MIXER] = &meson8b_mixer.hw,
+ [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
+ [CLKID_ADC] = &meson8b_adc.hw,
+ [CLKID_BLKMV] = &meson8b_blkmv.hw,
+ [CLKID_AIU] = &meson8b_aiu.hw,
+ [CLKID_UART1] = &meson8b_uart1.hw,
+ [CLKID_G2D] = &meson8b_g2d.hw,
+ [CLKID_USB0] = &meson8b_usb0.hw,
+ [CLKID_USB1] = &meson8b_usb1.hw,
+ [CLKID_RESET] = &meson8b_reset.hw,
+ [CLKID_NAND] = &meson8b_nand.hw,
+ [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
+ [CLKID_USB] = &meson8b_usb.hw,
+ [CLKID_VDIN1] = &meson8b_vdin1.hw,
+ [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
+ [CLKID_EFUSE] = &meson8b_efuse.hw,
+ [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
+ [CLKID_DVIN] = &meson8b_dvin.hw,
+ [CLKID_UART2] = &meson8b_uart2.hw,
+ [CLKID_SANA] = &meson8b_sana.hw,
+ [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
+ [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
+ [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
+ [CLKID_ENC480P] = &meson8b_enc480p.hw,
+ [CLKID_RNG1] = &meson8b_rng1.hw,
+ [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
+ [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
+ [CLKID_EDP] = &meson8b_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
+ [CLKID_MPLL0] = &meson8b_mpll0.hw,
+ [CLKID_MPLL1] = &meson8b_mpll1.hw,
+ [CLKID_MPLL2] = &meson8b_mpll2.hw,
+ [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
+ [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
+ [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
+ [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
+ [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
+ [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
+ [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
+ [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
+ [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
+ [CLKID_NAND_CLK] = &meson8b_nand_clk.hw,
+ [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
+ [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
+ [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
+ [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
+ [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
+ [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
+ [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
+ [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
+ [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
+ [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
+ [CLKID_APB_SEL] = &meson8b_apb_sel.hw,
+ [CLKID_APB] = &meson8b_apb.hw,
+ [CLKID_PERIPH_SEL] = &meson8b_periph_sel.hw,
+ [CLKID_PERIPH] = &meson8b_periph.hw,
+ [CLKID_AXI_SEL] = &meson8b_axi_sel.hw,
+ [CLKID_AXI] = &meson8b_axi.hw,
+ [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_sel.hw,
+ [CLKID_L2_DRAM] = &meson8b_l2_dram.hw,
+ [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
+ [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
+ [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
+ [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
+ [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
+ [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
+ [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
+ [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
+ [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
+ [CLKID_VCLK_EN] = &meson8b_vclk_en.hw,
+ [CLKID_VCLK_DIV1] = &meson8b_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
+ [CLKID_VCLK_DIV2] = &meson8b_vclk_div2.hw,
+ [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
+ [CLKID_VCLK_DIV4] = &meson8b_vclk_div4.hw,
+ [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
+ [CLKID_VCLK_DIV6] = &meson8b_vclk_div6.hw,
+ [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
+ [CLKID_VCLK_DIV12] = &meson8b_vclk_div12.hw,
+ [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
+ [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_in_en.hw,
+ [CLKID_VCLK2_EN] = &meson8b_vclk2_en.hw,
+ [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
+ [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
+ [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
+ [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
+ [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12.hw,
+ [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
+ [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
+ [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
+ [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
+ [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
+ [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
+ [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
+ [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
+ [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
+ [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
+ [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
+ [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
+ [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
+ [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
+ [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
+ [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
+ [CLKID_MALI] = &meson8b_mali_0.hw,
+ [CLKID_VPU_0_SEL] = &meson8b_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &meson8b_vpu_0_div.hw,
+ [CLKID_VPU] = &meson8b_vpu_0.hw,
+ [CLKID_VDEC_1_SEL] = &meson8b_vdec_1_sel.hw,
+ [CLKID_VDEC_1_1_DIV] = &meson8b_vdec_1_1_div.hw,
+ [CLKID_VDEC_1] = &meson8b_vdec_1_1.hw,
+ [CLKID_VDEC_HCODEC_SEL] = &meson8b_vdec_hcodec_sel.hw,
+ [CLKID_VDEC_HCODEC_DIV] = &meson8b_vdec_hcodec_div.hw,
+ [CLKID_VDEC_HCODEC] = &meson8b_vdec_hcodec.hw,
+ [CLKID_VDEC_2_SEL] = &meson8b_vdec_2_sel.hw,
+ [CLKID_VDEC_2_DIV] = &meson8b_vdec_2_div.hw,
+ [CLKID_VDEC_2] = &meson8b_vdec_2.hw,
+ [CLKID_VDEC_HEVC_SEL] = &meson8b_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &meson8b_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC_EN] = &meson8b_vdec_hevc_en.hw,
+ [CLKID_VDEC_HEVC] = &meson8b_vdec_hevc.hw,
+ [CLKID_CTS_AMCLK_SEL] = &meson8b_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &meson8b_cts_amclk_div.hw,
+ [CLKID_CTS_AMCLK] = &meson8b_cts_amclk.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &meson8b_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &meson8b_cts_mclk_i958_div.hw,
+ [CLKID_CTS_MCLK_I958] = &meson8b_cts_mclk_i958.hw,
+ [CLKID_CTS_I958] = &meson8b_cts_i958.hw,
+ [CLKID_VID_PLL_LVDS_EN] = &meson8b_vid_pll_lvds_en.hw,
+ [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw,
+};
+
+static struct clk_hw *meson8b_hw_clks[] = {
+ [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
+ [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
+ [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
+ [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
+ [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
+ [CLKID_MPEG_SEL] = &meson8b_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &meson8b_clk81_div.hw,
+ [CLKID_CLK81] = &meson8b_clk81.hw,
+ [CLKID_DDR] = &meson8b_ddr.hw,
+ [CLKID_DOS] = &meson8b_dos.hw,
+ [CLKID_ISA] = &meson8b_isa.hw,
+ [CLKID_PL301] = &meson8b_pl301.hw,
+ [CLKID_PERIPHS] = &meson8b_periphs.hw,
+ [CLKID_SPICC] = &meson8b_spicc.hw,
+ [CLKID_I2C] = &meson8b_i2c.hw,
+ [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
+ [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
+ [CLKID_RNG0] = &meson8b_rng0.hw,
+ [CLKID_UART0] = &meson8b_uart0.hw,
+ [CLKID_SDHC] = &meson8b_sdhc.hw,
+ [CLKID_STREAM] = &meson8b_stream.hw,
+ [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
+ [CLKID_SDIO] = &meson8b_sdio.hw,
+ [CLKID_ABUF] = &meson8b_abuf.hw,
+ [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
+ [CLKID_SPI] = &meson8b_spi.hw,
+ [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
+ [CLKID_ETH] = &meson8b_eth.hw,
+ [CLKID_DEMUX] = &meson8b_demux.hw,
+ [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
+ [CLKID_IEC958] = &meson8b_iec958.hw,
+ [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
+ [CLKID_AMCLK] = &meson8b_amclk.hw,
+ [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
+ [CLKID_MIXER] = &meson8b_mixer.hw,
+ [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
+ [CLKID_ADC] = &meson8b_adc.hw,
+ [CLKID_BLKMV] = &meson8b_blkmv.hw,
+ [CLKID_AIU] = &meson8b_aiu.hw,
+ [CLKID_UART1] = &meson8b_uart1.hw,
+ [CLKID_G2D] = &meson8b_g2d.hw,
+ [CLKID_USB0] = &meson8b_usb0.hw,
+ [CLKID_USB1] = &meson8b_usb1.hw,
+ [CLKID_RESET] = &meson8b_reset.hw,
+ [CLKID_NAND] = &meson8b_nand.hw,
+ [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
+ [CLKID_USB] = &meson8b_usb.hw,
+ [CLKID_VDIN1] = &meson8b_vdin1.hw,
+ [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
+ [CLKID_EFUSE] = &meson8b_efuse.hw,
+ [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
+ [CLKID_DVIN] = &meson8b_dvin.hw,
+ [CLKID_UART2] = &meson8b_uart2.hw,
+ [CLKID_SANA] = &meson8b_sana.hw,
+ [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
+ [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
+ [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
+ [CLKID_ENC480P] = &meson8b_enc480p.hw,
+ [CLKID_RNG1] = &meson8b_rng1.hw,
+ [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
+ [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
+ [CLKID_EDP] = &meson8b_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
+ [CLKID_MPLL0] = &meson8b_mpll0.hw,
+ [CLKID_MPLL1] = &meson8b_mpll1.hw,
+ [CLKID_MPLL2] = &meson8b_mpll2.hw,
+ [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
+ [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
+ [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
+ [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
+ [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
+ [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
+ [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
+ [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
+ [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
+ [CLKID_NAND_CLK] = &meson8b_nand_clk.hw,
+ [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
+ [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
+ [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
+ [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
+ [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
+ [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
+ [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
+ [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
+ [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
+ [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
+ [CLKID_APB_SEL] = &meson8b_apb_sel.hw,
+ [CLKID_APB] = &meson8b_apb.hw,
+ [CLKID_PERIPH_SEL] = &meson8b_periph_sel.hw,
+ [CLKID_PERIPH] = &meson8b_periph.hw,
+ [CLKID_AXI_SEL] = &meson8b_axi_sel.hw,
+ [CLKID_AXI] = &meson8b_axi.hw,
+ [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_sel.hw,
+ [CLKID_L2_DRAM] = &meson8b_l2_dram.hw,
+ [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
+ [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
+ [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
+ [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
+ [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
+ [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
+ [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
+ [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
+ [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
+ [CLKID_VCLK_EN] = &meson8b_vclk_en.hw,
+ [CLKID_VCLK_DIV1] = &meson8b_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
+ [CLKID_VCLK_DIV2] = &meson8b_vclk_div2.hw,
+ [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
+ [CLKID_VCLK_DIV4] = &meson8b_vclk_div4.hw,
+ [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
+ [CLKID_VCLK_DIV6] = &meson8b_vclk_div6.hw,
+ [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
+ [CLKID_VCLK_DIV12] = &meson8b_vclk_div12.hw,
+ [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
+ [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_in_en.hw,
+ [CLKID_VCLK2_EN] = &meson8b_vclk2_en.hw,
+ [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
+ [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
+ [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
+ [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
+ [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12.hw,
+ [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
+ [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
+ [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
+ [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
+ [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
+ [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
+ [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
+ [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
+ [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
+ [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
+ [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
+ [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
+ [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
+ [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
+ [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
+ [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
+ [CLKID_MALI_0] = &meson8b_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw,
+ [CLKID_MALI_1] = &meson8b_mali_1.hw,
+ [CLKID_MALI] = &meson8b_mali.hw,
+ [CLKID_VPU_0_SEL] = &meson8b_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &meson8b_vpu_0_div.hw,
+ [CLKID_VPU_0] = &meson8b_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &meson8b_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &meson8b_vpu_1_div.hw,
+ [CLKID_VPU_1] = &meson8b_vpu_1.hw,
+ [CLKID_VPU] = &meson8b_vpu.hw,
+ [CLKID_VDEC_1_SEL] = &meson8b_vdec_1_sel.hw,
+ [CLKID_VDEC_1_1_DIV] = &meson8b_vdec_1_1_div.hw,
+ [CLKID_VDEC_1_1] = &meson8b_vdec_1_1.hw,
+ [CLKID_VDEC_1_2_DIV] = &meson8b_vdec_1_2_div.hw,
+ [CLKID_VDEC_1_2] = &meson8b_vdec_1_2.hw,
+ [CLKID_VDEC_1] = &meson8b_vdec_1.hw,
+ [CLKID_VDEC_HCODEC_SEL] = &meson8b_vdec_hcodec_sel.hw,
+ [CLKID_VDEC_HCODEC_DIV] = &meson8b_vdec_hcodec_div.hw,
+ [CLKID_VDEC_HCODEC] = &meson8b_vdec_hcodec.hw,
+ [CLKID_VDEC_2_SEL] = &meson8b_vdec_2_sel.hw,
+ [CLKID_VDEC_2_DIV] = &meson8b_vdec_2_div.hw,
+ [CLKID_VDEC_2] = &meson8b_vdec_2.hw,
+ [CLKID_VDEC_HEVC_SEL] = &meson8b_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &meson8b_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC_EN] = &meson8b_vdec_hevc_en.hw,
+ [CLKID_VDEC_HEVC] = &meson8b_vdec_hevc.hw,
+ [CLKID_CTS_AMCLK_SEL] = &meson8b_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &meson8b_cts_amclk_div.hw,
+ [CLKID_CTS_AMCLK] = &meson8b_cts_amclk.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &meson8b_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &meson8b_cts_mclk_i958_div.hw,
+ [CLKID_CTS_MCLK_I958] = &meson8b_cts_mclk_i958.hw,
+ [CLKID_CTS_I958] = &meson8b_cts_i958.hw,
+ [CLKID_VID_PLL_LVDS_EN] = &meson8b_vid_pll_lvds_en.hw,
+ [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw,
+};
+
+static struct clk_hw *meson8m2_hw_clks[] = {
+ [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw,
+ [CLKID_PLL_VID] = &meson8b_vid_pll.hw,
+ [CLKID_PLL_SYS] = &meson8b_sys_pll.hw,
+ [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw,
+ [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw,
+ [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw,
+ [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw,
+ [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw,
+ [CLKID_CPUCLK] = &meson8b_cpu_clk.hw,
+ [CLKID_MPEG_SEL] = &meson8b_clk81_sel.hw,
+ [CLKID_MPEG_DIV] = &meson8b_clk81_div.hw,
+ [CLKID_CLK81] = &meson8b_clk81.hw,
+ [CLKID_DDR] = &meson8b_ddr.hw,
+ [CLKID_DOS] = &meson8b_dos.hw,
+ [CLKID_ISA] = &meson8b_isa.hw,
+ [CLKID_PL301] = &meson8b_pl301.hw,
+ [CLKID_PERIPHS] = &meson8b_periphs.hw,
+ [CLKID_SPICC] = &meson8b_spicc.hw,
+ [CLKID_I2C] = &meson8b_i2c.hw,
+ [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
+ [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
+ [CLKID_RNG0] = &meson8b_rng0.hw,
+ [CLKID_UART0] = &meson8b_uart0.hw,
+ [CLKID_SDHC] = &meson8b_sdhc.hw,
+ [CLKID_STREAM] = &meson8b_stream.hw,
+ [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
+ [CLKID_SDIO] = &meson8b_sdio.hw,
+ [CLKID_ABUF] = &meson8b_abuf.hw,
+ [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
+ [CLKID_SPI] = &meson8b_spi.hw,
+ [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
+ [CLKID_ETH] = &meson8b_eth.hw,
+ [CLKID_DEMUX] = &meson8b_demux.hw,
+ [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
+ [CLKID_IEC958] = &meson8b_iec958.hw,
+ [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
+ [CLKID_AMCLK] = &meson8b_amclk.hw,
+ [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
+ [CLKID_MIXER] = &meson8b_mixer.hw,
+ [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
+ [CLKID_ADC] = &meson8b_adc.hw,
+ [CLKID_BLKMV] = &meson8b_blkmv.hw,
+ [CLKID_AIU] = &meson8b_aiu.hw,
+ [CLKID_UART1] = &meson8b_uart1.hw,
+ [CLKID_G2D] = &meson8b_g2d.hw,
+ [CLKID_USB0] = &meson8b_usb0.hw,
+ [CLKID_USB1] = &meson8b_usb1.hw,
+ [CLKID_RESET] = &meson8b_reset.hw,
+ [CLKID_NAND] = &meson8b_nand.hw,
+ [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
+ [CLKID_USB] = &meson8b_usb.hw,
+ [CLKID_VDIN1] = &meson8b_vdin1.hw,
+ [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
+ [CLKID_EFUSE] = &meson8b_efuse.hw,
+ [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
+ [CLKID_DVIN] = &meson8b_dvin.hw,
+ [CLKID_UART2] = &meson8b_uart2.hw,
+ [CLKID_SANA] = &meson8b_sana.hw,
+ [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
+ [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
+ [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
+ [CLKID_ENC480P] = &meson8b_enc480p.hw,
+ [CLKID_RNG1] = &meson8b_rng1.hw,
+ [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
+ [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
+ [CLKID_EDP] = &meson8b_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
+ [CLKID_MPLL0] = &meson8b_mpll0.hw,
+ [CLKID_MPLL1] = &meson8b_mpll1.hw,
+ [CLKID_MPLL2] = &meson8b_mpll2.hw,
+ [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
+ [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
+ [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw,
+ [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw,
+ [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
+ [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
+ [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
+ [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw,
+ [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw,
+ [CLKID_NAND_CLK] = &meson8b_nand_clk.hw,
+ [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw,
+ [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw,
+ [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw,
+ [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw,
+ [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw,
+ [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw,
+ [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw,
+ [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw,
+ [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw,
+ [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw,
+ [CLKID_APB_SEL] = &meson8b_apb_sel.hw,
+ [CLKID_APB] = &meson8b_apb.hw,
+ [CLKID_PERIPH_SEL] = &meson8b_periph_sel.hw,
+ [CLKID_PERIPH] = &meson8b_periph.hw,
+ [CLKID_AXI_SEL] = &meson8b_axi_sel.hw,
+ [CLKID_AXI] = &meson8b_axi.hw,
+ [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_sel.hw,
+ [CLKID_L2_DRAM] = &meson8b_l2_dram.hw,
+ [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw,
+ [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw,
+ [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw,
+ [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw,
+ [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw,
+ [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw,
+ [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw,
+ [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw,
+ [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw,
+ [CLKID_VCLK_EN] = &meson8b_vclk_en.hw,
+ [CLKID_VCLK_DIV1] = &meson8b_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw,
+ [CLKID_VCLK_DIV2] = &meson8b_vclk_div2.hw,
+ [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw,
+ [CLKID_VCLK_DIV4] = &meson8b_vclk_div4.hw,
+ [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw,
+ [CLKID_VCLK_DIV6] = &meson8b_vclk_div6.hw,
+ [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw,
+ [CLKID_VCLK_DIV12] = &meson8b_vclk_div12.hw,
+ [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw,
+ [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_in_en.hw,
+ [CLKID_VCLK2_EN] = &meson8b_vclk2_en.hw,
+ [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw,
+ [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw,
+ [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw,
+ [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw,
+ [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12.hw,
+ [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw,
+ [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw,
+ [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw,
+ [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw,
+ [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw,
+ [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw,
+ [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw,
+ [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw,
+ [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw,
+ [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw,
+ [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw,
+ [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw,
+ [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw,
+ [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw,
+ [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw,
+ [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw,
+ [CLKID_MALI_0] = &meson8b_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw,
+ [CLKID_MALI_1] = &meson8b_mali_1.hw,
+ [CLKID_MALI] = &meson8b_mali.hw,
+ [CLKID_GP_PLL_DCO] = &meson8m2_gp_pll_dco.hw,
+ [CLKID_GP_PLL] = &meson8m2_gp_pll.hw,
+ [CLKID_VPU_0_SEL] = &meson8m2_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &meson8b_vpu_0_div.hw,
+ [CLKID_VPU_0] = &meson8b_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &meson8m2_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &meson8b_vpu_1_div.hw,
+ [CLKID_VPU_1] = &meson8b_vpu_1.hw,
+ [CLKID_VPU] = &meson8b_vpu.hw,
+ [CLKID_VDEC_1_SEL] = &meson8b_vdec_1_sel.hw,
+ [CLKID_VDEC_1_1_DIV] = &meson8b_vdec_1_1_div.hw,
+ [CLKID_VDEC_1_1] = &meson8b_vdec_1_1.hw,
+ [CLKID_VDEC_1_2_DIV] = &meson8b_vdec_1_2_div.hw,
+ [CLKID_VDEC_1_2] = &meson8b_vdec_1_2.hw,
+ [CLKID_VDEC_1] = &meson8b_vdec_1.hw,
+ [CLKID_VDEC_HCODEC_SEL] = &meson8b_vdec_hcodec_sel.hw,
+ [CLKID_VDEC_HCODEC_DIV] = &meson8b_vdec_hcodec_div.hw,
+ [CLKID_VDEC_HCODEC] = &meson8b_vdec_hcodec.hw,
+ [CLKID_VDEC_2_SEL] = &meson8b_vdec_2_sel.hw,
+ [CLKID_VDEC_2_DIV] = &meson8b_vdec_2_div.hw,
+ [CLKID_VDEC_2] = &meson8b_vdec_2.hw,
+ [CLKID_VDEC_HEVC_SEL] = &meson8b_vdec_hevc_sel.hw,
+ [CLKID_VDEC_HEVC_DIV] = &meson8b_vdec_hevc_div.hw,
+ [CLKID_VDEC_HEVC_EN] = &meson8b_vdec_hevc_en.hw,
+ [CLKID_VDEC_HEVC] = &meson8b_vdec_hevc.hw,
+ [CLKID_CTS_AMCLK_SEL] = &meson8b_cts_amclk_sel.hw,
+ [CLKID_CTS_AMCLK_DIV] = &meson8b_cts_amclk_div.hw,
+ [CLKID_CTS_AMCLK] = &meson8b_cts_amclk.hw,
+ [CLKID_CTS_MCLK_I958_SEL] = &meson8b_cts_mclk_i958_sel.hw,
+ [CLKID_CTS_MCLK_I958_DIV] = &meson8b_cts_mclk_i958_div.hw,
+ [CLKID_CTS_MCLK_I958] = &meson8b_cts_mclk_i958.hw,
+ [CLKID_CTS_I958] = &meson8b_cts_i958.hw,
+ [CLKID_VID_PLL_LVDS_EN] = &meson8b_vid_pll_lvds_en.hw,
+ [CLKID_HDMI_PLL_DCO_IN] = &hdmi_pll_dco_in.hw,
};
static const struct meson8b_clk_reset_line {
@@ -3710,7 +3540,6 @@ static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev,
container_of(rcdev, struct meson8b_clk_reset, reset);
const struct meson8b_clk_reset_line *reset;
unsigned int value = 0;
- unsigned long flags;
if (id >= ARRAY_SIZE(meson8b_clk_reset_bits))
return -EINVAL;
@@ -3720,13 +3549,9 @@ static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev,
if (assert != reset->active_low)
value = BIT(reset->bit_idx);
- spin_lock_irqsave(&meson_clk_lock, flags);
-
regmap_update_bits(meson8b_clk_reset->regmap, reset->reg,
BIT(reset->bit_idx), value);
- spin_unlock_irqrestore(&meson_clk_lock, flags);
-
return 0;
}
@@ -3788,16 +3613,34 @@ static struct meson8b_nb_data meson8b_cpu_nb_data = {
.nb.notifier_call = meson8b_cpu_clk_notifier_cb,
};
+static struct meson_clk_hw_data meson8_clks = {
+ .hws = meson8_hw_clks,
+ .num = ARRAY_SIZE(meson8_hw_clks),
+};
+
+static struct meson_clk_hw_data meson8b_clks = {
+ .hws = meson8b_hw_clks,
+ .num = ARRAY_SIZE(meson8b_hw_clks),
+};
+
+static struct meson_clk_hw_data meson8m2_clks = {
+ .hws = meson8m2_hw_clks,
+ .num = ARRAY_SIZE(meson8m2_hw_clks),
+};
+
static void __init meson8b_clkc_init_common(struct device_node *np,
- struct clk_hw_onecell_data *clk_hw_onecell_data)
+ struct meson_clk_hw_data *hw_clks)
{
struct meson8b_clk_reset *rstc;
+ struct device_node *parent_np;
const char *notifier_clk_name;
struct clk *notifier_clk;
struct regmap *map;
int i, ret;
- map = syscon_node_to_regmap(of_get_parent(np));
+ parent_np = of_get_parent(np);
+ map = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
if (IS_ERR(map)) {
pr_err("failed to get HHI regmap - Trying obsolete regs\n");
return;
@@ -3819,25 +3662,21 @@ static void __init meson8b_clkc_init_common(struct device_node *np,
return;
}
- /* Populate regmap for the regmap backed clocks */
- for (i = 0; i < ARRAY_SIZE(meson8b_clk_regmaps); i++)
- meson8b_clk_regmaps[i]->map = map;
-
/*
* register all clks and start with the first used ID (which is
* CLKID_PLL_FIXED)
*/
- for (i = CLKID_PLL_FIXED; i < CLK_NR_CLKS; i++) {
+ for (i = CLKID_PLL_FIXED; i < hw_clks->num; i++) {
/* array might be sparse */
- if (!clk_hw_onecell_data->hws[i])
+ if (!hw_clks->hws[i])
continue;
- ret = of_clk_hw_register(np, clk_hw_onecell_data->hws[i]);
+ ret = of_clk_hw_register(np, hw_clks->hws[i]);
if (ret)
return;
}
- meson8b_cpu_nb_data.cpu_clk = clk_hw_onecell_data->hws[CLKID_CPUCLK];
+ meson8b_cpu_nb_data.cpu_clk = hw_clks->hws[CLKID_CPUCLK];
/*
* FIXME we shouldn't program the muxes in notifier handlers. The
@@ -3853,25 +3692,24 @@ static void __init meson8b_clkc_init_common(struct device_node *np,
return;
}
- ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
- clk_hw_onecell_data);
+ ret = of_clk_add_hw_provider(np, meson_clk_hw_get, hw_clks);
if (ret)
pr_err("%s: failed to register clock provider\n", __func__);
}
static void __init meson8_clkc_init(struct device_node *np)
{
- return meson8b_clkc_init_common(np, &meson8_hw_onecell_data);
+ return meson8b_clkc_init_common(np, &meson8_clks);
}
static void __init meson8b_clkc_init(struct device_node *np)
{
- return meson8b_clkc_init_common(np, &meson8b_hw_onecell_data);
+ return meson8b_clkc_init_common(np, &meson8b_clks);
}
static void __init meson8m2_clkc_init(struct device_node *np)
{
- return meson8b_clkc_init_common(np, &meson8m2_hw_onecell_data);
+ return meson8b_clkc_init_common(np, &meson8m2_clks);
}
CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc",
diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h
deleted file mode 100644
index ce62ed47cbfc..000000000000
--- a/drivers/clk/meson/meson8b.h
+++ /dev/null
@@ -1,197 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2015 Endless Mobile, Inc.
- * Author: Carlo Caione <carlo@endlessm.com>
- *
- * Copyright (c) 2016 BayLibre, Inc.
- * Michael Turquette <mturquette@baylibre.com>
- */
-
-#ifndef __MESON8B_H
-#define __MESON8B_H
-
-/*
- * Clock controller register offsets
- *
- * Register offsets from the HardKernel[0] data sheet are listed in comment
- * blocks below. Those offsets must be multiplied by 4 before adding them to
- * the base address to get the right value
- *
- * [0] https://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf
- */
-#define HHI_GP_PLL_CNTL 0x40 /* 0x10 offset in data sheet */
-#define HHI_GP_PLL_CNTL2 0x44 /* 0x11 offset in data sheet */
-#define HHI_GP_PLL_CNTL3 0x48 /* 0x12 offset in data sheet */
-#define HHI_GP_PLL_CNTL4 0x4C /* 0x13 offset in data sheet */
-#define HHI_GP_PLL_CNTL5 0x50 /* 0x14 offset in data sheet */
-#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */
-#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */
-#define HHI_GCLK_MPEG0 0x140 /* 0x50 offset in data sheet */
-#define HHI_GCLK_MPEG1 0x144 /* 0x51 offset in data sheet */
-#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */
-#define HHI_GCLK_OTHER 0x150 /* 0x54 offset in data sheet */
-#define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */
-#define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */
-#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */
-#define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */
-#define HHI_AUD_CLK_CNTL 0x178 /* 0x5e offset in data sheet */
-#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */
-#define HHI_AUD_CLK_CNTL2 0x190 /* 0x64 offset in data sheet */
-#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */
-#define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */
-#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */
-#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */
-#define HHI_VPU_CLK_CNTL 0x1bc /* 0x6f offset in data sheet */
-#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */
-#define HHI_VDEC_CLK_CNTL 0x1e0 /* 0x78 offset in data sheet */
-#define HHI_VDEC2_CLK_CNTL 0x1e4 /* 0x79 offset in data sheet */
-#define HHI_VDEC3_CLK_CNTL 0x1e8 /* 0x7a offset in data sheet */
-#define HHI_NAND_CLK_CNTL 0x25c /* 0x97 offset in data sheet */
-#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */
-#define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */
-#define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */
-#define HHI_VID_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */
-#define HHI_VID_PLL_CNTL3 0x328 /* 0xca offset in data sheet */
-#define HHI_VID_PLL_CNTL4 0x32c /* 0xcb offset in data sheet */
-#define HHI_VID_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */
-#define HHI_VID_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */
-#define HHI_VID2_PLL_CNTL 0x380 /* 0xe0 offset in data sheet */
-#define HHI_VID2_PLL_CNTL2 0x384 /* 0xe1 offset in data sheet */
-#define HHI_VID2_PLL_CNTL3 0x388 /* 0xe2 offset in data sheet */
-#define HHI_VID2_PLL_CNTL4 0x38c /* 0xe3 offset in data sheet */
-#define HHI_VID2_PLL_CNTL5 0x390 /* 0xe4 offset in data sheet */
-#define HHI_VID2_PLL_CNTL6 0x394 /* 0xe5 offset in data sheet */
-
-/*
- * MPLL register offeset taken from the S905 datasheet. Vendor kernel source
- * confirm these are the same for the S805.
- */
-#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */
-#define HHI_MPLL_CNTL2 0x284 /* 0xa1 offset in data sheet */
-#define HHI_MPLL_CNTL3 0x288 /* 0xa2 offset in data sheet */
-#define HHI_MPLL_CNTL4 0x28C /* 0xa3 offset in data sheet */
-#define HHI_MPLL_CNTL5 0x290 /* 0xa4 offset in data sheet */
-#define HHI_MPLL_CNTL6 0x294 /* 0xa5 offset in data sheet */
-#define HHI_MPLL_CNTL7 0x298 /* 0xa6 offset in data sheet */
-#define HHI_MPLL_CNTL8 0x29C /* 0xa7 offset in data sheet */
-#define HHI_MPLL_CNTL9 0x2A0 /* 0xa8 offset in data sheet */
-#define HHI_MPLL_CNTL10 0x2A4 /* 0xa9 offset in data sheet */
-
-/*
- * CLKID index values
- *
- * These indices are entirely contrived and do not map onto the hardware.
- * It has now been decided to expose everything by default in the DT header:
- * include/dt-bindings/clock/gxbb-clkc.h. Only the clocks ids we don't want
- * to expose, such as the internal muxes and dividers of composite clocks,
- * will remain defined here.
- */
-
-#define CLKID_MPLL0_DIV 96
-#define CLKID_MPLL1_DIV 97
-#define CLKID_MPLL2_DIV 98
-#define CLKID_CPU_IN_SEL 99
-#define CLKID_CPU_IN_DIV2 100
-#define CLKID_CPU_IN_DIV3 101
-#define CLKID_CPU_SCALE_DIV 102
-#define CLKID_CPU_SCALE_OUT_SEL 103
-#define CLKID_MPLL_PREDIV 104
-#define CLKID_FCLK_DIV2_DIV 105
-#define CLKID_FCLK_DIV3_DIV 106
-#define CLKID_FCLK_DIV4_DIV 107
-#define CLKID_FCLK_DIV5_DIV 108
-#define CLKID_FCLK_DIV7_DIV 109
-#define CLKID_NAND_SEL 110
-#define CLKID_NAND_DIV 111
-#define CLKID_PLL_FIXED_DCO 113
-#define CLKID_HDMI_PLL_DCO 114
-#define CLKID_PLL_SYS_DCO 115
-#define CLKID_CPU_CLK_DIV2 116
-#define CLKID_CPU_CLK_DIV3 117
-#define CLKID_CPU_CLK_DIV4 118
-#define CLKID_CPU_CLK_DIV5 119
-#define CLKID_CPU_CLK_DIV6 120
-#define CLKID_CPU_CLK_DIV7 121
-#define CLKID_CPU_CLK_DIV8 122
-#define CLKID_APB_SEL 123
-#define CLKID_PERIPH_SEL 125
-#define CLKID_AXI_SEL 127
-#define CLKID_L2_DRAM_SEL 129
-#define CLKID_HDMI_PLL_LVDS_OUT 131
-#define CLKID_VID_PLL_IN_SEL 133
-#define CLKID_VID_PLL_IN_EN 134
-#define CLKID_VID_PLL_PRE_DIV 135
-#define CLKID_VID_PLL_POST_DIV 136
-#define CLKID_VCLK_IN_EN 139
-#define CLKID_VCLK_DIV1 140
-#define CLKID_VCLK_DIV2_DIV 141
-#define CLKID_VCLK_DIV2 142
-#define CLKID_VCLK_DIV4_DIV 143
-#define CLKID_VCLK_DIV4 144
-#define CLKID_VCLK_DIV6_DIV 145
-#define CLKID_VCLK_DIV6 146
-#define CLKID_VCLK_DIV12_DIV 147
-#define CLKID_VCLK_DIV12 148
-#define CLKID_VCLK2_IN_EN 150
-#define CLKID_VCLK2_DIV1 151
-#define CLKID_VCLK2_DIV2_DIV 152
-#define CLKID_VCLK2_DIV2 153
-#define CLKID_VCLK2_DIV4_DIV 154
-#define CLKID_VCLK2_DIV4 155
-#define CLKID_VCLK2_DIV6_DIV 156
-#define CLKID_VCLK2_DIV6 157
-#define CLKID_VCLK2_DIV12_DIV 158
-#define CLKID_VCLK2_DIV12 159
-#define CLKID_CTS_ENCT_SEL 160
-#define CLKID_CTS_ENCP_SEL 162
-#define CLKID_CTS_ENCI_SEL 164
-#define CLKID_HDMI_TX_PIXEL_SEL 166
-#define CLKID_CTS_ENCL_SEL 168
-#define CLKID_CTS_VDAC0_SEL 170
-#define CLKID_HDMI_SYS_SEL 172
-#define CLKID_HDMI_SYS_DIV 173
-#define CLKID_MALI_0_SEL 175
-#define CLKID_MALI_0_DIV 176
-#define CLKID_MALI_0 177
-#define CLKID_MALI_1_SEL 178
-#define CLKID_MALI_1_DIV 179
-#define CLKID_MALI_1 180
-#define CLKID_GP_PLL_DCO 181
-#define CLKID_GP_PLL 182
-#define CLKID_VPU_0_SEL 183
-#define CLKID_VPU_0_DIV 184
-#define CLKID_VPU_0 185
-#define CLKID_VPU_1_SEL 186
-#define CLKID_VPU_1_DIV 187
-#define CLKID_VPU_1 189
-#define CLKID_VDEC_1_SEL 191
-#define CLKID_VDEC_1_1_DIV 192
-#define CLKID_VDEC_1_1 193
-#define CLKID_VDEC_1_2_DIV 194
-#define CLKID_VDEC_1_2 195
-#define CLKID_VDEC_HCODEC_SEL 197
-#define CLKID_VDEC_HCODEC_DIV 198
-#define CLKID_VDEC_2_SEL 200
-#define CLKID_VDEC_2_DIV 201
-#define CLKID_VDEC_HEVC_SEL 203
-#define CLKID_VDEC_HEVC_DIV 204
-#define CLKID_VDEC_HEVC_EN 205
-#define CLKID_CTS_AMCLK_SEL 207
-#define CLKID_CTS_AMCLK_DIV 208
-#define CLKID_CTS_MCLK_I958_SEL 210
-#define CLKID_CTS_MCLK_I958_DIV 211
-#define CLKID_VCLK_EN 214
-#define CLKID_VCLK2_EN 215
-#define CLKID_VID_PLL_LVDS_EN 216
-#define CLKID_HDMI_PLL_DCO_IN 217
-
-#define CLK_NR_CLKS 218
-
-/*
- * include the CLKID and RESETID that have
- * been made part of the stable DT binding
- */
-#include <dt-bindings/clock/meson8b-clkc.h>
-#include <dt-bindings/reset/amlogic,meson8b-clkc-reset.h>
-
-#endif /* __MESON8B_H */
diff --git a/drivers/clk/meson/s4-peripherals.c b/drivers/clk/meson/s4-peripherals.c
new file mode 100644
index 000000000000..6d69b132d1e1
--- /dev/null
+++ b/drivers/clk/meson/s4-peripherals.c
@@ -0,0 +1,3061 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Amlogic S4 Peripherals Clock Controller Driver
+ *
+ * Copyright (c) 2022-2023 Amlogic, inc. All rights reserved
+ * Author: Yu Tu <yu.tu@amlogic.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-regmap.h"
+#include "vid-pll-div.h"
+#include "clk-dualdiv.h"
+#include "meson-clkc-utils.h"
+#include <dt-bindings/clock/amlogic,s4-peripherals-clkc.h>
+
+#define CLKCTRL_RTC_BY_OSCIN_CTRL0 0x008
+#define CLKCTRL_RTC_BY_OSCIN_CTRL1 0x00c
+#define CLKCTRL_RTC_CTRL 0x010
+#define CLKCTRL_SYS_CLK_CTRL0 0x040
+#define CLKCTRL_SYS_CLK_EN0_REG0 0x044
+#define CLKCTRL_SYS_CLK_EN0_REG1 0x048
+#define CLKCTRL_SYS_CLK_EN0_REG2 0x04c
+#define CLKCTRL_SYS_CLK_EN0_REG3 0x050
+#define CLKCTRL_CECA_CTRL0 0x088
+#define CLKCTRL_CECA_CTRL1 0x08c
+#define CLKCTRL_CECB_CTRL0 0x090
+#define CLKCTRL_CECB_CTRL1 0x094
+#define CLKCTRL_SC_CLK_CTRL 0x098
+#define CLKCTRL_CLK12_24_CTRL 0x0a8
+#define CLKCTRL_VID_CLK_CTRL 0x0c0
+#define CLKCTRL_VID_CLK_CTRL2 0x0c4
+#define CLKCTRL_VID_CLK_DIV 0x0c8
+#define CLKCTRL_VIID_CLK_DIV 0x0cc
+#define CLKCTRL_VIID_CLK_CTRL 0x0d0
+#define CLKCTRL_HDMI_CLK_CTRL 0x0e0
+#define CLKCTRL_VID_PLL_CLK_DIV 0x0e4
+#define CLKCTRL_VPU_CLK_CTRL 0x0e8
+#define CLKCTRL_VPU_CLKB_CTRL 0x0ec
+#define CLKCTRL_VPU_CLKC_CTRL 0x0f0
+#define CLKCTRL_VID_LOCK_CLK_CTRL 0x0f4
+#define CLKCTRL_VDIN_MEAS_CLK_CTRL 0x0f8
+#define CLKCTRL_VAPBCLK_CTRL 0x0fc
+#define CLKCTRL_HDCP22_CTRL 0x100
+#define CLKCTRL_VDEC_CLK_CTRL 0x140
+#define CLKCTRL_VDEC2_CLK_CTRL 0x144
+#define CLKCTRL_VDEC3_CLK_CTRL 0x148
+#define CLKCTRL_VDEC4_CLK_CTRL 0x14c
+#define CLKCTRL_TS_CLK_CTRL 0x158
+#define CLKCTRL_MALI_CLK_CTRL 0x15c
+#define CLKCTRL_NAND_CLK_CTRL 0x168
+#define CLKCTRL_SD_EMMC_CLK_CTRL 0x16c
+#define CLKCTRL_SPICC_CLK_CTRL 0x174
+#define CLKCTRL_GEN_CLK_CTRL 0x178
+#define CLKCTRL_SAR_CLK_CTRL 0x17c
+#define CLKCTRL_PWM_CLK_AB_CTRL 0x180
+#define CLKCTRL_PWM_CLK_CD_CTRL 0x184
+#define CLKCTRL_PWM_CLK_EF_CTRL 0x188
+#define CLKCTRL_PWM_CLK_GH_CTRL 0x18c
+#define CLKCTRL_PWM_CLK_IJ_CTRL 0x190
+#define CLKCTRL_DEMOD_CLK_CTRL 0x200
+
+#define S4_COMP_SEL(_name, _reg, _shift, _mask, _pdata) \
+ MESON_COMP_SEL(s4_, _name, _reg, _shift, _mask, _pdata, NULL, 0, 0)
+
+#define S4_COMP_DIV(_name, _reg, _shift, _width) \
+ MESON_COMP_DIV(s4_, _name, _reg, _shift, _width, 0, CLK_SET_RATE_PARENT)
+
+#define S4_COMP_GATE(_name, _reg, _bit) \
+ MESON_COMP_GATE(s4_, _name, _reg, _bit, CLK_SET_RATE_PARENT)
+
+static struct clk_regmap s4_rtc_32k_by_oscin_clkin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_by_oscin_clkin",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param s4_32k_div_table[] = {
+ {
+ .dual = 1,
+ .n1 = 733,
+ .m1 = 8,
+ .n2 = 732,
+ .m2 = 11,
+ },
+ {}
+};
+
+static struct clk_regmap s4_rtc_32k_by_oscin_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CLKCTRL_RTC_BY_OSCIN_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CLKCTRL_RTC_BY_OSCIN_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CLKCTRL_RTC_BY_OSCIN_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CLKCTRL_RTC_BY_OSCIN_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CLKCTRL_RTC_BY_OSCIN_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = s4_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_32k_by_oscin_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_rtc_32k_by_oscin_clkin.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_rtc_32k_by_oscin_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_RTC_BY_OSCIN_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_32k_by_oscin_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_rtc_32k_by_oscin_div.hw,
+ &s4_rtc_32k_by_oscin_clkin.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_rtc_32k_by_oscin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "rtc_32k_by_oscin",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_rtc_32k_by_oscin_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_rtc_clk = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_RTC_CTRL,
+ .mask = 0x3,
+ .shift = 0,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "rtc_clk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_rtc_32k_by_oscin.hw,
+ &s4_rtc_32k_by_oscin_div.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* The index 5 is AXI_CLK, which is dedicated to AXI. So skip it. */
+static u32 s4_sysclk_parents_val_table[] = { 0, 1, 2, 3, 4, 6, 7 };
+static const struct clk_parent_data s4_sysclk_parents[] = {
+ { .fw_name = "xtal" },
+ { .fw_name = "fclk_div2" },
+ { .fw_name = "fclk_div3" },
+ { .fw_name = "fclk_div4" },
+ { .fw_name = "fclk_div5" },
+ { .fw_name = "fclk_div7" },
+ { .hw = &s4_rtc_clk.hw }
+};
+
+/*
+ * This clock is initialized by ROMcode.
+ * The chip was changed SYS CLK for security reasons. SYS CLK registers are not writable
+ * in the kernel phase. Write of SYS related register will cause the system to crash.
+ * Meanwhile, these clock won't ever change at runtime.
+ * For the above reasons, we can only use ro_ops for SYS related clocks.
+ */
+static struct clk_regmap s4_sysclk_b_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 26,
+ .table = s4_sysclk_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sysclk_b_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = s4_sysclk_parents,
+ .num_parents = ARRAY_SIZE(s4_sysclk_parents),
+ },
+};
+
+static struct clk_regmap s4_sysclk_b_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .shift = 16,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sysclk_b_div",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sysclk_b_sel.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_sysclk_b = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sysclk_b",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sysclk_b_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_sysclk_a_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .mask = 0x7,
+ .shift = 10,
+ .table = s4_sysclk_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sysclk_a_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = s4_sysclk_parents,
+ .num_parents = ARRAY_SIZE(s4_sysclk_parents),
+ },
+};
+
+static struct clk_regmap s4_sysclk_a_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .shift = 0,
+ .width = 10,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sysclk_a_div",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sysclk_a_sel.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_sysclk_a = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sysclk_a",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sysclk_a_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_sys_clk = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SYS_CLK_CTRL0,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sys_clk",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sysclk_a.hw,
+ &s4_sysclk_b.hw
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap s4_ceca_32k_clkin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_CECA_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ceca_32k_clkin",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_ceca_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CLKCTRL_CECA_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CLKCTRL_CECA_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CLKCTRL_CECA_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CLKCTRL_CECA_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CLKCTRL_CECA_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = s4_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_ceca_32k_clkin.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_ceca_32k_sel_pre = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_CECA_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_sel_pre",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_ceca_32k_div.hw,
+ &s4_ceca_32k_clkin.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_ceca_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_CECA_CTRL1,
+ .mask = 0x1,
+ .shift = 31,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_ceca_32k_sel_pre.hw,
+ &s4_rtc_clk.hw
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap s4_ceca_32k_clkout = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_CECA_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ceca_32k_clkout",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_ceca_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cecb_32k_clkin = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_CECB_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cecb_32k_clkin",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_cecb_32k_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = CLKCTRL_CECB_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = CLKCTRL_CECB_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = CLKCTRL_CECB_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = CLKCTRL_CECB_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = CLKCTRL_CECB_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = s4_32k_div_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cecb_32k_clkin.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_cecb_32k_sel_pre = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_CECB_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_sel_pre",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cecb_32k_div.hw,
+ &s4_cecb_32k_clkin.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cecb_32k_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_CECB_CTRL1,
+ .mask = 0x1,
+ .shift = 31,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cecb_32k_sel_pre.hw,
+ &s4_rtc_clk.hw
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_regmap s4_cecb_32k_clkout = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_CECB_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cecb_32k_clkout",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cecb_32k_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_sc_clk_parents[] = {
+ { .fw_name = "fclk_div4" },
+ { .fw_name = "fclk_div3" },
+ { .fw_name = "fclk_div5" },
+ { .fw_name = "xtal", }
+};
+
+static struct clk_regmap s4_sc_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SC_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sc_clk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_sc_clk_parents,
+ .num_parents = ARRAY_SIZE(s4_sc_clk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sc_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_SC_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sc_clk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sc_clk_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sc_clk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_SC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sc_clk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sc_clk_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_12_24M = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_CLK12_24_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "12_24M",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor s4_12M_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "12M_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_12_24M.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_12_24M_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_CLK12_24_CTRL,
+ .mask = 0x1,
+ .shift = 10,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "12_24M_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_12_24M.hw,
+ &s4_12M_div.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* Video Clocks */
+static struct clk_regmap s4_vid_pll_div = {
+ .data = &(struct meson_vid_pll_div_data){
+ .val = {
+ .reg_off = CLKCTRL_VID_PLL_CLK_DIV,
+ .shift = 0,
+ .width = 15,
+ },
+ .sel = {
+ .reg_off = CLKCTRL_VID_PLL_CLK_DIV,
+ .shift = 16,
+ .width = 2,
+ },
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vid_pll_div",
+ /*
+ * TODO meson_vid_pll_div_ro_ops to meson_vid_pll_div_ops
+ */
+ .ops = &meson_vid_pll_div_ro_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "hdmi_pll", }
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vid_pll_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VID_PLL_CLK_DIV,
+ .mask = 0x1,
+ .shift = 18,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vid_pll_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &s4_vid_pll_div.hw },
+ { .fw_name = "hdmi_pll", }
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vid_pll = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_PLL_CLK_DIV,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vid_pll",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vid_pll_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_vclk_parents[] = {
+ { .hw = &s4_vid_pll.hw },
+ { .fw_name = "gp0_pll", },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "mpll1", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+};
+
+static struct clk_regmap s4_vclk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vclk_parents,
+ .num_parents = ARRAY_SIZE(s4_vclk_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vclk2_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vclk_parents,
+ .num_parents = ARRAY_SIZE(s4_vclk_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vclk_input = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_DIV,
+ .bit_idx = 16,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_input",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_input = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_DIV,
+ .bit_idx = 16,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_input",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk_input.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VIID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk2_input.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk_div1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk_div2_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div2_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk_div4_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div4_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk_div6_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div6_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk_div12_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk_div12_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_div1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_div2_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .bit_idx = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div2_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_div4_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div4_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_div6_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div6_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vclk2_div12_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VIID_CLK_CTRL,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk2_div12_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vclk2.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk_div2_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk_div4 = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div4",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk_div4_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk_div6 = {
+ .mult = 1,
+ .div = 6,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div6",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk_div6_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk_div12 = {
+ .mult = 1,
+ .div = 12,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk_div12",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk_div12_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk2_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk2_div2_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk2_div4 = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div4",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk2_div4_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk2_div6 = {
+ .mult = 1,
+ .div = 6,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div6",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk2_div6_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_vclk2_div12 = {
+ .mult = 1,
+ .div = 12,
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk2_div12",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vclk2_div12_en.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* The 5,6,7 indexes corresponds to no real clock, so there are not used. */
+static u32 s4_cts_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *s4_cts_parents[] = {
+ &s4_vclk_div1.hw,
+ &s4_vclk_div2.hw,
+ &s4_vclk_div4.hw,
+ &s4_vclk_div6.hw,
+ &s4_vclk_div12.hw,
+ &s4_vclk2_div1.hw,
+ &s4_vclk2_div2.hw,
+ &s4_vclk2_div4.hw,
+ &s4_vclk2_div6.hw,
+ &s4_vclk2_div12.hw
+};
+
+static struct clk_regmap s4_cts_enci_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 28,
+ .table = s4_cts_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_enci_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = s4_cts_parents,
+ .num_parents = ARRAY_SIZE(s4_cts_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cts_encp_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 20,
+ .table = s4_cts_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_encp_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = s4_cts_parents,
+ .num_parents = ARRAY_SIZE(s4_cts_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cts_vdac_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 28,
+ .table = s4_cts_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_vdac_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = s4_cts_parents,
+ .num_parents = ARRAY_SIZE(s4_cts_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* The 5,6,7 indexes corresponds to no real clock, so there are not used. */
+static u32 s4_hdmi_tx_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *s4_hdmi_tx_parents[] = {
+ &s4_vclk_div1.hw,
+ &s4_vclk_div2.hw,
+ &s4_vclk_div4.hw,
+ &s4_vclk_div6.hw,
+ &s4_vclk_div12.hw,
+ &s4_vclk2_div1.hw,
+ &s4_vclk2_div2.hw,
+ &s4_vclk2_div4.hw,
+ &s4_vclk2_div6.hw,
+ &s4_vclk2_div12.hw
+};
+
+static struct clk_regmap s4_hdmi_tx_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_HDMI_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 16,
+ .table = s4_hdmi_tx_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_tx_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = s4_hdmi_tx_parents,
+ .num_parents = ARRAY_SIZE(s4_hdmi_tx_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cts_enci = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL2,
+ .bit_idx = 0,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_enci",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cts_enci_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cts_encp = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL2,
+ .bit_idx = 2,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_encp",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cts_encp_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_cts_vdac = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL2,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_vdac",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_cts_vdac_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdmi_tx = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VID_CLK_CTRL2,
+ .bit_idx = 5,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmi_tx",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdmi_tx_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* HDMI Clocks */
+static const struct clk_parent_data s4_hdmi_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", }
+};
+
+static struct clk_regmap s4_hdmi_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_HDMI_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_hdmi_parents,
+ .num_parents = ARRAY_SIZE(s4_hdmi_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdmi_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_HDMI_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_hdmi_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdmi = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_HDMI_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmi",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_hdmi_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_ts_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_TS_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ts_clk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_ts_clk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_TS_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ts_clk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_ts_clk_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * The MALI IP is clocked by two identical clocks (mali_0 and mali_1)
+ * muxed by a glitch-free switch. The CCF can manage this glitch-free
+ * mux because it does top-to-bottom updates the each clock tree and
+ * switches to the "inactive" one when CLK_SET_RATE_GATE is set.
+ */
+static const struct clk_parent_data s4_mali_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "gp0_pll", },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "fclk_div2p5", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", }
+};
+
+static struct clk_regmap s4_mali_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_mali_parents,
+ .num_parents = ARRAY_SIZE(s4_mali_parents),
+ /*
+ * Don't request the parent to change the rate because
+ * all GPU frequencies can be derived from the fclk_*
+ * clocks and one special GP0_PLL setting. This is
+ * important because we need the HIFI PLL clock for audio.
+ */
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_mali_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mali_0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_mali_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mali_0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_mali_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_mali_parents,
+ .num_parents = ARRAY_SIZE(s4_mali_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_mali_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mali_1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_mali_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mali_1_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_mali_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_MALI_CLK_CTRL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mali_0.hw,
+ &s4_mali_1.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* VDEC clocks */
+static const struct clk_parent_data s4_dec_parents[] = {
+ { .fw_name = "fclk_div2p5", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "gp0_pll", },
+ { .fw_name = "xtal", }
+};
+
+static struct clk_regmap s4_vdec_p0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDEC_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdec_p0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_dec_parents,
+ .num_parents = ARRAY_SIZE(s4_dec_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vdec_p0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VDEC_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdec_p0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdec_p0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vdec_p0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VDEC_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vdec_p0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdec_p0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vdec_p1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDEC3_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdec_p1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_dec_parents,
+ .num_parents = ARRAY_SIZE(s4_dec_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vdec_p1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VDEC3_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdec_p1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdec_p1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vdec_p1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VDEC3_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vdec_p1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdec_p1_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vdec_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDEC3_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdec_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdec_p0.hw,
+ &s4_vdec_p1.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hevcf_p0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDEC2_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hevcf_p0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_dec_parents,
+ .num_parents = ARRAY_SIZE(s4_dec_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_hevcf_p0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VDEC2_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hevcf_p0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hevcf_p0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hevcf_p0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VDEC2_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hevcf_p0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hevcf_p0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hevcf_p1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDEC4_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ .flags = CLK_MUX_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hevcf_p1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_dec_parents,
+ .num_parents = ARRAY_SIZE(s4_dec_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_hevcf_p1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VDEC4_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hevcf_p1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hevcf_p1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hevcf_p1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VDEC4_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hevcf_p1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hevcf_p1_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hevcf_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDEC4_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hevcf_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hevcf_p0.hw,
+ &s4_hevcf_p1.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* VPU Clock */
+static const struct clk_parent_data s4_vpu_parents[] = {
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+ { .fw_name = "mpll1", },
+ { .hw = &s4_vid_pll.hw },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "gp0_pll", },
+};
+
+static struct clk_regmap s4_vpu_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vpu_parents,
+ .num_parents = ARRAY_SIZE(s4_vpu_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vpu_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vpu_0_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vpu_0_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vpu_parents,
+ .num_parents = ARRAY_SIZE(s4_vpu_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vpu_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vpu_1_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vpu_1_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLK_CTRL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_0.hw,
+ &s4_vpu_1.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data vpu_clkb_tmp_parents[] = {
+ { .hw = &s4_vpu.hw },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", }
+};
+
+static struct clk_regmap s4_vpu_clkb_tmp_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLKB_CTRL,
+ .mask = 0x3,
+ .shift = 20,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkb_tmp_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = vpu_clkb_tmp_parents,
+ .num_parents = ARRAY_SIZE(vpu_clkb_tmp_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkb_tmp_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VPU_CLKB_CTRL,
+ .shift = 16,
+ .width = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkb_tmp_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkb_tmp_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkb_tmp = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VPU_CLKB_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_clkb_tmp",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkb_tmp_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkb_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VPU_CLKB_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkb_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkb_tmp.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkb = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VPU_CLKB_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_clkb",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkb_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_vpu_clkc_parents[] = {
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+ { .fw_name = "mpll1", },
+ { .hw = &s4_vid_pll.hw },
+ { .fw_name = "mpll2", },
+ { .fw_name = "gp0_pll", },
+};
+
+static struct clk_regmap s4_vpu_clkc_p0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkc_p0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vpu_clkc_parents,
+ .num_parents = ARRAY_SIZE(s4_vpu_clkc_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkc_p0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkc_p0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkc_p0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkc_p0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_clkc_p0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkc_p0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkc_p1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkc_p1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vpu_clkc_parents,
+ .num_parents = ARRAY_SIZE(s4_vpu_clkc_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkc_p1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkc_p1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkc_p1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkc_p1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu_clkc_p1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkc_p1_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vpu_clkc_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VPU_CLKC_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vpu_clkc_p0.hw,
+ &s4_vpu_clkc_p1.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* VAPB Clock */
+static const struct clk_parent_data s4_vapb_parents[] = {
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+ { .fw_name = "mpll1", },
+ { .hw = &s4_vid_pll.hw },
+ { .fw_name = "mpll2", },
+ { .fw_name = "fclk_div2p5", },
+};
+
+static struct clk_regmap s4_vapb_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vapb_parents,
+ .num_parents = ARRAY_SIZE(s4_vapb_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vapb_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vapb_0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vapb_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vapb_0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vapb_0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vapb_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vapb_parents,
+ .num_parents = ARRAY_SIZE(s4_vapb_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_vapb_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vapb_1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vapb_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vapb_1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vapb_1_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vapb = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .mask = 1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vapb_0.hw,
+ &s4_vapb_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_ge2d = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VAPBCLK_CTRL,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ge2d",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_vapb.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_hdcp22_esmclk_parents[] = {
+ { .fw_name = "fclk_div7", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+};
+
+static struct clk_regmap s4_hdcp22_esmclk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_HDCP22_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdcp22_esmclk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_hdcp22_esmclk_parents,
+ .num_parents = ARRAY_SIZE(s4_hdcp22_esmclk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdcp22_esmclk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_HDCP22_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdcp22_esmclk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdcp22_esmclk_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdcp22_esmclk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_HDCP22_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdcp22_esmclk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdcp22_esmclk_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_hdcp22_skpclk_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+};
+
+static struct clk_regmap s4_hdcp22_skpclk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_HDCP22_CTRL,
+ .mask = 0x3,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdcp22_skpclk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_hdcp22_skpclk_parents,
+ .num_parents = ARRAY_SIZE(s4_hdcp22_skpclk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdcp22_skpclk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_HDCP22_CTRL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdcp22_skpclk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdcp22_skpclk_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdcp22_skpclk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_HDCP22_CTRL,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdcp22_skpclk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdcp22_skpclk_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_vdin_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div5", },
+ { .hw = &s4_vid_pll.hw }
+};
+
+static struct clk_regmap s4_vdin_meas_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_VDIN_MEAS_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdin_meas_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_vdin_parents,
+ .num_parents = ARRAY_SIZE(s4_vdin_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vdin_meas_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_VDIN_MEAS_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdin_meas_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdin_meas_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_vdin_meas = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_VDIN_MEAS_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vdin_meas",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_vdin_meas_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* EMMC/NAND clock */
+static const struct clk_parent_data s4_sd_emmc_clk0_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "fclk_div2p5", },
+ { .fw_name = "mpll2", },
+ { .fw_name = "mpll3", },
+ { .fw_name = "gp0_pll", },
+};
+
+static struct clk_regmap s4_sd_emmc_c_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_NAND_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_c_clk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(s4_sd_emmc_clk0_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_c_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_NAND_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_c_clk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sd_emmc_c_clk0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_c_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_NAND_CLK_CTRL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_c_clk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sd_emmc_c_clk0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_a_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SD_EMMC_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_a_clk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(s4_sd_emmc_clk0_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_a_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_SD_EMMC_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_a_clk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sd_emmc_a_clk0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_a_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_SD_EMMC_CLK_CTRL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_a_clk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sd_emmc_a_clk0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_b_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SD_EMMC_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 25,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_b_clk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_sd_emmc_clk0_parents,
+ .num_parents = ARRAY_SIZE(s4_sd_emmc_clk0_parents),
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_b_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_SD_EMMC_CLK_CTRL,
+ .shift = 16,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_b_clk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sd_emmc_b_clk0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_sd_emmc_b_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_SD_EMMC_CLK_CTRL,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "sd_emmc_b_clk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_sd_emmc_b_clk0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* SPICC Clock */
+static const struct clk_parent_data s4_spicc_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &s4_sys_clk.hw },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+};
+
+static struct clk_regmap s4_spicc0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_SPICC_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "spicc0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_spicc_parents,
+ .num_parents = ARRAY_SIZE(s4_spicc_parents),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_spicc0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_SPICC_CLK_CTRL,
+ .shift = 0,
+ .width = 6,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "spicc0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_spicc0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_spicc0_en = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_SPICC_CLK_CTRL,
+ .bit_idx = 6,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "spicc0_en",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_spicc0_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* PWM Clock */
+static const struct clk_parent_data s4_pwm_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &s4_vid_pll.hw },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div3", },
+};
+
+static S4_COMP_SEL(pwm_a, CLKCTRL_PWM_CLK_AB_CTRL, 9, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_a, CLKCTRL_PWM_CLK_AB_CTRL, 0, 8);
+static S4_COMP_GATE(pwm_a, CLKCTRL_PWM_CLK_AB_CTRL, 8);
+
+static S4_COMP_SEL(pwm_b, CLKCTRL_PWM_CLK_AB_CTRL, 25, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_b, CLKCTRL_PWM_CLK_AB_CTRL, 16, 8);
+static S4_COMP_GATE(pwm_b, CLKCTRL_PWM_CLK_AB_CTRL, 24);
+
+static S4_COMP_SEL(pwm_c, CLKCTRL_PWM_CLK_CD_CTRL, 9, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_c, CLKCTRL_PWM_CLK_CD_CTRL, 0, 8);
+static S4_COMP_GATE(pwm_c, CLKCTRL_PWM_CLK_CD_CTRL, 8);
+
+static S4_COMP_SEL(pwm_d, CLKCTRL_PWM_CLK_CD_CTRL, 25, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_d, CLKCTRL_PWM_CLK_CD_CTRL, 16, 8);
+static S4_COMP_GATE(pwm_d, CLKCTRL_PWM_CLK_CD_CTRL, 24);
+
+static S4_COMP_SEL(pwm_e, CLKCTRL_PWM_CLK_EF_CTRL, 9, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_e, CLKCTRL_PWM_CLK_EF_CTRL, 0, 8);
+static S4_COMP_GATE(pwm_e, CLKCTRL_PWM_CLK_EF_CTRL, 8);
+
+static S4_COMP_SEL(pwm_f, CLKCTRL_PWM_CLK_EF_CTRL, 25, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_f, CLKCTRL_PWM_CLK_EF_CTRL, 16, 8);
+static S4_COMP_GATE(pwm_f, CLKCTRL_PWM_CLK_EF_CTRL, 24);
+
+static S4_COMP_SEL(pwm_g, CLKCTRL_PWM_CLK_GH_CTRL, 9, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_g, CLKCTRL_PWM_CLK_GH_CTRL, 0, 8);
+static S4_COMP_GATE(pwm_g, CLKCTRL_PWM_CLK_GH_CTRL, 8);
+
+static S4_COMP_SEL(pwm_h, CLKCTRL_PWM_CLK_GH_CTRL, 25, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_h, CLKCTRL_PWM_CLK_GH_CTRL, 16, 8);
+static S4_COMP_GATE(pwm_h, CLKCTRL_PWM_CLK_GH_CTRL, 24);
+
+static S4_COMP_SEL(pwm_i, CLKCTRL_PWM_CLK_IJ_CTRL, 9, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_i, CLKCTRL_PWM_CLK_IJ_CTRL, 0, 8);
+static S4_COMP_GATE(pwm_i, CLKCTRL_PWM_CLK_IJ_CTRL, 8);
+
+static S4_COMP_SEL(pwm_j, CLKCTRL_PWM_CLK_IJ_CTRL, 25, 0x3, s4_pwm_parents);
+static S4_COMP_DIV(pwm_j, CLKCTRL_PWM_CLK_IJ_CTRL, 16, 8);
+static S4_COMP_GATE(pwm_j, CLKCTRL_PWM_CLK_IJ_CTRL, 24);
+
+static struct clk_regmap s4_saradc_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = CLKCTRL_SAR_CLK_CTRL,
+ .mask = 0x3,
+ .shift = 9,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", },
+ { .hw = &s4_sys_clk.hw },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_saradc_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = CLKCTRL_SAR_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_saradc_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_saradc = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = CLKCTRL_SAR_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "saradc",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_saradc_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * gen clk is designed for debug/monitor some internal clock quality. Some of the
+ * corresponding clock sources are not described in the clock tree and internal clock
+ * for debug, so they are skipped.
+ */
+static u32 s4_gen_clk_parents_val_table[] = { 0, 4, 5, 7, 19, 21, 22, 23, 24, 25, 26, 27, 28 };
+static const struct clk_parent_data s4_gen_clk_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &s4_vid_pll.hw },
+ { .fw_name = "gp0_pll", },
+ { .fw_name = "hifi_pll", },
+ { .fw_name = "fclk_div2", },
+ { .fw_name = "fclk_div3", },
+ { .fw_name = "fclk_div4", },
+ { .fw_name = "fclk_div5", },
+ { .fw_name = "fclk_div7", },
+ { .fw_name = "mpll0", },
+ { .fw_name = "mpll1", },
+ { .fw_name = "mpll2", },
+ { .fw_name = "mpll3", },
+};
+
+static struct clk_regmap s4_gen_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = CLKCTRL_GEN_CLK_CTRL,
+ .mask = 0x1f,
+ .shift = 12,
+ .table = s4_gen_clk_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gen_clk_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = s4_gen_clk_parents,
+ .num_parents = ARRAY_SIZE(s4_gen_clk_parents),
+ /*
+ * Because the GEN clock can be connected to an external pad
+ * and may be set up directly from the device tree. Don't
+ * really want to automatically reparent.
+ */
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap s4_gen_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = CLKCTRL_GEN_CLK_CTRL,
+ .shift = 0,
+ .width = 11,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gen_clk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_gen_clk_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_gen_clk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = CLKCTRL_GEN_CLK_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "gen_clk",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_gen_clk_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data s4_pclk_parents = { .hw = &s4_sys_clk.hw };
+
+#define S4_PCLK(_name, _reg, _bit, _flags) \
+ MESON_PCLK(_name, _reg, _bit, &s4_pclk_parents, _flags)
+
+/*
+ * NOTE: The gates below are marked with CLK_IGNORE_UNUSED for historic reasons
+ * Users are encouraged to test without it and submit changes to:
+ * - remove the flag if not necessary
+ * - replace the flag with something more adequate, such as CLK_IS_CRITICAL,
+ * if appropriate.
+ * - add a comment explaining why the use of CLK_IGNORE_UNUSED is desirable
+ * for a particular clock.
+ */
+static S4_PCLK(s4_ddr, CLKCTRL_SYS_CLK_EN0_REG0, 0, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_dos, CLKCTRL_SYS_CLK_EN0_REG0, 1, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_ethphy, CLKCTRL_SYS_CLK_EN0_REG0, 4, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_mali, CLKCTRL_SYS_CLK_EN0_REG0, 6, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_aocpu, CLKCTRL_SYS_CLK_EN0_REG0, 13, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_aucpu, CLKCTRL_SYS_CLK_EN0_REG0, 14, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_cec, CLKCTRL_SYS_CLK_EN0_REG0, 16, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_sdemmca, CLKCTRL_SYS_CLK_EN0_REG0, 24, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_sdemmcb, CLKCTRL_SYS_CLK_EN0_REG0, 25, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_nand, CLKCTRL_SYS_CLK_EN0_REG0, 26, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_smartcard, CLKCTRL_SYS_CLK_EN0_REG0, 27, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_acodec, CLKCTRL_SYS_CLK_EN0_REG0, 28, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_spifc, CLKCTRL_SYS_CLK_EN0_REG0, 29, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_msr_clk, CLKCTRL_SYS_CLK_EN0_REG0, 30, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_ir_ctrl, CLKCTRL_SYS_CLK_EN0_REG0, 31, CLK_IGNORE_UNUSED);
+
+static S4_PCLK(s4_audio, CLKCTRL_SYS_CLK_EN0_REG1, 0, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_eth, CLKCTRL_SYS_CLK_EN0_REG1, 3, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_uart_a, CLKCTRL_SYS_CLK_EN0_REG1, 5, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_uart_b, CLKCTRL_SYS_CLK_EN0_REG1, 6, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_uart_c, CLKCTRL_SYS_CLK_EN0_REG1, 7, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_uart_d, CLKCTRL_SYS_CLK_EN0_REG1, 8, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_uart_e, CLKCTRL_SYS_CLK_EN0_REG1, 9, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_aififo, CLKCTRL_SYS_CLK_EN0_REG1, 11, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_ts_ddr, CLKCTRL_SYS_CLK_EN0_REG1, 15, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_ts_pll, CLKCTRL_SYS_CLK_EN0_REG1, 16, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_g2d, CLKCTRL_SYS_CLK_EN0_REG1, 20, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_spicc0, CLKCTRL_SYS_CLK_EN0_REG1, 21, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_usb, CLKCTRL_SYS_CLK_EN0_REG1, 26, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_i2c_m_a, CLKCTRL_SYS_CLK_EN0_REG1, 30, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_i2c_m_b, CLKCTRL_SYS_CLK_EN0_REG1, 31, CLK_IGNORE_UNUSED);
+
+static S4_PCLK(s4_i2c_m_c, CLKCTRL_SYS_CLK_EN0_REG2, 0, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_i2c_m_d, CLKCTRL_SYS_CLK_EN0_REG2, 1, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_i2c_m_e, CLKCTRL_SYS_CLK_EN0_REG2, 2, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_hdmitx_apb, CLKCTRL_SYS_CLK_EN0_REG2, 4, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_i2c_s_a, CLKCTRL_SYS_CLK_EN0_REG2, 5, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_usb1_to_ddr, CLKCTRL_SYS_CLK_EN0_REG2, 8, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_hdcp22, CLKCTRL_SYS_CLK_EN0_REG2, 10, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_mmc_apb, CLKCTRL_SYS_CLK_EN0_REG2, 11, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_rsa, CLKCTRL_SYS_CLK_EN0_REG2, 18, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_cpu_debug, CLKCTRL_SYS_CLK_EN0_REG2, 19, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_vpu_intr, CLKCTRL_SYS_CLK_EN0_REG2, 25, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_demod, CLKCTRL_SYS_CLK_EN0_REG2, 27, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_sar_adc, CLKCTRL_SYS_CLK_EN0_REG2, 28, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_gic, CLKCTRL_SYS_CLK_EN0_REG2, 30, CLK_IGNORE_UNUSED);
+
+static S4_PCLK(s4_pwm_ab, CLKCTRL_SYS_CLK_EN0_REG3, 7, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_pwm_cd, CLKCTRL_SYS_CLK_EN0_REG3, 8, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_pwm_ef, CLKCTRL_SYS_CLK_EN0_REG3, 9, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_pwm_gh, CLKCTRL_SYS_CLK_EN0_REG3, 10, CLK_IGNORE_UNUSED);
+static S4_PCLK(s4_pwm_ij, CLKCTRL_SYS_CLK_EN0_REG3, 11, CLK_IGNORE_UNUSED);
+
+/* Array of all clocks provided by this provider */
+static struct clk_hw *s4_peripherals_hw_clks[] = {
+ [CLKID_RTC_32K_CLKIN] = &s4_rtc_32k_by_oscin_clkin.hw,
+ [CLKID_RTC_32K_DIV] = &s4_rtc_32k_by_oscin_div.hw,
+ [CLKID_RTC_32K_SEL] = &s4_rtc_32k_by_oscin_sel.hw,
+ [CLKID_RTC_32K_XATL] = &s4_rtc_32k_by_oscin.hw,
+ [CLKID_RTC] = &s4_rtc_clk.hw,
+ [CLKID_SYS_CLK_B_SEL] = &s4_sysclk_b_sel.hw,
+ [CLKID_SYS_CLK_B_DIV] = &s4_sysclk_b_div.hw,
+ [CLKID_SYS_CLK_B] = &s4_sysclk_b.hw,
+ [CLKID_SYS_CLK_A_SEL] = &s4_sysclk_a_sel.hw,
+ [CLKID_SYS_CLK_A_DIV] = &s4_sysclk_a_div.hw,
+ [CLKID_SYS_CLK_A] = &s4_sysclk_a.hw,
+ [CLKID_SYS] = &s4_sys_clk.hw,
+ [CLKID_CECA_32K_CLKIN] = &s4_ceca_32k_clkin.hw,
+ [CLKID_CECA_32K_DIV] = &s4_ceca_32k_div.hw,
+ [CLKID_CECA_32K_SEL_PRE] = &s4_ceca_32k_sel_pre.hw,
+ [CLKID_CECA_32K_SEL] = &s4_ceca_32k_sel.hw,
+ [CLKID_CECA_32K_CLKOUT] = &s4_ceca_32k_clkout.hw,
+ [CLKID_CECB_32K_CLKIN] = &s4_cecb_32k_clkin.hw,
+ [CLKID_CECB_32K_DIV] = &s4_cecb_32k_div.hw,
+ [CLKID_CECB_32K_SEL_PRE] = &s4_cecb_32k_sel_pre.hw,
+ [CLKID_CECB_32K_SEL] = &s4_cecb_32k_sel.hw,
+ [CLKID_CECB_32K_CLKOUT] = &s4_cecb_32k_clkout.hw,
+ [CLKID_SC_CLK_SEL] = &s4_sc_clk_sel.hw,
+ [CLKID_SC_CLK_DIV] = &s4_sc_clk_div.hw,
+ [CLKID_SC] = &s4_sc_clk.hw,
+ [CLKID_12_24M] = &s4_12_24M.hw,
+ [CLKID_12M_CLK_DIV] = &s4_12M_div.hw,
+ [CLKID_12_24M_CLK_SEL] = &s4_12_24M_sel.hw,
+ [CLKID_VID_PLL_DIV] = &s4_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &s4_vid_pll_sel.hw,
+ [CLKID_VID_PLL] = &s4_vid_pll.hw,
+ [CLKID_VCLK_SEL] = &s4_vclk_sel.hw,
+ [CLKID_VCLK2_SEL] = &s4_vclk2_sel.hw,
+ [CLKID_VCLK_INPUT] = &s4_vclk_input.hw,
+ [CLKID_VCLK2_INPUT] = &s4_vclk2_input.hw,
+ [CLKID_VCLK_DIV] = &s4_vclk_div.hw,
+ [CLKID_VCLK2_DIV] = &s4_vclk2_div.hw,
+ [CLKID_VCLK] = &s4_vclk.hw,
+ [CLKID_VCLK2] = &s4_vclk2.hw,
+ [CLKID_VCLK_DIV1] = &s4_vclk_div1.hw,
+ [CLKID_VCLK_DIV2_EN] = &s4_vclk_div2_en.hw,
+ [CLKID_VCLK_DIV4_EN] = &s4_vclk_div4_en.hw,
+ [CLKID_VCLK_DIV6_EN] = &s4_vclk_div6_en.hw,
+ [CLKID_VCLK_DIV12_EN] = &s4_vclk_div12_en.hw,
+ [CLKID_VCLK2_DIV1] = &s4_vclk2_div1.hw,
+ [CLKID_VCLK2_DIV2_EN] = &s4_vclk2_div2_en.hw,
+ [CLKID_VCLK2_DIV4_EN] = &s4_vclk2_div4_en.hw,
+ [CLKID_VCLK2_DIV6_EN] = &s4_vclk2_div6_en.hw,
+ [CLKID_VCLK2_DIV12_EN] = &s4_vclk2_div12_en.hw,
+ [CLKID_VCLK_DIV2] = &s4_vclk_div2.hw,
+ [CLKID_VCLK_DIV4] = &s4_vclk_div4.hw,
+ [CLKID_VCLK_DIV6] = &s4_vclk_div6.hw,
+ [CLKID_VCLK_DIV12] = &s4_vclk_div12.hw,
+ [CLKID_VCLK2_DIV2] = &s4_vclk2_div2.hw,
+ [CLKID_VCLK2_DIV4] = &s4_vclk2_div4.hw,
+ [CLKID_VCLK2_DIV6] = &s4_vclk2_div6.hw,
+ [CLKID_VCLK2_DIV12] = &s4_vclk2_div12.hw,
+ [CLKID_CTS_ENCI_SEL] = &s4_cts_enci_sel.hw,
+ [CLKID_CTS_ENCP_SEL] = &s4_cts_encp_sel.hw,
+ [CLKID_CTS_VDAC_SEL] = &s4_cts_vdac_sel.hw,
+ [CLKID_HDMI_TX_SEL] = &s4_hdmi_tx_sel.hw,
+ [CLKID_CTS_ENCI] = &s4_cts_enci.hw,
+ [CLKID_CTS_ENCP] = &s4_cts_encp.hw,
+ [CLKID_CTS_VDAC] = &s4_cts_vdac.hw,
+ [CLKID_HDMI_TX] = &s4_hdmi_tx.hw,
+ [CLKID_HDMI_SEL] = &s4_hdmi_sel.hw,
+ [CLKID_HDMI_DIV] = &s4_hdmi_div.hw,
+ [CLKID_HDMI] = &s4_hdmi.hw,
+ [CLKID_TS_CLK_DIV] = &s4_ts_clk_div.hw,
+ [CLKID_TS] = &s4_ts_clk.hw,
+ [CLKID_MALI_0_SEL] = &s4_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &s4_mali_0_div.hw,
+ [CLKID_MALI_0] = &s4_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &s4_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &s4_mali_1_div.hw,
+ [CLKID_MALI_1] = &s4_mali_1.hw,
+ [CLKID_MALI_SEL] = &s4_mali_sel.hw,
+ [CLKID_VDEC_P0_SEL] = &s4_vdec_p0_sel.hw,
+ [CLKID_VDEC_P0_DIV] = &s4_vdec_p0_div.hw,
+ [CLKID_VDEC_P0] = &s4_vdec_p0.hw,
+ [CLKID_VDEC_P1_SEL] = &s4_vdec_p1_sel.hw,
+ [CLKID_VDEC_P1_DIV] = &s4_vdec_p1_div.hw,
+ [CLKID_VDEC_P1] = &s4_vdec_p1.hw,
+ [CLKID_VDEC_SEL] = &s4_vdec_sel.hw,
+ [CLKID_HEVCF_P0_SEL] = &s4_hevcf_p0_sel.hw,
+ [CLKID_HEVCF_P0_DIV] = &s4_hevcf_p0_div.hw,
+ [CLKID_HEVCF_P0] = &s4_hevcf_p0.hw,
+ [CLKID_HEVCF_P1_SEL] = &s4_hevcf_p1_sel.hw,
+ [CLKID_HEVCF_P1_DIV] = &s4_hevcf_p1_div.hw,
+ [CLKID_HEVCF_P1] = &s4_hevcf_p1.hw,
+ [CLKID_HEVCF_SEL] = &s4_hevcf_sel.hw,
+ [CLKID_VPU_0_SEL] = &s4_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &s4_vpu_0_div.hw,
+ [CLKID_VPU_0] = &s4_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &s4_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &s4_vpu_1_div.hw,
+ [CLKID_VPU_1] = &s4_vpu_1.hw,
+ [CLKID_VPU] = &s4_vpu.hw,
+ [CLKID_VPU_CLKB_TMP_SEL] = &s4_vpu_clkb_tmp_sel.hw,
+ [CLKID_VPU_CLKB_TMP_DIV] = &s4_vpu_clkb_tmp_div.hw,
+ [CLKID_VPU_CLKB_TMP] = &s4_vpu_clkb_tmp.hw,
+ [CLKID_VPU_CLKB_DIV] = &s4_vpu_clkb_div.hw,
+ [CLKID_VPU_CLKB] = &s4_vpu_clkb.hw,
+ [CLKID_VPU_CLKC_P0_SEL] = &s4_vpu_clkc_p0_sel.hw,
+ [CLKID_VPU_CLKC_P0_DIV] = &s4_vpu_clkc_p0_div.hw,
+ [CLKID_VPU_CLKC_P0] = &s4_vpu_clkc_p0.hw,
+ [CLKID_VPU_CLKC_P1_SEL] = &s4_vpu_clkc_p1_sel.hw,
+ [CLKID_VPU_CLKC_P1_DIV] = &s4_vpu_clkc_p1_div.hw,
+ [CLKID_VPU_CLKC_P1] = &s4_vpu_clkc_p1.hw,
+ [CLKID_VPU_CLKC_SEL] = &s4_vpu_clkc_sel.hw,
+ [CLKID_VAPB_0_SEL] = &s4_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &s4_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &s4_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &s4_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &s4_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &s4_vapb_1.hw,
+ [CLKID_VAPB] = &s4_vapb.hw,
+ [CLKID_GE2D] = &s4_ge2d.hw,
+ [CLKID_VDIN_MEAS_SEL] = &s4_vdin_meas_sel.hw,
+ [CLKID_VDIN_MEAS_DIV] = &s4_vdin_meas_div.hw,
+ [CLKID_VDIN_MEAS] = &s4_vdin_meas.hw,
+ [CLKID_SD_EMMC_C_CLK_SEL] = &s4_sd_emmc_c_clk0_sel.hw,
+ [CLKID_SD_EMMC_C_CLK_DIV] = &s4_sd_emmc_c_clk0_div.hw,
+ [CLKID_SD_EMMC_C] = &s4_sd_emmc_c_clk0.hw,
+ [CLKID_SD_EMMC_A_CLK_SEL] = &s4_sd_emmc_a_clk0_sel.hw,
+ [CLKID_SD_EMMC_A_CLK_DIV] = &s4_sd_emmc_a_clk0_div.hw,
+ [CLKID_SD_EMMC_A] = &s4_sd_emmc_a_clk0.hw,
+ [CLKID_SD_EMMC_B_CLK_SEL] = &s4_sd_emmc_b_clk0_sel.hw,
+ [CLKID_SD_EMMC_B_CLK_DIV] = &s4_sd_emmc_b_clk0_div.hw,
+ [CLKID_SD_EMMC_B] = &s4_sd_emmc_b_clk0.hw,
+ [CLKID_SPICC0_SEL] = &s4_spicc0_sel.hw,
+ [CLKID_SPICC0_DIV] = &s4_spicc0_div.hw,
+ [CLKID_SPICC0_EN] = &s4_spicc0_en.hw,
+ [CLKID_PWM_A_SEL] = &s4_pwm_a_sel.hw,
+ [CLKID_PWM_A_DIV] = &s4_pwm_a_div.hw,
+ [CLKID_PWM_A] = &s4_pwm_a.hw,
+ [CLKID_PWM_B_SEL] = &s4_pwm_b_sel.hw,
+ [CLKID_PWM_B_DIV] = &s4_pwm_b_div.hw,
+ [CLKID_PWM_B] = &s4_pwm_b.hw,
+ [CLKID_PWM_C_SEL] = &s4_pwm_c_sel.hw,
+ [CLKID_PWM_C_DIV] = &s4_pwm_c_div.hw,
+ [CLKID_PWM_C] = &s4_pwm_c.hw,
+ [CLKID_PWM_D_SEL] = &s4_pwm_d_sel.hw,
+ [CLKID_PWM_D_DIV] = &s4_pwm_d_div.hw,
+ [CLKID_PWM_D] = &s4_pwm_d.hw,
+ [CLKID_PWM_E_SEL] = &s4_pwm_e_sel.hw,
+ [CLKID_PWM_E_DIV] = &s4_pwm_e_div.hw,
+ [CLKID_PWM_E] = &s4_pwm_e.hw,
+ [CLKID_PWM_F_SEL] = &s4_pwm_f_sel.hw,
+ [CLKID_PWM_F_DIV] = &s4_pwm_f_div.hw,
+ [CLKID_PWM_F] = &s4_pwm_f.hw,
+ [CLKID_PWM_G_SEL] = &s4_pwm_g_sel.hw,
+ [CLKID_PWM_G_DIV] = &s4_pwm_g_div.hw,
+ [CLKID_PWM_G] = &s4_pwm_g.hw,
+ [CLKID_PWM_H_SEL] = &s4_pwm_h_sel.hw,
+ [CLKID_PWM_H_DIV] = &s4_pwm_h_div.hw,
+ [CLKID_PWM_H] = &s4_pwm_h.hw,
+ [CLKID_PWM_I_SEL] = &s4_pwm_i_sel.hw,
+ [CLKID_PWM_I_DIV] = &s4_pwm_i_div.hw,
+ [CLKID_PWM_I] = &s4_pwm_i.hw,
+ [CLKID_PWM_J_SEL] = &s4_pwm_j_sel.hw,
+ [CLKID_PWM_J_DIV] = &s4_pwm_j_div.hw,
+ [CLKID_PWM_J] = &s4_pwm_j.hw,
+ [CLKID_SARADC_SEL] = &s4_saradc_sel.hw,
+ [CLKID_SARADC_DIV] = &s4_saradc_div.hw,
+ [CLKID_SARADC] = &s4_saradc.hw,
+ [CLKID_GEN_SEL] = &s4_gen_clk_sel.hw,
+ [CLKID_GEN_DIV] = &s4_gen_clk_div.hw,
+ [CLKID_GEN] = &s4_gen_clk.hw,
+ [CLKID_DDR] = &s4_ddr.hw,
+ [CLKID_DOS] = &s4_dos.hw,
+ [CLKID_ETHPHY] = &s4_ethphy.hw,
+ [CLKID_MALI] = &s4_mali.hw,
+ [CLKID_AOCPU] = &s4_aocpu.hw,
+ [CLKID_AUCPU] = &s4_aucpu.hw,
+ [CLKID_CEC] = &s4_cec.hw,
+ [CLKID_SDEMMC_A] = &s4_sdemmca.hw,
+ [CLKID_SDEMMC_B] = &s4_sdemmcb.hw,
+ [CLKID_NAND] = &s4_nand.hw,
+ [CLKID_SMARTCARD] = &s4_smartcard.hw,
+ [CLKID_ACODEC] = &s4_acodec.hw,
+ [CLKID_SPIFC] = &s4_spifc.hw,
+ [CLKID_MSR] = &s4_msr_clk.hw,
+ [CLKID_IR_CTRL] = &s4_ir_ctrl.hw,
+ [CLKID_AUDIO] = &s4_audio.hw,
+ [CLKID_ETH] = &s4_eth.hw,
+ [CLKID_UART_A] = &s4_uart_a.hw,
+ [CLKID_UART_B] = &s4_uart_b.hw,
+ [CLKID_UART_C] = &s4_uart_c.hw,
+ [CLKID_UART_D] = &s4_uart_d.hw,
+ [CLKID_UART_E] = &s4_uart_e.hw,
+ [CLKID_AIFIFO] = &s4_aififo.hw,
+ [CLKID_TS_DDR] = &s4_ts_ddr.hw,
+ [CLKID_TS_PLL] = &s4_ts_pll.hw,
+ [CLKID_G2D] = &s4_g2d.hw,
+ [CLKID_SPICC0] = &s4_spicc0.hw,
+ [CLKID_USB] = &s4_usb.hw,
+ [CLKID_I2C_M_A] = &s4_i2c_m_a.hw,
+ [CLKID_I2C_M_B] = &s4_i2c_m_b.hw,
+ [CLKID_I2C_M_C] = &s4_i2c_m_c.hw,
+ [CLKID_I2C_M_D] = &s4_i2c_m_d.hw,
+ [CLKID_I2C_M_E] = &s4_i2c_m_e.hw,
+ [CLKID_HDMITX_APB] = &s4_hdmitx_apb.hw,
+ [CLKID_I2C_S_A] = &s4_i2c_s_a.hw,
+ [CLKID_USB1_TO_DDR] = &s4_usb1_to_ddr.hw,
+ [CLKID_HDCP22] = &s4_hdcp22.hw,
+ [CLKID_MMC_APB] = &s4_mmc_apb.hw,
+ [CLKID_RSA] = &s4_rsa.hw,
+ [CLKID_CPU_DEBUG] = &s4_cpu_debug.hw,
+ [CLKID_VPU_INTR] = &s4_vpu_intr.hw,
+ [CLKID_DEMOD] = &s4_demod.hw,
+ [CLKID_SAR_ADC] = &s4_sar_adc.hw,
+ [CLKID_GIC] = &s4_gic.hw,
+ [CLKID_PWM_AB] = &s4_pwm_ab.hw,
+ [CLKID_PWM_CD] = &s4_pwm_cd.hw,
+ [CLKID_PWM_EF] = &s4_pwm_ef.hw,
+ [CLKID_PWM_GH] = &s4_pwm_gh.hw,
+ [CLKID_PWM_IJ] = &s4_pwm_ij.hw,
+ [CLKID_HDCP22_ESMCLK_SEL] = &s4_hdcp22_esmclk_sel.hw,
+ [CLKID_HDCP22_ESMCLK_DIV] = &s4_hdcp22_esmclk_div.hw,
+ [CLKID_HDCP22_ESMCLK] = &s4_hdcp22_esmclk.hw,
+ [CLKID_HDCP22_SKPCLK_SEL] = &s4_hdcp22_skpclk_sel.hw,
+ [CLKID_HDCP22_SKPCLK_DIV] = &s4_hdcp22_skpclk_div.hw,
+ [CLKID_HDCP22_SKPCLK] = &s4_hdcp22_skpclk.hw,
+};
+
+static const struct meson_clkc_data s4_peripherals_clkc_data = {
+ .hw_clks = {
+ .hws = s4_peripherals_hw_clks,
+ .num = ARRAY_SIZE(s4_peripherals_hw_clks),
+ },
+};
+
+static const struct of_device_id s4_peripherals_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,s4-peripherals-clkc",
+ .data = &s4_peripherals_clkc_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, s4_peripherals_clkc_match_table);
+
+static struct platform_driver s4_peripherals_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "s4-peripherals-clkc",
+ .of_match_table = s4_peripherals_clkc_match_table,
+ },
+};
+module_platform_driver(s4_peripherals_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic S4 Peripherals Clock Controller driver");
+MODULE_AUTHOR("Yu Tu <yu.tu@amlogic.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c
new file mode 100644
index 000000000000..56ce6f566e53
--- /dev/null
+++ b/drivers/clk/meson/s4-pll.c
@@ -0,0 +1,831 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Amlogic S4 PLL Clock Controller Driver
+ *
+ * Copyright (c) 2022-2023 Amlogic, inc. All rights reserved
+ * Author: Yu Tu <yu.tu@amlogic.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mpll.h"
+#include "clk-pll.h"
+#include "clk-regmap.h"
+#include "meson-clkc-utils.h"
+#include <dt-bindings/clock/amlogic,s4-pll-clkc.h>
+
+#define ANACTRL_FIXPLL_CTRL0 0x040
+#define ANACTRL_FIXPLL_CTRL1 0x044
+#define ANACTRL_FIXPLL_CTRL3 0x04c
+#define ANACTRL_GP0PLL_CTRL0 0x080
+#define ANACTRL_GP0PLL_CTRL1 0x084
+#define ANACTRL_GP0PLL_CTRL2 0x088
+#define ANACTRL_GP0PLL_CTRL3 0x08c
+#define ANACTRL_GP0PLL_CTRL4 0x090
+#define ANACTRL_GP0PLL_CTRL5 0x094
+#define ANACTRL_GP0PLL_CTRL6 0x098
+#define ANACTRL_HIFIPLL_CTRL0 0x100
+#define ANACTRL_HIFIPLL_CTRL1 0x104
+#define ANACTRL_HIFIPLL_CTRL2 0x108
+#define ANACTRL_HIFIPLL_CTRL3 0x10c
+#define ANACTRL_HIFIPLL_CTRL4 0x110
+#define ANACTRL_HIFIPLL_CTRL5 0x114
+#define ANACTRL_HIFIPLL_CTRL6 0x118
+#define ANACTRL_MPLL_CTRL0 0x180
+#define ANACTRL_MPLL_CTRL1 0x184
+#define ANACTRL_MPLL_CTRL2 0x188
+#define ANACTRL_MPLL_CTRL3 0x18c
+#define ANACTRL_MPLL_CTRL4 0x190
+#define ANACTRL_MPLL_CTRL5 0x194
+#define ANACTRL_MPLL_CTRL6 0x198
+#define ANACTRL_MPLL_CTRL7 0x19c
+#define ANACTRL_MPLL_CTRL8 0x1a0
+#define ANACTRL_HDMIPLL_CTRL0 0x1c0
+
+/*
+ * These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode.
+ * The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable
+ * in the kernel phase. Write of fixed PLL-related register will cause the system to crash.
+ * Meanwhile, these clock won't ever change at runtime.
+ * For the above reasons, we can only use ro_ops for fixed PLL related clocks.
+ */
+static struct clk_regmap s4_fixed_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .frac = {
+ .reg_off = ANACTRL_FIXPLL_CTRL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .n = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_FIXPLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fixed_pll_dco",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fixed_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = ANACTRL_FIXPLL_CTRL0,
+ .shift = 16,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fixed_pll",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ /*
+ * This clock won't ever change at runtime so
+ * CLK_SET_RATE_PARENT is not required
+ */
+ },
+};
+
+static struct clk_fixed_factor s4_fclk_div2_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL1,
+ .bit_idx = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fclk_div2_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor s4_fclk_div3_div = {
+ .mult = 1,
+ .div = 3,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL1,
+ .bit_idx = 20,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fclk_div3_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor s4_fclk_div4_div = {
+ .mult = 1,
+ .div = 4,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL1,
+ .bit_idx = 21,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fclk_div4_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor s4_fclk_div5_div = {
+ .mult = 1,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL1,
+ .bit_idx = 22,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fclk_div5_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor s4_fclk_div7_div = {
+ .mult = 1,
+ .div = 7,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL1,
+ .bit_idx = 23,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fclk_div7_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor s4_fclk_div2p5_div = {
+ .mult = 2,
+ .div = 5,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2p5_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fixed_pll.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_fclk_div2p5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_FIXPLL_CTRL1,
+ .bit_idx = 25,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2p5",
+ .ops = &clk_regmap_gate_ro_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fclk_div2p5_div.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct pll_mult_range s4_gp0_pll_mult_range = {
+ .min = 125,
+ .max = 250,
+};
+
+/*
+ * Internal gp0 pll emulation configuration parameters
+ */
+static const struct reg_sequence s4_gp0_pll_init_regs[] = {
+ { .reg = ANACTRL_GP0PLL_CTRL1, .def = 0x00000000 },
+ { .reg = ANACTRL_GP0PLL_CTRL2, .def = 0x00000000 },
+ { .reg = ANACTRL_GP0PLL_CTRL3, .def = 0x48681c00 },
+ { .reg = ANACTRL_GP0PLL_CTRL4, .def = 0x88770290 },
+ { .reg = ANACTRL_GP0PLL_CTRL5, .def = 0x39272000 },
+ { .reg = ANACTRL_GP0PLL_CTRL6, .def = 0x56540000 }
+};
+
+static struct clk_regmap s4_gp0_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_GP0PLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &s4_gp0_pll_mult_range,
+ .init_regs = s4_gp0_pll_init_regs,
+ .init_count = ARRAY_SIZE(s4_gp0_pll_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_gp0_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = ANACTRL_GP0PLL_CTRL0,
+ .shift = 16,
+ .width = 3,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_gp0_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * Internal hifi pll emulation configuration parameters
+ */
+static const struct reg_sequence s4_hifi_pll_init_regs[] = {
+ { .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00000000 },
+ { .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x6a285c00 },
+ { .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x65771290 },
+ { .reg = ANACTRL_HIFIPLL_CTRL5, .def = 0x39272000 },
+ { .reg = ANACTRL_HIFIPLL_CTRL6, .def = 0x56540000 }
+};
+
+static struct clk_regmap s4_hifi_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL1,
+ .shift = 0,
+ .width = 17,
+ },
+ .l = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &s4_gp0_pll_mult_range,
+ .init_regs = s4_hifi_pll_init_regs,
+ .init_count = ARRAY_SIZE(s4_hifi_pll_init_regs),
+ .frac_max = 100000,
+ .flags = CLK_MESON_PLL_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_hifi_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = ANACTRL_HIFIPLL_CTRL0,
+ .shift = 16,
+ .width = 2,
+ .flags = (CLK_DIVIDER_POWER_OF_TWO |
+ CLK_DIVIDER_ROUND_CLOSEST),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hifi_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdmi_pll_dco = {
+ .data = &(struct meson_clk_pll_data){
+ .en = {
+ .reg_off = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .m = {
+ .reg_off = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 0,
+ .width = 8,
+ },
+ .n = {
+ .reg_off = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 10,
+ .width = 5,
+ },
+ .l = {
+ .reg_off = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 29,
+ .width = 1,
+ },
+ .range = &s4_gp0_pll_mult_range,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_dco",
+ .ops = &meson_clk_pll_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_hdmi_pll_od = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 16,
+ .width = 4,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_od",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdmi_pll_dco.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap s4_hdmi_pll = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = ANACTRL_HDMIPLL_CTRL0,
+ .shift = 20,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_hdmi_pll_od.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor s4_mpll_50m_div = {
+ .mult = 1,
+ .div = 80,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_50m_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_mpll_50m = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = ANACTRL_FIXPLL_CTRL3,
+ .mask = 0x1,
+ .shift = 5,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_50m",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "xtal", },
+ { .hw = &s4_mpll_50m_div.hw },
+ },
+ .num_parents = 2,
+ },
+};
+
+static struct clk_fixed_factor s4_mpll_prediv = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_prediv",
+ .ops = &clk_fixed_factor_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_fixed_pll_dco.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct reg_sequence s4_mpll0_init_regs[] = {
+ { .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 }
+};
+
+static struct clk_regmap s4_mpll0_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = ANACTRL_MPLL_CTRL1,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = ANACTRL_MPLL_CTRL1,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = ANACTRL_MPLL_CTRL1,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = ANACTRL_MPLL_CTRL1,
+ .shift = 29,
+ .width = 1,
+ },
+ .init_regs = s4_mpll0_init_regs,
+ .init_count = ARRAY_SIZE(s4_mpll0_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mpll_prediv.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_mpll0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_MPLL_CTRL1,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct reg_sequence s4_mpll1_init_regs[] = {
+ { .reg = ANACTRL_MPLL_CTRL4, .def = 0x40000033 }
+};
+
+static struct clk_regmap s4_mpll1_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = ANACTRL_MPLL_CTRL3,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = ANACTRL_MPLL_CTRL3,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = ANACTRL_MPLL_CTRL3,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = ANACTRL_MPLL_CTRL3,
+ .shift = 29,
+ .width = 1,
+ },
+ .init_regs = s4_mpll1_init_regs,
+ .init_count = ARRAY_SIZE(s4_mpll1_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mpll_prediv.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_mpll1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_MPLL_CTRL3,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct reg_sequence s4_mpll2_init_regs[] = {
+ { .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 }
+};
+
+static struct clk_regmap s4_mpll2_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = ANACTRL_MPLL_CTRL5,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = ANACTRL_MPLL_CTRL5,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = ANACTRL_MPLL_CTRL5,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = ANACTRL_MPLL_CTRL5,
+ .shift = 29,
+ .width = 1,
+ },
+ .init_regs = s4_mpll2_init_regs,
+ .init_count = ARRAY_SIZE(s4_mpll2_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mpll_prediv.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_mpll2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_MPLL_CTRL5,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct reg_sequence s4_mpll3_init_regs[] = {
+ { .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 }
+};
+
+static struct clk_regmap s4_mpll3_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = ANACTRL_MPLL_CTRL7,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = ANACTRL_MPLL_CTRL7,
+ .shift = 30,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = ANACTRL_MPLL_CTRL7,
+ .shift = 20,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = ANACTRL_MPLL_CTRL7,
+ .shift = 29,
+ .width = 1,
+ },
+ .init_regs = s4_mpll3_init_regs,
+ .init_count = ARRAY_SIZE(s4_mpll3_init_regs),
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll3_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &s4_mpll_prediv.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap s4_mpll3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = ANACTRL_MPLL_CTRL7,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* Array of all clocks provided by this provider */
+static struct clk_hw *s4_pll_hw_clks[] = {
+ [CLKID_FIXED_PLL_DCO] = &s4_fixed_pll_dco.hw,
+ [CLKID_FIXED_PLL] = &s4_fixed_pll.hw,
+ [CLKID_FCLK_DIV2_DIV] = &s4_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV2] = &s4_fclk_div2.hw,
+ [CLKID_FCLK_DIV3_DIV] = &s4_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV3] = &s4_fclk_div3.hw,
+ [CLKID_FCLK_DIV4_DIV] = &s4_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV4] = &s4_fclk_div4.hw,
+ [CLKID_FCLK_DIV5_DIV] = &s4_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV5] = &s4_fclk_div5.hw,
+ [CLKID_FCLK_DIV7_DIV] = &s4_fclk_div7_div.hw,
+ [CLKID_FCLK_DIV7] = &s4_fclk_div7.hw,
+ [CLKID_FCLK_DIV2P5_DIV] = &s4_fclk_div2p5_div.hw,
+ [CLKID_FCLK_DIV2P5] = &s4_fclk_div2p5.hw,
+ [CLKID_GP0_PLL_DCO] = &s4_gp0_pll_dco.hw,
+ [CLKID_GP0_PLL] = &s4_gp0_pll.hw,
+ [CLKID_HIFI_PLL_DCO] = &s4_hifi_pll_dco.hw,
+ [CLKID_HIFI_PLL] = &s4_hifi_pll.hw,
+ [CLKID_HDMI_PLL_DCO] = &s4_hdmi_pll_dco.hw,
+ [CLKID_HDMI_PLL_OD] = &s4_hdmi_pll_od.hw,
+ [CLKID_HDMI_PLL] = &s4_hdmi_pll.hw,
+ [CLKID_MPLL_50M_DIV] = &s4_mpll_50m_div.hw,
+ [CLKID_MPLL_50M] = &s4_mpll_50m.hw,
+ [CLKID_MPLL_PREDIV] = &s4_mpll_prediv.hw,
+ [CLKID_MPLL0_DIV] = &s4_mpll0_div.hw,
+ [CLKID_MPLL0] = &s4_mpll0.hw,
+ [CLKID_MPLL1_DIV] = &s4_mpll1_div.hw,
+ [CLKID_MPLL1] = &s4_mpll1.hw,
+ [CLKID_MPLL2_DIV] = &s4_mpll2_div.hw,
+ [CLKID_MPLL2] = &s4_mpll2.hw,
+ [CLKID_MPLL3_DIV] = &s4_mpll3_div.hw,
+ [CLKID_MPLL3] = &s4_mpll3.hw,
+};
+
+static const struct reg_sequence s4_pll_init_regs[] = {
+ { .reg = ANACTRL_MPLL_CTRL0, .def = 0x00000543 },
+};
+
+static const struct meson_clkc_data s4_pll_clkc_data = {
+ .hw_clks = {
+ .hws = s4_pll_hw_clks,
+ .num = ARRAY_SIZE(s4_pll_hw_clks),
+ },
+ .init_regs = s4_pll_init_regs,
+ .init_count = ARRAY_SIZE(s4_pll_init_regs),
+};
+
+static const struct of_device_id s4_pll_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,s4-pll-clkc",
+ .data = &s4_pll_clkc_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, s4_pll_clkc_match_table);
+
+static struct platform_driver s4_pll_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "s4-pll-clkc",
+ .of_match_table = s4_pll_clkc_match_table,
+ },
+};
+module_platform_driver(s4_pll_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic S4 PLL Clock Controller driver");
+MODULE_AUTHOR("Yu Tu <yu.tu@amlogic.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c
index 76d31c0a3342..4ba3d82810e8 100644
--- a/drivers/clk/meson/sclk-div.c
+++ b/drivers/clk/meson/sclk-div.c
@@ -96,16 +96,17 @@ static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
return bestdiv;
}
-static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int sclk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
int div;
- div = sclk_div_bestdiv(hw, rate, prate, sclk);
+ div = sclk_div_bestdiv(hw, req->rate, &req->best_parent_rate, sclk);
+ req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div);
- return DIV_ROUND_UP_ULL((u64)*prate, div);
+ return 0;
}
static void sclk_apply_ratio(struct clk_regmap *clk,
@@ -221,6 +222,11 @@ static int sclk_div_init(struct clk_hw *hw)
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
unsigned int val;
+ int ret;
+
+ ret = clk_regmap_init(hw);
+ if (ret)
+ return ret;
val = meson_parm_read(clk->map, &sclk->div);
@@ -237,7 +243,7 @@ static int sclk_div_init(struct clk_hw *hw)
const struct clk_ops meson_sclk_div_ops = {
.recalc_rate = sclk_div_recalc_rate,
- .round_rate = sclk_div_round_rate,
+ .determine_rate = sclk_div_determine_rate,
.set_rate = sclk_div_set_rate,
.enable = sclk_div_enable,
.disable = sclk_div_disable,
@@ -246,8 +252,9 @@ const struct clk_ops meson_sclk_div_ops = {
.set_duty_cycle = sclk_div_set_duty_cycle,
.init = sclk_div_init,
};
-EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
+EXPORT_SYMBOL_NS_GPL(meson_sclk_div_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic Sample divider driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/vclk.c b/drivers/clk/meson/vclk.c
new file mode 100644
index 000000000000..009bd1193042
--- /dev/null
+++ b/drivers/clk/meson/vclk.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Neil Armstrong <neil.armstrong@linaro.org>
+ */
+
+#include <linux/module.h>
+#include "vclk.h"
+
+/* The VCLK gate has a supplementary reset bit to pulse after ungating */
+
+static inline struct meson_vclk_gate_data *
+clk_get_meson_vclk_gate_data(struct clk_regmap *clk)
+{
+ return (struct meson_vclk_gate_data *)clk->data;
+}
+
+static int meson_vclk_gate_enable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk);
+
+ meson_parm_write(clk->map, &vclk->enable, 1);
+
+ /* Do a reset pulse */
+ meson_parm_write(clk->map, &vclk->reset, 1);
+ meson_parm_write(clk->map, &vclk->reset, 0);
+
+ return 0;
+}
+
+static void meson_vclk_gate_disable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk);
+
+ meson_parm_write(clk->map, &vclk->enable, 0);
+}
+
+static int meson_vclk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk);
+
+ return meson_parm_read(clk->map, &vclk->enable);
+}
+
+const struct clk_ops meson_vclk_gate_ops = {
+ .init = clk_regmap_init,
+ .enable = meson_vclk_gate_enable,
+ .disable = meson_vclk_gate_disable,
+ .is_enabled = meson_vclk_gate_is_enabled,
+};
+EXPORT_SYMBOL_NS_GPL(meson_vclk_gate_ops, "CLK_MESON");
+
+/* The VCLK Divider has supplementary reset & enable bits */
+
+static inline struct meson_vclk_div_data *
+clk_get_meson_vclk_div_data(struct clk_regmap *clk)
+{
+ return (struct meson_vclk_div_data *)clk->data;
+}
+
+static unsigned long meson_vclk_div_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
+
+ return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
+ vclk->table, vclk->flags, vclk->div.width);
+}
+
+static int meson_vclk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
+
+ return divider_determine_rate(hw, req, vclk->table, vclk->div.width,
+ vclk->flags);
+}
+
+static int meson_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
+ int ret;
+
+ ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
+ vclk->flags);
+ if (ret < 0)
+ return ret;
+
+ meson_parm_write(clk->map, &vclk->div, ret);
+
+ return 0;
+};
+
+static int meson_vclk_div_enable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
+
+ /* Unreset the divider when ungating */
+ meson_parm_write(clk->map, &vclk->reset, 0);
+ meson_parm_write(clk->map, &vclk->enable, 1);
+
+ return 0;
+}
+
+static void meson_vclk_div_disable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
+
+ /* Reset the divider when gating */
+ meson_parm_write(clk->map, &vclk->enable, 0);
+ meson_parm_write(clk->map, &vclk->reset, 1);
+}
+
+static int meson_vclk_div_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
+
+ return meson_parm_read(clk->map, &vclk->enable);
+}
+
+const struct clk_ops meson_vclk_div_ops = {
+ .init = clk_regmap_init,
+ .recalc_rate = meson_vclk_div_recalc_rate,
+ .determine_rate = meson_vclk_div_determine_rate,
+ .set_rate = meson_vclk_div_set_rate,
+ .enable = meson_vclk_div_enable,
+ .disable = meson_vclk_div_disable,
+ .is_enabled = meson_vclk_div_is_enabled,
+};
+EXPORT_SYMBOL_NS_GPL(meson_vclk_div_ops, "CLK_MESON");
+
+MODULE_DESCRIPTION("Amlogic vclk clock driver");
+MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/meson/vclk.h b/drivers/clk/meson/vclk.h
new file mode 100644
index 000000000000..20b0b181db09
--- /dev/null
+++ b/drivers/clk/meson/vclk.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 Neil Armstrong <neil.armstrong@linaro.org>
+ */
+
+#ifndef __VCLK_H
+#define __VCLK_H
+
+#include "clk-regmap.h"
+#include "parm.h"
+
+/**
+ * struct meson_vclk_gate_data - vclk_gate regmap backed specific data
+ *
+ * @enable: vclk enable field
+ * @reset: vclk reset field
+ * @flags: hardware-specific flags
+ *
+ * Flags:
+ * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
+ */
+struct meson_vclk_gate_data {
+ struct parm enable;
+ struct parm reset;
+ u8 flags;
+};
+
+extern const struct clk_ops meson_vclk_gate_ops;
+
+/**
+ * struct meson_vclk_div_data - vclk_div regmap back specific data
+ *
+ * @div: divider field
+ * @enable: vclk divider enable field
+ * @reset: vclk divider reset field
+ * @table: array of value/divider pairs, last entry should have div = 0
+ *
+ * Flags:
+ * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
+ */
+struct meson_vclk_div_data {
+ struct parm div;
+ struct parm enable;
+ struct parm reset;
+ const struct clk_div_table *table;
+ u8 flags;
+};
+
+extern const struct clk_ops meson_vclk_div_ops;
+
+#endif /* __VCLK_H */
diff --git a/drivers/clk/meson/vid-pll-div.c b/drivers/clk/meson/vid-pll-div.c
index daff235bc763..2a3cdbe6d86a 100644
--- a/drivers/clk/meson/vid-pll-div.c
+++ b/drivers/clk/meson/vid-pll-div.c
@@ -90,10 +90,12 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw,
}
const struct clk_ops meson_vid_pll_div_ro_ops = {
+ .init = clk_regmap_init,
.recalc_rate = meson_vid_pll_div_recalc_rate,
};
-EXPORT_SYMBOL_GPL(meson_vid_pll_div_ro_ops);
+EXPORT_SYMBOL_NS_GPL(meson_vid_pll_div_ro_ops, "CLK_MESON");
MODULE_DESCRIPTION("Amlogic video pll divider driver");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
diff --git a/drivers/clk/microchip/Kconfig b/drivers/clk/microchip/Kconfig
index a5a99873c4f5..1b9e43eb5497 100644
--- a/drivers/clk/microchip/Kconfig
+++ b/drivers/clk/microchip/Kconfig
@@ -5,6 +5,10 @@ config COMMON_CLK_PIC32
config MCHP_CLK_MPFS
bool "Clk driver for PolarFire SoC"
- depends on (RISCV && SOC_MICROCHIP_POLARFIRE) || COMPILE_TEST
+ depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST
+ default ARCH_MICROCHIP_POLARFIRE
+ depends on MFD_SYSCON
+ select AUXILIARY_BUS
+ select REGMAP_MMIO
help
Supports Clock Configuration for PolarFire SoC
diff --git a/drivers/clk/microchip/Makefile b/drivers/clk/microchip/Makefile
index 5fa6dcf30a9a..13250e04e46c 100644
--- a/drivers/clk/microchip/Makefile
+++ b/drivers/clk/microchip/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_COMMON_CLK_PIC32) += clk-core.o
obj-$(CONFIG_PIC32MZDA) += clk-pic32mzda.o
obj-$(CONFIG_MCHP_CLK_MPFS) += clk-mpfs.o
+obj-$(CONFIG_MCHP_CLK_MPFS) += clk-mpfs-ccc.o
diff --git a/drivers/clk/microchip/clk-core.c b/drivers/clk/microchip/clk-core.c
index 1b4f023cdc8b..b34348d491f3 100644
--- a/drivers/clk/microchip/clk-core.c
+++ b/drivers/clk/microchip/clk-core.c
@@ -155,11 +155,13 @@ static unsigned long pbclk_recalc_rate(struct clk_hw *hw,
return parent_rate / pbclk_read_pbdiv(pb);
}
-static long pbclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int pbclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- return calc_best_divided_rate(rate, *parent_rate,
- PB_DIV_MAX, PB_DIV_MIN);
+ req->rate = calc_best_divided_rate(req->rate, req->best_parent_rate,
+ PB_DIV_MAX, PB_DIV_MIN);
+
+ return 0;
}
static int pbclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -207,7 +209,7 @@ const struct clk_ops pic32_pbclk_ops = {
.disable = pbclk_disable,
.is_enabled = pbclk_is_enabled,
.recalc_rate = pbclk_recalc_rate,
- .round_rate = pbclk_round_rate,
+ .determine_rate = pbclk_determine_rate,
.set_rate = pbclk_set_rate,
};
@@ -326,7 +328,7 @@ static void roclk_calc_div_trim(unsigned long rate,
* i.e. fout = fin / 2 * DIV
* whereas DIV = rodiv + (rotrim / 512)
*
- * Since kernel does not perform floating-point arithmatic so
+ * Since kernel does not perform floating-point arithmetic so
* (rotrim/512) will be zero. And DIV & rodiv will result same.
*
* ie. fout = (fin * 256) / [(512 * rodiv) + rotrim] ... from (1)
@@ -372,18 +374,6 @@ static unsigned long roclk_recalc_rate(struct clk_hw *hw,
return roclk_calc_rate(parent_rate, rodiv, rotrim);
}
-static long roclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
-{
- u32 rotrim, rodiv;
-
- /* calculate dividers for new rate */
- roclk_calc_div_trim(rate, *parent_rate, &rodiv, &rotrim);
-
- /* caclulate new rate (rounding) based on new rodiv & rotrim */
- return roclk_calc_rate(*parent_rate, rodiv, rotrim);
-}
-
static int roclk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
@@ -394,6 +384,8 @@ static int roclk_determine_rate(struct clk_hw *hw,
/* find a parent which can generate nearest clkrate >= rate */
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ u32 rotrim, rodiv;
+
/* get parent */
parent_clk = clk_hw_get_parent_by_index(hw, i);
if (!parent_clk)
@@ -404,7 +396,12 @@ static int roclk_determine_rate(struct clk_hw *hw,
if (req->rate > parent_rate)
continue;
- nearest_rate = roclk_round_rate(hw, req->rate, &parent_rate);
+ /* calculate dividers for new rate */
+ roclk_calc_div_trim(req->rate, req->best_parent_rate, &rodiv, &rotrim);
+
+ /* caclulate new rate (rounding) based on new rodiv & rotrim */
+ nearest_rate = roclk_calc_rate(req->best_parent_rate, rodiv, rotrim);
+
delta = abs(nearest_rate - req->rate);
if ((nearest_rate >= req->rate) && (delta < best_delta)) {
best_parent_clk = parent_clk;
@@ -665,12 +662,15 @@ static unsigned long spll_clk_recalc_rate(struct clk_hw *hw,
return rate64;
}
-static long spll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int spll_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct pic32_sys_pll *pll = clkhw_to_spll(hw);
- return spll_calc_mult_div(pll, rate, *parent_rate, NULL, NULL);
+ req->rate = spll_calc_mult_div(pll, req->rate, req->best_parent_rate,
+ NULL, NULL);
+
+ return 0;
}
static int spll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -725,7 +725,7 @@ static int spll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
/* SPLL clock operation */
const struct clk_ops pic32_spll_ops = {
.recalc_rate = spll_clk_recalc_rate,
- .round_rate = spll_clk_round_rate,
+ .determine_rate = spll_clk_determine_rate,
.set_rate = spll_clk_set_rate,
};
@@ -780,10 +780,13 @@ static unsigned long sclk_get_rate(struct clk_hw *hw, unsigned long parent_rate)
return parent_rate / div;
}
-static long sclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int sclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- return calc_best_divided_rate(rate, *parent_rate, SLEW_SYSDIV, 1);
+ req->rate = calc_best_divided_rate(req->rate, req->best_parent_rate,
+ SLEW_SYSDIV, 1);
+
+ return 0;
}
static int sclk_set_rate(struct clk_hw *hw,
@@ -909,7 +912,7 @@ static int sclk_init(struct clk_hw *hw)
const struct clk_ops pic32_sclk_ops = {
.get_parent = sclk_get_parent,
.set_parent = sclk_set_parent,
- .round_rate = sclk_round_rate,
+ .determine_rate = sclk_determine_rate,
.set_rate = sclk_set_rate,
.recalc_rate = sclk_get_rate,
.init = sclk_init,
diff --git a/drivers/clk/microchip/clk-mpfs-ccc.c b/drivers/clk/microchip/clk-mpfs-ccc.c
new file mode 100644
index 000000000000..3a3ea2d142f8
--- /dev/null
+++ b/drivers/clk/microchip/clk-mpfs-ccc.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ *
+ * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
+ */
+#include <linux/clk-provider.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/clock/microchip,mpfs-clock.h>
+
+/* address offset of control registers */
+#define MPFS_CCC_PLL_CR 0x04u
+#define MPFS_CCC_REF_CR 0x08u
+#define MPFS_CCC_SSCG_2_CR 0x2Cu
+#define MPFS_CCC_POSTDIV01_CR 0x10u
+#define MPFS_CCC_POSTDIV23_CR 0x14u
+
+#define MPFS_CCC_FBDIV_SHIFT 0x00u
+#define MPFS_CCC_FBDIV_WIDTH 0x0Cu
+#define MPFS_CCC_POSTDIV0_SHIFT 0x08u
+#define MPFS_CCC_POSTDIV1_SHIFT 0x18u
+#define MPFS_CCC_POSTDIV2_SHIFT MPFS_CCC_POSTDIV0_SHIFT
+#define MPFS_CCC_POSTDIV3_SHIFT MPFS_CCC_POSTDIV1_SHIFT
+#define MPFS_CCC_POSTDIV_WIDTH 0x06u
+#define MPFS_CCC_REFCLK_SEL BIT(6)
+#define MPFS_CCC_REFDIV_SHIFT 0x08u
+#define MPFS_CCC_REFDIV_WIDTH 0x06u
+
+#define MPFS_CCC_FIXED_DIV 4
+#define MPFS_CCC_OUTPUTS_PER_PLL 4
+#define MPFS_CCC_REFS_PER_PLL 2
+
+struct mpfs_ccc_data {
+ void __iomem **pll_base;
+ struct device *dev;
+ struct clk_hw_onecell_data hw_data;
+};
+
+struct mpfs_ccc_pll_hw_clock {
+ void __iomem *base;
+ const char *name;
+ const struct clk_parent_data *parents;
+ unsigned int id;
+ u32 reg_offset;
+ u32 shift;
+ u32 width;
+ u32 flags;
+ struct clk_hw hw;
+ struct clk_init_data init;
+};
+
+#define to_mpfs_ccc_clk(_hw) container_of(_hw, struct mpfs_ccc_pll_hw_clock, hw)
+
+/*
+ * mpfs_ccc_lock prevents anything else from writing to a fabric ccc
+ * while a software locked register is being written.
+ */
+static DEFINE_SPINLOCK(mpfs_ccc_lock);
+
+static const struct clk_parent_data mpfs_ccc_pll0_refs[] = {
+ { .fw_name = "pll0_ref0" },
+ { .fw_name = "pll0_ref1" },
+};
+
+static const struct clk_parent_data mpfs_ccc_pll1_refs[] = {
+ { .fw_name = "pll1_ref0" },
+ { .fw_name = "pll1_ref1" },
+};
+
+static unsigned long mpfs_ccc_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+ struct mpfs_ccc_pll_hw_clock *ccc_hw = to_mpfs_ccc_clk(hw);
+ void __iomem *mult_addr = ccc_hw->base + ccc_hw->reg_offset;
+ void __iomem *ref_div_addr = ccc_hw->base + MPFS_CCC_REF_CR;
+ u32 mult, ref_div;
+
+ mult = readl_relaxed(mult_addr) >> MPFS_CCC_FBDIV_SHIFT;
+ mult &= clk_div_mask(MPFS_CCC_FBDIV_WIDTH);
+ ref_div = readl_relaxed(ref_div_addr) >> MPFS_CCC_REFDIV_SHIFT;
+ ref_div &= clk_div_mask(MPFS_CCC_REFDIV_WIDTH);
+
+ return prate * mult / (ref_div * MPFS_CCC_FIXED_DIV);
+}
+
+static u8 mpfs_ccc_pll_get_parent(struct clk_hw *hw)
+{
+ struct mpfs_ccc_pll_hw_clock *ccc_hw = to_mpfs_ccc_clk(hw);
+ void __iomem *pll_cr_addr = ccc_hw->base + MPFS_CCC_PLL_CR;
+
+ return !!(readl_relaxed(pll_cr_addr) & MPFS_CCC_REFCLK_SEL);
+}
+
+static const struct clk_ops mpfs_ccc_pll_ops = {
+ .recalc_rate = mpfs_ccc_pll_recalc_rate,
+ .get_parent = mpfs_ccc_pll_get_parent,
+};
+
+#define CLK_CCC_PLL(_id, _parents, _shift, _width, _flags, _offset) { \
+ .id = _id, \
+ .shift = _shift, \
+ .width = _width, \
+ .reg_offset = _offset, \
+ .flags = _flags, \
+ .parents = _parents, \
+}
+
+static struct mpfs_ccc_pll_hw_clock mpfs_ccc_pll_clks[] = {
+ CLK_CCC_PLL(CLK_CCC_PLL0, mpfs_ccc_pll0_refs, MPFS_CCC_FBDIV_SHIFT,
+ MPFS_CCC_FBDIV_WIDTH, 0, MPFS_CCC_SSCG_2_CR),
+ CLK_CCC_PLL(CLK_CCC_PLL1, mpfs_ccc_pll1_refs, MPFS_CCC_FBDIV_SHIFT,
+ MPFS_CCC_FBDIV_WIDTH, 0, MPFS_CCC_SSCG_2_CR),
+};
+
+struct mpfs_ccc_out_hw_clock {
+ struct clk_divider divider;
+ struct clk_init_data init;
+ unsigned int id;
+ u32 reg_offset;
+};
+
+#define CLK_CCC_OUT(_id, _shift, _width, _flags, _offset) { \
+ .id = _id, \
+ .divider.shift = _shift, \
+ .divider.width = _width, \
+ .reg_offset = _offset, \
+ .divider.flags = _flags, \
+ .divider.lock = &mpfs_ccc_lock, \
+}
+
+static struct mpfs_ccc_out_hw_clock mpfs_ccc_pll0out_clks[] = {
+ CLK_CCC_OUT(CLK_CCC_PLL0_OUT0, MPFS_CCC_POSTDIV0_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV01_CR),
+ CLK_CCC_OUT(CLK_CCC_PLL0_OUT1, MPFS_CCC_POSTDIV1_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV01_CR),
+ CLK_CCC_OUT(CLK_CCC_PLL0_OUT2, MPFS_CCC_POSTDIV2_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV23_CR),
+ CLK_CCC_OUT(CLK_CCC_PLL0_OUT3, MPFS_CCC_POSTDIV3_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV23_CR),
+};
+
+static struct mpfs_ccc_out_hw_clock mpfs_ccc_pll1out_clks[] = {
+ CLK_CCC_OUT(CLK_CCC_PLL1_OUT0, MPFS_CCC_POSTDIV0_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV01_CR),
+ CLK_CCC_OUT(CLK_CCC_PLL1_OUT1, MPFS_CCC_POSTDIV1_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV01_CR),
+ CLK_CCC_OUT(CLK_CCC_PLL1_OUT2, MPFS_CCC_POSTDIV2_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV23_CR),
+ CLK_CCC_OUT(CLK_CCC_PLL1_OUT3, MPFS_CCC_POSTDIV3_SHIFT, MPFS_CCC_POSTDIV_WIDTH,
+ CLK_DIVIDER_ONE_BASED, MPFS_CCC_POSTDIV23_CR),
+};
+
+static struct mpfs_ccc_out_hw_clock *mpfs_ccc_pllout_clks[] = {
+ mpfs_ccc_pll0out_clks, mpfs_ccc_pll1out_clks
+};
+
+static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_clock *out_hws,
+ unsigned int num_clks, struct mpfs_ccc_data *data,
+ struct mpfs_ccc_pll_hw_clock *parent)
+{
+ int ret;
+
+ for (unsigned int i = 0; i < num_clks; i++) {
+ struct mpfs_ccc_out_hw_clock *out_hw = &out_hws[i];
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s_out%u", parent->name, i);
+
+ if (!name)
+ return -ENOMEM;
+
+ out_hw->divider.hw.init = CLK_HW_INIT_HW(name, &parent->hw, &clk_divider_ops, 0);
+ out_hw->divider.reg = data->pll_base[i / MPFS_CCC_OUTPUTS_PER_PLL] +
+ out_hw->reg_offset;
+
+ ret = devm_clk_hw_register(dev, &out_hw->divider.hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
+ out_hw->id);
+
+ data->hw_data.hws[out_hw->id] = &out_hw->divider.hw;
+ }
+
+ return 0;
+}
+
+#define CLK_HW_INIT_PARENTS_DATA_FIXED_SIZE(_name, _parents, _ops, _flags) \
+ (&(struct clk_init_data) { \
+ .flags = _flags, \
+ .name = _name, \
+ .parent_data = _parents, \
+ .num_parents = MPFS_CCC_REFS_PER_PLL, \
+ .ops = _ops, \
+ })
+
+static int mpfs_ccc_register_plls(struct device *dev, struct mpfs_ccc_pll_hw_clock *pll_hws,
+ unsigned int num_clks, struct mpfs_ccc_data *data)
+{
+ int ret;
+
+ for (unsigned int i = 0; i < num_clks; i++) {
+ struct mpfs_ccc_pll_hw_clock *pll_hw = &pll_hws[i];
+
+ pll_hw->name = devm_kasprintf(dev, GFP_KERNEL, "ccc%s_pll%u",
+ strchrnul(dev->of_node->full_name, '@'), i);
+ if (!pll_hw->name)
+ return -ENOMEM;
+
+ pll_hw->base = data->pll_base[i];
+ pll_hw->hw.init = CLK_HW_INIT_PARENTS_DATA_FIXED_SIZE(pll_hw->name,
+ pll_hw->parents,
+ &mpfs_ccc_pll_ops, 0);
+
+ ret = devm_clk_hw_register(dev, &pll_hw->hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register ccc id: %d\n",
+ pll_hw->id);
+
+ data->hw_data.hws[pll_hw->id] = &pll_hw->hw;
+
+ ret = mpfs_ccc_register_outputs(dev, mpfs_ccc_pllout_clks[i],
+ MPFS_CCC_OUTPUTS_PER_PLL, data, pll_hw);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mpfs_ccc_probe(struct platform_device *pdev)
+{
+ struct mpfs_ccc_data *clk_data;
+ void __iomem *pll_base[ARRAY_SIZE(mpfs_ccc_pll_clks)];
+ unsigned int num_clks;
+ int ret;
+
+ num_clks = ARRAY_SIZE(mpfs_ccc_pll_clks) + ARRAY_SIZE(mpfs_ccc_pll0out_clks) +
+ ARRAY_SIZE(mpfs_ccc_pll1out_clks);
+
+ clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, num_clks),
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ pll_base[0] = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pll_base[0]))
+ return PTR_ERR(pll_base[0]);
+
+ pll_base[1] = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(pll_base[1]))
+ return PTR_ERR(pll_base[1]);
+
+ clk_data->pll_base = pll_base;
+ clk_data->hw_data.num = num_clks;
+ clk_data->dev = &pdev->dev;
+
+ ret = mpfs_ccc_register_plls(clk_data->dev, mpfs_ccc_pll_clks,
+ ARRAY_SIZE(mpfs_ccc_pll_clks), clk_data);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(clk_data->dev, of_clk_hw_onecell_get,
+ &clk_data->hw_data);
+}
+
+static const struct of_device_id mpfs_ccc_of_match_table[] = {
+ { .compatible = "microchip,mpfs-ccc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mpfs_ccc_of_match_table);
+
+static struct platform_driver mpfs_ccc_driver = {
+ .probe = mpfs_ccc_probe,
+ .driver = {
+ .name = "microchip-mpfs-ccc",
+ .of_match_table = mpfs_ccc_of_match_table,
+ },
+};
+
+static int __init clk_ccc_init(void)
+{
+ return platform_driver_register(&mpfs_ccc_driver);
+}
+core_initcall(clk_ccc_init);
+
+static void __exit clk_ccc_exit(void)
+{
+ platform_driver_unregister(&mpfs_ccc_driver);
+}
+module_exit(clk_ccc_exit);
+
+MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Conditioning Circuitry Driver");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c
index 070c3b896559..ee58304913ef 100644
--- a/drivers/clk/microchip/clk-mpfs.c
+++ b/drivers/clk/microchip/clk-mpfs.c
@@ -1,18 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Daire McNamara,<daire.mcnamara@microchip.com>
- * Copyright (C) 2020 Microchip Technology Inc. All rights reserved.
+ * PolarFire SoC MSS/core complex clock control
+ *
+ * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved.
*/
+#include <linux/cleanup.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <linux/regmap.h>
#include <dt-bindings/clock/microchip,mpfs-clock.h>
+#include <soc/microchip/mpfs.h>
/* address offset of control registers */
#define REG_MSSPLL_REF_CR 0x08u
-#define REG_MSSPLL_POSTDIV_CR 0x10u
+#define REG_MSSPLL_POSTDIV01_CR 0x10u
+#define REG_MSSPLL_POSTDIV23_CR 0x14u
#define REG_MSSPLL_SSCG_2_CR 0x2Cu
#define REG_CLOCK_CONFIG_CR 0x08u
#define REG_RTC_CLOCK_CR 0x0Cu
@@ -23,11 +28,29 @@
#define MSSPLL_FBDIV_WIDTH 0x0Cu
#define MSSPLL_REFDIV_SHIFT 0x08u
#define MSSPLL_REFDIV_WIDTH 0x06u
-#define MSSPLL_POSTDIV_SHIFT 0x08u
+#define MSSPLL_POSTDIV02_SHIFT 0x08u
+#define MSSPLL_POSTDIV13_SHIFT 0x18u
#define MSSPLL_POSTDIV_WIDTH 0x07u
#define MSSPLL_FIXED_DIV 4u
+static const struct regmap_config mpfs_clk_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .max_register = REG_SUBBLK_RESET_CR,
+};
+
+/*
+ * This clock ID is defined here, rather than the binding headers, as it is an
+ * internal clock only, and therefore has no consumers in other peripheral
+ * blocks.
+ */
+#define CLK_MSSPLL_INTERNAL 38u
+
struct mpfs_clock_data {
+ struct device *dev;
+ struct regmap *regmap;
void __iomem *base;
void __iomem *msspll_base;
struct clk_hw_onecell_data hw_data;
@@ -35,51 +58,60 @@ struct mpfs_clock_data {
struct mpfs_msspll_hw_clock {
void __iomem *base;
+ struct clk_hw hw;
+ struct clk_init_data init;
unsigned int id;
u32 reg_offset;
u32 shift;
u32 width;
u32 flags;
- struct clk_hw hw;
- struct clk_init_data init;
};
#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)
-struct mpfs_cfg_clock {
- const struct clk_div_table *table;
+struct mpfs_msspll_out_hw_clock {
+ void __iomem *base;
+ struct clk_divider output;
+ struct clk_init_data init;
unsigned int id;
u32 reg_offset;
+};
+
+#define to_mpfs_msspll_out_clk(_hw) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw)
+
+struct mpfs_cfg_clock {
+ struct regmap *map;
+ const struct clk_div_table *table;
+ u8 map_offset;
u8 shift;
u8 width;
u8 flags;
};
struct mpfs_cfg_hw_clock {
- struct mpfs_cfg_clock cfg;
- void __iomem *sys_base;
struct clk_hw hw;
- struct clk_init_data init;
+ struct mpfs_cfg_clock cfg;
+ unsigned int id;
};
#define to_mpfs_cfg_clk(_hw) container_of(_hw, struct mpfs_cfg_hw_clock, hw)
struct mpfs_periph_clock {
- unsigned int id;
+ struct regmap *map;
+ u8 map_offset;
u8 shift;
};
struct mpfs_periph_hw_clock {
- struct mpfs_periph_clock periph;
- void __iomem *sys_base;
struct clk_hw hw;
+ struct mpfs_periph_clock periph;
+ unsigned int id;
};
#define to_mpfs_periph_clk(_hw) container_of(_hw, struct mpfs_periph_hw_clock, hw)
/*
- * mpfs_clk_lock prevents anything else from writing to the
- * mpfs clk block while a software locked register is being written.
+ * Protects MSSPLL outputs, since there's two to a register
*/
static DEFINE_SPINLOCK(mpfs_clk_lock);
@@ -108,22 +140,23 @@ static const struct clk_div_table mpfs_div_rtcref_table[] = {
{ 0, 0 }
};
+/*
+ * MSS PLL internal clock
+ */
+
static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
- void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
- u32 mult, ref_div, postdiv;
+ u32 mult, ref_div;
mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
- postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT;
- postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH);
- return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv);
+ return prate * mult / (ref_div * MSSPLL_FIXED_DIV);
}
static const struct clk_ops mpfs_clk_msspll_ops = {
@@ -132,40 +165,32 @@ static const struct clk_ops mpfs_clk_msspll_ops = {
#define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \
.id = _id, \
+ .flags = _flags, \
.shift = _shift, \
.width = _width, \
.reg_offset = _offset, \
- .flags = _flags, \
.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \
}
static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {
- CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
+ CLK_PLL(CLK_MSSPLL_INTERNAL, "clk_msspll_internal", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),
};
-static int mpfs_clk_register_msspll(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hw,
- void __iomem *base)
-{
- msspll_hw->base = base;
-
- return devm_clk_hw_register(dev, &msspll_hw->hw);
-}
-
static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws,
unsigned int num_clks, struct mpfs_clock_data *data)
{
- void __iomem *base = data->msspll_base;
unsigned int i;
int ret;
for (i = 0; i < num_clks; i++) {
struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];
- ret = mpfs_clk_register_msspll(dev, msspll_hw, base);
+ msspll_hw->base = data->msspll_base;
+ ret = devm_clk_hw_register(dev, &msspll_hw->hw);
if (ret)
return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",
- CLK_MSSPLL);
+ CLK_MSSPLL_INTERNAL);
data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;
}
@@ -174,71 +199,118 @@ static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_c
}
/*
- * "CFG" clocks
+ * MSS PLL output clocks
*/
+#define CLK_PLL_OUT(_id, _name, _parent, _flags, _shift, _width, _offset) { \
+ .id = _id, \
+ .output.shift = _shift, \
+ .output.width = _width, \
+ .output.table = NULL, \
+ .reg_offset = _offset, \
+ .output.flags = _flags, \
+ .output.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \
+ .output.lock = &mpfs_clk_lock, \
+}
+
+static struct mpfs_msspll_out_hw_clock mpfs_msspll_out_clks[] = {
+ CLK_PLL_OUT(CLK_MSSPLL0, "clk_msspll", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,
+ MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR),
+ CLK_PLL_OUT(CLK_MSSPLL1, "clk_msspll1", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,
+ MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR),
+ CLK_PLL_OUT(CLK_MSSPLL2, "clk_msspll2", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,
+ MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR),
+ CLK_PLL_OUT(CLK_MSSPLL3, "clk_msspll3", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED,
+ MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR),
+};
+
+static int mpfs_clk_register_msspll_outs(struct device *dev,
+ struct mpfs_msspll_out_hw_clock *msspll_out_hws,
+ unsigned int num_clks, struct mpfs_clock_data *data)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < num_clks; i++) {
+ struct mpfs_msspll_out_hw_clock *msspll_out_hw = &msspll_out_hws[i];
+
+ msspll_out_hw->output.reg = data->msspll_base + msspll_out_hw->reg_offset;
+ ret = devm_clk_hw_register(dev, &msspll_out_hw->output.hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register msspll out id: %d\n",
+ msspll_out_hw->id);
+
+ data->hw_data.hws[msspll_out_hw->id] = &msspll_out_hw->output.hw;
+ }
+
+ return 0;
+}
+
+/*
+ * "CFG" clocks
+ */
static unsigned long mpfs_cfg_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
- void __iomem *base_addr = cfg_hw->sys_base;
u32 val;
- val = readl_relaxed(base_addr + cfg->reg_offset) >> cfg->shift;
+ regmap_read(cfg->map, cfg->map_offset, &val);
+ val >>= cfg->shift;
val &= clk_div_mask(cfg->width);
return divider_recalc_rate(hw, prate, val, cfg->table, cfg->flags, cfg->width);
}
-static long mpfs_cfg_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
+static int mpfs_cfg_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
- return divider_round_rate(hw, rate, prate, cfg->table, cfg->width, 0);
+ return divider_determine_rate(hw, req, cfg->table, cfg->width, 0);
}
static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate)
{
struct mpfs_cfg_hw_clock *cfg_hw = to_mpfs_cfg_clk(hw);
struct mpfs_cfg_clock *cfg = &cfg_hw->cfg;
- void __iomem *base_addr = cfg_hw->sys_base;
- unsigned long flags;
- u32 val;
int divider_setting;
+ u32 val;
+ u32 mask;
divider_setting = divider_get_val(rate, prate, cfg->table, cfg->width, 0);
if (divider_setting < 0)
return divider_setting;
- spin_lock_irqsave(&mpfs_clk_lock, flags);
- val = readl_relaxed(base_addr + cfg->reg_offset);
- val &= ~(clk_div_mask(cfg->width) << cfg_hw->cfg.shift);
- val |= divider_setting << cfg->shift;
- writel_relaxed(val, base_addr + cfg->reg_offset);
-
- spin_unlock_irqrestore(&mpfs_clk_lock, flags);
+ mask = clk_div_mask(cfg->width) << cfg->shift;
+ val = divider_setting << cfg->shift;
+ regmap_update_bits(cfg->map, cfg->map_offset, val, mask);
return 0;
}
static const struct clk_ops mpfs_clk_cfg_ops = {
.recalc_rate = mpfs_cfg_clk_recalc_rate,
- .round_rate = mpfs_cfg_clk_round_rate,
+ .determine_rate = mpfs_cfg_clk_determine_rate,
.set_rate = mpfs_cfg_clk_set_rate,
};
-#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \
- .cfg.id = _id, \
- .cfg.shift = _shift, \
- .cfg.width = _width, \
- .cfg.table = _table, \
- .cfg.reg_offset = _offset, \
- .cfg.flags = _flags, \
- .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0), \
+#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) { \
+ .id = _id, \
+ .cfg.shift = _shift, \
+ .cfg.width = _width, \
+ .cfg.table = _table, \
+ .cfg.map_offset = _offset, \
+ .cfg.flags = _flags, \
+ .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0), \
}
+#define CLK_CPU_OFFSET 0u
+#define CLK_AXI_OFFSET 1u
+#define CLK_AHB_OFFSET 2u
+#define CLK_RTCREF_OFFSET 3u
+
static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,
REG_CLOCK_CONFIG_CR),
@@ -247,41 +319,33 @@ static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,
REG_CLOCK_CONFIG_CR),
{
- .cfg.id = CLK_RTCREF,
+ .id = CLK_RTCREF,
.cfg.shift = 0,
.cfg.width = 12,
.cfg.table = mpfs_div_rtcref_table,
- .cfg.reg_offset = REG_RTC_CLOCK_CR,
+ .cfg.map_offset = REG_RTC_CLOCK_CR,
.cfg.flags = CLK_DIVIDER_ONE_BASED,
.hw.init =
CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &mpfs_clk_cfg_ops, 0),
}
};
-static int mpfs_clk_register_cfg(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hw,
- void __iomem *sys_base)
-{
- cfg_hw->sys_base = sys_base;
-
- return devm_clk_hw_register(dev, &cfg_hw->hw);
-}
-
static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws,
unsigned int num_clks, struct mpfs_clock_data *data)
{
- void __iomem *sys_base = data->base;
unsigned int i, id;
int ret;
for (i = 0; i < num_clks; i++) {
struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];
- ret = mpfs_clk_register_cfg(dev, cfg_hw, sys_base);
+ cfg_hw->cfg.map = data->regmap;
+ ret = devm_clk_hw_register(dev, &cfg_hw->hw);
if (ret)
return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
- cfg_hw->cfg.id);
+ cfg_hw->id);
- id = cfg_hw->cfg.id;
+ id = cfg_hw->id;
data->hw_data.hws[id] = &cfg_hw->hw;
}
@@ -296,21 +360,9 @@ static int mpfs_periph_clk_enable(struct clk_hw *hw)
{
struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
struct mpfs_periph_clock *periph = &periph_hw->periph;
- void __iomem *base_addr = periph_hw->sys_base;
- u32 reg, val;
- unsigned long flags;
-
- spin_lock_irqsave(&mpfs_clk_lock, flags);
-
- reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
- val = reg & ~(1u << periph->shift);
- writel_relaxed(val, base_addr + REG_SUBBLK_RESET_CR);
- reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
- val = reg | (1u << periph->shift);
- writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
-
- spin_unlock_irqrestore(&mpfs_clk_lock, flags);
+ regmap_update_bits(periph->map, periph->map_offset,
+ BIT(periph->shift), BIT(periph->shift));
return 0;
}
@@ -319,34 +371,19 @@ static void mpfs_periph_clk_disable(struct clk_hw *hw)
{
struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
struct mpfs_periph_clock *periph = &periph_hw->periph;
- void __iomem *base_addr = periph_hw->sys_base;
- u32 reg, val;
- unsigned long flags;
-
- spin_lock_irqsave(&mpfs_clk_lock, flags);
- reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
- val = reg & ~(1u << periph->shift);
- writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
-
- spin_unlock_irqrestore(&mpfs_clk_lock, flags);
+ regmap_update_bits(periph->map, periph->map_offset, BIT(periph->shift), 0);
}
static int mpfs_periph_clk_is_enabled(struct clk_hw *hw)
{
struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
struct mpfs_periph_clock *periph = &periph_hw->periph;
- void __iomem *base_addr = periph_hw->sys_base;
- u32 reg;
-
- reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
- if ((reg & (1u << periph->shift)) == 0u) {
- reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
- if (reg & (1u << periph->shift))
- return 1;
- }
+ u32 val;
- return 0;
+ regmap_read(periph->map, periph->map_offset, &val);
+
+ return !!(val & BIT(periph->shift));
}
static const struct clk_ops mpfs_periph_clk_ops = {
@@ -355,14 +392,14 @@ static const struct clk_ops mpfs_periph_clk_ops = {
.is_enabled = mpfs_periph_clk_is_enabled,
};
-#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \
- .periph.id = _id, \
- .periph.shift = _shift, \
- .hw.init = CLK_HW_INIT_HW(_name, _parent, &mpfs_periph_clk_ops, \
- _flags), \
+#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \
+ .id = _id, \
+ .periph.map_offset = REG_SUBBLK_CLOCK_CR, \
+ .periph.shift = _shift, \
+ .hw.init = CLK_HW_INIT_HW(_name, _parent, &mpfs_periph_clk_ops, _flags), \
}
-#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT].hw)
+#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].hw)
/*
* Critical clocks:
@@ -370,6 +407,8 @@ static const struct clk_ops mpfs_periph_clk_ops = {
* trap handler
* - CLK_MMUART0: reserved by the hss
* - CLK_DDRC: provides clock to the ddr subsystem
+ * - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop
+ * if the AHB interface clock is disabled
* - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect)
* clock domain crossers which provide the interface to the FPGA fabric. Disabling them
* causes the FPGA fabric to go into reset.
@@ -394,7 +433,7 @@ static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {
CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0),
CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0),
CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0),
- CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, 0),
+ CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0),
CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0),
CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0),
@@ -408,36 +447,64 @@ static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {
CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0),
};
-static int mpfs_clk_register_periph(struct device *dev, struct mpfs_periph_hw_clock *periph_hw,
- void __iomem *sys_base)
-{
- periph_hw->sys_base = sys_base;
-
- return devm_clk_hw_register(dev, &periph_hw->hw);
-}
-
static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws,
int num_clks, struct mpfs_clock_data *data)
{
- void __iomem *sys_base = data->base;
unsigned int i, id;
int ret;
for (i = 0; i < num_clks; i++) {
struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];
- ret = mpfs_clk_register_periph(dev, periph_hw, sys_base);
+ periph_hw->periph.map = data->regmap;
+ ret = devm_clk_hw_register(dev, &periph_hw->hw);
if (ret)
return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
- periph_hw->periph.id);
+ periph_hw->id);
- id = periph_hws[i].periph.id;
+ id = periph_hws[i].id;
data->hw_data.hws[id] = &periph_hw->hw;
}
return 0;
}
+static inline int mpfs_clk_syscon_probe(struct mpfs_clock_data *clk_data,
+ struct platform_device *pdev)
+{
+ clk_data->regmap = syscon_regmap_lookup_by_compatible("microchip,mpfs-mss-top-sysreg");
+ if (IS_ERR(clk_data->regmap))
+ return PTR_ERR(clk_data->regmap);
+
+ clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(clk_data->msspll_base))
+ return PTR_ERR(clk_data->msspll_base);
+
+ return 0;
+}
+
+static inline int mpfs_clk_old_format_probe(struct mpfs_clock_data *clk_data,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ dev_warn(&pdev->dev, "falling back to old devicetree format");
+
+ clk_data->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(clk_data->base))
+ return PTR_ERR(clk_data->base);
+
+ clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(clk_data->msspll_base))
+ return PTR_ERR(clk_data->msspll_base);
+
+ clk_data->regmap = devm_regmap_init_mmio(dev, clk_data->base, &mpfs_clk_regmap_config);
+ if (IS_ERR(clk_data->regmap))
+ return PTR_ERR(clk_data->regmap);
+
+ return mpfs_reset_controller_register(dev, clk_data->regmap);
+}
+
static int mpfs_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -446,49 +513,52 @@ static int mpfs_clk_probe(struct platform_device *pdev)
int ret;
/* CLK_RESERVED is not part of clock arrays, so add 1 */
- num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks)
- + ARRAY_SIZE(mpfs_periph_clks) + 1;
+ num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_msspll_out_clks)
+ + ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1;
clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
- clk_data->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(clk_data->base))
- return PTR_ERR(clk_data->base);
-
- clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(clk_data->msspll_base))
- return PTR_ERR(clk_data->msspll_base);
+ ret = mpfs_clk_syscon_probe(clk_data, pdev);
+ if (ret) {
+ ret = mpfs_clk_old_format_probe(clk_data, pdev);
+ if (ret)
+ return ret;
+ }
clk_data->hw_data.num = num_clks;
+ clk_data->dev = dev;
+ dev_set_drvdata(dev, clk_data);
ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),
clk_data);
if (ret)
return ret;
- ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
+ ret = mpfs_clk_register_msspll_outs(dev, mpfs_msspll_out_clks,
+ ARRAY_SIZE(mpfs_msspll_out_clks),
+ clk_data);
if (ret)
return ret;
- ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),
- clk_data);
+ ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
if (ret)
return ret;
- ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
+ ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),
+ clk_data);
if (ret)
return ret;
- return ret;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
}
static const struct of_device_id mpfs_clk_of_match_table[] = {
{ .compatible = "microchip,mpfs-clkcfg", },
{}
};
-MODULE_DEVICE_TABLE(of, mpfs_clk_match_table);
+MODULE_DEVICE_TABLE(of, mpfs_clk_of_match_table);
static struct platform_driver mpfs_clk_driver = {
.probe = mpfs_clk_probe,
@@ -511,4 +581,7 @@ static void __exit clk_mpfs_exit(void)
module_exit(clk_mpfs_exit);
MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>");
+MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_IMPORT_NS("MCHP_CLK_MPFS");
diff --git a/drivers/clk/microchip/clk-pic32mzda.c b/drivers/clk/microchip/clk-pic32mzda.c
index b72c76f9ecd1..27599829ea40 100644
--- a/drivers/clk/microchip/clk-pic32mzda.c
+++ b/drivers/clk/microchip/clk-pic32mzda.c
@@ -9,8 +9,8 @@
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <asm/traps.h>
@@ -184,7 +184,7 @@ static int pic32mzda_clk_probe(struct platform_device *pdev)
clks[UPLLCLK] = clk_register_fixed_rate(&pdev->dev, "usbphy_clk", NULL,
0, 24000000);
/* fixed rate (optional) clock */
- if (of_find_property(np, "microchip,pic32mzda-sosc", NULL)) {
+ if (of_property_read_bool(np, "microchip,pic32mzda-sosc")) {
pr_info("pic32-clk: dt requests SOSC.\n");
clks[SOSCCLK] = pic32_sosc_clk_register(&sosc_clk, core);
}
diff --git a/drivers/clk/mmp/Kconfig b/drivers/clk/mmp/Kconfig
new file mode 100644
index 000000000000..b0d2fea3cda5
--- /dev/null
+++ b/drivers/clk/mmp/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config COMMON_CLK_PXA1908
+ bool "Clock driver for Marvell PXA1908"
+ depends on ARCH_MMP || COMPILE_TEST
+ depends on OF
+ default y if ARCH_MMP && ARM64
+ select AUXILIARY_BUS
+ help
+ This driver supports the Marvell PXA1908 SoC clocks.
diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile
index cbcc2f8430a2..0a94f2f08563 100644
--- a/drivers/clk/mmp/Makefile
+++ b/drivers/clk/mmp/Makefile
@@ -11,8 +11,7 @@ obj-$(CONFIG_MACH_MMP_DT) += clk-of-pxa168.o clk-of-pxa910.o
obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o pwr-island.o
obj-$(CONFIG_COMMON_CLK_MMP2_AUDIO) += clk-audio.o
-obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
-obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
-obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o
+obj-$(CONFIG_COMMON_CLK_PXA1908) += clk-pxa1908-apbc.o clk-pxa1908-apbcp.o \
+ clk-pxa1908-mpmu.o clk-pxa1908-apmu.o
-obj-y += clk-of-pxa1928.o
+obj-$(CONFIG_ARCH_MMP) += clk-of-pxa1928.o
diff --git a/drivers/clk/mmp/clk-audio.c b/drivers/clk/mmp/clk-audio.c
index 7aa7f4a9564f..ed27fc796c94 100644
--- a/drivers/clk/mmp/clk-audio.c
+++ b/drivers/clk/mmp/clk-audio.c
@@ -55,6 +55,8 @@
#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK (0x7ff << 0)
#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(x) ((x) << 0)
+#define CLK_AUDIO_NR_CLKS 3
+
struct mmp2_audio_clk {
void __iomem *mmio_base;
@@ -162,23 +164,23 @@ static unsigned long audio_pll_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int audio_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned int prediv;
unsigned int postdiv;
long rounded = 0;
for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) {
- if (predivs[prediv].parent_rate != *parent_rate)
+ if (predivs[prediv].parent_rate != req->best_parent_rate)
continue;
for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) {
long freq = predivs[prediv].freq_vco;
freq /= postdivs[postdiv].divisor;
- if (freq == rate)
- return rate;
- if (freq < rate)
+ if (freq == req->rate)
+ return 0;
+ if (freq < req->rate)
continue;
if (rounded && freq > rounded)
continue;
@@ -186,7 +188,9 @@ static long audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
}
}
- return rounded;
+ req->rate = rounded;
+
+ return 0;
}
static int audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -226,7 +230,7 @@ static int audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops audio_pll_ops = {
.recalc_rate = audio_pll_recalc_rate,
- .round_rate = audio_pll_round_rate,
+ .determine_rate = audio_pll_determine_rate,
.set_rate = audio_pll_set_rate,
};
@@ -336,7 +340,7 @@ static int register_clocks(struct mmp2_audio_clk *priv, struct device *dev)
priv->clk_data.hws[MMP2_CLK_AUDIO_SYSCLK] = &priv->sysclk_gate.hw;
priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA0] = &priv->sspa0_gate.hw;
priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA1] = &priv->sspa1_gate.hw;
- priv->clk_data.num = MMP2_CLK_AUDIO_NR_CLKS;
+ priv->clk_data.num = CLK_AUDIO_NR_CLKS;
return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
&priv->clk_data);
@@ -349,7 +353,7 @@ static int mmp2_audio_clk_probe(struct platform_device *pdev)
priv = devm_kzalloc(&pdev->dev,
struct_size(priv, clk_data.hws,
- MMP2_CLK_AUDIO_NR_CLKS),
+ CLK_AUDIO_NR_CLKS),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -384,12 +388,10 @@ disable_pm_runtime:
return ret;
}
-static int mmp2_audio_clk_remove(struct platform_device *pdev)
+static void mmp2_audio_clk_remove(struct platform_device *pdev)
{
pm_clk_destroy(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
#ifdef CONFIG_PM
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c
index 1b90867b60c4..0b1bb01346f0 100644
--- a/drivers/clk/mmp/clk-frac.c
+++ b/drivers/clk/mmp/clk-frac.c
@@ -21,30 +21,32 @@
#define to_clk_factor(hw) container_of(hw, struct mmp_clk_factor, hw)
-static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
- unsigned long *prate)
+static int clk_factor_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct mmp_clk_factor *factor = to_clk_factor(hw);
u64 rate = 0, prev_rate;
+ struct u32_fract *d;
int i;
for (i = 0; i < factor->ftbl_cnt; i++) {
- prev_rate = rate;
- rate = *prate;
- rate *= factor->ftbl[i].den;
- do_div(rate, factor->ftbl[i].num * factor->masks->factor);
+ d = &factor->ftbl[i];
- if (rate > drate)
+ prev_rate = rate;
+ rate = (u64)(req->best_parent_rate) * d->denominator;
+ do_div(rate, d->numerator * factor->masks->factor);
+ if (rate > req->rate)
break;
}
- if ((i == 0) || (i == factor->ftbl_cnt)) {
- return rate;
- } else {
- if ((drate - prev_rate) > (rate - drate))
- return rate;
- else
- return prev_rate;
- }
+
+ if ((i == 0) || (i == factor->ftbl_cnt))
+ req->rate = rate;
+ else if ((req->rate - prev_rate) > (rate - req->rate))
+ req->rate = rate;
+ else
+ req->rate = prev_rate;
+
+ return 0;
}
static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
@@ -52,23 +54,22 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
{
struct mmp_clk_factor *factor = to_clk_factor(hw);
struct mmp_clk_factor_masks *masks = factor->masks;
- unsigned int val, num, den;
+ struct u32_fract d;
+ unsigned int val;
u64 rate;
val = readl_relaxed(factor->base);
/* calculate numerator */
- num = (val >> masks->num_shift) & masks->num_mask;
+ d.numerator = (val >> masks->num_shift) & masks->num_mask;
/* calculate denominator */
- den = (val >> masks->den_shift) & masks->den_mask;
-
- if (!den)
+ d.denominator = (val >> masks->den_shift) & masks->den_mask;
+ if (!d.denominator)
return 0;
- rate = parent_rate;
- rate *= den;
- do_div(rate, num * factor->masks->factor);
+ rate = (u64)parent_rate * d.denominator;
+ do_div(rate, d.numerator * factor->masks->factor);
return rate;
}
@@ -82,18 +83,18 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
int i;
unsigned long val;
unsigned long flags = 0;
+ struct u32_fract *d;
u64 rate = 0;
for (i = 0; i < factor->ftbl_cnt; i++) {
- rate = prate;
- rate *= factor->ftbl[i].den;
- do_div(rate, factor->ftbl[i].num * factor->masks->factor);
+ d = &factor->ftbl[i];
+ rate = (u64)prate * d->denominator;
+ do_div(rate, d->numerator * factor->masks->factor);
if (rate > drate)
break;
}
- if (i > 0)
- i--;
+ d = i ? &factor->ftbl[i - 1] : &factor->ftbl[0];
if (factor->lock)
spin_lock_irqsave(factor->lock, flags);
@@ -101,10 +102,10 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
val = readl_relaxed(factor->base);
val &= ~(masks->num_mask << masks->num_shift);
- val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift;
+ val |= (d->numerator & masks->num_mask) << masks->num_shift;
val &= ~(masks->den_mask << masks->den_shift);
- val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift;
+ val |= (d->denominator & masks->den_mask) << masks->den_shift;
writel_relaxed(val, factor->base);
@@ -118,7 +119,8 @@ static int clk_factor_init(struct clk_hw *hw)
{
struct mmp_clk_factor *factor = to_clk_factor(hw);
struct mmp_clk_factor_masks *masks = factor->masks;
- u32 val, num, den;
+ struct u32_fract d;
+ u32 val;
int i;
unsigned long flags = 0;
@@ -128,23 +130,22 @@ static int clk_factor_init(struct clk_hw *hw)
val = readl(factor->base);
/* calculate numerator */
- num = (val >> masks->num_shift) & masks->num_mask;
+ d.numerator = (val >> masks->num_shift) & masks->num_mask;
/* calculate denominator */
- den = (val >> masks->den_shift) & masks->den_mask;
+ d.denominator = (val >> masks->den_shift) & masks->den_mask;
for (i = 0; i < factor->ftbl_cnt; i++)
- if (den == factor->ftbl[i].den && num == factor->ftbl[i].num)
+ if (d.denominator == factor->ftbl[i].denominator &&
+ d.numerator == factor->ftbl[i].numerator)
break;
if (i >= factor->ftbl_cnt) {
val &= ~(masks->num_mask << masks->num_shift);
- val |= (factor->ftbl[0].num & masks->num_mask) <<
- masks->num_shift;
+ val |= (factor->ftbl[0].numerator & masks->num_mask) << masks->num_shift;
val &= ~(masks->den_mask << masks->den_shift);
- val |= (factor->ftbl[0].den & masks->den_mask) <<
- masks->den_shift;
+ val |= (factor->ftbl[0].denominator & masks->den_mask) << masks->den_shift;
}
if (!(val & masks->enable_mask) || i >= factor->ftbl_cnt) {
@@ -160,7 +161,7 @@ static int clk_factor_init(struct clk_hw *hw)
static const struct clk_ops clk_factor_ops = {
.recalc_rate = clk_factor_recalc_rate,
- .round_rate = clk_factor_round_rate,
+ .determine_rate = clk_factor_determine_rate,
.set_rate = clk_factor_set_rate,
.init = clk_factor_init,
};
@@ -168,8 +169,7 @@ static const struct clk_ops clk_factor_ops = {
struct clk *mmp_clk_register_factor(const char *name, const char *parent_name,
unsigned long flags, void __iomem *base,
struct mmp_clk_factor_masks *masks,
- struct mmp_clk_factor_tbl *ftbl,
- unsigned int ftbl_cnt, spinlock_t *lock)
+ struct u32_fract *ftbl, unsigned int ftbl_cnt, spinlock_t *lock)
{
struct mmp_clk_factor *factor;
struct clk_init_data init;
diff --git a/drivers/clk/mmp/clk-gate.c b/drivers/clk/mmp/clk-gate.c
index 350eeb3e9e25..6855815ee8be 100644
--- a/drivers/clk/mmp/clk-gate.c
+++ b/drivers/clk/mmp/clk-gate.c
@@ -15,7 +15,7 @@
#include "clk.h"
/*
- * Some clocks will have mutiple bits to enable the clocks, and
+ * Some clocks will have multiple bits to enable the clocks, and
* the bits to disable the clock is not same as enabling bits.
*/
diff --git a/drivers/clk/mmp/clk-mix.c b/drivers/clk/mmp/clk-mix.c
index 454d131f475e..07ac9e6937e5 100644
--- a/drivers/clk/mmp/clk-mix.c
+++ b/drivers/clk/mmp/clk-mix.c
@@ -447,7 +447,6 @@ struct clk *mmp_clk_register_mix(struct device *dev,
struct mmp_clk_mix *mix;
struct clk *clk;
struct clk_init_data init;
- size_t table_bytes;
mix = kzalloc(sizeof(*mix), GFP_KERNEL);
if (!mix)
@@ -461,8 +460,8 @@ struct clk *mmp_clk_register_mix(struct device *dev,
memcpy(&mix->reg_info, &config->reg_info, sizeof(config->reg_info));
if (config->table) {
- table_bytes = sizeof(*config->table) * config->table_size;
- mix->table = kmemdup(config->table, table_bytes, GFP_KERNEL);
+ mix->table = kmemdup_array(config->table, config->table_size,
+ sizeof(*mix->table), GFP_KERNEL);
if (!mix->table)
goto free_mix;
@@ -470,9 +469,8 @@ struct clk *mmp_clk_register_mix(struct device *dev,
}
if (config->mux_table) {
- table_bytes = sizeof(u32) * num_parents;
- mix->mux_table = kmemdup(config->mux_table, table_bytes,
- GFP_KERNEL);
+ mix->mux_table = kmemdup_array(config->mux_table, num_parents,
+ sizeof(*mix->mux_table), GFP_KERNEL);
if (!mix->mux_table) {
kfree(mix->table);
goto free_mix;
diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c
deleted file mode 100644
index aabacfa10158..000000000000
--- a/drivers/clk/mmp/clk-mmp2.c
+++ /dev/null
@@ -1,454 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * mmp2 clock framework source file
- *
- * Copyright (C) 2012 Marvell
- * Chao Xie <xiechao.mail@gmail.com>
- */
-
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/clk/mmp.h>
-
-#include "clk.h"
-
-#define APBC_RTC 0x0
-#define APBC_TWSI0 0x4
-#define APBC_TWSI1 0x8
-#define APBC_TWSI2 0xc
-#define APBC_TWSI3 0x10
-#define APBC_TWSI4 0x7c
-#define APBC_TWSI5 0x80
-#define APBC_KPC 0x18
-#define APBC_UART0 0x2c
-#define APBC_UART1 0x30
-#define APBC_UART2 0x34
-#define APBC_UART3 0x88
-#define APBC_GPIO 0x38
-#define APBC_PWM0 0x3c
-#define APBC_PWM1 0x40
-#define APBC_PWM2 0x44
-#define APBC_PWM3 0x48
-#define APBC_SSP0 0x50
-#define APBC_SSP1 0x54
-#define APBC_SSP2 0x58
-#define APBC_SSP3 0x5c
-#define APMU_SDH0 0x54
-#define APMU_SDH1 0x58
-#define APMU_SDH2 0xe8
-#define APMU_SDH3 0xec
-#define APMU_USB 0x5c
-#define APMU_DISP0 0x4c
-#define APMU_DISP1 0x110
-#define APMU_CCIC0 0x50
-#define APMU_CCIC1 0xf4
-#define MPMU_UART_PLL 0x14
-
-static DEFINE_SPINLOCK(clk_lock);
-
-static struct mmp_clk_factor_masks uart_factor_masks = {
- .factor = 2,
- .num_mask = 0x1fff,
- .den_mask = 0x1fff,
- .num_shift = 16,
- .den_shift = 0,
-};
-
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 8125, .den = 1536}, /*14.745MHZ */
- {.num = 3521, .den = 689}, /*19.23MHZ */
-};
-
-static const char *uart_parent[] = {"uart_pll", "vctcxo"};
-static const char *ssp_parent[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
-static const char *sdh_parent[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
-static const char *disp_parent[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
-static const char *ccic_parent[] = {"pll1_2", "pll1_16", "vctcxo"};
-
-void __init mmp2_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys,
- phys_addr_t apbc_phys)
-{
- struct clk *clk;
- struct clk *vctcxo;
- void __iomem *mpmu_base;
- void __iomem *apmu_base;
- void __iomem *apbc_base;
-
- mpmu_base = ioremap(mpmu_phys, SZ_4K);
- if (!mpmu_base) {
- pr_err("error to ioremap MPMU base\n");
- return;
- }
-
- apmu_base = ioremap(apmu_phys, SZ_4K);
- if (!apmu_base) {
- pr_err("error to ioremap APMU base\n");
- return;
- }
-
- apbc_base = ioremap(apbc_phys, SZ_4K);
- if (!apbc_base) {
- pr_err("error to ioremap APBC base\n");
- return;
- }
-
- clk = clk_register_fixed_rate(NULL, "clk32", NULL, 0, 3200);
- clk_register_clkdev(clk, "clk32", NULL);
-
- vctcxo = clk_register_fixed_rate(NULL, "vctcxo", NULL, 0, 26000000);
- clk_register_clkdev(vctcxo, "vctcxo", NULL);
-
- clk = clk_register_fixed_rate(NULL, "pll1", NULL, 0, 800000000);
- clk_register_clkdev(clk, "pll1", NULL);
-
- clk = clk_register_fixed_rate(NULL, "usb_pll", NULL, 0, 480000000);
- clk_register_clkdev(clk, "usb_pll", NULL);
-
- clk = clk_register_fixed_rate(NULL, "pll2", NULL, 0, 960000000);
- clk_register_clkdev(clk, "pll2", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_2", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_4", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_8", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_16", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_20", "pll1_4",
- CLK_SET_RATE_PARENT, 1, 5);
- clk_register_clkdev(clk, "pll1_20", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_3", "pll1",
- CLK_SET_RATE_PARENT, 1, 3);
- clk_register_clkdev(clk, "pll1_3", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_3",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_6", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_12", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_2", "pll2",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll2_2", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_4", "pll2_2",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll2_4", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_8", "pll2_4",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll2_8", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_16", "pll2_8",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll2_16", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_3", "pll2",
- CLK_SET_RATE_PARENT, 1, 3);
- clk_register_clkdev(clk, "pll2_3", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_6", "pll2_3",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll2_6", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll2_12", "pll2_6",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll2_12", NULL);
-
- clk = clk_register_fixed_factor(NULL, "vctcxo_2", "vctcxo",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "vctcxo_2", NULL);
-
- clk = clk_register_fixed_factor(NULL, "vctcxo_4", "vctcxo_2",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "vctcxo_4", NULL);
-
- clk = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
- mpmu_base + MPMU_UART_PLL,
- &uart_factor_masks, uart_factor_tbl,
- ARRAY_SIZE(uart_factor_tbl), &clk_lock);
- clk_set_rate(clk, 14745600);
- clk_register_clkdev(clk, "uart_pll", NULL);
-
- clk = mmp_clk_register_apbc("twsi0", "vctcxo",
- apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
-
- clk = mmp_clk_register_apbc("twsi1", "vctcxo",
- apbc_base + APBC_TWSI1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
-
- clk = mmp_clk_register_apbc("twsi2", "vctcxo",
- apbc_base + APBC_TWSI2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.2");
-
- clk = mmp_clk_register_apbc("twsi3", "vctcxo",
- apbc_base + APBC_TWSI3, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.3");
-
- clk = mmp_clk_register_apbc("twsi4", "vctcxo",
- apbc_base + APBC_TWSI4, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.4");
-
- clk = mmp_clk_register_apbc("twsi5", "vctcxo",
- apbc_base + APBC_TWSI5, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.5");
-
- clk = mmp_clk_register_apbc("gpio", "vctcxo",
- apbc_base + APBC_GPIO, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp2-gpio");
-
- clk = mmp_clk_register_apbc("kpc", "clk32",
- apbc_base + APBC_KPC, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa27x-keypad");
-
- clk = mmp_clk_register_apbc("rtc", "clk32",
- apbc_base + APBC_RTC, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-rtc");
-
- clk = mmp_clk_register_apbc("pwm0", "vctcxo",
- apbc_base + APBC_PWM0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp2-pwm.0");
-
- clk = mmp_clk_register_apbc("pwm1", "vctcxo",
- apbc_base + APBC_PWM1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp2-pwm.1");
-
- clk = mmp_clk_register_apbc("pwm2", "vctcxo",
- apbc_base + APBC_PWM2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp2-pwm.2");
-
- clk = mmp_clk_register_apbc("pwm3", "vctcxo",
- apbc_base + APBC_PWM3, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp2-pwm.3");
-
- clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, vctcxo);
- clk_register_clkdev(clk, "uart_mux.0", NULL);
-
- clk = mmp_clk_register_apbc("uart0", "uart0_mux",
- apbc_base + APBC_UART0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
-
- clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, vctcxo);
- clk_register_clkdev(clk, "uart_mux.1", NULL);
-
- clk = mmp_clk_register_apbc("uart1", "uart1_mux",
- apbc_base + APBC_UART1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
-
- clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, vctcxo);
- clk_register_clkdev(clk, "uart_mux.2", NULL);
-
- clk = mmp_clk_register_apbc("uart2", "uart2_mux",
- apbc_base + APBC_UART2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
-
- clk = clk_register_mux(NULL, "uart3_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART3, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, vctcxo);
- clk_register_clkdev(clk, "uart_mux.3", NULL);
-
- clk = mmp_clk_register_apbc("uart3", "uart3_mux",
- apbc_base + APBC_UART3, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.3");
-
- clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "uart_mux.0", NULL);
-
- clk = mmp_clk_register_apbc("ssp0", "ssp0_mux",
- apbc_base + APBC_SSP0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.0");
-
- clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.1", NULL);
-
- clk = mmp_clk_register_apbc("ssp1", "ssp1_mux",
- apbc_base + APBC_SSP1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.1");
-
- clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.2", NULL);
-
- clk = mmp_clk_register_apbc("ssp2", "ssp2_mux",
- apbc_base + APBC_SSP2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.2");
-
- clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.3", NULL);
-
- clk = mmp_clk_register_apbc("ssp3", "ssp3_mux",
- apbc_base + APBC_SSP3, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.3");
-
- clk = clk_register_mux(NULL, "sdh_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock);
- clk_register_clkdev(clk, "sdh_mux", NULL);
-
- clk = clk_register_divider(NULL, "sdh_div", "sdh_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_SDH0,
- 10, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
- clk_register_clkdev(clk, "sdh_div", NULL);
-
- clk = mmp_clk_register_apmu("sdh0", "sdh_div", apmu_base + APMU_SDH0,
- 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxav3.0");
-
- clk = mmp_clk_register_apmu("sdh1", "sdh_div", apmu_base + APMU_SDH1,
- 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxav3.1");
-
- clk = mmp_clk_register_apmu("sdh2", "sdh_div", apmu_base + APMU_SDH2,
- 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxav3.2");
-
- clk = mmp_clk_register_apmu("sdh3", "sdh_div", apmu_base + APMU_SDH3,
- 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxav3.3");
-
- clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB,
- 0x9, &clk_lock);
- clk_register_clkdev(clk, "usb_clk", NULL);
-
- clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
- ARRAY_SIZE(disp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock);
- clk_register_clkdev(clk, "disp_mux.0", NULL);
-
- clk = clk_register_divider(NULL, "disp0_div", "disp0_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_DISP0,
- 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
- clk_register_clkdev(clk, "disp_div.0", NULL);
-
- clk = mmp_clk_register_apmu("disp0", "disp0_div",
- apmu_base + APMU_DISP0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-disp.0");
-
- clk = clk_register_divider(NULL, "disp0_sphy_div", "disp0_mux", 0,
- apmu_base + APMU_DISP0, 15, 5, 0, &clk_lock);
- clk_register_clkdev(clk, "disp_sphy_div.0", NULL);
-
- clk = mmp_clk_register_apmu("disp0_sphy", "disp0_sphy_div",
- apmu_base + APMU_DISP0, 0x1024, &clk_lock);
- clk_register_clkdev(clk, "disp_sphy.0", NULL);
-
- clk = clk_register_mux(NULL, "disp1_mux", disp_parent,
- ARRAY_SIZE(disp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock);
- clk_register_clkdev(clk, "disp_mux.1", NULL);
-
- clk = clk_register_divider(NULL, "disp1_div", "disp1_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_DISP1,
- 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
- clk_register_clkdev(clk, "disp_div.1", NULL);
-
- clk = mmp_clk_register_apmu("disp1", "disp1_div",
- apmu_base + APMU_DISP1, 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-disp.1");
-
- clk = mmp_clk_register_apmu("ccic_arbiter", "vctcxo",
- apmu_base + APMU_CCIC0, 0x1800, &clk_lock);
- clk_register_clkdev(clk, "ccic_arbiter", NULL);
-
- clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock);
- clk_register_clkdev(clk, "ccic_mux.0", NULL);
-
- clk = clk_register_divider(NULL, "ccic0_div", "ccic0_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
- 17, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
- clk_register_clkdev(clk, "ccic_div.0", NULL);
-
- clk = mmp_clk_register_apmu("ccic0", "ccic0_div",
- apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
-
- clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_div",
- apmu_base + APMU_CCIC0, 0x24, &clk_lock);
- clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
-
- clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_div",
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
- 10, 5, 0, &clk_lock);
- clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.0");
-
- clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
- apmu_base + APMU_CCIC0, 0x300, &clk_lock);
- clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
-
- clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock);
- clk_register_clkdev(clk, "ccic_mux.1", NULL);
-
- clk = clk_register_divider(NULL, "ccic1_div", "ccic1_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1,
- 16, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
- clk_register_clkdev(clk, "ccic_div.1", NULL);
-
- clk = mmp_clk_register_apmu("ccic1", "ccic1_div",
- apmu_base + APMU_CCIC1, 0x1b, &clk_lock);
- clk_register_clkdev(clk, "fnclk", "mmp-ccic.1");
-
- clk = mmp_clk_register_apmu("ccic1_phy", "ccic1_div",
- apmu_base + APMU_CCIC1, 0x24, &clk_lock);
- clk_register_clkdev(clk, "phyclk", "mmp-ccic.1");
-
- clk = clk_register_divider(NULL, "ccic1_sphy_div", "ccic1_div",
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1,
- 10, 5, 0, &clk_lock);
- clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.1");
-
- clk = mmp_clk_register_apmu("ccic1_sphy", "ccic1_sphy_div",
- apmu_base + APMU_CCIC1, 0x300, &clk_lock);
- clk_register_clkdev(clk, "sphyclk", "mmp-ccic.1");
-}
diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c
index bcf60f43aa13..a4f15cee630e 100644
--- a/drivers/clk/mmp/clk-of-mmp2.c
+++ b/drivers/clk/mmp/clk-of-mmp2.c
@@ -78,6 +78,8 @@
#define MPMU_PLL_DIFF_CTRL 0x68
#define MPMU_PLL2_CTRL1 0x414
+#define NR_CLKS 200
+
enum mmp2_clk_model {
CLK_MODEL_MMP2,
CLK_MODEL_MMP3,
@@ -141,9 +143,9 @@ static struct mmp_clk_factor_masks uart_factor_masks = {
.den_shift = 0,
};
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 8125, .den = 1536}, /*14.745MHZ */
- {.num = 3521, .den = 689}, /*19.23MHZ */
+static struct u32_fract uart_factor_tbl[] = {
+ { .numerator = 8125, .denominator = 1536 }, /* 14.745MHZ */
+ { .numerator = 3521, .denominator = 689 }, /* 19.23MHZ */
};
static struct mmp_clk_factor_masks i2s_factor_masks = {
@@ -155,16 +157,16 @@ static struct mmp_clk_factor_masks i2s_factor_masks = {
.enable_mask = 0xd0000000,
};
-static struct mmp_clk_factor_tbl i2s_factor_tbl[] = {
- {.num = 24868, .den = 511}, /* 2.0480 MHz */
- {.num = 28003, .den = 793}, /* 2.8224 MHz */
- {.num = 24941, .den = 1025}, /* 4.0960 MHz */
- {.num = 28003, .den = 1586}, /* 5.6448 MHz */
- {.num = 31158, .den = 2561}, /* 8.1920 MHz */
- {.num = 16288, .den = 1845}, /* 11.2896 MHz */
- {.num = 20772, .den = 2561}, /* 12.2880 MHz */
- {.num = 8144, .den = 1845}, /* 22.5792 MHz */
- {.num = 10386, .den = 2561}, /* 24.5760 MHz */
+static struct u32_fract i2s_factor_tbl[] = {
+ { .numerator = 24868, .denominator = 511 }, /* 2.0480 MHz */
+ { .numerator = 28003, .denominator = 793 }, /* 2.8224 MHz */
+ { .numerator = 24941, .denominator = 1025 }, /* 4.0960 MHz */
+ { .numerator = 28003, .denominator = 1586 }, /* 5.6448 MHz */
+ { .numerator = 31158, .denominator = 2561 }, /* 8.1920 MHz */
+ { .numerator = 16288, .denominator = 1845 }, /* 11.2896 MHz */
+ { .numerator = 20772, .denominator = 2561 }, /* 12.2880 MHz */
+ { .numerator = 8144, .denominator = 1845 }, /* 22.5792 MHz */
+ { .numerator = 10386, .denominator = 2561 }, /* 24.5760 MHz */
};
static DEFINE_SPINLOCK(acgr_lock);
@@ -543,7 +545,7 @@ static void __init mmp2_clk_init(struct device_node *np)
mmp2_pm_domain_init(np, pxa_unit);
- mmp_clk_init(np, &pxa_unit->unit, MMP2_NR_CLKS);
+ mmp_clk_init(np, &pxa_unit->unit, NR_CLKS);
mmp2_main_clk_init(pxa_unit);
diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c
index 48dfb18b490e..5f250427e60d 100644
--- a/drivers/clk/mmp/clk-of-pxa168.c
+++ b/drivers/clk/mmp/clk-of-pxa168.c
@@ -19,9 +19,6 @@
#include "clk.h"
#include "reset.h"
-#define APBC_RTC 0x28
-#define APBC_TWSI0 0x2c
-#define APBC_KPC 0x30
#define APBC_UART0 0x0
#define APBC_UART1 0x4
#define APBC_GPIO 0x8
@@ -29,22 +26,44 @@
#define APBC_PWM1 0x10
#define APBC_PWM2 0x14
#define APBC_PWM3 0x18
+#define APBC_RTC 0x28
+#define APBC_TWSI0 0x2c
+#define APBC_KPC 0x30
#define APBC_TIMER 0x34
+#define APBC_AIB 0x3c
+#define APBC_SW_JTAG 0x40
+#define APBC_ONEWIRE 0x48
+#define APBC_TWSI1 0x6c
+#define APBC_UART2 0x70
+#define APBC_AC97 0x84
#define APBC_SSP0 0x81c
#define APBC_SSP1 0x820
#define APBC_SSP2 0x84c
#define APBC_SSP3 0x858
#define APBC_SSP4 0x85c
-#define APBC_TWSI1 0x6c
-#define APBC_UART2 0x70
+#define APMU_DISP0 0x4c
+#define APMU_CCIC0 0x50
#define APMU_SDH0 0x54
#define APMU_SDH1 0x58
#define APMU_USB 0x5c
-#define APMU_DISP0 0x4c
-#define APMU_CCIC0 0x50
#define APMU_DFC 0x60
+#define APMU_DMA 0x64
+#define APMU_BUS 0x6c
+#define APMU_GC 0xcc
+#define APMU_SMC 0xd4
+#define APMU_XD 0xdc
+#define APMU_SDH2 0xe0
+#define APMU_SDH3 0xe4
+#define APMU_CF 0xf0
+#define APMU_MSP 0xf4
+#define APMU_CMU 0xf8
+#define APMU_FE 0xfc
+#define APMU_PCIE 0x100
+#define APMU_EPD 0x104
#define MPMU_UART_PLL 0x14
+#define NR_CLKS 200
+
struct pxa168_clk_unit {
struct mmp_clk_unit unit;
void __iomem *mpmu_base;
@@ -71,9 +90,12 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
{PXA168_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0},
{PXA168_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0},
{PXA168_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0},
- {PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0},
- {PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0},
+ {PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 1, 5, 0},
+ {PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 1, 5, 0},
{PXA168_CLK_PLL1_3_16, "pll1_3_16", "pll1", 3, 16, 0},
+ {PXA168_CLK_PLL1_2_1_10, "pll1_2_1_10", "pll1_2", 1, 10, 0},
+ {PXA168_CLK_PLL1_2_3_16, "pll1_2_3_16", "pll1_2", 3, 16, 0},
+ {PXA168_CLK_CLK32_2, "clk32_2", "clk32", 1, 2, 0},
};
static struct mmp_clk_factor_masks uart_factor_masks = {
@@ -84,8 +106,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = {
.den_shift = 0,
};
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 8125, .den = 1536}, /*14.745MHZ */
+static struct u32_fract uart_factor_tbl[] = {
+ { .numerator = 8125, .denominator = 1536 }, /* 14.745MHZ */
};
static void pxa168_pll_init(struct pxa168_clk_unit *pxa_unit)
@@ -107,24 +129,44 @@ static void pxa168_pll_init(struct pxa168_clk_unit *pxa_unit)
mmp_clk_add(unit, PXA168_CLK_UART_PLL, clk);
}
+static DEFINE_SPINLOCK(twsi0_lock);
+static DEFINE_SPINLOCK(twsi1_lock);
+static const char * const twsi_parent_names[] = {"pll1_2_1_10", "pll1_2_1_5"};
+
+static DEFINE_SPINLOCK(kpc_lock);
+static const char * const kpc_parent_names[] = {"clk32", "clk32_2", "pll1_24"};
+
+static DEFINE_SPINLOCK(pwm0_lock);
+static DEFINE_SPINLOCK(pwm1_lock);
+static DEFINE_SPINLOCK(pwm2_lock);
+static DEFINE_SPINLOCK(pwm3_lock);
+static const char * const pwm_parent_names[] = {"pll1_48", "clk32"};
+
static DEFINE_SPINLOCK(uart0_lock);
static DEFINE_SPINLOCK(uart1_lock);
static DEFINE_SPINLOCK(uart2_lock);
-static const char *uart_parent_names[] = {"pll1_3_16", "uart_pll"};
+static const char * const uart_parent_names[] = {"pll1_2_3_16", "uart_pll"};
static DEFINE_SPINLOCK(ssp0_lock);
static DEFINE_SPINLOCK(ssp1_lock);
static DEFINE_SPINLOCK(ssp2_lock);
static DEFINE_SPINLOCK(ssp3_lock);
static DEFINE_SPINLOCK(ssp4_lock);
-static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
+static const char * const ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
static DEFINE_SPINLOCK(timer_lock);
-static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"};
+static const char * const timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"};
static DEFINE_SPINLOCK(reset_lock);
static struct mmp_param_mux_clk apbc_mux_clks[] = {
+ {0, "twsi0_mux", twsi_parent_names, ARRAY_SIZE(twsi_parent_names), CLK_SET_RATE_PARENT, APBC_TWSI0, 4, 3, 0, &twsi0_lock},
+ {0, "twsi1_mux", twsi_parent_names, ARRAY_SIZE(twsi_parent_names), CLK_SET_RATE_PARENT, APBC_TWSI1, 4, 3, 0, &twsi1_lock},
+ {0, "kpc_mux", kpc_parent_names, ARRAY_SIZE(kpc_parent_names), CLK_SET_RATE_PARENT, APBC_KPC, 4, 3, 0, &kpc_lock},
+ {0, "pwm0_mux", pwm_parent_names, ARRAY_SIZE(pwm_parent_names), CLK_SET_RATE_PARENT, APBC_PWM0, 4, 3, 0, &pwm0_lock},
+ {0, "pwm1_mux", pwm_parent_names, ARRAY_SIZE(pwm_parent_names), CLK_SET_RATE_PARENT, APBC_PWM1, 4, 3, 0, &pwm1_lock},
+ {0, "pwm2_mux", pwm_parent_names, ARRAY_SIZE(pwm_parent_names), CLK_SET_RATE_PARENT, APBC_PWM2, 4, 3, 0, &pwm2_lock},
+ {0, "pwm3_mux", pwm_parent_names, ARRAY_SIZE(pwm_parent_names), CLK_SET_RATE_PARENT, APBC_PWM3, 4, 3, 0, &pwm3_lock},
{0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock},
{0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock},
{0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART2, 4, 3, 0, &uart2_lock},
@@ -137,16 +179,15 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = {
};
static struct mmp_param_gate_clk apbc_gate_clks[] = {
- {PXA168_CLK_TWSI0, "twsi0_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x3, 0x3, 0x0, 0, &reset_lock},
- {PXA168_CLK_TWSI1, "twsi1_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x3, 0x3, 0x0, 0, &reset_lock},
- {PXA168_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x3, 0x3, 0x0, 0, &reset_lock},
- {PXA168_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL},
+ {PXA168_CLK_TWSI0, "twsi0_clk", "twsi0_mux", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x3, 0x3, 0x0, 0, &twsi0_lock},
+ {PXA168_CLK_TWSI1, "twsi1_clk", "twsi1_mux", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x3, 0x3, 0x0, 0, &twsi1_lock},
+ {PXA168_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x1, 0x1, 0x0, 0, &reset_lock},
+ {PXA168_CLK_KPC, "kpc_clk", "kpc_mux", CLK_SET_RATE_PARENT, APBC_KPC, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &kpc_lock},
{PXA168_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL},
- {PXA168_CLK_PWM0, "pwm0_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM0, 0x3, 0x3, 0x0, 0, &reset_lock},
- {PXA168_CLK_PWM1, "pwm1_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM1, 0x3, 0x3, 0x0, 0, &reset_lock},
- {PXA168_CLK_PWM2, "pwm2_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM2, 0x3, 0x3, 0x0, 0, &reset_lock},
- {PXA168_CLK_PWM3, "pwm3_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM3, 0x3, 0x3, 0x0, 0, &reset_lock},
- /* The gate clocks has mux parent. */
+ {PXA168_CLK_PWM0, "pwm0_clk", "pwm0_mux", CLK_SET_RATE_PARENT, APBC_PWM0, 0x3, 0x3, 0x0, 0, &pwm0_lock},
+ {PXA168_CLK_PWM1, "pwm1_clk", "pwm1_mux", CLK_SET_RATE_PARENT, APBC_PWM1, 0x3, 0x3, 0x0, 0, &pwm1_lock},
+ {PXA168_CLK_PWM2, "pwm2_clk", "pwm2_mux", CLK_SET_RATE_PARENT, APBC_PWM2, 0x3, 0x3, 0x0, 0, &pwm2_lock},
+ {PXA168_CLK_PWM3, "pwm3_clk", "pwm3_mux", CLK_SET_RATE_PARENT, APBC_PWM3, 0x3, 0x3, 0x0, 0, &pwm3_lock},
{PXA168_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x3, 0x3, 0x0, 0, &uart0_lock},
{PXA168_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock},
{PXA168_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBC_UART2, 0x3, 0x3, 0x0, 0, &uart2_lock},
@@ -170,22 +211,30 @@ static void pxa168_apb_periph_clk_init(struct pxa168_clk_unit *pxa_unit)
}
+static DEFINE_SPINLOCK(dfc_lock);
+static const char * const dfc_parent_names[] = {"pll1_4", "pll1_8"};
+
static DEFINE_SPINLOCK(sdh0_lock);
static DEFINE_SPINLOCK(sdh1_lock);
-static const char *sdh_parent_names[] = {"pll1_12", "pll1_13"};
+static DEFINE_SPINLOCK(sdh2_lock);
+static DEFINE_SPINLOCK(sdh3_lock);
+static const char * const sdh_parent_names[] = {"pll1_13", "pll1_12", "pll1_8"};
static DEFINE_SPINLOCK(usb_lock);
static DEFINE_SPINLOCK(disp0_lock);
-static const char *disp_parent_names[] = {"pll1_2", "pll1_12"};
+static const char * const disp_parent_names[] = {"pll1", "pll1_2"};
static DEFINE_SPINLOCK(ccic0_lock);
-static const char *ccic_parent_names[] = {"pll1_2", "pll1_12"};
-static const char *ccic_phy_parent_names[] = {"pll1_6", "pll1_12"};
+static const char * const ccic_parent_names[] = {"pll1_4", "pll1_8"};
+static const char * const ccic_phy_parent_names[] = {"pll1_6", "pll1_12"};
static struct mmp_param_mux_clk apmu_mux_clks[] = {
- {0, "sdh0_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH0, 6, 1, 0, &sdh0_lock},
- {0, "sdh1_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH1, 6, 1, 0, &sdh1_lock},
+ {0, "dfc_mux", dfc_parent_names, ARRAY_SIZE(dfc_parent_names), CLK_SET_RATE_PARENT, APMU_DFC, 6, 1, 0, &dfc_lock},
+ {0, "sdh0_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH0, 6, 2, 0, &sdh0_lock},
+ {0, "sdh1_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH1, 6, 2, 0, &sdh1_lock},
+ {0, "sdh2_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH2, 6, 2, 0, &sdh2_lock},
+ {0, "sdh3_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH3, 6, 2, 0, &sdh3_lock},
{0, "disp0_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP0, 6, 1, 0, &disp0_lock},
{0, "ccic0_mux", ccic_parent_names, ARRAY_SIZE(ccic_parent_names), CLK_SET_RATE_PARENT, APMU_CCIC0, 6, 1, 0, &ccic0_lock},
{0, "ccic0_phy_mux", ccic_phy_parent_names, ARRAY_SIZE(ccic_phy_parent_names), CLK_SET_RATE_PARENT, APMU_CCIC0, 7, 1, 0, &ccic0_lock},
@@ -196,12 +245,16 @@ static struct mmp_param_div_clk apmu_div_clks[] = {
};
static struct mmp_param_gate_clk apmu_gate_clks[] = {
- {PXA168_CLK_DFC, "dfc_clk", "pll1_4", CLK_SET_RATE_PARENT, APMU_DFC, 0x19b, 0x19b, 0x0, 0, NULL},
+ {PXA168_CLK_DFC, "dfc_clk", "dfc_mux", CLK_SET_RATE_PARENT, APMU_DFC, 0x19b, 0x19b, 0x0, 0, &dfc_lock},
{PXA168_CLK_USB, "usb_clk", "usb_pll", 0, APMU_USB, 0x9, 0x9, 0x0, 0, &usb_lock},
{PXA168_CLK_SPH, "sph_clk", "usb_pll", 0, APMU_USB, 0x12, 0x12, 0x0, 0, &usb_lock},
- /* The gate clocks has mux parent. */
- {PXA168_CLK_SDH0, "sdh0_clk", "sdh0_mux", CLK_SET_RATE_PARENT, APMU_SDH0, 0x1b, 0x1b, 0x0, 0, &sdh0_lock},
- {PXA168_CLK_SDH1, "sdh1_clk", "sdh1_mux", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh1_lock},
+ {PXA168_CLK_SDH0, "sdh0_clk", "sdh0_mux", CLK_SET_RATE_PARENT, APMU_SDH0, 0x12, 0x12, 0x0, 0, &sdh0_lock},
+ {PXA168_CLK_SDH1, "sdh1_clk", "sdh1_mux", CLK_SET_RATE_PARENT, APMU_SDH1, 0x12, 0x12, 0x0, 0, &sdh1_lock},
+ {PXA168_CLK_SDH2, "sdh2_clk", "sdh2_mux", CLK_SET_RATE_PARENT, APMU_SDH2, 0x12, 0x12, 0x0, 0, &sdh2_lock},
+ {PXA168_CLK_SDH3, "sdh3_clk", "sdh3_mux", CLK_SET_RATE_PARENT, APMU_SDH3, 0x12, 0x12, 0x0, 0, &sdh3_lock},
+ /* SDH0/1 and 2/3 AXI clocks are also gated by common bits in SDH0 and SDH2 registers */
+ {PXA168_CLK_SDH01_AXI, "sdh01_axi_clk", NULL, CLK_SET_RATE_PARENT, APMU_SDH0, 0x9, 0x9, 0x0, 0, &sdh0_lock},
+ {PXA168_CLK_SDH23_AXI, "sdh23_axi_clk", NULL, CLK_SET_RATE_PARENT, APMU_SDH2, 0x9, 0x9, 0x0, 0, &sdh2_lock},
{PXA168_CLK_DISP0, "disp0_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock},
{PXA168_CLK_CCIC0, "ccic0_clk", "ccic0_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1b, 0x1b, 0x0, 0, &ccic0_lock},
{PXA168_CLK_CCIC0_PHY, "ccic0_phy_clk", "ccic0_phy_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x24, 0x24, 0x0, 0, &ccic0_lock},
@@ -255,22 +308,25 @@ static void __init pxa168_clk_init(struct device_node *np)
pxa_unit->mpmu_base = of_iomap(np, 0);
if (!pxa_unit->mpmu_base) {
pr_err("failed to map mpmu registers\n");
+ kfree(pxa_unit);
return;
}
pxa_unit->apmu_base = of_iomap(np, 1);
if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
+ kfree(pxa_unit);
return;
}
pxa_unit->apbc_base = of_iomap(np, 2);
if (!pxa_unit->apbc_base) {
pr_err("failed to map apbc registers\n");
+ kfree(pxa_unit);
return;
}
- mmp_clk_init(np, &pxa_unit->unit, PXA168_NR_CLKS);
+ mmp_clk_init(np, &pxa_unit->unit, NR_CLKS);
pxa168_pll_init(pxa_unit);
diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c
index 2508a0d795f8..ebb6e278eda3 100644
--- a/drivers/clk/mmp/clk-of-pxa1928.c
+++ b/drivers/clk/mmp/clk-of-pxa1928.c
@@ -22,6 +22,9 @@
#define MPMU_UART_PLL 0x14
+#define APBC_NR_CLKS 48
+#define APMU_NR_CLKS 96
+
struct pxa1928_clk_unit {
struct mmp_clk_unit unit;
void __iomem *mpmu_base;
@@ -58,9 +61,9 @@ static struct mmp_clk_factor_masks uart_factor_masks = {
.den_shift = 0,
};
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 832, .den = 234}, /*58.5MHZ */
- {.num = 1, .den = 1}, /*26MHZ */
+static struct u32_fract uart_factor_tbl[] = {
+ { .numerator = 832, .denominator = 234 }, /* 58.5MHZ */
+ { .numerator = 1, .denominator = 1 }, /* 26MHZ */
};
static void pxa1928_pll_init(struct pxa1928_clk_unit *pxa_unit)
@@ -235,7 +238,7 @@ static void __init pxa1928_apmu_clk_init(struct device_node *np)
return;
}
- mmp_clk_init(np, &pxa_unit->unit, PXA1928_APMU_NR_CLKS);
+ mmp_clk_init(np, &pxa_unit->unit, APMU_NR_CLKS);
pxa1928_axi_periph_clk_init(pxa_unit);
}
@@ -256,7 +259,7 @@ static void __init pxa1928_apbc_clk_init(struct device_node *np)
return;
}
- mmp_clk_init(np, &pxa_unit->unit, PXA1928_APBC_NR_CLKS);
+ mmp_clk_init(np, &pxa_unit->unit, APBC_NR_CLKS);
pxa1928_apb_periph_clk_init(pxa_unit);
pxa1928_clk_reset_init(np, pxa_unit);
diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c
index 4d15bac987eb..fe65e7bdb411 100644
--- a/drivers/clk/mmp/clk-of-pxa910.c
+++ b/drivers/clk/mmp/clk-of-pxa910.c
@@ -44,6 +44,8 @@
#define APMU_DFC 0x60
#define MPMU_UART_PLL 0x14
+#define NR_CLKS 200
+
struct pxa910_clk_unit {
struct mmp_clk_unit unit;
void __iomem *mpmu_base;
@@ -84,8 +86,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = {
.den_shift = 0,
};
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 8125, .den = 1536}, /*14.745MHZ */
+static struct u32_fract uart_factor_tbl[] = {
+ { .numerator = 8125, .denominator = 1536 }, /* 14.745MHZ */
};
static void pxa910_pll_init(struct pxa910_clk_unit *pxa_unit)
@@ -296,7 +298,7 @@ static void __init pxa910_clk_init(struct device_node *np)
goto unmap_apbc_region;
}
- mmp_clk_init(np, &pxa_unit->unit, PXA910_NR_CLKS);
+ mmp_clk_init(np, &pxa_unit->unit, NR_CLKS);
pxa910_pll_init(pxa_unit);
diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c
deleted file mode 100644
index 8a9b8fb3a465..000000000000
--- a/drivers/clk/mmp/clk-pxa168.c
+++ /dev/null
@@ -1,354 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * pxa168 clock framework source file
- *
- * Copyright (C) 2012 Marvell
- * Chao Xie <xiechao.mail@gmail.com>
- */
-
-#include <linux/clk.h>
-#include <linux/clk/mmp.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-
-#include "clk.h"
-
-#define APBC_RTC 0x28
-#define APBC_TWSI0 0x2c
-#define APBC_KPC 0x30
-#define APBC_UART0 0x0
-#define APBC_UART1 0x4
-#define APBC_GPIO 0x8
-#define APBC_PWM0 0xc
-#define APBC_PWM1 0x10
-#define APBC_PWM2 0x14
-#define APBC_PWM3 0x18
-#define APBC_SSP0 0x81c
-#define APBC_SSP1 0x820
-#define APBC_SSP2 0x84c
-#define APBC_SSP3 0x858
-#define APBC_SSP4 0x85c
-#define APBC_TWSI1 0x6c
-#define APBC_UART2 0x70
-#define APMU_SDH0 0x54
-#define APMU_SDH1 0x58
-#define APMU_USB 0x5c
-#define APMU_DISP0 0x4c
-#define APMU_CCIC0 0x50
-#define APMU_DFC 0x60
-#define MPMU_UART_PLL 0x14
-
-static DEFINE_SPINLOCK(clk_lock);
-
-static struct mmp_clk_factor_masks uart_factor_masks = {
- .factor = 2,
- .num_mask = 0x1fff,
- .den_mask = 0x1fff,
- .num_shift = 16,
- .den_shift = 0,
-};
-
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 8125, .den = 1536}, /*14.745MHZ */
-};
-
-static const char *uart_parent[] = {"pll1_3_16", "uart_pll"};
-static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
-static const char *sdh_parent[] = {"pll1_12", "pll1_13"};
-static const char *disp_parent[] = {"pll1_2", "pll1_12"};
-static const char *ccic_parent[] = {"pll1_2", "pll1_12"};
-static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"};
-
-void __init pxa168_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys,
- phys_addr_t apbc_phys)
-{
- struct clk *clk;
- struct clk *uart_pll;
- void __iomem *mpmu_base;
- void __iomem *apmu_base;
- void __iomem *apbc_base;
-
- mpmu_base = ioremap(mpmu_phys, SZ_4K);
- if (!mpmu_base) {
- pr_err("error to ioremap MPMU base\n");
- return;
- }
-
- apmu_base = ioremap(apmu_phys, SZ_4K);
- if (!apmu_base) {
- pr_err("error to ioremap APMU base\n");
- return;
- }
-
- apbc_base = ioremap(apbc_phys, SZ_4K);
- if (!apbc_base) {
- pr_err("error to ioremap APBC base\n");
- return;
- }
-
- clk = clk_register_fixed_rate(NULL, "clk32", NULL, 0, 3200);
- clk_register_clkdev(clk, "clk32", NULL);
-
- clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, 0, 26000000);
- clk_register_clkdev(clk, "vctcxo", NULL);
-
- clk = clk_register_fixed_rate(NULL, "pll1", NULL, 0, 624000000);
- clk_register_clkdev(clk, "pll1", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_2", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_4", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_8", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_16", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2",
- CLK_SET_RATE_PARENT, 1, 3);
- clk_register_clkdev(clk, "pll1_6", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_12", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_24", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_48", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_96", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1",
- CLK_SET_RATE_PARENT, 1, 13);
- clk_register_clkdev(clk, "pll1_13", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1",
- CLK_SET_RATE_PARENT, 2, 3);
- clk_register_clkdev(clk, "pll1_13_1_5", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1",
- CLK_SET_RATE_PARENT, 2, 3);
- clk_register_clkdev(clk, "pll1_2_1_5", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1",
- CLK_SET_RATE_PARENT, 3, 16);
- clk_register_clkdev(clk, "pll1_3_16", NULL);
-
- uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
- mpmu_base + MPMU_UART_PLL,
- &uart_factor_masks, uart_factor_tbl,
- ARRAY_SIZE(uart_factor_tbl), &clk_lock);
- clk_set_rate(uart_pll, 14745600);
- clk_register_clkdev(uart_pll, "uart_pll", NULL);
-
- clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5",
- apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
-
- clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5",
- apbc_base + APBC_TWSI1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
-
- clk = mmp_clk_register_apbc("gpio", "vctcxo",
- apbc_base + APBC_GPIO, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-gpio");
-
- clk = mmp_clk_register_apbc("kpc", "clk32",
- apbc_base + APBC_KPC, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa27x-keypad");
-
- clk = mmp_clk_register_apbc("rtc", "clk32",
- apbc_base + APBC_RTC, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "sa1100-rtc");
-
- clk = mmp_clk_register_apbc("pwm0", "pll1_48",
- apbc_base + APBC_PWM0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa168-pwm.0");
-
- clk = mmp_clk_register_apbc("pwm1", "pll1_48",
- apbc_base + APBC_PWM1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa168-pwm.1");
-
- clk = mmp_clk_register_apbc("pwm2", "pll1_48",
- apbc_base + APBC_PWM2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa168-pwm.2");
-
- clk = mmp_clk_register_apbc("pwm3", "pll1_48",
- apbc_base + APBC_PWM3, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa168-pwm.3");
-
- clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, uart_pll);
- clk_register_clkdev(clk, "uart_mux.0", NULL);
-
- clk = mmp_clk_register_apbc("uart0", "uart0_mux",
- apbc_base + APBC_UART0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
-
- clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, uart_pll);
- clk_register_clkdev(clk, "uart_mux.1", NULL);
-
- clk = mmp_clk_register_apbc("uart1", "uart1_mux",
- apbc_base + APBC_UART1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
-
- clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, uart_pll);
- clk_register_clkdev(clk, "uart_mux.2", NULL);
-
- clk = mmp_clk_register_apbc("uart2", "uart2_mux",
- apbc_base + APBC_UART2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
-
- clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "uart_mux.0", NULL);
-
- clk = mmp_clk_register_apbc("ssp0", "ssp0_mux", apbc_base + APBC_SSP0,
- 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.0");
-
- clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.1", NULL);
-
- clk = mmp_clk_register_apbc("ssp1", "ssp1_mux", apbc_base + APBC_SSP1,
- 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.1");
-
- clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.2", NULL);
-
- clk = mmp_clk_register_apbc("ssp2", "ssp1_mux", apbc_base + APBC_SSP2,
- 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.2");
-
- clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.3", NULL);
-
- clk = mmp_clk_register_apbc("ssp3", "ssp1_mux", apbc_base + APBC_SSP3,
- 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.3");
-
- clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.4", NULL);
-
- clk = mmp_clk_register_apbc("ssp4", "ssp1_mux", apbc_base + APBC_SSP4,
- 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.4");
-
- clk = mmp_clk_register_apmu("dfc", "pll1_4", apmu_base + APMU_DFC,
- 0x19b, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");
-
- clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "sdh0_mux", NULL);
-
- clk = mmp_clk_register_apmu("sdh0", "sdh_mux", apmu_base + APMU_SDH0,
- 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxa.0");
-
- clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "sdh1_mux", NULL);
-
- clk = mmp_clk_register_apmu("sdh1", "sdh1_mux", apmu_base + APMU_SDH1,
- 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxa.1");
-
- clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB,
- 0x9, &clk_lock);
- clk_register_clkdev(clk, "usb_clk", NULL);
-
- clk = mmp_clk_register_apmu("sph", "usb_pll", apmu_base + APMU_USB,
- 0x12, &clk_lock);
- clk_register_clkdev(clk, "sph_clk", NULL);
-
- clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
- ARRAY_SIZE(disp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "disp_mux.0", NULL);
-
- clk = mmp_clk_register_apmu("disp0", "disp0_mux",
- apmu_base + APMU_DISP0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, "fnclk", "mmp-disp.0");
-
- clk = mmp_clk_register_apmu("disp0_hclk", "disp0_mux",
- apmu_base + APMU_DISP0, 0x24, &clk_lock);
- clk_register_clkdev(clk, "hclk", "mmp-disp.0");
-
- clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "ccic_mux.0", NULL);
-
- clk = mmp_clk_register_apmu("ccic0", "ccic0_mux",
- apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
-
- clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
- ARRAY_SIZE(ccic_phy_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);
-
- clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
- apmu_base + APMU_CCIC0, 0x24, &clk_lock);
- clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
-
- clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
- 10, 5, 0, &clk_lock);
- clk_register_clkdev(clk, "sphyclk_div", NULL);
-
- clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
- apmu_base + APMU_CCIC0, 0x300, &clk_lock);
- clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
-}
diff --git a/drivers/clk/mmp/clk-pxa1908-apbc.c b/drivers/clk/mmp/clk-pxa1908-apbc.c
new file mode 100644
index 000000000000..3fd7b5e644f3
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa1908-apbc.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/marvell,pxa1908.h>
+
+#include "clk.h"
+
+#define APBC_UART0 0x0
+#define APBC_UART1 0x4
+#define APBC_GPIO 0x8
+#define APBC_PWM0 0xc
+#define APBC_PWM1 0x10
+#define APBC_PWM2 0x14
+#define APBC_PWM3 0x18
+#define APBC_SSP0 0x1c
+#define APBC_SSP1 0x20
+#define APBC_IPC_RST 0x24
+#define APBC_RTC 0x28
+#define APBC_TWSI0 0x2c
+#define APBC_KPC 0x30
+#define APBC_SWJTAG 0x40
+#define APBC_SSP2 0x4c
+#define APBC_TWSI1 0x60
+#define APBC_THERMAL 0x6c
+#define APBC_TWSI3 0x70
+
+#define APBC_NR_CLKS 19
+
+struct pxa1908_clk_unit {
+ struct mmp_clk_unit unit;
+ void __iomem *base;
+};
+
+static DEFINE_SPINLOCK(pwm0_lock);
+static DEFINE_SPINLOCK(pwm2_lock);
+
+static DEFINE_SPINLOCK(uart0_lock);
+static DEFINE_SPINLOCK(uart1_lock);
+
+static const char * const uart_parent_names[] = {"pll1_117", "uart_pll"};
+static const char * const ssp_parent_names[] = {"pll1_d16", "pll1_d48", "pll1_d24", "pll1_d12"};
+
+static struct mmp_param_gate_clk apbc_gate_clks[] = {
+ {PXA1908_CLK_TWSI0, "twsi0_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_TWSI1, "twsi1_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_TWSI3, "twsi3_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x7, 3, 0, MMP_CLK_GATE_NEED_DELAY, NULL},
+ {PXA1908_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x87, 0x83, 0, MMP_CLK_GATE_NEED_DELAY, NULL},
+ {PXA1908_CLK_PWM0, "pwm0_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM0, 0x2, 2, 0, 0, &pwm0_lock},
+ {PXA1908_CLK_PWM1, "pwm1_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM1, 0x6, 2, 0, 0, NULL},
+ {PXA1908_CLK_PWM2, "pwm2_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM2, 0x2, 2, 0, 0, NULL},
+ {PXA1908_CLK_PWM3, "pwm3_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM3, 0x6, 2, 0, 0, NULL},
+ {PXA1908_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x7, 3, 0, 0, &uart0_lock},
+ {PXA1908_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x7, 3, 0, 0, &uart1_lock},
+ {PXA1908_CLK_THERMAL, "thermal_clk", NULL, 0, APBC_THERMAL, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_IPC_RST, "ipc_clk", NULL, 0, APBC_IPC_RST, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_SSP0, "ssp0_clk", "ssp0_mux", 0, APBC_SSP0, 0x7, 3, 0, 0, NULL},
+ {PXA1908_CLK_SSP2, "ssp2_clk", "ssp2_mux", 0, APBC_SSP2, 0x7, 3, 0, 0, NULL},
+};
+
+static struct mmp_param_mux_clk apbc_mux_clks[] = {
+ {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock},
+ {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock},
+ {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), 0, APBC_SSP0, 4, 3, 0, NULL},
+ {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), 0, APBC_SSP2, 4, 3, 0, NULL},
+};
+
+static void pxa1908_apb_periph_clk_init(struct pxa1908_clk_unit *pxa_unit)
+{
+ struct mmp_clk_unit *unit = &pxa_unit->unit;
+ struct clk *clk;
+
+ mmp_clk_register_gate(NULL, "pwm01_apb_share", "pll1_d48",
+ CLK_SET_RATE_PARENT,
+ pxa_unit->base + APBC_PWM0,
+ 0x5, 1, 0, 0, &pwm0_lock);
+ mmp_clk_register_gate(NULL, "pwm23_apb_share", "pll1_d48",
+ CLK_SET_RATE_PARENT,
+ pxa_unit->base + APBC_PWM2,
+ 0x5, 1, 0, 0, &pwm2_lock);
+ clk = mmp_clk_register_apbc("swjtag", NULL,
+ pxa_unit->base + APBC_SWJTAG, 10, 0, NULL);
+ mmp_clk_add(unit, PXA1908_CLK_SWJTAG, clk);
+ mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->base,
+ ARRAY_SIZE(apbc_mux_clks));
+ mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->base,
+ ARRAY_SIZE(apbc_gate_clks));
+}
+
+static int pxa1908_apbc_probe(struct platform_device *pdev)
+{
+ struct pxa1908_clk_unit *pxa_unit;
+
+ pxa_unit = devm_kzalloc(&pdev->dev, sizeof(*pxa_unit), GFP_KERNEL);
+ if (!pxa_unit)
+ return -ENOMEM;
+
+ pxa_unit->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pxa_unit->base))
+ return PTR_ERR(pxa_unit->base);
+
+ mmp_clk_init(pdev->dev.of_node, &pxa_unit->unit, APBC_NR_CLKS);
+
+ pxa1908_apb_periph_clk_init(pxa_unit);
+
+ return 0;
+}
+
+static const struct of_device_id pxa1908_apbc_match_table[] = {
+ { .compatible = "marvell,pxa1908-apbc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pxa1908_apbc_match_table);
+
+static struct platform_driver pxa1908_apbc_driver = {
+ .probe = pxa1908_apbc_probe,
+ .driver = {
+ .name = "pxa1908-apbc",
+ .of_match_table = pxa1908_apbc_match_table
+ }
+};
+module_platform_driver(pxa1908_apbc_driver);
+
+MODULE_AUTHOR("Duje Mihanović <duje.mihanovic@skole.hr>");
+MODULE_DESCRIPTION("Marvell PXA1908 APBC Clock Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mmp/clk-pxa1908-apbcp.c b/drivers/clk/mmp/clk-pxa1908-apbcp.c
new file mode 100644
index 000000000000..f638d7e89b47
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa1908-apbcp.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/marvell,pxa1908.h>
+
+#include "clk.h"
+
+#define APBCP_UART2 0x1c
+#define APBCP_TWSI2 0x28
+#define APBCP_AICER 0x38
+
+#define APBCP_NR_CLKS 4
+
+struct pxa1908_clk_unit {
+ struct mmp_clk_unit unit;
+ void __iomem *base;
+};
+
+static DEFINE_SPINLOCK(uart2_lock);
+
+static const char * const uart_parent_names[] = {"pll1_117", "uart_pll"};
+
+static struct mmp_param_gate_clk apbcp_gate_clks[] = {
+ {PXA1908_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBCP_UART2, 0x7, 0x3, 0x0, 0, &uart2_lock},
+ {PXA1908_CLK_TWSI2, "twsi2_clk", "pll1_32", CLK_SET_RATE_PARENT, APBCP_TWSI2, 0x7, 0x3, 0x0, 0, NULL},
+ {PXA1908_CLK_AICER, "ripc_clk", NULL, 0, APBCP_AICER, 0x7, 0x2, 0x0, 0, NULL},
+};
+
+static struct mmp_param_mux_clk apbcp_mux_clks[] = {
+ {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBCP_UART2, 4, 3, 0, &uart2_lock},
+};
+
+static void pxa1908_apb_p_periph_clk_init(struct pxa1908_clk_unit *pxa_unit)
+{
+ struct mmp_clk_unit *unit = &pxa_unit->unit;
+
+ mmp_register_mux_clks(unit, apbcp_mux_clks, pxa_unit->base,
+ ARRAY_SIZE(apbcp_mux_clks));
+ mmp_register_gate_clks(unit, apbcp_gate_clks, pxa_unit->base,
+ ARRAY_SIZE(apbcp_gate_clks));
+}
+
+static int pxa1908_apbcp_probe(struct platform_device *pdev)
+{
+ struct pxa1908_clk_unit *pxa_unit;
+
+ pxa_unit = devm_kzalloc(&pdev->dev, sizeof(*pxa_unit), GFP_KERNEL);
+ if (!pxa_unit)
+ return -ENOMEM;
+
+ pxa_unit->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pxa_unit->base))
+ return PTR_ERR(pxa_unit->base);
+
+ mmp_clk_init(pdev->dev.of_node, &pxa_unit->unit, APBCP_NR_CLKS);
+
+ pxa1908_apb_p_periph_clk_init(pxa_unit);
+
+ return 0;
+}
+
+static const struct of_device_id pxa1908_apbcp_match_table[] = {
+ { .compatible = "marvell,pxa1908-apbcp" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pxa1908_apbcp_match_table);
+
+static struct platform_driver pxa1908_apbcp_driver = {
+ .probe = pxa1908_apbcp_probe,
+ .driver = {
+ .name = "pxa1908-apbcp",
+ .of_match_table = pxa1908_apbcp_match_table
+ }
+};
+module_platform_driver(pxa1908_apbcp_driver);
+
+MODULE_AUTHOR("Duje Mihanović <duje.mihanovic@skole.hr>");
+MODULE_DESCRIPTION("Marvell PXA1908 APBCP Clock Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mmp/clk-pxa1908-apmu.c b/drivers/clk/mmp/clk-pxa1908-apmu.c
new file mode 100644
index 000000000000..7594a495a009
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa1908-apmu.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/marvell,pxa1908.h>
+
+#include "clk.h"
+
+#define APMU_CLK_GATE_CTRL 0x40
+#define APMU_CCIC1 0x24
+#define APMU_ISP 0x38
+#define APMU_DSI1 0x44
+#define APMU_DISP1 0x4c
+#define APMU_CCIC0 0x50
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_USB 0x5c
+#define APMU_NF 0x60
+#define APMU_VPU 0xa4
+#define APMU_GC 0xcc
+#define APMU_SDH2 0xe0
+#define APMU_GC2D 0xf4
+#define APMU_TRACE 0x108
+#define APMU_DVC_DFC_DEBUG 0x140
+
+#define APMU_NR_CLKS 17
+
+struct pxa1908_clk_unit {
+ struct mmp_clk_unit unit;
+ void __iomem *base;
+};
+
+static DEFINE_SPINLOCK(pll1_lock);
+static struct mmp_param_general_gate_clk pll1_gate_clks[] = {
+ {PXA1908_CLK_PLL1_D2_GATE, "pll1_d2_gate", "pll1_d2", 0, APMU_CLK_GATE_CTRL, 29, 0, &pll1_lock},
+ {PXA1908_CLK_PLL1_416_GATE, "pll1_416_gate", "pll1_416", 0, APMU_CLK_GATE_CTRL, 27, 0, &pll1_lock},
+ {PXA1908_CLK_PLL1_624_GATE, "pll1_624_gate", "pll1_624", 0, APMU_CLK_GATE_CTRL, 26, 0, &pll1_lock},
+ {PXA1908_CLK_PLL1_832_GATE, "pll1_832_gate", "pll1_832", 0, APMU_CLK_GATE_CTRL, 30, 0, &pll1_lock},
+ {PXA1908_CLK_PLL1_1248_GATE, "pll1_1248_gate", "pll1_1248", 0, APMU_CLK_GATE_CTRL, 28, 0, &pll1_lock},
+};
+
+static DEFINE_SPINLOCK(sdh0_lock);
+static DEFINE_SPINLOCK(sdh1_lock);
+static DEFINE_SPINLOCK(sdh2_lock);
+
+static const char * const sdh_parent_names[] = {"pll1_416", "pll1_624"};
+
+static struct mmp_clk_mix_config sdh_mix_config = {
+ .reg_info = DEFINE_MIX_REG_INFO(3, 8, 2, 6, 11),
+};
+
+static struct mmp_param_gate_clk apmu_gate_clks[] = {
+ {PXA1908_CLK_USB, "usb_clk", NULL, 0, APMU_USB, 0x9, 0x9, 0x1, 0, NULL},
+ {PXA1908_CLK_SDH0, "sdh0_clk", "sdh0_mix_clk", CLK_SET_RATE_PARENT | CLK_SET_RATE_UNGATE, APMU_SDH0, 0x12, 0x12, 0x0, 0, &sdh0_lock},
+ {PXA1908_CLK_SDH1, "sdh1_clk", "sdh1_mix_clk", CLK_SET_RATE_PARENT | CLK_SET_RATE_UNGATE, APMU_SDH1, 0x12, 0x12, 0x0, 0, &sdh1_lock},
+ {PXA1908_CLK_SDH2, "sdh2_clk", "sdh2_mix_clk", CLK_SET_RATE_PARENT | CLK_SET_RATE_UNGATE, APMU_SDH2, 0x12, 0x12, 0x0, 0, &sdh2_lock}
+};
+
+static void pxa1908_axi_periph_clk_init(struct pxa1908_clk_unit *pxa_unit)
+{
+ struct mmp_clk_unit *unit = &pxa_unit->unit;
+
+ mmp_register_general_gate_clks(unit, pll1_gate_clks,
+ pxa_unit->base, ARRAY_SIZE(pll1_gate_clks));
+
+ sdh_mix_config.reg_info.reg_clk_ctrl = pxa_unit->base + APMU_SDH0;
+ mmp_clk_register_mix(NULL, "sdh0_mix_clk", sdh_parent_names,
+ ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT,
+ &sdh_mix_config, &sdh0_lock);
+ sdh_mix_config.reg_info.reg_clk_ctrl = pxa_unit->base + APMU_SDH1;
+ mmp_clk_register_mix(NULL, "sdh1_mix_clk", sdh_parent_names,
+ ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT,
+ &sdh_mix_config, &sdh1_lock);
+ sdh_mix_config.reg_info.reg_clk_ctrl = pxa_unit->base + APMU_SDH2;
+ mmp_clk_register_mix(NULL, "sdh2_mix_clk", sdh_parent_names,
+ ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT,
+ &sdh_mix_config, &sdh2_lock);
+
+ mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->base,
+ ARRAY_SIZE(apmu_gate_clks));
+}
+
+static int pxa1908_apmu_probe(struct platform_device *pdev)
+{
+ struct pxa1908_clk_unit *pxa_unit;
+ struct auxiliary_device *adev;
+
+ pxa_unit = devm_kzalloc(&pdev->dev, sizeof(*pxa_unit), GFP_KERNEL);
+ if (!pxa_unit)
+ return -ENOMEM;
+
+ pxa_unit->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pxa_unit->base))
+ return PTR_ERR(pxa_unit->base);
+
+ adev = devm_auxiliary_device_create(&pdev->dev, "power", NULL);
+ if (IS_ERR(adev))
+ return dev_err_probe(&pdev->dev, PTR_ERR(adev),
+ "Failed to register power controller\n");
+
+ mmp_clk_init(pdev->dev.of_node, &pxa_unit->unit, APMU_NR_CLKS);
+
+ pxa1908_axi_periph_clk_init(pxa_unit);
+
+ return 0;
+}
+
+static const struct of_device_id pxa1908_apmu_match_table[] = {
+ { .compatible = "marvell,pxa1908-apmu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pxa1908_apmu_match_table);
+
+static struct platform_driver pxa1908_apmu_driver = {
+ .probe = pxa1908_apmu_probe,
+ .driver = {
+ .name = "pxa1908-apmu",
+ .of_match_table = pxa1908_apmu_match_table
+ }
+};
+module_platform_driver(pxa1908_apmu_driver);
+
+MODULE_AUTHOR("Duje Mihanović <duje.mihanovic@skole.hr>");
+MODULE_DESCRIPTION("Marvell PXA1908 APMU Clock Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mmp/clk-pxa1908-mpmu.c b/drivers/clk/mmp/clk-pxa1908-mpmu.c
new file mode 100644
index 000000000000..90b4b2488574
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa1908-mpmu.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/units.h>
+
+#include <dt-bindings/clock/marvell,pxa1908.h>
+
+#include "clk.h"
+
+#define MPMU_UART_PLL 0x14
+
+#define MPMU_NR_CLKS 39
+
+struct pxa1908_clk_unit {
+ struct mmp_clk_unit unit;
+ void __iomem *base;
+};
+
+static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = {
+ {PXA1908_CLK_CLK32, "clk32", NULL, 0, 32768},
+ {PXA1908_CLK_VCTCXO, "vctcxo", NULL, 0, 26 * HZ_PER_MHZ},
+ {PXA1908_CLK_PLL1_624, "pll1_624", NULL, 0, 624 * HZ_PER_MHZ},
+ {PXA1908_CLK_PLL1_416, "pll1_416", NULL, 0, 416 * HZ_PER_MHZ},
+ {PXA1908_CLK_PLL1_499, "pll1_499", NULL, 0, 499 * HZ_PER_MHZ},
+ {PXA1908_CLK_PLL1_832, "pll1_832", NULL, 0, 832 * HZ_PER_MHZ},
+ {PXA1908_CLK_PLL1_1248, "pll1_1248", NULL, 0, 1248 * HZ_PER_MHZ},
+};
+
+static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
+ {PXA1908_CLK_PLL1_D2, "pll1_d2", "pll1_624", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D4, "pll1_d4", "pll1_d2", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D6, "pll1_d6", "pll1_d2", 1, 3, 0},
+ {PXA1908_CLK_PLL1_D8, "pll1_d8", "pll1_d4", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D12, "pll1_d12", "pll1_d6", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D13, "pll1_d13", "pll1_624", 1, 13, 0},
+ {PXA1908_CLK_PLL1_D16, "pll1_d16", "pll1_d8", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D24, "pll1_d24", "pll1_d12", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D48, "pll1_d48", "pll1_d24", 1, 2, 0},
+ {PXA1908_CLK_PLL1_D96, "pll1_d96", "pll1_d48", 1, 2, 0},
+ {PXA1908_CLK_PLL1_32, "pll1_32", "pll1_d13", 2, 3, 0},
+ {PXA1908_CLK_PLL1_208, "pll1_208", "pll1_d2", 2, 3, 0},
+ {PXA1908_CLK_PLL1_117, "pll1_117", "pll1_624", 3, 16, 0},
+};
+
+static struct u32_fract uart_factor_tbl[] = {
+ {.numerator = 8125, .denominator = 1536}, /* 14.745MHz */
+};
+
+static struct mmp_clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = GENMASK(12, 0),
+ .den_mask = GENMASK(12, 0),
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static void pxa1908_pll_init(struct pxa1908_clk_unit *pxa_unit)
+{
+ struct mmp_clk_unit *unit = &pxa_unit->unit;
+
+ mmp_register_fixed_rate_clks(unit, fixed_rate_clks,
+ ARRAY_SIZE(fixed_rate_clks));
+
+ mmp_register_fixed_factor_clks(unit, fixed_factor_clks,
+ ARRAY_SIZE(fixed_factor_clks));
+
+ mmp_clk_register_factor("uart_pll", "pll1_d4",
+ CLK_SET_RATE_PARENT,
+ pxa_unit->base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl), NULL);
+}
+
+static int pxa1908_mpmu_probe(struct platform_device *pdev)
+{
+ struct pxa1908_clk_unit *pxa_unit;
+
+ pxa_unit = devm_kzalloc(&pdev->dev, sizeof(*pxa_unit), GFP_KERNEL);
+ if (!pxa_unit)
+ return -ENOMEM;
+
+ pxa_unit->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pxa_unit->base))
+ return PTR_ERR(pxa_unit->base);
+
+ mmp_clk_init(pdev->dev.of_node, &pxa_unit->unit, MPMU_NR_CLKS);
+
+ pxa1908_pll_init(pxa_unit);
+
+ return 0;
+}
+
+static const struct of_device_id pxa1908_mpmu_match_table[] = {
+ { .compatible = "marvell,pxa1908-mpmu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pxa1908_mpmu_match_table);
+
+static struct platform_driver pxa1908_mpmu_driver = {
+ .probe = pxa1908_mpmu_probe,
+ .driver = {
+ .name = "pxa1908-mpmu",
+ .of_match_table = pxa1908_mpmu_match_table
+ }
+};
+module_platform_driver(pxa1908_mpmu_driver);
+
+MODULE_AUTHOR("Duje Mihanović <duje.mihanovic@skole.hr>");
+MODULE_DESCRIPTION("Marvell PXA1908 MPMU Clock Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c
deleted file mode 100644
index 9fcd76316d7e..000000000000
--- a/drivers/clk/mmp/clk-pxa910.c
+++ /dev/null
@@ -1,325 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * pxa910 clock framework source file
- *
- * Copyright (C) 2012 Marvell
- * Chao Xie <xiechao.mail@gmail.com>
- */
-
-#include <linux/clk.h>
-#include <linux/clk/mmp.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-
-#include "clk.h"
-
-#define APBC_RTC 0x28
-#define APBC_TWSI0 0x2c
-#define APBC_KPC 0x18
-#define APBC_UART0 0x0
-#define APBC_UART1 0x4
-#define APBC_GPIO 0x8
-#define APBC_PWM0 0xc
-#define APBC_PWM1 0x10
-#define APBC_PWM2 0x14
-#define APBC_PWM3 0x18
-#define APBC_SSP0 0x1c
-#define APBC_SSP1 0x20
-#define APBC_SSP2 0x4c
-#define APBCP_TWSI1 0x28
-#define APBCP_UART2 0x1c
-#define APMU_SDH0 0x54
-#define APMU_SDH1 0x58
-#define APMU_USB 0x5c
-#define APMU_DISP0 0x4c
-#define APMU_CCIC0 0x50
-#define APMU_DFC 0x60
-#define MPMU_UART_PLL 0x14
-
-static DEFINE_SPINLOCK(clk_lock);
-
-static struct mmp_clk_factor_masks uart_factor_masks = {
- .factor = 2,
- .num_mask = 0x1fff,
- .den_mask = 0x1fff,
- .num_shift = 16,
- .den_shift = 0,
-};
-
-static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
- {.num = 8125, .den = 1536}, /*14.745MHZ */
-};
-
-static const char *uart_parent[] = {"pll1_3_16", "uart_pll"};
-static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
-static const char *sdh_parent[] = {"pll1_12", "pll1_13"};
-static const char *disp_parent[] = {"pll1_2", "pll1_12"};
-static const char *ccic_parent[] = {"pll1_2", "pll1_12"};
-static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"};
-
-void __init pxa910_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys,
- phys_addr_t apbc_phys, phys_addr_t apbcp_phys)
-{
- struct clk *clk;
- struct clk *uart_pll;
- void __iomem *mpmu_base;
- void __iomem *apmu_base;
- void __iomem *apbcp_base;
- void __iomem *apbc_base;
-
- mpmu_base = ioremap(mpmu_phys, SZ_4K);
- if (!mpmu_base) {
- pr_err("error to ioremap MPMU base\n");
- return;
- }
-
- apmu_base = ioremap(apmu_phys, SZ_4K);
- if (!apmu_base) {
- pr_err("error to ioremap APMU base\n");
- return;
- }
-
- apbcp_base = ioremap(apbcp_phys, SZ_4K);
- if (!apbcp_base) {
- pr_err("error to ioremap APBC extension base\n");
- return;
- }
-
- apbc_base = ioremap(apbc_phys, SZ_4K);
- if (!apbc_base) {
- pr_err("error to ioremap APBC base\n");
- return;
- }
-
- clk = clk_register_fixed_rate(NULL, "clk32", NULL, 0, 3200);
- clk_register_clkdev(clk, "clk32", NULL);
-
- clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, 0, 26000000);
- clk_register_clkdev(clk, "vctcxo", NULL);
-
- clk = clk_register_fixed_rate(NULL, "pll1", NULL, 0, 624000000);
- clk_register_clkdev(clk, "pll1", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_2", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_4", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_8", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_16", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2",
- CLK_SET_RATE_PARENT, 1, 3);
- clk_register_clkdev(clk, "pll1_6", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_12", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_24", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_48", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48",
- CLK_SET_RATE_PARENT, 1, 2);
- clk_register_clkdev(clk, "pll1_96", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1",
- CLK_SET_RATE_PARENT, 1, 13);
- clk_register_clkdev(clk, "pll1_13", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1",
- CLK_SET_RATE_PARENT, 2, 3);
- clk_register_clkdev(clk, "pll1_13_1_5", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1",
- CLK_SET_RATE_PARENT, 2, 3);
- clk_register_clkdev(clk, "pll1_2_1_5", NULL);
-
- clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1",
- CLK_SET_RATE_PARENT, 3, 16);
- clk_register_clkdev(clk, "pll1_3_16", NULL);
-
- uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
- mpmu_base + MPMU_UART_PLL,
- &uart_factor_masks, uart_factor_tbl,
- ARRAY_SIZE(uart_factor_tbl), &clk_lock);
- clk_set_rate(uart_pll, 14745600);
- clk_register_clkdev(uart_pll, "uart_pll", NULL);
-
- clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5",
- apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
-
- clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5",
- apbcp_base + APBCP_TWSI1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
-
- clk = mmp_clk_register_apbc("gpio", "vctcxo",
- apbc_base + APBC_GPIO, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-gpio");
-
- clk = mmp_clk_register_apbc("kpc", "clk32",
- apbc_base + APBC_KPC, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa27x-keypad");
-
- clk = mmp_clk_register_apbc("rtc", "clk32",
- apbc_base + APBC_RTC, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "sa1100-rtc");
-
- clk = mmp_clk_register_apbc("pwm0", "pll1_48",
- apbc_base + APBC_PWM0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa910-pwm.0");
-
- clk = mmp_clk_register_apbc("pwm1", "pll1_48",
- apbc_base + APBC_PWM1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa910-pwm.1");
-
- clk = mmp_clk_register_apbc("pwm2", "pll1_48",
- apbc_base + APBC_PWM2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa910-pwm.2");
-
- clk = mmp_clk_register_apbc("pwm3", "pll1_48",
- apbc_base + APBC_PWM3, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa910-pwm.3");
-
- clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, uart_pll);
- clk_register_clkdev(clk, "uart_mux.0", NULL);
-
- clk = mmp_clk_register_apbc("uart0", "uart0_mux",
- apbc_base + APBC_UART0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
-
- clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, uart_pll);
- clk_register_clkdev(clk, "uart_mux.1", NULL);
-
- clk = mmp_clk_register_apbc("uart1", "uart1_mux",
- apbc_base + APBC_UART1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
-
- clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
- ARRAY_SIZE(uart_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock);
- clk_set_parent(clk, uart_pll);
- clk_register_clkdev(clk, "uart_mux.2", NULL);
-
- clk = mmp_clk_register_apbc("uart2", "uart2_mux",
- apbcp_base + APBCP_UART2, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
-
- clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "uart_mux.0", NULL);
-
- clk = mmp_clk_register_apbc("ssp0", "ssp0_mux",
- apbc_base + APBC_SSP0, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.0");
-
- clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
- clk_register_clkdev(clk, "ssp_mux.1", NULL);
-
- clk = mmp_clk_register_apbc("ssp1", "ssp1_mux",
- apbc_base + APBC_SSP1, 10, 0, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-ssp.1");
-
- clk = mmp_clk_register_apmu("dfc", "pll1_4",
- apmu_base + APMU_DFC, 0x19b, &clk_lock);
- clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");
-
- clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "sdh0_mux", NULL);
-
- clk = mmp_clk_register_apmu("sdh0", "sdh_mux",
- apmu_base + APMU_SDH0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxa.0");
-
- clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "sdh1_mux", NULL);
-
- clk = mmp_clk_register_apmu("sdh1", "sdh1_mux",
- apmu_base + APMU_SDH1, 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "sdhci-pxa.1");
-
- clk = mmp_clk_register_apmu("usb", "usb_pll",
- apmu_base + APMU_USB, 0x9, &clk_lock);
- clk_register_clkdev(clk, "usb_clk", NULL);
-
- clk = mmp_clk_register_apmu("sph", "usb_pll",
- apmu_base + APMU_USB, 0x12, &clk_lock);
- clk_register_clkdev(clk, "sph_clk", NULL);
-
- clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
- ARRAY_SIZE(disp_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "disp_mux.0", NULL);
-
- clk = mmp_clk_register_apmu("disp0", "disp0_mux",
- apmu_base + APMU_DISP0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, NULL, "mmp-disp.0");
-
- clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "ccic_mux.0", NULL);
-
- clk = mmp_clk_register_apmu("ccic0", "ccic0_mux",
- apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
- clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
-
- clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
- ARRAY_SIZE(ccic_phy_parent),
- CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
- apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock);
- clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);
-
- clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
- apmu_base + APMU_CCIC0, 0x24, &clk_lock);
- clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
-
- clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux",
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
- 10, 5, 0, &clk_lock);
- clk_register_clkdev(clk, "sphyclk_div", NULL);
-
- clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
- apmu_base + APMU_CCIC0, 0x300, &clk_lock);
- clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
-}
diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h
index 55ac05379781..c83cec169ddc 100644
--- a/drivers/clk/mmp/clk.h
+++ b/drivers/clk/mmp/clk.h
@@ -3,6 +3,7 @@
#define __MACH_MMP_CLK_H
#include <linux/clk-provider.h>
+#include <linux/math.h>
#include <linux/pm_domain.h>
#include <linux/clkdev.h>
@@ -20,16 +21,11 @@ struct mmp_clk_factor_masks {
unsigned int enable_mask;
};
-struct mmp_clk_factor_tbl {
- unsigned int num;
- unsigned int den;
-};
-
struct mmp_clk_factor {
struct clk_hw hw;
void __iomem *base;
struct mmp_clk_factor_masks *masks;
- struct mmp_clk_factor_tbl *ftbl;
+ struct u32_fract *ftbl;
unsigned int ftbl_cnt;
spinlock_t *lock;
};
@@ -37,7 +33,7 @@ struct mmp_clk_factor {
extern struct clk *mmp_clk_register_factor(const char *name,
const char *parent_name, unsigned long flags,
void __iomem *base, struct mmp_clk_factor_masks *masks,
- struct mmp_clk_factor_tbl *ftbl, unsigned int ftbl_cnt,
+ struct u32_fract *ftbl, unsigned int ftbl_cnt,
spinlock_t *lock);
/* Clock type "mix" */
diff --git a/drivers/clk/mmp/pwr-island.c b/drivers/clk/mmp/pwr-island.c
index edaa2433a472..eaf5d2c5e593 100644
--- a/drivers/clk/mmp/pwr-island.c
+++ b/drivers/clk/mmp/pwr-island.c
@@ -106,10 +106,10 @@ struct generic_pm_domain *mmp_pm_domain_register(const char *name,
pm_domain->flags = flags;
pm_domain->lock = lock;
- pm_genpd_init(&pm_domain->genpd, NULL, true);
pm_domain->genpd.name = name;
pm_domain->genpd.power_on = mmp_pm_domain_power_on;
pm_domain->genpd.power_off = mmp_pm_domain_power_off;
+ pm_genpd_init(&pm_domain->genpd, NULL, true);
return &pm_domain->genpd;
}
diff --git a/drivers/clk/mstar/Kconfig b/drivers/clk/mstar/Kconfig
index de37e1bce2d2..b9bcb5e02814 100644
--- a/drivers/clk/mstar/Kconfig
+++ b/drivers/clk/mstar/Kconfig
@@ -1,4 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
+config MSTAR_MSC313_CPUPLL
+ bool "MStar CPUPLL driver"
+ depends on ARCH_MSTARV7 || COMPILE_TEST
+ default ARCH_MSTARV7
+ help
+ Support for the CPU PLL present on MStar/Sigmastar SoCs.
+
config MSTAR_MSC313_MPLL
bool "MStar MPLL driver"
depends on ARCH_MSTARV7 || COMPILE_TEST
diff --git a/drivers/clk/mstar/Makefile b/drivers/clk/mstar/Makefile
index f8dcd25ede1d..17d97eedcd36 100644
--- a/drivers/clk/mstar/Makefile
+++ b/drivers/clk/mstar/Makefile
@@ -3,4 +3,5 @@
# Makefile for mstar specific clk
#
+obj-$(CONFIG_MSTAR_MSC313_CPUPLL) += clk-msc313-cpupll.o
obj-$(CONFIG_MSTAR_MSC313_MPLL) += clk-msc313-mpll.o
diff --git a/drivers/clk/mstar/clk-msc313-cpupll.c b/drivers/clk/mstar/clk-msc313-cpupll.c
new file mode 100644
index 000000000000..3e643be02fe2
--- /dev/null
+++ b/drivers/clk/mstar/clk-msc313-cpupll.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Daniel Palmer <daniel@thingy.jp>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+/*
+ * This IP is not documented outside of the messy vendor driver.
+ * Below is what we think the registers look like based on looking at
+ * the vendor code and poking at the hardware:
+ *
+ * 0x140 -- LPF low. Seems to store one half of the clock transition
+ * 0x144 /
+ * 0x148 -- LPF high. Seems to store one half of the clock transition
+ * 0x14c /
+ * 0x150 -- vendor code says "toggle lpf enable"
+ * 0x154 -- mu?
+ * 0x15c -- lpf_update_count?
+ * 0x160 -- vendor code says "switch to LPF". Clock source config? Register bank?
+ * 0x164 -- vendor code says "from low to high" which seems to mean transition from LPF low to
+ * LPF high.
+ * 0x174 -- Seems to be the PLL lock status bit
+ * 0x180 -- Seems to be the current frequency, this might need to be populated by software?
+ * 0x184 / The vendor driver uses these to set the initial value of LPF low
+ *
+ * Frequency seems to be calculated like this:
+ * (parent clock (432mhz) / register_magic_value) * 16 * 524288
+ * Only the lower 24 bits of the resulting value will be used. In addition, the
+ * PLL doesn't seem to be able to lock on frequencies lower than 220 MHz, as
+ * divisor 0xfb586f (220 MHz) works but 0xfb7fff locks up.
+ *
+ * Vendor values:
+ * frequency - register value
+ *
+ * 400000000 - 0x0067AE14
+ * 600000000 - 0x00451EB8,
+ * 800000000 - 0x0033D70A,
+ * 1000000000 - 0x002978d4,
+ */
+
+#define REG_LPF_LOW_L 0x140
+#define REG_LPF_LOW_H 0x144
+#define REG_LPF_HIGH_BOTTOM 0x148
+#define REG_LPF_HIGH_TOP 0x14c
+#define REG_LPF_TOGGLE 0x150
+#define REG_LPF_MYSTERYTWO 0x154
+#define REG_LPF_UPDATE_COUNT 0x15c
+#define REG_LPF_MYSTERYONE 0x160
+#define REG_LPF_TRANSITIONCTRL 0x164
+#define REG_LPF_LOCK 0x174
+#define REG_CURRENT 0x180
+
+#define LPF_LOCK_TIMEOUT 100000000
+
+#define MULTIPLIER_1 16
+#define MULTIPLIER_2 524288
+#define MULTIPLIER (MULTIPLIER_1 * MULTIPLIER_2)
+
+struct msc313_cpupll {
+ void __iomem *base;
+ struct clk_hw clk_hw;
+};
+
+#define to_cpupll(_hw) container_of(_hw, struct msc313_cpupll, clk_hw)
+
+static u32 msc313_cpupll_reg_read32(struct msc313_cpupll *cpupll, unsigned int reg)
+{
+ u32 value;
+
+ value = ioread16(cpupll->base + reg + 4) << 16;
+ value |= ioread16(cpupll->base + reg);
+
+ return value;
+}
+
+static void msc313_cpupll_reg_write32(struct msc313_cpupll *cpupll, unsigned int reg, u32 value)
+{
+ u16 l = value & 0xffff, h = (value >> 16) & 0xffff;
+
+ iowrite16(l, cpupll->base + reg);
+ iowrite16(h, cpupll->base + reg + 4);
+}
+
+static void msc313_cpupll_setfreq(struct msc313_cpupll *cpupll, u32 regvalue)
+{
+ ktime_t timeout;
+
+ msc313_cpupll_reg_write32(cpupll, REG_LPF_HIGH_BOTTOM, regvalue);
+
+ iowrite16(0x1, cpupll->base + REG_LPF_MYSTERYONE);
+ iowrite16(0x6, cpupll->base + REG_LPF_MYSTERYTWO);
+ iowrite16(0x8, cpupll->base + REG_LPF_UPDATE_COUNT);
+ iowrite16(BIT(12), cpupll->base + REG_LPF_TRANSITIONCTRL);
+
+ iowrite16(0, cpupll->base + REG_LPF_TOGGLE);
+ iowrite16(1, cpupll->base + REG_LPF_TOGGLE);
+
+ timeout = ktime_add_ns(ktime_get(), LPF_LOCK_TIMEOUT);
+ while (!(ioread16(cpupll->base + REG_LPF_LOCK))) {
+ if (ktime_after(ktime_get(), timeout)) {
+ pr_err("timeout waiting for LPF_LOCK\n");
+ return;
+ }
+ cpu_relax();
+ }
+
+ iowrite16(0, cpupll->base + REG_LPF_TOGGLE);
+
+ msc313_cpupll_reg_write32(cpupll, REG_LPF_LOW_L, regvalue);
+}
+
+static unsigned long msc313_cpupll_frequencyforreg(u32 reg, unsigned long parent_rate)
+{
+ unsigned long long prescaled = ((unsigned long long)parent_rate) * MULTIPLIER;
+
+ if (prescaled == 0 || reg == 0)
+ return 0;
+ return DIV_ROUND_DOWN_ULL(prescaled, reg);
+}
+
+static u32 msc313_cpupll_regforfrequecy(unsigned long rate, unsigned long parent_rate)
+{
+ unsigned long long prescaled = ((unsigned long long)parent_rate) * MULTIPLIER;
+
+ if (prescaled == 0 || rate == 0)
+ return 0;
+ return DIV_ROUND_UP_ULL(prescaled, rate);
+}
+
+static unsigned long msc313_cpupll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct msc313_cpupll *cpupll = to_cpupll(hw);
+
+ return msc313_cpupll_frequencyforreg(msc313_cpupll_reg_read32(cpupll, REG_LPF_LOW_L),
+ parent_rate);
+}
+
+static int msc313_cpupll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ u32 reg = msc313_cpupll_regforfrequecy(req->rate, req->best_parent_rate);
+ long rounded = msc313_cpupll_frequencyforreg(reg, req->best_parent_rate);
+
+ /*
+ * This is my poor attempt at making sure the resulting
+ * rate doesn't overshoot the requested rate.
+ */
+ for (; rounded >= req->rate && reg > 0; reg--)
+ rounded = msc313_cpupll_frequencyforreg(reg, req->best_parent_rate);
+
+ req->rate = rounded;
+
+ return 0;
+}
+
+static int msc313_cpupll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
+{
+ struct msc313_cpupll *cpupll = to_cpupll(hw);
+ u32 reg = msc313_cpupll_regforfrequecy(rate, parent_rate);
+
+ msc313_cpupll_setfreq(cpupll, reg);
+
+ return 0;
+}
+
+static const struct clk_ops msc313_cpupll_ops = {
+ .recalc_rate = msc313_cpupll_recalc_rate,
+ .determine_rate = msc313_cpupll_determine_rate,
+ .set_rate = msc313_cpupll_set_rate,
+};
+
+static const struct of_device_id msc313_cpupll_of_match[] = {
+ { .compatible = "mstar,msc313-cpupll" },
+ {}
+};
+
+static int msc313_cpupll_probe(struct platform_device *pdev)
+{
+ struct clk_init_data clk_init = {};
+ struct clk_parent_data cpupll_parent = { .index = 0 };
+ struct device *dev = &pdev->dev;
+ struct msc313_cpupll *cpupll;
+ int ret;
+
+ cpupll = devm_kzalloc(&pdev->dev, sizeof(*cpupll), GFP_KERNEL);
+ if (!cpupll)
+ return -ENOMEM;
+
+ cpupll->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cpupll->base))
+ return PTR_ERR(cpupll->base);
+
+ /* LPF might not contain the current frequency so fix that up */
+ msc313_cpupll_reg_write32(cpupll, REG_LPF_LOW_L,
+ msc313_cpupll_reg_read32(cpupll, REG_CURRENT));
+
+ clk_init.name = dev_name(dev);
+ clk_init.ops = &msc313_cpupll_ops;
+ clk_init.parent_data = &cpupll_parent;
+ clk_init.num_parents = 1;
+ cpupll->clk_hw.init = &clk_init;
+
+ ret = devm_clk_hw_register(dev, &cpupll->clk_hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, &cpupll->clk_hw);
+}
+
+static struct platform_driver msc313_cpupll_driver = {
+ .driver = {
+ .name = "mstar-msc313-cpupll",
+ .of_match_table = msc313_cpupll_of_match,
+ },
+ .probe = msc313_cpupll_probe,
+};
+builtin_platform_driver(msc313_cpupll_driver);
diff --git a/drivers/clk/mvebu/ap-cpu-clk.c b/drivers/clk/mvebu/ap-cpu-clk.c
index 71bdd7c3ff03..1e44ace7d951 100644
--- a/drivers/clk/mvebu/ap-cpu-clk.c
+++ b/drivers/clk/mvebu/ap-cpu-clk.c
@@ -15,7 +15,6 @@
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "armada_ap_cp_helper.h"
@@ -211,19 +210,21 @@ static int ap_cpu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static long ap_cpu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int ap_cpu_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- int divider = *parent_rate / rate;
+ int divider = req->best_parent_rate / req->rate;
divider = min(divider, APN806_MAX_DIVIDER);
- return *parent_rate / divider;
+ req->rate = req->best_parent_rate / divider;
+
+ return 0;
}
static const struct clk_ops ap_cpu_clk_ops = {
.recalc_rate = ap_cpu_clk_recalc_rate,
- .round_rate = ap_cpu_clk_round_rate,
+ .determine_rate = ap_cpu_clk_determine_rate,
.set_rate = ap_cpu_clk_set_rate,
};
@@ -253,12 +254,12 @@ static int ap_cpu_clock_probe(struct platform_device *pdev)
*/
nclusters = 1;
for_each_of_cpu_node(dn) {
- int cpu, err;
+ u64 cpu;
- err = of_property_read_u32(dn, "reg", &cpu);
- if (WARN_ON(err)) {
+ cpu = of_get_cpu_hwid(dn, 0);
+ if (WARN_ON(cpu == OF_BAD_ADDR)) {
of_node_put(dn);
- return err;
+ return -EINVAL;
}
/* If cpu2 or cpu3 is enabled */
@@ -288,12 +289,12 @@ static int ap_cpu_clock_probe(struct platform_device *pdev)
struct clk_init_data init;
const char *parent_name;
struct clk *parent;
- int cpu, err;
+ u64 cpu;
- err = of_property_read_u32(dn, "reg", &cpu);
- if (WARN_ON(err)) {
+ cpu = of_get_cpu_hwid(dn, 0);
+ if (WARN_ON(cpu == OF_BAD_ADDR)) {
of_node_put(dn);
- return err;
+ return -EINVAL;
}
cluster_index = cpu & APN806_CLUSTER_NUM_MASK;
diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
index e3777ca65912..bd0bc8e7b1e7 100644
--- a/drivers/clk/mvebu/armada-37xx-periph.c
+++ b/drivers/clk/mvebu/armada-37xx-periph.c
@@ -21,7 +21,6 @@
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -455,12 +454,12 @@ static unsigned long clk_pm_cpu_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
}
-static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_pm_cpu_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw);
struct regmap *base = pm_cpu->nb_pm_base;
- unsigned int div = *parent_rate / rate;
+ unsigned int div = req->best_parent_rate / req->rate;
unsigned int load_level;
/* only available when DVFS is enabled */
if (!armada_3700_pm_dvfs_is_enabled(base))
@@ -475,13 +474,16 @@ static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
val >>= offset;
val &= ARMADA_37XX_NB_TBG_DIV_MASK;
- if (val == div)
+ if (val == div) {
/*
* We found a load level matching the target
* divider, switch to this load level and
* return.
*/
- return *parent_rate / div;
+ req->rate = req->best_parent_rate / div;
+
+ return 0;
+ }
}
/* We didn't find any valid divider */
@@ -601,7 +603,7 @@ static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_pm_cpu_ops = {
.get_parent = clk_pm_cpu_get_parent,
- .round_rate = clk_pm_cpu_round_rate,
+ .determine_rate = clk_pm_cpu_determine_rate,
.set_rate = clk_pm_cpu_set_rate,
.recalc_rate = clk_pm_cpu_recalc_rate,
};
@@ -733,7 +735,6 @@ static int armada_3700_periph_clock_probe(struct platform_device *pdev)
const struct clk_periph_data *data;
struct device *dev = &pdev->dev;
int num_periph = 0, i, ret;
- struct resource *res;
data = of_device_get_match_data(dev);
if (!data)
@@ -754,8 +755,7 @@ static int armada_3700_periph_clock_probe(struct platform_device *pdev)
return -ENOMEM;
driver_data->hw_data->num = num_periph;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- driver_data->reg = devm_ioremap_resource(dev, res);
+ driver_data->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(driver_data->reg))
return PTR_ERR(driver_data->reg);
@@ -781,7 +781,7 @@ static int armada_3700_periph_clock_probe(struct platform_device *pdev)
return 0;
}
-static int armada_3700_periph_clock_remove(struct platform_device *pdev)
+static void armada_3700_periph_clock_remove(struct platform_device *pdev)
{
struct clk_periph_driver_data *data = platform_get_drvdata(pdev);
struct clk_hw_onecell_data *hw_data = data->hw_data;
@@ -791,8 +791,6 @@ static int armada_3700_periph_clock_remove(struct platform_device *pdev)
for (i = 0; i < hw_data->num; i++)
clk_hw_unregister(hw_data->hws[i]);
-
- return 0;
}
static struct platform_driver armada_3700_periph_clock_driver = {
diff --git a/drivers/clk/mvebu/armada-37xx-tbg.c b/drivers/clk/mvebu/armada-37xx-tbg.c
index 585a02e0b330..1a16f9c0b1d8 100644
--- a/drivers/clk/mvebu/armada-37xx-tbg.c
+++ b/drivers/clk/mvebu/armada-37xx-tbg.c
@@ -84,10 +84,9 @@ static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
struct clk_hw_onecell_data *hw_tbg_data;
struct device *dev = &pdev->dev;
const char *parent_name;
- struct resource *res;
struct clk *parent;
void __iomem *reg;
- int i, ret;
+ int i;
hw_tbg_data = devm_kzalloc(&pdev->dev,
struct_size(hw_tbg_data, hws, NUM_TBG),
@@ -105,8 +104,7 @@ static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
parent_name = __clk_get_name(parent);
clk_put(parent);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- reg = devm_ioremap_resource(dev, res);
+ reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg))
return PTR_ERR(reg);
@@ -123,12 +121,10 @@ static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
dev_err(dev, "Can't register TBG clock %s\n", name);
}
- ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
-
- return ret;
+ return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
}
-static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
+static void armada_3700_tbg_clock_remove(struct platform_device *pdev)
{
int i;
struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev);
@@ -136,8 +132,6 @@ static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
of_clk_del_provider(pdev->dev.of_node);
for (i = 0; i < hw_tbg_data->num; i++)
clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]);
-
- return 0;
}
static const struct of_device_id armada_3700_tbg_clock_of_match[] = {
diff --git a/drivers/clk/mvebu/armada-37xx-xtal.c b/drivers/clk/mvebu/armada-37xx-xtal.c
index 41271351cf1f..ca88e5e78b06 100644
--- a/drivers/clk/mvebu/armada-37xx-xtal.c
+++ b/drivers/clk/mvebu/armada-37xx-xtal.c
@@ -65,11 +65,9 @@ static int armada_3700_xtal_clock_probe(struct platform_device *pdev)
return ret;
}
-static int armada_3700_xtal_clock_remove(struct platform_device *pdev)
+static void armada_3700_xtal_clock_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
-
- return 0;
}
static const struct of_device_id armada_3700_xtal_clock_of_match[] = {
diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c
index 45665655a258..8d31a595a27c 100644
--- a/drivers/clk/mvebu/armada-xp.c
+++ b/drivers/clk/mvebu/armada-xp.c
@@ -7,7 +7,6 @@
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Andrew Lunn <andrew@lunn.ch>
- *
*/
#include <linux/kernel.h>
@@ -19,8 +18,8 @@
/*
* Core Clocks
*
- * Armada XP Sample At Reset is a 64 bit bitfiled split in two
- * register of 32 bits
+ * Armada XP Sample At Reset is a 64 bit bitfield split in two
+ * registers of 32 bits
*/
#define SARL 0 /* Low part [0:31] */
diff --git a/drivers/clk/mvebu/armada_ap_cp_helper.c b/drivers/clk/mvebu/armada_ap_cp_helper.c
index 6a930f697ee5..e7005de66327 100644
--- a/drivers/clk/mvebu/armada_ap_cp_helper.c
+++ b/drivers/clk/mvebu/armada_ap_cp_helper.c
@@ -16,15 +16,13 @@
char *ap_cp_unique_name(struct device *dev, struct device_node *np,
const char *name)
{
- const __be32 *reg;
- u64 addr;
+ struct resource res;
/* Do not create a name if there is no clock */
if (!name)
return NULL;
- reg = of_get_property(np, "reg", NULL);
- addr = of_translate_address(np, reg);
+ of_address_to_resource(np, 0, &res);
return devm_kasprintf(dev, GFP_KERNEL, "%llx-%s",
- (unsigned long long)addr, name);
+ (unsigned long long)res.start, name);
}
diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c
index 818b175391fa..628032341cbb 100644
--- a/drivers/clk/mvebu/clk-corediv.c
+++ b/drivers/clk/mvebu/clk-corediv.c
@@ -135,19 +135,21 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
return parent_rate / div;
}
-static long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_corediv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
/* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */
u32 div;
- div = *parent_rate / rate;
+ div = req->best_parent_rate / req->rate;
if (div < 4)
div = 4;
else if (div > 6)
div = 8;
- return *parent_rate / div;
+ req->rate = req->best_parent_rate / div;
+
+ return 0;
}
static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
@@ -199,7 +201,7 @@ static const struct clk_corediv_soc_desc armada370_corediv_soc = {
.disable = clk_corediv_disable,
.is_enabled = clk_corediv_is_enabled,
.recalc_rate = clk_corediv_recalc_rate,
- .round_rate = clk_corediv_round_rate,
+ .determine_rate = clk_corediv_determine_rate,
.set_rate = clk_corediv_set_rate,
},
.ratio_reload = BIT(8),
@@ -215,7 +217,7 @@ static const struct clk_corediv_soc_desc armada380_corediv_soc = {
.disable = clk_corediv_disable,
.is_enabled = clk_corediv_is_enabled,
.recalc_rate = clk_corediv_recalc_rate,
- .round_rate = clk_corediv_round_rate,
+ .determine_rate = clk_corediv_determine_rate,
.set_rate = clk_corediv_set_rate,
},
.ratio_reload = BIT(8),
@@ -228,7 +230,7 @@ static const struct clk_corediv_soc_desc armada375_corediv_soc = {
.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
.ops = {
.recalc_rate = clk_corediv_recalc_rate,
- .round_rate = clk_corediv_round_rate,
+ .determine_rate = clk_corediv_determine_rate,
.set_rate = clk_corediv_set_rate,
},
.ratio_reload = BIT(8),
@@ -240,7 +242,7 @@ static const struct clk_corediv_soc_desc mv98dx3236_corediv_soc = {
.ndescs = ARRAY_SIZE(mv98dx3236_corediv_desc),
.ops = {
.recalc_rate = clk_corediv_recalc_rate,
- .round_rate = clk_corediv_round_rate,
+ .determine_rate = clk_corediv_determine_rate,
.set_rate = clk_corediv_set_rate,
},
.ratio_reload = BIT(10),
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c
index c2af3395cf13..0de7660e73d2 100644
--- a/drivers/clk/mvebu/clk-cpu.c
+++ b/drivers/clk/mvebu/clk-cpu.c
@@ -56,19 +56,21 @@ static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk,
return parent_rate / div;
}
-static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_cpu_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
/* Valid ratio are 1:1, 1:2 and 1:3 */
u32 div;
- div = *parent_rate / rate;
+ div = req->best_parent_rate / req->rate;
if (div == 0)
div = 1;
else if (div > 3)
div = 3;
- return *parent_rate / div;
+ req->rate = req->best_parent_rate / div;
+
+ return 0;
}
static int clk_cpu_off_set_rate(struct clk_hw *hwclk, unsigned long rate,
@@ -159,7 +161,7 @@ static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate,
static const struct clk_ops cpu_ops = {
.recalc_rate = clk_cpu_recalc_rate,
- .round_rate = clk_cpu_round_rate,
+ .determine_rate = clk_cpu_determine_rate,
.set_rate = clk_cpu_set_rate,
};
@@ -168,8 +170,8 @@ static void __init of_cpu_clk_setup(struct device_node *node)
struct cpu_clk *cpuclk;
void __iomem *clock_complex_base = of_iomap(node, 0);
void __iomem *pmu_dfs_base = of_iomap(node, 1);
- int ncpus = 0;
- struct device_node *dn;
+ int ncpus = num_possible_cpus();
+ int cpu;
if (clock_complex_base == NULL) {
pr_err("%s: clock-complex base register not set\n",
@@ -181,9 +183,6 @@ static void __init of_cpu_clk_setup(struct device_node *node)
pr_warn("%s: pmu-dfs base register not set, dynamic frequency scaling not available\n",
__func__);
- for_each_of_cpu_node(dn)
- ncpus++;
-
cpuclk = kcalloc(ncpus, sizeof(*cpuclk), GFP_KERNEL);
if (WARN_ON(!cpuclk))
goto cpuclk_out;
@@ -192,19 +191,14 @@ static void __init of_cpu_clk_setup(struct device_node *node)
if (WARN_ON(!clks))
goto clks_out;
- for_each_of_cpu_node(dn) {
+ for_each_possible_cpu(cpu) {
struct clk_init_data init;
struct clk *clk;
char *clk_name = kzalloc(5, GFP_KERNEL);
- int cpu, err;
if (WARN_ON(!clk_name))
goto bail_out;
- err = of_property_read_u32(dn, "reg", &cpu);
- if (WARN_ON(err))
- goto bail_out;
-
sprintf(clk_name, "cpu%d", cpu);
cpuclk[cpu].parent_name = of_clk_get_parent_name(node, 0);
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 785dbede4835..5adbbd91a6db 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -215,22 +215,26 @@ static struct clk *clk_gating_get_src(
return ERR_PTR(-ENODEV);
}
-static int mvebu_clk_gating_suspend(void)
+static int mvebu_clk_gating_suspend(void *data)
{
ctrl->saved_reg = readl(ctrl->base);
return 0;
}
-static void mvebu_clk_gating_resume(void)
+static void mvebu_clk_gating_resume(void *data)
{
writel(ctrl->saved_reg, ctrl->base);
}
-static struct syscore_ops clk_gate_syscore_ops = {
+static const struct syscore_ops clk_gate_syscore_ops = {
.suspend = mvebu_clk_gating_suspend,
.resume = mvebu_clk_gating_resume,
};
+static struct syscore clk_gate_syscore = {
+ .ops = &clk_gate_syscore_ops,
+};
+
void __init mvebu_clk_gating_setup(struct device_node *np,
const struct clk_gating_soc_desc *desc)
{
@@ -284,7 +288,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
of_clk_add_provider(np, clk_gating_get_src, ctrl);
- register_syscore_ops(&clk_gate_syscore_ops);
+ register_syscore(&clk_gate_syscore);
return;
gates_out:
diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c
index 84c8900542e4..b47c86906046 100644
--- a/drivers/clk/mvebu/cp110-system-controller.c
+++ b/drivers/clk/mvebu/cp110-system-controller.c
@@ -110,6 +110,25 @@ static const char * const gate_base_names[] = {
[CP110_GATE_EIP197] = "eip197"
};
+static unsigned long gate_flags(const u8 bit_idx)
+{
+ switch (bit_idx) {
+ case CP110_GATE_PCIE_X1_0:
+ case CP110_GATE_PCIE_X1_1:
+ case CP110_GATE_PCIE_X4:
+ /*
+ * If a port had an active link at boot time, stopping
+ * the clock creates a failed state from which controller
+ * driver can not recover.
+ * Prevent stopping this clock till after a driver has taken
+ * ownership.
+ */
+ return CLK_IGNORE_UNUSED;
+ default:
+ return 0;
+ }
+};
+
struct cp110_gate_clk {
struct clk_hw hw;
struct regmap *regmap;
@@ -171,6 +190,7 @@ static struct clk_hw *cp110_register_gate(const char *name,
init.ops = &cp110_gate_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
+ init.flags = gate_flags(bit_idx);
gate->regmap = regmap;
gate->bit_idx = bit_idx;
@@ -240,9 +260,9 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
GFP_KERNEL);
if (!cp110_clk_data)
return -ENOMEM;
+ cp110_clk_data->num = CP110_CLK_NUM;
cp110_clks = cp110_clk_data->hws;
- cp110_clk_data->num = CP110_CLK_NUM;
/* Register the PLL0 which is the root of the hw tree */
pll0_name = ap_cp_unique_name(dev, syscon_node, "pll0");
diff --git a/drivers/clk/mvebu/dove-divider.c b/drivers/clk/mvebu/dove-divider.c
index 7e35c891e168..47cc49e4cd99 100644
--- a/drivers/clk/mvebu/dove-divider.c
+++ b/drivers/clk/mvebu/dove-divider.c
@@ -108,23 +108,23 @@ static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent)
return rate;
}
-static long dove_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent)
+static int dove_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct dove_clk *dc = to_dove_clk(hw);
- unsigned long parent_rate = *parent;
+ unsigned long parent_rate = req->best_parent_rate;
int divider;
- divider = dove_calc_divider(dc, rate, parent_rate, false);
+ divider = dove_calc_divider(dc, req->rate, parent_rate, false);
if (divider < 0)
return divider;
- rate = DIV_ROUND_CLOSEST(parent_rate, divider);
+ req->rate = DIV_ROUND_CLOSEST(parent_rate, divider);
pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
- __func__, dc->name, divider, parent_rate, rate);
+ __func__, dc->name, divider, parent_rate, req->rate);
- return rate;
+ return 0;
}
static int dove_set_clock(struct clk_hw *hw, unsigned long rate,
@@ -154,7 +154,7 @@ static int dove_set_clock(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops dove_divider_ops = {
.set_rate = dove_set_clock,
- .round_rate = dove_round_rate,
+ .determine_rate = dove_determine_rate,
.recalc_rate = dove_recalc_rate,
};
@@ -170,7 +170,7 @@ static struct clk *clk_register_dove_divider(struct device *dev,
.num_parents = num_parents,
};
- strlcpy(name, dc->name, sizeof(name));
+ strscpy(name, dc->name, sizeof(name));
dc->hw.init = &init;
dc->base = base;
diff --git a/drivers/clk/mxs/clk-div.c b/drivers/clk/mxs/clk-div.c
index 928e8b1c46a1..8afe1a9c1552 100644
--- a/drivers/clk/mxs/clk-div.c
+++ b/drivers/clk/mxs/clk-div.c
@@ -16,7 +16,7 @@
* @busy: busy bit shift
*
* The mxs divider clock is a subclass of basic clk_divider with an
- * addtional busy bit.
+ * additional busy bit.
*/
struct clk_div {
struct clk_divider divider;
@@ -40,12 +40,12 @@ static unsigned long clk_div_recalc_rate(struct clk_hw *hw,
return div->ops->recalc_rate(&div->divider.hw, parent_rate);
}
-static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_div *div = to_clk_div(hw);
- return div->ops->round_rate(&div->divider.hw, rate, prate);
+ return div->ops->determine_rate(&div->divider.hw, req);
}
static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -63,7 +63,7 @@ static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_div_ops = {
.recalc_rate = clk_div_recalc_rate,
- .round_rate = clk_div_round_rate,
+ .determine_rate = clk_div_determine_rate,
.set_rate = clk_div_set_rate,
};
diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c
index bba0d840dd76..73f514fb84ff 100644
--- a/drivers/clk/mxs/clk-frac.c
+++ b/drivers/clk/mxs/clk-frac.c
@@ -44,18 +44,18 @@ static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
return tmp_rate >> frac->width;
}
-static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_frac_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct clk_frac *frac = to_clk_frac(hw);
- unsigned long parent_rate = *prate;
+ unsigned long parent_rate = req->best_parent_rate;
u32 div;
u64 tmp, tmp_rate, result;
- if (rate > parent_rate)
+ if (req->rate > parent_rate)
return -EINVAL;
- tmp = rate;
+ tmp = req->rate;
tmp <<= frac->width;
do_div(tmp, parent_rate);
div = tmp;
@@ -67,7 +67,9 @@ static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
result = tmp_rate >> frac->width;
if ((result << frac->width) < tmp_rate)
result += 1;
- return result;
+ req->rate = result;
+
+ return 0;
}
static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -103,7 +105,7 @@ static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops clk_frac_ops = {
.recalc_rate = clk_frac_recalc_rate,
- .round_rate = clk_frac_round_rate,
+ .determine_rate = clk_frac_determine_rate,
.set_rate = clk_frac_set_rate,
};
diff --git a/drivers/clk/mxs/clk-ref.c b/drivers/clk/mxs/clk-ref.c
index 3161a0b021ee..a99ee4cd2ece 100644
--- a/drivers/clk/mxs/clk-ref.c
+++ b/drivers/clk/mxs/clk-ref.c
@@ -57,27 +57,24 @@ static unsigned long clk_ref_recalc_rate(struct clk_hw *hw,
return tmp;
}
-static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_ref_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- unsigned long parent_rate = *prate;
+ unsigned long parent_rate = req->best_parent_rate;
u64 tmp = parent_rate;
u8 frac;
- tmp = tmp * 18 + rate / 2;
- do_div(tmp, rate);
- frac = tmp;
-
- if (frac < 18)
- frac = 18;
- else if (frac > 35)
- frac = 35;
+ tmp = tmp * 18 + req->rate / 2;
+ do_div(tmp, req->rate);
+ frac = clamp(tmp, 18, 35);
tmp = parent_rate;
tmp *= 18;
do_div(tmp, frac);
- return tmp;
+ req->rate = tmp;
+
+ return 0;
}
static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -91,12 +88,7 @@ static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate,
tmp = tmp * 18 + rate / 2;
do_div(tmp, rate);
- frac = tmp;
-
- if (frac < 18)
- frac = 18;
- else if (frac > 35)
- frac = 35;
+ frac = clamp(tmp, 18, 35);
spin_lock_irqsave(&mxs_lock, flags);
@@ -114,7 +106,7 @@ static const struct clk_ops clk_ref_ops = {
.enable = clk_ref_enable,
.disable = clk_ref_disable,
.recalc_rate = clk_ref_recalc_rate,
- .round_rate = clk_ref_round_rate,
+ .determine_rate = clk_ref_determine_rate,
.set_rate = clk_ref_set_rate,
};
diff --git a/drivers/clk/nuvoton/Kconfig b/drivers/clk/nuvoton/Kconfig
new file mode 100644
index 000000000000..e7019b69ea74
--- /dev/null
+++ b/drivers/clk/nuvoton/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+# common clock support for Nuvoton SoC family.
+
+config COMMON_CLK_NUVOTON
+ bool "Nuvoton clock controller common support"
+ depends on ARCH_MA35 || COMPILE_TEST
+ default ARCH_MA35
+ help
+ Say y here to enable common clock controller for Nuvoton platforms.
+
+if COMMON_CLK_NUVOTON
+
+config CLK_MA35D1
+ bool "Nuvoton MA35D1 clock controller support"
+ default ARCH_MA35
+ help
+ Build the clock controller driver for MA35D1 SoC.
+
+endif
diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
new file mode 100644
index 000000000000..c3c59dd9f2aa
--- /dev/null
+++ b/drivers/clk/nuvoton/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1.o
+obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1-divider.o
+obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1-pll.o
diff --git a/drivers/clk/nuvoton/clk-ma35d1-divider.c b/drivers/clk/nuvoton/clk-ma35d1-divider.c
new file mode 100644
index 000000000000..e39f53d5bf45
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#include "clk-ma35d1.h"
+
+struct ma35d1_adc_clk_div {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 shift;
+ u8 width;
+ u32 mask;
+ const struct clk_div_table *table;
+ /* protects concurrent access to clock divider registers */
+ spinlock_t *lock;
+};
+
+static inline struct ma35d1_adc_clk_div *to_ma35d1_adc_clk_div(struct clk_hw *_hw)
+{
+ return container_of(_hw, struct ma35d1_adc_clk_div, hw);
+}
+
+static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ unsigned int val;
+ struct ma35d1_adc_clk_div *dclk = to_ma35d1_adc_clk_div(hw);
+
+ val = readl_relaxed(dclk->reg) >> dclk->shift;
+ val &= clk_div_mask(dclk->width);
+ val += 1;
+ return divider_recalc_rate(hw, parent_rate, val, dclk->table,
+ CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
+}
+
+static int ma35d1_clkdiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ma35d1_adc_clk_div *dclk = to_ma35d1_adc_clk_div(hw);
+
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate,
+ dclk->table, dclk->width,
+ CLK_DIVIDER_ROUND_CLOSEST);
+
+ return 0;
+}
+
+static int ma35d1_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
+{
+ int value;
+ unsigned long flags = 0;
+ u32 data;
+ struct ma35d1_adc_clk_div *dclk = to_ma35d1_adc_clk_div(hw);
+
+ value = divider_get_val(rate, parent_rate, dclk->table,
+ dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
+
+ spin_lock_irqsave(dclk->lock, flags);
+
+ data = readl_relaxed(dclk->reg);
+ data &= ~(clk_div_mask(dclk->width) << dclk->shift);
+ data |= (value - 1) << dclk->shift;
+ data |= dclk->mask;
+ writel_relaxed(data, dclk->reg);
+
+ spin_unlock_irqrestore(dclk->lock, flags);
+ return 0;
+}
+
+static const struct clk_ops ma35d1_adc_clkdiv_ops = {
+ .recalc_rate = ma35d1_clkdiv_recalc_rate,
+ .determine_rate = ma35d1_clkdiv_determine_rate,
+ .set_rate = ma35d1_clkdiv_set_rate,
+};
+
+struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
+ struct clk_hw *parent_hw, spinlock_t *lock,
+ unsigned long flags, void __iomem *reg,
+ u8 shift, u8 width, u32 mask_bit)
+{
+ struct ma35d1_adc_clk_div *div;
+ struct clk_init_data init;
+ struct clk_div_table *table;
+ struct clk_parent_data pdata = { .index = 0 };
+ u32 max_div, min_div;
+ struct clk_hw *hw;
+ int ret;
+ int i;
+
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ max_div = clk_div_mask(width) + 1;
+ min_div = 1;
+
+ table = devm_kcalloc(dev, max_div + 1, sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < max_div; i++) {
+ table[i].val = min_div + i;
+ table[i].div = 2 * table[i].val;
+ }
+ table[max_div].val = 0;
+ table[max_div].div = 0;
+
+ memset(&init, 0, sizeof(init));
+ init.name = name;
+ init.ops = &ma35d1_adc_clkdiv_ops;
+ init.flags |= flags;
+ pdata.hw = parent_hw;
+ init.parent_data = &pdata;
+ init.num_parents = 1;
+
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->mask = mask_bit ? BIT(mask_bit) : 0;
+ div->lock = lock;
+ div->hw.init = &init;
+ div->table = table;
+
+ hw = &div->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ERR_PTR(ret);
+ return hw;
+}
+EXPORT_SYMBOL_GPL(ma35d1_reg_adc_clkdiv);
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
new file mode 100644
index 000000000000..4620acfe47e8
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+#include "clk-ma35d1.h"
+
+/* PLL frequency limits */
+#define PLL_FREF_MAX_FREQ (200 * HZ_PER_MHZ)
+#define PLL_FREF_MIN_FREQ (1 * HZ_PER_MHZ)
+#define PLL_FREF_M_MAX_FREQ (40 * HZ_PER_MHZ)
+#define PLL_FREF_M_MIN_FREQ (10 * HZ_PER_MHZ)
+#define PLL_FCLK_MAX_FREQ (2400 * HZ_PER_MHZ)
+#define PLL_FCLK_MIN_FREQ (600 * HZ_PER_MHZ)
+#define PLL_FCLKO_MAX_FREQ (2400 * HZ_PER_MHZ)
+#define PLL_FCLKO_MIN_FREQ (85700 * HZ_PER_KHZ)
+#define PLL_SS_RATE 0x77
+#define PLL_SLOPE 0x58CFA
+
+#define REG_PLL_CTL0_OFFSET 0x0
+#define REG_PLL_CTL1_OFFSET 0x4
+#define REG_PLL_CTL2_OFFSET 0x8
+
+/* bit fields for REG_CLK_PLL0CTL0, which is SMIC PLL design */
+#define SPLL0_CTL0_FBDIV GENMASK(7, 0)
+#define SPLL0_CTL0_INDIV GENMASK(11, 8)
+#define SPLL0_CTL0_OUTDIV GENMASK(13, 12)
+#define SPLL0_CTL0_PD BIT(16)
+#define SPLL0_CTL0_BP BIT(17)
+
+/* bit fields for REG_CLK_PLLxCTL0 ~ REG_CLK_PLLxCTL2, where x = 2 ~ 5 */
+#define PLL_CTL0_FBDIV GENMASK(10, 0)
+#define PLL_CTL0_INDIV GENMASK(17, 12)
+#define PLL_CTL0_MODE GENMASK(19, 18)
+#define PLL_CTL0_SSRATE GENMASK(30, 20)
+#define PLL_CTL1_PD BIT(0)
+#define PLL_CTL1_BP BIT(1)
+#define PLL_CTL1_OUTDIV GENMASK(6, 4)
+#define PLL_CTL1_FRAC GENMASK(31, 24)
+#define PLL_CTL2_SLOPE GENMASK(23, 0)
+
+#define INDIV_MIN 1
+#define INDIV_MAX 63
+#define FBDIV_MIN 16
+#define FBDIV_MAX 2047
+#define FBDIV_FRAC_MIN 1600
+#define FBDIV_FRAC_MAX 204700
+#define OUTDIV_MIN 1
+#define OUTDIV_MAX 7
+
+#define PLL_MODE_INT 0
+#define PLL_MODE_FRAC 1
+#define PLL_MODE_SS 2
+
+struct ma35d1_clk_pll {
+ struct clk_hw hw;
+ u32 id;
+ u8 mode;
+ void __iomem *ctl0_base;
+ void __iomem *ctl1_base;
+ void __iomem *ctl2_base;
+};
+
+static inline struct ma35d1_clk_pll *to_ma35d1_clk_pll(struct clk_hw *_hw)
+{
+ return container_of(_hw, struct ma35d1_clk_pll, hw);
+}
+
+static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
+ unsigned long parent_rate)
+{
+ u32 m, n, p, outdiv;
+ u64 pll_freq;
+
+ if (pll0_ctl0 & SPLL0_CTL0_BP)
+ return parent_rate;
+
+ n = FIELD_GET(SPLL0_CTL0_FBDIV, pll0_ctl0);
+ m = FIELD_GET(SPLL0_CTL0_INDIV, pll0_ctl0);
+ p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
+ outdiv = 1 << p;
+ pll_freq = (u64)parent_rate * n;
+ div_u64(pll_freq, m * outdiv);
+ return pll_freq;
+}
+
+static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long parent_rate)
+{
+ unsigned long pll_freq, x;
+ u32 m, n, p;
+
+ if (reg_ctl[1] & PLL_CTL1_BP)
+ return parent_rate;
+
+ n = FIELD_GET(PLL_CTL0_FBDIV, reg_ctl[0]);
+ m = FIELD_GET(PLL_CTL0_INDIV, reg_ctl[0]);
+ p = FIELD_GET(PLL_CTL1_OUTDIV, reg_ctl[1]);
+
+ if (mode == PLL_MODE_INT) {
+ pll_freq = (u64)parent_rate * n;
+ div_u64(pll_freq, m * p);
+ } else {
+ x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
+ /* 2 decimal places floating to integer (ex. 1.23 to 123) */
+ n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
+ pll_freq = div_u64(parent_rate * n, 100 * m * p);
+ }
+ return pll_freq;
+}
+
+static int ma35d1_pll_find_closest(struct ma35d1_clk_pll *pll, unsigned long rate,
+ unsigned long parent_rate, u32 *reg_ctl,
+ unsigned long *freq)
+{
+ unsigned long min_diff = ULONG_MAX;
+ int fbdiv_min, fbdiv_max;
+ int p, m, n;
+
+ *freq = 0;
+ if (rate < PLL_FCLKO_MIN_FREQ || rate > PLL_FCLKO_MAX_FREQ)
+ return -EINVAL;
+
+ if (pll->mode == PLL_MODE_INT) {
+ fbdiv_min = FBDIV_MIN;
+ fbdiv_max = FBDIV_MAX;
+ } else {
+ fbdiv_min = FBDIV_FRAC_MIN;
+ fbdiv_max = FBDIV_FRAC_MAX;
+ }
+
+ for (m = INDIV_MIN; m <= INDIV_MAX; m++) {
+ for (n = fbdiv_min; n <= fbdiv_max; n++) {
+ for (p = OUTDIV_MIN; p <= OUTDIV_MAX; p++) {
+ unsigned long tmp, fout, fclk, diff;
+
+ tmp = div_u64(parent_rate, m);
+ if (tmp < PLL_FREF_M_MIN_FREQ ||
+ tmp > PLL_FREF_M_MAX_FREQ)
+ continue; /* constrain */
+
+ fclk = div_u64(parent_rate * n, m);
+ /* for 2 decimal places */
+ if (pll->mode != PLL_MODE_INT)
+ fclk = div_u64(fclk, 100);
+
+ if (fclk < PLL_FCLK_MIN_FREQ ||
+ fclk > PLL_FCLK_MAX_FREQ)
+ continue; /* constrain */
+
+ fout = div_u64(fclk, p);
+ if (fout < PLL_FCLKO_MIN_FREQ ||
+ fout > PLL_FCLKO_MAX_FREQ)
+ continue; /* constrain */
+
+ diff = abs(rate - fout);
+ if (diff < min_diff) {
+ reg_ctl[0] = FIELD_PREP(PLL_CTL0_INDIV, m) |
+ FIELD_PREP(PLL_CTL0_FBDIV, n);
+ reg_ctl[1] = FIELD_PREP(PLL_CTL1_OUTDIV, p);
+ *freq = fout;
+ min_diff = diff;
+ if (min_diff == 0)
+ break;
+ }
+ }
+ }
+ }
+ if (*freq == 0)
+ return -EINVAL; /* cannot find even one valid setting */
+ return 0;
+}
+
+static int ma35d1_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+ u32 reg_ctl[3] = { 0 };
+ unsigned long pll_freq;
+ int ret;
+
+ if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)
+ return -EINVAL;
+
+ ret = ma35d1_pll_find_closest(pll, rate, parent_rate, reg_ctl, &pll_freq);
+ if (ret != 0)
+ return ret;
+
+ switch (pll->mode) {
+ case PLL_MODE_INT:
+ reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_INT);
+ break;
+ case PLL_MODE_FRAC:
+ reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_FRAC);
+ break;
+ case PLL_MODE_SS:
+ reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_SS) |
+ FIELD_PREP(PLL_CTL0_SSRATE, PLL_SS_RATE);
+ reg_ctl[2] = FIELD_PREP(PLL_CTL2_SLOPE, PLL_SLOPE);
+ break;
+ }
+ reg_ctl[1] |= PLL_CTL1_PD;
+
+ writel_relaxed(reg_ctl[0], pll->ctl0_base);
+ writel_relaxed(reg_ctl[1], pll->ctl1_base);
+ writel_relaxed(reg_ctl[2], pll->ctl2_base);
+ return 0;
+}
+
+static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+ u32 reg_ctl[3];
+ unsigned long pll_freq;
+
+ if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)
+ return 0;
+
+ switch (pll->id) {
+ case CAPLL:
+ reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+ pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], parent_rate);
+ return pll_freq;
+ case DDRPLL:
+ case APLL:
+ case EPLL:
+ case VPLL:
+ reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+ reg_ctl[1] = readl_relaxed(pll->ctl1_base);
+ pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, parent_rate);
+ return pll_freq;
+ }
+ return 0;
+}
+
+static int ma35d1_clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+ u32 reg_ctl[3] = { 0 };
+ unsigned long pll_freq;
+ long ret;
+
+ if (req->best_parent_rate < PLL_FREF_MIN_FREQ || req->best_parent_rate > PLL_FREF_MAX_FREQ)
+ return -EINVAL;
+
+ ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate,
+ reg_ctl, &pll_freq);
+ if (ret < 0)
+ return ret;
+
+ switch (pll->id) {
+ case CAPLL:
+ reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+ pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate);
+ req->rate = pll_freq;
+
+ return 0;
+ case DDRPLL:
+ case APLL:
+ case EPLL:
+ case VPLL:
+ reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+ reg_ctl[1] = readl_relaxed(pll->ctl1_base);
+ pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate);
+ req->rate = pll_freq;
+
+ return 0;
+ }
+
+ req->rate = 0;
+
+ return 0;
+}
+
+static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
+{
+ struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+ u32 val = readl_relaxed(pll->ctl1_base);
+
+ return !(val & PLL_CTL1_PD);
+}
+
+static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
+{
+ struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+ u32 val;
+
+ val = readl_relaxed(pll->ctl1_base);
+ val &= ~PLL_CTL1_PD;
+ writel_relaxed(val, pll->ctl1_base);
+ return 0;
+}
+
+static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
+{
+ struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+ u32 val;
+
+ val = readl_relaxed(pll->ctl1_base);
+ val |= PLL_CTL1_PD;
+ writel_relaxed(val, pll->ctl1_base);
+}
+
+static const struct clk_ops ma35d1_clk_pll_ops = {
+ .is_prepared = ma35d1_clk_pll_is_prepared,
+ .prepare = ma35d1_clk_pll_prepare,
+ .unprepare = ma35d1_clk_pll_unprepare,
+ .set_rate = ma35d1_clk_pll_set_rate,
+ .recalc_rate = ma35d1_clk_pll_recalc_rate,
+ .determine_rate = ma35d1_clk_pll_determine_rate,
+};
+
+static const struct clk_ops ma35d1_clk_fixed_pll_ops = {
+ .recalc_rate = ma35d1_clk_pll_recalc_rate,
+ .determine_rate = ma35d1_clk_pll_determine_rate,
+};
+
+struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
+ struct clk_hw *parent_hw, void __iomem *base)
+{
+ struct clk_parent_data pdata = { .index = 0 };
+ struct clk_init_data init = {};
+ struct ma35d1_clk_pll *pll;
+ struct clk_hw *hw;
+ int ret;
+
+ pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->id = id;
+ pll->mode = u8mode;
+ pll->ctl0_base = base + REG_PLL_CTL0_OFFSET;
+ pll->ctl1_base = base + REG_PLL_CTL1_OFFSET;
+ pll->ctl2_base = base + REG_PLL_CTL2_OFFSET;
+
+ init.name = name;
+ init.flags = 0;
+ pdata.hw = parent_hw;
+ init.parent_data = &pdata;
+ init.num_parents = 1;
+
+ if (id == CAPLL || id == DDRPLL)
+ init.ops = &ma35d1_clk_fixed_pll_ops;
+ else
+ init.ops = &ma35d1_clk_pll_ops;
+
+ pll->hw.init = &init;
+ hw = &pll->hw;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ERR_PTR(ret);
+ return hw;
+}
+EXPORT_SYMBOL_GPL(ma35d1_reg_clk_pll);
diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
new file mode 100644
index 000000000000..9f65d0623517
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1.c
@@ -0,0 +1,1058 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+#include "clk-ma35d1.h"
+
+static DEFINE_SPINLOCK(ma35d1_lock);
+
+#define PLL_MAX_NUM 5
+
+/* Clock Control Registers Offset */
+#define REG_CLK_PWRCTL 0x00
+#define REG_CLK_SYSCLK0 0x04
+#define REG_CLK_SYSCLK1 0x08
+#define REG_CLK_APBCLK0 0x0c
+#define REG_CLK_APBCLK1 0x10
+#define REG_CLK_APBCLK2 0x14
+#define REG_CLK_CLKSEL0 0x18
+#define REG_CLK_CLKSEL1 0x1c
+#define REG_CLK_CLKSEL2 0x20
+#define REG_CLK_CLKSEL3 0x24
+#define REG_CLK_CLKSEL4 0x28
+#define REG_CLK_CLKDIV0 0x2c
+#define REG_CLK_CLKDIV1 0x30
+#define REG_CLK_CLKDIV2 0x34
+#define REG_CLK_CLKDIV3 0x38
+#define REG_CLK_CLKDIV4 0x3c
+#define REG_CLK_CLKOCTL 0x40
+#define REG_CLK_STATUS 0x50
+#define REG_CLK_PLL0CTL0 0x60
+#define REG_CLK_PLL2CTL0 0x80
+#define REG_CLK_PLL2CTL1 0x84
+#define REG_CLK_PLL2CTL2 0x88
+#define REG_CLK_PLL3CTL0 0x90
+#define REG_CLK_PLL3CTL1 0x94
+#define REG_CLK_PLL3CTL2 0x98
+#define REG_CLK_PLL4CTL0 0xa0
+#define REG_CLK_PLL4CTL1 0xa4
+#define REG_CLK_PLL4CTL2 0xa8
+#define REG_CLK_PLL5CTL0 0xb0
+#define REG_CLK_PLL5CTL1 0xb4
+#define REG_CLK_PLL5CTL2 0xb8
+#define REG_CLK_CLKDCTL 0xc0
+#define REG_CLK_CLKDSTS 0xc4
+#define REG_CLK_CDUPB 0xc8
+#define REG_CLK_CDLOWB 0xcc
+#define REG_CLK_CKFLTRCTL 0xd0
+#define REG_CLK_TESTCLK 0xf0
+#define REG_CLK_PLLCTL 0x40
+
+#define PLL_MODE_INT 0
+#define PLL_MODE_FRAC 1
+#define PLL_MODE_SS 2
+
+static const struct clk_parent_data ca35clk_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "capll", },
+ { .fw_name = "ddrpll", },
+};
+
+static const struct clk_parent_data sysclk0_sel_clks[] = {
+ { .fw_name = "epll_div2", },
+ { .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data sysclk1_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data axiclk_sel_clks[] = {
+ { .fw_name = "capll_div2", },
+ { .fw_name = "capll_div4", },
+};
+
+static const struct clk_parent_data ccap_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "vpll", },
+ { .fw_name = "apll", },
+ { .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data sdh_sel_clks[] = {
+ { .fw_name = "syspll", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data dcu_sel_clks[] = {
+ { .fw_name = "epll_div2", },
+ { .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data gfx_sel_clks[] = {
+ { .fw_name = "epll", },
+ { .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data dbg_sel_clks[] = {
+ { .fw_name = "hirc", },
+ { .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data timer0_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk0", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer1_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk0", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer2_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk1", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer3_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk1", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer4_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk2", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer5_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk2", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer6_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk0", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer7_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk0", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer8_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk1", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer9_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk1", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer10_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk2", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer11_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk2", },
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "lirc", },
+ { .index = -1, },
+ { .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data uart_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "sysclk1_div2", },
+};
+
+static const struct clk_parent_data wdt0_sel_clks[] = {
+ { .index = -1, },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk3_div4096", },
+ { .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wdt1_sel_clks[] = {
+ { .index = -1, },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk3_div4096", },
+ { .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wdt2_sel_clks[] = {
+ { .index = -1, },
+ { .fw_name = "lxt", },
+ { .fw_name = "pclk4_div4096", },
+ { .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wwdt0_sel_clks[] = {
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "pclk3_div4096", },
+ { .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wwdt1_sel_clks[] = {
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "pclk3_div4096", },
+ { .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wwdt2_sel_clks[] = {
+ { .index = -1, },
+ { .index = -1, },
+ { .fw_name = "pclk4_div4096", },
+ { .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data spi0_sel_clks[] = {
+ { .fw_name = "pclk1", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data spi1_sel_clks[] = {
+ { .fw_name = "pclk2", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data spi2_sel_clks[] = {
+ { .fw_name = "pclk1", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data spi3_sel_clks[] = {
+ { .fw_name = "pclk2", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data qspi0_sel_clks[] = {
+ { .fw_name = "pclk0", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data qspi1_sel_clks[] = {
+ { .fw_name = "pclk0", },
+ { .fw_name = "apll", },
+};
+
+static const struct clk_parent_data i2s0_sel_clks[] = {
+ { .fw_name = "apll", },
+ { .fw_name = "sysclk1_div2", },
+};
+
+static const struct clk_parent_data i2s1_sel_clks[] = {
+ { .fw_name = "apll", },
+ { .fw_name = "sysclk1_div2", },
+};
+
+static const struct clk_parent_data can_sel_clks[] = {
+ { .fw_name = "apll", },
+ { .fw_name = "vpll", },
+};
+
+static const struct clk_parent_data cko_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+ { .fw_name = "hirc", },
+ { .fw_name = "lirc", },
+ { .fw_name = "capll_div4", },
+ { .fw_name = "syspll", },
+ { .fw_name = "ddrpll", },
+ { .fw_name = "epll_div2", },
+ { .fw_name = "apll", },
+ { .fw_name = "vpll", },
+};
+
+static const struct clk_parent_data smc_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "pclk4", },
+};
+
+static const struct clk_parent_data kpi_sel_clks[] = {
+ { .fw_name = "hxt", },
+ { .fw_name = "lxt", },
+};
+
+static const struct clk_div_table ip_div_table[] = {
+ {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
+ {5, 12}, {6, 14}, {7, 16}, {0, 0},
+};
+
+static const struct clk_div_table eadc_div_table[] = {
+ {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
+ {5, 12}, {6, 14}, {7, 16}, {8, 18},
+ {9, 20}, {10, 22}, {11, 24}, {12, 26},
+ {13, 28}, {14, 30}, {15, 32}, {0, 0},
+};
+
+static struct clk_hw *ma35d1_clk_fixed(const char *name, int rate)
+{
+ return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
+static struct clk_hw *ma35d1_clk_mux_parent(struct device *dev, const char *name,
+ void __iomem *reg, u8 shift, u8 width,
+ const struct clk_parent_data *pdata,
+ int num_pdata)
+{
+ return clk_hw_register_mux_parent_data(dev, name, pdata, num_pdata,
+ CLK_SET_RATE_NO_REPARENT, reg, shift,
+ width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_mux(struct device *dev, const char *name,
+ void __iomem *reg, u8 shift, u8 width,
+ const struct clk_parent_data *pdata,
+ int num_pdata)
+{
+ return clk_hw_register_mux_parent_data(dev, name, pdata, num_pdata,
+ CLK_SET_RATE_NO_REPARENT, reg, shift,
+ width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_divider(struct device *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, u8 width)
+{
+ return devm_clk_hw_register_divider(dev, name, parent, CLK_SET_RATE_PARENT,
+ reg, shift, width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_divider_pow2(struct device *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, u8 width)
+{
+ return devm_clk_hw_register_divider(dev, name, parent,
+ CLK_DIVIDER_POWER_OF_TWO, reg, shift,
+ width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_divider_table(struct device *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, u8 width,
+ const struct clk_div_table *table)
+{
+ return devm_clk_hw_register_divider_table(dev, name, parent, 0,
+ reg, shift, width, 0,
+ table, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_fixed_factor(struct device *dev, const char *name,
+ const char *parent, unsigned int mult,
+ unsigned int div)
+{
+ return devm_clk_hw_register_fixed_factor(dev, name, parent,
+ CLK_SET_RATE_PARENT, mult, div);
+}
+
+static struct clk_hw *ma35d1_clk_gate(struct device *dev, const char *name, const char *parent,
+ void __iomem *reg, u8 shift)
+{
+ return devm_clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT,
+ reg, shift, 0, &ma35d1_lock);
+}
+
+static int ma35d1_get_pll_setting(struct device_node *clk_node, u32 *pllmode)
+{
+ const char *of_str;
+ int i;
+
+ for (i = 0; i < PLL_MAX_NUM; i++) {
+ if (of_property_read_string_index(clk_node, "nuvoton,pll-mode", i, &of_str))
+ return -EINVAL;
+ if (!strcmp(of_str, "integer"))
+ pllmode[i] = PLL_MODE_INT;
+ else if (!strcmp(of_str, "fractional"))
+ pllmode[i] = PLL_MODE_FRAC;
+ else if (!strcmp(of_str, "spread-spectrum"))
+ pllmode[i] = PLL_MODE_SS;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ma35d1_clocks_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *clk_node = pdev->dev.of_node;
+ void __iomem *clk_base;
+ static struct clk_hw **hws;
+ static struct clk_hw_onecell_data *ma35d1_hw_data;
+ u32 pllmode[PLL_MAX_NUM];
+ int ret;
+
+ ma35d1_hw_data = devm_kzalloc(dev,
+ struct_size(ma35d1_hw_data, hws, CLK_MAX_IDX),
+ GFP_KERNEL);
+ if (!ma35d1_hw_data)
+ return -ENOMEM;
+
+ ma35d1_hw_data->num = CLK_MAX_IDX;
+ hws = ma35d1_hw_data->hws;
+
+ clk_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(clk_base))
+ return PTR_ERR(clk_base);
+
+ ret = ma35d1_get_pll_setting(clk_node, pllmode);
+ if (ret < 0) {
+ dev_err(dev, "Invalid PLL setting!\n");
+ return -EINVAL;
+ }
+
+ hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
+ hws[HXT_GATE] = ma35d1_clk_gate(dev, "hxt_gate", "hxt",
+ clk_base + REG_CLK_PWRCTL, 0);
+ hws[LXT] = ma35d1_clk_fixed("lxt", 32768);
+ hws[LXT_GATE] = ma35d1_clk_gate(dev, "lxt_gate", "lxt",
+ clk_base + REG_CLK_PWRCTL, 1);
+ hws[HIRC] = ma35d1_clk_fixed("hirc", 12000000);
+ hws[HIRC_GATE] = ma35d1_clk_gate(dev, "hirc_gate", "hirc",
+ clk_base + REG_CLK_PWRCTL, 2);
+ hws[LIRC] = ma35d1_clk_fixed("lirc", 32000);
+ hws[LIRC_GATE] = ma35d1_clk_gate(dev, "lirc_gate", "lirc",
+ clk_base + REG_CLK_PWRCTL, 3);
+
+ hws[CAPLL] = ma35d1_reg_clk_pll(dev, CAPLL, pllmode[0], "capll",
+ hws[HXT], clk_base + REG_CLK_PLL0CTL0);
+ hws[SYSPLL] = ma35d1_clk_fixed("syspll", 180000000);
+ hws[DDRPLL] = ma35d1_reg_clk_pll(dev, DDRPLL, pllmode[1], "ddrpll",
+ hws[HXT], clk_base + REG_CLK_PLL2CTL0);
+ hws[APLL] = ma35d1_reg_clk_pll(dev, APLL, pllmode[2], "apll",
+ hws[HXT], clk_base + REG_CLK_PLL3CTL0);
+ hws[EPLL] = ma35d1_reg_clk_pll(dev, EPLL, pllmode[3], "epll",
+ hws[HXT], clk_base + REG_CLK_PLL4CTL0);
+ hws[VPLL] = ma35d1_reg_clk_pll(dev, VPLL, pllmode[4], "vpll",
+ hws[HXT], clk_base + REG_CLK_PLL5CTL0);
+
+ hws[EPLL_DIV2] = ma35d1_clk_fixed_factor(dev, "epll_div2", "epll", 1, 2);
+ hws[EPLL_DIV4] = ma35d1_clk_fixed_factor(dev, "epll_div4", "epll", 1, 4);
+ hws[EPLL_DIV8] = ma35d1_clk_fixed_factor(dev, "epll_div8", "epll", 1, 8);
+
+ hws[CA35CLK_MUX] = ma35d1_clk_mux_parent(dev, "ca35clk_mux",
+ clk_base + REG_CLK_CLKSEL0, 0, 2,
+ ca35clk_sel_clks,
+ ARRAY_SIZE(ca35clk_sel_clks));
+ hws[AXICLK_DIV2] = ma35d1_clk_fixed_factor(dev, "capll_div2", "ca35clk_mux", 1, 2);
+ hws[AXICLK_DIV4] = ma35d1_clk_fixed_factor(dev, "capll_div4", "ca35clk_mux", 1, 4);
+
+ hws[AXICLK_MUX] = ma35d1_clk_mux(dev, "axiclk_mux", clk_base + REG_CLK_CLKDIV0,
+ 26, 1, axiclk_sel_clks,
+ ARRAY_SIZE(axiclk_sel_clks));
+ hws[SYSCLK0_MUX] = ma35d1_clk_mux(dev, "sysclk0_mux", clk_base + REG_CLK_CLKSEL0,
+ 2, 1, sysclk0_sel_clks,
+ ARRAY_SIZE(sysclk0_sel_clks));
+ hws[SYSCLK1_MUX] = ma35d1_clk_mux(dev, "sysclk1_mux", clk_base + REG_CLK_CLKSEL0,
+ 4, 1, sysclk1_sel_clks,
+ ARRAY_SIZE(sysclk1_sel_clks));
+ hws[SYSCLK1_DIV2] = ma35d1_clk_fixed_factor(dev, "sysclk1_div2", "sysclk1_mux", 1, 2);
+
+ /* HCLK0~3 & PCLK0~4 */
+ hws[HCLK0] = ma35d1_clk_fixed_factor(dev, "hclk0", "sysclk1_mux", 1, 1);
+ hws[HCLK1] = ma35d1_clk_fixed_factor(dev, "hclk1", "sysclk1_mux", 1, 1);
+ hws[HCLK2] = ma35d1_clk_fixed_factor(dev, "hclk2", "sysclk1_mux", 1, 1);
+ hws[PCLK0] = ma35d1_clk_fixed_factor(dev, "pclk0", "sysclk1_mux", 1, 1);
+ hws[PCLK1] = ma35d1_clk_fixed_factor(dev, "pclk1", "sysclk1_mux", 1, 1);
+ hws[PCLK2] = ma35d1_clk_fixed_factor(dev, "pclk2", "sysclk1_mux", 1, 1);
+
+ hws[HCLK3] = ma35d1_clk_fixed_factor(dev, "hclk3", "sysclk1_mux", 1, 2);
+ hws[PCLK3] = ma35d1_clk_fixed_factor(dev, "pclk3", "sysclk1_mux", 1, 2);
+ hws[PCLK4] = ma35d1_clk_fixed_factor(dev, "pclk4", "sysclk1_mux", 1, 2);
+
+ hws[USBPHY0] = ma35d1_clk_fixed("usbphy0", 480000000);
+ hws[USBPHY1] = ma35d1_clk_fixed("usbphy1", 480000000);
+
+ /* DDR */
+ hws[DDR0_GATE] = ma35d1_clk_gate(dev, "ddr0_gate", "ddrpll",
+ clk_base + REG_CLK_SYSCLK0, 4);
+ hws[DDR6_GATE] = ma35d1_clk_gate(dev, "ddr6_gate", "ddrpll",
+ clk_base + REG_CLK_SYSCLK0, 5);
+
+ hws[CAN0_MUX] = ma35d1_clk_mux(dev, "can0_mux", clk_base + REG_CLK_CLKSEL4,
+ 16, 1, can_sel_clks, ARRAY_SIZE(can_sel_clks));
+ hws[CAN0_DIV] = ma35d1_clk_divider_table(dev, "can0_div", "can0_mux",
+ clk_base + REG_CLK_CLKDIV0,
+ 0, 3, ip_div_table);
+ hws[CAN0_GATE] = ma35d1_clk_gate(dev, "can0_gate", "can0_div",
+ clk_base + REG_CLK_SYSCLK0, 8);
+ hws[CAN1_MUX] = ma35d1_clk_mux(dev, "can1_mux", clk_base + REG_CLK_CLKSEL4,
+ 17, 1, can_sel_clks, ARRAY_SIZE(can_sel_clks));
+ hws[CAN1_DIV] = ma35d1_clk_divider_table(dev, "can1_div", "can1_mux",
+ clk_base + REG_CLK_CLKDIV0,
+ 4, 3, ip_div_table);
+ hws[CAN1_GATE] = ma35d1_clk_gate(dev, "can1_gate", "can1_div",
+ clk_base + REG_CLK_SYSCLK0, 9);
+ hws[CAN2_MUX] = ma35d1_clk_mux(dev, "can2_mux", clk_base + REG_CLK_CLKSEL4,
+ 18, 1, can_sel_clks, ARRAY_SIZE(can_sel_clks));
+ hws[CAN2_DIV] = ma35d1_clk_divider_table(dev, "can2_div", "can2_mux",
+ clk_base + REG_CLK_CLKDIV0,
+ 8, 3, ip_div_table);
+ hws[CAN2_GATE] = ma35d1_clk_gate(dev, "can2_gate", "can2_div",
+ clk_base + REG_CLK_SYSCLK0, 10);
+ hws[CAN3_MUX] = ma35d1_clk_mux(dev, "can3_mux", clk_base + REG_CLK_CLKSEL4,
+ 19, 1, can_sel_clks, ARRAY_SIZE(can_sel_clks));
+ hws[CAN3_DIV] = ma35d1_clk_divider_table(dev, "can3_div", "can3_mux",
+ clk_base + REG_CLK_CLKDIV0,
+ 12, 3, ip_div_table);
+ hws[CAN3_GATE] = ma35d1_clk_gate(dev, "can3_gate", "can3_div",
+ clk_base + REG_CLK_SYSCLK0, 11);
+
+ hws[SDH0_MUX] = ma35d1_clk_mux(dev, "sdh0_mux", clk_base + REG_CLK_CLKSEL0,
+ 16, 2, sdh_sel_clks, ARRAY_SIZE(sdh_sel_clks));
+ hws[SDH0_GATE] = ma35d1_clk_gate(dev, "sdh0_gate", "sdh0_mux",
+ clk_base + REG_CLK_SYSCLK0, 16);
+ hws[SDH1_MUX] = ma35d1_clk_mux(dev, "sdh1_mux", clk_base + REG_CLK_CLKSEL0,
+ 18, 2, sdh_sel_clks, ARRAY_SIZE(sdh_sel_clks));
+ hws[SDH1_GATE] = ma35d1_clk_gate(dev, "sdh1_gate", "sdh1_mux",
+ clk_base + REG_CLK_SYSCLK0, 17);
+
+ hws[NAND_GATE] = ma35d1_clk_gate(dev, "nand_gate", "hclk1",
+ clk_base + REG_CLK_SYSCLK0, 18);
+
+ hws[USBD_GATE] = ma35d1_clk_gate(dev, "usbd_gate", "usbphy0",
+ clk_base + REG_CLK_SYSCLK0, 19);
+ hws[USBH_GATE] = ma35d1_clk_gate(dev, "usbh_gate", "usbphy0",
+ clk_base + REG_CLK_SYSCLK0, 20);
+ hws[HUSBH0_GATE] = ma35d1_clk_gate(dev, "husbh0_gate", "usbphy0",
+ clk_base + REG_CLK_SYSCLK0, 21);
+ hws[HUSBH1_GATE] = ma35d1_clk_gate(dev, "husbh1_gate", "usbphy0",
+ clk_base + REG_CLK_SYSCLK0, 22);
+
+ hws[GFX_MUX] = ma35d1_clk_mux(dev, "gfx_mux", clk_base + REG_CLK_CLKSEL0,
+ 26, 1, gfx_sel_clks, ARRAY_SIZE(gfx_sel_clks));
+ hws[GFX_GATE] = ma35d1_clk_gate(dev, "gfx_gate", "gfx_mux",
+ clk_base + REG_CLK_SYSCLK0, 24);
+ hws[VC8K_GATE] = ma35d1_clk_gate(dev, "vc8k_gate", "sysclk0_mux",
+ clk_base + REG_CLK_SYSCLK0, 25);
+ hws[DCU_MUX] = ma35d1_clk_mux(dev, "dcu_mux", clk_base + REG_CLK_CLKSEL0,
+ 24, 1, dcu_sel_clks, ARRAY_SIZE(dcu_sel_clks));
+ hws[DCU_GATE] = ma35d1_clk_gate(dev, "dcu_gate", "dcu_mux",
+ clk_base + REG_CLK_SYSCLK0, 26);
+ hws[DCUP_DIV] = ma35d1_clk_divider_table(dev, "dcup_div", "vpll",
+ clk_base + REG_CLK_CLKDIV0,
+ 16, 3, ip_div_table);
+
+ hws[EMAC0_GATE] = ma35d1_clk_gate(dev, "emac0_gate", "epll_div2",
+ clk_base + REG_CLK_SYSCLK0, 27);
+ hws[EMAC1_GATE] = ma35d1_clk_gate(dev, "emac1_gate", "epll_div2",
+ clk_base + REG_CLK_SYSCLK0, 28);
+
+ hws[CCAP0_MUX] = ma35d1_clk_mux(dev, "ccap0_mux", clk_base + REG_CLK_CLKSEL0,
+ 12, 1, ccap_sel_clks, ARRAY_SIZE(ccap_sel_clks));
+ hws[CCAP0_DIV] = ma35d1_clk_divider(dev, "ccap0_div", "ccap0_mux",
+ clk_base + REG_CLK_CLKDIV1, 8, 4);
+ hws[CCAP0_GATE] = ma35d1_clk_gate(dev, "ccap0_gate", "ccap0_div",
+ clk_base + REG_CLK_SYSCLK0, 29);
+ hws[CCAP1_MUX] = ma35d1_clk_mux(dev, "ccap1_mux", clk_base + REG_CLK_CLKSEL0,
+ 14, 1, ccap_sel_clks, ARRAY_SIZE(ccap_sel_clks));
+ hws[CCAP1_DIV] = ma35d1_clk_divider(dev, "ccap1_div", "ccap1_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 12, 4);
+ hws[CCAP1_GATE] = ma35d1_clk_gate(dev, "ccap1_gate", "ccap1_div",
+ clk_base + REG_CLK_SYSCLK0, 30);
+
+ hws[PDMA0_GATE] = ma35d1_clk_gate(dev, "pdma0_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 0);
+ hws[PDMA1_GATE] = ma35d1_clk_gate(dev, "pdma1_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 1);
+ hws[PDMA2_GATE] = ma35d1_clk_gate(dev, "pdma2_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 2);
+ hws[PDMA3_GATE] = ma35d1_clk_gate(dev, "pdma3_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 3);
+
+ hws[WH0_GATE] = ma35d1_clk_gate(dev, "wh0_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 4);
+ hws[WH1_GATE] = ma35d1_clk_gate(dev, "wh1_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 5);
+
+ hws[HWS_GATE] = ma35d1_clk_gate(dev, "hws_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 6);
+
+ hws[EBI_GATE] = ma35d1_clk_gate(dev, "ebi_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 7);
+
+ hws[SRAM0_GATE] = ma35d1_clk_gate(dev, "sram0_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 8);
+ hws[SRAM1_GATE] = ma35d1_clk_gate(dev, "sram1_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 9);
+
+ hws[ROM_GATE] = ma35d1_clk_gate(dev, "rom_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 10);
+
+ hws[TRA_GATE] = ma35d1_clk_gate(dev, "tra_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 11);
+
+ hws[DBG_MUX] = ma35d1_clk_mux(dev, "dbg_mux", clk_base + REG_CLK_CLKSEL0,
+ 27, 1, dbg_sel_clks, ARRAY_SIZE(dbg_sel_clks));
+ hws[DBG_GATE] = ma35d1_clk_gate(dev, "dbg_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 12);
+
+ hws[CKO_MUX] = ma35d1_clk_mux(dev, "cko_mux", clk_base + REG_CLK_CLKSEL4,
+ 24, 4, cko_sel_clks, ARRAY_SIZE(cko_sel_clks));
+ hws[CKO_DIV] = ma35d1_clk_divider_pow2(dev, "cko_div", "cko_mux",
+ clk_base + REG_CLK_CLKOCTL, 0, 4);
+ hws[CKO_GATE] = ma35d1_clk_gate(dev, "cko_gate", "cko_div",
+ clk_base + REG_CLK_SYSCLK1, 13);
+
+ hws[GTMR_GATE] = ma35d1_clk_gate(dev, "gtmr_gate", "hirc",
+ clk_base + REG_CLK_SYSCLK1, 14);
+
+ hws[GPA_GATE] = ma35d1_clk_gate(dev, "gpa_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 16);
+ hws[GPB_GATE] = ma35d1_clk_gate(dev, "gpb_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 17);
+ hws[GPC_GATE] = ma35d1_clk_gate(dev, "gpc_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 18);
+ hws[GPD_GATE] = ma35d1_clk_gate(dev, "gpd_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 19);
+ hws[GPE_GATE] = ma35d1_clk_gate(dev, "gpe_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 20);
+ hws[GPF_GATE] = ma35d1_clk_gate(dev, "gpf_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 21);
+ hws[GPG_GATE] = ma35d1_clk_gate(dev, "gpg_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 22);
+ hws[GPH_GATE] = ma35d1_clk_gate(dev, "gph_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 23);
+ hws[GPI_GATE] = ma35d1_clk_gate(dev, "gpi_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 24);
+ hws[GPJ_GATE] = ma35d1_clk_gate(dev, "gpj_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 25);
+ hws[GPK_GATE] = ma35d1_clk_gate(dev, "gpk_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 26);
+ hws[GPL_GATE] = ma35d1_clk_gate(dev, "gpl_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 27);
+ hws[GPM_GATE] = ma35d1_clk_gate(dev, "gpm_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 28);
+ hws[GPN_GATE] = ma35d1_clk_gate(dev, "gpn_gate", "hclk0",
+ clk_base + REG_CLK_SYSCLK1, 29);
+
+ hws[TMR0_MUX] = ma35d1_clk_mux(dev, "tmr0_mux", clk_base + REG_CLK_CLKSEL1,
+ 0, 3, timer0_sel_clks,
+ ARRAY_SIZE(timer0_sel_clks));
+ hws[TMR0_GATE] = ma35d1_clk_gate(dev, "tmr0_gate", "tmr0_mux",
+ clk_base + REG_CLK_APBCLK0, 0);
+ hws[TMR1_MUX] = ma35d1_clk_mux(dev, "tmr1_mux", clk_base + REG_CLK_CLKSEL1,
+ 4, 3, timer1_sel_clks,
+ ARRAY_SIZE(timer1_sel_clks));
+ hws[TMR1_GATE] = ma35d1_clk_gate(dev, "tmr1_gate", "tmr1_mux",
+ clk_base + REG_CLK_APBCLK0, 1);
+ hws[TMR2_MUX] = ma35d1_clk_mux(dev, "tmr2_mux", clk_base + REG_CLK_CLKSEL1,
+ 8, 3, timer2_sel_clks,
+ ARRAY_SIZE(timer2_sel_clks));
+ hws[TMR2_GATE] = ma35d1_clk_gate(dev, "tmr2_gate", "tmr2_mux",
+ clk_base + REG_CLK_APBCLK0, 2);
+ hws[TMR3_MUX] = ma35d1_clk_mux(dev, "tmr3_mux", clk_base + REG_CLK_CLKSEL1,
+ 12, 3, timer3_sel_clks,
+ ARRAY_SIZE(timer3_sel_clks));
+ hws[TMR3_GATE] = ma35d1_clk_gate(dev, "tmr3_gate", "tmr3_mux",
+ clk_base + REG_CLK_APBCLK0, 3);
+ hws[TMR4_MUX] = ma35d1_clk_mux(dev, "tmr4_mux", clk_base + REG_CLK_CLKSEL1,
+ 16, 3, timer4_sel_clks,
+ ARRAY_SIZE(timer4_sel_clks));
+ hws[TMR4_GATE] = ma35d1_clk_gate(dev, "tmr4_gate", "tmr4_mux",
+ clk_base + REG_CLK_APBCLK0, 4);
+ hws[TMR5_MUX] = ma35d1_clk_mux(dev, "tmr5_mux", clk_base + REG_CLK_CLKSEL1,
+ 20, 3, timer5_sel_clks,
+ ARRAY_SIZE(timer5_sel_clks));
+ hws[TMR5_GATE] = ma35d1_clk_gate(dev, "tmr5_gate", "tmr5_mux",
+ clk_base + REG_CLK_APBCLK0, 5);
+ hws[TMR6_MUX] = ma35d1_clk_mux(dev, "tmr6_mux", clk_base + REG_CLK_CLKSEL1,
+ 24, 3, timer6_sel_clks,
+ ARRAY_SIZE(timer6_sel_clks));
+ hws[TMR6_GATE] = ma35d1_clk_gate(dev, "tmr6_gate", "tmr6_mux",
+ clk_base + REG_CLK_APBCLK0, 6);
+ hws[TMR7_MUX] = ma35d1_clk_mux(dev, "tmr7_mux", clk_base + REG_CLK_CLKSEL1,
+ 28, 3, timer7_sel_clks,
+ ARRAY_SIZE(timer7_sel_clks));
+ hws[TMR7_GATE] = ma35d1_clk_gate(dev, "tmr7_gate", "tmr7_mux",
+ clk_base + REG_CLK_APBCLK0, 7);
+ hws[TMR8_MUX] = ma35d1_clk_mux(dev, "tmr8_mux", clk_base + REG_CLK_CLKSEL2,
+ 0, 3, timer8_sel_clks,
+ ARRAY_SIZE(timer8_sel_clks));
+ hws[TMR8_GATE] = ma35d1_clk_gate(dev, "tmr8_gate", "tmr8_mux",
+ clk_base + REG_CLK_APBCLK0, 8);
+ hws[TMR9_MUX] = ma35d1_clk_mux(dev, "tmr9_mux", clk_base + REG_CLK_CLKSEL2,
+ 4, 3, timer9_sel_clks,
+ ARRAY_SIZE(timer9_sel_clks));
+ hws[TMR9_GATE] = ma35d1_clk_gate(dev, "tmr9_gate", "tmr9_mux",
+ clk_base + REG_CLK_APBCLK0, 9);
+ hws[TMR10_MUX] = ma35d1_clk_mux(dev, "tmr10_mux", clk_base + REG_CLK_CLKSEL2,
+ 8, 3, timer10_sel_clks,
+ ARRAY_SIZE(timer10_sel_clks));
+ hws[TMR10_GATE] = ma35d1_clk_gate(dev, "tmr10_gate", "tmr10_mux",
+ clk_base + REG_CLK_APBCLK0, 10);
+ hws[TMR11_MUX] = ma35d1_clk_mux(dev, "tmr11_mux", clk_base + REG_CLK_CLKSEL2,
+ 12, 3, timer11_sel_clks,
+ ARRAY_SIZE(timer11_sel_clks));
+ hws[TMR11_GATE] = ma35d1_clk_gate(dev, "tmr11_gate", "tmr11_mux",
+ clk_base + REG_CLK_APBCLK0, 11);
+
+ hws[UART0_MUX] = ma35d1_clk_mux(dev, "uart0_mux", clk_base + REG_CLK_CLKSEL2,
+ 16, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART0_DIV] = ma35d1_clk_divider(dev, "uart0_div", "uart0_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 16, 4);
+ hws[UART0_GATE] = ma35d1_clk_gate(dev, "uart0_gate", "uart0_div",
+ clk_base + REG_CLK_APBCLK0, 12);
+ hws[UART1_MUX] = ma35d1_clk_mux(dev, "uart1_mux", clk_base + REG_CLK_CLKSEL2,
+ 18, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART1_DIV] = ma35d1_clk_divider(dev, "uart1_div", "uart1_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 20, 4);
+ hws[UART1_GATE] = ma35d1_clk_gate(dev, "uart1_gate", "uart1_div",
+ clk_base + REG_CLK_APBCLK0, 13);
+ hws[UART2_MUX] = ma35d1_clk_mux(dev, "uart2_mux", clk_base + REG_CLK_CLKSEL2,
+ 20, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART2_DIV] = ma35d1_clk_divider(dev, "uart2_div", "uart2_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 24, 4);
+ hws[UART2_GATE] = ma35d1_clk_gate(dev, "uart2_gate", "uart2_div",
+ clk_base + REG_CLK_APBCLK0, 14);
+ hws[UART3_MUX] = ma35d1_clk_mux(dev, "uart3_mux", clk_base + REG_CLK_CLKSEL2,
+ 22, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART3_DIV] = ma35d1_clk_divider(dev, "uart3_div", "uart3_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 28, 4);
+ hws[UART3_GATE] = ma35d1_clk_gate(dev, "uart3_gate", "uart3_div",
+ clk_base + REG_CLK_APBCLK0, 15);
+ hws[UART4_MUX] = ma35d1_clk_mux(dev, "uart4_mux", clk_base + REG_CLK_CLKSEL2,
+ 24, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART4_DIV] = ma35d1_clk_divider(dev, "uart4_div", "uart4_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 0, 4);
+ hws[UART4_GATE] = ma35d1_clk_gate(dev, "uart4_gate", "uart4_div",
+ clk_base + REG_CLK_APBCLK0, 16);
+ hws[UART5_MUX] = ma35d1_clk_mux(dev, "uart5_mux", clk_base + REG_CLK_CLKSEL2,
+ 26, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART5_DIV] = ma35d1_clk_divider(dev, "uart5_div", "uart5_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 4, 4);
+ hws[UART5_GATE] = ma35d1_clk_gate(dev, "uart5_gate", "uart5_div",
+ clk_base + REG_CLK_APBCLK0, 17);
+ hws[UART6_MUX] = ma35d1_clk_mux(dev, "uart6_mux", clk_base + REG_CLK_CLKSEL2,
+ 28, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART6_DIV] = ma35d1_clk_divider(dev, "uart6_div", "uart6_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 8, 4);
+ hws[UART6_GATE] = ma35d1_clk_gate(dev, "uart6_gate", "uart6_div",
+ clk_base + REG_CLK_APBCLK0, 18);
+ hws[UART7_MUX] = ma35d1_clk_mux(dev, "uart7_mux", clk_base + REG_CLK_CLKSEL2,
+ 30, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART7_DIV] = ma35d1_clk_divider(dev, "uart7_div", "uart7_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 12, 4);
+ hws[UART7_GATE] = ma35d1_clk_gate(dev, "uart7_gate", "uart7_div",
+ clk_base + REG_CLK_APBCLK0, 19);
+ hws[UART8_MUX] = ma35d1_clk_mux(dev, "uart8_mux", clk_base + REG_CLK_CLKSEL3,
+ 0, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART8_DIV] = ma35d1_clk_divider(dev, "uart8_div", "uart8_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 16, 4);
+ hws[UART8_GATE] = ma35d1_clk_gate(dev, "uart8_gate", "uart8_div",
+ clk_base + REG_CLK_APBCLK0, 20);
+ hws[UART9_MUX] = ma35d1_clk_mux(dev, "uart9_mux", clk_base + REG_CLK_CLKSEL3,
+ 2, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART9_DIV] = ma35d1_clk_divider(dev, "uart9_div", "uart9_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 20, 4);
+ hws[UART9_GATE] = ma35d1_clk_gate(dev, "uart9_gate", "uart9_div",
+ clk_base + REG_CLK_APBCLK0, 21);
+ hws[UART10_MUX] = ma35d1_clk_mux(dev, "uart10_mux", clk_base + REG_CLK_CLKSEL3,
+ 4, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART10_DIV] = ma35d1_clk_divider(dev, "uart10_div", "uart10_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 24, 4);
+ hws[UART10_GATE] = ma35d1_clk_gate(dev, "uart10_gate", "uart10_div",
+ clk_base + REG_CLK_APBCLK0, 22);
+ hws[UART11_MUX] = ma35d1_clk_mux(dev, "uart11_mux", clk_base + REG_CLK_CLKSEL3,
+ 6, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART11_DIV] = ma35d1_clk_divider(dev, "uart11_div", "uart11_mux",
+ clk_base + REG_CLK_CLKDIV2,
+ 28, 4);
+ hws[UART11_GATE] = ma35d1_clk_gate(dev, "uart11_gate", "uart11_div",
+ clk_base + REG_CLK_APBCLK0, 23);
+ hws[UART12_MUX] = ma35d1_clk_mux(dev, "uart12_mux", clk_base + REG_CLK_CLKSEL3,
+ 8, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART12_DIV] = ma35d1_clk_divider(dev, "uart12_div", "uart12_mux",
+ clk_base + REG_CLK_CLKDIV3,
+ 0, 4);
+ hws[UART12_GATE] = ma35d1_clk_gate(dev, "uart12_gate", "uart12_div",
+ clk_base + REG_CLK_APBCLK0, 24);
+ hws[UART13_MUX] = ma35d1_clk_mux(dev, "uart13_mux", clk_base + REG_CLK_CLKSEL3,
+ 10, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART13_DIV] = ma35d1_clk_divider(dev, "uart13_div", "uart13_mux",
+ clk_base + REG_CLK_CLKDIV3,
+ 4, 4);
+ hws[UART13_GATE] = ma35d1_clk_gate(dev, "uart13_gate", "uart13_div",
+ clk_base + REG_CLK_APBCLK0, 25);
+ hws[UART14_MUX] = ma35d1_clk_mux(dev, "uart14_mux", clk_base + REG_CLK_CLKSEL3,
+ 12, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART14_DIV] = ma35d1_clk_divider(dev, "uart14_div", "uart14_mux",
+ clk_base + REG_CLK_CLKDIV3,
+ 8, 4);
+ hws[UART14_GATE] = ma35d1_clk_gate(dev, "uart14_gate", "uart14_div",
+ clk_base + REG_CLK_APBCLK0, 26);
+ hws[UART15_MUX] = ma35d1_clk_mux(dev, "uart15_mux", clk_base + REG_CLK_CLKSEL3,
+ 14, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART15_DIV] = ma35d1_clk_divider(dev, "uart15_div", "uart15_mux",
+ clk_base + REG_CLK_CLKDIV3,
+ 12, 4);
+ hws[UART15_GATE] = ma35d1_clk_gate(dev, "uart15_gate", "uart15_div",
+ clk_base + REG_CLK_APBCLK0, 27);
+ hws[UART16_MUX] = ma35d1_clk_mux(dev, "uart16_mux", clk_base + REG_CLK_CLKSEL3,
+ 16, 2, uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+ hws[UART16_DIV] = ma35d1_clk_divider(dev, "uart16_div", "uart16_mux",
+ clk_base + REG_CLK_CLKDIV3,
+ 16, 4);
+ hws[UART16_GATE] = ma35d1_clk_gate(dev, "uart16_gate", "uart16_div",
+ clk_base + REG_CLK_APBCLK0, 28);
+
+ hws[RTC_GATE] = ma35d1_clk_gate(dev, "rtc_gate", "lxt",
+ clk_base + REG_CLK_APBCLK0, 29);
+ hws[DDR_GATE] = ma35d1_clk_gate(dev, "ddr_gate", "ddrpll",
+ clk_base + REG_CLK_APBCLK0, 30);
+
+ hws[KPI_MUX] = ma35d1_clk_mux(dev, "kpi_mux", clk_base + REG_CLK_CLKSEL4,
+ 30, 1, kpi_sel_clks, ARRAY_SIZE(kpi_sel_clks));
+ hws[KPI_DIV] = ma35d1_clk_divider(dev, "kpi_div", "kpi_mux",
+ clk_base + REG_CLK_CLKDIV4,
+ 24, 8);
+ hws[KPI_GATE] = ma35d1_clk_gate(dev, "kpi_gate", "kpi_div",
+ clk_base + REG_CLK_APBCLK0, 31);
+
+ hws[I2C0_GATE] = ma35d1_clk_gate(dev, "i2c0_gate", "pclk0",
+ clk_base + REG_CLK_APBCLK1, 0);
+ hws[I2C1_GATE] = ma35d1_clk_gate(dev, "i2c1_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK1, 1);
+ hws[I2C2_GATE] = ma35d1_clk_gate(dev, "i2c2_gate", "pclk2",
+ clk_base + REG_CLK_APBCLK1, 2);
+ hws[I2C3_GATE] = ma35d1_clk_gate(dev, "i2c3_gate", "pclk0",
+ clk_base + REG_CLK_APBCLK1, 3);
+ hws[I2C4_GATE] = ma35d1_clk_gate(dev, "i2c4_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK1, 4);
+ hws[I2C5_GATE] = ma35d1_clk_gate(dev, "i2c5_gate", "pclk2",
+ clk_base + REG_CLK_APBCLK1, 5);
+
+ hws[QSPI0_MUX] = ma35d1_clk_mux(dev, "qspi0_mux", clk_base + REG_CLK_CLKSEL4,
+ 8, 2, qspi0_sel_clks, ARRAY_SIZE(qspi0_sel_clks));
+ hws[QSPI0_GATE] = ma35d1_clk_gate(dev, "qspi0_gate", "qspi0_mux",
+ clk_base + REG_CLK_APBCLK1, 6);
+ hws[QSPI1_MUX] = ma35d1_clk_mux(dev, "qspi1_mux", clk_base + REG_CLK_CLKSEL4,
+ 10, 2, qspi1_sel_clks, ARRAY_SIZE(qspi1_sel_clks));
+ hws[QSPI1_GATE] = ma35d1_clk_gate(dev, "qspi1_gate", "qspi1_mux",
+ clk_base + REG_CLK_APBCLK1, 7);
+
+ hws[SMC0_MUX] = ma35d1_clk_mux(dev, "smc0_mux", clk_base + REG_CLK_CLKSEL4,
+ 28, 1, smc_sel_clks, ARRAY_SIZE(smc_sel_clks));
+ hws[SMC0_DIV] = ma35d1_clk_divider(dev, "smc0_div", "smc0_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 0, 4);
+ hws[SMC0_GATE] = ma35d1_clk_gate(dev, "smc0_gate", "smc0_div",
+ clk_base + REG_CLK_APBCLK1, 12);
+ hws[SMC1_MUX] = ma35d1_clk_mux(dev, "smc1_mux", clk_base + REG_CLK_CLKSEL4,
+ 29, 1, smc_sel_clks, ARRAY_SIZE(smc_sel_clks));
+ hws[SMC1_DIV] = ma35d1_clk_divider(dev, "smc1_div", "smc1_mux",
+ clk_base + REG_CLK_CLKDIV1,
+ 4, 4);
+ hws[SMC1_GATE] = ma35d1_clk_gate(dev, "smc1_gate", "smc1_div",
+ clk_base + REG_CLK_APBCLK1, 13);
+
+ hws[WDT0_MUX] = ma35d1_clk_mux(dev, "wdt0_mux", clk_base + REG_CLK_CLKSEL3,
+ 20, 2, wdt0_sel_clks, ARRAY_SIZE(wdt0_sel_clks));
+ hws[WDT0_GATE] = ma35d1_clk_gate(dev, "wdt0_gate", "wdt0_mux",
+ clk_base + REG_CLK_APBCLK1, 16);
+ hws[WDT1_MUX] = ma35d1_clk_mux(dev, "wdt1_mux", clk_base + REG_CLK_CLKSEL3,
+ 24, 2, wdt1_sel_clks, ARRAY_SIZE(wdt1_sel_clks));
+ hws[WDT1_GATE] = ma35d1_clk_gate(dev, "wdt1_gate", "wdt1_mux",
+ clk_base + REG_CLK_APBCLK1, 17);
+ hws[WDT2_MUX] = ma35d1_clk_mux(dev, "wdt2_mux", clk_base + REG_CLK_CLKSEL3,
+ 28, 2, wdt2_sel_clks, ARRAY_SIZE(wdt2_sel_clks));
+ hws[WDT2_GATE] = ma35d1_clk_gate(dev, "wdt2_gate", "wdt2_mux",
+ clk_base + REG_CLK_APBCLK1, 18);
+
+ hws[WWDT0_MUX] = ma35d1_clk_mux(dev, "wwdt0_mux", clk_base + REG_CLK_CLKSEL3,
+ 22, 2, wwdt0_sel_clks, ARRAY_SIZE(wwdt0_sel_clks));
+ hws[WWDT1_MUX] = ma35d1_clk_mux(dev, "wwdt1_mux", clk_base + REG_CLK_CLKSEL3,
+ 26, 2, wwdt1_sel_clks, ARRAY_SIZE(wwdt1_sel_clks));
+ hws[WWDT2_MUX] = ma35d1_clk_mux(dev, "wwdt2_mux", clk_base + REG_CLK_CLKSEL3,
+ 30, 2, wwdt2_sel_clks, ARRAY_SIZE(wwdt2_sel_clks));
+
+ hws[EPWM0_GATE] = ma35d1_clk_gate(dev, "epwm0_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK1, 24);
+ hws[EPWM1_GATE] = ma35d1_clk_gate(dev, "epwm1_gate", "pclk2",
+ clk_base + REG_CLK_APBCLK1, 25);
+ hws[EPWM2_GATE] = ma35d1_clk_gate(dev, "epwm2_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK1, 26);
+
+ hws[I2S0_MUX] = ma35d1_clk_mux(dev, "i2s0_mux", clk_base + REG_CLK_CLKSEL4,
+ 12, 2, i2s0_sel_clks, ARRAY_SIZE(i2s0_sel_clks));
+ hws[I2S0_GATE] = ma35d1_clk_gate(dev, "i2s0_gate", "i2s0_mux",
+ clk_base + REG_CLK_APBCLK2, 0);
+ hws[I2S1_MUX] = ma35d1_clk_mux(dev, "i2s1_mux", clk_base + REG_CLK_CLKSEL4,
+ 14, 2, i2s1_sel_clks, ARRAY_SIZE(i2s1_sel_clks));
+ hws[I2S1_GATE] = ma35d1_clk_gate(dev, "i2s1_gate", "i2s1_mux",
+ clk_base + REG_CLK_APBCLK2, 1);
+
+ hws[SSMCC_GATE] = ma35d1_clk_gate(dev, "ssmcc_gate", "pclk3",
+ clk_base + REG_CLK_APBCLK2, 2);
+ hws[SSPCC_GATE] = ma35d1_clk_gate(dev, "sspcc_gate", "pclk3",
+ clk_base + REG_CLK_APBCLK2, 3);
+
+ hws[SPI0_MUX] = ma35d1_clk_mux(dev, "spi0_mux", clk_base + REG_CLK_CLKSEL4,
+ 0, 2, spi0_sel_clks, ARRAY_SIZE(spi0_sel_clks));
+ hws[SPI0_GATE] = ma35d1_clk_gate(dev, "spi0_gate", "spi0_mux",
+ clk_base + REG_CLK_APBCLK2, 4);
+ hws[SPI1_MUX] = ma35d1_clk_mux(dev, "spi1_mux", clk_base + REG_CLK_CLKSEL4,
+ 2, 2, spi1_sel_clks, ARRAY_SIZE(spi1_sel_clks));
+ hws[SPI1_GATE] = ma35d1_clk_gate(dev, "spi1_gate", "spi1_mux",
+ clk_base + REG_CLK_APBCLK2, 5);
+ hws[SPI2_MUX] = ma35d1_clk_mux(dev, "spi2_mux", clk_base + REG_CLK_CLKSEL4,
+ 4, 2, spi2_sel_clks, ARRAY_SIZE(spi2_sel_clks));
+ hws[SPI2_GATE] = ma35d1_clk_gate(dev, "spi2_gate", "spi2_mux",
+ clk_base + REG_CLK_APBCLK2, 6);
+ hws[SPI3_MUX] = ma35d1_clk_mux(dev, "spi3_mux", clk_base + REG_CLK_CLKSEL4,
+ 6, 2, spi3_sel_clks, ARRAY_SIZE(spi3_sel_clks));
+ hws[SPI3_GATE] = ma35d1_clk_gate(dev, "spi3_gate", "spi3_mux",
+ clk_base + REG_CLK_APBCLK2, 7);
+
+ hws[ECAP0_GATE] = ma35d1_clk_gate(dev, "ecap0_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK2, 8);
+ hws[ECAP1_GATE] = ma35d1_clk_gate(dev, "ecap1_gate", "pclk2",
+ clk_base + REG_CLK_APBCLK2, 9);
+ hws[ECAP2_GATE] = ma35d1_clk_gate(dev, "ecap2_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK2, 10);
+
+ hws[QEI0_GATE] = ma35d1_clk_gate(dev, "qei0_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK2, 12);
+ hws[QEI1_GATE] = ma35d1_clk_gate(dev, "qei1_gate", "pclk2",
+ clk_base + REG_CLK_APBCLK2, 13);
+ hws[QEI2_GATE] = ma35d1_clk_gate(dev, "qei2_gate", "pclk1",
+ clk_base + REG_CLK_APBCLK2, 14);
+
+ hws[ADC_DIV] = ma35d1_reg_adc_clkdiv(dev, "adc_div", hws[PCLK0],
+ &ma35d1_lock, 0,
+ clk_base + REG_CLK_CLKDIV4,
+ 4, 17, 0x1ffff);
+ hws[ADC_GATE] = ma35d1_clk_gate(dev, "adc_gate", "adc_div",
+ clk_base + REG_CLK_APBCLK2, 24);
+
+ hws[EADC_DIV] = ma35d1_clk_divider_table(dev, "eadc_div", "pclk2",
+ clk_base + REG_CLK_CLKDIV4,
+ 0, 4, eadc_div_table);
+ hws[EADC_GATE] = ma35d1_clk_gate(dev, "eadc_gate", "eadc_div",
+ clk_base + REG_CLK_APBCLK2, 25);
+
+ return devm_of_clk_add_hw_provider(dev,
+ of_clk_hw_onecell_get,
+ ma35d1_hw_data);
+}
+
+static const struct of_device_id ma35d1_clk_of_match[] = {
+ { .compatible = "nuvoton,ma35d1-clk" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ma35d1_clk_of_match);
+
+static struct platform_driver ma35d1_clk_driver = {
+ .probe = ma35d1_clocks_probe,
+ .driver = {
+ .name = "ma35d1-clk",
+ .of_match_table = ma35d1_clk_of_match,
+ },
+};
+
+static int __init ma35d1_clocks_init(void)
+{
+ return platform_driver_register(&ma35d1_clk_driver);
+}
+
+postcore_initcall(ma35d1_clocks_init);
+
+MODULE_AUTHOR("Chi-Fang Li <cfli0@nuvoton.com>");
+MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
new file mode 100644
index 000000000000..3adee440f60a
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#ifndef __DRV_CLK_NUVOTON_MA35D1_H
+#define __DRV_CLK_NUVOTON_MA35D1_H
+
+struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
+ struct clk_hw *parent_hw, void __iomem *base);
+
+struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
+ struct clk_hw *parent_hw, spinlock_t *lock,
+ unsigned long flags, void __iomem *reg,
+ u8 shift, u8 width, u32 mask_bit);
+
+#endif /* __DRV_CLK_NUVOTON_MA35D1_H */
diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c
index ddb28b38f549..751b786d73f8 100644
--- a/drivers/clk/nxp/clk-lpc18xx-ccu.c
+++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c
@@ -148,7 +148,7 @@ static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable)
val |= LPC18XX_CCU_RUN;
} else {
/*
- * To safely disable a branch clock a squence of two separate
+ * To safely disable a branch clock a sequence of two separate
* writes must be used. First write should set the AUTO bit
* and the next write should clear the RUN bit.
*/
diff --git a/drivers/clk/nxp/clk-lpc18xx-cgu.c b/drivers/clk/nxp/clk-lpc18xx-cgu.c
index f253ef1996b1..b9e204d63a97 100644
--- a/drivers/clk/nxp/clk-lpc18xx-cgu.c
+++ b/drivers/clk/nxp/clk-lpc18xx-cgu.c
@@ -250,7 +250,6 @@ static struct lpc18xx_cgu_base_clk lpc18xx_cgu_base_clks[] = {
struct lpc18xx_pll {
struct clk_hw hw;
void __iomem *reg;
- spinlock_t *lock;
u8 flags;
};
@@ -371,23 +370,25 @@ static unsigned long lpc18xx_pll0_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long lpc18xx_pll0_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int lpc18xx_pll0_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long m;
- if (*prate < rate) {
+ if (req->best_parent_rate < req->rate) {
pr_warn("%s: pll dividers not supported\n", __func__);
return -EINVAL;
}
- m = DIV_ROUND_UP_ULL(*prate, rate * 2);
- if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) {
- pr_warn("%s: unable to support rate %lu\n", __func__, rate);
+ m = DIV_ROUND_UP_ULL(req->best_parent_rate, req->rate * 2);
+ if (m == 0 || m > LPC18XX_PLL0_MSEL_MAX) {
+ pr_warn("%s: unable to support rate %lu\n", __func__, req->rate);
return -EINVAL;
}
- return 2 * *prate * m;
+ req->rate = 2 * req->best_parent_rate * m;
+
+ return 0;
}
static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -403,7 +404,7 @@ static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate,
}
m = DIV_ROUND_UP_ULL(parent_rate, rate * 2);
- if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) {
+ if (m == 0 || m > LPC18XX_PLL0_MSEL_MAX) {
pr_warn("%s: unable to support rate %lu\n", __func__, rate);
return -EINVAL;
}
@@ -444,7 +445,7 @@ static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops lpc18xx_pll0_ops = {
.recalc_rate = lpc18xx_pll0_recalc_rate,
- .round_rate = lpc18xx_pll0_round_rate,
+ .determine_rate = lpc18xx_pll0_determine_rate,
.set_rate = lpc18xx_pll0_set_rate,
};
@@ -606,7 +607,7 @@ static void __init lpc18xx_cgu_register_source_clks(struct device_node *np,
if (IS_ERR(clk))
pr_warn("%s: failed to register irc clk\n", __func__);
- /* Register crystal oscillator controlller */
+ /* Register crystal oscillator controller */
parents[0] = of_clk_get_parent_name(np, 0);
clk = clk_register_gate(NULL, clk_src_names[CLK_SRC_OSC], parents[0],
0, base + LPC18XX_CGU_XTAL_OSC_CTRL,
diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c
index d0f870eff0d6..23f980cf6a2b 100644
--- a/drivers/clk/nxp/clk-lpc32xx.c
+++ b/drivers/clk/nxp/clk-lpc32xx.c
@@ -61,14 +61,13 @@
#define LPC32XX_USB_CLK_CTRL 0xF4
#define LPC32XX_USB_CLK_STS 0xF8
-static struct regmap_config lpc32xx_scb_regmap_config = {
+static const struct regmap_config lpc32xx_scb_regmap_config = {
.name = "scb",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
.max_register = 0x114,
- .fast_io = true,
};
static struct regmap *clk_regmap;
@@ -579,17 +578,17 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return regmap_update_bits(clk_regmap, clk->reg, 0x1FFFF, val);
}
-static long clk_hclk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_hclk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct lpc32xx_pll_clk *clk = to_lpc32xx_pll_clk(hw);
- u64 m_i, o = rate, i = *parent_rate, d = (u64)rate << 6;
+ u64 m_i, o = req->rate, i = req->best_parent_rate, d = (u64)req->rate << 6;
u64 m = 0, n = 0, p = 0;
int p_i, n_i;
- pr_debug("%s: %lu/%lu\n", clk_hw_get_name(hw), *parent_rate, rate);
+ pr_debug("%s: %lu/%lu\n", clk_hw_get_name(hw), req->best_parent_rate, req->rate);
- if (rate > 266500000)
+ if (req->rate > 266500000)
return -EINVAL;
/* Have to check all 20 possibilities to find the minimal M */
@@ -614,9 +613,9 @@ static long clk_hclk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
}
}
- if (d == (u64)rate << 6) {
+ if (d == (u64)req->rate << 6) {
pr_err("%s: %lu: no valid PLL parameters are found\n",
- clk_hw_get_name(hw), rate);
+ clk_hw_get_name(hw), req->rate);
return -EINVAL;
}
@@ -634,22 +633,25 @@ static long clk_hclk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
if (!d)
pr_debug("%s: %lu: found exact match: %llu/%llu/%llu\n",
- clk_hw_get_name(hw), rate, m, n, p);
+ clk_hw_get_name(hw), req->rate, m, n, p);
else
pr_debug("%s: %lu: found closest: %llu/%llu/%llu - %llu\n",
- clk_hw_get_name(hw), rate, m, n, p, o);
+ clk_hw_get_name(hw), req->rate, m, n, p, o);
- return o;
+ req->rate = o;
+
+ return 0;
}
-static long clk_usb_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_usb_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct lpc32xx_pll_clk *clk = to_lpc32xx_pll_clk(hw);
struct clk_hw *usb_div_hw, *osc_hw;
u64 d_i, n_i, m, o;
- pr_debug("%s: %lu/%lu\n", clk_hw_get_name(hw), *parent_rate, rate);
+ pr_debug("%s: %lu/%lu\n", clk_hw_get_name(hw), req->best_parent_rate,
+ req->rate);
/*
* The only supported USB clock is 48MHz, with PLL internal constraints
@@ -657,7 +659,7 @@ static long clk_usb_pll_round_rate(struct clk_hw *hw, unsigned long rate,
* and post-divider must be 4, this slightly simplifies calculation of
* USB divider, USB PLL N and M parameters.
*/
- if (rate != 48000000)
+ if (req->rate != 48000000)
return -EINVAL;
/* USB divider clock */
@@ -685,30 +687,30 @@ static long clk_usb_pll_round_rate(struct clk_hw *hw, unsigned long rate,
clk->m_div = m;
clk->p_div = 2;
clk->mode = PLL_NON_INTEGER;
- *parent_rate = div64_u64(o, d_i);
+ req->best_parent_rate = div64_u64(o, d_i);
- return rate;
+ return 0;
}
}
return -EINVAL;
}
-#define LPC32XX_DEFINE_PLL_OPS(_name, _rc, _sr, _rr) \
+#define LPC32XX_DEFINE_PLL_OPS(_name, _rc, _sr, _dr) \
static const struct clk_ops clk_ ##_name ## _ops = { \
.enable = clk_pll_enable, \
.disable = clk_pll_disable, \
.is_enabled = clk_pll_is_enabled, \
.recalc_rate = _rc, \
.set_rate = _sr, \
- .round_rate = _rr, \
+ .determine_rate = _dr, \
}
LPC32XX_DEFINE_PLL_OPS(pll_397x, clk_pll_397x_recalc_rate, NULL, NULL);
LPC32XX_DEFINE_PLL_OPS(hclk_pll, clk_pll_recalc_rate,
- clk_pll_set_rate, clk_hclk_pll_round_rate);
+ clk_pll_set_rate, clk_hclk_pll_determine_rate);
LPC32XX_DEFINE_PLL_OPS(usb_pll, clk_pll_recalc_rate,
- clk_pll_set_rate, clk_usb_pll_round_rate);
+ clk_pll_set_rate, clk_usb_pll_determine_rate);
static int clk_ddram_is_enabled(struct clk_hw *hw)
{
@@ -955,8 +957,8 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
divider->flags, divider->width);
}
-static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct lpc32xx_clk_div *divider = to_lpc32xx_div(hw);
unsigned int bestdiv;
@@ -968,11 +970,15 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
divider->width);
- return DIV_ROUND_UP(*prate, bestdiv);
+ req->rate = DIV_ROUND_UP(req->best_parent_rate, bestdiv);
+
+ return 0;
}
- return divider_round_rate(hw, rate, prate, divider->table,
- divider->width, divider->flags);
+ req->rate = divider_round_rate(hw, req->rate, &req->best_parent_rate,
+ divider->table, divider->width, divider->flags);
+
+ return 0;
}
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -991,7 +997,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops lpc32xx_clk_divider_ops = {
.recalc_rate = clk_divider_recalc_rate,
- .round_rate = clk_divider_round_rate,
+ .determine_rate = clk_divider_determine_rate,
.set_rate = clk_divider_set_rate,
};
diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c
index 025b9df76cdb..d05337915e2b 100644
--- a/drivers/clk/pistachio/clk-pll.c
+++ b/drivers/clk/pistachio/clk-pll.c
@@ -139,19 +139,23 @@ pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref,
return NULL;
}
-static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
unsigned int i;
for (i = 0; i < pll->nr_rates; i++) {
- if (i > 0 && pll->rates[i].fref == *parent_rate &&
- pll->rates[i].fout <= rate)
- return pll->rates[i - 1].fout;
+ if (i > 0 && pll->rates[i].fref == req->best_parent_rate &&
+ pll->rates[i].fout <= req->rate) {
+ req->rate = pll->rates[i - 1].fout;
+
+ return 0;
+ }
}
- return pll->rates[0].fout;
+ req->rate = pll->rates[0].fout;
+
+ return 0;
}
static int pll_gf40lp_frac_enable(struct clk_hw *hw)
@@ -300,7 +304,7 @@ static const struct clk_ops pll_gf40lp_frac_ops = {
.disable = pll_gf40lp_frac_disable,
.is_enabled = pll_gf40lp_frac_is_enabled,
.recalc_rate = pll_gf40lp_frac_recalc_rate,
- .round_rate = pll_round_rate,
+ .determine_rate = pll_determine_rate,
.set_rate = pll_gf40lp_frac_set_rate,
};
@@ -432,7 +436,7 @@ static const struct clk_ops pll_gf40lp_laint_ops = {
.disable = pll_gf40lp_laint_disable,
.is_enabled = pll_gf40lp_laint_is_enabled,
.recalc_rate = pll_gf40lp_laint_recalc_rate,
- .round_rate = pll_round_rate,
+ .determine_rate = pll_determine_rate,
.set_rate = pll_gf40lp_laint_set_rate,
};
diff --git a/drivers/clk/pistachio/clk.h b/drivers/clk/pistachio/clk.h
index f9c31e3a0e47..2f4ffbd98282 100644
--- a/drivers/clk/pistachio/clk.h
+++ b/drivers/clk/pistachio/clk.h
@@ -31,10 +31,10 @@ struct pistachio_mux {
unsigned int shift;
unsigned int num_parents;
const char *name;
- const char **parents;
+ const char *const *parents;
};
-#define PNAME(x) static const char *x[] __initconst
+#define PNAME(x) static const char *const x[] __initconst
#define MUX(_id, _name, _pnames, _reg, _shift) \
{ \
diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c
index 03de634efc52..ebee2afd05de 100644
--- a/drivers/clk/pxa/clk-pxa.c
+++ b/drivers/clk/pxa/clk-pxa.c
@@ -82,6 +82,7 @@ static u8 cken_get_parent(struct clk_hw *hw)
}
static const struct clk_ops cken_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = cken_get_parent,
.set_parent = dummy_clk_set_parent,
};
@@ -104,6 +105,8 @@ int __init clk_pxa_cken_init(const struct desc_clk_cken *clks,
for (i = 0; i < nb_clks; i++) {
pxa_clk = kzalloc(sizeof(*pxa_clk), GFP_KERNEL);
+ if (!pxa_clk)
+ return -ENOMEM;
pxa_clk->is_in_low_power = clks[i].is_in_low_power;
pxa_clk->lp = clks[i].lp;
pxa_clk->hp = clks[i].hp;
diff --git a/drivers/clk/pxa/clk-pxa25x.c b/drivers/clk/pxa/clk-pxa25x.c
index 93d5907b8530..0a4da519d704 100644
--- a/drivers/clk/pxa/clk-pxa25x.c
+++ b/drivers/clk/pxa/clk-pxa25x.c
@@ -11,10 +11,12 @@
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
+#include <linux/clk/pxa.h>
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/soc/pxa/smemc.h>
+#include <linux/soc/pxa/cpu.h>
#include <dt-bindings/clock/pxa-clock.h>
#include "clk-pxa.h"
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
index 116c6ac666e3..2bea89874ec1 100644
--- a/drivers/clk/pxa/clk-pxa27x.c
+++ b/drivers/clk/pxa/clk-pxa27x.c
@@ -12,6 +12,7 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/soc/pxa/smemc.h>
+#include <linux/clk/pxa.h>
#include <dt-bindings/clock/pxa-clock.h>
#include "clk-pxa.h"
@@ -99,7 +100,7 @@ unsigned int pxa27x_get_clk_frequency_khz(int info)
return (unsigned int)clks[0] / KHz;
}
-bool pxa27x_is_ppll_disabled(void)
+static bool pxa27x_is_ppll_disabled(void)
{
unsigned long ccsr = readl(clk_regs + CCSR);
diff --git a/drivers/clk/pxa/clk-pxa3xx.c b/drivers/clk/pxa/clk-pxa3xx.c
index 42958a542662..621e298f101a 100644
--- a/drivers/clk/pxa/clk-pxa3xx.c
+++ b/drivers/clk/pxa/clk-pxa3xx.c
@@ -164,7 +164,7 @@ void pxa3xx_clk_update_accr(u32 disable, u32 enable, u32 xclkcfg, u32 mask)
accr &= ~disable;
accr |= enable;
- writel(accr, ACCR);
+ writel(accr, clk_regs + ACCR);
if (xclkcfg)
__asm__("mcr p14, 0, %0, c6, c0, 0\n" : : "r"(xclkcfg));
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 1cf1ef70e347..a284ba040b78 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -7,9 +7,6 @@ config QCOM_GDSC
bool
select PM_GENERIC_DOMAINS if PM
-config QCOM_RPMCC
- bool
-
menuconfig COMMON_CLK_QCOM
tristate "Support for Qualcomm's clock controllers"
depends on OF
@@ -17,9 +14,101 @@ menuconfig COMMON_CLK_QCOM
select RATIONAL
select REGMAP_MMIO
select RESET_CONTROLLER
+ select INTERCONNECT
+ select INTERCONNECT_CLK
if COMMON_CLK_QCOM
+config CLK_GLYMUR_DISPCC
+ tristate "GLYMUR Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select CLK_GLYMUR_GCC
+ help
+ Support for the display clock controllers on Qualcomm
+ Technologies, Inc. GLYMUR devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config CLK_GLYMUR_GCC
+ tristate "GLYMUR Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on GLYMUR devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ I2C, USB, UFS, SDCC, etc.
+
+config CLK_GLYMUR_TCSRCC
+ tristate "GLYMUR TCSR Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the TCSR clock controller on GLYMUR devices.
+ Say Y if you want to use peripheral devices such as USB/PCIe/EDP.
+
+config CLK_X1E80100_CAMCC
+ tristate "X1E80100 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select CLK_X1E80100_GCC
+ help
+ Support for the camera clock controller on X1E80100 devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config CLK_X1E80100_DISPCC
+ tristate "X1E80100 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select CLK_X1E80100_GCC
+ help
+ Support for the two display clock controllers on Qualcomm
+ Technologies, Inc. X1E80100 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config CLK_X1E80100_GCC
+ tristate "X1E80100 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on Qualcomm Technologies, Inc
+ X1E80100 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI, I2C,
+ USB, UFS, SD/eMMC, PCIe, etc.
+
+config CLK_X1E80100_GPUCC
+ tristate "X1E80100 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select CLK_X1E80100_GCC
+ help
+ Support for the graphics clock controller on X1E80100 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config CLK_X1E80100_TCSRCC
+ tristate "X1E80100 TCSR Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the TCSR clock controller on X1E80100 devices.
+ Say Y if you want to use peripheral devices such as SD/UFS.
+
+config CLK_X1P42100_GPUCC
+ tristate "X1P42100 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select CLK_X1E80100_GCC
+ help
+ Support for the graphics clock controller on X1P42100 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config CLK_QCM2290_GPUCC
+ tristate "QCM2290 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCM_GCC_2290
+ help
+ Support for the graphics clock controller on QCM2290 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
config QCOM_A53PLL
tristate "MSM8916 A53 PLL"
help
@@ -48,6 +137,7 @@ config QCOM_CLK_APCS_MSM8916
config QCOM_CLK_APCC_MSM8996
tristate "MSM8996 CPU Clock Controller"
select QCOM_KRYO_L2_ACCESSORS
+ select INTERCONNECT_CLK if INTERCONNECT
depends on ARM64
help
Support for the CPU clock controller on msm8996 devices.
@@ -57,6 +147,7 @@ config QCOM_CLK_APCC_MSM8996
config QCOM_CLK_APCS_SDX55
tristate "SDX55 and SDX65 APCS Clock Controller"
depends on QCOM_APCS_IPC || COMPILE_TEST
+ depends on ARM || COMPILE_TEST
help
Support for the APCS Clock Controller on SDX55, SDX65 platforms. The
APCS is managing the mux and divider which feeds the CPUs.
@@ -66,7 +157,6 @@ config QCOM_CLK_APCS_SDX55
config QCOM_CLK_RPM
tristate "RPM based Clock Controller"
depends on MFD_QCOM_RPM
- select QCOM_RPMCC
help
The RPM (Resource Power Manager) is a dedicated hardware engine for
managing the shared SoC resources in order to keep the lowest power
@@ -79,7 +169,6 @@ config QCOM_CLK_RPM
config QCOM_CLK_SMD_RPM
tristate "RPM over SMD based Clock Controller"
depends on QCOM_SMD_RPM
- select QCOM_RPMCC
help
The RPM (Resource Power Manager) is a dedicated hardware engine for
managing the shared SoC resources in order to keep the lowest power
@@ -100,6 +189,7 @@ config QCOM_CLK_RPMH
config APQ_GCC_8084
tristate "APQ8084 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on apq8084 devices.
@@ -108,6 +198,7 @@ config APQ_GCC_8084
config APQ_MMCC_8084
tristate "APQ8084 Multimedia Clock Controller"
+ depends on ARM || COMPILE_TEST
select APQ_GCC_8084
select QCOM_GDSC
help
@@ -123,10 +214,20 @@ config IPQ_APSS_PLL
Say Y if you want to support CPU frequency scaling on ipq based
devices.
+config IPQ_APSS_5424
+ tristate "IPQ5424 APSS Clock Controller"
+ select IPQ_APSS_PLL
+ default y if IPQ_GCC_5424
+ help
+ Support for APSS Clock controller on Qualcomm IPQ5424 platform.
+ Say Y if you want to support CPU frequency scaling on ipq based
+ devices.
+
config IPQ_APSS_6018
- tristate "IPQ APSS Clock Controller"
+ tristate "IPQ6018 APSS Clock Controller"
select IPQ_APSS_PLL
depends on QCOM_APCS_IPC || COMPILE_TEST
+ depends on QCOM_SMEM
help
Support for APSS clock controller on IPQ platforms. The
APSS clock controller manages the Mux and enable block that feeds the
@@ -134,6 +235,15 @@ config IPQ_APSS_6018
Say Y if you want to support CPU frequency scaling on
ipq based devices.
+config IPQ_CMN_PLL
+ tristate "IPQ CMN PLL Clock Controller"
+ help
+ Support for CMN PLL clock controller on IPQ platform. The
+ CMN PLL consumes the AHB/SYS clocks from GCC and supplies
+ the output clocks to the networking hardware and GCC blocks.
+ Say Y or M if you want to support CMN PLL clock on the IPQ
+ based devices.
+
config IPQ_GCC_4019
tristate "IPQ4019 Global Clock Controller"
help
@@ -141,6 +251,30 @@ config IPQ_GCC_4019
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, etc.
+config IPQ_GCC_5018
+ tristate "IPQ5018 Global Clock Controller"
+ depends on ARM || ARM64 || COMPILE_TEST
+ help
+ Support for global clock controller on ipq5018 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, etc.
+
+config IPQ_GCC_5332
+ tristate "IPQ5332 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ help
+ Support for the global clock controller on ipq5332 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, etc.
+
+config IPQ_GCC_5424
+ tristate "IPQ5424 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ help
+ Support for the global clock controller on ipq5424 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, etc.
+
config IPQ_GCC_6018
tristate "IPQ6018 Global Clock Controller"
help
@@ -151,6 +285,7 @@ config IPQ_GCC_6018
config IPQ_GCC_806X
tristate "IPQ806x Global Clock Controller"
+ depends on ARM || COMPILE_TEST
help
Support for the global clock controller on ipq806x devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -158,6 +293,7 @@ config IPQ_GCC_806X
config IPQ_LCC_806X
tristate "IPQ806x LPASS Clock Controller"
+ depends on ARM || COMPILE_TEST
select IPQ_GCC_806X
help
Support for the LPASS clock controller on ipq806x devices.
@@ -173,13 +309,58 @@ config IPQ_GCC_8074
i2c, USB, SD/eMMC, etc. Select this for the root clock
of ipq8074.
+config IPQ_GCC_9574
+ tristate "IPQ9574 Global Clock Controller"
+ help
+ Support for global clock controller on ipq9574 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, etc. Select this for the root clock
+ of ipq9574.
+
+config IPQ_NSSCC_5424
+ tristate "IPQ5424 NSS Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on IPQ_GCC_5424
+ help
+ Support for NSS clock controller on ipq5424 devices.
+ NSSCC receives the clock sources from GCC, CMN PLL and UNIPHY (PCS).
+ It in turn supplies the clocks and resets to the networking hardware.
+ Say Y or M if you want to enable networking function on the
+ IPQ5424 devices.
+
+config IPQ_NSSCC_9574
+ tristate "IPQ9574 NSS Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on IPQ_GCC_9574
+ help
+ Support for NSS clock controller on ipq9574 devices.
+
+config IPQ_NSSCC_QCA8K
+ tristate "QCA8K(QCA8386 or QCA8084) NSS Clock Controller"
+ depends on MDIO_BUS
+ help
+ Support for NSS(Network SubSystem) clock controller on
+ qca8386/qca8084 chip.
+ Say Y or M if you want to use network features of switch or
+ PHY device. Select this for the root clock of qca8k.
+
config MSM_GCC_8660
tristate "MSM8660 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
help
Support for the global clock controller on msm8660 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, etc.
+config MSM_GCC_8909
+ tristate "MSM8909 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on msm8909 devices.
+ Say Y if you want to use devices such as UART, SPI, I2C, USB,
+ SD/eMMC, display, graphics, camera etc.
+
config MSM_GCC_8916
tristate "MSM8916 Global Clock Controller"
select QCOM_GDSC
@@ -188,6 +369,16 @@ config MSM_GCC_8916
Say Y if you want to use devices such as UART, SPI i2c, USB,
SD/eMMC, display, graphics, camera etc.
+config MSM_GCC_8917
+ tristate "MSM89(17/37)/QM215 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on msm8917, msm8937
+ and qm215 devices.
+ Say Y if you want to use devices such as UART, SPI i2c, USB,
+ SD/eMMC, display, graphics, camera etc.
+
config MSM_GCC_8939
tristate "MSM8939 Global Clock Controller"
select QCOM_GDSC
@@ -198,21 +389,24 @@ config MSM_GCC_8939
config MSM_GCC_8960
tristate "APQ8064/MSM8960 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
help
Support for the global clock controller on apq8064/msm8960 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, SATA, PCIe, etc.
config MSM_LCC_8960
- tristate "APQ8064/MSM8960 LPASS Clock Controller"
- select MSM_GCC_8960
+ tristate "APQ8064/MSM8960/MDM9650 LPASS Clock Controller"
+ depends on ARM || COMPILE_TEST
help
- Support for the LPASS clock controller on apq8064/msm8960 devices.
+ Support for the LPASS clock controller on apq8064/msm8960/mdm9650
+ devices.
Say Y if you want to use audio devices such as i2s, pcm,
SLIMBus, etc.
config MDM_GCC_9607
tristate "MDM9607 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
help
Support for the global clock controller on mdm9607 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -220,21 +414,15 @@ config MDM_GCC_9607
config MDM_GCC_9615
tristate "MDM9615 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
help
Support for the global clock controller on mdm9615 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, etc.
-config MDM_LCC_9615
- tristate "MDM9615 LPASS Clock Controller"
- select MDM_GCC_9615
- help
- Support for the LPASS clock controller on mdm9615 devices.
- Say Y if you want to use audio devices such as i2s, pcm,
- SLIMBus, etc.
-
config MSM_MMCC_8960
tristate "MSM8960 Multimedia Clock Controller"
+ depends on ARM || COMPILE_TEST
select MSM_GCC_8960
help
Support for the multimedia clock controller on msm8960 devices.
@@ -251,6 +439,7 @@ config MSM_GCC_8953
config MSM_GCC_8974
tristate "MSM8974 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on msm8974 devices.
@@ -259,6 +448,7 @@ config MSM_GCC_8974
config MSM_MMCC_8974
tristate "MSM8974 Multimedia Clock Controller"
+ depends on ARM || COMPILE_TEST
select MSM_GCC_8974
select QCOM_GDSC
help
@@ -350,6 +540,26 @@ config QCM_DISPCC_2290
Say Y if you want to support display devices and functionality such as
splash screen.
+config QCS_DISPCC_615
+ tristate "QCS615 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCS_GCC_615
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ QCS615 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config QCS_CAMCC_615
+ tristate "QCS615 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCS_GCC_615
+ help
+ Support for the camera clock controller on Qualcomm Technologies, Inc
+ QCS615 devices.
+ Say Y if you want to support camera devices and functionality such as
+ capturing pictures.
+
config QCS_GCC_404
tristate "QCS404 Global Clock Controller"
help
@@ -357,8 +567,56 @@ config QCS_GCC_404
Say Y if you want to use multimedia devices or peripheral
devices such as UART, SPI, I2C, USB, SD/eMMC, PCIe etc.
+config SA_CAMCC_8775P
+ tristate "SA8775P Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SA_GCC_8775P
+ help
+ Support for the camera clock controller on Qualcomm Technologies, Inc
+ SA8775P devices.
+ Say Y if you want to support camera devices and functionality such as
+ capturing pictures.
+
+config QCS_GCC_8300
+ tristate "QCS8300 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on Qualcomm Technologies, Inc
+ QCS8300 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS, PCIe etc.
+
+config QCS_GCC_615
+ tristate "QCS615 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on QCS615 devices.
+ Say Y if you want to use multimedia devices or peripheral
+ devices such as UART, SPI, I2C, USB, SD/eMMC, PCIe etc.
+
+config QCS_GPUCC_615
+ tristate "QCS615 Graphics clock controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCS_GCC_615
+ help
+ Support for the graphics clock controller on QCS615 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config QCS_VIDEOCC_615
+ tristate "QCS615 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCS_GCC_615
+ help
+ Support for the video clock controller on QCS615 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode and decode.
+
config SC_CAMCC_7180
tristate "SC7180 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7180
help
Support for the camera clock controller on Qualcomm Technologies, Inc
@@ -368,6 +626,7 @@ config SC_CAMCC_7180
config SC_CAMCC_7280
tristate "SC7280 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7280
help
Support for the camera clock controller on Qualcomm Technologies, Inc
@@ -375,8 +634,39 @@ config SC_CAMCC_7280
Say Y if you want to support camera devices and functionality such as
capturing pictures.
+config SC_CAMCC_8180X
+ tristate "SC8180X Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SC_GCC_8180X
+ help
+ Support for the camera clock controller on Qualcomm Technologies, Inc
+ SC8180X devices.
+ Say Y if you want to support camera devices and functionality such as
+ capturing pictures.
+
+config SC_CAMCC_8280XP
+ tristate "SC8280XP Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SC_GCC_8280XP
+ help
+ Support for the camera clock controller on Qualcomm Technologies, Inc
+ SC8280XP devices.
+ Say Y if you want to support camera devices and functionality such as
+ capturing pictures.
+
+config SA_DISPCC_8775P
+ tristate "SA8775P Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SA_GCC_8775P
+ help
+ Support for the two display clock controllers on Qualcomm
+ Technologies, Inc. SA8775P devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
config SC_DISPCC_7180
tristate "SC7180 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7180
help
Support for the display clock controller on Qualcomm Technologies, Inc
@@ -386,6 +676,7 @@ config SC_DISPCC_7180
config SC_DISPCC_7280
tristate "SC7280 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7280
help
Support for the display clock controller on Qualcomm Technologies, Inc.
@@ -393,10 +684,57 @@ config SC_DISPCC_7280
Say Y if you want to support display devices and functionality such as
splash screen.
+config SC_DISPCC_8280XP
+ tristate "SC8280XP Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SC_GCC_8280XP
+ help
+ Support for the two display clock controllers on Qualcomm
+ Technologies, Inc. SC8280XP devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config SA_GCC_8775P
+ tristate "SA8775 Global Clock Controller"
+ select QCOM_GDSC
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the global clock controller on SA8775 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ I2C, USB, UFS, SDCC, etc.
+
+config SA_GPUCC_8775P
+ tristate "SA8775P Graphics clock controller"
+ select QCOM_GDSC
+ select SA_GCC_8775P
+ help
+ Support for the graphics clock controller on SA8775P devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SAR_GCC_2130P
+ tristate "SAR2130P Global Clock Controller"
+ select QCOM_GDSC
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the global clock controller on SAR2130P devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ I2C, USB, SDCC, etc.
+
+config SAR_GPUCC_2130P
+ tristate "SAR2130P Graphics clock controller"
+ select QCOM_GDSC
+ select SAR_GCC_2130P
+ help
+ Support for the graphics clock controller on SAR2130P devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
config SC_GCC_7180
tristate "SC7180 Global Clock Controller"
select QCOM_GDSC
depends on COMMON_CLK_QCOM
+ depends on ARM64 || COMPILE_TEST
help
Support for the global clock controller on SC7180 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -406,6 +744,7 @@ config SC_GCC_7280
tristate "SC7280 Global Clock Controller"
select QCOM_GDSC
depends on COMMON_CLK_QCOM
+ depends on ARM64 || COMPILE_TEST
help
Support for the global clock controller on SC7280 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -415,6 +754,7 @@ config SC_GCC_8180X
tristate "SC8180X Global Clock Controller"
select QCOM_GDSC
depends on COMMON_CLK_QCOM
+ depends on ARM64 || COMPILE_TEST
help
Support for the global clock controller on SC8180X devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -424,6 +764,7 @@ config SC_GCC_8280XP
tristate "SC8280XP Global Clock Controller"
select QCOM_GDSC
depends on COMMON_CLK_QCOM
+ depends on ARM64 || COMPILE_TEST
help
Support for the global clock controller on SC8280XP devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -431,6 +772,7 @@ config SC_GCC_8280XP
config SC_GPUCC_7180
tristate "SC7180 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7180
help
Support for the graphics clock controller on SC7180 devices.
@@ -439,22 +781,43 @@ config SC_GPUCC_7180
config SC_GPUCC_7280
tristate "SC7280 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7280
help
Support for the graphics clock controller on SC7280 devices.
Say Y if you want to support graphics controller devices and
functionality such as 3D graphics.
+config SC_GPUCC_8280XP
+ tristate "SC8280XP Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SC_GCC_8280XP
+ help
+ Support for the graphics clock controller on SC8280XP devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
config SC_LPASSCC_7280
tristate "SC7280 Low Power Audio Subsystem (LPASS) Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7280
help
Support for the LPASS clock controller on SC7280 devices.
Say Y if you want to use the LPASS branch clocks of the LPASS clock
controller to reset the LPASS subsystem.
+config SC_LPASSCC_8280XP
+ tristate "SC8280 Low Power Audio Subsystem (LPASS) Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SC_GCC_8280XP
+ help
+ Support for the LPASS clock controller on SC8280XP devices.
+ Say Y if you want to use the LPASS branch clocks of the LPASS clock
+ controller to reset the LPASS subsystem.
+
config SC_LPASS_CORECC_7180
tristate "SC7180 LPASS Core Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7180
help
Support for the LPASS(Low Power Audio Subsystem) core clock controller
@@ -464,6 +827,7 @@ config SC_LPASS_CORECC_7180
config SC_LPASS_CORECC_7280
tristate "SC7280 LPASS Core & Audio Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7280
select QCOM_GDSC
help
@@ -472,17 +836,9 @@ config SC_LPASS_CORECC_7280
Say Y if you want to use LPASS clocks and power domains of the LPASS
core clock controller.
-config SC_MSS_7180
- tristate "SC7180 Modem Clock Controller"
- select SC_GCC_7180
- help
- Support for the Modem Subsystem clock controller on Qualcomm
- Technologies, Inc on SC7180 devices.
- Say Y if you want to use the Modem branch clocks of the Modem
- subsystem clock controller to reset the MSS subsystem.
-
config SC_VIDEOCC_7180
tristate "SC7180 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7180
help
Support for the video clock controller on SC7180 devices.
@@ -491,6 +847,7 @@ config SC_VIDEOCC_7180
config SC_VIDEOCC_7280
tristate "SC7280 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SC_GCC_7280
help
Support for the video clock controller on SC7280 devices.
@@ -499,6 +856,7 @@ config SC_VIDEOCC_7280
config SDM_CAMCC_845
tristate "SDM845 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_845
help
Support for the camera clock controller on SDM845 devices.
@@ -506,6 +864,7 @@ config SDM_CAMCC_845
config SDM_GCC_660
tristate "SDM660 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on SDM660 devices.
@@ -514,6 +873,7 @@ config SDM_GCC_660
config SDM_MMCC_660
tristate "SDM660 Multimedia Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_660
select QCOM_GDSC
help
@@ -523,6 +883,7 @@ config SDM_MMCC_660
config SDM_GPUCC_660
tristate "SDM660 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_660
select QCOM_GDSC
help
@@ -544,16 +905,35 @@ config QCS_Q6SSTOP_404
Say Y if you want to use the Q6SSTOP branch clocks of the WCSS clock
controller to reset the Q6SSTOP subsystem.
+config QDU_GCC_1000
+ tristate "QDU1000/QRU1000 Global Clock Controller"
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on QDU1000 and
+ QRU1000 devices. Say Y if you want to use peripheral
+ devices such as UART, SPI, I2C, USB, SD, PCIe, etc.
+
+config QDU_ECPRICC_1000
+ tristate "QDU1000/QRU1000 ECPRI Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QDU_GCC_1000
+ help
+ Support for the ECPRI clock controller on QDU1000 and
+ QRU1000 devices. Say Y if you want to support the ECPRI
+ clock controller functionality such as Ethernet.
+
config SDM_GCC_845
- tristate "SDM845 Global Clock Controller"
+ tristate "SDM845/SDM670 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select QCOM_GDSC
help
- Support for the global clock controller on SDM845 devices.
+ Support for the global clock controller on SDM845 and SDM670 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2C, USB, UFS, SDDC, PCIe, etc.
config SDM_GPUCC_845
tristate "SDM845 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_845
help
Support for the graphics clock controller on SDM845 devices.
@@ -562,6 +942,7 @@ config SDM_GPUCC_845
config SDM_VIDEOCC_845
tristate "SDM845 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_845
select QCOM_GDSC
help
@@ -571,6 +952,7 @@ config SDM_VIDEOCC_845
config SDM_DISPCC_845
tristate "SDM845 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_845
help
Support for the display clock controller on Qualcomm Technologies, Inc
@@ -580,6 +962,7 @@ config SDM_DISPCC_845
config SDM_LPASSCC_845
tristate "SDM845 Low Power Audio Subsystem (LPAAS) Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SDM_GCC_845
help
Support for the LPASS clock controller on SDM845 devices.
@@ -588,6 +971,7 @@ config SDM_LPASSCC_845
config SDX_GCC_55
tristate "SDX55 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on SDX55 devices.
@@ -596,14 +980,66 @@ config SDX_GCC_55
config SDX_GCC_65
tristate "SDX65 Global Clock Controller"
+ depends on ARM || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on SDX65 devices.
Say Y if you want to use peripheral devices such as UART,
SPI, I2C, USB, SD/UFS, PCIe etc.
+config SDX_GCC_75
+ tristate "SDX75 Global Clock Controller"
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SDX75 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/eMMC, PCIe etc.
+
+config SM_CAMCC_4450
+ tristate "SM4450 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_4450
+ help
+ Support for the camera clock controller on SM4450 devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config SM_CAMCC_6350
+ tristate "SM6350 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_6350
+ help
+ Support for the camera clock controller on SM6350 devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config SM_CAMCC_7150
+ tristate "SM7150 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_7150
+ help
+ Support for the camera clock controller on SM7150 devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config SM_CAMCC_MILOS
+ tristate "Milos Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_MILOS
+ help
+ Support for the camera clock controller on Milos devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config SM_CAMCC_8150
+ tristate "SM8150 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8150
+ help
+ Support for the camera clock controller on Qualcomm Technologies, Inc
+ SM8150 devices.
+ Say Y if you want to support camera devices and functionality such as
+ capturing pictures.
+
config SM_CAMCC_8250
tristate "SM8250 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8250
help
Support for the camera clock controller on SM8250 devices.
@@ -611,13 +1047,51 @@ config SM_CAMCC_8250
config SM_CAMCC_8450
tristate "SM8450 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8450
help
- Support for the camera clock controller on SM8450 devices.
+ Support for the camera clock controller on SM8450 or SM8475 devices.
Say Y if you want to support camera devices and camera functionality.
+config SM_CAMCC_8550
+ tristate "SM8550 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8550
+ help
+ Support for the camera clock controller on SM8550 devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config SM_CAMCC_8650
+ tristate "SM8650 Camera Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8650
+ help
+ Support for the camera clock controller on SM8650 devices.
+ Say Y if you want to support camera devices and camera functionality.
+
+config SM_DISPCC_4450
+ tristate "SM4450 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_4450
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SM4450 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen
+
+config SM_DISPCC_6115
+ tristate "SM6115 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_6115
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SM6115/SM4250 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen
+
config SM_DISPCC_6125
tristate "SM6125 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
depends on SM_GCC_6125
help
Support for the display clock controller on Qualcomm Technologies, Inc
@@ -625,8 +1099,19 @@ config SM_DISPCC_6125
Say Y if you want to support display devices and functionality such as
splash screen
+config SM_DISPCC_7150
+ tristate "SM7150 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_7150
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SM7150 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
config SM_DISPCC_8250
tristate "SM8150/SM8250/SM8350 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
depends on SM_GCC_8150 || SM_GCC_8250 || SM_GCC_8350
help
Support for the display clock controller on Qualcomm Technologies, Inc
@@ -636,6 +1121,7 @@ config SM_DISPCC_8250
config SM_DISPCC_6350
tristate "SM6350 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
depends on SM_GCC_6350
help
Support for the display clock controller on Qualcomm Technologies, Inc
@@ -643,8 +1129,70 @@ config SM_DISPCC_6350
Say Y if you want to support display devices and functionality such as
splash screen.
+config SM_DISPCC_6375
+ tristate "SM6375 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_6375
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SM6375 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config SM_DISPCC_MILOS
+ tristate "Milos Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_MILOS
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ Milos devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config SM_DISPCC_8450
+ tristate "SM8450 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_8450
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SM8450 or SM8475 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config SM_DISPCC_8550
+ tristate "SM8550 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_8550 || SM_GCC_8650 || SAR_GCC_2130P
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SAR2130P, SM8550 or SM8650 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config SM_DISPCC_8750
+ tristate "SM8750 Display Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ depends on SM_GCC_8750
+ select QCOM_GDSC
+ help
+ Support for the display clock controller on Qualcomm Technologies, Inc
+ SM8750 devices.
+ Say Y if you want to support display devices and functionality such as
+ splash screen.
+
+config SM_GCC_4450
+ tristate "SM4450 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SM4450 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ I2C, USB, SD/UFS, PCIe, etc.
+
config SM_GCC_6115
tristate "SM6115 and SM4250 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
help
Support for the global clock controller on SM6115 and SM4250 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
@@ -652,6 +1200,8 @@ config SM_GCC_6115
config SM_GCC_6125
tristate "SM6125 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
help
Support for the global clock controller on SM6125 devices.
Say Y if you want to use peripheral devices such as UART,
@@ -659,14 +1209,44 @@ config SM_GCC_6125
config SM_GCC_6350
tristate "SM6350 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on SM6350 devices.
Say Y if you want to use peripheral devices such as UART,
SPI, I2C, USB, SD/UFS, PCIe etc.
+config SM_GCC_6375
+ tristate "SM6375 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SM6375 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS etc.
+
+config SM_GCC_7150
+ tristate "SM7150 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SM7150 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS, PCIe etc.
+
+config SM_GCC_MILOS
+ tristate "Milos Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on Milos devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS, PCIe etc.
+
config SM_GCC_8150
tristate "SM8150 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
help
Support for the global clock controller on SM8150 devices.
Say Y if you want to use peripheral devices such as UART,
@@ -674,6 +1254,7 @@ config SM_GCC_8150
config SM_GCC_8250
tristate "SM8250 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on SM8250 devices.
@@ -682,6 +1263,7 @@ config SM_GCC_8250
config SM_GCC_8350
tristate "SM8350 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select QCOM_GDSC
help
Support for the global clock controller on SM8350 devices.
@@ -690,22 +1272,98 @@ config SM_GCC_8350
config SM_GCC_8450
tristate "SM8450 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select QCOM_GDSC
help
- Support for the global clock controller on SM8450 devices.
+ Support for the global clock controller on SM8450 or SM8475
+ devices.
Say Y if you want to use peripheral devices such as UART,
SPI, I2C, USB, SD/UFS, PCIe etc.
+config SM_GCC_8550
+ tristate "SM8550 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SM8550 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS, PCIe etc.
+
+config SM_GCC_8650
+ tristate "SM8650 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SM8650 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS, PCIe etc.
+
+config SM_GCC_8750
+ tristate "SM8750 Global Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the global clock controller on SM8750 devices.
+ Say Y if you want to use peripheral devices such as UART,
+ SPI, I2C, USB, SD/UFS, PCIe etc.
+
+config SM_GPUCC_4450
+ tristate "SM4450 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_4450
+ help
+ Support for the graphics clock controller on SM4450 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SM_GPUCC_6115
+ tristate "SM6115 Graphics Clock Controller"
+ select SM_GCC_6115
+ depends on ARM64 || COMPILE_TEST
+ help
+ Support for the graphics clock controller on SM6115 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SM_GPUCC_6125
+ tristate "SM6125 Graphics Clock Controller"
+ select SM_GCC_6125
+ depends on ARM64 || COMPILE_TEST
+ help
+ Support for the graphics clock controller on SM6125 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SM_GPUCC_6375
+ tristate "SM6375 Graphics Clock Controller"
+ select SM_GCC_6375
+ depends on ARM64 || COMPILE_TEST
+ help
+ Support for the graphics clock controller on SM6375 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
config SM_GPUCC_6350
tristate "SM6350 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_6350
help
Support for the graphics clock controller on SM6350 devices.
Say Y if you want to support graphics controller devices and
functionality such as 3D graphics.
+config SM_GPUCC_MILOS
+ tristate "Milos Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_MILOS
+ help
+ Support for the graphics clock controller on Milos devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
config SM_GPUCC_8150
tristate "SM8150 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8150
help
Support for the graphics clock controller on SM8150 devices.
@@ -714,6 +1372,7 @@ config SM_GPUCC_8150
config SM_GPUCC_8250
tristate "SM8250 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8250
help
Support for the graphics clock controller on SM8250 devices.
@@ -722,14 +1381,119 @@ config SM_GPUCC_8250
config SM_GPUCC_8350
tristate "SM8350 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8350
help
Support for the graphics clock controller on SM8350 devices.
Say Y if you want to support graphics controller devices and
functionality such as 3D graphics.
+config SM_GPUCC_8450
+ tristate "SM8450 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8450
+ help
+ Support for the graphics clock controller on SM8450 or SM8475
+ devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SM_GPUCC_8550
+ tristate "SM8550 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8550
+ help
+ Support for the graphics clock controller on SM8550 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SM_GPUCC_8650
+ tristate "SM8650 Graphics Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8650
+ help
+ Support for the graphics clock controller on SM8650 devices.
+ Say Y if you want to support graphics controller devices and
+ functionality such as 3D graphics.
+
+config SM_LPASSCC_6115
+ tristate "SM6115 Low Power Audio Subsystem (LPASS) Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_6115
+ help
+ Support for the LPASS clock controller on SM6115 devices.
+ Say Y if you want to toggle LPASS-adjacent resets within
+ this clock controller to reset the LPASS subsystem.
+
+config SM_TCSRCC_8550
+ tristate "SM8550 TCSR Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the TCSR clock controller on SM8550 devices.
+ Say Y if you want to use peripheral devices such as SD/UFS.
+
+config SM_TCSRCC_8650
+ tristate "SM8650 TCSR Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the TCSR clock controller on SM8650 devices.
+ Say Y if you want to use peripheral devices such as SD/UFS.
+
+config SM_TCSRCC_8750
+ tristate "SM8750 TCSR Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the TCSR clock controller on SM8750 devices.
+ Say Y if you want to use peripheral devices such as UFS/USB/PCIe.
+
+config SA_VIDEOCC_8775P
+ tristate "SA8775P Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SA_GCC_8775P
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on Qualcomm Technologies, Inc.
+ SA8775P devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode/decode.
+
+config SM_VIDEOCC_6350
+ tristate "SM6350 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_6350
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on SM6350 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode and decode.
+
+config SM_VIDEOCC_7150
+ tristate "SM7150 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_7150
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on SM7150 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode and decode.
+
+config SM_VIDEOCC_MILOS
+ tristate "Milos Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_MILOS
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on Qualcomm Technologies, Inc.
+ Milos devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode/decode.
+
config SM_VIDEOCC_8150
tristate "SM8150 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8150
select QCOM_GDSC
help
@@ -739,6 +1503,7 @@ config SM_VIDEOCC_8150
config SM_VIDEOCC_8250
tristate "SM8250 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
select SM_GCC_8250
select QCOM_GDSC
help
@@ -746,6 +1511,37 @@ config SM_VIDEOCC_8250
Say Y if you want to support video devices and functionality such as
video encode and decode.
+config SM_VIDEOCC_8350
+ tristate "SM8350 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8350
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on SM8350 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode and decode.
+
+config SM_VIDEOCC_8550
+ tristate "SM8550 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on Qualcomm Technologies, Inc.
+ SM8550 or SM8650 or X1E80100 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode/decode.
+
+config SM_VIDEOCC_8750
+ tristate "SM8750 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8750
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on Qualcomm Technologies, Inc.
+ SM8750 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode/decode.
+
config SPMI_PMIC_CLKDIV
tristate "SPMI PMIC clkdiv Support"
depends on SPMI || COMPILE_TEST
@@ -779,8 +1575,19 @@ config KRAITCC
config CLK_GFM_LPASS_SM8250
tristate "SM8250 GFM LPASS Clocks"
+ depends on ARM64 || COMPILE_TEST
help
Support for the Glitch Free Mux (GFM) Low power audio
subsystem (LPASS) clocks found on SM8250 SoCs.
+config SM_VIDEOCC_8450
+ tristate "SM8450 Video Clock Controller"
+ depends on ARM64 || COMPILE_TEST
+ select SM_GCC_8450
+ select QCOM_GDSC
+ help
+ Support for the video clock controller on Qualcomm Technologies, Inc.
+ SM8450 or SM8475 devices.
+ Say Y if you want to support video devices and functionality such as
+ video encode/decode.
endif
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index fbcf04073f07..0ac8a9055a43 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -21,18 +21,38 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o
+obj-$(CONFIG_CLK_GLYMUR_DISPCC) += dispcc-glymur.o
+obj-$(CONFIG_CLK_GLYMUR_GCC) += gcc-glymur.o
+obj-$(CONFIG_CLK_GLYMUR_TCSRCC) += tcsrcc-glymur.o
+obj-$(CONFIG_CLK_X1E80100_CAMCC) += camcc-x1e80100.o
+obj-$(CONFIG_CLK_X1E80100_DISPCC) += dispcc-x1e80100.o
+obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o
+obj-$(CONFIG_CLK_X1E80100_GPUCC) += gpucc-x1e80100.o
+obj-$(CONFIG_CLK_X1E80100_TCSRCC) += tcsrcc-x1e80100.o
+obj-$(CONFIG_CLK_X1P42100_GPUCC) += gpucc-x1p42100.o
+obj-$(CONFIG_CLK_QCM2290_GPUCC) += gpucc-qcm2290.o
obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o
+obj-$(CONFIG_IPQ_APSS_5424) += apss-ipq5424.o
obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o
+obj-$(CONFIG_IPQ_CMN_PLL) += ipq-cmn-pll.o
obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
+obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o
+obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o
+obj-$(CONFIG_IPQ_GCC_5424) += gcc-ipq5424.o
obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o
obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
obj-$(CONFIG_IPQ_GCC_8074) += gcc-ipq8074.o
+obj-$(CONFIG_IPQ_GCC_9574) += gcc-ipq9574.o
+obj-$(CONFIG_IPQ_NSSCC_5424) += nsscc-ipq5424.o
+obj-$(CONFIG_IPQ_NSSCC_9574) += nsscc-ipq9574.o
obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o
+obj-$(CONFIG_IPQ_NSSCC_QCA8K) += nsscc-qca8k.o
obj-$(CONFIG_MDM_GCC_9607) += gcc-mdm9607.o
obj-$(CONFIG_MDM_GCC_9615) += gcc-mdm9615.o
-obj-$(CONFIG_MDM_LCC_9615) += lcc-mdm9615.o
obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
+obj-$(CONFIG_MSM_GCC_8909) += gcc-msm8909.o
obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o
+obj-$(CONFIG_MSM_GCC_8917) += gcc-msm8917.o
obj-$(CONFIG_MSM_GCC_8939) += gcc-msm8939.o
obj-$(CONFIG_MSM_GCC_8953) += gcc-msm8953.o
obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
@@ -51,30 +71,49 @@ obj-$(CONFIG_MSM_MMCC_8998) += mmcc-msm8998.o
obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o
obj-$(CONFIG_QCOM_A7PLL) += a7-pll.o
obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
-obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += clk-cpu-8996.o
+obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += apcs-msm8996.o clk-cpu-8996.o clk-cbf-8996.o
obj-$(CONFIG_QCOM_CLK_APCS_SDX55) += apcs-sdx55.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_QCM_GCC_2290) += gcc-qcm2290.o
obj-$(CONFIG_QCM_DISPCC_2290) += dispcc-qcm2290.o
+obj-$(CONFIG_QCS_DISPCC_615) += dispcc-qcs615.o
+obj-$(CONFIG_QCS_CAMCC_615) += camcc-qcs615.o
obj-$(CONFIG_QCS_GCC_404) += gcc-qcs404.o
+obj-$(CONFIG_QCS_GCC_615) += gcc-qcs615.o
+obj-$(CONFIG_QCS_GCC_8300) += gcc-qcs8300.o
+obj-$(CONFIG_QCS_GPUCC_615) += gpucc-qcs615.o
+obj-$(CONFIG_QCS_VIDEOCC_615) += videocc-qcs615.o
obj-$(CONFIG_QCS_Q6SSTOP_404) += q6sstop-qcs404.o
obj-$(CONFIG_QCS_TURING_404) += turingcc-qcs404.o
+obj-$(CONFIG_QDU_ECPRICC_1000) += ecpricc-qdu1000.o
+obj-$(CONFIG_QDU_GCC_1000) += gcc-qdu1000.o
obj-$(CONFIG_SC_CAMCC_7180) += camcc-sc7180.o
obj-$(CONFIG_SC_CAMCC_7280) += camcc-sc7280.o
+obj-$(CONFIG_SC_CAMCC_8180X) += camcc-sc8180x.o
+obj-$(CONFIG_SC_CAMCC_8280XP) += camcc-sc8280xp.o
obj-$(CONFIG_SC_DISPCC_7180) += dispcc-sc7180.o
obj-$(CONFIG_SC_DISPCC_7280) += dispcc-sc7280.o
+obj-$(CONFIG_SC_DISPCC_8280XP) += dispcc-sc8280xp.o
+obj-$(CONFIG_SA_CAMCC_8775P) += camcc-sa8775p.o
+obj-$(CONFIG_SA_DISPCC_8775P) += dispcc0-sa8775p.o dispcc1-sa8775p.o
+obj-$(CONFIG_SA_GCC_8775P) += gcc-sa8775p.o
+obj-$(CONFIG_SA_GPUCC_8775P) += gpucc-sa8775p.o
+obj-$(CONFIG_SA_VIDEOCC_8775P) += videocc-sa8775p.o
+obj-$(CONFIG_SAR_GCC_2130P) += gcc-sar2130p.o
+obj-$(CONFIG_SAR_GPUCC_2130P) += gpucc-sar2130p.o
obj-$(CONFIG_SC_GCC_7180) += gcc-sc7180.o
obj-$(CONFIG_SC_GCC_7280) += gcc-sc7280.o
obj-$(CONFIG_SC_GCC_8180X) += gcc-sc8180x.o
obj-$(CONFIG_SC_GCC_8280XP) += gcc-sc8280xp.o
obj-$(CONFIG_SC_GPUCC_7180) += gpucc-sc7180.o
obj-$(CONFIG_SC_GPUCC_7280) += gpucc-sc7280.o
+obj-$(CONFIG_SC_GPUCC_8280XP) += gpucc-sc8280xp.o
obj-$(CONFIG_SC_LPASSCC_7280) += lpasscc-sc7280.o
+obj-$(CONFIG_SC_LPASSCC_8280XP) += lpasscc-sc8280xp.o
obj-$(CONFIG_SC_LPASS_CORECC_7180) += lpasscorecc-sc7180.o
obj-$(CONFIG_SC_LPASS_CORECC_7280) += lpasscorecc-sc7280.o lpassaudiocc-sc7280.o
-obj-$(CONFIG_SC_MSS_7180) += mss-sc7180.o
obj-$(CONFIG_SC_VIDEOCC_7180) += videocc-sc7180.o
obj-$(CONFIG_SC_VIDEOCC_7280) += videocc-sc7280.o
obj-$(CONFIG_SDM_CAMCC_845) += camcc-sdm845.o
@@ -88,24 +127,66 @@ obj-$(CONFIG_SDM_LPASSCC_845) += lpasscc-sdm845.o
obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o
obj-$(CONFIG_SDX_GCC_55) += gcc-sdx55.o
obj-$(CONFIG_SDX_GCC_65) += gcc-sdx65.o
+obj-$(CONFIG_SDX_GCC_75) += gcc-sdx75.o
+obj-$(CONFIG_SM_CAMCC_4450) += camcc-sm4450.o
+obj-$(CONFIG_SM_CAMCC_6350) += camcc-sm6350.o
+obj-$(CONFIG_SM_CAMCC_7150) += camcc-sm7150.o
+obj-$(CONFIG_SM_CAMCC_8150) += camcc-sm8150.o
obj-$(CONFIG_SM_CAMCC_8250) += camcc-sm8250.o
obj-$(CONFIG_SM_CAMCC_8450) += camcc-sm8450.o
+obj-$(CONFIG_SM_CAMCC_8550) += camcc-sm8550.o
+obj-$(CONFIG_SM_CAMCC_8650) += camcc-sm8650.o
+obj-$(CONFIG_SM_CAMCC_MILOS) += camcc-milos.o
+obj-$(CONFIG_SM_DISPCC_4450) += dispcc-sm4450.o
+obj-$(CONFIG_SM_DISPCC_6115) += dispcc-sm6115.o
obj-$(CONFIG_SM_DISPCC_6125) += dispcc-sm6125.o
obj-$(CONFIG_SM_DISPCC_6350) += dispcc-sm6350.o
+obj-$(CONFIG_SM_DISPCC_6375) += dispcc-sm6375.o
+obj-$(CONFIG_SM_DISPCC_7150) += dispcc-sm7150.o
obj-$(CONFIG_SM_DISPCC_8250) += dispcc-sm8250.o
+obj-$(CONFIG_SM_DISPCC_8450) += dispcc-sm8450.o
+obj-$(CONFIG_SM_DISPCC_8550) += dispcc-sm8550.o
+obj-$(CONFIG_SM_DISPCC_8750) += dispcc-sm8750.o
+obj-$(CONFIG_SM_DISPCC_MILOS) += dispcc-milos.o
+obj-$(CONFIG_SM_GCC_4450) += gcc-sm4450.o
obj-$(CONFIG_SM_GCC_6115) += gcc-sm6115.o
obj-$(CONFIG_SM_GCC_6125) += gcc-sm6125.o
obj-$(CONFIG_SM_GCC_6350) += gcc-sm6350.o
+obj-$(CONFIG_SM_GCC_6375) += gcc-sm6375.o
+obj-$(CONFIG_SM_GCC_7150) += gcc-sm7150.o
obj-$(CONFIG_SM_GCC_8150) += gcc-sm8150.o
obj-$(CONFIG_SM_GCC_8250) += gcc-sm8250.o
obj-$(CONFIG_SM_GCC_8350) += gcc-sm8350.o
obj-$(CONFIG_SM_GCC_8450) += gcc-sm8450.o
+obj-$(CONFIG_SM_GCC_8550) += gcc-sm8550.o
+obj-$(CONFIG_SM_GCC_8650) += gcc-sm8650.o
+obj-$(CONFIG_SM_GCC_8750) += gcc-sm8750.o
+obj-$(CONFIG_SM_GCC_MILOS) += gcc-milos.o
+obj-$(CONFIG_SM_GPUCC_4450) += gpucc-sm4450.o
+obj-$(CONFIG_SM_GPUCC_6115) += gpucc-sm6115.o
+obj-$(CONFIG_SM_GPUCC_6125) += gpucc-sm6125.o
obj-$(CONFIG_SM_GPUCC_6350) += gpucc-sm6350.o
+obj-$(CONFIG_SM_GPUCC_6375) += gpucc-sm6375.o
obj-$(CONFIG_SM_GPUCC_8150) += gpucc-sm8150.o
obj-$(CONFIG_SM_GPUCC_8250) += gpucc-sm8250.o
obj-$(CONFIG_SM_GPUCC_8350) += gpucc-sm8350.o
+obj-$(CONFIG_SM_GPUCC_8450) += gpucc-sm8450.o
+obj-$(CONFIG_SM_GPUCC_8550) += gpucc-sm8550.o
+obj-$(CONFIG_SM_GPUCC_8650) += gpucc-sm8650.o
+obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o
+obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o
+obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o
+obj-$(CONFIG_SM_TCSRCC_8650) += tcsrcc-sm8650.o
+obj-$(CONFIG_SM_TCSRCC_8750) += tcsrcc-sm8750.o
+obj-$(CONFIG_SM_VIDEOCC_6350) += videocc-sm6350.o
+obj-$(CONFIG_SM_VIDEOCC_7150) += videocc-sm7150.o
obj-$(CONFIG_SM_VIDEOCC_8150) += videocc-sm8150.o
obj-$(CONFIG_SM_VIDEOCC_8250) += videocc-sm8250.o
+obj-$(CONFIG_SM_VIDEOCC_8350) += videocc-sm8350.o
+obj-$(CONFIG_SM_VIDEOCC_8450) += videocc-sm8450.o
+obj-$(CONFIG_SM_VIDEOCC_8550) += videocc-sm8550.o
+obj-$(CONFIG_SM_VIDEOCC_8750) += videocc-sm8750.o
+obj-$(CONFIG_SM_VIDEOCC_MILOS) += videocc-milos.o
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
diff --git a/drivers/clk/qcom/a53-pll.c b/drivers/clk/qcom/a53-pll.c
index 329d2c5356d8..724a642311e5 100644
--- a/drivers/clk/qcom/a53-pll.c
+++ b/drivers/clk/qcom/a53-pll.c
@@ -33,7 +33,6 @@ static const struct regmap_config a53pll_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x40,
- .fast_io = true,
};
static struct pll_freq_tbl *qcom_a53pll_get_freq_tbl(struct device *dev)
@@ -127,7 +126,9 @@ static int qcom_a53pll_probe(struct platform_device *pdev)
if (!init.name)
return -ENOMEM;
- init.parent_names = (const char *[]){ "xo" };
+ init.parent_data = &(const struct clk_parent_data){
+ .fw_name = "xo", .name = "xo_board",
+ };
init.num_parents = 1;
init.ops = &clk_pll_sr2_ops;
pll->clkr.hw.init = &init;
@@ -149,6 +150,7 @@ static int qcom_a53pll_probe(struct platform_device *pdev)
}
static const struct of_device_id qcom_a53pll_match_table[] = {
+ { .compatible = "qcom,msm8226-a7pll" },
{ .compatible = "qcom,msm8916-a53pll" },
{ .compatible = "qcom,msm8939-a53pll" },
{ }
diff --git a/drivers/clk/qcom/a7-pll.c b/drivers/clk/qcom/a7-pll.c
index c4a53e5db229..04b5492a3c21 100644
--- a/drivers/clk/qcom/a7-pll.c
+++ b/drivers/clk/qcom/a7-pll.c
@@ -27,7 +27,7 @@ static struct clk_alpha_pll a7pll = {
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "a7pll",
- .parent_data = &(const struct clk_parent_data){
+ .parent_data = &(const struct clk_parent_data){
.fw_name = "bi_tcxo",
},
.num_parents = 1,
@@ -50,7 +50,6 @@ static const struct regmap_config a7pll_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x1000,
- .fast_io = true,
};
static int qcom_a7pll_probe(struct platform_device *pdev)
diff --git a/drivers/clk/qcom/apcs-msm8916.c b/drivers/clk/qcom/apcs-msm8916.c
index 89e0730810ac..ef31386831eb 100644
--- a/drivers/clk/qcom/apcs-msm8916.c
+++ b/drivers/clk/qcom/apcs-msm8916.c
@@ -119,13 +119,11 @@ err:
return ret;
}
-static int qcom_apcs_msm8916_clk_remove(struct platform_device *pdev)
+static void qcom_apcs_msm8916_clk_remove(struct platform_device *pdev)
{
struct clk_regmap_mux_div *a53cc = platform_get_drvdata(pdev);
clk_notifier_unregister(a53cc->pclk, &a53cc->clk_nb);
-
- return 0;
}
static struct platform_driver qcom_apcs_msm8916_clk_driver = {
diff --git a/drivers/clk/qcom/apcs-msm8996.c b/drivers/clk/qcom/apcs-msm8996.c
new file mode 100644
index 000000000000..3e91e9e6da74
--- /dev/null
+++ b/drivers/clk/qcom/apcs-msm8996.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm APCS clock controller driver
+ *
+ * Copyright (c) 2022, Linaro Limited
+ * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define APCS_AUX_OFFSET 0x50
+
+#define APCS_AUX_DIV_MASK GENMASK(17, 16)
+#define APCS_AUX_DIV_2 0x1
+
+static int qcom_apcs_msm8996_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ unsigned int val;
+ int ret = -ENODEV;
+
+ regmap = dev_get_regmap(parent, NULL);
+ if (!regmap) {
+ dev_err(dev, "failed to get regmap: %d\n", ret);
+ return ret;
+ }
+
+ regmap_read(regmap, APCS_AUX_OFFSET, &val);
+ regmap_update_bits(regmap, APCS_AUX_OFFSET, APCS_AUX_DIV_MASK,
+ FIELD_PREP(APCS_AUX_DIV_MASK, APCS_AUX_DIV_2));
+
+ /*
+ * This clock is used during CPU cluster setup while setting up CPU PLLs.
+ * Add hardware mandated delay to make sure that the sys_apcs_aux clock
+ * is stable (after setting the divider) before continuing
+ * bootstrapping to keep CPUs from ending up in a weird state.
+ */
+ udelay(5);
+
+ /*
+ * As this clocks is a parent of the CPU cluster clocks and is actually
+ * used as a parent during CPU clocks setup, we want for it to register
+ * as early as possible, without letting fw_devlink to delay probing of
+ * either of the drivers.
+ *
+ * The sys_apcs_aux is a child (divider) of gpll0, but we register it
+ * as a fixed rate clock instead to ease bootstrapping procedure. By
+ * doing this we make sure that CPU cluster clocks are able to be setup
+ * early during the boot process (as it is recommended by Qualcomm).
+ */
+ hw = devm_clk_hw_register_fixed_rate(dev, "sys_apcs_aux", NULL, 0, 300000000);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
+static struct platform_driver qcom_apcs_msm8996_clk_driver = {
+ .probe = qcom_apcs_msm8996_clk_probe,
+ .driver = {
+ .name = "qcom-apcs-msm8996-clk",
+ },
+};
+
+/* Register early enough to fix the clock to be used for other cores */
+static int __init qcom_apcs_msm8996_clk_init(void)
+{
+ return platform_driver_register(&qcom_apcs_msm8996_clk_driver);
+}
+postcore_initcall(qcom_apcs_msm8996_clk_init);
+
+static void __exit qcom_apcs_msm8996_clk_exit(void)
+{
+ platform_driver_unregister(&qcom_apcs_msm8996_clk_driver);
+}
+module_exit(qcom_apcs_msm8996_clk_exit);
+
+MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm MSM8996 APCS clock driver");
diff --git a/drivers/clk/qcom/apcs-sdx55.c b/drivers/clk/qcom/apcs-sdx55.c
index e599f862ec44..90dd1f1855c2 100644
--- a/drivers/clk/qcom/apcs-sdx55.c
+++ b/drivers/clk/qcom/apcs-sdx55.c
@@ -111,7 +111,11 @@ static int qcom_apcs_sdx55_clk_probe(struct platform_device *pdev)
* driver, there seems to be no better place to do this. So do it here!
*/
cpu_dev = get_cpu_device(0);
- dev_pm_domain_attach(cpu_dev, true);
+ ret = dev_pm_domain_attach(cpu_dev, PD_FLAG_ATTACH_POWER_ON);
+ if (ret) {
+ dev_err_probe(dev, ret, "can't get PM domain: %d\n", ret);
+ goto err;
+ }
return 0;
@@ -120,15 +124,13 @@ err:
return ret;
}
-static int qcom_apcs_sdx55_clk_remove(struct platform_device *pdev)
+static void qcom_apcs_sdx55_clk_remove(struct platform_device *pdev)
{
struct device *cpu_dev = get_cpu_device(0);
struct clk_regmap_mux_div *a7cc = platform_get_drvdata(pdev);
clk_notifier_unregister(a7cc->pclk, &a7cc->clk_nb);
dev_pm_domain_detach(cpu_dev, true);
-
- return 0;
}
static struct platform_driver qcom_apcs_sdx55_clk_driver = {
diff --git a/drivers/clk/qcom/apss-ipq-pll.c b/drivers/clk/qcom/apss-ipq-pll.c
index bef7899ad0d6..3a8987fe7008 100644
--- a/drivers/clk/qcom/apss-ipq-pll.c
+++ b/drivers/clk/qcom/apss-ipq-pll.c
@@ -2,46 +2,166 @@
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <linux/clk-provider.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "clk-alpha-pll.h"
-static const u8 ipq_pll_offsets[] = {
- [PLL_OFF_L_VAL] = 0x08,
- [PLL_OFF_ALPHA_VAL] = 0x10,
- [PLL_OFF_USER_CTL] = 0x18,
- [PLL_OFF_CONFIG_CTL] = 0x20,
- [PLL_OFF_CONFIG_CTL_U] = 0x24,
- [PLL_OFF_STATUS] = 0x28,
- [PLL_OFF_TEST_CTL] = 0x30,
- [PLL_OFF_TEST_CTL_U] = 0x34,
+static struct clk_alpha_pll ipq_pll_huayra = {
+ .offset = 0x0,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_HUAYRA_APSS],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x0,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "a53pll",
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_huayra_ops,
+ },
+ },
};
-static struct clk_alpha_pll ipq_pll = {
+static struct clk_alpha_pll ipq_pll_stromer = {
.offset = 0x0,
- .regs = ipq_pll_offsets,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_STROMER],
.flags = SUPPORTS_DYNAMIC_UPDATE,
.clkr = {
.enable_reg = 0x0,
.enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
+ .hw.init = &(const struct clk_init_data) {
.name = "a53pll",
.parent_data = &(const struct clk_parent_data) {
.fw_name = "xo",
},
.num_parents = 1,
- .ops = &clk_alpha_pll_huayra_ops,
+ .ops = &clk_alpha_pll_stromer_ops,
},
},
};
-static const struct alpha_pll_config ipq_pll_config = {
+static struct clk_alpha_pll ipq_pll_stromer_plus = {
+ .offset = 0x0,
+ /*
+ * The register offsets of the Stromer Plus PLL used in IPQ5332
+ * are the same as the Stromer PLL's offsets.
+ */
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_STROMER],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x0,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "a53pll",
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_stromer_plus_ops,
+ },
+ },
+};
+
+/* 1.008 GHz configuration */
+static const struct alpha_pll_config ipq5018_pll_config = {
+ .l = 0x2a,
+ .config_ctl_val = 0x4001075b,
+ .main_output_mask = BIT(0),
+ .aux_output_mask = BIT(1),
+ .early_output_mask = BIT(3),
+ .status_val = 0x3,
+ .status_mask = GENMASK(10, 8),
+ .lock_det = BIT(2),
+ .test_ctl_hi_val = 0x00400003,
+};
+
+/* 1.080 GHz configuration */
+static const struct alpha_pll_config ipq5332_pll_config = {
+ .l = 0x2d,
+ .config_ctl_val = 0x4001075b,
+ .main_output_mask = BIT(0),
+ .aux_output_mask = BIT(1),
+ .early_output_mask = BIT(3),
+ .status_val = 0x3,
+ .status_mask = GENMASK(10, 8),
+ .lock_det = BIT(2),
+ .test_ctl_hi_val = 0x00400003,
+};
+
+static const struct alpha_pll_config ipq6018_pll_config = {
.l = 0x37,
- .config_ctl_val = 0x04141200,
- .config_ctl_hi_val = 0x0,
+ .config_ctl_val = 0x240d4828,
+ .config_ctl_hi_val = 0x6,
+ .early_output_mask = BIT(3),
+ .aux2_output_mask = BIT(2),
+ .aux_output_mask = BIT(1),
+ .main_output_mask = BIT(0),
+ .test_ctl_val = 0x1c0000C0,
+ .test_ctl_hi_val = 0x4000,
+};
+
+static const struct alpha_pll_config ipq8074_pll_config = {
+ .l = 0x48,
+ .config_ctl_val = 0x200d4828,
+ .config_ctl_hi_val = 0x6,
+ .early_output_mask = BIT(3),
+ .aux2_output_mask = BIT(2),
+ .aux_output_mask = BIT(1),
+ .main_output_mask = BIT(0),
+ .test_ctl_val = 0x1c000000,
+ .test_ctl_hi_val = 0x4000,
+};
+
+static const struct alpha_pll_config ipq9574_pll_config = {
+ .l = 0x3b,
+ .config_ctl_val = 0x200d4828,
+ .config_ctl_hi_val = 0x6,
.early_output_mask = BIT(3),
+ .aux2_output_mask = BIT(2),
+ .aux_output_mask = BIT(1),
.main_output_mask = BIT(0),
+ .test_ctl_val = 0x0,
+ .test_ctl_hi_val = 0x4000,
+};
+
+struct apss_pll_data {
+ int pll_type;
+ struct clk_alpha_pll *pll;
+ const struct alpha_pll_config *pll_config;
+};
+
+static const struct apss_pll_data ipq5018_pll_data = {
+ .pll_type = CLK_ALPHA_PLL_TYPE_STROMER,
+ .pll = &ipq_pll_stromer,
+ .pll_config = &ipq5018_pll_config,
+};
+
+static const struct apss_pll_data ipq5332_pll_data = {
+ .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
+ .pll = &ipq_pll_stromer_plus,
+ .pll_config = &ipq5332_pll_config,
+};
+
+static const struct apss_pll_data ipq8074_pll_data = {
+ .pll_type = CLK_ALPHA_PLL_TYPE_HUAYRA,
+ .pll = &ipq_pll_huayra,
+ .pll_config = &ipq8074_pll_config,
+};
+
+static const struct apss_pll_data ipq6018_pll_data = {
+ .pll_type = CLK_ALPHA_PLL_TYPE_HUAYRA,
+ .pll = &ipq_pll_huayra,
+ .pll_config = &ipq6018_pll_config,
+};
+
+static const struct apss_pll_data ipq9574_pll_data = {
+ .pll_type = CLK_ALPHA_PLL_TYPE_HUAYRA,
+ .pll = &ipq_pll_huayra,
+ .pll_config = &ipq9574_pll_config,
};
static const struct regmap_config ipq_pll_regmap_config = {
@@ -49,11 +169,11 @@ static const struct regmap_config ipq_pll_regmap_config = {
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x40,
- .fast_io = true,
};
static int apss_ipq_pll_probe(struct platform_device *pdev)
{
+ const struct apss_pll_data *data;
struct device *dev = &pdev->dev;
struct regmap *regmap;
void __iomem *base;
@@ -67,18 +187,30 @@ static int apss_ipq_pll_probe(struct platform_device *pdev)
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- clk_alpha_pll_configure(&ipq_pll, regmap, &ipq_pll_config);
+ data = of_device_get_match_data(&pdev->dev);
+ if (!data)
+ return -ENODEV;
+
+ if (data->pll_type == CLK_ALPHA_PLL_TYPE_HUAYRA)
+ clk_alpha_pll_configure(data->pll, regmap, data->pll_config);
+ else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER ||
+ data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS)
+ clk_stromer_pll_configure(data->pll, regmap, data->pll_config);
- ret = devm_clk_register_regmap(dev, &ipq_pll.clkr);
+ ret = devm_clk_register_regmap(dev, &data->pll->clkr);
if (ret)
return ret;
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
- &ipq_pll.clkr.hw);
+ &data->pll->clkr.hw);
}
static const struct of_device_id apss_ipq_pll_match_table[] = {
- { .compatible = "qcom,ipq6018-a53pll" },
+ { .compatible = "qcom,ipq5018-a53pll", .data = &ipq5018_pll_data },
+ { .compatible = "qcom,ipq5332-a53pll", .data = &ipq5332_pll_data },
+ { .compatible = "qcom,ipq6018-a53pll", .data = &ipq6018_pll_data },
+ { .compatible = "qcom,ipq8074-a53pll", .data = &ipq8074_pll_data },
+ { .compatible = "qcom,ipq9574-a73pll", .data = &ipq9574_pll_data },
{ }
};
MODULE_DEVICE_TABLE(of, apss_ipq_pll_match_table);
diff --git a/drivers/clk/qcom/apss-ipq5424.c b/drivers/clk/qcom/apss-ipq5424.c
new file mode 100644
index 000000000000..2d622c1fe5d0
--- /dev/null
+++ b/drivers/clk/qcom/apss-ipq5424.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/interconnect-provider.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/arm/qcom,ids.h>
+#include <dt-bindings/clock/qcom,apss-ipq.h>
+#include <dt-bindings/interconnect/qcom,ipq5424.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "common.h"
+
+enum {
+ DT_XO,
+ DT_CLK_REF,
+};
+
+enum {
+ P_XO,
+ P_GPLL0,
+ P_APSS_PLL_EARLY,
+ P_L3_PLL,
+};
+
+static const struct alpha_pll_config apss_pll_config = {
+ .l = 0x3b,
+ .config_ctl_val = 0x08200920,
+ .config_ctl_hi_val = 0x05008001,
+ .config_ctl_hi1_val = 0x04000000,
+ .user_ctl_val = 0xf,
+};
+
+static struct clk_alpha_pll ipq5424_apss_pll = {
+ .offset = 0x0,
+ .config = &apss_pll_config,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_HUAYRA_2290],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "apss_pll",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_XO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_huayra_ops,
+ },
+ },
+};
+
+static const struct clk_parent_data parents_apss_silver_clk_src[] = {
+ { .index = DT_XO },
+ { .index = DT_CLK_REF },
+ { .hw = &ipq5424_apss_pll.clkr.hw },
+};
+
+static const struct parent_map parents_apss_silver_clk_src_map[] = {
+ { P_XO, 0 },
+ { P_GPLL0, 4 },
+ { P_APSS_PLL_EARLY, 5 },
+};
+
+static const struct freq_tbl ftbl_apss_clk_src[] = {
+ F(816000000, P_APSS_PLL_EARLY, 1, 0, 0),
+ F(1416000000, P_APSS_PLL_EARLY, 1, 0, 0),
+ F(1800000000, P_APSS_PLL_EARLY, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 apss_silver_clk_src = {
+ .cmd_rcgr = 0x0080,
+ .freq_tbl = ftbl_apss_clk_src,
+ .hid_width = 5,
+ .parent_map = parents_apss_silver_clk_src_map,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "apss_silver_clk_src",
+ .parent_data = parents_apss_silver_clk_src,
+ .num_parents = ARRAY_SIZE(parents_apss_silver_clk_src),
+ .ops = &clk_rcg2_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_branch apss_silver_core_clk = {
+ .halt_reg = 0x008c,
+ .clkr = {
+ .enable_reg = 0x008c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "apss_silver_core_clk",
+ .parent_hws = (const struct clk_hw *[]) {
+ &apss_silver_clk_src.clkr.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static const struct alpha_pll_config l3_pll_config = {
+ .l = 0x29,
+ .config_ctl_val = 0x08200920,
+ .config_ctl_hi_val = 0x05008001,
+ .config_ctl_hi1_val = 0x04000000,
+ .user_ctl_val = 0xf,
+};
+
+static struct clk_alpha_pll ipq5424_l3_pll = {
+ .offset = 0x10000,
+ .config = &l3_pll_config,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_HUAYRA_2290],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "l3_pll",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_XO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_huayra_ops,
+ },
+ },
+};
+
+static const struct clk_parent_data parents_l3_clk_src[] = {
+ { .index = DT_XO },
+ { .index = DT_CLK_REF },
+ { .hw = &ipq5424_l3_pll.clkr.hw },
+};
+
+static const struct parent_map parents_l3_clk_src_map[] = {
+ { P_XO, 0 },
+ { P_GPLL0, 4 },
+ { P_L3_PLL, 5 },
+};
+
+static const struct freq_tbl ftbl_l3_clk_src[] = {
+ F(816000000, P_L3_PLL, 1, 0, 0),
+ F(984000000, P_L3_PLL, 1, 0, 0),
+ F(1272000000, P_L3_PLL, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 l3_clk_src = {
+ .cmd_rcgr = 0x10080,
+ .freq_tbl = ftbl_l3_clk_src,
+ .hid_width = 5,
+ .parent_map = parents_l3_clk_src_map,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "l3_clk_src",
+ .parent_data = parents_l3_clk_src,
+ .num_parents = ARRAY_SIZE(parents_l3_clk_src),
+ .ops = &clk_rcg2_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_branch l3_core_clk = {
+ .halt_reg = 0x1008c,
+ .clkr = {
+ .enable_reg = 0x1008c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data) {
+ .name = "l3_clk",
+ .parent_hws = (const struct clk_hw *[]) {
+ &l3_clk_src.clkr.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static const struct regmap_config apss_ipq5424_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x20000,
+ .fast_io = true,
+};
+
+static struct clk_regmap *apss_ipq5424_clks[] = {
+ [APSS_PLL_EARLY] = &ipq5424_apss_pll.clkr,
+ [APSS_SILVER_CLK_SRC] = &apss_silver_clk_src.clkr,
+ [APSS_SILVER_CORE_CLK] = &apss_silver_core_clk.clkr,
+ [L3_PLL] = &ipq5424_l3_pll.clkr,
+ [L3_CLK_SRC] = &l3_clk_src.clkr,
+ [L3_CORE_CLK] = &l3_core_clk.clkr,
+};
+
+static struct clk_alpha_pll *ipa5424_apss_plls[] = {
+ &ipq5424_l3_pll,
+ &ipq5424_apss_pll,
+};
+
+static struct qcom_cc_driver_data ipa5424_apss_driver_data = {
+ .alpha_plls = ipa5424_apss_plls,
+ .num_alpha_plls = ARRAY_SIZE(ipa5424_apss_plls),
+};
+
+#define IPQ_APPS_PLL_ID (5424 * 3) /* some unique value */
+
+static const struct qcom_icc_hws_data icc_ipq5424_cpu_l3[] = {
+ { MASTER_CPU, SLAVE_L3, L3_CORE_CLK },
+};
+
+static const struct qcom_cc_desc apss_ipq5424_desc = {
+ .config = &apss_ipq5424_regmap_config,
+ .clks = apss_ipq5424_clks,
+ .num_clks = ARRAY_SIZE(apss_ipq5424_clks),
+ .icc_hws = icc_ipq5424_cpu_l3,
+ .num_icc_hws = ARRAY_SIZE(icc_ipq5424_cpu_l3),
+ .icc_first_node_id = IPQ_APPS_PLL_ID,
+ .driver_data = &ipa5424_apss_driver_data,
+};
+
+static int apss_ipq5424_probe(struct platform_device *pdev)
+{
+ return qcom_cc_probe(pdev, &apss_ipq5424_desc);
+}
+
+static const struct of_device_id apss_ipq5424_match_table[] = {
+ { .compatible = "qcom,ipq5424-apss-clk" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apss_ipq5424_match_table);
+
+static struct platform_driver apss_ipq5424_driver = {
+ .probe = apss_ipq5424_probe,
+ .driver = {
+ .name = "apss-ipq5424-clk",
+ .of_match_table = apss_ipq5424_match_table,
+ .sync_state = icc_sync_state,
+ },
+};
+
+module_platform_driver(apss_ipq5424_driver);
+
+MODULE_DESCRIPTION("QCOM APSS IPQ5424 CLK Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/qcom/apss-ipq6018.c b/drivers/clk/qcom/apss-ipq6018.c
index d78ff2f310bf..c89d126ebac3 100644
--- a/drivers/clk/qcom/apss-ipq6018.c
+++ b/drivers/clk/qcom/apss-ipq6018.c
@@ -9,40 +9,45 @@
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/soc/qcom/smem.h>
#include <dt-bindings/clock/qcom,apss-ipq.h>
+#include <dt-bindings/arm/qcom,ids.h>
#include "common.h"
#include "clk-regmap.h"
#include "clk-branch.h"
#include "clk-alpha-pll.h"
-#include "clk-regmap-mux.h"
+#include "clk-rcg.h"
enum {
P_XO,
+ P_GPLL0,
P_APSS_PLL_EARLY,
};
static const struct clk_parent_data parents_apcs_alias0_clk_src[] = {
{ .fw_name = "xo" },
+ { .fw_name = "gpll0" },
{ .fw_name = "pll" },
};
static const struct parent_map parents_apcs_alias0_clk_src_map[] = {
{ P_XO, 0 },
+ { P_GPLL0, 4 },
{ P_APSS_PLL_EARLY, 5 },
};
-static struct clk_regmap_mux apcs_alias0_clk_src = {
- .reg = 0x0050,
- .width = 3,
- .shift = 7,
+static struct clk_rcg2 apcs_alias0_clk_src = {
+ .cmd_rcgr = 0x0050,
+ .hid_width = 5,
.parent_map = parents_apcs_alias0_clk_src_map,
.clkr.hw.init = &(struct clk_init_data){
.name = "apcs_alias0_clk_src",
.parent_data = parents_apcs_alias0_clk_src,
- .num_parents = 2,
- .ops = &clk_regmap_mux_closest_ops,
+ .num_parents = ARRAY_SIZE(parents_apcs_alias0_clk_src),
+ .ops = &clk_rcg2_mux_closest_ops,
.flags = CLK_SET_RATE_PARENT,
},
};
@@ -57,7 +62,7 @@ static struct clk_branch apcs_alias0_core_clk = {
.parent_hws = (const struct clk_hw *[]){
&apcs_alias0_clk_src.clkr.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
.ops = &clk_branch2_ops,
},
},
@@ -82,15 +87,68 @@ static const struct qcom_cc_desc apss_ipq6018_desc = {
.num_clks = ARRAY_SIZE(apss_ipq6018_clks),
};
+static int cpu_clk_notifier_fn(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct clk_hw *hw;
+ u8 index;
+ int err;
+
+ if (action == PRE_RATE_CHANGE)
+ index = P_GPLL0;
+ else if (action == POST_RATE_CHANGE || action == ABORT_RATE_CHANGE)
+ index = P_APSS_PLL_EARLY;
+ else
+ return NOTIFY_OK;
+
+ hw = &apcs_alias0_clk_src.clkr.hw;
+ err = clk_rcg2_mux_closest_ops.set_parent(hw, index);
+
+ return notifier_from_errno(err);
+}
+
static int apss_ipq6018_probe(struct platform_device *pdev)
{
+ struct clk_hw *hw = &apcs_alias0_clk_src.clkr.hw;
+ struct notifier_block *cpu_clk_notifier;
struct regmap *regmap;
+ u32 soc_id;
+ int ret;
+
+ ret = qcom_smem_get_soc_id(&soc_id);
+ if (ret)
+ return ret;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap)
return -ENODEV;
- return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap);
+ ret = qcom_cc_really_probe(&pdev->dev, &apss_ipq6018_desc, regmap);
+ if (ret)
+ return ret;
+
+ switch (soc_id) {
+ /* Only below variants of IPQ53xx support scaling */
+ case QCOM_ID_IPQ5332:
+ case QCOM_ID_IPQ5322:
+ case QCOM_ID_IPQ5300:
+ cpu_clk_notifier = devm_kzalloc(&pdev->dev,
+ sizeof(*cpu_clk_notifier),
+ GFP_KERNEL);
+ if (!cpu_clk_notifier)
+ return -ENOMEM;
+
+ cpu_clk_notifier->notifier_call = cpu_clk_notifier_fn;
+
+ ret = devm_clk_notifier_register(&pdev->dev, hw->clk, cpu_clk_notifier);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
}
static struct platform_driver apss_ipq6018_driver = {
diff --git a/drivers/clk/qcom/camcc-milos.c b/drivers/clk/qcom/camcc-milos.c
new file mode 100644
index 000000000000..0077c9c9249f
--- /dev/null
+++ b/drivers/clk/qcom/camcc-milos.c
@@ -0,0 +1,2161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2025, Luca Weiss <luca.weiss@fairphone.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,milos-camcc.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+#include "clk-regmap-mux.h"
+#include "common.h"
+#include "gdsc.h"
+#include "reset.h"
+
+/* Need to match the order of clocks in DT binding */
+enum {
+ DT_BI_TCXO,
+ DT_SLEEP_CLK,
+ DT_IFACE,
+};
+
+enum {
+ P_BI_TCXO,
+ P_CAM_CC_PLL0_OUT_EVEN,
+ P_CAM_CC_PLL0_OUT_MAIN,
+ P_CAM_CC_PLL0_OUT_ODD,
+ P_CAM_CC_PLL1_OUT_EVEN,
+ P_CAM_CC_PLL1_OUT_MAIN,
+ P_CAM_CC_PLL2_OUT_MAIN,
+ P_CAM_CC_PLL3_OUT_EVEN,
+ P_CAM_CC_PLL4_OUT_EVEN,
+ P_CAM_CC_PLL4_OUT_MAIN,
+ P_CAM_CC_PLL5_OUT_EVEN,
+ P_CAM_CC_PLL5_OUT_MAIN,
+ P_CAM_CC_PLL6_OUT_EVEN,
+ P_CAM_CC_PLL6_OUT_MAIN,
+ P_SLEEP_CLK,
+};
+
+static const struct pll_vco lucid_ole_vco[] = {
+ { 249600000, 2300000000, 0 },
+};
+
+static const struct pll_vco rivian_ole_vco[] = {
+ { 777000000, 1285000000, 0 },
+};
+
+/* 1200.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll0_config = {
+ .l = 0x3e,
+ .alpha = 0x8000,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00182261,
+ .config_ctl_hi1_val = 0x82aa299c,
+ .test_ctl_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000003,
+ .test_ctl_hi1_val = 0x00009000,
+ .test_ctl_hi2_val = 0x00000034,
+ .user_ctl_val = 0x00008400,
+ .user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll cam_cc_pll0 = {
+ .offset = 0x0,
+ .config = &cam_cc_pll0_config,
+ .vco_table = lucid_ole_vco,
+ .num_vco = ARRAY_SIZE(lucid_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll0",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_evo_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = {
+ .offset = 0x0,
+ .post_div_shift = 10,
+ .post_div_table = post_div_table_cam_cc_pll0_out_even,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll0_out_even",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll0.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll0_out_odd[] = {
+ { 0x2, 3 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll0_out_odd = {
+ .offset = 0x0,
+ .post_div_shift = 14,
+ .post_div_table = post_div_table_cam_cc_pll0_out_odd,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_odd),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll0_out_odd",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll0.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+/* 600.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll1_config = {
+ .l = 0x1f,
+ .alpha = 0x4000,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00182261,
+ .config_ctl_hi1_val = 0x82aa299c,
+ .test_ctl_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000003,
+ .test_ctl_hi1_val = 0x00009000,
+ .test_ctl_hi2_val = 0x00000034,
+ .user_ctl_val = 0x00000400,
+ .user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll cam_cc_pll1 = {
+ .offset = 0x1000,
+ .config = &cam_cc_pll1_config,
+ .vco_table = lucid_ole_vco,
+ .num_vco = ARRAY_SIZE(lucid_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll1",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_evo_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll1_out_even[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = {
+ .offset = 0x1000,
+ .post_div_shift = 10,
+ .post_div_table = post_div_table_cam_cc_pll1_out_even,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll1_out_even),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll1_out_even",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll1.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+/* 960.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll2_config = {
+ .l = 0x32,
+ .alpha = 0x0,
+ .config_ctl_val = 0x10000030,
+ .config_ctl_hi_val = 0x80890263,
+ .config_ctl_hi1_val = 0x00000217,
+ .user_ctl_val = 0x00000001,
+ .user_ctl_hi_val = 0x00100000,
+};
+
+static struct clk_alpha_pll cam_cc_pll2 = {
+ .offset = 0x2000,
+ .config = &cam_cc_pll2_config,
+ .vco_table = rivian_ole_vco,
+ .num_vco = ARRAY_SIZE(rivian_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll2",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_rivian_evo_ops,
+ },
+ },
+};
+
+/* 600.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll3_config = {
+ .l = 0x1f,
+ .alpha = 0x4000,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00182261,
+ .config_ctl_hi1_val = 0x82aa299c,
+ .test_ctl_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000003,
+ .test_ctl_hi1_val = 0x00009000,
+ .test_ctl_hi2_val = 0x00000034,
+ .user_ctl_val = 0x00000400,
+ .user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll cam_cc_pll3 = {
+ .offset = 0x3000,
+ .config = &cam_cc_pll3_config,
+ .vco_table = lucid_ole_vco,
+ .num_vco = ARRAY_SIZE(lucid_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll3",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_evo_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll3_out_even[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll3_out_even = {
+ .offset = 0x3000,
+ .post_div_shift = 10,
+ .post_div_table = post_div_table_cam_cc_pll3_out_even,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll3_out_even),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll3_out_even",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll3.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+/* 700.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll4_config = {
+ .l = 0x24,
+ .alpha = 0x7555,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00182261,
+ .config_ctl_hi1_val = 0x82aa299c,
+ .test_ctl_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000003,
+ .test_ctl_hi1_val = 0x00009000,
+ .test_ctl_hi2_val = 0x00000034,
+ .user_ctl_val = 0x00000400,
+ .user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll cam_cc_pll4 = {
+ .offset = 0x4000,
+ .config = &cam_cc_pll4_config,
+ .vco_table = lucid_ole_vco,
+ .num_vco = ARRAY_SIZE(lucid_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll4",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_evo_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll4_out_even[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll4_out_even = {
+ .offset = 0x4000,
+ .post_div_shift = 10,
+ .post_div_table = post_div_table_cam_cc_pll4_out_even,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll4_out_even),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll4_out_even",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll4.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+/* 700.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll5_config = {
+ .l = 0x24,
+ .alpha = 0x7555,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00182261,
+ .config_ctl_hi1_val = 0x82aa299c,
+ .test_ctl_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000003,
+ .test_ctl_hi1_val = 0x00009000,
+ .test_ctl_hi2_val = 0x00000034,
+ .user_ctl_val = 0x00000400,
+ .user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll cam_cc_pll5 = {
+ .offset = 0x5000,
+ .config = &cam_cc_pll5_config,
+ .vco_table = lucid_ole_vco,
+ .num_vco = ARRAY_SIZE(lucid_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll5",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_evo_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll5_out_even[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll5_out_even = {
+ .offset = 0x5000,
+ .post_div_shift = 10,
+ .post_div_table = post_div_table_cam_cc_pll5_out_even,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll5_out_even),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll5_out_even",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll5.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+/* 700.0 MHz Configuration */
+static const struct alpha_pll_config cam_cc_pll6_config = {
+ .l = 0x24,
+ .alpha = 0x7555,
+ .config_ctl_val = 0x20485699,
+ .config_ctl_hi_val = 0x00182261,
+ .config_ctl_hi1_val = 0x82aa299c,
+ .test_ctl_val = 0x00000000,
+ .test_ctl_hi_val = 0x00000003,
+ .test_ctl_hi1_val = 0x00009000,
+ .test_ctl_hi2_val = 0x00000034,
+ .user_ctl_val = 0x00000400,
+ .user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll cam_cc_pll6 = {
+ .offset = 0x6000,
+ .config = &cam_cc_pll6_config,
+ .vco_table = lucid_ole_vco,
+ .num_vco = ARRAY_SIZE(lucid_ole_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll6",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_lucid_evo_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll6_out_even[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll6_out_even = {
+ .offset = 0x6000,
+ .post_div_shift = 10,
+ .post_div_table = post_div_table_cam_cc_pll6_out_even,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll6_out_even),
+ .width = 4,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll6_out_even",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll6.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_alpha_pll_postdiv_lucid_ole_ops,
+ },
+};
+
+static const struct parent_map cam_cc_parent_map_0[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL0_OUT_ODD, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_0[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll0_out_odd.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_1[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL2_OUT_MAIN, 4 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_1[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll2.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_2[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL1_OUT_MAIN, 2 },
+ { P_CAM_CC_PLL1_OUT_EVEN, 3 },
+ { P_CAM_CC_PLL0_OUT_ODD, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_2[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll1.clkr.hw },
+ { .hw = &cam_cc_pll1_out_even.clkr.hw },
+ { .hw = &cam_cc_pll0_out_odd.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_3[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_ODD, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_3[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0_out_odd.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_4[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_4[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_5[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL3_OUT_EVEN, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_5[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll3_out_even.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_6[] = {
+ { P_SLEEP_CLK, 0 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_6_ao[] = {
+ { .index = DT_SLEEP_CLK },
+};
+
+static const struct parent_map cam_cc_parent_map_7[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL4_OUT_EVEN, 2 },
+ { P_CAM_CC_PLL4_OUT_MAIN, 3 },
+ { P_CAM_CC_PLL0_OUT_ODD, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_7[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll4_out_even.clkr.hw },
+ { .hw = &cam_cc_pll4.clkr.hw },
+ { .hw = &cam_cc_pll0_out_odd.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_8[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL5_OUT_EVEN, 2 },
+ { P_CAM_CC_PLL5_OUT_MAIN, 3 },
+ { P_CAM_CC_PLL0_OUT_ODD, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_8[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll5_out_even.clkr.hw },
+ { .hw = &cam_cc_pll5.clkr.hw },
+ { .hw = &cam_cc_pll0_out_odd.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_9[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_MAIN, 1 },
+ { P_CAM_CC_PLL6_OUT_EVEN, 2 },
+ { P_CAM_CC_PLL6_OUT_MAIN, 3 },
+ { P_CAM_CC_PLL0_OUT_ODD, 5 },
+ { P_CAM_CC_PLL0_OUT_EVEN, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_9[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+ { .hw = &cam_cc_pll6_out_even.clkr.hw },
+ { .hw = &cam_cc_pll6.clkr.hw },
+ { .hw = &cam_cc_pll0_out_odd.clkr.hw },
+ { .hw = &cam_cc_pll0_out_even.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_10[] = {
+ { P_BI_TCXO, 0 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_10[] = {
+ { .index = DT_BI_TCXO },
+};
+
+static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = {
+ F(300000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0),
+ F(410000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0),
+ F(460000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0),
+ F(600000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0),
+ F(700000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_bps_clk_src = {
+ .cmd_rcgr = 0x1a004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_2,
+ .freq_tbl = ftbl_cam_cc_bps_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_clk_src",
+ .parent_data = cam_cc_parent_data_2,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_2),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_camnoc_axi_clk_src[] = {
+ F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0),
+ F(240000000, P_CAM_CC_PLL0_OUT_EVEN, 2.5, 0, 0),
+ F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0),
+ F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_camnoc_axi_clk_src = {
+ .cmd_rcgr = 0x2401c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_camnoc_axi_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_axi_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = {
+ F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0),
+ F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 12, 0, 0),
+ F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_cci_0_clk_src = {
+ .cmd_rcgr = 0x21004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_3,
+ .freq_tbl = ftbl_cam_cc_cci_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cci_0_clk_src",
+ .parent_data = cam_cc_parent_data_3,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_3),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_cci_1_clk_src = {
+ .cmd_rcgr = 0x22004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_3,
+ .freq_tbl = ftbl_cam_cc_cci_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cci_1_clk_src",
+ .parent_data = cam_cc_parent_data_3,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_3),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = {
+ F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0),
+ F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0),
+ F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_cphy_rx_clk_src = {
+ .cmd_rcgr = 0x1c05c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cphy_rx_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_cre_clk_src[] = {
+ F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0),
+ F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_cre_clk_src = {
+ .cmd_rcgr = 0x27004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_2,
+ .freq_tbl = ftbl_cam_cc_cre_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cre_clk_src",
+ .parent_data = cam_cc_parent_data_2,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_2),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = {
+ F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = {
+ .cmd_rcgr = 0x19004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi0phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = {
+ .cmd_rcgr = 0x19028,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi1phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = {
+ .cmd_rcgr = 0x1904c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi2phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = {
+ .cmd_rcgr = 0x19070,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi3phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = {
+ F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
+ F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0),
+ F(200000000, P_CAM_CC_PLL0_OUT_MAIN, 6, 0, 0),
+ F(240000000, P_CAM_CC_PLL0_OUT_MAIN, 5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_fast_ahb_clk_src = {
+ .cmd_rcgr = 0x1a030,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_fast_ahb_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = {
+ F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0),
+ F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_icp_clk_src = {
+ .cmd_rcgr = 0x20014,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_4,
+ .freq_tbl = ftbl_cam_cc_icp_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_clk_src",
+ .parent_data = cam_cc_parent_data_4,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_4),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = {
+ F(19200000, P_CAM_CC_PLL2_OUT_MAIN, 1, 1, 50),
+ F(24000000, P_CAM_CC_PLL2_OUT_MAIN, 10, 1, 4),
+ F(64000000, P_CAM_CC_PLL2_OUT_MAIN, 15, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_mclk0_clk_src = {
+ .cmd_rcgr = 0x18004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk0_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk1_clk_src = {
+ .cmd_rcgr = 0x18024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk1_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk2_clk_src = {
+ .cmd_rcgr = 0x18044,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk2_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk3_clk_src = {
+ .cmd_rcgr = 0x18064,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk3_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk4_clk_src = {
+ .cmd_rcgr = 0x18084,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk4_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_ope_0_clk_src[] = {
+ F(300000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
+ F(410000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
+ F(520000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
+ F(645000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
+ F(700000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_ope_0_clk_src = {
+ .cmd_rcgr = 0x1b004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_5,
+ .freq_tbl = ftbl_cam_cc_ope_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ope_0_clk_src",
+ .parent_data = cam_cc_parent_data_5,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_5),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_sleep_clk_src[] = {
+ F(32000, P_SLEEP_CLK, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_sleep_clk_src = {
+ .cmd_rcgr = 0x25044,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_6,
+ .freq_tbl = ftbl_cam_cc_sleep_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_sleep_clk_src",
+ .parent_data = cam_cc_parent_data_6_ao,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_6_ao),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = {
+ F(80000000, P_CAM_CC_PLL0_OUT_EVEN, 7.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_slow_ahb_clk_src = {
+ .cmd_rcgr = 0x1a04c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_slow_ahb_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_tfe_0_clk_src[] = {
+ F(350000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0),
+ F(570000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0),
+ F(600000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0),
+ F(725000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_tfe_0_clk_src = {
+ .cmd_rcgr = 0x1c004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_7,
+ .freq_tbl = ftbl_cam_cc_tfe_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_0_clk_src",
+ .parent_data = cam_cc_parent_data_7,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_7),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_tfe_0_csid_clk_src = {
+ .cmd_rcgr = 0x1c030,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_0_csid_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_tfe_1_clk_src[] = {
+ F(350000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0),
+ F(570000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0),
+ F(600000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0),
+ F(725000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_tfe_1_clk_src = {
+ .cmd_rcgr = 0x1d004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_8,
+ .freq_tbl = ftbl_cam_cc_tfe_1_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_1_clk_src",
+ .parent_data = cam_cc_parent_data_8,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_8),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_tfe_1_csid_clk_src = {
+ .cmd_rcgr = 0x1d030,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_1_csid_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_tfe_2_clk_src[] = {
+ F(350000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0),
+ F(570000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0),
+ F(600000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0),
+ F(725000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_tfe_2_clk_src = {
+ .cmd_rcgr = 0x1e004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_9,
+ .freq_tbl = ftbl_cam_cc_tfe_2_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_2_clk_src",
+ .parent_data = cam_cc_parent_data_9,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_9),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_tfe_2_csid_clk_src = {
+ .cmd_rcgr = 0x1e030,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_2_csid_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_xo_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_xo_clk_src = {
+ .cmd_rcgr = 0x25020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_10,
+ .freq_tbl = ftbl_cam_cc_xo_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_xo_clk_src",
+ .parent_data = cam_cc_parent_data_10,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_10),
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_branch cam_cc_bps_ahb_clk = {
+ .halt_reg = 0x1a064,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1a064,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_bps_areg_clk = {
+ .halt_reg = 0x1a048,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1a048,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_areg_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_fast_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_bps_clk = {
+ .halt_reg = 0x1a01c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1a01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_bps_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_camnoc_atb_clk = {
+ .halt_reg = 0x24040,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x24040,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_atb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_camnoc_axi_hf_clk = {
+ .halt_reg = 0x24010,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x24010,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_axi_hf_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_camnoc_axi_sf_clk = {
+ .halt_reg = 0x24004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x24004,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_axi_sf_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_camnoc_nrt_axi_clk = {
+ .halt_reg = 0x2404c,
+ .halt_check = BRANCH_HALT_VOTED,
+ .hwcg_reg = 0x2404c,
+ .hwcg_bit = 1,
+ .clkr = {
+ .enable_reg = 0x2404c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_nrt_axi_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_camnoc_axi_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_camnoc_rt_axi_clk = {
+ .halt_reg = 0x24034,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x24034,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_rt_axi_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_camnoc_axi_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cci_0_clk = {
+ .halt_reg = 0x2101c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2101c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cci_0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cci_0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cci_1_clk = {
+ .halt_reg = 0x2201c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2201c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cci_1_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cci_1_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_core_ahb_clk = {
+ .halt_reg = 0x2501c,
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x2501c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_core_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cpas_ahb_clk = {
+ .halt_reg = 0x23004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x23004,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cpas_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cre_ahb_clk = {
+ .halt_reg = 0x27020,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x27020,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cre_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cre_clk = {
+ .halt_reg = 0x2701c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2701c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cre_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cre_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi0phytimer_clk = {
+ .halt_reg = 0x1901c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1901c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi0phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi0phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi1phytimer_clk = {
+ .halt_reg = 0x19040,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19040,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi1phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi1phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi2phytimer_clk = {
+ .halt_reg = 0x19064,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19064,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi2phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi2phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi3phytimer_clk = {
+ .halt_reg = 0x19088,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19088,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi3phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi3phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy0_clk = {
+ .halt_reg = 0x19020,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19020,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy1_clk = {
+ .halt_reg = 0x19044,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19044,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy1_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy2_clk = {
+ .halt_reg = 0x19068,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19068,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy2_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy3_clk = {
+ .halt_reg = 0x1908c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1908c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy3_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_icp_atb_clk = {
+ .halt_reg = 0x20004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x20004,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_atb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_icp_clk = {
+ .halt_reg = 0x2002c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2002c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_icp_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_icp_cti_clk = {
+ .halt_reg = 0x20008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x20008,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_cti_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_icp_ts_clk = {
+ .halt_reg = 0x2000c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2000c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_ts_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_mclk0_clk = {
+ .halt_reg = 0x1801c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1801c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_mclk0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_mclk1_clk = {
+ .halt_reg = 0x1803c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1803c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk1_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_mclk1_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_mclk2_clk = {
+ .halt_reg = 0x1805c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1805c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk2_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_mclk2_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_mclk3_clk = {
+ .halt_reg = 0x1807c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1807c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk3_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_mclk3_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_mclk4_clk = {
+ .halt_reg = 0x1809c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1809c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk4_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_mclk4_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ope_0_ahb_clk = {
+ .halt_reg = 0x1b034,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1b034,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ope_0_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ope_0_areg_clk = {
+ .halt_reg = 0x1b030,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1b030,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ope_0_areg_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_fast_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ope_0_clk = {
+ .halt_reg = 0x1b01c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1b01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ope_0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ope_0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_soc_ahb_clk = {
+ .halt_reg = 0x25018,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x25018,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_soc_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_sys_tmr_clk = {
+ .halt_reg = 0x20038,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x20038,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_sys_tmr_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_xo_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_0_ahb_clk = {
+ .halt_reg = 0x1c078,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1c078,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_0_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_0_clk = {
+ .halt_reg = 0x1c01c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1c01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_tfe_0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_0_cphy_rx_clk = {
+ .halt_reg = 0x1c074,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1c074,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_0_cphy_rx_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_0_csid_clk = {
+ .halt_reg = 0x1c048,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1c048,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_0_csid_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_tfe_0_csid_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_1_ahb_clk = {
+ .halt_reg = 0x1d058,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1d058,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_1_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_1_clk = {
+ .halt_reg = 0x1d01c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1d01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_1_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_tfe_1_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_1_cphy_rx_clk = {
+ .halt_reg = 0x1d054,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1d054,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_1_cphy_rx_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_1_csid_clk = {
+ .halt_reg = 0x1d048,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1d048,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_1_csid_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_tfe_1_csid_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_2_ahb_clk = {
+ .halt_reg = 0x1e058,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1e058,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_2_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_2_clk = {
+ .halt_reg = 0x1e01c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1e01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_2_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_tfe_2_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_2_cphy_rx_clk = {
+ .halt_reg = 0x1e054,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1e054,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_2_cphy_rx_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_tfe_2_csid_clk = {
+ .halt_reg = 0x1e048,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1e048,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_tfe_2_csid_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_tfe_2_csid_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_top_shift_clk = {
+ .halt_reg = 0x25040,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x25040,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_top_shift_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_xo_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct gdsc cam_cc_camss_top_gdsc = {
+ .gdscr = 0x25004,
+ .en_rest_wait_val = 0x2,
+ .en_few_wait_val = 0x2,
+ .clk_dis_wait_val = 0xf,
+ .pd = {
+ .name = "cam_cc_camss_top_gdsc",
+ },
+ .pwrsts = PWRSTS_OFF_ON,
+ .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE,
+};
+
+static struct clk_regmap *cam_cc_milos_clocks[] = {
+ [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr,
+ [CAM_CC_BPS_AREG_CLK] = &cam_cc_bps_areg_clk.clkr,
+ [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr,
+ [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr,
+ [CAM_CC_CAMNOC_ATB_CLK] = &cam_cc_camnoc_atb_clk.clkr,
+ [CAM_CC_CAMNOC_AXI_CLK_SRC] = &cam_cc_camnoc_axi_clk_src.clkr,
+ [CAM_CC_CAMNOC_AXI_HF_CLK] = &cam_cc_camnoc_axi_hf_clk.clkr,
+ [CAM_CC_CAMNOC_AXI_SF_CLK] = &cam_cc_camnoc_axi_sf_clk.clkr,
+ [CAM_CC_CAMNOC_NRT_AXI_CLK] = &cam_cc_camnoc_nrt_axi_clk.clkr,
+ [CAM_CC_CAMNOC_RT_AXI_CLK] = &cam_cc_camnoc_rt_axi_clk.clkr,
+ [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr,
+ [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr,
+ [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr,
+ [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr,
+ [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr,
+ [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr,
+ [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr,
+ [CAM_CC_CRE_AHB_CLK] = &cam_cc_cre_ahb_clk.clkr,
+ [CAM_CC_CRE_CLK] = &cam_cc_cre_clk.clkr,
+ [CAM_CC_CRE_CLK_SRC] = &cam_cc_cre_clk_src.clkr,
+ [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr,
+ [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr,
+ [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr,
+ [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr,
+ [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr,
+ [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr,
+ [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr,
+ [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr,
+ [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr,
+ [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr,
+ [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr,
+ [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr,
+ [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr,
+ [CAM_CC_ICP_ATB_CLK] = &cam_cc_icp_atb_clk.clkr,
+ [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr,
+ [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr,
+ [CAM_CC_ICP_CTI_CLK] = &cam_cc_icp_cti_clk.clkr,
+ [CAM_CC_ICP_TS_CLK] = &cam_cc_icp_ts_clk.clkr,
+ [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr,
+ [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr,
+ [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr,
+ [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr,
+ [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr,
+ [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr,
+ [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr,
+ [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr,
+ [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr,
+ [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr,
+ [CAM_CC_OPE_0_AHB_CLK] = &cam_cc_ope_0_ahb_clk.clkr,
+ [CAM_CC_OPE_0_AREG_CLK] = &cam_cc_ope_0_areg_clk.clkr,
+ [CAM_CC_OPE_0_CLK] = &cam_cc_ope_0_clk.clkr,
+ [CAM_CC_OPE_0_CLK_SRC] = &cam_cc_ope_0_clk_src.clkr,
+ [CAM_CC_PLL0] = &cam_cc_pll0.clkr,
+ [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr,
+ [CAM_CC_PLL0_OUT_ODD] = &cam_cc_pll0_out_odd.clkr,
+ [CAM_CC_PLL1] = &cam_cc_pll1.clkr,
+ [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr,
+ [CAM_CC_PLL2] = &cam_cc_pll2.clkr,
+ [CAM_CC_PLL3] = &cam_cc_pll3.clkr,
+ [CAM_CC_PLL3_OUT_EVEN] = &cam_cc_pll3_out_even.clkr,
+ [CAM_CC_PLL4] = &cam_cc_pll4.clkr,
+ [CAM_CC_PLL4_OUT_EVEN] = &cam_cc_pll4_out_even.clkr,
+ [CAM_CC_PLL5] = &cam_cc_pll5.clkr,
+ [CAM_CC_PLL5_OUT_EVEN] = &cam_cc_pll5_out_even.clkr,
+ [CAM_CC_PLL6] = &cam_cc_pll6.clkr,
+ [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr,
+ [CAM_CC_SLEEP_CLK_SRC] = &cam_cc_sleep_clk_src.clkr,
+ [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr,
+ [CAM_CC_SOC_AHB_CLK] = &cam_cc_soc_ahb_clk.clkr,
+ [CAM_CC_SYS_TMR_CLK] = &cam_cc_sys_tmr_clk.clkr,
+ [CAM_CC_TFE_0_AHB_CLK] = &cam_cc_tfe_0_ahb_clk.clkr,
+ [CAM_CC_TFE_0_CLK] = &cam_cc_tfe_0_clk.clkr,
+ [CAM_CC_TFE_0_CLK_SRC] = &cam_cc_tfe_0_clk_src.clkr,
+ [CAM_CC_TFE_0_CPHY_RX_CLK] = &cam_cc_tfe_0_cphy_rx_clk.clkr,
+ [CAM_CC_TFE_0_CSID_CLK] = &cam_cc_tfe_0_csid_clk.clkr,
+ [CAM_CC_TFE_0_CSID_CLK_SRC] = &cam_cc_tfe_0_csid_clk_src.clkr,
+ [CAM_CC_TFE_1_AHB_CLK] = &cam_cc_tfe_1_ahb_clk.clkr,
+ [CAM_CC_TFE_1_CLK] = &cam_cc_tfe_1_clk.clkr,
+ [CAM_CC_TFE_1_CLK_SRC] = &cam_cc_tfe_1_clk_src.clkr,
+ [CAM_CC_TFE_1_CPHY_RX_CLK] = &cam_cc_tfe_1_cphy_rx_clk.clkr,
+ [CAM_CC_TFE_1_CSID_CLK] = &cam_cc_tfe_1_csid_clk.clkr,
+ [CAM_CC_TFE_1_CSID_CLK_SRC] = &cam_cc_tfe_1_csid_clk_src.clkr,
+ [CAM_CC_TFE_2_AHB_CLK] = &cam_cc_tfe_2_ahb_clk.clkr,
+ [CAM_CC_TFE_2_CLK] = &cam_cc_tfe_2_clk.clkr,
+ [CAM_CC_TFE_2_CLK_SRC] = &cam_cc_tfe_2_clk_src.clkr,
+ [CAM_CC_TFE_2_CPHY_RX_CLK] = &cam_cc_tfe_2_cphy_rx_clk.clkr,
+ [CAM_CC_TFE_2_CSID_CLK] = &cam_cc_tfe_2_csid_clk.clkr,
+ [CAM_CC_TFE_2_CSID_CLK_SRC] = &cam_cc_tfe_2_csid_clk_src.clkr,
+ [CAM_CC_TOP_SHIFT_CLK] = &cam_cc_top_shift_clk.clkr,
+ [CAM_CC_XO_CLK_SRC] = &cam_cc_xo_clk_src.clkr,
+};
+
+static const struct qcom_reset_map cam_cc_milos_resets[] = {
+ [CAM_CC_BPS_BCR] = { 0x1a000 },
+ [CAM_CC_CAMNOC_BCR] = { 0x24000 },
+ [CAM_CC_CAMSS_TOP_BCR] = { 0x25000 },
+ [CAM_CC_CCI_0_BCR] = { 0x21000 },
+ [CAM_CC_CCI_1_BCR] = { 0x22000 },
+ [CAM_CC_CPAS_BCR] = { 0x23000 },
+ [CAM_CC_CRE_BCR] = { 0x27000 },
+ [CAM_CC_CSI0PHY_BCR] = { 0x19000 },
+ [CAM_CC_CSI1PHY_BCR] = { 0x19024 },
+ [CAM_CC_CSI2PHY_BCR] = { 0x19048 },
+ [CAM_CC_CSI3PHY_BCR] = { 0x1906c },
+ [CAM_CC_ICP_BCR] = { 0x20000 },
+ [CAM_CC_MCLK0_BCR] = { 0x18000 },
+ [CAM_CC_MCLK1_BCR] = { 0x18020 },
+ [CAM_CC_MCLK2_BCR] = { 0x18040 },
+ [CAM_CC_MCLK3_BCR] = { 0x18060 },
+ [CAM_CC_MCLK4_BCR] = { 0x18080 },
+ [CAM_CC_OPE_0_BCR] = { 0x1b000 },
+ [CAM_CC_TFE_0_BCR] = { 0x1c000 },
+ [CAM_CC_TFE_1_BCR] = { 0x1d000 },
+ [CAM_CC_TFE_2_BCR] = { 0x1e000 },
+};
+
+static struct gdsc *cam_cc_milos_gdscs[] = {
+ [CAM_CC_CAMSS_TOP_GDSC] = &cam_cc_camss_top_gdsc,
+};
+
+static struct clk_alpha_pll *cam_cc_milos_plls[] = {
+ &cam_cc_pll0,
+ &cam_cc_pll1,
+ &cam_cc_pll2,
+ &cam_cc_pll3,
+ &cam_cc_pll4,
+ &cam_cc_pll5,
+ &cam_cc_pll6,
+};
+
+static u32 cam_cc_milos_critical_cbcrs[] = {
+ 0x25038, /* CAM_CC_GDSC_CLK */
+ 0x2505c, /* CAM_CC_SLEEP_CLK */
+};
+
+static const struct regmap_config cam_cc_milos_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x30728,
+ .fast_io = true,
+};
+
+static struct qcom_cc_driver_data cam_cc_milos_driver_data = {
+ .alpha_plls = cam_cc_milos_plls,
+ .num_alpha_plls = ARRAY_SIZE(cam_cc_milos_plls),
+ .clk_cbcrs = cam_cc_milos_critical_cbcrs,
+ .num_clk_cbcrs = ARRAY_SIZE(cam_cc_milos_critical_cbcrs),
+};
+
+static const struct qcom_cc_desc cam_cc_milos_desc = {
+ .config = &cam_cc_milos_regmap_config,
+ .clks = cam_cc_milos_clocks,
+ .num_clks = ARRAY_SIZE(cam_cc_milos_clocks),
+ .resets = cam_cc_milos_resets,
+ .num_resets = ARRAY_SIZE(cam_cc_milos_resets),
+ .gdscs = cam_cc_milos_gdscs,
+ .num_gdscs = ARRAY_SIZE(cam_cc_milos_gdscs),
+ .use_rpm = true,
+ .driver_data = &cam_cc_milos_driver_data,
+};
+
+static const struct of_device_id cam_cc_milos_match_table[] = {
+ { .compatible = "qcom,milos-camcc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cam_cc_milos_match_table);
+
+static int cam_cc_milos_probe(struct platform_device *pdev)
+{
+ return qcom_cc_probe(pdev, &cam_cc_milos_desc);
+}
+
+static struct platform_driver cam_cc_milos_driver = {
+ .probe = cam_cc_milos_probe,
+ .driver = {
+ .name = "cam_cc-milos",
+ .of_match_table = cam_cc_milos_match_table,
+ },
+};
+
+module_platform_driver(cam_cc_milos_driver);
+
+MODULE_DESCRIPTION("QTI CAM_CC Milos Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/qcom/camcc-qcs615.c b/drivers/clk/qcom/camcc-qcs615.c
new file mode 100644
index 000000000000..c063a3bfacd0
--- /dev/null
+++ b/drivers/clk/qcom/camcc-qcs615.c
@@ -0,0 +1,1597 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,qcs615-camcc.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+#include "clk-regmap-mux.h"
+#include "common.h"
+#include "gdsc.h"
+#include "reset.h"
+
+enum {
+ DT_BI_TCXO,
+};
+
+enum {
+ P_BI_TCXO,
+ P_CAM_CC_PLL0_OUT_AUX,
+ P_CAM_CC_PLL1_OUT_AUX,
+ P_CAM_CC_PLL2_OUT_AUX2,
+ P_CAM_CC_PLL2_OUT_EARLY,
+ P_CAM_CC_PLL3_OUT_MAIN,
+};
+
+static const struct pll_vco brammo_vco[] = {
+ { 500000000, 1250000000, 0 },
+};
+
+static const struct pll_vco spark_vco[] = {
+ { 1000000000, 2100000000, 0 },
+ { 750000000, 1500000000, 1 },
+ { 500000000, 1000000000, 2 },
+ { 300000000, 500000000, 3 },
+ { 550000000, 1100000000, 4 },
+};
+
+/* 600MHz configuration VCO - 2 */
+static const struct alpha_pll_config cam_cc_pll0_config = {
+ .l = 0x1f,
+ .alpha_hi = 0x40,
+ .alpha_en_mask = BIT(24),
+ .vco_val = BIT(21),
+ .vco_mask = GENMASK(21, 20),
+ .aux_output_mask = BIT(1),
+ .config_ctl_val = 0x4001055b,
+ .test_ctl_hi_val = 0x1,
+ .test_ctl_hi_mask = 0x1,
+};
+
+static struct clk_alpha_pll cam_cc_pll0 = {
+ .offset = 0x0,
+ .config = &cam_cc_pll0_config,
+ .vco_table = spark_vco,
+ .num_vco = ARRAY_SIZE(spark_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll0",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+/* 808MHz configuration VCO - 2 */
+static struct alpha_pll_config cam_cc_pll1_config = {
+ .l = 0x2a,
+ .alpha_hi = 0x15,
+ .alpha = 0x55555555,
+ .alpha_en_mask = BIT(24),
+ .vco_val = BIT(21),
+ .vco_mask = GENMASK(21, 20),
+ .aux_output_mask = BIT(1),
+ .config_ctl_val = 0x4001055b,
+ .test_ctl_hi_val = 0x1,
+ .test_ctl_hi_mask = 0x1,
+};
+
+static struct clk_alpha_pll cam_cc_pll1 = {
+ .offset = 0x1000,
+ .config = &cam_cc_pll1_config,
+ .vco_table = spark_vco,
+ .num_vco = ARRAY_SIZE(spark_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll1",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+/* 960MHz configuration VCO - 0 */
+static struct alpha_pll_config cam_cc_pll2_config = {
+ .l = 0x32,
+ .vco_val = 0x0,
+ .vco_mask = GENMASK(21, 20),
+ .early_output_mask = BIT(3),
+ .aux2_output_mask = BIT(2),
+ .post_div_val = 0x1 << 8,
+ .post_div_mask = 0x3 << 8,
+ .config_ctl_val = 0x04289,
+ .test_ctl_val = 0x08000000,
+ .test_ctl_mask = 0x08000000,
+};
+
+static struct clk_alpha_pll cam_cc_pll2 = {
+ .offset = 0x2000,
+ .config = &cam_cc_pll2_config,
+ .vco_table = brammo_vco,
+ .num_vco = ARRAY_SIZE(brammo_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_BRAMMO],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll2",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+static const struct clk_div_table post_div_table_cam_cc_pll2_out_aux2[] = {
+ { 0x1, 2 },
+ { }
+};
+
+static struct clk_alpha_pll_postdiv cam_cc_pll2_out_aux2 = {
+ .offset = 0x2000,
+ .post_div_shift = 8,
+ .post_div_table = post_div_table_cam_cc_pll2_out_aux2,
+ .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll2_out_aux2),
+ .width = 2,
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_BRAMMO],
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll2_out_aux2",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_pll2.clkr.hw,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_postdiv_ops,
+ },
+};
+
+/* 1080MHz configuration - VCO - 0 */
+static struct alpha_pll_config cam_cc_pll3_config = {
+ .l = 0x38,
+ .alpha_hi = 0x40,
+ .alpha_en_mask = BIT(24),
+ .vco_val = 0x0,
+ .vco_mask = GENMASK(21, 20),
+ .main_output_mask = BIT(0),
+ .config_ctl_val = 0x4001055b,
+ .test_ctl_hi_val = 0x1,
+ .test_ctl_hi_mask = 0x1,
+};
+
+static struct clk_alpha_pll cam_cc_pll3 = {
+ .offset = 0x3000,
+ .config = &cam_cc_pll3_config,
+ .vco_table = spark_vco,
+ .num_vco = ARRAY_SIZE(spark_vco),
+ .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
+ .clkr = {
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_pll3",
+ .parent_data = &(const struct clk_parent_data) {
+ .index = DT_BI_TCXO,
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+static const struct parent_map cam_cc_parent_map_0[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL1_OUT_AUX, 2 },
+ { P_CAM_CC_PLL0_OUT_AUX, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_0[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll1.clkr.hw },
+ { .hw = &cam_cc_pll0.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_1[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL2_OUT_EARLY, 4 },
+ { P_CAM_CC_PLL3_OUT_MAIN, 5 },
+ { P_CAM_CC_PLL0_OUT_AUX, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_1[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll2.clkr.hw },
+ { .hw = &cam_cc_pll3.clkr.hw },
+ { .hw = &cam_cc_pll0.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_2[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL1_OUT_AUX, 2 },
+ { P_CAM_CC_PLL2_OUT_EARLY, 4 },
+ { P_CAM_CC_PLL3_OUT_MAIN, 5 },
+ { P_CAM_CC_PLL0_OUT_AUX, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_2[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll1.clkr.hw },
+ { .hw = &cam_cc_pll2.clkr.hw },
+ { .hw = &cam_cc_pll3.clkr.hw },
+ { .hw = &cam_cc_pll0.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_3[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL2_OUT_AUX2, 1 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_3[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll2_out_aux2.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_4[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL3_OUT_MAIN, 5 },
+ { P_CAM_CC_PLL0_OUT_AUX, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_4[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll3.clkr.hw },
+ { .hw = &cam_cc_pll0.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_5[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL0_OUT_AUX, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_5[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll0.clkr.hw },
+};
+
+static const struct parent_map cam_cc_parent_map_6[] = {
+ { P_BI_TCXO, 0 },
+ { P_CAM_CC_PLL1_OUT_AUX, 2 },
+ { P_CAM_CC_PLL3_OUT_MAIN, 5 },
+ { P_CAM_CC_PLL0_OUT_AUX, 6 },
+};
+
+static const struct clk_parent_data cam_cc_parent_data_6[] = {
+ { .index = DT_BI_TCXO },
+ { .hw = &cam_cc_pll1.clkr.hw },
+ { .hw = &cam_cc_pll3.clkr.hw },
+ { .hw = &cam_cc_pll0.clkr.hw },
+};
+
+static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = {
+ F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0),
+ F(360000000, P_CAM_CC_PLL3_OUT_MAIN, 3, 0, 0),
+ F(432000000, P_CAM_CC_PLL3_OUT_MAIN, 2.5, 0, 0),
+ F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0),
+ F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_bps_clk_src = {
+ .cmd_rcgr = 0x6010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_bps_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_cci_clk_src[] = {
+ F(37500000, P_CAM_CC_PLL0_OUT_AUX, 16, 0, 0),
+ F(50000000, P_CAM_CC_PLL0_OUT_AUX, 12, 0, 0),
+ F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_cci_clk_src = {
+ .cmd_rcgr = 0xb0d8,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_5,
+ .freq_tbl = ftbl_cam_cc_cci_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cci_clk_src",
+ .parent_data = cam_cc_parent_data_5,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_5),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = {
+ F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0),
+ F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0),
+ F(269333333, P_CAM_CC_PLL1_OUT_AUX, 3, 0, 0),
+ F(320000000, P_CAM_CC_PLL2_OUT_EARLY, 3, 0, 0),
+ F(384000000, P_CAM_CC_PLL2_OUT_EARLY, 2.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_cphy_rx_clk_src = {
+ .cmd_rcgr = 0x9064,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_2,
+ .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cphy_rx_clk_src",
+ .parent_data = cam_cc_parent_data_2,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_2),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = {
+ F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0),
+ F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0),
+ F(269333333, P_CAM_CC_PLL1_OUT_AUX, 3, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = {
+ .cmd_rcgr = 0x5004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi0phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = {
+ .cmd_rcgr = 0x5028,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi1phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = {
+ .cmd_rcgr = 0x504c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi2phytimer_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = {
+ F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0),
+ F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0),
+ F(300000000, P_CAM_CC_PLL0_OUT_AUX, 2, 0, 0),
+ F(404000000, P_CAM_CC_PLL1_OUT_AUX, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_fast_ahb_clk_src = {
+ .cmd_rcgr = 0x603c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_fast_ahb_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = {
+ F(240000000, P_CAM_CC_PLL0_OUT_AUX, 2.5, 0, 0),
+ F(360000000, P_CAM_CC_PLL3_OUT_MAIN, 3, 0, 0),
+ F(432000000, P_CAM_CC_PLL3_OUT_MAIN, 2.5, 0, 0),
+ F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0),
+ F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_icp_clk_src = {
+ .cmd_rcgr = 0xb088,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_icp_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = {
+ F(240000000, P_CAM_CC_PLL0_OUT_AUX, 2.5, 0, 0),
+ F(360000000, P_CAM_CC_PLL3_OUT_MAIN, 3, 0, 0),
+ F(432000000, P_CAM_CC_PLL3_OUT_MAIN, 2.5, 0, 0),
+ F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_ife_0_clk_src = {
+ .cmd_rcgr = 0x9010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_4,
+ .freq_tbl = ftbl_cam_cc_ife_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_clk_src",
+ .parent_data = cam_cc_parent_data_4,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_4),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_ife_0_csid_clk_src[] = {
+ F(100000000, P_CAM_CC_PLL0_OUT_AUX, 6, 0, 0),
+ F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0),
+ F(320000000, P_CAM_CC_PLL2_OUT_EARLY, 3, 0, 0),
+ F(404000000, P_CAM_CC_PLL1_OUT_AUX, 2, 0, 0),
+ F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0),
+ F(540000000, P_CAM_CC_PLL3_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_ife_0_csid_clk_src = {
+ .cmd_rcgr = 0x903c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_2,
+ .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_csid_clk_src",
+ .parent_data = cam_cc_parent_data_2,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_2),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_ife_1_clk_src = {
+ .cmd_rcgr = 0xa010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_4,
+ .freq_tbl = ftbl_cam_cc_ife_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_clk_src",
+ .parent_data = cam_cc_parent_data_4,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_4),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_ife_1_csid_clk_src = {
+ .cmd_rcgr = 0xa034,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_2,
+ .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_csid_clk_src",
+ .parent_data = cam_cc_parent_data_2,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_2),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_ife_lite_clk_src = {
+ .cmd_rcgr = 0xb004,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_4,
+ .freq_tbl = ftbl_cam_cc_ife_0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_lite_clk_src",
+ .parent_data = cam_cc_parent_data_4,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_4),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = {
+ .cmd_rcgr = 0xb024,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_2,
+ .freq_tbl = ftbl_cam_cc_ife_0_csid_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_lite_csid_clk_src",
+ .parent_data = cam_cc_parent_data_2,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_2),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_ipe_0_clk_src = {
+ .cmd_rcgr = 0x7010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_icp_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ipe_0_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_jpeg_clk_src[] = {
+ F(66666667, P_CAM_CC_PLL0_OUT_AUX, 9, 0, 0),
+ F(133333333, P_CAM_CC_PLL0_OUT_AUX, 4.5, 0, 0),
+ F(216000000, P_CAM_CC_PLL3_OUT_MAIN, 5, 0, 0),
+ F(320000000, P_CAM_CC_PLL2_OUT_EARLY, 3, 0, 0),
+ F(480000000, P_CAM_CC_PLL2_OUT_EARLY, 2, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_AUX, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_jpeg_clk_src = {
+ .cmd_rcgr = 0xb04c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_1,
+ .freq_tbl = ftbl_cam_cc_jpeg_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_jpeg_clk_src",
+ .parent_data = cam_cc_parent_data_1,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_1),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_lrme_clk_src[] = {
+ F(200000000, P_CAM_CC_PLL0_OUT_AUX, 3, 0, 0),
+ F(216000000, P_CAM_CC_PLL3_OUT_MAIN, 5, 0, 0),
+ F(300000000, P_CAM_CC_PLL0_OUT_AUX, 2, 0, 0),
+ F(404000000, P_CAM_CC_PLL1_OUT_AUX, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_lrme_clk_src = {
+ .cmd_rcgr = 0xb0f8,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_6,
+ .freq_tbl = ftbl_cam_cc_lrme_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_lrme_clk_src",
+ .parent_data = cam_cc_parent_data_6,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_6),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(24000000, P_CAM_CC_PLL2_OUT_AUX2, 10, 1, 2),
+ F(34285714, P_CAM_CC_PLL2_OUT_AUX2, 14, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_mclk0_clk_src = {
+ .cmd_rcgr = 0x4004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_3,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk0_clk_src",
+ .parent_data = cam_cc_parent_data_3,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_3),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk1_clk_src = {
+ .cmd_rcgr = 0x4024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_3,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk1_clk_src",
+ .parent_data = cam_cc_parent_data_3,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_3),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk2_clk_src = {
+ .cmd_rcgr = 0x4044,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_3,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk2_clk_src",
+ .parent_data = cam_cc_parent_data_3,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_3),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_rcg2 cam_cc_mclk3_clk_src = {
+ .cmd_rcgr = 0x4064,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_3,
+ .freq_tbl = ftbl_cam_cc_mclk0_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_mclk3_clk_src",
+ .parent_data = cam_cc_parent_data_3,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_3),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = {
+ F(80000000, P_CAM_CC_PLL0_OUT_AUX, 7.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cam_cc_slow_ahb_clk_src = {
+ .cmd_rcgr = 0x6058,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = cam_cc_parent_map_0,
+ .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src,
+ .clkr.hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_slow_ahb_clk_src",
+ .parent_data = cam_cc_parent_data_0,
+ .num_parents = ARRAY_SIZE(cam_cc_parent_data_0),
+ .ops = &clk_rcg2_shared_ops,
+ },
+};
+
+static struct clk_branch cam_cc_bps_ahb_clk = {
+ .halt_reg = 0x6070,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x6070,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_bps_areg_clk = {
+ .halt_reg = 0x6054,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x6054,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_areg_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_fast_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_bps_axi_clk = {
+ .halt_reg = 0x6038,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x6038,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_bps_clk = {
+ .halt_reg = 0x6028,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x6028,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_bps_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_bps_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_camnoc_axi_clk = {
+ .halt_reg = 0xb124,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb124,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_camnoc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cci_clk = {
+ .halt_reg = 0xb0f0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb0f0,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cci_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cci_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_core_ahb_clk = {
+ .halt_reg = 0xb144,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0xb144,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_core_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_cpas_ahb_clk = {
+ .halt_reg = 0xb11c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb11c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_cpas_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi0phytimer_clk = {
+ .halt_reg = 0x501c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x501c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi0phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi0phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi1phytimer_clk = {
+ .halt_reg = 0x5040,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5040,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi1phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi1phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csi2phytimer_clk = {
+ .halt_reg = 0x5064,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5064,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csi2phytimer_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_csi2phytimer_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy0_clk = {
+ .halt_reg = 0x5020,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5020,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy1_clk = {
+ .halt_reg = 0x5044,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5044,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy1_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_csiphy2_clk = {
+ .halt_reg = 0x5068,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5068,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_csiphy2_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_icp_clk = {
+ .halt_reg = 0xb0a0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb0a0,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_icp_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_icp_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_0_axi_clk = {
+ .halt_reg = 0x9080,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x9080,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_0_clk = {
+ .halt_reg = 0x9028,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x9028,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_0_cphy_rx_clk = {
+ .halt_reg = 0x907c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x907c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_cphy_rx_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_0_csid_clk = {
+ .halt_reg = 0x9054,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x9054,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_csid_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_0_csid_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_0_dsp_clk = {
+ .halt_reg = 0x9038,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x9038,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_0_dsp_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_1_axi_clk = {
+ .halt_reg = 0xa058,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xa058,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_1_clk = {
+ .halt_reg = 0xa028,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xa028,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_1_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_1_cphy_rx_clk = {
+ .halt_reg = 0xa054,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xa054,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_cphy_rx_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_1_csid_clk = {
+ .halt_reg = 0xa04c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xa04c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_csid_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_1_csid_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_1_dsp_clk = {
+ .halt_reg = 0xa030,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xa030,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_1_dsp_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_1_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_lite_clk = {
+ .halt_reg = 0xb01c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_lite_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_lite_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = {
+ .halt_reg = 0xb044,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb044,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_lite_cphy_rx_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_cphy_rx_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ife_lite_csid_clk = {
+ .halt_reg = 0xb03c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xb03c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ife_lite_csid_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ife_lite_csid_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ipe_0_ahb_clk = {
+ .halt_reg = 0x7040,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x7040,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ipe_0_ahb_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_slow_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ipe_0_areg_clk = {
+ .halt_reg = 0x703c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x703c,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ipe_0_areg_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_fast_ahb_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ipe_0_axi_clk = {
+ .halt_reg = 0x7038,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x7038,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ipe_0_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_ipe_0_clk = {
+ .halt_reg = 0x7028,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x7028,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "cam_cc_ipe_0_clk",
+ .parent_hws = (const struct clk_hw*[]) {
+ &cam_cc_ipe_0_clk_src.clkr.hw,
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch cam_cc_jpeg_clk = {
+ .halt_reg = 0xb064,